From 989c78dd56307c85f710533130a2a464f6f3c5ae Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 28 Oct 2013 23:49:26 +0100 Subject: [PATCH 001/105] usb: gadget: r8a66597-udc: convert to clk_prepare/unprepare Turn clk_enable() and clk_disable() calls into clk_prepare_enable() and clk_disable_unprepare() to get ready for the migration to the common clock framework. Cc: Felipe Balbi Cc: linux-usb@vger.kernel.org Signed-off-by: Laurent Pinchart Signed-off-by: Felipe Balbi --- drivers/usb/gadget/r8a66597-udc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/r8a66597-udc.c index 68be48d33404..47287518bff3 100644 --- a/drivers/usb/gadget/r8a66597-udc.c +++ b/drivers/usb/gadget/r8a66597-udc.c @@ -1833,7 +1833,7 @@ static int __exit r8a66597_remove(struct platform_device *pdev) r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req); if (r8a66597->pdata->on_chip) { - clk_disable(r8a66597->clk); + clk_disable_unprepare(r8a66597->clk); clk_put(r8a66597->clk); } @@ -1931,7 +1931,7 @@ static int __init r8a66597_probe(struct platform_device *pdev) ret = PTR_ERR(r8a66597->clk); goto clean_up; } - clk_enable(r8a66597->clk); + clk_prepare_enable(r8a66597->clk); } if (r8a66597->pdata->sudmac) { @@ -1996,7 +1996,7 @@ clean_up3: free_irq(irq, r8a66597); clean_up2: if (r8a66597->pdata->on_chip) { - clk_disable(r8a66597->clk); + clk_disable_unprepare(r8a66597->clk); clk_put(r8a66597->clk); } clean_up: From 943c13971c08ddeb06f4cb9dea1addb76f6b4423 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 29 Oct 2013 12:17:16 -0500 Subject: [PATCH 002/105] usb: musb: dsps: implement ->set_mode() this will let us support broken designs such as AM335x EVM SK where ID pin isn't routed anywhere on a host port. With this we can toggle internal IDDIG signal and make sure MUSB goes into host mode as necessary. Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_dsps.c | 51 ++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 1901f6fe5807..ce7ec014a125 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -106,6 +106,7 @@ struct dsps_musb_wrapper { /* bit positions for mode */ unsigned iddig:5; + unsigned iddig_mux:5; /* miscellaneous stuff */ u8 poll_seconds; }; @@ -406,6 +407,54 @@ static int dsps_musb_exit(struct musb *musb) return 0; } +static int dsps_musb_set_mode(struct musb *musb, u8 mode) +{ + struct device *dev = musb->controller; + struct dsps_glue *glue = dev_get_drvdata(dev->parent); + const struct dsps_musb_wrapper *wrp = glue->wrp; + void __iomem *ctrl_base = musb->ctrl_base; + void __iomem *base = musb->mregs; + u32 reg; + + reg = dsps_readl(base, wrp->mode); + + switch (mode) { + case MUSB_HOST: + reg &= ~(1 << wrp->iddig); + + /* + * if we're setting mode to host-only or device-only, we're + * going to ignore whatever the PHY sends us and just force + * ID pin status by SW + */ + reg |= (1 << wrp->iddig_mux); + + dsps_writel(base, wrp->mode, reg); + dsps_writel(ctrl_base, wrp->phy_utmi, 0x02); + break; + case MUSB_PERIPHERAL: + reg |= (1 << wrp->iddig); + + /* + * if we're setting mode to host-only or device-only, we're + * going to ignore whatever the PHY sends us and just force + * ID pin status by SW + */ + reg |= (1 << wrp->iddig_mux); + + dsps_writel(base, wrp->mode, reg); + break; + case MUSB_OTG: + dsps_writel(base, wrp->phy_utmi, 0x02); + break; + default: + dev_err(glue->dev, "unsupported mode %d\n", mode); + return -EINVAL; + } + + return 0; +} + static struct musb_platform_ops dsps_ops = { .init = dsps_musb_init, .exit = dsps_musb_exit, @@ -414,6 +463,7 @@ static struct musb_platform_ops dsps_ops = { .disable = dsps_musb_disable, .try_idle = dsps_musb_try_idle, + .set_mode = dsps_musb_set_mode, }; static u64 musb_dmamask = DMA_BIT_MASK(32); @@ -608,6 +658,7 @@ static const struct dsps_musb_wrapper am33xx_driver_data = { .reset = 0, .otg_disable = 21, .iddig = 8, + .iddig_mux = 7, .usb_shift = 0, .usb_mask = 0x1ff, .usb_bitmap = (0x1ff << 0), From 2df6761e5eca9a810050a15062ae8abce1bbae41 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 29 Oct 2013 12:17:17 -0500 Subject: [PATCH 003/105] usb: musb: core: call musb_platform_set_mode() during probe This will tell glue layer which mode we want port to be in. Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 0a43329569d1..377ef9b29c73 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1941,17 +1941,26 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) switch (musb->port_mode) { case MUSB_PORT_MODE_HOST: status = musb_host_setup(musb, plat->power); + if (status < 0) + goto fail3; + status = musb_platform_set_mode(musb, MUSB_HOST); break; case MUSB_PORT_MODE_GADGET: status = musb_gadget_setup(musb); + if (status < 0) + goto fail3; + status = musb_platform_set_mode(musb, MUSB_PERIPHERAL); break; case MUSB_PORT_MODE_DUAL_ROLE: status = musb_host_setup(musb, plat->power); if (status < 0) goto fail3; status = musb_gadget_setup(musb); - if (status) + if (status) { musb_host_cleanup(musb); + goto fail3; + } + status = musb_platform_set_mode(musb, MUSB_OTG); break; default: dev_err(dev, "unsupported port mode %d\n", musb->port_mode); From e0a6104e066595bcf791381a23b568ab0a890707 Mon Sep 17 00:00:00 2001 From: George Cherian Date: Wed, 25 Sep 2013 11:59:43 +0530 Subject: [PATCH 004/105] usb: dwc3: dwc3-omap: return PROBE_DEFER if extcon is missing and found in dt Due to inter dependencies of I2C and extcon by the time dwc3 checks for extcon device its not registered especially in case of J6. In O5 the vbus regulator save us from getting to this point. So for tiime being return PROBE_DEFER if extcon is enabled in dt. Signed-off-by: George Cherian --- drivers/usb/dwc3/dwc3-omap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 7f7ea62e961b..daab0ad10b25 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -535,7 +535,7 @@ static int dwc3_omap_probe(struct platform_device *pdev) edev = of_extcon_get_extcon_dev(dev, 0); if (IS_ERR(edev)) { dev_vdbg(dev, "couldn't get extcon device\n"); - ret = PTR_ERR(edev); + ret = -EPROBE_DEFER; goto err2; } From c338412b5dedf405d3f8ba3af1a61fa623319e1d Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Mon, 25 Nov 2013 22:26:40 +0100 Subject: [PATCH 005/105] usb: musb: unconditionally save and restore the context on suspend It appears not all platforms featuring a musb core need to save the musb core registers at suspend time and restore them on resume. The dsps platform does, however, and because it shouldn't cause any trouble on other platforms, do it unconditionally for all of them. Signed-off-by: Daniel Mack Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 377ef9b29c73..ac96e4ce7e95 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -2224,16 +2224,28 @@ static int musb_suspend(struct device *dev) */ } + musb_save_context(musb); + spin_unlock_irqrestore(&musb->lock, flags); return 0; } static int musb_resume_noirq(struct device *dev) { - /* for static cmos like DaVinci, register values were preserved + struct musb *musb = dev_to_musb(dev); + + /* + * For static cmos like DaVinci, register values were preserved * unless for some reason the whole soc powered down or the USB * module got reset through the PSC (vs just being disabled). + * + * For the DSPS glue layer though, a full register restore has to + * be done. As it shouldn't harm other platforms, we do it + * unconditionally. */ + + musb_restore_context(musb); + return 0; } From 94f72136a86141604bcab75a2548b6488e70128d Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Mon, 25 Nov 2013 22:26:41 +0100 Subject: [PATCH 006/105] usb: musb: call musb_port_suspend from musb_bus_suspend Make musb_port_suspend() externally available, and call it when to host goes into suspend. This allows the core to go into suspend while a device is connected. Signed-off-by: Daniel Mack Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_host.c | 2 ++ drivers/usb/musb/musb_host.h | 2 ++ drivers/usb/musb/musb_virthub.c | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 6582a20bec05..81caf9f4eb1a 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -2433,6 +2433,8 @@ static int musb_bus_suspend(struct usb_hcd *hcd) struct musb *musb = hcd_to_musb(hcd); u8 devctl; + musb_port_suspend(musb, true); + if (!is_host_active(musb)) return 0; diff --git a/drivers/usb/musb/musb_host.h b/drivers/usb/musb/musb_host.h index 960d73570b2f..e660af90272d 100644 --- a/drivers/usb/musb/musb_host.h +++ b/drivers/usb/musb/musb_host.h @@ -92,6 +92,7 @@ extern void musb_host_rx(struct musb *, u8); extern void musb_root_disconnect(struct musb *musb); extern void musb_host_resume_root_hub(struct musb *musb); extern void musb_host_poke_root_hub(struct musb *musb); +extern void musb_port_suspend(struct musb *musb, bool do_suspend); #else static inline struct musb *hcd_to_musb(struct usb_hcd *hcd) { @@ -121,6 +122,7 @@ static inline void musb_root_disconnect(struct musb *musb) {} static inline void musb_host_resume_root_hub(struct musb *musb) {} static inline void musb_host_poll_rh_status(struct musb *musb) {} static inline void musb_host_poke_root_hub(struct musb *musb) {} +static inline void musb_port_suspend(struct musb *musb, bool do_suspend) {} #endif struct usb_hcd; diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c index 9af6bba5eac9..e977441401ed 100644 --- a/drivers/usb/musb/musb_virthub.c +++ b/drivers/usb/musb/musb_virthub.c @@ -44,7 +44,7 @@ #include "musb_core.h" -static void musb_port_suspend(struct musb *musb, bool do_suspend) +void musb_port_suspend(struct musb *musb, bool do_suspend) { struct usb_otg *otg = musb->xceiv->otg; u8 power; From b991f9b77c029135f6e0d1d5d16869ebf755c4c0 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Mon, 25 Nov 2013 22:26:42 +0100 Subject: [PATCH 007/105] usb: musb: dsps: add {tx,rx}_mode to wrapper rx_mode and tx_mode need to be read at suspend time and restored on resume for dsps platforms. So add it to the wrapper struct first, and initialize the values. Signed-off-by: Daniel Mack Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_dsps.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index ce7ec014a125..3f3724471667 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -83,6 +83,8 @@ struct dsps_musb_wrapper { u16 coreintr_status; u16 phy_utmi; u16 mode; + u16 tx_mode; + u16 rx_mode; /* bit positions for control */ unsigned reset:5; @@ -655,6 +657,8 @@ static const struct dsps_musb_wrapper am33xx_driver_data = { .coreintr_status = 0x34, .phy_utmi = 0xe0, .mode = 0xe8, + .tx_mode = 0x70, + .rx_mode = 0x74, .reset = 0, .otg_disable = 21, .iddig = 8, From 869c597829817af4b9f85c5e4181c622918dc781 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Tue, 26 Nov 2013 13:31:14 +0100 Subject: [PATCH 008/105] usb: musb: dsps: add support for suspend and resume The dsps platform needs to save save some registers at suspend time and restore them after resume. This patch adds a struct for these registers, and also lets the musb core know that the core registers need to be saved as well. We also have to explicitly de-assert the port reset upon resume on this platform, but musb_port_reset() should not be called from glue layers. Hence, introduce a flag in struct musb_hdrc_config for this. Signed-off-by: Daniel Mack Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_dsps.c | 57 +++++++++++++++++++++++++++++++++ drivers/usb/musb/musb_host.c | 7 +++- drivers/usb/musb/musb_host.h | 2 ++ drivers/usb/musb/musb_virthub.c | 2 +- include/linux/usb/musb.h | 3 ++ 5 files changed, 69 insertions(+), 2 deletions(-) diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 3f3724471667..7cfa6e80ad60 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -113,6 +113,19 @@ struct dsps_musb_wrapper { u8 poll_seconds; }; +/* + * register shadow for suspend + */ +struct dsps_context { + u32 control; + u32 epintr; + u32 coreintr; + u32 phy_utmi; + u32 mode; + u32 tx_mode; + u32 rx_mode; +}; + /** * DSPS glue structure. */ @@ -122,6 +135,8 @@ struct dsps_glue { const struct dsps_musb_wrapper *wrp; /* wrapper register offsets */ struct timer_list timer; /* otg_workaround timer */ unsigned long last_timer; /* last timer data for each instance */ + + struct dsps_context context; }; static void dsps_musb_try_idle(struct musb *musb, unsigned long timeout) @@ -559,6 +574,7 @@ static int dsps_create_musb_pdev(struct dsps_glue *glue, config->num_eps = get_int_prop(dn, "mentor,num-eps"); config->ram_bits = get_int_prop(dn, "mentor,ram-bits"); + config->host_port_deassert_reset_at_resume = 1; pdata.mode = get_musb_port_mode(dev); /* DT keeps this entry in mA, musb expects it as per USB spec */ pdata.power = get_int_prop(dn, "mentor,power") / 2; @@ -683,11 +699,52 @@ static const struct of_device_id musb_dsps_of_match[] = { }; MODULE_DEVICE_TABLE(of, musb_dsps_of_match); +#ifdef CONFIG_PM +static int dsps_suspend(struct device *dev) +{ + struct dsps_glue *glue = dev_get_drvdata(dev); + const struct dsps_musb_wrapper *wrp = glue->wrp; + struct musb *musb = platform_get_drvdata(glue->musb); + void __iomem *mbase = musb->ctrl_base; + + glue->context.control = dsps_readl(mbase, wrp->control); + glue->context.epintr = dsps_readl(mbase, wrp->epintr_set); + glue->context.coreintr = dsps_readl(mbase, wrp->coreintr_set); + glue->context.phy_utmi = dsps_readl(mbase, wrp->phy_utmi); + glue->context.mode = dsps_readl(mbase, wrp->mode); + glue->context.tx_mode = dsps_readl(mbase, wrp->tx_mode); + glue->context.rx_mode = dsps_readl(mbase, wrp->rx_mode); + + return 0; +} + +static int dsps_resume(struct device *dev) +{ + struct dsps_glue *glue = dev_get_drvdata(dev); + const struct dsps_musb_wrapper *wrp = glue->wrp; + struct musb *musb = platform_get_drvdata(glue->musb); + void __iomem *mbase = musb->ctrl_base; + + dsps_writel(mbase, wrp->control, glue->context.control); + dsps_writel(mbase, wrp->epintr_set, glue->context.epintr); + dsps_writel(mbase, wrp->coreintr_set, glue->context.coreintr); + dsps_writel(mbase, wrp->phy_utmi, glue->context.phy_utmi); + dsps_writel(mbase, wrp->mode, glue->context.mode); + dsps_writel(mbase, wrp->tx_mode, glue->context.tx_mode); + dsps_writel(mbase, wrp->rx_mode, glue->context.rx_mode); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(dsps_pm_ops, dsps_suspend, dsps_resume); + static struct platform_driver dsps_usbss_driver = { .probe = dsps_probe, .remove = dsps_remove, .driver = { .name = "musb-dsps", + .pm = &dsps_pm_ops, .of_match_table = musb_dsps_of_match, }, }; diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 81caf9f4eb1a..822413899260 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -2464,7 +2464,12 @@ static int musb_bus_suspend(struct usb_hcd *hcd) static int musb_bus_resume(struct usb_hcd *hcd) { - /* resuming child port does the work */ + struct musb *musb = hcd_to_musb(hcd); + + if (musb->config && + musb->config->host_port_deassert_reset_at_resume) + musb_port_reset(musb, false); + return 0; } diff --git a/drivers/usb/musb/musb_host.h b/drivers/usb/musb/musb_host.h index e660af90272d..7436c24af0ff 100644 --- a/drivers/usb/musb/musb_host.h +++ b/drivers/usb/musb/musb_host.h @@ -93,6 +93,7 @@ extern void musb_root_disconnect(struct musb *musb); extern void musb_host_resume_root_hub(struct musb *musb); extern void musb_host_poke_root_hub(struct musb *musb); extern void musb_port_suspend(struct musb *musb, bool do_suspend); +extern void musb_port_reset(struct musb *musb, bool do_reset); #else static inline struct musb *hcd_to_musb(struct usb_hcd *hcd) { @@ -123,6 +124,7 @@ static inline void musb_host_resume_root_hub(struct musb *musb) {} static inline void musb_host_poll_rh_status(struct musb *musb) {} static inline void musb_host_poke_root_hub(struct musb *musb) {} static inline void musb_port_suspend(struct musb *musb, bool do_suspend) {} +static inline void musb_port_reset(struct musb *musb) {} #endif struct usb_hcd; diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c index e977441401ed..24e46c0c84b5 100644 --- a/drivers/usb/musb/musb_virthub.c +++ b/drivers/usb/musb/musb_virthub.c @@ -109,7 +109,7 @@ void musb_port_suspend(struct musb *musb, bool do_suspend) } } -static void musb_port_reset(struct musb *musb, bool do_reset) +void musb_port_reset(struct musb *musb, bool do_reset) { u8 power; void __iomem *mbase = musb->mregs; diff --git a/include/linux/usb/musb.h b/include/linux/usb/musb.h index eb505250940a..a4ee1b582183 100644 --- a/include/linux/usb/musb.h +++ b/include/linux/usb/musb.h @@ -76,6 +76,9 @@ struct musb_hdrc_config { unsigned dma:1 __deprecated; /* supports DMA */ unsigned vendor_req:1 __deprecated; /* vendor registers required */ + /* need to explicitly de-assert the port reset after resume? */ + unsigned host_port_deassert_reset_at_resume:1; + u8 num_eps; /* number of endpoints _with_ ep0 */ u8 dma_channels __deprecated; /* number of dma channels */ u8 dyn_fifo_size; /* dynamic size in bytes */ From c3505f04582f3fee1452613c7d36a1811e524ab4 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 8 Nov 2013 12:39:29 -0600 Subject: [PATCH 009/105] usb: gadget: epautoconf: switch over to usb_endpoint_type() we have a helper to fetch endpoint type out of a descriptor, let's use it. Signed-off-by: Felipe Balbi --- drivers/usb/gadget/epautoconf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index a777f7bd11b4..feaaa7b72ee3 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -58,7 +58,7 @@ ep_matches ( return 0; /* only support ep0 for portable CONTROL traffic */ - type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + type = usb_endpoint_type(desc); if (USB_ENDPOINT_XFER_CONTROL == type) return 0; From 4fbc5566c8b9b8e1c927583b89f3ff9e27ebad09 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Sun, 10 Nov 2013 20:35:55 +0100 Subject: [PATCH 010/105] usb: gadget: goku: remove unused argument The stop_activity function never uses driver argument (even though it modifies it) and thus it is safe to remove it. Signed-off-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/goku_udc.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c index f82768015715..7eda9fd6528e 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/goku_udc.c @@ -1350,16 +1350,12 @@ static int goku_udc_start(struct usb_gadget *g, return 0; } -static void -stop_activity(struct goku_udc *dev, struct usb_gadget_driver *driver) +static void stop_activity(struct goku_udc *dev) { unsigned i; DBG (dev, "%s\n", __func__); - if (dev->gadget.speed == USB_SPEED_UNKNOWN) - driver = NULL; - /* disconnect gadget driver after quiesceing hw and the driver */ udc_reset (dev); for (i = 0; i < 4; i++) @@ -1377,7 +1373,7 @@ static int goku_udc_stop(struct usb_gadget *g, spin_lock_irqsave(&dev->lock, flags); dev->driver = NULL; - stop_activity(dev, driver); + stop_activity(dev); spin_unlock_irqrestore(&dev->lock, flags); return 0; @@ -1521,7 +1517,7 @@ rescan: if (unlikely(stat & INT_DEVWIDE)) { if (stat & INT_SYSERROR) { ERROR(dev, "system error\n"); - stop_activity(dev, dev->driver); + stop_activity(dev); stat = 0; handled = 1; // FIXME have a neater way to prevent re-enumeration @@ -1536,7 +1532,7 @@ rescan: } else { DBG(dev, "disconnect\n"); if (dev->gadget.speed == USB_SPEED_FULL) - stop_activity(dev, dev->driver); + stop_activity(dev); dev->ep0state = EP0_DISCONNECT; dev->int_enable = INT_DEVWIDE; writel(dev->int_enable, &dev->regs->int_enable); From 7de7174aa36f548a53c26d6841f3f7aeb449513b Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 12 Nov 2013 20:07:24 +0100 Subject: [PATCH 011/105] usb: gadget: remove superfluous name casts device_driver.name is "const char *" Signed-off-by: Geert Uytterhoeven Cc: linux-usb@vger.kernel.org Signed-off-by: Felipe Balbi --- drivers/usb/gadget/fsl_qe_udc.c | 2 +- drivers/usb/gadget/fsl_udc_core.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index 807127d56fa3..6315ee698d4d 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -2717,7 +2717,7 @@ MODULE_DEVICE_TABLE(of, qe_udc_match); static struct platform_driver udc_driver = { .driver = { - .name = (char *)driver_name, + .name = driver_name, .owner = THIS_MODULE, .of_match_table = qe_udc_match, }, diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index 36ac7cfba91d..b7dea4eec32c 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -2666,7 +2666,7 @@ static struct platform_driver udc_driver = { .suspend = fsl_udc_suspend, .resume = fsl_udc_resume, .driver = { - .name = (char *)driver_name, + .name = driver_name, .owner = THIS_MODULE, /* udc suspend/resume called from OTG driver */ .suspend = fsl_udc_otg_suspend, From 574f24f797718937b48befb97d13850d5bebb72c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 14 Nov 2013 11:42:11 +0300 Subject: [PATCH 012/105] usb: gadget: update some out of date comments These functions used to return negative errror codes but now they return ERR_PTRs. Signed-off-by: Dan Carpenter Acked-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/gadget/u_ether.c | 2 +- drivers/usb/gadget/u_ether.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index 2aae0d61bb19..b7d4f82872b7 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -753,7 +753,7 @@ static struct device_type gadget_type = { * gadget driver using this framework. The link layer addresses are * set up using module parameters. * - * Returns negative errno, or zero on success + * Returns an eth_dev pointer on success, or an ERR_PTR on failure. */ struct eth_dev *gether_setup_name(struct usb_gadget *g, const char *dev_addr, const char *host_addr, diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/u_ether.h index fb23d1fde8eb..a32b41e27450 100644 --- a/drivers/usb/gadget/u_ether.h +++ b/drivers/usb/gadget/u_ether.h @@ -106,7 +106,7 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g, * gadget driver using this framework. The link layer addresses are * set up using module parameters. * - * Returns negative errno, or zero on success + * Returns a eth_dev pointer on success, or an ERR_PTR on failure */ static inline struct eth_dev *gether_setup(struct usb_gadget *g, const char *dev_addr, const char *host_addr, From cc9d9ccf2a1e8c9f36a436805317180003ba9719 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Nov 2013 10:06:00 -0600 Subject: [PATCH 013/105] usb: phy: fsm: don't depend on indirect includes this header uses spinlocks and errno values, so we must include and to avoid build errors. Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-fsm-usb.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/phy/phy-fsm-usb.h b/drivers/usb/phy/phy-fsm-usb.h index 7441b46a27f1..32f86a36ba50 100644 --- a/drivers/usb/phy/phy-fsm-usb.h +++ b/drivers/usb/phy/phy-fsm-usb.h @@ -15,6 +15,9 @@ * 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include +#include + #undef VERBOSE #ifdef VERBOSE From d49dd788840ff802421ed7412e967b659fe9ca58 Mon Sep 17 00:00:00 2001 From: Anton Tikhomirov Date: Tue, 26 Nov 2013 10:08:58 -0600 Subject: [PATCH 014/105] usb: phy: fsm: protect against multiple inclusions if this header is included twice, we would have redefinition build errors. Fix this. Signed-off-by: Anton Tikhomirov Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-fsm-usb.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/usb/phy/phy-fsm-usb.h b/drivers/usb/phy/phy-fsm-usb.h index 32f86a36ba50..200f4d156020 100644 --- a/drivers/usb/phy/phy-fsm-usb.h +++ b/drivers/usb/phy/phy-fsm-usb.h @@ -15,6 +15,9 @@ * 675 Mass Ave, Cambridge, MA 02139, USA. */ +#ifndef __LINUX_USB_OTG_FSM_H +#define __LINUX_USB_OTG_FSM_H + #include #include @@ -237,3 +240,5 @@ static inline int otg_start_gadget(struct otg_fsm *fsm, int on) } int otg_statemachine(struct otg_fsm *fsm); + +#endif /* __LINUX_USB_OTG_FSM_H */ From 16e569e9661ea2b964b8abb607a51e5285254021 Mon Sep 17 00:00:00 2001 From: Anton Tikhomirov Date: Tue, 26 Nov 2013 11:46:05 +0900 Subject: [PATCH 015/105] usb: phy: replace spinlock with mutex in OTG FSM OTG Final State Machine calls functions which may sleep. For example, start_gadget callback implementation can use usb_gadget_vbus_connect(), whose context: can sleep. If so, mutex should be used instead of spinlock. Signed-off-by: Anton Tikhomirov Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-fsl-usb.c | 7 +++---- drivers/usb/phy/phy-fsm-usb.c | 7 +++---- drivers/usb/phy/phy-fsm-usb.h | 4 ++-- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c index 7f3c73b967ce..62d5af22efaf 100644 --- a/drivers/usb/phy/phy-fsl-usb.c +++ b/drivers/usb/phy/phy-fsl-usb.c @@ -848,7 +848,7 @@ static int fsl_otg_conf(struct platform_device *pdev) pr_info("Couldn't init OTG timers\n"); goto err; } - spin_lock_init(&fsl_otg_tc->fsm.lock); + mutex_init(&fsl_otg_tc->fsm.lock); /* Set OTG state machine operations */ fsl_otg_tc->fsm.ops = &fsl_otg_ops; @@ -1017,10 +1017,9 @@ static int show_fsl_usb2_otg_state(struct device *dev, struct otg_fsm *fsm = &fsl_otg_dev->fsm; char *next = buf; unsigned size = PAGE_SIZE; - unsigned long flags; int t; - spin_lock_irqsave(&fsm->lock, flags); + mutex_lock(&fsm->lock); /* basic driver infomation */ t = scnprintf(next, size, @@ -1088,7 +1087,7 @@ static int show_fsl_usb2_otg_state(struct device *dev, size -= t; next += t; - spin_unlock_irqrestore(&fsm->lock, flags); + mutex_unlock(&fsm->lock); return PAGE_SIZE - size; } diff --git a/drivers/usb/phy/phy-fsm-usb.c b/drivers/usb/phy/phy-fsm-usb.c index 329c2d2f8595..2817b04a6c47 100644 --- a/drivers/usb/phy/phy-fsm-usb.c +++ b/drivers/usb/phy/phy-fsm-usb.c @@ -23,7 +23,7 @@ #include #include -#include +#include #include #include #include @@ -245,9 +245,8 @@ int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) int otg_statemachine(struct otg_fsm *fsm) { enum usb_otg_state state; - unsigned long flags; - spin_lock_irqsave(&fsm->lock, flags); + mutex_lock(&fsm->lock); state = fsm->otg->phy->state; state_changed = 0; @@ -359,7 +358,7 @@ int otg_statemachine(struct otg_fsm *fsm) default: break; } - spin_unlock_irqrestore(&fsm->lock, flags); + mutex_lock(&fsm->lock); VDBG("quit statemachine, changed = %d\n", state_changed); return state_changed; diff --git a/drivers/usb/phy/phy-fsm-usb.h b/drivers/usb/phy/phy-fsm-usb.h index 200f4d156020..b6ba1bfb86f2 100644 --- a/drivers/usb/phy/phy-fsm-usb.h +++ b/drivers/usb/phy/phy-fsm-usb.h @@ -18,7 +18,7 @@ #ifndef __LINUX_USB_OTG_FSM_H #define __LINUX_USB_OTG_FSM_H -#include +#include #include #undef VERBOSE @@ -116,7 +116,7 @@ struct otg_fsm { /* Current usb protocol used: 0:undefine; 1:host; 2:client */ int protocol; - spinlock_t lock; + struct mutex lock; }; struct otg_fsm_ops { From 5653668c9585441926dd2575791f6b5bb84bb254 Mon Sep 17 00:00:00 2001 From: Anton Tikhomirov Date: Tue, 26 Nov 2013 11:47:02 +0900 Subject: [PATCH 016/105] usb: phy: move OTG FSM header Other USB drivers may want to use OTG final state machine implementation, so make this header available for them. Signed-off-by: Anton Tikhomirov Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-fsl-usb.h | 2 +- drivers/usb/phy/phy-fsm-usb.c | 3 +-- drivers/usb/phy/phy-fsm-usb.h => include/linux/usb/otg-fsm.h | 0 3 files changed, 2 insertions(+), 3 deletions(-) rename drivers/usb/phy/phy-fsm-usb.h => include/linux/usb/otg-fsm.h (100%) diff --git a/drivers/usb/phy/phy-fsl-usb.h b/drivers/usb/phy/phy-fsl-usb.h index 7365170a2f23..5986c96354df 100644 --- a/drivers/usb/phy/phy-fsl-usb.h +++ b/drivers/usb/phy/phy-fsl-usb.h @@ -15,7 +15,7 @@ * 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "phy-fsm-usb.h" +#include #include #include diff --git a/drivers/usb/phy/phy-fsm-usb.c b/drivers/usb/phy/phy-fsm-usb.c index 2817b04a6c47..62238726fb1c 100644 --- a/drivers/usb/phy/phy-fsm-usb.c +++ b/drivers/usb/phy/phy-fsm-usb.c @@ -28,8 +28,7 @@ #include #include #include - -#include "phy-fsm-usb.h" +#include /* Change USB protocol when there is a protocol change */ static int otg_set_protocol(struct otg_fsm *fsm, int protocol) diff --git a/drivers/usb/phy/phy-fsm-usb.h b/include/linux/usb/otg-fsm.h similarity index 100% rename from drivers/usb/phy/phy-fsm-usb.h rename to include/linux/usb/otg-fsm.h From e1d2e31975e1e3a31ac592d5b1c5cb5d655b3f4e Mon Sep 17 00:00:00 2001 From: Anton Tikhomirov Date: Tue, 26 Nov 2013 11:47:53 +0900 Subject: [PATCH 017/105] usb: phy: Add OTG FSM configuration option This patch removes dependency on Freescale USB UTG Transceiver driver and makes OTG FSM implementation selectable. Signed-off-by: Anton Tikhomirov Signed-off-by: Felipe Balbi --- drivers/usb/phy/Kconfig | 10 +++++++++- drivers/usb/phy/Makefile | 4 ++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 08e2f39027ec..921a9f43e309 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -6,6 +6,14 @@ menu "USB Physical Layer drivers" config USB_PHY def_bool n +config USB_OTG_FSM + bool "USB 2.0 OTG FSM implementation" + select USB_OTG + select USB_PHY + help + Implements OTG Final State Machine as specified in On-The-Go + and Embedded Host Supplement to the USB Revision 2.0 Specification. + # # USB Transceiver Drivers # @@ -20,7 +28,7 @@ config AB8500_USB config FSL_USB2_OTG bool "Freescale USB OTG Transceiver Driver" - depends on USB_EHCI_FSL && USB_FSL_USB2 && PM_RUNTIME + depends on USB_EHCI_FSL && USB_FSL_USB2 && USB_OTG_FSM && PM_RUNTIME select USB_OTG select USB_PHY help diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index 022c1da7fb78..1bf6c083a36e 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -3,12 +3,12 @@ # obj-$(CONFIG_USB_PHY) += phy.o obj-$(CONFIG_OF) += of.o +obj-$(CONFIG_USB_OTG_FSM) += phy-fsm-usb.o # transceiver drivers, keep the list sorted obj-$(CONFIG_AB8500_USB) += phy-ab8500-usb.o -phy-fsl-usb2-objs := phy-fsl-usb.o phy-fsm-usb.o -obj-$(CONFIG_FSL_USB2_OTG) += phy-fsl-usb2.o +obj-$(CONFIG_FSL_USB2_OTG) += phy-fsl-usb.o obj-$(CONFIG_ISP1301_OMAP) += phy-isp1301-omap.o obj-$(CONFIG_MV_U3D_PHY) += phy-mv-u3d-usb.o obj-$(CONFIG_NOP_USB_XCEIV) += phy-generic.o From 5ace3d00fa11bb9ec5e1cc02805ac27201f27e61 Mon Sep 17 00:00:00 2001 From: Bin Liu Date: Wed, 30 Oct 2013 09:38:23 -0500 Subject: [PATCH 018/105] usb: musb: dsps: polling ID pin status only in otg mode Only start the otg_timer in dual role mode; otherwise in peripheral mode when musb is disconnected from the host port, otg_timer starts and continuously toggles the session, which causes VBUS pulse. Signed-off-by: Bin Liu Acked-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_dsps.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 7cfa6e80ad60..593d3c962565 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -359,8 +359,9 @@ static irqreturn_t dsps_interrupt(int irq, void *hci) if (musb->int_tx || musb->int_rx || musb->int_usb) ret |= musb_interrupt(musb); - /* Poll for ID change */ - if (musb->xceiv->state == OTG_STATE_B_IDLE) + /* Poll for ID change in OTG port mode */ + if (musb->xceiv->state == OTG_STATE_B_IDLE && + musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE) mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ); out: spin_unlock_irqrestore(&musb->lock, flags); From a01091e5ce866562ea2638a80f2241d8d6bde164 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 7 Nov 2013 08:41:25 +0100 Subject: [PATCH 019/105] usb: gadget: composite: redirect setup requests If there are setup requests not directed to an endpont or an interface, current config's setup() has been attempted so far. This patch, in case the above fails, adds code which tries the setup() of configuration's function if there is only one function in the configuration. This behavior is required to provide equivalent of gadget zero with configfs. The gadget zero has a "config driver" for sourcesink, but all it does is delegating the request to the function proper. So when the equivalent gadget is set up with configfs it needs to handle requests directed to "config driver", but with configfs it is not possible to specify "config drivers". Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/composite.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 3e7ae707f691..611fe89c3a8b 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1451,8 +1451,22 @@ unknown: struct usb_configuration *c; c = cdev->config; - if (c && c->setup) + if (!c) + goto done; + + /* try current config's setup */ + if (c->setup) { value = c->setup(c, ctrl); + goto done; + } + + /* try the only function in the current config */ + if (!list_is_singular(&c->functions)) + goto done; + f = list_first_entry(&c->functions, struct usb_function, + list); + if (f->setup) + value = f->setup(f, ctrl); } goto done; From 1efd54eab2b60c68c2ce75ea635306cef847d751 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 7 Nov 2013 08:41:26 +0100 Subject: [PATCH 020/105] usb: gadget: factor out alloc_ep_req alloc_ep_req() is a function repeated in several modules. Make a common implementation and use it. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Makefile | 2 +- drivers/usb/gadget/f_hid.c | 18 +++++------------ drivers/usb/gadget/f_loopback.c | 8 +++++++- drivers/usb/gadget/f_midi.c | 22 +++++++-------------- drivers/usb/gadget/f_sourcesink.c | 23 +++++----------------- drivers/usb/gadget/g_zero.h | 1 - drivers/usb/gadget/u_f.c | 32 +++++++++++++++++++++++++++++++ drivers/usb/gadget/u_f.h | 26 +++++++++++++++++++++++++ 8 files changed, 83 insertions(+), 49 deletions(-) create mode 100644 drivers/usb/gadget/u_f.c create mode 100644 drivers/usb/gadget/u_f.h diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index f1af39603d4d..40bd0f80b5ad 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -7,7 +7,7 @@ ccflags-$(CONFIG_USB_GADGET_VERBOSE) += -DVERBOSE_DEBUG obj-$(CONFIG_USB_GADGET) += udc-core.o obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o libcomposite-y := usbstring.o config.o epautoconf.o -libcomposite-y += composite.o functions.o configfs.o +libcomposite-y += composite.o functions.o configfs.o u_f.o obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o obj-$(CONFIG_USB_NET2272) += net2272.o obj-$(CONFIG_USB_NET2280) += net2280.o diff --git a/drivers/usb/gadget/f_hid.c b/drivers/usb/gadget/f_hid.c index 6e69a8e8d22a..a95290a1289f 100644 --- a/drivers/usb/gadget/f_hid.c +++ b/drivers/usb/gadget/f_hid.c @@ -20,6 +20,8 @@ #include #include +#include "u_f.h" + static int major, minors; static struct class *hidg_class; @@ -334,20 +336,10 @@ static int f_hidg_open(struct inode *inode, struct file *fd) /*-------------------------------------------------------------------------*/ /* usb_function */ -static struct usb_request *hidg_alloc_ep_req(struct usb_ep *ep, unsigned length) +static inline struct usb_request *hidg_alloc_ep_req(struct usb_ep *ep, + unsigned length) { - struct usb_request *req; - - req = usb_ep_alloc_request(ep, GFP_ATOMIC); - if (req) { - req->length = length; - req->buf = kmalloc(length, GFP_ATOMIC); - if (!req->buf) { - usb_ep_free_request(ep, req); - req = NULL; - } - } - return req; + return alloc_ep_req(ep, length, length); } static void hidg_set_report_complete(struct usb_ep *ep, struct usb_request *req) diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/f_loopback.c index 4a3873a0f2d0..b79065376a16 100644 --- a/drivers/usb/gadget/f_loopback.c +++ b/drivers/usb/gadget/f_loopback.c @@ -20,6 +20,7 @@ #include #include "g_zero.h" +#include "u_f.h" /* * LOOPBACK FUNCTION ... a testing vehicle for USB peripherals, @@ -293,6 +294,11 @@ static void disable_loopback(struct f_loopback *loop) VDBG(cdev, "%s disabled\n", loop->function.name); } +static inline struct usb_request *lb_alloc_ep_req(struct usb_ep *ep, int len) +{ + return alloc_ep_req(ep, len, buflen); +} + static int enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop) { @@ -332,7 +338,7 @@ fail0: * than 'buflen' bytes each. */ for (i = 0; i < qlen && result == 0; i++) { - req = alloc_ep_req(ep, 0); + req = lb_alloc_ep_req(ep, 0); if (req) { req->complete = loopback_complete; result = usb_ep_queue(ep, req, GFP_ATOMIC); diff --git a/drivers/usb/gadget/f_midi.c b/drivers/usb/gadget/f_midi.c index 263e721c2694..36d4bb23087f 100644 --- a/drivers/usb/gadget/f_midi.c +++ b/drivers/usb/gadget/f_midi.c @@ -32,6 +32,8 @@ #include #include +#include "u_f.h" + MODULE_AUTHOR("Ben Williamson"); MODULE_LICENSE("GPL v2"); @@ -191,20 +193,10 @@ static struct usb_gadget_strings *midi_strings[] = { NULL, }; -static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length) +static inline struct usb_request *midi_alloc_ep_req(struct usb_ep *ep, + unsigned length) { - struct usb_request *req; - - req = usb_ep_alloc_request(ep, GFP_ATOMIC); - if (req) { - req->length = length; - req->buf = kmalloc(length, GFP_ATOMIC); - if (!req->buf) { - usb_ep_free_request(ep, req); - req = NULL; - } - } - return req; + return alloc_ep_req(ep, length, length); } static void free_ep_req(struct usb_ep *ep, struct usb_request *req) @@ -365,7 +357,7 @@ static int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt) /* allocate a bunch of read buffers and queue them all at once. */ for (i = 0; i < midi->qlen && err == 0; i++) { struct usb_request *req = - alloc_ep_req(midi->out_ep, midi->buflen); + midi_alloc_ep_req(midi->out_ep, midi->buflen); if (req == NULL) return -ENOMEM; @@ -546,7 +538,7 @@ static void f_midi_transmit(struct f_midi *midi, struct usb_request *req) return; if (!req) - req = alloc_ep_req(ep, midi->buflen); + req = midi_alloc_ep_req(ep, midi->buflen); if (!req) { ERROR(midi, "gmidi_transmit: alloc_ep_request failed\n"); diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/f_sourcesink.c index a8895859a221..c5ad4a1fa3c7 100644 --- a/drivers/usb/gadget/f_sourcesink.c +++ b/drivers/usb/gadget/f_sourcesink.c @@ -21,6 +21,7 @@ #include "g_zero.h" #include "gadget_chips.h" +#include "u_f.h" /* * SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripheral @@ -301,23 +302,9 @@ static struct usb_gadget_strings *sourcesink_strings[] = { /*-------------------------------------------------------------------------*/ -struct usb_request *alloc_ep_req(struct usb_ep *ep, int len) +static inline struct usb_request *ss_alloc_ep_req(struct usb_ep *ep, int len) { - struct usb_request *req; - - req = usb_ep_alloc_request(ep, GFP_ATOMIC); - if (req) { - if (len) - req->length = len; - else - req->length = buflen; - req->buf = kmalloc(req->length, GFP_ATOMIC); - if (!req->buf) { - usb_ep_free_request(ep, req); - req = NULL; - } - } - return req; + return alloc_ep_req(ep, len, buflen); } void free_ep_req(struct usb_ep *ep, struct usb_request *req) @@ -628,10 +615,10 @@ static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in, break; } ep = is_in ? ss->iso_in_ep : ss->iso_out_ep; - req = alloc_ep_req(ep, size); + req = ss_alloc_ep_req(ep, size); } else { ep = is_in ? ss->in_ep : ss->out_ep; - req = alloc_ep_req(ep, 0); + req = ss_alloc_ep_req(ep, 0); } if (!req) diff --git a/drivers/usb/gadget/g_zero.h b/drivers/usb/gadget/g_zero.h index ef3e8515272b..a1c1a978b5e9 100644 --- a/drivers/usb/gadget/g_zero.h +++ b/drivers/usb/gadget/g_zero.h @@ -36,7 +36,6 @@ void lb_modexit(void); int lb_modinit(void); /* common utilities */ -struct usb_request *alloc_ep_req(struct usb_ep *ep, int len); void free_ep_req(struct usb_ep *ep, struct usb_request *req); void disable_endpoints(struct usb_composite_dev *cdev, struct usb_ep *in, struct usb_ep *out, diff --git a/drivers/usb/gadget/u_f.c b/drivers/usb/gadget/u_f.c new file mode 100644 index 000000000000..63b6642c162b --- /dev/null +++ b/drivers/usb/gadget/u_f.c @@ -0,0 +1,32 @@ +/* + * u_f.c -- USB function utilities for Gadget stack + * + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include "u_f.h" + +struct usb_request *alloc_ep_req(struct usb_ep *ep, int len, int default_len) +{ + struct usb_request *req; + + req = usb_ep_alloc_request(ep, GFP_ATOMIC); + if (req) { + req->length = len ?: default_len; + req->buf = kmalloc(req->length, GFP_ATOMIC); + if (!req->buf) { + usb_ep_free_request(ep, req); + req = NULL; + } + } + return req; +} +EXPORT_SYMBOL(alloc_ep_req); diff --git a/drivers/usb/gadget/u_f.h b/drivers/usb/gadget/u_f.h new file mode 100644 index 000000000000..71034c061fca --- /dev/null +++ b/drivers/usb/gadget/u_f.h @@ -0,0 +1,26 @@ +/* + * u_f.h + * + * Utility definitions for USB functions + * + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __U_F_H__ +#define __U_F_H__ + +struct usb_ep; +struct usb_request; + +struct usb_request *alloc_ep_req(struct usb_ep *ep, int len, int default_len); + +#endif /* __U_F_H__ */ + + From c0501f47c68997ea2933460b9908e6a049c59f21 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 7 Nov 2013 08:41:27 +0100 Subject: [PATCH 021/105] usb: gadget: f_loopback: add configfs support Add support for using the loopback USB function in gadgets composed with configfs. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- .../ABI/testing/configfs-usb-gadget-loopback | 8 ++ drivers/usb/gadget/Kconfig | 12 ++ drivers/usb/gadget/f_loopback.c | 132 ++++++++++++++++++ drivers/usb/gadget/g_zero.h | 12 ++ drivers/usb/gadget/zero.c | 4 +- 5 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 Documentation/ABI/testing/configfs-usb-gadget-loopback diff --git a/Documentation/ABI/testing/configfs-usb-gadget-loopback b/Documentation/ABI/testing/configfs-usb-gadget-loopback new file mode 100644 index 000000000000..852b2365a5b5 --- /dev/null +++ b/Documentation/ABI/testing/configfs-usb-gadget-loopback @@ -0,0 +1,8 @@ +What: /config/usb-gadget/gadget/functions/Loopback.name +Date: Nov 2013 +KenelVersion: 3.13 +Description: + The attributes: + + qlen - depth of loopback queue + bulk_buflen - buffer length diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index a91e6422f930..26fe8769be45 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -689,6 +689,18 @@ config USB_CONFIGFS_MASS_STORAGE device (in much the same way as the "loop" device driver), specified as a module parameter or sysfs option. +config USB_CONFIGFS_F_LB + boolean "Loopback function (for testing)" + depends on USB_CONFIGFS + select USB_F_SS_LB + help + It loops back a configurable number of transfers. + It also implements control requests, for "chapter 9" conformance. + Make this be the first driver you try using on top of any new + USB peripheral controller driver. Then you can use host-side + test software, like the "usbtest" driver, to put your hardware + and its driver through a basic set of functional tests. + config USB_ZERO tristate "Gadget Zero (DEVELOPMENT)" select USB_LIBCOMPOSITE diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/f_loopback.c index b79065376a16..c35bb40cabf2 100644 --- a/drivers/usb/gadget/f_loopback.c +++ b/drivers/usb/gadget/f_loopback.c @@ -231,6 +231,14 @@ autoconf_fail: static void lb_free_func(struct usb_function *f) { + struct f_lb_opts *opts; + + opts = container_of(f->fi, struct f_lb_opts, func_inst); + + mutex_lock(&opts->lock); + opts->refcnt--; + mutex_unlock(&opts->lock); + usb_free_all_descriptors(f); kfree(func_to_loop(f)); } @@ -386,6 +394,11 @@ static struct usb_function *loopback_alloc(struct usb_function_instance *fi) return ERR_PTR(-ENOMEM); lb_opts = container_of(fi, struct f_lb_opts, func_inst); + + mutex_lock(&lb_opts->lock); + lb_opts->refcnt++; + mutex_unlock(&lb_opts->lock); + buflen = lb_opts->bulk_buflen; qlen = lb_opts->qlen; if (!qlen) @@ -402,6 +415,118 @@ static struct usb_function *loopback_alloc(struct usb_function_instance *fi) return &loop->function; } +static inline struct f_lb_opts *to_f_lb_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_lb_opts, + func_inst.group); +} + +CONFIGFS_ATTR_STRUCT(f_lb_opts); +CONFIGFS_ATTR_OPS(f_lb_opts); + +static void lb_attr_release(struct config_item *item) +{ + struct f_lb_opts *lb_opts = to_f_lb_opts(item); + + usb_put_function_instance(&lb_opts->func_inst); +} + +static struct configfs_item_operations lb_item_ops = { + .release = lb_attr_release, + .show_attribute = f_lb_opts_attr_show, + .store_attribute = f_lb_opts_attr_store, +}; + +static ssize_t f_lb_opts_qlen_show(struct f_lb_opts *opts, char *page) +{ + int result; + + mutex_lock(&opts->lock); + result = sprintf(page, "%d", opts->qlen); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t f_lb_opts_qlen_store(struct f_lb_opts *opts, + const char *page, size_t len) +{ + int ret; + u32 num; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + + ret = kstrtou32(page, 0, &num); + if (ret) + goto end; + + opts->qlen = num; + ret = len; +end: + mutex_unlock(&opts->lock); + return ret; +} + +static struct f_lb_opts_attribute f_lb_opts_qlen = + __CONFIGFS_ATTR(qlen, S_IRUGO | S_IWUSR, + f_lb_opts_qlen_show, + f_lb_opts_qlen_store); + +static ssize_t f_lb_opts_bulk_buflen_show(struct f_lb_opts *opts, char *page) +{ + int result; + + mutex_lock(&opts->lock); + result = sprintf(page, "%d", opts->bulk_buflen); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t f_lb_opts_bulk_buflen_store(struct f_lb_opts *opts, + const char *page, size_t len) +{ + int ret; + u32 num; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + + ret = kstrtou32(page, 0, &num); + if (ret) + goto end; + + opts->bulk_buflen = num; + ret = len; +end: + mutex_unlock(&opts->lock); + return ret; +} + +static struct f_lb_opts_attribute f_lb_opts_bulk_buflen = + __CONFIGFS_ATTR(buflen, S_IRUGO | S_IWUSR, + f_lb_opts_bulk_buflen_show, + f_lb_opts_bulk_buflen_store); + +static struct configfs_attribute *lb_attrs[] = { + &f_lb_opts_qlen.attr, + &f_lb_opts_bulk_buflen.attr, + NULL, +}; + +static struct config_item_type lb_func_type = { + .ct_item_ops = &lb_item_ops, + .ct_attrs = lb_attrs, + .ct_owner = THIS_MODULE, +}; + static void lb_free_instance(struct usb_function_instance *fi) { struct f_lb_opts *lb_opts; @@ -417,7 +542,14 @@ static struct usb_function_instance *loopback_alloc_instance(void) lb_opts = kzalloc(sizeof(*lb_opts), GFP_KERNEL); if (!lb_opts) return ERR_PTR(-ENOMEM); + mutex_init(&lb_opts->lock); lb_opts->func_inst.free_func_inst = lb_free_instance; + lb_opts->bulk_buflen = GZERO_BULK_BUFLEN; + lb_opts->qlen = GZERO_QLEN; + + config_group_init_type_name(&lb_opts->func_inst.group, "", + &lb_func_type); + return &lb_opts->func_inst; } DECLARE_USB_FUNCTION(Loopback, loopback_alloc_instance, loopback_alloc); diff --git a/drivers/usb/gadget/g_zero.h b/drivers/usb/gadget/g_zero.h index a1c1a978b5e9..19ec50a42a74 100644 --- a/drivers/usb/gadget/g_zero.h +++ b/drivers/usb/gadget/g_zero.h @@ -6,6 +6,9 @@ #ifndef __G_ZERO_H #define __G_ZERO_H +#define GZERO_BULK_BUFLEN 4096 +#define GZERO_QLEN 32 + struct usb_zero_options { unsigned pattern; unsigned isoc_interval; @@ -30,6 +33,15 @@ struct f_lb_opts { struct usb_function_instance func_inst; unsigned bulk_buflen; unsigned qlen; + + /* + * Read/write access to configfs attributes is handled by configfs. + * + * This is to protect the data from concurrent access by read/write + * and create symlink/remove symlink. + */ + struct mutex lock; + int refcnt; }; void lb_modexit(void); diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c index 0dd07ae1555d..d954bba7b405 100644 --- a/drivers/usb/gadget/zero.c +++ b/drivers/usb/gadget/zero.c @@ -66,8 +66,8 @@ module_param(loopdefault, bool, S_IRUGO|S_IWUSR); static struct usb_zero_options gzero_options = { .isoc_interval = 4, .isoc_maxpacket = 1024, - .bulk_buflen = 4096, - .qlen = 32, + .bulk_buflen = GZERO_BULK_BUFLEN, + .qlen = GZERO_QLEN, }; /*-------------------------------------------------------------------------*/ From 25d8015177ae7baedb8bdc76dffc0884b0d785a3 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 7 Nov 2013 08:41:28 +0100 Subject: [PATCH 022/105] usb: gadget: f_sourcesink: add configfs support Add support for using the sourcesink function in gadgets composed with configfs. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- .../testing/configfs-usb-gadget-sourcesink | 12 + drivers/usb/gadget/Kconfig | 7 +- drivers/usb/gadget/f_sourcesink.c | 318 ++++++++++++++++++ drivers/usb/gadget/g_zero.h | 11 + drivers/usb/gadget/zero.c | 4 +- 5 files changed, 347 insertions(+), 5 deletions(-) create mode 100644 Documentation/ABI/testing/configfs-usb-gadget-sourcesink diff --git a/Documentation/ABI/testing/configfs-usb-gadget-sourcesink b/Documentation/ABI/testing/configfs-usb-gadget-sourcesink new file mode 100644 index 000000000000..a30f3093ef6c --- /dev/null +++ b/Documentation/ABI/testing/configfs-usb-gadget-sourcesink @@ -0,0 +1,12 @@ +What: /config/usb-gadget/gadget/functions/SourceSink.name +Date: Nov 2013 +KenelVersion: 3.13 +Description: + The attributes: + + pattern - 0 (all zeros), 1 (mod63), 2 (none) + isoc_interval - 1..16 + isoc_maxpacket - 0 - 1023 (fs), 0 - 1024 (hs/ss) + isoc_mult - 0..2 (hs/ss only) + isoc_maxburst - 0..15 (ss only) + qlen - buffer length diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 26fe8769be45..5f1d4443aa0c 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -689,12 +689,13 @@ config USB_CONFIGFS_MASS_STORAGE device (in much the same way as the "loop" device driver), specified as a module parameter or sysfs option. -config USB_CONFIGFS_F_LB - boolean "Loopback function (for testing)" +config USB_CONFIGFS_F_LB_SS + boolean "Loopback and sourcesink function (for testing)" depends on USB_CONFIGFS select USB_F_SS_LB help - It loops back a configurable number of transfers. + Loopback function loops back a configurable number of transfers. + Sourcesink function either sinks and sources bulk data. It also implements control requests, for "chapter 9" conformance. Make this be the first driver you try using on top of any new USB peripheral controller driver. Then you can use host-side diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/f_sourcesink.c index c5ad4a1fa3c7..5d4251e7a377 100644 --- a/drivers/usb/gadget/f_sourcesink.c +++ b/drivers/usb/gadget/f_sourcesink.c @@ -477,6 +477,14 @@ no_iso: static void sourcesink_free_func(struct usb_function *f) { + struct f_ss_opts *opts; + + opts = container_of(f->fi, struct f_ss_opts, func_inst); + + mutex_lock(&opts->lock); + opts->refcnt--; + mutex_unlock(&opts->lock); + usb_free_all_descriptors(f); kfree(func_to_ss(f)); } @@ -865,6 +873,11 @@ static struct usb_function *source_sink_alloc_func( return NULL; ss_opts = container_of(fi, struct f_ss_opts, func_inst); + + mutex_lock(&ss_opts->lock); + ss_opts->refcnt++; + mutex_unlock(&ss_opts->lock); + pattern = ss_opts->pattern; isoc_interval = ss_opts->isoc_interval; isoc_maxpacket = ss_opts->isoc_maxpacket; @@ -885,6 +898,303 @@ static struct usb_function *source_sink_alloc_func( return &ss->function; } +static inline struct f_ss_opts *to_f_ss_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_ss_opts, + func_inst.group); +} + +CONFIGFS_ATTR_STRUCT(f_ss_opts); +CONFIGFS_ATTR_OPS(f_ss_opts); + +static void ss_attr_release(struct config_item *item) +{ + struct f_ss_opts *ss_opts = to_f_ss_opts(item); + + usb_put_function_instance(&ss_opts->func_inst); +} + +static struct configfs_item_operations ss_item_ops = { + .release = ss_attr_release, + .show_attribute = f_ss_opts_attr_show, + .store_attribute = f_ss_opts_attr_store, +}; + +static ssize_t f_ss_opts_pattern_show(struct f_ss_opts *opts, char *page) +{ + int result; + + mutex_lock(&opts->lock); + result = sprintf(page, "%d", opts->pattern); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t f_ss_opts_pattern_store(struct f_ss_opts *opts, + const char *page, size_t len) +{ + int ret; + u8 num; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + + ret = kstrtou8(page, 0, &num); + if (ret) + goto end; + + if (num != 0 && num != 1 && num != 2) { + ret = -EINVAL; + goto end; + } + + opts->pattern = num; + ret = len; +end: + mutex_unlock(&opts->lock); + return ret; +} + +static struct f_ss_opts_attribute f_ss_opts_pattern = + __CONFIGFS_ATTR(pattern, S_IRUGO | S_IWUSR, + f_ss_opts_pattern_show, + f_ss_opts_pattern_store); + +static ssize_t f_ss_opts_isoc_interval_show(struct f_ss_opts *opts, char *page) +{ + int result; + + mutex_lock(&opts->lock); + result = sprintf(page, "%d", opts->isoc_interval); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t f_ss_opts_isoc_interval_store(struct f_ss_opts *opts, + const char *page, size_t len) +{ + int ret; + u8 num; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + + ret = kstrtou8(page, 0, &num); + if (ret) + goto end; + + if (num > 16) { + ret = -EINVAL; + goto end; + } + + opts->isoc_interval = num; + ret = len; +end: + mutex_unlock(&opts->lock); + return ret; +} + +static struct f_ss_opts_attribute f_ss_opts_isoc_interval = + __CONFIGFS_ATTR(isoc_interval, S_IRUGO | S_IWUSR, + f_ss_opts_isoc_interval_show, + f_ss_opts_isoc_interval_store); + +static ssize_t f_ss_opts_isoc_maxpacket_show(struct f_ss_opts *opts, char *page) +{ + int result; + + mutex_lock(&opts->lock); + result = sprintf(page, "%d", opts->isoc_maxpacket); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t f_ss_opts_isoc_maxpacket_store(struct f_ss_opts *opts, + const char *page, size_t len) +{ + int ret; + u16 num; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + + ret = kstrtou16(page, 0, &num); + if (ret) + goto end; + + if (num > 1024) { + ret = -EINVAL; + goto end; + } + + opts->isoc_maxpacket = num; + ret = len; +end: + mutex_unlock(&opts->lock); + return ret; +} + +static struct f_ss_opts_attribute f_ss_opts_isoc_maxpacket = + __CONFIGFS_ATTR(isoc_maxpacket, S_IRUGO | S_IWUSR, + f_ss_opts_isoc_maxpacket_show, + f_ss_opts_isoc_maxpacket_store); + +static ssize_t f_ss_opts_isoc_mult_show(struct f_ss_opts *opts, char *page) +{ + int result; + + mutex_lock(&opts->lock); + result = sprintf(page, "%d", opts->isoc_mult); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t f_ss_opts_isoc_mult_store(struct f_ss_opts *opts, + const char *page, size_t len) +{ + int ret; + u8 num; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + + ret = kstrtou8(page, 0, &num); + if (ret) + goto end; + + if (num > 2) { + ret = -EINVAL; + goto end; + } + + opts->isoc_mult = num; + ret = len; +end: + mutex_unlock(&opts->lock); + return ret; +} + +static struct f_ss_opts_attribute f_ss_opts_isoc_mult = + __CONFIGFS_ATTR(isoc_mult, S_IRUGO | S_IWUSR, + f_ss_opts_isoc_mult_show, + f_ss_opts_isoc_mult_store); + +static ssize_t f_ss_opts_isoc_maxburst_show(struct f_ss_opts *opts, char *page) +{ + int result; + + mutex_lock(&opts->lock); + result = sprintf(page, "%d", opts->isoc_maxburst); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t f_ss_opts_isoc_maxburst_store(struct f_ss_opts *opts, + const char *page, size_t len) +{ + int ret; + u8 num; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + + ret = kstrtou8(page, 0, &num); + if (ret) + goto end; + + if (num > 15) { + ret = -EINVAL; + goto end; + } + + opts->isoc_maxburst = num; + ret = len; +end: + mutex_unlock(&opts->lock); + return ret; +} + +static struct f_ss_opts_attribute f_ss_opts_isoc_maxburst = + __CONFIGFS_ATTR(isoc_maxburst, S_IRUGO | S_IWUSR, + f_ss_opts_isoc_maxburst_show, + f_ss_opts_isoc_maxburst_store); + +static ssize_t f_ss_opts_bulk_buflen_show(struct f_ss_opts *opts, char *page) +{ + int result; + + mutex_lock(&opts->lock); + result = sprintf(page, "%d", opts->bulk_buflen); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t f_ss_opts_bulk_buflen_store(struct f_ss_opts *opts, + const char *page, size_t len) +{ + int ret; + u32 num; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + + ret = kstrtou32(page, 0, &num); + if (ret) + goto end; + + opts->bulk_buflen = num; + ret = len; +end: + mutex_unlock(&opts->lock); + return ret; +} + +static struct f_ss_opts_attribute f_ss_opts_bulk_buflen = + __CONFIGFS_ATTR(buflen, S_IRUGO | S_IWUSR, + f_ss_opts_bulk_buflen_show, + f_ss_opts_bulk_buflen_store); + +static struct configfs_attribute *ss_attrs[] = { + &f_ss_opts_pattern.attr, + &f_ss_opts_isoc_interval.attr, + &f_ss_opts_isoc_maxpacket.attr, + &f_ss_opts_isoc_mult.attr, + &f_ss_opts_isoc_maxburst.attr, + &f_ss_opts_bulk_buflen.attr, + NULL, +}; + +static struct config_item_type ss_func_type = { + .ct_item_ops = &ss_item_ops, + .ct_attrs = ss_attrs, + .ct_owner = THIS_MODULE, +}; + static void source_sink_free_instance(struct usb_function_instance *fi) { struct f_ss_opts *ss_opts; @@ -900,7 +1210,15 @@ static struct usb_function_instance *source_sink_alloc_inst(void) ss_opts = kzalloc(sizeof(*ss_opts), GFP_KERNEL); if (!ss_opts) return ERR_PTR(-ENOMEM); + mutex_init(&ss_opts->lock); ss_opts->func_inst.free_func_inst = source_sink_free_instance; + ss_opts->isoc_interval = GZERO_ISOC_INTERVAL; + ss_opts->isoc_maxpacket = GZERO_ISOC_MAXPACKET; + ss_opts->bulk_buflen = GZERO_BULK_BUFLEN; + + config_group_init_type_name(&ss_opts->func_inst.group, "", + &ss_func_type); + return &ss_opts->func_inst; } DECLARE_USB_FUNCTION(SourceSink, source_sink_alloc_inst, diff --git a/drivers/usb/gadget/g_zero.h b/drivers/usb/gadget/g_zero.h index 19ec50a42a74..15f180904f8a 100644 --- a/drivers/usb/gadget/g_zero.h +++ b/drivers/usb/gadget/g_zero.h @@ -8,6 +8,8 @@ #define GZERO_BULK_BUFLEN 4096 #define GZERO_QLEN 32 +#define GZERO_ISOC_INTERVAL 4 +#define GZERO_ISOC_MAXPACKET 1024 struct usb_zero_options { unsigned pattern; @@ -27,6 +29,15 @@ struct f_ss_opts { unsigned isoc_mult; unsigned isoc_maxburst; unsigned bulk_buflen; + + /* + * Read/write access to configfs attributes is handled by configfs. + * + * This is to protect the data from concurrent access by read/write + * and create symlink/remove symlink. + */ + struct mutex lock; + int refcnt; }; struct f_lb_opts { diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c index d954bba7b405..00b401906a32 100644 --- a/drivers/usb/gadget/zero.c +++ b/drivers/usb/gadget/zero.c @@ -64,8 +64,8 @@ static bool loopdefault = 0; module_param(loopdefault, bool, S_IRUGO|S_IWUSR); static struct usb_zero_options gzero_options = { - .isoc_interval = 4, - .isoc_maxpacket = 1024, + .isoc_interval = GZERO_ISOC_INTERVAL, + .isoc_maxpacket = GZERO_ISOC_MAXPACKET, .bulk_buflen = GZERO_BULK_BUFLEN, .qlen = GZERO_QLEN, }; From 782df20c5f2ad45bd453e1f0fb58a49f251845b4 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 28 Nov 2013 14:15:46 +0900 Subject: [PATCH 023/105] usb: dwc3: pci: remove DEFINE_PCI_DEVICE_TABLE macro Don't use DEFINE_PCI_DEVICE_TABLE macro, because this macro is not preferred. Signed-off-by: Jingoo Han Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 31443aeedcdb..665686e7c8cd 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -182,7 +182,7 @@ static void dwc3_pci_remove(struct pci_dev *pci) pci_disable_device(pci); } -static DEFINE_PCI_DEVICE_TABLE(dwc3_pci_id_table) = { +static const struct pci_device_id dwc3_pci_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3), From ea25c37ea85b9d2b0ed3698f40753da005cfd4dc Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 28 Nov 2013 14:16:09 +0900 Subject: [PATCH 024/105] usb: gadget: pch_udc: remove DEFINE_PCI_DEVICE_TABLE macro Don't use DEFINE_PCI_DEVICE_TABLE macro, because this macro is not preferred. Signed-off-by: Jingoo Han Signed-off-by: Felipe Balbi --- drivers/usb/gadget/pch_udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c index 32d5e923750b..78a3d9289816 100644 --- a/drivers/usb/gadget/pch_udc.c +++ b/drivers/usb/gadget/pch_udc.c @@ -3210,7 +3210,7 @@ finished: return retval; } -static DEFINE_PCI_DEVICE_TABLE(pch_udc_pcidev_id) = { +static const struct pci_device_id pch_udc_pcidev_id[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EG20T_UDC), .class = (PCI_CLASS_SERIAL_USB << 8) | 0xfe, From 9510ecee62dff8cc603ea163bf72729c491fbe5b Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 28 Nov 2013 14:16:30 +0900 Subject: [PATCH 025/105] usb: gadget: amd5536udc: remove DEFINE_PCI_DEVICE_TABLE macro Don't use DEFINE_PCI_DEVICE_TABLE macro, because this macro is not preferred. Signed-off-by: Jingoo Han Signed-off-by: Felipe Balbi --- drivers/usb/gadget/amd5536udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index 54a1e2954cea..f0ff4a675e9d 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -3338,7 +3338,7 @@ static int udc_remote_wakeup(struct udc *dev) } /* PCI device parameters */ -static DEFINE_PCI_DEVICE_TABLE(pci_id) = { +static const struct pci_device_id pci_id[] = { { PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x2096), .class = (PCI_CLASS_SERIAL_USB << 8) | 0xfe, From 67c21fc803de62369dfc8e41dab352107d7f06ef Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 2 Dec 2013 01:02:34 -0200 Subject: [PATCH 026/105] usb: phy: phy-mxs-usb: Check the return value from clk_prepare_enable() clk_prepare_enable() may fail, so let's check its return value and propagate it in the case of error. Acked-by: Peter Chen Signed-off-by: Fabio Estevam Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-mxs-usb.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index fdd33b44dbd3..797c45b9ddab 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -63,9 +63,13 @@ static int mxs_phy_hw_init(struct mxs_phy *mxs_phy) static int mxs_phy_init(struct usb_phy *phy) { + int ret; struct mxs_phy *mxs_phy = to_mxs_phy(phy); - clk_prepare_enable(mxs_phy->clk); + ret = clk_prepare_enable(mxs_phy->clk); + if (ret) + return ret; + return mxs_phy_hw_init(mxs_phy); } @@ -81,6 +85,7 @@ static void mxs_phy_shutdown(struct usb_phy *phy) static int mxs_phy_suspend(struct usb_phy *x, int suspend) { + int ret; struct mxs_phy *mxs_phy = to_mxs_phy(x); if (suspend) { @@ -89,7 +94,9 @@ static int mxs_phy_suspend(struct usb_phy *x, int suspend) x->io_priv + HW_USBPHY_CTRL_SET); clk_disable_unprepare(mxs_phy->clk); } else { - clk_prepare_enable(mxs_phy->clk); + ret = clk_prepare_enable(mxs_phy->clk); + if (ret) + return ret; writel(BM_USBPHY_CTRL_CLKGATE, x->io_priv + HW_USBPHY_CTRL_CLR); writel(0, x->io_priv + HW_USBPHY_PWD); From 317a3fa6b0814e31de5eb083bd1d047d66485d09 Mon Sep 17 00:00:00 2001 From: Aaro Koskinen Date: Fri, 6 Dec 2013 16:13:04 +0200 Subject: [PATCH 027/105] ARM: OMAP1: USB: move omap_usb_config to platform data Move omap_usb_config to platform data, so that OTG driver can include it. Signed-off-by: Aaro Koskinen Acked-by: Tony Lindgren Signed-off-by: Felipe Balbi --- arch/arm/mach-omap1/include/mach/usb.h | 38 +----------------- include/linux/platform_data/usb-omap1.h | 51 +++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 37 deletions(-) create mode 100644 include/linux/platform_data/usb-omap1.h diff --git a/arch/arm/mach-omap1/include/mach/usb.h b/arch/arm/mach-omap1/include/mach/usb.h index 45e5ac707cbb..2c263051dc51 100644 --- a/arch/arm/mach-omap1/include/mach/usb.h +++ b/arch/arm/mach-omap1/include/mach/usb.h @@ -8,43 +8,7 @@ #define is_usb0_device(config) 0 #endif -struct omap_usb_config { - /* Configure drivers according to the connectors on your board: - * - "A" connector (rectagular) - * ... for host/OHCI use, set "register_host". - * - "B" connector (squarish) or "Mini-B" - * ... for device/gadget use, set "register_dev". - * - "Mini-AB" connector (very similar to Mini-B) - * ... for OTG use as device OR host, initialize "otg" - */ - unsigned register_host:1; - unsigned register_dev:1; - u8 otg; /* port number, 1-based: usb1 == 2 */ - - u8 hmc_mode; - - /* implicitly true if otg: host supports remote wakeup? */ - u8 rwc; - - /* signaling pins used to talk to transceiver on usbN: - * 0 == usbN unused - * 2 == usb0-only, using internal transceiver - * 3 == 3 wire bidirectional - * 4 == 4 wire bidirectional - * 6 == 6 wire unidirectional (or TLL) - */ - u8 pins[3]; - - struct platform_device *udc_device; - struct platform_device *ohci_device; - struct platform_device *otg_device; - - u32 (*usb0_init)(unsigned nwires, unsigned is_device); - u32 (*usb1_init)(unsigned nwires); - u32 (*usb2_init)(unsigned nwires, unsigned alt_pingroup); - - int (*ocpi_enable)(void); -}; +#include void omap_otg_init(struct omap_usb_config *config); diff --git a/include/linux/platform_data/usb-omap1.h b/include/linux/platform_data/usb-omap1.h new file mode 100644 index 000000000000..8c7764ddd284 --- /dev/null +++ b/include/linux/platform_data/usb-omap1.h @@ -0,0 +1,51 @@ +/* + * Platform data for OMAP1 USB + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive for + * more details. + */ +#ifndef __LINUX_USB_OMAP1_H +#define __LINUX_USB_OMAP1_H + +#include + +struct omap_usb_config { + /* Configure drivers according to the connectors on your board: + * - "A" connector (rectagular) + * ... for host/OHCI use, set "register_host". + * - "B" connector (squarish) or "Mini-B" + * ... for device/gadget use, set "register_dev". + * - "Mini-AB" connector (very similar to Mini-B) + * ... for OTG use as device OR host, initialize "otg" + */ + unsigned register_host:1; + unsigned register_dev:1; + u8 otg; /* port number, 1-based: usb1 == 2 */ + + u8 hmc_mode; + + /* implicitly true if otg: host supports remote wakeup? */ + u8 rwc; + + /* signaling pins used to talk to transceiver on usbN: + * 0 == usbN unused + * 2 == usb0-only, using internal transceiver + * 3 == 3 wire bidirectional + * 4 == 4 wire bidirectional + * 6 == 6 wire unidirectional (or TLL) + */ + u8 pins[3]; + + struct platform_device *udc_device; + struct platform_device *ohci_device; + struct platform_device *otg_device; + + u32 (*usb0_init)(unsigned nwires, unsigned is_device); + u32 (*usb1_init)(unsigned nwires); + u32 (*usb2_init)(unsigned nwires, unsigned alt_pingroup); + + int (*ocpi_enable)(void); +}; + +#endif /* __LINUX_USB_OMAP1_H */ From 339e008895a912a5049e076c9d27b29eb7ce80a7 Mon Sep 17 00:00:00 2001 From: Aaro Koskinen Date: Fri, 6 Dec 2013 16:13:05 +0200 Subject: [PATCH 028/105] usb: omap1: add extcon to platform data Add extcon field to platform data. Signed-off-by: Aaro Koskinen Signed-off-by: Felipe Balbi --- include/linux/platform_data/usb-omap1.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/platform_data/usb-omap1.h b/include/linux/platform_data/usb-omap1.h index 8c7764ddd284..43b5ce139c37 100644 --- a/include/linux/platform_data/usb-omap1.h +++ b/include/linux/platform_data/usb-omap1.h @@ -23,6 +23,8 @@ struct omap_usb_config { unsigned register_dev:1; u8 otg; /* port number, 1-based: usb1 == 2 */ + const char *extcon; /* extcon device for OTG */ + u8 hmc_mode; /* implicitly true if otg: host supports remote wakeup? */ From 449d2ba613a551046544ea75b99563ee643c8d7c Mon Sep 17 00:00:00 2001 From: Aaro Koskinen Date: Fri, 6 Dec 2013 16:13:06 +0200 Subject: [PATCH 029/105] usb: omap1: OTG controller driver Transceivers need to manage OTG controller state on OMAP1 to enable switching between peripheral and host modes. Provide a driver for that. Signed-off-by: Aaro Koskinen Signed-off-by: Felipe Balbi --- drivers/usb/phy/Kconfig | 10 ++ drivers/usb/phy/Makefile | 1 + drivers/usb/phy/phy-omap-otg.c | 169 +++++++++++++++++++++++++++++++++ 3 files changed, 180 insertions(+) create mode 100644 drivers/usb/phy/phy-omap-otg.c diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 921a9f43e309..0dbab6f5c2d4 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -142,6 +142,16 @@ config USB_GPIO_VBUS optionally control of a D+ pullup GPIO as well as a VBUS current limit regulator. +config OMAP_OTG + tristate "OMAP USB OTG controller driver" + depends on ARCH_OMAP_OTG && EXTCON + help + Enable this to support some transceivers on OMAP1 platforms. OTG + controller is needed to switch between host and peripheral modes. + + This driver can also be built as a module. If so, the module + will be called omap-otg. + config USB_ISP1301 tristate "NXP ISP1301 USB transceiver support" depends on USB || USB_GADGET diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index 1bf6c083a36e..64a9345e5633 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_NOP_USB_XCEIV) += phy-generic.o obj-$(CONFIG_OMAP_CONTROL_USB) += phy-omap-control.o obj-$(CONFIG_AM335X_CONTROL_USB) += phy-am335x-control.o obj-$(CONFIG_AM335X_PHY_USB) += phy-am335x.o +obj-$(CONFIG_OMAP_OTG) += phy-omap-otg.o obj-$(CONFIG_OMAP_USB3) += phy-omap-usb3.o obj-$(CONFIG_SAMSUNG_USBPHY) += phy-samsung-usb.o obj-$(CONFIG_SAMSUNG_USB2PHY) += phy-samsung-usb2.o diff --git a/drivers/usb/phy/phy-omap-otg.c b/drivers/usb/phy/phy-omap-otg.c new file mode 100644 index 000000000000..11598cdb3189 --- /dev/null +++ b/drivers/usb/phy/phy-omap-otg.c @@ -0,0 +1,169 @@ +/* + * OMAP OTG controller driver + * + * Based on code from tahvo-usb.c and isp1301_omap.c drivers. + * + * Copyright (C) 2005-2006 Nokia Corporation + * Copyright (C) 2004 Texas Instruments + * Copyright (C) 2004 David Brownell + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct otg_device { + void __iomem *base; + bool id; + bool vbus; + struct extcon_specific_cable_nb vbus_dev; + struct extcon_specific_cable_nb id_dev; + struct notifier_block vbus_nb; + struct notifier_block id_nb; +}; + +#define OMAP_OTG_CTRL 0x0c +#define OMAP_OTG_ASESSVLD (1 << 20) +#define OMAP_OTG_BSESSEND (1 << 19) +#define OMAP_OTG_BSESSVLD (1 << 18) +#define OMAP_OTG_VBUSVLD (1 << 17) +#define OMAP_OTG_ID (1 << 16) +#define OMAP_OTG_XCEIV_OUTPUTS \ + (OMAP_OTG_ASESSVLD | OMAP_OTG_BSESSEND | OMAP_OTG_BSESSVLD | \ + OMAP_OTG_VBUSVLD | OMAP_OTG_ID) + +static void omap_otg_ctrl(struct otg_device *otg_dev, u32 outputs) +{ + u32 l; + + l = readl(otg_dev->base + OMAP_OTG_CTRL); + l &= ~OMAP_OTG_XCEIV_OUTPUTS; + l |= outputs; + writel(l, otg_dev->base + OMAP_OTG_CTRL); +} + +static void omap_otg_set_mode(struct otg_device *otg_dev) +{ + if (!otg_dev->id && otg_dev->vbus) + /* Set B-session valid. */ + omap_otg_ctrl(otg_dev, OMAP_OTG_ID | OMAP_OTG_BSESSVLD); + else if (otg_dev->vbus) + /* Set A-session valid. */ + omap_otg_ctrl(otg_dev, OMAP_OTG_ASESSVLD); + else if (!otg_dev->id) + /* Set B-session end to indicate no VBUS. */ + omap_otg_ctrl(otg_dev, OMAP_OTG_ID | OMAP_OTG_BSESSEND); +} + +static int omap_otg_id_notifier(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct otg_device *otg_dev = container_of(nb, struct otg_device, id_nb); + + otg_dev->id = event; + omap_otg_set_mode(otg_dev); + + return NOTIFY_DONE; +} + +static int omap_otg_vbus_notifier(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct otg_device *otg_dev = container_of(nb, struct otg_device, + vbus_nb); + + otg_dev->vbus = event; + omap_otg_set_mode(otg_dev); + + return NOTIFY_DONE; +} + +static int omap_otg_probe(struct platform_device *pdev) +{ + const struct omap_usb_config *config = pdev->dev.platform_data; + struct otg_device *otg_dev; + struct extcon_dev *extcon; + int ret; + u32 rev; + + if (!config || !config->extcon) + return -ENODEV; + + extcon = extcon_get_extcon_dev(config->extcon); + if (!extcon) + return -EPROBE_DEFER; + + otg_dev = devm_kzalloc(&pdev->dev, sizeof(*otg_dev), GFP_KERNEL); + if (!otg_dev) + return -ENOMEM; + + otg_dev->base = devm_ioremap_resource(&pdev->dev, &pdev->resource[0]); + if (IS_ERR(otg_dev->base)) + return PTR_ERR(otg_dev->base); + + otg_dev->id_nb.notifier_call = omap_otg_id_notifier; + otg_dev->vbus_nb.notifier_call = omap_otg_vbus_notifier; + + ret = extcon_register_interest(&otg_dev->id_dev, config->extcon, + "USB-HOST", &otg_dev->id_nb); + if (ret) + return ret; + + ret = extcon_register_interest(&otg_dev->vbus_dev, config->extcon, + "USB", &otg_dev->vbus_nb); + if (ret) { + extcon_unregister_interest(&otg_dev->id_dev); + return ret; + } + + otg_dev->id = extcon_get_cable_state(extcon, "USB-HOST"); + otg_dev->vbus = extcon_get_cable_state(extcon, "USB"); + omap_otg_set_mode(otg_dev); + + rev = readl(otg_dev->base); + + dev_info(&pdev->dev, + "OMAP USB OTG controller rev %d.%d (%s, id=%d, vbus=%d)\n", + (rev >> 4) & 0xf, rev & 0xf, config->extcon, otg_dev->id, + otg_dev->vbus); + + return 0; +} + +static int omap_otg_remove(struct platform_device *pdev) +{ + struct otg_device *otg_dev = platform_get_drvdata(pdev); + + extcon_unregister_interest(&otg_dev->id_dev); + extcon_unregister_interest(&otg_dev->vbus_dev); + + return 0; +} + +static struct platform_driver omap_otg_driver = { + .probe = omap_otg_probe, + .remove = omap_otg_remove, + .driver = { + .owner = THIS_MODULE, + .name = "omap_otg", + }, +}; +module_platform_driver(omap_otg_driver); + +MODULE_DESCRIPTION("OMAP USB OTG controller driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Aaro Koskinen "); From 9ba96ae5074c9f15b357919e704ceba2bd34972d Mon Sep 17 00:00:00 2001 From: Aaro Koskinen Date: Fri, 6 Dec 2013 16:13:07 +0200 Subject: [PATCH 030/105] usb: omap1: Tahvo USB transceiver driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Tahvo USB transceiver driver. Based on old code from linux-omap tree. The original driver was written by Juha Yrjölä, Tony Lindgren, and Timo Teräs. Signed-off-by: Aaro Koskinen Signed-off-by: Felipe Balbi --- .../ABI/testing/sysfs-platform-tahvo-usb | 16 + drivers/usb/phy/Kconfig | 15 + drivers/usb/phy/Makefile | 1 + drivers/usb/phy/phy-tahvo.c | 463 ++++++++++++++++++ 4 files changed, 495 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-platform-tahvo-usb create mode 100644 drivers/usb/phy/phy-tahvo.c diff --git a/Documentation/ABI/testing/sysfs-platform-tahvo-usb b/Documentation/ABI/testing/sysfs-platform-tahvo-usb new file mode 100644 index 000000000000..f6e20ce4b538 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-platform-tahvo-usb @@ -0,0 +1,16 @@ +What: /sys/bus/platform/devices/tahvo-usb/otg_mode +Date: December 2013 +Contact: Aaro Koskinen +Description: + Set or read the current OTG mode. Valid values are "host" and + "peripheral". + + Reading: returns the current mode. + +What: /sys/bus/platform/devices/tahvo-usb/vbus +Date: December 2013 +Contact: Aaro Koskinen +Description: + Read the current VBUS state. + + Reading: returns "on" or "off". diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 0dbab6f5c2d4..4f22762f3d6f 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -152,6 +152,21 @@ config OMAP_OTG This driver can also be built as a module. If so, the module will be called omap-otg. +config TAHVO_USB + tristate "Tahvo USB transceiver driver" + depends on MFD_RETU && EXTCON + select USB_PHY + help + Enable this to support USB transceiver on Tahvo. This is used + at least on Nokia 770. + +config TAHVO_USB_HOST_BY_DEFAULT + depends on TAHVO_USB + boolean "Device in USB host mode by default" + help + Say Y here, if you want the device to enter USB host mode + by default on bootup. + config USB_ISP1301 tristate "NXP ISP1301 USB transceiver support" depends on USB || USB_GADGET diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index 64a9345e5633..9b3be9e0aeb4 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_FSL_USB2_OTG) += phy-fsl-usb.o obj-$(CONFIG_ISP1301_OMAP) += phy-isp1301-omap.o obj-$(CONFIG_MV_U3D_PHY) += phy-mv-u3d-usb.o obj-$(CONFIG_NOP_USB_XCEIV) += phy-generic.o +obj-$(CONFIG_TAHVO_USB) += phy-tahvo.o obj-$(CONFIG_OMAP_CONTROL_USB) += phy-omap-control.o obj-$(CONFIG_AM335X_CONTROL_USB) += phy-am335x-control.o obj-$(CONFIG_AM335X_PHY_USB) += phy-am335x.o diff --git a/drivers/usb/phy/phy-tahvo.c b/drivers/usb/phy/phy-tahvo.c new file mode 100644 index 000000000000..8bb833e22d64 --- /dev/null +++ b/drivers/usb/phy/phy-tahvo.c @@ -0,0 +1,463 @@ +/* + * Tahvo USB transceiver driver + * + * Copyright (C) 2005-2006 Nokia Corporation + * + * Parts copied from isp1301_omap.c. + * Copyright (C) 2004 Texas Instruments + * Copyright (C) 2004 David Brownell + * + * Original driver written by Juha Yrjölä, Tony Lindgren and Timo Teräs. + * Modified for Retu/Tahvo MFD by Aaro Koskinen. + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "tahvo-usb" + +#define TAHVO_REG_IDSR 0x02 +#define TAHVO_REG_USBR 0x06 + +#define USBR_SLAVE_CONTROL (1 << 8) +#define USBR_VPPVIO_SW (1 << 7) +#define USBR_SPEED (1 << 6) +#define USBR_REGOUT (1 << 5) +#define USBR_MASTER_SW2 (1 << 4) +#define USBR_MASTER_SW1 (1 << 3) +#define USBR_SLAVE_SW (1 << 2) +#define USBR_NSUSPEND (1 << 1) +#define USBR_SEMODE (1 << 0) + +#define TAHVO_MODE_HOST 0 +#define TAHVO_MODE_PERIPHERAL 1 + +struct tahvo_usb { + struct platform_device *pt_dev; + struct usb_phy phy; + int vbus_state; + struct mutex serialize; + struct clk *ick; + int irq; + int tahvo_mode; + struct extcon_dev extcon; +}; + +static const char *tahvo_cable[] = { + "USB-HOST", + "USB", + NULL, +}; + +static ssize_t vbus_state_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + struct tahvo_usb *tu = dev_get_drvdata(device); + return sprintf(buf, "%s\n", tu->vbus_state ? "on" : "off"); +} +static DEVICE_ATTR(vbus, 0444, vbus_state_show, NULL); + +static void check_vbus_state(struct tahvo_usb *tu) +{ + struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent); + int reg, prev_state; + + reg = retu_read(rdev, TAHVO_REG_IDSR); + if (reg & TAHVO_STAT_VBUS) { + switch (tu->phy.state) { + case OTG_STATE_B_IDLE: + /* Enable the gadget driver */ + if (tu->phy.otg->gadget) + usb_gadget_vbus_connect(tu->phy.otg->gadget); + tu->phy.state = OTG_STATE_B_PERIPHERAL; + break; + case OTG_STATE_A_IDLE: + /* + * Session is now valid assuming the USB hub is driving + * Vbus. + */ + tu->phy.state = OTG_STATE_A_HOST; + break; + default: + break; + } + dev_info(&tu->pt_dev->dev, "USB cable connected\n"); + } else { + switch (tu->phy.state) { + case OTG_STATE_B_PERIPHERAL: + if (tu->phy.otg->gadget) + usb_gadget_vbus_disconnect(tu->phy.otg->gadget); + tu->phy.state = OTG_STATE_B_IDLE; + break; + case OTG_STATE_A_HOST: + tu->phy.state = OTG_STATE_A_IDLE; + break; + default: + break; + } + dev_info(&tu->pt_dev->dev, "USB cable disconnected\n"); + } + + prev_state = tu->vbus_state; + tu->vbus_state = reg & TAHVO_STAT_VBUS; + if (prev_state != tu->vbus_state) { + extcon_set_cable_state(&tu->extcon, "USB", tu->vbus_state); + sysfs_notify(&tu->pt_dev->dev.kobj, NULL, "vbus_state"); + } +} + +static void tahvo_usb_become_host(struct tahvo_usb *tu) +{ + struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent); + + extcon_set_cable_state(&tu->extcon, "USB-HOST", true); + + /* Power up the transceiver in USB host mode */ + retu_write(rdev, TAHVO_REG_USBR, USBR_REGOUT | USBR_NSUSPEND | + USBR_MASTER_SW2 | USBR_MASTER_SW1); + tu->phy.state = OTG_STATE_A_IDLE; + + check_vbus_state(tu); +} + +static void tahvo_usb_stop_host(struct tahvo_usb *tu) +{ + tu->phy.state = OTG_STATE_A_IDLE; +} + +static void tahvo_usb_become_peripheral(struct tahvo_usb *tu) +{ + struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent); + + extcon_set_cable_state(&tu->extcon, "USB-HOST", false); + + /* Power up transceiver and set it in USB peripheral mode */ + retu_write(rdev, TAHVO_REG_USBR, USBR_SLAVE_CONTROL | USBR_REGOUT | + USBR_NSUSPEND | USBR_SLAVE_SW); + tu->phy.state = OTG_STATE_B_IDLE; + + check_vbus_state(tu); +} + +static void tahvo_usb_stop_peripheral(struct tahvo_usb *tu) +{ + if (tu->phy.otg->gadget) + usb_gadget_vbus_disconnect(tu->phy.otg->gadget); + tu->phy.state = OTG_STATE_B_IDLE; +} + +static void tahvo_usb_power_off(struct tahvo_usb *tu) +{ + struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent); + + /* Disable gadget controller if any */ + if (tu->phy.otg->gadget) + usb_gadget_vbus_disconnect(tu->phy.otg->gadget); + + /* Power off transceiver */ + retu_write(rdev, TAHVO_REG_USBR, 0); + tu->phy.state = OTG_STATE_UNDEFINED; +} + +static int tahvo_usb_set_suspend(struct usb_phy *dev, int suspend) +{ + struct tahvo_usb *tu = container_of(dev, struct tahvo_usb, phy); + struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent); + u16 w; + + dev_dbg(&tu->pt_dev->dev, "%s\n", __func__); + + w = retu_read(rdev, TAHVO_REG_USBR); + if (suspend) + w &= ~USBR_NSUSPEND; + else + w |= USBR_NSUSPEND; + retu_write(rdev, TAHVO_REG_USBR, w); + + return 0; +} + +static int tahvo_usb_set_host(struct usb_otg *otg, struct usb_bus *host) +{ + struct tahvo_usb *tu = container_of(otg->phy, struct tahvo_usb, phy); + + dev_dbg(&tu->pt_dev->dev, "%s %p\n", __func__, host); + + if (otg == NULL) + return -ENODEV; + + mutex_lock(&tu->serialize); + + if (host == NULL) { + if (tu->tahvo_mode == TAHVO_MODE_HOST) + tahvo_usb_power_off(tu); + otg->host = NULL; + mutex_unlock(&tu->serialize); + return 0; + } + + if (tu->tahvo_mode == TAHVO_MODE_HOST) { + otg->host = NULL; + tahvo_usb_become_host(tu); + } + + otg->host = host; + + mutex_unlock(&tu->serialize); + + return 0; +} + +static int tahvo_usb_set_peripheral(struct usb_otg *otg, + struct usb_gadget *gadget) +{ + struct tahvo_usb *tu = container_of(otg->phy, struct tahvo_usb, phy); + + dev_dbg(&tu->pt_dev->dev, "%s %p\n", __func__, gadget); + + if (!otg) + return -ENODEV; + + mutex_lock(&tu->serialize); + + if (!gadget) { + if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL) + tahvo_usb_power_off(tu); + tu->phy.otg->gadget = NULL; + mutex_unlock(&tu->serialize); + return 0; + } + + tu->phy.otg->gadget = gadget; + if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL) + tahvo_usb_become_peripheral(tu); + + mutex_unlock(&tu->serialize); + + return 0; +} + +static irqreturn_t tahvo_usb_vbus_interrupt(int irq, void *_tu) +{ + struct tahvo_usb *tu = _tu; + + mutex_lock(&tu->serialize); + check_vbus_state(tu); + mutex_unlock(&tu->serialize); + + return IRQ_HANDLED; +} + +static ssize_t otg_mode_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + struct tahvo_usb *tu = dev_get_drvdata(device); + + switch (tu->tahvo_mode) { + case TAHVO_MODE_HOST: + return sprintf(buf, "host\n"); + case TAHVO_MODE_PERIPHERAL: + return sprintf(buf, "peripheral\n"); + } + + return -EINVAL; +} + +static ssize_t otg_mode_store(struct device *device, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct tahvo_usb *tu = dev_get_drvdata(device); + int r; + + mutex_lock(&tu->serialize); + if (count >= 4 && strncmp(buf, "host", 4) == 0) { + if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL) + tahvo_usb_stop_peripheral(tu); + tu->tahvo_mode = TAHVO_MODE_HOST; + if (tu->phy.otg->host) { + dev_info(device, "HOST mode: host controller present\n"); + tahvo_usb_become_host(tu); + } else { + dev_info(device, "HOST mode: no host controller, powering off\n"); + tahvo_usb_power_off(tu); + } + r = strlen(buf); + } else if (count >= 10 && strncmp(buf, "peripheral", 10) == 0) { + if (tu->tahvo_mode == TAHVO_MODE_HOST) + tahvo_usb_stop_host(tu); + tu->tahvo_mode = TAHVO_MODE_PERIPHERAL; + if (tu->phy.otg->gadget) { + dev_info(device, "PERIPHERAL mode: gadget driver present\n"); + tahvo_usb_become_peripheral(tu); + } else { + dev_info(device, "PERIPHERAL mode: no gadget driver, powering off\n"); + tahvo_usb_power_off(tu); + } + r = strlen(buf); + } else { + r = -EINVAL; + } + mutex_unlock(&tu->serialize); + + return r; +} +static DEVICE_ATTR(otg_mode, 0644, otg_mode_show, otg_mode_store); + +static struct attribute *tahvo_attributes[] = { + &dev_attr_vbus.attr, + &dev_attr_otg_mode.attr, + NULL +}; + +static struct attribute_group tahvo_attr_group = { + .attrs = tahvo_attributes, +}; + +static int tahvo_usb_probe(struct platform_device *pdev) +{ + struct retu_dev *rdev = dev_get_drvdata(pdev->dev.parent); + struct tahvo_usb *tu; + int ret; + + tu = devm_kzalloc(&pdev->dev, sizeof(*tu), GFP_KERNEL); + if (!tu) + return -ENOMEM; + + tu->phy.otg = devm_kzalloc(&pdev->dev, sizeof(*tu->phy.otg), + GFP_KERNEL); + if (!tu->phy.otg) + return -ENOMEM; + + tu->pt_dev = pdev; + + /* Default mode */ +#ifdef CONFIG_TAHVO_USB_HOST_BY_DEFAULT + tu->tahvo_mode = TAHVO_MODE_HOST; +#else + tu->tahvo_mode = TAHVO_MODE_PERIPHERAL; +#endif + + mutex_init(&tu->serialize); + + tu->ick = devm_clk_get(&pdev->dev, "usb_l4_ick"); + if (!IS_ERR(tu->ick)) + clk_enable(tu->ick); + + /* + * Set initial state, so that we generate kevents only on state changes. + */ + tu->vbus_state = retu_read(rdev, TAHVO_REG_IDSR) & TAHVO_STAT_VBUS; + + tu->extcon.name = DRIVER_NAME; + tu->extcon.supported_cable = tahvo_cable; + tu->extcon.dev.parent = &pdev->dev; + + ret = extcon_dev_register(&tu->extcon); + if (ret) { + dev_err(&pdev->dev, "could not register extcon device: %d\n", + ret); + goto err_disable_clk; + } + + /* Set the initial cable state. */ + extcon_set_cable_state(&tu->extcon, "USB-HOST", + tu->tahvo_mode == TAHVO_MODE_HOST); + extcon_set_cable_state(&tu->extcon, "USB", tu->vbus_state); + + /* Create OTG interface */ + tahvo_usb_power_off(tu); + tu->phy.dev = &pdev->dev; + tu->phy.state = OTG_STATE_UNDEFINED; + tu->phy.label = DRIVER_NAME; + tu->phy.set_suspend = tahvo_usb_set_suspend; + + tu->phy.otg->phy = &tu->phy; + tu->phy.otg->set_host = tahvo_usb_set_host; + tu->phy.otg->set_peripheral = tahvo_usb_set_peripheral; + + ret = usb_add_phy(&tu->phy, USB_PHY_TYPE_USB2); + if (ret < 0) { + dev_err(&pdev->dev, "cannot register USB transceiver: %d\n", + ret); + goto err_extcon_unreg; + } + + dev_set_drvdata(&pdev->dev, tu); + + tu->irq = platform_get_irq(pdev, 0); + ret = request_threaded_irq(tu->irq, NULL, tahvo_usb_vbus_interrupt, 0, + "tahvo-vbus", tu); + if (ret) { + dev_err(&pdev->dev, "could not register tahvo-vbus irq: %d\n", + ret); + goto err_remove_phy; + } + + /* Attributes */ + ret = sysfs_create_group(&pdev->dev.kobj, &tahvo_attr_group); + if (ret) { + dev_err(&pdev->dev, "cannot create sysfs group: %d\n", ret); + goto err_free_irq; + } + + return 0; + +err_free_irq: + free_irq(tu->irq, tu); +err_remove_phy: + usb_remove_phy(&tu->phy); +err_extcon_unreg: + extcon_dev_unregister(&tu->extcon); +err_disable_clk: + if (!IS_ERR(tu->ick)) + clk_disable(tu->ick); + + return ret; +} + +static int tahvo_usb_remove(struct platform_device *pdev) +{ + struct tahvo_usb *tu = platform_get_drvdata(pdev); + + sysfs_remove_group(&pdev->dev.kobj, &tahvo_attr_group); + free_irq(tu->irq, tu); + usb_remove_phy(&tu->phy); + extcon_dev_unregister(&tu->extcon); + if (!IS_ERR(tu->ick)) + clk_disable(tu->ick); + + return 0; +} + +static struct platform_driver tahvo_usb_driver = { + .probe = tahvo_usb_probe, + .remove = tahvo_usb_remove, + .driver = { + .name = "tahvo-usb", + .owner = THIS_MODULE, + }, +}; +module_platform_driver(tahvo_usb_driver); + +MODULE_DESCRIPTION("Tahvo USB transceiver driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Juha Yrjölä, Tony Lindgren, and Timo Teräs"); +MODULE_AUTHOR("Aaro Koskinen "); From c6d7641470fed467777a886c64329fff8f5abf0b Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Sat, 7 Dec 2013 11:54:19 -0600 Subject: [PATCH 031/105] usb: dwc3: omap: remove unnecessary lock the lock was only taken inside the hardirq handler, which runs with IRQs disabled. There's no chance of any race condition happening, even on SMP machines. It's safe to remove that spinlock. Acked-by: Santosh Shilimkar Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-omap.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index daab0ad10b25..b269dbd47fc4 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -120,9 +119,6 @@ #define USBOTGSS_UTMI_OTG_STATUS_VBUSVALID (1 << 1) struct dwc3_omap { - /* device lock */ - spinlock_t lock; - struct device *dev; int irq; @@ -280,8 +276,6 @@ static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap) struct dwc3_omap *omap = _omap; u32 reg; - spin_lock(&omap->lock); - reg = dwc3_omap_read_irqmisc_status(omap); if (reg & USBOTGSS_IRQMISC_DMADISABLECLR) { @@ -322,8 +316,6 @@ static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap) dwc3_omap_write_irq0_status(omap, reg); - spin_unlock(&omap->lock); - return IRQ_HANDLED; } @@ -449,8 +441,6 @@ static int dwc3_omap_probe(struct platform_device *pdev) } } - spin_lock_init(&omap->lock); - omap->dev = dev; omap->irq = irq; omap->base = base; From c3e5d2985ef720cbbdc63546a5c545ac4450d96e Mon Sep 17 00:00:00 2001 From: Valentine Barshak Date: Mon, 9 Dec 2013 23:40:33 +0400 Subject: [PATCH 032/105] usb: phy: r-car gen2: use usb_add_phy_dev Use usb_add_phy_dev instead of usb_add_phy, so that devices can be bound to the phy. This is needed to set up USB phy for some internal PCI USB host controllers on R-Car Gen2. Signed-off-by: Valentine Barshak Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-rcar-gen2-usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/phy/phy-rcar-gen2-usb.c b/drivers/usb/phy/phy-rcar-gen2-usb.c index a99a6953f11c..9f57091bc9e0 100644 --- a/drivers/usb/phy/phy-rcar-gen2-usb.c +++ b/drivers/usb/phy/phy-rcar-gen2-usb.c @@ -213,7 +213,7 @@ static int rcar_gen2_usb_phy_probe(struct platform_device *pdev) priv->phy.shutdown = rcar_gen2_usb_phy_shutdown; priv->phy.set_suspend = rcar_gen2_usb_phy_set_suspend; - retval = usb_add_phy(&priv->phy, USB_PHY_TYPE_USB2); + retval = usb_add_phy_dev(&priv->phy); if (retval < 0) { dev_err(dev, "Failed to add USB phy\n"); return retval; From d8318d7f6bd08eba36ed2a7a23ac5389115958bc Mon Sep 17 00:00:00 2001 From: David Cohen Date: Mon, 9 Dec 2013 15:55:34 -0800 Subject: [PATCH 033/105] usb: gadget: move bitflags to the end of usb_gadget struct This patch moves all bitflags to the end of usb_gadget struct in order to improve readability. Signed-off-by: David Cohen Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- include/linux/usb/gadget.h | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 942ef5e053bf..23b3bfd0a842 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -485,6 +485,11 @@ struct usb_gadget_ops { * @max_speed: Maximal speed the UDC can handle. UDC must support this * and all slower speeds. * @state: the state we are now (attached, suspended, configured, etc) + * @name: Identifies the controller hardware type. Used in diagnostics + * and sometimes configuration. + * @dev: Driver model state for this abstract device. + * @out_epnum: last used out ep number + * @in_epnum: last used in ep number * @sg_supported: true if we can handle scatter-gather * @is_otg: True if the USB device port uses a Mini-AB jack, so that the * gadget driver must provide a USB OTG descriptor. @@ -497,11 +502,6 @@ struct usb_gadget_ops { * only supports HNP on a different root port. * @b_hnp_enable: OTG device feature flag, indicating that the A-Host * enabled HNP support. - * @name: Identifies the controller hardware type. Used in diagnostics - * and sometimes configuration. - * @dev: Driver model state for this abstract device. - * @out_epnum: last used out ep number - * @in_epnum: last used in ep number * * Gadgets have a mostly-portable "gadget driver" implementing device * functions, handling all usb configurations and interfaces. Gadget @@ -530,16 +530,17 @@ struct usb_gadget { enum usb_device_speed speed; enum usb_device_speed max_speed; enum usb_device_state state; + const char *name; + struct device dev; + unsigned out_epnum; + unsigned in_epnum; + unsigned sg_supported:1; unsigned is_otg:1; unsigned is_a_peripheral:1; unsigned b_hnp_enable:1; unsigned a_hnp_support:1; unsigned a_alt_hnp_support:1; - const char *name; - struct device dev; - unsigned out_epnum; - unsigned in_epnum; }; #define work_to_gadget(w) (container_of((w), struct usb_gadget, work)) From 0b2d2bbade59ab2067f326d6dbc2628bee227fd5 Mon Sep 17 00:00:00 2001 From: David Cohen Date: Mon, 9 Dec 2013 15:55:35 -0800 Subject: [PATCH 034/105] usb: gadget: add quirk_ep_out_aligned_size field to struct usb_gadget Due to USB controllers may have different restrictions, usb gadget layer needs to provide a generic way to inform gadget functions to complain with non-standard requirements. This patch adds 'quirk_ep_out_aligned_size' field to struct usb_gadget to inform when controller's epout requires buffer size to be aligned to MaxPacketSize. A helper is also provided to align buffer size when necessary. Cc: Alan Stern Acked-by: Michal Nazarewicz Signed-off-by: David Cohen Signed-off-by: Felipe Balbi --- include/linux/usb/gadget.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 23b3bfd0a842..cae8a6216551 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -502,6 +502,8 @@ struct usb_gadget_ops { * only supports HNP on a different root port. * @b_hnp_enable: OTG device feature flag, indicating that the A-Host * enabled HNP support. + * @quirk_ep_out_aligned_size: epout requires buffer size to be aligned to + * MaxPacketSize. * * Gadgets have a mostly-portable "gadget driver" implementing device * functions, handling all usb configurations and interfaces. Gadget @@ -541,6 +543,7 @@ struct usb_gadget { unsigned b_hnp_enable:1; unsigned a_hnp_support:1; unsigned a_alt_hnp_support:1; + unsigned quirk_ep_out_aligned_size:1; }; #define work_to_gadget(w) (container_of((w), struct usb_gadget, work)) @@ -558,6 +561,23 @@ static inline struct usb_gadget *dev_to_usb_gadget(struct device *dev) list_for_each_entry(tmp, &(gadget)->ep_list, ep_list) +/** + * usb_ep_align_maybe - returns @len aligned to ep's maxpacketsize if gadget + * requires quirk_ep_out_aligned_size, otherwise reguens len. + * @g: controller to check for quirk + * @ep: the endpoint whose maxpacketsize is used to align @len + * @len: buffer size's length to align to @ep's maxpacketsize + * + * This helper is used in case it's required for any reason to check and maybe + * align buffer's size to an ep's maxpacketsize. + */ +static inline size_t +usb_ep_align_maybe(struct usb_gadget *g, struct usb_ep *ep, size_t len) +{ + return !g->quirk_ep_out_aligned_size ? len : + round_up(len, (size_t)ep->desc->wMaxPacketSize); +} + /** * gadget_is_dualspeed - return true iff the hardware handles high speed * @g: controller that might support both high and full speeds From 7fa6803483ed215f9c73780194d49719a060bf1c Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Mon, 9 Dec 2013 15:55:36 -0800 Subject: [PATCH 035/105] usb: gadget: f_fs: remove loop from I/O function When endpoint changes (due to it being disabled or alt setting changed), mimic the action as if the change happened after the request has been queued, instead of retrying with the new endpoint. Signed-off-by: Michal Nazarewicz Cc: David Cohen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_fs.c | 118 +++++++++++++++++--------------------- 1 file changed, 52 insertions(+), 66 deletions(-) diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index 774e8b89cdb5..1222cf9b62d3 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -758,73 +758,59 @@ static ssize_t ffs_epfile_io(struct file *file, ssize_t ret; int halt; - goto first_try; - do { + /* Are we still active? */ + if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) { + ret = -ENODEV; + goto error; + } + + /* Wait for endpoint to be enabled */ + ep = epfile->ep; + if (!ep) { + if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + goto error; + } + + ret = wait_event_interruptible(epfile->wait, (ep = epfile->ep)); + if (ret) { + ret = -EINTR; + goto error; + } + } + + /* Do we halt? */ + halt = !read == !epfile->in; + if (halt && epfile->isoc) { + ret = -EINVAL; + goto error; + } + + /* Allocate & copy */ + if (!halt) { + data = kmalloc(len, GFP_KERNEL); + if (unlikely(!data)) + return -ENOMEM; + + if (!read && unlikely(copy_from_user(data, buf, len))) { + ret = -EFAULT; + goto error; + } + } + + /* We will be using request */ + ret = ffs_mutex_lock(&epfile->mutex, file->f_flags & O_NONBLOCK); + if (unlikely(ret)) + goto error; + + spin_lock_irq(&epfile->ffs->eps_lock); + + if (epfile->ep != ep) { + /* In the meantime, endpoint got disabled or changed. */ + ret = -ESHUTDOWN; spin_unlock_irq(&epfile->ffs->eps_lock); - mutex_unlock(&epfile->mutex); - -first_try: - /* Are we still active? */ - if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) { - ret = -ENODEV; - goto error; - } - - /* Wait for endpoint to be enabled */ - ep = epfile->ep; - if (!ep) { - if (file->f_flags & O_NONBLOCK) { - ret = -EAGAIN; - goto error; - } - - if (wait_event_interruptible(epfile->wait, - (ep = epfile->ep))) { - ret = -EINTR; - goto error; - } - } - - /* Do we halt? */ - halt = !read == !epfile->in; - if (halt && epfile->isoc) { - ret = -EINVAL; - goto error; - } - - /* Allocate & copy */ - if (!halt && !data) { - data = kzalloc(len, GFP_KERNEL); - if (unlikely(!data)) - return -ENOMEM; - - if (!read && - unlikely(__copy_from_user(data, buf, len))) { - ret = -EFAULT; - goto error; - } - } - - /* We will be using request */ - ret = ffs_mutex_lock(&epfile->mutex, - file->f_flags & O_NONBLOCK); - if (unlikely(ret)) - goto error; - - /* - * We're called from user space, we can use _irq rather then - * _irqsave - */ - spin_lock_irq(&epfile->ffs->eps_lock); - - /* - * While we were acquiring mutex endpoint got disabled - * or changed? - */ - } while (unlikely(epfile->ep != ep)); - - /* Halt */ - if (unlikely(halt)) { + } else if (halt) { + /* Halt */ if (likely(epfile->ep == ep) && !WARN_ON(!ep->ep)) usb_ep_set_halt(ep->ep); spin_unlock_irq(&epfile->ffs->eps_lock); From 219580e64f035bb9018dbb08d340f90b0ac50f8c Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Mon, 9 Dec 2013 15:55:37 -0800 Subject: [PATCH 036/105] usb: f_fs: check quirk to pad epout buf size when not aligned to maxpacketsize Check gadget.quirk_ep_out_aligned_size to decide if buffer size requires to be aligned to maxpacketsize of an out endpoint. ffs_epfile_io() needs to pad epout buffer to match above condition if quirk is found. Signed-off-by: Michal Nazarewicz Signed-off-by: David Cohen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_fs.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index 1222cf9b62d3..34940439bb18 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -753,9 +753,10 @@ static ssize_t ffs_epfile_io(struct file *file, char __user *buf, size_t len, int read) { struct ffs_epfile *epfile = file->private_data; + struct usb_gadget *gadget = epfile->ffs->gadget; struct ffs_ep *ep; char *data = NULL; - ssize_t ret; + ssize_t ret, data_len; int halt; /* Are we still active? */ @@ -788,7 +789,13 @@ static ssize_t ffs_epfile_io(struct file *file, /* Allocate & copy */ if (!halt) { - data = kmalloc(len, GFP_KERNEL); + /* + * Controller may require buffer size to be aligned to + * maxpacketsize of an out endpoint. + */ + data_len = read ? usb_ep_align_maybe(gadget, ep->ep, len) : len; + + data = kmalloc(data_len, GFP_KERNEL); if (unlikely(!data)) return -ENOMEM; @@ -823,7 +830,7 @@ static ssize_t ffs_epfile_io(struct file *file, req->context = &done; req->complete = ffs_epfile_io_complete; req->buf = data; - req->length = len; + req->length = data_len; ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC); @@ -835,9 +842,17 @@ static ssize_t ffs_epfile_io(struct file *file, ret = -EINTR; usb_ep_dequeue(ep->ep, req); } else { + /* + * XXX We may end up silently droping data here. + * Since data_len (i.e. req->length) may be bigger + * than len (after being rounded up to maxpacketsize), + * we may end up with more data then user space has + * space for. + */ ret = ep->status; if (read && ret > 0 && - unlikely(copy_to_user(buf, data, ret))) + unlikely(copy_to_user(buf, data, + min_t(size_t, ret, len)))) ret = -EFAULT; } } From a4b9d94b3dc8e65951c1819d37f213b3c6815adc Mon Sep 17 00:00:00 2001 From: David Cohen Date: Mon, 9 Dec 2013 15:55:38 -0800 Subject: [PATCH 037/105] usb: dwc3: set gadget's quirk ep_out_align_size DWC3 requires epout to have buffer size aligned to MaxPacketSize value. This patch sets necessary quirk for it. Signed-off-by: David Cohen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 5452c0fce360..b85ec110d6a0 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2599,6 +2599,12 @@ int dwc3_gadget_init(struct dwc3 *dwc) dwc->gadget.sg_supported = true; dwc->gadget.name = "dwc3-gadget"; + /* + * Per databook, DWC3 needs buffer size to be aligned to MaxPacketSize + * on ep out. + */ + dwc->gadget.quirk_ep_out_aligned_size = true; + /* * REVISIT: Here we should clear all pending IRQs to be * sure we're starting from a well known location. From 943befc314ae5848e3be50d1fcdbdf6456e8d6d4 Mon Sep 17 00:00:00 2001 From: WingMan Kwok Date: Thu, 12 Dec 2013 12:25:29 -0500 Subject: [PATCH 038/105] usb: dwc3: add Keystone specific glue layer Add Keystone platform specific glue layer to support USB3 Host mode. [ balbi@ti.com : fix order of clk_disable() and platform_device_unregister() ] Cc: Greg Kroah-Hartman Acked-by: Santosh Shilimkar Signed-off-by: WingMan Kwok Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/Kconfig | 7 ++ drivers/usb/dwc3/Makefile | 1 + drivers/usb/dwc3/dwc3-keystone.c | 202 +++++++++++++++++++++++++++++++ 3 files changed, 210 insertions(+) create mode 100644 drivers/usb/dwc3/dwc3-keystone.c diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 70fc43027a5c..e2c730fc9a90 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -70,6 +70,13 @@ config USB_DWC3_PCI One such PCIe-based platform is Synopsys' PCIe HAPS model of this IP. +config USB_DWC3_KEYSTONE + tristate "Texas Instruments Keystone2 Platforms" + default USB_DWC3 + help + Support of USB2/3 functionality in TI Keystone2 platforms. + Say 'Y' or 'M' here if you have one such device + comment "Debugging features" config USB_DWC3_DEBUG diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index dd1760145c46..10ac3e72482e 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -32,3 +32,4 @@ endif obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o +obj-$(CONFIG_USB_DWC3_KEYSTONE) += dwc3-keystone.o diff --git a/drivers/usb/dwc3/dwc3-keystone.c b/drivers/usb/dwc3/dwc3-keystone.c new file mode 100644 index 000000000000..1fad1618df6e --- /dev/null +++ b/drivers/usb/dwc3/dwc3-keystone.c @@ -0,0 +1,202 @@ +/** + * dwc3-keystone.c - Keystone Specific Glue layer + * + * Copyright (C) 2010-2013 Texas Instruments Incorporated - http://www.ti.com + * + * Author: WingMan Kwok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* USBSS register offsets */ +#define USBSS_REVISION 0x0000 +#define USBSS_SYSCONFIG 0x0010 +#define USBSS_IRQ_EOI 0x0018 +#define USBSS_IRQSTATUS_RAW_0 0x0020 +#define USBSS_IRQSTATUS_0 0x0024 +#define USBSS_IRQENABLE_SET_0 0x0028 +#define USBSS_IRQENABLE_CLR_0 0x002c + +/* IRQ register bits */ +#define USBSS_IRQ_EOI_LINE(n) BIT(n) +#define USBSS_IRQ_EVENT_ST BIT(0) +#define USBSS_IRQ_COREIRQ_EN BIT(0) +#define USBSS_IRQ_COREIRQ_CLR BIT(0) + +static u64 kdwc3_dma_mask; + +struct dwc3_keystone { + struct device *dev; + struct clk *clk; + void __iomem *usbss; +}; + +static inline u32 kdwc3_readl(void __iomem *base, u32 offset) +{ + return readl(base + offset); +} + +static inline void kdwc3_writel(void __iomem *base, u32 offset, u32 value) +{ + writel(value, base + offset); +} + +static void kdwc3_enable_irqs(struct dwc3_keystone *kdwc) +{ + u32 val; + + val = kdwc3_readl(kdwc->usbss, USBSS_IRQENABLE_SET_0); + val |= USBSS_IRQ_COREIRQ_EN; + kdwc3_writel(kdwc->usbss, USBSS_IRQENABLE_SET_0, val); +} + +static void kdwc3_disable_irqs(struct dwc3_keystone *kdwc) +{ + u32 val; + + val = kdwc3_readl(kdwc->usbss, USBSS_IRQENABLE_SET_0); + val &= ~USBSS_IRQ_COREIRQ_EN; + kdwc3_writel(kdwc->usbss, USBSS_IRQENABLE_SET_0, val); +} + +static irqreturn_t dwc3_keystone_interrupt(int irq, void *_kdwc) +{ + struct dwc3_keystone *kdwc = _kdwc; + + kdwc3_writel(kdwc->usbss, USBSS_IRQENABLE_CLR_0, USBSS_IRQ_COREIRQ_CLR); + kdwc3_writel(kdwc->usbss, USBSS_IRQSTATUS_0, USBSS_IRQ_EVENT_ST); + kdwc3_writel(kdwc->usbss, USBSS_IRQENABLE_SET_0, USBSS_IRQ_COREIRQ_EN); + kdwc3_writel(kdwc->usbss, USBSS_IRQ_EOI, USBSS_IRQ_EOI_LINE(0)); + + return IRQ_HANDLED; +} + +static int kdwc3_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = pdev->dev.of_node; + struct dwc3_keystone *kdwc; + struct resource *res; + int error, irq; + + kdwc = devm_kzalloc(dev, sizeof(*kdwc), GFP_KERNEL); + if (!kdwc) + return -ENOMEM; + + platform_set_drvdata(pdev, kdwc); + + kdwc->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "missing usbss resource\n"); + return -EINVAL; + } + + kdwc->usbss = devm_ioremap_resource(dev, res); + if (IS_ERR(kdwc->usbss)) + return PTR_ERR(kdwc->usbss); + + kdwc3_dma_mask = dma_get_mask(dev); + dev->dma_mask = &kdwc3_dma_mask; + + kdwc->clk = devm_clk_get(kdwc->dev, "usb"); + + error = clk_prepare_enable(kdwc->clk); + if (error < 0) { + dev_dbg(kdwc->dev, "unable to enable usb clock, err %d\n", + error); + return error; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "missing irq\n"); + goto err_irq; + } + + error = devm_request_irq(dev, irq, dwc3_keystone_interrupt, IRQF_SHARED, + dev_name(dev), kdwc); + if (error) { + dev_err(dev, "failed to request IRQ #%d --> %d\n", + irq, error); + goto err_irq; + } + + kdwc3_enable_irqs(kdwc); + + error = of_platform_populate(node, NULL, NULL, dev); + if (error) { + dev_err(&pdev->dev, "failed to create dwc3 core\n"); + goto err_core; + } + + return 0; + +err_core: + kdwc3_disable_irqs(kdwc); +err_irq: + clk_disable_unprepare(kdwc->clk); + + return error; +} + +static int kdwc3_remove_core(struct device *dev, void *c) +{ + struct platform_device *pdev = to_platform_device(dev); + + platform_device_unregister(pdev); + + return 0; +} + +static int kdwc3_remove(struct platform_device *pdev) +{ + struct dwc3_keystone *kdwc = platform_get_drvdata(pdev); + + kdwc3_disable_irqs(kdwc); + device_for_each_child(&pdev->dev, NULL, kdwc3_remove_core); + clk_disable_unprepare(kdwc->clk); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static const struct of_device_id kdwc3_of_match[] = { + { .compatible = "ti,keystone-dwc3", }, + {}, +}; +MODULE_DEVICE_TABLE(of, kdwc3_of_match); + +static struct platform_driver kdwc3_driver = { + .probe = kdwc3_probe, + .remove = kdwc3_remove, + .driver = { + .name = "keystone-dwc3", + .owner = THIS_MODULE, + .of_match_table = kdwc3_of_match, + }, +}; + +module_platform_driver(kdwc3_driver); + +MODULE_ALIAS("platform:keystone-dwc3"); +MODULE_AUTHOR("WingMan Kwok "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("DesignWare USB3 KEYSTONE Glue Layer"); From 25acdd08fd715cf612428c2b3c39ce2a41926238 Mon Sep 17 00:00:00 2001 From: WingMan Kwok Date: Thu, 12 Dec 2013 12:25:30 -0500 Subject: [PATCH 039/105] usb: phy: add Keystone usb phy driver Add Keystone platform USB PHY driver support. Current main purpose of this driver is to enable the PHY reference clock gate on the Keystone SoC. Otherwise it is a nop PHY. [ balbi@ti.com : add COMPILE_TEST as a possible dependency make sure drvdata is initialized before adding PHY ] Cc: Greg Kroah-Hartman Acked-by: Santosh Shilimkar Signed-off-by: WingMan Kwok Signed-off-by: Felipe Balbi --- drivers/usb/phy/Kconfig | 9 +++ drivers/usb/phy/Makefile | 1 + drivers/usb/phy/phy-keystone.c | 142 +++++++++++++++++++++++++++++++++ 3 files changed, 152 insertions(+) create mode 100644 drivers/usb/phy/phy-keystone.c diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 4f22762f3d6f..54bebba39e91 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -48,6 +48,15 @@ config ISP1301_OMAP This driver can also be built as a module. If so, the module will be called isp1301_omap. +config KEYSTONE_USB_PHY + tristate "Keystone USB PHY Driver" + depends on ARCH_KEYSTONE || COMPILE_TEST + select NOP_USB_XCEIV + help + Enable this to support Keystone USB phy. This driver provides + interface to interact with USB 2.0 and USB 3.0 PHY that is part + of the Keystone SOC. + config MV_U3D_PHY bool "Marvell USB 3.0 PHY controller Driver" depends on CPU_MMP3 diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index 9b3be9e0aeb4..be58adae3496 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -32,3 +32,4 @@ obj-$(CONFIG_USB_RCAR_PHY) += phy-rcar-usb.o obj-$(CONFIG_USB_RCAR_GEN2_PHY) += phy-rcar-gen2-usb.o obj-$(CONFIG_USB_ULPI) += phy-ulpi.o obj-$(CONFIG_USB_ULPI_VIEWPORT) += phy-ulpi-viewport.o +obj-$(CONFIG_KEYSTONE_USB_PHY) += phy-keystone.o diff --git a/drivers/usb/phy/phy-keystone.c b/drivers/usb/phy/phy-keystone.c new file mode 100644 index 000000000000..a04fb940d62c --- /dev/null +++ b/drivers/usb/phy/phy-keystone.c @@ -0,0 +1,142 @@ +/* + * phy-keystone - USB PHY, talking to dwc3 controller in Keystone. + * + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com + * 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. + * + * Author: WingMan Kwok + * + * 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. + * + */ + +#include +#include +#include +#include +#include + +#include "phy-generic.h" + +/* USB PHY control register offsets */ +#define USB_PHY_CTL_UTMI 0x0000 +#define USB_PHY_CTL_PIPE 0x0004 +#define USB_PHY_CTL_PARAM_1 0x0008 +#define USB_PHY_CTL_PARAM_2 0x000c +#define USB_PHY_CTL_CLOCK 0x0010 +#define USB_PHY_CTL_PLL 0x0014 + +#define PHY_REF_SSP_EN BIT(29) + +struct keystone_usbphy { + struct usb_phy_gen_xceiv usb_phy_gen; + void __iomem *phy_ctrl; +}; + +static inline u32 keystone_usbphy_readl(void __iomem *base, u32 offset) +{ + return readl(base + offset); +} + +static inline void keystone_usbphy_writel(void __iomem *base, + u32 offset, u32 value) +{ + writel(value, base + offset); +} + +static int keystone_usbphy_init(struct usb_phy *phy) +{ + struct keystone_usbphy *k_phy = dev_get_drvdata(phy->dev); + u32 val; + + val = keystone_usbphy_readl(k_phy->phy_ctrl, USB_PHY_CTL_CLOCK); + keystone_usbphy_writel(k_phy->phy_ctrl, USB_PHY_CTL_CLOCK, + val | PHY_REF_SSP_EN); + return 0; +} + +static void keystone_usbphy_shutdown(struct usb_phy *phy) +{ + struct keystone_usbphy *k_phy = dev_get_drvdata(phy->dev); + u32 val; + + val = keystone_usbphy_readl(k_phy->phy_ctrl, USB_PHY_CTL_CLOCK); + keystone_usbphy_writel(k_phy->phy_ctrl, USB_PHY_CTL_CLOCK, + val &= ~PHY_REF_SSP_EN); +} + +static int keystone_usbphy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct keystone_usbphy *k_phy; + struct resource *res; + int ret; + + k_phy = devm_kzalloc(dev, sizeof(*k_phy), GFP_KERNEL); + if (!k_phy) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "missing usb phy resource\n"); + return -EINVAL; + } + + k_phy->phy_ctrl = devm_ioremap_resource(dev, res); + if (IS_ERR(k_phy->phy_ctrl)) + return PTR_ERR(k_phy->phy_ctrl); + + ret = usb_phy_gen_create_phy(dev, &k_phy->usb_phy_gen, + USB_PHY_TYPE_USB2, 0, false); + if (ret) + return ret; + + k_phy->usb_phy_gen.phy.init = keystone_usbphy_init; + k_phy->usb_phy_gen.phy.shutdown = keystone_usbphy_shutdown; + + platform_set_drvdata(pdev, k_phy); + + ret = usb_add_phy_dev(&k_phy->usb_phy_gen.phy); + if (ret) + return ret; + + return 0; +} + +static int keystone_usbphy_remove(struct platform_device *pdev) +{ + struct keystone_usbphy *k_phy = platform_get_drvdata(pdev); + + usb_remove_phy(&k_phy->usb_phy_gen.phy); + + return 0; +} + +static const struct of_device_id keystone_usbphy_ids[] = { + { .compatible = "ti,keystone-usbphy" }, + { } +}; +MODULE_DEVICE_TABLE(of, keystone_usbphy_ids); + +static struct platform_driver keystone_usbphy_driver = { + .probe = keystone_usbphy_probe, + .remove = keystone_usbphy_remove, + .driver = { + .name = "keystone-usbphy", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(keystone_usbphy_ids), + }, +}; + +module_platform_driver(keystone_usbphy_driver); + +MODULE_ALIAS("platform:keystone-usbphy"); +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_DESCRIPTION("Keystone USB phy driver"); +MODULE_LICENSE("GPL v2"); From 1b2e21b082a1c67cd7861bd86c4a46a3f0f348ea Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 10 Dec 2013 19:09:02 -0800 Subject: [PATCH 040/105] usb: renesas_usbhs: fifo: request DMAEngine once DMAEngine uses IRQ if dma_request_channel() was called, and it is using devm_request_irq() today, OTOH, dma_request_channel() will be called when each USB connection happened on this driver. This means same IRQ will be requested many times whenever each USB connected, and this IRQ isn't freed when USB disconnected. Request DMAEngine once. Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi --- drivers/usb/renesas_usbhs/fifo.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 45b94019aec8..d49f9c326035 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -1124,19 +1124,8 @@ void usbhs_fifo_init(struct usbhs_priv *priv) mod->irq_brdysts = 0; cfifo->pipe = NULL; - cfifo->tx_chan = NULL; - cfifo->rx_chan = NULL; - d0fifo->pipe = NULL; - d0fifo->tx_chan = NULL; - d0fifo->rx_chan = NULL; - d1fifo->pipe = NULL; - d1fifo->tx_chan = NULL; - d1fifo->rx_chan = NULL; - - usbhsf_dma_init(priv, usbhsf_get_d0fifo(priv)); - usbhsf_dma_init(priv, usbhsf_get_d1fifo(priv)); } void usbhs_fifo_quit(struct usbhs_priv *priv) @@ -1147,9 +1136,6 @@ void usbhs_fifo_quit(struct usbhs_priv *priv) mod->irq_ready = NULL; mod->irq_bempsts = 0; mod->irq_brdysts = 0; - - usbhsf_dma_quit(priv, usbhsf_get_d0fifo(priv)); - usbhsf_dma_quit(priv, usbhsf_get_d1fifo(priv)); } int usbhs_fifo_probe(struct usbhs_priv *priv) @@ -1171,6 +1157,7 @@ int usbhs_fifo_probe(struct usbhs_priv *priv) fifo->ctr = D0FIFOCTR; fifo->tx_slave.shdma_slave.slave_id = usbhs_get_dparam(priv, d0_tx_id); fifo->rx_slave.shdma_slave.slave_id = usbhs_get_dparam(priv, d0_rx_id); + usbhsf_dma_init(priv, fifo); /* D1FIFO */ fifo = usbhsf_get_d1fifo(priv); @@ -1180,10 +1167,13 @@ int usbhs_fifo_probe(struct usbhs_priv *priv) fifo->ctr = D1FIFOCTR; fifo->tx_slave.shdma_slave.slave_id = usbhs_get_dparam(priv, d1_tx_id); fifo->rx_slave.shdma_slave.slave_id = usbhs_get_dparam(priv, d1_rx_id); + usbhsf_dma_init(priv, fifo); return 0; } void usbhs_fifo_remove(struct usbhs_priv *priv) { + usbhsf_dma_quit(priv, usbhsf_get_d0fifo(priv)); + usbhsf_dma_quit(priv, usbhsf_get_d1fifo(priv)); } From 31e322272d9d7da0724ae6e3180478575aa48909 Mon Sep 17 00:00:00 2001 From: Neil Zhang Date: Wed, 11 Dec 2013 14:45:14 +0800 Subject: [PATCH 041/105] usb: phy: initialize the notifier when add a new phy We need to initialize the notifer before use it. So lets initialize it when add a new phy device to reduce the code redundancy. Signed-off-by: Neil Zhang Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-ab8500-usb.c | 2 -- drivers/usb/phy/phy-generic.c | 1 - drivers/usb/phy/phy-gpio-vbus-usb.c | 2 -- drivers/usb/phy/phy-mxs-usb.c | 2 -- drivers/usb/phy/phy.c | 4 ++++ 5 files changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index 087402350b6d..11ab2c45e462 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -1415,8 +1415,6 @@ static int ab8500_usb_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ab); - ATOMIC_INIT_NOTIFIER_HEAD(&ab->phy.notifier); - /* all: Disable phy when called from set_host and set_peripheral */ INIT_WORK(&ab->phy_dis_work, ab8500_usb_phy_disable_work); diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c index fce3a9e9bb5d..cd19bbc6e3cd 100644 --- a/drivers/usb/phy/phy-generic.c +++ b/drivers/usb/phy/phy-generic.c @@ -210,7 +210,6 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_gen_xceiv *nop, nop->phy.otg->set_host = nop_set_host; nop->phy.otg->set_peripheral = nop_set_peripheral; - ATOMIC_INIT_NOTIFIER_HEAD(&nop->phy.notifier); return 0; } EXPORT_SYMBOL_GPL(usb_phy_gen_create_phy); diff --git a/drivers/usb/phy/phy-gpio-vbus-usb.c b/drivers/usb/phy/phy-gpio-vbus-usb.c index 02799a5efcd4..69462e09d014 100644 --- a/drivers/usb/phy/phy-gpio-vbus-usb.c +++ b/drivers/usb/phy/phy-gpio-vbus-usb.c @@ -314,8 +314,6 @@ static int gpio_vbus_probe(struct platform_device *pdev) goto err_irq; } - ATOMIC_INIT_NOTIFIER_HEAD(&gpio_vbus->phy.notifier); - INIT_DELAYED_WORK(&gpio_vbus->work, gpio_vbus_work); gpio_vbus->vbus_draw = regulator_get(&pdev->dev, "vbus_draw"); diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index 797c45b9ddab..fa44c0f3e861 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -167,8 +167,6 @@ static int mxs_phy_probe(struct platform_device *pdev) mxs_phy->phy.notify_disconnect = mxs_phy_on_disconnect; mxs_phy->phy.type = USB_PHY_TYPE_USB2; - ATOMIC_INIT_NOTIFIER_HEAD(&mxs_phy->phy.notifier); - mxs_phy->clk = clk; platform_set_drvdata(pdev, &mxs_phy->phy); diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c index 1b74523e1fee..e6f61e4361df 100644 --- a/drivers/usb/phy/phy.c +++ b/drivers/usb/phy/phy.c @@ -329,6 +329,8 @@ int usb_add_phy(struct usb_phy *x, enum usb_phy_type type) return -EINVAL; } + ATOMIC_INIT_NOTIFIER_HEAD(&x->notifier); + spin_lock_irqsave(&phy_lock, flags); list_for_each_entry(phy, &phy_list, head) { @@ -367,6 +369,8 @@ int usb_add_phy_dev(struct usb_phy *x) return -EINVAL; } + ATOMIC_INIT_NOTIFIER_HEAD(&x->notifier); + spin_lock_irqsave(&phy_lock, flags); list_for_each_entry(phy_bind, &phy_bind_list, list) if (!(strcmp(phy_bind->phy_dev_name, dev_name(x->dev)))) From 295538ff7e5389d5aee5845c548f114b793d60c9 Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Fri, 6 Dec 2013 13:03:44 +0100 Subject: [PATCH 042/105] usb: gadget: s3c-hsotg: fix maxpacket size in s3c_hsotg_irq_enumdone This patch set maximum possible maxpacket value for each speed. Previous values didn't allow to use maxpacket sizes greater than 64 in full speed and 512 in high speed, although hardware is able to handle up to 1023 in fs and 1024 in hs. Tested-by: Matt Porter Signed-off-by: Robert Baldyga Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/s3c-hsotg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 9875d9c0823f..2fa7a9c593e3 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -2080,13 +2080,13 @@ static void s3c_hsotg_irq_enumdone(struct s3c_hsotg *hsotg) case DSTS_EnumSpd_FS48: hsotg->gadget.speed = USB_SPEED_FULL; ep0_mps = EP0_MPS_LIMIT; - ep_mps = 64; + ep_mps = 1023; break; case DSTS_EnumSpd_HS: hsotg->gadget.speed = USB_SPEED_HIGH; ep0_mps = EP0_MPS_LIMIT; - ep_mps = 512; + ep_mps = 1024; break; case DSTS_EnumSpd_LS: From b963a81a4803fb387d6551ea0fb66876e0c8ff12 Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Fri, 6 Dec 2013 13:03:45 +0100 Subject: [PATCH 043/105] usb: gadget: s3c-hsotg: add flush TX FIFO when kill all requests This patch adds flushing TX FIFO in kill_all_requests() function in dedicated-fifo mode. It's because when requests are killed (when endpoint is disabled or in case of device reset/disconnection) in FIFO can stay some unsent data. In the worst case FIFO can stay full, and then if endpoint will be back enabled, sending new data will be impossible. Signed-off-by: Robert Baldyga Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/s3c-hsotg.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 2fa7a9c593e3..6c80bfcefa87 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -2150,6 +2150,9 @@ static void kill_all_requests(struct s3c_hsotg *hsotg, s3c_hsotg_complete_request(hsotg, ep, req, result); } + if(hsotg->dedicated_fifos) + if ((readl(hsotg->regs + DTXFSTS(ep->index)) & 0xffff) * 4 < 3072) + s3c_hsotg_txfifo_flush(hsotg, ep->index); } #define call_gadget(_hs, _entry) \ From 1933861db4f7c9beecca5cc7460c6c814554cc34 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Dec 2013 15:15:21 +0100 Subject: [PATCH 044/105] usb: gadget: configfs: allow setting function instance's name USB function's configfs config group is created in a generic way in usb/gadget/configfs.c:function_make(), which in turn delegates actual allocation and setup of the USB function instance to a particular implementation, e.g. in f_acm.c. The said implementation does its job in a parameter-less function e.g. acm_alloc_instance(), which results in creating an unnamed config group, whose name is set later in function_make(). function_make() creates the name by parsing a string of the form: . which comes from userspace as a parameter to mkdir invocation. Up to now only has been used, while has been ignored. This patch adds a set_inst_name() operation to struct usb_function_instance which allows passing the from function_make() so that it is not ignored. It is entirely up to the implementor of set_inst_name() what to do with the . In a typical case, the struct usb_function_instance is embedded in a larger struct which is retrieved in set_inst_name() with container_of(), and the larger struct contains a field to store the . Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/configfs.c | 7 +++++++ include/linux/usb/composite.h | 2 ++ 2 files changed, 9 insertions(+) diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 25885112fa35..d6c8ab4a5327 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -564,6 +564,13 @@ static struct config_group *function_make( usb_put_function_instance(fi); return ERR_PTR(ret); } + if (fi->set_inst_name) { + ret = fi->set_inst_name(fi, instance_name); + if (ret) { + usb_put_function_instance(fi); + return ERR_PTR(ret); + } + } gi = container_of(group, struct gadget_info, functions_group); diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 5e61589fc166..dba63f53906c 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -468,6 +468,8 @@ struct usb_function_instance { struct config_group group; struct list_head cfs_list; struct usb_function_driver *fd; + int (*set_inst_name)(struct usb_function_instance *inst, + const char *name); void (*free_func_inst)(struct usb_function_instance *inst); }; From 1ec8f00f34a78f8d43504ffba3f31eddf4433408 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Dec 2013 15:15:22 +0100 Subject: [PATCH 045/105] usb: gadget: g_ffs: remove a reduntant gfs_ether_setup variable Since d6a0143985489e470a118605352f4b18df0ce142 usb: gadget: move the global the_dev variable to their users "the_dev" variable can be used as a "setup done" flag; non-NULL meaning "setup done", NULL meaning "setup not done". Moreover, gether_cleanup() can be safely called with a NULL argument. Corrected a comment to be consistent with the code. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/g_ffs.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index 2344efe4f4ce..e9540824e713 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c @@ -182,7 +182,6 @@ static __refdata struct usb_composite_driver gfs_driver = { static DEFINE_MUTEX(gfs_lock); static unsigned int missing_funcs; -static bool gfs_ether_setup; static bool gfs_registered; static bool gfs_single_func; static struct gfs_ffs_obj *ffs_tab; @@ -364,7 +363,6 @@ static int gfs_bind(struct usb_composite_dev *cdev) ret = PTR_ERR(the_dev); goto error_quick; } - gfs_ether_setup = true; ret = usb_string_ids_tab(cdev, gfs_strings); if (unlikely(ret < 0)) @@ -401,8 +399,8 @@ error_unbind: functionfs_unbind(ffs_tab[i].ffs_data); error: gether_cleanup(the_dev); + the_dev = NULL; error_quick: - gfs_ether_setup = false; return ret; } @@ -415,18 +413,17 @@ static int gfs_unbind(struct usb_composite_dev *cdev) ENTER(); + gether_cleanup(the_dev); + the_dev = NULL; + /* * We may have been called in an error recovery from * composite_bind() after gfs_unbind() failure so we need to - * check if gfs_ffs_data is not NULL since gfs_bind() handles + * check if instance's ffs_data is not NULL since gfs_bind() handles * all error recovery itself. I'd rather we werent called * from composite on orror recovery, but what you're gonna * do...? */ - if (gfs_ether_setup) - gether_cleanup(the_dev); - gfs_ether_setup = false; - for (i = func_num; i--; ) if (ffs_tab[i].ffs_data) functionfs_unbind(ffs_tab[i].ffs_data); From f212ad4e39759ab24434858630ac627b59e879db Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Dec 2013 15:15:23 +0100 Subject: [PATCH 046/105] usb: gadget: g_ffs: convert to new interface of f_ecm There is a new funtion interface and g_ffs is the last gadget to use the old. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 1 + drivers/usb/gadget/g_ffs.c | 93 ++++++++++++++++++++++++++++++++------ 2 files changed, 80 insertions(+), 14 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 5f1d4443aa0c..b135856852ec 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -895,6 +895,7 @@ config USB_FUNCTIONFS_ETH bool "Include configuration with CDC ECM (Ethernet)" depends on USB_FUNCTIONFS && NET select USB_U_ETHER + select USB_F_ECM help Include a configuration with CDC ECM function (Ethernet) and the Function Filesystem. diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index e9540824e713..cda4c5d34260 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c @@ -28,8 +28,7 @@ # define USB_ETH_RNDIS y # endif -#define USBF_ECM_INCLUDED -# include "f_ecm.c" +# include "u_ecm.h" #define USB_FSUBSET_INCLUDED # include "f_subset.c" # ifdef USB_ETH_RNDIS @@ -39,11 +38,15 @@ # endif # include "u_ether.h" +USB_ETHERNET_MODULE_PARAMETERS(); + static u8 gfs_host_mac[ETH_ALEN]; static struct eth_dev *the_dev; # ifdef CONFIG_USB_FUNCTIONFS_ETH static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], struct eth_dev *dev); +static struct usb_function_instance *fi_ecm; +static struct usb_function *f_ecm; # endif #else # define the_dev NULL @@ -76,10 +79,6 @@ struct gfs_ffs_obj { USB_GADGET_COMPOSITE_OPTIONS(); -#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS -USB_ETHERNET_MODULE_PARAMETERS(); -#endif - static struct usb_device_descriptor gfs_dev_desc = { .bLength = sizeof gfs_dev_desc, .bDescriptorType = USB_DT_DEVICE, @@ -355,14 +354,50 @@ static int gfs_bind(struct usb_composite_dev *cdev) if (missing_funcs) return -ENODEV; -#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS +#if defined CONFIG_USB_FUNCTIONFS_ETH + if (can_support_ecm(cdev->gadget)) { + struct f_ecm_opts *ecm_opts; + + fi_ecm = usb_get_function_instance("ecm"); + if (IS_ERR(fi_ecm)) + return PTR_ERR(fi_ecm); + ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst); + + gether_set_qmult(ecm_opts->net, qmult); + + if (!gether_set_host_addr(ecm_opts->net, host_addr)) + pr_info("using host ethernet address: %s", host_addr); + if (!gether_set_dev_addr(ecm_opts->net, dev_addr)) + pr_info("using self ethernet address: %s", dev_addr); + + the_dev = netdev_priv(ecm_opts->net); + } else { + the_dev = gether_setup(cdev->gadget, dev_addr, host_addr, + gfs_host_mac, qmult); + } + +#elif defined CONFIG_USB_FUNCTIONFS_RNDIS + the_dev = gether_setup(cdev->gadget, dev_addr, host_addr, gfs_host_mac, qmult); #endif - if (IS_ERR(the_dev)) { - ret = PTR_ERR(the_dev); - goto error_quick; + if (IS_ERR(the_dev)) + return PTR_ERR(the_dev); + +#if defined CONFIG_USB_FUNCTIONFS_RNDIS && defined CONFIG_USB_FUNCTIONFS_ETH + if (can_support_ecm(cdev->gadget)) { + struct f_ecm_opts *ecm_opts; + + ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst); + + gether_set_gadget(ecm_opts->net, cdev->gadget); + ret = gether_register_netdev(ecm_opts->net); + if (ret) + goto error; + ecm_opts->bound = true; + gether_get_host_addr_u8(ecm_opts->net, gfs_host_mac); } +#endif ret = usb_string_ids_tab(cdev, gfs_strings); if (unlikely(ret < 0)) @@ -398,9 +433,16 @@ error_unbind: for (i = 0; i < func_num; i++) functionfs_unbind(ffs_tab[i].ffs_data); error: +#if defined CONFIG_USB_FUNCTIONFS_ETH + if (can_support_ecm(cdev->gadget)) + usb_put_function_instance(fi_ecm); + else + gether_cleanup(the_dev); + the_dev = NULL; +#elif defined CONFIG_USB_FUNCTIONFS_RNDIS gether_cleanup(the_dev); the_dev = NULL; -error_quick: +#endif return ret; } @@ -413,8 +455,19 @@ static int gfs_unbind(struct usb_composite_dev *cdev) ENTER(); + +#if defined CONFIG_USB_FUNCTIONFS_ETH + if (can_support_ecm(cdev->gadget)) { + usb_put_function(f_ecm); + usb_put_function_instance(fi_ecm); + } else { + gether_cleanup(the_dev); + } + the_dev = NULL; +#elif defined CONFIG_USB_FUNCTIONFS_RNDIS gether_cleanup(the_dev); the_dev = NULL; +#endif /* * We may have been called in an error recovery from @@ -483,9 +536,21 @@ static int gfs_do_config(struct usb_configuration *c) static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], struct eth_dev *dev) { - return can_support_ecm(c->cdev->gadget) - ? ecm_bind_config(c, ethaddr, dev) - : geth_bind_config(c, ethaddr, dev); + int status = 0; + + if (can_support_ecm(c->cdev->gadget)) { + f_ecm = usb_get_function(fi_ecm); + if (IS_ERR(f_ecm)) + return PTR_ERR(f_ecm); + + status = usb_add_function(c, f_ecm); + if (status < 0) + usb_put_function(f_ecm); + + } else { + status = geth_bind_config(c, ethaddr, dev); + } + return status; } #endif From f1a2ca2e4be7327e9f71257c88c0b4f511cb6677 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Dec 2013 15:15:24 +0100 Subject: [PATCH 047/105] usb: gadget: f_ecm: remove compatibility layer There are no old function interface users left, so the old interface can be removed. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmim Park Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_ecm.c | 73 +----------------------------------- drivers/usb/gadget/u_ether.h | 2 - 2 files changed, 1 insertion(+), 74 deletions(-) diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c index 8d9e6f7e8f1a..798760fa7e70 100644 --- a/drivers/usb/gadget/f_ecm.c +++ b/drivers/usb/gadget/f_ecm.c @@ -691,7 +691,6 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) int status; struct usb_ep *ep; -#ifndef USBF_ECM_INCLUDED struct f_ecm_opts *ecm_opts; if (!can_support_ecm(cdev->gadget)) @@ -715,7 +714,7 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) return status; ecm_opts->bound = true; } -#endif + us = usb_gstrings_attach(cdev, ecm_strings, ARRAY_SIZE(ecm_string_defs)); if (IS_ERR(us)) @@ -834,74 +833,6 @@ fail: return status; } -#ifdef USBF_ECM_INCLUDED - -static void -ecm_old_unbind(struct usb_configuration *c, struct usb_function *f) -{ - struct f_ecm *ecm = func_to_ecm(f); - - DBG(c->cdev, "ecm unbind\n"); - - usb_free_all_descriptors(f); - - kfree(ecm->notify_req->buf); - usb_ep_free_request(ecm->notify, ecm->notify_req); - kfree(ecm); -} - -/** - * ecm_bind_config - add CDC Ethernet network link to a configuration - * @c: the configuration to support the network link - * @ethaddr: a buffer in which the ethernet address of the host side - * side of the link was recorded - * @dev: eth_dev structure - * Context: single threaded during gadget setup - * - * Returns zero on success, else negative errno. - * - * Caller must have called @gether_setup(). Caller is also responsible - * for calling @gether_cleanup() before module unload. - */ -int -ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], - struct eth_dev *dev) -{ - struct f_ecm *ecm; - int status; - - if (!can_support_ecm(c->cdev->gadget) || !ethaddr) - return -EINVAL; - - /* allocate and initialize one new instance */ - ecm = kzalloc(sizeof *ecm, GFP_KERNEL); - if (!ecm) - return -ENOMEM; - - /* export host's Ethernet address in CDC format */ - snprintf(ecm->ethaddr, sizeof ecm->ethaddr, "%pm", ethaddr); - ecm_string_defs[1].s = ecm->ethaddr; - - ecm->port.ioport = dev; - ecm->port.cdc_filter = DEFAULT_FILTER; - - ecm->port.func.name = "cdc_ethernet"; - /* descriptors are per-instance copies */ - ecm->port.func.bind = ecm_bind; - ecm->port.func.unbind = ecm_old_unbind; - ecm->port.func.set_alt = ecm_set_alt; - ecm->port.func.get_alt = ecm_get_alt; - ecm->port.func.setup = ecm_setup; - ecm->port.func.disable = ecm_disable; - - status = usb_add_function(c, &ecm->port.func); - if (status) - kfree(ecm); - return status; -} - -#else - static inline struct f_ecm_opts *to_f_ecm_opts(struct config_item *item) { return container_of(to_config_group(item), struct f_ecm_opts, @@ -1040,5 +971,3 @@ static struct usb_function *ecm_alloc(struct usb_function_instance *fi) DECLARE_USB_FUNCTION_INIT(ecm, ecm_alloc_inst, ecm_alloc); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Brownell"); - -#endif diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/u_ether.h index a32b41e27450..c06059568d6c 100644 --- a/drivers/usb/gadget/u_ether.h +++ b/drivers/usb/gadget/u_ether.h @@ -270,8 +270,6 @@ static inline bool can_support_ecm(struct usb_gadget *gadget) /* each configuration may bind one instance of an ethernet link */ int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], struct eth_dev *dev); -int ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], - struct eth_dev *dev); #ifdef USB_ETH_RNDIS From 85aec59fabaa89f96cafe77fa02ee566e0499636 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Dec 2013 15:15:25 +0100 Subject: [PATCH 048/105] usb: gadget: g_ffs: convert to new interface of f_subset There is a new function interface of f_subset and g_ffs is the last to use the old one. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 1 + drivers/usb/gadget/g_ffs.c | 69 ++++++++++++++++++++++++++------------ 2 files changed, 48 insertions(+), 22 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index b135856852ec..d8a1d5cb1e75 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -896,6 +896,7 @@ config USB_FUNCTIONFS_ETH depends on USB_FUNCTIONFS && NET select USB_U_ETHER select USB_F_ECM + select USB_F_SUBSET help Include a configuration with CDC ECM function (Ethernet) and the Function Filesystem. diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index cda4c5d34260..99ae8ec23c9c 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c @@ -21,6 +21,8 @@ * a "gcc --combine ... part1.c part2.c part3.c ... " build would. */ #if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS +#include + # if defined USB_ETH_RNDIS # undef USB_ETH_RNDIS # endif @@ -29,8 +31,7 @@ # endif # include "u_ecm.h" -#define USB_FSUBSET_INCLUDED -# include "f_subset.c" +# include "u_gether.h" # ifdef USB_ETH_RNDIS # define USB_FRNDIS_INCLUDED # include "f_rndis.c" @@ -47,6 +48,8 @@ static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], struct eth_dev *dev); static struct usb_function_instance *fi_ecm; static struct usb_function *f_ecm; +static struct usb_function_instance *fi_geth; +static struct usb_function *f_geth; # endif #else # define the_dev NULL @@ -348,6 +351,9 @@ static void functionfs_release_dev_callback(struct ffs_data *ffs_data) */ static int gfs_bind(struct usb_composite_dev *cdev) { +#if defined CONFIG_USB_FUNCTIONFS_ETH + struct net_device *net; +#endif int ret, i; ENTER(); @@ -362,19 +368,25 @@ static int gfs_bind(struct usb_composite_dev *cdev) if (IS_ERR(fi_ecm)) return PTR_ERR(fi_ecm); ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst); - - gether_set_qmult(ecm_opts->net, qmult); - - if (!gether_set_host_addr(ecm_opts->net, host_addr)) - pr_info("using host ethernet address: %s", host_addr); - if (!gether_set_dev_addr(ecm_opts->net, dev_addr)) - pr_info("using self ethernet address: %s", dev_addr); - - the_dev = netdev_priv(ecm_opts->net); + net = ecm_opts->net; } else { - the_dev = gether_setup(cdev->gadget, dev_addr, host_addr, - gfs_host_mac, qmult); + struct f_gether_opts *geth_opts; + + fi_geth = usb_get_function_instance("geth"); + if (IS_ERR(fi_geth)) + return PTR_ERR(fi_geth); + geth_opts = container_of(fi_geth, struct f_gether_opts, + func_inst); + net = geth_opts->net; } + gether_set_qmult(net, qmult); + + if (!gether_set_host_addr(net, host_addr)) + pr_info("using host ethernet address: %s", host_addr); + if (!gether_set_dev_addr(net, dev_addr)) + pr_info("using self ethernet address: %s", dev_addr); + + the_dev = netdev_priv(net); #elif defined CONFIG_USB_FUNCTIONFS_RNDIS @@ -385,18 +397,24 @@ static int gfs_bind(struct usb_composite_dev *cdev) return PTR_ERR(the_dev); #if defined CONFIG_USB_FUNCTIONFS_RNDIS && defined CONFIG_USB_FUNCTIONFS_ETH + gether_set_gadget(net, cdev->gadget); + ret = gether_register_netdev(net); + if (ret) + goto error; + if (can_support_ecm(cdev->gadget)) { struct f_ecm_opts *ecm_opts; ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst); - - gether_set_gadget(ecm_opts->net, cdev->gadget); - ret = gether_register_netdev(ecm_opts->net); - if (ret) - goto error; ecm_opts->bound = true; - gether_get_host_addr_u8(ecm_opts->net, gfs_host_mac); + } else { + struct f_gether_opts *geth_opts; + + geth_opts = container_of(fi_geth, struct f_gether_opts, + func_inst); + geth_opts->bound = true; } + gether_get_host_addr_u8(net, gfs_host_mac); #endif ret = usb_string_ids_tab(cdev, gfs_strings); @@ -437,7 +455,7 @@ error: if (can_support_ecm(cdev->gadget)) usb_put_function_instance(fi_ecm); else - gether_cleanup(the_dev); + usb_put_function_instance(fi_geth); the_dev = NULL; #elif defined CONFIG_USB_FUNCTIONFS_RNDIS gether_cleanup(the_dev); @@ -461,7 +479,8 @@ static int gfs_unbind(struct usb_composite_dev *cdev) usb_put_function(f_ecm); usb_put_function_instance(fi_ecm); } else { - gether_cleanup(the_dev); + usb_put_function(f_geth); + usb_put_function_instance(fi_geth); } the_dev = NULL; #elif defined CONFIG_USB_FUNCTIONFS_RNDIS @@ -548,7 +567,13 @@ static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], usb_put_function(f_ecm); } else { - status = geth_bind_config(c, ethaddr, dev); + f_geth = usb_get_function(fi_geth); + if (IS_ERR(f_geth)) + return PTR_ERR(f_geth); + + status = usb_add_function(c, f_geth); + if (status < 0) + usb_put_function(f_geth); } return status; } From 05de3fe3d1c124e4de53b111806c5740acd8d732 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Dec 2013 15:15:26 +0100 Subject: [PATCH 049/105] usb: gadget: f_subset: remove compatibility layer There are no old function interface users left, so the old interface can be removed. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_subset.c | 60 +---------------------------------- drivers/usb/gadget/u_ether.h | 3 -- 2 files changed, 1 insertion(+), 62 deletions(-) diff --git a/drivers/usb/gadget/f_subset.c b/drivers/usb/gadget/f_subset.c index 7c8674fa7e80..f1a59190ac9a 100644 --- a/drivers/usb/gadget/f_subset.c +++ b/drivers/usb/gadget/f_subset.c @@ -301,7 +301,6 @@ geth_bind(struct usb_configuration *c, struct usb_function *f) int status; struct usb_ep *ep; -#ifndef USB_FSUBSET_INCLUDED struct f_gether_opts *gether_opts; gether_opts = container_of(f->fi, struct f_gether_opts, func_inst); @@ -322,7 +321,7 @@ geth_bind(struct usb_configuration *c, struct usb_function *f) return status; gether_opts->bound = true; } -#endif + us = usb_gstrings_attach(cdev, geth_strings, ARRAY_SIZE(geth_string_defs)); if (IS_ERR(us)) @@ -393,61 +392,6 @@ fail: return status; } -#ifdef USB_FSUBSET_INCLUDED - -static void -geth_old_unbind(struct usb_configuration *c, struct usb_function *f) -{ - geth_string_defs[0].id = 0; - usb_free_all_descriptors(f); - kfree(func_to_geth(f)); -} - -/** - * geth_bind_config - add CDC Subset network link to a configuration - * @c: the configuration to support the network link - * @ethaddr: a buffer in which the ethernet address of the host side - * side of the link was recorded - * @dev: eth_dev structure - * Context: single threaded during gadget setup - * - * Returns zero on success, else negative errno. - * - * Caller must have called @gether_setup(). Caller is also responsible - * for calling @gether_cleanup() before module unload. - */ -int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], - struct eth_dev *dev) -{ - struct f_gether *geth; - int status; - - /* allocate and initialize one new instance */ - geth = kzalloc(sizeof *geth, GFP_KERNEL); - if (!geth) - return -ENOMEM; - - /* export host's Ethernet address in CDC format */ - snprintf(geth->ethaddr, sizeof geth->ethaddr, "%pm", ethaddr); - geth_string_defs[1].s = geth->ethaddr; - - geth->port.ioport = dev; - geth->port.cdc_filter = DEFAULT_FILTER; - - geth->port.func.name = "cdc_subset"; - geth->port.func.bind = geth_bind; - geth->port.func.unbind = geth_old_unbind; - geth->port.func.set_alt = geth_set_alt; - geth->port.func.disable = geth_disable; - - status = usb_add_function(c, &geth->port.func); - if (status) - kfree(geth); - return status; -} - -#else - static inline struct f_gether_opts *to_f_gether_opts(struct config_item *item) { return container_of(to_config_group(item), struct f_gether_opts, @@ -573,5 +517,3 @@ static struct usb_function *geth_alloc(struct usb_function_instance *fi) DECLARE_USB_FUNCTION_INIT(geth, geth_alloc_inst, geth_alloc); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Brownell"); - -#endif diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/u_ether.h index c06059568d6c..72b6facde40d 100644 --- a/drivers/usb/gadget/u_ether.h +++ b/drivers/usb/gadget/u_ether.h @@ -268,9 +268,6 @@ static inline bool can_support_ecm(struct usb_gadget *gadget) } /* each configuration may bind one instance of an ethernet link */ -int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], - struct eth_dev *dev); - #ifdef USB_ETH_RNDIS int rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], From 6e257b14218d0d963058a96736da6c6e2abb08f3 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Dec 2013 15:15:27 +0100 Subject: [PATCH 050/105] usb: gadget: g_ffs: convert to new interface of f_rndis There is a new interface of f_rndis and g_ffs is the last to use the old one. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 1 + drivers/usb/gadget/g_ffs.c | 105 +++++++++++++++++++++++-------------- 2 files changed, 66 insertions(+), 40 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index d8a1d5cb1e75..8cea14f3d590 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -906,6 +906,7 @@ config USB_FUNCTIONFS_RNDIS depends on USB_FUNCTIONFS && NET select USB_U_ETHER select USB_U_RNDIS + select USB_F_RNDIS help Include a configuration with RNDIS function (Ethernet) and the Filesystem. diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index 99ae8ec23c9c..7099a112c734 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c @@ -33,29 +33,25 @@ # include "u_ecm.h" # include "u_gether.h" # ifdef USB_ETH_RNDIS -# define USB_FRNDIS_INCLUDED -# include "f_rndis.c" +# include "u_rndis.h" # include "rndis.h" # endif # include "u_ether.h" USB_ETHERNET_MODULE_PARAMETERS(); -static u8 gfs_host_mac[ETH_ALEN]; -static struct eth_dev *the_dev; # ifdef CONFIG_USB_FUNCTIONFS_ETH -static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], - struct eth_dev *dev); +static int eth_bind_config(struct usb_configuration *c); static struct usb_function_instance *fi_ecm; static struct usb_function *f_ecm; static struct usb_function_instance *fi_geth; static struct usb_function *f_geth; # endif -#else -# define the_dev NULL -# define gether_cleanup(dev) do { } while (0) -# define gfs_host_mac NULL -struct eth_dev; +# ifdef CONFIG_USB_FUNCTIONFS_RNDIS +static int bind_rndis_config(struct usb_configuration *c); +static struct usb_function_instance *fi_rndis; +static struct usb_function *f_rndis; +# endif #endif #include "f_fs.c" @@ -148,12 +144,11 @@ static struct usb_gadget_strings *gfs_dev_strings[] = { struct gfs_configuration { struct usb_configuration c; - int (*eth)(struct usb_configuration *c, u8 *ethaddr, - struct eth_dev *dev); + int (*eth)(struct usb_configuration *c); } gfs_configurations[] = { #ifdef CONFIG_USB_FUNCTIONFS_RNDIS { - .eth = rndis_bind_config, + .eth = bind_rndis_config, }, #endif @@ -351,7 +346,7 @@ static void functionfs_release_dev_callback(struct ffs_data *ffs_data) */ static int gfs_bind(struct usb_composite_dev *cdev) { -#if defined CONFIG_USB_FUNCTIONFS_ETH +#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS struct net_device *net; #endif int ret, i; @@ -379,28 +374,38 @@ static int gfs_bind(struct usb_composite_dev *cdev) func_inst); net = geth_opts->net; } - gether_set_qmult(net, qmult); +#endif +#ifdef CONFIG_USB_FUNCTIONFS_RNDIS + { + struct f_rndis_opts *rndis_opts; + + fi_rndis = usb_get_function_instance("rndis"); + if (IS_ERR(fi_rndis)) { + ret = PTR_ERR(fi_rndis); + goto error; + } + rndis_opts = container_of(fi_rndis, struct f_rndis_opts, + func_inst); +#ifndef CONFIG_USB_FUNCTIONFS_ETH + net = rndis_opts->net; +#endif + } +#endif + +#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS + gether_set_qmult(net, qmult); if (!gether_set_host_addr(net, host_addr)) pr_info("using host ethernet address: %s", host_addr); if (!gether_set_dev_addr(net, dev_addr)) pr_info("using self ethernet address: %s", dev_addr); - - the_dev = netdev_priv(net); - -#elif defined CONFIG_USB_FUNCTIONFS_RNDIS - - the_dev = gether_setup(cdev->gadget, dev_addr, host_addr, gfs_host_mac, - qmult); #endif - if (IS_ERR(the_dev)) - return PTR_ERR(the_dev); #if defined CONFIG_USB_FUNCTIONFS_RNDIS && defined CONFIG_USB_FUNCTIONFS_ETH gether_set_gadget(net, cdev->gadget); ret = gether_register_netdev(net); if (ret) - goto error; + goto error_rndis; if (can_support_ecm(cdev->gadget)) { struct f_ecm_opts *ecm_opts; @@ -414,12 +419,13 @@ static int gfs_bind(struct usb_composite_dev *cdev) func_inst); geth_opts->bound = true; } - gether_get_host_addr_u8(net, gfs_host_mac); + + rndis_borrow_net(fi_rndis, net); #endif ret = usb_string_ids_tab(cdev, gfs_strings); if (unlikely(ret < 0)) - goto error; + goto error_rndis; gfs_dev_desc.iProduct = gfs_strings[USB_GADGET_PRODUCT_IDX].id; for (i = func_num; i--; ) { @@ -427,7 +433,7 @@ static int gfs_bind(struct usb_composite_dev *cdev) if (unlikely(ret < 0)) { while (++i < func_num) functionfs_unbind(ffs_tab[i].ffs_data); - goto error; + goto error_rndis; } } @@ -450,16 +456,16 @@ static int gfs_bind(struct usb_composite_dev *cdev) error_unbind: for (i = 0; i < func_num; i++) functionfs_unbind(ffs_tab[i].ffs_data); +error_rndis: +#ifdef CONFIG_USB_FUNCTIONFS_RNDIS + usb_put_function_instance(fi_rndis); error: +#endif #if defined CONFIG_USB_FUNCTIONFS_ETH if (can_support_ecm(cdev->gadget)) usb_put_function_instance(fi_ecm); else usb_put_function_instance(fi_geth); - the_dev = NULL; -#elif defined CONFIG_USB_FUNCTIONFS_RNDIS - gether_cleanup(the_dev); - the_dev = NULL; #endif return ret; } @@ -474,6 +480,11 @@ static int gfs_unbind(struct usb_composite_dev *cdev) ENTER(); +#ifdef CONFIG_USB_FUNCTIONFS_RNDIS + usb_put_function(f_rndis); + usb_put_function_instance(fi_rndis); +#endif + #if defined CONFIG_USB_FUNCTIONFS_ETH if (can_support_ecm(cdev->gadget)) { usb_put_function(f_ecm); @@ -482,10 +493,6 @@ static int gfs_unbind(struct usb_composite_dev *cdev) usb_put_function(f_geth); usb_put_function_instance(fi_geth); } - the_dev = NULL; -#elif defined CONFIG_USB_FUNCTIONFS_RNDIS - gether_cleanup(the_dev); - the_dev = NULL; #endif /* @@ -523,7 +530,7 @@ static int gfs_do_config(struct usb_configuration *c) } if (gc->eth) { - ret = gc->eth(c, gfs_host_mac, the_dev); + ret = gc->eth(c); if (unlikely(ret < 0)) return ret; } @@ -552,8 +559,7 @@ static int gfs_do_config(struct usb_configuration *c) #ifdef CONFIG_USB_FUNCTIONFS_ETH -static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], - struct eth_dev *dev) +static int eth_bind_config(struct usb_configuration *c) { int status = 0; @@ -579,3 +585,22 @@ static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], } #endif + +#ifdef CONFIG_USB_FUNCTIONFS_RNDIS + +static int bind_rndis_config(struct usb_configuration *c) +{ + int status = 0; + + f_rndis = usb_get_function(fi_rndis); + if (IS_ERR(f_rndis)) + return PTR_ERR(f_rndis); + + status = usb_add_function(c, f_rndis); + if (status < 0) + usb_put_function(f_rndis); + + return status; +} + +#endif From 31f89db1b81ea1c54ca7d186a0c0ebab0d4988f0 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Dec 2013 15:15:28 +0100 Subject: [PATCH 051/105] usb: gadget: f_rndis: remove compatibility layer There are no old function interface users left, so the old interface can be removed. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_rndis.c | 72 +----------------------------------- drivers/usb/gadget/u_ether.h | 36 ------------------ 2 files changed, 1 insertion(+), 107 deletions(-) diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c index 717ed7f95639..9d7c99526dda 100644 --- a/drivers/usb/gadget/f_rndis.c +++ b/drivers/usb/gadget/f_rndis.c @@ -675,7 +675,6 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) int status; struct usb_ep *ep; -#ifndef USB_FRNDIS_INCLUDED struct f_rndis_opts *rndis_opts; if (!can_support_rndis(c)) @@ -697,7 +696,7 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) return status; rndis_opts->bound = true; } -#endif + us = usb_gstrings_attach(cdev, rndis_strings, ARRAY_SIZE(rndis_string_defs)); if (IS_ERR(us)) @@ -782,13 +781,6 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) rndis->port.open = rndis_open; rndis->port.close = rndis_close; -#ifdef USB_FRNDIS_INCLUDED - status = rndis_register(rndis_response_available, rndis); - if (status < 0) - goto fail; - rndis->config = status; -#endif - rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0); rndis_set_host_mac(rndis->config, rndis->ethaddr); @@ -830,66 +822,6 @@ fail: return status; } -#ifdef USB_FRNDIS_INCLUDED - -static void -rndis_old_unbind(struct usb_configuration *c, struct usb_function *f) -{ - struct f_rndis *rndis = func_to_rndis(f); - - rndis_deregister(rndis->config); - - usb_free_all_descriptors(f); - - kfree(rndis->notify_req->buf); - usb_ep_free_request(rndis->notify, rndis->notify_req); - - kfree(rndis); -} - -int -rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], - u32 vendorID, const char *manufacturer, struct eth_dev *dev) -{ - struct f_rndis *rndis; - int status; - - /* allocate and initialize one new instance */ - status = -ENOMEM; - rndis = kzalloc(sizeof *rndis, GFP_KERNEL); - if (!rndis) - goto fail; - - memcpy(rndis->ethaddr, ethaddr, ETH_ALEN); - rndis->vendorID = vendorID; - rndis->manufacturer = manufacturer; - - rndis->port.ioport = dev; - /* RNDIS activates when the host changes this filter */ - rndis->port.cdc_filter = 0; - - /* RNDIS has special (and complex) framing */ - rndis->port.header_len = sizeof(struct rndis_packet_msg_type); - rndis->port.wrap = rndis_add_header; - rndis->port.unwrap = rndis_rm_hdr; - - rndis->port.func.name = "rndis"; - /* descriptors are per-instance copies */ - rndis->port.func.bind = rndis_bind; - rndis->port.func.unbind = rndis_old_unbind; - rndis->port.func.set_alt = rndis_set_alt; - rndis->port.func.setup = rndis_setup; - rndis->port.func.disable = rndis_disable; - - status = usb_add_function(c, &rndis->port.func); - if (status) - kfree(rndis); -fail: - return status; -} - -#else - void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net) { struct f_rndis_opts *opts; @@ -1050,5 +982,3 @@ static struct usb_function *rndis_alloc(struct usb_function_instance *fi) DECLARE_USB_FUNCTION_INIT(rndis, rndis_alloc_inst, rndis_alloc); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Brownell"); - -#endif diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/u_ether.h index 72b6facde40d..0f0290acea7e 100644 --- a/drivers/usb/gadget/u_ether.h +++ b/drivers/usb/gadget/u_ether.h @@ -267,40 +267,4 @@ static inline bool can_support_ecm(struct usb_gadget *gadget) return true; } -/* each configuration may bind one instance of an ethernet link */ -#ifdef USB_ETH_RNDIS - -int rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], - u32 vendorID, const char *manufacturer, struct eth_dev *dev); - -#else - -static inline int -rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], - u32 vendorID, const char *manufacturer, struct eth_dev *dev) -{ - return 0; -} - -#endif - -/** - * rndis_bind_config - add RNDIS network link to a configuration - * @c: the configuration to support the network link - * @ethaddr: a buffer in which the ethernet address of the host side - * side of the link was recorded - * Context: single threaded during gadget setup - * - * Returns zero on success, else negative errno. - * - * Caller must have called @gether_setup(). Caller is also responsible - * for calling @gether_cleanup() before module unload. - */ -static inline int rndis_bind_config(struct usb_configuration *c, - u8 ethaddr[ETH_ALEN], struct eth_dev *dev) -{ - return rndis_bind_config_vendor(c, ethaddr, 0, NULL, dev); -} - - #endif /* __U_ETHER_H */ From 9c2b85f4f99cb5c5f4b8e29ef15e344f93ec5be1 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Dec 2013 15:15:29 +0100 Subject: [PATCH 052/105] usb: gadget: rndis: merge u_rndis.ko with usb_f_rndis.ko The rndis function's users use only the new interface, so the two modules can be merged. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 7 ------- drivers/usb/gadget/Makefile | 4 +--- drivers/usb/gadget/f_rndis.c | 22 +++++++++++++++++++++- drivers/usb/gadget/rndis.c | 7 ++----- drivers/usb/gadget/u_rndis.h | 2 ++ 5 files changed, 26 insertions(+), 16 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 8cea14f3d590..aa3610ea9d9f 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -512,9 +512,6 @@ config USB_U_SERIAL config USB_U_ETHER tristate -config USB_U_RNDIS - tristate - config USB_F_SERIAL tristate @@ -642,7 +639,6 @@ config USB_CONFIGFS_RNDIS depends on USB_CONFIGFS depends on NET select USB_U_ETHER - select USB_U_RNDIS select USB_F_RNDIS help Microsoft Windows XP bundles the "Remote NDIS" (RNDIS) protocol, @@ -772,7 +768,6 @@ config USB_ETH depends on NET select USB_LIBCOMPOSITE select USB_U_ETHER - select USB_U_RNDIS select USB_F_ECM select USB_F_SUBSET select CRC32 @@ -905,7 +900,6 @@ config USB_FUNCTIONFS_RNDIS bool "Include configuration with RNDIS (Ethernet)" depends on USB_FUNCTIONFS && NET select USB_U_ETHER - select USB_U_RNDIS select USB_F_RNDIS help Include a configuration with RNDIS function (Ethernet) and the Filesystem. @@ -1080,7 +1074,6 @@ config USB_G_MULTI config USB_G_MULTI_RNDIS bool "RNDIS + CDC Serial + Storage configuration" depends on USB_G_MULTI - select USB_U_RNDIS select USB_F_RNDIS default y help diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 40bd0f80b5ad..0c40277931f4 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -47,8 +47,6 @@ obj-$(CONFIG_USB_F_SERIAL) += usb_f_serial.o usb_f_obex-y := f_obex.o obj-$(CONFIG_USB_F_OBEX) += usb_f_obex.o obj-$(CONFIG_USB_U_ETHER) += u_ether.o -u_rndis-y := rndis.o -obj-$(CONFIG_USB_U_RNDIS) += u_rndis.o usb_f_ncm-y := f_ncm.o obj-$(CONFIG_USB_F_NCM) += usb_f_ncm.o usb_f_ecm-y := f_ecm.o @@ -59,7 +57,7 @@ usb_f_eem-y := f_eem.o obj-$(CONFIG_USB_F_EEM) += usb_f_eem.o usb_f_ecm_subset-y := f_subset.o obj-$(CONFIG_USB_F_SUBSET) += usb_f_ecm_subset.o -usb_f_rndis-y := f_rndis.o +usb_f_rndis-y := f_rndis.o rndis.o obj-$(CONFIG_USB_F_RNDIS) += usb_f_rndis.o usb_f_mass_storage-y := f_mass_storage.o storage_common.o obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c index 9d7c99526dda..c11761ce5113 100644 --- a/drivers/usb/gadget/f_rndis.c +++ b/drivers/usb/gadget/f_rndis.c @@ -979,6 +979,26 @@ static struct usb_function *rndis_alloc(struct usb_function_instance *fi) return &rndis->port.func; } -DECLARE_USB_FUNCTION_INIT(rndis, rndis_alloc_inst, rndis_alloc); +DECLARE_USB_FUNCTION(rndis, rndis_alloc_inst, rndis_alloc); + +static int __init rndis_mod_init(void) +{ + int ret; + + ret = rndis_init(); + if (ret) + return ret; + + return usb_function_register(&rndisusb_func); +} +module_init(rndis_mod_init); + +static void __exit rndis_mod_exit(void) +{ + usb_function_unregister(&rndisusb_func); + rndis_exit(); +} +module_exit(rndis_mod_exit); + MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Brownell"); diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c index a3ad732bc812..1ffbdb4659f2 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/rndis.c @@ -1142,7 +1142,7 @@ static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS]; #endif /* CONFIG_USB_GADGET_DEBUG_FILES */ -static int rndis_init(void) +int rndis_init(void) { u8 i; @@ -1174,9 +1174,8 @@ static int rndis_init(void) return 0; } -module_init(rndis_init); -static void rndis_exit(void) +void rndis_exit(void) { #ifdef CONFIG_USB_GADGET_DEBUG_FILES u8 i; @@ -1188,6 +1187,4 @@ static void rndis_exit(void) } #endif } -module_exit(rndis_exit); -MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/u_rndis.h b/drivers/usb/gadget/u_rndis.h index c62ba82e9600..7291b15c9dce 100644 --- a/drivers/usb/gadget/u_rndis.h +++ b/drivers/usb/gadget/u_rndis.h @@ -36,6 +36,8 @@ struct f_rndis_opts { int refcnt; }; +int rndis_init(void); +void rndis_exit(void); void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net); #endif /* U_RNDIS_H */ From e6f3862fa1ecea6579dd1727869655e88be7a5ef Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Dec 2013 15:15:30 +0100 Subject: [PATCH 053/105] usb: gadget: FunctionFS: Remove VLAIS usage from gadget code The use of variable length arrays in structs (VLAIS) in the Linux Kernel code precludes the use of compilers which don't implement VLAIS (for instance the Clang compiler). This alternate patch calculates offsets into the kmalloc-ed memory buffer using macros. The previous patch required multiple kmalloc and kfree calls. This version uses "group" vs "struct" since it really is not a struct and is essentially a group of VLA in a common allocated block. This version also fixes the issues pointed out by Andrzej Pietrasiewicz and Michal Nazarewicz. Signed-off-by: Mark Charlebois Signed-off-by: Behan Webster [elimination of miexed declaration and code, checkpatch cleanup] [fixes after Michal's review] Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_fs.c | 116 +++++++++++++++++++++++++------------- 1 file changed, 76 insertions(+), 40 deletions(-) diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index 34940439bb18..91b94b1dcfe3 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -30,6 +30,31 @@ #define FUNCTIONFS_MAGIC 0xa647361 /* Chosen by a honest dice roll ;) */ +/* Variable Length Array Macros **********************************************/ +#define vla_group(groupname) size_t groupname##__next = 0 +#define vla_group_size(groupname) groupname##__next + +#define vla_item(groupname, type, name, n) \ + size_t groupname##_##name##__offset = ({ \ + size_t align_mask = __alignof__(type) - 1; \ + size_t offset = (groupname##__next + align_mask) & ~align_mask;\ + size_t size = (n) * sizeof(type); \ + groupname##__next = offset + size; \ + offset; \ + }) + +#define vla_item_with_sz(groupname, type, name, n) \ + size_t groupname##_##name##__sz = (n) * sizeof(type); \ + size_t groupname##_##name##__offset = ({ \ + size_t align_mask = __alignof__(type) - 1; \ + size_t offset = (groupname##__next + align_mask) & ~align_mask;\ + size_t size = groupname##_##name##__sz; \ + groupname##__next = offset + size; \ + offset; \ + }) + +#define vla_ptr(ptr, groupname, name) \ + ((void *) ((char *)ptr + groupname##_##name##__offset)) /* Debugging ****************************************************************/ @@ -1902,30 +1927,34 @@ static int __ffs_data_got_strings(struct ffs_data *ffs, /* Allocate everything in one chunk so there's less maintenance. */ { - struct { - struct usb_gadget_strings *stringtabs[lang_count + 1]; - struct usb_gadget_strings stringtab[lang_count]; - struct usb_string strings[lang_count*(needed_count+1)]; - } *d; unsigned i = 0; + vla_group(d); + vla_item(d, struct usb_gadget_strings *, stringtabs, + lang_count + 1); + vla_item(d, struct usb_gadget_strings, stringtab, lang_count); + vla_item(d, struct usb_string, strings, + lang_count*(needed_count+1)); - d = kmalloc(sizeof *d, GFP_KERNEL); - if (unlikely(!d)) { + char *vlabuf = kmalloc(vla_group_size(d), GFP_KERNEL); + + if (unlikely(!vlabuf)) { kfree(_data); return -ENOMEM; } - stringtabs = d->stringtabs; - t = d->stringtab; + /* Initialize the VLA pointers */ + stringtabs = vla_ptr(vlabuf, d, stringtabs); + t = vla_ptr(vlabuf, d, stringtab); i = lang_count; do { *stringtabs++ = t++; } while (--i); *stringtabs = NULL; - stringtabs = d->stringtabs; - t = d->stringtab; - s = d->strings; + /* stringtabs = vlabuf = d_stringtabs for later kfree */ + stringtabs = vla_ptr(vlabuf, d, stringtabs); + t = vla_ptr(vlabuf, d, stringtab); + s = vla_ptr(vlabuf, d, strings); strings = s; } @@ -2201,16 +2230,16 @@ static int ffs_func_bind(struct usb_configuration *c, int ret; /* Make it a single chunk, less management later on */ - struct { - struct ffs_ep eps[ffs->eps_count]; - struct usb_descriptor_header - *fs_descs[full ? ffs->fs_descs_count + 1 : 0]; - struct usb_descriptor_header - *hs_descs[high ? ffs->hs_descs_count + 1 : 0]; - short inums[ffs->interfaces_count]; - char raw_descs[high ? ffs->raw_descs_length - : ffs->raw_fs_descs_length]; - } *data; + vla_group(d); + vla_item_with_sz(d, struct ffs_ep, eps, ffs->eps_count); + vla_item_with_sz(d, struct usb_descriptor_header *, fs_descs, + full ? ffs->fs_descs_count + 1 : 0); + vla_item_with_sz(d, struct usb_descriptor_header *, hs_descs, + high ? ffs->hs_descs_count + 1 : 0); + vla_item_with_sz(d, short, inums, ffs->interfaces_count); + vla_item_with_sz(d, char, raw_descs, + high ? ffs->raw_descs_length : ffs->raw_fs_descs_length); + char *vlabuf; ENTER(); @@ -2218,21 +2247,28 @@ static int ffs_func_bind(struct usb_configuration *c, if (unlikely(!(full | high))) return -ENOTSUPP; - /* Allocate */ - data = kmalloc(sizeof *data, GFP_KERNEL); - if (unlikely(!data)) + /* Allocate a single chunk, less management later on */ + vlabuf = kmalloc(vla_group_size(d), GFP_KERNEL); + if (unlikely(!vlabuf)) return -ENOMEM; /* Zero */ - memset(data->eps, 0, sizeof data->eps); - memcpy(data->raw_descs, ffs->raw_descs + 16, sizeof data->raw_descs); - memset(data->inums, 0xff, sizeof data->inums); - for (ret = ffs->eps_count; ret; --ret) - data->eps[ret].num = -1; + memset(vla_ptr(vlabuf, d, eps), 0, d_eps__sz); + memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs + 16, + d_raw_descs__sz); + memset(vla_ptr(vlabuf, d, inums), 0xff, d_inums__sz); + for (ret = ffs->eps_count; ret; --ret) { + struct ffs_ep *ptr; - /* Save pointers */ - func->eps = data->eps; - func->interfaces_nums = data->inums; + ptr = vla_ptr(vlabuf, d, eps); + ptr[ret].num = -1; + } + + /* Save pointers + * d_eps == vlabuf, func->eps used to kfree vlabuf later + */ + func->eps = vla_ptr(vlabuf, d, eps); + func->interfaces_nums = vla_ptr(vlabuf, d, inums); /* * Go through all the endpoint descriptors and allocate @@ -2240,10 +2276,10 @@ static int ffs_func_bind(struct usb_configuration *c, * numbers without worrying that it may be described later on. */ if (likely(full)) { - func->function.fs_descriptors = data->fs_descs; + func->function.fs_descriptors = vla_ptr(vlabuf, d, fs_descs); ret = ffs_do_descs(ffs->fs_descs_count, - data->raw_descs, - sizeof data->raw_descs, + vla_ptr(vlabuf, d, raw_descs), + d_raw_descs__sz, __ffs_func_bind_do_descs, func); if (unlikely(ret < 0)) goto error; @@ -2252,10 +2288,10 @@ static int ffs_func_bind(struct usb_configuration *c, } if (likely(high)) { - func->function.hs_descriptors = data->hs_descs; + func->function.hs_descriptors = vla_ptr(vlabuf, d, hs_descs); ret = ffs_do_descs(ffs->hs_descs_count, - data->raw_descs + ret, - (sizeof data->raw_descs) - ret, + vla_ptr(vlabuf, d, raw_descs) + ret, + d_raw_descs__sz - ret, __ffs_func_bind_do_descs, func); if (unlikely(ret < 0)) goto error; @@ -2268,7 +2304,7 @@ static int ffs_func_bind(struct usb_configuration *c, */ ret = ffs_do_descs(ffs->fs_descs_count + (high ? ffs->hs_descs_count : 0), - data->raw_descs, sizeof data->raw_descs, + vla_ptr(vlabuf, d, raw_descs), d_raw_descs__sz, __ffs_func_bind_do_nums, func); if (unlikely(ret < 0)) goto error; From e72c39c0692d17da4c7f08e20d6c57f7409415f7 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Dec 2013 15:15:31 +0100 Subject: [PATCH 054/105] usb: gadget: FunctionFS: create utility file A header file to be used by f_fs.c and g_ffs.c will be required when f_fs.c is converted into a module. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_fs.c | 1 + drivers/usb/gadget/g_ffs.c | 19 ++++++------------- drivers/usb/gadget/u_fs.h | 28 ++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 13 deletions(-) create mode 100644 drivers/usb/gadget/u_fs.h diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index 91b94b1dcfe3..c0b4cf808638 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -27,6 +27,7 @@ #include #include +#include "u_fs.h" #define FUNCTIONFS_MAGIC 0xa647361 /* Chosen by a honest dice roll ;) */ diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index 7099a112c734..1aaa103e01c6 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c @@ -69,13 +69,6 @@ MODULE_LICENSE("GPL"); #define GFS_MAX_DEVS 10 -struct gfs_ffs_obj { - const char *name; - bool mounted; - bool desc_ready; - struct ffs_data *ffs_data; -}; - USB_GADGET_COMPOSITE_OPTIONS(); static struct usb_device_descriptor gfs_dev_desc = { @@ -181,7 +174,7 @@ static DEFINE_MUTEX(gfs_lock); static unsigned int missing_funcs; static bool gfs_registered; static bool gfs_single_func; -static struct gfs_ffs_obj *ffs_tab; +static struct ffs_dev *ffs_tab; static int __init gfs_init(void) { @@ -224,7 +217,7 @@ static void __exit gfs_exit(void) } module_exit(gfs_exit); -static struct gfs_ffs_obj *gfs_find_dev(const char *dev_name) +static struct ffs_dev *gfs_find_dev(const char *dev_name) { int i; @@ -242,7 +235,7 @@ static struct gfs_ffs_obj *gfs_find_dev(const char *dev_name) static int functionfs_ready_callback(struct ffs_data *ffs) { - struct gfs_ffs_obj *ffs_obj; + struct ffs_dev *ffs_obj; int ret; ENTER(); @@ -283,7 +276,7 @@ done: static void functionfs_closed_callback(struct ffs_data *ffs) { - struct gfs_ffs_obj *ffs_obj; + struct ffs_dev *ffs_obj; ENTER(); mutex_lock(&gfs_lock); @@ -305,7 +298,7 @@ done: static void *functionfs_acquire_dev_callback(const char *dev_name) { - struct gfs_ffs_obj *ffs_dev; + struct ffs_dev *ffs_dev; ENTER(); mutex_lock(&gfs_lock); @@ -329,7 +322,7 @@ done: static void functionfs_release_dev_callback(struct ffs_data *ffs_data) { - struct gfs_ffs_obj *ffs_dev; + struct ffs_dev *ffs_dev; ENTER(); mutex_lock(&gfs_lock); diff --git a/drivers/usb/gadget/u_fs.h b/drivers/usb/gadget/u_fs.h new file mode 100644 index 000000000000..5d9229a01560 --- /dev/null +++ b/drivers/usb/gadget/u_fs.h @@ -0,0 +1,28 @@ +/* + * u_fs.h + * + * Utility definitions for the FunctionFS + * + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef U_FFS_H +#define U_FFS_H + +#include + +struct ffs_dev { + const char *name; + bool mounted; + bool desc_ready; + struct ffs_data *ffs_data; +}; + +#endif /* U_FFS_H */ From 4b187fceec3c731815ff8e6a7317fd5ba50d1d5d Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Dec 2013 15:15:32 +0100 Subject: [PATCH 055/105] usb: gadget: FunctionFS: add devices management code This will be required in order to use the new function interface (usb_get_function_instance/usb_put_function_instance) Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyunmgin Park Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_fs.c | 238 ++++++++++++++++++++++++++++++++- drivers/usb/gadget/g_ffs.c | 175 ++++++++---------------- drivers/usb/gadget/u_fs.h | 24 ++++ include/linux/usb/functionfs.h | 14 -- 4 files changed, 314 insertions(+), 137 deletions(-) diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index c0b4cf808638..d17930d9a843 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -90,7 +90,7 @@ enum ffs_state { /* * We've got descriptors and strings. We are or have called - * functionfs_ready_callback(). functionfs_bind() may have + * ffs_ready(). functionfs_bind() may have * been called but we don't know. * * This is the only state in which operations on epfiles may @@ -103,7 +103,7 @@ enum ffs_state { * we encounter an unrecoverable error. The only * unrecoverable error is situation when after reading strings * from user space we fail to initialise epfiles or - * functionfs_ready_callback() returns with error (<0). + * ffs_ready() returns with error (<0). * * In this state no open(2), read(2) or write(2) (both on ep0 * as well as epfile) may succeed (at this point epfiles are @@ -361,6 +361,15 @@ ffs_sb_create_file(struct super_block *sb, const char *name, void *data, const struct file_operations *fops, struct dentry **dentry_p); +/* Devices management *******************************************************/ + +DEFINE_MUTEX(ffs_lock); + +static struct ffs_dev *ffs_find_dev(const char *name); +static void *ffs_acquire_dev(const char *dev_name); +static void ffs_release_dev(struct ffs_data *ffs_data); +static int ffs_ready(struct ffs_data *ffs); +static void ffs_closed(struct ffs_data *ffs); /* Misc helper functions ****************************************************/ @@ -486,7 +495,7 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf, ffs->state = FFS_ACTIVE; mutex_unlock(&ffs->mutex); - ret = functionfs_ready_callback(ffs); + ret = ffs_ready(ffs); if (unlikely(ret < 0)) { ffs->state = FFS_CLOSING; return ret; @@ -1218,7 +1227,7 @@ ffs_fs_mount(struct file_system_type *t, int flags, return ERR_PTR(-ENOMEM); } - ffs_dev = functionfs_acquire_dev_callback(dev_name); + ffs_dev = ffs_acquire_dev(dev_name); if (IS_ERR(ffs_dev)) { ffs_data_put(ffs); return ERR_CAST(ffs_dev); @@ -1228,7 +1237,7 @@ ffs_fs_mount(struct file_system_type *t, int flags, rv = mount_nodev(t, flags, &data, ffs_sb_fill); if (IS_ERR(rv) && data.ffs_data) { - functionfs_release_dev_callback(data.ffs_data); + ffs_release_dev(data.ffs_data); ffs_data_put(data.ffs_data); } return rv; @@ -1241,7 +1250,7 @@ ffs_fs_kill_sb(struct super_block *sb) kill_litter_super(sb); if (sb->s_fs_info) { - functionfs_release_dev_callback(sb->s_fs_info); + ffs_release_dev(sb->s_fs_info); ffs_data_put(sb->s_fs_info); } } @@ -1354,7 +1363,7 @@ static void ffs_data_clear(struct ffs_data *ffs) ENTER(); if (test_and_clear_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags)) - functionfs_closed_callback(ffs); + ffs_closed(ffs); BUG_ON(ffs->gadget); @@ -2466,6 +2475,221 @@ static int ffs_func_revmap_intf(struct ffs_function *func, u8 intf) } +/* Devices management *******************************************************/ + +static LIST_HEAD(ffs_devices); + +static struct ffs_dev *_ffs_find_dev(const char *name) +{ + struct ffs_dev *dev; + + list_for_each_entry(dev, &ffs_devices, entry) { + if (!dev->name || !name) + continue; + if (strcmp(dev->name, name) == 0) + return dev; + } + + return NULL; +} + +/* + * ffs_lock must be taken by the caller of this function + */ +static struct ffs_dev *ffs_get_single_dev(void) +{ + struct ffs_dev *dev; + + if (list_is_singular(&ffs_devices)) { + dev = list_first_entry(&ffs_devices, struct ffs_dev, entry); + if (dev->single) + return dev; + } + + return NULL; +} + +/* + * ffs_lock must be taken by the caller of this function + */ +static struct ffs_dev *ffs_find_dev(const char *name) +{ + struct ffs_dev *dev; + + dev = ffs_get_single_dev(); + if (dev) + return dev; + + return _ffs_find_dev(name); +} + +/* + * ffs_lock must be taken by the caller of this function + */ +struct ffs_dev *ffs_alloc_dev(void) +{ + struct ffs_dev *dev; + int ret; + + if (ffs_get_single_dev()) + return ERR_PTR(-EBUSY); + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return ERR_PTR(-ENOMEM); + + if (list_empty(&ffs_devices)) { + ret = functionfs_init(); + if (ret) { + kfree(dev); + return ERR_PTR(ret); + } + } + + list_add(&dev->entry, &ffs_devices); + + return dev; +} + +/* + * ffs_lock must be taken by the caller of this function + * The caller is responsible for "name" being available whenever f_fs needs it + */ +static int _ffs_name_dev(struct ffs_dev *dev, const char *name) +{ + struct ffs_dev *existing; + + existing = _ffs_find_dev(name); + if (existing) + return -EBUSY; + + dev->name = name; + + return 0; +} + +/* + * The caller is responsible for "name" being available whenever f_fs needs it + */ +int ffs_name_dev(struct ffs_dev *dev, const char *name) +{ + int ret; + + ffs_dev_lock(); + ret = _ffs_name_dev(dev, name); + ffs_dev_unlock(); + + return ret; +} + +int ffs_single_dev(struct ffs_dev *dev) +{ + int ret; + + ret = 0; + ffs_dev_lock(); + + if (!list_is_singular(&ffs_devices)) + ret = -EBUSY; + else + dev->single = true; + + ffs_dev_unlock(); + return ret; +} + +/* + * ffs_lock must be taken by the caller of this function + */ +void ffs_free_dev(struct ffs_dev *dev) +{ + list_del(&dev->entry); + kfree(dev); + if (list_empty(&ffs_devices)) + functionfs_cleanup(); +} + +static void *ffs_acquire_dev(const char *dev_name) +{ + struct ffs_dev *ffs_dev; + + ENTER(); + ffs_dev_lock(); + + ffs_dev = ffs_find_dev(dev_name); + if (!ffs_dev) + ffs_dev = ERR_PTR(-ENODEV); + else if (ffs_dev->mounted) + ffs_dev = ERR_PTR(-EBUSY); + else + ffs_dev->mounted = true; + + ffs_dev_unlock(); + return ffs_dev; +} + +static void ffs_release_dev(struct ffs_data *ffs_data) +{ + struct ffs_dev *ffs_dev; + + ENTER(); + ffs_dev_lock(); + + ffs_dev = ffs_data->private_data; + if (ffs_dev) + ffs_dev->mounted = false; + + ffs_dev_unlock(); +} + +static int ffs_ready(struct ffs_data *ffs) +{ + struct ffs_dev *ffs_obj; + int ret = 0; + + ENTER(); + ffs_dev_lock(); + + ffs_obj = ffs->private_data; + if (!ffs_obj) { + ret = -EINVAL; + goto done; + } + if (WARN_ON(ffs_obj->desc_ready)) { + ret = -EBUSY; + goto done; + } + + ffs_obj->desc_ready = true; + ffs_obj->ffs_data = ffs; + + if (ffs_obj->ffs_ready_callback) + ret = ffs_obj->ffs_ready_callback(ffs); + +done: + ffs_dev_unlock(); + return ret; +} + +static void ffs_closed(struct ffs_data *ffs) +{ + struct ffs_dev *ffs_obj; + + ENTER(); + ffs_dev_lock(); + + ffs_obj = ffs->private_data; + if (!ffs_obj) + goto done; + + ffs_obj->desc_ready = false; + + if (ffs_obj->ffs_closed_callback) + ffs_obj->ffs_closed_callback(ffs); +done: + ffs_dev_unlock(); +} + /* Misc helper functions ****************************************************/ static int ffs_mutex_lock(struct mutex *mutex, unsigned nonblock) diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index 1aaa103e01c6..074df0d56255 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c @@ -157,6 +157,8 @@ struct gfs_configuration { #endif }; +static int functionfs_ready_callback(struct ffs_data *ffs); +static void functionfs_closed_callback(struct ffs_data *ffs); static int gfs_bind(struct usb_composite_dev *cdev); static int gfs_unbind(struct usb_composite_dev *cdev); static int gfs_do_config(struct usb_configuration *c); @@ -170,172 +172,113 @@ static __refdata struct usb_composite_driver gfs_driver = { .unbind = gfs_unbind, }; -static DEFINE_MUTEX(gfs_lock); static unsigned int missing_funcs; static bool gfs_registered; static bool gfs_single_func; -static struct ffs_dev *ffs_tab; +static struct ffs_dev **ffs_tab; static int __init gfs_init(void) { int i; + int ret = 0; ENTER(); - if (!func_num) { + if (func_num < 2) { gfs_single_func = true; func_num = 1; } - ffs_tab = kcalloc(func_num, sizeof *ffs_tab, GFP_KERNEL); + ffs_tab = kcalloc(func_num, sizeof(*ffs_tab), GFP_KERNEL); if (!ffs_tab) return -ENOMEM; - if (!gfs_single_func) - for (i = 0; i < func_num; i++) - ffs_tab[i].name = func_names[i]; + for (i = 0; i < func_num; i++) { + ffs_dev_lock(); + ffs_tab[i] = ffs_alloc_dev(); + ffs_dev_unlock(); + if (IS_ERR(ffs_tab[i])) { + ret = PTR_ERR(ffs_tab[i]); + --i; + goto no_dev; + } + if (gfs_single_func) + ret = ffs_single_dev(ffs_tab[i]); + else + ret = ffs_name_dev(ffs_tab[i], func_names[i]); + if (ret) + goto no_dev; + ffs_tab[i]->ffs_ready_callback = functionfs_ready_callback; + ffs_tab[i]->ffs_closed_callback = functionfs_closed_callback; + } missing_funcs = func_num; - return functionfs_init(); + return 0; +no_dev: + ffs_dev_lock(); + while (i >= 0) + ffs_free_dev(ffs_tab[i--]); + ffs_dev_unlock(); + kfree(ffs_tab); + return ret; } module_init(gfs_init); static void __exit gfs_exit(void) { + int i; + ENTER(); - mutex_lock(&gfs_lock); + ffs_dev_lock(); if (gfs_registered) usb_composite_unregister(&gfs_driver); gfs_registered = false; - functionfs_cleanup(); - - mutex_unlock(&gfs_lock); + for (i = 0; i < func_num; i++) + ffs_free_dev(ffs_tab[i]); + ffs_dev_unlock(); kfree(ffs_tab); } module_exit(gfs_exit); -static struct ffs_dev *gfs_find_dev(const char *dev_name) -{ - int i; - - ENTER(); - - if (gfs_single_func) - return &ffs_tab[0]; - - for (i = 0; i < func_num; i++) - if (strcmp(ffs_tab[i].name, dev_name) == 0) - return &ffs_tab[i]; - - return NULL; -} - +/* + * The caller of this function takes ffs_lock + */ static int functionfs_ready_callback(struct ffs_data *ffs) { - struct ffs_dev *ffs_obj; - int ret; + int ret = 0; - ENTER(); - mutex_lock(&gfs_lock); + if (--missing_funcs) + return 0; - ffs_obj = ffs->private_data; - if (!ffs_obj) { - ret = -EINVAL; - goto done; - } + if (gfs_registered) + return -EBUSY; - if (WARN_ON(ffs_obj->desc_ready)) { - ret = -EBUSY; - goto done; - } - ffs_obj->desc_ready = true; - ffs_obj->ffs_data = ffs; - - if (--missing_funcs) { - ret = 0; - goto done; - } - - if (gfs_registered) { - ret = -EBUSY; - goto done; - } gfs_registered = true; ret = usb_composite_probe(&gfs_driver); if (unlikely(ret < 0)) gfs_registered = false; - -done: - mutex_unlock(&gfs_lock); + return ret; } +/* + * The caller of this function takes ffs_lock + */ static void functionfs_closed_callback(struct ffs_data *ffs) { - struct ffs_dev *ffs_obj; - - ENTER(); - mutex_lock(&gfs_lock); - - ffs_obj = ffs->private_data; - if (!ffs_obj) - goto done; - - ffs_obj->desc_ready = false; missing_funcs++; if (gfs_registered) usb_composite_unregister(&gfs_driver); gfs_registered = false; - -done: - mutex_unlock(&gfs_lock); -} - -static void *functionfs_acquire_dev_callback(const char *dev_name) -{ - struct ffs_dev *ffs_dev; - - ENTER(); - mutex_lock(&gfs_lock); - - ffs_dev = gfs_find_dev(dev_name); - if (!ffs_dev) { - ffs_dev = ERR_PTR(-ENODEV); - goto done; - } - - if (ffs_dev->mounted) { - ffs_dev = ERR_PTR(-EBUSY); - goto done; - } - ffs_dev->mounted = true; - -done: - mutex_unlock(&gfs_lock); - return ffs_dev; -} - -static void functionfs_release_dev_callback(struct ffs_data *ffs_data) -{ - struct ffs_dev *ffs_dev; - - ENTER(); - mutex_lock(&gfs_lock); - - ffs_dev = ffs_data->private_data; - if (ffs_dev) - ffs_dev->mounted = false; - - mutex_unlock(&gfs_lock); } /* - * It is assumed that gfs_bind is called from a context where gfs_lock is held + * It is assumed that gfs_bind is called from a context where ffs_lock is held */ static int gfs_bind(struct usb_composite_dev *cdev) { @@ -422,10 +365,10 @@ static int gfs_bind(struct usb_composite_dev *cdev) gfs_dev_desc.iProduct = gfs_strings[USB_GADGET_PRODUCT_IDX].id; for (i = func_num; i--; ) { - ret = functionfs_bind(ffs_tab[i].ffs_data, cdev); + ret = functionfs_bind(ffs_tab[i]->ffs_data, cdev); if (unlikely(ret < 0)) { while (++i < func_num) - functionfs_unbind(ffs_tab[i].ffs_data); + functionfs_unbind(ffs_tab[i]->ffs_data); goto error_rndis; } } @@ -448,7 +391,7 @@ static int gfs_bind(struct usb_composite_dev *cdev) error_unbind: for (i = 0; i < func_num; i++) - functionfs_unbind(ffs_tab[i].ffs_data); + functionfs_unbind(ffs_tab[i]->ffs_data); error_rndis: #ifdef CONFIG_USB_FUNCTIONFS_RNDIS usb_put_function_instance(fi_rndis); @@ -464,7 +407,7 @@ error: } /* - * It is assumed that gfs_unbind is called from a context where gfs_lock is held + * It is assumed that gfs_unbind is called from a context where ffs_lock is held */ static int gfs_unbind(struct usb_composite_dev *cdev) { @@ -497,15 +440,15 @@ static int gfs_unbind(struct usb_composite_dev *cdev) * do...? */ for (i = func_num; i--; ) - if (ffs_tab[i].ffs_data) - functionfs_unbind(ffs_tab[i].ffs_data); + if (ffs_tab[i]->ffs_data) + functionfs_unbind(ffs_tab[i]->ffs_data); return 0; } /* * It is assumed that gfs_do_config is called from a context where - * gfs_lock is held + * ffs_lock is held */ static int gfs_do_config(struct usb_configuration *c) { @@ -529,7 +472,7 @@ static int gfs_do_config(struct usb_configuration *c) } for (i = 0; i < func_num; i++) { - ret = functionfs_bind_config(c->cdev, c, ffs_tab[i].ffs_data); + ret = functionfs_bind_config(c->cdev, c, ffs_tab[i]->ffs_data); if (unlikely(ret < 0)) return ret; } diff --git a/drivers/usb/gadget/u_fs.h b/drivers/usb/gadget/u_fs.h index 5d9229a01560..2d00f9d296a3 100644 --- a/drivers/usb/gadget/u_fs.h +++ b/drivers/usb/gadget/u_fs.h @@ -17,12 +17,36 @@ #define U_FFS_H #include +#include +#include struct ffs_dev { const char *name; bool mounted; bool desc_ready; + bool single; struct ffs_data *ffs_data; + struct list_head entry; + + int (*ffs_ready_callback)(struct ffs_data *ffs); + void (*ffs_closed_callback)(struct ffs_data *ffs); }; +extern struct mutex ffs_lock; + +static inline void ffs_dev_lock(void) +{ + mutex_lock(&ffs_lock); +} + +static inline void ffs_dev_unlock(void) +{ + mutex_unlock(&ffs_lock); +} + +struct ffs_dev *ffs_alloc_dev(void); +int ffs_name_dev(struct ffs_dev *dev, const char *name); +int ffs_single_dev(struct ffs_dev *dev); +void ffs_free_dev(struct ffs_dev *dev); + #endif /* U_FFS_H */ diff --git a/include/linux/usb/functionfs.h b/include/linux/usb/functionfs.h index 65d0a88dbc67..9c1e92620dfb 100644 --- a/include/linux/usb/functionfs.h +++ b/include/linux/usb/functionfs.h @@ -8,10 +8,6 @@ struct ffs_data; struct usb_composite_dev; struct usb_configuration; - -static int functionfs_init(void) __attribute__((warn_unused_result)); -static void functionfs_cleanup(void); - static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev) __attribute__((warn_unused_result, nonnull)); static void functionfs_unbind(struct ffs_data *ffs) @@ -23,14 +19,4 @@ static int functionfs_bind_config(struct usb_composite_dev *cdev, __attribute__((warn_unused_result, nonnull)); -static int functionfs_ready_callback(struct ffs_data *ffs) - __attribute__((warn_unused_result, nonnull)); -static void functionfs_closed_callback(struct ffs_data *ffs) - __attribute__((nonnull)); -static void *functionfs_acquire_dev_callback(const char *dev_name) - __attribute__((warn_unused_result, nonnull)); -static void functionfs_release_dev_callback(struct ffs_data *ffs_data) - __attribute__((nonnull)); - - #endif From 5920cda627688c3229fd63157ff031f3f174175e Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Dec 2013 15:15:33 +0100 Subject: [PATCH 056/105] usb: gadget: FunctionFS: convert to new function interface with backward compatibility This is required in order to integrate configfs support. f_fs needs to be a separately compiled module and so it needs to use the new interface. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 3 + drivers/usb/gadget/Makefile | 2 + drivers/usb/gadget/f_fs.c | 422 +++++++++++++++++---------------- drivers/usb/gadget/g_ffs.c | 1 + drivers/usb/gadget/u_fs.h | 214 +++++++++++++++++ include/linux/usb/functionfs.h | 2 + 6 files changed, 435 insertions(+), 209 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index aa3610ea9d9f..1761a3b8b31a 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -539,6 +539,9 @@ config USB_F_RNDIS config USB_F_MASS_STORAGE tristate +config USB_F_FS + tristate + choice tristate "USB Gadget Drivers" default USB_ETH diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 0c40277931f4..6cccdfed140c 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -61,6 +61,8 @@ usb_f_rndis-y := f_rndis.o rndis.o obj-$(CONFIG_USB_F_RNDIS) += usb_f_rndis.o usb_f_mass_storage-y := f_mass_storage.o storage_common.o obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o +usb_f_fs-y := f_fs.o +obj-$(CONFIG_USB_F_FS) += usb_f_fs.o # # USB gadget drivers diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index d17930d9a843..8d318eaaaf20 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -57,210 +58,6 @@ #define vla_ptr(ptr, groupname, name) \ ((void *) ((char *)ptr + groupname##_##name##__offset)) -/* Debugging ****************************************************************/ - -#ifdef VERBOSE_DEBUG -#ifndef pr_vdebug -# define pr_vdebug pr_debug -#endif /* pr_vdebug */ -# define ffs_dump_mem(prefix, ptr, len) \ - print_hex_dump_bytes(pr_fmt(prefix ": "), DUMP_PREFIX_NONE, ptr, len) -#else -#ifndef pr_vdebug -# define pr_vdebug(...) do { } while (0) -#endif /* pr_vdebug */ -# define ffs_dump_mem(prefix, ptr, len) do { } while (0) -#endif /* VERBOSE_DEBUG */ - -#define ENTER() pr_vdebug("%s()\n", __func__) - - -/* The data structure and setup file ****************************************/ - -enum ffs_state { - /* - * Waiting for descriptors and strings. - * - * In this state no open(2), read(2) or write(2) on epfiles - * may succeed (which should not be the problem as there - * should be no such files opened in the first place). - */ - FFS_READ_DESCRIPTORS, - FFS_READ_STRINGS, - - /* - * We've got descriptors and strings. We are or have called - * ffs_ready(). functionfs_bind() may have - * been called but we don't know. - * - * This is the only state in which operations on epfiles may - * succeed. - */ - FFS_ACTIVE, - - /* - * All endpoints have been closed. This state is also set if - * we encounter an unrecoverable error. The only - * unrecoverable error is situation when after reading strings - * from user space we fail to initialise epfiles or - * ffs_ready() returns with error (<0). - * - * In this state no open(2), read(2) or write(2) (both on ep0 - * as well as epfile) may succeed (at this point epfiles are - * unlinked and all closed so this is not a problem; ep0 is - * also closed but ep0 file exists and so open(2) on ep0 must - * fail). - */ - FFS_CLOSING -}; - - -enum ffs_setup_state { - /* There is no setup request pending. */ - FFS_NO_SETUP, - /* - * User has read events and there was a setup request event - * there. The next read/write on ep0 will handle the - * request. - */ - FFS_SETUP_PENDING, - /* - * There was event pending but before user space handled it - * some other event was introduced which canceled existing - * setup. If this state is set read/write on ep0 return - * -EIDRM. This state is only set when adding event. - */ - FFS_SETUP_CANCELED -}; - - - -struct ffs_epfile; -struct ffs_function; - -struct ffs_data { - struct usb_gadget *gadget; - - /* - * Protect access read/write operations, only one read/write - * at a time. As a consequence protects ep0req and company. - * While setup request is being processed (queued) this is - * held. - */ - struct mutex mutex; - - /* - * Protect access to endpoint related structures (basically - * usb_ep_queue(), usb_ep_dequeue(), etc. calls) except for - * endpoint zero. - */ - spinlock_t eps_lock; - - /* - * XXX REVISIT do we need our own request? Since we are not - * handling setup requests immediately user space may be so - * slow that another setup will be sent to the gadget but this - * time not to us but another function and then there could be - * a race. Is that the case? Or maybe we can use cdev->req - * after all, maybe we just need some spinlock for that? - */ - struct usb_request *ep0req; /* P: mutex */ - struct completion ep0req_completion; /* P: mutex */ - int ep0req_status; /* P: mutex */ - - /* reference counter */ - atomic_t ref; - /* how many files are opened (EP0 and others) */ - atomic_t opened; - - /* EP0 state */ - enum ffs_state state; - - /* - * Possible transitions: - * + FFS_NO_SETUP -> FFS_SETUP_PENDING -- P: ev.waitq.lock - * happens only in ep0 read which is P: mutex - * + FFS_SETUP_PENDING -> FFS_NO_SETUP -- P: ev.waitq.lock - * happens only in ep0 i/o which is P: mutex - * + FFS_SETUP_PENDING -> FFS_SETUP_CANCELED -- P: ev.waitq.lock - * + FFS_SETUP_CANCELED -> FFS_NO_SETUP -- cmpxchg - */ - enum ffs_setup_state setup_state; - -#define FFS_SETUP_STATE(ffs) \ - ((enum ffs_setup_state)cmpxchg(&(ffs)->setup_state, \ - FFS_SETUP_CANCELED, FFS_NO_SETUP)) - - /* Events & such. */ - struct { - u8 types[4]; - unsigned short count; - /* XXX REVISIT need to update it in some places, or do we? */ - unsigned short can_stall; - struct usb_ctrlrequest setup; - - wait_queue_head_t waitq; - } ev; /* the whole structure, P: ev.waitq.lock */ - - /* Flags */ - unsigned long flags; -#define FFS_FL_CALL_CLOSED_CALLBACK 0 -#define FFS_FL_BOUND 1 - - /* Active function */ - struct ffs_function *func; - - /* - * Device name, write once when file system is mounted. - * Intended for user to read if she wants. - */ - const char *dev_name; - /* Private data for our user (ie. gadget). Managed by user. */ - void *private_data; - - /* filled by __ffs_data_got_descs() */ - /* - * Real descriptors are 16 bytes after raw_descs (so you need - * to skip 16 bytes (ie. ffs->raw_descs + 16) to get to the - * first full speed descriptor). raw_descs_length and - * raw_fs_descs_length do not have those 16 bytes added. - */ - const void *raw_descs; - unsigned raw_descs_length; - unsigned raw_fs_descs_length; - unsigned fs_descs_count; - unsigned hs_descs_count; - - unsigned short strings_count; - unsigned short interfaces_count; - unsigned short eps_count; - unsigned short _pad1; - - /* filled by __ffs_data_got_strings() */ - /* ids in stringtabs are set in functionfs_bind() */ - const void *raw_strings; - struct usb_gadget_strings **stringtabs; - - /* - * File system's super block, write once when file system is - * mounted. - */ - struct super_block *sb; - - /* File permissions, written once when fs is mounted */ - struct ffs_file_perms { - umode_t mode; - kuid_t uid; - kgid_t gid; - } file_perms; - - /* - * The endpoint files, filled by ffs_epfiles_create(), - * destroyed by ffs_epfiles_destroy(). - */ - struct ffs_epfile *epfiles; -}; - /* Reference counter handling */ static void ffs_data_get(struct ffs_data *ffs); static void ffs_data_put(struct ffs_data *ffs); @@ -300,15 +97,19 @@ static struct ffs_function *ffs_func_from_usb(struct usb_function *f) return container_of(f, struct ffs_function, function); } +#ifdef USB_FFS_INCLUDED static void ffs_func_free(struct ffs_function *func); +#endif static void ffs_func_eps_disable(struct ffs_function *func); static int __must_check ffs_func_eps_enable(struct ffs_function *func); static int ffs_func_bind(struct usb_configuration *, struct usb_function *); -static void ffs_func_unbind(struct usb_configuration *, +#ifdef USB_FFS_INCLUDED +static void old_ffs_func_unbind(struct usb_configuration *, struct usb_function *); +#endif static int ffs_func_set_alt(struct usb_function *, unsigned, unsigned); static void ffs_func_disable(struct usb_function *); static int ffs_func_setup(struct usb_function *, @@ -364,6 +165,9 @@ ffs_sb_create_file(struct super_block *sb, const char *name, void *data, /* Devices management *******************************************************/ DEFINE_MUTEX(ffs_lock); +#ifndef USB_FFS_INCLUDED +EXPORT_SYMBOL(ffs_lock); +#endif static struct ffs_dev *ffs_find_dev(const char *name); static void *ffs_acquire_dev(const char *dev_name); @@ -1499,6 +1303,8 @@ static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count) kfree(epfiles); } +#ifdef USB_FFS_INCLUDED + static int functionfs_bind_config(struct usb_composite_dev *cdev, struct usb_configuration *c, struct ffs_data *ffs) @@ -1516,7 +1322,7 @@ static int functionfs_bind_config(struct usb_composite_dev *cdev, func->function.strings = ffs->stringtabs; func->function.bind = ffs_func_bind; - func->function.unbind = ffs_func_unbind; + func->function.unbind = old_ffs_func_unbind; func->function.set_alt = ffs_func_set_alt; func->function.disable = ffs_func_disable; func->function.setup = ffs_func_setup; @@ -1565,6 +1371,8 @@ static void ffs_func_free(struct ffs_function *func) kfree(func); } +#endif + static void ffs_func_eps_disable(struct ffs_function *func) { struct ffs_ep *ep = func->eps; @@ -2227,8 +2035,59 @@ static int __ffs_func_bind_do_nums(enum ffs_entity_type type, u8 *valuep, return 0; } -static int ffs_func_bind(struct usb_configuration *c, - struct usb_function *f) +#ifndef USB_FFS_INCLUDED +static inline struct f_fs_opts *ffs_do_functionfs_bind(struct usb_function *f, + struct usb_configuration *c) +{ + struct ffs_function *func = ffs_func_from_usb(f); + struct f_fs_opts *ffs_opts = + container_of(f->fi, struct f_fs_opts, func_inst); + int ret; + + ENTER(); + + /* + * Legacy gadget triggers binding in functionfs_ready_callback, + * which already uses locking; taking the same lock here would + * cause a deadlock. + * + * Configfs-enabled gadgets however do need ffs_dev_lock. + */ + if (!ffs_opts->no_configfs) + ffs_dev_lock(); + ret = ffs_opts->dev->desc_ready ? 0 : -ENODEV; + func->ffs = ffs_opts->dev->ffs_data; + if (!ffs_opts->no_configfs) + ffs_dev_unlock(); + if (ret) + return ERR_PTR(ret); + + func->conf = c; + func->gadget = c->cdev->gadget; + + ffs_data_get(func->ffs); + + /* + * in drivers/usb/gadget/configfs.c:configfs_composite_bind() + * configurations are bound in sequence with list_for_each_entry, + * in each configuration its functions are bound in sequence + * with list_for_each_entry, so we assume no race condition + * with regard to ffs_opts->bound access + */ + if (!ffs_opts->refcnt) { + ret = functionfs_bind(func->ffs, c->cdev); + if (ret) + return ERR_PTR(ret); + } + ffs_opts->refcnt++; + func->function.strings = func->ffs->stringtabs; + + return ffs_opts; +} +#endif + +static int _ffs_func_bind(struct usb_configuration *c, + struct usb_function *f) { struct ffs_function *func = ffs_func_from_usb(f); struct ffs_data *ffs = func->ffs; @@ -2328,10 +2187,25 @@ error: return ret; } +static int ffs_func_bind(struct usb_configuration *c, + struct usb_function *f) +{ +#ifndef USB_FFS_INCLUDED + struct f_fs_opts *ffs_opts = ffs_do_functionfs_bind(f, c); + + if (IS_ERR(ffs_opts)) + return PTR_ERR(ffs_opts); +#endif + + return _ffs_func_bind(c, f); +} + /* Other USB function hooks *************************************************/ -static void ffs_func_unbind(struct usb_configuration *c, +#ifdef USB_FFS_INCLUDED + +static void old_ffs_func_unbind(struct usb_configuration *c, struct usb_function *f) { struct ffs_function *func = ffs_func_from_usb(f); @@ -2349,6 +2223,8 @@ static void ffs_func_unbind(struct usb_configuration *c, ffs_func_free(func); } +#endif + static int ffs_func_set_alt(struct usb_function *f, unsigned interface, unsigned alt) { @@ -2523,6 +2399,116 @@ static struct ffs_dev *ffs_find_dev(const char *name) return _ffs_find_dev(name); } +/* Function registration interface ******************************************/ + +#ifndef USB_FFS_INCLUDED + +static void ffs_free_inst(struct usb_function_instance *f) +{ + struct f_fs_opts *opts; + + opts = to_f_fs_opts(f); + ffs_dev_lock(); + ffs_free_dev(opts->dev); + ffs_dev_unlock(); + kfree(opts); +} + +static struct usb_function_instance *ffs_alloc_inst(void) +{ + struct f_fs_opts *opts; + struct ffs_dev *dev; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + + opts->func_inst.free_func_inst = ffs_free_inst; + ffs_dev_lock(); + dev = ffs_alloc_dev(); + ffs_dev_unlock(); + if (IS_ERR(dev)) { + kfree(opts); + return ERR_CAST(dev); + } + opts->dev = dev; + + return &opts->func_inst; +} + +static void ffs_free(struct usb_function *f) +{ + kfree(ffs_func_from_usb(f)); +} + +static void ffs_func_unbind(struct usb_configuration *c, + struct usb_function *f) +{ + struct ffs_function *func = ffs_func_from_usb(f); + struct ffs_data *ffs = func->ffs; + struct f_fs_opts *opts = + container_of(f->fi, struct f_fs_opts, func_inst); + struct ffs_ep *ep = func->eps; + unsigned count = ffs->eps_count; + unsigned long flags; + + ENTER(); + if (ffs->func == func) { + ffs_func_eps_disable(func); + ffs->func = NULL; + } + + if (!--opts->refcnt) + functionfs_unbind(ffs); + + /* cleanup after autoconfig */ + spin_lock_irqsave(&func->ffs->eps_lock, flags); + do { + if (ep->ep && ep->req) + usb_ep_free_request(ep->ep, ep->req); + ep->req = NULL; + ++ep; + } while (--count); + spin_unlock_irqrestore(&func->ffs->eps_lock, flags); + kfree(func->eps); + func->eps = NULL; + /* + * eps, descriptors and interfaces_nums are allocated in the + * same chunk so only one free is required. + */ + func->function.fs_descriptors = NULL; + func->function.hs_descriptors = NULL; + func->interfaces_nums = NULL; + + ffs_event_add(ffs, FUNCTIONFS_UNBIND); +} + +static struct usb_function *ffs_alloc(struct usb_function_instance *fi) +{ + struct ffs_function *func; + + ENTER(); + + func = kzalloc(sizeof(*func), GFP_KERNEL); + if (unlikely(!func)) + return ERR_PTR(-ENOMEM); + + func->function.name = "Function FS Gadget"; + + func->function.bind = ffs_func_bind; + func->function.unbind = ffs_func_unbind; + func->function.set_alt = ffs_func_set_alt; + func->function.disable = ffs_func_disable; + func->function.setup = ffs_func_setup; + func->function.suspend = ffs_func_suspend; + func->function.resume = ffs_func_resume; + func->function.free_func = ffs_free; + + return &func->function; +} + +#endif + /* * ffs_lock must be taken by the caller of this function */ @@ -2581,6 +2567,9 @@ int ffs_name_dev(struct ffs_dev *dev, const char *name) return ret; } +#ifndef USB_FFS_INCLUDED +EXPORT_SYMBOL(ffs_name_dev); +#endif int ffs_single_dev(struct ffs_dev *dev) { @@ -2597,6 +2586,9 @@ int ffs_single_dev(struct ffs_dev *dev) ffs_dev_unlock(); return ret; } +#ifndef USB_FFS_INCLUDED +EXPORT_SYMBOL(ffs_single_dev); +#endif /* * ffs_lock must be taken by the caller of this function @@ -2621,6 +2613,9 @@ static void *ffs_acquire_dev(const char *dev_name) ffs_dev = ERR_PTR(-ENODEV); else if (ffs_dev->mounted) ffs_dev = ERR_PTR(-EBUSY); + else if (ffs_dev->ffs_acquire_dev_callback && + ffs_dev->ffs_acquire_dev_callback(ffs_dev)) + ffs_dev = ERR_PTR(-ENODEV); else ffs_dev->mounted = true; @@ -2638,6 +2633,9 @@ static void ffs_release_dev(struct ffs_data *ffs_data) ffs_dev = ffs_data->private_data; if (ffs_dev) ffs_dev->mounted = false; + + if (ffs_dev->ffs_release_dev_callback) + ffs_dev->ffs_release_dev_callback(ffs_dev); ffs_dev_unlock(); } @@ -2720,3 +2718,9 @@ static char *ffs_prepare_buffer(const char __user *buf, size_t len) return data; } + +#ifndef USB_FFS_INCLUDED +DECLARE_USB_FUNCTION_INIT(ffs, ffs_alloc_inst, ffs_alloc); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michal Nazarewicz"); +#endif diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index 074df0d56255..ffde36f2c672 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c @@ -54,6 +54,7 @@ static struct usb_function *f_rndis; # endif #endif +#define USB_FFS_INCLUDED #include "f_fs.c" #define DRIVER_NAME "g_ffs" diff --git a/drivers/usb/gadget/u_fs.h b/drivers/usb/gadget/u_fs.h index 2d00f9d296a3..d60a9ddc0332 100644 --- a/drivers/usb/gadget/u_fs.h +++ b/drivers/usb/gadget/u_fs.h @@ -20,6 +20,22 @@ #include #include +#ifdef VERBOSE_DEBUG +#ifndef pr_vdebug +# define pr_vdebug pr_debug +#endif /* pr_vdebug */ +# define ffs_dump_mem(prefix, ptr, len) \ + print_hex_dump_bytes(pr_fmt(prefix ": "), DUMP_PREFIX_NONE, ptr, len) +#else +#ifndef pr_vdebug +# define pr_vdebug(...) do { } while (0) +#endif /* pr_vdebug */ +# define ffs_dump_mem(prefix, ptr, len) do { } while (0) +#endif /* VERBOSE_DEBUG */ + +#define ENTER() pr_vdebug("%s()\n", __func__) + + struct ffs_dev { const char *name; bool mounted; @@ -30,6 +46,8 @@ struct ffs_dev { int (*ffs_ready_callback)(struct ffs_data *ffs); void (*ffs_closed_callback)(struct ffs_data *ffs); + void *(*ffs_acquire_dev_callback)(struct ffs_dev *dev); + void (*ffs_release_dev_callback)(struct ffs_dev *dev); }; extern struct mutex ffs_lock; @@ -49,4 +67,200 @@ int ffs_name_dev(struct ffs_dev *dev, const char *name); int ffs_single_dev(struct ffs_dev *dev); void ffs_free_dev(struct ffs_dev *dev); +struct ffs_epfile; +struct ffs_function; + +enum ffs_state { + /* + * Waiting for descriptors and strings. + * + * In this state no open(2), read(2) or write(2) on epfiles + * may succeed (which should not be the problem as there + * should be no such files opened in the first place). + */ + FFS_READ_DESCRIPTORS, + FFS_READ_STRINGS, + + /* + * We've got descriptors and strings. We are or have called + * functionfs_ready_callback(). functionfs_bind() may have + * been called but we don't know. + * + * This is the only state in which operations on epfiles may + * succeed. + */ + FFS_ACTIVE, + + /* + * All endpoints have been closed. This state is also set if + * we encounter an unrecoverable error. The only + * unrecoverable error is situation when after reading strings + * from user space we fail to initialise epfiles or + * functionfs_ready_callback() returns with error (<0). + * + * In this state no open(2), read(2) or write(2) (both on ep0 + * as well as epfile) may succeed (at this point epfiles are + * unlinked and all closed so this is not a problem; ep0 is + * also closed but ep0 file exists and so open(2) on ep0 must + * fail). + */ + FFS_CLOSING +}; + +enum ffs_setup_state { + /* There is no setup request pending. */ + FFS_NO_SETUP, + /* + * User has read events and there was a setup request event + * there. The next read/write on ep0 will handle the + * request. + */ + FFS_SETUP_PENDING, + /* + * There was event pending but before user space handled it + * some other event was introduced which canceled existing + * setup. If this state is set read/write on ep0 return + * -EIDRM. This state is only set when adding event. + */ + FFS_SETUP_CANCELED +}; + +struct ffs_data { + struct usb_gadget *gadget; + + /* + * Protect access read/write operations, only one read/write + * at a time. As a consequence protects ep0req and company. + * While setup request is being processed (queued) this is + * held. + */ + struct mutex mutex; + + /* + * Protect access to endpoint related structures (basically + * usb_ep_queue(), usb_ep_dequeue(), etc. calls) except for + * endpoint zero. + */ + spinlock_t eps_lock; + + /* + * XXX REVISIT do we need our own request? Since we are not + * handling setup requests immediately user space may be so + * slow that another setup will be sent to the gadget but this + * time not to us but another function and then there could be + * a race. Is that the case? Or maybe we can use cdev->req + * after all, maybe we just need some spinlock for that? + */ + struct usb_request *ep0req; /* P: mutex */ + struct completion ep0req_completion; /* P: mutex */ + int ep0req_status; /* P: mutex */ + + /* reference counter */ + atomic_t ref; + /* how many files are opened (EP0 and others) */ + atomic_t opened; + + /* EP0 state */ + enum ffs_state state; + + /* + * Possible transitions: + * + FFS_NO_SETUP -> FFS_SETUP_PENDING -- P: ev.waitq.lock + * happens only in ep0 read which is P: mutex + * + FFS_SETUP_PENDING -> FFS_NO_SETUP -- P: ev.waitq.lock + * happens only in ep0 i/o which is P: mutex + * + FFS_SETUP_PENDING -> FFS_SETUP_CANCELED -- P: ev.waitq.lock + * + FFS_SETUP_CANCELED -> FFS_NO_SETUP -- cmpxchg + */ + enum ffs_setup_state setup_state; + +#define FFS_SETUP_STATE(ffs) \ + ((enum ffs_setup_state)cmpxchg(&(ffs)->setup_state, \ + FFS_SETUP_CANCELED, FFS_NO_SETUP)) + + /* Events & such. */ + struct { + u8 types[4]; + unsigned short count; + /* XXX REVISIT need to update it in some places, or do we? */ + unsigned short can_stall; + struct usb_ctrlrequest setup; + + wait_queue_head_t waitq; + } ev; /* the whole structure, P: ev.waitq.lock */ + + /* Flags */ + unsigned long flags; +#define FFS_FL_CALL_CLOSED_CALLBACK 0 +#define FFS_FL_BOUND 1 + + /* Active function */ + struct ffs_function *func; + + /* + * Device name, write once when file system is mounted. + * Intended for user to read if she wants. + */ + const char *dev_name; + /* Private data for our user (ie. gadget). Managed by user. */ + void *private_data; + + /* filled by __ffs_data_got_descs() */ + /* + * Real descriptors are 16 bytes after raw_descs (so you need + * to skip 16 bytes (ie. ffs->raw_descs + 16) to get to the + * first full speed descriptor). raw_descs_length and + * raw_fs_descs_length do not have those 16 bytes added. + */ + const void *raw_descs; + unsigned raw_descs_length; + unsigned raw_fs_descs_length; + unsigned fs_descs_count; + unsigned hs_descs_count; + + unsigned short strings_count; + unsigned short interfaces_count; + unsigned short eps_count; + unsigned short _pad1; + + /* filled by __ffs_data_got_strings() */ + /* ids in stringtabs are set in functionfs_bind() */ + const void *raw_strings; + struct usb_gadget_strings **stringtabs; + + /* + * File system's super block, write once when file system is + * mounted. + */ + struct super_block *sb; + + /* File permissions, written once when fs is mounted */ + struct ffs_file_perms { + umode_t mode; + kuid_t uid; + kgid_t gid; + } file_perms; + + /* + * The endpoint files, filled by ffs_epfiles_create(), + * destroyed by ffs_epfiles_destroy(). + */ + struct ffs_epfile *epfiles; +}; + + +#ifndef USB_FFS_INCLUDED +struct f_fs_opts { + struct usb_function_instance func_inst; + struct ffs_dev *dev; + unsigned refcnt; + bool no_configfs; +}; + +static inline struct f_fs_opts *to_f_fs_opts(struct usb_function_instance *fi) +{ + return container_of(fi, struct f_fs_opts, func_inst); +} +#endif + #endif /* U_FFS_H */ diff --git a/include/linux/usb/functionfs.h b/include/linux/usb/functionfs.h index 9c1e92620dfb..3448efbe56aa 100644 --- a/include/linux/usb/functionfs.h +++ b/include/linux/usb/functionfs.h @@ -3,6 +3,7 @@ #include +#ifdef USB_FFS_INCLUDED struct ffs_data; struct usb_composite_dev; @@ -20,3 +21,4 @@ static int functionfs_bind_config(struct usb_composite_dev *cdev, #endif +#endif From 6f823cd5305c78ad1282fab8634b369eac4620b1 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Dec 2013 15:15:34 +0100 Subject: [PATCH 057/105] usb: gadget: g_ffs: convert to new interface of f_fs Prepare for configfs integration. Use the new interface so that f_fs can be made a module. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 1 + drivers/usb/gadget/g_ffs.c | 151 +++++++++++++++++++++++-------------- 2 files changed, 96 insertions(+), 56 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 1761a3b8b31a..97eb540ddef2 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -874,6 +874,7 @@ config USB_GADGETFS config USB_FUNCTIONFS tristate "Function Filesystem" select USB_LIBCOMPOSITE + select USB_F_FS select USB_FUNCTIONFS_GENERIC if !(USB_FUNCTIONFS_ETH || USB_FUNCTIONFS_RNDIS) help The Function Filesystem (FunctionFS) lets one create USB diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index ffde36f2c672..fe12e6a27448 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c @@ -13,13 +13,7 @@ #define pr_fmt(fmt) "g_ffs: " fmt #include -/* - * kbuild is not very cooperative with respect to linking separately - * compiled library objects into one module. So for now we won't use - * separate compilation ... ensuring init/exit sections work to shrink - * the runtime footprint, and giving us at least some parts of what - * a "gcc --combine ... part1.c part2.c part3.c ... " build would. - */ + #if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS #include @@ -54,8 +48,7 @@ static struct usb_function *f_rndis; # endif #endif -#define USB_FFS_INCLUDED -#include "f_fs.c" +#include "u_fs.h" #define DRIVER_NAME "g_ffs" #define DRIVER_DESC "USB Function Filesystem" @@ -139,6 +132,7 @@ static struct usb_gadget_strings *gfs_dev_strings[] = { struct gfs_configuration { struct usb_configuration c; int (*eth)(struct usb_configuration *c); + int num; } gfs_configurations[] = { #ifdef CONFIG_USB_FUNCTIONFS_RNDIS { @@ -158,12 +152,15 @@ struct gfs_configuration { #endif }; +static void *functionfs_acquire_dev(struct ffs_dev *dev); +static void functionfs_release_dev(struct ffs_dev *dev); static int functionfs_ready_callback(struct ffs_data *ffs); static void functionfs_closed_callback(struct ffs_data *ffs); static int gfs_bind(struct usb_composite_dev *cdev); static int gfs_unbind(struct usb_composite_dev *cdev); static int gfs_do_config(struct usb_configuration *c); + static __refdata struct usb_composite_driver gfs_driver = { .name = DRIVER_NAME, .dev = &gfs_dev_desc, @@ -176,10 +173,26 @@ static __refdata struct usb_composite_driver gfs_driver = { static unsigned int missing_funcs; static bool gfs_registered; static bool gfs_single_func; -static struct ffs_dev **ffs_tab; +static struct usb_function_instance **fi_ffs; +static struct usb_function **f_ffs[] = { +#ifdef CONFIG_USB_FUNCTIONFS_RNDIS + NULL, +#endif + +#ifdef CONFIG_USB_FUNCTIONFS_ETH + NULL, +#endif + +#ifdef CONFIG_USB_FUNCTIONFS_GENERIC + NULL, +#endif +}; + +#define N_CONF ARRAY_SIZE(f_ffs) static int __init gfs_init(void) { + struct f_fs_opts *opts; int i; int ret = 0; @@ -190,38 +203,53 @@ static int __init gfs_init(void) func_num = 1; } - ffs_tab = kcalloc(func_num, sizeof(*ffs_tab), GFP_KERNEL); - if (!ffs_tab) - return -ENOMEM; + /* + * Allocate in one chunk for easier maintenance + */ + f_ffs[0] = kcalloc(func_num * N_CONF, sizeof(*f_ffs), GFP_KERNEL); + if (!f_ffs[0]) { + ret = -ENOMEM; + goto no_func; + } + for (i = 1; i < N_CONF; ++i) + f_ffs[i] = f_ffs[0] + i * func_num; + + fi_ffs = kcalloc(func_num, sizeof(*fi_ffs), GFP_KERNEL); + if (!fi_ffs) { + ret = -ENOMEM; + goto no_func; + } for (i = 0; i < func_num; i++) { - ffs_dev_lock(); - ffs_tab[i] = ffs_alloc_dev(); - ffs_dev_unlock(); - if (IS_ERR(ffs_tab[i])) { - ret = PTR_ERR(ffs_tab[i]); + fi_ffs[i] = usb_get_function_instance("ffs"); + if (IS_ERR(fi_ffs[i])) { + ret = PTR_ERR(fi_ffs[i]); --i; goto no_dev; } + opts = to_f_fs_opts(fi_ffs[i]); if (gfs_single_func) - ret = ffs_single_dev(ffs_tab[i]); + ret = ffs_single_dev(opts->dev); else - ret = ffs_name_dev(ffs_tab[i], func_names[i]); + ret = ffs_name_dev(opts->dev, func_names[i]); if (ret) goto no_dev; - ffs_tab[i]->ffs_ready_callback = functionfs_ready_callback; - ffs_tab[i]->ffs_closed_callback = functionfs_closed_callback; + opts->dev->ffs_ready_callback = functionfs_ready_callback; + opts->dev->ffs_closed_callback = functionfs_closed_callback; + opts->dev->ffs_acquire_dev_callback = functionfs_acquire_dev; + opts->dev->ffs_release_dev_callback = functionfs_release_dev; + opts->no_configfs = true; } missing_funcs = func_num; return 0; no_dev: - ffs_dev_lock(); while (i >= 0) - ffs_free_dev(ffs_tab[i--]); - ffs_dev_unlock(); - kfree(ffs_tab); + usb_put_function_instance(fi_ffs[i--]); + kfree(fi_ffs); +no_func: + kfree(f_ffs[0]); return ret; } module_init(gfs_init); @@ -231,19 +259,33 @@ static void __exit gfs_exit(void) int i; ENTER(); - ffs_dev_lock(); if (gfs_registered) usb_composite_unregister(&gfs_driver); gfs_registered = false; + kfree(f_ffs[0]); + for (i = 0; i < func_num; i++) - ffs_free_dev(ffs_tab[i]); - ffs_dev_unlock(); - kfree(ffs_tab); + usb_put_function_instance(fi_ffs[i]); + + kfree(fi_ffs); } module_exit(gfs_exit); +static void *functionfs_acquire_dev(struct ffs_dev *dev) +{ + if (!try_module_get(THIS_MODULE)) + return ERR_PTR(-ENODEV); + + return 0; +} + +static void functionfs_release_dev(struct ffs_dev *dev) +{ + module_put(THIS_MODULE); +} + /* * The caller of this function takes ffs_lock */ @@ -360,20 +402,12 @@ static int gfs_bind(struct usb_composite_dev *cdev) rndis_borrow_net(fi_rndis, net); #endif + /* TODO: gstrings_attach? */ ret = usb_string_ids_tab(cdev, gfs_strings); if (unlikely(ret < 0)) goto error_rndis; gfs_dev_desc.iProduct = gfs_strings[USB_GADGET_PRODUCT_IDX].id; - for (i = func_num; i--; ) { - ret = functionfs_bind(ffs_tab[i]->ffs_data, cdev); - if (unlikely(ret < 0)) { - while (++i < func_num) - functionfs_unbind(ffs_tab[i]->ffs_data); - goto error_rndis; - } - } - for (i = 0; i < ARRAY_SIZE(gfs_configurations); ++i) { struct gfs_configuration *c = gfs_configurations + i; int sid = USB_GADGET_FIRST_AVAIL_IDX + i; @@ -383,6 +417,8 @@ static int gfs_bind(struct usb_composite_dev *cdev) c->c.bConfigurationValue = 1 + i; c->c.bmAttributes = USB_CONFIG_ATT_SELFPOWER; + c->num = i; + ret = usb_add_config(cdev, &c->c, gfs_do_config); if (unlikely(ret < 0)) goto error_unbind; @@ -390,9 +426,8 @@ static int gfs_bind(struct usb_composite_dev *cdev) usb_composite_overwrite_options(cdev, &coverwrite); return 0; +/* TODO */ error_unbind: - for (i = 0; i < func_num; i++) - functionfs_unbind(ffs_tab[i]->ffs_data); error_rndis: #ifdef CONFIG_USB_FUNCTIONFS_RNDIS usb_put_function_instance(fi_rndis); @@ -431,18 +466,8 @@ static int gfs_unbind(struct usb_composite_dev *cdev) usb_put_function_instance(fi_geth); } #endif - - /* - * We may have been called in an error recovery from - * composite_bind() after gfs_unbind() failure so we need to - * check if instance's ffs_data is not NULL since gfs_bind() handles - * all error recovery itself. I'd rather we werent called - * from composite on orror recovery, but what you're gonna - * do...? - */ - for (i = func_num; i--; ) - if (ffs_tab[i]->ffs_data) - functionfs_unbind(ffs_tab[i]->ffs_data); + for (i = 0; i < N_CONF * func_num; ++i) + usb_put_function(*(f_ffs[0] + i)); return 0; } @@ -473,9 +498,16 @@ static int gfs_do_config(struct usb_configuration *c) } for (i = 0; i < func_num; i++) { - ret = functionfs_bind_config(c->cdev, c, ffs_tab[i]->ffs_data); - if (unlikely(ret < 0)) - return ret; + f_ffs[gc->num][i] = usb_get_function(fi_ffs[i]); + if (IS_ERR(f_ffs[gc->num][i])) { + ret = PTR_ERR(f_ffs[gc->num][i]); + goto error; + } + ret = usb_add_function(c, f_ffs[gc->num][i]); + if (ret < 0) { + usb_put_function(f_ffs[gc->num][i]); + goto error; + } } /* @@ -492,6 +524,13 @@ static int gfs_do_config(struct usb_configuration *c) c->interface[c->next_interface_id] = NULL; return 0; +error: + while (--i >= 0) { + if (!IS_ERR(f_ffs[gc->num][i])) + usb_remove_function(c, f_ffs[gc->num][i]); + usb_put_function(f_ffs[gc->num][i]); + } + return ret; } #ifdef CONFIG_USB_FUNCTIONFS_ETH From 3d8d72a4c3c844c3c770c153bf570dc843143ac0 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Dec 2013 15:15:35 +0100 Subject: [PATCH 058/105] usb: gadget: FunctionFS: Remove compatibility layer There are no old function interface users left, so the old interface can be removed. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_fs.c | 114 --------------------------------- drivers/usb/gadget/u_fs.h | 2 - include/linux/usb/functionfs.h | 18 ------ 3 files changed, 134 deletions(-) diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index 8d318eaaaf20..9c8c74c25f1e 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -97,19 +97,12 @@ static struct ffs_function *ffs_func_from_usb(struct usb_function *f) return container_of(f, struct ffs_function, function); } -#ifdef USB_FFS_INCLUDED -static void ffs_func_free(struct ffs_function *func); -#endif static void ffs_func_eps_disable(struct ffs_function *func); static int __must_check ffs_func_eps_enable(struct ffs_function *func); static int ffs_func_bind(struct usb_configuration *, struct usb_function *); -#ifdef USB_FFS_INCLUDED -static void old_ffs_func_unbind(struct usb_configuration *, - struct usb_function *); -#endif static int ffs_func_set_alt(struct usb_function *, unsigned, unsigned); static void ffs_func_disable(struct usb_function *); static int ffs_func_setup(struct usb_function *, @@ -165,9 +158,7 @@ ffs_sb_create_file(struct super_block *sb, const char *name, void *data, /* Devices management *******************************************************/ DEFINE_MUTEX(ffs_lock); -#ifndef USB_FFS_INCLUDED EXPORT_SYMBOL(ffs_lock); -#endif static struct ffs_dev *ffs_find_dev(const char *name); static void *ffs_acquire_dev(const char *dev_name); @@ -1303,75 +1294,6 @@ static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count) kfree(epfiles); } -#ifdef USB_FFS_INCLUDED - -static int functionfs_bind_config(struct usb_composite_dev *cdev, - struct usb_configuration *c, - struct ffs_data *ffs) -{ - struct ffs_function *func; - int ret; - - ENTER(); - - func = kzalloc(sizeof *func, GFP_KERNEL); - if (unlikely(!func)) - return -ENOMEM; - - func->function.name = "Function FS Gadget"; - func->function.strings = ffs->stringtabs; - - func->function.bind = ffs_func_bind; - func->function.unbind = old_ffs_func_unbind; - func->function.set_alt = ffs_func_set_alt; - func->function.disable = ffs_func_disable; - func->function.setup = ffs_func_setup; - func->function.suspend = ffs_func_suspend; - func->function.resume = ffs_func_resume; - - func->conf = c; - func->gadget = cdev->gadget; - func->ffs = ffs; - ffs_data_get(ffs); - - ret = usb_add_function(c, &func->function); - if (unlikely(ret)) - ffs_func_free(func); - - return ret; -} - -static void ffs_func_free(struct ffs_function *func) -{ - struct ffs_ep *ep = func->eps; - unsigned count = func->ffs->eps_count; - unsigned long flags; - - ENTER(); - - /* cleanup after autoconfig */ - spin_lock_irqsave(&func->ffs->eps_lock, flags); - do { - if (ep->ep && ep->req) - usb_ep_free_request(ep->ep, ep->req); - ep->req = NULL; - ++ep; - } while (--count); - spin_unlock_irqrestore(&func->ffs->eps_lock, flags); - - ffs_data_put(func->ffs); - - kfree(func->eps); - /* - * eps and interfaces_nums are allocated in the same chunk so - * only one free is required. Descriptors are also allocated - * in the same chunk. - */ - - kfree(func); -} - -#endif static void ffs_func_eps_disable(struct ffs_function *func) { @@ -2035,7 +1957,6 @@ static int __ffs_func_bind_do_nums(enum ffs_entity_type type, u8 *valuep, return 0; } -#ifndef USB_FFS_INCLUDED static inline struct f_fs_opts *ffs_do_functionfs_bind(struct usb_function *f, struct usb_configuration *c) { @@ -2084,7 +2005,6 @@ static inline struct f_fs_opts *ffs_do_functionfs_bind(struct usb_function *f, return ffs_opts; } -#endif static int _ffs_func_bind(struct usb_configuration *c, struct usb_function *f) @@ -2190,12 +2110,10 @@ error: static int ffs_func_bind(struct usb_configuration *c, struct usb_function *f) { -#ifndef USB_FFS_INCLUDED struct f_fs_opts *ffs_opts = ffs_do_functionfs_bind(f, c); if (IS_ERR(ffs_opts)) return PTR_ERR(ffs_opts); -#endif return _ffs_func_bind(c, f); } @@ -2203,28 +2121,6 @@ static int ffs_func_bind(struct usb_configuration *c, /* Other USB function hooks *************************************************/ -#ifdef USB_FFS_INCLUDED - -static void old_ffs_func_unbind(struct usb_configuration *c, - struct usb_function *f) -{ - struct ffs_function *func = ffs_func_from_usb(f); - struct ffs_data *ffs = func->ffs; - - ENTER(); - - if (ffs->func == func) { - ffs_func_eps_disable(func); - ffs->func = NULL; - } - - ffs_event_add(ffs, FUNCTIONFS_UNBIND); - - ffs_func_free(func); -} - -#endif - static int ffs_func_set_alt(struct usb_function *f, unsigned interface, unsigned alt) { @@ -2401,8 +2297,6 @@ static struct ffs_dev *ffs_find_dev(const char *name) /* Function registration interface ******************************************/ -#ifndef USB_FFS_INCLUDED - static void ffs_free_inst(struct usb_function_instance *f) { struct f_fs_opts *opts; @@ -2507,8 +2401,6 @@ static struct usb_function *ffs_alloc(struct usb_function_instance *fi) return &func->function; } -#endif - /* * ffs_lock must be taken by the caller of this function */ @@ -2567,9 +2459,7 @@ int ffs_name_dev(struct ffs_dev *dev, const char *name) return ret; } -#ifndef USB_FFS_INCLUDED EXPORT_SYMBOL(ffs_name_dev); -#endif int ffs_single_dev(struct ffs_dev *dev) { @@ -2586,9 +2476,7 @@ int ffs_single_dev(struct ffs_dev *dev) ffs_dev_unlock(); return ret; } -#ifndef USB_FFS_INCLUDED EXPORT_SYMBOL(ffs_single_dev); -#endif /* * ffs_lock must be taken by the caller of this function @@ -2719,8 +2607,6 @@ static char *ffs_prepare_buffer(const char __user *buf, size_t len) return data; } -#ifndef USB_FFS_INCLUDED DECLARE_USB_FUNCTION_INIT(ffs, ffs_alloc_inst, ffs_alloc); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Michal Nazarewicz"); -#endif diff --git a/drivers/usb/gadget/u_fs.h b/drivers/usb/gadget/u_fs.h index d60a9ddc0332..09313750f913 100644 --- a/drivers/usb/gadget/u_fs.h +++ b/drivers/usb/gadget/u_fs.h @@ -249,7 +249,6 @@ struct ffs_data { }; -#ifndef USB_FFS_INCLUDED struct f_fs_opts { struct usb_function_instance func_inst; struct ffs_dev *dev; @@ -261,6 +260,5 @@ static inline struct f_fs_opts *to_f_fs_opts(struct usb_function_instance *fi) { return container_of(fi, struct f_fs_opts, func_inst); } -#endif #endif /* U_FFS_H */ diff --git a/include/linux/usb/functionfs.h b/include/linux/usb/functionfs.h index 3448efbe56aa..71190663f1ee 100644 --- a/include/linux/usb/functionfs.h +++ b/include/linux/usb/functionfs.h @@ -3,22 +3,4 @@ #include -#ifdef USB_FFS_INCLUDED - -struct ffs_data; -struct usb_composite_dev; -struct usb_configuration; - -static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev) - __attribute__((warn_unused_result, nonnull)); -static void functionfs_unbind(struct ffs_data *ffs) - __attribute__((nonnull)); - -static int functionfs_bind_config(struct usb_composite_dev *cdev, - struct usb_configuration *c, - struct ffs_data *ffs) - __attribute__((warn_unused_result, nonnull)); - - -#endif #endif From b658499f0f0f4ebf21d09c7da62a46f66ffa67cb Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Dec 2013 15:15:36 +0100 Subject: [PATCH 059/105] usb: gadget: FunctionFS: add configfs support Add support for using FunctionFS in configfs-based USB gadgets. [ balbi@ti.com : removed redefinition of VERBOSE_DEBUG and few trailing whitespaces ] Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- .../ABI/testing/configfs-usb-gadget-ffs | 9 +++ drivers/usb/gadget/Kconfig | 12 +++ drivers/usb/gadget/f_fs.c | 80 ++++++++++++++++++- drivers/usb/gadget/u_fs.h | 3 + 4 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 Documentation/ABI/testing/configfs-usb-gadget-ffs diff --git a/Documentation/ABI/testing/configfs-usb-gadget-ffs b/Documentation/ABI/testing/configfs-usb-gadget-ffs new file mode 100644 index 000000000000..14343e237e83 --- /dev/null +++ b/Documentation/ABI/testing/configfs-usb-gadget-ffs @@ -0,0 +1,9 @@ +What: /config/usb-gadget/gadget/functions/ffs.name +Date: Nov 2013 +KenelVersion: 3.13 +Description: The purpose of this directory is to create and remove it. + + A corresponding USB function instance is created/removed. + There are no attributes here. + + All parameters are set through FunctionFS. diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 97eb540ddef2..0ae2e6559397 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -701,6 +701,18 @@ config USB_CONFIGFS_F_LB_SS test software, like the "usbtest" driver, to put your hardware and its driver through a basic set of functional tests. +config USB_CONFIGFS_F_FS + boolean "Function filesystem (FunctionFS)" + depends on USB_CONFIGFS + select USB_F_FS + help + The Function Filesystem (FunctionFS) lets one create USB + composite functions in user space in the same way GadgetFS + lets one create USB gadgets in user space. This allows creation + of composite gadgets such that some of the functions are + implemented in kernel space (for instance Ethernet, serial or + mass storage) and other are implemented in user space. + config USB_ZERO tristate "Gadget Zero (DEVELOPMENT)" select USB_LIBCOMPOSITE diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index 9c8c74c25f1e..12a64e1c31ef 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -29,6 +29,7 @@ #include #include "u_fs.h" +#include "configfs.h" #define FUNCTIONFS_MAGIC 0xa647361 /* Chosen by a honest dice roll ;) */ @@ -161,6 +162,7 @@ DEFINE_MUTEX(ffs_lock); EXPORT_SYMBOL(ffs_lock); static struct ffs_dev *ffs_find_dev(const char *name); +static int _ffs_name_dev(struct ffs_dev *dev, const char *name); static void *ffs_acquire_dev(const char *dev_name); static void ffs_release_dev(struct ffs_data *ffs_data); static int ffs_ready(struct ffs_data *ffs); @@ -2261,7 +2263,7 @@ static struct ffs_dev *_ffs_find_dev(const char *name) if (strcmp(dev->name, name) == 0) return dev; } - + return NULL; } @@ -2295,6 +2297,31 @@ static struct ffs_dev *ffs_find_dev(const char *name) return _ffs_find_dev(name); } +/* Configfs support *********************************************************/ + +static inline struct f_fs_opts *to_ffs_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_fs_opts, + func_inst.group); +} + +static void ffs_attr_release(struct config_item *item) +{ + struct f_fs_opts *opts = to_ffs_opts(item); + + usb_put_function_instance(&opts->func_inst); +} + +static struct configfs_item_operations ffs_item_ops = { + .release = ffs_attr_release, +}; + +static struct config_item_type ffs_func_type = { + .ct_item_ops = &ffs_item_ops, + .ct_owner = THIS_MODULE, +}; + + /* Function registration interface ******************************************/ static void ffs_free_inst(struct usb_function_instance *f) @@ -2308,6 +2335,44 @@ static void ffs_free_inst(struct usb_function_instance *f) kfree(opts); } +#define MAX_INST_NAME_LEN 40 + +static int ffs_set_inst_name(struct usb_function_instance *fi, const char *name) +{ + struct f_fs_opts *opts; + char *ptr; + const char *tmp; + int name_len, ret; + + name_len = strlen(name) + 1; + if (name_len > MAX_INST_NAME_LEN) + return -ENAMETOOLONG; + + ptr = kstrndup(name, name_len, GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + opts = to_f_fs_opts(fi); + tmp = NULL; + + ffs_dev_lock(); + + tmp = opts->dev->name_allocated ? opts->dev->name : NULL; + ret = _ffs_name_dev(opts->dev, ptr); + if (ret) { + kfree(ptr); + ffs_dev_unlock(); + return ret; + } + opts->dev->name_allocated = true; + + ffs_dev_unlock(); + + kfree(tmp); + + return 0; +} + static struct usb_function_instance *ffs_alloc_inst(void) { struct f_fs_opts *opts; @@ -2317,6 +2382,7 @@ static struct usb_function_instance *ffs_alloc_inst(void) if (!opts) return ERR_PTR(-ENOMEM); + opts->func_inst.set_inst_name = ffs_set_inst_name; opts->func_inst.free_func_inst = ffs_free_inst; ffs_dev_lock(); dev = ffs_alloc_dev(); @@ -2326,7 +2392,10 @@ static struct usb_function_instance *ffs_alloc_inst(void) return ERR_CAST(dev); } opts->dev = dev; + dev->opts = opts; + config_group_init_type_name(&opts->func_inst.group, "", + &ffs_func_type); return &opts->func_inst; } @@ -2484,6 +2553,8 @@ EXPORT_SYMBOL(ffs_single_dev); void ffs_free_dev(struct ffs_dev *dev) { list_del(&dev->entry); + if (dev->name_allocated) + kfree(dev->name); kfree(dev); if (list_empty(&ffs_devices)) functionfs_cleanup(); @@ -2572,6 +2643,13 @@ static void ffs_closed(struct ffs_data *ffs) if (ffs_obj->ffs_closed_callback) ffs_obj->ffs_closed_callback(ffs); + + if (!ffs_obj->opts || ffs_obj->opts->no_configfs + || !ffs_obj->opts->func_inst.group.cg_item.ci_parent) + goto done; + + unregister_gadget_item(ffs_obj->opts-> + func_inst.group.cg_item.ci_parent->ci_parent); done: ffs_dev_unlock(); } diff --git a/drivers/usb/gadget/u_fs.h b/drivers/usb/gadget/u_fs.h index 09313750f913..bc2d3718219b 100644 --- a/drivers/usb/gadget/u_fs.h +++ b/drivers/usb/gadget/u_fs.h @@ -35,13 +35,16 @@ #define ENTER() pr_vdebug("%s()\n", __func__) +struct f_fs_opts; struct ffs_dev { const char *name; + bool name_allocated; bool mounted; bool desc_ready; bool single; struct ffs_data *ffs_data; + struct f_fs_opts *opts; struct list_head entry; int (*ffs_ready_callback)(struct ffs_data *ffs); From f8800d47bcdf5ae0582ac674657fd939a9105be0 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 12 Dec 2013 12:15:43 -0600 Subject: [PATCH 060/105] usb: gadget: f_fs: fix sparse warning use NULL when returning NULL pointers, not 0. Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index 12a64e1c31ef..306a2b52125c 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -1137,7 +1137,7 @@ static struct ffs_data *ffs_data_new(void) { struct ffs_data *ffs = kzalloc(sizeof *ffs, GFP_KERNEL); if (unlikely(!ffs)) - return 0; + return NULL; ENTER(); From e117e742d310683b410951faeab4b13b6c3c609f Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Fri, 13 Dec 2013 12:23:38 +0100 Subject: [PATCH 061/105] usb: gadget: add "maxpacket_limit" field to struct usb_ep This patch adds "maxpacket_limit" to struct usb_ep. This field contains maximum value of maxpacket supported by driver, and is set in driver probe. This value should be used by autoconfig() function, because value of field "maxpacket" is set to value from endpoint descriptor when endpoint becomes enabled. So when autoconfig() function will be called again for this endpoint, "maxpacket" value will contain wMaxPacketSize from descriptior instead of maximum packet size for this endpoint. For this reason this patch adds new field "maxpacket_limit" which contains value of maximum packet size (which defines maximum endpoint capabilities). This value is used in ep_matches() function used by autoconfig(). Value of "maxpacket_limit" should be set in UDC driver probe function, using usb_ep_set_maxpacket_limit() function, defined in gadget.h. This function set choosen value to both "maxpacket_limit" and "maxpacket" fields. This patch modifies UDC drivers by adding support for maxpacket_limit. Signed-off-by: Robert Baldyga Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/chipidea/udc.c | 4 ++-- drivers/usb/dwc3/gadget.c | 4 ++-- drivers/usb/gadget/amd5536udc.c | 15 +++++++++------ drivers/usb/gadget/at91_udc.c | 16 ++++++++-------- drivers/usb/gadget/atmel_usba_udc.c | 5 +++-- drivers/usb/gadget/bcm63xx_udc.c | 4 ++-- drivers/usb/gadget/dummy_hcd.c | 2 +- drivers/usb/gadget/epautoconf.c | 6 +++--- drivers/usb/gadget/fotg210-udc.c | 3 ++- drivers/usb/gadget/fsl_qe_udc.c | 2 +- drivers/usb/gadget/fsl_udc_core.c | 5 +++-- drivers/usb/gadget/fusb300_udc.c | 4 ++-- drivers/usb/gadget/goku_udc.c | 4 ++-- drivers/usb/gadget/lpc32xx_udc.c | 2 +- drivers/usb/gadget/m66592-udc.c | 4 ++-- drivers/usb/gadget/mv_u3d_core.c | 4 ++-- drivers/usb/gadget/mv_udc_core.c | 4 ++-- drivers/usb/gadget/net2272.c | 4 ++-- drivers/usb/gadget/net2280.c | 8 ++++---- drivers/usb/gadget/omap_udc.c | 3 ++- drivers/usb/gadget/pch_udc.c | 6 +++--- drivers/usb/gadget/pxa25x_udc.c | 1 + drivers/usb/gadget/pxa27x_udc.c | 5 ++++- drivers/usb/gadget/r8a66597-udc.c | 4 ++-- drivers/usb/gadget/s3c-hsotg.c | 2 +- drivers/usb/gadget/s3c-hsudc.c | 2 +- drivers/usb/gadget/s3c2410_udc.c | 1 + drivers/usb/musb/musb_gadget.c | 6 +++--- drivers/usb/renesas_usbhs/mod_gadget.c | 4 ++-- include/linux/usb/gadget.h | 19 +++++++++++++++++++ 30 files changed, 92 insertions(+), 61 deletions(-) diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index b34c81969cba..77e4a17cfb44 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1566,7 +1566,7 @@ static int init_eps(struct ci_hdrc *ci) * eps, maxP is set by epautoconfig() called * by gadget layer */ - hwep->ep.maxpacket = (unsigned short)~0; + usb_ep_set_maxpacket_limit(&hwep->ep, (unsigned short)~0); INIT_LIST_HEAD(&hwep->qh.queue); hwep->qh.ptr = dma_pool_alloc(ci->qh_pool, GFP_KERNEL, @@ -1586,7 +1586,7 @@ static int init_eps(struct ci_hdrc *ci) else ci->ep0in = hwep; - hwep->ep.maxpacket = CTRL_PAYLOAD_MAX; + usb_ep_set_maxpacket_limit(&hwep->ep, CTRL_PAYLOAD_MAX); continue; } diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index b85ec110d6a0..5401b2b573d5 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1653,7 +1653,7 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc, dev_vdbg(dwc->dev, "initializing %s\n", dep->name); if (epnum == 0 || epnum == 1) { - dep->endpoint.maxpacket = 512; + usb_ep_set_maxpacket_limit(&dep->endpoint, 512); dep->endpoint.maxburst = 1; dep->endpoint.ops = &dwc3_gadget_ep0_ops; if (!epnum) @@ -1661,7 +1661,7 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc, } else { int ret; - dep->endpoint.maxpacket = 1024; + usb_ep_set_maxpacket_limit(&dep->endpoint, 1024); dep->endpoint.max_streams = 15; dep->endpoint.ops = &dwc3_gadget_ep_ops; list_add_tail(&dep->endpoint.ep_list, diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index f0ff4a675e9d..a04aa8b64472 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -446,7 +446,7 @@ static void ep_init(struct udc_regs __iomem *regs, struct udc_ep *ep) ep->ep.ops = &udc_ep_ops; INIT_LIST_HEAD(&ep->queue); - ep->ep.maxpacket = (u16) ~0; + usb_ep_set_maxpacket_limit(&ep->ep,(u16) ~0); /* set NAK */ tmp = readl(&ep->regs->ctl); tmp |= AMD_BIT(UDC_EPCTL_SNAK); @@ -1564,12 +1564,15 @@ static void udc_setup_endpoints(struct udc *dev) } /* EP0 max packet */ if (dev->gadget.speed == USB_SPEED_FULL) { - dev->ep[UDC_EP0IN_IX].ep.maxpacket = UDC_FS_EP0IN_MAX_PKT_SIZE; - dev->ep[UDC_EP0OUT_IX].ep.maxpacket = - UDC_FS_EP0OUT_MAX_PKT_SIZE; + usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0IN_IX].ep, + UDC_FS_EP0IN_MAX_PKT_SIZE); + usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0OUT_IX].ep, + UDC_FS_EP0OUT_MAX_PKT_SIZE); } else if (dev->gadget.speed == USB_SPEED_HIGH) { - dev->ep[UDC_EP0IN_IX].ep.maxpacket = UDC_EP0IN_MAX_PKT_SIZE; - dev->ep[UDC_EP0OUT_IX].ep.maxpacket = UDC_EP0OUT_MAX_PKT_SIZE; + usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0IN_IX].ep, + UDC_EP0IN_MAX_PKT_SIZE); + usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0OUT_IX].ep, + UDC_EP0OUT_MAX_PKT_SIZE); } /* diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index 4cc4fd6d1473..0353b6471bde 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -834,7 +834,7 @@ static void udc_reinit(struct at91_udc *udc) ep->ep.desc = NULL; ep->stopped = 0; ep->fifo_bank = 0; - ep->ep.maxpacket = ep->maxpacket; + usb_ep_set_maxpacket_limit(&ep->ep, ep->maxpacket); ep->creg = (void __iomem *) udc->udp_baseaddr + AT91_UDP_CSR(i); /* initialize one queue per endpoint */ INIT_LIST_HEAD(&ep->queue); @@ -1759,15 +1759,15 @@ static int at91udc_probe(struct platform_device *pdev) /* newer chips have more FIFO memory than rm9200 */ if (cpu_is_at91sam9260() || cpu_is_at91sam9g20()) { - udc->ep[0].maxpacket = 64; - udc->ep[3].maxpacket = 64; - udc->ep[4].maxpacket = 512; - udc->ep[5].maxpacket = 512; + usb_ep_set_maxpacket_limit(&udc->ep[0], 64); + usb_ep_set_maxpacket_limit(&udc->ep[3], 64); + usb_ep_set_maxpacket_limit(&udc->ep[4], 512); + usb_ep_set_maxpacket_limit(&udc->ep[5], 512); } else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) { - udc->ep[3].maxpacket = 64; + usb_ep_set_maxpacket_limit(&udc->ep[3], 64); } else if (cpu_is_at91sam9263()) { - udc->ep[0].maxpacket = 64; - udc->ep[3].maxpacket = 64; + usb_ep_set_maxpacket_limit(&udc->ep[0], 64); + usb_ep_set_maxpacket_limit(&udc->ep[3], 64); } udc->udp_baseaddr = ioremap(res->start, resource_size(res)); diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index 2cb52e0438df..68cf3a40f98e 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -1904,7 +1904,7 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev, ep->dma_regs = udc->regs + USBA_DMA_BASE(i); ep->fifo = udc->fifo + USBA_FIFO_BASE(i); ep->ep.ops = &usba_ep_ops; - ep->ep.maxpacket = ep->fifo_size; + usb_ep_set_maxpacket_limit(&ep->ep, ep->fifo_size); ep->udc = udc; INIT_LIST_HEAD(&ep->queue); @@ -1957,7 +1957,8 @@ static struct usba_ep * usba_udc_pdata(struct platform_device *pdev, ep->fifo = udc->fifo + USBA_FIFO_BASE(i); ep->ep.ops = &usba_ep_ops; ep->ep.name = pdata->ep[i].name; - ep->fifo_size = ep->ep.maxpacket = pdata->ep[i].fifo_size; + ep->fifo_size = pdata->ep[i].fifo_size; + usb_ep_set_maxpacket_limit(&ep->ep, ep->fifo_size); ep->udc = udc; INIT_LIST_HEAD(&ep->queue); ep->nr_banks = pdata->ep[i].nr_banks; diff --git a/drivers/usb/gadget/bcm63xx_udc.c b/drivers/usb/gadget/bcm63xx_udc.c index c58fcf1ebe41..2ac7a8f4bfff 100644 --- a/drivers/usb/gadget/bcm63xx_udc.c +++ b/drivers/usb/gadget/bcm63xx_udc.c @@ -549,7 +549,7 @@ static void bcm63xx_ep_setup(struct bcm63xx_udc *udc) if (idx < 0) continue; - udc->bep[idx].ep.maxpacket = max_pkt; + usb_ep_set_maxpacket_limit(&udc->bep[idx].ep, max_pkt); val = (idx << USBD_CSR_EP_LOG_SHIFT) | (cfg->dir << USBD_CSR_EP_DIR_SHIFT) | @@ -943,7 +943,7 @@ static int bcm63xx_init_udc_hw(struct bcm63xx_udc *udc) bep->ep.ops = &bcm63xx_udc_ep_ops; list_add_tail(&bep->ep.ep_list, &udc->gadget.ep_list); bep->halted = 0; - bep->ep.maxpacket = BCM63XX_MAX_CTRL_PKT; + usb_ep_set_maxpacket_limit(&bep->ep, BCM63XX_MAX_CTRL_PKT); bep->udc = udc; bep->ep.desc = NULL; INIT_LIST_HEAD(&bep->queue); diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index 8f4dae310923..8c06430dcc47 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -951,7 +951,7 @@ static void init_dummy_udc_hw(struct dummy *dum) list_add_tail(&ep->ep.ep_list, &dum->gadget.ep_list); ep->halted = ep->wedged = ep->already_seen = ep->setup_stage = 0; - ep->ep.maxpacket = ~0; + usb_ep_set_maxpacket_limit(&ep->ep, ~0); ep->ep.max_streams = 16; ep->last_io = jiffies; ep->gadget = &dum->gadget; diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index feaaa7b72ee3..358de320afb0 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -129,7 +129,7 @@ ep_matches ( * and wants to know the maximum possible, provide the info. */ if (desc->wMaxPacketSize == 0) - desc->wMaxPacketSize = cpu_to_le16(ep->maxpacket); + desc->wMaxPacketSize = cpu_to_le16(ep->maxpacket_limit); /* endpoint maxpacket size is an input parameter, except for bulk * where it's an output parameter representing the full speed limit. @@ -145,7 +145,7 @@ ep_matches ( case USB_ENDPOINT_XFER_ISOC: /* ISO: limit 1023 bytes full speed, 1024 high/super speed */ - if (ep->maxpacket < max) + if (ep->maxpacket_limit < max) return 0; if (!gadget_is_dualspeed(gadget) && max > 1023) return 0; @@ -178,7 +178,7 @@ ep_matches ( /* report (variable) full speed bulk maxpacket */ if ((USB_ENDPOINT_XFER_BULK == type) && !ep_comp) { - int size = ep->maxpacket; + int size = ep->maxpacket_limit; /* min() doesn't work on bitfields with gcc-3.5 */ if (size > 64) diff --git a/drivers/usb/gadget/fotg210-udc.c b/drivers/usb/gadget/fotg210-udc.c index bbbfd1948778..2d0305280e8c 100644 --- a/drivers/usb/gadget/fotg210-udc.c +++ b/drivers/usb/gadget/fotg210-udc.c @@ -1157,8 +1157,9 @@ static int fotg210_udc_probe(struct platform_device *pdev) INIT_LIST_HEAD(&ep->queue); ep->ep.name = fotg210_ep_name[i]; ep->ep.ops = &fotg210_ep_ops; + usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0); } - fotg210->ep[0]->ep.maxpacket = 0x40; + usb_ep_set_maxpacket_limit(&fotg210->ep[0]->ep, 0x40); fotg210->gadget.ep0 = &fotg210->ep[0]->ep; INIT_LIST_HEAD(&fotg210->gadget.ep0->ep_list); diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index 6315ee698d4d..f60d4da8f2c0 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -2429,7 +2429,7 @@ static int qe_ep_config(struct qe_udc *udc, unsigned char pipe_num) ep->ep.ops = &qe_ep_ops; ep->stopped = 1; - ep->ep.maxpacket = (unsigned short) ~0; + usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0); ep->ep.desc = NULL; ep->dir = 0xff; ep->epnum = (u8)pipe_num; diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index b7dea4eec32c..15960af0f67e 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -2311,7 +2311,7 @@ static int __init struct_ep_setup(struct fsl_udc *udc, unsigned char index, /* for ep0: maxP defined in desc * for other eps, maxP is set by epautoconfig() called by gadget layer */ - ep->ep.maxpacket = (unsigned short) ~0; + usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0); /* the queue lists any req for this ep */ INIT_LIST_HEAD(&ep->queue); @@ -2469,7 +2469,8 @@ static int __init fsl_udc_probe(struct platform_device *pdev) * for other eps, gadget layer called ep_enable with defined desc */ udc_controller->eps[0].ep.desc = &fsl_ep0_desc; - udc_controller->eps[0].ep.maxpacket = USB_MAX_CTRL_PAYLOAD; + usb_ep_set_maxpacket_limit(&udc_controller->eps[0].ep, + USB_MAX_CTRL_PAYLOAD); /* setup the udc->eps[] for non-control endpoints and link * to gadget.ep_list */ diff --git a/drivers/usb/gadget/fusb300_udc.c b/drivers/usb/gadget/fusb300_udc.c index b278abe52453..6423f1840ed9 100644 --- a/drivers/usb/gadget/fusb300_udc.c +++ b/drivers/usb/gadget/fusb300_udc.c @@ -1452,9 +1452,9 @@ static int __init fusb300_probe(struct platform_device *pdev) INIT_LIST_HEAD(&ep->queue); ep->ep.name = fusb300_ep_name[i]; ep->ep.ops = &fusb300_ep_ops; - ep->ep.maxpacket = HS_BULK_MAX_PACKET_SIZE; + usb_ep_set_maxpacket_limit(&ep->ep, HS_BULK_MAX_PACKET_SIZE); } - fusb300->ep[0]->ep.maxpacket = HS_CTL_MAX_PACKET_SIZE; + usb_ep_set_maxpacket_limit(&fusb300->ep[0]->ep, HS_CTL_MAX_PACKET_SIZE); fusb300->ep[0]->epnum = 0; fusb300->gadget.ep0 = &fusb300->ep[0]->ep; INIT_LIST_HEAD(&fusb300->gadget.ep0->ep_list); diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c index 7eda9fd6528e..f66f3a7a35ef 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/goku_udc.c @@ -231,7 +231,7 @@ static void ep_reset(struct goku_udc_regs __iomem *regs, struct goku_ep *ep) } } - ep->ep.maxpacket = MAX_FIFO_SIZE; + usb_ep_set_maxpacket_limit(&ep->ep, MAX_FIFO_SIZE); ep->ep.desc = NULL; ep->stopped = 1; ep->irqs = 0; @@ -1251,7 +1251,7 @@ static void udc_reinit (struct goku_udc *dev) } dev->ep[0].reg_mode = NULL; - dev->ep[0].ep.maxpacket = MAX_EP0_SIZE; + usb_ep_set_maxpacket_limit(&dev->ep[0].ep, MAX_EP0_SIZE); list_del_init (&dev->ep[0].ep.ep_list); } diff --git a/drivers/usb/gadget/lpc32xx_udc.c b/drivers/usb/gadget/lpc32xx_udc.c index 6a2a65aa0057..049ebab0d360 100644 --- a/drivers/usb/gadget/lpc32xx_udc.c +++ b/drivers/usb/gadget/lpc32xx_udc.c @@ -1449,7 +1449,7 @@ static void udc_reinit(struct lpc32xx_udc *udc) if (i != 0) list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); - ep->ep.maxpacket = ep->maxpacket; + usb_ep_set_maxpacket_limit(&ep->ep, ep->maxpacket); INIT_LIST_HEAD(&ep->queue); ep->req_pending = 0; } diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c index d5f050d30edf..8cae01d88597 100644 --- a/drivers/usb/gadget/m66592-udc.c +++ b/drivers/usb/gadget/m66592-udc.c @@ -1647,9 +1647,9 @@ static int __init m66592_probe(struct platform_device *pdev) INIT_LIST_HEAD(&ep->queue); ep->ep.name = m66592_ep_name[i]; ep->ep.ops = &m66592_ep_ops; - ep->ep.maxpacket = 512; + usb_ep_set_maxpacket_limit(&ep->ep, 512); } - m66592->ep[0].ep.maxpacket = 64; + usb_ep_set_maxpacket_limit(&m66592->ep[0].ep, 64); m66592->ep[0].pipenum = 0; m66592->ep[0].fifoaddr = M66592_CFIFO; m66592->ep[0].fifosel = M66592_CFIFOSEL; diff --git a/drivers/usb/gadget/mv_u3d_core.c b/drivers/usb/gadget/mv_u3d_core.c index 234711eabea1..9fe31d7dded6 100644 --- a/drivers/usb/gadget/mv_u3d_core.c +++ b/drivers/usb/gadget/mv_u3d_core.c @@ -1336,7 +1336,7 @@ static int mv_u3d_eps_init(struct mv_u3d *u3d) ep->ep.name = ep->name; ep->ep.ops = &mv_u3d_ep_ops; ep->wedge = 0; - ep->ep.maxpacket = MV_U3D_EP0_MAX_PKT_SIZE; + usb_ep_set_maxpacket_limit(&ep->ep, MV_U3D_EP0_MAX_PKT_SIZE); ep->ep_num = 0; ep->ep.desc = &mv_u3d_ep0_desc; INIT_LIST_HEAD(&ep->queue); @@ -1361,7 +1361,7 @@ static int mv_u3d_eps_init(struct mv_u3d *u3d) ep->ep.name = ep->name; ep->ep.ops = &mv_u3d_ep_ops; - ep->ep.maxpacket = (unsigned short) ~0; + usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0); ep->ep_num = i / 2; INIT_LIST_HEAD(&ep->queue); diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index 104cdbea635a..d43ce95fc4bd 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -1261,7 +1261,7 @@ static int eps_init(struct mv_udc *udc) ep->ep.ops = &mv_ep_ops; ep->wedge = 0; ep->stopped = 0; - ep->ep.maxpacket = EP0_MAX_PKT_SIZE; + usb_ep_set_maxpacket_limit(&ep->ep, EP0_MAX_PKT_SIZE); ep->ep_num = 0; ep->ep.desc = &mv_ep0_desc; INIT_LIST_HEAD(&ep->queue); @@ -1284,7 +1284,7 @@ static int eps_init(struct mv_udc *udc) ep->ep.ops = &mv_ep_ops; ep->stopped = 0; - ep->ep.maxpacket = (unsigned short) ~0; + usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0); ep->ep_num = i / 2; INIT_LIST_HEAD(&ep->queue); diff --git a/drivers/usb/gadget/net2272.c b/drivers/usb/gadget/net2272.c index bf2bb39f35a2..ca15405583e2 100644 --- a/drivers/usb/gadget/net2272.c +++ b/drivers/usb/gadget/net2272.c @@ -266,7 +266,7 @@ static void net2272_ep_reset(struct net2272_ep *ep) ep->desc = NULL; INIT_LIST_HEAD(&ep->queue); - ep->ep.maxpacket = ~0; + usb_ep_set_maxpacket_limit(&ep->ep, ~0); ep->ep.ops = &net2272_ep_ops; /* disable irqs, endpoint */ @@ -1409,7 +1409,7 @@ net2272_usb_reinit(struct net2272 *dev) ep->fifo_size = 64; net2272_ep_reset(ep); } - dev->ep[0].ep.maxpacket = 64; + usb_ep_set_maxpacket_limit(&dev->ep[0].ep, 64); dev->gadget.ep0 = &dev->ep[0].ep; dev->ep[0].stopped = 0; diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index fc852177c087..43e5e2f9888f 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -293,7 +293,7 @@ static void ep_reset (struct net2280_regs __iomem *regs, struct net2280_ep *ep) ep->desc = NULL; INIT_LIST_HEAD (&ep->queue); - ep->ep.maxpacket = ~0; + usb_ep_set_maxpacket_limit(&ep->ep, ~0); ep->ep.ops = &net2280_ep_ops; /* disable the dma, irqs, endpoint... */ @@ -1805,9 +1805,9 @@ static void usb_reinit (struct net2280 *dev) ep->regs = &dev->epregs [tmp]; ep_reset (dev->regs, ep); } - dev->ep [0].ep.maxpacket = 64; - dev->ep [5].ep.maxpacket = 64; - dev->ep [6].ep.maxpacket = 64; + usb_ep_set_maxpacket_limit(&dev->ep [0].ep, 64); + usb_ep_set_maxpacket_limit(&dev->ep [5].ep, 64); + usb_ep_set_maxpacket_limit(&dev->ep [6].ep, 64); dev->gadget.ep0 = &dev->ep [0].ep; dev->ep [0].stopped = 0; diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 83957cc225d9..34bd713065c5 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -2586,7 +2586,8 @@ omap_ep_setup(char *name, u8 addr, u8 type, ep->ep.name = ep->name; ep->ep.ops = &omap_ep_ops; - ep->ep.maxpacket = ep->maxpacket = maxp; + ep->maxpacket = maxp; + usb_ep_set_maxpacket_limit(&ep->ep, ep->maxpacket); list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); return buf; diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c index 78a3d9289816..eb8c3bedb57a 100644 --- a/drivers/usb/gadget/pch_udc.c +++ b/drivers/usb/gadget/pch_udc.c @@ -2896,12 +2896,12 @@ static void pch_udc_pcd_reinit(struct pch_udc_dev *dev) ep->offset_addr = (UDC_EPINT_OUT_SHIFT + ep->num) * UDC_EP_REG_SHIFT; /* need to set ep->ep.maxpacket and set Default Configuration?*/ - ep->ep.maxpacket = UDC_BULK_MAX_PKT_SIZE; + usb_ep_set_maxpacket_limit(&ep->ep, UDC_BULK_MAX_PKT_SIZE); list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list); INIT_LIST_HEAD(&ep->queue); } - dev->ep[UDC_EP0IN_IDX].ep.maxpacket = UDC_EP0IN_MAX_PKT_SIZE; - dev->ep[UDC_EP0OUT_IDX].ep.maxpacket = UDC_EP0OUT_MAX_PKT_SIZE; + usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0IN_IDX].ep, UDC_EP0IN_MAX_PKT_SIZE); + usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0OUT_IDX].ep, UDC_EP0OUT_MAX_PKT_SIZE); /* remove ep0 in and out from the list. They have own pointer */ list_del_init(&dev->ep[UDC_EP0IN_IDX].ep.ep_list); diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index 0ac6064aa3b8..5b4f43730d8b 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -1193,6 +1193,7 @@ static void udc_reinit(struct pxa25x_udc *dev) ep->stopped = 0; INIT_LIST_HEAD (&ep->queue); ep->pio_irqs = 0; + usb_ep_set_maxpacket_limit(&ep->ep, ep->ep.maxpacket); } /* the rest was statically initialized, and is read-only */ diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index 3c97da7760da..cdf4d678be96 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -1737,9 +1737,12 @@ static void udc_init_data(struct pxa_udc *dev) } /* USB endpoints init */ - for (i = 1; i < NR_USB_ENDPOINTS; i++) + for (i = 1; i < NR_USB_ENDPOINTS; i++) { list_add_tail(&dev->udc_usb_ep[i].usb_ep.ep_list, &dev->gadget.ep_list); + usb_ep_set_maxpacket_limit(&dev->udc_usb_ep[i].usb_ep, + dev->udc_usb_ep[i].usb_ep.maxpacket); + } } /** diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/r8a66597-udc.c index 47287518bff3..aff0a6718bc6 100644 --- a/drivers/usb/gadget/r8a66597-udc.c +++ b/drivers/usb/gadget/r8a66597-udc.c @@ -1964,9 +1964,9 @@ static int __init r8a66597_probe(struct platform_device *pdev) INIT_LIST_HEAD(&ep->queue); ep->ep.name = r8a66597_ep_name[i]; ep->ep.ops = &r8a66597_ep_ops; - ep->ep.maxpacket = 512; + usb_ep_set_maxpacket_limit(&ep->ep, 512); } - r8a66597->ep[0].ep.maxpacket = 64; + usb_ep_set_maxpacket_limit(&r8a66597->ep[0].ep, 64); r8a66597->ep[0].pipenum = 0; r8a66597->ep[0].fifoaddr = CFIFO; r8a66597->ep[0].fifosel = CFIFOSEL; diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 6c80bfcefa87..50df18d1f1cf 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -3150,7 +3150,7 @@ static void s3c_hsotg_initep(struct s3c_hsotg *hsotg, hs_ep->parent = hsotg; hs_ep->ep.name = hs_ep->name; - hs_ep->ep.maxpacket = epnum ? 1024 : EP0_MPS_LIMIT; + usb_ep_set_maxpacket_limit(&hs_ep->ep, epnum ? 1024 : EP0_MPS_LIMIT); hs_ep->ep.ops = &s3c_hsotg_ep_ops; /* diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index 1a1a41498db2..ea4bbfe72ec0 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -999,7 +999,7 @@ static void s3c_hsudc_initep(struct s3c_hsudc *hsudc, hsep->dev = hsudc; hsep->ep.name = hsep->name; - hsep->ep.maxpacket = epnum ? 512 : 64; + usb_ep_set_maxpacket_limit(&hsep->ep, epnum ? 512 : 64); hsep->ep.ops = &s3c_hsudc_ep_ops; hsep->fifo = hsudc->regs + S3C_BR(epnum); hsep->ep.desc = NULL; diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c index c72d810e6b36..f04b2c3154de 100644 --- a/drivers/usb/gadget/s3c2410_udc.c +++ b/drivers/usb/gadget/s3c2410_udc.c @@ -1629,6 +1629,7 @@ static void s3c2410_udc_reinit(struct s3c2410_udc *dev) ep->ep.desc = NULL; ep->halted = 0; INIT_LIST_HEAD(&ep->queue); + usb_ep_set_maxpacket_limit(&ep->ep, &ep->ep.maxpacket); } } diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index d2d3a173b315..76f007654821 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1727,14 +1727,14 @@ init_peripheral_ep(struct musb *musb, struct musb_ep *ep, u8 epnum, int is_in) ep->end_point.name = ep->name; INIT_LIST_HEAD(&ep->end_point.ep_list); if (!epnum) { - ep->end_point.maxpacket = 64; + usb_ep_set_maxpacket_limit(&ep->end_point, 64); ep->end_point.ops = &musb_g_ep0_ops; musb->g.ep0 = &ep->end_point; } else { if (is_in) - ep->end_point.maxpacket = hw_ep->max_packet_sz_tx; + usb_ep_set_maxpacket_limit(&ep->end_point, hw_ep->max_packet_sz_tx); else - ep->end_point.maxpacket = hw_ep->max_packet_sz_rx; + usb_ep_set_maxpacket_limit(&ep->end_point, hw_ep->max_packet_sz_rx); ep->end_point.ops = &musb_ep_ops; list_add_tail(&ep->end_point.ep_list, &musb->g.ep_list); } diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index 3385aeb5a364..458f3766bef1 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -987,11 +987,11 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv) /* init DCP */ if (usbhsg_is_dcp(uep)) { gpriv->gadget.ep0 = &uep->ep; - uep->ep.maxpacket = 64; + usb_ep_set_maxpacket_limit(&uep->ep, 64); } /* init normal pipe */ else { - uep->ep.maxpacket = 512; + usb_ep_set_maxpacket_limit(&uep->ep, 512); list_add_tail(&uep->ep.ep_list, &gpriv->gadget.ep_list); } } diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index cae8a6216551..c3a61853cd13 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -148,6 +148,9 @@ struct usb_ep_ops { * @maxpacket:The maximum packet size used on this endpoint. The initial * value can sometimes be reduced (hardware allowing), according to * the endpoint descriptor used to configure the endpoint. + * @maxpacket_limit:The maximum packet size value which can be handled by this + * endpoint. It's set once by UDC driver when endpoint is initialized, and + * should not be changed. Should not be confused with maxpacket. * @max_streams: The maximum number of streams supported * by this EP (0 - 16, actual number is 2^n) * @mult: multiplier, 'mult' value for SS Isoc EPs @@ -171,6 +174,7 @@ struct usb_ep { const struct usb_ep_ops *ops; struct list_head ep_list; unsigned maxpacket:16; + unsigned maxpacket_limit:16; unsigned max_streams:16; unsigned mult:2; unsigned maxburst:5; @@ -181,6 +185,21 @@ struct usb_ep { /*-------------------------------------------------------------------------*/ +/** + * usb_ep_set_maxpacket_limit - set maximum packet size limit for endpoint + * @ep:the endpoint being configured + * @maxpacket_limit:value of maximum packet size limit + * + * This function shoud be used only in UDC drivers to initialize endpoint + * (usually in probe function). + */ +static inline void usb_ep_set_maxpacket_limit(struct usb_ep *ep, + unsigned maxpacket_limit) +{ + ep->maxpacket_limit = maxpacket_limit; + ep->maxpacket = maxpacket_limit; +} + /** * usb_ep_enable - configure endpoint, making it usable * @ep:the endpoint being configured. may not be the endpoint named "ep0". From 45ab460975c5433d1bd81b211fe643732abaae19 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Fri, 13 Dec 2013 14:46:40 +0100 Subject: [PATCH 062/105] usb: gadget: nokia: fix error recovery path for optional functions In the nokia gadget some USB functions (obex 1 and 2, phonet) are optional. If at the start of nokia_bind_config e.g. fi_phonet is an error pointer, which can happen because we don't fail the bind process if usb_get_function_instance() fails for fi_phonet, then f_phonet is NULL, and phonet_stat = usb_add_function(c, f_phonet); is never called and phonet_stat remains 0. If, in these circumstances, we hit the err_conf label then !phonet_stat evaluates to true and we try usb_remove_function() with its second parameter being f_phonet which is NULL and it causes NULL pointer dereference. This patch changes the initial values of (obex1|obex2|phonet)_stat to a nonzero value so that if the err_conf label is hit while the respective functions have not been acquired the usb_remove_function() is not called for those functions. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/nokia.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/nokia.c index 0a8099a488c4..3ab386167519 100644 --- a/drivers/usb/gadget/nokia.c +++ b/drivers/usb/gadget/nokia.c @@ -126,9 +126,9 @@ static int __init nokia_bind_config(struct usb_configuration *c) struct usb_function *f_ecm; struct usb_function *f_obex2 = NULL; int status = 0; - int obex1_stat = 0; - int obex2_stat = 0; - int phonet_stat = 0; + int obex1_stat = -1; + int obex2_stat = -1; + int phonet_stat = -1; if (!IS_ERR(fi_phonet)) { f_phonet = usb_get_function(fi_phonet); From 40a8fb2a671319c589baa9995e7b6f3b8c72c30c Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 16 Dec 2013 15:13:31 +0900 Subject: [PATCH 063/105] usb: gadget: atmel_usba: Use devm_*() functions Use devm_*() functions to make cleanup paths simpler. Acked-by: Nicolas Ferre Signed-off-by: Jingoo Han Signed-off-by: Felipe Balbi --- drivers/usb/gadget/atmel_usba_udc.c | 64 ++++++++--------------------- 1 file changed, 17 insertions(+), 47 deletions(-) diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index 68cf3a40f98e..bb1eb4220785 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -1996,14 +1996,12 @@ static int __init usba_udc_probe(struct platform_device *pdev) if (irq < 0) return irq; - pclk = clk_get(&pdev->dev, "pclk"); + pclk = devm_clk_get(&pdev->dev, "pclk"); if (IS_ERR(pclk)) return PTR_ERR(pclk); - hclk = clk_get(&pdev->dev, "hclk"); - if (IS_ERR(hclk)) { - ret = PTR_ERR(hclk); - goto err_get_hclk; - } + hclk = devm_clk_get(&pdev->dev, "hclk"); + if (IS_ERR(hclk)) + return PTR_ERR(hclk); spin_lock_init(&udc->lock); udc->pdev = pdev; @@ -2012,17 +2010,17 @@ static int __init usba_udc_probe(struct platform_device *pdev) udc->vbus_pin = -ENODEV; ret = -ENOMEM; - udc->regs = ioremap(regs->start, resource_size(regs)); + udc->regs = devm_ioremap(&pdev->dev, regs->start, resource_size(regs)); if (!udc->regs) { dev_err(&pdev->dev, "Unable to map I/O memory, aborting.\n"); - goto err_map_regs; + return ret; } dev_info(&pdev->dev, "MMIO registers at 0x%08lx mapped at %p\n", (unsigned long)regs->start, udc->regs); - udc->fifo = ioremap(fifo->start, resource_size(fifo)); + udc->fifo = devm_ioremap(&pdev->dev, fifo->start, resource_size(fifo)); if (!udc->fifo) { dev_err(&pdev->dev, "Unable to map FIFO, aborting.\n"); - goto err_map_fifo; + return ret; } dev_info(&pdev->dev, "FIFO at 0x%08lx mapped at %p\n", (unsigned long)fifo->start, udc->fifo); @@ -2033,7 +2031,7 @@ static int __init usba_udc_probe(struct platform_device *pdev) ret = clk_prepare_enable(pclk); if (ret) { dev_err(&pdev->dev, "Unable to enable pclk, aborting.\n"); - goto err_clk_enable; + return ret; } toggle_bias(0); usba_writel(udc, CTRL, USBA_DISABLE_MASK); @@ -2044,22 +2042,22 @@ static int __init usba_udc_probe(struct platform_device *pdev) else udc->usba_ep = usba_udc_pdata(pdev, udc); - if (IS_ERR(udc->usba_ep)) { - ret = PTR_ERR(udc->usba_ep); - goto err_alloc_ep; - } + if (IS_ERR(udc->usba_ep)) + return PTR_ERR(udc->usba_ep); - ret = request_irq(irq, usba_udc_irq, 0, "atmel_usba_udc", udc); + ret = devm_request_irq(&pdev->dev, irq, usba_udc_irq, 0, + "atmel_usba_udc", udc); if (ret) { dev_err(&pdev->dev, "Cannot request irq %d (error %d)\n", irq, ret); - goto err_request_irq; + return ret; } udc->irq = irq; if (gpio_is_valid(udc->vbus_pin)) { if (!devm_gpio_request(&pdev->dev, udc->vbus_pin, "atmel_usba_udc")) { - ret = request_irq(gpio_to_irq(udc->vbus_pin), + ret = devm_request_irq(&pdev->dev, + gpio_to_irq(udc->vbus_pin), usba_vbus_irq, 0, "atmel_usba_udc", udc); if (ret) { @@ -2078,31 +2076,13 @@ static int __init usba_udc_probe(struct platform_device *pdev) ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget); if (ret) - goto err_add_udc; + return ret; usba_init_debugfs(udc); for (i = 1; i < udc->num_ep; i++) usba_ep_init_debugfs(udc, &udc->usba_ep[i]); return 0; - -err_add_udc: - if (gpio_is_valid(udc->vbus_pin)) - free_irq(gpio_to_irq(udc->vbus_pin), udc); - - free_irq(irq, udc); -err_request_irq: -err_alloc_ep: -err_clk_enable: - iounmap(udc->fifo); -err_map_fifo: - iounmap(udc->regs); -err_map_regs: - clk_put(hclk); -err_get_hclk: - clk_put(pclk); - - return ret; } static int __exit usba_udc_remove(struct platform_device *pdev) @@ -2118,16 +2098,6 @@ static int __exit usba_udc_remove(struct platform_device *pdev) usba_ep_cleanup_debugfs(&udc->usba_ep[i]); usba_cleanup_debugfs(udc); - if (gpio_is_valid(udc->vbus_pin)) { - free_irq(gpio_to_irq(udc->vbus_pin), udc); - } - - free_irq(udc->irq, udc); - iounmap(udc->fifo); - iounmap(udc->regs); - clk_put(udc->hclk); - clk_put(udc->pclk); - return 0; } From 5056ee83e8e9b3b3eec895f8bb0804a802fb61d2 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 16 Dec 2013 15:14:23 +0900 Subject: [PATCH 064/105] usb: gadget: atmel_usba: Fix sparse warning 'usba_gadget_template' is used only in this file. Thus, make 'usba_gadget_template' static, in order to fix the following sparse warning. warning: symbol 'usba_gadget_template' was not declared. Should it be static? Acked-by: Nicolas Ferre Signed-off-by: Jingoo Han Signed-off-by: Felipe Balbi --- drivers/usb/gadget/atmel_usba_udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index bb1eb4220785..38bf67b1a97d 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -1012,7 +1012,7 @@ static void nop_release(struct device *dev) } -struct usb_gadget usba_gadget_template = { +static struct usb_gadget usba_gadget_template = { .ops = &usba_udc_ops, .max_speed = USB_SPEED_HIGH, .name = "atmel_usba_udc", From 086ed9a0bcca0201301b74a0c5160c04f2f38f8b Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Fri, 13 Dec 2013 10:47:28 +0000 Subject: [PATCH 065/105] usb: musb: ux500_dma: fix potential NULL dereference error static checker warning: "drivers/usb/musb/ux500_dma.c:335 ux500_dma_controller_start() error: potential NULL dereference 'param_array'." Acked-by: Linus Walleij Reported-by: Dan Carpenter Signed-off-by: Lee Jones Signed-off-by: Felipe Balbi --- drivers/usb/musb/ux500_dma.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c index 3700e9713258..9aad00f11bd5 100644 --- a/drivers/usb/musb/ux500_dma.c +++ b/drivers/usb/musb/ux500_dma.c @@ -336,7 +336,9 @@ static int ux500_dma_controller_start(struct ux500_dma_controller *controller) data ? data->dma_filter : NULL, - param_array[ch_num]); + param_array ? + param_array[ch_num] : + NULL); if (!ux500_channel->dma_chan) { ERR("Dma pipe allocation error dir=%d ch=%d\n", From 8002418dfff422f9b96de2a52112ed4d552d36bb Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 16 Dec 2013 18:40:49 +0900 Subject: [PATCH 066/105] usb: gadget: f_loopback: Fix sparse warning Make local symbols static in order to fix the following sparse warnings. drivers/usb/gadget/f_loopback.c:123:34: warning: symbol 'ss_loop_source_comp_desc' was not declared. Should it be static? drivers/usb/gadget/f_loopback.c:139:34: warning: symbol 'ss_loop_sink_comp_desc' was not declared. Should it be static? Signed-off-by: Jingoo Han Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_loopback.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/f_loopback.c index c35bb40cabf2..4557cd03f0b1 100644 --- a/drivers/usb/gadget/f_loopback.c +++ b/drivers/usb/gadget/f_loopback.c @@ -120,7 +120,7 @@ static struct usb_endpoint_descriptor ss_loop_source_desc = { .wMaxPacketSize = cpu_to_le16(1024), }; -struct usb_ss_ep_comp_descriptor ss_loop_source_comp_desc = { +static struct usb_ss_ep_comp_descriptor ss_loop_source_comp_desc = { .bLength = USB_DT_SS_EP_COMP_SIZE, .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, .bMaxBurst = 0, @@ -136,7 +136,7 @@ static struct usb_endpoint_descriptor ss_loop_sink_desc = { .wMaxPacketSize = cpu_to_le16(1024), }; -struct usb_ss_ep_comp_descriptor ss_loop_sink_comp_desc = { +static struct usb_ss_ep_comp_descriptor ss_loop_sink_comp_desc = { .bLength = USB_DT_SS_EP_COMP_SIZE, .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, .bMaxBurst = 0, From 6195174ef4704d6e57a246189d65caed704d8613 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 16 Dec 2013 18:42:48 +0900 Subject: [PATCH 067/105] usb: gadget: f_mass_storage: Fix sparse warning Use NULL instead of 0 when returning pointer, to fix the following sparse warnings. drivers/usb/gadget/f_mass_storage.c:3114:60: warning: Using plain integer as NULL pointer drivers/usb/gadget/f_mass_storage.c:3114:63: warning: Using plain integer as NULL pointer Signed-off-by: Jingoo Han Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_mass_storage.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index a03ba2c83589..1dfecdf6146f 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -3111,7 +3111,7 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f) fsg->common->can_stall); if (ret) return ret; - fsg_common_set_inquiry_string(fsg->common, 0, 0); + fsg_common_set_inquiry_string(fsg->common, NULL, NULL); ret = fsg_common_run_thread(fsg->common); if (ret) return ret; From 36a61125484a3a1cc6dc66f3e4e13b25e447cf3e Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 16 Dec 2013 18:43:32 +0900 Subject: [PATCH 068/105] usb: gadget: f_ncm: Fix sparse warning Make local symbol static in order to fix the following sparse warning. drivers/usb/gadget/f_ncm.c:1389:21: warning: symbol 'ncm_alloc' was not declared. Should it be static? Signed-off-by: Jingoo Han Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_ncm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/f_ncm.c b/drivers/usb/gadget/f_ncm.c index 1c28fe13328a..a9499fd30792 100644 --- a/drivers/usb/gadget/f_ncm.c +++ b/drivers/usb/gadget/f_ncm.c @@ -1386,7 +1386,7 @@ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f) usb_ep_free_request(ncm->notify, ncm->notify_req); } -struct usb_function *ncm_alloc(struct usb_function_instance *fi) +static struct usb_function *ncm_alloc(struct usb_function_instance *fi) { struct f_ncm *ncm; struct f_ncm_opts *opts; From 9272fe5a99962915241701f75c0efceae9e736b5 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 16 Dec 2013 18:43:50 +0900 Subject: [PATCH 069/105] usb: gadget: f_obex: Fix sparse warning Make local symbol static in order to fix the following sparse warning. drivers/usb/gadget/f_obex.c:502:21: warning: symbol 'obex_alloc' was not declared. Should it be static? Signed-off-by: Jingoo Han Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_obex.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/f_obex.c b/drivers/usb/gadget/f_obex.c index ad39f1dacba3..aebae1853bce 100644 --- a/drivers/usb/gadget/f_obex.c +++ b/drivers/usb/gadget/f_obex.c @@ -499,7 +499,7 @@ static void obex_unbind(struct usb_configuration *c, struct usb_function *f) usb_free_all_descriptors(f); } -struct usb_function *obex_alloc(struct usb_function_instance *fi) +static struct usb_function *obex_alloc(struct usb_function_instance *fi) { struct f_obex *obex; struct f_serial_opts *opts; From c3af8223d58787fb4279c8140f72873259964476 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 16 Dec 2013 18:44:07 +0900 Subject: [PATCH 070/105] usb: gadget: f_phonet: Fix sparse warning Make local symbol static in order to fix the following sparse warning. drivers/usb/gadget/f_phonet.c:692:21: warning: symbol 'phonet_alloc' was not declared. Should it be static? Signed-off-by: Jingoo Han Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_phonet.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/f_phonet.c b/drivers/usb/gadget/f_phonet.c index eb3aa817a662..f2b781773eed 100644 --- a/drivers/usb/gadget/f_phonet.c +++ b/drivers/usb/gadget/f_phonet.c @@ -689,7 +689,7 @@ static void pn_unbind(struct usb_configuration *c, struct usb_function *f) usb_free_all_descriptors(f); } -struct usb_function *phonet_alloc(struct usb_function_instance *fi) +static struct usb_function *phonet_alloc(struct usb_function_instance *fi) { struct f_phonet *fp; struct f_phonet_opts *opts; From b3d589f27962951ba0f5d028154cb60d6fdadff3 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 16 Dec 2013 18:44:25 +0900 Subject: [PATCH 071/105] usb: gadget: f_serial: Fix sparse warning Make local symbol static in order to fix the following sparse warning. drivers/usb/gadget/f_serial.c:357:21: warning: symbol 'gser_alloc' was not declared. Should it be static? Signed-off-by: Jingoo Han Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_serial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/f_serial.c index 981113c9924d..9ecbcbf36a45 100644 --- a/drivers/usb/gadget/f_serial.c +++ b/drivers/usb/gadget/f_serial.c @@ -354,7 +354,7 @@ static void gser_unbind(struct usb_configuration *c, struct usb_function *f) usb_free_all_descriptors(f); } -struct usb_function *gser_alloc(struct usb_function_instance *fi) +static struct usb_function *gser_alloc(struct usb_function_instance *fi) { struct f_gser *gser; struct f_serial_opts *opts; From 4a5ee77caad2a99b86d6bdd5f0064a60224a0760 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 16 Dec 2013 18:44:43 +0900 Subject: [PATCH 072/105] usb: gadget: f_sourcesink: Fix sparse warning Make local symbols static in order to fix the following sparse warnings. drivers/usb/gadget/f_sourcesink.c:205:34: warning: symbol 'ss_source_comp_desc' was not declared. Should it be static? drivers/usb/gadget/f_sourcesink.c:222:34: warning: symbol 'ss_sink_comp_desc' was not declared. Should it be static? drivers/usb/gadget/f_sourcesink.c:240:34: warning: symbol 'ss_iso_source_comp_desc' was not declared. Should it be static? drivers/usb/gadget/f_sourcesink.c:258:34: warning: symbol 'ss_iso_sink_comp_desc' was not declared. Should it be static? Signed-off-by: Jingoo Han Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_sourcesink.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/f_sourcesink.c index 5d4251e7a377..d3cd52db78fe 100644 --- a/drivers/usb/gadget/f_sourcesink.c +++ b/drivers/usb/gadget/f_sourcesink.c @@ -202,7 +202,7 @@ static struct usb_endpoint_descriptor ss_source_desc = { .wMaxPacketSize = cpu_to_le16(1024), }; -struct usb_ss_ep_comp_descriptor ss_source_comp_desc = { +static struct usb_ss_ep_comp_descriptor ss_source_comp_desc = { .bLength = USB_DT_SS_EP_COMP_SIZE, .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, @@ -219,7 +219,7 @@ static struct usb_endpoint_descriptor ss_sink_desc = { .wMaxPacketSize = cpu_to_le16(1024), }; -struct usb_ss_ep_comp_descriptor ss_sink_comp_desc = { +static struct usb_ss_ep_comp_descriptor ss_sink_comp_desc = { .bLength = USB_DT_SS_EP_COMP_SIZE, .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, @@ -237,7 +237,7 @@ static struct usb_endpoint_descriptor ss_iso_source_desc = { .bInterval = 4, }; -struct usb_ss_ep_comp_descriptor ss_iso_source_comp_desc = { +static struct usb_ss_ep_comp_descriptor ss_iso_source_comp_desc = { .bLength = USB_DT_SS_EP_COMP_SIZE, .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, @@ -255,7 +255,7 @@ static struct usb_endpoint_descriptor ss_iso_sink_desc = { .bInterval = 4, }; -struct usb_ss_ep_comp_descriptor ss_iso_sink_comp_desc = { +static struct usb_ss_ep_comp_descriptor ss_iso_sink_comp_desc = { .bLength = USB_DT_SS_EP_COMP_SIZE, .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, From 2fc711d763520fa4ec2f15b204efc61585817a39 Mon Sep 17 00:00:00 2001 From: George Cherian Date: Wed, 18 Dec 2013 18:52:09 +0530 Subject: [PATCH 073/105] usb: phy: am335x: Enable USB remote wakeup using PHY wakeup USB remote wakeup using PHY wakeup is supported only in standby mode. Enabling the PHY_WKUP will break DS0 mode of system suspend. If the same is enabled while entering DS0, AM33xx never stays in DS0, it returns immediately from DS0. By default make the PHY wakeup disabled, using sysfs entry enable the same manually to get the remote wakeup working from standby. echo enabled > /sys/bus/platform/device//power/wakeup This will enable the PHY wakeup while going to standby. PHY wakeup feature is required to wakeup the system from standby state. Since AM33xx has a bug in which PHY wakeup should not be enabled while entering DS0, disable the same by default. A user wishing to use USB wakeup from standby mode need to enable the same using the sysfs entries. Also remove am335x_phy_runtime_(suspend/resume) this driver doesnot really enable/disable the clocks to the PHY. Add am335x_phy_(suspend/resume) and use the same for enabling the PHY_WKUP. Signed-off-by: George Cherian Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-am335x.c | 46 +++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/drivers/usb/phy/phy-am335x.c b/drivers/usb/phy/phy-am335x.c index 0e3c60cb669a..e8088fa7f05c 100644 --- a/drivers/usb/phy/phy-am335x.c +++ b/drivers/usb/phy/phy-am335x.c @@ -63,6 +63,19 @@ static int am335x_phy_probe(struct platform_device *pdev) am_phy->usb_phy_gen.phy.shutdown = am335x_shutdown; platform_set_drvdata(pdev, am_phy); + device_init_wakeup(dev, true); + + /* + * If we leave PHY wakeup enabled then AM33XX wakes up + * immediately from DS0. To avoid this we mark dev->power.can_wakeup + * to false. The same is checked in suspend routine to decide + * on whether to enable PHY wakeup or not. + * PHY wakeup works fine in standby mode, there by allowing us to + * handle remote wakeup, wakeup on disconnect and connect. + */ + + device_set_wakeup_enable(dev, false); + phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, false); return 0; } @@ -75,40 +88,51 @@ static int am335x_phy_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_RUNTIME - -static int am335x_phy_runtime_suspend(struct device *dev) +#ifdef CONFIG_PM_SLEEP +static int am335x_phy_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct am335x_phy *am_phy = platform_get_drvdata(pdev); + /* + * Enable phy wakeup only if dev->power.can_wakeup is true. + * Make sure to enable wakeup to support remote wakeup in + * standby mode ( same is not supported in OFF(DS0) mode). + * Enable it by doing + * echo enabled > /sys/bus/platform/devices//power/wakeup + */ + if (device_may_wakeup(dev)) phy_ctrl_wkup(am_phy->phy_ctrl, am_phy->id, true); + phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, false); + return 0; } -static int am335x_phy_runtime_resume(struct device *dev) +static int am335x_phy_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct am335x_phy *am_phy = platform_get_drvdata(pdev); phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, true); + if (device_may_wakeup(dev)) phy_ctrl_wkup(am_phy->phy_ctrl, am_phy->id, false); + return 0; } +#define DEV_PM_OPS (&am335x_pm_ops) +#else +#define DEV_PM_OPS NULL +#endif + static const struct dev_pm_ops am335x_pm_ops = { - SET_RUNTIME_PM_OPS(am335x_phy_runtime_suspend, - am335x_phy_runtime_resume, NULL) + .suspend = am335x_phy_suspend, + .resume = am335x_phy_resume, }; -#define DEV_PM_OPS (&am335x_pm_ops) -#else -#define DEV_PM_OPS NULL -#endif - static const struct of_device_id am335x_phy_ids[] = { { .compatible = "ti,am335x-usb-phy" }, { } From 13518673f1419f2667985a6fca4543e44143408b Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 18 Dec 2013 16:41:25 +0200 Subject: [PATCH 074/105] usb: dwc3: fix the glue drivers using the nop phy The reset_gpio member of the usb_phy_gen_xceiv_platform_data structure needs the have negative value or phy-generic's probe will fail unless DT is used. 0 is a valid gpio number. This fixes an issue where phy-generic fails to probe with message: "usb_phy_gen_xceiv.0: Error requesting RESET GPIO 0". Cc: Signed-off-by: Heikki Krogerus Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-exynos.c | 1 + drivers/usb/dwc3/dwc3-pci.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c index 8b20c70d91e7..28c8ad79f5e6 100644 --- a/drivers/usb/dwc3/dwc3-exynos.c +++ b/drivers/usb/dwc3/dwc3-exynos.c @@ -50,6 +50,7 @@ static int dwc3_exynos_register_phys(struct dwc3_exynos *exynos) exynos->usb2_phy = pdev; pdata.type = USB_PHY_TYPE_USB2; + pdata.gpio_reset = -1; ret = platform_device_add_data(exynos->usb2_phy, &pdata, sizeof(pdata)); if (ret) diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 665686e7c8cd..f393c183cc69 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -52,6 +52,7 @@ static int dwc3_pci_register_phys(struct dwc3_pci *glue) glue->usb2_phy = pdev; pdata.type = USB_PHY_TYPE_USB2; + pdata.gpio_reset = -1; ret = platform_device_add_data(glue->usb2_phy, &pdata, sizeof(pdata)); if (ret) From 0009e99ab7f505fefdade85d65b982774d41c06d Mon Sep 17 00:00:00 2001 From: Rashika Kheria Date: Thu, 19 Dec 2013 15:37:37 +0530 Subject: [PATCH 075/105] usb: gadget: configfs: include appropriate header file in configfs.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Include appropriate header file drivers/usb/gadget/configfs.h in gadget/configfs.c because function unregister_gadget_item() has its prototype declaration in gadget/configfs.h. This eliminates the following warning in gadget/configfs.c: drivers/usb/gadget/configfs.c:994:6: warning: no previous prototype for ‘unregister_gadget_item’ [-Wmissing-prototypes] Signed-off-by: Rashika Kheria Reviewed-by: Josh Triplett Signed-off-by: Felipe Balbi --- drivers/usb/gadget/configfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index d6c8ab4a5327..7d1cc01796b6 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -4,6 +4,7 @@ #include #include #include +#include "configfs.h" int check_user_usb_string(const char *name, struct usb_gadget_strings *stringtab_dev) From db67bc04bdc8cac2307af09c92cd73751905ec0e Mon Sep 17 00:00:00 2001 From: Rashika Kheria Date: Thu, 19 Dec 2013 15:44:34 +0530 Subject: [PATCH 076/105] usb: phy: am335x-control: include appropriate header file in phy-am335x-control.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Include header file drivers/usb/phy/am35x-phy-control.h in phy/phy-am335x-control.c because function am335x_get_phy_control() has its prototype declaration in drivers/usb/phy/am35x-phy-control.h. Also, remove definition of structure phy_control because it is already defined in the included header. This eliminates the following warning in phy/phy-am335x-control.c: drivers/usb/phy/phy-am335x-control.c:114:21: warning: no previous prototype for ‘am335x_get_phy_control’ [-Wmissing-prototypes] Signed-off-by: Rashika Kheria Reviewed-by: Josh Triplett Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-am335x-control.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/usb/phy/phy-am335x-control.c b/drivers/usb/phy/phy-am335x-control.c index 634f49acd20e..d75196ad5f2f 100644 --- a/drivers/usb/phy/phy-am335x-control.c +++ b/drivers/usb/phy/phy-am335x-control.c @@ -3,11 +3,7 @@ #include #include #include - -struct phy_control { - void (*phy_power)(struct phy_control *phy_ctrl, u32 id, bool on); - void (*phy_wkup)(struct phy_control *phy_ctrl, u32 id, bool on); -}; +#include "am35x-phy-control.h" struct am335x_control_usb { struct device *dev; From c4b34a3b7a505dd63268cf6dcf57d10068b47cb6 Mon Sep 17 00:00:00 2001 From: George Cherian Date: Tue, 15 Oct 2013 15:32:14 +0530 Subject: [PATCH 077/105] usb: phy: omap: Add omap-control Support for AM437x This adds omap control module support for USBSS in AM437x SoC. Update DT binding information to reflect these changes. Acked-by: Roger Quadros Signed-off-by: George Cherian Signed-off-by: Felipe Balbi --- .../devicetree/bindings/usb/omap-usb.txt | 2 ++ drivers/usb/phy/phy-omap-control.c | 19 +++++++++++++++++++ include/linux/usb/omap_control_usb.h | 6 ++++++ 3 files changed, 27 insertions(+) diff --git a/Documentation/devicetree/bindings/usb/omap-usb.txt b/Documentation/devicetree/bindings/usb/omap-usb.txt index 090e5e22bd2b..c495135115cb 100644 --- a/Documentation/devicetree/bindings/usb/omap-usb.txt +++ b/Documentation/devicetree/bindings/usb/omap-usb.txt @@ -87,6 +87,8 @@ Required properties: e.g. USB3 PHY and SATA PHY on OMAP5. "ti,control-phy-dra7usb2" - if it has power down register like USB2 PHY on DRA7 platform. + "ti,control-phy-am437usb2" - if it has power down register like USB2 PHY on + AM437 platform. - reg : Address and length of the register set for the device. It contains the address of "otghs_control" for control-phy-otghs or "power" register for other types. diff --git a/drivers/usb/phy/phy-omap-control.c b/drivers/usb/phy/phy-omap-control.c index 09c5ace1edd8..e7253182e47d 100644 --- a/drivers/usb/phy/phy-omap-control.c +++ b/drivers/usb/phy/phy-omap-control.c @@ -84,6 +84,20 @@ void omap_control_usb_phy_power(struct device *dev, int on) else val |= OMAP_CTRL_USB2_PHY_PD; break; + + case OMAP_CTRL_TYPE_AM437USB2: + if (on) { + val &= ~(AM437X_CTRL_USB2_PHY_PD | + AM437X_CTRL_USB2_OTG_PD); + val |= (AM437X_CTRL_USB2_OTGVDET_EN | + AM437X_CTRL_USB2_OTGSESSEND_EN); + } else { + val &= ~(AM437X_CTRL_USB2_OTGVDET_EN | + AM437X_CTRL_USB2_OTGSESSEND_EN); + val |= (AM437X_CTRL_USB2_PHY_PD | + AM437X_CTRL_USB2_OTG_PD); + } + break; default: dev_err(dev, "%s: type %d not recognized\n", __func__, control_usb->type); @@ -197,6 +211,7 @@ static const enum omap_control_usb_type otghs_data = OMAP_CTRL_TYPE_OTGHS; static const enum omap_control_usb_type usb2_data = OMAP_CTRL_TYPE_USB2; static const enum omap_control_usb_type pipe3_data = OMAP_CTRL_TYPE_PIPE3; static const enum omap_control_usb_type dra7usb2_data = OMAP_CTRL_TYPE_DRA7USB2; +static const enum omap_control_usb_type am437usb2_data = OMAP_CTRL_TYPE_AM437USB2; static const struct of_device_id omap_control_usb_id_table[] = { { @@ -215,6 +230,10 @@ static const struct of_device_id omap_control_usb_id_table[] = { .compatible = "ti,control-phy-dra7usb2", .data = &dra7usb2_data, }, + { + .compatible = "ti,control-phy-am437usb2", + .data = &am437usb2_data, + }, {}, }; MODULE_DEVICE_TABLE(of, omap_control_usb_id_table); diff --git a/include/linux/usb/omap_control_usb.h b/include/linux/usb/omap_control_usb.h index 596b01918813..69ae383ee3cc 100644 --- a/include/linux/usb/omap_control_usb.h +++ b/include/linux/usb/omap_control_usb.h @@ -24,6 +24,7 @@ enum omap_control_usb_type { OMAP_CTRL_TYPE_USB2, /* USB2_PHY, power down in CONTROL_DEV_CONF */ OMAP_CTRL_TYPE_PIPE3, /* PIPE3 PHY, DPLL & seperate Rx/Tx power */ OMAP_CTRL_TYPE_DRA7USB2, /* USB2 PHY, power and power_aux e.g. DRA7 */ + OMAP_CTRL_TYPE_AM437USB2, /* USB2 PHY, power e.g. AM437x */ }; struct omap_control_usb { @@ -64,6 +65,11 @@ enum omap_control_usb_mode { #define OMAP_CTRL_USB2_PHY_PD BIT(28) +#define AM437X_CTRL_USB2_PHY_PD BIT(0) +#define AM437X_CTRL_USB2_OTG_PD BIT(1) +#define AM437X_CTRL_USB2_OTGVDET_EN BIT(19) +#define AM437X_CTRL_USB2_OTGSESSEND_EN BIT(20) + #if IS_ENABLED(CONFIG_OMAP_CONTROL_USB) extern void omap_control_usb_phy_power(struct device *dev, int on); extern void omap_control_usb_set_mode(struct device *dev, From 56b1b909d7afa5e0415363fafec3df0fc34b95c5 Mon Sep 17 00:00:00 2001 From: "Du, ChangbinX" Date: Tue, 17 Dec 2013 11:47:42 +0000 Subject: [PATCH 078/105] usb: gadget: should use u16 type variable to store MaxPower From 7e827a0d300e084f74c65122baa5e3193f9a7f18 Mon Sep 17 00:00:00 2001 From: "Du, Changbin" Date: Mon, 16 Dec 2013 20:32:13 +0800 Subject: [PATCH] usb/gadget: should use u16 type variable to store MaxPower The MaxPower field is of u16 type. So using u8 type variable can break data (high byte lost). Signed-off-by: Du, Changbin Signed-off-by: Felipe Balbi --- drivers/usb/gadget/composite.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 43cbd76fca06..d742bed7a5fa 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1728,7 +1728,7 @@ composite_resume(struct usb_gadget *gadget) { struct usb_composite_dev *cdev = get_gadget_data(gadget); struct usb_function *f; - u8 maxpower; + u16 maxpower; /* REVISIT: should we have config level * suspend/resume callbacks? From 8ed1fb790ea24bb223e3b30e2b22bccf5b0a76c9 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 18 Dec 2013 20:23:46 +0100 Subject: [PATCH 079/105] usb: musb: finish suspend/reset work independently from musb_hub_control() Currently, resume and reset is completed when the USB core calls back the root hub, asking for the port's state. This results in unpredictable timing of state assertion, which in turn renders some USB devices unusable after resume. Fix this by moving the logic to end the reset and suspend state out of musb_hub_control() into separate functions called from delayed workers. GetPortStatus only reports the current state now, without taking any real action. The rh_timeout variable is kept in order to define a minimum time gap between reset and resume only. FWIW, in my case, a Verbatim "STORE N GO" mass storage device won't resume cleanly without this patch. Signed-off-by: Daniel Mack Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 25 ++++++++++++- drivers/usb/musb/musb_core.h | 3 ++ drivers/usb/musb/musb_host.h | 2 + drivers/usb/musb/musb_virthub.c | 65 +++++++++++++++++---------------- 4 files changed, 61 insertions(+), 34 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 1ecdb94f3812..0e7f4a00ca7b 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -478,8 +478,8 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, musb->port1_status |= (USB_PORT_STAT_C_SUSPEND << 16) | MUSB_PORT_STAT_RESUME; - musb->rh_timer = jiffies - + msecs_to_jiffies(20); + schedule_delayed_work( + &musb->finish_resume_work, 20); musb->xceiv->state = OTG_STATE_A_HOST; musb->is_active = 1; @@ -1813,6 +1813,21 @@ static void musb_free(struct musb *musb) musb_host_free(musb); } +static void musb_deassert_reset(struct work_struct *work) +{ + struct musb *musb; + unsigned long flags; + + musb = container_of(work, struct musb, deassert_reset_work.work); + + spin_lock_irqsave(&musb->lock, flags); + + if (musb->port1_status & USB_PORT_STAT_RESET) + musb_port_reset(musb, false); + + spin_unlock_irqrestore(&musb->lock, flags); +} + /* * Perform generic per-controller initialization. * @@ -1897,6 +1912,8 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) /* Init IRQ workqueue before request_irq */ INIT_WORK(&musb->irq_work, musb_irq_work); + INIT_DELAYED_WORK(&musb->deassert_reset_work, musb_deassert_reset); + INIT_DELAYED_WORK(&musb->finish_resume_work, musb_host_finish_resume); /* setup musb parts of the core (especially endpoints) */ status = musb_core_init(plat->config->multipoint @@ -1990,6 +2007,8 @@ fail4: fail3: cancel_work_sync(&musb->irq_work); + cancel_delayed_work_sync(&musb->finish_resume_work); + cancel_delayed_work_sync(&musb->deassert_reset_work); if (musb->dma_controller) dma_controller_destroy(musb->dma_controller); fail2_5: @@ -2053,6 +2072,8 @@ static int musb_remove(struct platform_device *pdev) dma_controller_destroy(musb->dma_controller); cancel_work_sync(&musb->irq_work); + cancel_delayed_work_sync(&musb->finish_resume_work); + cancel_delayed_work_sync(&musb->deassert_reset_work); musb_free(musb); device_init_wakeup(dev, 0); return 0; diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 29f7cd7c7964..7083e82776ff 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -47,6 +47,7 @@ #include #include #include +#include struct musb; struct musb_hw_ep; @@ -295,6 +296,8 @@ struct musb { irqreturn_t (*isr)(int, void *); struct work_struct irq_work; + struct delayed_work deassert_reset_work; + struct delayed_work finish_resume_work; u16 hwvers; u16 intrrxe; diff --git a/drivers/usb/musb/musb_host.h b/drivers/usb/musb/musb_host.h index 7436c24af0ff..909ba49ef069 100644 --- a/drivers/usb/musb/musb_host.h +++ b/drivers/usb/musb/musb_host.h @@ -94,6 +94,7 @@ extern void musb_host_resume_root_hub(struct musb *musb); extern void musb_host_poke_root_hub(struct musb *musb); extern void musb_port_suspend(struct musb *musb, bool do_suspend); extern void musb_port_reset(struct musb *musb, bool do_reset); +extern void musb_host_finish_resume(struct work_struct *work); #else static inline struct musb *hcd_to_musb(struct usb_hcd *hcd) { @@ -125,6 +126,7 @@ static inline void musb_host_poll_rh_status(struct musb *musb) {} static inline void musb_host_poke_root_hub(struct musb *musb) {} static inline void musb_port_suspend(struct musb *musb, bool do_suspend) {} static inline void musb_port_reset(struct musb *musb) {} +static inline void musb_host_finish_resume(struct work_struct *work) {} #endif struct usb_hcd; diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c index 24e46c0c84b5..ad417fd0e1a7 100644 --- a/drivers/usb/musb/musb_virthub.c +++ b/drivers/usb/musb/musb_virthub.c @@ -44,6 +44,37 @@ #include "musb_core.h" +void musb_host_finish_resume(struct work_struct *work) +{ + struct musb *musb; + unsigned long flags; + u8 power; + + musb = container_of(work, struct musb, deassert_reset_work.work); + + spin_lock_irqsave(&musb->lock, flags); + + power = musb_readb(musb->mregs, MUSB_POWER); + power &= ~MUSB_POWER_RESUME; + dev_dbg(musb->controller, "root port resume stopped, power %02x\n", + power); + musb_writeb(musb->mregs, MUSB_POWER, power); + + /* + * ISSUE: DaVinci (RTL 1.300) disconnects after + * resume of high speed peripherals (but not full + * speed ones). + */ + musb->is_active = 1; + musb->port1_status &= ~(USB_PORT_STAT_SUSPEND | MUSB_PORT_STAT_RESUME); + musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16; + usb_hcd_poll_rh_status(musb->hcd); + /* NOTE: it might really be A_WAIT_BCON ... */ + musb->xceiv->state = OTG_STATE_A_HOST; + + spin_unlock_irqrestore(&musb->lock, flags); +} + void musb_port_suspend(struct musb *musb, bool do_suspend) { struct usb_otg *otg = musb->xceiv->otg; @@ -105,7 +136,7 @@ void musb_port_suspend(struct musb *musb, bool do_suspend) /* later, GetPortStatus will stop RESUME signaling */ musb->port1_status |= MUSB_PORT_STAT_RESUME; - musb->rh_timer = jiffies + msecs_to_jiffies(20); + schedule_delayed_work(&musb->finish_resume_work, 20); } } @@ -150,7 +181,7 @@ void musb_port_reset(struct musb *musb, bool do_reset) musb->port1_status |= USB_PORT_STAT_RESET; musb->port1_status &= ~USB_PORT_STAT_ENABLE; - musb->rh_timer = jiffies + msecs_to_jiffies(50); + schedule_delayed_work(&musb->deassert_reset_work, 50); } else { dev_dbg(musb->controller, "root port reset stopped\n"); musb_writeb(mbase, MUSB_POWER, @@ -325,36 +356,6 @@ int musb_hub_control( if (wIndex != 1) goto error; - /* finish RESET signaling? */ - if ((musb->port1_status & USB_PORT_STAT_RESET) - && time_after_eq(jiffies, musb->rh_timer)) - musb_port_reset(musb, false); - - /* finish RESUME signaling? */ - if ((musb->port1_status & MUSB_PORT_STAT_RESUME) - && time_after_eq(jiffies, musb->rh_timer)) { - u8 power; - - power = musb_readb(musb->mregs, MUSB_POWER); - power &= ~MUSB_POWER_RESUME; - dev_dbg(musb->controller, "root port resume stopped, power %02x\n", - power); - musb_writeb(musb->mregs, MUSB_POWER, power); - - /* ISSUE: DaVinci (RTL 1.300) disconnects after - * resume of high speed peripherals (but not full - * speed ones). - */ - - musb->is_active = 1; - musb->port1_status &= ~(USB_PORT_STAT_SUSPEND - | MUSB_PORT_STAT_RESUME); - musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16; - usb_hcd_poll_rh_status(musb->hcd); - /* NOTE: it might really be A_WAIT_BCON ... */ - musb->xceiv->state = OTG_STATE_A_HOST; - } - put_unaligned(cpu_to_le32(musb->port1_status & ~MUSB_PORT_STAT_RESUME), (__le32 *) buf); From 5b022849c84d426a2a365d916b5015b3bf6ad9db Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 19 Dec 2013 15:43:10 -0800 Subject: [PATCH 080/105] usb: gadget: fix up some comments about CONFIG_USB_DEBUG These two gadget drivers said that their #endif was for CONFIG_USB_DEBUG, but they really were not, so fix them up to be correct. Signed-off-by: Greg Kroah-Hartman Signed-off-by: Felipe Balbi --- drivers/usb/gadget/acm_ms.c | 2 +- drivers/usb/gadget/multi.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/acm_ms.c b/drivers/usb/gadget/acm_ms.c index 7bfa134fe0e3..a252444cc0a7 100644 --- a/drivers/usb/gadget/acm_ms.c +++ b/drivers/usb/gadget/acm_ms.c @@ -107,7 +107,7 @@ static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS; */ #define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS -#endif /* CONFIG_USB_DEBUG */ +#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data); diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c index 4fdaa54a2a2a..940f6cde8e89 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/multi.c @@ -134,7 +134,7 @@ static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS; */ #define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS -#endif /* CONFIG_USB_DEBUG */ +#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data); From 10434d273ccdcee8eab4f5e08497d65841bbf354 Mon Sep 17 00:00:00 2001 From: Apelete Seketeli Date: Thu, 19 Dec 2013 21:42:26 +0100 Subject: [PATCH 081/105] usb: musb: add support for JZ4740 usb device controller Add support for Ingenic JZ4740 USB Device Controller through a specific musb glue layer. JZ4740 UDC not being OTG compatible and missing some hardware registers, this musb glue layer is written from scratch to be used in gadget mode only and take silicon design specifics into account. Signed-off-by: Apelete Seketeli Signed-off-by: Lars-Peter Clausen Signed-off-by: Felipe Balbi --- drivers/usb/musb/Kconfig | 8 +- drivers/usb/musb/Makefile | 1 + drivers/usb/musb/jz4740.c | 201 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 drivers/usb/musb/jz4740.c diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 57dfc0cedb00..14d7e725c2ff 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -93,6 +93,12 @@ config USB_MUSB_BLACKFIN config USB_MUSB_UX500 tristate "Ux500 platforms" +config USB_MUSB_JZ4740 + tristate "JZ4740" + depends on MACH_JZ4740 || COMPILE_TEST + depends on USB_MUSB_GADGET + depends on USB_OTG_BLACKLIST_HUB + endchoice config USB_MUSB_AM335X_CHILD @@ -100,7 +106,7 @@ config USB_MUSB_AM335X_CHILD choice prompt 'MUSB DMA mode' - default MUSB_PIO_ONLY if ARCH_MULTIPLATFORM + default MUSB_PIO_ONLY if ARCH_MULTIPLATFORM || USB_MUSB_JZ4740 default USB_UX500_DMA if USB_MUSB_UX500 default USB_INVENTRA_DMA if USB_MUSB_OMAP2PLUS || USB_MUSB_BLACKFIN default USB_TI_CPPI_DMA if USB_MUSB_DAVINCI diff --git a/drivers/usb/musb/Makefile b/drivers/usb/musb/Makefile index c5ea5c6dc169..ba495018b416 100644 --- a/drivers/usb/musb/Makefile +++ b/drivers/usb/musb/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_USB_MUSB_DAVINCI) += davinci.o obj-$(CONFIG_USB_MUSB_DA8XX) += da8xx.o obj-$(CONFIG_USB_MUSB_BLACKFIN) += blackfin.o obj-$(CONFIG_USB_MUSB_UX500) += ux500.o +obj-$(CONFIG_USB_MUSB_JZ4740) += jz4740.o obj-$(CONFIG_USB_MUSB_AM335X_CHILD) += musb_am335x.o diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c new file mode 100644 index 000000000000..5f30537f1927 --- /dev/null +++ b/drivers/usb/musb/jz4740.c @@ -0,0 +1,201 @@ +/* + * Ingenic JZ4740 "glue layer" + * + * Copyright (C) 2013, Apelete Seketeli + * + * 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. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#include "musb_core.h" + +struct jz4740_glue { + struct device *dev; + struct platform_device *musb; + struct clk *clk; +}; + +static irqreturn_t jz4740_musb_interrupt(int irq, void *__hci) +{ + unsigned long flags; + irqreturn_t retval = IRQ_NONE; + struct musb *musb = __hci; + + spin_lock_irqsave(&musb->lock, flags); + + musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB); + musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX); + musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX); + + /* + * The controller is gadget only, the state of the host mode IRQ bits is + * undefined. Mask them to make sure that the musb driver core will + * never see them set + */ + musb->int_usb &= MUSB_INTR_SUSPEND | MUSB_INTR_RESUME | + MUSB_INTR_RESET | MUSB_INTR_SOF; + + if (musb->int_usb || musb->int_tx || musb->int_rx) + retval = musb_interrupt(musb); + + spin_unlock_irqrestore(&musb->lock, flags); + + return retval; +} + +static struct musb_fifo_cfg jz4740_musb_fifo_cfg[] = { +{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, +{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, +{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 64, }, +}; + +static struct musb_hdrc_config jz4740_musb_config = { + /* Silicon does not implement USB OTG. */ + .multipoint = 0, + /* Max EPs scanned, driver will decide which EP can be used. */ + .num_eps = 4, + /* RAMbits needed to configure EPs from table */ + .ram_bits = 9, + .fifo_cfg = jz4740_musb_fifo_cfg, + .fifo_cfg_size = ARRAY_SIZE(jz4740_musb_fifo_cfg), +}; + +static struct musb_hdrc_platform_data jz4740_musb_platform_data = { + .mode = MUSB_PERIPHERAL, + .config = &jz4740_musb_config, +}; + +static int jz4740_musb_init(struct musb *musb) +{ + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); + if (!musb->xceiv) { + pr_err("HS UDC: no transceiver configured\n"); + return -ENODEV; + } + + /* Silicon does not implement ConfigData register. + * Set dyn_fifo to avoid reading EP config from hardware. + */ + musb->dyn_fifo = true; + + musb->isr = jz4740_musb_interrupt; + + return 0; +} + +static int jz4740_musb_exit(struct musb *musb) +{ + usb_put_phy(musb->xceiv); + + return 0; +} + +static const struct musb_platform_ops jz4740_musb_ops = { + .init = jz4740_musb_init, + .exit = jz4740_musb_exit, +}; + +static int jz4740_probe(struct platform_device *pdev) +{ + struct musb_hdrc_platform_data *pdata = &jz4740_musb_platform_data; + struct platform_device *musb; + struct jz4740_glue *glue; + struct clk *clk; + int ret; + + glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); + if (!glue) + return -ENOMEM; + + musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO); + if (!musb) { + dev_err(&pdev->dev, "failed to allocate musb device\n"); + return -ENOMEM; + } + + clk = devm_clk_get(&pdev->dev, "udc"); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "failed to get clock\n"); + ret = PTR_ERR(clk); + goto err_platform_device_put; + } + + ret = clk_prepare_enable(clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable clock\n"); + goto err_platform_device_put; + } + + musb->dev.parent = &pdev->dev; + + glue->dev = &pdev->dev; + glue->musb = musb; + glue->clk = clk; + + pdata->platform_ops = &jz4740_musb_ops; + + platform_set_drvdata(pdev, glue); + + ret = platform_device_add_resources(musb, pdev->resource, + pdev->num_resources); + if (ret) { + dev_err(&pdev->dev, "failed to add resources\n"); + goto err_clk_disable; + } + + ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); + if (ret) { + dev_err(&pdev->dev, "failed to add platform_data\n"); + goto err_clk_disable; + } + + ret = platform_device_add(musb); + if (ret) { + dev_err(&pdev->dev, "failed to register musb device\n"); + goto err_clk_disable; + } + + return 0; + +err_clk_disable: + clk_disable_unprepare(clk); +err_platform_device_put: + platform_device_put(musb); + return ret; +} + +static int jz4740_remove(struct platform_device *pdev) +{ + struct jz4740_glue *glue = platform_get_drvdata(pdev); + + platform_device_unregister(glue->musb); + clk_disable_unprepare(glue->clk); + + return 0; +} + +static struct platform_driver jz4740_driver = { + .probe = jz4740_probe, + .remove = jz4740_remove, + .driver = { + .name = "musb-jz4740", + }, +}; + +MODULE_DESCRIPTION("JZ4740 MUSB Glue Layer"); +MODULE_AUTHOR("Apelete Seketeli "); +MODULE_LICENSE("GPL v2"); +module_platform_driver(jz4740_driver); From 23db9fd238d6ca5aa165900bf14fb42e3e2d0815 Mon Sep 17 00:00:00 2001 From: Apelete Seketeli Date: Thu, 19 Dec 2013 21:42:27 +0100 Subject: [PATCH 082/105] usb: musb: fix setting JZ4740 gadget periphal mode on reset JZ4740 USB Device Controller is not OTG compatible and does not have DEVCTL register in silicon. During ethernet-over-usb transactions, on reset, musb driver tries to read from DEVCTL and consequently sets device as host (A-Device) instead of peripheral (B-Device), which makes it a composite device to the USB gadget driver. This induces a kernel panic during power down where the USB gadget driver does a null pointer dereference when trying to access the composite device configuration. On reset, do not rely on DEVCTL value for setting gadget peripheral mode. Use is_otg flag instead to set it to B-Device. Signed-off-by: Apelete Seketeli Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_gadget.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index c410a7ff150b..d4aa779339f1 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -2119,7 +2119,15 @@ __acquires(musb->lock) /* Normal reset, as B-Device; * or else after HNP, as A-Device */ - if (devctl & MUSB_DEVCTL_BDEVICE) { + if (!musb->g.is_otg) { + /* USB device controllers that are not OTG compatible + * may not have DEVCTL register in silicon. + * In that case, do not rely on devctl for setting + * peripheral mode. + */ + musb->xceiv->state = OTG_STATE_B_PERIPHERAL; + musb->g.is_a_peripheral = 0; + } else if (devctl & MUSB_DEVCTL_BDEVICE) { musb->xceiv->state = OTG_STATE_B_PERIPHERAL; musb->g.is_a_peripheral = 0; } else { From 515610011edbec518e26b8222a16886cb66a4946 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 20 Dec 2013 15:04:09 -0600 Subject: [PATCH 083/105] usb: phy: am335x: fix randconfig errors by using SET_SYSTEM_SLEEP_PM_OPS, we will make sure that we don't use undefined functions. Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-am335x.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/usb/phy/phy-am335x.c b/drivers/usb/phy/phy-am335x.c index e8088fa7f05c..12fc3468a01e 100644 --- a/drivers/usb/phy/phy-am335x.c +++ b/drivers/usb/phy/phy-am335x.c @@ -122,17 +122,16 @@ static int am335x_phy_resume(struct device *dev) return 0; } + +static const struct dev_pm_ops am335x_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(am335x_phy_suspend, am335x_phy_resume) +}; + #define DEV_PM_OPS (&am335x_pm_ops) #else #define DEV_PM_OPS NULL #endif - -static const struct dev_pm_ops am335x_pm_ops = { - .suspend = am335x_phy_suspend, - .resume = am335x_phy_resume, -}; - static const struct of_device_id am335x_phy_ids[] = { { .compatible = "ti,am335x-usb-phy" }, { } From 071f58b361ef8651e84bb55f7ea5892a77945e5b Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Fri, 20 Dec 2013 10:47:14 +0100 Subject: [PATCH 084/105] usb: musb: fix prototype for musb_port_reset musb_port_reset() takes a 2nd arguments. This didn't hit us yet because this function was never called externally prior to the musb_hub_control cleanup patch. Signed-off-by: Daniel Mack Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_host.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/musb/musb_host.h b/drivers/usb/musb/musb_host.h index 909ba49ef069..7bbf01bf4bb0 100644 --- a/drivers/usb/musb/musb_host.h +++ b/drivers/usb/musb/musb_host.h @@ -125,7 +125,7 @@ static inline void musb_host_resume_root_hub(struct musb *musb) {} static inline void musb_host_poll_rh_status(struct musb *musb) {} static inline void musb_host_poke_root_hub(struct musb *musb) {} static inline void musb_port_suspend(struct musb *musb, bool do_suspend) {} -static inline void musb_port_reset(struct musb *musb) {} +static inline void musb_port_reset(struct musb *musb, bool do_reset) {} static inline void musb_host_finish_resume(struct work_struct *work) {} #endif From fa6997d3a58ba9732870a20364bccb501b641ba9 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Fri, 20 Dec 2013 10:47:15 +0100 Subject: [PATCH 085/105] usb: musb: fix musb pointer acqusition in musb_host_finish_resume This is a fall-out from "usb: musb: finish suspend/reset work independently from musb_hub_control()" that I missed because the MUSB_POWER register does not have the MUSB_POWER_SUSPENDM bit set on AM335x platforms; hence the code path was not travelled in my tests. Signed-off-by: Daniel Mack Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_virthub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c index ad417fd0e1a7..966cf95bb453 100644 --- a/drivers/usb/musb/musb_virthub.c +++ b/drivers/usb/musb/musb_virthub.c @@ -50,7 +50,7 @@ void musb_host_finish_resume(struct work_struct *work) unsigned long flags; u8 power; - musb = container_of(work, struct musb, deassert_reset_work.work); + musb = container_of(work, struct musb, finish_resume_work.work); spin_lock_irqsave(&musb->lock, flags); From 16da4b174b08c42076cd3384c420f352c909d467 Mon Sep 17 00:00:00 2001 From: Anton Tikhomirov Date: Fri, 20 Dec 2013 19:06:24 +0900 Subject: [PATCH 086/105] usb: phy: Fix double lock in OTG FSM Mutex obtained at the beginning of the function should be released at the end to avoid double locking. Signed-off-by: Anton Tikhomirov Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-fsm-usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/phy/phy-fsm-usb.c b/drivers/usb/phy/phy-fsm-usb.c index 62238726fb1c..65c3a728ef4f 100644 --- a/drivers/usb/phy/phy-fsm-usb.c +++ b/drivers/usb/phy/phy-fsm-usb.c @@ -357,7 +357,7 @@ int otg_statemachine(struct otg_fsm *fsm) default: break; } - mutex_lock(&fsm->lock); + mutex_unlock(&fsm->lock); VDBG("quit statemachine, changed = %d\n", state_changed); return state_changed; From ed40082da0a0bfbcab979952d1b6d122cb5fb67b Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Sat, 21 Dec 2013 15:20:07 +0530 Subject: [PATCH 087/105] usb: phy-keystone: Remove redundant of_match_ptr helper 'keystone_usbphy_ids' is always compiled in. Hence the helper macro is not needed. Signed-off-by: Sachin Kamat Cc: WingMan Kwok Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-keystone.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/phy/phy-keystone.c b/drivers/usb/phy/phy-keystone.c index 533db1284ce7..ee1d03b802e1 100644 --- a/drivers/usb/phy/phy-keystone.c +++ b/drivers/usb/phy/phy-keystone.c @@ -129,7 +129,7 @@ static struct platform_driver keystone_usbphy_driver = { .driver = { .name = "keystone-usbphy", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(keystone_usbphy_ids), + .of_match_table = keystone_usbphy_ids, }, }; From 7410f172aeceedd8b3bca6f461d798fc160598e9 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Sat, 21 Dec 2013 15:20:08 +0530 Subject: [PATCH 088/105] usb: phy-fsm: Staticize local symbols Local symbols appearing only in this file are made static. Signed-off-by: Sachin Kamat Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-fsm-usb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/phy/phy-fsm-usb.c b/drivers/usb/phy/phy-fsm-usb.c index 65c3a728ef4f..7aa314ef4a8a 100644 --- a/drivers/usb/phy/phy-fsm-usb.c +++ b/drivers/usb/phy/phy-fsm-usb.c @@ -64,7 +64,7 @@ static int otg_set_protocol(struct otg_fsm *fsm, int protocol) static int state_changed; /* Called when leaving a state. Do state clean up jobs here */ -void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state) +static void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state) { switch (old_state) { case OTG_STATE_B_IDLE: @@ -121,7 +121,7 @@ void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state) } /* Called when entering a state */ -int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) +static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) { state_changed = 1; if (fsm->otg->phy->state == new_state) From 2ee8ff30849def5ac0d942439bf533826ad8e4ba Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Sat, 21 Dec 2013 15:20:09 +0530 Subject: [PATCH 089/105] usb: phy-twl6030: Add missing braces Silences the below warning: WARNING: sizeof *twl should be sizeof(*twl) Signed-off-by: Sachin Kamat Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-twl6030-usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/phy/phy-twl6030-usb.c b/drivers/usb/phy/phy-twl6030-usb.c index 30e8a61552d4..d2682ba58211 100644 --- a/drivers/usb/phy/phy-twl6030-usb.c +++ b/drivers/usb/phy/phy-twl6030-usb.c @@ -327,7 +327,7 @@ static int twl6030_usb_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct twl4030_usb_data *pdata = dev_get_platdata(dev); - twl = devm_kzalloc(dev, sizeof *twl, GFP_KERNEL); + twl = devm_kzalloc(dev, sizeof(*twl), GFP_KERNEL); if (!twl) return -ENOMEM; From 80f46d5dcb0af46f325c897392baef4911f401b7 Mon Sep 17 00:00:00 2001 From: Aaro Koskinen Date: Sat, 21 Dec 2013 20:37:36 +0200 Subject: [PATCH 090/105] usb: phy: tahvo: fix smatch warnings phy-tahvo introduced the following smatch warnings: drivers/usb/phy/phy-tahvo.c:203 tahvo_usb_set_host() warn: variable dereferenced before check 'otg' (see line 199) drivers/usb/phy/phy-tahvo.c:235 tahvo_usb_set_peripheral() warn: variable dereferenced before check 'otg' (see line 231) Fix by deleting bogus NULL pointer checks. The USB framework will always call us with a valid OTG pointer. Reported-by: Dan Carpenter Signed-off-by: Aaro Koskinen Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-tahvo.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/usb/phy/phy-tahvo.c b/drivers/usb/phy/phy-tahvo.c index 8bb833e22d64..cc61ee44b911 100644 --- a/drivers/usb/phy/phy-tahvo.c +++ b/drivers/usb/phy/phy-tahvo.c @@ -200,9 +200,6 @@ static int tahvo_usb_set_host(struct usb_otg *otg, struct usb_bus *host) dev_dbg(&tu->pt_dev->dev, "%s %p\n", __func__, host); - if (otg == NULL) - return -ENODEV; - mutex_lock(&tu->serialize); if (host == NULL) { @@ -232,9 +229,6 @@ static int tahvo_usb_set_peripheral(struct usb_otg *otg, dev_dbg(&tu->pt_dev->dev, "%s %p\n", __func__, gadget); - if (!otg) - return -ENODEV; - mutex_lock(&tu->serialize); if (!gadget) { From 8b42a746e8703c271df3e7f1f5bf5f8f59773d23 Mon Sep 17 00:00:00 2001 From: Aaro Koskinen Date: Sat, 21 Dec 2013 20:37:37 +0200 Subject: [PATCH 091/105] usb: phy: isp1301-omap: fix smatch warnings phy-isp1301-omap produces the following smatch warnings: drivers/usb/phy/phy-isp1301-omap.c:1280 isp1301_set_host() warn: variable dereferenced before check 'otg' (see line 1278) drivers/usb/phy/phy-isp1301-omap.c:1336 isp1301_set_peripheral() warn: variable dereferenced before check 'otg' (see line 1334) drivers/usb/phy/phy-isp1301-omap.c:1417 isp1301_start_srp() warn: variable dereferenced before check 'otg' (see line 1414) drivers/usb/phy/phy-isp1301-omap.c:1445 isp1301_start_hnp() warn: variable dereferenced before check 'otg' (see line 1442) Fix by deleting bogus NULL pointer checks. The USB framework will always call us with a valid OTG pointer. Signed-off-by: Aaro Koskinen Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-isp1301-omap.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/usb/phy/phy-isp1301-omap.c b/drivers/usb/phy/phy-isp1301-omap.c index d3a5160e4cc7..6e146d723b37 100644 --- a/drivers/usb/phy/phy-isp1301-omap.c +++ b/drivers/usb/phy/phy-isp1301-omap.c @@ -1277,7 +1277,7 @@ isp1301_set_host(struct usb_otg *otg, struct usb_bus *host) { struct isp1301 *isp = container_of(otg->phy, struct isp1301, phy); - if (!otg || isp != the_transceiver) + if (isp != the_transceiver) return -ENODEV; if (!host) { @@ -1333,7 +1333,7 @@ isp1301_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget) { struct isp1301 *isp = container_of(otg->phy, struct isp1301, phy); - if (!otg || isp != the_transceiver) + if (isp != the_transceiver) return -ENODEV; if (!gadget) { @@ -1414,8 +1414,7 @@ isp1301_start_srp(struct usb_otg *otg) struct isp1301 *isp = container_of(otg->phy, struct isp1301, phy); u32 otg_ctrl; - if (!otg || isp != the_transceiver - || isp->phy.state != OTG_STATE_B_IDLE) + if (isp != the_transceiver || isp->phy.state != OTG_STATE_B_IDLE) return -ENODEV; otg_ctrl = omap_readl(OTG_CTRL); @@ -1442,7 +1441,7 @@ isp1301_start_hnp(struct usb_otg *otg) struct isp1301 *isp = container_of(otg->phy, struct isp1301, phy); u32 l; - if (!otg || isp != the_transceiver) + if (isp != the_transceiver) return -ENODEV; if (otg->default_a && (otg->host == NULL || !otg->host->b_hnp_enable)) return -ENOTCONN; From 12c8d64e29bb2cbcebe5606aab4d573b4be8606f Mon Sep 17 00:00:00 2001 From: Aaro Koskinen Date: Sat, 21 Dec 2013 20:37:38 +0200 Subject: [PATCH 092/105] usb: phy: fix some Kconfig descriptions Some module names are not up to date in Kconfig help texts. Fix that. Signed-off-by: Aaro Koskinen Signed-off-by: Felipe Balbi --- drivers/usb/phy/Kconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 54bebba39e91..3e9383698c85 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -46,7 +46,7 @@ config ISP1301_OMAP Instruments OMAP processors. This driver can also be built as a module. If so, the module - will be called isp1301_omap. + will be called phy-isp1301-omap. config KEYSTONE_USB_PHY tristate "Keystone USB PHY Driver" @@ -159,7 +159,7 @@ config OMAP_OTG controller is needed to switch between host and peripheral modes. This driver can also be built as a module. If so, the module - will be called omap-otg. + will be called phy-omap-otg. config TAHVO_USB tristate "Tahvo USB transceiver driver" @@ -187,7 +187,7 @@ config USB_ISP1301 and OTG drivers (to be selected separately). To compile this driver as a module, choose M here: the - module will be called isp1301. + module will be called phy-isp1301. config USB_MSM_OTG tristate "OTG support for Qualcomm on-chip USB controller" From 845c071b7853c0046693022f4e95c9cdd043e2db Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Sun, 22 Dec 2013 00:08:33 -0300 Subject: [PATCH 093/105] usb: musb: Rework USB and USB_GADGET dependency This USB controller can work in as host-only, gadget-only or dual-role modes. Rework the dependency on the USB and USB_GADGET configs in order to allow building the driver when !USB or !USG_GADGET. Signed-off-by: Ezequiel Garcia Signed-off-by: Felipe Balbi --- drivers/usb/Kconfig | 4 ++-- drivers/usb/musb/Kconfig | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 2642b8a11e05..a34fb9846417 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -94,8 +94,6 @@ source "drivers/usb/wusbcore/Kconfig" source "drivers/usb/host/Kconfig" -source "drivers/usb/musb/Kconfig" - source "drivers/usb/renesas_usbhs/Kconfig" source "drivers/usb/class/Kconfig" @@ -106,6 +104,8 @@ source "drivers/usb/image/Kconfig" endif +source "drivers/usb/musb/Kconfig" + source "drivers/usb/dwc3/Kconfig" source "drivers/usb/chipidea/Kconfig" diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 14d7e725c2ff..688dc8bb192d 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -6,7 +6,7 @@ # (M)HDRC = (Multipoint) Highspeed Dual-Role Controller config USB_MUSB_HDRC tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)' - depends on USB_GADGET + depends on (USB || USB_GADGET) help Say Y here if your system has a dual role high speed USB controller based on the Mentor Graphics silicon IP. Then @@ -35,21 +35,21 @@ choice config USB_MUSB_HOST bool "Host only mode" - depends on USB + depends on USB=y || USB=USB_MUSB_HDRC help Select this when you want to use MUSB in host mode only, thereby the gadget feature will be regressed. config USB_MUSB_GADGET bool "Gadget only mode" - depends on USB_GADGET + depends on USB_GADGET=y || USB_GADGET=USB_MUSB_HDRC help Select this when you want to use MUSB in gadget mode only, thereby the host feature will be regressed. config USB_MUSB_DUAL_ROLE bool "Dual Role mode" - depends on (USB && USB_GADGET) + depends on ((USB=y || USB=USB_MUSB_HDRC) && (USB_GADGET=y || USB_GADGET=USB_MUSB_HDRC)) help This is the default mode of working of MUSB controller where both host and gadget features are enabled. From 8feed347d33bb630d426b9f2ed88cbdf9795f624 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Thu, 19 Dec 2013 09:23:02 -0500 Subject: [PATCH 094/105] phy: add phy_get_bus_width()/phy_set_bus_width() calls This adds a pair of APIs that allows the generic PHY subsystem to provide information on the PHY bus width. The PHY provider driver may use phy_set_bus_width() to set the bus width that the PHY supports. The controller driver may then use phy_get_bus_width() to fetch the PHY bus width in order to properly configure the controller. Signed-off-by: Matt Porter Acked-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi --- include/linux/phy/phy.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h index 6d722695e027..e273e5ac19c9 100644 --- a/include/linux/phy/phy.h +++ b/include/linux/phy/phy.h @@ -37,6 +37,14 @@ struct phy_ops { struct module *owner; }; +/** + * struct phy_attrs - represents phy attributes + * @bus_width: Data path width implemented by PHY + */ +struct phy_attrs { + u32 bus_width; +}; + /** * struct phy - represents the phy device * @dev: phy device @@ -46,6 +54,7 @@ struct phy_ops { * @mutex: mutex to protect phy_ops * @init_count: used to protect when the PHY is used by multiple consumers * @power_count: used to protect when the PHY is used by multiple consumers + * @phy_attrs: used to specify PHY specific attributes */ struct phy { struct device dev; @@ -55,6 +64,7 @@ struct phy { struct mutex mutex; int init_count; int power_count; + struct phy_attrs attrs; }; /** @@ -127,6 +137,14 @@ int phy_init(struct phy *phy); int phy_exit(struct phy *phy); int phy_power_on(struct phy *phy); int phy_power_off(struct phy *phy); +static inline int phy_get_bus_width(struct phy *phy) +{ + return phy->attrs.bus_width; +} +static inline void phy_set_bus_width(struct phy *phy, int bus_width) +{ + phy->attrs.bus_width = bus_width; +} struct phy *phy_get(struct device *dev, const char *string); struct phy *devm_phy_get(struct device *dev, const char *string); void phy_put(struct phy *phy); @@ -199,6 +217,16 @@ static inline int phy_power_off(struct phy *phy) return -ENOSYS; } +static inline int phy_get_bus_width(struct phy *phy) +{ + return -ENOSYS; +} + +static inline void phy_set_bus_width(struct phy *phy, int bus_width) +{ + return; +} + static inline struct phy *phy_get(struct device *dev, const char *string) { return ERR_PTR(-ENOSYS); From 65b4eb9e351362ff8d41b873989e13a8fd35d13a Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Thu, 19 Dec 2013 09:23:03 -0500 Subject: [PATCH 095/105] staging: dwc2: update DT binding to add generic clock/phy properties dwc2/s3c-hsotg require a single clock to be specified and optionally a generic phy. On the s3c-hsotg driver old style USB phy support is present as a fallback so the generic phy properties are optional. Signed-off-by: Matt Porter Acked-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi --- Documentation/devicetree/bindings/staging/dwc2.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Documentation/devicetree/bindings/staging/dwc2.txt b/Documentation/devicetree/bindings/staging/dwc2.txt index 1a1b7cfa4845..a1753ed0dd84 100644 --- a/Documentation/devicetree/bindings/staging/dwc2.txt +++ b/Documentation/devicetree/bindings/staging/dwc2.txt @@ -5,6 +5,14 @@ Required properties: - compatible : "snps,dwc2" - reg : Should contain 1 register range (address and length) - interrupts : Should contain 1 interrupt +- clocks: clock provider specifier +- clock-names: shall be "otg" +Refer to clk/clock-bindings.txt for generic clock consumer properties + +Optional properties: +- phys: phy provider specifier +- phy-names: shall be "device" +Refer to phy/phy-bindings.txt for generic phy consumer properties Example: @@ -12,4 +20,8 @@ Example: compatible = "ralink,rt3050-usb, snps,dwc2"; reg = <0x101c0000 40000>; interrupts = <18>; + clocks = <&usb_otg_ahb_clk>; + clock-names = "otg"; + phys = <&usbphy>; + phy-names = "usb2-phy"; }; From 29026e09e3d7d4eae19f754772590fdaf9ef8c9d Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Thu, 19 Dec 2013 09:23:04 -0500 Subject: [PATCH 096/105] usb: gadget: s3c-hsotg: enable build for other platforms Remove unused Samsung-specific machine include and Kconfig dependency on S3C. Signed-off-by: Matt Porter Reviewed-by: Markus Mayer Reviewed-by: Tim Kryger Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 8 ++++---- drivers/usb/gadget/s3c-hsotg.c | 2 -- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index ef5075eaaa7a..fbc5607c8cd3 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -294,11 +294,11 @@ config USB_PXA27X gadget drivers to also be dynamically linked. config USB_S3C_HSOTG - tristate "S3C HS/OtG USB Device controller" - depends on S3C_DEV_USB_HSOTG + depends on ARM + tristate "Designware/S3C HS/OtG USB Device controller" help - The Samsung S3C64XX USB2.0 high-speed gadget controller - integrated into the S3C64XX series SoC. + The Designware USB2.0 high-speed gadget controller + integrated into many SoCs. config USB_S3C2410 tristate "S3C2410 USB Device Controller" diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 83bacf4b409b..8917ec3c336e 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -36,8 +36,6 @@ #include #include -#include - #include "s3c-hsotg.h" static const char * const s3c_hsotg_supply_names[] = { From 0d33d825632737c8d3353c80c8701252682bcb03 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Thu, 19 Dec 2013 09:23:05 -0500 Subject: [PATCH 097/105] usb: gadget: s3c-hsotg: add snps,dwc2 compatible string Enable support for the dwc2 binding. Signed-off-by: Matt Porter Signed-off-by: Felipe Balbi --- drivers/usb/gadget/s3c-hsotg.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 8917ec3c336e..50b57b21d130 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -3734,6 +3734,7 @@ static int s3c_hsotg_remove(struct platform_device *pdev) #ifdef CONFIG_OF static const struct of_device_id s3c_hsotg_of_ids[] = { { .compatible = "samsung,s3c6400-hsotg", }, + { .compatible = "snps,dwc2", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, s3c_hsotg_of_ids); From 74084844e8e7d5424e8b512b0ee6d46c72087b56 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Thu, 19 Dec 2013 09:23:06 -0500 Subject: [PATCH 098/105] usb: gadget: s3c-hsotg: enable generic phy support Adds support for the generic PHY subsystem. Generic PHY support is probed and then the driver falls back to checking for an old style USB PHY and pdata if not found. Signed-off-by: Matt Porter Signed-off-by: Felipe Balbi --- drivers/usb/gadget/s3c-hsotg.c | 55 ++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 16 deletions(-) diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 50b57b21d130..f39daf7a7dce 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include #include @@ -138,6 +140,7 @@ struct s3c_hsotg_ep { * @dev: The parent device supplied to the probe function * @driver: USB gadget driver * @phy: The otg phy transceiver structure for phy control. + * @uphy: The otg phy transceiver structure for old USB phy control. * @plat: The platform specific configuration data. This can be removed once * all SoCs support usb transceiver. * @regs: The memory area mapped for accessing registers. @@ -159,7 +162,8 @@ struct s3c_hsotg_ep { struct s3c_hsotg { struct device *dev; struct usb_gadget_driver *driver; - struct usb_phy *phy; + struct phy *phy; + struct usb_phy *uphy; struct s3c_hsotg_plat *plat; spinlock_t lock; @@ -2909,8 +2913,11 @@ static void s3c_hsotg_phy_enable(struct s3c_hsotg *hsotg) dev_dbg(hsotg->dev, "pdev 0x%p\n", pdev); - if (hsotg->phy) - usb_phy_init(hsotg->phy); + if (hsotg->phy) { + phy_init(hsotg->phy); + phy_power_on(hsotg->phy); + } else if (hsotg->uphy) + usb_phy_init(hsotg->uphy); else if (hsotg->plat->phy_init) hsotg->plat->phy_init(pdev, hsotg->plat->phy_type); } @@ -2926,8 +2933,11 @@ static void s3c_hsotg_phy_disable(struct s3c_hsotg *hsotg) { struct platform_device *pdev = to_platform_device(hsotg->dev); - if (hsotg->phy) - usb_phy_shutdown(hsotg->phy); + if (hsotg->phy) { + phy_power_off(hsotg->phy); + phy_exit(hsotg->phy); + } else if (hsotg->uphy) + usb_phy_shutdown(hsotg->uphy); else if (hsotg->plat->phy_exit) hsotg->plat->phy_exit(pdev, hsotg->plat->phy_type); } @@ -3534,7 +3544,8 @@ static void s3c_hsotg_delete_debug(struct s3c_hsotg *hsotg) static int s3c_hsotg_probe(struct platform_device *pdev) { struct s3c_hsotg_plat *plat = dev_get_platdata(&pdev->dev); - struct usb_phy *phy; + struct phy *phy; + struct usb_phy *uphy; struct device *dev = &pdev->dev; struct s3c_hsotg_ep *eps; struct s3c_hsotg *hsotg; @@ -3549,19 +3560,26 @@ static int s3c_hsotg_probe(struct platform_device *pdev) return -ENOMEM; } - phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); + /* + * Attempt to find a generic PHY, then look for an old style + * USB PHY, finally fall back to pdata + */ + phy = devm_phy_get(&pdev->dev, "usb2-phy"); if (IS_ERR(phy)) { - /* Fallback for pdata */ - plat = dev_get_platdata(&pdev->dev); - if (!plat) { - dev_err(&pdev->dev, "no platform data or transceiver defined\n"); - return -EPROBE_DEFER; - } else { + uphy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); + if (IS_ERR(uphy)) { + /* Fallback for pdata */ + plat = dev_get_platdata(&pdev->dev); + if (!plat) { + dev_err(&pdev->dev, + "no platform data or transceiver defined\n"); + return -EPROBE_DEFER; + } hsotg->plat = plat; - } - } else { + } else + hsotg->uphy = uphy; + } else hsotg->phy = phy; - } hsotg->dev = dev; @@ -3628,6 +3646,9 @@ static int s3c_hsotg_probe(struct platform_device *pdev) goto err_supplies; } + if (hsotg->phy) + phy_init(hsotg->phy); + /* usb phy enable */ s3c_hsotg_phy_enable(hsotg); @@ -3721,6 +3742,8 @@ static int s3c_hsotg_remove(struct platform_device *pdev) } s3c_hsotg_phy_disable(hsotg); + if (hsotg->phy) + phy_exit(hsotg->phy); clk_disable_unprepare(hsotg->clk); return 0; From f7e504c72d93d3257969cb1ca9b93116e7dac8b5 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Thu, 19 Dec 2013 09:23:07 -0500 Subject: [PATCH 099/105] usb: gadget: s3c-hsotg: get phy bus width from phy subsystem Adds support for querying the phy bus width from the generic phy subsystem. Configure UTMI bus width in GUSBCFG based on this value. Signed-off-by: Matt Porter Acked-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi --- drivers/usb/gadget/s3c-hsotg.c | 14 +++++++++++++- drivers/usb/gadget/s3c-hsotg.h | 1 + 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index f39daf7a7dce..c0ff1cb91f20 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -146,6 +146,7 @@ struct s3c_hsotg_ep { * @regs: The memory area mapped for accessing registers. * @irq: The IRQ number we are using * @supplies: Definition of USB power supplies + * @phyif: PHY interface width * @dedicated_fifos: Set if the hardware has dedicated IN-EP fifos. * @num_of_eps: Number of available EPs (excluding EP0) * @debug_root: root directrory for debugfs. @@ -174,6 +175,7 @@ struct s3c_hsotg { struct regulator_bulk_data supplies[ARRAY_SIZE(s3c_hsotg_supply_names)]; + u32 phyif; unsigned int dedicated_fifos:1; unsigned char num_of_eps; @@ -2288,7 +2290,7 @@ static void s3c_hsotg_core_init(struct s3c_hsotg *hsotg) */ /* set the PLL on, remove the HNP/SRP and set the PHY */ - writel(GUSBCFG_PHYIf16 | GUSBCFG_TOutCal(7) | + writel(hsotg->phyif | GUSBCFG_TOutCal(7) | (0x5 << 10), hsotg->regs + GUSBCFG); s3c_hsotg_init_fifo(hsotg); @@ -3646,6 +3648,16 @@ static int s3c_hsotg_probe(struct platform_device *pdev) goto err_supplies; } + /* Set default UTMI width */ + hsotg->phyif = GUSBCFG_PHYIf16; + + /* + * If using the generic PHY framework, check if the PHY bus + * width is 8-bit and set the phyif appropriately. + */ + if (hsotg->phy && (phy_get_bus_width(phy) == 8)) + hsotg->phyif = GUSBCFG_PHYIf8; + if (hsotg->phy) phy_init(hsotg->phy); diff --git a/drivers/usb/gadget/s3c-hsotg.h b/drivers/usb/gadget/s3c-hsotg.h index d650b1295831..85f549ff8c1f 100644 --- a/drivers/usb/gadget/s3c-hsotg.h +++ b/drivers/usb/gadget/s3c-hsotg.h @@ -55,6 +55,7 @@ #define GUSBCFG_HNPCap (1 << 9) #define GUSBCFG_SRPCap (1 << 8) #define GUSBCFG_PHYIf16 (1 << 3) +#define GUSBCFG_PHYIf8 (0 << 3) #define GUSBCFG_TOutCal_MASK (0x7 << 0) #define GUSBCFG_TOutCal_SHIFT (0) #define GUSBCFG_TOutCal_LIMIT (0x7) From 26799f1d23d07c2c4db2ccdba43c0e61baeaebee Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Thu, 19 Dec 2013 09:23:08 -0500 Subject: [PATCH 100/105] phy: add Broadcom Kona USB2 PHY DT binding Add a binding that describes the Broadcom Kona USB2 PHY found on the BCM281xx family of SoCs. Signed-off-by: Matt Porter Acked-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi --- Documentation/devicetree/bindings/phy/bcm-phy.txt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/bcm-phy.txt diff --git a/Documentation/devicetree/bindings/phy/bcm-phy.txt b/Documentation/devicetree/bindings/phy/bcm-phy.txt new file mode 100644 index 000000000000..3dc8b3d2ffbb --- /dev/null +++ b/Documentation/devicetree/bindings/phy/bcm-phy.txt @@ -0,0 +1,15 @@ +BROADCOM KONA USB2 PHY + +Required properties: + - compatible: brcm,kona-usb2-phy + - reg: offset and length of the PHY registers + - #phy-cells: must be 0 +Refer to phy/phy-bindings.txt for the generic PHY binding properties + +Example: + + usbphy: usb-phy@3f130000 { + compatible = "brcm,kona-usb2-phy"; + reg = <0x3f130000 0x28>; + #phy-cells = <0>; + }; From 7597fdfca983025a3de91d4b3cf7b21ba452003c Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Thu, 19 Dec 2013 09:23:09 -0500 Subject: [PATCH 101/105] phy: add Broadcom Kona USB2 PHY driver Add a driver for the internal Broadcom Kona USB 2.0 PHY found on the BCM281xx family of SoCs. Signed-off-by: Matt Porter Signed-off-by: Felipe Balbi --- drivers/phy/Kconfig | 6 ++ drivers/phy/Makefile | 1 + drivers/phy/phy-bcm-kona-usb2.c | 158 ++++++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+) create mode 100644 drivers/phy/phy-bcm-kona-usb2.c diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 330ef2d06567..a49e85349d81 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -51,4 +51,10 @@ config PHY_EXYNOS_DP_VIDEO help Support for Display Port PHY found on Samsung EXYNOS SoCs. +config BCM_KONA_USB2_PHY + tristate "Broadcom Kona USB2 PHY Driver" + depends on GENERIC_PHY + help + Enable this to support the Broadcom Kona USB 2.0 PHY. + endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index d0caae9cfb83..c447f1a98ac5 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -3,6 +3,7 @@ # obj-$(CONFIG_GENERIC_PHY) += phy-core.o +obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o diff --git a/drivers/phy/phy-bcm-kona-usb2.c b/drivers/phy/phy-bcm-kona-usb2.c new file mode 100644 index 000000000000..efc5c1a13a5d --- /dev/null +++ b/drivers/phy/phy-bcm-kona-usb2.c @@ -0,0 +1,158 @@ +/* + * phy-bcm-kona-usb2.c - Broadcom Kona USB2 Phy Driver + * + * Copyright (C) 2013 Linaro Limited + * Matt Porter + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define OTGCTL (0) +#define OTGCTL_OTGSTAT2 BIT(31) +#define OTGCTL_OTGSTAT1 BIT(30) +#define OTGCTL_PRST_N_SW BIT(11) +#define OTGCTL_HRESET_N BIT(10) +#define OTGCTL_UTMI_LINE_STATE1 BIT(9) +#define OTGCTL_UTMI_LINE_STATE0 BIT(8) + +#define P1CTL (8) +#define P1CTL_SOFT_RESET BIT(1) +#define P1CTL_NON_DRIVING BIT(0) + +struct bcm_kona_usb { + void __iomem *regs; +}; + +static void bcm_kona_usb_phy_power(struct bcm_kona_usb *phy, int on) +{ + u32 val; + + val = readl(phy->regs + OTGCTL); + if (on) { + /* Configure and power PHY */ + val &= ~(OTGCTL_OTGSTAT2 | OTGCTL_OTGSTAT1 | + OTGCTL_UTMI_LINE_STATE1 | OTGCTL_UTMI_LINE_STATE0); + val |= OTGCTL_PRST_N_SW | OTGCTL_HRESET_N; + } else { + val &= ~(OTGCTL_PRST_N_SW | OTGCTL_HRESET_N); + } + writel(val, phy->regs + OTGCTL); +} + +static int bcm_kona_usb_phy_init(struct phy *gphy) +{ + struct bcm_kona_usb *phy = phy_get_drvdata(gphy); + u32 val; + + /* Soft reset PHY */ + val = readl(phy->regs + P1CTL); + val &= ~P1CTL_NON_DRIVING; + val |= P1CTL_SOFT_RESET; + writel(val, phy->regs + P1CTL); + writel(val & ~P1CTL_SOFT_RESET, phy->regs + P1CTL); + /* Reset needs to be asserted for 2ms */ + mdelay(2); + writel(val | P1CTL_SOFT_RESET, phy->regs + P1CTL); + + return 0; +} + +static int bcm_kona_usb_phy_power_on(struct phy *gphy) +{ + struct bcm_kona_usb *phy = phy_get_drvdata(gphy); + + bcm_kona_usb_phy_power(phy, 1); + + return 0; +} + +static int bcm_kona_usb_phy_power_off(struct phy *gphy) +{ + struct bcm_kona_usb *phy = phy_get_drvdata(gphy); + + bcm_kona_usb_phy_power(phy, 0); + + return 0; +} + +static struct phy_ops ops = { + .init = bcm_kona_usb_phy_init, + .power_on = bcm_kona_usb_phy_power_on, + .power_off = bcm_kona_usb_phy_power_off, + .owner = THIS_MODULE, +}; + +static int bcm_kona_usb2_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct bcm_kona_usb *phy; + struct resource *res; + struct phy *gphy; + struct phy_provider *phy_provider; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + phy->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(phy->regs)) + return PTR_ERR(phy->regs); + + platform_set_drvdata(pdev, phy); + + gphy = devm_phy_create(dev, &ops, NULL); + if (IS_ERR(gphy)) + return PTR_ERR(gphy); + + /* The Kona PHY supports an 8-bit wide UTMI interface */ + phy_set_bus_width(gphy, 8); + + phy_set_drvdata(gphy, phy); + + phy_provider = devm_of_phy_provider_register(dev, + of_phy_simple_xlate); + if (IS_ERR(phy_provider)) + return PTR_ERR(phy_provider); + + return 0; +} + +static const struct of_device_id bcm_kona_usb2_dt_ids[] = { + { .compatible = "brcm,kona-usb2-phy" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, bcm_kona_usb2_dt_ids); + +static struct platform_driver bcm_kona_usb2_driver = { + .probe = bcm_kona_usb2_probe, + .driver = { + .name = "bcm-kona-usb2", + .owner = THIS_MODULE, + .of_match_table = bcm_kona_usb2_dt_ids, + }, +}; + +module_platform_driver(bcm_kona_usb2_driver); + +MODULE_ALIAS("platform:bcm-kona-usb2"); +MODULE_AUTHOR("Matt Porter "); +MODULE_DESCRIPTION("BCM Kona USB 2.0 PHY driver"); +MODULE_LICENSE("GPL v2"); From 27e9dcc924e92239625e670e269688ccbccbf777 Mon Sep 17 00:00:00 2001 From: Andreas Larsson Date: Mon, 23 Dec 2013 21:25:49 +0100 Subject: [PATCH 102/105] usb: gadget: Add UDC driver for Aeroflex Gaisler GRUSBDC This adds an UDC driver for GRUSBDC USB Device Controller cores available in the GRLIB VHDL IP core library. The driver only supports DMA mode. Signed-off-by: Andreas Larsson Signed-off-by: Felipe Balbi --- .../devicetree/bindings/usb/gr-udc.txt | 28 + drivers/usb/gadget/Kconfig | 7 + drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/gr_udc.c | 2242 +++++++++++++++++ drivers/usb/gadget/gr_udc.h | 220 ++ 5 files changed, 2498 insertions(+) create mode 100644 Documentation/devicetree/bindings/usb/gr-udc.txt create mode 100644 drivers/usb/gadget/gr_udc.c create mode 100644 drivers/usb/gadget/gr_udc.h diff --git a/Documentation/devicetree/bindings/usb/gr-udc.txt b/Documentation/devicetree/bindings/usb/gr-udc.txt new file mode 100644 index 000000000000..0c5118f7a916 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/gr-udc.txt @@ -0,0 +1,28 @@ +USB Peripheral Controller driver for Aeroflex Gaisler GRUSBDC. + +The GRUSBDC USB Device Controller core is available in the GRLIB VHDL +IP core library. + +Note: In the ordinary environment for the core, a Leon SPARC system, +these properties are built from information in the AMBA plug&play. + +Required properties: + +- name : Should be "GAISLER_USBDC" or "01_021" + +- reg : Address and length of the register set for the device + +- interrupts : Interrupt numbers for this device + +Optional properties: + +- epobufsizes : An array of buffer sizes for OUT endpoints. If the property is + not present, or for endpoints outside of the array, 1024 is assumed by + the driver. + +- epibufsizes : An array of buffer sizes for IN endpoints. If the property is + not present, or for endpoints outside of the array, 1024 is assumed by + the driver. + +For further information look in the documentation for the GLIB IP core library: +http://www.gaisler.com/products/grlib/grip.pdf diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index fbc5607c8cd3..8154165aa601 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -216,6 +216,13 @@ config USB_FOTG210_UDC Say "y" to link the driver statically, or "m" to build a dynamically linked module called "fotg210_udc". +config USB_GR_UDC + tristate "Aeroflex Gaisler GRUSBDC USB Peripheral Controller Driver" + depends on HAS_DMA + help + Select this to support Aeroflex Gaisler GRUSBDC cores from the GRLIB + VHDL IP core library. + config USB_OMAP tristate "OMAP USB Device Controller" depends on ARCH_OMAP1 diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 6cccdfed140c..5f150bc1b4bc 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -35,6 +35,7 @@ mv_udc-y := mv_udc_core.o obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o +obj-$(CONFIG_USB_GR_UDC) += gr_udc.o # USB Functions usb_f_acm-y := f_acm.o diff --git a/drivers/usb/gadget/gr_udc.c b/drivers/usb/gadget/gr_udc.c new file mode 100644 index 000000000000..5f9c65959dd2 --- /dev/null +++ b/drivers/usb/gadget/gr_udc.c @@ -0,0 +1,2242 @@ +/* + * USB Peripheral Controller driver for Aeroflex Gaisler GRUSBDC. + * + * 2013 (c) Aeroflex Gaisler AB + * + * This driver supports GRUSBDC USB Device Controller cores available in the + * GRLIB VHDL IP core library. + * + * Full documentation of the GRUSBDC core can be found here: + * http://www.gaisler.com/products/grlib/grip.pdf + * + * 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. + * + * Contributors: + * - Andreas Larsson + * - Marko Isomaki + */ + +/* + * A GRUSBDC core can have up to 16 IN endpoints and 16 OUT endpoints each + * individually configurable to any of the four USB transfer types. This driver + * only supports cores in DMA mode. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "gr_udc.h" + +#define DRIVER_NAME "gr_udc" +#define DRIVER_DESC "Aeroflex Gaisler GRUSBDC USB Peripheral Controller" + +static const char driver_name[] = DRIVER_NAME; +static const char driver_desc[] = DRIVER_DESC; + +#define gr_read32(x) (ioread32be((x))) +#define gr_write32(x, v) (iowrite32be((v), (x))) + +/* USB speed and corresponding string calculated from status register value */ +#define GR_SPEED(status) \ + ((status & GR_STATUS_SP) ? USB_SPEED_FULL : USB_SPEED_HIGH) +#define GR_SPEED_STR(status) usb_speed_string(GR_SPEED(status)) + +/* Size of hardware buffer calculated from epctrl register value */ +#define GR_BUFFER_SIZE(epctrl) \ + ((((epctrl) & GR_EPCTRL_BUFSZ_MASK) >> GR_EPCTRL_BUFSZ_POS) * \ + GR_EPCTRL_BUFSZ_SCALER) + +/* ---------------------------------------------------------------------- */ +/* Debug printout functionality */ + +static const char * const gr_modestring[] = {"control", "iso", "bulk", "int"}; + +static const char *gr_ep0state_string(enum gr_ep0state state) +{ + static const char *const names[] = { + [GR_EP0_DISCONNECT] = "disconnect", + [GR_EP0_SETUP] = "setup", + [GR_EP0_IDATA] = "idata", + [GR_EP0_ODATA] = "odata", + [GR_EP0_ISTATUS] = "istatus", + [GR_EP0_OSTATUS] = "ostatus", + [GR_EP0_STALL] = "stall", + [GR_EP0_SUSPEND] = "suspend", + }; + + if (state < 0 || state >= ARRAY_SIZE(names)) + return "UNKNOWN"; + + return names[state]; +} + +#ifdef VERBOSE_DEBUG + +static void gr_dbgprint_request(const char *str, struct gr_ep *ep, + struct gr_request *req) +{ + int buflen = ep->is_in ? req->req.length : req->req.actual; + int rowlen = 32; + int plen = min(rowlen, buflen); + + dev_dbg(ep->dev->dev, "%s: 0x%p, %d bytes data%s:\n", str, req, buflen, + (buflen > plen ? " (truncated)" : "")); + print_hex_dump_debug(" ", DUMP_PREFIX_NONE, + rowlen, 4, req->req.buf, plen, false); +} + +static void gr_dbgprint_devreq(struct gr_udc *dev, u8 type, u8 request, + u16 value, u16 index, u16 length) +{ + dev_vdbg(dev->dev, "REQ: %02x.%02x v%04x i%04x l%04x\n", + type, request, value, index, length); +} +#else /* !VERBOSE_DEBUG */ + +static void gr_dbgprint_request(const char *str, struct gr_ep *ep, + struct gr_request *req) {} + +static void gr_dbgprint_devreq(struct gr_udc *dev, u8 type, u8 request, + u16 value, u16 index, u16 length) {} + +#endif /* VERBOSE_DEBUG */ + +/* ---------------------------------------------------------------------- */ +/* Debugfs functionality */ + +#ifdef CONFIG_USB_GADGET_DEBUG_FS + +static void gr_seq_ep_show(struct seq_file *seq, struct gr_ep *ep) +{ + u32 epctrl = gr_read32(&ep->regs->epctrl); + u32 epstat = gr_read32(&ep->regs->epstat); + int mode = (epctrl & GR_EPCTRL_TT_MASK) >> GR_EPCTRL_TT_POS; + struct gr_request *req; + + seq_printf(seq, "%s:\n", ep->ep.name); + seq_printf(seq, " mode = %s\n", gr_modestring[mode]); + seq_printf(seq, " halted: %d\n", !!(epctrl & GR_EPCTRL_EH)); + seq_printf(seq, " disabled: %d\n", !!(epctrl & GR_EPCTRL_ED)); + seq_printf(seq, " valid: %d\n", !!(epctrl & GR_EPCTRL_EV)); + seq_printf(seq, " dma_start = %d\n", ep->dma_start); + seq_printf(seq, " stopped = %d\n", ep->stopped); + seq_printf(seq, " wedged = %d\n", ep->wedged); + seq_printf(seq, " callback = %d\n", ep->callback); + seq_printf(seq, " maxpacket = %d\n", ep->ep.maxpacket); + seq_printf(seq, " bytes_per_buffer = %d\n", ep->bytes_per_buffer); + if (mode == 1 || mode == 3) + seq_printf(seq, " nt = %d\n", + (epctrl & GR_EPCTRL_NT_MASK) >> GR_EPCTRL_NT_POS); + + seq_printf(seq, " Buffer 0: %s %s%d\n", + epstat & GR_EPSTAT_B0 ? "valid" : "invalid", + epstat & GR_EPSTAT_BS ? " " : "selected ", + (epstat & GR_EPSTAT_B0CNT_MASK) >> GR_EPSTAT_B0CNT_POS); + seq_printf(seq, " Buffer 1: %s %s%d\n", + epstat & GR_EPSTAT_B1 ? "valid" : "invalid", + epstat & GR_EPSTAT_BS ? "selected " : " ", + (epstat & GR_EPSTAT_B1CNT_MASK) >> GR_EPSTAT_B1CNT_POS); + + if (list_empty(&ep->queue)) { + seq_puts(seq, " Queue: empty\n\n"); + return; + } + + seq_puts(seq, " Queue:\n"); + list_for_each_entry(req, &ep->queue, queue) { + struct gr_dma_desc *desc; + struct gr_dma_desc *next; + + seq_printf(seq, " 0x%p: 0x%p %d %d\n", req, + &req->req.buf, req->req.actual, req->req.length); + + next = req->first_desc; + do { + desc = next; + next = desc->next_desc; + seq_printf(seq, " %c 0x%p (0x%08x): 0x%05x 0x%08x\n", + desc == req->curr_desc ? 'c' : ' ', + desc, desc->paddr, desc->ctrl, desc->data); + } while (desc != req->last_desc); + } + seq_puts(seq, "\n"); +} + + +static int gr_seq_show(struct seq_file *seq, void *v) +{ + struct gr_udc *dev = seq->private; + u32 control = gr_read32(&dev->regs->control); + u32 status = gr_read32(&dev->regs->status); + struct gr_ep *ep; + + seq_printf(seq, "usb state = %s\n", + usb_state_string(dev->gadget.state)); + seq_printf(seq, "address = %d\n", + (control & GR_CONTROL_UA_MASK) >> GR_CONTROL_UA_POS); + seq_printf(seq, "speed = %s\n", GR_SPEED_STR(status)); + seq_printf(seq, "ep0state = %s\n", gr_ep0state_string(dev->ep0state)); + seq_printf(seq, "irq_enabled = %d\n", dev->irq_enabled); + seq_printf(seq, "remote_wakeup = %d\n", dev->remote_wakeup); + seq_printf(seq, "test_mode = %d\n", dev->test_mode); + seq_puts(seq, "\n"); + + list_for_each_entry(ep, &dev->ep_list, ep_list) + gr_seq_ep_show(seq, ep); + + return 0; +} + +static int gr_dfs_open(struct inode *inode, struct file *file) +{ + return single_open(file, gr_seq_show, inode->i_private); +} + +static const struct file_operations gr_dfs_fops = { + .owner = THIS_MODULE, + .open = gr_dfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void gr_dfs_create(struct gr_udc *dev) +{ + const char *name = "gr_udc_state"; + + dev->dfs_root = debugfs_create_dir(dev_name(dev->dev), NULL); + if (IS_ERR(dev->dfs_root)) { + dev_err(dev->dev, "Failed to create debugfs directory\n"); + return; + } + dev->dfs_state = debugfs_create_file(name, 0444, dev->dfs_root, + dev, &gr_dfs_fops); + if (IS_ERR(dev->dfs_state)) + dev_err(dev->dev, "Failed to create debugfs file %s\n", name); +} + +static void gr_dfs_delete(struct gr_udc *dev) +{ + /* Handles NULL and ERR pointers internally */ + debugfs_remove(dev->dfs_state); + debugfs_remove(dev->dfs_root); +} + +#else /* !CONFIG_USB_GADGET_DEBUG_FS */ + +static void gr_dfs_create(struct gr_udc *dev) {} +static void gr_dfs_delete(struct gr_udc *dev) {} + +#endif /* CONFIG_USB_GADGET_DEBUG_FS */ + +/* ---------------------------------------------------------------------- */ +/* DMA and request handling */ + +/* Allocates a new struct gr_dma_desc, sets paddr and zeroes the rest */ +static struct gr_dma_desc *gr_alloc_dma_desc(struct gr_ep *ep, gfp_t gfp_flags) +{ + dma_addr_t paddr; + struct gr_dma_desc *dma_desc; + + dma_desc = dma_pool_alloc(ep->dev->desc_pool, gfp_flags, &paddr); + if (!dma_desc) { + dev_err(ep->dev->dev, "Could not allocate from DMA pool\n"); + return NULL; + } + + memset(dma_desc, 0, sizeof(*dma_desc)); + dma_desc->paddr = paddr; + + return dma_desc; +} + +static inline void gr_free_dma_desc(struct gr_udc *dev, + struct gr_dma_desc *desc) +{ + dma_pool_free(dev->desc_pool, desc, (dma_addr_t)desc->paddr); +} + +/* Frees the chain of struct gr_dma_desc for the given request */ +static void gr_free_dma_desc_chain(struct gr_udc *dev, struct gr_request *req) +{ + struct gr_dma_desc *desc; + struct gr_dma_desc *next; + + next = req->first_desc; + if (!next) + return; + + do { + desc = next; + next = desc->next_desc; + gr_free_dma_desc(dev, desc); + } while (desc != req->last_desc); + + req->first_desc = NULL; + req->curr_desc = NULL; + req->last_desc = NULL; +} + +static void gr_ep0_setup(struct gr_udc *dev, struct gr_request *req); + +/* + * Frees allocated resources and calls the appropriate completion function/setup + * package handler for a finished request. + * + * Must be called with dev->lock held and irqs disabled. + */ +static void gr_finish_request(struct gr_ep *ep, struct gr_request *req, + int status) + __releases(&dev->lock) + __acquires(&dev->lock) +{ + struct gr_udc *dev; + + list_del_init(&req->queue); + + if (likely(req->req.status == -EINPROGRESS)) + req->req.status = status; + else + status = req->req.status; + + dev = ep->dev; + usb_gadget_unmap_request(&dev->gadget, &req->req, ep->is_in); + gr_free_dma_desc_chain(dev, req); + + if (ep->is_in) /* For OUT, actual gets updated bit by bit */ + req->req.actual = req->req.length; + + if (!status) { + if (ep->is_in) + gr_dbgprint_request("SENT", ep, req); + else + gr_dbgprint_request("RECV", ep, req); + } + + /* Prevent changes to ep->queue during callback */ + ep->callback = 1; + if (req == dev->ep0reqo && !status) { + if (req->setup) + gr_ep0_setup(dev, req); + else + dev_err(dev->dev, + "Unexpected non setup packet on ep0in\n"); + } else if (req->req.complete) { + spin_unlock(&dev->lock); + + req->req.complete(&ep->ep, &req->req); + + spin_lock(&dev->lock); + } + ep->callback = 0; +} + +static struct usb_request *gr_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) +{ + struct gr_request *req; + + req = kzalloc(sizeof(*req), gfp_flags); + if (!req) + return NULL; + + INIT_LIST_HEAD(&req->queue); + + return &req->req; +} + +/* + * Starts DMA for endpoint ep if there are requests in the queue. + * + * Must be called with dev->lock held and with !ep->stopped. + */ +static void gr_start_dma(struct gr_ep *ep) +{ + struct gr_request *req; + u32 dmactrl; + + if (list_empty(&ep->queue)) { + ep->dma_start = 0; + return; + } + + req = list_first_entry(&ep->queue, struct gr_request, queue); + + /* A descriptor should already have been allocated */ + BUG_ON(!req->curr_desc); + + wmb(); /* Make sure all is settled before handing it over to DMA */ + + /* Set the descriptor pointer in the hardware */ + gr_write32(&ep->regs->dmaaddr, req->curr_desc->paddr); + + /* Announce available descriptors */ + dmactrl = gr_read32(&ep->regs->dmactrl); + gr_write32(&ep->regs->dmactrl, dmactrl | GR_DMACTRL_DA); + + ep->dma_start = 1; +} + +/* + * Finishes the first request in the ep's queue and, if available, starts the + * next request in queue. + * + * Must be called with dev->lock held, irqs disabled and with !ep->stopped. + */ +static void gr_dma_advance(struct gr_ep *ep, int status) +{ + struct gr_request *req; + + req = list_first_entry(&ep->queue, struct gr_request, queue); + gr_finish_request(ep, req, status); + gr_start_dma(ep); /* Regardless of ep->dma_start */ +} + +/* + * Abort DMA for an endpoint. Sets the abort DMA bit which causes an ongoing DMA + * transfer to be canceled and clears GR_DMACTRL_DA. + * + * Must be called with dev->lock held. + */ +static void gr_abort_dma(struct gr_ep *ep) +{ + u32 dmactrl; + + dmactrl = gr_read32(&ep->regs->dmactrl); + gr_write32(&ep->regs->dmactrl, dmactrl | GR_DMACTRL_AD); +} + +/* + * Allocates and sets up a struct gr_dma_desc and putting it on the descriptor + * chain. + * + * Size is not used for OUT endpoints. Hardware can not be instructed to handle + * smaller buffer than MAXPL in the OUT direction. + */ +static int gr_add_dma_desc(struct gr_ep *ep, struct gr_request *req, + dma_addr_t data, unsigned size, gfp_t gfp_flags) +{ + struct gr_dma_desc *desc; + + desc = gr_alloc_dma_desc(ep, gfp_flags); + if (!desc) + return -ENOMEM; + + desc->data = data; + if (ep->is_in) + desc->ctrl = + (GR_DESC_IN_CTRL_LEN_MASK & size) | GR_DESC_IN_CTRL_EN; + else + desc->ctrl = GR_DESC_OUT_CTRL_IE; + + if (!req->first_desc) { + req->first_desc = desc; + req->curr_desc = desc; + } else { + req->last_desc->next_desc = desc; + req->last_desc->next = desc->paddr; + req->last_desc->ctrl |= GR_DESC_OUT_CTRL_NX; + } + req->last_desc = desc; + + return 0; +} + +/* + * Sets up a chain of struct gr_dma_descriptors pointing to buffers that + * together covers req->req.length bytes of the buffer at DMA address + * req->req.dma for the OUT direction. + * + * The first descriptor in the chain is enabled, the rest disabled. The + * interrupt handler will later enable them one by one when needed so we can + * find out when the transfer is finished. For OUT endpoints, all descriptors + * therefore generate interrutps. + */ +static int gr_setup_out_desc_list(struct gr_ep *ep, struct gr_request *req, + gfp_t gfp_flags) +{ + u16 bytes_left; /* Bytes left to provide descriptors for */ + u16 bytes_used; /* Bytes accommodated for */ + int ret = 0; + + req->first_desc = NULL; /* Signals that no allocation is done yet */ + bytes_left = req->req.length; + bytes_used = 0; + while (bytes_left > 0) { + dma_addr_t start = req->req.dma + bytes_used; + u16 size = min(bytes_left, ep->bytes_per_buffer); + + /* Should not happen however - gr_queue stops such lengths */ + if (size < ep->bytes_per_buffer) + dev_warn(ep->dev->dev, + "Buffer overrun risk: %u < %u bytes/buffer\n", + size, ep->bytes_per_buffer); + + ret = gr_add_dma_desc(ep, req, start, size, gfp_flags); + if (ret) + goto alloc_err; + + bytes_left -= size; + bytes_used += size; + } + + req->first_desc->ctrl |= GR_DESC_OUT_CTRL_EN; + + return 0; + +alloc_err: + gr_free_dma_desc_chain(ep->dev, req); + + return ret; +} + +/* + * Sets up a chain of struct gr_dma_descriptors pointing to buffers that + * together covers req->req.length bytes of the buffer at DMA address + * req->req.dma for the IN direction. + * + * When more data is provided than the maximum payload size, the hardware splits + * this up into several payloads automatically. Moreover, ep->bytes_per_buffer + * is always set to a multiple of the maximum payload (restricted to the valid + * number of maximum payloads during high bandwidth isochronous or interrupt + * transfers) + * + * All descriptors are enabled from the beginning and we only generate an + * interrupt for the last one indicating that the entire request has been pushed + * to hardware. + */ +static int gr_setup_in_desc_list(struct gr_ep *ep, struct gr_request *req, + gfp_t gfp_flags) +{ + u16 bytes_left; /* Bytes left in req to provide descriptors for */ + u16 bytes_used; /* Bytes in req accommodated for */ + int ret = 0; + + req->first_desc = NULL; /* Signals that no allocation is done yet */ + bytes_left = req->req.length; + bytes_used = 0; + do { /* Allow for zero length packets */ + dma_addr_t start = req->req.dma + bytes_used; + u16 size = min(bytes_left, ep->bytes_per_buffer); + + ret = gr_add_dma_desc(ep, req, start, size, gfp_flags); + if (ret) + goto alloc_err; + + bytes_left -= size; + bytes_used += size; + } while (bytes_left > 0); + + /* + * Send an extra zero length packet to indicate that no more data is + * available when req->req.zero is set and the data length is even + * multiples of ep->ep.maxpacket. + */ + if (req->req.zero && (req->req.length % ep->ep.maxpacket == 0)) { + ret = gr_add_dma_desc(ep, req, 0, 0, gfp_flags); + if (ret) + goto alloc_err; + } + + /* + * For IN packets we only want to know when the last packet has been + * transmitted (not just put into internal buffers). + */ + req->last_desc->ctrl |= GR_DESC_IN_CTRL_PI; + + return 0; + +alloc_err: + gr_free_dma_desc_chain(ep->dev, req); + + return ret; +} + +/* Must be called with dev->lock held */ +static int gr_queue(struct gr_ep *ep, struct gr_request *req, gfp_t gfp_flags) +{ + struct gr_udc *dev = ep->dev; + int ret; + + if (unlikely(!ep->ep.desc && ep->num != 0)) { + dev_err(dev->dev, "No ep descriptor for %s\n", ep->ep.name); + return -EINVAL; + } + + if (unlikely(!req->req.buf || !list_empty(&req->queue))) { + dev_err(dev->dev, + "Invalid request for %s: buf=%p list_empty=%d\n", + ep->ep.name, req->req.buf, list_empty(&req->queue)); + return -EINVAL; + } + + /* + * The DMA controller can not handle smaller OUT buffers than + * maxpacket. It could lead to buffer overruns if unexpectedly long + * packet are received. + */ + if (!ep->is_in && (req->req.length % ep->ep.maxpacket) != 0) { + dev_err(dev->dev, + "OUT request length %d is not multiple of maxpacket\n", + req->req.length); + return -EMSGSIZE; + } + + if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) { + dev_err(dev->dev, "-ESHUTDOWN"); + return -ESHUTDOWN; + } + + /* Can't touch registers when suspended */ + if (dev->ep0state == GR_EP0_SUSPEND) { + dev_err(dev->dev, "-EBUSY"); + return -EBUSY; + } + + /* Set up DMA mapping in case the caller didn't */ + ret = usb_gadget_map_request(&dev->gadget, &req->req, ep->is_in); + if (ret) { + dev_err(dev->dev, "usb_gadget_map_request"); + return ret; + } + + if (ep->is_in) + ret = gr_setup_in_desc_list(ep, req, gfp_flags); + else + ret = gr_setup_out_desc_list(ep, req, gfp_flags); + if (ret) + return ret; + + req->req.status = -EINPROGRESS; + req->req.actual = 0; + list_add_tail(&req->queue, &ep->queue); + + /* Start DMA if not started, otherwise interrupt handler handles it */ + if (!ep->dma_start && likely(!ep->stopped)) + gr_start_dma(ep); + + return 0; +} + +/* + * Queue a request from within the driver. + * + * Must be called with dev->lock held. + */ +static inline int gr_queue_int(struct gr_ep *ep, struct gr_request *req, + gfp_t gfp_flags) +{ + if (ep->is_in) + gr_dbgprint_request("RESP", ep, req); + + return gr_queue(ep, req, gfp_flags); +} + +/* ---------------------------------------------------------------------- */ +/* General helper functions */ + +/* + * Dequeue ALL requests. + * + * Must be called with dev->lock held and irqs disabled. + */ +static void gr_ep_nuke(struct gr_ep *ep) +{ + struct gr_request *req; + struct gr_udc *dev; + + dev = ep->dev; + + ep->stopped = 1; + ep->dma_start = 0; + gr_abort_dma(ep); + + while (!list_empty(&ep->queue)) { + req = list_first_entry(&ep->queue, struct gr_request, queue); + gr_finish_request(ep, req, -ESHUTDOWN); + } +} + +/* + * Reset the hardware state of this endpoint. + * + * Must be called with dev->lock held. + */ +static void gr_ep_reset(struct gr_ep *ep) +{ + gr_write32(&ep->regs->epctrl, 0); + gr_write32(&ep->regs->dmactrl, 0); + + ep->ep.maxpacket = MAX_CTRL_PL_SIZE; + ep->ep.desc = NULL; + ep->stopped = 1; + ep->dma_start = 0; +} + +/* + * Generate STALL on ep0in/out. + * + * Must be called with dev->lock held. + */ +static void gr_control_stall(struct gr_udc *dev) +{ + u32 epctrl; + + epctrl = gr_read32(&dev->epo[0].regs->epctrl); + gr_write32(&dev->epo[0].regs->epctrl, epctrl | GR_EPCTRL_CS); + epctrl = gr_read32(&dev->epi[0].regs->epctrl); + gr_write32(&dev->epi[0].regs->epctrl, epctrl | GR_EPCTRL_CS); + + dev->ep0state = GR_EP0_STALL; +} + +/* + * Halts, halts and wedges, or clears halt for an endpoint. + * + * Must be called with dev->lock held. + */ +static int gr_ep_halt_wedge(struct gr_ep *ep, int halt, int wedge, int fromhost) +{ + u32 epctrl; + int retval = 0; + + if (ep->num && !ep->ep.desc) + return -EINVAL; + + if (ep->num && ep->ep.desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) + return -EOPNOTSUPP; + + /* Never actually halt ep0, and therefore never clear halt for ep0 */ + if (!ep->num) { + if (halt && !fromhost) { + /* ep0 halt from gadget - generate protocol stall */ + gr_control_stall(ep->dev); + dev_dbg(ep->dev->dev, "EP: stall ep0\n"); + return 0; + } + return -EINVAL; + } + + dev_dbg(ep->dev->dev, "EP: %s halt %s\n", + (halt ? (wedge ? "wedge" : "set") : "clear"), ep->ep.name); + + epctrl = gr_read32(&ep->regs->epctrl); + if (halt) { + /* Set HALT */ + gr_write32(&ep->regs->epctrl, epctrl | GR_EPCTRL_EH); + ep->stopped = 1; + if (wedge) + ep->wedged = 1; + } else { + gr_write32(&ep->regs->epctrl, epctrl & ~GR_EPCTRL_EH); + ep->stopped = 0; + ep->wedged = 0; + + /* Things might have been queued up in the meantime */ + if (!ep->dma_start) + gr_start_dma(ep); + } + + return retval; +} + +/* Must be called with dev->lock held */ +static inline void gr_set_ep0state(struct gr_udc *dev, enum gr_ep0state value) +{ + if (dev->ep0state != value) + dev_vdbg(dev->dev, "STATE: ep0state=%s\n", + gr_ep0state_string(value)); + dev->ep0state = value; +} + +/* + * Should only be called when endpoints can not generate interrupts. + * + * Must be called with dev->lock held. + */ +static void gr_disable_interrupts_and_pullup(struct gr_udc *dev) +{ + gr_write32(&dev->regs->control, 0); + wmb(); /* Make sure that we do not deny one of our interrupts */ + dev->irq_enabled = 0; +} + +/* + * Stop all device activity and disable data line pullup. + * + * Must be called with dev->lock held and irqs disabled. + */ +static void gr_stop_activity(struct gr_udc *dev) +{ + struct gr_ep *ep; + + list_for_each_entry(ep, &dev->ep_list, ep_list) + gr_ep_nuke(ep); + + gr_disable_interrupts_and_pullup(dev); + + gr_set_ep0state(dev, GR_EP0_DISCONNECT); + usb_gadget_set_state(&dev->gadget, USB_STATE_NOTATTACHED); +} + +/* ---------------------------------------------------------------------- */ +/* ep0 setup packet handling */ + +static void gr_ep0_testmode_complete(struct usb_ep *_ep, + struct usb_request *_req) +{ + struct gr_ep *ep; + struct gr_udc *dev; + u32 control; + + ep = container_of(_ep, struct gr_ep, ep); + dev = ep->dev; + + spin_lock(&dev->lock); + + control = gr_read32(&dev->regs->control); + control |= GR_CONTROL_TM | (dev->test_mode << GR_CONTROL_TS_POS); + gr_write32(&dev->regs->control, control); + + spin_unlock(&dev->lock); +} + +static void gr_ep0_dummy_complete(struct usb_ep *_ep, struct usb_request *_req) +{ + /* Nothing needs to be done here */ +} + +/* + * Queue a response on ep0in. + * + * Must be called with dev->lock held. + */ +static int gr_ep0_respond(struct gr_udc *dev, u8 *buf, int length, + void (*complete)(struct usb_ep *ep, + struct usb_request *req)) +{ + u8 *reqbuf = dev->ep0reqi->req.buf; + int status; + int i; + + for (i = 0; i < length; i++) + reqbuf[i] = buf[i]; + dev->ep0reqi->req.length = length; + dev->ep0reqi->req.complete = complete; + + status = gr_queue_int(&dev->epi[0], dev->ep0reqi, GFP_ATOMIC); + if (status < 0) + dev_err(dev->dev, + "Could not queue ep0in setup response: %d\n", status); + + return status; +} + +/* + * Queue a 2 byte response on ep0in. + * + * Must be called with dev->lock held. + */ +static inline int gr_ep0_respond_u16(struct gr_udc *dev, u16 response) +{ + __le16 le_response = cpu_to_le16(response); + + return gr_ep0_respond(dev, (u8 *)&le_response, 2, + gr_ep0_dummy_complete); +} + +/* + * Queue a ZLP response on ep0in. + * + * Must be called with dev->lock held. + */ +static inline int gr_ep0_respond_empty(struct gr_udc *dev) +{ + return gr_ep0_respond(dev, NULL, 0, gr_ep0_dummy_complete); +} + +/* + * This is run when a SET_ADDRESS request is received. First writes + * the new address to the control register which is updated internally + * when the next IN packet is ACKED. + * + * Must be called with dev->lock held. + */ +static void gr_set_address(struct gr_udc *dev, u8 address) +{ + u32 control; + + control = gr_read32(&dev->regs->control) & ~GR_CONTROL_UA_MASK; + control |= (address << GR_CONTROL_UA_POS) & GR_CONTROL_UA_MASK; + control |= GR_CONTROL_SU; + gr_write32(&dev->regs->control, control); +} + +/* + * Returns negative for STALL, 0 for successful handling and positive for + * delegation. + * + * Must be called with dev->lock held. + */ +static int gr_device_request(struct gr_udc *dev, u8 type, u8 request, + u16 value, u16 index) +{ + u16 response; + u8 test; + + switch (request) { + case USB_REQ_SET_ADDRESS: + dev_dbg(dev->dev, "STATUS: address %d\n", value & 0xff); + gr_set_address(dev, value & 0xff); + if (value) + usb_gadget_set_state(&dev->gadget, USB_STATE_ADDRESS); + else + usb_gadget_set_state(&dev->gadget, USB_STATE_DEFAULT); + return gr_ep0_respond_empty(dev); + + case USB_REQ_GET_STATUS: + /* Self powered | remote wakeup */ + response = 0x0001 | (dev->remote_wakeup ? 0x0002 : 0); + return gr_ep0_respond_u16(dev, response); + + case USB_REQ_SET_FEATURE: + switch (value) { + case USB_DEVICE_REMOTE_WAKEUP: + /* Allow remote wakeup */ + dev->remote_wakeup = 1; + return gr_ep0_respond_empty(dev); + + case USB_DEVICE_TEST_MODE: + /* The hardware does not support TEST_FORCE_EN */ + test = index >> 8; + if (test >= TEST_J && test <= TEST_PACKET) { + dev->test_mode = test; + return gr_ep0_respond(dev, NULL, 0, + gr_ep0_testmode_complete); + } + } + break; + + case USB_REQ_CLEAR_FEATURE: + switch (value) { + case USB_DEVICE_REMOTE_WAKEUP: + /* Disallow remote wakeup */ + dev->remote_wakeup = 0; + return gr_ep0_respond_empty(dev); + } + break; + } + + return 1; /* Delegate the rest */ +} + +/* + * Returns negative for STALL, 0 for successful handling and positive for + * delegation. + * + * Must be called with dev->lock held. + */ +static int gr_interface_request(struct gr_udc *dev, u8 type, u8 request, + u16 value, u16 index) +{ + if (dev->gadget.state != USB_STATE_CONFIGURED) + return -1; + + /* + * Should return STALL for invalid interfaces, but udc driver does not + * know anything about that. However, many gadget drivers do not handle + * GET_STATUS so we need to take care of that. + */ + + switch (request) { + case USB_REQ_GET_STATUS: + return gr_ep0_respond_u16(dev, 0x0000); + + case USB_REQ_SET_FEATURE: + case USB_REQ_CLEAR_FEATURE: + /* + * No possible valid standard requests. Still let gadget drivers + * have a go at it. + */ + break; + } + + return 1; /* Delegate the rest */ +} + +/* + * Returns negative for STALL, 0 for successful handling and positive for + * delegation. + * + * Must be called with dev->lock held. + */ +static int gr_endpoint_request(struct gr_udc *dev, u8 type, u8 request, + u16 value, u16 index) +{ + struct gr_ep *ep; + int status; + int halted; + u8 epnum = index & USB_ENDPOINT_NUMBER_MASK; + u8 is_in = index & USB_ENDPOINT_DIR_MASK; + + if ((is_in && epnum >= dev->nepi) || (!is_in && epnum >= dev->nepo)) + return -1; + + if (dev->gadget.state != USB_STATE_CONFIGURED && epnum != 0) + return -1; + + ep = (is_in ? &dev->epi[epnum] : &dev->epo[epnum]); + + switch (request) { + case USB_REQ_GET_STATUS: + halted = gr_read32(&ep->regs->epctrl) & GR_EPCTRL_EH; + return gr_ep0_respond_u16(dev, halted ? 0x0001 : 0); + + case USB_REQ_SET_FEATURE: + switch (value) { + case USB_ENDPOINT_HALT: + status = gr_ep_halt_wedge(ep, 1, 0, 1); + if (status >= 0) + status = gr_ep0_respond_empty(dev); + return status; + } + break; + + case USB_REQ_CLEAR_FEATURE: + switch (value) { + case USB_ENDPOINT_HALT: + if (ep->wedged) + return -1; + status = gr_ep_halt_wedge(ep, 0, 0, 1); + if (status >= 0) + status = gr_ep0_respond_empty(dev); + return status; + } + break; + } + + return 1; /* Delegate the rest */ +} + +/* Must be called with dev->lock held */ +static void gr_ep0out_requeue(struct gr_udc *dev) +{ + int ret = gr_queue_int(&dev->epo[0], dev->ep0reqo, GFP_ATOMIC); + + if (ret) + dev_err(dev->dev, "Could not queue ep0out setup request: %d\n", + ret); +} + +/* + * The main function dealing with setup requests on ep0. + * + * Must be called with dev->lock held and irqs disabled + */ +static void gr_ep0_setup(struct gr_udc *dev, struct gr_request *req) + __releases(&dev->lock) + __acquires(&dev->lock) +{ + union { + struct usb_ctrlrequest ctrl; + u8 raw[8]; + u32 word[2]; + } u; + u8 type; + u8 request; + u16 value; + u16 index; + u16 length; + int i; + int status; + + /* Restore from ep0 halt */ + if (dev->ep0state == GR_EP0_STALL) { + gr_set_ep0state(dev, GR_EP0_SETUP); + if (!req->req.actual) + goto out; + } + + if (dev->ep0state == GR_EP0_ISTATUS) { + gr_set_ep0state(dev, GR_EP0_SETUP); + if (req->req.actual > 0) + dev_dbg(dev->dev, + "Unexpected setup packet at state %s\n", + gr_ep0state_string(GR_EP0_ISTATUS)); + else + goto out; /* Got expected ZLP */ + } else if (dev->ep0state != GR_EP0_SETUP) { + dev_info(dev->dev, + "Unexpected ep0out request at state %s - stalling\n", + gr_ep0state_string(dev->ep0state)); + gr_control_stall(dev); + gr_set_ep0state(dev, GR_EP0_SETUP); + goto out; + } else if (!req->req.actual) { + dev_dbg(dev->dev, "Unexpected ZLP at state %s\n", + gr_ep0state_string(dev->ep0state)); + goto out; + } + + /* Handle SETUP packet */ + for (i = 0; i < req->req.actual; i++) + u.raw[i] = ((u8 *)req->req.buf)[i]; + + type = u.ctrl.bRequestType; + request = u.ctrl.bRequest; + value = le16_to_cpu(u.ctrl.wValue); + index = le16_to_cpu(u.ctrl.wIndex); + length = le16_to_cpu(u.ctrl.wLength); + + gr_dbgprint_devreq(dev, type, request, value, index, length); + + /* Check for data stage */ + if (length) { + if (type & USB_DIR_IN) + gr_set_ep0state(dev, GR_EP0_IDATA); + else + gr_set_ep0state(dev, GR_EP0_ODATA); + } + + status = 1; /* Positive status flags delegation */ + if ((type & USB_TYPE_MASK) == USB_TYPE_STANDARD) { + switch (type & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + status = gr_device_request(dev, type, request, + value, index); + break; + case USB_RECIP_ENDPOINT: + status = gr_endpoint_request(dev, type, request, + value, index); + break; + case USB_RECIP_INTERFACE: + status = gr_interface_request(dev, type, request, + value, index); + break; + } + } + + if (status > 0) { + spin_unlock(&dev->lock); + + dev_vdbg(dev->dev, "DELEGATE\n"); + status = dev->driver->setup(&dev->gadget, &u.ctrl); + + spin_lock(&dev->lock); + } + + /* Generate STALL on both ep0out and ep0in if requested */ + if (unlikely(status < 0)) { + dev_vdbg(dev->dev, "STALL\n"); + gr_control_stall(dev); + } + + if ((type & USB_TYPE_MASK) == USB_TYPE_STANDARD && + request == USB_REQ_SET_CONFIGURATION) { + if (!value) { + dev_dbg(dev->dev, "STATUS: deconfigured\n"); + usb_gadget_set_state(&dev->gadget, USB_STATE_ADDRESS); + } else if (status >= 0) { + /* Not configured unless gadget OK:s it */ + dev_dbg(dev->dev, "STATUS: configured: %d\n", value); + usb_gadget_set_state(&dev->gadget, + USB_STATE_CONFIGURED); + } + } + + /* Get ready for next stage */ + if (dev->ep0state == GR_EP0_ODATA) + gr_set_ep0state(dev, GR_EP0_OSTATUS); + else if (dev->ep0state == GR_EP0_IDATA) + gr_set_ep0state(dev, GR_EP0_ISTATUS); + else + gr_set_ep0state(dev, GR_EP0_SETUP); + +out: + gr_ep0out_requeue(dev); +} + +/* ---------------------------------------------------------------------- */ +/* VBUS and USB reset handling */ + +/* Must be called with dev->lock held and irqs disabled */ +static void gr_vbus_connected(struct gr_udc *dev, u32 status) +{ + u32 control; + + dev->gadget.speed = GR_SPEED(status); + usb_gadget_set_state(&dev->gadget, USB_STATE_POWERED); + + /* Turn on full interrupts and pullup */ + control = (GR_CONTROL_SI | GR_CONTROL_UI | GR_CONTROL_VI | + GR_CONTROL_SP | GR_CONTROL_EP); + gr_write32(&dev->regs->control, control); +} + +/* Must be called with dev->lock held */ +static void gr_enable_vbus_detect(struct gr_udc *dev) +{ + u32 status; + + dev->irq_enabled = 1; + wmb(); /* Make sure we do not ignore an interrupt */ + gr_write32(&dev->regs->control, GR_CONTROL_VI); + + /* Take care of the case we are already plugged in at this point */ + status = gr_read32(&dev->regs->status); + if (status & GR_STATUS_VB) + gr_vbus_connected(dev, status); +} + +/* Must be called with dev->lock held and irqs disabled */ +static void gr_vbus_disconnected(struct gr_udc *dev) +{ + gr_stop_activity(dev); + + /* Report disconnect */ + if (dev->driver && dev->driver->disconnect) { + spin_unlock(&dev->lock); + + dev->driver->disconnect(&dev->gadget); + + spin_lock(&dev->lock); + } + + gr_enable_vbus_detect(dev); +} + +/* Must be called with dev->lock held and irqs disabled */ +static void gr_udc_usbreset(struct gr_udc *dev, u32 status) +{ + gr_set_address(dev, 0); + gr_set_ep0state(dev, GR_EP0_SETUP); + usb_gadget_set_state(&dev->gadget, USB_STATE_DEFAULT); + dev->gadget.speed = GR_SPEED(status); + + gr_ep_nuke(&dev->epo[0]); + gr_ep_nuke(&dev->epi[0]); + dev->epo[0].stopped = 0; + dev->epi[0].stopped = 0; + gr_ep0out_requeue(dev); +} + +/* ---------------------------------------------------------------------- */ +/* Irq handling */ + +/* + * Handles interrupts from in endpoints. Returns whether something was handled. + * + * Must be called with dev->lock held, irqs disabled and with !ep->stopped. + */ +static int gr_handle_in_ep(struct gr_ep *ep) +{ + struct gr_request *req; + + req = list_first_entry(&ep->queue, struct gr_request, queue); + if (!req->last_desc) + return 0; + + if (ACCESS_ONCE(req->last_desc->ctrl) & GR_DESC_IN_CTRL_EN) + return 0; /* Not put in hardware buffers yet */ + + if (gr_read32(&ep->regs->epstat) & (GR_EPSTAT_B1 | GR_EPSTAT_B0)) + return 0; /* Not transmitted yet, still in hardware buffers */ + + /* Write complete */ + gr_dma_advance(ep, 0); + + return 1; +} + +/* + * Handles interrupts from out endpoints. Returns whether something was handled. + * + * Must be called with dev->lock held, irqs disabled and with !ep->stopped. + */ +static int gr_handle_out_ep(struct gr_ep *ep) +{ + u32 ep_dmactrl; + u32 ctrl; + u16 len; + struct gr_request *req; + struct gr_udc *dev = ep->dev; + + req = list_first_entry(&ep->queue, struct gr_request, queue); + if (!req->curr_desc) + return 0; + + ctrl = ACCESS_ONCE(req->curr_desc->ctrl); + if (ctrl & GR_DESC_OUT_CTRL_EN) + return 0; /* Not received yet */ + + /* Read complete */ + len = ctrl & GR_DESC_OUT_CTRL_LEN_MASK; + req->req.actual += len; + if (ctrl & GR_DESC_OUT_CTRL_SE) + req->setup = 1; + + if (len < ep->ep.maxpacket || req->req.actual == req->req.length) { + /* Short packet or the expected size - we are done */ + + if ((ep == &dev->epo[0]) && (dev->ep0state == GR_EP0_OSTATUS)) { + /* + * Send a status stage ZLP to ack the DATA stage in the + * OUT direction. This needs to be done before + * gr_dma_advance as that can lead to a call to + * ep0_setup that can change dev->ep0state. + */ + gr_ep0_respond_empty(dev); + gr_set_ep0state(dev, GR_EP0_SETUP); + } + + gr_dma_advance(ep, 0); + } else { + /* Not done yet. Enable the next descriptor to receive more. */ + req->curr_desc = req->curr_desc->next_desc; + req->curr_desc->ctrl |= GR_DESC_OUT_CTRL_EN; + + ep_dmactrl = gr_read32(&ep->regs->dmactrl); + gr_write32(&ep->regs->dmactrl, ep_dmactrl | GR_DMACTRL_DA); + } + + return 1; +} + +/* + * Handle state changes. Returns whether something was handled. + * + * Must be called with dev->lock held and irqs disabled. + */ +static int gr_handle_state_changes(struct gr_udc *dev) +{ + u32 status = gr_read32(&dev->regs->status); + int handled = 0; + int powstate = !(dev->gadget.state == USB_STATE_NOTATTACHED || + dev->gadget.state == USB_STATE_ATTACHED); + + /* VBUS valid detected */ + if (!powstate && (status & GR_STATUS_VB)) { + dev_dbg(dev->dev, "STATUS: vbus valid detected\n"); + gr_vbus_connected(dev, status); + handled = 1; + } + + /* Disconnect */ + if (powstate && !(status & GR_STATUS_VB)) { + dev_dbg(dev->dev, "STATUS: vbus invalid detected\n"); + gr_vbus_disconnected(dev); + handled = 1; + } + + /* USB reset detected */ + if (status & GR_STATUS_UR) { + dev_dbg(dev->dev, "STATUS: USB reset - speed is %s\n", + GR_SPEED_STR(status)); + gr_write32(&dev->regs->status, GR_STATUS_UR); + gr_udc_usbreset(dev, status); + handled = 1; + } + + /* Speed change */ + if (dev->gadget.speed != GR_SPEED(status)) { + dev_dbg(dev->dev, "STATUS: USB Speed change to %s\n", + GR_SPEED_STR(status)); + dev->gadget.speed = GR_SPEED(status); + handled = 1; + } + + /* Going into suspend */ + if ((dev->ep0state != GR_EP0_SUSPEND) && !(status & GR_STATUS_SU)) { + dev_dbg(dev->dev, "STATUS: USB suspend\n"); + gr_set_ep0state(dev, GR_EP0_SUSPEND); + dev->suspended_from = dev->gadget.state; + usb_gadget_set_state(&dev->gadget, USB_STATE_SUSPENDED); + + if ((dev->gadget.speed != USB_SPEED_UNKNOWN) && + dev->driver && dev->driver->suspend) { + spin_unlock(&dev->lock); + + dev->driver->suspend(&dev->gadget); + + spin_lock(&dev->lock); + } + handled = 1; + } + + /* Coming out of suspend */ + if ((dev->ep0state == GR_EP0_SUSPEND) && (status & GR_STATUS_SU)) { + dev_dbg(dev->dev, "STATUS: USB resume\n"); + if (dev->suspended_from == USB_STATE_POWERED) + gr_set_ep0state(dev, GR_EP0_DISCONNECT); + else + gr_set_ep0state(dev, GR_EP0_SETUP); + usb_gadget_set_state(&dev->gadget, dev->suspended_from); + + if ((dev->gadget.speed != USB_SPEED_UNKNOWN) && + dev->driver && dev->driver->resume) { + spin_unlock(&dev->lock); + + dev->driver->resume(&dev->gadget); + + spin_lock(&dev->lock); + } + handled = 1; + } + + return handled; +} + +/* Non-interrupt context irq handler */ +static irqreturn_t gr_irq_handler(int irq, void *_dev) +{ + struct gr_udc *dev = _dev; + struct gr_ep *ep; + int handled = 0; + int i; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + + if (!dev->irq_enabled) + goto out; + + /* + * Check IN ep interrupts. We check these before the OUT eps because + * some gadgets reuse the request that might already be currently + * outstanding and needs to be completed (mainly setup requests). + */ + for (i = 0; i < dev->nepi; i++) { + ep = &dev->epi[i]; + if (!ep->stopped && !ep->callback && !list_empty(&ep->queue)) + handled = gr_handle_in_ep(ep) || handled; + } + + /* Check OUT ep interrupts */ + for (i = 0; i < dev->nepo; i++) { + ep = &dev->epo[i]; + if (!ep->stopped && !ep->callback && !list_empty(&ep->queue)) + handled = gr_handle_out_ep(ep) || handled; + } + + /* Check status interrupts */ + handled = gr_handle_state_changes(dev) || handled; + + /* + * Check AMBA DMA errors. Only check if we didn't find anything else to + * handle because this shouldn't happen if we did everything right. + */ + if (!handled) { + list_for_each_entry(ep, &dev->ep_list, ep_list) { + if (gr_read32(&ep->regs->dmactrl) & GR_DMACTRL_AE) { + dev_err(dev->dev, + "AMBA Error occurred for %s\n", + ep->ep.name); + handled = 1; + } + } + } + +out: + spin_unlock_irqrestore(&dev->lock, flags); + + return handled ? IRQ_HANDLED : IRQ_NONE; +} + +/* Interrupt context irq handler */ +static irqreturn_t gr_irq(int irq, void *_dev) +{ + struct gr_udc *dev = _dev; + + if (!dev->irq_enabled) + return IRQ_NONE; + + return IRQ_WAKE_THREAD; +} + +/* ---------------------------------------------------------------------- */ +/* USB ep ops */ + +/* Enable endpoint. Not for ep0in and ep0out that are handled separately. */ +static int gr_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct gr_udc *dev; + struct gr_ep *ep; + u8 mode; + u8 nt; + u16 max; + u16 buffer_size = 0; + u32 epctrl; + + ep = container_of(_ep, struct gr_ep, ep); + if (!_ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) + return -EINVAL; + + dev = ep->dev; + + /* 'ep0' IN and OUT are reserved */ + if (ep == &dev->epo[0] || ep == &dev->epi[0]) + return -EINVAL; + + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + /* Make sure we are clear for enabling */ + epctrl = gr_read32(&ep->regs->epctrl); + if (epctrl & GR_EPCTRL_EV) + return -EBUSY; + + /* Check that directions match */ + if (!ep->is_in != !usb_endpoint_dir_in(desc)) + return -EINVAL; + + /* Check ep num */ + if ((!ep->is_in && ep->num >= dev->nepo) || + (ep->is_in && ep->num >= dev->nepi)) + return -EINVAL; + + if (usb_endpoint_xfer_control(desc)) { + mode = 0; + } else if (usb_endpoint_xfer_isoc(desc)) { + mode = 1; + } else if (usb_endpoint_xfer_bulk(desc)) { + mode = 2; + } else if (usb_endpoint_xfer_int(desc)) { + mode = 3; + } else { + dev_err(dev->dev, "Unknown transfer type for %s\n", + ep->ep.name); + return -EINVAL; + } + + /* + * Bits 10-0 set the max payload. 12-11 set the number of + * additional transactions. + */ + max = 0x7ff & usb_endpoint_maxp(desc); + nt = 0x3 & (usb_endpoint_maxp(desc) >> 11); + buffer_size = GR_BUFFER_SIZE(epctrl); + if (nt && (mode == 0 || mode == 2)) { + dev_err(dev->dev, + "%s mode: multiple trans./microframe not valid\n", + (mode == 2 ? "Bulk" : "Control")); + return -EINVAL; + } else if (nt == 0x11) { + dev_err(dev->dev, "Invalid value for trans./microframe\n"); + return -EINVAL; + } else if ((nt + 1) * max > buffer_size) { + dev_err(dev->dev, "Hw buffer size %d < max payload %d * %d\n", + buffer_size, (nt + 1), max); + return -EINVAL; + } else if (max == 0) { + dev_err(dev->dev, "Max payload cannot be set to 0\n"); + return -EINVAL; + } + + spin_lock(&ep->dev->lock); + + if (!ep->stopped) { + spin_unlock(&ep->dev->lock); + return -EBUSY; + } + + ep->stopped = 0; + ep->wedged = 0; + ep->ep.desc = desc; + ep->ep.maxpacket = max; + ep->dma_start = 0; + + + if (nt) { + /* + * Maximum possible size of all payloads in one microframe + * regardless of direction when using high-bandwidth mode. + */ + ep->bytes_per_buffer = (nt + 1) * max; + } else if (ep->is_in) { + /* + * The biggest multiple of maximum packet size that fits into + * the buffer. The hardware will split up into many packets in + * the IN direction. + */ + ep->bytes_per_buffer = (buffer_size / max) * max; + } else { + /* + * Only single packets will be placed the buffers in the OUT + * direction. + */ + ep->bytes_per_buffer = max; + } + + epctrl = (max << GR_EPCTRL_MAXPL_POS) + | (nt << GR_EPCTRL_NT_POS) + | (mode << GR_EPCTRL_TT_POS) + | GR_EPCTRL_EV; + if (ep->is_in) + epctrl |= GR_EPCTRL_PI; + gr_write32(&ep->regs->epctrl, epctrl); + + gr_write32(&ep->regs->dmactrl, GR_DMACTRL_IE | GR_DMACTRL_AI); + + spin_unlock(&ep->dev->lock); + + dev_dbg(ep->dev->dev, "EP: %s enabled - %s with %d bytes/buffer\n", + ep->ep.name, gr_modestring[mode], ep->bytes_per_buffer); + return 0; +} + +/* Disable endpoint. Not for ep0in and ep0out that are handled separately. */ +static int gr_ep_disable(struct usb_ep *_ep) +{ + struct gr_ep *ep; + struct gr_udc *dev; + unsigned long flags; + + ep = container_of(_ep, struct gr_ep, ep); + if (!_ep || !ep->ep.desc) + return -ENODEV; + + dev = ep->dev; + + /* 'ep0' IN and OUT are reserved */ + if (ep == &dev->epo[0] || ep == &dev->epi[0]) + return -EINVAL; + + if (dev->ep0state == GR_EP0_SUSPEND) + return -EBUSY; + + dev_dbg(ep->dev->dev, "EP: disable %s\n", ep->ep.name); + + spin_lock_irqsave(&dev->lock, flags); + + gr_ep_nuke(ep); + gr_ep_reset(ep); + ep->ep.desc = NULL; + + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; +} + +/* + * Frees a request, but not any DMA buffers associated with it + * (gr_finish_request should already have taken care of that). + */ +static void gr_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct gr_request *req; + + if (!_ep || !_req) + return; + req = container_of(_req, struct gr_request, req); + + /* Leads to memory leak */ + WARN(!list_empty(&req->queue), + "request not dequeued properly before freeing\n"); + + kfree(req); +} + +/* Queue a request from the gadget */ +static int gr_queue_ext(struct usb_ep *_ep, struct usb_request *_req, + gfp_t gfp_flags) +{ + struct gr_ep *ep; + struct gr_request *req; + struct gr_udc *dev; + int ret; + + if (unlikely(!_ep || !_req)) + return -EINVAL; + + ep = container_of(_ep, struct gr_ep, ep); + req = container_of(_req, struct gr_request, req); + dev = ep->dev; + + spin_lock(&ep->dev->lock); + + /* + * The ep0 pointer in the gadget struct is used both for ep0in and + * ep0out. In a data stage in the out direction ep0out needs to be used + * instead of the default ep0in. Completion functions might use + * driver_data, so that needs to be copied as well. + */ + if ((ep == &dev->epi[0]) && (dev->ep0state == GR_EP0_ODATA)) { + ep = &dev->epo[0]; + ep->ep.driver_data = dev->epi[0].ep.driver_data; + } + + if (ep->is_in) + gr_dbgprint_request("EXTERN", ep, req); + + ret = gr_queue(ep, req, gfp_flags); + + spin_unlock(&ep->dev->lock); + + return ret; +} + +/* Dequeue JUST ONE request */ +static int gr_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct gr_request *req; + struct gr_ep *ep; + struct gr_udc *dev; + int ret = 0; + unsigned long flags; + + ep = container_of(_ep, struct gr_ep, ep); + if (!_ep || !_req || (!ep->ep.desc && ep->num != 0)) + return -EINVAL; + dev = ep->dev; + if (!dev->driver) + return -ESHUTDOWN; + + /* We can't touch (DMA) registers when suspended */ + if (dev->ep0state == GR_EP0_SUSPEND) + return -EBUSY; + + spin_lock_irqsave(&dev->lock, flags); + + /* Make sure it's actually queued on this endpoint */ + list_for_each_entry(req, &ep->queue, queue) { + if (&req->req == _req) + break; + } + if (&req->req != _req) { + ret = -EINVAL; + goto out; + } + + if (list_first_entry(&ep->queue, struct gr_request, queue) == req) { + /* This request is currently being processed */ + gr_abort_dma(ep); + if (ep->stopped) + gr_finish_request(ep, req, -ECONNRESET); + else + gr_dma_advance(ep, -ECONNRESET); + } else if (!list_empty(&req->queue)) { + /* Not being processed - gr_finish_request dequeues it */ + gr_finish_request(ep, req, -ECONNRESET); + } else { + ret = -EOPNOTSUPP; + } + +out: + spin_unlock_irqrestore(&dev->lock, flags); + + return ret; +} + +/* Helper for gr_set_halt and gr_set_wedge */ +static int gr_set_halt_wedge(struct usb_ep *_ep, int halt, int wedge) +{ + int ret; + struct gr_ep *ep; + + if (!_ep) + return -ENODEV; + ep = container_of(_ep, struct gr_ep, ep); + + spin_lock(&ep->dev->lock); + + /* Halting an IN endpoint should fail if queue is not empty */ + if (halt && ep->is_in && !list_empty(&ep->queue)) { + ret = -EAGAIN; + goto out; + } + + ret = gr_ep_halt_wedge(ep, halt, wedge, 0); + +out: + spin_unlock(&ep->dev->lock); + + return ret; +} + +/* Halt endpoint */ +static int gr_set_halt(struct usb_ep *_ep, int halt) +{ + return gr_set_halt_wedge(_ep, halt, 0); +} + +/* Halt and wedge endpoint */ +static int gr_set_wedge(struct usb_ep *_ep) +{ + return gr_set_halt_wedge(_ep, 1, 1); +} + +/* + * Return the total number of bytes currently stored in the internal buffers of + * the endpoint. + */ +static int gr_fifo_status(struct usb_ep *_ep) +{ + struct gr_ep *ep; + u32 epstat; + u32 bytes = 0; + + if (!_ep) + return -ENODEV; + ep = container_of(_ep, struct gr_ep, ep); + + epstat = gr_read32(&ep->regs->epstat); + + if (epstat & GR_EPSTAT_B0) + bytes += (epstat & GR_EPSTAT_B0CNT_MASK) >> GR_EPSTAT_B0CNT_POS; + if (epstat & GR_EPSTAT_B1) + bytes += (epstat & GR_EPSTAT_B1CNT_MASK) >> GR_EPSTAT_B1CNT_POS; + + return bytes; +} + + +/* Empty data from internal buffers of an endpoint. */ +static void gr_fifo_flush(struct usb_ep *_ep) +{ + struct gr_ep *ep; + u32 epctrl; + + if (!_ep) + return; + ep = container_of(_ep, struct gr_ep, ep); + dev_vdbg(ep->dev->dev, "EP: flush fifo %s\n", ep->ep.name); + + spin_lock(&ep->dev->lock); + + epctrl = gr_read32(&ep->regs->epctrl); + epctrl |= GR_EPCTRL_CB; + gr_write32(&ep->regs->epctrl, epctrl); + + spin_unlock(&ep->dev->lock); +} + +static struct usb_ep_ops gr_ep_ops = { + .enable = gr_ep_enable, + .disable = gr_ep_disable, + + .alloc_request = gr_alloc_request, + .free_request = gr_free_request, + + .queue = gr_queue_ext, + .dequeue = gr_dequeue, + + .set_halt = gr_set_halt, + .set_wedge = gr_set_wedge, + .fifo_status = gr_fifo_status, + .fifo_flush = gr_fifo_flush, +}; + +/* ---------------------------------------------------------------------- */ +/* USB Gadget ops */ + +static int gr_get_frame(struct usb_gadget *_gadget) +{ + struct gr_udc *dev; + + if (!_gadget) + return -ENODEV; + dev = container_of(_gadget, struct gr_udc, gadget); + return gr_read32(&dev->regs->status) & GR_STATUS_FN_MASK; +} + +static int gr_wakeup(struct usb_gadget *_gadget) +{ + struct gr_udc *dev; + + if (!_gadget) + return -ENODEV; + dev = container_of(_gadget, struct gr_udc, gadget); + + /* Remote wakeup feature not enabled by host*/ + if (!dev->remote_wakeup) + return -EINVAL; + + spin_lock(&dev->lock); + + gr_write32(&dev->regs->control, + gr_read32(&dev->regs->control) | GR_CONTROL_RW); + + spin_unlock(&dev->lock); + + return 0; +} + +static int gr_pullup(struct usb_gadget *_gadget, int is_on) +{ + struct gr_udc *dev; + u32 control; + + if (!_gadget) + return -ENODEV; + dev = container_of(_gadget, struct gr_udc, gadget); + + spin_lock(&dev->lock); + + control = gr_read32(&dev->regs->control); + if (is_on) + control |= GR_CONTROL_EP; + else + control &= ~GR_CONTROL_EP; + gr_write32(&dev->regs->control, control); + + spin_unlock(&dev->lock); + + return 0; +} + +static int gr_udc_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + struct gr_udc *dev = to_gr_udc(gadget); + + spin_lock(&dev->lock); + + /* Hook up the driver */ + driver->driver.bus = NULL; + dev->driver = driver; + + /* Get ready for host detection */ + gr_enable_vbus_detect(dev); + + spin_unlock(&dev->lock); + + dev_info(dev->dev, "Started with gadget driver '%s'\n", + driver->driver.name); + + return 0; +} + +static int gr_udc_stop(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + struct gr_udc *dev = to_gr_udc(gadget); + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + + dev->driver = NULL; + gr_stop_activity(dev); + + spin_unlock_irqrestore(&dev->lock, flags); + + dev_info(dev->dev, "Stopped\n"); + + return 0; +} + +static const struct usb_gadget_ops gr_ops = { + .get_frame = gr_get_frame, + .wakeup = gr_wakeup, + .pullup = gr_pullup, + .udc_start = gr_udc_start, + .udc_stop = gr_udc_stop, + /* Other operations not supported */ +}; + +/* ---------------------------------------------------------------------- */ +/* Module probe, removal and of-matching */ + +static const char * const onames[] = { + "ep0out", "ep1out", "ep2out", "ep3out", "ep4out", "ep5out", + "ep6out", "ep7out", "ep8out", "ep9out", "ep10out", "ep11out", + "ep12out", "ep13out", "ep14out", "ep15out" +}; + +static const char * const inames[] = { + "ep0in", "ep1in", "ep2in", "ep3in", "ep4in", "ep5in", + "ep6in", "ep7in", "ep8in", "ep9in", "ep10in", "ep11in", + "ep12in", "ep13in", "ep14in", "ep15in" +}; + +/* Must be called with dev->lock held */ +static int gr_ep_init(struct gr_udc *dev, int num, int is_in, u32 maxplimit) +{ + struct gr_ep *ep; + struct gr_request *req; + struct usb_request *_req; + void *buf; + + if (is_in) { + ep = &dev->epi[num]; + ep->ep.name = inames[num]; + ep->regs = &dev->regs->epi[num]; + } else { + ep = &dev->epo[num]; + ep->ep.name = onames[num]; + ep->regs = &dev->regs->epo[num]; + } + + gr_ep_reset(ep); + ep->num = num; + ep->is_in = is_in; + ep->dev = dev; + ep->ep.ops = &gr_ep_ops; + INIT_LIST_HEAD(&ep->queue); + + if (num == 0) { + _req = gr_alloc_request(&ep->ep, GFP_KERNEL); + buf = devm_kzalloc(dev->dev, PAGE_SIZE, GFP_DMA | GFP_KERNEL); + if (!_req || !buf) { + /* possible _req freed by gr_probe via gr_remove */ + return -ENOMEM; + } + + req = container_of(_req, struct gr_request, req); + req->req.buf = buf; + req->req.length = MAX_CTRL_PL_SIZE; + + if (is_in) + dev->ep0reqi = req; /* Complete gets set as used */ + else + dev->ep0reqo = req; /* Completion treated separately */ + + usb_ep_set_maxpacket_limit(&ep->ep, MAX_CTRL_PL_SIZE); + ep->bytes_per_buffer = MAX_CTRL_PL_SIZE; + } else { + usb_ep_set_maxpacket_limit(&ep->ep, (u16)maxplimit); + list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list); + } + list_add_tail(&ep->ep_list, &dev->ep_list); + + return 0; +} + +/* Must be called with dev->lock held */ +static int gr_udc_init(struct gr_udc *dev) +{ + struct device_node *np = dev->dev->of_node; + u32 epctrl_val; + u32 dmactrl_val; + int i; + int ret = 0; + u32 *bufsizes; + u32 bufsize; + int len; + + gr_set_address(dev, 0); + + INIT_LIST_HEAD(&dev->gadget.ep_list); + dev->gadget.speed = USB_SPEED_UNKNOWN; + dev->gadget.ep0 = &dev->epi[0].ep; + + INIT_LIST_HEAD(&dev->ep_list); + gr_set_ep0state(dev, GR_EP0_DISCONNECT); + + bufsizes = (u32 *)of_get_property(np, "epobufsizes", &len); + len /= sizeof(u32); + for (i = 0; i < dev->nepo; i++) { + bufsize = (bufsizes && i < len) ? bufsizes[i] : 1024; + ret = gr_ep_init(dev, i, 0, bufsize); + if (ret) + return ret; + } + + bufsizes = (u32 *)of_get_property(np, "epibufsizes", &len); + len /= sizeof(u32); + for (i = 0; i < dev->nepi; i++) { + bufsize = (bufsizes && i < len) ? bufsizes[i] : 1024; + ret = gr_ep_init(dev, i, 1, bufsize); + if (ret) + return ret; + } + + /* Must be disabled by default */ + dev->remote_wakeup = 0; + + /* Enable ep0out and ep0in */ + epctrl_val = (MAX_CTRL_PL_SIZE << GR_EPCTRL_MAXPL_POS) | GR_EPCTRL_EV; + dmactrl_val = GR_DMACTRL_IE | GR_DMACTRL_AI; + gr_write32(&dev->epo[0].regs->epctrl, epctrl_val); + gr_write32(&dev->epi[0].regs->epctrl, epctrl_val | GR_EPCTRL_PI); + gr_write32(&dev->epo[0].regs->dmactrl, dmactrl_val); + gr_write32(&dev->epi[0].regs->dmactrl, dmactrl_val); + + return 0; +} + +static int gr_remove(struct platform_device *ofdev) +{ + struct gr_udc *dev = dev_get_drvdata(&ofdev->dev); + + if (dev->added) + usb_del_gadget_udc(&dev->gadget); /* Shuts everything down */ + if (dev->driver) + return -EBUSY; + + gr_dfs_delete(dev); + if (dev->desc_pool) + dma_pool_destroy(dev->desc_pool); + dev_set_drvdata(&ofdev->dev, NULL); + + gr_free_request(&dev->epi[0].ep, &dev->ep0reqi->req); + gr_free_request(&dev->epo[0].ep, &dev->ep0reqo->req); + + return 0; +} +static int gr_request_irq(struct gr_udc *dev, int irq) +{ + return devm_request_threaded_irq(dev->dev, irq, gr_irq, gr_irq_handler, + IRQF_SHARED, driver_name, dev); +} + +static int gr_probe(struct platform_device *ofdev) +{ + struct gr_udc *dev; + struct resource *res; + struct gr_regs __iomem *regs; + int retval; + u32 status; + + dev = devm_kzalloc(&ofdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + dev->dev = &ofdev->dev; + + res = platform_get_resource(ofdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(dev->dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + dev->irq = irq_of_parse_and_map(dev->dev->of_node, 0); + if (!dev->irq) { + dev_err(dev->dev, "No irq found\n"); + return -ENODEV; + } + + /* Some core configurations has separate irqs for IN and OUT events */ + dev->irqi = irq_of_parse_and_map(dev->dev->of_node, 1); + if (dev->irqi) { + dev->irqo = irq_of_parse_and_map(dev->dev->of_node, 2); + if (!dev->irqo) { + dev_err(dev->dev, "Found irqi but not irqo\n"); + return -ENODEV; + } + } + + dev->gadget.name = driver_name; + dev->gadget.max_speed = USB_SPEED_HIGH; + dev->gadget.ops = &gr_ops; + dev->gadget.quirk_ep_out_aligned_size = true; + + spin_lock_init(&dev->lock); + dev->regs = regs; + + dev_set_drvdata(&ofdev->dev, dev); + + /* Determine number of endpoints and data interface mode */ + status = gr_read32(&dev->regs->status); + dev->nepi = ((status & GR_STATUS_NEPI_MASK) >> GR_STATUS_NEPI_POS) + 1; + dev->nepo = ((status & GR_STATUS_NEPO_MASK) >> GR_STATUS_NEPO_POS) + 1; + + if (!(status & GR_STATUS_DM)) { + dev_err(dev->dev, "Slave mode cores are not supported\n"); + return -ENODEV; + } + + /* --- Effects of the following calls might need explicit cleanup --- */ + + /* Create DMA pool for descriptors */ + dev->desc_pool = dma_pool_create("desc_pool", dev->dev, + sizeof(struct gr_dma_desc), 4, 0); + if (!dev->desc_pool) { + dev_err(dev->dev, "Could not allocate DMA pool"); + return -ENOMEM; + } + + spin_lock(&dev->lock); + + /* Inside lock so that no gadget can use this udc until probe is done */ + retval = usb_add_gadget_udc(dev->dev, &dev->gadget); + if (retval) { + dev_err(dev->dev, "Could not add gadget udc"); + goto out; + } + dev->added = 1; + + retval = gr_udc_init(dev); + if (retval) + goto out; + + gr_dfs_create(dev); + + /* Clear all interrupt enables that might be left on since last boot */ + gr_disable_interrupts_and_pullup(dev); + + retval = gr_request_irq(dev, dev->irq); + if (retval) { + dev_err(dev->dev, "Failed to request irq %d\n", dev->irq); + goto out; + } + + if (dev->irqi) { + retval = gr_request_irq(dev, dev->irqi); + if (retval) { + dev_err(dev->dev, "Failed to request irqi %d\n", + dev->irqi); + goto out; + } + retval = gr_request_irq(dev, dev->irqo); + if (retval) { + dev_err(dev->dev, "Failed to request irqo %d\n", + dev->irqo); + goto out; + } + } + + if (dev->irqi) + dev_info(dev->dev, "regs: %p, irqs %d, %d, %d\n", dev->regs, + dev->irq, dev->irqi, dev->irqo); + else + dev_info(dev->dev, "regs: %p, irq %d\n", dev->regs, dev->irq); + +out: + spin_unlock(&dev->lock); + + if (retval) + gr_remove(ofdev); + + return retval; +} + +static struct of_device_id gr_match[] = { + {.name = "GAISLER_USBDC"}, + {.name = "01_021"}, + {}, +}; +MODULE_DEVICE_TABLE(of, gr_match); + +static struct platform_driver gr_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = gr_match, + }, + .probe = gr_probe, + .remove = gr_remove, +}; +module_platform_driver(gr_driver); + +MODULE_AUTHOR("Aeroflex Gaisler AB."); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/gr_udc.h b/drivers/usb/gadget/gr_udc.h new file mode 100644 index 000000000000..8388897d9ec3 --- /dev/null +++ b/drivers/usb/gadget/gr_udc.h @@ -0,0 +1,220 @@ +/* + * USB Peripheral Controller driver for Aeroflex Gaisler GRUSBDC. + * + * 2013 (c) Aeroflex Gaisler AB + * + * This driver supports GRUSBDC USB Device Controller cores available in the + * GRLIB VHDL IP core library. + * + * Full documentation of the GRUSBDC core can be found here: + * http://www.gaisler.com/products/grlib/grip.pdf + * + * 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. + * + * Contributors: + * - Andreas Larsson + * - Marko Isomaki + */ + +/* Control registers on the AMBA bus */ + +#define GR_MAXEP 16 /* Max # endpoints for *each* direction */ + +struct gr_epregs { + u32 epctrl; + union { + struct { /* Slave mode*/ + u32 slvctrl; + u32 slvdata; + }; + struct { /* DMA mode*/ + u32 dmactrl; + u32 dmaaddr; + }; + }; + u32 epstat; +}; + +struct gr_regs { + struct gr_epregs epo[GR_MAXEP]; /* 0x000 - 0x0fc */ + struct gr_epregs epi[GR_MAXEP]; /* 0x100 - 0x1fc */ + u32 control; /* 0x200 */ + u32 status; /* 0x204 */ +}; + +#define GR_EPCTRL_BUFSZ_SCALER 8 +#define GR_EPCTRL_BUFSZ_MASK 0xffe00000 +#define GR_EPCTRL_BUFSZ_POS 21 +#define GR_EPCTRL_PI BIT(20) +#define GR_EPCTRL_CB BIT(19) +#define GR_EPCTRL_CS BIT(18) +#define GR_EPCTRL_MAXPL_MASK 0x0003ff80 +#define GR_EPCTRL_MAXPL_POS 7 +#define GR_EPCTRL_NT_MASK 0x00000060 +#define GR_EPCTRL_NT_POS 5 +#define GR_EPCTRL_TT_MASK 0x00000018 +#define GR_EPCTRL_TT_POS 3 +#define GR_EPCTRL_EH BIT(2) +#define GR_EPCTRL_ED BIT(1) +#define GR_EPCTRL_EV BIT(0) + +#define GR_DMACTRL_AE BIT(10) +#define GR_DMACTRL_AD BIT(3) +#define GR_DMACTRL_AI BIT(2) +#define GR_DMACTRL_IE BIT(1) +#define GR_DMACTRL_DA BIT(0) + +#define GR_EPSTAT_PT BIT(29) +#define GR_EPSTAT_PR BIT(29) +#define GR_EPSTAT_B1CNT_MASK 0x1fff0000 +#define GR_EPSTAT_B1CNT_POS 16 +#define GR_EPSTAT_B0CNT_MASK 0x0000fff8 +#define GR_EPSTAT_B0CNT_POS 3 +#define GR_EPSTAT_B1 BIT(2) +#define GR_EPSTAT_B0 BIT(1) +#define GR_EPSTAT_BS BIT(0) + +#define GR_CONTROL_SI BIT(31) +#define GR_CONTROL_UI BIT(30) +#define GR_CONTROL_VI BIT(29) +#define GR_CONTROL_SP BIT(28) +#define GR_CONTROL_FI BIT(27) +#define GR_CONTROL_EP BIT(14) +#define GR_CONTROL_DH BIT(13) +#define GR_CONTROL_RW BIT(12) +#define GR_CONTROL_TS_MASK 0x00000e00 +#define GR_CONTROL_TS_POS 9 +#define GR_CONTROL_TM BIT(8) +#define GR_CONTROL_UA_MASK 0x000000fe +#define GR_CONTROL_UA_POS 1 +#define GR_CONTROL_SU BIT(0) + +#define GR_STATUS_NEPI_MASK 0xf0000000 +#define GR_STATUS_NEPI_POS 28 +#define GR_STATUS_NEPO_MASK 0x0f000000 +#define GR_STATUS_NEPO_POS 24 +#define GR_STATUS_DM BIT(23) +#define GR_STATUS_SU BIT(17) +#define GR_STATUS_UR BIT(16) +#define GR_STATUS_VB BIT(15) +#define GR_STATUS_SP BIT(14) +#define GR_STATUS_AF_MASK 0x00003800 +#define GR_STATUS_AF_POS 11 +#define GR_STATUS_FN_MASK 0x000007ff +#define GR_STATUS_FN_POS 0 + + +#define MAX_CTRL_PL_SIZE 64 /* As per USB standard for full and high speed */ + +/*-------------------------------------------------------------------------*/ + +/* Driver data structures and utilities */ + +struct gr_dma_desc { + u32 ctrl; + u32 data; + u32 next; + + /* These must be last because hw uses the previous three */ + u32 paddr; + struct gr_dma_desc *next_desc; +}; + +#define GR_DESC_OUT_CTRL_SE BIT(17) +#define GR_DESC_OUT_CTRL_IE BIT(15) +#define GR_DESC_OUT_CTRL_NX BIT(14) +#define GR_DESC_OUT_CTRL_EN BIT(13) +#define GR_DESC_OUT_CTRL_LEN_MASK 0x00001fff + +#define GR_DESC_IN_CTRL_MO BIT(18) +#define GR_DESC_IN_CTRL_PI BIT(17) +#define GR_DESC_IN_CTRL_ML BIT(16) +#define GR_DESC_IN_CTRL_IE BIT(15) +#define GR_DESC_IN_CTRL_NX BIT(14) +#define GR_DESC_IN_CTRL_EN BIT(13) +#define GR_DESC_IN_CTRL_LEN_MASK 0x00001fff + +#define GR_DESC_DMAADDR_MASK 0xfffffffc + +struct gr_ep { + struct usb_ep ep; + struct gr_udc *dev; + u16 bytes_per_buffer; + unsigned int dma_start; + struct gr_epregs __iomem *regs; + + unsigned num:8; + unsigned is_in:1; + unsigned stopped:1; + unsigned wedged:1; + unsigned callback:1; + + /* analogous to a host-side qh */ + struct list_head queue; + + struct list_head ep_list; +}; + +struct gr_request { + struct usb_request req; + struct list_head queue; + + /* Chain of dma descriptors */ + struct gr_dma_desc *first_desc; /* First in the chain */ + struct gr_dma_desc *curr_desc; /* Current descriptor */ + struct gr_dma_desc *last_desc; /* Last in the chain */ + + u8 setup; /* Setup packet */ +}; + +enum gr_ep0state { + GR_EP0_DISCONNECT = 0, /* No host */ + GR_EP0_SETUP, /* Between STATUS ack and SETUP report */ + GR_EP0_IDATA, /* IN data stage */ + GR_EP0_ODATA, /* OUT data stage */ + GR_EP0_ISTATUS, /* Status stage after IN data stage */ + GR_EP0_OSTATUS, /* Status stage after OUT data stage */ + GR_EP0_STALL, /* Data or status stages */ + GR_EP0_SUSPEND, /* USB suspend */ +}; + +struct gr_udc { + struct usb_gadget gadget; + struct gr_ep epi[GR_MAXEP]; + struct gr_ep epo[GR_MAXEP]; + struct usb_gadget_driver *driver; + struct dma_pool *desc_pool; + struct device *dev; + + enum gr_ep0state ep0state; + struct gr_request *ep0reqo; + struct gr_request *ep0reqi; + + struct gr_regs __iomem *regs; + int irq; + int irqi; + int irqo; + + unsigned added:1; + unsigned irq_enabled:1; + unsigned remote_wakeup:1; + + u8 test_mode; + + enum usb_device_state suspended_from; + + unsigned int nepi; + unsigned int nepo; + + struct list_head ep_list; + + spinlock_t lock; /* General lock, a.k.a. "dev->lock" in comments */ + + struct dentry *dfs_root; + struct dentry *dfs_state; +}; + +#define to_gr_udc(gadget) (container_of((gadget), struct gr_udc, gadget)) From 4685d021f5cd04bd2682bd4db6dfa9f784fabf9f Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 23 Dec 2013 19:28:17 -0600 Subject: [PATCH 103/105] usb: gadget: at91_udc: fix build warning commit e117e742 (usb: gadget: add "maxpacket_limit" field to struct usb_ep) added a build warning to at91_udc when it passed the wrong argument to usb_ep_set_maxpacket_limit(). Fix this by passing correct argument. Signed-off-by: Felipe Balbi --- drivers/usb/gadget/at91_udc.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index 0353b6471bde..c3f49fd83250 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -1759,15 +1759,15 @@ static int at91udc_probe(struct platform_device *pdev) /* newer chips have more FIFO memory than rm9200 */ if (cpu_is_at91sam9260() || cpu_is_at91sam9g20()) { - usb_ep_set_maxpacket_limit(&udc->ep[0], 64); - usb_ep_set_maxpacket_limit(&udc->ep[3], 64); - usb_ep_set_maxpacket_limit(&udc->ep[4], 512); - usb_ep_set_maxpacket_limit(&udc->ep[5], 512); + usb_ep_set_maxpacket_limit(&udc->ep[0].ep, 64); + usb_ep_set_maxpacket_limit(&udc->ep[3].ep, 64); + usb_ep_set_maxpacket_limit(&udc->ep[4].ep, 512); + usb_ep_set_maxpacket_limit(&udc->ep[5].ep, 512); } else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) { - usb_ep_set_maxpacket_limit(&udc->ep[3], 64); + usb_ep_set_maxpacket_limit(&udc->ep[3].ep, 64); } else if (cpu_is_at91sam9263()) { - usb_ep_set_maxpacket_limit(&udc->ep[0], 64); - usb_ep_set_maxpacket_limit(&udc->ep[3], 64); + usb_ep_set_maxpacket_limit(&udc->ep[0].ep, 64); + usb_ep_set_maxpacket_limit(&udc->ep[3].ep, 64); } udc->udp_baseaddr = ioremap(res->start, resource_size(res)); From 89f836a8c56bfea6585a09f7afff7094968a4fd0 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Thu, 26 Dec 2013 09:24:52 -0300 Subject: [PATCH 104/105] usb: musb: Remove usb_disable() check in module_init() Removing the check to usb_disable() before registering the platform driver allows to build this driver when !USB && USB_GADGET, to be used in gadget-only mode. Also, use module_platform_driver() to register the platform driver. Signed-off-by: Ezequiel Garcia Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 0e7f4a00ca7b..5942f002fa52 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -2325,19 +2325,4 @@ static struct platform_driver musb_driver = { .shutdown = musb_shutdown, }; -/*-------------------------------------------------------------------------*/ - -static int __init musb_init(void) -{ - if (usb_disabled()) - return 0; - - return platform_driver_register(&musb_driver); -} -module_init(musb_init); - -static void __exit musb_cleanup(void) -{ - platform_driver_unregister(&musb_driver); -} -module_exit(musb_cleanup); +module_platform_driver(musb_driver); From 836a2164491b19dcd4f29d574e548bcadd421a6a Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 24 Dec 2013 19:37:27 +0800 Subject: [PATCH 105/105] usb: phy: keystone: remove redundant return value check of platform_get_resource() Remove unneeded error handling on the result of a call to platform_get_resource() when the value is passed to devm_ioremap_resource(). Signed-off-by: Wei Yongjun Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-keystone.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/usb/phy/phy-keystone.c b/drivers/usb/phy/phy-keystone.c index ee1d03b802e1..d762003896c0 100644 --- a/drivers/usb/phy/phy-keystone.c +++ b/drivers/usb/phy/phy-keystone.c @@ -83,11 +83,6 @@ static int keystone_usbphy_probe(struct platform_device *pdev) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "missing usb phy resource\n"); - return -EINVAL; - } - k_phy->phy_ctrl = devm_ioremap_resource(dev, res); if (IS_ERR(k_phy->phy_ctrl)) return PTR_ERR(k_phy->phy_ctrl);