diff options
author | Jiri Kosina <jkosina@suse.cz> | 2014-02-20 14:54:28 +0100 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2014-02-20 14:54:28 +0100 |
commit | d4263348f796f29546f90802177865dd4379dd0a (patch) | |
tree | adcbdaebae584eee2f32fab95e826e8e49eef385 /drivers/usb | |
parent | be873ac782f5ff5ee6675f83929f4fe6737eead2 (diff) | |
parent | 6d0abeca3242a88cab8232e4acd7e2bf088f3bc2 (diff) | |
download | op-kernel-dev-d4263348f796f29546f90802177865dd4379dd0a.zip op-kernel-dev-d4263348f796f29546f90802177865dd4379dd0a.tar.gz |
Merge branch 'master' into for-next
Diffstat (limited to 'drivers/usb')
351 files changed, 23012 insertions, 3981 deletions
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 2642b8a..2e6b832 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,8 +104,12 @@ source "drivers/usb/image/Kconfig" endif +source "drivers/usb/musb/Kconfig" + source "drivers/usb/dwc3/Kconfig" +source "drivers/usb/dwc2/Kconfig" + source "drivers/usb/chipidea/Kconfig" comment "USB port drivers" diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 70d7c5b..1ae2bf3 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_USB) += core/ obj-$(CONFIG_USB_DWC3) += dwc3/ +obj-$(CONFIG_USB_DWC2) += dwc2/ obj-$(CONFIG_USB_MON) += mon/ diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c index 8a7eb77..813d4d3 100644 --- a/drivers/usb/atm/cxacru.c +++ b/drivers/usb/atm/cxacru.c @@ -35,7 +35,6 @@ #include <linux/timer.h> #include <linux/errno.h> #include <linux/slab.h> -#include <linux/init.h> #include <linux/device.h> #include <linux/firmware.h> #include <linux/mutex.h> diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c index 69461d6..0dc8c06 100644 --- a/drivers/usb/atm/speedtch.c +++ b/drivers/usb/atm/speedtch.c @@ -27,7 +27,6 @@ #include <linux/device.h> #include <linux/errno.h> #include <linux/firmware.h> -#include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/moduleparam.h> diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index defff43..5a45937 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -57,7 +57,6 @@ #include <linux/module.h> #include <linux/moduleparam.h> -#include <linux/init.h> #include <linux/crc32.h> #include <linux/usb.h> #include <linux/firmware.h> diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c index 25a7bfc..dada014 100644 --- a/drivers/usb/atm/usbatm.c +++ b/drivers/usb/atm/usbatm.c @@ -170,9 +170,9 @@ struct usbatm_control { static void usbatm_atm_dev_close(struct atm_dev *atm_dev); static int usbatm_atm_open(struct atm_vcc *vcc); static void usbatm_atm_close(struct atm_vcc *vcc); -static int usbatm_atm_ioctl(struct atm_dev *atm_dev, unsigned int cmd, void __user * arg); +static int usbatm_atm_ioctl(struct atm_dev *atm_dev, unsigned int cmd, void __user *arg); static int usbatm_atm_send(struct atm_vcc *vcc, struct sk_buff *skb); -static int usbatm_atm_proc_read(struct atm_dev *atm_dev, loff_t * pos, char *page); +static int usbatm_atm_proc_read(struct atm_dev *atm_dev, loff_t *pos, char *page); static struct atmdev_ops usbatm_atm_devops = { .dev_close = usbatm_atm_dev_close, @@ -739,7 +739,7 @@ static void usbatm_atm_dev_close(struct atm_dev *atm_dev) usbatm_put_instance(instance); /* taken in usbatm_atm_init */ } -static int usbatm_atm_proc_read(struct atm_dev *atm_dev, loff_t * pos, char *page) +static int usbatm_atm_proc_read(struct atm_dev *atm_dev, loff_t *pos, char *page) { struct usbatm_data *instance = atm_dev->dev_data; int left = *pos; @@ -895,7 +895,7 @@ static void usbatm_atm_close(struct atm_vcc *vcc) } static int usbatm_atm_ioctl(struct atm_dev *atm_dev, unsigned int cmd, - void __user * arg) + void __user *arg) { struct usbatm_data *instance = atm_dev->dev_data; diff --git a/drivers/usb/c67x00/Makefile b/drivers/usb/c67x00/Makefile index b121868..da5f314 100644 --- a/drivers/usb/c67x00/Makefile +++ b/drivers/usb/c67x00/Makefile @@ -2,8 +2,6 @@ # Makefile for Cypress C67X00 USB Controller # -ccflags-$(CONFIG_USB_DEBUG) := -DDEBUG - obj-$(CONFIG_USB_C67X00_HCD) += c67x00.o c67x00-y := c67x00-drv.o c67x00-ll-hpi.o c67x00-hcd.o c67x00-sched.o diff --git a/drivers/usb/c67x00/c67x00-hcd.c b/drivers/usb/c67x00/c67x00-hcd.c index 75e47b8..20ec4ee 100644 --- a/drivers/usb/c67x00/c67x00-hcd.c +++ b/drivers/usb/c67x00/c67x00-hcd.c @@ -384,6 +384,8 @@ int c67x00_hcd_probe(struct c67x00_sie *sie) goto err2; } + device_wakeup_enable(hcd->self.controller); + spin_lock_irqsave(&sie->lock, flags); sie->private_data = c67x00; sie->irq = c67x00_hcd_irq; diff --git a/drivers/usb/c67x00/c67x00-hcd.h b/drivers/usb/c67x00/c67x00-hcd.h index e3d493d..cf8a455 100644 --- a/drivers/usb/c67x00/c67x00-hcd.h +++ b/drivers/usb/c67x00/c67x00-hcd.h @@ -45,7 +45,7 @@ /* * The current implementation switches between _STD (default) and _ISO (when * isochronous transfers are scheduled), in order to optimize the throughput - * in normal cicrumstances, but also provide good isochronous behaviour. + * in normal circumstances, but also provide good isochronous behaviour. * * Bandwidth is described in bit time so with a 12MHz USB clock and 1ms * frames; there are 12000 bit times per frame. diff --git a/drivers/usb/c67x00/c67x00-ll-hpi.c b/drivers/usb/c67x00/c67x00-ll-hpi.c index 3a1ca4d..b581518 100644 --- a/drivers/usb/c67x00/c67x00-ll-hpi.c +++ b/drivers/usb/c67x00/c67x00-ll-hpi.c @@ -22,6 +22,7 @@ */ #include <asm/byteorder.h> +#include <linux/delay.h> #include <linux/io.h> #include <linux/jiffies.h> #include <linux/usb/c67x00.h> @@ -62,8 +63,8 @@ struct c67x00_lcp_int_data { * HPI implementation * * The c67x00 chip also support control via SPI or HSS serial - * interfaces. However, this driver assumes that register access can - * be performed from IRQ context. While this is a safe assuption with + * interfaces. However, this driver assumes that register access can + * be performed from IRQ context. While this is a safe assumption with * the HPI interface, it is not true for the serial interfaces. */ @@ -73,13 +74,22 @@ struct c67x00_lcp_int_data { #define HPI_ADDR 2 #define HPI_STATUS 3 +/* + * According to CY7C67300 specification (tables 140 and 141) HPI read and + * write cycle duration Tcyc must be at least 6T long, where T is 1/48MHz, + * which is 125ns. + */ +#define HPI_T_CYC_NS 125 + static inline u16 hpi_read_reg(struct c67x00_device *dev, int reg) { + ndelay(HPI_T_CYC_NS); return __raw_readw(dev->hpi.base + reg * dev->hpi.regstep); } static inline void hpi_write_reg(struct c67x00_device *dev, int reg, u16 value) { + ndelay(HPI_T_CYC_NS); __raw_writew(value, dev->hpi.base + reg * dev->hpi.regstep); } diff --git a/drivers/usb/c67x00/c67x00-sched.c b/drivers/usb/c67x00/c67x00-sched.c index 892cc96..7311ed6 100644 --- a/drivers/usb/c67x00/c67x00-sched.c +++ b/drivers/usb/c67x00/c67x00-sched.c @@ -144,8 +144,6 @@ struct c67x00_urb_priv { /* -------------------------------------------------------------------------- */ -#ifdef DEBUG - /** * dbg_td - Dump the contents of the TD */ @@ -166,16 +164,8 @@ static void dbg_td(struct c67x00_hcd *c67x00, struct c67x00_td *td, char *msg) dev_dbg(dev, "retry_cnt: 0x%02x\n", td->retry_cnt); dev_dbg(dev, "residue: 0x%02x\n", td->residue); dev_dbg(dev, "next_td_addr: 0x%04x\n", td_next_td_addr(td)); - dev_dbg(dev, "data:"); - print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 16, 1, - td->data, td_length(td), 1); + dev_dbg(dev, "data: %*ph\n", td_length(td), td->data); } -#else /* DEBUG */ - -static inline void -dbg_td(struct c67x00_hcd *c67x00, struct c67x00_td *td, char *msg) { } - -#endif /* DEBUG */ /* -------------------------------------------------------------------------- */ /* Helper functions */ @@ -372,6 +362,13 @@ int c67x00_urb_enqueue(struct usb_hcd *hcd, struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd); int port = get_root_port(urb->dev)-1; + /* Allocate and initialize urb private data */ + urbp = kzalloc(sizeof(*urbp), mem_flags); + if (!urbp) { + ret = -ENOMEM; + goto err_urbp; + } + spin_lock_irqsave(&c67x00->lock, flags); /* Make sure host controller is running */ @@ -384,13 +381,6 @@ int c67x00_urb_enqueue(struct usb_hcd *hcd, if (ret) goto err_not_linked; - /* Allocate and initialize urb private data */ - urbp = kzalloc(sizeof(*urbp), mem_flags); - if (!urbp) { - ret = -ENOMEM; - goto err_urbp; - } - INIT_LIST_HEAD(&urbp->hep_node); urbp->urb = urb; urbp->port = port; @@ -453,11 +443,11 @@ int c67x00_urb_enqueue(struct usb_hcd *hcd, return 0; err_epdata: - kfree(urbp); -err_urbp: usb_hcd_unlink_urb_from_ep(hcd, urb); err_not_linked: spin_unlock_irqrestore(&c67x00->lock, flags); + kfree(urbp); +err_urbp: return ret; } @@ -780,7 +770,8 @@ static int c67x00_add_iso_urb(struct c67x00_hcd *c67x00, struct urb *urb) ret = c67x00_create_td(c67x00, urb, td_buf, len, pid, 0, urbp->cnt); if (ret) { - printk(KERN_DEBUG "create failed: %d\n", ret); + dev_dbg(c67x00_hcd_dev(c67x00), "create failed: %d\n", + ret); urb->iso_frame_desc[urbp->cnt].actual_length = 0; urb->iso_frame_desc[urbp->cnt].status = ret; if (urbp->cnt + 1 == urb->number_of_packets) diff --git a/drivers/usb/chipidea/Makefile b/drivers/usb/chipidea/Makefile index a99d980..7345d21 100644 --- a/drivers/usb/chipidea/Makefile +++ b/drivers/usb/chipidea/Makefile @@ -17,5 +17,5 @@ ifneq ($(CONFIG_PCI),) endif ifneq ($(CONFIG_OF),) - obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_imx.o usbmisc_imx.o + obj-$(CONFIG_USB_CHIPIDEA) += usbmisc_imx.o ci_hdrc_imx.o endif diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index 1c94fc5..88b80f7 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -26,6 +26,35 @@ #define ENDPT_MAX 32 /****************************************************************************** + * REGISTERS + *****************************************************************************/ +/* register indices */ +enum ci_hw_regs { + CAP_CAPLENGTH, + CAP_HCCPARAMS, + CAP_DCCPARAMS, + CAP_TESTMODE, + CAP_LAST = CAP_TESTMODE, + OP_USBCMD, + OP_USBSTS, + OP_USBINTR, + OP_DEVICEADDR, + OP_ENDPTLISTADDR, + OP_PORTSC, + OP_DEVLC, + OP_OTGSC, + OP_USBMODE, + OP_ENDPTSETUPSTAT, + OP_ENDPTPRIME, + OP_ENDPTFLUSH, + OP_ENDPTSTAT, + OP_ENDPTCOMPLETE, + OP_ENDPTCTRL, + /* endptctrl1..15 follow */ + OP_LAST = OP_ENDPTCTRL + ENDPT_MAX / 2, +}; + +/****************************************************************************** * STRUCTURES *****************************************************************************/ /** @@ -98,7 +127,7 @@ struct hw_bank { void __iomem *cap; void __iomem *op; size_t size; - void __iomem **regmap; + void __iomem *regmap[OP_LAST + 1]; }; /** @@ -135,6 +164,7 @@ struct hw_bank { * @id_event: indicates there is an id event, and handled at ci_otg_work * @b_sess_valid_event: indicates there is a vbus event, and handled * at ci_otg_work + * @imx28_write_fix: Freescale imx28 needs swp instruction for writing */ struct ci_hdrc { struct device *dev; @@ -173,6 +203,7 @@ struct ci_hdrc { struct dentry *debugfs; bool id_event; bool b_sess_valid_event; + bool imx28_write_fix; }; static inline struct ci_role_driver *ci_role(struct ci_hdrc *ci) @@ -209,38 +240,6 @@ static inline void ci_role_stop(struct ci_hdrc *ci) ci->roles[role]->stop(ci); } -/****************************************************************************** - * REGISTERS - *****************************************************************************/ -/* register size */ -#define REG_BITS (32) - -/* register indices */ -enum ci_hw_regs { - CAP_CAPLENGTH, - CAP_HCCPARAMS, - CAP_DCCPARAMS, - CAP_TESTMODE, - CAP_LAST = CAP_TESTMODE, - OP_USBCMD, - OP_USBSTS, - OP_USBINTR, - OP_DEVICEADDR, - OP_ENDPTLISTADDR, - OP_PORTSC, - OP_DEVLC, - OP_OTGSC, - OP_USBMODE, - OP_ENDPTSETUPSTAT, - OP_ENDPTPRIME, - OP_ENDPTFLUSH, - OP_ENDPTSTAT, - OP_ENDPTCOMPLETE, - OP_ENDPTCTRL, - /* endptctrl1..15 follow */ - OP_LAST = OP_ENDPTCTRL + ENDPT_MAX / 2, -}; - /** * hw_read: reads from a hw register * @reg: register index @@ -253,6 +252,26 @@ static inline u32 hw_read(struct ci_hdrc *ci, enum ci_hw_regs reg, u32 mask) return ioread32(ci->hw_bank.regmap[reg]) & mask; } +#ifdef CONFIG_SOC_IMX28 +static inline void imx28_ci_writel(u32 val, volatile void __iomem *addr) +{ + __asm__ ("swp %0, %0, [%1]" : : "r"(val), "r"(addr)); +} +#else +static inline void imx28_ci_writel(u32 val, volatile void __iomem *addr) +{ +} +#endif + +static inline void __hw_write(struct ci_hdrc *ci, u32 val, + void __iomem *addr) +{ + if (ci->imx28_write_fix) + imx28_ci_writel(val, addr); + else + iowrite32(val, addr); +} + /** * hw_write: writes to a hw register * @reg: register index @@ -266,7 +285,7 @@ static inline void hw_write(struct ci_hdrc *ci, enum ci_hw_regs reg, data = (ioread32(ci->hw_bank.regmap[reg]) & ~mask) | (data & mask); - iowrite32(data, ci->hw_bank.regmap[reg]); + __hw_write(ci, data, ci->hw_bank.regmap[reg]); } /** @@ -281,7 +300,7 @@ static inline u32 hw_test_and_clear(struct ci_hdrc *ci, enum ci_hw_regs reg, { u32 val = ioread32(ci->hw_bank.regmap[reg]) & mask; - iowrite32(val, ci->hw_bank.regmap[reg]); + __hw_write(ci, val, ci->hw_bank.regmap[reg]); return val; } diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index bb5d976..c00f772 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -23,6 +23,26 @@ #include "ci.h" #include "ci_hdrc_imx.h" +#define CI_HDRC_IMX_IMX28_WRITE_FIX BIT(0) + +struct ci_hdrc_imx_platform_flag { + unsigned int flags; +}; + +static const struct ci_hdrc_imx_platform_flag imx27_usb_data = { +}; + +static const struct ci_hdrc_imx_platform_flag imx28_usb_data = { + .flags = CI_HDRC_IMX_IMX28_WRITE_FIX, +}; + +static const struct of_device_id ci_hdrc_imx_dt_ids[] = { + { .compatible = "fsl,imx28-usb", .data = &imx28_usb_data}, + { .compatible = "fsl,imx27-usb", .data = &imx27_usb_data}, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids); + struct ci_hdrc_imx_data { struct usb_phy *phy; struct platform_device *ci_pdev; @@ -82,6 +102,9 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) CI_HDRC_DISABLE_STREAMING, }; int ret; + const struct of_device_id *of_id = + of_match_device(ci_hdrc_imx_dt_ids, &pdev->dev); + const struct ci_hdrc_imx_platform_flag *imx_platform_flag = of_id->data; data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (!data) { @@ -115,6 +138,9 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) pdata.phy = data->phy; + if (imx_platform_flag->flags & CI_HDRC_IMX_IMX28_WRITE_FIX) + pdata.flags |= CI_HDRC_IMX28_WRITE_FIX; + ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); if (ret) goto err_clk; @@ -173,12 +199,6 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev) return 0; } -static const struct of_device_id ci_hdrc_imx_dt_ids[] = { - { .compatible = "fsl,imx27-usb", }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids); - static struct platform_driver ci_hdrc_imx_driver = { .probe = ci_hdrc_imx_probe, .remove = ci_hdrc_imx_remove, diff --git a/drivers/usb/chipidea/ci_hdrc_imx.h b/drivers/usb/chipidea/ci_hdrc_imx.h index c727159..996ec93 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.h +++ b/drivers/usb/chipidea/ci_hdrc_imx.h @@ -9,6 +9,9 @@ * http://www.gnu.org/copyleft/gpl.html */ +#ifndef __DRIVER_USB_CHIPIDEA_CI_HDRC_IMX_H +#define __DRIVER_USB_CHIPIDEA_CI_HDRC_IMX_H + struct imx_usbmisc_data { int index; @@ -18,3 +21,5 @@ struct imx_usbmisc_data { int imx_usbmisc_init(struct imx_usbmisc_data *); int imx_usbmisc_init_post(struct imx_usbmisc_data *); + +#endif /* __DRIVER_USB_CHIPIDEA_CI_HDRC_IMX_H */ diff --git a/drivers/usb/chipidea/ci_hdrc_pci.c b/drivers/usb/chipidea/ci_hdrc_pci.c index d514332..241ae34 100644 --- a/drivers/usb/chipidea/ci_hdrc_pci.c +++ b/drivers/usb/chipidea/ci_hdrc_pci.c @@ -112,7 +112,7 @@ static void ci_hdrc_pci_remove(struct pci_dev *pdev) * * Check "pci.h" for details */ -static DEFINE_PCI_DEVICE_TABLE(ci_hdrc_pci_id_table) = { +static const struct pci_device_id ci_hdrc_pci_id_table[] = { { PCI_DEVICE(0x153F, 0x1004), .driver_data = (kernel_ulong_t)&pci_platdata, diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 5d8981c..33f22bc 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -75,61 +75,54 @@ #include "otg.h" /* Controller register map */ -static uintptr_t ci_regs_nolpm[] = { - [CAP_CAPLENGTH] = 0x000UL, - [CAP_HCCPARAMS] = 0x008UL, - [CAP_DCCPARAMS] = 0x024UL, - [CAP_TESTMODE] = 0x038UL, - [OP_USBCMD] = 0x000UL, - [OP_USBSTS] = 0x004UL, - [OP_USBINTR] = 0x008UL, - [OP_DEVICEADDR] = 0x014UL, - [OP_ENDPTLISTADDR] = 0x018UL, - [OP_PORTSC] = 0x044UL, - [OP_DEVLC] = 0x084UL, - [OP_OTGSC] = 0x064UL, - [OP_USBMODE] = 0x068UL, - [OP_ENDPTSETUPSTAT] = 0x06CUL, - [OP_ENDPTPRIME] = 0x070UL, - [OP_ENDPTFLUSH] = 0x074UL, - [OP_ENDPTSTAT] = 0x078UL, - [OP_ENDPTCOMPLETE] = 0x07CUL, - [OP_ENDPTCTRL] = 0x080UL, +static const u8 ci_regs_nolpm[] = { + [CAP_CAPLENGTH] = 0x00U, + [CAP_HCCPARAMS] = 0x08U, + [CAP_DCCPARAMS] = 0x24U, + [CAP_TESTMODE] = 0x38U, + [OP_USBCMD] = 0x00U, + [OP_USBSTS] = 0x04U, + [OP_USBINTR] = 0x08U, + [OP_DEVICEADDR] = 0x14U, + [OP_ENDPTLISTADDR] = 0x18U, + [OP_PORTSC] = 0x44U, + [OP_DEVLC] = 0x84U, + [OP_OTGSC] = 0x64U, + [OP_USBMODE] = 0x68U, + [OP_ENDPTSETUPSTAT] = 0x6CU, + [OP_ENDPTPRIME] = 0x70U, + [OP_ENDPTFLUSH] = 0x74U, + [OP_ENDPTSTAT] = 0x78U, + [OP_ENDPTCOMPLETE] = 0x7CU, + [OP_ENDPTCTRL] = 0x80U, }; -static uintptr_t ci_regs_lpm[] = { - [CAP_CAPLENGTH] = 0x000UL, - [CAP_HCCPARAMS] = 0x008UL, - [CAP_DCCPARAMS] = 0x024UL, - [CAP_TESTMODE] = 0x0FCUL, - [OP_USBCMD] = 0x000UL, - [OP_USBSTS] = 0x004UL, - [OP_USBINTR] = 0x008UL, - [OP_DEVICEADDR] = 0x014UL, - [OP_ENDPTLISTADDR] = 0x018UL, - [OP_PORTSC] = 0x044UL, - [OP_DEVLC] = 0x084UL, - [OP_OTGSC] = 0x0C4UL, - [OP_USBMODE] = 0x0C8UL, - [OP_ENDPTSETUPSTAT] = 0x0D8UL, - [OP_ENDPTPRIME] = 0x0DCUL, - [OP_ENDPTFLUSH] = 0x0E0UL, - [OP_ENDPTSTAT] = 0x0E4UL, - [OP_ENDPTCOMPLETE] = 0x0E8UL, - [OP_ENDPTCTRL] = 0x0ECUL, +static const u8 ci_regs_lpm[] = { + [CAP_CAPLENGTH] = 0x00U, + [CAP_HCCPARAMS] = 0x08U, + [CAP_DCCPARAMS] = 0x24U, + [CAP_TESTMODE] = 0xFCU, + [OP_USBCMD] = 0x00U, + [OP_USBSTS] = 0x04U, + [OP_USBINTR] = 0x08U, + [OP_DEVICEADDR] = 0x14U, + [OP_ENDPTLISTADDR] = 0x18U, + [OP_PORTSC] = 0x44U, + [OP_DEVLC] = 0x84U, + [OP_OTGSC] = 0xC4U, + [OP_USBMODE] = 0xC8U, + [OP_ENDPTSETUPSTAT] = 0xD8U, + [OP_ENDPTPRIME] = 0xDCU, + [OP_ENDPTFLUSH] = 0xE0U, + [OP_ENDPTSTAT] = 0xE4U, + [OP_ENDPTCOMPLETE] = 0xE8U, + [OP_ENDPTCTRL] = 0xECU, }; static int hw_alloc_regmap(struct ci_hdrc *ci, bool is_lpm) { int i; - kfree(ci->hw_bank.regmap); - - ci->hw_bank.regmap = kzalloc((OP_LAST + 1) * sizeof(void *), - GFP_KERNEL); - if (!ci->hw_bank.regmap) - return -ENOMEM; - for (i = 0; i < OP_ENDPTCTRL; i++) ci->hw_bank.regmap[i] = (i <= CAP_LAST ? ci->hw_bank.cap : ci->hw_bank.op) + @@ -208,7 +201,8 @@ static int hw_device_init(struct ci_hdrc *ci, void __iomem *base) reg = hw_read(ci, CAP_HCCPARAMS, HCCPARAMS_LEN) >> __ffs(HCCPARAMS_LEN); ci->hw_bank.lpm = reg; - hw_alloc_regmap(ci, !!reg); + if (reg) + hw_alloc_regmap(ci, !!reg); ci->hw_bank.size = ci->hw_bank.op - ci->hw_bank.abs; ci->hw_bank.size += OP_LAST; ci->hw_bank.size /= sizeof(u32); @@ -242,7 +236,7 @@ static int hw_device_init(struct ci_hdrc *ci, void __iomem *base) static void hw_phymode_configure(struct ci_hdrc *ci) { - u32 portsc, lpm, sts; + u32 portsc, lpm, sts = 0; switch (ci->platdata->phy_mode) { case USBPHY_INTERFACE_MODE_UTMI: @@ -272,10 +266,12 @@ static void hw_phymode_configure(struct ci_hdrc *ci) if (ci->hw_bank.lpm) { hw_write(ci, OP_DEVLC, DEVLC_PTS(7) | DEVLC_PTW, lpm); - hw_write(ci, OP_DEVLC, DEVLC_STS, sts); + if (sts) + hw_write(ci, OP_DEVLC, DEVLC_STS, DEVLC_STS); } else { hw_write(ci, OP_PORTSC, PORTSC_PTS(7) | PORTSC_PTW, portsc); - hw_write(ci, OP_PORTSC, PORTSC_STS, sts); + if (sts) + hw_write(ci, OP_PORTSC, PORTSC_STS, PORTSC_STS); } } @@ -554,6 +550,8 @@ static int ci_hdrc_probe(struct platform_device *pdev) ci->dev = dev; ci->platdata = dev->platform_data; + ci->imx28_write_fix = !!(ci->platdata->flags & + CI_HDRC_IMX28_WRITE_FIX); ret = hw_device_init(ci, base); if (ret < 0) { @@ -561,6 +559,8 @@ static int ci_hdrc_probe(struct platform_device *pdev) return -ENODEV; } + hw_phymode_configure(ci); + ret = ci_usb_phy_init(ci); if (ret) { dev_err(dev, "unable to init phy: %d\n", ret); @@ -578,8 +578,6 @@ static int ci_hdrc_probe(struct platform_device *pdev) ci_get_otg_capable(ci); - hw_phymode_configure(ci); - dr_mode = ci->platdata->dr_mode; /* initialize role(s) before the interrupt is requested */ if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) { @@ -642,6 +640,10 @@ static int ci_hdrc_probe(struct platform_device *pdev) : CI_ROLE_GADGET; } + /* only update vbus status for peripheral */ + if (ci->role == CI_ROLE_GADGET) + ci_handle_vbus_change(ci); + ret = ci_role_start(ci, ci->role); if (ret) { dev_err(dev, "can't start %s role\n", ci_role(ci)->name); @@ -676,7 +678,6 @@ static int ci_hdrc_remove(struct platform_device *pdev) ci_role_destroy(ci); ci_hdrc_enter_lpm(ci, true); ci_usb_phy_destroy(ci); - kfree(ci->hw_bank.regmap); return 0; } diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c index 59e6020..a8ac6c1 100644 --- a/drivers/usb/chipidea/host.c +++ b/drivers/usb/chipidea/host.c @@ -65,6 +65,7 @@ static int host_start(struct ci_hdrc *ci) ehci->caps = ci->hw_bank.cap; ehci->has_hostpc = ci->hw_bank.lpm; ehci->has_tdi_phy_lpm = ci->hw_bank.lpm; + ehci->imx28_write_fix = ci->imx28_write_fix; if (ci->platdata->reg_vbus) { ret = regulator_enable(ci->platdata->reg_vbus); @@ -88,7 +89,8 @@ static int host_start(struct ci_hdrc *ci) return ret; disable_reg: - regulator_disable(ci->platdata->reg_vbus); + if (ci->platdata->reg_vbus) + regulator_disable(ci->platdata->reg_vbus); put_hcd: usb_put_hcd(hcd); diff --git a/drivers/usb/chipidea/otg.h b/drivers/usb/chipidea/otg.h index 2d9f090..449bee0 100644 --- a/drivers/usb/chipidea/otg.h +++ b/drivers/usb/chipidea/otg.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Freescale Semiconductor, Inc. + * Copyright (C) 2013-2014 Freescale Semiconductor, Inc. * * Author: Peter Chen * @@ -19,12 +19,12 @@ static inline void ci_clear_otg_interrupt(struct ci_hdrc *ci, u32 bits) static inline void ci_enable_otg_interrupt(struct ci_hdrc *ci, u32 bits) { - hw_write(ci, OP_OTGSC, bits, bits); + hw_write(ci, OP_OTGSC, bits | OTGSC_INT_STATUS_BITS, bits); } static inline void ci_disable_otg_interrupt(struct ci_hdrc *ci, u32 bits) { - hw_write(ci, OP_OTGSC, bits, 0); + hw_write(ci, OP_OTGSC, bits | OTGSC_INT_STATUS_BITS, 0); } int ci_hdrc_otg_init(struct ci_hdrc *ci); diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index b34c819..80de2f8 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -393,6 +393,14 @@ static int add_td_to_list(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq, node->ptr->token = cpu_to_le32(length << __ffs(TD_TOTAL_BYTES)); node->ptr->token &= cpu_to_le32(TD_TOTAL_BYTES); node->ptr->token |= cpu_to_le32(TD_STATUS_ACTIVE); + if (hwep->type == USB_ENDPOINT_XFER_ISOC && hwep->dir == TX) { + u32 mul = hwreq->req.length / hwep->ep.maxpacket; + + if (hwreq->req.length == 0 + || hwreq->req.length % hwep->ep.maxpacket) + mul++; + node->ptr->token |= mul << __ffs(TD_MULTO); + } temp = (u32) (hwreq->req.dma + hwreq->req.actual); if (length) { @@ -515,10 +523,11 @@ static int _hardware_enqueue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) hwep->qh.ptr->td.token &= cpu_to_le32(~(TD_STATUS_HALTED|TD_STATUS_ACTIVE)); - if (hwep->type == USB_ENDPOINT_XFER_ISOC) { + if (hwep->type == USB_ENDPOINT_XFER_ISOC && hwep->dir == RX) { u32 mul = hwreq->req.length / hwep->ep.maxpacket; - if (hwreq->req.length % hwep->ep.maxpacket) + if (hwreq->req.length == 0 + || hwreq->req.length % hwep->ep.maxpacket) mul++; hwep->qh.ptr->cap |= mul << __ffs(QH_MULT); } @@ -1173,6 +1182,12 @@ static int ep_enable(struct usb_ep *ep, if (hwep->num) cap |= QH_ZLT; cap |= (hwep->ep.maxpacket << __ffs(QH_MAX_PKT)) & QH_MAX_PKT; + /* + * For ISO-TX, we set mult at QH as the largest value, and use + * MultO at TD as real mult value. + */ + if (hwep->type == USB_ENDPOINT_XFER_ISOC && hwep->dir == TX) + cap |= 3 << __ffs(QH_MULT); hwep->qh.ptr->cap = cpu_to_le32(cap); @@ -1566,7 +1581,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 +1601,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; } @@ -1795,9 +1810,6 @@ static int udc_start(struct ci_hdrc *ci) pm_runtime_no_callbacks(&ci->gadget.dev); pm_runtime_enable(&ci->gadget.dev); - /* Update ci->vbus_active */ - ci_handle_vbus_change(ci); - return retval; destroy_eps: diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index 8a1094b..cd061ab 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -21,6 +21,10 @@ #define MX25_USB_PHY_CTRL_OFFSET 0x08 #define MX25_BM_EXTERNAL_VBUS_DIVIDER BIT(23) +#define MX27_H1_PM_BIT BIT(8) +#define MX27_H2_PM_BIT BIT(16) +#define MX27_OTG_PM_BIT BIT(24) + #define MX53_USB_OTG_PHY_CTRL_0_OFFSET 0x08 #define MX53_USB_UH2_CTRL_OFFSET 0x14 #define MX53_USB_UH3_CTRL_OFFSET 0x18 @@ -68,6 +72,36 @@ static int usbmisc_imx25_post(struct imx_usbmisc_data *data) return 0; } +static int usbmisc_imx27_init(struct imx_usbmisc_data *data) +{ + unsigned long flags; + u32 val; + + switch (data->index) { + case 0: + val = MX27_OTG_PM_BIT; + break; + case 1: + val = MX27_H1_PM_BIT; + break; + case 2: + val = MX27_H2_PM_BIT; + break; + default: + return -EINVAL; + }; + + spin_lock_irqsave(&usbmisc->lock, flags); + if (data->disable_oc) + val = readl(usbmisc->base) | val; + else + val = readl(usbmisc->base) & ~val; + writel(val, usbmisc->base); + spin_unlock_irqrestore(&usbmisc->lock, flags); + + return 0; +} + static int usbmisc_imx53_init(struct imx_usbmisc_data *data) { void __iomem *reg = NULL; @@ -128,6 +162,10 @@ static const struct usbmisc_ops imx25_usbmisc_ops = { .post = usbmisc_imx25_post, }; +static const struct usbmisc_ops imx27_usbmisc_ops = { + .init = usbmisc_imx27_init, +}; + static const struct usbmisc_ops imx53_usbmisc_ops = { .init = usbmisc_imx53_init, }; @@ -162,6 +200,14 @@ static const struct of_device_id usbmisc_imx_dt_ids[] = { .data = &imx25_usbmisc_ops, }, { + .compatible = "fsl,imx27-usbmisc", + .data = &imx27_usbmisc_ops, + }, + { + .compatible = "fsl,imx51-usbmisc", + .data = &imx53_usbmisc_ops, + }, + { .compatible = "fsl,imx53-usbmisc", .data = &imx53_usbmisc_ops, }, diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index e840431..900f7ff 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -262,6 +262,7 @@ static void acm_ctrl_irq(struct urb *urb) struct usb_cdc_notification *dr = urb->transfer_buffer; unsigned char *data; int newctrl; + int difference; int retval; int status = urb->status; @@ -302,20 +303,31 @@ static void acm_ctrl_irq(struct urb *urb) tty_port_tty_hangup(&acm->port, false); } + difference = acm->ctrlin ^ newctrl; + spin_lock(&acm->read_lock); acm->ctrlin = newctrl; + acm->oldcount = acm->iocount; + + if (difference & ACM_CTRL_DSR) + acm->iocount.dsr++; + if (difference & ACM_CTRL_BRK) + acm->iocount.brk++; + if (difference & ACM_CTRL_RI) + acm->iocount.rng++; + if (difference & ACM_CTRL_DCD) + acm->iocount.dcd++; + if (difference & ACM_CTRL_FRAMING) + acm->iocount.frame++; + if (difference & ACM_CTRL_PARITY) + acm->iocount.parity++; + if (difference & ACM_CTRL_OVERRUN) + acm->iocount.overrun++; + spin_unlock(&acm->read_lock); + + if (difference) + wake_up_all(&acm->wioctl); - dev_dbg(&acm->control->dev, - "%s - input control lines: dcd%c dsr%c break%c " - "ring%c framing%c parity%c overrun%c\n", - __func__, - acm->ctrlin & ACM_CTRL_DCD ? '+' : '-', - acm->ctrlin & ACM_CTRL_DSR ? '+' : '-', - acm->ctrlin & ACM_CTRL_BRK ? '+' : '-', - acm->ctrlin & ACM_CTRL_RI ? '+' : '-', - acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-', - acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-', - acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-'); - break; + break; default: dev_dbg(&acm->control->dev, @@ -796,6 +808,72 @@ static int set_serial_info(struct acm *acm, return retval; } +static int wait_serial_change(struct acm *acm, unsigned long arg) +{ + int rv = 0; + DECLARE_WAITQUEUE(wait, current); + struct async_icount old, new; + + if (arg & (TIOCM_DSR | TIOCM_RI | TIOCM_CD )) + return -EINVAL; + do { + spin_lock_irq(&acm->read_lock); + old = acm->oldcount; + new = acm->iocount; + acm->oldcount = new; + spin_unlock_irq(&acm->read_lock); + + if ((arg & TIOCM_DSR) && + old.dsr != new.dsr) + break; + if ((arg & TIOCM_CD) && + old.dcd != new.dcd) + break; + if ((arg & TIOCM_RI) && + old.rng != new.rng) + break; + + add_wait_queue(&acm->wioctl, &wait); + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + remove_wait_queue(&acm->wioctl, &wait); + if (acm->disconnected) { + if (arg & TIOCM_CD) + break; + else + rv = -ENODEV; + } else { + if (signal_pending(current)) + rv = -ERESTARTSYS; + } + } while (!rv); + + + + return rv; +} + +static int get_serial_usage(struct acm *acm, + struct serial_icounter_struct __user *count) +{ + struct serial_icounter_struct icount; + int rv = 0; + + memset(&icount, 0, sizeof(icount)); + icount.dsr = acm->iocount.dsr; + icount.rng = acm->iocount.rng; + icount.dcd = acm->iocount.dcd; + icount.frame = acm->iocount.frame; + icount.overrun = acm->iocount.overrun; + icount.parity = acm->iocount.parity; + icount.brk = acm->iocount.brk; + + if (copy_to_user(count, &icount, sizeof(icount)) > 0) + rv = -EFAULT; + + return rv; +} + static int acm_tty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { @@ -809,6 +887,18 @@ static int acm_tty_ioctl(struct tty_struct *tty, case TIOCSSERIAL: rv = set_serial_info(acm, (struct serial_struct __user *) arg); break; + case TIOCMIWAIT: + rv = usb_autopm_get_interface(acm->control); + if (rv < 0) { + rv = -EIO; + break; + } + rv = wait_serial_change(acm, arg); + usb_autopm_put_interface(acm->control); + break; + case TIOCGICOUNT: + rv = get_serial_usage(acm, (struct serial_icounter_struct __user *) arg); + break; } return rv; @@ -1167,6 +1257,7 @@ made_compressed_probe: acm->readsize = readsize; acm->rx_buflimit = num_rx_buf; INIT_WORK(&acm->work, acm_softint); + init_waitqueue_head(&acm->wioctl); spin_lock_init(&acm->write_lock); spin_lock_init(&acm->read_lock); mutex_init(&acm->mutex); @@ -1383,6 +1474,7 @@ static void acm_disconnect(struct usb_interface *intf) device_remove_file(&acm->control->dev, &dev_attr_iCountryCodeRelDate); } + wake_up_all(&acm->wioctl); device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities); usb_set_intfdata(acm->control, NULL); usb_set_intfdata(acm->data, NULL); diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index 0f76e4a..e38dc78 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -106,6 +106,9 @@ struct acm { struct work_struct work; /* work queue entry for line discipline waking up */ unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */ unsigned int ctrlout; /* output control lines (DTR, RTS) */ + struct async_icount iocount; /* counters for control line changes */ + struct async_icount oldcount; /* for comparison of counter */ + wait_queue_head_t wioctl; /* for ioctl */ unsigned int writesize; /* max packet size for the output bulk endpoint */ unsigned int readsize,ctrlsize; /* buffer sizes for freeing */ unsigned int minor; /* acm minor number */ diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 4d38759..a051a7a 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -432,6 +432,38 @@ outnl: return rv < 0 ? rv : count; } +/* + * clear WDM_READ flag and possibly submit the read urb if resp_count + * is non-zero. + * + * Called with desc->iuspin locked + */ +static int clear_wdm_read_flag(struct wdm_device *desc) +{ + int rv = 0; + + clear_bit(WDM_READ, &desc->flags); + + /* submit read urb only if the device is waiting for it */ + if (!desc->resp_count || !--desc->resp_count) + goto out; + + set_bit(WDM_RESPONDING, &desc->flags); + spin_unlock_irq(&desc->iuspin); + rv = usb_submit_urb(desc->response, GFP_KERNEL); + spin_lock_irq(&desc->iuspin); + if (rv) { + dev_err(&desc->intf->dev, + "usb_submit_urb failed with result %d\n", rv); + + /* make sure the next notification trigger a submit */ + clear_bit(WDM_RESPONDING, &desc->flags); + desc->resp_count = 0; + } +out: + return rv; +} + static ssize_t wdm_read (struct file *file, char __user *buffer, size_t count, loff_t *ppos) { @@ -503,8 +535,10 @@ retry: if (!desc->reslength) { /* zero length read */ dev_dbg(&desc->intf->dev, "%s: zero length - clearing WDM_READ\n", __func__); - clear_bit(WDM_READ, &desc->flags); + rv = clear_wdm_read_flag(desc); spin_unlock_irq(&desc->iuspin); + if (rv < 0) + goto err; goto retry; } cntr = desc->length; @@ -526,37 +560,9 @@ retry: desc->length -= cntr; /* in case we had outstanding data */ - if (!desc->length) { - clear_bit(WDM_READ, &desc->flags); - - if (--desc->resp_count) { - set_bit(WDM_RESPONDING, &desc->flags); - spin_unlock_irq(&desc->iuspin); - - rv = usb_submit_urb(desc->response, GFP_KERNEL); - if (rv) { - dev_err(&desc->intf->dev, - "%s: usb_submit_urb failed with result %d\n", - __func__, rv); - spin_lock_irq(&desc->iuspin); - clear_bit(WDM_RESPONDING, &desc->flags); - spin_unlock_irq(&desc->iuspin); - - if (rv == -ENOMEM) { - rv = schedule_work(&desc->rxwork); - if (rv) - dev_err(&desc->intf->dev, "Cannot schedule work\n"); - } else { - spin_lock_irq(&desc->iuspin); - desc->resp_count = 0; - spin_unlock_irq(&desc->iuspin); - } - } - } else - spin_unlock_irq(&desc->iuspin); - } else - spin_unlock_irq(&desc->iuspin); - + if (!desc->length) + clear_wdm_read_flag(desc); + spin_unlock_irq(&desc->iuspin); rv = cntr; err: @@ -854,13 +860,11 @@ static int wdm_manage_power(struct usb_interface *intf, int on) { /* need autopm_get/put here to ensure the usbcore sees the new value */ int rv = usb_autopm_get_interface(intf); - if (rv < 0) - goto err; intf->needs_remote_wakeup = on; - usb_autopm_put_interface(intf); -err: - return rv; + if (!rv) + usb_autopm_put_interface(intf); + return 0; } static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id) diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index d4c47d5..0924ee4 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -52,7 +52,6 @@ #include <linux/sched.h> #include <linux/signal.h> #include <linux/poll.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/lp.h> #include <linux/mutex.h> diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 09de131..cfbec9c 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -21,7 +21,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile index 5e847ad..2f6f932 100644 --- a/drivers/usb/core/Makefile +++ b/drivers/usb/core/Makefile @@ -2,8 +2,6 @@ # Makefile for USB Core files and filesystem # -ccflags-$(CONFIG_USB_DEBUG) := -DDEBUG - usbcore-y := usb.o hub.o hcd.o urb.o message.o driver.o usbcore-y += config.o file.o buffer.o sysfs.o endpoint.o usbcore-y += devio.o notify.o generic.o quirks.o devices.o diff --git a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c index 2355974..684ef70 100644 --- a/drivers/usb/core/buffer.c +++ b/drivers/usb/core/buffer.c @@ -2,7 +2,7 @@ * DMA memory management for framework level HCD code (hc_driver) * * This implementation plugs in through generic "usb_bus" level methods, - * and should work with all USB controllers, regardles of bus type. + * and should work with all USB controllers, regardless of bus type. */ #include <linux/module.h> diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index a6b2cab..8d72f0c 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -3,7 +3,6 @@ #include <linux/usb/hcd.h> #include <linux/usb/quirks.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/device.h> #include <asm/byteorder.h> @@ -651,10 +650,6 @@ void usb_destroy_configuration(struct usb_device *dev) * * hub-only!! ... and only in reset path, or usb_new_device() * (used by real hubs and virtual root hubs) - * - * NOTE: if this is a WUSB device and is not authorized, we skip the - * whole thing. A non-authorized USB device has no - * configurations. */ int usb_get_configuration(struct usb_device *dev) { @@ -666,8 +661,6 @@ int usb_get_configuration(struct usb_device *dev) struct usb_config_descriptor *desc; cfgno = 0; - if (dev->authorized == 0) /* Not really an error */ - goto out_not_authorized; result = -ENOMEM; if (ncfg > USB_MAXCONFIG) { dev_warn(ddev, "too many configurations: %d, " @@ -751,7 +744,6 @@ int usb_get_configuration(struct usb_device *dev) err: kfree(desc); -out_not_authorized: dev->descriptor.bNumConfigurations = cfgno; err2: if (result == -ENOMEM) diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 967152a..90e18f6 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -118,7 +118,7 @@ module_param(usbfs_memory_mb, uint, 0644); MODULE_PARM_DESC(usbfs_memory_mb, "maximum MB allowed for usbfs buffers (0 = no limit)"); -/* Hard limit, necessary to avoid aithmetic overflow */ +/* Hard limit, necessary to avoid arithmetic overflow */ #define USBFS_XFER_MAX (UINT_MAX / 2 - 1000000) static atomic_t usbfs_memory_usage; /* Total memory currently allocated */ diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 47aade2..ab90a01 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -37,6 +37,7 @@ * and cause the driver to probe for all devices again. */ ssize_t usb_store_new_id(struct usb_dynids *dynids, + const struct usb_device_id *id_table, struct device_driver *driver, const char *buf, size_t count) { @@ -44,11 +45,12 @@ ssize_t usb_store_new_id(struct usb_dynids *dynids, u32 idVendor = 0; u32 idProduct = 0; unsigned int bInterfaceClass = 0; + u32 refVendor, refProduct; int fields = 0; int retval = 0; - fields = sscanf(buf, "%x %x %x", &idVendor, &idProduct, - &bInterfaceClass); + fields = sscanf(buf, "%x %x %x %x %x", &idVendor, &idProduct, + &bInterfaceClass, &refVendor, &refProduct); if (fields < 2) return -EINVAL; @@ -60,11 +62,36 @@ ssize_t usb_store_new_id(struct usb_dynids *dynids, dynid->id.idVendor = idVendor; dynid->id.idProduct = idProduct; dynid->id.match_flags = USB_DEVICE_ID_MATCH_DEVICE; - if (fields == 3) { + if (fields > 2 && bInterfaceClass) { + if (bInterfaceClass > 255) { + retval = -EINVAL; + goto fail; + } + dynid->id.bInterfaceClass = (u8)bInterfaceClass; dynid->id.match_flags |= USB_DEVICE_ID_MATCH_INT_CLASS; } + if (fields > 4) { + const struct usb_device_id *id = id_table; + + if (!id) { + retval = -ENODEV; + goto fail; + } + + for (; id->match_flags; id++) + if (id->idVendor == refVendor && id->idProduct == refProduct) + break; + + if (id->match_flags) { + dynid->id.driver_info = id->driver_info; + } else { + retval = -ENODEV; + goto fail; + } + } + spin_lock(&dynids->lock); list_add_tail(&dynid->node, &dynids->list); spin_unlock(&dynids->lock); @@ -74,6 +101,10 @@ ssize_t usb_store_new_id(struct usb_dynids *dynids, if (retval) return retval; return count; + +fail: + kfree(dynid); + return retval; } EXPORT_SYMBOL_GPL(usb_store_new_id); @@ -106,7 +137,7 @@ static ssize_t new_id_store(struct device_driver *driver, { struct usb_driver *usb_drv = to_usb_driver(driver); - return usb_store_new_id(&usb_drv->dynids, driver, buf, count); + return usb_store_new_id(&usb_drv->dynids, usb_drv->id_table, driver, buf, count); } static DRIVER_ATTR_RW(new_id); @@ -839,7 +870,7 @@ int usb_register_device_driver(struct usb_device_driver *new_udriver, return -ENODEV; new_udriver->drvwrap.for_devices = 1; - new_udriver->drvwrap.driver.name = (char *) new_udriver->name; + new_udriver->drvwrap.driver.name = new_udriver->name; new_udriver->drvwrap.driver.bus = &usb_bus_type; new_udriver->drvwrap.driver.probe = usb_probe_device; new_udriver->drvwrap.driver.remove = usb_unbind_device; @@ -900,7 +931,7 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner, return -ENODEV; new_driver->drvwrap.for_devices = 0; - new_driver->drvwrap.driver.name = (char *) new_driver->name; + new_driver->drvwrap.driver.name = new_driver->name; new_driver->drvwrap.driver.bus = &usb_bus_type; new_driver->drvwrap.driver.probe = usb_probe_interface; new_driver->drvwrap.driver.remove = usb_unbind_interface; diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index dfe9d0f..d59d993 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -282,6 +282,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) if (retval != 0) goto unmap_registers; + device_wakeup_enable(hcd->self.controller); if (pci_dev_run_wake(dev)) pm_runtime_put_noidle(&dev->dev); diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 6bffb8c..2518c32 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -44,6 +44,7 @@ #include <linux/usb.h> #include <linux/usb/hcd.h> +#include <linux/usb/phy.h> #include "usb.h" @@ -1031,7 +1032,6 @@ static int register_root_hub(struct usb_hcd *hcd) dev_name(&usb_dev->dev), retval); return retval; } - usb_dev->lpm_capable = usb_device_supports_lpm(usb_dev); } retval = usb_new_device (usb_dev); @@ -1297,7 +1297,7 @@ EXPORT_SYMBOL_GPL(usb_hcd_unlink_urb_from_ep); * DMA framework is dma_declare_coherent_memory() * * - So we use that, even though the primary requirement - * is that the memory be "local" (hence addressible + * is that the memory be "local" (hence addressable * by that device), not "coherent". * */ @@ -2588,6 +2588,24 @@ int usb_add_hcd(struct usb_hcd *hcd, int retval; struct usb_device *rhdev; + if (IS_ENABLED(CONFIG_USB_PHY) && !hcd->phy) { + struct usb_phy *phy = usb_get_phy_dev(hcd->self.controller, 0); + + if (IS_ERR(phy)) { + retval = PTR_ERR(phy); + if (retval == -EPROBE_DEFER) + return retval; + } else { + retval = usb_phy_init(phy); + if (retval) { + usb_put_phy(phy); + return retval; + } + hcd->phy = phy; + hcd->remove_phy = 1; + } + } + dev_info(hcd->self.controller, "%s\n", hcd->product_desc); /* Keep old behaviour if authorized_default is not in [0, 1]. */ @@ -2603,7 +2621,7 @@ int usb_add_hcd(struct usb_hcd *hcd, */ if ((retval = hcd_buffer_create(hcd)) != 0) { dev_dbg(hcd->self.controller, "pool alloc failed\n"); - return retval; + goto err_remove_phy; } if ((retval = usb_register_bus(&hcd->self)) < 0) @@ -2693,12 +2711,6 @@ int usb_add_hcd(struct usb_hcd *hcd, if (hcd->uses_new_polling && HCD_POLL_RH(hcd)) usb_hcd_poll_rh_status(hcd); - /* - * Host controllers don't generate their own wakeup requests; - * they only forward requests from the root hub. Therefore - * controllers should always be enabled for remote wakeup. - */ - device_wakeup_enable(hcd->self.controller); return retval; error_create_attr_group: @@ -2734,6 +2746,12 @@ err_allocate_root_hub: usb_deregister_bus(&hcd->self); err_register_bus: hcd_buffer_destroy(hcd); +err_remove_phy: + if (hcd->remove_phy && hcd->phy) { + usb_phy_shutdown(hcd->phy); + usb_put_phy(hcd->phy); + hcd->phy = NULL; + } return retval; } EXPORT_SYMBOL_GPL(usb_add_hcd); @@ -2806,6 +2824,11 @@ void usb_remove_hcd(struct usb_hcd *hcd) usb_put_dev(hcd->self.root_hub); usb_deregister_bus(&hcd->self); hcd_buffer_destroy(hcd); + if (hcd->remove_phy && hcd->phy) { + usb_phy_shutdown(hcd->phy); + usb_put_phy(hcd->phy); + hcd->phy = NULL; + } } EXPORT_SYMBOL_GPL(usb_remove_hcd); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index bd9dc35..64ea219 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -33,13 +33,6 @@ #include "hub.h" -/* if we are in debug mode, always announce new devices */ -#ifdef DEBUG -#ifndef CONFIG_USB_ANNOUNCE_NEW_DEVICES -#define CONFIG_USB_ANNOUNCE_NEW_DEVICES -#endif -#endif - #define USB_VENDOR_GENESYS_LOGIC 0x05e3 #define HUB_QUIRK_CHECK_PORT_AUTOSUSPEND 0x01 @@ -135,7 +128,7 @@ struct usb_hub *usb_hub_to_struct_hub(struct usb_device *hdev) return usb_get_intfdata(hdev->actconfig->interface[0]); } -int usb_device_supports_lpm(struct usb_device *udev) +static int usb_device_supports_lpm(struct usb_device *udev) { /* USB 2.1 (and greater) devices indicate LPM support through * their USB 2.0 Extended Capabilities BOS descriptor. @@ -156,11 +149,6 @@ int usb_device_supports_lpm(struct usb_device *udev) "Power management will be impacted.\n"); return 0; } - - /* udev is root hub */ - if (!udev->parent) - return 1; - if (udev->parent->lpm_capable) return 1; @@ -1154,7 +1142,8 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) /* Tell khubd to disconnect the device or * check for a new connection */ - if (udev || (portstatus & USB_PORT_STAT_CONNECTION)) + if (udev || (portstatus & USB_PORT_STAT_CONNECTION) || + (portstatus & USB_PORT_STAT_OVERCURRENT)) set_bit(port1, hub->change_bits); } else if (portstatus & USB_PORT_STAT_ENABLE) { @@ -1607,7 +1596,7 @@ static void hub_disconnect(struct usb_interface *intf) { struct usb_hub *hub = usb_get_intfdata(intf); struct usb_device *hdev = interface_to_usbdev(intf); - int i; + int port1; /* Take the hub off the event list and don't let it be added again */ spin_lock_irq(&hub_event_lock); @@ -1622,11 +1611,15 @@ static void hub_disconnect(struct usb_interface *intf) hub->error = 0; hub_quiesce(hub, HUB_DISCONNECT); - usb_set_intfdata (intf, NULL); + /* Avoid races with recursively_mark_NOTATTACHED() */ + spin_lock_irq(&device_state_lock); + port1 = hdev->maxchild; + hdev->maxchild = 0; + usb_set_intfdata(intf, NULL); + spin_unlock_irq(&device_state_lock); - for (i = 0; i < hdev->maxchild; i++) - usb_hub_remove_port_device(hub, i + 1); - hub->hdev->maxchild = 0; + for (; port1 > 0; --port1) + usb_hub_remove_port_device(hub, port1); if (hub->hdev->speed == USB_SPEED_HIGH) highspeed_hubs--; @@ -2235,17 +2228,13 @@ static int usb_enumerate_device(struct usb_device *udev) return err; } } - if (udev->wusb == 1 && udev->authorized == 0) { - udev->product = kstrdup("n/a (unauthorized)", GFP_KERNEL); - udev->manufacturer = kstrdup("n/a (unauthorized)", GFP_KERNEL); - udev->serial = kstrdup("n/a (unauthorized)", GFP_KERNEL); - } else { - /* read the standard strings and cache them if present */ - udev->product = usb_cache_string(udev, udev->descriptor.iProduct); - udev->manufacturer = usb_cache_string(udev, - udev->descriptor.iManufacturer); - udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber); - } + + /* read the standard strings and cache them if present */ + udev->product = usb_cache_string(udev, udev->descriptor.iProduct); + udev->manufacturer = usb_cache_string(udev, + udev->descriptor.iManufacturer); + udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber); + err = usb_enumerate_device_otg(udev); if (err < 0) return err; @@ -2427,16 +2416,6 @@ int usb_deauthorize_device(struct usb_device *usb_dev) usb_dev->authorized = 0; usb_set_configuration(usb_dev, -1); - kfree(usb_dev->product); - usb_dev->product = kstrdup("n/a (unauthorized)", GFP_KERNEL); - kfree(usb_dev->manufacturer); - usb_dev->manufacturer = kstrdup("n/a (unauthorized)", GFP_KERNEL); - kfree(usb_dev->serial); - usb_dev->serial = kstrdup("n/a (unauthorized)", GFP_KERNEL); - - usb_destroy_configuration(usb_dev); - usb_dev->descriptor.bNumConfigurations = 0; - out_unauthorized: usb_unlock_device(usb_dev); return 0; @@ -2464,17 +2443,7 @@ int usb_authorize_device(struct usb_device *usb_dev) goto error_device_descriptor; } - kfree(usb_dev->product); - usb_dev->product = NULL; - kfree(usb_dev->manufacturer); - usb_dev->manufacturer = NULL; - kfree(usb_dev->serial); - usb_dev->serial = NULL; - usb_dev->authorized = 1; - result = usb_enumerate_device(usb_dev); - if (result < 0) - goto error_enumerate; /* Choose and set the configuration. This registers the interfaces * with the driver core and lets interface drivers bind to them. */ @@ -2490,7 +2459,6 @@ int usb_authorize_device(struct usb_device *usb_dev) } dev_info(&usb_dev->dev, "authorized to connect\n"); -error_enumerate: error_device_descriptor: usb_autosuspend_device(usb_dev); error_autoresume: @@ -2523,10 +2491,25 @@ static unsigned hub_is_wusb(struct usb_hub *hub) #define HUB_LONG_RESET_TIME 200 #define HUB_RESET_TIMEOUT 800 +/* + * "New scheme" enumeration causes an extra state transition to be + * exposed to an xhci host and causes USB3 devices to receive control + * commands in the default state. This has been seen to cause + * enumeration failures, so disable this enumeration scheme for USB3 + * devices. + */ +static bool use_new_scheme(struct usb_device *udev, int retry) +{ + if (udev->speed == USB_SPEED_SUPER) + return false; + + return USE_NEW_SCHEME(retry); +} + static int hub_port_reset(struct usb_hub *hub, int port1, struct usb_device *udev, unsigned int delay, bool warm); -/* Is a USB 3.0 port in the Inactive or Complinance Mode state? +/* Is a USB 3.0 port in the Inactive or Compliance Mode state? * Port worm reset is required to recover */ static bool hub_port_warm_reset_required(struct usb_hub *hub, u16 portstatus) @@ -3334,7 +3317,8 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) udev = hub->ports[port1 - 1]->child; if (udev && udev->can_submit) { - dev_warn(&intf->dev, "port %d nyet suspended\n", port1); + dev_warn(&intf->dev, "port %d not suspended yet\n", + port1); if (PMSG_IS_AUTO(msg)) return -EBUSY; } @@ -3981,6 +3965,20 @@ static void hub_set_initial_usb2_lpm_policy(struct usb_device *udev) } } +static int hub_enable_device(struct usb_device *udev) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + if (!hcd->driver->enable_device) + return 0; + if (udev->state == USB_STATE_ADDRESS) + return 0; + if (udev->state != USB_STATE_DEFAULT) + return -EINVAL; + + return hcd->driver->enable_device(hcd, udev); +} + /* Reset device, (re)assign address, get device descriptor. * Device connection must be stable, no more debouncing needed. * Returns device in USB_STATE_ADDRESS, except on error. @@ -4093,7 +4091,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, * this area, and this is how Linux has done it for ages. * Change it cautiously. * - * NOTE: If USE_NEW_SCHEME() is true we will start by issuing + * NOTE: If use_new_scheme() is true we will start by issuing * a 64-byte GET_DESCRIPTOR request. This is what Windows does, * so it may help with some non-standards-compliant devices. * Otherwise we start with SET_ADDRESS and then try to read the @@ -4101,10 +4099,17 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, * value. */ for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) { - if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3)) { + bool did_new_scheme = false; + + if (use_new_scheme(udev, retry_counter)) { struct usb_device_descriptor *buf; int r = 0; + did_new_scheme = true; + retval = hub_enable_device(udev); + if (retval < 0) + goto fail; + #define GET_DESCRIPTOR_BUFSIZE 64 buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO); if (!buf) { @@ -4193,7 +4198,11 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, * - read ep0 maxpacket even for high and low speed, */ msleep(10); - if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3)) + /* use_new_scheme() checks the speed which may have + * changed since the initial look so we cache the result + * in did_new_scheme + */ + if (did_new_scheme) break; } @@ -4900,7 +4909,7 @@ static void hub_events(void) static int hub_thread(void *__unused) { - /* khubd needs to be freezable to avoid intefering with USB-PERSIST + /* khubd needs to be freezable to avoid interfering with USB-PERSIST * port handover. Otherwise it might see that a full-speed device * was gone before the EHCI controller had handed its port over to * the companion full-speed controller. diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index 4e4790d..df629a3 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h @@ -78,7 +78,7 @@ struct usb_hub { /** * struct usb port - kernel's representation of a usb port - * @child: usb device attatched to the port + * @child: usb device attached to the port * @dev: generic device interface * @port_owner: port's owner * @connect_type: port's connect type diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 874d1a4..5239e51 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -6,7 +6,6 @@ #include <linux/usb.h> #include <linux/module.h> #include <linux/slab.h> -#include <linux/init.h> #include <linux/mm.h> #include <linux/timer.h> #include <linux/ctype.h> @@ -218,7 +217,7 @@ EXPORT_SYMBOL_GPL(usb_interrupt_msg); * * Return: * If successful, 0. Otherwise a negative error number. The number of actual - * bytes transferred will be stored in the @actual_length paramater. + * bytes transferred will be stored in the @actual_length parameter. * */ int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, @@ -518,7 +517,7 @@ void usb_sg_wait(struct usb_sg_request *io) io->urbs[i]->dev = io->dev; retval = usb_submit_urb(io->urbs[i], GFP_ATOMIC); - /* after we submit, let completions or cancelations fire; + /* after we submit, let completions or cancellations fire; * we handshake using io->status. */ spin_unlock_irq(&io->lock); diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 12924db..8f37063 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -98,9 +98,6 @@ static const struct usb_device_id usb_quirk_list[] = { /* Alcor Micro Corp. Hub */ { USB_DEVICE(0x058f, 0x9254), .driver_info = USB_QUIRK_RESET_RESUME }, - /* MicroTouch Systems touchscreen */ - { USB_DEVICE(0x0596, 0x051e), .driver_info = USB_QUIRK_RESET_RESUME }, - /* appletouch */ { USB_DEVICE(0x05ac, 0x021a), .driver_info = USB_QUIRK_RESET_RESUME }, diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 52a97ad..1236c60 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -837,7 +837,7 @@ void usb_remove_sysfs_dev_files(struct usb_device *udev) device_remove_bin_file(dev, &dev_bin_attr_descriptors); } -/* Interface Accociation Descriptor fields */ +/* Interface Association Descriptor fields */ #define usb_intf_assoc_attr(field, format_string) \ static ssize_t \ iad_##field##_show(struct device *dev, struct device_attribute *attr, \ diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index e726f5e..991386c 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -2,7 +2,6 @@ #include <linux/string.h> #include <linux/bitops.h> #include <linux/slab.h> -#include <linux/init.h> #include <linux/log2.h> #include <linux/usb.h> #include <linux/wait.h> @@ -53,7 +52,7 @@ EXPORT_SYMBOL_GPL(usb_init_urb); * valid options for this. * * Creates an urb for the USB driver to use, initializes a few internal - * structures, incrementes the usage counter, and returns a pointer to it. + * structures, increments the usage counter, and returns a pointer to it. * * If the driver want to use this urb for interrupt, control, or bulk * endpoints, pass '0' as the number of iso packets. @@ -281,7 +280,7 @@ EXPORT_SYMBOL_GPL(usb_unanchor_urb); * * Device drivers must explicitly request that repetition, by ensuring that * some URB is always on the endpoint's queue (except possibly for short - * periods during completion callacks). When there is no longer an urb + * periods during completion callbacks). When there is no longer an urb * queued, the endpoint's bandwidth reservation is canceled. This means * drivers can use their completion handlers to ensure they keep bandwidth * they need, by reinitializing and resubmitting the just-completed urb @@ -325,10 +324,14 @@ EXPORT_SYMBOL_GPL(usb_unanchor_urb); */ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) { + static int pipetypes[4] = { + PIPE_CONTROL, PIPE_ISOCHRONOUS, PIPE_BULK, PIPE_INTERRUPT + }; int xfertype, max; struct usb_device *dev; struct usb_host_endpoint *ep; int is_out; + unsigned int allowed; if (!urb || !urb->complete) return -EINVAL; @@ -436,15 +439,10 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) if (urb->transfer_buffer_length > INT_MAX) return -EMSGSIZE; -#ifdef DEBUG - /* stuff that drivers shouldn't do, but which shouldn't + /* + * stuff that drivers shouldn't do, but which shouldn't * cause problems in HCDs if they get it wrong. */ - { - unsigned int allowed; - static int pipetypes[4] = { - PIPE_CONTROL, PIPE_ISOCHRONOUS, PIPE_BULK, PIPE_INTERRUPT - }; /* Check that the pipe's type matches the endpoint's type */ if (usb_pipetype(urb->pipe) != pipetypes[xfertype]) @@ -476,8 +474,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) if (allowed != urb->transfer_flags) dev_WARN(&dev->dev, "BOGUS urb flags, %x --> %x\n", urb->transfer_flags, allowed); - } -#endif + /* * Force periodic transfer intervals to be legal values that are * a power of two (so HCDs don't need to). @@ -492,9 +489,9 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) /* too small? */ switch (dev->speed) { case USB_SPEED_WIRELESS: - if (urb->interval < 6) + if ((urb->interval < 6) + && (xfertype == USB_ENDPOINT_XFER_INT)) return -EINVAL; - break; default: if (urb->interval <= 0) return -EINVAL; diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c index 4e243c3..5ca4070 100644 --- a/drivers/usb/core/usb-acpi.c +++ b/drivers/usb/core/usb-acpi.c @@ -16,7 +16,6 @@ #include <linux/acpi.h> #include <linux/pci.h> #include <linux/usb/hcd.h> -#include <acpi/acpi_bus.h> #include "usb.h" @@ -92,7 +91,7 @@ static int usb_acpi_check_port_connect_type(struct usb_device *hdev, int ret = 0; /* - * Accoding to ACPI Spec 9.13. PLD indicates whether usb port is + * According to ACPI Spec 9.13. PLD indicates whether usb port is * user visible and _UPC indicates whether it is connectable. If * the port was visible and connectable, it could be freely connected * and disconnected with USB devices. If no visible and connectable, @@ -127,7 +126,7 @@ out: return ret; } -static int usb_acpi_find_device(struct device *dev, acpi_handle *handle) +static struct acpi_device *usb_acpi_find_companion(struct device *dev) { struct usb_device *udev; acpi_handle *parent_handle; @@ -169,16 +168,15 @@ static int usb_acpi_find_device(struct device *dev, acpi_handle *handle) break; } - return -ENODEV; + return NULL; } /* root hub's parent is the usb hcd. */ - parent_handle = ACPI_HANDLE(dev->parent); - *handle = acpi_get_child(parent_handle, udev->portnum); - if (!*handle) - return -ENODEV; - return 0; + return acpi_find_child_device(ACPI_COMPANION(dev->parent), + udev->portnum, false); } else if (is_usb_port(dev)) { + struct acpi_device *adev = NULL; + sscanf(dev_name(dev), "port%d", &port_num); /* Get the struct usb_device point of port's hub */ udev = to_usb_device(dev->parent->parent); @@ -194,26 +192,27 @@ static int usb_acpi_find_device(struct device *dev, acpi_handle *handle) raw_port_num = usb_hcd_find_raw_port_number(hcd, port_num); - *handle = acpi_get_child(ACPI_HANDLE(&udev->dev), - raw_port_num); - if (!*handle) - return -ENODEV; + adev = acpi_find_child_device(ACPI_COMPANION(&udev->dev), + raw_port_num, false); + if (!adev) + return NULL; } else { parent_handle = usb_get_hub_port_acpi_handle(udev->parent, udev->portnum); if (!parent_handle) - return -ENODEV; + return NULL; - *handle = acpi_get_child(parent_handle, port_num); - if (!*handle) - return -ENODEV; + acpi_bus_get_device(parent_handle, &adev); + adev = acpi_find_child_device(adev, port_num, false); + if (!adev) + return NULL; } - usb_acpi_check_port_connect_type(udev, *handle, port_num); - } else - return -ENODEV; + usb_acpi_check_port_connect_type(udev, adev->handle, port_num); + return adev; + } - return 0; + return NULL; } static bool usb_acpi_bus_match(struct device *dev) @@ -224,7 +223,7 @@ static bool usb_acpi_bus_match(struct device *dev) static struct acpi_bus_type usb_acpi_bus = { .name = "USB", .match = usb_acpi_bus_match, - .find_device = usb_acpi_find_device, + .find_companion = usb_acpi_find_companion, }; int usb_acpi_register(void) diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index c493836..8238577 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -35,7 +35,6 @@ extern int usb_get_device_descriptor(struct usb_device *dev, unsigned int size); extern int usb_get_bos_descriptor(struct usb_device *dev); extern void usb_release_bos_descriptor(struct usb_device *dev); -extern int usb_device_supports_lpm(struct usb_device *udev); extern char *usb_cache_string(struct usb_device *udev, int index); extern int usb_set_configuration(struct usb_device *dev, int configuration); extern int usb_choose_configuration(struct usb_device *udev); diff --git a/drivers/usb/dwc2/Kconfig b/drivers/usb/dwc2/Kconfig new file mode 100644 index 0000000..be947d6 --- /dev/null +++ b/drivers/usb/dwc2/Kconfig @@ -0,0 +1,53 @@ +config USB_DWC2 + tristate "DesignWare USB2 DRD Core Support" + depends on USB + help + Say Y or M here if your system has a Dual Role HighSpeed + USB controller based on the DesignWare HSOTG IP Core. + + If you choose to build this driver as dynamically linked + modules, the core module will be called dwc2.ko, the + PCI bus interface module (if you have a PCI bus system) + will be called dwc2_pci.ko and the platform interface module + (for controllers directly connected to the CPU) will be called + dwc2_platform.ko. + + NOTE: This driver at present only implements the Host mode + of the controller. The existing s3c-hsotg driver supports + Peripheral mode, but only for the Samsung S3C platforms. + There are plans to merge the s3c-hsotg driver with this + driver in the near future to create a dual-role driver. + +if USB_DWC2 + +config USB_DWC2_DEBUG + bool "Enable Debugging Messages" + help + Say Y here to enable debugging messages in the DWC2 Driver. + +config USB_DWC2_VERBOSE + bool "Enable Verbose Debugging Messages" + depends on USB_DWC2_DEBUG + help + Say Y here to enable verbose debugging messages in the DWC2 Driver. + WARNING: Enabling this will quickly fill your message log. + If in doubt, say N. + +config USB_DWC2_TRACK_MISSED_SOFS + bool "Enable Missed SOF Tracking" + help + Say Y here to enable logging of missed SOF events to the dmesg log. + WARNING: This feature is still experimental. + If in doubt, say N. + +config USB_DWC2_DEBUG_PERIODIC + bool "Enable Debugging Messages For Periodic Transfers" + depends on USB_DWC2_DEBUG || USB_DWC2_VERBOSE + default y + help + Say N here to disable (verbose) debugging messages to be + logged for periodic transfers. This allows better debugging of + non-periodic transfers, but of course the debug logs will be + incomplete. Note that this also disables some debug messages + for which the transfer type cannot be deduced. +endif diff --git a/drivers/usb/dwc2/Makefile b/drivers/usb/dwc2/Makefile new file mode 100644 index 0000000..11529d3 --- /dev/null +++ b/drivers/usb/dwc2/Makefile @@ -0,0 +1,25 @@ +ccflags-$(CONFIG_USB_DWC2_DEBUG) += -DDEBUG +ccflags-$(CONFIG_USB_DWC2_VERBOSE) += -DVERBOSE_DEBUG + +obj-$(CONFIG_USB_DWC2) += dwc2.o + +dwc2-y += core.o core_intr.o + +# NOTE: This driver at present only implements the Host mode +# of the controller. The existing s3c-hsotg driver supports +# Peripheral mode, but only for the Samsung S3C platforms. +# There are plans to merge the s3c-hsotg driver with this +# driver in the near future to create a dual-role driver. Once +# that is done, Host mode will become an optional feature that +# is selected with a config option. + +dwc2-y += hcd.o hcd_intr.o +dwc2-y += hcd_queue.o hcd_ddma.o + +ifneq ($(CONFIG_PCI),) + obj-$(CONFIG_USB_DWC2) += dwc2_pci.o +endif +obj-$(CONFIG_USB_DWC2) += dwc2_platform.o + +dwc2_pci-y += pci.o +dwc2_platform-y += platform.o diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c new file mode 100644 index 0000000..1d12988 --- /dev/null +++ b/drivers/usb/dwc2/core.c @@ -0,0 +1,2777 @@ +/* + * core.c - DesignWare HS OTG Controller common routines + * + * Copyright (C) 2004-2013 Synopsys, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * The Core code provides basic services for accessing and managing the + * DWC_otg hardware. These services are used by both the Host Controller + * Driver and the Peripheral Controller Driver. + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/dma-mapping.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/usb.h> + +#include <linux/usb/hcd.h> +#include <linux/usb/ch11.h> + +#include "core.h" +#include "hcd.h" + +/** + * dwc2_enable_common_interrupts() - Initializes the commmon interrupts, + * used in both device and host modes + * + * @hsotg: Programming view of the DWC_otg controller + */ +static void dwc2_enable_common_interrupts(struct dwc2_hsotg *hsotg) +{ + u32 intmsk; + + /* Clear any pending OTG Interrupts */ + writel(0xffffffff, hsotg->regs + GOTGINT); + + /* Clear any pending interrupts */ + writel(0xffffffff, hsotg->regs + GINTSTS); + + /* Enable the interrupts in the GINTMSK */ + intmsk = GINTSTS_MODEMIS | GINTSTS_OTGINT; + + if (hsotg->core_params->dma_enable <= 0) + intmsk |= GINTSTS_RXFLVL; + + intmsk |= GINTSTS_CONIDSTSCHNG | GINTSTS_WKUPINT | GINTSTS_USBSUSP | + GINTSTS_SESSREQINT; + + writel(intmsk, hsotg->regs + GINTMSK); +} + +/* + * Initializes the FSLSPClkSel field of the HCFG register depending on the + * PHY type + */ +static void dwc2_init_fs_ls_pclk_sel(struct dwc2_hsotg *hsotg) +{ + u32 hcfg, val; + + if ((hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI && + hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED && + hsotg->core_params->ulpi_fs_ls > 0) || + hsotg->core_params->phy_type == DWC2_PHY_TYPE_PARAM_FS) { + /* Full speed PHY */ + val = HCFG_FSLSPCLKSEL_48_MHZ; + } else { + /* High speed PHY running at full speed or high speed */ + val = HCFG_FSLSPCLKSEL_30_60_MHZ; + } + + dev_dbg(hsotg->dev, "Initializing HCFG.FSLSPClkSel to %08x\n", val); + hcfg = readl(hsotg->regs + HCFG); + hcfg &= ~HCFG_FSLSPCLKSEL_MASK; + hcfg |= val << HCFG_FSLSPCLKSEL_SHIFT; + writel(hcfg, hsotg->regs + HCFG); +} + +/* + * Do core a soft reset of the core. Be careful with this because it + * resets all the internal state machines of the core. + */ +static int dwc2_core_reset(struct dwc2_hsotg *hsotg) +{ + u32 greset; + int count = 0; + + dev_vdbg(hsotg->dev, "%s()\n", __func__); + + /* Wait for AHB master IDLE state */ + do { + usleep_range(20000, 40000); + greset = readl(hsotg->regs + GRSTCTL); + if (++count > 50) { + dev_warn(hsotg->dev, + "%s() HANG! AHB Idle GRSTCTL=%0x\n", + __func__, greset); + return -EBUSY; + } + } while (!(greset & GRSTCTL_AHBIDLE)); + + /* Core Soft Reset */ + count = 0; + greset |= GRSTCTL_CSFTRST; + writel(greset, hsotg->regs + GRSTCTL); + do { + usleep_range(20000, 40000); + greset = readl(hsotg->regs + GRSTCTL); + if (++count > 50) { + dev_warn(hsotg->dev, + "%s() HANG! Soft Reset GRSTCTL=%0x\n", + __func__, greset); + return -EBUSY; + } + } while (greset & GRSTCTL_CSFTRST); + + /* + * NOTE: This long sleep is _very_ important, otherwise the core will + * not stay in host mode after a connector ID change! + */ + usleep_range(150000, 200000); + + return 0; +} + +static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) +{ + u32 usbcfg, i2cctl; + int retval = 0; + + /* + * core_init() is now called on every switch so only call the + * following for the first time through + */ + if (select_phy) { + dev_dbg(hsotg->dev, "FS PHY selected\n"); + usbcfg = readl(hsotg->regs + GUSBCFG); + usbcfg |= GUSBCFG_PHYSEL; + writel(usbcfg, hsotg->regs + GUSBCFG); + + /* Reset after a PHY select */ + retval = dwc2_core_reset(hsotg); + if (retval) { + dev_err(hsotg->dev, "%s() Reset failed, aborting", + __func__); + return retval; + } + } + + /* + * Program DCFG.DevSpd or HCFG.FSLSPclkSel to 48Mhz in FS. Also + * do this on HNP Dev/Host mode switches (done in dev_init and + * host_init). + */ + if (dwc2_is_host_mode(hsotg)) + dwc2_init_fs_ls_pclk_sel(hsotg); + + if (hsotg->core_params->i2c_enable > 0) { + dev_dbg(hsotg->dev, "FS PHY enabling I2C\n"); + + /* Program GUSBCFG.OtgUtmiFsSel to I2C */ + usbcfg = readl(hsotg->regs + GUSBCFG); + usbcfg |= GUSBCFG_OTG_UTMI_FS_SEL; + writel(usbcfg, hsotg->regs + GUSBCFG); + + /* Program GI2CCTL.I2CEn */ + i2cctl = readl(hsotg->regs + GI2CCTL); + i2cctl &= ~GI2CCTL_I2CDEVADDR_MASK; + i2cctl |= 1 << GI2CCTL_I2CDEVADDR_SHIFT; + i2cctl &= ~GI2CCTL_I2CEN; + writel(i2cctl, hsotg->regs + GI2CCTL); + i2cctl |= GI2CCTL_I2CEN; + writel(i2cctl, hsotg->regs + GI2CCTL); + } + + return retval; +} + +static int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) +{ + u32 usbcfg; + int retval = 0; + + if (!select_phy) + return 0; + + usbcfg = readl(hsotg->regs + GUSBCFG); + + /* + * HS PHY parameters. These parameters are preserved during soft reset + * so only program the first time. Do a soft reset immediately after + * setting phyif. + */ + switch (hsotg->core_params->phy_type) { + case DWC2_PHY_TYPE_PARAM_ULPI: + /* ULPI interface */ + dev_dbg(hsotg->dev, "HS ULPI PHY selected\n"); + usbcfg |= GUSBCFG_ULPI_UTMI_SEL; + usbcfg &= ~(GUSBCFG_PHYIF16 | GUSBCFG_DDRSEL); + if (hsotg->core_params->phy_ulpi_ddr > 0) + usbcfg |= GUSBCFG_DDRSEL; + break; + case DWC2_PHY_TYPE_PARAM_UTMI: + /* UTMI+ interface */ + dev_dbg(hsotg->dev, "HS UTMI+ PHY selected\n"); + usbcfg &= ~(GUSBCFG_ULPI_UTMI_SEL | GUSBCFG_PHYIF16); + if (hsotg->core_params->phy_utmi_width == 16) + usbcfg |= GUSBCFG_PHYIF16; + break; + default: + dev_err(hsotg->dev, "FS PHY selected at HS!\n"); + break; + } + + writel(usbcfg, hsotg->regs + GUSBCFG); + + /* Reset after setting the PHY parameters */ + retval = dwc2_core_reset(hsotg); + if (retval) { + dev_err(hsotg->dev, "%s() Reset failed, aborting", + __func__); + return retval; + } + + return retval; +} + +static int dwc2_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) +{ + u32 usbcfg; + int retval = 0; + + if (hsotg->core_params->speed == DWC2_SPEED_PARAM_FULL && + hsotg->core_params->phy_type == DWC2_PHY_TYPE_PARAM_FS) { + /* If FS mode with FS PHY */ + retval = dwc2_fs_phy_init(hsotg, select_phy); + if (retval) + return retval; + } else { + /* High speed PHY */ + retval = dwc2_hs_phy_init(hsotg, select_phy); + if (retval) + return retval; + } + + if (hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI && + hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED && + hsotg->core_params->ulpi_fs_ls > 0) { + dev_dbg(hsotg->dev, "Setting ULPI FSLS\n"); + usbcfg = readl(hsotg->regs + GUSBCFG); + usbcfg |= GUSBCFG_ULPI_FS_LS; + usbcfg |= GUSBCFG_ULPI_CLK_SUSP_M; + writel(usbcfg, hsotg->regs + GUSBCFG); + } else { + usbcfg = readl(hsotg->regs + GUSBCFG); + usbcfg &= ~GUSBCFG_ULPI_FS_LS; + usbcfg &= ~GUSBCFG_ULPI_CLK_SUSP_M; + writel(usbcfg, hsotg->regs + GUSBCFG); + } + + return retval; +} + +static int dwc2_gahbcfg_init(struct dwc2_hsotg *hsotg) +{ + u32 ahbcfg = readl(hsotg->regs + GAHBCFG); + + switch (hsotg->hw_params.arch) { + case GHWCFG2_EXT_DMA_ARCH: + dev_err(hsotg->dev, "External DMA Mode not supported\n"); + return -EINVAL; + + case GHWCFG2_INT_DMA_ARCH: + dev_dbg(hsotg->dev, "Internal DMA Mode\n"); + if (hsotg->core_params->ahbcfg != -1) { + ahbcfg &= GAHBCFG_CTRL_MASK; + ahbcfg |= hsotg->core_params->ahbcfg & + ~GAHBCFG_CTRL_MASK; + } + break; + + case GHWCFG2_SLAVE_ONLY_ARCH: + default: + dev_dbg(hsotg->dev, "Slave Only Mode\n"); + break; + } + + dev_dbg(hsotg->dev, "dma_enable:%d dma_desc_enable:%d\n", + hsotg->core_params->dma_enable, + hsotg->core_params->dma_desc_enable); + + if (hsotg->core_params->dma_enable > 0) { + if (hsotg->core_params->dma_desc_enable > 0) + dev_dbg(hsotg->dev, "Using Descriptor DMA mode\n"); + else + dev_dbg(hsotg->dev, "Using Buffer DMA mode\n"); + } else { + dev_dbg(hsotg->dev, "Using Slave mode\n"); + hsotg->core_params->dma_desc_enable = 0; + } + + if (hsotg->core_params->dma_enable > 0) + ahbcfg |= GAHBCFG_DMA_EN; + + writel(ahbcfg, hsotg->regs + GAHBCFG); + + return 0; +} + +static void dwc2_gusbcfg_init(struct dwc2_hsotg *hsotg) +{ + u32 usbcfg; + + usbcfg = readl(hsotg->regs + GUSBCFG); + usbcfg &= ~(GUSBCFG_HNPCAP | GUSBCFG_SRPCAP); + + switch (hsotg->hw_params.op_mode) { + case GHWCFG2_OP_MODE_HNP_SRP_CAPABLE: + if (hsotg->core_params->otg_cap == + DWC2_CAP_PARAM_HNP_SRP_CAPABLE) + usbcfg |= GUSBCFG_HNPCAP; + if (hsotg->core_params->otg_cap != + DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE) + usbcfg |= GUSBCFG_SRPCAP; + break; + + case GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE: + case GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE: + case GHWCFG2_OP_MODE_SRP_CAPABLE_HOST: + if (hsotg->core_params->otg_cap != + DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE) + usbcfg |= GUSBCFG_SRPCAP; + break; + + case GHWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE: + case GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE: + case GHWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST: + default: + break; + } + + writel(usbcfg, hsotg->regs + GUSBCFG); +} + +/** + * dwc2_core_init() - Initializes the DWC_otg controller registers and + * prepares the core for device mode or host mode operation + * + * @hsotg: Programming view of the DWC_otg controller + * @select_phy: If true then also set the Phy type + * @irq: If >= 0, the irq to register + */ +int dwc2_core_init(struct dwc2_hsotg *hsotg, bool select_phy, int irq) +{ + u32 usbcfg, otgctl; + int retval; + + dev_dbg(hsotg->dev, "%s(%p)\n", __func__, hsotg); + + usbcfg = readl(hsotg->regs + GUSBCFG); + + /* Set ULPI External VBUS bit if needed */ + usbcfg &= ~GUSBCFG_ULPI_EXT_VBUS_DRV; + if (hsotg->core_params->phy_ulpi_ext_vbus == + DWC2_PHY_ULPI_EXTERNAL_VBUS) + usbcfg |= GUSBCFG_ULPI_EXT_VBUS_DRV; + + /* Set external TS Dline pulsing bit if needed */ + usbcfg &= ~GUSBCFG_TERMSELDLPULSE; + if (hsotg->core_params->ts_dline > 0) + usbcfg |= GUSBCFG_TERMSELDLPULSE; + + writel(usbcfg, hsotg->regs + GUSBCFG); + + /* Reset the Controller */ + retval = dwc2_core_reset(hsotg); + if (retval) { + dev_err(hsotg->dev, "%s(): Reset failed, aborting\n", + __func__); + return retval; + } + + /* + * This needs to happen in FS mode before any other programming occurs + */ + retval = dwc2_phy_init(hsotg, select_phy); + if (retval) + return retval; + + /* Program the GAHBCFG Register */ + retval = dwc2_gahbcfg_init(hsotg); + if (retval) + return retval; + + /* Program the GUSBCFG register */ + dwc2_gusbcfg_init(hsotg); + + /* Program the GOTGCTL register */ + otgctl = readl(hsotg->regs + GOTGCTL); + otgctl &= ~GOTGCTL_OTGVER; + if (hsotg->core_params->otg_ver > 0) + otgctl |= GOTGCTL_OTGVER; + writel(otgctl, hsotg->regs + GOTGCTL); + dev_dbg(hsotg->dev, "OTG VER PARAM: %d\n", hsotg->core_params->otg_ver); + + /* Clear the SRP success bit for FS-I2c */ + hsotg->srp_success = 0; + + if (irq >= 0) { + dev_dbg(hsotg->dev, "registering common handler for irq%d\n", + irq); + retval = devm_request_irq(hsotg->dev, irq, + dwc2_handle_common_intr, IRQF_SHARED, + dev_name(hsotg->dev), hsotg); + if (retval) + return retval; + } + + /* Enable common interrupts */ + dwc2_enable_common_interrupts(hsotg); + + /* + * Do device or host intialization based on mode during PCD and + * HCD initialization + */ + if (dwc2_is_host_mode(hsotg)) { + dev_dbg(hsotg->dev, "Host Mode\n"); + hsotg->op_state = OTG_STATE_A_HOST; + } else { + dev_dbg(hsotg->dev, "Device Mode\n"); + hsotg->op_state = OTG_STATE_B_PERIPHERAL; + } + + return 0; +} + +/** + * dwc2_enable_host_interrupts() - Enables the Host mode interrupts + * + * @hsotg: Programming view of DWC_otg controller + */ +void dwc2_enable_host_interrupts(struct dwc2_hsotg *hsotg) +{ + u32 intmsk; + + dev_dbg(hsotg->dev, "%s()\n", __func__); + + /* Disable all interrupts */ + writel(0, hsotg->regs + GINTMSK); + writel(0, hsotg->regs + HAINTMSK); + + /* Enable the common interrupts */ + dwc2_enable_common_interrupts(hsotg); + + /* Enable host mode interrupts without disturbing common interrupts */ + intmsk = readl(hsotg->regs + GINTMSK); + intmsk |= GINTSTS_DISCONNINT | GINTSTS_PRTINT | GINTSTS_HCHINT; + writel(intmsk, hsotg->regs + GINTMSK); +} + +/** + * dwc2_disable_host_interrupts() - Disables the Host Mode interrupts + * + * @hsotg: Programming view of DWC_otg controller + */ +void dwc2_disable_host_interrupts(struct dwc2_hsotg *hsotg) +{ + u32 intmsk = readl(hsotg->regs + GINTMSK); + + /* Disable host mode interrupts without disturbing common interrupts */ + intmsk &= ~(GINTSTS_SOF | GINTSTS_PRTINT | GINTSTS_HCHINT | + GINTSTS_PTXFEMP | GINTSTS_NPTXFEMP); + writel(intmsk, hsotg->regs + GINTMSK); +} + +static void dwc2_config_fifos(struct dwc2_hsotg *hsotg) +{ + struct dwc2_core_params *params = hsotg->core_params; + u32 nptxfsiz, hptxfsiz, dfifocfg, grxfsiz; + + if (!params->enable_dynamic_fifo) + return; + + /* Rx FIFO */ + grxfsiz = readl(hsotg->regs + GRXFSIZ); + dev_dbg(hsotg->dev, "initial grxfsiz=%08x\n", grxfsiz); + grxfsiz &= ~GRXFSIZ_DEPTH_MASK; + grxfsiz |= params->host_rx_fifo_size << + GRXFSIZ_DEPTH_SHIFT & GRXFSIZ_DEPTH_MASK; + writel(grxfsiz, hsotg->regs + GRXFSIZ); + dev_dbg(hsotg->dev, "new grxfsiz=%08x\n", readl(hsotg->regs + GRXFSIZ)); + + /* Non-periodic Tx FIFO */ + dev_dbg(hsotg->dev, "initial gnptxfsiz=%08x\n", + readl(hsotg->regs + GNPTXFSIZ)); + nptxfsiz = params->host_nperio_tx_fifo_size << + FIFOSIZE_DEPTH_SHIFT & FIFOSIZE_DEPTH_MASK; + nptxfsiz |= params->host_rx_fifo_size << + FIFOSIZE_STARTADDR_SHIFT & FIFOSIZE_STARTADDR_MASK; + writel(nptxfsiz, hsotg->regs + GNPTXFSIZ); + dev_dbg(hsotg->dev, "new gnptxfsiz=%08x\n", + readl(hsotg->regs + GNPTXFSIZ)); + + /* Periodic Tx FIFO */ + dev_dbg(hsotg->dev, "initial hptxfsiz=%08x\n", + readl(hsotg->regs + HPTXFSIZ)); + hptxfsiz = params->host_perio_tx_fifo_size << + FIFOSIZE_DEPTH_SHIFT & FIFOSIZE_DEPTH_MASK; + hptxfsiz |= (params->host_rx_fifo_size + + params->host_nperio_tx_fifo_size) << + FIFOSIZE_STARTADDR_SHIFT & FIFOSIZE_STARTADDR_MASK; + writel(hptxfsiz, hsotg->regs + HPTXFSIZ); + dev_dbg(hsotg->dev, "new hptxfsiz=%08x\n", + readl(hsotg->regs + HPTXFSIZ)); + + if (hsotg->core_params->en_multiple_tx_fifo > 0 && + hsotg->hw_params.snpsid <= DWC2_CORE_REV_2_94a) { + /* + * Global DFIFOCFG calculation for Host mode - + * include RxFIFO, NPTXFIFO and HPTXFIFO + */ + dfifocfg = readl(hsotg->regs + GDFIFOCFG); + dfifocfg &= ~GDFIFOCFG_EPINFOBASE_MASK; + dfifocfg |= (params->host_rx_fifo_size + + params->host_nperio_tx_fifo_size + + params->host_perio_tx_fifo_size) << + GDFIFOCFG_EPINFOBASE_SHIFT & + GDFIFOCFG_EPINFOBASE_MASK; + writel(dfifocfg, hsotg->regs + GDFIFOCFG); + } +} + +/** + * dwc2_core_host_init() - Initializes the DWC_otg controller registers for + * Host mode + * + * @hsotg: Programming view of DWC_otg controller + * + * This function flushes the Tx and Rx FIFOs and flushes any entries in the + * request queues. Host channels are reset to ensure that they are ready for + * performing transfers. + */ +void dwc2_core_host_init(struct dwc2_hsotg *hsotg) +{ + u32 hcfg, hfir, otgctl; + + dev_dbg(hsotg->dev, "%s(%p)\n", __func__, hsotg); + + /* Restart the Phy Clock */ + writel(0, hsotg->regs + PCGCTL); + + /* Initialize Host Configuration Register */ + dwc2_init_fs_ls_pclk_sel(hsotg); + if (hsotg->core_params->speed == DWC2_SPEED_PARAM_FULL) { + hcfg = readl(hsotg->regs + HCFG); + hcfg |= HCFG_FSLSSUPP; + writel(hcfg, hsotg->regs + HCFG); + } + + /* + * This bit allows dynamic reloading of the HFIR register during + * runtime. This bit needs to be programmed during initial configuration + * and its value must not be changed during runtime. + */ + if (hsotg->core_params->reload_ctl > 0) { + hfir = readl(hsotg->regs + HFIR); + hfir |= HFIR_RLDCTRL; + writel(hfir, hsotg->regs + HFIR); + } + + if (hsotg->core_params->dma_desc_enable > 0) { + u32 op_mode = hsotg->hw_params.op_mode; + if (hsotg->hw_params.snpsid < DWC2_CORE_REV_2_90a || + !hsotg->hw_params.dma_desc_enable || + op_mode == GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE || + op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE || + op_mode == GHWCFG2_OP_MODE_UNDEFINED) { + dev_err(hsotg->dev, + "Hardware does not support descriptor DMA mode -\n"); + dev_err(hsotg->dev, + "falling back to buffer DMA mode.\n"); + hsotg->core_params->dma_desc_enable = 0; + } else { + hcfg = readl(hsotg->regs + HCFG); + hcfg |= HCFG_DESCDMA; + writel(hcfg, hsotg->regs + HCFG); + } + } + + /* Configure data FIFO sizes */ + dwc2_config_fifos(hsotg); + + /* TODO - check this */ + /* Clear Host Set HNP Enable in the OTG Control Register */ + otgctl = readl(hsotg->regs + GOTGCTL); + otgctl &= ~GOTGCTL_HSTSETHNPEN; + writel(otgctl, hsotg->regs + GOTGCTL); + + /* Make sure the FIFOs are flushed */ + dwc2_flush_tx_fifo(hsotg, 0x10 /* all TX FIFOs */); + dwc2_flush_rx_fifo(hsotg); + + /* Clear Host Set HNP Enable in the OTG Control Register */ + otgctl = readl(hsotg->regs + GOTGCTL); + otgctl &= ~GOTGCTL_HSTSETHNPEN; + writel(otgctl, hsotg->regs + GOTGCTL); + + if (hsotg->core_params->dma_desc_enable <= 0) { + int num_channels, i; + u32 hcchar; + + /* Flush out any leftover queued requests */ + num_channels = hsotg->core_params->host_channels; + for (i = 0; i < num_channels; i++) { + hcchar = readl(hsotg->regs + HCCHAR(i)); + hcchar &= ~HCCHAR_CHENA; + hcchar |= HCCHAR_CHDIS; + hcchar &= ~HCCHAR_EPDIR; + writel(hcchar, hsotg->regs + HCCHAR(i)); + } + + /* Halt all channels to put them into a known state */ + for (i = 0; i < num_channels; i++) { + int count = 0; + + hcchar = readl(hsotg->regs + HCCHAR(i)); + hcchar |= HCCHAR_CHENA | HCCHAR_CHDIS; + hcchar &= ~HCCHAR_EPDIR; + writel(hcchar, hsotg->regs + HCCHAR(i)); + dev_dbg(hsotg->dev, "%s: Halt channel %d\n", + __func__, i); + do { + hcchar = readl(hsotg->regs + HCCHAR(i)); + if (++count > 1000) { + dev_err(hsotg->dev, + "Unable to clear enable on channel %d\n", + i); + break; + } + udelay(1); + } while (hcchar & HCCHAR_CHENA); + } + } + + /* Turn on the vbus power */ + dev_dbg(hsotg->dev, "Init: Port Power? op_state=%d\n", hsotg->op_state); + if (hsotg->op_state == OTG_STATE_A_HOST) { + u32 hprt0 = dwc2_read_hprt0(hsotg); + + dev_dbg(hsotg->dev, "Init: Power Port (%d)\n", + !!(hprt0 & HPRT0_PWR)); + if (!(hprt0 & HPRT0_PWR)) { + hprt0 |= HPRT0_PWR; + writel(hprt0, hsotg->regs + HPRT0); + } + } + + dwc2_enable_host_interrupts(hsotg); +} + +static void dwc2_hc_enable_slave_ints(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan) +{ + u32 hcintmsk = HCINTMSK_CHHLTD; + + switch (chan->ep_type) { + case USB_ENDPOINT_XFER_CONTROL: + case USB_ENDPOINT_XFER_BULK: + dev_vdbg(hsotg->dev, "control/bulk\n"); + hcintmsk |= HCINTMSK_XFERCOMPL; + hcintmsk |= HCINTMSK_STALL; + hcintmsk |= HCINTMSK_XACTERR; + hcintmsk |= HCINTMSK_DATATGLERR; + if (chan->ep_is_in) { + hcintmsk |= HCINTMSK_BBLERR; + } else { + hcintmsk |= HCINTMSK_NAK; + hcintmsk |= HCINTMSK_NYET; + if (chan->do_ping) + hcintmsk |= HCINTMSK_ACK; + } + + if (chan->do_split) { + hcintmsk |= HCINTMSK_NAK; + if (chan->complete_split) + hcintmsk |= HCINTMSK_NYET; + else + hcintmsk |= HCINTMSK_ACK; + } + + if (chan->error_state) + hcintmsk |= HCINTMSK_ACK; + break; + + case USB_ENDPOINT_XFER_INT: + if (dbg_perio()) + dev_vdbg(hsotg->dev, "intr\n"); + hcintmsk |= HCINTMSK_XFERCOMPL; + hcintmsk |= HCINTMSK_NAK; + hcintmsk |= HCINTMSK_STALL; + hcintmsk |= HCINTMSK_XACTERR; + hcintmsk |= HCINTMSK_DATATGLERR; + hcintmsk |= HCINTMSK_FRMOVRUN; + + if (chan->ep_is_in) + hcintmsk |= HCINTMSK_BBLERR; + if (chan->error_state) + hcintmsk |= HCINTMSK_ACK; + if (chan->do_split) { + if (chan->complete_split) + hcintmsk |= HCINTMSK_NYET; + else + hcintmsk |= HCINTMSK_ACK; + } + break; + + case USB_ENDPOINT_XFER_ISOC: + if (dbg_perio()) + dev_vdbg(hsotg->dev, "isoc\n"); + hcintmsk |= HCINTMSK_XFERCOMPL; + hcintmsk |= HCINTMSK_FRMOVRUN; + hcintmsk |= HCINTMSK_ACK; + + if (chan->ep_is_in) { + hcintmsk |= HCINTMSK_XACTERR; + hcintmsk |= HCINTMSK_BBLERR; + } + break; + default: + dev_err(hsotg->dev, "## Unknown EP type ##\n"); + break; + } + + writel(hcintmsk, hsotg->regs + HCINTMSK(chan->hc_num)); + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "set HCINTMSK to %08x\n", hcintmsk); +} + +static void dwc2_hc_enable_dma_ints(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan) +{ + u32 hcintmsk = HCINTMSK_CHHLTD; + + /* + * For Descriptor DMA mode core halts the channel on AHB error. + * Interrupt is not required. + */ + if (hsotg->core_params->dma_desc_enable <= 0) { + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "desc DMA disabled\n"); + hcintmsk |= HCINTMSK_AHBERR; + } else { + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "desc DMA enabled\n"); + if (chan->ep_type == USB_ENDPOINT_XFER_ISOC) + hcintmsk |= HCINTMSK_XFERCOMPL; + } + + if (chan->error_state && !chan->do_split && + chan->ep_type != USB_ENDPOINT_XFER_ISOC) { + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "setting ACK\n"); + hcintmsk |= HCINTMSK_ACK; + if (chan->ep_is_in) { + hcintmsk |= HCINTMSK_DATATGLERR; + if (chan->ep_type != USB_ENDPOINT_XFER_INT) + hcintmsk |= HCINTMSK_NAK; + } + } + + writel(hcintmsk, hsotg->regs + HCINTMSK(chan->hc_num)); + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "set HCINTMSK to %08x\n", hcintmsk); +} + +static void dwc2_hc_enable_ints(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan) +{ + u32 intmsk; + + if (hsotg->core_params->dma_enable > 0) { + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "DMA enabled\n"); + dwc2_hc_enable_dma_ints(hsotg, chan); + } else { + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "DMA disabled\n"); + dwc2_hc_enable_slave_ints(hsotg, chan); + } + + /* Enable the top level host channel interrupt */ + intmsk = readl(hsotg->regs + HAINTMSK); + intmsk |= 1 << chan->hc_num; + writel(intmsk, hsotg->regs + HAINTMSK); + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "set HAINTMSK to %08x\n", intmsk); + + /* Make sure host channel interrupts are enabled */ + intmsk = readl(hsotg->regs + GINTMSK); + intmsk |= GINTSTS_HCHINT; + writel(intmsk, hsotg->regs + GINTMSK); + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "set GINTMSK to %08x\n", intmsk); +} + +/** + * dwc2_hc_init() - Prepares a host channel for transferring packets to/from + * a specific endpoint + * + * @hsotg: Programming view of DWC_otg controller + * @chan: Information needed to initialize the host channel + * + * The HCCHARn register is set up with the characteristics specified in chan. + * Host channel interrupts that may need to be serviced while this transfer is + * in progress are enabled. + */ +void dwc2_hc_init(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan) +{ + u8 hc_num = chan->hc_num; + u32 hcintmsk; + u32 hcchar; + u32 hcsplt = 0; + + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "%s()\n", __func__); + + /* Clear old interrupt conditions for this host channel */ + hcintmsk = 0xffffffff; + hcintmsk &= ~HCINTMSK_RESERVED14_31; + writel(hcintmsk, hsotg->regs + HCINT(hc_num)); + + /* Enable channel interrupts required for this transfer */ + dwc2_hc_enable_ints(hsotg, chan); + + /* + * Program the HCCHARn register with the endpoint characteristics for + * the current transfer + */ + hcchar = chan->dev_addr << HCCHAR_DEVADDR_SHIFT & HCCHAR_DEVADDR_MASK; + hcchar |= chan->ep_num << HCCHAR_EPNUM_SHIFT & HCCHAR_EPNUM_MASK; + if (chan->ep_is_in) + hcchar |= HCCHAR_EPDIR; + if (chan->speed == USB_SPEED_LOW) + hcchar |= HCCHAR_LSPDDEV; + hcchar |= chan->ep_type << HCCHAR_EPTYPE_SHIFT & HCCHAR_EPTYPE_MASK; + hcchar |= chan->max_packet << HCCHAR_MPS_SHIFT & HCCHAR_MPS_MASK; + writel(hcchar, hsotg->regs + HCCHAR(hc_num)); + if (dbg_hc(chan)) { + dev_vdbg(hsotg->dev, "set HCCHAR(%d) to %08x\n", + hc_num, hcchar); + + dev_vdbg(hsotg->dev, "%s: Channel %d\n", + __func__, hc_num); + dev_vdbg(hsotg->dev, " Dev Addr: %d\n", + chan->dev_addr); + dev_vdbg(hsotg->dev, " Ep Num: %d\n", + chan->ep_num); + dev_vdbg(hsotg->dev, " Is In: %d\n", + chan->ep_is_in); + dev_vdbg(hsotg->dev, " Is Low Speed: %d\n", + chan->speed == USB_SPEED_LOW); + dev_vdbg(hsotg->dev, " Ep Type: %d\n", + chan->ep_type); + dev_vdbg(hsotg->dev, " Max Pkt: %d\n", + chan->max_packet); + } + + /* Program the HCSPLT register for SPLITs */ + if (chan->do_split) { + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, + "Programming HC %d with split --> %s\n", + hc_num, + chan->complete_split ? "CSPLIT" : "SSPLIT"); + if (chan->complete_split) + hcsplt |= HCSPLT_COMPSPLT; + hcsplt |= chan->xact_pos << HCSPLT_XACTPOS_SHIFT & + HCSPLT_XACTPOS_MASK; + hcsplt |= chan->hub_addr << HCSPLT_HUBADDR_SHIFT & + HCSPLT_HUBADDR_MASK; + hcsplt |= chan->hub_port << HCSPLT_PRTADDR_SHIFT & + HCSPLT_PRTADDR_MASK; + if (dbg_hc(chan)) { + dev_vdbg(hsotg->dev, " comp split %d\n", + chan->complete_split); + dev_vdbg(hsotg->dev, " xact pos %d\n", + chan->xact_pos); + dev_vdbg(hsotg->dev, " hub addr %d\n", + chan->hub_addr); + dev_vdbg(hsotg->dev, " hub port %d\n", + chan->hub_port); + dev_vdbg(hsotg->dev, " is_in %d\n", + chan->ep_is_in); + dev_vdbg(hsotg->dev, " Max Pkt %d\n", + chan->max_packet); + dev_vdbg(hsotg->dev, " xferlen %d\n", + chan->xfer_len); + } + } + + writel(hcsplt, hsotg->regs + HCSPLT(hc_num)); +} + +/** + * dwc2_hc_halt() - Attempts to halt a host channel + * + * @hsotg: Controller register interface + * @chan: Host channel to halt + * @halt_status: Reason for halting the channel + * + * This function should only be called in Slave mode or to abort a transfer in + * either Slave mode or DMA mode. Under normal circumstances in DMA mode, the + * controller halts the channel when the transfer is complete or a condition + * occurs that requires application intervention. + * + * In slave mode, checks for a free request queue entry, then sets the Channel + * Enable and Channel Disable bits of the Host Channel Characteristics + * register of the specified channel to intiate the halt. If there is no free + * request queue entry, sets only the Channel Disable bit of the HCCHARn + * register to flush requests for this channel. In the latter case, sets a + * flag to indicate that the host channel needs to be halted when a request + * queue slot is open. + * + * In DMA mode, always sets the Channel Enable and Channel Disable bits of the + * HCCHARn register. The controller ensures there is space in the request + * queue before submitting the halt request. + * + * Some time may elapse before the core flushes any posted requests for this + * host channel and halts. The Channel Halted interrupt handler completes the + * deactivation of the host channel. + */ +void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan, + enum dwc2_halt_status halt_status) +{ + u32 nptxsts, hptxsts, hcchar; + + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "%s()\n", __func__); + if (halt_status == DWC2_HC_XFER_NO_HALT_STATUS) + dev_err(hsotg->dev, "!!! halt_status = %d !!!\n", halt_status); + + if (halt_status == DWC2_HC_XFER_URB_DEQUEUE || + halt_status == DWC2_HC_XFER_AHB_ERR) { + /* + * Disable all channel interrupts except Ch Halted. The QTD + * and QH state associated with this transfer has been cleared + * (in the case of URB_DEQUEUE), so the channel needs to be + * shut down carefully to prevent crashes. + */ + u32 hcintmsk = HCINTMSK_CHHLTD; + + dev_vdbg(hsotg->dev, "dequeue/error\n"); + writel(hcintmsk, hsotg->regs + HCINTMSK(chan->hc_num)); + + /* + * Make sure no other interrupts besides halt are currently + * pending. Handling another interrupt could cause a crash due + * to the QTD and QH state. + */ + writel(~hcintmsk, hsotg->regs + HCINT(chan->hc_num)); + + /* + * Make sure the halt status is set to URB_DEQUEUE or AHB_ERR + * even if the channel was already halted for some other + * reason + */ + chan->halt_status = halt_status; + + hcchar = readl(hsotg->regs + HCCHAR(chan->hc_num)); + if (!(hcchar & HCCHAR_CHENA)) { + /* + * The channel is either already halted or it hasn't + * started yet. In DMA mode, the transfer may halt if + * it finishes normally or a condition occurs that + * requires driver intervention. Don't want to halt + * the channel again. In either Slave or DMA mode, + * it's possible that the transfer has been assigned + * to a channel, but not started yet when an URB is + * dequeued. Don't want to halt a channel that hasn't + * started yet. + */ + return; + } + } + if (chan->halt_pending) { + /* + * A halt has already been issued for this channel. This might + * happen when a transfer is aborted by a higher level in + * the stack. + */ + dev_vdbg(hsotg->dev, + "*** %s: Channel %d, chan->halt_pending already set ***\n", + __func__, chan->hc_num); + return; + } + + hcchar = readl(hsotg->regs + HCCHAR(chan->hc_num)); + + /* No need to set the bit in DDMA for disabling the channel */ + /* TODO check it everywhere channel is disabled */ + if (hsotg->core_params->dma_desc_enable <= 0) { + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "desc DMA disabled\n"); + hcchar |= HCCHAR_CHENA; + } else { + if (dbg_hc(chan)) + dev_dbg(hsotg->dev, "desc DMA enabled\n"); + } + hcchar |= HCCHAR_CHDIS; + + if (hsotg->core_params->dma_enable <= 0) { + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "DMA not enabled\n"); + hcchar |= HCCHAR_CHENA; + + /* Check for space in the request queue to issue the halt */ + if (chan->ep_type == USB_ENDPOINT_XFER_CONTROL || + chan->ep_type == USB_ENDPOINT_XFER_BULK) { + dev_vdbg(hsotg->dev, "control/bulk\n"); + nptxsts = readl(hsotg->regs + GNPTXSTS); + if ((nptxsts & TXSTS_QSPCAVAIL_MASK) == 0) { + dev_vdbg(hsotg->dev, "Disabling channel\n"); + hcchar &= ~HCCHAR_CHENA; + } + } else { + if (dbg_perio()) + dev_vdbg(hsotg->dev, "isoc/intr\n"); + hptxsts = readl(hsotg->regs + HPTXSTS); + if ((hptxsts & TXSTS_QSPCAVAIL_MASK) == 0 || + hsotg->queuing_high_bandwidth) { + if (dbg_perio()) + dev_vdbg(hsotg->dev, "Disabling channel\n"); + hcchar &= ~HCCHAR_CHENA; + } + } + } else { + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "DMA enabled\n"); + } + + writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num)); + chan->halt_status = halt_status; + + if (hcchar & HCCHAR_CHENA) { + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "Channel enabled\n"); + chan->halt_pending = 1; + chan->halt_on_queue = 0; + } else { + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "Channel disabled\n"); + chan->halt_on_queue = 1; + } + + if (dbg_hc(chan)) { + dev_vdbg(hsotg->dev, "%s: Channel %d\n", __func__, + chan->hc_num); + dev_vdbg(hsotg->dev, " hcchar: 0x%08x\n", + hcchar); + dev_vdbg(hsotg->dev, " halt_pending: %d\n", + chan->halt_pending); + dev_vdbg(hsotg->dev, " halt_on_queue: %d\n", + chan->halt_on_queue); + dev_vdbg(hsotg->dev, " halt_status: %d\n", + chan->halt_status); + } +} + +/** + * dwc2_hc_cleanup() - Clears the transfer state for a host channel + * + * @hsotg: Programming view of DWC_otg controller + * @chan: Identifies the host channel to clean up + * + * This function is normally called after a transfer is done and the host + * channel is being released + */ +void dwc2_hc_cleanup(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan) +{ + u32 hcintmsk; + + chan->xfer_started = 0; + + /* + * Clear channel interrupt enables and any unhandled channel interrupt + * conditions + */ + writel(0, hsotg->regs + HCINTMSK(chan->hc_num)); + hcintmsk = 0xffffffff; + hcintmsk &= ~HCINTMSK_RESERVED14_31; + writel(hcintmsk, hsotg->regs + HCINT(chan->hc_num)); +} + +/** + * dwc2_hc_set_even_odd_frame() - Sets the channel property that indicates in + * which frame a periodic transfer should occur + * + * @hsotg: Programming view of DWC_otg controller + * @chan: Identifies the host channel to set up and its properties + * @hcchar: Current value of the HCCHAR register for the specified host channel + * + * This function has no effect on non-periodic transfers + */ +static void dwc2_hc_set_even_odd_frame(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, u32 *hcchar) +{ + if (chan->ep_type == USB_ENDPOINT_XFER_INT || + chan->ep_type == USB_ENDPOINT_XFER_ISOC) { + /* 1 if _next_ frame is odd, 0 if it's even */ + if (!(dwc2_hcd_get_frame_number(hsotg) & 0x1)) + *hcchar |= HCCHAR_ODDFRM; + } +} + +static void dwc2_set_pid_isoc(struct dwc2_host_chan *chan) +{ + /* Set up the initial PID for the transfer */ + if (chan->speed == USB_SPEED_HIGH) { + if (chan->ep_is_in) { + if (chan->multi_count == 1) + chan->data_pid_start = DWC2_HC_PID_DATA0; + else if (chan->multi_count == 2) + chan->data_pid_start = DWC2_HC_PID_DATA1; + else + chan->data_pid_start = DWC2_HC_PID_DATA2; + } else { + if (chan->multi_count == 1) + chan->data_pid_start = DWC2_HC_PID_DATA0; + else + chan->data_pid_start = DWC2_HC_PID_MDATA; + } + } else { + chan->data_pid_start = DWC2_HC_PID_DATA0; + } +} + +/** + * dwc2_hc_write_packet() - Writes a packet into the Tx FIFO associated with + * the Host Channel + * + * @hsotg: Programming view of DWC_otg controller + * @chan: Information needed to initialize the host channel + * + * This function should only be called in Slave mode. For a channel associated + * with a non-periodic EP, the non-periodic Tx FIFO is written. For a channel + * associated with a periodic EP, the periodic Tx FIFO is written. + * + * Upon return the xfer_buf and xfer_count fields in chan are incremented by + * the number of bytes written to the Tx FIFO. + */ +static void dwc2_hc_write_packet(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan) +{ + u32 i; + u32 remaining_count; + u32 byte_count; + u32 dword_count; + u32 __iomem *data_fifo; + u32 *data_buf = (u32 *)chan->xfer_buf; + + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "%s()\n", __func__); + + data_fifo = (u32 __iomem *)(hsotg->regs + HCFIFO(chan->hc_num)); + + remaining_count = chan->xfer_len - chan->xfer_count; + if (remaining_count > chan->max_packet) + byte_count = chan->max_packet; + else + byte_count = remaining_count; + + dword_count = (byte_count + 3) / 4; + + if (((unsigned long)data_buf & 0x3) == 0) { + /* xfer_buf is DWORD aligned */ + for (i = 0; i < dword_count; i++, data_buf++) + writel(*data_buf, data_fifo); + } else { + /* xfer_buf is not DWORD aligned */ + for (i = 0; i < dword_count; i++, data_buf++) { + u32 data = data_buf[0] | data_buf[1] << 8 | + data_buf[2] << 16 | data_buf[3] << 24; + writel(data, data_fifo); + } + } + + chan->xfer_count += byte_count; + chan->xfer_buf += byte_count; +} + +/** + * dwc2_hc_start_transfer() - Does the setup for a data transfer for a host + * channel and starts the transfer + * + * @hsotg: Programming view of DWC_otg controller + * @chan: Information needed to initialize the host channel. The xfer_len value + * may be reduced to accommodate the max widths of the XferSize and + * PktCnt fields in the HCTSIZn register. The multi_count value may be + * changed to reflect the final xfer_len value. + * + * This function may be called in either Slave mode or DMA mode. In Slave mode, + * the caller must ensure that there is sufficient space in the request queue + * and Tx Data FIFO. + * + * For an OUT transfer in Slave mode, it loads a data packet into the + * appropriate FIFO. If necessary, additional data packets are loaded in the + * Host ISR. + * + * For an IN transfer in Slave mode, a data packet is requested. The data + * packets are unloaded from the Rx FIFO in the Host ISR. If necessary, + * additional data packets are requested in the Host ISR. + * + * For a PING transfer in Slave mode, the Do Ping bit is set in the HCTSIZ + * register along with a packet count of 1 and the channel is enabled. This + * causes a single PING transaction to occur. Other fields in HCTSIZ are + * simply set to 0 since no data transfer occurs in this case. + * + * For a PING transfer in DMA mode, the HCTSIZ register is initialized with + * all the information required to perform the subsequent data transfer. In + * addition, the Do Ping bit is set in the HCTSIZ register. In this case, the + * controller performs the entire PING protocol, then starts the data + * transfer. + */ +void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan) +{ + u32 max_hc_xfer_size = hsotg->core_params->max_transfer_size; + u16 max_hc_pkt_count = hsotg->core_params->max_packet_count; + u32 hcchar; + u32 hctsiz = 0; + u16 num_packets; + + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "%s()\n", __func__); + + if (chan->do_ping) { + if (hsotg->core_params->dma_enable <= 0) { + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "ping, no DMA\n"); + dwc2_hc_do_ping(hsotg, chan); + chan->xfer_started = 1; + return; + } else { + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "ping, DMA\n"); + hctsiz |= TSIZ_DOPNG; + } + } + + if (chan->do_split) { + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "split\n"); + num_packets = 1; + + if (chan->complete_split && !chan->ep_is_in) + /* + * For CSPLIT OUT Transfer, set the size to 0 so the + * core doesn't expect any data written to the FIFO + */ + chan->xfer_len = 0; + else if (chan->ep_is_in || chan->xfer_len > chan->max_packet) + chan->xfer_len = chan->max_packet; + else if (!chan->ep_is_in && chan->xfer_len > 188) + chan->xfer_len = 188; + + hctsiz |= chan->xfer_len << TSIZ_XFERSIZE_SHIFT & + TSIZ_XFERSIZE_MASK; + } else { + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "no split\n"); + /* + * Ensure that the transfer length and packet count will fit + * in the widths allocated for them in the HCTSIZn register + */ + if (chan->ep_type == USB_ENDPOINT_XFER_INT || + chan->ep_type == USB_ENDPOINT_XFER_ISOC) { + /* + * Make sure the transfer size is no larger than one + * (micro)frame's worth of data. (A check was done + * when the periodic transfer was accepted to ensure + * that a (micro)frame's worth of data can be + * programmed into a channel.) + */ + u32 max_periodic_len = + chan->multi_count * chan->max_packet; + + if (chan->xfer_len > max_periodic_len) + chan->xfer_len = max_periodic_len; + } else if (chan->xfer_len > max_hc_xfer_size) { + /* + * Make sure that xfer_len is a multiple of max packet + * size + */ + chan->xfer_len = + max_hc_xfer_size - chan->max_packet + 1; + } + + if (chan->xfer_len > 0) { + num_packets = (chan->xfer_len + chan->max_packet - 1) / + chan->max_packet; + if (num_packets > max_hc_pkt_count) { + num_packets = max_hc_pkt_count; + chan->xfer_len = num_packets * chan->max_packet; + } + } else { + /* Need 1 packet for transfer length of 0 */ + num_packets = 1; + } + + if (chan->ep_is_in) + /* + * Always program an integral # of max packets for IN + * transfers + */ + chan->xfer_len = num_packets * chan->max_packet; + + if (chan->ep_type == USB_ENDPOINT_XFER_INT || + chan->ep_type == USB_ENDPOINT_XFER_ISOC) + /* + * Make sure that the multi_count field matches the + * actual transfer length + */ + chan->multi_count = num_packets; + + if (chan->ep_type == USB_ENDPOINT_XFER_ISOC) + dwc2_set_pid_isoc(chan); + + hctsiz |= chan->xfer_len << TSIZ_XFERSIZE_SHIFT & + TSIZ_XFERSIZE_MASK; + } + + chan->start_pkt_count = num_packets; + hctsiz |= num_packets << TSIZ_PKTCNT_SHIFT & TSIZ_PKTCNT_MASK; + hctsiz |= chan->data_pid_start << TSIZ_SC_MC_PID_SHIFT & + TSIZ_SC_MC_PID_MASK; + writel(hctsiz, hsotg->regs + HCTSIZ(chan->hc_num)); + if (dbg_hc(chan)) { + dev_vdbg(hsotg->dev, "Wrote %08x to HCTSIZ(%d)\n", + hctsiz, chan->hc_num); + + dev_vdbg(hsotg->dev, "%s: Channel %d\n", __func__, + chan->hc_num); + dev_vdbg(hsotg->dev, " Xfer Size: %d\n", + (hctsiz & TSIZ_XFERSIZE_MASK) >> + TSIZ_XFERSIZE_SHIFT); + dev_vdbg(hsotg->dev, " Num Pkts: %d\n", + (hctsiz & TSIZ_PKTCNT_MASK) >> + TSIZ_PKTCNT_SHIFT); + dev_vdbg(hsotg->dev, " Start PID: %d\n", + (hctsiz & TSIZ_SC_MC_PID_MASK) >> + TSIZ_SC_MC_PID_SHIFT); + } + + if (hsotg->core_params->dma_enable > 0) { + dma_addr_t dma_addr; + + if (chan->align_buf) { + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "align_buf\n"); + dma_addr = chan->align_buf; + } else { + dma_addr = chan->xfer_dma; + } + writel((u32)dma_addr, hsotg->regs + HCDMA(chan->hc_num)); + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "Wrote %08lx to HCDMA(%d)\n", + (unsigned long)dma_addr, chan->hc_num); + } + + /* Start the split */ + if (chan->do_split) { + u32 hcsplt = readl(hsotg->regs + HCSPLT(chan->hc_num)); + + hcsplt |= HCSPLT_SPLTENA; + writel(hcsplt, hsotg->regs + HCSPLT(chan->hc_num)); + } + + hcchar = readl(hsotg->regs + HCCHAR(chan->hc_num)); + hcchar &= ~HCCHAR_MULTICNT_MASK; + hcchar |= chan->multi_count << HCCHAR_MULTICNT_SHIFT & + HCCHAR_MULTICNT_MASK; + dwc2_hc_set_even_odd_frame(hsotg, chan, &hcchar); + + if (hcchar & HCCHAR_CHDIS) + dev_warn(hsotg->dev, + "%s: chdis set, channel %d, hcchar 0x%08x\n", + __func__, chan->hc_num, hcchar); + + /* Set host channel enable after all other setup is complete */ + hcchar |= HCCHAR_CHENA; + hcchar &= ~HCCHAR_CHDIS; + + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, " Multi Cnt: %d\n", + (hcchar & HCCHAR_MULTICNT_MASK) >> + HCCHAR_MULTICNT_SHIFT); + + writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num)); + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "Wrote %08x to HCCHAR(%d)\n", hcchar, + chan->hc_num); + + chan->xfer_started = 1; + chan->requests++; + + if (hsotg->core_params->dma_enable <= 0 && + !chan->ep_is_in && chan->xfer_len > 0) + /* Load OUT packet into the appropriate Tx FIFO */ + dwc2_hc_write_packet(hsotg, chan); +} + +/** + * dwc2_hc_start_transfer_ddma() - Does the setup for a data transfer for a + * host channel and starts the transfer in Descriptor DMA mode + * + * @hsotg: Programming view of DWC_otg controller + * @chan: Information needed to initialize the host channel + * + * Initializes HCTSIZ register. For a PING transfer the Do Ping bit is set. + * Sets PID and NTD values. For periodic transfers initializes SCHED_INFO field + * with micro-frame bitmap. + * + * Initializes HCDMA register with descriptor list address and CTD value then + * starts the transfer via enabling the channel. + */ +void dwc2_hc_start_transfer_ddma(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan) +{ + u32 hcchar; + u32 hc_dma; + u32 hctsiz = 0; + + if (chan->do_ping) + hctsiz |= TSIZ_DOPNG; + + if (chan->ep_type == USB_ENDPOINT_XFER_ISOC) + dwc2_set_pid_isoc(chan); + + /* Packet Count and Xfer Size are not used in Descriptor DMA mode */ + hctsiz |= chan->data_pid_start << TSIZ_SC_MC_PID_SHIFT & + TSIZ_SC_MC_PID_MASK; + + /* 0 - 1 descriptor, 1 - 2 descriptors, etc */ + hctsiz |= (chan->ntd - 1) << TSIZ_NTD_SHIFT & TSIZ_NTD_MASK; + + /* Non-zero only for high-speed interrupt endpoints */ + hctsiz |= chan->schinfo << TSIZ_SCHINFO_SHIFT & TSIZ_SCHINFO_MASK; + + if (dbg_hc(chan)) { + dev_vdbg(hsotg->dev, "%s: Channel %d\n", __func__, + chan->hc_num); + dev_vdbg(hsotg->dev, " Start PID: %d\n", + chan->data_pid_start); + dev_vdbg(hsotg->dev, " NTD: %d\n", chan->ntd - 1); + } + + writel(hctsiz, hsotg->regs + HCTSIZ(chan->hc_num)); + + hc_dma = (u32)chan->desc_list_addr & HCDMA_DMA_ADDR_MASK; + + /* Always start from first descriptor */ + hc_dma &= ~HCDMA_CTD_MASK; + writel(hc_dma, hsotg->regs + HCDMA(chan->hc_num)); + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "Wrote %08x to HCDMA(%d)\n", + hc_dma, chan->hc_num); + + hcchar = readl(hsotg->regs + HCCHAR(chan->hc_num)); + hcchar &= ~HCCHAR_MULTICNT_MASK; + hcchar |= chan->multi_count << HCCHAR_MULTICNT_SHIFT & + HCCHAR_MULTICNT_MASK; + + if (hcchar & HCCHAR_CHDIS) + dev_warn(hsotg->dev, + "%s: chdis set, channel %d, hcchar 0x%08x\n", + __func__, chan->hc_num, hcchar); + + /* Set host channel enable after all other setup is complete */ + hcchar |= HCCHAR_CHENA; + hcchar &= ~HCCHAR_CHDIS; + + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, " Multi Cnt: %d\n", + (hcchar & HCCHAR_MULTICNT_MASK) >> + HCCHAR_MULTICNT_SHIFT); + + writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num)); + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "Wrote %08x to HCCHAR(%d)\n", hcchar, + chan->hc_num); + + chan->xfer_started = 1; + chan->requests++; +} + +/** + * dwc2_hc_continue_transfer() - Continues a data transfer that was started by + * a previous call to dwc2_hc_start_transfer() + * + * @hsotg: Programming view of DWC_otg controller + * @chan: Information needed to initialize the host channel + * + * The caller must ensure there is sufficient space in the request queue and Tx + * Data FIFO. This function should only be called in Slave mode. In DMA mode, + * the controller acts autonomously to complete transfers programmed to a host + * channel. + * + * For an OUT transfer, a new data packet is loaded into the appropriate FIFO + * if there is any data remaining to be queued. For an IN transfer, another + * data packet is always requested. For the SETUP phase of a control transfer, + * this function does nothing. + * + * Return: 1 if a new request is queued, 0 if no more requests are required + * for this transfer + */ +int dwc2_hc_continue_transfer(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan) +{ + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "%s: Channel %d\n", __func__, + chan->hc_num); + + if (chan->do_split) + /* SPLITs always queue just once per channel */ + return 0; + + if (chan->data_pid_start == DWC2_HC_PID_SETUP) + /* SETUPs are queued only once since they can't be NAK'd */ + return 0; + + if (chan->ep_is_in) { + /* + * Always queue another request for other IN transfers. If + * back-to-back INs are issued and NAKs are received for both, + * the driver may still be processing the first NAK when the + * second NAK is received. When the interrupt handler clears + * the NAK interrupt for the first NAK, the second NAK will + * not be seen. So we can't depend on the NAK interrupt + * handler to requeue a NAK'd request. Instead, IN requests + * are issued each time this function is called. When the + * transfer completes, the extra requests for the channel will + * be flushed. + */ + u32 hcchar = readl(hsotg->regs + HCCHAR(chan->hc_num)); + + dwc2_hc_set_even_odd_frame(hsotg, chan, &hcchar); + hcchar |= HCCHAR_CHENA; + hcchar &= ~HCCHAR_CHDIS; + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, " IN xfer: hcchar = 0x%08x\n", + hcchar); + writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num)); + chan->requests++; + return 1; + } + + /* OUT transfers */ + + if (chan->xfer_count < chan->xfer_len) { + if (chan->ep_type == USB_ENDPOINT_XFER_INT || + chan->ep_type == USB_ENDPOINT_XFER_ISOC) { + u32 hcchar = readl(hsotg->regs + + HCCHAR(chan->hc_num)); + + dwc2_hc_set_even_odd_frame(hsotg, chan, + &hcchar); + } + + /* Load OUT packet into the appropriate Tx FIFO */ + dwc2_hc_write_packet(hsotg, chan); + chan->requests++; + return 1; + } + + return 0; +} + +/** + * dwc2_hc_do_ping() - Starts a PING transfer + * + * @hsotg: Programming view of DWC_otg controller + * @chan: Information needed to initialize the host channel + * + * This function should only be called in Slave mode. The Do Ping bit is set in + * the HCTSIZ register, then the channel is enabled. + */ +void dwc2_hc_do_ping(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan) +{ + u32 hcchar; + u32 hctsiz; + + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "%s: Channel %d\n", __func__, + chan->hc_num); + + + hctsiz = TSIZ_DOPNG; + hctsiz |= 1 << TSIZ_PKTCNT_SHIFT; + writel(hctsiz, hsotg->regs + HCTSIZ(chan->hc_num)); + + hcchar = readl(hsotg->regs + HCCHAR(chan->hc_num)); + hcchar |= HCCHAR_CHENA; + hcchar &= ~HCCHAR_CHDIS; + writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num)); +} + +/** + * dwc2_calc_frame_interval() - Calculates the correct frame Interval value for + * the HFIR register according to PHY type and speed + * + * @hsotg: Programming view of DWC_otg controller + * + * NOTE: The caller can modify the value of the HFIR register only after the + * Port Enable bit of the Host Port Control and Status register (HPRT.EnaPort) + * has been set + */ +u32 dwc2_calc_frame_interval(struct dwc2_hsotg *hsotg) +{ + u32 usbcfg; + u32 hprt0; + int clock = 60; /* default value */ + + usbcfg = readl(hsotg->regs + GUSBCFG); + hprt0 = readl(hsotg->regs + HPRT0); + + if (!(usbcfg & GUSBCFG_PHYSEL) && (usbcfg & GUSBCFG_ULPI_UTMI_SEL) && + !(usbcfg & GUSBCFG_PHYIF16)) + clock = 60; + if ((usbcfg & GUSBCFG_PHYSEL) && hsotg->hw_params.fs_phy_type == + GHWCFG2_FS_PHY_TYPE_SHARED_ULPI) + clock = 48; + if (!(usbcfg & GUSBCFG_PHY_LP_CLK_SEL) && !(usbcfg & GUSBCFG_PHYSEL) && + !(usbcfg & GUSBCFG_ULPI_UTMI_SEL) && (usbcfg & GUSBCFG_PHYIF16)) + clock = 30; + if (!(usbcfg & GUSBCFG_PHY_LP_CLK_SEL) && !(usbcfg & GUSBCFG_PHYSEL) && + !(usbcfg & GUSBCFG_ULPI_UTMI_SEL) && !(usbcfg & GUSBCFG_PHYIF16)) + clock = 60; + if ((usbcfg & GUSBCFG_PHY_LP_CLK_SEL) && !(usbcfg & GUSBCFG_PHYSEL) && + !(usbcfg & GUSBCFG_ULPI_UTMI_SEL) && (usbcfg & GUSBCFG_PHYIF16)) + clock = 48; + if ((usbcfg & GUSBCFG_PHYSEL) && !(usbcfg & GUSBCFG_PHYIF16) && + hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_SHARED_UTMI) + clock = 48; + if ((usbcfg & GUSBCFG_PHYSEL) && + hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED) + clock = 48; + + if ((hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT == HPRT0_SPD_HIGH_SPEED) + /* High speed case */ + return 125 * clock; + else + /* FS/LS case */ + return 1000 * clock; +} + +/** + * dwc2_read_packet() - Reads a packet from the Rx FIFO into the destination + * buffer + * + * @core_if: Programming view of DWC_otg controller + * @dest: Destination buffer for the packet + * @bytes: Number of bytes to copy to the destination + */ +void dwc2_read_packet(struct dwc2_hsotg *hsotg, u8 *dest, u16 bytes) +{ + u32 __iomem *fifo = hsotg->regs + HCFIFO(0); + u32 *data_buf = (u32 *)dest; + int word_count = (bytes + 3) / 4; + int i; + + /* + * Todo: Account for the case where dest is not dword aligned. This + * requires reading data from the FIFO into a u32 temp buffer, then + * moving it into the data buffer. + */ + + dev_vdbg(hsotg->dev, "%s(%p,%p,%d)\n", __func__, hsotg, dest, bytes); + + for (i = 0; i < word_count; i++, data_buf++) + *data_buf = readl(fifo); +} + +/** + * dwc2_dump_host_registers() - Prints the host registers + * + * @hsotg: Programming view of DWC_otg controller + * + * NOTE: This function will be removed once the peripheral controller code + * is integrated and the driver is stable + */ +void dwc2_dump_host_registers(struct dwc2_hsotg *hsotg) +{ +#ifdef DEBUG + u32 __iomem *addr; + int i; + + dev_dbg(hsotg->dev, "Host Global Registers\n"); + addr = hsotg->regs + HCFG; + dev_dbg(hsotg->dev, "HCFG @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + addr = hsotg->regs + HFIR; + dev_dbg(hsotg->dev, "HFIR @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + addr = hsotg->regs + HFNUM; + dev_dbg(hsotg->dev, "HFNUM @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + addr = hsotg->regs + HPTXSTS; + dev_dbg(hsotg->dev, "HPTXSTS @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + addr = hsotg->regs + HAINT; + dev_dbg(hsotg->dev, "HAINT @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + addr = hsotg->regs + HAINTMSK; + dev_dbg(hsotg->dev, "HAINTMSK @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + if (hsotg->core_params->dma_desc_enable > 0) { + addr = hsotg->regs + HFLBADDR; + dev_dbg(hsotg->dev, "HFLBADDR @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + } + + addr = hsotg->regs + HPRT0; + dev_dbg(hsotg->dev, "HPRT0 @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + + for (i = 0; i < hsotg->core_params->host_channels; i++) { + dev_dbg(hsotg->dev, "Host Channel %d Specific Registers\n", i); + addr = hsotg->regs + HCCHAR(i); + dev_dbg(hsotg->dev, "HCCHAR @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + addr = hsotg->regs + HCSPLT(i); + dev_dbg(hsotg->dev, "HCSPLT @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + addr = hsotg->regs + HCINT(i); + dev_dbg(hsotg->dev, "HCINT @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + addr = hsotg->regs + HCINTMSK(i); + dev_dbg(hsotg->dev, "HCINTMSK @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + addr = hsotg->regs + HCTSIZ(i); + dev_dbg(hsotg->dev, "HCTSIZ @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + addr = hsotg->regs + HCDMA(i); + dev_dbg(hsotg->dev, "HCDMA @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + if (hsotg->core_params->dma_desc_enable > 0) { + addr = hsotg->regs + HCDMAB(i); + dev_dbg(hsotg->dev, "HCDMAB @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + } + } +#endif +} + +/** + * dwc2_dump_global_registers() - Prints the core global registers + * + * @hsotg: Programming view of DWC_otg controller + * + * NOTE: This function will be removed once the peripheral controller code + * is integrated and the driver is stable + */ +void dwc2_dump_global_registers(struct dwc2_hsotg *hsotg) +{ +#ifdef DEBUG + u32 __iomem *addr; + + dev_dbg(hsotg->dev, "Core Global Registers\n"); + addr = hsotg->regs + GOTGCTL; + dev_dbg(hsotg->dev, "GOTGCTL @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + addr = hsotg->regs + GOTGINT; + dev_dbg(hsotg->dev, "GOTGINT @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + addr = hsotg->regs + GAHBCFG; + dev_dbg(hsotg->dev, "GAHBCFG @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + addr = hsotg->regs + GUSBCFG; + dev_dbg(hsotg->dev, "GUSBCFG @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + addr = hsotg->regs + GRSTCTL; + dev_dbg(hsotg->dev, "GRSTCTL @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + addr = hsotg->regs + GINTSTS; + dev_dbg(hsotg->dev, "GINTSTS @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + addr = hsotg->regs + GINTMSK; + dev_dbg(hsotg->dev, "GINTMSK @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + addr = hsotg->regs + GRXSTSR; + dev_dbg(hsotg->dev, "GRXSTSR @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + addr = hsotg->regs + GRXFSIZ; + dev_dbg(hsotg->dev, "GRXFSIZ @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + addr = hsotg->regs + GNPTXFSIZ; + dev_dbg(hsotg->dev, "GNPTXFSIZ @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + addr = hsotg->regs + GNPTXSTS; + dev_dbg(hsotg->dev, "GNPTXSTS @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + addr = hsotg->regs + GI2CCTL; + dev_dbg(hsotg->dev, "GI2CCTL @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + addr = hsotg->regs + GPVNDCTL; + dev_dbg(hsotg->dev, "GPVNDCTL @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + addr = hsotg->regs + GGPIO; + dev_dbg(hsotg->dev, "GGPIO @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + addr = hsotg->regs + GUID; + dev_dbg(hsotg->dev, "GUID @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + addr = hsotg->regs + GSNPSID; + dev_dbg(hsotg->dev, "GSNPSID @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + addr = hsotg->regs + GHWCFG1; + dev_dbg(hsotg->dev, "GHWCFG1 @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + addr = hsotg->regs + GHWCFG2; + dev_dbg(hsotg->dev, "GHWCFG2 @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + addr = hsotg->regs + GHWCFG3; + dev_dbg(hsotg->dev, "GHWCFG3 @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + addr = hsotg->regs + GHWCFG4; + dev_dbg(hsotg->dev, "GHWCFG4 @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + addr = hsotg->regs + GLPMCFG; + dev_dbg(hsotg->dev, "GLPMCFG @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + addr = hsotg->regs + GPWRDN; + dev_dbg(hsotg->dev, "GPWRDN @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + addr = hsotg->regs + GDFIFOCFG; + dev_dbg(hsotg->dev, "GDFIFOCFG @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + addr = hsotg->regs + HPTXFSIZ; + dev_dbg(hsotg->dev, "HPTXFSIZ @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); + + addr = hsotg->regs + PCGCTL; + dev_dbg(hsotg->dev, "PCGCTL @0x%08lX : 0x%08X\n", + (unsigned long)addr, readl(addr)); +#endif +} + +/** + * dwc2_flush_tx_fifo() - Flushes a Tx FIFO + * + * @hsotg: Programming view of DWC_otg controller + * @num: Tx FIFO to flush + */ +void dwc2_flush_tx_fifo(struct dwc2_hsotg *hsotg, const int num) +{ + u32 greset; + int count = 0; + + dev_vdbg(hsotg->dev, "Flush Tx FIFO %d\n", num); + + greset = GRSTCTL_TXFFLSH; + greset |= num << GRSTCTL_TXFNUM_SHIFT & GRSTCTL_TXFNUM_MASK; + writel(greset, hsotg->regs + GRSTCTL); + + do { + greset = readl(hsotg->regs + GRSTCTL); + if (++count > 10000) { + dev_warn(hsotg->dev, + "%s() HANG! GRSTCTL=%0x GNPTXSTS=0x%08x\n", + __func__, greset, + readl(hsotg->regs + GNPTXSTS)); + break; + } + udelay(1); + } while (greset & GRSTCTL_TXFFLSH); + + /* Wait for at least 3 PHY Clocks */ + udelay(1); +} + +/** + * dwc2_flush_rx_fifo() - Flushes the Rx FIFO + * + * @hsotg: Programming view of DWC_otg controller + */ +void dwc2_flush_rx_fifo(struct dwc2_hsotg *hsotg) +{ + u32 greset; + int count = 0; + + dev_vdbg(hsotg->dev, "%s()\n", __func__); + + greset = GRSTCTL_RXFFLSH; + writel(greset, hsotg->regs + GRSTCTL); + + do { + greset = readl(hsotg->regs + GRSTCTL); + if (++count > 10000) { + dev_warn(hsotg->dev, "%s() HANG! GRSTCTL=%0x\n", + __func__, greset); + break; + } + udelay(1); + } while (greset & GRSTCTL_RXFFLSH); + + /* Wait for at least 3 PHY Clocks */ + udelay(1); +} + +#define DWC2_OUT_OF_BOUNDS(a, b, c) ((a) < (b) || (a) > (c)) + +/* Parameter access functions */ +void dwc2_set_param_otg_cap(struct dwc2_hsotg *hsotg, int val) +{ + int valid = 1; + + switch (val) { + case DWC2_CAP_PARAM_HNP_SRP_CAPABLE: + if (hsotg->hw_params.op_mode != GHWCFG2_OP_MODE_HNP_SRP_CAPABLE) + valid = 0; + break; + case DWC2_CAP_PARAM_SRP_ONLY_CAPABLE: + switch (hsotg->hw_params.op_mode) { + case GHWCFG2_OP_MODE_HNP_SRP_CAPABLE: + case GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE: + case GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE: + case GHWCFG2_OP_MODE_SRP_CAPABLE_HOST: + break; + default: + valid = 0; + break; + } + break; + case DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE: + /* always valid */ + break; + default: + valid = 0; + break; + } + + if (!valid) { + if (val >= 0) + dev_err(hsotg->dev, + "%d invalid for otg_cap parameter. Check HW configuration.\n", + val); + switch (hsotg->hw_params.op_mode) { + case GHWCFG2_OP_MODE_HNP_SRP_CAPABLE: + val = DWC2_CAP_PARAM_HNP_SRP_CAPABLE; + break; + case GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE: + case GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE: + case GHWCFG2_OP_MODE_SRP_CAPABLE_HOST: + val = DWC2_CAP_PARAM_SRP_ONLY_CAPABLE; + break; + default: + val = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE; + break; + } + dev_dbg(hsotg->dev, "Setting otg_cap to %d\n", val); + } + + hsotg->core_params->otg_cap = val; +} + +void dwc2_set_param_dma_enable(struct dwc2_hsotg *hsotg, int val) +{ + int valid = 1; + + if (val > 0 && hsotg->hw_params.arch == GHWCFG2_SLAVE_ONLY_ARCH) + valid = 0; + if (val < 0) + valid = 0; + + if (!valid) { + if (val >= 0) + dev_err(hsotg->dev, + "%d invalid for dma_enable parameter. Check HW configuration.\n", + val); + val = hsotg->hw_params.arch != GHWCFG2_SLAVE_ONLY_ARCH; + dev_dbg(hsotg->dev, "Setting dma_enable to %d\n", val); + } + + hsotg->core_params->dma_enable = val; +} + +void dwc2_set_param_dma_desc_enable(struct dwc2_hsotg *hsotg, int val) +{ + int valid = 1; + + if (val > 0 && (hsotg->core_params->dma_enable <= 0 || + !hsotg->hw_params.dma_desc_enable)) + valid = 0; + if (val < 0) + valid = 0; + + if (!valid) { + if (val >= 0) + dev_err(hsotg->dev, + "%d invalid for dma_desc_enable parameter. Check HW configuration.\n", + val); + val = (hsotg->core_params->dma_enable > 0 && + hsotg->hw_params.dma_desc_enable); + dev_dbg(hsotg->dev, "Setting dma_desc_enable to %d\n", val); + } + + hsotg->core_params->dma_desc_enable = val; +} + +void dwc2_set_param_host_support_fs_ls_low_power(struct dwc2_hsotg *hsotg, + int val) +{ + if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) { + if (val >= 0) { + dev_err(hsotg->dev, + "Wrong value for host_support_fs_low_power\n"); + dev_err(hsotg->dev, + "host_support_fs_low_power must be 0 or 1\n"); + } + val = 0; + dev_dbg(hsotg->dev, + "Setting host_support_fs_low_power to %d\n", val); + } + + hsotg->core_params->host_support_fs_ls_low_power = val; +} + +void dwc2_set_param_enable_dynamic_fifo(struct dwc2_hsotg *hsotg, int val) +{ + int valid = 1; + + if (val > 0 && !hsotg->hw_params.enable_dynamic_fifo) + valid = 0; + if (val < 0) + valid = 0; + + if (!valid) { + if (val >= 0) + dev_err(hsotg->dev, + "%d invalid for enable_dynamic_fifo parameter. Check HW configuration.\n", + val); + val = hsotg->hw_params.enable_dynamic_fifo; + dev_dbg(hsotg->dev, "Setting enable_dynamic_fifo to %d\n", val); + } + + hsotg->core_params->enable_dynamic_fifo = val; +} + +void dwc2_set_param_host_rx_fifo_size(struct dwc2_hsotg *hsotg, int val) +{ + int valid = 1; + + if (val < 16 || val > hsotg->hw_params.host_rx_fifo_size) + valid = 0; + + if (!valid) { + if (val >= 0) + dev_err(hsotg->dev, + "%d invalid for host_rx_fifo_size. Check HW configuration.\n", + val); + val = hsotg->hw_params.host_rx_fifo_size; + dev_dbg(hsotg->dev, "Setting host_rx_fifo_size to %d\n", val); + } + + hsotg->core_params->host_rx_fifo_size = val; +} + +void dwc2_set_param_host_nperio_tx_fifo_size(struct dwc2_hsotg *hsotg, int val) +{ + int valid = 1; + + if (val < 16 || val > hsotg->hw_params.host_nperio_tx_fifo_size) + valid = 0; + + if (!valid) { + if (val >= 0) + dev_err(hsotg->dev, + "%d invalid for host_nperio_tx_fifo_size. Check HW configuration.\n", + val); + val = hsotg->hw_params.host_nperio_tx_fifo_size; + dev_dbg(hsotg->dev, "Setting host_nperio_tx_fifo_size to %d\n", + val); + } + + hsotg->core_params->host_nperio_tx_fifo_size = val; +} + +void dwc2_set_param_host_perio_tx_fifo_size(struct dwc2_hsotg *hsotg, int val) +{ + int valid = 1; + + if (val < 16 || val > hsotg->hw_params.host_perio_tx_fifo_size) + valid = 0; + + if (!valid) { + if (val >= 0) + dev_err(hsotg->dev, + "%d invalid for host_perio_tx_fifo_size. Check HW configuration.\n", + val); + val = hsotg->hw_params.host_perio_tx_fifo_size; + dev_dbg(hsotg->dev, "Setting host_perio_tx_fifo_size to %d\n", + val); + } + + hsotg->core_params->host_perio_tx_fifo_size = val; +} + +void dwc2_set_param_max_transfer_size(struct dwc2_hsotg *hsotg, int val) +{ + int valid = 1; + + if (val < 2047 || val > hsotg->hw_params.max_transfer_size) + valid = 0; + + if (!valid) { + if (val >= 0) + dev_err(hsotg->dev, + "%d invalid for max_transfer_size. Check HW configuration.\n", + val); + val = hsotg->hw_params.max_transfer_size; + dev_dbg(hsotg->dev, "Setting max_transfer_size to %d\n", val); + } + + hsotg->core_params->max_transfer_size = val; +} + +void dwc2_set_param_max_packet_count(struct dwc2_hsotg *hsotg, int val) +{ + int valid = 1; + + if (val < 15 || val > hsotg->hw_params.max_packet_count) + valid = 0; + + if (!valid) { + if (val >= 0) + dev_err(hsotg->dev, + "%d invalid for max_packet_count. Check HW configuration.\n", + val); + val = hsotg->hw_params.max_packet_count; + dev_dbg(hsotg->dev, "Setting max_packet_count to %d\n", val); + } + + hsotg->core_params->max_packet_count = val; +} + +void dwc2_set_param_host_channels(struct dwc2_hsotg *hsotg, int val) +{ + int valid = 1; + + if (val < 1 || val > hsotg->hw_params.host_channels) + valid = 0; + + if (!valid) { + if (val >= 0) + dev_err(hsotg->dev, + "%d invalid for host_channels. Check HW configuration.\n", + val); + val = hsotg->hw_params.host_channels; + dev_dbg(hsotg->dev, "Setting host_channels to %d\n", val); + } + + hsotg->core_params->host_channels = val; +} + +void dwc2_set_param_phy_type(struct dwc2_hsotg *hsotg, int val) +{ + int valid = 0; + u32 hs_phy_type, fs_phy_type; + + if (DWC2_OUT_OF_BOUNDS(val, DWC2_PHY_TYPE_PARAM_FS, + DWC2_PHY_TYPE_PARAM_ULPI)) { + if (val >= 0) { + dev_err(hsotg->dev, "Wrong value for phy_type\n"); + dev_err(hsotg->dev, "phy_type must be 0, 1 or 2\n"); + } + + valid = 0; + } + + hs_phy_type = hsotg->hw_params.hs_phy_type; + fs_phy_type = hsotg->hw_params.fs_phy_type; + if (val == DWC2_PHY_TYPE_PARAM_UTMI && + (hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI || + hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI_ULPI)) + valid = 1; + else if (val == DWC2_PHY_TYPE_PARAM_ULPI && + (hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI || + hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI_ULPI)) + valid = 1; + else if (val == DWC2_PHY_TYPE_PARAM_FS && + fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED) + valid = 1; + + if (!valid) { + if (val >= 0) + dev_err(hsotg->dev, + "%d invalid for phy_type. Check HW configuration.\n", + val); + val = DWC2_PHY_TYPE_PARAM_FS; + if (hs_phy_type != GHWCFG2_HS_PHY_TYPE_NOT_SUPPORTED) { + if (hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI || + hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI_ULPI) + val = DWC2_PHY_TYPE_PARAM_UTMI; + else + val = DWC2_PHY_TYPE_PARAM_ULPI; + } + dev_dbg(hsotg->dev, "Setting phy_type to %d\n", val); + } + + hsotg->core_params->phy_type = val; +} + +static int dwc2_get_param_phy_type(struct dwc2_hsotg *hsotg) +{ + return hsotg->core_params->phy_type; +} + +void dwc2_set_param_speed(struct dwc2_hsotg *hsotg, int val) +{ + int valid = 1; + + if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) { + if (val >= 0) { + dev_err(hsotg->dev, "Wrong value for speed parameter\n"); + dev_err(hsotg->dev, "max_speed parameter must be 0 or 1\n"); + } + valid = 0; + } + + if (val == DWC2_SPEED_PARAM_HIGH && + dwc2_get_param_phy_type(hsotg) == DWC2_PHY_TYPE_PARAM_FS) + valid = 0; + + if (!valid) { + if (val >= 0) + dev_err(hsotg->dev, + "%d invalid for speed parameter. Check HW configuration.\n", + val); + val = dwc2_get_param_phy_type(hsotg) == DWC2_PHY_TYPE_PARAM_FS ? + DWC2_SPEED_PARAM_FULL : DWC2_SPEED_PARAM_HIGH; + dev_dbg(hsotg->dev, "Setting speed to %d\n", val); + } + + hsotg->core_params->speed = val; +} + +void dwc2_set_param_host_ls_low_power_phy_clk(struct dwc2_hsotg *hsotg, int val) +{ + int valid = 1; + + if (DWC2_OUT_OF_BOUNDS(val, DWC2_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ, + DWC2_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ)) { + if (val >= 0) { + dev_err(hsotg->dev, + "Wrong value for host_ls_low_power_phy_clk parameter\n"); + dev_err(hsotg->dev, + "host_ls_low_power_phy_clk must be 0 or 1\n"); + } + valid = 0; + } + + if (val == DWC2_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ && + dwc2_get_param_phy_type(hsotg) == DWC2_PHY_TYPE_PARAM_FS) + valid = 0; + + if (!valid) { + if (val >= 0) + dev_err(hsotg->dev, + "%d invalid for host_ls_low_power_phy_clk. Check HW configuration.\n", + val); + val = dwc2_get_param_phy_type(hsotg) == DWC2_PHY_TYPE_PARAM_FS + ? DWC2_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ + : DWC2_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ; + dev_dbg(hsotg->dev, "Setting host_ls_low_power_phy_clk to %d\n", + val); + } + + hsotg->core_params->host_ls_low_power_phy_clk = val; +} + +void dwc2_set_param_phy_ulpi_ddr(struct dwc2_hsotg *hsotg, int val) +{ + if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) { + if (val >= 0) { + dev_err(hsotg->dev, "Wrong value for phy_ulpi_ddr\n"); + dev_err(hsotg->dev, "phy_upli_ddr must be 0 or 1\n"); + } + val = 0; + dev_dbg(hsotg->dev, "Setting phy_upli_ddr to %d\n", val); + } + + hsotg->core_params->phy_ulpi_ddr = val; +} + +void dwc2_set_param_phy_ulpi_ext_vbus(struct dwc2_hsotg *hsotg, int val) +{ + if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) { + if (val >= 0) { + dev_err(hsotg->dev, + "Wrong value for phy_ulpi_ext_vbus\n"); + dev_err(hsotg->dev, + "phy_ulpi_ext_vbus must be 0 or 1\n"); + } + val = 0; + dev_dbg(hsotg->dev, "Setting phy_ulpi_ext_vbus to %d\n", val); + } + + hsotg->core_params->phy_ulpi_ext_vbus = val; +} + +void dwc2_set_param_phy_utmi_width(struct dwc2_hsotg *hsotg, int val) +{ + int valid = 0; + + switch (hsotg->hw_params.utmi_phy_data_width) { + case GHWCFG4_UTMI_PHY_DATA_WIDTH_8: + valid = (val == 8); + break; + case GHWCFG4_UTMI_PHY_DATA_WIDTH_16: + valid = (val == 16); + break; + case GHWCFG4_UTMI_PHY_DATA_WIDTH_8_OR_16: + valid = (val == 8 || val == 16); + break; + } + + if (!valid) { + if (val >= 0) { + dev_err(hsotg->dev, + "%d invalid for phy_utmi_width. Check HW configuration.\n", + val); + } + val = (hsotg->hw_params.utmi_phy_data_width == + GHWCFG4_UTMI_PHY_DATA_WIDTH_8) ? 8 : 16; + dev_dbg(hsotg->dev, "Setting phy_utmi_width to %d\n", val); + } + + hsotg->core_params->phy_utmi_width = val; +} + +void dwc2_set_param_ulpi_fs_ls(struct dwc2_hsotg *hsotg, int val) +{ + if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) { + if (val >= 0) { + dev_err(hsotg->dev, "Wrong value for ulpi_fs_ls\n"); + dev_err(hsotg->dev, "ulpi_fs_ls must be 0 or 1\n"); + } + val = 0; + dev_dbg(hsotg->dev, "Setting ulpi_fs_ls to %d\n", val); + } + + hsotg->core_params->ulpi_fs_ls = val; +} + +void dwc2_set_param_ts_dline(struct dwc2_hsotg *hsotg, int val) +{ + if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) { + if (val >= 0) { + dev_err(hsotg->dev, "Wrong value for ts_dline\n"); + dev_err(hsotg->dev, "ts_dline must be 0 or 1\n"); + } + val = 0; + dev_dbg(hsotg->dev, "Setting ts_dline to %d\n", val); + } + + hsotg->core_params->ts_dline = val; +} + +void dwc2_set_param_i2c_enable(struct dwc2_hsotg *hsotg, int val) +{ + int valid = 1; + + if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) { + if (val >= 0) { + dev_err(hsotg->dev, "Wrong value for i2c_enable\n"); + dev_err(hsotg->dev, "i2c_enable must be 0 or 1\n"); + } + + valid = 0; + } + + if (val == 1 && !(hsotg->hw_params.i2c_enable)) + valid = 0; + + if (!valid) { + if (val >= 0) + dev_err(hsotg->dev, + "%d invalid for i2c_enable. Check HW configuration.\n", + val); + val = hsotg->hw_params.i2c_enable; + dev_dbg(hsotg->dev, "Setting i2c_enable to %d\n", val); + } + + hsotg->core_params->i2c_enable = val; +} + +void dwc2_set_param_en_multiple_tx_fifo(struct dwc2_hsotg *hsotg, int val) +{ + int valid = 1; + + if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) { + if (val >= 0) { + dev_err(hsotg->dev, + "Wrong value for en_multiple_tx_fifo,\n"); + dev_err(hsotg->dev, + "en_multiple_tx_fifo must be 0 or 1\n"); + } + valid = 0; + } + + if (val == 1 && !hsotg->hw_params.en_multiple_tx_fifo) + valid = 0; + + if (!valid) { + if (val >= 0) + dev_err(hsotg->dev, + "%d invalid for parameter en_multiple_tx_fifo. Check HW configuration.\n", + val); + val = hsotg->hw_params.en_multiple_tx_fifo; + dev_dbg(hsotg->dev, "Setting en_multiple_tx_fifo to %d\n", val); + } + + hsotg->core_params->en_multiple_tx_fifo = val; +} + +void dwc2_set_param_reload_ctl(struct dwc2_hsotg *hsotg, int val) +{ + int valid = 1; + + if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) { + if (val >= 0) { + dev_err(hsotg->dev, + "'%d' invalid for parameter reload_ctl\n", val); + dev_err(hsotg->dev, "reload_ctl must be 0 or 1\n"); + } + valid = 0; + } + + if (val == 1 && hsotg->hw_params.snpsid < DWC2_CORE_REV_2_92a) + valid = 0; + + if (!valid) { + if (val >= 0) + dev_err(hsotg->dev, + "%d invalid for parameter reload_ctl. Check HW configuration.\n", + val); + val = hsotg->hw_params.snpsid >= DWC2_CORE_REV_2_92a; + dev_dbg(hsotg->dev, "Setting reload_ctl to %d\n", val); + } + + hsotg->core_params->reload_ctl = val; +} + +void dwc2_set_param_ahbcfg(struct dwc2_hsotg *hsotg, int val) +{ + if (val != -1) + hsotg->core_params->ahbcfg = val; + else + hsotg->core_params->ahbcfg = GAHBCFG_HBSTLEN_INCR4 << + GAHBCFG_HBSTLEN_SHIFT; +} + +void dwc2_set_param_otg_ver(struct dwc2_hsotg *hsotg, int val) +{ + if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) { + if (val >= 0) { + dev_err(hsotg->dev, + "'%d' invalid for parameter otg_ver\n", val); + dev_err(hsotg->dev, + "otg_ver must be 0 (for OTG 1.3 support) or 1 (for OTG 2.0 support)\n"); + } + val = 0; + dev_dbg(hsotg->dev, "Setting otg_ver to %d\n", val); + } + + hsotg->core_params->otg_ver = val; +} + +static void dwc2_set_param_uframe_sched(struct dwc2_hsotg *hsotg, int val) +{ + if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) { + if (val >= 0) { + dev_err(hsotg->dev, + "'%d' invalid for parameter uframe_sched\n", + val); + dev_err(hsotg->dev, "uframe_sched must be 0 or 1\n"); + } + val = 1; + dev_dbg(hsotg->dev, "Setting uframe_sched to %d\n", val); + } + + hsotg->core_params->uframe_sched = val; +} + +/* + * This function is called during module intialization to pass module parameters + * for the DWC_otg core. + */ +void dwc2_set_parameters(struct dwc2_hsotg *hsotg, + const struct dwc2_core_params *params) +{ + dev_dbg(hsotg->dev, "%s()\n", __func__); + + dwc2_set_param_otg_cap(hsotg, params->otg_cap); + dwc2_set_param_dma_enable(hsotg, params->dma_enable); + dwc2_set_param_dma_desc_enable(hsotg, params->dma_desc_enable); + dwc2_set_param_host_support_fs_ls_low_power(hsotg, + params->host_support_fs_ls_low_power); + dwc2_set_param_enable_dynamic_fifo(hsotg, + params->enable_dynamic_fifo); + dwc2_set_param_host_rx_fifo_size(hsotg, + params->host_rx_fifo_size); + dwc2_set_param_host_nperio_tx_fifo_size(hsotg, + params->host_nperio_tx_fifo_size); + dwc2_set_param_host_perio_tx_fifo_size(hsotg, + params->host_perio_tx_fifo_size); + dwc2_set_param_max_transfer_size(hsotg, + params->max_transfer_size); + dwc2_set_param_max_packet_count(hsotg, + params->max_packet_count); + dwc2_set_param_host_channels(hsotg, params->host_channels); + dwc2_set_param_phy_type(hsotg, params->phy_type); + dwc2_set_param_speed(hsotg, params->speed); + dwc2_set_param_host_ls_low_power_phy_clk(hsotg, + params->host_ls_low_power_phy_clk); + dwc2_set_param_phy_ulpi_ddr(hsotg, params->phy_ulpi_ddr); + dwc2_set_param_phy_ulpi_ext_vbus(hsotg, + params->phy_ulpi_ext_vbus); + dwc2_set_param_phy_utmi_width(hsotg, params->phy_utmi_width); + dwc2_set_param_ulpi_fs_ls(hsotg, params->ulpi_fs_ls); + dwc2_set_param_ts_dline(hsotg, params->ts_dline); + dwc2_set_param_i2c_enable(hsotg, params->i2c_enable); + dwc2_set_param_en_multiple_tx_fifo(hsotg, + params->en_multiple_tx_fifo); + dwc2_set_param_reload_ctl(hsotg, params->reload_ctl); + dwc2_set_param_ahbcfg(hsotg, params->ahbcfg); + dwc2_set_param_otg_ver(hsotg, params->otg_ver); + dwc2_set_param_uframe_sched(hsotg, params->uframe_sched); +} + +/** + * During device initialization, read various hardware configuration + * registers and interpret the contents. + */ +int dwc2_get_hwparams(struct dwc2_hsotg *hsotg) +{ + struct dwc2_hw_params *hw = &hsotg->hw_params; + unsigned width; + u32 hwcfg1, hwcfg2, hwcfg3, hwcfg4; + u32 hptxfsiz, grxfsiz, gnptxfsiz; + u32 gusbcfg; + + /* + * Attempt to ensure this device is really a DWC_otg Controller. + * Read and verify the GSNPSID register contents. The value should be + * 0x45f42xxx or 0x45f43xxx, which corresponds to either "OT2" or "OT3", + * as in "OTG version 2.xx" or "OTG version 3.xx". + */ + hw->snpsid = readl(hsotg->regs + GSNPSID); + if ((hw->snpsid & 0xfffff000) != 0x4f542000 && + (hw->snpsid & 0xfffff000) != 0x4f543000) { + dev_err(hsotg->dev, "Bad value for GSNPSID: 0x%08x\n", + hw->snpsid); + return -ENODEV; + } + + dev_dbg(hsotg->dev, "Core Release: %1x.%1x%1x%1x (snpsid=%x)\n", + hw->snpsid >> 12 & 0xf, hw->snpsid >> 8 & 0xf, + hw->snpsid >> 4 & 0xf, hw->snpsid & 0xf, hw->snpsid); + + hwcfg1 = readl(hsotg->regs + GHWCFG1); + hwcfg2 = readl(hsotg->regs + GHWCFG2); + hwcfg3 = readl(hsotg->regs + GHWCFG3); + hwcfg4 = readl(hsotg->regs + GHWCFG4); + gnptxfsiz = readl(hsotg->regs + GNPTXFSIZ); + grxfsiz = readl(hsotg->regs + GRXFSIZ); + + dev_dbg(hsotg->dev, "hwcfg1=%08x\n", hwcfg1); + dev_dbg(hsotg->dev, "hwcfg2=%08x\n", hwcfg2); + dev_dbg(hsotg->dev, "hwcfg3=%08x\n", hwcfg3); + dev_dbg(hsotg->dev, "hwcfg4=%08x\n", hwcfg4); + dev_dbg(hsotg->dev, "gnptxfsiz=%08x\n", gnptxfsiz); + dev_dbg(hsotg->dev, "grxfsiz=%08x\n", grxfsiz); + + /* Force host mode to get HPTXFSIZ exact power on value */ + gusbcfg = readl(hsotg->regs + GUSBCFG); + gusbcfg |= GUSBCFG_FORCEHOSTMODE; + writel(gusbcfg, hsotg->regs + GUSBCFG); + usleep_range(100000, 150000); + + hptxfsiz = readl(hsotg->regs + HPTXFSIZ); + dev_dbg(hsotg->dev, "hptxfsiz=%08x\n", hptxfsiz); + gusbcfg = readl(hsotg->regs + GUSBCFG); + gusbcfg &= ~GUSBCFG_FORCEHOSTMODE; + writel(gusbcfg, hsotg->regs + GUSBCFG); + usleep_range(100000, 150000); + + /* hwcfg2 */ + hw->op_mode = (hwcfg2 & GHWCFG2_OP_MODE_MASK) >> + GHWCFG2_OP_MODE_SHIFT; + hw->arch = (hwcfg2 & GHWCFG2_ARCHITECTURE_MASK) >> + GHWCFG2_ARCHITECTURE_SHIFT; + hw->enable_dynamic_fifo = !!(hwcfg2 & GHWCFG2_DYNAMIC_FIFO); + hw->host_channels = 1 + ((hwcfg2 & GHWCFG2_NUM_HOST_CHAN_MASK) >> + GHWCFG2_NUM_HOST_CHAN_SHIFT); + hw->hs_phy_type = (hwcfg2 & GHWCFG2_HS_PHY_TYPE_MASK) >> + GHWCFG2_HS_PHY_TYPE_SHIFT; + hw->fs_phy_type = (hwcfg2 & GHWCFG2_FS_PHY_TYPE_MASK) >> + GHWCFG2_FS_PHY_TYPE_SHIFT; + hw->num_dev_ep = (hwcfg2 & GHWCFG2_NUM_DEV_EP_MASK) >> + GHWCFG2_NUM_DEV_EP_SHIFT; + hw->nperio_tx_q_depth = + (hwcfg2 & GHWCFG2_NONPERIO_TX_Q_DEPTH_MASK) >> + GHWCFG2_NONPERIO_TX_Q_DEPTH_SHIFT << 1; + hw->host_perio_tx_q_depth = + (hwcfg2 & GHWCFG2_HOST_PERIO_TX_Q_DEPTH_MASK) >> + GHWCFG2_HOST_PERIO_TX_Q_DEPTH_SHIFT << 1; + hw->dev_token_q_depth = + (hwcfg2 & GHWCFG2_DEV_TOKEN_Q_DEPTH_MASK) >> + GHWCFG2_DEV_TOKEN_Q_DEPTH_SHIFT; + + /* hwcfg3 */ + width = (hwcfg3 & GHWCFG3_XFER_SIZE_CNTR_WIDTH_MASK) >> + GHWCFG3_XFER_SIZE_CNTR_WIDTH_SHIFT; + hw->max_transfer_size = (1 << (width + 11)) - 1; + width = (hwcfg3 & GHWCFG3_PACKET_SIZE_CNTR_WIDTH_MASK) >> + GHWCFG3_PACKET_SIZE_CNTR_WIDTH_SHIFT; + hw->max_packet_count = (1 << (width + 4)) - 1; + hw->i2c_enable = !!(hwcfg3 & GHWCFG3_I2C); + hw->total_fifo_size = (hwcfg3 & GHWCFG3_DFIFO_DEPTH_MASK) >> + GHWCFG3_DFIFO_DEPTH_SHIFT; + + /* hwcfg4 */ + hw->en_multiple_tx_fifo = !!(hwcfg4 & GHWCFG4_DED_FIFO_EN); + hw->num_dev_perio_in_ep = (hwcfg4 & GHWCFG4_NUM_DEV_PERIO_IN_EP_MASK) >> + GHWCFG4_NUM_DEV_PERIO_IN_EP_SHIFT; + hw->dma_desc_enable = !!(hwcfg4 & GHWCFG4_DESC_DMA); + hw->power_optimized = !!(hwcfg4 & GHWCFG4_POWER_OPTIMIZ); + hw->utmi_phy_data_width = (hwcfg4 & GHWCFG4_UTMI_PHY_DATA_WIDTH_MASK) >> + GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT; + + /* fifo sizes */ + hw->host_rx_fifo_size = (grxfsiz & GRXFSIZ_DEPTH_MASK) >> + GRXFSIZ_DEPTH_SHIFT; + hw->host_nperio_tx_fifo_size = (gnptxfsiz & FIFOSIZE_DEPTH_MASK) >> + FIFOSIZE_DEPTH_SHIFT; + hw->host_perio_tx_fifo_size = (hptxfsiz & FIFOSIZE_DEPTH_MASK) >> + FIFOSIZE_DEPTH_SHIFT; + + dev_dbg(hsotg->dev, "Detected values from hardware:\n"); + dev_dbg(hsotg->dev, " op_mode=%d\n", + hw->op_mode); + dev_dbg(hsotg->dev, " arch=%d\n", + hw->arch); + dev_dbg(hsotg->dev, " dma_desc_enable=%d\n", + hw->dma_desc_enable); + dev_dbg(hsotg->dev, " power_optimized=%d\n", + hw->power_optimized); + dev_dbg(hsotg->dev, " i2c_enable=%d\n", + hw->i2c_enable); + dev_dbg(hsotg->dev, " hs_phy_type=%d\n", + hw->hs_phy_type); + dev_dbg(hsotg->dev, " fs_phy_type=%d\n", + hw->fs_phy_type); + dev_dbg(hsotg->dev, " utmi_phy_data_wdith=%d\n", + hw->utmi_phy_data_width); + dev_dbg(hsotg->dev, " num_dev_ep=%d\n", + hw->num_dev_ep); + dev_dbg(hsotg->dev, " num_dev_perio_in_ep=%d\n", + hw->num_dev_perio_in_ep); + dev_dbg(hsotg->dev, " host_channels=%d\n", + hw->host_channels); + dev_dbg(hsotg->dev, " max_transfer_size=%d\n", + hw->max_transfer_size); + dev_dbg(hsotg->dev, " max_packet_count=%d\n", + hw->max_packet_count); + dev_dbg(hsotg->dev, " nperio_tx_q_depth=0x%0x\n", + hw->nperio_tx_q_depth); + dev_dbg(hsotg->dev, " host_perio_tx_q_depth=0x%0x\n", + hw->host_perio_tx_q_depth); + dev_dbg(hsotg->dev, " dev_token_q_depth=0x%0x\n", + hw->dev_token_q_depth); + dev_dbg(hsotg->dev, " enable_dynamic_fifo=%d\n", + hw->enable_dynamic_fifo); + dev_dbg(hsotg->dev, " en_multiple_tx_fifo=%d\n", + hw->en_multiple_tx_fifo); + dev_dbg(hsotg->dev, " total_fifo_size=%d\n", + hw->total_fifo_size); + dev_dbg(hsotg->dev, " host_rx_fifo_size=%d\n", + hw->host_rx_fifo_size); + dev_dbg(hsotg->dev, " host_nperio_tx_fifo_size=%d\n", + hw->host_nperio_tx_fifo_size); + dev_dbg(hsotg->dev, " host_perio_tx_fifo_size=%d\n", + hw->host_perio_tx_fifo_size); + dev_dbg(hsotg->dev, "\n"); + + return 0; +} + +u16 dwc2_get_otg_version(struct dwc2_hsotg *hsotg) +{ + return hsotg->core_params->otg_ver == 1 ? 0x0200 : 0x0103; +} + +bool dwc2_is_controller_alive(struct dwc2_hsotg *hsotg) +{ + if (readl(hsotg->regs + GSNPSID) == 0xffffffff) + return false; + else + return true; +} + +/** + * dwc2_enable_global_interrupts() - Enables the controller's Global + * Interrupt in the AHB Config register + * + * @hsotg: Programming view of DWC_otg controller + */ +void dwc2_enable_global_interrupts(struct dwc2_hsotg *hsotg) +{ + u32 ahbcfg = readl(hsotg->regs + GAHBCFG); + + ahbcfg |= GAHBCFG_GLBL_INTR_EN; + writel(ahbcfg, hsotg->regs + GAHBCFG); +} + +/** + * dwc2_disable_global_interrupts() - Disables the controller's Global + * Interrupt in the AHB Config register + * + * @hsotg: Programming view of DWC_otg controller + */ +void dwc2_disable_global_interrupts(struct dwc2_hsotg *hsotg) +{ + u32 ahbcfg = readl(hsotg->regs + GAHBCFG); + + ahbcfg &= ~GAHBCFG_GLBL_INTR_EN; + writel(ahbcfg, hsotg->regs + GAHBCFG); +} + +MODULE_DESCRIPTION("DESIGNWARE HS OTG Core"); +MODULE_AUTHOR("Synopsys, Inc."); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h new file mode 100644 index 0000000..648519c --- /dev/null +++ b/drivers/usb/dwc2/core.h @@ -0,0 +1,768 @@ +/* + * core.h - DesignWare HS OTG Controller common declarations + * + * Copyright (C) 2004-2013 Synopsys, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __DWC2_CORE_H__ +#define __DWC2_CORE_H__ + +#include <linux/usb/phy.h> +#include "hw.h" + +#ifdef DWC2_LOG_WRITES +static inline void do_write(u32 value, void *addr) +{ + writel(value, addr); + pr_info("INFO:: wrote %08x to %p\n", value, addr); +} + +#undef writel +#define writel(v, a) do_write(v, a) +#endif + +/* Maximum number of Endpoints/HostChannels */ +#define MAX_EPS_CHANNELS 16 + +struct dwc2_hsotg; +struct dwc2_host_chan; + +/* Device States */ +enum dwc2_lx_state { + DWC2_L0, /* On state */ + DWC2_L1, /* LPM sleep state */ + DWC2_L2, /* USB suspend state */ + DWC2_L3, /* Off state */ +}; + +/** + * struct dwc2_core_params - Parameters for configuring the core + * + * @otg_cap: Specifies the OTG capabilities. + * 0 - HNP and SRP capable + * 1 - SRP Only capable + * 2 - No HNP/SRP capable (always available) + * Defaults to best available option (0, 1, then 2) + * @otg_ver: OTG version supported + * 0 - 1.3 (default) + * 1 - 2.0 + * @dma_enable: Specifies whether to use slave or DMA mode for accessing + * the data FIFOs. The driver will automatically detect the + * value for this parameter if none is specified. + * 0 - Slave (always available) + * 1 - DMA (default, if available) + * @dma_desc_enable: When DMA mode is enabled, specifies whether to use + * address DMA mode or descriptor DMA mode for accessing + * the data FIFOs. The driver will automatically detect the + * value for this if none is specified. + * 0 - Address DMA + * 1 - Descriptor DMA (default, if available) + * @speed: Specifies the maximum speed of operation in host and + * device mode. The actual speed depends on the speed of + * the attached device and the value of phy_type. + * 0 - High Speed + * (default when phy_type is UTMI+ or ULPI) + * 1 - Full Speed + * (default when phy_type is Full Speed) + * @enable_dynamic_fifo: 0 - Use coreConsultant-specified FIFO size parameters + * 1 - Allow dynamic FIFO sizing (default, if available) + * @en_multiple_tx_fifo: Specifies whether dedicated per-endpoint transmit FIFOs + * are enabled + * @host_rx_fifo_size: Number of 4-byte words in the Rx FIFO in host mode when + * dynamic FIFO sizing is enabled + * 16 to 32768 + * Actual maximum value is autodetected and also + * the default. + * @host_nperio_tx_fifo_size: Number of 4-byte words in the non-periodic Tx FIFO + * in host mode when dynamic FIFO sizing is enabled + * 16 to 32768 + * Actual maximum value is autodetected and also + * the default. + * @host_perio_tx_fifo_size: Number of 4-byte words in the periodic Tx FIFO in + * host mode when dynamic FIFO sizing is enabled + * 16 to 32768 + * Actual maximum value is autodetected and also + * the default. + * @max_transfer_size: The maximum transfer size supported, in bytes + * 2047 to 65,535 + * Actual maximum value is autodetected and also + * the default. + * @max_packet_count: The maximum number of packets in a transfer + * 15 to 511 + * Actual maximum value is autodetected and also + * the default. + * @host_channels: The number of host channel registers to use + * 1 to 16 + * Actual maximum value is autodetected and also + * the default. + * @phy_type: Specifies the type of PHY interface to use. By default, + * the driver will automatically detect the phy_type. + * 0 - Full Speed Phy + * 1 - UTMI+ Phy + * 2 - ULPI Phy + * Defaults to best available option (2, 1, then 0) + * @phy_utmi_width: Specifies the UTMI+ Data Width (in bits). This parameter + * is applicable for a phy_type of UTMI+ or ULPI. (For a + * ULPI phy_type, this parameter indicates the data width + * between the MAC and the ULPI Wrapper.) Also, this + * parameter is applicable only if the OTG_HSPHY_WIDTH cC + * parameter was set to "8 and 16 bits", meaning that the + * core has been configured to work at either data path + * width. + * 8 or 16 (default 16 if available) + * @phy_ulpi_ddr: Specifies whether the ULPI operates at double or single + * data rate. This parameter is only applicable if phy_type + * is ULPI. + * 0 - single data rate ULPI interface with 8 bit wide + * data bus (default) + * 1 - double data rate ULPI interface with 4 bit wide + * data bus + * @phy_ulpi_ext_vbus: For a ULPI phy, specifies whether to use the internal or + * external supply to drive the VBus + * 0 - Internal supply (default) + * 1 - External supply + * @i2c_enable: Specifies whether to use the I2Cinterface for a full + * speed PHY. This parameter is only applicable if phy_type + * is FS. + * 0 - No (default) + * 1 - Yes + * @ulpi_fs_ls: Make ULPI phy operate in FS/LS mode only + * 0 - No (default) + * 1 - Yes + * @host_support_fs_ls_low_power: Specifies whether low power mode is supported + * when attached to a Full Speed or Low Speed device in + * host mode. + * 0 - Don't support low power mode (default) + * 1 - Support low power mode + * @host_ls_low_power_phy_clk: Specifies the PHY clock rate in low power mode + * when connected to a Low Speed device in host + * mode. This parameter is applicable only if + * host_support_fs_ls_low_power is enabled. + * 0 - 48 MHz + * (default when phy_type is UTMI+ or ULPI) + * 1 - 6 MHz + * (default when phy_type is Full Speed) + * @ts_dline: Enable Term Select Dline pulsing + * 0 - No (default) + * 1 - Yes + * @reload_ctl: Allow dynamic reloading of HFIR register during runtime + * 0 - No (default for core < 2.92a) + * 1 - Yes (default for core >= 2.92a) + * @ahbcfg: This field allows the default value of the GAHBCFG + * register to be overridden + * -1 - GAHBCFG value will be set to 0x06 + * (INCR4, default) + * all others - GAHBCFG value will be overridden with + * this value + * Not all bits can be controlled like this, the + * bits defined by GAHBCFG_CTRL_MASK are controlled + * by the driver and are ignored in this + * configuration value. + * @uframe_sched: True to enable the microframe scheduler + * + * The following parameters may be specified when starting the module. These + * parameters define how the DWC_otg controller should be configured. A + * value of -1 (or any other out of range value) for any parameter means + * to read the value from hardware (if possible) or use the builtin + * default described above. + */ +struct dwc2_core_params { + /* + * Don't add any non-int members here, this will break + * dwc2_set_all_params! + */ + int otg_cap; + int otg_ver; + int dma_enable; + int dma_desc_enable; + int speed; + int enable_dynamic_fifo; + int en_multiple_tx_fifo; + int host_rx_fifo_size; + int host_nperio_tx_fifo_size; + int host_perio_tx_fifo_size; + int max_transfer_size; + int max_packet_count; + int host_channels; + int phy_type; + int phy_utmi_width; + int phy_ulpi_ddr; + int phy_ulpi_ext_vbus; + int i2c_enable; + int ulpi_fs_ls; + int host_support_fs_ls_low_power; + int host_ls_low_power_phy_clk; + int ts_dline; + int reload_ctl; + int ahbcfg; + int uframe_sched; +}; + +/** + * struct dwc2_hw_params - Autodetected parameters. + * + * These parameters are the various parameters read from hardware + * registers during initialization. They typically contain the best + * supported or maximum value that can be configured in the + * corresponding dwc2_core_params value. + * + * The values that are not in dwc2_core_params are documented below. + * + * @op_mode Mode of Operation + * 0 - HNP- and SRP-Capable OTG (Host & Device) + * 1 - SRP-Capable OTG (Host & Device) + * 2 - Non-HNP and Non-SRP Capable OTG (Host & Device) + * 3 - SRP-Capable Device + * 4 - Non-OTG Device + * 5 - SRP-Capable Host + * 6 - Non-OTG Host + * @arch Architecture + * 0 - Slave only + * 1 - External DMA + * 2 - Internal DMA + * @power_optimized Are power optimizations enabled? + * @num_dev_ep Number of device endpoints available + * @num_dev_perio_in_ep Number of device periodic IN endpoints + * avaialable + * @dev_token_q_depth Device Mode IN Token Sequence Learning Queue + * Depth + * 0 to 30 + * @host_perio_tx_q_depth + * Host Mode Periodic Request Queue Depth + * 2, 4 or 8 + * @nperio_tx_q_depth + * Non-Periodic Request Queue Depth + * 2, 4 or 8 + * @hs_phy_type High-speed PHY interface type + * 0 - High-speed interface not supported + * 1 - UTMI+ + * 2 - ULPI + * 3 - UTMI+ and ULPI + * @fs_phy_type Full-speed PHY interface type + * 0 - Full speed interface not supported + * 1 - Dedicated full speed interface + * 2 - FS pins shared with UTMI+ pins + * 3 - FS pins shared with ULPI pins + * @total_fifo_size: Total internal RAM for FIFOs (bytes) + * @utmi_phy_data_width UTMI+ PHY data width + * 0 - 8 bits + * 1 - 16 bits + * 2 - 8 or 16 bits + * @snpsid: Value from SNPSID register + */ +struct dwc2_hw_params { + unsigned op_mode:3; + unsigned arch:2; + unsigned dma_desc_enable:1; + unsigned enable_dynamic_fifo:1; + unsigned en_multiple_tx_fifo:1; + unsigned host_rx_fifo_size:16; + unsigned host_nperio_tx_fifo_size:16; + unsigned host_perio_tx_fifo_size:16; + unsigned nperio_tx_q_depth:3; + unsigned host_perio_tx_q_depth:3; + unsigned dev_token_q_depth:5; + unsigned max_transfer_size:26; + unsigned max_packet_count:11; + unsigned host_channels:5; + unsigned hs_phy_type:2; + unsigned fs_phy_type:2; + unsigned i2c_enable:1; + unsigned num_dev_ep:4; + unsigned num_dev_perio_in_ep:4; + unsigned total_fifo_size:16; + unsigned power_optimized:1; + unsigned utmi_phy_data_width:2; + u32 snpsid; +}; + +/** + * struct dwc2_hsotg - Holds the state of the driver, including the non-periodic + * and periodic schedules + * + * @dev: The struct device pointer + * @regs: Pointer to controller regs + * @core_params: Parameters that define how the core should be configured + * @hw_params: Parameters that were autodetected from the + * hardware registers + * @op_state: The operational State, during transitions (a_host=> + * a_peripheral and b_device=>b_host) this may not match + * the core, but allows the software to determine + * transitions + * @queuing_high_bandwidth: True if multiple packets of a high-bandwidth + * transfer are in process of being queued + * @srp_success: Stores status of SRP request in the case of a FS PHY + * with an I2C interface + * @wq_otg: Workqueue object used for handling of some interrupts + * @wf_otg: Work object for handling Connector ID Status Change + * interrupt + * @wkp_timer: Timer object for handling Wakeup Detected interrupt + * @lx_state: Lx state of connected device + * @flags: Flags for handling root port state changes + * @non_periodic_sched_inactive: Inactive QHs in the non-periodic schedule. + * Transfers associated with these QHs are not currently + * assigned to a host channel. + * @non_periodic_sched_active: Active QHs in the non-periodic schedule. + * Transfers associated with these QHs are currently + * assigned to a host channel. + * @non_periodic_qh_ptr: Pointer to next QH to process in the active + * non-periodic schedule + * @periodic_sched_inactive: Inactive QHs in the periodic schedule. This is a + * list of QHs for periodic transfers that are _not_ + * scheduled for the next frame. Each QH in the list has an + * interval counter that determines when it needs to be + * scheduled for execution. This scheduling mechanism + * allows only a simple calculation for periodic bandwidth + * used (i.e. must assume that all periodic transfers may + * need to execute in the same frame). However, it greatly + * simplifies scheduling and should be sufficient for the + * vast majority of OTG hosts, which need to connect to a + * small number of peripherals at one time. Items move from + * this list to periodic_sched_ready when the QH interval + * counter is 0 at SOF. + * @periodic_sched_ready: List of periodic QHs that are ready for execution in + * the next frame, but have not yet been assigned to host + * channels. Items move from this list to + * periodic_sched_assigned as host channels become + * available during the current frame. + * @periodic_sched_assigned: List of periodic QHs to be executed in the next + * frame that are assigned to host channels. Items move + * from this list to periodic_sched_queued as the + * transactions for the QH are queued to the DWC_otg + * controller. + * @periodic_sched_queued: List of periodic QHs that have been queued for + * execution. Items move from this list to either + * periodic_sched_inactive or periodic_sched_ready when the + * channel associated with the transfer is released. If the + * interval for the QH is 1, the item moves to + * periodic_sched_ready because it must be rescheduled for + * the next frame. Otherwise, the item moves to + * periodic_sched_inactive. + * @periodic_usecs: Total bandwidth claimed so far for periodic transfers. + * This value is in microseconds per (micro)frame. The + * assumption is that all periodic transfers may occur in + * the same (micro)frame. + * @frame_usecs: Internal variable used by the microframe scheduler + * @frame_number: Frame number read from the core at SOF. The value ranges + * from 0 to HFNUM_MAX_FRNUM. + * @periodic_qh_count: Count of periodic QHs, if using several eps. Used for + * SOF enable/disable. + * @free_hc_list: Free host channels in the controller. This is a list of + * struct dwc2_host_chan items. + * @periodic_channels: Number of host channels assigned to periodic transfers. + * Currently assuming that there is a dedicated host + * channel for each periodic transaction and at least one + * host channel is available for non-periodic transactions. + * @non_periodic_channels: Number of host channels assigned to non-periodic + * transfers + * @available_host_channels Number of host channels available for the microframe + * scheduler to use + * @hc_ptr_array: Array of pointers to the host channel descriptors. + * Allows accessing a host channel descriptor given the + * host channel number. This is useful in interrupt + * handlers. + * @status_buf: Buffer used for data received during the status phase of + * a control transfer. + * @status_buf_dma: DMA address for status_buf + * @start_work: Delayed work for handling host A-cable connection + * @reset_work: Delayed work for handling a port reset + * @lock: Spinlock that protects all the driver data structures + * @priv: Stores a pointer to the struct usb_hcd + * @otg_port: OTG port number + * @frame_list: Frame list + * @frame_list_dma: Frame list DMA address + */ +struct dwc2_hsotg { + struct device *dev; + void __iomem *regs; + /** Params detected from hardware */ + struct dwc2_hw_params hw_params; + /** Params to actually use */ + struct dwc2_core_params *core_params; + enum usb_otg_state op_state; + + unsigned int queuing_high_bandwidth:1; + unsigned int srp_success:1; + + struct workqueue_struct *wq_otg; + struct work_struct wf_otg; + struct timer_list wkp_timer; + enum dwc2_lx_state lx_state; + + union dwc2_hcd_internal_flags { + u32 d32; + struct { + unsigned port_connect_status_change:1; + unsigned port_connect_status:1; + unsigned port_reset_change:1; + unsigned port_enable_change:1; + unsigned port_suspend_change:1; + unsigned port_over_current_change:1; + unsigned port_l1_change:1; + unsigned reserved:26; + } b; + } flags; + + struct list_head non_periodic_sched_inactive; + struct list_head non_periodic_sched_active; + struct list_head *non_periodic_qh_ptr; + struct list_head periodic_sched_inactive; + struct list_head periodic_sched_ready; + struct list_head periodic_sched_assigned; + struct list_head periodic_sched_queued; + u16 periodic_usecs; + u16 frame_usecs[8]; + u16 frame_number; + u16 periodic_qh_count; + +#ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS +#define FRAME_NUM_ARRAY_SIZE 1000 + u16 last_frame_num; + u16 *frame_num_array; + u16 *last_frame_num_array; + int frame_num_idx; + int dumped_frame_num_array; +#endif + + struct list_head free_hc_list; + int periodic_channels; + int non_periodic_channels; + int available_host_channels; + struct dwc2_host_chan *hc_ptr_array[MAX_EPS_CHANNELS]; + u8 *status_buf; + dma_addr_t status_buf_dma; +#define DWC2_HCD_STATUS_BUF_SIZE 64 + + struct delayed_work start_work; + struct delayed_work reset_work; + spinlock_t lock; + void *priv; + u8 otg_port; + u32 *frame_list; + dma_addr_t frame_list_dma; + + /* DWC OTG HW Release versions */ +#define DWC2_CORE_REV_2_71a 0x4f54271a +#define DWC2_CORE_REV_2_90a 0x4f54290a +#define DWC2_CORE_REV_2_92a 0x4f54292a +#define DWC2_CORE_REV_2_94a 0x4f54294a +#define DWC2_CORE_REV_3_00a 0x4f54300a + +#ifdef DEBUG + u32 frrem_samples; + u64 frrem_accum; + + u32 hfnum_7_samples_a; + u64 hfnum_7_frrem_accum_a; + u32 hfnum_0_samples_a; + u64 hfnum_0_frrem_accum_a; + u32 hfnum_other_samples_a; + u64 hfnum_other_frrem_accum_a; + + u32 hfnum_7_samples_b; + u64 hfnum_7_frrem_accum_b; + u32 hfnum_0_samples_b; + u64 hfnum_0_frrem_accum_b; + u32 hfnum_other_samples_b; + u64 hfnum_other_frrem_accum_b; +#endif +}; + +/* Reasons for halting a host channel */ +enum dwc2_halt_status { + DWC2_HC_XFER_NO_HALT_STATUS, + DWC2_HC_XFER_COMPLETE, + DWC2_HC_XFER_URB_COMPLETE, + DWC2_HC_XFER_ACK, + DWC2_HC_XFER_NAK, + DWC2_HC_XFER_NYET, + DWC2_HC_XFER_STALL, + DWC2_HC_XFER_XACT_ERR, + DWC2_HC_XFER_FRAME_OVERRUN, + DWC2_HC_XFER_BABBLE_ERR, + DWC2_HC_XFER_DATA_TOGGLE_ERR, + DWC2_HC_XFER_AHB_ERR, + DWC2_HC_XFER_PERIODIC_INCOMPLETE, + DWC2_HC_XFER_URB_DEQUEUE, +}; + +/* + * The following functions support initialization of the core driver component + * and the DWC_otg controller + */ +extern void dwc2_core_host_init(struct dwc2_hsotg *hsotg); + +/* + * Host core Functions. + * The following functions support managing the DWC_otg controller in host + * mode. + */ +extern void dwc2_hc_init(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan); +extern void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan, + enum dwc2_halt_status halt_status); +extern void dwc2_hc_cleanup(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan); +extern void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan); +extern void dwc2_hc_start_transfer_ddma(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan); +extern int dwc2_hc_continue_transfer(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan); +extern void dwc2_hc_do_ping(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan); +extern void dwc2_enable_host_interrupts(struct dwc2_hsotg *hsotg); +extern void dwc2_disable_host_interrupts(struct dwc2_hsotg *hsotg); + +extern u32 dwc2_calc_frame_interval(struct dwc2_hsotg *hsotg); +extern bool dwc2_is_controller_alive(struct dwc2_hsotg *hsotg); + +/* + * Common core Functions. + * The following functions support managing the DWC_otg controller in either + * device or host mode. + */ +extern void dwc2_read_packet(struct dwc2_hsotg *hsotg, u8 *dest, u16 bytes); +extern void dwc2_flush_tx_fifo(struct dwc2_hsotg *hsotg, const int num); +extern void dwc2_flush_rx_fifo(struct dwc2_hsotg *hsotg); + +extern int dwc2_core_init(struct dwc2_hsotg *hsotg, bool select_phy, int irq); +extern void dwc2_enable_global_interrupts(struct dwc2_hsotg *hcd); +extern void dwc2_disable_global_interrupts(struct dwc2_hsotg *hcd); + +/* This function should be called on every hardware interrupt. */ +extern irqreturn_t dwc2_handle_common_intr(int irq, void *dev); + +/* OTG Core Parameters */ + +/* + * Specifies the OTG capabilities. The driver will automatically + * detect the value for this parameter if none is specified. + * 0 - HNP and SRP capable (default) + * 1 - SRP Only capable + * 2 - No HNP/SRP capable + */ +extern void dwc2_set_param_otg_cap(struct dwc2_hsotg *hsotg, int val); +#define DWC2_CAP_PARAM_HNP_SRP_CAPABLE 0 +#define DWC2_CAP_PARAM_SRP_ONLY_CAPABLE 1 +#define DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE 2 + +/* + * Specifies whether to use slave or DMA mode for accessing the data + * FIFOs. The driver will automatically detect the value for this + * parameter if none is specified. + * 0 - Slave + * 1 - DMA (default, if available) + */ +extern void dwc2_set_param_dma_enable(struct dwc2_hsotg *hsotg, int val); + +/* + * When DMA mode is enabled specifies whether to use + * address DMA or DMA Descritor mode for accessing the data + * FIFOs in device mode. The driver will automatically detect + * the value for this parameter if none is specified. + * 0 - address DMA + * 1 - DMA Descriptor(default, if available) + */ +extern void dwc2_set_param_dma_desc_enable(struct dwc2_hsotg *hsotg, int val); + +/* + * Specifies the maximum speed of operation in host and device mode. + * The actual speed depends on the speed of the attached device and + * the value of phy_type. The actual speed depends on the speed of the + * attached device. + * 0 - High Speed (default) + * 1 - Full Speed + */ +extern void dwc2_set_param_speed(struct dwc2_hsotg *hsotg, int val); +#define DWC2_SPEED_PARAM_HIGH 0 +#define DWC2_SPEED_PARAM_FULL 1 + +/* + * Specifies whether low power mode is supported when attached + * to a Full Speed or Low Speed device in host mode. + * + * 0 - Don't support low power mode (default) + * 1 - Support low power mode + */ +extern void dwc2_set_param_host_support_fs_ls_low_power( + struct dwc2_hsotg *hsotg, int val); + +/* + * Specifies the PHY clock rate in low power mode when connected to a + * Low Speed device in host mode. This parameter is applicable only if + * HOST_SUPPORT_FS_LS_LOW_POWER is enabled. If PHY_TYPE is set to FS + * then defaults to 6 MHZ otherwise 48 MHZ. + * + * 0 - 48 MHz + * 1 - 6 MHz + */ +extern void dwc2_set_param_host_ls_low_power_phy_clk(struct dwc2_hsotg *hsotg, + int val); +#define DWC2_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ 0 +#define DWC2_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ 1 + +/* + * 0 - Use cC FIFO size parameters + * 1 - Allow dynamic FIFO sizing (default) + */ +extern void dwc2_set_param_enable_dynamic_fifo(struct dwc2_hsotg *hsotg, + int val); + +/* + * Number of 4-byte words in the Rx FIFO in host mode when dynamic + * FIFO sizing is enabled. + * 16 to 32768 (default 1024) + */ +extern void dwc2_set_param_host_rx_fifo_size(struct dwc2_hsotg *hsotg, int val); + +/* + * Number of 4-byte words in the non-periodic Tx FIFO in host mode + * when Dynamic FIFO sizing is enabled in the core. + * 16 to 32768 (default 256) + */ +extern void dwc2_set_param_host_nperio_tx_fifo_size(struct dwc2_hsotg *hsotg, + int val); + +/* + * Number of 4-byte words in the host periodic Tx FIFO when dynamic + * FIFO sizing is enabled. + * 16 to 32768 (default 256) + */ +extern void dwc2_set_param_host_perio_tx_fifo_size(struct dwc2_hsotg *hsotg, + int val); + +/* + * The maximum transfer size supported in bytes. + * 2047 to 65,535 (default 65,535) + */ +extern void dwc2_set_param_max_transfer_size(struct dwc2_hsotg *hsotg, int val); + +/* + * The maximum number of packets in a transfer. + * 15 to 511 (default 511) + */ +extern void dwc2_set_param_max_packet_count(struct dwc2_hsotg *hsotg, int val); + +/* + * The number of host channel registers to use. + * 1 to 16 (default 11) + * Note: The FPGA configuration supports a maximum of 11 host channels. + */ +extern void dwc2_set_param_host_channels(struct dwc2_hsotg *hsotg, int val); + +/* + * Specifies the type of PHY interface to use. By default, the driver + * will automatically detect the phy_type. + * + * 0 - Full Speed PHY + * 1 - UTMI+ (default) + * 2 - ULPI + */ +extern void dwc2_set_param_phy_type(struct dwc2_hsotg *hsotg, int val); +#define DWC2_PHY_TYPE_PARAM_FS 0 +#define DWC2_PHY_TYPE_PARAM_UTMI 1 +#define DWC2_PHY_TYPE_PARAM_ULPI 2 + +/* + * Specifies the UTMI+ Data Width. This parameter is + * applicable for a PHY_TYPE of UTMI+ or ULPI. (For a ULPI + * PHY_TYPE, this parameter indicates the data width between + * the MAC and the ULPI Wrapper.) Also, this parameter is + * applicable only if the OTG_HSPHY_WIDTH cC parameter was set + * to "8 and 16 bits", meaning that the core has been + * configured to work at either data path width. + * + * 8 or 16 bits (default 16) + */ +extern void dwc2_set_param_phy_utmi_width(struct dwc2_hsotg *hsotg, int val); + +/* + * Specifies whether the ULPI operates at double or single + * data rate. This parameter is only applicable if PHY_TYPE is + * ULPI. + * + * 0 - single data rate ULPI interface with 8 bit wide data + * bus (default) + * 1 - double data rate ULPI interface with 4 bit wide data + * bus + */ +extern void dwc2_set_param_phy_ulpi_ddr(struct dwc2_hsotg *hsotg, int val); + +/* + * Specifies whether to use the internal or external supply to + * drive the vbus with a ULPI phy. + */ +extern void dwc2_set_param_phy_ulpi_ext_vbus(struct dwc2_hsotg *hsotg, int val); +#define DWC2_PHY_ULPI_INTERNAL_VBUS 0 +#define DWC2_PHY_ULPI_EXTERNAL_VBUS 1 + +/* + * Specifies whether to use the I2Cinterface for full speed PHY. This + * parameter is only applicable if PHY_TYPE is FS. + * 0 - No (default) + * 1 - Yes + */ +extern void dwc2_set_param_i2c_enable(struct dwc2_hsotg *hsotg, int val); + +extern void dwc2_set_param_ulpi_fs_ls(struct dwc2_hsotg *hsotg, int val); + +extern void dwc2_set_param_ts_dline(struct dwc2_hsotg *hsotg, int val); + +/* + * Specifies whether dedicated transmit FIFOs are + * enabled for non periodic IN endpoints in device mode + * 0 - No + * 1 - Yes + */ +extern void dwc2_set_param_en_multiple_tx_fifo(struct dwc2_hsotg *hsotg, + int val); + +extern void dwc2_set_param_reload_ctl(struct dwc2_hsotg *hsotg, int val); + +extern void dwc2_set_param_ahbcfg(struct dwc2_hsotg *hsotg, int val); + +extern void dwc2_set_param_otg_ver(struct dwc2_hsotg *hsotg, int val); + +/* + * Dump core registers and SPRAM + */ +extern void dwc2_dump_dev_registers(struct dwc2_hsotg *hsotg); +extern void dwc2_dump_host_registers(struct dwc2_hsotg *hsotg); +extern void dwc2_dump_global_registers(struct dwc2_hsotg *hsotg); + +/* + * Return OTG version - either 1.3 or 2.0 + */ +extern u16 dwc2_get_otg_version(struct dwc2_hsotg *hsotg); + +#endif /* __DWC2_CORE_H__ */ diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c new file mode 100644 index 0000000..8205799 --- /dev/null +++ b/drivers/usb/dwc2/core_intr.c @@ -0,0 +1,492 @@ +/* + * core_intr.c - DesignWare HS OTG Controller common interrupt handling + * + * Copyright (C) 2004-2013 Synopsys, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This file contains the common interrupt handlers + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/dma-mapping.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/usb.h> + +#include <linux/usb/hcd.h> +#include <linux/usb/ch11.h> + +#include "core.h" +#include "hcd.h" + +static const char *dwc2_op_state_str(struct dwc2_hsotg *hsotg) +{ + switch (hsotg->op_state) { + case OTG_STATE_A_HOST: + return "a_host"; + case OTG_STATE_A_SUSPEND: + return "a_suspend"; + case OTG_STATE_A_PERIPHERAL: + return "a_peripheral"; + case OTG_STATE_B_PERIPHERAL: + return "b_peripheral"; + case OTG_STATE_B_HOST: + return "b_host"; + default: + return "unknown"; + } +} + +/** + * dwc2_handle_mode_mismatch_intr() - Logs a mode mismatch warning message + * + * @hsotg: Programming view of DWC_otg controller + */ +static void dwc2_handle_mode_mismatch_intr(struct dwc2_hsotg *hsotg) +{ + dev_warn(hsotg->dev, "Mode Mismatch Interrupt: currently in %s mode\n", + dwc2_is_host_mode(hsotg) ? "Host" : "Device"); + + /* Clear interrupt */ + writel(GINTSTS_MODEMIS, hsotg->regs + GINTSTS); +} + +/** + * dwc2_handle_otg_intr() - Handles the OTG Interrupts. It reads the OTG + * Interrupt Register (GOTGINT) to determine what interrupt has occurred. + * + * @hsotg: Programming view of DWC_otg controller + */ +static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg) +{ + u32 gotgint; + u32 gotgctl; + u32 gintmsk; + + gotgint = readl(hsotg->regs + GOTGINT); + gotgctl = readl(hsotg->regs + GOTGCTL); + dev_dbg(hsotg->dev, "++OTG Interrupt gotgint=%0x [%s]\n", gotgint, + dwc2_op_state_str(hsotg)); + + if (gotgint & GOTGINT_SES_END_DET) { + dev_dbg(hsotg->dev, + " ++OTG Interrupt: Session End Detected++ (%s)\n", + dwc2_op_state_str(hsotg)); + gotgctl = readl(hsotg->regs + GOTGCTL); + + if (hsotg->op_state == OTG_STATE_B_HOST) { + hsotg->op_state = OTG_STATE_B_PERIPHERAL; + } else { + /* + * If not B_HOST and Device HNP still set, HNP did + * not succeed! + */ + if (gotgctl & GOTGCTL_DEVHNPEN) { + dev_dbg(hsotg->dev, "Session End Detected\n"); + dev_err(hsotg->dev, + "Device Not Connected/Responding!\n"); + } + + /* + * If Session End Detected the B-Cable has been + * disconnected + */ + /* Reset to a clean state */ + hsotg->lx_state = DWC2_L0; + } + + gotgctl = readl(hsotg->regs + GOTGCTL); + gotgctl &= ~GOTGCTL_DEVHNPEN; + writel(gotgctl, hsotg->regs + GOTGCTL); + } + + if (gotgint & GOTGINT_SES_REQ_SUC_STS_CHNG) { + dev_dbg(hsotg->dev, + " ++OTG Interrupt: Session Request Success Status Change++\n"); + gotgctl = readl(hsotg->regs + GOTGCTL); + if (gotgctl & GOTGCTL_SESREQSCS) { + if (hsotg->core_params->phy_type == + DWC2_PHY_TYPE_PARAM_FS + && hsotg->core_params->i2c_enable > 0) { + hsotg->srp_success = 1; + } else { + /* Clear Session Request */ + gotgctl = readl(hsotg->regs + GOTGCTL); + gotgctl &= ~GOTGCTL_SESREQ; + writel(gotgctl, hsotg->regs + GOTGCTL); + } + } + } + + if (gotgint & GOTGINT_HST_NEG_SUC_STS_CHNG) { + /* + * Print statements during the HNP interrupt handling + * can cause it to fail + */ + gotgctl = readl(hsotg->regs + GOTGCTL); + /* + * WA for 3.00a- HW is not setting cur_mode, even sometimes + * this does not help + */ + if (hsotg->hw_params.snpsid >= DWC2_CORE_REV_3_00a) + udelay(100); + if (gotgctl & GOTGCTL_HSTNEGSCS) { + if (dwc2_is_host_mode(hsotg)) { + hsotg->op_state = OTG_STATE_B_HOST; + /* + * Need to disable SOF interrupt immediately. + * When switching from device to host, the PCD + * interrupt handler won't handle the interrupt + * if host mode is already set. The HCD + * interrupt handler won't get called if the + * HCD state is HALT. This means that the + * interrupt does not get handled and Linux + * complains loudly. + */ + gintmsk = readl(hsotg->regs + GINTMSK); + gintmsk &= ~GINTSTS_SOF; + writel(gintmsk, hsotg->regs + GINTMSK); + + /* + * Call callback function with spin lock + * released + */ + spin_unlock(&hsotg->lock); + + /* Initialize the Core for Host mode */ + dwc2_hcd_start(hsotg); + spin_lock(&hsotg->lock); + hsotg->op_state = OTG_STATE_B_HOST; + } + } else { + gotgctl = readl(hsotg->regs + GOTGCTL); + gotgctl &= ~(GOTGCTL_HNPREQ | GOTGCTL_DEVHNPEN); + writel(gotgctl, hsotg->regs + GOTGCTL); + dev_dbg(hsotg->dev, "HNP Failed\n"); + dev_err(hsotg->dev, + "Device Not Connected/Responding\n"); + } + } + + if (gotgint & GOTGINT_HST_NEG_DET) { + /* + * The disconnect interrupt is set at the same time as + * Host Negotiation Detected. During the mode switch all + * interrupts are cleared so the disconnect interrupt + * handler will not get executed. + */ + dev_dbg(hsotg->dev, + " ++OTG Interrupt: Host Negotiation Detected++ (%s)\n", + (dwc2_is_host_mode(hsotg) ? "Host" : "Device")); + if (dwc2_is_device_mode(hsotg)) { + dev_dbg(hsotg->dev, "a_suspend->a_peripheral (%d)\n", + hsotg->op_state); + spin_unlock(&hsotg->lock); + dwc2_hcd_disconnect(hsotg); + spin_lock(&hsotg->lock); + hsotg->op_state = OTG_STATE_A_PERIPHERAL; + } else { + /* Need to disable SOF interrupt immediately */ + gintmsk = readl(hsotg->regs + GINTMSK); + gintmsk &= ~GINTSTS_SOF; + writel(gintmsk, hsotg->regs + GINTMSK); + spin_unlock(&hsotg->lock); + dwc2_hcd_start(hsotg); + spin_lock(&hsotg->lock); + hsotg->op_state = OTG_STATE_A_HOST; + } + } + + if (gotgint & GOTGINT_A_DEV_TOUT_CHG) + dev_dbg(hsotg->dev, + " ++OTG Interrupt: A-Device Timeout Change++\n"); + if (gotgint & GOTGINT_DBNCE_DONE) + dev_dbg(hsotg->dev, " ++OTG Interrupt: Debounce Done++\n"); + + /* Clear GOTGINT */ + writel(gotgint, hsotg->regs + GOTGINT); +} + +/** + * dwc2_handle_conn_id_status_change_intr() - Handles the Connector ID Status + * Change Interrupt + * + * @hsotg: Programming view of DWC_otg controller + * + * Reads the OTG Interrupt Register (GOTCTL) to determine whether this is a + * Device to Host Mode transition or a Host to Device Mode transition. This only + * occurs when the cable is connected/removed from the PHY connector. + */ +static void dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg *hsotg) +{ + u32 gintmsk = readl(hsotg->regs + GINTMSK); + + /* Need to disable SOF interrupt immediately */ + gintmsk &= ~GINTSTS_SOF; + writel(gintmsk, hsotg->regs + GINTMSK); + + dev_dbg(hsotg->dev, " ++Connector ID Status Change Interrupt++ (%s)\n", + dwc2_is_host_mode(hsotg) ? "Host" : "Device"); + + /* + * Need to schedule a work, as there are possible DELAY function calls. + * Release lock before scheduling workq as it holds spinlock during + * scheduling. + */ + spin_unlock(&hsotg->lock); + queue_work(hsotg->wq_otg, &hsotg->wf_otg); + spin_lock(&hsotg->lock); + + /* Clear interrupt */ + writel(GINTSTS_CONIDSTSCHNG, hsotg->regs + GINTSTS); +} + +/** + * dwc2_handle_session_req_intr() - This interrupt indicates that a device is + * initiating the Session Request Protocol to request the host to turn on bus + * power so a new session can begin + * + * @hsotg: Programming view of DWC_otg controller + * + * This handler responds by turning on bus power. If the DWC_otg controller is + * in low power mode, this handler brings the controller out of low power mode + * before turning on bus power. + */ +static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg) +{ + dev_dbg(hsotg->dev, "++Session Request Interrupt++\n"); + + /* Clear interrupt */ + writel(GINTSTS_SESSREQINT, hsotg->regs + GINTSTS); +} + +/* + * This interrupt indicates that the DWC_otg controller has detected a + * resume or remote wakeup sequence. If the DWC_otg controller is in + * low power mode, the handler must brings the controller out of low + * power mode. The controller automatically begins resume signaling. + * The handler schedules a time to stop resume signaling. + */ +static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) +{ + dev_dbg(hsotg->dev, "++Resume or Remote Wakeup Detected Interrupt++\n"); + dev_dbg(hsotg->dev, "%s lxstate = %d\n", __func__, hsotg->lx_state); + + if (dwc2_is_device_mode(hsotg)) { + dev_dbg(hsotg->dev, "DSTS=0x%0x\n", readl(hsotg->regs + DSTS)); + if (hsotg->lx_state == DWC2_L2) { + u32 dctl = readl(hsotg->regs + DCTL); + + /* Clear Remote Wakeup Signaling */ + dctl &= ~DCTL_RMTWKUPSIG; + writel(dctl, hsotg->regs + DCTL); + } + /* Change to L0 state */ + hsotg->lx_state = DWC2_L0; + } else { + if (hsotg->lx_state != DWC2_L1) { + u32 pcgcctl = readl(hsotg->regs + PCGCTL); + + /* Restart the Phy Clock */ + pcgcctl &= ~PCGCTL_STOPPCLK; + writel(pcgcctl, hsotg->regs + PCGCTL); + mod_timer(&hsotg->wkp_timer, + jiffies + msecs_to_jiffies(71)); + } else { + /* Change to L0 state */ + hsotg->lx_state = DWC2_L0; + } + } + + /* Clear interrupt */ + writel(GINTSTS_WKUPINT, hsotg->regs + GINTSTS); +} + +/* + * This interrupt indicates that a device has been disconnected from the + * root port + */ +static void dwc2_handle_disconnect_intr(struct dwc2_hsotg *hsotg) +{ + dev_dbg(hsotg->dev, "++Disconnect Detected Interrupt++ (%s) %s\n", + dwc2_is_host_mode(hsotg) ? "Host" : "Device", + dwc2_op_state_str(hsotg)); + + /* Change to L3 (OFF) state */ + hsotg->lx_state = DWC2_L3; + + writel(GINTSTS_DISCONNINT, hsotg->regs + GINTSTS); +} + +/* + * This interrupt indicates that SUSPEND state has been detected on the USB. + * + * For HNP the USB Suspend interrupt signals the change from "a_peripheral" + * to "a_host". + * + * When power management is enabled the core will be put in low power mode. + */ +static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) +{ + u32 dsts; + + dev_dbg(hsotg->dev, "USB SUSPEND\n"); + + if (dwc2_is_device_mode(hsotg)) { + /* + * Check the Device status register to determine if the Suspend + * state is active + */ + dsts = readl(hsotg->regs + DSTS); + dev_dbg(hsotg->dev, "DSTS=0x%0x\n", dsts); + dev_dbg(hsotg->dev, + "DSTS.Suspend Status=%d HWCFG4.Power Optimize=%d\n", + !!(dsts & DSTS_SUSPSTS), + hsotg->hw_params.power_optimized); + } else { + if (hsotg->op_state == OTG_STATE_A_PERIPHERAL) { + dev_dbg(hsotg->dev, "a_peripheral->a_host\n"); + + /* Clear the a_peripheral flag, back to a_host */ + spin_unlock(&hsotg->lock); + dwc2_hcd_start(hsotg); + spin_lock(&hsotg->lock); + hsotg->op_state = OTG_STATE_A_HOST; + } + } + + /* Change to L2 (suspend) state */ + hsotg->lx_state = DWC2_L2; + + /* Clear interrupt */ + writel(GINTSTS_USBSUSP, hsotg->regs + GINTSTS); +} + +#define GINTMSK_COMMON (GINTSTS_WKUPINT | GINTSTS_SESSREQINT | \ + GINTSTS_CONIDSTSCHNG | GINTSTS_OTGINT | \ + GINTSTS_MODEMIS | GINTSTS_DISCONNINT | \ + GINTSTS_USBSUSP | GINTSTS_PRTINT) + +/* + * This function returns the Core Interrupt register + */ +static u32 dwc2_read_common_intr(struct dwc2_hsotg *hsotg) +{ + u32 gintsts; + u32 gintmsk; + u32 gahbcfg; + u32 gintmsk_common = GINTMSK_COMMON; + + gintsts = readl(hsotg->regs + GINTSTS); + gintmsk = readl(hsotg->regs + GINTMSK); + gahbcfg = readl(hsotg->regs + GAHBCFG); + + /* If any common interrupts set */ + if (gintsts & gintmsk_common) + dev_dbg(hsotg->dev, "gintsts=%08x gintmsk=%08x\n", + gintsts, gintmsk); + + if (gahbcfg & GAHBCFG_GLBL_INTR_EN) + return gintsts & gintmsk & gintmsk_common; + else + return 0; +} + +/* + * Common interrupt handler + * + * The common interrupts are those that occur in both Host and Device mode. + * This handler handles the following interrupts: + * - Mode Mismatch Interrupt + * - OTG Interrupt + * - Connector ID Status Change Interrupt + * - Disconnect Interrupt + * - Session Request Interrupt + * - Resume / Remote Wakeup Detected Interrupt + * - Suspend Interrupt + */ +irqreturn_t dwc2_handle_common_intr(int irq, void *dev) +{ + struct dwc2_hsotg *hsotg = dev; + u32 gintsts; + irqreturn_t retval = IRQ_NONE; + + if (!dwc2_is_controller_alive(hsotg)) { + dev_warn(hsotg->dev, "Controller is dead\n"); + goto out; + } + + spin_lock(&hsotg->lock); + + gintsts = dwc2_read_common_intr(hsotg); + if (gintsts & ~GINTSTS_PRTINT) + retval = IRQ_HANDLED; + + if (gintsts & GINTSTS_MODEMIS) + dwc2_handle_mode_mismatch_intr(hsotg); + if (gintsts & GINTSTS_OTGINT) + dwc2_handle_otg_intr(hsotg); + if (gintsts & GINTSTS_CONIDSTSCHNG) + dwc2_handle_conn_id_status_change_intr(hsotg); + if (gintsts & GINTSTS_DISCONNINT) + dwc2_handle_disconnect_intr(hsotg); + if (gintsts & GINTSTS_SESSREQINT) + dwc2_handle_session_req_intr(hsotg); + if (gintsts & GINTSTS_WKUPINT) + dwc2_handle_wakeup_detected_intr(hsotg); + if (gintsts & GINTSTS_USBSUSP) + dwc2_handle_usb_suspend_intr(hsotg); + + if (gintsts & GINTSTS_PRTINT) { + /* + * The port interrupt occurs while in device mode with HPRT0 + * Port Enable/Disable + */ + if (dwc2_is_device_mode(hsotg)) { + dev_dbg(hsotg->dev, + " --Port interrupt received in Device mode--\n"); + gintsts = GINTSTS_PRTINT; + writel(gintsts, hsotg->regs + GINTSTS); + retval = 1; + } + } + + spin_unlock(&hsotg->lock); +out: + return retval; +} +EXPORT_SYMBOL_GPL(dwc2_handle_common_intr); diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c new file mode 100644 index 0000000..4d918ed --- /dev/null +++ b/drivers/usb/dwc2/hcd.c @@ -0,0 +1,2981 @@ +/* + * hcd.c - DesignWare HS OTG Controller host-mode routines + * + * Copyright (C) 2004-2013 Synopsys, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This file contains the core HCD code, and implements the Linux hc_driver + * API + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/dma-mapping.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/usb.h> + +#include <linux/usb/hcd.h> +#include <linux/usb/ch11.h> + +#include "core.h" +#include "hcd.h" + +/** + * dwc2_dump_channel_info() - Prints the state of a host channel + * + * @hsotg: Programming view of DWC_otg controller + * @chan: Pointer to the channel to dump + * + * Must be called with interrupt disabled and spinlock held + * + * NOTE: This function will be removed once the peripheral controller code + * is integrated and the driver is stable + */ +static void dwc2_dump_channel_info(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan) +{ +#ifdef VERBOSE_DEBUG + int num_channels = hsotg->core_params->host_channels; + struct dwc2_qh *qh; + u32 hcchar; + u32 hcsplt; + u32 hctsiz; + u32 hc_dma; + int i; + + if (chan == NULL) + return; + + hcchar = readl(hsotg->regs + HCCHAR(chan->hc_num)); + hcsplt = readl(hsotg->regs + HCSPLT(chan->hc_num)); + hctsiz = readl(hsotg->regs + HCTSIZ(chan->hc_num)); + hc_dma = readl(hsotg->regs + HCDMA(chan->hc_num)); + + dev_dbg(hsotg->dev, " Assigned to channel %p:\n", chan); + dev_dbg(hsotg->dev, " hcchar 0x%08x, hcsplt 0x%08x\n", + hcchar, hcsplt); + dev_dbg(hsotg->dev, " hctsiz 0x%08x, hc_dma 0x%08x\n", + hctsiz, hc_dma); + dev_dbg(hsotg->dev, " dev_addr: %d, ep_num: %d, ep_is_in: %d\n", + chan->dev_addr, chan->ep_num, chan->ep_is_in); + dev_dbg(hsotg->dev, " ep_type: %d\n", chan->ep_type); + dev_dbg(hsotg->dev, " max_packet: %d\n", chan->max_packet); + dev_dbg(hsotg->dev, " data_pid_start: %d\n", chan->data_pid_start); + dev_dbg(hsotg->dev, " xfer_started: %d\n", chan->xfer_started); + dev_dbg(hsotg->dev, " halt_status: %d\n", chan->halt_status); + dev_dbg(hsotg->dev, " xfer_buf: %p\n", chan->xfer_buf); + dev_dbg(hsotg->dev, " xfer_dma: %08lx\n", + (unsigned long)chan->xfer_dma); + dev_dbg(hsotg->dev, " xfer_len: %d\n", chan->xfer_len); + dev_dbg(hsotg->dev, " qh: %p\n", chan->qh); + dev_dbg(hsotg->dev, " NP inactive sched:\n"); + list_for_each_entry(qh, &hsotg->non_periodic_sched_inactive, + qh_list_entry) + dev_dbg(hsotg->dev, " %p\n", qh); + dev_dbg(hsotg->dev, " NP active sched:\n"); + list_for_each_entry(qh, &hsotg->non_periodic_sched_active, + qh_list_entry) + dev_dbg(hsotg->dev, " %p\n", qh); + dev_dbg(hsotg->dev, " Channels:\n"); + for (i = 0; i < num_channels; i++) { + struct dwc2_host_chan *chan = hsotg->hc_ptr_array[i]; + + dev_dbg(hsotg->dev, " %2d: %p\n", i, chan); + } +#endif /* VERBOSE_DEBUG */ +} + +/* + * Processes all the URBs in a single list of QHs. Completes them with + * -ETIMEDOUT and frees the QTD. + * + * Must be called with interrupt disabled and spinlock held + */ +static void dwc2_kill_urbs_in_qh_list(struct dwc2_hsotg *hsotg, + struct list_head *qh_list) +{ + struct dwc2_qh *qh, *qh_tmp; + struct dwc2_qtd *qtd, *qtd_tmp; + + list_for_each_entry_safe(qh, qh_tmp, qh_list, qh_list_entry) { + list_for_each_entry_safe(qtd, qtd_tmp, &qh->qtd_list, + qtd_list_entry) { + dwc2_host_complete(hsotg, qtd, -ETIMEDOUT); + dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh); + } + } +} + +static void dwc2_qh_list_free(struct dwc2_hsotg *hsotg, + struct list_head *qh_list) +{ + struct dwc2_qtd *qtd, *qtd_tmp; + struct dwc2_qh *qh, *qh_tmp; + unsigned long flags; + + if (!qh_list->next) + /* The list hasn't been initialized yet */ + return; + + spin_lock_irqsave(&hsotg->lock, flags); + + /* Ensure there are no QTDs or URBs left */ + dwc2_kill_urbs_in_qh_list(hsotg, qh_list); + + list_for_each_entry_safe(qh, qh_tmp, qh_list, qh_list_entry) { + dwc2_hcd_qh_unlink(hsotg, qh); + + /* Free each QTD in the QH's QTD list */ + list_for_each_entry_safe(qtd, qtd_tmp, &qh->qtd_list, + qtd_list_entry) + dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh); + + spin_unlock_irqrestore(&hsotg->lock, flags); + dwc2_hcd_qh_free(hsotg, qh); + spin_lock_irqsave(&hsotg->lock, flags); + } + + spin_unlock_irqrestore(&hsotg->lock, flags); +} + +/* + * Responds with an error status of -ETIMEDOUT to all URBs in the non-periodic + * and periodic schedules. The QTD associated with each URB is removed from + * the schedule and freed. This function may be called when a disconnect is + * detected or when the HCD is being stopped. + * + * Must be called with interrupt disabled and spinlock held + */ +static void dwc2_kill_all_urbs(struct dwc2_hsotg *hsotg) +{ + dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->non_periodic_sched_inactive); + dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->non_periodic_sched_active); + dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->periodic_sched_inactive); + dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->periodic_sched_ready); + dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->periodic_sched_assigned); + dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->periodic_sched_queued); +} + +/** + * dwc2_hcd_start() - Starts the HCD when switching to Host mode + * + * @hsotg: Pointer to struct dwc2_hsotg + */ +void dwc2_hcd_start(struct dwc2_hsotg *hsotg) +{ + u32 hprt0; + + if (hsotg->op_state == OTG_STATE_B_HOST) { + /* + * Reset the port. During a HNP mode switch the reset + * needs to occur within 1ms and have a duration of at + * least 50ms. + */ + hprt0 = dwc2_read_hprt0(hsotg); + hprt0 |= HPRT0_RST; + writel(hprt0, hsotg->regs + HPRT0); + } + + queue_delayed_work(hsotg->wq_otg, &hsotg->start_work, + msecs_to_jiffies(50)); +} + +/* Must be called with interrupt disabled and spinlock held */ +static void dwc2_hcd_cleanup_channels(struct dwc2_hsotg *hsotg) +{ + int num_channels = hsotg->core_params->host_channels; + struct dwc2_host_chan *channel; + u32 hcchar; + int i; + + if (hsotg->core_params->dma_enable <= 0) { + /* Flush out any channel requests in slave mode */ + for (i = 0; i < num_channels; i++) { + channel = hsotg->hc_ptr_array[i]; + if (!list_empty(&channel->hc_list_entry)) + continue; + hcchar = readl(hsotg->regs + HCCHAR(i)); + if (hcchar & HCCHAR_CHENA) { + hcchar &= ~(HCCHAR_CHENA | HCCHAR_EPDIR); + hcchar |= HCCHAR_CHDIS; + writel(hcchar, hsotg->regs + HCCHAR(i)); + } + } + } + + for (i = 0; i < num_channels; i++) { + channel = hsotg->hc_ptr_array[i]; + if (!list_empty(&channel->hc_list_entry)) + continue; + hcchar = readl(hsotg->regs + HCCHAR(i)); + if (hcchar & HCCHAR_CHENA) { + /* Halt the channel */ + hcchar |= HCCHAR_CHDIS; + writel(hcchar, hsotg->regs + HCCHAR(i)); + } + + dwc2_hc_cleanup(hsotg, channel); + list_add_tail(&channel->hc_list_entry, &hsotg->free_hc_list); + /* + * Added for Descriptor DMA to prevent channel double cleanup in + * release_channel_ddma(), which is called from ep_disable when + * device disconnects + */ + channel->qh = NULL; + } +} + +/** + * dwc2_hcd_disconnect() - Handles disconnect of the HCD + * + * @hsotg: Pointer to struct dwc2_hsotg + * + * Must be called with interrupt disabled and spinlock held + */ +void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg) +{ + u32 intr; + + /* Set status flags for the hub driver */ + hsotg->flags.b.port_connect_status_change = 1; + hsotg->flags.b.port_connect_status = 0; + + /* + * Shutdown any transfers in process by clearing the Tx FIFO Empty + * interrupt mask and status bits and disabling subsequent host + * channel interrupts. + */ + intr = readl(hsotg->regs + GINTMSK); + intr &= ~(GINTSTS_NPTXFEMP | GINTSTS_PTXFEMP | GINTSTS_HCHINT); + writel(intr, hsotg->regs + GINTMSK); + intr = GINTSTS_NPTXFEMP | GINTSTS_PTXFEMP | GINTSTS_HCHINT; + writel(intr, hsotg->regs + GINTSTS); + + /* + * Turn off the vbus power only if the core has transitioned to device + * mode. If still in host mode, need to keep power on to detect a + * reconnection. + */ + if (dwc2_is_device_mode(hsotg)) { + if (hsotg->op_state != OTG_STATE_A_SUSPEND) { + dev_dbg(hsotg->dev, "Disconnect: PortPower off\n"); + writel(0, hsotg->regs + HPRT0); + } + + dwc2_disable_host_interrupts(hsotg); + } + + /* Respond with an error status to all URBs in the schedule */ + dwc2_kill_all_urbs(hsotg); + + if (dwc2_is_host_mode(hsotg)) + /* Clean up any host channels that were in use */ + dwc2_hcd_cleanup_channels(hsotg); + + dwc2_host_disconnect(hsotg); +} + +/** + * dwc2_hcd_rem_wakeup() - Handles Remote Wakeup + * + * @hsotg: Pointer to struct dwc2_hsotg + */ +static void dwc2_hcd_rem_wakeup(struct dwc2_hsotg *hsotg) +{ + if (hsotg->lx_state == DWC2_L2) + hsotg->flags.b.port_suspend_change = 1; + else + hsotg->flags.b.port_l1_change = 1; +} + +/** + * dwc2_hcd_stop() - Halts the DWC_otg host mode operations in a clean manner + * + * @hsotg: Pointer to struct dwc2_hsotg + * + * Must be called with interrupt disabled and spinlock held + */ +void dwc2_hcd_stop(struct dwc2_hsotg *hsotg) +{ + dev_dbg(hsotg->dev, "DWC OTG HCD STOP\n"); + + /* + * The root hub should be disconnected before this function is called. + * The disconnect will clear the QTD lists (via ..._hcd_urb_dequeue) + * and the QH lists (via ..._hcd_endpoint_disable). + */ + + /* Turn off all host-specific interrupts */ + dwc2_disable_host_interrupts(hsotg); + + /* Turn off the vbus power */ + dev_dbg(hsotg->dev, "PortPower off\n"); + writel(0, hsotg->regs + HPRT0); +} + +static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg, + struct dwc2_hcd_urb *urb, void **ep_handle, + gfp_t mem_flags) +{ + struct dwc2_qtd *qtd; + unsigned long flags; + u32 intr_mask; + int retval; + int dev_speed; + + if (!hsotg->flags.b.port_connect_status) { + /* No longer connected */ + dev_err(hsotg->dev, "Not connected\n"); + return -ENODEV; + } + + dev_speed = dwc2_host_get_speed(hsotg, urb->priv); + + /* Some configurations cannot support LS traffic on a FS root port */ + if ((dev_speed == USB_SPEED_LOW) && + (hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED) && + (hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI)) { + u32 hprt0 = readl(hsotg->regs + HPRT0); + u32 prtspd = (hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT; + + if (prtspd == HPRT0_SPD_FULL_SPEED) + return -ENODEV; + } + + qtd = kzalloc(sizeof(*qtd), mem_flags); + if (!qtd) + return -ENOMEM; + + dwc2_hcd_qtd_init(qtd, urb); + retval = dwc2_hcd_qtd_add(hsotg, qtd, (struct dwc2_qh **)ep_handle, + mem_flags); + if (retval) { + dev_err(hsotg->dev, + "DWC OTG HCD URB Enqueue failed adding QTD. Error status %d\n", + retval); + kfree(qtd); + return retval; + } + + intr_mask = readl(hsotg->regs + GINTMSK); + if (!(intr_mask & GINTSTS_SOF)) { + enum dwc2_transaction_type tr_type; + + if (qtd->qh->ep_type == USB_ENDPOINT_XFER_BULK && + !(qtd->urb->flags & URB_GIVEBACK_ASAP)) + /* + * Do not schedule SG transactions until qtd has + * URB_GIVEBACK_ASAP set + */ + return 0; + + spin_lock_irqsave(&hsotg->lock, flags); + tr_type = dwc2_hcd_select_transactions(hsotg); + if (tr_type != DWC2_TRANSACTION_NONE) + dwc2_hcd_queue_transactions(hsotg, tr_type); + spin_unlock_irqrestore(&hsotg->lock, flags); + } + + return 0; +} + +/* Must be called with interrupt disabled and spinlock held */ +static int dwc2_hcd_urb_dequeue(struct dwc2_hsotg *hsotg, + struct dwc2_hcd_urb *urb) +{ + struct dwc2_qh *qh; + struct dwc2_qtd *urb_qtd; + + urb_qtd = urb->qtd; + if (!urb_qtd) { + dev_dbg(hsotg->dev, "## Urb QTD is NULL ##\n"); + return -EINVAL; + } + + qh = urb_qtd->qh; + if (!qh) { + dev_dbg(hsotg->dev, "## Urb QTD QH is NULL ##\n"); + return -EINVAL; + } + + urb->priv = NULL; + + if (urb_qtd->in_process && qh->channel) { + dwc2_dump_channel_info(hsotg, qh->channel); + + /* The QTD is in process (it has been assigned to a channel) */ + if (hsotg->flags.b.port_connect_status) + /* + * If still connected (i.e. in host mode), halt the + * channel so it can be used for other transfers. If + * no longer connected, the host registers can't be + * written to halt the channel since the core is in + * device mode. + */ + dwc2_hc_halt(hsotg, qh->channel, + DWC2_HC_XFER_URB_DEQUEUE); + } + + /* + * Free the QTD and clean up the associated QH. Leave the QH in the + * schedule if it has any remaining QTDs. + */ + if (hsotg->core_params->dma_desc_enable <= 0) { + u8 in_process = urb_qtd->in_process; + + dwc2_hcd_qtd_unlink_and_free(hsotg, urb_qtd, qh); + if (in_process) { + dwc2_hcd_qh_deactivate(hsotg, qh, 0); + qh->channel = NULL; + } else if (list_empty(&qh->qtd_list)) { + dwc2_hcd_qh_unlink(hsotg, qh); + } + } else { + dwc2_hcd_qtd_unlink_and_free(hsotg, urb_qtd, qh); + } + + return 0; +} + +/* Must NOT be called with interrupt disabled or spinlock held */ +static int dwc2_hcd_endpoint_disable(struct dwc2_hsotg *hsotg, + struct usb_host_endpoint *ep, int retry) +{ + struct dwc2_qtd *qtd, *qtd_tmp; + struct dwc2_qh *qh; + unsigned long flags; + int rc; + + spin_lock_irqsave(&hsotg->lock, flags); + + qh = ep->hcpriv; + if (!qh) { + rc = -EINVAL; + goto err; + } + + while (!list_empty(&qh->qtd_list) && retry--) { + if (retry == 0) { + dev_err(hsotg->dev, + "## timeout in dwc2_hcd_endpoint_disable() ##\n"); + rc = -EBUSY; + goto err; + } + + spin_unlock_irqrestore(&hsotg->lock, flags); + usleep_range(20000, 40000); + spin_lock_irqsave(&hsotg->lock, flags); + qh = ep->hcpriv; + if (!qh) { + rc = -EINVAL; + goto err; + } + } + + dwc2_hcd_qh_unlink(hsotg, qh); + + /* Free each QTD in the QH's QTD list */ + list_for_each_entry_safe(qtd, qtd_tmp, &qh->qtd_list, qtd_list_entry) + dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh); + + ep->hcpriv = NULL; + spin_unlock_irqrestore(&hsotg->lock, flags); + dwc2_hcd_qh_free(hsotg, qh); + + return 0; + +err: + ep->hcpriv = NULL; + spin_unlock_irqrestore(&hsotg->lock, flags); + + return rc; +} + +/* Must be called with interrupt disabled and spinlock held */ +static int dwc2_hcd_endpoint_reset(struct dwc2_hsotg *hsotg, + struct usb_host_endpoint *ep) +{ + struct dwc2_qh *qh = ep->hcpriv; + + if (!qh) + return -EINVAL; + + qh->data_toggle = DWC2_HC_PID_DATA0; + + return 0; +} + +/* + * Initializes dynamic portions of the DWC_otg HCD state + * + * Must be called with interrupt disabled and spinlock held + */ +static void dwc2_hcd_reinit(struct dwc2_hsotg *hsotg) +{ + struct dwc2_host_chan *chan, *chan_tmp; + int num_channels; + int i; + + hsotg->flags.d32 = 0; + hsotg->non_periodic_qh_ptr = &hsotg->non_periodic_sched_active; + + if (hsotg->core_params->uframe_sched > 0) { + hsotg->available_host_channels = + hsotg->core_params->host_channels; + } else { + hsotg->non_periodic_channels = 0; + hsotg->periodic_channels = 0; + } + + /* + * Put all channels in the free channel list and clean up channel + * states + */ + list_for_each_entry_safe(chan, chan_tmp, &hsotg->free_hc_list, + hc_list_entry) + list_del_init(&chan->hc_list_entry); + + num_channels = hsotg->core_params->host_channels; + for (i = 0; i < num_channels; i++) { + chan = hsotg->hc_ptr_array[i]; + list_add_tail(&chan->hc_list_entry, &hsotg->free_hc_list); + dwc2_hc_cleanup(hsotg, chan); + } + + /* Initialize the DWC core for host mode operation */ + dwc2_core_host_init(hsotg); +} + +static void dwc2_hc_init_split(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, + struct dwc2_qtd *qtd, struct dwc2_hcd_urb *urb) +{ + int hub_addr, hub_port; + + chan->do_split = 1; + chan->xact_pos = qtd->isoc_split_pos; + chan->complete_split = qtd->complete_split; + dwc2_host_hub_info(hsotg, urb->priv, &hub_addr, &hub_port); + chan->hub_addr = (u8)hub_addr; + chan->hub_port = (u8)hub_port; +} + +static void *dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, + struct dwc2_qtd *qtd, void *bufptr) +{ + struct dwc2_hcd_urb *urb = qtd->urb; + struct dwc2_hcd_iso_packet_desc *frame_desc; + + switch (dwc2_hcd_get_pipe_type(&urb->pipe_info)) { + case USB_ENDPOINT_XFER_CONTROL: + chan->ep_type = USB_ENDPOINT_XFER_CONTROL; + + switch (qtd->control_phase) { + case DWC2_CONTROL_SETUP: + dev_vdbg(hsotg->dev, " Control setup transaction\n"); + chan->do_ping = 0; + chan->ep_is_in = 0; + chan->data_pid_start = DWC2_HC_PID_SETUP; + if (hsotg->core_params->dma_enable > 0) + chan->xfer_dma = urb->setup_dma; + else + chan->xfer_buf = urb->setup_packet; + chan->xfer_len = 8; + bufptr = NULL; + break; + + case DWC2_CONTROL_DATA: + dev_vdbg(hsotg->dev, " Control data transaction\n"); + chan->data_pid_start = qtd->data_toggle; + break; + + case DWC2_CONTROL_STATUS: + /* + * Direction is opposite of data direction or IN if no + * data + */ + dev_vdbg(hsotg->dev, " Control status transaction\n"); + if (urb->length == 0) + chan->ep_is_in = 1; + else + chan->ep_is_in = + dwc2_hcd_is_pipe_out(&urb->pipe_info); + if (chan->ep_is_in) + chan->do_ping = 0; + chan->data_pid_start = DWC2_HC_PID_DATA1; + chan->xfer_len = 0; + if (hsotg->core_params->dma_enable > 0) + chan->xfer_dma = hsotg->status_buf_dma; + else + chan->xfer_buf = hsotg->status_buf; + bufptr = NULL; + break; + } + break; + + case USB_ENDPOINT_XFER_BULK: + chan->ep_type = USB_ENDPOINT_XFER_BULK; + break; + + case USB_ENDPOINT_XFER_INT: + chan->ep_type = USB_ENDPOINT_XFER_INT; + break; + + case USB_ENDPOINT_XFER_ISOC: + chan->ep_type = USB_ENDPOINT_XFER_ISOC; + if (hsotg->core_params->dma_desc_enable > 0) + break; + + frame_desc = &urb->iso_descs[qtd->isoc_frame_index]; + frame_desc->status = 0; + + if (hsotg->core_params->dma_enable > 0) { + chan->xfer_dma = urb->dma; + chan->xfer_dma += frame_desc->offset + + qtd->isoc_split_offset; + } else { + chan->xfer_buf = urb->buf; + chan->xfer_buf += frame_desc->offset + + qtd->isoc_split_offset; + } + + chan->xfer_len = frame_desc->length - qtd->isoc_split_offset; + + /* For non-dword aligned buffers */ + if (hsotg->core_params->dma_enable > 0 && + (chan->xfer_dma & 0x3)) + bufptr = (u8 *)urb->buf + frame_desc->offset + + qtd->isoc_split_offset; + else + bufptr = NULL; + + if (chan->xact_pos == DWC2_HCSPLT_XACTPOS_ALL) { + if (chan->xfer_len <= 188) + chan->xact_pos = DWC2_HCSPLT_XACTPOS_ALL; + else + chan->xact_pos = DWC2_HCSPLT_XACTPOS_BEGIN; + } + break; + } + + return bufptr; +} + +static int dwc2_hc_setup_align_buf(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, + struct dwc2_host_chan *chan, void *bufptr) +{ + u32 buf_size; + + if (chan->ep_type != USB_ENDPOINT_XFER_ISOC) + buf_size = hsotg->core_params->max_transfer_size; + else + buf_size = 4096; + + if (!qh->dw_align_buf) { + qh->dw_align_buf = dma_alloc_coherent(hsotg->dev, buf_size, + &qh->dw_align_buf_dma, + GFP_ATOMIC); + if (!qh->dw_align_buf) + return -ENOMEM; + } + + if (!chan->ep_is_in && chan->xfer_len) { + dma_sync_single_for_cpu(hsotg->dev, chan->xfer_dma, buf_size, + DMA_TO_DEVICE); + memcpy(qh->dw_align_buf, bufptr, chan->xfer_len); + dma_sync_single_for_device(hsotg->dev, chan->xfer_dma, buf_size, + DMA_TO_DEVICE); + } + + chan->align_buf = qh->dw_align_buf_dma; + return 0; +} + +/** + * dwc2_assign_and_init_hc() - Assigns transactions from a QTD to a free host + * channel and initializes the host channel to perform the transactions. The + * host channel is removed from the free list. + * + * @hsotg: The HCD state structure + * @qh: Transactions from the first QTD for this QH are selected and assigned + * to a free host channel + */ +static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) +{ + struct dwc2_host_chan *chan; + struct dwc2_hcd_urb *urb; + struct dwc2_qtd *qtd; + void *bufptr = NULL; + + if (dbg_qh(qh)) + dev_vdbg(hsotg->dev, "%s(%p,%p)\n", __func__, hsotg, qh); + + if (list_empty(&qh->qtd_list)) { + dev_dbg(hsotg->dev, "No QTDs in QH list\n"); + return -ENOMEM; + } + + if (list_empty(&hsotg->free_hc_list)) { + dev_dbg(hsotg->dev, "No free channel to assign\n"); + return -ENOMEM; + } + + chan = list_first_entry(&hsotg->free_hc_list, struct dwc2_host_chan, + hc_list_entry); + + /* Remove host channel from free list */ + list_del_init(&chan->hc_list_entry); + + qtd = list_first_entry(&qh->qtd_list, struct dwc2_qtd, qtd_list_entry); + urb = qtd->urb; + qh->channel = chan; + qtd->in_process = 1; + + /* + * Use usb_pipedevice to determine device address. This address is + * 0 before the SET_ADDRESS command and the correct address afterward. + */ + chan->dev_addr = dwc2_hcd_get_dev_addr(&urb->pipe_info); + chan->ep_num = dwc2_hcd_get_ep_num(&urb->pipe_info); + chan->speed = qh->dev_speed; + chan->max_packet = dwc2_max_packet(qh->maxp); + + chan->xfer_started = 0; + chan->halt_status = DWC2_HC_XFER_NO_HALT_STATUS; + chan->error_state = (qtd->error_count > 0); + chan->halt_on_queue = 0; + chan->halt_pending = 0; + chan->requests = 0; + + /* + * The following values may be modified in the transfer type section + * below. The xfer_len value may be reduced when the transfer is + * started to accommodate the max widths of the XferSize and PktCnt + * fields in the HCTSIZn register. + */ + + chan->ep_is_in = (dwc2_hcd_is_pipe_in(&urb->pipe_info) != 0); + if (chan->ep_is_in) + chan->do_ping = 0; + else + chan->do_ping = qh->ping_state; + + chan->data_pid_start = qh->data_toggle; + chan->multi_count = 1; + + if (urb->actual_length > urb->length && + !dwc2_hcd_is_pipe_in(&urb->pipe_info)) + urb->actual_length = urb->length; + + if (hsotg->core_params->dma_enable > 0) { + chan->xfer_dma = urb->dma + urb->actual_length; + + /* For non-dword aligned case */ + if (hsotg->core_params->dma_desc_enable <= 0 && + (chan->xfer_dma & 0x3)) + bufptr = (u8 *)urb->buf + urb->actual_length; + } else { + chan->xfer_buf = (u8 *)urb->buf + urb->actual_length; + } + + chan->xfer_len = urb->length - urb->actual_length; + chan->xfer_count = 0; + + /* Set the split attributes if required */ + if (qh->do_split) + dwc2_hc_init_split(hsotg, chan, qtd, urb); + else + chan->do_split = 0; + + /* Set the transfer attributes */ + bufptr = dwc2_hc_init_xfer(hsotg, chan, qtd, bufptr); + + /* Non DWORD-aligned buffer case */ + if (bufptr) { + dev_vdbg(hsotg->dev, "Non-aligned buffer\n"); + if (dwc2_hc_setup_align_buf(hsotg, qh, chan, bufptr)) { + dev_err(hsotg->dev, + "%s: Failed to allocate memory to handle non-dword aligned buffer\n", + __func__); + /* Add channel back to free list */ + chan->align_buf = 0; + chan->multi_count = 0; + list_add_tail(&chan->hc_list_entry, + &hsotg->free_hc_list); + qtd->in_process = 0; + qh->channel = NULL; + return -ENOMEM; + } + } else { + chan->align_buf = 0; + } + + if (chan->ep_type == USB_ENDPOINT_XFER_INT || + chan->ep_type == USB_ENDPOINT_XFER_ISOC) + /* + * This value may be modified when the transfer is started + * to reflect the actual transfer length + */ + chan->multi_count = dwc2_hb_mult(qh->maxp); + + if (hsotg->core_params->dma_desc_enable > 0) + chan->desc_list_addr = qh->desc_list_dma; + + dwc2_hc_init(hsotg, chan); + chan->qh = qh; + + return 0; +} + +/** + * dwc2_hcd_select_transactions() - Selects transactions from the HCD transfer + * schedule and assigns them to available host channels. Called from the HCD + * interrupt handler functions. + * + * @hsotg: The HCD state structure + * + * Return: The types of new transactions that were assigned to host channels + */ +enum dwc2_transaction_type dwc2_hcd_select_transactions( + struct dwc2_hsotg *hsotg) +{ + enum dwc2_transaction_type ret_val = DWC2_TRANSACTION_NONE; + struct list_head *qh_ptr; + struct dwc2_qh *qh; + int num_channels; + +#ifdef DWC2_DEBUG_SOF + dev_vdbg(hsotg->dev, " Select Transactions\n"); +#endif + + /* Process entries in the periodic ready list */ + qh_ptr = hsotg->periodic_sched_ready.next; + while (qh_ptr != &hsotg->periodic_sched_ready) { + if (list_empty(&hsotg->free_hc_list)) + break; + if (hsotg->core_params->uframe_sched > 0) { + if (hsotg->available_host_channels <= 1) + break; + hsotg->available_host_channels--; + } + qh = list_entry(qh_ptr, struct dwc2_qh, qh_list_entry); + if (dwc2_assign_and_init_hc(hsotg, qh)) + break; + + /* + * Move the QH from the periodic ready schedule to the + * periodic assigned schedule + */ + qh_ptr = qh_ptr->next; + list_move(&qh->qh_list_entry, &hsotg->periodic_sched_assigned); + ret_val = DWC2_TRANSACTION_PERIODIC; + } + + /* + * Process entries in the inactive portion of the non-periodic + * schedule. Some free host channels may not be used if they are + * reserved for periodic transfers. + */ + num_channels = hsotg->core_params->host_channels; + qh_ptr = hsotg->non_periodic_sched_inactive.next; + while (qh_ptr != &hsotg->non_periodic_sched_inactive) { + if (hsotg->core_params->uframe_sched <= 0 && + hsotg->non_periodic_channels >= num_channels - + hsotg->periodic_channels) + break; + if (list_empty(&hsotg->free_hc_list)) + break; + qh = list_entry(qh_ptr, struct dwc2_qh, qh_list_entry); + if (hsotg->core_params->uframe_sched > 0) { + if (hsotg->available_host_channels < 1) + break; + hsotg->available_host_channels--; + } + + if (dwc2_assign_and_init_hc(hsotg, qh)) + break; + + /* + * Move the QH from the non-periodic inactive schedule to the + * non-periodic active schedule + */ + qh_ptr = qh_ptr->next; + list_move(&qh->qh_list_entry, + &hsotg->non_periodic_sched_active); + + if (ret_val == DWC2_TRANSACTION_NONE) + ret_val = DWC2_TRANSACTION_NON_PERIODIC; + else + ret_val = DWC2_TRANSACTION_ALL; + + if (hsotg->core_params->uframe_sched <= 0) + hsotg->non_periodic_channels++; + } + + return ret_val; +} + +/** + * dwc2_queue_transaction() - Attempts to queue a single transaction request for + * a host channel associated with either a periodic or non-periodic transfer + * + * @hsotg: The HCD state structure + * @chan: Host channel descriptor associated with either a periodic or + * non-periodic transfer + * @fifo_dwords_avail: Number of DWORDs available in the periodic Tx FIFO + * for periodic transfers or the non-periodic Tx FIFO + * for non-periodic transfers + * + * Return: 1 if a request is queued and more requests may be needed to + * complete the transfer, 0 if no more requests are required for this + * transfer, -1 if there is insufficient space in the Tx FIFO + * + * This function assumes that there is space available in the appropriate + * request queue. For an OUT transfer or SETUP transaction in Slave mode, + * it checks whether space is available in the appropriate Tx FIFO. + * + * Must be called with interrupt disabled and spinlock held + */ +static int dwc2_queue_transaction(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, + u16 fifo_dwords_avail) +{ + int retval = 0; + + if (hsotg->core_params->dma_enable > 0) { + if (hsotg->core_params->dma_desc_enable > 0) { + if (!chan->xfer_started || + chan->ep_type == USB_ENDPOINT_XFER_ISOC) { + dwc2_hcd_start_xfer_ddma(hsotg, chan->qh); + chan->qh->ping_state = 0; + } + } else if (!chan->xfer_started) { + dwc2_hc_start_transfer(hsotg, chan); + chan->qh->ping_state = 0; + } + } else if (chan->halt_pending) { + /* Don't queue a request if the channel has been halted */ + } else if (chan->halt_on_queue) { + dwc2_hc_halt(hsotg, chan, chan->halt_status); + } else if (chan->do_ping) { + if (!chan->xfer_started) + dwc2_hc_start_transfer(hsotg, chan); + } else if (!chan->ep_is_in || + chan->data_pid_start == DWC2_HC_PID_SETUP) { + if ((fifo_dwords_avail * 4) >= chan->max_packet) { + if (!chan->xfer_started) { + dwc2_hc_start_transfer(hsotg, chan); + retval = 1; + } else { + retval = dwc2_hc_continue_transfer(hsotg, chan); + } + } else { + retval = -1; + } + } else { + if (!chan->xfer_started) { + dwc2_hc_start_transfer(hsotg, chan); + retval = 1; + } else { + retval = dwc2_hc_continue_transfer(hsotg, chan); + } + } + + return retval; +} + +/* + * Processes periodic channels for the next frame and queues transactions for + * these channels to the DWC_otg controller. After queueing transactions, the + * Periodic Tx FIFO Empty interrupt is enabled if there are more transactions + * to queue as Periodic Tx FIFO or request queue space becomes available. + * Otherwise, the Periodic Tx FIFO Empty interrupt is disabled. + * + * Must be called with interrupt disabled and spinlock held + */ +static void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg) +{ + struct list_head *qh_ptr; + struct dwc2_qh *qh; + u32 tx_status; + u32 fspcavail; + u32 gintmsk; + int status; + int no_queue_space = 0; + int no_fifo_space = 0; + u32 qspcavail; + + if (dbg_perio()) + dev_vdbg(hsotg->dev, "Queue periodic transactions\n"); + + tx_status = readl(hsotg->regs + HPTXSTS); + qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >> + TXSTS_QSPCAVAIL_SHIFT; + fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >> + TXSTS_FSPCAVAIL_SHIFT; + + if (dbg_perio()) { + dev_vdbg(hsotg->dev, " P Tx Req Queue Space Avail (before queue): %d\n", + qspcavail); + dev_vdbg(hsotg->dev, " P Tx FIFO Space Avail (before queue): %d\n", + fspcavail); + } + + qh_ptr = hsotg->periodic_sched_assigned.next; + while (qh_ptr != &hsotg->periodic_sched_assigned) { + tx_status = readl(hsotg->regs + HPTXSTS); + qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >> + TXSTS_QSPCAVAIL_SHIFT; + if (qspcavail == 0) { + no_queue_space = 1; + break; + } + + qh = list_entry(qh_ptr, struct dwc2_qh, qh_list_entry); + if (!qh->channel) { + qh_ptr = qh_ptr->next; + continue; + } + + /* Make sure EP's TT buffer is clean before queueing qtds */ + if (qh->tt_buffer_dirty) { + qh_ptr = qh_ptr->next; + continue; + } + + /* + * Set a flag if we're queuing high-bandwidth in slave mode. + * The flag prevents any halts to get into the request queue in + * the middle of multiple high-bandwidth packets getting queued. + */ + if (hsotg->core_params->dma_enable <= 0 && + qh->channel->multi_count > 1) + hsotg->queuing_high_bandwidth = 1; + + fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >> + TXSTS_FSPCAVAIL_SHIFT; + status = dwc2_queue_transaction(hsotg, qh->channel, fspcavail); + if (status < 0) { + no_fifo_space = 1; + break; + } + + /* + * In Slave mode, stay on the current transfer until there is + * nothing more to do or the high-bandwidth request count is + * reached. In DMA mode, only need to queue one request. The + * controller automatically handles multiple packets for + * high-bandwidth transfers. + */ + if (hsotg->core_params->dma_enable > 0 || status == 0 || + qh->channel->requests == qh->channel->multi_count) { + qh_ptr = qh_ptr->next; + /* + * Move the QH from the periodic assigned schedule to + * the periodic queued schedule + */ + list_move(&qh->qh_list_entry, + &hsotg->periodic_sched_queued); + + /* done queuing high bandwidth */ + hsotg->queuing_high_bandwidth = 0; + } + } + + if (hsotg->core_params->dma_enable <= 0) { + tx_status = readl(hsotg->regs + HPTXSTS); + qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >> + TXSTS_QSPCAVAIL_SHIFT; + fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >> + TXSTS_FSPCAVAIL_SHIFT; + if (dbg_perio()) { + dev_vdbg(hsotg->dev, + " P Tx Req Queue Space Avail (after queue): %d\n", + qspcavail); + dev_vdbg(hsotg->dev, + " P Tx FIFO Space Avail (after queue): %d\n", + fspcavail); + } + + if (!list_empty(&hsotg->periodic_sched_assigned) || + no_queue_space || no_fifo_space) { + /* + * May need to queue more transactions as the request + * queue or Tx FIFO empties. Enable the periodic Tx + * FIFO empty interrupt. (Always use the half-empty + * level to ensure that new requests are loaded as + * soon as possible.) + */ + gintmsk = readl(hsotg->regs + GINTMSK); + gintmsk |= GINTSTS_PTXFEMP; + writel(gintmsk, hsotg->regs + GINTMSK); + } else { + /* + * Disable the Tx FIFO empty interrupt since there are + * no more transactions that need to be queued right + * now. This function is called from interrupt + * handlers to queue more transactions as transfer + * states change. + */ + gintmsk = readl(hsotg->regs + GINTMSK); + gintmsk &= ~GINTSTS_PTXFEMP; + writel(gintmsk, hsotg->regs + GINTMSK); + } + } +} + +/* + * Processes active non-periodic channels and queues transactions for these + * channels to the DWC_otg controller. After queueing transactions, the NP Tx + * FIFO Empty interrupt is enabled if there are more transactions to queue as + * NP Tx FIFO or request queue space becomes available. Otherwise, the NP Tx + * FIFO Empty interrupt is disabled. + * + * Must be called with interrupt disabled and spinlock held + */ +static void dwc2_process_non_periodic_channels(struct dwc2_hsotg *hsotg) +{ + struct list_head *orig_qh_ptr; + struct dwc2_qh *qh; + u32 tx_status; + u32 qspcavail; + u32 fspcavail; + u32 gintmsk; + int status; + int no_queue_space = 0; + int no_fifo_space = 0; + int more_to_do = 0; + + dev_vdbg(hsotg->dev, "Queue non-periodic transactions\n"); + + tx_status = readl(hsotg->regs + GNPTXSTS); + qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >> + TXSTS_QSPCAVAIL_SHIFT; + fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >> + TXSTS_FSPCAVAIL_SHIFT; + dev_vdbg(hsotg->dev, " NP Tx Req Queue Space Avail (before queue): %d\n", + qspcavail); + dev_vdbg(hsotg->dev, " NP Tx FIFO Space Avail (before queue): %d\n", + fspcavail); + + /* + * Keep track of the starting point. Skip over the start-of-list + * entry. + */ + if (hsotg->non_periodic_qh_ptr == &hsotg->non_periodic_sched_active) + hsotg->non_periodic_qh_ptr = hsotg->non_periodic_qh_ptr->next; + orig_qh_ptr = hsotg->non_periodic_qh_ptr; + + /* + * Process once through the active list or until no more space is + * available in the request queue or the Tx FIFO + */ + do { + tx_status = readl(hsotg->regs + GNPTXSTS); + qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >> + TXSTS_QSPCAVAIL_SHIFT; + if (hsotg->core_params->dma_enable <= 0 && qspcavail == 0) { + no_queue_space = 1; + break; + } + + qh = list_entry(hsotg->non_periodic_qh_ptr, struct dwc2_qh, + qh_list_entry); + if (!qh->channel) + goto next; + + /* Make sure EP's TT buffer is clean before queueing qtds */ + if (qh->tt_buffer_dirty) + goto next; + + fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >> + TXSTS_FSPCAVAIL_SHIFT; + status = dwc2_queue_transaction(hsotg, qh->channel, fspcavail); + + if (status > 0) { + more_to_do = 1; + } else if (status < 0) { + no_fifo_space = 1; + break; + } +next: + /* Advance to next QH, skipping start-of-list entry */ + hsotg->non_periodic_qh_ptr = hsotg->non_periodic_qh_ptr->next; + if (hsotg->non_periodic_qh_ptr == + &hsotg->non_periodic_sched_active) + hsotg->non_periodic_qh_ptr = + hsotg->non_periodic_qh_ptr->next; + } while (hsotg->non_periodic_qh_ptr != orig_qh_ptr); + + if (hsotg->core_params->dma_enable <= 0) { + tx_status = readl(hsotg->regs + GNPTXSTS); + qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >> + TXSTS_QSPCAVAIL_SHIFT; + fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >> + TXSTS_FSPCAVAIL_SHIFT; + dev_vdbg(hsotg->dev, + " NP Tx Req Queue Space Avail (after queue): %d\n", + qspcavail); + dev_vdbg(hsotg->dev, + " NP Tx FIFO Space Avail (after queue): %d\n", + fspcavail); + + if (more_to_do || no_queue_space || no_fifo_space) { + /* + * May need to queue more transactions as the request + * queue or Tx FIFO empties. Enable the non-periodic + * Tx FIFO empty interrupt. (Always use the half-empty + * level to ensure that new requests are loaded as + * soon as possible.) + */ + gintmsk = readl(hsotg->regs + GINTMSK); + gintmsk |= GINTSTS_NPTXFEMP; + writel(gintmsk, hsotg->regs + GINTMSK); + } else { + /* + * Disable the Tx FIFO empty interrupt since there are + * no more transactions that need to be queued right + * now. This function is called from interrupt + * handlers to queue more transactions as transfer + * states change. + */ + gintmsk = readl(hsotg->regs + GINTMSK); + gintmsk &= ~GINTSTS_NPTXFEMP; + writel(gintmsk, hsotg->regs + GINTMSK); + } + } +} + +/** + * dwc2_hcd_queue_transactions() - Processes the currently active host channels + * and queues transactions for these channels to the DWC_otg controller. Called + * from the HCD interrupt handler functions. + * + * @hsotg: The HCD state structure + * @tr_type: The type(s) of transactions to queue (non-periodic, periodic, + * or both) + * + * Must be called with interrupt disabled and spinlock held + */ +void dwc2_hcd_queue_transactions(struct dwc2_hsotg *hsotg, + enum dwc2_transaction_type tr_type) +{ +#ifdef DWC2_DEBUG_SOF + dev_vdbg(hsotg->dev, "Queue Transactions\n"); +#endif + /* Process host channels associated with periodic transfers */ + if ((tr_type == DWC2_TRANSACTION_PERIODIC || + tr_type == DWC2_TRANSACTION_ALL) && + !list_empty(&hsotg->periodic_sched_assigned)) + dwc2_process_periodic_channels(hsotg); + + /* Process host channels associated with non-periodic transfers */ + if (tr_type == DWC2_TRANSACTION_NON_PERIODIC || + tr_type == DWC2_TRANSACTION_ALL) { + if (!list_empty(&hsotg->non_periodic_sched_active)) { + dwc2_process_non_periodic_channels(hsotg); + } else { + /* + * Ensure NP Tx FIFO empty interrupt is disabled when + * there are no non-periodic transfers to process + */ + u32 gintmsk = readl(hsotg->regs + GINTMSK); + + gintmsk &= ~GINTSTS_NPTXFEMP; + writel(gintmsk, hsotg->regs + GINTMSK); + } + } +} + +static void dwc2_conn_id_status_change(struct work_struct *work) +{ + struct dwc2_hsotg *hsotg = container_of(work, struct dwc2_hsotg, + wf_otg); + u32 count = 0; + u32 gotgctl; + + dev_dbg(hsotg->dev, "%s()\n", __func__); + + gotgctl = readl(hsotg->regs + GOTGCTL); + dev_dbg(hsotg->dev, "gotgctl=%0x\n", gotgctl); + dev_dbg(hsotg->dev, "gotgctl.b.conidsts=%d\n", + !!(gotgctl & GOTGCTL_CONID_B)); + + /* B-Device connector (Device Mode) */ + if (gotgctl & GOTGCTL_CONID_B) { + /* Wait for switch to device mode */ + dev_dbg(hsotg->dev, "connId B\n"); + while (!dwc2_is_device_mode(hsotg)) { + dev_info(hsotg->dev, + "Waiting for Peripheral Mode, Mode=%s\n", + dwc2_is_host_mode(hsotg) ? "Host" : + "Peripheral"); + usleep_range(20000, 40000); + if (++count > 250) + break; + } + if (count > 250) + dev_err(hsotg->dev, + "Connection id status change timed out\n"); + hsotg->op_state = OTG_STATE_B_PERIPHERAL; + dwc2_core_init(hsotg, false, -1); + dwc2_enable_global_interrupts(hsotg); + } else { + /* A-Device connector (Host Mode) */ + dev_dbg(hsotg->dev, "connId A\n"); + while (!dwc2_is_host_mode(hsotg)) { + dev_info(hsotg->dev, "Waiting for Host Mode, Mode=%s\n", + dwc2_is_host_mode(hsotg) ? + "Host" : "Peripheral"); + usleep_range(20000, 40000); + if (++count > 250) + break; + } + if (count > 250) + dev_err(hsotg->dev, + "Connection id status change timed out\n"); + hsotg->op_state = OTG_STATE_A_HOST; + + /* Initialize the Core for Host mode */ + dwc2_core_init(hsotg, false, -1); + dwc2_enable_global_interrupts(hsotg); + dwc2_hcd_start(hsotg); + } +} + +static void dwc2_wakeup_detected(unsigned long data) +{ + struct dwc2_hsotg *hsotg = (struct dwc2_hsotg *)data; + u32 hprt0; + + dev_dbg(hsotg->dev, "%s()\n", __func__); + + /* + * Clear the Resume after 70ms. (Need 20 ms minimum. Use 70 ms + * so that OPT tests pass with all PHYs.) + */ + hprt0 = dwc2_read_hprt0(hsotg); + dev_dbg(hsotg->dev, "Resume: HPRT0=%0x\n", hprt0); + hprt0 &= ~HPRT0_RES; + writel(hprt0, hsotg->regs + HPRT0); + dev_dbg(hsotg->dev, "Clear Resume: HPRT0=%0x\n", + readl(hsotg->regs + HPRT0)); + + dwc2_hcd_rem_wakeup(hsotg); + + /* Change to L0 state */ + hsotg->lx_state = DWC2_L0; +} + +static int dwc2_host_is_b_hnp_enabled(struct dwc2_hsotg *hsotg) +{ + struct usb_hcd *hcd = dwc2_hsotg_to_hcd(hsotg); + + return hcd->self.b_hnp_enable; +} + +/* Must NOT be called with interrupt disabled or spinlock held */ +static void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex) +{ + unsigned long flags; + u32 hprt0; + u32 pcgctl; + u32 gotgctl; + + dev_dbg(hsotg->dev, "%s()\n", __func__); + + spin_lock_irqsave(&hsotg->lock, flags); + + if (windex == hsotg->otg_port && dwc2_host_is_b_hnp_enabled(hsotg)) { + gotgctl = readl(hsotg->regs + GOTGCTL); + gotgctl |= GOTGCTL_HSTSETHNPEN; + writel(gotgctl, hsotg->regs + GOTGCTL); + hsotg->op_state = OTG_STATE_A_SUSPEND; + } + + hprt0 = dwc2_read_hprt0(hsotg); + hprt0 |= HPRT0_SUSP; + writel(hprt0, hsotg->regs + HPRT0); + + /* Update lx_state */ + hsotg->lx_state = DWC2_L2; + + /* Suspend the Phy Clock */ + pcgctl = readl(hsotg->regs + PCGCTL); + pcgctl |= PCGCTL_STOPPCLK; + writel(pcgctl, hsotg->regs + PCGCTL); + udelay(10); + + /* For HNP the bus must be suspended for at least 200ms */ + if (dwc2_host_is_b_hnp_enabled(hsotg)) { + pcgctl = readl(hsotg->regs + PCGCTL); + pcgctl &= ~PCGCTL_STOPPCLK; + writel(pcgctl, hsotg->regs + PCGCTL); + + spin_unlock_irqrestore(&hsotg->lock, flags); + + usleep_range(200000, 250000); + } else { + spin_unlock_irqrestore(&hsotg->lock, flags); + } +} + +/* Handles hub class-specific requests */ +static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, + u16 wvalue, u16 windex, char *buf, u16 wlength) +{ + struct usb_hub_descriptor *hub_desc; + int retval = 0; + u32 hprt0; + u32 port_status; + u32 speed; + u32 pcgctl; + + switch (typereq) { + case ClearHubFeature: + dev_dbg(hsotg->dev, "ClearHubFeature %1xh\n", wvalue); + + switch (wvalue) { + case C_HUB_LOCAL_POWER: + case C_HUB_OVER_CURRENT: + /* Nothing required here */ + break; + + default: + retval = -EINVAL; + dev_err(hsotg->dev, + "ClearHubFeature request %1xh unknown\n", + wvalue); + } + break; + + case ClearPortFeature: + if (wvalue != USB_PORT_FEAT_L1) + if (!windex || windex > 1) + goto error; + switch (wvalue) { + case USB_PORT_FEAT_ENABLE: + dev_dbg(hsotg->dev, + "ClearPortFeature USB_PORT_FEAT_ENABLE\n"); + hprt0 = dwc2_read_hprt0(hsotg); + hprt0 |= HPRT0_ENA; + writel(hprt0, hsotg->regs + HPRT0); + break; + + case USB_PORT_FEAT_SUSPEND: + dev_dbg(hsotg->dev, + "ClearPortFeature USB_PORT_FEAT_SUSPEND\n"); + writel(0, hsotg->regs + PCGCTL); + usleep_range(20000, 40000); + + hprt0 = dwc2_read_hprt0(hsotg); + hprt0 |= HPRT0_RES; + writel(hprt0, hsotg->regs + HPRT0); + hprt0 &= ~HPRT0_SUSP; + usleep_range(100000, 150000); + + hprt0 &= ~HPRT0_RES; + writel(hprt0, hsotg->regs + HPRT0); + break; + + case USB_PORT_FEAT_POWER: + dev_dbg(hsotg->dev, + "ClearPortFeature USB_PORT_FEAT_POWER\n"); + hprt0 = dwc2_read_hprt0(hsotg); + hprt0 &= ~HPRT0_PWR; + writel(hprt0, hsotg->regs + HPRT0); + break; + + case USB_PORT_FEAT_INDICATOR: + dev_dbg(hsotg->dev, + "ClearPortFeature USB_PORT_FEAT_INDICATOR\n"); + /* Port indicator not supported */ + break; + + case USB_PORT_FEAT_C_CONNECTION: + /* + * Clears driver's internal Connect Status Change flag + */ + dev_dbg(hsotg->dev, + "ClearPortFeature USB_PORT_FEAT_C_CONNECTION\n"); + hsotg->flags.b.port_connect_status_change = 0; + break; + + case USB_PORT_FEAT_C_RESET: + /* Clears driver's internal Port Reset Change flag */ + dev_dbg(hsotg->dev, + "ClearPortFeature USB_PORT_FEAT_C_RESET\n"); + hsotg->flags.b.port_reset_change = 0; + break; + + case USB_PORT_FEAT_C_ENABLE: + /* + * Clears the driver's internal Port Enable/Disable + * Change flag + */ + dev_dbg(hsotg->dev, + "ClearPortFeature USB_PORT_FEAT_C_ENABLE\n"); + hsotg->flags.b.port_enable_change = 0; + break; + + case USB_PORT_FEAT_C_SUSPEND: + /* + * Clears the driver's internal Port Suspend Change + * flag, which is set when resume signaling on the host + * port is complete + */ + dev_dbg(hsotg->dev, + "ClearPortFeature USB_PORT_FEAT_C_SUSPEND\n"); + hsotg->flags.b.port_suspend_change = 0; + break; + + case USB_PORT_FEAT_C_PORT_L1: + dev_dbg(hsotg->dev, + "ClearPortFeature USB_PORT_FEAT_C_PORT_L1\n"); + hsotg->flags.b.port_l1_change = 0; + break; + + case USB_PORT_FEAT_C_OVER_CURRENT: + dev_dbg(hsotg->dev, + "ClearPortFeature USB_PORT_FEAT_C_OVER_CURRENT\n"); + hsotg->flags.b.port_over_current_change = 0; + break; + + default: + retval = -EINVAL; + dev_err(hsotg->dev, + "ClearPortFeature request %1xh unknown or unsupported\n", + wvalue); + } + break; + + case GetHubDescriptor: + dev_dbg(hsotg->dev, "GetHubDescriptor\n"); + hub_desc = (struct usb_hub_descriptor *)buf; + hub_desc->bDescLength = 9; + hub_desc->bDescriptorType = 0x29; + hub_desc->bNbrPorts = 1; + hub_desc->wHubCharacteristics = cpu_to_le16(0x08); + hub_desc->bPwrOn2PwrGood = 1; + hub_desc->bHubContrCurrent = 0; + hub_desc->u.hs.DeviceRemovable[0] = 0; + hub_desc->u.hs.DeviceRemovable[1] = 0xff; + break; + + case GetHubStatus: + dev_dbg(hsotg->dev, "GetHubStatus\n"); + memset(buf, 0, 4); + break; + + case GetPortStatus: + dev_vdbg(hsotg->dev, + "GetPortStatus wIndex=0x%04x flags=0x%08x\n", windex, + hsotg->flags.d32); + if (!windex || windex > 1) + goto error; + + port_status = 0; + if (hsotg->flags.b.port_connect_status_change) + port_status |= USB_PORT_STAT_C_CONNECTION << 16; + if (hsotg->flags.b.port_enable_change) + port_status |= USB_PORT_STAT_C_ENABLE << 16; + if (hsotg->flags.b.port_suspend_change) + port_status |= USB_PORT_STAT_C_SUSPEND << 16; + if (hsotg->flags.b.port_l1_change) + port_status |= USB_PORT_STAT_C_L1 << 16; + if (hsotg->flags.b.port_reset_change) + port_status |= USB_PORT_STAT_C_RESET << 16; + if (hsotg->flags.b.port_over_current_change) { + dev_warn(hsotg->dev, "Overcurrent change detected\n"); + port_status |= USB_PORT_STAT_C_OVERCURRENT << 16; + } + + if (!hsotg->flags.b.port_connect_status) { + /* + * The port is disconnected, which means the core is + * either in device mode or it soon will be. Just + * return 0's for the remainder of the port status + * since the port register can't be read if the core + * is in device mode. + */ + *(__le32 *)buf = cpu_to_le32(port_status); + break; + } + + hprt0 = readl(hsotg->regs + HPRT0); + dev_vdbg(hsotg->dev, " HPRT0: 0x%08x\n", hprt0); + + if (hprt0 & HPRT0_CONNSTS) + port_status |= USB_PORT_STAT_CONNECTION; + if (hprt0 & HPRT0_ENA) + port_status |= USB_PORT_STAT_ENABLE; + if (hprt0 & HPRT0_SUSP) + port_status |= USB_PORT_STAT_SUSPEND; + if (hprt0 & HPRT0_OVRCURRACT) + port_status |= USB_PORT_STAT_OVERCURRENT; + if (hprt0 & HPRT0_RST) + port_status |= USB_PORT_STAT_RESET; + if (hprt0 & HPRT0_PWR) + port_status |= USB_PORT_STAT_POWER; + + speed = (hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT; + if (speed == HPRT0_SPD_HIGH_SPEED) + port_status |= USB_PORT_STAT_HIGH_SPEED; + else if (speed == HPRT0_SPD_LOW_SPEED) + port_status |= USB_PORT_STAT_LOW_SPEED; + + if (hprt0 & HPRT0_TSTCTL_MASK) + port_status |= USB_PORT_STAT_TEST; + /* USB_PORT_FEAT_INDICATOR unsupported always 0 */ + + dev_vdbg(hsotg->dev, "port_status=%08x\n", port_status); + *(__le32 *)buf = cpu_to_le32(port_status); + break; + + case SetHubFeature: + dev_dbg(hsotg->dev, "SetHubFeature\n"); + /* No HUB features supported */ + break; + + case SetPortFeature: + dev_dbg(hsotg->dev, "SetPortFeature\n"); + if (wvalue != USB_PORT_FEAT_TEST && (!windex || windex > 1)) + goto error; + + if (!hsotg->flags.b.port_connect_status) { + /* + * The port is disconnected, which means the core is + * either in device mode or it soon will be. Just + * return without doing anything since the port + * register can't be written if the core is in device + * mode. + */ + break; + } + + switch (wvalue) { + case USB_PORT_FEAT_SUSPEND: + dev_dbg(hsotg->dev, + "SetPortFeature - USB_PORT_FEAT_SUSPEND\n"); + if (windex != hsotg->otg_port) + goto error; + dwc2_port_suspend(hsotg, windex); + break; + + case USB_PORT_FEAT_POWER: + dev_dbg(hsotg->dev, + "SetPortFeature - USB_PORT_FEAT_POWER\n"); + hprt0 = dwc2_read_hprt0(hsotg); + hprt0 |= HPRT0_PWR; + writel(hprt0, hsotg->regs + HPRT0); + break; + + case USB_PORT_FEAT_RESET: + hprt0 = dwc2_read_hprt0(hsotg); + dev_dbg(hsotg->dev, + "SetPortFeature - USB_PORT_FEAT_RESET\n"); + pcgctl = readl(hsotg->regs + PCGCTL); + pcgctl &= ~(PCGCTL_ENBL_SLEEP_GATING | PCGCTL_STOPPCLK); + writel(pcgctl, hsotg->regs + PCGCTL); + /* ??? Original driver does this */ + writel(0, hsotg->regs + PCGCTL); + + hprt0 = dwc2_read_hprt0(hsotg); + /* Clear suspend bit if resetting from suspend state */ + hprt0 &= ~HPRT0_SUSP; + + /* + * When B-Host the Port reset bit is set in the Start + * HCD Callback function, so that the reset is started + * within 1ms of the HNP success interrupt + */ + if (!dwc2_hcd_is_b_host(hsotg)) { + hprt0 |= HPRT0_PWR | HPRT0_RST; + dev_dbg(hsotg->dev, + "In host mode, hprt0=%08x\n", hprt0); + writel(hprt0, hsotg->regs + HPRT0); + } + + /* Clear reset bit in 10ms (FS/LS) or 50ms (HS) */ + usleep_range(50000, 70000); + hprt0 &= ~HPRT0_RST; + writel(hprt0, hsotg->regs + HPRT0); + hsotg->lx_state = DWC2_L0; /* Now back to On state */ + break; + + case USB_PORT_FEAT_INDICATOR: + dev_dbg(hsotg->dev, + "SetPortFeature - USB_PORT_FEAT_INDICATOR\n"); + /* Not supported */ + break; + + default: + retval = -EINVAL; + dev_err(hsotg->dev, + "SetPortFeature %1xh unknown or unsupported\n", + wvalue); + break; + } + break; + + default: +error: + retval = -EINVAL; + dev_dbg(hsotg->dev, + "Unknown hub control request: %1xh wIndex: %1xh wValue: %1xh\n", + typereq, windex, wvalue); + break; + } + + return retval; +} + +static int dwc2_hcd_is_status_changed(struct dwc2_hsotg *hsotg, int port) +{ + int retval; + + if (port != 1) + return -EINVAL; + + retval = (hsotg->flags.b.port_connect_status_change || + hsotg->flags.b.port_reset_change || + hsotg->flags.b.port_enable_change || + hsotg->flags.b.port_suspend_change || + hsotg->flags.b.port_over_current_change); + + if (retval) { + dev_dbg(hsotg->dev, + "DWC OTG HCD HUB STATUS DATA: Root port status changed\n"); + dev_dbg(hsotg->dev, " port_connect_status_change: %d\n", + hsotg->flags.b.port_connect_status_change); + dev_dbg(hsotg->dev, " port_reset_change: %d\n", + hsotg->flags.b.port_reset_change); + dev_dbg(hsotg->dev, " port_enable_change: %d\n", + hsotg->flags.b.port_enable_change); + dev_dbg(hsotg->dev, " port_suspend_change: %d\n", + hsotg->flags.b.port_suspend_change); + dev_dbg(hsotg->dev, " port_over_current_change: %d\n", + hsotg->flags.b.port_over_current_change); + } + + return retval; +} + +int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg) +{ + u32 hfnum = readl(hsotg->regs + HFNUM); + +#ifdef DWC2_DEBUG_SOF + dev_vdbg(hsotg->dev, "DWC OTG HCD GET FRAME NUMBER %d\n", + (hfnum & HFNUM_FRNUM_MASK) >> HFNUM_FRNUM_SHIFT); +#endif + return (hfnum & HFNUM_FRNUM_MASK) >> HFNUM_FRNUM_SHIFT; +} + +int dwc2_hcd_is_b_host(struct dwc2_hsotg *hsotg) +{ + return hsotg->op_state == OTG_STATE_B_HOST; +} + +static struct dwc2_hcd_urb *dwc2_hcd_urb_alloc(struct dwc2_hsotg *hsotg, + int iso_desc_count, + gfp_t mem_flags) +{ + struct dwc2_hcd_urb *urb; + u32 size = sizeof(*urb) + iso_desc_count * + sizeof(struct dwc2_hcd_iso_packet_desc); + + urb = kzalloc(size, mem_flags); + if (urb) + urb->packet_count = iso_desc_count; + return urb; +} + +static void dwc2_hcd_urb_set_pipeinfo(struct dwc2_hsotg *hsotg, + struct dwc2_hcd_urb *urb, u8 dev_addr, + u8 ep_num, u8 ep_type, u8 ep_dir, u16 mps) +{ + if (dbg_perio() || + ep_type == USB_ENDPOINT_XFER_BULK || + ep_type == USB_ENDPOINT_XFER_CONTROL) + dev_vdbg(hsotg->dev, + "addr=%d, ep_num=%d, ep_dir=%1x, ep_type=%1x, mps=%d\n", + dev_addr, ep_num, ep_dir, ep_type, mps); + urb->pipe_info.dev_addr = dev_addr; + urb->pipe_info.ep_num = ep_num; + urb->pipe_info.pipe_type = ep_type; + urb->pipe_info.pipe_dir = ep_dir; + urb->pipe_info.mps = mps; +} + +/* + * NOTE: This function will be removed once the peripheral controller code + * is integrated and the driver is stable + */ +void dwc2_hcd_dump_state(struct dwc2_hsotg *hsotg) +{ +#ifdef DEBUG + struct dwc2_host_chan *chan; + struct dwc2_hcd_urb *urb; + struct dwc2_qtd *qtd; + int num_channels; + u32 np_tx_status; + u32 p_tx_status; + int i; + + num_channels = hsotg->core_params->host_channels; + dev_dbg(hsotg->dev, "\n"); + dev_dbg(hsotg->dev, + "************************************************************\n"); + dev_dbg(hsotg->dev, "HCD State:\n"); + dev_dbg(hsotg->dev, " Num channels: %d\n", num_channels); + + for (i = 0; i < num_channels; i++) { + chan = hsotg->hc_ptr_array[i]; + dev_dbg(hsotg->dev, " Channel %d:\n", i); + dev_dbg(hsotg->dev, + " dev_addr: %d, ep_num: %d, ep_is_in: %d\n", + chan->dev_addr, chan->ep_num, chan->ep_is_in); + dev_dbg(hsotg->dev, " speed: %d\n", chan->speed); + dev_dbg(hsotg->dev, " ep_type: %d\n", chan->ep_type); + dev_dbg(hsotg->dev, " max_packet: %d\n", chan->max_packet); + dev_dbg(hsotg->dev, " data_pid_start: %d\n", + chan->data_pid_start); + dev_dbg(hsotg->dev, " multi_count: %d\n", chan->multi_count); + dev_dbg(hsotg->dev, " xfer_started: %d\n", + chan->xfer_started); + dev_dbg(hsotg->dev, " xfer_buf: %p\n", chan->xfer_buf); + dev_dbg(hsotg->dev, " xfer_dma: %08lx\n", + (unsigned long)chan->xfer_dma); + dev_dbg(hsotg->dev, " xfer_len: %d\n", chan->xfer_len); + dev_dbg(hsotg->dev, " xfer_count: %d\n", chan->xfer_count); + dev_dbg(hsotg->dev, " halt_on_queue: %d\n", + chan->halt_on_queue); + dev_dbg(hsotg->dev, " halt_pending: %d\n", + chan->halt_pending); + dev_dbg(hsotg->dev, " halt_status: %d\n", chan->halt_status); + dev_dbg(hsotg->dev, " do_split: %d\n", chan->do_split); + dev_dbg(hsotg->dev, " complete_split: %d\n", + chan->complete_split); + dev_dbg(hsotg->dev, " hub_addr: %d\n", chan->hub_addr); + dev_dbg(hsotg->dev, " hub_port: %d\n", chan->hub_port); + dev_dbg(hsotg->dev, " xact_pos: %d\n", chan->xact_pos); + dev_dbg(hsotg->dev, " requests: %d\n", chan->requests); + dev_dbg(hsotg->dev, " qh: %p\n", chan->qh); + + if (chan->xfer_started) { + u32 hfnum, hcchar, hctsiz, hcint, hcintmsk; + + hfnum = readl(hsotg->regs + HFNUM); + hcchar = readl(hsotg->regs + HCCHAR(i)); + hctsiz = readl(hsotg->regs + HCTSIZ(i)); + hcint = readl(hsotg->regs + HCINT(i)); + hcintmsk = readl(hsotg->regs + HCINTMSK(i)); + dev_dbg(hsotg->dev, " hfnum: 0x%08x\n", hfnum); + dev_dbg(hsotg->dev, " hcchar: 0x%08x\n", hcchar); + dev_dbg(hsotg->dev, " hctsiz: 0x%08x\n", hctsiz); + dev_dbg(hsotg->dev, " hcint: 0x%08x\n", hcint); + dev_dbg(hsotg->dev, " hcintmsk: 0x%08x\n", hcintmsk); + } + + if (!(chan->xfer_started && chan->qh)) + continue; + + list_for_each_entry(qtd, &chan->qh->qtd_list, qtd_list_entry) { + if (!qtd->in_process) + break; + urb = qtd->urb; + dev_dbg(hsotg->dev, " URB Info:\n"); + dev_dbg(hsotg->dev, " qtd: %p, urb: %p\n", + qtd, urb); + if (urb) { + dev_dbg(hsotg->dev, + " Dev: %d, EP: %d %s\n", + dwc2_hcd_get_dev_addr(&urb->pipe_info), + dwc2_hcd_get_ep_num(&urb->pipe_info), + dwc2_hcd_is_pipe_in(&urb->pipe_info) ? + "IN" : "OUT"); + dev_dbg(hsotg->dev, + " Max packet size: %d\n", + dwc2_hcd_get_mps(&urb->pipe_info)); + dev_dbg(hsotg->dev, + " transfer_buffer: %p\n", + urb->buf); + dev_dbg(hsotg->dev, + " transfer_dma: %08lx\n", + (unsigned long)urb->dma); + dev_dbg(hsotg->dev, + " transfer_buffer_length: %d\n", + urb->length); + dev_dbg(hsotg->dev, " actual_length: %d\n", + urb->actual_length); + } + } + } + + dev_dbg(hsotg->dev, " non_periodic_channels: %d\n", + hsotg->non_periodic_channels); + dev_dbg(hsotg->dev, " periodic_channels: %d\n", + hsotg->periodic_channels); + dev_dbg(hsotg->dev, " periodic_usecs: %d\n", hsotg->periodic_usecs); + np_tx_status = readl(hsotg->regs + GNPTXSTS); + dev_dbg(hsotg->dev, " NP Tx Req Queue Space Avail: %d\n", + (np_tx_status & TXSTS_QSPCAVAIL_MASK) >> TXSTS_QSPCAVAIL_SHIFT); + dev_dbg(hsotg->dev, " NP Tx FIFO Space Avail: %d\n", + (np_tx_status & TXSTS_FSPCAVAIL_MASK) >> TXSTS_FSPCAVAIL_SHIFT); + p_tx_status = readl(hsotg->regs + HPTXSTS); + dev_dbg(hsotg->dev, " P Tx Req Queue Space Avail: %d\n", + (p_tx_status & TXSTS_QSPCAVAIL_MASK) >> TXSTS_QSPCAVAIL_SHIFT); + dev_dbg(hsotg->dev, " P Tx FIFO Space Avail: %d\n", + (p_tx_status & TXSTS_FSPCAVAIL_MASK) >> TXSTS_FSPCAVAIL_SHIFT); + dwc2_hcd_dump_frrem(hsotg); + dwc2_dump_global_registers(hsotg); + dwc2_dump_host_registers(hsotg); + dev_dbg(hsotg->dev, + "************************************************************\n"); + dev_dbg(hsotg->dev, "\n"); +#endif +} + +/* + * NOTE: This function will be removed once the peripheral controller code + * is integrated and the driver is stable + */ +void dwc2_hcd_dump_frrem(struct dwc2_hsotg *hsotg) +{ +#ifdef DWC2_DUMP_FRREM + dev_dbg(hsotg->dev, "Frame remaining at SOF:\n"); + dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n", + hsotg->frrem_samples, hsotg->frrem_accum, + hsotg->frrem_samples > 0 ? + hsotg->frrem_accum / hsotg->frrem_samples : 0); + dev_dbg(hsotg->dev, "\n"); + dev_dbg(hsotg->dev, "Frame remaining at start_transfer (uframe 7):\n"); + dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n", + hsotg->hfnum_7_samples, + hsotg->hfnum_7_frrem_accum, + hsotg->hfnum_7_samples > 0 ? + hsotg->hfnum_7_frrem_accum / hsotg->hfnum_7_samples : 0); + dev_dbg(hsotg->dev, "Frame remaining at start_transfer (uframe 0):\n"); + dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n", + hsotg->hfnum_0_samples, + hsotg->hfnum_0_frrem_accum, + hsotg->hfnum_0_samples > 0 ? + hsotg->hfnum_0_frrem_accum / hsotg->hfnum_0_samples : 0); + dev_dbg(hsotg->dev, "Frame remaining at start_transfer (uframe 1-6):\n"); + dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n", + hsotg->hfnum_other_samples, + hsotg->hfnum_other_frrem_accum, + hsotg->hfnum_other_samples > 0 ? + hsotg->hfnum_other_frrem_accum / hsotg->hfnum_other_samples : + 0); + dev_dbg(hsotg->dev, "\n"); + dev_dbg(hsotg->dev, "Frame remaining at sample point A (uframe 7):\n"); + dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n", + hsotg->hfnum_7_samples_a, hsotg->hfnum_7_frrem_accum_a, + hsotg->hfnum_7_samples_a > 0 ? + hsotg->hfnum_7_frrem_accum_a / hsotg->hfnum_7_samples_a : 0); + dev_dbg(hsotg->dev, "Frame remaining at sample point A (uframe 0):\n"); + dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n", + hsotg->hfnum_0_samples_a, hsotg->hfnum_0_frrem_accum_a, + hsotg->hfnum_0_samples_a > 0 ? + hsotg->hfnum_0_frrem_accum_a / hsotg->hfnum_0_samples_a : 0); + dev_dbg(hsotg->dev, "Frame remaining at sample point A (uframe 1-6):\n"); + dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n", + hsotg->hfnum_other_samples_a, hsotg->hfnum_other_frrem_accum_a, + hsotg->hfnum_other_samples_a > 0 ? + hsotg->hfnum_other_frrem_accum_a / hsotg->hfnum_other_samples_a + : 0); + dev_dbg(hsotg->dev, "\n"); + dev_dbg(hsotg->dev, "Frame remaining at sample point B (uframe 7):\n"); + dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n", + hsotg->hfnum_7_samples_b, hsotg->hfnum_7_frrem_accum_b, + hsotg->hfnum_7_samples_b > 0 ? + hsotg->hfnum_7_frrem_accum_b / hsotg->hfnum_7_samples_b : 0); + dev_dbg(hsotg->dev, "Frame remaining at sample point B (uframe 0):\n"); + dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n", + hsotg->hfnum_0_samples_b, hsotg->hfnum_0_frrem_accum_b, + (hsotg->hfnum_0_samples_b > 0) ? + hsotg->hfnum_0_frrem_accum_b / hsotg->hfnum_0_samples_b : 0); + dev_dbg(hsotg->dev, "Frame remaining at sample point B (uframe 1-6):\n"); + dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n", + hsotg->hfnum_other_samples_b, hsotg->hfnum_other_frrem_accum_b, + (hsotg->hfnum_other_samples_b > 0) ? + hsotg->hfnum_other_frrem_accum_b / hsotg->hfnum_other_samples_b + : 0); +#endif +} + +struct wrapper_priv_data { + struct dwc2_hsotg *hsotg; +}; + +/* Gets the dwc2_hsotg from a usb_hcd */ +static struct dwc2_hsotg *dwc2_hcd_to_hsotg(struct usb_hcd *hcd) +{ + struct wrapper_priv_data *p; + + p = (struct wrapper_priv_data *) &hcd->hcd_priv; + return p->hsotg; +} + +static int _dwc2_hcd_start(struct usb_hcd *hcd); + +void dwc2_host_start(struct dwc2_hsotg *hsotg) +{ + struct usb_hcd *hcd = dwc2_hsotg_to_hcd(hsotg); + + hcd->self.is_b_host = dwc2_hcd_is_b_host(hsotg); + _dwc2_hcd_start(hcd); +} + +void dwc2_host_disconnect(struct dwc2_hsotg *hsotg) +{ + struct usb_hcd *hcd = dwc2_hsotg_to_hcd(hsotg); + + hcd->self.is_b_host = 0; +} + +void dwc2_host_hub_info(struct dwc2_hsotg *hsotg, void *context, int *hub_addr, + int *hub_port) +{ + struct urb *urb = context; + + if (urb->dev->tt) + *hub_addr = urb->dev->tt->hub->devnum; + else + *hub_addr = 0; + *hub_port = urb->dev->ttport; +} + +int dwc2_host_get_speed(struct dwc2_hsotg *hsotg, void *context) +{ + struct urb *urb = context; + + return urb->dev->speed; +} + +static void dwc2_allocate_bus_bandwidth(struct usb_hcd *hcd, u16 bw, + struct urb *urb) +{ + struct usb_bus *bus = hcd_to_bus(hcd); + + if (urb->interval) + bus->bandwidth_allocated += bw / urb->interval; + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) + bus->bandwidth_isoc_reqs++; + else + bus->bandwidth_int_reqs++; +} + +static void dwc2_free_bus_bandwidth(struct usb_hcd *hcd, u16 bw, + struct urb *urb) +{ + struct usb_bus *bus = hcd_to_bus(hcd); + + if (urb->interval) + bus->bandwidth_allocated -= bw / urb->interval; + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) + bus->bandwidth_isoc_reqs--; + else + bus->bandwidth_int_reqs--; +} + +/* + * Sets the final status of an URB and returns it to the upper layer. Any + * required cleanup of the URB is performed. + * + * Must be called with interrupt disabled and spinlock held + */ +void dwc2_host_complete(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd, + int status) +{ + struct urb *urb; + int i; + + if (!qtd) { + dev_dbg(hsotg->dev, "## %s: qtd is NULL ##\n", __func__); + return; + } + + if (!qtd->urb) { + dev_dbg(hsotg->dev, "## %s: qtd->urb is NULL ##\n", __func__); + return; + } + + urb = qtd->urb->priv; + if (!urb) { + dev_dbg(hsotg->dev, "## %s: urb->priv is NULL ##\n", __func__); + return; + } + + urb->actual_length = dwc2_hcd_urb_get_actual_length(qtd->urb); + + if (dbg_urb(urb)) + dev_vdbg(hsotg->dev, + "%s: urb %p device %d ep %d-%s status %d actual %d\n", + __func__, urb, usb_pipedevice(urb->pipe), + usb_pipeendpoint(urb->pipe), + usb_pipein(urb->pipe) ? "IN" : "OUT", status, + urb->actual_length); + + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS && dbg_perio()) { + for (i = 0; i < urb->number_of_packets; i++) + dev_vdbg(hsotg->dev, " ISO Desc %d status %d\n", + i, urb->iso_frame_desc[i].status); + } + + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { + urb->error_count = dwc2_hcd_urb_get_error_count(qtd->urb); + for (i = 0; i < urb->number_of_packets; ++i) { + urb->iso_frame_desc[i].actual_length = + dwc2_hcd_urb_get_iso_desc_actual_length( + qtd->urb, i); + urb->iso_frame_desc[i].status = + dwc2_hcd_urb_get_iso_desc_status(qtd->urb, i); + } + } + + urb->status = status; + if (!status) { + if ((urb->transfer_flags & URB_SHORT_NOT_OK) && + urb->actual_length < urb->transfer_buffer_length) + urb->status = -EREMOTEIO; + } + + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS || + usb_pipetype(urb->pipe) == PIPE_INTERRUPT) { + struct usb_host_endpoint *ep = urb->ep; + + if (ep) + dwc2_free_bus_bandwidth(dwc2_hsotg_to_hcd(hsotg), + dwc2_hcd_get_ep_bandwidth(hsotg, ep), + urb); + } + + usb_hcd_unlink_urb_from_ep(dwc2_hsotg_to_hcd(hsotg), urb); + urb->hcpriv = NULL; + kfree(qtd->urb); + qtd->urb = NULL; + + spin_unlock(&hsotg->lock); + usb_hcd_giveback_urb(dwc2_hsotg_to_hcd(hsotg), urb, status); + spin_lock(&hsotg->lock); +} + +/* + * Work queue function for starting the HCD when A-Cable is connected + */ +static void dwc2_hcd_start_func(struct work_struct *work) +{ + struct dwc2_hsotg *hsotg = container_of(work, struct dwc2_hsotg, + start_work.work); + + dev_dbg(hsotg->dev, "%s() %p\n", __func__, hsotg); + dwc2_host_start(hsotg); +} + +/* + * Reset work queue function + */ +static void dwc2_hcd_reset_func(struct work_struct *work) +{ + struct dwc2_hsotg *hsotg = container_of(work, struct dwc2_hsotg, + reset_work.work); + u32 hprt0; + + dev_dbg(hsotg->dev, "USB RESET function called\n"); + hprt0 = dwc2_read_hprt0(hsotg); + hprt0 &= ~HPRT0_RST; + writel(hprt0, hsotg->regs + HPRT0); + hsotg->flags.b.port_reset_change = 1; +} + +/* + * ========================================================================= + * Linux HC Driver Functions + * ========================================================================= + */ + +/* + * Initializes the DWC_otg controller and its root hub and prepares it for host + * mode operation. Activates the root port. Returns 0 on success and a negative + * error code on failure. + */ +static int _dwc2_hcd_start(struct usb_hcd *hcd) +{ + struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); + struct usb_bus *bus = hcd_to_bus(hcd); + unsigned long flags; + + dev_dbg(hsotg->dev, "DWC OTG HCD START\n"); + + spin_lock_irqsave(&hsotg->lock, flags); + + hcd->state = HC_STATE_RUNNING; + + if (dwc2_is_device_mode(hsotg)) { + spin_unlock_irqrestore(&hsotg->lock, flags); + return 0; /* why 0 ?? */ + } + + dwc2_hcd_reinit(hsotg); + + /* Initialize and connect root hub if one is not already attached */ + if (bus->root_hub) { + dev_dbg(hsotg->dev, "DWC OTG HCD Has Root Hub\n"); + /* Inform the HUB driver to resume */ + usb_hcd_resume_root_hub(hcd); + } + + spin_unlock_irqrestore(&hsotg->lock, flags); + return 0; +} + +/* + * Halts the DWC_otg host mode operations in a clean manner. USB transfers are + * stopped. + */ +static void _dwc2_hcd_stop(struct usb_hcd *hcd) +{ + struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); + unsigned long flags; + + spin_lock_irqsave(&hsotg->lock, flags); + dwc2_hcd_stop(hsotg); + spin_unlock_irqrestore(&hsotg->lock, flags); + + usleep_range(1000, 3000); +} + +/* Returns the current frame number */ +static int _dwc2_hcd_get_frame_number(struct usb_hcd *hcd) +{ + struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); + + return dwc2_hcd_get_frame_number(hsotg); +} + +static void dwc2_dump_urb_info(struct usb_hcd *hcd, struct urb *urb, + char *fn_name) +{ +#ifdef VERBOSE_DEBUG + struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); + char *pipetype; + char *speed; + + dev_vdbg(hsotg->dev, "%s, urb %p\n", fn_name, urb); + dev_vdbg(hsotg->dev, " Device address: %d\n", + usb_pipedevice(urb->pipe)); + dev_vdbg(hsotg->dev, " Endpoint: %d, %s\n", + usb_pipeendpoint(urb->pipe), + usb_pipein(urb->pipe) ? "IN" : "OUT"); + + switch (usb_pipetype(urb->pipe)) { + case PIPE_CONTROL: + pipetype = "CONTROL"; + break; + case PIPE_BULK: + pipetype = "BULK"; + break; + case PIPE_INTERRUPT: + pipetype = "INTERRUPT"; + break; + case PIPE_ISOCHRONOUS: + pipetype = "ISOCHRONOUS"; + break; + default: + pipetype = "UNKNOWN"; + break; + } + + dev_vdbg(hsotg->dev, " Endpoint type: %s %s (%s)\n", pipetype, + usb_urb_dir_in(urb) ? "IN" : "OUT", usb_pipein(urb->pipe) ? + "IN" : "OUT"); + + switch (urb->dev->speed) { + case USB_SPEED_HIGH: + speed = "HIGH"; + break; + case USB_SPEED_FULL: + speed = "FULL"; + break; + case USB_SPEED_LOW: + speed = "LOW"; + break; + default: + speed = "UNKNOWN"; + break; + } + + dev_vdbg(hsotg->dev, " Speed: %s\n", speed); + dev_vdbg(hsotg->dev, " Max packet size: %d\n", + usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))); + dev_vdbg(hsotg->dev, " Data buffer length: %d\n", + urb->transfer_buffer_length); + dev_vdbg(hsotg->dev, " Transfer buffer: %p, Transfer DMA: %08lx\n", + urb->transfer_buffer, (unsigned long)urb->transfer_dma); + dev_vdbg(hsotg->dev, " Setup buffer: %p, Setup DMA: %08lx\n", + urb->setup_packet, (unsigned long)urb->setup_dma); + dev_vdbg(hsotg->dev, " Interval: %d\n", urb->interval); + + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { + int i; + + for (i = 0; i < urb->number_of_packets; i++) { + dev_vdbg(hsotg->dev, " ISO Desc %d:\n", i); + dev_vdbg(hsotg->dev, " offset: %d, length %d\n", + urb->iso_frame_desc[i].offset, + urb->iso_frame_desc[i].length); + } + } +#endif +} + +/* + * Starts processing a USB transfer request specified by a USB Request Block + * (URB). mem_flags indicates the type of memory allocation to use while + * processing this URB. + */ +static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags) +{ + struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); + struct usb_host_endpoint *ep = urb->ep; + struct dwc2_hcd_urb *dwc2_urb; + int i; + int retval; + int alloc_bandwidth = 0; + u8 ep_type = 0; + u32 tflags = 0; + void *buf; + unsigned long flags; + + if (dbg_urb(urb)) { + dev_vdbg(hsotg->dev, "DWC OTG HCD URB Enqueue\n"); + dwc2_dump_urb_info(hcd, urb, "urb_enqueue"); + } + + if (ep == NULL) + return -EINVAL; + + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS || + usb_pipetype(urb->pipe) == PIPE_INTERRUPT) { + spin_lock_irqsave(&hsotg->lock, flags); + if (!dwc2_hcd_is_bandwidth_allocated(hsotg, ep)) + alloc_bandwidth = 1; + spin_unlock_irqrestore(&hsotg->lock, flags); + } + + switch (usb_pipetype(urb->pipe)) { + case PIPE_CONTROL: + ep_type = USB_ENDPOINT_XFER_CONTROL; + break; + case PIPE_ISOCHRONOUS: + ep_type = USB_ENDPOINT_XFER_ISOC; + break; + case PIPE_BULK: + ep_type = USB_ENDPOINT_XFER_BULK; + break; + case PIPE_INTERRUPT: + ep_type = USB_ENDPOINT_XFER_INT; + break; + default: + dev_warn(hsotg->dev, "Wrong ep type\n"); + } + + dwc2_urb = dwc2_hcd_urb_alloc(hsotg, urb->number_of_packets, + mem_flags); + if (!dwc2_urb) + return -ENOMEM; + + dwc2_hcd_urb_set_pipeinfo(hsotg, dwc2_urb, usb_pipedevice(urb->pipe), + usb_pipeendpoint(urb->pipe), ep_type, + usb_pipein(urb->pipe), + usb_maxpacket(urb->dev, urb->pipe, + !(usb_pipein(urb->pipe)))); + + buf = urb->transfer_buffer; + + if (hcd->self.uses_dma) { + if (!buf && (urb->transfer_dma & 3)) { + dev_err(hsotg->dev, + "%s: unaligned transfer with no transfer_buffer", + __func__); + retval = -EINVAL; + goto fail1; + } + } + + if (!(urb->transfer_flags & URB_NO_INTERRUPT)) + tflags |= URB_GIVEBACK_ASAP; + if (urb->transfer_flags & URB_ZERO_PACKET) + tflags |= URB_SEND_ZERO_PACKET; + + dwc2_urb->priv = urb; + dwc2_urb->buf = buf; + dwc2_urb->dma = urb->transfer_dma; + dwc2_urb->length = urb->transfer_buffer_length; + dwc2_urb->setup_packet = urb->setup_packet; + dwc2_urb->setup_dma = urb->setup_dma; + dwc2_urb->flags = tflags; + dwc2_urb->interval = urb->interval; + dwc2_urb->status = -EINPROGRESS; + + for (i = 0; i < urb->number_of_packets; ++i) + dwc2_hcd_urb_set_iso_desc_params(dwc2_urb, i, + urb->iso_frame_desc[i].offset, + urb->iso_frame_desc[i].length); + + urb->hcpriv = dwc2_urb; + + spin_lock_irqsave(&hsotg->lock, flags); + retval = usb_hcd_link_urb_to_ep(hcd, urb); + spin_unlock_irqrestore(&hsotg->lock, flags); + if (retval) + goto fail1; + + retval = dwc2_hcd_urb_enqueue(hsotg, dwc2_urb, &ep->hcpriv, mem_flags); + if (retval) + goto fail2; + + if (alloc_bandwidth) { + spin_lock_irqsave(&hsotg->lock, flags); + dwc2_allocate_bus_bandwidth(hcd, + dwc2_hcd_get_ep_bandwidth(hsotg, ep), + urb); + spin_unlock_irqrestore(&hsotg->lock, flags); + } + + return 0; + +fail2: + spin_lock_irqsave(&hsotg->lock, flags); + dwc2_urb->priv = NULL; + usb_hcd_unlink_urb_from_ep(hcd, urb); + spin_unlock_irqrestore(&hsotg->lock, flags); +fail1: + urb->hcpriv = NULL; + kfree(dwc2_urb); + + return retval; +} + +/* + * Aborts/cancels a USB transfer request. Always returns 0 to indicate success. + */ +static int _dwc2_hcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, + int status) +{ + struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); + int rc; + unsigned long flags; + + dev_dbg(hsotg->dev, "DWC OTG HCD URB Dequeue\n"); + dwc2_dump_urb_info(hcd, urb, "urb_dequeue"); + + spin_lock_irqsave(&hsotg->lock, flags); + + rc = usb_hcd_check_unlink_urb(hcd, urb, status); + if (rc) + goto out; + + if (!urb->hcpriv) { + dev_dbg(hsotg->dev, "## urb->hcpriv is NULL ##\n"); + goto out; + } + + rc = dwc2_hcd_urb_dequeue(hsotg, urb->hcpriv); + + usb_hcd_unlink_urb_from_ep(hcd, urb); + + kfree(urb->hcpriv); + urb->hcpriv = NULL; + + /* Higher layer software sets URB status */ + spin_unlock(&hsotg->lock); + usb_hcd_giveback_urb(hcd, urb, status); + spin_lock(&hsotg->lock); + + dev_dbg(hsotg->dev, "Called usb_hcd_giveback_urb()\n"); + dev_dbg(hsotg->dev, " urb->status = %d\n", urb->status); +out: + spin_unlock_irqrestore(&hsotg->lock, flags); + + return rc; +} + +/* + * Frees resources in the DWC_otg controller related to a given endpoint. Also + * clears state in the HCD related to the endpoint. Any URBs for the endpoint + * must already be dequeued. + */ +static void _dwc2_hcd_endpoint_disable(struct usb_hcd *hcd, + struct usb_host_endpoint *ep) +{ + struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); + + dev_dbg(hsotg->dev, + "DWC OTG HCD EP DISABLE: bEndpointAddress=0x%02x, ep->hcpriv=%p\n", + ep->desc.bEndpointAddress, ep->hcpriv); + dwc2_hcd_endpoint_disable(hsotg, ep, 250); +} + +/* + * Resets endpoint specific parameter values, in current version used to reset + * the data toggle (as a WA). This function can be called from usb_clear_halt + * routine. + */ +static void _dwc2_hcd_endpoint_reset(struct usb_hcd *hcd, + struct usb_host_endpoint *ep) +{ + struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); + unsigned long flags; + + dev_dbg(hsotg->dev, + "DWC OTG HCD EP RESET: bEndpointAddress=0x%02x\n", + ep->desc.bEndpointAddress); + + spin_lock_irqsave(&hsotg->lock, flags); + dwc2_hcd_endpoint_reset(hsotg, ep); + spin_unlock_irqrestore(&hsotg->lock, flags); +} + +/* + * Handles host mode interrupts for the DWC_otg controller. Returns IRQ_NONE if + * there was no interrupt to handle. Returns IRQ_HANDLED if there was a valid + * interrupt. + * + * This function is called by the USB core when an interrupt occurs + */ +static irqreturn_t _dwc2_hcd_irq(struct usb_hcd *hcd) +{ + struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); + + return dwc2_handle_hcd_intr(hsotg); +} + +/* + * Creates Status Change bitmap for the root hub and root port. The bitmap is + * returned in buf. Bit 0 is the status change indicator for the root hub. Bit 1 + * is the status change indicator for the single root port. Returns 1 if either + * change indicator is 1, otherwise returns 0. + */ +static int _dwc2_hcd_hub_status_data(struct usb_hcd *hcd, char *buf) +{ + struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); + + buf[0] = dwc2_hcd_is_status_changed(hsotg, 1) << 1; + return buf[0] != 0; +} + +/* Handles hub class-specific requests */ +static int _dwc2_hcd_hub_control(struct usb_hcd *hcd, u16 typereq, u16 wvalue, + u16 windex, char *buf, u16 wlength) +{ + int retval = dwc2_hcd_hub_control(dwc2_hcd_to_hsotg(hcd), typereq, + wvalue, windex, buf, wlength); + return retval; +} + +/* Handles hub TT buffer clear completions */ +static void _dwc2_hcd_clear_tt_buffer_complete(struct usb_hcd *hcd, + struct usb_host_endpoint *ep) +{ + struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); + struct dwc2_qh *qh; + unsigned long flags; + + qh = ep->hcpriv; + if (!qh) + return; + + spin_lock_irqsave(&hsotg->lock, flags); + qh->tt_buffer_dirty = 0; + + if (hsotg->flags.b.port_connect_status) + dwc2_hcd_queue_transactions(hsotg, DWC2_TRANSACTION_ALL); + + spin_unlock_irqrestore(&hsotg->lock, flags); +} + +static struct hc_driver dwc2_hc_driver = { + .description = "dwc2_hsotg", + .product_desc = "DWC OTG Controller", + .hcd_priv_size = sizeof(struct wrapper_priv_data), + + .irq = _dwc2_hcd_irq, + .flags = HCD_MEMORY | HCD_USB2, + + .start = _dwc2_hcd_start, + .stop = _dwc2_hcd_stop, + .urb_enqueue = _dwc2_hcd_urb_enqueue, + .urb_dequeue = _dwc2_hcd_urb_dequeue, + .endpoint_disable = _dwc2_hcd_endpoint_disable, + .endpoint_reset = _dwc2_hcd_endpoint_reset, + .get_frame_number = _dwc2_hcd_get_frame_number, + + .hub_status_data = _dwc2_hcd_hub_status_data, + .hub_control = _dwc2_hcd_hub_control, + .clear_tt_buffer_complete = _dwc2_hcd_clear_tt_buffer_complete, +}; + +/* + * Frees secondary storage associated with the dwc2_hsotg structure contained + * in the struct usb_hcd field + */ +static void dwc2_hcd_free(struct dwc2_hsotg *hsotg) +{ + u32 ahbcfg; + u32 dctl; + int i; + + dev_dbg(hsotg->dev, "DWC OTG HCD FREE\n"); + + /* Free memory for QH/QTD lists */ + dwc2_qh_list_free(hsotg, &hsotg->non_periodic_sched_inactive); + dwc2_qh_list_free(hsotg, &hsotg->non_periodic_sched_active); + dwc2_qh_list_free(hsotg, &hsotg->periodic_sched_inactive); + dwc2_qh_list_free(hsotg, &hsotg->periodic_sched_ready); + dwc2_qh_list_free(hsotg, &hsotg->periodic_sched_assigned); + dwc2_qh_list_free(hsotg, &hsotg->periodic_sched_queued); + + /* Free memory for the host channels */ + for (i = 0; i < MAX_EPS_CHANNELS; i++) { + struct dwc2_host_chan *chan = hsotg->hc_ptr_array[i]; + + if (chan != NULL) { + dev_dbg(hsotg->dev, "HCD Free channel #%i, chan=%p\n", + i, chan); + hsotg->hc_ptr_array[i] = NULL; + kfree(chan); + } + } + + if (hsotg->core_params->dma_enable > 0) { + if (hsotg->status_buf) { + dma_free_coherent(hsotg->dev, DWC2_HCD_STATUS_BUF_SIZE, + hsotg->status_buf, + hsotg->status_buf_dma); + hsotg->status_buf = NULL; + } + } else { + kfree(hsotg->status_buf); + hsotg->status_buf = NULL; + } + + ahbcfg = readl(hsotg->regs + GAHBCFG); + + /* Disable all interrupts */ + ahbcfg &= ~GAHBCFG_GLBL_INTR_EN; + writel(ahbcfg, hsotg->regs + GAHBCFG); + writel(0, hsotg->regs + GINTMSK); + + if (hsotg->hw_params.snpsid >= DWC2_CORE_REV_3_00a) { + dctl = readl(hsotg->regs + DCTL); + dctl |= DCTL_SFTDISCON; + writel(dctl, hsotg->regs + DCTL); + } + + if (hsotg->wq_otg) { + if (!cancel_work_sync(&hsotg->wf_otg)) + flush_workqueue(hsotg->wq_otg); + destroy_workqueue(hsotg->wq_otg); + } + + kfree(hsotg->core_params); + hsotg->core_params = NULL; + del_timer(&hsotg->wkp_timer); +} + +static void dwc2_hcd_release(struct dwc2_hsotg *hsotg) +{ + /* Turn off all host-specific interrupts */ + dwc2_disable_host_interrupts(hsotg); + + dwc2_hcd_free(hsotg); +} + +/* + * Sets all parameters to the given value. + * + * Assumes that the dwc2_core_params struct contains only integers. + */ +void dwc2_set_all_params(struct dwc2_core_params *params, int value) +{ + int *p = (int *)params; + size_t size = sizeof(*params) / sizeof(*p); + int i; + + for (i = 0; i < size; i++) + p[i] = value; +} +EXPORT_SYMBOL_GPL(dwc2_set_all_params); + +/* + * Initializes the HCD. This function allocates memory for and initializes the + * static parts of the usb_hcd and dwc2_hsotg structures. It also registers the + * USB bus with the core and calls the hc_driver->start() function. It returns + * a negative error on failure. + */ +int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq, + const struct dwc2_core_params *params) +{ + struct usb_hcd *hcd; + struct dwc2_host_chan *channel; + u32 hcfg; + int i, num_channels; + int retval; + + dev_dbg(hsotg->dev, "DWC OTG HCD INIT\n"); + + /* Detect config values from hardware */ + retval = dwc2_get_hwparams(hsotg); + + if (retval) + return retval; + + retval = -ENOMEM; + + hcfg = readl(hsotg->regs + HCFG); + dev_dbg(hsotg->dev, "hcfg=%08x\n", hcfg); + +#ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS + hsotg->frame_num_array = kzalloc(sizeof(*hsotg->frame_num_array) * + FRAME_NUM_ARRAY_SIZE, GFP_KERNEL); + if (!hsotg->frame_num_array) + goto error1; + hsotg->last_frame_num_array = kzalloc( + sizeof(*hsotg->last_frame_num_array) * + FRAME_NUM_ARRAY_SIZE, GFP_KERNEL); + if (!hsotg->last_frame_num_array) + goto error1; + hsotg->last_frame_num = HFNUM_MAX_FRNUM; +#endif + + hsotg->core_params = kzalloc(sizeof(*hsotg->core_params), GFP_KERNEL); + if (!hsotg->core_params) + goto error1; + + dwc2_set_all_params(hsotg->core_params, -1); + + /* Validate parameter values */ + dwc2_set_parameters(hsotg, params); + + /* Check if the bus driver or platform code has setup a dma_mask */ + if (hsotg->core_params->dma_enable > 0 && + hsotg->dev->dma_mask == NULL) { + dev_warn(hsotg->dev, + "dma_mask not set, disabling DMA\n"); + hsotg->core_params->dma_enable = 0; + hsotg->core_params->dma_desc_enable = 0; + } + + /* Set device flags indicating whether the HCD supports DMA */ + if (hsotg->core_params->dma_enable > 0) { + if (dma_set_mask(hsotg->dev, DMA_BIT_MASK(32)) < 0) + dev_warn(hsotg->dev, "can't set DMA mask\n"); + if (dma_set_coherent_mask(hsotg->dev, DMA_BIT_MASK(32)) < 0) + dev_warn(hsotg->dev, "can't set coherent DMA mask\n"); + } + + hcd = usb_create_hcd(&dwc2_hc_driver, hsotg->dev, dev_name(hsotg->dev)); + if (!hcd) + goto error1; + + if (hsotg->core_params->dma_enable <= 0) + hcd->self.uses_dma = 0; + + hcd->has_tt = 1; + + spin_lock_init(&hsotg->lock); + ((struct wrapper_priv_data *) &hcd->hcd_priv)->hsotg = hsotg; + hsotg->priv = hcd; + + /* + * Disable the global interrupt until all the interrupt handlers are + * installed + */ + dwc2_disable_global_interrupts(hsotg); + + /* Initialize the DWC_otg core, and select the Phy type */ + retval = dwc2_core_init(hsotg, true, irq); + if (retval) + goto error2; + + /* Create new workqueue and init work */ + retval = -ENOMEM; + hsotg->wq_otg = create_singlethread_workqueue("dwc2"); + if (!hsotg->wq_otg) { + dev_err(hsotg->dev, "Failed to create workqueue\n"); + goto error2; + } + INIT_WORK(&hsotg->wf_otg, dwc2_conn_id_status_change); + + setup_timer(&hsotg->wkp_timer, dwc2_wakeup_detected, + (unsigned long)hsotg); + + /* Initialize the non-periodic schedule */ + INIT_LIST_HEAD(&hsotg->non_periodic_sched_inactive); + INIT_LIST_HEAD(&hsotg->non_periodic_sched_active); + + /* Initialize the periodic schedule */ + INIT_LIST_HEAD(&hsotg->periodic_sched_inactive); + INIT_LIST_HEAD(&hsotg->periodic_sched_ready); + INIT_LIST_HEAD(&hsotg->periodic_sched_assigned); + INIT_LIST_HEAD(&hsotg->periodic_sched_queued); + + /* + * Create a host channel descriptor for each host channel implemented + * in the controller. Initialize the channel descriptor array. + */ + INIT_LIST_HEAD(&hsotg->free_hc_list); + num_channels = hsotg->core_params->host_channels; + memset(&hsotg->hc_ptr_array[0], 0, sizeof(hsotg->hc_ptr_array)); + + for (i = 0; i < num_channels; i++) { + channel = kzalloc(sizeof(*channel), GFP_KERNEL); + if (channel == NULL) + goto error3; + channel->hc_num = i; + hsotg->hc_ptr_array[i] = channel; + } + + if (hsotg->core_params->uframe_sched > 0) + dwc2_hcd_init_usecs(hsotg); + + /* Initialize hsotg start work */ + INIT_DELAYED_WORK(&hsotg->start_work, dwc2_hcd_start_func); + + /* Initialize port reset work */ + INIT_DELAYED_WORK(&hsotg->reset_work, dwc2_hcd_reset_func); + + /* + * Allocate space for storing data on status transactions. Normally no + * data is sent, but this space acts as a bit bucket. This must be + * done after usb_add_hcd since that function allocates the DMA buffer + * pool. + */ + if (hsotg->core_params->dma_enable > 0) + hsotg->status_buf = dma_alloc_coherent(hsotg->dev, + DWC2_HCD_STATUS_BUF_SIZE, + &hsotg->status_buf_dma, GFP_KERNEL); + else + hsotg->status_buf = kzalloc(DWC2_HCD_STATUS_BUF_SIZE, + GFP_KERNEL); + + if (!hsotg->status_buf) + goto error3; + + hsotg->otg_port = 1; + hsotg->frame_list = NULL; + hsotg->frame_list_dma = 0; + hsotg->periodic_qh_count = 0; + + /* Initiate lx_state to L3 disconnected state */ + hsotg->lx_state = DWC2_L3; + + hcd->self.otg_port = hsotg->otg_port; + + /* Don't support SG list at this point */ + hcd->self.sg_tablesize = 0; + + /* + * Finish generic HCD initialization and start the HCD. This function + * allocates the DMA buffer pool, registers the USB bus, requests the + * IRQ line, and calls hcd_start method. + */ + retval = usb_add_hcd(hcd, irq, IRQF_SHARED); + if (retval < 0) + goto error3; + + device_wakeup_enable(hcd->self.controller); + + dwc2_hcd_dump_state(hsotg); + + dwc2_enable_global_interrupts(hsotg); + + return 0; + +error3: + dwc2_hcd_release(hsotg); +error2: + usb_put_hcd(hcd); +error1: + kfree(hsotg->core_params); + +#ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS + kfree(hsotg->last_frame_num_array); + kfree(hsotg->frame_num_array); +#endif + + dev_err(hsotg->dev, "%s() FAILED, returning %d\n", __func__, retval); + return retval; +} +EXPORT_SYMBOL_GPL(dwc2_hcd_init); + +/* + * Removes the HCD. + * Frees memory and resources associated with the HCD and deregisters the bus. + */ +void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) +{ + struct usb_hcd *hcd; + + dev_dbg(hsotg->dev, "DWC OTG HCD REMOVE\n"); + + hcd = dwc2_hsotg_to_hcd(hsotg); + dev_dbg(hsotg->dev, "hsotg->hcd = %p\n", hcd); + + if (!hcd) { + dev_dbg(hsotg->dev, "%s: dwc2_hsotg_to_hcd(hsotg) NULL!\n", + __func__); + return; + } + + usb_remove_hcd(hcd); + hsotg->priv = NULL; + dwc2_hcd_release(hsotg); + usb_put_hcd(hcd); + +#ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS + kfree(hsotg->last_frame_num_array); + kfree(hsotg->frame_num_array); +#endif +} +EXPORT_SYMBOL_GPL(dwc2_hcd_remove); diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h new file mode 100644 index 0000000..fdc6d48 --- /dev/null +++ b/drivers/usb/dwc2/hcd.h @@ -0,0 +1,769 @@ +/* + * hcd.h - DesignWare HS OTG Controller host-mode declarations + * + * Copyright (C) 2004-2013 Synopsys, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __DWC2_HCD_H__ +#define __DWC2_HCD_H__ + +/* + * This file contains the structures, constants, and interfaces for the + * Host Contoller Driver (HCD) + * + * The Host Controller Driver (HCD) is responsible for translating requests + * from the USB Driver into the appropriate actions on the DWC_otg controller. + * It isolates the USBD from the specifics of the controller by providing an + * API to the USBD. + */ + +struct dwc2_qh; + +/** + * struct dwc2_host_chan - Software host channel descriptor + * + * @hc_num: Host channel number, used for register address lookup + * @dev_addr: Address of the device + * @ep_num: Endpoint of the device + * @ep_is_in: Endpoint direction + * @speed: Device speed. One of the following values: + * - USB_SPEED_LOW + * - USB_SPEED_FULL + * - USB_SPEED_HIGH + * @ep_type: Endpoint type. One of the following values: + * - USB_ENDPOINT_XFER_CONTROL: 0 + * - USB_ENDPOINT_XFER_ISOC: 1 + * - USB_ENDPOINT_XFER_BULK: 2 + * - USB_ENDPOINT_XFER_INTR: 3 + * @max_packet: Max packet size in bytes + * @data_pid_start: PID for initial transaction. + * 0: DATA0 + * 1: DATA2 + * 2: DATA1 + * 3: MDATA (non-Control EP), + * SETUP (Control EP) + * @multi_count: Number of additional periodic transactions per + * (micro)frame + * @xfer_buf: Pointer to current transfer buffer position + * @xfer_dma: DMA address of xfer_buf + * @align_buf: In Buffer DMA mode this will be used if xfer_buf is not + * DWORD aligned + * @xfer_len: Total number of bytes to transfer + * @xfer_count: Number of bytes transferred so far + * @start_pkt_count: Packet count at start of transfer + * @xfer_started: True if the transfer has been started + * @ping: True if a PING request should be issued on this channel + * @error_state: True if the error count for this transaction is non-zero + * @halt_on_queue: True if this channel should be halted the next time a + * request is queued for the channel. This is necessary in + * slave mode if no request queue space is available when + * an attempt is made to halt the channel. + * @halt_pending: True if the host channel has been halted, but the core + * is not finished flushing queued requests + * @do_split: Enable split for the channel + * @complete_split: Enable complete split + * @hub_addr: Address of high speed hub for the split + * @hub_port: Port of the low/full speed device for the split + * @xact_pos: Split transaction position. One of the following values: + * - DWC2_HCSPLT_XACTPOS_MID + * - DWC2_HCSPLT_XACTPOS_BEGIN + * - DWC2_HCSPLT_XACTPOS_END + * - DWC2_HCSPLT_XACTPOS_ALL + * @requests: Number of requests issued for this channel since it was + * assigned to the current transfer (not counting PINGs) + * @schinfo: Scheduling micro-frame bitmap + * @ntd: Number of transfer descriptors for the transfer + * @halt_status: Reason for halting the host channel + * @hcint Contents of the HCINT register when the interrupt came + * @qh: QH for the transfer being processed by this channel + * @hc_list_entry: For linking to list of host channels + * @desc_list_addr: Current QH's descriptor list DMA address + * + * This structure represents the state of a single host channel when acting in + * host mode. It contains the data items needed to transfer packets to an + * endpoint via a host channel. + */ +struct dwc2_host_chan { + u8 hc_num; + + unsigned dev_addr:7; + unsigned ep_num:4; + unsigned ep_is_in:1; + unsigned speed:4; + unsigned ep_type:2; + unsigned max_packet:11; + unsigned data_pid_start:2; +#define DWC2_HC_PID_DATA0 TSIZ_SC_MC_PID_DATA0 +#define DWC2_HC_PID_DATA2 TSIZ_SC_MC_PID_DATA2 +#define DWC2_HC_PID_DATA1 TSIZ_SC_MC_PID_DATA1 +#define DWC2_HC_PID_MDATA TSIZ_SC_MC_PID_MDATA +#define DWC2_HC_PID_SETUP TSIZ_SC_MC_PID_SETUP + + unsigned multi_count:2; + + u8 *xfer_buf; + dma_addr_t xfer_dma; + dma_addr_t align_buf; + u32 xfer_len; + u32 xfer_count; + u16 start_pkt_count; + u8 xfer_started; + u8 do_ping; + u8 error_state; + u8 halt_on_queue; + u8 halt_pending; + u8 do_split; + u8 complete_split; + u8 hub_addr; + u8 hub_port; + u8 xact_pos; +#define DWC2_HCSPLT_XACTPOS_MID HCSPLT_XACTPOS_MID +#define DWC2_HCSPLT_XACTPOS_END HCSPLT_XACTPOS_END +#define DWC2_HCSPLT_XACTPOS_BEGIN HCSPLT_XACTPOS_BEGIN +#define DWC2_HCSPLT_XACTPOS_ALL HCSPLT_XACTPOS_ALL + + u8 requests; + u8 schinfo; + u16 ntd; + enum dwc2_halt_status halt_status; + u32 hcint; + struct dwc2_qh *qh; + struct list_head hc_list_entry; + dma_addr_t desc_list_addr; +}; + +struct dwc2_hcd_pipe_info { + u8 dev_addr; + u8 ep_num; + u8 pipe_type; + u8 pipe_dir; + u16 mps; +}; + +struct dwc2_hcd_iso_packet_desc { + u32 offset; + u32 length; + u32 actual_length; + u32 status; +}; + +struct dwc2_qtd; + +struct dwc2_hcd_urb { + void *priv; + struct dwc2_qtd *qtd; + void *buf; + dma_addr_t dma; + void *setup_packet; + dma_addr_t setup_dma; + u32 length; + u32 actual_length; + u32 status; + u32 error_count; + u32 packet_count; + u32 flags; + u16 interval; + struct dwc2_hcd_pipe_info pipe_info; + struct dwc2_hcd_iso_packet_desc iso_descs[0]; +}; + +/* Phases for control transfers */ +enum dwc2_control_phase { + DWC2_CONTROL_SETUP, + DWC2_CONTROL_DATA, + DWC2_CONTROL_STATUS, +}; + +/* Transaction types */ +enum dwc2_transaction_type { + DWC2_TRANSACTION_NONE, + DWC2_TRANSACTION_PERIODIC, + DWC2_TRANSACTION_NON_PERIODIC, + DWC2_TRANSACTION_ALL, +}; + +/** + * struct dwc2_qh - Software queue head structure + * + * @ep_type: Endpoint type. One of the following values: + * - USB_ENDPOINT_XFER_CONTROL + * - USB_ENDPOINT_XFER_BULK + * - USB_ENDPOINT_XFER_INT + * - USB_ENDPOINT_XFER_ISOC + * @ep_is_in: Endpoint direction + * @maxp: Value from wMaxPacketSize field of Endpoint Descriptor + * @dev_speed: Device speed. One of the following values: + * - USB_SPEED_LOW + * - USB_SPEED_FULL + * - USB_SPEED_HIGH + * @data_toggle: Determines the PID of the next data packet for + * non-controltransfers. Ignored for control transfers. + * One of the following values: + * - DWC2_HC_PID_DATA0 + * - DWC2_HC_PID_DATA1 + * @ping_state: Ping state + * @do_split: Full/low speed endpoint on high-speed hub requires split + * @td_first: Index of first activated isochronous transfer descriptor + * @td_last: Index of last activated isochronous transfer descriptor + * @usecs: Bandwidth in microseconds per (micro)frame + * @interval: Interval between transfers in (micro)frames + * @sched_frame: (Micro)frame to initialize a periodic transfer. + * The transfer executes in the following (micro)frame. + * @frame_usecs: Internal variable used by the microframe scheduler + * @start_split_frame: (Micro)frame at which last start split was initialized + * @ntd: Actual number of transfer descriptors in a list + * @dw_align_buf: Used instead of original buffer if its physical address + * is not dword-aligned + * @dw_align_buf_dma: DMA address for align_buf + * @qtd_list: List of QTDs for this QH + * @channel: Host channel currently processing transfers for this QH + * @qh_list_entry: Entry for QH in either the periodic or non-periodic + * schedule + * @desc_list: List of transfer descriptors + * @desc_list_dma: Physical address of desc_list + * @n_bytes: Xfer Bytes array. Each element corresponds to a transfer + * descriptor and indicates original XferSize value for the + * descriptor + * @tt_buffer_dirty True if clear_tt_buffer_complete is pending + * + * A Queue Head (QH) holds the static characteristics of an endpoint and + * maintains a list of transfers (QTDs) for that endpoint. A QH structure may + * be entered in either the non-periodic or periodic schedule. + */ +struct dwc2_qh { + u8 ep_type; + u8 ep_is_in; + u16 maxp; + u8 dev_speed; + u8 data_toggle; + u8 ping_state; + u8 do_split; + u8 td_first; + u8 td_last; + u16 usecs; + u16 interval; + u16 sched_frame; + u16 frame_usecs[8]; + u16 start_split_frame; + u16 ntd; + u8 *dw_align_buf; + dma_addr_t dw_align_buf_dma; + struct list_head qtd_list; + struct dwc2_host_chan *channel; + struct list_head qh_list_entry; + struct dwc2_hcd_dma_desc *desc_list; + dma_addr_t desc_list_dma; + u32 *n_bytes; + unsigned tt_buffer_dirty:1; +}; + +/** + * struct dwc2_qtd - Software queue transfer descriptor (QTD) + * + * @control_phase: Current phase for control transfers (Setup, Data, or + * Status) + * @in_process: Indicates if this QTD is currently processed by HW + * @data_toggle: Determines the PID of the next data packet for the + * data phase of control transfers. Ignored for other + * transfer types. One of the following values: + * - DWC2_HC_PID_DATA0 + * - DWC2_HC_PID_DATA1 + * @complete_split: Keeps track of the current split type for FS/LS + * endpoints on a HS Hub + * @isoc_split_pos: Position of the ISOC split in full/low speed + * @isoc_frame_index: Index of the next frame descriptor for an isochronous + * transfer. A frame descriptor describes the buffer + * position and length of the data to be transferred in the + * next scheduled (micro)frame of an isochronous transfer. + * It also holds status for that transaction. The frame + * index starts at 0. + * @isoc_split_offset: Position of the ISOC split in the buffer for the + * current frame + * @ssplit_out_xfer_count: How many bytes transferred during SSPLIT OUT + * @error_count: Holds the number of bus errors that have occurred for + * a transaction within this transfer + * @n_desc: Number of DMA descriptors for this QTD + * @isoc_frame_index_last: Last activated frame (packet) index, used in + * descriptor DMA mode only + * @urb: URB for this transfer + * @qh: Queue head for this QTD + * @qtd_list_entry: For linking to the QH's list of QTDs + * + * A Queue Transfer Descriptor (QTD) holds the state of a bulk, control, + * interrupt, or isochronous transfer. A single QTD is created for each URB + * (of one of these types) submitted to the HCD. The transfer associated with + * a QTD may require one or multiple transactions. + * + * A QTD is linked to a Queue Head, which is entered in either the + * non-periodic or periodic schedule for execution. When a QTD is chosen for + * execution, some or all of its transactions may be executed. After + * execution, the state of the QTD is updated. The QTD may be retired if all + * its transactions are complete or if an error occurred. Otherwise, it + * remains in the schedule so more transactions can be executed later. + */ +struct dwc2_qtd { + enum dwc2_control_phase control_phase; + u8 in_process; + u8 data_toggle; + u8 complete_split; + u8 isoc_split_pos; + u16 isoc_frame_index; + u16 isoc_split_offset; + u32 ssplit_out_xfer_count; + u8 error_count; + u8 n_desc; + u16 isoc_frame_index_last; + struct dwc2_hcd_urb *urb; + struct dwc2_qh *qh; + struct list_head qtd_list_entry; +}; + +#ifdef DEBUG +struct hc_xfer_info { + struct dwc2_hsotg *hsotg; + struct dwc2_host_chan *chan; +}; +#endif + +/* Gets the struct usb_hcd that contains a struct dwc2_hsotg */ +static inline struct usb_hcd *dwc2_hsotg_to_hcd(struct dwc2_hsotg *hsotg) +{ + return (struct usb_hcd *)hsotg->priv; +} + +/* + * Inline used to disable one channel interrupt. Channel interrupts are + * disabled when the channel is halted or released by the interrupt handler. + * There is no need to handle further interrupts of that type until the + * channel is re-assigned. In fact, subsequent handling may cause crashes + * because the channel structures are cleaned up when the channel is released. + */ +static inline void disable_hc_int(struct dwc2_hsotg *hsotg, int chnum, u32 intr) +{ + u32 mask = readl(hsotg->regs + HCINTMSK(chnum)); + + mask &= ~intr; + writel(mask, hsotg->regs + HCINTMSK(chnum)); +} + +/* + * Returns the mode of operation, host or device + */ +static inline int dwc2_is_host_mode(struct dwc2_hsotg *hsotg) +{ + return (readl(hsotg->regs + GINTSTS) & GINTSTS_CURMODE_HOST) != 0; +} +static inline int dwc2_is_device_mode(struct dwc2_hsotg *hsotg) +{ + return (readl(hsotg->regs + GINTSTS) & GINTSTS_CURMODE_HOST) == 0; +} + +/* + * Reads HPRT0 in preparation to modify. It keeps the WC bits 0 so that if they + * are read as 1, they won't clear when written back. + */ +static inline u32 dwc2_read_hprt0(struct dwc2_hsotg *hsotg) +{ + u32 hprt0 = readl(hsotg->regs + HPRT0); + + hprt0 &= ~(HPRT0_ENA | HPRT0_CONNDET | HPRT0_ENACHG | HPRT0_OVRCURRCHG); + return hprt0; +} + +static inline u8 dwc2_hcd_get_ep_num(struct dwc2_hcd_pipe_info *pipe) +{ + return pipe->ep_num; +} + +static inline u8 dwc2_hcd_get_pipe_type(struct dwc2_hcd_pipe_info *pipe) +{ + return pipe->pipe_type; +} + +static inline u16 dwc2_hcd_get_mps(struct dwc2_hcd_pipe_info *pipe) +{ + return pipe->mps; +} + +static inline u8 dwc2_hcd_get_dev_addr(struct dwc2_hcd_pipe_info *pipe) +{ + return pipe->dev_addr; +} + +static inline u8 dwc2_hcd_is_pipe_isoc(struct dwc2_hcd_pipe_info *pipe) +{ + return pipe->pipe_type == USB_ENDPOINT_XFER_ISOC; +} + +static inline u8 dwc2_hcd_is_pipe_int(struct dwc2_hcd_pipe_info *pipe) +{ + return pipe->pipe_type == USB_ENDPOINT_XFER_INT; +} + +static inline u8 dwc2_hcd_is_pipe_bulk(struct dwc2_hcd_pipe_info *pipe) +{ + return pipe->pipe_type == USB_ENDPOINT_XFER_BULK; +} + +static inline u8 dwc2_hcd_is_pipe_control(struct dwc2_hcd_pipe_info *pipe) +{ + return pipe->pipe_type == USB_ENDPOINT_XFER_CONTROL; +} + +static inline u8 dwc2_hcd_is_pipe_in(struct dwc2_hcd_pipe_info *pipe) +{ + return pipe->pipe_dir == USB_DIR_IN; +} + +static inline u8 dwc2_hcd_is_pipe_out(struct dwc2_hcd_pipe_info *pipe) +{ + return !dwc2_hcd_is_pipe_in(pipe); +} + +extern int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq, + const struct dwc2_core_params *params); +extern void dwc2_hcd_remove(struct dwc2_hsotg *hsotg); +extern void dwc2_set_parameters(struct dwc2_hsotg *hsotg, + const struct dwc2_core_params *params); +extern void dwc2_set_all_params(struct dwc2_core_params *params, int value); +extern int dwc2_get_hwparams(struct dwc2_hsotg *hsotg); + +/* Transaction Execution Functions */ +extern enum dwc2_transaction_type dwc2_hcd_select_transactions( + struct dwc2_hsotg *hsotg); +extern void dwc2_hcd_queue_transactions(struct dwc2_hsotg *hsotg, + enum dwc2_transaction_type tr_type); + +/* Schedule Queue Functions */ +/* Implemented in hcd_queue.c */ +extern void dwc2_hcd_init_usecs(struct dwc2_hsotg *hsotg); +extern void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh); +extern int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh); +extern void dwc2_hcd_qh_unlink(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh); +extern void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, + int sched_csplit); + +extern void dwc2_hcd_qtd_init(struct dwc2_qtd *qtd, struct dwc2_hcd_urb *urb); +extern int dwc2_hcd_qtd_add(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd, + struct dwc2_qh **qh, gfp_t mem_flags); + +/* Unlinks and frees a QTD */ +static inline void dwc2_hcd_qtd_unlink_and_free(struct dwc2_hsotg *hsotg, + struct dwc2_qtd *qtd, + struct dwc2_qh *qh) +{ + list_del(&qtd->qtd_list_entry); + kfree(qtd); +} + +/* Descriptor DMA support functions */ +extern void dwc2_hcd_start_xfer_ddma(struct dwc2_hsotg *hsotg, + struct dwc2_qh *qh); +extern void dwc2_hcd_complete_xfer_ddma(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, int chnum, + enum dwc2_halt_status halt_status); + +extern int dwc2_hcd_qh_init_ddma(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, + gfp_t mem_flags); +extern void dwc2_hcd_qh_free_ddma(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh); + +/* Check if QH is non-periodic */ +#define dwc2_qh_is_non_per(_qh_ptr_) \ + ((_qh_ptr_)->ep_type == USB_ENDPOINT_XFER_BULK || \ + (_qh_ptr_)->ep_type == USB_ENDPOINT_XFER_CONTROL) + +#ifdef CONFIG_USB_DWC2_DEBUG_PERIODIC +static inline bool dbg_hc(struct dwc2_host_chan *hc) { return true; } +static inline bool dbg_qh(struct dwc2_qh *qh) { return true; } +static inline bool dbg_urb(struct urb *urb) { return true; } +static inline bool dbg_perio(void) { return true; } +#else /* !CONFIG_USB_DWC2_DEBUG_PERIODIC */ +static inline bool dbg_hc(struct dwc2_host_chan *hc) +{ + return hc->ep_type == USB_ENDPOINT_XFER_BULK || + hc->ep_type == USB_ENDPOINT_XFER_CONTROL; +} + +static inline bool dbg_qh(struct dwc2_qh *qh) +{ + return qh->ep_type == USB_ENDPOINT_XFER_BULK || + qh->ep_type == USB_ENDPOINT_XFER_CONTROL; +} + +static inline bool dbg_urb(struct urb *urb) +{ + return usb_pipetype(urb->pipe) == PIPE_BULK || + usb_pipetype(urb->pipe) == PIPE_CONTROL; +} + +static inline bool dbg_perio(void) { return false; } +#endif + +/* High bandwidth multiplier as encoded in highspeed endpoint descriptors */ +#define dwc2_hb_mult(wmaxpacketsize) (1 + (((wmaxpacketsize) >> 11) & 0x03)) + +/* Packet size for any kind of endpoint descriptor */ +#define dwc2_max_packet(wmaxpacketsize) ((wmaxpacketsize) & 0x07ff) + +/* + * Returns true if frame1 is less than or equal to frame2. The comparison is + * done modulo HFNUM_MAX_FRNUM. This accounts for the rollover of the + * frame number when the max frame number is reached. + */ +static inline int dwc2_frame_num_le(u16 frame1, u16 frame2) +{ + return ((frame2 - frame1) & HFNUM_MAX_FRNUM) <= (HFNUM_MAX_FRNUM >> 1); +} + +/* + * Returns true if frame1 is greater than frame2. The comparison is done + * modulo HFNUM_MAX_FRNUM. This accounts for the rollover of the frame + * number when the max frame number is reached. + */ +static inline int dwc2_frame_num_gt(u16 frame1, u16 frame2) +{ + return (frame1 != frame2) && + ((frame1 - frame2) & HFNUM_MAX_FRNUM) < (HFNUM_MAX_FRNUM >> 1); +} + +/* + * Increments frame by the amount specified by inc. The addition is done + * modulo HFNUM_MAX_FRNUM. Returns the incremented value. + */ +static inline u16 dwc2_frame_num_inc(u16 frame, u16 inc) +{ + return (frame + inc) & HFNUM_MAX_FRNUM; +} + +static inline u16 dwc2_full_frame_num(u16 frame) +{ + return (frame & HFNUM_MAX_FRNUM) >> 3; +} + +static inline u16 dwc2_micro_frame_num(u16 frame) +{ + return frame & 0x7; +} + +/* + * Returns the Core Interrupt Status register contents, ANDed with the Core + * Interrupt Mask register contents + */ +static inline u32 dwc2_read_core_intr(struct dwc2_hsotg *hsotg) +{ + return readl(hsotg->regs + GINTSTS) & readl(hsotg->regs + GINTMSK); +} + +static inline u32 dwc2_hcd_urb_get_status(struct dwc2_hcd_urb *dwc2_urb) +{ + return dwc2_urb->status; +} + +static inline u32 dwc2_hcd_urb_get_actual_length( + struct dwc2_hcd_urb *dwc2_urb) +{ + return dwc2_urb->actual_length; +} + +static inline u32 dwc2_hcd_urb_get_error_count(struct dwc2_hcd_urb *dwc2_urb) +{ + return dwc2_urb->error_count; +} + +static inline void dwc2_hcd_urb_set_iso_desc_params( + struct dwc2_hcd_urb *dwc2_urb, int desc_num, u32 offset, + u32 length) +{ + dwc2_urb->iso_descs[desc_num].offset = offset; + dwc2_urb->iso_descs[desc_num].length = length; +} + +static inline u32 dwc2_hcd_urb_get_iso_desc_status( + struct dwc2_hcd_urb *dwc2_urb, int desc_num) +{ + return dwc2_urb->iso_descs[desc_num].status; +} + +static inline u32 dwc2_hcd_urb_get_iso_desc_actual_length( + struct dwc2_hcd_urb *dwc2_urb, int desc_num) +{ + return dwc2_urb->iso_descs[desc_num].actual_length; +} + +static inline int dwc2_hcd_is_bandwidth_allocated(struct dwc2_hsotg *hsotg, + struct usb_host_endpoint *ep) +{ + struct dwc2_qh *qh = ep->hcpriv; + + if (qh && !list_empty(&qh->qh_list_entry)) + return 1; + + return 0; +} + +static inline u16 dwc2_hcd_get_ep_bandwidth(struct dwc2_hsotg *hsotg, + struct usb_host_endpoint *ep) +{ + struct dwc2_qh *qh = ep->hcpriv; + + if (!qh) { + WARN_ON(1); + return 0; + } + + return qh->usecs; +} + +extern void dwc2_hcd_save_data_toggle(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, int chnum, + struct dwc2_qtd *qtd); + +/* HCD Core API */ + +/** + * dwc2_handle_hcd_intr() - Called on every hardware interrupt + * + * @hsotg: The DWC2 HCD + * + * Returns IRQ_HANDLED if interrupt is handled + * Return IRQ_NONE if interrupt is not handled + */ +extern irqreturn_t dwc2_handle_hcd_intr(struct dwc2_hsotg *hsotg); + +/** + * dwc2_hcd_stop() - Halts the DWC_otg host mode operation + * + * @hsotg: The DWC2 HCD + */ +extern void dwc2_hcd_stop(struct dwc2_hsotg *hsotg); + +extern void dwc2_hcd_start(struct dwc2_hsotg *hsotg); +extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg); + +/** + * dwc2_hcd_is_b_host() - Returns 1 if core currently is acting as B host, + * and 0 otherwise + * + * @hsotg: The DWC2 HCD + */ +extern int dwc2_hcd_is_b_host(struct dwc2_hsotg *hsotg); + +/** + * dwc2_hcd_get_frame_number() - Returns current frame number + * + * @hsotg: The DWC2 HCD + */ +extern int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg); + +/** + * dwc2_hcd_dump_state() - Dumps hsotg state + * + * @hsotg: The DWC2 HCD + * + * NOTE: This function will be removed once the peripheral controller code + * is integrated and the driver is stable + */ +extern void dwc2_hcd_dump_state(struct dwc2_hsotg *hsotg); + +/** + * dwc2_hcd_dump_frrem() - Dumps the average frame remaining at SOF + * + * @hsotg: The DWC2 HCD + * + * This can be used to determine average interrupt latency. Frame remaining is + * also shown for start transfer and two additional sample points. + * + * NOTE: This function will be removed once the peripheral controller code + * is integrated and the driver is stable + */ +extern void dwc2_hcd_dump_frrem(struct dwc2_hsotg *hsotg); + +/* URB interface */ + +/* Transfer flags */ +#define URB_GIVEBACK_ASAP 0x1 +#define URB_SEND_ZERO_PACKET 0x2 + +/* Host driver callbacks */ + +extern void dwc2_host_start(struct dwc2_hsotg *hsotg); +extern void dwc2_host_disconnect(struct dwc2_hsotg *hsotg); +extern void dwc2_host_hub_info(struct dwc2_hsotg *hsotg, void *context, + int *hub_addr, int *hub_port); +extern int dwc2_host_get_speed(struct dwc2_hsotg *hsotg, void *context); +extern void dwc2_host_complete(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd, + int status); + +#ifdef DEBUG +/* + * Macro to sample the remaining PHY clocks left in the current frame. This + * may be used during debugging to determine the average time it takes to + * execute sections of code. There are two possible sample points, "a" and + * "b", so the _letter_ argument must be one of these values. + * + * To dump the average sample times, read the "hcd_frrem" sysfs attribute. For + * example, "cat /sys/devices/lm0/hcd_frrem". + */ +#define dwc2_sample_frrem(_hcd_, _qh_, _letter_) \ +do { \ + struct hfnum_data _hfnum_; \ + struct dwc2_qtd *_qtd_; \ + \ + _qtd_ = list_entry((_qh_)->qtd_list.next, struct dwc2_qtd, \ + qtd_list_entry); \ + if (usb_pipeint(_qtd_->urb->pipe) && \ + (_qh_)->start_split_frame != 0 && !_qtd_->complete_split) { \ + _hfnum_.d32 = readl((_hcd_)->regs + HFNUM); \ + switch (_hfnum_.b.frnum & 0x7) { \ + case 7: \ + (_hcd_)->hfnum_7_samples_##_letter_++; \ + (_hcd_)->hfnum_7_frrem_accum_##_letter_ += \ + _hfnum_.b.frrem; \ + break; \ + case 0: \ + (_hcd_)->hfnum_0_samples_##_letter_++; \ + (_hcd_)->hfnum_0_frrem_accum_##_letter_ += \ + _hfnum_.b.frrem; \ + break; \ + default: \ + (_hcd_)->hfnum_other_samples_##_letter_++; \ + (_hcd_)->hfnum_other_frrem_accum_##_letter_ += \ + _hfnum_.b.frrem; \ + break; \ + } \ + } \ +} while (0) +#else +#define dwc2_sample_frrem(_hcd_, _qh_, _letter_) do {} while (0) +#endif + +#endif /* __DWC2_HCD_H__ */ diff --git a/drivers/usb/dwc2/hcd_ddma.c b/drivers/usb/dwc2/hcd_ddma.c new file mode 100644 index 0000000..3376177 --- /dev/null +++ b/drivers/usb/dwc2/hcd_ddma.c @@ -0,0 +1,1212 @@ +/* + * hcd_ddma.c - DesignWare HS OTG Controller descriptor DMA routines + * + * Copyright (C) 2004-2013 Synopsys, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This file contains the Descriptor DMA implementation for Host mode + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/dma-mapping.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/usb.h> + +#include <linux/usb/hcd.h> +#include <linux/usb/ch11.h> + +#include "core.h" +#include "hcd.h" + +static u16 dwc2_frame_list_idx(u16 frame) +{ + return frame & (FRLISTEN_64_SIZE - 1); +} + +static u16 dwc2_desclist_idx_inc(u16 idx, u16 inc, u8 speed) +{ + return (idx + inc) & + ((speed == USB_SPEED_HIGH ? MAX_DMA_DESC_NUM_HS_ISOC : + MAX_DMA_DESC_NUM_GENERIC) - 1); +} + +static u16 dwc2_desclist_idx_dec(u16 idx, u16 inc, u8 speed) +{ + return (idx - inc) & + ((speed == USB_SPEED_HIGH ? MAX_DMA_DESC_NUM_HS_ISOC : + MAX_DMA_DESC_NUM_GENERIC) - 1); +} + +static u16 dwc2_max_desc_num(struct dwc2_qh *qh) +{ + return (qh->ep_type == USB_ENDPOINT_XFER_ISOC && + qh->dev_speed == USB_SPEED_HIGH) ? + MAX_DMA_DESC_NUM_HS_ISOC : MAX_DMA_DESC_NUM_GENERIC; +} + +static u16 dwc2_frame_incr_val(struct dwc2_qh *qh) +{ + return qh->dev_speed == USB_SPEED_HIGH ? + (qh->interval + 8 - 1) / 8 : qh->interval; +} + +static int dwc2_desc_list_alloc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, + gfp_t flags) +{ + qh->desc_list = dma_alloc_coherent(hsotg->dev, + sizeof(struct dwc2_hcd_dma_desc) * + dwc2_max_desc_num(qh), &qh->desc_list_dma, + flags); + + if (!qh->desc_list) + return -ENOMEM; + + memset(qh->desc_list, 0, + sizeof(struct dwc2_hcd_dma_desc) * dwc2_max_desc_num(qh)); + + qh->n_bytes = kzalloc(sizeof(u32) * dwc2_max_desc_num(qh), flags); + if (!qh->n_bytes) { + dma_free_coherent(hsotg->dev, sizeof(struct dwc2_hcd_dma_desc) + * dwc2_max_desc_num(qh), qh->desc_list, + qh->desc_list_dma); + qh->desc_list = NULL; + return -ENOMEM; + } + + return 0; +} + +static void dwc2_desc_list_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) +{ + if (qh->desc_list) { + dma_free_coherent(hsotg->dev, sizeof(struct dwc2_hcd_dma_desc) + * dwc2_max_desc_num(qh), qh->desc_list, + qh->desc_list_dma); + qh->desc_list = NULL; + } + + kfree(qh->n_bytes); + qh->n_bytes = NULL; +} + +static int dwc2_frame_list_alloc(struct dwc2_hsotg *hsotg, gfp_t mem_flags) +{ + if (hsotg->frame_list) + return 0; + + hsotg->frame_list = dma_alloc_coherent(hsotg->dev, + 4 * FRLISTEN_64_SIZE, + &hsotg->frame_list_dma, + mem_flags); + if (!hsotg->frame_list) + return -ENOMEM; + + memset(hsotg->frame_list, 0, 4 * FRLISTEN_64_SIZE); + return 0; +} + +static void dwc2_frame_list_free(struct dwc2_hsotg *hsotg) +{ + u32 *frame_list; + dma_addr_t frame_list_dma; + unsigned long flags; + + spin_lock_irqsave(&hsotg->lock, flags); + + if (!hsotg->frame_list) { + spin_unlock_irqrestore(&hsotg->lock, flags); + return; + } + + frame_list = hsotg->frame_list; + frame_list_dma = hsotg->frame_list_dma; + hsotg->frame_list = NULL; + + spin_unlock_irqrestore(&hsotg->lock, flags); + + dma_free_coherent(hsotg->dev, 4 * FRLISTEN_64_SIZE, frame_list, + frame_list_dma); +} + +static void dwc2_per_sched_enable(struct dwc2_hsotg *hsotg, u32 fr_list_en) +{ + u32 hcfg; + unsigned long flags; + + spin_lock_irqsave(&hsotg->lock, flags); + + hcfg = readl(hsotg->regs + HCFG); + if (hcfg & HCFG_PERSCHEDENA) { + /* already enabled */ + spin_unlock_irqrestore(&hsotg->lock, flags); + return; + } + + writel(hsotg->frame_list_dma, hsotg->regs + HFLBADDR); + + hcfg &= ~HCFG_FRLISTEN_MASK; + hcfg |= fr_list_en | HCFG_PERSCHEDENA; + dev_vdbg(hsotg->dev, "Enabling Periodic schedule\n"); + writel(hcfg, hsotg->regs + HCFG); + + spin_unlock_irqrestore(&hsotg->lock, flags); +} + +static void dwc2_per_sched_disable(struct dwc2_hsotg *hsotg) +{ + u32 hcfg; + unsigned long flags; + + spin_lock_irqsave(&hsotg->lock, flags); + + hcfg = readl(hsotg->regs + HCFG); + if (!(hcfg & HCFG_PERSCHEDENA)) { + /* already disabled */ + spin_unlock_irqrestore(&hsotg->lock, flags); + return; + } + + hcfg &= ~HCFG_PERSCHEDENA; + dev_vdbg(hsotg->dev, "Disabling Periodic schedule\n"); + writel(hcfg, hsotg->regs + HCFG); + + spin_unlock_irqrestore(&hsotg->lock, flags); +} + +/* + * Activates/Deactivates FrameList entries for the channel based on endpoint + * servicing period + */ +static void dwc2_update_frame_list(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, + int enable) +{ + struct dwc2_host_chan *chan; + u16 i, j, inc; + + if (!hsotg) { + pr_err("hsotg = %p\n", hsotg); + return; + } + + if (!qh->channel) { + dev_err(hsotg->dev, "qh->channel = %p\n", qh->channel); + return; + } + + if (!hsotg->frame_list) { + dev_err(hsotg->dev, "hsotg->frame_list = %p\n", + hsotg->frame_list); + return; + } + + chan = qh->channel; + inc = dwc2_frame_incr_val(qh); + if (qh->ep_type == USB_ENDPOINT_XFER_ISOC) + i = dwc2_frame_list_idx(qh->sched_frame); + else + i = 0; + + j = i; + do { + if (enable) + hsotg->frame_list[j] |= 1 << chan->hc_num; + else + hsotg->frame_list[j] &= ~(1 << chan->hc_num); + j = (j + inc) & (FRLISTEN_64_SIZE - 1); + } while (j != i); + + if (!enable) + return; + + chan->schinfo = 0; + if (chan->speed == USB_SPEED_HIGH && qh->interval) { + j = 1; + /* TODO - check this */ + inc = (8 + qh->interval - 1) / qh->interval; + for (i = 0; i < inc; i++) { + chan->schinfo |= j; + j = j << qh->interval; + } + } else { + chan->schinfo = 0xff; + } +} + +static void dwc2_release_channel_ddma(struct dwc2_hsotg *hsotg, + struct dwc2_qh *qh) +{ + struct dwc2_host_chan *chan = qh->channel; + + if (dwc2_qh_is_non_per(qh)) { + if (hsotg->core_params->uframe_sched > 0) + hsotg->available_host_channels++; + else + hsotg->non_periodic_channels--; + } else { + dwc2_update_frame_list(hsotg, qh, 0); + } + + /* + * The condition is added to prevent double cleanup try in case of + * device disconnect. See channel cleanup in dwc2_hcd_disconnect(). + */ + if (chan->qh) { + if (!list_empty(&chan->hc_list_entry)) + list_del(&chan->hc_list_entry); + dwc2_hc_cleanup(hsotg, chan); + list_add_tail(&chan->hc_list_entry, &hsotg->free_hc_list); + chan->qh = NULL; + } + + qh->channel = NULL; + qh->ntd = 0; + + if (qh->desc_list) + memset(qh->desc_list, 0, sizeof(struct dwc2_hcd_dma_desc) * + dwc2_max_desc_num(qh)); +} + +/** + * dwc2_hcd_qh_init_ddma() - Initializes a QH structure's Descriptor DMA + * related members + * + * @hsotg: The HCD state structure for the DWC OTG controller + * @qh: The QH to init + * + * Return: 0 if successful, negative error code otherwise + * + * Allocates memory for the descriptor list. For the first periodic QH, + * allocates memory for the FrameList and enables periodic scheduling. + */ +int dwc2_hcd_qh_init_ddma(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, + gfp_t mem_flags) +{ + int retval; + + if (qh->do_split) { + dev_err(hsotg->dev, + "SPLIT Transfers are not supported in Descriptor DMA mode.\n"); + retval = -EINVAL; + goto err0; + } + + retval = dwc2_desc_list_alloc(hsotg, qh, mem_flags); + if (retval) + goto err0; + + if (qh->ep_type == USB_ENDPOINT_XFER_ISOC || + qh->ep_type == USB_ENDPOINT_XFER_INT) { + if (!hsotg->frame_list) { + retval = dwc2_frame_list_alloc(hsotg, mem_flags); + if (retval) + goto err1; + /* Enable periodic schedule on first periodic QH */ + dwc2_per_sched_enable(hsotg, HCFG_FRLISTEN_64); + } + } + + qh->ntd = 0; + return 0; + +err1: + dwc2_desc_list_free(hsotg, qh); +err0: + return retval; +} + +/** + * dwc2_hcd_qh_free_ddma() - Frees a QH structure's Descriptor DMA related + * members + * + * @hsotg: The HCD state structure for the DWC OTG controller + * @qh: The QH to free + * + * Frees descriptor list memory associated with the QH. If QH is periodic and + * the last, frees FrameList memory and disables periodic scheduling. + */ +void dwc2_hcd_qh_free_ddma(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) +{ + dwc2_desc_list_free(hsotg, qh); + + /* + * Channel still assigned due to some reasons. + * Seen on Isoc URB dequeue. Channel halted but no subsequent + * ChHalted interrupt to release the channel. Afterwards + * when it comes here from endpoint disable routine + * channel remains assigned. + */ + if (qh->channel) + dwc2_release_channel_ddma(hsotg, qh); + + if ((qh->ep_type == USB_ENDPOINT_XFER_ISOC || + qh->ep_type == USB_ENDPOINT_XFER_INT) && + (hsotg->core_params->uframe_sched > 0 || + !hsotg->periodic_channels) && hsotg->frame_list) { + dwc2_per_sched_disable(hsotg); + dwc2_frame_list_free(hsotg); + } +} + +static u8 dwc2_frame_to_desc_idx(struct dwc2_qh *qh, u16 frame_idx) +{ + if (qh->dev_speed == USB_SPEED_HIGH) + /* Descriptor set (8 descriptors) index which is 8-aligned */ + return (frame_idx & ((MAX_DMA_DESC_NUM_HS_ISOC / 8) - 1)) * 8; + else + return frame_idx & (MAX_DMA_DESC_NUM_GENERIC - 1); +} + +/* + * Determine starting frame for Isochronous transfer. + * Few frames skipped to prevent race condition with HC. + */ +static u16 dwc2_calc_starting_frame(struct dwc2_hsotg *hsotg, + struct dwc2_qh *qh, u16 *skip_frames) +{ + u16 frame; + + hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg); + + /* sched_frame is always frame number (not uFrame) both in FS and HS! */ + + /* + * skip_frames is used to limit activated descriptors number + * to avoid the situation when HC services the last activated + * descriptor firstly. + * Example for FS: + * Current frame is 1, scheduled frame is 3. Since HC always fetches + * the descriptor corresponding to curr_frame+1, the descriptor + * corresponding to frame 2 will be fetched. If the number of + * descriptors is max=64 (or greather) the list will be fully programmed + * with Active descriptors and it is possible case (rare) that the + * latest descriptor(considering rollback) corresponding to frame 2 will + * be serviced first. HS case is more probable because, in fact, up to + * 11 uframes (16 in the code) may be skipped. + */ + if (qh->dev_speed == USB_SPEED_HIGH) { + /* + * Consider uframe counter also, to start xfer asap. If half of + * the frame elapsed skip 2 frames otherwise just 1 frame. + * Starting descriptor index must be 8-aligned, so if the + * current frame is near to complete the next one is skipped as + * well. + */ + if (dwc2_micro_frame_num(hsotg->frame_number) >= 5) { + *skip_frames = 2 * 8; + frame = dwc2_frame_num_inc(hsotg->frame_number, + *skip_frames); + } else { + *skip_frames = 1 * 8; + frame = dwc2_frame_num_inc(hsotg->frame_number, + *skip_frames); + } + + frame = dwc2_full_frame_num(frame); + } else { + /* + * Two frames are skipped for FS - the current and the next. + * But for descriptor programming, 1 frame (descriptor) is + * enough, see example above. + */ + *skip_frames = 1; + frame = dwc2_frame_num_inc(hsotg->frame_number, 2); + } + + return frame; +} + +/* + * Calculate initial descriptor index for isochronous transfer based on + * scheduled frame + */ +static u16 dwc2_recalc_initial_desc_idx(struct dwc2_hsotg *hsotg, + struct dwc2_qh *qh) +{ + u16 frame, fr_idx, fr_idx_tmp, skip_frames; + + /* + * With current ISOC processing algorithm the channel is being released + * when no more QTDs in the list (qh->ntd == 0). Thus this function is + * called only when qh->ntd == 0 and qh->channel == 0. + * + * So qh->channel != NULL branch is not used and just not removed from + * the source file. It is required for another possible approach which + * is, do not disable and release the channel when ISOC session + * completed, just move QH to inactive schedule until new QTD arrives. + * On new QTD, the QH moved back to 'ready' schedule, starting frame and + * therefore starting desc_index are recalculated. In this case channel + * is released only on ep_disable. + */ + + /* + * Calculate starting descriptor index. For INTERRUPT endpoint it is + * always 0. + */ + if (qh->channel) { + frame = dwc2_calc_starting_frame(hsotg, qh, &skip_frames); + /* + * Calculate initial descriptor index based on FrameList current + * bitmap and servicing period + */ + fr_idx_tmp = dwc2_frame_list_idx(frame); + fr_idx = (FRLISTEN_64_SIZE + + dwc2_frame_list_idx(qh->sched_frame) - fr_idx_tmp) + % dwc2_frame_incr_val(qh); + fr_idx = (fr_idx + fr_idx_tmp) % FRLISTEN_64_SIZE; + } else { + qh->sched_frame = dwc2_calc_starting_frame(hsotg, qh, + &skip_frames); + fr_idx = dwc2_frame_list_idx(qh->sched_frame); + } + + qh->td_first = qh->td_last = dwc2_frame_to_desc_idx(qh, fr_idx); + + return skip_frames; +} + +#define ISOC_URB_GIVEBACK_ASAP + +#define MAX_ISOC_XFER_SIZE_FS 1023 +#define MAX_ISOC_XFER_SIZE_HS 3072 +#define DESCNUM_THRESHOLD 4 + +static void dwc2_fill_host_isoc_dma_desc(struct dwc2_hsotg *hsotg, + struct dwc2_qtd *qtd, + struct dwc2_qh *qh, u32 max_xfer_size, + u16 idx) +{ + struct dwc2_hcd_dma_desc *dma_desc = &qh->desc_list[idx]; + struct dwc2_hcd_iso_packet_desc *frame_desc; + + memset(dma_desc, 0, sizeof(*dma_desc)); + frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index_last]; + + if (frame_desc->length > max_xfer_size) + qh->n_bytes[idx] = max_xfer_size; + else + qh->n_bytes[idx] = frame_desc->length; + + dma_desc->buf = (u32)(qtd->urb->dma + frame_desc->offset); + dma_desc->status = qh->n_bytes[idx] << HOST_DMA_ISOC_NBYTES_SHIFT & + HOST_DMA_ISOC_NBYTES_MASK; + +#ifdef ISOC_URB_GIVEBACK_ASAP + /* Set IOC for each descriptor corresponding to last frame of URB */ + if (qtd->isoc_frame_index_last == qtd->urb->packet_count) + dma_desc->status |= HOST_DMA_IOC; +#endif + + qh->ntd++; + qtd->isoc_frame_index_last++; +} + +static void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg, + struct dwc2_qh *qh, u16 skip_frames) +{ + struct dwc2_qtd *qtd; + u32 max_xfer_size; + u16 idx, inc, n_desc, ntd_max = 0; + + idx = qh->td_last; + inc = qh->interval; + n_desc = 0; + + if (qh->interval) { + ntd_max = (dwc2_max_desc_num(qh) + qh->interval - 1) / + qh->interval; + if (skip_frames && !qh->channel) + ntd_max -= skip_frames / qh->interval; + } + + max_xfer_size = qh->dev_speed == USB_SPEED_HIGH ? + MAX_ISOC_XFER_SIZE_HS : MAX_ISOC_XFER_SIZE_FS; + + list_for_each_entry(qtd, &qh->qtd_list, qtd_list_entry) { + while (qh->ntd < ntd_max && qtd->isoc_frame_index_last < + qtd->urb->packet_count) { + if (n_desc > 1) + qh->desc_list[n_desc - 1].status |= HOST_DMA_A; + dwc2_fill_host_isoc_dma_desc(hsotg, qtd, qh, + max_xfer_size, idx); + idx = dwc2_desclist_idx_inc(idx, inc, qh->dev_speed); + n_desc++; + } + qtd->in_process = 1; + } + + qh->td_last = idx; + +#ifdef ISOC_URB_GIVEBACK_ASAP + /* Set IOC for last descriptor if descriptor list is full */ + if (qh->ntd == ntd_max) { + idx = dwc2_desclist_idx_dec(qh->td_last, inc, qh->dev_speed); + qh->desc_list[idx].status |= HOST_DMA_IOC; + } +#else + /* + * Set IOC bit only for one descriptor. Always try to be ahead of HW + * processing, i.e. on IOC generation driver activates next descriptor + * but core continues to process descriptors following the one with IOC + * set. + */ + + if (n_desc > DESCNUM_THRESHOLD) + /* + * Move IOC "up". Required even if there is only one QTD + * in the list, because QTDs might continue to be queued, + * but during the activation it was only one queued. + * Actually more than one QTD might be in the list if this + * function called from XferCompletion - QTDs was queued during + * HW processing of the previous descriptor chunk. + */ + idx = dwc2_desclist_idx_dec(idx, inc * ((qh->ntd + 1) / 2), + qh->dev_speed); + else + /* + * Set the IOC for the latest descriptor if either number of + * descriptors is not greater than threshold or no more new + * descriptors activated + */ + idx = dwc2_desclist_idx_dec(qh->td_last, inc, qh->dev_speed); + + qh->desc_list[idx].status |= HOST_DMA_IOC; +#endif + + if (n_desc) { + qh->desc_list[n_desc - 1].status |= HOST_DMA_A; + if (n_desc > 1) + qh->desc_list[0].status |= HOST_DMA_A; + } +} + +static void dwc2_fill_host_dma_desc(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, + struct dwc2_qtd *qtd, struct dwc2_qh *qh, + int n_desc) +{ + struct dwc2_hcd_dma_desc *dma_desc = &qh->desc_list[n_desc]; + int len = chan->xfer_len; + + if (len > MAX_DMA_DESC_SIZE - (chan->max_packet - 1)) + len = MAX_DMA_DESC_SIZE - (chan->max_packet - 1); + + if (chan->ep_is_in) { + int num_packets; + + if (len > 0 && chan->max_packet) + num_packets = (len + chan->max_packet - 1) + / chan->max_packet; + else + /* Need 1 packet for transfer length of 0 */ + num_packets = 1; + + /* Always program an integral # of packets for IN transfers */ + len = num_packets * chan->max_packet; + } + + dma_desc->status = len << HOST_DMA_NBYTES_SHIFT & HOST_DMA_NBYTES_MASK; + qh->n_bytes[n_desc] = len; + + if (qh->ep_type == USB_ENDPOINT_XFER_CONTROL && + qtd->control_phase == DWC2_CONTROL_SETUP) + dma_desc->status |= HOST_DMA_SUP; + + dma_desc->buf = (u32)chan->xfer_dma; + + /* + * Last (or only) descriptor of IN transfer with actual size less + * than MaxPacket + */ + if (len > chan->xfer_len) { + chan->xfer_len = 0; + } else { + chan->xfer_dma += len; + chan->xfer_len -= len; + } +} + +static void dwc2_init_non_isoc_dma_desc(struct dwc2_hsotg *hsotg, + struct dwc2_qh *qh) +{ + struct dwc2_qtd *qtd; + struct dwc2_host_chan *chan = qh->channel; + int n_desc = 0; + + dev_vdbg(hsotg->dev, "%s(): qh=%p dma=%08lx len=%d\n", __func__, qh, + (unsigned long)chan->xfer_dma, chan->xfer_len); + + /* + * Start with chan->xfer_dma initialized in assign_and_init_hc(), then + * if SG transfer consists of multiple URBs, this pointer is re-assigned + * to the buffer of the currently processed QTD. For non-SG request + * there is always one QTD active. + */ + + list_for_each_entry(qtd, &qh->qtd_list, qtd_list_entry) { + dev_vdbg(hsotg->dev, "qtd=%p\n", qtd); + + if (n_desc) { + /* SG request - more than 1 QTD */ + chan->xfer_dma = qtd->urb->dma + + qtd->urb->actual_length; + chan->xfer_len = qtd->urb->length - + qtd->urb->actual_length; + dev_vdbg(hsotg->dev, "buf=%08lx len=%d\n", + (unsigned long)chan->xfer_dma, chan->xfer_len); + } + + qtd->n_desc = 0; + do { + if (n_desc > 1) { + qh->desc_list[n_desc - 1].status |= HOST_DMA_A; + dev_vdbg(hsotg->dev, + "set A bit in desc %d (%p)\n", + n_desc - 1, + &qh->desc_list[n_desc - 1]); + } + dwc2_fill_host_dma_desc(hsotg, chan, qtd, qh, n_desc); + dev_vdbg(hsotg->dev, + "desc %d (%p) buf=%08x status=%08x\n", + n_desc, &qh->desc_list[n_desc], + qh->desc_list[n_desc].buf, + qh->desc_list[n_desc].status); + qtd->n_desc++; + n_desc++; + } while (chan->xfer_len > 0 && + n_desc != MAX_DMA_DESC_NUM_GENERIC); + + dev_vdbg(hsotg->dev, "n_desc=%d\n", n_desc); + qtd->in_process = 1; + if (qh->ep_type == USB_ENDPOINT_XFER_CONTROL) + break; + if (n_desc == MAX_DMA_DESC_NUM_GENERIC) + break; + } + + if (n_desc) { + qh->desc_list[n_desc - 1].status |= + HOST_DMA_IOC | HOST_DMA_EOL | HOST_DMA_A; + dev_vdbg(hsotg->dev, "set IOC/EOL/A bits in desc %d (%p)\n", + n_desc - 1, &qh->desc_list[n_desc - 1]); + if (n_desc > 1) { + qh->desc_list[0].status |= HOST_DMA_A; + dev_vdbg(hsotg->dev, "set A bit in desc 0 (%p)\n", + &qh->desc_list[0]); + } + chan->ntd = n_desc; + } +} + +/** + * dwc2_hcd_start_xfer_ddma() - Starts a transfer in Descriptor DMA mode + * + * @hsotg: The HCD state structure for the DWC OTG controller + * @qh: The QH to init + * + * Return: 0 if successful, negative error code otherwise + * + * For Control and Bulk endpoints, initializes descriptor list and starts the + * transfer. For Interrupt and Isochronous endpoints, initializes descriptor + * list then updates FrameList, marking appropriate entries as active. + * + * For Isochronous endpoints the starting descriptor index is calculated based + * on the scheduled frame, but only on the first transfer descriptor within a + * session. Then the transfer is started via enabling the channel. + * + * For Isochronous endpoints the channel is not halted on XferComplete + * interrupt so remains assigned to the endpoint(QH) until session is done. + */ +void dwc2_hcd_start_xfer_ddma(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) +{ + /* Channel is already assigned */ + struct dwc2_host_chan *chan = qh->channel; + u16 skip_frames = 0; + + switch (chan->ep_type) { + case USB_ENDPOINT_XFER_CONTROL: + case USB_ENDPOINT_XFER_BULK: + dwc2_init_non_isoc_dma_desc(hsotg, qh); + dwc2_hc_start_transfer_ddma(hsotg, chan); + break; + case USB_ENDPOINT_XFER_INT: + dwc2_init_non_isoc_dma_desc(hsotg, qh); + dwc2_update_frame_list(hsotg, qh, 1); + dwc2_hc_start_transfer_ddma(hsotg, chan); + break; + case USB_ENDPOINT_XFER_ISOC: + if (!qh->ntd) + skip_frames = dwc2_recalc_initial_desc_idx(hsotg, qh); + dwc2_init_isoc_dma_desc(hsotg, qh, skip_frames); + + if (!chan->xfer_started) { + dwc2_update_frame_list(hsotg, qh, 1); + + /* + * Always set to max, instead of actual size. Otherwise + * ntd will be changed with channel being enabled. Not + * recommended. + */ + chan->ntd = dwc2_max_desc_num(qh); + + /* Enable channel only once for ISOC */ + dwc2_hc_start_transfer_ddma(hsotg, chan); + } + + break; + default: + break; + } +} + +#define DWC2_CMPL_DONE 1 +#define DWC2_CMPL_STOP 2 + +static int dwc2_cmpl_host_isoc_dma_desc(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, + struct dwc2_qtd *qtd, + struct dwc2_qh *qh, u16 idx) +{ + struct dwc2_hcd_dma_desc *dma_desc = &qh->desc_list[idx]; + struct dwc2_hcd_iso_packet_desc *frame_desc; + u16 remain = 0; + int rc = 0; + + if (!qtd->urb) + return -EINVAL; + + frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index_last]; + dma_desc->buf = (u32)(qtd->urb->dma + frame_desc->offset); + if (chan->ep_is_in) + remain = (dma_desc->status & HOST_DMA_ISOC_NBYTES_MASK) >> + HOST_DMA_ISOC_NBYTES_SHIFT; + + if ((dma_desc->status & HOST_DMA_STS_MASK) == HOST_DMA_STS_PKTERR) { + /* + * XactError, or unable to complete all the transactions + * in the scheduled micro-frame/frame, both indicated by + * HOST_DMA_STS_PKTERR + */ + qtd->urb->error_count++; + frame_desc->actual_length = qh->n_bytes[idx] - remain; + frame_desc->status = -EPROTO; + } else { + /* Success */ + frame_desc->actual_length = qh->n_bytes[idx] - remain; + frame_desc->status = 0; + } + + if (++qtd->isoc_frame_index == qtd->urb->packet_count) { + /* + * urb->status is not used for isoc transfers here. The + * individual frame_desc status are used instead. + */ + dwc2_host_complete(hsotg, qtd, 0); + dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh); + + /* + * This check is necessary because urb_dequeue can be called + * from urb complete callback (sound driver for example). All + * pending URBs are dequeued there, so no need for further + * processing. + */ + if (chan->halt_status == DWC2_HC_XFER_URB_DEQUEUE) + return -1; + rc = DWC2_CMPL_DONE; + } + + qh->ntd--; + + /* Stop if IOC requested descriptor reached */ + if (dma_desc->status & HOST_DMA_IOC) + rc = DWC2_CMPL_STOP; + + return rc; +} + +static void dwc2_complete_isoc_xfer_ddma(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, + enum dwc2_halt_status halt_status) +{ + struct dwc2_hcd_iso_packet_desc *frame_desc; + struct dwc2_qtd *qtd, *qtd_tmp; + struct dwc2_qh *qh; + u16 idx; + int rc; + + qh = chan->qh; + idx = qh->td_first; + + if (chan->halt_status == DWC2_HC_XFER_URB_DEQUEUE) { + list_for_each_entry(qtd, &qh->qtd_list, qtd_list_entry) + qtd->in_process = 0; + return; + } + + if (halt_status == DWC2_HC_XFER_AHB_ERR || + halt_status == DWC2_HC_XFER_BABBLE_ERR) { + /* + * Channel is halted in these error cases, considered as serious + * issues. + * Complete all URBs marking all frames as failed, irrespective + * whether some of the descriptors (frames) succeeded or not. + * Pass error code to completion routine as well, to update + * urb->status, some of class drivers might use it to stop + * queing transfer requests. + */ + int err = halt_status == DWC2_HC_XFER_AHB_ERR ? + -EIO : -EOVERFLOW; + + list_for_each_entry_safe(qtd, qtd_tmp, &qh->qtd_list, + qtd_list_entry) { + if (qtd->urb) { + for (idx = 0; idx < qtd->urb->packet_count; + idx++) { + frame_desc = &qtd->urb->iso_descs[idx]; + frame_desc->status = err; + } + + dwc2_host_complete(hsotg, qtd, err); + } + + dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh); + } + + return; + } + + list_for_each_entry_safe(qtd, qtd_tmp, &qh->qtd_list, qtd_list_entry) { + if (!qtd->in_process) + break; + do { + rc = dwc2_cmpl_host_isoc_dma_desc(hsotg, chan, qtd, qh, + idx); + if (rc < 0) + return; + idx = dwc2_desclist_idx_inc(idx, qh->interval, + chan->speed); + if (rc == DWC2_CMPL_STOP) + goto stop_scan; + if (rc == DWC2_CMPL_DONE) + break; + } while (idx != qh->td_first); + } + +stop_scan: + qh->td_first = idx; +} + +static int dwc2_update_non_isoc_urb_state_ddma(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, + struct dwc2_qtd *qtd, + struct dwc2_hcd_dma_desc *dma_desc, + enum dwc2_halt_status halt_status, + u32 n_bytes, int *xfer_done) +{ + struct dwc2_hcd_urb *urb = qtd->urb; + u16 remain = 0; + + if (chan->ep_is_in) + remain = (dma_desc->status & HOST_DMA_NBYTES_MASK) >> + HOST_DMA_NBYTES_SHIFT; + + dev_vdbg(hsotg->dev, "remain=%d dwc2_urb=%p\n", remain, urb); + + if (halt_status == DWC2_HC_XFER_AHB_ERR) { + dev_err(hsotg->dev, "EIO\n"); + urb->status = -EIO; + return 1; + } + + if ((dma_desc->status & HOST_DMA_STS_MASK) == HOST_DMA_STS_PKTERR) { + switch (halt_status) { + case DWC2_HC_XFER_STALL: + dev_vdbg(hsotg->dev, "Stall\n"); + urb->status = -EPIPE; + break; + case DWC2_HC_XFER_BABBLE_ERR: + dev_err(hsotg->dev, "Babble\n"); + urb->status = -EOVERFLOW; + break; + case DWC2_HC_XFER_XACT_ERR: + dev_err(hsotg->dev, "XactErr\n"); + urb->status = -EPROTO; + break; + default: + dev_err(hsotg->dev, + "%s: Unhandled descriptor error status (%d)\n", + __func__, halt_status); + break; + } + return 1; + } + + if (dma_desc->status & HOST_DMA_A) { + dev_vdbg(hsotg->dev, + "Active descriptor encountered on channel %d\n", + chan->hc_num); + return 0; + } + + if (chan->ep_type == USB_ENDPOINT_XFER_CONTROL) { + if (qtd->control_phase == DWC2_CONTROL_DATA) { + urb->actual_length += n_bytes - remain; + if (remain || urb->actual_length >= urb->length) { + /* + * For Control Data stage do not set urb->status + * to 0, to prevent URB callback. Set it when + * Status phase is done. See below. + */ + *xfer_done = 1; + } + } else if (qtd->control_phase == DWC2_CONTROL_STATUS) { + urb->status = 0; + *xfer_done = 1; + } + /* No handling for SETUP stage */ + } else { + /* BULK and INTR */ + urb->actual_length += n_bytes - remain; + dev_vdbg(hsotg->dev, "length=%d actual=%d\n", urb->length, + urb->actual_length); + if (remain || urb->actual_length >= urb->length) { + urb->status = 0; + *xfer_done = 1; + } + } + + return 0; +} + +static int dwc2_process_non_isoc_desc(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, + int chnum, struct dwc2_qtd *qtd, + int desc_num, + enum dwc2_halt_status halt_status, + int *xfer_done) +{ + struct dwc2_qh *qh = chan->qh; + struct dwc2_hcd_urb *urb = qtd->urb; + struct dwc2_hcd_dma_desc *dma_desc; + u32 n_bytes; + int failed; + + dev_vdbg(hsotg->dev, "%s()\n", __func__); + + if (!urb) + return -EINVAL; + + dma_desc = &qh->desc_list[desc_num]; + n_bytes = qh->n_bytes[desc_num]; + dev_vdbg(hsotg->dev, + "qtd=%p dwc2_urb=%p desc_num=%d desc=%p n_bytes=%d\n", + qtd, urb, desc_num, dma_desc, n_bytes); + failed = dwc2_update_non_isoc_urb_state_ddma(hsotg, chan, qtd, dma_desc, + halt_status, n_bytes, + xfer_done); + if (failed || (*xfer_done && urb->status != -EINPROGRESS)) { + dwc2_host_complete(hsotg, qtd, urb->status); + dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh); + dev_vdbg(hsotg->dev, "failed=%1x xfer_done=%1x status=%08x\n", + failed, *xfer_done, urb->status); + return failed; + } + + if (qh->ep_type == USB_ENDPOINT_XFER_CONTROL) { + switch (qtd->control_phase) { + case DWC2_CONTROL_SETUP: + if (urb->length > 0) + qtd->control_phase = DWC2_CONTROL_DATA; + else + qtd->control_phase = DWC2_CONTROL_STATUS; + dev_vdbg(hsotg->dev, + " Control setup transaction done\n"); + break; + case DWC2_CONTROL_DATA: + if (*xfer_done) { + qtd->control_phase = DWC2_CONTROL_STATUS; + dev_vdbg(hsotg->dev, + " Control data transfer done\n"); + } else if (desc_num + 1 == qtd->n_desc) { + /* + * Last descriptor for Control data stage which + * is not completed yet + */ + dwc2_hcd_save_data_toggle(hsotg, chan, chnum, + qtd); + } + break; + default: + break; + } + } + + return 0; +} + +static void dwc2_complete_non_isoc_xfer_ddma(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, + int chnum, + enum dwc2_halt_status halt_status) +{ + struct list_head *qtd_item, *qtd_tmp; + struct dwc2_qh *qh = chan->qh; + struct dwc2_qtd *qtd = NULL; + int xfer_done; + int desc_num = 0; + + if (chan->halt_status == DWC2_HC_XFER_URB_DEQUEUE) { + list_for_each_entry(qtd, &qh->qtd_list, qtd_list_entry) + qtd->in_process = 0; + return; + } + + list_for_each_safe(qtd_item, qtd_tmp, &qh->qtd_list) { + int i; + + qtd = list_entry(qtd_item, struct dwc2_qtd, qtd_list_entry); + xfer_done = 0; + + for (i = 0; i < qtd->n_desc; i++) { + if (dwc2_process_non_isoc_desc(hsotg, chan, chnum, qtd, + desc_num, halt_status, + &xfer_done)) { + qtd = NULL; + break; + } + desc_num++; + } + } + + if (qh->ep_type != USB_ENDPOINT_XFER_CONTROL) { + /* + * Resetting the data toggle for bulk and interrupt endpoints + * in case of stall. See handle_hc_stall_intr(). + */ + if (halt_status == DWC2_HC_XFER_STALL) + qh->data_toggle = DWC2_HC_PID_DATA0; + else if (qtd) + dwc2_hcd_save_data_toggle(hsotg, chan, chnum, qtd); + } + + if (halt_status == DWC2_HC_XFER_COMPLETE) { + if (chan->hcint & HCINTMSK_NYET) { + /* + * Got a NYET on the last transaction of the transfer. + * It means that the endpoint should be in the PING + * state at the beginning of the next transfer. + */ + qh->ping_state = 1; + } + } +} + +/** + * dwc2_hcd_complete_xfer_ddma() - Scans the descriptor list, updates URB's + * status and calls completion routine for the URB if it's done. Called from + * interrupt handlers. + * + * @hsotg: The HCD state structure for the DWC OTG controller + * @chan: Host channel the transfer is completed on + * @chnum: Index of Host channel registers + * @halt_status: Reason the channel is being halted or just XferComplete + * for isochronous transfers + * + * Releases the channel to be used by other transfers. + * In case of Isochronous endpoint the channel is not halted until the end of + * the session, i.e. QTD list is empty. + * If periodic channel released the FrameList is updated accordingly. + * Calls transaction selection routines to activate pending transfers. + */ +void dwc2_hcd_complete_xfer_ddma(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, int chnum, + enum dwc2_halt_status halt_status) +{ + struct dwc2_qh *qh = chan->qh; + int continue_isoc_xfer = 0; + enum dwc2_transaction_type tr_type; + + if (chan->ep_type == USB_ENDPOINT_XFER_ISOC) { + dwc2_complete_isoc_xfer_ddma(hsotg, chan, halt_status); + + /* Release the channel if halted or session completed */ + if (halt_status != DWC2_HC_XFER_COMPLETE || + list_empty(&qh->qtd_list)) { + /* Halt the channel if session completed */ + if (halt_status == DWC2_HC_XFER_COMPLETE) + dwc2_hc_halt(hsotg, chan, halt_status); + dwc2_release_channel_ddma(hsotg, qh); + dwc2_hcd_qh_unlink(hsotg, qh); + } else { + /* Keep in assigned schedule to continue transfer */ + list_move(&qh->qh_list_entry, + &hsotg->periodic_sched_assigned); + continue_isoc_xfer = 1; + } + /* + * Todo: Consider the case when period exceeds FrameList size. + * Frame Rollover interrupt should be used. + */ + } else { + /* + * Scan descriptor list to complete the URB(s), then release + * the channel + */ + dwc2_complete_non_isoc_xfer_ddma(hsotg, chan, chnum, + halt_status); + dwc2_release_channel_ddma(hsotg, qh); + dwc2_hcd_qh_unlink(hsotg, qh); + + if (!list_empty(&qh->qtd_list)) { + /* + * Add back to inactive non-periodic schedule on normal + * completion + */ + dwc2_hcd_qh_add(hsotg, qh); + } + } + + tr_type = dwc2_hcd_select_transactions(hsotg); + if (tr_type != DWC2_TRANSACTION_NONE || continue_isoc_xfer) { + if (continue_isoc_xfer) { + if (tr_type == DWC2_TRANSACTION_NONE) + tr_type = DWC2_TRANSACTION_PERIODIC; + else if (tr_type == DWC2_TRANSACTION_NON_PERIODIC) + tr_type = DWC2_TRANSACTION_ALL; + } + dwc2_hcd_queue_transactions(hsotg, tr_type); + } +} diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c new file mode 100644 index 0000000..012f17e --- /dev/null +++ b/drivers/usb/dwc2/hcd_intr.c @@ -0,0 +1,2119 @@ +/* + * hcd_intr.c - DesignWare HS OTG Controller host-mode interrupt handling + * + * Copyright (C) 2004-2013 Synopsys, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This file contains the interrupt handlers for Host mode + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/dma-mapping.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/usb.h> + +#include <linux/usb/hcd.h> +#include <linux/usb/ch11.h> + +#include "core.h" +#include "hcd.h" + +/* This function is for debug only */ +static void dwc2_track_missed_sofs(struct dwc2_hsotg *hsotg) +{ +#ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS + u16 curr_frame_number = hsotg->frame_number; + + if (hsotg->frame_num_idx < FRAME_NUM_ARRAY_SIZE) { + if (((hsotg->last_frame_num + 1) & HFNUM_MAX_FRNUM) != + curr_frame_number) { + hsotg->frame_num_array[hsotg->frame_num_idx] = + curr_frame_number; + hsotg->last_frame_num_array[hsotg->frame_num_idx] = + hsotg->last_frame_num; + hsotg->frame_num_idx++; + } + } else if (!hsotg->dumped_frame_num_array) { + int i; + + dev_info(hsotg->dev, "Frame Last Frame\n"); + dev_info(hsotg->dev, "----- ----------\n"); + for (i = 0; i < FRAME_NUM_ARRAY_SIZE; i++) { + dev_info(hsotg->dev, "0x%04x 0x%04x\n", + hsotg->frame_num_array[i], + hsotg->last_frame_num_array[i]); + } + hsotg->dumped_frame_num_array = 1; + } + hsotg->last_frame_num = curr_frame_number; +#endif +} + +static void dwc2_hc_handle_tt_clear(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, + struct dwc2_qtd *qtd) +{ + struct urb *usb_urb; + + if (!chan->qh) + return; + + if (chan->qh->dev_speed == USB_SPEED_HIGH) + return; + + if (!qtd->urb) + return; + + usb_urb = qtd->urb->priv; + if (!usb_urb || !usb_urb->dev || !usb_urb->dev->tt) + return; + + if (qtd->urb->status != -EPIPE && qtd->urb->status != -EREMOTEIO) { + chan->qh->tt_buffer_dirty = 1; + if (usb_hub_clear_tt_buffer(usb_urb)) + /* Clear failed; let's hope things work anyway */ + chan->qh->tt_buffer_dirty = 0; + } +} + +/* + * Handles the start-of-frame interrupt in host mode. Non-periodic + * transactions may be queued to the DWC_otg controller for the current + * (micro)frame. Periodic transactions may be queued to the controller + * for the next (micro)frame. + */ +static void dwc2_sof_intr(struct dwc2_hsotg *hsotg) +{ + struct list_head *qh_entry; + struct dwc2_qh *qh; + enum dwc2_transaction_type tr_type; + +#ifdef DEBUG_SOF + dev_vdbg(hsotg->dev, "--Start of Frame Interrupt--\n"); +#endif + + hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg); + + dwc2_track_missed_sofs(hsotg); + + /* Determine whether any periodic QHs should be executed */ + qh_entry = hsotg->periodic_sched_inactive.next; + while (qh_entry != &hsotg->periodic_sched_inactive) { + qh = list_entry(qh_entry, struct dwc2_qh, qh_list_entry); + qh_entry = qh_entry->next; + if (dwc2_frame_num_le(qh->sched_frame, hsotg->frame_number)) + /* + * Move QH to the ready list to be executed next + * (micro)frame + */ + list_move(&qh->qh_list_entry, + &hsotg->periodic_sched_ready); + } + tr_type = dwc2_hcd_select_transactions(hsotg); + if (tr_type != DWC2_TRANSACTION_NONE) + dwc2_hcd_queue_transactions(hsotg, tr_type); + + /* Clear interrupt */ + writel(GINTSTS_SOF, hsotg->regs + GINTSTS); +} + +/* + * Handles the Rx FIFO Level Interrupt, which indicates that there is + * at least one packet in the Rx FIFO. The packets are moved from the FIFO to + * memory if the DWC_otg controller is operating in Slave mode. + */ +static void dwc2_rx_fifo_level_intr(struct dwc2_hsotg *hsotg) +{ + u32 grxsts, chnum, bcnt, dpid, pktsts; + struct dwc2_host_chan *chan; + + if (dbg_perio()) + dev_vdbg(hsotg->dev, "--RxFIFO Level Interrupt--\n"); + + grxsts = readl(hsotg->regs + GRXSTSP); + chnum = (grxsts & GRXSTS_HCHNUM_MASK) >> GRXSTS_HCHNUM_SHIFT; + chan = hsotg->hc_ptr_array[chnum]; + if (!chan) { + dev_err(hsotg->dev, "Unable to get corresponding channel\n"); + return; + } + + bcnt = (grxsts & GRXSTS_BYTECNT_MASK) >> GRXSTS_BYTECNT_SHIFT; + dpid = (grxsts & GRXSTS_DPID_MASK) >> GRXSTS_DPID_SHIFT; + pktsts = (grxsts & GRXSTS_PKTSTS_MASK) >> GRXSTS_PKTSTS_SHIFT; + + /* Packet Status */ + if (dbg_perio()) { + dev_vdbg(hsotg->dev, " Ch num = %d\n", chnum); + dev_vdbg(hsotg->dev, " Count = %d\n", bcnt); + dev_vdbg(hsotg->dev, " DPID = %d, chan.dpid = %d\n", dpid, + chan->data_pid_start); + dev_vdbg(hsotg->dev, " PStatus = %d\n", pktsts); + } + + switch (pktsts) { + case GRXSTS_PKTSTS_HCHIN: + /* Read the data into the host buffer */ + if (bcnt > 0) { + dwc2_read_packet(hsotg, chan->xfer_buf, bcnt); + + /* Update the HC fields for the next packet received */ + chan->xfer_count += bcnt; + chan->xfer_buf += bcnt; + } + break; + case GRXSTS_PKTSTS_HCHIN_XFER_COMP: + case GRXSTS_PKTSTS_DATATOGGLEERR: + case GRXSTS_PKTSTS_HCHHALTED: + /* Handled in interrupt, just ignore data */ + break; + default: + dev_err(hsotg->dev, + "RxFIFO Level Interrupt: Unknown status %d\n", pktsts); + break; + } +} + +/* + * This interrupt occurs when the non-periodic Tx FIFO is half-empty. More + * data packets may be written to the FIFO for OUT transfers. More requests + * may be written to the non-periodic request queue for IN transfers. This + * interrupt is enabled only in Slave mode. + */ +static void dwc2_np_tx_fifo_empty_intr(struct dwc2_hsotg *hsotg) +{ + dev_vdbg(hsotg->dev, "--Non-Periodic TxFIFO Empty Interrupt--\n"); + dwc2_hcd_queue_transactions(hsotg, DWC2_TRANSACTION_NON_PERIODIC); +} + +/* + * This interrupt occurs when the periodic Tx FIFO is half-empty. More data + * packets may be written to the FIFO for OUT transfers. More requests may be + * written to the periodic request queue for IN transfers. This interrupt is + * enabled only in Slave mode. + */ +static void dwc2_perio_tx_fifo_empty_intr(struct dwc2_hsotg *hsotg) +{ + if (dbg_perio()) + dev_vdbg(hsotg->dev, "--Periodic TxFIFO Empty Interrupt--\n"); + dwc2_hcd_queue_transactions(hsotg, DWC2_TRANSACTION_PERIODIC); +} + +static void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0, + u32 *hprt0_modify) +{ + struct dwc2_core_params *params = hsotg->core_params; + int do_reset = 0; + u32 usbcfg; + u32 prtspd; + u32 hcfg; + u32 fslspclksel; + u32 hfir; + + dev_vdbg(hsotg->dev, "%s(%p)\n", __func__, hsotg); + + /* Every time when port enables calculate HFIR.FrInterval */ + hfir = readl(hsotg->regs + HFIR); + hfir &= ~HFIR_FRINT_MASK; + hfir |= dwc2_calc_frame_interval(hsotg) << HFIR_FRINT_SHIFT & + HFIR_FRINT_MASK; + writel(hfir, hsotg->regs + HFIR); + + /* Check if we need to adjust the PHY clock speed for low power */ + if (!params->host_support_fs_ls_low_power) { + /* Port has been enabled, set the reset change flag */ + hsotg->flags.b.port_reset_change = 1; + return; + } + + usbcfg = readl(hsotg->regs + GUSBCFG); + prtspd = (hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT; + + if (prtspd == HPRT0_SPD_LOW_SPEED || prtspd == HPRT0_SPD_FULL_SPEED) { + /* Low power */ + if (!(usbcfg & GUSBCFG_PHY_LP_CLK_SEL)) { + /* Set PHY low power clock select for FS/LS devices */ + usbcfg |= GUSBCFG_PHY_LP_CLK_SEL; + writel(usbcfg, hsotg->regs + GUSBCFG); + do_reset = 1; + } + + hcfg = readl(hsotg->regs + HCFG); + fslspclksel = (hcfg & HCFG_FSLSPCLKSEL_MASK) >> + HCFG_FSLSPCLKSEL_SHIFT; + + if (prtspd == HPRT0_SPD_LOW_SPEED && + params->host_ls_low_power_phy_clk == + DWC2_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ) { + /* 6 MHZ */ + dev_vdbg(hsotg->dev, + "FS_PHY programming HCFG to 6 MHz\n"); + if (fslspclksel != HCFG_FSLSPCLKSEL_6_MHZ) { + fslspclksel = HCFG_FSLSPCLKSEL_6_MHZ; + hcfg &= ~HCFG_FSLSPCLKSEL_MASK; + hcfg |= fslspclksel << HCFG_FSLSPCLKSEL_SHIFT; + writel(hcfg, hsotg->regs + HCFG); + do_reset = 1; + } + } else { + /* 48 MHZ */ + dev_vdbg(hsotg->dev, + "FS_PHY programming HCFG to 48 MHz\n"); + if (fslspclksel != HCFG_FSLSPCLKSEL_48_MHZ) { + fslspclksel = HCFG_FSLSPCLKSEL_48_MHZ; + hcfg &= ~HCFG_FSLSPCLKSEL_MASK; + hcfg |= fslspclksel << HCFG_FSLSPCLKSEL_SHIFT; + writel(hcfg, hsotg->regs + HCFG); + do_reset = 1; + } + } + } else { + /* Not low power */ + if (usbcfg & GUSBCFG_PHY_LP_CLK_SEL) { + usbcfg &= ~GUSBCFG_PHY_LP_CLK_SEL; + writel(usbcfg, hsotg->regs + GUSBCFG); + do_reset = 1; + } + } + + if (do_reset) { + *hprt0_modify |= HPRT0_RST; + queue_delayed_work(hsotg->wq_otg, &hsotg->reset_work, + msecs_to_jiffies(60)); + } else { + /* Port has been enabled, set the reset change flag */ + hsotg->flags.b.port_reset_change = 1; + } +} + +/* + * There are multiple conditions that can cause a port interrupt. This function + * determines which interrupt conditions have occurred and handles them + * appropriately. + */ +static void dwc2_port_intr(struct dwc2_hsotg *hsotg) +{ + u32 hprt0; + u32 hprt0_modify; + + dev_vdbg(hsotg->dev, "--Port Interrupt--\n"); + + hprt0 = readl(hsotg->regs + HPRT0); + hprt0_modify = hprt0; + + /* + * Clear appropriate bits in HPRT0 to clear the interrupt bit in + * GINTSTS + */ + hprt0_modify &= ~(HPRT0_ENA | HPRT0_CONNDET | HPRT0_ENACHG | + HPRT0_OVRCURRCHG); + + /* + * Port Connect Detected + * Set flag and clear if detected + */ + if (hprt0 & HPRT0_CONNDET) { + dev_vdbg(hsotg->dev, + "--Port Interrupt HPRT0=0x%08x Port Connect Detected--\n", + hprt0); + hsotg->flags.b.port_connect_status_change = 1; + hsotg->flags.b.port_connect_status = 1; + hprt0_modify |= HPRT0_CONNDET; + + /* + * The Hub driver asserts a reset when it sees port connect + * status change flag + */ + } + + /* + * Port Enable Changed + * Clear if detected - Set internal flag if disabled + */ + if (hprt0 & HPRT0_ENACHG) { + dev_vdbg(hsotg->dev, + " --Port Interrupt HPRT0=0x%08x Port Enable Changed (now %d)--\n", + hprt0, !!(hprt0 & HPRT0_ENA)); + hprt0_modify |= HPRT0_ENACHG; + if (hprt0 & HPRT0_ENA) + dwc2_hprt0_enable(hsotg, hprt0, &hprt0_modify); + else + hsotg->flags.b.port_enable_change = 1; + } + + /* Overcurrent Change Interrupt */ + if (hprt0 & HPRT0_OVRCURRCHG) { + dev_vdbg(hsotg->dev, + " --Port Interrupt HPRT0=0x%08x Port Overcurrent Changed--\n", + hprt0); + hsotg->flags.b.port_over_current_change = 1; + hprt0_modify |= HPRT0_OVRCURRCHG; + } + + /* Clear Port Interrupts */ + writel(hprt0_modify, hsotg->regs + HPRT0); +} + +/* + * Gets the actual length of a transfer after the transfer halts. halt_status + * holds the reason for the halt. + * + * For IN transfers where halt_status is DWC2_HC_XFER_COMPLETE, *short_read + * is set to 1 upon return if less than the requested number of bytes were + * transferred. short_read may also be NULL on entry, in which case it remains + * unchanged. + */ +static u32 dwc2_get_actual_xfer_length(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, int chnum, + struct dwc2_qtd *qtd, + enum dwc2_halt_status halt_status, + int *short_read) +{ + u32 hctsiz, count, length; + + hctsiz = readl(hsotg->regs + HCTSIZ(chnum)); + + if (halt_status == DWC2_HC_XFER_COMPLETE) { + if (chan->ep_is_in) { + count = (hctsiz & TSIZ_XFERSIZE_MASK) >> + TSIZ_XFERSIZE_SHIFT; + length = chan->xfer_len - count; + if (short_read != NULL) + *short_read = (count != 0); + } else if (chan->qh->do_split) { + length = qtd->ssplit_out_xfer_count; + } else { + length = chan->xfer_len; + } + } else { + /* + * Must use the hctsiz.pktcnt field to determine how much data + * has been transferred. This field reflects the number of + * packets that have been transferred via the USB. This is + * always an integral number of packets if the transfer was + * halted before its normal completion. (Can't use the + * hctsiz.xfersize field because that reflects the number of + * bytes transferred via the AHB, not the USB). + */ + count = (hctsiz & TSIZ_PKTCNT_MASK) >> TSIZ_PKTCNT_SHIFT; + length = (chan->start_pkt_count - count) * chan->max_packet; + } + + return length; +} + +/** + * dwc2_update_urb_state() - Updates the state of the URB after a Transfer + * Complete interrupt on the host channel. Updates the actual_length field + * of the URB based on the number of bytes transferred via the host channel. + * Sets the URB status if the data transfer is finished. + * + * Return: 1 if the data transfer specified by the URB is completely finished, + * 0 otherwise + */ +static int dwc2_update_urb_state(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, int chnum, + struct dwc2_hcd_urb *urb, + struct dwc2_qtd *qtd) +{ + u32 hctsiz; + int xfer_done = 0; + int short_read = 0; + int xfer_length = dwc2_get_actual_xfer_length(hsotg, chan, chnum, qtd, + DWC2_HC_XFER_COMPLETE, + &short_read); + + if (urb->actual_length + xfer_length > urb->length) { + dev_warn(hsotg->dev, "%s(): trimming xfer length\n", __func__); + xfer_length = urb->length - urb->actual_length; + } + + /* Non DWORD-aligned buffer case handling */ + if (chan->align_buf && xfer_length && chan->ep_is_in) { + dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); + dma_sync_single_for_cpu(hsotg->dev, urb->dma, urb->length, + DMA_FROM_DEVICE); + memcpy(urb->buf + urb->actual_length, chan->qh->dw_align_buf, + xfer_length); + dma_sync_single_for_device(hsotg->dev, urb->dma, urb->length, + DMA_FROM_DEVICE); + } + + dev_vdbg(hsotg->dev, "urb->actual_length=%d xfer_length=%d\n", + urb->actual_length, xfer_length); + urb->actual_length += xfer_length; + + if (xfer_length && chan->ep_type == USB_ENDPOINT_XFER_BULK && + (urb->flags & URB_SEND_ZERO_PACKET) && + urb->actual_length >= urb->length && + !(urb->length % chan->max_packet)) { + xfer_done = 0; + } else if (short_read || urb->actual_length >= urb->length) { + xfer_done = 1; + urb->status = 0; + } + + hctsiz = readl(hsotg->regs + HCTSIZ(chnum)); + dev_vdbg(hsotg->dev, "DWC_otg: %s: %s, channel %d\n", + __func__, (chan->ep_is_in ? "IN" : "OUT"), chnum); + dev_vdbg(hsotg->dev, " chan->xfer_len %d\n", chan->xfer_len); + dev_vdbg(hsotg->dev, " hctsiz.xfersize %d\n", + (hctsiz & TSIZ_XFERSIZE_MASK) >> TSIZ_XFERSIZE_SHIFT); + dev_vdbg(hsotg->dev, " urb->transfer_buffer_length %d\n", urb->length); + dev_vdbg(hsotg->dev, " urb->actual_length %d\n", urb->actual_length); + dev_vdbg(hsotg->dev, " short_read %d, xfer_done %d\n", short_read, + xfer_done); + + return xfer_done; +} + +/* + * Save the starting data toggle for the next transfer. The data toggle is + * saved in the QH for non-control transfers and it's saved in the QTD for + * control transfers. + */ +void dwc2_hcd_save_data_toggle(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, int chnum, + struct dwc2_qtd *qtd) +{ + u32 hctsiz = readl(hsotg->regs + HCTSIZ(chnum)); + u32 pid = (hctsiz & TSIZ_SC_MC_PID_MASK) >> TSIZ_SC_MC_PID_SHIFT; + + if (chan->ep_type != USB_ENDPOINT_XFER_CONTROL) { + if (pid == TSIZ_SC_MC_PID_DATA0) + chan->qh->data_toggle = DWC2_HC_PID_DATA0; + else + chan->qh->data_toggle = DWC2_HC_PID_DATA1; + } else { + if (pid == TSIZ_SC_MC_PID_DATA0) + qtd->data_toggle = DWC2_HC_PID_DATA0; + else + qtd->data_toggle = DWC2_HC_PID_DATA1; + } +} + +/** + * dwc2_update_isoc_urb_state() - Updates the state of an Isochronous URB when + * the transfer is stopped for any reason. The fields of the current entry in + * the frame descriptor array are set based on the transfer state and the input + * halt_status. Completes the Isochronous URB if all the URB frames have been + * completed. + * + * Return: DWC2_HC_XFER_COMPLETE if there are more frames remaining to be + * transferred in the URB. Otherwise return DWC2_HC_XFER_URB_COMPLETE. + */ +static enum dwc2_halt_status dwc2_update_isoc_urb_state( + struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan, + int chnum, struct dwc2_qtd *qtd, + enum dwc2_halt_status halt_status) +{ + struct dwc2_hcd_iso_packet_desc *frame_desc; + struct dwc2_hcd_urb *urb = qtd->urb; + + if (!urb) + return DWC2_HC_XFER_NO_HALT_STATUS; + + frame_desc = &urb->iso_descs[qtd->isoc_frame_index]; + + switch (halt_status) { + case DWC2_HC_XFER_COMPLETE: + frame_desc->status = 0; + frame_desc->actual_length = dwc2_get_actual_xfer_length(hsotg, + chan, chnum, qtd, halt_status, NULL); + + /* Non DWORD-aligned buffer case handling */ + if (chan->align_buf && frame_desc->actual_length && + chan->ep_is_in) { + dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", + __func__); + dma_sync_single_for_cpu(hsotg->dev, urb->dma, + urb->length, DMA_FROM_DEVICE); + memcpy(urb->buf + frame_desc->offset + + qtd->isoc_split_offset, chan->qh->dw_align_buf, + frame_desc->actual_length); + dma_sync_single_for_device(hsotg->dev, urb->dma, + urb->length, + DMA_FROM_DEVICE); + } + break; + case DWC2_HC_XFER_FRAME_OVERRUN: + urb->error_count++; + if (chan->ep_is_in) + frame_desc->status = -ENOSR; + else + frame_desc->status = -ECOMM; + frame_desc->actual_length = 0; + break; + case DWC2_HC_XFER_BABBLE_ERR: + urb->error_count++; + frame_desc->status = -EOVERFLOW; + /* Don't need to update actual_length in this case */ + break; + case DWC2_HC_XFER_XACT_ERR: + urb->error_count++; + frame_desc->status = -EPROTO; + frame_desc->actual_length = dwc2_get_actual_xfer_length(hsotg, + chan, chnum, qtd, halt_status, NULL); + + /* Non DWORD-aligned buffer case handling */ + if (chan->align_buf && frame_desc->actual_length && + chan->ep_is_in) { + dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", + __func__); + dma_sync_single_for_cpu(hsotg->dev, urb->dma, + urb->length, DMA_FROM_DEVICE); + memcpy(urb->buf + frame_desc->offset + + qtd->isoc_split_offset, chan->qh->dw_align_buf, + frame_desc->actual_length); + dma_sync_single_for_device(hsotg->dev, urb->dma, + urb->length, + DMA_FROM_DEVICE); + } + + /* Skip whole frame */ + if (chan->qh->do_split && + chan->ep_type == USB_ENDPOINT_XFER_ISOC && chan->ep_is_in && + hsotg->core_params->dma_enable > 0) { + qtd->complete_split = 0; + qtd->isoc_split_offset = 0; + } + + break; + default: + dev_err(hsotg->dev, "Unhandled halt_status (%d)\n", + halt_status); + break; + } + + if (++qtd->isoc_frame_index == urb->packet_count) { + /* + * urb->status is not used for isoc transfers. The individual + * frame_desc statuses are used instead. + */ + dwc2_host_complete(hsotg, qtd, 0); + halt_status = DWC2_HC_XFER_URB_COMPLETE; + } else { + halt_status = DWC2_HC_XFER_COMPLETE; + } + + return halt_status; +} + +/* + * Frees the first QTD in the QH's list if free_qtd is 1. For non-periodic + * QHs, removes the QH from the active non-periodic schedule. If any QTDs are + * still linked to the QH, the QH is added to the end of the inactive + * non-periodic schedule. For periodic QHs, removes the QH from the periodic + * schedule if no more QTDs are linked to the QH. + */ +static void dwc2_deactivate_qh(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, + int free_qtd) +{ + int continue_split = 0; + struct dwc2_qtd *qtd; + + if (dbg_qh(qh)) + dev_vdbg(hsotg->dev, " %s(%p,%p,%d)\n", __func__, + hsotg, qh, free_qtd); + + if (list_empty(&qh->qtd_list)) { + dev_dbg(hsotg->dev, "## QTD list empty ##\n"); + goto no_qtd; + } + + qtd = list_first_entry(&qh->qtd_list, struct dwc2_qtd, qtd_list_entry); + + if (qtd->complete_split) + continue_split = 1; + else if (qtd->isoc_split_pos == DWC2_HCSPLT_XACTPOS_MID || + qtd->isoc_split_pos == DWC2_HCSPLT_XACTPOS_END) + continue_split = 1; + + if (free_qtd) { + dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh); + continue_split = 0; + } + +no_qtd: + if (qh->channel) + qh->channel->align_buf = 0; + qh->channel = NULL; + dwc2_hcd_qh_deactivate(hsotg, qh, continue_split); +} + +/** + * dwc2_release_channel() - Releases a host channel for use by other transfers + * + * @hsotg: The HCD state structure + * @chan: The host channel to release + * @qtd: The QTD associated with the host channel. This QTD may be + * freed if the transfer is complete or an error has occurred. + * @halt_status: Reason the channel is being released. This status + * determines the actions taken by this function. + * + * Also attempts to select and queue more transactions since at least one host + * channel is available. + */ +static void dwc2_release_channel(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, + struct dwc2_qtd *qtd, + enum dwc2_halt_status halt_status) +{ + enum dwc2_transaction_type tr_type; + u32 haintmsk; + int free_qtd = 0; + + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, " %s: channel %d, halt_status %d\n", + __func__, chan->hc_num, halt_status); + + switch (halt_status) { + case DWC2_HC_XFER_URB_COMPLETE: + free_qtd = 1; + break; + case DWC2_HC_XFER_AHB_ERR: + case DWC2_HC_XFER_STALL: + case DWC2_HC_XFER_BABBLE_ERR: + free_qtd = 1; + break; + case DWC2_HC_XFER_XACT_ERR: + if (qtd && qtd->error_count >= 3) { + dev_vdbg(hsotg->dev, + " Complete URB with transaction error\n"); + free_qtd = 1; + dwc2_host_complete(hsotg, qtd, -EPROTO); + } + break; + case DWC2_HC_XFER_URB_DEQUEUE: + /* + * The QTD has already been removed and the QH has been + * deactivated. Don't want to do anything except release the + * host channel and try to queue more transfers. + */ + goto cleanup; + case DWC2_HC_XFER_PERIODIC_INCOMPLETE: + dev_vdbg(hsotg->dev, " Complete URB with I/O error\n"); + free_qtd = 1; + dwc2_host_complete(hsotg, qtd, -EIO); + break; + case DWC2_HC_XFER_NO_HALT_STATUS: + default: + break; + } + + dwc2_deactivate_qh(hsotg, chan->qh, free_qtd); + +cleanup: + /* + * Release the host channel for use by other transfers. The cleanup + * function clears the channel interrupt enables and conditions, so + * there's no need to clear the Channel Halted interrupt separately. + */ + if (!list_empty(&chan->hc_list_entry)) + list_del(&chan->hc_list_entry); + dwc2_hc_cleanup(hsotg, chan); + list_add_tail(&chan->hc_list_entry, &hsotg->free_hc_list); + + if (hsotg->core_params->uframe_sched > 0) { + hsotg->available_host_channels++; + } else { + switch (chan->ep_type) { + case USB_ENDPOINT_XFER_CONTROL: + case USB_ENDPOINT_XFER_BULK: + hsotg->non_periodic_channels--; + break; + default: + /* + * Don't release reservations for periodic channels + * here. That's done when a periodic transfer is + * descheduled (i.e. when the QH is removed from the + * periodic schedule). + */ + break; + } + } + + haintmsk = readl(hsotg->regs + HAINTMSK); + haintmsk &= ~(1 << chan->hc_num); + writel(haintmsk, hsotg->regs + HAINTMSK); + + /* Try to queue more transfers now that there's a free channel */ + tr_type = dwc2_hcd_select_transactions(hsotg); + if (tr_type != DWC2_TRANSACTION_NONE) + dwc2_hcd_queue_transactions(hsotg, tr_type); +} + +/* + * Halts a host channel. If the channel cannot be halted immediately because + * the request queue is full, this function ensures that the FIFO empty + * interrupt for the appropriate queue is enabled so that the halt request can + * be queued when there is space in the request queue. + * + * This function may also be called in DMA mode. In that case, the channel is + * simply released since the core always halts the channel automatically in + * DMA mode. + */ +static void dwc2_halt_channel(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, struct dwc2_qtd *qtd, + enum dwc2_halt_status halt_status) +{ + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "%s()\n", __func__); + + if (hsotg->core_params->dma_enable > 0) { + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "DMA enabled\n"); + dwc2_release_channel(hsotg, chan, qtd, halt_status); + return; + } + + /* Slave mode processing */ + dwc2_hc_halt(hsotg, chan, halt_status); + + if (chan->halt_on_queue) { + u32 gintmsk; + + dev_vdbg(hsotg->dev, "Halt on queue\n"); + if (chan->ep_type == USB_ENDPOINT_XFER_CONTROL || + chan->ep_type == USB_ENDPOINT_XFER_BULK) { + dev_vdbg(hsotg->dev, "control/bulk\n"); + /* + * Make sure the Non-periodic Tx FIFO empty interrupt + * is enabled so that the non-periodic schedule will + * be processed + */ + gintmsk = readl(hsotg->regs + GINTMSK); + gintmsk |= GINTSTS_NPTXFEMP; + writel(gintmsk, hsotg->regs + GINTMSK); + } else { + dev_vdbg(hsotg->dev, "isoc/intr\n"); + /* + * Move the QH from the periodic queued schedule to + * the periodic assigned schedule. This allows the + * halt to be queued when the periodic schedule is + * processed. + */ + list_move(&chan->qh->qh_list_entry, + &hsotg->periodic_sched_assigned); + + /* + * Make sure the Periodic Tx FIFO Empty interrupt is + * enabled so that the periodic schedule will be + * processed + */ + gintmsk = readl(hsotg->regs + GINTMSK); + gintmsk |= GINTSTS_PTXFEMP; + writel(gintmsk, hsotg->regs + GINTMSK); + } + } +} + +/* + * Performs common cleanup for non-periodic transfers after a Transfer + * Complete interrupt. This function should be called after any endpoint type + * specific handling is finished to release the host channel. + */ +static void dwc2_complete_non_periodic_xfer(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, + int chnum, struct dwc2_qtd *qtd, + enum dwc2_halt_status halt_status) +{ + dev_vdbg(hsotg->dev, "%s()\n", __func__); + + qtd->error_count = 0; + + if (chan->hcint & HCINTMSK_NYET) { + /* + * Got a NYET on the last transaction of the transfer. This + * means that the endpoint should be in the PING state at the + * beginning of the next transfer. + */ + dev_vdbg(hsotg->dev, "got NYET\n"); + chan->qh->ping_state = 1; + } + + /* + * Always halt and release the host channel to make it available for + * more transfers. There may still be more phases for a control + * transfer or more data packets for a bulk transfer at this point, + * but the host channel is still halted. A channel will be reassigned + * to the transfer when the non-periodic schedule is processed after + * the channel is released. This allows transactions to be queued + * properly via dwc2_hcd_queue_transactions, which also enables the + * Tx FIFO Empty interrupt if necessary. + */ + if (chan->ep_is_in) { + /* + * IN transfers in Slave mode require an explicit disable to + * halt the channel. (In DMA mode, this call simply releases + * the channel.) + */ + dwc2_halt_channel(hsotg, chan, qtd, halt_status); + } else { + /* + * The channel is automatically disabled by the core for OUT + * transfers in Slave mode + */ + dwc2_release_channel(hsotg, chan, qtd, halt_status); + } +} + +/* + * Performs common cleanup for periodic transfers after a Transfer Complete + * interrupt. This function should be called after any endpoint type specific + * handling is finished to release the host channel. + */ +static void dwc2_complete_periodic_xfer(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, int chnum, + struct dwc2_qtd *qtd, + enum dwc2_halt_status halt_status) +{ + u32 hctsiz = readl(hsotg->regs + HCTSIZ(chnum)); + + qtd->error_count = 0; + + if (!chan->ep_is_in || (hctsiz & TSIZ_PKTCNT_MASK) == 0) + /* Core halts channel in these cases */ + dwc2_release_channel(hsotg, chan, qtd, halt_status); + else + /* Flush any outstanding requests from the Tx queue */ + dwc2_halt_channel(hsotg, chan, qtd, halt_status); +} + +static int dwc2_xfercomp_isoc_split_in(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, int chnum, + struct dwc2_qtd *qtd) +{ + struct dwc2_hcd_iso_packet_desc *frame_desc; + u32 len; + + if (!qtd->urb) + return 0; + + frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index]; + len = dwc2_get_actual_xfer_length(hsotg, chan, chnum, qtd, + DWC2_HC_XFER_COMPLETE, NULL); + if (!len) { + qtd->complete_split = 0; + qtd->isoc_split_offset = 0; + return 0; + } + + frame_desc->actual_length += len; + + if (chan->align_buf) { + dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); + dma_sync_single_for_cpu(hsotg->dev, qtd->urb->dma, + qtd->urb->length, DMA_FROM_DEVICE); + memcpy(qtd->urb->buf + frame_desc->offset + + qtd->isoc_split_offset, chan->qh->dw_align_buf, len); + dma_sync_single_for_device(hsotg->dev, qtd->urb->dma, + qtd->urb->length, DMA_FROM_DEVICE); + } + + qtd->isoc_split_offset += len; + + if (frame_desc->actual_length >= frame_desc->length) { + frame_desc->status = 0; + qtd->isoc_frame_index++; + qtd->complete_split = 0; + qtd->isoc_split_offset = 0; + } + + if (qtd->isoc_frame_index == qtd->urb->packet_count) { + dwc2_host_complete(hsotg, qtd, 0); + dwc2_release_channel(hsotg, chan, qtd, + DWC2_HC_XFER_URB_COMPLETE); + } else { + dwc2_release_channel(hsotg, chan, qtd, + DWC2_HC_XFER_NO_HALT_STATUS); + } + + return 1; /* Indicates that channel released */ +} + +/* + * Handles a host channel Transfer Complete interrupt. This handler may be + * called in either DMA mode or Slave mode. + */ +static void dwc2_hc_xfercomp_intr(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, int chnum, + struct dwc2_qtd *qtd) +{ + struct dwc2_hcd_urb *urb = qtd->urb; + int pipe_type = dwc2_hcd_get_pipe_type(&urb->pipe_info); + enum dwc2_halt_status halt_status = DWC2_HC_XFER_COMPLETE; + int urb_xfer_done; + + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, + "--Host Channel %d Interrupt: Transfer Complete--\n", + chnum); + + if (hsotg->core_params->dma_desc_enable > 0) { + dwc2_hcd_complete_xfer_ddma(hsotg, chan, chnum, halt_status); + if (pipe_type == USB_ENDPOINT_XFER_ISOC) + /* Do not disable the interrupt, just clear it */ + return; + goto handle_xfercomp_done; + } + + /* Handle xfer complete on CSPLIT */ + if (chan->qh->do_split) { + if (chan->ep_type == USB_ENDPOINT_XFER_ISOC && chan->ep_is_in && + hsotg->core_params->dma_enable > 0) { + if (qtd->complete_split && + dwc2_xfercomp_isoc_split_in(hsotg, chan, chnum, + qtd)) + goto handle_xfercomp_done; + } else { + qtd->complete_split = 0; + } + } + + if (!urb) + goto handle_xfercomp_done; + + /* Update the QTD and URB states */ + switch (pipe_type) { + case USB_ENDPOINT_XFER_CONTROL: + switch (qtd->control_phase) { + case DWC2_CONTROL_SETUP: + if (urb->length > 0) + qtd->control_phase = DWC2_CONTROL_DATA; + else + qtd->control_phase = DWC2_CONTROL_STATUS; + dev_vdbg(hsotg->dev, + " Control setup transaction done\n"); + halt_status = DWC2_HC_XFER_COMPLETE; + break; + case DWC2_CONTROL_DATA: + urb_xfer_done = dwc2_update_urb_state(hsotg, chan, + chnum, urb, qtd); + if (urb_xfer_done) { + qtd->control_phase = DWC2_CONTROL_STATUS; + dev_vdbg(hsotg->dev, + " Control data transfer done\n"); + } else { + dwc2_hcd_save_data_toggle(hsotg, chan, chnum, + qtd); + } + halt_status = DWC2_HC_XFER_COMPLETE; + break; + case DWC2_CONTROL_STATUS: + dev_vdbg(hsotg->dev, " Control transfer complete\n"); + if (urb->status == -EINPROGRESS) + urb->status = 0; + dwc2_host_complete(hsotg, qtd, urb->status); + halt_status = DWC2_HC_XFER_URB_COMPLETE; + break; + } + + dwc2_complete_non_periodic_xfer(hsotg, chan, chnum, qtd, + halt_status); + break; + case USB_ENDPOINT_XFER_BULK: + dev_vdbg(hsotg->dev, " Bulk transfer complete\n"); + urb_xfer_done = dwc2_update_urb_state(hsotg, chan, chnum, urb, + qtd); + if (urb_xfer_done) { + dwc2_host_complete(hsotg, qtd, urb->status); + halt_status = DWC2_HC_XFER_URB_COMPLETE; + } else { + halt_status = DWC2_HC_XFER_COMPLETE; + } + + dwc2_hcd_save_data_toggle(hsotg, chan, chnum, qtd); + dwc2_complete_non_periodic_xfer(hsotg, chan, chnum, qtd, + halt_status); + break; + case USB_ENDPOINT_XFER_INT: + dev_vdbg(hsotg->dev, " Interrupt transfer complete\n"); + urb_xfer_done = dwc2_update_urb_state(hsotg, chan, chnum, urb, + qtd); + + /* + * Interrupt URB is done on the first transfer complete + * interrupt + */ + if (urb_xfer_done) { + dwc2_host_complete(hsotg, qtd, urb->status); + halt_status = DWC2_HC_XFER_URB_COMPLETE; + } else { + halt_status = DWC2_HC_XFER_COMPLETE; + } + + dwc2_hcd_save_data_toggle(hsotg, chan, chnum, qtd); + dwc2_complete_periodic_xfer(hsotg, chan, chnum, qtd, + halt_status); + break; + case USB_ENDPOINT_XFER_ISOC: + if (dbg_perio()) + dev_vdbg(hsotg->dev, " Isochronous transfer complete\n"); + if (qtd->isoc_split_pos == DWC2_HCSPLT_XACTPOS_ALL) + halt_status = dwc2_update_isoc_urb_state(hsotg, chan, + chnum, qtd, DWC2_HC_XFER_COMPLETE); + dwc2_complete_periodic_xfer(hsotg, chan, chnum, qtd, + halt_status); + break; + } + +handle_xfercomp_done: + disable_hc_int(hsotg, chnum, HCINTMSK_XFERCOMPL); +} + +/* + * Handles a host channel STALL interrupt. This handler may be called in + * either DMA mode or Slave mode. + */ +static void dwc2_hc_stall_intr(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, int chnum, + struct dwc2_qtd *qtd) +{ + struct dwc2_hcd_urb *urb = qtd->urb; + int pipe_type = dwc2_hcd_get_pipe_type(&urb->pipe_info); + + dev_dbg(hsotg->dev, "--Host Channel %d Interrupt: STALL Received--\n", + chnum); + + if (hsotg->core_params->dma_desc_enable > 0) { + dwc2_hcd_complete_xfer_ddma(hsotg, chan, chnum, + DWC2_HC_XFER_STALL); + goto handle_stall_done; + } + + if (!urb) + goto handle_stall_halt; + + if (pipe_type == USB_ENDPOINT_XFER_CONTROL) + dwc2_host_complete(hsotg, qtd, -EPIPE); + + if (pipe_type == USB_ENDPOINT_XFER_BULK || + pipe_type == USB_ENDPOINT_XFER_INT) { + dwc2_host_complete(hsotg, qtd, -EPIPE); + /* + * USB protocol requires resetting the data toggle for bulk + * and interrupt endpoints when a CLEAR_FEATURE(ENDPOINT_HALT) + * setup command is issued to the endpoint. Anticipate the + * CLEAR_FEATURE command since a STALL has occurred and reset + * the data toggle now. + */ + chan->qh->data_toggle = 0; + } + +handle_stall_halt: + dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_STALL); + +handle_stall_done: + disable_hc_int(hsotg, chnum, HCINTMSK_STALL); +} + +/* + * Updates the state of the URB when a transfer has been stopped due to an + * abnormal condition before the transfer completes. Modifies the + * actual_length field of the URB to reflect the number of bytes that have + * actually been transferred via the host channel. + */ +static void dwc2_update_urb_state_abn(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, int chnum, + struct dwc2_hcd_urb *urb, + struct dwc2_qtd *qtd, + enum dwc2_halt_status halt_status) +{ + u32 xfer_length = dwc2_get_actual_xfer_length(hsotg, chan, chnum, + qtd, halt_status, NULL); + u32 hctsiz; + + if (urb->actual_length + xfer_length > urb->length) { + dev_warn(hsotg->dev, "%s(): trimming xfer length\n", __func__); + xfer_length = urb->length - urb->actual_length; + } + + /* Non DWORD-aligned buffer case handling */ + if (chan->align_buf && xfer_length && chan->ep_is_in) { + dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); + dma_sync_single_for_cpu(hsotg->dev, urb->dma, urb->length, + DMA_FROM_DEVICE); + memcpy(urb->buf + urb->actual_length, chan->qh->dw_align_buf, + xfer_length); + dma_sync_single_for_device(hsotg->dev, urb->dma, urb->length, + DMA_FROM_DEVICE); + } + + urb->actual_length += xfer_length; + + hctsiz = readl(hsotg->regs + HCTSIZ(chnum)); + dev_vdbg(hsotg->dev, "DWC_otg: %s: %s, channel %d\n", + __func__, (chan->ep_is_in ? "IN" : "OUT"), chnum); + dev_vdbg(hsotg->dev, " chan->start_pkt_count %d\n", + chan->start_pkt_count); + dev_vdbg(hsotg->dev, " hctsiz.pktcnt %d\n", + (hctsiz & TSIZ_PKTCNT_MASK) >> TSIZ_PKTCNT_SHIFT); + dev_vdbg(hsotg->dev, " chan->max_packet %d\n", chan->max_packet); + dev_vdbg(hsotg->dev, " bytes_transferred %d\n", + xfer_length); + dev_vdbg(hsotg->dev, " urb->actual_length %d\n", + urb->actual_length); + dev_vdbg(hsotg->dev, " urb->transfer_buffer_length %d\n", + urb->length); +} + +/* + * Handles a host channel NAK interrupt. This handler may be called in either + * DMA mode or Slave mode. + */ +static void dwc2_hc_nak_intr(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, int chnum, + struct dwc2_qtd *qtd) +{ + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "--Host Channel %d Interrupt: NAK Received--\n", + chnum); + + /* + * Handle NAK for IN/OUT SSPLIT/CSPLIT transfers, bulk, control, and + * interrupt. Re-start the SSPLIT transfer. + */ + if (chan->do_split) { + if (chan->complete_split) + qtd->error_count = 0; + qtd->complete_split = 0; + dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_NAK); + goto handle_nak_done; + } + + switch (dwc2_hcd_get_pipe_type(&qtd->urb->pipe_info)) { + case USB_ENDPOINT_XFER_CONTROL: + case USB_ENDPOINT_XFER_BULK: + if (hsotg->core_params->dma_enable > 0 && chan->ep_is_in) { + /* + * NAK interrupts are enabled on bulk/control IN + * transfers in DMA mode for the sole purpose of + * resetting the error count after a transaction error + * occurs. The core will continue transferring data. + */ + qtd->error_count = 0; + break; + } + + /* + * NAK interrupts normally occur during OUT transfers in DMA + * or Slave mode. For IN transfers, more requests will be + * queued as request queue space is available. + */ + qtd->error_count = 0; + + if (!chan->qh->ping_state) { + dwc2_update_urb_state_abn(hsotg, chan, chnum, qtd->urb, + qtd, DWC2_HC_XFER_NAK); + dwc2_hcd_save_data_toggle(hsotg, chan, chnum, qtd); + + if (chan->speed == USB_SPEED_HIGH) + chan->qh->ping_state = 1; + } + + /* + * Halt the channel so the transfer can be re-started from + * the appropriate point or the PING protocol will + * start/continue + */ + dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_NAK); + break; + case USB_ENDPOINT_XFER_INT: + qtd->error_count = 0; + dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_NAK); + break; + case USB_ENDPOINT_XFER_ISOC: + /* Should never get called for isochronous transfers */ + dev_err(hsotg->dev, "NACK interrupt for ISOC transfer\n"); + break; + } + +handle_nak_done: + disable_hc_int(hsotg, chnum, HCINTMSK_NAK); +} + +/* + * Handles a host channel ACK interrupt. This interrupt is enabled when + * performing the PING protocol in Slave mode, when errors occur during + * either Slave mode or DMA mode, and during Start Split transactions. + */ +static void dwc2_hc_ack_intr(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, int chnum, + struct dwc2_qtd *qtd) +{ + struct dwc2_hcd_iso_packet_desc *frame_desc; + + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "--Host Channel %d Interrupt: ACK Received--\n", + chnum); + + if (chan->do_split) { + /* Handle ACK on SSPLIT. ACK should not occur in CSPLIT. */ + if (!chan->ep_is_in && + chan->data_pid_start != DWC2_HC_PID_SETUP) + qtd->ssplit_out_xfer_count = chan->xfer_len; + + if (chan->ep_type != USB_ENDPOINT_XFER_ISOC || chan->ep_is_in) { + qtd->complete_split = 1; + dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_ACK); + } else { + /* ISOC OUT */ + switch (chan->xact_pos) { + case DWC2_HCSPLT_XACTPOS_ALL: + break; + case DWC2_HCSPLT_XACTPOS_END: + qtd->isoc_split_pos = DWC2_HCSPLT_XACTPOS_ALL; + qtd->isoc_split_offset = 0; + break; + case DWC2_HCSPLT_XACTPOS_BEGIN: + case DWC2_HCSPLT_XACTPOS_MID: + /* + * For BEGIN or MID, calculate the length for + * the next microframe to determine the correct + * SSPLIT token, either MID or END + */ + frame_desc = &qtd->urb->iso_descs[ + qtd->isoc_frame_index]; + qtd->isoc_split_offset += 188; + + if (frame_desc->length - qtd->isoc_split_offset + <= 188) + qtd->isoc_split_pos = + DWC2_HCSPLT_XACTPOS_END; + else + qtd->isoc_split_pos = + DWC2_HCSPLT_XACTPOS_MID; + break; + } + } + } else { + qtd->error_count = 0; + + if (chan->qh->ping_state) { + chan->qh->ping_state = 0; + /* + * Halt the channel so the transfer can be re-started + * from the appropriate point. This only happens in + * Slave mode. In DMA mode, the ping_state is cleared + * when the transfer is started because the core + * automatically executes the PING, then the transfer. + */ + dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_ACK); + } + } + + /* + * If the ACK occurred when _not_ in the PING state, let the channel + * continue transferring data after clearing the error count + */ + disable_hc_int(hsotg, chnum, HCINTMSK_ACK); +} + +/* + * Handles a host channel NYET interrupt. This interrupt should only occur on + * Bulk and Control OUT endpoints and for complete split transactions. If a + * NYET occurs at the same time as a Transfer Complete interrupt, it is + * handled in the xfercomp interrupt handler, not here. This handler may be + * called in either DMA mode or Slave mode. + */ +static void dwc2_hc_nyet_intr(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, int chnum, + struct dwc2_qtd *qtd) +{ + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "--Host Channel %d Interrupt: NYET Received--\n", + chnum); + + /* + * NYET on CSPLIT + * re-do the CSPLIT immediately on non-periodic + */ + if (chan->do_split && chan->complete_split) { + if (chan->ep_is_in && chan->ep_type == USB_ENDPOINT_XFER_ISOC && + hsotg->core_params->dma_enable > 0) { + qtd->complete_split = 0; + qtd->isoc_split_offset = 0; + qtd->isoc_frame_index++; + if (qtd->urb && + qtd->isoc_frame_index == qtd->urb->packet_count) { + dwc2_host_complete(hsotg, qtd, 0); + dwc2_release_channel(hsotg, chan, qtd, + DWC2_HC_XFER_URB_COMPLETE); + } else { + dwc2_release_channel(hsotg, chan, qtd, + DWC2_HC_XFER_NO_HALT_STATUS); + } + goto handle_nyet_done; + } + + if (chan->ep_type == USB_ENDPOINT_XFER_INT || + chan->ep_type == USB_ENDPOINT_XFER_ISOC) { + int frnum = dwc2_hcd_get_frame_number(hsotg); + + if (dwc2_full_frame_num(frnum) != + dwc2_full_frame_num(chan->qh->sched_frame)) { + /* + * No longer in the same full speed frame. + * Treat this as a transaction error. + */ +#if 0 + /* + * Todo: Fix system performance so this can + * be treated as an error. Right now complete + * splits cannot be scheduled precisely enough + * due to other system activity, so this error + * occurs regularly in Slave mode. + */ + qtd->error_count++; +#endif + qtd->complete_split = 0; + dwc2_halt_channel(hsotg, chan, qtd, + DWC2_HC_XFER_XACT_ERR); + /* Todo: add support for isoc release */ + goto handle_nyet_done; + } + } + + dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_NYET); + goto handle_nyet_done; + } + + chan->qh->ping_state = 1; + qtd->error_count = 0; + + dwc2_update_urb_state_abn(hsotg, chan, chnum, qtd->urb, qtd, + DWC2_HC_XFER_NYET); + dwc2_hcd_save_data_toggle(hsotg, chan, chnum, qtd); + + /* + * Halt the channel and re-start the transfer so the PING protocol + * will start + */ + dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_NYET); + +handle_nyet_done: + disable_hc_int(hsotg, chnum, HCINTMSK_NYET); +} + +/* + * Handles a host channel babble interrupt. This handler may be called in + * either DMA mode or Slave mode. + */ +static void dwc2_hc_babble_intr(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, int chnum, + struct dwc2_qtd *qtd) +{ + dev_dbg(hsotg->dev, "--Host Channel %d Interrupt: Babble Error--\n", + chnum); + + dwc2_hc_handle_tt_clear(hsotg, chan, qtd); + + if (hsotg->core_params->dma_desc_enable > 0) { + dwc2_hcd_complete_xfer_ddma(hsotg, chan, chnum, + DWC2_HC_XFER_BABBLE_ERR); + goto disable_int; + } + + if (chan->ep_type != USB_ENDPOINT_XFER_ISOC) { + dwc2_host_complete(hsotg, qtd, -EOVERFLOW); + dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_BABBLE_ERR); + } else { + enum dwc2_halt_status halt_status; + + halt_status = dwc2_update_isoc_urb_state(hsotg, chan, chnum, + qtd, DWC2_HC_XFER_BABBLE_ERR); + dwc2_halt_channel(hsotg, chan, qtd, halt_status); + } + +disable_int: + disable_hc_int(hsotg, chnum, HCINTMSK_BBLERR); +} + +/* + * Handles a host channel AHB error interrupt. This handler is only called in + * DMA mode. + */ +static void dwc2_hc_ahberr_intr(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, int chnum, + struct dwc2_qtd *qtd) +{ + struct dwc2_hcd_urb *urb = qtd->urb; + char *pipetype, *speed; + u32 hcchar; + u32 hcsplt; + u32 hctsiz; + u32 hc_dma; + + dev_dbg(hsotg->dev, "--Host Channel %d Interrupt: AHB Error--\n", + chnum); + + if (!urb) + goto handle_ahberr_halt; + + dwc2_hc_handle_tt_clear(hsotg, chan, qtd); + + hcchar = readl(hsotg->regs + HCCHAR(chnum)); + hcsplt = readl(hsotg->regs + HCSPLT(chnum)); + hctsiz = readl(hsotg->regs + HCTSIZ(chnum)); + hc_dma = readl(hsotg->regs + HCDMA(chnum)); + + dev_err(hsotg->dev, "AHB ERROR, Channel %d\n", chnum); + dev_err(hsotg->dev, " hcchar 0x%08x, hcsplt 0x%08x\n", hcchar, hcsplt); + dev_err(hsotg->dev, " hctsiz 0x%08x, hc_dma 0x%08x\n", hctsiz, hc_dma); + dev_err(hsotg->dev, " Device address: %d\n", + dwc2_hcd_get_dev_addr(&urb->pipe_info)); + dev_err(hsotg->dev, " Endpoint: %d, %s\n", + dwc2_hcd_get_ep_num(&urb->pipe_info), + dwc2_hcd_is_pipe_in(&urb->pipe_info) ? "IN" : "OUT"); + + switch (dwc2_hcd_get_pipe_type(&urb->pipe_info)) { + case USB_ENDPOINT_XFER_CONTROL: + pipetype = "CONTROL"; + break; + case USB_ENDPOINT_XFER_BULK: + pipetype = "BULK"; + break; + case USB_ENDPOINT_XFER_INT: + pipetype = "INTERRUPT"; + break; + case USB_ENDPOINT_XFER_ISOC: + pipetype = "ISOCHRONOUS"; + break; + default: + pipetype = "UNKNOWN"; + break; + } + + dev_err(hsotg->dev, " Endpoint type: %s\n", pipetype); + + switch (chan->speed) { + case USB_SPEED_HIGH: + speed = "HIGH"; + break; + case USB_SPEED_FULL: + speed = "FULL"; + break; + case USB_SPEED_LOW: + speed = "LOW"; + break; + default: + speed = "UNKNOWN"; + break; + } + + dev_err(hsotg->dev, " Speed: %s\n", speed); + + dev_err(hsotg->dev, " Max packet size: %d\n", + dwc2_hcd_get_mps(&urb->pipe_info)); + dev_err(hsotg->dev, " Data buffer length: %d\n", urb->length); + dev_err(hsotg->dev, " Transfer buffer: %p, Transfer DMA: %08lx\n", + urb->buf, (unsigned long)urb->dma); + dev_err(hsotg->dev, " Setup buffer: %p, Setup DMA: %08lx\n", + urb->setup_packet, (unsigned long)urb->setup_dma); + dev_err(hsotg->dev, " Interval: %d\n", urb->interval); + + /* Core halts the channel for Descriptor DMA mode */ + if (hsotg->core_params->dma_desc_enable > 0) { + dwc2_hcd_complete_xfer_ddma(hsotg, chan, chnum, + DWC2_HC_XFER_AHB_ERR); + goto handle_ahberr_done; + } + + dwc2_host_complete(hsotg, qtd, -EIO); + +handle_ahberr_halt: + /* + * Force a channel halt. Don't call dwc2_halt_channel because that won't + * write to the HCCHARn register in DMA mode to force the halt. + */ + dwc2_hc_halt(hsotg, chan, DWC2_HC_XFER_AHB_ERR); + +handle_ahberr_done: + disable_hc_int(hsotg, chnum, HCINTMSK_AHBERR); +} + +/* + * Handles a host channel transaction error interrupt. This handler may be + * called in either DMA mode or Slave mode. + */ +static void dwc2_hc_xacterr_intr(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, int chnum, + struct dwc2_qtd *qtd) +{ + dev_dbg(hsotg->dev, + "--Host Channel %d Interrupt: Transaction Error--\n", chnum); + + dwc2_hc_handle_tt_clear(hsotg, chan, qtd); + + if (hsotg->core_params->dma_desc_enable > 0) { + dwc2_hcd_complete_xfer_ddma(hsotg, chan, chnum, + DWC2_HC_XFER_XACT_ERR); + goto handle_xacterr_done; + } + + switch (dwc2_hcd_get_pipe_type(&qtd->urb->pipe_info)) { + case USB_ENDPOINT_XFER_CONTROL: + case USB_ENDPOINT_XFER_BULK: + qtd->error_count++; + if (!chan->qh->ping_state) { + + dwc2_update_urb_state_abn(hsotg, chan, chnum, qtd->urb, + qtd, DWC2_HC_XFER_XACT_ERR); + dwc2_hcd_save_data_toggle(hsotg, chan, chnum, qtd); + if (!chan->ep_is_in && chan->speed == USB_SPEED_HIGH) + chan->qh->ping_state = 1; + } + + /* + * Halt the channel so the transfer can be re-started from + * the appropriate point or the PING protocol will start + */ + dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_XACT_ERR); + break; + case USB_ENDPOINT_XFER_INT: + qtd->error_count++; + if (chan->do_split && chan->complete_split) + qtd->complete_split = 0; + dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_XACT_ERR); + break; + case USB_ENDPOINT_XFER_ISOC: + { + enum dwc2_halt_status halt_status; + + halt_status = dwc2_update_isoc_urb_state(hsotg, chan, + chnum, qtd, DWC2_HC_XFER_XACT_ERR); + dwc2_halt_channel(hsotg, chan, qtd, halt_status); + } + break; + } + +handle_xacterr_done: + disable_hc_int(hsotg, chnum, HCINTMSK_XACTERR); +} + +/* + * Handles a host channel frame overrun interrupt. This handler may be called + * in either DMA mode or Slave mode. + */ +static void dwc2_hc_frmovrun_intr(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, int chnum, + struct dwc2_qtd *qtd) +{ + enum dwc2_halt_status halt_status; + + if (dbg_hc(chan)) + dev_dbg(hsotg->dev, "--Host Channel %d Interrupt: Frame Overrun--\n", + chnum); + + dwc2_hc_handle_tt_clear(hsotg, chan, qtd); + + switch (dwc2_hcd_get_pipe_type(&qtd->urb->pipe_info)) { + case USB_ENDPOINT_XFER_CONTROL: + case USB_ENDPOINT_XFER_BULK: + break; + case USB_ENDPOINT_XFER_INT: + dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_FRAME_OVERRUN); + break; + case USB_ENDPOINT_XFER_ISOC: + halt_status = dwc2_update_isoc_urb_state(hsotg, chan, chnum, + qtd, DWC2_HC_XFER_FRAME_OVERRUN); + dwc2_halt_channel(hsotg, chan, qtd, halt_status); + break; + } + + disable_hc_int(hsotg, chnum, HCINTMSK_FRMOVRUN); +} + +/* + * Handles a host channel data toggle error interrupt. This handler may be + * called in either DMA mode or Slave mode. + */ +static void dwc2_hc_datatglerr_intr(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, int chnum, + struct dwc2_qtd *qtd) +{ + dev_dbg(hsotg->dev, + "--Host Channel %d Interrupt: Data Toggle Error--\n", chnum); + + if (chan->ep_is_in) + qtd->error_count = 0; + else + dev_err(hsotg->dev, + "Data Toggle Error on OUT transfer, channel %d\n", + chnum); + + dwc2_hc_handle_tt_clear(hsotg, chan, qtd); + disable_hc_int(hsotg, chnum, HCINTMSK_DATATGLERR); +} + +/* + * For debug only. It checks that a valid halt status is set and that + * HCCHARn.chdis is clear. If there's a problem, corrective action is + * taken and a warning is issued. + * + * Return: true if halt status is ok, false otherwise + */ +static bool dwc2_halt_status_ok(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, int chnum, + struct dwc2_qtd *qtd) +{ +#ifdef DEBUG + u32 hcchar; + u32 hctsiz; + u32 hcintmsk; + u32 hcsplt; + + if (chan->halt_status == DWC2_HC_XFER_NO_HALT_STATUS) { + /* + * This code is here only as a check. This condition should + * never happen. Ignore the halt if it does occur. + */ + hcchar = readl(hsotg->regs + HCCHAR(chnum)); + hctsiz = readl(hsotg->regs + HCTSIZ(chnum)); + hcintmsk = readl(hsotg->regs + HCINTMSK(chnum)); + hcsplt = readl(hsotg->regs + HCSPLT(chnum)); + dev_dbg(hsotg->dev, + "%s: chan->halt_status DWC2_HC_XFER_NO_HALT_STATUS,\n", + __func__); + dev_dbg(hsotg->dev, + "channel %d, hcchar 0x%08x, hctsiz 0x%08x,\n", + chnum, hcchar, hctsiz); + dev_dbg(hsotg->dev, + "hcint 0x%08x, hcintmsk 0x%08x, hcsplt 0x%08x,\n", + chan->hcint, hcintmsk, hcsplt); + if (qtd) + dev_dbg(hsotg->dev, "qtd->complete_split %d\n", + qtd->complete_split); + dev_warn(hsotg->dev, + "%s: no halt status, channel %d, ignoring interrupt\n", + __func__, chnum); + return false; + } + + /* + * This code is here only as a check. hcchar.chdis should never be set + * when the halt interrupt occurs. Halt the channel again if it does + * occur. + */ + hcchar = readl(hsotg->regs + HCCHAR(chnum)); + if (hcchar & HCCHAR_CHDIS) { + dev_warn(hsotg->dev, + "%s: hcchar.chdis set unexpectedly, hcchar 0x%08x, trying to halt again\n", + __func__, hcchar); + chan->halt_pending = 0; + dwc2_halt_channel(hsotg, chan, qtd, chan->halt_status); + return false; + } +#endif + + return true; +} + +/* + * Handles a host Channel Halted interrupt in DMA mode. This handler + * determines the reason the channel halted and proceeds accordingly. + */ +static void dwc2_hc_chhltd_intr_dma(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, int chnum, + struct dwc2_qtd *qtd) +{ + u32 hcintmsk; + int out_nak_enh = 0; + + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, + "--Host Channel %d Interrupt: DMA Channel Halted--\n", + chnum); + + /* + * For core with OUT NAK enhancement, the flow for high-speed + * CONTROL/BULK OUT is handled a little differently + */ + if (hsotg->hw_params.snpsid >= DWC2_CORE_REV_2_71a) { + if (chan->speed == USB_SPEED_HIGH && !chan->ep_is_in && + (chan->ep_type == USB_ENDPOINT_XFER_CONTROL || + chan->ep_type == USB_ENDPOINT_XFER_BULK)) { + out_nak_enh = 1; + } + } + + if (chan->halt_status == DWC2_HC_XFER_URB_DEQUEUE || + (chan->halt_status == DWC2_HC_XFER_AHB_ERR && + hsotg->core_params->dma_desc_enable <= 0)) { + if (hsotg->core_params->dma_desc_enable > 0) + dwc2_hcd_complete_xfer_ddma(hsotg, chan, chnum, + chan->halt_status); + else + /* + * Just release the channel. A dequeue can happen on a + * transfer timeout. In the case of an AHB Error, the + * channel was forced to halt because there's no way to + * gracefully recover. + */ + dwc2_release_channel(hsotg, chan, qtd, + chan->halt_status); + return; + } + + hcintmsk = readl(hsotg->regs + HCINTMSK(chnum)); + + if (chan->hcint & HCINTMSK_XFERCOMPL) { + /* + * Todo: This is here because of a possible hardware bug. Spec + * says that on SPLIT-ISOC OUT transfers in DMA mode that a HALT + * interrupt w/ACK bit set should occur, but I only see the + * XFERCOMP bit, even with it masked out. This is a workaround + * for that behavior. Should fix this when hardware is fixed. + */ + if (chan->ep_type == USB_ENDPOINT_XFER_ISOC && !chan->ep_is_in) + dwc2_hc_ack_intr(hsotg, chan, chnum, qtd); + dwc2_hc_xfercomp_intr(hsotg, chan, chnum, qtd); + } else if (chan->hcint & HCINTMSK_STALL) { + dwc2_hc_stall_intr(hsotg, chan, chnum, qtd); + } else if ((chan->hcint & HCINTMSK_XACTERR) && + hsotg->core_params->dma_desc_enable <= 0) { + if (out_nak_enh) { + if (chan->hcint & + (HCINTMSK_NYET | HCINTMSK_NAK | HCINTMSK_ACK)) { + dev_vdbg(hsotg->dev, + "XactErr with NYET/NAK/ACK\n"); + qtd->error_count = 0; + } else { + dev_vdbg(hsotg->dev, + "XactErr without NYET/NAK/ACK\n"); + } + } + + /* + * Must handle xacterr before nak or ack. Could get a xacterr + * at the same time as either of these on a BULK/CONTROL OUT + * that started with a PING. The xacterr takes precedence. + */ + dwc2_hc_xacterr_intr(hsotg, chan, chnum, qtd); + } else if ((chan->hcint & HCINTMSK_XCS_XACT) && + hsotg->core_params->dma_desc_enable > 0) { + dwc2_hc_xacterr_intr(hsotg, chan, chnum, qtd); + } else if ((chan->hcint & HCINTMSK_AHBERR) && + hsotg->core_params->dma_desc_enable > 0) { + dwc2_hc_ahberr_intr(hsotg, chan, chnum, qtd); + } else if (chan->hcint & HCINTMSK_BBLERR) { + dwc2_hc_babble_intr(hsotg, chan, chnum, qtd); + } else if (chan->hcint & HCINTMSK_FRMOVRUN) { + dwc2_hc_frmovrun_intr(hsotg, chan, chnum, qtd); + } else if (!out_nak_enh) { + if (chan->hcint & HCINTMSK_NYET) { + /* + * Must handle nyet before nak or ack. Could get a nyet + * at the same time as either of those on a BULK/CONTROL + * OUT that started with a PING. The nyet takes + * precedence. + */ + dwc2_hc_nyet_intr(hsotg, chan, chnum, qtd); + } else if ((chan->hcint & HCINTMSK_NAK) && + !(hcintmsk & HCINTMSK_NAK)) { + /* + * If nak is not masked, it's because a non-split IN + * transfer is in an error state. In that case, the nak + * is handled by the nak interrupt handler, not here. + * Handle nak here for BULK/CONTROL OUT transfers, which + * halt on a NAK to allow rewinding the buffer pointer. + */ + dwc2_hc_nak_intr(hsotg, chan, chnum, qtd); + } else if ((chan->hcint & HCINTMSK_ACK) && + !(hcintmsk & HCINTMSK_ACK)) { + /* + * If ack is not masked, it's because a non-split IN + * transfer is in an error state. In that case, the ack + * is handled by the ack interrupt handler, not here. + * Handle ack here for split transfers. Start splits + * halt on ACK. + */ + dwc2_hc_ack_intr(hsotg, chan, chnum, qtd); + } else { + if (chan->ep_type == USB_ENDPOINT_XFER_INT || + chan->ep_type == USB_ENDPOINT_XFER_ISOC) { + /* + * A periodic transfer halted with no other + * channel interrupts set. Assume it was halted + * by the core because it could not be completed + * in its scheduled (micro)frame. + */ + dev_dbg(hsotg->dev, + "%s: Halt channel %d (assume incomplete periodic transfer)\n", + __func__, chnum); + dwc2_halt_channel(hsotg, chan, qtd, + DWC2_HC_XFER_PERIODIC_INCOMPLETE); + } else { + dev_err(hsotg->dev, + "%s: Channel %d - ChHltd set, but reason is unknown\n", + __func__, chnum); + dev_err(hsotg->dev, + "hcint 0x%08x, intsts 0x%08x\n", + chan->hcint, + readl(hsotg->regs + GINTSTS)); + } + } + } else { + dev_info(hsotg->dev, + "NYET/NAK/ACK/other in non-error case, 0x%08x\n", + chan->hcint); + } +} + +/* + * Handles a host channel Channel Halted interrupt + * + * In slave mode, this handler is called only when the driver specifically + * requests a halt. This occurs during handling other host channel interrupts + * (e.g. nak, xacterr, stall, nyet, etc.). + * + * In DMA mode, this is the interrupt that occurs when the core has finished + * processing a transfer on a channel. Other host channel interrupts (except + * ahberr) are disabled in DMA mode. + */ +static void dwc2_hc_chhltd_intr(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, int chnum, + struct dwc2_qtd *qtd) +{ + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "--Host Channel %d Interrupt: Channel Halted--\n", + chnum); + + if (hsotg->core_params->dma_enable > 0) { + dwc2_hc_chhltd_intr_dma(hsotg, chan, chnum, qtd); + } else { + if (!dwc2_halt_status_ok(hsotg, chan, chnum, qtd)) + return; + dwc2_release_channel(hsotg, chan, qtd, chan->halt_status); + } +} + +/* Handles interrupt for a specific Host Channel */ +static void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum) +{ + struct dwc2_qtd *qtd; + struct dwc2_host_chan *chan; + u32 hcint, hcintmsk; + + chan = hsotg->hc_ptr_array[chnum]; + + hcint = readl(hsotg->regs + HCINT(chnum)); + hcintmsk = readl(hsotg->regs + HCINTMSK(chnum)); + if (!chan) { + dev_err(hsotg->dev, "## hc_ptr_array for channel is NULL ##\n"); + writel(hcint, hsotg->regs + HCINT(chnum)); + return; + } + + if (dbg_hc(chan)) { + dev_vdbg(hsotg->dev, "--Host Channel Interrupt--, Channel %d\n", + chnum); + dev_vdbg(hsotg->dev, + " hcint 0x%08x, hcintmsk 0x%08x, hcint&hcintmsk 0x%08x\n", + hcint, hcintmsk, hcint & hcintmsk); + } + + writel(hcint, hsotg->regs + HCINT(chnum)); + chan->hcint = hcint; + hcint &= hcintmsk; + + /* + * If the channel was halted due to a dequeue, the qtd list might + * be empty or at least the first entry will not be the active qtd. + * In this case, take a shortcut and just release the channel. + */ + if (chan->halt_status == DWC2_HC_XFER_URB_DEQUEUE) { + /* + * If the channel was halted, this should be the only + * interrupt unmasked + */ + WARN_ON(hcint != HCINTMSK_CHHLTD); + if (hsotg->core_params->dma_desc_enable > 0) + dwc2_hcd_complete_xfer_ddma(hsotg, chan, chnum, + chan->halt_status); + else + dwc2_release_channel(hsotg, chan, NULL, + chan->halt_status); + return; + } + + if (list_empty(&chan->qh->qtd_list)) { + /* + * TODO: Will this ever happen with the + * DWC2_HC_XFER_URB_DEQUEUE handling above? + */ + dev_dbg(hsotg->dev, "## no QTD queued for channel %d ##\n", + chnum); + dev_dbg(hsotg->dev, + " hcint 0x%08x, hcintmsk 0x%08x, hcint&hcintmsk 0x%08x\n", + chan->hcint, hcintmsk, hcint); + chan->halt_status = DWC2_HC_XFER_NO_HALT_STATUS; + disable_hc_int(hsotg, chnum, HCINTMSK_CHHLTD); + chan->hcint = 0; + return; + } + + qtd = list_first_entry(&chan->qh->qtd_list, struct dwc2_qtd, + qtd_list_entry); + + if (hsotg->core_params->dma_enable <= 0) { + if ((hcint & HCINTMSK_CHHLTD) && hcint != HCINTMSK_CHHLTD) + hcint &= ~HCINTMSK_CHHLTD; + } + + if (hcint & HCINTMSK_XFERCOMPL) { + dwc2_hc_xfercomp_intr(hsotg, chan, chnum, qtd); + /* + * If NYET occurred at same time as Xfer Complete, the NYET is + * handled by the Xfer Complete interrupt handler. Don't want + * to call the NYET interrupt handler in this case. + */ + hcint &= ~HCINTMSK_NYET; + } + if (hcint & HCINTMSK_CHHLTD) + dwc2_hc_chhltd_intr(hsotg, chan, chnum, qtd); + if (hcint & HCINTMSK_AHBERR) + dwc2_hc_ahberr_intr(hsotg, chan, chnum, qtd); + if (hcint & HCINTMSK_STALL) + dwc2_hc_stall_intr(hsotg, chan, chnum, qtd); + if (hcint & HCINTMSK_NAK) + dwc2_hc_nak_intr(hsotg, chan, chnum, qtd); + if (hcint & HCINTMSK_ACK) + dwc2_hc_ack_intr(hsotg, chan, chnum, qtd); + if (hcint & HCINTMSK_NYET) + dwc2_hc_nyet_intr(hsotg, chan, chnum, qtd); + if (hcint & HCINTMSK_XACTERR) + dwc2_hc_xacterr_intr(hsotg, chan, chnum, qtd); + if (hcint & HCINTMSK_BBLERR) + dwc2_hc_babble_intr(hsotg, chan, chnum, qtd); + if (hcint & HCINTMSK_FRMOVRUN) + dwc2_hc_frmovrun_intr(hsotg, chan, chnum, qtd); + if (hcint & HCINTMSK_DATATGLERR) + dwc2_hc_datatglerr_intr(hsotg, chan, chnum, qtd); + + chan->hcint = 0; +} + +/* + * This interrupt indicates that one or more host channels has a pending + * interrupt. There are multiple conditions that can cause each host channel + * interrupt. This function determines which conditions have occurred for each + * host channel interrupt and handles them appropriately. + */ +static void dwc2_hc_intr(struct dwc2_hsotg *hsotg) +{ + u32 haint; + int i; + + haint = readl(hsotg->regs + HAINT); + if (dbg_perio()) { + dev_vdbg(hsotg->dev, "%s()\n", __func__); + + dev_vdbg(hsotg->dev, "HAINT=%08x\n", haint); + } + + for (i = 0; i < hsotg->core_params->host_channels; i++) { + if (haint & (1 << i)) + dwc2_hc_n_intr(hsotg, i); + } +} + +/* This function handles interrupts for the HCD */ +irqreturn_t dwc2_handle_hcd_intr(struct dwc2_hsotg *hsotg) +{ + u32 gintsts, dbg_gintsts; + irqreturn_t retval = IRQ_NONE; + + if (!dwc2_is_controller_alive(hsotg)) { + dev_warn(hsotg->dev, "Controller is dead\n"); + return retval; + } + + spin_lock(&hsotg->lock); + + /* Check if HOST Mode */ + if (dwc2_is_host_mode(hsotg)) { + gintsts = dwc2_read_core_intr(hsotg); + if (!gintsts) { + spin_unlock(&hsotg->lock); + return retval; + } + + retval = IRQ_HANDLED; + + dbg_gintsts = gintsts; +#ifndef DEBUG_SOF + dbg_gintsts &= ~GINTSTS_SOF; +#endif + if (!dbg_perio()) + dbg_gintsts &= ~(GINTSTS_HCHINT | GINTSTS_RXFLVL | + GINTSTS_PTXFEMP); + + /* Only print if there are any non-suppressed interrupts left */ + if (dbg_gintsts) + dev_vdbg(hsotg->dev, + "DWC OTG HCD Interrupt Detected gintsts&gintmsk=0x%08x\n", + gintsts); + + if (gintsts & GINTSTS_SOF) + dwc2_sof_intr(hsotg); + if (gintsts & GINTSTS_RXFLVL) + dwc2_rx_fifo_level_intr(hsotg); + if (gintsts & GINTSTS_NPTXFEMP) + dwc2_np_tx_fifo_empty_intr(hsotg); + if (gintsts & GINTSTS_PRTINT) + dwc2_port_intr(hsotg); + if (gintsts & GINTSTS_HCHINT) + dwc2_hc_intr(hsotg); + if (gintsts & GINTSTS_PTXFEMP) + dwc2_perio_tx_fifo_empty_intr(hsotg); + + if (dbg_gintsts) { + dev_vdbg(hsotg->dev, + "DWC OTG HCD Finished Servicing Interrupts\n"); + dev_vdbg(hsotg->dev, + "DWC OTG HCD gintsts=0x%08x gintmsk=0x%08x\n", + readl(hsotg->regs + GINTSTS), + readl(hsotg->regs + GINTMSK)); + } + } + + spin_unlock(&hsotg->lock); + + return retval; +} diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c new file mode 100644 index 0000000..9540f7e --- /dev/null +++ b/drivers/usb/dwc2/hcd_queue.c @@ -0,0 +1,835 @@ +/* + * hcd_queue.c - DesignWare HS OTG Controller host queuing routines + * + * Copyright (C) 2004-2013 Synopsys, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This file contains the functions to manage Queue Heads and Queue + * Transfer Descriptors for Host mode + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/dma-mapping.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/usb.h> + +#include <linux/usb/hcd.h> +#include <linux/usb/ch11.h> + +#include "core.h" +#include "hcd.h" + +/** + * dwc2_qh_init() - Initializes a QH structure + * + * @hsotg: The HCD state structure for the DWC OTG controller + * @qh: The QH to init + * @urb: Holds the information about the device/endpoint needed to initialize + * the QH + */ +#define SCHEDULE_SLOP 10 +static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, + struct dwc2_hcd_urb *urb) +{ + int dev_speed, hub_addr, hub_port; + char *speed, *type; + + dev_vdbg(hsotg->dev, "%s()\n", __func__); + + /* Initialize QH */ + qh->ep_type = dwc2_hcd_get_pipe_type(&urb->pipe_info); + qh->ep_is_in = dwc2_hcd_is_pipe_in(&urb->pipe_info) ? 1 : 0; + + qh->data_toggle = DWC2_HC_PID_DATA0; + qh->maxp = dwc2_hcd_get_mps(&urb->pipe_info); + INIT_LIST_HEAD(&qh->qtd_list); + INIT_LIST_HEAD(&qh->qh_list_entry); + + /* FS/LS Endpoint on HS Hub, NOT virtual root hub */ + dev_speed = dwc2_host_get_speed(hsotg, urb->priv); + + dwc2_host_hub_info(hsotg, urb->priv, &hub_addr, &hub_port); + + if ((dev_speed == USB_SPEED_LOW || dev_speed == USB_SPEED_FULL) && + hub_addr != 0 && hub_addr != 1) { + dev_vdbg(hsotg->dev, + "QH init: EP %d: TT found at hub addr %d, for port %d\n", + dwc2_hcd_get_ep_num(&urb->pipe_info), hub_addr, + hub_port); + qh->do_split = 1; + } + + if (qh->ep_type == USB_ENDPOINT_XFER_INT || + qh->ep_type == USB_ENDPOINT_XFER_ISOC) { + /* Compute scheduling parameters once and save them */ + u32 hprt, prtspd; + + /* Todo: Account for split transfers in the bus time */ + int bytecount = + dwc2_hb_mult(qh->maxp) * dwc2_max_packet(qh->maxp); + + qh->usecs = NS_TO_US(usb_calc_bus_time(qh->do_split ? + USB_SPEED_HIGH : dev_speed, qh->ep_is_in, + qh->ep_type == USB_ENDPOINT_XFER_ISOC, + bytecount)); + /* Start in a slightly future (micro)frame */ + qh->sched_frame = dwc2_frame_num_inc(hsotg->frame_number, + SCHEDULE_SLOP); + qh->interval = urb->interval; +#if 0 + /* Increase interrupt polling rate for debugging */ + if (qh->ep_type == USB_ENDPOINT_XFER_INT) + qh->interval = 8; +#endif + hprt = readl(hsotg->regs + HPRT0); + prtspd = (hprt & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT; + if (prtspd == HPRT0_SPD_HIGH_SPEED && + (dev_speed == USB_SPEED_LOW || + dev_speed == USB_SPEED_FULL)) { + qh->interval *= 8; + qh->sched_frame |= 0x7; + qh->start_split_frame = qh->sched_frame; + } + dev_dbg(hsotg->dev, "interval=%d\n", qh->interval); + } + + dev_vdbg(hsotg->dev, "DWC OTG HCD QH Initialized\n"); + dev_vdbg(hsotg->dev, "DWC OTG HCD QH - qh = %p\n", qh); + dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Device Address = %d\n", + dwc2_hcd_get_dev_addr(&urb->pipe_info)); + dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Endpoint %d, %s\n", + dwc2_hcd_get_ep_num(&urb->pipe_info), + dwc2_hcd_is_pipe_in(&urb->pipe_info) ? "IN" : "OUT"); + + qh->dev_speed = dev_speed; + + switch (dev_speed) { + case USB_SPEED_LOW: + speed = "low"; + break; + case USB_SPEED_FULL: + speed = "full"; + break; + case USB_SPEED_HIGH: + speed = "high"; + break; + default: + speed = "?"; + break; + } + dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Speed = %s\n", speed); + + switch (qh->ep_type) { + case USB_ENDPOINT_XFER_ISOC: + type = "isochronous"; + break; + case USB_ENDPOINT_XFER_INT: + type = "interrupt"; + break; + case USB_ENDPOINT_XFER_CONTROL: + type = "control"; + break; + case USB_ENDPOINT_XFER_BULK: + type = "bulk"; + break; + default: + type = "?"; + break; + } + + dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Type = %s\n", type); + + if (qh->ep_type == USB_ENDPOINT_XFER_INT) { + dev_vdbg(hsotg->dev, "DWC OTG HCD QH - usecs = %d\n", + qh->usecs); + dev_vdbg(hsotg->dev, "DWC OTG HCD QH - interval = %d\n", + qh->interval); + } +} + +/** + * dwc2_hcd_qh_create() - Allocates and initializes a QH + * + * @hsotg: The HCD state structure for the DWC OTG controller + * @urb: Holds the information about the device/endpoint needed + * to initialize the QH + * @atomic_alloc: Flag to do atomic allocation if needed + * + * Return: Pointer to the newly allocated QH, or NULL on error + */ +static struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg, + struct dwc2_hcd_urb *urb, + gfp_t mem_flags) +{ + struct dwc2_qh *qh; + + if (!urb->priv) + return NULL; + + /* Allocate memory */ + qh = kzalloc(sizeof(*qh), mem_flags); + if (!qh) + return NULL; + + dwc2_qh_init(hsotg, qh, urb); + + if (hsotg->core_params->dma_desc_enable > 0 && + dwc2_hcd_qh_init_ddma(hsotg, qh, mem_flags) < 0) { + dwc2_hcd_qh_free(hsotg, qh); + return NULL; + } + + return qh; +} + +/** + * dwc2_hcd_qh_free() - Frees the QH + * + * @hsotg: HCD instance + * @qh: The QH to free + * + * QH should already be removed from the list. QTD list should already be empty + * if called from URB Dequeue. + * + * Must NOT be called with interrupt disabled or spinlock held + */ +void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) +{ + u32 buf_size; + + if (hsotg->core_params->dma_desc_enable > 0) { + dwc2_hcd_qh_free_ddma(hsotg, qh); + } else if (qh->dw_align_buf) { + if (qh->ep_type == USB_ENDPOINT_XFER_ISOC) + buf_size = 4096; + else + buf_size = hsotg->core_params->max_transfer_size; + dma_free_coherent(hsotg->dev, buf_size, qh->dw_align_buf, + qh->dw_align_buf_dma); + } + + kfree(qh); +} + +/** + * dwc2_periodic_channel_available() - Checks that a channel is available for a + * periodic transfer + * + * @hsotg: The HCD state structure for the DWC OTG controller + * + * Return: 0 if successful, negative error code otherwise + */ +static int dwc2_periodic_channel_available(struct dwc2_hsotg *hsotg) +{ + /* + * Currently assuming that there is a dedicated host channel for + * each periodic transaction plus at least one host channel for + * non-periodic transactions + */ + int status; + int num_channels; + + num_channels = hsotg->core_params->host_channels; + if (hsotg->periodic_channels + hsotg->non_periodic_channels < + num_channels + && hsotg->periodic_channels < num_channels - 1) { + status = 0; + } else { + dev_dbg(hsotg->dev, + "%s: Total channels: %d, Periodic: %d, " + "Non-periodic: %d\n", __func__, num_channels, + hsotg->periodic_channels, hsotg->non_periodic_channels); + status = -ENOSPC; + } + + return status; +} + +/** + * dwc2_check_periodic_bandwidth() - Checks that there is sufficient bandwidth + * for the specified QH in the periodic schedule + * + * @hsotg: The HCD state structure for the DWC OTG controller + * @qh: QH containing periodic bandwidth required + * + * Return: 0 if successful, negative error code otherwise + * + * For simplicity, this calculation assumes that all the transfers in the + * periodic schedule may occur in the same (micro)frame + */ +static int dwc2_check_periodic_bandwidth(struct dwc2_hsotg *hsotg, + struct dwc2_qh *qh) +{ + int status; + s16 max_claimed_usecs; + + status = 0; + + if (qh->dev_speed == USB_SPEED_HIGH || qh->do_split) { + /* + * High speed mode + * Max periodic usecs is 80% x 125 usec = 100 usec + */ + max_claimed_usecs = 100 - qh->usecs; + } else { + /* + * Full speed mode + * Max periodic usecs is 90% x 1000 usec = 900 usec + */ + max_claimed_usecs = 900 - qh->usecs; + } + + if (hsotg->periodic_usecs > max_claimed_usecs) { + dev_err(hsotg->dev, + "%s: already claimed usecs %d, required usecs %d\n", + __func__, hsotg->periodic_usecs, qh->usecs); + status = -ENOSPC; + } + + return status; +} + +/** + * Microframe scheduler + * track the total use in hsotg->frame_usecs + * keep each qh use in qh->frame_usecs + * when surrendering the qh then donate the time back + */ +static const unsigned short max_uframe_usecs[] = { + 100, 100, 100, 100, 100, 100, 30, 0 +}; + +void dwc2_hcd_init_usecs(struct dwc2_hsotg *hsotg) +{ + int i; + + for (i = 0; i < 8; i++) + hsotg->frame_usecs[i] = max_uframe_usecs[i]; +} + +static int dwc2_find_single_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) +{ + unsigned short utime = qh->usecs; + int i; + + for (i = 0; i < 8; i++) { + /* At the start hsotg->frame_usecs[i] = max_uframe_usecs[i] */ + if (utime <= hsotg->frame_usecs[i]) { + hsotg->frame_usecs[i] -= utime; + qh->frame_usecs[i] += utime; + return i; + } + } + return -ENOSPC; +} + +/* + * use this for FS apps that can span multiple uframes + */ +static int dwc2_find_multi_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) +{ + unsigned short utime = qh->usecs; + unsigned short xtime; + int t_left; + int i; + int j; + int k; + + for (i = 0; i < 8; i++) { + if (hsotg->frame_usecs[i] <= 0) + continue; + + /* + * we need n consecutive slots so use j as a start slot + * j plus j+1 must be enough time (for now) + */ + xtime = hsotg->frame_usecs[i]; + for (j = i + 1; j < 8; j++) { + /* + * if we add this frame remaining time to xtime we may + * be OK, if not we need to test j for a complete frame + */ + if (xtime + hsotg->frame_usecs[j] < utime) { + if (hsotg->frame_usecs[j] < + max_uframe_usecs[j]) + continue; + } + if (xtime >= utime) { + t_left = utime; + for (k = i; k < 8; k++) { + t_left -= hsotg->frame_usecs[k]; + if (t_left <= 0) { + qh->frame_usecs[k] += + hsotg->frame_usecs[k] + + t_left; + hsotg->frame_usecs[k] = -t_left; + return i; + } else { + qh->frame_usecs[k] += + hsotg->frame_usecs[k]; + hsotg->frame_usecs[k] = 0; + } + } + } + /* add the frame time to x time */ + xtime += hsotg->frame_usecs[j]; + /* we must have a fully available next frame or break */ + if (xtime < utime && + hsotg->frame_usecs[j] == max_uframe_usecs[j]) + continue; + } + } + return -ENOSPC; +} + +static int dwc2_find_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) +{ + int ret; + + if (qh->dev_speed == USB_SPEED_HIGH) { + /* if this is a hs transaction we need a full frame */ + ret = dwc2_find_single_uframe(hsotg, qh); + } else { + /* + * if this is a fs transaction we may need a sequence + * of frames + */ + ret = dwc2_find_multi_uframe(hsotg, qh); + } + return ret; +} + +/** + * dwc2_check_max_xfer_size() - Checks that the max transfer size allowed in a + * host channel is large enough to handle the maximum data transfer in a single + * (micro)frame for a periodic transfer + * + * @hsotg: The HCD state structure for the DWC OTG controller + * @qh: QH for a periodic endpoint + * + * Return: 0 if successful, negative error code otherwise + */ +static int dwc2_check_max_xfer_size(struct dwc2_hsotg *hsotg, + struct dwc2_qh *qh) +{ + u32 max_xfer_size; + u32 max_channel_xfer_size; + int status = 0; + + max_xfer_size = dwc2_max_packet(qh->maxp) * dwc2_hb_mult(qh->maxp); + max_channel_xfer_size = hsotg->core_params->max_transfer_size; + + if (max_xfer_size > max_channel_xfer_size) { + dev_err(hsotg->dev, + "%s: Periodic xfer length %d > max xfer length for channel %d\n", + __func__, max_xfer_size, max_channel_xfer_size); + status = -ENOSPC; + } + + return status; +} + +/** + * dwc2_schedule_periodic() - Schedules an interrupt or isochronous transfer in + * the periodic schedule + * + * @hsotg: The HCD state structure for the DWC OTG controller + * @qh: QH for the periodic transfer. The QH should already contain the + * scheduling information. + * + * Return: 0 if successful, negative error code otherwise + */ +static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) +{ + int status; + + if (hsotg->core_params->uframe_sched > 0) { + int frame = -1; + + status = dwc2_find_uframe(hsotg, qh); + if (status == 0) + frame = 7; + else if (status > 0) + frame = status - 1; + + /* Set the new frame up */ + if (frame >= 0) { + qh->sched_frame &= ~0x7; + qh->sched_frame |= (frame & 7); + } + + if (status > 0) + status = 0; + } else { + status = dwc2_periodic_channel_available(hsotg); + if (status) { + dev_info(hsotg->dev, + "%s: No host channel available for periodic transfer\n", + __func__); + return status; + } + + status = dwc2_check_periodic_bandwidth(hsotg, qh); + } + + if (status) { + dev_dbg(hsotg->dev, + "%s: Insufficient periodic bandwidth for periodic transfer\n", + __func__); + return status; + } + + status = dwc2_check_max_xfer_size(hsotg, qh); + if (status) { + dev_dbg(hsotg->dev, + "%s: Channel max transfer size too small for periodic transfer\n", + __func__); + return status; + } + + if (hsotg->core_params->dma_desc_enable > 0) + /* Don't rely on SOF and start in ready schedule */ + list_add_tail(&qh->qh_list_entry, &hsotg->periodic_sched_ready); + else + /* Always start in inactive schedule */ + list_add_tail(&qh->qh_list_entry, + &hsotg->periodic_sched_inactive); + + if (hsotg->core_params->uframe_sched <= 0) + /* Reserve periodic channel */ + hsotg->periodic_channels++; + + /* Update claimed usecs per (micro)frame */ + hsotg->periodic_usecs += qh->usecs; + + return status; +} + +/** + * dwc2_deschedule_periodic() - Removes an interrupt or isochronous transfer + * from the periodic schedule + * + * @hsotg: The HCD state structure for the DWC OTG controller + * @qh: QH for the periodic transfer + */ +static void dwc2_deschedule_periodic(struct dwc2_hsotg *hsotg, + struct dwc2_qh *qh) +{ + int i; + + list_del_init(&qh->qh_list_entry); + + /* Update claimed usecs per (micro)frame */ + hsotg->periodic_usecs -= qh->usecs; + + if (hsotg->core_params->uframe_sched > 0) { + for (i = 0; i < 8; i++) { + hsotg->frame_usecs[i] += qh->frame_usecs[i]; + qh->frame_usecs[i] = 0; + } + } else { + /* Release periodic channel reservation */ + hsotg->periodic_channels--; + } +} + +/** + * dwc2_hcd_qh_add() - Adds a QH to either the non periodic or periodic + * schedule if it is not already in the schedule. If the QH is already in + * the schedule, no action is taken. + * + * @hsotg: The HCD state structure for the DWC OTG controller + * @qh: The QH to add + * + * Return: 0 if successful, negative error code otherwise + */ +int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) +{ + int status; + u32 intr_mask; + + if (dbg_qh(qh)) + dev_vdbg(hsotg->dev, "%s()\n", __func__); + + if (!list_empty(&qh->qh_list_entry)) + /* QH already in a schedule */ + return 0; + + /* Add the new QH to the appropriate schedule */ + if (dwc2_qh_is_non_per(qh)) { + /* Always start in inactive schedule */ + list_add_tail(&qh->qh_list_entry, + &hsotg->non_periodic_sched_inactive); + return 0; + } + + status = dwc2_schedule_periodic(hsotg, qh); + if (status) + return status; + if (!hsotg->periodic_qh_count) { + intr_mask = readl(hsotg->regs + GINTMSK); + intr_mask |= GINTSTS_SOF; + writel(intr_mask, hsotg->regs + GINTMSK); + } + hsotg->periodic_qh_count++; + + return 0; +} + +/** + * dwc2_hcd_qh_unlink() - Removes a QH from either the non-periodic or periodic + * schedule. Memory is not freed. + * + * @hsotg: The HCD state structure + * @qh: QH to remove from schedule + */ +void dwc2_hcd_qh_unlink(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) +{ + u32 intr_mask; + + dev_vdbg(hsotg->dev, "%s()\n", __func__); + + if (list_empty(&qh->qh_list_entry)) + /* QH is not in a schedule */ + return; + + if (dwc2_qh_is_non_per(qh)) { + if (hsotg->non_periodic_qh_ptr == &qh->qh_list_entry) + hsotg->non_periodic_qh_ptr = + hsotg->non_periodic_qh_ptr->next; + list_del_init(&qh->qh_list_entry); + return; + } + + dwc2_deschedule_periodic(hsotg, qh); + hsotg->periodic_qh_count--; + if (!hsotg->periodic_qh_count) { + intr_mask = readl(hsotg->regs + GINTMSK); + intr_mask &= ~GINTSTS_SOF; + writel(intr_mask, hsotg->regs + GINTMSK); + } +} + +/* + * Schedule the next continuing periodic split transfer + */ +static void dwc2_sched_periodic_split(struct dwc2_hsotg *hsotg, + struct dwc2_qh *qh, u16 frame_number, + int sched_next_periodic_split) +{ + u16 incr; + + if (sched_next_periodic_split) { + qh->sched_frame = frame_number; + incr = dwc2_frame_num_inc(qh->start_split_frame, 1); + if (dwc2_frame_num_le(frame_number, incr)) { + /* + * Allow one frame to elapse after start split + * microframe before scheduling complete split, but + * DON'T if we are doing the next start split in the + * same frame for an ISOC out + */ + if (qh->ep_type != USB_ENDPOINT_XFER_ISOC || + qh->ep_is_in != 0) { + qh->sched_frame = + dwc2_frame_num_inc(qh->sched_frame, 1); + } + } + } else { + qh->sched_frame = dwc2_frame_num_inc(qh->start_split_frame, + qh->interval); + if (dwc2_frame_num_le(qh->sched_frame, frame_number)) + qh->sched_frame = frame_number; + qh->sched_frame |= 0x7; + qh->start_split_frame = qh->sched_frame; + } +} + +/* + * Deactivates a QH. For non-periodic QHs, removes the QH from the active + * non-periodic schedule. The QH is added to the inactive non-periodic + * schedule if any QTDs are still attached to the QH. + * + * For periodic QHs, the QH is removed from the periodic queued schedule. If + * there are any QTDs still attached to the QH, the QH is added to either the + * periodic inactive schedule or the periodic ready schedule and its next + * scheduled frame is calculated. The QH is placed in the ready schedule if + * the scheduled frame has been reached already. Otherwise it's placed in the + * inactive schedule. If there are no QTDs attached to the QH, the QH is + * completely removed from the periodic schedule. + */ +void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, + int sched_next_periodic_split) +{ + u16 frame_number; + + if (dbg_qh(qh)) + dev_vdbg(hsotg->dev, "%s()\n", __func__); + + if (dwc2_qh_is_non_per(qh)) { + dwc2_hcd_qh_unlink(hsotg, qh); + if (!list_empty(&qh->qtd_list)) + /* Add back to inactive non-periodic schedule */ + dwc2_hcd_qh_add(hsotg, qh); + return; + } + + frame_number = dwc2_hcd_get_frame_number(hsotg); + + if (qh->do_split) { + dwc2_sched_periodic_split(hsotg, qh, frame_number, + sched_next_periodic_split); + } else { + qh->sched_frame = dwc2_frame_num_inc(qh->sched_frame, + qh->interval); + if (dwc2_frame_num_le(qh->sched_frame, frame_number)) + qh->sched_frame = frame_number; + } + + if (list_empty(&qh->qtd_list)) { + dwc2_hcd_qh_unlink(hsotg, qh); + return; + } + /* + * Remove from periodic_sched_queued and move to + * appropriate queue + */ + if ((hsotg->core_params->uframe_sched > 0 && + dwc2_frame_num_le(qh->sched_frame, frame_number)) || + (hsotg->core_params->uframe_sched <= 0 && + qh->sched_frame == frame_number)) + list_move(&qh->qh_list_entry, &hsotg->periodic_sched_ready); + else + list_move(&qh->qh_list_entry, &hsotg->periodic_sched_inactive); +} + +/** + * dwc2_hcd_qtd_init() - Initializes a QTD structure + * + * @qtd: The QTD to initialize + * @urb: The associated URB + */ +void dwc2_hcd_qtd_init(struct dwc2_qtd *qtd, struct dwc2_hcd_urb *urb) +{ + qtd->urb = urb; + if (dwc2_hcd_get_pipe_type(&urb->pipe_info) == + USB_ENDPOINT_XFER_CONTROL) { + /* + * The only time the QTD data toggle is used is on the data + * phase of control transfers. This phase always starts with + * DATA1. + */ + qtd->data_toggle = DWC2_HC_PID_DATA1; + qtd->control_phase = DWC2_CONTROL_SETUP; + } + + /* Start split */ + qtd->complete_split = 0; + qtd->isoc_split_pos = DWC2_HCSPLT_XACTPOS_ALL; + qtd->isoc_split_offset = 0; + qtd->in_process = 0; + + /* Store the qtd ptr in the urb to reference the QTD */ + urb->qtd = qtd; +} + +/** + * dwc2_hcd_qtd_add() - Adds a QTD to the QTD-list of a QH + * + * @hsotg: The DWC HCD structure + * @qtd: The QTD to add + * @qh: Out parameter to return queue head + * @atomic_alloc: Flag to do atomic alloc if needed + * + * Return: 0 if successful, negative error code otherwise + * + * Finds the correct QH to place the QTD into. If it does not find a QH, it + * will create a new QH. If the QH to which the QTD is added is not currently + * scheduled, it is placed into the proper schedule based on its EP type. + */ +int dwc2_hcd_qtd_add(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd, + struct dwc2_qh **qh, gfp_t mem_flags) +{ + struct dwc2_hcd_urb *urb = qtd->urb; + unsigned long flags; + int allocated = 0; + int retval; + + /* + * Get the QH which holds the QTD-list to insert to. Create QH if it + * doesn't exist. + */ + if (*qh == NULL) { + *qh = dwc2_hcd_qh_create(hsotg, urb, mem_flags); + if (*qh == NULL) + return -ENOMEM; + allocated = 1; + } + + spin_lock_irqsave(&hsotg->lock, flags); + + retval = dwc2_hcd_qh_add(hsotg, *qh); + if (retval) + goto fail; + + qtd->qh = *qh; + list_add_tail(&qtd->qtd_list_entry, &(*qh)->qtd_list); + spin_unlock_irqrestore(&hsotg->lock, flags); + + return 0; + +fail: + if (allocated) { + struct dwc2_qtd *qtd2, *qtd2_tmp; + struct dwc2_qh *qh_tmp = *qh; + + *qh = NULL; + dwc2_hcd_qh_unlink(hsotg, qh_tmp); + + /* Free each QTD in the QH's QTD list */ + list_for_each_entry_safe(qtd2, qtd2_tmp, &qh_tmp->qtd_list, + qtd_list_entry) + dwc2_hcd_qtd_unlink_and_free(hsotg, qtd2, qh_tmp); + + spin_unlock_irqrestore(&hsotg->lock, flags); + dwc2_hcd_qh_free(hsotg, qh_tmp); + } else { + spin_unlock_irqrestore(&hsotg->lock, flags); + } + + return retval; +} diff --git a/drivers/usb/dwc2/hw.h b/drivers/usb/dwc2/hw.h new file mode 100644 index 0000000..9c92a3c --- /dev/null +++ b/drivers/usb/dwc2/hw.h @@ -0,0 +1,809 @@ +/* + * hw.h - DesignWare HS OTG Controller hardware definitions + * + * Copyright 2004-2013 Synopsys, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __DWC2_HW_H__ +#define __DWC2_HW_H__ + +#define HSOTG_REG(x) (x) + +#define GOTGCTL HSOTG_REG(0x000) +#define GOTGCTL_CHIRPEN (1 << 27) +#define GOTGCTL_MULT_VALID_BC_MASK (0x1f << 22) +#define GOTGCTL_MULT_VALID_BC_SHIFT 22 +#define GOTGCTL_OTGVER (1 << 20) +#define GOTGCTL_BSESVLD (1 << 19) +#define GOTGCTL_ASESVLD (1 << 18) +#define GOTGCTL_DBNC_SHORT (1 << 17) +#define GOTGCTL_CONID_B (1 << 16) +#define GOTGCTL_DEVHNPEN (1 << 11) +#define GOTGCTL_HSTSETHNPEN (1 << 10) +#define GOTGCTL_HNPREQ (1 << 9) +#define GOTGCTL_HSTNEGSCS (1 << 8) +#define GOTGCTL_SESREQ (1 << 1) +#define GOTGCTL_SESREQSCS (1 << 0) + +#define GOTGINT HSOTG_REG(0x004) +#define GOTGINT_DBNCE_DONE (1 << 19) +#define GOTGINT_A_DEV_TOUT_CHG (1 << 18) +#define GOTGINT_HST_NEG_DET (1 << 17) +#define GOTGINT_HST_NEG_SUC_STS_CHNG (1 << 9) +#define GOTGINT_SES_REQ_SUC_STS_CHNG (1 << 8) +#define GOTGINT_SES_END_DET (1 << 2) + +#define GAHBCFG HSOTG_REG(0x008) +#define GAHBCFG_AHB_SINGLE (1 << 23) +#define GAHBCFG_NOTI_ALL_DMA_WRIT (1 << 22) +#define GAHBCFG_REM_MEM_SUPP (1 << 21) +#define GAHBCFG_P_TXF_EMP_LVL (1 << 8) +#define GAHBCFG_NP_TXF_EMP_LVL (1 << 7) +#define GAHBCFG_DMA_EN (1 << 5) +#define GAHBCFG_HBSTLEN_MASK (0xf << 1) +#define GAHBCFG_HBSTLEN_SHIFT 1 +#define GAHBCFG_HBSTLEN_SINGLE 0 +#define GAHBCFG_HBSTLEN_INCR 1 +#define GAHBCFG_HBSTLEN_INCR4 3 +#define GAHBCFG_HBSTLEN_INCR8 5 +#define GAHBCFG_HBSTLEN_INCR16 7 +#define GAHBCFG_GLBL_INTR_EN (1 << 0) +#define GAHBCFG_CTRL_MASK (GAHBCFG_P_TXF_EMP_LVL | \ + GAHBCFG_NP_TXF_EMP_LVL | \ + GAHBCFG_DMA_EN | \ + GAHBCFG_GLBL_INTR_EN) + +#define GUSBCFG HSOTG_REG(0x00C) +#define GUSBCFG_FORCEDEVMODE (1 << 30) +#define GUSBCFG_FORCEHOSTMODE (1 << 29) +#define GUSBCFG_TXENDDELAY (1 << 28) +#define GUSBCFG_ICTRAFFICPULLREMOVE (1 << 27) +#define GUSBCFG_ICUSBCAP (1 << 26) +#define GUSBCFG_ULPI_INT_PROT_DIS (1 << 25) +#define GUSBCFG_INDICATORPASSTHROUGH (1 << 24) +#define GUSBCFG_INDICATORCOMPLEMENT (1 << 23) +#define GUSBCFG_TERMSELDLPULSE (1 << 22) +#define GUSBCFG_ULPI_INT_VBUS_IND (1 << 21) +#define GUSBCFG_ULPI_EXT_VBUS_DRV (1 << 20) +#define GUSBCFG_ULPI_CLK_SUSP_M (1 << 19) +#define GUSBCFG_ULPI_AUTO_RES (1 << 18) +#define GUSBCFG_ULPI_FS_LS (1 << 17) +#define GUSBCFG_OTG_UTMI_FS_SEL (1 << 16) +#define GUSBCFG_PHY_LP_CLK_SEL (1 << 15) +#define GUSBCFG_USBTRDTIM_MASK (0xf << 10) +#define GUSBCFG_USBTRDTIM_SHIFT 10 +#define GUSBCFG_HNPCAP (1 << 9) +#define GUSBCFG_SRPCAP (1 << 8) +#define GUSBCFG_DDRSEL (1 << 7) +#define GUSBCFG_PHYSEL (1 << 6) +#define GUSBCFG_FSINTF (1 << 5) +#define GUSBCFG_ULPI_UTMI_SEL (1 << 4) +#define GUSBCFG_PHYIF16 (1 << 3) +#define GUSBCFG_TOUTCAL_MASK (0x7 << 0) +#define GUSBCFG_TOUTCAL_SHIFT 0 +#define GUSBCFG_TOUTCAL_LIMIT 0x7 +#define GUSBCFG_TOUTCAL(_x) ((_x) << 0) + +#define GRSTCTL HSOTG_REG(0x010) +#define GRSTCTL_AHBIDLE (1 << 31) +#define GRSTCTL_DMAREQ (1 << 30) +#define GRSTCTL_TXFNUM_MASK (0x1f << 6) +#define GRSTCTL_TXFNUM_SHIFT 6 +#define GRSTCTL_TXFNUM_LIMIT 0x1f +#define GRSTCTL_TXFNUM(_x) ((_x) << 6) +#define GRSTCTL_TXFFLSH (1 << 5) +#define GRSTCTL_RXFFLSH (1 << 4) +#define GRSTCTL_IN_TKNQ_FLSH (1 << 3) +#define GRSTCTL_FRMCNTRRST (1 << 2) +#define GRSTCTL_HSFTRST (1 << 1) +#define GRSTCTL_CSFTRST (1 << 0) + +#define GINTSTS HSOTG_REG(0x014) +#define GINTMSK HSOTG_REG(0x018) +#define GINTSTS_WKUPINT (1 << 31) +#define GINTSTS_SESSREQINT (1 << 30) +#define GINTSTS_DISCONNINT (1 << 29) +#define GINTSTS_CONIDSTSCHNG (1 << 28) +#define GINTSTS_LPMTRANRCVD (1 << 27) +#define GINTSTS_PTXFEMP (1 << 26) +#define GINTSTS_HCHINT (1 << 25) +#define GINTSTS_PRTINT (1 << 24) +#define GINTSTS_RESETDET (1 << 23) +#define GINTSTS_FET_SUSP (1 << 22) +#define GINTSTS_INCOMPL_IP (1 << 21) +#define GINTSTS_INCOMPL_SOIN (1 << 20) +#define GINTSTS_OEPINT (1 << 19) +#define GINTSTS_IEPINT (1 << 18) +#define GINTSTS_EPMIS (1 << 17) +#define GINTSTS_RESTOREDONE (1 << 16) +#define GINTSTS_EOPF (1 << 15) +#define GINTSTS_ISOUTDROP (1 << 14) +#define GINTSTS_ENUMDONE (1 << 13) +#define GINTSTS_USBRST (1 << 12) +#define GINTSTS_USBSUSP (1 << 11) +#define GINTSTS_ERLYSUSP (1 << 10) +#define GINTSTS_I2CINT (1 << 9) +#define GINTSTS_ULPI_CK_INT (1 << 8) +#define GINTSTS_GOUTNAKEFF (1 << 7) +#define GINTSTS_GINNAKEFF (1 << 6) +#define GINTSTS_NPTXFEMP (1 << 5) +#define GINTSTS_RXFLVL (1 << 4) +#define GINTSTS_SOF (1 << 3) +#define GINTSTS_OTGINT (1 << 2) +#define GINTSTS_MODEMIS (1 << 1) +#define GINTSTS_CURMODE_HOST (1 << 0) + +#define GRXSTSR HSOTG_REG(0x01C) +#define GRXSTSP HSOTG_REG(0x020) +#define GRXSTS_FN_MASK (0x7f << 25) +#define GRXSTS_FN_SHIFT 25 +#define GRXSTS_PKTSTS_MASK (0xf << 17) +#define GRXSTS_PKTSTS_SHIFT 17 +#define GRXSTS_PKTSTS_GLOBALOUTNAK 1 +#define GRXSTS_PKTSTS_OUTRX 2 +#define GRXSTS_PKTSTS_HCHIN 2 +#define GRXSTS_PKTSTS_OUTDONE 3 +#define GRXSTS_PKTSTS_HCHIN_XFER_COMP 3 +#define GRXSTS_PKTSTS_SETUPDONE 4 +#define GRXSTS_PKTSTS_DATATOGGLEERR 5 +#define GRXSTS_PKTSTS_SETUPRX 6 +#define GRXSTS_PKTSTS_HCHHALTED 7 +#define GRXSTS_HCHNUM_MASK (0xf << 0) +#define GRXSTS_HCHNUM_SHIFT 0 +#define GRXSTS_DPID_MASK (0x3 << 15) +#define GRXSTS_DPID_SHIFT 15 +#define GRXSTS_BYTECNT_MASK (0x7ff << 4) +#define GRXSTS_BYTECNT_SHIFT 4 +#define GRXSTS_EPNUM_MASK (0xf << 0) +#define GRXSTS_EPNUM_SHIFT 0 + +#define GRXFSIZ HSOTG_REG(0x024) +#define GRXFSIZ_DEPTH_MASK (0xffff << 0) +#define GRXFSIZ_DEPTH_SHIFT 0 + +#define GNPTXFSIZ HSOTG_REG(0x028) +/* Use FIFOSIZE_* constants to access this register */ + +#define GNPTXSTS HSOTG_REG(0x02C) +#define GNPTXSTS_NP_TXQ_TOP_MASK (0x7f << 24) +#define GNPTXSTS_NP_TXQ_TOP_SHIFT 24 +#define GNPTXSTS_NP_TXQ_SPC_AVAIL_MASK (0xff << 16) +#define GNPTXSTS_NP_TXQ_SPC_AVAIL_SHIFT 16 +#define GNPTXSTS_NP_TXQ_SPC_AVAIL_GET(_v) (((_v) >> 16) & 0xff) +#define GNPTXSTS_NP_TXF_SPC_AVAIL_MASK (0xffff << 0) +#define GNPTXSTS_NP_TXF_SPC_AVAIL_SHIFT 0 +#define GNPTXSTS_NP_TXF_SPC_AVAIL_GET(_v) (((_v) >> 0) & 0xffff) + +#define GI2CCTL HSOTG_REG(0x0030) +#define GI2CCTL_BSYDNE (1 << 31) +#define GI2CCTL_RW (1 << 30) +#define GI2CCTL_I2CDATSE0 (1 << 28) +#define GI2CCTL_I2CDEVADDR_MASK (0x3 << 26) +#define GI2CCTL_I2CDEVADDR_SHIFT 26 +#define GI2CCTL_I2CSUSPCTL (1 << 25) +#define GI2CCTL_ACK (1 << 24) +#define GI2CCTL_I2CEN (1 << 23) +#define GI2CCTL_ADDR_MASK (0x7f << 16) +#define GI2CCTL_ADDR_SHIFT 16 +#define GI2CCTL_REGADDR_MASK (0xff << 8) +#define GI2CCTL_REGADDR_SHIFT 8 +#define GI2CCTL_RWDATA_MASK (0xff << 0) +#define GI2CCTL_RWDATA_SHIFT 0 + +#define GPVNDCTL HSOTG_REG(0x0034) +#define GGPIO HSOTG_REG(0x0038) +#define GUID HSOTG_REG(0x003c) +#define GSNPSID HSOTG_REG(0x0040) +#define GHWCFG1 HSOTG_REG(0x0044) + +#define GHWCFG2 HSOTG_REG(0x0048) +#define GHWCFG2_OTG_ENABLE_IC_USB (1 << 31) +#define GHWCFG2_DEV_TOKEN_Q_DEPTH_MASK (0x1f << 26) +#define GHWCFG2_DEV_TOKEN_Q_DEPTH_SHIFT 26 +#define GHWCFG2_HOST_PERIO_TX_Q_DEPTH_MASK (0x3 << 24) +#define GHWCFG2_HOST_PERIO_TX_Q_DEPTH_SHIFT 24 +#define GHWCFG2_NONPERIO_TX_Q_DEPTH_MASK (0x3 << 22) +#define GHWCFG2_NONPERIO_TX_Q_DEPTH_SHIFT 22 +#define GHWCFG2_MULTI_PROC_INT (1 << 20) +#define GHWCFG2_DYNAMIC_FIFO (1 << 19) +#define GHWCFG2_PERIO_EP_SUPPORTED (1 << 18) +#define GHWCFG2_NUM_HOST_CHAN_MASK (0xf << 14) +#define GHWCFG2_NUM_HOST_CHAN_SHIFT 14 +#define GHWCFG2_NUM_DEV_EP_MASK (0xf << 10) +#define GHWCFG2_NUM_DEV_EP_SHIFT 10 +#define GHWCFG2_FS_PHY_TYPE_MASK (0x3 << 8) +#define GHWCFG2_FS_PHY_TYPE_SHIFT 8 +#define GHWCFG2_FS_PHY_TYPE_NOT_SUPPORTED 0 +#define GHWCFG2_FS_PHY_TYPE_DEDICATED 1 +#define GHWCFG2_FS_PHY_TYPE_SHARED_UTMI 2 +#define GHWCFG2_FS_PHY_TYPE_SHARED_ULPI 3 +#define GHWCFG2_HS_PHY_TYPE_MASK (0x3 << 6) +#define GHWCFG2_HS_PHY_TYPE_SHIFT 6 +#define GHWCFG2_HS_PHY_TYPE_NOT_SUPPORTED 0 +#define GHWCFG2_HS_PHY_TYPE_UTMI 1 +#define GHWCFG2_HS_PHY_TYPE_ULPI 2 +#define GHWCFG2_HS_PHY_TYPE_UTMI_ULPI 3 +#define GHWCFG2_POINT2POINT (1 << 5) +#define GHWCFG2_ARCHITECTURE_MASK (0x3 << 3) +#define GHWCFG2_ARCHITECTURE_SHIFT 3 +#define GHWCFG2_SLAVE_ONLY_ARCH 0 +#define GHWCFG2_EXT_DMA_ARCH 1 +#define GHWCFG2_INT_DMA_ARCH 2 +#define GHWCFG2_OP_MODE_MASK (0x7 << 0) +#define GHWCFG2_OP_MODE_SHIFT 0 +#define GHWCFG2_OP_MODE_HNP_SRP_CAPABLE 0 +#define GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE 1 +#define GHWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE 2 +#define GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE 3 +#define GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE 4 +#define GHWCFG2_OP_MODE_SRP_CAPABLE_HOST 5 +#define GHWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST 6 +#define GHWCFG2_OP_MODE_UNDEFINED 7 + +#define GHWCFG3 HSOTG_REG(0x004c) +#define GHWCFG3_DFIFO_DEPTH_MASK (0xffff << 16) +#define GHWCFG3_DFIFO_DEPTH_SHIFT 16 +#define GHWCFG3_OTG_LPM_EN (1 << 15) +#define GHWCFG3_BC_SUPPORT (1 << 14) +#define GHWCFG3_OTG_ENABLE_HSIC (1 << 13) +#define GHWCFG3_ADP_SUPP (1 << 12) +#define GHWCFG3_SYNCH_RESET_TYPE (1 << 11) +#define GHWCFG3_OPTIONAL_FEATURES (1 << 10) +#define GHWCFG3_VENDOR_CTRL_IF (1 << 9) +#define GHWCFG3_I2C (1 << 8) +#define GHWCFG3_OTG_FUNC (1 << 7) +#define GHWCFG3_PACKET_SIZE_CNTR_WIDTH_MASK (0x7 << 4) +#define GHWCFG3_PACKET_SIZE_CNTR_WIDTH_SHIFT 4 +#define GHWCFG3_XFER_SIZE_CNTR_WIDTH_MASK (0xf << 0) +#define GHWCFG3_XFER_SIZE_CNTR_WIDTH_SHIFT 0 + +#define GHWCFG4 HSOTG_REG(0x0050) +#define GHWCFG4_DESC_DMA_DYN (1 << 31) +#define GHWCFG4_DESC_DMA (1 << 30) +#define GHWCFG4_NUM_IN_EPS_MASK (0xf << 26) +#define GHWCFG4_NUM_IN_EPS_SHIFT 26 +#define GHWCFG4_DED_FIFO_EN (1 << 25) +#define GHWCFG4_SESSION_END_FILT_EN (1 << 24) +#define GHWCFG4_B_VALID_FILT_EN (1 << 23) +#define GHWCFG4_A_VALID_FILT_EN (1 << 22) +#define GHWCFG4_VBUS_VALID_FILT_EN (1 << 21) +#define GHWCFG4_IDDIG_FILT_EN (1 << 20) +#define GHWCFG4_NUM_DEV_MODE_CTRL_EP_MASK (0xf << 16) +#define GHWCFG4_NUM_DEV_MODE_CTRL_EP_SHIFT 16 +#define GHWCFG4_UTMI_PHY_DATA_WIDTH_MASK (0x3 << 14) +#define GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT 14 +#define GHWCFG4_UTMI_PHY_DATA_WIDTH_8 0 +#define GHWCFG4_UTMI_PHY_DATA_WIDTH_16 1 +#define GHWCFG4_UTMI_PHY_DATA_WIDTH_8_OR_16 2 +#define GHWCFG4_XHIBER (1 << 7) +#define GHWCFG4_HIBER (1 << 6) +#define GHWCFG4_MIN_AHB_FREQ (1 << 5) +#define GHWCFG4_POWER_OPTIMIZ (1 << 4) +#define GHWCFG4_NUM_DEV_PERIO_IN_EP_MASK (0xf << 0) +#define GHWCFG4_NUM_DEV_PERIO_IN_EP_SHIFT 0 + +#define GLPMCFG HSOTG_REG(0x0054) +#define GLPMCFG_INV_SEL_HSIC (1 << 31) +#define GLPMCFG_HSIC_CONNECT (1 << 30) +#define GLPMCFG_RETRY_COUNT_STS_MASK (0x7 << 25) +#define GLPMCFG_RETRY_COUNT_STS_SHIFT 25 +#define GLPMCFG_SEND_LPM (1 << 24) +#define GLPMCFG_RETRY_COUNT_MASK (0x7 << 21) +#define GLPMCFG_RETRY_COUNT_SHIFT 21 +#define GLPMCFG_LPM_CHAN_INDEX_MASK (0xf << 17) +#define GLPMCFG_LPM_CHAN_INDEX_SHIFT 17 +#define GLPMCFG_SLEEP_STATE_RESUMEOK (1 << 16) +#define GLPMCFG_PRT_SLEEP_STS (1 << 15) +#define GLPMCFG_LPM_RESP_MASK (0x3 << 13) +#define GLPMCFG_LPM_RESP_SHIFT 13 +#define GLPMCFG_HIRD_THRES_MASK (0x1f << 8) +#define GLPMCFG_HIRD_THRES_SHIFT 8 +#define GLPMCFG_HIRD_THRES_EN (0x10 << 8) +#define GLPMCFG_EN_UTMI_SLEEP (1 << 7) +#define GLPMCFG_REM_WKUP_EN (1 << 6) +#define GLPMCFG_HIRD_MASK (0xf << 2) +#define GLPMCFG_HIRD_SHIFT 2 +#define GLPMCFG_APPL_RESP (1 << 1) +#define GLPMCFG_LPM_CAP_EN (1 << 0) + +#define GPWRDN HSOTG_REG(0x0058) +#define GPWRDN_MULT_VAL_ID_BC_MASK (0x1f << 24) +#define GPWRDN_MULT_VAL_ID_BC_SHIFT 24 +#define GPWRDN_ADP_INT (1 << 23) +#define GPWRDN_BSESSVLD (1 << 22) +#define GPWRDN_IDSTS (1 << 21) +#define GPWRDN_LINESTATE_MASK (0x3 << 19) +#define GPWRDN_LINESTATE_SHIFT 19 +#define GPWRDN_STS_CHGINT_MSK (1 << 18) +#define GPWRDN_STS_CHGINT (1 << 17) +#define GPWRDN_SRP_DET_MSK (1 << 16) +#define GPWRDN_SRP_DET (1 << 15) +#define GPWRDN_CONNECT_DET_MSK (1 << 14) +#define GPWRDN_CONNECT_DET (1 << 13) +#define GPWRDN_DISCONN_DET_MSK (1 << 12) +#define GPWRDN_DISCONN_DET (1 << 11) +#define GPWRDN_RST_DET_MSK (1 << 10) +#define GPWRDN_RST_DET (1 << 9) +#define GPWRDN_LNSTSCHG_MSK (1 << 8) +#define GPWRDN_LNSTSCHG (1 << 7) +#define GPWRDN_DIS_VBUS (1 << 6) +#define GPWRDN_PWRDNSWTCH (1 << 5) +#define GPWRDN_PWRDNRSTN (1 << 4) +#define GPWRDN_PWRDNCLMP (1 << 3) +#define GPWRDN_RESTORE (1 << 2) +#define GPWRDN_PMUACTV (1 << 1) +#define GPWRDN_PMUINTSEL (1 << 0) + +#define GDFIFOCFG HSOTG_REG(0x005c) +#define GDFIFOCFG_EPINFOBASE_MASK (0xffff << 16) +#define GDFIFOCFG_EPINFOBASE_SHIFT 16 +#define GDFIFOCFG_GDFIFOCFG_MASK (0xffff << 0) +#define GDFIFOCFG_GDFIFOCFG_SHIFT 0 + +#define ADPCTL HSOTG_REG(0x0060) +#define ADPCTL_AR_MASK (0x3 << 27) +#define ADPCTL_AR_SHIFT 27 +#define ADPCTL_ADP_TMOUT_INT_MSK (1 << 26) +#define ADPCTL_ADP_SNS_INT_MSK (1 << 25) +#define ADPCTL_ADP_PRB_INT_MSK (1 << 24) +#define ADPCTL_ADP_TMOUT_INT (1 << 23) +#define ADPCTL_ADP_SNS_INT (1 << 22) +#define ADPCTL_ADP_PRB_INT (1 << 21) +#define ADPCTL_ADPENA (1 << 20) +#define ADPCTL_ADPRES (1 << 19) +#define ADPCTL_ENASNS (1 << 18) +#define ADPCTL_ENAPRB (1 << 17) +#define ADPCTL_RTIM_MASK (0x7ff << 6) +#define ADPCTL_RTIM_SHIFT 6 +#define ADPCTL_PRB_PER_MASK (0x3 << 4) +#define ADPCTL_PRB_PER_SHIFT 4 +#define ADPCTL_PRB_DELTA_MASK (0x3 << 2) +#define ADPCTL_PRB_DELTA_SHIFT 2 +#define ADPCTL_PRB_DSCHRG_MASK (0x3 << 0) +#define ADPCTL_PRB_DSCHRG_SHIFT 0 + +#define HPTXFSIZ HSOTG_REG(0x100) +/* Use FIFOSIZE_* constants to access this register */ + +#define DPTXFSIZN(_a) HSOTG_REG(0x104 + (((_a) - 1) * 4)) +/* Use FIFOSIZE_* constants to access this register */ + +/* These apply to the GNPTXFSIZ, HPTXFSIZ and DPTXFSIZN registers */ +#define FIFOSIZE_DEPTH_MASK (0xffff << 16) +#define FIFOSIZE_DEPTH_SHIFT 16 +#define FIFOSIZE_STARTADDR_MASK (0xffff << 0) +#define FIFOSIZE_STARTADDR_SHIFT 0 + +/* Device mode registers */ + +#define DCFG HSOTG_REG(0x800) +#define DCFG_EPMISCNT_MASK (0x1f << 18) +#define DCFG_EPMISCNT_SHIFT 18 +#define DCFG_EPMISCNT_LIMIT 0x1f +#define DCFG_EPMISCNT(_x) ((_x) << 18) +#define DCFG_PERFRINT_MASK (0x3 << 11) +#define DCFG_PERFRINT_SHIFT 11 +#define DCFG_PERFRINT_LIMIT 0x3 +#define DCFG_PERFRINT(_x) ((_x) << 11) +#define DCFG_DEVADDR_MASK (0x7f << 4) +#define DCFG_DEVADDR_SHIFT 4 +#define DCFG_DEVADDR_LIMIT 0x7f +#define DCFG_DEVADDR(_x) ((_x) << 4) +#define DCFG_NZ_STS_OUT_HSHK (1 << 2) +#define DCFG_DEVSPD_MASK (0x3 << 0) +#define DCFG_DEVSPD_SHIFT 0 +#define DCFG_DEVSPD_HS 0 +#define DCFG_DEVSPD_FS 1 +#define DCFG_DEVSPD_LS 2 +#define DCFG_DEVSPD_FS48 3 + +#define DCTL HSOTG_REG(0x804) +#define DCTL_PWRONPRGDONE (1 << 11) +#define DCTL_CGOUTNAK (1 << 10) +#define DCTL_SGOUTNAK (1 << 9) +#define DCTL_CGNPINNAK (1 << 8) +#define DCTL_SGNPINNAK (1 << 7) +#define DCTL_TSTCTL_MASK (0x7 << 4) +#define DCTL_TSTCTL_SHIFT 4 +#define DCTL_GOUTNAKSTS (1 << 3) +#define DCTL_GNPINNAKSTS (1 << 2) +#define DCTL_SFTDISCON (1 << 1) +#define DCTL_RMTWKUPSIG (1 << 0) + +#define DSTS HSOTG_REG(0x808) +#define DSTS_SOFFN_MASK (0x3fff << 8) +#define DSTS_SOFFN_SHIFT 8 +#define DSTS_SOFFN_LIMIT 0x3fff +#define DSTS_SOFFN(_x) ((_x) << 8) +#define DSTS_ERRATICERR (1 << 3) +#define DSTS_ENUMSPD_MASK (0x3 << 1) +#define DSTS_ENUMSPD_SHIFT 1 +#define DSTS_ENUMSPD_HS 0 +#define DSTS_ENUMSPD_FS 1 +#define DSTS_ENUMSPD_LS 2 +#define DSTS_ENUMSPD_FS48 3 +#define DSTS_SUSPSTS (1 << 0) + +#define DIEPMSK HSOTG_REG(0x810) +#define DIEPMSK_TXFIFOEMPTY (1 << 7) +#define DIEPMSK_INEPNAKEFFMSK (1 << 6) +#define DIEPMSK_INTKNEPMISMSK (1 << 5) +#define DIEPMSK_INTKNTXFEMPMSK (1 << 4) +#define DIEPMSK_TIMEOUTMSK (1 << 3) +#define DIEPMSK_AHBERRMSK (1 << 2) +#define DIEPMSK_EPDISBLDMSK (1 << 1) +#define DIEPMSK_XFERCOMPLMSK (1 << 0) + +#define DOEPMSK HSOTG_REG(0x814) +#define DOEPMSK_BACK2BACKSETUP (1 << 6) +#define DOEPMSK_OUTTKNEPDISMSK (1 << 4) +#define DOEPMSK_SETUPMSK (1 << 3) +#define DOEPMSK_AHBERRMSK (1 << 2) +#define DOEPMSK_EPDISBLDMSK (1 << 1) +#define DOEPMSK_XFERCOMPLMSK (1 << 0) + +#define DAINT HSOTG_REG(0x818) +#define DAINTMSK HSOTG_REG(0x81C) +#define DAINT_OUTEP_SHIFT 16 +#define DAINT_OUTEP(_x) (1 << ((_x) + 16)) +#define DAINT_INEP(_x) (1 << (_x)) + +#define DTKNQR1 HSOTG_REG(0x820) +#define DTKNQR2 HSOTG_REG(0x824) +#define DTKNQR3 HSOTG_REG(0x830) +#define DTKNQR4 HSOTG_REG(0x834) + +#define DVBUSDIS HSOTG_REG(0x828) +#define DVBUSPULSE HSOTG_REG(0x82C) + +#define DIEPCTL0 HSOTG_REG(0x900) +#define DIEPCTL(_a) HSOTG_REG(0x900 + ((_a) * 0x20)) + +#define DOEPCTL0 HSOTG_REG(0xB00) +#define DOEPCTL(_a) HSOTG_REG(0xB00 + ((_a) * 0x20)) + +/* EP0 specialness: + * bits[29..28] - reserved (no SetD0PID, SetD1PID) + * bits[25..22] - should always be zero, this isn't a periodic endpoint + * bits[10..0] - MPS setting different for EP0 + */ +#define D0EPCTL_MPS_MASK (0x3 << 0) +#define D0EPCTL_MPS_SHIFT 0 +#define D0EPCTL_MPS_64 0 +#define D0EPCTL_MPS_32 1 +#define D0EPCTL_MPS_16 2 +#define D0EPCTL_MPS_8 3 + +#define DXEPCTL_EPENA (1 << 31) +#define DXEPCTL_EPDIS (1 << 30) +#define DXEPCTL_SETD1PID (1 << 29) +#define DXEPCTL_SETODDFR (1 << 29) +#define DXEPCTL_SETD0PID (1 << 28) +#define DXEPCTL_SETEVENFR (1 << 28) +#define DXEPCTL_SNAK (1 << 27) +#define DXEPCTL_CNAK (1 << 26) +#define DXEPCTL_TXFNUM_MASK (0xf << 22) +#define DXEPCTL_TXFNUM_SHIFT 22 +#define DXEPCTL_TXFNUM_LIMIT 0xf +#define DXEPCTL_TXFNUM(_x) ((_x) << 22) +#define DXEPCTL_STALL (1 << 21) +#define DXEPCTL_SNP (1 << 20) +#define DXEPCTL_EPTYPE_MASK (0x3 << 18) +#define DXEPCTL_EPTYPE_SHIFT 18 +#define DXEPCTL_EPTYPE_CONTROL 0 +#define DXEPCTL_EPTYPE_ISO 1 +#define DXEPCTL_EPTYPE_BULK 2 +#define DXEPCTL_EPTYPE_INTTERUPT 3 +#define DXEPCTL_NAKSTS (1 << 17) +#define DXEPCTL_DPID (1 << 16) +#define DXEPCTL_EOFRNUM (1 << 16) +#define DXEPCTL_USBACTEP (1 << 15) +#define DXEPCTL_NEXTEP_MASK (0xf << 11) +#define DXEPCTL_NEXTEP_SHIFT 11 +#define DXEPCTL_NEXTEP_LIMIT 0xf +#define DXEPCTL_NEXTEP(_x) ((_x) << 11) +#define DXEPCTL_MPS_MASK (0x7ff << 0) +#define DXEPCTL_MPS_SHIFT 0 +#define DXEPCTL_MPS_LIMIT 0x7ff +#define DXEPCTL_MPS(_x) ((_x) << 0) + +#define DIEPINT(_a) HSOTG_REG(0x908 + ((_a) * 0x20)) +#define DOEPINT(_a) HSOTG_REG(0xB08 + ((_a) * 0x20)) +#define DXEPINT_INEPNAKEFF (1 << 6) +#define DXEPINT_BACK2BACKSETUP (1 << 6) +#define DXEPINT_INTKNEPMIS (1 << 5) +#define DXEPINT_INTKNTXFEMP (1 << 4) +#define DXEPINT_OUTTKNEPDIS (1 << 4) +#define DXEPINT_TIMEOUT (1 << 3) +#define DXEPINT_SETUP (1 << 3) +#define DXEPINT_AHBERR (1 << 2) +#define DXEPINT_EPDISBLD (1 << 1) +#define DXEPINT_XFERCOMPL (1 << 0) + +#define DIEPTSIZ0 HSOTG_REG(0x910) +#define DIEPTSIZ0_PKTCNT_MASK (0x3 << 19) +#define DIEPTSIZ0_PKTCNT_SHIFT 19 +#define DIEPTSIZ0_PKTCNT_LIMIT 0x3 +#define DIEPTSIZ0_PKTCNT(_x) ((_x) << 19) +#define DIEPTSIZ0_XFERSIZE_MASK (0x7f << 0) +#define DIEPTSIZ0_XFERSIZE_SHIFT 0 +#define DIEPTSIZ0_XFERSIZE_LIMIT 0x7f +#define DIEPTSIZ0_XFERSIZE(_x) ((_x) << 0) + +#define DOEPTSIZ0 HSOTG_REG(0xB10) +#define DOEPTSIZ0_SUPCNT_MASK (0x3 << 29) +#define DOEPTSIZ0_SUPCNT_SHIFT 29 +#define DOEPTSIZ0_SUPCNT_LIMIT 0x3 +#define DOEPTSIZ0_SUPCNT(_x) ((_x) << 29) +#define DOEPTSIZ0_PKTCNT (1 << 19) +#define DOEPTSIZ0_XFERSIZE_MASK (0x7f << 0) +#define DOEPTSIZ0_XFERSIZE_SHIFT 0 + +#define DIEPTSIZ(_a) HSOTG_REG(0x910 + ((_a) * 0x20)) +#define DOEPTSIZ(_a) HSOTG_REG(0xB10 + ((_a) * 0x20)) +#define DXEPTSIZ_MC_MASK (0x3 << 29) +#define DXEPTSIZ_MC_SHIFT 29 +#define DXEPTSIZ_MC_LIMIT 0x3 +#define DXEPTSIZ_MC(_x) ((_x) << 29) +#define DXEPTSIZ_PKTCNT_MASK (0x3ff << 19) +#define DXEPTSIZ_PKTCNT_SHIFT 19 +#define DXEPTSIZ_PKTCNT_LIMIT 0x3ff +#define DXEPTSIZ_PKTCNT_GET(_v) (((_v) >> 19) & 0x3ff) +#define DXEPTSIZ_PKTCNT(_x) ((_x) << 19) +#define DXEPTSIZ_XFERSIZE_MASK (0x7ffff << 0) +#define DXEPTSIZ_XFERSIZE_SHIFT 0 +#define DXEPTSIZ_XFERSIZE_LIMIT 0x7ffff +#define DXEPTSIZ_XFERSIZE_GET(_v) (((_v) >> 0) & 0x7ffff) +#define DXEPTSIZ_XFERSIZE(_x) ((_x) << 0) + +#define DIEPDMA(_a) HSOTG_REG(0x914 + ((_a) * 0x20)) +#define DOEPDMA(_a) HSOTG_REG(0xB14 + ((_a) * 0x20)) + +#define DTXFSTS(_a) HSOTG_REG(0x918 + ((_a) * 0x20)) + +#define PCGCTL HSOTG_REG(0x0e00) +#define PCGCTL_IF_DEV_MODE (1 << 31) +#define PCGCTL_P2HD_PRT_SPD_MASK (0x3 << 29) +#define PCGCTL_P2HD_PRT_SPD_SHIFT 29 +#define PCGCTL_P2HD_DEV_ENUM_SPD_MASK (0x3 << 27) +#define PCGCTL_P2HD_DEV_ENUM_SPD_SHIFT 27 +#define PCGCTL_MAC_DEV_ADDR_MASK (0x7f << 20) +#define PCGCTL_MAC_DEV_ADDR_SHIFT 20 +#define PCGCTL_MAX_TERMSEL (1 << 19) +#define PCGCTL_MAX_XCVRSELECT_MASK (0x3 << 17) +#define PCGCTL_MAX_XCVRSELECT_SHIFT 17 +#define PCGCTL_PORT_POWER (1 << 16) +#define PCGCTL_PRT_CLK_SEL_MASK (0x3 << 14) +#define PCGCTL_PRT_CLK_SEL_SHIFT 14 +#define PCGCTL_ESS_REG_RESTORED (1 << 13) +#define PCGCTL_EXTND_HIBER_SWITCH (1 << 12) +#define PCGCTL_EXTND_HIBER_PWRCLMP (1 << 11) +#define PCGCTL_ENBL_EXTND_HIBER (1 << 10) +#define PCGCTL_RESTOREMODE (1 << 9) +#define PCGCTL_RESETAFTSUSP (1 << 8) +#define PCGCTL_DEEP_SLEEP (1 << 7) +#define PCGCTL_PHY_IN_SLEEP (1 << 6) +#define PCGCTL_ENBL_SLEEP_GATING (1 << 5) +#define PCGCTL_RSTPDWNMODULE (1 << 3) +#define PCGCTL_PWRCLMP (1 << 2) +#define PCGCTL_GATEHCLK (1 << 1) +#define PCGCTL_STOPPCLK (1 << 0) + +#define EPFIFO(_a) HSOTG_REG(0x1000 + ((_a) * 0x1000)) + +/* Host Mode Registers */ + +#define HCFG HSOTG_REG(0x0400) +#define HCFG_MODECHTIMEN (1 << 31) +#define HCFG_PERSCHEDENA (1 << 26) +#define HCFG_FRLISTEN_MASK (0x3 << 24) +#define HCFG_FRLISTEN_SHIFT 24 +#define HCFG_FRLISTEN_8 (0 << 24) +#define FRLISTEN_8_SIZE 8 +#define HCFG_FRLISTEN_16 (1 << 24) +#define FRLISTEN_16_SIZE 16 +#define HCFG_FRLISTEN_32 (2 << 24) +#define FRLISTEN_32_SIZE 32 +#define HCFG_FRLISTEN_64 (3 << 24) +#define FRLISTEN_64_SIZE 64 +#define HCFG_DESCDMA (1 << 23) +#define HCFG_RESVALID_MASK (0xff << 8) +#define HCFG_RESVALID_SHIFT 8 +#define HCFG_ENA32KHZ (1 << 7) +#define HCFG_FSLSSUPP (1 << 2) +#define HCFG_FSLSPCLKSEL_MASK (0x3 << 0) +#define HCFG_FSLSPCLKSEL_SHIFT 0 +#define HCFG_FSLSPCLKSEL_30_60_MHZ 0 +#define HCFG_FSLSPCLKSEL_48_MHZ 1 +#define HCFG_FSLSPCLKSEL_6_MHZ 2 + +#define HFIR HSOTG_REG(0x0404) +#define HFIR_FRINT_MASK (0xffff << 0) +#define HFIR_FRINT_SHIFT 0 +#define HFIR_RLDCTRL (1 << 16) + +#define HFNUM HSOTG_REG(0x0408) +#define HFNUM_FRREM_MASK (0xffff << 16) +#define HFNUM_FRREM_SHIFT 16 +#define HFNUM_FRNUM_MASK (0xffff << 0) +#define HFNUM_FRNUM_SHIFT 0 +#define HFNUM_MAX_FRNUM 0x3fff + +#define HPTXSTS HSOTG_REG(0x0410) +#define TXSTS_QTOP_ODD (1 << 31) +#define TXSTS_QTOP_CHNEP_MASK (0xf << 27) +#define TXSTS_QTOP_CHNEP_SHIFT 27 +#define TXSTS_QTOP_TOKEN_MASK (0x3 << 25) +#define TXSTS_QTOP_TOKEN_SHIFT 25 +#define TXSTS_QTOP_TERMINATE (1 << 24) +#define TXSTS_QSPCAVAIL_MASK (0xff << 16) +#define TXSTS_QSPCAVAIL_SHIFT 16 +#define TXSTS_FSPCAVAIL_MASK (0xffff << 0) +#define TXSTS_FSPCAVAIL_SHIFT 0 + +#define HAINT HSOTG_REG(0x0414) +#define HAINTMSK HSOTG_REG(0x0418) +#define HFLBADDR HSOTG_REG(0x041c) + +#define HPRT0 HSOTG_REG(0x0440) +#define HPRT0_SPD_MASK (0x3 << 17) +#define HPRT0_SPD_SHIFT 17 +#define HPRT0_SPD_HIGH_SPEED 0 +#define HPRT0_SPD_FULL_SPEED 1 +#define HPRT0_SPD_LOW_SPEED 2 +#define HPRT0_TSTCTL_MASK (0xf << 13) +#define HPRT0_TSTCTL_SHIFT 13 +#define HPRT0_PWR (1 << 12) +#define HPRT0_LNSTS_MASK (0x3 << 10) +#define HPRT0_LNSTS_SHIFT 10 +#define HPRT0_RST (1 << 8) +#define HPRT0_SUSP (1 << 7) +#define HPRT0_RES (1 << 6) +#define HPRT0_OVRCURRCHG (1 << 5) +#define HPRT0_OVRCURRACT (1 << 4) +#define HPRT0_ENACHG (1 << 3) +#define HPRT0_ENA (1 << 2) +#define HPRT0_CONNDET (1 << 1) +#define HPRT0_CONNSTS (1 << 0) + +#define HCCHAR(_ch) HSOTG_REG(0x0500 + 0x20 * (_ch)) +#define HCCHAR_CHENA (1 << 31) +#define HCCHAR_CHDIS (1 << 30) +#define HCCHAR_ODDFRM (1 << 29) +#define HCCHAR_DEVADDR_MASK (0x7f << 22) +#define HCCHAR_DEVADDR_SHIFT 22 +#define HCCHAR_MULTICNT_MASK (0x3 << 20) +#define HCCHAR_MULTICNT_SHIFT 20 +#define HCCHAR_EPTYPE_MASK (0x3 << 18) +#define HCCHAR_EPTYPE_SHIFT 18 +#define HCCHAR_LSPDDEV (1 << 17) +#define HCCHAR_EPDIR (1 << 15) +#define HCCHAR_EPNUM_MASK (0xf << 11) +#define HCCHAR_EPNUM_SHIFT 11 +#define HCCHAR_MPS_MASK (0x7ff << 0) +#define HCCHAR_MPS_SHIFT 0 + +#define HCSPLT(_ch) HSOTG_REG(0x0504 + 0x20 * (_ch)) +#define HCSPLT_SPLTENA (1 << 31) +#define HCSPLT_COMPSPLT (1 << 16) +#define HCSPLT_XACTPOS_MASK (0x3 << 14) +#define HCSPLT_XACTPOS_SHIFT 14 +#define HCSPLT_XACTPOS_MID 0 +#define HCSPLT_XACTPOS_END 1 +#define HCSPLT_XACTPOS_BEGIN 2 +#define HCSPLT_XACTPOS_ALL 3 +#define HCSPLT_HUBADDR_MASK (0x7f << 7) +#define HCSPLT_HUBADDR_SHIFT 7 +#define HCSPLT_PRTADDR_MASK (0x7f << 0) +#define HCSPLT_PRTADDR_SHIFT 0 + +#define HCINT(_ch) HSOTG_REG(0x0508 + 0x20 * (_ch)) +#define HCINTMSK(_ch) HSOTG_REG(0x050c + 0x20 * (_ch)) +#define HCINTMSK_RESERVED14_31 (0x3ffff << 14) +#define HCINTMSK_FRM_LIST_ROLL (1 << 13) +#define HCINTMSK_XCS_XACT (1 << 12) +#define HCINTMSK_BNA (1 << 11) +#define HCINTMSK_DATATGLERR (1 << 10) +#define HCINTMSK_FRMOVRUN (1 << 9) +#define HCINTMSK_BBLERR (1 << 8) +#define HCINTMSK_XACTERR (1 << 7) +#define HCINTMSK_NYET (1 << 6) +#define HCINTMSK_ACK (1 << 5) +#define HCINTMSK_NAK (1 << 4) +#define HCINTMSK_STALL (1 << 3) +#define HCINTMSK_AHBERR (1 << 2) +#define HCINTMSK_CHHLTD (1 << 1) +#define HCINTMSK_XFERCOMPL (1 << 0) + +#define HCTSIZ(_ch) HSOTG_REG(0x0510 + 0x20 * (_ch)) +#define TSIZ_DOPNG (1 << 31) +#define TSIZ_SC_MC_PID_MASK (0x3 << 29) +#define TSIZ_SC_MC_PID_SHIFT 29 +#define TSIZ_SC_MC_PID_DATA0 0 +#define TSIZ_SC_MC_PID_DATA2 1 +#define TSIZ_SC_MC_PID_DATA1 2 +#define TSIZ_SC_MC_PID_MDATA 3 +#define TSIZ_SC_MC_PID_SETUP 3 +#define TSIZ_PKTCNT_MASK (0x3ff << 19) +#define TSIZ_PKTCNT_SHIFT 19 +#define TSIZ_NTD_MASK (0xff << 8) +#define TSIZ_NTD_SHIFT 8 +#define TSIZ_SCHINFO_MASK (0xff << 0) +#define TSIZ_SCHINFO_SHIFT 0 +#define TSIZ_XFERSIZE_MASK (0x7ffff << 0) +#define TSIZ_XFERSIZE_SHIFT 0 + +#define HCDMA(_ch) HSOTG_REG(0x0514 + 0x20 * (_ch)) +#define HCDMA_DMA_ADDR_MASK (0x1fffff << 11) +#define HCDMA_DMA_ADDR_SHIFT 11 +#define HCDMA_CTD_MASK (0xff << 3) +#define HCDMA_CTD_SHIFT 3 + +#define HCDMAB(_ch) HSOTG_REG(0x051c + 0x20 * (_ch)) + +#define HCFIFO(_ch) HSOTG_REG(0x1000 + 0x1000 * (_ch)) + +/** + * struct dwc2_hcd_dma_desc - Host-mode DMA descriptor structure + * + * @status: DMA descriptor status quadlet + * @buf: DMA descriptor data buffer pointer + * + * DMA Descriptor structure contains two quadlets: + * Status quadlet and Data buffer pointer. + */ +struct dwc2_hcd_dma_desc { + u32 status; + u32 buf; +}; + +#define HOST_DMA_A (1 << 31) +#define HOST_DMA_STS_MASK (0x3 << 28) +#define HOST_DMA_STS_SHIFT 28 +#define HOST_DMA_STS_PKTERR (1 << 28) +#define HOST_DMA_EOL (1 << 26) +#define HOST_DMA_IOC (1 << 25) +#define HOST_DMA_SUP (1 << 24) +#define HOST_DMA_ALT_QTD (1 << 23) +#define HOST_DMA_QTD_OFFSET_MASK (0x3f << 17) +#define HOST_DMA_QTD_OFFSET_SHIFT 17 +#define HOST_DMA_ISOC_NBYTES_MASK (0xfff << 0) +#define HOST_DMA_ISOC_NBYTES_SHIFT 0 +#define HOST_DMA_NBYTES_MASK (0x1ffff << 0) +#define HOST_DMA_NBYTES_SHIFT 0 + +#define MAX_DMA_DESC_SIZE 131071 +#define MAX_DMA_DESC_NUM_GENERIC 64 +#define MAX_DMA_DESC_NUM_HS_ISOC 256 + +#endif /* __DWC2_HW_H__ */ diff --git a/drivers/usb/dwc2/pci.c b/drivers/usb/dwc2/pci.c new file mode 100644 index 0000000..c291fca --- /dev/null +++ b/drivers/usb/dwc2/pci.c @@ -0,0 +1,178 @@ +/* + * pci.c - DesignWare HS OTG Controller PCI driver + * + * Copyright (C) 2004-2013 Synopsys, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Provides the initialization and cleanup entry points for the DWC_otg PCI + * driver + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/pci.h> +#include <linux/usb.h> + +#include <linux/usb/hcd.h> +#include <linux/usb/ch11.h> + +#include "core.h" +#include "hcd.h" + +#define PCI_VENDOR_ID_SYNOPSYS 0x16c3 +#define PCI_PRODUCT_ID_HAPS_HSOTG 0xabc0 + +static const char dwc2_driver_name[] = "dwc2"; + +static const struct dwc2_core_params dwc2_module_params = { + .otg_cap = -1, + .otg_ver = -1, + .dma_enable = -1, + .dma_desc_enable = 0, + .speed = -1, + .enable_dynamic_fifo = -1, + .en_multiple_tx_fifo = -1, + .host_rx_fifo_size = 1024, + .host_nperio_tx_fifo_size = 256, + .host_perio_tx_fifo_size = 1024, + .max_transfer_size = 65535, + .max_packet_count = 511, + .host_channels = -1, + .phy_type = -1, + .phy_utmi_width = -1, + .phy_ulpi_ddr = -1, + .phy_ulpi_ext_vbus = -1, + .i2c_enable = -1, + .ulpi_fs_ls = -1, + .host_support_fs_ls_low_power = -1, + .host_ls_low_power_phy_clk = -1, + .ts_dline = -1, + .reload_ctl = -1, + .ahbcfg = -1, + .uframe_sched = -1, +}; + +/** + * dwc2_driver_remove() - Called when the DWC_otg core is unregistered with the + * DWC_otg driver + * + * @dev: Bus device + * + * This routine is called, for example, when the rmmod command is executed. The + * device may or may not be electrically present. If it is present, the driver + * stops device processing. Any resources used on behalf of this device are + * freed. + */ +static void dwc2_driver_remove(struct pci_dev *dev) +{ + struct dwc2_hsotg *hsotg = pci_get_drvdata(dev); + + dwc2_hcd_remove(hsotg); + pci_disable_device(dev); +} + +/** + * dwc2_driver_probe() - Called when the DWC_otg core is bound to the DWC_otg + * driver + * + * @dev: Bus device + * + * This routine creates the driver components required to control the device + * (core, HCD, and PCD) and initializes the device. The driver components are + * stored in a dwc2_hsotg structure. A reference to the dwc2_hsotg is saved + * in the device private data. This allows the driver to access the dwc2_hsotg + * structure on subsequent calls to driver methods for this device. + */ +static int dwc2_driver_probe(struct pci_dev *dev, + const struct pci_device_id *id) +{ + struct dwc2_hsotg *hsotg; + int retval; + + hsotg = devm_kzalloc(&dev->dev, sizeof(*hsotg), GFP_KERNEL); + if (!hsotg) + return -ENOMEM; + + hsotg->dev = &dev->dev; + hsotg->regs = devm_ioremap_resource(&dev->dev, &dev->resource[0]); + if (IS_ERR(hsotg->regs)) + return PTR_ERR(hsotg->regs); + + dev_dbg(&dev->dev, "mapped PA %08lx to VA %p\n", + (unsigned long)pci_resource_start(dev, 0), hsotg->regs); + + if (pci_enable_device(dev) < 0) + return -ENODEV; + + pci_set_master(dev); + + retval = dwc2_hcd_init(hsotg, dev->irq, &dwc2_module_params); + if (retval) { + pci_disable_device(dev); + return retval; + } + + pci_set_drvdata(dev, hsotg); + + return retval; +} + +static const struct pci_device_id dwc2_pci_ids[] = { + { + PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, PCI_PRODUCT_ID_HAPS_HSOTG), + }, + { + PCI_DEVICE(PCI_VENDOR_ID_STMICRO, + PCI_DEVICE_ID_STMICRO_USB_OTG), + }, + { /* end: all zeroes */ } +}; +MODULE_DEVICE_TABLE(pci, dwc2_pci_ids); + +static struct pci_driver dwc2_pci_driver = { + .name = dwc2_driver_name, + .id_table = dwc2_pci_ids, + .probe = dwc2_driver_probe, + .remove = dwc2_driver_remove, +}; + +module_pci_driver(dwc2_pci_driver); + +MODULE_DESCRIPTION("DESIGNWARE HS OTG PCI Bus Glue"); +MODULE_AUTHOR("Synopsys, Inc."); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c new file mode 100644 index 0000000..eaba547 --- /dev/null +++ b/drivers/usb/dwc2/platform.c @@ -0,0 +1,190 @@ +/* + * platform.c - DesignWare HS OTG Controller platform driver + * + * Copyright (C) Matthijs Kooijman <matthijs@stdin.nl> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> + +#include "core.h" +#include "hcd.h" + +static const char dwc2_driver_name[] = "dwc2"; + +static const struct dwc2_core_params params_bcm2835 = { + .otg_cap = 0, /* HNP/SRP capable */ + .otg_ver = 0, /* 1.3 */ + .dma_enable = 1, + .dma_desc_enable = 0, + .speed = 0, /* High Speed */ + .enable_dynamic_fifo = 1, + .en_multiple_tx_fifo = 1, + .host_rx_fifo_size = 774, /* 774 DWORDs */ + .host_nperio_tx_fifo_size = 256, /* 256 DWORDs */ + .host_perio_tx_fifo_size = 512, /* 512 DWORDs */ + .max_transfer_size = 65535, + .max_packet_count = 511, + .host_channels = 8, + .phy_type = 1, /* UTMI */ + .phy_utmi_width = 8, /* 8 bits */ + .phy_ulpi_ddr = 0, /* Single */ + .phy_ulpi_ext_vbus = 0, + .i2c_enable = 0, + .ulpi_fs_ls = 0, + .host_support_fs_ls_low_power = 0, + .host_ls_low_power_phy_clk = 0, /* 48 MHz */ + .ts_dline = 0, + .reload_ctl = 0, + .ahbcfg = 0x10, + .uframe_sched = 0, +}; + +/** + * dwc2_driver_remove() - Called when the DWC_otg core is unregistered with the + * DWC_otg driver + * + * @dev: Platform device + * + * This routine is called, for example, when the rmmod command is executed. The + * device may or may not be electrically present. If it is present, the driver + * stops device processing. Any resources used on behalf of this device are + * freed. + */ +static int dwc2_driver_remove(struct platform_device *dev) +{ + struct dwc2_hsotg *hsotg = platform_get_drvdata(dev); + + dwc2_hcd_remove(hsotg); + + return 0; +} + +static const struct of_device_id dwc2_of_match_table[] = { + { .compatible = "brcm,bcm2835-usb", .data = ¶ms_bcm2835 }, + { .compatible = "snps,dwc2", .data = NULL }, + {}, +}; +MODULE_DEVICE_TABLE(of, dwc2_of_match_table); + +/** + * dwc2_driver_probe() - Called when the DWC_otg core is bound to the DWC_otg + * driver + * + * @dev: Platform device + * + * This routine creates the driver components required to control the device + * (core, HCD, and PCD) and initializes the device. The driver components are + * stored in a dwc2_hsotg structure. A reference to the dwc2_hsotg is saved + * in the device private data. This allows the driver to access the dwc2_hsotg + * structure on subsequent calls to driver methods for this device. + */ +static int dwc2_driver_probe(struct platform_device *dev) +{ + const struct of_device_id *match; + const struct dwc2_core_params *params; + struct dwc2_core_params defparams; + struct dwc2_hsotg *hsotg; + struct resource *res; + int retval; + int irq; + + if (usb_disabled()) + return -ENODEV; + + match = of_match_device(dwc2_of_match_table, &dev->dev); + if (match && match->data) { + params = match->data; + } else { + /* Default all params to autodetect */ + dwc2_set_all_params(&defparams, -1); + params = &defparams; + } + + hsotg = devm_kzalloc(&dev->dev, sizeof(*hsotg), GFP_KERNEL); + if (!hsotg) + return -ENOMEM; + + hsotg->dev = &dev->dev; + + /* + * Use reasonable defaults so platforms don't have to provide these. + */ + if (!dev->dev.dma_mask) + dev->dev.dma_mask = &dev->dev.coherent_dma_mask; + retval = dma_set_coherent_mask(&dev->dev, DMA_BIT_MASK(32)); + if (retval) + return retval; + + irq = platform_get_irq(dev, 0); + if (irq < 0) { + dev_err(&dev->dev, "missing IRQ resource\n"); + return irq; + } + + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + hsotg->regs = devm_ioremap_resource(&dev->dev, res); + if (IS_ERR(hsotg->regs)) + return PTR_ERR(hsotg->regs); + + dev_dbg(&dev->dev, "mapped PA %08lx to VA %p\n", + (unsigned long)res->start, hsotg->regs); + + retval = dwc2_hcd_init(hsotg, irq, params); + if (retval) + return retval; + + platform_set_drvdata(dev, hsotg); + + return retval; +} + +static struct platform_driver dwc2_platform_driver = { + .driver = { + .name = dwc2_driver_name, + .of_match_table = dwc2_of_match_table, + }, + .probe = dwc2_driver_probe, + .remove = dwc2_driver_remove, +}; + +module_platform_driver(dwc2_platform_driver); + +MODULE_DESCRIPTION("DESIGNWARE HS OTG Platform Glue"); +MODULE_AUTHOR("Matthijs Kooijman <matthijs@stdin.nl>"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 70fc430..e2c730f 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 dd17601..10ac3e7 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/core.c b/drivers/usb/dwc3/core.c index 74f9cf0..a49217a 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -455,9 +455,6 @@ static int dwc3_probe(struct platform_device *pdev) if (IS_ERR(regs)) return PTR_ERR(regs); - usb_phy_set_suspend(dwc->usb2_phy, 0); - usb_phy_set_suspend(dwc->usb3_phy, 0); - spin_lock_init(&dwc->lock); platform_set_drvdata(pdev, dwc); @@ -488,6 +485,9 @@ static int dwc3_probe(struct platform_device *pdev) goto err0; } + usb_phy_set_suspend(dwc->usb2_phy, 0); + usb_phy_set_suspend(dwc->usb3_phy, 0); + ret = dwc3_event_buffers_setup(dwc); if (ret) { dev_err(dwc->dev, "failed to setup event buffers\n"); @@ -569,6 +569,8 @@ err2: dwc3_event_buffers_cleanup(dwc); err1: + usb_phy_set_suspend(dwc->usb2_phy, 1); + usb_phy_set_suspend(dwc->usb3_phy, 1); dwc3_core_exit(dwc); err0: diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c index 8b20c70..28c8ad7 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-keystone.c b/drivers/usb/dwc3/dwc3-keystone.c new file mode 100644 index 0000000..1fad161 --- /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 <w-kwok2@ti.com> + * + * 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 <linux/clk.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/io.h> +#include <linux/of_platform.h> + +/* 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 <w-kwok2@ti.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("DesignWare USB3 KEYSTONE Glue Layer"); diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 7f7ea62..b269dbd 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -20,7 +20,6 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/interrupt.h> -#include <linux/spinlock.h> #include <linux/platform_device.h> #include <linux/platform_data/dwc3-omap.h> #include <linux/pm_runtime.h> @@ -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; @@ -535,7 +525,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; } diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 31443ae..f393c18 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) @@ -182,7 +183,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), diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 02e44fc..2da0a5a 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1650,7 +1650,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) @@ -1658,7 +1658,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, @@ -2597,6 +2597,12 @@ int dwc3_gadget_init(struct dwc3 *dwc) 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. */ diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index f66d96a..8154165 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 @@ -294,11 +301,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" @@ -512,9 +519,6 @@ config USB_U_SERIAL config USB_U_ETHER tristate -config USB_U_RNDIS - tristate - config USB_F_SERIAL tristate @@ -542,6 +546,9 @@ config USB_F_RNDIS config USB_F_MASS_STORAGE tristate +config USB_F_FS + tristate + choice tristate "USB Gadget Drivers" default USB_ETH @@ -642,7 +649,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, @@ -690,6 +696,31 @@ 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_SS + boolean "Loopback and sourcesink function (for testing)" + depends on USB_CONFIGFS + select USB_F_SS_LB + help + 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 + 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 @@ -760,7 +791,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 @@ -864,6 +894,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 @@ -883,6 +914,8 @@ config USB_FUNCTIONFS_ETH bool "Include configuration with CDC ECM (Ethernet)" 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. @@ -891,7 +924,7 @@ 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. @@ -1065,7 +1098,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 f1af396..5f150bc 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 @@ -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 @@ -47,8 +48,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,10 +58,12 @@ 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 +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/acm_ms.c b/drivers/usb/gadget/acm_ms.c index 7bfa134..a252444 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/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index 54a1e29..41b062e 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -40,7 +40,6 @@ #include <linux/sched.h> #include <linux/slab.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/timer.h> #include <linux/list.h> #include <linux/interrupt.h> @@ -446,7 +445,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 +1563,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); } /* @@ -3338,7 +3340,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, diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index 4cc4fd6..cea8c20 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -21,7 +21,6 @@ #include <linux/ioport.h> #include <linux/slab.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/list.h> #include <linux/interrupt.h> #include <linux/proc_fs.h> @@ -834,7 +833,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 +1758,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].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()) { - udc->ep[3].maxpacket = 64; + usb_ep_set_maxpacket_limit(&udc->ep[3].ep, 64); } else if (cpu_is_at91sam9263()) { - udc->ep[0].maxpacket = 64; - udc->ep[3].maxpacket = 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)); diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index 2cb52e0..52771d4 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -326,7 +326,7 @@ static int vbus_is_present(struct usba_udc *udc) #if defined(CONFIG_ARCH_AT91SAM9RL) -#include <mach/at91_pmc.h> +#include <linux/clk/at91_pmc.h> static void toggle_bias(int is_on) { @@ -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", @@ -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; @@ -1995,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; @@ -2011,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); @@ -2032,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); @@ -2043,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) { @@ -2077,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) @@ -2117,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; } diff --git a/drivers/usb/gadget/bcm63xx_udc.c b/drivers/usb/gadget/bcm63xx_udc.c index c58fcf1..888fbb4 100644 --- a/drivers/usb/gadget/bcm63xx_udc.c +++ b/drivers/usb/gadget/bcm63xx_udc.c @@ -19,7 +19,6 @@ #include <linux/device.h> #include <linux/dma-mapping.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/ioport.h> #include <linux/kconfig.h> @@ -549,7 +548,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 +942,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/composite.c b/drivers/usb/gadget/composite.c index 2018ba1..d742bed 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1452,8 +1452,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; @@ -1714,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? diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 2588511..7d1cc01 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -4,6 +4,7 @@ #include <linux/device.h> #include <linux/usb/composite.h> #include <linux/usb/gadget_configfs.h> +#include "configfs.h" int check_user_usb_string(const char *name, struct usb_gadget_strings *stringtab_dev) @@ -564,6 +565,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/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index 8f4dae3..8c06430 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 a777f7b..0567cca 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -11,7 +11,6 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/types.h> #include <linux/device.h> @@ -58,7 +57,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; @@ -129,7 +128,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 +144,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 +177,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/f_ecm.c b/drivers/usb/gadget/f_ecm.c index 8d9e6f7..798760f 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/f_fs.c b/drivers/usb/gadget/f_fs.c index 241fc87..306a2b5 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -22,218 +22,42 @@ #include <linux/pagemap.h> #include <linux/export.h> #include <linux/hid.h> +#include <linux/module.h> #include <asm/unaligned.h> #include <linux/usb/composite.h> #include <linux/usb/functionfs.h> +#include "u_fs.h" +#include "configfs.h" #define FUNCTIONFS_MAGIC 0xa647361 /* Chosen by a honest dice roll ;) */ - -/* 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 - * 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_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; -}; +/* 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)) /* Reference counter handling */ static void ffs_data_get(struct ffs_data *ffs); @@ -274,15 +98,12 @@ static struct ffs_function *ffs_func_from_usb(struct usb_function *f) return container_of(f, struct ffs_function, function); } -static void ffs_func_free(struct ffs_function *func); 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 *, - struct usb_function *); 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 *, @@ -335,6 +156,17 @@ 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); +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); +static void ffs_closed(struct ffs_data *ffs); /* Misc helper functions ****************************************************/ @@ -460,7 +292,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; @@ -753,78 +585,71 @@ 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; - goto first_try; - do { - spin_unlock_irq(&epfile->ffs->eps_lock); - mutex_unlock(&epfile->mutex); + /* Are we still active? */ + if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) { + ret = -ENODEV; + goto error; + } -first_try: - /* Are we still active? */ - if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) { - ret = -ENODEV; + /* Wait for endpoint to be enabled */ + ep = epfile->ep; + if (!ep) { + if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; 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; + ret = wait_event_interruptible(epfile->wait, (ep = epfile->ep)); + if (ret) { + ret = -EINTR; goto error; } + } - /* Allocate & copy */ - if (!halt && !data) { - data = kzalloc(len, GFP_KERNEL); - if (unlikely(!data)) - return -ENOMEM; + /* Do we halt? */ + halt = !read == !epfile->in; + if (halt && epfile->isoc) { + ret = -EINVAL; + goto error; + } - if (!read && - unlikely(__copy_from_user(data, buf, len))) { - ret = -EFAULT; - goto error; - } - } + /* Allocate & copy */ + if (!halt) { + /* + * 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; - /* We will be using request */ - ret = ffs_mutex_lock(&epfile->mutex, - file->f_flags & O_NONBLOCK); - if (unlikely(ret)) + if (!read && unlikely(copy_from_user(data, buf, len))) { + ret = -EFAULT; goto error; + } + } - /* - * We're called from user space, we can use _irq rather then - * _irqsave - */ - spin_lock_irq(&epfile->ffs->eps_lock); + /* We will be using request */ + ret = ffs_mutex_lock(&epfile->mutex, file->f_flags & O_NONBLOCK); + if (unlikely(ret)) + goto error; - /* - * While we were acquiring mutex endpoint got disabled - * or changed? - */ - } while (unlikely(epfile->ep != ep)); + spin_lock_irq(&epfile->ffs->eps_lock); - /* Halt */ - if (unlikely(halt)) { + if (epfile->ep != ep) { + /* In the meantime, endpoint got disabled or changed. */ + ret = -ESHUTDOWN; + spin_unlock_irq(&epfile->ffs->eps_lock); + } 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); @@ -837,7 +662,7 @@ first_try: 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); @@ -849,9 +674,17 @@ first_try: 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; } } @@ -1191,7 +1024,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); @@ -1201,7 +1034,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; @@ -1214,7 +1047,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); } } @@ -1327,7 +1160,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); @@ -1463,71 +1296,6 @@ static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count) kfree(epfiles); } -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 = 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); -} static void ffs_func_eps_disable(struct ffs_function *func) { @@ -1901,30 +1669,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)); + + char *vlabuf = kmalloc(vla_group_size(d), GFP_KERNEL); - d = kmalloc(sizeof *d, GFP_KERNEL); - if (unlikely(!d)) { + 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; } @@ -2187,8 +1959,57 @@ 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) +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; +} + +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; @@ -2200,16 +2021,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(); @@ -2217,21 +2038,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; + + ptr = vla_ptr(vlabuf, d, eps); + ptr[ret].num = -1; + } - /* Save pointers */ - func->eps = data->eps; - func->interfaces_nums = data->inums; + /* 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 @@ -2239,10 +2067,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; @@ -2251,10 +2079,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; @@ -2267,7 +2095,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; @@ -2281,26 +2109,19 @@ error: return ret; } - -/* Other USB function hooks *************************************************/ - -static void ffs_func_unbind(struct usb_configuration *c, - struct usb_function *f) +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; + struct f_fs_opts *ffs_opts = ffs_do_functionfs_bind(f, c); - ENTER(); + if (IS_ERR(ffs_opts)) + return PTR_ERR(ffs_opts); - if (ffs->func == func) { - ffs_func_eps_disable(func); - ffs->func = NULL; - } + return _ffs_func_bind(c, f); +} - ffs_event_add(ffs, FUNCTIONFS_UNBIND); - ffs_func_free(func); -} +/* Other USB function hooks *************************************************/ static int ffs_func_set_alt(struct usb_function *f, unsigned interface, unsigned alt) @@ -2428,6 +2249,411 @@ 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); +} + +/* 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) +{ + struct f_fs_opts *opts; + + opts = to_f_fs_opts(f); + ffs_dev_lock(); + ffs_free_dev(opts->dev); + ffs_dev_unlock(); + 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; + struct ffs_dev *dev; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + 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(); + ffs_dev_unlock(); + if (IS_ERR(dev)) { + kfree(opts); + 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; +} + +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; +} + +/* + * 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; +} +EXPORT_SYMBOL(ffs_name_dev); + +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; +} +EXPORT_SYMBOL(ffs_single_dev); + +/* + * ffs_lock must be taken by the caller of this function + */ +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(); +} + +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 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; + + 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; + + if (ffs_dev->ffs_release_dev_callback) + ffs_dev->ffs_release_dev_callback(ffs_dev); + + 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); + + 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(); +} + /* Misc helper functions ****************************************************/ static int ffs_mutex_lock(struct mutex *mutex, unsigned nonblock) @@ -2458,3 +2684,7 @@ static char *ffs_prepare_buffer(const char __user *buf, size_t len) return data; } + +DECLARE_USB_FUNCTION_INIT(ffs, ffs_alloc_inst, ffs_alloc); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michal Nazarewicz"); diff --git a/drivers/usb/gadget/f_hid.c b/drivers/usb/gadget/f_hid.c index 6e69a8e..a95290a 100644 --- a/drivers/usb/gadget/f_hid.c +++ b/drivers/usb/gadget/f_hid.c @@ -20,6 +20,8 @@ #include <linux/sched.h> #include <linux/usb/g_hid.h> +#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 4a3873a..4557cd0 100644 --- a/drivers/usb/gadget/f_loopback.c +++ b/drivers/usb/gadget/f_loopback.c @@ -20,6 +20,7 @@ #include <linux/usb/composite.h> #include "g_zero.h" +#include "u_f.h" /* * LOOPBACK FUNCTION ... a testing vehicle for USB peripherals, @@ -119,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, @@ -135,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, @@ -230,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)); } @@ -293,6 +302,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 +346,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); @@ -380,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) @@ -396,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; @@ -411,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/f_midi.c b/drivers/usb/gadget/f_midi.c index 263e721..36d4bb2 100644 --- a/drivers/usb/gadget/f_midi.c +++ b/drivers/usb/gadget/f_midi.c @@ -32,6 +32,8 @@ #include <linux/usb/audio.h> #include <linux/usb/midi.h> +#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_ncm.c b/drivers/usb/gadget/f_ncm.c index 1c28fe1..a9499fd 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; diff --git a/drivers/usb/gadget/f_obex.c b/drivers/usb/gadget/f_obex.c index ad39f1d..aebae18 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; diff --git a/drivers/usb/gadget/f_phonet.c b/drivers/usb/gadget/f_phonet.c index eb3aa81..f2b7817 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; diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c index 717ed7f..c11761c 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; @@ -1047,8 +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"); - -#endif diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/f_serial.c index 981113c..9ecbcbf 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; diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/f_sourcesink.c index a889585..d3cd52d 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 @@ -201,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, @@ -218,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, @@ -236,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, @@ -254,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, @@ -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) @@ -490,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)); } @@ -628,10 +623,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) @@ -878,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; @@ -898,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; @@ -913,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/f_subset.c b/drivers/usb/gadget/f_subset.c index 7c8674f..f1a5919 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/fotg210-udc.c b/drivers/usb/gadget/fotg210-udc.c index bbbfd19..2d03052 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 807127d..ad54833 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -22,7 +22,6 @@ #include <linux/module.h> #include <linux/kernel.h> -#include <linux/init.h> #include <linux/ioport.h> #include <linux/types.h> #include <linux/errno.h> @@ -2429,7 +2428,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; @@ -2717,7 +2716,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 36ac7cf..15960af 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 */ @@ -2666,7 +2667,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, diff --git a/drivers/usb/gadget/fusb300_udc.c b/drivers/usb/gadget/fusb300_udc.c index b278abe..6423f18 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/g_ffs.c b/drivers/usb/gadget/g_ffs.c index 2344efe..fe12e6a 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c @@ -13,14 +13,10 @@ #define pr_fmt(fmt) "g_ffs: " fmt #include <linux/module.h> -/* - * 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 <linux/netdevice.h> + # if defined USB_ETH_RNDIS # undef USB_ETH_RNDIS # endif @@ -28,31 +24,31 @@ # define USB_ETH_RNDIS y # endif -#define USBF_ECM_INCLUDED -# include "f_ecm.c" -#define USB_FSUBSET_INCLUDED -# include "f_subset.c" +# 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" -static u8 gfs_host_mac[ETH_ALEN]; -static struct eth_dev *the_dev; +USB_ETHERNET_MODULE_PARAMETERS(); + # 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 +# 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 -#else -# define the_dev NULL -# define gether_cleanup(dev) do { } while (0) -# define gfs_host_mac NULL -struct eth_dev; #endif -#include "f_fs.c" +#include "u_fs.h" #define DRIVER_NAME "g_ffs" #define DRIVER_DESC "USB Function Filesystem" @@ -67,19 +63,8 @@ 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(); -#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, @@ -146,12 +131,12 @@ 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); + int num; } gfs_configurations[] = { #ifdef CONFIG_USB_FUNCTIONFS_RNDIS { - .eth = rndis_bind_config, + .eth = bind_rndis_config, }, #endif @@ -167,10 +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, @@ -180,206 +170,244 @@ static __refdata struct usb_composite_driver gfs_driver = { .unbind = gfs_unbind, }; -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; +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; 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); - 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; - if (!gfs_single_func) - for (i = 0; i < func_num; i++) - ffs_tab[i].name = func_names[i]; + 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++) { + 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(opts->dev); + else + ret = ffs_name_dev(opts->dev, func_names[i]); + if (ret) + goto no_dev; + 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 functionfs_init(); + return 0; +no_dev: + while (i >= 0) + usb_put_function_instance(fi_ffs[i--]); + kfree(fi_ffs); +no_func: + kfree(f_ffs[0]); + return ret; } module_init(gfs_init); static void __exit gfs_exit(void) { + int i; + ENTER(); - mutex_lock(&gfs_lock); if (gfs_registered) usb_composite_unregister(&gfs_driver); gfs_registered = false; - functionfs_cleanup(); + kfree(f_ffs[0]); + + for (i = 0; i < func_num; i++) + usb_put_function_instance(fi_ffs[i]); - mutex_unlock(&gfs_lock); - kfree(ffs_tab); + kfree(fi_ffs); } module_exit(gfs_exit); -static struct gfs_ffs_obj *gfs_find_dev(const char *dev_name) +static void *functionfs_acquire_dev(struct ffs_dev *dev) { - 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]; + if (!try_module_get(THIS_MODULE)) + return ERR_PTR(-ENODEV); + + return 0; +} - return NULL; +static void functionfs_release_dev(struct ffs_dev *dev) +{ + module_put(THIS_MODULE); } +/* + * The caller of this function takes ffs_lock + */ static int functionfs_ready_callback(struct ffs_data *ffs) { - struct gfs_ffs_obj *ffs_obj; - int ret; - - ENTER(); - mutex_lock(&gfs_lock); + int ret = 0; - ffs_obj = ffs->private_data; - if (!ffs_obj) { - ret = -EINVAL; - goto done; - } + if (--missing_funcs) + return 0; - 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) + return -EBUSY; - 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 gfs_ffs_obj *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) +/* + * 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) { - struct gfs_ffs_obj *ffs_dev; +#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS + struct net_device *net; +#endif + int ret, i; 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; + if (missing_funcs) + return -ENODEV; +#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); + net = ecm_opts->net; + } else { + 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; } - ffs_dev->mounted = true; +#endif -done: - mutex_unlock(&gfs_lock); - return ffs_dev; -} +#ifdef CONFIG_USB_FUNCTIONFS_RNDIS + { + struct f_rndis_opts *rndis_opts; -static void functionfs_release_dev_callback(struct ffs_data *ffs_data) -{ - struct gfs_ffs_obj *ffs_dev; + 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 - ENTER(); - mutex_lock(&gfs_lock); +#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); +#endif - ffs_dev = ffs_data->private_data; - if (ffs_dev) - ffs_dev->mounted = false; +#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_rndis; - mutex_unlock(&gfs_lock); -} + if (can_support_ecm(cdev->gadget)) { + struct f_ecm_opts *ecm_opts; -/* - * It is assumed that gfs_bind is called from a context where gfs_lock is held - */ -static int gfs_bind(struct usb_composite_dev *cdev) -{ - int ret, i; + ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst); + ecm_opts->bound = true; + } else { + struct f_gether_opts *geth_opts; - ENTER(); + geth_opts = container_of(fi_geth, struct f_gether_opts, + func_inst); + geth_opts->bound = true; + } - if (missing_funcs) - return -ENODEV; -#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS - the_dev = gether_setup(cdev->gadget, dev_addr, host_addr, gfs_host_mac, - qmult); + rndis_borrow_net(fi_rndis, net); #endif - if (IS_ERR(the_dev)) { - ret = PTR_ERR(the_dev); - goto error_quick; - } - gfs_ether_setup = true; + /* TODO: gstrings_attach? */ 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--; ) { - 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; - } - } - for (i = 0; i < ARRAY_SIZE(gfs_configurations); ++i) { struct gfs_configuration *c = gfs_configurations + i; int sid = USB_GADGET_FIRST_AVAIL_IDX + i; @@ -389,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; @@ -396,18 +426,24 @@ 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); error: - gether_cleanup(the_dev); -error_quick: - gfs_ether_setup = false; +#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); +#endif return ret; } /* - * 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) { @@ -415,28 +451,30 @@ static int gfs_unbind(struct usb_composite_dev *cdev) ENTER(); - /* - * 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 - * 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); +#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); + usb_put_function_instance(fi_ecm); + } else { + usb_put_function(f_geth); + usb_put_function_instance(fi_geth); + } +#endif + for (i = 0; i < N_CONF * func_num; ++i) + usb_put_function(*(f_ffs[0] + i)); 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) { @@ -454,15 +492,22 @@ 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; } 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; + } } /* @@ -479,16 +524,59 @@ 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 -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; + + 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 { + 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; +} + +#endif + +#ifdef CONFIG_USB_FUNCTIONFS_RNDIS + +static int bind_rndis_config(struct usb_configuration *c) { - return can_support_ecm(c->cdev->gadget) - ? ecm_bind_config(c, ethaddr, dev) - : geth_bind_config(c, ethaddr, dev); + 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 diff --git a/drivers/usb/gadget/g_zero.h b/drivers/usb/gadget/g_zero.h index ef3e851..15f1809 100644 --- a/drivers/usb/gadget/g_zero.h +++ b/drivers/usb/gadget/g_zero.h @@ -6,6 +6,11 @@ #ifndef __G_ZERO_H #define __G_ZERO_H +#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; unsigned isoc_interval; @@ -24,19 +29,36 @@ 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 { 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); 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/goku_udc.c b/drivers/usb/gadget/goku_udc.c index f827680..6c85839 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/goku_udc.c @@ -30,7 +30,6 @@ #include <linux/ioport.h> #include <linux/slab.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/timer.h> #include <linux/list.h> #include <linux/interrupt.h> @@ -231,7 +230,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 +1250,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); } @@ -1350,16 +1349,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 +1372,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 +1516,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 +1531,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); diff --git a/drivers/usb/gadget/gr_udc.c b/drivers/usb/gadget/gr_udc.c new file mode 100644 index 0000000..914cbd8 --- /dev/null +++ b/drivers/usb/gadget/gr_udc.c @@ -0,0 +1,2238 @@ +/* + * 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 <andreas@gaisler.com> + * - 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 <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/errno.h> +#include <linux/list.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/dma-mapping.h> +#include <linux/dmapool.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/of_platform.h> +#include <linux/of_irq.h> +#include <linux/of_address.h> + +#include <asm/byteorder.h> + +#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; + + 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 0000000..8388897 --- /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 <andreas@gaisler.com> + * - 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)) diff --git a/drivers/usb/gadget/lpc32xx_udc.c b/drivers/usb/gadget/lpc32xx_udc.c index 6a2a65a..049ebab 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 d5f050d..8cae01d 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/multi.c b/drivers/usb/gadget/multi.c index 4fdaa54..940f6cd 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); diff --git a/drivers/usb/gadget/mv_u3d_core.c b/drivers/usb/gadget/mv_u3d_core.c index 234711e..d2ca59e 100644 --- a/drivers/usb/gadget/mv_u3d_core.c +++ b/drivers/usb/gadget/mv_u3d_core.c @@ -15,7 +15,6 @@ #include <linux/sched.h> #include <linux/slab.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/timer.h> #include <linux/list.h> #include <linux/notifier.h> @@ -1336,7 +1335,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 +1360,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 104cdbe..fcff3a5 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -20,7 +20,6 @@ #include <linux/slab.h> #include <linux/errno.h> #include <linux/err.h> -#include <linux/init.h> #include <linux/timer.h> #include <linux/list.h> #include <linux/interrupt.h> @@ -1261,7 +1260,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 +1283,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 bf2bb39..ca15405 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 fc85217..43e5e2f 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/nokia.c b/drivers/usb/gadget/nokia.c index 0a8099a..3ab3861 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); diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 83957cc..2ae4f6d 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -22,7 +22,6 @@ #include <linux/errno.h> #include <linux/delay.h> #include <linux/slab.h> -#include <linux/init.h> #include <linux/timer.h> #include <linux/list.h> #include <linux/interrupt.h> @@ -2586,7 +2585,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 32d5e92..eb8c3be 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); @@ -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, diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index 409a3c4..9984437 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -24,7 +24,6 @@ #include <linux/err.h> #include <linux/delay.h> #include <linux/slab.h> -#include <linux/init.h> #include <linux/timer.h> #include <linux/list.h> #include <linux/interrupt.h> @@ -1194,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 3c97da7..cdf4d67 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 68be48d..aff0a67 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) { @@ -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; @@ -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: diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c index a3ad732..d822d82 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/rndis.c @@ -25,7 +25,6 @@ #include <linux/moduleparam.h> #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/list.h> #include <linux/proc_fs.h> #include <linux/slab.h> @@ -1142,7 +1141,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 +1173,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 +1186,4 @@ static void rndis_exit(void) } #endif } -module_exit(rndis_exit); -MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index e20bc10..1172eae 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -30,14 +30,13 @@ #include <linux/clk.h> #include <linux/regulator/consumer.h> #include <linux/of_platform.h> +#include <linux/phy/phy.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> #include <linux/usb/phy.h> #include <linux/platform_data/s3c-hsotg.h> -#include <mach/map.h> - #include "s3c-hsotg.h" static const char * const s3c_hsotg_supply_names[] = { @@ -140,11 +139,13 @@ 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. * @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. @@ -161,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; @@ -172,6 +174,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; @@ -2086,13 +2089,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: @@ -2156,6 +2159,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) \ @@ -2283,7 +2289,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); @@ -2908,8 +2914,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); } @@ -2925,8 +2934,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); } @@ -3152,7 +3164,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; /* @@ -3533,7 +3545,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; @@ -3548,19 +3561,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; @@ -3627,6 +3647,19 @@ 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); + /* usb phy enable */ s3c_hsotg_phy_enable(hsotg); @@ -3720,6 +3753,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; @@ -3733,6 +3768,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); diff --git a/drivers/usb/gadget/s3c-hsotg.h b/drivers/usb/gadget/s3c-hsotg.h index d650b12..85f549f 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) diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index 1a1a414..ea4bbfe 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 c72d810..f04b2c3 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/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index 2aae0d6..b7d4f82 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 fb23d1f..0f0290a 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, @@ -267,45 +267,4 @@ static inline bool can_support_ecm(struct usb_gadget *gadget) return true; } -/* 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 - -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 */ diff --git a/drivers/usb/gadget/u_f.c b/drivers/usb/gadget/u_f.c new file mode 100644 index 0000000..63b6642 --- /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 <andrzej.p@samsung.com> + * + * 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 <linux/usb/gadget.h> +#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 0000000..71034c0 --- /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 <andrzej.p@samsung.com> + * + * 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__ */ + + diff --git a/drivers/usb/gadget/u_fs.h b/drivers/usb/gadget/u_fs.h new file mode 100644 index 0000000..bc2d371 --- /dev/null +++ b/drivers/usb/gadget/u_fs.h @@ -0,0 +1,267 @@ +/* + * u_fs.h + * + * Utility definitions for the FunctionFS + * + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> + * + * 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 <linux/usb/composite.h> +#include <linux/list.h> +#include <linux/mutex.h> + +#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 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); + 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; + +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); + +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; +}; + + +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 /* U_FFS_H */ diff --git a/drivers/usb/gadget/u_rndis.h b/drivers/usb/gadget/u_rndis.h index c62ba82..7291b15 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 */ diff --git a/drivers/usb/gadget/usbstring.c b/drivers/usb/gadget/usbstring.c index 1f49fce..73a4dfb 100644 --- a/drivers/usb/gadget/usbstring.c +++ b/drivers/usb/gadget/usbstring.c @@ -13,7 +13,6 @@ #include <linux/list.h> #include <linux/string.h> #include <linux/device.h> -#include <linux/init.h> #include <linux/nls.h> #include <linux/usb/ch9.h> diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c index f49b0b6..9f170c5 100644 --- a/drivers/usb/gadget/zero.c +++ b/drivers/usb/gadget/zero.c @@ -64,10 +64,10 @@ 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, - .bulk_buflen = 4096, - .qlen = 32, + .isoc_interval = GZERO_ISOC_INTERVAL, + .isoc_maxpacket = GZERO_ISOC_MAXPACKET, + .bulk_buflen = GZERO_BULK_BUFLEN, + .qlen = GZERO_QLEN, }; /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 01e879e..7530468 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -2,8 +2,6 @@ # Makefile for USB Host Controller Drivers # -ccflags-$(CONFIG_USB_DEBUG) := -DDEBUG - # tell define_trace.h where to find the xhci trace header CFLAGS_xhci-trace.o := -I$(src) diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c index 284f841..ec9f7b7 100644 --- a/drivers/usb/host/ehci-atmel.c +++ b/drivers/usb/host/ehci-atmel.c @@ -153,6 +153,7 @@ static int ehci_atmel_drv_probe(struct platform_device *pdev) retval = usb_add_hcd(hcd, irq, IRQF_SHARED); if (retval) goto fail_add_hcd; + device_wakeup_enable(hcd->self.controller); return retval; diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 4a9c2ed..524cbf2 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -18,7 +18,7 @@ /* this file is part of ehci-hcd.c */ -#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG) +#ifdef CONFIG_DYNAMIC_DEBUG /* check the values in the HCSPARAMS register * (host controller _Structural_ parameters) @@ -62,7 +62,7 @@ static inline void dbg_hcs_params (struct ehci_hcd *ehci, char *label) {} #endif -#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG) +#ifdef CONFIG_DYNAMIC_DEBUG /* check the values in the HCCPARAMS register * (host controller _Capability_ parameters) @@ -101,7 +101,7 @@ static inline void dbg_hcc_params (struct ehci_hcd *ehci, char *label) {} #endif -#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG) +#ifdef CONFIG_DYNAMIC_DEBUG static void __maybe_unused dbg_qtd (const char *label, struct ehci_hcd *ehci, struct ehci_qtd *qtd) @@ -301,7 +301,7 @@ static inline int __maybe_unused dbg_port_buf (char *buf, unsigned len, const char *label, int port, u32 status) { return 0; } -#endif /* DEBUG || CONFIG_DYNAMIC_DEBUG */ +#endif /* CONFIG_DYNAMIC_DEBUG */ /* functions have the "wrong" filename when they're output... */ #define dbg_status(ehci, label, status) { \ @@ -818,7 +818,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf) #ifdef CONFIG_PCI /* EHCI 0.96 and later may have "extended capabilities" */ - if (hcd->self.controller->bus == &pci_bus_type) { + if (dev_is_pci(hcd->self.controller)) { struct pci_dev *pdev; u32 offset, cap, cap2; unsigned count = 256/4; diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c index e97c198..d1d8c47 100644 --- a/drivers/usb/host/ehci-exynos.c +++ b/drivers/usb/host/ehci-exynos.c @@ -166,6 +166,7 @@ skip_phy: dev_err(&pdev->dev, "Failed to add USB HCD\n"); goto fail_add_hcd; } + device_wakeup_enable(hcd->self.controller); platform_set_drvdata(pdev, hcd); diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index a06d501..6f2c8d3 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -102,19 +102,11 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, } hcd->rsrc_start = res->start; hcd->rsrc_len = resource_size(res); - if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, - driver->description)) { - dev_dbg(&pdev->dev, "controller already in use\n"); - retval = -EBUSY; + hcd->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(hcd->regs)) { + retval = PTR_ERR(hcd->regs); goto err2; } - hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); - - if (hcd->regs == NULL) { - dev_dbg(&pdev->dev, "error mapping memory\n"); - retval = -EFAULT; - goto err3; - } pdata->regs = hcd->regs; @@ -126,7 +118,7 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, */ if (pdata->init && pdata->init(pdev)) { retval = -ENODEV; - goto err4; + goto err2; } /* Enable USB controller, 83xx or 8536 */ @@ -137,7 +129,8 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, retval = usb_add_hcd(hcd, irq, IRQF_SHARED); if (retval != 0) - goto err4; + goto err2; + device_wakeup_enable(hcd->self.controller); #ifdef CONFIG_USB_OTG if (pdata->operating_mode == FSL_USB2_DR_OTG) { @@ -152,21 +145,17 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, &ehci_to_hcd(ehci)->self); if (retval) { usb_put_phy(hcd->phy); - goto err4; + goto err2; } } else { dev_err(&pdev->dev, "can't find phy\n"); retval = -ENODEV; - goto err4; + goto err2; } } #endif return retval; - err4: - iounmap(hcd->regs); - err3: - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); err2: usb_put_hcd(hcd); err1: @@ -205,8 +194,6 @@ static void usb_hcd_fsl_remove(struct usb_hcd *hcd, */ if (pdata->exit) pdata->exit(pdev); - iounmap(hcd->regs); - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); } @@ -267,7 +254,7 @@ static int ehci_fsl_setup_phy(struct usb_hcd *hcd, if (!(spin_event_timeout(in_be32(non_ehci + FSL_SOC_USB_CTRL) & PHY_CLK_VALID, FSL_USB_PHY_CLK_TIMEOUT, 0) || in_be32(non_ehci + FSL_SOC_USB_PRICTRL))) { - printk(KERN_WARNING "fsl-ehci: USB PHY clock invalid\n"); + dev_warn(hcd->self.controller, "USB PHY clock invalid\n"); return -EINVAL; } } @@ -413,7 +400,7 @@ static int ehci_fsl_mpc512x_drv_suspend(struct device *dev) struct fsl_usb2_platform_data *pdata = dev_get_platdata(dev); u32 tmp; -#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG) +#ifdef CONFIG_DYNAMIC_DEBUG u32 mode = ehci_readl(ehci, hcd->regs + FSL_SOC_USB_USBMODE); mode &= USBMODE_CM_MASK; tmp = ehci_readl(ehci, hcd->regs + 0x140); /* usbcmd */ diff --git a/drivers/usb/host/ehci-grlib.c b/drivers/usb/host/ehci-grlib.c index b52a66c..495b6fb 100644 --- a/drivers/usb/host/ehci-grlib.c +++ b/drivers/usb/host/ehci-grlib.c @@ -113,7 +113,8 @@ static int ehci_hcd_grlib_probe(struct platform_device *op) irq = irq_of_parse_and_map(dn, 0); if (irq == NO_IRQ) { - printk(KERN_ERR "%s: irq_of_parse_and_map failed\n", __FILE__); + dev_err(&op->dev, "%s: irq_of_parse_and_map failed\n", + __FILE__); rv = -EBUSY; goto err_irq; } @@ -140,6 +141,7 @@ static int ehci_hcd_grlib_probe(struct platform_device *op) if (rv) goto err_ioremap; + device_wakeup_enable(hcd->self.controller); return 0; err_ioremap: diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index e8ba4c4..4711427 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -71,7 +71,6 @@ static const char hcd_name [] = "ehci_hcd"; -#undef VERBOSE_DEBUG #undef EHCI_URB_TRACE /* magic numbers that can affect system performance */ @@ -714,13 +713,6 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) cmd = ehci_readl(ehci, &ehci->regs->command); bh = 0; -#ifdef VERBOSE_DEBUG - /* unrequested/ignored: Frame List Rollover */ - dbg_status (ehci, "irq", status); -#endif - - /* INT, ERR, and IAA interrupt rates can be throttled */ - /* normal [4.15.1.2] or error [4.15.1.1] completion */ if (likely ((status & (STS_INT|STS_ERR)) != 0)) { if (likely ((status & STS_ERR) == 0)) @@ -1320,7 +1312,7 @@ static int __init ehci_hcd_init(void) sizeof(struct ehci_qh), sizeof(struct ehci_qtd), sizeof(struct ehci_itd), sizeof(struct ehci_sitd)); -#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG) +#ifdef CONFIG_DYNAMIC_DEBUG ehci_debug_root = debugfs_create_dir("ehci", usb_debug_root); if (!ehci_debug_root) { retval = -ENOENT; @@ -1369,7 +1361,7 @@ clean2: platform_driver_unregister(&PLATFORM_DRIVER); clean0: #endif -#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG) +#ifdef CONFIG_DYNAMIC_DEBUG debugfs_remove(ehci_debug_root); ehci_debug_root = NULL; err_debug: @@ -1393,7 +1385,7 @@ static void __exit ehci_hcd_cleanup(void) #ifdef PS3_SYSTEM_BUS_DRIVER ps3_ehci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); #endif -#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG) +#ifdef CONFIG_DYNAMIC_DEBUG debugfs_remove(ehci_debug_root); #endif clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 835fc08..47b858f 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -1114,10 +1114,8 @@ static int ehci_hub_control ( if (test_bit(wIndex, &ehci->port_c_suspend)) status |= USB_PORT_STAT_C_SUSPEND << 16; -#ifndef VERBOSE_DEBUG - if (status & ~0xffff) /* only if wPortChange is interesting */ -#endif - dbg_port (ehci, "GetStatus", wIndex + 1, temp); + if (status & ~0xffff) /* only if wPortChange is interesting */ + dbg_port(ehci, "GetStatus", wIndex + 1, temp); put_unaligned_le32(status, buf); break; case SetHubFeature: diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c index 417c10d..bd61612 100644 --- a/drivers/usb/host/ehci-mv.c +++ b/drivers/usb/host/ehci-mv.c @@ -178,7 +178,7 @@ static int mv_ehci_probe(struct platform_device *pdev) ehci_mv->phy_regs = devm_ioremap(&pdev->dev, r->start, resource_size(r)); - if (ehci_mv->phy_regs == 0) { + if (!ehci_mv->phy_regs) { dev_err(&pdev->dev, "failed to map phy I/O memory\n"); retval = -EFAULT; goto err_put_hcd; @@ -257,6 +257,7 @@ static int mv_ehci_probe(struct platform_device *pdev) "failed to add hcd with err %d\n", retval); goto err_set_vbus; } + device_wakeup_enable(hcd->self.controller); } if (pdata->private_init) diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c index 0528dc4..dbe5e4e 100644 --- a/drivers/usb/host/ehci-mxc.c +++ b/drivers/usb/host/ehci-mxc.c @@ -155,6 +155,7 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev) if (ret) goto err_add; + device_wakeup_enable(hcd->self.controller); return 0; err_add: diff --git a/drivers/usb/host/ehci-octeon.c b/drivers/usb/host/ehci-octeon.c index 4c528b2..9051439 100644 --- a/drivers/usb/host/ehci-octeon.c +++ b/drivers/usb/host/ehci-octeon.c @@ -128,20 +128,12 @@ static int ehci_octeon_drv_probe(struct platform_device *pdev) hcd->rsrc_start = res_mem->start; hcd->rsrc_len = resource_size(res_mem); - if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, - OCTEON_EHCI_HCD_NAME)) { - dev_err(&pdev->dev, "request_mem_region failed\n"); - ret = -EBUSY; + hcd->regs = devm_ioremap_resource(&pdev->dev, res_mem); + if (IS_ERR(hcd->regs)) { + ret = PTR_ERR(hcd->regs); goto err1; } - hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); - if (!hcd->regs) { - dev_err(&pdev->dev, "ioremap failed\n"); - ret = -ENOMEM; - goto err2; - } - ehci_octeon_start(); ehci = hcd_to_ehci(hcd); @@ -156,18 +148,16 @@ static int ehci_octeon_drv_probe(struct platform_device *pdev) ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) { dev_dbg(&pdev->dev, "failed to add hcd with err %d\n", ret); - goto err3; + goto err2; } + device_wakeup_enable(hcd->self.controller); platform_set_drvdata(pdev, hcd); return 0; -err3: +err2: ehci_octeon_stop(); - iounmap(hcd->regs); -err2: - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); err1: usb_put_hcd(hcd); return ret; @@ -180,8 +170,6 @@ static int ehci_octeon_drv_remove(struct platform_device *pdev) usb_remove_hcd(hcd); ehci_octeon_stop(); - iounmap(hcd->regs); - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); return 0; diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 6fa82d6..a24720b 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -215,6 +215,7 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) dev_err(dev, "failed to add hcd with err %d\n", ret); goto err_pm_runtime; } + device_wakeup_enable(hcd->self.controller); /* * Bring PHYs out of reset for non PHY modes. diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index 2ba7673..30d35e5 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -184,33 +184,23 @@ static int ehci_orion_drv_probe(struct platform_device *pdev) if (err) goto err1; - if (!request_mem_region(res->start, resource_size(res), - ehci_orion_hc_driver.description)) { - dev_dbg(&pdev->dev, "controller already in use\n"); - err = -EBUSY; + regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(regs)) { + err = PTR_ERR(regs); goto err1; } - regs = ioremap(res->start, resource_size(res)); - if (regs == NULL) { - dev_dbg(&pdev->dev, "error mapping memory\n"); - err = -EFAULT; - goto err2; - } - /* Not all platforms can gate the clock, so it is not an error if the clock does not exists. */ - clk = clk_get(&pdev->dev, NULL); - if (!IS_ERR(clk)) { + clk = devm_clk_get(&pdev->dev, NULL); + if (!IS_ERR(clk)) clk_prepare_enable(clk); - clk_put(clk); - } hcd = usb_create_hcd(&ehci_orion_hc_driver, &pdev->dev, dev_name(&pdev->dev)); if (!hcd) { err = -ENOMEM; - goto err3; + goto err2; } hcd->rsrc_start = res->start; @@ -245,25 +235,21 @@ static int ehci_orion_drv_probe(struct platform_device *pdev) case EHCI_PHY_DD: case EHCI_PHY_KW: default: - printk(KERN_WARNING "Orion ehci -USB phy version isn't supported.\n"); + dev_warn(&pdev->dev, "USB phy version isn't supported.\n"); } err = usb_add_hcd(hcd, irq, IRQF_SHARED); if (err) - goto err4; + goto err3; + device_wakeup_enable(hcd->self.controller); return 0; -err4: - usb_put_hcd(hcd); err3: - if (!IS_ERR(clk)) { - clk_disable_unprepare(clk); - clk_put(clk); - } - iounmap(regs); + usb_put_hcd(hcd); err2: - release_mem_region(res->start, resource_size(res)); + if (!IS_ERR(clk)) + clk_disable_unprepare(clk); err1: dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), err); @@ -277,15 +263,11 @@ static int ehci_orion_drv_remove(struct platform_device *pdev) struct clk *clk; usb_remove_hcd(hcd); - iounmap(hcd->regs); - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); - clk = clk_get(&pdev->dev, NULL); - if (!IS_ERR(clk)) { + clk = devm_clk_get(&pdev->dev, NULL); + if (!IS_ERR(clk)) clk_disable_unprepare(clk); - clk_put(clk); - } return 0; } diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index 7f30b71..01536cf 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -132,6 +132,7 @@ static int ehci_platform_probe(struct platform_device *dev) if (err) goto err_put_hcd; + device_wakeup_enable(hcd->self.controller); platform_set_drvdata(dev, hcd); return err; diff --git a/drivers/usb/host/ehci-pmcmsp.c b/drivers/usb/host/ehci-pmcmsp.c index 893b707..af3974a 100644 --- a/drivers/usb/host/ehci-pmcmsp.c +++ b/drivers/usb/host/ehci-pmcmsp.c @@ -210,8 +210,10 @@ int usb_hcd_msp_probe(const struct hc_driver *driver, retval = usb_add_hcd(hcd, res->start, IRQF_SHARED); - if (retval == 0) + if (retval == 0) { + device_wakeup_enable(hcd->self.controller); return 0; + } usb_remove_hcd(hcd); err3: diff --git a/drivers/usb/host/ehci-ppc-of.c b/drivers/usb/host/ehci-ppc-of.c index 875d2fc..5479247 100644 --- a/drivers/usb/host/ehci-ppc-of.c +++ b/drivers/usb/host/ehci-ppc-of.c @@ -119,7 +119,8 @@ static int ehci_hcd_ppc_of_probe(struct platform_device *op) irq = irq_of_parse_and_map(dn, 0); if (irq == NO_IRQ) { - printk(KERN_ERR "%s: irq_of_parse_and_map failed\n", __FILE__); + dev_err(&op->dev, "%s: irq_of_parse_and_map failed\n", + __FILE__); rv = -EBUSY; goto err_irq; } @@ -169,6 +170,7 @@ static int ehci_hcd_ppc_of_probe(struct platform_device *op) if (rv) goto err_ioremap; + device_wakeup_enable(hcd->self.controller); return 0; err_ioremap: diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c index 8188542..7934ff9 100644 --- a/drivers/usb/host/ehci-ps3.c +++ b/drivers/usb/host/ehci-ps3.c @@ -189,6 +189,7 @@ static int ps3_ehci_probe(struct ps3_system_bus_device *dev) goto fail_add_hcd; } + device_wakeup_enable(hcd->self.controller); return result; fail_add_hcd: diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index db05bd8..54f5332 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -168,13 +168,13 @@ static void ehci_clear_tt_buffer(struct ehci_hcd *ehci, struct ehci_qh *qh, * Note: this routine is never called for Isochronous transfers. */ if (urb->dev->tt && !usb_pipeint(urb->pipe) && !qh->clearing_tt) { -#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG) +#ifdef CONFIG_DYNAMIC_DEBUG struct usb_device *tt = urb->dev->tt->hub; dev_dbg(&tt->dev, "clear tt buffer port %d, a%d ep%d t%08x\n", urb->dev->ttport, urb->dev->devnum, usb_pipeendpoint(urb->pipe), token); -#endif /* DEBUG || CONFIG_DYNAMIC_DEBUG */ +#endif /* CONFIG_DYNAMIC_DEBUG */ if (!ehci_is_TDI(ehci) || urb->dev->tt->hub != ehci_to_hcd(ehci)->self.root_hub) { diff --git a/drivers/usb/host/ehci-sead3.c b/drivers/usb/host/ehci-sead3.c index 8a73449..cf12676 100644 --- a/drivers/usb/host/ehci-sead3.c +++ b/drivers/usb/host/ehci-sead3.c @@ -126,6 +126,7 @@ static int ehci_hcd_sead3_drv_probe(struct platform_device *pdev) IRQF_SHARED); if (ret == 0) { platform_set_drvdata(pdev, hcd); + device_wakeup_enable(hcd->self.controller); return ret; } diff --git a/drivers/usb/host/ehci-sh.c b/drivers/usb/host/ehci-sh.c index dc899eb..9b9b9f5 100644 --- a/drivers/usb/host/ehci-sh.c +++ b/drivers/usb/host/ehci-sh.c @@ -151,6 +151,7 @@ static int ehci_hcd_sh_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Failed to add hcd"); goto fail_add_hcd; } + device_wakeup_enable(hcd->self.controller); priv->hcd = hcd; platform_set_drvdata(pdev, priv); diff --git a/drivers/usb/host/ehci-spear.c b/drivers/usb/host/ehci-spear.c index ee6f9ff..8bd915b 100644 --- a/drivers/usb/host/ehci-spear.c +++ b/drivers/usb/host/ehci-spear.c @@ -130,6 +130,7 @@ static int spear_ehci_hcd_drv_probe(struct platform_device *pdev) if (retval) goto err_stop_ehci; + device_wakeup_enable(hcd->self.controller); return retval; err_stop_ehci: diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index b9fd039..af28b74 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -17,7 +17,6 @@ */ #include <linux/clk.h> -#include <linux/clk/tegra.h> #include <linux/dma-mapping.h> #include <linux/err.h> #include <linux/gpio.h> @@ -29,6 +28,7 @@ #include <linux/of_gpio.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/reset.h> #include <linux/slab.h> #include <linux/usb/ehci_def.h> #include <linux/usb/tegra_usb_phy.h> @@ -62,6 +62,7 @@ static int (*orig_hub_control)(struct usb_hcd *hcd, struct tegra_ehci_hcd { struct tegra_usb_phy *phy; struct clk *clk; + struct reset_control *rst; int port_resuming; bool needs_double_reset; enum tegra_usb_phy_port_speed port_speed; @@ -385,13 +386,20 @@ static int tegra_ehci_probe(struct platform_device *pdev) goto cleanup_hcd_create; } + tegra->rst = devm_reset_control_get(&pdev->dev, "usb"); + if (IS_ERR(tegra->rst)) { + dev_err(&pdev->dev, "Can't get ehci reset\n"); + err = PTR_ERR(tegra->rst); + goto cleanup_hcd_create; + } + err = clk_prepare_enable(tegra->clk); if (err) goto cleanup_hcd_create; - tegra_periph_reset_assert(tegra->clk); + reset_control_assert(tegra->rst); udelay(1); - tegra_periph_reset_deassert(tegra->clk); + reset_control_deassert(tegra->rst); u_phy = devm_usb_get_phy_by_phandle(&pdev->dev, "nvidia,phy", 0); if (IS_ERR(u_phy)) { @@ -455,6 +463,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Failed to add USB HCD\n"); goto cleanup_otg_set_host; } + device_wakeup_enable(hcd->self.controller); return err; diff --git a/drivers/usb/host/ehci-tilegx.c b/drivers/usb/host/ehci-tilegx.c index 67026ff..f3713d3 100644 --- a/drivers/usb/host/ehci-tilegx.c +++ b/drivers/usb/host/ehci-tilegx.c @@ -170,6 +170,7 @@ static int ehci_hcd_tilegx_drv_probe(struct platform_device *pdev) ret = usb_add_hcd(hcd, pdata->irq, IRQF_SHARED); if (ret == 0) { platform_set_drvdata(pdev, hcd); + device_wakeup_enable(hcd->self.controller); return ret; } diff --git a/drivers/usb/host/ehci-w90x900.c b/drivers/usb/host/ehci-w90x900.c index cdad843..a9303af 100644 --- a/drivers/usb/host/ehci-w90x900.c +++ b/drivers/usb/host/ehci-w90x900.c @@ -58,17 +58,12 @@ static int usb_w90x900_probe(const struct hc_driver *driver, hcd->rsrc_start = res->start; hcd->rsrc_len = resource_size(res); - if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { - retval = -EBUSY; + hcd->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(hcd->regs)) { + retval = PTR_ERR(hcd->regs); goto err2; } - hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); - if (hcd->regs == NULL) { - retval = -EFAULT; - goto err3; - } - ehci = hcd_to_ehci(hcd); ehci->caps = hcd->regs; ehci->regs = hcd->regs + @@ -88,17 +83,14 @@ static int usb_w90x900_probe(const struct hc_driver *driver, irq = platform_get_irq(pdev, 0); if (irq < 0) - goto err4; + goto err2; retval = usb_add_hcd(hcd, irq, IRQF_SHARED); if (retval != 0) - goto err4; + goto err2; + device_wakeup_enable(hcd->self.controller); return retval; -err4: - iounmap(hcd->regs); -err3: - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); err2: usb_put_hcd(hcd); err1: @@ -109,8 +101,6 @@ static void usb_w90x900_remove(struct usb_hcd *hcd, struct platform_device *pdev) { usb_remove_hcd(hcd); - iounmap(hcd->regs); - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); } diff --git a/drivers/usb/host/ehci-xilinx-of.c b/drivers/usb/host/ehci-xilinx-of.c index 95979f9..fe57710 100644 --- a/drivers/usb/host/ehci-xilinx-of.c +++ b/drivers/usb/host/ehci-xilinx-of.c @@ -155,7 +155,8 @@ static int ehci_hcd_xilinx_of_probe(struct platform_device *op) irq = irq_of_parse_and_map(dn, 0); if (!irq) { - printk(KERN_ERR "%s: irq_of_parse_and_map failed\n", __FILE__); + dev_err(&op->dev, "%s: irq_of_parse_and_map failed\n", + __FILE__); rv = -EBUSY; goto err_irq; } @@ -191,8 +192,10 @@ static int ehci_hcd_xilinx_of_probe(struct platform_device *op) ehci->caps = hcd->regs + 0x100; rv = usb_add_hcd(hcd, irq, 0); - if (rv == 0) + if (rv == 0) { + device_wakeup_enable(hcd->self.controller); return 0; + } err_irq: usb_put_hcd(hcd); diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index e8f41c5..9dfc6c1 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -38,7 +38,7 @@ typedef __u16 __bitwise __hc16; #endif /* statistics can be kept for tuning/monitoring */ -#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG) +#ifdef CONFIG_DYNAMIC_DEBUG #define EHCI_STATS #endif @@ -225,6 +225,7 @@ struct ehci_hcd { /* one per controller */ unsigned has_synopsys_hc_bug:1; /* Synopsys HC */ unsigned frame_index_bug:1; /* MosChip (AKA NetMos) */ unsigned need_oc_pp_cycle:1; /* MPC834X port power */ + unsigned imx28_write_fix:1; /* For Freescale i.MX28 */ /* required for usb32 quirk */ #define OHCI_CTRL_HCFS (3 << 6) @@ -248,7 +249,7 @@ struct ehci_hcd { /* one per controller */ #endif /* debug files */ -#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG) +#ifdef CONFIG_DYNAMIC_DEBUG struct dentry *debug_dir; #endif @@ -728,6 +729,18 @@ static inline unsigned int ehci_readl(const struct ehci_hcd *ehci, #endif } +#ifdef CONFIG_SOC_IMX28 +static inline void imx28_ehci_writel(const unsigned int val, + volatile __u32 __iomem *addr) +{ + __asm__ ("swp %0, %0, [%1]" : : "r"(val), "r"(addr)); +} +#else +static inline void imx28_ehci_writel(const unsigned int val, + volatile __u32 __iomem *addr) +{ +} +#endif static inline void ehci_writel(const struct ehci_hcd *ehci, const unsigned int val, __u32 __iomem *regs) { @@ -736,7 +749,10 @@ static inline void ehci_writel(const struct ehci_hcd *ehci, writel_be(val, regs) : writel(val, regs); #else - writel(val, regs); + if (ehci->imx28_write_fix) + imx28_ehci_writel(val, regs); + else + writel(val, regs); #endif } @@ -832,9 +848,9 @@ static inline u32 hc32_to_cpup (const struct ehci_hcd *ehci, const __hc32 *x) dev_warn(ehci_to_hcd(ehci)->self.controller , fmt , ## args) -#if !defined(DEBUG) && !defined(CONFIG_DYNAMIC_DEBUG) +#ifndef CONFIG_DYNAMIC_DEBUG #define STUB_DEBUG_FILES -#endif /* !DEBUG && !CONFIG_DYNAMIC_DEBUG */ +#endif /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c index 0551c0a..1cf68ea 100644 --- a/drivers/usb/host/fhci-hcd.c +++ b/drivers/usb/host/fhci-hcd.c @@ -754,6 +754,8 @@ static int of_fhci_probe(struct platform_device *ofdev) if (ret < 0) goto err_add_hcd; + device_wakeup_enable(hcd->self.controller); + fhci_dfs_create(fhci); return 0; diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c index 55486bd..98a89d1 100644 --- a/drivers/usb/host/fotg210-hcd.c +++ b/drivers/usb/host/fotg210-hcd.c @@ -56,12 +56,9 @@ static const char hcd_name[] = "fotg210_hcd"; -#undef VERBOSE_DEBUG #undef FOTG210_URB_TRACE -#ifdef DEBUG #define FOTG210_STATS -#endif /* magic numbers that can affect system performance */ #define FOTG210_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ @@ -107,14 +104,6 @@ MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us"); #define fotg210_warn(fotg210, fmt, args...) \ dev_warn(fotg210_to_hcd(fotg210)->self.controller , fmt , ## args) -#ifdef VERBOSE_DEBUG -# define fotg210_vdbg fotg210_dbg -#else - static inline void fotg210_vdbg(struct fotg210_hcd *fotg210, ...) {} -#endif - -#ifdef DEBUG - /* check the values in the HCSPARAMS register * (host controller _Structural_ parameters) * see EHCI spec, Table 2-4 for each value @@ -129,13 +118,6 @@ static void dbg_hcs_params(struct fotg210_hcd *fotg210, char *label) HCS_N_PORTS(params) ); } -#else - -static inline void dbg_hcs_params(struct fotg210_hcd *fotg210, char *label) {} - -#endif - -#ifdef DEBUG /* check the values in the HCCPARAMS register * (host controller _Capability_ parameters) @@ -152,13 +134,6 @@ static void dbg_hcc_params(struct fotg210_hcd *fotg210, char *label) HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024", HCC_CANPARK(params) ? " park" : ""); } -#else - -static inline void dbg_hcc_params(struct fotg210_hcd *fotg210, char *label) {} - -#endif - -#ifdef DEBUG static void __maybe_unused dbg_qtd(const char *label, struct fotg210_hcd *fotg210, struct fotg210_qtd *qtd) @@ -272,8 +247,8 @@ dbg_command_buf(char *buf, unsigned len, const char *label, u32 command) ); } -static int -dbg_port_buf(char *buf, unsigned len, const char *label, int port, u32 status) +static char +*dbg_port_buf(char *buf, unsigned len, const char *label, int port, u32 status) { char *sig; @@ -293,7 +268,7 @@ dbg_port_buf(char *buf, unsigned len, const char *label, int port, u32 status) break; } - return scnprintf(buf, len, + scnprintf(buf, len, "%s%sport:%d status %06x %d " "sig=%s%s%s%s%s%s%s%s", label, label[0] ? " " : "", port, status, @@ -306,31 +281,9 @@ dbg_port_buf(char *buf, unsigned len, const char *label, int port, u32 status) (status & PORT_PE) ? " PE" : "", (status & PORT_CSC) ? " CSC" : "", (status & PORT_CONNECT) ? " CONNECT" : ""); + return buf; } -#else -static inline void __maybe_unused -dbg_qh(char *label, struct fotg210_hcd *fotg210, struct fotg210_qh *qh) -{} - -static inline int __maybe_unused -dbg_status_buf(char *buf, unsigned len, const char *label, u32 status) -{ return 0; } - -static inline int __maybe_unused -dbg_command_buf(char *buf, unsigned len, const char *label, u32 command) -{ return 0; } - -static inline int __maybe_unused -dbg_intr_buf(char *buf, unsigned len, const char *label, u32 enable) -{ return 0; } - -static inline int __maybe_unused -dbg_port_buf(char *buf, unsigned len, const char *label, int port, u32 status) -{ return 0; } - -#endif /* DEBUG */ - /* functions have the "wrong" filename when they're output... */ #define dbg_status(fotg210, label, status) { \ char _buf[80]; \ @@ -346,19 +299,11 @@ dbg_port_buf(char *buf, unsigned len, const char *label, int port, u32 status) #define dbg_port(fotg210, label, port, status) { \ char _buf[80]; \ - dbg_port_buf(_buf, sizeof(_buf), label, port, status); \ - fotg210_dbg(fotg210, "%s\n", _buf); \ + fotg210_dbg(fotg210, "%s\n", dbg_port_buf(_buf, sizeof(_buf), label, port, status) ); \ } /*-------------------------------------------------------------------------*/ -#ifdef STUB_DEBUG_FILES - -static inline void create_debug_files(struct fotg210_hcd *bus) { } -static inline void remove_debug_files(struct fotg210_hcd *bus) { } - -#else - /* troubleshooting help: expose state in debugfs */ static int debug_async_open(struct inode *, struct file *); @@ -954,7 +899,6 @@ static inline void remove_debug_files(struct fotg210_hcd *fotg210) debugfs_remove_recursive(fotg210->debug_dir); } -#endif /* STUB_DEBUG_FILES */ /*-------------------------------------------------------------------------*/ /* @@ -1398,7 +1342,7 @@ static void fotg210_iaa_watchdog(struct fotg210_hcd *fotg210) &fotg210->regs->status); } - fotg210_vdbg(fotg210, "IAA watchdog: status %x cmd %x\n", + fotg210_dbg(fotg210, "IAA watchdog: status %x cmd %x\n", status, cmd); end_unlink_async(fotg210); } @@ -1810,10 +1754,8 @@ static int fotg210_hub_control( if (test_bit(wIndex, &fotg210->port_c_suspend)) status |= USB_PORT_STAT_C_SUSPEND << 16; -#ifndef VERBOSE_DEBUG - if (status & ~0xffff) /* only if wPortChange is interesting */ -#endif - dbg_port(fotg210, "GetStatus", wIndex + 1, temp); + if (status & ~0xffff) /* only if wPortChange is interesting */ + dbg_port(fotg210, "GetStatus", wIndex + 1, temp); put_unaligned_le32(status, buf); break; case SetHubFeature: @@ -1856,7 +1798,7 @@ static int fotg210_hub_control( * which can be fine if this root hub has a * transaction translator built in. */ - fotg210_vdbg(fotg210, "port %d reset\n", wIndex + 1); + fotg210_dbg(fotg210, "port %d reset\n", wIndex + 1); temp |= PORT_RESET; temp &= ~PORT_PE; @@ -2274,13 +2216,12 @@ static void fotg210_clear_tt_buffer(struct fotg210_hcd *fotg210, * Note: this routine is never called for Isochronous transfers. */ if (urb->dev->tt && !usb_pipeint(urb->pipe) && !qh->clearing_tt) { -#ifdef DEBUG struct usb_device *tt = urb->dev->tt->hub; dev_dbg(&tt->dev, "clear tt buffer port %d, a%d ep%d t%08x\n", urb->dev->ttport, urb->dev->devnum, usb_pipeendpoint(urb->pipe), token); -#endif /* DEBUG */ + if (urb->dev->tt->hub != fotg210_to_hcd(fotg210)->self.root_hub) { if (usb_hub_clear_tt_buffer(urb) == 0) @@ -2341,7 +2282,7 @@ static int qtd_copy_status( status = -EPROTO; } - fotg210_vdbg(fotg210, + fotg210_dbg(fotg210, "dev%d ep%d%s qtd token %08x --> status %d\n", usb_pipedevice(urb->pipe), usb_pipeendpoint(urb->pipe), @@ -3583,11 +3524,9 @@ periodic_usecs(struct fotg210_hcd *fotg210, unsigned frame, unsigned uframe) break; } } -#ifdef DEBUG if (usecs > fotg210->uframe_periodic_max) fotg210_err(fotg210, "uframe %d sched overrun: %d usecs\n", frame * 8 + uframe, usecs); -#endif return usecs; } @@ -4646,7 +4585,7 @@ static void itd_link_urb( if (unlikely(list_empty(&stream->td_list))) { fotg210_to_hcd(fotg210)->self.bandwidth_allocated += stream->bandwidth; - fotg210_vdbg(fotg210, + fotg210_dbg(fotg210, "schedule devp %s ep%d%s-iso period %d start %d.%d\n", urb->dev->devpath, stream->bEndpointAddress & 0x0f, (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out", @@ -4779,7 +4718,7 @@ static bool itd_complete(struct fotg210_hcd *fotg210, struct fotg210_itd *itd) if (unlikely(list_is_singular(&stream->td_list))) { fotg210_to_hcd(fotg210)->self.bandwidth_allocated -= stream->bandwidth; - fotg210_vdbg(fotg210, + fotg210_dbg(fotg210, "deschedule devp %s ep%d%s-iso\n", dev->devpath, stream->bEndpointAddress & 0x0f, (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); @@ -5444,10 +5383,8 @@ static irqreturn_t fotg210_irq(struct usb_hcd *hcd) cmd = fotg210_readl(fotg210, &fotg210->regs->command); bh = 0; -#ifdef VERBOSE_DEBUG /* unrequested/ignored: Frame List Rollover */ dbg_status(fotg210, "irq", status); -#endif /* INT, ERR, and IAA interrupt rates can be throttled */ @@ -5952,6 +5889,7 @@ static int fotg210_hcd_probe(struct platform_device *pdev) dev_err(dev, "failed to add hcd with err %d\n", retval); goto fail_add_hcd; } + device_wakeup_enable(hcd->self.controller); return retval; @@ -6013,13 +5951,11 @@ static int __init fotg210_hcd_init(void) sizeof(struct fotg210_qh), sizeof(struct fotg210_qtd), sizeof(struct fotg210_itd)); -#ifdef DEBUG fotg210_debug_root = debugfs_create_dir("fotg210", usb_debug_root); if (!fotg210_debug_root) { retval = -ENOENT; goto err_debug; } -#endif retval = platform_driver_register(&fotg210_hcd_driver); if (retval < 0) @@ -6028,11 +5964,9 @@ static int __init fotg210_hcd_init(void) platform_driver_unregister(&fotg210_hcd_driver); clean: -#ifdef DEBUG debugfs_remove(fotg210_debug_root); fotg210_debug_root = NULL; err_debug: -#endif clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); return retval; } @@ -6041,9 +5975,7 @@ module_init(fotg210_hcd_init); static void __exit fotg210_hcd_cleanup(void) { platform_driver_unregister(&fotg210_hcd_driver); -#ifdef DEBUG debugfs_remove(fotg210_debug_root); -#endif clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); } module_exit(fotg210_hcd_cleanup); diff --git a/drivers/usb/host/fotg210.h b/drivers/usb/host/fotg210.h index 8920f9d..ac6cd1b 100644 --- a/drivers/usb/host/fotg210.h +++ b/drivers/usb/host/fotg210.h @@ -174,9 +174,7 @@ struct fotg210_hcd { /* one per controller */ #endif /* debug files */ -#ifdef DEBUG struct dentry *debug_dir; -#endif }; /* convert between an HCD pointer and the corresponding FOTG210_HCD */ @@ -741,10 +739,4 @@ static inline unsigned fotg210_read_frame_index(struct fotg210_hcd *fotg210) }) /*-------------------------------------------------------------------------*/ -#ifndef DEBUG -#define STUB_DEBUG_FILES -#endif /* DEBUG */ - -/*-------------------------------------------------------------------------*/ - #endif /* __LINUX_FOTG210_H */ diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c index abd5050..9162d1b 100644 --- a/drivers/usb/host/fsl-mph-dr-of.c +++ b/drivers/usb/host/fsl-mph-dr-of.c @@ -261,19 +261,8 @@ int fsl_usb2_mpc5121_init(struct platform_device *pdev) struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev); struct clk *clk; int err; - char clk_name[10]; - int base, clk_num; - - base = pdev->resource->start & 0xf000; - if (base == 0x3000) - clk_num = 1; - else if (base == 0x4000) - clk_num = 2; - else - return -ENODEV; - snprintf(clk_name, sizeof(clk_name), "usb%d_clk", clk_num); - clk = devm_clk_get(pdev->dev.parent, clk_name); + clk = devm_clk_get(pdev->dev.parent, "ipg"); if (IS_ERR(clk)) { dev_err(&pdev->dev, "failed to get clk\n"); return PTR_ERR(clk); diff --git a/drivers/usb/host/fusbh200-hcd.c b/drivers/usb/host/fusbh200-hcd.c index e1c6d85..ba94990 100644 --- a/drivers/usb/host/fusbh200-hcd.c +++ b/drivers/usb/host/fusbh200-hcd.c @@ -57,13 +57,8 @@ static const char hcd_name [] = "fusbh200_hcd"; -#undef VERBOSE_DEBUG #undef FUSBH200_URB_TRACE -#ifdef DEBUG -#define FUSBH200_STATS -#endif - /* magic numbers that can affect system performance */ #define FUSBH200_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ #define FUSBH200_TUNE_RL_HS 4 /* nak throttle; see 4.9 */ @@ -108,14 +103,6 @@ MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us"); #define fusbh200_warn(fusbh200, fmt, args...) \ dev_warn (fusbh200_to_hcd(fusbh200)->self.controller , fmt , ## args ) -#ifdef VERBOSE_DEBUG -# define fusbh200_vdbg fusbh200_dbg -#else - static inline void fusbh200_vdbg(struct fusbh200_hcd *fusbh200, ...) {} -#endif - -#ifdef DEBUG - /* check the values in the HCSPARAMS register * (host controller _Structural_ parameters) * see EHCI spec, Table 2-4 for each value @@ -130,13 +117,6 @@ static void dbg_hcs_params (struct fusbh200_hcd *fusbh200, char *label) HCS_N_PORTS (params) ); } -#else - -static inline void dbg_hcs_params (struct fusbh200_hcd *fusbh200, char *label) {} - -#endif - -#ifdef DEBUG /* check the values in the HCCPARAMS register * (host controller _Capability_ parameters) @@ -153,13 +133,6 @@ static void dbg_hcc_params (struct fusbh200_hcd *fusbh200, char *label) HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024", HCC_CANPARK(params) ? " park" : ""); } -#else - -static inline void dbg_hcc_params (struct fusbh200_hcd *fusbh200, char *label) {} - -#endif - -#ifdef DEBUG static void __maybe_unused dbg_qtd (const char *label, struct fusbh200_hcd *fusbh200, struct fusbh200_qtd *qtd) @@ -302,29 +275,6 @@ dbg_port_buf (char *buf, unsigned len, const char *label, int port, u32 status) (status & PORT_CONNECT) ? " CONNECT" : ""); } -#else -static inline void __maybe_unused -dbg_qh (char *label, struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh) -{} - -static inline int __maybe_unused -dbg_status_buf (char *buf, unsigned len, const char *label, u32 status) -{ return 0; } - -static inline int __maybe_unused -dbg_command_buf (char *buf, unsigned len, const char *label, u32 command) -{ return 0; } - -static inline int __maybe_unused -dbg_intr_buf (char *buf, unsigned len, const char *label, u32 enable) -{ return 0; } - -static inline int __maybe_unused -dbg_port_buf (char *buf, unsigned len, const char *label, int port, u32 status) -{ return 0; } - -#endif /* DEBUG */ - /* functions have the "wrong" filename when they're output... */ #define dbg_status(fusbh200, label, status) { \ char _buf [80]; \ @@ -346,13 +296,6 @@ dbg_port_buf (char *buf, unsigned len, const char *label, int port, u32 status) /*-------------------------------------------------------------------------*/ -#ifdef STUB_DEBUG_FILES - -static inline void create_debug_files (struct fusbh200_hcd *bus) { } -static inline void remove_debug_files (struct fusbh200_hcd *bus) { } - -#else - /* troubleshooting help: expose state in debugfs */ static int debug_async_open(struct inode *, struct file *); @@ -775,7 +718,6 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf) next += temp; } -#ifdef FUSBH200_STATS temp = scnprintf (next, size, "irq normal %ld err %ld iaa %ld (lost %ld)\n", fusbh200->stats.normal, fusbh200->stats.error, fusbh200->stats.iaa, @@ -787,7 +729,6 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf) fusbh200->stats.complete, fusbh200->stats.unlink); size -= temp; next += temp; -#endif done: spin_unlock_irqrestore (&fusbh200->lock, flags); @@ -928,7 +869,6 @@ static inline void remove_debug_files (struct fusbh200_hcd *fusbh200) debugfs_remove_recursive(fusbh200->debug_dir); } -#endif /* STUB_DEBUG_FILES */ /*-------------------------------------------------------------------------*/ /* @@ -1362,7 +1302,7 @@ static void fusbh200_iaa_watchdog(struct fusbh200_hcd *fusbh200) fusbh200_writel(fusbh200, STS_IAA, &fusbh200->regs->status); } - fusbh200_vdbg(fusbh200, "IAA watchdog: status %x cmd %x\n", + fusbh200_dbg(fusbh200, "IAA watchdog: status %x cmd %x\n", status, cmd); end_unlink_async(fusbh200); } @@ -1769,10 +1709,8 @@ static int fusbh200_hub_control ( if (test_bit(wIndex, &fusbh200->port_c_suspend)) status |= USB_PORT_STAT_C_SUSPEND << 16; -#ifndef VERBOSE_DEBUG - if (status & ~0xffff) /* only if wPortChange is interesting */ -#endif - dbg_port (fusbh200, "GetStatus", wIndex + 1, temp); + if (status & ~0xffff) /* only if wPortChange is interesting */ + dbg_port(fusbh200, "GetStatus", wIndex + 1, temp); put_unaligned_le32(status, buf); break; case SetHubFeature: @@ -1814,7 +1752,7 @@ static int fusbh200_hub_control ( * which can be fine if this root hub has a * transaction translator built in. */ - fusbh200_vdbg (fusbh200, "port %d reset\n", wIndex + 1); + fusbh200_dbg(fusbh200, "port %d reset\n", wIndex + 1); temp |= PORT_RESET; temp &= ~PORT_PE; @@ -2230,13 +2168,13 @@ static void fusbh200_clear_tt_buffer(struct fusbh200_hcd *fusbh200, struct fusbh * Note: this routine is never called for Isochronous transfers. */ if (urb->dev->tt && !usb_pipeint(urb->pipe) && !qh->clearing_tt) { -#ifdef DEBUG struct usb_device *tt = urb->dev->tt->hub; + dev_dbg(&tt->dev, "clear tt buffer port %d, a%d ep%d t%08x\n", urb->dev->ttport, urb->dev->devnum, usb_pipeendpoint(urb->pipe), token); -#endif /* DEBUG */ + if (urb->dev->tt->hub != fusbh200_to_hcd(fusbh200)->self.root_hub) { if (usb_hub_clear_tt_buffer(urb) == 0) @@ -2297,7 +2235,7 @@ static int qtd_copy_status ( status = -EPROTO; } - fusbh200_vdbg (fusbh200, + fusbh200_dbg(fusbh200, "dev%d ep%d%s qtd token %08x --> status %d\n", usb_pipedevice (urb->pipe), usb_pipeendpoint (urb->pipe), @@ -3529,11 +3467,9 @@ periodic_usecs (struct fusbh200_hcd *fusbh200, unsigned frame, unsigned uframe) break; } } -#ifdef DEBUG if (usecs > fusbh200->uframe_periodic_max) fusbh200_err (fusbh200, "uframe %d sched overrun: %d usecs\n", frame * 8 + uframe, usecs); -#endif return usecs; } @@ -4586,7 +4522,7 @@ static void itd_link_urb( if (unlikely (list_empty(&stream->td_list))) { fusbh200_to_hcd(fusbh200)->self.bandwidth_allocated += stream->bandwidth; - fusbh200_vdbg (fusbh200, + fusbh200_dbg(fusbh200, "schedule devp %s ep%d%s-iso period %d start %d.%d\n", urb->dev->devpath, stream->bEndpointAddress & 0x0f, (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out", @@ -4717,7 +4653,7 @@ static bool itd_complete(struct fusbh200_hcd *fusbh200, struct fusbh200_itd *itd if (unlikely(list_is_singular(&stream->td_list))) { fusbh200_to_hcd(fusbh200)->self.bandwidth_allocated -= stream->bandwidth; - fusbh200_vdbg (fusbh200, + fusbh200_dbg(fusbh200, "deschedule devp %s ep%d%s-iso\n", dev->devpath, stream->bEndpointAddress & 0x0f, (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); @@ -5115,13 +5051,11 @@ static void fusbh200_stop (struct usb_hcd *hcd) spin_unlock_irq (&fusbh200->lock); fusbh200_mem_cleanup (fusbh200); -#ifdef FUSBH200_STATS fusbh200_dbg(fusbh200, "irq normal %ld err %ld iaa %ld (lost %ld)\n", fusbh200->stats.normal, fusbh200->stats.error, fusbh200->stats.iaa, fusbh200->stats.lost_iaa); fusbh200_dbg (fusbh200, "complete %ld unlink %ld\n", fusbh200->stats.complete, fusbh200->stats.unlink); -#endif dbg_status (fusbh200, "fusbh200_stop completed", fusbh200_readl(fusbh200, &fusbh200->regs->status)); @@ -5365,13 +5299,6 @@ static irqreturn_t fusbh200_irq (struct usb_hcd *hcd) cmd = fusbh200_readl(fusbh200, &fusbh200->regs->command); bh = 0; -#ifdef VERBOSE_DEBUG - /* unrequested/ignored: Frame List Rollover */ - dbg_status (fusbh200, "irq", status); -#endif - - /* INT, ERR, and IAA interrupt rates can be throttled */ - /* normal [4.15.1.2] or error [4.15.1.1] completion */ if (likely ((status & (STS_INT|STS_ERR)) != 0)) { if (likely ((status & STS_ERR) == 0)) @@ -5871,6 +5798,7 @@ static int fusbh200_hcd_probe(struct platform_device *pdev) dev_err(dev, "failed to add hcd with err %d\n", retval); goto fail_add_hcd; } + device_wakeup_enable(hcd->self.controller); return retval; @@ -5936,13 +5864,11 @@ static int __init fusbh200_hcd_init(void) sizeof(struct fusbh200_qh), sizeof(struct fusbh200_qtd), sizeof(struct fusbh200_itd)); -#ifdef DEBUG fusbh200_debug_root = debugfs_create_dir("fusbh200", usb_debug_root); if (!fusbh200_debug_root) { retval = -ENOENT; goto err_debug; } -#endif retval = platform_driver_register(&fusbh200_hcd_fusbh200_driver); if (retval < 0) @@ -5951,11 +5877,9 @@ static int __init fusbh200_hcd_init(void) platform_driver_unregister(&fusbh200_hcd_fusbh200_driver); clean: -#ifdef DEBUG debugfs_remove(fusbh200_debug_root); fusbh200_debug_root = NULL; err_debug: -#endif clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); return retval; } @@ -5964,9 +5888,7 @@ module_init(fusbh200_hcd_init); static void __exit fusbh200_hcd_cleanup(void) { platform_driver_unregister(&fusbh200_hcd_fusbh200_driver); -#ifdef DEBUG debugfs_remove(fusbh200_debug_root); -#endif clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); } module_exit(fusbh200_hcd_cleanup); diff --git a/drivers/usb/host/fusbh200.h b/drivers/usb/host/fusbh200.h index 797c9e8..6b719e0 100644 --- a/drivers/usb/host/fusbh200.h +++ b/drivers/usb/host/fusbh200.h @@ -165,17 +165,11 @@ struct fusbh200_hcd { /* one per controller */ u8 sbrn; /* packed release number */ /* irq statistics */ -#ifdef FUSBH200_STATS struct fusbh200_stats stats; # define COUNT(x) do { (x)++; } while (0) -#else -# define COUNT(x) do {} while (0) -#endif /* debug files */ -#ifdef DEBUG struct dentry *debug_dir; -#endif }; /* convert between an HCD pointer and the corresponding FUSBH200_HCD */ @@ -734,10 +728,4 @@ static inline unsigned fusbh200_read_frame_index(struct fusbh200_hcd *fusbh200) }) /*-------------------------------------------------------------------------*/ -#ifndef DEBUG -#define STUB_DEBUG_FILES -#endif /* DEBUG */ - -/*-------------------------------------------------------------------------*/ - #endif /* __LINUX_FUSBH200_H */ diff --git a/drivers/usb/host/hwa-hc.c b/drivers/usb/host/hwa-hc.c index ada0a52..e076699 100644 --- a/drivers/usb/host/hwa-hc.c +++ b/drivers/usb/host/hwa-hc.c @@ -54,7 +54,6 @@ * DWA). */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/workqueue.h> @@ -86,7 +85,7 @@ static int __hwahc_set_cluster_id(struct hwahc *hwahc, u8 cluster_id) USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, cluster_id, wa->usb_iface->cur_altsetting->desc.bInterfaceNumber, - NULL, 0, 1000 /* FIXME: arbitrary */); + NULL, 0, USB_CTRL_SET_TIMEOUT); if (result < 0) dev_err(dev, "Cannot set WUSB Cluster ID to 0x%02x: %d\n", cluster_id, result); @@ -106,7 +105,7 @@ static int __hwahc_op_set_num_dnts(struct wusbhc *wusbhc, u8 interval, u8 slots) USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, interval << 8 | slots, wa->usb_iface->cur_altsetting->desc.bInterfaceNumber, - NULL, 0, 1000 /* FIXME: arbitrary */); + NULL, 0, USB_CTRL_SET_TIMEOUT); } /* @@ -224,7 +223,7 @@ static int hwahc_op_urb_dequeue(struct usb_hcd *usb_hcd, struct urb *urb, struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); - return wa_urb_dequeue(&hwahc->wa, urb); + return wa_urb_dequeue(&hwahc->wa, urb, status); } /* @@ -281,7 +280,7 @@ static void __hwahc_op_wusbhc_stop(struct wusbhc *wusbhc, int delay) USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, delay * 1000, iface_no, - NULL, 0, 1000 /* FIXME: arbitrary */); + NULL, 0, USB_CTRL_SET_TIMEOUT); if (ret == 0) msleep(delay); @@ -310,7 +309,7 @@ static int __hwahc_op_bwa_set(struct wusbhc *wusbhc, s8 stream_index, USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, stream_index, wa->usb_iface->cur_altsetting->desc.bInterfaceNumber, - NULL, 0, 1000 /* FIXME: arbitrary */); + NULL, 0, USB_CTRL_SET_TIMEOUT); if (result < 0) { dev_err(dev, "Cannot set WUSB stream index: %d\n", result); goto out; @@ -321,7 +320,7 @@ static int __hwahc_op_bwa_set(struct wusbhc *wusbhc, s8 stream_index, WUSB_REQ_SET_WUSB_MAS, USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, wa->usb_iface->cur_altsetting->desc.bInterfaceNumber, - mas_le, 32, 1000 /* FIXME: arbitrary */); + mas_le, 32, USB_CTRL_SET_TIMEOUT); if (result < 0) dev_err(dev, "Cannot set WUSB MAS allocation: %d\n", result); out: @@ -355,7 +354,7 @@ static int __hwahc_op_mmcie_add(struct wusbhc *wusbhc, u8 interval, USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, interval << 8 | repeat_cnt, handle << 8 | iface_no, - wuie, wuie->bLength, 1000 /* FIXME: arbitrary */); + wuie, wuie->bLength, USB_CTRL_SET_TIMEOUT); } /* @@ -372,7 +371,7 @@ static int __hwahc_op_mmcie_rm(struct wusbhc *wusbhc, u8 handle) WUSB_REQ_REMOVE_MMC_IE, USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, handle << 8 | iface_no, - NULL, 0, 1000 /* FIXME: arbitrary */); + NULL, 0, USB_CTRL_SET_TIMEOUT); } /* @@ -415,7 +414,7 @@ static int __hwahc_op_dev_info_set(struct wusbhc *wusbhc, USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, wusb_dev->port_idx << 8 | iface_no, dev_info, sizeof(struct hwa_dev_info), - 1000 /* FIXME: arbitrary */); + USB_CTRL_SET_TIMEOUT); kfree(dev_info); return ret; } @@ -455,7 +454,7 @@ static int __hwahc_dev_set_key(struct wusbhc *wusbhc, u8 port_idx, u32 tkid, USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, USB_DT_KEY << 8 | key_idx, port_idx << 8 | iface_no, - keyd, keyd_len, 1000 /* FIXME: arbitrary */); + keyd, keyd_len, USB_CTRL_SET_TIMEOUT); kzfree(keyd); /* clear keys etc. */ return result; @@ -497,7 +496,7 @@ static int __hwahc_op_set_ptk(struct wusbhc *wusbhc, u8 port_idx, u32 tkid, USB_REQ_SET_ENCRYPTION, USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, encryption_value, port_idx << 8 | iface_no, - NULL, 0, 1000 /* FIXME: arbitrary */); + NULL, 0, USB_CTRL_SET_TIMEOUT); if (result < 0) dev_err(wusbhc->dev, "Can't set host's WUSB encryption for " "port index %u to %s (value %d): %d\n", port_idx, @@ -791,6 +790,7 @@ static int hwahc_probe(struct usb_interface *usb_iface, dev_err(dev, "Cannot add HCD: %d\n", result); goto error_add_hcd; } + device_wakeup_enable(usb_hcd->self.controller); result = wusbhc_b_create(&hwahc->wusbhc); if (result < 0) { dev_err(dev, "Cannot setup phase B of WUSBHC: %d\n", result); diff --git a/drivers/usb/host/imx21-dbg.c b/drivers/usb/host/imx21-dbg.c index ec98ece..4f320d0 100644 --- a/drivers/usb/host/imx21-dbg.c +++ b/drivers/usb/host/imx21-dbg.c @@ -18,6 +18,10 @@ /* this file is part of imx21-hcd.c */ +#ifdef CONFIG_DYNAMIC_DEBUG +#define DEBUG +#endif + #ifndef DEBUG static inline void create_debug_files(struct imx21 *imx21) { } diff --git a/drivers/usb/host/imx21-hcd.c b/drivers/usb/host/imx21-hcd.c index adb01d9..207bad9 100644 --- a/drivers/usb/host/imx21-hcd.c +++ b/drivers/usb/host/imx21-hcd.c @@ -62,6 +62,10 @@ #include "imx21-hcd.h" +#ifdef CONFIG_DYNAMIC_DEBUG +#define DEBUG +#endif + #ifdef DEBUG #define DEBUG_LOG_FRAME(imx21, etd, event) \ (etd)->event##_frame = readl((imx21)->regs + USBH_FRMNUB) @@ -1906,6 +1910,7 @@ static int imx21_probe(struct platform_device *pdev) dev_err(imx21->dev, "usb_add_hcd() returned %d\n", ret); goto failed_add_hcd; } + device_wakeup_enable(hcd->self.controller); return 0; @@ -1926,7 +1931,7 @@ failed_request_mem: static struct platform_driver imx21_hcd_driver = { .driver = { - .name = (char *)hcd_name, + .name = hcd_name, }, .probe = imx21_probe, .remove = imx21_remove, diff --git a/drivers/usb/host/imx21-hcd.h b/drivers/usb/host/imx21-hcd.h index c005770..05122f8 100644 --- a/drivers/usb/host/imx21-hcd.h +++ b/drivers/usb/host/imx21-hcd.h @@ -24,6 +24,10 @@ #ifndef __LINUX_IMX21_HCD_H__ #define __LINUX_IMX21_HCD_H__ +#ifdef CONFIG_DYNAMIC_DEBUG +#define DEBUG +#endif + #include <linux/platform_data/usb-mx2.h> #define NUM_ISO_ETDS 2 diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index c7d0f8f..240e792 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -60,7 +60,6 @@ #include <linux/debugfs.h> #include <linux/seq_file.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/list.h> #include <linux/slab.h> #include <linux/usb.h> @@ -1645,6 +1644,8 @@ static int isp116x_probe(struct platform_device *pdev) if (ret) goto err6; + device_wakeup_enable(hcd->self.controller); + ret = create_debug_file(isp116x); if (ret) { ERR("Couldn't create debugfs entry\n"); @@ -1705,7 +1706,7 @@ static struct platform_driver isp116x_driver = { .suspend = isp116x_suspend, .resume = isp116x_resume, .driver = { - .name = (char *)hcd_name, + .name = hcd_name, .owner = THIS_MODULE, }, }; diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c index 935a2dd..875bcfd 100644 --- a/drivers/usb/host/isp1362-hcd.c +++ b/drivers/usb/host/isp1362-hcd.c @@ -67,7 +67,6 @@ #include <linux/sched.h> #include <linux/slab.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/list.h> #include <linux/interrupt.h> #include <linux/usb.h> @@ -2746,6 +2745,8 @@ static int isp1362_probe(struct platform_device *pdev) retval = usb_add_hcd(hcd, irq, irq_flags | IRQF_SHARED); if (retval != 0) goto err6; + device_wakeup_enable(hcd->self.controller); + pr_info("%s, irq %d\n", hcd->product_desc, irq); create_debug_file(isp1362_hcd); @@ -2829,7 +2830,7 @@ static struct platform_driver isp1362_driver = { .suspend = isp1362_suspend, .resume = isp1362_resume, .driver = { - .name = (char *)hcd_name, + .name = hcd_name, .owner = THIS_MODULE, }, }; diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index 2facee5..51a0ae9 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -2250,6 +2250,7 @@ struct usb_hcd *isp1760_register(phys_addr_t res_start, resource_size_t res_len, ret = usb_add_hcd(hcd, irq, irqflags); if (ret) goto err_unmap; + device_wakeup_enable(hcd->self.controller); return hcd; diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index 418444e..091ae49 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -136,61 +136,58 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver, struct ohci_hcd *ohci; int retval; struct usb_hcd *hcd = NULL; - - if (pdev->num_resources != 2) { - pr_debug("hcd probe: invalid num_resources"); - return -ENODEV; + struct device *dev = &pdev->dev; + struct resource *res; + int irq; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_dbg(dev, "hcd probe: missing memory resource\n"); + return -ENXIO; } - if ((pdev->resource[0].flags != IORESOURCE_MEM) - || (pdev->resource[1].flags != IORESOURCE_IRQ)) { - pr_debug("hcd probe: invalid resource type\n"); - return -ENODEV; + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_dbg(dev, "hcd probe: missing irq resource\n"); + return irq; } - hcd = usb_create_hcd(driver, &pdev->dev, "at91"); + hcd = usb_create_hcd(driver, dev, "at91"); if (!hcd) return -ENOMEM; - hcd->rsrc_start = pdev->resource[0].start; - hcd->rsrc_len = resource_size(&pdev->resource[0]); - - if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { - pr_debug("request_mem_region failed\n"); - retval = -EBUSY; - goto err1; - } + hcd->rsrc_start = res->start; + hcd->rsrc_len = resource_size(res); - hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); - if (!hcd->regs) { - pr_debug("ioremap failed\n"); - retval = -EIO; - goto err2; + hcd->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(hcd->regs)) { + retval = PTR_ERR(hcd->regs); + goto err; } - iclk = clk_get(&pdev->dev, "ohci_clk"); + iclk = devm_clk_get(dev, "ohci_clk"); if (IS_ERR(iclk)) { - dev_err(&pdev->dev, "failed to get ohci_clk\n"); + dev_err(dev, "failed to get ohci_clk\n"); retval = PTR_ERR(iclk); - goto err3; + goto err; } - fclk = clk_get(&pdev->dev, "uhpck"); + fclk = devm_clk_get(dev, "uhpck"); if (IS_ERR(fclk)) { - dev_err(&pdev->dev, "failed to get uhpck\n"); + dev_err(dev, "failed to get uhpck\n"); retval = PTR_ERR(fclk); - goto err4; + goto err; } - hclk = clk_get(&pdev->dev, "hclk"); + hclk = devm_clk_get(dev, "hclk"); if (IS_ERR(hclk)) { - dev_err(&pdev->dev, "failed to get hclk\n"); + dev_err(dev, "failed to get hclk\n"); retval = PTR_ERR(hclk); - goto err5; + goto err; } if (IS_ENABLED(CONFIG_COMMON_CLK)) { - uclk = clk_get(&pdev->dev, "usb_clk"); + uclk = devm_clk_get(dev, "usb_clk"); if (IS_ERR(uclk)) { - dev_err(&pdev->dev, "failed to get uclk\n"); + dev_err(dev, "failed to get uclk\n"); retval = PTR_ERR(uclk); - goto err6; + goto err; } } @@ -199,29 +196,16 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver, ohci->num_ports = board->ports; at91_start_hc(pdev); - retval = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_SHARED); - if (retval == 0) + retval = usb_add_hcd(hcd, irq, IRQF_SHARED); + if (retval == 0) { + device_wakeup_enable(hcd->self.controller); return retval; + } /* Error handling */ at91_stop_hc(pdev); - if (IS_ENABLED(CONFIG_COMMON_CLK)) - clk_put(uclk); - err6: - clk_put(hclk); - err5: - clk_put(fclk); - err4: - clk_put(iclk); - - err3: - iounmap(hcd->regs); - - err2: - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); - - err1: + err: usb_put_hcd(hcd); return retval; } @@ -244,16 +228,7 @@ static void usb_hcd_at91_remove(struct usb_hcd *hcd, { usb_remove_hcd(hcd); at91_stop_hc(pdev); - iounmap(hcd->regs); - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); - - if (IS_ENABLED(CONFIG_COMMON_CLK)) - clk_put(uclk); - clk_put(hclk); - clk_put(fclk); - clk_put(iclk); - fclk = iclk = hclk = NULL; } /*-------------------------------------------------------------------------*/ @@ -635,10 +610,17 @@ ohci_hcd_at91_drv_suspend(struct platform_device *pdev, pm_message_t mesg) { struct usb_hcd *hcd = platform_get_drvdata(pdev); struct ohci_hcd *ohci = hcd_to_ohci(hcd); + bool do_wakeup = device_may_wakeup(&pdev->dev); + int ret; - if (device_may_wakeup(&pdev->dev)) + if (do_wakeup) enable_irq_wake(hcd->irq); + ret = ohci_suspend(hcd, do_wakeup); + if (ret) { + disable_irq_wake(hcd->irq); + return ret; + } /* * The integrated transceivers seem unable to notice disconnect, * reconnect, or wakeup without the 48 MHz clock active. so for @@ -657,7 +639,7 @@ ohci_hcd_at91_drv_suspend(struct platform_device *pdev, pm_message_t mesg) at91_stop_clock(); } - return 0; + return ret; } static int ohci_hcd_at91_drv_resume(struct platform_device *pdev) diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c index 9be59f1..df06be6 100644 --- a/drivers/usb/host/ohci-da8xx.c +++ b/drivers/usb/host/ohci-da8xx.c @@ -300,41 +300,28 @@ static int usb_hcd_da8xx_probe(const struct hc_driver *driver, if (hub == NULL) return -ENODEV; - usb11_clk = clk_get(&pdev->dev, "usb11"); + usb11_clk = devm_clk_get(&pdev->dev, "usb11"); if (IS_ERR(usb11_clk)) return PTR_ERR(usb11_clk); - usb20_clk = clk_get(&pdev->dev, "usb20"); - if (IS_ERR(usb20_clk)) { - error = PTR_ERR(usb20_clk); - goto err0; - } + usb20_clk = devm_clk_get(&pdev->dev, "usb20"); + if (IS_ERR(usb20_clk)) + return PTR_ERR(usb20_clk); hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); - if (!hcd) { - error = -ENOMEM; - goto err1; - } + if (!hcd) + return -ENOMEM; mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) { - error = -ENODEV; - goto err2; - } + if (!mem) + return -ENODEV; hcd->rsrc_start = mem->start; hcd->rsrc_len = resource_size(mem); - if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { - dev_dbg(&pdev->dev, "request_mem_region failed\n"); - error = -EBUSY; - goto err2; - } - - hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); - if (!hcd->regs) { - dev_err(&pdev->dev, "ioremap failed\n"); - error = -ENOMEM; - goto err3; + hcd->regs = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(hcd->regs)) { + error = PTR_ERR(hcd->regs); + goto err; } ohci_hcd_init(hcd_to_ohci(hcd)); @@ -342,11 +329,13 @@ static int usb_hcd_da8xx_probe(const struct hc_driver *driver, irq = platform_get_irq(pdev, 0); if (irq < 0) { error = -ENODEV; - goto err4; + goto err; } error = usb_add_hcd(hcd, irq, 0); if (error) - goto err4; + goto err; + + device_wakeup_enable(hcd->self.controller); if (hub->ocic_notify) { error = hub->ocic_notify(ohci_da8xx_ocic_handler); @@ -355,16 +344,8 @@ static int usb_hcd_da8xx_probe(const struct hc_driver *driver, } usb_remove_hcd(hcd); -err4: - iounmap(hcd->regs); -err3: - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); -err2: +err: usb_put_hcd(hcd); -err1: - clk_put(usb20_clk); -err0: - clk_put(usb11_clk); return error; } @@ -384,11 +365,7 @@ usb_hcd_da8xx_remove(struct usb_hcd *hcd, struct platform_device *pdev) hub->ocic_notify(NULL); usb_remove_hcd(hcd); - iounmap(hcd->regs); - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); - clk_put(usb20_clk); - clk_put(usb11_clk); } static int ohci_hcd_da8xx_drv_probe(struct platform_device *dev) @@ -406,19 +383,27 @@ static int ohci_hcd_da8xx_drv_remove(struct platform_device *dev) } #ifdef CONFIG_PM -static int ohci_da8xx_suspend(struct platform_device *dev, pm_message_t message) +static int ohci_da8xx_suspend(struct platform_device *pdev, + pm_message_t message) { - struct usb_hcd *hcd = platform_get_drvdata(dev); + struct usb_hcd *hcd = platform_get_drvdata(pdev); struct ohci_hcd *ohci = hcd_to_ohci(hcd); + bool do_wakeup = device_may_wakeup(&pdev->dev); + int ret; + if (time_before(jiffies, ohci->next_statechange)) msleep(5); ohci->next_statechange = jiffies; + ret = ohci_suspend(hcd, do_wakeup); + if (ret) + return ret; + ohci_da8xx_clock(0); hcd->state = HC_STATE_SUSPENDED; - dev->dev.power.power_state = PMSG_SUSPEND; - return 0; + + return ret; } static int ohci_da8xx_resume(struct platform_device *dev) diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c index 3fca52e..45032e9 100644 --- a/drivers/usb/host/ohci-dbg.c +++ b/drivers/usb/host/ohci-dbg.c @@ -9,8 +9,6 @@ /*-------------------------------------------------------------------------*/ -#ifdef DEBUG - #define edstring(ed_type) ({ char *temp; \ switch (ed_type) { \ case PIPE_CONTROL: temp = "ctrl"; break; \ @@ -20,57 +18,6 @@ } temp;}) #define pipestring(pipe) edstring(usb_pipetype(pipe)) -/* debug| print the main components of an URB - * small: 0) header + data packets 1) just header - */ -static void __maybe_unused -urb_print(struct urb * urb, char * str, int small, int status) -{ - unsigned int pipe= urb->pipe; - - if (!urb->dev || !urb->dev->bus) { - printk(KERN_DEBUG "%s URB: no dev\n", str); - return; - } - -#ifndef OHCI_VERBOSE_DEBUG - if (status != 0) -#endif - printk(KERN_DEBUG "%s %p dev=%d ep=%d%s-%s flags=%x len=%d/%d stat=%d\n", - str, - urb, - usb_pipedevice (pipe), - usb_pipeendpoint (pipe), - usb_pipeout (pipe)? "out" : "in", - pipestring (pipe), - urb->transfer_flags, - urb->actual_length, - urb->transfer_buffer_length, - status); - -#ifdef OHCI_VERBOSE_DEBUG - if (!small) { - int i, len; - - if (usb_pipecontrol (pipe)) { - printk (KERN_DEBUG "%s: setup(8):", __FILE__); - for (i = 0; i < 8 ; i++) - printk (" %02x", ((__u8 *) urb->setup_packet) [i]); - printk ("\n"); - } - if (urb->transfer_buffer_length > 0 && urb->transfer_buffer) { - printk (KERN_DEBUG "%s: data(%d/%d):", __FILE__, - urb->actual_length, - urb->transfer_buffer_length); - len = usb_pipeout (pipe)? - urb->transfer_buffer_length: urb->actual_length; - for (i = 0; i < 16 && i < len; i++) - printk (" %02x", ((__u8 *) urb->transfer_buffer) [i]); - printk ("%s stat:%d\n", i < len? "...": "", status); - } - } -#endif -} #define ohci_dbg_sw(ohci, next, size, format, arg...) \ do { \ @@ -407,22 +354,8 @@ ohci_dump_ed (const struct ohci_hcd *ohci, const char *label, } } -#else -static inline void ohci_dump (struct ohci_hcd *controller, int verbose) {} - -#undef OHCI_VERBOSE_DEBUG - -#endif /* DEBUG */ - /*-------------------------------------------------------------------------*/ -#ifdef STUB_DEBUG_FILES - -static inline void create_debug_files (struct ohci_hcd *bus) { } -static inline void remove_debug_files (struct ohci_hcd *bus) { } - -#else - static int debug_async_open(struct inode *, struct file *); static int debug_periodic_open(struct inode *, struct file *); static int debug_registers_open(struct inode *, struct file *); @@ -871,7 +804,5 @@ static inline void remove_debug_files (struct ohci_hcd *ohci) debugfs_remove(ohci->debug_dir); } -#endif - /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c index 91ec9b2..68588d8 100644 --- a/drivers/usb/host/ohci-exynos.c +++ b/drivers/usb/host/ohci-exynos.c @@ -146,6 +146,7 @@ skip_phy: dev_err(&pdev->dev, "Failed to add USB HCD\n"); goto fail_add_hcd; } + device_wakeup_enable(hcd->self.controller); return 0; fail_add_hcd: @@ -191,23 +192,14 @@ static int exynos_ohci_suspend(struct device *dev) struct exynos_ohci_hcd *exynos_ohci = to_exynos_ohci(hcd); struct ohci_hcd *ohci = hcd_to_ohci(hcd); struct platform_device *pdev = to_platform_device(dev); + bool do_wakeup = device_may_wakeup(dev); unsigned long flags; - int rc = 0; + int rc = ohci_suspend(hcd, do_wakeup); - /* - * Root hub was already suspended. Disable irq emission and - * mark HW unaccessible, bail out if RH has been resumed. Use - * the spinlock to properly synchronize with possible pending - * RH suspend or resume activity. - */ - spin_lock_irqsave(&ohci->lock, flags); - if (ohci->rh_state != OHCI_RH_SUSPENDED && - ohci->rh_state != OHCI_RH_HALTED) { - rc = -EINVAL; - goto fail; - } + if (rc) + return rc; - clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + spin_lock_irqsave(&ohci->lock, flags); if (exynos_ohci->otg) exynos_ohci->otg->set_host(exynos_ohci->otg, &hcd->self); @@ -216,10 +208,9 @@ static int exynos_ohci_suspend(struct device *dev) clk_disable_unprepare(exynos_ohci->clk); -fail: spin_unlock_irqrestore(&ohci->lock, flags); - return rc; + return 0; } static int exynos_ohci_resume(struct device *dev) diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 8ada13f..3586460 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -51,8 +51,6 @@ /*-------------------------------------------------------------------------*/ -#undef OHCI_VERBOSE_DEBUG /* not always helpful */ - /* For initializing controller (mask in an HCFS mode too) */ #define OHCI_CONTROL_INIT OHCI_CTRL_CBSR #define OHCI_INTR_INIT \ @@ -127,10 +125,6 @@ static int ohci_urb_enqueue ( unsigned long flags; int retval = 0; -#ifdef OHCI_VERBOSE_DEBUG - urb_print(urb, "SUB", usb_pipein(pipe), -EINPROGRESS); -#endif - /* every endpoint has a ed, locate and maybe (re)initialize it */ if (! (ed = ed_get (ohci, urb->ep, urb->dev, pipe, urb->interval))) return -ENOMEM; @@ -284,10 +278,6 @@ static int ohci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) unsigned long flags; int rc; -#ifdef OHCI_VERBOSE_DEBUG - urb_print(urb, "UNLINK", 1, status); -#endif - spin_lock_irqsave (&ohci->lock, flags); rc = usb_hcd_check_unlink_urb(hcd, urb, status); if (rc) { @@ -840,7 +830,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) } if (ints & OHCI_INTR_RHSC) { - ohci_vdbg(ohci, "rhsc\n"); + ohci_dbg(ohci, "rhsc\n"); ohci->next_statechange = jiffies + STATECHANGE_DELAY; ohci_writel(ohci, OHCI_INTR_RD | OHCI_INTR_RHSC, ®s->intrstatus); @@ -862,7 +852,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) * this might not happen. */ else if (ints & OHCI_INTR_RD) { - ohci_vdbg(ohci, "resume detect\n"); + ohci_dbg(ohci, "resume detect\n"); ohci_writel(ohci, OHCI_INTR_RD, ®s->intrstatus); set_bit(HCD_FLAG_POLL_RH, &hcd->flags); if (ohci->autostop) { @@ -1036,6 +1026,7 @@ int ohci_suspend(struct usb_hcd *hcd, bool do_wakeup) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); unsigned long flags; + int rc = 0; /* Disable irq emission and mark HW unaccessible. Use * the spinlock to properly synchronize with possible pending @@ -1048,7 +1039,13 @@ int ohci_suspend(struct usb_hcd *hcd, bool do_wakeup) clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); spin_unlock_irqrestore (&ohci->lock, flags); - return 0; + synchronize_irq(hcd->irq); + + if (do_wakeup && HCD_WAKEUP_PENDING(hcd)) { + ohci_resume(hcd, false); + rc = -EBUSY; + } + return rc; } EXPORT_SYMBOL_GPL(ohci_suspend); @@ -1233,13 +1230,11 @@ static int __init ohci_hcd_mod_init(void) sizeof (struct ed), sizeof (struct td)); set_bit(USB_OHCI_LOADED, &usb_hcds_loaded); -#ifdef DEBUG ohci_debug_root = debugfs_create_dir("ohci", usb_debug_root); if (!ohci_debug_root) { retval = -ENOENT; goto error_debug; } -#endif #ifdef PS3_SYSTEM_BUS_DRIVER retval = ps3_ohci_driver_register(&PS3_SYSTEM_BUS_DRIVER); @@ -1314,11 +1309,9 @@ static int __init ohci_hcd_mod_init(void) ps3_ohci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); error_ps3: #endif -#ifdef DEBUG debugfs_remove(ohci_debug_root); ohci_debug_root = NULL; error_debug: -#endif clear_bit(USB_OHCI_LOADED, &usb_hcds_loaded); return retval; @@ -1348,9 +1341,7 @@ static void __exit ohci_hcd_mod_exit(void) #ifdef PS3_SYSTEM_BUS_DRIVER ps3_ohci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); #endif -#ifdef DEBUG debugfs_remove(ohci_debug_root); -#endif clear_bit(USB_OHCI_LOADED, &usb_hcds_loaded); } module_exit(ohci_hcd_mod_exit); diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index 61705a7..c81c872 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -725,10 +725,8 @@ static int ohci_hub_control ( temp = roothub_portstatus (ohci, wIndex); put_unaligned_le32(temp, buf); -#ifndef OHCI_VERBOSE_DEBUG - if (*(u16*)(buf+2)) /* only if wPortChange is interesting */ -#endif - dbg_port (ohci, "GetStatus", wIndex, temp); + if (*(u16*)(buf+2)) /* only if wPortChange is interesting */ + dbg_port(ohci, "GetStatus", wIndex, temp); break; case SetHubFeature: switch (wValue) { diff --git a/drivers/usb/host/ohci-jz4740.c b/drivers/usb/host/ohci-jz4740.c index d4ef539..af8dc1b 100644 --- a/drivers/usb/host/ohci-jz4740.c +++ b/drivers/usb/host/ohci-jz4740.c @@ -174,31 +174,23 @@ static int jz4740_ohci_probe(struct platform_device *pdev) jz4740_ohci = hcd_to_jz4740_hcd(hcd); - res = request_mem_region(res->start, resource_size(res), hcd_name); - if (!res) { - dev_err(&pdev->dev, "Failed to request mem region.\n"); - ret = -EBUSY; - goto err_free; - } - hcd->rsrc_start = res->start; hcd->rsrc_len = resource_size(res); - hcd->regs = ioremap(res->start, resource_size(res)); - if (!hcd->regs) { - dev_err(&pdev->dev, "Failed to ioremap registers.\n"); - ret = -EBUSY; - goto err_release_mem; + hcd->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(hcd->regs)) { + ret = PTR_ERR(hcd->regs); + goto err_free; } - jz4740_ohci->clk = clk_get(&pdev->dev, "uhc"); + jz4740_ohci->clk = devm_clk_get(&pdev->dev, "uhc"); if (IS_ERR(jz4740_ohci->clk)) { ret = PTR_ERR(jz4740_ohci->clk); dev_err(&pdev->dev, "Failed to get clock: %d\n", ret); - goto err_iounmap; + goto err_free; } - jz4740_ohci->vbus = regulator_get(&pdev->dev, "vbus"); + jz4740_ohci->vbus = devm_regulator_get(&pdev->dev, "vbus"); if (IS_ERR(jz4740_ohci->vbus)) jz4740_ohci->vbus = NULL; @@ -217,21 +209,15 @@ static int jz4740_ohci_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Failed to add hcd: %d\n", ret); goto err_disable; } + device_wakeup_enable(hcd->self.controller); return 0; err_disable: - if (jz4740_ohci->vbus) { + if (jz4740_ohci->vbus) regulator_disable(jz4740_ohci->vbus); - regulator_put(jz4740_ohci->vbus); - } clk_disable(jz4740_ohci->clk); - clk_put(jz4740_ohci->clk); -err_iounmap: - iounmap(hcd->regs); -err_release_mem: - release_mem_region(res->start, resource_size(res)); err_free: usb_put_hcd(hcd); @@ -245,16 +231,10 @@ static int jz4740_ohci_remove(struct platform_device *pdev) usb_remove_hcd(hcd); - if (jz4740_ohci->vbus) { + if (jz4740_ohci->vbus) regulator_disable(jz4740_ohci->vbus); - regulator_put(jz4740_ohci->vbus); - } clk_disable(jz4740_ohci->clk); - clk_put(jz4740_ohci->clk); - - iounmap(hcd->regs); - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); diff --git a/drivers/usb/host/ohci-nxp.c b/drivers/usb/host/ohci-nxp.c index e99db8a..ba180ed 100644 --- a/drivers/usb/host/ohci-nxp.c +++ b/drivers/usb/host/ohci-nxp.c @@ -196,17 +196,17 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev) __raw_writel(USB_SLAVE_HCLK_EN | PAD_CONTROL_LAST_DRIVEN, USB_CTRL); /* Enable USB PLL */ - usb_pll_clk = clk_get(&pdev->dev, "ck_pll5"); + usb_pll_clk = devm_clk_get(&pdev->dev, "ck_pll5"); if (IS_ERR(usb_pll_clk)) { dev_err(&pdev->dev, "failed to acquire USB PLL\n"); ret = PTR_ERR(usb_pll_clk); - goto fail_pll; + goto fail_disable; } ret = clk_enable(usb_pll_clk); if (ret < 0) { dev_err(&pdev->dev, "failed to start USB PLL\n"); - goto fail_pllen; + goto fail_disable; } ret = clk_set_rate(usb_pll_clk, 48000); @@ -216,21 +216,21 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev) } /* Enable USB device clock */ - usb_dev_clk = clk_get(&pdev->dev, "ck_usbd"); + usb_dev_clk = devm_clk_get(&pdev->dev, "ck_usbd"); if (IS_ERR(usb_dev_clk)) { dev_err(&pdev->dev, "failed to acquire USB DEV Clock\n"); ret = PTR_ERR(usb_dev_clk); - goto fail_dev; + goto fail_rate; } ret = clk_enable(usb_dev_clk); if (ret < 0) { dev_err(&pdev->dev, "failed to start USB DEV Clock\n"); - goto fail_deven; + goto fail_rate; } /* Enable USB otg clocks */ - usb_otg_clk = clk_get(&pdev->dev, "ck_usb_otg"); + usb_otg_clk = devm_clk_get(&pdev->dev, "ck_usb_otg"); if (IS_ERR(usb_otg_clk)) { dev_err(&pdev->dev, "failed to acquire USB DEV Clock\n"); ret = PTR_ERR(usb_otg_clk); @@ -242,7 +242,7 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev) ret = clk_enable(usb_otg_clk); if (ret < 0) { dev_err(&pdev->dev, "failed to start USB DEV Clock\n"); - goto fail_otgen; + goto fail_otg; } isp1301_configure(); @@ -274,26 +274,20 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev) dev_info(&pdev->dev, "at 0x%p, irq %d\n", hcd->regs, hcd->irq); ret = usb_add_hcd(hcd, irq, 0); - if (ret == 0) + if (ret == 0) { + device_wakeup_enable(hcd->self.controller); return ret; + } ohci_nxp_stop_hc(); fail_resource: usb_put_hcd(hcd); fail_hcd: clk_disable(usb_otg_clk); -fail_otgen: - clk_put(usb_otg_clk); fail_otg: clk_disable(usb_dev_clk); -fail_deven: - clk_put(usb_dev_clk); -fail_dev: fail_rate: clk_disable(usb_pll_clk); -fail_pllen: - clk_put(usb_pll_clk); -fail_pll: fail_disable: isp1301_i2c_client = NULL; return ret; @@ -307,9 +301,7 @@ static int ohci_hcd_nxp_remove(struct platform_device *pdev) ohci_nxp_stop_hc(); usb_put_hcd(hcd); clk_disable(usb_pll_clk); - clk_put(usb_pll_clk); clk_disable(usb_dev_clk); - clk_put(usb_dev_clk); i2c_unregister_device(isp1301_i2c_client); isp1301_i2c_client = NULL; diff --git a/drivers/usb/host/ohci-octeon.c b/drivers/usb/host/ohci-octeon.c index 6c16dce..15af895 100644 --- a/drivers/usb/host/ohci-octeon.c +++ b/drivers/usb/host/ohci-octeon.c @@ -138,20 +138,12 @@ static int ohci_octeon_drv_probe(struct platform_device *pdev) hcd->rsrc_start = res_mem->start; hcd->rsrc_len = resource_size(res_mem); - if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, - OCTEON_OHCI_HCD_NAME)) { - dev_err(&pdev->dev, "request_mem_region failed\n"); - ret = -EBUSY; + reg_base = devm_ioremap_resource(&pdev->dev, res_mem); + if (IS_ERR(reg_base)) { + ret = PTR_ERR(reg_base); goto err1; } - reg_base = ioremap(hcd->rsrc_start, hcd->rsrc_len); - if (!reg_base) { - dev_err(&pdev->dev, "ioremap failed\n"); - ret = -ENOMEM; - goto err2; - } - ohci_octeon_hw_start(); hcd->regs = reg_base; @@ -168,19 +160,18 @@ static int ohci_octeon_drv_probe(struct platform_device *pdev) ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) { dev_dbg(&pdev->dev, "failed to add hcd with err %d\n", ret); - goto err3; + goto err2; } + device_wakeup_enable(hcd->self.controller); + platform_set_drvdata(pdev, hcd); return 0; -err3: +err2: ohci_octeon_hw_stop(); - iounmap(hcd->regs); -err2: - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); err1: usb_put_hcd(hcd); return ret; @@ -193,8 +184,6 @@ static int ohci_octeon_drv_remove(struct platform_device *pdev) usb_remove_hcd(hcd); ohci_octeon_hw_stop(); - iounmap(hcd->regs); - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); return 0; diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index f253214..c923caf 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -311,14 +311,14 @@ static int usb_hcd_omap_probe (const struct hc_driver *driver, struct usb_hcd *hcd = 0; if (pdev->num_resources != 2) { - printk(KERN_ERR "hcd probe: invalid num_resources: %i\n", + dev_err(&pdev->dev, "invalid num_resources: %i\n", pdev->num_resources); return -ENODEV; } if (pdev->resource[0].flags != IORESOURCE_MEM || pdev->resource[1].flags != IORESOURCE_IRQ) { - printk(KERN_ERR "hcd probe: invalid resource type\n"); + dev_err(&pdev->dev, "invalid resource type\n"); return -ENODEV; } @@ -367,6 +367,7 @@ static int usb_hcd_omap_probe (const struct hc_driver *driver, if (retval) goto err3; + device_wakeup_enable(hcd->self.controller); return 0; err3: iounmap(hcd->regs); diff --git a/drivers/usb/host/ohci-omap3.c b/drivers/usb/host/ohci-omap3.c index 2145741..ec15aeb 100644 --- a/drivers/usb/host/ohci-omap3.c +++ b/drivers/usb/host/ohci-omap3.c @@ -130,6 +130,7 @@ static int ohci_hcd_omap3_probe(struct platform_device *pdev) dev_dbg(dev, "failed to add hcd with err %d\n", ret); goto err_add_hcd; } + device_wakeup_enable(hcd->self.controller); return 0; diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c index f351ff5..68f674c 100644 --- a/drivers/usb/host/ohci-platform.c +++ b/drivers/usb/host/ohci-platform.c @@ -108,6 +108,8 @@ static int ohci_platform_probe(struct platform_device *dev) if (err) goto err_put_hcd; + device_wakeup_enable(hcd->self.controller); + platform_set_drvdata(dev, hcd); return err; diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c index 81f3eba..965e3e9 100644 --- a/drivers/usb/host/ohci-ppc-of.c +++ b/drivers/usb/host/ohci-ppc-of.c @@ -115,24 +115,18 @@ static int ohci_hcd_ppc_of_probe(struct platform_device *op) hcd->rsrc_start = res.start; hcd->rsrc_len = resource_size(&res); - if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { - printk(KERN_ERR "%s: request_mem_region failed\n", __FILE__); - rv = -EBUSY; + hcd->regs = devm_ioremap_resource(&op->dev, &res); + if (IS_ERR(hcd->regs)) { + rv = PTR_ERR(hcd->regs); goto err_rmr; } irq = irq_of_parse_and_map(dn, 0); if (irq == NO_IRQ) { - printk(KERN_ERR "%s: irq_of_parse_and_map failed\n", __FILE__); + dev_err(&op->dev, "%s: irq_of_parse_and_map failed\n", + __FILE__); rv = -EBUSY; - goto err_irq; - } - - hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); - if (!hcd->regs) { - printk(KERN_ERR "%s: ioremap failed\n", __FILE__); - rv = -ENOMEM; - goto err_ioremap; + goto err_rmr; } ohci = hcd_to_ohci(hcd); @@ -147,8 +141,10 @@ static int ohci_hcd_ppc_of_probe(struct platform_device *op) ohci_hcd_init(ohci); rv = usb_add_hcd(hcd, irq, 0); - if (rv == 0) + if (rv == 0) { + device_wakeup_enable(hcd->self.controller); return 0; + } /* by now, 440epx is known to show usb_23 erratum */ np = of_find_compatible_node(NULL, NULL, "ibm,usb-ehci-440epx"); @@ -174,11 +170,7 @@ static int ohci_hcd_ppc_of_probe(struct platform_device *op) pr_debug("%s: cannot get ehci offset from fdt\n", __FILE__); } - iounmap(hcd->regs); -err_ioremap: irq_dispose_mapping(irq); -err_irq: - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); err_rmr: usb_put_hcd(hcd); @@ -193,9 +185,7 @@ static int ohci_hcd_ppc_of_remove(struct platform_device *op) usb_remove_hcd(hcd); - iounmap(hcd->regs); irq_dispose_mapping(hcd->irq); - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); diff --git a/drivers/usb/host/ohci-ps3.c b/drivers/usb/host/ohci-ps3.c index 7d35cd9..71d8bc4 100644 --- a/drivers/usb/host/ohci-ps3.c +++ b/drivers/usb/host/ohci-ps3.c @@ -173,6 +173,7 @@ static int ps3_ohci_probe(struct ps3_system_bus_device *dev) goto fail_add_hcd; } + device_wakeup_enable(hcd->self.controller); return result; fail_add_hcd: diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c index 9b7435f..d21d5fe 100644 --- a/drivers/usb/host/ohci-pxa27x.c +++ b/drivers/usb/host/ohci-pxa27x.c @@ -388,37 +388,28 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device return -ENXIO; } - usb_clk = clk_get(&pdev->dev, NULL); + usb_clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(usb_clk)) return PTR_ERR(usb_clk); hcd = usb_create_hcd (driver, &pdev->dev, "pxa27x"); - if (!hcd) { - retval = -ENOMEM; - goto err0; - } + if (!hcd) + return -ENOMEM; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!r) { pr_err("no resource of IORESOURCE_MEM"); retval = -ENXIO; - goto err1; + goto err; } hcd->rsrc_start = r->start; hcd->rsrc_len = resource_size(r); - if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { - pr_debug("request_mem_region failed"); - retval = -EBUSY; - goto err1; - } - - hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); - if (!hcd->regs) { - pr_debug("ioremap failed"); - retval = -ENOMEM; - goto err2; + hcd->regs = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(hcd->regs)) { + retval = PTR_ERR(hcd->regs); + goto err; } /* initialize "struct pxa27x_ohci" */ @@ -429,7 +420,7 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device retval = pxa27x_start_hc(pxa_ohci, &pdev->dev); if (retval < 0) { pr_debug("pxa27x_start_hc failed"); - goto err3; + goto err; } /* Select Power Management Mode */ @@ -443,18 +434,14 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device ohci->num_ports = 3; retval = usb_add_hcd(hcd, irq, 0); - if (retval == 0) + if (retval == 0) { + device_wakeup_enable(hcd->self.controller); return retval; + } pxa27x_stop_hc(pxa_ohci, &pdev->dev); - err3: - iounmap(hcd->regs); - err2: - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); - err1: + err: usb_put_hcd(hcd); - err0: - clk_put(usb_clk); return retval; } @@ -478,9 +465,6 @@ void usb_hcd_pxa27x_remove (struct usb_hcd *hcd, struct platform_device *pdev) usb_remove_hcd(hcd); pxa27x_stop_hc(pxa_ohci, &pdev->dev); - iounmap(hcd->regs); - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); - clk_put(pxa_ohci->clk); usb_put_hcd(hcd); } diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c index e7f577e..d4253e3 100644 --- a/drivers/usb/host/ohci-q.c +++ b/drivers/usb/host/ohci-q.c @@ -68,10 +68,6 @@ __acquires(ohci->lock) break; } -#ifdef OHCI_VERBOSE_DEBUG - urb_print(urb, "RET", usb_pipeout (urb->pipe), status); -#endif - /* urb->complete() can reenter this HCD */ usb_hcd_unlink_urb_from_ep(ohci_to_hcd(ohci), urb); spin_unlock (&ohci->lock); @@ -147,7 +143,7 @@ static void periodic_link (struct ohci_hcd *ohci, struct ed *ed) { unsigned i; - ohci_vdbg (ohci, "link %sed %p branch %d [%dus.], interval %d\n", + ohci_dbg(ohci, "link %sed %p branch %d [%dus.], interval %d\n", (ed->hwINFO & cpu_to_hc32 (ohci, ED_ISO)) ? "iso " : "", ed, ed->branch, ed->load, ed->interval); @@ -294,7 +290,7 @@ static void periodic_unlink (struct ohci_hcd *ohci, struct ed *ed) } ohci_to_hcd(ohci)->self.bandwidth_allocated -= ed->load / ed->interval; - ohci_vdbg (ohci, "unlink %sed %p branch %d [%dus.], interval %d\n", + ohci_dbg(ohci, "unlink %sed %p branch %d [%dus.], interval %d\n", (ed->hwINFO & cpu_to_hc32 (ohci, ED_ISO)) ? "iso " : "", ed, ed->branch, ed->load, ed->interval); } @@ -765,7 +761,7 @@ static int td_done(struct ohci_hcd *ohci, struct urb *urb, struct td *td) urb->iso_frame_desc [td->index].status = cc_to_error [cc]; if (cc != TD_CC_NOERROR) - ohci_vdbg (ohci, + ohci_dbg(ohci, "urb %p iso td %p (%d) len %d cc %d\n", urb, td, 1 + td->index, dlen, cc); @@ -797,7 +793,7 @@ static int td_done(struct ohci_hcd *ohci, struct urb *urb, struct td *td) } if (cc != TD_CC_NOERROR && cc < 0x0E) - ohci_vdbg (ohci, + ohci_dbg(ohci, "urb %p td %p (%d) cc %d, len=%d/%d\n", urb, td, 1 + td->index, cc, urb->actual_length, diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c index f90101b..ff7c8f1 100644 --- a/drivers/usb/host/ohci-s3c2410.c +++ b/drivers/usb/host/ohci-s3c2410.c @@ -395,6 +395,7 @@ static int usb_hcd_s3c2410_probe(const struct hc_driver *driver, if (retval != 0) goto err_ioremap; + device_wakeup_enable(hcd->self.controller); return 0; err_ioremap: @@ -426,28 +427,15 @@ static int ohci_hcd_s3c2410_drv_remove(struct platform_device *pdev) static int ohci_hcd_s3c2410_drv_suspend(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); - struct ohci_hcd *ohci = hcd_to_ohci(hcd); struct platform_device *pdev = to_platform_device(dev); - unsigned long flags; + bool do_wakeup = device_may_wakeup(dev); int rc = 0; - /* - * Root hub was already suspended. Disable irq emission and - * mark HW unaccessible, bail out if RH has been resumed. Use - * the spinlock to properly synchronize with possible pending - * RH suspend or resume activity. - */ - spin_lock_irqsave(&ohci->lock, flags); - if (ohci->rh_state != OHCI_RH_SUSPENDED) { - rc = -EINVAL; - goto bail; - } - - clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + rc = ohci_suspend(hcd, do_wakeup); + if (rc) + return rc; s3c2410_stop_hc(pdev); -bail: - spin_unlock_irqrestore(&ohci->lock, flags); return rc; } diff --git a/drivers/usb/host/ohci-sa1111.c b/drivers/usb/host/ohci-sa1111.c index aa9e127..2ac266d 100644 --- a/drivers/usb/host/ohci-sa1111.c +++ b/drivers/usb/host/ohci-sa1111.c @@ -211,8 +211,10 @@ static int ohci_hcd_sa1111_probe(struct sa1111_dev *dev) goto err2; ret = usb_add_hcd(hcd, dev->irq[1], 0); - if (ret == 0) + if (ret == 0) { + device_wakeup_enable(hcd->self.controller); return ret; + } sa1111_stop_hc(dev); err2: diff --git a/drivers/usb/host/ohci-sm501.c b/drivers/usb/host/ohci-sm501.c index 2a5de5f..4e81c80 100644 --- a/drivers/usb/host/ohci-sm501.c +++ b/drivers/usb/host/ohci-sm501.c @@ -168,6 +168,7 @@ static int ohci_hcd_sm501_drv_probe(struct platform_device *pdev) retval = usb_add_hcd(hcd, irq, IRQF_SHARED); if (retval) goto err5; + device_wakeup_enable(hcd->self.controller); /* enable power and unmask interrupts */ diff --git a/drivers/usb/host/ohci-spear.c b/drivers/usb/host/ohci-spear.c index 6b02107..8b29a0c 100644 --- a/drivers/usb/host/ohci-spear.c +++ b/drivers/usb/host/ohci-spear.c @@ -81,17 +81,10 @@ static int spear_ohci_hcd_drv_probe(struct platform_device *pdev) hcd->rsrc_start = pdev->resource[0].start; hcd->rsrc_len = resource_size(res); - if (!devm_request_mem_region(&pdev->dev, hcd->rsrc_start, hcd->rsrc_len, - hcd_name)) { - dev_dbg(&pdev->dev, "request_mem_region failed\n"); - retval = -EBUSY; - goto err_put_hcd; - } - hcd->regs = devm_ioremap(&pdev->dev, hcd->rsrc_start, hcd->rsrc_len); - if (!hcd->regs) { - dev_dbg(&pdev->dev, "ioremap failed\n"); - retval = -ENOMEM; + hcd->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(hcd->regs)) { + retval = PTR_ERR(hcd->regs); goto err_put_hcd; } @@ -103,8 +96,10 @@ static int spear_ohci_hcd_drv_probe(struct platform_device *pdev) ohci = hcd_to_ohci(hcd); retval = usb_add_hcd(hcd, platform_get_irq(pdev, 0), 0); - if (retval == 0) + if (retval == 0) { + device_wakeup_enable(hcd->self.controller); return retval; + } clk_disable_unprepare(sohci_p->clk); err_put_hcd: @@ -129,20 +124,26 @@ static int spear_ohci_hcd_drv_remove(struct platform_device *pdev) } #if defined(CONFIG_PM) -static int spear_ohci_hcd_drv_suspend(struct platform_device *dev, +static int spear_ohci_hcd_drv_suspend(struct platform_device *pdev, pm_message_t message) { - struct usb_hcd *hcd = platform_get_drvdata(dev); + struct usb_hcd *hcd = platform_get_drvdata(pdev); struct ohci_hcd *ohci = hcd_to_ohci(hcd); struct spear_ohci *sohci_p = to_spear_ohci(hcd); + bool do_wakeup = device_may_wakeup(&pdev->dev); + int ret; if (time_before(jiffies, ohci->next_statechange)) msleep(5); ohci->next_statechange = jiffies; + ret = ohci_suspend(hcd, do_wakeup); + if (ret) + return ret; + clk_disable_unprepare(sohci_p->clk); - return 0; + return ret; } static int spear_ohci_hcd_drv_resume(struct platform_device *dev) diff --git a/drivers/usb/host/ohci-tilegx.c b/drivers/usb/host/ohci-tilegx.c index 22540ab..0b183e0 100644 --- a/drivers/usb/host/ohci-tilegx.c +++ b/drivers/usb/host/ohci-tilegx.c @@ -159,6 +159,7 @@ static int ohci_hcd_tilegx_drv_probe(struct platform_device *pdev) ret = usb_add_hcd(hcd, pdata->irq, IRQF_SHARED); if (ret == 0) { platform_set_drvdata(pdev, hcd); + device_wakeup_enable(hcd->self.controller); return ret; } diff --git a/drivers/usb/host/ohci-tmio.c b/drivers/usb/host/ohci-tmio.c index ecb09a5..bb40958 100644 --- a/drivers/usb/host/ohci-tmio.c +++ b/drivers/usb/host/ohci-tmio.c @@ -27,7 +27,6 @@ /*#include <linux/fs.h> #include <linux/mount.h> #include <linux/pagemap.h> -#include <linux/init.h> #include <linux/namei.h> #include <linux/sched.h>*/ #include <linux/platform_device.h> @@ -250,6 +249,7 @@ static int ohci_hcd_tmio_drv_probe(struct platform_device *dev) if (ret) goto err_add_hcd; + device_wakeup_enable(hcd->self.controller); if (ret == 0) return ret; diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index e2e5faa..9250cad 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -415,12 +415,11 @@ struct ohci_hcd { struct ed *ed_to_check; unsigned zf_delay; -#ifdef DEBUG struct dentry *debug_dir; struct dentry *debug_async; struct dentry *debug_periodic; struct dentry *debug_registers; -#endif + /* platform-specific data -- must come last */ unsigned long priv[0] __aligned(sizeof(s64)); @@ -474,10 +473,6 @@ static inline struct usb_hcd *ohci_to_hcd (const struct ohci_hcd *ohci) /*-------------------------------------------------------------------------*/ -#ifndef DEBUG -#define STUB_DEBUG_FILES -#endif /* DEBUG */ - #define ohci_dbg(ohci, fmt, args...) \ dev_dbg (ohci_to_hcd(ohci)->self.controller , fmt , ## args ) #define ohci_err(ohci, fmt, args...) \ @@ -487,12 +482,6 @@ static inline struct usb_hcd *ohci_to_hcd (const struct ohci_hcd *ohci) #define ohci_warn(ohci, fmt, args...) \ dev_warn (ohci_to_hcd(ohci)->self.controller , fmt , ## args ) -#ifdef OHCI_VERBOSE_DEBUG -# define ohci_vdbg ohci_dbg -#else -# define ohci_vdbg(ohci, fmt, args...) do { } while (0) -#endif - /*-------------------------------------------------------------------------*/ /* diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c index 4a6df2d..e07248b 100644 --- a/drivers/usb/host/oxu210hp-hcd.c +++ b/drivers/usb/host/oxu210hp-hcd.c @@ -29,7 +29,6 @@ #include <linux/sched.h> #include <linux/slab.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/timer.h> #include <linux/list.h> #include <linux/interrupt.h> @@ -60,6 +59,10 @@ #define oxu_info(oxu, fmt, args...) \ dev_info(oxu_to_hcd(oxu)->self.controller , fmt , ## args) +#ifdef CONFIG_DYNAMIC_DEBUG +#define DEBUG +#endif + static inline struct usb_hcd *oxu_to_hcd(struct oxu_hcd *oxu) { return container_of((void *) oxu, struct usb_hcd, hcd_priv); @@ -3747,6 +3750,7 @@ static struct usb_hcd *oxu_create(struct platform_device *pdev, if (ret < 0) return ERR_PTR(ret); + device_wakeup_enable(hcd->self.controller); return hcd; } diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index dfbdd3a..00661d3 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -12,7 +12,6 @@ #include <linux/kconfig.h> #include <linux/kernel.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/delay.h> #include <linux/export.h> #include <linux/acpi.h> diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index 2ad004a..110b4b9 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -27,7 +27,6 @@ #include <linux/kernel.h> #include <linux/sched.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/timer.h> #include <linux/delay.h> #include <linux/list.h> @@ -95,7 +94,7 @@ static int r8a66597_clock_enable(struct r8a66597 *r8a66597) int i = 0; if (r8a66597->pdata->on_chip) { - clk_enable(r8a66597->clk); + clk_prepare_enable(r8a66597->clk); do { r8a66597_write(r8a66597, SCKE, SYSCFG0); tmp = r8a66597_read(r8a66597, SYSCFG0); @@ -139,7 +138,7 @@ static void r8a66597_clock_disable(struct r8a66597 *r8a66597) udelay(1); if (r8a66597->pdata->on_chip) { - clk_disable(r8a66597->clk); + clk_disable_unprepare(r8a66597->clk); } else { r8a66597_bclr(r8a66597, PLLC, SYSCFG0); r8a66597_bclr(r8a66597, XCKE, SYSCFG0); @@ -2514,6 +2513,7 @@ static int r8a66597_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Failed to add hcd\n"); goto clean_up3; } + device_wakeup_enable(hcd->self.controller); return 0; @@ -2534,7 +2534,7 @@ static struct platform_driver r8a66597_driver = { .probe = r8a66597_probe, .remove = r8a66597_remove, .driver = { - .name = (char *) hcd_name, + .name = hcd_name, .owner = THIS_MODULE, .pm = R8A66597_DEV_PM_OPS, }, diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index 79620c3..a517151 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -39,7 +39,6 @@ #include <linux/sched.h> #include <linux/slab.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/timer.h> #include <linux/list.h> #include <linux/interrupt.h> @@ -1732,6 +1731,8 @@ sl811h_probe(struct platform_device *dev) if (retval != 0) goto err6; + device_wakeup_enable(hcd->self.controller); + create_debug_file(sl811); return retval; diff --git a/drivers/usb/host/sl811_cs.c b/drivers/usb/host/sl811_cs.c index 469564e..88a9bff 100644 --- a/drivers/usb/host/sl811_cs.c +++ b/drivers/usb/host/sl811_cs.c @@ -12,7 +12,6 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/string.h> diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c index e402beb..c067175 100644 --- a/drivers/usb/host/u132-hcd.c +++ b/drivers/usb/host/u132-hcd.c @@ -3133,6 +3133,7 @@ static int u132_probe(struct platform_device *pdev) u132_u132_put_kref(u132); return retval; } else { + device_wakeup_enable(hcd->self.controller); u132_monitor_queue_work(u132, 100); return 0; } @@ -3217,7 +3218,7 @@ static struct platform_driver u132_platform_driver = { .suspend = u132_suspend, .resume = u132_resume, .driver = { - .name = (char *)hcd_name, + .name = hcd_name, .owner = THIS_MODULE, }, }; diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c index 8e239cd..1b28a00 100644 --- a/drivers/usb/host/uhci-debug.c +++ b/drivers/usb/host/uhci-debug.c @@ -20,7 +20,7 @@ static struct dentry *uhci_debugfs_root; -#ifdef DEBUG +#ifdef CONFIG_DYNAMIC_DEBUG /* Handle REALLY large printks so we don't overflow buffers */ static void lprintk(char *buf) @@ -635,7 +635,7 @@ static const struct file_operations uhci_debug_operations = { #endif /* CONFIG_DEBUG_FS */ -#else /* DEBUG */ +#else /* CONFIG_DYNAMIC_DEBUG*/ static inline void lprintk(char *buf) {} diff --git a/drivers/usb/host/uhci-grlib.c b/drivers/usb/host/uhci-grlib.c index 53c23ff..ab25dc3 100644 --- a/drivers/usb/host/uhci-grlib.c +++ b/drivers/usb/host/uhci-grlib.c @@ -141,6 +141,7 @@ static int uhci_hcd_grlib_probe(struct platform_device *op) if (rv) goto err_uhci; + device_wakeup_enable(hcd->self.controller); return 0; err_uhci: diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 4a86b63..27f35e8 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -69,18 +69,21 @@ MODULE_PARM_DESC(ignore_oc, "ignore hardware overcurrent indications"); * show all queues in /sys/kernel/debug/uhci/[pci_addr] * debug = 3, show all TDs in URBs when dumping */ -#ifdef DEBUG -#define DEBUG_CONFIGURED 1 +#ifdef CONFIG_DYNAMIC_DEBUG + static int debug = 1; module_param(debug, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(debug, "Debug level"); +static char *errbuf; #else -#define DEBUG_CONFIGURED 0 -#define debug 0 + +#define debug 0 +#define errbuf NULL + #endif -static char *errbuf; + #define ERRBUF_LEN (32 * 1024) static struct kmem_cache *uhci_up_cachep; /* urb_priv */ @@ -516,13 +519,12 @@ static void release_uhci(struct uhci_hcd *uhci) { int i; - if (DEBUG_CONFIGURED) { - spin_lock_irq(&uhci->lock); - uhci->is_initialized = 0; - spin_unlock_irq(&uhci->lock); - debugfs_remove(uhci->dentry); - } + spin_lock_irq(&uhci->lock); + uhci->is_initialized = 0; + spin_unlock_irq(&uhci->lock); + + debugfs_remove(uhci->dentry); for (i = 0; i < UHCI_NUM_SKELQH; i++) uhci_free_qh(uhci, uhci->skelqh[i]); @@ -868,14 +870,14 @@ static int __init uhci_hcd_init(void) ignore_oc ? ", overcurrent ignored" : ""); set_bit(USB_UHCI_LOADED, &usb_hcds_loaded); - if (DEBUG_CONFIGURED) { - errbuf = kmalloc(ERRBUF_LEN, GFP_KERNEL); - if (!errbuf) - goto errbuf_failed; - uhci_debugfs_root = debugfs_create_dir("uhci", usb_debug_root); - if (!uhci_debugfs_root) - goto debug_failed; - } +#ifdef CONFIG_DYNAMIC_DEBUG + errbuf = kmalloc(ERRBUF_LEN, GFP_KERNEL); + if (!errbuf) + goto errbuf_failed; + uhci_debugfs_root = debugfs_create_dir("uhci", usb_debug_root); + if (!uhci_debugfs_root) + goto debug_failed; +#endif uhci_up_cachep = kmem_cache_create("uhci_urb_priv", sizeof(struct urb_priv), 0, 0, NULL); @@ -906,12 +908,14 @@ clean0: kmem_cache_destroy(uhci_up_cachep); up_failed: +#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG) debugfs_remove(uhci_debugfs_root); debug_failed: kfree(errbuf); errbuf_failed: +#endif clear_bit(USB_UHCI_LOADED, &usb_hcds_loaded); return retval; @@ -927,7 +931,9 @@ static void __exit uhci_hcd_cleanup(void) #endif kmem_cache_destroy(uhci_up_cachep); debugfs_remove(uhci_debugfs_root); +#ifdef CONFIG_DYNAMIC_DEBUG kfree(errbuf); +#endif clear_bit(USB_UHCI_LOADED, &usb_hcds_loaded); } diff --git a/drivers/usb/host/uhci-pci.c b/drivers/usb/host/uhci-pci.c index 4cd7988..940304c 100644 --- a/drivers/usb/host/uhci-pci.c +++ b/drivers/usb/host/uhci-pci.c @@ -279,7 +279,7 @@ static const struct hc_driver uhci_driver = { .hub_control = uhci_hub_control, }; -static DEFINE_PCI_DEVICE_TABLE(uhci_pci_ids) = { { +static const struct pci_device_id uhci_pci_ids[] = { { /* handle any USB UHCI controller */ PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_UHCI, ~0), .driver_data = (unsigned long) &uhci_driver, diff --git a/drivers/usb/host/uhci-platform.c b/drivers/usb/host/uhci-platform.c index 3003fef..44e6c9d 100644 --- a/drivers/usb/host/uhci-platform.c +++ b/drivers/usb/host/uhci-platform.c @@ -108,6 +108,7 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev) if (ret) goto err_uhci; + device_wakeup_enable(hcd->self.controller); return 0; err_uhci: diff --git a/drivers/usb/host/whci/hcd.c b/drivers/usb/host/whci/hcd.c index 1b0888f..d7b363a 100644 --- a/drivers/usb/host/whci/hcd.c +++ b/drivers/usb/host/whci/hcd.c @@ -293,6 +293,7 @@ static int whc_probe(struct umc_dev *umc) dev_err(dev, "cannot add HCD: %d\n", ret); goto error_usb_add_hcd; } + device_wakeup_enable(usb_hcd->self.controller); ret = wusbhc_b_create(wusbhc); if (ret) { diff --git a/drivers/usb/host/whci/int.c b/drivers/usb/host/whci/int.c index 6aae700..0c086b2 100644 --- a/drivers/usb/host/whci/int.c +++ b/drivers/usb/host/whci/int.c @@ -16,7 +16,6 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/uwb/umc.h> #include "../../wusbcore/wusbhc.h" diff --git a/drivers/usb/host/whci/wusb.c b/drivers/usb/host/whci/wusb.c index f24efde..8d27626 100644 --- a/drivers/usb/host/whci/wusb.c +++ b/drivers/usb/host/whci/wusb.c @@ -16,7 +16,6 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/uwb/umc.h> #include "../../wusbcore/wusbhc.h" diff --git a/drivers/usb/host/xhci-dbg.c b/drivers/usb/host/xhci-dbg.c index 73503a8..eb009a4 100644 --- a/drivers/usb/host/xhci-dbg.c +++ b/drivers/usb/host/xhci-dbg.c @@ -32,7 +32,7 @@ void xhci_dbg_regs(struct xhci_hcd *xhci) xhci_dbg(xhci, "// xHCI capability registers at %p:\n", xhci->cap_regs); - temp = xhci_readl(xhci, &xhci->cap_regs->hc_capbase); + temp = readl(&xhci->cap_regs->hc_capbase); xhci_dbg(xhci, "// @%p = 0x%x (CAPLENGTH AND HCIVERSION)\n", &xhci->cap_regs->hc_capbase, temp); xhci_dbg(xhci, "// CAPLENGTH: 0x%x\n", @@ -44,13 +44,13 @@ void xhci_dbg_regs(struct xhci_hcd *xhci) xhci_dbg(xhci, "// xHCI operational registers at %p:\n", xhci->op_regs); - temp = xhci_readl(xhci, &xhci->cap_regs->run_regs_off); + temp = readl(&xhci->cap_regs->run_regs_off); xhci_dbg(xhci, "// @%p = 0x%x RTSOFF\n", &xhci->cap_regs->run_regs_off, (unsigned int) temp & RTSOFF_MASK); xhci_dbg(xhci, "// xHCI runtime registers at %p:\n", xhci->run_regs); - temp = xhci_readl(xhci, &xhci->cap_regs->db_off); + temp = readl(&xhci->cap_regs->db_off); xhci_dbg(xhci, "// @%p = 0x%x DBOFF\n", &xhci->cap_regs->db_off, temp); xhci_dbg(xhci, "// Doorbell array at %p:\n", xhci->dba); } @@ -61,7 +61,7 @@ static void xhci_print_cap_regs(struct xhci_hcd *xhci) xhci_dbg(xhci, "xHCI capability registers at %p:\n", xhci->cap_regs); - temp = xhci_readl(xhci, &xhci->cap_regs->hc_capbase); + temp = readl(&xhci->cap_regs->hc_capbase); xhci_dbg(xhci, "CAPLENGTH AND HCIVERSION 0x%x:\n", (unsigned int) temp); xhci_dbg(xhci, "CAPLENGTH: 0x%x\n", @@ -69,7 +69,7 @@ static void xhci_print_cap_regs(struct xhci_hcd *xhci) xhci_dbg(xhci, "HCIVERSION: 0x%x\n", (unsigned int) HC_VERSION(temp)); - temp = xhci_readl(xhci, &xhci->cap_regs->hcs_params1); + temp = readl(&xhci->cap_regs->hcs_params1); xhci_dbg(xhci, "HCSPARAMS 1: 0x%x\n", (unsigned int) temp); xhci_dbg(xhci, " Max device slots: %u\n", @@ -79,7 +79,7 @@ static void xhci_print_cap_regs(struct xhci_hcd *xhci) xhci_dbg(xhci, " Max ports: %u\n", (unsigned int) HCS_MAX_PORTS(temp)); - temp = xhci_readl(xhci, &xhci->cap_regs->hcs_params2); + temp = readl(&xhci->cap_regs->hcs_params2); xhci_dbg(xhci, "HCSPARAMS 2: 0x%x\n", (unsigned int) temp); xhci_dbg(xhci, " Isoc scheduling threshold: %u\n", @@ -87,7 +87,7 @@ static void xhci_print_cap_regs(struct xhci_hcd *xhci) xhci_dbg(xhci, " Maximum allowed segments in event ring: %u\n", (unsigned int) HCS_ERST_MAX(temp)); - temp = xhci_readl(xhci, &xhci->cap_regs->hcs_params3); + temp = readl(&xhci->cap_regs->hcs_params3); xhci_dbg(xhci, "HCSPARAMS 3 0x%x:\n", (unsigned int) temp); xhci_dbg(xhci, " Worst case U1 device exit latency: %u\n", @@ -95,14 +95,14 @@ static void xhci_print_cap_regs(struct xhci_hcd *xhci) xhci_dbg(xhci, " Worst case U2 device exit latency: %u\n", (unsigned int) HCS_U2_LATENCY(temp)); - temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params); + temp = readl(&xhci->cap_regs->hcc_params); xhci_dbg(xhci, "HCC PARAMS 0x%x:\n", (unsigned int) temp); xhci_dbg(xhci, " HC generates %s bit addresses\n", HCC_64BIT_ADDR(temp) ? "64" : "32"); /* FIXME */ xhci_dbg(xhci, " FIXME: more HCCPARAMS debugging\n"); - temp = xhci_readl(xhci, &xhci->cap_regs->run_regs_off); + temp = readl(&xhci->cap_regs->run_regs_off); xhci_dbg(xhci, "RTSOFF 0x%x:\n", temp & RTSOFF_MASK); } @@ -110,7 +110,7 @@ static void xhci_print_command_reg(struct xhci_hcd *xhci) { u32 temp; - temp = xhci_readl(xhci, &xhci->op_regs->command); + temp = readl(&xhci->op_regs->command); xhci_dbg(xhci, "USBCMD 0x%x:\n", temp); xhci_dbg(xhci, " HC is %s\n", (temp & CMD_RUN) ? "running" : "being stopped"); @@ -128,7 +128,7 @@ static void xhci_print_status(struct xhci_hcd *xhci) { u32 temp; - temp = xhci_readl(xhci, &xhci->op_regs->status); + temp = readl(&xhci->op_regs->status); xhci_dbg(xhci, "USBSTS 0x%x:\n", temp); xhci_dbg(xhci, " Event ring is %sempty\n", (temp & STS_EINT) ? "not " : ""); @@ -163,7 +163,7 @@ static void xhci_print_ports(struct xhci_hcd *xhci) for (j = 0; j < NUM_PORT_REGS; ++j) { xhci_dbg(xhci, "%p port %s reg = 0x%x\n", addr, names[j], - (unsigned int) xhci_readl(xhci, addr)); + (unsigned int) readl(addr)); addr++; } } @@ -177,7 +177,7 @@ void xhci_print_ir_set(struct xhci_hcd *xhci, int set_num) u64 temp_64; addr = &ir_set->irq_pending; - temp = xhci_readl(xhci, addr); + temp = readl(addr); if (temp == XHCI_INIT_VALUE) return; @@ -187,17 +187,17 @@ void xhci_print_ir_set(struct xhci_hcd *xhci, int set_num) (unsigned int)temp); addr = &ir_set->irq_control; - temp = xhci_readl(xhci, addr); + temp = readl(addr); xhci_dbg(xhci, " %p: ir_set.control = 0x%x\n", addr, (unsigned int)temp); addr = &ir_set->erst_size; - temp = xhci_readl(xhci, addr); + temp = readl(addr); xhci_dbg(xhci, " %p: ir_set.erst_size = 0x%x\n", addr, (unsigned int)temp); addr = &ir_set->rsvd; - temp = xhci_readl(xhci, addr); + temp = readl(addr); if (temp != XHCI_INIT_VALUE) xhci_dbg(xhci, " WARN: %p: ir_set.rsvd = 0x%x\n", addr, (unsigned int)temp); @@ -219,12 +219,12 @@ void xhci_print_run_regs(struct xhci_hcd *xhci) int i; xhci_dbg(xhci, "xHCI runtime registers at %p:\n", xhci->run_regs); - temp = xhci_readl(xhci, &xhci->run_regs->microframe_index); + temp = readl(&xhci->run_regs->microframe_index); xhci_dbg(xhci, " %p: Microframe index = 0x%x\n", &xhci->run_regs->microframe_index, (unsigned int) temp); for (i = 0; i < 7; ++i) { - temp = xhci_readl(xhci, &xhci->run_regs->rsvd[i]); + temp = readl(&xhci->run_regs->rsvd[i]); if (temp != XHCI_INIT_VALUE) xhci_dbg(xhci, " WARN: %p: Rsvd[%i] = 0x%x\n", &xhci->run_regs->rsvd[i], diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 805f234..9992fbf 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -94,7 +94,7 @@ static void xhci_usb2_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci, */ memset(port_removable, 0, sizeof(port_removable)); for (i = 0; i < ports; i++) { - portsc = xhci_readl(xhci, xhci->usb2_ports[i]); + portsc = readl(xhci->usb2_ports[i]); /* If a device is removable, PORTSC reports a 0, same as in the * hub descriptor DeviceRemovable bits. */ @@ -148,7 +148,7 @@ static void xhci_usb3_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci, port_removable = 0; /* bit 0 is reserved, bit 1 is for port 1, etc. */ for (i = 0; i < ports; i++) { - portsc = xhci_readl(xhci, xhci->usb3_ports[i]); + portsc = readl(xhci->usb3_ports[i]); if (portsc & PORT_DEV_REMOVE) port_removable |= 1 << (i + 1); } @@ -342,8 +342,8 @@ static void xhci_disable_port(struct usb_hcd *hcd, struct xhci_hcd *xhci, } /* Write 1 to disable the port */ - xhci_writel(xhci, port_status | PORT_PE, addr); - port_status = xhci_readl(xhci, addr); + writel(port_status | PORT_PE, addr); + port_status = readl(addr); xhci_dbg(xhci, "disable port, actual port %d status = 0x%x\n", wIndex, port_status); } @@ -388,8 +388,8 @@ static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue, return; } /* Change bits are all write 1 to clear */ - xhci_writel(xhci, port_status | status, addr); - port_status = xhci_readl(xhci, addr); + writel(port_status | status, addr); + port_status = readl(addr); xhci_dbg(xhci, "clear port %s change, actual port %d status = 0x%x\n", port_change_bit, wIndex, port_status); } @@ -415,11 +415,11 @@ void xhci_set_link_state(struct xhci_hcd *xhci, __le32 __iomem **port_array, { u32 temp; - temp = xhci_readl(xhci, port_array[port_id]); + temp = readl(port_array[port_id]); temp = xhci_port_state_to_neutral(temp); temp &= ~PORT_PLS_MASK; temp |= PORT_LINK_STROBE | link_state; - xhci_writel(xhci, temp, port_array[port_id]); + writel(temp, port_array[port_id]); } static void xhci_set_remote_wake_mask(struct xhci_hcd *xhci, @@ -427,7 +427,7 @@ static void xhci_set_remote_wake_mask(struct xhci_hcd *xhci, { u32 temp; - temp = xhci_readl(xhci, port_array[port_id]); + temp = readl(port_array[port_id]); temp = xhci_port_state_to_neutral(temp); if (wake_mask & USB_PORT_FEAT_REMOTE_WAKE_CONNECT) @@ -445,7 +445,7 @@ static void xhci_set_remote_wake_mask(struct xhci_hcd *xhci, else temp &= ~PORT_WKOC_E; - xhci_writel(xhci, temp, port_array[port_id]); + writel(temp, port_array[port_id]); } /* Test and clear port RWC bit */ @@ -454,11 +454,11 @@ void xhci_test_and_clear_bit(struct xhci_hcd *xhci, __le32 __iomem **port_array, { u32 temp; - temp = xhci_readl(xhci, port_array[port_id]); + temp = readl(port_array[port_id]); if (temp & port_bit) { temp = xhci_port_state_to_neutral(temp); temp |= port_bit; - xhci_writel(xhci, temp, port_array[port_id]); + writel(temp, port_array[port_id]); } } @@ -623,8 +623,7 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, } xhci_ring_device(xhci, slot_id); } else { - int port_status = xhci_readl(xhci, - port_array[wIndex]); + int port_status = readl(port_array[wIndex]); xhci_warn(xhci, "Port resume took longer than %i msec, port status = 0x%x\n", XHCI_MAX_REXIT_TIMEOUT, port_status); @@ -733,12 +732,12 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, /* Set the U1 and U2 exit latencies. */ memcpy(buf, &usb_bos_descriptor, USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE); - temp = xhci_readl(xhci, &xhci->cap_regs->hcs_params3); + temp = readl(&xhci->cap_regs->hcs_params3); buf[12] = HCS_U1_LATENCY(temp); put_unaligned_le16(HCS_U2_LATENCY(temp), &buf[13]); /* Indicate whether the host has LTM support. */ - temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params); + temp = readl(&xhci->cap_regs->hcc_params); if (HCC_LTC(temp)) buf[8] |= USB_LTM_SUPPORT; @@ -748,7 +747,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, if (!wIndex || wIndex > max_ports) goto error; wIndex--; - temp = xhci_readl(xhci, port_array[wIndex]); + temp = readl(port_array[wIndex]); if (temp == 0xffffffff) { retval = -ENODEV; break; @@ -775,7 +774,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, if (!wIndex || wIndex > max_ports) goto error; wIndex--; - temp = xhci_readl(xhci, port_array[wIndex]); + temp = readl(port_array[wIndex]); if (temp == 0xffffffff) { retval = -ENODEV; break; @@ -784,7 +783,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, /* FIXME: What new port features do we need to support? */ switch (wValue) { case USB_PORT_FEAT_SUSPEND: - temp = xhci_readl(xhci, port_array[wIndex]); + temp = readl(port_array[wIndex]); if ((temp & PORT_PLS_MASK) != XDEV_U0) { /* Resume the port to U0 first */ xhci_set_link_state(xhci, port_array, wIndex, @@ -797,7 +796,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, * a port unless the port reports that it is in the * enabled (PED = ‘1’,PLS < ‘3’) state. */ - temp = xhci_readl(xhci, port_array[wIndex]); + temp = readl(port_array[wIndex]); if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) || (temp & PORT_PLS_MASK) >= XDEV_U3) { xhci_warn(xhci, "USB core suspending device " @@ -822,11 +821,11 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, msleep(10); /* wait device to enter */ spin_lock_irqsave(&xhci->lock, flags); - temp = xhci_readl(xhci, port_array[wIndex]); + temp = readl(port_array[wIndex]); bus_state->suspended_ports |= 1 << wIndex; break; case USB_PORT_FEAT_LINK_STATE: - temp = xhci_readl(xhci, port_array[wIndex]); + temp = readl(port_array[wIndex]); /* Disable port */ if (link_state == USB_SS_PORT_LS_SS_DISABLED) { @@ -839,9 +838,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, temp |= PORT_CSC | PORT_PEC | PORT_WRC | PORT_OCC | PORT_RC | PORT_PLC | PORT_CEC; - xhci_writel(xhci, temp | PORT_PE, - port_array[wIndex]); - temp = xhci_readl(xhci, port_array[wIndex]); + writel(temp | PORT_PE, port_array[wIndex]); + temp = readl(port_array[wIndex]); break; } @@ -850,7 +848,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, xhci_dbg(xhci, "Enable port %d\n", wIndex); xhci_set_link_state(xhci, port_array, wIndex, link_state); - temp = xhci_readl(xhci, port_array[wIndex]); + temp = readl(port_array[wIndex]); break; } @@ -884,7 +882,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, msleep(20); /* wait device to enter */ spin_lock_irqsave(&xhci->lock, flags); - temp = xhci_readl(xhci, port_array[wIndex]); + temp = readl(port_array[wIndex]); if (link_state == USB_SS_PORT_LS_U3) bus_state->suspended_ports |= 1 << wIndex; break; @@ -895,10 +893,9 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, * However, khubd will ignore the roothub events until * the roothub is registered. */ - xhci_writel(xhci, temp | PORT_POWER, - port_array[wIndex]); + writel(temp | PORT_POWER, port_array[wIndex]); - temp = xhci_readl(xhci, port_array[wIndex]); + temp = readl(port_array[wIndex]); xhci_dbg(xhci, "set port power, actual port %d status = 0x%x\n", wIndex, temp); spin_unlock_irqrestore(&xhci->lock, flags); @@ -911,52 +908,52 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, break; case USB_PORT_FEAT_RESET: temp = (temp | PORT_RESET); - xhci_writel(xhci, temp, port_array[wIndex]); + writel(temp, port_array[wIndex]); - temp = xhci_readl(xhci, port_array[wIndex]); + temp = readl(port_array[wIndex]); xhci_dbg(xhci, "set port reset, actual port %d status = 0x%x\n", wIndex, temp); break; case USB_PORT_FEAT_REMOTE_WAKE_MASK: xhci_set_remote_wake_mask(xhci, port_array, wIndex, wake_mask); - temp = xhci_readl(xhci, port_array[wIndex]); + temp = readl(port_array[wIndex]); xhci_dbg(xhci, "set port remote wake mask, " "actual port %d status = 0x%x\n", wIndex, temp); break; case USB_PORT_FEAT_BH_PORT_RESET: temp |= PORT_WR; - xhci_writel(xhci, temp, port_array[wIndex]); + writel(temp, port_array[wIndex]); - temp = xhci_readl(xhci, port_array[wIndex]); + temp = readl(port_array[wIndex]); break; case USB_PORT_FEAT_U1_TIMEOUT: if (hcd->speed != HCD_USB3) goto error; - temp = xhci_readl(xhci, port_array[wIndex] + PORTPMSC); + temp = readl(port_array[wIndex] + PORTPMSC); temp &= ~PORT_U1_TIMEOUT_MASK; temp |= PORT_U1_TIMEOUT(timeout); - xhci_writel(xhci, temp, port_array[wIndex] + PORTPMSC); + writel(temp, port_array[wIndex] + PORTPMSC); break; case USB_PORT_FEAT_U2_TIMEOUT: if (hcd->speed != HCD_USB3) goto error; - temp = xhci_readl(xhci, port_array[wIndex] + PORTPMSC); + temp = readl(port_array[wIndex] + PORTPMSC); temp &= ~PORT_U2_TIMEOUT_MASK; temp |= PORT_U2_TIMEOUT(timeout); - xhci_writel(xhci, temp, port_array[wIndex] + PORTPMSC); + writel(temp, port_array[wIndex] + PORTPMSC); break; default: goto error; } /* unblock any posted writes */ - temp = xhci_readl(xhci, port_array[wIndex]); + temp = readl(port_array[wIndex]); break; case ClearPortFeature: if (!wIndex || wIndex > max_ports) goto error; wIndex--; - temp = xhci_readl(xhci, port_array[wIndex]); + temp = readl(port_array[wIndex]); if (temp == 0xffffffff) { retval = -ENODEV; break; @@ -965,7 +962,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, temp = xhci_port_state_to_neutral(temp); switch (wValue) { case USB_PORT_FEAT_SUSPEND: - temp = xhci_readl(xhci, port_array[wIndex]); + temp = readl(port_array[wIndex]); xhci_dbg(xhci, "clear USB_PORT_FEAT_SUSPEND\n"); xhci_dbg(xhci, "PORTSC %04x\n", temp); if (temp & PORT_RESET) @@ -1008,8 +1005,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, port_array[wIndex], temp); break; case USB_PORT_FEAT_POWER: - xhci_writel(xhci, temp & ~PORT_POWER, - port_array[wIndex]); + writel(temp & ~PORT_POWER, port_array[wIndex]); spin_unlock_irqrestore(&xhci->lock, flags); temp = usb_acpi_power_manageable(hcd->self.root_hub, @@ -1070,7 +1066,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) spin_lock_irqsave(&xhci->lock, flags); /* For each port, did anything change? If so, set that bit in buf. */ for (i = 0; i < max_ports; i++) { - temp = xhci_readl(xhci, port_array[i]); + temp = readl(port_array[i]); if (temp == 0xffffffff) { retval = -ENODEV; break; @@ -1124,7 +1120,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd) u32 t1, t2; int slot_id; - t1 = xhci_readl(xhci, port_array[port_index]); + t1 = readl(port_array[port_index]); t2 = xhci_port_state_to_neutral(t1); if ((t1 & PORT_PE) && !(t1 & PORT_PLS_MASK)) { @@ -1157,7 +1153,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd) t1 = xhci_port_state_to_neutral(t1); if (t1 != t2) - xhci_writel(xhci, t2, port_array[port_index]); + writel(t2, port_array[port_index]); } hcd->state = HC_STATE_SUSPENDED; bus_state->next_statechange = jiffies + msecs_to_jiffies(10); @@ -1187,9 +1183,9 @@ int xhci_bus_resume(struct usb_hcd *hcd) } /* delay the irqs */ - temp = xhci_readl(xhci, &xhci->op_regs->command); + temp = readl(&xhci->op_regs->command); temp &= ~CMD_EIE; - xhci_writel(xhci, temp, &xhci->op_regs->command); + writel(temp, &xhci->op_regs->command); port_index = max_ports; while (port_index--) { @@ -1198,7 +1194,7 @@ int xhci_bus_resume(struct usb_hcd *hcd) u32 temp; int slot_id; - temp = xhci_readl(xhci, port_array[port_index]); + temp = readl(port_array[port_index]); if (DEV_SUPERSPEED(temp)) temp &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS); else @@ -1235,17 +1231,17 @@ int xhci_bus_resume(struct usb_hcd *hcd) if (slot_id) xhci_ring_device(xhci, slot_id); } else - xhci_writel(xhci, temp, port_array[port_index]); + writel(temp, port_array[port_index]); } - (void) xhci_readl(xhci, &xhci->op_regs->command); + (void) readl(&xhci->op_regs->command); bus_state->next_statechange = jiffies + msecs_to_jiffies(5); /* re-enable irqs */ - temp = xhci_readl(xhci, &xhci->op_regs->command); + temp = readl(&xhci->op_regs->command); temp |= CMD_EIE; - xhci_writel(xhci, temp, &xhci->op_regs->command); - temp = xhci_readl(xhci, &xhci->op_regs->command); + writel(temp, &xhci->op_regs->command); + temp = readl(&xhci->op_regs->command); spin_unlock_irqrestore(&xhci->lock, flags); return 0; diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 49b8bd0..bce4391 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -57,7 +57,7 @@ static struct xhci_segment *xhci_segment_alloc(struct xhci_hcd *xhci, /* If the cycle state is 0, set the cycle bit to 1 for all the TRBs */ if (cycle_state == 0) { for (i = 0; i < TRBS_PER_SEGMENT; i++) - seg->trbs[i].link.control |= TRB_CYCLE; + seg->trbs[i].link.control |= cpu_to_le32(TRB_CYCLE); } seg->dma = dma; seg->next = NULL; @@ -308,7 +308,8 @@ static void xhci_reinit_cached_ring(struct xhci_hcd *xhci, sizeof(union xhci_trb)*TRBS_PER_SEGMENT); if (cycle_state == 0) { for (i = 0; i < TRBS_PER_SEGMENT; i++) - seg->trbs[i].link.control |= TRB_CYCLE; + seg->trbs[i].link.control |= + cpu_to_le32(TRB_CYCLE); } /* All endpoint rings have link TRBs */ xhci_link_segments(xhci, seg, seg->next, type); @@ -432,10 +433,10 @@ static void xhci_free_stream_ctx(struct xhci_hcd *xhci, unsigned int num_stream_ctxs, struct xhci_stream_ctx *stream_ctx, dma_addr_t dma) { - struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); + struct device *dev = xhci_to_hcd(xhci)->self.controller; if (num_stream_ctxs > MEDIUM_STREAM_ARRAY_SIZE) - dma_free_coherent(&pdev->dev, + dma_free_coherent(dev, sizeof(struct xhci_stream_ctx)*num_stream_ctxs, stream_ctx, dma); else if (num_stream_ctxs <= SMALL_STREAM_ARRAY_SIZE) @@ -460,10 +461,10 @@ static struct xhci_stream_ctx *xhci_alloc_stream_ctx(struct xhci_hcd *xhci, unsigned int num_stream_ctxs, dma_addr_t *dma, gfp_t mem_flags) { - struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); + struct device *dev = xhci_to_hcd(xhci)->self.controller; if (num_stream_ctxs > MEDIUM_STREAM_ARRAY_SIZE) - return dma_alloc_coherent(&pdev->dev, + return dma_alloc_coherent(dev, sizeof(struct xhci_stream_ctx)*num_stream_ctxs, dma, mem_flags); else if (num_stream_ctxs <= SMALL_STREAM_ARRAY_SIZE) @@ -721,8 +722,7 @@ void xhci_free_stream_info(struct xhci_hcd *xhci, stream_info->stream_ctx_array, stream_info->ctx_array_dma); - if (stream_info) - kfree(stream_info->stream_rings); + kfree(stream_info->stream_rings); kfree(stream_info); } @@ -1616,7 +1616,7 @@ static void scratchpad_free(struct xhci_hcd *xhci) { int num_sp; int i; - struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); + struct device *dev = xhci_to_hcd(xhci)->self.controller; if (!xhci->scratchpad) return; @@ -1624,13 +1624,13 @@ static void scratchpad_free(struct xhci_hcd *xhci) num_sp = HCS_MAX_SCRATCHPAD(xhci->hcs_params2); for (i = 0; i < num_sp; i++) { - dma_free_coherent(&pdev->dev, xhci->page_size, + dma_free_coherent(dev, xhci->page_size, xhci->scratchpad->sp_buffers[i], xhci->scratchpad->sp_dma_buffers[i]); } kfree(xhci->scratchpad->sp_dma_buffers); kfree(xhci->scratchpad->sp_buffers); - dma_free_coherent(&pdev->dev, num_sp * sizeof(u64), + dma_free_coherent(dev, num_sp * sizeof(u64), xhci->scratchpad->sp_array, xhci->scratchpad->sp_dma); kfree(xhci->scratchpad); @@ -1692,7 +1692,7 @@ void xhci_free_command(struct xhci_hcd *xhci, void xhci_mem_cleanup(struct xhci_hcd *xhci) { - struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); + struct device *dev = xhci_to_hcd(xhci)->self.controller; struct xhci_cd *cur_cd, *next_cd; int size; int i, j, num_ports; @@ -1700,7 +1700,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) /* Free the Event Ring Segment Table and the actual Event Ring */ size = sizeof(struct xhci_erst_entry)*(xhci->erst.num_entries); if (xhci->erst.entries) - dma_free_coherent(&pdev->dev, size, + dma_free_coherent(dev, size, xhci->erst.entries, xhci->erst.erst_dma_addr); xhci->erst.entries = NULL; xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed ERST"); @@ -1748,7 +1748,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) "Freed medium stream array pool"); if (xhci->dcbaa) - dma_free_coherent(&pdev->dev, sizeof(*xhci->dcbaa), + dma_free_coherent(dev, sizeof(*xhci->dcbaa), xhci->dcbaa, xhci->dcbaa->dma); xhci->dcbaa = NULL; @@ -1986,7 +1986,7 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, } /* Port offset and count in the third dword, see section 7.2 */ - temp = xhci_readl(xhci, addr + 2); + temp = readl(addr + 2); port_offset = XHCI_EXT_PORT_OFF(temp); port_count = XHCI_EXT_PORT_COUNT(temp); xhci_dbg_trace(xhci, trace_xhci_dbg_init, @@ -2069,7 +2069,7 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) int cap_count = 0; addr = &xhci->cap_regs->hcc_params; - offset = XHCI_HCC_EXT_CAPS(xhci_readl(xhci, addr)); + offset = XHCI_HCC_EXT_CAPS(readl(addr)); if (offset == 0) { xhci_err(xhci, "No Extended Capability registers, " "unable to set up roothub.\n"); @@ -2106,7 +2106,7 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) /* count extended protocol capability entries for later caching */ do { u32 cap_id; - cap_id = xhci_readl(xhci, tmp_addr); + cap_id = readl(tmp_addr); if (XHCI_EXT_CAPS_ID(cap_id) == XHCI_EXT_CAPS_PROTOCOL) cap_count++; tmp_offset = XHCI_EXT_CAPS_NEXT(cap_id); @@ -2120,7 +2120,7 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) while (1) { u32 cap_id; - cap_id = xhci_readl(xhci, addr); + cap_id = readl(addr); if (XHCI_EXT_CAPS_ID(cap_id) == XHCI_EXT_CAPS_PROTOCOL) xhci_add_in_port(xhci, num_ports, addr, (u8) XHCI_EXT_PORT_MAJOR(cap_id), @@ -2224,7 +2224,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) INIT_LIST_HEAD(&xhci->cancel_cmd_list); - page_size = xhci_readl(xhci, &xhci->op_regs->page_size); + page_size = readl(&xhci->op_regs->page_size); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Supported page size register = 0x%x", page_size); for (i = 0; i < 16; i++) { @@ -2247,14 +2247,14 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) * Program the Number of Device Slots Enabled field in the CONFIG * register with the max value of slots the HC can handle. */ - val = HCS_MAX_SLOTS(xhci_readl(xhci, &xhci->cap_regs->hcs_params1)); + val = HCS_MAX_SLOTS(readl(&xhci->cap_regs->hcs_params1)); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// xHC can handle at most %d device slots.", val); - val2 = xhci_readl(xhci, &xhci->op_regs->config_reg); + val2 = readl(&xhci->op_regs->config_reg); val |= (val2 & ~HCS_SLOTS_MASK); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Setting Max device slots reg = 0x%x.", val); - xhci_writel(xhci, val, &xhci->op_regs->config_reg); + writel(val, &xhci->op_regs->config_reg); /* * Section 5.4.8 - doorbell array must be @@ -2331,7 +2331,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) */ xhci->cmd_ring_reserved_trbs++; - val = xhci_readl(xhci, &xhci->cap_regs->db_off); + val = readl(&xhci->cap_regs->db_off); val &= DBOFF_MASK; xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Doorbell array is located at offset 0x%x" @@ -2382,13 +2382,13 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) } /* set ERST count with the number of entries in the segment table */ - val = xhci_readl(xhci, &xhci->ir_set->erst_size); + val = readl(&xhci->ir_set->erst_size); val &= ERST_SIZE_MASK; val |= ERST_NUM_SEGS; xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Write ERST size = %i to ir_set 0 (some bits preserved)", val); - xhci_writel(xhci, val, &xhci->ir_set->erst_size); + writel(val, &xhci->ir_set->erst_size); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Set ERST entries to point to event ring."); @@ -2431,10 +2431,10 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) * is necessary for allowing USB 3.0 devices to do remote wakeup from * U3 (device suspend). */ - temp = xhci_readl(xhci, &xhci->op_regs->dev_notification); + temp = readl(&xhci->op_regs->dev_notification); temp &= ~DEV_NOTE_MASK; temp |= DEV_NOTE_FWAKE; - xhci_writel(xhci, temp, &xhci->op_regs->dev_notification); + writel(temp, &xhci->op_regs->dev_notification); return 0; diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index b8dffd5..04f986d 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -128,7 +128,12 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) * any other sleep) on Haswell machines with LPT and LPT-LP * with the new Intel BIOS */ - xhci->quirks |= XHCI_SPURIOUS_WAKEUP; + /* Limit the quirk to only known vendors, as this triggers + * yet another BIOS bug on some other machines + * https://bugzilla.kernel.org/show_bug.cgi?id=66171 + */ + if (pdev->subsystem_vendor == PCI_VENDOR_ID_HP) + xhci->quirks |= XHCI_SPURIOUS_WAKEUP; } if (pdev->vendor == PCI_VENDOR_ID_ETRON && pdev->device == PCI_DEVICE_ID_ASROCK_P67) { @@ -137,6 +142,11 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) "QUIRK: Resetting on resume"); xhci->quirks |= XHCI_TRUST_TX_LENGTH; } + if (pdev->vendor == PCI_VENDOR_ID_RENESAS && + pdev->device == 0x0015 && + pdev->subsystem_vendor == PCI_VENDOR_ID_SAMSUNG && + pdev->subsystem_device == 0xc0cd) + xhci->quirks |= XHCI_RESET_ON_RESUME; if (pdev->vendor == PCI_VENDOR_ID_VIA) xhci->quirks |= XHCI_RESET_ON_RESUME; } @@ -331,6 +341,7 @@ static const struct hc_driver xhci_pci_hc_driver = { .check_bandwidth = xhci_check_bandwidth, .reset_bandwidth = xhci_reset_bandwidth, .address_device = xhci_address_device, + .enable_device = xhci_enable_device, .update_hub_device = xhci_update_hub_device, .reset_device = xhci_discover_or_reset_device, diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index d9c169f..8abda5c 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -69,6 +69,7 @@ static const struct hc_driver xhci_plat_xhci_driver = { .check_bandwidth = xhci_check_bandwidth, .reset_bandwidth = xhci_reset_bandwidth, .address_device = xhci_address_device, + .enable_device = xhci_enable_device, .update_hub_device = xhci_update_hub_device, .reset_device = xhci_discover_or_reset_device, @@ -139,6 +140,7 @@ static int xhci_plat_probe(struct platform_device *pdev) ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) goto unmap_registers; + device_wakeup_enable(hcd->self.controller); /* USB 2.0 roothub is stored in the platform_device now. */ hcd = platform_get_drvdata(pdev); diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 53c2e29..0ed64eb 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -156,8 +156,6 @@ static void next_trb(struct xhci_hcd *xhci, */ static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring) { - unsigned long long addr; - ring->deq_updates++; /* @@ -186,8 +184,6 @@ static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring) ring->dequeue++; } } while (last_trb(xhci, ring, ring->deq_seg, ring->dequeue)); - - addr = (unsigned long long) xhci_trb_virt_to_dma(ring->deq_seg, ring->dequeue); } /* @@ -212,7 +208,6 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring, { u32 chain; union xhci_trb *next; - unsigned long long addr; chain = le32_to_cpu(ring->enqueue->generic.field[3]) & TRB_CHAIN; /* If this is not event ring, there is one less usable TRB */ @@ -264,7 +259,6 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring, ring->enqueue = ring->enq_seg->trbs; next = ring->enqueue; } - addr = (unsigned long long) xhci_trb_virt_to_dma(ring->enq_seg, ring->enqueue); } /* @@ -295,9 +289,9 @@ void xhci_ring_cmd_db(struct xhci_hcd *xhci) return; xhci_dbg(xhci, "// Ding dong!\n"); - xhci_writel(xhci, DB_VALUE_HOST, &xhci->dba->doorbell[0]); + writel(DB_VALUE_HOST, &xhci->dba->doorbell[0]); /* Flush PCI posted writes */ - xhci_readl(xhci, &xhci->dba->doorbell[0]); + readl(&xhci->dba->doorbell[0]); } static int xhci_abort_cmd_ring(struct xhci_hcd *xhci) @@ -427,7 +421,7 @@ void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, if ((ep_state & EP_HALT_PENDING) || (ep_state & SET_DEQ_PENDING) || (ep_state & EP_HALTED)) return; - xhci_writel(xhci, DB_VALUE(ep_index, stream_id), db_addr); + writel(DB_VALUE(ep_index, stream_id), db_addr); /* The CPU has better things to do at this point than wait for a * write-posting flush. It'll get there soon enough. */ @@ -1655,7 +1649,7 @@ static void handle_device_notification(struct xhci_hcd *xhci, u32 slot_id; struct usb_device *udev; - slot_id = TRB_TO_SLOT_ID(event->generic.field[3]); + slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->generic.field[3])); if (!xhci->devs[slot_id]) { xhci_warn(xhci, "Device Notification event for " "unused slot %u\n", slot_id); @@ -1739,7 +1733,7 @@ static void handle_port_status(struct xhci_hcd *xhci, faked_port_index = find_faked_portnum_from_hw_portnum(hcd, xhci, port_id); - temp = xhci_readl(xhci, port_array[faked_port_index]); + temp = readl(port_array[faked_port_index]); if (hcd->state == HC_STATE_SUSPENDED) { xhci_dbg(xhci, "resume root hub\n"); usb_hcd_resume_root_hub(hcd); @@ -1748,7 +1742,7 @@ static void handle_port_status(struct xhci_hcd *xhci, if ((temp & PORT_PLC) && (temp & PORT_PLS_MASK) == XDEV_RESUME) { xhci_dbg(xhci, "port resume event for port %d\n", port_id); - temp1 = xhci_readl(xhci, &xhci->op_regs->command); + temp1 = readl(&xhci->op_regs->command); if (!(temp1 & CMD_RUN)) { xhci_warn(xhci, "xHC is not running.\n"); goto cleanup; @@ -2831,7 +2825,7 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) spin_lock(&xhci->lock); /* Check if the xHC generated the interrupt, or the irq is shared */ - status = xhci_readl(xhci, &xhci->op_regs->status); + status = readl(&xhci->op_regs->status); if (status == 0xffffffff) goto hw_died; @@ -2853,16 +2847,16 @@ hw_died: * Write 1 to clear the interrupt status. */ status |= STS_EINT; - xhci_writel(xhci, status, &xhci->op_regs->status); + writel(status, &xhci->op_regs->status); /* FIXME when MSI-X is supported and there are multiple vectors */ /* Clear the MSI-X event interrupt status */ if (hcd->irq) { u32 irq_pending; /* Acknowledge the PCI interrupt */ - irq_pending = xhci_readl(xhci, &xhci->ir_set->irq_pending); + irq_pending = readl(&xhci->ir_set->irq_pending); irq_pending |= IMAN_IP; - xhci_writel(xhci, irq_pending, &xhci->ir_set->irq_pending); + writel(irq_pending, &xhci->ir_set->irq_pending); } if (xhci->xhc_state & XHCI_STATE_DYING) { @@ -2973,58 +2967,8 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring, } while (1) { - if (room_on_ring(xhci, ep_ring, num_trbs)) { - union xhci_trb *trb = ep_ring->enqueue; - unsigned int usable = ep_ring->enq_seg->trbs + - TRBS_PER_SEGMENT - 1 - trb; - u32 nop_cmd; - - /* - * Section 4.11.7.1 TD Fragments states that a link - * TRB must only occur at the boundary between - * data bursts (eg 512 bytes for 480M). - * While it is possible to split a large fragment - * we don't know the size yet. - * Simplest solution is to fill the trb before the - * LINK with nop commands. - */ - if (num_trbs == 1 || num_trbs <= usable || usable == 0) - break; - - if (ep_ring->type != TYPE_BULK) - /* - * While isoc transfers might have a buffer that - * crosses a 64k boundary it is unlikely. - * Since we can't add NOPs without generating - * gaps in the traffic just hope it never - * happens at the end of the ring. - * This could be fixed by writing a LINK TRB - * instead of the first NOP - however the - * TRB_TYPE_LINK_LE32() calls would all need - * changing to check the ring length. - */ - break; - - if (num_trbs >= TRBS_PER_SEGMENT) { - xhci_err(xhci, "Too many fragments %d, max %d\n", - num_trbs, TRBS_PER_SEGMENT - 1); - return -ENOMEM; - } - - nop_cmd = cpu_to_le32(TRB_TYPE(TRB_TR_NOOP) | - ep_ring->cycle_state); - ep_ring->num_trbs_free -= usable; - do { - trb->generic.field[0] = 0; - trb->generic.field[1] = 0; - trb->generic.field[2] = 0; - trb->generic.field[3] = nop_cmd; - trb++; - } while (--usable); - ep_ring->enqueue = trb; - if (room_on_ring(xhci, ep_ring, num_trbs)) - break; - } + if (room_on_ring(xhci, ep_ring, num_trbs)) + break; if (ep_ring == xhci->cmd_ring) { xhci_err(xhci, "Do not support expand command ring\n"); @@ -3981,7 +3925,7 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags, if (ret) return ret; - start_frame = xhci_readl(xhci, &xhci->run_regs->microframe_index); + start_frame = readl(&xhci->run_regs->microframe_index); start_frame &= 0x3fff; urb->start_frame = start_frame; @@ -4056,12 +4000,12 @@ int xhci_queue_slot_control(struct xhci_hcd *xhci, u32 trb_type, u32 slot_id) /* Queue an address device command TRB */ int xhci_queue_address_device(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr, - u32 slot_id) + u32 slot_id, enum xhci_setup_dev setup) { return queue_command(xhci, lower_32_bits(in_ctx_ptr), upper_32_bits(in_ctx_ptr), 0, - TRB_TYPE(TRB_ADDR_DEV) | SLOT_ID_FOR_TRB(slot_id), - false); + TRB_TYPE(TRB_ADDR_DEV) | SLOT_ID_FOR_TRB(slot_id) + | (setup == SETUP_CONTEXT_ONLY ? TRB_BSR : 0), false); } int xhci_queue_vendor_command(struct xhci_hcd *xhci, diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h index 20364cc..dde3959 100644 --- a/drivers/usb/host/xhci-trace.h +++ b/drivers/usb/host/xhci-trace.h @@ -116,12 +116,12 @@ DECLARE_EVENT_CLASS(xhci_log_event, __field(u64, dma) __field(u32, status) __field(u32, flags) - __dynamic_array(__le32, trb, 4) + __dynamic_array(u8, trb, sizeof(struct xhci_generic_trb)) ), TP_fast_assign( __entry->va = trb_va; - __entry->dma = le64_to_cpu(((u64)ev->field[1]) << 32 | - ev->field[0]); + __entry->dma = ((u64)le32_to_cpu(ev->field[1])) << 32 | + le32_to_cpu(ev->field[0]); __entry->status = le32_to_cpu(ev->field[2]); __entry->flags = le32_to_cpu(ev->field[3]); memcpy(__get_dynamic_array(trb), trb_va, diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 4265b48..6fe577d 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -40,6 +40,10 @@ static int link_quirk; module_param(link_quirk, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(link_quirk, "Don't clear the chain bit on a link TRB"); +static unsigned int quirks; +module_param(quirks, uint, S_IRUGO); +MODULE_PARM_DESC(quirks, "Bit flags for quirks to be enabled as default"); + /* TODO: copied from ehci-hcd.c - can this be refactored? */ /* * xhci_handshake - spin reading hc until handshake completes or fails @@ -60,7 +64,7 @@ int xhci_handshake(struct xhci_hcd *xhci, void __iomem *ptr, u32 result; do { - result = xhci_readl(xhci, ptr); + result = readl(ptr); if (result == ~(u32)0) /* card removed */ return -ENODEV; result &= mask; @@ -82,13 +86,13 @@ void xhci_quiesce(struct xhci_hcd *xhci) u32 mask; mask = ~(XHCI_IRQS); - halted = xhci_readl(xhci, &xhci->op_regs->status) & STS_HALT; + halted = readl(&xhci->op_regs->status) & STS_HALT; if (!halted) mask &= ~CMD_RUN; - cmd = xhci_readl(xhci, &xhci->op_regs->command); + cmd = readl(&xhci->op_regs->command); cmd &= mask; - xhci_writel(xhci, cmd, &xhci->op_regs->command); + writel(cmd, &xhci->op_regs->command); } /* @@ -124,11 +128,11 @@ static int xhci_start(struct xhci_hcd *xhci) u32 temp; int ret; - temp = xhci_readl(xhci, &xhci->op_regs->command); + temp = readl(&xhci->op_regs->command); temp |= (CMD_RUN); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Turn on HC, cmd = 0x%x.", temp); - xhci_writel(xhci, temp, &xhci->op_regs->command); + writel(temp, &xhci->op_regs->command); /* * Wait for the HCHalted Status bit to be 0 to indicate the host is @@ -158,16 +162,16 @@ int xhci_reset(struct xhci_hcd *xhci) u32 state; int ret, i; - state = xhci_readl(xhci, &xhci->op_regs->status); + state = readl(&xhci->op_regs->status); if ((state & STS_HALT) == 0) { xhci_warn(xhci, "Host controller not halted, aborting reset.\n"); return 0; } xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Reset the HC"); - command = xhci_readl(xhci, &xhci->op_regs->command); + command = readl(&xhci->op_regs->command); command |= CMD_RESET; - xhci_writel(xhci, command, &xhci->op_regs->command); + writel(command, &xhci->op_regs->command); ret = xhci_handshake(xhci, &xhci->op_regs->command, CMD_RESET, 0, 10 * 1000 * 1000); @@ -321,6 +325,9 @@ static void xhci_cleanup_msix(struct xhci_hcd *xhci) struct usb_hcd *hcd = xhci_to_hcd(xhci); struct pci_dev *pdev = to_pci_dev(hcd->self.controller); + if (xhci->quirks & XHCI_PLAT) + return; + xhci_free_irq(xhci); if (xhci->msix_entries) { @@ -422,7 +429,7 @@ static void compliance_mode_recovery(unsigned long arg) xhci = (struct xhci_hcd *)arg; for (i = 0; i < xhci->num_usb3_ports; i++) { - temp = xhci_readl(xhci, xhci->usb3_ports[i]); + temp = readl(xhci->usb3_ports[i]); if ((temp & PORT_PLS_MASK) == USB_SS_PORT_LS_COMP_MOD) { /* * Compliance Mode Detected. Letting USB Core @@ -611,24 +618,23 @@ int xhci_run(struct usb_hcd *hcd) xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Set the interrupt modulation register"); - temp = xhci_readl(xhci, &xhci->ir_set->irq_control); + temp = readl(&xhci->ir_set->irq_control); temp &= ~ER_IRQ_INTERVAL_MASK; temp |= (u32) 160; - xhci_writel(xhci, temp, &xhci->ir_set->irq_control); + writel(temp, &xhci->ir_set->irq_control); /* Set the HCD state before we enable the irqs */ - temp = xhci_readl(xhci, &xhci->op_regs->command); + temp = readl(&xhci->op_regs->command); temp |= (CMD_EIE); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Enable interrupts, cmd = 0x%x.", temp); - xhci_writel(xhci, temp, &xhci->op_regs->command); + writel(temp, &xhci->op_regs->command); - temp = xhci_readl(xhci, &xhci->ir_set->irq_pending); + temp = readl(&xhci->ir_set->irq_pending); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Enabling event ring interrupter %p by writing 0x%x to irq_pending", xhci->ir_set, (unsigned int) ER_IRQ_ENABLE(temp)); - xhci_writel(xhci, ER_IRQ_ENABLE(temp), - &xhci->ir_set->irq_pending); + writel(ER_IRQ_ENABLE(temp), &xhci->ir_set->irq_pending); xhci_print_ir_set(xhci, 0); if (xhci->quirks & XHCI_NEC_HOST) @@ -698,18 +704,17 @@ void xhci_stop(struct usb_hcd *hcd) xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Disabling event ring interrupts"); - temp = xhci_readl(xhci, &xhci->op_regs->status); - xhci_writel(xhci, temp & ~STS_EINT, &xhci->op_regs->status); - temp = xhci_readl(xhci, &xhci->ir_set->irq_pending); - xhci_writel(xhci, ER_IRQ_DISABLE(temp), - &xhci->ir_set->irq_pending); + temp = readl(&xhci->op_regs->status); + writel(temp & ~STS_EINT, &xhci->op_regs->status); + temp = readl(&xhci->ir_set->irq_pending); + writel(ER_IRQ_DISABLE(temp), &xhci->ir_set->irq_pending); xhci_print_ir_set(xhci, 0); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "cleaning up memory"); xhci_mem_cleanup(xhci); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "xhci_stop completed - status = %x", - xhci_readl(xhci, &xhci->op_regs->status)); + readl(&xhci->op_regs->status)); } /* @@ -739,7 +744,7 @@ void xhci_shutdown(struct usb_hcd *hcd) xhci_dbg_trace(xhci, trace_xhci_dbg_init, "xhci_shutdown completed - status = %x", - xhci_readl(xhci, &xhci->op_regs->status)); + readl(&xhci->op_regs->status)); /* Yet another workaround for spurious wakeups at shutdown with HSW */ if (xhci->quirks & XHCI_SPURIOUS_WAKEUP) @@ -749,28 +754,28 @@ void xhci_shutdown(struct usb_hcd *hcd) #ifdef CONFIG_PM static void xhci_save_registers(struct xhci_hcd *xhci) { - xhci->s3.command = xhci_readl(xhci, &xhci->op_regs->command); - xhci->s3.dev_nt = xhci_readl(xhci, &xhci->op_regs->dev_notification); + xhci->s3.command = readl(&xhci->op_regs->command); + xhci->s3.dev_nt = readl(&xhci->op_regs->dev_notification); xhci->s3.dcbaa_ptr = xhci_read_64(xhci, &xhci->op_regs->dcbaa_ptr); - xhci->s3.config_reg = xhci_readl(xhci, &xhci->op_regs->config_reg); - xhci->s3.erst_size = xhci_readl(xhci, &xhci->ir_set->erst_size); + xhci->s3.config_reg = readl(&xhci->op_regs->config_reg); + xhci->s3.erst_size = readl(&xhci->ir_set->erst_size); xhci->s3.erst_base = xhci_read_64(xhci, &xhci->ir_set->erst_base); xhci->s3.erst_dequeue = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); - xhci->s3.irq_pending = xhci_readl(xhci, &xhci->ir_set->irq_pending); - xhci->s3.irq_control = xhci_readl(xhci, &xhci->ir_set->irq_control); + xhci->s3.irq_pending = readl(&xhci->ir_set->irq_pending); + xhci->s3.irq_control = readl(&xhci->ir_set->irq_control); } static void xhci_restore_registers(struct xhci_hcd *xhci) { - xhci_writel(xhci, xhci->s3.command, &xhci->op_regs->command); - xhci_writel(xhci, xhci->s3.dev_nt, &xhci->op_regs->dev_notification); + writel(xhci->s3.command, &xhci->op_regs->command); + writel(xhci->s3.dev_nt, &xhci->op_regs->dev_notification); xhci_write_64(xhci, xhci->s3.dcbaa_ptr, &xhci->op_regs->dcbaa_ptr); - xhci_writel(xhci, xhci->s3.config_reg, &xhci->op_regs->config_reg); - xhci_writel(xhci, xhci->s3.erst_size, &xhci->ir_set->erst_size); + writel(xhci->s3.config_reg, &xhci->op_regs->config_reg); + writel(xhci->s3.erst_size, &xhci->ir_set->erst_size); xhci_write_64(xhci, xhci->s3.erst_base, &xhci->ir_set->erst_base); xhci_write_64(xhci, xhci->s3.erst_dequeue, &xhci->ir_set->erst_dequeue); - xhci_writel(xhci, xhci->s3.irq_pending, &xhci->ir_set->irq_pending); - xhci_writel(xhci, xhci->s3.irq_control, &xhci->ir_set->irq_control); + writel(xhci->s3.irq_pending, &xhci->ir_set->irq_pending); + writel(xhci->s3.irq_control, &xhci->ir_set->irq_control); } static void xhci_set_cmd_ring_deq(struct xhci_hcd *xhci) @@ -866,9 +871,9 @@ int xhci_suspend(struct xhci_hcd *xhci) /* skipped assuming that port suspend has done */ /* step 2: clear Run/Stop bit */ - command = xhci_readl(xhci, &xhci->op_regs->command); + command = readl(&xhci->op_regs->command); command &= ~CMD_RUN; - xhci_writel(xhci, command, &xhci->op_regs->command); + writel(command, &xhci->op_regs->command); /* Some chips from Fresco Logic need an extraordinary delay */ delay *= (xhci->quirks & XHCI_SLOW_SUSPEND) ? 10 : 1; @@ -885,9 +890,9 @@ int xhci_suspend(struct xhci_hcd *xhci) xhci_save_registers(xhci); /* step 4: set CSS flag */ - command = xhci_readl(xhci, &xhci->op_regs->command); + command = readl(&xhci->op_regs->command); command |= CMD_CSS; - xhci_writel(xhci, command, &xhci->op_regs->command); + writel(command, &xhci->op_regs->command); if (xhci_handshake(xhci, &xhci->op_regs->status, STS_SAVE, 0, 10 * 1000)) { xhci_warn(xhci, "WARN: xHC save state timeout\n"); @@ -951,16 +956,16 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) xhci_set_cmd_ring_deq(xhci); /* step 3: restore state and start state*/ /* step 3: set CRS flag */ - command = xhci_readl(xhci, &xhci->op_regs->command); + command = readl(&xhci->op_regs->command); command |= CMD_CRS; - xhci_writel(xhci, command, &xhci->op_regs->command); + writel(command, &xhci->op_regs->command); if (xhci_handshake(xhci, &xhci->op_regs->status, STS_RESTORE, 0, 10 * 1000)) { xhci_warn(xhci, "WARN: xHC restore state timeout\n"); spin_unlock_irq(&xhci->lock); return -ETIMEDOUT; } - temp = xhci_readl(xhci, &xhci->op_regs->status); + temp = readl(&xhci->op_regs->status); } /* If restore operation fails, re-initialize the HC during resume */ @@ -984,17 +989,16 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) xhci_cleanup_msix(xhci); xhci_dbg(xhci, "// Disabling event ring interrupts\n"); - temp = xhci_readl(xhci, &xhci->op_regs->status); - xhci_writel(xhci, temp & ~STS_EINT, &xhci->op_regs->status); - temp = xhci_readl(xhci, &xhci->ir_set->irq_pending); - xhci_writel(xhci, ER_IRQ_DISABLE(temp), - &xhci->ir_set->irq_pending); + temp = readl(&xhci->op_regs->status); + writel(temp & ~STS_EINT, &xhci->op_regs->status); + temp = readl(&xhci->ir_set->irq_pending); + writel(ER_IRQ_DISABLE(temp), &xhci->ir_set->irq_pending); xhci_print_ir_set(xhci, 0); xhci_dbg(xhci, "cleaning up memory\n"); xhci_mem_cleanup(xhci); xhci_dbg(xhci, "xhci_stop completed - status = %x\n", - xhci_readl(xhci, &xhci->op_regs->status)); + readl(&xhci->op_regs->status)); /* USB core calls the PCI reinit and start functions twice: * first with the primary HCD, and then with the secondary HCD. @@ -1023,9 +1027,9 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) } /* step 4: set Run/Stop bit */ - command = xhci_readl(xhci, &xhci->op_regs->command); + command = readl(&xhci->op_regs->command); command |= CMD_RUN; - xhci_writel(xhci, command, &xhci->op_regs->command); + writel(command, &xhci->op_regs->command); xhci_handshake(xhci, &xhci->op_regs->status, STS_HALT, 0, 250 * 1000); @@ -1464,7 +1468,7 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) ret = usb_hcd_check_unlink_urb(hcd, urb, status); if (ret || !urb->hcpriv) goto done; - temp = xhci_readl(xhci, &xhci->op_regs->status); + temp = readl(&xhci->op_regs->status); if (temp == 0xffffffff || (xhci->xhc_state & XHCI_STATE_HALTED)) { xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, "HW died, freeing TD."); @@ -1892,8 +1896,8 @@ static u32 xhci_count_num_new_endpoints(struct xhci_hcd *xhci, * (bit 1). The default control endpoint is added during the Address * Device command and is never removed until the slot is disabled. */ - valid_add_flags = ctrl_ctx->add_flags >> 2; - valid_drop_flags = ctrl_ctx->drop_flags >> 2; + valid_add_flags = le32_to_cpu(ctrl_ctx->add_flags) >> 2; + valid_drop_flags = le32_to_cpu(ctrl_ctx->drop_flags) >> 2; /* Use hweight32 to count the number of ones in the add flags, or * number of endpoints added. Don't count endpoints that are changed @@ -1909,8 +1913,8 @@ static unsigned int xhci_count_num_dropped_endpoints(struct xhci_hcd *xhci, u32 valid_add_flags; u32 valid_drop_flags; - valid_add_flags = ctrl_ctx->add_flags >> 2; - valid_drop_flags = ctrl_ctx->drop_flags >> 2; + valid_add_flags = le32_to_cpu(ctrl_ctx->add_flags) >> 2; + valid_drop_flags = le32_to_cpu(ctrl_ctx->drop_flags) >> 2; return hweight32(valid_drop_flags) - hweight32(valid_add_flags & valid_drop_flags); @@ -3585,7 +3589,7 @@ void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev) spin_lock_irqsave(&xhci->lock, flags); /* Don't disable the slot if the host controller is dead. */ - state = xhci_readl(xhci, &xhci->op_regs->status); + state = readl(&xhci->op_regs->status); if (state == 0xffffffff || (xhci->xhc_state & XHCI_STATE_DYING) || (xhci->xhc_state & XHCI_STATE_HALTED)) { xhci_free_virt_device(xhci, udev->slot_id); @@ -3712,13 +3716,15 @@ disable_slot: } /* - * Issue an Address Device command (which will issue a SetAddress request to - * the device). + * Issue an Address Device command and optionally send a corresponding + * SetAddress request to the device. * We should be protected by the usb_address0_mutex in khubd's hub_port_init, so * we should only issue and wait on one address command at the same time. */ -int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) +static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, + enum xhci_setup_dev setup) { + const char *act = setup == SETUP_CONTEXT_ONLY ? "context" : "address"; unsigned long flags; int timeleft; struct xhci_virt_device *virt_dev; @@ -3771,12 +3777,12 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) xhci_dbg(xhci, "Slot ID %d Input Context:\n", udev->slot_id); xhci_dbg_ctx(xhci, virt_dev->in_ctx, 2); trace_xhci_address_ctx(xhci, virt_dev->in_ctx, - slot_ctx->dev_info >> 27); + le32_to_cpu(slot_ctx->dev_info) >> 27); spin_lock_irqsave(&xhci->lock, flags); cmd_trb = xhci_find_next_enqueue(xhci->cmd_ring); ret = xhci_queue_address_device(xhci, virt_dev->in_ctx->dma, - udev->slot_id); + udev->slot_id, setup); if (ret) { spin_unlock_irqrestore(&xhci->lock, flags); xhci_dbg_trace(xhci, trace_xhci_dbg_address, @@ -3794,8 +3800,8 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) * command on a timeout. */ if (timeleft <= 0) { - xhci_warn(xhci, "%s while waiting for address device command\n", - timeleft == 0 ? "Timeout" : "Signal"); + xhci_warn(xhci, "%s while waiting for setup %s command\n", + timeleft == 0 ? "Timeout" : "Signal", act); /* cancel the address device command */ ret = xhci_cancel_cmd(xhci, NULL, cmd_trb); if (ret < 0) @@ -3806,26 +3812,27 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) switch (virt_dev->cmd_status) { case COMP_CTX_STATE: case COMP_EBADSLT: - xhci_err(xhci, "Setup ERROR: address device command for slot %d.\n", - udev->slot_id); + xhci_err(xhci, "Setup ERROR: setup %s command for slot %d.\n", + act, udev->slot_id); ret = -EINVAL; break; case COMP_TX_ERR: - dev_warn(&udev->dev, "Device not responding to set address.\n"); + dev_warn(&udev->dev, "Device not responding to setup %s.\n", act); ret = -EPROTO; break; case COMP_DEV_ERR: - dev_warn(&udev->dev, "ERROR: Incompatible device for address " - "device command.\n"); + dev_warn(&udev->dev, + "ERROR: Incompatible device for setup %s command\n", act); ret = -ENODEV; break; case COMP_SUCCESS: xhci_dbg_trace(xhci, trace_xhci_dbg_address, - "Successful Address Device command"); + "Successful setup %s command", act); break; default: - xhci_err(xhci, "ERROR: unexpected command completion " - "code 0x%x.\n", virt_dev->cmd_status); + xhci_err(xhci, + "ERROR: unexpected setup %s command completion code 0x%x.\n", + act, virt_dev->cmd_status); xhci_dbg(xhci, "Slot ID %d Output Context:\n", udev->slot_id); xhci_dbg_ctx(xhci, virt_dev->out_ctx, 2); trace_xhci_address_ctx(xhci, virt_dev->out_ctx, 1); @@ -3850,7 +3857,7 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) xhci_dbg(xhci, "Slot ID %d Input Context:\n", udev->slot_id); xhci_dbg_ctx(xhci, virt_dev->in_ctx, 2); trace_xhci_address_ctx(xhci, virt_dev->in_ctx, - slot_ctx->dev_info >> 27); + le32_to_cpu(slot_ctx->dev_info) >> 27); xhci_dbg(xhci, "Slot ID %d Output Context:\n", udev->slot_id); xhci_dbg_ctx(xhci, virt_dev->out_ctx, 2); /* @@ -3859,7 +3866,7 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) */ slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->out_ctx); trace_xhci_address_ctx(xhci, virt_dev->out_ctx, - slot_ctx->dev_info >> 27); + le32_to_cpu(slot_ctx->dev_info) >> 27); /* Zero the input context control for later use */ ctrl_ctx->add_flags = 0; ctrl_ctx->drop_flags = 0; @@ -3871,6 +3878,16 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) return 0; } +int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) +{ + return xhci_setup_device(hcd, udev, SETUP_CONTEXT_ADDRESS); +} + +int xhci_enable_device(struct usb_hcd *hcd, struct usb_device *udev) +{ + return xhci_setup_device(hcd, udev, SETUP_CONTEXT_ONLY); +} + /* * Transfer the port index into real index in the HW port status * registers. Caculate offset between the port's PORTSC register @@ -4042,7 +4059,7 @@ int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, port_array = xhci->usb2_ports; port_num = udev->portnum - 1; pm_addr = port_array[port_num] + PORTPMSC; - pm_val = xhci_readl(xhci, pm_addr); + pm_val = readl(pm_addr); hlpm_addr = port_array[port_num] + PORTHLPMC; field = le32_to_cpu(udev->bos->ext_cap->bmAttributes); @@ -4082,26 +4099,26 @@ int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, spin_lock_irqsave(&xhci->lock, flags); hlpm_val = xhci_calculate_usb2_hw_lpm_params(udev); - xhci_writel(xhci, hlpm_val, hlpm_addr); + writel(hlpm_val, hlpm_addr); /* flush write */ - xhci_readl(xhci, hlpm_addr); + readl(hlpm_addr); } else { hird = xhci_calculate_hird_besl(xhci, udev); } pm_val &= ~PORT_HIRD_MASK; pm_val |= PORT_HIRD(hird) | PORT_RWE | PORT_L1DS(udev->slot_id); - xhci_writel(xhci, pm_val, pm_addr); - pm_val = xhci_readl(xhci, pm_addr); + writel(pm_val, pm_addr); + pm_val = readl(pm_addr); pm_val |= PORT_HLE; - xhci_writel(xhci, pm_val, pm_addr); + writel(pm_val, pm_addr); /* flush write */ - xhci_readl(xhci, pm_addr); + readl(pm_addr); } else { pm_val &= ~(PORT_HLE | PORT_RWE | PORT_HIRD_MASK | PORT_L1DS_MASK); - xhci_writel(xhci, pm_val, pm_addr); + writel(pm_val, pm_addr); /* flush write */ - xhci_readl(xhci, pm_addr); + readl(pm_addr); if (udev->usb2_hw_lpm_besl_capable) { spin_unlock_irqrestore(&xhci->lock, flags); mutex_lock(hcd->bandwidth_mutex); @@ -4455,7 +4472,7 @@ static u16 xhci_calculate_lpm_timeout(struct usb_hcd *hcd, if (!config) return timeout; - for (i = 0; i < USB_MAXINTERFACES; i++) { + for (i = 0; i < config->desc.bNumInterfaces; i++) { struct usb_driver *driver; struct usb_interface *intf = config->interface[i]; @@ -4704,7 +4721,7 @@ int xhci_get_frame(struct usb_hcd *hcd) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); /* EHCI mods by the periodic size. Why? */ - return xhci_readl(xhci, &xhci->run_regs->microframe_index) >> 3; + return readl(&xhci->run_regs->microframe_index) >> 3; } int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) @@ -4716,9 +4733,6 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) /* Accept arbitrarily long scatter-gather lists */ hcd->self.sg_tablesize = ~0; - /* support to build packet from discontinuous buffers */ - hcd->self.no_sg_constraint = 1; - /* XHCI controllers don't stop the ep queue on short packets :| */ hcd->self.no_stop_on_short = 1; @@ -4743,23 +4757,33 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) /* xHCI private pointer was set in xhci_pci_probe for the second * registered roothub. */ + xhci = hcd_to_xhci(hcd); + /* + * Support arbitrarily aligned sg-list entries on hosts without + * TD fragment rules (which are currently unsupported). + */ + if (xhci->hci_version < 0x100) + hcd->self.no_sg_constraint = 1; + return 0; } xhci->cap_regs = hcd->regs; xhci->op_regs = hcd->regs + - HC_LENGTH(xhci_readl(xhci, &xhci->cap_regs->hc_capbase)); + HC_LENGTH(readl(&xhci->cap_regs->hc_capbase)); xhci->run_regs = hcd->regs + - (xhci_readl(xhci, &xhci->cap_regs->run_regs_off) & RTSOFF_MASK); + (readl(&xhci->cap_regs->run_regs_off) & RTSOFF_MASK); /* Cache read-only capability registers */ - xhci->hcs_params1 = xhci_readl(xhci, &xhci->cap_regs->hcs_params1); - xhci->hcs_params2 = xhci_readl(xhci, &xhci->cap_regs->hcs_params2); - xhci->hcs_params3 = xhci_readl(xhci, &xhci->cap_regs->hcs_params3); - xhci->hcc_params = xhci_readl(xhci, &xhci->cap_regs->hc_capbase); + xhci->hcs_params1 = readl(&xhci->cap_regs->hcs_params1); + xhci->hcs_params2 = readl(&xhci->cap_regs->hcs_params2); + xhci->hcs_params3 = readl(&xhci->cap_regs->hcs_params3); + xhci->hcc_params = readl(&xhci->cap_regs->hc_capbase); xhci->hci_version = HC_VERSION(xhci->hcc_params); - xhci->hcc_params = xhci_readl(xhci, &xhci->cap_regs->hcc_params); + xhci->hcc_params = readl(&xhci->cap_regs->hcc_params); xhci_print_registers(xhci); + xhci->quirks = quirks; + get_quirks(dev, xhci); /* In xhci controllers which follow xhci 1.0 spec gives a spurious @@ -4769,6 +4793,9 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) if (xhci->hci_version > 0x96) xhci->quirks |= XHCI_SPURIOUS_SUCCESS; + if (xhci->hci_version < 0x100) + hcd->self.no_sg_constraint = 1; + /* Make sure the HC is halted. */ retval = xhci_halt(xhci); if (retval) diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 03c74b7..58ed9d0 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -752,7 +752,7 @@ struct xhci_stream_ctx { }; /* Stream Context Types (section 6.4.1) - bits 3:1 of stream ctx deq ptr */ -#define SCT_FOR_CTX(p) (((p) << 1) & 0x7) +#define SCT_FOR_CTX(p) (((p) & 0x7) << 1) /* Secondary stream array type, dequeue pointer is to a transfer ring */ #define SCT_SEC_TR 0 /* Primary stream array type, dequeue pointer is to a transfer ring */ @@ -1097,6 +1097,14 @@ struct xhci_event_cmd { }; /* flags bitmasks */ + +/* Address device - disable SetAddress */ +#define TRB_BSR (1<<9) +enum xhci_setup_dev { + SETUP_CONTEXT_ONLY, + SETUP_CONTEXT_ADDRESS, +}; + /* bits 16:23 are the virtual function ID */ /* bits 24:31 are the slot ID */ #define TRB_TO_SLOT_ID(p) (((p) & (0xff<<24)) >> 24) @@ -1595,19 +1603,6 @@ static inline struct usb_hcd *xhci_to_hcd(struct xhci_hcd *xhci) #define xhci_warn_ratelimited(xhci, fmt, args...) \ dev_warn_ratelimited(xhci_to_hcd(xhci)->self.controller , fmt , ## args) -/* TODO: copied from ehci.h - can be refactored? */ -/* xHCI spec says all registers are little endian */ -static inline unsigned int xhci_readl(const struct xhci_hcd *xhci, - __le32 __iomem *regs) -{ - return readl(regs); -} -static inline void xhci_writel(struct xhci_hcd *xhci, - const unsigned int val, __le32 __iomem *regs) -{ - writel(val, regs); -} - /* * Registers should always be accessed with double word or quad word accesses. * @@ -1790,6 +1785,7 @@ int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev, struct usb_host_endpoint **eps, unsigned int num_eps, gfp_t mem_flags); int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev); +int xhci_enable_device(struct usb_hcd *hcd, struct usb_device *udev); int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev); int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, struct usb_device *udev, int enable); @@ -1813,7 +1809,7 @@ int xhci_is_vendor_info_code(struct xhci_hcd *xhci, unsigned int trb_comp_code); void xhci_ring_cmd_db(struct xhci_hcd *xhci); int xhci_queue_slot_control(struct xhci_hcd *xhci, u32 trb_type, u32 slot_id); int xhci_queue_address_device(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr, - u32 slot_id); + u32 slot_id, enum xhci_setup_dev); int xhci_queue_vendor_command(struct xhci_hcd *xhci, u32 field1, u32 field2, u32 field3, u32 field4); int xhci_queue_stop_endpoint(struct xhci_hcd *xhci, int slot_id, diff --git a/drivers/usb/image/mdc800.c b/drivers/usb/image/mdc800.c index 7121b50..a62865a 100644 --- a/drivers/usb/image/mdc800.c +++ b/drivers/usb/image/mdc800.c @@ -51,7 +51,7 @@ * * version 0.7.3 * bugfix : The mdc800->state field gets set to READY after the - * the diconnect function sets it to NOT_CONNECTED. This makes the + * the disconnect function sets it to NOT_CONNECTED. This makes the * driver running like the camera is connected and causes some * hang ups. * diff --git a/drivers/usb/image/microtek.c b/drivers/usb/image/microtek.c index 9c0f8ca..37b44b0 100644 --- a/drivers/usb/image/microtek.c +++ b/drivers/usb/image/microtek.c @@ -125,7 +125,6 @@ #include <linux/errno.h> #include <linux/random.h> #include <linux/poll.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/usb.h> diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c index 3eaa83f..493c7f2 100644 --- a/drivers/usb/misc/adutux.c +++ b/drivers/usb/misc/adutux.c @@ -22,7 +22,6 @@ #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/usb.h> diff --git a/drivers/usb/misc/cypress_cy7c63.c b/drivers/usb/misc/cypress_cy7c63.c index 3f7c1a9..402b94d 100644 --- a/drivers/usb/misc/cypress_cy7c63.c +++ b/drivers/usb/misc/cypress_cy7c63.c @@ -29,7 +29,6 @@ * published by the Free Software Foundation, version 2. */ -#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/slab.h> diff --git a/drivers/usb/misc/cytherm.c b/drivers/usb/misc/cytherm.c index 5b9831b..9bab1a3 100644 --- a/drivers/usb/misc/cytherm.c +++ b/drivers/usb/misc/cytherm.c @@ -16,7 +16,6 @@ #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/usb.h> diff --git a/drivers/usb/misc/emi26.c b/drivers/usb/misc/emi26.c index d65984d..8950fa5 100644 --- a/drivers/usb/misc/emi26.c +++ b/drivers/usb/misc/emi26.c @@ -13,7 +13,6 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/usb.h> #include <linux/delay.h> #include <linux/firmware.h> diff --git a/drivers/usb/misc/emi62.c b/drivers/usb/misc/emi62.c index ae794b9..1d9be44 100644 --- a/drivers/usb/misc/emi62.c +++ b/drivers/usb/misc/emi62.c @@ -10,7 +10,6 @@ #include <linux/kernel.h> #include <linux/errno.h> #include <linux/slab.h> -#include <linux/init.h> #include <linux/module.h> #include <linux/usb.h> #include <linux/delay.h> diff --git a/drivers/usb/misc/ezusb.c b/drivers/usb/misc/ezusb.c index e712afe..947811b 100644 --- a/drivers/usb/misc/ezusb.c +++ b/drivers/usb/misc/ezusb.c @@ -9,7 +9,6 @@ */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/usb.h> diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c index ce97838..4e38683c 100644 --- a/drivers/usb/misc/idmouse.c +++ b/drivers/usb/misc/idmouse.c @@ -19,7 +19,6 @@ #include <linux/kernel.h> #include <linux/errno.h> #include <linux/delay.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/completion.h> @@ -386,7 +385,7 @@ static int idmouse_probe(struct usb_interface *interface, result = usb_register_dev(interface, &idmouse_class); if (result) { /* something prevented us from registering this device */ - dev_err(&interface->dev, "Unble to allocate minor number.\n"); + dev_err(&interface->dev, "Unable to allocate minor number.\n"); usb_set_intfdata(interface, NULL); idmouse_delete(dev); return result; diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index d36f34e..20bcfdd 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -15,7 +15,6 @@ #include <linux/module.h> #include <linux/usb.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/sched.h> #include <linux/mutex.h> @@ -300,7 +299,7 @@ static ssize_t iowarrior_read(struct file *file, char __user *buffer, do { atomic_set(&dev->overflow_flag, 0); if ((read_idx = read_index(dev)) == -1) { - /* queue emty */ + /* queue empty */ if (file->f_flags & O_NONBLOCK) return -EAGAIN; else { diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c index b1d5953..82503a7 100644 --- a/drivers/usb/misc/ldusb.c +++ b/drivers/usb/misc/ldusb.c @@ -24,7 +24,6 @@ #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/mutex.h> diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index eb37c95..97cd9e2 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -79,7 +79,6 @@ #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/completion.h> diff --git a/drivers/usb/misc/rio500.c b/drivers/usb/misc/rio500.c index b9b356a..13731d5 100644 --- a/drivers/usb/misc/rio500.c +++ b/drivers/usb/misc/rio500.c @@ -36,7 +36,6 @@ #include <linux/errno.h> #include <linux/random.h> #include <linux/poll.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/usb.h> diff --git a/drivers/usb/misc/sisusbvga/sisusb_init.c b/drivers/usb/misc/sisusbvga/sisusb_init.c index cb8a3d9..bf0032c 100644 --- a/drivers/usb/misc/sisusbvga/sisusb_init.c +++ b/drivers/usb/misc/sisusbvga/sisusb_init.c @@ -40,7 +40,6 @@ #include <linux/kernel.h> #include <linux/errno.h> #include <linux/poll.h> -#include <linux/init.h> #include <linux/spinlock.h> #include "sisusb.h" diff --git a/drivers/usb/misc/trancevibrator.c b/drivers/usb/misc/trancevibrator.c index 741efed..4145314 100644 --- a/drivers/usb/misc/trancevibrator.c +++ b/drivers/usb/misc/trancevibrator.c @@ -21,7 +21,6 @@ /* Standard include files */ #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/usb.h> diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c index 89927bc..1184390 100644 --- a/drivers/usb/misc/usblcd.c +++ b/drivers/usb/misc/usblcd.c @@ -14,7 +14,6 @@ *****************************************************************************/ #include <linux/module.h> #include <linux/kernel.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/errno.h> #include <linux/mutex.h> diff --git a/drivers/usb/misc/usbled.c b/drivers/usb/misc/usbled.c index 12d03e7..78eb4ff 100644 --- a/drivers/usb/misc/usbled.c +++ b/drivers/usb/misc/usbled.c @@ -11,7 +11,6 @@ #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/usb.h> diff --git a/drivers/usb/misc/usbsevseg.c b/drivers/usb/misc/usbsevseg.c index b2d82b9..1fe6b73 100644 --- a/drivers/usb/misc/usbsevseg.c +++ b/drivers/usb/misc/usbsevseg.c @@ -12,7 +12,6 @@ #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/string.h> @@ -57,7 +56,7 @@ struct usb_sevsegdev { * if str commands are used, we would assume the end of string * so mem commands are used. */ -inline size_t my_memlen(const char *buf, size_t count) +static inline size_t my_memlen(const char *buf, size_t count) { if (count > 0 && buf[count-1] == '\n') return count - 1; diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index b415282..f6568b5 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -10,6 +10,7 @@ #include <linux/usb.h> +#define SIMPLE_IO_TIMEOUT 10000 /* in milliseconds */ /*-------------------------------------------------------------------------*/ @@ -366,6 +367,7 @@ static int simple_io( int max = urb->transfer_buffer_length; struct completion completion; int retval = 0; + unsigned long expire; urb->context = &completion; while (retval == 0 && iterations-- > 0) { @@ -378,9 +380,15 @@ static int simple_io( if (retval != 0) break; - /* NOTE: no timeouts; can't be broken out of by interrupt */ - wait_for_completion(&completion); - retval = urb->status; + expire = msecs_to_jiffies(SIMPLE_IO_TIMEOUT); + if (!wait_for_completion_timeout(&completion, expire)) { + usb_kill_urb(urb); + retval = (urb->status == -ENOENT ? + -ETIMEDOUT : urb->status); + } else { + retval = urb->status; + } + urb->dev = udev; if (retval == 0 && usb_pipein(urb->pipe)) retval = simple_check_buf(tdev, urb); @@ -619,8 +627,8 @@ static int is_good_ext(struct usbtest_dev *tdev, u8 *buf) } attr = le32_to_cpu(ext->bmAttributes); - /* bits[1:4] is used and others are reserved */ - if (attr & ~0x1e) { /* reserved == 0 */ + /* bits[1:15] is used and others are reserved */ + if (attr & ~0xfffe) { /* reserved == 0 */ ERROR(tdev, "reserved bits set\n"); return 0; } @@ -763,7 +771,7 @@ static int ch9_postconfig(struct usbtest_dev *dev) * there's always [9.4.3] a bos device descriptor [9.6.2] in USB * 3.0 spec */ - if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0300) { + if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0210) { struct usb_bos_descriptor *bos = NULL; struct usb_dev_cap_header *header = NULL; unsigned total, num, length; @@ -944,7 +952,7 @@ struct ctrl_ctx { int last; }; -#define NUM_SUBCASES 15 /* how many test subcases here? */ +#define NUM_SUBCASES 16 /* how many test subcases here? */ struct subcase { struct usb_ctrlrequest setup; @@ -1218,6 +1226,15 @@ test_ctrl_queue(struct usbtest_dev *dev, struct usbtest_param *param) } expected = -EREMOTEIO; break; + case 15: + req.wValue = cpu_to_le16(USB_DT_BOS << 8); + if (udev->bos) + len = le16_to_cpu(udev->bos->desc->wTotalLength); + else + len = sizeof(struct usb_bos_descriptor); + if (le16_to_cpu(udev->descriptor.bcdUSB) < 0x0201) + expected = -EPIPE; + break; default: ERROR(dev, "bogus number of ctrl queue testcases!\n"); context.status = -EINVAL; @@ -1537,8 +1554,17 @@ static int test_halt(struct usbtest_dev *tdev, int ep, struct urb *urb) return retval; } retval = verify_halted(tdev, ep, urb); - if (retval < 0) + if (retval < 0) { + int ret; + + /* clear halt anyways, else further tests will fail */ + ret = usb_clear_halt(urb->dev, urb->pipe); + if (ret) + ERROR(tdev, "ep %02x couldn't clear halt, %d\n", + ep, ret); + return retval; + } /* clear halt (tests API + protocol), verify it worked */ retval = usb_clear_halt(urb->dev, urb->pipe); diff --git a/drivers/usb/misc/yurex.c b/drivers/usb/misc/yurex.c index b6ab515..2427820 100644 --- a/drivers/usb/misc/yurex.c +++ b/drivers/usb/misc/yurex.c @@ -11,7 +11,6 @@ #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/kref.h> @@ -464,7 +463,7 @@ static ssize_t yurex_write(struct file *file, const char *user_buffer, size_t co goto error; mutex_lock(&dev->io_mutex); - if (!dev->interface) { /* alreaday disconnected */ + if (!dev->interface) { /* already disconnected */ mutex_unlock(&dev->io_mutex); retval = -ENODEV; goto error; diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 57dfc0c..688dc8b 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. @@ -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 c5ea5c6..ba49501 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/am35x.c b/drivers/usb/musb/am35x.c index ca45b39..b3aa018 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -26,7 +26,6 @@ * */ -#include <linux/init.h> #include <linux/module.h> #include <linux/clk.h> #include <linux/err.h> diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index d9692f7..796677f 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -11,7 +11,6 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/sched.h> -#include <linux/init.h> #include <linux/list.h> #include <linux/gpio.h> #include <linux/io.h> @@ -77,7 +76,7 @@ void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src) bfin_write16(USB_DMA_REG(epnum, USB_DMAx_CTRL), dma_reg); SSYNC(); - /* Wait for compelete */ + /* Wait for complete */ while (!(bfin_read_USB_DMA_INTERRUPT() & (1 << epnum))) cpu_relax(); @@ -131,7 +130,7 @@ void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) bfin_write16(USB_DMA_REG(epnum, USB_DMAx_CTRL), dma_reg); SSYNC(); - /* Wait for compelete */ + /* Wait for complete */ while (!(bfin_read_USB_DMA_INTERRUPT() & (1 << epnum))) cpu_relax(); diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 2f2c1cb..e3486de 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -26,7 +26,6 @@ * */ -#include <linux/init.h> #include <linux/module.h> #include <linux/clk.h> #include <linux/err.h> diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index 1121fd7..c259dac 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -24,7 +24,6 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/sched.h> -#include <linux/init.h> #include <linux/list.h> #include <linux/delay.h> #include <linux/clk.h> diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c new file mode 100644 index 0000000..5f30537 --- /dev/null +++ b/drivers/usb/musb/jz4740.c @@ -0,0 +1,201 @@ +/* + * Ingenic JZ4740 "glue layer" + * + * Copyright (C) 2013, Apelete Seketeli <apelete@seketeli.net> + * + * 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 <linux/clk.h> +#include <linux/dma-mapping.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#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 <apelete@seketeli.net>"); +MODULE_LICENSE("GPL v2"); +module_platform_driver(jz4740_driver); diff --git a/drivers/usb/musb/musb_am335x.c b/drivers/usb/musb/musb_am335x.c index 8be9b02..d235378 100644 --- a/drivers/usb/musb/musb_am335x.c +++ b/drivers/usb/musb/musb_am335x.c @@ -1,4 +1,3 @@ -#include <linux/init.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/module.h> diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 4d4499b..fc192ad 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -83,7 +83,7 @@ * This gets many kinds of configuration information: * - Kconfig for everything user-configurable * - platform_device for addressing, irq, and platform_data - * - platform_data is mostly for board-specific informarion + * - platform_data is mostly for board-specific information * (plus recentrly, SOC or family details) * * Most of the conditional compilation will (someday) vanish. @@ -93,7 +93,6 @@ #include <linux/kernel.h> #include <linux/sched.h> #include <linux/slab.h> -#include <linux/init.h> #include <linux/list.h> #include <linux/kobject.h> #include <linux/prefetch.h> @@ -478,8 +477,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; @@ -1187,7 +1186,7 @@ fifo_setup(struct musb *musb, struct musb_hw_ep *hw_ep, musb_writeb(mbase, MUSB_INDEX, hw_ep->epnum); /* EP0 reserved endpoint for control, bidirectional; - * EP1 reserved for bulk, two unidirection halves. + * EP1 reserved for bulk, two unidirectional halves. */ if (hw_ep->epnum == 1) musb->bulk_ep = hw_ep; @@ -1813,6 +1812,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. * @@ -1857,7 +1871,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) /* The musb_platform_init() call: * - adjusts musb->mregs * - sets the musb->isr - * - may initialize an integrated tranceiver + * - may initialize an integrated transceiver * - initializes musb->xceiv, usually by otg_get_phy() * - stops powering VBUS * @@ -1897,6 +1911,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 @@ -1940,17 +1956,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); @@ -1981,6 +2006,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: @@ -2044,6 +2071,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; @@ -2216,16 +2245,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; } @@ -2283,19 +2324,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); diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 29f7cd7..7083e82 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -47,6 +47,7 @@ #include <linux/usb/otg.h> #include <linux/usb/musb.h> #include <linux/phy/phy.h> +#include <linux/workqueue.h> 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_cppi41.c b/drivers/usb/musb/musb_cppi41.c index a12bd30..f889296 100644 --- a/drivers/usb/musb/musb_cppi41.c +++ b/drivers/usb/musb/musb_cppi41.c @@ -615,7 +615,7 @@ static int cppi41_dma_controller_start(struct cppi41_dma_controller *controller) dc = dma_request_slave_channel(dev, str); if (!dc) { - dev_err(dev, "Falied to request %s.\n", str); + dev_err(dev, "Failed to request %s.\n", str); ret = -EPROBE_DEFER; goto err; } diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 1901f6f..7a109ea 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -29,7 +29,6 @@ * da8xx.c would be merged to this file after testing. */ -#include <linux/init.h> #include <linux/io.h> #include <linux/err.h> #include <linux/platform_device.h> @@ -83,6 +82,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; @@ -106,10 +107,24 @@ struct dsps_musb_wrapper { /* bit positions for mode */ unsigned iddig:5; + unsigned iddig_mux:5; /* miscellaneous stuff */ 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. */ @@ -119,6 +134,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) @@ -341,8 +358,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); @@ -406,6 +424,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 +480,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); @@ -507,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; @@ -605,9 +673,12 @@ 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, + .iddig_mux = 7, .usb_shift = 0, .usb_mask = 0x1ff, .usb_bitmap = (0x1ff << 0), @@ -628,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_gadget.c b/drivers/usb/musb/musb_gadget.c index 32fb057c..d4aa779 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); } @@ -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 { diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 6582a20..ed45572 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -39,7 +39,6 @@ #include <linux/sched.h> #include <linux/slab.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/list.h> #include <linux/dma-mapping.h> @@ -2013,7 +2012,7 @@ static int musb_schedule( head = &musb->out_bulk; /* Enable bulk RX/TX NAK timeout scheme when bulk requests are - * multiplexed. This scheme doen't work in high speed to full + * multiplexed. This scheme does not work in high speed to full * speed scenario as NAK interrupts are not coming from a * full speed device connected to a high speed device. * NAK timeout interval is 8 (128 uframe or 16ms) for HS and @@ -2433,6 +2432,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; @@ -2462,7 +2463,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; } @@ -2657,6 +2663,7 @@ int musb_host_setup(struct musb *musb, int power_budget) if (ret < 0) return ret; + device_wakeup_enable(hcd->self.controller); return 0; } diff --git a/drivers/usb/musb/musb_host.h b/drivers/usb/musb/musb_host.h index 960d735..7bbf01b 100644 --- a/drivers/usb/musb/musb_host.h +++ b/drivers/usb/musb/musb_host.h @@ -92,6 +92,9 @@ 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); +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) { @@ -121,6 +124,9 @@ 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) {} +static inline void musb_port_reset(struct musb *musb, bool do_reset) {} +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 9af6bba..eb63443 100644 --- a/drivers/usb/musb/musb_virthub.c +++ b/drivers/usb/musb/musb_virthub.c @@ -36,7 +36,6 @@ #include <linux/kernel.h> #include <linux/sched.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/time.h> #include <linux/timer.h> @@ -44,7 +43,38 @@ #include "musb_core.h" -static void musb_port_suspend(struct musb *musb, bool do_suspend) +void musb_host_finish_resume(struct work_struct *work) +{ + struct musb *musb; + unsigned long flags; + u8 power; + + musb = container_of(work, struct musb, finish_resume_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; u8 power; @@ -105,11 +135,11 @@ static 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); } } -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; @@ -150,7 +180,7 @@ static 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 +355,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); diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index 4432314..4e9fb1d 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -18,7 +18,6 @@ #include <linux/kernel.h> #include <linux/errno.h> #include <linux/err.h> -#include <linux/init.h> #include <linux/prefetch.h> #include <linux/usb.h> #include <linux/irq.h> diff --git a/drivers/usb/musb/tusb6010_omap.c b/drivers/usb/musb/tusb6010_omap.c index b8794eb..e33b6b2 100644 --- a/drivers/usb/musb/tusb6010_omap.c +++ b/drivers/usb/musb/tusb6010_omap.c @@ -11,7 +11,6 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/usb.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index 122446b..c2e45e63 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -21,7 +21,6 @@ #include <linux/module.h> #include <linux/kernel.h> -#include <linux/init.h> #include <linux/clk.h> #include <linux/err.h> #include <linux/io.h> diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c index 3700e97..9aad00f 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", diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 08e2f39..7d1451d 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -6,6 +6,15 @@ menu "USB Physical Layer drivers" config USB_PHY def_bool n +config USB_OTG_FSM + tristate "USB 2.0 OTG FSM implementation" + depends on USB + 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 +29,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 @@ -29,6 +38,7 @@ config FSL_USB2_OTG config ISP1301_OMAP tristate "Philips ISP1301 with OMAP OTG" depends on I2C && ARCH_OMAP_OTG + depends on USB select USB_PHY help If you say yes here you get support for the Philips ISP1301 @@ -38,7 +48,16 @@ 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" + 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" @@ -134,6 +153,31 @@ 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 phy-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 @@ -145,7 +189,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" diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index 022c1da..be58ada 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -3,18 +3,20 @@ # 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 +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 +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 @@ -30,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-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index 0874023..11ab2c4 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-am335x-control.c b/drivers/usb/phy/phy-am335x-control.c index 634f49a..d75196a 100644 --- a/drivers/usb/phy/phy-am335x-control.c +++ b/drivers/usb/phy/phy-am335x-control.c @@ -3,11 +3,7 @@ #include <linux/err.h> #include <linux/of.h> #include <linux/io.h> - -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; diff --git a/drivers/usb/phy/phy-am335x.c b/drivers/usb/phy/phy-am335x.c index 0e3c60cb..12fc346 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,38 +88,48 @@ 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/<usb-phy-id>/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; } static const struct dev_pm_ops am335x_pm_ops = { - SET_RUNTIME_PM_OPS(am335x_phy_runtime_suspend, - am335x_phy_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(am335x_phy_suspend, am335x_phy_resume) }; -#define DEV_PM_OPS (&am335x_pm_ops) +#define DEV_PM_OPS (&am335x_pm_ops) #else -#define DEV_PM_OPS NULL +#define DEV_PM_OPS NULL #endif static const struct of_device_id am335x_phy_ids[] = { diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c index 7f3c73b..2b0f968 100644 --- a/drivers/usb/phy/phy-fsl-usb.c +++ b/drivers/usb/phy/phy-fsl-usb.c @@ -27,7 +27,6 @@ #include <linux/slab.h> #include <linux/proc_fs.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/timer.h> @@ -848,7 +847,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 +1016,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 +1086,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-fsl-usb.h b/drivers/usb/phy/phy-fsl-usb.h index 7365170..5986c96 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 <linux/usb/otg-fsm.h> #include <linux/usb/otg.h> #include <linux/ioctl.h> diff --git a/drivers/usb/phy/phy-fsm-usb.c b/drivers/usb/phy/phy-fsm-usb.c index 329c2d2..7aa314e 100644 --- a/drivers/usb/phy/phy-fsm-usb.c +++ b/drivers/usb/phy/phy-fsm-usb.c @@ -23,13 +23,12 @@ #include <linux/kernel.h> #include <linux/types.h> -#include <linux/spinlock.h> +#include <linux/mutex.h> #include <linux/delay.h> #include <linux/usb.h> #include <linux/usb/gadget.h> #include <linux/usb/otg.h> - -#include "phy-fsm-usb.h" +#include <linux/usb/otg-fsm.h> /* Change USB protocol when there is a protocol change */ static int otg_set_protocol(struct otg_fsm *fsm, int protocol) @@ -65,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: @@ -122,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) @@ -245,9 +244,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 +357,7 @@ int otg_statemachine(struct otg_fsm *fsm) default: break; } - spin_unlock_irqrestore(&fsm->lock, flags); + mutex_unlock(&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 deleted file mode 100644 index 7441b46..0000000 --- a/drivers/usb/phy/phy-fsm-usb.h +++ /dev/null @@ -1,236 +0,0 @@ -/* Copyright (C) 2007,2008 Freescale Semiconductor, Inc. - * - * 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. - * - * 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. - * - * 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. - */ - -#undef VERBOSE - -#ifdef VERBOSE -#define VDBG(fmt, args...) pr_debug("[%s] " fmt , \ - __func__, ## args) -#else -#define VDBG(stuff...) do {} while (0) -#endif - -#ifdef VERBOSE -#define MPC_LOC printk("Current Location [%s]:[%d]\n", __FILE__, __LINE__) -#else -#define MPC_LOC do {} while (0) -#endif - -#define PROTO_UNDEF (0) -#define PROTO_HOST (1) -#define PROTO_GADGET (2) - -enum otg_fsm_timer { - /* Standard OTG timers */ - A_WAIT_VRISE, - A_WAIT_VFALL, - A_WAIT_BCON, - A_AIDL_BDIS, - B_ASE0_BRST, - A_BIDL_ADIS, - - /* Auxiliary timers */ - B_SE0_SRP, - B_SRP_FAIL, - A_WAIT_ENUM, - - NUM_OTG_FSM_TIMERS, -}; - -/* OTG state machine according to the OTG spec */ -struct otg_fsm { - /* Input */ - int id; - int adp_change; - int power_up; - int test_device; - int a_bus_drop; - int a_bus_req; - int a_srp_det; - int a_vbus_vld; - int b_conn; - int a_bus_resume; - int a_bus_suspend; - int a_conn; - int b_bus_req; - int b_se0_srp; - int b_ssend_srp; - int b_sess_vld; - /* Auxilary inputs */ - int a_sess_vld; - int b_bus_resume; - int b_bus_suspend; - - /* Output */ - int data_pulse; - int drv_vbus; - int loc_conn; - int loc_sof; - int adp_prb; - int adp_sns; - - /* Internal variables */ - int a_set_b_hnp_en; - int b_srp_done; - int b_hnp_enable; - int a_clr_err; - - /* Informative variables */ - int a_bus_drop_inf; - int a_bus_req_inf; - int a_clr_err_inf; - int b_bus_req_inf; - /* Auxilary informative variables */ - int a_suspend_req_inf; - - /* Timeout indicator for timers */ - int a_wait_vrise_tmout; - int a_wait_vfall_tmout; - int a_wait_bcon_tmout; - int a_aidl_bdis_tmout; - int b_ase0_brst_tmout; - int a_bidl_adis_tmout; - - struct otg_fsm_ops *ops; - struct usb_otg *otg; - - /* Current usb protocol used: 0:undefine; 1:host; 2:client */ - int protocol; - spinlock_t lock; -}; - -struct otg_fsm_ops { - void (*chrg_vbus)(struct otg_fsm *fsm, int on); - void (*drv_vbus)(struct otg_fsm *fsm, int on); - void (*loc_conn)(struct otg_fsm *fsm, int on); - void (*loc_sof)(struct otg_fsm *fsm, int on); - void (*start_pulse)(struct otg_fsm *fsm); - void (*start_adp_prb)(struct otg_fsm *fsm); - void (*start_adp_sns)(struct otg_fsm *fsm); - void (*add_timer)(struct otg_fsm *fsm, enum otg_fsm_timer timer); - void (*del_timer)(struct otg_fsm *fsm, enum otg_fsm_timer timer); - int (*start_host)(struct otg_fsm *fsm, int on); - int (*start_gadget)(struct otg_fsm *fsm, int on); -}; - - -static inline int otg_chrg_vbus(struct otg_fsm *fsm, int on) -{ - if (!fsm->ops->chrg_vbus) - return -EOPNOTSUPP; - fsm->ops->chrg_vbus(fsm, on); - return 0; -} - -static inline int otg_drv_vbus(struct otg_fsm *fsm, int on) -{ - if (!fsm->ops->drv_vbus) - return -EOPNOTSUPP; - if (fsm->drv_vbus != on) { - fsm->drv_vbus = on; - fsm->ops->drv_vbus(fsm, on); - } - return 0; -} - -static inline int otg_loc_conn(struct otg_fsm *fsm, int on) -{ - if (!fsm->ops->loc_conn) - return -EOPNOTSUPP; - if (fsm->loc_conn != on) { - fsm->loc_conn = on; - fsm->ops->loc_conn(fsm, on); - } - return 0; -} - -static inline int otg_loc_sof(struct otg_fsm *fsm, int on) -{ - if (!fsm->ops->loc_sof) - return -EOPNOTSUPP; - if (fsm->loc_sof != on) { - fsm->loc_sof = on; - fsm->ops->loc_sof(fsm, on); - } - return 0; -} - -static inline int otg_start_pulse(struct otg_fsm *fsm) -{ - if (!fsm->ops->start_pulse) - return -EOPNOTSUPP; - if (!fsm->data_pulse) { - fsm->data_pulse = 1; - fsm->ops->start_pulse(fsm); - } - return 0; -} - -static inline int otg_start_adp_prb(struct otg_fsm *fsm) -{ - if (!fsm->ops->start_adp_prb) - return -EOPNOTSUPP; - if (!fsm->adp_prb) { - fsm->adp_sns = 0; - fsm->adp_prb = 1; - fsm->ops->start_adp_prb(fsm); - } - return 0; -} - -static inline int otg_start_adp_sns(struct otg_fsm *fsm) -{ - if (!fsm->ops->start_adp_sns) - return -EOPNOTSUPP; - if (!fsm->adp_sns) { - fsm->adp_sns = 1; - fsm->ops->start_adp_sns(fsm); - } - return 0; -} - -static inline int otg_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer timer) -{ - if (!fsm->ops->add_timer) - return -EOPNOTSUPP; - fsm->ops->add_timer(fsm, timer); - return 0; -} - -static inline int otg_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer timer) -{ - if (!fsm->ops->del_timer) - return -EOPNOTSUPP; - fsm->ops->del_timer(fsm, timer); - return 0; -} - -static inline int otg_start_host(struct otg_fsm *fsm, int on) -{ - if (!fsm->ops->start_host) - return -EOPNOTSUPP; - return fsm->ops->start_host(fsm, on); -} - -static inline int otg_start_gadget(struct otg_fsm *fsm, int on) -{ - if (!fsm->ops->start_gadget) - return -EOPNOTSUPP; - return fsm->ops->start_gadget(fsm, on); -} - -int otg_statemachine(struct otg_fsm *fsm); diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c index aa6d37b..bb39498 100644 --- a/drivers/usb/phy/phy-generic.c +++ b/drivers/usb/phy/phy-generic.c @@ -241,7 +241,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 02799a5..69462e0 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-isp1301-omap.c b/drivers/usb/phy/phy-isp1301-omap.c index d3a5160..6e146d7 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; diff --git a/drivers/usb/phy/phy-keystone.c b/drivers/usb/phy/phy-keystone.c new file mode 100644 index 0000000..d762003 --- /dev/null +++ b/drivers/usb/phy/phy-keystone.c @@ -0,0 +1,136 @@ +/* + * 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 <w-kwok2@ti.com> + * + * 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 <linux/module.h> +#include <linux/platform_device.h> +#include <linux/usb/usb_phy_gen_xceiv.h> +#include <linux/io.h> +#include <linux/of.h> + +#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); + 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, NULL); + 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 = 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"); diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index e9d4cd9..8546c8d 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -40,8 +40,6 @@ #include <linux/usb/msm_hsusb_hw.h> #include <linux/regulator/consumer.h> -#include <mach/clk.h> - #define MSM_USB_BASE (motg->regs) #define DRIVER_NAME "msm_otg" @@ -308,33 +306,30 @@ static void ulpi_init(struct msm_otg *motg) static int msm_otg_link_clk_reset(struct msm_otg *motg, bool assert) { - int ret; + int ret = 0; + + if (!motg->pdata->link_clk_reset) + return ret; + + ret = motg->pdata->link_clk_reset(motg->clk, assert); + if (ret) + dev_err(motg->phy.dev, "usb link clk reset %s failed\n", + assert ? "assert" : "deassert"); - if (assert) { - ret = clk_reset(motg->clk, CLK_RESET_ASSERT); - if (ret) - dev_err(motg->phy.dev, "usb hs_clk assert failed\n"); - } else { - ret = clk_reset(motg->clk, CLK_RESET_DEASSERT); - if (ret) - dev_err(motg->phy.dev, "usb hs_clk deassert failed\n"); - } return ret; } static int msm_otg_phy_clk_reset(struct msm_otg *motg) { - int ret; + int ret = 0; - ret = clk_reset(motg->phy_reset_clk, CLK_RESET_ASSERT); - if (ret) { - dev_err(motg->phy.dev, "usb phy clk assert failed\n"); + if (!motg->pdata->phy_clk_reset) return ret; - } - usleep_range(10000, 12000); - ret = clk_reset(motg->phy_reset_clk, CLK_RESET_DEASSERT); + + ret = motg->pdata->phy_clk_reset(motg->phy_reset_clk); if (ret) - dev_err(motg->phy.dev, "usb phy clk deassert failed\n"); + dev_err(motg->phy.dev, "usb phy clk reset failed\n"); + return ret; } @@ -669,6 +664,7 @@ static void msm_otg_start_host(struct usb_phy *phy, int on) pdata->setup_gpio(OTG_STATE_A_HOST); #ifdef CONFIG_USB usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); + device_wakeup_enable(hcd->self.controller); #endif } else { dev_dbg(phy->dev, "host off\n"); diff --git a/drivers/usb/phy/phy-mv-usb.c b/drivers/usb/phy/phy-mv-usb.c index 98f6ac6a..7d80c54 100644 --- a/drivers/usb/phy/phy-mv-usb.c +++ b/drivers/usb/phy/phy-mv-usb.c @@ -11,7 +11,6 @@ #include <linux/module.h> #include <linux/kernel.h> -#include <linux/init.h> #include <linux/io.h> #include <linux/uaccess.h> #include <linux/device.h> @@ -213,10 +212,12 @@ static void mv_otg_start_host(struct mv_otg *mvotg, int on) hcd = bus_to_hcd(otg->host); - if (on) + if (on) { usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); - else + device_wakeup_enable(hcd->self.controller); + } else { usb_remove_hcd(hcd); + } #endif /* CONFIG_USB */ } diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index 545844b..b42897b 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); @@ -160,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); diff --git a/drivers/usb/phy/phy-omap-control.c b/drivers/usb/phy/phy-omap-control.c index 09c5ace..e725318 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/drivers/usb/phy/phy-omap-otg.c b/drivers/usb/phy/phy-omap-otg.c new file mode 100644 index 0000000..11598cd --- /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 <linux/io.h> +#include <linux/err.h> +#include <linux/extcon.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/platform_data/usb-omap1.h> + +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 <aaro.koskinen@iki.fi>"); diff --git a/drivers/usb/phy/phy-rcar-gen2-usb.c b/drivers/usb/phy/phy-rcar-gen2-usb.c index db3ab34..551e0a6 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; diff --git a/drivers/usb/phy/phy-tahvo.c b/drivers/usb/phy/phy-tahvo.c new file mode 100644 index 0000000..cc61ee4 --- /dev/null +++ b/drivers/usb/phy/phy-tahvo.c @@ -0,0 +1,457 @@ +/* + * 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 <linux/io.h> +#include <linux/clk.h> +#include <linux/usb.h> +#include <linux/extcon.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/usb/otg.h> +#include <linux/mfd/retu.h> +#include <linux/usb/gadget.h> +#include <linux/platform_device.h> + +#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); + + 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); + + 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 <aaro.koskinen@iki.fi>"); diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index 82232ac..bbe4f8e 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -876,7 +876,7 @@ static int utmi_phy_probe(struct tegra_usb_phy *tegra_phy, tegra_phy->pad_regs = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (!tegra_phy->regs) { + if (!tegra_phy->pad_regs) { dev_err(&pdev->dev, "Failed to remap UTMI Pad regs\n"); return -ENOMEM; } diff --git a/drivers/usb/phy/phy-twl6030-usb.c b/drivers/usb/phy/phy-twl6030-usb.c index 30e8a61..214172b 100644 --- a/drivers/usb/phy/phy-twl6030-usb.c +++ b/drivers/usb/phy/phy-twl6030-usb.c @@ -127,7 +127,8 @@ static inline int twl6030_writeb(struct twl6030_usb *twl, u8 module, static inline u8 twl6030_readb(struct twl6030_usb *twl, u8 module, u8 address) { - u8 data, ret = 0; + u8 data; + int ret; ret = twl_i2c_read_u8(module, &data, address); if (ret >= 0) @@ -327,7 +328,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; diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c index 1b74523..8afa813 100644 --- a/drivers/usb/phy/phy.c +++ b/drivers/usb/phy/phy.c @@ -130,7 +130,7 @@ struct usb_phy *usb_get_phy(enum usb_phy_type type) phy = __usb_find_phy(&phy_list, type); if (IS_ERR(phy) || !try_module_get(phy->dev->driver->owner)) { - pr_err("unable to find transceiver of type %s\n", + pr_debug("PHY: unable to find transceiver of type %s\n", usb_phy_type_string(type)); goto err0; } @@ -228,7 +228,7 @@ struct usb_phy *usb_get_phy_dev(struct device *dev, u8 index) phy = __usb_find_phy_dev(dev, &phy_bind_list, index); if (IS_ERR(phy) || !try_module_get(phy->dev->driver->owner)) { - pr_err("unable to find transceiver\n"); + dev_dbg(dev, "unable to find transceiver\n"); goto err0; } @@ -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)))) @@ -420,10 +424,8 @@ int usb_bind_phy(const char *dev_name, u8 index, unsigned long flags; phy_bind = kzalloc(sizeof(*phy_bind), GFP_KERNEL); - if (!phy_bind) { - pr_err("phy_bind(): No memory for phy_bind"); + if (!phy_bind) return -ENOMEM; - } phy_bind->dev_name = dev_name; phy_bind->phy_dev_name = phy_dev_name; diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 45b9401..d49f9c3 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)); } diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index 3385aeb..458f376 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/drivers/usb/renesas_usbhs/mod_host.c b/drivers/usb/renesas_usbhs/mod_host.c index e40f565..10e1ded 100644 --- a/drivers/usb/renesas_usbhs/mod_host.c +++ b/drivers/usb/renesas_usbhs/mod_host.c @@ -1469,6 +1469,7 @@ static int usbhsh_start(struct usbhs_priv *priv) ret = usb_add_hcd(hcd, 0, 0); if (ret < 0) return 0; + device_wakeup_enable(hcd->self.controller); /* * pipe initialize and enable DCP diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index ddb9c51..3ce5c74 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig @@ -472,6 +472,35 @@ config USB_SERIAL_MOS7840 To compile this driver as a module, choose M here: the module will be called mos7840. If unsure, choose N. +config USB_SERIAL_MXUPORT + tristate "USB Moxa UPORT Serial Driver" + ---help--- + Say Y here if you want to use a MOXA UPort Serial hub. + + This driver supports: + + [2 Port] + - UPort 1250 : 2 Port RS-232/422/485 USB to Serial Hub + - UPort 1250I : 2 Port RS-232/422/485 USB to Serial Hub with + Isolation + + [4 Port] + - UPort 1410 : 4 Port RS-232 USB to Serial Hub + - UPort 1450 : 4 Port RS-232/422/485 USB to Serial Hub + - UPort 1450I : 4 Port RS-232/422/485 USB to Serial Hub with + Isolation + + [8 Port] + - UPort 1610-8 : 8 Port RS-232 USB to Serial Hub + - UPort 1650-8 : 8 Port RS-232/422/485 USB to Serial Hub + + [16 Port] + - UPort 1610-16 : 16 Port RS-232 USB to Serial Hub + - UPort 1650-16 : 16 Port RS-232/422/485 USB to Serial Hub + + To compile this driver as a module, choose M here: the + module will be called mxuport. + config USB_SERIAL_NAVMAN tristate "USB Navman GPS device" help diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile index 42670f0..bfdafd3 100644 --- a/drivers/usb/serial/Makefile +++ b/drivers/usb/serial/Makefile @@ -37,6 +37,7 @@ obj-$(CONFIG_USB_SERIAL_MCT_U232) += mct_u232.o obj-$(CONFIG_USB_SERIAL_METRO) += metro-usb.o obj-$(CONFIG_USB_SERIAL_MOS7720) += mos7720.o obj-$(CONFIG_USB_SERIAL_MOS7840) += mos7840.o +obj-$(CONFIG_USB_SERIAL_MXUPORT) += mxuport.o obj-$(CONFIG_USB_SERIAL_NAVMAN) += navman.o obj-$(CONFIG_USB_SERIAL_OMNINET) += omninet.o obj-$(CONFIG_USB_SERIAL_OPTICON) += opticon.o diff --git a/drivers/usb/serial/aircable.c b/drivers/usb/serial/aircable.c index 6e320ce..80a9845 100644 --- a/drivers/usb/serial/aircable.c +++ b/drivers/usb/serial/aircable.c @@ -10,9 +10,9 @@ * * The device works as an standard CDC device, it has 2 interfaces, the first * one is for firmware access and the second is the serial one. - * The protocol is very simply, there are two posibilities reading or writing. + * The protocol is very simply, there are two possibilities reading or writing. * When writing the first urb must have a Header that starts with 0x20 0x29 the - * next two bytes must say how much data will be sended. + * next two bytes must say how much data will be sent. * When reading the process is almost equal except that the header starts with * 0x00 0x20. * @@ -31,15 +31,15 @@ * * The driver registers himself with the USB-serial core and the USB Core. I had * to implement a probe function against USB-serial, because other way, the - * driver was attaching himself to both interfaces. I have tryed with different + * driver was attaching himself to both interfaces. I have tried with different * configurations of usb_serial_driver with out exit, only the probe function * could handle this correctly. * * I have taken some info from a Greg Kroah-Hartman article: * http://www.linuxjournal.com/article/6573 * And from Linux Device Driver Kit CD, which is a great work, the authors taken - * the work to recompile lots of information an knowladge in drivers development - * and made it all avaible inside a cd. + * the work to recompile lots of information an knowledge in drivers development + * and made it all available inside a cd. * URL: http://kernel.org/pub/linux/kernel/people/gregkh/ddk/ * */ diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index bc77e95..1532cde 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -23,7 +23,6 @@ */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/ioctl.h> #include <linux/tty.h> #include <linux/slab.h> @@ -71,7 +70,7 @@ struct ark3116_private { __u32 lcr; /* line control register value */ __u32 hcr; /* handshake control register (0x8) * value */ - __u32 mcr; /* modem contol register value */ + __u32 mcr; /* modem control register value */ /* protects the status values below */ spinlock_t status_lock; @@ -609,7 +608,7 @@ static void ark3116_read_int_callback(struct urb *urb) } -/* Data comes in via the bulk (data) URB, erors/interrupts via the int URB. +/* Data comes in via the bulk (data) URB, errors/interrupts via the int URB. * This means that we cannot be sure which data byte has an associated error * condition, so we report an error for all data in the next bulk read. * diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index 84217e7..15bc718 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -18,14 +18,13 @@ * driver * * TODO: - * -- Add true modem contol line query capability. Currently we track the + * -- Add true modem control line query capability. Currently we track the * states reported by the interrupt and the states we request. * -- Add support for flush commands */ #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/tty.h> #include <linux/tty_driver.h> diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c index 6335490..35a2373 100644 --- a/drivers/usb/serial/bus.c +++ b/drivers/usb/serial/bus.c @@ -125,10 +125,12 @@ static ssize_t new_id_store(struct device_driver *driver, const char *buf, size_t count) { struct usb_serial_driver *usb_drv = to_usb_serial_driver(driver); - ssize_t retval = usb_store_new_id(&usb_drv->dynids, driver, buf, count); + ssize_t retval = usb_store_new_id(&usb_drv->dynids, usb_drv->id_table, + driver, buf, count); if (retval >= 0 && usb_drv->usb_driver != NULL) retval = usb_store_new_id(&usb_drv->usb_driver->dynids, + usb_drv->usb_driver->id_table, &usb_drv->usb_driver->drvwrap.driver, buf, count); return retval; diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index c2a4171..82371f6 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -16,7 +16,6 @@ */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/tty.h> #include <linux/module.h> #include <linux/slab.h> @@ -83,7 +82,6 @@ struct ch341_private { unsigned baud_rate; /* set baud rate */ u8 line_control; /* set line control value RTS/DTR */ u8 line_status; /* active status of modem control inputs */ - u8 multi_status_change; /* status changed multiple since last call */ }; static int ch341_control_out(struct usb_device *dev, u8 request, @@ -174,7 +172,6 @@ static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv) r = 0; spin_lock_irqsave(&priv->lock, flags); priv->line_status = (~(*buffer)) & CH341_BITS_MODEM_STAT; - priv->multi_status_change = 0; spin_unlock_irqrestore(&priv->lock, flags); } else r = -EPROTO; @@ -384,10 +381,8 @@ static void ch341_break_ctl(struct tty_struct *tty, int break_state) uint8_t *break_reg; break_reg = kmalloc(2, GFP_KERNEL); - if (!break_reg) { - dev_err(&port->dev, "%s - kmalloc failed\n", __func__); + if (!break_reg) return; - } r = ch341_control_in(port->serial->dev, CH341_REQ_READ_REG, ch341_break_reg, 0, break_reg, 2); @@ -442,11 +437,55 @@ static int ch341_tiocmset(struct tty_struct *tty, return ch341_set_handshake(port->serial->dev, control); } +static void ch341_update_line_status(struct usb_serial_port *port, + unsigned char *data, size_t len) +{ + struct ch341_private *priv = usb_get_serial_port_data(port); + struct tty_struct *tty; + unsigned long flags; + u8 status; + u8 delta; + + if (len < 4) + return; + + status = ~data[2] & CH341_BITS_MODEM_STAT; + + spin_lock_irqsave(&priv->lock, flags); + delta = status ^ priv->line_status; + priv->line_status = status; + spin_unlock_irqrestore(&priv->lock, flags); + + if (data[1] & CH341_MULT_STAT) + dev_dbg(&port->dev, "%s - multiple status change\n", __func__); + + if (!delta) + return; + + if (delta & CH341_BIT_CTS) + port->icount.cts++; + if (delta & CH341_BIT_DSR) + port->icount.dsr++; + if (delta & CH341_BIT_RI) + port->icount.rng++; + if (delta & CH341_BIT_DCD) { + port->icount.dcd++; + tty = tty_port_tty_get(&port->port); + if (tty) { + usb_serial_handle_dcd_change(port, tty, + status & CH341_BIT_DCD); + tty_kref_put(tty); + } + } + + wake_up_interruptible(&port->port.delta_msr_wait); +} + static void ch341_read_int_callback(struct urb *urb) { - struct usb_serial_port *port = (struct usb_serial_port *) urb->context; + struct usb_serial_port *port = urb->context; unsigned char *data = urb->transfer_buffer; - unsigned int actual_length = urb->actual_length; + unsigned int len = urb->actual_length; int status; switch (urb->status) { @@ -457,89 +496,23 @@ static void ch341_read_int_callback(struct urb *urb) case -ENOENT: case -ESHUTDOWN: /* this urb is terminated, clean up */ - dev_dbg(&urb->dev->dev, "%s - urb shutting down with status: %d\n", + dev_dbg(&urb->dev->dev, "%s - urb shutting down: %d\n", __func__, urb->status); return; default: - dev_dbg(&urb->dev->dev, "%s - nonzero urb status received: %d\n", + dev_dbg(&urb->dev->dev, "%s - nonzero urb status: %d\n", __func__, urb->status); goto exit; } - usb_serial_debug_data(&port->dev, __func__, - urb->actual_length, urb->transfer_buffer); - - if (actual_length >= 4) { - struct ch341_private *priv = usb_get_serial_port_data(port); - unsigned long flags; - u8 prev_line_status = priv->line_status; - - spin_lock_irqsave(&priv->lock, flags); - priv->line_status = (~(data[2])) & CH341_BITS_MODEM_STAT; - if ((data[1] & CH341_MULT_STAT)) - priv->multi_status_change = 1; - spin_unlock_irqrestore(&priv->lock, flags); - - if ((priv->line_status ^ prev_line_status) & CH341_BIT_DCD) { - struct tty_struct *tty = tty_port_tty_get(&port->port); - if (tty) - usb_serial_handle_dcd_change(port, tty, - priv->line_status & CH341_BIT_DCD); - tty_kref_put(tty); - } - - wake_up_interruptible(&port->port.delta_msr_wait); - } - + usb_serial_debug_data(&port->dev, __func__, len, data); + ch341_update_line_status(port, data, len); exit: status = usb_submit_urb(urb, GFP_ATOMIC); - if (status) - dev_err(&urb->dev->dev, - "%s - usb_submit_urb failed with result %d\n", + if (status) { + dev_err(&urb->dev->dev, "%s - usb_submit_urb failed: %d\n", __func__, status); -} - -static int ch341_tiocmiwait(struct tty_struct *tty, unsigned long arg) -{ - struct usb_serial_port *port = tty->driver_data; - struct ch341_private *priv = usb_get_serial_port_data(port); - unsigned long flags; - u8 prevstatus; - u8 status; - u8 changed; - u8 multi_change = 0; - - spin_lock_irqsave(&priv->lock, flags); - prevstatus = priv->line_status; - priv->multi_status_change = 0; - spin_unlock_irqrestore(&priv->lock, flags); - - while (!multi_change) { - interruptible_sleep_on(&port->port.delta_msr_wait); - /* see if a signal did it */ - if (signal_pending(current)) - return -ERESTARTSYS; - - if (port->serial->disconnected) - return -EIO; - - spin_lock_irqsave(&priv->lock, flags); - status = priv->line_status; - multi_change = priv->multi_status_change; - spin_unlock_irqrestore(&priv->lock, flags); - - changed = prevstatus ^ status; - - if (((arg & TIOCM_RNG) && (changed & CH341_BIT_RI)) || - ((arg & TIOCM_DSR) && (changed & CH341_BIT_DSR)) || - ((arg & TIOCM_CD) && (changed & CH341_BIT_DCD)) || - ((arg & TIOCM_CTS) && (changed & CH341_BIT_CTS))) { - return 0; - } - prevstatus = status; } - - return 0; } static int ch341_tiocmget(struct tty_struct *tty) @@ -595,7 +568,7 @@ static struct usb_serial_driver ch341_device = { .break_ctl = ch341_break_ctl, .tiocmget = ch341_tiocmget, .tiocmset = ch341_tiocmset, - .tiocmiwait = ch341_tiocmiwait, + .tiocmiwait = usb_serial_generic_tiocmiwait, .read_int_callback = ch341_read_int_callback, .port_probe = ch341_port_probe, .port_remove = ch341_port_remove, diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c index c69bb50..8d7fc48 100644 --- a/drivers/usb/serial/console.c +++ b/drivers/usb/serial/console.c @@ -14,7 +14,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/kernel.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/tty.h> #include <linux/console.h> @@ -135,7 +134,6 @@ static int usb_console_setup(struct console *co, char *options) tty = kzalloc(sizeof(*tty), GFP_KERNEL); if (!tty) { retval = -ENOMEM; - dev_err(&port->dev, "no more memory\n"); goto reset_open_count; } kref_init(&tty->kref); @@ -144,7 +142,6 @@ static int usb_console_setup(struct console *co, char *options) tty->index = co->index; if (tty_init_termios(tty)) { retval = -ENOMEM; - dev_err(&port->dev, "no more memory\n"); goto free_tty; } } diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 6987b53..95fa121 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -305,10 +305,8 @@ static int cp210x_get_config(struct usb_serial_port *port, u8 request, length = (((size - 1) | 3) + 1) / 4; buf = kcalloc(length, sizeof(__le32), GFP_KERNEL); - if (!buf) { - dev_err(&port->dev, "%s - out of memory.\n", __func__); + if (!buf) return -ENOMEM; - } /* Issue the request, attempting to read 'size' bytes */ result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), @@ -352,10 +350,8 @@ static int cp210x_set_config(struct usb_serial_port *port, u8 request, length = (((size - 1) | 3) + 1) / 4; buf = kmalloc(length * sizeof(__le32), GFP_KERNEL); - if (!buf) { - dev_err(&port->dev, "%s - out of memory.\n", __func__); + if (!buf) return -ENOMEM; - } /* Array of integers into bytes */ for (i = 0; i < length; i++) diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index 6e1b69d..0ac3b3b 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -30,7 +30,6 @@ #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/tty.h> #include <linux/tty_driver.h> @@ -285,7 +284,7 @@ static void cyberjack_read_int_callback(struct urb *urb) goto resubmit; } - /* "+=" is probably more fault tollerant than "=" */ + /* "+=" is probably more fault tolerant than "=" */ priv->rdtodo += size; dev_dbg(dev, "%s - rdtodo: %d\n", __func__, priv->rdtodo); diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 558605d..bccb122 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -27,7 +27,6 @@ #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/tty.h> #include <linux/tty_driver.h> @@ -113,7 +112,7 @@ struct cypress_private { int baud_rate; /* stores current baud rate in integer form */ int isthrottled; /* if throttled, discard reads */ - char prev_status, diff_status; /* used for TIOCMIWAIT */ + char prev_status; /* used for TIOCMIWAIT */ /* we pass a pointer to this as the argument sent to cypress_set_termios old_termios */ struct ktermios tmp_termios; /* stores the old termios settings */ @@ -136,7 +135,6 @@ static void cypress_set_termios(struct tty_struct *tty, static int cypress_tiocmget(struct tty_struct *tty); static int cypress_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); -static int cypress_tiocmiwait(struct tty_struct *tty, unsigned long arg); static int cypress_chars_in_buffer(struct tty_struct *tty); static void cypress_throttle(struct tty_struct *tty); static void cypress_unthrottle(struct tty_struct *tty); @@ -162,7 +160,7 @@ static struct usb_serial_driver cypress_earthmate_device = { .set_termios = cypress_set_termios, .tiocmget = cypress_tiocmget, .tiocmset = cypress_tiocmset, - .tiocmiwait = cypress_tiocmiwait, + .tiocmiwait = usb_serial_generic_tiocmiwait, .chars_in_buffer = cypress_chars_in_buffer, .throttle = cypress_throttle, .unthrottle = cypress_unthrottle, @@ -188,7 +186,7 @@ static struct usb_serial_driver cypress_hidcom_device = { .set_termios = cypress_set_termios, .tiocmget = cypress_tiocmget, .tiocmset = cypress_tiocmset, - .tiocmiwait = cypress_tiocmiwait, + .tiocmiwait = usb_serial_generic_tiocmiwait, .chars_in_buffer = cypress_chars_in_buffer, .throttle = cypress_throttle, .unthrottle = cypress_unthrottle, @@ -214,7 +212,7 @@ static struct usb_serial_driver cypress_ca42v2_device = { .set_termios = cypress_set_termios, .tiocmget = cypress_tiocmget, .tiocmset = cypress_tiocmset, - .tiocmiwait = cypress_tiocmiwait, + .tiocmiwait = usb_serial_generic_tiocmiwait, .chars_in_buffer = cypress_chars_in_buffer, .throttle = cypress_throttle, .unthrottle = cypress_unthrottle, @@ -864,45 +862,6 @@ static int cypress_tiocmset(struct tty_struct *tty, return cypress_write(tty, port, NULL, 0); } - -static int cypress_tiocmiwait(struct tty_struct *tty, unsigned long arg) -{ - struct usb_serial_port *port = tty->driver_data; - struct cypress_private *priv = usb_get_serial_port_data(port); - char diff; - - for (;;) { - interruptible_sleep_on(&port->port.delta_msr_wait); - /* see if a signal did it */ - if (signal_pending(current)) - return -ERESTARTSYS; - - if (port->serial->disconnected) - return -EIO; - - diff = priv->diff_status; - if (diff == 0) - return -EIO; /* no change => error */ - - /* consume all events */ - priv->diff_status = 0; - - /* return 0 if caller wanted to know about - these bits */ - if (((arg & TIOCM_RNG) && (diff & UART_RI)) || - ((arg & TIOCM_DSR) && (diff & UART_DSR)) || - ((arg & TIOCM_CD) && (diff & UART_CD)) || - ((arg & TIOCM_CTS) && (diff & UART_CTS))) - return 0; - /* otherwise caller can't care less about what - * happened, and so we continue to wait for - * more events. - */ - } - - return 0; -} - static void cypress_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { @@ -1185,9 +1144,21 @@ static void cypress_read_int_callback(struct urb *urb) spin_lock_irqsave(&priv->lock, flags); /* check to see if status has changed */ if (priv->current_status != priv->prev_status) { - priv->diff_status |= priv->current_status ^ - priv->prev_status; - wake_up_interruptible(&port->port.delta_msr_wait); + u8 delta = priv->current_status ^ priv->prev_status; + + if (delta & UART_MSR_MASK) { + if (delta & UART_CTS) + port->icount.cts++; + if (delta & UART_DSR) + port->icount.dsr++; + if (delta & UART_RI) + port->icount.rng++; + if (delta & UART_CD) + port->icount.dcd++; + + wake_up_interruptible(&port->port.delta_msr_wait); + } + priv->prev_status = priv->current_status; } spin_unlock_irqrestore(&priv->lock, flags); diff --git a/drivers/usb/serial/cypress_m8.h b/drivers/usb/serial/cypress_m8.h index b461311..119d2e1 100644 --- a/drivers/usb/serial/cypress_m8.h +++ b/drivers/usb/serial/cypress_m8.h @@ -55,19 +55,23 @@ #define CT_GENERIC 0x0F /* End of chiptype definitions */ -/* RS-232 serial data communication protocol definitions */ -/* these are sent / read at byte 0 of the input/output hid reports */ -/* You can find these values defined in the CY4601 USB to Serial design notes */ - -#define CONTROL_DTR 0x20 /* data terminal ready - flow control - host to device */ -#define UART_DSR 0x20 /* data set ready - flow control - device to host */ -#define CONTROL_RTS 0x10 /* request to send - flow control - host to device */ -#define UART_CTS 0x10 /* clear to send - flow control - device to host */ -#define UART_RI 0x10 /* ring indicator - modem - device to host */ -#define UART_CD 0x40 /* carrier detect - modem - device to host */ -#define CYP_ERROR 0x08 /* received from input report - device to host */ -/* Note - the below has nothing to do with the "feature report" reset */ -#define CONTROL_RESET 0x08 /* sent with output report - host to device */ +/* + * RS-232 serial data communication protocol definitions. + * + * These are sent / read at byte 0 of the input/output hid reports. + * You can find these values defined in the CY4601 USB to Serial design notes. + */ + +#define CONTROL_DTR 0x20 /* data terminal ready */ +#define CONTROL_RTS 0x10 /* request to send */ +#define CONTROL_RESET 0x08 /* sent with output report */ + +#define UART_MSR_MASK 0xf0 +#define UART_RI 0x80 /* ring indicator */ +#define UART_CD 0x40 /* carrier detect */ +#define UART_DSR 0x20 /* data set ready */ +#define UART_CTS 0x10 /* clear to send */ +#define CYP_ERROR 0x08 /* received from input report */ /* End of RS-232 protocol definitions */ diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 19b467f..8a23c53 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -17,7 +17,6 @@ #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/tty.h> #include <linux/tty_driver.h> diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c index 0f65861..90e603d 100644 --- a/drivers/usb/serial/empeg.c +++ b/drivers/usb/serial/empeg.c @@ -17,7 +17,6 @@ #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/tty.h> #include <linux/tty_driver.h> diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index 639a18f..c5dc233 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -12,7 +12,6 @@ #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/tty.h> #include <linux/tty_driver.h> @@ -55,6 +54,13 @@ static void f81232_update_line_status(struct usb_serial_port *port, unsigned char *data, unsigned int actual_length) { + /* + * FIXME: Update port->icount, and call + * + * wake_up_interruptible(&port->port.delta_msr_wait); + * + * on MSR changes. + */ } static void f81232_read_int_callback(struct urb *urb) @@ -110,7 +116,6 @@ static void f81232_process_read_urb(struct urb *urb) line_status = priv->line_status; priv->line_status &= ~UART_STATE_TRANSIENT_MASK; spin_unlock_irqrestore(&priv->lock, flags); - wake_up_interruptible(&port->port.delta_msr_wait); if (!urb->actual_length) return; @@ -241,54 +246,12 @@ static int f81232_carrier_raised(struct usb_serial_port *port) return 0; } -static int f81232_tiocmiwait(struct tty_struct *tty, unsigned long arg) -{ - struct usb_serial_port *port = tty->driver_data; - struct f81232_private *priv = usb_get_serial_port_data(port); - unsigned long flags; - unsigned int prevstatus; - unsigned int status; - unsigned int changed; - - spin_lock_irqsave(&priv->lock, flags); - prevstatus = priv->line_status; - spin_unlock_irqrestore(&priv->lock, flags); - - while (1) { - interruptible_sleep_on(&port->port.delta_msr_wait); - /* see if a signal did it */ - if (signal_pending(current)) - return -ERESTARTSYS; - - if (port->serial->disconnected) - return -EIO; - - spin_lock_irqsave(&priv->lock, flags); - status = priv->line_status; - spin_unlock_irqrestore(&priv->lock, flags); - - changed = prevstatus ^ status; - - if (((arg & TIOCM_RNG) && (changed & UART_RING)) || - ((arg & TIOCM_DSR) && (changed & UART_DSR)) || - ((arg & TIOCM_CD) && (changed & UART_DCD)) || - ((arg & TIOCM_CTS) && (changed & UART_CTS))) { - return 0; - } - prevstatus = status; - } - /* NOTREACHED */ - return 0; -} - static int f81232_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct serial_struct ser; struct usb_serial_port *port = tty->driver_data; - dev_dbg(&port->dev, "%s cmd = 0x%04x\n", __func__, cmd); - switch (cmd) { case TIOCGSERIAL: memset(&ser, 0, sizeof ser); @@ -302,8 +265,6 @@ static int f81232_ioctl(struct tty_struct *tty, return 0; default: - dev_dbg(&port->dev, "%s not supported = 0x%04x\n", - __func__, cmd); break; } return -ENOIOCTLCMD; @@ -354,7 +315,7 @@ static struct usb_serial_driver f81232_device = { .set_termios = f81232_set_termios, .tiocmget = f81232_tiocmget, .tiocmset = f81232_tiocmset, - .tiocmiwait = f81232_tiocmiwait, + .tiocmiwait = usb_serial_generic_tiocmiwait, .process_read_urb = f81232_process_read_urb, .read_int_callback = f81232_read_int_callback, .port_probe = f81232_port_probe, diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index fb0d5374..ee1f00f 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -33,7 +33,6 @@ #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/tty.h> #include <linux/tty_driver.h> @@ -145,7 +144,7 @@ static struct ftdi_sio_quirk ftdi_8u2232c_quirk = { * Device ID not listed? Test it using * /sys/bus/usb-serial/drivers/ftdi_sio/new_id and send a patch or report. */ -static struct usb_device_id id_table_combined [] = { +static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(FTDI_VID, FTDI_ZEITCONTROL_TAGTRACE_MIFARE_PID) }, { USB_DEVICE(FTDI_VID, FTDI_CTI_MINI_PID) }, { USB_DEVICE(FTDI_VID, FTDI_CTI_NANO_PID) }, @@ -153,6 +152,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_CANUSB_PID) }, { USB_DEVICE(FTDI_VID, FTDI_CANDAPTER_PID) }, { USB_DEVICE(FTDI_VID, FTDI_NXTCAM_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_EV3CON_PID) }, { USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_0_PID) }, { USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_1_PID) }, { USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_2_PID) }, @@ -192,6 +192,8 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(INTERBIOMETRICS_VID, INTERBIOMETRICS_IOBOARD_PID) }, { USB_DEVICE(INTERBIOMETRICS_VID, INTERBIOMETRICS_MINI_IOBOARD_PID) }, { USB_DEVICE(FTDI_VID, FTDI_SPROG_II) }, + { USB_DEVICE(FTDI_VID, FTDI_TAGSYS_LP101_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_TAGSYS_P200X_PID) }, { USB_DEVICE(FTDI_VID, FTDI_LENZ_LIUSB_PID) }, { USB_DEVICE(FTDI_VID, FTDI_XF_632_PID) }, { USB_DEVICE(FTDI_VID, FTDI_XF_634_PID) }, @@ -1695,11 +1697,8 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port) priv = kzalloc(sizeof(struct ftdi_private), GFP_KERNEL); - if (!priv) { - dev_err(&port->dev, "%s- kmalloc(%Zd) failed.\n", __func__, - sizeof(struct ftdi_private)); + if (!priv) return -ENOMEM; - } mutex_init(&priv->cfg_lock); @@ -2124,10 +2123,20 @@ static void ftdi_set_termios(struct tty_struct *tty, } /* - * All FTDI UART chips are limited to CS7/8. We won't pretend to + * All FTDI UART chips are limited to CS7/8. We shouldn't pretend to * support CS5/6 and revert the CSIZE setting instead. + * + * CS5 however is used to control some smartcard readers which abuse + * this limitation to switch modes. Original FTDI chips fall back to + * eight data bits. + * + * TODO: Implement a quirk to only allow this with mentioned + * readers. One I know of (Argolis Smartreader V1) + * returns "USB smartcard server" as iInterface string. + * The vendor didn't bother with a custom VID/PID of + * course. */ - if ((C_CSIZE(tty) != CS8) && (C_CSIZE(tty) != CS7)) { + if (C_CSIZE(tty) == CS6) { dev_warn(ddev, "requested CSIZE setting not supported\n"); termios->c_cflag &= ~CSIZE; @@ -2174,6 +2183,9 @@ no_skip: urb_value |= FTDI_SIO_SET_DATA_PARITY_NONE; } switch (cflag & CSIZE) { + case CS5: + dev_dbg(ddev, "Setting CS5 quirk\n"); + break; case CS7: urb_value |= 7; dev_dbg(ddev, "Setting CS7\n"); @@ -2383,8 +2395,6 @@ static int ftdi_ioctl(struct tty_struct *tty, { struct usb_serial_port *port = tty->driver_data; - dev_dbg(&port->dev, "%s cmd 0x%04x\n", __func__, cmd); - /* Based on code from acm.c and others */ switch (cmd) { @@ -2401,11 +2411,7 @@ static int ftdi_ioctl(struct tty_struct *tty, default: break; } - /* This is not necessarily an error - turns out the higher layers - * will do some ioctls themselves (see comment above) - */ - dev_dbg(&port->dev, "%s arg not supported - it was 0x%04x - check /usr/include/asm/ioctls.h\n", - __func__, cmd); + return -ENOIOCTLCMD; } diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index a7019d1..1e2d369 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -50,6 +50,7 @@ #define TI_XDS100V2_PID 0xa6d0 #define FTDI_NXTCAM_PID 0xABB8 /* NXTCam for Mindstorms NXT */ +#define FTDI_EV3CON_PID 0xABB9 /* Mindstorms EV3 Console Adapter */ /* US Interface Navigator (http://www.usinterface.com/) */ #define FTDI_USINT_CAT_PID 0xb810 /* Navigator CAT and 2nd PTT lines */ @@ -363,6 +364,12 @@ /* Sprog II (Andrew Crosland's SprogII DCC interface) */ #define FTDI_SPROG_II 0xF0C8 +/* + * Two of the Tagsys RFID Readers + */ +#define FTDI_TAGSYS_LP101_PID 0xF0E9 /* Tagsys L-P101 RFID*/ +#define FTDI_TAGSYS_P200X_PID 0xF0EE /* Tagsys Medio P200x RFID*/ + /* an infrared receiver for user access control with IR tags */ #define FTDI_PIEGROUP_PID 0xF208 /* Product Id */ diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index 04b5ed9..db591d1 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -25,7 +25,6 @@ #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/timer.h> #include <linux/tty.h> @@ -275,14 +274,13 @@ static int pkt_add(struct garmin_data *garmin_data_p, unsigned long flags; struct garmin_packet *pkt; - /* process only packets containg data ... */ + /* process only packets containing data ... */ if (data_length) { pkt = kmalloc(sizeof(struct garmin_packet)+data_length, GFP_ATOMIC); - if (pkt == NULL) { - dev_err(&garmin_data_p->port->dev, "out of memory\n"); + if (!pkt) return 0; - } + pkt->size = data_length; memcpy(pkt->data, data, data_length); @@ -1006,14 +1004,11 @@ static int garmin_write_bulk(struct usb_serial_port *port, spin_unlock_irqrestore(&garmin_data_p->lock, flags); buffer = kmalloc(count, GFP_ATOMIC); - if (!buffer) { - dev_err(&port->dev, "out of memory\n"); + if (!buffer) return -ENOMEM; - } urb = usb_alloc_urb(0, GFP_ATOMIC); if (!urb) { - dev_err(&port->dev, "no more free urbs\n"); kfree(buffer); return -ENOMEM; } @@ -1148,7 +1143,7 @@ static void garmin_read_process(struct garmin_data *garmin_data_p, unsigned long flags; if (garmin_data_p->flags & FLAGS_DROP_DATA) { - /* abort-transfer cmd is actice */ + /* abort-transfer cmd is active */ dev_dbg(&garmin_data_p->port->dev, "%s - pkt dropped\n", __func__); } else if (garmin_data_p->state != STATE_DISCONNECTED && garmin_data_p->state != STATE_RESET) { @@ -1393,10 +1388,9 @@ static int garmin_port_probe(struct usb_serial_port *port) struct garmin_data *garmin_data_p; garmin_data_p = kzalloc(sizeof(struct garmin_data), GFP_KERNEL); - if (garmin_data_p == NULL) { - dev_err(&port->dev, "%s - Out of memory\n", __func__); + if (!garmin_data_p) return -ENOMEM; - } + init_timer(&garmin_data_p->timer); spin_lock_init(&garmin_data_p->lock); INIT_LIST_HEAD(&garmin_data_p->pktlist); diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index c91481d..c086697 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -32,7 +32,6 @@ #include <linux/kernel.h> #include <linux/jiffies.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/tty.h> #include <linux/tty_driver.h> @@ -898,7 +897,6 @@ static int edge_open(struct tty_struct *tty, struct usb_serial_port *port) edge_port->txfifo.fifo = kmalloc(edge_port->maxTxCredits, GFP_KERNEL); if (!edge_port->txfifo.fifo) { - dev_dbg(dev, "%s - no memory\n", __func__); edge_close(port); return -ENOMEM; } @@ -908,7 +906,6 @@ static int edge_open(struct tty_struct *tty, struct usb_serial_port *port) edge_port->write_in_progress = false; if (!edge_port->write_urb) { - dev_dbg(dev, "%s - no memory\n", __func__); edge_close(port); return -ENOMEM; } @@ -1245,9 +1242,7 @@ static void send_more_port_data(struct edgeport_serial *edge_serial, to send out */ count = fifo->count; buffer = kmalloc(count+2, GFP_ATOMIC); - if (buffer == NULL) { - dev_err_console(edge_port->port, - "%s - no more kernel memory...\n", __func__); + if (!buffer) { edge_port->write_in_progress = false; goto exit_send; } @@ -1593,8 +1588,6 @@ static int edge_ioctl(struct tty_struct *tty, DEFINE_WAIT(wait); struct edgeport_port *edge_port = usb_get_serial_port_data(port); - dev_dbg(&port->dev, "%s - cmd = 0x%x\n", __func__, cmd); - switch (cmd) { case TIOCSERGETLSR: dev_dbg(&port->dev, "%s TIOCSERGETLSR\n", __func__); @@ -2027,11 +2020,8 @@ static int sram_write(struct usb_serial *serial, __u16 extAddr, __u16 addr, dev_dbg(&serial->dev->dev, "%s - %x, %x, %d\n", __func__, extAddr, addr, length); transfer_buffer = kmalloc(64, GFP_KERNEL); - if (!transfer_buffer) { - dev_err(&serial->dev->dev, "%s - kmalloc(%d) failed.\n", - __func__, 64); + if (!transfer_buffer) return -ENOMEM; - } /* need to split these writes up into 64 byte chunks */ result = 0; @@ -2075,11 +2065,8 @@ static int rom_write(struct usb_serial *serial, __u16 extAddr, __u16 addr, unsigned char *transfer_buffer; transfer_buffer = kmalloc(64, GFP_KERNEL); - if (!transfer_buffer) { - dev_err(&serial->dev->dev, "%s - kmalloc(%d) failed.\n", - __func__, 64); + if (!transfer_buffer) return -ENOMEM; - } /* need to split these writes up into 64 byte chunks */ result = 0; @@ -2121,11 +2108,8 @@ static int rom_read(struct usb_serial *serial, __u16 extAddr, unsigned char *transfer_buffer; transfer_buffer = kmalloc(64, GFP_KERNEL); - if (!transfer_buffer) { - dev_err(&serial->dev->dev, - "%s - kmalloc(%d) failed.\n", __func__, 64); + if (!transfer_buffer) return -ENOMEM; - } /* need to split these reads up into 64 byte chunks */ result = 0; @@ -2165,11 +2149,8 @@ static int send_iosp_ext_cmd(struct edgeport_port *edge_port, int status = 0; buffer = kmalloc(10, GFP_ATOMIC); - if (!buffer) { - dev_err(&edge_port->port->dev, - "%s - kmalloc(%d) failed.\n", __func__, 10); + if (!buffer) return -ENOMEM; - } currentCommand = buffer; @@ -2276,10 +2257,9 @@ static int send_cmd_write_baud_rate(struct edgeport_port *edge_port, /* Alloc memory for the string of commands. */ cmdBuffer = kmalloc(0x100, GFP_ATOMIC); - if (!cmdBuffer) { - dev_err(dev, "%s - kmalloc(%d) failed.\n", __func__, 0x100); + if (!cmdBuffer) return -ENOMEM; - } + currCmd = cmdBuffer; /* Enable access to divisor latch */ @@ -2785,10 +2765,9 @@ static int edge_startup(struct usb_serial *serial) /* create our private serial structure */ edge_serial = kzalloc(sizeof(struct edgeport_serial), GFP_KERNEL); - if (edge_serial == NULL) { - dev_err(&serial->dev->dev, "%s - Out of memory\n", __func__); + if (!edge_serial) return -ENOMEM; - } + spin_lock_init(&edge_serial->es_lock); edge_serial->serial = serial; usb_set_serial_data(serial, edge_serial); @@ -2877,14 +2856,12 @@ static int edge_startup(struct usb_serial *serial) /* not set up yet, so do it now */ edge_serial->interrupt_read_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!edge_serial->interrupt_read_urb) { - dev_err(ddev, "out of memory\n"); + if (!edge_serial->interrupt_read_urb) return -ENOMEM; - } + edge_serial->interrupt_in_buffer = kmalloc(buffer_size, GFP_KERNEL); if (!edge_serial->interrupt_in_buffer) { - dev_err(ddev, "out of memory\n"); usb_free_urb(edge_serial->interrupt_read_urb); return -ENOMEM; } @@ -2914,14 +2891,12 @@ static int edge_startup(struct usb_serial *serial) /* not set up yet, so do it now */ edge_serial->read_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!edge_serial->read_urb) { - dev_err(ddev, "out of memory\n"); + if (!edge_serial->read_urb) return -ENOMEM; - } + edge_serial->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); if (!edge_serial->bulk_in_buffer) { - dev_err(&dev->dev, "out of memory\n"); usb_free_urb(edge_serial->read_urb); return -ENOMEM; } diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index b7187bf..a2db5be 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -20,7 +20,6 @@ #include <linux/kernel.h> #include <linux/jiffies.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/tty.h> #include <linux/tty_driver.h> @@ -364,11 +363,9 @@ static int write_boot_mem(struct edgeport_serial *serial, /* Must do a read before write */ if (!serial->TiReadI2C) { temp = kmalloc(1, GFP_KERNEL); - if (!temp) { - dev_err(&serial->serial->dev->dev, - "%s - out of memory\n", __func__); + if (!temp) return -ENOMEM; - } + status = read_boot_mem(serial, 0, 1, temp); kfree(temp); if (status) @@ -471,10 +468,8 @@ static int tx_active(struct edgeport_port *port) int bytes_left = 0; oedb = kmalloc(sizeof(*oedb), GFP_KERNEL); - if (!oedb) { - dev_err(&port->port->dev, "%s - out of memory\n", __func__); + if (!oedb) return -ENOMEM; - } lsr = kmalloc(1, GFP_KERNEL); /* Sigh, that's right, just one byte, as not all platforms can do DMA @@ -625,14 +620,11 @@ static int check_i2c_image(struct edgeport_serial *serial) __u16 ttype; rom_desc = kmalloc(sizeof(*rom_desc), GFP_KERNEL); - if (!rom_desc) { - dev_err(dev, "%s - out of memory\n", __func__); + if (!rom_desc) return -ENOMEM; - } + buffer = kmalloc(TI_MAX_I2C_SIZE, GFP_KERNEL); if (!buffer) { - dev_err(dev, "%s - out of memory when allocating buffer\n", - __func__); kfree(rom_desc); return -ENOMEM; } @@ -706,10 +698,9 @@ static int get_manuf_info(struct edgeport_serial *serial, __u8 *buffer) struct device *dev = &serial->serial->dev->dev; rom_desc = kmalloc(sizeof(*rom_desc), GFP_KERNEL); - if (!rom_desc) { - dev_err(dev, "%s - out of memory\n", __func__); + if (!rom_desc) return -ENOMEM; - } + start_address = get_descriptor_addr(serial, I2C_DESC_TYPE_ION, rom_desc); @@ -769,10 +760,8 @@ static int build_i2c_fw_hdr(__u8 *header, struct device *dev) sizeof(struct ti_i2c_firmware_rec)); buffer = kmalloc(buffer_size, GFP_KERNEL); - if (!buffer) { - dev_err(dev, "%s - out of memory\n", __func__); + if (!buffer) return -ENOMEM; - } // Set entire image of 0xffs memset(buffer, 0xff, buffer_size); @@ -832,10 +821,8 @@ static int i2c_type_bootmode(struct edgeport_serial *serial) u8 *data; data = kmalloc(1, GFP_KERNEL); - if (!data) { - dev_err(dev, "%s - out of memory\n", __func__); + if (!data) return -ENOMEM; - } /* Try to read type 2 */ status = ti_vread_sync(serial->serial->dev, UMPC_MEMORY_READ, @@ -986,10 +973,9 @@ static int download_fw(struct edgeport_serial *serial) * Read Manufacturing Descriptor from TI Based Edgeport */ ti_manuf_desc = kmalloc(sizeof(*ti_manuf_desc), GFP_KERNEL); - if (!ti_manuf_desc) { - dev_err(dev, "%s - out of memory.\n", __func__); + if (!ti_manuf_desc) return -ENOMEM; - } + status = get_manuf_info(serial, (__u8 *)ti_manuf_desc); if (status) { kfree(ti_manuf_desc); @@ -1006,7 +992,6 @@ static int download_fw(struct edgeport_serial *serial) rom_desc = kmalloc(sizeof(*rom_desc), GFP_KERNEL); if (!rom_desc) { - dev_err(dev, "%s - out of memory.\n", __func__); kfree(ti_manuf_desc); return -ENOMEM; } @@ -1023,7 +1008,6 @@ static int download_fw(struct edgeport_serial *serial) firmware_version = kmalloc(sizeof(*firmware_version), GFP_KERNEL); if (!firmware_version) { - dev_err(dev, "%s - out of memory.\n", __func__); kfree(rom_desc); kfree(ti_manuf_desc); return -ENOMEM; @@ -1068,8 +1052,6 @@ static int download_fw(struct edgeport_serial *serial) record = kmalloc(1, GFP_KERNEL); if (!record) { - dev_err(dev, "%s - out of memory.\n", - __func__); kfree(firmware_version); kfree(rom_desc); kfree(ti_manuf_desc); @@ -1153,7 +1135,6 @@ static int download_fw(struct edgeport_serial *serial) header = kmalloc(HEADER_SIZE, GFP_KERNEL); if (!header) { - dev_err(dev, "%s - out of memory.\n", __func__); kfree(rom_desc); kfree(ti_manuf_desc); return -ENOMEM; @@ -1161,7 +1142,6 @@ static int download_fw(struct edgeport_serial *serial) vheader = kmalloc(HEADER_SIZE, GFP_KERNEL); if (!vheader) { - dev_err(dev, "%s - out of memory.\n", __func__); kfree(header); kfree(rom_desc); kfree(ti_manuf_desc); @@ -1290,10 +1270,9 @@ static int download_fw(struct edgeport_serial *serial) * Read Manufacturing Descriptor from TI Based Edgeport */ ti_manuf_desc = kmalloc(sizeof(*ti_manuf_desc), GFP_KERNEL); - if (!ti_manuf_desc) { - dev_err(dev, "%s - out of memory.\n", __func__); + if (!ti_manuf_desc) return -ENOMEM; - } + status = get_manuf_info(serial, (__u8 *)ti_manuf_desc); if (status) { kfree(ti_manuf_desc); @@ -1328,10 +1307,8 @@ static int download_fw(struct edgeport_serial *serial) buffer_size = (((1024 * 16) - 512) + sizeof(struct ti_i2c_image_header)); buffer = kmalloc(buffer_size, GFP_KERNEL); - if (!buffer) { - dev_err(dev, "%s - out of memory\n", __func__); + if (!buffer) return -ENOMEM; - } /* Initialize the buffer to 0xff (pad the buffer) */ memset(buffer, 0xff, buffer_size); @@ -2122,7 +2099,6 @@ static void change_port_settings(struct tty_struct *tty, config = kmalloc (sizeof (*config), GFP_KERNEL); if (!config) { tty->termios = *old_termios; - dev_err(dev, "%s - out of memory\n", __func__); return; } @@ -2362,8 +2338,6 @@ static int edge_ioctl(struct tty_struct *tty, struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); - dev_dbg(&port->dev, "%s - cmd = 0x%x\n", __func__, cmd); - switch (cmd) { case TIOCGSERIAL: dev_dbg(&port->dev, "%s - TIOCGSERIAL\n", __func__); @@ -2395,10 +2369,9 @@ static int edge_startup(struct usb_serial *serial) /* create our private serial structure */ edge_serial = kzalloc(sizeof(struct edgeport_serial), GFP_KERNEL); - if (edge_serial == NULL) { - dev_err(&serial->dev->dev, "%s - Out of memory\n", __func__); + if (!edge_serial) return -ENOMEM; - } + mutex_init(&edge_serial->es_lock); edge_serial->serial = serial; usb_set_serial_data(serial, edge_serial); diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c index 76c9a84..f51a5d5 100644 --- a/drivers/usb/serial/ipaq.c +++ b/drivers/usb/serial/ipaq.c @@ -12,7 +12,6 @@ #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/tty.h> #include <linux/tty_driver.h> @@ -37,7 +36,7 @@ static int ipaq_open(struct tty_struct *tty, static int ipaq_calc_num_ports(struct usb_serial *serial); static int ipaq_startup(struct usb_serial *serial); -static struct usb_device_id ipaq_id_table [] = { +static const struct usb_device_id ipaq_id_table[] = { { USB_DEVICE(0x0104, 0x00BE) }, /* Socket USB Sync */ { USB_DEVICE(0x03F0, 0x1016) }, /* HP USB Sync */ { USB_DEVICE(0x03F0, 0x1116) }, /* HP USB Sync 1611 */ diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c index 155eab1..8b1cf18 100644 --- a/drivers/usb/serial/ipw.c +++ b/drivers/usb/serial/ipw.c @@ -38,7 +38,6 @@ #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/tty.h> #include <linux/tty_flip.h> diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index 716930a..73956d4 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -377,15 +377,12 @@ static void ir_set_termios(struct tty_struct *tty, * send the baud change out on an "empty" data packet */ urb = usb_alloc_urb(0, GFP_KERNEL); - if (!urb) { - dev_err(&port->dev, "%s - no more urbs\n", __func__); + if (!urb) return; - } + transfer_buffer = kmalloc(1, GFP_KERNEL); - if (!transfer_buffer) { - dev_err(&port->dev, "%s - out of memory\n", __func__); + if (!transfer_buffer) goto err_buf; - } *transfer_buffer = ir_xbof | ir_baud; diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index 57c439a..d00dae1 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -17,7 +17,6 @@ */ #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/tty.h> #include <linux/tty_driver.h> @@ -770,7 +769,7 @@ uart_enable_failed: return status; } -/* Diables the IUU UART (a.k.a. the Phoenix voiderface) */ +/* Disables the IUU UART (a.k.a. the Phoenix voiderface) */ static int iuu_uart_off(struct usb_serial_port *port) { int status; diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index d6960ae..265c677 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -31,7 +31,6 @@ #include <linux/kernel.h> #include <linux/jiffies.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/tty.h> #include <linux/tty_driver.h> @@ -165,7 +164,7 @@ static void keyspan_set_termios(struct tty_struct *tty, if (d_details->calculate_baud_rate(port, baud_rate, d_details->baudclk, NULL, NULL, NULL, device_port) == KEYSPAN_BAUD_RATE_OK) { /* FIXME - more to do here to ensure rate changes cleanly */ - /* FIXME - calcuate exact rate from divisor ? */ + /* FIXME - calculate exact rate from divisor ? */ p_priv->baud = baud_rate; } else baud_rate = tty_termios_baud_rate(old_termios); @@ -1226,10 +1225,8 @@ static struct urb *keyspan_setup_urb(struct usb_serial *serial, int endpoint, dev_dbg(&serial->interface->dev, "%s - alloc for endpoint %d.\n", __func__, endpoint); urb = usb_alloc_urb(0, GFP_KERNEL); /* No ISO */ - if (urb == NULL) { - dev_dbg(&serial->interface->dev, "%s - alloc for endpoint %d failed.\n", __func__, endpoint); + if (!urb) return NULL; - } if (endpoint == 0) { /* control EP filled in when used */ @@ -2312,10 +2309,8 @@ static int keyspan_startup(struct usb_serial *serial) /* Setup private data for serial driver */ s_priv = kzalloc(sizeof(struct keyspan_serial_private), GFP_KERNEL); - if (!s_priv) { - dev_dbg(&serial->dev->dev, "%s - kmalloc for keyspan_serial_private failed.\n", __func__); + if (!s_priv) return -ENOMEM; - } s_priv->instat_buf = kzalloc(INSTAT_BUFLEN, GFP_KERNEL); if (!s_priv->instat_buf) diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index 5f1d382e..e972412 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -17,7 +17,6 @@ #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/tty.h> #include <linux/tty_driver.h> diff --git a/drivers/usb/serial/keyspan_usa26msg.h b/drivers/usb/serial/keyspan_usa26msg.h index 3808727..09e21e8 100644 --- a/drivers/usb/serial/keyspan_usa26msg.h +++ b/drivers/usb/serial/keyspan_usa26msg.h @@ -62,7 +62,7 @@ or: (b) 0x80 bit set - indiates that the bytes following alternate data and + indicates that the bytes following alternate data and status bytes: STAT DATA STAT DATA STAT DATA STAT DATA ... diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index 1b4054f..c88cc49 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -37,7 +37,6 @@ #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/tty.h> #include <linux/tty_driver.h> @@ -182,11 +181,9 @@ static int klsi_105_get_line_state(struct usb_serial_port *port, dev_info(&port->serial->dev->dev, "sending SIO Poll request\n"); status_buf = kmalloc(KLSI_STATUSBUF_LEN, GFP_KERNEL); - if (!status_buf) { - dev_err(&port->dev, "%s - out of memory for status buffer.\n", - __func__); + if (!status_buf) return -ENOMEM; - } + status_buf[0] = 0xff; status_buf[1] = 0xff; rc = usb_control_msg(port->serial->dev, @@ -273,11 +270,9 @@ static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port) * priv->line_state. */ cfg = kmalloc(sizeof(*cfg), GFP_KERNEL); - if (!cfg) { - dev_err(&port->dev, "%s - out of memory for config buffer.\n", - __func__); + if (!cfg) return -ENOMEM; - } + cfg->pktlen = 5; cfg->baudrate = kl5kusb105a_sio_b9600; cfg->databits = kl5kusb105a_dtb_8; @@ -417,10 +412,8 @@ static void klsi_105_set_termios(struct tty_struct *tty, speed_t baud; cfg = kmalloc(sizeof(*cfg), GFP_KERNEL); - if (!cfg) { - dev_err(dev, "%s - out of memory for config buffer.\n", __func__); + if (!cfg) return; - } /* lock while we are modifying the settings */ spin_lock_irqsave(&priv->lock, flags); diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 4c94ffc..618c1c1 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -25,7 +25,6 @@ #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/tty.h> #include <linux/tty_driver.h> diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 6a15adf..fd707d6 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -23,7 +23,6 @@ #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/tty.h> #include <linux/tty_driver.h> diff --git a/drivers/usb/serial/metro-usb.c b/drivers/usb/serial/metro-usb.c index 40ccf6e..39e6830 100644 --- a/drivers/usb/serial/metro-usb.c +++ b/drivers/usb/serial/metro-usb.c @@ -7,7 +7,6 @@ */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/tty.h> #include <linux/module.h> #include <linux/usb.h> @@ -43,7 +42,7 @@ struct metrousb_private { }; /* Device table list. */ -static struct usb_device_id id_table[] = { +static const struct usb_device_id id_table[] = { { USB_DEVICE(FOCUS_VENDOR_ID, FOCUS_PRODUCT_ID_BI) }, { USB_DEVICE(FOCUS_VENDOR_ID, FOCUS_PRODUCT_ID_UNI) }, { }, /* Terminating entry. */ @@ -54,7 +53,7 @@ MODULE_DEVICE_TABLE(usb, id_table); #define UNI_CMD_OPEN 0x80 #define UNI_CMD_CLOSE 0xFF -inline int metrousb_is_unidirectional_mode(struct usb_serial_port *port) +static inline int metrousb_is_unidirectional_mode(struct usb_serial_port *port) { __u16 product_id = le16_to_cpu( port->serial->dev->descriptor.idProduct); diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 439c951..4eb2772 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -1,6 +1,6 @@ /* * mos7720.c - * Controls the Moschip 7720 usb to dual port serial convertor + * Controls the Moschip 7720 usb to dual port serial converter * * Copyright 2006 Moschip Semiconductor Tech. Ltd. * @@ -22,7 +22,6 @@ */ #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/tty.h> #include <linux/tty_driver.h> @@ -46,7 +45,7 @@ #define MOS_WRITE 0x0E #define MOS_READ 0x0D -/* Interrupt Rotinue Defines */ +/* Interrupt Routines Defines */ #define SERIAL_IIR_RLS 0x06 #define SERIAL_IIR_RDA 0x04 #define SERIAL_IIR_CTI 0x0c @@ -362,15 +361,13 @@ static int write_parport_reg_nonblock(struct mos7715_parport *mos_parport, /* create and initialize the control urb and containing urbtracker */ urbtrack = kmalloc(sizeof(struct urbtracker), GFP_ATOMIC); - if (urbtrack == NULL) { - dev_err(&usbdev->dev, "out of memory"); + if (!urbtrack) return -ENOMEM; - } + kref_get(&mos_parport->ref_count); urbtrack->mos_parport = mos_parport; urbtrack->urb = usb_alloc_urb(0, GFP_ATOMIC); - if (urbtrack->urb == NULL) { - dev_err(&usbdev->dev, "out of urbs"); + if (!urbtrack->urb) { kfree(urbtrack); return -ENOMEM; } @@ -440,7 +437,7 @@ static int write_parport_reg_nonblock(struct mos7715_parport *mos_parport, * not called the release function yet because someone has a serial port open. * The shared release_lock prevents the first, and the mutex and disconnected * flag maintained by usbserial covers the second. We also use the msg_pending - * flag to ensure that all synchronous usb messgage calls have completed before + * flag to ensure that all synchronous usb message calls have completed before * our release function can return. */ static int parport_prologue(struct parport *pp) @@ -471,7 +468,7 @@ static int parport_prologue(struct parport *pp) } /* - * This is the the common bottom part of all parallel port functions that send + * This is the common bottom part of all parallel port functions that send * synchronous messages to the device. */ static inline void parport_epilogue(struct parport *pp) @@ -702,10 +699,9 @@ static int mos7715_parport_init(struct usb_serial *serial) /* allocate and initialize parallel port control struct */ mos_parport = kzalloc(sizeof(struct mos7715_parport), GFP_KERNEL); - if (mos_parport == NULL) { - dev_dbg(&serial->dev->dev, "%s: kzalloc failed\n", __func__); + if (!mos_parport) return -ENOMEM; - } + mos_parport->msg_pending = false; kref_init(&mos_parport->ref_count); spin_lock_init(&mos_parport->listlock); @@ -1018,18 +1014,12 @@ static int mos7720_open(struct tty_struct *tty, struct usb_serial_port *port) for (j = 0; j < NUM_URBS; ++j) { urb = usb_alloc_urb(0, GFP_KERNEL); mos7720_port->write_urb_pool[j] = urb; - - if (urb == NULL) { - dev_err(&port->dev, "No more urbs???\n"); + if (!urb) continue; - } urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL); if (!urb->transfer_buffer) { - dev_err(&port->dev, - "%s-out of memory for urb buffers.\n", - __func__); usb_free_urb(mos7720_port->write_urb_pool[j]); mos7720_port->write_urb_pool[j] = NULL; continue; @@ -1250,11 +1240,8 @@ static int mos7720_write(struct tty_struct *tty, struct usb_serial_port *port, if (urb->transfer_buffer == NULL) { urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL); - if (urb->transfer_buffer == NULL) { - dev_err_console(port, "%s no more kernel memory...\n", - __func__); + if (!urb->transfer_buffer) goto exit; - } } transfer_size = min(count, URB_TRANSFER_BUFFER_SIZE); @@ -1885,8 +1872,6 @@ static int mos7720_ioctl(struct tty_struct *tty, if (mos7720_port == NULL) return -ENODEV; - dev_dbg(&port->dev, "%s - cmd = 0x%x", __func__, cmd); - switch (cmd) { case TIOCSERGETLSR: dev_dbg(&port->dev, "%s TIOCSERGETLSR\n", __func__); diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index a69da83..e9d967f 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -24,7 +24,6 @@ #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/tty.h> #include <linux/tty_driver.h> @@ -876,20 +875,14 @@ static int mos7840_open(struct tty_struct *tty, struct usb_serial_port *port) for (j = 0; j < NUM_URBS; ++j) { urb = usb_alloc_urb(0, GFP_KERNEL); mos7840_port->write_urb_pool[j] = urb; - - if (urb == NULL) { - dev_err(&port->dev, "No more urbs???\n"); + if (!urb) continue; - } urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL); if (!urb->transfer_buffer) { usb_free_urb(urb); mos7840_port->write_urb_pool[j] = NULL; - dev_err(&port->dev, - "%s-out of memory for urb buffers.\n", - __func__); continue; } } @@ -1381,12 +1374,8 @@ static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port, if (urb->transfer_buffer == NULL) { urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL); - - if (urb->transfer_buffer == NULL) { - dev_err_console(port, "%s no more kernel memory...\n", - __func__); + if (!urb->transfer_buffer) goto exit; - } } transfer_size = min(count, URB_TRANSFER_BUFFER_SIZE); @@ -2070,8 +2059,6 @@ static int mos7840_ioctl(struct tty_struct *tty, if (mos7840_port == NULL) return -1; - dev_dbg(&port->dev, "%s - cmd = 0x%x\n", __func__, cmd); - switch (cmd) { /* return number of bytes available */ @@ -2208,10 +2195,8 @@ static int mos7840_port_probe(struct usb_serial_port *port) dev_dbg(&port->dev, "mos7840_startup: configuring port %d\n", pnum); mos7840_port = kzalloc(sizeof(struct moschip_port), GFP_KERNEL); - if (mos7840_port == NULL) { - dev_err(&port->dev, "%s - Out of memory\n", __func__); + if (!mos7840_port) return -ENOMEM; - } /* Initialize all port interrupt end point to port 0 int * endpoint. Our device has only one interrupt end point diff --git a/drivers/usb/serial/mxuport.c b/drivers/usb/serial/mxuport.c new file mode 100644 index 0000000..ab1d690 --- /dev/null +++ b/drivers/usb/serial/mxuport.c @@ -0,0 +1,1393 @@ +/* + * mxuport.c - MOXA UPort series driver + * + * Copyright (c) 2006 Moxa Technologies Co., Ltd. + * Copyright (c) 2013 Andrew Lunn <andrew@lunn.ch> + * + * 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. + * + * Supports the following Moxa USB to serial converters: + * 2 ports : UPort 1250, UPort 1250I + * 4 ports : UPort 1410, UPort 1450, UPort 1450I + * 8 ports : UPort 1610-8, UPort 1650-8 + * 16 ports : UPort 1610-16, UPort 1650-16 + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/firmware.h> +#include <linux/jiffies.h> +#include <linux/serial.h> +#include <linux/serial_reg.h> +#include <linux/slab.h> +#include <linux/tty.h> +#include <linux/tty_driver.h> +#include <linux/tty_flip.h> +#include <linux/uaccess.h> +#include <linux/usb.h> +#include <linux/usb/serial.h> +#include <asm/unaligned.h> + +/* Definitions for the vendor ID and device ID */ +#define MX_USBSERIAL_VID 0x110A +#define MX_UPORT1250_PID 0x1250 +#define MX_UPORT1251_PID 0x1251 +#define MX_UPORT1410_PID 0x1410 +#define MX_UPORT1450_PID 0x1450 +#define MX_UPORT1451_PID 0x1451 +#define MX_UPORT1618_PID 0x1618 +#define MX_UPORT1658_PID 0x1658 +#define MX_UPORT1613_PID 0x1613 +#define MX_UPORT1653_PID 0x1653 + +/* Definitions for USB info */ +#define HEADER_SIZE 4 +#define EVENT_LENGTH 8 +#define DOWN_BLOCK_SIZE 64 + +/* Definitions for firmware info */ +#define VER_ADDR_1 0x20 +#define VER_ADDR_2 0x24 +#define VER_ADDR_3 0x28 + +/* Definitions for USB vendor request */ +#define RQ_VENDOR_NONE 0x00 +#define RQ_VENDOR_SET_BAUD 0x01 /* Set baud rate */ +#define RQ_VENDOR_SET_LINE 0x02 /* Set line status */ +#define RQ_VENDOR_SET_CHARS 0x03 /* Set Xon/Xoff chars */ +#define RQ_VENDOR_SET_RTS 0x04 /* Set RTS */ +#define RQ_VENDOR_SET_DTR 0x05 /* Set DTR */ +#define RQ_VENDOR_SET_XONXOFF 0x06 /* Set auto Xon/Xoff */ +#define RQ_VENDOR_SET_RX_HOST_EN 0x07 /* Set RX host enable */ +#define RQ_VENDOR_SET_OPEN 0x08 /* Set open/close port */ +#define RQ_VENDOR_PURGE 0x09 /* Purge Rx/Tx buffer */ +#define RQ_VENDOR_SET_MCR 0x0A /* Set MCR register */ +#define RQ_VENDOR_SET_BREAK 0x0B /* Set Break signal */ + +#define RQ_VENDOR_START_FW_DOWN 0x0C /* Start firmware download */ +#define RQ_VENDOR_STOP_FW_DOWN 0x0D /* Stop firmware download */ +#define RQ_VENDOR_QUERY_FW_READY 0x0E /* Query if new firmware ready */ + +#define RQ_VENDOR_SET_FIFO_DISABLE 0x0F /* Set fifo disable */ +#define RQ_VENDOR_SET_INTERFACE 0x10 /* Set interface */ +#define RQ_VENDOR_SET_HIGH_PERFOR 0x11 /* Set hi-performance */ + +#define RQ_VENDOR_ERASE_BLOCK 0x12 /* Erase flash block */ +#define RQ_VENDOR_WRITE_PAGE 0x13 /* Write flash page */ +#define RQ_VENDOR_PREPARE_WRITE 0x14 /* Prepare write flash */ +#define RQ_VENDOR_CONFIRM_WRITE 0x15 /* Confirm write flash */ +#define RQ_VENDOR_LOCATE 0x16 /* Locate the device */ + +#define RQ_VENDOR_START_ROM_DOWN 0x17 /* Start firmware download */ +#define RQ_VENDOR_ROM_DATA 0x18 /* Rom file data */ +#define RQ_VENDOR_STOP_ROM_DOWN 0x19 /* Stop firmware download */ +#define RQ_VENDOR_FW_DATA 0x20 /* Firmware data */ + +#define RQ_VENDOR_RESET_DEVICE 0x23 /* Try to reset the device */ +#define RQ_VENDOR_QUERY_FW_CONFIG 0x24 + +#define RQ_VENDOR_GET_VERSION 0x81 /* Get firmware version */ +#define RQ_VENDOR_GET_PAGE 0x82 /* Read flash page */ +#define RQ_VENDOR_GET_ROM_PROC 0x83 /* Get ROM process state */ + +#define RQ_VENDOR_GET_INQUEUE 0x84 /* Data in input buffer */ +#define RQ_VENDOR_GET_OUTQUEUE 0x85 /* Data in output buffer */ + +#define RQ_VENDOR_GET_MSR 0x86 /* Get modem status register */ + +/* Definitions for UPort event type */ +#define UPORT_EVENT_NONE 0 /* None */ +#define UPORT_EVENT_TXBUF_THRESHOLD 1 /* Tx buffer threshold */ +#define UPORT_EVENT_SEND_NEXT 2 /* Send next */ +#define UPORT_EVENT_MSR 3 /* Modem status */ +#define UPORT_EVENT_LSR 4 /* Line status */ +#define UPORT_EVENT_MCR 5 /* Modem control */ + +/* Definitions for serial event type */ +#define SERIAL_EV_CTS 0x0008 /* CTS changed state */ +#define SERIAL_EV_DSR 0x0010 /* DSR changed state */ +#define SERIAL_EV_RLSD 0x0020 /* RLSD changed state */ + +/* Definitions for modem control event type */ +#define SERIAL_EV_XOFF 0x40 /* XOFF received */ + +/* Definitions for line control of communication */ +#define MX_WORDLENGTH_5 5 +#define MX_WORDLENGTH_6 6 +#define MX_WORDLENGTH_7 7 +#define MX_WORDLENGTH_8 8 + +#define MX_PARITY_NONE 0 +#define MX_PARITY_ODD 1 +#define MX_PARITY_EVEN 2 +#define MX_PARITY_MARK 3 +#define MX_PARITY_SPACE 4 + +#define MX_STOP_BITS_1 0 +#define MX_STOP_BITS_1_5 1 +#define MX_STOP_BITS_2 2 + +#define MX_RTS_DISABLE 0x0 +#define MX_RTS_ENABLE 0x1 +#define MX_RTS_HW 0x2 +#define MX_RTS_NO_CHANGE 0x3 /* Flag, not valid register value*/ + +#define MX_INT_RS232 0 +#define MX_INT_2W_RS485 1 +#define MX_INT_RS422 2 +#define MX_INT_4W_RS485 3 + +/* Definitions for holding reason */ +#define MX_WAIT_FOR_CTS 0x0001 +#define MX_WAIT_FOR_DSR 0x0002 +#define MX_WAIT_FOR_DCD 0x0004 +#define MX_WAIT_FOR_XON 0x0008 +#define MX_WAIT_FOR_START_TX 0x0010 +#define MX_WAIT_FOR_UNTHROTTLE 0x0020 +#define MX_WAIT_FOR_LOW_WATER 0x0040 +#define MX_WAIT_FOR_SEND_NEXT 0x0080 + +#define MX_UPORT_2_PORT BIT(0) +#define MX_UPORT_4_PORT BIT(1) +#define MX_UPORT_8_PORT BIT(2) +#define MX_UPORT_16_PORT BIT(3) + +/* This structure holds all of the local port information */ +struct mxuport_port { + u8 mcr_state; /* Last MCR state */ + u8 msr_state; /* Last MSR state */ + struct mutex mutex; /* Protects mcr_state */ + spinlock_t spinlock; /* Protects msr_state */ +}; + +/* Table of devices that work with this driver */ +static const struct usb_device_id mxuport_idtable[] = { + { USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1250_PID), + .driver_info = MX_UPORT_2_PORT }, + { USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1251_PID), + .driver_info = MX_UPORT_2_PORT }, + { USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1410_PID), + .driver_info = MX_UPORT_4_PORT }, + { USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1450_PID), + .driver_info = MX_UPORT_4_PORT }, + { USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1451_PID), + .driver_info = MX_UPORT_4_PORT }, + { USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1618_PID), + .driver_info = MX_UPORT_8_PORT }, + { USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1658_PID), + .driver_info = MX_UPORT_8_PORT }, + { USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1613_PID), + .driver_info = MX_UPORT_16_PORT }, + { USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1653_PID), + .driver_info = MX_UPORT_16_PORT }, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, mxuport_idtable); + +/* + * Add a four byte header containing the port number and the number of + * bytes of data in the message. Return the number of bytes in the + * buffer. + */ +static int mxuport_prepare_write_buffer(struct usb_serial_port *port, + void *dest, size_t size) +{ + u8 *buf = dest; + int count; + + count = kfifo_out_locked(&port->write_fifo, buf + HEADER_SIZE, + size - HEADER_SIZE, + &port->lock); + + put_unaligned_be16(port->port_number, buf); + put_unaligned_be16(count, buf + 2); + + dev_dbg(&port->dev, "%s - size %zd count %d\n", __func__, + size, count); + + return count + HEADER_SIZE; +} + +/* Read the given buffer in from the control pipe. */ +static int mxuport_recv_ctrl_urb(struct usb_serial *serial, + u8 request, u16 value, u16 index, + u8 *data, size_t size) +{ + int status; + + status = usb_control_msg(serial->dev, + usb_rcvctrlpipe(serial->dev, 0), + request, + (USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_DEVICE), value, index, + data, size, + USB_CTRL_GET_TIMEOUT); + if (status < 0) { + dev_err(&serial->interface->dev, + "%s - usb_control_msg failed (%d)\n", + __func__, status); + return status; + } + + if (status != size) { + dev_err(&serial->interface->dev, + "%s - short read (%d / %zd)\n", + __func__, status, size); + return -EIO; + } + + return status; +} + +/* Write the given buffer out to the control pipe. */ +static int mxuport_send_ctrl_data_urb(struct usb_serial *serial, + u8 request, + u16 value, u16 index, + u8 *data, size_t size) +{ + int status; + + status = usb_control_msg(serial->dev, + usb_sndctrlpipe(serial->dev, 0), + request, + (USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE), value, index, + data, size, + USB_CTRL_SET_TIMEOUT); + if (status < 0) { + dev_err(&serial->interface->dev, + "%s - usb_control_msg failed (%d)\n", + __func__, status); + return status; + } + + if (status != size) { + dev_err(&serial->interface->dev, + "%s - short write (%d / %zd)\n", + __func__, status, size); + return -EIO; + } + + return 0; +} + +/* Send a vendor request without any data */ +static int mxuport_send_ctrl_urb(struct usb_serial *serial, + u8 request, u16 value, u16 index) +{ + return mxuport_send_ctrl_data_urb(serial, request, value, index, + NULL, 0); +} + +/* + * mxuport_throttle - throttle function of driver + * + * This function is called by the tty driver when it wants to stop the + * data being read from the port. Since all the data comes over one + * bulk in endpoint, we cannot stop submitting urbs by setting + * port->throttle. Instead tell the device to stop sending us data for + * the port. + */ +static void mxuport_throttle(struct tty_struct *tty) +{ + struct usb_serial_port *port = tty->driver_data; + struct usb_serial *serial = port->serial; + + dev_dbg(&port->dev, "%s\n", __func__); + + mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_RX_HOST_EN, + 0, port->port_number); +} + +/* + * mxuport_unthrottle - unthrottle function of driver + * + * This function is called by the tty driver when it wants to resume + * the data being read from the port. Tell the device it can resume + * sending us received data from the port. + */ +static void mxuport_unthrottle(struct tty_struct *tty) +{ + + struct usb_serial_port *port = tty->driver_data; + struct usb_serial *serial = port->serial; + + dev_dbg(&port->dev, "%s\n", __func__); + + mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_RX_HOST_EN, + 1, port->port_number); +} + +/* + * Processes one chunk of data received for a port. Mostly a copy of + * usb_serial_generic_process_read_urb(). + */ +static void mxuport_process_read_urb_data(struct usb_serial_port *port, + char *data, int size) +{ + int i; + + if (!port->port.console || !port->sysrq) { + tty_insert_flip_string(&port->port, data, size); + } else { + for (i = 0; i < size; i++, data++) { + if (!usb_serial_handle_sysrq_char(port, *data)) + tty_insert_flip_char(&port->port, *data, + TTY_NORMAL); + } + } + tty_flip_buffer_push(&port->port); +} + +static void mxuport_msr_event(struct usb_serial_port *port, u8 buf[4]) +{ + struct mxuport_port *mxport = usb_get_serial_port_data(port); + u8 rcv_msr_hold = buf[2] & 0xF0; + u16 rcv_msr_event = get_unaligned_be16(buf); + unsigned long flags; + + if (rcv_msr_event == 0) + return; + + /* Update MSR status */ + spin_lock_irqsave(&mxport->spinlock, flags); + + dev_dbg(&port->dev, "%s - current MSR status = 0x%x\n", + __func__, mxport->msr_state); + + if (rcv_msr_hold & UART_MSR_CTS) { + mxport->msr_state |= UART_MSR_CTS; + dev_dbg(&port->dev, "%s - CTS high\n", __func__); + } else { + mxport->msr_state &= ~UART_MSR_CTS; + dev_dbg(&port->dev, "%s - CTS low\n", __func__); + } + + if (rcv_msr_hold & UART_MSR_DSR) { + mxport->msr_state |= UART_MSR_DSR; + dev_dbg(&port->dev, "%s - DSR high\n", __func__); + } else { + mxport->msr_state &= ~UART_MSR_DSR; + dev_dbg(&port->dev, "%s - DSR low\n", __func__); + } + + if (rcv_msr_hold & UART_MSR_DCD) { + mxport->msr_state |= UART_MSR_DCD; + dev_dbg(&port->dev, "%s - DCD high\n", __func__); + } else { + mxport->msr_state &= ~UART_MSR_DCD; + dev_dbg(&port->dev, "%s - DCD low\n", __func__); + } + spin_unlock_irqrestore(&mxport->spinlock, flags); + + if (rcv_msr_event & + (SERIAL_EV_CTS | SERIAL_EV_DSR | SERIAL_EV_RLSD)) { + + if (rcv_msr_event & SERIAL_EV_CTS) { + port->icount.cts++; + dev_dbg(&port->dev, "%s - CTS change\n", __func__); + } + + if (rcv_msr_event & SERIAL_EV_DSR) { + port->icount.dsr++; + dev_dbg(&port->dev, "%s - DSR change\n", __func__); + } + + if (rcv_msr_event & SERIAL_EV_RLSD) { + port->icount.dcd++; + dev_dbg(&port->dev, "%s - DCD change\n", __func__); + } + wake_up_interruptible(&port->port.delta_msr_wait); + } +} + +static void mxuport_lsr_event(struct usb_serial_port *port, u8 buf[4]) +{ + u8 lsr_event = buf[2]; + + if (lsr_event & UART_LSR_BI) { + port->icount.brk++; + dev_dbg(&port->dev, "%s - break error\n", __func__); + } + + if (lsr_event & UART_LSR_FE) { + port->icount.frame++; + dev_dbg(&port->dev, "%s - frame error\n", __func__); + } + + if (lsr_event & UART_LSR_PE) { + port->icount.parity++; + dev_dbg(&port->dev, "%s - parity error\n", __func__); + } + + if (lsr_event & UART_LSR_OE) { + port->icount.overrun++; + dev_dbg(&port->dev, "%s - overrun error\n", __func__); + } +} + +/* + * When something interesting happens, modem control lines XON/XOFF + * etc, the device sends an event. Process these events. + */ +static void mxuport_process_read_urb_event(struct usb_serial_port *port, + u8 buf[4], u32 event) +{ + dev_dbg(&port->dev, "%s - receive event : %04x\n", __func__, event); + + switch (event) { + case UPORT_EVENT_SEND_NEXT: + /* + * Sent as part of the flow control on device buffers. + * Not currently used. + */ + break; + case UPORT_EVENT_MSR: + mxuport_msr_event(port, buf); + break; + case UPORT_EVENT_LSR: + mxuport_lsr_event(port, buf); + break; + case UPORT_EVENT_MCR: + /* + * Event to indicate a change in XON/XOFF from the + * peer. Currently not used. We just continue + * sending the device data and it will buffer it if + * needed. This event could be used for flow control + * between the host and the device. + */ + break; + default: + dev_dbg(&port->dev, "Unexpected event\n"); + break; + } +} + +/* + * One URB can contain data for multiple ports. Demultiplex the data, + * checking the port exists, is opened and the message is valid. + */ +static void mxuport_process_read_urb_demux_data(struct urb *urb) +{ + struct usb_serial_port *port = urb->context; + struct usb_serial *serial = port->serial; + u8 *data = urb->transfer_buffer; + u8 *end = data + urb->actual_length; + struct usb_serial_port *demux_port; + u8 *ch; + u16 rcv_port; + u16 rcv_len; + + while (data < end) { + if (data + HEADER_SIZE > end) { + dev_warn(&port->dev, "%s - message with short header\n", + __func__); + return; + } + + rcv_port = get_unaligned_be16(data); + if (rcv_port >= serial->num_ports) { + dev_warn(&port->dev, "%s - message for invalid port\n", + __func__); + return; + } + + demux_port = serial->port[rcv_port]; + rcv_len = get_unaligned_be16(data + 2); + if (!rcv_len || data + HEADER_SIZE + rcv_len > end) { + dev_warn(&port->dev, "%s - short data\n", __func__); + return; + } + + if (test_bit(ASYNCB_INITIALIZED, &demux_port->port.flags)) { + ch = data + HEADER_SIZE; + mxuport_process_read_urb_data(demux_port, ch, rcv_len); + } else { + dev_dbg(&demux_port->dev, "%s - data for closed port\n", + __func__); + } + data += HEADER_SIZE + rcv_len; + } +} + +/* + * One URB can contain events for multiple ports. Demultiplex the event, + * checking the port exists, and is opened. + */ +static void mxuport_process_read_urb_demux_event(struct urb *urb) +{ + struct usb_serial_port *port = urb->context; + struct usb_serial *serial = port->serial; + u8 *data = urb->transfer_buffer; + u8 *end = data + urb->actual_length; + struct usb_serial_port *demux_port; + u8 *ch; + u16 rcv_port; + u16 rcv_event; + + while (data < end) { + if (data + EVENT_LENGTH > end) { + dev_warn(&port->dev, "%s - message with short event\n", + __func__); + return; + } + + rcv_port = get_unaligned_be16(data); + if (rcv_port >= serial->num_ports) { + dev_warn(&port->dev, "%s - message for invalid port\n", + __func__); + return; + } + + demux_port = serial->port[rcv_port]; + if (test_bit(ASYNCB_INITIALIZED, &demux_port->port.flags)) { + ch = data + HEADER_SIZE; + rcv_event = get_unaligned_be16(data + 2); + mxuport_process_read_urb_event(demux_port, ch, + rcv_event); + } else { + dev_dbg(&demux_port->dev, + "%s - event for closed port\n", __func__); + } + data += EVENT_LENGTH; + } +} + +/* + * This is called when we have received data on the bulk in + * endpoint. Depending on which port it was received on, it can + * contain serial data or events. + */ +static void mxuport_process_read_urb(struct urb *urb) +{ + struct usb_serial_port *port = urb->context; + struct usb_serial *serial = port->serial; + + if (port == serial->port[0]) + mxuport_process_read_urb_demux_data(urb); + + if (port == serial->port[1]) + mxuport_process_read_urb_demux_event(urb); +} + +/* + * Ask the device how many bytes it has queued to be sent out. If + * there are none, return true. + */ +static bool mxuport_tx_empty(struct usb_serial_port *port) +{ + struct usb_serial *serial = port->serial; + bool is_empty = true; + u32 txlen; + u8 *len_buf; + int err; + + len_buf = kzalloc(4, GFP_KERNEL); + if (!len_buf) + goto out; + + err = mxuport_recv_ctrl_urb(serial, RQ_VENDOR_GET_OUTQUEUE, 0, + port->port_number, len_buf, 4); + if (err < 0) + goto out; + + txlen = get_unaligned_be32(len_buf); + dev_dbg(&port->dev, "%s - tx len = %u\n", __func__, txlen); + + if (txlen != 0) + is_empty = false; + +out: + kfree(len_buf); + return is_empty; +} + +static int mxuport_set_mcr(struct usb_serial_port *port, u8 mcr_state) +{ + struct usb_serial *serial = port->serial; + int err; + + dev_dbg(&port->dev, "%s - %02x\n", __func__, mcr_state); + + err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_MCR, + mcr_state, port->port_number); + if (err) + dev_err(&port->dev, "%s - failed to change MCR\n", __func__); + + return err; +} + +static int mxuport_set_dtr(struct usb_serial_port *port, int on) +{ + struct mxuport_port *mxport = usb_get_serial_port_data(port); + struct usb_serial *serial = port->serial; + int err; + + mutex_lock(&mxport->mutex); + + err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_DTR, + !!on, port->port_number); + if (!err) { + if (on) + mxport->mcr_state |= UART_MCR_DTR; + else + mxport->mcr_state &= ~UART_MCR_DTR; + } + + mutex_unlock(&mxport->mutex); + + return err; +} + +static int mxuport_set_rts(struct usb_serial_port *port, u8 state) +{ + struct mxuport_port *mxport = usb_get_serial_port_data(port); + struct usb_serial *serial = port->serial; + int err; + u8 mcr_state; + + mutex_lock(&mxport->mutex); + mcr_state = mxport->mcr_state; + + switch (state) { + case MX_RTS_DISABLE: + mcr_state &= ~UART_MCR_RTS; + break; + case MX_RTS_ENABLE: + mcr_state |= UART_MCR_RTS; + break; + case MX_RTS_HW: + /* + * Do not update mxport->mcr_state when doing hardware + * flow control. + */ + break; + default: + /* + * Should not happen, but somebody might try passing + * MX_RTS_NO_CHANGE, which is not valid. + */ + err = -EINVAL; + goto out; + } + err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_RTS, + state, port->port_number); + if (!err) + mxport->mcr_state = mcr_state; + +out: + mutex_unlock(&mxport->mutex); + + return err; +} + +static void mxuport_dtr_rts(struct usb_serial_port *port, int on) +{ + struct mxuport_port *mxport = usb_get_serial_port_data(port); + u8 mcr_state; + int err; + + mutex_lock(&mxport->mutex); + mcr_state = mxport->mcr_state; + + if (on) + mcr_state |= (UART_MCR_RTS | UART_MCR_DTR); + else + mcr_state &= ~(UART_MCR_RTS | UART_MCR_DTR); + + err = mxuport_set_mcr(port, mcr_state); + if (!err) + mxport->mcr_state = mcr_state; + + mutex_unlock(&mxport->mutex); +} + +static int mxuport_tiocmset(struct tty_struct *tty, unsigned int set, + unsigned int clear) +{ + struct usb_serial_port *port = tty->driver_data; + struct mxuport_port *mxport = usb_get_serial_port_data(port); + int err; + u8 mcr_state; + + mutex_lock(&mxport->mutex); + mcr_state = mxport->mcr_state; + + if (set & TIOCM_RTS) + mcr_state |= UART_MCR_RTS; + + if (set & TIOCM_DTR) + mcr_state |= UART_MCR_DTR; + + if (clear & TIOCM_RTS) + mcr_state &= ~UART_MCR_RTS; + + if (clear & TIOCM_DTR) + mcr_state &= ~UART_MCR_DTR; + + err = mxuport_set_mcr(port, mcr_state); + if (!err) + mxport->mcr_state = mcr_state; + + mutex_unlock(&mxport->mutex); + + return err; +} + +static int mxuport_tiocmget(struct tty_struct *tty) +{ + struct mxuport_port *mxport; + struct usb_serial_port *port = tty->driver_data; + unsigned int result; + unsigned long flags; + unsigned int msr; + unsigned int mcr; + + mxport = usb_get_serial_port_data(port); + + mutex_lock(&mxport->mutex); + spin_lock_irqsave(&mxport->spinlock, flags); + + msr = mxport->msr_state; + mcr = mxport->mcr_state; + + spin_unlock_irqrestore(&mxport->spinlock, flags); + mutex_unlock(&mxport->mutex); + + result = (((mcr & UART_MCR_DTR) ? TIOCM_DTR : 0) | /* 0x002 */ + ((mcr & UART_MCR_RTS) ? TIOCM_RTS : 0) | /* 0x004 */ + ((msr & UART_MSR_CTS) ? TIOCM_CTS : 0) | /* 0x020 */ + ((msr & UART_MSR_DCD) ? TIOCM_CAR : 0) | /* 0x040 */ + ((msr & UART_MSR_RI) ? TIOCM_RI : 0) | /* 0x080 */ + ((msr & UART_MSR_DSR) ? TIOCM_DSR : 0)); /* 0x100 */ + + dev_dbg(&port->dev, "%s - 0x%04x\n", __func__, result); + + return result; +} + +static int mxuport_set_termios_flow(struct tty_struct *tty, + struct ktermios *old_termios, + struct usb_serial_port *port, + struct usb_serial *serial) +{ + u8 xon = START_CHAR(tty); + u8 xoff = STOP_CHAR(tty); + int enable; + int err; + u8 *buf; + u8 rts; + + buf = kmalloc(2, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* S/W flow control settings */ + if (I_IXOFF(tty) || I_IXON(tty)) { + enable = 1; + buf[0] = xon; + buf[1] = xoff; + + err = mxuport_send_ctrl_data_urb(serial, RQ_VENDOR_SET_CHARS, + 0, port->port_number, + buf, 2); + if (err) + goto out; + + dev_dbg(&port->dev, "%s - XON = 0x%02x, XOFF = 0x%02x\n", + __func__, xon, xoff); + } else { + enable = 0; + } + + err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_XONXOFF, + enable, port->port_number); + if (err) + goto out; + + rts = MX_RTS_NO_CHANGE; + + /* H/W flow control settings */ + if (!old_termios || + C_CRTSCTS(tty) != (old_termios->c_cflag & CRTSCTS)) { + if (C_CRTSCTS(tty)) + rts = MX_RTS_HW; + else + rts = MX_RTS_ENABLE; + } + + if (C_BAUD(tty)) { + if (old_termios && (old_termios->c_cflag & CBAUD) == B0) { + /* Raise DTR and RTS */ + if (C_CRTSCTS(tty)) + rts = MX_RTS_HW; + else + rts = MX_RTS_ENABLE; + mxuport_set_dtr(port, 1); + } + } else { + /* Drop DTR and RTS */ + rts = MX_RTS_DISABLE; + mxuport_set_dtr(port, 0); + } + + if (rts != MX_RTS_NO_CHANGE) + err = mxuport_set_rts(port, rts); + +out: + kfree(buf); + return err; +} + +static void mxuport_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, + struct ktermios *old_termios) +{ + struct usb_serial *serial = port->serial; + u8 *buf; + u8 data_bits; + u8 stop_bits; + u8 parity; + int baud; + int err; + + if (old_termios && + !tty_termios_hw_change(&tty->termios, old_termios) && + tty->termios.c_iflag == old_termios->c_iflag) { + dev_dbg(&port->dev, "%s - nothing to change\n", __func__); + return; + } + + buf = kmalloc(4, GFP_KERNEL); + if (!buf) + return; + + /* Set data bit of termios */ + switch (C_CSIZE(tty)) { + case CS5: + data_bits = MX_WORDLENGTH_5; + break; + case CS6: + data_bits = MX_WORDLENGTH_6; + break; + case CS7: + data_bits = MX_WORDLENGTH_7; + break; + case CS8: + default: + data_bits = MX_WORDLENGTH_8; + break; + } + + /* Set parity of termios */ + if (C_PARENB(tty)) { + if (C_CMSPAR(tty)) { + if (C_PARODD(tty)) + parity = MX_PARITY_MARK; + else + parity = MX_PARITY_SPACE; + } else { + if (C_PARODD(tty)) + parity = MX_PARITY_ODD; + else + parity = MX_PARITY_EVEN; + } + } else { + parity = MX_PARITY_NONE; + } + + /* Set stop bit of termios */ + if (C_CSTOPB(tty)) + stop_bits = MX_STOP_BITS_2; + else + stop_bits = MX_STOP_BITS_1; + + buf[0] = data_bits; + buf[1] = parity; + buf[2] = stop_bits; + buf[3] = 0; + + err = mxuport_send_ctrl_data_urb(serial, RQ_VENDOR_SET_LINE, + 0, port->port_number, buf, 4); + if (err) + goto out; + + err = mxuport_set_termios_flow(tty, old_termios, port, serial); + if (err) + goto out; + + baud = tty_get_baud_rate(tty); + if (!baud) + baud = 9600; + + /* Note: Little Endian */ + put_unaligned_le32(baud, buf); + + err = mxuport_send_ctrl_data_urb(serial, RQ_VENDOR_SET_BAUD, + 0, port->port_number, + buf, 4); + if (err) + goto out; + + dev_dbg(&port->dev, "baud_rate : %d\n", baud); + dev_dbg(&port->dev, "data_bits : %d\n", data_bits); + dev_dbg(&port->dev, "parity : %d\n", parity); + dev_dbg(&port->dev, "stop_bits : %d\n", stop_bits); + +out: + kfree(buf); +} + +/* + * Determine how many ports this device has dynamically. It will be + * called after the probe() callback is called, but before attach(). + */ +static int mxuport_calc_num_ports(struct usb_serial *serial) +{ + unsigned long features = (unsigned long)usb_get_serial_data(serial); + + if (features & MX_UPORT_2_PORT) + return 2; + if (features & MX_UPORT_4_PORT) + return 4; + if (features & MX_UPORT_8_PORT) + return 8; + if (features & MX_UPORT_16_PORT) + return 16; + + return 0; +} + +/* Get the version of the firmware currently running. */ +static int mxuport_get_fw_version(struct usb_serial *serial, u32 *version) +{ + u8 *ver_buf; + int err; + + ver_buf = kzalloc(4, GFP_KERNEL); + if (!ver_buf) + return -ENOMEM; + + /* Get firmware version from SDRAM */ + err = mxuport_recv_ctrl_urb(serial, RQ_VENDOR_GET_VERSION, 0, 0, + ver_buf, 4); + if (err != 4) { + err = -EIO; + goto out; + } + + *version = (ver_buf[0] << 16) | (ver_buf[1] << 8) | ver_buf[2]; + err = 0; +out: + kfree(ver_buf); + return err; +} + +/* Given a firmware blob, download it to the device. */ +static int mxuport_download_fw(struct usb_serial *serial, + const struct firmware *fw_p) +{ + u8 *fw_buf; + size_t txlen; + size_t fwidx; + int err; + + fw_buf = kmalloc(DOWN_BLOCK_SIZE, GFP_KERNEL); + if (!fw_buf) + return -ENOMEM; + + dev_dbg(&serial->interface->dev, "Starting firmware download...\n"); + err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_START_FW_DOWN, 0, 0); + if (err) + goto out; + + fwidx = 0; + do { + txlen = min_t(size_t, (fw_p->size - fwidx), DOWN_BLOCK_SIZE); + + memcpy(fw_buf, &fw_p->data[fwidx], txlen); + err = mxuport_send_ctrl_data_urb(serial, RQ_VENDOR_FW_DATA, + 0, 0, fw_buf, txlen); + if (err) { + mxuport_send_ctrl_urb(serial, RQ_VENDOR_STOP_FW_DOWN, + 0, 0); + goto out; + } + + fwidx += txlen; + usleep_range(1000, 2000); + + } while (fwidx < fw_p->size); + + msleep(1000); + err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_STOP_FW_DOWN, 0, 0); + if (err) + goto out; + + msleep(1000); + err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_QUERY_FW_READY, 0, 0); + +out: + kfree(fw_buf); + return err; +} + +static int mxuport_probe(struct usb_serial *serial, + const struct usb_device_id *id) +{ + u16 productid = le16_to_cpu(serial->dev->descriptor.idProduct); + const struct firmware *fw_p = NULL; + u32 version; + int local_ver; + char buf[32]; + int err; + + /* Load our firmware */ + err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_QUERY_FW_CONFIG, 0, 0); + if (err) { + mxuport_send_ctrl_urb(serial, RQ_VENDOR_RESET_DEVICE, 0, 0); + return err; + } + + err = mxuport_get_fw_version(serial, &version); + if (err < 0) + return err; + + dev_dbg(&serial->interface->dev, "Device firmware version v%x.%x.%x\n", + (version & 0xff0000) >> 16, + (version & 0xff00) >> 8, + (version & 0xff)); + + snprintf(buf, sizeof(buf) - 1, "moxa/moxa-%04x.fw", productid); + + err = request_firmware(&fw_p, buf, &serial->interface->dev); + if (err) { + dev_warn(&serial->interface->dev, "Firmware %s not found\n", + buf); + + /* Use the firmware already in the device */ + err = 0; + } else { + local_ver = ((fw_p->data[VER_ADDR_1] << 16) | + (fw_p->data[VER_ADDR_2] << 8) | + fw_p->data[VER_ADDR_3]); + dev_dbg(&serial->interface->dev, + "Available firmware version v%x.%x.%x\n", + fw_p->data[VER_ADDR_1], fw_p->data[VER_ADDR_2], + fw_p->data[VER_ADDR_3]); + if (local_ver > version) { + err = mxuport_download_fw(serial, fw_p); + if (err) + goto out; + err = mxuport_get_fw_version(serial, &version); + if (err < 0) + goto out; + } + } + + dev_info(&serial->interface->dev, + "Using device firmware version v%x.%x.%x\n", + (version & 0xff0000) >> 16, + (version & 0xff00) >> 8, + (version & 0xff)); + + /* + * Contains the features of this hardware. Store away for + * later use, eg, number of ports. + */ + usb_set_serial_data(serial, (void *)id->driver_info); +out: + if (fw_p) + release_firmware(fw_p); + return err; +} + + +static int mxuport_port_probe(struct usb_serial_port *port) +{ + struct usb_serial *serial = port->serial; + struct mxuport_port *mxport; + int err; + + mxport = devm_kzalloc(&port->dev, sizeof(struct mxuport_port), + GFP_KERNEL); + if (!mxport) + return -ENOMEM; + + mutex_init(&mxport->mutex); + spin_lock_init(&mxport->spinlock); + + /* Set the port private data */ + usb_set_serial_port_data(port, mxport); + + /* Set FIFO (Enable) */ + err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_FIFO_DISABLE, + 0, port->port_number); + if (err) + return err; + + /* Set transmission mode (Hi-Performance) */ + err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_HIGH_PERFOR, + 0, port->port_number); + if (err) + return err; + + /* Set interface (RS-232) */ + err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_INTERFACE, + MX_INT_RS232, + port->port_number); + if (err) + return err; + + return 0; +} + +static int mxuport_alloc_write_urb(struct usb_serial *serial, + struct usb_serial_port *port, + struct usb_serial_port *port0, + int j) +{ + struct usb_device *dev = interface_to_usbdev(serial->interface); + + set_bit(j, &port->write_urbs_free); + port->write_urbs[j] = usb_alloc_urb(0, GFP_KERNEL); + if (!port->write_urbs[j]) + return -ENOMEM; + + port->bulk_out_buffers[j] = kmalloc(port0->bulk_out_size, GFP_KERNEL); + if (!port->bulk_out_buffers[j]) + return -ENOMEM; + + usb_fill_bulk_urb(port->write_urbs[j], dev, + usb_sndbulkpipe(dev, port->bulk_out_endpointAddress), + port->bulk_out_buffers[j], + port->bulk_out_size, + serial->type->write_bulk_callback, + port); + return 0; +} + + +static int mxuport_alloc_write_urbs(struct usb_serial *serial, + struct usb_serial_port *port, + struct usb_serial_port *port0) +{ + int j; + int ret; + + for (j = 0; j < ARRAY_SIZE(port->write_urbs); ++j) { + ret = mxuport_alloc_write_urb(serial, port, port0, j); + if (ret) + return ret; + } + return 0; +} + + +static int mxuport_attach(struct usb_serial *serial) +{ + struct usb_serial_port *port0 = serial->port[0]; + struct usb_serial_port *port1 = serial->port[1]; + struct usb_serial_port *port; + int err; + int i; + int j; + + /* + * Throw away all but the first allocated write URBs so we can + * set them up again to fit the multiplexing scheme. + */ + for (i = 1; i < serial->num_bulk_out; ++i) { + port = serial->port[i]; + for (j = 0; j < ARRAY_SIZE(port->write_urbs); ++j) { + usb_free_urb(port->write_urbs[j]); + kfree(port->bulk_out_buffers[j]); + port->write_urbs[j] = NULL; + port->bulk_out_buffers[j] = NULL; + } + port->write_urbs_free = 0; + } + + /* + * All write data is sent over the first bulk out endpoint, + * with an added header to indicate the port. Allocate URBs + * for each port to the first bulk out endpoint. + */ + for (i = 1; i < serial->num_ports; ++i) { + port = serial->port[i]; + port->bulk_out_size = port0->bulk_out_size; + port->bulk_out_endpointAddress = + port0->bulk_out_endpointAddress; + + err = mxuport_alloc_write_urbs(serial, port, port0); + if (err) + return err; + + port->write_urb = port->write_urbs[0]; + port->bulk_out_buffer = port->bulk_out_buffers[0]; + + /* + * Ensure each port has a fifo. The framework only + * allocates a fifo to ports with a bulk out endpoint, + * where as we need one for every port. + */ + if (!kfifo_initialized(&port->write_fifo)) { + err = kfifo_alloc(&port->write_fifo, PAGE_SIZE, + GFP_KERNEL); + if (err) + return err; + } + } + + /* + * All data from the ports is received on the first bulk in + * endpoint, with a multiplex header. The second bulk in is + * used for events. + * + * Start to read from the device. + */ + err = usb_serial_generic_submit_read_urbs(port0, GFP_KERNEL); + if (err) + return err; + + err = usb_serial_generic_submit_read_urbs(port1, GFP_KERNEL); + if (err) { + usb_serial_generic_close(port0); + return err; + } + + return 0; +} + +static int mxuport_open(struct tty_struct *tty, struct usb_serial_port *port) +{ + struct mxuport_port *mxport = usb_get_serial_port_data(port); + struct usb_serial *serial = port->serial; + int err; + + /* Set receive host (enable) */ + err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_RX_HOST_EN, + 1, port->port_number); + if (err) + return err; + + err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_OPEN, + 1, port->port_number); + if (err) { + mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_RX_HOST_EN, + 0, port->port_number); + return err; + } + + /* Initial port termios */ + mxuport_set_termios(tty, port, NULL); + + /* + * TODO: use RQ_VENDOR_GET_MSR, once we know what it + * returns. + */ + mxport->msr_state = 0; + + return err; +} + +static void mxuport_close(struct usb_serial_port *port) +{ + struct usb_serial *serial = port->serial; + + mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_OPEN, 0, + port->port_number); + + mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_RX_HOST_EN, 0, + port->port_number); +} + +/* Send a break to the port. */ +static void mxuport_break_ctl(struct tty_struct *tty, int break_state) +{ + struct usb_serial_port *port = tty->driver_data; + struct usb_serial *serial = port->serial; + int enable; + + if (break_state == -1) { + enable = 1; + dev_dbg(&port->dev, "%s - sending break\n", __func__); + } else { + enable = 0; + dev_dbg(&port->dev, "%s - clearing break\n", __func__); + } + + mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_BREAK, + enable, port->port_number); +} + +static int mxuport_resume(struct usb_serial *serial) +{ + struct usb_serial_port *port; + int c = 0; + int i; + int r; + + for (i = 0; i < 2; i++) { + port = serial->port[i]; + + r = usb_serial_generic_submit_read_urbs(port, GFP_NOIO); + if (r < 0) + c++; + } + + for (i = 0; i < serial->num_ports; i++) { + port = serial->port[i]; + if (!test_bit(ASYNCB_INITIALIZED, &port->port.flags)) + continue; + + r = usb_serial_generic_write_start(port, GFP_NOIO); + if (r < 0) + c++; + } + + return c ? -EIO : 0; +} + +static struct usb_serial_driver mxuport_device = { + .driver = { + .owner = THIS_MODULE, + .name = "mxuport", + }, + .description = "MOXA UPort", + .id_table = mxuport_idtable, + .num_ports = 0, + .probe = mxuport_probe, + .port_probe = mxuport_port_probe, + .attach = mxuport_attach, + .calc_num_ports = mxuport_calc_num_ports, + .open = mxuport_open, + .close = mxuport_close, + .set_termios = mxuport_set_termios, + .break_ctl = mxuport_break_ctl, + .tx_empty = mxuport_tx_empty, + .tiocmiwait = usb_serial_generic_tiocmiwait, + .get_icount = usb_serial_generic_get_icount, + .throttle = mxuport_throttle, + .unthrottle = mxuport_unthrottle, + .tiocmget = mxuport_tiocmget, + .tiocmset = mxuport_tiocmset, + .dtr_rts = mxuport_dtr_rts, + .process_read_urb = mxuport_process_read_urb, + .prepare_write_buffer = mxuport_prepare_write_buffer, + .resume = mxuport_resume, +}; + +static struct usb_serial_driver *const serial_drivers[] = { + &mxuport_device, NULL +}; + +module_usb_serial_driver(serial_drivers, mxuport_idtable); + +MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>"); +MODULE_AUTHOR("<support@moxa.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/serial/navman.c b/drivers/usb/serial/navman.c index 38725fc..2a97cdc 100644 --- a/drivers/usb/serial/navman.c +++ b/drivers/usb/serial/navman.c @@ -14,7 +14,6 @@ #include <linux/gfp.h> #include <linux/kernel.h> -#include <linux/init.h> #include <linux/tty.h> #include <linux/tty_flip.h> #include <linux/module.h> diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index 5739bf6..f6c6900 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -13,7 +13,6 @@ #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/tty.h> #include <linux/tty_driver.h> diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c index cbe779f..4856fb7 100644 --- a/drivers/usb/serial/opticon.c +++ b/drivers/usb/serial/opticon.c @@ -12,7 +12,6 @@ */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/tty.h> #include <linux/tty_driver.h> #include <linux/slab.h> @@ -139,7 +138,7 @@ static int opticon_open(struct tty_struct *tty, struct usb_serial_port *port) /* Clear RTS line */ send_control_msg(port, CONTROL_RTS, 0); - /* clear the halt status of the enpoint */ + /* clear the halt status of the endpoint */ usb_clear_halt(port->serial->dev, port->read_urb->pipe); res = usb_serial_generic_open(tty, port); @@ -200,15 +199,12 @@ static int opticon_write(struct tty_struct *tty, struct usb_serial_port *port, buffer = kmalloc(count, GFP_ATOMIC); if (!buffer) { - dev_err(&port->dev, "out of memory\n"); count = -ENOMEM; - goto error_no_buffer; } urb = usb_alloc_urb(0, GFP_ATOMIC); if (!urb) { - dev_err(&port->dev, "no more free urbs\n"); count = -ENOMEM; goto error_no_urb; } @@ -217,11 +213,10 @@ static int opticon_write(struct tty_struct *tty, struct usb_serial_port *port, usb_serial_debug_data(&port->dev, __func__, count, buffer); - /* The conncected devices do not have a bulk write endpoint, + /* The connected devices do not have a bulk write endpoint, * to transmit data to de barcode device the control endpoint is used */ dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO); if (!dr) { - dev_err(&port->dev, "out of memory\n"); count = -ENOMEM; goto error_no_dr; } @@ -367,8 +362,6 @@ static int opticon_ioctl(struct tty_struct *tty, { struct usb_serial_port *port = tty->driver_data; - dev_dbg(&port->dev, "%s - cmd = 0x%x\n", __func__, cmd); - switch (cmd) { case TIOCGSERIAL: return get_serial_info(port, diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 496b7e39..216d20a 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -251,6 +251,7 @@ static void option_instat_callback(struct urb *urb); #define ZTE_PRODUCT_MF628 0x0015 #define ZTE_PRODUCT_MF626 0x0031 #define ZTE_PRODUCT_MC2718 0xffe8 +#define ZTE_PRODUCT_AC2726 0xfff1 #define BENQ_VENDOR_ID 0x04a5 #define BENQ_PRODUCT_H10 0x4068 @@ -319,6 +320,9 @@ static void option_instat_callback(struct urb *urb); * It seems to contain a Qualcomm QSC6240/6290 chipset */ #define FOUR_G_SYSTEMS_PRODUCT_W14 0x9603 +/* iBall 3.5G connect wireless modem */ +#define IBALL_3_5G_CONNECT 0x9605 + /* Zoom */ #define ZOOM_PRODUCT_4597 0x9607 @@ -1358,7 +1362,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1267, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1268, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1269, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1270, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1270, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf5_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1271, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1272, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1273, 0xff, 0xff, 0xff) }, @@ -1446,6 +1451,17 @@ static const struct usb_device_id option_ids[] = { .driver_info = (kernel_ulong_t)&net_intf3_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0178, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&net_intf3_blacklist }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffe9, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff8b, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff8c, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff8d, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff8e, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff8f, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff90, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff91, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff92, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff93, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff94, 0xff, 0xff, 0xff) }, /* NOTE: most ZTE CDMA devices should be driven by zte_ev, not option */ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MC2718, 0xff, 0xff, 0xff), @@ -1453,6 +1469,7 @@ static const struct usb_device_id option_ids[] = { { USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x02, 0x01) }, { USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x02, 0x05) }, { USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x86, 0x10) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC2726, 0xff, 0xff, 0xff) }, { USB_DEVICE(BENQ_VENDOR_ID, BENQ_PRODUCT_H10) }, { USB_DEVICE(DLINK_VENDOR_ID, DLINK_PRODUCT_DWM_652) }, @@ -1487,6 +1504,7 @@ static const struct usb_device_id option_ids[] = { .driver_info = (kernel_ulong_t)&four_g_w14_blacklist }, { USB_DEVICE(LONGCHEER_VENDOR_ID, ZOOM_PRODUCT_4597) }, + { USB_DEVICE(LONGCHEER_VENDOR_ID, IBALL_3_5G_CONNECT) }, { USB_DEVICE(HAIER_VENDOR_ID, HAIER_PRODUCT_CE100) }, /* Pirelli */ { USB_DEVICE_INTERFACE_CLASS(PIRELLI_VENDOR_ID, PIRELLI_PRODUCT_C100_1, 0xff) }, diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index a2080ac..a4b88bc 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -39,7 +39,6 @@ #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/tty.h> #include <linux/tty_driver.h> @@ -103,6 +102,7 @@ struct oti6858_control_pkt { #define TX_BUFFER_EMPTIED 0x09 u8 pin_state; #define PIN_MASK 0x3f +#define PIN_MSR_MASK 0x1b #define PIN_RTS 0x20 /* output pin */ #define PIN_CTS 0x10 /* input pin, active low */ #define PIN_DSR 0x08 /* input pin, active low */ @@ -134,7 +134,6 @@ static int oti6858_chars_in_buffer(struct tty_struct *tty); static int oti6858_tiocmget(struct tty_struct *tty); static int oti6858_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); -static int oti6858_tiocmiwait(struct tty_struct *tty, unsigned long arg); static int oti6858_port_probe(struct usb_serial_port *port); static int oti6858_port_remove(struct usb_serial_port *port); @@ -153,7 +152,7 @@ static struct usb_serial_driver oti6858_device = { .init_termios = oti6858_init_termios, .tiocmget = oti6858_tiocmget, .tiocmset = oti6858_tiocmset, - .tiocmiwait = oti6858_tiocmiwait, + .tiocmiwait = usb_serial_generic_tiocmiwait, .read_bulk_callback = oti6858_read_bulk_callback, .read_int_callback = oti6858_read_int_callback, .write_bulk_callback = oti6858_write_bulk_callback, @@ -200,8 +199,7 @@ static void setup_line(struct work_struct *work) int result; new_setup = kmalloc(OTI6858_CTRL_PKT_SIZE, GFP_KERNEL); - if (new_setup == NULL) { - dev_err(&port->dev, "%s(): out of memory!\n", __func__); + if (!new_setup) { /* we will try again */ schedule_delayed_work(&priv->delayed_setup_work, msecs_to_jiffies(2)); @@ -287,11 +285,9 @@ static void send_data(struct work_struct *work) if (count != 0) { allow = kmalloc(1, GFP_KERNEL); - if (!allow) { - dev_err_console(port, "%s(): kmalloc failed\n", - __func__); + if (!allow) return; - } + result = usb_control_msg(port->serial->dev, usb_rcvctrlpipe(port->serial->dev, 0), OTI6858_REQ_T_CHECK_TXBUFF, @@ -517,10 +513,8 @@ static int oti6858_open(struct tty_struct *tty, struct usb_serial_port *port) usb_clear_halt(serial->dev, port->read_urb->pipe); buf = kmalloc(OTI6858_CTRL_PKT_SIZE, GFP_KERNEL); - if (buf == NULL) { - dev_err(&port->dev, "%s(): out of memory!\n", __func__); + if (!buf) return -ENOMEM; - } result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), OTI6858_REQ_T_GET_STATUS, @@ -647,46 +641,6 @@ static int oti6858_tiocmget(struct tty_struct *tty) return result; } -static int oti6858_tiocmiwait(struct tty_struct *tty, unsigned long arg) -{ - struct usb_serial_port *port = tty->driver_data; - struct oti6858_private *priv = usb_get_serial_port_data(port); - unsigned long flags; - unsigned int prev, status; - unsigned int changed; - - spin_lock_irqsave(&priv->lock, flags); - prev = priv->status.pin_state; - spin_unlock_irqrestore(&priv->lock, flags); - - while (1) { - wait_event_interruptible(port->port.delta_msr_wait, - port->serial->disconnected || - priv->status.pin_state != prev); - if (signal_pending(current)) - return -ERESTARTSYS; - - if (port->serial->disconnected) - return -EIO; - - spin_lock_irqsave(&priv->lock, flags); - status = priv->status.pin_state & PIN_MASK; - spin_unlock_irqrestore(&priv->lock, flags); - - changed = prev ^ status; - /* FIXME: check if this is correct (active high/low) */ - if (((arg & TIOCM_RNG) && (changed & PIN_RI)) || - ((arg & TIOCM_DSR) && (changed & PIN_DSR)) || - ((arg & TIOCM_CD) && (changed & PIN_DCD)) || - ((arg & TIOCM_CTS) && (changed & PIN_CTS))) - return 0; - prev = status; - } - - /* NOTREACHED */ - return 0; -} - static void oti6858_read_int_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; @@ -744,8 +698,21 @@ static void oti6858_read_int_callback(struct urb *urb) } if (!priv->transient) { - if (xs->pin_state != priv->status.pin_state) + u8 delta = xs->pin_state ^ priv->status.pin_state; + + if (delta & PIN_MSR_MASK) { + if (delta & PIN_CTS) + port->icount.cts++; + if (delta & PIN_DSR) + port->icount.dsr++; + if (delta & PIN_RI) + port->icount.rng++; + if (delta & PIN_DCD) + port->icount.dcd++; + wake_up_interruptible(&port->port.delta_msr_wait); + } + memcpy(&priv->status, xs, OTI6858_CTRL_PKT_SIZE); } diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 1e3318d..2e22fc2 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -12,12 +12,10 @@ * * See Documentation/usb/usb-serial.txt for more information on using this * driver - * */ #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/tty.h> #include <linux/tty_driver.h> @@ -32,10 +30,9 @@ #include <asm/unaligned.h> #include "pl2303.h" -/* - * Version Information - */ -#define DRIVER_DESC "Prolific PL2303 USB to serial adaptor driver" + +#define PL2303_QUIRK_UART_STATE_IDX0 BIT(0) +#define PL2303_QUIRK_LEGACY BIT(1) static const struct usb_device_id id_table[] = { { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID) }, @@ -64,9 +61,12 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(SITECOM_VENDOR_ID, SITECOM_PRODUCT_ID) }, { USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_ID) }, { USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_ID) }, - { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_SX1) }, - { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_X65) }, - { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_X75) }, + { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_SX1), + .driver_info = PL2303_QUIRK_UART_STATE_IDX0 }, + { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_X65), + .driver_info = PL2303_QUIRK_UART_STATE_IDX0 }, + { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_X75), + .driver_info = PL2303_QUIRK_UART_STATE_IDX0 }, { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_EF81) }, { USB_DEVICE(BENQ_VENDOR_ID, BENQ_PRODUCT_ID_S81) }, /* Benq/Siemens S81 */ { USB_DEVICE(SYNTECH_VENDOR_ID, SYNTECH_PRODUCT_ID) }, @@ -116,7 +116,8 @@ MODULE_DEVICE_TABLE(usb, id_table); #define VENDOR_READ_REQUEST_TYPE 0xc0 #define VENDOR_READ_REQUEST 0x01 -#define UART_STATE 0x08 +#define UART_STATE_INDEX 8 +#define UART_STATE_MSR_MASK 0x8b #define UART_STATE_TRANSIENT_MASK 0x74 #define UART_DCD 0x01 #define UART_DSR 0x02 @@ -129,98 +130,142 @@ MODULE_DEVICE_TABLE(usb, id_table); enum pl2303_type { - type_0, /* don't know the difference between type 0 and */ - type_1, /* type 1, until someone from prolific tells us... */ - HX, /* HX version of the pl2303 chip */ + TYPE_01, /* Type 0 and 1 (difference unknown) */ + TYPE_HX, /* HX version of the pl2303 chip */ + TYPE_COUNT +}; + +struct pl2303_type_data { + speed_t max_baud_rate; + unsigned long quirks; }; struct pl2303_serial_private { - enum pl2303_type type; + const struct pl2303_type_data *type; + unsigned long quirks; }; struct pl2303_private { spinlock_t lock; u8 line_control; u8 line_status; + + u8 line_settings[7]; +}; + +static const struct pl2303_type_data pl2303_type_data[TYPE_COUNT] = { + [TYPE_01] = { + .max_baud_rate = 1228800, + .quirks = PL2303_QUIRK_LEGACY, + }, }; -static int pl2303_vendor_read(__u16 value, __u16 index, - struct usb_serial *serial, unsigned char *buf) +static int pl2303_vendor_read(struct usb_serial *serial, u16 value, + unsigned char buf[1]) { - int res = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), + struct device *dev = &serial->interface->dev; + int res; + + res = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), VENDOR_READ_REQUEST, VENDOR_READ_REQUEST_TYPE, - value, index, buf, 1, 100); - dev_dbg(&serial->interface->dev, "0x%x:0x%x:0x%x:0x%x %d - %x\n", - VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, value, index, - res, buf[0]); - return res; + value, 0, buf, 1, 100); + if (res != 1) { + dev_err(dev, "%s - failed to read [%04x]: %d\n", __func__, + value, res); + if (res >= 0) + res = -EIO; + + return res; + } + + dev_dbg(dev, "%s - [%04x] = %02x\n", __func__, value, buf[0]); + + return 0; } -static int pl2303_vendor_write(__u16 value, __u16 index, - struct usb_serial *serial) +static int pl2303_vendor_write(struct usb_serial *serial, u16 value, u16 index) { - int res = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + struct device *dev = &serial->interface->dev; + int res; + + dev_dbg(dev, "%s - [%04x] = %02x\n", __func__, value, index); + + res = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), VENDOR_WRITE_REQUEST, VENDOR_WRITE_REQUEST_TYPE, value, index, NULL, 0, 100); - dev_dbg(&serial->interface->dev, "0x%x:0x%x:0x%x:0x%x %d\n", - VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, value, index, - res); - return res; + if (res) { + dev_err(dev, "%s - failed to write [%04x]: %d\n", __func__, + value, res); + return res; + } + + return 0; +} + +static int pl2303_probe(struct usb_serial *serial, + const struct usb_device_id *id) +{ + usb_set_serial_data(serial, (void *)id->driver_info); + + return 0; } static int pl2303_startup(struct usb_serial *serial) { struct pl2303_serial_private *spriv; - enum pl2303_type type = type_0; + enum pl2303_type type = TYPE_01; unsigned char *buf; spriv = kzalloc(sizeof(*spriv), GFP_KERNEL); if (!spriv) return -ENOMEM; - buf = kmalloc(10, GFP_KERNEL); + buf = kmalloc(1, GFP_KERNEL); if (!buf) { kfree(spriv); return -ENOMEM; } if (serial->dev->descriptor.bDeviceClass == 0x02) - type = type_0; + type = TYPE_01; /* type 0 */ else if (serial->dev->descriptor.bMaxPacketSize0 == 0x40) - type = HX; + type = TYPE_HX; else if (serial->dev->descriptor.bDeviceClass == 0x00) - type = type_1; + type = TYPE_01; /* type 1 */ else if (serial->dev->descriptor.bDeviceClass == 0xFF) - type = type_1; + type = TYPE_01; /* type 1 */ dev_dbg(&serial->interface->dev, "device type: %d\n", type); - spriv->type = type; + spriv->type = &pl2303_type_data[type]; + spriv->quirks = (unsigned long)usb_get_serial_data(serial); + spriv->quirks |= spriv->type->quirks; + usb_set_serial_data(serial, spriv); - pl2303_vendor_read(0x8484, 0, serial, buf); - pl2303_vendor_write(0x0404, 0, serial); - pl2303_vendor_read(0x8484, 0, serial, buf); - pl2303_vendor_read(0x8383, 0, serial, buf); - pl2303_vendor_read(0x8484, 0, serial, buf); - pl2303_vendor_write(0x0404, 1, serial); - pl2303_vendor_read(0x8484, 0, serial, buf); - pl2303_vendor_read(0x8383, 0, serial, buf); - pl2303_vendor_write(0, 1, serial); - pl2303_vendor_write(1, 0, serial); - if (type == HX) - pl2303_vendor_write(2, 0x44, serial); + pl2303_vendor_read(serial, 0x8484, buf); + pl2303_vendor_write(serial, 0x0404, 0); + pl2303_vendor_read(serial, 0x8484, buf); + pl2303_vendor_read(serial, 0x8383, buf); + pl2303_vendor_read(serial, 0x8484, buf); + pl2303_vendor_write(serial, 0x0404, 1); + pl2303_vendor_read(serial, 0x8484, buf); + pl2303_vendor_read(serial, 0x8383, buf); + pl2303_vendor_write(serial, 0, 1); + pl2303_vendor_write(serial, 1, 0); + if (spriv->quirks & PL2303_QUIRK_LEGACY) + pl2303_vendor_write(serial, 2, 0x24); else - pl2303_vendor_write(2, 0x24, serial); + pl2303_vendor_write(serial, 2, 0x44); kfree(buf); + return 0; } static void pl2303_release(struct usb_serial *serial) { - struct pl2303_serial_private *spriv; + struct pl2303_serial_private *spriv = usb_get_serial_data(serial); - spriv = usb_get_serial_data(serial); kfree(spriv); } @@ -243,9 +288,8 @@ static int pl2303_port_probe(struct usb_serial_port *port) static int pl2303_port_remove(struct usb_serial_port *port) { - struct pl2303_private *priv; + struct pl2303_private *priv = usb_get_serial_port_data(port); - priv = usb_get_serial_port_data(port); kfree(priv); return 0; @@ -256,39 +300,31 @@ static int pl2303_set_control_lines(struct usb_serial_port *port, u8 value) struct usb_device *dev = port->serial->dev; int retval; + dev_dbg(&port->dev, "%s - %02x\n", __func__, value); + retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CONTROL_REQUEST, SET_CONTROL_REQUEST_TYPE, value, 0, NULL, 0, 100); - dev_dbg(&port->dev, "%s - value = %d, retval = %d\n", __func__, - value, retval); + if (retval) + dev_err(&port->dev, "%s - failed: %d\n", __func__, retval); + return retval; } -static void pl2303_encode_baudrate(struct tty_struct *tty, - struct usb_serial_port *port, - u8 buf[4]) +/* + * Returns the nearest supported baud rate that can be set directly without + * using divisors. + */ +static speed_t pl2303_get_supported_baud_rate(speed_t baud) { - const int baud_sup[] = { 75, 150, 300, 600, 1200, 1800, 2400, 3600, - 4800, 7200, 9600, 14400, 19200, 28800, 38400, - 57600, 115200, 230400, 460800, 500000, 614400, - 921600, 1228800, 2457600, 3000000, 6000000 }; - - struct usb_serial *serial = port->serial; - struct pl2303_serial_private *spriv = usb_get_serial_data(serial); - int baud; - int i; + static const speed_t baud_sup[] = { + 75, 150, 300, 600, 1200, 1800, 2400, 3600, 4800, 7200, 9600, + 14400, 19200, 28800, 38400, 57600, 115200, 230400, 460800, + 614400, 921600, 1228800, 2457600, 3000000, 6000000 + }; - /* - * NOTE: Only the values defined in baud_sup are supported! - * => if unsupported values are set, the PL2303 seems to use - * 9600 baud (at least my PL2303X always does) - */ - baud = tty_get_baud_rate(tty); - dev_dbg(&port->dev, "baud requested = %d\n", baud); - if (!baud) - return; + unsigned i; - /* Set baudrate to nearest supported value */ for (i = 0; i < ARRAY_SIZE(baud_sup); ++i) { if (baud_sup[i] > baud) break; @@ -301,31 +337,120 @@ static void pl2303_encode_baudrate(struct tty_struct *tty, else baud = baud_sup[i]; - /* type_0, type_1 only support up to 1228800 baud */ - if (spriv->type != HX) - baud = min_t(int, baud, 1228800); + return baud; +} - if (baud <= 115200) { - put_unaligned_le32(baud, buf); - } else { - /* - * Apparently the formula for higher speeds is: - * baudrate = 12M * 32 / (2^buf[1]) / buf[0] - */ - unsigned tmp = 12000000 * 32 / baud; - buf[3] = 0x80; - buf[2] = 0; - buf[1] = (tmp >= 256); - while (tmp >= 256) { - tmp >>= 2; - buf[1] <<= 1; - } - buf[0] = tmp; +/* + * NOTE: If unsupported baud rates are set directly, the PL2303 seems to + * use 9600 baud. + */ +static speed_t pl2303_encode_baud_rate_direct(unsigned char buf[4], + speed_t baud) +{ + put_unaligned_le32(baud, buf); + + return baud; +} + +static speed_t pl2303_encode_baud_rate_divisor(unsigned char buf[4], + speed_t baud) +{ + unsigned int tmp; + + /* + * Apparently the formula is: + * baudrate = 12M * 32 / (2^buf[1]) / buf[0] + */ + tmp = 12000000 * 32 / baud; + buf[3] = 0x80; + buf[2] = 0; + buf[1] = (tmp >= 256); + while (tmp >= 256) { + tmp >>= 2; + buf[1] <<= 1; } + buf[0] = tmp; + + return baud; +} + +static void pl2303_encode_baud_rate(struct tty_struct *tty, + struct usb_serial_port *port, + u8 buf[4]) +{ + struct usb_serial *serial = port->serial; + struct pl2303_serial_private *spriv = usb_get_serial_data(serial); + speed_t baud_sup; + speed_t baud; + + baud = tty_get_baud_rate(tty); + dev_dbg(&port->dev, "baud requested = %u\n", baud); + if (!baud) + return; + + if (spriv->type->max_baud_rate) + baud = min_t(speed_t, baud, spriv->type->max_baud_rate); + /* + * Set baud rate to nearest supported value. + * + * NOTE: Baud rate 500k can only be set using divisors. + */ + baud_sup = pl2303_get_supported_baud_rate(baud); + + if (baud == 500000) + baud = pl2303_encode_baud_rate_divisor(buf, baud); + else + baud = pl2303_encode_baud_rate_direct(buf, baud_sup); /* Save resulting baud rate */ tty_encode_baud_rate(tty, baud, baud); - dev_dbg(&port->dev, "baud set = %d\n", baud); + dev_dbg(&port->dev, "baud set = %u\n", baud); +} + +static int pl2303_get_line_request(struct usb_serial_port *port, + unsigned char buf[7]) +{ + struct usb_device *udev = port->serial->dev; + int ret; + + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE, + 0, 0, buf, 7, 100); + if (ret != 7) { + dev_err(&port->dev, "%s - failed: %d\n", __func__, ret); + + if (ret > 0) + ret = -EIO; + + return ret; + } + + dev_dbg(&port->dev, "%s - %7ph\n", __func__, buf); + + return 0; +} + +static int pl2303_set_line_request(struct usb_serial_port *port, + unsigned char buf[7]) +{ + struct usb_device *udev = port->serial->dev; + int ret; + + ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + SET_LINE_REQUEST, SET_LINE_REQUEST_TYPE, + 0, 0, buf, 7, 100); + if (ret != 7) { + dev_err(&port->dev, "%s - failed: %d\n", __func__, ret); + + if (ret > 0) + ret = -EIO; + + return ret; + } + + dev_dbg(&port->dev, "%s - %7ph\n", __func__, buf); + + return 0; } static void pl2303_set_termios(struct tty_struct *tty, @@ -336,30 +461,21 @@ static void pl2303_set_termios(struct tty_struct *tty, struct pl2303_private *priv = usb_get_serial_port_data(port); unsigned long flags; unsigned char *buf; - int i; + int ret; u8 control; - /* - * The PL2303 is reported to lose bytes if you change serial settings - * even to the same values as before. Thus we actually need to filter - * in this specific case. - */ if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios)) return; buf = kzalloc(7, GFP_KERNEL); if (!buf) { - dev_err(&port->dev, "%s - out of memory.\n", __func__); /* Report back no change occurred */ if (old_termios) tty->termios = *old_termios; return; } - i = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), - GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE, - 0, 0, buf, 7, 100); - dev_dbg(&port->dev, "0xa1:0x21:0:0 %d - %7ph\n", i, buf); + pl2303_get_line_request(port, buf); switch (C_CSIZE(tty)) { case CS5: @@ -378,7 +494,7 @@ static void pl2303_set_termios(struct tty_struct *tty, dev_dbg(&port->dev, "data bits = %d\n", buf[6]); /* For reference buf[0]:buf[3] baud rate value */ - pl2303_encode_baudrate(tty, port, &buf[0]); + pl2303_encode_baud_rate(tty, port, &buf[0]); /* For reference buf[4]=0 is 1 stop bits */ /* For reference buf[4]=1 is 1.5 stop bits */ @@ -407,7 +523,7 @@ static void pl2303_set_termios(struct tty_struct *tty, /* For reference buf[5]=3 is mark parity */ /* For reference buf[5]=4 is space parity */ if (C_PARODD(tty)) { - if (tty->termios.c_cflag & CMSPAR) { + if (C_CMSPAR(tty)) { buf[5] = 3; dev_dbg(&port->dev, "parity = mark\n"); } else { @@ -415,7 +531,7 @@ static void pl2303_set_termios(struct tty_struct *tty, dev_dbg(&port->dev, "parity = odd\n"); } } else { - if (tty->termios.c_cflag & CMSPAR) { + if (C_CMSPAR(tty)) { buf[5] = 4; dev_dbg(&port->dev, "parity = space\n"); } else { @@ -428,10 +544,23 @@ static void pl2303_set_termios(struct tty_struct *tty, dev_dbg(&port->dev, "parity = none\n"); } - i = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), - SET_LINE_REQUEST, SET_LINE_REQUEST_TYPE, - 0, 0, buf, 7, 100); - dev_dbg(&port->dev, "0x21:0x20:0:0 %d\n", i); + /* + * Some PL2303 are known to lose bytes if you change serial settings + * even to the same values as before. Thus we actually need to filter + * in this specific case. + * + * Note that the tty_termios_hw_change check above is not sufficient + * as a previously requested baud rate may differ from the one + * actually used (and stored in old_termios). + * + * NOTE: No additional locking needed for line_settings as it is + * only used in set_termios, which is serialised against itself. + */ + if (!old_termios || memcmp(buf, priv->line_settings, 7)) { + ret = pl2303_set_line_request(port, buf); + if (!ret) + memcpy(priv->line_settings, buf, 7); + } /* change control lines if we are switching to or from B0 */ spin_lock_irqsave(&priv->lock, flags); @@ -448,19 +577,13 @@ static void pl2303_set_termios(struct tty_struct *tty, spin_unlock_irqrestore(&priv->lock, flags); } - memset(buf, 0, 7); - i = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), - GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE, - 0, 0, buf, 7, 100); - dev_dbg(&port->dev, "0xa1:0x21:0:0 %d - %7ph\n", i, buf); - if (C_CRTSCTS(tty)) { - if (spriv->type == HX) - pl2303_vendor_write(0x0, 0x61, serial); + if (spriv->quirks & PL2303_QUIRK_LEGACY) + pl2303_vendor_write(serial, 0x0, 0x41); else - pl2303_vendor_write(0x0, 0x41, serial); + pl2303_vendor_write(serial, 0x0, 0x61); } else { - pl2303_vendor_write(0x0, 0x0, serial); + pl2303_vendor_write(serial, 0x0, 0x0); } kfree(buf); @@ -473,13 +596,13 @@ static void pl2303_dtr_rts(struct usb_serial_port *port, int on) u8 control; spin_lock_irqsave(&priv->lock, flags); - /* Change DTR and RTS */ if (on) priv->line_control |= (CONTROL_DTR | CONTROL_RTS); else priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS); control = priv->line_control; spin_unlock_irqrestore(&priv->lock, flags); + pl2303_set_control_lines(port, control); } @@ -495,13 +618,13 @@ static int pl2303_open(struct tty_struct *tty, struct usb_serial_port *port) struct pl2303_serial_private *spriv = usb_get_serial_data(serial); int result; - if (spriv->type != HX) { + if (spriv->quirks & PL2303_QUIRK_LEGACY) { usb_clear_halt(serial->dev, port->write_urb->pipe); usb_clear_halt(serial->dev, port->read_urb->pipe); } else { /* reset upstream data pipes */ - pl2303_vendor_write(8, 0, serial); - pl2303_vendor_write(9, 0, serial); + pl2303_vendor_write(serial, 8, 0); + pl2303_vendor_write(serial, 9, 0); } /* Setup termios */ @@ -510,8 +633,8 @@ static int pl2303_open(struct tty_struct *tty, struct usb_serial_port *port) result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); if (result) { - dev_err(&port->dev, "%s - failed submitting interrupt urb," - " error %d\n", __func__, result); + dev_err(&port->dev, "failed to submit interrupt urb: %d\n", + result); return result; } @@ -581,48 +704,10 @@ static int pl2303_tiocmget(struct tty_struct *tty) static int pl2303_carrier_raised(struct usb_serial_port *port) { struct pl2303_private *priv = usb_get_serial_port_data(port); + if (priv->line_status & UART_DCD) return 1; - return 0; -} -static int pl2303_tiocmiwait(struct tty_struct *tty, unsigned long arg) -{ - struct usb_serial_port *port = tty->driver_data; - struct pl2303_private *priv = usb_get_serial_port_data(port); - unsigned long flags; - unsigned int prevstatus; - unsigned int status; - unsigned int changed; - - spin_lock_irqsave(&priv->lock, flags); - prevstatus = priv->line_status; - spin_unlock_irqrestore(&priv->lock, flags); - - while (1) { - interruptible_sleep_on(&port->port.delta_msr_wait); - /* see if a signal did it */ - if (signal_pending(current)) - return -ERESTARTSYS; - - if (port->serial->disconnected) - return -EIO; - - spin_lock_irqsave(&priv->lock, flags); - status = priv->line_status; - spin_unlock_irqrestore(&priv->lock, flags); - - changed = prevstatus ^ status; - - if (((arg & TIOCM_RNG) && (changed & UART_RING)) || - ((arg & TIOCM_DSR) && (changed & UART_DSR)) || - ((arg & TIOCM_CD) && (changed & UART_DCD)) || - ((arg & TIOCM_CTS) && (changed & UART_CTS))) { - return 0; - } - prevstatus = status; - } - /* NOTREACHED */ return 0; } @@ -632,8 +717,6 @@ static int pl2303_ioctl(struct tty_struct *tty, struct serial_struct ser; struct usb_serial_port *port = tty->driver_data; - dev_dbg(&port->dev, "%s cmd = 0x%04x\n", __func__, cmd); - switch (cmd) { case TIOCGSERIAL: memset(&ser, 0, sizeof ser); @@ -647,9 +730,9 @@ static int pl2303_ioctl(struct tty_struct *tty, return 0; default: - dev_dbg(&port->dev, "%s not supported = 0x%04x\n", __func__, cmd); break; } + return -ENOIOCTLCMD; } @@ -664,6 +747,7 @@ static void pl2303_break_ctl(struct tty_struct *tty, int break_state) state = BREAK_OFF; else state = BREAK_ON; + dev_dbg(&port->dev, "%s - turning break %s\n", __func__, state == BREAK_OFF ? "off" : "on"); @@ -678,48 +762,51 @@ static void pl2303_update_line_status(struct usb_serial_port *port, unsigned char *data, unsigned int actual_length) { - + struct usb_serial *serial = port->serial; + struct pl2303_serial_private *spriv = usb_get_serial_data(serial); struct pl2303_private *priv = usb_get_serial_port_data(port); struct tty_struct *tty; unsigned long flags; - u8 status_idx = UART_STATE; - u8 length = UART_STATE + 1; - u8 prev_line_status; - u16 idv, idp; - - idv = le16_to_cpu(port->serial->dev->descriptor.idVendor); - idp = le16_to_cpu(port->serial->dev->descriptor.idProduct); - + unsigned int status_idx = UART_STATE_INDEX; + u8 status; + u8 delta; - if (idv == SIEMENS_VENDOR_ID) { - if (idp == SIEMENS_PRODUCT_ID_X65 || - idp == SIEMENS_PRODUCT_ID_SX1 || - idp == SIEMENS_PRODUCT_ID_X75) { + if (spriv->quirks & PL2303_QUIRK_UART_STATE_IDX0) + status_idx = 0; - length = 1; - status_idx = 0; - } - } - - if (actual_length < length) + if (actual_length < status_idx + 1) return; + status = data[status_idx]; + /* Save off the uart status for others to look at */ spin_lock_irqsave(&priv->lock, flags); - prev_line_status = priv->line_status; - priv->line_status = data[status_idx]; + delta = priv->line_status ^ status; + priv->line_status = status; spin_unlock_irqrestore(&priv->lock, flags); - if (priv->line_status & UART_BREAK_ERROR) + + if (status & UART_BREAK_ERROR) usb_serial_handle_break(port); - wake_up_interruptible(&port->port.delta_msr_wait); - tty = tty_port_tty_get(&port->port); - if (!tty) - return; - if ((priv->line_status ^ prev_line_status) & UART_DCD) - usb_serial_handle_dcd_change(port, tty, - priv->line_status & UART_DCD); - tty_kref_put(tty); + if (delta & UART_STATE_MSR_MASK) { + if (delta & UART_CTS) + port->icount.cts++; + if (delta & UART_DSR) + port->icount.dsr++; + if (delta & UART_RING) + port->icount.rng++; + if (delta & UART_DCD) { + port->icount.dcd++; + tty = tty_port_tty_get(&port->port); + if (tty) { + usb_serial_handle_dcd_change(port, tty, + status & UART_DCD); + tty_kref_put(tty); + } + } + + wake_up_interruptible(&port->port.delta_msr_wait); + } } static void pl2303_read_int_callback(struct urb *urb) @@ -754,10 +841,11 @@ static void pl2303_read_int_callback(struct urb *urb) exit: retval = usb_submit_urb(urb, GFP_ATOMIC); - if (retval) + if (retval) { dev_err(&port->dev, "%s - usb_submit_urb failed with result %d\n", __func__, retval); + } } static void pl2303_process_read_urb(struct urb *urb) @@ -775,13 +863,14 @@ static void pl2303_process_read_urb(struct urb *urb) line_status = priv->line_status; priv->line_status &= ~UART_STATE_TRANSIENT_MASK; spin_unlock_irqrestore(&priv->lock, flags); - wake_up_interruptible(&port->port.delta_msr_wait); if (!urb->actual_length) return; - /* break takes precedence over parity, */ - /* which takes precedence over framing errors */ + /* + * Break takes precedence over parity, which takes precedence over + * framing errors. + */ if (line_status & UART_BREAK_ERROR) tty_flag = TTY_BREAK; else if (line_status & UART_PARITY_ERROR) @@ -809,7 +898,6 @@ static void pl2303_process_read_urb(struct urb *urb) tty_flip_buffer_push(&port->port); } -/* All of the device info needed for the PL2303 SIO serial converter */ static struct usb_serial_driver pl2303_device = { .driver = { .owner = THIS_MODULE, @@ -821,16 +909,17 @@ static struct usb_serial_driver pl2303_device = { .bulk_out_size = 256, .open = pl2303_open, .close = pl2303_close, - .dtr_rts = pl2303_dtr_rts, + .dtr_rts = pl2303_dtr_rts, .carrier_raised = pl2303_carrier_raised, .ioctl = pl2303_ioctl, .break_ctl = pl2303_break_ctl, .set_termios = pl2303_set_termios, .tiocmget = pl2303_tiocmget, .tiocmset = pl2303_tiocmset, - .tiocmiwait = pl2303_tiocmiwait, + .tiocmiwait = usb_serial_generic_tiocmiwait, .process_read_urb = pl2303_process_read_urb, .read_int_callback = pl2303_read_int_callback, + .probe = pl2303_probe, .attach = pl2303_startup, .release = pl2303_release, .port_probe = pl2303_port_probe, @@ -843,5 +932,5 @@ static struct usb_serial_driver * const serial_drivers[] = { module_usb_serial_driver(serial_drivers, id_table); -MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_DESCRIPTION("Prolific PL2303 USB to serial adaptor driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/usb/serial/qcaux.c b/drivers/usb/serial/qcaux.c index 31f81c3..6e9f8af 100644 --- a/drivers/usb/serial/qcaux.c +++ b/drivers/usb/serial/qcaux.c @@ -16,7 +16,6 @@ */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/tty.h> #include <linux/module.h> #include <linux/usb.h> @@ -54,7 +53,7 @@ #define SAMSUNG_VENDOR_ID 0x04e8 #define SAMSUNG_PRODUCT_U520 0x6640 /* SCH-U520 */ -static struct usb_device_id id_table[] = { +static const struct usb_device_id id_table[] = { { USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, UTSTARCOM_PRODUCT_PC5740, 0xff, 0x00, 0x00) }, { USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, UTSTARCOM_PRODUCT_PC5750, 0xff, 0x00, 0x00) }, { USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, UTSTARCOM_PRODUCT_UM150, 0xff, 0x00, 0x00) }, diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index c65437c..968a402 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -139,6 +139,9 @@ static const struct usb_device_id id_table[] = { {USB_DEVICE_INTERFACE_NUMBER(0x1199, 0x901c, 0)}, /* Sierra Wireless EM7700 Device Management */ {USB_DEVICE_INTERFACE_NUMBER(0x1199, 0x901c, 2)}, /* Sierra Wireless EM7700 NMEA */ {USB_DEVICE_INTERFACE_NUMBER(0x1199, 0x901c, 3)}, /* Sierra Wireless EM7700 Modem */ + {USB_DEVICE_INTERFACE_NUMBER(0x1199, 0x9051, 0)}, /* Netgear AirCard 340U Device Management */ + {USB_DEVICE_INTERFACE_NUMBER(0x1199, 0x9051, 2)}, /* Netgear AirCard 340U NMEA */ + {USB_DEVICE_INTERFACE_NUMBER(0x1199, 0x9051, 3)}, /* Netgear AirCard 340U Modem */ { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index a24d59a..7725ed2 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -15,7 +15,6 @@ #include <asm/unaligned.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/tty.h> #include <linux/tty_driver.h> @@ -676,10 +675,8 @@ static int qt2_setup_urbs(struct usb_serial *serial) serial_priv = usb_get_serial_data(serial); serial_priv->read_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!serial_priv->read_urb) { - dev_err(&serial->dev->dev, "No free urbs available\n"); + if (!serial_priv->read_urb) return -ENOMEM; - } usb_fill_bulk_urb(serial_priv->read_urb, serial->dev, usb_rcvbulkpipe(serial->dev, @@ -715,10 +712,8 @@ static int qt2_attach(struct usb_serial *serial) } serial_priv = kzalloc(sizeof(*serial_priv), GFP_KERNEL); - if (!serial_priv) { - dev_err(&serial->dev->dev, "%s - Out of memory\n", __func__); + if (!serial_priv) return -ENOMEM; - } serial_priv->read_buffer = kmalloc(QT2_READ_BUFFER_SIZE, GFP_KERNEL); if (!serial_priv->read_buffer) { diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c index ba89598..b2dff0f 100644 --- a/drivers/usb/serial/safe_serial.c +++ b/drivers/usb/serial/safe_serial.c @@ -67,7 +67,6 @@ #include <linux/kernel.h> #include <linux/errno.h> #include <linux/gfp.h> -#include <linux/init.h> #include <linux/tty.h> #include <linux/tty_driver.h> #include <linux/tty_flip.h> @@ -125,7 +124,7 @@ MODULE_PARM_DESC(padded, "Pad to full wMaxPacketSize On/Off"); .bInterfaceClass = (ic), \ .bInterfaceSubClass = (isc), -static struct usb_device_id id_table[] = { +static const struct usb_device_id id_table[] = { {MY_USB_DEVICE(0x49f, 0xffff, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, /* Itsy */ {MY_USB_DEVICE(0x3f0, 0x2101, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, /* Calypso */ {MY_USB_DEVICE(0x4dd, 0x8001, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL)}, /* Iris */ diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index de958c5..a9eb6221 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -497,14 +497,12 @@ static int sierra_write(struct tty_struct *tty, struct usb_serial_port *port, buffer = kmalloc(writesize, GFP_ATOMIC); if (!buffer) { - dev_err(&port->dev, "out of memory\n"); retval = -ENOMEM; goto error_no_buffer; } urb = usb_alloc_urb(0, GFP_ATOMIC); if (!urb) { - dev_err(&port->dev, "no more free urbs\n"); retval = -ENOMEM; goto error_no_urb; } @@ -736,11 +734,8 @@ static struct urb *sierra_setup_urb(struct usb_serial *serial, int endpoint, return NULL; urb = usb_alloc_urb(0, mem_flags); - if (urb == NULL) { - dev_dbg(&serial->dev->dev, "%s: alloc for endpoint %d failed\n", - __func__, endpoint); + if (!urb) return NULL; - } buf = kmalloc(len, mem_flags); if (buf) { @@ -752,9 +747,6 @@ static struct urb *sierra_setup_urb(struct usb_serial *serial, int endpoint, dev_dbg(&serial->dev->dev, "%s %c u : %p d:%p\n", __func__, dir == USB_DIR_IN ? 'i' : 'o', urb, buf); } else { - dev_dbg(&serial->dev->dev, "%s %c u:%p d:%p\n", __func__, - dir == USB_DIR_IN ? 'i' : 'o', urb, buf); - sierra_release_urb(urb); urb = NULL; } diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index 5b793c3..4ec04f7 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -16,7 +16,6 @@ */ #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/tty.h> #include <linux/tty_driver.h> diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c index e5750be..a7fe664 100644 --- a/drivers/usb/serial/ssu100.c +++ b/drivers/usb/serial/ssu100.c @@ -6,7 +6,6 @@ */ #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/tty.h> #include <linux/tty_driver.h> @@ -342,8 +341,6 @@ static int ssu100_ioctl(struct tty_struct *tty, { struct usb_serial_port *port = tty->driver_data; - dev_dbg(&port->dev, "%s cmd 0x%04x\n", __func__, cmd); - switch (cmd) { case TIOCGSERIAL: return get_serial_info(port, @@ -352,8 +349,6 @@ static int ssu100_ioctl(struct tty_struct *tty, break; } - dev_dbg(&port->dev, "%s arg not supported\n", __func__); - return -ENOIOCTLCMD; } diff --git a/drivers/usb/serial/symbolserial.c b/drivers/usb/serial/symbolserial.c index 9b16489..9fa7dd4 100644 --- a/drivers/usb/serial/symbolserial.c +++ b/drivers/usb/serial/symbolserial.c @@ -11,7 +11,6 @@ */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/tty.h> #include <linux/slab.h> #include <linux/tty_driver.h> diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index c9a3569..ec7cea5 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -21,7 +21,6 @@ #include <linux/kernel.h> #include <linux/errno.h> #include <linux/firmware.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/tty.h> #include <linux/tty_driver.h> @@ -143,7 +142,7 @@ static int ti_download_firmware(struct ti_device *tdev); static int closing_wait = TI_DEFAULT_CLOSING_WAIT; /* supported devices */ -static struct usb_device_id ti_id_table_3410[] = { +static const struct usb_device_id ti_id_table_3410[] = { { USB_DEVICE(TI_VENDOR_ID, TI_3410_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, TI_3410_EZ430_ID) }, { USB_DEVICE(MTS_VENDOR_ID, MTS_GSM_NO_FW_PRODUCT_ID) }, @@ -163,7 +162,7 @@ static struct usb_device_id ti_id_table_3410[] = { { } /* terminator */ }; -static struct usb_device_id ti_id_table_5052[] = { +static const struct usb_device_id ti_id_table_5052[] = { { USB_DEVICE(TI_VENDOR_ID, TI_5052_BOOT_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, TI_5152_BOOT_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, TI_5052_EEPROM_PRODUCT_ID) }, @@ -171,7 +170,7 @@ static struct usb_device_id ti_id_table_5052[] = { { } /* terminator */ }; -static struct usb_device_id ti_id_table_combined[] = { +static const struct usb_device_id ti_id_table_combined[] = { { USB_DEVICE(TI_VENDOR_ID, TI_3410_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, TI_3410_EZ430_ID) }, { USB_DEVICE(MTS_VENDOR_ID, MTS_GSM_NO_FW_PRODUCT_ID) }, @@ -301,10 +300,9 @@ static int ti_startup(struct usb_serial *serial) /* create device structure */ tdev = kzalloc(sizeof(struct ti_device), GFP_KERNEL); - if (tdev == NULL) { - dev_err(&dev->dev, "%s - out of memory\n", __func__); + if (!tdev) return -ENOMEM; - } + mutex_init(&tdev->td_open_close_lock); tdev->td_serial = serial; usb_set_serial_data(serial, tdev); @@ -683,8 +681,6 @@ static int ti_ioctl(struct tty_struct *tty, struct usb_serial_port *port = tty->driver_data; struct ti_port *tport = usb_get_serial_port_data(port); - dev_dbg(&port->dev, "%s - cmd = 0x%04X\n", __func__, cmd); - if (tport == NULL) return -ENODEV; @@ -724,10 +720,8 @@ static void ti_set_termios(struct tty_struct *tty, return; config = kmalloc(sizeof(*config), GFP_KERNEL); - if (!config) { - dev_err(&port->dev, "%s - out of memory\n", __func__); + if (!config) return; - } config->wFlags = 0; @@ -1196,10 +1190,8 @@ static int ti_get_lsr(struct ti_port *tport, u8 *lsr) size = sizeof(struct ti_port_status); data = kmalloc(size, GFP_KERNEL); - if (!data) { - dev_err(&port->dev, "%s - out of memory\n", __func__); + if (!data) return -ENOMEM; - } status = ti_command_in_sync(tdev, TI_GET_PORT_STATUS, (__u8)(TI_UART1_PORT+port_number), 0, (__u8 *)data, size); @@ -1399,10 +1391,8 @@ static int ti_write_byte(struct usb_serial_port *port, size = sizeof(struct ti_write_data_bytes) + 2; data = kmalloc(size, GFP_KERNEL); - if (!data) { - dev_err(&port->dev, "%s - out of memory\n", __func__); + if (!data) return -ENOMEM; - } data->bAddrType = TI_RW_DATA_ADDR_XDATA; data->bDataType = TI_RW_DATA_BYTE; @@ -1518,7 +1508,6 @@ static int ti_download_firmware(struct ti_device *tdev) status = ti_do_download(dev, pipe, buffer, fw_p->size); kfree(buffer); } else { - dev_dbg(&dev->dev, "%s ENOMEM\n", __func__); status = -ENOMEM; } release_firmware(fw_p); diff --git a/drivers/usb/serial/usb-serial-simple.c b/drivers/usb/serial/usb-serial-simple.c index 52eb91f..fb79775 100644 --- a/drivers/usb/serial/usb-serial-simple.c +++ b/drivers/usb/serial/usb-serial-simple.c @@ -15,7 +15,6 @@ */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/tty.h> #include <linux/module.h> #include <linux/usb.h> @@ -72,7 +71,8 @@ DEVICE(hp4x, HP4X_IDS); /* Suunto ANT+ USB Driver */ #define SUUNTO_IDS() \ - { USB_DEVICE(0x0fcf, 0x1008) } + { USB_DEVICE(0x0fcf, 0x1008) }, \ + { USB_DEVICE(0x0fcf, 0x1009) } /* Dynastream ANT USB-m Stick */ DEVICE(suunto, SUUNTO_IDS); /* Siemens USB/MPI adapter */ diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 6091bd5..7c9dc28 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -405,7 +405,7 @@ static int serial_ioctl(struct tty_struct *tty, struct usb_serial_port *port = tty->driver_data; int retval = -ENOIOCTLCMD; - dev_dbg(tty->dev, "%s - cmd 0x%.4x\n", __func__, cmd); + dev_dbg(tty->dev, "%s - cmd 0x%04x\n", __func__, cmd); switch (cmd) { case TIOCMIWAIT: diff --git a/drivers/usb/serial/usb_debug.c b/drivers/usb/serial/usb_debug.c index 5760f97..ca2fa5b 100644 --- a/drivers/usb/serial/usb_debug.c +++ b/drivers/usb/serial/usb_debug.c @@ -10,7 +10,6 @@ #include <linux/gfp.h> #include <linux/kernel.h> -#include <linux/init.h> #include <linux/tty.h> #include <linux/module.h> #include <linux/usb.h> diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c index 8536578..640fe01 100644 --- a/drivers/usb/serial/usb_wwan.c +++ b/drivers/usb/serial/usb_wwan.c @@ -447,12 +447,8 @@ static struct urb *usb_wwan_setup_urb(struct usb_serial_port *port, struct urb *urb; urb = usb_alloc_urb(0, GFP_KERNEL); /* No ISO */ - if (urb == NULL) { - dev_dbg(&serial->interface->dev, - "%s: alloc for endpoint %d failed.\n", __func__, - endpoint); + if (!urb) return NULL; - } /* Fill URB using supplied data. */ usb_fill_bulk_urb(urb, serial->dev, diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 9910aa2..bf2bd40 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -16,7 +16,6 @@ #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/tty.h> #include <linux/tty_driver.h> @@ -51,7 +50,7 @@ static int palm_os_3_probe(struct usb_serial *serial, static int palm_os_4_probe(struct usb_serial *serial, const struct usb_device_id *id); -static struct usb_device_id id_table [] = { +static const struct usb_device_id id_table[] = { { USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_VISOR_ID), .driver_info = (kernel_ulong_t)&palm_os_3_probe }, { USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_TREO_ID), @@ -113,18 +112,18 @@ static struct usb_device_id id_table [] = { { } /* Terminating entry */ }; -static struct usb_device_id clie_id_5_table [] = { +static const struct usb_device_id clie_id_5_table[] = { { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_UX50_ID), .driver_info = (kernel_ulong_t)&palm_os_4_probe }, { } /* Terminating entry */ }; -static struct usb_device_id clie_id_3_5_table [] = { +static const struct usb_device_id clie_id_3_5_table[] = { { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_3_5_ID) }, { } /* Terminating entry */ }; -static struct usb_device_id id_table_combined [] = { +static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_VISOR_ID) }, { USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_TREO_ID) }, { USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_TREO600_ID) }, @@ -324,11 +323,8 @@ static int palm_os_3_probe(struct usb_serial *serial, int num_ports = 0; transfer_buffer = kmalloc(sizeof(*connection_info), GFP_KERNEL); - if (!transfer_buffer) { - dev_err(dev, "%s - kmalloc(%Zd) failed.\n", __func__, - sizeof(*connection_info)); + if (!transfer_buffer) return -ENOMEM; - } /* send a get connection info request */ retval = usb_control_msg(serial->dev, @@ -419,11 +415,8 @@ static int palm_os_4_probe(struct usb_serial *serial, int retval; transfer_buffer = kmalloc(sizeof(*connection_info), GFP_KERNEL); - if (!transfer_buffer) { - dev_err(dev, "%s - kmalloc(%Zd) failed.\n", __func__, - sizeof(*connection_info)); + if (!transfer_buffer) return -ENOMEM; - } retval = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), diff --git a/drivers/usb/serial/visor.h b/drivers/usb/serial/visor.h index 88db4d0..4c456dd 100644 --- a/drivers/usb/serial/visor.h +++ b/drivers/usb/serial/visor.h @@ -136,7 +136,7 @@ struct visor_connection_info { * connections.end_point_info is non-zero. If value is 0, then * connections.port contains the endpoint number, which is the same for in * and out. - * @port_function_id: contains the creator id of the applicaton that opened + * @port_function_id: contains the creator id of the application that opened * this connection. * @port: contains the in/out endpoint number. Is 0 if in and out endpoint * numbers are different. diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 36a7740..e62f2df 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -18,7 +18,6 @@ #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/tty.h> #include <linux/tty_driver.h> @@ -288,12 +287,8 @@ static int whiteheat_attach(struct usb_serial *serial) command_info = kmalloc(sizeof(struct whiteheat_command_private), GFP_KERNEL); - if (command_info == NULL) { - dev_err(&serial->dev->dev, - "%s: Out of memory for port structures\n", - serial->type->description); + if (!command_info) goto no_command_private; - } mutex_init(&command_info->mutex); command_info->port_running = 0; @@ -455,8 +450,6 @@ static int whiteheat_ioctl(struct tty_struct *tty, struct serial_struct serstruct; void __user *user_arg = (void __user *)arg; - dev_dbg(&port->dev, "%s - cmd 0x%.4x\n", __func__, cmd); - switch (cmd) { case TIOCGSERIAL: memset(&serstruct, 0, sizeof(serstruct)); diff --git a/drivers/usb/serial/wishbone-serial.c b/drivers/usb/serial/wishbone-serial.c index 100573c..4fed4a0 100644 --- a/drivers/usb/serial/wishbone-serial.c +++ b/drivers/usb/serial/wishbone-serial.c @@ -11,7 +11,6 @@ */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/tty.h> #include <linux/module.h> #include <linux/usb.h> diff --git a/drivers/usb/serial/xsens_mt.c b/drivers/usb/serial/xsens_mt.c index 1d5798d..4841fb5 100644 --- a/drivers/usb/serial/xsens_mt.c +++ b/drivers/usb/serial/xsens_mt.c @@ -9,7 +9,6 @@ */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/tty.h> #include <linux/module.h> #include <linux/usb.h> diff --git a/drivers/usb/serial/zte_ev.c b/drivers/usb/serial/zte_ev.c index fca4c75..e40ab73 100644 --- a/drivers/usb/serial/zte_ev.c +++ b/drivers/usb/serial/zte_ev.c @@ -13,7 +13,6 @@ * show the commands used to talk to the device, but I am not sure. */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/tty.h> #include <linux/slab.h> #include <linux/module.h> @@ -53,7 +52,7 @@ static int zte_ev_usb_serial_open(struct tty_struct *tty, USB_CTRL_GET_TIMEOUT); dev_dbg(dev, "result = %d\n", result); - /* send 2st cmd and recieve data */ + /* send 2st cmd and receive data */ /* * 16.0 CTL a1 21 00 00 00 00 07 00 CLASS 25.1.0(5) * 16.0 DI 00 96 00 00 00 00 08 @@ -65,7 +64,7 @@ static int zte_ev_usb_serial_open(struct tty_struct *tty, USB_CTRL_GET_TIMEOUT); debug_data(dev, __func__, len, buf, result); - /* send 3 cmd */ + /* send 3rd cmd */ /* * 16.0 CTL 21 20 00 00 00 00 07 00 CLASS 30.1.0 * 16.0 DO 80 25 00 00 00 00 08 .%..... 30.2.0 @@ -84,7 +83,7 @@ static int zte_ev_usb_serial_open(struct tty_struct *tty, USB_CTRL_GET_TIMEOUT); debug_data(dev, __func__, len, buf, result); - /* send 4 cmd */ + /* send 4th cmd */ /* * 16.0 CTL 21 22 03 00 00 00 00 00 */ @@ -95,7 +94,7 @@ static int zte_ev_usb_serial_open(struct tty_struct *tty, USB_CTRL_GET_TIMEOUT); dev_dbg(dev, "result = %d\n", result); - /* send 5 cmd */ + /* send 5th cmd */ /* * 16.0 CTL a1 21 00 00 00 00 07 00 CLASS 33.1.0 * 16.0 DI 80 25 00 00 00 00 08 @@ -107,7 +106,7 @@ static int zte_ev_usb_serial_open(struct tty_struct *tty, USB_CTRL_GET_TIMEOUT); debug_data(dev, __func__, len, buf, result); - /* send 6 cmd */ + /* send 6th cmd */ /* * 16.0 CTL 21 20 00 00 00 00 07 00 CLASS 34.1.0 * 16.0 DO 80 25 00 00 00 00 08 @@ -195,7 +194,7 @@ static void zte_ev_usb_serial_close(struct usb_serial_port *port) USB_CTRL_GET_TIMEOUT); debug_data(dev, __func__, len, buf, result); - /* send 4 cmd */ + /* send 4th cmd */ /* * 16.0 CTL 21 20 00 00 00 00 07 00 CLASS 30.1.0 * 16.0 DO 00 c2 01 00 00 00 08 .%..... 30.2.0 @@ -214,7 +213,7 @@ static void zte_ev_usb_serial_close(struct usb_serial_port *port) USB_CTRL_GET_TIMEOUT); debug_data(dev, __func__, len, buf, result); - /* send 5 cmd */ + /* send 5th cmd */ /* * 16.0 CTL 21 22 03 00 00 00 00 00 */ @@ -225,7 +224,7 @@ static void zte_ev_usb_serial_close(struct usb_serial_port *port) USB_CTRL_GET_TIMEOUT); dev_dbg(dev, "result = %d\n", result); - /* send 6 cmd */ + /* send 6th cmd */ /* * 16.0 CTL a1 21 00 00 00 00 07 00 CLASS 33.1.0 * 16.0 DI 00 c2 01 00 00 00 08 @@ -237,7 +236,7 @@ static void zte_ev_usb_serial_close(struct usb_serial_port *port) USB_CTRL_GET_TIMEOUT); debug_data(dev, __func__, len, buf, result); - /* send 7 cmd */ + /* send 7th cmd */ /* * 16.0 CTL 21 20 00 00 00 00 07 00 CLASS 354.1.0 * 16.0 DO 00 c2 01 00 00 00 08 ....... 354.2.0 @@ -256,7 +255,7 @@ static void zte_ev_usb_serial_close(struct usb_serial_port *port) USB_CTRL_GET_TIMEOUT); debug_data(dev, __func__, len, buf, result); - /* send 8 cmd */ + /* send 8th cmd */ /* * 16.0 CTL 21 22 03 00 00 00 00 00 */ @@ -281,8 +280,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x19d2, 0xfffd) }, { USB_DEVICE(0x19d2, 0xfffc) }, { USB_DEVICE(0x19d2, 0xfffb) }, - /* AC2726, AC8710_V3 */ - { USB_DEVICE_AND_INTERFACE_INFO(0x19d2, 0xfff1, 0xff, 0xff, 0xff) }, + /* AC8710_V3 */ { USB_DEVICE(0x19d2, 0xfff6) }, { USB_DEVICE(0x19d2, 0xfff7) }, { USB_DEVICE(0x19d2, 0xfff8) }, diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig index 8470e1b..1dd0604 100644 --- a/drivers/usb/storage/Kconfig +++ b/drivers/usb/storage/Kconfig @@ -18,7 +18,9 @@ config USB_STORAGE This option depends on 'SCSI' support being enabled, but you probably also need 'SCSI device support: SCSI disk support' - (BLK_DEV_SD) for most USB storage devices. + (BLK_DEV_SD) for most USB storage devices. Some devices also + will require 'Probe all LUNs on each SCSI device' + (SCSI_MULTI_LUN). To compile this driver as a module, choose M here: the module will be called usb-storage. diff --git a/drivers/usb/storage/onetouch.c b/drivers/usb/storage/onetouch.c index 2696489..74e2aa2 100644 --- a/drivers/usb/storage/onetouch.c +++ b/drivers/usb/storage/onetouch.c @@ -30,7 +30,6 @@ #include <linux/kernel.h> #include <linux/input.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/usb/input.h> diff --git a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c index 5dfb4c3..12e3c2f 100644 --- a/drivers/usb/storage/protocol.c +++ b/drivers/usb/storage/protocol.c @@ -135,69 +135,42 @@ unsigned int usb_stor_access_xfer_buf(unsigned char *buffer, unsigned int buflen, struct scsi_cmnd *srb, struct scatterlist **sgptr, unsigned int *offset, enum xfer_buf_dir dir) { - unsigned int cnt; + unsigned int cnt = 0; struct scatterlist *sg = *sgptr; + struct sg_mapping_iter miter; + unsigned int nents = scsi_sg_count(srb); - /* We have to go through the list one entry - * at a time. Each s-g entry contains some number of pages, and - * each page has to be kmap()'ed separately. If the page is already - * in kernel-addressable memory then kmap() will return its address. - * If the page is not directly accessible -- such as a user buffer - * located in high memory -- then kmap() will map it to a temporary - * position in the kernel's virtual address space. - */ - - if (!sg) + if (sg) + nents = sg_nents(sg); + else sg = scsi_sglist(srb); - /* This loop handles a single s-g list entry, which may - * include multiple pages. Find the initial page structure - * and the starting offset within the page, and update - * the *offset and **sgptr values for the next loop. - */ - cnt = 0; - while (cnt < buflen && sg) { - struct page *page = sg_page(sg) + - ((sg->offset + *offset) >> PAGE_SHIFT); - unsigned int poff = (sg->offset + *offset) & (PAGE_SIZE-1); - unsigned int sglen = sg->length - *offset; - - if (sglen > buflen - cnt) { - - /* Transfer ends within this s-g entry */ - sglen = buflen - cnt; - *offset += sglen; - } else { + sg_miter_start(&miter, sg, nents, dir == FROM_XFER_BUF ? + SG_MITER_FROM_SG: SG_MITER_TO_SG); - /* Transfer continues to next s-g entry */ - *offset = 0; - sg = sg_next(sg); - } + if (!sg_miter_skip(&miter, *offset)) + return cnt; + + while (sg_miter_next(&miter) && cnt < buflen) { + unsigned int len = min_t(unsigned int, miter.length, + buflen - cnt); + + if (dir == FROM_XFER_BUF) + memcpy(buffer + cnt, miter.addr, len); + else + memcpy(miter.addr, buffer + cnt, len); - /* Transfer the data for all the pages in this - * s-g entry. For each page: call kmap(), do the - * transfer, and call kunmap() immediately after. */ - while (sglen > 0) { - unsigned int plen = min(sglen, (unsigned int) - PAGE_SIZE - poff); - unsigned char *ptr = kmap(page); - - if (dir == TO_XFER_BUF) - memcpy(ptr + poff, buffer + cnt, plen); - else - memcpy(buffer + cnt, ptr + poff, plen); - kunmap(page); - - /* Start at the beginning of the next page */ - poff = 0; - ++page; - cnt += plen; - sglen -= plen; + if (*offset + len < miter.piter.sg->length) { + *offset += len; + *sgptr = miter.piter.sg; + } else { + *offset = 0; + *sgptr = sg_next(miter.piter.sg); } + cnt += len; } - *sgptr = sg; + sg_miter_stop(&miter); - /* Return the amount actually transferred */ return cnt; } EXPORT_SYMBOL_GPL(usb_stor_access_xfer_buf); diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index 18509e6..9d38ddc 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -78,6 +78,8 @@ static const char* host_info(struct Scsi_Host *host) static int slave_alloc (struct scsi_device *sdev) { + struct us_data *us = host_to_us(sdev->host); + /* * Set the INQUIRY transfer length to 36. We don't use any of * the extra data and many devices choke if asked for more or @@ -102,6 +104,10 @@ static int slave_alloc (struct scsi_device *sdev) */ blk_queue_update_dma_alignment(sdev->request_queue, (512 - 1)); + /* Tell the SCSI layer if we know there is more than one LUN */ + if (us->protocol == USB_PR_BULK && us->max_lun > 0) + sdev->sdev_bflags |= BLIST_FORCELUN; + return 0; } diff --git a/drivers/usb/storage/unusual_cypress.h b/drivers/usb/storage/unusual_cypress.h index 65a6a75..82e8ed0 100644 --- a/drivers/usb/storage/unusual_cypress.h +++ b/drivers/usb/storage/unusual_cypress.h @@ -31,7 +31,7 @@ UNUSUAL_DEV( 0x04b4, 0x6831, 0x0000, 0x9999, "Cypress ISD-300LP", USB_SC_CYP_ATACB, USB_PR_DEVICE, NULL, 0), -UNUSUAL_DEV( 0x14cd, 0x6116, 0x0000, 0x0219, +UNUSUAL_DEV( 0x14cd, 0x6116, 0x0160, 0x0160, "Super Top", "USB 2.0 SATA BRIDGE", USB_SC_CYP_ATACB, USB_PR_DEVICE, NULL, 0), diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index de32cfa..adbeb25 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -234,6 +234,13 @@ UNUSUAL_DEV( 0x0421, 0x0495, 0x0370, 0x0370, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_MAX_SECTORS_64 ), +/* Patch submitted by Mikhail Zolotaryov <lebon@lebon.org.ua> */ +UNUSUAL_DEV( 0x0421, 0x06aa, 0x1110, 0x1110, + "Nokia", + "502", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_MAX_SECTORS_64 ), + #ifdef NO_SDDR09 UNUSUAL_DEV( 0x0436, 0x0005, 0x0100, 0x0100, "Microtech", @@ -1448,6 +1455,13 @@ UNUSUAL_DEV( 0x0f88, 0x042e, 0x0100, 0x0100, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_FIX_CAPACITY ), +/* Reported by Moritz Moeller-Herrmann <moritz-kernel@moeller-herrmann.de> */ +UNUSUAL_DEV( 0x0fca, 0x8004, 0x0201, 0x0201, + "Research In Motion", + "BlackBerry Bold 9000", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_MAX_SECTORS_64 ), + /* Reported by Michael Stattmann <michael@stattmann.com> */ UNUSUAL_DEV( 0x0fce, 0xd008, 0x0000, 0x0000, "Sony Ericsson", diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 5c4fe07..1c0b89f2 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -53,7 +53,6 @@ #include <linux/errno.h> #include <linux/freezer.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/kthread.h> #include <linux/mutex.h> diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c index ff97652..545d09b 100644 --- a/drivers/usb/usb-skeleton.c +++ b/drivers/usb/usb-skeleton.c @@ -14,7 +14,6 @@ #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/kref.h> diff --git a/drivers/usb/wusbcore/cbaf.c b/drivers/usb/wusbcore/cbaf.c index f06ed82..da1b872 100644 --- a/drivers/usb/wusbcore/cbaf.c +++ b/drivers/usb/wusbcore/cbaf.c @@ -144,7 +144,7 @@ static int cbaf_check(struct cbaf *cbaf) CBAF_REQ_GET_ASSOCIATION_INFORMATION, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, cbaf->usb_iface->cur_altsetting->desc.bInterfaceNumber, - cbaf->buffer, cbaf->buffer_size, 1000 /* FIXME: arbitrary */); + cbaf->buffer, cbaf->buffer_size, USB_CTRL_GET_TIMEOUT); if (result < 0) { dev_err(dev, "Cannot get available association types: %d\n", result); @@ -184,7 +184,7 @@ static int cbaf_check(struct cbaf *cbaf) assoc_request = itr; if (top - itr < sizeof(*assoc_request)) { - dev_err(dev, "Not enough data to decode associaton " + dev_err(dev, "Not enough data to decode association " "request (%zu vs %zu bytes needed)\n", top - itr, sizeof(*assoc_request)); break; @@ -235,7 +235,7 @@ static int cbaf_check(struct cbaf *cbaf) static const struct wusb_cbaf_host_info cbaf_host_info_defaults = { .AssociationTypeId_hdr = WUSB_AR_AssociationTypeId, - .AssociationTypeId = cpu_to_le16(AR_TYPE_WUSB), + .AssociationTypeId = cpu_to_le16(AR_TYPE_WUSB), .AssociationSubTypeId_hdr = WUSB_AR_AssociationSubTypeId, .AssociationSubTypeId = cpu_to_le16(AR_TYPE_WUSB_RETRIEVE_HOST_INFO), .CHID_hdr = WUSB_AR_CHID, @@ -260,12 +260,13 @@ static int cbaf_send_host_info(struct cbaf *cbaf) hi->HostFriendlyName_hdr.len = cpu_to_le16(name_len); hi_size = sizeof(*hi) + name_len; - return usb_control_msg(cbaf->usb_dev, usb_sndctrlpipe(cbaf->usb_dev, 0), + return usb_control_msg(cbaf->usb_dev, + usb_sndctrlpipe(cbaf->usb_dev, 0), CBAF_REQ_SET_ASSOCIATION_RESPONSE, USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0x0101, cbaf->usb_iface->cur_altsetting->desc.bInterfaceNumber, - hi, hi_size, 1000 /* FIXME: arbitrary */); + hi, hi_size, USB_CTRL_SET_TIMEOUT); } /* @@ -288,9 +289,10 @@ static int cbaf_cdid_get(struct cbaf *cbaf) CBAF_REQ_GET_ASSOCIATION_REQUEST, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0x0200, cbaf->usb_iface->cur_altsetting->desc.bInterfaceNumber, - di, cbaf->buffer_size, 1000 /* FIXME: arbitrary */); + di, cbaf->buffer_size, USB_CTRL_GET_TIMEOUT); if (result < 0) { - dev_err(dev, "Cannot request device information: %d\n", result); + dev_err(dev, "Cannot request device information: %d\n", + result); return result; } @@ -491,11 +493,11 @@ static DEVICE_ATTR(wusb_device_name, 0600, cbaf_wusb_device_name_show, NULL); static const struct wusb_cbaf_cc_data cbaf_cc_data_defaults = { .AssociationTypeId_hdr = WUSB_AR_AssociationTypeId, - .AssociationTypeId = cpu_to_le16(AR_TYPE_WUSB), + .AssociationTypeId = cpu_to_le16(AR_TYPE_WUSB), .AssociationSubTypeId_hdr = WUSB_AR_AssociationSubTypeId, .AssociationSubTypeId = cpu_to_le16(AR_TYPE_WUSB_ASSOCIATE), .Length_hdr = WUSB_AR_Length, - .Length = cpu_to_le32(sizeof(struct wusb_cbaf_cc_data)), + .Length = cpu_to_le32(sizeof(struct wusb_cbaf_cc_data)), .ConnectionContext_hdr = WUSB_AR_ConnectionContext, .BandGroups_hdr = WUSB_AR_BandGroups, }; @@ -536,7 +538,7 @@ static int cbaf_cc_upload(struct cbaf *cbaf) CBAF_REQ_SET_ASSOCIATION_RESPONSE, USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0x0201, cbaf->usb_iface->cur_altsetting->desc.bInterfaceNumber, - ccd, sizeof(*ccd), 1000 /* FIXME: arbitrary */); + ccd, sizeof(*ccd), USB_CTRL_SET_TIMEOUT); return result; } diff --git a/drivers/usb/wusbcore/crypto.c b/drivers/usb/wusbcore/crypto.c index 7e4bf95..9a95b2d 100644 --- a/drivers/usb/wusbcore/crypto.c +++ b/drivers/usb/wusbcore/crypto.c @@ -87,7 +87,7 @@ struct aes_ccm_block { * B1 contains l(a), the MAC header, the encryption offset and padding. * * If EO is nonzero, additional blocks are built from payload bytes - * until EO is exahusted (FIXME: padding to 16 bytes, I guess). The + * until EO is exhausted (FIXME: padding to 16 bytes, I guess). The * padding is not xmitted. */ diff --git a/drivers/usb/wusbcore/devconnect.c b/drivers/usb/wusbcore/devconnect.c index f14e792..3b959e8 100644 --- a/drivers/usb/wusbcore/devconnect.c +++ b/drivers/usb/wusbcore/devconnect.c @@ -265,9 +265,9 @@ static void wusbhc_devconnect_acked_work(struct work_struct *work) * Addresses: because WUSB hosts have no downstream hubs, we can do a * 1:1 mapping between 'port number' and device * address. This simplifies many things, as during this - * initial connect phase the USB stack has no knoledge of + * initial connect phase the USB stack has no knowledge of * the device and hasn't assigned an address yet--we know - * USB's choose_address() will use the same euristics we + * USB's choose_address() will use the same heuristics we * use here, so we can assume which address will be assigned. * * USB stack always assigns address 1 to the root hub, so diff --git a/drivers/usb/wusbcore/mmc.c b/drivers/usb/wusbcore/mmc.c index b71760c..4474126 100644 --- a/drivers/usb/wusbcore/mmc.c +++ b/drivers/usb/wusbcore/mmc.c @@ -206,13 +206,15 @@ int wusbhc_start(struct wusbhc *wusbhc) result = wusbhc_devconnect_start(wusbhc); if (result < 0) { - dev_err(dev, "error enabling device connections: %d\n", result); + dev_err(dev, "error enabling device connections: %d\n", + result); goto error_devconnect_start; } result = wusbhc_sec_start(wusbhc); if (result < 0) { - dev_err(dev, "error starting security in the HC: %d\n", result); + dev_err(dev, "error starting security in the HC: %d\n", + result); goto error_sec_start; } @@ -284,7 +286,8 @@ int wusbhc_chid_set(struct wusbhc *wusbhc, const struct wusb_ckhdid *chid) wusbhc->uwb_rc = uwb_rc_get_by_grandpa(wusbhc->dev->parent); if (wusbhc->uwb_rc == NULL) { result = -ENODEV; - dev_err(wusbhc->dev, "Cannot get associated UWB Host Controller\n"); + dev_err(wusbhc->dev, + "Cannot get associated UWB Host Controller\n"); goto error_rc_get; } diff --git a/drivers/usb/wusbcore/pal.c b/drivers/usb/wusbcore/pal.c index 59e100c..090f273 100644 --- a/drivers/usb/wusbcore/pal.c +++ b/drivers/usb/wusbcore/pal.c @@ -22,6 +22,7 @@ static void wusbhc_channel_changed(struct uwb_pal *pal, int channel) { struct wusbhc *wusbhc = container_of(pal, struct wusbhc, pal); + dev_dbg(wusbhc->dev, "%s: channel = %d\n", __func__, channel); if (channel < 0) wusbhc_stop(wusbhc); else diff --git a/drivers/usb/wusbcore/reservation.c b/drivers/usb/wusbcore/reservation.c index ead79f7..d5efd0f 100644 --- a/drivers/usb/wusbcore/reservation.c +++ b/drivers/usb/wusbcore/reservation.c @@ -51,6 +51,7 @@ static void wusbhc_rsv_complete_cb(struct uwb_rsv *rsv) struct uwb_mas_bm mas; char buf[72]; + dev_dbg(dev, "%s: state = %d\n", __func__, rsv->state); switch (rsv->state) { case UWB_RSV_STATE_O_ESTABLISHED: uwb_rsv_get_usable_mas(rsv, &mas); diff --git a/drivers/usb/wusbcore/security.c b/drivers/usb/wusbcore/security.c index 4c40d0d..95be995 100644 --- a/drivers/usb/wusbcore/security.c +++ b/drivers/usb/wusbcore/security.c @@ -33,7 +33,8 @@ static void wusbhc_gtk_rekey_work(struct work_struct *work); int wusbhc_sec_create(struct wusbhc *wusbhc) { - wusbhc->gtk.descr.bLength = sizeof(wusbhc->gtk.descr) + sizeof(wusbhc->gtk.data); + wusbhc->gtk.descr.bLength = sizeof(wusbhc->gtk.descr) + + sizeof(wusbhc->gtk.data); wusbhc->gtk.descr.bDescriptorType = USB_DT_KEY; wusbhc->gtk.descr.bReserved = 0; wusbhc->gtk_index = 0; @@ -56,7 +57,7 @@ void wusbhc_sec_destroy(struct wusbhc *wusbhc) * @wusb_dev: the device whose PTK the TKID is for * (or NULL for a TKID for a GTK) * - * The generated TKID consist of two parts: the device's authenicated + * The generated TKID consists of two parts: the device's authenticated * address (or 0 or a GTK); and an incrementing number. This ensures * that TKIDs cannot be shared between devices and by the time the * incrementing number wraps around the older TKIDs will no longer be @@ -138,7 +139,7 @@ const char *wusb_et_name(u8 x) case USB_ENC_TYPE_WIRED: return "wired"; case USB_ENC_TYPE_CCM_1: return "CCM-1"; case USB_ENC_TYPE_RSA_1: return "RSA-1"; - default: return "unknown"; + default: return "unknown"; } } EXPORT_SYMBOL_GPL(wusb_et_name); @@ -165,7 +166,7 @@ static int wusb_dev_set_encryption(struct usb_device *usb_dev, int value) result = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), USB_REQ_SET_ENCRYPTION, USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, - value, 0, NULL, 0, 1000 /* FIXME: arbitrary */); + value, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); if (result < 0) dev_err(dev, "Can't set device's WUSB encryption to " "%s (value %d): %d\n", @@ -191,7 +192,7 @@ static int wusb_dev_set_gtk(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev) USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, USB_DT_KEY << 8 | key_index, 0, &wusbhc->gtk.descr, wusbhc->gtk.descr.bLength, - 1000); + USB_CTRL_SET_TIMEOUT); } @@ -222,7 +223,8 @@ int wusb_dev_sec_add(struct wusbhc *wusbhc, secd_size = le16_to_cpu(secd->wTotalLength); new_secd = krealloc(secd, secd_size, GFP_KERNEL); if (new_secd == NULL) { - dev_err(dev, "Can't allocate space for security descriptors\n"); + dev_err(dev, + "Can't allocate space for security descriptors\n"); goto out; } secd = new_secd; @@ -301,8 +303,9 @@ int wusb_dev_update_address(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev) /* Set address 0 */ result = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), - USB_REQ_SET_ADDRESS, 0, - 0, 0, NULL, 0, 1000 /* FIXME: arbitrary */); + USB_REQ_SET_ADDRESS, + USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, + 0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); if (result < 0) { dev_err(dev, "auth failed: can't set address 0: %d\n", result); @@ -316,9 +319,10 @@ int wusb_dev_update_address(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev) /* Set new (authenticated) address. */ result = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), - USB_REQ_SET_ADDRESS, 0, - new_address, 0, NULL, 0, - 1000 /* FIXME: arbitrary */); + USB_REQ_SET_ADDRESS, + USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, + new_address, 0, NULL, 0, + USB_CTRL_SET_TIMEOUT); if (result < 0) { dev_err(dev, "auth failed: can't set address %u: %d\n", new_address, result); @@ -375,13 +379,13 @@ int wusb_dev_4way_handshake(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev, hs[0].bReserved = 0; memcpy(hs[0].CDID, &wusb_dev->cdid, sizeof(hs[0].CDID)); get_random_bytes(&hs[0].nonce, sizeof(hs[0].nonce)); - memset(hs[0].MIC, 0, sizeof(hs[0].MIC)); /* Per WUSB1.0[T7-22] */ + memset(hs[0].MIC, 0, sizeof(hs[0].MIC)); /* Per WUSB1.0[T7-22] */ result = usb_control_msg( usb_dev, usb_sndctrlpipe(usb_dev, 0), USB_REQ_SET_HANDSHAKE, USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, - 1, 0, &hs[0], sizeof(hs[0]), 1000 /* FIXME: arbitrary */); + 1, 0, &hs[0], sizeof(hs[0]), USB_CTRL_SET_TIMEOUT); if (result < 0) { dev_err(dev, "Handshake1: request failed: %d\n", result); goto error_hs1; @@ -392,7 +396,7 @@ int wusb_dev_4way_handshake(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev, usb_dev, usb_rcvctrlpipe(usb_dev, 0), USB_REQ_GET_HANDSHAKE, USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE, - 2, 0, &hs[1], sizeof(hs[1]), 1000 /* FIXME: arbitrary */); + 2, 0, &hs[1], sizeof(hs[1]), USB_CTRL_GET_TIMEOUT); if (result < 0) { dev_err(dev, "Handshake2: request failed: %d\n", result); goto error_hs2; @@ -422,7 +426,7 @@ int wusb_dev_4way_handshake(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev, } /* Setup the CCM nonce */ - memset(&ccm_n.sfn, 0, sizeof(ccm_n.sfn)); /* Per WUSB1.0[6.5.2] */ + memset(&ccm_n.sfn, 0, sizeof(ccm_n.sfn)); /* Per WUSB1.0[6.5.2] */ memcpy(ccm_n.tkid, &tkid_le, sizeof(ccm_n.tkid)); ccm_n.src_addr = wusbhc->uwb_rc->uwb_dev.dev_addr; ccm_n.dest_addr.data[0] = wusb_dev->addr; @@ -469,7 +473,7 @@ int wusb_dev_4way_handshake(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev, usb_dev, usb_sndctrlpipe(usb_dev, 0), USB_REQ_SET_HANDSHAKE, USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, - 3, 0, &hs[2], sizeof(hs[2]), 1000 /* FIXME: arbitrary */); + 3, 0, &hs[2], sizeof(hs[2]), USB_CTRL_SET_TIMEOUT); if (result < 0) { dev_err(dev, "Handshake3: request failed: %d\n", result); goto error_hs3; @@ -553,11 +557,13 @@ static void wusbhc_gtk_rekey_work(struct work_struct *work) list_for_each_entry_safe(wusb_dev, wusb_dev_next, &rekey_list, rekey_node) { list_del_init(&wusb_dev->rekey_node); - dev_dbg(&wusb_dev->usb_dev->dev, "%s: rekey device at port %d\n", + dev_dbg(&wusb_dev->usb_dev->dev, + "%s: rekey device at port %d\n", __func__, wusb_dev->port_idx); if (wusb_dev_set_gtk(wusbhc, wusb_dev) < 0) { - dev_err(&wusb_dev->usb_dev->dev, "%s: rekey device at port %d failed\n", + dev_err(&wusb_dev->usb_dev->dev, + "%s: rekey device at port %d failed\n", __func__, wusb_dev->port_idx); } wusb_dev_put(wusb_dev); diff --git a/drivers/usb/wusbcore/wa-hc.h b/drivers/usb/wusbcore/wa-hc.h index e614f02..a2ef84b 100644 --- a/drivers/usb/wusbcore/wa-hc.h +++ b/drivers/usb/wusbcore/wa-hc.h @@ -36,7 +36,7 @@ * * hcd glue with the USB API Host Controller Interface API. * - * nep Notification EndPoint managent: collect notifications + * nep Notification EndPoint management: collect notifications * and queue them with the workqueue daemon. * * Handle notifications as coming from the NEP. Sends them @@ -144,7 +144,7 @@ enum wa_quirks { * * @wa_descr Can be accessed without locking because it is in * the same area where the device descriptors were - * read, so it is guaranteed to exist umodified while + * read, so it is guaranteed to exist unmodified while * the device exists. * * Endianess has been converted to CPU's. @@ -167,8 +167,8 @@ enum wa_quirks { * submitted from an atomic context). * * FIXME: this needs to be layered up: a wusbhc layer (for sharing - * comonalities with WHCI), a wa layer (for sharing - * comonalities with DWA-RC). + * commonalities with WHCI), a wa layer (for sharing + * commonalities with DWA-RC). */ struct wahc { struct usb_device *usb_dev; @@ -197,10 +197,10 @@ struct wahc { struct mutex rpipe_mutex; /* assigning resources to endpoints */ /* - * dti_state is used to track the state of the dti_urb. When dti_state + * dti_state is used to track the state of the dti_urb. When dti_state * is WA_DTI_ISOC_PACKET_STATUS_PENDING, dti_isoc_xfer_in_progress and - * dti_isoc_xfer_seg identify which xfer the incoming isoc packet status - * refers to. + * dti_isoc_xfer_seg identify which xfer the incoming isoc packet + * status refers to. */ enum wa_dti_state dti_state; u32 dti_isoc_xfer_in_progress; @@ -211,7 +211,7 @@ struct wahc { void *dti_buf; size_t dti_buf_size; - unsigned long dto_in_use; /* protect dto endoint serialization. */ + unsigned long dto_in_use; /* protect dto endoint serialization */ s32 status; /* For reading status */ @@ -332,7 +332,7 @@ static inline int rpipe_avail_inc(struct wa_rpipe *rpipe) /* Transferring data */ extern int wa_urb_enqueue(struct wahc *, struct usb_host_endpoint *, struct urb *, gfp_t); -extern int wa_urb_dequeue(struct wahc *, struct urb *); +extern int wa_urb_dequeue(struct wahc *, struct urb *, int); extern void wa_handle_notif_xfer(struct wahc *, struct wa_notif_hdr *); @@ -345,7 +345,7 @@ extern void wa_handle_notif_xfer(struct wahc *, struct wa_notif_hdr *); * it...no RC specific function is called...unless I miss * something. * - * FIXME: has to go away in favour of an 'struct' hcd based sollution + * FIXME: has to go away in favour of a 'struct' hcd based solution */ static inline struct wahc *wa_get(struct wahc *wa) { @@ -366,7 +366,7 @@ static inline int __wa_feature(struct wahc *wa, unsigned op, u16 feature) USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, feature, wa->usb_iface->cur_altsetting->desc.bInterfaceNumber, - NULL, 0, 1000 /* FIXME: arbitrary */); + NULL, 0, USB_CTRL_SET_TIMEOUT); } @@ -400,8 +400,7 @@ s32 __wa_get_status(struct wahc *wa) USB_REQ_GET_STATUS, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, wa->usb_iface->cur_altsetting->desc.bInterfaceNumber, - &wa->status, sizeof(wa->status), - 1000 /* FIXME: arbitrary */); + &wa->status, sizeof(wa->status), USB_CTRL_GET_TIMEOUT); if (result >= 0) result = wa->status; return result; diff --git a/drivers/usb/wusbcore/wa-nep.c b/drivers/usb/wusbcore/wa-nep.c index ada4e08..60a10d2 100644 --- a/drivers/usb/wusbcore/wa-nep.c +++ b/drivers/usb/wusbcore/wa-nep.c @@ -69,8 +69,8 @@ struct wa_notif_work { * [the wuswad daemon, basically] * * @_nw: Pointer to a descriptor which has the pointer to the - * @wa, the size of the buffer and the work queue - * structure (so we can free all when done). + * @wa, the size of the buffer and the work queue + * structure (so we can free all when done). * @returns 0 if ok, < 0 errno code on error. * * All notifications follow the same format; they need to start with a @@ -93,7 +93,8 @@ static void wa_notif_dispatch(struct work_struct *ws) { void *itr; u8 missing = 0; - struct wa_notif_work *nw = container_of(ws, struct wa_notif_work, work); + struct wa_notif_work *nw = container_of(ws, struct wa_notif_work, + work); struct wahc *wa = nw->wa; struct wa_notif_hdr *notif_hdr; size_t size; @@ -271,7 +272,8 @@ int wa_nep_create(struct wahc *wa, struct usb_interface *iface) wa->nep_buffer_size = 1024; wa->nep_buffer = kmalloc(wa->nep_buffer_size, GFP_KERNEL); if (wa->nep_buffer == NULL) { - dev_err(dev, "Unable to allocate notification's read buffer\n"); + dev_err(dev, + "Unable to allocate notification's read buffer\n"); goto error_nep_buffer; } wa->nep_urb = usb_alloc_urb(0, GFP_KERNEL); diff --git a/drivers/usb/wusbcore/wa-rpipe.c b/drivers/usb/wusbcore/wa-rpipe.c index b48e74c..6ca80a4 100644 --- a/drivers/usb/wusbcore/wa-rpipe.c +++ b/drivers/usb/wusbcore/wa-rpipe.c @@ -57,7 +57,6 @@ * urb->dev->devnum, to make sure that we always have the right * destination address. */ -#include <linux/init.h> #include <linux/atomic.h> #include <linux/bitmap.h> #include <linux/slab.h> @@ -80,7 +79,7 @@ static int __rpipe_get_descr(struct wahc *wa, USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_RPIPE, USB_DT_RPIPE<<8, index, descr, sizeof(*descr), - 1000 /* FIXME: arbitrary */); + USB_CTRL_GET_TIMEOUT); if (result < 0) { dev_err(dev, "rpipe %u: get descriptor failed: %d\n", index, (int)result); @@ -118,7 +117,7 @@ static int __rpipe_set_descr(struct wahc *wa, USB_REQ_SET_DESCRIPTOR, USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_RPIPE, USB_DT_RPIPE<<8, index, descr, sizeof(*descr), - HZ / 10); + USB_CTRL_SET_TIMEOUT); if (result < 0) { dev_err(dev, "rpipe %u: set descriptor failed: %d\n", index, (int)result); @@ -184,7 +183,7 @@ EXPORT_SYMBOL_GPL(rpipe_destroy); /* * Locate an idle rpipe, create an structure for it and return it * - * @wa is referenced and unlocked + * @wa is referenced and unlocked * @crs enum rpipe_attr, required endpoint characteristics * * The rpipe can be used only sequentially (not in parallel). @@ -237,7 +236,7 @@ static int __rpipe_reset(struct wahc *wa, unsigned index) wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0), USB_REQ_RPIPE_RESET, USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_RPIPE, - 0, index, NULL, 0, 1000 /* FIXME: arbitrary */); + 0, index, NULL, 0, USB_CTRL_SET_TIMEOUT); if (result < 0) dev_err(dev, "rpipe %u: reset failed: %d\n", index, result); @@ -308,7 +307,7 @@ out: /* * Aim an rpipe to its device & endpoint destination * - * Make sure we change the address to unauthenticathed if the device + * Make sure we change the address to unauthenticated if the device * is WUSB and it is not authenticated. */ static int rpipe_aim(struct wa_rpipe *rpipe, struct wahc *wa, @@ -329,7 +328,8 @@ static int rpipe_aim(struct wa_rpipe *rpipe, struct wahc *wa, } unauth = usb_dev->wusb && !usb_dev->authenticated ? 0x80 : 0; __rpipe_reset(wa, le16_to_cpu(rpipe->descr.wRPipeIndex)); - atomic_set(&rpipe->segs_available, le16_to_cpu(rpipe->descr.wRequests)); + atomic_set(&rpipe->segs_available, + le16_to_cpu(rpipe->descr.wRequests)); /* FIXME: block allocation system; request with queuing and timeout */ /* FIXME: compute so seg_size > ep->maxpktsize */ rpipe->descr.wBlocks = cpu_to_le16(16); /* given */ @@ -527,7 +527,7 @@ void rpipe_ep_disable(struct wahc *wa, struct usb_host_endpoint *ep) wa->usb_dev, usb_rcvctrlpipe(wa->usb_dev, 0), USB_REQ_RPIPE_ABORT, USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_RPIPE, - 0, index, NULL, 0, 1000 /* FIXME: arbitrary */); + 0, index, NULL, 0, USB_CTRL_SET_TIMEOUT); rpipe_put(rpipe); } mutex_unlock(&wa->rpipe_mutex); @@ -548,9 +548,8 @@ void rpipe_clear_feature_stalled(struct wahc *wa, struct usb_host_endpoint *ep) wa->usb_dev, usb_rcvctrlpipe(wa->usb_dev, 0), USB_REQ_CLEAR_FEATURE, USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_RPIPE, - RPIPE_STALL, index, NULL, 0, 1000); + RPIPE_STALL, index, NULL, 0, USB_CTRL_SET_TIMEOUT); } mutex_unlock(&wa->rpipe_mutex); } EXPORT_SYMBOL_GPL(rpipe_clear_feature_stalled); - diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c index ed5abe8..3cd96e9 100644 --- a/drivers/usb/wusbcore/wa-xfer.c +++ b/drivers/usb/wusbcore/wa-xfer.c @@ -79,7 +79,6 @@ * availability of the different required components (blocks, * rpipes, segment slots, etc), we go scheduling them. Painful. */ -#include <linux/init.h> #include <linux/spinlock.h> #include <linux/slab.h> #include <linux/hash.h> @@ -124,6 +123,8 @@ struct wa_seg { u8 index; /* which segment we are */ int isoc_frame_count; /* number of isoc frames in this segment. */ int isoc_frame_offset; /* starting frame offset in the xfer URB. */ + /* Isoc frame that the current transfer buffer corresponds to. */ + int isoc_frame_index; int isoc_size; /* size of all isoc frames sent by this seg. */ enum wa_seg_status status; ssize_t result; /* bytes xfered or error */ @@ -158,8 +159,6 @@ struct wa_xfer { unsigned is_dma:1; size_t seg_size; int result; - /* Isoc frame that the current transfer buffer corresponds to. */ - int dto_isoc_frame_index; gfp_t gfp; /* allocation mask */ @@ -282,6 +281,7 @@ static void wa_xfer_giveback(struct wa_xfer *xfer) spin_lock_irqsave(&xfer->wa->xfer_list_lock, flags); list_del_init(&xfer->list_node); + usb_hcd_unlink_urb_from_ep(&(xfer->wa->wusb->usb_hcd), xfer->urb); spin_unlock_irqrestore(&xfer->wa->xfer_list_lock, flags); /* FIXME: segmentation broken -- kills DWA */ wusbhc_giveback_urb(xfer->wa->wusb, xfer->urb, xfer->result); @@ -372,10 +372,10 @@ static unsigned __wa_xfer_is_done(struct wa_xfer *xfer) seg->result); goto out; case WA_SEG_ABORTED: - dev_dbg(dev, "xfer %p ID %08X#%u ABORTED: result %d\n", - xfer, wa_xfer_id(xfer), seg->index, - urb->status); - xfer->result = urb->status; + xfer->result = seg->result; + dev_dbg(dev, "xfer %p ID %08X#%u: ABORTED result %zu(0x%08zX)\n", + xfer, wa_xfer_id(xfer), seg->index, seg->result, + seg->result); goto out; default: dev_warn(dev, "xfer %p ID %08X#%u: is_done bad state %d\n", @@ -487,13 +487,14 @@ static int __wa_seg_calculate_isoc_frame_count(struct wa_xfer *xfer, && ((segment_size + iso_frame_desc[index].length) <= xfer->seg_size)) { /* - * For Alereon HWA devices, only include an isoc frame in a - * segment if it is physically contiguous with the previous + * For Alereon HWA devices, only include an isoc frame in an + * out segment if it is physically contiguous with the previous * frame. This is required because those devices expect * the isoc frames to be sent as a single USB transaction as * opposed to one transaction per frame with standard HWA. */ if ((xfer->wa->quirks & WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC) + && (xfer->is_inbound == 0) && (index > isoc_frame_offset) && ((iso_frame_desc[index - 1].offset + iso_frame_desc[index - 1].length) != @@ -536,14 +537,8 @@ static ssize_t __wa_xfer_setup_sizes(struct wa_xfer *xfer, result = sizeof(struct wa_xfer_bi); break; case USB_ENDPOINT_XFER_ISOC: - if (usb_pipeout(urb->pipe)) { - *pxfer_type = WA_XFER_TYPE_ISO; - result = sizeof(struct wa_xfer_hwaiso); - } else { - dev_err(dev, "FIXME: ISOC IN not implemented\n"); - result = -ENOSYS; - goto error; - } + *pxfer_type = WA_XFER_TYPE_ISO; + result = sizeof(struct wa_xfer_hwaiso); break; default: /* never happens */ @@ -554,10 +549,22 @@ static ssize_t __wa_xfer_setup_sizes(struct wa_xfer *xfer, xfer->is_dma = urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP ? 1 : 0; maxpktsize = le16_to_cpu(rpipe->descr.wMaxPacketSize); + xfer->seg_size = le16_to_cpu(rpipe->descr.wBlocks) + * 1 << (xfer->wa->wa_descr->bRPipeBlockSize - 1); + /* Compute the segment size and make sure it is a multiple of + * the maxpktsize (WUSB1.0[8.3.3.1])...not really too much of + * a check (FIXME) */ + if (xfer->seg_size < maxpktsize) { + dev_err(dev, + "HW BUG? seg_size %zu smaller than maxpktsize %zu\n", + xfer->seg_size, maxpktsize); + result = -EINVAL; + goto error; + } + xfer->seg_size = (xfer->seg_size / maxpktsize) * maxpktsize; if ((rpipe->descr.bmAttribute & 0x3) == USB_ENDPOINT_XFER_ISOC) { int index = 0; - xfer->seg_size = maxpktsize; xfer->segs = 0; /* * loop over urb->number_of_packets to determine how many @@ -570,19 +577,6 @@ static ssize_t __wa_xfer_setup_sizes(struct wa_xfer *xfer, ++xfer->segs; } } else { - xfer->seg_size = le16_to_cpu(rpipe->descr.wBlocks) - * 1 << (xfer->wa->wa_descr->bRPipeBlockSize - 1); - /* Compute the segment size and make sure it is a multiple of - * the maxpktsize (WUSB1.0[8.3.3.1])...not really too much of - * a check (FIXME) */ - if (xfer->seg_size < maxpktsize) { - dev_err(dev, - "HW BUG? seg_size %zu smaller than maxpktsize %zu\n", - xfer->seg_size, maxpktsize); - result = -EINVAL; - goto error; - } - xfer->seg_size = (xfer->seg_size / maxpktsize) * maxpktsize; xfer->segs = DIV_ROUND_UP(urb->transfer_buffer_length, xfer->seg_size); if (xfer->segs == 0 && *pxfer_type == WA_XFER_TYPE_CTL) @@ -700,23 +694,23 @@ static void wa_seg_dto_cb(struct urb *urb) if (usb_pipeisoc(xfer->urb->pipe)) { /* Alereon HWA sends all isoc frames in a single transfer. */ if (wa->quirks & WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC) - xfer->dto_isoc_frame_index += seg->isoc_frame_count; + seg->isoc_frame_index += seg->isoc_frame_count; else - xfer->dto_isoc_frame_index += 1; - if (xfer->dto_isoc_frame_index < seg->isoc_frame_count) { + seg->isoc_frame_index += 1; + if (seg->isoc_frame_index < seg->isoc_frame_count) { data_send_done = 0; holding_dto = 1; /* checked in error cases. */ /* * if this is the last isoc frame of the segment, we * can release DTO after sending this frame. */ - if ((xfer->dto_isoc_frame_index + 1) >= + if ((seg->isoc_frame_index + 1) >= seg->isoc_frame_count) release_dto = 1; } dev_dbg(dev, "xfer 0x%08X#%u: isoc frame = %d, holding_dto = %d, release_dto = %d.\n", - wa_xfer_id(xfer), seg->index, - xfer->dto_isoc_frame_index, holding_dto, release_dto); + wa_xfer_id(xfer), seg->index, seg->isoc_frame_index, + holding_dto, release_dto); } spin_unlock_irqrestore(&xfer->lock, flags); @@ -736,8 +730,7 @@ static void wa_seg_dto_cb(struct urb *urb) * send the URB and release DTO if we no longer need it. */ __wa_populate_dto_urb_isoc(xfer, seg, - seg->isoc_frame_offset + - xfer->dto_isoc_frame_index); + seg->isoc_frame_offset + seg->isoc_frame_index); /* resubmit the URB with the next isoc frame. */ result = usb_submit_urb(seg->dto_urb, GFP_ATOMIC); @@ -844,7 +837,7 @@ static void wa_seg_iso_pack_desc_cb(struct urb *urb) wa_xfer_id(xfer), seg->index, urb->status); if (edc_inc(&wa->nep_edc, EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)){ - dev_err(dev, "DTO: URB max acceptable errors exceeded, resetting device\n"); + dev_err(dev, "iso xfer: URB max acceptable errors exceeded, resetting device\n"); wa_reset_all(wa); } if (seg->status != WA_SEG_ERROR) { @@ -1108,7 +1101,7 @@ static int __wa_xfer_setup_segs(struct wa_xfer *xfer, size_t xfer_hdr_size) const struct usb_endpoint_descriptor *dto_epd = xfer->wa->dto_epd; struct wa_seg *seg; size_t buf_itr, buf_size, buf_itr_size; - int xfer_isoc_frame_offset = 0; + int isoc_frame_offset = 0; result = -ENOMEM; xfer->seg = kcalloc(xfer->segs, sizeof(xfer->seg[0]), GFP_ATOMIC); @@ -1121,10 +1114,14 @@ static int __wa_xfer_setup_segs(struct wa_xfer *xfer, size_t xfer_hdr_size) size_t iso_pkt_descr_size = 0; int seg_isoc_frame_count = 0, seg_isoc_size = 0; + /* + * Adjust the size of the segment object to contain space for + * the isoc packet descriptor buffer. + */ if (usb_pipeisoc(xfer->urb->pipe)) { seg_isoc_frame_count = __wa_seg_calculate_isoc_frame_count(xfer, - xfer_isoc_frame_offset, &seg_isoc_size); + isoc_frame_offset, &seg_isoc_size); iso_pkt_descr_size = sizeof(struct wa_xfer_packet_info_hwaiso) + @@ -1137,15 +1134,40 @@ static int __wa_xfer_setup_segs(struct wa_xfer *xfer, size_t xfer_hdr_size) wa_seg_init(seg); seg->xfer = xfer; seg->index = cnt; - seg->isoc_frame_count = seg_isoc_frame_count; - seg->isoc_frame_offset = xfer_isoc_frame_offset; - seg->isoc_size = seg_isoc_size; usb_fill_bulk_urb(&seg->tr_urb, usb_dev, usb_sndbulkpipe(usb_dev, dto_epd->bEndpointAddress), &seg->xfer_hdr, xfer_hdr_size, wa_seg_tr_cb, seg); buf_itr_size = min(buf_size, xfer->seg_size); + + if (usb_pipeisoc(xfer->urb->pipe)) { + seg->isoc_frame_count = seg_isoc_frame_count; + seg->isoc_frame_offset = isoc_frame_offset; + seg->isoc_size = seg_isoc_size; + /* iso packet descriptor. */ + seg->isoc_pack_desc_urb = + usb_alloc_urb(0, GFP_ATOMIC); + if (seg->isoc_pack_desc_urb == NULL) + goto error_iso_pack_desc_alloc; + /* + * The buffer for the isoc packet descriptor starts + * after the transfer request header in the + * segment object memory buffer. + */ + usb_fill_bulk_urb( + seg->isoc_pack_desc_urb, usb_dev, + usb_sndbulkpipe(usb_dev, + dto_epd->bEndpointAddress), + (void *)(&seg->xfer_hdr) + + xfer_hdr_size, + iso_pkt_descr_size, + wa_seg_iso_pack_desc_cb, seg); + + /* adjust starting frame offset for next seg. */ + isoc_frame_offset += seg_isoc_frame_count; + } + if (xfer->is_inbound == 0 && buf_size > 0) { /* outbound data. */ seg->dto_urb = usb_alloc_urb(0, GFP_ATOMIC); @@ -1158,25 +1180,6 @@ static int __wa_xfer_setup_segs(struct wa_xfer *xfer, size_t xfer_hdr_size) NULL, 0, wa_seg_dto_cb, seg); if (usb_pipeisoc(xfer->urb->pipe)) { - /* iso packet descriptor. */ - seg->isoc_pack_desc_urb = - usb_alloc_urb(0, GFP_ATOMIC); - if (seg->isoc_pack_desc_urb == NULL) - goto error_iso_pack_desc_alloc; - /* - * The buffer for the isoc packet descriptor - * after the transfer request header in the - * segment object memory buffer. - */ - usb_fill_bulk_urb( - seg->isoc_pack_desc_urb, usb_dev, - usb_sndbulkpipe(usb_dev, - dto_epd->bEndpointAddress), - (void *)(&seg->xfer_hdr) + - xfer_hdr_size, - iso_pkt_descr_size, - wa_seg_iso_pack_desc_cb, seg); - /* * Fill in the xfer buffer information for the * first isoc frame. Subsequent frames in this @@ -1184,9 +1187,7 @@ static int __wa_xfer_setup_segs(struct wa_xfer *xfer, size_t xfer_hdr_size) * DTO completion routine, if needed. */ __wa_populate_dto_urb_isoc(xfer, seg, - xfer_isoc_frame_offset); - /* adjust starting frame offset for next seg. */ - xfer_isoc_frame_offset += seg_isoc_frame_count; + seg->isoc_frame_offset); } else { /* fill in the xfer buffer information. */ result = __wa_populate_dto_urb(xfer, seg, @@ -1207,10 +1208,11 @@ static int __wa_xfer_setup_segs(struct wa_xfer *xfer, size_t xfer_hdr_size) * Use the fact that cnt is left at were it failed. The remaining * segments will be cleaned up by wa_xfer_destroy. */ -error_iso_pack_desc_alloc: error_seg_outbound_populate: usb_free_urb(xfer->seg[cnt]->dto_urb); error_dto_alloc: + usb_free_urb(xfer->seg[cnt]->isoc_pack_desc_urb); +error_iso_pack_desc_alloc: kfree(xfer->seg[cnt]); xfer->seg[cnt] = NULL; error_seg_kmalloc: @@ -1259,8 +1261,11 @@ static int __wa_xfer_setup(struct wa_xfer *xfer, struct urb *urb) for (cnt = 1; cnt < xfer->segs; cnt++) { struct wa_xfer_packet_info_hwaiso *packet_desc; struct wa_seg *seg = xfer->seg[cnt]; + struct wa_xfer_hwaiso *xfer_iso; xfer_hdr = &seg->xfer_hdr; + xfer_iso = container_of(xfer_hdr, + struct wa_xfer_hwaiso, hdr); packet_desc = ((void *)xfer_hdr) + xfer_hdr_size; /* * Copy values from the 0th header. Segment specific @@ -1270,6 +1275,8 @@ static int __wa_xfer_setup(struct wa_xfer *xfer, struct urb *urb) xfer_hdr->bTransferSegment = cnt; xfer_hdr->dwTransferLength = cpu_to_le32(seg->isoc_size); + xfer_iso->dwNumOfPackets = + cpu_to_le32(seg->isoc_frame_count); __wa_setup_isoc_packet_descr(packet_desc, xfer, seg); seg->status = WA_SEG_READY; } @@ -1320,32 +1327,31 @@ static int __wa_seg_submit(struct wa_rpipe *rpipe, struct wa_xfer *xfer, } /* submit the isoc packet descriptor if present. */ if (seg->isoc_pack_desc_urb) { - struct wahc *wa = xfer->wa; - result = usb_submit_urb(seg->isoc_pack_desc_urb, GFP_ATOMIC); + seg->isoc_frame_index = 0; if (result < 0) { pr_err("%s: xfer %p#%u: ISO packet descriptor submit failed: %d\n", __func__, xfer, seg->index, result); goto error_iso_pack_desc_submit; } - xfer->dto_isoc_frame_index = 0; - /* - * If this segment contains more than one isoc frame, hold - * onto the dto resource until we send all frames. - * Only applies to non-Alereon devices. - */ - if (((wa->quirks & WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC) == 0) - && (seg->isoc_frame_count > 1)) - *dto_done = 0; } /* submit the out data if this is an out request. */ if (seg->dto_urb) { + struct wahc *wa = xfer->wa; result = usb_submit_urb(seg->dto_urb, GFP_ATOMIC); if (result < 0) { pr_err("%s: xfer %p#%u: DTO submit failed: %d\n", __func__, xfer, seg->index, result); goto error_dto_submit; } + /* + * If this segment contains more than one isoc frame, hold + * onto the dto resource until we send all frames. + * Only applies to non-Alereon devices. + */ + if (((wa->quirks & WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC) == 0) + && (seg->isoc_frame_count > 1)) + *dto_done = 0; } seg->status = WA_SEG_SUBMITTED; rpipe_avail_dec(rpipe); @@ -1567,7 +1573,8 @@ static int wa_urb_enqueue_b(struct wa_xfer *xfer) wusb_dev = __wusb_dev_get_by_usb_dev(wusbhc, urb->dev); if (wusb_dev == NULL) { mutex_unlock(&wusbhc->mutex); - pr_err("%s: error wusb dev gone\n", __func__); + dev_err(&(urb->dev->dev), "%s: error wusb dev gone\n", + __func__); goto error_dev_gone; } mutex_unlock(&wusbhc->mutex); @@ -1576,18 +1583,18 @@ static int wa_urb_enqueue_b(struct wa_xfer *xfer) xfer->wusb_dev = wusb_dev; result = urb->status; if (urb->status != -EINPROGRESS) { - pr_err("%s: error_dequeued\n", __func__); + dev_err(&(urb->dev->dev), "%s: error_dequeued\n", __func__); goto error_dequeued; } result = __wa_xfer_setup(xfer, urb); if (result < 0) { - pr_err("%s: error_xfer_setup\n", __func__); + dev_err(&(urb->dev->dev), "%s: error_xfer_setup\n", __func__); goto error_xfer_setup; } result = __wa_xfer_submit(xfer); if (result < 0) { - pr_err("%s: error_xfer_submit\n", __func__); + dev_err(&(urb->dev->dev), "%s: error_xfer_submit\n", __func__); goto error_xfer_submit; } spin_unlock_irqrestore(&xfer->lock, flags); @@ -1730,6 +1737,12 @@ int wa_urb_enqueue(struct wahc *wa, struct usb_host_endpoint *ep, dump_stack(); } + spin_lock_irqsave(&wa->xfer_list_lock, my_flags); + result = usb_hcd_link_urb_to_ep(&(wa->wusb->usb_hcd), urb); + spin_unlock_irqrestore(&wa->xfer_list_lock, my_flags); + if (result < 0) + goto error_link_urb; + result = -ENOMEM; xfer = kzalloc(sizeof(*xfer), gfp); if (xfer == NULL) @@ -1769,6 +1782,9 @@ int wa_urb_enqueue(struct wahc *wa, struct usb_host_endpoint *ep, __func__, result); wa_put(xfer->wa); wa_xfer_put(xfer); + spin_lock_irqsave(&wa->xfer_list_lock, my_flags); + usb_hcd_unlink_urb_from_ep(&(wa->wusb->usb_hcd), urb); + spin_unlock_irqrestore(&wa->xfer_list_lock, my_flags); return result; } } @@ -1777,6 +1793,10 @@ int wa_urb_enqueue(struct wahc *wa, struct usb_host_endpoint *ep, error_dequeued: kfree(xfer); error_kmalloc: + spin_lock_irqsave(&wa->xfer_list_lock, my_flags); + usb_hcd_unlink_urb_from_ep(&(wa->wusb->usb_hcd), urb); + spin_unlock_irqrestore(&wa->xfer_list_lock, my_flags); +error_link_urb: return result; } EXPORT_SYMBOL_GPL(wa_urb_enqueue); @@ -1799,7 +1819,7 @@ EXPORT_SYMBOL_GPL(wa_urb_enqueue); * asynch request] and then make sure we cancel each segment. * */ -int wa_urb_dequeue(struct wahc *wa, struct urb *urb) +int wa_urb_dequeue(struct wahc *wa, struct urb *urb, int status) { unsigned long flags, flags2; struct wa_xfer *xfer; @@ -1807,6 +1827,14 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb) struct wa_rpipe *rpipe; unsigned cnt, done = 0, xfer_abort_pending; unsigned rpipe_ready = 0; + int result; + + /* check if it is safe to unlink. */ + spin_lock_irqsave(&wa->xfer_list_lock, flags); + result = usb_hcd_check_unlink_urb(&(wa->wusb->usb_hcd), urb, status); + spin_unlock_irqrestore(&wa->xfer_list_lock, flags); + if (result) + return result; xfer = urb->hcpriv; if (xfer == NULL) { @@ -1822,9 +1850,10 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb) pr_debug("%s: DEQUEUE xfer id 0x%08X\n", __func__, wa_xfer_id(xfer)); rpipe = xfer->ep->hcpriv; if (rpipe == NULL) { - pr_debug("%s: xfer id 0x%08X has no RPIPE. %s", - __func__, wa_xfer_id(xfer), + pr_debug("%s: xfer %p id 0x%08X has no RPIPE. %s", + __func__, xfer, wa_xfer_id(xfer), "Probably already aborted.\n" ); + result = -ENOENT; goto out_unlock; } /* Check the delayed list -> if there, release and complete */ @@ -1855,6 +1884,7 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb) * segments will be completed in the DTI interrupt. */ seg->status = WA_SEG_ABORTED; + seg->result = -ENOENT; spin_lock_irqsave(&rpipe->seg_lock, flags2); list_del(&seg->list_node); xfer->segs_done++; @@ -1894,12 +1924,12 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb) wa_xfer_completion(xfer); if (rpipe_ready) wa_xfer_delayed_run(rpipe); - return 0; + return result; out_unlock: spin_unlock_irqrestore(&xfer->lock, flags); out: - return 0; + return result; dequeue_delayed: list_del_init(&xfer->list_node); @@ -1935,7 +1965,7 @@ static int wa_xfer_status_to_errno(u8 status) [WA_XFER_STATUS_NOT_FOUND] = 0, [WA_XFER_STATUS_INSUFFICIENT_RESOURCE] = -ENOMEM, [WA_XFER_STATUS_TRANSACTION_ERROR] = -EILSEQ, - [WA_XFER_STATUS_ABORTED] = -EINTR, + [WA_XFER_STATUS_ABORTED] = -ENOENT, [WA_XFER_STATUS_RPIPE_NOT_READY] = EINVAL, [WA_XFER_INVALID_FORMAT] = EINVAL, [WA_XFER_UNEXPECTED_SEGMENT_NUMBER] = EINVAL, @@ -1968,7 +1998,7 @@ static int wa_xfer_status_to_errno(u8 status) * the xfer will complete cleanly. */ static void wa_complete_remaining_xfer_segs(struct wa_xfer *xfer, - struct wa_seg *incoming_seg) + struct wa_seg *incoming_seg, enum wa_seg_status status) { int index; struct wa_rpipe *rpipe = xfer->ep->hcpriv; @@ -1990,7 +2020,7 @@ static void wa_complete_remaining_xfer_segs(struct wa_xfer *xfer, */ case WA_SEG_DELAYED: xfer->segs_done++; - current_seg->status = incoming_seg->status; + current_seg->status = status; break; case WA_SEG_ABORTED: break; @@ -2003,6 +2033,77 @@ static void wa_complete_remaining_xfer_segs(struct wa_xfer *xfer, } } +/* Populate the wa->buf_in_urb based on the current isoc transfer state. */ +static void __wa_populate_buf_in_urb_isoc(struct wahc *wa, struct wa_xfer *xfer, + struct wa_seg *seg, int curr_iso_frame) +{ + BUG_ON(wa->buf_in_urb->status == -EINPROGRESS); + + /* this should always be 0 before a resubmit. */ + wa->buf_in_urb->num_mapped_sgs = 0; + wa->buf_in_urb->transfer_dma = xfer->urb->transfer_dma + + xfer->urb->iso_frame_desc[curr_iso_frame].offset; + wa->buf_in_urb->transfer_buffer_length = + xfer->urb->iso_frame_desc[curr_iso_frame].length; + wa->buf_in_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + wa->buf_in_urb->transfer_buffer = NULL; + wa->buf_in_urb->sg = NULL; + wa->buf_in_urb->num_sgs = 0; + wa->buf_in_urb->context = seg; +} + +/* Populate the wa->buf_in_urb based on the current transfer state. */ +static int wa_populate_buf_in_urb(struct wahc *wa, struct wa_xfer *xfer, + unsigned int seg_idx, unsigned int bytes_transferred) +{ + int result = 0; + struct wa_seg *seg = xfer->seg[seg_idx]; + + BUG_ON(wa->buf_in_urb->status == -EINPROGRESS); + /* this should always be 0 before a resubmit. */ + wa->buf_in_urb->num_mapped_sgs = 0; + + if (xfer->is_dma) { + wa->buf_in_urb->transfer_dma = xfer->urb->transfer_dma + + (seg_idx * xfer->seg_size); + wa->buf_in_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + wa->buf_in_urb->transfer_buffer = NULL; + wa->buf_in_urb->sg = NULL; + wa->buf_in_urb->num_sgs = 0; + } else { + /* do buffer or SG processing. */ + wa->buf_in_urb->transfer_flags &= ~URB_NO_TRANSFER_DMA_MAP; + + if (xfer->urb->transfer_buffer) { + wa->buf_in_urb->transfer_buffer = + xfer->urb->transfer_buffer + + (seg_idx * xfer->seg_size); + wa->buf_in_urb->sg = NULL; + wa->buf_in_urb->num_sgs = 0; + } else { + /* allocate an SG list to store seg_size bytes + and copy the subset of the xfer->urb->sg + that matches the buffer subset we are + about to read. */ + wa->buf_in_urb->sg = wa_xfer_create_subset_sg( + xfer->urb->sg, + seg_idx * xfer->seg_size, + bytes_transferred, + &(wa->buf_in_urb->num_sgs)); + + if (!(wa->buf_in_urb->sg)) { + wa->buf_in_urb->num_sgs = 0; + result = -ENOMEM; + } + wa->buf_in_urb->transfer_buffer = NULL; + } + } + wa->buf_in_urb->transfer_buffer_length = bytes_transferred; + wa->buf_in_urb->context = seg; + + return result; +} + /* * Process a xfer result completion message * @@ -2016,12 +2117,13 @@ static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer, int result; struct device *dev = &wa->usb_iface->dev; unsigned long flags; - u8 seg_idx; + unsigned int seg_idx; struct wa_seg *seg; struct wa_rpipe *rpipe; unsigned done = 0; u8 usb_status; unsigned rpipe_ready = 0; + unsigned bytes_transferred = le32_to_cpu(xfer_result->dwTransferLength); spin_lock_irqsave(&xfer->lock, flags); seg_idx = xfer_result->bTransferSegment & 0x7f; @@ -2054,66 +2156,34 @@ static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer, /* FIXME: we ignore warnings, tally them for stats */ if (usb_status & 0x40) /* Warning?... */ usb_status = 0; /* ... pass */ - if (usb_pipeisoc(xfer->urb->pipe)) { + /* + * If the last segment bit is set, complete the remaining segments. + * When the current segment is completed, either in wa_buf_in_cb for + * transfers with data or below for no data, the xfer will complete. + */ + if (xfer_result->bTransferSegment & 0x80) + wa_complete_remaining_xfer_segs(xfer, seg, WA_SEG_DONE); + if (usb_pipeisoc(xfer->urb->pipe) + && (le32_to_cpu(xfer_result->dwNumOfPackets) > 0)) { /* set up WA state to read the isoc packet status next. */ wa->dti_isoc_xfer_in_progress = wa_xfer_id(xfer); wa->dti_isoc_xfer_seg = seg_idx; wa->dti_state = WA_DTI_ISOC_PACKET_STATUS_PENDING; - } else if (xfer->is_inbound) { /* IN data phase: read to buffer */ + } else if (xfer->is_inbound && !usb_pipeisoc(xfer->urb->pipe) + && (bytes_transferred > 0)) { + /* IN data phase: read to buffer */ seg->status = WA_SEG_DTI_PENDING; - BUG_ON(wa->buf_in_urb->status == -EINPROGRESS); - /* this should always be 0 before a resubmit. */ - wa->buf_in_urb->num_mapped_sgs = 0; - - if (xfer->is_dma) { - wa->buf_in_urb->transfer_dma = - xfer->urb->transfer_dma - + (seg_idx * xfer->seg_size); - wa->buf_in_urb->transfer_flags - |= URB_NO_TRANSFER_DMA_MAP; - wa->buf_in_urb->transfer_buffer = NULL; - wa->buf_in_urb->sg = NULL; - wa->buf_in_urb->num_sgs = 0; - } else { - /* do buffer or SG processing. */ - wa->buf_in_urb->transfer_flags - &= ~URB_NO_TRANSFER_DMA_MAP; - - if (xfer->urb->transfer_buffer) { - wa->buf_in_urb->transfer_buffer = - xfer->urb->transfer_buffer - + (seg_idx * xfer->seg_size); - wa->buf_in_urb->sg = NULL; - wa->buf_in_urb->num_sgs = 0; - } else { - /* allocate an SG list to store seg_size bytes - and copy the subset of the xfer->urb->sg - that matches the buffer subset we are - about to read. */ - wa->buf_in_urb->sg = wa_xfer_create_subset_sg( - xfer->urb->sg, - seg_idx * xfer->seg_size, - le32_to_cpu( - xfer_result->dwTransferLength), - &(wa->buf_in_urb->num_sgs)); - - if (!(wa->buf_in_urb->sg)) { - wa->buf_in_urb->num_sgs = 0; - goto error_sg_alloc; - } - wa->buf_in_urb->transfer_buffer = NULL; - } - } - wa->buf_in_urb->transfer_buffer_length = - le32_to_cpu(xfer_result->dwTransferLength); - wa->buf_in_urb->context = seg; + result = wa_populate_buf_in_urb(wa, xfer, seg_idx, + bytes_transferred); + if (result < 0) + goto error_buf_in_populate; result = usb_submit_urb(wa->buf_in_urb, GFP_ATOMIC); if (result < 0) goto error_submit_buf_in; } else { - /* OUT data phase, complete it -- */ + /* OUT data phase or no data, complete it -- */ seg->status = WA_SEG_DONE; - seg->result = le32_to_cpu(xfer_result->dwTransferLength); + seg->result = bytes_transferred; xfer->segs_done++; rpipe_ready = rpipe_avail_inc(rpipe); done = __wa_xfer_is_done(xfer); @@ -2137,13 +2207,13 @@ error_submit_buf_in: seg->result = result; kfree(wa->buf_in_urb->sg); wa->buf_in_urb->sg = NULL; -error_sg_alloc: +error_buf_in_populate: __wa_xfer_abort(xfer); seg->status = WA_SEG_ERROR; error_complete: xfer->segs_done++; rpipe_ready = rpipe_avail_inc(rpipe); - wa_complete_remaining_xfer_segs(xfer, seg); + wa_complete_remaining_xfer_segs(xfer, seg, seg->status); done = __wa_xfer_is_done(xfer); /* * queue work item to clear STALL for control endpoints. @@ -2172,7 +2242,7 @@ error_complete: error_bad_seg: spin_unlock_irqrestore(&xfer->lock, flags); - wa_urb_dequeue(wa, xfer->urb); + wa_urb_dequeue(wa, xfer->urb, -ENOENT); if (printk_ratelimit()) dev_err(dev, "xfer %p#%u: bad segment\n", xfer, seg_idx); if (edc_inc(&wa->dti_edc, EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) { @@ -2192,7 +2262,7 @@ segment_aborted: * * inbound transfers: need to schedule a buf_in_urb read */ -static void wa_process_iso_packet_status(struct wahc *wa, struct urb *urb) +static int wa_process_iso_packet_status(struct wahc *wa, struct urb *urb) { struct device *dev = &wa->usb_iface->dev; struct wa_xfer_packet_status_hwaiso *packet_status; @@ -2201,8 +2271,8 @@ static void wa_process_iso_packet_status(struct wahc *wa, struct urb *urb) unsigned long flags; struct wa_seg *seg; struct wa_rpipe *rpipe; - unsigned done = 0; - unsigned rpipe_ready = 0, seg_index; + unsigned done = 0, dti_busy = 0, data_frame_count = 0, seg_index; + unsigned first_frame_index = 0, rpipe_ready = 0; int expected_size; /* We have a xfer result buffer; check it */ @@ -2238,18 +2308,48 @@ static void wa_process_iso_packet_status(struct wahc *wa, struct urb *urb) le16_to_cpu(packet_status->wLength)); goto error_bad_seg; } - /* isoc packet status and lengths back xfer urb. */ + /* write isoc packet status and lengths back to the xfer urb. */ status_array = packet_status->PacketStatus; + xfer->urb->start_frame = + wa->wusb->usb_hcd.driver->get_frame_number(&wa->wusb->usb_hcd); for (seg_index = 0; seg_index < seg->isoc_frame_count; ++seg_index) { - xfer->urb->iso_frame_desc[seg->index].status = + struct usb_iso_packet_descriptor *iso_frame_desc = + xfer->urb->iso_frame_desc; + const int urb_frame_index = + seg->isoc_frame_offset + seg_index; + + iso_frame_desc[urb_frame_index].status = wa_xfer_status_to_errno( le16_to_cpu(status_array[seg_index].PacketStatus)); - xfer->urb->iso_frame_desc[seg->index].actual_length = + iso_frame_desc[urb_frame_index].actual_length = le16_to_cpu(status_array[seg_index].PacketLength); + /* track the number of frames successfully transferred. */ + if (iso_frame_desc[urb_frame_index].actual_length > 0) { + /* save the starting frame index for buf_in_urb. */ + if (!data_frame_count) + first_frame_index = seg_index; + ++data_frame_count; + } } - if (!xfer->is_inbound) { - /* OUT transfer, complete it -- */ + if (xfer->is_inbound && data_frame_count) { + int result; + + seg->isoc_frame_index = first_frame_index; + /* submit a read URB for the first frame with data. */ + __wa_populate_buf_in_urb_isoc(wa, xfer, seg, + seg->isoc_frame_index + seg->isoc_frame_offset); + + result = usb_submit_urb(wa->buf_in_urb, GFP_ATOMIC); + if (result < 0) { + dev_err(dev, "DTI Error: Could not submit buf in URB (%d)", + result); + wa_reset_all(wa); + } else if (data_frame_count > 1) + /* If we need to read multiple frames, set DTI busy. */ + dti_busy = 1; + } else { + /* OUT transfer or no more IN data, complete it -- */ seg->status = WA_SEG_DONE; xfer->segs_done++; rpipe_ready = rpipe_avail_inc(rpipe); @@ -2262,13 +2362,13 @@ static void wa_process_iso_packet_status(struct wahc *wa, struct urb *urb) if (rpipe_ready) wa_xfer_delayed_run(rpipe); wa_xfer_put(xfer); - return; + return dti_busy; error_bad_seg: spin_unlock_irqrestore(&xfer->lock, flags); wa_xfer_put(xfer); error_parse_buffer: - return; + return dti_busy; } /* @@ -2288,7 +2388,7 @@ static void wa_buf_in_cb(struct urb *urb) struct wahc *wa; struct device *dev; struct wa_rpipe *rpipe; - unsigned rpipe_ready; + unsigned rpipe_ready = 0, seg_index, isoc_data_frame_count = 0; unsigned long flags; u8 done = 0; @@ -2296,19 +2396,61 @@ static void wa_buf_in_cb(struct urb *urb) kfree(urb->sg); urb->sg = NULL; + spin_lock_irqsave(&xfer->lock, flags); + wa = xfer->wa; + dev = &wa->usb_iface->dev; + + if (usb_pipeisoc(xfer->urb->pipe)) { + /* + * Find the next isoc frame with data. Bail out after + * isoc_data_frame_count > 1 since there is no need to walk + * the entire frame array. We just need to know if + * isoc_data_frame_count is 0, 1, or >1. + */ + seg_index = seg->isoc_frame_index + 1; + while ((seg_index < seg->isoc_frame_count) + && (isoc_data_frame_count <= 1)) { + struct usb_iso_packet_descriptor *iso_frame_desc = + xfer->urb->iso_frame_desc; + const int urb_frame_index = + seg->isoc_frame_offset + seg_index; + + if (iso_frame_desc[urb_frame_index].actual_length > 0) { + /* save the index of the next frame with data */ + if (!isoc_data_frame_count) + seg->isoc_frame_index = seg_index; + ++isoc_data_frame_count; + } + ++seg_index; + } + } + spin_unlock_irqrestore(&xfer->lock, flags); + switch (urb->status) { case 0: spin_lock_irqsave(&xfer->lock, flags); - wa = xfer->wa; - dev = &wa->usb_iface->dev; - rpipe = xfer->ep->hcpriv; - dev_dbg(dev, "xfer %p#%u: data in done (%zu bytes)\n", - xfer, seg->index, (size_t)urb->actual_length); - seg->status = WA_SEG_DONE; - seg->result = urb->actual_length; - xfer->segs_done++; - rpipe_ready = rpipe_avail_inc(rpipe); - done = __wa_xfer_is_done(xfer); + + seg->result += urb->actual_length; + if (isoc_data_frame_count > 0) { + int result; + /* submit a read URB for the first frame with data. */ + __wa_populate_buf_in_urb_isoc(wa, xfer, seg, + seg->isoc_frame_index + seg->isoc_frame_offset); + result = usb_submit_urb(wa->buf_in_urb, GFP_ATOMIC); + if (result < 0) { + dev_err(dev, "DTI Error: Could not submit buf in URB (%d)", + result); + wa_reset_all(wa); + } + } else { + rpipe = xfer->ep->hcpriv; + seg->status = WA_SEG_DONE; + dev_dbg(dev, "xfer %p#%u: data in done (%zu bytes)\n", + xfer, seg->index, seg->result); + xfer->segs_done++; + rpipe_ready = rpipe_avail_inc(rpipe); + done = __wa_xfer_is_done(xfer); + } spin_unlock_irqrestore(&xfer->lock, flags); if (done) wa_xfer_completion(xfer); @@ -2320,8 +2462,6 @@ static void wa_buf_in_cb(struct urb *urb) break; default: /* Other errors ... */ spin_lock_irqsave(&xfer->lock, flags); - wa = xfer->wa; - dev = &wa->usb_iface->dev; rpipe = xfer->ep->hcpriv; if (printk_ratelimit()) dev_err(dev, "xfer %p#%u: data in error %d\n", @@ -2344,6 +2484,20 @@ static void wa_buf_in_cb(struct urb *urb) if (rpipe_ready) wa_xfer_delayed_run(rpipe); } + /* + * If we are in this callback and isoc_data_frame_count > 0, it means + * that the dti_urb submission was delayed in wa_dti_cb. Once + * isoc_data_frame_count gets to 1, we can submit the deferred URB + * since the last buf_in_urb was just submitted. + */ + if (isoc_data_frame_count == 1) { + int result = usb_submit_urb(wa->dti_urb, GFP_ATOMIC); + if (result < 0) { + dev_err(dev, "DTI Error: Could not submit DTI URB (%d)\n", + result); + wa_reset_all(wa); + } + } } /* @@ -2374,7 +2528,7 @@ static void wa_buf_in_cb(struct urb *urb) */ static void wa_dti_cb(struct urb *urb) { - int result; + int result, dti_busy = 0; struct wahc *wa = urb->context; struct device *dev = &wa->usb_iface->dev; u32 xfer_id; @@ -2422,7 +2576,7 @@ static void wa_dti_cb(struct urb *urb) wa_xfer_result_chew(wa, xfer, xfer_result); wa_xfer_put(xfer); } else if (wa->dti_state == WA_DTI_ISOC_PACKET_STATUS_PENDING) { - wa_process_iso_packet_status(wa, urb); + dti_busy = wa_process_iso_packet_status(wa, urb); } else { dev_err(dev, "DTI Error: unexpected EP state = %d\n", wa->dti_state); @@ -2445,12 +2599,15 @@ static void wa_dti_cb(struct urb *urb) dev_err(dev, "DTI: URB error %d\n", urb->status); break; } - /* Resubmit the DTI URB */ - result = usb_submit_urb(wa->dti_urb, GFP_ATOMIC); - if (result < 0) { - dev_err(dev, "DTI Error: Could not submit DTI URB (%d), " - "resetting\n", result); - wa_reset_all(wa); + + /* Resubmit the DTI URB if we are not busy processing isoc in frames. */ + if (!dti_busy) { + result = usb_submit_urb(wa->dti_urb, GFP_ATOMIC); + if (result < 0) { + dev_err(dev, "DTI Error: Could not submit DTI URB (%d)\n", + result); + wa_reset_all(wa); + } } out: return; @@ -2517,8 +2674,8 @@ void wa_handle_notif_xfer(struct wahc *wa, struct wa_notif_hdr *notif_hdr) NULL, 0, wa_buf_in_cb, wa); result = usb_submit_urb(wa->dti_urb, GFP_KERNEL); if (result < 0) { - dev_err(dev, "DTI Error: Could not submit DTI URB (%d), " - "resetting\n", result); + dev_err(dev, "DTI Error: Could not submit DTI URB (%d) resetting\n", + result); goto error_dti_urb_submit; } out: diff --git a/drivers/usb/wusbcore/wusbhc.c b/drivers/usb/wusbcore/wusbhc.c index 742c607..3e1ba51 100644 --- a/drivers/usb/wusbcore/wusbhc.c +++ b/drivers/usb/wusbcore/wusbhc.c @@ -55,7 +55,8 @@ static struct wusbhc *usbhc_dev_to_wusbhc(struct device *dev) * value of trust_timeout is jiffies. */ static ssize_t wusb_trust_timeout_show(struct device *dev, - struct device_attribute *attr, char *buf) + struct device_attribute *attr, + char *buf) { struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev); @@ -173,7 +174,8 @@ static ssize_t wusb_phy_rate_store(struct device *dev, wusbhc->phy_rate = phy_rate; return size; } -static DEVICE_ATTR(wusb_phy_rate, 0644, wusb_phy_rate_show, wusb_phy_rate_store); +static DEVICE_ATTR(wusb_phy_rate, 0644, wusb_phy_rate_show, + wusb_phy_rate_store); static ssize_t wusb_dnts_show(struct device *dev, struct device_attribute *attr, @@ -227,7 +229,8 @@ static ssize_t wusb_retry_count_store(struct device *dev, if (result != 1) return -EINVAL; - wusbhc->retry_count = max_t(uint8_t, retry_count, WUSB_RETRY_COUNT_MAX); + wusbhc->retry_count = max_t(uint8_t, retry_count, + WUSB_RETRY_COUNT_MAX); return size; } @@ -321,7 +324,8 @@ int wusbhc_b_create(struct wusbhc *wusbhc) result = sysfs_create_group(wusbhc_kobj(wusbhc), &wusbhc_attr_group); if (result < 0) { - dev_err(dev, "Cannot register WUSBHC attributes: %d\n", result); + dev_err(dev, "Cannot register WUSBHC attributes: %d\n", + result); goto error_create_attr_group; } @@ -419,13 +423,14 @@ EXPORT_SYMBOL_GPL(wusb_cluster_id_put); * - After a successful transfer, update the trust timeout timestamp * for the WUSB device. * - * - [WUSB] sections 4.13 and 7.5.1 specifies the stop retrasmittion + * - [WUSB] sections 4.13 and 7.5.1 specify the stop retransmission * condition for the WCONNECTACK_IE is that the host has observed * the associated device responding to a control transfer. */ void wusbhc_giveback_urb(struct wusbhc *wusbhc, struct urb *urb, int status) { - struct wusb_dev *wusb_dev = __wusb_dev_get_by_usb_dev(wusbhc, urb->dev); + struct wusb_dev *wusb_dev = __wusb_dev_get_by_usb_dev(wusbhc, + urb->dev); if (status == 0 && wusb_dev) { wusb_dev->entry_ts = jiffies; diff --git a/drivers/usb/wusbcore/wusbhc.h b/drivers/usb/wusbcore/wusbhc.h index 6bd3b81..2384add 100644 --- a/drivers/usb/wusbcore/wusbhc.h +++ b/drivers/usb/wusbcore/wusbhc.h @@ -164,7 +164,7 @@ struct wusb_port { * functions/operations that only deal with general Wireless USB HC * issues use this data type to refer to the host. * - * @usb_hcd Instantiation of a USB host controller + * @usb_hcd Instantiation of a USB host controller * (initialized by upper layer [HWA=HC or WHCI]. * * @dev Device that implements this; initialized by the @@ -196,7 +196,7 @@ struct wusb_port { * @ports_max Number of simultaneous device connections (fake * ports) this HC will take. Read-only. * - * @port Array of port status for each fake root port. Guaranteed to + * @port Array of port status for each fake root port. Guaranteed to * always be the same length during device existence * [this allows for some unlocked but referenced reading]. * @@ -329,7 +329,8 @@ void wusbhc_pal_unregister(struct wusbhc *wusbhc); * This is a safe assumption as @usb_dev->bus is referenced all the * time during the @usb_dev life cycle. */ -static inline struct usb_hcd *usb_hcd_get_by_usb_dev(struct usb_device *usb_dev) +static inline +struct usb_hcd *usb_hcd_get_by_usb_dev(struct usb_device *usb_dev) { struct usb_hcd *usb_hcd; usb_hcd = container_of(usb_dev->bus, struct usb_hcd, self); |