From dc93b41a15614f51bc1ea47d12b245d60f79731d Mon Sep 17 00:00:00 2001 From: Mickael Maison Date: Tue, 23 Dec 2014 17:34:43 +0100 Subject: [PATCH 001/214] usb: dwc3: Fixed a typo in comments Fixed a typo in comments Signed-off-by: Mickael Maison Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index f03b136ecfce..bd9f48d0dff8 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2077,7 +2077,7 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force) * We have discussed this with the IP Provider and it was * suggested to giveback all requests here, but give HW some * extra time to synchronize with the interconnect. We're using - * an arbitraty 100us delay for that. + * an arbitrary 100us delay for that. * * Note also that a similar handling was tested by Synopsys * (thanks a lot Paul) and nothing bad has come out of it. From d16cd0b8f70062f7d0f96adc42f86421b71eb21c Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Sat, 20 Dec 2014 23:30:11 +0100 Subject: [PATCH 002/214] usb: gadget: udc: s3c2410_udc.c: Remove some unused functions Removes some functions that are not used anywhere: s3c2410_udc_clear_ep_state() s3c2410_udc_set_ep0_sse_out() This was partially found by using a static code analysis program called cppcheck. Signed-off-by: Rickard Strandqvist Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/s3c2410_udc.c | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/drivers/usb/gadget/udc/s3c2410_udc.c b/drivers/usb/gadget/udc/s3c2410_udc.c index 824cf12e9add..256a67ba2158 100644 --- a/drivers/usb/gadget/udc/s3c2410_udc.c +++ b/drivers/usb/gadget/udc/s3c2410_udc.c @@ -238,14 +238,6 @@ static inline void s3c2410_udc_set_ep0_de_out(void __iomem *base) S3C2410_UDC_EP0_CSR_REG); } -static inline void s3c2410_udc_set_ep0_sse_out(void __iomem *base) -{ - udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG); - udc_writeb(base, (S3C2410_UDC_EP0_CSR_SOPKTRDY - | S3C2410_UDC_EP0_CSR_SSE), - S3C2410_UDC_EP0_CSR_REG); -} - static inline void s3c2410_udc_set_ep0_de_in(void __iomem *base) { udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG); @@ -291,18 +283,6 @@ static void s3c2410_udc_nuke(struct s3c2410_udc *udc, } } -static inline void s3c2410_udc_clear_ep_state(struct s3c2410_udc *dev) -{ - unsigned i; - - /* hardware SET_{CONFIGURATION,INTERFACE} automagic resets endpoint - * fifos, and pending transactions mustn't be continued in any case. - */ - - for (i = 1; i < S3C2410_ENDPOINTS; i++) - s3c2410_udc_nuke(dev, &dev->ep[i], -ECONNABORTED); -} - static inline int s3c2410_udc_fifo_count_out(void) { int tmp; From 997f4f81dff243858b6cdd8c3ddb39e05a050c22 Mon Sep 17 00:00:00 2001 From: Mickael Maison Date: Tue, 23 Dec 2014 17:39:45 +0100 Subject: [PATCH 003/214] usb: dwc2: Fixed a few typos in comments Fixed 3 typos in comments Signed-off-by: Mickael Maison Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/core.c | 2 +- drivers/usb/dwc2/core.h | 2 +- drivers/usb/dwc2/gadget.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index 7605850b7a9c..d5197d492e21 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -462,7 +462,7 @@ int dwc2_core_init(struct dwc2_hsotg *hsotg, bool select_phy, int irq) dwc2_enable_common_interrupts(hsotg); /* - * Do device or host intialization based on mode during PCD and + * Do device or host initialization based on mode during PCD and * HCD initialization */ if (dwc2_is_host_mode(hsotg)) { diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 7a70a1349334..0d2ee29ac85a 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -381,7 +381,7 @@ struct dwc2_core_params { * @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 + * available * @dev_token_q_depth Device Mode IN Token Sequence Learning Queue * Depth * 0 to 30 diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 200168ec2d75..ede69dc75f0d 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -65,7 +65,7 @@ static inline void __bic32(void __iomem *ptr, u32 val) writel(readl(ptr) & ~val, ptr); } -/* forward decleration of functions */ +/* forward declaration of functions */ static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg); /** From 110381e11b5fbf84bc0994d4c3f4629bc362ab4c Mon Sep 17 00:00:00 2001 From: Amit Virdi Date: Fri, 19 Dec 2014 12:40:17 +0530 Subject: [PATCH 004/214] usb: dwc3: gadget: Remove redundant check dwc3_gadget_init_hw_endpoints calls dwc3_alloc_trb_pool only if epnum is not equal to 0 or 1. Hence, rechecking it in the called function is redundant. Signed-off-by: Amit Virdi Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index bd9f48d0dff8..4e2593993fae 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -352,9 +352,6 @@ static int dwc3_alloc_trb_pool(struct dwc3_ep *dep) if (dep->trb_pool) return 0; - if (dep->number == 0 || dep->number == 1) - return 0; - dep->trb_pool = dma_alloc_coherent(dwc->dev, sizeof(struct dwc3_trb) * DWC3_TRB_NUM, &dep->trb_pool_dma, GFP_KERNEL); From 3cd0e29d5e28331dc78416bf7acd4e7cb63664aa Mon Sep 17 00:00:00 2001 From: Amit Virdi Date: Fri, 19 Dec 2014 12:40:18 +0530 Subject: [PATCH 005/214] usb: dwc3: Remove current_trb as it is unused This field was introduced but never used. So, remove it. Signed-off-by: Amit Virdi Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 4bb9aa696ede..0842aa80976f 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -431,7 +431,6 @@ struct dwc3_event_buffer { * @dwc: pointer to DWC controller * @saved_state: ep state saved during hibernation * @flags: endpoint flags (wedged, stalled, ...) - * @current_trb: index of current used trb * @number: endpoint number (1 - 15) * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK * @resource_index: Resource transfer index @@ -464,8 +463,6 @@ struct dwc3_ep { /* This last one is specific to EP0 */ #define DWC3_EP0_DIR_IN (1 << 31) - unsigned current_trb; - u8 number; u8 type; u8 resource_index; From b797ef4e167b68dc5d6bfcb1b77f7f077dddb825 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:24 +0100 Subject: [PATCH 006/214] Documentation: usb: gadget_serial: update generic serial setup instruction Using module parameters to specify accepted Vendor ID, Product ID is considered legacy now. Update the documentation to reflect it. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget_serial.txt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Documentation/usb/gadget_serial.txt b/Documentation/usb/gadget_serial.txt index 61e67f6a20a0..6b4a88a8c8e3 100644 --- a/Documentation/usb/gadget_serial.txt +++ b/Documentation/usb/gadget_serial.txt @@ -236,8 +236,12 @@ I: If#= 0 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=serial E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms -You must explicitly load the usbserial driver with parameters to -configure it to recognize the gadget serial device, like this: +You must load the usbserial driver and explicitly set its parameters +to configure it to recognize the gadget serial device, like this: + + echo 0x0525 0xA4A6 >/sys/bus/usb-serial/drivers/generic/new_id + +The legacy way is to use module parameters: modprobe usbserial vendor=0x0525 product=0xA4A6 From e38eb2c8cb435729579576b2b5bc5247ebcd6f5b Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:25 +0100 Subject: [PATCH 007/214] Documentation: usb: ACM function testing The newly added file will be used to provide descriptions of how to test the functions of USB gadgets. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 34 ++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 Documentation/usb/gadget-testing.txt diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt new file mode 100644 index 000000000000..2a448f812f0e --- /dev/null +++ b/Documentation/usb/gadget-testing.txt @@ -0,0 +1,34 @@ +This file summarizes information on basic testing of USB functions +provided by gadgets. + +1. ACM function + + +1. ACM function +=============== + +The function is provided by usb_f_acm.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "acm". +The ACM function provides just one attribute in its function directory: + + port_num + +The attribute is read-only. + +There can be at most 4 ACM/generic serial/OBEX ports in the system. + + +Testing the ACM function +------------------------ + +On the host: cat > /dev/ttyACM +On the device : cat /dev/ttyGS + +then the other way round + +On the device: cat > /dev/ttyGS +On the host: cat /dev/ttyACM From d5862ca6da545ec63ec2d2a8c687bf214c87e00e Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:26 +0100 Subject: [PATCH 008/214] Documentation: usb: ECM function testing Summary of how to test ECM function of USB gadget. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 34 ++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index 2a448f812f0e..7df8785fc70d 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -2,6 +2,7 @@ This file summarizes information on basic testing of USB functions provided by gadgets. 1. ACM function +2. ECM function 1. ACM function @@ -32,3 +33,36 @@ then the other way round On the device: cat > /dev/ttyGS On the host: cat /dev/ttyACM + +2. ECM function +=============== + +The function is provided by usb_f_ecm.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "ecm". +The ECM function provides these attributes in its function directory: + + ifname - network device interface name associated with this + function instance + qmult - queue length multiplier for high and super speed + host_addr - MAC address of host's end of this + Ethernet over USB link + dev_addr - MAC address of device's end of this + Ethernet over USB link + +and after creating the functions/ecm. they contain default +values: qmult is 5, dev_addr and host_addr are randomly selected. +Except for ifname they can be written to until the function is linked to a +configuration. The ifname is read-only and contains the name of the interface +which was assigned by the net core, e. g. usb0. + +Testing the ECM function +------------------------ + +Configure IP addresses of the device and the host. Then: + +On the device: ping +On the host: ping From 7bfbc6e3fbd9ae35b28db1839689b7eb4025c93d Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:27 +0100 Subject: [PATCH 009/214] Documentation: usb: ECM subset function testing Summary of how to test ECM subset function of USB gadget. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 34 ++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index 7df8785fc70d..b40db750890f 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -3,6 +3,7 @@ provided by gadgets. 1. ACM function 2. ECM function +3. ECM subset function 1. ACM function @@ -66,3 +67,36 @@ Configure IP addresses of the device and the host. Then: On the device: ping On the host: ping + +3. ECM subset function +====================== + +The function is provided by usb_f_ecm_subset.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "geth". +The ECM subset function provides these attributes in its function directory: + + ifname - network device interface name associated with this + function instance + qmult - queue length multiplier for high and super speed + host_addr - MAC address of host's end of this + Ethernet over USB link + dev_addr - MAC address of device's end of this + Ethernet over USB link + +and after creating the functions/ecm. they contain default +values: qmult is 5, dev_addr and host_addr are randomly selected. +Except for ifname they can be written to until the function is linked to a +configuration. The ifname is read-only and contains the name of the interface +which was assigned by the net core, e. g. usb0. + +Testing the ECM subset function +------------------------------- + +Configure IP addresses of the device and the host. Then: + +On the device: ping +On the host: ping From 4ca560a6d31d1deae004a9977adf64766e87a021 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:28 +0100 Subject: [PATCH 010/214] Documentation: usb: EEM function testing Summary of how to test EEM function of USB gadget. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 34 ++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index b40db750890f..50b0a6cc6674 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -4,6 +4,7 @@ provided by gadgets. 1. ACM function 2. ECM function 3. ECM subset function +4. EEM function 1. ACM function @@ -100,3 +101,36 @@ Configure IP addresses of the device and the host. Then: On the device: ping On the host: ping + +4. EEM function +=============== + +The function is provided by usb_f_eem.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "eem". +The EEM function provides these attributes in its function directory: + + ifname - network device interface name associated with this + function instance + qmult - queue length multiplier for high and super speed + host_addr - MAC address of host's end of this + Ethernet over USB link + dev_addr - MAC address of device's end of this + Ethernet over USB link + +and after creating the functions/eem. they contain default +values: qmult is 5, dev_addr and host_addr are randomly selected. +Except for ifname they can be written to until the function is linked to a +configuration. The ifname is read-only and contains the name of the interface +which was assigned by the net core, e. g. usb0. + +Testing the EEM function +------------------------ + +Configure IP addresses of the device and the host. Then: + +On the device: ping +On the host: ping From 2c0f62f9e4f2c450342369dfd5858db51bc90fe3 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:29 +0100 Subject: [PATCH 011/214] Documentation: usb: FFS function testing Summary of how to test FFS (FunctionFS) function of USB gadget. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index 50b0a6cc6674..141a1d01f312 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -5,6 +5,7 @@ provided by gadgets. 2. ECM function 3. ECM subset function 4. EEM function +5. FFS function 1. ACM function @@ -134,3 +135,26 @@ Configure IP addresses of the device and the host. Then: On the device: ping On the host: ping + +5. FFS function +=============== + +The function is provided by usb_f_fs.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "ffs". +The function directory is intentionally empty and not modifiable. + +After creating the directory there is a new instance (a "device") of FunctionFS +available in the system. Once a "device" is available, the user should follow +the standard procedure for using FunctionFS (mount it, run the userspace +process which implements the function proper). The gadget should be enabled +by writing a suitable string to usb_gadget//UDC. + +Testing the FFS function +------------------------ + +On the device: start the function's userspace daemon, enable the gadget +On the host: use the USB function provided by the device From f7e3c3cd2401b3531a3ac4e279a08d03adb722e3 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:30 +0100 Subject: [PATCH 012/214] Documentation: usb: HID function testing Summary of how to test HID function of USB gadget. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 47 ++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index 141a1d01f312..f117e5c84a1e 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -6,6 +6,7 @@ provided by gadgets. 3. ECM subset function 4. EEM function 5. FFS function +6. HID function 1. ACM function @@ -158,3 +159,49 @@ Testing the FFS function On the device: start the function's userspace daemon, enable the gadget On the host: use the USB function provided by the device + +6. HID function +=============== + +The function is provided by usb_f_hid.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "hid". +The HID function provides these attributes in its function directory: + + protocol - HID protocol to use + report_desc - data to be used in HID reports, except data + passed with /dev/hidg + report_length - HID report length + subclass - HID subclass to use + +For a keyboard the protocol and the subclass are 1, the report_length is 8, +while the report_desc is: + +$ hd my_report_desc +00000000 05 01 09 06 a1 01 05 07 19 e0 29 e7 15 00 25 01 |..........)...%.| +00000010 75 01 95 08 81 02 95 01 75 08 81 03 95 05 75 01 |u.......u.....u.| +00000020 05 08 19 01 29 05 91 02 95 01 75 03 91 03 95 06 |....).....u.....| +00000030 75 08 15 00 25 65 05 07 19 00 29 65 81 00 c0 |u...%e....)e...| +0000003f + +Such a sequence of bytes can be stored to the attribute with echo: + +$ echo -ne \\x05\\x01\\x09\\x06\\xa1..... + +Testing the HID function +------------------------ + +Device: +- create the gadget +- connect the gadget to a host, preferably not the one used +to control the gadget +- run a program which writes to /dev/hidg, e.g. +a userspace program found in Documentation/usb/gadget_hid.txt: + +$ ./hid_gadget_test /dev/hidg0 keyboard + +Host: +- observe the keystrokes from the gadget From ec91aff7630df1e1864d95f5efd25c212d484837 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:31 +0100 Subject: [PATCH 013/214] Documentation: usb: LOOPBACK function testing Summary of how to test LOOPBACK function of USB gadget. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index f117e5c84a1e..b13491d1e352 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -7,6 +7,7 @@ provided by gadgets. 4. EEM function 5. FFS function 6. HID function +7. LOOPBACK function 1. ACM function @@ -205,3 +206,25 @@ $ ./hid_gadget_test /dev/hidg0 keyboard Host: - observe the keystrokes from the gadget + +7. LOOPBACK function +==================== + +The function is provided by usb_f_ss_lb.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "Loopback". +The LOOPBACK function provides these attributes in its function directory: + + qlen - depth of loopback queue + bulk_buflen - buffer length + +Testing the LOOPBACK function +----------------------------- + +device: run the gadget +host: test-usb + +http://www.linux-usb.org/usbtest/testusb.c From cdbe287d271637cb90f239d6a826e2aed555c1d7 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:32 +0100 Subject: [PATCH 014/214] Documentation: usb: MASS STORAGE function testing Summary of how to test MASS STORAGE function of USB gadget. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 54 ++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index b13491d1e352..01b9ffe2eabc 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -8,6 +8,7 @@ provided by gadgets. 5. FFS function 6. HID function 7. LOOPBACK function +8. MASS STORAGE function 1. ACM function @@ -228,3 +229,56 @@ device: run the gadget host: test-usb http://www.linux-usb.org/usbtest/testusb.c + +8. MASS STORAGE function +======================== + +The function is provided by usb_f_mass_storage.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "mass_storage". +The MASS STORAGE function provides these attributes in its directory: +files: + + stall - Set to permit function to halt bulk endpoints. + Disabled on some USB devices known not to work + correctly. You should set it to true. + num_buffers - Number of pipeline buffers. Valid numbers + are 2..4. Available only if + CONFIG_USB_GADGET_DEBUG_FILES is set. + +and a default lun.0 directory corresponding to SCSI LUN #0. + +A new lun can be added with mkdir: + +$ mkdir functions/mass_storage.0/partition.5 + +Lun numbering does not have to be continuous, except for lun #0 which is +created by default. A maximum of 8 luns can be specified and they all must be +named following the . scheme. The numbers can be 0..8. +Probably a good convention is to name the luns "lun.", +although it is not mandatory. + +In each lun directory there are the following attribute files: + + file - The path to the backing file for the LUN. + Required if LUN is not marked as removable. + ro - Flag specifying access to the LUN shall be + read-only. This is implied if CD-ROM emulation + is enabled as well as when it was impossible + to open "filename" in R/W mode. + removable - Flag specifying that LUN shall be indicated as + being removable. + cdrom - Flag specifying that LUN shall be reported as + being a CD-ROM. + nofua - Flag specifying that FUA flag + in SCSI WRITE(10,12) + +Testing the MASS STORAGE function +--------------------------------- + +device: connect the gadget, enable it +host: dmesg, see the USB drives appear (if system configured to automatically +mount) From 0d6be59a1d8a93086cd81e1c59f8d60ba35b40a2 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:33 +0100 Subject: [PATCH 015/214] Documentation: usb: MIDI function testing Summary of how to test MIDI function of USB gadget. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 84 ++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index 01b9ffe2eabc..c2f148d245a5 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -9,6 +9,7 @@ provided by gadgets. 6. HID function 7. LOOPBACK function 8. MASS STORAGE function +9. MIDI function 1. ACM function @@ -282,3 +283,86 @@ Testing the MASS STORAGE function device: connect the gadget, enable it host: dmesg, see the USB drives appear (if system configured to automatically mount) + +9. MIDI function +================ + +The function is provided by usb_f_midi.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "midi". +The MIDI function provides these attributes in its function directory: + + buflen - MIDI buffer length + id - ID string for the USB MIDI adapter + in_ports - number of MIDI input ports + index - index value for the USB MIDI adapter + out_ports - number of MIDI output ports + qlen - USB read request queue length + +Testing the MIDI function +------------------------- + +There are two cases: playing a mid from the gadget to +the host and playing a mid from the host to the gadget. + +1) Playing a mid from the gadget to the host +host) + +$ arecordmidi -l + Port Client name Port name + 14:0 Midi Through Midi Through Port-0 + 24:0 MIDI Gadget MIDI Gadget MIDI 1 +$ arecordmidi -p 24:0 from_gadget.mid + +gadget) + +$ aplaymidi -l + Port Client name Port name + 20:0 f_midi f_midi + +$ aplaymidi -p 20:0 to_host.mid + +2) Playing a mid from the host to the gadget +gadget) + +$ arecordmidi -l + Port Client name Port name + 20:0 f_midi f_midi + +$ arecordmidi -p 20:0 from_host.mid + +host) + +$ aplaymidi -l + Port Client name Port name + 14:0 Midi Through Midi Through Port-0 + 24:0 MIDI Gadget MIDI Gadget MIDI 1 + +$ aplaymidi -p24:0 to_gadget.mid + +The from_gadget.mid should sound identical to the to_host.mid. +The from_host.id should sound identical to the to_gadget.mid. + +MIDI files can be played to speakers/headphones with e.g. timidity installed + +$ aplaymidi -l + Port Client name Port name + 14:0 Midi Through Midi Through Port-0 + 24:0 MIDI Gadget MIDI Gadget MIDI 1 +128:0 TiMidity TiMidity port 0 +128:1 TiMidity TiMidity port 1 +128:2 TiMidity TiMidity port 2 +128:3 TiMidity TiMidity port 3 + +$ aplaymidi -p 128:0 file.mid + +MIDI ports can be logically connected using the aconnect utility, e.g.: + +$ aconnect 24:0 128:0 # try it on the host + +After the gadget's MIDI port is connected to timidity's MIDI port, +whatever is played at the gadget side with aplaymidi -l is audible +in host's speakers/headphones. From 4d0fa79e0ef78f516888068e240d38650eb404d2 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:34 +0100 Subject: [PATCH 016/214] Documentation: usb: NCM function testing Summary of how to test NCM function of USB gadget. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 34 ++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index c2f148d245a5..493f79b96f91 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -10,6 +10,7 @@ provided by gadgets. 7. LOOPBACK function 8. MASS STORAGE function 9. MIDI function +10. NCM function 1. ACM function @@ -366,3 +367,36 @@ $ aconnect 24:0 128:0 # try it on the host After the gadget's MIDI port is connected to timidity's MIDI port, whatever is played at the gadget side with aplaymidi -l is audible in host's speakers/headphones. + +10. NCM function +================ + +The function is provided by usb_f_ncm.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "ncm". +The NCM function provides these attributes in its function directory: + + ifname - network device interface name associated with this + function instance + qmult - queue length multiplier for high and super speed + host_addr - MAC address of host's end of this + Ethernet over USB link + dev_addr - MAC address of device's end of this + Ethernet over USB link + +and after creating the functions/ncm. they contain default +values: qmult is 5, dev_addr and host_addr are randomly selected. +Except for ifname they can be written to until the function is linked to a +configuration. The ifname is read-only and contains the name of the interface +which was assigned by the net core, e. g. usb0. + +Testing the NCM function +------------------------ + +Configure IP addresses of the device and the host. Then: + +On the device: ping +On the host: ping From d81b85dcf186f6fa4b59e5b8181e85366ebfdd61 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:35 +0100 Subject: [PATCH 017/214] Documentation: usb: OBEX function testing Summary of how to test OBEX function of USB gadget. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 29 ++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index 493f79b96f91..c8ae54022ec8 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -11,6 +11,7 @@ provided by gadgets. 8. MASS STORAGE function 9. MIDI function 10. NCM function +11. OBEX function 1. ACM function @@ -400,3 +401,31 @@ Configure IP addresses of the device and the host. Then: On the device: ping On the host: ping + +11. OBEX function +================= + +The function is provided by usb_f_obex.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "obex". +The OBEX function provides just one attribute in its function directory: + + port_num + +The attribute is read-only. + +There can be at most 4 ACM/generic serial/OBEX ports in the system. + +Testing the OBEX function +------------------------- + +On device: seriald -f /dev/ttyGS -s 1024 +On host: serialc -v -p -i -a1 -s1024 \ + -t -r + +where seriald and serialc are Felipe's utilities found here: + +https://git.gitorious.org/usb/usb-tools.git master From da2907d2b08c2495c2e39f799b9ccbf76fd1aa58 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:36 +0100 Subject: [PATCH 018/214] Documentation: usb: PHONET function testing Summary of how to test PHONET function of USB gadget. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 64 ++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index c8ae54022ec8..02d4366a847d 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -12,6 +12,7 @@ provided by gadgets. 9. MIDI function 10. NCM function 11. OBEX function +12. PHONET function 1. ACM function @@ -429,3 +430,66 @@ On host: serialc -v -p -i -a1 -s1024 \ where seriald and serialc are Felipe's utilities found here: https://git.gitorious.org/usb/usb-tools.git master + +12. PHONET function +=================== + +The function is provided by usb_f_phonet.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "phonet". +The PHONET function provides just one attribute in its function directory: + + ifname - network device interface name associated with this + function instance + +Testing the PHONET function +--------------------------- + +It is not possible to test the SOCK_STREAM protocol without a specific piece +of hardware, so only SOCK_DGRAM has been tested. For the latter to work, +in the past I had to apply the patch mentioned here: + +http://www.spinics.net/lists/linux-usb/msg85689.html + +These tools are required: + +git://git.gitorious.org/meego-cellular/phonet-utils.git + +On the host: + +$ ./phonet -a 0x10 -i usbpn0 +$ ./pnroute add 0x6c usbpn0 +$./pnroute add 0x10 usbpn0 +$ ifconfig usbpn0 up + +On the device: + +$ ./phonet -a 0x6c -i upnlink0 +$ ./pnroute add 0x10 upnlink0 +$ ifconfig upnlink0 up + +Then a test program can be used: + +http://www.spinics.net/lists/linux-usb/msg85690.html + +On the device: + +$ ./pnxmit -a 0x6c -r + +On the host: + +$ ./pnxmit -a 0x10 -s 0x6c + +As a result some data should be sent from host to device. +Then the other way round: + +On the host: + +$ ./pnxmit -a 0x10 -r + +On the device: + +$ ./pnxmit -a 0x6c -s 0x10 From ddb722449cfcfe30d3c3ed2cd404551e1c3c3670 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:37 +0100 Subject: [PATCH 019/214] Documentation: usb: RNDIS function testing Summary of how to test RNDIS function of USB gadget. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 36 ++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index 02d4366a847d..61bc5f5934e1 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -13,6 +13,7 @@ provided by gadgets. 10. NCM function 11. OBEX function 12. PHONET function +13. RNDIS function 1. ACM function @@ -493,3 +494,38 @@ $ ./pnxmit -a 0x10 -r On the device: $ ./pnxmit -a 0x6c -s 0x10 + +13. RNDIS function +================== + +The function is provided by usb_f_rndis.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "rndis". +The RNDIS function provides these attributes in its function directory: + + ifname - network device interface name associated with this + function instance + qmult - queue length multiplier for high and super speed + host_addr - MAC address of host's end of this + Ethernet over USB link + dev_addr - MAC address of device's end of this + Ethernet over USB link + +and after creating the functions/rndis. they contain default +values: qmult is 5, dev_addr and host_addr are randomly selected. +Except for ifname they can be written to until the function is linked to a +configuration. The ifname is read-only and contains the name of the interface +which was assigned by the net core, e. g. usb0. + +By default there can be only 1 RNDIS interface in the system. + +Testing the RNDIS function +-------------------------- + +Configure IP addresses of the device and the host. Then: + +On the device: ping +On the host: ping From 4dfcec8a2f9e22ac409ae512d5ff6acced8c588f Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:38 +0100 Subject: [PATCH 020/214] Documentation: usb: SERIAL function testing Summary of how to test SERIAL function of USB gadget. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 31 ++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index 61bc5f5934e1..bc57c65e76b6 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -14,6 +14,7 @@ provided by gadgets. 11. OBEX function 12. PHONET function 13. RNDIS function +14. SERIAL function 1. ACM function @@ -529,3 +530,33 @@ Configure IP addresses of the device and the host. Then: On the device: ping On the host: ping + +14. SERIAL function +=================== + +The function is provided by usb_f_gser.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "gser". +The SERIAL function provides just one attribute in its function directory: + + port_num + +The attribute is read-only. + +There can be at most 4 ACM/generic serial/OBEX ports in the system. + +Testing the SERIAL function +--------------------------- + +On host: insmod usbserial + echo VID PID >/sys/bus/usb-serial/drivers/generic/new_id +On host: cat > /dev/ttyUSB +On target: cat /dev/ttyGS + +then the other way round + +On target: cat > /dev/ttyGS +On host: cat /dev/ttyUSB From 480a912be4eb51e30aec721731b9cf476b2ee861 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:39 +0100 Subject: [PATCH 021/214] Documentation: usb: SOURCESINK function testing Summary of how to test SOURCESINK function of USB gadget. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index bc57c65e76b6..73a5b0c34174 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -15,6 +15,7 @@ provided by gadgets. 12. PHONET function 13. RNDIS function 14. SERIAL function +15. SOURCESINK function 1. ACM function @@ -560,3 +561,29 @@ then the other way round On target: cat > /dev/ttyGS On host: cat /dev/ttyUSB + +15. SOURCESINK function +======================= + +The function is provided by usb_f_ss_lb.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "SourceSink". +The SOURCESINK function provides these attributes in its function directory: + + pattern - 0 (all zeros), 1 (mod63), 2 (none) + isoc_interval - 1..16 + isoc_maxpacket - 0 - 1023 (fs), 0 - 1024 (hs/ss) + isoc_mult - 0..2 (hs/ss only) + isoc_maxburst - 0..15 (ss only) + bulk_buflen - buffer length + +Testing the SOURCESINK function +------------------------------- + +device: run the gadget +host: test-usb + +http://www.linux-usb.org/usbtest/testusb.c From dae0358694abe0bbca3b83a922698b1f24c4299a Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:40 +0100 Subject: [PATCH 022/214] Documentation: usb: UAC1 function testing Summary of how to test UAC1 function of USB gadget. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index 73a5b0c34174..40d22d82cc5a 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -16,6 +16,7 @@ provided by gadgets. 13. RNDIS function 14. SERIAL function 15. SOURCESINK function +16. UAC1 function 1. ACM function @@ -587,3 +588,29 @@ device: run the gadget host: test-usb http://www.linux-usb.org/usbtest/testusb.c + +16. UAC1 function +================= + +The function is provided by usb_f_uac1.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "uac1". +The uac1 function provides these attributes in its function directory: + + audio_buf_size - audio buffer size + fn_cap - capture pcm device file name + fn_cntl - control device file name + fn_play - playback pcm device file name + req_buf_size - ISO OUT endpoint request buffer size + req_count - ISO OUT endpoint request count + +The attributes have sane default values. + +Testing the UAC1 function +------------------------- + +device: run the gadget +host: aplay -l # should list our USB Audio Gadget From 020c6f93484370672060fabebc49aed5d1a7c58f Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:41 +0100 Subject: [PATCH 023/214] Documentation: usb: UAC2 function testing Summary of how to test UAC2 function of USB gadget. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 39 ++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index 40d22d82cc5a..5ac9dc702c0e 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -17,6 +17,7 @@ provided by gadgets. 14. SERIAL function 15. SOURCESINK function 16. UAC1 function +17. UAC2 function 1. ACM function @@ -614,3 +615,41 @@ Testing the UAC1 function device: run the gadget host: aplay -l # should list our USB Audio Gadget + +17. UAC2 function +================= + +The function is provided by usb_f_uac2.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "uac2". +The uac2 function provides these attributes in its function directory: + + chmask - capture channel mask + c_srate - capture sampling rate + c_ssize - capture sample size (bytes) + p_chmask - playback channel mask + p_srate - playback sampling rate + p_ssize - playback sample size (bytes) + +The attributes have sane default values. + +Testing the UAC2 function +------------------------- + +device: run the gadget +host: aplay -l # should list our USB Audio Gadget + +This function does not require real hardware support, it just +sends a stream of audio data to/from the host. In order to +actually hear something at the device side, a command similar +to this must be used at the device side: + +$ arecord -f dat -t wav -D hw:2,0 | aplay -D hw:0,0 & + +e.g.: + +$ arecord -f dat -t wav -D hw:CARD=UAC2Gadget,DEV=0 | \ +aplay -D default:CARD=OdroidU3 From 9c4f538bfaf696c5cb24ec58fbeba12155c56c31 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:42 +0100 Subject: [PATCH 024/214] Documentation: usb: UVC function testing Summary of how to test UVC function of USB gadget. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 73 ++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index 5ac9dc702c0e..076ac7ba7f93 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -18,6 +18,7 @@ provided by gadgets. 15. SOURCESINK function 16. UAC1 function 17. UAC2 function +18. UVC function 1. ACM function @@ -653,3 +654,75 @@ e.g.: $ arecord -f dat -t wav -D hw:CARD=UAC2Gadget,DEV=0 | \ aplay -D default:CARD=OdroidU3 + +18. UVC function +================ + +The function is provided by usb_f_uvc.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "uvc". +The uvc function provides these attributes in its function directory: + + streaming_interval - interval for polling endpoint for data transfers + streaming_maxburst - bMaxBurst for super speed companion descriptor + streaming_maxpacket - maximum packet size this endpoint is capable of + sending or receiving when this configuration is + selected + +There are also "control" and "streaming" subdirectories, each of which contain +a number of their subdirectories. There are some sane defaults provided, but +the user must provide the following: + + control header - create in control/header, link from control/class/fs + and/or control/class/ss + streaming header - create in streaming/header, link from + streaming/class/fs and/or streaming/class/hs and/or + streaming/class/ss + format description - create in streaming/mjpeg and/or + streaming/uncompressed + frame description - create in streaming/mjpeg/ and/or in + streaming/uncompressed/ + +Each frame description contains frame interval specification, and each +such specification consists of a number of lines with an inverval value +in each line. The rules stated above are best illustrated with an example: + +# mkdir functions/uvc.usb0/control/header/h +# cd functions/uvc.usb0/control/header/h +# ln -s header/h class/fs +# ln -s header/h class/ss +# mkdir -p functions/uvc.usb0/streaming/uncompressed/u/360p +# cat < functions/uvc.usb0/streaming/uncompressed/u/360p/dwFrameInterval +666666 +1000000 +5000000 +EOF +# cd $GADGET_CONFIGFS_ROOT +# mkdir functions/uvc.usb0/streaming/header/h +# cd functions/uvc.usb0/streaming/header/h +# ln -s ../../uncompressed/u +# cd ../../class/fs +# ln -s ../../header/h +# cd ../../class/hs +# ln -s ../../header/h +# cd ../../class/ss +# ln -s ../../header/h + + +Testing the UVC function +------------------------ + +device: run the gadget, modprobe vivid + +# uvc-gadget -u /dev/video -v /dev/video + +where uvc-gadget is this program: +http://git.ideasonboard.org/uvc-gadget.git + +with these patches: +http://www.spinics.net/lists/linux-usb/msg99220.html + +host: luvcview -f yuv From 39a2ac2738658fd3e56987fb8b43b604298e9ad2 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Mon, 15 Dec 2014 13:50:05 +0100 Subject: [PATCH 025/214] usb: gadget: hid: consistently use 2^n - 1 for max values A maximum value which fits in 16 bits, unsigned, is 65535. Acked-by: Geert Uytterhoeven Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_hid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c index 6e04e302dc3a..9660ade9fdaa 100644 --- a/drivers/usb/gadget/function/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c @@ -758,7 +758,7 @@ static struct f_hid_opts_attribute f_hid_opts_##name = \ F_HID_OPT(subclass, 8, 255); F_HID_OPT(protocol, 8, 255); -F_HID_OPT(report_length, 16, 65536); +F_HID_OPT(report_length, 16, 65535); static ssize_t f_hid_opts_report_desc_show(struct f_hid_opts *opts, char *page) { From 368504c00a2dfce637a55fcbf0cc0339f9ff5ca3 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 12 Dec 2014 22:53:18 +0300 Subject: [PATCH 026/214] usb: renesas_usbhs: kill dead code in usbhs_probe() usbhsc_drvcllbck_notify_hotplug() always returns 0, so it's rather pointless to store and check its result for being < 0. Signed-off-by: Sergei Shtylyov Signed-off-by: Felipe Balbi --- drivers/usb/renesas_usbhs/common.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 371478704899..a706fc4fc13b 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -632,16 +632,12 @@ static int usbhs_probe(struct platform_device *pdev) /* * manual call notify_hotplug for cold plug */ - ret = usbhsc_drvcllbck_notify_hotplug(pdev); - if (ret < 0) - goto probe_end_call_remove; + usbhsc_drvcllbck_notify_hotplug(pdev); dev_info(&pdev->dev, "probed\n"); return ret; -probe_end_call_remove: - usbhs_platform_call(priv, hardware_exit, pdev); probe_end_mod_exit: usbhs_mod_remove(priv); probe_end_fifo_exit: From 6b1b0fe0698f0e4d528a33f43d5c56f7e6e8f756 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 12 Dec 2014 13:08:26 -0200 Subject: [PATCH 027/214] Documentation: usb: phy: nop: Fix the description of 'vcc-supply' Since bd27fa44e13830d2b ("usb: phy: generic: Don't use regulator framework for RESET line") we no longer model the reset line as a regulator supply, so adapt the documentation accordingly. Acked-by: Roger Quadros Signed-off-by: Fabio Estevam Signed-off-by: Felipe Balbi --- Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt b/Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt index 1bd37faba05b..8db5b33d6fdc 100644 --- a/Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt +++ b/Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt @@ -13,7 +13,7 @@ Optional properties: - clock-frequency: the clock frequency (in Hz) that the PHY clock must be configured to. -- vcc-supply: phandle to the regulator that provides RESET to the PHY. +- vcc-supply: phandle to the regulator that provides power to the PHY. - reset-gpios: Should specify the GPIO for reset. From bbea6de1bd12dd509f774f2a3165216eeac7bc54 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Wed, 10 Dec 2014 12:34:00 +0100 Subject: [PATCH 028/214] usb: gadget: f_uvc: rename a macro to avoid conflicts When configfs is integrated, CONFIGFS_ATTR_STRUCT and CONFIGFS_ATTR_OPS macros should be used, but the latter expects that tere is a to_f_uvc_opts function accepting a config_item, whereas the macro being changed can be applied to a different type of argument. Signed-off-by: Andrzej Pietrasiewicz Acked-by: Laurent Pinchart Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uvc.c | 6 +++--- drivers/usb/gadget/function/u_uvc.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 945b3bd2ca98..5b4ab39cce44 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -605,7 +605,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) INFO(cdev, "uvc_function_bind\n"); - opts = to_f_uvc_opts(f->fi); + opts = fi_to_f_uvc_opts(f->fi); /* Sanity check the streaming endpoint module parameters. */ opts->streaming_interval = clamp(opts->streaming_interval, 1U, 16U); @@ -766,7 +766,7 @@ error: static void uvc_free_inst(struct usb_function_instance *f) { - struct f_uvc_opts *opts = to_f_uvc_opts(f); + struct f_uvc_opts *opts = fi_to_f_uvc_opts(f); kfree(opts); } @@ -818,7 +818,7 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi) return ERR_PTR(-ENOMEM); uvc->state = UVC_STATE_DISCONNECTED; - opts = to_f_uvc_opts(fi); + opts = fi_to_f_uvc_opts(fi); uvc->desc.fs_control = opts->fs_control; uvc->desc.ss_control = opts->ss_control; diff --git a/drivers/usb/gadget/function/u_uvc.h b/drivers/usb/gadget/function/u_uvc.h index 2a8dfdff0332..c0706a3d7019 100644 --- a/drivers/usb/gadget/function/u_uvc.h +++ b/drivers/usb/gadget/function/u_uvc.h @@ -18,7 +18,7 @@ #include -#define to_f_uvc_opts(f) container_of(f, struct f_uvc_opts, func_inst) +#define fi_to_f_uvc_opts(f) container_of(f, struct f_uvc_opts, func_inst) struct f_uvc_opts { struct usb_function_instance func_inst; From 6c25955ed632227d28b85db274e519b766e26ddd Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Wed, 10 Dec 2014 12:34:01 +0100 Subject: [PATCH 029/214] usb: gadget: uvc: verify descriptors presence If the caller of uvc_alloc() does not provide enough descriptors, binding the function should fail, so appropriate code is returned from uvc_copy_descriptors(). uvc_function_bind() is modified accordingly to account for possible errors from uvc_copy_descriptors(). Signed-off-by: Andrzej Pietrasiewicz Acked-by: Laurent Pinchart Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uvc.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 5b4ab39cce44..62ca0c5c7f6e 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -509,6 +509,9 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) break; } + if (!uvc_control_desc || !uvc_streaming_cls) + return ERR_PTR(-ENODEV); + /* Descriptors layout * * uvc_iad @@ -700,10 +703,27 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) /* Copy descriptors */ f->fs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_FULL); - if (gadget_is_dualspeed(cdev->gadget)) + if (IS_ERR(f->fs_descriptors)) { + ret = PTR_ERR(f->fs_descriptors); + f->fs_descriptors = NULL; + goto error; + } + if (gadget_is_dualspeed(cdev->gadget)) { f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH); - if (gadget_is_superspeed(c->cdev->gadget)) + if (IS_ERR(f->hs_descriptors)) { + ret = PTR_ERR(f->hs_descriptors); + f->hs_descriptors = NULL; + goto error; + } + } + if (gadget_is_superspeed(c->cdev->gadget)) { f->ss_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER); + if (IS_ERR(f->ss_descriptors)) { + ret = PTR_ERR(f->ss_descriptors); + f->ss_descriptors = NULL; + goto error; + } + } /* Preallocate control endpoint request. */ uvc->control_req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL); From 46919a23ee87bbc4eeb6d958471174e26836f0e1 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Wed, 10 Dec 2014 12:34:02 +0100 Subject: [PATCH 030/214] usb: gadget: uvc: configfs support in uvc function Add support for using the uvc function as a component of USB gadgets composed with configfs. Acked-by: Laurent Pinchart Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- .../ABI/testing/configfs-usb-gadget-uvc | 265 ++ drivers/usb/gadget/Kconfig | 11 + drivers/usb/gadget/function/Makefile | 2 +- drivers/usb/gadget/function/f_uvc.c | 104 +- drivers/usb/gadget/function/u_uvc.h | 50 + drivers/usb/gadget/function/uvc_configfs.c | 2439 +++++++++++++++++ drivers/usb/gadget/function/uvc_configfs.h | 22 + 7 files changed, 2890 insertions(+), 3 deletions(-) create mode 100644 Documentation/ABI/testing/configfs-usb-gadget-uvc create mode 100644 drivers/usb/gadget/function/uvc_configfs.c create mode 100644 drivers/usb/gadget/function/uvc_configfs.h diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uvc b/Documentation/ABI/testing/configfs-usb-gadget-uvc new file mode 100644 index 000000000000..2f4a0051b32d --- /dev/null +++ b/Documentation/ABI/testing/configfs-usb-gadget-uvc @@ -0,0 +1,265 @@ +What: /config/usb-gadget/gadget/functions/uvc.name +Date: Dec 2014 +KernelVersion: 3.20 +Description: UVC function directory + + streaming_maxburst - 0..15 (ss only) + streaming_maxpacket - 1..1023 (fs), 1..3072 (hs/ss) + streaming_interval - 1..16 + +What: /config/usb-gadget/gadget/functions/uvc.name/control +Date: Dec 2014 +KernelVersion: 3.20 +Description: Control descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/control/class +Date: Dec 2014 +KernelVersion: 3.20 +Description: Class descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/control/class/ss +Date: Dec 2014 +KernelVersion: 3.20 +Description: Super speed control class descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/control/class/fs +Date: Dec 2014 +KernelVersion: 3.20 +Description: Full speed control class descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/control/terminal +Date: Dec 2014 +KernelVersion: 3.20 +Description: Terminal descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/control/terminal/output +Date: Dec 2014 +KernelVersion: 3.20 +Description: Output terminal descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/control/terminal/output/default +Date: Dec 2014 +KernelVersion: 3.20 +Description: Default output terminal descriptors + + All attributes read only: + iTerminal - index of string descriptor + bSourceID - id of the terminal to which this terminal + is connected + bAssocTerminal - id of the input terminal to which this output + terminal is associated + wTerminalType - terminal type + bTerminalID - a non-zero id of this terminal + +What: /config/usb-gadget/gadget/functions/uvc.name/control/terminal/camera +Date: Dec 2014 +KernelVersion: 3.20 +Description: Camera terminal descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/control/terminal/camera/default +Date: Dec 2014 +KernelVersion: 3.20 +Description: Default camera terminal descriptors + + All attributes read only: + bmControls - bitmap specifying which controls are + supported for the video stream + wOcularFocalLength - the value of Locular + wObjectiveFocalLengthMax- the value of Lmin + wObjectiveFocalLengthMin- the value of Lmax + iTerminal - index of string descriptor + bAssocTerminal - id of the output terminal to which + this terminal is connected + wTerminalType - terminal type + bTerminalID - a non-zero id of this terminal + +What: /config/usb-gadget/gadget/functions/uvc.name/control/processing +Date: Dec 2014 +KernelVersion: 3.20 +Description: Processing unit descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/control/processing/default +Date: Dec 2014 +KernelVersion: 3.20 +Description: Default processing unit descriptors + + All attributes read only: + iProcessing - index of string descriptor + bmControls - bitmap specifying which controls are + supported for the video stream + wMaxMultiplier - maximum digital magnification x100 + bSourceID - id of the terminal to which this unit is + connected + bUnitID - a non-zero id of this unit + +What: /config/usb-gadget/gadget/functions/uvc.name/control/header +Date: Dec 2014 +KernelVersion: 3.20 +Description: Control header descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/control/header/name +Date: Dec 2014 +KernelVersion: 3.20 +Description: Specific control header descriptors + +dwClockFrequency +bcdUVC +What: /config/usb-gadget/gadget/functions/uvc.name/streaming +Date: Dec 2014 +KernelVersion: 3.20 +Description: Streaming descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/class +Date: Dec 2014 +KernelVersion: 3.20 +Description: Streaming class descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/class/ss +Date: Dec 2014 +KernelVersion: 3.20 +Description: Super speed streaming class descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/class/hs +Date: Dec 2014 +KernelVersion: 3.20 +Description: High speed streaming class descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/class/fs +Date: Dec 2014 +KernelVersion: 3.20 +Description: Full speed streaming class descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/color_matching +Date: Dec 2014 +KernelVersion: 3.20 +Description: Color matching descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/color_matching/default +Date: Dec 2014 +KernelVersion: 3.20 +Description: Default color matching descriptors + + All attributes read only: + bMatrixCoefficients - matrix used to compute luma and + chroma values from the color primaries + bTransferCharacteristics- optoelectronic transfer + characteristic of the source picutre, + also called the gamma function + bColorPrimaries - color primaries and the reference + white + +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/mjpeg +Date: Dec 2014 +KernelVersion: 3.20 +Description: MJPEG format descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/mjpeg/name +Date: Dec 2014 +KernelVersion: 3.20 +Description: Specific MJPEG format descriptors + + All attributes read only, + except bmaControls and bDefaultFrameIndex: + bmaControls - this format's data for bmaControls in + the streaming header + bmInterfaceFlags - specifies interlace information, + read-only + bAspectRatioY - the X dimension of the picture aspect + ratio, read-only + bAspectRatioX - the Y dimension of the picture aspect + ratio, read-only + bmFlags - characteristics of this format, + read-only + bDefaultFrameIndex - optimum frame index for this stream + +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/mjpeg/name/name +Date: Dec 2014 +KernelVersion: 3.20 +Description: Specific MJPEG frame descriptors + + dwFrameInterval - indicates how frame interval can be + programmed; a number of values + separated by newline can be specified + dwDefaultFrameInterval - the frame interval the device would + like to use as default + dwMaxVideoFrameBufferSize- the maximum number of bytes the + compressor will produce for a video + frame or still image + dwMaxBitRate - the maximum bit rate at the shortest + frame interval in bps + dwMinBitRate - the minimum bit rate at the longest + frame interval in bps + wHeight - height of decoded bitmap frame in px + wWidth - width of decoded bitmam frame in px + bmCapabilities - still image support, fixed frame-rate + support + +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/uncompressed +Date: Dec 2014 +KernelVersion: 3.20 +Description: Uncompressed format descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/uncompressed/name +Date: Dec 2014 +KernelVersion: 3.20 +Description: Specific uncompressed format descriptors + + bmaControls - this format's data for bmaControls in + the streaming header + bmInterfaceFlags - specifies interlace information, + read-only + bAspectRatioY - the X dimension of the picture aspect + ratio, read-only + bAspectRatioX - the Y dimension of the picture aspect + ratio, read-only + bDefaultFrameIndex - optimum frame index for this stream + bBitsPerPixel - number of bits per pixel used to + specify color in the decoded video + frame + guidFormat - globally unique id used to identify + stream-encoding format + +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/uncompressed/name/name +Date: Dec 2014 +KernelVersion: 3.20 +Description: Specific uncompressed frame descriptors + + dwFrameInterval - indicates how frame interval can be + programmed; a number of values + separated by newline can be specified + dwDefaultFrameInterval - the frame interval the device would + like to use as default + dwMaxVideoFrameBufferSize- the maximum number of bytes the + compressor will produce for a video + frame or still image + dwMaxBitRate - the maximum bit rate at the shortest + frame interval in bps + dwMinBitRate - the minimum bit rate at the longest + frame interval in bps + wHeight - height of decoded bitmap frame in px + wWidth - width of decoded bitmam frame in px + bmCapabilities - still image support, fixed frame-rate + support + +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/header +Date: Dec 2014 +KernelVersion: 3.20 +Description: Streaming header descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/header/name +Date: Dec 2014 +KernelVersion: 3.20 +Description: Specific streaming header descriptors + + All attributes read only: + bTriggerUsage - how the host software will respond to + a hardware trigger interrupt event + bTriggerSupport - flag specifying if hardware + triggering is supported + bStillCaptureMethod - method of still image caputre + supported + bTerminalLink - id of the output terminal to which + the video endpoint of this interface + is connected + bmInfo - capabilities of this video streaming + interface diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 747ef53bda14..65f7f1265522 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -423,6 +423,17 @@ config USB_CONFIGFS_F_HID For more information, see Documentation/usb/gadget_hid.txt. +config USB_CONFIGFS_F_UVC + boolean "USB Webcam function" + depends on USB_CONFIGFS + depends on VIDEO_DEV + select VIDEOBUF2_VMALLOC + select USB_F_UVC + help + The Webcam function acts as a composite USB Audio and Video Class + device. It provides a userspace API to process UVC control requests + and stream video data to the host. + source "drivers/usb/gadget/legacy/Kconfig" endchoice diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index dd68091d92f0..f71b1aaa0edf 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -36,7 +36,7 @@ usb_f_uac1-y := f_uac1.o u_uac1.o obj-$(CONFIG_USB_F_UAC1) += usb_f_uac1.o usb_f_uac2-y := f_uac2.o obj-$(CONFIG_USB_F_UAC2) += usb_f_uac2.o -usb_f_uvc-y := f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o +usb_f_uvc-y := f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_configfs.o obj-$(CONFIG_USB_F_UVC) += usb_f_uvc.o usb_f_midi-y := f_midi.o obj-$(CONFIG_USB_F_MIDI) += usb_f_midi.o diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 62ca0c5c7f6e..76891adfba7a 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -27,10 +27,11 @@ #include #include +#include "u_uvc.h" #include "uvc.h" +#include "uvc_configfs.h" #include "uvc_v4l2.h" #include "uvc_video.h" -#include "u_uvc.h" unsigned int uvc_gadget_trace_param; @@ -788,25 +789,104 @@ static void uvc_free_inst(struct usb_function_instance *f) { struct f_uvc_opts *opts = fi_to_f_uvc_opts(f); + mutex_destroy(&opts->lock); kfree(opts); } static struct usb_function_instance *uvc_alloc_inst(void) { struct f_uvc_opts *opts; + struct uvc_camera_terminal_descriptor *cd; + struct uvc_processing_unit_descriptor *pd; + struct uvc_output_terminal_descriptor *od; + struct uvc_color_matching_descriptor *md; + struct uvc_descriptor_header **ctl_cls; opts = kzalloc(sizeof(*opts), GFP_KERNEL); if (!opts) return ERR_PTR(-ENOMEM); opts->func_inst.free_func_inst = uvc_free_inst; + mutex_init(&opts->lock); + cd = &opts->uvc_camera_terminal; + cd->bLength = UVC_DT_CAMERA_TERMINAL_SIZE(3); + cd->bDescriptorType = USB_DT_CS_INTERFACE; + cd->bDescriptorSubType = UVC_VC_INPUT_TERMINAL; + cd->bTerminalID = 1; + cd->wTerminalType = cpu_to_le16(0x0201); + cd->bAssocTerminal = 0; + cd->iTerminal = 0; + cd->wObjectiveFocalLengthMin = cpu_to_le16(0); + cd->wObjectiveFocalLengthMax = cpu_to_le16(0); + cd->wOcularFocalLength = cpu_to_le16(0); + cd->bControlSize = 3; + cd->bmControls[0] = 2; + cd->bmControls[1] = 0; + cd->bmControls[2] = 0; + + pd = &opts->uvc_processing; + pd->bLength = UVC_DT_PROCESSING_UNIT_SIZE(2); + pd->bDescriptorType = USB_DT_CS_INTERFACE; + pd->bDescriptorSubType = UVC_VC_PROCESSING_UNIT; + pd->bUnitID = 2; + pd->bSourceID = 1; + pd->wMaxMultiplier = cpu_to_le16(16*1024); + pd->bControlSize = 2; + pd->bmControls[0] = 1; + pd->bmControls[1] = 0; + pd->iProcessing = 0; + + od = &opts->uvc_output_terminal; + od->bLength = UVC_DT_OUTPUT_TERMINAL_SIZE; + od->bDescriptorType = USB_DT_CS_INTERFACE; + od->bDescriptorSubType = UVC_VC_OUTPUT_TERMINAL; + od->bTerminalID = 3; + od->wTerminalType = cpu_to_le16(0x0101); + od->bAssocTerminal = 0; + od->bSourceID = 2; + od->iTerminal = 0; + + md = &opts->uvc_color_matching; + md->bLength = UVC_DT_COLOR_MATCHING_SIZE; + md->bDescriptorType = USB_DT_CS_INTERFACE; + md->bDescriptorSubType = UVC_VS_COLORFORMAT; + md->bColorPrimaries = 1; + md->bTransferCharacteristics = 1; + md->bMatrixCoefficients = 4; + + /* Prepare fs control class descriptors for configfs-based gadgets */ + ctl_cls = opts->uvc_fs_control_cls; + ctl_cls[0] = NULL; /* assigned elsewhere by configfs */ + ctl_cls[1] = (struct uvc_descriptor_header *)cd; + ctl_cls[2] = (struct uvc_descriptor_header *)pd; + ctl_cls[3] = (struct uvc_descriptor_header *)od; + ctl_cls[4] = NULL; /* NULL-terminate */ + opts->fs_control = + (const struct uvc_descriptor_header * const *)ctl_cls; + + /* Prepare hs control class descriptors for configfs-based gadgets */ + ctl_cls = opts->uvc_ss_control_cls; + ctl_cls[0] = NULL; /* assigned elsewhere by configfs */ + ctl_cls[1] = (struct uvc_descriptor_header *)cd; + ctl_cls[2] = (struct uvc_descriptor_header *)pd; + ctl_cls[3] = (struct uvc_descriptor_header *)od; + ctl_cls[4] = NULL; /* NULL-terminate */ + opts->ss_control = + (const struct uvc_descriptor_header * const *)ctl_cls; + + opts->streaming_interval = 1; + opts->streaming_maxpacket = 1024; + + uvcg_attach_configfs(opts); return &opts->func_inst; } static void uvc_free(struct usb_function *f) { struct uvc_device *uvc = to_uvc(f); - + struct f_uvc_opts *opts = container_of(f->fi, struct f_uvc_opts, + func_inst); + --opts->refcnt; kfree(uvc); } @@ -832,6 +912,7 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi) { struct uvc_device *uvc; struct f_uvc_opts *opts; + struct uvc_descriptor_header **strm_cls; uvc = kzalloc(sizeof(*uvc), GFP_KERNEL); if (uvc == NULL) @@ -840,11 +921,30 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi) uvc->state = UVC_STATE_DISCONNECTED; opts = fi_to_f_uvc_opts(fi); + mutex_lock(&opts->lock); + if (opts->uvc_fs_streaming_cls) { + strm_cls = opts->uvc_fs_streaming_cls; + opts->fs_streaming = + (const struct uvc_descriptor_header * const *)strm_cls; + } + if (opts->uvc_hs_streaming_cls) { + strm_cls = opts->uvc_hs_streaming_cls; + opts->hs_streaming = + (const struct uvc_descriptor_header * const *)strm_cls; + } + if (opts->uvc_ss_streaming_cls) { + strm_cls = opts->uvc_ss_streaming_cls; + opts->ss_streaming = + (const struct uvc_descriptor_header * const *)strm_cls; + } + uvc->desc.fs_control = opts->fs_control; uvc->desc.ss_control = opts->ss_control; uvc->desc.fs_streaming = opts->fs_streaming; uvc->desc.hs_streaming = opts->hs_streaming; uvc->desc.ss_streaming = opts->ss_streaming; + ++opts->refcnt; + mutex_unlock(&opts->lock); /* Register the function. */ uvc->func.name = "uvc"; diff --git a/drivers/usb/gadget/function/u_uvc.h b/drivers/usb/gadget/function/u_uvc.h index c0706a3d7019..4676b60a5063 100644 --- a/drivers/usb/gadget/function/u_uvc.h +++ b/drivers/usb/gadget/function/u_uvc.h @@ -17,6 +17,7 @@ #define U_UVC_H #include +#include #define fi_to_f_uvc_opts(f) container_of(f, struct f_uvc_opts, func_inst) @@ -26,11 +27,60 @@ struct f_uvc_opts { unsigned int streaming_interval; unsigned int streaming_maxpacket; unsigned int streaming_maxburst; + + /* + * Control descriptors array pointers for full-/high-speed and + * super-speed. They point by default to the uvc_fs_control_cls and + * uvc_ss_control_cls arrays respectively. Legacy gadgets must + * override them in their gadget bind callback. + */ const struct uvc_descriptor_header * const *fs_control; const struct uvc_descriptor_header * const *ss_control; + + /* + * Streaming descriptors array pointers for full-speed, high-speed and + * super-speed. They will point to the uvc_[fhs]s_streaming_cls arrays + * for configfs-based gadgets. Legacy gadgets must initialize them in + * their gadget bind callback. + */ const struct uvc_descriptor_header * const *fs_streaming; const struct uvc_descriptor_header * const *hs_streaming; const struct uvc_descriptor_header * const *ss_streaming; + + /* Default control descriptors for configfs-based gadgets. */ + struct uvc_camera_terminal_descriptor uvc_camera_terminal; + struct uvc_processing_unit_descriptor uvc_processing; + struct uvc_output_terminal_descriptor uvc_output_terminal; + struct uvc_color_matching_descriptor uvc_color_matching; + + /* + * Control descriptors pointers arrays for full-/high-speed and + * super-speed. The first element is a configurable control header + * descriptor, the other elements point to the fixed default control + * descriptors. Used by configfs only, must not be touched by legacy + * gadgets. + */ + struct uvc_descriptor_header *uvc_fs_control_cls[5]; + struct uvc_descriptor_header *uvc_ss_control_cls[5]; + + /* + * Streaming descriptors for full-speed, high-speed and super-speed. + * Used by configfs only, must not be touched by legacy gadgets. The + * arrays are allocated at runtime as the number of descriptors isn't + * known in advance. + */ + struct uvc_descriptor_header **uvc_fs_streaming_cls; + struct uvc_descriptor_header **uvc_hs_streaming_cls; + struct uvc_descriptor_header **uvc_ss_streaming_cls; + + /* + * Read/write access to configfs attributes is handled by configfs. + * + * This lock protects the descriptors from concurrent access by + * read/write and symlink creation/removal. + */ + struct mutex lock; + int refcnt; }; void uvc_set_trace_param(unsigned int trace); diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c new file mode 100644 index 000000000000..33d92ab3c031 --- /dev/null +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -0,0 +1,2439 @@ +/* + * uvc_configfs.c + * + * Configfs support for the uvc function. + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include "u_uvc.h" +#include "uvc_configfs.h" + +#define UVCG_STREAMING_CONTROL_SIZE 1 + +#define CONFIGFS_ATTR_OPS_RO(_item) \ +static ssize_t _item##_attr_show(struct config_item *item, \ + struct configfs_attribute *attr, \ + char *page) \ +{ \ + struct _item *_item = to_##_item(item); \ + struct _item##_attribute *_item##_attr = \ + container_of(attr, struct _item##_attribute, attr); \ + ssize_t ret = 0; \ + \ + if (_item##_attr->show) \ + ret = _item##_attr->show(_item, page); \ + return ret; \ +} + +static inline struct f_uvc_opts *to_f_uvc_opts(struct config_item *item); + +/* control/header/ */ +DECLARE_UVC_HEADER_DESCRIPTOR(1); + +struct uvcg_control_header { + struct config_item item; + struct UVC_HEADER_DESCRIPTOR(1) desc; + unsigned linked; +}; + +struct uvcg_control_header *to_uvcg_control_header(struct config_item *item) +{ + return container_of(item, struct uvcg_control_header, item); +} + +CONFIGFS_ATTR_STRUCT(uvcg_control_header); +CONFIGFS_ATTR_OPS(uvcg_control_header); + +static struct configfs_item_operations uvcg_control_header_item_ops = { + .show_attribute = uvcg_control_header_attr_show, + .store_attribute = uvcg_control_header_attr_store, +}; + +#define UVCG_CTRL_HDR_ATTR(cname, aname, conv, str2u, uxx, vnoc, limit) \ +static ssize_t uvcg_control_header_##cname##_show( \ + struct uvcg_control_header *ch, char *page) \ +{ \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &ch->item.ci_group->cg_subsys->su_mutex;\ + int result; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = ch->item.ci_parent->ci_parent->ci_parent; \ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%d\n", conv(ch->desc.aname)); \ + mutex_unlock(&opts->lock); \ + \ + mutex_unlock(su_mutex); \ + return result; \ +} \ + \ +static ssize_t \ +uvcg_control_header_##cname##_store(struct uvcg_control_header *ch, \ + const char *page, size_t len) \ +{ \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &ch->item.ci_group->cg_subsys->su_mutex;\ + int ret; \ + uxx num; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = ch->item.ci_parent->ci_parent->ci_parent; \ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + if (ch->linked || opts->refcnt) { \ + ret = -EBUSY; \ + goto end; \ + } \ + \ + ret = str2u(page, 0, &num); \ + if (ret) \ + goto end; \ + \ + if (num > limit) { \ + ret = -EINVAL; \ + goto end; \ + } \ + ch->desc.aname = vnoc(num); \ + ret = len; \ +end: \ + mutex_unlock(&opts->lock); \ + mutex_unlock(su_mutex); \ + return ret; \ +} \ + \ +static struct uvcg_control_header_attribute \ + uvcg_control_header_##cname = \ + __CONFIGFS_ATTR(aname, S_IRUGO | S_IWUSR, \ + uvcg_control_header_##cname##_show, \ + uvcg_control_header_##cname##_store) + +UVCG_CTRL_HDR_ATTR(bcd_uvc, bcdUVC, le16_to_cpu, kstrtou16, u16, cpu_to_le16, + 0xffff); + +UVCG_CTRL_HDR_ATTR(dw_clock_frequency, dwClockFrequency, le32_to_cpu, kstrtou32, + u32, cpu_to_le32, 0x7fffffff); + +#undef UVCG_CTRL_HDR_ATTR + +static struct configfs_attribute *uvcg_control_header_attrs[] = { + &uvcg_control_header_bcd_uvc.attr, + &uvcg_control_header_dw_clock_frequency.attr, + NULL, +}; + +struct config_item_type uvcg_control_header_type = { + .ct_item_ops = &uvcg_control_header_item_ops, + .ct_attrs = uvcg_control_header_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_item *uvcg_control_header_make(struct config_group *group, + const char *name) +{ + struct uvcg_control_header *h; + + h = kzalloc(sizeof(*h), GFP_KERNEL); + if (!h) + return ERR_CAST(h); + + h->desc.bLength = UVC_DT_HEADER_SIZE(1); + h->desc.bDescriptorType = USB_DT_CS_INTERFACE; + h->desc.bDescriptorSubType = UVC_VC_HEADER; + h->desc.bcdUVC = cpu_to_le16(0x0100); + h->desc.dwClockFrequency = cpu_to_le32(48000000); + + config_item_init_type_name(&h->item, name, &uvcg_control_header_type); + + return &h->item; +} + +void uvcg_control_header_drop(struct config_group *group, + struct config_item *item) +{ + struct uvcg_control_header *h = to_uvcg_control_header(item); + + kfree(h); +} + +/* control/header */ +static struct uvcg_control_header_grp { + struct config_group group; +} uvcg_control_header_grp; + +static struct configfs_group_operations uvcg_control_header_grp_ops = { + .make_item = uvcg_control_header_make, + .drop_item = uvcg_control_header_drop, +}; + +static struct config_item_type uvcg_control_header_grp_type = { + .ct_group_ops = &uvcg_control_header_grp_ops, + .ct_owner = THIS_MODULE, +}; + +/* control/processing/default */ +static struct uvcg_default_processing { + struct config_group group; +} uvcg_default_processing; + +static inline struct uvcg_default_processing +*to_uvcg_default_processing(struct config_item *item) +{ + return container_of(to_config_group(item), + struct uvcg_default_processing, group); +} + +CONFIGFS_ATTR_STRUCT(uvcg_default_processing); +CONFIGFS_ATTR_OPS_RO(uvcg_default_processing); + +static struct configfs_item_operations uvcg_default_processing_item_ops = { + .show_attribute = uvcg_default_processing_attr_show, +}; + +#define UVCG_DEFAULT_PROCESSING_ATTR(cname, aname, conv) \ +static ssize_t uvcg_default_processing_##cname##_show( \ + struct uvcg_default_processing *dp, char *page) \ +{ \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &dp->group.cg_subsys->su_mutex; \ + struct uvc_processing_unit_descriptor *pd; \ + int result; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = dp->group.cg_item.ci_parent->ci_parent->ci_parent; \ + opts = to_f_uvc_opts(opts_item); \ + pd = &opts->uvc_processing; \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%d\n", conv(pd->aname)); \ + mutex_unlock(&opts->lock); \ + \ + mutex_unlock(su_mutex); \ + return result; \ +} \ + \ +static struct uvcg_default_processing_attribute \ + uvcg_default_processing_##cname = \ + __CONFIGFS_ATTR_RO(aname, uvcg_default_processing_##cname##_show) + +#define identity_conv(x) (x) + +UVCG_DEFAULT_PROCESSING_ATTR(b_unit_id, bUnitID, identity_conv); +UVCG_DEFAULT_PROCESSING_ATTR(b_source_id, bSourceID, identity_conv); +UVCG_DEFAULT_PROCESSING_ATTR(w_max_multiplier, wMaxMultiplier, le16_to_cpu); +UVCG_DEFAULT_PROCESSING_ATTR(i_processing, iProcessing, identity_conv); + +#undef identity_conv + +#undef UVCG_DEFAULT_PROCESSING_ATTR + +static ssize_t uvcg_default_processing_bm_controls_show( + struct uvcg_default_processing *dp, char *page) +{ + struct f_uvc_opts *opts; + struct config_item *opts_item; + struct mutex *su_mutex = &dp->group.cg_subsys->su_mutex; + struct uvc_processing_unit_descriptor *pd; + int result, i; + char *pg = page; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = dp->group.cg_item.ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + pd = &opts->uvc_processing; + + mutex_lock(&opts->lock); + for (result = 0, i = 0; i < pd->bControlSize; ++i) { + result += sprintf(pg, "%d\n", pd->bmControls[i]); + pg = page + result; + } + mutex_unlock(&opts->lock); + + mutex_unlock(su_mutex); + + return result; +} + +static struct uvcg_default_processing_attribute + uvcg_default_processing_bm_controls = + __CONFIGFS_ATTR_RO(bmControls, + uvcg_default_processing_bm_controls_show); + +static struct configfs_attribute *uvcg_default_processing_attrs[] = { + &uvcg_default_processing_b_unit_id.attr, + &uvcg_default_processing_b_source_id.attr, + &uvcg_default_processing_w_max_multiplier.attr, + &uvcg_default_processing_bm_controls.attr, + &uvcg_default_processing_i_processing.attr, + NULL, +}; + +static struct config_item_type uvcg_default_processing_type = { + .ct_item_ops = &uvcg_default_processing_item_ops, + .ct_attrs = uvcg_default_processing_attrs, + .ct_owner = THIS_MODULE, +}; + +/* struct uvcg_processing {}; */ + +static struct config_group *uvcg_processing_default_groups[] = { + &uvcg_default_processing.group, + NULL, +}; + +/* control/processing */ +static struct uvcg_processing_grp { + struct config_group group; +} uvcg_processing_grp; + +static struct config_item_type uvcg_processing_grp_type = { + .ct_owner = THIS_MODULE, +}; + +/* control/terminal/camera/default */ +static struct uvcg_default_camera { + struct config_group group; +} uvcg_default_camera; + +static inline struct uvcg_default_camera +*to_uvcg_default_camera(struct config_item *item) +{ + return container_of(to_config_group(item), + struct uvcg_default_camera, group); +} + +CONFIGFS_ATTR_STRUCT(uvcg_default_camera); +CONFIGFS_ATTR_OPS_RO(uvcg_default_camera); + +static struct configfs_item_operations uvcg_default_camera_item_ops = { + .show_attribute = uvcg_default_camera_attr_show, +}; + +#define UVCG_DEFAULT_CAMERA_ATTR(cname, aname, conv) \ +static ssize_t uvcg_default_camera_##cname##_show( \ + struct uvcg_default_camera *dc, char *page) \ +{ \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &dc->group.cg_subsys->su_mutex; \ + struct uvc_camera_terminal_descriptor *cd; \ + int result; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = dc->group.cg_item.ci_parent->ci_parent->ci_parent-> \ + ci_parent; \ + opts = to_f_uvc_opts(opts_item); \ + cd = &opts->uvc_camera_terminal; \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%d\n", conv(cd->aname)); \ + mutex_unlock(&opts->lock); \ + \ + mutex_unlock(su_mutex); \ + \ + return result; \ +} \ + \ +static struct uvcg_default_camera_attribute \ + uvcg_default_camera_##cname = \ + __CONFIGFS_ATTR_RO(aname, uvcg_default_camera_##cname##_show) + +#define identity_conv(x) (x) + +UVCG_DEFAULT_CAMERA_ATTR(b_terminal_id, bTerminalID, identity_conv); +UVCG_DEFAULT_CAMERA_ATTR(w_terminal_type, wTerminalType, le16_to_cpu); +UVCG_DEFAULT_CAMERA_ATTR(b_assoc_terminal, bAssocTerminal, identity_conv); +UVCG_DEFAULT_CAMERA_ATTR(i_terminal, iTerminal, identity_conv); +UVCG_DEFAULT_CAMERA_ATTR(w_objective_focal_length_min, wObjectiveFocalLengthMin, + le16_to_cpu); +UVCG_DEFAULT_CAMERA_ATTR(w_objective_focal_length_max, wObjectiveFocalLengthMax, + le16_to_cpu); +UVCG_DEFAULT_CAMERA_ATTR(w_ocular_focal_length, wOcularFocalLength, + le16_to_cpu); + +#undef identity_conv + +#undef UVCG_DEFAULT_CAMERA_ATTR + +static ssize_t uvcg_default_camera_bm_controls_show( + struct uvcg_default_camera *dc, char *page) +{ + struct f_uvc_opts *opts; + struct config_item *opts_item; + struct mutex *su_mutex = &dc->group.cg_subsys->su_mutex; + struct uvc_camera_terminal_descriptor *cd; + int result, i; + char *pg = page; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = dc->group.cg_item.ci_parent->ci_parent->ci_parent-> + ci_parent; + opts = to_f_uvc_opts(opts_item); + cd = &opts->uvc_camera_terminal; + + mutex_lock(&opts->lock); + for (result = 0, i = 0; i < cd->bControlSize; ++i) { + result += sprintf(pg, "%d\n", cd->bmControls[i]); + pg = page + result; + } + mutex_unlock(&opts->lock); + + mutex_unlock(su_mutex); + return result; +} + +static struct uvcg_default_camera_attribute + uvcg_default_camera_bm_controls = + __CONFIGFS_ATTR_RO(bmControls, uvcg_default_camera_bm_controls_show); + +static struct configfs_attribute *uvcg_default_camera_attrs[] = { + &uvcg_default_camera_b_terminal_id.attr, + &uvcg_default_camera_w_terminal_type.attr, + &uvcg_default_camera_b_assoc_terminal.attr, + &uvcg_default_camera_i_terminal.attr, + &uvcg_default_camera_w_objective_focal_length_min.attr, + &uvcg_default_camera_w_objective_focal_length_max.attr, + &uvcg_default_camera_w_ocular_focal_length.attr, + &uvcg_default_camera_bm_controls.attr, + NULL, +}; + +static struct config_item_type uvcg_default_camera_type = { + .ct_item_ops = &uvcg_default_camera_item_ops, + .ct_attrs = uvcg_default_camera_attrs, + .ct_owner = THIS_MODULE, +}; + +/* struct uvcg_camera {}; */ + +static struct config_group *uvcg_camera_default_groups[] = { + &uvcg_default_camera.group, + NULL, +}; + +/* control/terminal/camera */ +static struct uvcg_camera_grp { + struct config_group group; +} uvcg_camera_grp; + +static struct config_item_type uvcg_camera_grp_type = { + .ct_owner = THIS_MODULE, +}; + +/* control/terminal/output/default */ +static struct uvcg_default_output { + struct config_group group; +} uvcg_default_output; + +static inline struct uvcg_default_output +*to_uvcg_default_output(struct config_item *item) +{ + return container_of(to_config_group(item), + struct uvcg_default_output, group); +} + +CONFIGFS_ATTR_STRUCT(uvcg_default_output); +CONFIGFS_ATTR_OPS_RO(uvcg_default_output); + +static struct configfs_item_operations uvcg_default_output_item_ops = { + .show_attribute = uvcg_default_output_attr_show, +}; + +#define UVCG_DEFAULT_OUTPUT_ATTR(cname, aname, conv) \ +static ssize_t uvcg_default_output_##cname##_show( \ + struct uvcg_default_output *dout, char *page) \ +{ \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &dout->group.cg_subsys->su_mutex; \ + struct uvc_output_terminal_descriptor *cd; \ + int result; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = dout->group.cg_item.ci_parent->ci_parent-> \ + ci_parent->ci_parent; \ + opts = to_f_uvc_opts(opts_item); \ + cd = &opts->uvc_output_terminal; \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%d\n", conv(cd->aname)); \ + mutex_unlock(&opts->lock); \ + \ + mutex_unlock(su_mutex); \ + \ + return result; \ +} \ + \ +static struct uvcg_default_output_attribute \ + uvcg_default_output_##cname = \ + __CONFIGFS_ATTR_RO(aname, uvcg_default_output_##cname##_show) + +#define identity_conv(x) (x) + +UVCG_DEFAULT_OUTPUT_ATTR(b_terminal_id, bTerminalID, identity_conv); +UVCG_DEFAULT_OUTPUT_ATTR(w_terminal_type, wTerminalType, le16_to_cpu); +UVCG_DEFAULT_OUTPUT_ATTR(b_assoc_terminal, bAssocTerminal, identity_conv); +UVCG_DEFAULT_OUTPUT_ATTR(b_source_id, bSourceID, identity_conv); +UVCG_DEFAULT_OUTPUT_ATTR(i_terminal, iTerminal, identity_conv); + +#undef identity_conv + +#undef UVCG_DEFAULT_OUTPUT_ATTR + +static struct configfs_attribute *uvcg_default_output_attrs[] = { + &uvcg_default_output_b_terminal_id.attr, + &uvcg_default_output_w_terminal_type.attr, + &uvcg_default_output_b_assoc_terminal.attr, + &uvcg_default_output_b_source_id.attr, + &uvcg_default_output_i_terminal.attr, + NULL, +}; + +static struct config_item_type uvcg_default_output_type = { + .ct_item_ops = &uvcg_default_output_item_ops, + .ct_attrs = uvcg_default_output_attrs, + .ct_owner = THIS_MODULE, +}; + +/* struct uvcg_output {}; */ + +static struct config_group *uvcg_output_default_groups[] = { + &uvcg_default_output.group, + NULL, +}; + +/* control/terminal/output */ +static struct uvcg_output_grp { + struct config_group group; +} uvcg_output_grp; + +static struct config_item_type uvcg_output_grp_type = { + .ct_owner = THIS_MODULE, +}; + +static struct config_group *uvcg_terminal_default_groups[] = { + &uvcg_camera_grp.group, + &uvcg_output_grp.group, + NULL, +}; + +/* control/terminal */ +static struct uvcg_terminal_grp { + struct config_group group; +} uvcg_terminal_grp; + +static struct config_item_type uvcg_terminal_grp_type = { + .ct_owner = THIS_MODULE, +}; + +/* control/class/{fs} */ +static struct uvcg_control_class { + struct config_group group; +} uvcg_control_class_fs, uvcg_control_class_ss; + + +static inline struct uvc_descriptor_header +**uvcg_get_ctl_class_arr(struct config_item *i, struct f_uvc_opts *o) +{ + struct uvcg_control_class *cl = container_of(to_config_group(i), + struct uvcg_control_class, group); + + if (cl == &uvcg_control_class_fs) + return o->uvc_fs_control_cls; + + if (cl == &uvcg_control_class_ss) + return o->uvc_ss_control_cls; + + return NULL; +} + +static int uvcg_control_class_allow_link(struct config_item *src, + struct config_item *target) +{ + struct config_item *control, *header; + struct f_uvc_opts *opts; + struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex; + struct uvc_descriptor_header **class_array; + struct uvcg_control_header *target_hdr; + int ret = -EINVAL; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + control = src->ci_parent->ci_parent; + header = config_group_find_item(to_config_group(control), "header"); + if (!header || target->ci_parent != header) + goto out; + + opts = to_f_uvc_opts(control->ci_parent); + + mutex_lock(&opts->lock); + + class_array = uvcg_get_ctl_class_arr(src, opts); + if (!class_array) + goto unlock; + if (opts->refcnt || class_array[0]) { + ret = -EBUSY; + goto unlock; + } + + target_hdr = to_uvcg_control_header(target); + ++target_hdr->linked; + class_array[0] = (struct uvc_descriptor_header *)&target_hdr->desc; + ret = 0; + +unlock: + mutex_unlock(&opts->lock); +out: + mutex_unlock(su_mutex); + return ret; +} + +static int uvcg_control_class_drop_link(struct config_item *src, + struct config_item *target) +{ + struct config_item *control, *header; + struct f_uvc_opts *opts; + struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex; + struct uvc_descriptor_header **class_array; + struct uvcg_control_header *target_hdr; + int ret = -EINVAL; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + control = src->ci_parent->ci_parent; + header = config_group_find_item(to_config_group(control), "header"); + if (!header || target->ci_parent != header) + goto out; + + opts = to_f_uvc_opts(control->ci_parent); + + mutex_lock(&opts->lock); + + class_array = uvcg_get_ctl_class_arr(src, opts); + if (!class_array) + goto unlock; + if (opts->refcnt) { + ret = -EBUSY; + goto unlock; + } + + target_hdr = to_uvcg_control_header(target); + --target_hdr->linked; + class_array[0] = NULL; + ret = 0; + +unlock: + mutex_unlock(&opts->lock); +out: + mutex_unlock(su_mutex); + return ret; +} + +static struct configfs_item_operations uvcg_control_class_item_ops = { + .allow_link = uvcg_control_class_allow_link, + .drop_link = uvcg_control_class_drop_link, +}; + +static struct config_item_type uvcg_control_class_type = { + .ct_item_ops = &uvcg_control_class_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct config_group *uvcg_control_class_default_groups[] = { + &uvcg_control_class_fs.group, + &uvcg_control_class_ss.group, + NULL, +}; + +/* control/class */ +static struct uvcg_control_class_grp { + struct config_group group; +} uvcg_control_class_grp; + +static struct config_item_type uvcg_control_class_grp_type = { + .ct_owner = THIS_MODULE, +}; + +static struct config_group *uvcg_control_default_groups[] = { + &uvcg_control_header_grp.group, + &uvcg_processing_grp.group, + &uvcg_terminal_grp.group, + &uvcg_control_class_grp.group, + NULL, +}; + +/* control */ +static struct uvcg_control_grp { + struct config_group group; +} uvcg_control_grp; + +static struct config_item_type uvcg_control_grp_type = { + .ct_owner = THIS_MODULE, +}; + +/* streaming/uncompressed */ +static struct uvcg_uncompressed_grp { + struct config_group group; +} uvcg_uncompressed_grp; + +/* streaming/mjpeg */ +static struct uvcg_mjpeg_grp { + struct config_group group; +} uvcg_mjpeg_grp; + +static struct config_item *fmt_parent[] = { + &uvcg_uncompressed_grp.group.cg_item, + &uvcg_mjpeg_grp.group.cg_item, +}; + +enum uvcg_format_type { + UVCG_UNCOMPRESSED = 0, + UVCG_MJPEG, +}; + +struct uvcg_format { + struct config_group group; + enum uvcg_format_type type; + unsigned linked; + unsigned num_frames; + __u8 bmaControls[UVCG_STREAMING_CONTROL_SIZE]; +}; + +struct uvcg_format *to_uvcg_format(struct config_item *item) +{ + return container_of(to_config_group(item), struct uvcg_format, group); +} + +static ssize_t uvcg_format_bma_controls_show(struct uvcg_format *f, char *page) +{ + struct f_uvc_opts *opts; + struct config_item *opts_item; + struct mutex *su_mutex = &f->group.cg_subsys->su_mutex; + int result, i; + char *pg = page; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = f->group.cg_item.ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + result = sprintf(pg, "0x"); + pg += result; + for (i = 0; i < UVCG_STREAMING_CONTROL_SIZE; ++i) { + result += sprintf(pg, "%x\n", f->bmaControls[i]); + pg = page + result; + } + mutex_unlock(&opts->lock); + + mutex_unlock(su_mutex); + return result; +} + +static ssize_t uvcg_format_bma_controls_store(struct uvcg_format *ch, + const char *page, size_t len) +{ + struct f_uvc_opts *opts; + struct config_item *opts_item; + struct mutex *su_mutex = &ch->group.cg_subsys->su_mutex; + int ret = -EINVAL; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = ch->group.cg_item.ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + if (ch->linked || opts->refcnt) { + ret = -EBUSY; + goto end; + } + + if (len < 4 || *page != '0' || + (*(page + 1) != 'x' && *(page + 1) != 'X')) + goto end; + ret = hex2bin(ch->bmaControls, page + 2, 1); + if (ret < 0) + goto end; + ret = len; +end: + mutex_unlock(&opts->lock); + mutex_unlock(su_mutex); + return ret; +} + +struct uvcg_format_ptr { + struct uvcg_format *fmt; + struct list_head entry; +}; + +/* streaming/header/ */ +struct uvcg_streaming_header { + struct config_item item; + struct uvc_input_header_descriptor desc; + unsigned linked; + struct list_head formats; + unsigned num_fmt; +}; + +struct uvcg_streaming_header *to_uvcg_streaming_header(struct config_item *item) +{ + return container_of(item, struct uvcg_streaming_header, item); +} + +CONFIGFS_ATTR_STRUCT(uvcg_streaming_header); +CONFIGFS_ATTR_OPS(uvcg_streaming_header); + +static int uvcg_streaming_header_allow_link(struct config_item *src, + struct config_item *target) +{ + struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex; + struct config_item *opts_item; + struct f_uvc_opts *opts; + struct uvcg_streaming_header *src_hdr; + struct uvcg_format *target_fmt = NULL; + struct uvcg_format_ptr *format_ptr; + int i, ret = -EINVAL; + + src_hdr = to_uvcg_streaming_header(src); + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = src->ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + + if (src_hdr->linked) { + ret = -EBUSY; + goto out; + } + + for (i = 0; i < ARRAY_SIZE(fmt_parent); ++i) + if (target->ci_parent == fmt_parent[i]) + break; + if (i == ARRAY_SIZE(fmt_parent)) + goto out; + + target_fmt = container_of(to_config_group(target), struct uvcg_format, + group); + if (!target_fmt) + goto out; + + format_ptr = kzalloc(sizeof(*format_ptr), GFP_KERNEL); + if (!format_ptr) { + ret = PTR_ERR(format_ptr); + goto out; + } + ret = 0; + format_ptr->fmt = target_fmt; + list_add_tail(&format_ptr->entry, &src_hdr->formats); + ++src_hdr->num_fmt; + +out: + mutex_unlock(&opts->lock); + mutex_unlock(su_mutex); + return ret; +} + +static int uvcg_streaming_header_drop_link(struct config_item *src, + struct config_item *target) +{ + struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex; + struct config_item *opts_item; + struct f_uvc_opts *opts; + struct uvcg_streaming_header *src_hdr; + struct uvcg_format *target_fmt = NULL; + struct uvcg_format_ptr *format_ptr, *tmp; + int ret = -EINVAL; + + src_hdr = to_uvcg_streaming_header(src); + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = src->ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + target_fmt = container_of(to_config_group(target), struct uvcg_format, + group); + if (!target_fmt) + goto out; + + list_for_each_entry_safe(format_ptr, tmp, &src_hdr->formats, entry) + if (format_ptr->fmt == target_fmt) { + list_del(&format_ptr->entry); + kfree(format_ptr); + --src_hdr->num_fmt; + break; + } + +out: + mutex_unlock(&opts->lock); + mutex_unlock(su_mutex); + return ret; + +} + +static struct configfs_item_operations uvcg_streaming_header_item_ops = { + .show_attribute = uvcg_streaming_header_attr_show, + .store_attribute = uvcg_streaming_header_attr_store, + .allow_link = uvcg_streaming_header_allow_link, + .drop_link = uvcg_streaming_header_drop_link, +}; + +#define UVCG_STREAMING_HEADER_ATTR(cname, aname, conv) \ +static ssize_t uvcg_streaming_header_##cname##_show( \ + struct uvcg_streaming_header *sh, char *page) \ +{ \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &sh->item.ci_group->cg_subsys->su_mutex;\ + int result; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = sh->item.ci_parent->ci_parent->ci_parent; \ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%d\n", conv(sh->desc.aname)); \ + mutex_unlock(&opts->lock); \ + \ + mutex_unlock(su_mutex); \ + return result; \ +} \ + \ +static struct uvcg_streaming_header_attribute \ + uvcg_streaming_header_##cname = \ + __CONFIGFS_ATTR_RO(aname, uvcg_streaming_header_##cname##_show) + +#define identity_conv(x) (x) + +UVCG_STREAMING_HEADER_ATTR(bm_info, bmInfo, identity_conv); +UVCG_STREAMING_HEADER_ATTR(b_terminal_link, bTerminalLink, identity_conv); +UVCG_STREAMING_HEADER_ATTR(b_still_capture_method, bStillCaptureMethod, + identity_conv); +UVCG_STREAMING_HEADER_ATTR(b_trigger_support, bTriggerSupport, identity_conv); +UVCG_STREAMING_HEADER_ATTR(b_trigger_usage, bTriggerUsage, identity_conv); + +#undef identity_conv + +#undef UVCG_STREAMING_HEADER_ATTR + +static struct configfs_attribute *uvcg_streaming_header_attrs[] = { + &uvcg_streaming_header_bm_info.attr, + &uvcg_streaming_header_b_terminal_link.attr, + &uvcg_streaming_header_b_still_capture_method.attr, + &uvcg_streaming_header_b_trigger_support.attr, + &uvcg_streaming_header_b_trigger_usage.attr, + NULL, +}; + +struct config_item_type uvcg_streaming_header_type = { + .ct_item_ops = &uvcg_streaming_header_item_ops, + .ct_attrs = uvcg_streaming_header_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_item +*uvcg_streaming_header_make(struct config_group *group, const char *name) +{ + struct uvcg_streaming_header *h; + + h = kzalloc(sizeof(*h), GFP_KERNEL); + if (!h) + return ERR_CAST(h); + + INIT_LIST_HEAD(&h->formats); + h->desc.bDescriptorType = USB_DT_CS_INTERFACE; + h->desc.bDescriptorSubType = UVC_VS_INPUT_HEADER; + h->desc.bTerminalLink = 3; + h->desc.bControlSize = UVCG_STREAMING_CONTROL_SIZE; + + config_item_init_type_name(&h->item, name, &uvcg_streaming_header_type); + + return &h->item; +} + +void uvcg_streaming_header_drop(struct config_group *group, + struct config_item *item) +{ + struct uvcg_streaming_header *h = to_uvcg_streaming_header(item); + + kfree(h); +} + +/* streaming/header */ +static struct uvcg_streaming_header_grp { + struct config_group group; +} uvcg_streaming_header_grp; + +static struct configfs_group_operations uvcg_streaming_header_grp_ops = { + .make_item = uvcg_streaming_header_make, + .drop_item = uvcg_streaming_header_drop, +}; + +static struct config_item_type uvcg_streaming_header_grp_type = { + .ct_group_ops = &uvcg_streaming_header_grp_ops, + .ct_owner = THIS_MODULE, +}; + +/* streaming/// */ +struct uvcg_frame { + struct { + u8 b_length; + u8 b_descriptor_type; + u8 b_descriptor_subtype; + u8 b_frame_index; + u8 bm_capabilities; + u16 w_width; + u16 w_height; + u32 dw_min_bit_rate; + u32 dw_max_bit_rate; + u32 dw_max_video_frame_buffer_size; + u32 dw_default_frame_interval; + u8 b_frame_interval_type; + } __attribute__((packed)) frame; + u32 *dw_frame_interval; + enum uvcg_format_type fmt_type; + struct config_item item; +}; + +struct uvcg_frame *to_uvcg_frame(struct config_item *item) +{ + return container_of(item, struct uvcg_frame, item); +} + +CONFIGFS_ATTR_STRUCT(uvcg_frame); +CONFIGFS_ATTR_OPS(uvcg_frame); + +static struct configfs_item_operations uvcg_frame_item_ops = { + .show_attribute = uvcg_frame_attr_show, + .store_attribute = uvcg_frame_attr_store, +}; + +#define UVCG_FRAME_ATTR(cname, aname, conv, str2u, uxx, vnoc, limit) \ +static ssize_t uvcg_frame_##cname##_show(struct uvcg_frame *f, char *page)\ +{ \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &f->item.ci_group->cg_subsys->su_mutex;\ + int result; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = f->item.ci_parent->ci_parent->ci_parent->ci_parent; \ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%d\n", conv(f->frame.cname)); \ + mutex_unlock(&opts->lock); \ + \ + mutex_unlock(su_mutex); \ + return result; \ +} \ + \ +static ssize_t uvcg_frame_##cname##_store(struct uvcg_frame *f, \ + const char *page, size_t len)\ +{ \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct uvcg_format *fmt; \ + struct mutex *su_mutex = &f->item.ci_group->cg_subsys->su_mutex;\ + int ret; \ + uxx num; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = f->item.ci_parent->ci_parent->ci_parent->ci_parent; \ + opts = to_f_uvc_opts(opts_item); \ + fmt = to_uvcg_format(f->item.ci_parent); \ + \ + mutex_lock(&opts->lock); \ + if (fmt->linked || opts->refcnt) { \ + ret = -EBUSY; \ + goto end; \ + } \ + \ + ret = str2u(page, 0, &num); \ + if (ret) \ + goto end; \ + \ + if (num > limit) { \ + ret = -EINVAL; \ + goto end; \ + } \ + f->frame.cname = vnoc(num); \ + ret = len; \ +end: \ + mutex_unlock(&opts->lock); \ + mutex_unlock(su_mutex); \ + return ret; \ +} \ + \ +static struct uvcg_frame_attribute \ + uvcg_frame_##cname = \ + __CONFIGFS_ATTR(aname, S_IRUGO | S_IWUSR, \ + uvcg_frame_##cname##_show, \ + uvcg_frame_##cname##_store) + +#define identity_conv(x) (x) + +UVCG_FRAME_ATTR(bm_capabilities, bmCapabilities, identity_conv, kstrtou8, u8, + identity_conv, 0xFF); +UVCG_FRAME_ATTR(w_width, wWidth, le16_to_cpu, kstrtou16, u16, cpu_to_le16, + 0xFFFF); +UVCG_FRAME_ATTR(w_height, wHeight, le16_to_cpu, kstrtou16, u16, cpu_to_le16, + 0xFFFF); +UVCG_FRAME_ATTR(dw_min_bit_rate, dwMinBitRate, le32_to_cpu, kstrtou32, u32, + cpu_to_le32, 0xFFFFFFFF); +UVCG_FRAME_ATTR(dw_max_bit_rate, dwMaxBitRate, le32_to_cpu, kstrtou32, u32, + cpu_to_le32, 0xFFFFFFFF); +UVCG_FRAME_ATTR(dw_max_video_frame_buffer_size, dwMaxVideoFrameBufferSize, + le32_to_cpu, kstrtou32, u32, cpu_to_le32, 0xFFFFFFFF); +UVCG_FRAME_ATTR(dw_default_frame_interval, dwDefaultFrameInterval, + le32_to_cpu, kstrtou32, u32, cpu_to_le32, 0xFFFFFFFF); + +#undef identity_conv + +#undef UVCG_FRAME_ATTR + +static ssize_t uvcg_frame_dw_frame_interval_show(struct uvcg_frame *frm, + char *page) +{ + struct f_uvc_opts *opts; + struct config_item *opts_item; + struct mutex *su_mutex = &frm->item.ci_group->cg_subsys->su_mutex; + int result, i; + char *pg = page; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = frm->item.ci_parent->ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + for (result = 0, i = 0; i < frm->frame.b_frame_interval_type; ++i) { + result += sprintf(pg, "%d\n", + le32_to_cpu(frm->dw_frame_interval[i])); + pg = page + result; + } + mutex_unlock(&opts->lock); + + mutex_unlock(su_mutex); + return result; +} + +static inline int __uvcg_count_frm_intrv(char *buf, void *priv) +{ + ++*((int *)priv); + return 0; +} + +static inline int __uvcg_fill_frm_intrv(char *buf, void *priv) +{ + u32 num, **interv; + int ret; + + ret = kstrtou32(buf, 0, &num); + if (ret) + return ret; + if (num > 0xFFFFFFFF) + return -EINVAL; + + interv = priv; + **interv = cpu_to_le32(num); + ++*interv; + + return 0; +} + +static int __uvcg_iter_frm_intrv(const char *page, size_t len, + int (*fun)(char *, void *), void *priv) +{ + /* sign, base 2 representation, newline, terminator */ + char buf[1 + sizeof(u32) * 8 + 1 + 1]; + const char *pg = page; + int i, ret; + + if (!fun) + return -EINVAL; + + while (pg - page < len) { + i = 0; + while (i < sizeof(buf) && (pg - page < len) && + *pg != '\0' && *pg != '\n') + buf[i++] = *pg++; + if (i == sizeof(buf)) + return -EINVAL; + while ((pg - page < len) && (*pg == '\0' || *pg == '\n')) + ++pg; + buf[i] = '\0'; + ret = fun(buf, priv); + if (ret) + return ret; + } + + return 0; +} + +static ssize_t uvcg_frame_dw_frame_interval_store(struct uvcg_frame *ch, + const char *page, size_t len) +{ + struct f_uvc_opts *opts; + struct config_item *opts_item; + struct uvcg_format *fmt; + struct mutex *su_mutex = &ch->item.ci_group->cg_subsys->su_mutex; + int ret = 0, n = 0; + u32 *frm_intrv, *tmp; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = ch->item.ci_parent->ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + fmt = to_uvcg_format(ch->item.ci_parent); + + mutex_lock(&opts->lock); + if (fmt->linked || opts->refcnt) { + ret = -EBUSY; + goto end; + } + + ret = __uvcg_iter_frm_intrv(page, len, __uvcg_count_frm_intrv, &n); + if (ret) + goto end; + + tmp = frm_intrv = kcalloc(n, sizeof(u32), GFP_KERNEL); + if (!frm_intrv) { + ret = -ENOMEM; + goto end; + } + + ret = __uvcg_iter_frm_intrv(page, len, __uvcg_fill_frm_intrv, &tmp); + if (ret) { + kfree(frm_intrv); + goto end; + } + + kfree(ch->dw_frame_interval); + ch->dw_frame_interval = frm_intrv; + ch->frame.b_frame_interval_type = n; + ret = len; + +end: + mutex_unlock(&opts->lock); + mutex_unlock(su_mutex); + return ret; +} + +static struct uvcg_frame_attribute + uvcg_frame_dw_frame_interval = + __CONFIGFS_ATTR(dwFrameInterval, S_IRUGO | S_IWUSR, + uvcg_frame_dw_frame_interval_show, + uvcg_frame_dw_frame_interval_store); + +static struct configfs_attribute *uvcg_frame_attrs[] = { + &uvcg_frame_bm_capabilities.attr, + &uvcg_frame_w_width.attr, + &uvcg_frame_w_height.attr, + &uvcg_frame_dw_min_bit_rate.attr, + &uvcg_frame_dw_max_bit_rate.attr, + &uvcg_frame_dw_max_video_frame_buffer_size.attr, + &uvcg_frame_dw_default_frame_interval.attr, + &uvcg_frame_dw_frame_interval.attr, + NULL, +}; + +struct config_item_type uvcg_frame_type = { + .ct_item_ops = &uvcg_frame_item_ops, + .ct_attrs = uvcg_frame_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_item *uvcg_frame_make(struct config_group *group, + const char *name) +{ + struct uvcg_frame *h; + struct uvcg_format *fmt; + struct f_uvc_opts *opts; + struct config_item *opts_item; + + h = kzalloc(sizeof(*h), GFP_KERNEL); + if (!h) + return ERR_CAST(h); + + h->frame.b_descriptor_type = USB_DT_CS_INTERFACE; + h->frame.b_frame_index = 1; + h->frame.w_width = cpu_to_le16(640); + h->frame.w_height = cpu_to_le16(360); + h->frame.dw_min_bit_rate = cpu_to_le32(18432000); + h->frame.dw_max_bit_rate = cpu_to_le32(55296000); + h->frame.dw_max_video_frame_buffer_size = cpu_to_le32(460800); + h->frame.dw_default_frame_interval = cpu_to_le32(666666); + + opts_item = group->cg_item.ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + fmt = to_uvcg_format(&group->cg_item); + if (fmt->type == UVCG_UNCOMPRESSED) { + h->frame.b_descriptor_subtype = UVC_VS_FRAME_UNCOMPRESSED; + h->fmt_type = UVCG_UNCOMPRESSED; + } else if (fmt->type == UVCG_MJPEG) { + h->frame.b_descriptor_subtype = UVC_VS_FRAME_MJPEG; + h->fmt_type = UVCG_MJPEG; + } else { + mutex_unlock(&opts->lock); + return ERR_PTR(-EINVAL); + } + ++fmt->num_frames; + mutex_unlock(&opts->lock); + + config_item_init_type_name(&h->item, name, &uvcg_frame_type); + + return &h->item; +} + +void uvcg_frame_drop(struct config_group *group, struct config_item *item) +{ + struct uvcg_frame *h = to_uvcg_frame(item); + struct uvcg_format *fmt; + struct f_uvc_opts *opts; + struct config_item *opts_item; + + opts_item = group->cg_item.ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + fmt = to_uvcg_format(&group->cg_item); + --fmt->num_frames; + kfree(h); + mutex_unlock(&opts->lock); +} + +/* streaming/uncompressed/ */ +struct uvcg_uncompressed { + struct uvcg_format fmt; + struct uvc_format_uncompressed desc; +}; + +struct uvcg_uncompressed *to_uvcg_uncompressed(struct config_item *item) +{ + return container_of( + container_of(to_config_group(item), struct uvcg_format, group), + struct uvcg_uncompressed, fmt); +} + +CONFIGFS_ATTR_STRUCT(uvcg_uncompressed); +CONFIGFS_ATTR_OPS(uvcg_uncompressed); + +static struct configfs_item_operations uvcg_uncompressed_item_ops = { + .show_attribute = uvcg_uncompressed_attr_show, + .store_attribute = uvcg_uncompressed_attr_store, +}; + +static struct configfs_group_operations uvcg_uncompressed_group_ops = { + .make_item = uvcg_frame_make, + .drop_item = uvcg_frame_drop, +}; + +static ssize_t uvcg_uncompressed_guid_format_show(struct uvcg_uncompressed *ch, + char *page) +{ + struct f_uvc_opts *opts; + struct config_item *opts_item; + struct mutex *su_mutex = &ch->fmt.group.cg_subsys->su_mutex; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = ch->fmt.group.cg_item.ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + memcpy(page, ch->desc.guidFormat, sizeof(ch->desc.guidFormat)); + mutex_unlock(&opts->lock); + + mutex_unlock(su_mutex); + + return sizeof(ch->desc.guidFormat); +} + +static ssize_t uvcg_uncompressed_guid_format_store(struct uvcg_uncompressed *ch, + const char *page, size_t len) +{ + struct f_uvc_opts *opts; + struct config_item *opts_item; + struct mutex *su_mutex = &ch->fmt.group.cg_subsys->su_mutex; + int ret; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = ch->fmt.group.cg_item.ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + if (ch->fmt.linked || opts->refcnt) { + ret = -EBUSY; + goto end; + } + + memcpy(ch->desc.guidFormat, page, + min(sizeof(ch->desc.guidFormat), len)); + ret = sizeof(ch->desc.guidFormat); + +end: + mutex_unlock(&opts->lock); + mutex_unlock(su_mutex); + return ret; +} + +static struct uvcg_uncompressed_attribute uvcg_uncompressed_guid_format = + __CONFIGFS_ATTR(guidFormat, S_IRUGO | S_IWUSR, + uvcg_uncompressed_guid_format_show, + uvcg_uncompressed_guid_format_store); + + +#define UVCG_UNCOMPRESSED_ATTR_RO(cname, aname, conv) \ +static ssize_t uvcg_uncompressed_##cname##_show( \ + struct uvcg_uncompressed *u, char *page) \ +{ \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ + int result; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%d\n", conv(u->desc.aname)); \ + mutex_unlock(&opts->lock); \ + \ + mutex_unlock(su_mutex); \ + return result; \ +} \ + \ +static struct uvcg_uncompressed_attribute \ + uvcg_uncompressed_##cname = \ + __CONFIGFS_ATTR_RO(aname, uvcg_uncompressed_##cname##_show) + +#define UVCG_UNCOMPRESSED_ATTR(cname, aname, conv) \ +static ssize_t uvcg_uncompressed_##cname##_show( \ + struct uvcg_uncompressed *u, char *page) \ +{ \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ + int result; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%d\n", conv(u->desc.aname)); \ + mutex_unlock(&opts->lock); \ + \ + mutex_unlock(su_mutex); \ + return result; \ +} \ + \ +static ssize_t \ +uvcg_uncompressed_##cname##_store(struct uvcg_uncompressed *u, \ + const char *page, size_t len) \ +{ \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ + int ret; \ + u8 num; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + if (u->fmt.linked || opts->refcnt) { \ + ret = -EBUSY; \ + goto end; \ + } \ + \ + ret = kstrtou8(page, 0, &num); \ + if (ret) \ + goto end; \ + \ + if (num > 255) { \ + ret = -EINVAL; \ + goto end; \ + } \ + u->desc.aname = num; \ + ret = len; \ +end: \ + mutex_unlock(&opts->lock); \ + mutex_unlock(su_mutex); \ + return ret; \ +} \ + \ +static struct uvcg_uncompressed_attribute \ + uvcg_uncompressed_##cname = \ + __CONFIGFS_ATTR(aname, S_IRUGO | S_IWUSR, \ + uvcg_uncompressed_##cname##_show, \ + uvcg_uncompressed_##cname##_store) + +#define identity_conv(x) (x) + +UVCG_UNCOMPRESSED_ATTR(b_bits_per_pixel, bBitsPerPixel, identity_conv); +UVCG_UNCOMPRESSED_ATTR(b_default_frame_index, bDefaultFrameIndex, + identity_conv); +UVCG_UNCOMPRESSED_ATTR_RO(b_aspect_ratio_x, bAspectRatioX, identity_conv); +UVCG_UNCOMPRESSED_ATTR_RO(b_aspect_ratio_y, bAspectRatioY, identity_conv); +UVCG_UNCOMPRESSED_ATTR_RO(bm_interface_flags, bmInterfaceFlags, identity_conv); + +#undef identity_conv + +#undef UVCG_UNCOMPRESSED_ATTR +#undef UVCG_UNCOMPRESSED_ATTR_RO + +static inline ssize_t +uvcg_uncompressed_bma_controls_show(struct uvcg_uncompressed *unc, char *page) +{ + return uvcg_format_bma_controls_show(&unc->fmt, page); +} + +static inline ssize_t +uvcg_uncompressed_bma_controls_store(struct uvcg_uncompressed *ch, + const char *page, size_t len) +{ + return uvcg_format_bma_controls_store(&ch->fmt, page, len); +} + +static struct uvcg_uncompressed_attribute uvcg_uncompressed_bma_controls = + __CONFIGFS_ATTR(bmaControls, S_IRUGO | S_IWUSR, + uvcg_uncompressed_bma_controls_show, + uvcg_uncompressed_bma_controls_store); + +static struct configfs_attribute *uvcg_uncompressed_attrs[] = { + &uvcg_uncompressed_guid_format.attr, + &uvcg_uncompressed_b_bits_per_pixel.attr, + &uvcg_uncompressed_b_default_frame_index.attr, + &uvcg_uncompressed_b_aspect_ratio_x.attr, + &uvcg_uncompressed_b_aspect_ratio_y.attr, + &uvcg_uncompressed_bm_interface_flags.attr, + &uvcg_uncompressed_bma_controls.attr, + NULL, +}; + +struct config_item_type uvcg_uncompressed_type = { + .ct_item_ops = &uvcg_uncompressed_item_ops, + .ct_group_ops = &uvcg_uncompressed_group_ops, + .ct_attrs = uvcg_uncompressed_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_group *uvcg_uncompressed_make(struct config_group *group, + const char *name) +{ + static char guid[] = { + 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 + }; + struct uvcg_uncompressed *h; + + h = kzalloc(sizeof(*h), GFP_KERNEL); + if (!h) + return ERR_CAST(h); + + h->desc.bLength = UVC_DT_FORMAT_UNCOMPRESSED_SIZE; + h->desc.bDescriptorType = USB_DT_CS_INTERFACE; + h->desc.bDescriptorSubType = UVC_VS_FORMAT_UNCOMPRESSED; + memcpy(h->desc.guidFormat, guid, sizeof(guid)); + h->desc.bBitsPerPixel = 16; + h->desc.bDefaultFrameIndex = 1; + h->desc.bAspectRatioX = 0; + h->desc.bAspectRatioY = 0; + h->desc.bmInterfaceFlags = 0; + h->desc.bCopyProtect = 0; + + h->fmt.type = UVCG_UNCOMPRESSED; + config_group_init_type_name(&h->fmt.group, name, + &uvcg_uncompressed_type); + + return &h->fmt.group; +} + +void uvcg_uncompressed_drop(struct config_group *group, + struct config_item *item) +{ + struct uvcg_uncompressed *h = to_uvcg_uncompressed(item); + + kfree(h); +} + +static struct configfs_group_operations uvcg_uncompressed_grp_ops = { + .make_group = uvcg_uncompressed_make, + .drop_item = uvcg_uncompressed_drop, +}; + +static struct config_item_type uvcg_uncompressed_grp_type = { + .ct_group_ops = &uvcg_uncompressed_grp_ops, + .ct_owner = THIS_MODULE, +}; + +/* streaming/mjpeg/ */ +struct uvcg_mjpeg { + struct uvcg_format fmt; + struct uvc_format_mjpeg desc; +}; + +struct uvcg_mjpeg *to_uvcg_mjpeg(struct config_item *item) +{ + return container_of( + container_of(to_config_group(item), struct uvcg_format, group), + struct uvcg_mjpeg, fmt); +} + +CONFIGFS_ATTR_STRUCT(uvcg_mjpeg); +CONFIGFS_ATTR_OPS(uvcg_mjpeg); + +static struct configfs_item_operations uvcg_mjpeg_item_ops = { + .show_attribute = uvcg_mjpeg_attr_show, + .store_attribute = uvcg_mjpeg_attr_store, +}; + +static struct configfs_group_operations uvcg_mjpeg_group_ops = { + .make_item = uvcg_frame_make, + .drop_item = uvcg_frame_drop, +}; + +#define UVCG_MJPEG_ATTR_RO(cname, aname, conv) \ +static ssize_t uvcg_mjpeg_##cname##_show(struct uvcg_mjpeg *u, char *page)\ +{ \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ + int result; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%d\n", conv(u->desc.aname)); \ + mutex_unlock(&opts->lock); \ + \ + mutex_unlock(su_mutex); \ + return result; \ +} \ + \ +static struct uvcg_mjpeg_attribute \ + uvcg_mjpeg_##cname = \ + __CONFIGFS_ATTR_RO(aname, uvcg_mjpeg_##cname##_show) + +#define UVCG_MJPEG_ATTR(cname, aname, conv) \ +static ssize_t uvcg_mjpeg_##cname##_show(struct uvcg_mjpeg *u, char *page)\ +{ \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ + int result; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%d\n", conv(u->desc.aname)); \ + mutex_unlock(&opts->lock); \ + \ + mutex_unlock(su_mutex); \ + return result; \ +} \ + \ +static ssize_t \ +uvcg_mjpeg_##cname##_store(struct uvcg_mjpeg *u, \ + const char *page, size_t len) \ +{ \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ + int ret; \ + u8 num; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + if (u->fmt.linked || opts->refcnt) { \ + ret = -EBUSY; \ + goto end; \ + } \ + \ + ret = kstrtou8(page, 0, &num); \ + if (ret) \ + goto end; \ + \ + if (num > 255) { \ + ret = -EINVAL; \ + goto end; \ + } \ + u->desc.aname = num; \ + ret = len; \ +end: \ + mutex_unlock(&opts->lock); \ + mutex_unlock(su_mutex); \ + return ret; \ +} \ + \ +static struct uvcg_mjpeg_attribute \ + uvcg_mjpeg_##cname = \ + __CONFIGFS_ATTR(aname, S_IRUGO | S_IWUSR, \ + uvcg_mjpeg_##cname##_show, \ + uvcg_mjpeg_##cname##_store) + +#define identity_conv(x) (x) + +UVCG_MJPEG_ATTR(b_default_frame_index, bDefaultFrameIndex, + identity_conv); +UVCG_MJPEG_ATTR_RO(bm_flags, bmFlags, identity_conv); +UVCG_MJPEG_ATTR_RO(b_aspect_ratio_x, bAspectRatioX, identity_conv); +UVCG_MJPEG_ATTR_RO(b_aspect_ratio_y, bAspectRatioY, identity_conv); +UVCG_MJPEG_ATTR_RO(bm_interface_flags, bmInterfaceFlags, identity_conv); + +#undef identity_conv + +#undef UVCG_MJPEG_ATTR +#undef UVCG_MJPEG_ATTR_RO + +static inline ssize_t +uvcg_mjpeg_bma_controls_show(struct uvcg_mjpeg *unc, char *page) +{ + return uvcg_format_bma_controls_show(&unc->fmt, page); +} + +static inline ssize_t +uvcg_mjpeg_bma_controls_store(struct uvcg_mjpeg *ch, + const char *page, size_t len) +{ + return uvcg_format_bma_controls_store(&ch->fmt, page, len); +} + +static struct uvcg_mjpeg_attribute uvcg_mjpeg_bma_controls = + __CONFIGFS_ATTR(bmaControls, S_IRUGO | S_IWUSR, + uvcg_mjpeg_bma_controls_show, + uvcg_mjpeg_bma_controls_store); + +static struct configfs_attribute *uvcg_mjpeg_attrs[] = { + &uvcg_mjpeg_b_default_frame_index.attr, + &uvcg_mjpeg_bm_flags.attr, + &uvcg_mjpeg_b_aspect_ratio_x.attr, + &uvcg_mjpeg_b_aspect_ratio_y.attr, + &uvcg_mjpeg_bm_interface_flags.attr, + &uvcg_mjpeg_bma_controls.attr, + NULL, +}; + +struct config_item_type uvcg_mjpeg_type = { + .ct_item_ops = &uvcg_mjpeg_item_ops, + .ct_group_ops = &uvcg_mjpeg_group_ops, + .ct_attrs = uvcg_mjpeg_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_group *uvcg_mjpeg_make(struct config_group *group, + const char *name) +{ + struct uvcg_mjpeg *h; + + h = kzalloc(sizeof(*h), GFP_KERNEL); + if (!h) + return ERR_CAST(h); + + h->desc.bLength = UVC_DT_FORMAT_MJPEG_SIZE; + h->desc.bDescriptorType = USB_DT_CS_INTERFACE; + h->desc.bDescriptorSubType = UVC_VS_FORMAT_MJPEG; + h->desc.bDefaultFrameIndex = 1; + h->desc.bAspectRatioX = 0; + h->desc.bAspectRatioY = 0; + h->desc.bmInterfaceFlags = 0; + h->desc.bCopyProtect = 0; + + h->fmt.type = UVCG_MJPEG; + config_group_init_type_name(&h->fmt.group, name, + &uvcg_mjpeg_type); + + return &h->fmt.group; +} + +void uvcg_mjpeg_drop(struct config_group *group, + struct config_item *item) +{ + struct uvcg_mjpeg *h = to_uvcg_mjpeg(item); + + kfree(h); +} + +static struct configfs_group_operations uvcg_mjpeg_grp_ops = { + .make_group = uvcg_mjpeg_make, + .drop_item = uvcg_mjpeg_drop, +}; + +static struct config_item_type uvcg_mjpeg_grp_type = { + .ct_group_ops = &uvcg_mjpeg_grp_ops, + .ct_owner = THIS_MODULE, +}; + +/* streaming/color_matching/default */ +static struct uvcg_default_color_matching { + struct config_group group; +} uvcg_default_color_matching; + +static inline struct uvcg_default_color_matching +*to_uvcg_default_color_matching(struct config_item *item) +{ + return container_of(to_config_group(item), + struct uvcg_default_color_matching, group); +} + +CONFIGFS_ATTR_STRUCT(uvcg_default_color_matching); +CONFIGFS_ATTR_OPS_RO(uvcg_default_color_matching); + +static struct configfs_item_operations uvcg_default_color_matching_item_ops = { + .show_attribute = uvcg_default_color_matching_attr_show, +}; + +#define UVCG_DEFAULT_COLOR_MATCHING_ATTR(cname, aname, conv) \ +static ssize_t uvcg_default_color_matching_##cname##_show( \ + struct uvcg_default_color_matching *dc, char *page) \ +{ \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &dc->group.cg_subsys->su_mutex; \ + struct uvc_color_matching_descriptor *cd; \ + int result; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = dc->group.cg_item.ci_parent->ci_parent->ci_parent; \ + opts = to_f_uvc_opts(opts_item); \ + cd = &opts->uvc_color_matching; \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%d\n", conv(cd->aname)); \ + mutex_unlock(&opts->lock); \ + \ + mutex_unlock(su_mutex); \ + return result; \ +} \ + \ +static struct uvcg_default_color_matching_attribute \ + uvcg_default_color_matching_##cname = \ + __CONFIGFS_ATTR_RO(aname, uvcg_default_color_matching_##cname##_show) + +#define identity_conv(x) (x) + +UVCG_DEFAULT_COLOR_MATCHING_ATTR(b_color_primaries, bColorPrimaries, + identity_conv); +UVCG_DEFAULT_COLOR_MATCHING_ATTR(b_transfer_characteristics, + bTransferCharacteristics, identity_conv); +UVCG_DEFAULT_COLOR_MATCHING_ATTR(b_matrix_coefficients, bMatrixCoefficients, + identity_conv); + +#undef identity_conv + +#undef UVCG_DEFAULT_COLOR_MATCHING_ATTR + +static struct configfs_attribute *uvcg_default_color_matching_attrs[] = { + &uvcg_default_color_matching_b_color_primaries.attr, + &uvcg_default_color_matching_b_transfer_characteristics.attr, + &uvcg_default_color_matching_b_matrix_coefficients.attr, + NULL, +}; + +static struct config_item_type uvcg_default_color_matching_type = { + .ct_item_ops = &uvcg_default_color_matching_item_ops, + .ct_attrs = uvcg_default_color_matching_attrs, + .ct_owner = THIS_MODULE, +}; + +/* struct uvcg_color_matching {}; */ + +static struct config_group *uvcg_color_matching_default_groups[] = { + &uvcg_default_color_matching.group, + NULL, +}; + +/* streaming/color_matching */ +static struct uvcg_color_matching_grp { + struct config_group group; +} uvcg_color_matching_grp; + +static struct config_item_type uvcg_color_matching_grp_type = { + .ct_owner = THIS_MODULE, +}; + +/* streaming/class/{fs|hs|ss} */ +static struct uvcg_streaming_class { + struct config_group group; +} uvcg_streaming_class_fs, uvcg_streaming_class_hs, uvcg_streaming_class_ss; + + +static inline struct uvc_descriptor_header +***__uvcg_get_stream_class_arr(struct config_item *i, struct f_uvc_opts *o) +{ + struct uvcg_streaming_class *cl = container_of(to_config_group(i), + struct uvcg_streaming_class, group); + + if (cl == &uvcg_streaming_class_fs) + return &o->uvc_fs_streaming_cls; + + if (cl == &uvcg_streaming_class_hs) + return &o->uvc_hs_streaming_cls; + + if (cl == &uvcg_streaming_class_ss) + return &o->uvc_ss_streaming_cls; + + return NULL; +} + +enum uvcg_strm_type { + UVCG_HEADER = 0, + UVCG_FORMAT, + UVCG_FRAME +}; + +static int __uvcg_iter_strm_cls(void *priv1, void *priv2, void *priv3, + int (*fun)(void *, void *, void *, int, enum uvcg_strm_type type)) +{ + struct uvcg_streaming_header *h = priv1; + struct uvcg_format_ptr *f; + struct config_group *grp; + struct config_item *item; + struct uvcg_frame *frm; + int ret, i, j; + + if (!fun) + return -EINVAL; + + i = j = 0; + ret = fun(h, priv2, priv3, 0, UVCG_HEADER); + if (ret) + return ret; + list_for_each_entry(f, &h->formats, entry) { + ret = fun(f->fmt, priv2, priv3, i++, UVCG_FORMAT); + if (ret) + return ret; + grp = &f->fmt->group; + list_for_each_entry(item, &grp->cg_children, ci_entry) { + frm = to_uvcg_frame(item); + ret = fun(frm, priv2, priv3, j++, UVCG_FRAME); + if (ret) + return ret; + } + } + + return ret; +} + +static int __uvcg_cnt_strm(void *priv1, void *priv2, void *priv3, int n, + enum uvcg_strm_type type) +{ + size_t *size = priv2; + size_t *count = priv3; + + switch (type) { + case UVCG_HEADER: { + struct uvcg_streaming_header *h = priv1; + + *size += sizeof(h->desc); + /* bmaControls */ + *size += h->num_fmt * UVCG_STREAMING_CONTROL_SIZE; + } + break; + case UVCG_FORMAT: { + struct uvcg_format *fmt = priv1; + + if (fmt->type == UVCG_UNCOMPRESSED) { + struct uvcg_uncompressed *u = + container_of(fmt, struct uvcg_uncompressed, + fmt); + + *size += sizeof(u->desc); + } else if (fmt->type == UVCG_MJPEG) { + struct uvcg_mjpeg *m = + container_of(fmt, struct uvcg_mjpeg, fmt); + + *size += sizeof(m->desc); + } else { + return -EINVAL; + } + } + break; + case UVCG_FRAME: { + struct uvcg_frame *frm = priv1; + int sz = sizeof(frm->dw_frame_interval); + + *size += sizeof(frm->frame); + *size += frm->frame.b_frame_interval_type * sz; + } + break; + } + + ++*count; + + return 0; +} + +static int __uvcg_fill_strm(void *priv1, void *priv2, void *priv3, int n, + enum uvcg_strm_type type) +{ + void **dest = priv2; + struct uvc_descriptor_header ***array = priv3; + size_t sz; + + **array = *dest; + ++*array; + + switch (type) { + case UVCG_HEADER: { + struct uvc_input_header_descriptor *ihdr = *dest; + struct uvcg_streaming_header *h = priv1; + struct uvcg_format_ptr *f; + + memcpy(*dest, &h->desc, sizeof(h->desc)); + *dest += sizeof(h->desc); + sz = UVCG_STREAMING_CONTROL_SIZE; + list_for_each_entry(f, &h->formats, entry) { + memcpy(*dest, f->fmt->bmaControls, sz); + *dest += sz; + } + ihdr->bLength = sizeof(h->desc) + h->num_fmt * sz; + ihdr->bNumFormats = h->num_fmt; + } + break; + case UVCG_FORMAT: { + struct uvcg_format *fmt = priv1; + + if (fmt->type == UVCG_UNCOMPRESSED) { + struct uvc_format_uncompressed *unc = *dest; + struct uvcg_uncompressed *u = + container_of(fmt, struct uvcg_uncompressed, + fmt); + + memcpy(*dest, &u->desc, sizeof(u->desc)); + *dest += sizeof(u->desc); + unc->bNumFrameDescriptors = fmt->num_frames; + unc->bFormatIndex = n + 1; + } else if (fmt->type == UVCG_MJPEG) { + struct uvc_format_mjpeg *mjp = *dest; + struct uvcg_mjpeg *m = + container_of(fmt, struct uvcg_mjpeg, fmt); + + memcpy(*dest, &m->desc, sizeof(m->desc)); + *dest += sizeof(m->desc); + mjp->bNumFrameDescriptors = fmt->num_frames; + mjp->bFormatIndex = n + 1; + } else { + return -EINVAL; + } + } + break; + case UVCG_FRAME: { + struct uvcg_frame *frm = priv1; + struct uvc_descriptor_header *h = *dest; + + sz = sizeof(frm->frame); + memcpy(*dest, &frm->frame, sz); + *dest += sz; + sz = frm->frame.b_frame_interval_type * + sizeof(*frm->dw_frame_interval); + memcpy(*dest, frm->dw_frame_interval, sz); + *dest += sz; + if (frm->fmt_type == UVCG_UNCOMPRESSED) + h->bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE( + frm->frame.b_frame_interval_type); + else if (frm->fmt_type == UVCG_MJPEG) + h->bLength = UVC_DT_FRAME_MJPEG_SIZE( + frm->frame.b_frame_interval_type); + } + break; + } + + return 0; +} + +static int uvcg_streaming_class_allow_link(struct config_item *src, + struct config_item *target) +{ + struct config_item *streaming, *header; + struct f_uvc_opts *opts; + struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex; + struct uvc_descriptor_header ***class_array, **cl_arr; + struct uvcg_streaming_header *target_hdr; + void *data; + size_t size = 0, count = 0; + int ret = -EINVAL; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + streaming = src->ci_parent->ci_parent; + header = config_group_find_item(to_config_group(streaming), "header"); + if (!header || target->ci_parent != header) + goto out; + + opts = to_f_uvc_opts(streaming->ci_parent); + + mutex_lock(&opts->lock); + + class_array = __uvcg_get_stream_class_arr(src, opts); + if (!class_array || *class_array || opts->refcnt) { + ret = -EBUSY; + goto unlock; + } + + target_hdr = to_uvcg_streaming_header(target); + ret = __uvcg_iter_strm_cls(target_hdr, &size, &count, __uvcg_cnt_strm); + if (ret) + goto unlock; + + count += 2; /* color_matching, NULL */ + *class_array = kcalloc(count, sizeof(void *), GFP_KERNEL); + if (!*class_array) { + ret = PTR_ERR(*class_array); + goto unlock; + } + + data = kzalloc(size, GFP_KERNEL); + if (!data) { + kfree(*class_array); + *class_array = NULL; + ret = PTR_ERR(data); + goto unlock; + } + cl_arr = *class_array; + ret = __uvcg_iter_strm_cls(target_hdr, &data, &cl_arr, + __uvcg_fill_strm); + if (ret) { + kfree(*class_array); + *class_array = NULL; + kfree(data); + goto unlock; + } + *cl_arr = (struct uvc_descriptor_header *)&opts->uvc_color_matching; + + ++target_hdr->linked; + ret = 0; + +unlock: + mutex_unlock(&opts->lock); +out: + mutex_unlock(su_mutex); + return ret; +} + +static int uvcg_streaming_class_drop_link(struct config_item *src, + struct config_item *target) +{ + struct config_item *streaming, *header; + struct f_uvc_opts *opts; + struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex; + struct uvc_descriptor_header ***class_array; + struct uvcg_streaming_header *target_hdr; + int ret = -EINVAL; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + streaming = src->ci_parent->ci_parent; + header = config_group_find_item(to_config_group(streaming), "header"); + if (!header || target->ci_parent != header) + goto out; + + opts = to_f_uvc_opts(streaming->ci_parent); + + mutex_lock(&opts->lock); + + class_array = __uvcg_get_stream_class_arr(src, opts); + if (!class_array || !*class_array) + goto unlock; + + if (opts->refcnt) { + ret = -EBUSY; + goto unlock; + } + + target_hdr = to_uvcg_streaming_header(target); + --target_hdr->linked; + kfree(**class_array); + kfree(*class_array); + *class_array = NULL; + ret = 0; + +unlock: + mutex_unlock(&opts->lock); +out: + mutex_unlock(su_mutex); + return ret; +} + +static struct configfs_item_operations uvcg_streaming_class_item_ops = { + .allow_link = uvcg_streaming_class_allow_link, + .drop_link = uvcg_streaming_class_drop_link, +}; + +static struct config_item_type uvcg_streaming_class_type = { + .ct_item_ops = &uvcg_streaming_class_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct config_group *uvcg_streaming_class_default_groups[] = { + &uvcg_streaming_class_fs.group, + &uvcg_streaming_class_hs.group, + &uvcg_streaming_class_ss.group, + NULL, +}; + +/* streaming/class */ +static struct uvcg_streaming_class_grp { + struct config_group group; +} uvcg_streaming_class_grp; + +static struct config_item_type uvcg_streaming_class_grp_type = { + .ct_owner = THIS_MODULE, +}; + +static struct config_group *uvcg_streaming_default_groups[] = { + &uvcg_streaming_header_grp.group, + &uvcg_uncompressed_grp.group, + &uvcg_mjpeg_grp.group, + &uvcg_color_matching_grp.group, + &uvcg_streaming_class_grp.group, + NULL, +}; + +/* streaming */ +static struct uvcg_streaming_grp { + struct config_group group; +} uvcg_streaming_grp; + +static struct config_item_type uvcg_streaming_grp_type = { + .ct_owner = THIS_MODULE, +}; + +static struct config_group *uvcg_default_groups[] = { + &uvcg_control_grp.group, + &uvcg_streaming_grp.group, + NULL, +}; + +static inline struct f_uvc_opts *to_f_uvc_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_uvc_opts, + func_inst.group); +} + +CONFIGFS_ATTR_STRUCT(f_uvc_opts); +CONFIGFS_ATTR_OPS(f_uvc_opts); + +static void uvc_attr_release(struct config_item *item) +{ + struct f_uvc_opts *opts = to_f_uvc_opts(item); + + usb_put_function_instance(&opts->func_inst); +} + +static struct configfs_item_operations uvc_item_ops = { + .release = uvc_attr_release, + .show_attribute = f_uvc_opts_attr_show, + .store_attribute = f_uvc_opts_attr_store, +}; + +#define UVCG_OPTS_ATTR(cname, conv, str2u, uxx, vnoc, limit) \ +static ssize_t f_uvc_opts_##cname##_show( \ + struct f_uvc_opts *opts, char *page) \ +{ \ + int result; \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%d\n", conv(opts->cname)); \ + mutex_unlock(&opts->lock); \ + \ + return result; \ +} \ + \ +static ssize_t \ +f_uvc_opts_##cname##_store(struct f_uvc_opts *opts, \ + const char *page, size_t len) \ +{ \ + int ret; \ + uxx num; \ + \ + mutex_lock(&opts->lock); \ + if (opts->refcnt) { \ + ret = -EBUSY; \ + goto end; \ + } \ + \ + ret = str2u(page, 0, &num); \ + if (ret) \ + goto end; \ + \ + if (num > limit) { \ + ret = -EINVAL; \ + goto end; \ + } \ + opts->cname = vnoc(num); \ + ret = len; \ +end: \ + mutex_unlock(&opts->lock); \ + return ret; \ +} \ + \ +static struct f_uvc_opts_attribute \ + f_uvc_opts_attribute_##cname = \ + __CONFIGFS_ATTR(cname, S_IRUGO | S_IWUSR, \ + f_uvc_opts_##cname##_show, \ + f_uvc_opts_##cname##_store) + +#define identity_conv(x) (x) + +UVCG_OPTS_ATTR(streaming_interval, identity_conv, kstrtou8, u8, identity_conv, + 16); +UVCG_OPTS_ATTR(streaming_maxpacket, le16_to_cpu, kstrtou16, u16, le16_to_cpu, + 3072); +UVCG_OPTS_ATTR(streaming_maxburst, identity_conv, kstrtou8, u8, identity_conv, + 15); + +#undef identity_conv + +#undef UVCG_OPTS_ATTR + +static struct configfs_attribute *uvc_attrs[] = { + &f_uvc_opts_attribute_streaming_interval.attr, + &f_uvc_opts_attribute_streaming_maxpacket.attr, + &f_uvc_opts_attribute_streaming_maxburst.attr, + NULL, +}; + +static struct config_item_type uvc_func_type = { + .ct_item_ops = &uvc_item_ops, + .ct_attrs = uvc_attrs, + .ct_owner = THIS_MODULE, +}; + +static inline void uvcg_init_group(struct config_group *g, + struct config_group **default_groups, + const char *name, + struct config_item_type *type) +{ + g->default_groups = default_groups; + config_group_init_type_name(g, name, type); +} + +int uvcg_attach_configfs(struct f_uvc_opts *opts) +{ + config_group_init_type_name(&uvcg_control_header_grp.group, + "header", + &uvcg_control_header_grp_type); + config_group_init_type_name(&uvcg_default_processing.group, + "default", + &uvcg_default_processing_type); + uvcg_init_group(&uvcg_processing_grp.group, + uvcg_processing_default_groups, + "processing", + &uvcg_processing_grp_type); + config_group_init_type_name(&uvcg_default_camera.group, + "default", + &uvcg_default_camera_type); + uvcg_init_group(&uvcg_camera_grp.group, + uvcg_camera_default_groups, + "camera", + &uvcg_camera_grp_type); + config_group_init_type_name(&uvcg_default_output.group, + "default", + &uvcg_default_output_type); + uvcg_init_group(&uvcg_output_grp.group, + uvcg_output_default_groups, + "output", + &uvcg_output_grp_type); + uvcg_init_group(&uvcg_terminal_grp.group, + uvcg_terminal_default_groups, + "terminal", + &uvcg_terminal_grp_type); + config_group_init_type_name(&uvcg_control_class_fs.group, + "fs", + &uvcg_control_class_type); + config_group_init_type_name(&uvcg_control_class_ss.group, + "ss", + &uvcg_control_class_type); + uvcg_init_group(&uvcg_control_class_grp.group, + uvcg_control_class_default_groups, + "class", + &uvcg_control_class_grp_type); + uvcg_init_group(&uvcg_control_grp.group, + uvcg_control_default_groups, + "control", + &uvcg_control_grp_type); + config_group_init_type_name(&uvcg_streaming_header_grp.group, + "header", + &uvcg_streaming_header_grp_type); + config_group_init_type_name(&uvcg_uncompressed_grp.group, + "uncompressed", + &uvcg_uncompressed_grp_type); + config_group_init_type_name(&uvcg_mjpeg_grp.group, + "mjpeg", + &uvcg_mjpeg_grp_type); + config_group_init_type_name(&uvcg_default_color_matching.group, + "default", + &uvcg_default_color_matching_type); + uvcg_init_group(&uvcg_color_matching_grp.group, + uvcg_color_matching_default_groups, + "color_matching", + &uvcg_color_matching_grp_type); + config_group_init_type_name(&uvcg_streaming_class_fs.group, + "fs", + &uvcg_streaming_class_type); + config_group_init_type_name(&uvcg_streaming_class_hs.group, + "hs", + &uvcg_streaming_class_type); + config_group_init_type_name(&uvcg_streaming_class_ss.group, + "ss", + &uvcg_streaming_class_type); + uvcg_init_group(&uvcg_streaming_class_grp.group, + uvcg_streaming_class_default_groups, + "class", + &uvcg_streaming_class_grp_type); + uvcg_init_group(&uvcg_streaming_grp.group, + uvcg_streaming_default_groups, + "streaming", + &uvcg_streaming_grp_type); + uvcg_init_group(&opts->func_inst.group, + uvcg_default_groups, + "", + &uvc_func_type); + return 0; +} diff --git a/drivers/usb/gadget/function/uvc_configfs.h b/drivers/usb/gadget/function/uvc_configfs.h new file mode 100644 index 000000000000..085e67be7c71 --- /dev/null +++ b/drivers/usb/gadget/function/uvc_configfs.h @@ -0,0 +1,22 @@ +/* + * uvc_configfs.h + * + * Configfs support for the uvc function. + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef UVC_CONFIGFS_H +#define UVC_CONFIGFS_H + +struct f_uvc_opts; + +int uvcg_attach_configfs(struct f_uvc_opts *opts); + +#endif /* UVC_CONFIGFS_H */ From 135b3c4304dacd8bb01f1aa6fe4165126bee3812 Mon Sep 17 00:00:00 2001 From: Yunzhi Li Date: Mon, 8 Dec 2014 17:46:26 +0800 Subject: [PATCH 031/214] usb: dwc2: platform: add generic PHY framework support Get PHY parameters from devicetree and power off usb PHY during system suspend. Acked-by: Paul Zimmerman Signed-off-by: Yunzhi Li Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 33 ++++++++++++--------------------- drivers/usb/dwc2/platform.c | 36 ++++++++++++++++++++++++++++++++++-- 2 files changed, 46 insertions(+), 23 deletions(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index ede69dc75f0d..a461406fe8f8 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -3410,8 +3410,6 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) { struct device *dev = hsotg->dev; struct s3c_hsotg_plat *plat = dev->platform_data; - struct phy *phy; - struct usb_phy *uphy; struct s3c_hsotg_ep *eps; int epnum; int ret; @@ -3421,30 +3419,23 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) hsotg->phyif = GUSBCFG_PHYIF16; /* - * Attempt to find a generic PHY, then look for an old style - * USB PHY, finally fall back to pdata + * If platform probe couldn't find a generic PHY or an old style + * USB PHY, fall back to pdata */ - phy = devm_phy_get(dev, "usb2-phy"); - if (IS_ERR(phy)) { - uphy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); - if (IS_ERR(uphy)) { - /* Fallback for pdata */ - plat = dev_get_platdata(dev); - if (!plat) { - dev_err(dev, - "no platform data or transceiver defined\n"); - return -EPROBE_DEFER; - } - hsotg->plat = plat; - } else - hsotg->uphy = uphy; - } else { - hsotg->phy = phy; + if (IS_ERR_OR_NULL(hsotg->phy) && IS_ERR_OR_NULL(hsotg->uphy)) { + plat = dev_get_platdata(dev); + if (!plat) { + dev_err(dev, + "no platform data or transceiver defined\n"); + return -EPROBE_DEFER; + } + hsotg->plat = plat; + } else if (hsotg->phy) { /* * If using the generic PHY framework, check if the PHY bus * width is 8-bit and set the phyif appropriately. */ - if (phy_get_bus_width(phy) == 8) + if (phy_get_bus_width(hsotg->phy) == 8) hsotg->phyif = GUSBCFG_PHYIF8; } diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 6a795aa2ff05..ae095f009b4f 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -155,6 +155,8 @@ static int dwc2_driver_probe(struct platform_device *dev) struct dwc2_core_params defparams; struct dwc2_hsotg *hsotg; struct resource *res; + struct phy *phy; + struct usb_phy *uphy; int retval; int irq; @@ -212,6 +214,24 @@ static int dwc2_driver_probe(struct platform_device *dev) hsotg->dr_mode = of_usb_get_dr_mode(dev->dev.of_node); + /* + * Attempt to find a generic PHY, then look for an old style + * USB PHY + */ + phy = devm_phy_get(&dev->dev, "usb2-phy"); + if (IS_ERR(phy)) { + hsotg->phy = NULL; + uphy = devm_usb_get_phy(&dev->dev, USB_PHY_TYPE_USB2); + if (IS_ERR(uphy)) + hsotg->uphy = NULL; + else + hsotg->uphy = uphy; + } else { + hsotg->phy = phy; + phy_power_on(hsotg->phy); + phy_init(hsotg->phy); + } + spin_lock_init(&hsotg->lock); mutex_init(&hsotg->init_mutex); retval = dwc2_gadget_init(hsotg, irq); @@ -231,8 +251,15 @@ static int __maybe_unused dwc2_suspend(struct device *dev) struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev); int ret = 0; - if (dwc2_is_device_mode(dwc2)) + if (dwc2_is_device_mode(dwc2)) { ret = s3c_hsotg_suspend(dwc2); + } else { + if (dwc2->lx_state == DWC2_L0) + return 0; + phy_exit(dwc2->phy); + phy_power_off(dwc2->phy); + + } return ret; } @@ -241,8 +268,13 @@ static int __maybe_unused dwc2_resume(struct device *dev) struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev); int ret = 0; - if (dwc2_is_device_mode(dwc2)) + if (dwc2_is_device_mode(dwc2)) { ret = s3c_hsotg_resume(dwc2); + } else { + phy_power_on(dwc2->phy); + phy_init(dwc2->phy); + + } return ret; } From 5e841efef7543209c1d62526311fbbcf85f3613d Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 25 Nov 2014 18:55:57 +0300 Subject: [PATCH 032/214] usb: gadget: udc: clean up a printk We already know what "value" is, so there is no need to check. It puzzles static checkers to have the unneeded condition. Signed-off-by: Dan Carpenter Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/bdc/bdc_ep.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/udc/bdc/bdc_ep.c b/drivers/usb/gadget/udc/bdc/bdc_ep.c index ff67ceac77c4..ebf82bd050cf 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_ep.c +++ b/drivers/usb/gadget/udc/bdc/bdc_ep.c @@ -881,8 +881,8 @@ static int ep_set_halt(struct bdc_ep *ep, u32 value) ret = bdc_ep_set_stall(bdc, ep->ep_num); if (ret) - dev_err(bdc->dev, "failed to %s STALL on %s\n", - value ? "set" : "clear", ep->name); + dev_err(bdc->dev, "failed to set STALL on %s\n", + ep->name); else ep->flags |= BDC_EP_STALL; } else { @@ -890,8 +890,8 @@ static int ep_set_halt(struct bdc_ep *ep, u32 value) dev_dbg(bdc->dev, "Before Clear\n"); ret = bdc_ep_clear_stall(bdc, ep->ep_num); if (ret) - dev_err(bdc->dev, "failed to %s STALL on %s\n", - value ? "set" : "clear", ep->name); + dev_err(bdc->dev, "failed to clear STALL on %s\n", + ep->name); else ep->flags &= ~BDC_EP_STALL; dev_dbg(bdc->dev, "After Clear\n"); From d71b0d7764a7b018bf73ae44f67dc8545e614eef Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 25 Nov 2014 18:56:22 +0300 Subject: [PATCH 033/214] usb: gadget: udc: remove bogus NULL check "ep" isn't NULL here, and static checkers complain because we dereferenced it on the previous line. Signed-off-by: Dan Carpenter Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/bdc/bdc_ep.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/bdc/bdc_ep.c b/drivers/usb/gadget/udc/bdc/bdc_ep.c index ebf82bd050cf..fdc0e9fc39d0 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_ep.c +++ b/drivers/usb/gadget/udc/bdc/bdc_ep.c @@ -719,7 +719,7 @@ static int ep_queue(struct bdc_ep *ep, struct bdc_req *req) int ret = 0; bdc = ep->bdc; - if (!req || !ep || !ep->usb_ep.desc) + if (!req || !ep->usb_ep.desc) return -EINVAL; req->usb_req.actual = 0; From 90f9e53345d307f53b040e7a50193f20f88ab87a Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Wed, 26 Nov 2014 07:00:45 +0100 Subject: [PATCH 034/214] usb: gadget: fix platform_no_drv_owner.cocci warnings Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci Signed-off-by: Julia Lawall Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/bdc/bdc_core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c index c6dfef8c7bbc..5c8f4effb62a 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_core.c +++ b/drivers/usb/gadget/udc/bdc/bdc_core.c @@ -521,7 +521,6 @@ static int bdc_remove(struct platform_device *pdev) static struct platform_driver bdc_driver = { .driver = { .name = BRCM_BDC_NAME, - .owner = THIS_MODULE }, .probe = bdc_probe, .remove = bdc_remove, From 625763d101c7aa706b35f052cd978a61f1ef26bc Mon Sep 17 00:00:00 2001 From: Xuebing Wang Date: Wed, 10 Dec 2014 16:28:14 +0800 Subject: [PATCH 035/214] usb: gadget: f_uac1: configure endpoint before using it UAC1 forget to do it. Signed-off-by: Xuebing Wang Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uac1.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index f7b203293205..380b13778556 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -584,6 +584,7 @@ static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (intf == 1) { if (alt == 1) { + config_ep_by_speed(cdev->gadget, f, out_ep); usb_ep_enable(out_ep); out_ep->driver_data = audio; audio->copy_buf = f_audio_buffer_alloc(audio_buf_size); From 8d252db174ab27485126f540f368149e3c875e5a Mon Sep 17 00:00:00 2001 From: Xuebing Wang Date: Wed, 10 Dec 2014 16:28:15 +0800 Subject: [PATCH 036/214] usb: gadget: f_uac1: update Class-Specific AudioControl Interface Header Descriptor Update this according to USB Audio Class 1.0 spec. This fixes the Windows 7 detection issue. Cc: Rafael Brune Signed-off-by: Xuebing Wang Signed-off-by: Fabio Estevam Signed-off-by: Peter Chen (Fixed some code style issues) Tested-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uac1.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index 380b13778556..d8b4af6b5273 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -31,7 +31,7 @@ static int generic_get_cmd(struct usb_audio_control *con, u8 cmd); */ #define F_AUDIO_AC_INTERFACE 0 #define F_AUDIO_AS_INTERFACE 1 -#define F_AUDIO_NUM_INTERFACES 2 +#define F_AUDIO_NUM_INTERFACES 1 /* B.3.1 Standard AC Interface Descriptor */ static struct usb_interface_descriptor ac_interface_desc = { @@ -42,14 +42,18 @@ static struct usb_interface_descriptor ac_interface_desc = { .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, }; -DECLARE_UAC_AC_HEADER_DESCRIPTOR(2); +/* + * The number of AudioStreaming and MIDIStreaming interfaces + * in the Audio Interface Collection + */ +DECLARE_UAC_AC_HEADER_DESCRIPTOR(1); #define UAC_DT_AC_HEADER_LENGTH UAC_DT_AC_HEADER_SIZE(F_AUDIO_NUM_INTERFACES) /* 1 input terminal, 1 output terminal and 1 feature unit */ #define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH + UAC_DT_INPUT_TERMINAL_SIZE \ + UAC_DT_OUTPUT_TERMINAL_SIZE + UAC_DT_FEATURE_UNIT_SIZE(0)) /* B.3.2 Class-Specific AC Interface Descriptor */ -static struct uac1_ac_header_descriptor_2 ac_header_desc = { +static struct uac1_ac_header_descriptor_1 ac_header_desc = { .bLength = UAC_DT_AC_HEADER_LENGTH, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = UAC_HEADER, @@ -57,8 +61,8 @@ static struct uac1_ac_header_descriptor_2 ac_header_desc = { .wTotalLength = __constant_cpu_to_le16(UAC_DT_TOTAL_LENGTH), .bInCollection = F_AUDIO_NUM_INTERFACES, .baInterfaceNr = { - [0] = F_AUDIO_AC_INTERFACE, - [1] = F_AUDIO_AS_INTERFACE, + /* Interface number of the first AudioStream interface */ + [0] = 1, } }; From 9f56ce075dd83d6b69e26d4d4fcf49ec703119cf Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Tue, 23 Dec 2014 19:50:14 +0100 Subject: [PATCH 037/214] usb: gadget: cleanup on stack DECLARE_COMPLETIONs fixups for incorrect use of DECLARE_COMPLETION. see also commit 6e9a4738 ("completions: lockdep annotate on stack completions") patch is against 3.18.0 linux-next This was only code reviewed and compile tested Signed-off-by: Nicholas Mc Guire Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/fsl_qe_udc.c | 2 +- drivers/usb/gadget/udc/fsl_udc_core.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/udc/fsl_qe_udc.c b/drivers/usb/gadget/udc/fsl_qe_udc.c index 795c99c77697..e0822f1b6639 100644 --- a/drivers/usb/gadget/udc/fsl_qe_udc.c +++ b/drivers/usb/gadget/udc/fsl_qe_udc.c @@ -2630,7 +2630,7 @@ static int qe_udc_remove(struct platform_device *ofdev) struct qe_udc *udc = platform_get_drvdata(ofdev); struct qe_ep *ep; unsigned int size; - DECLARE_COMPLETION(done); + DECLARE_COMPLETION_ONSTACK(done); usb_del_gadget_udc(&udc->gadget); diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index 2df807403f22..c3830ad68edf 100644 --- a/drivers/usb/gadget/udc/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -2529,7 +2529,7 @@ static int __exit fsl_udc_remove(struct platform_device *pdev) struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev); - DECLARE_COMPLETION(done); + DECLARE_COMPLETION_ONSTACK(done); if (!udc_controller) return -ENODEV; From 48ac1e57665fa351b2bde534fd64cce2e1b3a4a1 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Wed, 5 Nov 2014 01:48:48 +0300 Subject: [PATCH 038/214] usb: renesas_usbhs: fix platform init error message There is a typo ("prove" instead of "probe") in the error message printed when the platform initialization fails. Replace that word with more fitting "init". Signed-off-by: Sergei Shtylyov Signed-off-by: Felipe Balbi --- drivers/usb/renesas_usbhs/common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index a706fc4fc13b..76d14f5d246a 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -615,7 +615,7 @@ static int usbhs_probe(struct platform_device *pdev) */ ret = usbhs_platform_call(priv, hardware_init, pdev); if (ret < 0) { - dev_err(&pdev->dev, "platform prove failed.\n"); + dev_err(&pdev->dev, "platform init failed.\n"); goto probe_end_mod_exit; } From 906641980c3ae4e4175268168897ebdc7314c73c Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Fri, 28 Nov 2014 14:50:46 +0100 Subject: [PATCH 039/214] usb: gadget: udc: net2280: Remove obsolete module param use_dma_chaining use_dma_chaining module parameter was designed to avoid creating one irq per package on a group of packages (with the help of the driver's flag no_interrupt). Unfortunately, when this parameter is enabled, the driver fails to work on both net2280 and 3380 chips. This patch removes this parameter, which was disabled by default. Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/net2280.c | 147 +++---------------------------- 1 file changed, 13 insertions(+), 134 deletions(-) diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index d6411e0a8e03..08c16ee092f2 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -12,11 +12,7 @@ * the Mass Storage, Serial, and Ethernet/RNDIS gadget drivers * as well as Gadget Zero and Gadgetfs. * - * DMA is enabled by default. Drivers using transfer queues might use - * DMA chaining to remove IRQ latencies between transfers. (Except when - * short OUT transfers happen.) Drivers can use the req->no_interrupt - * hint to completely eliminate some IRQs, if a later IRQ is guaranteed - * and DMA chaining is enabled. + * DMA is enabled by default. * * MSI is enabled by default. The legacy IRQ is used if MSI couldn't * be enabled. @@ -85,7 +81,6 @@ static const char *const ep_name[] = { }; /* use_dma -- general goodness, fewer interrupts, less cpu load (vs PIO) - * use_dma_chaining -- dma descriptor queueing gives even more irq reduction * * The net2280 DMA engines are not tightly integrated with their FIFOs; * not all cases are (yet) handled well in this driver or the silicon. @@ -93,12 +88,10 @@ static const char *const ep_name[] = { * These two parameters let you use PIO or more aggressive DMA. */ static bool use_dma = true; -static bool use_dma_chaining; static bool use_msi = true; /* "modprobe net2280 use_dma=n" etc */ module_param(use_dma, bool, 0444); -module_param(use_dma_chaining, bool, 0444); module_param(use_msi, bool, 0444); /* mode 0 == ep-{a,b,c,d} 1K fifo each @@ -202,15 +195,6 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) /* set speed-dependent max packet; may kick in high bandwidth */ set_max_speed(ep, max); - /* FIFO lines can't go to different packets. PIO is ok, so - * use it instead of troublesome (non-bulk) multi-packet DMA. - */ - if (ep->dma && (max % 4) != 0 && use_dma_chaining) { - ep_dbg(ep->dev, "%s, no dma for maxpacket %d\n", - ep->ep.name, ep->ep.maxpacket); - ep->dma = NULL; - } - /* set type, direction, address; reset fifo counters */ writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat); tmp = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK); @@ -747,8 +731,7 @@ static void fill_dma_desc(struct net2280_ep *ep, req->valid = valid; if (valid) dmacount |= BIT(VALID_BIT); - if (likely(!req->req.no_interrupt || !use_dma_chaining)) - dmacount |= BIT(DMA_DONE_INTERRUPT_ENABLE); + dmacount |= BIT(DMA_DONE_INTERRUPT_ENABLE); /* td->dmadesc = previously set by caller */ td->dmaaddr = cpu_to_le32 (req->req.dma); @@ -862,8 +845,7 @@ static void start_dma(struct net2280_ep *ep, struct net2280_request *req) req->td->dmadesc = cpu_to_le32 (ep->td_dma); fill_dma_desc(ep, req, 1); - if (!use_dma_chaining) - req->td->dmacount |= cpu_to_le32(BIT(END_OF_CHAIN)); + req->td->dmacount |= cpu_to_le32(BIT(END_OF_CHAIN)); start_queue(ep, tmp, req->td_dma); } @@ -1150,64 +1132,12 @@ static void scan_dma_completions(struct net2280_ep *ep) static void restart_dma(struct net2280_ep *ep) { struct net2280_request *req; - u32 dmactl = dmactl_default; if (ep->stopped) return; req = list_entry(ep->queue.next, struct net2280_request, queue); - if (!use_dma_chaining) { - start_dma(ep, req); - return; - } - - /* the 2280 will be processing the queue unless queue hiccups after - * the previous transfer: - * IN: wanted automagic zlp, head doesn't (or vice versa) - * DMA_FIFO_VALIDATE doesn't init from dma descriptors. - * OUT: was "usb-short", we must restart. - */ - if (ep->is_in && !req->valid) { - struct net2280_request *entry, *prev = NULL; - int reqmode, done = 0; - - ep_dbg(ep->dev, "%s dma hiccup td %p\n", ep->ep.name, req->td); - ep->in_fifo_validate = likely(req->req.zero || - (req->req.length % ep->ep.maxpacket) != 0); - if (ep->in_fifo_validate) - dmactl |= BIT(DMA_FIFO_VALIDATE); - list_for_each_entry(entry, &ep->queue, queue) { - __le32 dmacount; - - if (entry == req) - continue; - dmacount = entry->td->dmacount; - if (!done) { - reqmode = likely(entry->req.zero || - (entry->req.length % ep->ep.maxpacket)); - if (reqmode == ep->in_fifo_validate) { - entry->valid = 1; - dmacount |= valid_bit; - entry->td->dmacount = dmacount; - prev = entry; - continue; - } else { - /* force a hiccup */ - prev->td->dmacount |= dma_done_ie; - done = 1; - } - } - - /* walk the rest of the queue so unlinks behave */ - entry->valid = 0; - dmacount &= ~valid_bit; - entry->td->dmacount = dmacount; - prev = entry; - } - } - - writel(0, &ep->dma->dmactl); - start_queue(ep, dmactl, req->td_dma); + start_dma(ep, req); } static void abort_dma_228x(struct net2280_ep *ep) @@ -1306,25 +1236,6 @@ static int net2280_dequeue(struct usb_ep *_ep, struct usb_request *_req) done(ep, req, -ECONNRESET); } req = NULL; - - /* patch up hardware chaining data */ - } else if (ep->dma && use_dma_chaining) { - if (req->queue.prev == ep->queue.next) { - writel(le32_to_cpu(req->td->dmadesc), - &ep->dma->dmadesc); - if (req->td->dmacount & dma_done_ie) - writel(readl(&ep->dma->dmacount) | - le32_to_cpu(dma_done_ie), - &ep->dma->dmacount); - } else { - struct net2280_request *prev; - - prev = list_entry(req->queue.prev, - struct net2280_request, queue); - prev->td->dmadesc = req->td->dmadesc; - if (req->td->dmacount & dma_done_ie) - prev->td->dmacount |= dma_done_ie; - } } if (req) @@ -1609,9 +1520,7 @@ static ssize_t registers_show(struct device *_dev, "pci irqenb0 %02x irqenb1 %08x " "irqstat0 %04x irqstat1 %08x\n", driver_name, dev->chiprev, - use_dma - ? (use_dma_chaining ? "chaining" : "enabled") - : "disabled", + use_dma ? "enabled" : "disabled", readl(&dev->regs->devinit), readl(&dev->regs->fifoctl), s, @@ -3423,17 +3332,12 @@ static void handle_stat1_irqs(struct net2280 *dev, u32 stat) continue; } - /* chaining should stop on abort, short OUT from fifo, - * or (stat0 codepath) short OUT transfer. - */ - if (!use_dma_chaining) { - if (!(tmp & BIT(DMA_TRANSACTION_DONE_INTERRUPT))) { - ep_dbg(ep->dev, "%s no xact done? %08x\n", - ep->ep.name, tmp); - continue; - } - stop_dma(ep->dma); + if (!(tmp & BIT(DMA_TRANSACTION_DONE_INTERRUPT))) { + ep_dbg(ep->dev, "%s no xact done? %08x\n", + ep->ep.name, tmp); + continue; } + stop_dma(ep->dma); /* OUT transfers terminate when the data from the * host is in our memory. Process whatever's done. @@ -3448,30 +3352,9 @@ static void handle_stat1_irqs(struct net2280 *dev, u32 stat) scan_dma_completions(ep); /* disable dma on inactive queues; else maybe restart */ - if (list_empty(&ep->queue)) { - if (use_dma_chaining) - stop_dma(ep->dma); - } else { + if (!list_empty(&ep->queue)) { tmp = readl(&dma->dmactl); - if (!use_dma_chaining || (tmp & BIT(DMA_ENABLE)) == 0) - restart_dma(ep); - else if (ep->is_in && use_dma_chaining) { - struct net2280_request *req; - __le32 dmacount; - - /* the descriptor at the head of the chain - * may still have VALID_BIT clear; that's - * used to trigger changing DMA_FIFO_VALIDATE - * (affects automagic zlp writes). - */ - req = list_entry(ep->queue.next, - struct net2280_request, queue); - dmacount = req->td->dmacount; - dmacount &= cpu_to_le32(BIT(VALID_BIT) | - DMA_BYTE_COUNT_MASK); - if (dmacount && (dmacount & valid_bit) == 0) - restart_dma(ep); - } + restart_dma(ep); } ep->irqs++; } @@ -3581,9 +3464,6 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id) void __iomem *base = NULL; int retval, i; - if (!use_dma) - use_dma_chaining = 0; - /* alloc, and start init */ dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (dev == NULL) { @@ -3742,8 +3622,7 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id) ep_info(dev, "irq %d, pci mem %p, chip rev %04x\n", pdev->irq, base, dev->chiprev); ep_info(dev, "version: " DRIVER_VERSION "; dma %s %s\n", - use_dma ? (use_dma_chaining ? "chaining" : "enabled") - : "disabled", + use_dma ? "enabled" : "disabled", dev->enhanced_mode ? "enhanced mode" : "legacy mode"); retval = device_create_file(&pdev->dev, &dev_attr_registers); if (retval) From 7a74c48172d8fad6da0f9be5d353cf322305af07 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Fri, 28 Nov 2014 14:50:47 +0100 Subject: [PATCH 040/214] usb: gadget: udc: net2280: remove full_speed module parameter This patch removes the full_speed parameter used force full-speed operation. It was designed exclusively for testing purposes, and there is no reason to maintain this in a production kernel. Reverts: 2f0760774711c957c395b31131b848043af98edf Suggested-by: Alan Stern Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/net2280.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index 08c16ee092f2..27b94c69bef5 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -113,11 +113,6 @@ static bool enable_suspend; /* "modprobe net2280 enable_suspend=1" etc */ module_param(enable_suspend, bool, 0444); -/* force full-speed operation */ -static bool full_speed; -module_param(full_speed, bool, 0444); -MODULE_PARM_DESC(full_speed, "force full-speed mode -- for testing only!"); - #define DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out") static char *type_string(u8 bmAttributes) @@ -2291,11 +2286,7 @@ static int net2280_start(struct usb_gadget *_gadget, if (retval) goto err_func; - /* Enable force-full-speed testing mode, if desired */ - if (full_speed && (dev->quirks & PLX_LEGACY)) - writel(BIT(FORCE_FULL_SPEED_MODE), &dev->usb->xcvrdiag); - - /* ... then enable host detection and ep0; and we're ready + /* enable host detection and ep0; and we're ready * for set_configuration as well as eventual disconnect. */ net2280_led_active(dev, 1); @@ -2353,10 +2344,6 @@ static int net2280_stop(struct usb_gadget *_gadget) net2280_led_active(dev, 0); - /* Disable full-speed test mode */ - if (dev->quirks & PLX_LEGACY) - writel(0, &dev->usb->xcvrdiag); - device_remove_file(&dev->pdev->dev, &dev_attr_function); device_remove_file(&dev->pdev->dev, &dev_attr_queues); @@ -3655,9 +3642,6 @@ static void net2280_shutdown(struct pci_dev *pdev) /* disable the pullup so the host will think we're gone */ writel(0, &dev->usb->usbctl); - /* Disable full-speed test mode */ - if (dev->quirks & PLX_LEGACY) - writel(0, &dev->usb->xcvrdiag); } From 9c864c23424fa6a2e23b8e81faa6a100ab01481c Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Fri, 28 Nov 2014 14:50:48 +0100 Subject: [PATCH 041/214] usb: gadget: udc: net2280: Remove module parameter use_msi Parameter use_msi was used to enable msi irq on usb338x chips, it was enabled by default. There is no reason to prefer non-msi irq on usb338x, and it falls back to non msi on error. Therefore remove this option. Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/net2280.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index 27b94c69bef5..fc5c0a225814 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -88,11 +88,9 @@ static const char *const ep_name[] = { * These two parameters let you use PIO or more aggressive DMA. */ static bool use_dma = true; -static bool use_msi = true; /* "modprobe net2280 use_dma=n" etc */ module_param(use_dma, bool, 0444); -module_param(use_msi, bool, 0444); /* mode 0 == ep-{a,b,c,d} 1K fifo each * mode 1 == ep-{a,b} 2K fifo each, ep-{c,d} unavailable @@ -3426,7 +3424,7 @@ static void net2280_remove(struct pci_dev *pdev) } if (dev->got_irq) free_irq(pdev->irq, dev); - if (use_msi && dev->quirks & PLX_SUPERSPEED) + if (dev->quirks & PLX_SUPERSPEED) pci_disable_msi(pdev); if (dev->regs) iounmap(dev->regs); @@ -3549,7 +3547,7 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto done; } - if (use_msi && (dev->quirks & PLX_SUPERSPEED)) + if (dev->quirks & PLX_SUPERSPEED) if (pci_enable_msi(pdev)) ep_err(dev, "Failed to enable MSI mode\n"); From d588ff58c20d8503f789b9cbbed55e796ae6d04e Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Fri, 28 Nov 2014 14:50:49 +0100 Subject: [PATCH 042/214] usb: gadget: udc: net2280: Remove use_dma module parameter use_dma parameter was designed to enable the dma on the chip. It was enabled by default. It comes from the time when the dma was not reliable. Now it has been working ok in production. This patch removes this parameter. Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/net2280.c | 33 +++++--------------------------- 1 file changed, 5 insertions(+), 28 deletions(-) diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index fc5c0a225814..230b8d45f3e6 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -80,18 +80,6 @@ static const char *const ep_name[] = { "ep-e", "ep-f", "ep-g", "ep-h", }; -/* use_dma -- general goodness, fewer interrupts, less cpu load (vs PIO) - * - * The net2280 DMA engines are not tightly integrated with their FIFOs; - * not all cases are (yet) handled well in this driver or the silicon. - * Some gadget drivers work better with the dma support here than others. - * These two parameters let you use PIO or more aggressive DMA. - */ -static bool use_dma = true; - -/* "modprobe net2280 use_dma=n" etc */ -module_param(use_dma, bool, 0444); - /* mode 0 == ep-{a,b,c,d} 1K fifo each * mode 1 == ep-{a,b} 2K fifo each, ep-{c,d} unavailable * mode 2 == ep-a 2K fifo, ep-{b,c} 1K each, ep-d unavailable @@ -455,7 +443,7 @@ static int net2280_disable(struct usb_ep *_ep) /* synch memory views with the device */ (void)readl(&ep->cfg->ep_cfg); - if (use_dma && !ep->dma && ep->num >= 1 && ep->num <= 4) + if (!ep->dma && ep->num >= 1 && ep->num <= 4) ep->dma = &ep->dev->dma[ep->num - 1]; spin_unlock_irqrestore(&ep->dev->lock, flags); @@ -1508,12 +1496,11 @@ static ssize_t registers_show(struct device *_dev, /* Main Control Registers */ t = scnprintf(next, size, "%s version " DRIVER_VERSION - ", chiprev %04x, dma %s\n\n" + ", chiprev %04x\n\n" "devinit %03x fifoctl %08x gadget '%s'\n" "pci irqenb0 %02x irqenb1 %08x " "irqstat0 %04x irqstat1 %08x\n", driver_name, dev->chiprev, - use_dma ? "enabled" : "disabled", readl(&dev->regs->devinit), readl(&dev->regs->fifoctl), s, @@ -1995,10 +1982,6 @@ static void usb_reset(struct net2280 *dev) static void usb_reinit_228x(struct net2280 *dev) { u32 tmp; - int init_dma; - - /* use_dma changes are ignored till next device re-init */ - init_dma = use_dma; /* basic endpoint init */ for (tmp = 0; tmp < 7; tmp++) { @@ -2010,8 +1993,7 @@ static void usb_reinit_228x(struct net2280 *dev) if (tmp > 0 && tmp <= 4) { ep->fifo_size = 1024; - if (init_dma) - ep->dma = &dev->dma[tmp - 1]; + ep->dma = &dev->dma[tmp - 1]; } else ep->fifo_size = 64; ep->regs = &dev->epregs[tmp]; @@ -2035,7 +2017,6 @@ static void usb_reinit_228x(struct net2280 *dev) static void usb_reinit_338x(struct net2280 *dev) { - int init_dma; int i; u32 tmp, val; u32 fsmvalue; @@ -2043,9 +2024,6 @@ static void usb_reinit_338x(struct net2280 *dev) static const u32 ep_reg_addr[9] = { 0x00, 0xC0, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0xC0 }; - /* use_dma changes are ignored till next device re-init */ - init_dma = use_dma; - /* basic endpoint init */ for (i = 0; i < dev->n_ep; i++) { struct net2280_ep *ep = &dev->ep[i]; @@ -2054,7 +2032,7 @@ static void usb_reinit_338x(struct net2280 *dev) ep->dev = dev; ep->num = i; - if (i > 0 && i <= 4 && init_dma) + if (i > 0 && i <= 4) ep->dma = &dev->dma[i - 1]; if (dev->enhanced_mode) { @@ -3606,8 +3584,7 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id) ep_info(dev, "%s\n", driver_desc); ep_info(dev, "irq %d, pci mem %p, chip rev %04x\n", pdev->irq, base, dev->chiprev); - ep_info(dev, "version: " DRIVER_VERSION "; dma %s %s\n", - use_dma ? "enabled" : "disabled", + ep_info(dev, "version: " DRIVER_VERSION "; %s\n", dev->enhanced_mode ? "enhanced mode" : "legacy mode"); retval = device_create_file(&pdev->dev, &dev_attr_registers); if (retval) From cb52c698c6030c88733c87def5c359a09b76bc1e Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Fri, 28 Nov 2014 14:50:50 +0100 Subject: [PATCH 043/214] usb: gadget: udc: net2280: Remove dma_started field Remove dma_started field from net2280_ep structure, since it is not used by any function. Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/net2280.c | 4 ---- drivers/usb/gadget/udc/net2280.h | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index 230b8d45f3e6..03404626143d 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -834,16 +834,12 @@ static void start_dma(struct net2280_ep *ep, struct net2280_request *req) static inline void resume_dma(struct net2280_ep *ep) { writel(readl(&ep->dma->dmactl) | BIT(DMA_ENABLE), &ep->dma->dmactl); - - ep->dma_started = true; } static inline void ep_stop_dma(struct net2280_ep *ep) { writel(readl(&ep->dma->dmactl) & ~BIT(DMA_ENABLE), &ep->dma->dmactl); spin_stop_dma(ep->dma); - - ep->dma_started = false; } static inline void diff --git a/drivers/usb/gadget/udc/net2280.h b/drivers/usb/gadget/udc/net2280.h index 03f15242d794..4c53b8373dba 100644 --- a/drivers/usb/gadget/udc/net2280.h +++ b/drivers/usb/gadget/udc/net2280.h @@ -100,7 +100,7 @@ struct net2280_ep { dma_addr_t td_dma; /* of dummy */ struct net2280 *dev; unsigned long irqs; - unsigned is_halt:1, dma_started:1; + unsigned is_halt:1; /* analogous to a host-side qh */ struct list_head queue; From 61e72dc65a9a4937d351a4b67386a6bbf70a1146 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Fri, 28 Nov 2014 14:50:51 +0100 Subject: [PATCH 044/214] usb: gadget: udc: net2280: Remove restart_dma inline function definition restart_dma is not used before it is declaration. Therefore we can remove this definition. Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/net2280.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index 03404626143d..e8c36db93e9e 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -1043,8 +1043,6 @@ dma_done(struct net2280_ep *ep, struct net2280_request *req, u32 dmacount, done(ep, req, status); } -static void restart_dma(struct net2280_ep *ep); - static void scan_dma_completions(struct net2280_ep *ep) { /* only look at descriptors that were "naturally" retired, From cf8b1cdebab3c6fb01d37e62e14f9dd8cc99d09b Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Fri, 28 Nov 2014 14:50:52 +0100 Subject: [PATCH 045/214] usb: gadget: udc: net2280: Code cleanout remove ep_stdrsp function ep_stdrsp was only called by handle_stat0_irqs_superspeed and with always the same flags. Remove the function and replace the call by the code inside the function, since it is very simple once the dead code is removed. Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/net2280.c | 49 ++++++-------------------------- 1 file changed, 8 insertions(+), 41 deletions(-) diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index e8c36db93e9e..653a8a2597a8 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -2668,46 +2668,6 @@ static void ep_stall(struct net2280_ep *ep, int stall) } } -static void ep_stdrsp(struct net2280_ep *ep, int value, int wedged) -{ - /* set/clear, then synch memory views with the device */ - if (value) { - ep->stopped = 1; - if (ep->num == 0) - ep->dev->protocol_stall = 1; - else { - if (ep->dma) - ep_stop_dma(ep); - ep_stall(ep, true); - } - - if (wedged) - ep->wedged = 1; - } else { - ep->stopped = 0; - ep->wedged = 0; - - ep_stall(ep, false); - - /* Flush the queue */ - if (!list_empty(&ep->queue)) { - struct net2280_request *req = - list_entry(ep->queue.next, struct net2280_request, - queue); - if (ep->dma) - resume_dma(ep); - else { - if (ep->is_in) - write_fifo(ep, &req->req); - else { - if (read_fifo(ep, req)) - done(ep, req, 0); - } - } - } - } -} - static void handle_stat0_irqs_superspeed(struct net2280 *dev, struct net2280_ep *ep, struct usb_ctrlrequest r) { @@ -2864,7 +2824,14 @@ static void handle_stat0_irqs_superspeed(struct net2280 *dev, e = get_ep_by_addr(dev, w_index); if (!e || (w_value != USB_ENDPOINT_HALT)) goto do_stall3; - ep_stdrsp(e, true, false); + ep->stopped = 1; + if (ep->num == 0) + ep->dev->protocol_stall = 1; + else { + if (ep->dma) + ep_stop_dma(ep); + ep_stall(ep, true); + } allow_status_338x(ep); break; From 5d1b6840fd871b51765e7edee82876f760c46adf Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Fri, 28 Nov 2014 14:50:53 +0100 Subject: [PATCH 046/214] usb: gadget: udc: net2280: Remove field is_halt Field is_halt is never used by any function. Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/net2280.c | 6 +----- drivers/usb/gadget/udc/net2280.h | 1 - 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index 653a8a2597a8..48e8d82b5c4c 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -2643,7 +2643,6 @@ static void ep_stall(struct net2280_ep *ep, int stall) /* BIT(SET_NAK_PACKETS) | */ BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE), &ep->regs->ep_rsp); - ep->is_halt = 1; } else { if (dev->gadget.speed == USB_SPEED_SUPER) { /* @@ -2663,7 +2662,6 @@ static void ep_stall(struct net2280_ep *ep, int stall) writel(val, /* | BIT(CLEAR_NAK_PACKETS),*/ &ep->regs->ep_rsp); - ep->is_halt = 0; val = readl(&ep->regs->ep_rsp); } } @@ -2924,9 +2922,7 @@ static void handle_stat0_irqs(struct net2280 *dev, u32 stat) } ep->stopped = 0; dev->protocol_stall = 0; - if (dev->quirks & PLX_SUPERSPEED) - ep->is_halt = 0; - else{ + if (!(dev->quirks & PLX_SUPERSPEED)) { if (ep->dev->quirks & PLX_2280) tmp = BIT(FIFO_OVERFLOW) | BIT(FIFO_UNDERFLOW); diff --git a/drivers/usb/gadget/udc/net2280.h b/drivers/usb/gadget/udc/net2280.h index 4c53b8373dba..a16494af72a2 100644 --- a/drivers/usb/gadget/udc/net2280.h +++ b/drivers/usb/gadget/udc/net2280.h @@ -100,7 +100,6 @@ struct net2280_ep { dma_addr_t td_dma; /* of dummy */ struct net2280 *dev; unsigned long irqs; - unsigned is_halt:1; /* analogous to a host-side qh */ struct list_head queue; From e0cbb04627556a953c6fe8d5ba42b9dda68146af Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Fri, 28 Nov 2014 14:50:54 +0100 Subject: [PATCH 047/214] usb: gadget: udc: net2280: Remove function ep_stall irqs_superspeed calls ep_stall instead of set/clear_halt, due to a workaround for SS seqnum. Create a function with the workaround and call set/clear_halt instead. This way we can compare the code of super/normal speed and it is easier to follow the code. Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/net2280.c | 44 +++++++++++--------------------- 1 file changed, 15 insertions(+), 29 deletions(-) diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index 48e8d82b5c4c..5ba51328b262 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -2632,38 +2632,19 @@ restore_data_eps: return; } -static void ep_stall(struct net2280_ep *ep, int stall) +static void ep_clear_seqnum(struct net2280_ep *ep) { struct net2280 *dev = ep->dev; u32 val; static const u32 ep_pl[9] = { 0, 3, 4, 7, 8, 2, 5, 6, 9 }; - if (stall) { - writel(BIT(SET_ENDPOINT_HALT) | - /* BIT(SET_NAK_PACKETS) | */ - BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE), - &ep->regs->ep_rsp); - } else { - if (dev->gadget.speed == USB_SPEED_SUPER) { - /* - * Workaround for SS SeqNum not cleared via - * Endpoint Halt (Clear) bit. select endpoint - */ - val = readl(&dev->plregs->pl_ep_ctrl); - val = (val & ~0x1f) | ep_pl[ep->num]; - writel(val, &dev->plregs->pl_ep_ctrl); + val = readl(&dev->plregs->pl_ep_ctrl) & ~0x1f; + val |= ep_pl[ep->num]; + writel(val, &dev->plregs->pl_ep_ctrl); + val |= BIT(SEQUENCE_NUMBER_RESET); + writel(val, &dev->plregs->pl_ep_ctrl); - val |= BIT(SEQUENCE_NUMBER_RESET); - writel(val, &dev->plregs->pl_ep_ctrl); - } - val = readl(&ep->regs->ep_rsp); - val |= BIT(CLEAR_ENDPOINT_HALT) | - BIT(CLEAR_ENDPOINT_TOGGLE); - writel(val, - /* | BIT(CLEAR_NAK_PACKETS),*/ - &ep->regs->ep_rsp); - val = readl(&ep->regs->ep_rsp); - } + return; } static void handle_stat0_irqs_superspeed(struct net2280 *dev, @@ -2764,7 +2745,12 @@ static void handle_stat0_irqs_superspeed(struct net2280 *dev, if (w_value != USB_ENDPOINT_HALT) goto do_stall3; ep_vdbg(dev, "%s clear halt\n", e->ep.name); - ep_stall(e, false); + /* + * Workaround for SS SeqNum not cleared via + * Endpoint Halt (Clear) bit. select endpoint + */ + ep_clear_seqnum(e); + clear_halt(e); if (!list_empty(&e->queue) && e->td_dma) restart_dma(e); allow_status(ep); @@ -2828,7 +2814,7 @@ static void handle_stat0_irqs_superspeed(struct net2280 *dev, else { if (ep->dma) ep_stop_dma(ep); - ep_stall(ep, true); + set_halt(ep); } allow_status_338x(ep); break; @@ -2857,7 +2843,7 @@ do_stall3: r.bRequestType, r.bRequest, tmp); dev->protocol_stall = 1; /* TD 9.9 Halt Endpoint test. TD 9.22 Set feature test */ - ep_stall(ep, true); + set_halt(ep); } next_endpoints3: From e721c4575db77a543d0a28c56e95b84ee37a551c Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Fri, 28 Nov 2014 14:50:55 +0100 Subject: [PATCH 048/214] usb: gadget: udc: net2280: Merge abort_dma for 228x and 338x We can use the same function for both families of chips and also remove the ep_stop_dma() function. Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/net2280.c | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index 5ba51328b262..46b66b3fe8e1 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -836,12 +836,6 @@ static inline void resume_dma(struct net2280_ep *ep) writel(readl(&ep->dma->dmactl) | BIT(DMA_ENABLE), &ep->dma->dmactl); } -static inline void ep_stop_dma(struct net2280_ep *ep) -{ - writel(readl(&ep->dma->dmactl) & ~BIT(DMA_ENABLE), &ep->dma->dmactl); - spin_stop_dma(ep->dma); -} - static inline void queue_dma(struct net2280_ep *ep, struct net2280_request *req, int valid) { @@ -1115,7 +1109,7 @@ static void restart_dma(struct net2280_ep *ep) start_dma(ep, req); } -static void abort_dma_228x(struct net2280_ep *ep) +static void abort_dma(struct net2280_ep *ep) { /* abort the current transfer */ if (likely(!list_empty(&ep->queue))) { @@ -1127,19 +1121,6 @@ static void abort_dma_228x(struct net2280_ep *ep) scan_dma_completions(ep); } -static void abort_dma_338x(struct net2280_ep *ep) -{ - writel(BIT(DMA_ABORT), &ep->dma->dmastat); - spin_stop_dma(ep->dma); -} - -static void abort_dma(struct net2280_ep *ep) -{ - if (ep->dev->quirks & PLX_LEGACY) - return abort_dma_228x(ep); - return abort_dma_338x(ep); -} - /* dequeue ALL requests */ static void nuke(struct net2280_ep *ep) { @@ -2813,7 +2794,7 @@ static void handle_stat0_irqs_superspeed(struct net2280 *dev, ep->dev->protocol_stall = 1; else { if (ep->dma) - ep_stop_dma(ep); + abort_dma(ep); set_halt(ep); } allow_status_338x(ep); From 485f44d06b352a788d07e79a5744cc7420adc1e4 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Fri, 28 Nov 2014 14:50:56 +0100 Subject: [PATCH 049/214] usb: gadget: udc: net2280: Clean function net2280_queue Do not duplicate the code for the else branch of the condition. Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/net2280.c | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index 46b66b3fe8e1..ade46939b02a 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -937,24 +937,12 @@ net2280_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) _req->actual = 0; /* kickstart this i/o queue? */ - if (list_empty(&ep->queue) && !ep->stopped) { - /* DMA request while EP halted */ - if (ep->dma && - (readl(&ep->regs->ep_rsp) & BIT(CLEAR_ENDPOINT_HALT)) && - (dev->quirks & PLX_SUPERSPEED)) { - int valid = 1; - if (ep->is_in) { - int expect; - expect = likely(req->req.zero || - ((req->req.length % - ep->ep.maxpacket) != 0)); - if (expect != ep->in_fifo_validate) - valid = 0; - } - queue_dma(ep, req, valid); - } + if (list_empty(&ep->queue) && !ep->stopped && + !((dev->quirks & PLX_SUPERSPEED) && ep->dma && + (readl(&ep->regs->ep_rsp) & BIT(CLEAR_ENDPOINT_HALT)))) { + /* use DMA if the endpoint supports it, else pio */ - else if (ep->dma) + if (ep->dma) start_dma(ep, req); else { /* maybe there's no control data, just status ack */ From 5517525e05b5d21c26e2b729a7e977f7d69714af Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Fri, 28 Nov 2014 14:50:57 +0100 Subject: [PATCH 050/214] usb: gadget: udc: net2280: Improve patching of defect 7374 Once the defect 7374 is patched, there is no reason the keep reading the idx scratch register. Cache the content of the scratch idx register on device flag. Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/net2280.c | 149 +++++++++++++------------------ drivers/usb/gadget/udc/net2280.h | 3 +- 2 files changed, 66 insertions(+), 86 deletions(-) diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index ade46939b02a..d4255f95c92f 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -1765,76 +1765,73 @@ static void defect7374_disable_data_eps(struct net2280 *dev) static void defect7374_enable_data_eps_zero(struct net2280 *dev) { u32 tmp = 0, tmp_reg; - u32 fsmvalue, scratch; + u32 scratch; int i; unsigned char ep_sel; scratch = get_idx_reg(dev->regs, SCRATCH); - fsmvalue = scratch & (0xf << DEFECT7374_FSM_FIELD); + + WARN_ON((scratch & (0xf << DEFECT7374_FSM_FIELD)) + == DEFECT7374_FSM_SS_CONTROL_READ); + scratch &= ~(0xf << DEFECT7374_FSM_FIELD); - /*See if firmware needs to set up for workaround*/ - if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ) { - ep_warn(dev, "Operate Defect 7374 workaround soft this time"); - ep_warn(dev, "It will operate on cold-reboot and SS connect"); + ep_warn(dev, "Operate Defect 7374 workaround soft this time"); + ep_warn(dev, "It will operate on cold-reboot and SS connect"); - /*GPEPs:*/ - tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_DIRECTION) | - (2 << OUT_ENDPOINT_TYPE) | (2 << IN_ENDPOINT_TYPE) | - ((dev->enhanced_mode) ? - BIT(OUT_ENDPOINT_ENABLE) : BIT(ENDPOINT_ENABLE)) | - BIT(IN_ENDPOINT_ENABLE)); + /*GPEPs:*/ + tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_DIRECTION) | + (2 << OUT_ENDPOINT_TYPE) | (2 << IN_ENDPOINT_TYPE) | + ((dev->enhanced_mode) ? + BIT(OUT_ENDPOINT_ENABLE) : BIT(ENDPOINT_ENABLE)) | + BIT(IN_ENDPOINT_ENABLE)); - for (i = 1; i < 5; i++) - writel(tmp, &dev->ep[i].cfg->ep_cfg); + for (i = 1; i < 5; i++) + writel(tmp, &dev->ep[i].cfg->ep_cfg); - /* CSRIN, PCIIN, STATIN, RCIN*/ - tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_ENABLE)); - writel(tmp, &dev->dep[1].dep_cfg); - writel(tmp, &dev->dep[3].dep_cfg); - writel(tmp, &dev->dep[4].dep_cfg); - writel(tmp, &dev->dep[5].dep_cfg); + /* CSRIN, PCIIN, STATIN, RCIN*/ + tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_ENABLE)); + writel(tmp, &dev->dep[1].dep_cfg); + writel(tmp, &dev->dep[3].dep_cfg); + writel(tmp, &dev->dep[4].dep_cfg); + writel(tmp, &dev->dep[5].dep_cfg); - /*Implemented for development and debug. - * Can be refined/tuned later.*/ - for (ep_sel = 0; ep_sel <= 21; ep_sel++) { - /* Select an endpoint for subsequent operations: */ - tmp_reg = readl(&dev->plregs->pl_ep_ctrl); - writel(((tmp_reg & ~0x1f) | ep_sel), - &dev->plregs->pl_ep_ctrl); + /*Implemented for development and debug. + * Can be refined/tuned later.*/ + for (ep_sel = 0; ep_sel <= 21; ep_sel++) { + /* Select an endpoint for subsequent operations: */ + tmp_reg = readl(&dev->plregs->pl_ep_ctrl); + writel(((tmp_reg & ~0x1f) | ep_sel), + &dev->plregs->pl_ep_ctrl); - if (ep_sel == 1) { - tmp = - (readl(&dev->plregs->pl_ep_ctrl) | - BIT(CLEAR_ACK_ERROR_CODE) | 0); - writel(tmp, &dev->plregs->pl_ep_ctrl); - continue; - } - - if (ep_sel == 0 || (ep_sel > 9 && ep_sel < 14) || - ep_sel == 18 || ep_sel == 20) - continue; - - tmp = (readl(&dev->plregs->pl_ep_cfg_4) | - BIT(NON_CTRL_IN_TOLERATE_BAD_DIR) | 0); - writel(tmp, &dev->plregs->pl_ep_cfg_4); - - tmp = readl(&dev->plregs->pl_ep_ctrl) & - ~BIT(EP_INITIALIZED); + if (ep_sel == 1) { + tmp = + (readl(&dev->plregs->pl_ep_ctrl) | + BIT(CLEAR_ACK_ERROR_CODE) | 0); writel(tmp, &dev->plregs->pl_ep_ctrl); - + continue; } - /* Set FSM to focus on the first Control Read: - * - Tip: Connection speed is known upon the first - * setup request.*/ - scratch |= DEFECT7374_FSM_WAITING_FOR_CONTROL_READ; - set_idx_reg(dev->regs, SCRATCH, scratch); + if (ep_sel == 0 || (ep_sel > 9 && ep_sel < 14) || + ep_sel == 18 || ep_sel == 20) + continue; + + tmp = (readl(&dev->plregs->pl_ep_cfg_4) | + BIT(NON_CTRL_IN_TOLERATE_BAD_DIR) | 0); + writel(tmp, &dev->plregs->pl_ep_cfg_4); + + tmp = readl(&dev->plregs->pl_ep_ctrl) & + ~BIT(EP_INITIALIZED); + writel(tmp, &dev->plregs->pl_ep_ctrl); - } else{ - ep_warn(dev, "Defect 7374 workaround soft will NOT operate"); - ep_warn(dev, "It will operate on cold-reboot and SS connect"); } + + /* Set FSM to focus on the first Control Read: + * - Tip: Connection speed is known upon the first + * setup request.*/ + scratch |= DEFECT7374_FSM_WAITING_FOR_CONTROL_READ; + set_idx_reg(dev->regs, SCRATCH, scratch); + } /* keeping it simple: @@ -1885,21 +1882,13 @@ static void usb_reset_228x(struct net2280 *dev) static void usb_reset_338x(struct net2280 *dev) { u32 tmp; - u32 fsmvalue; dev->gadget.speed = USB_SPEED_UNKNOWN; (void)readl(&dev->usb->usbctl); net2280_led_init(dev); - fsmvalue = get_idx_reg(dev->regs, SCRATCH) & - (0xf << DEFECT7374_FSM_FIELD); - - /* See if firmware needs to set up for workaround: */ - if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ) { - ep_info(dev, "%s: Defect 7374 FsmValue 0x%08x\n", __func__, - fsmvalue); - } else { + if (dev->bug7734_patched) { /* disable automatic responses, and irqs */ writel(0, &dev->usb->stdrsp); writel(0, &dev->regs->pciirqenb0); @@ -1916,7 +1905,7 @@ static void usb_reset_338x(struct net2280 *dev) writel(~0, &dev->regs->irqstat0), writel(~0, &dev->regs->irqstat1); - if (fsmvalue == DEFECT7374_FSM_SS_CONTROL_READ) { + if (dev->bug7734_patched) { /* reset, and enable pci */ tmp = readl(&dev->regs->devinit) | BIT(PCI_ENABLE) | @@ -1982,7 +1971,6 @@ static void usb_reinit_338x(struct net2280 *dev) { int i; u32 tmp, val; - u32 fsmvalue; static const u32 ne[9] = { 0, 1, 2, 3, 4, 1, 2, 3, 4 }; static const u32 ep_reg_addr[9] = { 0x00, 0xC0, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0xC0 }; @@ -2020,14 +2008,7 @@ static void usb_reinit_338x(struct net2280 *dev) dev->ep[0].stopped = 0; /* Link layer set up */ - fsmvalue = get_idx_reg(dev->regs, SCRATCH) & - (0xf << DEFECT7374_FSM_FIELD); - - /* See if driver needs to set up for workaround: */ - if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ) - ep_info(dev, "%s: Defect 7374 FsmValue %08x\n", - __func__, fsmvalue); - else { + if (dev->bug7734_patched) { tmp = readl(&dev->usb_ext->usbctl2) & ~(BIT(U1_ENABLE) | BIT(U2_ENABLE) | BIT(LTM_ENABLE)); writel(tmp, &dev->usb_ext->usbctl2); @@ -2134,15 +2115,8 @@ static void ep0_start_228x(struct net2280 *dev) static void ep0_start_338x(struct net2280 *dev) { - u32 fsmvalue; - fsmvalue = get_idx_reg(dev->regs, SCRATCH) & - (0xf << DEFECT7374_FSM_FIELD); - - if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ) - ep_info(dev, "%s: Defect 7374 FsmValue %08x\n", __func__, - fsmvalue); - else + if (dev->bug7734_patched) writel(BIT(CLEAR_NAK_OUT_PACKETS_MODE) | BIT(SET_EP_HIDE_STATUS_PHASE), &dev->epregs[0].ep_rsp); @@ -2230,7 +2204,7 @@ static int net2280_start(struct usb_gadget *_gadget, */ net2280_led_active(dev, 1); - if (dev->quirks & PLX_SUPERSPEED) + if ((dev->quirks & PLX_SUPERSPEED) && !dev->bug7734_patched) defect7374_enable_data_eps_zero(dev); ep0_start(dev); @@ -2552,6 +2526,7 @@ static void defect7374_workaround(struct net2280 *dev, struct usb_ctrlrequest r) * run after the next USB connection. */ scratch |= DEFECT7374_FSM_NON_SS_CONTROL_READ; + dev->bug7734_patched = 1; goto restore_data_eps; } @@ -2565,6 +2540,7 @@ static void defect7374_workaround(struct net2280 *dev, struct usb_ctrlrequest r) if ((state >= (ACK_GOOD_NORMAL << STATE)) && (state <= (ACK_GOOD_MORE_ACKS_TO_COME << STATE))) { scratch |= DEFECT7374_FSM_SS_CONTROL_READ; + dev->bug7734_patched = 1; break; } @@ -2904,7 +2880,7 @@ static void handle_stat0_irqs(struct net2280 *dev, u32 stat) cpu_to_le32s(&u.raw[0]); cpu_to_le32s(&u.raw[1]); - if (dev->quirks & PLX_SUPERSPEED) + if ((dev->quirks & PLX_SUPERSPEED) && !dev->bug7734_patched) defect7374_workaround(dev, u.r); tmp = 0; @@ -3418,9 +3394,12 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id) fsmvalue = get_idx_reg(dev->regs, SCRATCH) & (0xf << DEFECT7374_FSM_FIELD); /* See if firmware needs to set up for workaround: */ - if (fsmvalue == DEFECT7374_FSM_SS_CONTROL_READ) + if (fsmvalue == DEFECT7374_FSM_SS_CONTROL_READ) { + dev->bug7734_patched = 1; writel(0, &dev->usb->usbctl); - } else{ + } else + dev->bug7734_patched = 0; + } else { dev->enhanced_mode = 0; dev->n_ep = 7; /* put into initial config, link up all endpoints */ diff --git a/drivers/usb/gadget/udc/net2280.h b/drivers/usb/gadget/udc/net2280.h index a16494af72a2..c7c79812041e 100644 --- a/drivers/usb/gadget/udc/net2280.h +++ b/drivers/usb/gadget/udc/net2280.h @@ -165,7 +165,8 @@ struct net2280 { ltm_enable:1, wakeup_enable:1, selfpowered:1, - addressed_state:1; + addressed_state:1, + bug7734_patched:1; u16 chiprev; int enhanced_mode; int n_ep; From 5153c219e77985be2fb861814184729bb40b0987 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Fri, 28 Nov 2014 14:50:58 +0100 Subject: [PATCH 051/214] usb: gadget: udc: net2280: Remove function resume_dma Function resume_dma is not used, remove it. The reason the compiler did not catch this dead code is the inline modifier. Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/net2280.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index d4255f95c92f..cc7618146e79 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -831,11 +831,6 @@ static void start_dma(struct net2280_ep *ep, struct net2280_request *req) start_queue(ep, tmp, req->td_dma); } -static inline void resume_dma(struct net2280_ep *ep) -{ - writel(readl(&ep->dma->dmactl) | BIT(DMA_ENABLE), &ep->dma->dmactl); -} - static inline void queue_dma(struct net2280_ep *ep, struct net2280_request *req, int valid) { From 6897d4b2bafe189863b2fe448c4afde37844cdbf Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Fri, 28 Nov 2014 14:50:59 +0100 Subject: [PATCH 052/214] usb: gadget: udc: net2280: Declare allow_status_338x as inline The function is very simple, does not declare any variable and it is called in the irq path. The counterpart for net228x is already declared as inline. Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/net2280.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/net2280.h b/drivers/usb/gadget/udc/net2280.h index c7c79812041e..b31deb03ca38 100644 --- a/drivers/usb/gadget/udc/net2280.h +++ b/drivers/usb/gadget/udc/net2280.h @@ -125,7 +125,7 @@ static inline void allow_status(struct net2280_ep *ep) ep->stopped = 1; } -static void allow_status_338x(struct net2280_ep *ep) +static inline void allow_status_338x(struct net2280_ep *ep) { /* * Control Status Phase Handshake was set by the chip when the setup From 43780aaa1c23c572ef57267d43d520affa7b4723 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Fri, 28 Nov 2014 14:51:00 +0100 Subject: [PATCH 053/214] usb: gadget: udc: net2280: Simplify scan_dma_completions After fix superspeed dma_done was applied we can simplify the code by removing the duplicated dma_done and letting the function check if there are more completed dma transactions. Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/net2280.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index cc7618146e79..a978e6e69eed 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -1051,9 +1051,8 @@ static void scan_dma_completions(struct net2280_ep *ep) dma_done(ep, req, tmp, 0); break; } else if (!ep->is_in && - (req->req.length % ep->ep.maxpacket) != 0) { - if (ep->dev->quirks & PLX_SUPERSPEED) - return dma_done(ep, req, tmp, 0); + (req->req.length % ep->ep.maxpacket) && + !(ep->dev->quirks & PLX_SUPERSPEED)) { tmp = readl(&ep->regs->ep_stat); /* AVOID TROUBLE HERE by not issuing short reads from From d82f3db266f3be7f85de33905dba2a6caccb97f0 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Fri, 28 Nov 2014 14:51:01 +0100 Subject: [PATCH 054/214] usb: gadget: udc: net2280: Move ASSERT_OUT_NAKING into out_flush ASSERT_OUT_NAKING was only called by out_flush and was hidden behind a ifdef. This patch moves the inline function into out_flush and remove the ifdef. The user can decide to print the debug message or not via dynamic printk Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/net2280.c | 10 ++++++++-- drivers/usb/gadget/udc/net2280.h | 17 ----------------- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index a978e6e69eed..b1c253e6916e 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -575,9 +575,15 @@ static void out_flush(struct net2280_ep *ep) u32 __iomem *statp; u32 tmp; - ASSERT_OUT_NAKING(ep); - statp = &ep->regs->ep_stat; + + tmp = readl(statp); + if (tmp & BIT(NAK_OUT_PACKETS)) { + ep_dbg(ep->dev, "%s %s %08x !NAK\n", + ep->ep.name, __func__, tmp); + writel(BIT(SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp); + } + writel(BIT(DATA_OUT_PING_TOKEN_INTERRUPT) | BIT(DATA_PACKET_RECEIVED_INTERRUPT), statp); diff --git a/drivers/usb/gadget/udc/net2280.h b/drivers/usb/gadget/udc/net2280.h index b31deb03ca38..a307dce2e184 100644 --- a/drivers/usb/gadget/udc/net2280.h +++ b/drivers/usb/gadget/udc/net2280.h @@ -356,23 +356,6 @@ static inline void start_out_naking(struct net2280_ep *ep) readl(&ep->regs->ep_rsp); } -#ifdef DEBUG -static inline void assert_out_naking(struct net2280_ep *ep, const char *where) -{ - u32 tmp = readl(&ep->regs->ep_stat); - - if ((tmp & BIT(NAK_OUT_PACKETS)) == 0) { - ep_dbg(ep->dev, "%s %s %08x !NAK\n", - ep->ep.name, where, tmp); - writel(BIT(SET_NAK_OUT_PACKETS), - &ep->regs->ep_rsp); - } -} -#define ASSERT_OUT_NAKING(ep) assert_out_naking(ep, __func__) -#else -#define ASSERT_OUT_NAKING(ep) do {} while (0) -#endif - static inline void stop_out_naking(struct net2280_ep *ep) { u32 tmp; From cb442ee1592d26815156201afa986a4ec3fb4bbf Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Fri, 28 Nov 2014 14:51:02 +0100 Subject: [PATCH 055/214] usb: gadget: udc: net2280: Re-enable dynamic debug messages Some debug messages were not build due to unconditional #if 0. These messages are very useful for debugging and the user can enable them on demand via dynamic debug. If they are not enabled the performance is not affected. Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/net2280.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index b1c253e6916e..d2fabdc0deaa 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -927,10 +927,8 @@ net2280_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) return ret; } -#if 0 ep_vdbg(dev, "%s queue req %p, len %d buf %p\n", _ep->name, _req, _req->length, _req->buf); -#endif spin_lock_irqsave(&dev->lock, flags); @@ -2287,10 +2285,10 @@ static void handle_ep_small(struct net2280_ep *ep) /* ack all, and handle what we care about */ t = readl(&ep->regs->ep_stat); ep->irqs++; -#if 0 + ep_vdbg(ep->dev, "%s ack ep_stat %08x, req %p\n", ep->ep.name, t, req ? &req->req : 0); -#endif + if (!ep->is_in || (ep->dev->quirks & PLX_2280)) writel(t & ~BIT(NAK_OUT_PACKETS), &ep->regs->ep_stat); else From 6b448af46e2a2fc9563fd86fb3b7cf06fcf2dd47 Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Tue, 16 Dec 2014 11:51:44 +0100 Subject: [PATCH 056/214] drivers: usb: dwc2: remove 'force' parameter from kill_all_requests() This patch fixes in simpler way the bug described in [1] and [2]. It looks like DWC2 is the only UDC driver that doesn't force usb requests to complete in ep_disable() function. This causes described problem, because we have no guarantee that all requests will be completed before unbind of usb function. To fix this problem we force all requests of disabled endpoint to complete. Also currently running request is not handled. This allowed to simplify code of kill_all_requests() function, because 'force' parameter is always set to true, so we don't need it anymore. In s3c_hsotg_rx_data() we change function used to print message when active request is NULL from dev_warn() to dev_dbg(), because such situation is harmless for driver and now it can take place during normal endpoint disabling. [1] https://lkml.org/lkml/2014/12/9/283 [2] https://lkml.org/lkml/2014/12/12/360 Acked-by: Paul Zimmerman Signed-off-by: Robert Baldyga Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index a461406fe8f8..1a8e0a6d2ef8 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -1305,7 +1305,7 @@ static void s3c_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size) u32 epctl = readl(hsotg->regs + DOEPCTL(ep_idx)); int ptr; - dev_warn(hsotg->dev, + dev_dbg(hsotg->dev, "%s: FIFO %d bytes on ep%d but no req (DXEPCTl=0x%08x)\n", __func__, size, ep_idx, epctl); @@ -1988,30 +1988,23 @@ static void s3c_hsotg_irq_enumdone(struct dwc2_hsotg *hsotg) * @hsotg: The device state. * @ep: The endpoint the requests may be on. * @result: The result code to use. - * @force: Force removal of any current requests * * Go through the requests on the given endpoint and mark them * completed with the given result code. */ static void kill_all_requests(struct dwc2_hsotg *hsotg, struct s3c_hsotg_ep *ep, - int result, bool force) + int result) { struct s3c_hsotg_req *req, *treq; unsigned size; - list_for_each_entry_safe(req, treq, &ep->queue, queue) { - /* - * currently, we can't do much about an already - * running request on an in endpoint - */ - - if (ep->req == req && ep->dir_in && !force) - continue; + ep->req = NULL; + list_for_each_entry_safe(req, treq, &ep->queue, queue) s3c_hsotg_complete_request(hsotg, ep, req, result); - } + if (!hsotg->dedicated_fifos) return; size = (readl(hsotg->regs + DTXFSTS(ep->index)) & 0xffff) * 4; @@ -2036,7 +2029,7 @@ void s3c_hsotg_disconnect(struct dwc2_hsotg *hsotg) hsotg->connected = 0; for (ep = 0; ep < hsotg->num_of_eps; ep++) - kill_all_requests(hsotg, &hsotg->eps[ep], -ESHUTDOWN, true); + kill_all_requests(hsotg, &hsotg->eps[ep], -ESHUTDOWN); call_gadget(hsotg, disconnect); } @@ -2334,7 +2327,7 @@ irq_retry: msecs_to_jiffies(200))) { kill_all_requests(hsotg, &hsotg->eps[0], - -ECONNRESET, true); + -ECONNRESET); s3c_hsotg_core_init_disconnected(hsotg); s3c_hsotg_core_connect(hsotg); @@ -2588,7 +2581,7 @@ static int s3c_hsotg_ep_disable(struct usb_ep *ep) spin_lock_irqsave(&hsotg->lock, flags); /* terminate all requests with shutdown */ - kill_all_requests(hsotg, hs_ep, -ESHUTDOWN, false); + kill_all_requests(hsotg, hs_ep, -ESHUTDOWN); hsotg->fifo_map &= ~(1<fifo_index); hs_ep->fifo_index = 0; From fdf80e78acbaa6a50531490b3a6ba027a52a69ad Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 24 Dec 2014 13:48:02 +0800 Subject: [PATCH 057/214] usb: phy: phy-mxs-usb: add power down and disable wakeup for .shutdown When we shut down the PHY, we need to power down all PHY's functions as well as disable wakeup, it is the opposite operation we do at .init. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-mxs-usb.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index b9589f663683..eaf94b036571 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -293,6 +293,17 @@ static int mxs_phy_init(struct usb_phy *phy) static void mxs_phy_shutdown(struct usb_phy *phy) { struct mxs_phy *mxs_phy = to_mxs_phy(phy); + u32 value = BM_USBPHY_CTRL_ENVBUSCHG_WKUP | + BM_USBPHY_CTRL_ENDPDMCHG_WKUP | + BM_USBPHY_CTRL_ENIDCHG_WKUP | + BM_USBPHY_CTRL_ENAUTOSET_USBCLKS | + BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE | + BM_USBPHY_CTRL_ENAUTOCLR_PHY_PWD | + BM_USBPHY_CTRL_ENAUTOCLR_CLKGATE | + BM_USBPHY_CTRL_ENAUTO_PWRON_PLL; + + writel(value, phy->io_priv + HW_USBPHY_CTRL_CLR); + writel(0xffffffff, phy->io_priv + HW_USBPHY_PWD); writel(BM_USBPHY_CTRL_CLKGATE, phy->io_priv + HW_USBPHY_CTRL_SET); From f78c0957674bda6687992058554c07683418ebc2 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 24 Dec 2014 13:48:03 +0800 Subject: [PATCH 058/214] usb: phy: phy-mxs-usb: do not depend on speed for disconnect notifier For some user cases, like plug out and replug in usb device during the system suspend, the speed negotiation will be error due to host doesn't know the device's disconnection, and it still hopes the high speed device, but the device backs to "powered" state which its high speed termination is not enabled, the usb core calls the PHY's disconnect notifier with "full speed", it will NOT take effect at all. If the usb core calls disconnect notifer, the port change must happen, so it is safe to disable high speed disconenct detector, since connect notifier will be called soon if the device is still connected on the port, and we will enable high speed disconnect detector at that time. Acked-by: Li Jun Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-mxs-usb.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index eaf94b036571..58cae78b12a4 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -370,7 +370,9 @@ static int mxs_phy_on_disconnect(struct usb_phy *phy, dev_dbg(phy->dev, "%s device has disconnected\n", (speed == USB_SPEED_HIGH) ? "HS" : "FS/LS"); - if (speed == USB_SPEED_HIGH) + /* Sometimes, the speed is not high speed when the error occurs */ + if (readl(phy->io_priv + HW_USBPHY_CTRL) & + BM_USBPHY_CTRL_ENHOSTDISCONDETECT) writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, phy->io_priv + HW_USBPHY_CTRL_CLR); From 07673204777abe893092cc6e42ea04e5c0ac8cb7 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Tue, 30 Dec 2014 14:02:28 +0800 Subject: [PATCH 059/214] usb: phy: change some comments - Delete the OTG stuffs - .set_suspend is for controller, not for A-device or B-device. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- include/linux/usb/phy.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/usb/phy.h b/include/linux/usb/phy.h index f499c23e6342..bc91b5d380fd 100644 --- a/include/linux/usb/phy.h +++ b/include/linux/usb/phy.h @@ -1,5 +1,5 @@ -/* USB OTG (On The Go) defines */ /* + * USB PHY defines * * These APIs may be used between USB controllers. USB device drivers * (for either host or peripheral roles) don't use these calls; they @@ -106,7 +106,7 @@ struct usb_phy { int (*set_power)(struct usb_phy *x, unsigned mA); - /* for non-OTG B devices: set transceiver into suspend mode */ + /* Set transceiver into suspend mode */ int (*set_suspend)(struct usb_phy *x, int suspend); From 6f15e2dc710cc604cc81f0ff596d83c332b5cbe8 Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Thu, 1 Jan 2015 20:15:34 +0100 Subject: [PATCH 060/214] usb: phy: phy-fsl-usb: Remove some unused functions Removes some functions that are not used anywhere: fsl_otg_tick_timer() view_ulpi() This was partially found by using a static code analysis program called cppcheck. Signed-off-by: Rickard Strandqvist Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-fsl-usb.c | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c index ab38aa32a6c1..94eb2923afed 100644 --- a/drivers/usb/phy/phy-fsl-usb.c +++ b/drivers/usb/phy/phy-fsl-usb.c @@ -107,19 +107,6 @@ static void (*_fsl_writel)(u32 v, unsigned __iomem *p); #define fsl_writel(val, addr) writel(val, addr) #endif /* CONFIG_PPC32 */ -/* Routines to access transceiver ULPI registers */ -u8 view_ulpi(u8 addr) -{ - u32 temp; - - temp = 0x40000000 | (addr << 16); - fsl_writel(temp, &usb_dr_regs->ulpiview); - udelay(1000); - while (temp & 0x40) - temp = fsl_readl(&usb_dr_regs->ulpiview); - return (le32_to_cpu(temp) & 0x0000ff00) >> 8; -} - int write_ulpi(u8 addr, u8 data) { u32 temp; @@ -460,28 +447,6 @@ static void fsl_otg_fsm_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer t) fsl_otg_del_timer(fsm, timer); } -/* - * Reduce timer count by 1, and find timeout conditions. - * Called by fsl_otg 1ms timer interrupt - */ -int fsl_otg_tick_timer(void) -{ - struct fsl_otg_timer *tmp_timer, *del_tmp; - int expired = 0; - - list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) { - tmp_timer->count--; - /* check if timer expires */ - if (!tmp_timer->count) { - list_del(&tmp_timer->list); - tmp_timer->function(tmp_timer->data); - expired = 1; - } - } - - return expired; -} - /* Reset controller, not reset the bus */ void otg_reset_controller(void) { From 3280e67536f8a4d4adf8dcde10cb4c4b577c34f4 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Tue, 6 Jan 2015 14:46:58 +0100 Subject: [PATCH 061/214] usb: atmel_usba_udc: Rework at91sam9rl errata handling at91sam9rl SoC has an erratum forcing us to toggle the BIAS on USB suspend/resume events. This specific handling is only activated when CONFIG_ARCH_AT91SAM9RL is set and this option is only set when building a non-DT kernel, which is problematic since non-DT support for at91sam9rl SoC has been removed. Rework the toggle_bias implementation to attach it to the "at91sam9rl-udc" compatible string. Add new compatible strings to avoid executing at91sam9rl erratum handling on other SoCs. Acked-by: Alexandre Belloni Signed-off-by: Boris Brezillon Signed-off-by: Felipe Balbi --- .../devicetree/bindings/usb/atmel-usb.txt | 5 +- drivers/usb/gadget/udc/atmel_usba_udc.c | 78 ++++++++++--------- drivers/usb/gadget/udc/atmel_usba_udc.h | 5 ++ 3 files changed, 52 insertions(+), 36 deletions(-) diff --git a/Documentation/devicetree/bindings/usb/atmel-usb.txt b/Documentation/devicetree/bindings/usb/atmel-usb.txt index bc2222ca3f2a..38fee0f66c12 100644 --- a/Documentation/devicetree/bindings/usb/atmel-usb.txt +++ b/Documentation/devicetree/bindings/usb/atmel-usb.txt @@ -51,7 +51,10 @@ usb1: gadget@fffa4000 { Atmel High-Speed USB device controller Required properties: - - compatible: Should be "atmel,at91sam9rl-udc" + - compatible: Should be one of the following + "at91sam9rl-udc" + "at91sam9g45-udc" + "sama5d3-udc" - reg: Address and length of the register set for the device - interrupts: Should contain usba interrupt - ep childnode: To specify the number of endpoints and their properties. diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index ce882371786b..36fd34b77387 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -8,6 +8,7 @@ * published by the Free Software Foundation. */ #include +#include #include #include #include @@ -324,28 +325,12 @@ static int vbus_is_present(struct usba_udc *udc) return 1; } -#if defined(CONFIG_ARCH_AT91SAM9RL) - -#include - -static void toggle_bias(int is_on) +static void toggle_bias(struct usba_udc *udc, int is_on) { - unsigned int uckr = at91_pmc_read(AT91_CKGR_UCKR); - - if (is_on) - at91_pmc_write(AT91_CKGR_UCKR, uckr | AT91_PMC_BIASEN); - else - at91_pmc_write(AT91_CKGR_UCKR, uckr & ~(AT91_PMC_BIASEN)); + if (udc->errata && udc->errata->toggle_bias) + udc->errata->toggle_bias(udc, is_on); } -#else - -static void toggle_bias(int is_on) -{ -} - -#endif /* CONFIG_ARCH_AT91SAM9RL */ - static void next_fifo_transaction(struct usba_ep *ep, struct usba_request *req) { unsigned int transaction_len; @@ -1620,7 +1605,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) DBG(DBG_INT, "irq, status=%#08x\n", status); if (status & USBA_DET_SUSPEND) { - toggle_bias(0); + toggle_bias(udc, 0); usba_writel(udc, INT_CLR, USBA_DET_SUSPEND); DBG(DBG_BUS, "Suspend detected\n"); if (udc->gadget.speed != USB_SPEED_UNKNOWN @@ -1632,7 +1617,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) } if (status & USBA_WAKE_UP) { - toggle_bias(1); + toggle_bias(udc, 1); usba_writel(udc, INT_CLR, USBA_WAKE_UP); DBG(DBG_BUS, "Wake Up CPU detected\n"); } @@ -1736,13 +1721,13 @@ static irqreturn_t usba_vbus_irq(int irq, void *devid) vbus = vbus_is_present(udc); if (vbus != udc->vbus_prev) { if (vbus) { - toggle_bias(1); + toggle_bias(udc, 1); usba_writel(udc, CTRL, USBA_ENABLE_MASK); usba_writel(udc, INT_ENB, USBA_END_OF_RESET); } else { udc->gadget.speed = USB_SPEED_UNKNOWN; reset_all_endpoints(udc); - toggle_bias(0); + toggle_bias(udc, 0); usba_writel(udc, CTRL, USBA_DISABLE_MASK); if (udc->driver->disconnect) { spin_unlock(&udc->lock); @@ -1788,7 +1773,7 @@ static int atmel_usba_start(struct usb_gadget *gadget, /* If Vbus is present, enable the controller and wait for reset */ spin_lock_irqsave(&udc->lock, flags); if (vbus_is_present(udc) && udc->vbus_prev == 0) { - toggle_bias(1); + toggle_bias(udc, 1); usba_writel(udc, CTRL, USBA_ENABLE_MASK); usba_writel(udc, INT_ENB, USBA_END_OF_RESET); } @@ -1811,7 +1796,7 @@ static int atmel_usba_stop(struct usb_gadget *gadget) spin_unlock_irqrestore(&udc->lock, flags); /* This will also disable the DP pullup */ - toggle_bias(0); + toggle_bias(udc, 0); usba_writel(udc, CTRL, USBA_DISABLE_MASK); clk_disable_unprepare(udc->hclk); @@ -1823,6 +1808,29 @@ static int atmel_usba_stop(struct usb_gadget *gadget) } #ifdef CONFIG_OF +static void at91sam9rl_toggle_bias(struct usba_udc *udc, int is_on) +{ + unsigned int uckr = at91_pmc_read(AT91_CKGR_UCKR); + + if (is_on) + at91_pmc_write(AT91_CKGR_UCKR, uckr | AT91_PMC_BIASEN); + else + at91_pmc_write(AT91_CKGR_UCKR, uckr & ~(AT91_PMC_BIASEN)); +} + +static const struct usba_udc_errata at91sam9rl_errata = { + .toggle_bias = at91sam9rl_toggle_bias, +}; + +static const struct of_device_id atmel_udc_dt_ids[] = { + { .compatible = "atmel,at91sam9rl-udc", .data = &at91sam9rl_errata }, + { .compatible = "atmel,at91sam9g45-udc" }, + { .compatible = "atmel,sama5d3-udc" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, atmel_udc_dt_ids); + static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev, struct usba_udc *udc) { @@ -1830,10 +1838,17 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev, const char *name; enum of_gpio_flags flags; struct device_node *np = pdev->dev.of_node; + const struct of_device_id *match; struct device_node *pp; int i, ret; struct usba_ep *eps, *ep; + match = of_match_node(atmel_udc_dt_ids, np); + if (!match) + return ERR_PTR(-EINVAL); + + udc->errata = match->data; + udc->num_ep = 0; udc->vbus_pin = of_get_named_gpio_flags(np, "atmel,vbus-gpio", 0, @@ -2024,7 +2039,7 @@ static int usba_udc_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Unable to enable pclk, aborting.\n"); return ret; } - toggle_bias(0); + usba_writel(udc, CTRL, USBA_DISABLE_MASK); clk_disable_unprepare(pclk); @@ -2033,6 +2048,8 @@ static int usba_udc_probe(struct platform_device *pdev) else udc->usba_ep = usba_udc_pdata(pdev, udc); + toggle_bias(udc, 0); + if (IS_ERR(udc->usba_ep)) return PTR_ERR(udc->usba_ep); @@ -2092,15 +2109,6 @@ static int __exit usba_udc_remove(struct platform_device *pdev) return 0; } -#if defined(CONFIG_OF) -static const struct of_device_id atmel_udc_dt_ids[] = { - { .compatible = "atmel,at91sam9rl-udc" }, - { /* sentinel */ } -}; - -MODULE_DEVICE_TABLE(of, atmel_udc_dt_ids); -#endif - static struct platform_driver udc_driver = { .remove = __exit_p(usba_udc_remove), .driver = { diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.h b/drivers/usb/gadget/udc/atmel_usba_udc.h index a70706e8cb02..456899e9ee62 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.h +++ b/drivers/usb/gadget/udc/atmel_usba_udc.h @@ -304,6 +304,10 @@ struct usba_request { unsigned int mapped:1; }; +struct usba_udc_errata { + void (*toggle_bias)(struct usba_udc *udc, int is_on); +}; + struct usba_udc { /* Protect hw registers from concurrent modifications */ spinlock_t lock; @@ -314,6 +318,7 @@ struct usba_udc { struct usb_gadget gadget; struct usb_gadget_driver *driver; struct platform_device *pdev; + const struct usba_udc_errata *errata; int irq; int vbus_pin; int vbus_pin_inverted; From 258e2ddd634c20065d1c41290d10d2e5cf2f56e2 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Tue, 6 Jan 2015 14:46:59 +0100 Subject: [PATCH 062/214] usb: atmel_usba_udc: Add at91sam9g45 and at91sam9x5 errata handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit at91sam9g45 and at91sam9x5 SoCs have an hardware bug forcing us to generate a pulse on the BIAS signal on "USB end of reset” and “USB end of resume" events. Acked-by: Alexandre Belloni Reported-by: Patrice VILCHEZ Signed-off-by: Boris Brezillon Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/atmel_usba_udc.c | 28 ++++++++++++++++++++++++- drivers/usb/gadget/udc/atmel_usba_udc.h | 2 ++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 36fd34b77387..55c8dde67f83 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -331,6 +331,17 @@ static void toggle_bias(struct usba_udc *udc, int is_on) udc->errata->toggle_bias(udc, is_on); } +static void generate_bias_pulse(struct usba_udc *udc) +{ + if (!udc->bias_pulse_needed) + return; + + if (udc->errata && udc->errata->pulse_bias) + udc->errata->pulse_bias(udc); + + udc->bias_pulse_needed = false; +} + static void next_fifo_transaction(struct usba_ep *ep, struct usba_request *req) { unsigned int transaction_len; @@ -1607,6 +1618,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) if (status & USBA_DET_SUSPEND) { toggle_bias(udc, 0); usba_writel(udc, INT_CLR, USBA_DET_SUSPEND); + udc->bias_pulse_needed = true; DBG(DBG_BUS, "Suspend detected\n"); if (udc->gadget.speed != USB_SPEED_UNKNOWN && udc->driver && udc->driver->suspend) { @@ -1624,6 +1636,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) if (status & USBA_END_OF_RESUME) { usba_writel(udc, INT_CLR, USBA_END_OF_RESUME); + generate_bias_pulse(udc); DBG(DBG_BUS, "Resume detected\n"); if (udc->gadget.speed != USB_SPEED_UNKNOWN && udc->driver && udc->driver->resume) { @@ -1659,6 +1672,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) struct usba_ep *ep0; usba_writel(udc, INT_CLR, USBA_END_OF_RESET); + generate_bias_pulse(udc); reset_all_endpoints(udc); if (udc->gadget.speed != USB_SPEED_UNKNOWN && udc->driver) { @@ -1818,13 +1832,25 @@ static void at91sam9rl_toggle_bias(struct usba_udc *udc, int is_on) at91_pmc_write(AT91_CKGR_UCKR, uckr & ~(AT91_PMC_BIASEN)); } +static void at91sam9g45_pulse_bias(struct usba_udc *udc) +{ + unsigned int uckr = at91_pmc_read(AT91_CKGR_UCKR); + + at91_pmc_write(AT91_CKGR_UCKR, uckr & ~(AT91_PMC_BIASEN)); + at91_pmc_write(AT91_CKGR_UCKR, uckr | AT91_PMC_BIASEN); +} + static const struct usba_udc_errata at91sam9rl_errata = { .toggle_bias = at91sam9rl_toggle_bias, }; +static const struct usba_udc_errata at91sam9g45_errata = { + .pulse_bias = at91sam9g45_pulse_bias, +}; + static const struct of_device_id atmel_udc_dt_ids[] = { { .compatible = "atmel,at91sam9rl-udc", .data = &at91sam9rl_errata }, - { .compatible = "atmel,at91sam9g45-udc" }, + { .compatible = "atmel,at91sam9g45-udc", .data = &at91sam9g45_errata }, { .compatible = "atmel,sama5d3-udc" }, { /* sentinel */ } }; diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.h b/drivers/usb/gadget/udc/atmel_usba_udc.h index 456899e9ee62..72b3537f56ce 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.h +++ b/drivers/usb/gadget/udc/atmel_usba_udc.h @@ -306,6 +306,7 @@ struct usba_request { struct usba_udc_errata { void (*toggle_bias)(struct usba_udc *udc, int is_on); + void (*pulse_bias)(struct usba_udc *udc); }; struct usba_udc { @@ -326,6 +327,7 @@ struct usba_udc { struct clk *pclk; struct clk *hclk; struct usba_ep *usba_ep; + bool bias_pulse_needed; u16 devstatus; From 9870d895ad87efae130b2990db9ba265271f113b Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Tue, 6 Jan 2015 14:47:01 +0100 Subject: [PATCH 063/214] usb: atmel_usba_udc: Mask status with enabled irqs Avoid interpreting useless status flags when we're not waiting for such events by masking the status variable with the interrupt enabled register value. Acked-by: Alexandre Belloni Reported-by: Patrice VILCHEZ Signed-off-by: Boris Brezillon Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/atmel_usba_udc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 55c8dde67f83..bc3a53264c43 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -1612,12 +1612,14 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) spin_lock(&udc->lock); - status = usba_readl(udc, INT_STA); + status = usba_readl(udc, INT_STA) & usba_readl(udc, INT_ENB); DBG(DBG_INT, "irq, status=%#08x\n", status); if (status & USBA_DET_SUSPEND) { toggle_bias(udc, 0); usba_writel(udc, INT_CLR, USBA_DET_SUSPEND); + usba_writel(udc, INT_ENB, + usba_readl(udc, INT_ENB) | USBA_WAKE_UP); udc->bias_pulse_needed = true; DBG(DBG_BUS, "Suspend detected\n"); if (udc->gadget.speed != USB_SPEED_UNKNOWN @@ -1631,6 +1633,8 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) if (status & USBA_WAKE_UP) { toggle_bias(udc, 1); usba_writel(udc, INT_CLR, USBA_WAKE_UP); + usba_writel(udc, INT_ENB, + usba_readl(udc, INT_ENB) & ~USBA_WAKE_UP); DBG(DBG_BUS, "Wake Up CPU detected\n"); } From e3a912a124c380db61eff762faa0547ea4c90eb4 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Tue, 6 Jan 2015 14:47:02 +0100 Subject: [PATCH 064/214] usb: gadget: atmel_usba: Cache INT_ENB register value Cache INT_ENB register value in order to avoid uncached iomem access, and thus improve access time to INT_ENB value. Acked-by: Alexandre Belloni Signed-off-by: Boris Brezillon Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/atmel_usba_udc.c | 52 +++++++++++++------------ drivers/usb/gadget/udc/atmel_usba_udc.h | 2 + 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index bc3a53264c43..6dfe17b583fb 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -316,6 +316,17 @@ static inline void usba_cleanup_debugfs(struct usba_udc *udc) } #endif +static inline u32 usba_int_enb_get(struct usba_udc *udc) +{ + return udc->int_enb_cache; +} + +static inline void usba_int_enb_set(struct usba_udc *udc, u32 val) +{ + usba_writel(udc, INT_ENB, val); + udc->int_enb_cache = val; +} + static int vbus_is_present(struct usba_udc *udc) { if (gpio_is_valid(udc->vbus_pin)) @@ -597,16 +608,14 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) if (ep->can_dma) { u32 ctrl; - usba_writel(udc, INT_ENB, - (usba_readl(udc, INT_ENB) - | USBA_BF(EPT_INT, 1 << ep->index) - | USBA_BF(DMA_INT, 1 << ep->index))); + usba_int_enb_set(udc, usba_int_enb_get(udc) | + USBA_BF(EPT_INT, 1 << ep->index) | + USBA_BF(DMA_INT, 1 << ep->index)); ctrl = USBA_AUTO_VALID | USBA_INTDIS_DMA; usba_ep_writel(ep, CTL_ENB, ctrl); } else { - usba_writel(udc, INT_ENB, - (usba_readl(udc, INT_ENB) - | USBA_BF(EPT_INT, 1 << ep->index))); + usba_int_enb_set(udc, usba_int_enb_get(udc) | + USBA_BF(EPT_INT, 1 << ep->index)); } spin_unlock_irqrestore(&udc->lock, flags); @@ -614,7 +623,7 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) DBG(DBG_HW, "EPT_CFG%d after init: %#08lx\n", ep->index, (unsigned long)usba_ep_readl(ep, CFG)); DBG(DBG_HW, "INT_ENB after init: %#08lx\n", - (unsigned long)usba_readl(udc, INT_ENB)); + (unsigned long)usba_int_enb_get(udc)); return 0; } @@ -650,9 +659,8 @@ static int usba_ep_disable(struct usb_ep *_ep) usba_dma_readl(ep, STATUS); } usba_ep_writel(ep, CTL_DIS, USBA_EPT_ENABLE); - usba_writel(udc, INT_ENB, - usba_readl(udc, INT_ENB) - & ~USBA_BF(EPT_INT, 1 << ep->index)); + usba_int_enb_set(udc, usba_int_enb_get(udc) & + ~USBA_BF(EPT_INT, 1 << ep->index)); request_complete_list(ep, &req_list, -ESHUTDOWN); @@ -1606,20 +1614,20 @@ static void usba_dma_irq(struct usba_udc *udc, struct usba_ep *ep) static irqreturn_t usba_udc_irq(int irq, void *devid) { struct usba_udc *udc = devid; - u32 status; + u32 status, int_enb; u32 dma_status; u32 ep_status; spin_lock(&udc->lock); - status = usba_readl(udc, INT_STA) & usba_readl(udc, INT_ENB); + int_enb = usba_int_enb_get(udc); + status = usba_readl(udc, INT_STA) & int_enb; DBG(DBG_INT, "irq, status=%#08x\n", status); if (status & USBA_DET_SUSPEND) { toggle_bias(udc, 0); usba_writel(udc, INT_CLR, USBA_DET_SUSPEND); - usba_writel(udc, INT_ENB, - usba_readl(udc, INT_ENB) | USBA_WAKE_UP); + usba_int_enb_set(udc, int_enb | USBA_WAKE_UP); udc->bias_pulse_needed = true; DBG(DBG_BUS, "Suspend detected\n"); if (udc->gadget.speed != USB_SPEED_UNKNOWN @@ -1633,8 +1641,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) if (status & USBA_WAKE_UP) { toggle_bias(udc, 1); usba_writel(udc, INT_CLR, USBA_WAKE_UP); - usba_writel(udc, INT_ENB, - usba_readl(udc, INT_ENB) & ~USBA_WAKE_UP); + usba_int_enb_set(udc, int_enb & ~USBA_WAKE_UP); DBG(DBG_BUS, "Wake Up CPU detected\n"); } @@ -1702,11 +1709,8 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) | USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE))); usba_ep_writel(ep0, CTL_ENB, USBA_EPT_ENABLE | USBA_RX_SETUP); - usba_writel(udc, INT_ENB, - (usba_readl(udc, INT_ENB) - | USBA_BF(EPT_INT, 1) - | USBA_DET_SUSPEND - | USBA_END_OF_RESUME)); + usba_int_enb_set(udc, int_enb | USBA_BF(EPT_INT, 1) | + USBA_DET_SUSPEND | USBA_END_OF_RESUME); /* * Unclear why we hit this irregularly, e.g. in usbtest, @@ -1741,7 +1745,7 @@ static irqreturn_t usba_vbus_irq(int irq, void *devid) if (vbus) { toggle_bias(udc, 1); usba_writel(udc, CTRL, USBA_ENABLE_MASK); - usba_writel(udc, INT_ENB, USBA_END_OF_RESET); + usba_int_enb_set(udc, USBA_END_OF_RESET); } else { udc->gadget.speed = USB_SPEED_UNKNOWN; reset_all_endpoints(udc); @@ -1793,7 +1797,7 @@ static int atmel_usba_start(struct usb_gadget *gadget, if (vbus_is_present(udc) && udc->vbus_prev == 0) { toggle_bias(udc, 1); usba_writel(udc, CTRL, USBA_ENABLE_MASK); - usba_writel(udc, INT_ENB, USBA_END_OF_RESET); + usba_int_enb_set(udc, USBA_END_OF_RESET); } spin_unlock_irqrestore(&udc->lock, flags); diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.h b/drivers/usb/gadget/udc/atmel_usba_udc.h index 72b3537f56ce..497cd18836f3 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.h +++ b/drivers/usb/gadget/udc/atmel_usba_udc.h @@ -334,6 +334,8 @@ struct usba_udc { u16 test_mode; int vbus_prev; + u32 int_enb_cache; + #ifdef CONFIG_USB_GADGET_DEBUG_FS struct dentry *debugfs_root; struct dentry *debugfs_regs; From e9f2cefb0cdc2aea8b70dbf68a3f78b88e57cf34 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sat, 6 Dec 2014 22:05:13 +0100 Subject: [PATCH 065/214] usb: phy: generic: migrate to gpio_desc Change internal gpio handling from integer gpios into gpio descriptors. This change only addresses the internal API and device-tree/ACPI, while the legacy platform data remains integer space based. This change is only build compile tested, and very prone to error. I leave this comment for now in the commit message so that this patch gets some testing as I'm pretty sure it's buggy. Signed-off-by: Robert Jarzmik Acked-by: Linus Walleij Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-generic.c | 61 +++++++++++------------------------ drivers/usb/phy/phy-generic.h | 4 +-- 2 files changed, 21 insertions(+), 44 deletions(-) diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c index f1b719b45a53..d53928f3a6ea 100644 --- a/drivers/usb/phy/phy-generic.c +++ b/drivers/usb/phy/phy-generic.c @@ -59,16 +59,8 @@ static int nop_set_suspend(struct usb_phy *x, int suspend) static void nop_reset_set(struct usb_phy_generic *nop, int asserted) { - int value; - - if (!gpio_is_valid(nop->gpio_reset)) - return; - - value = asserted; - if (nop->reset_active_low) - value = !value; - - gpio_set_value_cansleep(nop->gpio_reset, value); + if (nop->gpiod_reset) + gpiod_set_value(nop->gpiod_reset, asserted); if (!asserted) usleep_range(10000, 20000); @@ -143,35 +135,38 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop, struct usb_phy_generic_platform_data *pdata) { enum usb_phy_type type = USB_PHY_TYPE_USB2; - int err; + int err = 0; u32 clk_rate = 0; bool needs_vcc = false; - nop->reset_active_low = true; /* default behaviour */ - if (dev->of_node) { struct device_node *node = dev->of_node; - enum of_gpio_flags flags = 0; if (of_property_read_u32(node, "clock-frequency", &clk_rate)) clk_rate = 0; needs_vcc = of_property_read_bool(node, "vcc-supply"); - nop->gpio_reset = of_get_named_gpio_flags(node, "reset-gpios", - 0, &flags); - if (nop->gpio_reset == -EPROBE_DEFER) - return -EPROBE_DEFER; - - nop->reset_active_low = flags & OF_GPIO_ACTIVE_LOW; - + nop->gpiod_reset = devm_gpiod_get(dev, "reset-gpios"); + err = PTR_ERR(nop->gpiod_reset); } else if (pdata) { type = pdata->type; clk_rate = pdata->clk_rate; needs_vcc = pdata->needs_vcc; - nop->gpio_reset = pdata->gpio_reset; - } else { - nop->gpio_reset = -1; + if (gpio_is_valid(gpio->gpio_reset)) { + err = devm_gpio_request_one(dev, pdata->gpio_reset, 0, + dev_name(dev)); + if (!err) + nop->gpiod_reset = + gpio_to_desc(pdata->gpio_reset); + } + } + + if (err == -EPROBE_DEFER) + return -EPROBE_DEFER; + if (err) { + dev_err(dev, "Error requesting RESET GPIO\n"); + return err; } nop->phy.otg = devm_kzalloc(dev, sizeof(*nop->phy.otg), @@ -201,24 +196,6 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop, return -EPROBE_DEFER; } - if (gpio_is_valid(nop->gpio_reset)) { - unsigned long gpio_flags; - - /* Assert RESET */ - if (nop->reset_active_low) - gpio_flags = GPIOF_OUT_INIT_LOW; - else - gpio_flags = GPIOF_OUT_INIT_HIGH; - - err = devm_gpio_request_one(dev, nop->gpio_reset, - gpio_flags, dev_name(dev)); - if (err) { - dev_err(dev, "Error requesting RESET GPIO %d\n", - nop->gpio_reset); - return err; - } - } - nop->dev = dev; nop->phy.dev = nop->dev; nop->phy.label = "nop-xceiv"; diff --git a/drivers/usb/phy/phy-generic.h b/drivers/usb/phy/phy-generic.h index d8feacc0b7fb..09924fdaaabe 100644 --- a/drivers/usb/phy/phy-generic.h +++ b/drivers/usb/phy/phy-generic.h @@ -2,14 +2,14 @@ #define _PHY_GENERIC_H_ #include +#include struct usb_phy_generic { struct usb_phy phy; struct device *dev; struct clk *clk; struct regulator *vcc; - int gpio_reset; - bool reset_active_low; + struct gpio_desc *gpiod_reset; }; int usb_gen_phy_init(struct usb_phy *phy); From 7bdea87a871f217a8a463f9203e6d0c702b7fda9 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sat, 6 Dec 2014 22:05:14 +0100 Subject: [PATCH 066/214] usb: phy: nop: device tree documentation for vbus Enhance the phy documentation by adding 2 new optional bindings : - the vbus gpio, which detects usb insertion - the vbus regulator, which provides current drawn from the usb cable Signed-off-by: Robert Jarzmik Signed-off-by: Felipe Balbi --- Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt b/Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt index 8db5b33d6fdc..5be01c859b7a 100644 --- a/Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt +++ b/Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt @@ -17,6 +17,11 @@ Optional properties: - reset-gpios: Should specify the GPIO for reset. +- vbus-detect-gpio: should specify the GPIO detecting a VBus insertion + (see Documentation/devicetree/bindings/gpio/gpio.txt) +- vbus-regulator : should specifiy the regulator supplying current drawn from + the VBus line (see Documentation/devicetree/bindings/regulator/regulator.txt). + Example: hsusb1_phy { @@ -26,8 +31,11 @@ Example: clock-names = "main_clk"; vcc-supply = <&hsusb1_vcc_regulator>; reset-gpios = <&gpio1 7 GPIO_ACTIVE_LOW>; + vbus-detect-gpio = <&gpio2 13 GPIO_ACTIVE_HIGH>; + vbus-regulator = <&vbus_regulator>; }; hsusb1_phy is a NOP USB PHY device that gets its clock from an oscillator and expects that clock to be configured to 19.2MHz by the NOP PHY driver. hsusb1_vcc_regulator provides power to the PHY and GPIO 7 controls RESET. +GPIO 13 detects VBus insertion, and accordingly notifies the vbus-regulator. From 7acc9973e3c42de9926b28eec8ae3434dfdde3be Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sat, 6 Dec 2014 22:05:15 +0100 Subject: [PATCH 067/214] usb: phy: generic: add vbus support Add support for vbus detection and power supply. This code is more or less stolen from phy-gpio-vbus-usb.c, and aims at providing a detection mechanism for VBus (ie. usb cable plug) based on a GPIO line, and a power supply activation which draws current from the VBus. [ balbi@ti.com : fix build break ] Signed-off-by: Robert Jarzmik Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-generic.c | 91 ++++++++++++++++++++++++++++- drivers/usb/phy/phy-generic.h | 6 ++ include/linux/usb/usb_phy_generic.h | 2 + 3 files changed, 98 insertions(+), 1 deletion(-) diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c index d53928f3a6ea..dd05254241fb 100644 --- a/drivers/usb/phy/phy-generic.c +++ b/drivers/usb/phy/phy-generic.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -39,6 +40,9 @@ #include "phy-generic.h" +#define VBUS_IRQ_FLAGS \ + (IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING) + struct platform_device *usb_phy_generic_register(void) { return platform_device_register_simple("usb_phy_generic", @@ -66,6 +70,73 @@ static void nop_reset_set(struct usb_phy_generic *nop, int asserted) usleep_range(10000, 20000); } +/* interface to regulator framework */ +static void nop_set_vbus_draw(struct usb_phy_generic *nop, unsigned mA) +{ + struct regulator *vbus_draw = nop->vbus_draw; + int enabled; + int ret; + + if (!vbus_draw) + return; + + enabled = nop->vbus_draw_enabled; + if (mA) { + regulator_set_current_limit(vbus_draw, 0, 1000 * mA); + if (!enabled) { + ret = regulator_enable(vbus_draw); + if (ret < 0) + return; + nop->vbus_draw_enabled = 1; + } + } else { + if (enabled) { + ret = regulator_disable(vbus_draw); + if (ret < 0) + return; + nop->vbus_draw_enabled = 0; + } + } + nop->mA = mA; +} + + +static irqreturn_t nop_gpio_vbus_thread(int irq, void *data) +{ + struct usb_phy_generic *nop = data; + struct usb_otg *otg = nop->phy.otg; + int vbus, status; + + vbus = gpiod_get_value(nop->gpiod_vbus); + if ((vbus ^ nop->vbus) == 0) + return IRQ_HANDLED; + nop->vbus = vbus; + + if (vbus) { + status = USB_EVENT_VBUS; + otg->state = OTG_STATE_B_PERIPHERAL; + nop->phy.last_event = status; + usb_gadget_vbus_connect(otg->gadget); + + /* drawing a "unit load" is *always* OK, except for OTG */ + nop_set_vbus_draw(nop, 100); + + atomic_notifier_call_chain(&nop->phy.notifier, status, + otg->gadget); + } else { + nop_set_vbus_draw(nop, 0); + + usb_gadget_vbus_disconnect(otg->gadget); + status = USB_EVENT_NONE; + otg->state = OTG_STATE_B_IDLE; + nop->phy.last_event = status; + + atomic_notifier_call_chain(&nop->phy.notifier, status, + otg->gadget); + } + return IRQ_HANDLED; +} + int usb_gen_phy_init(struct usb_phy *phy) { struct usb_phy_generic *nop = dev_get_drvdata(phy->dev); @@ -149,17 +220,23 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop, needs_vcc = of_property_read_bool(node, "vcc-supply"); nop->gpiod_reset = devm_gpiod_get(dev, "reset-gpios"); err = PTR_ERR(nop->gpiod_reset); + if (!err) { + nop->gpiod_vbus = devm_gpiod_get(dev, + "vbus-detect-gpio"); + err = PTR_ERR(nop->gpiod_vbus); + } } else if (pdata) { type = pdata->type; clk_rate = pdata->clk_rate; needs_vcc = pdata->needs_vcc; - if (gpio_is_valid(gpio->gpio_reset)) { + if (gpio_is_valid(pdata->gpio_reset)) { err = devm_gpio_request_one(dev, pdata->gpio_reset, 0, dev_name(dev)); if (!err) nop->gpiod_reset = gpio_to_desc(pdata->gpio_reset); } + nop->gpiod_vbus = pdata->gpiod_vbus; } if (err == -EPROBE_DEFER) @@ -224,6 +301,18 @@ static int usb_phy_generic_probe(struct platform_device *pdev) err = usb_phy_gen_create_phy(dev, nop, dev_get_platdata(&pdev->dev)); if (err) return err; + if (nop->gpiod_vbus) { + err = devm_request_threaded_irq(&pdev->dev, + gpiod_to_irq(nop->gpiod_vbus), + NULL, nop_gpio_vbus_thread, + VBUS_IRQ_FLAGS, "vbus_detect", + nop); + if (err) { + dev_err(&pdev->dev, "can't request irq %i, err: %d\n", + gpiod_to_irq(nop->gpiod_vbus), err); + return err; + } + } nop->phy.init = usb_gen_phy_init; nop->phy.shutdown = usb_gen_phy_shutdown; diff --git a/drivers/usb/phy/phy-generic.h b/drivers/usb/phy/phy-generic.h index 09924fdaaabe..0d0eadd54ed9 100644 --- a/drivers/usb/phy/phy-generic.h +++ b/drivers/usb/phy/phy-generic.h @@ -3,6 +3,7 @@ #include #include +#include struct usb_phy_generic { struct usb_phy phy; @@ -10,6 +11,11 @@ struct usb_phy_generic { struct clk *clk; struct regulator *vcc; struct gpio_desc *gpiod_reset; + struct gpio_desc *gpiod_vbus; + struct regulator *vbus_draw; + bool vbus_draw_enabled; + unsigned long mA; + unsigned int vbus; }; int usb_gen_phy_init(struct usb_phy *phy); diff --git a/include/linux/usb/usb_phy_generic.h b/include/linux/usb/usb_phy_generic.h index 68adae83affc..c13632d5292e 100644 --- a/include/linux/usb/usb_phy_generic.h +++ b/include/linux/usb/usb_phy_generic.h @@ -2,6 +2,7 @@ #define __LINUX_USB_NOP_XCEIV_H #include +#include struct usb_phy_generic_platform_data { enum usb_phy_type type; @@ -11,6 +12,7 @@ struct usb_phy_generic_platform_data { unsigned int needs_vcc:1; unsigned int needs_reset:1; /* deprecated */ int gpio_reset; + struct gpio_desc *gpiod_vbus; }; #if IS_ENABLED(CONFIG_NOP_USB_XCEIV) From a7657a93d63d87399b53d15999e77973b4f02cf3 Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Mon, 5 Jan 2015 10:44:44 +0100 Subject: [PATCH 068/214] tools: ffs-aio-example: add missing wMaxPacketSize for HS descs It's needed, to have more than 64 bytes of maxpacketsize. Signed-off-by: Robert Baldyga Signed-off-by: Felipe Balbi --- tools/usb/ffs-aio-example/simple/device_app/aio_simple.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/usb/ffs-aio-example/simple/device_app/aio_simple.c b/tools/usb/ffs-aio-example/simple/device_app/aio_simple.c index adc310a6d489..1f44a29818bf 100644 --- a/tools/usb/ffs-aio-example/simple/device_app/aio_simple.c +++ b/tools/usb/ffs-aio-example/simple/device_app/aio_simple.c @@ -103,12 +103,14 @@ static const struct { .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 1 | USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = htole16(512), }, .bulk_source = { .bLength = sizeof(descriptors.hs_descs.bulk_source), .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 2 | USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = htole16(512), }, }, }; From 67913bbd0b62d297520667cd3057aff5a5890ca6 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Wed, 10 Sep 2014 17:50:24 +0200 Subject: [PATCH 069/214] usb: gadget: f_fs: refactor and document __ffs_ep0_read_events better Instead of using variable length array, use a static length equal to the size of the ffs->ev.types array. This gets rid of a sparse warning: drivers/usb/gadget/function/f_fs.c:401:44: warning: Variable length array is used. and makes it more explicit that the array has a very tight upper size limit. Also add some more documentation about the ev.types array and how its size is limited and affects the rest of the code. Reported-by: Dan Carpenter Reported-by: Rohith Seelaboyina Signed-off-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_fs.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 63314ede7ba6..a00ee977305c 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -390,17 +390,20 @@ done_spin: return ret; } +/* Called with ffs->ev.waitq.lock and ffs->mutex held, both released on exit. */ static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf, size_t n) { /* - * We are holding ffs->ev.waitq.lock and ffs->mutex and we need - * to release them. + * n cannot be bigger than ffs->ev.count, which cannot be bigger than + * size of ffs->ev.types array (which is four) so that's how much space + * we reserve. */ - struct usb_functionfs_event events[n]; + struct usb_functionfs_event events[ARRAY_SIZE(ffs->ev.types)]; + const size_t size = n * sizeof *events; unsigned i = 0; - memset(events, 0, sizeof events); + memset(events, 0, size); do { events[i].type = ffs->ev.types[i]; @@ -410,19 +413,15 @@ static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf, } } while (++i < n); - if (n < ffs->ev.count) { - ffs->ev.count -= n; + ffs->ev.count -= n; + if (ffs->ev.count) memmove(ffs->ev.types, ffs->ev.types + n, ffs->ev.count * sizeof *ffs->ev.types); - } else { - ffs->ev.count = 0; - } spin_unlock_irq(&ffs->ev.waitq.lock); mutex_unlock(&ffs->mutex); - return unlikely(__copy_to_user(buf, events, sizeof events)) - ? -EFAULT : sizeof events; + return unlikely(__copy_to_user(buf, events, size)) ? -EFAULT : size; } static ssize_t ffs_ep0_read(struct file *file, char __user *buf, @@ -2377,6 +2376,13 @@ static void __ffs_event_add(struct ffs_data *ffs, if (ffs->setup_state == FFS_SETUP_PENDING) ffs->setup_state = FFS_SETUP_CANCELLED; + /* + * Logic of this function guarantees that there are at most four pending + * evens on ffs->ev.types queue. This is important because the queue + * has space for four elements only and __ffs_ep0_read_events function + * depends on that limit as well. If more event types are added, those + * limits have to be revisited or guaranteed to still hold. + */ switch (type) { case FUNCTIONFS_RESUME: rem_type2 = FUNCTIONFS_SUSPEND; From 42c1ecff0e5bdd35659d143ad3d0e40e190b4050 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Mon, 12 Jan 2015 16:37:55 +0800 Subject: [PATCH 070/214] usb: gadget: pxa27x_udc: delete pullup operation at .udc_start and .udc_stop UDC core has already done it before .udc_stop and after .udc_start. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/pxa27x_udc.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c index c61a896061fa..6a855fc9bd84 100644 --- a/drivers/usb/gadget/udc/pxa27x_udc.c +++ b/drivers/usb/gadget/udc/pxa27x_udc.c @@ -1809,7 +1809,6 @@ static int pxa27x_udc_start(struct usb_gadget *g, /* first hook up the driver ... */ udc->driver = driver; - dplus_pullup(udc, 1); if (!IS_ERR_OR_NULL(udc->transceiver)) { retval = otg_set_peripheral(udc->transceiver->otg, @@ -1862,7 +1861,6 @@ static int pxa27x_udc_stop(struct usb_gadget *g) stop_activity(udc, NULL); udc_disable(udc); - dplus_pullup(udc, 0); udc->driver = NULL; From 14d19d9f05184c38001be47b7567c8d53fce88ff Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Mon, 12 Jan 2015 16:37:56 +0800 Subject: [PATCH 071/214] usb: gadget: pxa27x_udc: delete pullup operation at .udc_start and .udc_stop UDC core has already done it before .udc_stop and after .udc_start. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/pxa25x_udc.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/usb/gadget/udc/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c index 8550b2d5db32..f6cbe667ce39 100644 --- a/drivers/usb/gadget/udc/pxa25x_udc.c +++ b/drivers/usb/gadget/udc/pxa25x_udc.c @@ -1272,7 +1272,6 @@ static int pxa25x_udc_start(struct usb_gadget *g, goto bind_fail; } - pullup(dev); dump_state(dev); return 0; bind_fail: @@ -1339,7 +1338,6 @@ static int pxa25x_udc_stop(struct usb_gadget*g) local_irq_disable(); dev->pullup = 0; - pullup(dev); stop_activity(dev, NULL); local_irq_enable(); From 487d60cc09237d24f93ec2892cd8437ae689d7e0 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Mon, 12 Jan 2015 16:37:57 +0800 Subject: [PATCH 072/214] usb: gadget: mv_udc_core: delete pullup operation at .udc_start UDC core has already done it after .udc_start. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/mv_udc_core.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/usb/gadget/udc/mv_udc_core.c b/drivers/usb/gadget/udc/mv_udc_core.c index 253f3df8326a..d32160d6463f 100644 --- a/drivers/usb/gadget/udc/mv_udc_core.c +++ b/drivers/usb/gadget/udc/mv_udc_core.c @@ -1378,9 +1378,6 @@ static int mv_udc_start(struct usb_gadget *gadget, } } - /* pullup is always on */ - mv_udc_pullup(&udc->gadget, 1); - /* When boot with cable attached, there will be no vbus irq occurred */ if (udc->qwork) queue_work(udc->qwork, &udc->vbus_work); From f6c015922c1dc99c53fb2ca95361086e65897b7c Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Fri, 9 Jan 2015 13:38:41 +0100 Subject: [PATCH 073/214] usb: dwc2: gadget: register gadget handle to the phy Bind peripheral controller to the phy on udc_start. Unbind on udc_stop. Tested-by: Robert Baldyga Acked-by: Paul Zimmerman Signed-off-by: Gregory Herrero Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 1a8e0a6d2ef8..b5332f01be85 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2878,6 +2878,8 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget, } s3c_hsotg_phy_enable(hsotg); + if (!IS_ERR_OR_NULL(hsotg->uphy)) + otg_set_peripheral(hsotg->uphy->otg, &hsotg->gadget); spin_lock_irqsave(&hsotg->lock, flags); s3c_hsotg_init(hsotg); @@ -2927,6 +2929,8 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget) spin_unlock_irqrestore(&hsotg->lock, flags); + if (!IS_ERR_OR_NULL(hsotg->uphy)) + otg_set_peripheral(hsotg->uphy->otg, NULL); s3c_hsotg_phy_disable(hsotg); regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies); From 6ff2e8326fb50aee80f93e12de367065cc089aae Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Fri, 9 Jan 2015 13:38:42 +0100 Subject: [PATCH 074/214] usb: dwc2: gadget: mask fifo empty irq with dma When using DMA, keep fifo empty interrupt disabled. Otherwise core is flooded by interrupts. Tested-by: Robert Baldyga Acked-by: Paul Zimmerman Signed-off-by: Gregory Herrero Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index b5332f01be85..843f3ee0fbbd 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2170,8 +2170,8 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg) * interrupts. */ - writel(((hsotg->dedicated_fifos) ? DIEPMSK_TXFIFOEMPTY | - DIEPMSK_INTKNTXFEMPMSK : 0) | + writel(((hsotg->dedicated_fifos && !using_dma(hsotg)) ? + DIEPMSK_TXFIFOEMPTY | DIEPMSK_INTKNTXFEMPMSK : 0) | DIEPMSK_EPDISBLDMSK | DIEPMSK_XFERCOMPLMSK | DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK | DIEPMSK_INTKNEPMISMSK, From b787d75503fb6f68aea5220ca23bb0c02d14ae74 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Fri, 9 Jan 2015 13:38:43 +0100 Subject: [PATCH 075/214] usb: dwc2: gadget: don't process XferCompl on setup packet Only process DOEPINT.XferCompl on data packet as DOEPINTn.SetUp can occur with or without DOEPINT.XferCompl. When DOEPINT.SetUp occurs with DOEPINT.XferCompl, only DOEPINT.SetUp needs to be handled. Moreover, ignore DOEPINT.XferCompl when it occurs with DOEPINT.StupPktRcvd as driver needs to wait for DOEPINT.SetUp to continue. Tested-by: Robert Baldyga Acked-by: Paul Zimmerman Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 4 ++++ drivers/usb/dwc2/hw.h | 1 + 2 files changed, 5 insertions(+) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 843f3ee0fbbd..45afbf840f03 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -1810,6 +1810,10 @@ static void s3c_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx, dev_dbg(hsotg->dev, "%s: ep%d(%s) DxEPINT=0x%08x\n", __func__, idx, dir_in ? "in" : "out", ints); + /* Don't process XferCompl interrupt if it is a setup packet */ + if (idx == 0 && (ints & (DXEPINT_SETUP | DXEPINT_SETUP_RCVD))) + ints &= ~DXEPINT_XFERCOMPL; + if (ints & DXEPINT_XFERCOMPL) { if (hs_ep->isochronous && hs_ep->interval == 1) { if (ctrl & DXEPCTL_EOFRNUM) diff --git a/drivers/usb/dwc2/hw.h b/drivers/usb/dwc2/hw.h index 51248b935493..d018ebef15cc 100644 --- a/drivers/usb/dwc2/hw.h +++ b/drivers/usb/dwc2/hw.h @@ -541,6 +541,7 @@ #define DIEPINT(_a) HSOTG_REG(0x908 + ((_a) * 0x20)) #define DOEPINT(_a) HSOTG_REG(0xB08 + ((_a) * 0x20)) +#define DXEPINT_SETUP_RCVD (1 << 15) #define DXEPINT_INEPNAKEFF (1 << 6) #define DXEPINT_BACK2BACKSETUP (1 << 6) #define DXEPINT_INTKNEPMIS (1 << 5) From 3f95001db7998c350422708a437694dda07bc04e Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Fri, 9 Jan 2015 13:38:44 +0100 Subject: [PATCH 076/214] usb: dwc2: gadget: don't embed ep0 buffers When using DMA, data of the previous setup packet can be read back from cache because ep0 and ctrl buffers are embedded in struct s3c_hsotg. Allocate buffers instead of embedding them. Tested-by: Robert Baldyga Acked-by: Paul Zimmerman Signed-off-by: Mian Yousaf Kaukab Reviewed-by: Robert Baldyga Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/core.h | 7 +++++-- drivers/usb/dwc2/gadget.c | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 0d2ee29ac85a..7db83d058da3 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -434,6 +434,9 @@ struct dwc2_hw_params { u32 snpsid; }; +/* Size of control and EP0 buffers */ +#define DWC2_CTRL_BUFF_SIZE 8 + /** * struct dwc2_hsotg - Holds the state of the driver, including the non-periodic * and periodic schedules @@ -684,8 +687,8 @@ struct dwc2_hsotg { struct usb_request *ep0_reply; struct usb_request *ctrl_req; - u8 ep0_buff[8]; - u8 ctrl_buff[8]; + void *ep0_buff; + void *ctrl_buff; struct usb_gadget gadget; unsigned int enabled:1; diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 45afbf840f03..35d346fcd900 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -3486,6 +3486,22 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) s3c_hsotg_hw_cfg(hsotg); s3c_hsotg_init(hsotg); + hsotg->ctrl_buff = devm_kzalloc(hsotg->dev, + DWC2_CTRL_BUFF_SIZE, GFP_KERNEL); + if (!hsotg->ctrl_buff) { + dev_err(dev, "failed to allocate ctrl request buff\n"); + ret = -ENOMEM; + goto err_supplies; + } + + hsotg->ep0_buff = devm_kzalloc(hsotg->dev, + DWC2_CTRL_BUFF_SIZE, GFP_KERNEL); + if (!hsotg->ep0_buff) { + dev_err(dev, "failed to allocate ctrl reply buff\n"); + ret = -ENOMEM; + goto err_supplies; + } + ret = devm_request_irq(hsotg->dev, irq, s3c_hsotg_irq, IRQF_SHARED, dev_name(hsotg->dev), hsotg); if (ret < 0) { From c139ec27f9c77c7c215d7989f6d77e2dbaba63df Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Fri, 9 Jan 2015 13:38:45 +0100 Subject: [PATCH 077/214] usb: dwc2: gadget: fix error path in dwc2_gadget_init In the error path, s3c_hsotg_phy_disable should be called after a call to s3c_hsotg_phy_enable is made. Tested-by: Robert Baldyga Acked-by: Paul Zimmerman Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 35d346fcd900..a4d2c2622597 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -3476,7 +3476,7 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) if (ret) { dev_err(dev, "failed to enable supplies: %d\n", ret); - goto err_supplies; + goto err_clk; } /* usb phy enable */ @@ -3510,7 +3510,7 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies); dev_err(dev, "cannot claim IRQ for gadget\n"); - goto err_clk; + goto err_supplies; } /* hsotg->num_of_eps holds number of EPs other than ep0 */ From 5f05048e5473a0570b1db9c2828d2a469e8bbbee Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Fri, 9 Jan 2015 13:38:46 +0100 Subject: [PATCH 078/214] usb: dwc2: gadget: write correct value in ahbcfg register HBstLen is GAHBCFG[4:1]. Use GAHBCFG_HBSTLEN_SHIFT to write burst- length at correct position. Tested-by: Robert Baldyga Acked-by: Paul Zimmerman Signed-off-by: Gregory Herrero Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index a4d2c2622597..b69b8fa81779 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2160,7 +2160,7 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg) if (using_dma(hsotg)) writel(GAHBCFG_GLBL_INTR_EN | GAHBCFG_DMA_EN | - GAHBCFG_HBSTLEN_INCR4, + (GAHBCFG_HBSTLEN_INCR4 << GAHBCFG_HBSTLEN_SHIFT), hsotg->regs + GAHBCFG); else writel(((hsotg->dedicated_fifos) ? (GAHBCFG_NP_TXF_EMP_LVL | From f5090044763fd954a002a6ef466fcbc21033518f Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Fri, 9 Jan 2015 13:38:47 +0100 Subject: [PATCH 079/214] usb: dwc2: gadget: don't erase gahbcfg register when enabling dma Do a read-modify-write instead of only setting DMAEn bit. Tested-by: Robert Baldyga Acked-by: Paul Zimmerman Signed-off-by: Gregory Herrero Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index b69b8fa81779..b98622f2092d 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2827,8 +2827,8 @@ static void s3c_hsotg_init(struct dwc2_hsotg *hsotg) writel(GUSBCFG_PHYIF16 | GUSBCFG_TOUTCAL(7) | (0x5 << 10), hsotg->regs + GUSBCFG); - writel(using_dma(hsotg) ? GAHBCFG_DMA_EN : 0x0, - hsotg->regs + GAHBCFG); + if (using_dma(hsotg)) + __orr32(hsotg->regs + GAHBCFG, GAHBCFG_DMA_EN); } /** From edd74be83047f439e9507cedbbbef0891f3a9a48 Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Fri, 9 Jan 2015 13:38:48 +0100 Subject: [PATCH 080/214] usb: dwc2: gadget: add device tree property to enable dma * Add an of specific function to parse device node properties. * Enable dma usage only if device tree property 'g_use_dma' is present. Tested-by: Robert Baldyga Acked-by: Paul Zimmerman Signed-off-by: Gregory Herrero Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/core.h | 2 ++ drivers/usb/dwc2/gadget.c | 18 ++++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 7db83d058da3..376a008ef437 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -563,6 +563,7 @@ struct dwc2_hw_params { * @setup: NAK management for EP0 SETUP * @last_rst: Time of last reset * @eps: The endpoints being supplied to the gadget framework + * @g_using_dma: Indicate if dma usage is enabled */ struct dwc2_hsotg { struct device *dev; @@ -696,6 +697,7 @@ struct dwc2_hsotg { unsigned int setup:1; unsigned long last_rst; struct s3c_hsotg_ep *eps; + u32 g_using_dma; #endif /* CONFIG_USB_DWC2_PERIPHERAL || CONFIG_USB_DWC2_DUAL_ROLE */ }; diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index b98622f2092d..c24cb3bef06a 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -85,11 +85,11 @@ static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg); * a core reset. This means we either need to fix the gadgets to take * account of DMA alignment, or add bounce buffers (yuerk). * - * Until this issue is sorted out, we always return 'false'. + * g_using_dma is set depending on dts flag. */ static inline bool using_dma(struct dwc2_hsotg *hsotg) { - return false; /* support is not complete */ + return hsotg->g_using_dma; } /** @@ -3402,6 +3402,18 @@ static void s3c_hsotg_delete_debug(struct dwc2_hsotg *hsotg) debugfs_remove(hsotg->debug_root); } +#ifdef CONFIG_OF +static void s3c_hsotg_of_probe(struct dwc2_hsotg *hsotg) +{ + struct device_node *np = hsotg->dev->of_node; + + /* Enable dma if requested in device tree */ + hsotg->g_using_dma = of_property_read_bool(np, "g-use-dma"); +} +#else +static inline void s3c_hsotg_of_probe(struct dwc2_hsotg *hsotg) { } +#endif + /** * dwc2_gadget_init - init function for gadget * @dwc2: The data structure for the DWC2 driver. @@ -3419,6 +3431,8 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) /* Set default UTMI width */ hsotg->phyif = GUSBCFG_PHYIF16; + s3c_hsotg_of_probe(hsotg); + /* * If platform probe couldn't find a generic PHY or an old style * USB PHY, fall back to pdata From b2f93ef30fc64dab53f64bcabe23d9377abf454e Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Fri, 9 Jan 2015 13:38:49 +0100 Subject: [PATCH 081/214] Documentation: dt-bindings: add dt binding info for dwc2 g-use-dma Indicate if gadget driver must use dma for usb transfers. Tested-by: Robert Baldyga Acked-by: Paul Zimmerman Signed-off-by: Gregory Herrero Signed-off-by: Felipe Balbi --- Documentation/devicetree/bindings/usb/dwc2.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/usb/dwc2.txt b/Documentation/devicetree/bindings/usb/dwc2.txt index 482f815363ef..a7a9cb647a0b 100644 --- a/Documentation/devicetree/bindings/usb/dwc2.txt +++ b/Documentation/devicetree/bindings/usb/dwc2.txt @@ -20,6 +20,7 @@ Optional properties: Refer to phy/phy-bindings.txt for generic phy consumer properties - dr_mode: shall be one of "host", "peripheral" and "otg" Refer to usb/generic.txt +- g-use-dma: enable dma usage in gadget driver. Example: From c6f5c050e2a7f6776ffa5594b6b2eedbf0137fe8 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Fri, 9 Jan 2015 13:38:50 +0100 Subject: [PATCH 082/214] usb: dwc2: gadget: add bi-directional endpoint support GHWCFG1 provides hardware configuration of each endpoint. Use it to configure the endpoints instead of assuming all even endpoint are OUT and all odd endpoints are IN. Tested-by: Robert Baldyga Acked-by: Paul Zimmerman Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/core.h | 3 +- drivers/usb/dwc2/gadget.c | 328 ++++++++++++++++++++++++-------------- 2 files changed, 211 insertions(+), 120 deletions(-) diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 376a008ef437..7a0309d760d8 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -696,7 +696,8 @@ struct dwc2_hsotg { unsigned int connected:1; unsigned int setup:1; unsigned long last_rst; - struct s3c_hsotg_ep *eps; + struct s3c_hsotg_ep *eps_in[MAX_EPS_CHANNELS]; + struct s3c_hsotg_ep *eps_out[MAX_EPS_CHANNELS]; u32 g_using_dma; #endif /* CONFIG_USB_DWC2_PERIPHERAL || CONFIG_USB_DWC2_DUAL_ROLE */ }; diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index c24cb3bef06a..530c2468dd66 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -65,6 +65,15 @@ static inline void __bic32(void __iomem *ptr, u32 val) writel(readl(ptr) & ~val, ptr); } +static inline struct s3c_hsotg_ep *index_to_ep(struct dwc2_hsotg *hsotg, + u32 ep_index, u32 dir_in) +{ + if (dir_in) + return hsotg->eps_in[ep_index]; + else + return hsotg->eps_out[ep_index]; +} + /* forward declaration of functions */ static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg); @@ -819,7 +828,7 @@ static void s3c_hsotg_complete_oursetup(struct usb_ep *ep, static struct s3c_hsotg_ep *ep_from_windex(struct dwc2_hsotg *hsotg, u32 windex) { - struct s3c_hsotg_ep *ep = &hsotg->eps[windex & 0x7F]; + struct s3c_hsotg_ep *ep; int dir = (windex & USB_DIR_IN) ? 1 : 0; int idx = windex & 0x7F; @@ -829,6 +838,8 @@ static struct s3c_hsotg_ep *ep_from_windex(struct dwc2_hsotg *hsotg, if (idx > hsotg->num_of_eps) return NULL; + ep = index_to_ep(hsotg, idx, dir); + if (idx && ep->dir_in != dir) return NULL; @@ -889,7 +900,7 @@ static int s3c_hsotg_send_reply(struct dwc2_hsotg *hsotg, static int s3c_hsotg_process_req_status(struct dwc2_hsotg *hsotg, struct usb_ctrlrequest *ctrl) { - struct s3c_hsotg_ep *ep0 = &hsotg->eps[0]; + struct s3c_hsotg_ep *ep0 = hsotg->eps_out[0]; struct s3c_hsotg_ep *ep; __le16 reply; int ret; @@ -960,7 +971,7 @@ static struct s3c_hsotg_req *get_ep_head(struct s3c_hsotg_ep *hs_ep) static int s3c_hsotg_process_req_feature(struct dwc2_hsotg *hsotg, struct usb_ctrlrequest *ctrl) { - struct s3c_hsotg_ep *ep0 = &hsotg->eps[0]; + struct s3c_hsotg_ep *ep0 = hsotg->eps_out[0]; struct s3c_hsotg_req *hs_req; bool restart; bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE); @@ -1040,7 +1051,7 @@ static void s3c_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg); */ static void s3c_hsotg_stall_ep0(struct dwc2_hsotg *hsotg) { - struct s3c_hsotg_ep *ep0 = &hsotg->eps[0]; + struct s3c_hsotg_ep *ep0 = hsotg->eps_out[0]; u32 reg; u32 ctrl; @@ -1080,7 +1091,7 @@ static void s3c_hsotg_stall_ep0(struct dwc2_hsotg *hsotg) static void s3c_hsotg_process_control(struct dwc2_hsotg *hsotg, struct usb_ctrlrequest *ctrl) { - struct s3c_hsotg_ep *ep0 = &hsotg->eps[0]; + struct s3c_hsotg_ep *ep0 = hsotg->eps_out[0]; int ret = 0; u32 dcfg; @@ -1201,9 +1212,9 @@ static void s3c_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg) return; } - hsotg->eps[0].dir_in = 0; + hsotg->eps_out[0]->dir_in = 0; - ret = s3c_hsotg_ep_queue(&hsotg->eps[0].ep, req, GFP_ATOMIC); + ret = s3c_hsotg_ep_queue(&hsotg->eps_out[0]->ep, req, GFP_ATOMIC); if (ret < 0) { dev_err(hsotg->dev, "%s: failed queue (%d)\n", __func__, ret); /* @@ -1293,7 +1304,7 @@ static void s3c_hsotg_complete_request(struct dwc2_hsotg *hsotg, */ static void s3c_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size) { - struct s3c_hsotg_ep *hs_ep = &hsotg->eps[ep_idx]; + struct s3c_hsotg_ep *hs_ep = hsotg->eps_out[ep_idx]; struct s3c_hsotg_req *hs_req = hs_ep->req; void __iomem *fifo = hsotg->regs + EPFIFO(ep_idx); int to_read; @@ -1367,13 +1378,14 @@ static void s3c_hsotg_send_zlp(struct dwc2_hsotg *hsotg, } if (req->req.length == 0) { - hsotg->eps[0].sent_zlp = 1; + hsotg->eps_out[0]->sent_zlp = 1; s3c_hsotg_enqueue_setup(hsotg); return; } - hsotg->eps[0].dir_in = 1; - hsotg->eps[0].sent_zlp = 1; + /* eps_out[0] is used in both directions */ + hsotg->eps_out[0]->dir_in = 1; + hsotg->eps_out[0]->sent_zlp = 1; dev_dbg(hsotg->dev, "sending zero-length packet\n"); @@ -1402,7 +1414,7 @@ static void s3c_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, int epnum, bool was_setup) { u32 epsize = readl(hsotg->regs + DOEPTSIZ(epnum)); - struct s3c_hsotg_ep *hs_ep = &hsotg->eps[epnum]; + struct s3c_hsotg_ep *hs_ep = hsotg->eps_out[epnum]; struct s3c_hsotg_req *hs_req = hs_ep->req; struct usb_request *req = &hs_req->req; unsigned size_left = DXEPTSIZ_XFERSIZE_GET(epsize); @@ -1591,14 +1603,18 @@ static u32 s3c_hsotg_ep0_mps(unsigned int mps) * the hardware control registers to reflect this. */ static void s3c_hsotg_set_ep_maxpacket(struct dwc2_hsotg *hsotg, - unsigned int ep, unsigned int mps) + unsigned int ep, unsigned int mps, unsigned int dir_in) { - struct s3c_hsotg_ep *hs_ep = &hsotg->eps[ep]; + struct s3c_hsotg_ep *hs_ep; void __iomem *regs = hsotg->regs; u32 mpsval; u32 mcval; u32 reg; + hs_ep = index_to_ep(hsotg, ep, dir_in); + if (!hs_ep) + return; + if (ep == 0) { /* EP0 is a special case */ mpsval = s3c_hsotg_ep0_mps(mps); @@ -1617,17 +1633,12 @@ static void s3c_hsotg_set_ep_maxpacket(struct dwc2_hsotg *hsotg, hs_ep->ep.maxpacket = mpsval; } - /* - * update both the in and out endpoint controldir_ registers, even - * if one of the directions may not be in use. - */ - - reg = readl(regs + DIEPCTL(ep)); - reg &= ~DXEPCTL_MPS_MASK; - reg |= mpsval; - writel(reg, regs + DIEPCTL(ep)); - - if (ep) { + if (dir_in) { + reg = readl(regs + DIEPCTL(ep)); + reg &= ~DXEPCTL_MPS_MASK; + reg |= mpsval; + writel(reg, regs + DIEPCTL(ep)); + } else { reg = readl(regs + DOEPCTL(ep)); reg &= ~DXEPCTL_MPS_MASK; reg |= mpsval; @@ -1727,7 +1738,7 @@ static void s3c_hsotg_complete_in(struct dwc2_hsotg *hsotg, } /* Finish ZLP handling for IN EP0 transactions */ - if (hsotg->eps[0].sent_zlp) { + if (hsotg->eps_out[0]->sent_zlp) { dev_dbg(hsotg->dev, "zlp packet received\n"); s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0); return; @@ -1794,7 +1805,7 @@ static void s3c_hsotg_complete_in(struct dwc2_hsotg *hsotg, static void s3c_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx, int dir_in) { - struct s3c_hsotg_ep *hs_ep = &hsotg->eps[idx]; + struct s3c_hsotg_ep *hs_ep = index_to_ep(hsotg, idx, dir_in); u32 epint_reg = dir_in ? DIEPINT(idx) : DOEPINT(idx); u32 epctl_reg = dir_in ? DIEPCTL(idx) : DOEPCTL(idx); u32 epsiz_reg = dir_in ? DIEPTSIZ(idx) : DOEPTSIZ(idx); @@ -1807,6 +1818,12 @@ static void s3c_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx, /* Clear endpoint interrupts */ writel(ints, hsotg->regs + epint_reg); + if (!hs_ep) { + dev_err(hsotg->dev, "%s:Interrupt for unconfigured ep%d(%s)\n", + __func__, idx, dir_in ? "in" : "out"); + return; + } + dev_dbg(hsotg->dev, "%s: ep%d(%s) DxEPINT=0x%08x\n", __func__, idx, dir_in ? "in" : "out", ints); @@ -1973,9 +1990,15 @@ static void s3c_hsotg_irq_enumdone(struct dwc2_hsotg *hsotg) if (ep0_mps) { int i; - s3c_hsotg_set_ep_maxpacket(hsotg, 0, ep0_mps); - for (i = 1; i < hsotg->num_of_eps; i++) - s3c_hsotg_set_ep_maxpacket(hsotg, i, ep_mps); + /* Initialize ep0 for both in and out directions */ + s3c_hsotg_set_ep_maxpacket(hsotg, 0, ep0_mps, 1); + s3c_hsotg_set_ep_maxpacket(hsotg, 0, ep0_mps, 0); + for (i = 1; i < hsotg->num_of_eps; i++) { + if (hsotg->eps_in[i]) + s3c_hsotg_set_ep_maxpacket(hsotg, i, ep_mps, 1); + if (hsotg->eps_out[i]) + s3c_hsotg_set_ep_maxpacket(hsotg, i, ep_mps, 0); + } } /* ensure after enumeration our EP0 is active */ @@ -2032,8 +2055,15 @@ void s3c_hsotg_disconnect(struct dwc2_hsotg *hsotg) return; hsotg->connected = 0; - for (ep = 0; ep < hsotg->num_of_eps; ep++) - kill_all_requests(hsotg, &hsotg->eps[ep], -ESHUTDOWN); + + for (ep = 0; ep < hsotg->num_of_eps; ep++) { + if (hsotg->eps_in[ep]) + kill_all_requests(hsotg, hsotg->eps_in[ep], + -ESHUTDOWN); + if (hsotg->eps_out[ep]) + kill_all_requests(hsotg, hsotg->eps_out[ep], + -ESHUTDOWN); + } call_gadget(hsotg, disconnect); } @@ -2050,9 +2080,11 @@ static void s3c_hsotg_irq_fifoempty(struct dwc2_hsotg *hsotg, bool periodic) int epno, ret; /* look through for any more data to transmit */ - for (epno = 0; epno < hsotg->num_of_eps; epno++) { - ep = &hsotg->eps[epno]; + ep = index_to_ep(hsotg, epno, 1); + + if (!ep) + continue; if (!ep->dir_in) continue; @@ -2227,13 +2259,13 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg) writel(DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) | DXEPTSIZ_XFERSIZE(8), hsotg->regs + DOEPTSIZ0); - writel(s3c_hsotg_ep0_mps(hsotg->eps[0].ep.maxpacket) | + writel(s3c_hsotg_ep0_mps(hsotg->eps_out[0]->ep.maxpacket) | DXEPCTL_CNAK | DXEPCTL_EPENA | DXEPCTL_USBACTEP, hsotg->regs + DOEPCTL0); /* enable, but don't activate EP0in */ - writel(s3c_hsotg_ep0_mps(hsotg->eps[0].ep.maxpacket) | + writel(s3c_hsotg_ep0_mps(hsotg->eps_out[0]->ep.maxpacket) | DXEPCTL_USBACTEP, hsotg->regs + DIEPCTL0); s3c_hsotg_enqueue_setup(hsotg); @@ -2330,7 +2362,7 @@ irq_retry: if (time_after(jiffies, hsotg->last_rst + msecs_to_jiffies(200))) { - kill_all_requests(hsotg, &hsotg->eps[0], + kill_all_requests(hsotg, hsotg->eps_out[0], -ECONNRESET); s3c_hsotg_core_init_disconnected(hsotg); @@ -2479,7 +2511,7 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep, epctrl |= DXEPCTL_SNAK; /* update the endpoint state */ - s3c_hsotg_set_ep_maxpacket(hsotg, hs_ep->index, mps); + s3c_hsotg_set_ep_maxpacket(hsotg, hs_ep->index, mps, dir_in); /* default, set to non-periodic */ hs_ep->isochronous = 0; @@ -2576,7 +2608,7 @@ static int s3c_hsotg_ep_disable(struct usb_ep *ep) dev_dbg(hsotg->dev, "%s(ep %p)\n", __func__, ep); - if (ep == &hsotg->eps[0].ep) { + if (ep == &hsotg->eps_out[0]->ep) { dev_err(hsotg->dev, "%s: called for ep0\n", __func__); return -EINVAL; } @@ -2675,40 +2707,39 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value) return 0; } - /* write both IN and OUT control registers */ + if (hs_ep->dir_in) { + epreg = DIEPCTL(index); + epctl = readl(hs->regs + epreg); - epreg = DIEPCTL(index); - epctl = readl(hs->regs + epreg); - - if (value) { - epctl |= DXEPCTL_STALL + DXEPCTL_SNAK; - if (epctl & DXEPCTL_EPENA) - epctl |= DXEPCTL_EPDIS; + if (value) { + epctl |= DXEPCTL_STALL + DXEPCTL_SNAK; + if (epctl & DXEPCTL_EPENA) + epctl |= DXEPCTL_EPDIS; + } else { + epctl &= ~DXEPCTL_STALL; + xfertype = epctl & DXEPCTL_EPTYPE_MASK; + if (xfertype == DXEPCTL_EPTYPE_BULK || + xfertype == DXEPCTL_EPTYPE_INTERRUPT) + epctl |= DXEPCTL_SETD0PID; + } + writel(epctl, hs->regs + epreg); } else { - epctl &= ~DXEPCTL_STALL; - xfertype = epctl & DXEPCTL_EPTYPE_MASK; - if (xfertype == DXEPCTL_EPTYPE_BULK || - xfertype == DXEPCTL_EPTYPE_INTERRUPT) - epctl |= DXEPCTL_SETD0PID; + + epreg = DOEPCTL(index); + epctl = readl(hs->regs + epreg); + + if (value) + epctl |= DXEPCTL_STALL; + else { + epctl &= ~DXEPCTL_STALL; + xfertype = epctl & DXEPCTL_EPTYPE_MASK; + if (xfertype == DXEPCTL_EPTYPE_BULK || + xfertype == DXEPCTL_EPTYPE_INTERRUPT) + epctl |= DXEPCTL_SETD0PID; + } + writel(epctl, hs->regs + epreg); } - writel(epctl, hs->regs + epreg); - - epreg = DOEPCTL(index); - epctl = readl(hs->regs + epreg); - - if (value) - epctl |= DXEPCTL_STALL; - else { - epctl &= ~DXEPCTL_STALL; - xfertype = epctl & DXEPCTL_EPTYPE_MASK; - if (xfertype == DXEPCTL_EPTYPE_BULK || - xfertype == DXEPCTL_EPTYPE_INTERRUPT) - epctl |= DXEPCTL_SETD0PID; - } - - writel(epctl, hs->regs + epreg); - hs_ep->halted = value; return 0; @@ -2922,8 +2953,12 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget) mutex_lock(&hsotg->init_mutex); /* all endpoints should be shutdown */ - for (ep = 1; ep < hsotg->num_of_eps; ep++) - s3c_hsotg_ep_disable(&hsotg->eps[ep].ep); + for (ep = 1; ep < hsotg->num_of_eps; ep++) { + if (hsotg->eps_in[ep]) + s3c_hsotg_ep_disable(&hsotg->eps_in[ep]->ep); + if (hsotg->eps_out[ep]) + s3c_hsotg_ep_disable(&hsotg->eps_out[ep]->ep); + } spin_lock_irqsave(&hsotg->lock, flags); @@ -3009,19 +3044,19 @@ static const struct usb_gadget_ops s3c_hsotg_gadget_ops = { */ static void s3c_hsotg_initep(struct dwc2_hsotg *hsotg, struct s3c_hsotg_ep *hs_ep, - int epnum) + int epnum, + bool dir_in) { char *dir; if (epnum == 0) dir = ""; - else if ((epnum % 2) == 0) { - dir = "out"; - } else { + else if (dir_in) dir = "in"; - hs_ep->dir_in = 1; - } + else + dir = "out"; + hs_ep->dir_in = dir_in; hs_ep->index = epnum; snprintf(hs_ep->name, sizeof(hs_ep->name), "ep%d%s", epnum, dir); @@ -3045,8 +3080,10 @@ static void s3c_hsotg_initep(struct dwc2_hsotg *hsotg, if (using_dma(hsotg)) { u32 next = DXEPCTL_NEXTEP((epnum + 1) % 15); - writel(next, hsotg->regs + DIEPCTL(epnum)); - writel(next, hsotg->regs + DOEPCTL(epnum)); + if (dir_in) + writel(next, hsotg->regs + DIEPCTL(epnum)); + else + writel(next, hsotg->regs + DOEPCTL(epnum)); } } @@ -3056,24 +3093,56 @@ static void s3c_hsotg_initep(struct dwc2_hsotg *hsotg, * * Read the USB core HW configuration registers */ -static void s3c_hsotg_hw_cfg(struct dwc2_hsotg *hsotg) +static int s3c_hsotg_hw_cfg(struct dwc2_hsotg *hsotg) { - u32 cfg2, cfg3, cfg4; + u32 cfg; + u32 ep_type; + u32 i; + /* check hardware configuration */ - cfg2 = readl(hsotg->regs + 0x48); - hsotg->num_of_eps = (cfg2 >> 10) & 0xF; + cfg = readl(hsotg->regs + GHWCFG2); + hsotg->num_of_eps = (cfg >> 10) & 0xF; + /* Add ep0 */ + hsotg->num_of_eps++; - cfg3 = readl(hsotg->regs + 0x4C); - hsotg->fifo_mem = (cfg3 >> 16); + hsotg->eps_in[0] = devm_kzalloc(hsotg->dev, sizeof(struct s3c_hsotg_ep), + GFP_KERNEL); + if (!hsotg->eps_in[0]) + return -ENOMEM; + /* Same s3c_hsotg_ep is used in both directions for ep0 */ + hsotg->eps_out[0] = hsotg->eps_in[0]; - cfg4 = readl(hsotg->regs + 0x50); - hsotg->dedicated_fifos = (cfg4 >> 25) & 1; + cfg = readl(hsotg->regs + GHWCFG1); + for (i = 1; i < hsotg->num_of_eps; i++, cfg >>= 2) { + ep_type = cfg & 3; + /* Direction in or both */ + if (!(ep_type & 2)) { + hsotg->eps_in[i] = devm_kzalloc(hsotg->dev, + sizeof(struct s3c_hsotg_ep), GFP_KERNEL); + if (!hsotg->eps_in[i]) + return -ENOMEM; + } + /* Direction out or both */ + if (!(ep_type & 1)) { + hsotg->eps_out[i] = devm_kzalloc(hsotg->dev, + sizeof(struct s3c_hsotg_ep), GFP_KERNEL); + if (!hsotg->eps_out[i]) + return -ENOMEM; + } + } + + cfg = readl(hsotg->regs + GHWCFG3); + hsotg->fifo_mem = (cfg >> 16); + + cfg = readl(hsotg->regs + GHWCFG4); + hsotg->dedicated_fifos = (cfg >> 25) & 1; dev_info(hsotg->dev, "EPs: %d, %s fifos, %d entries in SPRAM\n", hsotg->num_of_eps, hsotg->dedicated_fifos ? "dedicated" : "shared", hsotg->fifo_mem); + return 0; } /** @@ -3368,17 +3437,33 @@ static void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg) if (IS_ERR(hsotg->debug_fifo)) dev_err(hsotg->dev, "%s: failed to create fifo\n", __func__); - /* create one file for each endpoint */ - + /* Create one file for each out endpoint */ for (epidx = 0; epidx < hsotg->num_of_eps; epidx++) { - struct s3c_hsotg_ep *ep = &hsotg->eps[epidx]; + struct s3c_hsotg_ep *ep; - ep->debugfs = debugfs_create_file(ep->name, 0444, - root, ep, &ep_fops); + ep = hsotg->eps_out[epidx]; + if (ep) { + ep->debugfs = debugfs_create_file(ep->name, 0444, + root, ep, &ep_fops); - if (IS_ERR(ep->debugfs)) - dev_err(hsotg->dev, "failed to create %s debug file\n", - ep->name); + if (IS_ERR(ep->debugfs)) + dev_err(hsotg->dev, "failed to create %s debug file\n", + ep->name); + } + } + /* Create one file for each in endpoint. EP0 is handled with out eps */ + for (epidx = 1; epidx < hsotg->num_of_eps; epidx++) { + struct s3c_hsotg_ep *ep; + + ep = hsotg->eps_in[epidx]; + if (ep) { + ep->debugfs = debugfs_create_file(ep->name, 0444, + root, ep, &ep_fops); + + if (IS_ERR(ep->debugfs)) + dev_err(hsotg->dev, "failed to create %s debug file\n", + ep->name); + } } } @@ -3393,8 +3478,10 @@ static void s3c_hsotg_delete_debug(struct dwc2_hsotg *hsotg) unsigned epidx; for (epidx = 0; epidx < hsotg->num_of_eps; epidx++) { - struct s3c_hsotg_ep *ep = &hsotg->eps[epidx]; - debugfs_remove(ep->debugfs); + if (hsotg->eps_in[epidx]) + debugfs_remove(hsotg->eps_in[epidx]->debugfs); + if (hsotg->eps_out[epidx]) + debugfs_remove(hsotg->eps_out[epidx]->debugfs); } debugfs_remove(hsotg->debug_file); @@ -3423,7 +3510,6 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) { struct device *dev = hsotg->dev; struct s3c_hsotg_plat *plat = dev->platform_data; - struct s3c_hsotg_ep *eps; int epnum; int ret; int i; @@ -3497,7 +3583,12 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) s3c_hsotg_phy_enable(hsotg); s3c_hsotg_corereset(hsotg); - s3c_hsotg_hw_cfg(hsotg); + ret = s3c_hsotg_hw_cfg(hsotg); + if (ret) { + dev_err(hsotg->dev, "Hardware configuration failed: %d\n", ret); + goto err_clk; + } + s3c_hsotg_init(hsotg); hsotg->ctrl_buff = devm_kzalloc(hsotg->dev, @@ -3535,33 +3626,30 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) goto err_supplies; } - eps = kcalloc(hsotg->num_of_eps + 1, sizeof(struct s3c_hsotg_ep), - GFP_KERNEL); - if (!eps) { - ret = -ENOMEM; - goto err_supplies; - } - - hsotg->eps = eps; - /* setup endpoint information */ INIT_LIST_HEAD(&hsotg->gadget.ep_list); - hsotg->gadget.ep0 = &hsotg->eps[0].ep; + hsotg->gadget.ep0 = &hsotg->eps_out[0]->ep; /* allocate EP0 request */ - hsotg->ctrl_req = s3c_hsotg_ep_alloc_request(&hsotg->eps[0].ep, + hsotg->ctrl_req = s3c_hsotg_ep_alloc_request(&hsotg->eps_out[0]->ep, GFP_KERNEL); if (!hsotg->ctrl_req) { dev_err(dev, "failed to allocate ctrl req\n"); ret = -ENOMEM; - goto err_ep_mem; + goto err_supplies; } /* initialise the endpoints now the core has been initialised */ - for (epnum = 0; epnum < hsotg->num_of_eps; epnum++) - s3c_hsotg_initep(hsotg, &hsotg->eps[epnum], epnum); + for (epnum = 0; epnum < hsotg->num_of_eps; epnum++) { + if (hsotg->eps_in[epnum]) + s3c_hsotg_initep(hsotg, hsotg->eps_in[epnum], + epnum, 1); + if (hsotg->eps_out[epnum]) + s3c_hsotg_initep(hsotg, hsotg->eps_out[epnum], + epnum, 0); + } /* disable power and clock */ s3c_hsotg_phy_disable(hsotg); @@ -3570,12 +3658,12 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) hsotg->supplies); if (ret) { dev_err(dev, "failed to disable supplies: %d\n", ret); - goto err_ep_mem; + goto err_supplies; } ret = usb_add_gadget_udc(dev, &hsotg->gadget); if (ret) - goto err_ep_mem; + goto err_supplies; s3c_hsotg_create_debug(hsotg); @@ -3583,8 +3671,6 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) return 0; -err_ep_mem: - kfree(eps); err_supplies: s3c_hsotg_phy_disable(hsotg); err_clk: @@ -3630,8 +3716,12 @@ int s3c_hsotg_suspend(struct dwc2_hsotg *hsotg) s3c_hsotg_phy_disable(hsotg); - for (ep = 0; ep < hsotg->num_of_eps; ep++) - s3c_hsotg_ep_disable(&hsotg->eps[ep].ep); + for (ep = 0; ep < hsotg->num_of_eps; ep++) { + if (hsotg->eps_in[ep]) + s3c_hsotg_ep_disable(&hsotg->eps_in[ep]->ep); + if (hsotg->eps_out[ep]) + s3c_hsotg_ep_disable(&hsotg->eps_out[ep]->ep); + } ret = regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies); From cec87f1d2686057654e4565b8eaab4f539e9afca Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Fri, 9 Jan 2015 13:38:51 +0100 Subject: [PATCH 083/214] usb: dwc2: gadget: check interrupts for all endpoints Current code does not check endpoint 15 interrupt. Use number of endpoint configured in hardware instead of the hardcoded value. Tested-by: Robert Baldyga Acked-by: Paul Zimmerman Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 530c2468dd66..b7879fd82674 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2337,12 +2337,14 @@ irq_retry: dev_dbg(hsotg->dev, "%s: daint=%08x\n", __func__, daint); - for (ep = 0; ep < 15 && daint_out; ep++, daint_out >>= 1) { + for (ep = 0; ep < hsotg->num_of_eps && daint_out; + ep++, daint_out >>= 1) { if (daint_out & 1) s3c_hsotg_epint(hsotg, ep, 0); } - for (ep = 0; ep < 15 && daint_in; ep++, daint_in >>= 1) { + for (ep = 0; ep < hsotg->num_of_eps && daint_in; + ep++, daint_in >>= 1) { if (daint_in & 1) s3c_hsotg_epint(hsotg, ep, 1); } From 0a176279db6817cf865ed4c685e4a414be8226fd Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Fri, 9 Jan 2015 13:38:52 +0100 Subject: [PATCH 084/214] usb: dwc2: gadget: configure fifos from device tree As fifo size can vary between SOCs, add possibility to configure them from device tree. Fifo sizes used by the legacy driver will be used If they are not provided by the device tree. Tested-by: Robert Baldyga Acked-by: Paul Zimmerman Signed-off-by: Gregory Herrero Signed-off-by: Mian Yousaf Kaukab Reviewed-by: Robert Baldyga Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/core.h | 13 ++++++ drivers/usb/dwc2/gadget.c | 88 ++++++++++++++++++++++++++------------- 2 files changed, 73 insertions(+), 28 deletions(-) diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 7a0309d760d8..df6a64f463e8 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -193,6 +193,13 @@ enum dwc2_lx_state { DWC2_L3, /* Off state */ }; +/* + * Gadget periodic tx fifo sizes as used by legacy driver + * EP0 is not included + */ +#define DWC2_G_P_LEGACY_TX_FIFO_SIZE {256, 256, 256, 256, 768, 768, 768, \ + 768, 0, 0, 0, 0, 0, 0, 0} + /** * struct dwc2_core_params - Parameters for configuring the core * @@ -564,6 +571,9 @@ struct dwc2_hw_params { * @last_rst: Time of last reset * @eps: The endpoints being supplied to the gadget framework * @g_using_dma: Indicate if dma usage is enabled + * @g_rx_fifo_sz: Contains rx fifo size value + * @g_np_g_tx_fifo_sz: Contains Non-Periodic tx fifo size value + * @g_tx_fifo_sz: Contains tx fifo size value per endpoints */ struct dwc2_hsotg { struct device *dev; @@ -699,6 +709,9 @@ struct dwc2_hsotg { struct s3c_hsotg_ep *eps_in[MAX_EPS_CHANNELS]; struct s3c_hsotg_ep *eps_out[MAX_EPS_CHANNELS]; u32 g_using_dma; + u32 g_rx_fifo_sz; + u32 g_np_g_tx_fifo_sz; + u32 g_tx_fifo_sz[MAX_EPS_CHANNELS]; #endif /* CONFIG_USB_DWC2_PERIPHERAL || CONFIG_USB_DWC2_DUAL_ROLE */ }; diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index b7879fd82674..c43ce7e8809a 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -174,15 +174,14 @@ static void s3c_hsotg_init_fifo(struct dwc2_hsotg *hsotg) { unsigned int ep; unsigned int addr; - unsigned int size; int timeout; u32 val; - /* set FIFO sizes to 2048/1024 */ - - writel(2048, hsotg->regs + GRXFSIZ); - writel((2048 << FIFOSIZE_STARTADDR_SHIFT) | - (1024 << FIFOSIZE_DEPTH_SHIFT), hsotg->regs + GNPTXFSIZ); + /* set RX/NPTX FIFO sizes */ + writel(hsotg->g_rx_fifo_sz, hsotg->regs + GRXFSIZ); + writel((hsotg->g_rx_fifo_sz << FIFOSIZE_STARTADDR_SHIFT) | + (hsotg->g_np_g_tx_fifo_sz << FIFOSIZE_DEPTH_SHIFT), + hsotg->regs + GNPTXFSIZ); /* * arange all the rest of the TX FIFOs, as some versions of this @@ -192,35 +191,21 @@ static void s3c_hsotg_init_fifo(struct dwc2_hsotg *hsotg) */ /* start at the end of the GNPTXFSIZ, rounded up */ - addr = 2048 + 1024; + addr = hsotg->g_rx_fifo_sz + hsotg->g_np_g_tx_fifo_sz; /* - * Because we have not enough memory to have each TX FIFO of size at - * least 3072 bytes (the maximum single packet size), we create four - * FIFOs of lenght 1024, and four of length 3072 bytes, and assing + * Configure fifos sizes from provided configuration and assign * them to endpoints dynamically according to maxpacket size value of * given endpoint. */ - - /* 256*4=1024 bytes FIFO length */ - size = 256; - for (ep = 1; ep <= 4; ep++) { + for (ep = 1; ep < MAX_EPS_CHANNELS; ep++) { + if (!hsotg->g_tx_fifo_sz[ep]) + continue; val = addr; - val |= size << FIFOSIZE_DEPTH_SHIFT; - WARN_ONCE(addr + size > hsotg->fifo_mem, + val |= hsotg->g_tx_fifo_sz[ep] << FIFOSIZE_DEPTH_SHIFT; + WARN_ONCE(addr + hsotg->g_tx_fifo_sz[ep] > hsotg->fifo_mem, "insufficient fifo memory"); - addr += size; - - writel(val, hsotg->regs + DPTXFSIZN(ep)); - } - /* 768*4=3072 bytes FIFO length */ - size = 768; - for (ep = 5; ep <= 8; ep++) { - val = addr; - val |= size << FIFOSIZE_DEPTH_SHIFT; - WARN_ONCE(addr + size > hsotg->fifo_mem, - "insufficient fifo memory"); - addr += size; + addr += hsotg->g_tx_fifo_sz[ep]; writel(val, hsotg->regs + DPTXFSIZN(ep)); } @@ -3495,9 +3480,42 @@ static void s3c_hsotg_delete_debug(struct dwc2_hsotg *hsotg) static void s3c_hsotg_of_probe(struct dwc2_hsotg *hsotg) { struct device_node *np = hsotg->dev->of_node; + u32 len = 0; + u32 i = 0; /* Enable dma if requested in device tree */ hsotg->g_using_dma = of_property_read_bool(np, "g-use-dma"); + + /* + * Register TX periodic fifo size per endpoint. + * EP0 is excluded since it has no fifo configuration. + */ + if (!of_find_property(np, "g-tx-fifo-size", &len)) + goto rx_fifo; + + len /= sizeof(u32); + + /* Read tx fifo sizes other than ep0 */ + if (of_property_read_u32_array(np, "g-tx-fifo-size", + &hsotg->g_tx_fifo_sz[1], len)) + goto rx_fifo; + + /* Add ep0 */ + len++; + + /* Make remaining TX fifos unavailable */ + if (len < MAX_EPS_CHANNELS) { + for (i = len; i < MAX_EPS_CHANNELS; i++) + hsotg->g_tx_fifo_sz[i] = 0; + } + +rx_fifo: + /* Register RX fifo size */ + of_property_read_u32(np, "g-rx-fifo-size", &hsotg->g_rx_fifo_sz); + + /* Register NPTX fifo size */ + of_property_read_u32(np, "g-np-tx-fifo-size", + &hsotg->g_np_g_tx_fifo_sz); } #else static inline void s3c_hsotg_of_probe(struct dwc2_hsotg *hsotg) { } @@ -3515,12 +3533,26 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) int epnum; int ret; int i; + u32 p_tx_fifo[] = DWC2_G_P_LEGACY_TX_FIFO_SIZE; /* Set default UTMI width */ hsotg->phyif = GUSBCFG_PHYIF16; s3c_hsotg_of_probe(hsotg); + /* Initialize to legacy fifo configuration values */ + hsotg->g_rx_fifo_sz = 2048; + hsotg->g_np_g_tx_fifo_sz = 1024; + memcpy(&hsotg->g_tx_fifo_sz[1], p_tx_fifo, sizeof(p_tx_fifo)); + /* Device tree specific probe */ + s3c_hsotg_of_probe(hsotg); + /* Dump fifo information */ + dev_dbg(dev, "NonPeriodic TXFIFO size: %d\n", + hsotg->g_np_g_tx_fifo_sz); + dev_dbg(dev, "RXFIFO size: %d\n", hsotg->g_rx_fifo_sz); + for (i = 0; i < MAX_EPS_CHANNELS; i++) + dev_dbg(dev, "Periodic TXFIFO%2d size: %d\n", i, + hsotg->g_tx_fifo_sz[i]); /* * If platform probe couldn't find a generic PHY or an old style * USB PHY, fall back to pdata From bb8693a98ebbe863401108d1cc4f8c0bc72e1900 Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Fri, 9 Jan 2015 13:38:53 +0100 Subject: [PATCH 085/214] Documentation: dt-bindings: add dt binding info for dwc2 fifo resizing Tested-by: Robert Baldyga Acked-by: Paul Zimmerman Signed-off-by: Gregory Herrero Signed-off-by: Felipe Balbi --- Documentation/devicetree/bindings/usb/dwc2.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/usb/dwc2.txt b/Documentation/devicetree/bindings/usb/dwc2.txt index a7a9cb647a0b..fd132cbee70e 100644 --- a/Documentation/devicetree/bindings/usb/dwc2.txt +++ b/Documentation/devicetree/bindings/usb/dwc2.txt @@ -21,6 +21,9 @@ Refer to phy/phy-bindings.txt for generic phy consumer properties - dr_mode: shall be one of "host", "peripheral" and "otg" Refer to usb/generic.txt - g-use-dma: enable dma usage in gadget driver. +- g-rx-fifo-size: size of rx fifo size in gadget mode. +- g-np-tx-fifo-size: size of non-periodic tx fifo size in gadget mode. +- g-tx-fifo-size: size of periodic tx fifo per endpoint (except ep0) in gadget mode. Example: From ea5a8774a273c62047abb3cb5cca69c8368d05da Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Fri, 9 Jan 2015 13:38:54 +0100 Subject: [PATCH 086/214] usb: dwc2: gadget: remove unused members from hsotg_req These members are only occupying space. Tested-by: Robert Baldyga Acked-by: Paul Zimmerman Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/core.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index df6a64f463e8..db5348c21382 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -158,14 +158,10 @@ struct s3c_hsotg_ep { * struct s3c_hsotg_req - data transfer request * @req: The USB gadget request * @queue: The list of requests for the endpoint this is queued for. - * @in_progress: Has already had size/packets written to core - * @mapped: DMA buffer for this request has been mapped via dma_map_single(). */ struct s3c_hsotg_req { struct usb_request req; struct list_head queue; - unsigned char in_progress; - unsigned char mapped; }; #if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) From 364f8e933869699167db935e13b41b1d92ab369d Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Fri, 9 Jan 2015 13:38:55 +0100 Subject: [PATCH 087/214] usb: dwc2: gadget: fix debug loop limits < 15 check doesn't show debug information for endpoint 15. It is possible to have less than 15 endpoints so use limit provided by hardware configuration. Tested-by: Robert Baldyga Acked-by: Paul Zimmerman Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index c43ce7e8809a..7dfda954aba1 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -3156,14 +3156,14 @@ static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg) /* show periodic fifo settings */ - for (idx = 1; idx <= 15; idx++) { + for (idx = 1; idx < hsotg->num_of_eps; idx++) { val = readl(regs + DPTXFSIZN(idx)); dev_info(dev, "DPTx[%d] FSize=%d, StAddr=0x%08x\n", idx, val >> FIFOSIZE_DEPTH_SHIFT, val & FIFOSIZE_STARTADDR_MASK); } - for (idx = 0; idx < 15; idx++) { + for (idx = 0; idx < hsotg->num_of_eps; idx++) { dev_info(dev, "ep%d-in: EPCTL=0x%08x, SIZ=0x%08x, DMA=0x%08x\n", idx, readl(regs + DIEPCTL(idx)), @@ -3221,7 +3221,7 @@ static int state_show(struct seq_file *seq, void *v) seq_puts(seq, "\nEndpoint status:\n"); - for (idx = 0; idx < 15; idx++) { + for (idx = 0; idx < hsotg->num_of_eps; idx++) { u32 in, out; in = readl(regs + DIEPCTL(idx)); @@ -3280,7 +3280,7 @@ static int fifo_show(struct seq_file *seq, void *v) seq_puts(seq, "\nPeriodic TXFIFOs:\n"); - for (idx = 1; idx <= 15; idx++) { + for (idx = 1; idx < hsotg->num_of_eps; idx++) { val = readl(regs + DPTXFSIZN(idx)); seq_printf(seq, "\tDPTXFIFO%2d: Size %d, Start 0x%08x\n", idx, From 5f2196bd7c72cbead9d47da32816dd69a06b692e Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Fri, 9 Jan 2015 13:38:56 +0100 Subject: [PATCH 088/214] usb: dwc2: gadget: consider all tx fifos When matching tx fifo to endpoint, consider all fifos instead of hard limiting to 8 Moreover, print error in case no fifo could be found. Tested-by: Robert Baldyga Acked-by: Paul Zimmerman Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 7dfda954aba1..cd8880d6be53 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2540,7 +2540,7 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep, */ if (dir_in && hsotg->dedicated_fifos) { size = hs_ep->ep.maxpacket*hs_ep->mc; - for (i = 1; i <= 8; ++i) { + for (i = 1; i < hsotg->num_of_eps; ++i) { if (hsotg->fifo_map & (1<regs + DPTXFSIZN(i)); @@ -2554,7 +2554,9 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep, hs_ep->fifo_size = val; break; } - if (i == 8) { + if (i == hsotg->num_of_eps) { + dev_err(hsotg->dev, + "%s: No suitable fifo found\n", __func__); ret = -ENOMEM; goto error; } From 1141ea01d5faa82f4910b4a8f896d10490860f24 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Fri, 9 Jan 2015 13:38:57 +0100 Subject: [PATCH 089/214] usb: dwc2: gadget: kill requests after disabling ep kill_all_requests() can flush the fifo. Call it after disabling the endpoint. Tested-by: Robert Baldyga Acked-by: Paul Zimmerman Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index cd8880d6be53..3dda17eb0700 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2605,8 +2605,6 @@ static int s3c_hsotg_ep_disable(struct usb_ep *ep) epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index); spin_lock_irqsave(&hsotg->lock, flags); - /* terminate all requests with shutdown */ - kill_all_requests(hsotg, hs_ep, -ESHUTDOWN); hsotg->fifo_map &= ~(1<fifo_index); hs_ep->fifo_index = 0; @@ -2623,6 +2621,9 @@ static int s3c_hsotg_ep_disable(struct usb_ep *ep) /* disable endpoint interrupts */ s3c_hsotg_ctrl_epint(hsotg, hs_ep->index, hs_ep->dir_in, 0); + /* terminate all requests with shutdown */ + kill_all_requests(hsotg, hs_ep, -ESHUTDOWN); + spin_unlock_irqrestore(&hsotg->lock, flags); return 0; } From fe0b94abcdf6d80bd575e91baf1e0f85a4978d49 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Fri, 9 Jan 2015 13:38:58 +0100 Subject: [PATCH 090/214] usb: dwc2: gadget: manage ep0 state in software Manage ep0 state in software to add handling of status OUT stage. Just toggling hsotg->setup in s3c_hsotg_handle_outdone leaves it in wrong state in 2-stage control transfers. Moreover, ensure that for setup-packet s3c_hsotg_handle_outdone is called either from SetupDone or OutDone but not both. Dwc2 ip v3.00a generates both SetupDone and OutDone on setup packets. Tested-by: Robert Baldyga Acked-by: Paul Zimmerman Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/core.h | 13 +++- drivers/usb/dwc2/gadget.c | 156 +++++++++++++++++++------------------- 2 files changed, 89 insertions(+), 80 deletions(-) diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index db5348c21382..e963aef1daa2 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -196,6 +196,15 @@ enum dwc2_lx_state { #define DWC2_G_P_LEGACY_TX_FIFO_SIZE {256, 256, 256, 256, 768, 768, 768, \ 768, 0, 0, 0, 0, 0, 0, 0} +/* Gadget ep0 states */ +enum dwc2_ep0_state { + DWC2_EP0_SETUP, + DWC2_EP0_DATA_IN, + DWC2_EP0_DATA_OUT, + DWC2_EP0_STATUS_IN, + DWC2_EP0_STATUS_OUT, +}; + /** * struct dwc2_core_params - Parameters for configuring the core * @@ -563,7 +572,7 @@ struct dwc2_hw_params { * @ep0_buff: Buffer for EP0 reply data, if needed. * @ctrl_buff: Buffer for EP0 control requests. * @ctrl_req: Request for EP0 control packets. - * @setup: NAK management for EP0 SETUP + * @ep0_state: EP0 control transfers state * @last_rst: Time of last reset * @eps: The endpoints being supplied to the gadget framework * @g_using_dma: Indicate if dma usage is enabled @@ -696,11 +705,11 @@ struct dwc2_hsotg { struct usb_request *ctrl_req; void *ep0_buff; void *ctrl_buff; + enum dwc2_ep0_state ep0_state; struct usb_gadget gadget; unsigned int enabled:1; unsigned int connected:1; - unsigned int setup:1; unsigned long last_rst; struct s3c_hsotg_ep *eps_in[MAX_EPS_CHANNELS]; struct s3c_hsotg_ep *eps_out[MAX_EPS_CHANNELS]; diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 3dda17eb0700..c3977a8d70ac 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -638,15 +638,12 @@ static void s3c_hsotg_start_req(struct dwc2_hsotg *hsotg, ctrl |= DXEPCTL_EPENA; /* ensure ep enabled */ ctrl |= DXEPCTL_USBACTEP; - dev_dbg(hsotg->dev, "setup req:%d\n", hsotg->setup); + dev_dbg(hsotg->dev, "ep0 state:%d\n", hsotg->ep0_state); /* For Setup request do not clear NAK */ - if (hsotg->setup && index == 0) - hsotg->setup = 0; - else + if (!(index == 0 && hsotg->ep0_state == DWC2_EP0_SETUP)) ctrl |= DXEPCTL_CNAK; /* clear NAK set by core */ - dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x\n", __func__, ctrl); writel(ctrl, hsotg->regs + epctrl_reg); @@ -865,8 +862,6 @@ static int s3c_hsotg_send_reply(struct dwc2_hsotg *hsotg, if (length) memcpy(req->buf, buff, length); - else - ep->sent_zlp = 1; ret = s3c_hsotg_ep_queue(&ep->ep, req, GFP_ATOMIC); if (ret) { @@ -1080,26 +1075,20 @@ static void s3c_hsotg_process_control(struct dwc2_hsotg *hsotg, int ret = 0; u32 dcfg; - ep0->sent_zlp = 0; - dev_dbg(hsotg->dev, "ctrl Req=%02x, Type=%02x, V=%04x, L=%04x\n", ctrl->bRequest, ctrl->bRequestType, ctrl->wValue, ctrl->wLength); - /* - * record the direction of the request, for later use when enquing - * packets onto EP0. - */ - - ep0->dir_in = (ctrl->bRequestType & USB_DIR_IN) ? 1 : 0; - dev_dbg(hsotg->dev, "ctrl: dir_in=%d\n", ep0->dir_in); - - /* - * if we've no data with this request, then the last part of the - * transaction is going to implicitly be IN. - */ - if (ctrl->wLength == 0) + if (ctrl->wLength == 0) { ep0->dir_in = 1; + hsotg->ep0_state = DWC2_EP0_STATUS_IN; + } else if (ctrl->bRequestType & USB_DIR_IN) { + ep0->dir_in = 1; + hsotg->ep0_state = DWC2_EP0_DATA_IN; + } else { + ep0->dir_in = 0; + hsotg->ep0_state = DWC2_EP0_DATA_OUT; + } if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { switch (ctrl->bRequest) { @@ -1198,6 +1187,8 @@ static void s3c_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg) } hsotg->eps_out[0]->dir_in = 0; + hsotg->eps_out[0]->sent_zlp = 0; + hsotg->ep0_state = DWC2_EP0_SETUP; ret = s3c_hsotg_ep_queue(&hsotg->eps_out[0]->ep, req, GFP_ATOMIC); if (ret < 0) { @@ -1209,6 +1200,27 @@ static void s3c_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg) } } +static void s3c_hsotg_program_zlp(struct dwc2_hsotg *hsotg, + struct s3c_hsotg_ep *hs_ep) +{ + u32 ctrl; + u8 index = hs_ep->index; + u32 epctl_reg = hs_ep->dir_in ? DIEPCTL(index) : DOEPCTL(index); + u32 epsiz_reg = hs_ep->dir_in ? DIEPTSIZ(index) : DOEPTSIZ(index); + + dev_dbg(hsotg->dev, "Sending zero-length packet on ep%d\n", index); + + writel(DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) | + DXEPTSIZ_XFERSIZE(0), hsotg->regs + + epsiz_reg); + + ctrl = readl(hsotg->regs + epctl_reg); + ctrl |= DXEPCTL_CNAK; /* clear NAK set by core */ + ctrl |= DXEPCTL_EPENA; /* ensure ep enabled */ + ctrl |= DXEPCTL_USBACTEP; + writel(ctrl, hsotg->regs + epctl_reg); +} + /** * s3c_hsotg_complete_request - complete a request given to us * @hsotg: The device state. @@ -1341,9 +1353,9 @@ static void s3c_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size) } /** - * s3c_hsotg_send_zlp - send zero-length packet on control endpoint + * s3c_hsotg_ep0_zlp - send/receive zero-length packet on control endpoint * @hsotg: The device instance - * @req: The request currently on this endpoint + * @dir_in: If IN zlp * * Generate a zero-length IN packet request for terminating a SETUP * transaction. @@ -1352,51 +1364,25 @@ static void s3c_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size) * currently believed that we do not need to wait for any space in * the TxFIFO. */ -static void s3c_hsotg_send_zlp(struct dwc2_hsotg *hsotg, - struct s3c_hsotg_req *req) +static void s3c_hsotg_ep0_zlp(struct dwc2_hsotg *hsotg, bool dir_in) { - u32 ctrl; - - if (!req) { - dev_warn(hsotg->dev, "%s: no request?\n", __func__); - return; - } - - if (req->req.length == 0) { - hsotg->eps_out[0]->sent_zlp = 1; - s3c_hsotg_enqueue_setup(hsotg); - return; - } - /* eps_out[0] is used in both directions */ - hsotg->eps_out[0]->dir_in = 1; - hsotg->eps_out[0]->sent_zlp = 1; + hsotg->eps_out[0]->dir_in = dir_in; + hsotg->ep0_state = dir_in ? DWC2_EP0_STATUS_IN : DWC2_EP0_STATUS_OUT; - dev_dbg(hsotg->dev, "sending zero-length packet\n"); - - /* issue a zero-sized packet to terminate this */ - writel(DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) | - DXEPTSIZ_XFERSIZE(0), hsotg->regs + DIEPTSIZ(0)); - - ctrl = readl(hsotg->regs + DIEPCTL0); - ctrl |= DXEPCTL_CNAK; /* clear NAK set by core */ - ctrl |= DXEPCTL_EPENA; /* ensure ep enabled */ - ctrl |= DXEPCTL_USBACTEP; - writel(ctrl, hsotg->regs + DIEPCTL0); + s3c_hsotg_program_zlp(hsotg, hsotg->eps_out[0]); } /** * s3c_hsotg_handle_outdone - handle receiving OutDone/SetupDone from RXFIFO * @hsotg: The device instance * @epnum: The endpoint received from - * @was_setup: Set if processing a SetupDone event. * * The RXFIFO has delivered an OutDone event, which means that the data * transfer for an OUT endpoint has been completed, either by a short * packet or by the finish of a transfer. */ -static void s3c_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, - int epnum, bool was_setup) +static void s3c_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, int epnum) { u32 epsize = readl(hsotg->regs + DOEPTSIZ(epnum)); struct s3c_hsotg_ep *hs_ep = hsotg->eps_out[epnum]; @@ -1410,6 +1396,13 @@ static void s3c_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, return; } + if (epnum == 0 && hsotg->ep0_state == DWC2_EP0_STATUS_OUT) { + dev_dbg(hsotg->dev, "zlp packet received\n"); + s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0); + s3c_hsotg_enqueue_setup(hsotg); + return; + } + if (using_dma(hsotg)) { unsigned size_done; @@ -1432,12 +1425,6 @@ static void s3c_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, if (req->actual < req->length && size_left == 0) { s3c_hsotg_start_req(hsotg, hs_ep, hs_req, true); return; - } else if (epnum == 0) { - /* - * After was_setup = 1 => - * set CNAK for non Setup requests - */ - hsotg->setup = was_setup ? 0 : 1; } if (req->actual < req->length && req->short_not_ok) { @@ -1450,13 +1437,10 @@ static void s3c_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, */ } - if (epnum == 0) { - /* - * Condition req->complete != s3c_hsotg_complete_setup says: - * send ZLP when we have an asynchronous request from gadget - */ - if (!was_setup && req->complete != s3c_hsotg_complete_setup) - s3c_hsotg_send_zlp(hsotg, hs_req); + if (epnum == 0 && hsotg->ep0_state == DWC2_EP0_DATA_OUT) { + /* Move to STATUS IN */ + s3c_hsotg_ep0_zlp(hsotg, true); + return; } s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, result); @@ -1522,7 +1506,7 @@ static void s3c_hsotg_handle_rx(struct dwc2_hsotg *hsotg) s3c_hsotg_read_frameno(hsotg)); if (!using_dma(hsotg)) - s3c_hsotg_handle_outdone(hsotg, epnum, false); + s3c_hsotg_handle_outdone(hsotg, epnum); break; case GRXSTS_PKTSTS_SETUPDONE: @@ -1530,8 +1514,13 @@ static void s3c_hsotg_handle_rx(struct dwc2_hsotg *hsotg) "SetupDone (Frame=0x%08x, DOPEPCTL=0x%08x)\n", s3c_hsotg_read_frameno(hsotg), readl(hsotg->regs + DOEPCTL(0))); - - s3c_hsotg_handle_outdone(hsotg, epnum, true); + /* + * Call s3c_hsotg_handle_outdone here if it was not called from + * GRXSTS_PKTSTS_OUTDONE. That is, if the core didn't + * generate GRXSTS_PKTSTS_OUTDONE for setup packet. + */ + if (hsotg->ep0_state == DWC2_EP0_SETUP) + s3c_hsotg_handle_outdone(hsotg, epnum); break; case GRXSTS_PKTSTS_OUTRX: @@ -1544,6 +1533,8 @@ static void s3c_hsotg_handle_rx(struct dwc2_hsotg *hsotg) s3c_hsotg_read_frameno(hsotg), readl(hsotg->regs + DOEPCTL(0))); + WARN_ON(hsotg->ep0_state != DWC2_EP0_SETUP); + s3c_hsotg_rx_data(hsotg, epnum, size); break; @@ -1723,9 +1714,10 @@ static void s3c_hsotg_complete_in(struct dwc2_hsotg *hsotg, } /* Finish ZLP handling for IN EP0 transactions */ - if (hsotg->eps_out[0]->sent_zlp) { - dev_dbg(hsotg->dev, "zlp packet received\n"); + if (hs_ep->index == 0 && hsotg->ep0_state == DWC2_EP0_STATUS_IN) { + dev_dbg(hsotg->dev, "zlp packet sent\n"); s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0); + s3c_hsotg_enqueue_setup(hsotg); return; } @@ -1767,7 +1759,7 @@ static void s3c_hsotg_complete_in(struct dwc2_hsotg *hsotg, !(hs_req->req.length % hs_ep->ep.maxpacket)) { dev_dbg(hsotg->dev, "ep0 zlp IN packet sent\n"); - s3c_hsotg_send_zlp(hsotg, hs_req); + s3c_hsotg_program_zlp(hsotg, hs_ep); return; } @@ -1775,8 +1767,16 @@ static void s3c_hsotg_complete_in(struct dwc2_hsotg *hsotg, if (!size_left && hs_req->req.actual < hs_req->req.length) { dev_dbg(hsotg->dev, "%s trying more for req...\n", __func__); s3c_hsotg_start_req(hsotg, hs_ep, hs_req, true); - } else - s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0); + return; + } + + if (hs_ep->index == 0 && hsotg->ep0_state == DWC2_EP0_DATA_IN) { + /* Move to STATUS OUT */ + s3c_hsotg_ep0_zlp(hsotg, false); + return; + } + + s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0); } /** @@ -1845,7 +1845,7 @@ static void s3c_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx, * as we ignore the RXFIFO. */ - s3c_hsotg_handle_outdone(hsotg, idx, false); + s3c_hsotg_handle_outdone(hsotg, idx); } } @@ -1884,7 +1884,7 @@ static void s3c_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx, if (dir_in) WARN_ON_ONCE(1); else - s3c_hsotg_handle_outdone(hsotg, 0, true); + s3c_hsotg_handle_outdone(hsotg, 0); } } From f71b5e2533debe564f21c068aaabc42c6293d18b Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Fri, 9 Jan 2015 13:38:59 +0100 Subject: [PATCH 091/214] usb: dwc2: gadget: fix zero length packet transfers According to programming guide, zero length packet should be programmed on its own and should not be counted in DIEPTSIZ.PktCnt with other packets. For ep0, this is the zlp for DATA IN stage (if required) and not for the STATUS stage. Tested-by: Robert Baldyga Acked-by: Paul Zimmerman Signed-off-by: Gregory Herrero Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 51 +++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index c3977a8d70ac..992a2eb0dd81 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -598,14 +598,15 @@ static void s3c_hsotg_start_req(struct dwc2_hsotg *hsotg, else epsize = 0; - if (index != 0 && ureq->zero) { - /* - * test for the packets being exactly right for the - * transfer - */ - - if (length == (packets * hs_ep->ep.maxpacket)) - packets++; + /* + * zero length packet should be programmed on its own and should not + * be counted in DIEPTSIZ.PktCnt with other packets. + */ + if (dir_in && ureq->zero && !continuing) { + /* Test if zlp is actually required. */ + if ((ureq->length >= hs_ep->ep.maxpacket) && + !(ureq->length % hs_ep->ep.maxpacket)) + hs_ep->sent_zlp = 1; } epsize |= DXEPTSIZ_PKTCNT(packets); @@ -857,7 +858,11 @@ static int s3c_hsotg_send_reply(struct dwc2_hsotg *hsotg, req->buf = hsotg->ep0_buff; req->length = length; - req->zero = 1; /* always do zero-length final transfer */ + /* + * zero flag is for sending zlp in DATA IN stage. It has no impact on + * STATUS stage. + */ + req->zero = 0; req->complete = s3c_hsotg_complete_oursetup; if (length) @@ -1744,32 +1749,20 @@ static void s3c_hsotg_complete_in(struct dwc2_hsotg *hsotg, dev_dbg(hsotg->dev, "req->length:%d req->actual:%d req->zero:%d\n", hs_req->req.length, hs_req->req.actual, hs_req->req.zero); - /* - * Check if dealing with Maximum Packet Size(MPS) IN transfer at EP0 - * When sent data is a multiple MPS size (e.g. 64B ,128B ,192B - * ,256B ... ), after last MPS sized packet send IN ZLP packet to - * inform the host that no more data is available. - * The state of req.zero member is checked to be sure that the value to - * send is smaller than wValue expected from host. - * Check req.length to NOT send another ZLP when the current one is - * under completion (the one for which this completion has been called). - */ - if (hs_req->req.length && hs_ep->index == 0 && hs_req->req.zero && - hs_req->req.length == hs_req->req.actual && - !(hs_req->req.length % hs_ep->ep.maxpacket)) { - - dev_dbg(hsotg->dev, "ep0 zlp IN packet sent\n"); - s3c_hsotg_program_zlp(hsotg, hs_ep); - - return; - } - if (!size_left && hs_req->req.actual < hs_req->req.length) { dev_dbg(hsotg->dev, "%s trying more for req...\n", __func__); s3c_hsotg_start_req(hsotg, hs_ep, hs_req, true); return; } + /* Zlp for all endpoints, for ep0 only in DATA IN stage */ + if (hs_ep->sent_zlp) { + s3c_hsotg_program_zlp(hsotg, hs_ep); + hs_ep->sent_zlp = 0; + /* transfer will be completed on next complete interrupt */ + return; + } + if (hs_ep->index == 0 && hsotg->ep0_state == DWC2_EP0_DATA_IN) { /* Move to STATUS OUT */ s3c_hsotg_ep0_zlp(hsotg, false); From 1a0ed863eef7ea6af63afd60ca4fa16edf6b4244 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Fri, 9 Jan 2015 13:39:00 +0100 Subject: [PATCH 092/214] usb: dwc2: gadget: dont warn if endpoint is not enabled The warning is probably good but it has false positives in both dma and non-dma cases. So its not very helpful in either. Tested-by: Robert Baldyga Acked-by: Paul Zimmerman Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 992a2eb0dd81..6d76e28a69da 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -678,7 +678,7 @@ static void s3c_hsotg_start_req(struct dwc2_hsotg *hsotg, /* check ep is enabled */ if (!(readl(hsotg->regs + epctrl_reg) & DXEPCTL_EPENA)) - dev_warn(hsotg->dev, + dev_dbg(hsotg->dev, "ep%d: failed to become enabled (DXEPCTL=0x%08x)?\n", index, readl(hsotg->regs + epctrl_reg)); From 48b20bcb3b2e9b15be1ebb9b574b003cffb033d5 Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Fri, 9 Jan 2015 13:39:01 +0100 Subject: [PATCH 093/214] usb: dwc2: gadget: don't block after fifo flush timeout Can't stay in the loop forever. Break it after timeout. Tested-by: Robert Baldyga Acked-by: Paul Zimmerman Signed-off-by: Gregory Herrero Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 6d76e28a69da..e3bb525ae54d 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -230,6 +230,7 @@ static void s3c_hsotg_init_fifo(struct dwc2_hsotg *hsotg) dev_err(hsotg->dev, "%s: timeout flushing fifos (GRSTCTL=%08x)\n", __func__, val); + break; } udelay(1); From 83d982234ee65900521e763161f409b29f89b8f6 Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Fri, 9 Jan 2015 13:39:02 +0100 Subject: [PATCH 094/214] usb: dwc2: gadget: add vbus_session support Enable phy driver to report vbus session. This allows us to remove D+ pullup when vbus is not present. Tested-by: Robert Baldyga Acked-by: Paul Zimmerman Signed-off-by: Gregory Herrero Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index e3bb525ae54d..65ec0f2dfab1 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -3009,11 +3009,35 @@ static int s3c_hsotg_pullup(struct usb_gadget *gadget, int is_on) return 0; } +static int s3c_hsotg_vbus_session(struct usb_gadget *gadget, int is_active) +{ + struct dwc2_hsotg *hsotg = to_hsotg(gadget); + unsigned long flags; + + dev_dbg(hsotg->dev, "%s: is_active: %d\n", __func__, is_active); + spin_lock_irqsave(&hsotg->lock, flags); + + if (is_active) { + /* Kill any ep0 requests as controller will be reinitialized */ + kill_all_requests(hsotg, hsotg->eps_out[0], -ECONNRESET); + s3c_hsotg_core_init_disconnected(hsotg); + if (hsotg->enabled) + s3c_hsotg_core_connect(hsotg); + } else { + s3c_hsotg_core_disconnect(hsotg); + s3c_hsotg_disconnect(hsotg); + } + + spin_unlock_irqrestore(&hsotg->lock, flags); + return 0; +} + static const struct usb_gadget_ops s3c_hsotg_gadget_ops = { .get_frame = s3c_hsotg_gadget_getframe, .udc_start = s3c_hsotg_udc_start, .udc_stop = s3c_hsotg_udc_stop, .pullup = s3c_hsotg_pullup, + .vbus_session = s3c_hsotg_vbus_session, }; /** From 8a20fa457eb5a8d247fd82d4bd3c04225fd2f253 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Fri, 9 Jan 2015 13:39:03 +0100 Subject: [PATCH 095/214] usb: dwc2: gadget: rename sent_zlp to send_zlp This flag is set before sending the zlp. So use present tense instead of the past tense. Tested-by: Robert Baldyga Acked-by: Paul Zimmerman Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/core.h | 4 ++-- drivers/usb/dwc2/gadget.c | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index e963aef1daa2..f09b3deffdee 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -108,7 +108,7 @@ struct s3c_hsotg_req; * @halted: Set if the endpoint has been halted. * @periodic: Set if this is a periodic ep, such as Interrupt * @isochronous: Set if this is a isochronous ep - * @sent_zlp: Set if we've sent a zero-length packet. + * @send_zlp: Set if we need to send a zero-length packet. * @total_data: The total number of data bytes done. * @fifo_size: The size of the FIFO (for periodic IN endpoints) * @fifo_load: The amount of data loaded into the FIFO (periodic IN) @@ -149,7 +149,7 @@ struct s3c_hsotg_ep { unsigned int halted:1; unsigned int periodic:1; unsigned int isochronous:1; - unsigned int sent_zlp:1; + unsigned int send_zlp:1; char name[10]; }; diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 65ec0f2dfab1..5fdc3f54f2a8 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -607,7 +607,7 @@ static void s3c_hsotg_start_req(struct dwc2_hsotg *hsotg, /* Test if zlp is actually required. */ if ((ureq->length >= hs_ep->ep.maxpacket) && !(ureq->length % hs_ep->ep.maxpacket)) - hs_ep->sent_zlp = 1; + hs_ep->send_zlp = 1; } epsize |= DXEPTSIZ_PKTCNT(packets); @@ -1193,7 +1193,7 @@ static void s3c_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg) } hsotg->eps_out[0]->dir_in = 0; - hsotg->eps_out[0]->sent_zlp = 0; + hsotg->eps_out[0]->send_zlp = 0; hsotg->ep0_state = DWC2_EP0_SETUP; ret = s3c_hsotg_ep_queue(&hsotg->eps_out[0]->ep, req, GFP_ATOMIC); @@ -1757,9 +1757,9 @@ static void s3c_hsotg_complete_in(struct dwc2_hsotg *hsotg, } /* Zlp for all endpoints, for ep0 only in DATA IN stage */ - if (hs_ep->sent_zlp) { + if (hs_ep->send_zlp) { s3c_hsotg_program_zlp(hsotg, hs_ep); - hs_ep->sent_zlp = 0; + hs_ep->send_zlp = 0; /* transfer will be completed on next complete interrupt */ return; } From ca4c55ad89edb54b1eb04fe2d4c6915c9f8d69b9 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Fri, 9 Jan 2015 13:39:04 +0100 Subject: [PATCH 096/214] usb: dwc2: gadget: pick smallest acceptable fifo Current algorithm picks the first fifo which is equal to or greater than the required size. This can result in bigger fifos assigned to endpoints with smaller maxps. Change the algorithm to pick the smallest fifo which is greater than or equal to the required size. Moreover, only use signed variables when required. Tested-by: Robert Baldyga Acked-by: Paul Zimmerman Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 5fdc3f54f2a8..54e6d153dfbc 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2439,12 +2439,12 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep, struct s3c_hsotg_ep *hs_ep = our_ep(ep); struct dwc2_hsotg *hsotg = hs_ep->parent; unsigned long flags; - int index = hs_ep->index; + unsigned int index = hs_ep->index; u32 epctrl_reg; u32 epctrl; u32 mps; - int dir_in; - int i, val, size; + unsigned int dir_in; + unsigned int i, val, size; int ret = 0; dev_dbg(hsotg->dev, @@ -2533,6 +2533,8 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep, * a unique tx-fifo even if it is non-periodic. */ if (dir_in && hsotg->dedicated_fifos) { + u32 fifo_index = 0; + u32 fifo_size = UINT_MAX; size = hs_ep->ep.maxpacket*hs_ep->mc; for (i = 1; i < hsotg->num_of_eps; ++i) { if (hsotg->fifo_map & (1<> FIFOSIZE_DEPTH_SHIFT)*4; if (val < size) continue; - hsotg->fifo_map |= 1<fifo_index = i; - hs_ep->fifo_size = val; - break; + /* Search for smallest acceptable fifo */ + if (val < fifo_size) { + fifo_size = val; + fifo_index = i; + } } - if (i == hsotg->num_of_eps) { + if (!fifo_index) { dev_err(hsotg->dev, "%s: No suitable fifo found\n", __func__); ret = -ENOMEM; goto error; } + hsotg->fifo_map |= 1 << fifo_index; + epctrl |= DXEPCTL_TXFNUM(fifo_index); + hs_ep->fifo_index = fifo_index; + hs_ep->fifo_size = fifo_size; } /* for non control endpoints, set PID to D0 */ From 4556e12c9cf732343224308866dad2a12887dcd6 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Fri, 9 Jan 2015 13:39:05 +0100 Subject: [PATCH 097/214] usb: dwc2: gadget: fix fifo allocation leak When selecting different alt setting, s3c_hsotg_ep_enable can be called with fifo already allocated. Allocate fifo again only if required and after deallocating the previous fifo. Tested-by: Robert Baldyga Acked-by: Paul Zimmerman Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 54e6d153dfbc..de5da92a8537 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2528,11 +2528,22 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep, break; } + /* If fifo is already allocated for this ep */ + if (hs_ep->fifo_index) { + size = hs_ep->ep.maxpacket * hs_ep->mc; + /* If bigger fifo is required deallocate current one */ + if (size > hs_ep->fifo_size) { + hsotg->fifo_map &= ~(1 << hs_ep->fifo_index); + hs_ep->fifo_index = 0; + hs_ep->fifo_size = 0; + } + } + /* * if the hardware has dedicated fifos, we must give each IN EP * a unique tx-fifo even if it is non-periodic. */ - if (dir_in && hsotg->dedicated_fifos) { + if (dir_in && hsotg->dedicated_fifos && !hs_ep->fifo_index) { u32 fifo_index = 0; u32 fifo_size = UINT_MAX; size = hs_ep->ep.maxpacket*hs_ep->mc; From 7fcbc95c1fa4d6e3f4645f9552d358f07ab93ad1 Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Fri, 9 Jan 2015 13:39:06 +0100 Subject: [PATCH 098/214] usb: dwc2: gadget: reset fifo_map when initializing fifos After all endpoints are disabled, fifo_map should have reached 0. Its a bug if if didn't, so warn about it and reset it to 0 so that driver can continue using all the fifos. Tested-by: Robert Baldyga Acked-by: Paul Zimmerman Signed-off-by: Gregory Herrero Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index de5da92a8537..a1fd86febe98 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -177,6 +177,10 @@ static void s3c_hsotg_init_fifo(struct dwc2_hsotg *hsotg) int timeout; u32 val; + /* Reset fifo map if not correctly cleared during previous session */ + WARN_ON(hsotg->fifo_map); + hsotg->fifo_map = 0; + /* set RX/NPTX FIFO sizes */ writel(hsotg->g_rx_fifo_sz, hsotg->regs + GRXFSIZ); writel((hsotg->g_rx_fifo_sz << FIFOSIZE_STARTADDR_SHIFT) | From 6d13673e6bc5b666913e92ca59518a449da3b4a9 Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Fri, 9 Jan 2015 13:39:07 +0100 Subject: [PATCH 099/214] usb: dwc2: gadget: fix pullup handling Gadget must be informed about disconnection when pullup is removed. Tested-by: Robert Baldyga Acked-by: Paul Zimmerman Signed-off-by: Gregory Herrero Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index a1fd86febe98..2719d48c73df 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -3018,6 +3018,7 @@ static int s3c_hsotg_pullup(struct usb_gadget *gadget, int is_on) s3c_hsotg_core_connect(hsotg); } else { s3c_hsotg_core_disconnect(hsotg); + s3c_hsotg_disconnect(hsotg); hsotg->enabled = 0; clk_disable(hsotg->clk); } From 596d696a5d0e066f2ed4bd6b0e40dedd52513c7c Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Fri, 9 Jan 2015 13:39:08 +0100 Subject: [PATCH 100/214] usb: dwc2: gadget: add vbus_draw support This callback informs the driver about the total amount of current it is allowed to draw. Share this information with the phy so that current limits can be set for charging for example. Tested-by: Robert Baldyga Acked-by: Paul Zimmerman Signed-off-by: Gregory Herrero Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 2719d48c73df..f47770940a7a 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -3053,12 +3053,29 @@ static int s3c_hsotg_vbus_session(struct usb_gadget *gadget, int is_active) return 0; } +/** + * s3c_hsotg_vbus_draw - report bMaxPower field + * @gadget: The usb gadget state + * @mA: Amount of current + * + * Report how much power the device may consume to the phy. + */ +static int s3c_hsotg_vbus_draw(struct usb_gadget *gadget, unsigned mA) +{ + struct dwc2_hsotg *hsotg = to_hsotg(gadget); + + if (IS_ERR_OR_NULL(hsotg->uphy)) + return -ENOTSUPP; + return usb_phy_set_power(hsotg->uphy, mA); +} + static const struct usb_gadget_ops s3c_hsotg_gadget_ops = { .get_frame = s3c_hsotg_gadget_getframe, .udc_start = s3c_hsotg_udc_start, .udc_stop = s3c_hsotg_udc_stop, .pullup = s3c_hsotg_pullup, .vbus_session = s3c_hsotg_vbus_session, + .vbus_draw = s3c_hsotg_vbus_draw, }; /** From 1b7a66b4d3399da5e0b13cf90fb4bd33b7197ff2 Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Fri, 9 Jan 2015 13:39:09 +0100 Subject: [PATCH 101/214] usb: dwc2: gadget: force gadget initialization in dev mode When booting with id pin grounded, dwc2 default to host mode. Thus, force device mode prior initializing gadget part. Else fifo init will fail since fifo values are not correct in host mode. Tested-by: Robert Baldyga Acked-by: Paul Zimmerman Signed-off-by: Gregory Herrero Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index f47770940a7a..df9080d20606 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -3675,6 +3675,19 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) /* usb phy enable */ s3c_hsotg_phy_enable(hsotg); + /* + * Force Device mode before initialization. + * This allows correctly configuring fifo for device mode. + */ + __bic32(hsotg->regs + GUSBCFG, GUSBCFG_FORCEHOSTMODE); + __orr32(hsotg->regs + GUSBCFG, GUSBCFG_FORCEDEVMODE); + + /* + * According to Synopsys databook, this sleep is needed for the force + * device mode to take effect. + */ + msleep(25); + s3c_hsotg_corereset(hsotg); ret = s3c_hsotg_hw_cfg(hsotg); if (ret) { @@ -3684,6 +3697,9 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) s3c_hsotg_init(hsotg); + /* Switch back to default configuration */ + __bic32(hsotg->regs + GUSBCFG, GUSBCFG_FORCEDEVMODE); + hsotg->ctrl_buff = devm_kzalloc(hsotg->dev, DWC2_CTRL_BUFF_SIZE, GFP_KERNEL); if (!hsotg->ctrl_buff) { From 6d713c1531638df8d459d248a89948318cbeec4c Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Fri, 9 Jan 2015 13:39:10 +0100 Subject: [PATCH 102/214] usb: dwc2: gadget: report disconnection after reset If usb bus is reset without a physical disconnection, all endpoints will remain open. Call s3c_hsotg_disconnect() from reset handler to report a disconnect to gadget framework. hsotg->connected is checked in s3c_hsotg_disconnect() before processing disconnect. In some cases, USBRst is seen before EnumDone and after it as well. So move setting of hsotg->connected to set-address to avoid reporting extra disconnection in this case. Tested-by: Robert Baldyga Acked-by: Paul Zimmerman Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index df9080d20606..882a1a8953f5 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -1103,6 +1103,7 @@ static void s3c_hsotg_process_control(struct dwc2_hsotg *hsotg, if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { switch (ctrl->bRequest) { case USB_REQ_SET_ADDRESS: + hsotg->connected = 1; dcfg = readl(hsotg->regs + DCFG); dcfg &= ~DCFG_DEVADDR_MASK; dcfg |= (le16_to_cpu(ctrl->wValue) << @@ -2305,7 +2306,6 @@ irq_retry: writel(GINTSTS_ENUMDONE, hsotg->regs + GINTSTS); s3c_hsotg_irq_enumdone(hsotg); - hsotg->connected = 1; } if (gintsts & (GINTSTS_OEPINT | GINTSTS_IEPINT)) { @@ -2343,6 +2343,9 @@ irq_retry: writel(GINTSTS_USBRST, hsotg->regs + GINTSTS); + /* Report disconnection if it is not already done. */ + s3c_hsotg_disconnect(hsotg); + if (usb_status & GOTGCTL_BSESVLD) { if (time_after(jiffies, hsotg->last_rst + msecs_to_jiffies(200))) { From d7b3968dbcbb620acf92164752031fa276917db9 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Tue, 16 Dec 2014 01:42:13 +0300 Subject: [PATCH 103/214] usb: renesas_usbhs: add OTG ID signal sensing On the Renesas R8A7791 SoC based boards there's MAX3355 USB OTG chip and mini-AB USB connector corresponding to USB port 0 driven either by EHCI/OHCI or Renesas USBHS gadget controller. And we'd like the host/gadget drivers to work based on the cable type connected. An 'extcon' driver for MAX3355 has been written, so we only need to bind to it via device tree which I'm doing in this patch. (Perhaps, it would also make sense to use OTG HNP when the USBHS host mode is active and a B-cable is connected but I don't have access to host-capable USBHS, so wouldn't be able to test it.) Signed-off-by: Sergei Shtylyov Signed-off-by: Felipe Balbi --- drivers/usb/renesas_usbhs/common.c | 17 +++++++++++++++++ drivers/usb/renesas_usbhs/common.h | 3 +++ 2 files changed, 20 insertions(+) diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 76d14f5d246a..4cf77d3c3bd2 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -363,6 +363,7 @@ static void usbhsc_hotplug(struct usbhs_priv *priv) struct usbhs_mod *mod = usbhs_mod_get_current(priv); int id; int enable; + int cable; int ret; /* @@ -376,6 +377,16 @@ static void usbhsc_hotplug(struct usbhs_priv *priv) id = usbhs_platform_call(priv, get_id, pdev); if (enable && !mod) { + if (priv->edev) { + cable = extcon_get_cable_state(priv->edev, "USB-HOST"); + if ((cable > 0 && id != USBHS_HOST) || + (!cable && id != USBHS_GADGET)) { + dev_info(&pdev->dev, + "USB cable plugged in doesn't match the selected role!\n"); + return; + } + } + ret = usbhs_mod_change(priv, id); if (ret < 0) return; @@ -514,6 +525,12 @@ static int usbhs_probe(struct platform_device *pdev) if (IS_ERR(priv->base)) return PTR_ERR(priv->base); + if (of_property_read_bool(pdev->dev.of_node, "extcon")) { + priv->edev = extcon_get_edev_by_phandle(&pdev->dev, 0); + if (IS_ERR(priv->edev)) + return PTR_ERR(priv->edev); + } + /* * care platform info */ diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h index 0427cdd1a483..fc96e924edc4 100644 --- a/drivers/usb/renesas_usbhs/common.h +++ b/drivers/usb/renesas_usbhs/common.h @@ -17,6 +17,7 @@ #ifndef RENESAS_USB_DRIVER_H #define RENESAS_USB_DRIVER_H +#include #include #include @@ -254,6 +255,8 @@ struct usbhs_priv { struct delayed_work notify_hotplug_work; struct platform_device *pdev; + struct extcon_dev *edev; + spinlock_t lock; u32 flags; From 3703cfe78295b1ccbad091871ccc0fa0e5d0589f Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Mon, 1 Dec 2014 16:09:28 +0800 Subject: [PATCH 104/214] usb: gadget: uac1: struct gaudio is useless for struct f_uac1_opts Since we call gaudio_cleanup at f_audio_free, the f_uac1_opts doesn't need to use gaudio any more. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uac1.c | 1 - drivers/usb/gadget/function/u_uac1.h | 1 - 2 files changed, 2 deletions(-) diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index d8b4af6b5273..9cf225243f9f 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -674,7 +674,6 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f) audio_opts = container_of(f->fi, struct f_uac1_opts, func_inst); audio->card.gadget = c->cdev->gadget; - audio_opts->card = &audio->card; /* set up ASLA audio devices */ if (!audio_opts->bound) { status = gaudio_setup(&audio->card); diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h index f8b17fe82efe..fe386df6dd3e 100644 --- a/drivers/usb/gadget/function/u_uac1.h +++ b/drivers/usb/gadget/function/u_uac1.h @@ -70,7 +70,6 @@ struct f_uac1_opts { unsigned fn_play_alloc:1; unsigned fn_cap_alloc:1; unsigned fn_cntl_alloc:1; - struct gaudio *card; struct mutex lock; int refcnt; }; From c76abecc42b63f70d33f9e266c62002c2b1234d1 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Mon, 1 Dec 2014 16:09:29 +0800 Subject: [PATCH 105/214] usb: gadget: u_uac1: fix one code style problem Fix one code style problem. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/u_uac1.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/gadget/function/u_uac1.c b/drivers/usb/gadget/function/u_uac1.c index 53842a1b947f..c78c84138a28 100644 --- a/drivers/usb/gadget/function/u_uac1.c +++ b/drivers/usb/gadget/function/u_uac1.c @@ -308,8 +308,7 @@ int gaudio_setup(struct gaudio *card) */ void gaudio_cleanup(struct gaudio *the_card) { - if (the_card) { + if (the_card) gaudio_close_snd_dev(the_card); - } } From fc12c68b4ff2904c3322b7a12089f0b9808e2383 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Tue, 13 Jan 2015 10:54:58 +0100 Subject: [PATCH 106/214] usb: gadget: net2280: Dont use 0 as NULL pointer Fix sparse warning Fixes: cb442ee1592d2681 (usb: gadget: udc: net2280: Re-enable dynamic debug messages) Reported-by: kbuild test robot Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/net2280.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index d2fabdc0deaa..6411ed8e14bc 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -2287,7 +2287,7 @@ static void handle_ep_small(struct net2280_ep *ep) ep->irqs++; ep_vdbg(ep->dev, "%s ack ep_stat %08x, req %p\n", - ep->ep.name, t, req ? &req->req : 0); + ep->ep.name, t, req ? &req->req : NULL); if (!ep->is_in || (ep->dev->quirks & PLX_2280)) writel(t & ~BIT(NAK_OUT_PACKETS), &ep->regs->ep_stat); From 969678c09877fd1116be6ae304e59a27658226c2 Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Tue, 13 Jan 2015 14:56:28 +0100 Subject: [PATCH 107/214] tools: ffs-aio-example: use endpoint addresses from descriptors This makes examples more platform independent and more compatible with USB standard, as endpoint addresses in given interface may differ between hardware platforms or even between configurations in single USB device. Signed-off-by: Robert Baldyga Signed-off-by: Felipe Balbi --- .../ffs-aio-example/multibuff/host_app/test.c | 14 ++++++++------ .../usb/ffs-aio-example/simple/host_app/test.c | 17 ++++++++++------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/tools/usb/ffs-aio-example/multibuff/host_app/test.c b/tools/usb/ffs-aio-example/multibuff/host_app/test.c index daa3abe6bebd..2cbcce6e8dd7 100644 --- a/tools/usb/ffs-aio-example/multibuff/host_app/test.c +++ b/tools/usb/ffs-aio-example/multibuff/host_app/test.c @@ -33,11 +33,6 @@ #define VENDOR 0x1d6b #define PRODUCT 0x0105 -/* endpoints indexes */ - -#define EP_BULK_IN (1 | LIBUSB_ENDPOINT_IN) -#define EP_BULK_OUT (2 | LIBUSB_ENDPOINT_OUT) - #define BUF_LEN 8192 /* @@ -159,14 +154,21 @@ void test_exit(struct test_state *state) int main(void) { struct test_state state; + struct libusb_config_descriptor *conf; + struct libusb_interface_descriptor const *iface; + unsigned char addr; if (test_init(&state)) return 1; + libusb_get_config_descriptor(state.found, 0, &conf); + iface = &conf->interface[0].altsetting[0]; + addr = iface->endpoint[0].bEndpointAddress; + while (1) { static unsigned char buffer[BUF_LEN]; int bytes; - libusb_bulk_transfer(state.handle, EP_BULK_IN, buffer, BUF_LEN, + libusb_bulk_transfer(state.handle, addr, buffer, BUF_LEN, &bytes, 500); } test_exit(&state); diff --git a/tools/usb/ffs-aio-example/simple/host_app/test.c b/tools/usb/ffs-aio-example/simple/host_app/test.c index acd6332811f3..aed86ffff280 100644 --- a/tools/usb/ffs-aio-example/simple/host_app/test.c +++ b/tools/usb/ffs-aio-example/simple/host_app/test.c @@ -33,11 +33,6 @@ #define VENDOR 0x1d6b #define PRODUCT 0x0105 -/* endpoints indexes */ - -#define EP_BULK_IN (1 | LIBUSB_ENDPOINT_IN) -#define EP_BULK_OUT (2 | LIBUSB_ENDPOINT_OUT) - #define BUF_LEN 8192 /* @@ -159,16 +154,24 @@ void test_exit(struct test_state *state) int main(void) { struct test_state state; + struct libusb_config_descriptor *conf; + struct libusb_interface_descriptor const *iface; + unsigned char in_addr, out_addr; if (test_init(&state)) return 1; + libusb_get_config_descriptor(state.found, 0, &conf); + iface = &conf->interface[0].altsetting[0]; + in_addr = iface->endpoint[0].bEndpointAddress; + out_addr = iface->endpoint[1].bEndpointAddress; + while (1) { static unsigned char buffer[BUF_LEN]; int bytes; - libusb_bulk_transfer(state.handle, EP_BULK_IN, buffer, BUF_LEN, + libusb_bulk_transfer(state.handle, in_addr, buffer, BUF_LEN, &bytes, 500); - libusb_bulk_transfer(state.handle, EP_BULK_OUT, buffer, BUF_LEN, + libusb_bulk_transfer(state.handle, out_addr, buffer, BUF_LEN, &bytes, 500); } test_exit(&state); From f093a2d465371d106384559a3ac0abf701be820e Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Tue, 13 Jan 2015 16:55:38 +0800 Subject: [PATCH 108/214] usb: gadget: uvc: to_uvcg_control_header() can be static drivers/usb/gadget/function/uvc_configfs.c:46:28: sparse: symbol 'to_uvcg_control_header' was not declared. Should it be static? drivers/usb/gadget/function/uvc_configfs.c:138:25: sparse: symbol 'uvcg_control_header_type' was not declared. Should it be static? drivers/usb/gadget/function/uvc_configfs.c:164:6: sparse: symbol 'uvcg_control_header_drop' was not declared. Should it be static? drivers/usb/gadget/function/uvc_configfs.c:721:20: sparse: symbol 'to_uvcg_format' was not declared. Should it be static? drivers/usb/gadget/function/uvc_configfs.c:798:30: sparse: symbol 'to_uvcg_streaming_header' was not declared. Should it be static? drivers/usb/gadget/function/uvc_configfs.c:950:25: sparse: symbol 'uvcg_streaming_header_type' was not declared. Should it be static? drivers/usb/gadget/function/uvc_configfs.c:976:6: sparse: symbol 'uvcg_streaming_header_drop' was not declared. Should it be static? drivers/usb/gadget/function/uvc_configfs.c:1020:19: sparse: symbol 'to_uvcg_frame' was not declared. Should it be static? drivers/usb/gadget/function/uvc_configfs.c:1265:25: sparse: symbol 'uvcg_frame_type' was not declared. Should it be static? drivers/usb/gadget/function/uvc_configfs.c:1315:6: sparse: symbol 'uvcg_frame_drop' was not declared. Should it be static? drivers/usb/gadget/function/uvc_configfs.c:1338:26: sparse: symbol 'to_uvcg_uncompressed' was not declared. Should it be static? drivers/usb/gadget/function/uvc_configfs.c:1548:25: sparse: symbol 'uvcg_uncompressed_type' was not declared. Should it be static? drivers/usb/gadget/function/uvc_configfs.c:1586:6: sparse: symbol 'uvcg_uncompressed_drop' was not declared. Should it be static? drivers/usb/gadget/function/uvc_configfs.c:1610:19: sparse: symbol 'to_uvcg_mjpeg' was not declared. Should it be static? drivers/usb/gadget/function/uvc_configfs.c:1761:25: sparse: symbol 'uvcg_mjpeg_type' was not declared. Should it be static? drivers/usb/gadget/function/uvc_configfs.c:1793:6: sparse: symbol 'uvcg_mjpeg_drop' was not declared. Should it be static? Signed-off-by: Fengguang Wu Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/uvc_configfs.c | 32 +++++++++++----------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index 33d92ab3c031..f69f47a00051 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -43,7 +43,7 @@ struct uvcg_control_header { unsigned linked; }; -struct uvcg_control_header *to_uvcg_control_header(struct config_item *item) +static struct uvcg_control_header *to_uvcg_control_header(struct config_item *item) { return container_of(item, struct uvcg_control_header, item); } @@ -135,7 +135,7 @@ static struct configfs_attribute *uvcg_control_header_attrs[] = { NULL, }; -struct config_item_type uvcg_control_header_type = { +static struct config_item_type uvcg_control_header_type = { .ct_item_ops = &uvcg_control_header_item_ops, .ct_attrs = uvcg_control_header_attrs, .ct_owner = THIS_MODULE, @@ -161,7 +161,7 @@ static struct config_item *uvcg_control_header_make(struct config_group *group, return &h->item; } -void uvcg_control_header_drop(struct config_group *group, +static void uvcg_control_header_drop(struct config_group *group, struct config_item *item) { struct uvcg_control_header *h = to_uvcg_control_header(item); @@ -718,7 +718,7 @@ struct uvcg_format { __u8 bmaControls[UVCG_STREAMING_CONTROL_SIZE]; }; -struct uvcg_format *to_uvcg_format(struct config_item *item) +static struct uvcg_format *to_uvcg_format(struct config_item *item) { return container_of(to_config_group(item), struct uvcg_format, group); } @@ -795,7 +795,7 @@ struct uvcg_streaming_header { unsigned num_fmt; }; -struct uvcg_streaming_header *to_uvcg_streaming_header(struct config_item *item) +static struct uvcg_streaming_header *to_uvcg_streaming_header(struct config_item *item) { return container_of(item, struct uvcg_streaming_header, item); } @@ -947,7 +947,7 @@ static struct configfs_attribute *uvcg_streaming_header_attrs[] = { NULL, }; -struct config_item_type uvcg_streaming_header_type = { +static struct config_item_type uvcg_streaming_header_type = { .ct_item_ops = &uvcg_streaming_header_item_ops, .ct_attrs = uvcg_streaming_header_attrs, .ct_owner = THIS_MODULE, @@ -973,7 +973,7 @@ static struct config_item return &h->item; } -void uvcg_streaming_header_drop(struct config_group *group, +static void uvcg_streaming_header_drop(struct config_group *group, struct config_item *item) { struct uvcg_streaming_header *h = to_uvcg_streaming_header(item); @@ -1017,7 +1017,7 @@ struct uvcg_frame { struct config_item item; }; -struct uvcg_frame *to_uvcg_frame(struct config_item *item) +static struct uvcg_frame *to_uvcg_frame(struct config_item *item) { return container_of(item, struct uvcg_frame, item); } @@ -1262,7 +1262,7 @@ static struct configfs_attribute *uvcg_frame_attrs[] = { NULL, }; -struct config_item_type uvcg_frame_type = { +static struct config_item_type uvcg_frame_type = { .ct_item_ops = &uvcg_frame_item_ops, .ct_attrs = uvcg_frame_attrs, .ct_owner = THIS_MODULE, @@ -1312,7 +1312,7 @@ static struct config_item *uvcg_frame_make(struct config_group *group, return &h->item; } -void uvcg_frame_drop(struct config_group *group, struct config_item *item) +static void uvcg_frame_drop(struct config_group *group, struct config_item *item) { struct uvcg_frame *h = to_uvcg_frame(item); struct uvcg_format *fmt; @@ -1335,7 +1335,7 @@ struct uvcg_uncompressed { struct uvc_format_uncompressed desc; }; -struct uvcg_uncompressed *to_uvcg_uncompressed(struct config_item *item) +static struct uvcg_uncompressed *to_uvcg_uncompressed(struct config_item *item) { return container_of( container_of(to_config_group(item), struct uvcg_format, group), @@ -1545,7 +1545,7 @@ static struct configfs_attribute *uvcg_uncompressed_attrs[] = { NULL, }; -struct config_item_type uvcg_uncompressed_type = { +static struct config_item_type uvcg_uncompressed_type = { .ct_item_ops = &uvcg_uncompressed_item_ops, .ct_group_ops = &uvcg_uncompressed_group_ops, .ct_attrs = uvcg_uncompressed_attrs, @@ -1583,7 +1583,7 @@ static struct config_group *uvcg_uncompressed_make(struct config_group *group, return &h->fmt.group; } -void uvcg_uncompressed_drop(struct config_group *group, +static void uvcg_uncompressed_drop(struct config_group *group, struct config_item *item) { struct uvcg_uncompressed *h = to_uvcg_uncompressed(item); @@ -1607,7 +1607,7 @@ struct uvcg_mjpeg { struct uvc_format_mjpeg desc; }; -struct uvcg_mjpeg *to_uvcg_mjpeg(struct config_item *item) +static struct uvcg_mjpeg *to_uvcg_mjpeg(struct config_item *item) { return container_of( container_of(to_config_group(item), struct uvcg_format, group), @@ -1758,7 +1758,7 @@ static struct configfs_attribute *uvcg_mjpeg_attrs[] = { NULL, }; -struct config_item_type uvcg_mjpeg_type = { +static struct config_item_type uvcg_mjpeg_type = { .ct_item_ops = &uvcg_mjpeg_item_ops, .ct_group_ops = &uvcg_mjpeg_group_ops, .ct_attrs = uvcg_mjpeg_attrs, @@ -1790,7 +1790,7 @@ static struct config_group *uvcg_mjpeg_make(struct config_group *group, return &h->fmt.group; } -void uvcg_mjpeg_drop(struct config_group *group, +static void uvcg_mjpeg_drop(struct config_group *group, struct config_item *item) { struct uvcg_mjpeg *h = to_uvcg_mjpeg(item); From d3fdcc78b2d6f1338fa9abea2d37bc10da7f71b8 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 12 Jan 2015 14:20:12 +0200 Subject: [PATCH 109/214] usb: dwc3: pci: remove registration of NOP PHYs None of the PCI platforms need the NOP transceivers, and since we can now live without the PHYs, removing registration of the platform devices for them. Acked-by: Huang Rui Signed-off-by: Heikki Krogerus Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-pci.c | 68 ------------------------------------- 1 file changed, 68 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 7c4faf738747..82a439b075d7 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -22,9 +22,6 @@ #include #include -#include -#include - #include "platform_data.h" /* FIXME define these in */ @@ -37,65 +34,8 @@ struct dwc3_pci { struct device *dev; struct platform_device *dwc3; - struct platform_device *usb2_phy; - struct platform_device *usb3_phy; }; -static int dwc3_pci_register_phys(struct dwc3_pci *glue) -{ - struct usb_phy_generic_platform_data pdata; - struct platform_device *pdev; - int ret; - - memset(&pdata, 0x00, sizeof(pdata)); - - pdev = platform_device_alloc("usb_phy_generic", 0); - if (!pdev) - return -ENOMEM; - - 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) - goto err1; - - pdev = platform_device_alloc("usb_phy_generic", 1); - if (!pdev) { - ret = -ENOMEM; - goto err1; - } - - glue->usb3_phy = pdev; - pdata.type = USB_PHY_TYPE_USB3; - - ret = platform_device_add_data(glue->usb3_phy, &pdata, sizeof(pdata)); - if (ret) - goto err2; - - ret = platform_device_add(glue->usb2_phy); - if (ret) - goto err2; - - ret = platform_device_add(glue->usb3_phy); - if (ret) - goto err3; - - return 0; - -err3: - platform_device_del(glue->usb2_phy); - -err2: - platform_device_put(glue->usb3_phy); - -err1: - platform_device_put(glue->usb2_phy); - - return ret; -} - static int dwc3_pci_probe(struct pci_dev *pci, const struct pci_device_id *id) { @@ -122,12 +62,6 @@ static int dwc3_pci_probe(struct pci_dev *pci, pci_set_master(pci); - ret = dwc3_pci_register_phys(glue); - if (ret) { - dev_err(dev, "couldn't register PHYs\n"); - return ret; - } - dwc3 = platform_device_alloc("dwc3", PLATFORM_DEVID_AUTO); if (!dwc3) { dev_err(dev, "couldn't allocate dwc3 device\n"); @@ -207,8 +141,6 @@ static void dwc3_pci_remove(struct pci_dev *pci) struct dwc3_pci *glue = pci_get_drvdata(pci); platform_device_unregister(glue->dwc3); - platform_device_unregister(glue->usb2_phy); - platform_device_unregister(glue->usb3_phy); } static const struct pci_device_id dwc3_pci_id_table[] = { From 3b44ed90cdb26ce3a06a1b41c5fcbb01c7d63b3c Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 12 Jan 2015 14:20:13 +0200 Subject: [PATCH 110/214] usb: dwc3: pci: rely on default PM callbacks from PCI driver utility There is nothing specific being done in the suspend and resume callbacks that is not already taken care of in PCI driver core, so dropping the functions. Acked-by: Huang Rui Signed-off-by: Heikki Krogerus Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-pci.c | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 82a439b075d7..5c471d2fe732 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -156,45 +156,11 @@ static const struct pci_device_id dwc3_pci_id_table[] = { }; MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table); -#ifdef CONFIG_PM_SLEEP -static int dwc3_pci_suspend(struct device *dev) -{ - struct pci_dev *pci = to_pci_dev(dev); - - pci_disable_device(pci); - - return 0; -} - -static int dwc3_pci_resume(struct device *dev) -{ - struct pci_dev *pci = to_pci_dev(dev); - int ret; - - ret = pci_enable_device(pci); - if (ret) { - dev_err(dev, "can't re-enable device --> %d\n", ret); - return ret; - } - - pci_set_master(pci); - - return 0; -} -#endif /* CONFIG_PM_SLEEP */ - -static const struct dev_pm_ops dwc3_pci_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(dwc3_pci_suspend, dwc3_pci_resume) -}; - static struct pci_driver dwc3_pci_driver = { .name = "dwc3-pci", .id_table = dwc3_pci_id_table, .probe = dwc3_pci_probe, .remove = dwc3_pci_remove, - .driver = { - .pm = &dwc3_pci_dev_pm_ops, - }, }; MODULE_AUTHOR("Felipe Balbi "); From 2cd9ddf77e0da465da4535b685587338260eca99 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 12 Jan 2015 14:20:14 +0200 Subject: [PATCH 111/214] usb: dwc3: pci: code cleanup Removing a few items that are not needed anymore and adding separate function for quirks. Acked-by: Huang Rui Signed-off-by: Heikki Krogerus Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-pci.c | 96 ++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 54 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 5c471d2fe732..5f5fa98f399d 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -31,28 +31,50 @@ #define PCI_DEVICE_ID_INTEL_MRFLD 0x119e #define PCI_DEVICE_ID_INTEL_BSW 0x22B7 -struct dwc3_pci { - struct device *dev; - struct platform_device *dwc3; -}; +static int dwc3_pci_quirks(struct pci_dev *pdev) +{ + if (pdev->vendor == PCI_VENDOR_ID_AMD && + pdev->device == PCI_DEVICE_ID_AMD_NL_USB) { + struct dwc3_platform_data pdata; + + memset(&pdata, 0, sizeof(pdata)); + + pdata.has_lpm_erratum = true; + pdata.lpm_nyet_threshold = 0xf; + + pdata.u2exit_lfps_quirk = true; + pdata.u2ss_inp3_quirk = true; + pdata.req_p1p2p3_quirk = true; + pdata.del_p1p2p3_quirk = true; + pdata.del_phy_power_chg_quirk = true; + pdata.lfps_filter_quirk = true; + pdata.rx_detect_poll_quirk = true; + + pdata.tx_de_emphasis_quirk = true; + pdata.tx_de_emphasis = 1; + + /* + * FIXME these quirks should be removed when AMD NL + * taps out + */ + pdata.disable_scramble_quirk = true; + pdata.dis_u3_susphy_quirk = true; + pdata.dis_u2_susphy_quirk = true; + + return platform_device_add_data(pci_get_drvdata(pdev), &pdata, + sizeof(pdata)); + } + + return 0; +} static int dwc3_pci_probe(struct pci_dev *pci, const struct pci_device_id *id) { struct resource res[2]; struct platform_device *dwc3; - struct dwc3_pci *glue; int ret; struct device *dev = &pci->dev; - struct dwc3_platform_data dwc3_pdata; - - memset(&dwc3_pdata, 0x00, sizeof(dwc3_pdata)); - - glue = devm_kzalloc(dev, sizeof(*glue), GFP_KERNEL); - if (!glue) - return -ENOMEM; - - glue->dev = dev; ret = pcim_enable_device(pci); if (ret) { @@ -79,68 +101,34 @@ static int dwc3_pci_probe(struct pci_dev *pci, res[1].name = "dwc_usb3"; res[1].flags = IORESOURCE_IRQ; - if (pci->vendor == PCI_VENDOR_ID_AMD && - pci->device == PCI_DEVICE_ID_AMD_NL_USB) { - dwc3_pdata.has_lpm_erratum = true; - dwc3_pdata.lpm_nyet_threshold = 0xf; - - dwc3_pdata.u2exit_lfps_quirk = true; - dwc3_pdata.u2ss_inp3_quirk = true; - dwc3_pdata.req_p1p2p3_quirk = true; - dwc3_pdata.del_p1p2p3_quirk = true; - dwc3_pdata.del_phy_power_chg_quirk = true; - dwc3_pdata.lfps_filter_quirk = true; - dwc3_pdata.rx_detect_poll_quirk = true; - - dwc3_pdata.tx_de_emphasis_quirk = true; - dwc3_pdata.tx_de_emphasis = 1; - - /* - * FIXME these quirks should be removed when AMD NL - * taps out - */ - dwc3_pdata.disable_scramble_quirk = true; - dwc3_pdata.dis_u3_susphy_quirk = true; - dwc3_pdata.dis_u2_susphy_quirk = true; - } - ret = platform_device_add_resources(dwc3, res, ARRAY_SIZE(res)); if (ret) { dev_err(dev, "couldn't add resources to dwc3 device\n"); return ret; } - pci_set_drvdata(pci, glue); - - ret = platform_device_add_data(dwc3, &dwc3_pdata, sizeof(dwc3_pdata)); + pci_set_drvdata(pci, dwc3); + ret = dwc3_pci_quirks(pci); if (ret) - goto err3; + goto err; - dma_set_coherent_mask(&dwc3->dev, dev->coherent_dma_mask); - - dwc3->dev.dma_mask = dev->dma_mask; - dwc3->dev.dma_parms = dev->dma_parms; dwc3->dev.parent = dev; - glue->dwc3 = dwc3; ret = platform_device_add(dwc3); if (ret) { dev_err(dev, "failed to register dwc3 device\n"); - goto err3; + goto err; } return 0; - -err3: +err: platform_device_put(dwc3); return ret; } static void dwc3_pci_remove(struct pci_dev *pci) { - struct dwc3_pci *glue = pci_get_drvdata(pci); - - platform_device_unregister(glue->dwc3); + platform_device_unregister(pci_get_drvdata(pci)); } static const struct pci_device_id dwc3_pci_id_table[] = { From 18d6b32fca3841f7cd9479b4024abd8a9b299281 Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Thu, 18 Dec 2014 09:55:10 +0100 Subject: [PATCH 112/214] usb: gadget: f_fs: add "no_disconnect" mode Since we can compose gadgets from many functions, there is the problem related to gadget breakage while FunctionFS daemon being closed. FFS function is userspace code so there is no way to know when it will close files (it doesn't matter what is the reason of this situation, it can be daemon logic, program breakage, process kill or any other). So when we have another function in gadget which, for example, sends some amount of data, does some software update or implements some real-time functionality, we may want to keep the gadget connected despite FFS function is no longer functional. We can't just remove one of functions from gadget since it has been enumerated, so the only way to keep entire gadget working is to make broken FFS function deactivated but still visible to host. For this purpose this patch introduces "no_disconnect" mode. It can be enabled by setting mount option "no_disconnect=1", and results with defering function disconnect to the moment of reopen ep0 file or filesystem unmount. After closing all endpoint files, FunctionFS is set to state FFS_DEACTIVATED. When ffs->state == FFS_DEACTIVATED: - function is still bound and visible to host, - setup requests are automatically stalled, - transfers on other endpoints are refused, - epfiles, except ep0, are deleted from the filesystem, - opening ep0 causes the function to be closed, and then FunctionFS is ready for descriptors and string write, - altsetting change causes the function to be closed - we want to keep function alive until another functions are potentialy used, altsetting change means that another configuration is being selected or USB cable was unplugged, which indicates that we don't need to stay longer in FFS_DEACTIVATED state - unmounting of the FunctionFS instance causes the function to be closed. Tested-by: David Cohen Acked-by: Michal Nazarewicz Signed-off-by: Robert Baldyga Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_fs.c | 56 +++++++++++++++++++++++++++--- drivers/usb/gadget/function/u_fs.h | 24 +++++++++++++ 2 files changed, 75 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index a00ee977305c..e78a2c6af8d3 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -605,6 +605,8 @@ static unsigned int ffs_ep0_poll(struct file *file, poll_table *wait) } case FFS_CLOSING: break; + case FFS_DEACTIVATED: + break; } mutex_unlock(&ffs->mutex); @@ -1179,6 +1181,7 @@ struct ffs_sb_fill_data { struct ffs_file_perms perms; umode_t root_mode; const char *dev_name; + bool no_disconnect; struct ffs_data *ffs_data; }; @@ -1249,6 +1252,12 @@ static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts) /* Interpret option */ switch (eq - opts) { + case 13: + if (!memcmp(opts, "no_disconnect", 13)) + data->no_disconnect = !!value; + else + goto invalid; + break; case 5: if (!memcmp(opts, "rmode", 5)) data->root_mode = (value & 0555) | S_IFDIR; @@ -1313,6 +1322,7 @@ ffs_fs_mount(struct file_system_type *t, int flags, .gid = GLOBAL_ROOT_GID, }, .root_mode = S_IFDIR | 0500, + .no_disconnect = false, }; struct dentry *rv; int ret; @@ -1329,6 +1339,7 @@ ffs_fs_mount(struct file_system_type *t, int flags, if (unlikely(!ffs)) return ERR_PTR(-ENOMEM); ffs->file_perms = data.perms; + ffs->no_disconnect = data.no_disconnect; ffs->dev_name = kstrdup(dev_name, GFP_KERNEL); if (unlikely(!ffs->dev_name)) { @@ -1360,6 +1371,7 @@ ffs_fs_kill_sb(struct super_block *sb) kill_litter_super(sb); if (sb->s_fs_info) { ffs_release_dev(sb->s_fs_info); + ffs_data_closed(sb->s_fs_info); ffs_data_put(sb->s_fs_info); } } @@ -1416,7 +1428,11 @@ static void ffs_data_opened(struct ffs_data *ffs) ENTER(); atomic_inc(&ffs->ref); - atomic_inc(&ffs->opened); + if (atomic_add_return(1, &ffs->opened) == 1 && + ffs->state == FFS_DEACTIVATED) { + ffs->state = FFS_CLOSING; + ffs_data_reset(ffs); + } } static void ffs_data_put(struct ffs_data *ffs) @@ -1438,6 +1454,21 @@ static void ffs_data_closed(struct ffs_data *ffs) ENTER(); if (atomic_dec_and_test(&ffs->opened)) { + if (ffs->no_disconnect) { + ffs->state = FFS_DEACTIVATED; + if (ffs->epfiles) { + ffs_epfiles_destroy(ffs->epfiles, + ffs->eps_count); + ffs->epfiles = NULL; + } + if (ffs->setup_state == FFS_SETUP_PENDING) + __ffs_ep0_stall(ffs); + } else { + ffs->state = FFS_CLOSING; + ffs_data_reset(ffs); + } + } + if (atomic_read(&ffs->opened) < 0) { ffs->state = FFS_CLOSING; ffs_data_reset(ffs); } @@ -1615,7 +1646,6 @@ static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count) kfree(epfiles); } - static void ffs_func_eps_disable(struct ffs_function *func) { struct ffs_ep *ep = func->eps; @@ -1628,10 +1658,12 @@ static void ffs_func_eps_disable(struct ffs_function *func) /* pending requests get nuked */ if (likely(ep->ep)) usb_ep_disable(ep->ep); - epfile->ep = NULL; - ++ep; - ++epfile; + + if (epfile) { + epfile->ep = NULL; + ++epfile; + } } while (--count); spin_unlock_irqrestore(&func->ffs->eps_lock, flags); } @@ -2894,6 +2926,13 @@ static int ffs_func_bind(struct usb_configuration *c, /* Other USB function hooks *************************************************/ +static void ffs_reset_work(struct work_struct *work) +{ + struct ffs_data *ffs = container_of(work, + struct ffs_data, reset_work); + ffs_data_reset(ffs); +} + static int ffs_func_set_alt(struct usb_function *f, unsigned interface, unsigned alt) { @@ -2910,6 +2949,13 @@ static int ffs_func_set_alt(struct usb_function *f, if (ffs->func) ffs_func_eps_disable(ffs->func); + if (ffs->state == FFS_DEACTIVATED) { + ffs->state = FFS_CLOSING; + INIT_WORK(&ffs->reset_work, ffs_reset_work); + schedule_work(&ffs->reset_work); + return -ENODEV; + } + if (ffs->state != FFS_ACTIVE) return -ENODEV; diff --git a/drivers/usb/gadget/function/u_fs.h b/drivers/usb/gadget/function/u_fs.h index cd128e31f808..284a1f00a980 100644 --- a/drivers/usb/gadget/function/u_fs.h +++ b/drivers/usb/gadget/function/u_fs.h @@ -19,6 +19,7 @@ #include #include #include +#include #ifdef VERBOSE_DEBUG #ifndef pr_vdebug @@ -92,6 +93,26 @@ enum ffs_state { */ FFS_ACTIVE, + /* + * Function is visible to host, but it's not functional. All + * setup requests are stalled and transfers on another endpoints + * are refused. All epfiles, except ep0, are deleted so there + * is no way to perform any operations on them. + * + * This state is set after closing all functionfs files, when + * mount parameter "no_disconnect=1" has been set. Function will + * remain in deactivated state until filesystem is umounted or + * ep0 is opened again. In the second case functionfs state will + * be reset, and it will be ready for descriptors and strings + * writing. + * + * This is useful only when functionfs is composed to gadget + * with another function which can perform some critical + * operations, and it's strongly desired to have this operations + * completed, even after functionfs files closure. + */ + FFS_DEACTIVATED, + /* * All endpoints have been closed. This state is also set if * we encounter an unrecoverable error. The only @@ -251,6 +272,9 @@ struct ffs_data { kgid_t gid; } file_perms; + bool no_disconnect; + struct work_struct reset_work; + /* * The endpoint files, filled by ffs_epfiles_create(), * destroyed by ffs_epfiles_destroy(). From fe198e34a44ce7f27e39c303d62a754129938194 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Wed, 14 Jan 2015 16:08:38 +0100 Subject: [PATCH 113/214] usb: musb: debugfs: improve copy_from_user() argument While the code is correct and functions well, it's still a bit misleading to add the reference operator in from of the buf argument. This patch simply removes that operator in order to make use of buf slightly better to the eyes. Signed-off-by: Markus Pargmann Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/musb/musb_debugfs.c b/drivers/usb/musb/musb_debugfs.c index ad3701a97389..bb13e0420140 100644 --- a/drivers/usb/musb/musb_debugfs.c +++ b/drivers/usb/musb/musb_debugfs.c @@ -194,7 +194,7 @@ static ssize_t musb_test_mode_write(struct file *file, memset(buf, 0x00, sizeof(buf)); - if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) + if (copy_from_user(buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) return -EFAULT; if (!strncmp(buf, "force host", 9)) From df90f8381972369ff33580e5c4b3c3eea91cc91b Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 14 Jan 2015 23:59:48 +0300 Subject: [PATCH 114/214] usb: gadget: uvc: fix some error codes We're basically saying ERR_CAST(NULL) and PTR_ERR(NULL) here, which is nonsensical. Acked-by: Andrzej Pietrasiewicz Acked-by: Laurent Pinchart Signed-off-by: Dan Carpenter Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/uvc_configfs.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index f69f47a00051..ff3058866a14 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -148,7 +148,7 @@ static struct config_item *uvcg_control_header_make(struct config_group *group, h = kzalloc(sizeof(*h), GFP_KERNEL); if (!h) - return ERR_CAST(h); + return ERR_PTR(-ENOMEM); h->desc.bLength = UVC_DT_HEADER_SIZE(1); h->desc.bDescriptorType = USB_DT_CS_INTERFACE; @@ -840,7 +840,7 @@ static int uvcg_streaming_header_allow_link(struct config_item *src, format_ptr = kzalloc(sizeof(*format_ptr), GFP_KERNEL); if (!format_ptr) { - ret = PTR_ERR(format_ptr); + ret = -ENOMEM; goto out; } ret = 0; @@ -960,7 +960,7 @@ static struct config_item h = kzalloc(sizeof(*h), GFP_KERNEL); if (!h) - return ERR_CAST(h); + return ERR_PTR(-ENOMEM); INIT_LIST_HEAD(&h->formats); h->desc.bDescriptorType = USB_DT_CS_INTERFACE; @@ -1278,7 +1278,7 @@ static struct config_item *uvcg_frame_make(struct config_group *group, h = kzalloc(sizeof(*h), GFP_KERNEL); if (!h) - return ERR_CAST(h); + return ERR_PTR(-ENOMEM); h->frame.b_descriptor_type = USB_DT_CS_INTERFACE; h->frame.b_frame_index = 1; @@ -1563,7 +1563,7 @@ static struct config_group *uvcg_uncompressed_make(struct config_group *group, h = kzalloc(sizeof(*h), GFP_KERNEL); if (!h) - return ERR_CAST(h); + return ERR_PTR(-ENOMEM); h->desc.bLength = UVC_DT_FORMAT_UNCOMPRESSED_SIZE; h->desc.bDescriptorType = USB_DT_CS_INTERFACE; @@ -1772,7 +1772,7 @@ static struct config_group *uvcg_mjpeg_make(struct config_group *group, h = kzalloc(sizeof(*h), GFP_KERNEL); if (!h) - return ERR_CAST(h); + return ERR_PTR(-ENOMEM); h->desc.bLength = UVC_DT_FORMAT_MJPEG_SIZE; h->desc.bDescriptorType = USB_DT_CS_INTERFACE; @@ -2124,7 +2124,7 @@ static int uvcg_streaming_class_allow_link(struct config_item *src, count += 2; /* color_matching, NULL */ *class_array = kcalloc(count, sizeof(void *), GFP_KERNEL); if (!*class_array) { - ret = PTR_ERR(*class_array); + ret = -ENOMEM; goto unlock; } From ceeb010ba2534f0b78efde6b3aa7d208148df92f Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 15 Jan 2015 00:02:18 +0300 Subject: [PATCH 115/214] usb: gadget: uvc: remove an impossible condition "num" is a u32 so "(num > 0xFFFFFFFF)" is never true. Also the range is already checked in kstrtou32(). Acked-by: Andrzej Pietrasiewicz Acked-by: Laurent Pinchart Signed-off-by: Dan Carpenter Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/uvc_configfs.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index ff3058866a14..6d9c68163fe1 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -1156,8 +1156,6 @@ static inline int __uvcg_fill_frm_intrv(char *buf, void *priv) ret = kstrtou32(buf, 0, &num); if (ret) return ret; - if (num > 0xFFFFFFFF) - return -EINVAL; interv = priv; **interv = cpu_to_le32(num); From c5b2dc68a7a7e31d8e88b3bf9d45bd9e78e9fb78 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 15 Jan 2015 00:03:08 +0300 Subject: [PATCH 116/214] usb: gadget: uvc: memory leak in uvcg_frame_make() We need to add a kfree(h) on an error path. Acked-by: Andrzej Pietrasiewicz Acked-by: Laurent Pinchart Signed-off-by: Dan Carpenter Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/uvc_configfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index 6d9c68163fe1..09028d21a3b6 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -1300,6 +1300,7 @@ static struct config_item *uvcg_frame_make(struct config_group *group, h->fmt_type = UVCG_MJPEG; } else { mutex_unlock(&opts->lock); + kfree(h); return ERR_PTR(-EINVAL); } ++fmt->num_frames; From 3c4c733ca9e3f88e3107b6e23b9789bf7769e1f4 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 15 Jan 2015 00:06:35 +0300 Subject: [PATCH 117/214] usb: gadget: uvc: cleanup UVCG_FRAME_ATTR macro 1) Change "conv" an "vnoc" to "to_cpu_endian" to "to_little_endian". 2) No need to check the "limit" because that is already handled in kstrtoXX so delete that parameter along with the check. 3) By using a "bits" parameter, we can combine the "uxx" parameter and the "str2u" parameters. 4) The kstrtou##bits() conversion does not need to be done under the mutex so move it to the start of the function. 5) Change the name of "identity_conv" to "noop_conversion". Acked-by: Andrzej Pietrasiewicz Acked-by: Laurent Pinchart Signed-off-by: Dan Carpenter Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/uvc_configfs.c | 44 +++++++++------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index 09028d21a3b6..cc2a6139b2c8 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -1030,7 +1030,7 @@ static struct configfs_item_operations uvcg_frame_item_ops = { .store_attribute = uvcg_frame_attr_store, }; -#define UVCG_FRAME_ATTR(cname, aname, conv, str2u, uxx, vnoc, limit) \ +#define UVCG_FRAME_ATTR(cname, aname, to_cpu_endian, to_little_endian, bits) \ static ssize_t uvcg_frame_##cname##_show(struct uvcg_frame *f, char *page)\ { \ struct f_uvc_opts *opts; \ @@ -1044,7 +1044,7 @@ static ssize_t uvcg_frame_##cname##_show(struct uvcg_frame *f, char *page)\ opts = to_f_uvc_opts(opts_item); \ \ mutex_lock(&opts->lock); \ - result = sprintf(page, "%d\n", conv(f->frame.cname)); \ + result = sprintf(page, "%d\n", to_cpu_endian(f->frame.cname)); \ mutex_unlock(&opts->lock); \ \ mutex_unlock(su_mutex); \ @@ -1059,7 +1059,11 @@ static ssize_t uvcg_frame_##cname##_store(struct uvcg_frame *f, \ struct uvcg_format *fmt; \ struct mutex *su_mutex = &f->item.ci_group->cg_subsys->su_mutex;\ int ret; \ - uxx num; \ + u##bits num; \ + \ + ret = kstrtou##bits(page, 0, &num); \ + if (ret) \ + return ret; \ \ mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ \ @@ -1073,15 +1077,7 @@ static ssize_t uvcg_frame_##cname##_store(struct uvcg_frame *f, \ goto end; \ } \ \ - ret = str2u(page, 0, &num); \ - if (ret) \ - goto end; \ - \ - if (num > limit) { \ - ret = -EINVAL; \ - goto end; \ - } \ - f->frame.cname = vnoc(num); \ + f->frame.cname = to_little_endian(num); \ ret = len; \ end: \ mutex_unlock(&opts->lock); \ @@ -1095,24 +1091,20 @@ static struct uvcg_frame_attribute \ uvcg_frame_##cname##_show, \ uvcg_frame_##cname##_store) -#define identity_conv(x) (x) +#define noop_conversion(x) (x) -UVCG_FRAME_ATTR(bm_capabilities, bmCapabilities, identity_conv, kstrtou8, u8, - identity_conv, 0xFF); -UVCG_FRAME_ATTR(w_width, wWidth, le16_to_cpu, kstrtou16, u16, cpu_to_le16, - 0xFFFF); -UVCG_FRAME_ATTR(w_height, wHeight, le16_to_cpu, kstrtou16, u16, cpu_to_le16, - 0xFFFF); -UVCG_FRAME_ATTR(dw_min_bit_rate, dwMinBitRate, le32_to_cpu, kstrtou32, u32, - cpu_to_le32, 0xFFFFFFFF); -UVCG_FRAME_ATTR(dw_max_bit_rate, dwMaxBitRate, le32_to_cpu, kstrtou32, u32, - cpu_to_le32, 0xFFFFFFFF); +UVCG_FRAME_ATTR(bm_capabilities, bmCapabilities, noop_conversion, + noop_conversion, 8); +UVCG_FRAME_ATTR(w_width, wWidth, le16_to_cpu, cpu_to_le16, 16); +UVCG_FRAME_ATTR(w_height, wHeight, le16_to_cpu, cpu_to_le16, 16); +UVCG_FRAME_ATTR(dw_min_bit_rate, dwMinBitRate, le32_to_cpu, cpu_to_le32, 32); +UVCG_FRAME_ATTR(dw_max_bit_rate, dwMaxBitRate, le32_to_cpu, cpu_to_le32, 32); UVCG_FRAME_ATTR(dw_max_video_frame_buffer_size, dwMaxVideoFrameBufferSize, - le32_to_cpu, kstrtou32, u32, cpu_to_le32, 0xFFFFFFFF); + le32_to_cpu, cpu_to_le32, 32); UVCG_FRAME_ATTR(dw_default_frame_interval, dwDefaultFrameInterval, - le32_to_cpu, kstrtou32, u32, cpu_to_le32, 0xFFFFFFFF); + le32_to_cpu, cpu_to_le32, 32); -#undef identity_conv +#undef noop_conversion #undef UVCG_FRAME_ATTR From da89dba1a0c69ed01e33ebf41327b90993d08492 Mon Sep 17 00:00:00 2001 From: Paul Zimmerman Date: Thu, 15 Jan 2015 02:45:12 +0000 Subject: [PATCH 118/214] usb: phy: make GPIOs optional for the generic phy The use of GPIOs should be optional for the generic phy, otherwise the Altera SOCFPGA platform at least is broken. Fixes breakage caused by a combination of e9f2cefb0cd "usb: phy: generic: migrate to gpio_desc" and 135b3c4304d "usb: dwc2: platform: add generic PHY framework support". Reviewed-by: Robert Jarzmik Signed-off-by: Paul Zimmerman Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-generic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c index dd05254241fb..9a826ff31951 100644 --- a/drivers/usb/phy/phy-generic.c +++ b/drivers/usb/phy/phy-generic.c @@ -218,10 +218,10 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop, clk_rate = 0; needs_vcc = of_property_read_bool(node, "vcc-supply"); - nop->gpiod_reset = devm_gpiod_get(dev, "reset-gpios"); + nop->gpiod_reset = devm_gpiod_get_optional(dev, "reset-gpios"); err = PTR_ERR(nop->gpiod_reset); if (!err) { - nop->gpiod_vbus = devm_gpiod_get(dev, + nop->gpiod_vbus = devm_gpiod_get_optional(dev, "vbus-detect-gpio"); err = PTR_ERR(nop->gpiod_vbus); } From 27f387059bedc3c4b3eb5b4c8bfe229dba59ce24 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 15 Jan 2015 13:40:04 +0200 Subject: [PATCH 119/214] usb: gadget: ethernet: re-use %pM specifier to print MAC Instead of custom approach the patch converts code to use %pM specifier to print MAC. Signed-off-by: Andy Shevchenko Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/u_ether.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index 6e6f87656e7b..f1fd777ef4ec 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -729,9 +729,7 @@ static int get_ether_addr_str(u8 dev_addr[ETH_ALEN], char *str, int len) if (len < 18) return -EINVAL; - snprintf(str, len, "%02x:%02x:%02x:%02x:%02x:%02x", - dev_addr[0], dev_addr[1], dev_addr[2], - dev_addr[3], dev_addr[4], dev_addr[5]); + snprintf(str, len, "%pM", dev_addr); return 18; } From b0bb9bb6ce01e37e47db04a31fd93dbe2d2be3d3 Mon Sep 17 00:00:00 2001 From: Paul Zimmerman Date: Thu, 15 Jan 2015 19:21:46 +0000 Subject: [PATCH 120/214] Revert "usb: dwc2: add bus suspend/resume for dwc2" This reverts commit 0cf884e819e05437287a668b9bfcc198bab6329c. Even after applying the follow-on patch at https://patchwork.kernel.org/patch/5325111 there are still problems with device connect on the Altera SOCFPGA platform at least. One possible fix would be to add a whitelist to enable suspend/resume on platforms where it does work correctly. Signed-off-by: Paul Zimmerman Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/hcd.c | 88 ++++++------------------------------------ 1 file changed, 11 insertions(+), 77 deletions(-) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index a0cd9db6f4cd..755e16b9cbb3 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -1473,30 +1473,6 @@ static void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex) } } -static void dwc2_port_resume(struct dwc2_hsotg *hsotg) -{ - u32 hprt0; - - /* After clear the Stop PHY clock bit, we should wait for a moment - * for PLL work stable with clock output. - */ - writel(0, hsotg->regs + PCGCTL); - usleep_range(2000, 4000); - - hprt0 = dwc2_read_hprt0(hsotg); - hprt0 |= HPRT0_RES; - writel(hprt0, hsotg->regs + HPRT0); - hprt0 &= ~HPRT0_SUSP; - /* according to USB2.0 Spec 7.1.7.7, the host must send the resume - * signal for at least 20ms - */ - usleep_range(20000, 25000); - - hprt0 &= ~HPRT0_RES; - writel(hprt0, hsotg->regs + HPRT0); - hsotg->lx_state = DWC2_L0; -} - /* 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) @@ -1542,7 +1518,17 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, case USB_PORT_FEAT_SUSPEND: dev_dbg(hsotg->dev, "ClearPortFeature USB_PORT_FEAT_SUSPEND\n"); - dwc2_port_resume(hsotg); + 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: @@ -2315,55 +2301,6 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd) usleep_range(1000, 3000); } -static int _dwc2_hcd_suspend(struct usb_hcd *hcd) -{ - struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); - u32 hprt0; - - if (!((hsotg->op_state == OTG_STATE_B_HOST) || - (hsotg->op_state == OTG_STATE_A_HOST))) - return 0; - - /* TODO: We get into suspend from 'on' state, maybe we need to do - * something if we get here from DWC2_L1(LPM sleep) state one day. - */ - if (hsotg->lx_state != DWC2_L0) - return 0; - - hprt0 = dwc2_read_hprt0(hsotg); - if (hprt0 & HPRT0_CONNSTS) { - dwc2_port_suspend(hsotg, 1); - } else { - u32 pcgctl = readl(hsotg->regs + PCGCTL); - - pcgctl |= PCGCTL_STOPPCLK; - writel(pcgctl, hsotg->regs + PCGCTL); - } - - return 0; -} - -static int _dwc2_hcd_resume(struct usb_hcd *hcd) -{ - struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); - u32 hprt0; - - if (!((hsotg->op_state == OTG_STATE_B_HOST) || - (hsotg->op_state == OTG_STATE_A_HOST))) - return 0; - - if (hsotg->lx_state != DWC2_L2) - return 0; - - hprt0 = dwc2_read_hprt0(hsotg); - if ((hprt0 & HPRT0_CONNSTS) && (hprt0 & HPRT0_SUSP)) - dwc2_port_resume(hsotg); - else - writel(0, hsotg->regs + PCGCTL); - - return 0; -} - /* Returns the current frame number */ static int _dwc2_hcd_get_frame_number(struct usb_hcd *hcd) { @@ -2734,9 +2671,6 @@ static struct hc_driver dwc2_hc_driver = { .hub_status_data = _dwc2_hcd_hub_status_data, .hub_control = _dwc2_hcd_hub_control, .clear_tt_buffer_complete = _dwc2_hcd_clear_tt_buffer_complete, - - .bus_suspend = _dwc2_hcd_suspend, - .bus_resume = _dwc2_hcd_resume, }; /* From 16272ae77e0f167af70eabd476059137d64569a8 Mon Sep 17 00:00:00 2001 From: Paul Zimmerman Date: Thu, 15 Jan 2015 20:14:10 +0000 Subject: [PATCH 121/214] MAINTAINERS: update maintainer entry for dwc2 driver Update the MAINTAINERS entry for the dwc2 driver to show John Youn as the new maintainer Signed-off-by: Paul Zimmerman Signed-off-by: Felipe Balbi --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 2fa385321245..97de006f38f0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3028,7 +3028,7 @@ S: Maintained F: drivers/platform/x86/dell-wmi.c DESIGNWARE USB2 DRD IP DRIVER -M: Paul Zimmerman +M: John Youn L: linux-usb@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git S: Maintained From 06ab8b04d232c80cb4b23984290ce413058cc975 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Fri, 16 Jan 2015 15:14:27 +0100 Subject: [PATCH 122/214] usb: gadget: uvc: preserve the address passed to kfree() __uvcg_fill_strm() called from __uvcg_iter_stream_cls() might have advanced the "data" even if __uvcg_iter_stream_cls() returns an error, so use a backup copy as an argument to kfree(). Acked-by: Laurent Pinchart Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/uvc_configfs.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index cc2a6139b2c8..49f25e806e38 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -2086,7 +2086,7 @@ static int uvcg_streaming_class_allow_link(struct config_item *src, struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex; struct uvc_descriptor_header ***class_array, **cl_arr; struct uvcg_streaming_header *target_hdr; - void *data; + void *data, *data_save; size_t size = 0, count = 0; int ret = -EINVAL; @@ -2119,7 +2119,7 @@ static int uvcg_streaming_class_allow_link(struct config_item *src, goto unlock; } - data = kzalloc(size, GFP_KERNEL); + data = data_save = kzalloc(size, GFP_KERNEL); if (!data) { kfree(*class_array); *class_array = NULL; @@ -2132,7 +2132,11 @@ static int uvcg_streaming_class_allow_link(struct config_item *src, if (ret) { kfree(*class_array); *class_array = NULL; - kfree(data); + /* + * __uvcg_fill_strm() called from __uvcg_iter_stream_cls() + * might have advanced the "data", so use a backup copy + */ + kfree(data_save); goto unlock; } *cl_arr = (struct uvc_descriptor_header *)&opts->uvc_color_matching; From 3d040de802d82fa744f599d8a886dda3462fb6e6 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Mon, 19 Jan 2015 01:54:15 +0300 Subject: [PATCH 123/214] usb: dwc2: hcd: use HUB_CHAR_* Fix using the bare number to set the 'wHubCharacteristics' field of the Hub Descriptor while the values are #define'd in . Signed-off-by: Sergei Shtylyov Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/hcd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 755e16b9cbb3..c3e66f0be02c 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -1608,7 +1608,9 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, hub_desc->bDescLength = 9; hub_desc->bDescriptorType = 0x29; hub_desc->bNbrPorts = 1; - hub_desc->wHubCharacteristics = cpu_to_le16(0x08); + hub_desc->wHubCharacteristics = + cpu_to_le16(HUB_CHAR_COMMON_LPSM | + HUB_CHAR_INDV_PORT_OCPM); hub_desc->bPwrOn2PwrGood = 1; hub_desc->bHubContrCurrent = 0; hub_desc->u.hs.DeviceRemovable[0] = 0; From d906d61cf8a3080b3bf7fb2e12efc2b645d514d4 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Mon, 19 Jan 2015 01:55:55 +0300 Subject: [PATCH 124/214] usb: dummy_hcd: use HUB_CHAR_* Fix using the bare numbers to set the 'wHubCharacteristics' field of the Hub Descriptor while the values are #define'd in . Acked-by: Alan Stern Signed-off-by: Sergei Shtylyov Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/dummy_hcd.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index 9c598801404c..270c1ec650fa 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -1924,7 +1924,9 @@ ss_hub_descriptor(struct usb_hub_descriptor *desc) memset(desc, 0, sizeof *desc); desc->bDescriptorType = 0x2a; desc->bDescLength = 12; - desc->wHubCharacteristics = cpu_to_le16(0x0001); + desc->wHubCharacteristics = cpu_to_le16( + HUB_CHAR_INDV_PORT_LPSM | + HUB_CHAR_COMMON_OCPM); desc->bNbrPorts = 1; desc->u.ss.bHubHdrDecLat = 0x04; /* Worst case: 0.4 micro sec*/ desc->u.ss.DeviceRemovable = 0xffff; @@ -1935,7 +1937,9 @@ static inline void hub_descriptor(struct usb_hub_descriptor *desc) memset(desc, 0, sizeof *desc); desc->bDescriptorType = 0x29; desc->bDescLength = 9; - desc->wHubCharacteristics = cpu_to_le16(0x0001); + desc->wHubCharacteristics = cpu_to_le16( + HUB_CHAR_INDV_PORT_LPSM | + HUB_CHAR_COMMON_OCPM); desc->bNbrPorts = 1; desc->u.hs.DeviceRemovable[0] = 0xff; desc->u.hs.DeviceRemovable[1] = 0xff; From c509ba6e48d394aa7fe0f04c38493967c1268317 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Mon, 19 Jan 2015 01:57:18 +0300 Subject: [PATCH 125/214] usb: musb: virthub: use HUB_CHAR_* Fix using the bare numbers to set the 'wHubCharacteristics' field of the Hub Descriptor while the values are #define'd in . Signed-off-by: Sergei Shtylyov Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_virthub.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c index b072420e44f5..662de58630e9 100644 --- a/drivers/usb/musb/musb_virthub.c +++ b/drivers/usb/musb/musb_virthub.c @@ -349,9 +349,9 @@ int musb_hub_control( desc->bDescriptorType = 0x29; desc->bNbrPorts = 1; desc->wHubCharacteristics = cpu_to_le16( - 0x0001 /* per-port power switching */ - | 0x0010 /* no overcurrent reporting */ - ); + HUB_CHAR_INDV_PORT_LPSM /* per-port power switching */ + | HUB_CHAR_NO_OCPM /* no overcurrent reporting */ + ); desc->bPwrOn2PwrGood = 5; /* msec/2 */ desc->bHubContrCurrent = 0; From 16eb1a67f3e6f8b90474b882fe210df229da7573 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Mon, 19 Jan 2015 01:59:09 +0300 Subject: [PATCH 126/214] usb: renesas_usbhs: mod_host: use HUB_CHAR_* Fix using the bare number to set the 'wHubCharacteristics' field of the Hub Descriptor while the values are #define'd in . Signed-off-by: Sergei Shtylyov Signed-off-by: Felipe Balbi --- drivers/usb/renesas_usbhs/mod_host.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/renesas_usbhs/mod_host.c b/drivers/usb/renesas_usbhs/mod_host.c index f0d323125871..96eead619282 100644 --- a/drivers/usb/renesas_usbhs/mod_host.c +++ b/drivers/usb/renesas_usbhs/mod_host.c @@ -1234,7 +1234,8 @@ static int __usbhsh_hub_get_status(struct usbhsh_hpriv *hpriv, desc->bNbrPorts = roothub_id; desc->bDescLength = 9; desc->bPwrOn2PwrGood = 0; - desc->wHubCharacteristics = cpu_to_le16(0x0011); + desc->wHubCharacteristics = + cpu_to_le16(HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_NO_OCPM); desc->u.hs.DeviceRemovable[0] = (roothub_id << 1); desc->u.hs.DeviceRemovable[1] = ~0; dev_dbg(dev, "%s :: GetHubDescriptor\n", __func__); From 6e3f53ab5bd3092e195b290802208a39261fdf26 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Mon, 19 Jan 2015 12:53:16 +0900 Subject: [PATCH 127/214] usb: renesas_usbhs: add usbhsf_dma_init_pdev() function To add support for requesting DT DMA in the future, this patch adds usbhsf_dma_init_pdev() function. Signed-off-by: Yoshihiro Shimoda Signed-off-by: Felipe Balbi --- drivers/usb/renesas_usbhs/fifo.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index f46271ce1b15..48e31b94313d 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -1054,10 +1054,8 @@ static void usbhsf_dma_quit(struct usbhs_priv *priv, struct usbhs_fifo *fifo) fifo->rx_chan = NULL; } -static void usbhsf_dma_init(struct usbhs_priv *priv, - struct usbhs_fifo *fifo) +static void usbhsf_dma_init_pdev(struct usbhs_fifo *fifo) { - struct device *dev = usbhs_priv_to_dev(priv); dma_cap_mask_t mask; dma_cap_zero(mask); @@ -1069,6 +1067,14 @@ static void usbhsf_dma_init(struct usbhs_priv *priv, dma_cap_set(DMA_SLAVE, mask); fifo->rx_chan = dma_request_channel(mask, usbhsf_dma_filter, &fifo->rx_slave); +} + +static void usbhsf_dma_init(struct usbhs_priv *priv, + struct usbhs_fifo *fifo) +{ + struct device *dev = usbhs_priv_to_dev(priv); + + usbhsf_dma_init_pdev(fifo); if (fifo->tx_chan || fifo->rx_chan) dev_dbg(dev, "enable DMAEngine (%s%s%s)\n", From abd2dbf6bb1b5f3a03a8c76b1a8879da1dd30caa Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Mon, 19 Jan 2015 12:53:17 +0900 Subject: [PATCH 128/214] usb: renesas_usbhs: add support for requesting DT DMA This patch adds dma_request_slave_channel_reason() calling to request dma slave channels for multiplatform environment. Signed-off-by: Yoshihiro Shimoda Signed-off-by: Felipe Balbi --- .../devicetree/bindings/usb/renesas_usbhs.txt | 2 ++ drivers/usb/renesas_usbhs/fifo.c | 11 ++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/usb/renesas_usbhs.txt b/Documentation/devicetree/bindings/usb/renesas_usbhs.txt index b08c903f8668..61b045b6d50e 100644 --- a/Documentation/devicetree/bindings/usb/renesas_usbhs.txt +++ b/Documentation/devicetree/bindings/usb/renesas_usbhs.txt @@ -14,6 +14,8 @@ Optional properties: function should be enabled - phys: phandle + phy specifier pair - phy-names: must be "usb" + - dmas: Must contain a list of references to DMA specifiers. + - dma-names : Must contain a list of DMA names, "tx" or "rx". Example: usbhs: usb@e6590000 { diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 48e31b94313d..4c086b1cda04 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -1069,12 +1069,21 @@ static void usbhsf_dma_init_pdev(struct usbhs_fifo *fifo) &fifo->rx_slave); } +static void usbhsf_dma_init_dt(struct device *dev, struct usbhs_fifo *fifo) +{ + fifo->tx_chan = dma_request_slave_channel_reason(dev, "tx"); + fifo->rx_chan = dma_request_slave_channel_reason(dev, "rx"); +} + static void usbhsf_dma_init(struct usbhs_priv *priv, struct usbhs_fifo *fifo) { struct device *dev = usbhs_priv_to_dev(priv); - usbhsf_dma_init_pdev(fifo); + if (dev->of_node) + usbhsf_dma_init_dt(dev, fifo); + else + usbhsf_dma_init_pdev(fifo); if (fifo->tx_chan || fifo->rx_chan) dev_dbg(dev, "enable DMAEngine (%s%s%s)\n", From 0149b07a9e28b0d8bd2fc1c238ffe7d530c2673f Mon Sep 17 00:00:00 2001 From: Bin Liu Date: Mon, 26 Jan 2015 16:22:06 -0600 Subject: [PATCH 129/214] usb: musb: cppi41: correct the macro name EP_MODE_AUTOREG_* The macro EP_MODE_AUTOREG_* should be called EP_MODE_AUTOREQ_*, as they are used for register AUTOREQ. Signed-off-by: Bin Liu Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_cppi41.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c index c39a16ad7832..6af10e7e66a7 100644 --- a/drivers/usb/musb/musb_cppi41.c +++ b/drivers/usb/musb/musb_cppi41.c @@ -9,9 +9,9 @@ #define RNDIS_REG(x) (0x80 + ((x - 1) * 4)) -#define EP_MODE_AUTOREG_NONE 0 -#define EP_MODE_AUTOREG_ALL_NEOP 1 -#define EP_MODE_AUTOREG_ALWAYS 3 +#define EP_MODE_AUTOREQ_NONE 0 +#define EP_MODE_AUTOREQ_ALL_NEOP 1 +#define EP_MODE_AUTOREQ_ALWAYS 3 #define EP_MODE_DMA_TRANSPARENT 0 #define EP_MODE_DMA_RNDIS 1 @@ -404,19 +404,19 @@ static bool cppi41_configure_channel(struct dma_channel *channel, /* auto req */ cppi41_set_autoreq_mode(cppi41_channel, - EP_MODE_AUTOREG_ALL_NEOP); + EP_MODE_AUTOREQ_ALL_NEOP); } else { musb_writel(musb->ctrl_base, RNDIS_REG(cppi41_channel->port_num), 0); cppi41_set_dma_mode(cppi41_channel, EP_MODE_DMA_TRANSPARENT); cppi41_set_autoreq_mode(cppi41_channel, - EP_MODE_AUTOREG_NONE); + EP_MODE_AUTOREQ_NONE); } } else { /* fallback mode */ cppi41_set_dma_mode(cppi41_channel, EP_MODE_DMA_TRANSPARENT); - cppi41_set_autoreq_mode(cppi41_channel, EP_MODE_AUTOREG_NONE); + cppi41_set_autoreq_mode(cppi41_channel, EP_MODE_AUTOREQ_NONE); len = min_t(u32, packet_sz, len); } cppi41_channel->prog_len = len; From cb83df77f3ec151d68a1b6be957207e6fc7b7f50 Mon Sep 17 00:00:00 2001 From: Bin Liu Date: Mon, 26 Jan 2015 16:22:07 -0600 Subject: [PATCH 130/214] usb: musb: cppi41: improve rx channel abort routine 1. set AUTOREQ to NONE at the beginning of teardown; 2. add delay for dma pipeline to drain; 3. Do not set USB_TDOWN bit for RX teardown. The CPPI hw has an issue that when tearing down a RX channel, if another RX channel is receiving data, the CPPI will lockup. To workaround the issue, do not set the CPPI TD bit. The steps before this point ensures the CPPI channel will be torn down properly. Signed-off-by: Bin Liu Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_cppi41.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c index 6af10e7e66a7..be84562d021b 100644 --- a/drivers/usb/musb/musb_cppi41.c +++ b/drivers/usb/musb/musb_cppi41.c @@ -549,10 +549,15 @@ static int cppi41_dma_channel_abort(struct dma_channel *channel) csr &= ~MUSB_TXCSR_DMAENAB; musb_writew(epio, MUSB_TXCSR, csr); } else { + cppi41_set_autoreq_mode(cppi41_channel, EP_MODE_AUTOREQ_NONE); + csr = musb_readw(epio, MUSB_RXCSR); csr &= ~(MUSB_RXCSR_H_REQPKT | MUSB_RXCSR_DMAENAB); musb_writew(epio, MUSB_RXCSR, csr); + /* wait to drain cppi dma pipe line */ + udelay(50); + csr = musb_readw(epio, MUSB_RXCSR); if (csr & MUSB_RXCSR_RXPKTRDY) { csr |= MUSB_RXCSR_FLUSHFIFO; @@ -566,13 +571,14 @@ static int cppi41_dma_channel_abort(struct dma_channel *channel) tdbit <<= 16; do { - musb_writel(musb->ctrl_base, USB_TDOWN, tdbit); + if (is_tx) + musb_writel(musb->ctrl_base, USB_TDOWN, tdbit); ret = dmaengine_terminate_all(cppi41_channel->dc); } while (ret == -EAGAIN); - musb_writel(musb->ctrl_base, USB_TDOWN, tdbit); - if (is_tx) { + musb_writel(musb->ctrl_base, USB_TDOWN, tdbit); + csr = musb_readw(epio, MUSB_TXCSR); if (csr & MUSB_TXCSR_TXPKTRDY) { csr |= MUSB_TXCSR_FLUSHFIFO; From df6738d0d23321da834e9025845e26e09b0f6615 Mon Sep 17 00:00:00 2001 From: Mario Schuknecht Date: Mon, 26 Jan 2015 20:30:27 +0100 Subject: [PATCH 131/214] usb: gadget: Fix os desc test USB vendor type is encoded in field bmRequestType. Make test USB_TYPE_VENDOR with bRequestType instead of bRequest. Signed-off-by: Mario Schuknecht Signed-off-by: Felipe Balbi --- drivers/usb/gadget/composite.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 617835348569..13adfd1a3f54 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1655,7 +1655,7 @@ unknown: * OS descriptors handling */ if (cdev->use_os_string && cdev->os_desc_config && - (ctrl->bRequest & USB_TYPE_VENDOR) && + (ctrl->bRequestType & USB_TYPE_VENDOR) && ctrl->bRequest == cdev->b_vendor_code) { struct usb_request *req; struct usb_configuration *os_desc_cfg; From acba23fec527012e901636e4ba091ee25461c943 Mon Sep 17 00:00:00 2001 From: Mario Schuknecht Date: Mon, 26 Jan 2015 20:40:21 +0100 Subject: [PATCH 132/214] usb: gadget: f_fs: Fix loop variable Use if-loop variable 'epfile' instead of start variable 'epfiles'. Now the correct endpoint file name is stored. Signed-off-by: Mario Schuknecht Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_fs.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index e78a2c6af8d3..14e44d7083a1 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -1611,10 +1611,10 @@ static int ffs_epfiles_create(struct ffs_data *ffs) mutex_init(&epfile->mutex); init_waitqueue_head(&epfile->wait); if (ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR) - sprintf(epfiles->name, "ep%02x", ffs->eps_addrmap[i]); + sprintf(epfile->name, "ep%02x", ffs->eps_addrmap[i]); else - sprintf(epfiles->name, "ep%u", i); - epfile->dentry = ffs_sb_create_file(ffs->sb, epfiles->name, + sprintf(epfile->name, "ep%u", i); + epfile->dentry = ffs_sb_create_file(ffs->sb, epfile->name, epfile, &ffs_epfile_operations); if (unlikely(!epfile->dentry)) { From 5e33f6fdf735cda1d4580fe6f1878da05718fe73 Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Fri, 23 Jan 2015 13:41:01 +0100 Subject: [PATCH 133/214] usb: gadget: ffs: add eventfd notification about ffs events Add eventfd which notifies userspace about ep0 events and AIO completion events. It simplifies using of FunctionFS with event loop, because now we need to poll on single file (instead of polling on ep0 and eventfd's supplied to AIO layer). FunctionFS eventfd is not triggered if another eventfd is supplied to AIO layer (in AIO request). It can be useful, for example, when we want to handle AIO transations for chosen endpoint in separate thread. Signed-off-by: Robert Baldyga Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_fs.c | 29 ++++++++++++++++++++++++++++- drivers/usb/gadget/function/u_fs.h | 1 + include/uapi/linux/usb/functionfs.h | 1 + 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 14e44d7083a1..af98b096af2f 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "u_fs.h" #include "u_f.h" @@ -153,6 +154,8 @@ struct ffs_io_data { struct usb_ep *ep; struct usb_request *req; + + struct ffs_data *ffs; }; struct ffs_desc_helper { @@ -674,6 +677,9 @@ static void ffs_user_copy_worker(struct work_struct *work) aio_complete(io_data->kiocb, ret, ret); + if (io_data->ffs->ffs_eventfd && !io_data->kiocb->ki_eventfd) + eventfd_signal(io_data->ffs->ffs_eventfd, 1); + usb_ep_free_request(io_data->ep, io_data->req); io_data->kiocb->private = NULL; @@ -827,6 +833,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) io_data->buf = data; io_data->ep = ep->ep; io_data->req = req; + io_data->ffs = epfile->ffs; req->context = io_data; req->complete = ffs_epfile_async_io_complete; @@ -1510,6 +1517,9 @@ static void ffs_data_clear(struct ffs_data *ffs) if (ffs->epfiles) ffs_epfiles_destroy(ffs->epfiles, ffs->eps_count); + if (ffs->ffs_eventfd) + eventfd_ctx_put(ffs->ffs_eventfd); + kfree(ffs->raw_descs_data); kfree(ffs->raw_strings); kfree(ffs->stringtabs); @@ -2169,7 +2179,8 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, FUNCTIONFS_HAS_HS_DESC | FUNCTIONFS_HAS_SS_DESC | FUNCTIONFS_HAS_MS_OS_DESC | - FUNCTIONFS_VIRTUAL_ADDR)) { + FUNCTIONFS_VIRTUAL_ADDR | + FUNCTIONFS_EVENTFD)) { ret = -ENOSYS; goto error; } @@ -2180,6 +2191,20 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, goto error; } + if (flags & FUNCTIONFS_EVENTFD) { + if (len < 4) + goto error; + ffs->ffs_eventfd = + eventfd_ctx_fdget((int)get_unaligned_le32(data)); + if (IS_ERR(ffs->ffs_eventfd)) { + ret = PTR_ERR(ffs->ffs_eventfd); + ffs->ffs_eventfd = NULL; + goto error; + } + data += 4; + len -= 4; + } + /* Read fs_count, hs_count and ss_count (if present) */ for (i = 0; i < 3; ++i) { if (!(flags & (1 << i))) { @@ -2454,6 +2479,8 @@ static void __ffs_event_add(struct ffs_data *ffs, pr_vdebug("adding event %d\n", type); ffs->ev.types[ffs->ev.count++] = type; wake_up_locked(&ffs->ev.waitq); + if (ffs->ffs_eventfd) + eventfd_signal(ffs->ffs_eventfd, 1); } static void ffs_event_add(struct ffs_data *ffs, diff --git a/drivers/usb/gadget/function/u_fs.h b/drivers/usb/gadget/function/u_fs.h index 284a1f00a980..60139854e0b1 100644 --- a/drivers/usb/gadget/function/u_fs.h +++ b/drivers/usb/gadget/function/u_fs.h @@ -272,6 +272,7 @@ struct ffs_data { kgid_t gid; } file_perms; + struct eventfd_ctx *ffs_eventfd; bool no_disconnect; struct work_struct reset_work; diff --git a/include/uapi/linux/usb/functionfs.h b/include/uapi/linux/usb/functionfs.h index 295ba299e7bd..108dd7997014 100644 --- a/include/uapi/linux/usb/functionfs.h +++ b/include/uapi/linux/usb/functionfs.h @@ -20,6 +20,7 @@ enum functionfs_flags { FUNCTIONFS_HAS_SS_DESC = 4, FUNCTIONFS_HAS_MS_OS_DESC = 8, FUNCTIONFS_VIRTUAL_ADDR = 16, + FUNCTIONFS_EVENTFD = 32, }; /* Descriptor of an non-audio endpoint */ From 9fdd84d23c76825f3fc3c0782ea433c52d41971a Mon Sep 17 00:00:00 2001 From: Asaf Vertz Date: Wed, 21 Jan 2015 11:06:34 +0200 Subject: [PATCH 134/214] usb: gadget: zero: fix format string warnings Fixed the following warnings (reported by cppcheck): [drivers/usb/gadget/function/f_sourcesink.c:1217]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'. [drivers/usb/gadget/function/f_sourcesink.c:1261]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'. [drivers/usb/gadget/function/f_sourcesink.c:1305]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'. [drivers/usb/gadget/function/f_sourcesink.c:1349]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'. [drivers/usb/gadget/function/f_sourcesink.c:1393]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'. [drivers/usb/gadget/function/f_sourcesink.c:1437]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'. [drivers/usb/gadget/function/f_sourcesink.c:1476]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'. [drivers/usb/gadget/function/f_sourcesink.c:1520]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'. [drivers/usb/gadget/function/f_sourcesink.c:1564]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'. [drivers/usb/gadget/function/f_sourcesink.c:1608]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int' Reviewed-by: Amit Virdi Signed-off-by: Asaf Vertz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_sourcesink.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c index 80be25b32cd7..e07c50ced64d 100644 --- a/drivers/usb/gadget/function/f_sourcesink.c +++ b/drivers/usb/gadget/function/f_sourcesink.c @@ -1214,7 +1214,7 @@ 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); + result = sprintf(page, "%u", opts->pattern); mutex_unlock(&opts->lock); return result; @@ -1258,7 +1258,7 @@ 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); + result = sprintf(page, "%u", opts->isoc_interval); mutex_unlock(&opts->lock); return result; @@ -1302,7 +1302,7 @@ 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); + result = sprintf(page, "%u", opts->isoc_maxpacket); mutex_unlock(&opts->lock); return result; @@ -1346,7 +1346,7 @@ 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); + result = sprintf(page, "%u", opts->isoc_mult); mutex_unlock(&opts->lock); return result; @@ -1390,7 +1390,7 @@ 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); + result = sprintf(page, "%u", opts->isoc_maxburst); mutex_unlock(&opts->lock); return result; @@ -1434,7 +1434,7 @@ 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); + result = sprintf(page, "%u", opts->bulk_buflen); mutex_unlock(&opts->lock); return result; @@ -1473,7 +1473,7 @@ static ssize_t f_ss_opts_int_interval_show(struct f_ss_opts *opts, char *page) int result; mutex_lock(&opts->lock); - result = sprintf(page, "%d", opts->int_interval); + result = sprintf(page, "%u", opts->int_interval); mutex_unlock(&opts->lock); return result; @@ -1517,7 +1517,7 @@ static ssize_t f_ss_opts_int_maxpacket_show(struct f_ss_opts *opts, char *page) int result; mutex_lock(&opts->lock); - result = sprintf(page, "%d", opts->int_maxpacket); + result = sprintf(page, "%u", opts->int_maxpacket); mutex_unlock(&opts->lock); return result; @@ -1561,7 +1561,7 @@ static ssize_t f_ss_opts_int_mult_show(struct f_ss_opts *opts, char *page) int result; mutex_lock(&opts->lock); - result = sprintf(page, "%d", opts->int_mult); + result = sprintf(page, "%u", opts->int_mult); mutex_unlock(&opts->lock); return result; @@ -1605,7 +1605,7 @@ static ssize_t f_ss_opts_int_maxburst_show(struct f_ss_opts *opts, char *page) int result; mutex_lock(&opts->lock); - result = sprintf(page, "%d", opts->int_maxburst); + result = sprintf(page, "%u", opts->int_maxburst); mutex_unlock(&opts->lock); return result; From 7eb42c6e808a0a18b8493268d06e05c1654fbbb2 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Jan 2015 00:55:40 +0200 Subject: [PATCH 135/214] usb: isp1760: Use the gpio descriptor API Switching to the managed gpio descriptor API simplifies both error and cleanup code paths (by removing the need to free the gpio) and runtime code (by removing manual handling of the active low flag). It also permits handling the reset gpio entirely from within the HCD code, sharing it between the different glue layers. Signed-off-by: Laurent Pinchart Signed-off-by: Felipe Balbi --- drivers/usb/host/isp1760-hcd.c | 24 ++++++++++----------- drivers/usb/host/isp1760-hcd.h | 2 -- drivers/usb/host/isp1760-if.c | 39 ++++++---------------------------- 3 files changed, 19 insertions(+), 46 deletions(-) diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index 395649f357aa..e4a94242328e 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -11,6 +11,7 @@ * (c) 2011 Arvid Brodin * */ +#include #include #include #include @@ -24,7 +25,6 @@ #include #include #include -#include #include "isp1760-hcd.h" @@ -57,7 +57,7 @@ struct isp1760_hcd { unsigned long next_statechange; unsigned int devflags; - int rst_gpio; + struct gpio_desc *rst_gpio; }; static inline struct isp1760_hcd *hcd_to_priv(struct usb_hcd *hcd) @@ -444,15 +444,10 @@ static int isp1760_hc_setup(struct usb_hcd *hcd) u32 scratch, hwmode; /* low-level chip reset */ - if (gpio_is_valid(priv->rst_gpio)) { - unsigned int rst_lvl; - - rst_lvl = (priv->devflags & - ISP1760_FLAG_RESET_ACTIVE_HIGH) ? 1 : 0; - - gpio_set_value(priv->rst_gpio, rst_lvl); + if (priv->rst_gpio) { + gpiod_set_value_cansleep(priv->rst_gpio, 1); mdelay(50); - gpio_set_value(priv->rst_gpio, !rst_lvl); + gpiod_set_value_cansleep(priv->rst_gpio, 0); } /* Setup HW Mode Control: This assumes a level active-low interrupt */ @@ -2215,7 +2210,6 @@ void deinit_kmem_cache(void) struct usb_hcd *isp1760_register(phys_addr_t res_start, resource_size_t res_len, int irq, unsigned long irqflags, - int rst_gpio, struct device *dev, const char *busname, unsigned int devflags) { @@ -2235,7 +2229,13 @@ struct usb_hcd *isp1760_register(phys_addr_t res_start, resource_size_t res_len, priv = hcd_to_priv(hcd); priv->devflags = devflags; - priv->rst_gpio = rst_gpio; + + priv->rst_gpio = devm_gpiod_get_optional(dev, NULL, GPIOD_OUT_HIGH); + if (IS_ERR(priv->rst_gpio)) { + ret = PTR_ERR(priv->rst_gpio); + goto err_put; + } + init_memory(priv); hcd->regs = ioremap(res_start, res_len); if (!hcd->regs) { diff --git a/drivers/usb/host/isp1760-hcd.h b/drivers/usb/host/isp1760-hcd.h index 33dc79ccaa6b..fda0f2d54e3d 100644 --- a/drivers/usb/host/isp1760-hcd.h +++ b/drivers/usb/host/isp1760-hcd.h @@ -4,7 +4,6 @@ /* exports for if */ struct usb_hcd *isp1760_register(phys_addr_t res_start, resource_size_t res_len, int irq, unsigned long irqflags, - int rst_gpio, struct device *dev, const char *busname, unsigned int devflags); int init_kmem_once(void); @@ -127,7 +126,6 @@ typedef void (packet_enqueue)(struct usb_hcd *hcd, struct isp1760_qh *qh, #define ISP1760_FLAG_ISP1761 0x00000040 /* Chip is ISP1761 */ #define ISP1760_FLAG_INTR_POL_HIGH 0x00000080 /* Interrupt polarity active high */ #define ISP1760_FLAG_INTR_EDGE_TRIG 0x00000100 /* Interrupt edge triggered */ -#define ISP1760_FLAG_RESET_ACTIVE_HIGH 0x80000000 /* RESET GPIO active high */ /* chip memory management */ struct memory_chunk { diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c index 09254a43bc01..7e8e7f60ee57 100644 --- a/drivers/usb/host/isp1760-if.c +++ b/drivers/usb/host/isp1760-if.c @@ -24,7 +24,6 @@ #include #include #include -#include #endif #ifdef CONFIG_PCI @@ -34,7 +33,6 @@ #if defined(CONFIG_OF) && defined(CONFIG_OF_IRQ) struct isp1760 { struct usb_hcd *hcd; - int rst_gpio; }; static int of_isp1760_probe(struct platform_device *dev) @@ -47,7 +45,6 @@ static int of_isp1760_probe(struct platform_device *dev) resource_size_t res_len; int ret; unsigned int devflags = 0; - enum of_gpio_flags gpio_flags; u32 bus_width = 0; drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL); @@ -94,36 +91,17 @@ static int of_isp1760_probe(struct platform_device *dev) if (of_get_property(dp, "dreq-polarity", NULL) != NULL) devflags |= ISP1760_FLAG_DREQ_POL_HIGH; - drvdata->rst_gpio = of_get_gpio_flags(dp, 0, &gpio_flags); - if (gpio_is_valid(drvdata->rst_gpio)) { - ret = gpio_request(drvdata->rst_gpio, dev_name(&dev->dev)); - if (!ret) { - if (!(gpio_flags & OF_GPIO_ACTIVE_LOW)) { - devflags |= ISP1760_FLAG_RESET_ACTIVE_HIGH; - gpio_direction_output(drvdata->rst_gpio, 0); - } else { - gpio_direction_output(drvdata->rst_gpio, 1); - } - } else { - drvdata->rst_gpio = ret; - } - } - drvdata->hcd = isp1760_register(memory.start, res_len, virq, - IRQF_SHARED, drvdata->rst_gpio, - &dev->dev, dev_name(&dev->dev), - devflags); + IRQF_SHARED, &dev->dev, + dev_name(&dev->dev), devflags); if (IS_ERR(drvdata->hcd)) { ret = PTR_ERR(drvdata->hcd); - goto free_gpio; + goto release_reg; } platform_set_drvdata(dev, drvdata); return ret; -free_gpio: - if (gpio_is_valid(drvdata->rst_gpio)) - gpio_free(drvdata->rst_gpio); release_reg: release_mem_region(memory.start, res_len); free_data: @@ -140,9 +118,6 @@ static int of_isp1760_remove(struct platform_device *dev) release_mem_region(drvdata->hcd->rsrc_start, drvdata->hcd->rsrc_len); usb_put_hcd(drvdata->hcd); - if (gpio_is_valid(drvdata->rst_gpio)) - gpio_free(drvdata->rst_gpio); - kfree(drvdata); return 0; } @@ -279,8 +254,8 @@ static int isp1761_pci_probe(struct pci_dev *dev, dev->dev.dma_mask = NULL; hcd = isp1760_register(pci_mem_phy0, memlength, dev->irq, - IRQF_SHARED, -ENOENT, &dev->dev, dev_name(&dev->dev), - devflags); + IRQF_SHARED, &dev->dev, dev_name(&dev->dev), + devflags); if (IS_ERR(hcd)) { ret_status = -ENODEV; goto cleanup3; @@ -392,8 +367,8 @@ static int isp1760_plat_probe(struct platform_device *pdev) } hcd = isp1760_register(mem_res->start, mem_size, irq_res->start, - irqflags, -ENOENT, - &pdev->dev, dev_name(&pdev->dev), devflags); + irqflags, &pdev->dev, dev_name(&pdev->dev), + devflags); platform_set_drvdata(pdev, hcd); From 8a8b96f487e8b7ba0a9384df8e7ecc514152114c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Jan 2015 00:55:41 +0200 Subject: [PATCH 136/214] usb: isp1760: Remove isp1760 glue structure The structure is allocated for the sole purpose of holding a pointer to the HCD and being stored in platform device driver data. Store the HCD pointer directly instead. Signed-off-by: Laurent Pinchart Signed-off-by: Felipe Balbi --- drivers/usb/host/isp1760-if.c | 46 ++++++++++++----------------------- 1 file changed, 15 insertions(+), 31 deletions(-) diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c index 7e8e7f60ee57..b5bcb99ffde5 100644 --- a/drivers/usb/host/isp1760-if.c +++ b/drivers/usb/host/isp1760-if.c @@ -31,13 +31,9 @@ #endif #if defined(CONFIG_OF) && defined(CONFIG_OF_IRQ) -struct isp1760 { - struct usb_hcd *hcd; -}; - static int of_isp1760_probe(struct platform_device *dev) { - struct isp1760 *drvdata; + struct usb_hcd *hcd; struct device_node *dp = dev->dev.of_node; struct resource *res; struct resource memory; @@ -47,23 +43,15 @@ static int of_isp1760_probe(struct platform_device *dev) unsigned int devflags = 0; u32 bus_width = 0; - drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL); - if (!drvdata) - return -ENOMEM; - ret = of_address_to_resource(dp, 0, &memory); - if (ret) { - ret = -ENXIO; - goto free_data; - } + if (ret) + return -ENXIO; res_len = resource_size(&memory); res = request_mem_region(memory.start, res_len, dev_name(&dev->dev)); - if (!res) { - ret = -EBUSY; - goto free_data; - } + if (!res) + return -EBUSY; virq = irq_of_parse_and_map(dp, 0); if (!virq) { @@ -91,34 +79,30 @@ static int of_isp1760_probe(struct platform_device *dev) if (of_get_property(dp, "dreq-polarity", NULL) != NULL) devflags |= ISP1760_FLAG_DREQ_POL_HIGH; - drvdata->hcd = isp1760_register(memory.start, res_len, virq, - IRQF_SHARED, &dev->dev, - dev_name(&dev->dev), devflags); - if (IS_ERR(drvdata->hcd)) { - ret = PTR_ERR(drvdata->hcd); + hcd = isp1760_register(memory.start, res_len, virq, IRQF_SHARED, + &dev->dev, dev_name(&dev->dev), devflags); + if (IS_ERR(hcd)) { + ret = PTR_ERR(hcd); goto release_reg; } - platform_set_drvdata(dev, drvdata); + platform_set_drvdata(dev, hcd); return ret; release_reg: release_mem_region(memory.start, res_len); -free_data: - kfree(drvdata); return ret; } static int of_isp1760_remove(struct platform_device *dev) { - struct isp1760 *drvdata = platform_get_drvdata(dev); + struct usb_hcd *hcd = platform_get_drvdata(dev); - usb_remove_hcd(drvdata->hcd); - iounmap(drvdata->hcd->regs); - release_mem_region(drvdata->hcd->rsrc_start, drvdata->hcd->rsrc_len); - usb_put_hcd(drvdata->hcd); + usb_remove_hcd(hcd); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); - kfree(drvdata); return 0; } From fcead8431d245af312afcbcbfbfc34a0fec77cd9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Jan 2015 00:55:42 +0200 Subject: [PATCH 137/214] usb: isp1760: Retrieve pdev memory resource from hcd at remove time The platform driver remove function needs to release the memory resource requested at probe time. Instead of retrieving the resource from the platform device, retrieve it from the usb_hcd. This mimics the behaviour of the PCI and OF glues, and will make it easier to share code between all three glue layers. Signed-off-by: Laurent Pinchart Reviewed-by: Felipe Balbi Signed-off-by: Felipe Balbi --- drivers/usb/host/isp1760-if.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c index b5bcb99ffde5..025edf2fb05a 100644 --- a/drivers/usb/host/isp1760-if.c +++ b/drivers/usb/host/isp1760-if.c @@ -373,15 +373,11 @@ out: static int isp1760_plat_remove(struct platform_device *pdev) { - struct resource *mem_res; - resource_size_t mem_size; struct usb_hcd *hcd = platform_get_drvdata(pdev); usb_remove_hcd(hcd); - mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - mem_size = resource_size(mem_res); - release_mem_region(mem_res->start, mem_size); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); From 5324713c998d6615d858606188fe69a23d1de9d8 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Jan 2015 00:55:43 +0200 Subject: [PATCH 138/214] usb: isp1760: Unmap I/O registers at platform device removal The I/O registers are mapped in the HCD code but must be unmapped by glue code. The OF and PCI glue code unmap them correctly but the platform glue code is missing an iounmap() call. Fix it. Signed-off-by: Laurent Pinchart Signed-off-by: Felipe Balbi --- drivers/usb/host/isp1760-if.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c index 025edf2fb05a..730caa18cbd2 100644 --- a/drivers/usb/host/isp1760-if.c +++ b/drivers/usb/host/isp1760-if.c @@ -376,9 +376,8 @@ static int isp1760_plat_remove(struct platform_device *pdev) struct usb_hcd *hcd = platform_get_drvdata(pdev); usb_remove_hcd(hcd); - + iounmap(hcd->regs); release_mem_region(hcd->rsrc_start, hcd->rsrc_len); - usb_put_hcd(hcd); return 0; From c3cc40ccea2cd3533a90d915b40f304843642df9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Jan 2015 00:55:44 +0200 Subject: [PATCH 139/214] usb: isp1760: Merge platform and OF glue codes Both handle platform devices, merge them. Signed-off-by: Laurent Pinchart Signed-off-by: Felipe Balbi --- drivers/usb/host/isp1760-if.c | 190 ++++++++++------------------------ 1 file changed, 55 insertions(+), 135 deletions(-) diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c index 730caa18cbd2..3db98daa16d5 100644 --- a/drivers/usb/host/isp1760-if.c +++ b/drivers/usb/host/isp1760-if.c @@ -12,121 +12,18 @@ #include #include #include +#include #include +#include #include #include #include "isp1760-hcd.h" -#if defined(CONFIG_OF) && defined(CONFIG_OF_IRQ) -#include -#include -#include -#include -#include -#endif - #ifdef CONFIG_PCI #include #endif -#if defined(CONFIG_OF) && defined(CONFIG_OF_IRQ) -static int of_isp1760_probe(struct platform_device *dev) -{ - struct usb_hcd *hcd; - struct device_node *dp = dev->dev.of_node; - struct resource *res; - struct resource memory; - int virq; - resource_size_t res_len; - int ret; - unsigned int devflags = 0; - u32 bus_width = 0; - - ret = of_address_to_resource(dp, 0, &memory); - if (ret) - return -ENXIO; - - res_len = resource_size(&memory); - - res = request_mem_region(memory.start, res_len, dev_name(&dev->dev)); - if (!res) - return -EBUSY; - - virq = irq_of_parse_and_map(dp, 0); - if (!virq) { - ret = -ENODEV; - goto release_reg; - } - - if (of_device_is_compatible(dp, "nxp,usb-isp1761")) - devflags |= ISP1760_FLAG_ISP1761; - - /* Some systems wire up only 16 of the 32 data lines */ - of_property_read_u32(dp, "bus-width", &bus_width); - if (bus_width == 16) - devflags |= ISP1760_FLAG_BUS_WIDTH_16; - - if (of_get_property(dp, "port1-otg", NULL) != NULL) - devflags |= ISP1760_FLAG_OTG_EN; - - if (of_get_property(dp, "analog-oc", NULL) != NULL) - devflags |= ISP1760_FLAG_ANALOG_OC; - - if (of_get_property(dp, "dack-polarity", NULL) != NULL) - devflags |= ISP1760_FLAG_DACK_POL_HIGH; - - if (of_get_property(dp, "dreq-polarity", NULL) != NULL) - devflags |= ISP1760_FLAG_DREQ_POL_HIGH; - - hcd = isp1760_register(memory.start, res_len, virq, IRQF_SHARED, - &dev->dev, dev_name(&dev->dev), devflags); - if (IS_ERR(hcd)) { - ret = PTR_ERR(hcd); - goto release_reg; - } - - platform_set_drvdata(dev, hcd); - return ret; - -release_reg: - release_mem_region(memory.start, res_len); - return ret; -} - -static int of_isp1760_remove(struct platform_device *dev) -{ - struct usb_hcd *hcd = platform_get_drvdata(dev); - - usb_remove_hcd(hcd); - iounmap(hcd->regs); - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); - usb_put_hcd(hcd); - - return 0; -} - -static const struct of_device_id of_isp1760_match[] = { - { - .compatible = "nxp,usb-isp1760", - }, - { - .compatible = "nxp,usb-isp1761", - }, - { }, -}; -MODULE_DEVICE_TABLE(of, of_isp1760_match); - -static struct platform_driver isp1760_of_driver = { - .driver = { - .name = "nxp-isp1760", - .of_match_table = of_isp1760_match, - }, - .probe = of_isp1760_probe, - .remove = of_isp1760_remove, -}; -#endif - #ifdef CONFIG_PCI static int isp1761_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) @@ -304,26 +201,23 @@ static struct pci_driver isp1761_pci_driver = { static int isp1760_plat_probe(struct platform_device *pdev) { - int ret = 0; - struct usb_hcd *hcd; + unsigned long irqflags = IRQF_SHARED; + unsigned int devflags = 0; struct resource *mem_res; struct resource *irq_res; resource_size_t mem_size; - struct isp1760_platform_data *priv = dev_get_platdata(&pdev->dev); - unsigned int devflags = 0; - unsigned long irqflags = IRQF_SHARED; + struct usb_hcd *hcd; + int ret; mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem_res) { pr_warning("isp1760: Memory resource not available\n"); - ret = -ENODEV; - goto out; + return -ENODEV; } mem_size = resource_size(mem_res); if (!request_mem_region(mem_res->start, mem_size, "isp1760")) { pr_warning("isp1760: Cannot reserve the memory resource\n"); - ret = -EBUSY; - goto out; + return -EBUSY; } irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); @@ -335,39 +229,63 @@ static int isp1760_plat_probe(struct platform_device *pdev) irqflags |= irq_res->flags & IRQF_TRIGGER_MASK; - if (priv) { - if (priv->is_isp1761) + if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) { + struct device_node *dp = pdev->dev.of_node; + u32 bus_width = 0; + + if (of_device_is_compatible(dp, "nxp,usb-isp1761")) devflags |= ISP1760_FLAG_ISP1761; - if (priv->bus_width_16) + + /* Some systems wire up only 16 of the 32 data lines */ + of_property_read_u32(dp, "bus-width", &bus_width); + if (bus_width == 16) devflags |= ISP1760_FLAG_BUS_WIDTH_16; - if (priv->port1_otg) + + if (of_property_read_bool(dp, "port1-otg")) devflags |= ISP1760_FLAG_OTG_EN; - if (priv->analog_oc) + + if (of_property_read_bool(dp, "analog-oc")) devflags |= ISP1760_FLAG_ANALOG_OC; - if (priv->dack_polarity_high) + + if (of_property_read_bool(dp, "dack-polarity")) devflags |= ISP1760_FLAG_DACK_POL_HIGH; - if (priv->dreq_polarity_high) + + if (of_property_read_bool(dp, "dreq-polarity")) + devflags |= ISP1760_FLAG_DREQ_POL_HIGH; + } else if (dev_get_platdata(&pdev->dev)) { + struct isp1760_platform_data *pdata = + dev_get_platdata(&pdev->dev); + + if (pdata->is_isp1761) + devflags |= ISP1760_FLAG_ISP1761; + if (pdata->bus_width_16) + devflags |= ISP1760_FLAG_BUS_WIDTH_16; + if (pdata->port1_otg) + devflags |= ISP1760_FLAG_OTG_EN; + if (pdata->analog_oc) + devflags |= ISP1760_FLAG_ANALOG_OC; + if (pdata->dack_polarity_high) + devflags |= ISP1760_FLAG_DACK_POL_HIGH; + if (pdata->dreq_polarity_high) devflags |= ISP1760_FLAG_DREQ_POL_HIGH; } hcd = isp1760_register(mem_res->start, mem_size, irq_res->start, irqflags, &pdev->dev, dev_name(&pdev->dev), devflags); - - platform_set_drvdata(pdev, hcd); - if (IS_ERR(hcd)) { pr_warning("isp1760: Failed to register the HCD device\n"); - ret = -ENODEV; + ret = PTR_ERR(hcd); goto cleanup; } + platform_set_drvdata(pdev, hcd); + pr_info("ISP1760 USB device initialised\n"); - return ret; + return 0; cleanup: release_mem_region(mem_res->start, mem_size); -out: return ret; } @@ -383,11 +301,21 @@ static int isp1760_plat_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id isp1760_of_match[] = { + { .compatible = "nxp,usb-isp1760", }, + { .compatible = "nxp,usb-isp1761", }, + { }, +}; +MODULE_DEVICE_TABLE(of, isp1760_of_match); +#endif + static struct platform_driver isp1760_plat_driver = { .probe = isp1760_plat_probe, .remove = isp1760_plat_remove, .driver = { .name = "isp1760", + .of_match_table = of_match_ptr(isp1760_of_match), }, }; @@ -400,11 +328,6 @@ static int __init isp1760_init(void) ret = platform_driver_register(&isp1760_plat_driver); if (!ret) any_ret = 0; -#if defined(CONFIG_OF) && defined(CONFIG_OF_IRQ) - ret = platform_driver_register(&isp1760_of_driver); - if (!ret) - any_ret = 0; -#endif #ifdef CONFIG_PCI ret = pci_register_driver(&isp1761_pci_driver); if (!ret) @@ -420,9 +343,6 @@ module_init(isp1760_init); static void __exit isp1760_exit(void) { platform_driver_unregister(&isp1760_plat_driver); -#if defined(CONFIG_OF) && defined(CONFIG_OF_IRQ) - platform_driver_unregister(&isp1760_of_driver); -#endif #ifdef CONFIG_PCI pci_unregister_driver(&isp1761_pci_driver); #endif From 10c73f09dc9be7c7373483f9965dd535c5788f09 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Jan 2015 00:55:45 +0200 Subject: [PATCH 140/214] usb: isp1760: Move removal cleanup code to isp1760-hcd.c The removal cleanup code is duplicated between the different bus glues. Move it to a central location. Signed-off-by: Laurent Pinchart Signed-off-by: Felipe Balbi --- drivers/usb/host/isp1760-hcd.c | 10 ++++++++++ drivers/usb/host/isp1760-hcd.h | 2 ++ drivers/usb/host/isp1760-if.c | 16 ++-------------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index e4a94242328e..74987276ac73 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -2263,6 +2263,16 @@ err_put: return ERR_PTR(ret); } +void isp1760_unregister(struct device *dev) +{ + struct usb_hcd *hcd = dev_get_drvdata(dev); + + usb_remove_hcd(hcd); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); +} + MODULE_DESCRIPTION("Driver for the ISP1760 USB-controller from NXP"); MODULE_AUTHOR("Sebastian Siewior "); MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/host/isp1760-hcd.h b/drivers/usb/host/isp1760-hcd.h index fda0f2d54e3d..ea13a58d44f8 100644 --- a/drivers/usb/host/isp1760-hcd.h +++ b/drivers/usb/host/isp1760-hcd.h @@ -6,6 +6,8 @@ struct usb_hcd *isp1760_register(phys_addr_t res_start, resource_size_t res_len, int irq, unsigned long irqflags, struct device *dev, const char *busname, unsigned int devflags); +void isp1760_unregister(struct device *dev); + int init_kmem_once(void); void deinit_kmem_cache(void); diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c index 3db98daa16d5..b96c62f40863 100644 --- a/drivers/usb/host/isp1760-if.c +++ b/drivers/usb/host/isp1760-if.c @@ -160,14 +160,7 @@ cleanup1: static void isp1761_pci_remove(struct pci_dev *dev) { - struct usb_hcd *hcd; - - hcd = pci_get_drvdata(dev); - - usb_remove_hcd(hcd); - iounmap(hcd->regs); - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); - usb_put_hcd(hcd); + isp1760_unregister(&dev->dev); pci_disable_device(dev); } @@ -291,12 +284,7 @@ cleanup: static int isp1760_plat_remove(struct platform_device *pdev) { - struct usb_hcd *hcd = platform_get_drvdata(pdev); - - usb_remove_hcd(hcd); - iounmap(hcd->regs); - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); - usb_put_hcd(hcd); + isp1760_unregister(&pdev->dev); return 0; } From 57f068bedca8b92157d09f971f2fb3adb76f8f96 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Jan 2015 00:55:46 +0200 Subject: [PATCH 141/214] usb: isp1760: Manage device driver data in common code Don't duplicate *_set_drvdata calls in glue code. Signed-off-by: Laurent Pinchart Reviewed-by: Felipe Balbi Signed-off-by: Felipe Balbi --- drivers/usb/host/isp1760-hcd.c | 2 ++ drivers/usb/host/isp1760-if.c | 3 --- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index 74987276ac73..19fbd6965630 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -2252,6 +2252,8 @@ struct usb_hcd *isp1760_register(phys_addr_t res_start, resource_size_t res_len, goto err_unmap; device_wakeup_enable(hcd->self.controller); + dev_set_drvdata(dev, hcd); + return hcd; err_unmap: diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c index b96c62f40863..64eaf5d0157e 100644 --- a/drivers/usb/host/isp1760-if.c +++ b/drivers/usb/host/isp1760-if.c @@ -146,7 +146,6 @@ static int isp1761_pci_probe(struct pci_dev *dev, iounmap(iobase); release_mem_region(nxp_pci_io_base, iolength); - pci_set_drvdata(dev, hcd); return 0; cleanup3: @@ -272,8 +271,6 @@ static int isp1760_plat_probe(struct platform_device *pdev) goto cleanup; } - platform_set_drvdata(pdev, hcd); - pr_info("ISP1760 USB device initialised\n"); return 0; From f0bdbb0ec6be7465eecbdbea058c027485a0d966 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Jan 2015 00:55:47 +0200 Subject: [PATCH 142/214] usb: isp1760: Don't expose hcd to glue code from isp1760_register The glue code probe functions don't need to access the hcd structure anymore. Modify isp1760_register to return an integer error code instead of the hcd pointer. Signed-off-by: Laurent Pinchart Reviewed-by: Felipe Balbi Signed-off-by: Felipe Balbi --- drivers/usb/host/isp1760-hcd.c | 18 ++++++++++-------- drivers/usb/host/isp1760-hcd.h | 10 +++------- drivers/usb/host/isp1760-if.c | 19 ++++++------------- 3 files changed, 19 insertions(+), 28 deletions(-) diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index 19fbd6965630..4d6e50b13d81 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -60,6 +60,9 @@ struct isp1760_hcd { struct gpio_desc *rst_gpio; }; +typedef void (packet_enqueue)(struct usb_hcd *hcd, struct isp1760_qh *qh, + struct isp1760_qtd *qtd); + static inline struct isp1760_hcd *hcd_to_priv(struct usb_hcd *hcd) { return (struct isp1760_hcd *) (hcd->hcd_priv); @@ -2208,24 +2211,23 @@ void deinit_kmem_cache(void) kmem_cache_destroy(urb_listitem_cachep); } -struct usb_hcd *isp1760_register(phys_addr_t res_start, resource_size_t res_len, - int irq, unsigned long irqflags, - struct device *dev, const char *busname, - unsigned int devflags) +int isp1760_register(phys_addr_t res_start, resource_size_t res_len, int irq, + unsigned long irqflags, struct device *dev, + const char *busname, unsigned int devflags) { struct usb_hcd *hcd; struct isp1760_hcd *priv; int ret; if (usb_disabled()) - return ERR_PTR(-ENODEV); + return -ENODEV; /* prevent usb-core allocating DMA pages */ dev->dma_mask = NULL; hcd = usb_create_hcd(&isp1760_hc_driver, dev, dev_name(dev)); if (!hcd) - return ERR_PTR(-ENOMEM); + return -ENOMEM; priv = hcd_to_priv(hcd); priv->devflags = devflags; @@ -2254,7 +2256,7 @@ struct usb_hcd *isp1760_register(phys_addr_t res_start, resource_size_t res_len, dev_set_drvdata(dev, hcd); - return hcd; + return 0; err_unmap: iounmap(hcd->regs); @@ -2262,7 +2264,7 @@ err_unmap: err_put: usb_put_hcd(hcd); - return ERR_PTR(ret); + return ret; } void isp1760_unregister(struct device *dev) diff --git a/drivers/usb/host/isp1760-hcd.h b/drivers/usb/host/isp1760-hcd.h index ea13a58d44f8..372d2e5f1210 100644 --- a/drivers/usb/host/isp1760-hcd.h +++ b/drivers/usb/host/isp1760-hcd.h @@ -2,10 +2,9 @@ #define _ISP1760_HCD_H_ /* exports for if */ -struct usb_hcd *isp1760_register(phys_addr_t res_start, resource_size_t res_len, - int irq, unsigned long irqflags, - struct device *dev, const char *busname, - unsigned int devflags); +int isp1760_register(phys_addr_t res_start, resource_size_t res_len, int irq, + unsigned long irqflags, struct device *dev, + const char *busname, unsigned int devflags); void isp1760_unregister(struct device *dev); int init_kmem_once(void); @@ -112,9 +111,6 @@ struct slotinfo { }; -typedef void (packet_enqueue)(struct usb_hcd *hcd, struct isp1760_qh *qh, - struct isp1760_qtd *qtd); - /* * Device flags that can vary from board to board. All of these * indicate the most "atypical" case, so that a devflags of 0 is diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c index 64eaf5d0157e..03243b0dbe09 100644 --- a/drivers/usb/host/isp1760-if.c +++ b/drivers/usb/host/isp1760-if.c @@ -31,7 +31,6 @@ static int isp1761_pci_probe(struct pci_dev *dev, u8 latency, limit; __u32 reg_data; int retry_count; - struct usb_hcd *hcd; unsigned int devflags = 0; int ret_status = 0; @@ -134,13 +133,11 @@ static int isp1761_pci_probe(struct pci_dev *dev, writel(reg_data, iobase + PLX_INT_CSR_REG); dev->dev.dma_mask = NULL; - hcd = isp1760_register(pci_mem_phy0, memlength, dev->irq, - IRQF_SHARED, &dev->dev, dev_name(&dev->dev), - devflags); - if (IS_ERR(hcd)) { - ret_status = -ENODEV; + ret_status = isp1760_register(pci_mem_phy0, memlength, dev->irq, + IRQF_SHARED, &dev->dev, + dev_name(&dev->dev), devflags); + if (ret_status < 0) goto cleanup3; - } /* done with PLX IO access */ iounmap(iobase); @@ -198,7 +195,6 @@ static int isp1760_plat_probe(struct platform_device *pdev) struct resource *mem_res; struct resource *irq_res; resource_size_t mem_size; - struct usb_hcd *hcd; int ret; mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -262,14 +258,11 @@ static int isp1760_plat_probe(struct platform_device *pdev) devflags |= ISP1760_FLAG_DREQ_POL_HIGH; } - hcd = isp1760_register(mem_res->start, mem_size, irq_res->start, + ret = isp1760_register(mem_res->start, mem_size, irq_res->start, irqflags, &pdev->dev, dev_name(&pdev->dev), devflags); - if (IS_ERR(hcd)) { - pr_warning("isp1760: Failed to register the HCD device\n"); - ret = PTR_ERR(hcd); + if (ret < 0) goto cleanup; - } pr_info("ISP1760 USB device initialised\n"); return 0; From 30573751daf60835ad1dba8b040b6a345f49120a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Jan 2015 00:55:48 +0200 Subject: [PATCH 143/214] usb: isp1760: Fix indentation in probe error path Signed-off-by: Laurent Pinchart Reviewed-by: Felipe Balbi Signed-off-by: Felipe Balbi --- drivers/usb/host/isp1760-hcd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index 4d6e50b13d81..aa894a127b2c 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -2259,12 +2259,12 @@ int isp1760_register(phys_addr_t res_start, resource_size_t res_len, int irq, return 0; err_unmap: - iounmap(hcd->regs); + iounmap(hcd->regs); err_put: - usb_put_hcd(hcd); + usb_put_hcd(hcd); - return ret; + return ret; } void isp1760_unregister(struct device *dev) From 5a6356ac62c8fa732e260123feb73655f7d919e6 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Jan 2015 00:55:49 +0200 Subject: [PATCH 144/214] usb: isp1760: Prefix init_kmem_once and deinit_kmem_cache with isp1760_ The two functions are specific to the driver but have very generic names, subject to collisions. Rename them. Signed-off-by: Laurent Pinchart Reviewed-by: Felipe Balbi Signed-off-by: Felipe Balbi --- drivers/usb/host/isp1760-hcd.c | 4 ++-- drivers/usb/host/isp1760-hcd.h | 4 ++-- drivers/usb/host/isp1760-if.c | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index aa894a127b2c..0ae1719efb2b 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -2177,7 +2177,7 @@ static const struct hc_driver isp1760_hc_driver = { .clear_tt_buffer_complete = isp1760_clear_tt_buffer_complete, }; -int __init init_kmem_once(void) +int __init isp1760_init_kmem_once(void) { urb_listitem_cachep = kmem_cache_create("isp1760_urb_listitem", sizeof(struct urb_listitem), 0, SLAB_TEMPORARY | @@ -2204,7 +2204,7 @@ int __init init_kmem_once(void) return 0; } -void deinit_kmem_cache(void) +void isp1760_deinit_kmem_cache(void) { kmem_cache_destroy(qtd_cachep); kmem_cache_destroy(qh_cachep); diff --git a/drivers/usb/host/isp1760-hcd.h b/drivers/usb/host/isp1760-hcd.h index 372d2e5f1210..f97c9d625595 100644 --- a/drivers/usb/host/isp1760-hcd.h +++ b/drivers/usb/host/isp1760-hcd.h @@ -7,8 +7,8 @@ int isp1760_register(phys_addr_t res_start, resource_size_t res_len, int irq, const char *busname, unsigned int devflags); void isp1760_unregister(struct device *dev); -int init_kmem_once(void); -void deinit_kmem_cache(void); +int isp1760_init_kmem_once(void); +void isp1760_deinit_kmem_cache(void); /* EHCI capability registers */ #define HC_CAPLENGTH 0x00 diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c index 03243b0dbe09..e268b20a1cb0 100644 --- a/drivers/usb/host/isp1760-if.c +++ b/drivers/usb/host/isp1760-if.c @@ -301,7 +301,7 @@ static int __init isp1760_init(void) { int ret, any_ret = -ENODEV; - init_kmem_once(); + isp1760_init_kmem_once(); ret = platform_driver_register(&isp1760_plat_driver); if (!ret) @@ -313,7 +313,7 @@ static int __init isp1760_init(void) #endif if (any_ret) - deinit_kmem_cache(); + isp1760_deinit_kmem_cache(); return any_ret; } module_init(isp1760_init); @@ -324,6 +324,6 @@ static void __exit isp1760_exit(void) #ifdef CONFIG_PCI pci_unregister_driver(&isp1761_pci_driver); #endif - deinit_kmem_cache(); + isp1760_deinit_kmem_cache(); } module_exit(isp1760_exit); From d69292a8f5eed074412b70bb59b745fc17213661 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Jan 2015 00:55:50 +0200 Subject: [PATCH 145/214] usb: isp1760: Remove busname argument to isp1760_register The argument is unused, remove it. Signed-off-by: Laurent Pinchart Reviewed-by: Felipe Balbi Signed-off-by: Felipe Balbi --- drivers/usb/host/isp1760-hcd.c | 2 +- drivers/usb/host/isp1760-hcd.h | 2 +- drivers/usb/host/isp1760-if.c | 6 ++---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index 0ae1719efb2b..9ba3023130eb 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -2213,7 +2213,7 @@ void isp1760_deinit_kmem_cache(void) int isp1760_register(phys_addr_t res_start, resource_size_t res_len, int irq, unsigned long irqflags, struct device *dev, - const char *busname, unsigned int devflags) + unsigned int devflags) { struct usb_hcd *hcd; struct isp1760_hcd *priv; diff --git a/drivers/usb/host/isp1760-hcd.h b/drivers/usb/host/isp1760-hcd.h index f97c9d625595..cc656028eb3d 100644 --- a/drivers/usb/host/isp1760-hcd.h +++ b/drivers/usb/host/isp1760-hcd.h @@ -4,7 +4,7 @@ /* exports for if */ int isp1760_register(phys_addr_t res_start, resource_size_t res_len, int irq, unsigned long irqflags, struct device *dev, - const char *busname, unsigned int devflags); + unsigned int devflags); void isp1760_unregister(struct device *dev); int isp1760_init_kmem_once(void); diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c index e268b20a1cb0..ce572cc5ea50 100644 --- a/drivers/usb/host/isp1760-if.c +++ b/drivers/usb/host/isp1760-if.c @@ -134,8 +134,7 @@ static int isp1761_pci_probe(struct pci_dev *dev, dev->dev.dma_mask = NULL; ret_status = isp1760_register(pci_mem_phy0, memlength, dev->irq, - IRQF_SHARED, &dev->dev, - dev_name(&dev->dev), devflags); + IRQF_SHARED, &dev->dev, devflags); if (ret_status < 0) goto cleanup3; @@ -259,8 +258,7 @@ static int isp1760_plat_probe(struct platform_device *pdev) } ret = isp1760_register(mem_res->start, mem_size, irq_res->start, - irqflags, &pdev->dev, dev_name(&pdev->dev), - devflags); + irqflags, &pdev->dev, devflags); if (ret < 0) goto cleanup; From 4942e00e5582dcb45d432c35d47980ced72cdb8e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Jan 2015 00:55:51 +0200 Subject: [PATCH 146/214] usb: isp1760: Pass resource pointer to isp1760_register The function takes quite a few arguments, passing the resource pointer instead of the start address and length simplifies it a bit. Signed-off-by: Laurent Pinchart Reviewed-by: Felipe Balbi Signed-off-by: Felipe Balbi --- drivers/usb/host/isp1760-hcd.c | 11 +++++------ drivers/usb/host/isp1760-hcd.h | 5 ++--- drivers/usb/host/isp1760-if.c | 8 ++++---- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index 9ba3023130eb..e99dafa6cacd 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -2211,9 +2211,8 @@ void isp1760_deinit_kmem_cache(void) kmem_cache_destroy(urb_listitem_cachep); } -int isp1760_register(phys_addr_t res_start, resource_size_t res_len, int irq, - unsigned long irqflags, struct device *dev, - unsigned int devflags) +int isp1760_register(struct resource *mem, int irq, unsigned long irqflags, + struct device *dev, unsigned int devflags) { struct usb_hcd *hcd; struct isp1760_hcd *priv; @@ -2239,15 +2238,15 @@ int isp1760_register(phys_addr_t res_start, resource_size_t res_len, int irq, } init_memory(priv); - hcd->regs = ioremap(res_start, res_len); + hcd->regs = ioremap(mem->start, resource_size(mem)); if (!hcd->regs) { ret = -EIO; goto err_put; } hcd->irq = irq; - hcd->rsrc_start = res_start; - hcd->rsrc_len = res_len; + hcd->rsrc_start = mem->start; + hcd->rsrc_len = resource_size(mem); ret = usb_add_hcd(hcd, irq, irqflags); if (ret) diff --git a/drivers/usb/host/isp1760-hcd.h b/drivers/usb/host/isp1760-hcd.h index cc656028eb3d..7fc510f9bf6e 100644 --- a/drivers/usb/host/isp1760-hcd.h +++ b/drivers/usb/host/isp1760-hcd.h @@ -2,9 +2,8 @@ #define _ISP1760_HCD_H_ /* exports for if */ -int isp1760_register(phys_addr_t res_start, resource_size_t res_len, int irq, - unsigned long irqflags, struct device *dev, - unsigned int devflags); +int isp1760_register(struct resource *mem, int irq, unsigned long irqflags, + struct device *dev, unsigned int devflags); void isp1760_unregister(struct device *dev); int isp1760_init_kmem_once(void); diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c index ce572cc5ea50..3a9b4f6d29cb 100644 --- a/drivers/usb/host/isp1760-if.c +++ b/drivers/usb/host/isp1760-if.c @@ -133,8 +133,8 @@ static int isp1761_pci_probe(struct pci_dev *dev, writel(reg_data, iobase + PLX_INT_CSR_REG); dev->dev.dma_mask = NULL; - ret_status = isp1760_register(pci_mem_phy0, memlength, dev->irq, - IRQF_SHARED, &dev->dev, devflags); + ret_status = isp1760_register(&dev->resource[3], dev->irq, IRQF_SHARED, + &dev->dev, devflags); if (ret_status < 0) goto cleanup3; @@ -257,8 +257,8 @@ static int isp1760_plat_probe(struct platform_device *pdev) devflags |= ISP1760_FLAG_DREQ_POL_HIGH; } - ret = isp1760_register(mem_res->start, mem_size, irq_res->start, - irqflags, &pdev->dev, devflags); + ret = isp1760_register(mem_res, irq_res->start, irqflags, &pdev->dev, + devflags); if (ret < 0) goto cleanup; From 10feee144664a87a07f75756f1ee577ce17616c4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Jan 2015 00:55:52 +0200 Subject: [PATCH 147/214] usb: isp1760: Use the managed devm_ioremap_resource() API This simplifies error and remove code paths. Signed-off-by: Laurent Pinchart Signed-off-by: Felipe Balbi --- drivers/usb/host/isp1760-hcd.c | 20 +++++++----------- drivers/usb/host/isp1760-if.c | 37 +++++++++------------------------- 2 files changed, 16 insertions(+), 41 deletions(-) diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index e99dafa6cacd..2e38efe8d4d4 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -2234,14 +2234,14 @@ int isp1760_register(struct resource *mem, int irq, unsigned long irqflags, priv->rst_gpio = devm_gpiod_get_optional(dev, NULL, GPIOD_OUT_HIGH); if (IS_ERR(priv->rst_gpio)) { ret = PTR_ERR(priv->rst_gpio); - goto err_put; + goto error; } init_memory(priv); - hcd->regs = ioremap(mem->start, resource_size(mem)); - if (!hcd->regs) { - ret = -EIO; - goto err_put; + hcd->regs = devm_ioremap_resource(dev, mem); + if (IS_ERR(hcd->regs)) { + ret = PTR_ERR(hcd->regs); + goto error; } hcd->irq = irq; @@ -2250,19 +2250,15 @@ int isp1760_register(struct resource *mem, int irq, unsigned long irqflags, ret = usb_add_hcd(hcd, irq, irqflags); if (ret) - goto err_unmap; + goto error; device_wakeup_enable(hcd->self.controller); dev_set_drvdata(dev, hcd); return 0; -err_unmap: - iounmap(hcd->regs); - -err_put: +error: usb_put_hcd(hcd); - return ret; } @@ -2271,8 +2267,6 @@ void isp1760_unregister(struct device *dev) struct usb_hcd *hcd = dev_get_drvdata(dev); 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/isp1760-if.c b/drivers/usb/host/isp1760-if.c index 3a9b4f6d29cb..dbb287ca9d82 100644 --- a/drivers/usb/host/isp1760-if.c +++ b/drivers/usb/host/isp1760-if.c @@ -85,8 +85,9 @@ static int isp1761_pci_probe(struct pci_dev *dev, chip_addr = ioremap_nocache(pci_mem_phy0,memlength); if (!chip_addr) { printk(KERN_ERR "Error ioremap failed\n"); + release_mem_region(pci_mem_phy0, memlength); ret_status = -ENOMEM; - goto cleanup3; + goto cleanup2; } /* bad pci latencies can contribute to overruns */ @@ -114,6 +115,7 @@ static int isp1761_pci_probe(struct pci_dev *dev, } iounmap(chip_addr); + release_mem_region(pci_mem_phy0, memlength); /* Host Controller presence is detected by writing to scratch register * and reading back and checking the contents are same or not @@ -121,7 +123,7 @@ static int isp1761_pci_probe(struct pci_dev *dev, if (reg_data != 0xFACE) { dev_err(&dev->dev, "scratch register mismatch %x\n", reg_data); ret_status = -ENOMEM; - goto cleanup3; + goto cleanup2; } pci_set_master(dev); @@ -132,20 +134,14 @@ static int isp1761_pci_probe(struct pci_dev *dev, reg_data |= 0x900; writel(reg_data, iobase + PLX_INT_CSR_REG); - dev->dev.dma_mask = NULL; - ret_status = isp1760_register(&dev->resource[3], dev->irq, IRQF_SHARED, - &dev->dev, devflags); - if (ret_status < 0) - goto cleanup3; - /* done with PLX IO access */ iounmap(iobase); release_mem_region(nxp_pci_io_base, iolength); - return 0; + dev->dev.dma_mask = NULL; + return isp1760_register(&dev->resource[3], dev->irq, IRQF_SHARED, + &dev->dev, devflags); -cleanup3: - release_mem_region(pci_mem_phy0, memlength); cleanup2: iounmap(iobase); cleanup1: @@ -193,25 +189,14 @@ static int isp1760_plat_probe(struct platform_device *pdev) unsigned int devflags = 0; struct resource *mem_res; struct resource *irq_res; - resource_size_t mem_size; int ret; mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem_res) { - pr_warning("isp1760: Memory resource not available\n"); - return -ENODEV; - } - mem_size = resource_size(mem_res); - if (!request_mem_region(mem_res->start, mem_size, "isp1760")) { - pr_warning("isp1760: Cannot reserve the memory resource\n"); - return -EBUSY; - } irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!irq_res) { pr_warning("isp1760: IRQ resource not available\n"); - ret = -ENODEV; - goto cleanup; + return -ENODEV; } irqflags |= irq_res->flags & IRQF_TRIGGER_MASK; @@ -260,14 +245,10 @@ static int isp1760_plat_probe(struct platform_device *pdev) ret = isp1760_register(mem_res, irq_res->start, irqflags, &pdev->dev, devflags); if (ret < 0) - goto cleanup; + return ret; pr_info("ISP1760 USB device initialised\n"); return 0; - -cleanup: - release_mem_region(mem_res->start, mem_size); - return ret; } static int isp1760_plat_remove(struct platform_device *pdev) From c770e00122a939f4ec9eeb663b6be6a131065926 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Jan 2015 00:55:53 +0200 Subject: [PATCH 148/214] usb: isp1760: Refactor PCI initialization code Move the initialization code out of the PCI probe function to a new function in order to simplify and fix error handling. Signed-off-by: Laurent Pinchart Signed-off-by: Felipe Balbi --- drivers/usb/host/isp1760-if.c | 142 +++++++++++++++++----------------- 1 file changed, 73 insertions(+), 69 deletions(-) diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c index dbb287ca9d82..7c03320f7831 100644 --- a/drivers/usb/host/isp1760-if.c +++ b/drivers/usb/host/isp1760-if.c @@ -25,69 +25,34 @@ #endif #ifdef CONFIG_PCI -static int isp1761_pci_probe(struct pci_dev *dev, - const struct pci_device_id *id) +static int isp1761_pci_init(struct pci_dev *dev) { - u8 latency, limit; - __u32 reg_data; - int retry_count; - unsigned int devflags = 0; - int ret_status = 0; - - resource_size_t pci_mem_phy0; - resource_size_t memlength; - - u8 __iomem *chip_addr; + resource_size_t mem_start; + resource_size_t mem_length; u8 __iomem *iobase; - resource_size_t nxp_pci_io_base; - resource_size_t iolength; + u8 latency, limit; + int retry_count; + u32 reg_data; - if (usb_disabled()) - return -ENODEV; + /* Grab the PLX PCI shared memory of the ISP 1761 we need */ + mem_start = pci_resource_start(dev, 3); + mem_length = pci_resource_len(dev, 3); + if (mem_length < 0xffff) { + printk(KERN_ERR "memory length for this resource is wrong\n"); + return -ENOMEM; + } - if (pci_enable_device(dev) < 0) - return -ENODEV; - - if (!dev->irq) - return -ENODEV; - - /* Grab the PLX PCI mem maped port start address we need */ - nxp_pci_io_base = pci_resource_start(dev, 0); - iolength = pci_resource_len(dev, 0); - - if (!request_mem_region(nxp_pci_io_base, iolength, "ISP1761 IO MEM")) { - printk(KERN_ERR "request region #1\n"); + if (!request_mem_region(mem_start, mem_length, "ISP-PCI")) { + printk(KERN_ERR "host controller already in use\n"); return -EBUSY; } - iobase = ioremap_nocache(nxp_pci_io_base, iolength); - if (!iobase) { - printk(KERN_ERR "ioremap #1\n"); - ret_status = -ENOMEM; - goto cleanup1; - } - /* Grab the PLX PCI shared memory of the ISP 1761 we need */ - pci_mem_phy0 = pci_resource_start(dev, 3); - memlength = pci_resource_len(dev, 3); - if (memlength < 0xffff) { - printk(KERN_ERR "memory length for this resource is wrong\n"); - ret_status = -ENOMEM; - goto cleanup2; - } - - if (!request_mem_region(pci_mem_phy0, memlength, "ISP-PCI")) { - printk(KERN_ERR "host controller already in use\n"); - ret_status = -EBUSY; - goto cleanup2; - } - /* map available memory */ - chip_addr = ioremap_nocache(pci_mem_phy0,memlength); - if (!chip_addr) { + iobase = ioremap_nocache(mem_start, mem_length); + if (!iobase) { printk(KERN_ERR "Error ioremap failed\n"); - release_mem_region(pci_mem_phy0, memlength); - ret_status = -ENOMEM; - goto cleanup2; + release_mem_region(mem_start, mem_length); + return -ENOMEM; } /* bad pci latencies can contribute to overruns */ @@ -108,25 +73,38 @@ static int isp1761_pci_probe(struct pci_dev *dev, /*by default host is in 16bit mode, so * io operations at this stage must be 16 bit * */ - writel(0xface, chip_addr + HC_SCRATCH_REG); + writel(0xface, iobase + HC_SCRATCH_REG); udelay(100); - reg_data = readl(chip_addr + HC_SCRATCH_REG) & 0x0000ffff; + reg_data = readl(iobase + HC_SCRATCH_REG) & 0x0000ffff; retry_count--; } - iounmap(chip_addr); - release_mem_region(pci_mem_phy0, memlength); + iounmap(iobase); + release_mem_region(mem_start, mem_length); /* Host Controller presence is detected by writing to scratch register * and reading back and checking the contents are same or not */ if (reg_data != 0xFACE) { dev_err(&dev->dev, "scratch register mismatch %x\n", reg_data); - ret_status = -ENOMEM; - goto cleanup2; + return -ENOMEM; } - pci_set_master(dev); + /* Grab the PLX PCI mem maped port start address we need */ + mem_start = pci_resource_start(dev, 0); + mem_length = pci_resource_len(dev, 0); + + if (!request_mem_region(mem_start, mem_length, "ISP1761 IO MEM")) { + printk(KERN_ERR "request region #1\n"); + return -EBUSY; + } + + iobase = ioremap_nocache(mem_start, mem_length); + if (!iobase) { + printk(KERN_ERR "ioremap #1\n"); + release_mem_region(mem_start, mem_length); + return -ENOMEM; + } /* configure PLX PCI chip to pass interrupts */ #define PLX_INT_CSR_REG 0x68 @@ -136,17 +114,43 @@ static int isp1761_pci_probe(struct pci_dev *dev, /* done with PLX IO access */ iounmap(iobase); - release_mem_region(nxp_pci_io_base, iolength); + release_mem_region(mem_start, mem_length); + + return 0; +} + +static int isp1761_pci_probe(struct pci_dev *dev, + const struct pci_device_id *id) +{ + unsigned int devflags = 0; + int ret; + + if (usb_disabled()) + return -ENODEV; + + if (!dev->irq) + return -ENODEV; + + if (pci_enable_device(dev) < 0) + return -ENODEV; + + ret = isp1761_pci_init(dev); + if (ret < 0) + goto error; + + pci_set_master(dev); dev->dev.dma_mask = NULL; - return isp1760_register(&dev->resource[3], dev->irq, IRQF_SHARED, - &dev->dev, devflags); + ret = isp1760_register(&dev->resource[3], dev->irq, IRQF_SHARED, + &dev->dev, devflags); + if (ret < 0) + goto error; -cleanup2: - iounmap(iobase); -cleanup1: - release_mem_region(nxp_pci_io_base, iolength); - return ret_status; + return 0; + +error: + pci_disable_device(dev); + return ret; } static void isp1761_pci_remove(struct pci_dev *dev) From cdd36e872c55603af60a9dc1f9e4b3de31ffc21a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Jan 2015 00:55:54 +0200 Subject: [PATCH 149/214] usb: isp1760: Decouple usb_hdc and isp1760_priv Allocate the driver private data structure manually instead of using the usb_hcd private space. This will allow skipping hcd registration for the isp1761 when used in device mode only. Signed-off-by: Laurent Pinchart Reviewed-by: Felipe Balbi Signed-off-by: Felipe Balbi --- drivers/usb/host/isp1760-hcd.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index 2e38efe8d4d4..55c0add700b1 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -40,6 +40,8 @@ enum queue_head_types { }; struct isp1760_hcd { + struct usb_hcd *hcd; + u32 hcs_params; spinlock_t lock; struct slotinfo atl_slots[32]; @@ -65,7 +67,7 @@ typedef void (packet_enqueue)(struct usb_hcd *hcd, struct isp1760_qh *qh, static inline struct isp1760_hcd *hcd_to_priv(struct usb_hcd *hcd) { - return (struct isp1760_hcd *) (hcd->hcd_priv); + return *(struct isp1760_hcd **)hcd->hcd_priv; } /* Section 2.2 Host Controller Capability Registers */ @@ -2161,7 +2163,7 @@ static void isp1760_clear_tt_buffer_complete(struct usb_hcd *hcd, static const struct hc_driver isp1760_hc_driver = { .description = "isp1760-hcd", .product_desc = "NXP ISP1760 USB Host Controller", - .hcd_priv_size = sizeof(struct isp1760_hcd), + .hcd_priv_size = sizeof(struct isp1760_hcd *), .irq = isp1760_irq, .flags = HCD_MEMORY | HCD_USB2, .reset = isp1760_hc_setup, @@ -2214,13 +2216,17 @@ void isp1760_deinit_kmem_cache(void) int isp1760_register(struct resource *mem, int irq, unsigned long irqflags, struct device *dev, unsigned int devflags) { - struct usb_hcd *hcd; + struct usb_hcd *hcd = NULL; struct isp1760_hcd *priv; int ret; if (usb_disabled()) return -ENODEV; + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + /* prevent usb-core allocating DMA pages */ dev->dma_mask = NULL; @@ -2228,7 +2234,9 @@ int isp1760_register(struct resource *mem, int irq, unsigned long irqflags, if (!hcd) return -ENOMEM; - priv = hcd_to_priv(hcd); + priv->hcd = hcd; + *(struct isp1760_hcd **)hcd->hcd_priv = priv; + priv->devflags = devflags; priv->rst_gpio = devm_gpiod_get_optional(dev, NULL, GPIOD_OUT_HIGH); @@ -2253,7 +2261,7 @@ int isp1760_register(struct resource *mem, int irq, unsigned long irqflags, goto error; device_wakeup_enable(hcd->self.controller); - dev_set_drvdata(dev, hcd); + dev_set_drvdata(dev, priv); return 0; @@ -2264,7 +2272,8 @@ error: void isp1760_unregister(struct device *dev) { - struct usb_hcd *hcd = dev_get_drvdata(dev); + struct isp1760_hcd *priv = dev_get_drvdata(dev); + struct usb_hcd *hcd = priv->hcd; usb_remove_hcd(hcd); usb_put_hcd(hcd); From ea0b1fabc77420261c57c4ac4f0ea384f5a8c438 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Jan 2015 00:55:55 +0200 Subject: [PATCH 150/214] usb: isp1760: Prefix driver data structures with isp1760_ The slotinfo and memory_chunk structures are specific to the driver and defined in a header file. Prefix them with isp1760_ to avoid namespace clashes. Signed-off-by: Laurent Pinchart Reviewed-by: Felipe Balbi Signed-off-by: Felipe Balbi --- drivers/usb/host/isp1760-hcd.c | 15 ++++++++------- drivers/usb/host/isp1760-hcd.h | 4 ++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index 55c0add700b1..99f56c6686e3 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -44,11 +44,11 @@ struct isp1760_hcd { u32 hcs_params; spinlock_t lock; - struct slotinfo atl_slots[32]; + struct isp1760_slotinfo atl_slots[32]; int atl_done_map; - struct slotinfo int_slots[32]; + struct isp1760_slotinfo int_slots[32]; int int_done_map; - struct memory_chunk memory_pool[BLOCKS]; + struct isp1760_memory_chunk memory_pool[BLOCKS]; struct list_head qh_list[QH_END]; /* periodic schedule support */ @@ -743,8 +743,9 @@ static void qtd_free(struct isp1760_qtd *qtd) } static void start_bus_transfer(struct usb_hcd *hcd, u32 ptd_offset, int slot, - struct slotinfo *slots, struct isp1760_qtd *qtd, - struct isp1760_qh *qh, struct ptd *ptd) + struct isp1760_slotinfo *slots, + struct isp1760_qtd *qtd, struct isp1760_qh *qh, + struct ptd *ptd) { struct isp1760_hcd *priv = hcd_to_priv(hcd); int skip_map; @@ -857,7 +858,7 @@ static void enqueue_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh) { struct isp1760_hcd *priv = hcd_to_priv(hcd); int ptd_offset; - struct slotinfo *slots; + struct isp1760_slotinfo *slots; int curr_slot, free_slot; int n; struct ptd ptd; @@ -1097,7 +1098,7 @@ static void handle_done_ptds(struct usb_hcd *hcd) struct isp1760_qh *qh; int slot; int state; - struct slotinfo *slots; + struct isp1760_slotinfo *slots; u32 ptd_offset; struct isp1760_qtd *qtd; int modified; diff --git a/drivers/usb/host/isp1760-hcd.h b/drivers/usb/host/isp1760-hcd.h index 7fc510f9bf6e..3056bcd8a393 100644 --- a/drivers/usb/host/isp1760-hcd.h +++ b/drivers/usb/host/isp1760-hcd.h @@ -103,7 +103,7 @@ struct ptd { #define ATL_PTD_OFFSET 0x0c00 #define PAYLOAD_OFFSET 0x1000 -struct slotinfo { +struct isp1760_slotinfo { struct isp1760_qh *qh; struct isp1760_qtd *qtd; unsigned long timestamp; @@ -125,7 +125,7 @@ struct slotinfo { #define ISP1760_FLAG_INTR_EDGE_TRIG 0x00000100 /* Interrupt edge triggered */ /* chip memory management */ -struct memory_chunk { +struct isp1760_memory_chunk { unsigned int start; unsigned int size; unsigned int free; From e19c99e7592e06b6fdf558aa8877b671f8cf0329 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Jan 2015 00:55:56 +0200 Subject: [PATCH 151/214] usb: isp1760: Reorganize header files The isp1760-rhcd.h header contains PTD constants and structures only useful for the HCD implementation. It also contains register definitions that will be needed by common code when implementing support for the ISP1761 device controller, but doesn't contain the isp1760_hcd structure definition that will also be used by common code. Move definitions to the right location and create an isp1760-regs.h to store register definitions. No change other than moving definitions and modifying indentation is performed. Signed-off-by: Laurent Pinchart Reviewed-by: Felipe Balbi Signed-off-by: Felipe Balbi --- drivers/usb/host/isp1760-hcd.c | 126 ++++++++++------- drivers/usb/host/isp1760-hcd.h | 242 +++++++++----------------------- drivers/usb/host/isp1760-if.c | 1 + drivers/usb/host/isp1760-regs.h | 120 ++++++++++++++++ 4 files changed, 260 insertions(+), 229 deletions(-) create mode 100644 drivers/usb/host/isp1760-regs.h diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index 99f56c6686e3..50434cc5335c 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -27,41 +27,12 @@ #include #include "isp1760-hcd.h" +#include "isp1760-regs.h" static struct kmem_cache *qtd_cachep; static struct kmem_cache *qh_cachep; static struct kmem_cache *urb_listitem_cachep; -enum queue_head_types { - QH_CONTROL, - QH_BULK, - QH_INTERRUPT, - QH_END -}; - -struct isp1760_hcd { - struct usb_hcd *hcd; - - u32 hcs_params; - spinlock_t lock; - struct isp1760_slotinfo atl_slots[32]; - int atl_done_map; - struct isp1760_slotinfo int_slots[32]; - int int_done_map; - struct isp1760_memory_chunk memory_pool[BLOCKS]; - struct list_head qh_list[QH_END]; - - /* periodic schedule support */ -#define DEFAULT_I_TDPS 1024 - unsigned periodic_size; - unsigned i_thresh; - unsigned long reset_done; - unsigned long next_statechange; - unsigned int devflags; - - struct gpio_desc *rst_gpio; -}; - typedef void (packet_enqueue)(struct usb_hcd *hcd, struct isp1760_qh *qh, struct isp1760_qtd *qtd); @@ -70,32 +41,79 @@ static inline struct isp1760_hcd *hcd_to_priv(struct usb_hcd *hcd) return *(struct isp1760_hcd **)hcd->hcd_priv; } -/* Section 2.2 Host Controller Capability Registers */ -#define HC_LENGTH(p) (((p)>>00)&0x00ff) /* bits 7:0 */ -#define HC_VERSION(p) (((p)>>16)&0xffff) /* bits 31:16 */ -#define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */ -#define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */ -#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */ -#define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */ -#define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */ +/* urb state*/ +#define DELETE_URB (0x0008) +#define NO_TRANSFER_ACTIVE (0xffffffff) -/* Section 2.3 Host Controller Operational Registers */ -#define CMD_LRESET (1<<7) /* partial reset (no ports, etc) */ -#define CMD_RESET (1<<1) /* reset HC not bus */ -#define CMD_RUN (1<<0) /* start/stop HC */ -#define STS_PCD (1<<2) /* port change detect */ -#define FLAG_CF (1<<0) /* true: we'll support "high speed" */ +/* Philips Proprietary Transfer Descriptor (PTD) */ +typedef __u32 __bitwise __dw; +struct ptd { + __dw dw0; + __dw dw1; + __dw dw2; + __dw dw3; + __dw dw4; + __dw dw5; + __dw dw6; + __dw dw7; +}; +#define PTD_OFFSET 0x0400 +#define ISO_PTD_OFFSET 0x0400 +#define INT_PTD_OFFSET 0x0800 +#define ATL_PTD_OFFSET 0x0c00 +#define PAYLOAD_OFFSET 0x1000 -#define PORT_OWNER (1<<13) /* true: companion hc owns this port */ -#define PORT_POWER (1<<12) /* true: has power (see PPC) */ -#define PORT_USB11(x) (((x) & (3 << 10)) == (1 << 10)) /* USB 1.1 device */ -#define PORT_RESET (1<<8) /* reset port */ -#define PORT_SUSPEND (1<<7) /* suspend port */ -#define PORT_RESUME (1<<6) /* resume it */ -#define PORT_PE (1<<2) /* port enable */ -#define PORT_CSC (1<<1) /* connect status change */ -#define PORT_CONNECT (1<<0) /* device connected */ -#define PORT_RWC_BITS (PORT_CSC) + +/* ATL */ +/* DW0 */ +#define DW0_VALID_BIT 1 +#define FROM_DW0_VALID(x) ((x) & 0x01) +#define TO_DW0_LENGTH(x) (((u32) x) << 3) +#define TO_DW0_MAXPACKET(x) (((u32) x) << 18) +#define TO_DW0_MULTI(x) (((u32) x) << 29) +#define TO_DW0_ENDPOINT(x) (((u32) x) << 31) +/* DW1 */ +#define TO_DW1_DEVICE_ADDR(x) (((u32) x) << 3) +#define TO_DW1_PID_TOKEN(x) (((u32) x) << 10) +#define DW1_TRANS_BULK ((u32) 2 << 12) +#define DW1_TRANS_INT ((u32) 3 << 12) +#define DW1_TRANS_SPLIT ((u32) 1 << 14) +#define DW1_SE_USB_LOSPEED ((u32) 2 << 16) +#define TO_DW1_PORT_NUM(x) (((u32) x) << 18) +#define TO_DW1_HUB_NUM(x) (((u32) x) << 25) +/* DW2 */ +#define TO_DW2_DATA_START_ADDR(x) (((u32) x) << 8) +#define TO_DW2_RL(x) ((x) << 25) +#define FROM_DW2_RL(x) (((x) >> 25) & 0xf) +/* DW3 */ +#define FROM_DW3_NRBYTESTRANSFERRED(x) ((x) & 0x7fff) +#define FROM_DW3_SCS_NRBYTESTRANSFERRED(x) ((x) & 0x07ff) +#define TO_DW3_NAKCOUNT(x) ((x) << 19) +#define FROM_DW3_NAKCOUNT(x) (((x) >> 19) & 0xf) +#define TO_DW3_CERR(x) ((x) << 23) +#define FROM_DW3_CERR(x) (((x) >> 23) & 0x3) +#define TO_DW3_DATA_TOGGLE(x) ((x) << 25) +#define FROM_DW3_DATA_TOGGLE(x) (((x) >> 25) & 0x1) +#define TO_DW3_PING(x) ((x) << 26) +#define FROM_DW3_PING(x) (((x) >> 26) & 0x1) +#define DW3_ERROR_BIT (1 << 28) +#define DW3_BABBLE_BIT (1 << 29) +#define DW3_HALT_BIT (1 << 30) +#define DW3_ACTIVE_BIT (1 << 31) +#define FROM_DW3_ACTIVE(x) (((x) >> 31) & 0x01) + +#define INT_UNDERRUN (1 << 2) +#define INT_BABBLE (1 << 1) +#define INT_EXACT (1 << 0) + +#define SETUP_PID (2) +#define IN_PID (1) +#define OUT_PID (0) + +/* Errata 1 */ +#define RL_COUNTER (0) +#define NAK_COUNTER (0) +#define ERR_COUNTER (2) struct isp1760_qtd { u8 packet_type; diff --git a/drivers/usb/host/isp1760-hcd.h b/drivers/usb/host/isp1760-hcd.h index 3056bcd8a393..44486c86f5f7 100644 --- a/drivers/usb/host/isp1760-hcd.h +++ b/drivers/usb/host/isp1760-hcd.h @@ -1,135 +1,13 @@ #ifndef _ISP1760_HCD_H_ #define _ISP1760_HCD_H_ -/* exports for if */ -int isp1760_register(struct resource *mem, int irq, unsigned long irqflags, - struct device *dev, unsigned int devflags); -void isp1760_unregister(struct device *dev); +#include -int isp1760_init_kmem_once(void); -void isp1760_deinit_kmem_cache(void); - -/* EHCI capability registers */ -#define HC_CAPLENGTH 0x00 -#define HC_HCSPARAMS 0x04 -#define HC_HCCPARAMS 0x08 - -/* EHCI operational registers */ -#define HC_USBCMD 0x20 -#define HC_USBSTS 0x24 -#define HC_FRINDEX 0x2c -#define HC_CONFIGFLAG 0x60 -#define HC_PORTSC1 0x64 -#define HC_ISO_PTD_DONEMAP_REG 0x130 -#define HC_ISO_PTD_SKIPMAP_REG 0x134 -#define HC_ISO_PTD_LASTPTD_REG 0x138 -#define HC_INT_PTD_DONEMAP_REG 0x140 -#define HC_INT_PTD_SKIPMAP_REG 0x144 -#define HC_INT_PTD_LASTPTD_REG 0x148 -#define HC_ATL_PTD_DONEMAP_REG 0x150 -#define HC_ATL_PTD_SKIPMAP_REG 0x154 -#define HC_ATL_PTD_LASTPTD_REG 0x158 - -/* Configuration Register */ -#define HC_HW_MODE_CTRL 0x300 -#define ALL_ATX_RESET (1 << 31) -#define HW_ANA_DIGI_OC (1 << 15) -#define HW_DATA_BUS_32BIT (1 << 8) -#define HW_DACK_POL_HIGH (1 << 6) -#define HW_DREQ_POL_HIGH (1 << 5) -#define HW_INTR_HIGH_ACT (1 << 2) -#define HW_INTR_EDGE_TRIG (1 << 1) -#define HW_GLOBAL_INTR_EN (1 << 0) - -#define HC_CHIP_ID_REG 0x304 -#define HC_SCRATCH_REG 0x308 - -#define HC_RESET_REG 0x30c -#define SW_RESET_RESET_HC (1 << 1) -#define SW_RESET_RESET_ALL (1 << 0) - -#define HC_BUFFER_STATUS_REG 0x334 -#define ISO_BUF_FILL (1 << 2) -#define INT_BUF_FILL (1 << 1) -#define ATL_BUF_FILL (1 << 0) - -#define HC_MEMORY_REG 0x33c -#define ISP_BANK(x) ((x) << 16) - -#define HC_PORT1_CTRL 0x374 -#define PORT1_POWER (3 << 3) -#define PORT1_INIT1 (1 << 7) -#define PORT1_INIT2 (1 << 23) -#define HW_OTG_CTRL_SET 0x374 -#define HW_OTG_CTRL_CLR 0x376 - -/* Interrupt Register */ -#define HC_INTERRUPT_REG 0x310 - -#define HC_INTERRUPT_ENABLE 0x314 -#define HC_ISO_INT (1 << 9) -#define HC_ATL_INT (1 << 8) -#define HC_INTL_INT (1 << 7) -#define HC_EOT_INT (1 << 3) -#define HC_SOT_INT (1 << 1) -#define INTERRUPT_ENABLE_MASK (HC_INTL_INT | HC_ATL_INT) - -#define HC_ISO_IRQ_MASK_OR_REG 0x318 -#define HC_INT_IRQ_MASK_OR_REG 0x31C -#define HC_ATL_IRQ_MASK_OR_REG 0x320 -#define HC_ISO_IRQ_MASK_AND_REG 0x324 -#define HC_INT_IRQ_MASK_AND_REG 0x328 -#define HC_ATL_IRQ_MASK_AND_REG 0x32C - -/* urb state*/ -#define DELETE_URB (0x0008) -#define NO_TRANSFER_ACTIVE (0xffffffff) - -/* Philips Proprietary Transfer Descriptor (PTD) */ -typedef __u32 __bitwise __dw; -struct ptd { - __dw dw0; - __dw dw1; - __dw dw2; - __dw dw3; - __dw dw4; - __dw dw5; - __dw dw6; - __dw dw7; -}; -#define PTD_OFFSET 0x0400 -#define ISO_PTD_OFFSET 0x0400 -#define INT_PTD_OFFSET 0x0800 -#define ATL_PTD_OFFSET 0x0c00 -#define PAYLOAD_OFFSET 0x1000 - -struct isp1760_slotinfo { - struct isp1760_qh *qh; - struct isp1760_qtd *qtd; - unsigned long timestamp; -}; - - -/* - * Device flags that can vary from board to board. All of these - * indicate the most "atypical" case, so that a devflags of 0 is - * a sane default configuration. - */ -#define ISP1760_FLAG_BUS_WIDTH_16 0x00000002 /* 16-bit data bus width */ -#define ISP1760_FLAG_OTG_EN 0x00000004 /* Port 1 supports OTG */ -#define ISP1760_FLAG_ANALOG_OC 0x00000008 /* Analog overcurrent */ -#define ISP1760_FLAG_DACK_POL_HIGH 0x00000010 /* DACK active high */ -#define ISP1760_FLAG_DREQ_POL_HIGH 0x00000020 /* DREQ active high */ -#define ISP1760_FLAG_ISP1761 0x00000040 /* Chip is ISP1761 */ -#define ISP1760_FLAG_INTR_POL_HIGH 0x00000080 /* Interrupt polarity active high */ -#define ISP1760_FLAG_INTR_EDGE_TRIG 0x00000100 /* Interrupt edge triggered */ - -/* chip memory management */ -struct isp1760_memory_chunk { - unsigned int start; - unsigned int size; - unsigned int free; -}; +struct gpio_desc; +struct isp1760_qh; +struct isp1760_qtd; +struct resource; +struct usb_hcd; /* * 60kb divided in: @@ -149,55 +27,69 @@ struct isp1760_memory_chunk { #define MAX_PAYLOAD_SIZE BLOCK_3_SIZE #define PAYLOAD_AREA_SIZE 0xf000 -/* ATL */ -/* DW0 */ -#define DW0_VALID_BIT 1 -#define FROM_DW0_VALID(x) ((x) & 0x01) -#define TO_DW0_LENGTH(x) (((u32) x) << 3) -#define TO_DW0_MAXPACKET(x) (((u32) x) << 18) -#define TO_DW0_MULTI(x) (((u32) x) << 29) -#define TO_DW0_ENDPOINT(x) (((u32) x) << 31) -/* DW1 */ -#define TO_DW1_DEVICE_ADDR(x) (((u32) x) << 3) -#define TO_DW1_PID_TOKEN(x) (((u32) x) << 10) -#define DW1_TRANS_BULK ((u32) 2 << 12) -#define DW1_TRANS_INT ((u32) 3 << 12) -#define DW1_TRANS_SPLIT ((u32) 1 << 14) -#define DW1_SE_USB_LOSPEED ((u32) 2 << 16) -#define TO_DW1_PORT_NUM(x) (((u32) x) << 18) -#define TO_DW1_HUB_NUM(x) (((u32) x) << 25) -/* DW2 */ -#define TO_DW2_DATA_START_ADDR(x) (((u32) x) << 8) -#define TO_DW2_RL(x) ((x) << 25) -#define FROM_DW2_RL(x) (((x) >> 25) & 0xf) -/* DW3 */ -#define FROM_DW3_NRBYTESTRANSFERRED(x) ((x) & 0x7fff) -#define FROM_DW3_SCS_NRBYTESTRANSFERRED(x) ((x) & 0x07ff) -#define TO_DW3_NAKCOUNT(x) ((x) << 19) -#define FROM_DW3_NAKCOUNT(x) (((x) >> 19) & 0xf) -#define TO_DW3_CERR(x) ((x) << 23) -#define FROM_DW3_CERR(x) (((x) >> 23) & 0x3) -#define TO_DW3_DATA_TOGGLE(x) ((x) << 25) -#define FROM_DW3_DATA_TOGGLE(x) (((x) >> 25) & 0x1) -#define TO_DW3_PING(x) ((x) << 26) -#define FROM_DW3_PING(x) (((x) >> 26) & 0x1) -#define DW3_ERROR_BIT (1 << 28) -#define DW3_BABBLE_BIT (1 << 29) -#define DW3_HALT_BIT (1 << 30) -#define DW3_ACTIVE_BIT (1 << 31) -#define FROM_DW3_ACTIVE(x) (((x) >> 31) & 0x01) +/* + * Device flags that can vary from board to board. All of these + * indicate the most "atypical" case, so that a devflags of 0 is + * a sane default configuration. + */ +#define ISP1760_FLAG_BUS_WIDTH_16 0x00000002 /* 16-bit data bus width */ +#define ISP1760_FLAG_OTG_EN 0x00000004 /* Port 1 supports OTG */ +#define ISP1760_FLAG_ANALOG_OC 0x00000008 /* Analog overcurrent */ +#define ISP1760_FLAG_DACK_POL_HIGH 0x00000010 /* DACK active high */ +#define ISP1760_FLAG_DREQ_POL_HIGH 0x00000020 /* DREQ active high */ +#define ISP1760_FLAG_ISP1761 0x00000040 /* Chip is ISP1761 */ +#define ISP1760_FLAG_INTR_POL_HIGH 0x00000080 /* Interrupt polarity active high */ +#define ISP1760_FLAG_INTR_EDGE_TRIG 0x00000100 /* Interrupt edge triggered */ -#define INT_UNDERRUN (1 << 2) -#define INT_BABBLE (1 << 1) -#define INT_EXACT (1 << 0) +struct isp1760_slotinfo { + struct isp1760_qh *qh; + struct isp1760_qtd *qtd; + unsigned long timestamp; +}; -#define SETUP_PID (2) -#define IN_PID (1) -#define OUT_PID (0) +/* chip memory management */ +struct isp1760_memory_chunk { + unsigned int start; + unsigned int size; + unsigned int free; +}; -/* Errata 1 */ -#define RL_COUNTER (0) -#define NAK_COUNTER (0) -#define ERR_COUNTER (2) +enum isp1760_queue_head_types { + QH_CONTROL, + QH_BULK, + QH_INTERRUPT, + QH_END +}; + +struct isp1760_hcd { + struct usb_hcd *hcd; + + u32 hcs_params; + spinlock_t lock; + struct isp1760_slotinfo atl_slots[32]; + int atl_done_map; + struct isp1760_slotinfo int_slots[32]; + int int_done_map; + struct isp1760_memory_chunk memory_pool[BLOCKS]; + struct list_head qh_list[QH_END]; + + /* periodic schedule support */ +#define DEFAULT_I_TDPS 1024 + unsigned periodic_size; + unsigned i_thresh; + unsigned long reset_done; + unsigned long next_statechange; + unsigned int devflags; + + struct gpio_desc *rst_gpio; +}; + +/* exports for if */ +int isp1760_register(struct resource *mem, int irq, unsigned long irqflags, + struct device *dev, unsigned int devflags); +void isp1760_unregister(struct device *dev); + +int isp1760_init_kmem_once(void); +void isp1760_deinit_kmem_cache(void); #endif /* _ISP1760_HCD_H_ */ diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c index 7c03320f7831..b1591c6cf54a 100644 --- a/drivers/usb/host/isp1760-if.c +++ b/drivers/usb/host/isp1760-if.c @@ -19,6 +19,7 @@ #include #include "isp1760-hcd.h" +#include "isp1760-regs.h" #ifdef CONFIG_PCI #include diff --git a/drivers/usb/host/isp1760-regs.h b/drivers/usb/host/isp1760-regs.h new file mode 100644 index 000000000000..c2a39009d11f --- /dev/null +++ b/drivers/usb/host/isp1760-regs.h @@ -0,0 +1,120 @@ +/* + * Driver for the NXP ISP1760 chip + * + * Copyright 2014 Laurent Pinchart + * Copyright 2007 Sebastian Siewior + * + * Contacts: + * Sebastian Siewior + * Laurent Pinchart + * + * 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 _ISP1760_REGS_H_ +#define _ISP1760_REGS_H_ + +/* EHCI capability registers */ +#define HC_CAPLENGTH 0x000 +#define HC_LENGTH(p) (((p) >> 00) & 0x00ff) /* bits 7:0 */ +#define HC_VERSION(p) (((p) >> 16) & 0xffff) /* bits 31:16 */ + +#define HC_HCSPARAMS 0x004 +#define HCS_INDICATOR(p) ((p) & (1 << 16)) /* true: has port indicators */ +#define HCS_PPC(p) ((p) & (1 << 4)) /* true: port power control */ +#define HCS_N_PORTS(p) (((p) >> 0) & 0xf) /* bits 3:0, ports on HC */ + +#define HC_HCCPARAMS 0x008 +#define HCC_ISOC_CACHE(p) ((p) & (1 << 7)) /* true: can cache isoc frame */ +#define HCC_ISOC_THRES(p) (((p) >> 4) & 0x7) /* bits 6:4, uframes cached */ + +/* EHCI operational registers */ +#define HC_USBCMD 0x020 +#define CMD_LRESET (1 << 7) /* partial reset (no ports, etc) */ +#define CMD_RESET (1 << 1) /* reset HC not bus */ +#define CMD_RUN (1 << 0) /* start/stop HC */ + +#define HC_USBSTS 0x024 +#define STS_PCD (1 << 2) /* port change detect */ + +#define HC_FRINDEX 0x02c + +#define HC_CONFIGFLAG 0x060 +#define FLAG_CF (1 << 0) /* true: we'll support "high speed" */ + +#define HC_PORTSC1 0x064 +#define PORT_OWNER (1 << 13) /* true: companion hc owns this port */ +#define PORT_POWER (1 << 12) /* true: has power (see PPC) */ +#define PORT_USB11(x) (((x) & (3 << 10)) == (1 << 10)) /* USB 1.1 device */ +#define PORT_RESET (1 << 8) /* reset port */ +#define PORT_SUSPEND (1 << 7) /* suspend port */ +#define PORT_RESUME (1 << 6) /* resume it */ +#define PORT_PE (1 << 2) /* port enable */ +#define PORT_CSC (1 << 1) /* connect status change */ +#define PORT_CONNECT (1 << 0) /* device connected */ +#define PORT_RWC_BITS (PORT_CSC) + +#define HC_ISO_PTD_DONEMAP_REG 0x130 +#define HC_ISO_PTD_SKIPMAP_REG 0x134 +#define HC_ISO_PTD_LASTPTD_REG 0x138 +#define HC_INT_PTD_DONEMAP_REG 0x140 +#define HC_INT_PTD_SKIPMAP_REG 0x144 +#define HC_INT_PTD_LASTPTD_REG 0x148 +#define HC_ATL_PTD_DONEMAP_REG 0x150 +#define HC_ATL_PTD_SKIPMAP_REG 0x154 +#define HC_ATL_PTD_LASTPTD_REG 0x158 + +/* Configuration Register */ +#define HC_HW_MODE_CTRL 0x300 +#define ALL_ATX_RESET (1 << 31) +#define HW_ANA_DIGI_OC (1 << 15) +#define HW_DATA_BUS_32BIT (1 << 8) +#define HW_DACK_POL_HIGH (1 << 6) +#define HW_DREQ_POL_HIGH (1 << 5) +#define HW_INTR_HIGH_ACT (1 << 2) +#define HW_INTR_EDGE_TRIG (1 << 1) +#define HW_GLOBAL_INTR_EN (1 << 0) + +#define HC_CHIP_ID_REG 0x304 +#define HC_SCRATCH_REG 0x308 + +#define HC_RESET_REG 0x30c +#define SW_RESET_RESET_HC (1 << 1) +#define SW_RESET_RESET_ALL (1 << 0) + +#define HC_BUFFER_STATUS_REG 0x334 +#define ISO_BUF_FILL (1 << 2) +#define INT_BUF_FILL (1 << 1) +#define ATL_BUF_FILL (1 << 0) + +#define HC_MEMORY_REG 0x33c +#define ISP_BANK(x) ((x) << 16) + +#define HC_PORT1_CTRL 0x374 +#define PORT1_POWER (3 << 3) +#define PORT1_INIT1 (1 << 7) +#define PORT1_INIT2 (1 << 23) +#define HW_OTG_CTRL_SET 0x374 +#define HW_OTG_CTRL_CLR 0x376 + +/* Interrupt Register */ +#define HC_INTERRUPT_REG 0x310 + +#define HC_INTERRUPT_ENABLE 0x314 +#define HC_ISO_INT (1 << 9) +#define HC_ATL_INT (1 << 8) +#define HC_INTL_INT (1 << 7) +#define HC_EOT_INT (1 << 3) +#define HC_SOT_INT (1 << 1) +#define INTERRUPT_ENABLE_MASK (HC_INTL_INT | HC_ATL_INT) + +#define HC_ISO_IRQ_MASK_OR_REG 0x318 +#define HC_INT_IRQ_MASK_OR_REG 0x31c +#define HC_ATL_IRQ_MASK_OR_REG 0x320 +#define HC_ISO_IRQ_MASK_AND_REG 0x324 +#define HC_INT_IRQ_MASK_AND_REG 0x328 +#define HC_ATL_IRQ_MASK_AND_REG 0x32c + +#endif From 4b1a577d41c99f2aa548e8de3effe1033d9ca40b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Jan 2015 00:55:57 +0200 Subject: [PATCH 152/214] usb: isp1760: Move core code to isp1760-core.c Move core device initialization to a central location in order to share it with the device mode implementation. Signed-off-by: Laurent Pinchart Signed-off-by: Felipe Balbi --- drivers/usb/host/Makefile | 2 +- drivers/usb/host/isp1760-core.c | 65 +++++++++++++++++++++++++++++++++ drivers/usb/host/isp1760-core.h | 33 +++++++++++++++++ drivers/usb/host/isp1760-hcd.c | 42 +++++---------------- drivers/usb/host/isp1760-hcd.h | 8 ++-- drivers/usb/host/isp1760-if.c | 2 +- 6 files changed, 114 insertions(+), 38 deletions(-) create mode 100644 drivers/usb/host/isp1760-core.c create mode 100644 drivers/usb/host/isp1760-core.h diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index d6216a493bab..4dea9b164210 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -5,7 +5,7 @@ # tell define_trace.h where to find the xhci trace header CFLAGS_xhci-trace.o := -I$(src) -isp1760-y := isp1760-hcd.o isp1760-if.o +isp1760-y := isp1760-core.o isp1760-hcd.o isp1760-if.o fhci-y := fhci-hcd.o fhci-hub.o fhci-q.o fhci-y += fhci-mem.o fhci-tds.o fhci-sched.o diff --git a/drivers/usb/host/isp1760-core.c b/drivers/usb/host/isp1760-core.c new file mode 100644 index 000000000000..d38efa0e340a --- /dev/null +++ b/drivers/usb/host/isp1760-core.c @@ -0,0 +1,65 @@ +/* + * Driver for the NXP ISP1760 chip + * + * Copyright 2014 Laurent Pinchart + * Copyright 2007 Sebastian Siewior + * + * Contacts: + * Sebastian Siewior + * Laurent Pinchart + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include "isp1760-core.h" +#include "isp1760-hcd.h" + +int isp1760_register(struct resource *mem, int irq, unsigned long irqflags, + struct device *dev, unsigned int devflags) +{ + struct isp1760_device *isp; + int ret; + + if (usb_disabled()) + return -ENODEV; + + /* prevent usb-core allocating DMA pages */ + dev->dma_mask = NULL; + + isp = devm_kzalloc(dev, sizeof(*isp), GFP_KERNEL); + if (!isp) + return -ENOMEM; + + isp->regs = devm_ioremap_resource(dev, mem); + if (IS_ERR(isp->regs)) + return PTR_ERR(isp->regs); + + ret = isp1760_hcd_register(&isp->hcd, isp->regs, mem, irq, irqflags, + dev, devflags); + if (ret < 0) + return ret; + + dev_set_drvdata(dev, isp); + + return 0; +} + +void isp1760_unregister(struct device *dev) +{ + struct isp1760_device *isp = dev_get_drvdata(dev); + + isp1760_hcd_unregister(&isp->hcd); +} + +MODULE_DESCRIPTION("Driver for the ISP1760 USB-controller from NXP"); +MODULE_AUTHOR("Sebastian Siewior "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/host/isp1760-core.h b/drivers/usb/host/isp1760-core.h new file mode 100644 index 000000000000..0caeb1135275 --- /dev/null +++ b/drivers/usb/host/isp1760-core.h @@ -0,0 +1,33 @@ +/* + * Driver for the NXP ISP1760 chip + * + * Copyright 2014 Laurent Pinchart + * Copyright 2007 Sebastian Siewior + * + * Contacts: + * Sebastian Siewior + * Laurent Pinchart + * + * 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 _ISP1760_CORE_H_ +#define _ISP1760_CORE_H_ + +#include + +#include "isp1760-hcd.h" + +struct isp1760_device { + void __iomem *regs; + + struct isp1760_hcd hcd; +}; + +int isp1760_register(struct resource *mem, int irq, unsigned long irqflags, + struct device *dev, unsigned int devflags); +void isp1760_unregister(struct device *dev); + +#endif diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index 50434cc5335c..0cf620b1f6aa 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -2232,30 +2232,20 @@ void isp1760_deinit_kmem_cache(void) kmem_cache_destroy(urb_listitem_cachep); } -int isp1760_register(struct resource *mem, int irq, unsigned long irqflags, - struct device *dev, unsigned int devflags) +int isp1760_hcd_register(struct isp1760_hcd *priv, void __iomem *regs, + struct resource *mem, int irq, unsigned long irqflags, + struct device *dev, unsigned int devflags) { - struct usb_hcd *hcd = NULL; - struct isp1760_hcd *priv; + struct usb_hcd *hcd; int ret; - if (usb_disabled()) - return -ENODEV; - - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - /* prevent usb-core allocating DMA pages */ - dev->dma_mask = NULL; - hcd = usb_create_hcd(&isp1760_hc_driver, dev, dev_name(dev)); if (!hcd) return -ENOMEM; - priv->hcd = hcd; *(struct isp1760_hcd **)hcd->hcd_priv = priv; + priv->hcd = hcd; priv->devflags = devflags; priv->rst_gpio = devm_gpiod_get_optional(dev, NULL, GPIOD_OUT_HIGH); @@ -2265,22 +2255,17 @@ int isp1760_register(struct resource *mem, int irq, unsigned long irqflags, } init_memory(priv); - hcd->regs = devm_ioremap_resource(dev, mem); - if (IS_ERR(hcd->regs)) { - ret = PTR_ERR(hcd->regs); - goto error; - } hcd->irq = irq; + hcd->regs = regs; hcd->rsrc_start = mem->start; hcd->rsrc_len = resource_size(mem); ret = usb_add_hcd(hcd, irq, irqflags); if (ret) goto error; - device_wakeup_enable(hcd->self.controller); - dev_set_drvdata(dev, priv); + device_wakeup_enable(hcd->self.controller); return 0; @@ -2289,15 +2274,8 @@ error: return ret; } -void isp1760_unregister(struct device *dev) +void isp1760_hcd_unregister(struct isp1760_hcd *priv) { - struct isp1760_hcd *priv = dev_get_drvdata(dev); - struct usb_hcd *hcd = priv->hcd; - - usb_remove_hcd(hcd); - usb_put_hcd(hcd); + usb_remove_hcd(priv->hcd); + usb_put_hcd(priv->hcd); } - -MODULE_DESCRIPTION("Driver for the ISP1760 USB-controller from NXP"); -MODULE_AUTHOR("Sebastian Siewior "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/host/isp1760-hcd.h b/drivers/usb/host/isp1760-hcd.h index 44486c86f5f7..dcd2232848cd 100644 --- a/drivers/usb/host/isp1760-hcd.h +++ b/drivers/usb/host/isp1760-hcd.h @@ -84,10 +84,10 @@ struct isp1760_hcd { struct gpio_desc *rst_gpio; }; -/* exports for if */ -int isp1760_register(struct resource *mem, int irq, unsigned long irqflags, - struct device *dev, unsigned int devflags); -void isp1760_unregister(struct device *dev); +int isp1760_hcd_register(struct isp1760_hcd *priv, void __iomem *regs, + struct resource *mem, int irq, unsigned long irqflags, + struct device *dev, unsigned int devflags); +void isp1760_hcd_unregister(struct isp1760_hcd *priv); int isp1760_init_kmem_once(void); void isp1760_deinit_kmem_cache(void); diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c index b1591c6cf54a..f2a399051244 100644 --- a/drivers/usb/host/isp1760-if.c +++ b/drivers/usb/host/isp1760-if.c @@ -18,7 +18,7 @@ #include #include -#include "isp1760-hcd.h" +#include "isp1760-core.h" #include "isp1760-regs.h" #ifdef CONFIG_PCI From 667c45c2f159d3c4e1d592df42ffbc7d4d73e07b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Jan 2015 00:55:58 +0200 Subject: [PATCH 153/214] usb: isp1760: Set IRQF_SHARED flag in core code The IRQF_SHARED flag needs to be set regardless of the bus type. Don't require glue code to set it manually. Signed-off-by: Laurent Pinchart Reviewed-by: Felipe Balbi Signed-off-by: Felipe Balbi --- drivers/usb/host/isp1760-core.c | 4 ++-- drivers/usb/host/isp1760-if.c | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/usb/host/isp1760-core.c b/drivers/usb/host/isp1760-core.c index d38efa0e340a..35278a8356b3 100644 --- a/drivers/usb/host/isp1760-core.c +++ b/drivers/usb/host/isp1760-core.c @@ -43,8 +43,8 @@ int isp1760_register(struct resource *mem, int irq, unsigned long irqflags, if (IS_ERR(isp->regs)) return PTR_ERR(isp->regs); - ret = isp1760_hcd_register(&isp->hcd, isp->regs, mem, irq, irqflags, - dev, devflags); + ret = isp1760_hcd_register(&isp->hcd, isp->regs, mem, irq, + irqflags | IRQF_SHARED, dev, devflags); if (ret < 0) return ret; diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c index f2a399051244..c2a94c966350 100644 --- a/drivers/usb/host/isp1760-if.c +++ b/drivers/usb/host/isp1760-if.c @@ -142,8 +142,8 @@ static int isp1761_pci_probe(struct pci_dev *dev, pci_set_master(dev); dev->dev.dma_mask = NULL; - ret = isp1760_register(&dev->resource[3], dev->irq, IRQF_SHARED, - &dev->dev, devflags); + ret = isp1760_register(&dev->resource[3], dev->irq, 0, &dev->dev, + devflags); if (ret < 0) goto error; @@ -190,7 +190,7 @@ static struct pci_driver isp1761_pci_driver = { static int isp1760_plat_probe(struct platform_device *pdev) { - unsigned long irqflags = IRQF_SHARED; + unsigned long irqflags; unsigned int devflags = 0; struct resource *mem_res; struct resource *irq_res; @@ -203,8 +203,7 @@ static int isp1760_plat_probe(struct platform_device *pdev) pr_warning("isp1760: IRQ resource not available\n"); return -ENODEV; } - - irqflags |= irq_res->flags & IRQF_TRIGGER_MASK; + irqflags = irq_res->flags & IRQF_TRIGGER_MASK; if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) { struct device_node *dp = pdev->dev.of_node; From 5171446a3aec607c4f94a32758f51a68bc627fe3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Jan 2015 00:55:59 +0200 Subject: [PATCH 154/214] usb: isp1760: Initialize the bus interface in core code Although the corresponding register is part of the HCD register space, processor bus initialization is not specific to the HCD. To prepare for device controller support, move bus interface initialization to core code. Signed-off-by: Laurent Pinchart Reviewed-by: Felipe Balbi Signed-off-by: Felipe Balbi --- drivers/usb/host/isp1760-core.c | 62 +++++++++++++++++++++++++++++- drivers/usb/host/isp1760-core.h | 31 +++++++++++++++ drivers/usb/host/isp1760-hcd.c | 67 +++++++-------------------------- drivers/usb/host/isp1760-hcd.h | 20 +--------- 4 files changed, 105 insertions(+), 75 deletions(-) diff --git a/drivers/usb/host/isp1760-core.c b/drivers/usb/host/isp1760-core.c index 35278a8356b3..e840a1d3676b 100644 --- a/drivers/usb/host/isp1760-core.c +++ b/drivers/usb/host/isp1760-core.c @@ -13,7 +13,8 @@ * version 2 as published by the Free Software Foundation. */ -#include +#include +#include #include #include #include @@ -22,6 +23,54 @@ #include "isp1760-core.h" #include "isp1760-hcd.h" +#include "isp1760-regs.h" + +static void isp1760_init_core(struct isp1760_device *isp) +{ + u32 hwmode; + + /* Low-level chip reset */ + if (isp->rst_gpio) { + gpiod_set_value_cansleep(isp->rst_gpio, 1); + mdelay(50); + gpiod_set_value_cansleep(isp->rst_gpio, 0); + } + + /* + * Reset the host controller, including the CPU interface + * configuration. + */ + isp1760_write32(isp->regs, HC_RESET_REG, SW_RESET_RESET_ALL); + msleep(100); + + /* Setup HW Mode Control: This assumes a level active-low interrupt */ + hwmode = HW_DATA_BUS_32BIT; + + if (isp->devflags & ISP1760_FLAG_BUS_WIDTH_16) + hwmode &= ~HW_DATA_BUS_32BIT; + if (isp->devflags & ISP1760_FLAG_ANALOG_OC) + hwmode |= HW_ANA_DIGI_OC; + if (isp->devflags & ISP1760_FLAG_DACK_POL_HIGH) + hwmode |= HW_DACK_POL_HIGH; + if (isp->devflags & ISP1760_FLAG_DREQ_POL_HIGH) + hwmode |= HW_DREQ_POL_HIGH; + if (isp->devflags & ISP1760_FLAG_INTR_POL_HIGH) + hwmode |= HW_INTR_HIGH_ACT; + if (isp->devflags & ISP1760_FLAG_INTR_EDGE_TRIG) + hwmode |= HW_INTR_EDGE_TRIG; + + /* + * We have to set this first in case we're in 16-bit mode. + * Write it twice to ensure correct upper bits if switching + * to 16-bit mode. + */ + isp1760_write32(isp->regs, HC_HW_MODE_CTRL, hwmode); + isp1760_write32(isp->regs, HC_HW_MODE_CTRL, hwmode); + + dev_info(isp->dev, "bus width: %u, oc: %s\n", + isp->devflags & ISP1760_FLAG_BUS_WIDTH_16 ? 16 : 32, + isp->devflags & ISP1760_FLAG_ANALOG_OC ? "analog" : "digital"); +} int isp1760_register(struct resource *mem, int irq, unsigned long irqflags, struct device *dev, unsigned int devflags) @@ -39,12 +88,21 @@ int isp1760_register(struct resource *mem, int irq, unsigned long irqflags, if (!isp) return -ENOMEM; + isp->dev = dev; + isp->devflags = devflags; + + isp->rst_gpio = devm_gpiod_get_optional(dev, NULL, GPIOD_OUT_HIGH); + if (IS_ERR(isp->rst_gpio)) + return PTR_ERR(isp->rst_gpio); + isp->regs = devm_ioremap_resource(dev, mem); if (IS_ERR(isp->regs)) return PTR_ERR(isp->regs); + isp1760_init_core(isp); + ret = isp1760_hcd_register(&isp->hcd, isp->regs, mem, irq, - irqflags | IRQF_SHARED, dev, devflags); + irqflags | IRQF_SHARED, dev); if (ret < 0) return ret; diff --git a/drivers/usb/host/isp1760-core.h b/drivers/usb/host/isp1760-core.h index 0caeb1135275..cd4a0f3981d9 100644 --- a/drivers/usb/host/isp1760-core.h +++ b/drivers/usb/host/isp1760-core.h @@ -20,8 +20,29 @@ #include "isp1760-hcd.h" +struct device; +struct gpio_desc; + +/* + * Device flags that can vary from board to board. All of these + * indicate the most "atypical" case, so that a devflags of 0 is + * a sane default configuration. + */ +#define ISP1760_FLAG_BUS_WIDTH_16 0x00000002 /* 16-bit data bus width */ +#define ISP1760_FLAG_OTG_EN 0x00000004 /* Port 1 supports OTG */ +#define ISP1760_FLAG_ANALOG_OC 0x00000008 /* Analog overcurrent */ +#define ISP1760_FLAG_DACK_POL_HIGH 0x00000010 /* DACK active high */ +#define ISP1760_FLAG_DREQ_POL_HIGH 0x00000020 /* DREQ active high */ +#define ISP1760_FLAG_ISP1761 0x00000040 /* Chip is ISP1761 */ +#define ISP1760_FLAG_INTR_POL_HIGH 0x00000080 /* Interrupt polarity active high */ +#define ISP1760_FLAG_INTR_EDGE_TRIG 0x00000100 /* Interrupt edge triggered */ + struct isp1760_device { + struct device *dev; + void __iomem *regs; + unsigned int devflags; + struct gpio_desc *rst_gpio; struct isp1760_hcd hcd; }; @@ -30,4 +51,14 @@ int isp1760_register(struct resource *mem, int irq, unsigned long irqflags, struct device *dev, unsigned int devflags); void isp1760_unregister(struct device *dev); +static inline u32 isp1760_read32(void __iomem *base, u32 reg) +{ + return readl(base + reg); +} + +static inline void isp1760_write32(void __iomem *base, u32 reg, u32 val) +{ + writel(val, base + reg); +} + #endif diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index 0cf620b1f6aa..5309d7324485 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -26,6 +26,7 @@ #include #include +#include "isp1760-core.h" #include "isp1760-hcd.h" #include "isp1760-regs.h" @@ -160,12 +161,12 @@ struct urb_listitem { */ static u32 reg_read32(void __iomem *base, u32 reg) { - return readl(base + reg); + return isp1760_read32(base, reg); } static void reg_write32(void __iomem *base, u32 reg, u32 val) { - writel(val, base + reg); + isp1760_write32(base, reg, val); } /* @@ -466,37 +467,6 @@ static int isp1760_hc_setup(struct usb_hcd *hcd) int result; u32 scratch, hwmode; - /* low-level chip reset */ - if (priv->rst_gpio) { - gpiod_set_value_cansleep(priv->rst_gpio, 1); - mdelay(50); - gpiod_set_value_cansleep(priv->rst_gpio, 0); - } - - /* Setup HW Mode Control: This assumes a level active-low interrupt */ - hwmode = HW_DATA_BUS_32BIT; - - if (priv->devflags & ISP1760_FLAG_BUS_WIDTH_16) - hwmode &= ~HW_DATA_BUS_32BIT; - if (priv->devflags & ISP1760_FLAG_ANALOG_OC) - hwmode |= HW_ANA_DIGI_OC; - if (priv->devflags & ISP1760_FLAG_DACK_POL_HIGH) - hwmode |= HW_DACK_POL_HIGH; - if (priv->devflags & ISP1760_FLAG_DREQ_POL_HIGH) - hwmode |= HW_DREQ_POL_HIGH; - if (priv->devflags & ISP1760_FLAG_INTR_POL_HIGH) - hwmode |= HW_INTR_HIGH_ACT; - if (priv->devflags & ISP1760_FLAG_INTR_EDGE_TRIG) - hwmode |= HW_INTR_EDGE_TRIG; - - /* - * We have to set this first in case we're in 16-bit mode. - * Write it twice to ensure correct upper bits if switching - * to 16-bit mode. - */ - reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode); - reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode); - reg_write32(hcd->regs, HC_SCRATCH_REG, 0xdeadbabe); /* Change bus pattern */ scratch = reg_read32(hcd->regs, HC_CHIP_ID_REG); @@ -506,31 +476,27 @@ static int isp1760_hc_setup(struct usb_hcd *hcd) return -ENODEV; } - /* pre reset */ + /* + * The RESET_HC bit in the SW_RESET register is supposed to reset the + * host controller without touching the CPU interface registers, but at + * least on the ISP1761 it seems to behave as the RESET_ALL bit and + * reset the whole device. We thus can't use it here, so let's reset + * the host controller through the EHCI USB Command register. The device + * has been reset in core code anyway, so this shouldn't matter. + */ reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, 0); reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE); reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE); reg_write32(hcd->regs, HC_ISO_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE); - /* reset */ - reg_write32(hcd->regs, HC_RESET_REG, SW_RESET_RESET_ALL); - mdelay(100); - - reg_write32(hcd->regs, HC_RESET_REG, SW_RESET_RESET_HC); - mdelay(100); - result = ehci_reset(hcd); if (result) return result; /* Step 11 passed */ - dev_info(hcd->self.controller, "bus width: %d, oc: %s\n", - (priv->devflags & ISP1760_FLAG_BUS_WIDTH_16) ? - 16 : 32, (priv->devflags & ISP1760_FLAG_ANALOG_OC) ? - "analog" : "digital"); - /* ATL reset */ + hwmode = reg_read32(hcd->regs, HC_HW_MODE_CTRL) & ~ALL_ATX_RESET; reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode | ALL_ATX_RESET); mdelay(10); reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode); @@ -2234,7 +2200,7 @@ void isp1760_deinit_kmem_cache(void) int isp1760_hcd_register(struct isp1760_hcd *priv, void __iomem *regs, struct resource *mem, int irq, unsigned long irqflags, - struct device *dev, unsigned int devflags) + struct device *dev) { struct usb_hcd *hcd; int ret; @@ -2246,13 +2212,6 @@ int isp1760_hcd_register(struct isp1760_hcd *priv, void __iomem *regs, *(struct isp1760_hcd **)hcd->hcd_priv = priv; priv->hcd = hcd; - priv->devflags = devflags; - - priv->rst_gpio = devm_gpiod_get_optional(dev, NULL, GPIOD_OUT_HIGH); - if (IS_ERR(priv->rst_gpio)) { - ret = PTR_ERR(priv->rst_gpio); - goto error; - } init_memory(priv); diff --git a/drivers/usb/host/isp1760-hcd.h b/drivers/usb/host/isp1760-hcd.h index dcd2232848cd..df7ea3684b77 100644 --- a/drivers/usb/host/isp1760-hcd.h +++ b/drivers/usb/host/isp1760-hcd.h @@ -3,7 +3,6 @@ #include -struct gpio_desc; struct isp1760_qh; struct isp1760_qtd; struct resource; @@ -27,20 +26,6 @@ struct usb_hcd; #define MAX_PAYLOAD_SIZE BLOCK_3_SIZE #define PAYLOAD_AREA_SIZE 0xf000 -/* - * Device flags that can vary from board to board. All of these - * indicate the most "atypical" case, so that a devflags of 0 is - * a sane default configuration. - */ -#define ISP1760_FLAG_BUS_WIDTH_16 0x00000002 /* 16-bit data bus width */ -#define ISP1760_FLAG_OTG_EN 0x00000004 /* Port 1 supports OTG */ -#define ISP1760_FLAG_ANALOG_OC 0x00000008 /* Analog overcurrent */ -#define ISP1760_FLAG_DACK_POL_HIGH 0x00000010 /* DACK active high */ -#define ISP1760_FLAG_DREQ_POL_HIGH 0x00000020 /* DREQ active high */ -#define ISP1760_FLAG_ISP1761 0x00000040 /* Chip is ISP1761 */ -#define ISP1760_FLAG_INTR_POL_HIGH 0x00000080 /* Interrupt polarity active high */ -#define ISP1760_FLAG_INTR_EDGE_TRIG 0x00000100 /* Interrupt edge triggered */ - struct isp1760_slotinfo { struct isp1760_qh *qh; struct isp1760_qtd *qtd; @@ -79,14 +64,11 @@ struct isp1760_hcd { unsigned i_thresh; unsigned long reset_done; unsigned long next_statechange; - unsigned int devflags; - - struct gpio_desc *rst_gpio; }; int isp1760_hcd_register(struct isp1760_hcd *priv, void __iomem *regs, struct resource *mem, int irq, unsigned long irqflags, - struct device *dev, unsigned int devflags); + struct device *dev); void isp1760_hcd_unregister(struct isp1760_hcd *priv); int isp1760_init_kmem_once(void); From 9a66e13290f16be59ac38e1955e15e8929076fc6 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Jan 2015 00:56:00 +0200 Subject: [PATCH 155/214] usb: isp1760: Move PORT1 configuration to core code Configuring the mode of operation of port 1 doesn't belong to the HCD code, as it's related to the soon to come UDC support. Move the configuration to core code. Signed-off-by: Laurent Pinchart Signed-off-by: Felipe Balbi --- drivers/usb/host/isp1760-core.c | 8 ++++++++ drivers/usb/host/isp1760-hcd.c | 9 --------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/usb/host/isp1760-core.c b/drivers/usb/host/isp1760-core.c index e840a1d3676b..1cba3e08898a 100644 --- a/drivers/usb/host/isp1760-core.c +++ b/drivers/usb/host/isp1760-core.c @@ -67,6 +67,14 @@ static void isp1760_init_core(struct isp1760_device *isp) isp1760_write32(isp->regs, HC_HW_MODE_CTRL, hwmode); isp1760_write32(isp->regs, HC_HW_MODE_CTRL, hwmode); + /* + * PORT 1 Control register of the ISP1760 is the OTG control register on + * ISP1761. Since there is no OTG or device controller support in this + * driver, we use port 1 as a "normal" USB host port on both chips. + */ + isp1760_write32(isp->regs, HC_PORT1_CTRL, PORT1_POWER | PORT1_INIT2); + usleep_range(10000, 11000); + dev_info(isp->dev, "bus width: %u, oc: %s\n", isp->devflags & ISP1760_FLAG_BUS_WIDTH_16 ? 16 : 32, isp->devflags & ISP1760_FLAG_ANALOG_OC ? "analog" : "digital"); diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index 5309d7324485..568446c9ce8d 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -503,15 +503,6 @@ static int isp1760_hc_setup(struct usb_hcd *hcd) reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, INTERRUPT_ENABLE_MASK); - /* - * PORT 1 Control register of the ISP1760 is the OTG control - * register on ISP1761. Since there is no OTG or device controller - * support in this driver, we use port 1 as a "normal" USB host port on - * both chips. - */ - reg_write32(hcd->regs, HC_PORT1_CTRL, PORT1_POWER | PORT1_INIT2); - mdelay(10); - priv->hcs_params = reg_read32(hcd->regs, HC_HCSPARAMS); return priv_init(hcd); From 0316ca6319b98e485325be98a47d08fed07ead43 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Jan 2015 00:56:01 +0200 Subject: [PATCH 156/214] usb: isp1760: Add device controller support The ISP1761 is a dual-mode host and device controller backward compatible on the host side with the ISP1760. Add support for the device controller. Signed-off-by: Laurent Pinchart Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/Kconfig | 7 + drivers/usb/host/Makefile | 1 + drivers/usb/host/isp1760-core.c | 50 +- drivers/usb/host/isp1760-core.h | 4 + drivers/usb/host/isp1760-regs.h | 110 +++ drivers/usb/host/isp1760-udc.c | 1495 +++++++++++++++++++++++++++++++ drivers/usb/host/isp1760-udc.h | 106 +++ 7 files changed, 1768 insertions(+), 5 deletions(-) create mode 100644 drivers/usb/host/isp1760-udc.c create mode 100644 drivers/usb/host/isp1760-udc.h diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig index b8e213eb36cc..c9152e260fd4 100644 --- a/drivers/usb/gadget/udc/Kconfig +++ b/drivers/usb/gadget/udc/Kconfig @@ -109,6 +109,13 @@ config USB_GR_UDC Select this to support Aeroflex Gaisler GRUSBDC cores from the GRLIB VHDL IP core library. +config USB_ISP1761_UDC + boolean "NXP ISP1761 USB Device Controller" + depends on USB_ISP1760_HCD + help + The NXP ISP1761 is a dual-role high-speed USB host and device + controller. + config USB_OMAP tristate "OMAP USB Device Controller" depends on ARCH_OMAP1 diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 4dea9b164210..67d3f1843857 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -6,6 +6,7 @@ CFLAGS_xhci-trace.o := -I$(src) isp1760-y := isp1760-core.o isp1760-hcd.o isp1760-if.o +isp1760-$(CONFIG_USB_ISP1761_UDC) += isp1760-udc.o fhci-y := fhci-hcd.o fhci-hub.o fhci-q.o fhci-y += fhci-mem.o fhci-tds.o fhci-sched.o diff --git a/drivers/usb/host/isp1760-core.c b/drivers/usb/host/isp1760-core.c index 1cba3e08898a..727e90ad15bd 100644 --- a/drivers/usb/host/isp1760-core.c +++ b/drivers/usb/host/isp1760-core.c @@ -24,9 +24,11 @@ #include "isp1760-core.h" #include "isp1760-hcd.h" #include "isp1760-regs.h" +#include "isp1760-udc.h" static void isp1760_init_core(struct isp1760_device *isp) { + u32 otgctrl; u32 hwmode; /* Low-level chip reset */ @@ -59,6 +61,17 @@ static void isp1760_init_core(struct isp1760_device *isp) if (isp->devflags & ISP1760_FLAG_INTR_EDGE_TRIG) hwmode |= HW_INTR_EDGE_TRIG; + /* + * The ISP1761 has a dedicated DC IRQ line but supports sharing the HC + * IRQ line for both the host and device controllers. Hardcode IRQ + * sharing for now and disable the DC interrupts globally to avoid + * spurious interrupts during HCD registration. + */ + if (isp->devflags & ISP1760_FLAG_ISP1761) { + isp1760_write32(isp->regs, DC_MODE, 0); + hwmode |= HW_COMN_IRQ; + } + /* * We have to set this first in case we're in 16-bit mode. * Write it twice to ensure correct upper bits if switching @@ -68,18 +81,33 @@ static void isp1760_init_core(struct isp1760_device *isp) isp1760_write32(isp->regs, HC_HW_MODE_CTRL, hwmode); /* - * PORT 1 Control register of the ISP1760 is the OTG control register on - * ISP1761. Since there is no OTG or device controller support in this - * driver, we use port 1 as a "normal" USB host port on both chips. + * PORT 1 Control register of the ISP1760 is the OTG control register + * on ISP1761. + * + * TODO: Really support OTG. For now we configure port 1 in device mode + * when OTG is requested. */ - isp1760_write32(isp->regs, HC_PORT1_CTRL, PORT1_POWER | PORT1_INIT2); - usleep_range(10000, 11000); + if ((isp->devflags & ISP1760_FLAG_ISP1761) && + (isp->devflags & ISP1760_FLAG_OTG_EN)) + otgctrl = ((HW_DM_PULLDOWN | HW_DP_PULLDOWN) << 16) + | HW_OTG_DISABLE; + else + otgctrl = (HW_SW_SEL_HC_DC << 16) + | (HW_VBUS_DRV | HW_SEL_CP_EXT); + + isp1760_write32(isp->regs, HC_PORT1_CTRL, otgctrl); dev_info(isp->dev, "bus width: %u, oc: %s\n", isp->devflags & ISP1760_FLAG_BUS_WIDTH_16 ? 16 : 32, isp->devflags & ISP1760_FLAG_ANALOG_OC ? "analog" : "digital"); } +void isp1760_set_pullup(struct isp1760_device *isp, bool enable) +{ + isp1760_write32(isp->regs, HW_OTG_CTRL_SET, + enable ? HW_DP_PULLUP : HW_DP_PULLUP << 16); +} + int isp1760_register(struct resource *mem, int irq, unsigned long irqflags, struct device *dev, unsigned int devflags) { @@ -114,6 +142,15 @@ int isp1760_register(struct resource *mem, int irq, unsigned long irqflags, if (ret < 0) return ret; + if (devflags & ISP1760_FLAG_ISP1761) { + ret = isp1760_udc_register(isp, irq, irqflags | IRQF_SHARED | + IRQF_DISABLED); + if (ret < 0) { + isp1760_hcd_unregister(&isp->hcd); + return ret; + } + } + dev_set_drvdata(dev, isp); return 0; @@ -123,6 +160,9 @@ void isp1760_unregister(struct device *dev) { struct isp1760_device *isp = dev_get_drvdata(dev); + if (isp->devflags & ISP1760_FLAG_ISP1761) + isp1760_udc_unregister(isp); + isp1760_hcd_unregister(&isp->hcd); } diff --git a/drivers/usb/host/isp1760-core.h b/drivers/usb/host/isp1760-core.h index cd4a0f3981d9..c70f8368a794 100644 --- a/drivers/usb/host/isp1760-core.h +++ b/drivers/usb/host/isp1760-core.h @@ -19,6 +19,7 @@ #include #include "isp1760-hcd.h" +#include "isp1760-udc.h" struct device; struct gpio_desc; @@ -45,12 +46,15 @@ struct isp1760_device { struct gpio_desc *rst_gpio; struct isp1760_hcd hcd; + struct isp1760_udc udc; }; int isp1760_register(struct resource *mem, int irq, unsigned long irqflags, struct device *dev, unsigned int devflags); void isp1760_unregister(struct device *dev); +void isp1760_set_pullup(struct isp1760_device *isp, bool enable); + static inline u32 isp1760_read32(void __iomem *base, u32 reg) { return readl(base + reg); diff --git a/drivers/usb/host/isp1760-regs.h b/drivers/usb/host/isp1760-regs.h index c2a39009d11f..b67095c9a9d4 100644 --- a/drivers/usb/host/isp1760-regs.h +++ b/drivers/usb/host/isp1760-regs.h @@ -16,6 +16,10 @@ #ifndef _ISP1760_REGS_H_ #define _ISP1760_REGS_H_ +/* ----------------------------------------------------------------------------- + * Host Controller + */ + /* EHCI capability registers */ #define HC_CAPLENGTH 0x000 #define HC_LENGTH(p) (((p) >> 00) & 0x00ff) /* bits 7:0 */ @@ -70,6 +74,9 @@ #define HC_HW_MODE_CTRL 0x300 #define ALL_ATX_RESET (1 << 31) #define HW_ANA_DIGI_OC (1 << 15) +#define HW_DEV_DMA (1 << 11) +#define HW_COMN_IRQ (1 << 10) +#define HW_COMN_DMA (1 << 9) #define HW_DATA_BUS_32BIT (1 << 8) #define HW_DACK_POL_HIGH (1 << 6) #define HW_DREQ_POL_HIGH (1 << 5) @@ -98,6 +105,17 @@ #define PORT1_INIT2 (1 << 23) #define HW_OTG_CTRL_SET 0x374 #define HW_OTG_CTRL_CLR 0x376 +#define HW_OTG_DISABLE (1 << 10) +#define HW_OTG_SE0_EN (1 << 9) +#define HW_BDIS_ACON_EN (1 << 8) +#define HW_SW_SEL_HC_DC (1 << 7) +#define HW_VBUS_CHRG (1 << 6) +#define HW_VBUS_DISCHRG (1 << 5) +#define HW_VBUS_DRV (1 << 4) +#define HW_SEL_CP_EXT (1 << 3) +#define HW_DM_PULLDOWN (1 << 2) +#define HW_DP_PULLDOWN (1 << 1) +#define HW_DP_PULLUP (1 << 0) /* Interrupt Register */ #define HC_INTERRUPT_REG 0x310 @@ -117,4 +135,96 @@ #define HC_INT_IRQ_MASK_AND_REG 0x328 #define HC_ATL_IRQ_MASK_AND_REG 0x32c +/* ----------------------------------------------------------------------------- + * Peripheral Controller + */ + +/* Initialization Registers */ +#define DC_ADDRESS 0x0200 +#define DC_DEVEN (1 << 7) + +#define DC_MODE 0x020c +#define DC_DMACLKON (1 << 9) +#define DC_VBUSSTAT (1 << 8) +#define DC_CLKAON (1 << 7) +#define DC_SNDRSU (1 << 6) +#define DC_GOSUSP (1 << 5) +#define DC_SFRESET (1 << 4) +#define DC_GLINTENA (1 << 3) +#define DC_WKUPCS (1 << 2) + +#define DC_INTCONF 0x0210 +#define DC_CDBGMOD_ACK_NAK (0 << 6) +#define DC_CDBGMOD_ACK (1 << 6) +#define DC_CDBGMOD_ACK_1NAK (2 << 6) +#define DC_DDBGMODIN_ACK_NAK (0 << 4) +#define DC_DDBGMODIN_ACK (1 << 4) +#define DC_DDBGMODIN_ACK_1NAK (2 << 4) +#define DC_DDBGMODOUT_ACK_NYET_NAK (0 << 2) +#define DC_DDBGMODOUT_ACK_NYET (1 << 2) +#define DC_DDBGMODOUT_ACK_NYET_1NAK (2 << 2) +#define DC_INTLVL (1 << 1) +#define DC_INTPOL (1 << 0) + +#define DC_DEBUG 0x0212 +#define DC_INTENABLE 0x0214 +#define DC_IEPTX(n) (1 << (11 + 2 * (n))) +#define DC_IEPRX(n) (1 << (10 + 2 * (n))) +#define DC_IEPRXTX(n) (3 << (10 + 2 * (n))) +#define DC_IEP0SETUP (1 << 8) +#define DC_IEVBUS (1 << 7) +#define DC_IEDMA (1 << 6) +#define DC_IEHS_STA (1 << 5) +#define DC_IERESM (1 << 4) +#define DC_IESUSP (1 << 3) +#define DC_IEPSOF (1 << 2) +#define DC_IESOF (1 << 1) +#define DC_IEBRST (1 << 0) + +/* Data Flow Registers */ +#define DC_EPINDEX 0x022c +#define DC_EP0SETUP (1 << 5) +#define DC_ENDPIDX(n) ((n) << 1) +#define DC_EPDIR (1 << 0) + +#define DC_CTRLFUNC 0x0228 +#define DC_CLBUF (1 << 4) +#define DC_VENDP (1 << 3) +#define DC_DSEN (1 << 2) +#define DC_STATUS (1 << 1) +#define DC_STALL (1 << 0) + +#define DC_DATAPORT 0x0220 +#define DC_BUFLEN 0x021c +#define DC_DATACOUNT_MASK 0xffff +#define DC_BUFSTAT 0x021e +#define DC_EPMAXPKTSZ 0x0204 + +#define DC_EPTYPE 0x0208 +#define DC_NOEMPKT (1 << 4) +#define DC_EPENABLE (1 << 3) +#define DC_DBLBUF (1 << 2) +#define DC_ENDPTYP_ISOC (1 << 0) +#define DC_ENDPTYP_BULK (2 << 0) +#define DC_ENDPTYP_INTERRUPT (3 << 0) + +/* DMA Registers */ +#define DC_DMACMD 0x0230 +#define DC_DMATXCOUNT 0x0234 +#define DC_DMACONF 0x0238 +#define DC_DMAHW 0x023c +#define DC_DMAINTREASON 0x0250 +#define DC_DMAINTEN 0x0254 +#define DC_DMAEP 0x0258 +#define DC_DMABURSTCOUNT 0x0264 + +/* General Registers */ +#define DC_INTERRUPT 0x0218 +#define DC_CHIPID 0x0270 +#define DC_FRAMENUM 0x0274 +#define DC_SCRATCH 0x0278 +#define DC_UNLOCKDEV 0x027c +#define DC_INTPULSEWIDTH 0x0280 +#define DC_TESTMODE 0x0284 + #endif diff --git a/drivers/usb/host/isp1760-udc.c b/drivers/usb/host/isp1760-udc.c new file mode 100644 index 000000000000..6bfda3082807 --- /dev/null +++ b/drivers/usb/host/isp1760-udc.c @@ -0,0 +1,1495 @@ +/* + * Driver for the NXP ISP1761 device controller + * + * Copyright 2014 Ideas on Board Oy + * + * Contacts: + * Laurent Pinchart + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "isp1760-core.h" +#include "isp1760-regs.h" +#include "isp1760-udc.h" + +#define ISP1760_VBUS_POLL_INTERVAL msecs_to_jiffies(500) + +struct isp1760_request { + struct usb_request req; + struct list_head queue; + struct isp1760_ep *ep; + unsigned int packet_size; +}; + +static inline struct isp1760_udc *gadget_to_udc(struct usb_gadget *gadget) +{ + return container_of(gadget, struct isp1760_udc, gadget); +} + +static inline struct isp1760_ep *ep_to_udc_ep(struct usb_ep *ep) +{ + return container_of(ep, struct isp1760_ep, ep); +} + +static inline struct isp1760_request *req_to_udc_req(struct usb_request *req) +{ + return container_of(req, struct isp1760_request, req); +} + +static inline u32 isp1760_udc_read(struct isp1760_udc *udc, u16 reg) +{ + return isp1760_read32(udc->regs, reg); +} + +static inline void isp1760_udc_write(struct isp1760_udc *udc, u16 reg, u32 val) +{ + isp1760_write32(udc->regs, reg, val); +} + +/* ----------------------------------------------------------------------------- + * Endpoint Management + */ + +static struct isp1760_ep *isp1760_udc_find_ep(struct isp1760_udc *udc, + u16 index) +{ + unsigned int i; + + if (index == 0) + return &udc->ep[0]; + + for (i = 1; i < ARRAY_SIZE(udc->ep); ++i) { + if (udc->ep[i].addr == index) + return udc->ep[i].desc ? &udc->ep[i] : NULL; + } + + return NULL; +} + +static void __isp1760_udc_select_ep(struct isp1760_ep *ep, int dir) +{ + isp1760_udc_write(ep->udc, DC_EPINDEX, + DC_ENDPIDX(ep->addr & USB_ENDPOINT_NUMBER_MASK) | + (dir == USB_DIR_IN ? DC_EPDIR : 0)); +} + +/** + * isp1760_udc_select_ep - Select an endpoint for register access + * @ep: The endpoint + * + * The ISP1761 endpoint registers are banked. This function selects the target + * endpoint for banked register access. The selection remains valid until the + * next call to this function, the next direct access to the EPINDEX register + * or the next reset, whichever comes first. + * + * Called with the UDC spinlock held. + */ +static void isp1760_udc_select_ep(struct isp1760_ep *ep) +{ + __isp1760_udc_select_ep(ep, ep->addr & USB_ENDPOINT_DIR_MASK); +} + +/* Called with the UDC spinlock held. */ +static void isp1760_udc_ctrl_send_status(struct isp1760_ep *ep, int dir) +{ + struct isp1760_udc *udc = ep->udc; + + /* + * Proceed to the status stage. The status stage data packet flows in + * the direction opposite to the data stage data packets, we thus need + * to select the OUT/IN endpoint for IN/OUT transfers. + */ + isp1760_udc_write(udc, DC_EPINDEX, DC_ENDPIDX(0) | + (dir == USB_DIR_IN ? 0 : DC_EPDIR)); + isp1760_udc_write(udc, DC_CTRLFUNC, DC_STATUS); + + /* + * The hardware will terminate the request automatically and go back to + * the setup stage without notifying us. + */ + udc->ep0_state = ISP1760_CTRL_SETUP; +} + +/* Called without the UDC spinlock held. */ +static void isp1760_udc_request_complete(struct isp1760_ep *ep, + struct isp1760_request *req, + int status) +{ + struct isp1760_udc *udc = ep->udc; + unsigned long flags; + + dev_dbg(ep->udc->isp->dev, "completing request %p with status %d\n", + req, status); + + req->ep = NULL; + req->req.status = status; + req->req.complete(&ep->ep, &req->req); + + spin_lock_irqsave(&udc->lock, flags); + + /* + * When completing control OUT requests, move to the status stage after + * calling the request complete callback. This gives the gadget an + * opportunity to stall the control transfer if needed. + */ + if (status == 0 && ep->addr == 0 && udc->ep0_dir == USB_DIR_OUT) + isp1760_udc_ctrl_send_status(ep, USB_DIR_OUT); + + spin_unlock_irqrestore(&udc->lock, flags); +} + +static void isp1760_udc_ctrl_send_stall(struct isp1760_ep *ep) +{ + struct isp1760_udc *udc = ep->udc; + unsigned long flags; + + dev_dbg(ep->udc->isp->dev, "%s(ep%02x)\n", __func__, ep->addr); + + spin_lock_irqsave(&udc->lock, flags); + + /* Stall both the IN and OUT endpoints. */ + __isp1760_udc_select_ep(ep, USB_DIR_OUT); + isp1760_udc_write(udc, DC_CTRLFUNC, DC_STALL); + __isp1760_udc_select_ep(ep, USB_DIR_IN); + isp1760_udc_write(udc, DC_CTRLFUNC, DC_STALL); + + /* A protocol stall completes the control transaction. */ + udc->ep0_state = ISP1760_CTRL_SETUP; + + spin_unlock_irqrestore(&udc->lock, flags); +} + +/* ----------------------------------------------------------------------------- + * Data Endpoints + */ + +/* Called with the UDC spinlock held. */ +static bool isp1760_udc_receive(struct isp1760_ep *ep, + struct isp1760_request *req) +{ + struct isp1760_udc *udc = ep->udc; + unsigned int len; + u32 *buf; + int i; + + isp1760_udc_select_ep(ep); + len = isp1760_udc_read(udc, DC_BUFLEN) & DC_DATACOUNT_MASK; + + dev_dbg(udc->isp->dev, "%s: received %u bytes (%u/%u done)\n", + __func__, len, req->req.actual, req->req.length); + + len = min(len, req->req.length - req->req.actual); + + if (!len) { + /* + * There's no data to be read from the FIFO, acknowledge the RX + * interrupt by clearing the buffer. + * + * TODO: What if another packet arrives in the meantime ? The + * datasheet doesn't clearly document how this should be + * handled. + */ + isp1760_udc_write(udc, DC_CTRLFUNC, DC_CLBUF); + return false; + } + + buf = req->req.buf + req->req.actual; + + /* + * Make sure not to read more than one extra byte, otherwise data from + * the next packet might be removed from the FIFO. + */ + for (i = len; i > 2; i -= 4, ++buf) + *buf = le32_to_cpu(isp1760_udc_read(udc, DC_DATAPORT)); + if (i > 0) + *(u16 *)buf = le16_to_cpu(readw(udc->regs + DC_DATAPORT)); + + req->req.actual += len; + + /* + * TODO: The short_not_ok flag isn't supported yet, but isn't used by + * any gadget driver either. + */ + + dev_dbg(udc->isp->dev, + "%s: req %p actual/length %u/%u maxpacket %u packet size %u\n", + __func__, req, req->req.actual, req->req.length, ep->maxpacket, + len); + + ep->rx_pending = false; + + /* + * Complete the request if all data has been received or if a short + * packet has been received. + */ + if (req->req.actual == req->req.length || len < ep->maxpacket) { + list_del(&req->queue); + return true; + } + + return false; +} + +static void isp1760_udc_transmit(struct isp1760_ep *ep, + struct isp1760_request *req) +{ + struct isp1760_udc *udc = ep->udc; + u32 *buf = req->req.buf + req->req.actual; + int i; + + req->packet_size = min(req->req.length - req->req.actual, + ep->maxpacket); + + dev_dbg(udc->isp->dev, "%s: transferring %u bytes (%u/%u done)\n", + __func__, req->packet_size, req->req.actual, + req->req.length); + + __isp1760_udc_select_ep(ep, USB_DIR_IN); + + if (req->packet_size) + isp1760_udc_write(udc, DC_BUFLEN, req->packet_size); + + /* + * Make sure not to write more than one extra byte, otherwise extra data + * will stay in the FIFO and will be transmitted during the next control + * request. The endpoint control CLBUF bit is supposed to allow flushing + * the FIFO for this kind of conditions, but doesn't seem to work. + */ + for (i = req->packet_size; i > 2; i -= 4, ++buf) + isp1760_udc_write(udc, DC_DATAPORT, cpu_to_le32(*buf)); + if (i > 0) + writew(cpu_to_le16(*(u16 *)buf), udc->regs + DC_DATAPORT); + + if (ep->addr == 0) + isp1760_udc_write(udc, DC_CTRLFUNC, DC_DSEN); + if (!req->packet_size) + isp1760_udc_write(udc, DC_CTRLFUNC, DC_VENDP); +} + +static void isp1760_ep_rx_ready(struct isp1760_ep *ep) +{ + struct isp1760_udc *udc = ep->udc; + struct isp1760_request *req; + bool complete; + + spin_lock(&udc->lock); + + if (ep->addr == 0 && udc->ep0_state != ISP1760_CTRL_DATA_OUT) { + spin_unlock(&udc->lock); + dev_dbg(udc->isp->dev, "%s: invalid ep0 state %u\n", __func__, + udc->ep0_state); + return; + } + + if (ep->addr != 0 && !ep->desc) { + spin_unlock(&udc->lock); + dev_dbg(udc->isp->dev, "%s: ep%02x is disabled\n", __func__, + ep->addr); + return; + } + + if (list_empty(&ep->queue)) { + ep->rx_pending = true; + spin_unlock(&udc->lock); + dev_dbg(udc->isp->dev, "%s: ep%02x (%p) has no request queued\n", + __func__, ep->addr, ep); + return; + } + + req = list_first_entry(&ep->queue, struct isp1760_request, + queue); + complete = isp1760_udc_receive(ep, req); + + spin_unlock(&udc->lock); + + if (complete) + isp1760_udc_request_complete(ep, req, 0); +} + +static void isp1760_ep_tx_complete(struct isp1760_ep *ep) +{ + struct isp1760_udc *udc = ep->udc; + struct isp1760_request *complete = NULL; + struct isp1760_request *req; + bool need_zlp; + + spin_lock(&udc->lock); + + if (ep->addr == 0 && udc->ep0_state != ISP1760_CTRL_DATA_IN) { + spin_unlock(&udc->lock); + dev_dbg(udc->isp->dev, "TX IRQ: invalid endpoint state %u\n", + udc->ep0_state); + return; + } + + if (list_empty(&ep->queue)) { + /* + * This can happen for the control endpoint when the reply to + * the GET_STATUS IN control request is sent directly by the + * setup IRQ handler. Just proceed to the status stage. + */ + if (ep->addr == 0) { + isp1760_udc_ctrl_send_status(ep, USB_DIR_IN); + spin_unlock(&udc->lock); + return; + } + + spin_unlock(&udc->lock); + dev_dbg(udc->isp->dev, "%s: ep%02x has no request queued\n", + __func__, ep->addr); + return; + } + + req = list_first_entry(&ep->queue, struct isp1760_request, + queue); + req->req.actual += req->packet_size; + + need_zlp = req->req.actual == req->req.length && + !(req->req.length % ep->maxpacket) && + req->packet_size && req->req.zero; + + dev_dbg(udc->isp->dev, + "TX IRQ: req %p actual/length %u/%u maxpacket %u packet size %u zero %u need zlp %u\n", + req, req->req.actual, req->req.length, ep->maxpacket, + req->packet_size, req->req.zero, need_zlp); + + /* + * Complete the request if all data has been sent and we don't need to + * transmit a zero length packet. + */ + if (req->req.actual == req->req.length && !need_zlp) { + complete = req; + list_del(&req->queue); + + if (ep->addr == 0) + isp1760_udc_ctrl_send_status(ep, USB_DIR_IN); + + if (!list_empty(&ep->queue)) + req = list_first_entry(&ep->queue, + struct isp1760_request, queue); + else + req = NULL; + } + + /* + * Transmit the next packet or start the next request, if any. + * + * TODO: If the endpoint is stalled the next request shouldn't be + * started, but what about the next packet ? + */ + if (req) + isp1760_udc_transmit(ep, req); + + spin_unlock(&udc->lock); + + if (complete) + isp1760_udc_request_complete(ep, complete, 0); +} + +static int __isp1760_udc_set_halt(struct isp1760_ep *ep, bool halt) +{ + struct isp1760_udc *udc = ep->udc; + + dev_dbg(udc->isp->dev, "%s: %s halt on ep%02x\n", __func__, + halt ? "set" : "clear", ep->addr); + + if (ep->desc && usb_endpoint_xfer_isoc(ep->desc)) { + dev_dbg(udc->isp->dev, "%s: ep%02x is isochronous\n", __func__, + ep->addr); + return -EINVAL; + } + + isp1760_udc_select_ep(ep); + isp1760_udc_write(udc, DC_CTRLFUNC, halt ? DC_STALL : 0); + + if (ep->addr == 0) { + /* When halting the control endpoint, stall both IN and OUT. */ + __isp1760_udc_select_ep(ep, USB_DIR_IN); + isp1760_udc_write(udc, DC_CTRLFUNC, halt ? DC_STALL : 0); + } else if (!halt) { + /* Reset the data PID by cycling the endpoint enable bit. */ + u16 eptype = isp1760_udc_read(udc, DC_EPTYPE); + + isp1760_udc_write(udc, DC_EPTYPE, eptype & ~DC_EPENABLE); + isp1760_udc_write(udc, DC_EPTYPE, eptype); + + /* + * Disabling the endpoint emptied the transmit FIFO, fill it + * again if a request is pending. + * + * TODO: Does the gadget framework require synchronizatino with + * the TX IRQ handler ? + */ + if ((ep->addr & USB_DIR_IN) && !list_empty(&ep->queue)) { + struct isp1760_request *req; + + req = list_first_entry(&ep->queue, + struct isp1760_request, queue); + isp1760_udc_transmit(ep, req); + } + } + + ep->halted = halt; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Control Endpoint + */ + +static int isp1760_udc_get_status(struct isp1760_udc *udc, + const struct usb_ctrlrequest *req) +{ + struct isp1760_ep *ep; + u16 status; + + if (req->wLength != cpu_to_le16(2) || req->wValue != cpu_to_le16(0)) + return -EINVAL; + + switch (req->bRequestType) { + case USB_DIR_IN | USB_RECIP_DEVICE: + status = udc->devstatus; + break; + + case USB_DIR_IN | USB_RECIP_INTERFACE: + status = 0; + break; + + case USB_DIR_IN | USB_RECIP_ENDPOINT: + ep = isp1760_udc_find_ep(udc, le16_to_cpu(req->wIndex)); + if (!ep) + return -EINVAL; + + status = 0; + if (ep->halted) + status |= 1 << USB_ENDPOINT_HALT; + break; + + default: + return -EINVAL; + } + + isp1760_udc_write(udc, DC_EPINDEX, DC_ENDPIDX(0) | DC_EPDIR); + isp1760_udc_write(udc, DC_BUFLEN, 2); + + writew(cpu_to_le16(status), udc->regs + DC_DATAPORT); + + isp1760_udc_write(udc, DC_CTRLFUNC, DC_DSEN); + + dev_dbg(udc->isp->dev, "%s: status 0x%04x\n", __func__, status); + + return 0; +} + +static int isp1760_udc_set_address(struct isp1760_udc *udc, u16 addr) +{ + if (addr > 127) { + dev_dbg(udc->isp->dev, "invalid device address %u\n", addr); + return -EINVAL; + } + + if (udc->gadget.state != USB_STATE_DEFAULT && + udc->gadget.state != USB_STATE_ADDRESS) { + dev_dbg(udc->isp->dev, "can't set address in state %u\n", + udc->gadget.state); + return -EINVAL; + } + + usb_gadget_set_state(&udc->gadget, addr ? USB_STATE_ADDRESS : + USB_STATE_DEFAULT); + + isp1760_udc_write(udc, DC_ADDRESS, DC_DEVEN | addr); + + spin_lock(&udc->lock); + isp1760_udc_ctrl_send_status(&udc->ep[0], USB_DIR_OUT); + spin_unlock(&udc->lock); + + return 0; +} + +static bool isp1760_ep0_setup_standard(struct isp1760_udc *udc, + struct usb_ctrlrequest *req) +{ + bool stall; + + switch (req->bRequest) { + case USB_REQ_GET_STATUS: + return isp1760_udc_get_status(udc, req); + + case USB_REQ_CLEAR_FEATURE: + switch (req->bRequestType) { + case USB_DIR_OUT | USB_RECIP_DEVICE: { + /* TODO: Handle remote wakeup feature. */ + return true; + } + + case USB_DIR_OUT | USB_RECIP_ENDPOINT: { + u16 index = le16_to_cpu(req->wIndex); + struct isp1760_ep *ep; + + if (req->wLength != cpu_to_le16(0) || + req->wValue != cpu_to_le16(USB_ENDPOINT_HALT)) + return true; + + ep = isp1760_udc_find_ep(udc, index); + if (!ep) + return true; + + spin_lock(&udc->lock); + + /* + * If the endpoint is wedged only the gadget can clear + * the halt feature. Pretend success in that case, but + * keep the endpoint halted. + */ + if (!ep->wedged) + stall = __isp1760_udc_set_halt(ep, false); + else + stall = false; + + if (!stall) + isp1760_udc_ctrl_send_status(&udc->ep[0], + USB_DIR_OUT); + + spin_unlock(&udc->lock); + return stall; + } + + default: + return true; + } + break; + + case USB_REQ_SET_FEATURE: + switch (req->bRequestType) { + case USB_DIR_OUT | USB_RECIP_DEVICE: { + /* TODO: Handle remote wakeup and test mode features */ + return true; + } + + case USB_DIR_OUT | USB_RECIP_ENDPOINT: { + u16 index = le16_to_cpu(req->wIndex); + struct isp1760_ep *ep; + + if (req->wLength != cpu_to_le16(0) || + req->wValue != cpu_to_le16(USB_ENDPOINT_HALT)) + return true; + + ep = isp1760_udc_find_ep(udc, index); + if (!ep) + return true; + + spin_lock(&udc->lock); + + stall = __isp1760_udc_set_halt(ep, true); + if (!stall) + isp1760_udc_ctrl_send_status(&udc->ep[0], + USB_DIR_OUT); + + spin_unlock(&udc->lock); + return stall; + } + + default: + return true; + } + break; + + case USB_REQ_SET_ADDRESS: + if (req->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE)) + return true; + + return isp1760_udc_set_address(udc, le16_to_cpu(req->wValue)); + + case USB_REQ_SET_CONFIGURATION: + if (req->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE)) + return true; + + if (udc->gadget.state != USB_STATE_ADDRESS && + udc->gadget.state != USB_STATE_CONFIGURED) + return true; + + stall = udc->driver->setup(&udc->gadget, req) < 0; + if (stall) + return true; + + usb_gadget_set_state(&udc->gadget, req->wValue ? + USB_STATE_CONFIGURED : USB_STATE_ADDRESS); + + /* + * SET_CONFIGURATION (and SET_INTERFACE) must reset the halt + * feature on all endpoints. There is however no need to do so + * explicitly here as the gadget driver will disable and + * reenable endpoints, clearing the halt feature. + */ + return false; + + default: + return udc->driver->setup(&udc->gadget, req) < 0; + } +} + +static void isp1760_ep0_setup(struct isp1760_udc *udc) +{ + union { + struct usb_ctrlrequest r; + u32 data[2]; + } req; + unsigned int count; + bool stall = false; + + spin_lock(&udc->lock); + + isp1760_udc_write(udc, DC_EPINDEX, DC_EP0SETUP); + + count = isp1760_udc_read(udc, DC_BUFLEN) & DC_DATACOUNT_MASK; + if (count != sizeof(req)) { + spin_unlock(&udc->lock); + + dev_err(udc->isp->dev, "invalid length %u for setup packet\n", + count); + + isp1760_udc_ctrl_send_stall(&udc->ep[0]); + return; + } + + req.data[0] = isp1760_udc_read(udc, DC_DATAPORT); + req.data[1] = isp1760_udc_read(udc, DC_DATAPORT); + + if (udc->ep0_state != ISP1760_CTRL_SETUP) { + spin_unlock(&udc->lock); + dev_dbg(udc->isp->dev, "unexpected SETUP packet\n"); + return; + } + + /* Move to the data stage. */ + if (!req.r.wLength) + udc->ep0_state = ISP1760_CTRL_STATUS; + else if (req.r.bRequestType & USB_DIR_IN) + udc->ep0_state = ISP1760_CTRL_DATA_IN; + else + udc->ep0_state = ISP1760_CTRL_DATA_OUT; + + udc->ep0_dir = req.r.bRequestType & USB_DIR_IN; + udc->ep0_length = le16_to_cpu(req.r.wLength); + + spin_unlock(&udc->lock); + + dev_dbg(udc->isp->dev, + "%s: bRequestType 0x%02x bRequest 0x%02x wValue 0x%04x wIndex 0x%04x wLength 0x%04x\n", + __func__, req.r.bRequestType, req.r.bRequest, + le16_to_cpu(req.r.wValue), le16_to_cpu(req.r.wIndex), + le16_to_cpu(req.r.wLength)); + + if ((req.r.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) + stall = isp1760_ep0_setup_standard(udc, &req.r); + else + stall = udc->driver->setup(&udc->gadget, &req.r) < 0; + + if (stall) + isp1760_udc_ctrl_send_stall(&udc->ep[0]); +} + +/* ----------------------------------------------------------------------------- + * Gadget Endpoint Operations + */ + +static int isp1760_ep_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + struct isp1760_ep *uep = ep_to_udc_ep(ep); + struct isp1760_udc *udc = uep->udc; + unsigned long flags; + unsigned int type; + + dev_dbg(uep->udc->isp->dev, "%s\n", __func__); + + /* + * Validate the descriptor. The control endpoint can't be enabled + * manually. + */ + if (desc->bDescriptorType != USB_DT_ENDPOINT || + desc->bEndpointAddress == 0 || + desc->bEndpointAddress != uep->addr || + le16_to_cpu(desc->wMaxPacketSize) > ep->maxpacket) { + dev_dbg(udc->isp->dev, + "%s: invalid descriptor type %u addr %02x ep addr %02x max packet size %u/%u\n", + __func__, desc->bDescriptorType, + desc->bEndpointAddress, uep->addr, + le16_to_cpu(desc->wMaxPacketSize), ep->maxpacket); + return -EINVAL; + } + + switch (usb_endpoint_type(desc)) { + case USB_ENDPOINT_XFER_ISOC: + type = DC_ENDPTYP_ISOC; + break; + case USB_ENDPOINT_XFER_BULK: + type = DC_ENDPTYP_BULK; + break; + case USB_ENDPOINT_XFER_INT: + type = DC_ENDPTYP_INTERRUPT; + break; + case USB_ENDPOINT_XFER_CONTROL: + default: + dev_dbg(udc->isp->dev, "%s: control endpoints unsupported\n", + __func__); + return -EINVAL; + } + + spin_lock_irqsave(&udc->lock, flags); + + uep->desc = desc; + uep->maxpacket = le16_to_cpu(desc->wMaxPacketSize); + uep->rx_pending = false; + uep->halted = false; + uep->wedged = false; + + isp1760_udc_select_ep(uep); + isp1760_udc_write(udc, DC_EPMAXPKTSZ, uep->maxpacket); + isp1760_udc_write(udc, DC_BUFLEN, uep->maxpacket); + isp1760_udc_write(udc, DC_EPTYPE, DC_EPENABLE | type); + + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + +static int isp1760_ep_disable(struct usb_ep *ep) +{ + struct isp1760_ep *uep = ep_to_udc_ep(ep); + struct isp1760_udc *udc = uep->udc; + struct isp1760_request *req, *nreq; + LIST_HEAD(req_list); + unsigned long flags; + + dev_dbg(udc->isp->dev, "%s\n", __func__); + + spin_lock_irqsave(&udc->lock, flags); + + if (!uep->desc) { + dev_dbg(udc->isp->dev, "%s: endpoint not enabled\n", __func__); + spin_unlock_irqrestore(&udc->lock, flags); + return -EINVAL; + } + + uep->desc = NULL; + uep->maxpacket = 0; + + isp1760_udc_select_ep(uep); + isp1760_udc_write(udc, DC_EPTYPE, 0); + + /* TODO Synchronize with the IRQ handler */ + + list_splice_init(&uep->queue, &req_list); + + spin_unlock_irqrestore(&udc->lock, flags); + + list_for_each_entry_safe(req, nreq, &req_list, queue) { + list_del(&req->queue); + isp1760_udc_request_complete(uep, req, -ESHUTDOWN); + } + + return 0; +} + +static struct usb_request *isp1760_ep_alloc_request(struct usb_ep *ep, + gfp_t gfp_flags) +{ + struct isp1760_request *req; + + req = kzalloc(sizeof(*req), gfp_flags); + + return &req->req; +} + +static void isp1760_ep_free_request(struct usb_ep *ep, struct usb_request *_req) +{ + struct isp1760_request *req = req_to_udc_req(_req); + + kfree(req); +} + +static int isp1760_ep_queue(struct usb_ep *ep, struct usb_request *_req, + gfp_t gfp_flags) +{ + struct isp1760_request *req = req_to_udc_req(_req); + struct isp1760_ep *uep = ep_to_udc_ep(ep); + struct isp1760_udc *udc = uep->udc; + bool complete = false; + unsigned long flags; + int ret = 0; + + _req->status = -EINPROGRESS; + _req->actual = 0; + + spin_lock_irqsave(&udc->lock, flags); + + dev_dbg(udc->isp->dev, + "%s: req %p (%u bytes%s) ep %p(0x%02x)\n", __func__, _req, + _req->length, _req->zero ? " (zlp)" : "", uep, uep->addr); + + req->ep = uep; + + if (uep->addr == 0) { + if (_req->length != udc->ep0_length && + udc->ep0_state != ISP1760_CTRL_DATA_IN) { + dev_dbg(udc->isp->dev, + "%s: invalid length %u for req %p\n", + __func__, _req->length, req); + ret = -EINVAL; + goto done; + } + + switch (udc->ep0_state) { + case ISP1760_CTRL_DATA_IN: + dev_dbg(udc->isp->dev, "%s: transmitting req %p\n", + __func__, req); + + list_add_tail(&req->queue, &uep->queue); + isp1760_udc_transmit(uep, req); + break; + + case ISP1760_CTRL_DATA_OUT: + list_add_tail(&req->queue, &uep->queue); + __isp1760_udc_select_ep(uep, USB_DIR_OUT); + isp1760_udc_write(udc, DC_CTRLFUNC, DC_DSEN); + break; + + case ISP1760_CTRL_STATUS: + complete = true; + break; + + default: + dev_dbg(udc->isp->dev, "%s: invalid ep0 state\n", + __func__); + ret = -EINVAL; + break; + } + } else if (uep->desc) { + bool empty = list_empty(&uep->queue); + + list_add_tail(&req->queue, &uep->queue); + if ((uep->addr & USB_DIR_IN) && !uep->halted && empty) + isp1760_udc_transmit(uep, req); + else if (!(uep->addr & USB_DIR_IN) && uep->rx_pending) + complete = isp1760_udc_receive(uep, req); + } else { + dev_dbg(udc->isp->dev, + "%s: can't queue request to disabled ep%02x\n", + __func__, uep->addr); + ret = -ESHUTDOWN; + } + +done: + if (ret < 0) + req->ep = NULL; + + spin_unlock_irqrestore(&udc->lock, flags); + + if (complete) + isp1760_udc_request_complete(uep, req, 0); + + return ret; +} + +static int isp1760_ep_dequeue(struct usb_ep *ep, struct usb_request *_req) +{ + struct isp1760_request *req = req_to_udc_req(_req); + struct isp1760_ep *uep = ep_to_udc_ep(ep); + struct isp1760_udc *udc = uep->udc; + unsigned long flags; + + dev_dbg(uep->udc->isp->dev, "%s(ep%02x)\n", __func__, uep->addr); + + spin_lock_irqsave(&udc->lock, flags); + + if (req->ep != uep) + req = NULL; + else + list_del(&req->queue); + + spin_unlock_irqrestore(&udc->lock, flags); + + if (!req) + return -EINVAL; + + isp1760_udc_request_complete(uep, req, -ECONNRESET); + return 0; +} + +static int __isp1760_ep_set_halt(struct isp1760_ep *uep, bool stall, bool wedge) +{ + struct isp1760_udc *udc = uep->udc; + int ret; + + if (!uep->addr) { + /* + * Halting the control endpoint is only valid as a delayed error + * response to a SETUP packet. Make sure EP0 is in the right + * stage and that the gadget isn't trying to clear the halt + * condition. + */ + if (WARN_ON(udc->ep0_state == ISP1760_CTRL_SETUP || !stall || + wedge)) { + return -EINVAL; + } + } + + if (uep->addr && !uep->desc) { + dev_dbg(udc->isp->dev, "%s: ep%02x is disabled\n", __func__, + uep->addr); + return -EINVAL; + } + + if (uep->addr & USB_DIR_IN) { + /* Refuse to halt IN endpoints with active transfers. */ + if (!list_empty(&uep->queue)) { + dev_dbg(udc->isp->dev, + "%s: ep%02x has request pending\n", __func__, + uep->addr); + return -EAGAIN; + } + } + + ret = __isp1760_udc_set_halt(uep, stall); + if (ret < 0) + return ret; + + if (!uep->addr) { + /* + * Stalling EP0 completes the control transaction, move back to + * the SETUP state. + */ + udc->ep0_state = ISP1760_CTRL_SETUP; + return 0; + } + + if (wedge) + uep->wedged = true; + else if (!stall) + uep->wedged = false; + + return 0; +} + +static int isp1760_ep_set_halt(struct usb_ep *ep, int value) +{ + struct isp1760_ep *uep = ep_to_udc_ep(ep); + unsigned long flags; + int ret; + + dev_dbg(uep->udc->isp->dev, "%s: %s halt on ep%02x\n", __func__, + value ? "set" : "clear", uep->addr); + + spin_lock_irqsave(&uep->udc->lock, flags); + ret = __isp1760_ep_set_halt(uep, value, false); + spin_unlock_irqrestore(&uep->udc->lock, flags); + + return ret; +} + +static int isp1760_ep_set_wedge(struct usb_ep *ep) +{ + struct isp1760_ep *uep = ep_to_udc_ep(ep); + unsigned long flags; + int ret; + + dev_dbg(uep->udc->isp->dev, "%s: set wedge on ep%02x)\n", __func__, + uep->addr); + + spin_lock_irqsave(&uep->udc->lock, flags); + ret = __isp1760_ep_set_halt(uep, true, true); + spin_unlock_irqrestore(&uep->udc->lock, flags); + + return ret; +} + +static void isp1760_ep_fifo_flush(struct usb_ep *ep) +{ + struct isp1760_ep *uep = ep_to_udc_ep(ep); + struct isp1760_udc *udc = uep->udc; + unsigned long flags; + + spin_lock_irqsave(&udc->lock, flags); + + isp1760_udc_select_ep(uep); + + /* + * Set the CLBUF bit twice to flush both buffers in case double + * buffering is enabled. + */ + isp1760_udc_write(udc, DC_CTRLFUNC, DC_CLBUF); + isp1760_udc_write(udc, DC_CTRLFUNC, DC_CLBUF); + + spin_unlock_irqrestore(&udc->lock, flags); +} + +static const struct usb_ep_ops isp1760_ep_ops = { + .enable = isp1760_ep_enable, + .disable = isp1760_ep_disable, + .alloc_request = isp1760_ep_alloc_request, + .free_request = isp1760_ep_free_request, + .queue = isp1760_ep_queue, + .dequeue = isp1760_ep_dequeue, + .set_halt = isp1760_ep_set_halt, + .set_wedge = isp1760_ep_set_wedge, + .fifo_flush = isp1760_ep_fifo_flush, +}; + +/* ----------------------------------------------------------------------------- + * Device States + */ + +/* Called with the UDC spinlock held. */ +static void isp1760_udc_connect(struct isp1760_udc *udc) +{ + usb_gadget_set_state(&udc->gadget, USB_STATE_POWERED); + mod_timer(&udc->vbus_timer, jiffies + ISP1760_VBUS_POLL_INTERVAL); +} + +/* Called with the UDC spinlock held. */ +static void isp1760_udc_disconnect(struct isp1760_udc *udc) +{ + if (udc->gadget.state < USB_STATE_POWERED) + return; + + dev_dbg(udc->isp->dev, "Device disconnected in state %u\n", + udc->gadget.state); + + udc->gadget.speed = USB_SPEED_UNKNOWN; + usb_gadget_set_state(&udc->gadget, USB_STATE_ATTACHED); + + if (udc->driver->disconnect) + udc->driver->disconnect(&udc->gadget); + + del_timer(&udc->vbus_timer); + + /* TODO Reset all endpoints ? */ +} + +static void isp1760_udc_init_hw(struct isp1760_udc *udc) +{ + /* + * The device controller currently shares its interrupt with the host + * controller, the DC_IRQ polarity and signaling mode are ignored. Set + * the to active-low level-triggered. + * + * Configure the control, in and out pipes to generate interrupts on + * ACK tokens only (and NYET for the out pipe). The default + * configuration also generates an interrupt on the first NACK token. + */ + isp1760_udc_write(udc, DC_INTCONF, DC_CDBGMOD_ACK | DC_DDBGMODIN_ACK | + DC_DDBGMODOUT_ACK_NYET); + + isp1760_udc_write(udc, DC_INTENABLE, DC_IEPRXTX(7) | DC_IEPRXTX(6) | + DC_IEPRXTX(5) | DC_IEPRXTX(4) | DC_IEPRXTX(3) | + DC_IEPRXTX(2) | DC_IEPRXTX(1) | DC_IEPRXTX(0) | + DC_IEP0SETUP | DC_IEVBUS | DC_IERESM | DC_IESUSP | + DC_IEHS_STA | DC_IEBRST); + + if (udc->connected) + isp1760_set_pullup(udc->isp, true); + + isp1760_udc_write(udc, DC_ADDRESS, DC_DEVEN); +} + +static void isp1760_udc_reset(struct isp1760_udc *udc) +{ + unsigned long flags; + + spin_lock_irqsave(&udc->lock, flags); + + /* + * The bus reset has reset most registers to their default value, + * reinitialize the UDC hardware. + */ + isp1760_udc_init_hw(udc); + + udc->ep0_state = ISP1760_CTRL_SETUP; + udc->gadget.speed = USB_SPEED_FULL; + + usb_gadget_udc_reset(&udc->gadget, udc->driver); + + spin_unlock_irqrestore(&udc->lock, flags); +} + +static void isp1760_udc_suspend(struct isp1760_udc *udc) +{ + if (udc->gadget.state < USB_STATE_DEFAULT) + return; + + if (udc->driver->suspend) + udc->driver->suspend(&udc->gadget); +} + +static void isp1760_udc_resume(struct isp1760_udc *udc) +{ + if (udc->gadget.state < USB_STATE_DEFAULT) + return; + + if (udc->driver->resume) + udc->driver->resume(&udc->gadget); +} + +/* ----------------------------------------------------------------------------- + * Gadget Operations + */ + +static int isp1760_udc_get_frame(struct usb_gadget *gadget) +{ + struct isp1760_udc *udc = gadget_to_udc(gadget); + + return isp1760_udc_read(udc, DC_FRAMENUM) & ((1 << 11) - 1); +} + +static int isp1760_udc_wakeup(struct usb_gadget *gadget) +{ + struct isp1760_udc *udc = gadget_to_udc(gadget); + + dev_dbg(udc->isp->dev, "%s\n", __func__); + return -ENOTSUPP; +} + +static int isp1760_udc_set_selfpowered(struct usb_gadget *gadget, + int is_selfpowered) +{ + struct isp1760_udc *udc = gadget_to_udc(gadget); + + if (is_selfpowered) + udc->devstatus |= 1 << USB_DEVICE_SELF_POWERED; + else + udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED); + + return 0; +} + +static int isp1760_udc_pullup(struct usb_gadget *gadget, int is_on) +{ + struct isp1760_udc *udc = gadget_to_udc(gadget); + + isp1760_set_pullup(udc->isp, is_on); + udc->connected = is_on; + + return 0; +} + +static int isp1760_udc_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + struct isp1760_udc *udc = gadget_to_udc(gadget); + + /* The hardware doesn't support low speed. */ + if (driver->max_speed < USB_SPEED_FULL) { + dev_err(udc->isp->dev, "Invalid gadget driver\n"); + return -EINVAL; + } + + spin_lock(&udc->lock); + + if (udc->driver) { + dev_err(udc->isp->dev, "UDC already has a gadget driver\n"); + spin_unlock(&udc->lock); + return -EBUSY; + } + + udc->driver = driver; + + spin_unlock(&udc->lock); + + dev_dbg(udc->isp->dev, "starting UDC with driver %s\n", + driver->function); + + udc->devstatus = 0; + udc->connected = true; + + usb_gadget_set_state(&udc->gadget, USB_STATE_ATTACHED); + + /* DMA isn't supported yet, don't enable the DMA clock. */ + isp1760_udc_write(udc, DC_MODE, DC_GLINTENA); + + isp1760_udc_init_hw(udc); + + dev_dbg(udc->isp->dev, "UDC started with driver %s\n", + driver->function); + + return 0; +} + +static int isp1760_udc_stop(struct usb_gadget *gadget) +{ + struct isp1760_udc *udc = gadget_to_udc(gadget); + + dev_dbg(udc->isp->dev, "%s\n", __func__); + + del_timer_sync(&udc->vbus_timer); + + isp1760_udc_write(udc, DC_MODE, 0); + + spin_lock(&udc->lock); + udc->driver = NULL; + spin_unlock(&udc->lock); + + return 0; +} + +static struct usb_gadget_ops isp1760_udc_ops = { + .get_frame = isp1760_udc_get_frame, + .wakeup = isp1760_udc_wakeup, + .set_selfpowered = isp1760_udc_set_selfpowered, + .pullup = isp1760_udc_pullup, + .udc_start = isp1760_udc_start, + .udc_stop = isp1760_udc_stop, +}; + +/* ----------------------------------------------------------------------------- + * Interrupt Handling + */ + +static irqreturn_t isp1760_udc_irq(int irq, void *dev) +{ + struct isp1760_udc *udc = dev; + unsigned int i; + u32 status; + + status = isp1760_udc_read(udc, DC_INTERRUPT) + & isp1760_udc_read(udc, DC_INTENABLE); + isp1760_udc_write(udc, DC_INTERRUPT, status); + + if (status & DC_IEVBUS) { + dev_dbg(udc->isp->dev, "%s(VBUS)\n", __func__); + /* The VBUS interrupt is only triggered when VBUS appears. */ + spin_lock(&udc->lock); + isp1760_udc_connect(udc); + spin_unlock(&udc->lock); + } + + if (status & DC_IEBRST) { + dev_dbg(udc->isp->dev, "%s(BRST)\n", __func__); + + isp1760_udc_reset(udc); + } + + for (i = 0; i <= 7; ++i) { + struct isp1760_ep *ep = &udc->ep[i*2]; + + if (status & DC_IEPTX(i)) { + dev_dbg(udc->isp->dev, "%s(EPTX%u)\n", __func__, i); + isp1760_ep_tx_complete(ep); + } + + if (status & DC_IEPRX(i)) { + dev_dbg(udc->isp->dev, "%s(EPRX%u)\n", __func__, i); + isp1760_ep_rx_ready(i ? ep - 1 : ep); + } + } + + if (status & DC_IEP0SETUP) { + dev_dbg(udc->isp->dev, "%s(EP0SETUP)\n", __func__); + + isp1760_ep0_setup(udc); + } + + if (status & DC_IERESM) { + dev_dbg(udc->isp->dev, "%s(RESM)\n", __func__); + isp1760_udc_resume(udc); + } + + if (status & DC_IESUSP) { + dev_dbg(udc->isp->dev, "%s(SUSP)\n", __func__); + + spin_lock(&udc->lock); + if (!(isp1760_udc_read(udc, DC_MODE) & DC_VBUSSTAT)) + isp1760_udc_disconnect(udc); + else + isp1760_udc_suspend(udc); + spin_unlock(&udc->lock); + } + + if (status & DC_IEHS_STA) { + dev_dbg(udc->isp->dev, "%s(HS_STA)\n", __func__); + udc->gadget.speed = USB_SPEED_HIGH; + } + + return status ? IRQ_HANDLED : IRQ_NONE; +} + +static void isp1760_udc_vbus_poll(unsigned long data) +{ + struct isp1760_udc *udc = (struct isp1760_udc *)data; + unsigned long flags; + + spin_lock_irqsave(&udc->lock, flags); + + if (!(isp1760_udc_read(udc, DC_MODE) & DC_VBUSSTAT)) + isp1760_udc_disconnect(udc); + else if (udc->gadget.state >= USB_STATE_POWERED) + mod_timer(&udc->vbus_timer, + jiffies + ISP1760_VBUS_POLL_INTERVAL); + + spin_unlock_irqrestore(&udc->lock, flags); +} + +/* ----------------------------------------------------------------------------- + * Registration + */ + +static void isp1760_udc_init_eps(struct isp1760_udc *udc) +{ + unsigned int i; + + INIT_LIST_HEAD(&udc->gadget.ep_list); + + for (i = 0; i < ARRAY_SIZE(udc->ep); ++i) { + struct isp1760_ep *ep = &udc->ep[i]; + unsigned int ep_num = (i + 1) / 2; + bool is_in = !(i & 1); + + ep->udc = udc; + + INIT_LIST_HEAD(&ep->queue); + + ep->addr = (ep_num && is_in ? USB_DIR_IN : USB_DIR_OUT) + | ep_num; + ep->desc = NULL; + + sprintf(ep->name, "ep%u%s", ep_num, + ep_num ? (is_in ? "in" : "out") : ""); + + ep->ep.ops = &isp1760_ep_ops; + ep->ep.name = ep->name; + + /* + * Hardcode the maximum packet sizes for now, to 64 bytes for + * the control endpoint and 512 bytes for all other endpoints. + * This fits in the 8kB FIFO without double-buffering. + */ + if (ep_num == 0) { + ep->ep.maxpacket = 64; + ep->maxpacket = 64; + udc->gadget.ep0 = &ep->ep; + } else { + ep->ep.maxpacket = 512; + ep->maxpacket = 0; + list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); + } + } +} + +static int isp1760_udc_init(struct isp1760_udc *udc) +{ + u16 scratch; + u32 chipid; + + /* + * Check that the controller is present by writing to the scratch + * register, modifying the bus pattern by reading from the chip ID + * register, and reading the scratch register value back. The chip ID + * and scratch register contents must match the expected values. + */ + isp1760_udc_write(udc, DC_SCRATCH, 0xbabe); + chipid = isp1760_udc_read(udc, DC_CHIPID); + scratch = isp1760_udc_read(udc, DC_SCRATCH); + + if (scratch != 0xbabe) { + dev_err(udc->isp->dev, + "udc: scratch test failed (0x%04x/0x%08x)\n", + scratch, chipid); + return -ENODEV; + } + + if (chipid != 0x00011582) { + dev_err(udc->isp->dev, "udc: invalid chip ID 0x%08x\n", chipid); + return -ENODEV; + } + + /* Reset the device controller. */ + isp1760_udc_write(udc, DC_MODE, DC_SFRESET); + usleep_range(10000, 11000); + isp1760_udc_write(udc, DC_MODE, 0); + usleep_range(10000, 11000); + + return 0; +} + +int isp1760_udc_register(struct isp1760_device *isp, int irq, + unsigned long irqflags) +{ + struct isp1760_udc *udc = &isp->udc; + const char *devname; + int ret; + + udc->irq = -1; + udc->isp = isp; + udc->regs = isp->regs; + + spin_lock_init(&udc->lock); + setup_timer(&udc->vbus_timer, isp1760_udc_vbus_poll, + (unsigned long)udc); + + ret = isp1760_udc_init(udc); + if (ret < 0) + return ret; + + devname = dev_name(isp->dev); + udc->irqname = kmalloc(strlen(devname) + 7, GFP_KERNEL); + if (!udc->irqname) + return -ENOMEM; + + sprintf(udc->irqname, "%s (udc)", devname); + + ret = request_irq(irq, isp1760_udc_irq, IRQF_SHARED | IRQF_DISABLED | + irqflags, udc->irqname, udc); + if (ret < 0) + goto error; + + udc->irq = irq; + + /* + * Initialize the gadget static fields and register its device. Gadget + * fields that vary during the life time of the gadget are initialized + * by the UDC core. + */ + udc->gadget.ops = &isp1760_udc_ops; + udc->gadget.speed = USB_SPEED_UNKNOWN; + udc->gadget.max_speed = USB_SPEED_HIGH; + udc->gadget.name = "isp1761_udc"; + + isp1760_udc_init_eps(udc); + + ret = usb_add_gadget_udc(isp->dev, &udc->gadget); + if (ret < 0) + goto error; + + return 0; + +error: + if (udc->irq >= 0) + free_irq(udc->irq, udc); + kfree(udc->irqname); + + return ret; +} + +void isp1760_udc_unregister(struct isp1760_device *isp) +{ + struct isp1760_udc *udc = &isp->udc; + + usb_del_gadget_udc(&udc->gadget); + + free_irq(udc->irq, udc); + kfree(udc->irqname); +} diff --git a/drivers/usb/host/isp1760-udc.h b/drivers/usb/host/isp1760-udc.h new file mode 100644 index 000000000000..4af6ba6eda86 --- /dev/null +++ b/drivers/usb/host/isp1760-udc.h @@ -0,0 +1,106 @@ +/* + * Driver for the NXP ISP1761 device controller + * + * Copyright 2014 Ideas on Board Oy + * + * Contacts: + * Laurent Pinchart + * + * 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 _ISP1760_UDC_H_ +#define _ISP1760_UDC_H_ + +#include +#include +#include +#include +#include + +struct isp1760_device; +struct isp1760_udc; + +enum isp1760_ctrl_state { + ISP1760_CTRL_SETUP, /* Waiting for a SETUP transaction */ + ISP1760_CTRL_DATA_IN, /* Setup received, data IN stage */ + ISP1760_CTRL_DATA_OUT, /* Setup received, data OUT stage */ + ISP1760_CTRL_STATUS, /* 0-length request in status stage */ +}; + +struct isp1760_ep { + struct isp1760_udc *udc; + struct usb_ep ep; + + struct list_head queue; + + unsigned int addr; + unsigned int maxpacket; + char name[7]; + + const struct usb_endpoint_descriptor *desc; + + bool rx_pending; + bool halted; + bool wedged; +}; + +/** + * struct isp1760_udc - UDC state information + * irq: IRQ number + * irqname: IRQ name (as passed to request_irq) + * regs: Base address of the UDC registers + * driver: Gadget driver + * gadget: Gadget device + * lock: Protects driver, vbus_timer, ep, ep0_*, DC_EPINDEX register + * ep: Array of endpoints + * ep0_state: Control request state for endpoint 0 + * ep0_dir: Direction of the current control request + * ep0_length: Length of the current control request + * connected: Tracks gadget driver bus connection state + */ +struct isp1760_udc { +#if CONFIG_USB_ISP1761_UDC + struct isp1760_device *isp; + + int irq; + char *irqname; + void __iomem *regs; + + struct usb_gadget_driver *driver; + struct usb_gadget gadget; + + spinlock_t lock; + struct timer_list vbus_timer; + + struct isp1760_ep ep[15]; + + enum isp1760_ctrl_state ep0_state; + u8 ep0_dir; + u16 ep0_length; + + bool connected; + + unsigned int devstatus; +#endif +}; + +#if CONFIG_USB_ISP1761_UDC +int isp1760_udc_register(struct isp1760_device *isp, int irq, + unsigned long irqflags); +void isp1760_udc_unregister(struct isp1760_device *isp); +#else +static inline int isp1760_udc_register(struct isp1760_device *isp, int irq, + unsigned long irqflags) +{ + return 0; +} + +static inline void isp1760_udc_unregister(struct isp1760_device *isp) +{ +} +#endif + +#endif From 7ef077a8ad3557f030d0407c4f56c5a0cf1e418a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Jan 2015 00:56:02 +0200 Subject: [PATCH 157/214] usb: isp1760: Move driver from drivers/usb/host/ to drivers/usb/isp1760/ Now that this is DRD, it doesn't make sense to keep it under drivers/usb/host. Signed-off-by: Laurent Pinchart Signed-off-by: Felipe Balbi --- drivers/usb/Kconfig | 2 ++ drivers/usb/Makefile | 2 +- drivers/usb/gadget/udc/Kconfig | 7 ------- drivers/usb/host/Kconfig | 14 ------------- drivers/usb/host/Makefile | 4 ---- drivers/usb/isp1760/Kconfig | 22 ++++++++++++++++++++ drivers/usb/isp1760/Makefile | 4 ++++ drivers/usb/{host => isp1760}/isp1760-core.c | 0 drivers/usb/{host => isp1760}/isp1760-core.h | 0 drivers/usb/{host => isp1760}/isp1760-hcd.c | 0 drivers/usb/{host => isp1760}/isp1760-hcd.h | 0 drivers/usb/{host => isp1760}/isp1760-if.c | 0 drivers/usb/{host => isp1760}/isp1760-regs.h | 0 drivers/usb/{host => isp1760}/isp1760-udc.c | 0 drivers/usb/{host => isp1760}/isp1760-udc.h | 0 15 files changed, 29 insertions(+), 26 deletions(-) create mode 100644 drivers/usb/isp1760/Kconfig create mode 100644 drivers/usb/isp1760/Makefile rename drivers/usb/{host => isp1760}/isp1760-core.c (100%) rename drivers/usb/{host => isp1760}/isp1760-core.h (100%) rename drivers/usb/{host => isp1760}/isp1760-hcd.c (100%) rename drivers/usb/{host => isp1760}/isp1760-hcd.h (100%) rename drivers/usb/{host => isp1760}/isp1760-if.c (100%) rename drivers/usb/{host => isp1760}/isp1760-regs.h (100%) rename drivers/usb/{host => isp1760}/isp1760-udc.c (100%) rename drivers/usb/{host => isp1760}/isp1760-udc.h (100%) diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index ae481c37a208..8ed451dd651e 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -104,6 +104,8 @@ source "drivers/usb/dwc2/Kconfig" source "drivers/usb/chipidea/Kconfig" +source "drivers/usb/isp1760/Kconfig" + comment "USB port drivers" if USB diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index d7be71778059..2f1e2aa42b44 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_USB) += core/ obj-$(CONFIG_USB_DWC3) += dwc3/ obj-$(CONFIG_USB_DWC2) += dwc2/ +obj-$(CONFIG_USB_ISP1760) += isp1760/ obj-$(CONFIG_USB_MON) += mon/ @@ -23,7 +24,6 @@ obj-$(CONFIG_USB_ISP1362_HCD) += host/ obj-$(CONFIG_USB_U132_HCD) += host/ obj-$(CONFIG_USB_R8A66597_HCD) += host/ obj-$(CONFIG_USB_HWA_HCD) += host/ -obj-$(CONFIG_USB_ISP1760_HCD) += host/ obj-$(CONFIG_USB_IMX21_HCD) += host/ obj-$(CONFIG_USB_FSL_MPH_DR_OF) += host/ obj-$(CONFIG_USB_FUSBH200_HCD) += host/ diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig index c9152e260fd4..b8e213eb36cc 100644 --- a/drivers/usb/gadget/udc/Kconfig +++ b/drivers/usb/gadget/udc/Kconfig @@ -109,13 +109,6 @@ config USB_GR_UDC Select this to support Aeroflex Gaisler GRUSBDC cores from the GRLIB VHDL IP core library. -config USB_ISP1761_UDC - boolean "NXP ISP1761 USB Device Controller" - depends on USB_ISP1760_HCD - help - The NXP ISP1761 is a dual-role high-speed USB host and device - controller. - config USB_OMAP tristate "OMAP USB Device Controller" depends on ARCH_OMAP1 diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index fafc628480e0..3de291b6ac04 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -331,20 +331,6 @@ config USB_ISP116X_HCD To compile this driver as a module, choose M here: the module will be called isp116x-hcd. -config USB_ISP1760_HCD - tristate "ISP 1760 HCD support" - ---help--- - The ISP1760 chip is a USB 2.0 host controller. - - This driver does not support isochronous transfers or OTG. - This USB controller is usually attached to a non-DMA-Master - capable bus. NXP's eval kit brings this chip on PCI card - where the chip itself is behind a PLB to simulate such - a bus. - - To compile this driver as a module, choose M here: the - module will be called isp1760. - config USB_ISP1362_HCD tristate "ISP1362 HCD support" ---help--- diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 67d3f1843857..65b0b6a58599 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -5,9 +5,6 @@ # tell define_trace.h where to find the xhci trace header CFLAGS_xhci-trace.o := -I$(src) -isp1760-y := isp1760-core.o isp1760-hcd.o isp1760-if.o -isp1760-$(CONFIG_USB_ISP1761_UDC) += isp1760-udc.o - fhci-y := fhci-hcd.o fhci-hub.o fhci-q.o fhci-y += fhci-mem.o fhci-tds.o fhci-sched.o @@ -70,7 +67,6 @@ obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o obj-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o -obj-$(CONFIG_USB_ISP1760_HCD) += isp1760.o obj-$(CONFIG_USB_HWA_HCD) += hwa-hc.o obj-$(CONFIG_USB_IMX21_HCD) += imx21-hcd.o obj-$(CONFIG_USB_FSL_MPH_DR_OF) += fsl-mph-dr-of.o diff --git a/drivers/usb/isp1760/Kconfig b/drivers/usb/isp1760/Kconfig new file mode 100644 index 000000000000..c09ab8fa0e10 --- /dev/null +++ b/drivers/usb/isp1760/Kconfig @@ -0,0 +1,22 @@ +config USB_ISP1760 + tristate "NXP ISP 1760/1761 support" + depends on USB + help + Say Y or M here if your system as an ISP1760 USB host controller + or an ISP1761 USB dual-role controller. + + This driver does not support isochronous transfers or OTG. + This USB controller is usually attached to a non-DMA-Master + capable bus. NXP's eval kit brings this chip on PCI card + where the chip itself is behind a PLB to simulate such + a bus. + + To compile this driver as a module, choose M here: the + module will be called isp1760. + +config USB_ISP1761_UDC + boolean "NXP ISP1761 USB Device Controller" + depends on USB_ISP1760 && USB_GADGET + help + The NXP ISP1761 is a dual-role high-speed USB host and device + controller. diff --git a/drivers/usb/isp1760/Makefile b/drivers/usb/isp1760/Makefile new file mode 100644 index 000000000000..698ccb0b2c65 --- /dev/null +++ b/drivers/usb/isp1760/Makefile @@ -0,0 +1,4 @@ +isp1760-y := isp1760-core.o isp1760-hcd.o isp1760-if.o +isp1760-$(CONFIG_USB_ISP1761_UDC) += isp1760-udc.o + +obj-$(CONFIG_USB_ISP1760) += isp1760.o diff --git a/drivers/usb/host/isp1760-core.c b/drivers/usb/isp1760/isp1760-core.c similarity index 100% rename from drivers/usb/host/isp1760-core.c rename to drivers/usb/isp1760/isp1760-core.c diff --git a/drivers/usb/host/isp1760-core.h b/drivers/usb/isp1760/isp1760-core.h similarity index 100% rename from drivers/usb/host/isp1760-core.h rename to drivers/usb/isp1760/isp1760-core.h diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/isp1760/isp1760-hcd.c similarity index 100% rename from drivers/usb/host/isp1760-hcd.c rename to drivers/usb/isp1760/isp1760-hcd.c diff --git a/drivers/usb/host/isp1760-hcd.h b/drivers/usb/isp1760/isp1760-hcd.h similarity index 100% rename from drivers/usb/host/isp1760-hcd.h rename to drivers/usb/isp1760/isp1760-hcd.h diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/isp1760/isp1760-if.c similarity index 100% rename from drivers/usb/host/isp1760-if.c rename to drivers/usb/isp1760/isp1760-if.c diff --git a/drivers/usb/host/isp1760-regs.h b/drivers/usb/isp1760/isp1760-regs.h similarity index 100% rename from drivers/usb/host/isp1760-regs.h rename to drivers/usb/isp1760/isp1760-regs.h diff --git a/drivers/usb/host/isp1760-udc.c b/drivers/usb/isp1760/isp1760-udc.c similarity index 100% rename from drivers/usb/host/isp1760-udc.c rename to drivers/usb/isp1760/isp1760-udc.c diff --git a/drivers/usb/host/isp1760-udc.h b/drivers/usb/isp1760/isp1760-udc.h similarity index 100% rename from drivers/usb/host/isp1760-udc.h rename to drivers/usb/isp1760/isp1760-udc.h From 100832abf065bc186ae48165c16546784b90a4be Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Jan 2015 00:56:03 +0200 Subject: [PATCH 158/214] usb: isp1760: Make HCD support optional Enable compilation of the isp1760 driver in pure host mode, pure device mode, or dual-role mode. Signed-off-by: Laurent Pinchart Signed-off-by: Felipe Balbi --- drivers/usb/isp1760/Kconfig | 47 +++++++++++++++++++++++++++---- drivers/usb/isp1760/Makefile | 3 +- drivers/usb/isp1760/isp1760-hcd.h | 25 ++++++++++++++++ drivers/usb/isp1760/isp1760-udc.h | 4 +-- 4 files changed, 71 insertions(+), 8 deletions(-) diff --git a/drivers/usb/isp1760/Kconfig b/drivers/usb/isp1760/Kconfig index c09ab8fa0e10..c94b7d953399 100644 --- a/drivers/usb/isp1760/Kconfig +++ b/drivers/usb/isp1760/Kconfig @@ -1,6 +1,6 @@ config USB_ISP1760 tristate "NXP ISP 1760/1761 support" - depends on USB + depends on USB || USB_GADGET help Say Y or M here if your system as an ISP1760 USB host controller or an ISP1761 USB dual-role controller. @@ -14,9 +14,46 @@ config USB_ISP1760 To compile this driver as a module, choose M here: the module will be called isp1760. +config USB_ISP1760_HCD + bool + config USB_ISP1761_UDC - boolean "NXP ISP1761 USB Device Controller" - depends on USB_ISP1760 && USB_GADGET + bool + +if USB_ISP1760 + +choice + bool "ISP1760 Mode Selection" + default USB_ISP1760_DUAL_ROLE if (USB && USB_GADGET) + default USB_ISP1760_HOST_ROLE if (USB && !USB_GADGET) + default USB_ISP1760_GADGET_ROLE if (!USB && USB_GADGET) + +config USB_ISP1760_HOST_ROLE + bool "Host only mode" + depends on USB=y || USB=USB_ISP1760 + select USB_ISP1760_HCD help - The NXP ISP1761 is a dual-role high-speed USB host and device - controller. + Select this if you want to use the ISP1760 in host mode only. The + gadget function will be disabled. + +config USB_ISP1760_GADGET_ROLE + bool "Gadget only mode" + depends on USB_GADGET=y || USB_GADGET=USB_ISP1760 + select USB_ISP1761_UDC + help + Select this if you want to use the ISP1760 in peripheral mode only. + The host function will be disabled. + +config USB_ISP1760_DUAL_ROLE + bool "Dual Role mode" + depends on USB=y || USB=USB_ISP1760 + depends on USB_GADGET=y || USB_GADGET=USB_ISP1760 + select USB_ISP1760_HCD + select USB_ISP1761_UDC + help + Select this if you want to use the ISP1760 in both host and + peripheral modes. + +endchoice + +endif diff --git a/drivers/usb/isp1760/Makefile b/drivers/usb/isp1760/Makefile index 698ccb0b2c65..2b741074ad2b 100644 --- a/drivers/usb/isp1760/Makefile +++ b/drivers/usb/isp1760/Makefile @@ -1,4 +1,5 @@ -isp1760-y := isp1760-core.o isp1760-hcd.o isp1760-if.o +isp1760-y := isp1760-core.o isp1760-if.o +isp1760-$(CONFIG_USB_ISP1760_HCD) += isp1760-hcd.o isp1760-$(CONFIG_USB_ISP1761_UDC) += isp1760-udc.o obj-$(CONFIG_USB_ISP1760) += isp1760.o diff --git a/drivers/usb/isp1760/isp1760-hcd.h b/drivers/usb/isp1760/isp1760-hcd.h index df7ea3684b77..0c1c98d6ea08 100644 --- a/drivers/usb/isp1760/isp1760-hcd.h +++ b/drivers/usb/isp1760/isp1760-hcd.h @@ -47,6 +47,7 @@ enum isp1760_queue_head_types { }; struct isp1760_hcd { +#ifdef CONFIG_USB_ISP1760_HCD struct usb_hcd *hcd; u32 hcs_params; @@ -64,8 +65,10 @@ struct isp1760_hcd { unsigned i_thresh; unsigned long reset_done; unsigned long next_statechange; +#endif }; +#ifdef CONFIG_USB_ISP1760_HCD int isp1760_hcd_register(struct isp1760_hcd *priv, void __iomem *regs, struct resource *mem, int irq, unsigned long irqflags, struct device *dev); @@ -73,5 +76,27 @@ void isp1760_hcd_unregister(struct isp1760_hcd *priv); int isp1760_init_kmem_once(void); void isp1760_deinit_kmem_cache(void); +#else +static inline int isp1760_hcd_register(struct isp1760_hcd *priv, + void __iomem *regs, struct resource *mem, + int irq, unsigned long irqflags, + struct device *dev) +{ + return 0; +} + +static inline void isp1760_hcd_unregister(struct isp1760_hcd *priv) +{ +} + +static inline int isp1760_init_kmem_once(void) +{ + return 0; +} + +static inline void isp1760_deinit_kmem_cache(void) +{ +} +#endif #endif /* _ISP1760_HCD_H_ */ diff --git a/drivers/usb/isp1760/isp1760-udc.h b/drivers/usb/isp1760/isp1760-udc.h index 4af6ba6eda86..26899ed81145 100644 --- a/drivers/usb/isp1760/isp1760-udc.h +++ b/drivers/usb/isp1760/isp1760-udc.h @@ -62,7 +62,7 @@ struct isp1760_ep { * connected: Tracks gadget driver bus connection state */ struct isp1760_udc { -#if CONFIG_USB_ISP1761_UDC +#ifdef CONFIG_USB_ISP1761_UDC struct isp1760_device *isp; int irq; @@ -87,7 +87,7 @@ struct isp1760_udc { #endif }; -#if CONFIG_USB_ISP1761_UDC +#ifdef CONFIG_USB_ISP1761_UDC int isp1760_udc_register(struct isp1760_device *isp, int irq, unsigned long irqflags); void isp1760_udc_unregister(struct isp1760_device *isp); From 1f8d9b9b503070e5450f49e0a341ac3b43d9164d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Jan 2015 00:56:04 +0200 Subject: [PATCH 159/214] usb: isp1760: Remove duplicate usb_disabled() check Both isp1760_register() and isp1761_pci_probe() check whether USB is disabled by calling usb_disabled(), and bail out with an error if it is. One check is enough, remove the PCI-specific check. Signed-off-by: Laurent Pinchart Signed-off-by: Felipe Balbi --- drivers/usb/isp1760/isp1760-if.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/usb/isp1760/isp1760-if.c b/drivers/usb/isp1760/isp1760-if.c index c2a94c966350..264be4d21706 100644 --- a/drivers/usb/isp1760/isp1760-if.c +++ b/drivers/usb/isp1760/isp1760-if.c @@ -126,9 +126,6 @@ static int isp1761_pci_probe(struct pci_dev *dev, unsigned int devflags = 0; int ret; - if (usb_disabled()) - return -ENODEV; - if (!dev->irq) return -ENODEV; From d21daf1e90514cee8e3fb11c8e28acee3fb87edf Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 Jan 2015 00:56:05 +0200 Subject: [PATCH 160/214] usb: isp1760: Fix USB disabled check The isp1760 driver registration function returns an error if USB is disabled. This made sense when the driver only supported host controllers, but now that it supports peripheral controllers host support isn't mandatory anymore. Fix this by returning an error only when both the HCD and UDC functions are disabled, either through the kernel configuration or at runtime. Signed-off-by: Laurent Pinchart Signed-off-by: Felipe Balbi --- drivers/usb/isp1760/isp1760-core.c | 24 +++++++++++++++--------- drivers/usb/isp1760/isp1760-hcd.c | 3 +++ drivers/usb/isp1760/isp1760-udc.c | 3 +++ 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/drivers/usb/isp1760/isp1760-core.c b/drivers/usb/isp1760/isp1760-core.c index 727e90ad15bd..b9827556455f 100644 --- a/drivers/usb/isp1760/isp1760-core.c +++ b/drivers/usb/isp1760/isp1760-core.c @@ -112,9 +112,15 @@ int isp1760_register(struct resource *mem, int irq, unsigned long irqflags, struct device *dev, unsigned int devflags) { struct isp1760_device *isp; + bool udc_disabled = !(devflags & ISP1760_FLAG_ISP1761); int ret; - if (usb_disabled()) + /* + * If neither the HCD not the UDC is enabled return an error, as no + * device would be registered. + */ + if ((!IS_ENABLED(CONFIG_USB_ISP1760_HCD) || usb_disabled()) && + (!IS_ENABLED(CONFIG_USB_ISP1761_UDC) || udc_disabled)) return -ENODEV; /* prevent usb-core allocating DMA pages */ @@ -137,12 +143,14 @@ int isp1760_register(struct resource *mem, int irq, unsigned long irqflags, isp1760_init_core(isp); - ret = isp1760_hcd_register(&isp->hcd, isp->regs, mem, irq, - irqflags | IRQF_SHARED, dev); - if (ret < 0) - return ret; + if (IS_ENABLED(CONFIG_USB_ISP1760_HCD) && !usb_disabled()) { + ret = isp1760_hcd_register(&isp->hcd, isp->regs, mem, irq, + irqflags | IRQF_SHARED, dev); + if (ret < 0) + return ret; + } - if (devflags & ISP1760_FLAG_ISP1761) { + if (IS_ENABLED(CONFIG_USB_ISP1761_UDC) && !udc_disabled) { ret = isp1760_udc_register(isp, irq, irqflags | IRQF_SHARED | IRQF_DISABLED); if (ret < 0) { @@ -160,9 +168,7 @@ void isp1760_unregister(struct device *dev) { struct isp1760_device *isp = dev_get_drvdata(dev); - if (isp->devflags & ISP1760_FLAG_ISP1761) - isp1760_udc_unregister(isp); - + isp1760_udc_unregister(isp); isp1760_hcd_unregister(&isp->hcd); } diff --git a/drivers/usb/isp1760/isp1760-hcd.c b/drivers/usb/isp1760/isp1760-hcd.c index 568446c9ce8d..996b2c157d12 100644 --- a/drivers/usb/isp1760/isp1760-hcd.c +++ b/drivers/usb/isp1760/isp1760-hcd.c @@ -2226,6 +2226,9 @@ error: void isp1760_hcd_unregister(struct isp1760_hcd *priv) { + if (!priv->hcd) + return; + usb_remove_hcd(priv->hcd); usb_put_hcd(priv->hcd); } diff --git a/drivers/usb/isp1760/isp1760-udc.c b/drivers/usb/isp1760/isp1760-udc.c index 6bfda3082807..9612d7990565 100644 --- a/drivers/usb/isp1760/isp1760-udc.c +++ b/drivers/usb/isp1760/isp1760-udc.c @@ -1488,6 +1488,9 @@ void isp1760_udc_unregister(struct isp1760_device *isp) { struct isp1760_udc *udc = &isp->udc; + if (!udc->isp) + return; + usb_del_gadget_udc(&udc->gadget); free_irq(udc->irq, udc); From dd811ba7427f7e6b7e521f3738a5313d00319675 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Fri, 16 Jan 2015 18:28:58 +0800 Subject: [PATCH 161/214] usb: phy: mxs: don't need IP fix for imx6sx The RLT code has already done it, so no software operation is needed. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-mxs-usb.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index 58cae78b12a4..fcadbd2d65a8 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -131,8 +131,7 @@ static const struct mxs_phy_data vf610_phy_data = { }; static const struct mxs_phy_data imx6sx_phy_data = { - .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS | - MXS_PHY_NEED_IP_FIX, + .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS, }; static const struct of_device_id mxs_phy_dt_ids[] = { From 7b09e67639d6857439bae0d6276d443a7c2c9c40 Mon Sep 17 00:00:00 2001 From: Li Jun Date: Fri, 16 Jan 2015 18:28:59 +0800 Subject: [PATCH 162/214] usb: phy: mxs: refine mxs_phy_disconnect_line For non-otg mode, we keep the usage of disconnect line between phy analog and digital unchanging; for otg mode, at peripheral role, we keep the usage unchanging too, at host role, the digital part needs to know dp/dm change to respond device's data pulse when it is at low power mode. Signed-off-by: Peter Chen Signed-off-by: Li Jun Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-mxs-usb.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index fcadbd2d65a8..eb746051eda4 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -40,6 +40,7 @@ #define BM_USBPHY_CTRL_SFTRST BIT(31) #define BM_USBPHY_CTRL_CLKGATE BIT(30) +#define BM_USBPHY_CTRL_OTG_ID_VALUE BIT(27) #define BM_USBPHY_CTRL_ENAUTOSET_USBCLKS BIT(26) #define BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE BIT(25) #define BM_USBPHY_CTRL_ENVBUSCHG_WKUP BIT(23) @@ -255,6 +256,18 @@ static void __mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool disconnect) usleep_range(500, 1000); } +static bool mxs_phy_is_otg_host(struct mxs_phy *mxs_phy) +{ + void __iomem *base = mxs_phy->phy.io_priv; + u32 phyctrl = readl(base + HW_USBPHY_CTRL); + + if (IS_ENABLED(CONFIG_USB_OTG) && + !(phyctrl & BM_USBPHY_CTRL_OTG_ID_VALUE)) + return true; + + return false; +} + static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on) { bool vbus_is_on = false; @@ -269,7 +282,7 @@ static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on) vbus_is_on = mxs_phy_get_vbus_status(mxs_phy); - if (on && !vbus_is_on) + if (on && !vbus_is_on && !mxs_phy_is_otg_host(mxs_phy)) __mxs_phy_disconnect_line(mxs_phy, true); else __mxs_phy_disconnect_line(mxs_phy, false); From efdbd3a5d6e6108f1565ab4dc4c53e77aba6fe0a Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Fri, 16 Jan 2015 18:29:00 +0800 Subject: [PATCH 163/214] usb: phy: mxs: do not set PWD.RXPWD1PT1 for low speed connection At very rare cases, the SoF will not send out after resume with low speed connection. The workaround is do not power down PWD.RXPWD1PT1 bit during the suspend. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-mxs-usb.c | 47 ++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index eb746051eda4..c3177a1757ee 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -70,6 +70,9 @@ #define ANADIG_USB2_LOOPBACK_SET 0x244 #define ANADIG_USB2_LOOPBACK_CLR 0x248 +#define ANADIG_USB1_MISC 0x1f0 +#define ANADIG_USB2_MISC 0x250 + #define BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG BIT(12) #define BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG_SL BIT(11) @@ -81,6 +84,11 @@ #define BM_ANADIG_USB2_LOOPBACK_UTMI_DIG_TST1 BIT(2) #define BM_ANADIG_USB2_LOOPBACK_TSTI_TX_EN BIT(5) +#define BM_ANADIG_USB1_MISC_RX_VPIN_FS BIT(29) +#define BM_ANADIG_USB1_MISC_RX_VMIN_FS BIT(28) +#define BM_ANADIG_USB2_MISC_RX_VPIN_FS BIT(29) +#define BM_ANADIG_USB2_MISC_RX_VMIN_FS BIT(28) + #define to_mxs_phy(p) container_of((p), struct mxs_phy, phy) /* Do disconnection between PHY and controller without vbus */ @@ -323,13 +331,50 @@ static void mxs_phy_shutdown(struct usb_phy *phy) clk_disable_unprepare(mxs_phy->clk); } +static bool mxs_phy_is_low_speed_connection(struct mxs_phy *mxs_phy) +{ + unsigned int line_state; + /* bit definition is the same for all controllers */ + unsigned int dp_bit = BM_ANADIG_USB1_MISC_RX_VPIN_FS, + dm_bit = BM_ANADIG_USB1_MISC_RX_VMIN_FS; + unsigned int reg = ANADIG_USB1_MISC; + + /* If the SoCs don't have anatop, quit */ + if (!mxs_phy->regmap_anatop) + return false; + + if (mxs_phy->port_id == 0) + reg = ANADIG_USB1_MISC; + else if (mxs_phy->port_id == 1) + reg = ANADIG_USB2_MISC; + + regmap_read(mxs_phy->regmap_anatop, reg, &line_state); + + if ((line_state & (dp_bit | dm_bit)) == dm_bit) + return true; + else + return false; +} + static int mxs_phy_suspend(struct usb_phy *x, int suspend) { int ret; struct mxs_phy *mxs_phy = to_mxs_phy(x); + bool low_speed_connection, vbus_is_on; + + low_speed_connection = mxs_phy_is_low_speed_connection(mxs_phy); + vbus_is_on = mxs_phy_get_vbus_status(mxs_phy); if (suspend) { - writel(0xffffffff, x->io_priv + HW_USBPHY_PWD); + /* + * FIXME: Do not power down RXPWD1PT1 bit for low speed + * connect. The low speed connection will have problem at + * very rare cases during usb suspend and resume process. + */ + if (low_speed_connection & vbus_is_on) + writel(0xfffbffff, x->io_priv + HW_USBPHY_PWD); + else + writel(0xffffffff, x->io_priv + HW_USBPHY_PWD); writel(BM_USBPHY_CTRL_CLKGATE, x->io_priv + HW_USBPHY_CTRL_SET); clk_disable_unprepare(mxs_phy->clk); From e235f7b86f33beea7e096b46db1802dbf5d7d22e Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Fri, 16 Jan 2015 18:29:01 +0800 Subject: [PATCH 164/214] usb: phy: mxs: add delay before set phyctrl.clkgate There is a request from IC engineer that if we doesn't set phypwd as 0xffffffff, we need to delay about five 32Khz cycles before set phy's pwd register, otherwise, the wakeup signal may can't wake up controller. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-mxs-usb.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index c3177a1757ee..8f7cb068d29b 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -371,10 +371,16 @@ static int mxs_phy_suspend(struct usb_phy *x, int suspend) * connect. The low speed connection will have problem at * very rare cases during usb suspend and resume process. */ - if (low_speed_connection & vbus_is_on) - writel(0xfffbffff, x->io_priv + HW_USBPHY_PWD); - else + if (low_speed_connection & vbus_is_on) { + /* + * If value to be set as pwd value is not 0xffffffff, + * several 32Khz cycles are needed. + */ + mxs_phy_clock_switch_delay(); + writel(0xffbfffff, x->io_priv + HW_USBPHY_PWD); + } else { writel(0xffffffff, x->io_priv + HW_USBPHY_PWD); + } writel(BM_USBPHY_CTRL_CLKGATE, x->io_priv + HW_USBPHY_CTRL_SET); clk_disable_unprepare(mxs_phy->clk); From 727968357eb84497ac8e2514cf06be3d8779d24f Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Mon, 19 Jan 2015 13:52:57 +0100 Subject: [PATCH 165/214] usb: gadget: uvc: use explicit type instead of void * The first parameter of __uvcg_iter_strm_cls() is always used in the context of struct uvcg_streaming_header, so change the function prototype accordingly. Acked-by: Laurent Pinchart Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/uvc_configfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index 49f25e806e38..51d8e9ee5323 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -1918,10 +1918,10 @@ enum uvcg_strm_type { UVCG_FRAME }; -static int __uvcg_iter_strm_cls(void *priv1, void *priv2, void *priv3, +static int __uvcg_iter_strm_cls(struct uvcg_streaming_header *h, + void *priv2, void *priv3, int (*fun)(void *, void *, void *, int, enum uvcg_strm_type type)) { - struct uvcg_streaming_header *h = priv1; struct uvcg_format_ptr *f; struct config_group *grp; struct config_item *item; From 578d0b6b6171e7a7afa49e74f87c683297e9cae9 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Mon, 19 Jan 2015 13:52:58 +0100 Subject: [PATCH 166/214] usb: gadget: uvc: comments for iterating over streaming hierarchy The purpose of the functions and their parametrs might not be obvious to the reader, so explain it. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/uvc_configfs.c | 34 ++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index 51d8e9ee5323..3c0467bcb14f 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -1918,6 +1918,25 @@ enum uvcg_strm_type { UVCG_FRAME }; +/* + * Iterate over a hierarchy of streaming descriptors' config items. + * The items are created by the user with configfs. + * + * It "processes" the header pointed to by @priv1, then for each format + * that follows the header "processes" the format itself and then for + * each frame inside a format "processes" the frame. + * + * As a "processing" function the @fun is used. + * + * __uvcg_iter_strm_cls() is used in two context: first, to calculate + * the amount of memory needed for an array of streaming descriptors + * and second, to actually fill the array. + * + * @h: streaming header pointer + * @priv2: an "inout" parameter (the caller might want to see the changes to it) + * @priv3: an "inout" parameter (the caller might want to see the changes to it) + * @fun: callback function for processing each level of the hierarchy + */ static int __uvcg_iter_strm_cls(struct uvcg_streaming_header *h, void *priv2, void *priv3, int (*fun)(void *, void *, void *, int, enum uvcg_strm_type type)) @@ -1951,6 +1970,14 @@ static int __uvcg_iter_strm_cls(struct uvcg_streaming_header *h, return ret; } +/* + * Count how many bytes are needed for an array of streaming descriptors. + * + * @priv1: pointer to a header, format or frame + * @priv2: inout parameter, accumulated size of the array + * @priv3: inout parameter, accumulated number of the array elements + * @n: unused, this function's prototype must match @fun in __uvcg_iter_strm_cls + */ static int __uvcg_cnt_strm(void *priv1, void *priv2, void *priv3, int n, enum uvcg_strm_type type) { @@ -2000,6 +2027,13 @@ static int __uvcg_cnt_strm(void *priv1, void *priv2, void *priv3, int n, return 0; } +/* + * Fill an array of streaming descriptors. + * + * @priv1: pointer to a header, format or frame + * @priv2: inout parameter, pointer into a block of memory + * @priv3: inout parameter, pointer to a 2-dimensional array + */ static int __uvcg_fill_strm(void *priv1, void *priv2, void *priv3, int n, enum uvcg_strm_type type) { From 4cd8f6d05599be8bd2052df28c805f7c2f8ff479 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 27 Jan 2015 13:24:26 -0600 Subject: [PATCH 167/214] usb: dwc3: gadget: avoid variable shadowing We already have both ret and dwc defined in this same function. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 6c5e344822b9..944036d4c83d 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1110,15 +1110,10 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) * handled. */ if (dep->stream_capable) { - int ret; - ret = __dwc3_gadget_kick_transfer(dep, 0, true); - if (ret && ret != -EBUSY) { - struct dwc3 *dwc = dep->dwc; - + if (ret && ret != -EBUSY) dev_dbg(dwc->dev, "%s: failed to kick transfers\n", dep->name); - } } return 0; From e9f2aa871c732acfe29c3ba5e6b541b2aa786778 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 27 Jan 2015 13:49:28 -0600 Subject: [PATCH 168/214] usb: dwc3: gadget: WARN() in case of unknown IRQ if an unknown IRQ event is triggered, that means the HW is really misbehaving. Instead of printing a debug message, let's WARN() so users report when that happens. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 944036d4c83d..c6eec261a304 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2518,7 +2518,7 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc, dev_vdbg(dwc->dev, "Overflow\n"); break; default: - dev_dbg(dwc->dev, "UNKNOWN IRQ %d\n", event->type); + dev_WARN(dwc->dev, "UNKNOWN IRQ %d\n", event->type); } } From 6bac4ff0a52262faea4649dc8142962767f88b82 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 27 Jan 2015 13:47:02 -0600 Subject: [PATCH 169/214] usb: dwc3: trace: add trace logs for core and gadget Sometimes we want to just print a formatted string without passing any extra data. The following will be used for removing reliance on dev_vdbg() from dwc3. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/trace.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h index 9fc20b33dd8e..9c10669ab91f 100644 --- a/drivers/usb/dwc3/trace.h +++ b/drivers/usb/dwc3/trace.h @@ -47,6 +47,16 @@ DEFINE_EVENT(dwc3_log_msg, dwc3_writel, TP_ARGS(vaf) ); +DEFINE_EVENT(dwc3_log_msg, dwc3_gadget, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +DEFINE_EVENT(dwc3_log_msg, dwc3_core, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + DEFINE_EVENT(dwc3_log_msg, dwc3_ep0, TP_PROTO(struct va_format *vaf), TP_ARGS(vaf) From 73815280a5af8d95e818eb25f4091e86497dfa4b Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 27 Jan 2015 13:48:14 -0600 Subject: [PATCH 170/214] usb: dwc3: remove reliance on dev_vdbg() By moving all dev_vdbg() to tracepoints, we can finally get rid of dev_vdbg() usage from dwc3. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/Kconfig | 6 ---- drivers/usb/dwc3/Makefile | 1 - drivers/usb/dwc3/core.c | 2 +- drivers/usb/dwc3/gadget.c | 65 +++++++++++++++++++++++---------------- 4 files changed, 39 insertions(+), 35 deletions(-) diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 58b5b2cde4c5..edbf9c85af7e 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -104,12 +104,6 @@ config USB_DWC3_DEBUG help Say Y here to enable debugging messages on DWC3 Driver. -config USB_DWC3_VERBOSE - bool "Enable Verbose Debugging Messages" - depends on USB_DWC3_DEBUG - help - Say Y here to enable verbose debugging messages on DWC3 Driver. - config DWC3_HOST_USB3_LPM_ENABLE bool "Enable USB3 LPM Capability" depends on USB_DWC3_HOST=y || USB_DWC3_DUAL_ROLE=y diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index bb34fbcfeab3..46172f47f02d 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -2,7 +2,6 @@ CFLAGS_trace.o := -I$(src) ccflags-$(CONFIG_USB_DWC3_DEBUG) := -DDEBUG -ccflags-$(CONFIG_USB_DWC3_VERBOSE) += -DVERBOSE_DEBUG obj-$(CONFIG_USB_DWC3) += dwc3.o diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 25ddc39efad8..9f0e209b8f6c 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -345,7 +345,7 @@ static void dwc3_core_num_eps(struct dwc3 *dwc) dwc->num_in_eps = DWC3_NUM_IN_EPS(parms); dwc->num_out_eps = DWC3_NUM_EPS(parms) - dwc->num_in_eps; - dev_vdbg(dwc->dev, "found %d IN and %d OUT endpoints\n", + dwc3_trace(trace_dwc3_core, "found %d IN and %d OUT endpoints", dwc->num_in_eps, dwc->num_out_eps); } diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index c6eec261a304..7cce00e7102b 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -139,7 +139,8 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state) udelay(5); } - dev_vdbg(dwc->dev, "link state change request timed out\n"); + dwc3_trace(trace_dwc3_gadget, + "link state change request timed out"); return -ETIMEDOUT; } @@ -219,7 +220,7 @@ int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc) fifo_size |= (last_fifo_depth << 16); - dev_vdbg(dwc->dev, "%s: Fifo Addr %04x Size %d\n", + dwc3_trace(trace_dwc3_gadget, "%s: Fifo Addr %04x Size %d", dep->name, last_fifo_depth, fifo_size & 0xffff); dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num), fifo_size); @@ -287,7 +288,8 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param) do { reg = dwc3_readl(dwc->regs, DWC3_DGCMD); if (!(reg & DWC3_DGCMD_CMDACT)) { - dev_vdbg(dwc->dev, "Command Complete --> %d\n", + dwc3_trace(trace_dwc3_gadget, + "Command Complete --> %d", DWC3_DGCMD_STATUS(reg)); return 0; } @@ -297,8 +299,11 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param) * interrupt context. */ timeout--; - if (!timeout) + if (!timeout) { + dwc3_trace(trace_dwc3_gadget, + "Command Timed Out"); return -ETIMEDOUT; + } udelay(1); } while (1); } @@ -320,7 +325,8 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, do { reg = dwc3_readl(dwc->regs, DWC3_DEPCMD(ep)); if (!(reg & DWC3_DEPCMD_CMDACT)) { - dev_vdbg(dwc->dev, "Command Complete --> %d\n", + dwc3_trace(trace_dwc3_gadget, + "Command Complete --> %d", DWC3_DEPCMD_STATUS(reg)); return 0; } @@ -330,8 +336,11 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, * interrupt context. */ timeout--; - if (!timeout) + if (!timeout) { + dwc3_trace(trace_dwc3_gadget, + "Command Timed Out"); return -ETIMEDOUT; + } udelay(1); } while (1); @@ -489,7 +498,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, u32 reg; int ret; - dev_vdbg(dwc->dev, "Enabling %s\n", dep->name); + dwc3_trace(trace_dwc3_gadget, "Enabling %s", dep->name); if (!(dep->flags & DWC3_EP_ENABLED)) { ret = dwc3_gadget_start_config(dwc, dep); @@ -726,10 +735,9 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_request *req, dma_addr_t dma, unsigned length, unsigned last, unsigned chain, unsigned node) { - struct dwc3 *dwc = dep->dwc; struct dwc3_trb *trb; - dev_vdbg(dwc->dev, "%s: req %p dma %08llx length %d%s%s\n", + dwc3_trace(trace_dwc3_gadget, "%s: req %p dma %08llx length %d%s%s", dep->name, req, (unsigned long long) dma, length, last ? " last" : "", chain ? " chain" : ""); @@ -931,7 +939,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param, u32 cmd; if (start_new && (dep->flags & DWC3_EP_BUSY)) { - dev_vdbg(dwc->dev, "%s: endpoint busy\n", dep->name); + dwc3_trace(trace_dwc3_gadget, "%s: endpoint busy", dep->name); return -EBUSY; } dep->flags &= ~DWC3_EP_PENDING_REQUEST; @@ -1002,8 +1010,9 @@ static void __dwc3_gadget_start_isoc(struct dwc3 *dwc, u32 uf; if (list_empty(&dep->request_list)) { - dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n", - dep->name); + dwc3_trace(trace_dwc3_gadget, + "ISOC ep %s run out for requests", + dep->name); dep->flags |= DWC3_EP_PENDING_REQUEST; return; } @@ -1144,8 +1153,6 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request, goto out; } - dev_vdbg(dwc->dev, "queing request %p to %s length %d\n", - request, ep->name, request->length); trace_dwc3_ep_queue(req); ret = __dwc3_gadget_ep_queue(dep, req); @@ -1460,7 +1467,7 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend) udelay(1); } while (1); - dev_vdbg(dwc->dev, "gadget %s data soft-%s\n", + dwc3_trace(trace_dwc3_gadget, "gadget %s data soft-%s", dwc->gadget_driver ? dwc->gadget_driver->function : "no-function", is_on ? "connect" : "disconnect"); @@ -1680,7 +1687,7 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc, dep->endpoint.name = dep->name; - dev_vdbg(dwc->dev, "initializing %s\n", dep->name); + dwc3_trace(trace_dwc3_gadget, "initializing %s", dep->name); if (epnum == 0 || epnum == 1) { usb_ep_set_maxpacket_limit(&dep->endpoint, 512); @@ -1717,13 +1724,15 @@ static int dwc3_gadget_init_endpoints(struct dwc3 *dwc) ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_out_eps, 0); if (ret < 0) { - dev_vdbg(dwc->dev, "failed to allocate OUT endpoints\n"); + dwc3_trace(trace_dwc3_gadget, + "failed to allocate OUT endpoints"); return ret; } ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_in_eps, 1); if (ret < 0) { - dev_vdbg(dwc->dev, "failed to allocate IN endpoints\n"); + dwc3_trace(trace_dwc3_gadget, + "failed to allocate IN endpoints"); return ret; } @@ -1969,7 +1978,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, } else { int ret; - dev_vdbg(dwc->dev, "%s: reason %s\n", + dwc3_trace(trace_dwc3_gadget, "%s: reason %s", dep->name, event->status & DEPEVT_STATUS_TRANSFER_ACTIVE ? "Transfer Active" @@ -1993,7 +2002,8 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, switch (event->status) { case DEPEVT_STREAMEVT_FOUND: - dev_vdbg(dwc->dev, "Stream %d found and started\n", + dwc3_trace(trace_dwc3_gadget, + "Stream %d found and started", event->parameters); break; @@ -2007,7 +2017,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, dev_dbg(dwc->dev, "%s FIFO Overrun\n", dep->name); break; case DWC3_DEPEVT_EPCMDCMPLT: - dev_vdbg(dwc->dev, "Endpoint Command Complete\n"); + dwc3_trace(trace_dwc3_gadget, "Endpoint Command Complete"); break; } } @@ -2381,7 +2391,8 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, (pwropt != DWC3_GHWPARAMS1_EN_PWROPT_HIB)) { if ((dwc->link_state == DWC3_LINK_STATE_U3) && (next == DWC3_LINK_STATE_RESUME)) { - dev_vdbg(dwc->dev, "ignoring transition U3 -> Resume\n"); + dwc3_trace(trace_dwc3_gadget, + "ignoring transition U3 -> Resume"); return; } } @@ -2503,19 +2514,19 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc, dwc3_gadget_linksts_change_interrupt(dwc, event->event_info); break; case DWC3_DEVICE_EVENT_EOPF: - dev_vdbg(dwc->dev, "End of Periodic Frame\n"); + dwc3_trace(trace_dwc3_gadget, "End of Periodic Frame"); break; case DWC3_DEVICE_EVENT_SOF: - dev_vdbg(dwc->dev, "Start of Periodic Frame\n"); + dwc3_trace(trace_dwc3_gadget, "Start of Periodic Frame"); break; case DWC3_DEVICE_EVENT_ERRATIC_ERROR: - dev_vdbg(dwc->dev, "Erratic Error\n"); + dwc3_trace(trace_dwc3_gadget, "Erratic Error"); break; case DWC3_DEVICE_EVENT_CMD_CMPL: - dev_vdbg(dwc->dev, "Command Complete\n"); + dwc3_trace(trace_dwc3_gadget, "Command Complete"); break; case DWC3_DEVICE_EVENT_OVERFLOW: - dev_vdbg(dwc->dev, "Overflow\n"); + dwc3_trace(trace_dwc3_gadget, "Overflow"); break; default: dev_WARN(dwc->dev, "UNKNOWN IRQ %d\n", event->type); From 5c7b3b02de766a8634708953e805399e3544a290 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 29 Jan 2015 10:29:18 -0600 Subject: [PATCH 171/214] usb: dwc3: gadget: add missing spin_lock() commit 8e74475b0e0a (usb: dwc3: gadget: use udc-core's reset notifier) added support for the new UDC core's reset notifier to dwc3 but while at it, it removed a spin_lock() from dwc3_reset_gadget() which might cause an unbalanced spin_unlock() further down the line Fixes: 8e74475b0e0a (usb: dwc3: gadget: use udc-core's reset notifier) Cc: # v3.19 Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 7cce00e7102b..eccd29b16840 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2045,6 +2045,7 @@ static void dwc3_resume_gadget(struct dwc3 *dwc) if (dwc->gadget_driver && dwc->gadget_driver->resume) { spin_unlock(&dwc->lock); dwc->gadget_driver->resume(&dwc->gadget); + spin_lock(&dwc->lock); } } From 80b2502cea34a965a6b3390691854e753945ca5f Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 28 Jan 2015 16:32:24 +0800 Subject: [PATCH 172/214] usb: gadget: introduce is_selfpowered for usb_gadget Whether the gadget is selfpowerwed or not can be determined by composite core, so we can use a common entry to indicate if the self-powered is supported by gadget, and the related private variable at individual udc driver can be deleted. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- include/linux/usb/gadget.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 70ddb3943b62..e2f00fd8cd47 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -523,6 +523,7 @@ struct usb_gadget_ops { * enabled HNP support. * @quirk_ep_out_aligned_size: epout requires buffer size to be aligned to * MaxPacketSize. + * @is_selfpowered: if the gadget is self-powered. * * Gadgets have a mostly-portable "gadget driver" implementing device * functions, handling all usb configurations and interfaces. Gadget @@ -563,6 +564,7 @@ struct usb_gadget { unsigned a_hnp_support:1; unsigned a_alt_hnp_support:1; unsigned quirk_ep_out_aligned_size:1; + unsigned is_selfpowered:1; }; #define work_to_gadget(w) (container_of((w), struct usb_gadget, work)) From 1009f9a36d30f87676ede4c69f97a4eb45001f15 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 28 Jan 2015 16:32:25 +0800 Subject: [PATCH 173/214] usb: chipidea: udc: add set_selfpowered gaget ops The gadget power property will be used at get_status request. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/chipidea/udc.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 4fe18ce3bd5a..ff451048c1ac 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -819,8 +819,8 @@ __acquires(hwep->lock) } if ((setup->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) { - /* Assume that device is bus powered for now. */ - *(u16 *)req->buf = ci->remote_wakeup << 1; + *(u16 *)req->buf = (ci->remote_wakeup << 1) | + ci->gadget.is_selfpowered; } else if ((setup->bRequestType & USB_RECIP_MASK) \ == USB_RECIP_ENDPOINT) { dir = (le16_to_cpu(setup->wIndex) & USB_ENDPOINT_DIR_MASK) ? @@ -1520,6 +1520,19 @@ static int ci_udc_vbus_draw(struct usb_gadget *_gadget, unsigned ma) return -ENOTSUPP; } +static int ci_udc_selfpowered(struct usb_gadget *_gadget, int is_on) +{ + struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget); + struct ci_hw_ep *hwep = ci->ep0in; + unsigned long flags; + + spin_lock_irqsave(hwep->lock, flags); + _gadget->is_selfpowered = (is_on != 0); + spin_unlock_irqrestore(hwep->lock, flags); + + return 0; +} + /* Change Data+ pullup status * this func is used by usb_gadget_connect/disconnet */ @@ -1549,6 +1562,7 @@ static int ci_udc_stop(struct usb_gadget *gadget); static const struct usb_gadget_ops usb_gadget_ops = { .vbus_session = ci_udc_vbus_session, .wakeup = ci_udc_wakeup, + .set_selfpowered = ci_udc_selfpowered, .pullup = ci_udc_pullup, .vbus_draw = ci_udc_vbus_draw, .udc_start = ci_udc_start, From 7301971f50d08250ecddc1a2298dde5466d6e315 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 28 Jan 2015 16:32:26 +0800 Subject: [PATCH 174/214] usb: gadget: at91_udc: use common is_selfpowered Delete private selfpowered variable, and use common one. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/at91_udc.c | 8 ++++---- drivers/usb/gadget/udc/at91_udc.h | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/udc/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c index c862656d18b8..c0ec5f71f16f 100644 --- a/drivers/usb/gadget/udc/at91_udc.c +++ b/drivers/usb/gadget/udc/at91_udc.c @@ -176,7 +176,7 @@ static int proc_udc_show(struct seq_file *s, void *unused) udc->enabled ? (udc->vbus ? "active" : "enabled") : "disabled", - udc->selfpowered ? "self" : "VBUS", + udc->gadget.is_selfpowered ? "self" : "VBUS", udc->suspended ? ", suspended" : "", udc->driver ? udc->driver->driver.name : "(none)"); @@ -1000,7 +1000,7 @@ static int at91_set_selfpowered(struct usb_gadget *gadget, int is_on) unsigned long flags; spin_lock_irqsave(&udc->lock, flags); - udc->selfpowered = (is_on != 0); + gadget->is_selfpowered = (is_on != 0); spin_unlock_irqrestore(&udc->lock, flags); return 0; } @@ -1149,7 +1149,7 @@ static void handle_setup(struct at91_udc *udc, struct at91_ep *ep, u32 csr) */ case ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE) << 8) | USB_REQ_GET_STATUS: - tmp = (udc->selfpowered << USB_DEVICE_SELF_POWERED); + tmp = (udc->gadget.is_selfpowered << USB_DEVICE_SELF_POWERED); if (at91_udp_read(udc, AT91_UDP_GLB_STAT) & AT91_UDP_ESR) tmp |= (1 << USB_DEVICE_REMOTE_WAKEUP); PACKET("get device status\n"); @@ -1653,7 +1653,7 @@ static int at91_start(struct usb_gadget *gadget, udc->driver = driver; udc->gadget.dev.of_node = udc->pdev->dev.of_node; udc->enabled = 1; - udc->selfpowered = 1; + udc->gadget.is_selfpowered = 1; return 0; } diff --git a/drivers/usb/gadget/udc/at91_udc.h b/drivers/usb/gadget/udc/at91_udc.h index 017524663381..467dcfb74a51 100644 --- a/drivers/usb/gadget/udc/at91_udc.h +++ b/drivers/usb/gadget/udc/at91_udc.h @@ -122,7 +122,6 @@ struct at91_udc { unsigned req_pending:1; unsigned wait_for_addr_ack:1; unsigned wait_for_config_ack:1; - unsigned selfpowered:1; unsigned active_suspend:1; u8 addr; struct at91_udc_data board; From 9239d88fc5e58a2a72bc949362f999aac9bffb29 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 28 Jan 2015 16:32:27 +0800 Subject: [PATCH 175/214] staging: emxx_udc: use common is_selfpowered Delete private selfpowered variable, and use common one. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/staging/emxx_udc/emxx_udc.c | 9 +++------ drivers/staging/emxx_udc/emxx_udc.h | 1 - 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/staging/emxx_udc/emxx_udc.c b/drivers/staging/emxx_udc/emxx_udc.c index eb178fcb7954..1d3135a33258 100644 --- a/drivers/staging/emxx_udc/emxx_udc.c +++ b/drivers/staging/emxx_udc/emxx_udc.c @@ -1608,7 +1608,7 @@ static int std_req_get_status(struct nbu2ss_udc *udc) switch (recipient) { case USB_RECIP_DEVICE: if (udc->ctrl.wIndex == 0x0000) { - if (udc->self_powered) + if (udc->gadget.is_selfpowered) status_data |= (1 << USB_DEVICE_SELF_POWERED); if (udc->remote_wakeup) @@ -3117,7 +3117,6 @@ static int nbu2ss_gad_wakeup(struct usb_gadget *pgadget) static int nbu2ss_gad_set_selfpowered(struct usb_gadget *pgadget, int is_selfpowered) { - struct nbu2ss_udc *udc; unsigned long flags; /* INFO("=== %s()\n", __func__); */ @@ -3127,10 +3126,8 @@ static int nbu2ss_gad_set_selfpowered(struct usb_gadget *pgadget, return -EINVAL; } - udc = container_of(pgadget, struct nbu2ss_udc, gadget); - spin_lock_irqsave(&udc->lock, flags); - udc->self_powered = (is_selfpowered != 0); + pgadget->is_selfpowered = (is_selfpowered != 0); spin_unlock_irqrestore(&udc->lock, flags); return 0; @@ -3308,7 +3305,7 @@ static int __init nbu2ss_drv_contest_init( spin_lock_init(&udc->lock); udc->dev = &pdev->dev; - udc->self_powered = 1; + udc->gadget.is_selfpowered = 1; udc->devstate = USB_STATE_NOTATTACHED; udc->pdev = pdev; udc->mA = 0; diff --git a/drivers/staging/emxx_udc/emxx_udc.h b/drivers/staging/emxx_udc/emxx_udc.h index ee1b80d705fa..202e2dc72bba 100644 --- a/drivers/staging/emxx_udc/emxx_udc.h +++ b/drivers/staging/emxx_udc/emxx_udc.h @@ -624,7 +624,6 @@ struct nbu2ss_udc { unsigned linux_suspended:1; unsigned linux_resume:1; unsigned usb_suspended:1; - unsigned self_powered:1; unsigned remote_wakeup:1; unsigned udc_enabled:1; From a17fd41206a6d3efc565d8319405692325874884 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 28 Jan 2015 16:32:28 +0800 Subject: [PATCH 176/214] usb: renesas_usbhs: gadget: set value for common is_selfpowered Set value for common is_selfpowered. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/renesas_usbhs/mod_gadget.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index 8697e6efcabf..e0384af77e56 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -926,6 +926,8 @@ static int usbhsg_set_selfpowered(struct usb_gadget *gadget, int is_self) else usbhsg_status_clr(gpriv, USBHSG_STATUS_SELF_POWERED); + gadget->is_selfpowered = (is_self != 0); + return 0; } From 4605437e335bf1cf3b30c1261a6a146dbd830188 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 28 Jan 2015 16:32:29 +0800 Subject: [PATCH 177/214] usb: gadget: bdc_udc: set value for common is_selfpowered Set value for common is_selfpowered. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/bdc/bdc_udc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/udc/bdc/bdc_udc.c b/drivers/usb/gadget/udc/bdc/bdc_udc.c index 3700ce70b0be..7f77db5d1278 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_udc.c +++ b/drivers/usb/gadget/udc/bdc/bdc_udc.c @@ -454,6 +454,7 @@ static int bdc_udc_set_selfpowered(struct usb_gadget *gadget, unsigned long flags; dev_dbg(bdc->dev, "%s()\n", __func__); + gadget->is_selfpowered = (is_self != 0); spin_lock_irqsave(&bdc->lock, flags); if (!is_self) bdc->devstatus |= 1 << USB_DEVICE_SELF_POWERED; From 5d9cb6afc496b20b2d2ef71e4dd3adf50841f8c8 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 28 Jan 2015 16:32:30 +0800 Subject: [PATCH 178/214] usb: gadget: dummy_hcd: set value for common is_selfpowered Set value for common is_selfpowered. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/dummy_hcd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index 270c1ec650fa..8dda48445f6f 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -802,6 +802,7 @@ static int dummy_set_selfpowered(struct usb_gadget *_gadget, int value) { struct dummy *dum; + _gadget->is_selfpowered = (value != 0); dum = gadget_to_dummy_hcd(_gadget)->dum; if (value) dum->devstatus |= (1 << USB_DEVICE_SELF_POWERED); From d60d939221dc16dfc0589f4fbb730aab1aa4daeb Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 28 Jan 2015 16:32:31 +0800 Subject: [PATCH 179/214] usb: gadget: lpc32xx_udc: use common is_selfpowered Delete private selfpowered variable, and use common one. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/lpc32xx_udc.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c index 34d9b7b831b3..27fd41333f71 100644 --- a/drivers/usb/gadget/udc/lpc32xx_udc.c +++ b/drivers/usb/gadget/udc/lpc32xx_udc.c @@ -191,7 +191,6 @@ struct lpc32xx_udc { bool enabled; bool clocked; bool suspended; - bool selfpowered; int ep0state; atomic_t enabled_ep_cnt; wait_queue_head_t ep_disable_wait_queue; @@ -547,7 +546,7 @@ static int proc_udc_show(struct seq_file *s, void *unused) udc->vbus ? "present" : "off", udc->enabled ? (udc->vbus ? "active" : "enabled") : "disabled", - udc->selfpowered ? "self" : "VBUS", + udc->gadget.is_selfpowered ? "self" : "VBUS", udc->suspended ? ", suspended" : "", udc->driver ? udc->driver->driver.name : "(none)"); @@ -2212,7 +2211,7 @@ static int udc_get_status(struct lpc32xx_udc *udc, u16 reqtype, u16 wIndex) break; /* Not supported */ case USB_RECIP_DEVICE: - ep0buff = (udc->selfpowered << USB_DEVICE_SELF_POWERED); + ep0buff = udc->gadget.is_selfpowered; if (udc->dev_status & (1 << USB_DEVICE_REMOTE_WAKEUP)) ep0buff |= (1 << USB_DEVICE_REMOTE_WAKEUP); break; @@ -2498,10 +2497,7 @@ static int lpc32xx_wakeup(struct usb_gadget *gadget) static int lpc32xx_set_selfpowered(struct usb_gadget *gadget, int is_on) { - struct lpc32xx_udc *udc = to_udc(gadget); - - /* Always self-powered */ - udc->selfpowered = (is_on != 0); + gadget->is_selfpowered = (is_on != 0); return 0; } @@ -2946,7 +2942,7 @@ static int lpc32xx_start(struct usb_gadget *gadget, udc->driver = driver; udc->gadget.dev.of_node = udc->dev->of_node; udc->enabled = 1; - udc->selfpowered = 1; + udc->gadget.is_selfpowered = 1; udc->vbus = 0; /* Force VBUS process once to check for cable insertion */ From 4651fcf3294eaa018c63dfdae0b1fb65e432f727 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 28 Jan 2015 16:32:32 +0800 Subject: [PATCH 180/214] usb: gadget: fsl_udc_core: set value for common is_selfpowered fsl udc core assumes itself always self powered, so set is_selfpowered is 1. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/fsl_udc_core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index c3830ad68edf..55fcb930f92e 100644 --- a/drivers/usb/gadget/udc/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -1337,7 +1337,7 @@ static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value, if ((request_type & USB_RECIP_MASK) == USB_RECIP_DEVICE) { /* Get device status */ - tmp = 1 << USB_DEVICE_SELF_POWERED; + tmp = udc->gadget.is_selfpowered; tmp |= udc->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP; } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_INTERFACE) { /* Get interface status */ @@ -1948,6 +1948,7 @@ static int fsl_udc_start(struct usb_gadget *g, /* hook up the driver */ udc_controller->driver = driver; spin_unlock_irqrestore(&udc_controller->lock, flags); + g->is_selfpowered = 1; if (!IS_ERR_OR_NULL(udc_controller->transceiver)) { /* Suspend the controller until OTG enable it */ From 58ae8e0b1455ab2adc7c6733ad2dde9e0317cc36 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 28 Jan 2015 16:32:33 +0800 Subject: [PATCH 181/214] usb: gadget: omap_udc: set value for common is_selfpowered Set value for common is_selfpowered. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/omap_udc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/udc/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c index 288c087220a8..e2fcdb8e5596 100644 --- a/drivers/usb/gadget/udc/omap_udc.c +++ b/drivers/usb/gadget/udc/omap_udc.c @@ -1171,6 +1171,7 @@ omap_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered) unsigned long flags; u16 syscon1; + gadget->is_selfpowered = (is_selfpowered != 0); udc = container_of(gadget, struct omap_udc, gadget); spin_lock_irqsave(&udc->lock, flags); syscon1 = omap_readw(UDC_SYSCON1); From 0bcff9eaa83228f36b8ae42cedc588e9337b7e71 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 28 Jan 2015 16:32:34 +0800 Subject: [PATCH 182/214] usb: gadget: r8a66597-udc: set value for common is_selfpowered Set value for common is_selfpowered. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/r8a66597-udc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/udc/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c index 06870da0b988..2495fe9c95c5 100644 --- a/drivers/usb/gadget/udc/r8a66597-udc.c +++ b/drivers/usb/gadget/udc/r8a66597-udc.c @@ -1803,6 +1803,7 @@ static int r8a66597_set_selfpowered(struct usb_gadget *gadget, int is_self) { struct r8a66597 *r8a66597 = gadget_to_r8a66597(gadget); + gadget->is_selfpowered = (is_self != 0); if (is_self) r8a66597->device_status |= 1 << USB_DEVICE_SELF_POWERED; else From c8678d9f288e7df66f50ab7be1eff1beb6bc7d70 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 28 Jan 2015 16:32:35 +0800 Subject: [PATCH 183/214] usb: gadget: net2280: use common is_selfpowered Delete private selfpowered variable, and use common one. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/net2280.c | 6 +++--- drivers/usb/gadget/udc/net2280.h | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index 6411ed8e14bc..d2c0bf65e345 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -1365,10 +1365,10 @@ static int net2280_set_selfpowered(struct usb_gadget *_gadget, int value) tmp = readl(&dev->usb->usbctl); if (value) { tmp |= BIT(SELF_POWERED_STATUS); - dev->selfpowered = 1; + _gadget->is_selfpowered = 1; } else { tmp &= ~BIT(SELF_POWERED_STATUS); - dev->selfpowered = 0; + _gadget->is_selfpowered = 0; } writel(tmp, &dev->usb->usbctl); spin_unlock_irqrestore(&dev->lock, flags); @@ -2611,7 +2611,7 @@ static void handle_stat0_irqs_superspeed(struct net2280 *dev, switch (r.bRequestType) { case (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE): status = dev->wakeup_enable ? 0x02 : 0x00; - if (dev->selfpowered) + if (dev->gadget.is_selfpowered) status |= BIT(0); status |= (dev->u1_enable << 2 | dev->u2_enable << 3 | dev->ltm_enable << 4); diff --git a/drivers/usb/gadget/udc/net2280.h b/drivers/usb/gadget/udc/net2280.h index a307dce2e184..ac8d5a20a378 100644 --- a/drivers/usb/gadget/udc/net2280.h +++ b/drivers/usb/gadget/udc/net2280.h @@ -164,7 +164,6 @@ struct net2280 { u2_enable:1, ltm_enable:1, wakeup_enable:1, - selfpowered:1, addressed_state:1, bug7734_patched:1; u16 chiprev; From b3764dd1a318a227099984a4406f47f861b1d6eb Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 28 Jan 2015 16:32:36 +0800 Subject: [PATCH 184/214] usb: gadget: s3c2410_udc: set value for common is_selfpowered Set value for common is_selfpowered. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/s3c2410_udc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/udc/s3c2410_udc.c b/drivers/usb/gadget/udc/s3c2410_udc.c index 256a67ba2158..b808951491cc 100644 --- a/drivers/usb/gadget/udc/s3c2410_udc.c +++ b/drivers/usb/gadget/udc/s3c2410_udc.c @@ -1434,6 +1434,7 @@ static int s3c2410_udc_set_selfpowered(struct usb_gadget *gadget, int value) dprintk(DEBUG_NORMAL, "%s()\n", __func__); + gadget->is_selfpowered = (value != 0); if (value) udc->devstatus |= (1 << USB_DEVICE_SELF_POWERED); else From d4a1c47946a2796520a95da6bed8e5642ce9fb19 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 28 Jan 2015 16:32:37 +0800 Subject: [PATCH 185/214] usb: gadget: net2272: use common is_selfpowered Delete private selfpowered variable, and use common one. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/net2272.c | 7 ++----- drivers/usb/gadget/udc/net2272.h | 1 - 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/usb/gadget/udc/net2272.c b/drivers/usb/gadget/udc/net2272.c index d20de1fab08e..195baf3e1fcd 100644 --- a/drivers/usb/gadget/udc/net2272.c +++ b/drivers/usb/gadget/udc/net2272.c @@ -1132,13 +1132,10 @@ net2272_wakeup(struct usb_gadget *_gadget) static int net2272_set_selfpowered(struct usb_gadget *_gadget, int value) { - struct net2272 *dev; - if (!_gadget) return -ENODEV; - dev = container_of(_gadget, struct net2272, gadget); - dev->is_selfpowered = value; + _gadget->is_selfpowered = (value != 0); return 0; } @@ -1844,7 +1841,7 @@ net2272_handle_stat0_irqs(struct net2272 *dev, u8 stat) case USB_RECIP_DEVICE: if (u.r.wLength > 2) goto do_stall; - if (dev->is_selfpowered) + if (dev->gadget.is_selfpowered) status = (1 << USB_DEVICE_SELF_POWERED); /* don't bother with a request object! */ diff --git a/drivers/usb/gadget/udc/net2272.h b/drivers/usb/gadget/udc/net2272.h index e59505789359..127ab03fcde3 100644 --- a/drivers/usb/gadget/udc/net2272.h +++ b/drivers/usb/gadget/udc/net2272.h @@ -458,7 +458,6 @@ struct net2272 { struct usb_gadget_driver *driver; unsigned protocol_stall:1, softconnect:1, - is_selfpowered:1, wakeup:1, dma_eot_polarity:1, dma_dack_polarity:1, From d618c368ef98631da3adf802e500d5c61103804f Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 28 Jan 2015 16:32:38 +0800 Subject: [PATCH 186/214] usb: gadget: atmel_usba_udc: set value for common is_selfpowered Set value for common is_selfpowered. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/atmel_usba_udc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 4b5b7fafac85..c0410862c2a1 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -989,6 +989,7 @@ usba_udc_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered) struct usba_udc *udc = to_usba_udc(gadget); unsigned long flags; + gadget->is_selfpowered = (is_selfpowered != 0); spin_lock_irqsave(&udc->lock, flags); if (is_selfpowered) udc->devstatus |= 1 << USB_DEVICE_SELF_POWERED; From 716013b0ab2b13fe6a176d869b9fcb9bf0f8e112 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 28 Jan 2015 16:32:39 +0800 Subject: [PATCH 187/214] usb: gadget: pch_udc: set value for common is_selfpowered Set value for common is_selfpowered. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/pch_udc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c index 1c7379ac2379..613547f07828 100644 --- a/drivers/usb/gadget/udc/pch_udc.c +++ b/drivers/usb/gadget/udc/pch_udc.c @@ -1161,6 +1161,7 @@ static int pch_udc_pcd_selfpowered(struct usb_gadget *gadget, int value) if (!gadget) return -EINVAL; + gadget->is_selfpowered = (value != 0); dev = container_of(gadget, struct pch_udc_dev, gadget); if (value) pch_udc_set_selfpowered(dev); From bcdea50312b291ee3920bad43a13e42d7102f900 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 28 Jan 2015 16:32:40 +0800 Subject: [PATCH 188/214] usb: dwc3: gadget: use common is_selfpowered Delete private selfpowered variable, and use common one. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 2 -- drivers/usb/dwc3/ep0.c | 2 +- drivers/usb/dwc3/gadget.c | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 0842aa80976f..d201910b892f 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -682,7 +682,6 @@ struct dwc3_scratchpad_array { * @is_utmi_l1_suspend: the core asserts output signal * 0 - utmi_sleep_n * 1 - utmi_l1_suspend_n - * @is_selfpowered: true when we are selfpowered * @is_fpga: true when we are using the FPGA board * @needs_fifo_resize: not all users might want fifo resizing, flag it * @pullups_connected: true when Run/Stop bit is set @@ -806,7 +805,6 @@ struct dwc3 { unsigned has_hibernation:1; unsigned has_lpm_erratum:1; unsigned is_utmi_l1_suspend:1; - unsigned is_selfpowered:1; unsigned is_fpga:1; unsigned needs_fifo_resize:1; unsigned pullups_connected:1; diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 1bc77a3b4997..2ef3c8d6a9db 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -344,7 +344,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc, /* * LTM will be set once we know how to set this in HW. */ - usb_status |= dwc->is_selfpowered << USB_DEVICE_SELF_POWERED; + usb_status |= dwc->gadget.is_selfpowered; if (dwc->speed == DWC3_DSTS_SUPERSPEED) { reg = dwc3_readl(dwc->regs, DWC3_DCTL); diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index eccd29b16840..a03a485205c7 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1415,7 +1415,7 @@ static int dwc3_gadget_set_selfpowered(struct usb_gadget *g, unsigned long flags; spin_lock_irqsave(&dwc->lock, flags); - dwc->is_selfpowered = !!is_selfpowered; + g->is_selfpowered = !!is_selfpowered; spin_unlock_irqrestore(&dwc->lock, flags); return 0; From dadac9861f466635c3ac0851f2a39be68fa10a71 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 28 Jan 2015 16:32:41 +0800 Subject: [PATCH 189/214] usb: musb: gadget: use common is_selfpowered Delete private selfpowered variable, and use common one. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_gadget.c | 4 +--- drivers/usb/musb/musb_gadget_ep0.c | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 49b04cb6f5ca..b2d9040c7685 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1612,9 +1612,7 @@ done: static int musb_gadget_set_self_powered(struct usb_gadget *gadget, int is_selfpowered) { - struct musb *musb = gadget_to_musb(gadget); - - musb->is_self_powered = !!is_selfpowered; + gadget->is_selfpowered = !!is_selfpowered; return 0; } diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c index 2af45a0c8930..10d30afe4a3c 100644 --- a/drivers/usb/musb/musb_gadget_ep0.c +++ b/drivers/usb/musb/musb_gadget_ep0.c @@ -85,7 +85,7 @@ static int service_tx_status_request( switch (recip) { case USB_RECIP_DEVICE: - result[0] = musb->is_self_powered << USB_DEVICE_SELF_POWERED; + result[0] = musb->g.is_selfpowered << USB_DEVICE_SELF_POWERED; result[0] |= musb->may_wakeup << USB_DEVICE_REMOTE_WAKEUP; if (musb->g.is_otg) { result[0] |= musb->g.b_hnp_enable From 3f6dd4feda71760c41336963c17b594872346654 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 28 Jan 2015 16:32:42 +0800 Subject: [PATCH 190/214] usb: udc-core: add is_selfpowered sys entry The user can read it through sys entry. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/udc-core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c index e31d574d8860..5a81cb086b99 100644 --- a/drivers/usb/gadget/udc/udc-core.c +++ b/drivers/usb/gadget/udc/udc-core.c @@ -564,6 +564,7 @@ static USB_UDC_ATTR(is_a_peripheral); static USB_UDC_ATTR(b_hnp_enable); static USB_UDC_ATTR(a_hnp_support); static USB_UDC_ATTR(a_alt_hnp_support); +static USB_UDC_ATTR(is_selfpowered); static struct attribute *usb_udc_attrs[] = { &dev_attr_srp.attr, @@ -577,6 +578,7 @@ static struct attribute *usb_udc_attrs[] = { &dev_attr_b_hnp_enable.attr, &dev_attr_a_hnp_support.attr, &dev_attr_a_alt_hnp_support.attr, + &dev_attr_is_selfpowered.attr, NULL, }; From a0cb12e2ed61f4c1bb586268ca43b27dd8ce959e Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 28 Jan 2015 22:50:04 +0100 Subject: [PATCH 191/214] usb: musb: add generic usb phy dependencies Multiple musb glue drivers depend on the generic usb phy support, but fail to list it as a dependency in Kconfig. This results in build erros like: drivers/built-in.o: In function `am35x_remove': :(.text+0xadacc): undefined reference to `usb_phy_generic_unregister' drivers/built-in.o: In function `am35x_probe': :(.text+0xae1c8): undefined reference to `usb_phy_generic_register' :(.text+0xae244): undefined reference to `usb_phy_generic_unregister' drivers/built-in.o: In function `jz4740_remove': :(.text+0xaf648): undefined reference to `usb_phy_generic_unregister' drivers/built-in.o: In function `jz4740_musb_init': :(.text+0xaf694): undefined reference to `usb_phy_generic_register' This adds the ones that are missing. Signed-off-by: Arnd Bergmann Signed-off-by: Felipe Balbi --- drivers/usb/musb/Kconfig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index b005010240e5..26c835f1b726 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -63,11 +63,13 @@ comment "Platform Glue Layer" config USB_MUSB_DAVINCI tristate "DaVinci" depends on ARCH_DAVINCI_DMx + depends on NOP_USB_XCEIV depends on BROKEN config USB_MUSB_DA8XX tristate "DA8xx/OMAP-L1x" depends on ARCH_DAVINCI_DA8XX + depends on NOP_USB_XCEIV depends on BROKEN config USB_MUSB_TUSB6010 @@ -83,6 +85,7 @@ config USB_MUSB_OMAP2PLUS config USB_MUSB_AM35X tristate "AM35x" depends on ARCH_OMAP + depends on NOP_USB_XCEIV config USB_MUSB_DSPS tristate "TI DSPS platforms" @@ -93,6 +96,7 @@ config USB_MUSB_DSPS config USB_MUSB_BLACKFIN tristate "Blackfin" depends on (BF54x && !BF544) || (BF52x && ! BF522 && !BF523) + depends on NOP_USB_XCEIV config USB_MUSB_UX500 tristate "Ux500 platforms" @@ -100,6 +104,7 @@ config USB_MUSB_UX500 config USB_MUSB_JZ4740 tristate "JZ4740" + depends on NOP_USB_XCEIV depends on MACH_JZ4740 || COMPILE_TEST depends on USB_MUSB_GADGET depends on USB_OTG_BLACKLIST_HUB From fbba7db3990cb707ff91cd6507d53a0a730afe97 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 28 Jan 2015 22:51:04 +0100 Subject: [PATCH 192/214] usb: musb: add omap-control dependency The omap musb front-end calls into the phy driver directly instead of using a generic phy interface, which causes a link error when the specific driver is not built-in: drivers/built-in.o: In function `omap2430_musb_disable': usb/musb/omap2430.c:480: undefined reference to `omap_control_usb_set_mode' drivers/built-in.o: In function `omap2430_musb_enable': usb/musb/omap2430.c:466: undefined reference to `omap_control_usb_set_mode' usb/musb/omap2430.c:447: undefined reference to `omap_control_usb_set_mode' drivers/built-in.o: In function `omap_musb_set_mailbox': usb/musb/omap2430.c:273: undefined reference to `omap_control_usb_set_mode' usb/musb/omap2430.c:304: undefined reference to `omap_control_usb_set_mode' drivers/built-in.o:(.debug_addr+0xbd9e0): more undefined references to `omap_control_usb_set_mode' follow This adds an explicit dependency. Signed-off-by: Arnd Bergmann Fixes: ca784be36cc725 ("usb: start using the control module driver") Cc: # v3.9+ Signed-off-by: Felipe Balbi --- drivers/usb/musb/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 26c835f1b726..14e1628483d9 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -79,7 +79,7 @@ config USB_MUSB_TUSB6010 config USB_MUSB_OMAP2PLUS tristate "OMAP2430 and onwards" - depends on ARCH_OMAP2PLUS && USB + depends on ARCH_OMAP2PLUS && USB && OMAP_CONTROL_PHY select GENERIC_PHY config USB_MUSB_AM35X From b46146d59fdac6a6f559d5e6618f128abf0c2912 Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Fri, 30 Jan 2015 09:09:26 +0100 Subject: [PATCH 193/214] usb: dwc2: host: resume root hub on remote wakeup When a remote wakeup happens during bus_suspend, hcd needs to resume its root hub. Acked-by: John Youn Signed-off-by: Gregory Herrero Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/hcd.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index c3e66f0be02c..4a9bcdd5cb23 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -316,10 +316,12 @@ void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg) */ static void dwc2_hcd_rem_wakeup(struct dwc2_hsotg *hsotg) { - if (hsotg->lx_state == DWC2_L2) + if (hsotg->lx_state == DWC2_L2) { hsotg->flags.b.port_suspend_change = 1; - else + usb_hcd_resume_root_hub(hsotg->priv); + } else { hsotg->flags.b.port_l1_change = 1; + } } /** From c00dd4a6ec20a58ed38be85f8e47efda40625f17 Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Fri, 30 Jan 2015 09:09:27 +0100 Subject: [PATCH 194/214] usb: dwc2: gadget: fix clear halt feature handling When clearing HALT on an endpoint, req->complete of in progress requests must be called with locks off. New request should only be started if there is not already a pending request on the endpoint. Tested-by: Robert Baldyga Acked-by: John Youn Signed-off-by: Gregory Herrero Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 50ae09648cc1..02d0e9af9242 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -1007,16 +1007,22 @@ static int s3c_hsotg_process_req_feature(struct dwc2_hsotg *hsotg, hs_req = ep->req; ep->req = NULL; list_del_init(&hs_req->queue); - usb_gadget_giveback_request(&ep->ep, - &hs_req->req); + if (hs_req->req.complete) { + spin_unlock(&hsotg->lock); + usb_gadget_giveback_request( + &ep->ep, &hs_req->req); + spin_lock(&hsotg->lock); + } } /* If we have pending request, then start it */ - restart = !list_empty(&ep->queue); - if (restart) { - hs_req = get_ep_head(ep); - s3c_hsotg_start_req(hsotg, ep, - hs_req, false); + if (!ep->req) { + restart = !list_empty(&ep->queue); + if (restart) { + hs_req = get_ep_head(ep); + s3c_hsotg_start_req(hsotg, ep, + hs_req, false); + } } } From 9e14d0a566f48d33a9253096963b3b8199e4df57 Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Fri, 30 Jan 2015 09:09:28 +0100 Subject: [PATCH 195/214] usb: dwc2: gadget: add TEST_MODE feature support Handle SET_FEATURE TEST_MODE request sent by the host. Slightly rework FEATURE request handling to allow parsing other request types than Endpoint. Also add a debugfs to change test mode value from user space. Tested-by: Robert Baldyga Acked-by: John Youn Signed-off-by: Gregory Herrero Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/core.h | 4 + drivers/usb/dwc2/gadget.c | 189 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 186 insertions(+), 7 deletions(-) diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index f09b3deffdee..c750fd3d88b3 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -567,12 +567,14 @@ struct dwc2_hw_params { * @num_of_eps: Number of available EPs (excluding EP0) * @debug_root: Root directrory for debugfs. * @debug_file: Main status file for debugfs. + * @debug_testmode: Testmode status file for debugfs. * @debug_fifo: FIFO status file for debugfs. * @ep0_reply: Request used for ep0 reply. * @ep0_buff: Buffer for EP0 reply data, if needed. * @ctrl_buff: Buffer for EP0 control requests. * @ctrl_req: Request for EP0 control packets. * @ep0_state: EP0 control transfers state + * @test_mode: USB test mode requested by the host * @last_rst: Time of last reset * @eps: The endpoints being supplied to the gadget framework * @g_using_dma: Indicate if dma usage is enabled @@ -610,6 +612,7 @@ struct dwc2_hsotg { struct dentry *debug_root; struct dentry *debug_file; + struct dentry *debug_testmode; struct dentry *debug_fifo; /* DWC OTG HW Release versions */ @@ -706,6 +709,7 @@ struct dwc2_hsotg { void *ep0_buff; void *ctrl_buff; enum dwc2_ep0_state ep0_state; + u8 test_mode; struct usb_gadget gadget; unsigned int enabled:1; diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 02d0e9af9242..67ea258335ad 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "core.h" #include "hw.h" @@ -834,6 +835,32 @@ static struct s3c_hsotg_ep *ep_from_windex(struct dwc2_hsotg *hsotg, return ep; } +/** + * s3c_hsotg_set_test_mode - Enable usb Test Modes + * @hsotg: The driver state. + * @testmode: requested usb test mode + * Enable usb Test Mode requested by the Host. + */ +static int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode) +{ + int dctl = readl(hsotg->regs + DCTL); + + dctl &= ~DCTL_TSTCTL_MASK; + switch (testmode) { + case TEST_J: + case TEST_K: + case TEST_SE0_NAK: + case TEST_PACKET: + case TEST_FORCE_EN: + dctl |= testmode << DCTL_TSTCTL_SHIFT; + break; + default: + return -EINVAL; + } + writel(dctl, hsotg->regs + DCTL); + return 0; +} + /** * s3c_hsotg_send_reply - send reply to control request * @hsotg: The device state @@ -968,19 +995,48 @@ static int s3c_hsotg_process_req_feature(struct dwc2_hsotg *hsotg, struct s3c_hsotg_ep *ep; int ret; bool halted; + u32 recip; + u32 wValue; + u32 wIndex; dev_dbg(hsotg->dev, "%s: %s_FEATURE\n", __func__, set ? "SET" : "CLEAR"); - if (ctrl->bRequestType == USB_RECIP_ENDPOINT) { - ep = ep_from_windex(hsotg, le16_to_cpu(ctrl->wIndex)); + wValue = le16_to_cpu(ctrl->wValue); + wIndex = le16_to_cpu(ctrl->wIndex); + recip = ctrl->bRequestType & USB_RECIP_MASK; + + switch (recip) { + case USB_RECIP_DEVICE: + switch (wValue) { + case USB_DEVICE_TEST_MODE: + if ((wIndex & 0xff) != 0) + return -EINVAL; + if (!set) + return -EINVAL; + + hsotg->test_mode = wIndex >> 8; + ret = s3c_hsotg_send_reply(hsotg, ep0, NULL, 0); + if (ret) { + dev_err(hsotg->dev, + "%s: failed to send reply\n", __func__); + return ret; + } + break; + default: + return -ENOENT; + } + break; + + case USB_RECIP_ENDPOINT: + ep = ep_from_windex(hsotg, wIndex); if (!ep) { dev_dbg(hsotg->dev, "%s: no endpoint for 0x%04x\n", - __func__, le16_to_cpu(ctrl->wIndex)); + __func__, wIndex); return -ENOENT; } - switch (le16_to_cpu(ctrl->wValue)) { + switch (wValue) { case USB_ENDPOINT_HALT: halted = ep->halted; @@ -1031,9 +1087,10 @@ static int s3c_hsotg_process_req_feature(struct dwc2_hsotg *hsotg, default: return -ENOENT; } - } else - return -ENOENT; /* currently only deal with endpoint */ - + break; + default: + return -ENOENT; + } return 1; } @@ -1734,6 +1791,17 @@ static void s3c_hsotg_complete_in(struct dwc2_hsotg *hsotg, if (hs_ep->index == 0 && hsotg->ep0_state == DWC2_EP0_STATUS_IN) { dev_dbg(hsotg->dev, "zlp packet sent\n"); s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0); + if (hsotg->test_mode) { + int ret; + + ret = s3c_hsotg_set_test_mode(hsotg, hsotg->test_mode); + if (ret < 0) { + dev_dbg(hsotg->dev, "Invalid Test #%d\n", + hsotg->test_mode); + s3c_hsotg_stall_ep0(hsotg); + return; + } + } s3c_hsotg_enqueue_setup(hsotg); return; } @@ -2045,6 +2113,7 @@ void s3c_hsotg_disconnect(struct dwc2_hsotg *hsotg) return; hsotg->connected = 0; + hsotg->test_mode = 0; for (ep = 0; ep < hsotg->num_of_eps; ep++) { if (hsotg->eps_in[ep]) @@ -3256,6 +3325,103 @@ static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg) #endif } +/** + * testmode_write - debugfs: change usb test mode + * @seq: The seq file to write to. + * @v: Unused parameter. + * + * This debugfs entry modify the current usb test mode. + */ +static ssize_t testmode_write(struct file *file, const char __user *ubuf, size_t + count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct dwc2_hsotg *hsotg = s->private; + unsigned long flags; + u32 testmode = 0; + char buf[32]; + + if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) + return -EFAULT; + + if (!strncmp(buf, "test_j", 6)) + testmode = TEST_J; + else if (!strncmp(buf, "test_k", 6)) + testmode = TEST_K; + else if (!strncmp(buf, "test_se0_nak", 12)) + testmode = TEST_SE0_NAK; + else if (!strncmp(buf, "test_packet", 11)) + testmode = TEST_PACKET; + else if (!strncmp(buf, "test_force_enable", 17)) + testmode = TEST_FORCE_EN; + else + testmode = 0; + + spin_lock_irqsave(&hsotg->lock, flags); + s3c_hsotg_set_test_mode(hsotg, testmode); + spin_unlock_irqrestore(&hsotg->lock, flags); + return count; +} + +/** + * testmode_show - debugfs: show usb test mode state + * @seq: The seq file to write to. + * @v: Unused parameter. + * + * This debugfs entry shows which usb test mode is currently enabled. + */ +static int testmode_show(struct seq_file *s, void *unused) +{ + struct dwc2_hsotg *hsotg = s->private; + unsigned long flags; + int dctl; + + spin_lock_irqsave(&hsotg->lock, flags); + dctl = readl(hsotg->regs + DCTL); + dctl &= DCTL_TSTCTL_MASK; + dctl >>= DCTL_TSTCTL_SHIFT; + spin_unlock_irqrestore(&hsotg->lock, flags); + + switch (dctl) { + case 0: + seq_puts(s, "no test\n"); + break; + case TEST_J: + seq_puts(s, "test_j\n"); + break; + case TEST_K: + seq_puts(s, "test_k\n"); + break; + case TEST_SE0_NAK: + seq_puts(s, "test_se0_nak\n"); + break; + case TEST_PACKET: + seq_puts(s, "test_packet\n"); + break; + case TEST_FORCE_EN: + seq_puts(s, "test_force_enable\n"); + break; + default: + seq_printf(s, "UNKNOWN %d\n", dctl); + } + + return 0; +} + +static int testmode_open(struct inode *inode, struct file *file) +{ + return single_open(file, testmode_show, inode->i_private); +} + +static const struct file_operations testmode_fops = { + .owner = THIS_MODULE, + .open = testmode_open, + .write = testmode_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + /** * state_show - debugfs: show overall driver and device state. * @seq: The seq file to write to. @@ -3490,6 +3656,14 @@ static void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg) if (IS_ERR(hsotg->debug_file)) dev_err(hsotg->dev, "%s: failed to create state\n", __func__); + hsotg->debug_testmode = debugfs_create_file("testmode", + S_IRUGO | S_IWUSR, root, + hsotg, &testmode_fops); + + if (IS_ERR(hsotg->debug_testmode)) + dev_err(hsotg->dev, "%s: failed to create testmode\n", + __func__); + hsotg->debug_fifo = debugfs_create_file("fifo", 0444, root, hsotg, &fifo_fops); @@ -3544,6 +3718,7 @@ static void s3c_hsotg_delete_debug(struct dwc2_hsotg *hsotg) } debugfs_remove(hsotg->debug_file); + debugfs_remove(hsotg->debug_testmode); debugfs_remove(hsotg->debug_fifo); debugfs_remove(hsotg->debug_root); } From 58f7c43e06d0560ace01137d158108df0a08dc08 Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Fri, 30 Jan 2015 09:09:29 +0100 Subject: [PATCH 196/214] usb: dwc2: gadget: fix a typo in comment s3c_hsotg_process_req_feature comments was not correct Tested-by: Robert Baldyga Acked-by: John Youn Signed-off-by: Gregory Herrero Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 67ea258335ad..aa5c0ba5ed13 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -981,7 +981,7 @@ static struct s3c_hsotg_req *get_ep_head(struct s3c_hsotg_ep *hs_ep) } /** - * s3c_hsotg_process_req_featire - process request {SET,CLEAR}_FEATURE + * s3c_hsotg_process_req_feature - process request {SET,CLEAR}_FEATURE * @hsotg: The device state * @ctrl: USB control request */ From d7c747c590a98d2d75e4c2c97c875f0dcd5743ee Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Fri, 30 Jan 2015 09:09:30 +0100 Subject: [PATCH 197/214] usb: dwc2: gadget: remove hardcoded if (0) and if (1) checks Remove dead code as well. Tested-by: Robert Baldyga Acked-by: John Youn Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index aa5c0ba5ed13..fc9462aff67f 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -566,11 +566,6 @@ static void s3c_hsotg_start_req(struct dwc2_hsotg *hsotg, length = ureq->length - ureq->actual; dev_dbg(hsotg->dev, "ureq->length:%d ureq->actual:%d\n", ureq->length, ureq->actual); - if (0) - dev_dbg(hsotg->dev, - "REQ buf %p len %d dma %pad noi=%d zp=%d snok=%d\n", - ureq->buf, length, &ureq->dma, - ureq->no_interrupt, ureq->zero, ureq->short_not_ok); maxreq = get_ep_limit(hs_ep); if (length > maxreq) { @@ -1566,8 +1561,7 @@ static void s3c_hsotg_handle_rx(struct dwc2_hsotg *hsotg) size = grxstsr & GRXSTS_BYTECNT_MASK; size >>= GRXSTS_BYTECNT_SHIFT; - if (1) - dev_dbg(hsotg->dev, "%s: GRXSTSP=0x%08x (%d@%d)\n", + dev_dbg(hsotg->dev, "%s: GRXSTSP=0x%08x (%d@%d)\n", __func__, grxstsr, size, epnum); switch ((status & GRXSTS_PKTSTS_MASK) >> GRXSTS_PKTSTS_SHIFT) { @@ -2926,12 +2920,6 @@ static void s3c_hsotg_init(struct dwc2_hsotg *hsotg) /* Be in disconnected state until gadget is registered */ __orr32(hsotg->regs + DCTL, DCTL_SFTDISCON); - if (0) { - /* post global nak until we're ready */ - writel(DCTL_SGNPINNAK | DCTL_SGOUTNAK, - hsotg->regs + DCTL); - } - /* setup fifos */ dev_dbg(hsotg->dev, "GRXFSIZ=0x%08x, GNPTXFSIZ=0x%08x\n", From 7d24c1b5a77c71d885fca047d1f25721f6b366e7 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Fri, 30 Jan 2015 09:09:31 +0100 Subject: [PATCH 198/214] usb: dwc2: gadget: add unaligned buffers support When using DMA, dwc2 requires buffers to be 4 bytes aligned. Use bounce buffers if they are not. Tested-by: Robert Baldyga Acked-by: John Youn Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/core.h | 2 ++ drivers/usb/dwc2/gadget.c | 62 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index c750fd3d88b3..485b195126e7 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -158,10 +158,12 @@ struct s3c_hsotg_ep { * struct s3c_hsotg_req - data transfer request * @req: The USB gadget request * @queue: The list of requests for the endpoint this is queued for. + * @saved_req_buf: variable to save req.buf when bounce buffers are used. */ struct s3c_hsotg_req { struct usb_request req; struct list_head queue; + void *saved_req_buf; }; #if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index fc9462aff67f..5e91cf154bba 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -726,6 +726,59 @@ dma_error: return -EIO; } +static int s3c_hsotg_handle_unaligned_buf_start(struct dwc2_hsotg *hsotg, + struct s3c_hsotg_ep *hs_ep, struct s3c_hsotg_req *hs_req) +{ + void *req_buf = hs_req->req.buf; + + /* If dma is not being used or buffer is aligned */ + if (!using_dma(hsotg) || !((long)req_buf & 3)) + return 0; + + WARN_ON(hs_req->saved_req_buf); + + dev_dbg(hsotg->dev, "%s: %s: buf=%p length=%d\n", __func__, + hs_ep->ep.name, req_buf, hs_req->req.length); + + hs_req->req.buf = kmalloc(hs_req->req.length, GFP_ATOMIC); + if (!hs_req->req.buf) { + hs_req->req.buf = req_buf; + dev_err(hsotg->dev, + "%s: unable to allocate memory for bounce buffer\n", + __func__); + return -ENOMEM; + } + + /* Save actual buffer */ + hs_req->saved_req_buf = req_buf; + + if (hs_ep->dir_in) + memcpy(hs_req->req.buf, req_buf, hs_req->req.length); + return 0; +} + +static void s3c_hsotg_handle_unaligned_buf_complete(struct dwc2_hsotg *hsotg, + struct s3c_hsotg_ep *hs_ep, struct s3c_hsotg_req *hs_req) +{ + /* If dma is not being used or buffer was aligned */ + if (!using_dma(hsotg) || !hs_req->saved_req_buf) + return; + + dev_dbg(hsotg->dev, "%s: %s: status=%d actual-length=%d\n", __func__, + hs_ep->ep.name, hs_req->req.status, hs_req->req.actual); + + /* Copy data from bounce buffer on successful out transfer */ + if (!hs_ep->dir_in && !hs_req->req.status) + memcpy(hs_req->saved_req_buf, hs_req->req.buf, + hs_req->req.actual); + + /* Free bounce buffer */ + kfree(hs_req->req.buf); + + hs_req->req.buf = hs_req->saved_req_buf; + hs_req->saved_req_buf = NULL; +} + static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req, gfp_t gfp_flags) { @@ -733,6 +786,7 @@ static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req, struct s3c_hsotg_ep *hs_ep = our_ep(ep); struct dwc2_hsotg *hs = hs_ep->parent; bool first; + int ret; dev_dbg(hs->dev, "%s: req %p: %d@%p, noi=%d, zero=%d, snok=%d\n", ep->name, req, req->length, req->buf, req->no_interrupt, @@ -743,9 +797,13 @@ static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req, req->actual = 0; req->status = -EINPROGRESS; + ret = s3c_hsotg_handle_unaligned_buf_start(hs, hs_ep, hs_req); + if (ret) + return ret; + /* if we're using DMA, sync the buffers as necessary */ if (using_dma(hs)) { - int ret = s3c_hsotg_map_dma(hs, hs_ep, req); + ret = s3c_hsotg_map_dma(hs, hs_ep, req); if (ret) return ret; } @@ -1326,6 +1384,8 @@ static void s3c_hsotg_complete_request(struct dwc2_hsotg *hsotg, if (hs_req->req.status == -EINPROGRESS) hs_req->req.status = result; + s3c_hsotg_handle_unaligned_buf_complete(hsotg, hs_ep, hs_req); + hs_ep->req = NULL; list_del_init(&hs_req->queue); From 643cc4dee1ee804b46f8d288babca046cca7e099 Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Fri, 30 Jan 2015 09:09:32 +0100 Subject: [PATCH 199/214] usb: dwc2: gadget: add reset flag in init function Add a flag to request physical reset of the controller when s3c_hsotg_core_init_disconnected is called. During the usb reset, controller must not be fully reconfigured and resetted. Else this leads to shorter chirp-k duration during enumeration. Tested-by: Robert Baldyga Acked-by: John Youn Signed-off-by: Gregory Herrero Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/core.h | 6 ++++-- drivers/usb/dwc2/gadget.c | 33 +++++++++++++++++++++------------ drivers/usb/dwc2/hcd.c | 2 +- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 485b195126e7..f74304b12652 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -999,7 +999,8 @@ extern int s3c_hsotg_remove(struct dwc2_hsotg *hsotg); extern int s3c_hsotg_suspend(struct dwc2_hsotg *dwc2); extern int s3c_hsotg_resume(struct dwc2_hsotg *dwc2); extern int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq); -extern void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2); +extern void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2, + bool reset); extern void s3c_hsotg_core_connect(struct dwc2_hsotg *hsotg); extern void s3c_hsotg_disconnect(struct dwc2_hsotg *dwc2); #else @@ -1011,7 +1012,8 @@ static inline int s3c_hsotg_resume(struct dwc2_hsotg *dwc2) { return 0; } static inline int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) { return 0; } -static inline void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2) {} +static inline void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2, + bool reset) {} static inline void s3c_hsotg_core_connect(struct dwc2_hsotg *hsotg) {} static inline void s3c_hsotg_disconnect(struct dwc2_hsotg *dwc2) {} #endif diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 5e91cf154bba..3cf9c6a5e2c7 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2271,9 +2271,13 @@ static int s3c_hsotg_corereset(struct dwc2_hsotg *hsotg) * * Issue a soft reset to the core, and await the core finishing it. */ -void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg) +void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, + bool is_usb_reset) { - s3c_hsotg_corereset(hsotg); + u32 val; + + if (!is_usb_reset) + s3c_hsotg_corereset(hsotg); /* * we must now enable ep0 ready for host detection and then @@ -2286,7 +2290,8 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg) s3c_hsotg_init_fifo(hsotg); - __orr32(hsotg->regs + DCTL, DCTL_SFTDISCON); + if (!is_usb_reset) + __orr32(hsotg->regs + DCTL, DCTL_SFTDISCON); writel(1 << 18 | DCFG_DEVSPD_HS, hsotg->regs + DCFG); @@ -2357,9 +2362,11 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg) s3c_hsotg_ctrl_epint(hsotg, 0, 0, 1); s3c_hsotg_ctrl_epint(hsotg, 0, 1, 1); - __orr32(hsotg->regs + DCTL, DCTL_PWRONPRGDONE); - udelay(10); /* see openiboot */ - __bic32(hsotg->regs + DCTL, DCTL_PWRONPRGDONE); + if (!is_usb_reset) { + __orr32(hsotg->regs + DCTL, DCTL_PWRONPRGDONE); + udelay(10); /* see openiboot */ + __bic32(hsotg->regs + DCTL, DCTL_PWRONPRGDONE); + } dev_dbg(hsotg->dev, "DCTL=0x%08x\n", readl(hsotg->regs + DCTL)); @@ -2388,8 +2395,10 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg) readl(hsotg->regs + DOEPCTL0)); /* clear global NAKs */ - writel(DCTL_CGOUTNAK | DCTL_CGNPINNAK | DCTL_SFTDISCON, - hsotg->regs + DCTL); + val = DCTL_CGOUTNAK | DCTL_CGNPINNAK; + if (!is_usb_reset) + val |= DCTL_SFTDISCON; + __orr32(hsotg->regs + DCTL, val); /* must be at-least 3ms to allow bus to see disconnect */ mdelay(3); @@ -2482,7 +2491,7 @@ irq_retry: kill_all_requests(hsotg, hsotg->eps_out[0], -ECONNRESET); - s3c_hsotg_core_init_disconnected(hsotg); + s3c_hsotg_core_init_disconnected(hsotg, true); s3c_hsotg_core_connect(hsotg); } } @@ -3052,7 +3061,7 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget, spin_lock_irqsave(&hsotg->lock, flags); s3c_hsotg_init(hsotg); - s3c_hsotg_core_init_disconnected(hsotg); + s3c_hsotg_core_init_disconnected(hsotg, false); hsotg->enabled = 0; spin_unlock_irqrestore(&hsotg->lock, flags); @@ -3171,7 +3180,7 @@ static int s3c_hsotg_vbus_session(struct usb_gadget *gadget, int is_active) if (is_active) { /* Kill any ep0 requests as controller will be reinitialized */ kill_all_requests(hsotg, hsotg->eps_out[0], -ECONNRESET); - s3c_hsotg_core_init_disconnected(hsotg); + s3c_hsotg_core_init_disconnected(hsotg, false); if (hsotg->enabled) s3c_hsotg_core_connect(hsotg); } else { @@ -4097,7 +4106,7 @@ int s3c_hsotg_resume(struct dwc2_hsotg *hsotg) s3c_hsotg_phy_enable(hsotg); spin_lock_irqsave(&hsotg->lock, flags); - s3c_hsotg_core_init_disconnected(hsotg); + s3c_hsotg_core_init_disconnected(hsotg, false); if (hsotg->enabled) s3c_hsotg_core_connect(hsotg); spin_unlock_irqrestore(&hsotg->lock, flags); diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 4a9bcdd5cb23..c78c8740db1d 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -1373,7 +1373,7 @@ static void dwc2_conn_id_status_change(struct work_struct *work) hsotg->op_state = OTG_STATE_B_PERIPHERAL; dwc2_core_init(hsotg, false, -1); dwc2_enable_global_interrupts(hsotg); - s3c_hsotg_core_init_disconnected(hsotg); + s3c_hsotg_core_init_disconnected(hsotg, false); s3c_hsotg_core_connect(hsotg); } else { /* A-Device connector (Host Mode) */ From 86e37bf9f5d7f1e634ba5ca6bbc02aa85d004d2f Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Fri, 30 Jan 2015 09:09:33 +0100 Subject: [PATCH 200/214] usb: dwc2: gadget: don't modify pullup status during reset Pullup doesn't need to be enabled during usb reset since it is already enabled. This leads to shorter chirp-k duration if done during usb reset. Tested-by: Robert Baldyga Acked-by: John Youn Signed-off-by: Gregory Herrero Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 3cf9c6a5e2c7..50903003f4d9 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2492,7 +2492,6 @@ irq_retry: -ECONNRESET); s3c_hsotg_core_init_disconnected(hsotg, true); - s3c_hsotg_core_connect(hsotg); } } } From ccb34a9101b340885b1ca3ec766e8544a6a433a7 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Fri, 30 Jan 2015 09:09:34 +0100 Subject: [PATCH 201/214] usb: dwc2: gadget: fix debug message for zlp Print debug message according to zlp direction. Always saying "Sending" is misleading. Tested-by: Robert Baldyga Acked-by: John Youn Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 50903003f4d9..38ba0221482e 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -1335,7 +1335,12 @@ static void s3c_hsotg_program_zlp(struct dwc2_hsotg *hsotg, u32 epctl_reg = hs_ep->dir_in ? DIEPCTL(index) : DOEPCTL(index); u32 epsiz_reg = hs_ep->dir_in ? DIEPTSIZ(index) : DOEPTSIZ(index); - dev_dbg(hsotg->dev, "Sending zero-length packet on ep%d\n", index); + if (hs_ep->dir_in) + dev_dbg(hsotg->dev, "Sending zero-length packet on ep%d\n", + index); + else + dev_dbg(hsotg->dev, "Receiving zero-length packet on ep%d\n", + index); writel(DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) | DXEPTSIZ_XFERSIZE(0), hsotg->regs + From fa4a8d722bc2f0b1887ea929874a0f6754eda8b4 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Fri, 30 Jan 2015 09:09:35 +0100 Subject: [PATCH 202/214] usb: dwc2: gadget: fix phy interface configuration hsotg->phyif is set in dwc2_gadget_init according to phy interface width. Use it for configuration instead of hardcoded value. Moreover, set USB turnaround time according to phy width. Tested-by: Robert Baldyga Acked-by: John Youn Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 38ba0221482e..8c3f8f8f769a 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2290,8 +2290,9 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, */ /* set the PLL on, remove the HNP/SRP and set the PHY */ + val = (hsotg->phyif == GUSBCFG_PHYIF8) ? 9 : 5; writel(hsotg->phyif | GUSBCFG_TOUTCAL(7) | - (0x5 << 10), hsotg->regs + GUSBCFG); + (val << 10), hsotg->regs + GUSBCFG); s3c_hsotg_init_fifo(hsotg); @@ -2978,6 +2979,7 @@ static void s3c_hsotg_phy_disable(struct dwc2_hsotg *hsotg) */ static void s3c_hsotg_init(struct dwc2_hsotg *hsotg) { + u32 trdtim; /* unmask subset of endpoint interrupts */ writel(DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK | @@ -3002,8 +3004,10 @@ static void s3c_hsotg_init(struct dwc2_hsotg *hsotg) s3c_hsotg_init_fifo(hsotg); /* set the PLL on, remove the HNP/SRP and set the PHY */ - writel(GUSBCFG_PHYIF16 | GUSBCFG_TOUTCAL(7) | (0x5 << 10), - hsotg->regs + GUSBCFG); + trdtim = (hsotg->phyif == GUSBCFG_PHYIF8) ? 9 : 5; + writel(hsotg->phyif | GUSBCFG_TOUTCAL(7) | + (trdtim << 10), + hsotg->regs + GUSBCFG); if (using_dma(hsotg)) __orr32(hsotg->regs + GAHBCFG, GAHBCFG_DMA_EN); From f889f23d1c15fa73edaecc2cbce35a441eb52581 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Fri, 30 Jan 2015 09:09:36 +0100 Subject: [PATCH 203/214] usb: dwc2: gadget: replace constants with defines Defines are more readable and searchable than constants. Tested-by: Robert Baldyga Acked-by: John Youn Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 24 ++++++++++++------------ drivers/usb/dwc2/hw.h | 1 + 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 8c3f8f8f769a..f201f6d17bc7 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2292,14 +2292,14 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, /* set the PLL on, remove the HNP/SRP and set the PHY */ val = (hsotg->phyif == GUSBCFG_PHYIF8) ? 9 : 5; writel(hsotg->phyif | GUSBCFG_TOUTCAL(7) | - (val << 10), hsotg->regs + GUSBCFG); + (val << GUSBCFG_USBTRDTIM_SHIFT), hsotg->regs + GUSBCFG); s3c_hsotg_init_fifo(hsotg); if (!is_usb_reset) __orr32(hsotg->regs + DCTL, DCTL_SFTDISCON); - writel(1 << 18 | DCFG_DEVSPD_HS, hsotg->regs + DCFG); + writel(DCFG_EPMISCNT(1) | DCFG_DEVSPD_HS, hsotg->regs + DCFG); /* Clear any pending OTG interrupts */ writel(0xffffffff, hsotg->regs + GOTGINT); @@ -3006,7 +3006,7 @@ static void s3c_hsotg_init(struct dwc2_hsotg *hsotg) /* set the PLL on, remove the HNP/SRP and set the PHY */ trdtim = (hsotg->phyif == GUSBCFG_PHYIF8) ? 9 : 5; writel(hsotg->phyif | GUSBCFG_TOUTCAL(7) | - (trdtim << 10), + (trdtim << GUSBCFG_USBTRDTIM_SHIFT), hsotg->regs + GUSBCFG); if (using_dma(hsotg)) @@ -3295,7 +3295,7 @@ static int s3c_hsotg_hw_cfg(struct dwc2_hsotg *hsotg) /* check hardware configuration */ cfg = readl(hsotg->regs + GHWCFG2); - hsotg->num_of_eps = (cfg >> 10) & 0xF; + hsotg->num_of_eps = (cfg >> GHWCFG2_NUM_DEV_EP_SHIFT) & 0xF; /* Add ep0 */ hsotg->num_of_eps++; @@ -3326,10 +3326,10 @@ static int s3c_hsotg_hw_cfg(struct dwc2_hsotg *hsotg) } cfg = readl(hsotg->regs + GHWCFG3); - hsotg->fifo_mem = (cfg >> 16); + hsotg->fifo_mem = (cfg >> GHWCFG3_DFIFO_DEPTH_SHIFT); cfg = readl(hsotg->regs + GHWCFG4); - hsotg->dedicated_fifos = (cfg >> 25) & 1; + hsotg->dedicated_fifos = (cfg >> GHWCFG4_DED_FIFO_SHIFT) & 1; dev_info(hsotg->dev, "EPs: %d, %s fifos, %d entries in SPRAM\n", hsotg->num_of_eps, @@ -3354,8 +3354,8 @@ static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg) readl(regs + DCFG), readl(regs + DCTL), readl(regs + DIEPMSK)); - dev_info(dev, "GAHBCFG=0x%08x, 0x44=0x%08x\n", - readl(regs + GAHBCFG), readl(regs + 0x44)); + dev_info(dev, "GAHBCFG=0x%08x, GHWCFG1=0x%08x\n", + readl(regs + GAHBCFG), readl(regs + GHWCFG1)); dev_info(dev, "GRXFSIZ=0x%08x, GNPTXFSIZ=0x%08x\n", readl(regs + GRXFSIZ), readl(regs + GNPTXFSIZ)); @@ -3715,7 +3715,7 @@ static void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg) /* create general state file */ - hsotg->debug_file = debugfs_create_file("state", 0444, root, + hsotg->debug_file = debugfs_create_file("state", S_IRUGO, root, hsotg, &state_fops); if (IS_ERR(hsotg->debug_file)) @@ -3729,7 +3729,7 @@ static void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg) dev_err(hsotg->dev, "%s: failed to create testmode\n", __func__); - hsotg->debug_fifo = debugfs_create_file("fifo", 0444, root, + hsotg->debug_fifo = debugfs_create_file("fifo", S_IRUGO, root, hsotg, &fifo_fops); if (IS_ERR(hsotg->debug_fifo)) @@ -3741,7 +3741,7 @@ static void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg) ep = hsotg->eps_out[epidx]; if (ep) { - ep->debugfs = debugfs_create_file(ep->name, 0444, + ep->debugfs = debugfs_create_file(ep->name, S_IRUGO, root, ep, &ep_fops); if (IS_ERR(ep->debugfs)) @@ -3755,7 +3755,7 @@ static void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg) ep = hsotg->eps_in[epidx]; if (ep) { - ep->debugfs = debugfs_create_file(ep->name, 0444, + ep->debugfs = debugfs_create_file(ep->name, S_IRUGO, root, ep, &ep_fops); if (IS_ERR(ep->debugfs)) diff --git a/drivers/usb/dwc2/hw.h b/drivers/usb/dwc2/hw.h index d018ebef15cc..d0a5ed8fa15a 100644 --- a/drivers/usb/dwc2/hw.h +++ b/drivers/usb/dwc2/hw.h @@ -294,6 +294,7 @@ #define GHWCFG4_NUM_IN_EPS_MASK (0xf << 26) #define GHWCFG4_NUM_IN_EPS_SHIFT 26 #define GHWCFG4_DED_FIFO_EN (1 << 25) +#define GHWCFG4_DED_FIFO_SHIFT 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) From b4c2378df6419b81eefcb9cd56726321c29a70ca Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Fri, 30 Jan 2015 09:09:37 +0100 Subject: [PATCH 204/214] usb: dwc2: gadget: initialize controller in pullup callback USB reset interrupt is no more used to reset the controller. Thus, reset the controller in pullup callback as described by Synopsys programming guide. Otherwise enumeration sometimes fails when usb configuration is switched without physical disconnection. Tested-by: Robert Baldyga Acked-by: John Youn Signed-off-by: Gregory Herrero Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index f201f6d17bc7..15aa5785090b 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -3162,6 +3162,7 @@ static int s3c_hsotg_pullup(struct usb_gadget *gadget, int is_on) if (is_on) { clk_enable(hsotg->clk); hsotg->enabled = 1; + s3c_hsotg_core_init_disconnected(hsotg, false); s3c_hsotg_core_connect(hsotg); } else { s3c_hsotg_core_disconnect(hsotg); From 9eb0797722895f4309b46d122e24d87ad17f473b Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Fri, 30 Jan 2015 17:22:45 +0100 Subject: [PATCH 205/214] usb: phy: generic: fix the gpios to be optional All the gpios, ie. reset-gpios and vbus-detect-gpio, should be optional and not prevent the driver from working. Fix the regression in the behavior introduced by commit "usb: phy: generic: migrate to gpio_desc". Signed-off-by: Robert Jarzmik Tested-by: Fabio Estevam Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-generic.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c index 9a826ff31951..bdb4cb3920f1 100644 --- a/drivers/usb/phy/phy-generic.c +++ b/drivers/usb/phy/phy-generic.c @@ -218,12 +218,12 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop, clk_rate = 0; needs_vcc = of_property_read_bool(node, "vcc-supply"); - nop->gpiod_reset = devm_gpiod_get_optional(dev, "reset-gpios"); - err = PTR_ERR(nop->gpiod_reset); + nop->gpiod_reset = devm_gpiod_get_optional(dev, "reset"); + err = PTR_ERR_OR_ZERO(nop->gpiod_reset); if (!err) { nop->gpiod_vbus = devm_gpiod_get_optional(dev, - "vbus-detect-gpio"); - err = PTR_ERR(nop->gpiod_vbus); + "vbus-detect"); + err = PTR_ERR_OR_ZERO(nop->gpiod_vbus); } } else if (pdata) { type = pdata->type; @@ -242,9 +242,11 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop, if (err == -EPROBE_DEFER) return -EPROBE_DEFER; if (err) { - dev_err(dev, "Error requesting RESET GPIO\n"); + dev_err(dev, "Error requesting RESET or VBUS GPIO\n"); return err; } + if (nop->gpiod_reset) + gpiod_direction_output(nop->gpiod_reset, 1); nop->phy.otg = devm_kzalloc(dev, sizeof(*nop->phy.otg), GFP_KERNEL); From 0f4ff5f1f95796ff5e32766136f3d3dfce1ec2d7 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Fri, 30 Jan 2015 17:22:46 +0100 Subject: [PATCH 206/214] usb: phy: generic: fix the vbus interrupt request Declare the interrupt as "one shot" so that it is masked until the end of the threaded handler. This prevents the irq core from spitting out an error : "Threaded irq requested with handler=NULL and !ONESHOT for irq 63" This was introduced by commit "usb: phy: generic: add vbus support". Signed-off-by: Robert Jarzmik Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-generic.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c index bdb4cb3920f1..48af0687bf11 100644 --- a/drivers/usb/phy/phy-generic.c +++ b/drivers/usb/phy/phy-generic.c @@ -41,7 +41,8 @@ #include "phy-generic.h" #define VBUS_IRQ_FLAGS \ - (IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING) + (IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | \ + IRQF_ONESHOT) struct platform_device *usb_phy_generic_register(void) { From ffb9da65755815611010e07ad1c937858f226cb2 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Fri, 30 Jan 2015 15:58:23 +0900 Subject: [PATCH 207/214] usb: renesas_usbhs: fix NULL pointer dereference in dma_release_channel() This patch fixes an issue that the following commit causes NULL pointer dereference in dma_release_channel(). "usb: renesas_usbhs: add support for requesting DT DMA" (commit id abd2dbf6bb1b5f3a03a8c76b1a8879da1dd30caa) The usbhsf_dma_init_dt() should set fifo->{t,r}x_chan to NULL if dma_request_slave_channel_reason() returns IS_ERR value. Otherwise, usbhsf_dma_quit() will call dma_release_channel(), and then NULL pointer dereference happens. Acked-by: Geert Uytterhoeven Tested-by: Simon Horman Reported-by: Simon Horman Signed-off-by: Yoshihiro Shimoda Signed-off-by: Felipe Balbi --- drivers/usb/renesas_usbhs/fifo.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 4c086b1cda04..d891bff39d66 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -1072,7 +1072,11 @@ static void usbhsf_dma_init_pdev(struct usbhs_fifo *fifo) static void usbhsf_dma_init_dt(struct device *dev, struct usbhs_fifo *fifo) { fifo->tx_chan = dma_request_slave_channel_reason(dev, "tx"); + if (IS_ERR(fifo->tx_chan)) + fifo->tx_chan = NULL; fifo->rx_chan = dma_request_slave_channel_reason(dev, "rx"); + if (IS_ERR(fifo->rx_chan)) + fifo->rx_chan = NULL; } static void usbhsf_dma_init(struct usbhs_priv *priv, From b7974de821bb65a7efe1961a600c850b5029ed6d Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 28 Jan 2015 22:49:00 +0100 Subject: [PATCH 208/214] usb: dwc2: fix USB core dependencies It is currently possible to configure the dwc2 driver as built-in when host mode or dual-role is enabled, but the USB core is a loadable module. This leads to a link failure: drivers/built-in.o: In function `_dwc2_hcd_start': :(.text+0x84538): undefined reference to `usb_hcd_resume_root_hub' drivers/built-in.o: In function `_dwc2_hcd_urb_dequeue': :(.text+0x84aa0): undefined reference to `usb_hcd_check_unlink_urb' :(.text+0x84e4c): undefined reference to `usb_hcd_unlink_urb_from_ep' :(.text+0x84e74): undefined reference to `usb_hcd_giveback_urb' drivers/built-in.o: In function `dwc2_assign_and_init_hc': :(.text+0x86b98): undefined reference to `usb_hcd_unmap_urb_for_dma' drivers/built-in.o: In function `_dwc2_hcd_urb_enqueue': :(.text+0x8717c): undefined reference to `usb_hcd_link_urb_to_ep' :(.text+0x872f4): undefined reference to `usb_hcd_unlink_urb_from_ep' drivers/built-in.o: In function `dwc2_host_complete': :(.text+0x875d4): undefined reference to `usb_hcd_unlink_urb_from_ep' :(.text+0x87600): undefined reference to `usb_hcd_giveback_urb' drivers/built-in.o: In function `dwc2_hcd_init': :(.text+0x87ba8): undefined reference to `usb_disabled' :(.text+0x87d38): undefined reference to `usb_create_hcd' :(.text+0x88094): undefined reference to `usb_add_hcd' :(.text+0x880dc): undefined reference to `usb_put_hcd' drivers/built-in.o: In function `dwc2_hcd_remove': :(.text+0x8821c): undefined reference to `usb_remove_hcd' :(.text+0x8823c): undefined reference to `usb_put_hcd' drivers/built-in.o: In function `dwc2_hc_handle_tt_clear.isra.10': :(.text+0x88e2c): undefined reference to `usb_hub_clear_tt_buffer' drivers/built-in.o: In function `dwc2_hcd_qtd_add': :(.text+0x8b554): undefined reference to `usb_calc_bus_time' To fix the problem, this patch changes the dependencies so that dwc2 host mode can only be enabled if either the USB core is built-in or both USB and dwc2 are modules. Acked-by: John Youn Signed-off-by: Arnd Bergmann Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc2/Kconfig b/drivers/usb/dwc2/Kconfig index b323c4c11b0a..76b9ba4dc925 100644 --- a/drivers/usb/dwc2/Kconfig +++ b/drivers/usb/dwc2/Kconfig @@ -23,7 +23,7 @@ choice config USB_DWC2_HOST bool "Host only mode" - depends on USB + depends on USB=y || (USB_DWC2=m && USB) help The Designware USB2.0 high-speed host controller integrated into many SoCs. Select this option if you want the @@ -42,7 +42,7 @@ config USB_DWC2_PERIPHERAL config USB_DWC2_DUAL_ROLE bool "Dual Role mode" - depends on (USB=y || USB=USB_DWC2) && (USB_GADGET=y || USB_GADGET=USB_DWC2) + depends on (USB=y && USB_GADGET=y) || (USB_DWC2=m && USB && USB_GADGET) help Select this option if you want the driver to work in a dual-role mode. In this mode both host and gadget features are enabled, and From 74379991f6eb760c8767a39c60b4a918af30f80b Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 28 Jan 2015 13:19:17 -0200 Subject: [PATCH 209/214] usb: phy: phy-generic: Fix USB PHY gpio reset Since commit e9f2cefb0cdc2ae ("usb: phy: generic: migrate to gpio_desc") a kernel hang is observed on imx51-babbage board: [ 1.392824] ci_hdrc ci_hdrc.1: doesn't support gadget [ 1.397975] ci_hdrc ci_hdrc.1: EHCI Host Controller [ 1.403205] ci_hdrc ci_hdrc.1: new USB bus registered, assigned bus number 1 [ 1.422335] ci_hdrc ci_hdrc.1: USB 2.0 started, EHCI 1.00 [ 1.432962] hub 1-0:1.0: USB hub found [ 1.437119] hub 1-0:1.0: 1 port detected This hang happens because the reset GPIO stays at logic level 0. The USB PHY reset gpio is defined in the dts file as: reset-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>; , which means it is active low, so what the gpio reset pin needs to do in this case is the following: - Go to logic level 0 to reset the USB PHY - Stay at 0 for a bit - Go back to logic level 1 When switching to gpiod API we need to following according to Documentation/gpio/consumer.txt: "The first thing a driver must do with a GPIO is setting its direction. If no direction-setting flags have been given to gpiod_get*(), this is done by invoking one of the gpiod_direction_*() functions: int gpiod_direction_input(struct gpio_desc *desc) int gpiod_direction_output(struct gpio_desc *desc, int value)" Since no direction-setting flags have been given to devm_gpiod_get_optional() in our case, we need to use gpiod_direction_output to comply with the gpiod API. With this change the USB PHY reset performs a proper reset, the kernel boots fine and USB host is functional. Signed-off-by: Fabio Estevam Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-generic.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c index 48af0687bf11..70be50b734b2 100644 --- a/drivers/usb/phy/phy-generic.c +++ b/drivers/usb/phy/phy-generic.c @@ -64,11 +64,12 @@ static int nop_set_suspend(struct usb_phy *x, int suspend) static void nop_reset_set(struct usb_phy_generic *nop, int asserted) { - if (nop->gpiod_reset) - gpiod_set_value(nop->gpiod_reset, asserted); + if (!nop->gpiod_reset) + return; - if (!asserted) - usleep_range(10000, 20000); + gpiod_direction_output(nop->gpiod_reset, !asserted); + usleep_range(10000, 20000); + gpiod_set_value(nop->gpiod_reset, asserted); } /* interface to regulator framework */ From d9b2b19fc45265a2423fa68e22318882b42a111c Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 31 Jan 2015 21:13:59 -0800 Subject: [PATCH 210/214] usb: musb: blackfin: remove incorrect __exit_p() bfin_remove() is not (nor should it be) marked as __exit, so we should not be using __exit_p() wrapper with it, otherwise unbinding through sysfs does not work properly. Signed-off-by: Dmitry Torokhov Signed-off-by: Felipe Balbi --- drivers/usb/musb/blackfin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 178250145613..6123b748d262 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -608,7 +608,7 @@ static SIMPLE_DEV_PM_OPS(bfin_pm_ops, bfin_suspend, bfin_resume); static struct platform_driver bfin_driver = { .probe = bfin_probe, - .remove = __exit_p(bfin_remove), + .remove = bfin_remove, .driver = { .name = "musb-blackfin", .pm = &bfin_pm_ops, From 8333d3cd06f89bdddea61c64ae123b2265ffab9d Mon Sep 17 00:00:00 2001 From: Christoph Jaeger Date: Mon, 2 Feb 2015 10:00:03 -0500 Subject: [PATCH 211/214] usb: gadget: Kconfig: use bool instead of boolean Keyword 'boolean' for type definition attributes is considered deprecated and, therefore, should not be used anymore. See http://lkml.kernel.org/r/cover.1418003065.git.cj@linux.com See http://lkml.kernel.org/r/1419108071-11607-1-git-send-email-cj@linux.com Signed-off-by: Christoph Jaeger Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 65f7f1265522..96539038c03a 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -424,7 +424,7 @@ config USB_CONFIGFS_F_HID For more information, see Documentation/usb/gadget_hid.txt. config USB_CONFIGFS_F_UVC - boolean "USB Webcam function" + bool "USB Webcam function" depends on USB_CONFIGFS depends on VIDEO_DEV select VIDEOBUF2_VMALLOC From 1fa2df0c70dadba1139c9dd52c9070d00fe23c98 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Tue, 3 Feb 2015 10:37:06 +0800 Subject: [PATCH 212/214] staging: emxx_udc: fix the build error Fix below build error: reproduce: wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross git checkout 9239d88fc5e58a2a72bc949362f999aac9bffb29 # save the attached .config to linux build tree make.cross ARCH=arm All error/warnings: In file included from include/linux/seqlock.h:35:0, from include/linux/time.h:5, from include/linux/stat.h:18, from include/linux/module.h:10, from drivers/staging/emxx_udc/emxx_udc.c:22: drivers/staging/emxx_udc/emxx_udc.c: In function 'nbu2ss_gad_set_selfpowered': >> drivers/staging/emxx_udc/emxx_udc.c:3129:21: error: 'udc' undeclared (first use in this function) spin_lock_irqsave(&udc->lock, flags); ^ include/linux/spinlock.h:215:34: note: in definition of macro 'raw_spin_lock_irqsave' flags = _raw_spin_lock_irqsave(lock); \ ^ >> drivers/staging/emxx_udc/emxx_udc.c:3129:2: note: in expansion of macro 'spin_lock_irqsave' spin_lock_irqsave(&udc->lock, flags); ^ drivers/staging/emxx_udc/emxx_udc.c:3129:21: note: each undeclared identifier is reported only once for each function it appears in spin_lock_irqsave(&udc->lock, flags); ^ include/linux/spinlock.h:215:34: note: in definition of macro 'raw_spin_lock_irqsave' flags = _raw_spin_lock_irqsave(lock); \ ^ >> drivers/staging/emxx_udc/emxx_udc.c:3129:2: note: in expansion of macro 'spin_lock_irqsave' spin_lock_irqsave(&udc->lock, flags); ^ vim +/udc +3129 drivers/staging/emxx_udc/emxx_udc.c 33aa8d45 Magnus Damm 2014-06-06 3123 33aa8d45 Magnus Damm 2014-06-06 3124 if (pgadget == NULL) { 33aa8d45 Magnus Damm 2014-06-06 3125 ERR("%s, bad param\n", __func__); 33aa8d45 Magnus Damm 2014-06-06 3126 return -EINVAL; 33aa8d45 Magnus Damm 2014-06-06 3127 } 33aa8d45 Magnus Damm 2014-06-06 3128 33aa8d45 Magnus Damm 2014-06-06 @3129 spin_lock_irqsave(&udc->lock, flags); 9239d88f Peter Chen 2015-01-28 3130 pgadget->is_selfpowered = (is_selfpowered != 0); 33aa8d45 Magnus Damm 2014-06-06 3131 spin_unlock_irqrestore(&udc->lock, flags); 33aa8d45 Magnus Damm 2014-06-06 3132 Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/staging/emxx_udc/emxx_udc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/emxx_udc/emxx_udc.c b/drivers/staging/emxx_udc/emxx_udc.c index 1d3135a33258..bd70ea05708b 100644 --- a/drivers/staging/emxx_udc/emxx_udc.c +++ b/drivers/staging/emxx_udc/emxx_udc.c @@ -3117,6 +3117,7 @@ static int nbu2ss_gad_wakeup(struct usb_gadget *pgadget) static int nbu2ss_gad_set_selfpowered(struct usb_gadget *pgadget, int is_selfpowered) { + struct nbu2ss_udc *udc; unsigned long flags; /* INFO("=== %s()\n", __func__); */ @@ -3126,6 +3127,8 @@ static int nbu2ss_gad_set_selfpowered(struct usb_gadget *pgadget, return -EINVAL; } + udc = container_of(pgadget, struct nbu2ss_udc, gadget); + spin_lock_irqsave(&udc->lock, flags); pgadget->is_selfpowered = (is_selfpowered != 0); spin_unlock_irqrestore(&udc->lock, flags); From 251a17f5aff990ab117590df2a13ef032464c0d3 Mon Sep 17 00:00:00 2001 From: Roshan Pius Date: Mon, 2 Feb 2015 14:55:38 -0800 Subject: [PATCH 213/214] usb: dwc2: Fix a bug in reading the endpoint directions from reg. According to the DWC2 datasheet, the HWCFG1 register stores the configured endpoint directions for endpoints 0-15 in bit positions 0-31. ========================== Endpoint Direction (EpDir) This 32-bit field uses two bits per endpoint to determine the endpoint direction. Endpoint Bits [31:30]: Endpoint 15 direction Bits [29:28]: Endpoint 14 direction .... Bits [3:2]: Endpoint 1 direction Bits[1:0]: Endpoint 0 direction (always BIDIR) ========================== The DWC2 driver is currently interpreting the contents of the register as directions for endpoints 1-15 which leads to an error in determining the configured endpoint directions in the core because the first 2 bits determine the direction of endpoint 0 and not 1. This is based on testing/next branch in Felipe's git. Signed-off-by: Roshan Pius Acked-by: John Youn Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 15aa5785090b..6a30887082cd 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -3308,7 +3308,7 @@ static int s3c_hsotg_hw_cfg(struct dwc2_hsotg *hsotg) hsotg->eps_out[0] = hsotg->eps_in[0]; cfg = readl(hsotg->regs + GHWCFG1); - for (i = 1; i < hsotg->num_of_eps; i++, cfg >>= 2) { + for (i = 1, cfg >>= 2; i < hsotg->num_of_eps; i++, cfg >>= 2) { ep_type = cfg & 3; /* Direction in or both */ if (!(ep_type & 2)) { From 9298b4aad37e8c6962edcdbd0b62620adb207d03 Mon Sep 17 00:00:00 2001 From: Bin Liu Date: Tue, 3 Feb 2015 11:02:10 -0600 Subject: [PATCH 214/214] usb: musb: fix device hotplug behind hub The commit 889ad3b "usb: musb: try a race-free wakeup" breaks device hotplug enumeraitonn when the device is connected behind a hub while usb autosuspend is enabled. Adding finish_resume_work into runtime resume callback fixes the issue. Also resume root hub is required to resume the bus from runtime suspend, so move musb_host_resume_root_hub() back to its original location, where handles RESUME interrupt. Signed-off-by: Bin Liu Tested-by: Tony Lindgren Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 7 +++++++ drivers/usb/musb/musb_virthub.c | 1 - 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 34cce3e38c49..e6f4cbfeed97 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -567,6 +567,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, musb->xceiv->otg->state = OTG_STATE_A_HOST; musb->is_active = 1; + musb_host_resume_root_hub(musb); break; case OTG_STATE_B_WAIT_ACON: musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL; @@ -2500,6 +2501,12 @@ static int musb_runtime_resume(struct device *dev) musb_restore_context(musb); first = 0; + if (musb->need_finish_resume) { + musb->need_finish_resume = 0; + schedule_delayed_work(&musb->finish_resume_work, + msecs_to_jiffies(20)); + } + return 0; } diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c index 662de58630e9..294e159f4afe 100644 --- a/drivers/usb/musb/musb_virthub.c +++ b/drivers/usb/musb/musb_virthub.c @@ -72,7 +72,6 @@ void musb_host_finish_resume(struct work_struct *work) musb->xceiv->otg->state = OTG_STATE_A_HOST; spin_unlock_irqrestore(&musb->lock, flags); - musb_host_resume_root_hub(musb); } void musb_port_suspend(struct musb *musb, bool do_suspend)