USB patches for 4.6-rc1
Here is the big USB patchset for 4.6-rc1. The normal mess is here, gadget and xhci fixes and updates, and lots of other driver updates and cleanups as well. Full details are in the shortlog. All have been in linux-next for a while with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iEYEABECAAYFAlbp8/EACgkQMUfUDdst+ylsyQCgnVK6ZIFVPV9VijJvBIjxS3F+ fTMAoIMQwNrRMHQOq/lhxX00AgN0B9Ch =2EQp -----END PGP SIGNATURE----- Merge tag 'usb-4.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb Pull USB updates from Greg KH: "Here is the big USB patchset for 4.6-rc1. The normal mess is here, gadget and xhci fixes and updates, and lots of other driver updates and cleanups as well. Full details are in the shortlog. All have been in linux-next for a while with no reported issues" * tag 'usb-4.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (266 commits) USB: core: let USB device know device node usb: devio: Add ioctl to disallow detaching kernel USB drivers. usb: gadget: f_acm: Fix configfs attr name usb: udc: lpc32xx: remove USB PLL and USB OTG clock management usb: udc: lpc32xx: remove direct access to clock controller registers usb: udc: lpc32xx: switch to clock prepare/unprepare model usb: renesas_usbhs: gadget: fix giveback status code in usbhsg_pipe_disable() usb: gadget: renesas_usb3: Use ARCH_RENESAS usb: dwc2: Fix issues in dwc2_complete_non_isoc_xfer_ddma() usb: dwc2: Add support for Lantiq ARX and XRX SoCs usb: phy: generic: Handle late registration of gadget usb: gadget: bdc_udc: fix race condition in bdc_udc_exit() usb: musb: core: added missing const qualifier to musb_hdrc_platform_data::config usb: dwc2: Move host-specific core functions into hcd.c usb: dwc2: Move register save and restore functions usb: dwc2: Use kmem_cache_free() usb: dwc2: host: If using uframe scheduler, end splits better usb: dwc2: host: Totally redo the microframe scheduler usb: dwc2: host: Properly set even/odd frame usb: dwc2: host: Add dwc2_hcd_get_future_frame_number() call ...
This commit is contained in:
commit
48d10bda1f
|
@ -732,6 +732,18 @@ usbdev_ioctl (int fd, int ifno, unsigned request, void *param)
|
|||
or SET_INTERFACE.
|
||||
</para></warning></listitem></varlistentry>
|
||||
|
||||
<varlistentry><term>USBDEVFS_DROP_PRIVILEGES</term>
|
||||
<listitem><para>This is used to relinquish the ability
|
||||
to do certain operations which are considered to be
|
||||
privileged on a usbfs file descriptor.
|
||||
This includes claiming arbitrary interfaces, resetting
|
||||
a device on which there are currently claimed interfaces
|
||||
from other users, and issuing USBDEVFS_IOCTL calls.
|
||||
The ioctl parameter is a 32 bit mask of interfaces
|
||||
the user is allowed to claim on this file descriptor.
|
||||
You may issue this ioctl more than one time to narrow
|
||||
said mask.
|
||||
</para></listitem></varlistentry>
|
||||
</variablelist>
|
||||
|
||||
</sect2>
|
||||
|
|
|
@ -7,33 +7,26 @@ Required properties:
|
|||
- compatible: "renesas,usb2-phy-r8a7795" if the device is a part of an R8A7795
|
||||
SoC.
|
||||
- reg: offset and length of the partial USB 2.0 Host register block.
|
||||
- reg-names: must be "usb2_host".
|
||||
- clocks: clock phandle and specifier pair(s).
|
||||
- #phy-cells: see phy-bindings.txt in the same directory, must be <0>.
|
||||
|
||||
Optional properties:
|
||||
To use a USB channel where USB 2.0 Host and HSUSB (USB 2.0 Peripheral) are
|
||||
combined, the device tree node should set HSUSB properties to reg and reg-names
|
||||
properties. This is because HSUSB has registers to select USB 2.0 host or
|
||||
peripheral at that channel:
|
||||
- reg: offset and length of the partial HSUSB register block.
|
||||
- reg-names: must be "hsusb".
|
||||
combined, the device tree node should set interrupt properties to use the
|
||||
channel as USB OTG:
|
||||
- interrupts: interrupt specifier for the PHY.
|
||||
|
||||
Example (R-Car H3):
|
||||
|
||||
usb-phy@ee080200 {
|
||||
compatible = "renesas,usb2-phy-r8a7795";
|
||||
reg = <0 0xee080200 0 0x700>, <0 0xe6590100 0 0x100>;
|
||||
reg-names = "usb2_host", "hsusb";
|
||||
reg = <0 0xee080200 0 0x700>;
|
||||
interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&mstp7_clks R8A7795_CLK_EHCI0>,
|
||||
<&mstp7_clks R8A7795_CLK_HSUSB>;
|
||||
clocks = <&mstp7_clks R8A7795_CLK_EHCI0>;
|
||||
};
|
||||
|
||||
usb-phy@ee0a0200 {
|
||||
compatible = "renesas,usb2-phy-r8a7795";
|
||||
reg = <0 0xee0a0200 0 0x700>;
|
||||
reg-names = "usb2_host";
|
||||
clocks = <&mstp7_clks R8A7795_CLK_EHCI0>;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
Rockchip specific extensions to the Analogix Display Port PHY
|
||||
------------------------------------
|
||||
|
||||
Required properties:
|
||||
- compatible : should be one of the following supported values:
|
||||
- "rockchip.rk3288-dp-phy"
|
||||
- clocks: from common clock binding: handle to dp clock.
|
||||
of memory mapped region.
|
||||
- clock-names: from common clock binding:
|
||||
Required elements: "24m"
|
||||
- rockchip,grf: phandle to the syscon managing the "general register files"
|
||||
- #phy-cells : from the generic PHY bindings, must be 0;
|
||||
|
||||
Example:
|
||||
|
||||
edp_phy: edp-phy {
|
||||
compatible = "rockchip,rk3288-dp-phy";
|
||||
rockchip,grf = <&grf>;
|
||||
clocks = <&cru SCLK_EDP_24M>;
|
||||
clock-names = "24m";
|
||||
#phy-cells = <0>;
|
||||
};
|
|
@ -0,0 +1,19 @@
|
|||
Rockchip EMMC PHY
|
||||
-----------------------
|
||||
|
||||
Required properties:
|
||||
- compatible: rockchip,rk3399-emmc-phy
|
||||
- rockchip,grf : phandle to the syscon managing the "general
|
||||
register files"
|
||||
- #phy-cells: must be 0
|
||||
- reg: PHY configure reg address offset in "general
|
||||
register files"
|
||||
|
||||
Example:
|
||||
|
||||
emmcphy: phy {
|
||||
compatible = "rockchip,rk3399-emmc-phy";
|
||||
rockchip,grf = <&grf>;
|
||||
reg = <0xf780>;
|
||||
#phy-cells = <0>;
|
||||
};
|
|
@ -2,7 +2,14 @@
|
|||
|
||||
Required properties:
|
||||
- compatible: should be one of:
|
||||
"fsl,imx23-usb"
|
||||
"fsl,imx27-usb"
|
||||
"fsl,imx28-usb"
|
||||
"fsl,imx6q-usb"
|
||||
"fsl,imx6sl-usb"
|
||||
"fsl,imx6sx-usb"
|
||||
"fsl,imx6ul-usb"
|
||||
"fsl,imx7d-usb"
|
||||
"lsi,zevio-usb"
|
||||
"qcom,ci-hdrc"
|
||||
"chipidea,usb2"
|
||||
|
@ -53,6 +60,22 @@ Optional properties:
|
|||
be specified.
|
||||
- phy-clkgate-delay-us: the delay time (us) between putting the PHY into
|
||||
low power mode and gating the PHY clock.
|
||||
- non-zero-ttctrl-ttha: after setting this property, the value of register
|
||||
ttctrl.ttha will be 0x7f; if not, the value will be 0x0, this is the default
|
||||
value. It needs to be very carefully for setting this property, it is
|
||||
recommended that consult with your IC engineer before setting this value.
|
||||
On the most of chipidea platforms, the "usage_tt" flag at RTL is 0, so this
|
||||
property only affects siTD.
|
||||
If this property is not set, the max packet size is 1023 bytes, and if
|
||||
the total of packet size for pervious transactions are more than 256 bytes,
|
||||
it can't accept any transactions within this frame. The use case is single
|
||||
transaction, but higher frame rate.
|
||||
If this property is set, the max packet size is 188 bytes, it can handle
|
||||
more transactions than above case, it can accept transactions until it
|
||||
considers the left room size within frame is less than 188 bytes, software
|
||||
needs to make sure it does not send more than 90%
|
||||
maximum_periodic_data_per_frame. The use case is multiple transactions, but
|
||||
less frame rate.
|
||||
|
||||
i.mx specific properties
|
||||
- fsl,usbmisc: phandler of non-core register device, with one
|
||||
|
|
|
@ -8,6 +8,8 @@ Required properties:
|
|||
- rockchip,rk3066-usb: The DWC2 USB controller instance in the rk3066 Soc;
|
||||
- "rockchip,rk3188-usb", "rockchip,rk3066-usb", "snps,dwc2": for rk3188 Soc;
|
||||
- "rockchip,rk3288-usb", "rockchip,rk3066-usb", "snps,dwc2": for rk3288 Soc;
|
||||
- "lantiq,arx100-usb": The DWC2 USB controller instance in Lantiq ARX SoCs;
|
||||
- "lantiq,xrx200-usb": The DWC2 USB controller instance in Lantiq XRX SoCs;
|
||||
- snps,dwc2: A generic DWC2 USB controller with default parameters.
|
||||
- reg : Should contain 1 register range (address and length)
|
||||
- interrupts : Should contain 1 interrupt
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
Generic USB Device Properties
|
||||
|
||||
Usually, we only use device tree for hard wired USB device.
|
||||
The reference binding doc is from:
|
||||
http://www.firmware.org/1275/bindings/usb/usb-1_0.ps
|
||||
|
||||
Required properties:
|
||||
- compatible: usbVID,PID. The textual representation of VID, PID shall
|
||||
be in lower case hexadecimal with leading zeroes suppressed. The
|
||||
other compatible strings from the above standard binding could also
|
||||
be used, but a device adhering to this binding may leave out all except
|
||||
for usbVID,PID.
|
||||
- reg: the port number which this device is connecting to, the range
|
||||
is 1-31.
|
||||
|
||||
Example:
|
||||
|
||||
&usb1 {
|
||||
status = "okay";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
hub: genesys@1 {
|
||||
compatible = "usb5e3,608";
|
||||
reg = <1>;
|
||||
};
|
||||
}
|
|
@ -1,10 +1,23 @@
|
|||
USB xHCI controllers
|
||||
|
||||
Required properties:
|
||||
- compatible: should be one of "generic-xhci",
|
||||
"marvell,armada-375-xhci", "marvell,armada-380-xhci",
|
||||
"renesas,xhci-r8a7790", "renesas,xhci-r8a7791", "renesas,xhci-r8a7793",
|
||||
"renesas,xhci-r8a7795" (deprecated: "xhci-platform").
|
||||
- compatible: should be one or more of
|
||||
|
||||
- "generic-xhci" for generic XHCI device
|
||||
- "marvell,armada-375-xhci" for Armada 375 SoCs
|
||||
- "marvell,armada-380-xhci" for Armada 38x SoCs
|
||||
- "renesas,xhci-r8a7790" for r8a7790 SoC
|
||||
- "renesas,xhci-r8a7791" for r8a7791 SoC
|
||||
- "renesas,xhci-r8a7793" for r8a7793 SoC
|
||||
- "renesas,xhci-r8a7795" for r8a7795 SoC
|
||||
- "renesas,rcar-gen2-xhci" for a generic R-Car Gen2 compatible device
|
||||
- "renesas,rcar-gen3-xhci" for a generic R-Car Gen3 compatible device
|
||||
- "xhci-platform" (deprecated)
|
||||
|
||||
When compatible with the generic version, nodes must list the
|
||||
SoC-specific version corresponding to the platform first
|
||||
followed by the generic version.
|
||||
|
||||
- reg: should contain address and length of the standard XHCI
|
||||
register set for the device.
|
||||
- interrupts: one XHCI interrupt should be described here.
|
||||
|
|
|
@ -3533,6 +3533,12 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
|
|||
on Mark read-only kernel memory as read-only (default).
|
||||
off Leave read-only kernel memory writable for debugging.
|
||||
|
||||
rockchip.usb_uart
|
||||
Enable the uart passthrough on the designated usb port
|
||||
on Rockchip SoCs. When active, the signals of the
|
||||
debug-uart get routed to the D+ and D- pins of the usb
|
||||
port and the regular usb controller gets disabled.
|
||||
|
||||
root= [KNL] Root filesystem
|
||||
See name_to_dev_t comment in init/do_mounts.c.
|
||||
|
||||
|
|
|
@ -26,16 +26,17 @@ cat /sys/kernel/debug/ci_hdrc.0/registers
|
|||
On B-device:
|
||||
echo 1 > /sys/bus/platform/devices/ci_hdrc.0/inputs/b_bus_req
|
||||
|
||||
if HNP polling is not supported, also need:
|
||||
On A-device:
|
||||
echo 0 > /sys/bus/platform/devices/ci_hdrc.0/inputs/a_bus_req
|
||||
|
||||
B-device should take host role and enumrate A-device.
|
||||
|
||||
4) A-device switch back to host.
|
||||
On B-device:
|
||||
echo 0 > /sys/bus/platform/devices/ci_hdrc.0/inputs/b_bus_req
|
||||
|
||||
or, by introducing HNP polling, B-Host can know when A-peripheral wish
|
||||
to be host role, so this role switch also can be trigged in A-peripheral
|
||||
side by answering the polling from B-Host, this can be done on A-device:
|
||||
echo 1 > /sys/bus/platform/devices/ci_hdrc.0/inputs/a_bus_req
|
||||
|
||||
A-device should switch back to host and enumrate B-device.
|
||||
|
||||
5) Remove B-device(unplug micro B plug) and insert again in 10 seconds,
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <linux/usbdevice_fs.h>
|
||||
|
||||
/* For building without an updated set of headers */
|
||||
#ifndef USBDEVFS_DROP_PRIVILEGES
|
||||
#define USBDEVFS_DROP_PRIVILEGES _IOW('U', 30, __u32)
|
||||
#define USBDEVFS_CAP_DROP_PRIVILEGES 0x40
|
||||
#endif
|
||||
|
||||
void drop_privileges(int fd, uint32_t mask)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ioctl(fd, USBDEVFS_DROP_PRIVILEGES, &mask);
|
||||
if (res)
|
||||
printf("ERROR: USBDEVFS_DROP_PRIVILEGES returned %d\n", res);
|
||||
else
|
||||
printf("OK: privileges dropped!\n");
|
||||
}
|
||||
|
||||
void reset_device(int fd)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ioctl(fd, USBDEVFS_RESET);
|
||||
if (!res)
|
||||
printf("OK: USBDEVFS_RESET succeeded\n");
|
||||
else
|
||||
printf("ERROR: reset failed! (%d - %s)\n",
|
||||
-res, strerror(-res));
|
||||
}
|
||||
|
||||
void claim_some_intf(int fd)
|
||||
{
|
||||
int i, res;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
res = ioctl(fd, USBDEVFS_CLAIMINTERFACE, &i);
|
||||
if (!res)
|
||||
printf("OK: claimed if %d\n", i);
|
||||
else
|
||||
printf("ERROR claiming if %d (%d - %s)\n",
|
||||
i, -res, strerror(-res));
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
uint32_t mask, caps;
|
||||
int c, fd;
|
||||
|
||||
fd = open(argv[1], O_RDWR);
|
||||
if (fd < 0) {
|
||||
printf("Failed to open file\n");
|
||||
goto err_fd;
|
||||
}
|
||||
|
||||
/*
|
||||
* check if dropping privileges is supported,
|
||||
* bail on systems where the capability is not present
|
||||
*/
|
||||
ioctl(fd, USBDEVFS_GET_CAPABILITIES, &caps);
|
||||
if (!(caps & USBDEVFS_CAP_DROP_PRIVILEGES)) {
|
||||
printf("DROP_PRIVILEGES not supported\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Drop privileges but keep the ability to claim all
|
||||
* free interfaces (i.e., those not used by kernel drivers)
|
||||
*/
|
||||
drop_privileges(fd, -1U);
|
||||
|
||||
printf("Available options:\n"
|
||||
"[0] Exit now\n"
|
||||
"[1] Reset device. Should fail if device is in use\n"
|
||||
"[2] Claim 4 interfaces. Should succeed where not in use\n"
|
||||
"[3] Narrow interface permission mask\n"
|
||||
"Which option shall I run?: ");
|
||||
|
||||
while (scanf("%d", &c) == 1) {
|
||||
switch (c) {
|
||||
case 0:
|
||||
goto exit;
|
||||
case 1:
|
||||
reset_device(fd);
|
||||
break;
|
||||
case 2:
|
||||
claim_some_intf(fd);
|
||||
break;
|
||||
case 3:
|
||||
printf("Insert new mask: ");
|
||||
scanf("%x", &mask);
|
||||
drop_privileges(fd, mask);
|
||||
break;
|
||||
default:
|
||||
printf("I don't recognize that\n");
|
||||
}
|
||||
|
||||
printf("Which test shall I run next?: ");
|
||||
}
|
||||
|
||||
exit:
|
||||
close(fd);
|
||||
return 0;
|
||||
|
||||
err:
|
||||
close(fd);
|
||||
err_fd:
|
||||
return 1;
|
||||
}
|
|
@ -11316,7 +11316,7 @@ F: include/linux/mtd/ubi.h
|
|||
F: include/uapi/mtd/ubi-user.h
|
||||
|
||||
USB ACM DRIVER
|
||||
M: Oliver Neukum <oliver@neukum.org>
|
||||
M: Oliver Neukum <oneukum@suse.com>
|
||||
L: linux-usb@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/usb/acm.txt
|
||||
|
@ -11440,6 +11440,7 @@ M: Valentina Manea <valentina.manea.m@gmail.com>
|
|||
M: Shuah Khan <shuah.kh@samsung.com>
|
||||
L: linux-usb@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/usb/usbip_protocol.txt
|
||||
F: drivers/usb/usbip/
|
||||
F: tools/usb/usbip/
|
||||
|
||||
|
|
|
@ -395,204 +395,6 @@
|
|||
#define CRP_AD_CBE_BESL 20
|
||||
#define CRP_AD_CBE_WRITE 0x00010000
|
||||
|
||||
|
||||
/*
|
||||
* USB Device Controller
|
||||
*
|
||||
* These are used by the USB gadget driver, so they don't follow the
|
||||
* IXP4XX_ naming convetions.
|
||||
*
|
||||
*/
|
||||
# define IXP4XX_USB_REG(x) (*((volatile u32 *)(x)))
|
||||
|
||||
/* UDC Undocumented - Reserved1 */
|
||||
#define UDC_RES1 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0004)
|
||||
/* UDC Undocumented - Reserved2 */
|
||||
#define UDC_RES2 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0008)
|
||||
/* UDC Undocumented - Reserved3 */
|
||||
#define UDC_RES3 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x000C)
|
||||
/* UDC Control Register */
|
||||
#define UDCCR IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0000)
|
||||
/* UDC Endpoint 0 Control/Status Register */
|
||||
#define UDCCS0 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0010)
|
||||
/* UDC Endpoint 1 (IN) Control/Status Register */
|
||||
#define UDCCS1 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0014)
|
||||
/* UDC Endpoint 2 (OUT) Control/Status Register */
|
||||
#define UDCCS2 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0018)
|
||||
/* UDC Endpoint 3 (IN) Control/Status Register */
|
||||
#define UDCCS3 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x001C)
|
||||
/* UDC Endpoint 4 (OUT) Control/Status Register */
|
||||
#define UDCCS4 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0020)
|
||||
/* UDC Endpoint 5 (Interrupt) Control/Status Register */
|
||||
#define UDCCS5 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0024)
|
||||
/* UDC Endpoint 6 (IN) Control/Status Register */
|
||||
#define UDCCS6 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0028)
|
||||
/* UDC Endpoint 7 (OUT) Control/Status Register */
|
||||
#define UDCCS7 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x002C)
|
||||
/* UDC Endpoint 8 (IN) Control/Status Register */
|
||||
#define UDCCS8 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0030)
|
||||
/* UDC Endpoint 9 (OUT) Control/Status Register */
|
||||
#define UDCCS9 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0034)
|
||||
/* UDC Endpoint 10 (Interrupt) Control/Status Register */
|
||||
#define UDCCS10 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0038)
|
||||
/* UDC Endpoint 11 (IN) Control/Status Register */
|
||||
#define UDCCS11 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x003C)
|
||||
/* UDC Endpoint 12 (OUT) Control/Status Register */
|
||||
#define UDCCS12 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0040)
|
||||
/* UDC Endpoint 13 (IN) Control/Status Register */
|
||||
#define UDCCS13 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0044)
|
||||
/* UDC Endpoint 14 (OUT) Control/Status Register */
|
||||
#define UDCCS14 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0048)
|
||||
/* UDC Endpoint 15 (Interrupt) Control/Status Register */
|
||||
#define UDCCS15 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x004C)
|
||||
/* UDC Frame Number Register High */
|
||||
#define UFNRH IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0060)
|
||||
/* UDC Frame Number Register Low */
|
||||
#define UFNRL IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0064)
|
||||
/* UDC Byte Count Reg 2 */
|
||||
#define UBCR2 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0068)
|
||||
/* UDC Byte Count Reg 4 */
|
||||
#define UBCR4 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x006c)
|
||||
/* UDC Byte Count Reg 7 */
|
||||
#define UBCR7 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0070)
|
||||
/* UDC Byte Count Reg 9 */
|
||||
#define UBCR9 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0074)
|
||||
/* UDC Byte Count Reg 12 */
|
||||
#define UBCR12 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0078)
|
||||
/* UDC Byte Count Reg 14 */
|
||||
#define UBCR14 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x007c)
|
||||
/* UDC Endpoint 0 Data Register */
|
||||
#define UDDR0 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0080)
|
||||
/* UDC Endpoint 1 Data Register */
|
||||
#define UDDR1 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0100)
|
||||
/* UDC Endpoint 2 Data Register */
|
||||
#define UDDR2 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0180)
|
||||
/* UDC Endpoint 3 Data Register */
|
||||
#define UDDR3 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0200)
|
||||
/* UDC Endpoint 4 Data Register */
|
||||
#define UDDR4 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0400)
|
||||
/* UDC Endpoint 5 Data Register */
|
||||
#define UDDR5 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x00A0)
|
||||
/* UDC Endpoint 6 Data Register */
|
||||
#define UDDR6 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0600)
|
||||
/* UDC Endpoint 7 Data Register */
|
||||
#define UDDR7 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0680)
|
||||
/* UDC Endpoint 8 Data Register */
|
||||
#define UDDR8 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0700)
|
||||
/* UDC Endpoint 9 Data Register */
|
||||
#define UDDR9 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0900)
|
||||
/* UDC Endpoint 10 Data Register */
|
||||
#define UDDR10 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x00C0)
|
||||
/* UDC Endpoint 11 Data Register */
|
||||
#define UDDR11 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0B00)
|
||||
/* UDC Endpoint 12 Data Register */
|
||||
#define UDDR12 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0B80)
|
||||
/* UDC Endpoint 13 Data Register */
|
||||
#define UDDR13 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0C00)
|
||||
/* UDC Endpoint 14 Data Register */
|
||||
#define UDDR14 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0E00)
|
||||
/* UDC Endpoint 15 Data Register */
|
||||
#define UDDR15 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x00E0)
|
||||
/* UDC Interrupt Control Register 0 */
|
||||
#define UICR0 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0050)
|
||||
/* UDC Interrupt Control Register 1 */
|
||||
#define UICR1 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0054)
|
||||
/* UDC Status Interrupt Register 0 */
|
||||
#define USIR0 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x0058)
|
||||
/* UDC Status Interrupt Register 1 */
|
||||
#define USIR1 IXP4XX_USB_REG(IXP4XX_USB_BASE_VIRT+0x005C)
|
||||
|
||||
#define UDCCR_UDE (1 << 0) /* UDC enable */
|
||||
#define UDCCR_UDA (1 << 1) /* UDC active */
|
||||
#define UDCCR_RSM (1 << 2) /* Device resume */
|
||||
#define UDCCR_RESIR (1 << 3) /* Resume interrupt request */
|
||||
#define UDCCR_SUSIR (1 << 4) /* Suspend interrupt request */
|
||||
#define UDCCR_SRM (1 << 5) /* Suspend/resume interrupt mask */
|
||||
#define UDCCR_RSTIR (1 << 6) /* Reset interrupt request */
|
||||
#define UDCCR_REM (1 << 7) /* Reset interrupt mask */
|
||||
|
||||
#define UDCCS0_OPR (1 << 0) /* OUT packet ready */
|
||||
#define UDCCS0_IPR (1 << 1) /* IN packet ready */
|
||||
#define UDCCS0_FTF (1 << 2) /* Flush Tx FIFO */
|
||||
#define UDCCS0_DRWF (1 << 3) /* Device remote wakeup feature */
|
||||
#define UDCCS0_SST (1 << 4) /* Sent stall */
|
||||
#define UDCCS0_FST (1 << 5) /* Force stall */
|
||||
#define UDCCS0_RNE (1 << 6) /* Receive FIFO no empty */
|
||||
#define UDCCS0_SA (1 << 7) /* Setup active */
|
||||
|
||||
#define UDCCS_BI_TFS (1 << 0) /* Transmit FIFO service */
|
||||
#define UDCCS_BI_TPC (1 << 1) /* Transmit packet complete */
|
||||
#define UDCCS_BI_FTF (1 << 2) /* Flush Tx FIFO */
|
||||
#define UDCCS_BI_TUR (1 << 3) /* Transmit FIFO underrun */
|
||||
#define UDCCS_BI_SST (1 << 4) /* Sent stall */
|
||||
#define UDCCS_BI_FST (1 << 5) /* Force stall */
|
||||
#define UDCCS_BI_TSP (1 << 7) /* Transmit short packet */
|
||||
|
||||
#define UDCCS_BO_RFS (1 << 0) /* Receive FIFO service */
|
||||
#define UDCCS_BO_RPC (1 << 1) /* Receive packet complete */
|
||||
#define UDCCS_BO_DME (1 << 3) /* DMA enable */
|
||||
#define UDCCS_BO_SST (1 << 4) /* Sent stall */
|
||||
#define UDCCS_BO_FST (1 << 5) /* Force stall */
|
||||
#define UDCCS_BO_RNE (1 << 6) /* Receive FIFO not empty */
|
||||
#define UDCCS_BO_RSP (1 << 7) /* Receive short packet */
|
||||
|
||||
#define UDCCS_II_TFS (1 << 0) /* Transmit FIFO service */
|
||||
#define UDCCS_II_TPC (1 << 1) /* Transmit packet complete */
|
||||
#define UDCCS_II_FTF (1 << 2) /* Flush Tx FIFO */
|
||||
#define UDCCS_II_TUR (1 << 3) /* Transmit FIFO underrun */
|
||||
#define UDCCS_II_TSP (1 << 7) /* Transmit short packet */
|
||||
|
||||
#define UDCCS_IO_RFS (1 << 0) /* Receive FIFO service */
|
||||
#define UDCCS_IO_RPC (1 << 1) /* Receive packet complete */
|
||||
#define UDCCS_IO_ROF (1 << 3) /* Receive overflow */
|
||||
#define UDCCS_IO_DME (1 << 3) /* DMA enable */
|
||||
#define UDCCS_IO_RNE (1 << 6) /* Receive FIFO not empty */
|
||||
#define UDCCS_IO_RSP (1 << 7) /* Receive short packet */
|
||||
|
||||
#define UDCCS_INT_TFS (1 << 0) /* Transmit FIFO service */
|
||||
#define UDCCS_INT_TPC (1 << 1) /* Transmit packet complete */
|
||||
#define UDCCS_INT_FTF (1 << 2) /* Flush Tx FIFO */
|
||||
#define UDCCS_INT_TUR (1 << 3) /* Transmit FIFO underrun */
|
||||
#define UDCCS_INT_SST (1 << 4) /* Sent stall */
|
||||
#define UDCCS_INT_FST (1 << 5) /* Force stall */
|
||||
#define UDCCS_INT_TSP (1 << 7) /* Transmit short packet */
|
||||
|
||||
#define UICR0_IM0 (1 << 0) /* Interrupt mask ep 0 */
|
||||
#define UICR0_IM1 (1 << 1) /* Interrupt mask ep 1 */
|
||||
#define UICR0_IM2 (1 << 2) /* Interrupt mask ep 2 */
|
||||
#define UICR0_IM3 (1 << 3) /* Interrupt mask ep 3 */
|
||||
#define UICR0_IM4 (1 << 4) /* Interrupt mask ep 4 */
|
||||
#define UICR0_IM5 (1 << 5) /* Interrupt mask ep 5 */
|
||||
#define UICR0_IM6 (1 << 6) /* Interrupt mask ep 6 */
|
||||
#define UICR0_IM7 (1 << 7) /* Interrupt mask ep 7 */
|
||||
|
||||
#define UICR1_IM8 (1 << 0) /* Interrupt mask ep 8 */
|
||||
#define UICR1_IM9 (1 << 1) /* Interrupt mask ep 9 */
|
||||
#define UICR1_IM10 (1 << 2) /* Interrupt mask ep 10 */
|
||||
#define UICR1_IM11 (1 << 3) /* Interrupt mask ep 11 */
|
||||
#define UICR1_IM12 (1 << 4) /* Interrupt mask ep 12 */
|
||||
#define UICR1_IM13 (1 << 5) /* Interrupt mask ep 13 */
|
||||
#define UICR1_IM14 (1 << 6) /* Interrupt mask ep 14 */
|
||||
#define UICR1_IM15 (1 << 7) /* Interrupt mask ep 15 */
|
||||
|
||||
#define USIR0_IR0 (1 << 0) /* Interrupt request ep 0 */
|
||||
#define USIR0_IR1 (1 << 1) /* Interrupt request ep 1 */
|
||||
#define USIR0_IR2 (1 << 2) /* Interrupt request ep 2 */
|
||||
#define USIR0_IR3 (1 << 3) /* Interrupt request ep 3 */
|
||||
#define USIR0_IR4 (1 << 4) /* Interrupt request ep 4 */
|
||||
#define USIR0_IR5 (1 << 5) /* Interrupt request ep 5 */
|
||||
#define USIR0_IR6 (1 << 6) /* Interrupt request ep 6 */
|
||||
#define USIR0_IR7 (1 << 7) /* Interrupt request ep 7 */
|
||||
|
||||
#define USIR1_IR8 (1 << 0) /* Interrupt request ep 8 */
|
||||
#define USIR1_IR9 (1 << 1) /* Interrupt request ep 9 */
|
||||
#define USIR1_IR10 (1 << 2) /* Interrupt request ep 10 */
|
||||
#define USIR1_IR11 (1 << 3) /* Interrupt request ep 11 */
|
||||
#define USIR1_IR12 (1 << 4) /* Interrupt request ep 12 */
|
||||
#define USIR1_IR13 (1 << 5) /* Interrupt request ep 13 */
|
||||
#define USIR1_IR14 (1 << 6) /* Interrupt request ep 14 */
|
||||
#define USIR1_IR15 (1 << 7) /* Interrupt request ep 15 */
|
||||
|
||||
#define DCMD_LENGTH 0x01fff /* length mask (max = 8K - 1) */
|
||||
|
||||
/* "fuse" bits of IXP_EXP_CFG2 */
|
||||
|
|
|
@ -1,163 +0,0 @@
|
|||
#ifndef _ASM_ARCH_PXA25X_UDC_H
|
||||
#define _ASM_ARCH_PXA25X_UDC_H
|
||||
|
||||
#ifdef _ASM_ARCH_PXA27X_UDC_H
|
||||
#error "You can't include both PXA25x and PXA27x UDC support"
|
||||
#endif
|
||||
|
||||
#define UDC_RES1 __REG(0x40600004) /* UDC Undocumented - Reserved1 */
|
||||
#define UDC_RES2 __REG(0x40600008) /* UDC Undocumented - Reserved2 */
|
||||
#define UDC_RES3 __REG(0x4060000C) /* UDC Undocumented - Reserved3 */
|
||||
|
||||
#define UDCCR __REG(0x40600000) /* UDC Control Register */
|
||||
#define UDCCR_UDE (1 << 0) /* UDC enable */
|
||||
#define UDCCR_UDA (1 << 1) /* UDC active */
|
||||
#define UDCCR_RSM (1 << 2) /* Device resume */
|
||||
#define UDCCR_RESIR (1 << 3) /* Resume interrupt request */
|
||||
#define UDCCR_SUSIR (1 << 4) /* Suspend interrupt request */
|
||||
#define UDCCR_SRM (1 << 5) /* Suspend/resume interrupt mask */
|
||||
#define UDCCR_RSTIR (1 << 6) /* Reset interrupt request */
|
||||
#define UDCCR_REM (1 << 7) /* Reset interrupt mask */
|
||||
|
||||
#define UDCCS0 __REG(0x40600010) /* UDC Endpoint 0 Control/Status Register */
|
||||
#define UDCCS0_OPR (1 << 0) /* OUT packet ready */
|
||||
#define UDCCS0_IPR (1 << 1) /* IN packet ready */
|
||||
#define UDCCS0_FTF (1 << 2) /* Flush Tx FIFO */
|
||||
#define UDCCS0_DRWF (1 << 3) /* Device remote wakeup feature */
|
||||
#define UDCCS0_SST (1 << 4) /* Sent stall */
|
||||
#define UDCCS0_FST (1 << 5) /* Force stall */
|
||||
#define UDCCS0_RNE (1 << 6) /* Receive FIFO no empty */
|
||||
#define UDCCS0_SA (1 << 7) /* Setup active */
|
||||
|
||||
/* Bulk IN - Endpoint 1,6,11 */
|
||||
#define UDCCS1 __REG(0x40600014) /* UDC Endpoint 1 (IN) Control/Status Register */
|
||||
#define UDCCS6 __REG(0x40600028) /* UDC Endpoint 6 (IN) Control/Status Register */
|
||||
#define UDCCS11 __REG(0x4060003C) /* UDC Endpoint 11 (IN) Control/Status Register */
|
||||
|
||||
#define UDCCS_BI_TFS (1 << 0) /* Transmit FIFO service */
|
||||
#define UDCCS_BI_TPC (1 << 1) /* Transmit packet complete */
|
||||
#define UDCCS_BI_FTF (1 << 2) /* Flush Tx FIFO */
|
||||
#define UDCCS_BI_TUR (1 << 3) /* Transmit FIFO underrun */
|
||||
#define UDCCS_BI_SST (1 << 4) /* Sent stall */
|
||||
#define UDCCS_BI_FST (1 << 5) /* Force stall */
|
||||
#define UDCCS_BI_TSP (1 << 7) /* Transmit short packet */
|
||||
|
||||
/* Bulk OUT - Endpoint 2,7,12 */
|
||||
#define UDCCS2 __REG(0x40600018) /* UDC Endpoint 2 (OUT) Control/Status Register */
|
||||
#define UDCCS7 __REG(0x4060002C) /* UDC Endpoint 7 (OUT) Control/Status Register */
|
||||
#define UDCCS12 __REG(0x40600040) /* UDC Endpoint 12 (OUT) Control/Status Register */
|
||||
|
||||
#define UDCCS_BO_RFS (1 << 0) /* Receive FIFO service */
|
||||
#define UDCCS_BO_RPC (1 << 1) /* Receive packet complete */
|
||||
#define UDCCS_BO_DME (1 << 3) /* DMA enable */
|
||||
#define UDCCS_BO_SST (1 << 4) /* Sent stall */
|
||||
#define UDCCS_BO_FST (1 << 5) /* Force stall */
|
||||
#define UDCCS_BO_RNE (1 << 6) /* Receive FIFO not empty */
|
||||
#define UDCCS_BO_RSP (1 << 7) /* Receive short packet */
|
||||
|
||||
/* Isochronous IN - Endpoint 3,8,13 */
|
||||
#define UDCCS3 __REG(0x4060001C) /* UDC Endpoint 3 (IN) Control/Status Register */
|
||||
#define UDCCS8 __REG(0x40600030) /* UDC Endpoint 8 (IN) Control/Status Register */
|
||||
#define UDCCS13 __REG(0x40600044) /* UDC Endpoint 13 (IN) Control/Status Register */
|
||||
|
||||
#define UDCCS_II_TFS (1 << 0) /* Transmit FIFO service */
|
||||
#define UDCCS_II_TPC (1 << 1) /* Transmit packet complete */
|
||||
#define UDCCS_II_FTF (1 << 2) /* Flush Tx FIFO */
|
||||
#define UDCCS_II_TUR (1 << 3) /* Transmit FIFO underrun */
|
||||
#define UDCCS_II_TSP (1 << 7) /* Transmit short packet */
|
||||
|
||||
/* Isochronous OUT - Endpoint 4,9,14 */
|
||||
#define UDCCS4 __REG(0x40600020) /* UDC Endpoint 4 (OUT) Control/Status Register */
|
||||
#define UDCCS9 __REG(0x40600034) /* UDC Endpoint 9 (OUT) Control/Status Register */
|
||||
#define UDCCS14 __REG(0x40600048) /* UDC Endpoint 14 (OUT) Control/Status Register */
|
||||
|
||||
#define UDCCS_IO_RFS (1 << 0) /* Receive FIFO service */
|
||||
#define UDCCS_IO_RPC (1 << 1) /* Receive packet complete */
|
||||
#define UDCCS_IO_ROF (1 << 2) /* Receive overflow */
|
||||
#define UDCCS_IO_DME (1 << 3) /* DMA enable */
|
||||
#define UDCCS_IO_RNE (1 << 6) /* Receive FIFO not empty */
|
||||
#define UDCCS_IO_RSP (1 << 7) /* Receive short packet */
|
||||
|
||||
/* Interrupt IN - Endpoint 5,10,15 */
|
||||
#define UDCCS5 __REG(0x40600024) /* UDC Endpoint 5 (Interrupt) Control/Status Register */
|
||||
#define UDCCS10 __REG(0x40600038) /* UDC Endpoint 10 (Interrupt) Control/Status Register */
|
||||
#define UDCCS15 __REG(0x4060004C) /* UDC Endpoint 15 (Interrupt) Control/Status Register */
|
||||
|
||||
#define UDCCS_INT_TFS (1 << 0) /* Transmit FIFO service */
|
||||
#define UDCCS_INT_TPC (1 << 1) /* Transmit packet complete */
|
||||
#define UDCCS_INT_FTF (1 << 2) /* Flush Tx FIFO */
|
||||
#define UDCCS_INT_TUR (1 << 3) /* Transmit FIFO underrun */
|
||||
#define UDCCS_INT_SST (1 << 4) /* Sent stall */
|
||||
#define UDCCS_INT_FST (1 << 5) /* Force stall */
|
||||
#define UDCCS_INT_TSP (1 << 7) /* Transmit short packet */
|
||||
|
||||
#define UFNRH __REG(0x40600060) /* UDC Frame Number Register High */
|
||||
#define UFNRL __REG(0x40600064) /* UDC Frame Number Register Low */
|
||||
#define UBCR2 __REG(0x40600068) /* UDC Byte Count Reg 2 */
|
||||
#define UBCR4 __REG(0x4060006c) /* UDC Byte Count Reg 4 */
|
||||
#define UBCR7 __REG(0x40600070) /* UDC Byte Count Reg 7 */
|
||||
#define UBCR9 __REG(0x40600074) /* UDC Byte Count Reg 9 */
|
||||
#define UBCR12 __REG(0x40600078) /* UDC Byte Count Reg 12 */
|
||||
#define UBCR14 __REG(0x4060007c) /* UDC Byte Count Reg 14 */
|
||||
#define UDDR0 __REG(0x40600080) /* UDC Endpoint 0 Data Register */
|
||||
#define UDDR1 __REG(0x40600100) /* UDC Endpoint 1 Data Register */
|
||||
#define UDDR2 __REG(0x40600180) /* UDC Endpoint 2 Data Register */
|
||||
#define UDDR3 __REG(0x40600200) /* UDC Endpoint 3 Data Register */
|
||||
#define UDDR4 __REG(0x40600400) /* UDC Endpoint 4 Data Register */
|
||||
#define UDDR5 __REG(0x406000A0) /* UDC Endpoint 5 Data Register */
|
||||
#define UDDR6 __REG(0x40600600) /* UDC Endpoint 6 Data Register */
|
||||
#define UDDR7 __REG(0x40600680) /* UDC Endpoint 7 Data Register */
|
||||
#define UDDR8 __REG(0x40600700) /* UDC Endpoint 8 Data Register */
|
||||
#define UDDR9 __REG(0x40600900) /* UDC Endpoint 9 Data Register */
|
||||
#define UDDR10 __REG(0x406000C0) /* UDC Endpoint 10 Data Register */
|
||||
#define UDDR11 __REG(0x40600B00) /* UDC Endpoint 11 Data Register */
|
||||
#define UDDR12 __REG(0x40600B80) /* UDC Endpoint 12 Data Register */
|
||||
#define UDDR13 __REG(0x40600C00) /* UDC Endpoint 13 Data Register */
|
||||
#define UDDR14 __REG(0x40600E00) /* UDC Endpoint 14 Data Register */
|
||||
#define UDDR15 __REG(0x406000E0) /* UDC Endpoint 15 Data Register */
|
||||
|
||||
#define UICR0 __REG(0x40600050) /* UDC Interrupt Control Register 0 */
|
||||
|
||||
#define UICR0_IM0 (1 << 0) /* Interrupt mask ep 0 */
|
||||
#define UICR0_IM1 (1 << 1) /* Interrupt mask ep 1 */
|
||||
#define UICR0_IM2 (1 << 2) /* Interrupt mask ep 2 */
|
||||
#define UICR0_IM3 (1 << 3) /* Interrupt mask ep 3 */
|
||||
#define UICR0_IM4 (1 << 4) /* Interrupt mask ep 4 */
|
||||
#define UICR0_IM5 (1 << 5) /* Interrupt mask ep 5 */
|
||||
#define UICR0_IM6 (1 << 6) /* Interrupt mask ep 6 */
|
||||
#define UICR0_IM7 (1 << 7) /* Interrupt mask ep 7 */
|
||||
|
||||
#define UICR1 __REG(0x40600054) /* UDC Interrupt Control Register 1 */
|
||||
|
||||
#define UICR1_IM8 (1 << 0) /* Interrupt mask ep 8 */
|
||||
#define UICR1_IM9 (1 << 1) /* Interrupt mask ep 9 */
|
||||
#define UICR1_IM10 (1 << 2) /* Interrupt mask ep 10 */
|
||||
#define UICR1_IM11 (1 << 3) /* Interrupt mask ep 11 */
|
||||
#define UICR1_IM12 (1 << 4) /* Interrupt mask ep 12 */
|
||||
#define UICR1_IM13 (1 << 5) /* Interrupt mask ep 13 */
|
||||
#define UICR1_IM14 (1 << 6) /* Interrupt mask ep 14 */
|
||||
#define UICR1_IM15 (1 << 7) /* Interrupt mask ep 15 */
|
||||
|
||||
#define USIR0 __REG(0x40600058) /* UDC Status Interrupt Register 0 */
|
||||
|
||||
#define USIR0_IR0 (1 << 0) /* Interrupt request ep 0 */
|
||||
#define USIR0_IR1 (1 << 1) /* Interrupt request ep 1 */
|
||||
#define USIR0_IR2 (1 << 2) /* Interrupt request ep 2 */
|
||||
#define USIR0_IR3 (1 << 3) /* Interrupt request ep 3 */
|
||||
#define USIR0_IR4 (1 << 4) /* Interrupt request ep 4 */
|
||||
#define USIR0_IR5 (1 << 5) /* Interrupt request ep 5 */
|
||||
#define USIR0_IR6 (1 << 6) /* Interrupt request ep 6 */
|
||||
#define USIR0_IR7 (1 << 7) /* Interrupt request ep 7 */
|
||||
|
||||
#define USIR1 __REG(0x4060005C) /* UDC Status Interrupt Register 1 */
|
||||
|
||||
#define USIR1_IR8 (1 << 0) /* Interrupt request ep 8 */
|
||||
#define USIR1_IR9 (1 << 1) /* Interrupt request ep 9 */
|
||||
#define USIR1_IR10 (1 << 2) /* Interrupt request ep 10 */
|
||||
#define USIR1_IR11 (1 << 3) /* Interrupt request ep 11 */
|
||||
#define USIR1_IR12 (1 << 4) /* Interrupt request ep 12 */
|
||||
#define USIR1_IR13 (1 << 5) /* Interrupt request ep 13 */
|
||||
#define USIR1_IR14 (1 << 6) /* Interrupt request ep 14 */
|
||||
#define USIR1_IR15 (1 << 7) /* Interrupt request ep 15 */
|
||||
|
||||
#endif
|
|
@ -32,7 +32,7 @@ config PHY_BERLIN_SATA
|
|||
config ARMADA375_USBCLUSTER_PHY
|
||||
def_bool y
|
||||
depends on MACH_ARMADA_375 || COMPILE_TEST
|
||||
depends on OF
|
||||
depends on OF && HAS_IOMEM
|
||||
select GENERIC_PHY
|
||||
|
||||
config PHY_DM816X_USB
|
||||
|
@ -337,6 +337,20 @@ config PHY_ROCKCHIP_USB
|
|||
help
|
||||
Enable this to support the Rockchip USB 2.0 PHY.
|
||||
|
||||
config PHY_ROCKCHIP_EMMC
|
||||
tristate "Rockchip EMMC PHY Driver"
|
||||
depends on ARCH_ROCKCHIP && OF
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Enable this to support the Rockchip EMMC PHY.
|
||||
|
||||
config PHY_ROCKCHIP_DP
|
||||
tristate "Rockchip Display Port PHY Driver"
|
||||
depends on ARCH_ROCKCHIP && OF
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Enable this to support the Rockchip Display Port PHY.
|
||||
|
||||
config PHY_ST_SPEAR1310_MIPHY
|
||||
tristate "ST SPEAR1310-MIPHY driver"
|
||||
select GENERIC_PHY
|
||||
|
|
|
@ -37,6 +37,8 @@ phy-exynos-usb2-$(CONFIG_PHY_S5PV210_USB2) += phy-s5pv210-usb2.o
|
|||
obj-$(CONFIG_PHY_EXYNOS5_USBDRD) += phy-exynos5-usbdrd.o
|
||||
obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o
|
||||
obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o
|
||||
obj-$(CONFIG_PHY_ROCKCHIP_EMMC) += phy-rockchip-emmc.o
|
||||
obj-$(CONFIG_PHY_ROCKCHIP_DP) += phy-rockchip-dp.o
|
||||
obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o
|
||||
obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY) += phy-spear1310-miphy.o
|
||||
obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY) += phy-spear1340-miphy.o
|
||||
|
|
|
@ -118,7 +118,7 @@ static const struct phy_ops ops = {
|
|||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int dm816x_usb_phy_runtime_suspend(struct device *dev)
|
||||
static int __maybe_unused dm816x_usb_phy_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct dm816x_usb_phy *phy = dev_get_drvdata(dev);
|
||||
unsigned int mask, val;
|
||||
|
@ -136,7 +136,7 @@ static int dm816x_usb_phy_runtime_suspend(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int dm816x_usb_phy_runtime_resume(struct device *dev)
|
||||
static int __maybe_unused dm816x_usb_phy_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct dm816x_usb_phy *phy = dev_get_drvdata(dev);
|
||||
unsigned int mask, val;
|
||||
|
|
|
@ -74,20 +74,6 @@
|
|||
#define USB2_ADPCTRL_IDPULLUP BIT(5) /* 1 = ID sampling is enabled */
|
||||
#define USB2_ADPCTRL_DRVVBUS BIT(4)
|
||||
|
||||
/******* HSUSB registers (original offset is +0x100) *******/
|
||||
#define HSUSB_LPSTS 0x02
|
||||
#define HSUSB_UGCTRL2 0x84
|
||||
|
||||
/* Low Power Status register (LPSTS) */
|
||||
#define HSUSB_LPSTS_SUSPM 0x4000
|
||||
|
||||
/* USB General control register 2 (UGCTRL2) */
|
||||
#define HSUSB_UGCTRL2_MASK 0x00000031 /* bit[31:6] should be 0 */
|
||||
#define HSUSB_UGCTRL2_USB0SEL 0x00000030
|
||||
#define HSUSB_UGCTRL2_USB0SEL_HOST 0x00000010
|
||||
#define HSUSB_UGCTRL2_USB0SEL_HS_USB 0x00000020
|
||||
#define HSUSB_UGCTRL2_USB0SEL_OTG 0x00000030
|
||||
|
||||
struct rcar_gen3_data {
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
|
@ -95,8 +81,8 @@ struct rcar_gen3_data {
|
|||
|
||||
struct rcar_gen3_chan {
|
||||
struct rcar_gen3_data usb2;
|
||||
struct rcar_gen3_data hsusb;
|
||||
struct phy *phy;
|
||||
bool has_otg;
|
||||
};
|
||||
|
||||
static void rcar_gen3_set_host_mode(struct rcar_gen3_chan *ch, int host)
|
||||
|
@ -202,24 +188,15 @@ static int rcar_gen3_phy_usb2_init(struct phy *p)
|
|||
{
|
||||
struct rcar_gen3_chan *channel = phy_get_drvdata(p);
|
||||
void __iomem *usb2_base = channel->usb2.base;
|
||||
void __iomem *hsusb_base = channel->hsusb.base;
|
||||
u32 val;
|
||||
|
||||
/* Initialize USB2 part */
|
||||
writel(USB2_INT_ENABLE_INIT, usb2_base + USB2_INT_ENABLE);
|
||||
writel(USB2_SPD_RSM_TIMSET_INIT, usb2_base + USB2_SPD_RSM_TIMSET);
|
||||
writel(USB2_OC_TIMSET_INIT, usb2_base + USB2_OC_TIMSET);
|
||||
|
||||
/* Initialize HSUSB part */
|
||||
if (hsusb_base) {
|
||||
val = readl(hsusb_base + HSUSB_UGCTRL2);
|
||||
val = (val & ~HSUSB_UGCTRL2_USB0SEL) |
|
||||
HSUSB_UGCTRL2_USB0SEL_OTG;
|
||||
writel(val & HSUSB_UGCTRL2_MASK, hsusb_base + HSUSB_UGCTRL2);
|
||||
|
||||
/* Initialize otg part */
|
||||
/* Initialize otg part */
|
||||
if (channel->has_otg)
|
||||
rcar_gen3_init_otg(channel);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -237,7 +214,6 @@ static int rcar_gen3_phy_usb2_power_on(struct phy *p)
|
|||
{
|
||||
struct rcar_gen3_chan *channel = phy_get_drvdata(p);
|
||||
void __iomem *usb2_base = channel->usb2.base;
|
||||
void __iomem *hsusb_base = channel->hsusb.base;
|
||||
u32 val;
|
||||
|
||||
val = readl(usb2_base + USB2_USBCTR);
|
||||
|
@ -246,33 +222,6 @@ static int rcar_gen3_phy_usb2_power_on(struct phy *p)
|
|||
val &= ~USB2_USBCTR_PLL_RST;
|
||||
writel(val, usb2_base + USB2_USBCTR);
|
||||
|
||||
/*
|
||||
* TODO: To reduce power consuming, this driver should set the SUSPM
|
||||
* after the PHY detects ID pin as peripheral.
|
||||
*/
|
||||
if (hsusb_base) {
|
||||
/* Power on HSUSB PHY */
|
||||
val = readw(hsusb_base + HSUSB_LPSTS);
|
||||
val |= HSUSB_LPSTS_SUSPM;
|
||||
writew(val, hsusb_base + HSUSB_LPSTS);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcar_gen3_phy_usb2_power_off(struct phy *p)
|
||||
{
|
||||
struct rcar_gen3_chan *channel = phy_get_drvdata(p);
|
||||
void __iomem *hsusb_base = channel->hsusb.base;
|
||||
u32 val;
|
||||
|
||||
if (hsusb_base) {
|
||||
/* Power off HSUSB PHY */
|
||||
val = readw(hsusb_base + HSUSB_LPSTS);
|
||||
val &= ~HSUSB_LPSTS_SUSPM;
|
||||
writew(val, hsusb_base + HSUSB_LPSTS);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -280,7 +229,6 @@ static struct phy_ops rcar_gen3_phy_usb2_ops = {
|
|||
.init = rcar_gen3_phy_usb2_init,
|
||||
.exit = rcar_gen3_phy_usb2_exit,
|
||||
.power_on = rcar_gen3_phy_usb2_power_on,
|
||||
.power_off = rcar_gen3_phy_usb2_power_off,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
@ -313,6 +261,7 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
|
|||
struct rcar_gen3_chan *channel;
|
||||
struct phy_provider *provider;
|
||||
struct resource *res;
|
||||
int irq;
|
||||
|
||||
if (!dev->of_node) {
|
||||
dev_err(dev, "This driver needs device tree\n");
|
||||
|
@ -323,29 +272,19 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
|
|||
if (!channel)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "usb2_host");
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
channel->usb2.base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(channel->usb2.base))
|
||||
return PTR_ERR(channel->usb2.base);
|
||||
|
||||
/* "hsusb" memory resource is optional */
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hsusb");
|
||||
|
||||
/* To avoid error message by devm_ioremap_resource() */
|
||||
if (res) {
|
||||
int irq;
|
||||
|
||||
channel->hsusb.base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(channel->hsusb.base))
|
||||
channel->hsusb.base = NULL;
|
||||
/* call request_irq for OTG */
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq >= 0)
|
||||
irq = devm_request_irq(dev, irq, rcar_gen3_phy_usb2_irq,
|
||||
IRQF_SHARED, dev_name(dev),
|
||||
channel);
|
||||
/* call request_irq for OTG */
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq >= 0) {
|
||||
irq = devm_request_irq(dev, irq, rcar_gen3_phy_usb2_irq,
|
||||
IRQF_SHARED, dev_name(dev), channel);
|
||||
if (irq < 0)
|
||||
dev_err(dev, "No irq handler (%d)\n", irq);
|
||||
channel->has_otg = true;
|
||||
}
|
||||
|
||||
/* devm_phy_create() will call pm_runtime_enable(dev); */
|
||||
|
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* Rockchip DP PHY driver
|
||||
*
|
||||
* Copyright (C) 2016 FuZhou Rockchip Co., Ltd.
|
||||
* Author: Yakir Yang <ykk@@rock-chips.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define GRF_SOC_CON12 0x0274
|
||||
|
||||
#define GRF_EDP_REF_CLK_SEL_INTER_HIWORD_MASK BIT(20)
|
||||
#define GRF_EDP_REF_CLK_SEL_INTER BIT(4)
|
||||
|
||||
#define GRF_EDP_PHY_SIDDQ_HIWORD_MASK BIT(21)
|
||||
#define GRF_EDP_PHY_SIDDQ_ON 0
|
||||
#define GRF_EDP_PHY_SIDDQ_OFF BIT(5)
|
||||
|
||||
struct rockchip_dp_phy {
|
||||
struct device *dev;
|
||||
struct regmap *grf;
|
||||
struct clk *phy_24m;
|
||||
};
|
||||
|
||||
static int rockchip_set_phy_state(struct phy *phy, bool enable)
|
||||
{
|
||||
struct rockchip_dp_phy *dp = phy_get_drvdata(phy);
|
||||
int ret;
|
||||
|
||||
if (enable) {
|
||||
ret = regmap_write(dp->grf, GRF_SOC_CON12,
|
||||
GRF_EDP_PHY_SIDDQ_HIWORD_MASK |
|
||||
GRF_EDP_PHY_SIDDQ_ON);
|
||||
if (ret < 0) {
|
||||
dev_err(dp->dev, "Can't enable PHY power %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(dp->phy_24m);
|
||||
} else {
|
||||
clk_disable_unprepare(dp->phy_24m);
|
||||
|
||||
ret = regmap_write(dp->grf, GRF_SOC_CON12,
|
||||
GRF_EDP_PHY_SIDDQ_HIWORD_MASK |
|
||||
GRF_EDP_PHY_SIDDQ_OFF);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rockchip_dp_phy_power_on(struct phy *phy)
|
||||
{
|
||||
return rockchip_set_phy_state(phy, true);
|
||||
}
|
||||
|
||||
static int rockchip_dp_phy_power_off(struct phy *phy)
|
||||
{
|
||||
return rockchip_set_phy_state(phy, false);
|
||||
}
|
||||
|
||||
static const struct phy_ops rockchip_dp_phy_ops = {
|
||||
.power_on = rockchip_dp_phy_power_on,
|
||||
.power_off = rockchip_dp_phy_power_off,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int rockchip_dp_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct phy_provider *phy_provider;
|
||||
struct rockchip_dp_phy *dp;
|
||||
struct phy *phy;
|
||||
int ret;
|
||||
|
||||
if (!np)
|
||||
return -ENODEV;
|
||||
|
||||
dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
|
||||
if (IS_ERR(dp))
|
||||
return -ENOMEM;
|
||||
|
||||
dp->dev = dev;
|
||||
|
||||
dp->phy_24m = devm_clk_get(dev, "24m");
|
||||
if (IS_ERR(dp->phy_24m)) {
|
||||
dev_err(dev, "cannot get clock 24m\n");
|
||||
return PTR_ERR(dp->phy_24m);
|
||||
}
|
||||
|
||||
ret = clk_set_rate(dp->phy_24m, 24000000);
|
||||
if (ret < 0) {
|
||||
dev_err(dp->dev, "cannot set clock phy_24m %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
|
||||
if (IS_ERR(dp->grf)) {
|
||||
dev_err(dev, "rk3288-dp needs rockchip,grf property\n");
|
||||
return PTR_ERR(dp->grf);
|
||||
}
|
||||
|
||||
ret = regmap_write(dp->grf, GRF_SOC_CON12, GRF_EDP_REF_CLK_SEL_INTER |
|
||||
GRF_EDP_REF_CLK_SEL_INTER_HIWORD_MASK);
|
||||
if (ret != 0) {
|
||||
dev_err(dp->dev, "Could not config GRF edp ref clk: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
phy = devm_phy_create(dev, np, &rockchip_dp_phy_ops);
|
||||
if (IS_ERR(phy)) {
|
||||
dev_err(dev, "failed to create phy\n");
|
||||
return PTR_ERR(phy);
|
||||
}
|
||||
phy_set_drvdata(phy, dp);
|
||||
|
||||
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
|
||||
return PTR_ERR_OR_ZERO(phy_provider);
|
||||
}
|
||||
|
||||
static const struct of_device_id rockchip_dp_phy_dt_ids[] = {
|
||||
{ .compatible = "rockchip,rk3288-dp-phy" },
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, rockchip_dp_phy_dt_ids);
|
||||
|
||||
static struct platform_driver rockchip_dp_phy_driver = {
|
||||
.probe = rockchip_dp_phy_probe,
|
||||
.driver = {
|
||||
.name = "rockchip-dp-phy",
|
||||
.of_match_table = rockchip_dp_phy_dt_ids,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(rockchip_dp_phy_driver);
|
||||
|
||||
MODULE_AUTHOR("Yakir Yang <ykk@rock-chips.com>");
|
||||
MODULE_DESCRIPTION("Rockchip DP PHY driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,229 @@
|
|||
/*
|
||||
* Rockchip emmc PHY driver
|
||||
*
|
||||
* Copyright (C) 2016 Shawn Lin <shawn.lin@rock-chips.com>
|
||||
* Copyright (C) 2016 ROCKCHIP, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/*
|
||||
* The higher 16-bit of this register is used for write protection
|
||||
* only if BIT(x + 16) set to 1 the BIT(x) can be written.
|
||||
*/
|
||||
#define HIWORD_UPDATE(val, mask, shift) \
|
||||
((val) << (shift) | (mask) << ((shift) + 16))
|
||||
|
||||
/* Register definition */
|
||||
#define GRF_EMMCPHY_CON0 0x0
|
||||
#define GRF_EMMCPHY_CON1 0x4
|
||||
#define GRF_EMMCPHY_CON2 0x8
|
||||
#define GRF_EMMCPHY_CON3 0xc
|
||||
#define GRF_EMMCPHY_CON4 0x10
|
||||
#define GRF_EMMCPHY_CON5 0x14
|
||||
#define GRF_EMMCPHY_CON6 0x18
|
||||
#define GRF_EMMCPHY_STATUS 0x20
|
||||
|
||||
#define PHYCTRL_PDB_MASK 0x1
|
||||
#define PHYCTRL_PDB_SHIFT 0x0
|
||||
#define PHYCTRL_PDB_PWR_ON 0x1
|
||||
#define PHYCTRL_PDB_PWR_OFF 0x0
|
||||
#define PHYCTRL_ENDLL_MASK 0x1
|
||||
#define PHYCTRL_ENDLL_SHIFT 0x1
|
||||
#define PHYCTRL_ENDLL_ENABLE 0x1
|
||||
#define PHYCTRL_ENDLL_DISABLE 0x0
|
||||
#define PHYCTRL_CALDONE_MASK 0x1
|
||||
#define PHYCTRL_CALDONE_SHIFT 0x6
|
||||
#define PHYCTRL_CALDONE_DONE 0x1
|
||||
#define PHYCTRL_CALDONE_GOING 0x0
|
||||
#define PHYCTRL_DLLRDY_MASK 0x1
|
||||
#define PHYCTRL_DLLRDY_SHIFT 0x5
|
||||
#define PHYCTRL_DLLRDY_DONE 0x1
|
||||
#define PHYCTRL_DLLRDY_GOING 0x0
|
||||
|
||||
struct rockchip_emmc_phy {
|
||||
unsigned int reg_offset;
|
||||
struct regmap *reg_base;
|
||||
};
|
||||
|
||||
static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy,
|
||||
bool on_off)
|
||||
{
|
||||
unsigned int caldone;
|
||||
unsigned int dllrdy;
|
||||
|
||||
/*
|
||||
* Keep phyctrl_pdb and phyctrl_endll low to allow
|
||||
* initialization of CALIO state M/C DFFs
|
||||
*/
|
||||
regmap_write(rk_phy->reg_base,
|
||||
rk_phy->reg_offset + GRF_EMMCPHY_CON6,
|
||||
HIWORD_UPDATE(PHYCTRL_PDB_PWR_OFF,
|
||||
PHYCTRL_PDB_MASK,
|
||||
PHYCTRL_PDB_SHIFT));
|
||||
regmap_write(rk_phy->reg_base,
|
||||
rk_phy->reg_offset + GRF_EMMCPHY_CON6,
|
||||
HIWORD_UPDATE(PHYCTRL_ENDLL_DISABLE,
|
||||
PHYCTRL_ENDLL_MASK,
|
||||
PHYCTRL_ENDLL_SHIFT));
|
||||
|
||||
/* Already finish power_off above */
|
||||
if (on_off == PHYCTRL_PDB_PWR_OFF)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* According to the user manual, calpad calibration
|
||||
* cycle takes more than 2us without the minimal recommended
|
||||
* value, so we may need a little margin here
|
||||
*/
|
||||
udelay(3);
|
||||
regmap_write(rk_phy->reg_base,
|
||||
rk_phy->reg_offset + GRF_EMMCPHY_CON6,
|
||||
HIWORD_UPDATE(PHYCTRL_PDB_PWR_ON,
|
||||
PHYCTRL_PDB_MASK,
|
||||
PHYCTRL_PDB_SHIFT));
|
||||
|
||||
/*
|
||||
* According to the user manual, it asks driver to
|
||||
* wait 5us for calpad busy trimming
|
||||
*/
|
||||
udelay(5);
|
||||
regmap_read(rk_phy->reg_base,
|
||||
rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
|
||||
&caldone);
|
||||
caldone = (caldone >> PHYCTRL_CALDONE_SHIFT) & PHYCTRL_CALDONE_MASK;
|
||||
if (caldone != PHYCTRL_CALDONE_DONE) {
|
||||
pr_err("rockchip_emmc_phy_power: caldone timeout.\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
regmap_write(rk_phy->reg_base,
|
||||
rk_phy->reg_offset + GRF_EMMCPHY_CON6,
|
||||
HIWORD_UPDATE(PHYCTRL_ENDLL_ENABLE,
|
||||
PHYCTRL_ENDLL_MASK,
|
||||
PHYCTRL_ENDLL_SHIFT));
|
||||
/*
|
||||
* After enable analog DLL circuits, we need extra 10.2us
|
||||
* for dll to be ready for work.
|
||||
*/
|
||||
udelay(11);
|
||||
regmap_read(rk_phy->reg_base,
|
||||
rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
|
||||
&dllrdy);
|
||||
dllrdy = (dllrdy >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK;
|
||||
if (dllrdy != PHYCTRL_DLLRDY_DONE) {
|
||||
pr_err("rockchip_emmc_phy_power: dllrdy timeout.\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_emmc_phy_power_off(struct phy *phy)
|
||||
{
|
||||
struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);
|
||||
int ret = 0;
|
||||
|
||||
/* Power down emmc phy analog blocks */
|
||||
ret = rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_OFF);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_emmc_phy_power_on(struct phy *phy)
|
||||
{
|
||||
struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);
|
||||
int ret = 0;
|
||||
|
||||
/* Power up emmc phy analog blocks */
|
||||
ret = rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_ON);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct phy_ops ops = {
|
||||
.power_on = rockchip_emmc_phy_power_on,
|
||||
.power_off = rockchip_emmc_phy_power_off,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int rockchip_emmc_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct rockchip_emmc_phy *rk_phy;
|
||||
struct phy *generic_phy;
|
||||
struct phy_provider *phy_provider;
|
||||
struct regmap *grf;
|
||||
unsigned int reg_offset;
|
||||
|
||||
grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
|
||||
if (IS_ERR(grf)) {
|
||||
dev_err(dev, "Missing rockchip,grf property\n");
|
||||
return PTR_ERR(grf);
|
||||
}
|
||||
|
||||
rk_phy = devm_kzalloc(dev, sizeof(*rk_phy), GFP_KERNEL);
|
||||
if (!rk_phy)
|
||||
return -ENOMEM;
|
||||
|
||||
if (of_property_read_u32(dev->of_node, "reg", ®_offset)) {
|
||||
dev_err(dev, "missing reg property in node %s\n",
|
||||
dev->of_node->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rk_phy->reg_offset = reg_offset;
|
||||
rk_phy->reg_base = grf;
|
||||
|
||||
generic_phy = devm_phy_create(dev, dev->of_node, &ops);
|
||||
if (IS_ERR(generic_phy)) {
|
||||
dev_err(dev, "failed to create PHY\n");
|
||||
return PTR_ERR(generic_phy);
|
||||
}
|
||||
|
||||
phy_set_drvdata(generic_phy, rk_phy);
|
||||
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
|
||||
return PTR_ERR_OR_ZERO(phy_provider);
|
||||
}
|
||||
|
||||
static const struct of_device_id rockchip_emmc_phy_dt_ids[] = {
|
||||
{ .compatible = "rockchip,rk3399-emmc-phy" },
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, rockchip_emmc_phy_dt_ids);
|
||||
|
||||
static struct platform_driver rockchip_emmc_driver = {
|
||||
.probe = rockchip_emmc_phy_probe,
|
||||
.driver = {
|
||||
.name = "rockchip-emmc-phy",
|
||||
.of_match_table = rockchip_emmc_phy_dt_ids,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(rockchip_emmc_driver);
|
||||
|
||||
MODULE_AUTHOR("Shawn Lin <shawn.lin@rock-chips.com>");
|
||||
MODULE_DESCRIPTION("Rockchip EMMC PHY driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -30,21 +30,23 @@
|
|||
#include <linux/regmap.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
|
||||
/*
|
||||
* The higher 16-bit of this register is used for write protection
|
||||
* only if BIT(13 + 16) set to 1 the BIT(13) can be written.
|
||||
*/
|
||||
#define SIDDQ_WRITE_ENA BIT(29)
|
||||
#define SIDDQ_ON BIT(13)
|
||||
#define SIDDQ_OFF (0 << 13)
|
||||
static int enable_usb_uart;
|
||||
|
||||
#define HIWORD_UPDATE(val, mask) \
|
||||
((val) | (mask) << 16)
|
||||
|
||||
#define UOC_CON0_SIDDQ BIT(13)
|
||||
|
||||
struct rockchip_usb_phys {
|
||||
int reg;
|
||||
const char *pll_name;
|
||||
};
|
||||
|
||||
struct rockchip_usb_phy_base;
|
||||
struct rockchip_usb_phy_pdata {
|
||||
struct rockchip_usb_phys *phys;
|
||||
int (*init_usb_uart)(struct regmap *grf);
|
||||
int usb_uart_phy;
|
||||
};
|
||||
|
||||
struct rockchip_usb_phy_base {
|
||||
|
@ -61,13 +63,15 @@ struct rockchip_usb_phy {
|
|||
struct clk *clk480m;
|
||||
struct clk_hw clk480m_hw;
|
||||
struct phy *phy;
|
||||
bool uart_enabled;
|
||||
};
|
||||
|
||||
static int rockchip_usb_phy_power(struct rockchip_usb_phy *phy,
|
||||
bool siddq)
|
||||
{
|
||||
return regmap_write(phy->base->reg_base, phy->reg_offset,
|
||||
SIDDQ_WRITE_ENA | (siddq ? SIDDQ_ON : SIDDQ_OFF));
|
||||
u32 val = HIWORD_UPDATE(siddq ? UOC_CON0_SIDDQ : 0, UOC_CON0_SIDDQ);
|
||||
|
||||
return regmap_write(phy->base->reg_base, phy->reg_offset, val);
|
||||
}
|
||||
|
||||
static unsigned long rockchip_usb_phy480m_recalc_rate(struct clk_hw *hw,
|
||||
|
@ -108,7 +112,7 @@ static int rockchip_usb_phy480m_is_enabled(struct clk_hw *hw)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return (val & SIDDQ_ON) ? 0 : 1;
|
||||
return (val & UOC_CON0_SIDDQ) ? 0 : 1;
|
||||
}
|
||||
|
||||
static const struct clk_ops rockchip_usb_phy480m_ops = {
|
||||
|
@ -122,6 +126,9 @@ static int rockchip_usb_phy_power_off(struct phy *_phy)
|
|||
{
|
||||
struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
|
||||
|
||||
if (phy->uart_enabled)
|
||||
return -EBUSY;
|
||||
|
||||
clk_disable_unprepare(phy->clk480m);
|
||||
|
||||
return 0;
|
||||
|
@ -131,6 +138,9 @@ static int rockchip_usb_phy_power_on(struct phy *_phy)
|
|||
{
|
||||
struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
|
||||
|
||||
if (phy->uart_enabled)
|
||||
return -EBUSY;
|
||||
|
||||
return clk_prepare_enable(phy->clk480m);
|
||||
}
|
||||
|
||||
|
@ -144,8 +154,10 @@ static void rockchip_usb_phy_action(void *data)
|
|||
{
|
||||
struct rockchip_usb_phy *rk_phy = data;
|
||||
|
||||
of_clk_del_provider(rk_phy->np);
|
||||
clk_unregister(rk_phy->clk480m);
|
||||
if (!rk_phy->uart_enabled) {
|
||||
of_clk_del_provider(rk_phy->np);
|
||||
clk_unregister(rk_phy->clk480m);
|
||||
}
|
||||
|
||||
if (rk_phy->clk)
|
||||
clk_put(rk_phy->clk);
|
||||
|
@ -194,31 +206,36 @@ static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (rk_phy->clk) {
|
||||
clk_name = __clk_get_name(rk_phy->clk);
|
||||
init.flags = 0;
|
||||
init.parent_names = &clk_name;
|
||||
init.num_parents = 1;
|
||||
if (enable_usb_uart && base->pdata->usb_uart_phy == i) {
|
||||
dev_dbg(base->dev, "phy%d used as uart output\n", i);
|
||||
rk_phy->uart_enabled = true;
|
||||
} else {
|
||||
init.flags = CLK_IS_ROOT;
|
||||
init.parent_names = NULL;
|
||||
init.num_parents = 0;
|
||||
if (rk_phy->clk) {
|
||||
clk_name = __clk_get_name(rk_phy->clk);
|
||||
init.flags = 0;
|
||||
init.parent_names = &clk_name;
|
||||
init.num_parents = 1;
|
||||
} else {
|
||||
init.flags = CLK_IS_ROOT;
|
||||
init.parent_names = NULL;
|
||||
init.num_parents = 0;
|
||||
}
|
||||
|
||||
init.ops = &rockchip_usb_phy480m_ops;
|
||||
rk_phy->clk480m_hw.init = &init;
|
||||
|
||||
rk_phy->clk480m = clk_register(base->dev, &rk_phy->clk480m_hw);
|
||||
if (IS_ERR(rk_phy->clk480m)) {
|
||||
err = PTR_ERR(rk_phy->clk480m);
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
err = of_clk_add_provider(child, of_clk_src_simple_get,
|
||||
rk_phy->clk480m);
|
||||
if (err < 0)
|
||||
goto err_clk_prov;
|
||||
}
|
||||
|
||||
init.ops = &rockchip_usb_phy480m_ops;
|
||||
rk_phy->clk480m_hw.init = &init;
|
||||
|
||||
rk_phy->clk480m = clk_register(base->dev, &rk_phy->clk480m_hw);
|
||||
if (IS_ERR(rk_phy->clk480m)) {
|
||||
err = PTR_ERR(rk_phy->clk480m);
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
err = of_clk_add_provider(child, of_clk_src_simple_get,
|
||||
rk_phy->clk480m);
|
||||
if (err < 0)
|
||||
goto err_clk_prov;
|
||||
|
||||
err = devm_add_action(base->dev, rockchip_usb_phy_action, rk_phy);
|
||||
if (err)
|
||||
goto err_devm_action;
|
||||
|
@ -230,13 +247,21 @@ static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base,
|
|||
}
|
||||
phy_set_drvdata(rk_phy->phy, rk_phy);
|
||||
|
||||
/* only power up usb phy when it use, so disable it when init*/
|
||||
return rockchip_usb_phy_power(rk_phy, 1);
|
||||
/*
|
||||
* When acting as uart-pipe, just keep clock on otherwise
|
||||
* only power up usb phy when it use, so disable it when init
|
||||
*/
|
||||
if (rk_phy->uart_enabled)
|
||||
return clk_prepare_enable(rk_phy->clk);
|
||||
else
|
||||
return rockchip_usb_phy_power(rk_phy, 1);
|
||||
|
||||
err_devm_action:
|
||||
of_clk_del_provider(child);
|
||||
if (!rk_phy->uart_enabled)
|
||||
of_clk_del_provider(child);
|
||||
err_clk_prov:
|
||||
clk_unregister(rk_phy->clk480m);
|
||||
if (!rk_phy->uart_enabled)
|
||||
clk_unregister(rk_phy->clk480m);
|
||||
err_clk:
|
||||
if (rk_phy->clk)
|
||||
clk_put(rk_phy->clk);
|
||||
|
@ -259,6 +284,86 @@ static const struct rockchip_usb_phy_pdata rk3188_pdata = {
|
|||
},
|
||||
};
|
||||
|
||||
#define RK3288_UOC0_CON0 0x320
|
||||
#define RK3288_UOC0_CON0_COMMON_ON_N BIT(0)
|
||||
#define RK3288_UOC0_CON0_DISABLE BIT(4)
|
||||
|
||||
#define RK3288_UOC0_CON2 0x328
|
||||
#define RK3288_UOC0_CON2_SOFT_CON_SEL BIT(2)
|
||||
|
||||
#define RK3288_UOC0_CON3 0x32c
|
||||
#define RK3288_UOC0_CON3_UTMI_SUSPENDN BIT(0)
|
||||
#define RK3288_UOC0_CON3_UTMI_OPMODE_NODRIVING (1 << 1)
|
||||
#define RK3288_UOC0_CON3_UTMI_OPMODE_MASK (3 << 1)
|
||||
#define RK3288_UOC0_CON3_UTMI_XCVRSEELCT_FSTRANSC (1 << 3)
|
||||
#define RK3288_UOC0_CON3_UTMI_XCVRSEELCT_MASK (3 << 3)
|
||||
#define RK3288_UOC0_CON3_UTMI_TERMSEL_FULLSPEED BIT(5)
|
||||
#define RK3288_UOC0_CON3_BYPASSDMEN BIT(6)
|
||||
#define RK3288_UOC0_CON3_BYPASSSEL BIT(7)
|
||||
|
||||
/*
|
||||
* Enable the bypass of uart2 data through the otg usb phy.
|
||||
* Original description in the TRM.
|
||||
* 1. Disable the OTG block by setting OTGDISABLE0 to 1’b1.
|
||||
* 2. Disable the pull-up resistance on the D+ line by setting
|
||||
* OPMODE0[1:0] to 2’b01.
|
||||
* 3. To ensure that the XO, Bias, and PLL blocks are powered down in Suspend
|
||||
* mode, set COMMONONN to 1’b1.
|
||||
* 4. Place the USB PHY in Suspend mode by setting SUSPENDM0 to 1’b0.
|
||||
* 5. Set BYPASSSEL0 to 1’b1.
|
||||
* 6. To transmit data, controls BYPASSDMEN0, and BYPASSDMDATA0.
|
||||
* To receive data, monitor FSVPLUS0.
|
||||
*
|
||||
* The actual code in the vendor kernel does some things differently.
|
||||
*/
|
||||
static int __init rk3288_init_usb_uart(struct regmap *grf)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* COMMON_ON and DISABLE settings are described in the TRM,
|
||||
* but were not present in the original code.
|
||||
* Also disable the analog phy components to save power.
|
||||
*/
|
||||
val = HIWORD_UPDATE(RK3288_UOC0_CON0_COMMON_ON_N
|
||||
| RK3288_UOC0_CON0_DISABLE
|
||||
| UOC_CON0_SIDDQ,
|
||||
RK3288_UOC0_CON0_COMMON_ON_N
|
||||
| RK3288_UOC0_CON0_DISABLE
|
||||
| UOC_CON0_SIDDQ);
|
||||
ret = regmap_write(grf, RK3288_UOC0_CON0, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val = HIWORD_UPDATE(RK3288_UOC0_CON2_SOFT_CON_SEL,
|
||||
RK3288_UOC0_CON2_SOFT_CON_SEL);
|
||||
ret = regmap_write(grf, RK3288_UOC0_CON2, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val = HIWORD_UPDATE(RK3288_UOC0_CON3_UTMI_OPMODE_NODRIVING
|
||||
| RK3288_UOC0_CON3_UTMI_XCVRSEELCT_FSTRANSC
|
||||
| RK3288_UOC0_CON3_UTMI_TERMSEL_FULLSPEED,
|
||||
RK3288_UOC0_CON3_UTMI_SUSPENDN
|
||||
| RK3288_UOC0_CON3_UTMI_OPMODE_MASK
|
||||
| RK3288_UOC0_CON3_UTMI_XCVRSEELCT_MASK
|
||||
| RK3288_UOC0_CON3_UTMI_TERMSEL_FULLSPEED);
|
||||
ret = regmap_write(grf, RK3288_UOC0_CON3, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val = HIWORD_UPDATE(RK3288_UOC0_CON3_BYPASSSEL
|
||||
| RK3288_UOC0_CON3_BYPASSDMEN,
|
||||
RK3288_UOC0_CON3_BYPASSSEL
|
||||
| RK3288_UOC0_CON3_BYPASSDMEN);
|
||||
ret = regmap_write(grf, RK3288_UOC0_CON3, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct rockchip_usb_phy_pdata rk3288_pdata = {
|
||||
.phys = (struct rockchip_usb_phys[]){
|
||||
{ .reg = 0x320, .pll_name = "sclk_otgphy0_480m" },
|
||||
|
@ -266,6 +371,8 @@ static const struct rockchip_usb_phy_pdata rk3288_pdata = {
|
|||
{ .reg = 0x348, .pll_name = "sclk_otgphy2_480m" },
|
||||
{ /* sentinel */ }
|
||||
},
|
||||
.init_usb_uart = rk3288_init_usb_uart,
|
||||
.usb_uart_phy = 0,
|
||||
};
|
||||
|
||||
static int rockchip_usb_phy_probe(struct platform_device *pdev)
|
||||
|
@ -328,6 +435,60 @@ static struct platform_driver rockchip_usb_driver = {
|
|||
|
||||
module_platform_driver(rockchip_usb_driver);
|
||||
|
||||
#ifndef MODULE
|
||||
static int __init rockchip_init_usb_uart(void)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
const struct rockchip_usb_phy_pdata *data;
|
||||
struct device_node *np;
|
||||
struct regmap *grf;
|
||||
int ret;
|
||||
|
||||
if (!enable_usb_uart)
|
||||
return 0;
|
||||
|
||||
np = of_find_matching_node_and_match(NULL, rockchip_usb_phy_dt_ids,
|
||||
&match);
|
||||
if (!np) {
|
||||
pr_err("%s: failed to find usbphy node\n", __func__);
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
pr_debug("%s: using settings for %s\n", __func__, match->compatible);
|
||||
data = match->data;
|
||||
|
||||
if (!data->init_usb_uart) {
|
||||
pr_err("%s: usb-uart not available on %s\n",
|
||||
__func__, match->compatible);
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
|
||||
if (IS_ERR(grf)) {
|
||||
pr_err("%s: Missing rockchip,grf property, %lu\n",
|
||||
__func__, PTR_ERR(grf));
|
||||
return PTR_ERR(grf);
|
||||
}
|
||||
|
||||
ret = data->init_usb_uart(grf);
|
||||
if (ret) {
|
||||
pr_err("%s: could not init usb_uart, %d\n", __func__, ret);
|
||||
enable_usb_uart = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
early_initcall(rockchip_init_usb_uart);
|
||||
|
||||
static int __init rockchip_usb_uart(char *buf)
|
||||
{
|
||||
enable_usb_uart = true;
|
||||
return 0;
|
||||
}
|
||||
early_param("rockchip.usb_uart", rockchip_usb_uart);
|
||||
#endif
|
||||
|
||||
MODULE_AUTHOR("Yunzhi Li <lyz@rock-chips.com>");
|
||||
MODULE_DESCRIPTION("Rockchip USB 2.0 PHY driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
@ -391,7 +391,7 @@ static void __twl4030_phy_power(struct twl4030_usb *twl, int on)
|
|||
WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0);
|
||||
}
|
||||
|
||||
static int twl4030_usb_runtime_suspend(struct device *dev)
|
||||
static int __maybe_unused twl4030_usb_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct twl4030_usb *twl = dev_get_drvdata(dev);
|
||||
|
||||
|
@ -405,7 +405,7 @@ static int twl4030_usb_runtime_suspend(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int twl4030_usb_runtime_resume(struct device *dev)
|
||||
static int __maybe_unused twl4030_usb_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct twl4030_usb *twl = dev_get_drvdata(dev);
|
||||
int res;
|
||||
|
|
|
@ -26,7 +26,7 @@ obj-$(CONFIG_USB_U132_HCD) += host/
|
|||
obj-$(CONFIG_USB_R8A66597_HCD) += host/
|
||||
obj-$(CONFIG_USB_HWA_HCD) += host/
|
||||
obj-$(CONFIG_USB_IMX21_HCD) += host/
|
||||
obj-$(CONFIG_USB_FSL_MPH_DR_OF) += host/
|
||||
obj-$(CONFIG_USB_FSL_USB2) += host/
|
||||
obj-$(CONFIG_USB_FOTG210_HCD) += host/
|
||||
obj-$(CONFIG_USB_MAX3421_HCD) += host/
|
||||
|
||||
|
|
|
@ -476,6 +476,8 @@ static ssize_t cxacru_sysfs_store_adsl_config(struct device *dev,
|
|||
return -EINVAL;
|
||||
if (index < 0 || index > 0x7f)
|
||||
return -EINVAL;
|
||||
if (tmp < 0 || tmp > len - pos)
|
||||
return -EINVAL;
|
||||
pos += tmp;
|
||||
|
||||
/* skip trailing newline */
|
||||
|
|
|
@ -28,6 +28,11 @@ struct ci_hdrc_imx_platform_flag {
|
|||
bool runtime_pm;
|
||||
};
|
||||
|
||||
static const struct ci_hdrc_imx_platform_flag imx23_usb_data = {
|
||||
.flags = CI_HDRC_TURN_VBUS_EARLY_ON |
|
||||
CI_HDRC_DISABLE_STREAMING,
|
||||
};
|
||||
|
||||
static const struct ci_hdrc_imx_platform_flag imx27_usb_data = {
|
||||
CI_HDRC_DISABLE_STREAMING,
|
||||
};
|
||||
|
@ -66,6 +71,7 @@ static const struct ci_hdrc_imx_platform_flag imx7d_usb_data = {
|
|||
};
|
||||
|
||||
static const struct of_device_id ci_hdrc_imx_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx23-usb", .data = &imx23_usb_data},
|
||||
{ .compatible = "fsl,imx28-usb", .data = &imx28_usb_data},
|
||||
{ .compatible = "fsl,imx27-usb", .data = &imx27_usb_data},
|
||||
{ .compatible = "fsl,imx6q-usb", .data = &imx6q_usb_data},
|
||||
|
@ -244,7 +250,6 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
|
|||
struct ci_hdrc_platform_data pdata = {
|
||||
.name = dev_name(&pdev->dev),
|
||||
.capoffset = DEF_CAPOFFSET,
|
||||
.flags = CI_HDRC_SET_NON_ZERO_TTHA,
|
||||
};
|
||||
int ret;
|
||||
const struct of_device_id *of_id;
|
||||
|
@ -302,9 +307,9 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
|
|||
&pdata);
|
||||
if (IS_ERR(data->ci_pdev)) {
|
||||
ret = PTR_ERR(data->ci_pdev);
|
||||
dev_err(&pdev->dev,
|
||||
"Can't register ci_hdrc platform device, err=%d\n",
|
||||
ret);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev,
|
||||
"ci_hdrc_add_device failed, err=%d\n", ret);
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
|
|
|
@ -721,6 +721,9 @@ static int ci_get_platdata(struct device *dev,
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (of_find_property(dev->of_node, "non-zero-ttctrl-ttha", NULL))
|
||||
platdata->flags |= CI_HDRC_SET_NON_ZERO_TTHA;
|
||||
|
||||
ext_id = ERR_PTR(-ENODEV);
|
||||
ext_vbus = ERR_PTR(-ENODEV);
|
||||
if (of_property_read_bool(dev->of_node, "extcon")) {
|
||||
|
|
|
@ -175,7 +175,6 @@ static int ci_requests_show(struct seq_file *s, void *data)
|
|||
{
|
||||
struct ci_hdrc *ci = s->private;
|
||||
unsigned long flags;
|
||||
struct list_head *ptr = NULL;
|
||||
struct ci_hw_req *req = NULL;
|
||||
struct td_node *node, *tmpnode;
|
||||
unsigned i, j, qsize = sizeof(struct ci_hw_td)/sizeof(u32);
|
||||
|
@ -187,9 +186,7 @@ static int ci_requests_show(struct seq_file *s, void *data)
|
|||
|
||||
spin_lock_irqsave(&ci->lock, flags);
|
||||
for (i = 0; i < ci->hw_ep_max; i++)
|
||||
list_for_each(ptr, &ci->ci_hw_ep[i].qh.queue) {
|
||||
req = list_entry(ptr, struct ci_hw_req, queue);
|
||||
|
||||
list_for_each_entry(req, &ci->ci_hw_ep[i].qh.queue, queue) {
|
||||
list_for_each_entry_safe(node, tmpnode, &req->tds, td) {
|
||||
seq_printf(s, "EP=%02i: TD=%08X %s\n",
|
||||
i % (ci->hw_ep_max / 2),
|
||||
|
|
|
@ -66,6 +66,11 @@ set_a_bus_req(struct device *dev, struct device_attribute *attr,
|
|||
return count;
|
||||
}
|
||||
ci->fsm.a_bus_req = 1;
|
||||
if (ci->fsm.otg->state == OTG_STATE_A_PERIPHERAL) {
|
||||
ci->gadget.host_request_flag = 1;
|
||||
mutex_unlock(&ci->fsm.lock);
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
ci_otg_queue_work(ci);
|
||||
|
@ -144,8 +149,14 @@ set_b_bus_req(struct device *dev, struct device_attribute *attr,
|
|||
mutex_lock(&ci->fsm.lock);
|
||||
if (buf[0] == '0')
|
||||
ci->fsm.b_bus_req = 0;
|
||||
else if (buf[0] == '1')
|
||||
else if (buf[0] == '1') {
|
||||
ci->fsm.b_bus_req = 1;
|
||||
if (ci->fsm.otg->state == OTG_STATE_B_PERIPHERAL) {
|
||||
ci->gadget.host_request_flag = 1;
|
||||
mutex_unlock(&ci->fsm.lock);
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
ci_otg_queue_work(ci);
|
||||
mutex_unlock(&ci->fsm.lock);
|
||||
|
@ -198,6 +209,7 @@ static unsigned otg_timer_ms[] = {
|
|||
TA_AIDL_BDIS,
|
||||
TB_ASE0_BRST,
|
||||
TA_BIDL_ADIS,
|
||||
TB_AIDL_BDIS,
|
||||
TB_SE0_SRP,
|
||||
TB_SRP_FAIL,
|
||||
0,
|
||||
|
@ -309,6 +321,12 @@ static int a_bidl_adis_tmout(struct ci_hdrc *ci)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int b_aidl_bdis_tmout(struct ci_hdrc *ci)
|
||||
{
|
||||
ci->fsm.a_bus_suspend = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int b_se0_srp_tmout(struct ci_hdrc *ci)
|
||||
{
|
||||
ci->fsm.b_se0_srp = 1;
|
||||
|
@ -353,6 +371,7 @@ static int (*otg_timer_handlers[])(struct ci_hdrc *) = {
|
|||
a_aidl_bdis_tmout, /* A_AIDL_BDIS */
|
||||
b_ase0_brst_tmout, /* B_ASE0_BRST */
|
||||
a_bidl_adis_tmout, /* A_BIDL_ADIS */
|
||||
b_aidl_bdis_tmout, /* B_AIDL_BDIS */
|
||||
b_se0_srp_tmout, /* B_SE0_SRP */
|
||||
b_srp_fail_tmout, /* B_SRP_FAIL */
|
||||
NULL, /* A_WAIT_ENUM */
|
||||
|
@ -644,9 +663,9 @@ static void ci_otg_fsm_event(struct ci_hdrc *ci)
|
|||
break;
|
||||
case OTG_STATE_B_PERIPHERAL:
|
||||
if ((intr_sts & USBi_SLI) && port_conn && otg_bsess_vld) {
|
||||
fsm->a_bus_suspend = 1;
|
||||
ci_otg_queue_work(ci);
|
||||
ci_otg_add_timer(ci, B_AIDL_BDIS);
|
||||
} else if (intr_sts & USBi_PCI) {
|
||||
ci_otg_del_timer(ci, B_AIDL_BDIS);
|
||||
if (fsm->a_bus_suspend == 1)
|
||||
fsm->a_bus_suspend = 0;
|
||||
}
|
||||
|
@ -786,6 +805,10 @@ int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci)
|
|||
ci->fsm.id = hw_read_otgsc(ci, OTGSC_ID) ? 1 : 0;
|
||||
ci->fsm.otg->state = OTG_STATE_UNDEFINED;
|
||||
ci->fsm.ops = &ci_otg_ops;
|
||||
ci->gadget.hnp_polling_support = 1;
|
||||
ci->fsm.host_req_flag = devm_kzalloc(ci->dev, 1, GFP_KERNEL);
|
||||
if (!ci->fsm.host_req_flag)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&ci->fsm.lock);
|
||||
|
||||
|
|
|
@ -62,6 +62,8 @@
|
|||
/* SSEND time before SRP */
|
||||
#define TB_SSEND_SRP (1500) /* minimum 1.5 sec, section:5.1.2 */
|
||||
|
||||
#define TB_AIDL_BDIS (20) /* 4ms ~ 150ms, section 5.2.1 */
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_OTG_FSM)
|
||||
|
||||
int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci);
|
||||
|
|
|
@ -819,7 +819,6 @@ static int _ep_queue(struct usb_ep *ep, struct usb_request *req,
|
|||
ci->ep0out : ci->ep0in;
|
||||
if (!list_empty(&hwep->qh.queue)) {
|
||||
_ep_nuke(hwep);
|
||||
retval = -EOVERFLOW;
|
||||
dev_warn(hwep->ci->dev, "endpoint ctrl %X nuked\n",
|
||||
_usb_addr(hwep));
|
||||
}
|
||||
|
@ -1068,7 +1067,8 @@ __acquires(ci->lock)
|
|||
}
|
||||
break;
|
||||
case USB_REQ_GET_STATUS:
|
||||
if (type != (USB_DIR_IN|USB_RECIP_DEVICE) &&
|
||||
if ((type != (USB_DIR_IN|USB_RECIP_DEVICE) ||
|
||||
le16_to_cpu(req.wIndex) == OTG_STS_SELECTOR) &&
|
||||
type != (USB_DIR_IN|USB_RECIP_ENDPOINT) &&
|
||||
type != (USB_DIR_IN|USB_RECIP_INTERFACE))
|
||||
goto delegate;
|
||||
|
|
|
@ -713,9 +713,20 @@ static int acm_tty_write(struct tty_struct *tty,
|
|||
}
|
||||
|
||||
if (acm->susp_count) {
|
||||
if (acm->putbuffer) {
|
||||
/* now to preserve order */
|
||||
usb_anchor_urb(acm->putbuffer->urb, &acm->delayed);
|
||||
acm->putbuffer = NULL;
|
||||
}
|
||||
usb_anchor_urb(wb->urb, &acm->delayed);
|
||||
spin_unlock_irqrestore(&acm->write_lock, flags);
|
||||
return count;
|
||||
} else {
|
||||
if (acm->putbuffer) {
|
||||
/* at this point there is no good way to handle errors */
|
||||
acm_start_wb(acm, acm->putbuffer);
|
||||
acm->putbuffer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
stat = acm_start_wb(acm, wb);
|
||||
|
@ -726,6 +737,60 @@ static int acm_tty_write(struct tty_struct *tty,
|
|||
return count;
|
||||
}
|
||||
|
||||
static void acm_tty_flush_chars(struct tty_struct *tty)
|
||||
{
|
||||
struct acm *acm = tty->driver_data;
|
||||
struct acm_wb *cur = acm->putbuffer;
|
||||
int err;
|
||||
unsigned long flags;
|
||||
|
||||
acm->putbuffer = NULL;
|
||||
err = usb_autopm_get_interface_async(acm->control);
|
||||
spin_lock_irqsave(&acm->write_lock, flags);
|
||||
if (err < 0) {
|
||||
cur->use = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (acm->susp_count)
|
||||
usb_anchor_urb(cur->urb, &acm->delayed);
|
||||
else
|
||||
acm_start_wb(acm, cur);
|
||||
out:
|
||||
spin_unlock_irqrestore(&acm->write_lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
static int acm_tty_put_char(struct tty_struct *tty, unsigned char ch)
|
||||
{
|
||||
struct acm *acm = tty->driver_data;
|
||||
struct acm_wb *cur;
|
||||
int wbn;
|
||||
unsigned long flags;
|
||||
|
||||
overflow:
|
||||
cur = acm->putbuffer;
|
||||
if (!cur) {
|
||||
spin_lock_irqsave(&acm->write_lock, flags);
|
||||
wbn = acm_wb_alloc(acm);
|
||||
if (wbn >= 0) {
|
||||
cur = &acm->wb[wbn];
|
||||
acm->putbuffer = cur;
|
||||
}
|
||||
spin_unlock_irqrestore(&acm->write_lock, flags);
|
||||
if (!cur)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cur->len == acm->writesize) {
|
||||
acm_tty_flush_chars(tty);
|
||||
goto overflow;
|
||||
}
|
||||
|
||||
cur->buf[cur->len++] = ch;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int acm_tty_write_room(struct tty_struct *tty)
|
||||
{
|
||||
struct acm *acm = tty->driver_data;
|
||||
|
@ -1905,6 +1970,8 @@ static const struct tty_operations acm_ops = {
|
|||
.cleanup = acm_tty_cleanup,
|
||||
.hangup = acm_tty_hangup,
|
||||
.write = acm_tty_write,
|
||||
.put_char = acm_tty_put_char,
|
||||
.flush_chars = acm_tty_flush_chars,
|
||||
.write_room = acm_tty_write_room,
|
||||
.ioctl = acm_tty_ioctl,
|
||||
.throttle = acm_tty_throttle,
|
||||
|
|
|
@ -94,6 +94,7 @@ struct acm {
|
|||
unsigned long read_urbs_free;
|
||||
struct urb *read_urbs[ACM_NR];
|
||||
struct acm_rb read_buffers[ACM_NR];
|
||||
struct acm_wb *putbuffer; /* for acm_tty_put_char() */
|
||||
int rx_buflimit;
|
||||
int rx_endpoint;
|
||||
spinlock_t read_lock;
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <linux/uaccess.h>
|
||||
#include <linux/kref.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/tmc.h>
|
||||
|
@ -87,6 +88,23 @@ struct usbtmc_device_data {
|
|||
u8 bTag_last_write; /* needed for abort */
|
||||
u8 bTag_last_read; /* needed for abort */
|
||||
|
||||
/* data for interrupt in endpoint handling */
|
||||
u8 bNotify1;
|
||||
u8 bNotify2;
|
||||
u16 ifnum;
|
||||
u8 iin_bTag;
|
||||
u8 *iin_buffer;
|
||||
atomic_t iin_data_valid;
|
||||
unsigned int iin_ep;
|
||||
int iin_ep_present;
|
||||
int iin_interval;
|
||||
struct urb *iin_urb;
|
||||
u16 iin_wMaxPacketSize;
|
||||
atomic_t srq_asserted;
|
||||
|
||||
/* coalesced usb488_caps from usbtmc_dev_capabilities */
|
||||
__u8 usb488_caps;
|
||||
|
||||
u8 rigol_quirk;
|
||||
|
||||
/* attributes from the USB TMC spec for this device */
|
||||
|
@ -99,6 +117,8 @@ struct usbtmc_device_data {
|
|||
struct usbtmc_dev_capabilities capabilities;
|
||||
struct kref kref;
|
||||
struct mutex io_mutex; /* only one i/o function running at a time */
|
||||
wait_queue_head_t waitq;
|
||||
struct fasync_struct *fasync;
|
||||
};
|
||||
#define to_usbtmc_data(d) container_of(d, struct usbtmc_device_data, kref)
|
||||
|
||||
|
@ -373,6 +393,142 @@ exit:
|
|||
return rv;
|
||||
}
|
||||
|
||||
static int usbtmc488_ioctl_read_stb(struct usbtmc_device_data *data,
|
||||
void __user *arg)
|
||||
{
|
||||
struct device *dev = &data->intf->dev;
|
||||
u8 *buffer;
|
||||
u8 tag;
|
||||
__u8 stb;
|
||||
int rv;
|
||||
|
||||
dev_dbg(dev, "Enter ioctl_read_stb iin_ep_present: %d\n",
|
||||
data->iin_ep_present);
|
||||
|
||||
buffer = kmalloc(8, GFP_KERNEL);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
atomic_set(&data->iin_data_valid, 0);
|
||||
|
||||
/* must issue read_stb before using poll or select */
|
||||
atomic_set(&data->srq_asserted, 0);
|
||||
|
||||
rv = usb_control_msg(data->usb_dev,
|
||||
usb_rcvctrlpipe(data->usb_dev, 0),
|
||||
USBTMC488_REQUEST_READ_STATUS_BYTE,
|
||||
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
|
||||
data->iin_bTag,
|
||||
data->ifnum,
|
||||
buffer, 0x03, USBTMC_TIMEOUT);
|
||||
if (rv < 0) {
|
||||
dev_err(dev, "stb usb_control_msg returned %d\n", rv);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (buffer[0] != USBTMC_STATUS_SUCCESS) {
|
||||
dev_err(dev, "control status returned %x\n", buffer[0]);
|
||||
rv = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (data->iin_ep_present) {
|
||||
rv = wait_event_interruptible_timeout(
|
||||
data->waitq,
|
||||
atomic_read(&data->iin_data_valid) != 0,
|
||||
USBTMC_TIMEOUT);
|
||||
if (rv < 0) {
|
||||
dev_dbg(dev, "wait interrupted %d\n", rv);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (rv == 0) {
|
||||
dev_dbg(dev, "wait timed out\n");
|
||||
rv = -ETIME;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
tag = data->bNotify1 & 0x7f;
|
||||
if (tag != data->iin_bTag) {
|
||||
dev_err(dev, "expected bTag %x got %x\n",
|
||||
data->iin_bTag, tag);
|
||||
}
|
||||
|
||||
stb = data->bNotify2;
|
||||
} else {
|
||||
stb = buffer[2];
|
||||
}
|
||||
|
||||
rv = copy_to_user(arg, &stb, sizeof(stb));
|
||||
if (rv)
|
||||
rv = -EFAULT;
|
||||
|
||||
exit:
|
||||
/* bump interrupt bTag */
|
||||
data->iin_bTag += 1;
|
||||
if (data->iin_bTag > 127)
|
||||
/* 1 is for SRQ see USBTMC-USB488 subclass spec section 4.3.1 */
|
||||
data->iin_bTag = 2;
|
||||
|
||||
kfree(buffer);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int usbtmc488_ioctl_simple(struct usbtmc_device_data *data,
|
||||
void __user *arg, unsigned int cmd)
|
||||
{
|
||||
struct device *dev = &data->intf->dev;
|
||||
__u8 val;
|
||||
u8 *buffer;
|
||||
u16 wValue;
|
||||
int rv;
|
||||
|
||||
if (!(data->usb488_caps & USBTMC488_CAPABILITY_SIMPLE))
|
||||
return -EINVAL;
|
||||
|
||||
buffer = kmalloc(8, GFP_KERNEL);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
if (cmd == USBTMC488_REQUEST_REN_CONTROL) {
|
||||
rv = copy_from_user(&val, arg, sizeof(val));
|
||||
if (rv) {
|
||||
rv = -EFAULT;
|
||||
goto exit;
|
||||
}
|
||||
wValue = val ? 1 : 0;
|
||||
} else {
|
||||
wValue = 0;
|
||||
}
|
||||
|
||||
rv = usb_control_msg(data->usb_dev,
|
||||
usb_rcvctrlpipe(data->usb_dev, 0),
|
||||
cmd,
|
||||
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
|
||||
wValue,
|
||||
data->ifnum,
|
||||
buffer, 0x01, USBTMC_TIMEOUT);
|
||||
if (rv < 0) {
|
||||
dev_err(dev, "simple usb_control_msg failed %d\n", rv);
|
||||
goto exit;
|
||||
} else if (rv != 1) {
|
||||
dev_warn(dev, "simple usb_control_msg returned %d\n", rv);
|
||||
rv = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (buffer[0] != USBTMC_STATUS_SUCCESS) {
|
||||
dev_err(dev, "simple control status returned %x\n", buffer[0]);
|
||||
rv = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
rv = 0;
|
||||
|
||||
exit:
|
||||
kfree(buffer);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sends a REQUEST_DEV_DEP_MSG_IN message on the Bulk-IN endpoint.
|
||||
* @transfer_size: number of bytes to request from the device.
|
||||
|
@ -895,6 +1051,7 @@ static int get_capabilities(struct usbtmc_device_data *data)
|
|||
data->capabilities.device_capabilities = buffer[5];
|
||||
data->capabilities.usb488_interface_capabilities = buffer[14];
|
||||
data->capabilities.usb488_device_capabilities = buffer[15];
|
||||
data->usb488_caps = (buffer[14] & 0x07) | ((buffer[15] & 0x0f) << 4);
|
||||
rv = 0;
|
||||
|
||||
err_out:
|
||||
|
@ -1069,6 +1226,33 @@ static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|||
case USBTMC_IOCTL_ABORT_BULK_IN:
|
||||
retval = usbtmc_ioctl_abort_bulk_in(data);
|
||||
break;
|
||||
|
||||
case USBTMC488_IOCTL_GET_CAPS:
|
||||
retval = copy_to_user((void __user *)arg,
|
||||
&data->usb488_caps,
|
||||
sizeof(data->usb488_caps));
|
||||
if (retval)
|
||||
retval = -EFAULT;
|
||||
break;
|
||||
|
||||
case USBTMC488_IOCTL_READ_STB:
|
||||
retval = usbtmc488_ioctl_read_stb(data, (void __user *)arg);
|
||||
break;
|
||||
|
||||
case USBTMC488_IOCTL_REN_CONTROL:
|
||||
retval = usbtmc488_ioctl_simple(data, (void __user *)arg,
|
||||
USBTMC488_REQUEST_REN_CONTROL);
|
||||
break;
|
||||
|
||||
case USBTMC488_IOCTL_GOTO_LOCAL:
|
||||
retval = usbtmc488_ioctl_simple(data, (void __user *)arg,
|
||||
USBTMC488_REQUEST_GOTO_LOCAL);
|
||||
break;
|
||||
|
||||
case USBTMC488_IOCTL_LOCAL_LOCKOUT:
|
||||
retval = usbtmc488_ioctl_simple(data, (void __user *)arg,
|
||||
USBTMC488_REQUEST_LOCAL_LOCKOUT);
|
||||
break;
|
||||
}
|
||||
|
||||
skip_io_on_zombie:
|
||||
|
@ -1076,6 +1260,34 @@ skip_io_on_zombie:
|
|||
return retval;
|
||||
}
|
||||
|
||||
static int usbtmc_fasync(int fd, struct file *file, int on)
|
||||
{
|
||||
struct usbtmc_device_data *data = file->private_data;
|
||||
|
||||
return fasync_helper(fd, file, on, &data->fasync);
|
||||
}
|
||||
|
||||
static unsigned int usbtmc_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct usbtmc_device_data *data = file->private_data;
|
||||
unsigned int mask;
|
||||
|
||||
mutex_lock(&data->io_mutex);
|
||||
|
||||
if (data->zombie) {
|
||||
mask = POLLHUP | POLLERR;
|
||||
goto no_poll;
|
||||
}
|
||||
|
||||
poll_wait(file, &data->waitq, wait);
|
||||
|
||||
mask = (atomic_read(&data->srq_asserted)) ? POLLIN | POLLRDNORM : 0;
|
||||
|
||||
no_poll:
|
||||
mutex_unlock(&data->io_mutex);
|
||||
return mask;
|
||||
}
|
||||
|
||||
static const struct file_operations fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = usbtmc_read,
|
||||
|
@ -1083,6 +1295,8 @@ static const struct file_operations fops = {
|
|||
.open = usbtmc_open,
|
||||
.release = usbtmc_release,
|
||||
.unlocked_ioctl = usbtmc_ioctl,
|
||||
.fasync = usbtmc_fasync,
|
||||
.poll = usbtmc_poll,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
|
@ -1092,6 +1306,67 @@ static struct usb_class_driver usbtmc_class = {
|
|||
.minor_base = USBTMC_MINOR_BASE,
|
||||
};
|
||||
|
||||
static void usbtmc_interrupt(struct urb *urb)
|
||||
{
|
||||
struct usbtmc_device_data *data = urb->context;
|
||||
struct device *dev = &data->intf->dev;
|
||||
int status = urb->status;
|
||||
int rv;
|
||||
|
||||
dev_dbg(&data->intf->dev, "int status: %d len %d\n",
|
||||
status, urb->actual_length);
|
||||
|
||||
switch (status) {
|
||||
case 0: /* SUCCESS */
|
||||
/* check for valid STB notification */
|
||||
if (data->iin_buffer[0] > 0x81) {
|
||||
data->bNotify1 = data->iin_buffer[0];
|
||||
data->bNotify2 = data->iin_buffer[1];
|
||||
atomic_set(&data->iin_data_valid, 1);
|
||||
wake_up_interruptible(&data->waitq);
|
||||
goto exit;
|
||||
}
|
||||
/* check for SRQ notification */
|
||||
if (data->iin_buffer[0] == 0x81) {
|
||||
if (data->fasync)
|
||||
kill_fasync(&data->fasync,
|
||||
SIGIO, POLL_IN);
|
||||
|
||||
atomic_set(&data->srq_asserted, 1);
|
||||
wake_up_interruptible(&data->waitq);
|
||||
goto exit;
|
||||
}
|
||||
dev_warn(dev, "invalid notification: %x\n", data->iin_buffer[0]);
|
||||
break;
|
||||
case -EOVERFLOW:
|
||||
dev_err(dev, "overflow with length %d, actual length is %d\n",
|
||||
data->iin_wMaxPacketSize, urb->actual_length);
|
||||
case -ECONNRESET:
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
case -EILSEQ:
|
||||
case -ETIME:
|
||||
/* urb terminated, clean up */
|
||||
dev_dbg(dev, "urb terminated, status: %d\n", status);
|
||||
return;
|
||||
default:
|
||||
dev_err(dev, "unknown status received: %d\n", status);
|
||||
}
|
||||
exit:
|
||||
rv = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (rv)
|
||||
dev_err(dev, "usb_submit_urb failed: %d\n", rv);
|
||||
}
|
||||
|
||||
static void usbtmc_free_int(struct usbtmc_device_data *data)
|
||||
{
|
||||
if (!data->iin_ep_present || !data->iin_urb)
|
||||
return;
|
||||
usb_kill_urb(data->iin_urb);
|
||||
kfree(data->iin_buffer);
|
||||
usb_free_urb(data->iin_urb);
|
||||
kref_put(&data->kref, usbtmc_delete);
|
||||
}
|
||||
|
||||
static int usbtmc_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
|
@ -1114,6 +1389,9 @@ static int usbtmc_probe(struct usb_interface *intf,
|
|||
usb_set_intfdata(intf, data);
|
||||
kref_init(&data->kref);
|
||||
mutex_init(&data->io_mutex);
|
||||
init_waitqueue_head(&data->waitq);
|
||||
atomic_set(&data->iin_data_valid, 0);
|
||||
atomic_set(&data->srq_asserted, 0);
|
||||
data->zombie = 0;
|
||||
|
||||
/* Determine if it is a Rigol or not */
|
||||
|
@ -1134,9 +1412,12 @@ static int usbtmc_probe(struct usb_interface *intf,
|
|||
data->bTag = 1;
|
||||
data->TermCharEnabled = 0;
|
||||
data->TermChar = '\n';
|
||||
/* 2 <= bTag <= 127 USBTMC-USB488 subclass specification 4.3.1 */
|
||||
data->iin_bTag = 2;
|
||||
|
||||
/* USBTMC devices have only one setting, so use that */
|
||||
iface_desc = data->intf->cur_altsetting;
|
||||
data->ifnum = iface_desc->desc.bInterfaceNumber;
|
||||
|
||||
/* Find bulk in endpoint */
|
||||
for (n = 0; n < iface_desc->desc.bNumEndpoints; n++) {
|
||||
|
@ -1161,6 +1442,20 @@ static int usbtmc_probe(struct usb_interface *intf,
|
|||
break;
|
||||
}
|
||||
}
|
||||
/* Find int endpoint */
|
||||
for (n = 0; n < iface_desc->desc.bNumEndpoints; n++) {
|
||||
endpoint = &iface_desc->endpoint[n].desc;
|
||||
|
||||
if (usb_endpoint_is_int_in(endpoint)) {
|
||||
data->iin_ep_present = 1;
|
||||
data->iin_ep = endpoint->bEndpointAddress;
|
||||
data->iin_wMaxPacketSize = usb_endpoint_maxp(endpoint);
|
||||
data->iin_interval = endpoint->bInterval;
|
||||
dev_dbg(&intf->dev, "Found Int in endpoint at %u\n",
|
||||
data->iin_ep);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
retcode = get_capabilities(data);
|
||||
if (retcode)
|
||||
|
@ -1169,6 +1464,39 @@ static int usbtmc_probe(struct usb_interface *intf,
|
|||
retcode = sysfs_create_group(&intf->dev.kobj,
|
||||
&capability_attr_grp);
|
||||
|
||||
if (data->iin_ep_present) {
|
||||
/* allocate int urb */
|
||||
data->iin_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!data->iin_urb) {
|
||||
dev_err(&intf->dev, "Failed to allocate int urb\n");
|
||||
goto error_register;
|
||||
}
|
||||
|
||||
/* will reference data in int urb */
|
||||
kref_get(&data->kref);
|
||||
|
||||
/* allocate buffer for interrupt in */
|
||||
data->iin_buffer = kmalloc(data->iin_wMaxPacketSize,
|
||||
GFP_KERNEL);
|
||||
if (!data->iin_buffer) {
|
||||
dev_err(&intf->dev, "Failed to allocate int buf\n");
|
||||
goto error_register;
|
||||
}
|
||||
|
||||
/* fill interrupt urb */
|
||||
usb_fill_int_urb(data->iin_urb, data->usb_dev,
|
||||
usb_rcvintpipe(data->usb_dev, data->iin_ep),
|
||||
data->iin_buffer, data->iin_wMaxPacketSize,
|
||||
usbtmc_interrupt,
|
||||
data, data->iin_interval);
|
||||
|
||||
retcode = usb_submit_urb(data->iin_urb, GFP_KERNEL);
|
||||
if (retcode) {
|
||||
dev_err(&intf->dev, "Failed to submit iin_urb\n");
|
||||
goto error_register;
|
||||
}
|
||||
}
|
||||
|
||||
retcode = sysfs_create_group(&intf->dev.kobj, &data_attr_grp);
|
||||
|
||||
retcode = usb_register_dev(intf, &usbtmc_class);
|
||||
|
@ -1185,6 +1513,7 @@ static int usbtmc_probe(struct usb_interface *intf,
|
|||
error_register:
|
||||
sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp);
|
||||
sysfs_remove_group(&intf->dev.kobj, &data_attr_grp);
|
||||
usbtmc_free_int(data);
|
||||
kref_put(&data->kref, usbtmc_delete);
|
||||
return retcode;
|
||||
}
|
||||
|
@ -1201,7 +1530,9 @@ static void usbtmc_disconnect(struct usb_interface *intf)
|
|||
sysfs_remove_group(&intf->dev.kobj, &data_attr_grp);
|
||||
mutex_lock(&data->io_mutex);
|
||||
data->zombie = 1;
|
||||
wake_up_all(&data->waitq);
|
||||
mutex_unlock(&data->io_mutex);
|
||||
usbtmc_free_int(data);
|
||||
kref_put(&data->kref, usbtmc_delete);
|
||||
}
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@ static const char *const speed_names[] = {
|
|||
[USB_SPEED_HIGH] = "high-speed",
|
||||
[USB_SPEED_WIRELESS] = "wireless",
|
||||
[USB_SPEED_SUPER] = "super-speed",
|
||||
[USB_SPEED_SUPER_PLUS] = "super-speed-plus",
|
||||
};
|
||||
|
||||
const char *usb_speed_string(enum usb_device_speed speed)
|
||||
|
|
|
@ -78,6 +78,8 @@ static void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state)
|
|||
fsm->b_srp_done = 0;
|
||||
break;
|
||||
case OTG_STATE_B_PERIPHERAL:
|
||||
if (fsm->otg->gadget)
|
||||
fsm->otg->gadget->host_request_flag = 0;
|
||||
break;
|
||||
case OTG_STATE_B_WAIT_ACON:
|
||||
otg_del_timer(fsm, B_ASE0_BRST);
|
||||
|
@ -107,6 +109,8 @@ static void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state)
|
|||
case OTG_STATE_A_PERIPHERAL:
|
||||
otg_del_timer(fsm, A_BIDL_ADIS);
|
||||
fsm->a_bidl_adis_tmout = 0;
|
||||
if (fsm->otg->gadget)
|
||||
fsm->otg->gadget->host_request_flag = 0;
|
||||
break;
|
||||
case OTG_STATE_A_WAIT_VFALL:
|
||||
otg_del_timer(fsm, A_WAIT_VFALL);
|
||||
|
@ -120,6 +124,87 @@ static void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state)
|
|||
}
|
||||
}
|
||||
|
||||
static void otg_hnp_polling_work(struct work_struct *work)
|
||||
{
|
||||
struct otg_fsm *fsm = container_of(to_delayed_work(work),
|
||||
struct otg_fsm, hnp_polling_work);
|
||||
struct usb_device *udev;
|
||||
enum usb_otg_state state = fsm->otg->state;
|
||||
u8 flag;
|
||||
int retval;
|
||||
|
||||
if (state != OTG_STATE_A_HOST && state != OTG_STATE_B_HOST)
|
||||
return;
|
||||
|
||||
udev = usb_hub_find_child(fsm->otg->host->root_hub, 1);
|
||||
if (!udev) {
|
||||
dev_err(fsm->otg->host->controller,
|
||||
"no usb dev connected, can't start HNP polling\n");
|
||||
return;
|
||||
}
|
||||
|
||||
*fsm->host_req_flag = 0;
|
||||
/* Get host request flag from connected USB device */
|
||||
retval = usb_control_msg(udev,
|
||||
usb_rcvctrlpipe(udev, 0),
|
||||
USB_REQ_GET_STATUS,
|
||||
USB_DIR_IN | USB_RECIP_DEVICE,
|
||||
0,
|
||||
OTG_STS_SELECTOR,
|
||||
fsm->host_req_flag,
|
||||
1,
|
||||
USB_CTRL_GET_TIMEOUT);
|
||||
if (retval != 1) {
|
||||
dev_err(&udev->dev, "Get one byte OTG status failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
flag = *fsm->host_req_flag;
|
||||
if (flag == 0) {
|
||||
/* Continue HNP polling */
|
||||
schedule_delayed_work(&fsm->hnp_polling_work,
|
||||
msecs_to_jiffies(T_HOST_REQ_POLL));
|
||||
return;
|
||||
} else if (flag != HOST_REQUEST_FLAG) {
|
||||
dev_err(&udev->dev, "host request flag %d is invalid\n", flag);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Host request flag is set */
|
||||
if (state == OTG_STATE_A_HOST) {
|
||||
/* Set b_hnp_enable */
|
||||
if (!fsm->otg->host->b_hnp_enable) {
|
||||
retval = usb_control_msg(udev,
|
||||
usb_sndctrlpipe(udev, 0),
|
||||
USB_REQ_SET_FEATURE, 0,
|
||||
USB_DEVICE_B_HNP_ENABLE,
|
||||
0, NULL, 0,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
if (retval >= 0)
|
||||
fsm->otg->host->b_hnp_enable = 1;
|
||||
}
|
||||
fsm->a_bus_req = 0;
|
||||
} else if (state == OTG_STATE_B_HOST) {
|
||||
fsm->b_bus_req = 0;
|
||||
}
|
||||
|
||||
otg_statemachine(fsm);
|
||||
}
|
||||
|
||||
static void otg_start_hnp_polling(struct otg_fsm *fsm)
|
||||
{
|
||||
/*
|
||||
* The memory of host_req_flag should be allocated by
|
||||
* controller driver, otherwise, hnp polling is not started.
|
||||
*/
|
||||
if (!fsm->host_req_flag)
|
||||
return;
|
||||
|
||||
INIT_DELAYED_WORK(&fsm->hnp_polling_work, otg_hnp_polling_work);
|
||||
schedule_delayed_work(&fsm->hnp_polling_work,
|
||||
msecs_to_jiffies(T_HOST_REQ_POLL));
|
||||
}
|
||||
|
||||
/* Called when entering a state */
|
||||
static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
|
||||
{
|
||||
|
@ -169,6 +254,7 @@ static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
|
|||
otg_set_protocol(fsm, PROTO_HOST);
|
||||
usb_bus_start_enum(fsm->otg->host,
|
||||
fsm->otg->host->otg_port);
|
||||
otg_start_hnp_polling(fsm);
|
||||
break;
|
||||
case OTG_STATE_A_IDLE:
|
||||
otg_drv_vbus(fsm, 0);
|
||||
|
@ -203,6 +289,7 @@ static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
|
|||
*/
|
||||
if (!fsm->a_bus_req || fsm->a_suspend_req_inf)
|
||||
otg_add_timer(fsm, A_WAIT_ENUM);
|
||||
otg_start_hnp_polling(fsm);
|
||||
break;
|
||||
case OTG_STATE_A_SUSPEND:
|
||||
otg_drv_vbus(fsm, 1);
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
usbcore-y := usb.o hub.o hcd.o urb.o message.o driver.o
|
||||
usbcore-y += config.o file.o buffer.o sysfs.o endpoint.o
|
||||
usbcore-y += devio.o notify.o generic.o quirks.o devices.o
|
||||
usbcore-y += port.o
|
||||
usbcore-y += port.o of.o
|
||||
|
||||
usbcore-$(CONFIG_PCI) += hcd-pci.o
|
||||
usbcore-$(CONFIG_ACPI) += usb-acpi.o
|
||||
|
|
|
@ -62,8 +62,9 @@ int hcd_buffer_create(struct usb_hcd *hcd)
|
|||
char name[16];
|
||||
int i, size;
|
||||
|
||||
if (!hcd->self.controller->dma_mask &&
|
||||
!(hcd->driver->flags & HCD_LOCAL_MEM))
|
||||
if (!IS_ENABLED(CONFIG_HAS_DMA) ||
|
||||
(!hcd->self.controller->dma_mask &&
|
||||
!(hcd->driver->flags & HCD_LOCAL_MEM)))
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < HCD_BUFFER_POOLS; i++) {
|
||||
|
@ -93,6 +94,9 @@ void hcd_buffer_destroy(struct usb_hcd *hcd)
|
|||
{
|
||||
int i;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_HAS_DMA))
|
||||
return;
|
||||
|
||||
for (i = 0; i < HCD_BUFFER_POOLS; i++) {
|
||||
struct dma_pool *pool = hcd->pool[i];
|
||||
|
||||
|
@ -119,8 +123,9 @@ void *hcd_buffer_alloc(
|
|||
int i;
|
||||
|
||||
/* some USB hosts just use PIO */
|
||||
if (!bus->controller->dma_mask &&
|
||||
!(hcd->driver->flags & HCD_LOCAL_MEM)) {
|
||||
if (!IS_ENABLED(CONFIG_HAS_DMA) ||
|
||||
(!bus->controller->dma_mask &&
|
||||
!(hcd->driver->flags & HCD_LOCAL_MEM))) {
|
||||
*dma = ~(dma_addr_t) 0;
|
||||
return kmalloc(size, mem_flags);
|
||||
}
|
||||
|
@ -145,8 +150,9 @@ void hcd_buffer_free(
|
|||
if (!addr)
|
||||
return;
|
||||
|
||||
if (!bus->controller->dma_mask &&
|
||||
!(hcd->driver->flags & HCD_LOCAL_MEM)) {
|
||||
if (!IS_ENABLED(CONFIG_HAS_DMA) ||
|
||||
(!bus->controller->dma_mask &&
|
||||
!(hcd->driver->flags & HCD_LOCAL_MEM))) {
|
||||
kfree(addr);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -43,6 +43,27 @@ static int find_next_descriptor(unsigned char *buffer, int size,
|
|||
return buffer - buffer0;
|
||||
}
|
||||
|
||||
static void usb_parse_ssp_isoc_endpoint_companion(struct device *ddev,
|
||||
int cfgno, int inum, int asnum, struct usb_host_endpoint *ep,
|
||||
unsigned char *buffer, int size)
|
||||
{
|
||||
struct usb_ssp_isoc_ep_comp_descriptor *desc;
|
||||
|
||||
/*
|
||||
* The SuperSpeedPlus Isoc endpoint companion descriptor immediately
|
||||
* follows the SuperSpeed Endpoint Companion descriptor
|
||||
*/
|
||||
desc = (struct usb_ssp_isoc_ep_comp_descriptor *) buffer;
|
||||
if (desc->bDescriptorType != USB_DT_SSP_ISOC_ENDPOINT_COMP ||
|
||||
size < USB_DT_SSP_ISOC_EP_COMP_SIZE) {
|
||||
dev_warn(ddev, "Invalid SuperSpeedPlus isoc endpoint companion"
|
||||
"for config %d interface %d altsetting %d ep %d.\n",
|
||||
cfgno, inum, asnum, ep->desc.bEndpointAddress);
|
||||
return;
|
||||
}
|
||||
memcpy(&ep->ssp_isoc_ep_comp, desc, USB_DT_SSP_ISOC_EP_COMP_SIZE);
|
||||
}
|
||||
|
||||
static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
|
||||
int inum, int asnum, struct usb_host_endpoint *ep,
|
||||
unsigned char *buffer, int size)
|
||||
|
@ -54,6 +75,9 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
|
|||
* be the first thing immediately following the endpoint descriptor.
|
||||
*/
|
||||
desc = (struct usb_ss_ep_comp_descriptor *) buffer;
|
||||
buffer += desc->bLength;
|
||||
size -= desc->bLength;
|
||||
|
||||
if (desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP ||
|
||||
size < USB_DT_SS_EP_COMP_SIZE) {
|
||||
dev_warn(ddev, "No SuperSpeed endpoint companion for config %d "
|
||||
|
@ -112,6 +136,7 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
|
|||
cfgno, inum, asnum, ep->desc.bEndpointAddress);
|
||||
ep->ss_ep_comp.bmAttributes = 16;
|
||||
} else if (usb_endpoint_xfer_isoc(&ep->desc) &&
|
||||
!USB_SS_SSP_ISOC_COMP(desc->bmAttributes) &&
|
||||
USB_SS_MULT(desc->bmAttributes) > 3) {
|
||||
dev_warn(ddev, "Isoc endpoint has Mult of %d in "
|
||||
"config %d interface %d altsetting %d ep %d: "
|
||||
|
@ -121,6 +146,12 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
|
|||
ep->ss_ep_comp.bmAttributes = 2;
|
||||
}
|
||||
|
||||
/* Parse a possible SuperSpeedPlus isoc ep companion descriptor */
|
||||
if (usb_endpoint_xfer_isoc(&ep->desc) &&
|
||||
USB_SS_SSP_ISOC_COMP(desc->bmAttributes))
|
||||
usb_parse_ssp_isoc_endpoint_companion(ddev, cfgno, inum, asnum,
|
||||
ep, buffer, size);
|
||||
|
||||
if (usb_endpoint_xfer_isoc(&ep->desc))
|
||||
max_tx = (desc->bMaxBurst + 1) *
|
||||
(USB_SS_MULT(desc->bmAttributes)) *
|
||||
|
@ -191,6 +222,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
|
|||
if (usb_endpoint_xfer_int(d)) {
|
||||
i = 1;
|
||||
switch (to_usb_device(ddev)->speed) {
|
||||
case USB_SPEED_SUPER_PLUS:
|
||||
case USB_SPEED_SUPER:
|
||||
case USB_SPEED_HIGH:
|
||||
/* Many device manufacturers are using full-speed
|
||||
|
@ -274,7 +306,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
|
|||
}
|
||||
|
||||
/* Parse a possible SuperSpeed endpoint companion descriptor */
|
||||
if (to_usb_device(ddev)->speed == USB_SPEED_SUPER)
|
||||
if (to_usb_device(ddev)->speed >= USB_SPEED_SUPER)
|
||||
usb_parse_ss_endpoint_companion(ddev, cfgno,
|
||||
inum, asnum, endpoint, buffer, size);
|
||||
|
||||
|
@ -862,6 +894,9 @@ int usb_get_bos_descriptor(struct usb_device *dev)
|
|||
dev->bos->ss_id =
|
||||
(struct usb_ss_container_id_descriptor *)buffer;
|
||||
break;
|
||||
case USB_PTM_CAP_TYPE:
|
||||
dev->bos->ptm_cap =
|
||||
(struct usb_ptm_cap_descriptor *)buffer;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -110,13 +110,6 @@ static const char format_endpt[] =
|
|||
/* E: Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=D?s */
|
||||
"E: Ad=%02x(%c) Atr=%02x(%-4s) MxPS=%4d Ivl=%d%cs\n";
|
||||
|
||||
|
||||
/*
|
||||
* Need access to the driver and USB bus lists.
|
||||
* extern struct list_head usb_bus_list;
|
||||
* However, these will come from functions that return ptrs to each of them.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Wait for an connect/disconnect event to happen. We initialize
|
||||
* the event counter with an odd number, and each event will increment
|
||||
|
@ -221,7 +214,7 @@ static char *usb_dump_endpoint_descriptor(int speed, char *start, char *end,
|
|||
break;
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
type = "Int.";
|
||||
if (speed == USB_SPEED_HIGH || speed == USB_SPEED_SUPER)
|
||||
if (speed == USB_SPEED_HIGH || speed >= USB_SPEED_SUPER)
|
||||
interval = 1 << (desc->bInterval - 1);
|
||||
else
|
||||
interval = desc->bInterval;
|
||||
|
@ -230,7 +223,7 @@ static char *usb_dump_endpoint_descriptor(int speed, char *start, char *end,
|
|||
return start;
|
||||
}
|
||||
interval *= (speed == USB_SPEED_HIGH ||
|
||||
speed == USB_SPEED_SUPER) ? 125 : 1000;
|
||||
speed >= USB_SPEED_SUPER) ? 125 : 1000;
|
||||
if (interval % 1000)
|
||||
unit = 'u';
|
||||
else {
|
||||
|
@ -322,7 +315,7 @@ static char *usb_dump_config_descriptor(char *start, char *end,
|
|||
|
||||
if (start > end)
|
||||
return start;
|
||||
if (speed == USB_SPEED_SUPER)
|
||||
if (speed >= USB_SPEED_SUPER)
|
||||
mul = 8;
|
||||
else
|
||||
mul = 2;
|
||||
|
@ -534,6 +527,8 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes,
|
|||
speed = "480"; break;
|
||||
case USB_SPEED_SUPER:
|
||||
speed = "5000"; break;
|
||||
case USB_SPEED_SUPER_PLUS:
|
||||
speed = "10000"; break;
|
||||
default:
|
||||
speed = "??";
|
||||
}
|
||||
|
@ -553,7 +548,7 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes,
|
|||
|
||||
/* super/high speed reserves 80%, full/low reserves 90% */
|
||||
if (usbdev->speed == USB_SPEED_HIGH ||
|
||||
usbdev->speed == USB_SPEED_SUPER)
|
||||
usbdev->speed >= USB_SPEED_SUPER)
|
||||
max = 800;
|
||||
else
|
||||
max = FRAME_TIME_MAX_USECS_ALLOC;
|
||||
|
@ -616,6 +611,7 @@ static ssize_t usb_device_read(struct file *file, char __user *buf,
|
|||
struct usb_bus *bus;
|
||||
ssize_t ret, total_written = 0;
|
||||
loff_t skip_bytes = *ppos;
|
||||
int id;
|
||||
|
||||
if (*ppos < 0)
|
||||
return -EINVAL;
|
||||
|
@ -624,9 +620,9 @@ static ssize_t usb_device_read(struct file *file, char __user *buf,
|
|||
if (!access_ok(VERIFY_WRITE, buf, nbytes))
|
||||
return -EFAULT;
|
||||
|
||||
mutex_lock(&usb_bus_list_lock);
|
||||
mutex_lock(&usb_bus_idr_lock);
|
||||
/* print devices for all busses */
|
||||
list_for_each_entry(bus, &usb_bus_list, bus_list) {
|
||||
idr_for_each_entry(&usb_bus_idr, bus, id) {
|
||||
/* recurse through all children of the root hub */
|
||||
if (!bus_to_hcd(bus)->rh_registered)
|
||||
continue;
|
||||
|
@ -635,12 +631,12 @@ static ssize_t usb_device_read(struct file *file, char __user *buf,
|
|||
bus->root_hub, bus, 0, 0, 0);
|
||||
usb_unlock_device(bus->root_hub);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&usb_bus_list_lock);
|
||||
mutex_unlock(&usb_bus_idr_lock);
|
||||
return ret;
|
||||
}
|
||||
total_written += ret;
|
||||
}
|
||||
mutex_unlock(&usb_bus_list_lock);
|
||||
mutex_unlock(&usb_bus_idr_lock);
|
||||
return total_written;
|
||||
}
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
#include <linux/user_namespace.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <linux/moduleparam.h>
|
||||
|
||||
|
@ -69,6 +70,7 @@ struct usb_dev_state {
|
|||
spinlock_t lock; /* protects the async urb lists */
|
||||
struct list_head async_pending;
|
||||
struct list_head async_completed;
|
||||
struct list_head memory_list;
|
||||
wait_queue_head_t wait; /* wake up if a request completed */
|
||||
unsigned int discsignr;
|
||||
struct pid *disc_pid;
|
||||
|
@ -77,6 +79,19 @@ struct usb_dev_state {
|
|||
unsigned long ifclaimed;
|
||||
u32 secid;
|
||||
u32 disabled_bulk_eps;
|
||||
bool privileges_dropped;
|
||||
unsigned long interface_allowed_mask;
|
||||
};
|
||||
|
||||
struct usb_memory {
|
||||
struct list_head memlist;
|
||||
int vma_use_count;
|
||||
int urb_use_count;
|
||||
u32 size;
|
||||
void *mem;
|
||||
dma_addr_t dma_handle;
|
||||
unsigned long vm_start;
|
||||
struct usb_dev_state *ps;
|
||||
};
|
||||
|
||||
struct async {
|
||||
|
@ -89,6 +104,7 @@ struct async {
|
|||
void __user *userbuffer;
|
||||
void __user *userurb;
|
||||
struct urb *urb;
|
||||
struct usb_memory *usbm;
|
||||
unsigned int mem_usage;
|
||||
int status;
|
||||
u32 secid;
|
||||
|
@ -162,6 +178,111 @@ static int connected(struct usb_dev_state *ps)
|
|||
ps->dev->state != USB_STATE_NOTATTACHED);
|
||||
}
|
||||
|
||||
static void dec_usb_memory_use_count(struct usb_memory *usbm, int *count)
|
||||
{
|
||||
struct usb_dev_state *ps = usbm->ps;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ps->lock, flags);
|
||||
--*count;
|
||||
if (usbm->urb_use_count == 0 && usbm->vma_use_count == 0) {
|
||||
list_del(&usbm->memlist);
|
||||
spin_unlock_irqrestore(&ps->lock, flags);
|
||||
|
||||
usb_free_coherent(ps->dev, usbm->size, usbm->mem,
|
||||
usbm->dma_handle);
|
||||
usbfs_decrease_memory_usage(
|
||||
usbm->size + sizeof(struct usb_memory));
|
||||
kfree(usbm);
|
||||
} else {
|
||||
spin_unlock_irqrestore(&ps->lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
static void usbdev_vm_open(struct vm_area_struct *vma)
|
||||
{
|
||||
struct usb_memory *usbm = vma->vm_private_data;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&usbm->ps->lock, flags);
|
||||
++usbm->vma_use_count;
|
||||
spin_unlock_irqrestore(&usbm->ps->lock, flags);
|
||||
}
|
||||
|
||||
static void usbdev_vm_close(struct vm_area_struct *vma)
|
||||
{
|
||||
struct usb_memory *usbm = vma->vm_private_data;
|
||||
|
||||
dec_usb_memory_use_count(usbm, &usbm->vma_use_count);
|
||||
}
|
||||
|
||||
struct vm_operations_struct usbdev_vm_ops = {
|
||||
.open = usbdev_vm_open,
|
||||
.close = usbdev_vm_close
|
||||
};
|
||||
|
||||
static int usbdev_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
struct usb_memory *usbm = NULL;
|
||||
struct usb_dev_state *ps = file->private_data;
|
||||
size_t size = vma->vm_end - vma->vm_start;
|
||||
void *mem;
|
||||
unsigned long flags;
|
||||
dma_addr_t dma_handle;
|
||||
int ret;
|
||||
|
||||
ret = usbfs_increase_memory_usage(size + sizeof(struct usb_memory));
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
usbm = kzalloc(sizeof(struct usb_memory), GFP_KERNEL);
|
||||
if (!usbm) {
|
||||
ret = -ENOMEM;
|
||||
goto error_decrease_mem;
|
||||
}
|
||||
|
||||
mem = usb_alloc_coherent(ps->dev, size, GFP_USER, &dma_handle);
|
||||
if (!mem) {
|
||||
ret = -ENOMEM;
|
||||
goto error_free_usbm;
|
||||
}
|
||||
|
||||
memset(mem, 0, size);
|
||||
|
||||
usbm->mem = mem;
|
||||
usbm->dma_handle = dma_handle;
|
||||
usbm->size = size;
|
||||
usbm->ps = ps;
|
||||
usbm->vm_start = vma->vm_start;
|
||||
usbm->vma_use_count = 1;
|
||||
INIT_LIST_HEAD(&usbm->memlist);
|
||||
|
||||
if (remap_pfn_range(vma, vma->vm_start,
|
||||
virt_to_phys(usbm->mem) >> PAGE_SHIFT,
|
||||
size, vma->vm_page_prot) < 0) {
|
||||
dec_usb_memory_use_count(usbm, &usbm->vma_use_count);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
vma->vm_flags |= VM_IO;
|
||||
vma->vm_flags |= (VM_DONTEXPAND | VM_DONTDUMP);
|
||||
vma->vm_ops = &usbdev_vm_ops;
|
||||
vma->vm_private_data = usbm;
|
||||
|
||||
spin_lock_irqsave(&ps->lock, flags);
|
||||
list_add_tail(&usbm->memlist, &ps->memory_list);
|
||||
spin_unlock_irqrestore(&ps->lock, flags);
|
||||
|
||||
return 0;
|
||||
|
||||
error_free_usbm:
|
||||
kfree(usbm);
|
||||
error_decrease_mem:
|
||||
usbfs_decrease_memory_usage(size + sizeof(struct usb_memory));
|
||||
error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes,
|
||||
loff_t *ppos)
|
||||
{
|
||||
|
@ -278,8 +399,13 @@ static void free_async(struct async *as)
|
|||
if (sg_page(&as->urb->sg[i]))
|
||||
kfree(sg_virt(&as->urb->sg[i]));
|
||||
}
|
||||
|
||||
kfree(as->urb->sg);
|
||||
kfree(as->urb->transfer_buffer);
|
||||
if (as->usbm == NULL)
|
||||
kfree(as->urb->transfer_buffer);
|
||||
else
|
||||
dec_usb_memory_use_count(as->usbm, &as->usbm->urb_use_count);
|
||||
|
||||
kfree(as->urb->setup_packet);
|
||||
usb_free_urb(as->urb);
|
||||
usbfs_decrease_memory_usage(as->mem_usage);
|
||||
|
@ -624,6 +750,10 @@ static int claimintf(struct usb_dev_state *ps, unsigned int ifnum)
|
|||
if (test_bit(ifnum, &ps->ifclaimed))
|
||||
return 0;
|
||||
|
||||
if (ps->privileges_dropped &&
|
||||
!test_bit(ifnum, &ps->interface_allowed_mask))
|
||||
return -EACCES;
|
||||
|
||||
intf = usb_ifnum_to_if(dev, ifnum);
|
||||
if (!intf)
|
||||
err = -ENOENT;
|
||||
|
@ -848,7 +978,7 @@ static struct usb_device *usbdev_lookup_by_devt(dev_t devt)
|
|||
(void *) (unsigned long) devt, match_devt);
|
||||
if (!dev)
|
||||
return NULL;
|
||||
return container_of(dev, struct usb_device, dev);
|
||||
return to_usb_device(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -861,7 +991,7 @@ static int usbdev_open(struct inode *inode, struct file *file)
|
|||
int ret;
|
||||
|
||||
ret = -ENOMEM;
|
||||
ps = kmalloc(sizeof(struct usb_dev_state), GFP_KERNEL);
|
||||
ps = kzalloc(sizeof(struct usb_dev_state), GFP_KERNEL);
|
||||
if (!ps)
|
||||
goto out_free_ps;
|
||||
|
||||
|
@ -889,16 +1019,15 @@ static int usbdev_open(struct inode *inode, struct file *file)
|
|||
|
||||
ps->dev = dev;
|
||||
ps->file = file;
|
||||
ps->interface_allowed_mask = 0xFFFFFFFF; /* 32 bits */
|
||||
spin_lock_init(&ps->lock);
|
||||
INIT_LIST_HEAD(&ps->list);
|
||||
INIT_LIST_HEAD(&ps->async_pending);
|
||||
INIT_LIST_HEAD(&ps->async_completed);
|
||||
INIT_LIST_HEAD(&ps->memory_list);
|
||||
init_waitqueue_head(&ps->wait);
|
||||
ps->discsignr = 0;
|
||||
ps->disc_pid = get_pid(task_pid(current));
|
||||
ps->cred = get_current_cred();
|
||||
ps->disccontext = NULL;
|
||||
ps->ifclaimed = 0;
|
||||
security_task_getsecid(current, &ps->secid);
|
||||
smp_wmb();
|
||||
list_add_tail(&ps->list, &dev->filelist);
|
||||
|
@ -945,6 +1074,7 @@ static int usbdev_release(struct inode *inode, struct file *file)
|
|||
free_async(as);
|
||||
as = async_getcompleted(ps);
|
||||
}
|
||||
|
||||
kfree(ps);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1198,6 +1328,28 @@ static int proc_connectinfo(struct usb_dev_state *ps, void __user *arg)
|
|||
|
||||
static int proc_resetdevice(struct usb_dev_state *ps)
|
||||
{
|
||||
struct usb_host_config *actconfig = ps->dev->actconfig;
|
||||
struct usb_interface *interface;
|
||||
int i, number;
|
||||
|
||||
/* Don't allow a device reset if the process has dropped the
|
||||
* privilege to do such things and any of the interfaces are
|
||||
* currently claimed.
|
||||
*/
|
||||
if (ps->privileges_dropped && actconfig) {
|
||||
for (i = 0; i < actconfig->desc.bNumInterfaces; ++i) {
|
||||
interface = actconfig->interface[i];
|
||||
number = interface->cur_altsetting->desc.bInterfaceNumber;
|
||||
if (usb_interface_claimed(interface) &&
|
||||
!test_bit(number, &ps->ifclaimed)) {
|
||||
dev_warn(&ps->dev->dev,
|
||||
"usbfs: interface %d claimed by %s while '%s' resets device\n",
|
||||
number, interface->dev.driver->name, current->comm);
|
||||
return -EACCES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return usb_reset_device(ps->dev);
|
||||
}
|
||||
|
||||
|
@ -1266,6 +1418,31 @@ static int proc_setconfig(struct usb_dev_state *ps, void __user *arg)
|
|||
return status;
|
||||
}
|
||||
|
||||
static struct usb_memory *
|
||||
find_memory_area(struct usb_dev_state *ps, const struct usbdevfs_urb *uurb)
|
||||
{
|
||||
struct usb_memory *usbm = NULL, *iter;
|
||||
unsigned long flags;
|
||||
unsigned long uurb_start = (unsigned long)uurb->buffer;
|
||||
|
||||
spin_lock_irqsave(&ps->lock, flags);
|
||||
list_for_each_entry(iter, &ps->memory_list, memlist) {
|
||||
if (uurb_start >= iter->vm_start &&
|
||||
uurb_start < iter->vm_start + iter->size) {
|
||||
if (uurb->buffer_length > iter->vm_start + iter->size -
|
||||
uurb_start) {
|
||||
usbm = ERR_PTR(-EINVAL);
|
||||
} else {
|
||||
usbm = iter;
|
||||
usbm->urb_use_count++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&ps->lock, flags);
|
||||
return usbm;
|
||||
}
|
||||
|
||||
static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb,
|
||||
struct usbdevfs_iso_packet_desc __user *iso_frame_desc,
|
||||
void __user *arg)
|
||||
|
@ -1378,11 +1555,10 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
|
|||
number_of_packets = uurb->number_of_packets;
|
||||
isofrmlen = sizeof(struct usbdevfs_iso_packet_desc) *
|
||||
number_of_packets;
|
||||
isopkt = kmalloc(isofrmlen, GFP_KERNEL);
|
||||
if (!isopkt)
|
||||
return -ENOMEM;
|
||||
if (copy_from_user(isopkt, iso_frame_desc, isofrmlen)) {
|
||||
ret = -EFAULT;
|
||||
isopkt = memdup_user(iso_frame_desc, isofrmlen);
|
||||
if (IS_ERR(isopkt)) {
|
||||
ret = PTR_ERR(isopkt);
|
||||
isopkt = NULL;
|
||||
goto error;
|
||||
}
|
||||
for (totlen = u = 0; u < number_of_packets; u++) {
|
||||
|
@ -1422,6 +1598,19 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
|
|||
goto error;
|
||||
}
|
||||
|
||||
as->usbm = find_memory_area(ps, uurb);
|
||||
if (IS_ERR(as->usbm)) {
|
||||
ret = PTR_ERR(as->usbm);
|
||||
as->usbm = NULL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* do not use SG buffers when memory mapped segments
|
||||
* are in use
|
||||
*/
|
||||
if (as->usbm)
|
||||
num_sgs = 0;
|
||||
|
||||
u += sizeof(struct async) + sizeof(struct urb) + uurb->buffer_length +
|
||||
num_sgs * sizeof(struct scatterlist);
|
||||
ret = usbfs_increase_memory_usage(u);
|
||||
|
@ -1459,29 +1648,35 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
|
|||
totlen -= u;
|
||||
}
|
||||
} else if (uurb->buffer_length > 0) {
|
||||
as->urb->transfer_buffer = kmalloc(uurb->buffer_length,
|
||||
GFP_KERNEL);
|
||||
if (!as->urb->transfer_buffer) {
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
if (as->usbm) {
|
||||
unsigned long uurb_start = (unsigned long)uurb->buffer;
|
||||
|
||||
if (!is_in) {
|
||||
if (copy_from_user(as->urb->transfer_buffer,
|
||||
uurb->buffer,
|
||||
uurb->buffer_length)) {
|
||||
ret = -EFAULT;
|
||||
as->urb->transfer_buffer = as->usbm->mem +
|
||||
(uurb_start - as->usbm->vm_start);
|
||||
} else {
|
||||
as->urb->transfer_buffer = kmalloc(uurb->buffer_length,
|
||||
GFP_KERNEL);
|
||||
if (!as->urb->transfer_buffer) {
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
} else if (uurb->type == USBDEVFS_URB_TYPE_ISO) {
|
||||
/*
|
||||
* Isochronous input data may end up being
|
||||
* discontiguous if some of the packets are short.
|
||||
* Clear the buffer so that the gaps don't leak
|
||||
* kernel data to userspace.
|
||||
*/
|
||||
memset(as->urb->transfer_buffer, 0,
|
||||
uurb->buffer_length);
|
||||
if (!is_in) {
|
||||
if (copy_from_user(as->urb->transfer_buffer,
|
||||
uurb->buffer,
|
||||
uurb->buffer_length)) {
|
||||
ret = -EFAULT;
|
||||
goto error;
|
||||
}
|
||||
} else if (uurb->type == USBDEVFS_URB_TYPE_ISO) {
|
||||
/*
|
||||
* Isochronous input data may end up being
|
||||
* discontiguous if some of the packets are
|
||||
* short. Clear the buffer so that the gaps
|
||||
* don't leak kernel data to userspace.
|
||||
*/
|
||||
memset(as->urb->transfer_buffer, 0,
|
||||
uurb->buffer_length);
|
||||
}
|
||||
}
|
||||
}
|
||||
as->urb->dev = ps->dev;
|
||||
|
@ -1528,10 +1723,14 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
|
|||
isopkt = NULL;
|
||||
as->ps = ps;
|
||||
as->userurb = arg;
|
||||
if (is_in && uurb->buffer_length > 0)
|
||||
if (as->usbm) {
|
||||
unsigned long uurb_start = (unsigned long)uurb->buffer;
|
||||
|
||||
as->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
as->urb->transfer_dma = as->usbm->dma_handle +
|
||||
(uurb_start - as->usbm->vm_start);
|
||||
} else if (is_in && uurb->buffer_length > 0)
|
||||
as->userbuffer = uurb->buffer;
|
||||
else
|
||||
as->userbuffer = NULL;
|
||||
as->signr = uurb->signr;
|
||||
as->ifnum = ifnum;
|
||||
as->pid = get_pid(task_pid(current));
|
||||
|
@ -1587,6 +1786,8 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
|
|||
return 0;
|
||||
|
||||
error:
|
||||
if (as && as->usbm)
|
||||
dec_usb_memory_use_count(as->usbm, &as->usbm->urb_use_count);
|
||||
kfree(isopkt);
|
||||
kfree(dr);
|
||||
if (as)
|
||||
|
@ -1903,7 +2104,7 @@ static int proc_releaseinterface(struct usb_dev_state *ps, void __user *arg)
|
|||
ret = releaseintf(ps, ifnum);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
destroy_async_on_interface (ps, ifnum);
|
||||
destroy_async_on_interface(ps, ifnum);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1915,6 +2116,9 @@ static int proc_ioctl(struct usb_dev_state *ps, struct usbdevfs_ioctl *ctl)
|
|||
struct usb_interface *intf = NULL;
|
||||
struct usb_driver *driver = NULL;
|
||||
|
||||
if (ps->privileges_dropped)
|
||||
return -EACCES;
|
||||
|
||||
/* alloc buffer */
|
||||
size = _IOC_SIZE(ctl->ioctl_code);
|
||||
if (size > 0) {
|
||||
|
@ -2040,7 +2244,8 @@ static int proc_get_capabilities(struct usb_dev_state *ps, void __user *arg)
|
|||
__u32 caps;
|
||||
|
||||
caps = USBDEVFS_CAP_ZERO_PACKET | USBDEVFS_CAP_NO_PACKET_SIZE_LIM |
|
||||
USBDEVFS_CAP_REAP_AFTER_DISCONNECT;
|
||||
USBDEVFS_CAP_REAP_AFTER_DISCONNECT | USBDEVFS_CAP_MMAP |
|
||||
USBDEVFS_CAP_DROP_PRIVILEGES;
|
||||
if (!ps->dev->bus->no_stop_on_short)
|
||||
caps |= USBDEVFS_CAP_BULK_CONTINUATION;
|
||||
if (ps->dev->bus->sg_tablesize)
|
||||
|
@ -2067,6 +2272,9 @@ static int proc_disconnect_claim(struct usb_dev_state *ps, void __user *arg)
|
|||
if (intf->dev.driver) {
|
||||
struct usb_driver *driver = to_usb_driver(intf->dev.driver);
|
||||
|
||||
if (ps->privileges_dropped)
|
||||
return -EACCES;
|
||||
|
||||
if ((dc.flags & USBDEVFS_DISCONNECT_CLAIM_IF_DRIVER) &&
|
||||
strncmp(dc.driver, intf->dev.driver->name,
|
||||
sizeof(dc.driver)) != 0)
|
||||
|
@ -2123,6 +2331,23 @@ static int proc_free_streams(struct usb_dev_state *ps, void __user *arg)
|
|||
return r;
|
||||
}
|
||||
|
||||
static int proc_drop_privileges(struct usb_dev_state *ps, void __user *arg)
|
||||
{
|
||||
u32 data;
|
||||
|
||||
if (copy_from_user(&data, arg, sizeof(data)))
|
||||
return -EFAULT;
|
||||
|
||||
/* This is an one way operation. Once privileges are
|
||||
* dropped, you cannot regain them. You may however reissue
|
||||
* this ioctl to shrink the allowed interfaces mask.
|
||||
*/
|
||||
ps->interface_allowed_mask &= data;
|
||||
ps->privileges_dropped = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: All requests here that have interface numbers as parameters
|
||||
* are assuming that somehow the configuration has been prevented from
|
||||
|
@ -2311,6 +2536,9 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
|
|||
case USBDEVFS_FREE_STREAMS:
|
||||
ret = proc_free_streams(ps, p);
|
||||
break;
|
||||
case USBDEVFS_DROP_PRIVILEGES:
|
||||
ret = proc_drop_privileges(ps, p);
|
||||
break;
|
||||
}
|
||||
|
||||
done:
|
||||
|
@ -2366,6 +2594,7 @@ const struct file_operations usbdev_file_operations = {
|
|||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = usbdev_compat_ioctl,
|
||||
#endif
|
||||
.mmap = usbdev_mmap,
|
||||
.open = usbdev_open,
|
||||
.release = usbdev_release,
|
||||
};
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/errno.h>
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include "usb.h"
|
||||
|
@ -155,7 +156,6 @@ int usb_register_dev(struct usb_interface *intf,
|
|||
int minor_base = class_driver->minor_base;
|
||||
int minor;
|
||||
char name[20];
|
||||
char *temp;
|
||||
|
||||
#ifdef CONFIG_USB_DYNAMIC_MINORS
|
||||
/*
|
||||
|
@ -192,14 +192,9 @@ int usb_register_dev(struct usb_interface *intf,
|
|||
|
||||
/* create a usb class device for this usb interface */
|
||||
snprintf(name, sizeof(name), class_driver->name, minor - minor_base);
|
||||
temp = strrchr(name, '/');
|
||||
if (temp && (temp[1] != '\0'))
|
||||
++temp;
|
||||
else
|
||||
temp = name;
|
||||
intf->usb_dev = device_create(usb_class->class, &intf->dev,
|
||||
MKDEV(USB_MAJOR, minor), class_driver,
|
||||
"%s", temp);
|
||||
"%s", kbasename(name));
|
||||
if (IS_ERR(intf->usb_dev)) {
|
||||
down_write(&minor_rwsem);
|
||||
usb_minors[minor] = NULL;
|
||||
|
|
|
@ -196,7 +196,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
|||
* The xHCI driver has its own irq management
|
||||
* make sure irq setup is not touched for xhci in generic hcd code
|
||||
*/
|
||||
if ((driver->flags & HCD_MASK) != HCD_USB3) {
|
||||
if ((driver->flags & HCD_MASK) < HCD_USB3) {
|
||||
if (!dev->irq) {
|
||||
dev_err(&dev->dev,
|
||||
"Found HC with no IRQ. Check BIOS/PCI %s setup!\n",
|
||||
|
|
|
@ -90,16 +90,15 @@ unsigned long usb_hcds_loaded;
|
|||
EXPORT_SYMBOL_GPL(usb_hcds_loaded);
|
||||
|
||||
/* host controllers we manage */
|
||||
LIST_HEAD (usb_bus_list);
|
||||
EXPORT_SYMBOL_GPL (usb_bus_list);
|
||||
DEFINE_IDR (usb_bus_idr);
|
||||
EXPORT_SYMBOL_GPL (usb_bus_idr);
|
||||
|
||||
/* used when allocating bus numbers */
|
||||
#define USB_MAXBUS 64
|
||||
static DECLARE_BITMAP(busmap, USB_MAXBUS);
|
||||
|
||||
/* used when updating list of hcds */
|
||||
DEFINE_MUTEX(usb_bus_list_lock); /* exported only for usbfs */
|
||||
EXPORT_SYMBOL_GPL (usb_bus_list_lock);
|
||||
DEFINE_MUTEX(usb_bus_idr_lock); /* exported only for usbfs */
|
||||
EXPORT_SYMBOL_GPL (usb_bus_idr_lock);
|
||||
|
||||
/* used for controlling access to virtual root hubs */
|
||||
static DEFINE_SPINLOCK(hcd_root_hub_lock);
|
||||
|
@ -128,6 +127,27 @@ static inline int is_root_hub(struct usb_device *udev)
|
|||
#define KERNEL_REL bin2bcd(((LINUX_VERSION_CODE >> 16) & 0x0ff))
|
||||
#define KERNEL_VER bin2bcd(((LINUX_VERSION_CODE >> 8) & 0x0ff))
|
||||
|
||||
/* usb 3.1 root hub device descriptor */
|
||||
static const u8 usb31_rh_dev_descriptor[18] = {
|
||||
0x12, /* __u8 bLength; */
|
||||
USB_DT_DEVICE, /* __u8 bDescriptorType; Device */
|
||||
0x10, 0x03, /* __le16 bcdUSB; v3.1 */
|
||||
|
||||
0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
|
||||
0x00, /* __u8 bDeviceSubClass; */
|
||||
0x03, /* __u8 bDeviceProtocol; USB 3 hub */
|
||||
0x09, /* __u8 bMaxPacketSize0; 2^9 = 512 Bytes */
|
||||
|
||||
0x6b, 0x1d, /* __le16 idVendor; Linux Foundation 0x1d6b */
|
||||
0x03, 0x00, /* __le16 idProduct; device 0x0003 */
|
||||
KERNEL_VER, KERNEL_REL, /* __le16 bcdDevice */
|
||||
|
||||
0x03, /* __u8 iManufacturer; */
|
||||
0x02, /* __u8 iProduct; */
|
||||
0x01, /* __u8 iSerialNumber; */
|
||||
0x01 /* __u8 bNumConfigurations; */
|
||||
};
|
||||
|
||||
/* usb 3.0 root hub device descriptor */
|
||||
static const u8 usb3_rh_dev_descriptor[18] = {
|
||||
0x12, /* __u8 bLength; */
|
||||
|
@ -557,6 +577,8 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
|
|||
case USB_DT_DEVICE << 8:
|
||||
switch (hcd->speed) {
|
||||
case HCD_USB31:
|
||||
bufp = usb31_rh_dev_descriptor;
|
||||
break;
|
||||
case HCD_USB3:
|
||||
bufp = usb3_rh_dev_descriptor;
|
||||
break;
|
||||
|
@ -645,9 +667,15 @@ nongeneric:
|
|||
/* non-generic request */
|
||||
switch (typeReq) {
|
||||
case GetHubStatus:
|
||||
case GetPortStatus:
|
||||
len = 4;
|
||||
break;
|
||||
case GetPortStatus:
|
||||
if (wValue == HUB_PORT_STATUS)
|
||||
len = 4;
|
||||
else
|
||||
/* other port status types return 8 bytes */
|
||||
len = 8;
|
||||
break;
|
||||
case GetHubDescriptor:
|
||||
len = sizeof (struct usb_hub_descriptor);
|
||||
break;
|
||||
|
@ -967,8 +995,6 @@ static void usb_bus_init (struct usb_bus *bus)
|
|||
bus->bandwidth_int_reqs = 0;
|
||||
bus->bandwidth_isoc_reqs = 0;
|
||||
mutex_init(&bus->usb_address0_mutex);
|
||||
|
||||
INIT_LIST_HEAD (&bus->bus_list);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
@ -988,18 +1014,14 @@ static int usb_register_bus(struct usb_bus *bus)
|
|||
int result = -E2BIG;
|
||||
int busnum;
|
||||
|
||||
mutex_lock(&usb_bus_list_lock);
|
||||
busnum = find_next_zero_bit(busmap, USB_MAXBUS, 1);
|
||||
if (busnum >= USB_MAXBUS) {
|
||||
printk (KERN_ERR "%s: too many buses\n", usbcore_name);
|
||||
mutex_lock(&usb_bus_idr_lock);
|
||||
busnum = idr_alloc(&usb_bus_idr, bus, 1, USB_MAXBUS, GFP_KERNEL);
|
||||
if (busnum < 0) {
|
||||
pr_err("%s: failed to get bus number\n", usbcore_name);
|
||||
goto error_find_busnum;
|
||||
}
|
||||
set_bit(busnum, busmap);
|
||||
bus->busnum = busnum;
|
||||
|
||||
/* Add it to the local list of buses */
|
||||
list_add (&bus->bus_list, &usb_bus_list);
|
||||
mutex_unlock(&usb_bus_list_lock);
|
||||
mutex_unlock(&usb_bus_idr_lock);
|
||||
|
||||
usb_notify_add_bus(bus);
|
||||
|
||||
|
@ -1008,7 +1030,7 @@ static int usb_register_bus(struct usb_bus *bus)
|
|||
return 0;
|
||||
|
||||
error_find_busnum:
|
||||
mutex_unlock(&usb_bus_list_lock);
|
||||
mutex_unlock(&usb_bus_idr_lock);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -1029,13 +1051,11 @@ static void usb_deregister_bus (struct usb_bus *bus)
|
|||
* controller code, as well as having it call this when cleaning
|
||||
* itself up
|
||||
*/
|
||||
mutex_lock(&usb_bus_list_lock);
|
||||
list_del (&bus->bus_list);
|
||||
mutex_unlock(&usb_bus_list_lock);
|
||||
mutex_lock(&usb_bus_idr_lock);
|
||||
idr_remove(&usb_bus_idr, bus->busnum);
|
||||
mutex_unlock(&usb_bus_idr_lock);
|
||||
|
||||
usb_notify_remove_bus(bus);
|
||||
|
||||
clear_bit(bus->busnum, busmap);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1063,12 +1083,12 @@ static int register_root_hub(struct usb_hcd *hcd)
|
|||
set_bit (devnum, usb_dev->bus->devmap.devicemap);
|
||||
usb_set_device_state(usb_dev, USB_STATE_ADDRESS);
|
||||
|
||||
mutex_lock(&usb_bus_list_lock);
|
||||
mutex_lock(&usb_bus_idr_lock);
|
||||
|
||||
usb_dev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
|
||||
retval = usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE);
|
||||
if (retval != sizeof usb_dev->descriptor) {
|
||||
mutex_unlock(&usb_bus_list_lock);
|
||||
mutex_unlock(&usb_bus_idr_lock);
|
||||
dev_dbg (parent_dev, "can't read %s device descriptor %d\n",
|
||||
dev_name(&usb_dev->dev), retval);
|
||||
return (retval < 0) ? retval : -EMSGSIZE;
|
||||
|
@ -1078,8 +1098,8 @@ static int register_root_hub(struct usb_hcd *hcd)
|
|||
retval = usb_get_bos_descriptor(usb_dev);
|
||||
if (!retval) {
|
||||
usb_dev->lpm_capable = usb_device_supports_lpm(usb_dev);
|
||||
} else if (usb_dev->speed == USB_SPEED_SUPER) {
|
||||
mutex_unlock(&usb_bus_list_lock);
|
||||
} else if (usb_dev->speed >= USB_SPEED_SUPER) {
|
||||
mutex_unlock(&usb_bus_idr_lock);
|
||||
dev_dbg(parent_dev, "can't read %s bos descriptor %d\n",
|
||||
dev_name(&usb_dev->dev), retval);
|
||||
return retval;
|
||||
|
@ -1099,7 +1119,7 @@ static int register_root_hub(struct usb_hcd *hcd)
|
|||
if (HCD_DEAD(hcd))
|
||||
usb_hc_died (hcd); /* This time clean up */
|
||||
}
|
||||
mutex_unlock(&usb_bus_list_lock);
|
||||
mutex_unlock(&usb_bus_idr_lock);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
@ -1408,7 +1428,8 @@ static void hcd_free_coherent(struct usb_bus *bus, dma_addr_t *dma_handle,
|
|||
|
||||
void usb_hcd_unmap_urb_setup_for_dma(struct usb_hcd *hcd, struct urb *urb)
|
||||
{
|
||||
if (urb->transfer_flags & URB_SETUP_MAP_SINGLE)
|
||||
if (IS_ENABLED(CONFIG_HAS_DMA) &&
|
||||
(urb->transfer_flags & URB_SETUP_MAP_SINGLE))
|
||||
dma_unmap_single(hcd->self.controller,
|
||||
urb->setup_dma,
|
||||
sizeof(struct usb_ctrlrequest),
|
||||
|
@ -1440,17 +1461,20 @@ void usb_hcd_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
|
|||
usb_hcd_unmap_urb_setup_for_dma(hcd, urb);
|
||||
|
||||
dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
|
||||
if (urb->transfer_flags & URB_DMA_MAP_SG)
|
||||
if (IS_ENABLED(CONFIG_HAS_DMA) &&
|
||||
(urb->transfer_flags & URB_DMA_MAP_SG))
|
||||
dma_unmap_sg(hcd->self.controller,
|
||||
urb->sg,
|
||||
urb->num_sgs,
|
||||
dir);
|
||||
else if (urb->transfer_flags & URB_DMA_MAP_PAGE)
|
||||
else if (IS_ENABLED(CONFIG_HAS_DMA) &&
|
||||
(urb->transfer_flags & URB_DMA_MAP_PAGE))
|
||||
dma_unmap_page(hcd->self.controller,
|
||||
urb->transfer_dma,
|
||||
urb->transfer_buffer_length,
|
||||
dir);
|
||||
else if (urb->transfer_flags & URB_DMA_MAP_SINGLE)
|
||||
else if (IS_ENABLED(CONFIG_HAS_DMA) &&
|
||||
(urb->transfer_flags & URB_DMA_MAP_SINGLE))
|
||||
dma_unmap_single(hcd->self.controller,
|
||||
urb->transfer_dma,
|
||||
urb->transfer_buffer_length,
|
||||
|
@ -1492,7 +1516,7 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
|
|||
if (usb_endpoint_xfer_control(&urb->ep->desc)) {
|
||||
if (hcd->self.uses_pio_for_control)
|
||||
return ret;
|
||||
if (hcd->self.uses_dma) {
|
||||
if (IS_ENABLED(CONFIG_HAS_DMA) && hcd->self.uses_dma) {
|
||||
urb->setup_dma = dma_map_single(
|
||||
hcd->self.controller,
|
||||
urb->setup_packet,
|
||||
|
@ -1518,7 +1542,7 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
|
|||
dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
|
||||
if (urb->transfer_buffer_length != 0
|
||||
&& !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
|
||||
if (hcd->self.uses_dma) {
|
||||
if (IS_ENABLED(CONFIG_HAS_DMA) && hcd->self.uses_dma) {
|
||||
if (urb->num_sgs) {
|
||||
int n;
|
||||
|
||||
|
@ -2112,7 +2136,7 @@ int usb_alloc_streams(struct usb_interface *interface,
|
|||
hcd = bus_to_hcd(dev->bus);
|
||||
if (!hcd->driver->alloc_streams || !hcd->driver->free_streams)
|
||||
return -EINVAL;
|
||||
if (dev->speed != USB_SPEED_SUPER)
|
||||
if (dev->speed < USB_SPEED_SUPER)
|
||||
return -EINVAL;
|
||||
if (dev->state < USB_STATE_CONFIGURED)
|
||||
return -ENODEV;
|
||||
|
@ -2160,7 +2184,7 @@ int usb_free_streams(struct usb_interface *interface,
|
|||
|
||||
dev = interface_to_usbdev(interface);
|
||||
hcd = bus_to_hcd(dev->bus);
|
||||
if (dev->speed != USB_SPEED_SUPER)
|
||||
if (dev->speed < USB_SPEED_SUPER)
|
||||
return -EINVAL;
|
||||
|
||||
/* Double-free is not allowed */
|
||||
|
@ -2208,7 +2232,7 @@ int usb_hcd_get_frame_number (struct usb_device *udev)
|
|||
|
||||
int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg)
|
||||
{
|
||||
struct usb_hcd *hcd = container_of(rhdev->bus, struct usb_hcd, self);
|
||||
struct usb_hcd *hcd = bus_to_hcd(rhdev->bus);
|
||||
int status;
|
||||
int old_state = hcd->state;
|
||||
|
||||
|
@ -2257,7 +2281,7 @@ int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg)
|
|||
|
||||
int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg)
|
||||
{
|
||||
struct usb_hcd *hcd = container_of(rhdev->bus, struct usb_hcd, self);
|
||||
struct usb_hcd *hcd = bus_to_hcd(rhdev->bus);
|
||||
int status;
|
||||
int old_state = hcd->state;
|
||||
|
||||
|
@ -2371,7 +2395,7 @@ int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num)
|
|||
* boards with root hubs hooked up to internal devices (instead of
|
||||
* just the OTG port) may need more attention to resetting...
|
||||
*/
|
||||
hcd = container_of (bus, struct usb_hcd, self);
|
||||
hcd = bus_to_hcd(bus);
|
||||
if (port_num && hcd->driver->start_port_reset)
|
||||
status = hcd->driver->start_port_reset(hcd, port_num);
|
||||
|
||||
|
@ -2778,9 +2802,11 @@ int usb_add_hcd(struct usb_hcd *hcd,
|
|||
rhdev->speed = USB_SPEED_WIRELESS;
|
||||
break;
|
||||
case HCD_USB3:
|
||||
case HCD_USB31:
|
||||
rhdev->speed = USB_SPEED_SUPER;
|
||||
break;
|
||||
case HCD_USB31:
|
||||
rhdev->speed = USB_SPEED_SUPER_PLUS;
|
||||
break;
|
||||
default:
|
||||
retval = -EINVAL;
|
||||
goto err_set_rh_speed;
|
||||
|
@ -2863,9 +2889,9 @@ error_create_attr_group:
|
|||
#ifdef CONFIG_PM
|
||||
cancel_work_sync(&hcd->wakeup_work);
|
||||
#endif
|
||||
mutex_lock(&usb_bus_list_lock);
|
||||
mutex_lock(&usb_bus_idr_lock);
|
||||
usb_disconnect(&rhdev); /* Sets rhdev to NULL */
|
||||
mutex_unlock(&usb_bus_list_lock);
|
||||
mutex_unlock(&usb_bus_idr_lock);
|
||||
err_register_root_hub:
|
||||
hcd->rh_pollable = 0;
|
||||
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
|
||||
|
@ -2932,9 +2958,9 @@ void usb_remove_hcd(struct usb_hcd *hcd)
|
|||
cancel_work_sync(&hcd->wakeup_work);
|
||||
#endif
|
||||
|
||||
mutex_lock(&usb_bus_list_lock);
|
||||
mutex_lock(&usb_bus_idr_lock);
|
||||
usb_disconnect(&rhdev); /* Sets rhdev to NULL */
|
||||
mutex_unlock(&usb_bus_list_lock);
|
||||
mutex_unlock(&usb_bus_idr_lock);
|
||||
|
||||
/*
|
||||
* tasklet_kill() isn't needed here because:
|
||||
|
|
|
@ -49,7 +49,7 @@ static void hub_event(struct work_struct *work);
|
|||
DEFINE_MUTEX(usb_port_peer_mutex);
|
||||
|
||||
/* cycle leds on hubs that aren't blinking for attention */
|
||||
static bool blinkenlights = 0;
|
||||
static bool blinkenlights;
|
||||
module_param(blinkenlights, bool, S_IRUGO);
|
||||
MODULE_PARM_DESC(blinkenlights, "true to cycle leds on hubs");
|
||||
|
||||
|
@ -78,7 +78,7 @@ MODULE_PARM_DESC(initial_descriptor_timeout,
|
|||
* otherwise the new scheme is used. If that fails and "use_both_schemes"
|
||||
* is set, then the driver will make another attempt, using the other scheme.
|
||||
*/
|
||||
static bool old_scheme_first = 0;
|
||||
static bool old_scheme_first;
|
||||
module_param(old_scheme_first, bool, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(old_scheme_first,
|
||||
"start with the old device initialization scheme");
|
||||
|
@ -298,7 +298,7 @@ static void usb_set_lpm_parameters(struct usb_device *udev)
|
|||
unsigned int hub_u1_del;
|
||||
unsigned int hub_u2_del;
|
||||
|
||||
if (!udev->lpm_capable || udev->speed != USB_SPEED_SUPER)
|
||||
if (!udev->lpm_capable || udev->speed < USB_SPEED_SUPER)
|
||||
return;
|
||||
|
||||
hub = usb_hub_to_struct_hub(udev->parent);
|
||||
|
@ -537,29 +537,34 @@ static int get_hub_status(struct usb_device *hdev,
|
|||
|
||||
/*
|
||||
* USB 2.0 spec Section 11.24.2.7
|
||||
* USB 3.1 takes into use the wValue and wLength fields, spec Section 10.16.2.6
|
||||
*/
|
||||
static int get_port_status(struct usb_device *hdev, int port1,
|
||||
struct usb_port_status *data)
|
||||
void *data, u16 value, u16 length)
|
||||
{
|
||||
int i, status = -ETIMEDOUT;
|
||||
|
||||
for (i = 0; i < USB_STS_RETRIES &&
|
||||
(status == -ETIMEDOUT || status == -EPIPE); i++) {
|
||||
status = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
|
||||
USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port1,
|
||||
data, sizeof(*data), USB_STS_TIMEOUT);
|
||||
USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, value,
|
||||
port1, data, length, USB_STS_TIMEOUT);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static int hub_port_status(struct usb_hub *hub, int port1,
|
||||
u16 *status, u16 *change)
|
||||
static int hub_ext_port_status(struct usb_hub *hub, int port1, int type,
|
||||
u16 *status, u16 *change, u32 *ext_status)
|
||||
{
|
||||
int ret;
|
||||
int len = 4;
|
||||
|
||||
if (type != HUB_PORT_STATUS)
|
||||
len = 8;
|
||||
|
||||
mutex_lock(&hub->status_mutex);
|
||||
ret = get_port_status(hub->hdev, port1, &hub->status->port);
|
||||
if (ret < 4) {
|
||||
ret = get_port_status(hub->hdev, port1, &hub->status->port, type, len);
|
||||
if (ret < len) {
|
||||
if (ret != -ENODEV)
|
||||
dev_err(hub->intfdev,
|
||||
"%s failed (err = %d)\n", __func__, ret);
|
||||
|
@ -568,13 +573,22 @@ static int hub_port_status(struct usb_hub *hub, int port1,
|
|||
} else {
|
||||
*status = le16_to_cpu(hub->status->port.wPortStatus);
|
||||
*change = le16_to_cpu(hub->status->port.wPortChange);
|
||||
|
||||
if (type != HUB_PORT_STATUS && ext_status)
|
||||
*ext_status = le32_to_cpu(
|
||||
hub->status->port.dwExtPortStatus);
|
||||
ret = 0;
|
||||
}
|
||||
mutex_unlock(&hub->status_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hub_port_status(struct usb_hub *hub, int port1,
|
||||
u16 *status, u16 *change)
|
||||
{
|
||||
return hub_ext_port_status(hub, port1, HUB_PORT_STATUS,
|
||||
status, change, NULL);
|
||||
}
|
||||
|
||||
static void kick_hub_wq(struct usb_hub *hub)
|
||||
{
|
||||
struct usb_interface *intf;
|
||||
|
@ -2131,7 +2145,7 @@ static void hub_disconnect_children(struct usb_device *udev)
|
|||
* Something got disconnected. Get rid of it and all of its children.
|
||||
*
|
||||
* If *pdev is a normal device then the parent hub must already be locked.
|
||||
* If *pdev is a root hub then the caller must hold the usb_bus_list_lock,
|
||||
* If *pdev is a root hub then the caller must hold the usb_bus_idr_lock,
|
||||
* which protects the set of root hubs as well as the list of buses.
|
||||
*
|
||||
* Only hub drivers (including virtual root hub drivers for host
|
||||
|
@ -2429,7 +2443,7 @@ static void set_usb_port_removable(struct usb_device *udev)
|
|||
* enumerated. The device descriptor is available, but not descriptors
|
||||
* for any device configuration. The caller must have locked either
|
||||
* the parent hub (if udev is a normal device) or else the
|
||||
* usb_bus_list_lock (if udev is a root hub). The parent's pointer to
|
||||
* usb_bus_idr_lock (if udev is a root hub). The parent's pointer to
|
||||
* udev has already been installed, but udev is not yet visible through
|
||||
* sysfs or other filesystem code.
|
||||
*
|
||||
|
@ -2612,6 +2626,32 @@ out_authorized:
|
|||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return 1 if port speed is SuperSpeedPlus, 0 otherwise
|
||||
* check it from the link protocol field of the current speed ID attribute.
|
||||
* current speed ID is got from ext port status request. Sublink speed attribute
|
||||
* table is returned with the hub BOS SSP device capability descriptor
|
||||
*/
|
||||
static int port_speed_is_ssp(struct usb_device *hdev, int speed_id)
|
||||
{
|
||||
int ssa_count;
|
||||
u32 ss_attr;
|
||||
int i;
|
||||
struct usb_ssp_cap_descriptor *ssp_cap = hdev->bos->ssp_cap;
|
||||
|
||||
if (!ssp_cap)
|
||||
return 0;
|
||||
|
||||
ssa_count = le32_to_cpu(ssp_cap->bmAttributes) &
|
||||
USB_SSP_SUBLINK_SPEED_ATTRIBS;
|
||||
|
||||
for (i = 0; i <= ssa_count; i++) {
|
||||
ss_attr = le32_to_cpu(ssp_cap->bmSublinkSpeedAttr[i]);
|
||||
if (speed_id == (ss_attr & USB_SSP_SUBLINK_SPEED_SSID))
|
||||
return !!(ss_attr & USB_SSP_SUBLINK_SPEED_LP);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns 1 if @hub is a WUSB root hub, 0 otherwise */
|
||||
static unsigned hub_is_wusb(struct usb_hub *hub)
|
||||
|
@ -2619,7 +2659,7 @@ static unsigned hub_is_wusb(struct usb_hub *hub)
|
|||
struct usb_hcd *hcd;
|
||||
if (hub->hdev->parent != NULL) /* not a root hub? */
|
||||
return 0;
|
||||
hcd = container_of(hub->hdev->bus, struct usb_hcd, self);
|
||||
hcd = bus_to_hcd(hub->hdev->bus);
|
||||
return hcd->wireless;
|
||||
}
|
||||
|
||||
|
@ -2645,7 +2685,7 @@ static unsigned hub_is_wusb(struct usb_hub *hub)
|
|||
*/
|
||||
static bool use_new_scheme(struct usb_device *udev, int retry)
|
||||
{
|
||||
if (udev->speed == USB_SPEED_SUPER)
|
||||
if (udev->speed >= USB_SPEED_SUPER)
|
||||
return false;
|
||||
|
||||
return USE_NEW_SCHEME(retry);
|
||||
|
@ -2676,6 +2716,7 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
|
|||
int delay_time, ret;
|
||||
u16 portstatus;
|
||||
u16 portchange;
|
||||
u32 ext_portstatus = 0;
|
||||
|
||||
for (delay_time = 0;
|
||||
delay_time < HUB_RESET_TIMEOUT;
|
||||
|
@ -2684,7 +2725,14 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
|
|||
msleep(delay);
|
||||
|
||||
/* read and decode port status */
|
||||
ret = hub_port_status(hub, port1, &portstatus, &portchange);
|
||||
if (hub_is_superspeedplus(hub->hdev))
|
||||
ret = hub_ext_port_status(hub, port1,
|
||||
HUB_EXT_PORT_STATUS,
|
||||
&portstatus, &portchange,
|
||||
&ext_portstatus);
|
||||
else
|
||||
ret = hub_port_status(hub, port1, &portstatus,
|
||||
&portchange);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -2727,6 +2775,10 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
|
|||
|
||||
if (hub_is_wusb(hub))
|
||||
udev->speed = USB_SPEED_WIRELESS;
|
||||
else if (hub_is_superspeedplus(hub->hdev) &&
|
||||
port_speed_is_ssp(hub->hdev, ext_portstatus &
|
||||
USB_EXT_PORT_STAT_RX_SPEED_ID))
|
||||
udev->speed = USB_SPEED_SUPER_PLUS;
|
||||
else if (hub_is_superspeed(hub->hdev))
|
||||
udev->speed = USB_SPEED_SUPER;
|
||||
else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
|
||||
|
@ -3989,7 +4041,7 @@ int usb_disable_lpm(struct usb_device *udev)
|
|||
struct usb_hcd *hcd;
|
||||
|
||||
if (!udev || !udev->parent ||
|
||||
udev->speed != USB_SPEED_SUPER ||
|
||||
udev->speed < USB_SPEED_SUPER ||
|
||||
!udev->lpm_capable ||
|
||||
udev->state < USB_STATE_DEFAULT)
|
||||
return 0;
|
||||
|
@ -4048,7 +4100,7 @@ void usb_enable_lpm(struct usb_device *udev)
|
|||
struct usb_port *port_dev;
|
||||
|
||||
if (!udev || !udev->parent ||
|
||||
udev->speed != USB_SPEED_SUPER ||
|
||||
udev->speed < USB_SPEED_SUPER ||
|
||||
!udev->lpm_capable ||
|
||||
udev->state < USB_STATE_DEFAULT)
|
||||
return;
|
||||
|
@ -4292,7 +4344,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
{
|
||||
struct usb_device *hdev = hub->hdev;
|
||||
struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
|
||||
int i, j, retval;
|
||||
int retries, operations, retval, i;
|
||||
unsigned delay = HUB_SHORT_RESET_TIME;
|
||||
enum usb_device_speed oldspeed = udev->speed;
|
||||
const char *speed;
|
||||
|
@ -4323,7 +4375,9 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
|
||||
retval = -ENODEV;
|
||||
|
||||
if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != udev->speed) {
|
||||
/* Don't allow speed changes at reset, except usb 3.0 to faster */
|
||||
if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != udev->speed &&
|
||||
!(oldspeed == USB_SPEED_SUPER && udev->speed > oldspeed)) {
|
||||
dev_dbg(&udev->dev, "device reset changed speed!\n");
|
||||
goto fail;
|
||||
}
|
||||
|
@ -4335,6 +4389,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
* reported as 0xff in the device descriptor). WUSB1.0[4.8.1].
|
||||
*/
|
||||
switch (udev->speed) {
|
||||
case USB_SPEED_SUPER_PLUS:
|
||||
case USB_SPEED_SUPER:
|
||||
case USB_SPEED_WIRELESS: /* fixed at 512 */
|
||||
udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);
|
||||
|
@ -4361,7 +4416,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
else
|
||||
speed = usb_speed_string(udev->speed);
|
||||
|
||||
if (udev->speed != USB_SPEED_SUPER)
|
||||
if (udev->speed < USB_SPEED_SUPER)
|
||||
dev_info(&udev->dev,
|
||||
"%s %s USB device number %d using %s\n",
|
||||
(udev->config) ? "reset" : "new", speed,
|
||||
|
@ -4394,7 +4449,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
* first 8 bytes of the device descriptor to get the ep0 maxpacket
|
||||
* value.
|
||||
*/
|
||||
for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) {
|
||||
for (retries = 0; retries < GET_DESCRIPTOR_TRIES; (++retries, msleep(100))) {
|
||||
bool did_new_scheme = false;
|
||||
|
||||
if (use_new_scheme(udev, retry_counter)) {
|
||||
|
@ -4421,7 +4476,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
* 255 is for WUSB devices, we actually need to use
|
||||
* 512 (WUSB1.0[4.8.1]).
|
||||
*/
|
||||
for (j = 0; j < 3; ++j) {
|
||||
for (operations = 0; operations < 3; ++operations) {
|
||||
buf->bMaxPacketSize0 = 0;
|
||||
r = usb_control_msg(udev, usb_rcvaddr0pipe(),
|
||||
USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
|
||||
|
@ -4441,7 +4496,13 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
r = -EPROTO;
|
||||
break;
|
||||
}
|
||||
if (r == 0)
|
||||
/*
|
||||
* Some devices time out if they are powered on
|
||||
* when already connected. They need a second
|
||||
* reset. But only on the first attempt,
|
||||
* lest we get into a time out/reset loop
|
||||
*/
|
||||
if (r == 0 || (r == -ETIMEDOUT && retries == 0))
|
||||
break;
|
||||
}
|
||||
udev->descriptor.bMaxPacketSize0 =
|
||||
|
@ -4473,7 +4534,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
* authorization will assign the final address.
|
||||
*/
|
||||
if (udev->wusb == 0) {
|
||||
for (j = 0; j < SET_ADDRESS_TRIES; ++j) {
|
||||
for (operations = 0; operations < SET_ADDRESS_TRIES; ++operations) {
|
||||
retval = hub_set_address(udev, devnum);
|
||||
if (retval >= 0)
|
||||
break;
|
||||
|
@ -4485,11 +4546,12 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
devnum, retval);
|
||||
goto fail;
|
||||
}
|
||||
if (udev->speed == USB_SPEED_SUPER) {
|
||||
if (udev->speed >= USB_SPEED_SUPER) {
|
||||
devnum = udev->devnum;
|
||||
dev_info(&udev->dev,
|
||||
"%s SuperSpeed USB device number %d using %s\n",
|
||||
"%s SuperSpeed%s USB device number %d using %s\n",
|
||||
(udev->config) ? "reset" : "new",
|
||||
(udev->speed == USB_SPEED_SUPER_PLUS) ? "Plus" : "",
|
||||
devnum, udev->bus->controller->driver->name);
|
||||
}
|
||||
|
||||
|
@ -4528,7 +4590,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
* got from those devices show they aren't superspeed devices. Warm
|
||||
* reset the port attached by the devices can fix them.
|
||||
*/
|
||||
if ((udev->speed == USB_SPEED_SUPER) &&
|
||||
if ((udev->speed >= USB_SPEED_SUPER) &&
|
||||
(le16_to_cpu(udev->descriptor.bcdUSB) < 0x0300)) {
|
||||
dev_err(&udev->dev, "got a wrong device descriptor, "
|
||||
"warm reset device\n");
|
||||
|
@ -4539,7 +4601,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
}
|
||||
|
||||
if (udev->descriptor.bMaxPacketSize0 == 0xff ||
|
||||
udev->speed == USB_SPEED_SUPER)
|
||||
udev->speed >= USB_SPEED_SUPER)
|
||||
i = 512;
|
||||
else
|
||||
i = udev->descriptor.bMaxPacketSize0;
|
||||
|
@ -4749,7 +4811,7 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
|
|||
udev->level = hdev->level + 1;
|
||||
udev->wusb = hub_is_wusb(hub);
|
||||
|
||||
/* Only USB 3.0 devices are connected to SuperSpeed hubs. */
|
||||
/* Devices connected to SuperSpeed hubs are USB 3.0 or later */
|
||||
if (hub_is_superspeed(hub->hdev))
|
||||
udev->speed = USB_SPEED_SUPER;
|
||||
else
|
||||
|
|
|
@ -140,6 +140,13 @@ static inline int hub_is_superspeed(struct usb_device *hdev)
|
|||
return hdev->descriptor.bDeviceProtocol == USB_HUB_PR_SS;
|
||||
}
|
||||
|
||||
static inline int hub_is_superspeedplus(struct usb_device *hdev)
|
||||
{
|
||||
return (hdev->descriptor.bDeviceProtocol == USB_HUB_PR_SS &&
|
||||
le16_to_cpu(hdev->descriptor.bcdUSB) >= 0x0310 &&
|
||||
hdev->bos->ssp_cap);
|
||||
}
|
||||
|
||||
static inline unsigned hub_power_on_good_delay(struct usb_hub *hub)
|
||||
{
|
||||
unsigned delay = hub->descriptor->bPwrOn2PwrGood * 2;
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* of.c The helpers for hcd device tree support
|
||||
*
|
||||
* Copyright (C) 2016 Freescale Semiconductor, Inc.
|
||||
* Author: Peter Chen <peter.chen@freescale.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 of
|
||||
* the License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/of.h>
|
||||
|
||||
/**
|
||||
* usb_of_get_child_node - Find the device node match port number
|
||||
* @parent: the parent device node
|
||||
* @portnum: the port number which device is connecting
|
||||
*
|
||||
* Find the node from device tree according to its port number.
|
||||
*
|
||||
* Return: On success, a pointer to the device node, %NULL on failure.
|
||||
*/
|
||||
struct device_node *usb_of_get_child_node(struct device_node *parent,
|
||||
int portnum)
|
||||
{
|
||||
struct device_node *node;
|
||||
u32 port;
|
||||
|
||||
for_each_child_of_node(parent, node) {
|
||||
if (!of_property_read_u32(node, "reg", &port)) {
|
||||
if (port == portnum)
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_of_get_child_node);
|
||||
|
|
@ -23,10 +23,12 @@ static ssize_t field##_show(struct device *dev, \
|
|||
{ \
|
||||
struct usb_device *udev; \
|
||||
struct usb_host_config *actconfig; \
|
||||
ssize_t rc = 0; \
|
||||
ssize_t rc; \
|
||||
\
|
||||
udev = to_usb_device(dev); \
|
||||
usb_lock_device(udev); \
|
||||
rc = usb_lock_device_interruptible(udev); \
|
||||
if (rc < 0) \
|
||||
return -EINTR; \
|
||||
actconfig = udev->actconfig; \
|
||||
if (actconfig) \
|
||||
rc = sprintf(buf, format_string, \
|
||||
|
@ -47,10 +49,12 @@ static ssize_t bMaxPower_show(struct device *dev,
|
|||
{
|
||||
struct usb_device *udev;
|
||||
struct usb_host_config *actconfig;
|
||||
ssize_t rc = 0;
|
||||
ssize_t rc;
|
||||
|
||||
udev = to_usb_device(dev);
|
||||
usb_lock_device(udev);
|
||||
rc = usb_lock_device_interruptible(udev);
|
||||
if (rc < 0)
|
||||
return -EINTR;
|
||||
actconfig = udev->actconfig;
|
||||
if (actconfig)
|
||||
rc = sprintf(buf, "%dmA\n", usb_get_max_power(udev, actconfig));
|
||||
|
@ -64,10 +68,12 @@ static ssize_t configuration_show(struct device *dev,
|
|||
{
|
||||
struct usb_device *udev;
|
||||
struct usb_host_config *actconfig;
|
||||
ssize_t rc = 0;
|
||||
ssize_t rc;
|
||||
|
||||
udev = to_usb_device(dev);
|
||||
usb_lock_device(udev);
|
||||
rc = usb_lock_device_interruptible(udev);
|
||||
if (rc < 0)
|
||||
return -EINTR;
|
||||
actconfig = udev->actconfig;
|
||||
if (actconfig && actconfig->string)
|
||||
rc = sprintf(buf, "%s\n", actconfig->string);
|
||||
|
@ -84,11 +90,13 @@ static ssize_t bConfigurationValue_store(struct device *dev,
|
|||
const char *buf, size_t count)
|
||||
{
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
int config, value;
|
||||
int config, value, rc;
|
||||
|
||||
if (sscanf(buf, "%d", &config) != 1 || config < -1 || config > 255)
|
||||
return -EINVAL;
|
||||
usb_lock_device(udev);
|
||||
rc = usb_lock_device_interruptible(udev);
|
||||
if (rc < 0)
|
||||
return -EINTR;
|
||||
value = usb_set_configuration(udev, config);
|
||||
usb_unlock_device(udev);
|
||||
return (value < 0) ? value : count;
|
||||
|
@ -105,7 +113,9 @@ static ssize_t name##_show(struct device *dev, \
|
|||
int retval; \
|
||||
\
|
||||
udev = to_usb_device(dev); \
|
||||
usb_lock_device(udev); \
|
||||
retval = usb_lock_device_interruptible(udev); \
|
||||
if (retval < 0) \
|
||||
return -EINTR; \
|
||||
retval = sprintf(buf, "%s\n", udev->name); \
|
||||
usb_unlock_device(udev); \
|
||||
return retval; \
|
||||
|
@ -141,6 +151,9 @@ static ssize_t speed_show(struct device *dev, struct device_attribute *attr,
|
|||
case USB_SPEED_SUPER:
|
||||
speed = "5000";
|
||||
break;
|
||||
case USB_SPEED_SUPER_PLUS:
|
||||
speed = "10000";
|
||||
break;
|
||||
default:
|
||||
speed = "unknown";
|
||||
}
|
||||
|
@ -224,11 +237,13 @@ static ssize_t avoid_reset_quirk_store(struct device *dev,
|
|||
const char *buf, size_t count)
|
||||
{
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
int val;
|
||||
int val, rc;
|
||||
|
||||
if (sscanf(buf, "%d", &val) != 1 || val < 0 || val > 1)
|
||||
return -EINVAL;
|
||||
usb_lock_device(udev);
|
||||
rc = usb_lock_device_interruptible(udev);
|
||||
if (rc < 0)
|
||||
return -EINTR;
|
||||
if (val)
|
||||
udev->quirks |= USB_QUIRK_RESET;
|
||||
else
|
||||
|
@ -294,7 +309,7 @@ static ssize_t persist_store(struct device *dev, struct device_attribute *attr,
|
|||
const char *buf, size_t count)
|
||||
{
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
int value;
|
||||
int value, rc;
|
||||
|
||||
/* Hubs are always enabled for USB_PERSIST */
|
||||
if (udev->descriptor.bDeviceClass == USB_CLASS_HUB)
|
||||
|
@ -303,7 +318,9 @@ static ssize_t persist_store(struct device *dev, struct device_attribute *attr,
|
|||
if (sscanf(buf, "%d", &value) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
usb_lock_device(udev);
|
||||
rc = usb_lock_device_interruptible(udev);
|
||||
if (rc < 0)
|
||||
return -EINTR;
|
||||
udev->persist_enabled = !!value;
|
||||
usb_unlock_device(udev);
|
||||
return count;
|
||||
|
@ -420,13 +437,16 @@ static ssize_t level_store(struct device *dev, struct device_attribute *attr,
|
|||
int len = count;
|
||||
char *cp;
|
||||
int rc = count;
|
||||
int rv;
|
||||
|
||||
warn_level();
|
||||
cp = memchr(buf, '\n', count);
|
||||
if (cp)
|
||||
len = cp - buf;
|
||||
|
||||
usb_lock_device(udev);
|
||||
rv = usb_lock_device_interruptible(udev);
|
||||
if (rv < 0)
|
||||
return -EINTR;
|
||||
|
||||
if (len == sizeof on_string - 1 &&
|
||||
strncmp(buf, on_string, len) == 0)
|
||||
|
@ -466,7 +486,9 @@ static ssize_t usb2_hardware_lpm_store(struct device *dev,
|
|||
bool value;
|
||||
int ret;
|
||||
|
||||
usb_lock_device(udev);
|
||||
ret = usb_lock_device_interruptible(udev);
|
||||
if (ret < 0)
|
||||
return -EINTR;
|
||||
|
||||
ret = strtobool(buf, &value);
|
||||
|
||||
|
@ -536,8 +558,11 @@ static ssize_t usb3_hardware_lpm_u1_show(struct device *dev,
|
|||
{
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
const char *p;
|
||||
int rc;
|
||||
|
||||
usb_lock_device(udev);
|
||||
rc = usb_lock_device_interruptible(udev);
|
||||
if (rc < 0)
|
||||
return -EINTR;
|
||||
|
||||
if (udev->usb3_lpm_u1_enabled)
|
||||
p = "enabled";
|
||||
|
@ -555,8 +580,11 @@ static ssize_t usb3_hardware_lpm_u2_show(struct device *dev,
|
|||
{
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
const char *p;
|
||||
int rc;
|
||||
|
||||
usb_lock_device(udev);
|
||||
rc = usb_lock_device_interruptible(udev);
|
||||
if (rc < 0)
|
||||
return -EINTR;
|
||||
|
||||
if (udev->usb3_lpm_u2_enabled)
|
||||
p = "enabled";
|
||||
|
@ -822,7 +850,6 @@ read_descriptors(struct file *filp, struct kobject *kobj,
|
|||
* Following that are the raw descriptor entries for all the
|
||||
* configurations (config plus subsidiary descriptors).
|
||||
*/
|
||||
usb_lock_device(udev);
|
||||
for (cfgno = -1; cfgno < udev->descriptor.bNumConfigurations &&
|
||||
nleft > 0; ++cfgno) {
|
||||
if (cfgno < 0) {
|
||||
|
@ -843,7 +870,6 @@ read_descriptors(struct file *filp, struct kobject *kobj,
|
|||
off -= srclen;
|
||||
}
|
||||
}
|
||||
usb_unlock_device(udev);
|
||||
return count - nleft;
|
||||
}
|
||||
|
||||
|
@ -969,7 +995,9 @@ static ssize_t supports_autosuspend_show(struct device *dev,
|
|||
{
|
||||
int s;
|
||||
|
||||
device_lock(dev);
|
||||
s = device_lock_interruptible(dev);
|
||||
if (s < 0)
|
||||
return -EINTR;
|
||||
/* Devices will be autosuspended even when an interface isn't claimed */
|
||||
s = (!dev->driver || to_usb_driver(dev->driver)->supports_autosuspend);
|
||||
device_unlock(dev);
|
||||
|
|
|
@ -401,7 +401,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
|
|||
/* SuperSpeed isoc endpoints have up to 16 bursts of up to
|
||||
* 3 packets each
|
||||
*/
|
||||
if (dev->speed == USB_SPEED_SUPER) {
|
||||
if (dev->speed >= USB_SPEED_SUPER) {
|
||||
int burst = 1 + ep->ss_ep_comp.bMaxBurst;
|
||||
int mult = USB_SS_MULT(ep->ss_ep_comp.bmAttributes);
|
||||
max *= burst;
|
||||
|
@ -499,6 +499,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
|
|||
}
|
||||
/* too big? */
|
||||
switch (dev->speed) {
|
||||
case USB_SPEED_SUPER_PLUS:
|
||||
case USB_SPEED_SUPER: /* units are 125us */
|
||||
/* Handle up to 2^(16-1) microframes */
|
||||
if (urb->interval > (1 << 15))
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include <linux/mutex.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/usb/of.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <linux/scatterlist.h>
|
||||
|
@ -241,7 +242,7 @@ static int __each_dev(struct device *dev, void *data)
|
|||
if (!is_usb_device(dev))
|
||||
return 0;
|
||||
|
||||
return arg->fn(container_of(dev, struct usb_device, dev), arg->data);
|
||||
return arg->fn(to_usb_device(dev), arg->data);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -397,7 +398,7 @@ struct device_type usb_device_type = {
|
|||
/* Returns 1 if @usb_bus is WUSB, 0 otherwise */
|
||||
static unsigned usb_bus_is_wusb(struct usb_bus *bus)
|
||||
{
|
||||
struct usb_hcd *hcd = container_of(bus, struct usb_hcd, self);
|
||||
struct usb_hcd *hcd = bus_to_hcd(bus);
|
||||
return hcd->wireless;
|
||||
}
|
||||
|
||||
|
@ -470,6 +471,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
|
|||
dev->route = 0;
|
||||
|
||||
dev->dev.parent = bus->controller;
|
||||
dev->dev.of_node = bus->controller->of_node;
|
||||
dev_set_name(&dev->dev, "usb%d", bus->busnum);
|
||||
root_hub = 1;
|
||||
} else {
|
||||
|
@ -494,6 +496,14 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
|
|||
dev->dev.parent = &parent->dev;
|
||||
dev_set_name(&dev->dev, "%d-%s", bus->busnum, dev->devpath);
|
||||
|
||||
if (!parent->parent) {
|
||||
/* device under root hub's port */
|
||||
port1 = usb_hcd_find_raw_port_number(usb_hcd,
|
||||
port1);
|
||||
}
|
||||
dev->dev.of_node = usb_of_get_child_node(parent->dev.of_node,
|
||||
port1);
|
||||
|
||||
/* hub driver sets up TT records */
|
||||
}
|
||||
|
||||
|
@ -1115,6 +1125,7 @@ static void __exit usb_exit(void)
|
|||
bus_unregister(&usb_bus_type);
|
||||
usb_acpi_unregister();
|
||||
usb_debugfs_cleanup();
|
||||
idr_destroy(&usb_bus_idr);
|
||||
}
|
||||
|
||||
subsys_initcall(usb_init);
|
||||
|
|
|
@ -45,7 +45,7 @@ static inline unsigned usb_get_max_power(struct usb_device *udev,
|
|||
struct usb_host_config *c)
|
||||
{
|
||||
/* SuperSpeed power is in 8 mA units; others are in 2 mA units */
|
||||
unsigned mul = (udev->speed == USB_SPEED_SUPER ? 8 : 2);
|
||||
unsigned mul = (udev->speed >= USB_SPEED_SUPER ? 8 : 2);
|
||||
|
||||
return c->desc.bMaxPower * mul;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ config USB_DWC2
|
|||
tristate "DesignWare USB2 DRD Core Support"
|
||||
depends on HAS_DMA
|
||||
depends on USB || USB_GADGET
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Say Y here if your system has a Dual Role Hi-Speed USB
|
||||
controller based on the DesignWare HSOTG IP Core.
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -44,6 +44,26 @@
|
|||
#include <linux/usb/phy.h>
|
||||
#include "hw.h"
|
||||
|
||||
/*
|
||||
* Suggested defines for tracers:
|
||||
* - no_printk: Disable tracing
|
||||
* - pr_info: Print this info to the console
|
||||
* - trace_printk: Print this info to trace buffer (good for verbose logging)
|
||||
*/
|
||||
|
||||
#define DWC2_TRACE_SCHEDULER no_printk
|
||||
#define DWC2_TRACE_SCHEDULER_VB no_printk
|
||||
|
||||
/* Detailed scheduler tracing, but won't overwhelm console */
|
||||
#define dwc2_sch_dbg(hsotg, fmt, ...) \
|
||||
DWC2_TRACE_SCHEDULER(pr_fmt("%s: SCH: " fmt), \
|
||||
dev_name(hsotg->dev), ##__VA_ARGS__)
|
||||
|
||||
/* Verbose scheduler tracing */
|
||||
#define dwc2_sch_vdbg(hsotg, fmt, ...) \
|
||||
DWC2_TRACE_SCHEDULER_VB(pr_fmt("%s: SCH: " fmt), \
|
||||
dev_name(hsotg->dev), ##__VA_ARGS__)
|
||||
|
||||
static inline u32 dwc2_readl(const void __iomem *addr)
|
||||
{
|
||||
u32 value = __raw_readl(addr);
|
||||
|
@ -572,6 +592,84 @@ struct dwc2_hregs_backup {
|
|||
bool valid;
|
||||
};
|
||||
|
||||
/*
|
||||
* Constants related to high speed periodic scheduling
|
||||
*
|
||||
* We have a periodic schedule that is DWC2_HS_SCHEDULE_UFRAMES long. From a
|
||||
* reservation point of view it's assumed that the schedule goes right back to
|
||||
* the beginning after the end of the schedule.
|
||||
*
|
||||
* What does that mean for scheduling things with a long interval? It means
|
||||
* we'll reserve time for them in every possible microframe that they could
|
||||
* ever be scheduled in. ...but we'll still only actually schedule them as
|
||||
* often as they were requested.
|
||||
*
|
||||
* We keep our schedule in a "bitmap" structure. This simplifies having
|
||||
* to keep track of and merge intervals: we just let the bitmap code do most
|
||||
* of the heavy lifting. In a way scheduling is much like memory allocation.
|
||||
*
|
||||
* We schedule 100us per uframe or 80% of 125us (the maximum amount you're
|
||||
* supposed to schedule for periodic transfers). That's according to spec.
|
||||
*
|
||||
* Note that though we only schedule 80% of each microframe, the bitmap that we
|
||||
* keep the schedule in is tightly packed (AKA it doesn't have 100us worth of
|
||||
* space for each uFrame).
|
||||
*
|
||||
* Requirements:
|
||||
* - DWC2_HS_SCHEDULE_UFRAMES must even divide 0x4000 (HFNUM_MAX_FRNUM + 1)
|
||||
* - DWC2_HS_SCHEDULE_UFRAMES must be 8 times DWC2_LS_SCHEDULE_FRAMES (probably
|
||||
* could be any multiple of 8 times DWC2_LS_SCHEDULE_FRAMES, but there might
|
||||
* be bugs). The 8 comes from the USB spec: number of microframes per frame.
|
||||
*/
|
||||
#define DWC2_US_PER_UFRAME 125
|
||||
#define DWC2_HS_PERIODIC_US_PER_UFRAME 100
|
||||
|
||||
#define DWC2_HS_SCHEDULE_UFRAMES 8
|
||||
#define DWC2_HS_SCHEDULE_US (DWC2_HS_SCHEDULE_UFRAMES * \
|
||||
DWC2_HS_PERIODIC_US_PER_UFRAME)
|
||||
|
||||
/*
|
||||
* Constants related to low speed scheduling
|
||||
*
|
||||
* For high speed we schedule every 1us. For low speed that's a bit overkill,
|
||||
* so we make up a unit called a "slice" that's worth 25us. There are 40
|
||||
* slices in a full frame and we can schedule 36 of those (90%) for periodic
|
||||
* transfers.
|
||||
*
|
||||
* Our low speed schedule can be as short as 1 frame or could be longer. When
|
||||
* we only schedule 1 frame it means that we'll need to reserve a time every
|
||||
* frame even for things that only transfer very rarely, so something that runs
|
||||
* every 2048 frames will get time reserved in every frame. Our low speed
|
||||
* schedule can be longer and we'll be able to handle more overlap, but that
|
||||
* will come at increased memory cost and increased time to schedule.
|
||||
*
|
||||
* Note: one other advantage of a short low speed schedule is that if we mess
|
||||
* up and miss scheduling we can jump in and use any of the slots that we
|
||||
* happened to reserve.
|
||||
*
|
||||
* With 25 us per slice and 1 frame in the schedule, we only need 4 bytes for
|
||||
* the schedule. There will be one schedule per TT.
|
||||
*
|
||||
* Requirements:
|
||||
* - DWC2_US_PER_SLICE must evenly divide DWC2_LS_PERIODIC_US_PER_FRAME.
|
||||
*/
|
||||
#define DWC2_US_PER_SLICE 25
|
||||
#define DWC2_SLICES_PER_UFRAME (DWC2_US_PER_UFRAME / DWC2_US_PER_SLICE)
|
||||
|
||||
#define DWC2_ROUND_US_TO_SLICE(us) \
|
||||
(DIV_ROUND_UP((us), DWC2_US_PER_SLICE) * \
|
||||
DWC2_US_PER_SLICE)
|
||||
|
||||
#define DWC2_LS_PERIODIC_US_PER_FRAME \
|
||||
900
|
||||
#define DWC2_LS_PERIODIC_SLICES_PER_FRAME \
|
||||
(DWC2_LS_PERIODIC_US_PER_FRAME / \
|
||||
DWC2_US_PER_SLICE)
|
||||
|
||||
#define DWC2_LS_SCHEDULE_FRAMES 1
|
||||
#define DWC2_LS_SCHEDULE_SLICES (DWC2_LS_SCHEDULE_FRAMES * \
|
||||
DWC2_LS_PERIODIC_SLICES_PER_FRAME)
|
||||
|
||||
/**
|
||||
* struct dwc2_hsotg - Holds the state of the driver, including the non-periodic
|
||||
* and periodic schedules
|
||||
|
@ -657,11 +755,14 @@ struct dwc2_hregs_backup {
|
|||
* periodic_sched_ready because it must be rescheduled for
|
||||
* the next frame. Otherwise, the item moves to
|
||||
* periodic_sched_inactive.
|
||||
* @split_order: List keeping track of channels doing splits, in order.
|
||||
* @periodic_usecs: Total bandwidth claimed so far for periodic transfers.
|
||||
* This value is in microseconds per (micro)frame. The
|
||||
* assumption is that all periodic transfers may occur in
|
||||
* the same (micro)frame.
|
||||
* @frame_usecs: Internal variable used by the microframe scheduler
|
||||
* @hs_periodic_bitmap: Bitmap used by the microframe scheduler any time the
|
||||
* host is in high speed mode; low speed schedules are
|
||||
* stored elsewhere since we need one per TT.
|
||||
* @frame_number: Frame number read from the core at SOF. The value ranges
|
||||
* from 0 to HFNUM_MAX_FRNUM.
|
||||
* @periodic_qh_count: Count of periodic QHs, if using several eps. Used for
|
||||
|
@ -780,16 +881,19 @@ struct dwc2_hsotg {
|
|||
struct list_head periodic_sched_ready;
|
||||
struct list_head periodic_sched_assigned;
|
||||
struct list_head periodic_sched_queued;
|
||||
struct list_head split_order;
|
||||
u16 periodic_usecs;
|
||||
u16 frame_usecs[8];
|
||||
unsigned long hs_periodic_bitmap[
|
||||
DIV_ROUND_UP(DWC2_HS_SCHEDULE_US, BITS_PER_LONG)];
|
||||
u16 frame_number;
|
||||
u16 periodic_qh_count;
|
||||
bool bus_suspended;
|
||||
bool new_connection;
|
||||
|
||||
u16 last_frame_num;
|
||||
|
||||
#ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
|
||||
#define FRAME_NUM_ARRAY_SIZE 1000
|
||||
u16 last_frame_num;
|
||||
u16 *frame_num_array;
|
||||
u16 *last_frame_num_array;
|
||||
int frame_num_idx;
|
||||
|
@ -885,34 +989,11 @@ enum dwc2_halt_status {
|
|||
*/
|
||||
extern int dwc2_core_reset(struct dwc2_hsotg *hsotg);
|
||||
extern int dwc2_core_reset_and_force_dr_mode(struct dwc2_hsotg *hsotg);
|
||||
extern void dwc2_core_host_init(struct dwc2_hsotg *hsotg);
|
||||
extern int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg);
|
||||
extern int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore);
|
||||
|
||||
void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg);
|
||||
|
||||
/*
|
||||
* Host core Functions.
|
||||
* The following functions support managing the DWC_otg controller in host
|
||||
* mode.
|
||||
*/
|
||||
extern void dwc2_hc_init(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan);
|
||||
extern void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan,
|
||||
enum dwc2_halt_status halt_status);
|
||||
extern void dwc2_hc_cleanup(struct dwc2_hsotg *hsotg,
|
||||
struct dwc2_host_chan *chan);
|
||||
extern void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
|
||||
struct dwc2_host_chan *chan);
|
||||
extern void dwc2_hc_start_transfer_ddma(struct dwc2_hsotg *hsotg,
|
||||
struct dwc2_host_chan *chan);
|
||||
extern int dwc2_hc_continue_transfer(struct dwc2_hsotg *hsotg,
|
||||
struct dwc2_host_chan *chan);
|
||||
extern void dwc2_hc_do_ping(struct dwc2_hsotg *hsotg,
|
||||
struct dwc2_host_chan *chan);
|
||||
extern void dwc2_enable_host_interrupts(struct dwc2_hsotg *hsotg);
|
||||
extern void dwc2_disable_host_interrupts(struct dwc2_hsotg *hsotg);
|
||||
|
||||
extern u32 dwc2_calc_frame_interval(struct dwc2_hsotg *hsotg);
|
||||
extern bool dwc2_is_controller_alive(struct dwc2_hsotg *hsotg);
|
||||
|
||||
/*
|
||||
|
@ -924,7 +1005,6 @@ extern void dwc2_read_packet(struct dwc2_hsotg *hsotg, u8 *dest, u16 bytes);
|
|||
extern void dwc2_flush_tx_fifo(struct dwc2_hsotg *hsotg, const int num);
|
||||
extern void dwc2_flush_rx_fifo(struct dwc2_hsotg *hsotg);
|
||||
|
||||
extern int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup);
|
||||
extern void dwc2_enable_global_interrupts(struct dwc2_hsotg *hcd);
|
||||
extern void dwc2_disable_global_interrupts(struct dwc2_hsotg *hcd);
|
||||
|
||||
|
@ -1191,6 +1271,8 @@ extern void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg);
|
|||
extern void dwc2_hsotg_disconnect(struct dwc2_hsotg *dwc2);
|
||||
extern int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode);
|
||||
#define dwc2_is_device_connected(hsotg) (hsotg->connected)
|
||||
int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg);
|
||||
int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg);
|
||||
#else
|
||||
static inline int dwc2_hsotg_remove(struct dwc2_hsotg *dwc2)
|
||||
{ return 0; }
|
||||
|
@ -1208,22 +1290,37 @@ static inline int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg,
|
|||
int testmode)
|
||||
{ return 0; }
|
||||
#define dwc2_is_device_connected(hsotg) (0)
|
||||
static inline int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
|
||||
{ return 0; }
|
||||
static inline int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg)
|
||||
{ return 0; }
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
|
||||
extern int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg);
|
||||
extern int dwc2_hcd_get_future_frame_number(struct dwc2_hsotg *hsotg, int us);
|
||||
extern void dwc2_hcd_connect(struct dwc2_hsotg *hsotg);
|
||||
extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force);
|
||||
extern void dwc2_hcd_start(struct dwc2_hsotg *hsotg);
|
||||
int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg);
|
||||
int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg);
|
||||
#else
|
||||
static inline int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg)
|
||||
{ return 0; }
|
||||
static inline int dwc2_hcd_get_future_frame_number(struct dwc2_hsotg *hsotg,
|
||||
int us)
|
||||
{ return 0; }
|
||||
static inline void dwc2_hcd_connect(struct dwc2_hsotg *hsotg) {}
|
||||
static inline void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force) {}
|
||||
static inline void dwc2_hcd_start(struct dwc2_hsotg *hsotg) {}
|
||||
static inline void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) {}
|
||||
static inline int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
|
||||
{ return 0; }
|
||||
static inline int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg)
|
||||
{ return 0; }
|
||||
static inline int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg)
|
||||
{ return 0; }
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* __DWC2_CORE_H__ */
|
||||
|
|
|
@ -3668,3 +3668,105 @@ int dwc2_hsotg_resume(struct dwc2_hsotg *hsotg)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_backup_device_registers() - Backup controller device registers.
|
||||
* When suspending usb bus, registers needs to be backuped
|
||||
* if controller power is disabled once suspended.
|
||||
*
|
||||
* @hsotg: Programming view of the DWC_otg controller
|
||||
*/
|
||||
int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dwc2_dregs_backup *dr;
|
||||
int i;
|
||||
|
||||
dev_dbg(hsotg->dev, "%s\n", __func__);
|
||||
|
||||
/* Backup dev regs */
|
||||
dr = &hsotg->dr_backup;
|
||||
|
||||
dr->dcfg = dwc2_readl(hsotg->regs + DCFG);
|
||||
dr->dctl = dwc2_readl(hsotg->regs + DCTL);
|
||||
dr->daintmsk = dwc2_readl(hsotg->regs + DAINTMSK);
|
||||
dr->diepmsk = dwc2_readl(hsotg->regs + DIEPMSK);
|
||||
dr->doepmsk = dwc2_readl(hsotg->regs + DOEPMSK);
|
||||
|
||||
for (i = 0; i < hsotg->num_of_eps; i++) {
|
||||
/* Backup IN EPs */
|
||||
dr->diepctl[i] = dwc2_readl(hsotg->regs + DIEPCTL(i));
|
||||
|
||||
/* Ensure DATA PID is correctly configured */
|
||||
if (dr->diepctl[i] & DXEPCTL_DPID)
|
||||
dr->diepctl[i] |= DXEPCTL_SETD1PID;
|
||||
else
|
||||
dr->diepctl[i] |= DXEPCTL_SETD0PID;
|
||||
|
||||
dr->dieptsiz[i] = dwc2_readl(hsotg->regs + DIEPTSIZ(i));
|
||||
dr->diepdma[i] = dwc2_readl(hsotg->regs + DIEPDMA(i));
|
||||
|
||||
/* Backup OUT EPs */
|
||||
dr->doepctl[i] = dwc2_readl(hsotg->regs + DOEPCTL(i));
|
||||
|
||||
/* Ensure DATA PID is correctly configured */
|
||||
if (dr->doepctl[i] & DXEPCTL_DPID)
|
||||
dr->doepctl[i] |= DXEPCTL_SETD1PID;
|
||||
else
|
||||
dr->doepctl[i] |= DXEPCTL_SETD0PID;
|
||||
|
||||
dr->doeptsiz[i] = dwc2_readl(hsotg->regs + DOEPTSIZ(i));
|
||||
dr->doepdma[i] = dwc2_readl(hsotg->regs + DOEPDMA(i));
|
||||
}
|
||||
dr->valid = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_restore_device_registers() - Restore controller device registers.
|
||||
* When resuming usb bus, device registers needs to be restored
|
||||
* if controller power were disabled.
|
||||
*
|
||||
* @hsotg: Programming view of the DWC_otg controller
|
||||
*/
|
||||
int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dwc2_dregs_backup *dr;
|
||||
u32 dctl;
|
||||
int i;
|
||||
|
||||
dev_dbg(hsotg->dev, "%s\n", __func__);
|
||||
|
||||
/* Restore dev regs */
|
||||
dr = &hsotg->dr_backup;
|
||||
if (!dr->valid) {
|
||||
dev_err(hsotg->dev, "%s: no device registers to restore\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
dr->valid = false;
|
||||
|
||||
dwc2_writel(dr->dcfg, hsotg->regs + DCFG);
|
||||
dwc2_writel(dr->dctl, hsotg->regs + DCTL);
|
||||
dwc2_writel(dr->daintmsk, hsotg->regs + DAINTMSK);
|
||||
dwc2_writel(dr->diepmsk, hsotg->regs + DIEPMSK);
|
||||
dwc2_writel(dr->doepmsk, hsotg->regs + DOEPMSK);
|
||||
|
||||
for (i = 0; i < hsotg->num_of_eps; i++) {
|
||||
/* Restore IN EPs */
|
||||
dwc2_writel(dr->diepctl[i], hsotg->regs + DIEPCTL(i));
|
||||
dwc2_writel(dr->dieptsiz[i], hsotg->regs + DIEPTSIZ(i));
|
||||
dwc2_writel(dr->diepdma[i], hsotg->regs + DIEPDMA(i));
|
||||
|
||||
/* Restore OUT EPs */
|
||||
dwc2_writel(dr->doepctl[i], hsotg->regs + DOEPCTL(i));
|
||||
dwc2_writel(dr->doeptsiz[i], hsotg->regs + DOEPTSIZ(i));
|
||||
dwc2_writel(dr->doepdma[i], hsotg->regs + DOEPDMA(i));
|
||||
}
|
||||
|
||||
/* Set the Power-On Programming done bit */
|
||||
dctl = dwc2_readl(hsotg->regs + DCTL);
|
||||
dctl |= DCTL_PWRONPRGDONE;
|
||||
dwc2_writel(dctl, hsotg->regs + DCTL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -75,8 +75,6 @@ struct dwc2_qh;
|
|||
* (micro)frame
|
||||
* @xfer_buf: Pointer to current transfer buffer position
|
||||
* @xfer_dma: DMA address of xfer_buf
|
||||
* @align_buf: In Buffer DMA mode this will be used if xfer_buf is not
|
||||
* DWORD aligned
|
||||
* @xfer_len: Total number of bytes to transfer
|
||||
* @xfer_count: Number of bytes transferred so far
|
||||
* @start_pkt_count: Packet count at start of transfer
|
||||
|
@ -108,6 +106,7 @@ struct dwc2_qh;
|
|||
* @hc_list_entry: For linking to list of host channels
|
||||
* @desc_list_addr: Current QH's descriptor list DMA address
|
||||
* @desc_list_sz: Current QH's descriptor list size
|
||||
* @split_order_list_entry: List entry for keeping track of the order of splits
|
||||
*
|
||||
* This structure represents the state of a single host channel when acting in
|
||||
* host mode. It contains the data items needed to transfer packets to an
|
||||
|
@ -133,7 +132,6 @@ struct dwc2_host_chan {
|
|||
|
||||
u8 *xfer_buf;
|
||||
dma_addr_t xfer_dma;
|
||||
dma_addr_t align_buf;
|
||||
u32 xfer_len;
|
||||
u32 xfer_count;
|
||||
u16 start_pkt_count;
|
||||
|
@ -161,6 +159,7 @@ struct dwc2_host_chan {
|
|||
struct list_head hc_list_entry;
|
||||
dma_addr_t desc_list_addr;
|
||||
u32 desc_list_sz;
|
||||
struct list_head split_order_list_entry;
|
||||
};
|
||||
|
||||
struct dwc2_hcd_pipe_info {
|
||||
|
@ -213,9 +212,47 @@ enum dwc2_transaction_type {
|
|||
DWC2_TRANSACTION_ALL,
|
||||
};
|
||||
|
||||
/* The number of elements per LS bitmap (per port on multi_tt) */
|
||||
#define DWC2_ELEMENTS_PER_LS_BITMAP DIV_ROUND_UP(DWC2_LS_SCHEDULE_SLICES, \
|
||||
BITS_PER_LONG)
|
||||
|
||||
/**
|
||||
* struct dwc2_tt - dwc2 data associated with a usb_tt
|
||||
*
|
||||
* @refcount: Number of Queue Heads (QHs) holding a reference.
|
||||
* @usb_tt: Pointer back to the official usb_tt.
|
||||
* @periodic_bitmaps: Bitmap for which parts of the 1ms frame are accounted
|
||||
* for already. Each is DWC2_ELEMENTS_PER_LS_BITMAP
|
||||
* elements (so sizeof(long) times that in bytes).
|
||||
*
|
||||
* This structure is stored in the hcpriv of the official usb_tt.
|
||||
*/
|
||||
struct dwc2_tt {
|
||||
int refcount;
|
||||
struct usb_tt *usb_tt;
|
||||
unsigned long periodic_bitmaps[];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dwc2_hs_transfer_time - Info about a transfer on the high speed bus.
|
||||
*
|
||||
* @start_schedule_usecs: The start time on the main bus schedule. Note that
|
||||
* the main bus schedule is tightly packed and this
|
||||
* time should be interpreted as tightly packed (so
|
||||
* uFrame 0 starts at 0 us, uFrame 1 starts at 100 us
|
||||
* instead of 125 us).
|
||||
* @duration_us: How long this transfer goes.
|
||||
*/
|
||||
|
||||
struct dwc2_hs_transfer_time {
|
||||
u32 start_schedule_us;
|
||||
u16 duration_us;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dwc2_qh - Software queue head structure
|
||||
*
|
||||
* @hsotg: The HCD state structure for the DWC OTG controller
|
||||
* @ep_type: Endpoint type. One of the following values:
|
||||
* - USB_ENDPOINT_XFER_CONTROL
|
||||
* - USB_ENDPOINT_XFER_BULK
|
||||
|
@ -236,17 +273,35 @@ enum dwc2_transaction_type {
|
|||
* @do_split: Full/low speed endpoint on high-speed hub requires split
|
||||
* @td_first: Index of first activated isochronous transfer descriptor
|
||||
* @td_last: Index of last activated isochronous transfer descriptor
|
||||
* @usecs: Bandwidth in microseconds per (micro)frame
|
||||
* @interval: Interval between transfers in (micro)frames
|
||||
* @sched_frame: (Micro)frame to initialize a periodic transfer.
|
||||
* The transfer executes in the following (micro)frame.
|
||||
* @frame_usecs: Internal variable used by the microframe scheduler
|
||||
* @start_split_frame: (Micro)frame at which last start split was initialized
|
||||
* @host_us: Bandwidth in microseconds per transfer as seen by host
|
||||
* @device_us: Bandwidth in microseconds per transfer as seen by device
|
||||
* @host_interval: Interval between transfers as seen by the host. If
|
||||
* the host is high speed and the device is low speed this
|
||||
* will be 8 times device interval.
|
||||
* @device_interval: Interval between transfers as seen by the device.
|
||||
* interval.
|
||||
* @next_active_frame: (Micro)frame _before_ we next need to put something on
|
||||
* the bus. We'll move the qh to active here. If the
|
||||
* host is in high speed mode this will be a uframe. If
|
||||
* the host is in low speed mode this will be a full frame.
|
||||
* @start_active_frame: If we are partway through a split transfer, this will be
|
||||
* what next_active_frame was when we started. Otherwise
|
||||
* it should always be the same as next_active_frame.
|
||||
* @num_hs_transfers: Number of transfers in hs_transfers.
|
||||
* Normally this is 1 but can be more than one for splits.
|
||||
* Always >= 1 unless the host is in low/full speed mode.
|
||||
* @hs_transfers: Transfers that are scheduled as seen by the high speed
|
||||
* bus. Not used if host is in low or full speed mode (but
|
||||
* note that it IS USED if the device is low or full speed
|
||||
* as long as the HOST is in high speed mode).
|
||||
* @ls_start_schedule_slice: Start time (in slices) on the low speed bus
|
||||
* schedule that's being used by this device. This
|
||||
* will be on the periodic_bitmap in a
|
||||
* "struct dwc2_tt". Not used if this device is high
|
||||
* speed. Note that this is in "schedule slice" which
|
||||
* is tightly packed.
|
||||
* @ls_duration_us: Duration on the low speed bus schedule.
|
||||
* @ntd: Actual number of transfer descriptors in a list
|
||||
* @dw_align_buf: Used instead of original buffer if its physical address
|
||||
* is not dword-aligned
|
||||
* @dw_align_buf_size: Size of dw_align_buf
|
||||
* @dw_align_buf_dma: DMA address for dw_align_buf
|
||||
* @qtd_list: List of QTDs for this QH
|
||||
* @channel: Host channel currently processing transfers for this QH
|
||||
* @qh_list_entry: Entry for QH in either the periodic or non-periodic
|
||||
|
@ -257,13 +312,20 @@ enum dwc2_transaction_type {
|
|||
* @n_bytes: Xfer Bytes array. Each element corresponds to a transfer
|
||||
* descriptor and indicates original XferSize value for the
|
||||
* descriptor
|
||||
* @unreserve_timer: Timer for releasing periodic reservation.
|
||||
* @dwc2_tt: Pointer to our tt info (or NULL if no tt).
|
||||
* @ttport: Port number within our tt.
|
||||
* @tt_buffer_dirty True if clear_tt_buffer_complete is pending
|
||||
* @unreserve_pending: True if we planned to unreserve but haven't yet.
|
||||
* @schedule_low_speed: True if we have a low/full speed component (either the
|
||||
* host is in low/full speed mode or do_split).
|
||||
*
|
||||
* A Queue Head (QH) holds the static characteristics of an endpoint and
|
||||
* maintains a list of transfers (QTDs) for that endpoint. A QH structure may
|
||||
* be entered in either the non-periodic or periodic schedule.
|
||||
*/
|
||||
struct dwc2_qh {
|
||||
struct dwc2_hsotg *hsotg;
|
||||
u8 ep_type;
|
||||
u8 ep_is_in;
|
||||
u16 maxp;
|
||||
|
@ -273,15 +335,16 @@ struct dwc2_qh {
|
|||
u8 do_split;
|
||||
u8 td_first;
|
||||
u8 td_last;
|
||||
u16 usecs;
|
||||
u16 interval;
|
||||
u16 sched_frame;
|
||||
u16 frame_usecs[8];
|
||||
u16 start_split_frame;
|
||||
u16 host_us;
|
||||
u16 device_us;
|
||||
u16 host_interval;
|
||||
u16 device_interval;
|
||||
u16 next_active_frame;
|
||||
u16 start_active_frame;
|
||||
s16 num_hs_transfers;
|
||||
struct dwc2_hs_transfer_time hs_transfers[DWC2_HS_SCHEDULE_UFRAMES];
|
||||
u32 ls_start_schedule_slice;
|
||||
u16 ntd;
|
||||
u8 *dw_align_buf;
|
||||
int dw_align_buf_size;
|
||||
dma_addr_t dw_align_buf_dma;
|
||||
struct list_head qtd_list;
|
||||
struct dwc2_host_chan *channel;
|
||||
struct list_head qh_list_entry;
|
||||
|
@ -289,7 +352,12 @@ struct dwc2_qh {
|
|||
dma_addr_t desc_list_dma;
|
||||
u32 desc_list_sz;
|
||||
u32 *n_bytes;
|
||||
struct timer_list unreserve_timer;
|
||||
struct dwc2_tt *dwc_tt;
|
||||
int ttport;
|
||||
unsigned tt_buffer_dirty:1;
|
||||
unsigned unreserve_pending:1;
|
||||
unsigned schedule_low_speed:1;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -362,6 +430,8 @@ struct hc_xfer_info {
|
|||
};
|
||||
#endif
|
||||
|
||||
u32 dwc2_calc_frame_interval(struct dwc2_hsotg *hsotg);
|
||||
|
||||
/* Gets the struct usb_hcd that contains a struct dwc2_hsotg */
|
||||
static inline struct usb_hcd *dwc2_hsotg_to_hcd(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
|
@ -383,6 +453,12 @@ static inline void disable_hc_int(struct dwc2_hsotg *hsotg, int chnum, u32 intr)
|
|||
dwc2_writel(mask, hsotg->regs + HCINTMSK(chnum));
|
||||
}
|
||||
|
||||
void dwc2_hc_cleanup(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan);
|
||||
void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan,
|
||||
enum dwc2_halt_status halt_status);
|
||||
void dwc2_hc_start_transfer_ddma(struct dwc2_hsotg *hsotg,
|
||||
struct dwc2_host_chan *chan);
|
||||
|
||||
/*
|
||||
* Reads HPRT0 in preparation to modify. It keeps the WC bits 0 so that if they
|
||||
* are read as 1, they won't clear when written back.
|
||||
|
@ -456,7 +532,6 @@ extern void dwc2_hcd_queue_transactions(struct dwc2_hsotg *hsotg,
|
|||
|
||||
/* Schedule Queue Functions */
|
||||
/* Implemented in hcd_queue.c */
|
||||
extern void dwc2_hcd_init_usecs(struct dwc2_hsotg *hsotg);
|
||||
extern struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
|
||||
struct dwc2_hcd_urb *urb,
|
||||
gfp_t mem_flags);
|
||||
|
@ -571,6 +646,11 @@ static inline u16 dwc2_frame_num_inc(u16 frame, u16 inc)
|
|||
return (frame + inc) & HFNUM_MAX_FRNUM;
|
||||
}
|
||||
|
||||
static inline u16 dwc2_frame_num_dec(u16 frame, u16 dec)
|
||||
{
|
||||
return (frame + HFNUM_MAX_FRNUM + 1 - dec) & HFNUM_MAX_FRNUM;
|
||||
}
|
||||
|
||||
static inline u16 dwc2_full_frame_num(u16 frame)
|
||||
{
|
||||
return (frame & HFNUM_MAX_FRNUM) >> 3;
|
||||
|
@ -648,7 +728,7 @@ static inline u16 dwc2_hcd_get_ep_bandwidth(struct dwc2_hsotg *hsotg,
|
|||
return 0;
|
||||
}
|
||||
|
||||
return qh->usecs;
|
||||
return qh->host_us;
|
||||
}
|
||||
|
||||
extern void dwc2_hcd_save_data_toggle(struct dwc2_hsotg *hsotg,
|
||||
|
@ -717,6 +797,12 @@ extern void dwc2_host_start(struct dwc2_hsotg *hsotg);
|
|||
extern void dwc2_host_disconnect(struct dwc2_hsotg *hsotg);
|
||||
extern void dwc2_host_hub_info(struct dwc2_hsotg *hsotg, void *context,
|
||||
int *hub_addr, int *hub_port);
|
||||
extern struct dwc2_tt *dwc2_host_get_tt_info(struct dwc2_hsotg *hsotg,
|
||||
void *context, gfp_t mem_flags,
|
||||
int *ttport);
|
||||
|
||||
extern void dwc2_host_put_tt_info(struct dwc2_hsotg *hsotg,
|
||||
struct dwc2_tt *dwc_tt);
|
||||
extern int dwc2_host_get_speed(struct dwc2_hsotg *hsotg, void *context);
|
||||
extern void dwc2_host_complete(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
|
||||
int status);
|
||||
|
@ -739,7 +825,7 @@ do { \
|
|||
_qtd_ = list_entry((_qh_)->qtd_list.next, struct dwc2_qtd, \
|
||||
qtd_list_entry); \
|
||||
if (usb_pipeint(_qtd_->urb->pipe) && \
|
||||
(_qh_)->start_split_frame != 0 && !_qtd_->complete_split) { \
|
||||
(_qh_)->start_active_frame != 0 && !_qtd_->complete_split) { \
|
||||
_hfnum_.d32 = dwc2_readl((_hcd_)->regs + HFNUM); \
|
||||
switch (_hfnum_.b.frnum & 0x7) { \
|
||||
case 7: \
|
||||
|
|
|
@ -81,7 +81,7 @@ static u16 dwc2_max_desc_num(struct dwc2_qh *qh)
|
|||
static u16 dwc2_frame_incr_val(struct dwc2_qh *qh)
|
||||
{
|
||||
return qh->dev_speed == USB_SPEED_HIGH ?
|
||||
(qh->interval + 8 - 1) / 8 : qh->interval;
|
||||
(qh->host_interval + 8 - 1) / 8 : qh->host_interval;
|
||||
}
|
||||
|
||||
static int dwc2_desc_list_alloc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
|
||||
|
@ -111,7 +111,7 @@ static int dwc2_desc_list_alloc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
|
|||
dma_unmap_single(hsotg->dev, qh->desc_list_dma,
|
||||
qh->desc_list_sz,
|
||||
DMA_FROM_DEVICE);
|
||||
kfree(qh->desc_list);
|
||||
kmem_cache_free(desc_cache, qh->desc_list);
|
||||
qh->desc_list = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
@ -252,7 +252,7 @@ static void dwc2_update_frame_list(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
|
|||
chan = qh->channel;
|
||||
inc = dwc2_frame_incr_val(qh);
|
||||
if (qh->ep_type == USB_ENDPOINT_XFER_ISOC)
|
||||
i = dwc2_frame_list_idx(qh->sched_frame);
|
||||
i = dwc2_frame_list_idx(qh->next_active_frame);
|
||||
else
|
||||
i = 0;
|
||||
|
||||
|
@ -278,13 +278,13 @@ static void dwc2_update_frame_list(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
|
|||
return;
|
||||
|
||||
chan->schinfo = 0;
|
||||
if (chan->speed == USB_SPEED_HIGH && qh->interval) {
|
||||
if (chan->speed == USB_SPEED_HIGH && qh->host_interval) {
|
||||
j = 1;
|
||||
/* TODO - check this */
|
||||
inc = (8 + qh->interval - 1) / qh->interval;
|
||||
inc = (8 + qh->host_interval - 1) / qh->host_interval;
|
||||
for (i = 0; i < inc; i++) {
|
||||
chan->schinfo |= j;
|
||||
j = j << qh->interval;
|
||||
j = j << qh->host_interval;
|
||||
}
|
||||
} else {
|
||||
chan->schinfo = 0xff;
|
||||
|
@ -431,7 +431,10 @@ static u16 dwc2_calc_starting_frame(struct dwc2_hsotg *hsotg,
|
|||
|
||||
hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg);
|
||||
|
||||
/* sched_frame is always frame number (not uFrame) both in FS and HS! */
|
||||
/*
|
||||
* next_active_frame is always frame number (not uFrame) both in FS
|
||||
* and HS!
|
||||
*/
|
||||
|
||||
/*
|
||||
* skip_frames is used to limit activated descriptors number
|
||||
|
@ -514,13 +517,13 @@ static u16 dwc2_recalc_initial_desc_idx(struct dwc2_hsotg *hsotg,
|
|||
*/
|
||||
fr_idx_tmp = dwc2_frame_list_idx(frame);
|
||||
fr_idx = (FRLISTEN_64_SIZE +
|
||||
dwc2_frame_list_idx(qh->sched_frame) - fr_idx_tmp)
|
||||
% dwc2_frame_incr_val(qh);
|
||||
dwc2_frame_list_idx(qh->next_active_frame) -
|
||||
fr_idx_tmp) % dwc2_frame_incr_val(qh);
|
||||
fr_idx = (fr_idx + fr_idx_tmp) % FRLISTEN_64_SIZE;
|
||||
} else {
|
||||
qh->sched_frame = dwc2_calc_starting_frame(hsotg, qh,
|
||||
qh->next_active_frame = dwc2_calc_starting_frame(hsotg, qh,
|
||||
&skip_frames);
|
||||
fr_idx = dwc2_frame_list_idx(qh->sched_frame);
|
||||
fr_idx = dwc2_frame_list_idx(qh->next_active_frame);
|
||||
}
|
||||
|
||||
qh->td_first = qh->td_last = dwc2_frame_to_desc_idx(qh, fr_idx);
|
||||
|
@ -583,7 +586,7 @@ static void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
|
|||
u16 next_idx;
|
||||
|
||||
idx = qh->td_last;
|
||||
inc = qh->interval;
|
||||
inc = qh->host_interval;
|
||||
hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg);
|
||||
cur_idx = dwc2_frame_list_idx(hsotg->frame_number);
|
||||
next_idx = dwc2_desclist_idx_inc(qh->td_last, inc, qh->dev_speed);
|
||||
|
@ -605,11 +608,11 @@ static void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
|
|||
}
|
||||
}
|
||||
|
||||
if (qh->interval) {
|
||||
ntd_max = (dwc2_max_desc_num(qh) + qh->interval - 1) /
|
||||
qh->interval;
|
||||
if (qh->host_interval) {
|
||||
ntd_max = (dwc2_max_desc_num(qh) + qh->host_interval - 1) /
|
||||
qh->host_interval;
|
||||
if (skip_frames && !qh->channel)
|
||||
ntd_max -= skip_frames / qh->interval;
|
||||
ntd_max -= skip_frames / qh->host_interval;
|
||||
}
|
||||
|
||||
max_xfer_size = qh->dev_speed == USB_SPEED_HIGH ?
|
||||
|
@ -1029,7 +1032,7 @@ static void dwc2_complete_isoc_xfer_ddma(struct dwc2_hsotg *hsotg,
|
|||
idx);
|
||||
if (rc < 0)
|
||||
return;
|
||||
idx = dwc2_desclist_idx_inc(idx, qh->interval,
|
||||
idx = dwc2_desclist_idx_inc(idx, qh->host_interval,
|
||||
chan->speed);
|
||||
if (!rc)
|
||||
continue;
|
||||
|
@ -1039,7 +1042,7 @@ static void dwc2_complete_isoc_xfer_ddma(struct dwc2_hsotg *hsotg,
|
|||
|
||||
/* rc == DWC2_CMPL_STOP */
|
||||
|
||||
if (qh->interval >= 32)
|
||||
if (qh->host_interval >= 32)
|
||||
goto stop_scan;
|
||||
|
||||
qh->td_first = idx;
|
||||
|
@ -1242,8 +1245,10 @@ static void dwc2_complete_non_isoc_xfer_ddma(struct dwc2_hsotg *hsotg,
|
|||
for (i = 0; i < qtd_desc_count; i++) {
|
||||
if (dwc2_process_non_isoc_desc(hsotg, chan, chnum, qtd,
|
||||
desc_num, halt_status,
|
||||
&xfer_done))
|
||||
&xfer_done)) {
|
||||
qtd = NULL;
|
||||
goto stop_scan;
|
||||
}
|
||||
|
||||
desc_num++;
|
||||
}
|
||||
|
@ -1258,7 +1263,7 @@ stop_scan:
|
|||
if (halt_status == DWC2_HC_XFER_STALL)
|
||||
qh->data_toggle = DWC2_HC_PID_DATA0;
|
||||
else
|
||||
dwc2_hcd_save_data_toggle(hsotg, chan, chnum, qtd);
|
||||
dwc2_hcd_save_data_toggle(hsotg, chan, chnum, NULL);
|
||||
}
|
||||
|
||||
if (halt_status == DWC2_HC_XFER_COMPLETE) {
|
||||
|
@ -1326,8 +1331,8 @@ void dwc2_hcd_complete_xfer_ddma(struct dwc2_hsotg *hsotg,
|
|||
dwc2_hcd_qh_unlink(hsotg, qh);
|
||||
} else {
|
||||
/* Keep in assigned schedule to continue transfer */
|
||||
list_move(&qh->qh_list_entry,
|
||||
&hsotg->periodic_sched_assigned);
|
||||
list_move_tail(&qh->qh_list_entry,
|
||||
&hsotg->periodic_sched_assigned);
|
||||
/*
|
||||
* If channel has been halted during giveback of urb
|
||||
* then prevent any new scheduling.
|
||||
|
|
|
@ -55,12 +55,16 @@
|
|||
/* This function is for debug only */
|
||||
static void dwc2_track_missed_sofs(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
#ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
|
||||
u16 curr_frame_number = hsotg->frame_number;
|
||||
u16 expected = dwc2_frame_num_inc(hsotg->last_frame_num, 1);
|
||||
|
||||
if (expected != curr_frame_number)
|
||||
dwc2_sch_vdbg(hsotg, "MISSED SOF %04x != %04x\n",
|
||||
expected, curr_frame_number);
|
||||
|
||||
#ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
|
||||
if (hsotg->frame_num_idx < FRAME_NUM_ARRAY_SIZE) {
|
||||
if (((hsotg->last_frame_num + 1) & HFNUM_MAX_FRNUM) !=
|
||||
curr_frame_number) {
|
||||
if (expected != curr_frame_number) {
|
||||
hsotg->frame_num_array[hsotg->frame_num_idx] =
|
||||
curr_frame_number;
|
||||
hsotg->last_frame_num_array[hsotg->frame_num_idx] =
|
||||
|
@ -79,14 +83,15 @@ static void dwc2_track_missed_sofs(struct dwc2_hsotg *hsotg)
|
|||
}
|
||||
hsotg->dumped_frame_num_array = 1;
|
||||
}
|
||||
hsotg->last_frame_num = curr_frame_number;
|
||||
#endif
|
||||
hsotg->last_frame_num = curr_frame_number;
|
||||
}
|
||||
|
||||
static void dwc2_hc_handle_tt_clear(struct dwc2_hsotg *hsotg,
|
||||
struct dwc2_host_chan *chan,
|
||||
struct dwc2_qtd *qtd)
|
||||
{
|
||||
struct usb_device *root_hub = dwc2_hsotg_to_hcd(hsotg)->self.root_hub;
|
||||
struct urb *usb_urb;
|
||||
|
||||
if (!chan->qh)
|
||||
|
@ -102,6 +107,15 @@ static void dwc2_hc_handle_tt_clear(struct dwc2_hsotg *hsotg,
|
|||
if (!usb_urb || !usb_urb->dev || !usb_urb->dev->tt)
|
||||
return;
|
||||
|
||||
/*
|
||||
* The root hub doesn't really have a TT, but Linux thinks it
|
||||
* does because how could you have a "high speed hub" that
|
||||
* directly talks directly to low speed devices without a TT?
|
||||
* It's all lies. Lies, I tell you.
|
||||
*/
|
||||
if (usb_urb->dev->tt->hub == root_hub)
|
||||
return;
|
||||
|
||||
if (qtd->urb->status != -EPIPE && qtd->urb->status != -EREMOTEIO) {
|
||||
chan->qh->tt_buffer_dirty = 1;
|
||||
if (usb_hub_clear_tt_buffer(usb_urb))
|
||||
|
@ -138,13 +152,19 @@ static void dwc2_sof_intr(struct dwc2_hsotg *hsotg)
|
|||
while (qh_entry != &hsotg->periodic_sched_inactive) {
|
||||
qh = list_entry(qh_entry, struct dwc2_qh, qh_list_entry);
|
||||
qh_entry = qh_entry->next;
|
||||
if (dwc2_frame_num_le(qh->sched_frame, hsotg->frame_number))
|
||||
if (dwc2_frame_num_le(qh->next_active_frame,
|
||||
hsotg->frame_number)) {
|
||||
dwc2_sch_vdbg(hsotg, "QH=%p ready fn=%04x, nxt=%04x\n",
|
||||
qh, hsotg->frame_number,
|
||||
qh->next_active_frame);
|
||||
|
||||
/*
|
||||
* Move QH to the ready list to be executed next
|
||||
* (micro)frame
|
||||
*/
|
||||
list_move(&qh->qh_list_entry,
|
||||
list_move_tail(&qh->qh_list_entry,
|
||||
&hsotg->periodic_sched_ready);
|
||||
}
|
||||
}
|
||||
tr_type = dwc2_hcd_select_transactions(hsotg);
|
||||
if (tr_type != DWC2_TRANSACTION_NONE)
|
||||
|
@ -472,18 +492,6 @@ static int dwc2_update_urb_state(struct dwc2_hsotg *hsotg,
|
|||
xfer_length = urb->length - urb->actual_length;
|
||||
}
|
||||
|
||||
/* Non DWORD-aligned buffer case handling */
|
||||
if (chan->align_buf && xfer_length) {
|
||||
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
|
||||
dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
|
||||
chan->qh->dw_align_buf_size,
|
||||
chan->ep_is_in ?
|
||||
DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
||||
if (chan->ep_is_in)
|
||||
memcpy(urb->buf + urb->actual_length,
|
||||
chan->qh->dw_align_buf, xfer_length);
|
||||
}
|
||||
|
||||
dev_vdbg(hsotg->dev, "urb->actual_length=%d xfer_length=%d\n",
|
||||
urb->actual_length, xfer_length);
|
||||
urb->actual_length += xfer_length;
|
||||
|
@ -573,21 +581,6 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state(
|
|||
frame_desc->status = 0;
|
||||
frame_desc->actual_length = dwc2_get_actual_xfer_length(hsotg,
|
||||
chan, chnum, qtd, halt_status, NULL);
|
||||
|
||||
/* Non DWORD-aligned buffer case handling */
|
||||
if (chan->align_buf && frame_desc->actual_length) {
|
||||
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n",
|
||||
__func__);
|
||||
dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
|
||||
chan->qh->dw_align_buf_size,
|
||||
chan->ep_is_in ?
|
||||
DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
||||
if (chan->ep_is_in)
|
||||
memcpy(urb->buf + frame_desc->offset +
|
||||
qtd->isoc_split_offset,
|
||||
chan->qh->dw_align_buf,
|
||||
frame_desc->actual_length);
|
||||
}
|
||||
break;
|
||||
case DWC2_HC_XFER_FRAME_OVERRUN:
|
||||
urb->error_count++;
|
||||
|
@ -608,21 +601,6 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state(
|
|||
frame_desc->actual_length = dwc2_get_actual_xfer_length(hsotg,
|
||||
chan, chnum, qtd, halt_status, NULL);
|
||||
|
||||
/* Non DWORD-aligned buffer case handling */
|
||||
if (chan->align_buf && frame_desc->actual_length) {
|
||||
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n",
|
||||
__func__);
|
||||
dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
|
||||
chan->qh->dw_align_buf_size,
|
||||
chan->ep_is_in ?
|
||||
DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
||||
if (chan->ep_is_in)
|
||||
memcpy(urb->buf + frame_desc->offset +
|
||||
qtd->isoc_split_offset,
|
||||
chan->qh->dw_align_buf,
|
||||
frame_desc->actual_length);
|
||||
}
|
||||
|
||||
/* Skip whole frame */
|
||||
if (chan->qh->do_split &&
|
||||
chan->ep_type == USB_ENDPOINT_XFER_ISOC && chan->ep_is_in &&
|
||||
|
@ -688,8 +666,6 @@ static void dwc2_deactivate_qh(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
|
|||
}
|
||||
|
||||
no_qtd:
|
||||
if (qh->channel)
|
||||
qh->channel->align_buf = 0;
|
||||
qh->channel = NULL;
|
||||
dwc2_hcd_qh_deactivate(hsotg, qh, continue_split);
|
||||
}
|
||||
|
@ -846,7 +822,7 @@ static void dwc2_halt_channel(struct dwc2_hsotg *hsotg,
|
|||
* halt to be queued when the periodic schedule is
|
||||
* processed.
|
||||
*/
|
||||
list_move(&chan->qh->qh_list_entry,
|
||||
list_move_tail(&chan->qh->qh_list_entry,
|
||||
&hsotg->periodic_sched_assigned);
|
||||
|
||||
/*
|
||||
|
@ -954,14 +930,6 @@ static int dwc2_xfercomp_isoc_split_in(struct dwc2_hsotg *hsotg,
|
|||
|
||||
frame_desc->actual_length += len;
|
||||
|
||||
if (chan->align_buf) {
|
||||
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
|
||||
dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
|
||||
chan->qh->dw_align_buf_size, DMA_FROM_DEVICE);
|
||||
memcpy(qtd->urb->buf + frame_desc->offset +
|
||||
qtd->isoc_split_offset, chan->qh->dw_align_buf, len);
|
||||
}
|
||||
|
||||
qtd->isoc_split_offset += len;
|
||||
|
||||
if (frame_desc->actual_length >= frame_desc->length) {
|
||||
|
@ -1184,19 +1152,6 @@ static void dwc2_update_urb_state_abn(struct dwc2_hsotg *hsotg,
|
|||
xfer_length = urb->length - urb->actual_length;
|
||||
}
|
||||
|
||||
/* Non DWORD-aligned buffer case handling */
|
||||
if (chan->align_buf && xfer_length && chan->ep_is_in) {
|
||||
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
|
||||
dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
|
||||
chan->qh->dw_align_buf_size,
|
||||
chan->ep_is_in ?
|
||||
DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
||||
if (chan->ep_is_in)
|
||||
memcpy(urb->buf + urb->actual_length,
|
||||
chan->qh->dw_align_buf,
|
||||
xfer_length);
|
||||
}
|
||||
|
||||
urb->actual_length += xfer_length;
|
||||
|
||||
hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum));
|
||||
|
@ -1416,14 +1371,50 @@ static void dwc2_hc_nyet_intr(struct dwc2_hsotg *hsotg,
|
|||
|
||||
if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
|
||||
chan->ep_type == USB_ENDPOINT_XFER_ISOC) {
|
||||
int frnum = dwc2_hcd_get_frame_number(hsotg);
|
||||
struct dwc2_qh *qh = chan->qh;
|
||||
bool past_end;
|
||||
|
||||
if (hsotg->core_params->uframe_sched <= 0) {
|
||||
int frnum = dwc2_hcd_get_frame_number(hsotg);
|
||||
|
||||
/* Don't have num_hs_transfers; simple logic */
|
||||
past_end = dwc2_full_frame_num(frnum) !=
|
||||
dwc2_full_frame_num(qh->next_active_frame);
|
||||
} else {
|
||||
int end_frnum;
|
||||
|
||||
if (dwc2_full_frame_num(frnum) !=
|
||||
dwc2_full_frame_num(chan->qh->sched_frame)) {
|
||||
/*
|
||||
* No longer in the same full speed frame.
|
||||
* Treat this as a transaction error.
|
||||
*/
|
||||
* Figure out the end frame based on schedule.
|
||||
*
|
||||
* We don't want to go on trying again and again
|
||||
* forever. Let's stop when we've done all the
|
||||
* transfers that were scheduled.
|
||||
*
|
||||
* We're going to be comparing start_active_frame
|
||||
* and next_active_frame, both of which are 1
|
||||
* before the time the packet goes on the wire,
|
||||
* so that cancels out. Basically if had 1
|
||||
* transfer and we saw 1 NYET then we're done.
|
||||
* We're getting a NYET here so if next >=
|
||||
* (start + num_transfers) we're done. The
|
||||
* complexity is that for all but ISOC_OUT we
|
||||
* skip one slot.
|
||||
*/
|
||||
end_frnum = dwc2_frame_num_inc(
|
||||
qh->start_active_frame,
|
||||
qh->num_hs_transfers);
|
||||
|
||||
if (qh->ep_type != USB_ENDPOINT_XFER_ISOC ||
|
||||
qh->ep_is_in)
|
||||
end_frnum =
|
||||
dwc2_frame_num_inc(end_frnum, 1);
|
||||
|
||||
past_end = dwc2_frame_num_le(
|
||||
end_frnum, qh->next_active_frame);
|
||||
}
|
||||
|
||||
if (past_end) {
|
||||
/* Treat this as a transaction error. */
|
||||
#if 0
|
||||
/*
|
||||
* Todo: Fix system performance so this can
|
||||
|
@ -2008,6 +1999,16 @@ static void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum)
|
|||
}
|
||||
|
||||
dwc2_writel(hcint, hsotg->regs + HCINT(chnum));
|
||||
|
||||
/*
|
||||
* If we got an interrupt after someone called
|
||||
* dwc2_hcd_endpoint_disable() we don't want to crash below
|
||||
*/
|
||||
if (!chan->qh) {
|
||||
dev_warn(hsotg->dev, "Interrupt on disabled channel\n");
|
||||
return;
|
||||
}
|
||||
|
||||
chan->hcint = hcint;
|
||||
hcint &= hcintmsk;
|
||||
|
||||
|
@ -2130,6 +2131,7 @@ static void dwc2_hc_intr(struct dwc2_hsotg *hsotg)
|
|||
{
|
||||
u32 haint;
|
||||
int i;
|
||||
struct dwc2_host_chan *chan, *chan_tmp;
|
||||
|
||||
haint = dwc2_readl(hsotg->regs + HAINT);
|
||||
if (dbg_perio()) {
|
||||
|
@ -2138,6 +2140,22 @@ static void dwc2_hc_intr(struct dwc2_hsotg *hsotg)
|
|||
dev_vdbg(hsotg->dev, "HAINT=%08x\n", haint);
|
||||
}
|
||||
|
||||
/*
|
||||
* According to USB 2.0 spec section 11.18.8, a host must
|
||||
* issue complete-split transactions in a microframe for a
|
||||
* set of full-/low-speed endpoints in the same relative
|
||||
* order as the start-splits were issued in a microframe for.
|
||||
*/
|
||||
list_for_each_entry_safe(chan, chan_tmp, &hsotg->split_order,
|
||||
split_order_list_entry) {
|
||||
int hc_num = chan->hc_num;
|
||||
|
||||
if (haint & (1 << hc_num)) {
|
||||
dwc2_hc_n_intr(hsotg, hc_num);
|
||||
haint &= ~(1 << hc_num);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < hsotg->core_params->host_channels; i++) {
|
||||
if (haint & (1 << i))
|
||||
dwc2_hc_n_intr(hsotg, i);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -126,10 +126,10 @@ static const struct dwc2_core_params params_rk3066 = {
|
|||
.speed = -1,
|
||||
.enable_dynamic_fifo = 1,
|
||||
.en_multiple_tx_fifo = -1,
|
||||
.host_rx_fifo_size = 520, /* 520 DWORDs */
|
||||
.host_rx_fifo_size = 525, /* 525 DWORDs */
|
||||
.host_nperio_tx_fifo_size = 128, /* 128 DWORDs */
|
||||
.host_perio_tx_fifo_size = 256, /* 256 DWORDs */
|
||||
.max_transfer_size = 65535,
|
||||
.max_transfer_size = -1,
|
||||
.max_packet_count = -1,
|
||||
.host_channels = -1,
|
||||
.phy_type = -1,
|
||||
|
@ -149,6 +149,38 @@ static const struct dwc2_core_params params_rk3066 = {
|
|||
.hibernation = -1,
|
||||
};
|
||||
|
||||
static const struct dwc2_core_params params_ltq = {
|
||||
.otg_cap = 2, /* non-HNP/non-SRP */
|
||||
.otg_ver = -1,
|
||||
.dma_enable = -1,
|
||||
.dma_desc_enable = -1,
|
||||
.dma_desc_fs_enable = -1,
|
||||
.speed = -1,
|
||||
.enable_dynamic_fifo = -1,
|
||||
.en_multiple_tx_fifo = -1,
|
||||
.host_rx_fifo_size = 288, /* 288 DWORDs */
|
||||
.host_nperio_tx_fifo_size = 128, /* 128 DWORDs */
|
||||
.host_perio_tx_fifo_size = 96, /* 96 DWORDs */
|
||||
.max_transfer_size = 65535,
|
||||
.max_packet_count = 511,
|
||||
.host_channels = -1,
|
||||
.phy_type = -1,
|
||||
.phy_utmi_width = -1,
|
||||
.phy_ulpi_ddr = -1,
|
||||
.phy_ulpi_ext_vbus = -1,
|
||||
.i2c_enable = -1,
|
||||
.ulpi_fs_ls = -1,
|
||||
.host_support_fs_ls_low_power = -1,
|
||||
.host_ls_low_power_phy_clk = -1,
|
||||
.ts_dline = -1,
|
||||
.reload_ctl = -1,
|
||||
.ahbcfg = GAHBCFG_HBSTLEN_INCR16 <<
|
||||
GAHBCFG_HBSTLEN_SHIFT,
|
||||
.uframe_sched = -1,
|
||||
.external_id_pin_ctl = -1,
|
||||
.hibernation = -1,
|
||||
};
|
||||
|
||||
/*
|
||||
* Check the dr_mode against the module configuration and hardware
|
||||
* capabilities.
|
||||
|
@ -428,6 +460,8 @@ static const struct of_device_id dwc2_of_match_table[] = {
|
|||
{ .compatible = "brcm,bcm2835-usb", .data = ¶ms_bcm2835 },
|
||||
{ .compatible = "hisilicon,hi6220-usb", .data = ¶ms_hi6220 },
|
||||
{ .compatible = "rockchip,rk3066-usb", .data = ¶ms_rk3066 },
|
||||
{ .compatible = "lantiq,arx100-usb", .data = ¶ms_ltq },
|
||||
{ .compatible = "lantiq,xrx200-usb", .data = ¶ms_ltq },
|
||||
{ .compatible = "snps,dwc2", .data = NULL },
|
||||
{ .compatible = "samsung,s3c6400-hsotg", .data = NULL},
|
||||
{},
|
||||
|
|
|
@ -962,10 +962,6 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
fladj = pdata->fladj_value;
|
||||
}
|
||||
|
||||
/* default to superspeed if no maximum_speed passed */
|
||||
if (dwc->maximum_speed == USB_SPEED_UNKNOWN)
|
||||
dwc->maximum_speed = USB_SPEED_SUPER;
|
||||
|
||||
dwc->lpm_nyet_threshold = lpm_nyet_threshold;
|
||||
dwc->tx_de_emphasis = tx_de_emphasis;
|
||||
|
||||
|
@ -1016,6 +1012,33 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
goto err1;
|
||||
}
|
||||
|
||||
/* Check the maximum_speed parameter */
|
||||
switch (dwc->maximum_speed) {
|
||||
case USB_SPEED_LOW:
|
||||
case USB_SPEED_FULL:
|
||||
case USB_SPEED_HIGH:
|
||||
case USB_SPEED_SUPER:
|
||||
case USB_SPEED_SUPER_PLUS:
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "invalid maximum_speed parameter %d\n",
|
||||
dwc->maximum_speed);
|
||||
/* fall through */
|
||||
case USB_SPEED_UNKNOWN:
|
||||
/* default to superspeed */
|
||||
dwc->maximum_speed = USB_SPEED_SUPER;
|
||||
|
||||
/*
|
||||
* default to superspeed plus if we are capable.
|
||||
*/
|
||||
if (dwc3_is_usb31(dwc) &&
|
||||
(DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) ==
|
||||
DWC3_GHWPARAMS3_SSPHY_IFC_GEN2))
|
||||
dwc->maximum_speed = USB_SPEED_SUPER_PLUS;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* Adjust Frame Length */
|
||||
dwc3_frame_length_adjustment(dwc, fladj);
|
||||
|
||||
|
|
|
@ -223,7 +223,8 @@
|
|||
/* Global HWPARAMS3 Register */
|
||||
#define DWC3_GHWPARAMS3_SSPHY_IFC(n) ((n) & 3)
|
||||
#define DWC3_GHWPARAMS3_SSPHY_IFC_DIS 0
|
||||
#define DWC3_GHWPARAMS3_SSPHY_IFC_ENA 1
|
||||
#define DWC3_GHWPARAMS3_SSPHY_IFC_GEN1 1
|
||||
#define DWC3_GHWPARAMS3_SSPHY_IFC_GEN2 2 /* DWC_usb31 only */
|
||||
#define DWC3_GHWPARAMS3_HSPHY_IFC(n) (((n) & (3 << 2)) >> 2)
|
||||
#define DWC3_GHWPARAMS3_HSPHY_IFC_DIS 0
|
||||
#define DWC3_GHWPARAMS3_HSPHY_IFC_UTMI 1
|
||||
|
@ -249,6 +250,7 @@
|
|||
#define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f)
|
||||
|
||||
#define DWC3_DCFG_SPEED_MASK (7 << 0)
|
||||
#define DWC3_DCFG_SUPERSPEED_PLUS (5 << 0) /* DWC_usb31 only */
|
||||
#define DWC3_DCFG_SUPERSPEED (4 << 0)
|
||||
#define DWC3_DCFG_HIGHSPEED (0 << 0)
|
||||
#define DWC3_DCFG_FULLSPEED2 (1 << 0)
|
||||
|
@ -339,6 +341,7 @@
|
|||
|
||||
#define DWC3_DSTS_CONNECTSPD (7 << 0)
|
||||
|
||||
#define DWC3_DSTS_SUPERSPEED_PLUS (5 << 0) /* DWC_usb31 only */
|
||||
#define DWC3_DSTS_SUPERSPEED (4 << 0)
|
||||
#define DWC3_DSTS_HIGHSPEED (0 << 0)
|
||||
#define DWC3_DSTS_FULLSPEED2 (1 << 0)
|
||||
|
@ -1024,6 +1027,12 @@ struct dwc3_gadget_ep_cmd_params {
|
|||
void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
|
||||
int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc);
|
||||
|
||||
/* check whether we are on the DWC_usb31 core */
|
||||
static inline bool dwc3_is_usb31(struct dwc3 *dwc)
|
||||
{
|
||||
return !!(dwc->revision & DWC3_REVISION_IS_DWC31);
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
|
||||
int dwc3_host_init(struct dwc3 *dwc);
|
||||
void dwc3_host_exit(struct dwc3 *dwc);
|
||||
|
|
|
@ -356,7 +356,8 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
|
|||
*/
|
||||
usb_status |= dwc->gadget.is_selfpowered;
|
||||
|
||||
if (dwc->speed == DWC3_DSTS_SUPERSPEED) {
|
||||
if ((dwc->speed == DWC3_DSTS_SUPERSPEED) ||
|
||||
(dwc->speed == DWC3_DSTS_SUPERSPEED_PLUS)) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
if (reg & DWC3_DCTL_INITU1ENA)
|
||||
usb_status |= 1 << USB_DEV_STAT_U1_ENABLED;
|
||||
|
@ -426,7 +427,8 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
|
|||
case USB_DEVICE_U1_ENABLE:
|
||||
if (state != USB_STATE_CONFIGURED)
|
||||
return -EINVAL;
|
||||
if (dwc->speed != DWC3_DSTS_SUPERSPEED)
|
||||
if ((dwc->speed != DWC3_DSTS_SUPERSPEED) &&
|
||||
(dwc->speed != DWC3_DSTS_SUPERSPEED_PLUS))
|
||||
return -EINVAL;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
|
@ -440,7 +442,8 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
|
|||
case USB_DEVICE_U2_ENABLE:
|
||||
if (state != USB_STATE_CONFIGURED)
|
||||
return -EINVAL;
|
||||
if (dwc->speed != DWC3_DSTS_SUPERSPEED)
|
||||
if ((dwc->speed != DWC3_DSTS_SUPERSPEED) &&
|
||||
(dwc->speed != DWC3_DSTS_SUPERSPEED_PLUS))
|
||||
return -EINVAL;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
|
|
|
@ -463,7 +463,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
|
|||
| DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc));
|
||||
|
||||
/* Burst size is only needed in SuperSpeed mode */
|
||||
if (dwc->gadget.speed == USB_SPEED_SUPER) {
|
||||
if (dwc->gadget.speed >= USB_SPEED_SUPER) {
|
||||
u32 burst = dep->endpoint.maxburst - 1;
|
||||
|
||||
params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst);
|
||||
|
@ -1441,7 +1441,8 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
|
|||
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
|
||||
|
||||
speed = reg & DWC3_DSTS_CONNECTSPD;
|
||||
if (speed == DWC3_DSTS_SUPERSPEED) {
|
||||
if ((speed == DWC3_DSTS_SUPERSPEED) ||
|
||||
(speed == DWC3_DSTS_SUPERSPEED_PLUS)) {
|
||||
dwc3_trace(trace_dwc3_gadget, "no wakeup on SuperSpeed\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
|
@ -1666,10 +1667,16 @@ static int dwc3_gadget_start(struct usb_gadget *g,
|
|||
case USB_SPEED_HIGH:
|
||||
reg |= DWC3_DSTS_HIGHSPEED;
|
||||
break;
|
||||
case USB_SPEED_SUPER: /* FALLTHROUGH */
|
||||
case USB_SPEED_UNKNOWN: /* FALTHROUGH */
|
||||
case USB_SPEED_SUPER_PLUS:
|
||||
reg |= DWC3_DSTS_SUPERSPEED_PLUS;
|
||||
break;
|
||||
default:
|
||||
reg |= DWC3_DSTS_SUPERSPEED;
|
||||
dev_err(dwc->dev, "invalid dwc->maximum_speed (%d)\n",
|
||||
dwc->maximum_speed);
|
||||
/* fall through */
|
||||
case USB_SPEED_SUPER:
|
||||
reg |= DWC3_DCFG_SUPERSPEED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
|
||||
|
@ -2340,7 +2347,8 @@ static void dwc3_update_ram_clk_sel(struct dwc3 *dwc, u32 speed)
|
|||
* this. Maybe it becomes part of the power saving plan.
|
||||
*/
|
||||
|
||||
if (speed != DWC3_DSTS_SUPERSPEED)
|
||||
if ((speed != DWC3_DSTS_SUPERSPEED) &&
|
||||
(speed != DWC3_DSTS_SUPERSPEED_PLUS))
|
||||
return;
|
||||
|
||||
/*
|
||||
|
@ -2369,6 +2377,11 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
|
|||
dwc3_update_ram_clk_sel(dwc, speed);
|
||||
|
||||
switch (speed) {
|
||||
case DWC3_DCFG_SUPERSPEED_PLUS:
|
||||
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
|
||||
dwc->gadget.ep0->maxpacket = 512;
|
||||
dwc->gadget.speed = USB_SPEED_SUPER_PLUS;
|
||||
break;
|
||||
case DWC3_DCFG_SUPERSPEED:
|
||||
/*
|
||||
* WORKAROUND: DWC3 revisions <1.90a have an issue which
|
||||
|
@ -2410,8 +2423,9 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
|
|||
|
||||
/* Enable USB2 LPM Capability */
|
||||
|
||||
if ((dwc->revision > DWC3_REVISION_194A)
|
||||
&& (speed != DWC3_DCFG_SUPERSPEED)) {
|
||||
if ((dwc->revision > DWC3_REVISION_194A) &&
|
||||
(speed != DWC3_DCFG_SUPERSPEED) &&
|
||||
(speed != DWC3_DCFG_SUPERSPEED_PLUS)) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
|
||||
reg |= DWC3_DCFG_LPM_CAP;
|
||||
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
|
||||
|
|
|
@ -53,6 +53,36 @@ static struct usb_gadget_strings **get_containers_gs(
|
|||
return (struct usb_gadget_strings **)uc->stash;
|
||||
}
|
||||
|
||||
/**
|
||||
* function_descriptors() - get function descriptors for speed
|
||||
* @f: the function
|
||||
* @speed: the speed
|
||||
*
|
||||
* Returns the descriptors or NULL if not set.
|
||||
*/
|
||||
static struct usb_descriptor_header **
|
||||
function_descriptors(struct usb_function *f,
|
||||
enum usb_device_speed speed)
|
||||
{
|
||||
struct usb_descriptor_header **descriptors;
|
||||
|
||||
switch (speed) {
|
||||
case USB_SPEED_SUPER_PLUS:
|
||||
descriptors = f->ssp_descriptors;
|
||||
break;
|
||||
case USB_SPEED_SUPER:
|
||||
descriptors = f->ss_descriptors;
|
||||
break;
|
||||
case USB_SPEED_HIGH:
|
||||
descriptors = f->hs_descriptors;
|
||||
break;
|
||||
default:
|
||||
descriptors = f->fs_descriptors;
|
||||
}
|
||||
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
/**
|
||||
* next_ep_desc() - advance to the next EP descriptor
|
||||
* @t: currect pointer within descriptor array
|
||||
|
@ -118,6 +148,13 @@ int config_ep_by_speed(struct usb_gadget *g,
|
|||
|
||||
/* select desired speed */
|
||||
switch (g->speed) {
|
||||
case USB_SPEED_SUPER_PLUS:
|
||||
if (gadget_is_superspeed_plus(g)) {
|
||||
speed_desc = f->ssp_descriptors;
|
||||
want_comp_desc = 1;
|
||||
break;
|
||||
}
|
||||
/* else: Fall trough */
|
||||
case USB_SPEED_SUPER:
|
||||
if (gadget_is_superspeed(g)) {
|
||||
speed_desc = f->ss_descriptors;
|
||||
|
@ -161,7 +198,7 @@ ep_found:
|
|||
(comp_desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP))
|
||||
return -EIO;
|
||||
_ep->comp_desc = comp_desc;
|
||||
if (g->speed == USB_SPEED_SUPER) {
|
||||
if (g->speed >= USB_SPEED_SUPER) {
|
||||
switch (usb_endpoint_type(_ep->desc)) {
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
/* mult: bits 1:0 of bmAttributes */
|
||||
|
@ -237,6 +274,8 @@ int usb_add_function(struct usb_configuration *config,
|
|||
config->highspeed = true;
|
||||
if (!config->superspeed && function->ss_descriptors)
|
||||
config->superspeed = true;
|
||||
if (!config->superspeed_plus && function->ssp_descriptors)
|
||||
config->superspeed_plus = true;
|
||||
|
||||
done:
|
||||
if (value)
|
||||
|
@ -417,17 +456,7 @@ static int config_buf(struct usb_configuration *config,
|
|||
list_for_each_entry(f, &config->functions, list) {
|
||||
struct usb_descriptor_header **descriptors;
|
||||
|
||||
switch (speed) {
|
||||
case USB_SPEED_SUPER:
|
||||
descriptors = f->ss_descriptors;
|
||||
break;
|
||||
case USB_SPEED_HIGH:
|
||||
descriptors = f->hs_descriptors;
|
||||
break;
|
||||
default:
|
||||
descriptors = f->fs_descriptors;
|
||||
}
|
||||
|
||||
descriptors = function_descriptors(f, speed);
|
||||
if (!descriptors)
|
||||
continue;
|
||||
status = usb_descriptor_fillbuf(next, len,
|
||||
|
@ -451,7 +480,7 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value)
|
|||
u8 type = w_value >> 8;
|
||||
enum usb_device_speed speed = USB_SPEED_UNKNOWN;
|
||||
|
||||
if (gadget->speed == USB_SPEED_SUPER)
|
||||
if (gadget->speed >= USB_SPEED_SUPER)
|
||||
speed = gadget->speed;
|
||||
else if (gadget_is_dualspeed(gadget)) {
|
||||
int hs = 0;
|
||||
|
@ -482,6 +511,10 @@ static int config_desc(struct usb_composite_dev *cdev, unsigned w_value)
|
|||
check_config:
|
||||
/* ignore configs that won't work at this speed */
|
||||
switch (speed) {
|
||||
case USB_SPEED_SUPER_PLUS:
|
||||
if (!c->superspeed_plus)
|
||||
continue;
|
||||
break;
|
||||
case USB_SPEED_SUPER:
|
||||
if (!c->superspeed)
|
||||
continue;
|
||||
|
@ -509,18 +542,24 @@ static int count_configs(struct usb_composite_dev *cdev, unsigned type)
|
|||
unsigned count = 0;
|
||||
int hs = 0;
|
||||
int ss = 0;
|
||||
int ssp = 0;
|
||||
|
||||
if (gadget_is_dualspeed(gadget)) {
|
||||
if (gadget->speed == USB_SPEED_HIGH)
|
||||
hs = 1;
|
||||
if (gadget->speed == USB_SPEED_SUPER)
|
||||
ss = 1;
|
||||
if (gadget->speed == USB_SPEED_SUPER_PLUS)
|
||||
ssp = 1;
|
||||
if (type == USB_DT_DEVICE_QUALIFIER)
|
||||
hs = !hs;
|
||||
}
|
||||
list_for_each_entry(c, &cdev->configs, list) {
|
||||
/* ignore configs that won't work at this speed */
|
||||
if (ss) {
|
||||
if (ssp) {
|
||||
if (!c->superspeed_plus)
|
||||
continue;
|
||||
} else if (ss) {
|
||||
if (!c->superspeed)
|
||||
continue;
|
||||
} else if (hs) {
|
||||
|
@ -597,6 +636,48 @@ static int bos_desc(struct usb_composite_dev *cdev)
|
|||
ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat;
|
||||
ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat;
|
||||
|
||||
/* The SuperSpeedPlus USB Device Capability descriptor */
|
||||
if (gadget_is_superspeed_plus(cdev->gadget)) {
|
||||
struct usb_ssp_cap_descriptor *ssp_cap;
|
||||
|
||||
ssp_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
|
||||
bos->bNumDeviceCaps++;
|
||||
|
||||
/*
|
||||
* Report typical values.
|
||||
*/
|
||||
|
||||
le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SSP_CAP_SIZE(1));
|
||||
ssp_cap->bLength = USB_DT_USB_SSP_CAP_SIZE(1);
|
||||
ssp_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
|
||||
ssp_cap->bDevCapabilityType = USB_SSP_CAP_TYPE;
|
||||
|
||||
/* SSAC = 1 (2 attributes) */
|
||||
ssp_cap->bmAttributes = cpu_to_le32(1);
|
||||
|
||||
/* Min RX/TX Lane Count = 1 */
|
||||
ssp_cap->wFunctionalitySupport = (1 << 8) | (1 << 12);
|
||||
|
||||
/*
|
||||
* bmSublinkSpeedAttr[0]:
|
||||
* ST = Symmetric, RX
|
||||
* LSE = 3 (Gbps)
|
||||
* LP = 1 (SuperSpeedPlus)
|
||||
* LSM = 10 (10 Gbps)
|
||||
*/
|
||||
ssp_cap->bmSublinkSpeedAttr[0] =
|
||||
(3 << 4) | (1 << 14) | (0xa << 16);
|
||||
/*
|
||||
* bmSublinkSpeedAttr[1] =
|
||||
* ST = Symmetric, TX
|
||||
* LSE = 3 (Gbps)
|
||||
* LP = 1 (SuperSpeedPlus)
|
||||
* LSM = 10 (10 Gbps)
|
||||
*/
|
||||
ssp_cap->bmSublinkSpeedAttr[1] =
|
||||
(3 << 4) | (1 << 14) | (0xa << 16) | (1 << 7);
|
||||
}
|
||||
|
||||
return le16_to_cpu(bos->wTotalLength);
|
||||
}
|
||||
|
||||
|
@ -690,16 +771,7 @@ static int set_config(struct usb_composite_dev *cdev,
|
|||
* function's setup callback instead of the current
|
||||
* configuration's setup callback.
|
||||
*/
|
||||
switch (gadget->speed) {
|
||||
case USB_SPEED_SUPER:
|
||||
descriptors = f->ss_descriptors;
|
||||
break;
|
||||
case USB_SPEED_HIGH:
|
||||
descriptors = f->hs_descriptors;
|
||||
break;
|
||||
default:
|
||||
descriptors = f->fs_descriptors;
|
||||
}
|
||||
descriptors = function_descriptors(f, gadget->speed);
|
||||
|
||||
for (; *descriptors; ++descriptors) {
|
||||
struct usb_endpoint_descriptor *ep;
|
||||
|
@ -819,8 +891,9 @@ int usb_add_config(struct usb_composite_dev *cdev,
|
|||
} else {
|
||||
unsigned i;
|
||||
|
||||
DBG(cdev, "cfg %d/%p speeds:%s%s%s\n",
|
||||
DBG(cdev, "cfg %d/%p speeds:%s%s%s%s\n",
|
||||
config->bConfigurationValue, config,
|
||||
config->superspeed_plus ? " superplus" : "",
|
||||
config->superspeed ? " super" : "",
|
||||
config->highspeed ? " high" : "",
|
||||
config->fullspeed
|
||||
|
@ -1499,7 +1572,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|
|||
cdev->gadget->ep0->maxpacket;
|
||||
if (gadget_is_superspeed(gadget)) {
|
||||
if (gadget->speed >= USB_SPEED_SUPER) {
|
||||
cdev->desc.bcdUSB = cpu_to_le16(0x0300);
|
||||
cdev->desc.bcdUSB = cpu_to_le16(0x0310);
|
||||
cdev->desc.bMaxPacketSize0 = 9;
|
||||
} else {
|
||||
cdev->desc.bcdUSB = cpu_to_le16(0x0210);
|
||||
|
@ -1634,15 +1707,24 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|
|||
*((u8 *)req->buf) = value;
|
||||
value = min(w_length, (u16) 1);
|
||||
break;
|
||||
|
||||
/*
|
||||
* USB 3.0 additions:
|
||||
* Function driver should handle get_status request. If such cb
|
||||
* wasn't supplied we respond with default value = 0
|
||||
* Note: function driver should supply such cb only for the first
|
||||
* interface of the function
|
||||
*/
|
||||
case USB_REQ_GET_STATUS:
|
||||
if (gadget_is_otg(gadget) && gadget->hnp_polling_support &&
|
||||
(w_index == OTG_STS_SELECTOR)) {
|
||||
if (ctrl->bRequestType != (USB_DIR_IN |
|
||||
USB_RECIP_DEVICE))
|
||||
goto unknown;
|
||||
*((u8 *)req->buf) = gadget->host_request_flag;
|
||||
value = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* USB 3.0 additions:
|
||||
* Function driver should handle get_status request. If such cb
|
||||
* wasn't supplied we respond with default value = 0
|
||||
* Note: function driver should supply such cb only for the
|
||||
* first interface of the function
|
||||
*/
|
||||
if (!gadget_is_superspeed(gadget))
|
||||
goto unknown;
|
||||
if (ctrl->bRequestType != (USB_DIR_IN | USB_RECIP_INTERFACE))
|
||||
|
|
|
@ -163,7 +163,8 @@ EXPORT_SYMBOL_GPL(usb_copy_descriptors);
|
|||
int usb_assign_descriptors(struct usb_function *f,
|
||||
struct usb_descriptor_header **fs,
|
||||
struct usb_descriptor_header **hs,
|
||||
struct usb_descriptor_header **ss)
|
||||
struct usb_descriptor_header **ss,
|
||||
struct usb_descriptor_header **ssp)
|
||||
{
|
||||
struct usb_gadget *g = f->config->cdev->gadget;
|
||||
|
||||
|
@ -182,6 +183,11 @@ int usb_assign_descriptors(struct usb_function *f,
|
|||
if (!f->ss_descriptors)
|
||||
goto err;
|
||||
}
|
||||
if (ssp && gadget_is_superspeed_plus(g)) {
|
||||
f->ssp_descriptors = usb_copy_descriptors(ssp);
|
||||
if (!f->ssp_descriptors)
|
||||
goto err;
|
||||
}
|
||||
return 0;
|
||||
err:
|
||||
usb_free_all_descriptors(f);
|
||||
|
@ -194,6 +200,7 @@ void usb_free_all_descriptors(struct usb_function *f)
|
|||
usb_free_descriptors(f->fs_descriptors);
|
||||
usb_free_descriptors(f->hs_descriptors);
|
||||
usb_free_descriptors(f->ss_descriptors);
|
||||
usb_free_descriptors(f->ssp_descriptors);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_free_all_descriptors);
|
||||
|
||||
|
|
|
@ -1229,6 +1229,7 @@ static void purge_configs_funcs(struct gadget_info *gi)
|
|||
}
|
||||
c->next_interface_id = 0;
|
||||
memset(c->interface, 0, sizeof(c->interface));
|
||||
c->superspeed_plus = 0;
|
||||
c->superspeed = 0;
|
||||
c->highspeed = 0;
|
||||
c->fullspeed = 0;
|
||||
|
|
|
@ -685,7 +685,7 @@ acm_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
acm_ss_out_desc.bEndpointAddress = acm_fs_out_desc.bEndpointAddress;
|
||||
|
||||
status = usb_assign_descriptors(f, acm_fs_function, acm_hs_function,
|
||||
acm_ss_function);
|
||||
acm_ss_function, NULL);
|
||||
if (status)
|
||||
goto fail;
|
||||
|
||||
|
@ -777,10 +777,10 @@ static ssize_t f_acm_port_num_show(struct config_item *item, char *page)
|
|||
return sprintf(page, "%u\n", to_f_serial_opts(item)->port_num);
|
||||
}
|
||||
|
||||
CONFIGFS_ATTR_RO(f_acm_port_, num);
|
||||
CONFIGFS_ATTR_RO(f_acm_, port_num);
|
||||
|
||||
static struct configfs_attribute *acm_attrs[] = {
|
||||
&f_acm_port_attr_num,
|
||||
&f_acm_attr_port_num,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
|
|
@ -786,7 +786,7 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
fs_ecm_notify_desc.bEndpointAddress;
|
||||
|
||||
status = usb_assign_descriptors(f, ecm_fs_function, ecm_hs_function,
|
||||
ecm_ss_function);
|
||||
ecm_ss_function, NULL);
|
||||
if (status)
|
||||
goto fail;
|
||||
|
||||
|
|
|
@ -309,7 +309,7 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
eem_ss_out_desc.bEndpointAddress = eem_fs_out_desc.bEndpointAddress;
|
||||
|
||||
status = usb_assign_descriptors(f, eem_fs_function, eem_hs_function,
|
||||
eem_ss_function);
|
||||
eem_ss_function, NULL);
|
||||
if (status)
|
||||
goto fail;
|
||||
|
||||
|
|
|
@ -684,44 +684,38 @@ static void ffs_epfile_async_io_complete(struct usb_ep *_ep,
|
|||
static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
|
||||
{
|
||||
struct ffs_epfile *epfile = file->private_data;
|
||||
struct usb_request *req;
|
||||
struct ffs_ep *ep;
|
||||
char *data = NULL;
|
||||
ssize_t ret, data_len = -EINVAL;
|
||||
int halt;
|
||||
|
||||
/* Are we still active? */
|
||||
if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) {
|
||||
ret = -ENODEV;
|
||||
goto error;
|
||||
}
|
||||
if (WARN_ON(epfile->ffs->state != FFS_ACTIVE))
|
||||
return -ENODEV;
|
||||
|
||||
/* Wait for endpoint to be enabled */
|
||||
ep = epfile->ep;
|
||||
if (!ep) {
|
||||
if (file->f_flags & O_NONBLOCK) {
|
||||
ret = -EAGAIN;
|
||||
goto error;
|
||||
}
|
||||
if (file->f_flags & O_NONBLOCK)
|
||||
return -EAGAIN;
|
||||
|
||||
ret = wait_event_interruptible(epfile->wait, (ep = epfile->ep));
|
||||
if (ret) {
|
||||
ret = -EINTR;
|
||||
goto error;
|
||||
}
|
||||
if (ret)
|
||||
return -EINTR;
|
||||
}
|
||||
|
||||
/* Do we halt? */
|
||||
halt = (!io_data->read == !epfile->in);
|
||||
if (halt && epfile->isoc) {
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
if (halt && epfile->isoc)
|
||||
return -EINVAL;
|
||||
|
||||
/* Allocate & copy */
|
||||
if (!halt) {
|
||||
/*
|
||||
* if we _do_ wait above, the epfile->ffs->gadget might be NULL
|
||||
* before the waiting completes, so do not assign to 'gadget' earlier
|
||||
* before the waiting completes, so do not assign to 'gadget'
|
||||
* earlier
|
||||
*/
|
||||
struct usb_gadget *gadget = epfile->ffs->gadget;
|
||||
size_t copied;
|
||||
|
@ -763,17 +757,12 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
|
|||
if (epfile->ep != ep) {
|
||||
/* In the meantime, endpoint got disabled or changed. */
|
||||
ret = -ESHUTDOWN;
|
||||
spin_unlock_irq(&epfile->ffs->eps_lock);
|
||||
} else if (halt) {
|
||||
/* Halt */
|
||||
if (likely(epfile->ep == ep) && !WARN_ON(!ep->ep))
|
||||
usb_ep_set_halt(ep->ep);
|
||||
spin_unlock_irq(&epfile->ffs->eps_lock);
|
||||
ret = -EBADMSG;
|
||||
} else {
|
||||
/* Fire the request */
|
||||
struct usb_request *req;
|
||||
|
||||
} else if (unlikely(data_len == -EINVAL)) {
|
||||
/*
|
||||
* Sanity Check: even though data_len can't be used
|
||||
* uninitialized at the time I write this comment, some
|
||||
|
@ -785,80 +774,80 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
|
|||
* For such reason, we're adding this redundant sanity check
|
||||
* here.
|
||||
*/
|
||||
if (unlikely(data_len == -EINVAL)) {
|
||||
WARN(1, "%s: data_len == -EINVAL\n", __func__);
|
||||
ret = -EINVAL;
|
||||
WARN(1, "%s: data_len == -EINVAL\n", __func__);
|
||||
ret = -EINVAL;
|
||||
} else if (!io_data->aio) {
|
||||
DECLARE_COMPLETION_ONSTACK(done);
|
||||
bool interrupted = false;
|
||||
|
||||
req = ep->req;
|
||||
req->buf = data;
|
||||
req->length = data_len;
|
||||
|
||||
req->context = &done;
|
||||
req->complete = ffs_epfile_io_complete;
|
||||
|
||||
ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
|
||||
if (unlikely(ret < 0))
|
||||
goto error_lock;
|
||||
|
||||
spin_unlock_irq(&epfile->ffs->eps_lock);
|
||||
|
||||
if (unlikely(wait_for_completion_interruptible(&done))) {
|
||||
/*
|
||||
* To avoid race condition with ffs_epfile_io_complete,
|
||||
* dequeue the request first then check
|
||||
* status. usb_ep_dequeue API should guarantee no race
|
||||
* condition with req->complete callback.
|
||||
*/
|
||||
usb_ep_dequeue(ep->ep, req);
|
||||
interrupted = ep->status < 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX We may end up silently droping data here. Since data_len
|
||||
* (i.e. req->length) may be bigger than len (after being
|
||||
* rounded up to maxpacketsize), we may end up with more data
|
||||
* then user space has space for.
|
||||
*/
|
||||
ret = interrupted ? -EINTR : ep->status;
|
||||
if (io_data->read && ret > 0) {
|
||||
ret = copy_to_iter(data, ret, &io_data->data);
|
||||
if (!ret)
|
||||
ret = -EFAULT;
|
||||
}
|
||||
goto error_mutex;
|
||||
} else if (!(req = usb_ep_alloc_request(ep->ep, GFP_KERNEL))) {
|
||||
ret = -ENOMEM;
|
||||
} else {
|
||||
req->buf = data;
|
||||
req->length = data_len;
|
||||
|
||||
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;
|
||||
|
||||
ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
|
||||
if (unlikely(ret)) {
|
||||
usb_ep_free_request(ep->ep, req);
|
||||
goto error_lock;
|
||||
}
|
||||
|
||||
if (io_data->aio) {
|
||||
req = usb_ep_alloc_request(ep->ep, GFP_KERNEL);
|
||||
if (unlikely(!req))
|
||||
goto error_lock;
|
||||
|
||||
req->buf = data;
|
||||
req->length = data_len;
|
||||
|
||||
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;
|
||||
|
||||
ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
|
||||
if (unlikely(ret)) {
|
||||
usb_ep_free_request(ep->ep, req);
|
||||
goto error_lock;
|
||||
}
|
||||
ret = -EIOCBQUEUED;
|
||||
|
||||
spin_unlock_irq(&epfile->ffs->eps_lock);
|
||||
} else {
|
||||
DECLARE_COMPLETION_ONSTACK(done);
|
||||
|
||||
req = ep->req;
|
||||
req->buf = data;
|
||||
req->length = data_len;
|
||||
|
||||
req->context = &done;
|
||||
req->complete = ffs_epfile_io_complete;
|
||||
|
||||
ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
|
||||
|
||||
spin_unlock_irq(&epfile->ffs->eps_lock);
|
||||
|
||||
if (unlikely(ret < 0)) {
|
||||
/* nop */
|
||||
} else if (unlikely(
|
||||
wait_for_completion_interruptible(&done))) {
|
||||
ret = -EINTR;
|
||||
usb_ep_dequeue(ep->ep, req);
|
||||
} else {
|
||||
/*
|
||||
* XXX We may end up silently droping data
|
||||
* here. Since data_len (i.e. req->length) may
|
||||
* be bigger than len (after being rounded up
|
||||
* to maxpacketsize), we may end up with more
|
||||
* data then user space has space for.
|
||||
*/
|
||||
ret = ep->status;
|
||||
if (io_data->read && ret > 0) {
|
||||
ret = copy_to_iter(data, ret, &io_data->data);
|
||||
if (!ret)
|
||||
ret = -EFAULT;
|
||||
}
|
||||
}
|
||||
kfree(data);
|
||||
}
|
||||
ret = -EIOCBQUEUED;
|
||||
/*
|
||||
* Do not kfree the buffer in this function. It will be freed
|
||||
* by ffs_user_copy_worker.
|
||||
*/
|
||||
data = NULL;
|
||||
}
|
||||
|
||||
mutex_unlock(&epfile->mutex);
|
||||
return ret;
|
||||
|
||||
error_lock:
|
||||
spin_unlock_irq(&epfile->ffs->eps_lock);
|
||||
error_mutex:
|
||||
mutex_unlock(&epfile->mutex);
|
||||
error:
|
||||
kfree(data);
|
||||
|
|
|
@ -646,7 +646,7 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
hidg_fs_out_ep_desc.bEndpointAddress;
|
||||
|
||||
status = usb_assign_descriptors(f, hidg_fs_descriptors,
|
||||
hidg_hs_descriptors, NULL);
|
||||
hidg_hs_descriptors, NULL, NULL);
|
||||
if (status)
|
||||
goto fail;
|
||||
|
||||
|
|
|
@ -211,7 +211,7 @@ autoconf_fail:
|
|||
ss_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress;
|
||||
|
||||
ret = usb_assign_descriptors(f, fs_loopback_descs, hs_loopback_descs,
|
||||
ss_loopback_descs);
|
||||
ss_loopback_descs, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -3093,7 +3093,7 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
fsg_ss_bulk_out_comp_desc.bMaxBurst = max_burst;
|
||||
|
||||
ret = usb_assign_descriptors(f, fsg_fs_function, fsg_hs_function,
|
||||
fsg_ss_function);
|
||||
fsg_ss_function, fsg_ss_function);
|
||||
if (ret)
|
||||
goto autoconf_fail;
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ static const char f_midi_longname[] = "MIDI Gadget";
|
|||
* USB <- IN endpoint <- rawmidi
|
||||
*/
|
||||
struct gmidi_in_port {
|
||||
struct f_midi *midi;
|
||||
struct snd_rawmidi_substream *substream;
|
||||
int active;
|
||||
uint8_t cable;
|
||||
uint8_t state;
|
||||
|
@ -78,9 +78,7 @@ struct f_midi {
|
|||
struct snd_rawmidi *rmidi;
|
||||
u8 ms_id;
|
||||
|
||||
struct snd_rawmidi_substream *in_substream[MAX_PORTS];
|
||||
struct snd_rawmidi_substream *out_substream[MAX_PORTS];
|
||||
struct gmidi_in_port *in_port[MAX_PORTS];
|
||||
|
||||
unsigned long out_triggered;
|
||||
struct tasklet_struct tasklet;
|
||||
|
@ -92,6 +90,8 @@ struct f_midi {
|
|||
/* This fifo is used as a buffer ring for pre-allocated IN usb_requests */
|
||||
DECLARE_KFIFO_PTR(in_req_fifo, struct usb_request *);
|
||||
unsigned int in_last_port;
|
||||
|
||||
struct gmidi_in_port in_ports_array[/* in_ports */];
|
||||
};
|
||||
|
||||
static inline struct f_midi *func_to_midi(struct usb_function *f)
|
||||
|
@ -518,98 +518,95 @@ static void f_midi_drop_out_substreams(struct f_midi *midi)
|
|||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < MAX_PORTS; i++) {
|
||||
struct gmidi_in_port *port = midi->in_port[i];
|
||||
struct snd_rawmidi_substream *substream = midi->in_substream[i];
|
||||
for (i = 0; i < midi->in_ports; i++) {
|
||||
struct gmidi_in_port *port = midi->in_ports_array + i;
|
||||
struct snd_rawmidi_substream *substream = port->substream;
|
||||
if (port->active && substream)
|
||||
snd_rawmidi_drop_output(substream);
|
||||
}
|
||||
}
|
||||
|
||||
if (!port)
|
||||
break;
|
||||
static int f_midi_do_transmit(struct f_midi *midi, struct usb_ep *ep)
|
||||
{
|
||||
struct usb_request *req = NULL;
|
||||
unsigned int len, i;
|
||||
bool active = false;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* We peek the request in order to reuse it if it fails to enqueue on
|
||||
* its endpoint
|
||||
*/
|
||||
len = kfifo_peek(&midi->in_req_fifo, &req);
|
||||
if (len != 1) {
|
||||
ERROR(midi, "%s: Couldn't get usb request\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If buffer overrun, then we ignore this transmission.
|
||||
* IMPORTANT: This will cause the user-space rawmidi device to block
|
||||
* until a) usb requests have been completed or b) snd_rawmidi_write()
|
||||
* times out.
|
||||
*/
|
||||
if (req->length > 0)
|
||||
return 0;
|
||||
|
||||
for (i = midi->in_last_port; i < midi->in_ports; ++i) {
|
||||
struct gmidi_in_port *port = midi->in_ports_array + i;
|
||||
struct snd_rawmidi_substream *substream = port->substream;
|
||||
|
||||
if (!port->active || !substream)
|
||||
continue;
|
||||
|
||||
snd_rawmidi_drop_output(substream);
|
||||
while (req->length + 3 < midi->buflen) {
|
||||
uint8_t b;
|
||||
|
||||
if (snd_rawmidi_transmit(substream, &b, 1) != 1) {
|
||||
port->active = 0;
|
||||
break;
|
||||
}
|
||||
f_midi_transmit_byte(req, port, b);
|
||||
}
|
||||
|
||||
active = !!port->active;
|
||||
if (active)
|
||||
break;
|
||||
}
|
||||
midi->in_last_port = active ? i : 0;
|
||||
|
||||
if (req->length <= 0)
|
||||
goto done;
|
||||
|
||||
err = usb_ep_queue(ep, req, GFP_ATOMIC);
|
||||
if (err < 0) {
|
||||
ERROR(midi, "%s failed to queue req: %d\n",
|
||||
midi->in_ep->name, err);
|
||||
req->length = 0; /* Re-use request next time. */
|
||||
} else {
|
||||
/* Upon success, put request at the back of the queue. */
|
||||
kfifo_skip(&midi->in_req_fifo);
|
||||
kfifo_put(&midi->in_req_fifo, req);
|
||||
}
|
||||
|
||||
done:
|
||||
return active;
|
||||
}
|
||||
|
||||
static void f_midi_transmit(struct f_midi *midi)
|
||||
{
|
||||
struct usb_ep *ep = midi->in_ep;
|
||||
bool active;
|
||||
int ret;
|
||||
|
||||
/* We only care about USB requests if IN endpoint is enabled */
|
||||
if (!ep || !ep->enabled)
|
||||
goto drop_out;
|
||||
|
||||
do {
|
||||
struct usb_request *req = NULL;
|
||||
unsigned int len, i;
|
||||
|
||||
active = false;
|
||||
|
||||
/* We peek the request in order to reuse it if it fails
|
||||
* to enqueue on its endpoint */
|
||||
len = kfifo_peek(&midi->in_req_fifo, &req);
|
||||
if (len != 1) {
|
||||
ERROR(midi, "%s: Couldn't get usb request\n", __func__);
|
||||
ret = f_midi_do_transmit(midi, ep);
|
||||
if (ret < 0)
|
||||
goto drop_out;
|
||||
}
|
||||
|
||||
/* If buffer overrun, then we ignore this transmission.
|
||||
* IMPORTANT: This will cause the user-space rawmidi device to block until a) usb
|
||||
* requests have been completed or b) snd_rawmidi_write() times out. */
|
||||
if (req->length > 0)
|
||||
return;
|
||||
|
||||
for (i = midi->in_last_port; i < MAX_PORTS; i++) {
|
||||
struct gmidi_in_port *port = midi->in_port[i];
|
||||
struct snd_rawmidi_substream *substream = midi->in_substream[i];
|
||||
|
||||
if (!port) {
|
||||
/* Reset counter when we reach the last available port */
|
||||
midi->in_last_port = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!port->active || !substream)
|
||||
continue;
|
||||
|
||||
while (req->length + 3 < midi->buflen) {
|
||||
uint8_t b;
|
||||
|
||||
if (snd_rawmidi_transmit(substream, &b, 1) != 1) {
|
||||
port->active = 0;
|
||||
break;
|
||||
}
|
||||
f_midi_transmit_byte(req, port, b);
|
||||
}
|
||||
|
||||
active = !!port->active;
|
||||
/* Check if last port is still active, which means that
|
||||
* there is still data on that substream but this current
|
||||
* request run out of space. */
|
||||
if (active) {
|
||||
midi->in_last_port = i;
|
||||
/* There is no need to re-iterate though midi ports. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (req->length > 0) {
|
||||
int err;
|
||||
|
||||
err = usb_ep_queue(ep, req, GFP_ATOMIC);
|
||||
if (err < 0) {
|
||||
ERROR(midi, "%s failed to queue req: %d\n",
|
||||
midi->in_ep->name, err);
|
||||
req->length = 0; /* Re-use request next time. */
|
||||
} else {
|
||||
/* Upon success, put request at the back of the queue. */
|
||||
kfifo_skip(&midi->in_req_fifo);
|
||||
kfifo_put(&midi->in_req_fifo, req);
|
||||
}
|
||||
}
|
||||
} while (active);
|
||||
} while (ret);
|
||||
|
||||
return;
|
||||
|
||||
|
@ -626,13 +623,15 @@ static void f_midi_in_tasklet(unsigned long data)
|
|||
static int f_midi_in_open(struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
struct f_midi *midi = substream->rmidi->private_data;
|
||||
struct gmidi_in_port *port;
|
||||
|
||||
if (!midi->in_port[substream->number])
|
||||
if (substream->number >= midi->in_ports)
|
||||
return -EINVAL;
|
||||
|
||||
VDBG(midi, "%s()\n", __func__);
|
||||
midi->in_substream[substream->number] = substream;
|
||||
midi->in_port[substream->number]->state = STATE_UNKNOWN;
|
||||
port = midi->in_ports_array + substream->number;
|
||||
port->substream = substream;
|
||||
port->state = STATE_UNKNOWN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -648,11 +647,11 @@ static void f_midi_in_trigger(struct snd_rawmidi_substream *substream, int up)
|
|||
{
|
||||
struct f_midi *midi = substream->rmidi->private_data;
|
||||
|
||||
if (!midi->in_port[substream->number])
|
||||
if (substream->number >= midi->in_ports)
|
||||
return;
|
||||
|
||||
VDBG(midi, "%s() %d\n", __func__, up);
|
||||
midi->in_port[substream->number]->active = up;
|
||||
midi->in_ports_array[substream->number].active = up;
|
||||
if (up)
|
||||
tasklet_hi_schedule(&midi->tasklet);
|
||||
}
|
||||
|
@ -1128,14 +1127,11 @@ static void f_midi_free(struct usb_function *f)
|
|||
{
|
||||
struct f_midi *midi;
|
||||
struct f_midi_opts *opts;
|
||||
int i;
|
||||
|
||||
midi = func_to_midi(f);
|
||||
opts = container_of(f->fi, struct f_midi_opts, func_inst);
|
||||
kfree(midi->id);
|
||||
mutex_lock(&opts->lock);
|
||||
for (i = opts->in_ports - 1; i >= 0; --i)
|
||||
kfree(midi->in_port[i]);
|
||||
kfifo_free(&midi->in_req_fifo);
|
||||
kfree(midi);
|
||||
--opts->refcnt;
|
||||
|
@ -1163,7 +1159,7 @@ static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f)
|
|||
|
||||
static struct usb_function *f_midi_alloc(struct usb_function_instance *fi)
|
||||
{
|
||||
struct f_midi *midi;
|
||||
struct f_midi *midi = NULL;
|
||||
struct f_midi_opts *opts;
|
||||
int status, i;
|
||||
|
||||
|
@ -1172,37 +1168,26 @@ static struct usb_function *f_midi_alloc(struct usb_function_instance *fi)
|
|||
mutex_lock(&opts->lock);
|
||||
/* sanity check */
|
||||
if (opts->in_ports > MAX_PORTS || opts->out_ports > MAX_PORTS) {
|
||||
mutex_unlock(&opts->lock);
|
||||
return ERR_PTR(-EINVAL);
|
||||
status = -EINVAL;
|
||||
goto setup_fail;
|
||||
}
|
||||
|
||||
/* allocate and initialize one new instance */
|
||||
midi = kzalloc(sizeof(*midi), GFP_KERNEL);
|
||||
midi = kzalloc(
|
||||
sizeof(*midi) + opts->in_ports * sizeof(*midi->in_ports_array),
|
||||
GFP_KERNEL);
|
||||
if (!midi) {
|
||||
mutex_unlock(&opts->lock);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
status = -ENOMEM;
|
||||
goto setup_fail;
|
||||
}
|
||||
|
||||
for (i = 0; i < opts->in_ports; i++) {
|
||||
struct gmidi_in_port *port = kzalloc(sizeof(*port), GFP_KERNEL);
|
||||
|
||||
if (!port) {
|
||||
status = -ENOMEM;
|
||||
mutex_unlock(&opts->lock);
|
||||
goto setup_fail;
|
||||
}
|
||||
|
||||
port->midi = midi;
|
||||
port->active = 0;
|
||||
port->cable = i;
|
||||
midi->in_port[i] = port;
|
||||
}
|
||||
for (i = 0; i < opts->in_ports; i++)
|
||||
midi->in_ports_array[i].cable = i;
|
||||
|
||||
/* set up ALSA midi devices */
|
||||
midi->id = kstrdup(opts->id, GFP_KERNEL);
|
||||
if (opts->id && !midi->id) {
|
||||
status = -ENOMEM;
|
||||
mutex_unlock(&opts->lock);
|
||||
goto setup_fail;
|
||||
}
|
||||
midi->in_ports = opts->in_ports;
|
||||
|
@ -1229,8 +1214,7 @@ static struct usb_function *f_midi_alloc(struct usb_function_instance *fi)
|
|||
return &midi->func;
|
||||
|
||||
setup_fail:
|
||||
for (--i; i >= 0; i--)
|
||||
kfree(midi->in_port[i]);
|
||||
mutex_unlock(&opts->lock);
|
||||
kfree(midi);
|
||||
return ERR_PTR(status);
|
||||
}
|
||||
|
|
|
@ -1432,7 +1432,7 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
fs_ncm_notify_desc.bEndpointAddress;
|
||||
|
||||
status = usb_assign_descriptors(f, ncm_fs_function, ncm_hs_function,
|
||||
NULL);
|
||||
NULL, NULL);
|
||||
if (status)
|
||||
goto fail;
|
||||
|
||||
|
|
|
@ -364,7 +364,8 @@ static int obex_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
obex_hs_ep_out_desc.bEndpointAddress =
|
||||
obex_fs_ep_out_desc.bEndpointAddress;
|
||||
|
||||
status = usb_assign_descriptors(f, fs_function, hs_function, NULL);
|
||||
status = usb_assign_descriptors(f, fs_function, hs_function, NULL,
|
||||
NULL);
|
||||
if (status)
|
||||
goto fail;
|
||||
|
||||
|
|
|
@ -541,7 +541,7 @@ static int pn_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
|
||||
/* Do not try to bind Phonet twice... */
|
||||
status = usb_assign_descriptors(f, fs_pn_function, hs_pn_function,
|
||||
NULL);
|
||||
NULL, NULL);
|
||||
if (status)
|
||||
goto err;
|
||||
|
||||
|
|
|
@ -1051,7 +1051,7 @@ autoconf_fail:
|
|||
ss_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;
|
||||
|
||||
ret = usb_assign_descriptors(f, fs_printer_function,
|
||||
hs_printer_function, ss_printer_function);
|
||||
hs_printer_function, ss_printer_function, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -783,7 +783,7 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
ss_notify_desc.bEndpointAddress = fs_notify_desc.bEndpointAddress;
|
||||
|
||||
status = usb_assign_descriptors(f, eth_fs_function, eth_hs_function,
|
||||
eth_ss_function);
|
||||
eth_ss_function, NULL);
|
||||
if (status)
|
||||
goto fail;
|
||||
|
||||
|
|
|
@ -236,7 +236,7 @@ static int gser_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
gser_ss_out_desc.bEndpointAddress = gser_fs_out_desc.bEndpointAddress;
|
||||
|
||||
status = usb_assign_descriptors(f, gser_fs_function, gser_hs_function,
|
||||
gser_ss_function);
|
||||
gser_ss_function, NULL);
|
||||
if (status)
|
||||
goto fail;
|
||||
dev_dbg(&cdev->gadget->dev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n",
|
||||
|
|
|
@ -437,7 +437,7 @@ no_iso:
|
|||
ss_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress;
|
||||
|
||||
ret = usb_assign_descriptors(f, fs_source_sink_descs,
|
||||
hs_source_sink_descs, ss_source_sink_descs);
|
||||
hs_source_sink_descs, ss_source_sink_descs, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -362,7 +362,7 @@ geth_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
fs_subset_out_desc.bEndpointAddress;
|
||||
|
||||
status = usb_assign_descriptors(f, fs_eth_function, hs_eth_function,
|
||||
ss_eth_function);
|
||||
ss_eth_function, NULL);
|
||||
if (status)
|
||||
goto fail;
|
||||
|
||||
|
|
|
@ -2098,7 +2098,7 @@ static int tcm_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
uasp_fs_cmd_desc.bEndpointAddress = uasp_ss_cmd_desc.bEndpointAddress;
|
||||
|
||||
ret = usb_assign_descriptors(f, uasp_fs_function_desc,
|
||||
uasp_hs_function_desc, uasp_ss_function_desc);
|
||||
uasp_hs_function_desc, uasp_ss_function_desc, NULL);
|
||||
if (ret)
|
||||
goto ep_fail;
|
||||
|
||||
|
|
|
@ -721,7 +721,8 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
status = -ENOMEM;
|
||||
|
||||
/* copy descriptors, and track endpoint copies */
|
||||
status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, NULL);
|
||||
status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, NULL,
|
||||
NULL);
|
||||
if (status)
|
||||
goto fail;
|
||||
return 0;
|
||||
|
|
|
@ -1100,7 +1100,8 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
|
|||
hs_epout_desc.bEndpointAddress = fs_epout_desc.bEndpointAddress;
|
||||
hs_epin_desc.bEndpointAddress = fs_epin_desc.bEndpointAddress;
|
||||
|
||||
ret = usb_assign_descriptors(fn, fs_audio_desc, hs_audio_desc, NULL);
|
||||
ret = usb_assign_descriptors(fn, fs_audio_desc, hs_audio_desc, NULL,
|
||||
NULL);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
|
|
|
@ -914,7 +914,7 @@ struct rndis_params *rndis_register(void (*resp_avail)(void *v), void *v)
|
|||
params->media_state = RNDIS_MEDIA_STATE_DISCONNECTED;
|
||||
params->resp_avail = resp_avail;
|
||||
params->v = v;
|
||||
INIT_LIST_HEAD(&(params->resp_queue));
|
||||
INIT_LIST_HEAD(¶ms->resp_queue);
|
||||
pr_debug("%s: configNr = %d\n", __func__, i);
|
||||
|
||||
return params;
|
||||
|
@ -1006,13 +1006,10 @@ EXPORT_SYMBOL_GPL(rndis_add_hdr);
|
|||
|
||||
void rndis_free_response(struct rndis_params *params, u8 *buf)
|
||||
{
|
||||
rndis_resp_t *r;
|
||||
struct list_head *act, *tmp;
|
||||
rndis_resp_t *r, *n;
|
||||
|
||||
list_for_each_safe(act, tmp, &(params->resp_queue))
|
||||
{
|
||||
r = list_entry(act, rndis_resp_t, list);
|
||||
if (r && r->buf == buf) {
|
||||
list_for_each_entry_safe(r, n, ¶ms->resp_queue, list) {
|
||||
if (r->buf == buf) {
|
||||
list_del(&r->list);
|
||||
kfree(r);
|
||||
}
|
||||
|
@ -1022,14 +1019,11 @@ EXPORT_SYMBOL_GPL(rndis_free_response);
|
|||
|
||||
u8 *rndis_get_next_response(struct rndis_params *params, u32 *length)
|
||||
{
|
||||
rndis_resp_t *r;
|
||||
struct list_head *act, *tmp;
|
||||
rndis_resp_t *r, *n;
|
||||
|
||||
if (!length) return NULL;
|
||||
|
||||
list_for_each_safe(act, tmp, &(params->resp_queue))
|
||||
{
|
||||
r = list_entry(act, rndis_resp_t, list);
|
||||
list_for_each_entry_safe(r, n, ¶ms->resp_queue, list) {
|
||||
if (!r->send) {
|
||||
r->send = 1;
|
||||
*length = r->length;
|
||||
|
@ -1053,7 +1047,7 @@ static rndis_resp_t *rndis_add_response(struct rndis_params *params, u32 length)
|
|||
r->length = length;
|
||||
r->send = 0;
|
||||
|
||||
list_add_tail(&r->list, &(params->resp_queue));
|
||||
list_add_tail(&r->list, ¶ms->resp_queue);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
|
|
@ -1699,28 +1699,6 @@ static struct usb_gadget_driver gadgetfs_driver = {
|
|||
};
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
static void gadgetfs_nop(struct usb_gadget *arg) { }
|
||||
|
||||
static int gadgetfs_probe(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
CHIP = gadget->name;
|
||||
return -EISNAM;
|
||||
}
|
||||
|
||||
static struct usb_gadget_driver probe_driver = {
|
||||
.max_speed = USB_SPEED_HIGH,
|
||||
.bind = gadgetfs_probe,
|
||||
.unbind = gadgetfs_nop,
|
||||
.setup = (void *)gadgetfs_nop,
|
||||
.disconnect = gadgetfs_nop,
|
||||
.driver = {
|
||||
.name = "nop",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
/* DEVICE INITIALIZATION
|
||||
*
|
||||
* fd = open ("/dev/gadget/$CHIP", O_RDWR)
|
||||
|
@ -1971,9 +1949,7 @@ gadgetfs_fill_super (struct super_block *sb, void *opts, int silent)
|
|||
if (the_device)
|
||||
return -ESRCH;
|
||||
|
||||
/* fake probe to determine $CHIP */
|
||||
CHIP = NULL;
|
||||
usb_gadget_probe_driver(&probe_driver);
|
||||
CHIP = usb_get_gadget_udc_name();
|
||||
if (!CHIP)
|
||||
return -ENODEV;
|
||||
|
||||
|
@ -2034,6 +2010,8 @@ gadgetfs_kill_sb (struct super_block *sb)
|
|||
put_dev (the_device);
|
||||
the_device = NULL;
|
||||
}
|
||||
kfree(CHIP);
|
||||
CHIP = NULL;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
|
|
@ -74,7 +74,6 @@ config USB_BCM63XX_UDC
|
|||
config USB_FSL_USB2
|
||||
tristate "Freescale Highspeed USB DR Peripheral Controller"
|
||||
depends on FSL_SOC || ARCH_MXC
|
||||
select USB_FSL_MPH_DR_OF if OF
|
||||
help
|
||||
Some of Freescale PowerPC and i.MX processors have a High Speed
|
||||
Dual-Role(DR) USB controller, which supports device mode.
|
||||
|
@ -128,6 +127,7 @@ config USB_OMAP
|
|||
config USB_PXA25X
|
||||
tristate "PXA 25x or IXP 4xx"
|
||||
depends on (ARCH_PXA && PXA25x) || ARCH_IXP4XX
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Intel's PXA 25x series XScale ARM-5TE processors include
|
||||
an integrated full speed USB 1.1 device controller. The
|
||||
|
@ -176,7 +176,7 @@ config USB_RENESAS_USBHS_UDC
|
|||
|
||||
config USB_RENESAS_USB3
|
||||
tristate 'Renesas USB3.0 Peripheral controller'
|
||||
depends on ARCH_SHMOBILE || COMPILE_TEST
|
||||
depends on ARCH_RENESAS || COMPILE_TEST
|
||||
help
|
||||
Renesas USB3.0 Peripheral controller is a USB peripheral controller
|
||||
that supports super, high, and full speed USB 3.0 data transfers.
|
||||
|
@ -187,6 +187,7 @@ config USB_RENESAS_USB3
|
|||
|
||||
config USB_PXA27X
|
||||
tristate "PXA 27x"
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Intel's PXA 27x series XScale ARM v5TE processors include
|
||||
an integrated full speed USB 1.1 device controller.
|
||||
|
@ -244,6 +245,7 @@ config USB_MV_U3D
|
|||
|
||||
config USB_M66592
|
||||
tristate "Renesas M66592 USB Peripheral Controller"
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
M66592 is a discrete USB peripheral controller chip that
|
||||
supports both full and high speed USB 2.0 data transfers.
|
||||
|
@ -287,6 +289,7 @@ config USB_FSL_QE
|
|||
dynamically linked module called "fsl_qe_udc".
|
||||
|
||||
config USB_NET2272
|
||||
depends on HAS_IOMEM
|
||||
tristate "PLX NET2272"
|
||||
help
|
||||
PLX NET2272 is a USB peripheral controller which supports
|
||||
|
|
|
@ -581,8 +581,13 @@ err0:
|
|||
|
||||
void bdc_udc_exit(struct bdc *bdc)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
dev_dbg(bdc->dev, "%s()\n", __func__);
|
||||
spin_lock_irqsave(&bdc->lock, flags);
|
||||
bdc_ep_disable(bdc->bdc_ep_array[1]);
|
||||
spin_unlock_irqrestore(&bdc->lock, flags);
|
||||
|
||||
usb_del_gadget_udc(&bdc->gadget);
|
||||
bdc_free_ep(bdc);
|
||||
}
|
||||
|
|
|
@ -49,7 +49,6 @@
|
|||
#endif
|
||||
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/platform.h>
|
||||
|
||||
/*
|
||||
* USB device configuration structure
|
||||
|
@ -147,9 +146,7 @@ struct lpc32xx_udc {
|
|||
u32 io_p_size;
|
||||
void __iomem *udp_baseaddr;
|
||||
int udp_irq[4];
|
||||
struct clk *usb_pll_clk;
|
||||
struct clk *usb_slv_clk;
|
||||
struct clk *usb_otg_clk;
|
||||
|
||||
/* DMA support */
|
||||
u32 *udca_v_base;
|
||||
|
@ -210,16 +207,6 @@ static inline struct lpc32xx_udc *to_udc(struct usb_gadget *g)
|
|||
|
||||
#define UDCA_BUFF_SIZE (128)
|
||||
|
||||
/* TODO: When the clock framework is introduced in LPC32xx, IO_ADDRESS will
|
||||
* be replaced with an inremap()ed pointer
|
||||
* */
|
||||
#define USB_CTRL IO_ADDRESS(LPC32XX_CLK_PM_BASE + 0x64)
|
||||
|
||||
/* USB_CTRL bit defines */
|
||||
#define USB_SLAVE_HCLK_EN (1 << 24)
|
||||
#define USB_HOST_NEED_CLK_EN (1 << 21)
|
||||
#define USB_DEV_NEED_CLK_EN (1 << 22)
|
||||
|
||||
/**********************************************************************
|
||||
* USB device controller register offsets
|
||||
**********************************************************************/
|
||||
|
@ -639,9 +626,6 @@ static void isp1301_udc_configure(struct lpc32xx_udc *udc)
|
|||
i2c_smbus_write_byte_data(udc->isp1301_i2c_client,
|
||||
ISP1301_I2C_INTERRUPT_RISING, INT_VBUS_VLD);
|
||||
|
||||
/* Enable usb_need_clk clock after transceiver is initialized */
|
||||
writel((readl(USB_CTRL) | USB_DEV_NEED_CLK_EN), USB_CTRL);
|
||||
|
||||
dev_info(udc->dev, "ISP1301 Vendor ID : 0x%04x\n",
|
||||
i2c_smbus_read_word_data(udc->isp1301_i2c_client, 0x00));
|
||||
dev_info(udc->dev, "ISP1301 Product ID : 0x%04x\n",
|
||||
|
@ -980,31 +964,13 @@ static void udc_clk_set(struct lpc32xx_udc *udc, int enable)
|
|||
return;
|
||||
|
||||
udc->clocked = 1;
|
||||
|
||||
/* 48MHz PLL up */
|
||||
clk_enable(udc->usb_pll_clk);
|
||||
|
||||
/* Enable the USB device clock */
|
||||
writel(readl(USB_CTRL) | USB_DEV_NEED_CLK_EN,
|
||||
USB_CTRL);
|
||||
|
||||
clk_enable(udc->usb_otg_clk);
|
||||
clk_prepare_enable(udc->usb_slv_clk);
|
||||
} else {
|
||||
if (!udc->clocked)
|
||||
return;
|
||||
|
||||
udc->clocked = 0;
|
||||
|
||||
/* Never disable the USB_HCLK during normal operation */
|
||||
|
||||
/* 48MHz PLL dpwn */
|
||||
clk_disable(udc->usb_pll_clk);
|
||||
|
||||
/* Disable the USB device clock */
|
||||
writel(readl(USB_CTRL) & ~USB_DEV_NEED_CLK_EN,
|
||||
USB_CTRL);
|
||||
|
||||
clk_disable(udc->usb_otg_clk);
|
||||
clk_disable_unprepare(udc->usb_slv_clk);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3125,58 +3091,21 @@ static int lpc32xx_udc_probe(struct platform_device *pdev)
|
|||
goto io_map_fail;
|
||||
}
|
||||
|
||||
/* Enable AHB slave USB clock, needed for further USB clock control */
|
||||
writel(USB_SLAVE_HCLK_EN | (1 << 19), USB_CTRL);
|
||||
|
||||
/* Get required clocks */
|
||||
udc->usb_pll_clk = clk_get(&pdev->dev, "ck_pll5");
|
||||
if (IS_ERR(udc->usb_pll_clk)) {
|
||||
dev_err(udc->dev, "failed to acquire USB PLL\n");
|
||||
retval = PTR_ERR(udc->usb_pll_clk);
|
||||
goto pll_get_fail;
|
||||
}
|
||||
udc->usb_slv_clk = clk_get(&pdev->dev, "ck_usbd");
|
||||
/* Get USB device clock */
|
||||
udc->usb_slv_clk = clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(udc->usb_slv_clk)) {
|
||||
dev_err(udc->dev, "failed to acquire USB device clock\n");
|
||||
retval = PTR_ERR(udc->usb_slv_clk);
|
||||
goto usb_clk_get_fail;
|
||||
}
|
||||
udc->usb_otg_clk = clk_get(&pdev->dev, "ck_usb_otg");
|
||||
if (IS_ERR(udc->usb_otg_clk)) {
|
||||
dev_err(udc->dev, "failed to acquire USB otg clock\n");
|
||||
retval = PTR_ERR(udc->usb_otg_clk);
|
||||
goto usb_otg_clk_get_fail;
|
||||
}
|
||||
|
||||
/* Setup PLL clock to 48MHz */
|
||||
retval = clk_enable(udc->usb_pll_clk);
|
||||
if (retval < 0) {
|
||||
dev_err(udc->dev, "failed to start USB PLL\n");
|
||||
goto pll_enable_fail;
|
||||
}
|
||||
|
||||
retval = clk_set_rate(udc->usb_pll_clk, 48000);
|
||||
if (retval < 0) {
|
||||
dev_err(udc->dev, "failed to set USB clock rate\n");
|
||||
goto pll_set_fail;
|
||||
}
|
||||
|
||||
writel(readl(USB_CTRL) | USB_DEV_NEED_CLK_EN, USB_CTRL);
|
||||
|
||||
/* Enable USB device clock */
|
||||
retval = clk_enable(udc->usb_slv_clk);
|
||||
retval = clk_prepare_enable(udc->usb_slv_clk);
|
||||
if (retval < 0) {
|
||||
dev_err(udc->dev, "failed to start USB device clock\n");
|
||||
goto usb_clk_enable_fail;
|
||||
}
|
||||
|
||||
/* Enable USB OTG clock */
|
||||
retval = clk_enable(udc->usb_otg_clk);
|
||||
if (retval < 0) {
|
||||
dev_err(udc->dev, "failed to start USB otg clock\n");
|
||||
goto usb_otg_clk_enable_fail;
|
||||
}
|
||||
|
||||
/* Setup deferred workqueue data */
|
||||
udc->poweron = udc->pullup = 0;
|
||||
INIT_WORK(&udc->pullup_job, pullup_work);
|
||||
|
@ -3287,19 +3216,10 @@ dma_alloc_fail:
|
|||
dma_free_coherent(&pdev->dev, UDCA_BUFF_SIZE,
|
||||
udc->udca_v_base, udc->udca_p_base);
|
||||
i2c_fail:
|
||||
clk_disable(udc->usb_otg_clk);
|
||||
usb_otg_clk_enable_fail:
|
||||
clk_disable(udc->usb_slv_clk);
|
||||
clk_disable_unprepare(udc->usb_slv_clk);
|
||||
usb_clk_enable_fail:
|
||||
pll_set_fail:
|
||||
clk_disable(udc->usb_pll_clk);
|
||||
pll_enable_fail:
|
||||
clk_put(udc->usb_otg_clk);
|
||||
usb_otg_clk_get_fail:
|
||||
clk_put(udc->usb_slv_clk);
|
||||
usb_clk_get_fail:
|
||||
clk_put(udc->usb_pll_clk);
|
||||
pll_get_fail:
|
||||
iounmap(udc->udp_baseaddr);
|
||||
io_map_fail:
|
||||
release_mem_region(udc->io_p_start, udc->io_p_size);
|
||||
|
@ -3336,12 +3256,9 @@ static int lpc32xx_udc_remove(struct platform_device *pdev)
|
|||
free_irq(udc->udp_irq[IRQ_USB_HP], udc);
|
||||
free_irq(udc->udp_irq[IRQ_USB_LP], udc);
|
||||
|
||||
clk_disable(udc->usb_otg_clk);
|
||||
clk_put(udc->usb_otg_clk);
|
||||
clk_disable(udc->usb_slv_clk);
|
||||
clk_disable_unprepare(udc->usb_slv_clk);
|
||||
clk_put(udc->usb_slv_clk);
|
||||
clk_disable(udc->usb_pll_clk);
|
||||
clk_put(udc->usb_pll_clk);
|
||||
|
||||
iounmap(udc->udp_baseaddr);
|
||||
release_mem_region(udc->io_p_start, udc->io_p_size);
|
||||
kfree(udc);
|
||||
|
@ -3367,7 +3284,7 @@ static int lpc32xx_udc_suspend(struct platform_device *pdev, pm_message_t mesg)
|
|||
udc->clocked = 1;
|
||||
|
||||
/* Kill global USB clock */
|
||||
clk_disable(udc->usb_slv_clk);
|
||||
clk_disable_unprepare(udc->usb_slv_clk);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -3379,7 +3296,7 @@ static int lpc32xx_udc_resume(struct platform_device *pdev)
|
|||
|
||||
if (udc->clocked) {
|
||||
/* Enable global USB clock */
|
||||
clk_enable(udc->usb_slv_clk);
|
||||
clk_prepare_enable(udc->usb_slv_clk);
|
||||
|
||||
/* Enable clocking */
|
||||
udc_clk_set(udc, 1);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -56,9 +56,9 @@ struct pxa25x_ep {
|
|||
* UDDR = UDC Endpoint Data Register (the fifo)
|
||||
* DRCM = DMA Request Channel Map
|
||||
*/
|
||||
volatile u32 *reg_udccs;
|
||||
volatile u32 *reg_ubcr;
|
||||
volatile u32 *reg_uddr;
|
||||
u32 regoff_udccs;
|
||||
u32 regoff_ubcr;
|
||||
u32 regoff_uddr;
|
||||
};
|
||||
|
||||
struct pxa25x_request {
|
||||
|
@ -125,6 +125,7 @@ struct pxa25x_udc {
|
|||
#ifdef CONFIG_USB_GADGET_DEBUG_FS
|
||||
struct dentry *debugfs_udc;
|
||||
#endif
|
||||
void __iomem *regs;
|
||||
};
|
||||
#define to_pxa25x(g) (container_of((g), struct pxa25x_udc, gadget))
|
||||
|
||||
|
@ -197,6 +198,8 @@ dump_udccs0(const char *label)
|
|||
(udccs0 & UDCCS0_OPR) ? " opr" : "");
|
||||
}
|
||||
|
||||
static inline u32 udc_ep_get_UDCCS(struct pxa25x_ep *);
|
||||
|
||||
static void __maybe_unused
|
||||
dump_state(struct pxa25x_udc *dev)
|
||||
{
|
||||
|
@ -228,7 +231,7 @@ dump_state(struct pxa25x_udc *dev)
|
|||
for (i = 1; i < PXA_UDC_NUM_ENDPOINTS; i++) {
|
||||
if (dev->ep[i].ep.desc == NULL)
|
||||
continue;
|
||||
DMSG ("udccs%d = %02x\n", i, *dev->ep->reg_udccs);
|
||||
DMSG ("udccs%d = %02x\n", i, udc_ep_get_UDCCS(&dev->ep[i]));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -442,6 +442,36 @@ err1:
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release);
|
||||
|
||||
/**
|
||||
* usb_get_gadget_udc_name - get the name of the first UDC controller
|
||||
* This functions returns the name of the first UDC controller in the system.
|
||||
* Please note that this interface is usefull only for legacy drivers which
|
||||
* assume that there is only one UDC controller in the system and they need to
|
||||
* get its name before initialization. There is no guarantee that the UDC
|
||||
* of the returned name will be still available, when gadget driver registers
|
||||
* itself.
|
||||
*
|
||||
* Returns pointer to string with UDC controller name on success, NULL
|
||||
* otherwise. Caller should kfree() returned string.
|
||||
*/
|
||||
char *usb_get_gadget_udc_name(void)
|
||||
{
|
||||
struct usb_udc *udc;
|
||||
char *name = NULL;
|
||||
|
||||
/* For now we take the first available UDC */
|
||||
mutex_lock(&udc_lock);
|
||||
list_for_each_entry(udc, &udc_list, list) {
|
||||
if (!udc->driver) {
|
||||
name = kstrdup(udc->gadget->name, GFP_KERNEL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&udc_lock);
|
||||
return name;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_get_gadget_udc_name);
|
||||
|
||||
/**
|
||||
* usb_add_gadget_udc - adds a new gadget to the udc class driver list
|
||||
* @parent: the parent device to this udc. Usually the controller
|
||||
|
|
|
@ -5,6 +5,7 @@ comment "USB Host Controller Drivers"
|
|||
|
||||
config USB_C67X00_HCD
|
||||
tristate "Cypress C67x00 HCD support"
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
The Cypress C67x00 (EZ-Host/EZ-OTG) chips are dual-role
|
||||
host/peripheral/OTG USB controllers.
|
||||
|
@ -17,6 +18,7 @@ config USB_C67X00_HCD
|
|||
|
||||
config USB_XHCI_HCD
|
||||
tristate "xHCI HCD (USB 3.0) support"
|
||||
depends on HAS_DMA && HAS_IOMEM
|
||||
---help---
|
||||
The eXtensible Host Controller Interface (xHCI) is standard for USB 3.0
|
||||
"SuperSpeed" host controller hardware.
|
||||
|
@ -53,6 +55,7 @@ config USB_XHCI_MTK
|
|||
config USB_XHCI_MVEBU
|
||||
tristate "xHCI support for Marvell Armada 375/38x"
|
||||
select USB_XHCI_PLATFORM
|
||||
depends on HAS_IOMEM
|
||||
depends on ARCH_MVEBU || COMPILE_TEST
|
||||
---help---
|
||||
Say 'Y' to enable the support for the xHCI host controller
|
||||
|
@ -61,7 +64,7 @@ config USB_XHCI_MVEBU
|
|||
config USB_XHCI_RCAR
|
||||
tristate "xHCI support for Renesas R-Car SoCs"
|
||||
select USB_XHCI_PLATFORM
|
||||
depends on ARCH_SHMOBILE || COMPILE_TEST
|
||||
depends on ARCH_RENESAS || COMPILE_TEST
|
||||
---help---
|
||||
Say 'Y' to enable the support for the xHCI host controller
|
||||
found in Renesas R-Car ARM SoCs.
|
||||
|
@ -70,6 +73,7 @@ endif # USB_XHCI_HCD
|
|||
|
||||
config USB_EHCI_HCD
|
||||
tristate "EHCI HCD (USB 2.0) support"
|
||||
depends on HAS_DMA && HAS_IOMEM
|
||||
---help---
|
||||
The Enhanced Host Controller Interface (EHCI) is standard for USB 2.0
|
||||
"high speed" (480 Mbit/sec, 60 Mbyte/sec) host controller hardware.
|
||||
|
@ -121,9 +125,6 @@ config USB_EHCI_TT_NEWSCHED
|
|||
|
||||
If unsure, say Y.
|
||||
|
||||
config USB_FSL_MPH_DR_OF
|
||||
tristate
|
||||
|
||||
if USB_EHCI_HCD
|
||||
|
||||
config USB_EHCI_PCI
|
||||
|
@ -156,7 +157,6 @@ config USB_EHCI_FSL
|
|||
tristate "Support for Freescale PPC on-chip EHCI USB controller"
|
||||
depends on FSL_SOC
|
||||
select USB_EHCI_ROOT_HUB_TT
|
||||
select USB_FSL_MPH_DR_OF if OF
|
||||
---help---
|
||||
Variation of ARC USB block used in some Freescale chips.
|
||||
|
||||
|
@ -328,6 +328,7 @@ endif # USB_EHCI_HCD
|
|||
|
||||
config USB_OXU210HP_HCD
|
||||
tristate "OXU210HP HCD support"
|
||||
depends on HAS_IOMEM
|
||||
---help---
|
||||
The OXU210HP is an USB host/OTG/device controller. Enable this
|
||||
option if your board has this chip. If unsure, say N.
|
||||
|
@ -340,6 +341,7 @@ config USB_OXU210HP_HCD
|
|||
|
||||
config USB_ISP116X_HCD
|
||||
tristate "ISP116X HCD support"
|
||||
depends on HAS_IOMEM
|
||||
---help---
|
||||
The ISP1160 and ISP1161 chips are USB host controllers. Enable this
|
||||
option if your board has this chip. If unsure, say N.
|
||||
|
@ -351,6 +353,7 @@ config USB_ISP116X_HCD
|
|||
|
||||
config USB_ISP1362_HCD
|
||||
tristate "ISP1362 HCD support"
|
||||
depends on HAS_IOMEM
|
||||
---help---
|
||||
Supports the Philips ISP1362 chip as a host controller
|
||||
|
||||
|
@ -361,7 +364,7 @@ config USB_ISP1362_HCD
|
|||
|
||||
config USB_FOTG210_HCD
|
||||
tristate "FOTG210 HCD support"
|
||||
depends on USB
|
||||
depends on USB && HAS_DMA && HAS_IOMEM
|
||||
---help---
|
||||
Faraday FOTG210 is an OTG controller which can be configured as
|
||||
an USB2.0 host. It is designed to meet USB2.0 EHCI specification
|
||||
|
@ -383,6 +386,7 @@ config USB_MAX3421_HCD
|
|||
|
||||
config USB_OHCI_HCD
|
||||
tristate "OHCI HCD (USB 1.1) support"
|
||||
depends on HAS_DMA && HAS_IOMEM
|
||||
---help---
|
||||
The Open Host Controller Interface (OHCI) is a standard for accessing
|
||||
USB 1.1 host controller hardware. It does more in hardware than Intel's
|
||||
|
@ -668,6 +672,7 @@ config USB_U132_HCD
|
|||
|
||||
config USB_SL811_HCD
|
||||
tristate "SL811HS HCD support"
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
The SL811HS is a single-port USB controller that supports either
|
||||
host side or peripheral side roles. Enable this option if your
|
||||
|
@ -699,6 +704,7 @@ config USB_SL811_CS
|
|||
|
||||
config USB_R8A66597_HCD
|
||||
tristate "R8A66597 HCD support"
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
The R8A66597 is a USB 2.0 host and peripheral controller.
|
||||
|
||||
|
|
|
@ -74,7 +74,8 @@ obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o
|
|||
obj-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.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
|
||||
obj-$(CONFIG_USB_FSL_USB2) += fsl-mph-dr-of.o
|
||||
obj-$(CONFIG_USB_EHCI_FSL) += fsl-mph-dr-of.o
|
||||
obj-$(CONFIG_USB_EHCI_FSL) += ehci-fsl.o
|
||||
obj-$(CONFIG_USB_HCD_BCMA) += bcma-hcd.o
|
||||
obj-$(CONFIG_USB_HCD_SSB) += ssb-hcd.o
|
||||
|
|
|
@ -35,6 +35,7 @@ MODULE_DESCRIPTION("Common USB driver for BCMA Bus");
|
|||
MODULE_LICENSE("GPL");
|
||||
|
||||
struct bcma_hcd_device {
|
||||
struct bcma_device *core;
|
||||
struct platform_device *ehci_dev;
|
||||
struct platform_device *ohci_dev;
|
||||
struct gpio_desc *gpio_desc;
|
||||
|
@ -244,7 +245,10 @@ static const struct usb_ehci_pdata ehci_pdata = {
|
|||
static const struct usb_ohci_pdata ohci_pdata = {
|
||||
};
|
||||
|
||||
static struct platform_device *bcma_hcd_create_pdev(struct bcma_device *dev, bool ohci, u32 addr)
|
||||
static struct platform_device *bcma_hcd_create_pdev(struct bcma_device *dev,
|
||||
const char *name, u32 addr,
|
||||
const void *data,
|
||||
size_t size)
|
||||
{
|
||||
struct platform_device *hci_dev;
|
||||
struct resource hci_res[2];
|
||||
|
@ -259,8 +263,7 @@ static struct platform_device *bcma_hcd_create_pdev(struct bcma_device *dev, boo
|
|||
hci_res[1].start = dev->irq;
|
||||
hci_res[1].flags = IORESOURCE_IRQ;
|
||||
|
||||
hci_dev = platform_device_alloc(ohci ? "ohci-platform" :
|
||||
"ehci-platform" , 0);
|
||||
hci_dev = platform_device_alloc(name, 0);
|
||||
if (!hci_dev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
|
@ -271,12 +274,8 @@ static struct platform_device *bcma_hcd_create_pdev(struct bcma_device *dev, boo
|
|||
ARRAY_SIZE(hci_res));
|
||||
if (ret)
|
||||
goto err_alloc;
|
||||
if (ohci)
|
||||
ret = platform_device_add_data(hci_dev, &ohci_pdata,
|
||||
sizeof(ohci_pdata));
|
||||
else
|
||||
ret = platform_device_add_data(hci_dev, &ehci_pdata,
|
||||
sizeof(ehci_pdata));
|
||||
if (data)
|
||||
ret = platform_device_add_data(hci_dev, data, size);
|
||||
if (ret)
|
||||
goto err_alloc;
|
||||
ret = platform_device_add(hci_dev);
|
||||
|
@ -290,31 +289,16 @@ err_alloc:
|
|||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static int bcma_hcd_probe(struct bcma_device *dev)
|
||||
static int bcma_hcd_usb20_init(struct bcma_hcd_device *usb_dev)
|
||||
{
|
||||
int err;
|
||||
struct bcma_device *dev = usb_dev->core;
|
||||
struct bcma_chipinfo *chipinfo = &dev->bus->chipinfo;
|
||||
u32 ohci_addr;
|
||||
struct bcma_hcd_device *usb_dev;
|
||||
struct bcma_chipinfo *chipinfo;
|
||||
|
||||
chipinfo = &dev->bus->chipinfo;
|
||||
|
||||
/* TODO: Probably need checks here; is the core connected? */
|
||||
int err;
|
||||
|
||||
if (dma_set_mask_and_coherent(dev->dma_dev, DMA_BIT_MASK(32)))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
usb_dev = devm_kzalloc(&dev->dev, sizeof(struct bcma_hcd_device),
|
||||
GFP_KERNEL);
|
||||
if (!usb_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
if (dev->dev.of_node)
|
||||
usb_dev->gpio_desc = devm_get_gpiod_from_child(&dev->dev, "vcc",
|
||||
&dev->dev.of_node->fwnode);
|
||||
if (!IS_ERR_OR_NULL(usb_dev->gpio_desc))
|
||||
gpiod_direction_output(usb_dev->gpio_desc, 1);
|
||||
|
||||
switch (dev->id.id) {
|
||||
case BCMA_CORE_NS_USB20:
|
||||
bcma_hcd_init_chip_arm(dev);
|
||||
|
@ -333,17 +317,20 @@ static int bcma_hcd_probe(struct bcma_device *dev)
|
|||
&& chipinfo->rev == 0)
|
||||
ohci_addr = 0x18009000;
|
||||
|
||||
usb_dev->ohci_dev = bcma_hcd_create_pdev(dev, true, ohci_addr);
|
||||
usb_dev->ohci_dev = bcma_hcd_create_pdev(dev, "ohci-platform",
|
||||
ohci_addr, &ohci_pdata,
|
||||
sizeof(ohci_pdata));
|
||||
if (IS_ERR(usb_dev->ohci_dev))
|
||||
return PTR_ERR(usb_dev->ohci_dev);
|
||||
|
||||
usb_dev->ehci_dev = bcma_hcd_create_pdev(dev, false, dev->addr);
|
||||
usb_dev->ehci_dev = bcma_hcd_create_pdev(dev, "ehci-platform",
|
||||
dev->addr, &ehci_pdata,
|
||||
sizeof(ehci_pdata));
|
||||
if (IS_ERR(usb_dev->ehci_dev)) {
|
||||
err = PTR_ERR(usb_dev->ehci_dev);
|
||||
goto err_unregister_ohci_dev;
|
||||
}
|
||||
|
||||
bcma_set_drvdata(dev, usb_dev);
|
||||
return 0;
|
||||
|
||||
err_unregister_ohci_dev:
|
||||
|
@ -351,6 +338,40 @@ err_unregister_ohci_dev:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int bcma_hcd_probe(struct bcma_device *core)
|
||||
{
|
||||
int err;
|
||||
struct bcma_hcd_device *usb_dev;
|
||||
|
||||
/* TODO: Probably need checks here; is the core connected? */
|
||||
|
||||
usb_dev = devm_kzalloc(&core->dev, sizeof(struct bcma_hcd_device),
|
||||
GFP_KERNEL);
|
||||
if (!usb_dev)
|
||||
return -ENOMEM;
|
||||
usb_dev->core = core;
|
||||
|
||||
if (core->dev.of_node)
|
||||
usb_dev->gpio_desc = devm_get_gpiod_from_child(&core->dev, "vcc",
|
||||
&core->dev.of_node->fwnode);
|
||||
if (!IS_ERR_OR_NULL(usb_dev->gpio_desc))
|
||||
gpiod_direction_output(usb_dev->gpio_desc, 1);
|
||||
|
||||
switch (core->id.id) {
|
||||
case BCMA_CORE_USB20_HOST:
|
||||
case BCMA_CORE_NS_USB20:
|
||||
err = bcma_hcd_usb20_init(usb_dev);
|
||||
if (err)
|
||||
return err;
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
bcma_set_drvdata(core, usb_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bcma_hcd_remove(struct bcma_device *dev)
|
||||
{
|
||||
struct bcma_hcd_device *usb_dev = bcma_get_drvdata(dev);
|
||||
|
|
|
@ -185,8 +185,7 @@ static int ehci_atmel_drv_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int ehci_atmel_drv_suspend(struct device *dev)
|
||||
static int __maybe_unused ehci_atmel_drv_suspend(struct device *dev)
|
||||
{
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
struct atmel_ehci_priv *atmel_ehci = hcd_to_atmel_ehci_priv(hcd);
|
||||
|
@ -200,7 +199,7 @@ static int ehci_atmel_drv_suspend(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ehci_atmel_drv_resume(struct device *dev)
|
||||
static int __maybe_unused ehci_atmel_drv_resume(struct device *dev)
|
||||
{
|
||||
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
||||
struct atmel_ehci_priv *atmel_ehci = hcd_to_atmel_ehci_priv(hcd);
|
||||
|
@ -208,7 +207,6 @@ static int ehci_atmel_drv_resume(struct device *dev)
|
|||
atmel_start_clock(atmel_ehci);
|
||||
return ehci_resume(hcd, false);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id atmel_ehci_dt_ids[] = {
|
||||
|
|
|
@ -11,76 +11,73 @@
|
|||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/* this file is part of ehci-hcd.c */
|
||||
|
||||
#ifdef CONFIG_DYNAMIC_DEBUG
|
||||
|
||||
/* check the values in the HCSPARAMS register
|
||||
/*
|
||||
* check the values in the HCSPARAMS register
|
||||
* (host controller _Structural_ parameters)
|
||||
* see EHCI spec, Table 2-4 for each value
|
||||
*/
|
||||
static void dbg_hcs_params (struct ehci_hcd *ehci, char *label)
|
||||
static void dbg_hcs_params(struct ehci_hcd *ehci, char *label)
|
||||
{
|
||||
u32 params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
||||
|
||||
ehci_dbg (ehci,
|
||||
ehci_dbg(ehci,
|
||||
"%s hcs_params 0x%x dbg=%d%s cc=%d pcc=%d%s%s ports=%d\n",
|
||||
label, params,
|
||||
HCS_DEBUG_PORT (params),
|
||||
HCS_INDICATOR (params) ? " ind" : "",
|
||||
HCS_N_CC (params),
|
||||
HCS_N_PCC (params),
|
||||
HCS_PORTROUTED (params) ? "" : " ordered",
|
||||
HCS_PPC (params) ? "" : " !ppc",
|
||||
HCS_N_PORTS (params)
|
||||
);
|
||||
HCS_DEBUG_PORT(params),
|
||||
HCS_INDICATOR(params) ? " ind" : "",
|
||||
HCS_N_CC(params),
|
||||
HCS_N_PCC(params),
|
||||
HCS_PORTROUTED(params) ? "" : " ordered",
|
||||
HCS_PPC(params) ? "" : " !ppc",
|
||||
HCS_N_PORTS(params));
|
||||
/* Port routing, per EHCI 0.95 Spec, Section 2.2.5 */
|
||||
if (HCS_PORTROUTED (params)) {
|
||||
if (HCS_PORTROUTED(params)) {
|
||||
int i;
|
||||
char buf [46], tmp [7], byte;
|
||||
char buf[46], tmp[7], byte;
|
||||
|
||||
buf[0] = 0;
|
||||
for (i = 0; i < HCS_N_PORTS (params); i++) {
|
||||
// FIXME MIPS won't readb() ...
|
||||
byte = readb (&ehci->caps->portroute[(i>>1)]);
|
||||
for (i = 0; i < HCS_N_PORTS(params); i++) {
|
||||
/* FIXME MIPS won't readb() ... */
|
||||
byte = readb(&ehci->caps->portroute[(i >> 1)]);
|
||||
sprintf(tmp, "%d ",
|
||||
((i & 0x1) ? ((byte)&0xf) : ((byte>>4)&0xf)));
|
||||
(i & 0x1) ? byte & 0xf : (byte >> 4) & 0xf);
|
||||
strcat(buf, tmp);
|
||||
}
|
||||
ehci_dbg (ehci, "%s portroute %s\n",
|
||||
label, buf);
|
||||
ehci_dbg(ehci, "%s portroute %s\n", label, buf);
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
static inline void dbg_hcs_params (struct ehci_hcd *ehci, char *label) {}
|
||||
static inline void dbg_hcs_params(struct ehci_hcd *ehci, char *label) {}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DYNAMIC_DEBUG
|
||||
|
||||
/* check the values in the HCCPARAMS register
|
||||
/*
|
||||
* check the values in the HCCPARAMS register
|
||||
* (host controller _Capability_ parameters)
|
||||
* see EHCI Spec, Table 2-5 for each value
|
||||
* */
|
||||
static void dbg_hcc_params (struct ehci_hcd *ehci, char *label)
|
||||
*/
|
||||
static void dbg_hcc_params(struct ehci_hcd *ehci, char *label)
|
||||
{
|
||||
u32 params = ehci_readl(ehci, &ehci->caps->hcc_params);
|
||||
|
||||
if (HCC_ISOC_CACHE (params)) {
|
||||
ehci_dbg (ehci,
|
||||
if (HCC_ISOC_CACHE(params)) {
|
||||
ehci_dbg(ehci,
|
||||
"%s hcc_params %04x caching frame %s%s%s\n",
|
||||
label, params,
|
||||
HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024",
|
||||
HCC_CANPARK(params) ? " park" : "",
|
||||
HCC_64BIT_ADDR(params) ? " 64 bit addr" : "");
|
||||
} else {
|
||||
ehci_dbg (ehci,
|
||||
ehci_dbg(ehci,
|
||||
"%s hcc_params %04x thresh %d uframes %s%s%s%s%s%s%s\n",
|
||||
label,
|
||||
params,
|
||||
|
@ -97,21 +94,21 @@ static void dbg_hcc_params (struct ehci_hcd *ehci, char *label)
|
|||
}
|
||||
#else
|
||||
|
||||
static inline void dbg_hcc_params (struct ehci_hcd *ehci, char *label) {}
|
||||
static inline void dbg_hcc_params(struct ehci_hcd *ehci, char *label) {}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DYNAMIC_DEBUG
|
||||
|
||||
static void __maybe_unused
|
||||
dbg_qtd (const char *label, struct ehci_hcd *ehci, struct ehci_qtd *qtd)
|
||||
dbg_qtd(const char *label, struct ehci_hcd *ehci, struct ehci_qtd *qtd)
|
||||
{
|
||||
ehci_dbg(ehci, "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd,
|
||||
hc32_to_cpup(ehci, &qtd->hw_next),
|
||||
hc32_to_cpup(ehci, &qtd->hw_alt_next),
|
||||
hc32_to_cpup(ehci, &qtd->hw_token),
|
||||
hc32_to_cpup(ehci, &qtd->hw_buf [0]));
|
||||
if (qtd->hw_buf [1])
|
||||
hc32_to_cpup(ehci, &qtd->hw_buf[0]));
|
||||
if (qtd->hw_buf[1])
|
||||
ehci_dbg(ehci, " p1=%08x p2=%08x p3=%08x p4=%08x\n",
|
||||
hc32_to_cpup(ehci, &qtd->hw_buf[1]),
|
||||
hc32_to_cpup(ehci, &qtd->hw_buf[2]),
|
||||
|
@ -120,22 +117,22 @@ dbg_qtd (const char *label, struct ehci_hcd *ehci, struct ehci_qtd *qtd)
|
|||
}
|
||||
|
||||
static void __maybe_unused
|
||||
dbg_qh (const char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
dbg_qh(const char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
{
|
||||
struct ehci_qh_hw *hw = qh->hw;
|
||||
|
||||
ehci_dbg (ehci, "%s qh %p n%08x info %x %x qtd %x\n", label,
|
||||
ehci_dbg(ehci, "%s qh %p n%08x info %x %x qtd %x\n", label,
|
||||
qh, hw->hw_next, hw->hw_info1, hw->hw_info2, hw->hw_current);
|
||||
dbg_qtd("overlay", ehci, (struct ehci_qtd *) &hw->hw_qtd_next);
|
||||
}
|
||||
|
||||
static void __maybe_unused
|
||||
dbg_itd (const char *label, struct ehci_hcd *ehci, struct ehci_itd *itd)
|
||||
dbg_itd(const char *label, struct ehci_hcd *ehci, struct ehci_itd *itd)
|
||||
{
|
||||
ehci_dbg (ehci, "%s [%d] itd %p, next %08x, urb %p\n",
|
||||
ehci_dbg(ehci, "%s [%d] itd %p, next %08x, urb %p\n",
|
||||
label, itd->frame, itd, hc32_to_cpu(ehci, itd->hw_next),
|
||||
itd->urb);
|
||||
ehci_dbg (ehci,
|
||||
ehci_dbg(ehci,
|
||||
" trans: %08x %08x %08x %08x %08x %08x %08x %08x\n",
|
||||
hc32_to_cpu(ehci, itd->hw_transaction[0]),
|
||||
hc32_to_cpu(ehci, itd->hw_transaction[1]),
|
||||
|
@ -145,7 +142,7 @@ dbg_itd (const char *label, struct ehci_hcd *ehci, struct ehci_itd *itd)
|
|||
hc32_to_cpu(ehci, itd->hw_transaction[5]),
|
||||
hc32_to_cpu(ehci, itd->hw_transaction[6]),
|
||||
hc32_to_cpu(ehci, itd->hw_transaction[7]));
|
||||
ehci_dbg (ehci,
|
||||
ehci_dbg(ehci,
|
||||
" buf: %08x %08x %08x %08x %08x %08x %08x\n",
|
||||
hc32_to_cpu(ehci, itd->hw_bufp[0]),
|
||||
hc32_to_cpu(ehci, itd->hw_bufp[1]),
|
||||
|
@ -154,19 +151,19 @@ dbg_itd (const char *label, struct ehci_hcd *ehci, struct ehci_itd *itd)
|
|||
hc32_to_cpu(ehci, itd->hw_bufp[4]),
|
||||
hc32_to_cpu(ehci, itd->hw_bufp[5]),
|
||||
hc32_to_cpu(ehci, itd->hw_bufp[6]));
|
||||
ehci_dbg (ehci, " index: %d %d %d %d %d %d %d %d\n",
|
||||
ehci_dbg(ehci, " index: %d %d %d %d %d %d %d %d\n",
|
||||
itd->index[0], itd->index[1], itd->index[2],
|
||||
itd->index[3], itd->index[4], itd->index[5],
|
||||
itd->index[6], itd->index[7]);
|
||||
}
|
||||
|
||||
static void __maybe_unused
|
||||
dbg_sitd (const char *label, struct ehci_hcd *ehci, struct ehci_sitd *sitd)
|
||||
dbg_sitd(const char *label, struct ehci_hcd *ehci, struct ehci_sitd *sitd)
|
||||
{
|
||||
ehci_dbg (ehci, "%s [%d] sitd %p, next %08x, urb %p\n",
|
||||
ehci_dbg(ehci, "%s [%d] sitd %p, next %08x, urb %p\n",
|
||||
label, sitd->frame, sitd, hc32_to_cpu(ehci, sitd->hw_next),
|
||||
sitd->urb);
|
||||
ehci_dbg (ehci,
|
||||
ehci_dbg(ehci,
|
||||
" addr %08x sched %04x result %08x buf %08x %08x\n",
|
||||
hc32_to_cpu(ehci, sitd->hw_fullspeed_ep),
|
||||
hc32_to_cpu(ehci, sitd->hw_uframe),
|
||||
|
@ -176,11 +173,11 @@ dbg_sitd (const char *label, struct ehci_hcd *ehci, struct ehci_sitd *sitd)
|
|||
}
|
||||
|
||||
static int __maybe_unused
|
||||
dbg_status_buf (char *buf, unsigned len, const char *label, u32 status)
|
||||
dbg_status_buf(char *buf, unsigned len, const char *label, u32 status)
|
||||
{
|
||||
return scnprintf (buf, len,
|
||||
return scnprintf(buf, len,
|
||||
"%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s%s",
|
||||
label, label [0] ? " " : "", status,
|
||||
label, label[0] ? " " : "", status,
|
||||
(status & STS_PPCE_MASK) ? " PPCE" : "",
|
||||
(status & STS_ASS) ? " Async" : "",
|
||||
(status & STS_PSS) ? " Periodic" : "",
|
||||
|
@ -191,79 +188,83 @@ dbg_status_buf (char *buf, unsigned len, const char *label, u32 status)
|
|||
(status & STS_FLR) ? " FLR" : "",
|
||||
(status & STS_PCD) ? " PCD" : "",
|
||||
(status & STS_ERR) ? " ERR" : "",
|
||||
(status & STS_INT) ? " INT" : ""
|
||||
);
|
||||
(status & STS_INT) ? " INT" : "");
|
||||
}
|
||||
|
||||
static int __maybe_unused
|
||||
dbg_intr_buf (char *buf, unsigned len, const char *label, u32 enable)
|
||||
dbg_intr_buf(char *buf, unsigned len, const char *label, u32 enable)
|
||||
{
|
||||
return scnprintf (buf, len,
|
||||
return scnprintf(buf, len,
|
||||
"%s%sintrenable %02x%s%s%s%s%s%s%s",
|
||||
label, label [0] ? " " : "", enable,
|
||||
label, label[0] ? " " : "", enable,
|
||||
(enable & STS_PPCE_MASK) ? " PPCE" : "",
|
||||
(enable & STS_IAA) ? " IAA" : "",
|
||||
(enable & STS_FATAL) ? " FATAL" : "",
|
||||
(enable & STS_FLR) ? " FLR" : "",
|
||||
(enable & STS_PCD) ? " PCD" : "",
|
||||
(enable & STS_ERR) ? " ERR" : "",
|
||||
(enable & STS_INT) ? " INT" : ""
|
||||
);
|
||||
(enable & STS_INT) ? " INT" : "");
|
||||
}
|
||||
|
||||
static const char *const fls_strings [] =
|
||||
{ "1024", "512", "256", "??" };
|
||||
static const char *const fls_strings[] = { "1024", "512", "256", "??" };
|
||||
|
||||
static int
|
||||
dbg_command_buf (char *buf, unsigned len, const char *label, u32 command)
|
||||
dbg_command_buf(char *buf, unsigned len, const char *label, u32 command)
|
||||
{
|
||||
return scnprintf (buf, len,
|
||||
return scnprintf(buf, len,
|
||||
"%s%scommand %07x %s%s%s%s%s%s=%d ithresh=%d%s%s%s%s "
|
||||
"period=%s%s %s",
|
||||
label, label [0] ? " " : "", command,
|
||||
label, label[0] ? " " : "", command,
|
||||
(command & CMD_HIRD) ? " HIRD" : "",
|
||||
(command & CMD_PPCEE) ? " PPCEE" : "",
|
||||
(command & CMD_FSP) ? " FSP" : "",
|
||||
(command & CMD_ASPE) ? " ASPE" : "",
|
||||
(command & CMD_PSPE) ? " PSPE" : "",
|
||||
(command & CMD_PARK) ? " park" : "(park)",
|
||||
CMD_PARK_CNT (command),
|
||||
CMD_PARK_CNT(command),
|
||||
(command >> 16) & 0x3f,
|
||||
(command & CMD_LRESET) ? " LReset" : "",
|
||||
(command & CMD_IAAD) ? " IAAD" : "",
|
||||
(command & CMD_ASE) ? " Async" : "",
|
||||
(command & CMD_PSE) ? " Periodic" : "",
|
||||
fls_strings [(command >> 2) & 0x3],
|
||||
fls_strings[(command >> 2) & 0x3],
|
||||
(command & CMD_RESET) ? " Reset" : "",
|
||||
(command & CMD_RUN) ? "RUN" : "HALT"
|
||||
);
|
||||
(command & CMD_RUN) ? "RUN" : "HALT");
|
||||
}
|
||||
|
||||
static int
|
||||
dbg_port_buf (char *buf, unsigned len, const char *label, int port, u32 status)
|
||||
dbg_port_buf(char *buf, unsigned len, const char *label, int port, u32 status)
|
||||
{
|
||||
char *sig;
|
||||
|
||||
/* signaling state */
|
||||
switch (status & (3 << 10)) {
|
||||
case 0 << 10: sig = "se0"; break;
|
||||
case 1 << 10: sig = "k"; break; /* low speed */
|
||||
case 2 << 10: sig = "j"; break;
|
||||
default: sig = "?"; break;
|
||||
case 0 << 10:
|
||||
sig = "se0";
|
||||
break;
|
||||
case 1 << 10: /* low speed */
|
||||
sig = "k";
|
||||
break;
|
||||
case 2 << 10:
|
||||
sig = "j";
|
||||
break;
|
||||
default:
|
||||
sig = "?";
|
||||
break;
|
||||
}
|
||||
|
||||
return scnprintf (buf, len,
|
||||
return scnprintf(buf, len,
|
||||
"%s%sport:%d status %06x %d %s%s%s%s%s%s "
|
||||
"sig=%s%s%s%s%s%s%s%s%s%s%s",
|
||||
label, label [0] ? " " : "", port, status,
|
||||
status>>25,/*device address */
|
||||
(status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_ACK ?
|
||||
label, label[0] ? " " : "", port, status,
|
||||
status >> 25, /*device address */
|
||||
(status & PORT_SSTS) >> 23 == PORTSC_SUSPEND_STS_ACK ?
|
||||
" ACK" : "",
|
||||
(status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_NYET ?
|
||||
(status & PORT_SSTS) >> 23 == PORTSC_SUSPEND_STS_NYET ?
|
||||
" NYET" : "",
|
||||
(status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_STALL ?
|
||||
(status & PORT_SSTS) >> 23 == PORTSC_SUSPEND_STS_STALL ?
|
||||
" STALL" : "",
|
||||
(status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_ERR ?
|
||||
(status & PORT_SSTS) >> 23 == PORTSC_SUSPEND_STS_ERR ?
|
||||
" ERR" : "",
|
||||
(status & PORT_POWER) ? " POWER" : "",
|
||||
(status & PORT_OWNER) ? " OWNER" : "",
|
||||
|
@ -282,52 +283,68 @@ dbg_port_buf (char *buf, unsigned len, const char *label, int port, u32 status)
|
|||
|
||||
#else
|
||||
static inline void __maybe_unused
|
||||
dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
dbg_qh(char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||
{}
|
||||
|
||||
static inline int __maybe_unused
|
||||
dbg_status_buf (char *buf, unsigned len, const char *label, u32 status)
|
||||
{ return 0; }
|
||||
dbg_status_buf(char *buf, unsigned len, const char *label, u32 status)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int __maybe_unused
|
||||
dbg_command_buf (char *buf, unsigned len, const char *label, u32 command)
|
||||
{ return 0; }
|
||||
dbg_command_buf(char *buf, unsigned len, const char *label, u32 command)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int __maybe_unused
|
||||
dbg_intr_buf (char *buf, unsigned len, const char *label, u32 enable)
|
||||
{ return 0; }
|
||||
dbg_intr_buf(char *buf, unsigned len, const char *label, u32 enable)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int __maybe_unused
|
||||
dbg_port_buf (char *buf, unsigned len, const char *label, int port, u32 status)
|
||||
{ return 0; }
|
||||
dbg_port_buf(char *buf, unsigned len, const char *label, int port, u32 status)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_DYNAMIC_DEBUG */
|
||||
|
||||
/* functions have the "wrong" filename when they're output... */
|
||||
#define dbg_status(ehci, label, status) { \
|
||||
char _buf [80]; \
|
||||
dbg_status_buf (_buf, sizeof _buf, label, status); \
|
||||
ehci_dbg (ehci, "%s\n", _buf); \
|
||||
static inline void
|
||||
dbg_status(struct ehci_hcd *ehci, const char *label, u32 status)
|
||||
{
|
||||
char buf[80];
|
||||
|
||||
dbg_status_buf(buf, sizeof(buf), label, status);
|
||||
ehci_dbg(ehci, "%s\n", buf);
|
||||
}
|
||||
|
||||
#define dbg_cmd(ehci, label, command) { \
|
||||
char _buf [80]; \
|
||||
dbg_command_buf (_buf, sizeof _buf, label, command); \
|
||||
ehci_dbg (ehci, "%s\n", _buf); \
|
||||
static inline void
|
||||
dbg_cmd(struct ehci_hcd *ehci, const char *label, u32 command)
|
||||
{
|
||||
char buf[80];
|
||||
|
||||
dbg_command_buf(buf, sizeof(buf), label, command);
|
||||
ehci_dbg(ehci, "%s\n", buf);
|
||||
}
|
||||
|
||||
#define dbg_port(ehci, label, port, status) { \
|
||||
char _buf [80]; \
|
||||
dbg_port_buf (_buf, sizeof _buf, label, port, status); \
|
||||
ehci_dbg (ehci, "%s\n", _buf); \
|
||||
static inline void
|
||||
dbg_port(struct ehci_hcd *ehci, const char *label, int port, u32 status)
|
||||
{
|
||||
char buf[80];
|
||||
|
||||
dbg_port_buf(buf, sizeof(buf), label, port, status);
|
||||
ehci_dbg(ehci, "%s\n", buf);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#ifdef STUB_DEBUG_FILES
|
||||
#ifndef CONFIG_DYNAMIC_DEBUG
|
||||
|
||||
static inline void create_debug_files (struct ehci_hcd *bus) { }
|
||||
static inline void remove_debug_files (struct ehci_hcd *bus) { }
|
||||
static inline void create_debug_files(struct ehci_hcd *bus) { }
|
||||
static inline void remove_debug_files(struct ehci_hcd *bus) { }
|
||||
|
||||
#else
|
||||
|
||||
|
@ -348,6 +365,7 @@ static const struct file_operations debug_async_fops = {
|
|||
.release = debug_close,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static const struct file_operations debug_bandwidth_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = debug_bandwidth_open,
|
||||
|
@ -355,6 +373,7 @@ static const struct file_operations debug_bandwidth_fops = {
|
|||
.release = debug_close,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static const struct file_operations debug_periodic_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = debug_periodic_open,
|
||||
|
@ -362,6 +381,7 @@ static const struct file_operations debug_periodic_fops = {
|
|||
.release = debug_close,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static const struct file_operations debug_registers_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = debug_registers_open,
|
||||
|
@ -381,13 +401,19 @@ struct debug_buffer {
|
|||
size_t alloc_size;
|
||||
};
|
||||
|
||||
#define speed_char(info1) ({ char tmp; \
|
||||
switch (info1 & (3 << 12)) { \
|
||||
case QH_FULL_SPEED: tmp = 'f'; break; \
|
||||
case QH_LOW_SPEED: tmp = 'l'; break; \
|
||||
case QH_HIGH_SPEED: tmp = 'h'; break; \
|
||||
default: tmp = '?'; break; \
|
||||
} tmp; })
|
||||
static inline char speed_char(u32 info1)
|
||||
{
|
||||
switch (info1 & (3 << 12)) {
|
||||
case QH_FULL_SPEED:
|
||||
return 'f';
|
||||
case QH_LOW_SPEED:
|
||||
return 'l';
|
||||
case QH_HIGH_SPEED:
|
||||
return 'h';
|
||||
default:
|
||||
return '?';
|
||||
}
|
||||
}
|
||||
|
||||
static inline char token_mark(struct ehci_hcd *ehci, __hc32 token)
|
||||
{
|
||||
|
@ -397,18 +423,14 @@ static inline char token_mark(struct ehci_hcd *ehci, __hc32 token)
|
|||
return '*';
|
||||
if (v & QTD_STS_HALT)
|
||||
return '-';
|
||||
if (!IS_SHORT_READ (v))
|
||||
if (!IS_SHORT_READ(v))
|
||||
return ' ';
|
||||
/* tries to advance through hw_alt_next */
|
||||
return '/';
|
||||
}
|
||||
|
||||
static void qh_lines (
|
||||
struct ehci_hcd *ehci,
|
||||
struct ehci_qh *qh,
|
||||
char **nextp,
|
||||
unsigned *sizep
|
||||
)
|
||||
static void qh_lines(struct ehci_hcd *ehci, struct ehci_qh *qh,
|
||||
char **nextp, unsigned *sizep)
|
||||
{
|
||||
u32 scratch;
|
||||
u32 hw_curr;
|
||||
|
@ -435,7 +457,7 @@ static void qh_lines (
|
|||
}
|
||||
scratch = hc32_to_cpup(ehci, &hw->hw_info1);
|
||||
hw_curr = (mark == '*') ? hc32_to_cpup(ehci, &hw->hw_current) : 0;
|
||||
temp = scnprintf (next, size,
|
||||
temp = scnprintf(next, size,
|
||||
"qh/%p dev%d %cs ep%d %08x %08x (%08x%c %s nak%d)"
|
||||
" [cur %08x next %08x buf[0] %08x]",
|
||||
qh, scratch & 0x007f,
|
||||
|
@ -453,46 +475,52 @@ static void qh_lines (
|
|||
next += temp;
|
||||
|
||||
/* hc may be modifying the list as we read it ... */
|
||||
list_for_each (entry, &qh->qtd_list) {
|
||||
td = list_entry (entry, struct ehci_qtd, qtd_list);
|
||||
list_for_each(entry, &qh->qtd_list) {
|
||||
char *type;
|
||||
|
||||
td = list_entry(entry, struct ehci_qtd, qtd_list);
|
||||
scratch = hc32_to_cpup(ehci, &td->hw_token);
|
||||
mark = ' ';
|
||||
if (hw_curr == td->qtd_dma)
|
||||
if (hw_curr == td->qtd_dma) {
|
||||
mark = '*';
|
||||
else if (hw->hw_qtd_next == cpu_to_hc32(ehci, td->qtd_dma))
|
||||
} else if (hw->hw_qtd_next == cpu_to_hc32(ehci, td->qtd_dma)) {
|
||||
mark = '+';
|
||||
else if (QTD_LENGTH (scratch)) {
|
||||
} else if (QTD_LENGTH(scratch)) {
|
||||
if (td->hw_alt_next == ehci->async->hw->hw_alt_next)
|
||||
mark = '#';
|
||||
else if (td->hw_alt_next != list_end)
|
||||
mark = '/';
|
||||
}
|
||||
temp = snprintf (next, size,
|
||||
switch ((scratch >> 8) & 0x03) {
|
||||
case 0:
|
||||
type = "out";
|
||||
break;
|
||||
case 1:
|
||||
type = "in";
|
||||
break;
|
||||
case 2:
|
||||
type = "setup";
|
||||
break;
|
||||
default:
|
||||
type = "?";
|
||||
break;
|
||||
}
|
||||
temp = scnprintf(next, size,
|
||||
"\n\t%p%c%s len=%d %08x urb %p"
|
||||
" [td %08x buf[0] %08x]",
|
||||
td, mark, ({ char *tmp;
|
||||
switch ((scratch>>8)&0x03) {
|
||||
case 0: tmp = "out"; break;
|
||||
case 1: tmp = "in"; break;
|
||||
case 2: tmp = "setup"; break;
|
||||
default: tmp = "?"; break;
|
||||
} tmp;}),
|
||||
td, mark, type,
|
||||
(scratch >> 16) & 0x7fff,
|
||||
scratch,
|
||||
td->urb,
|
||||
(u32) td->qtd_dma,
|
||||
hc32_to_cpup(ehci, &td->hw_buf[0]));
|
||||
if (size < temp)
|
||||
temp = size;
|
||||
size -= temp;
|
||||
next += temp;
|
||||
if (temp == size)
|
||||
goto done;
|
||||
}
|
||||
|
||||
temp = snprintf (next, size, "\n");
|
||||
if (size < temp)
|
||||
temp = size;
|
||||
temp = scnprintf(next, size, "\n");
|
||||
size -= temp;
|
||||
next += temp;
|
||||
|
||||
|
@ -511,19 +539,20 @@ static ssize_t fill_async_buffer(struct debug_buffer *buf)
|
|||
struct ehci_qh *qh;
|
||||
|
||||
hcd = bus_to_hcd(buf->bus);
|
||||
ehci = hcd_to_ehci (hcd);
|
||||
ehci = hcd_to_ehci(hcd);
|
||||
next = buf->output_buf;
|
||||
size = buf->alloc_size;
|
||||
|
||||
*next = 0;
|
||||
|
||||
/* dumps a snapshot of the async schedule.
|
||||
/*
|
||||
* dumps a snapshot of the async schedule.
|
||||
* usually empty except for long-term bulk reads, or head.
|
||||
* one QH per line, and TDs we know about
|
||||
*/
|
||||
spin_lock_irqsave (&ehci->lock, flags);
|
||||
spin_lock_irqsave(&ehci->lock, flags);
|
||||
for (qh = ehci->async->qh_next.qh; size > 0 && qh; qh = qh->qh_next.qh)
|
||||
qh_lines (ehci, qh, &next, &size);
|
||||
qh_lines(ehci, qh, &next, &size);
|
||||
if (!list_empty(&ehci->async_unlink) && size > 0) {
|
||||
temp = scnprintf(next, size, "\nunlink =\n");
|
||||
size -= temp;
|
||||
|
@ -535,7 +564,7 @@ static ssize_t fill_async_buffer(struct debug_buffer *buf)
|
|||
qh_lines(ehci, qh, &next, &size);
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore (&ehci->lock, flags);
|
||||
spin_unlock_irqrestore(&ehci->lock, flags);
|
||||
|
||||
return strlen(buf->output_buf);
|
||||
}
|
||||
|
@ -623,6 +652,33 @@ static ssize_t fill_bandwidth_buffer(struct debug_buffer *buf)
|
|||
return next - buf->output_buf;
|
||||
}
|
||||
|
||||
static unsigned output_buf_tds_dir(char *buf, struct ehci_hcd *ehci,
|
||||
struct ehci_qh_hw *hw, struct ehci_qh *qh, unsigned size)
|
||||
{
|
||||
u32 scratch = hc32_to_cpup(ehci, &hw->hw_info1);
|
||||
struct ehci_qtd *qtd;
|
||||
char *type = "";
|
||||
unsigned temp = 0;
|
||||
|
||||
/* count tds, get ep direction */
|
||||
list_for_each_entry(qtd, &qh->qtd_list, qtd_list) {
|
||||
temp++;
|
||||
switch ((hc32_to_cpu(ehci, qtd->hw_token) >> 8) & 0x03) {
|
||||
case 0:
|
||||
type = "out";
|
||||
continue;
|
||||
case 1:
|
||||
type = "in";
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return scnprintf(buf, size, " (%c%d ep%d%s [%d/%d] q%d p%d)",
|
||||
speed_char(scratch), scratch & 0x007f,
|
||||
(scratch >> 8) & 0x000f, type, qh->ps.usecs,
|
||||
qh->ps.c_usecs, temp, 0x7ff & (scratch >> 16));
|
||||
}
|
||||
|
||||
#define DBG_SCHED_LIMIT 64
|
||||
static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
|
||||
{
|
||||
|
@ -635,31 +691,32 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
|
|||
unsigned i;
|
||||
__hc32 tag;
|
||||
|
||||
seen = kmalloc(DBG_SCHED_LIMIT * sizeof *seen, GFP_ATOMIC);
|
||||
seen = kmalloc_array(DBG_SCHED_LIMIT, sizeof(*seen), GFP_ATOMIC);
|
||||
if (!seen)
|
||||
return 0;
|
||||
seen_count = 0;
|
||||
|
||||
hcd = bus_to_hcd(buf->bus);
|
||||
ehci = hcd_to_ehci (hcd);
|
||||
ehci = hcd_to_ehci(hcd);
|
||||
next = buf->output_buf;
|
||||
size = buf->alloc_size;
|
||||
|
||||
temp = scnprintf (next, size, "size = %d\n", ehci->periodic_size);
|
||||
temp = scnprintf(next, size, "size = %d\n", ehci->periodic_size);
|
||||
size -= temp;
|
||||
next += temp;
|
||||
|
||||
/* dump a snapshot of the periodic schedule.
|
||||
/*
|
||||
* dump a snapshot of the periodic schedule.
|
||||
* iso changes, interrupt usually doesn't.
|
||||
*/
|
||||
spin_lock_irqsave (&ehci->lock, flags);
|
||||
spin_lock_irqsave(&ehci->lock, flags);
|
||||
for (i = 0; i < ehci->periodic_size; i++) {
|
||||
p = ehci->pshadow [i];
|
||||
if (likely (!p.ptr))
|
||||
p = ehci->pshadow[i];
|
||||
if (likely(!p.ptr))
|
||||
continue;
|
||||
tag = Q_NEXT_TYPE(ehci, ehci->periodic [i]);
|
||||
tag = Q_NEXT_TYPE(ehci, ehci->periodic[i]);
|
||||
|
||||
temp = scnprintf (next, size, "%4d: ", i);
|
||||
temp = scnprintf(next, size, "%4d: ", i);
|
||||
size -= temp;
|
||||
next += temp;
|
||||
|
||||
|
@ -669,7 +726,7 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
|
|||
switch (hc32_to_cpu(ehci, tag)) {
|
||||
case Q_TYPE_QH:
|
||||
hw = p.qh->hw;
|
||||
temp = scnprintf (next, size, " qh%d-%04x/%p",
|
||||
temp = scnprintf(next, size, " qh%d-%04x/%p",
|
||||
p.qh->ps.period,
|
||||
hc32_to_cpup(ehci,
|
||||
&hw->hw_info2)
|
||||
|
@ -680,10 +737,10 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
|
|||
next += temp;
|
||||
/* don't repeat what follows this qh */
|
||||
for (temp = 0; temp < seen_count; temp++) {
|
||||
if (seen [temp].ptr != p.ptr)
|
||||
if (seen[temp].ptr != p.ptr)
|
||||
continue;
|
||||
if (p.qh->qh_next.ptr) {
|
||||
temp = scnprintf (next, size,
|
||||
temp = scnprintf(next, size,
|
||||
" ...");
|
||||
size -= temp;
|
||||
next += temp;
|
||||
|
@ -692,58 +749,32 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
|
|||
}
|
||||
/* show more info the first time around */
|
||||
if (temp == seen_count) {
|
||||
u32 scratch = hc32_to_cpup(ehci,
|
||||
&hw->hw_info1);
|
||||
struct ehci_qtd *qtd;
|
||||
char *type = "";
|
||||
|
||||
/* count tds, get ep direction */
|
||||
temp = 0;
|
||||
list_for_each_entry (qtd,
|
||||
&p.qh->qtd_list,
|
||||
qtd_list) {
|
||||
temp++;
|
||||
switch (0x03 & (hc32_to_cpu(
|
||||
ehci,
|
||||
qtd->hw_token) >> 8)) {
|
||||
case 0: type = "out"; continue;
|
||||
case 1: type = "in"; continue;
|
||||
}
|
||||
}
|
||||
|
||||
temp = scnprintf (next, size,
|
||||
" (%c%d ep%d%s "
|
||||
"[%d/%d] q%d p%d)",
|
||||
speed_char (scratch),
|
||||
scratch & 0x007f,
|
||||
(scratch >> 8) & 0x000f, type,
|
||||
p.qh->ps.usecs,
|
||||
p.qh->ps.c_usecs,
|
||||
temp,
|
||||
0x7ff & (scratch >> 16));
|
||||
temp = output_buf_tds_dir(next, ehci,
|
||||
hw, p.qh, size);
|
||||
|
||||
if (seen_count < DBG_SCHED_LIMIT)
|
||||
seen [seen_count++].qh = p.qh;
|
||||
} else
|
||||
seen[seen_count++].qh = p.qh;
|
||||
} else {
|
||||
temp = 0;
|
||||
}
|
||||
tag = Q_NEXT_TYPE(ehci, hw->hw_next);
|
||||
p = p.qh->qh_next;
|
||||
break;
|
||||
case Q_TYPE_FSTN:
|
||||
temp = scnprintf (next, size,
|
||||
temp = scnprintf(next, size,
|
||||
" fstn-%8x/%p", p.fstn->hw_prev,
|
||||
p.fstn);
|
||||
tag = Q_NEXT_TYPE(ehci, p.fstn->hw_next);
|
||||
p = p.fstn->fstn_next;
|
||||
break;
|
||||
case Q_TYPE_ITD:
|
||||
temp = scnprintf (next, size,
|
||||
temp = scnprintf(next, size,
|
||||
" itd/%p", p.itd);
|
||||
tag = Q_NEXT_TYPE(ehci, p.itd->hw_next);
|
||||
p = p.itd->itd_next;
|
||||
break;
|
||||
case Q_TYPE_SITD:
|
||||
temp = scnprintf (next, size,
|
||||
temp = scnprintf(next, size,
|
||||
" sitd%d-%04x/%p",
|
||||
p.sitd->stream->ps.period,
|
||||
hc32_to_cpup(ehci, &p.sitd->hw_uframe)
|
||||
|
@ -757,12 +788,12 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
|
|||
next += temp;
|
||||
} while (p.ptr);
|
||||
|
||||
temp = scnprintf (next, size, "\n");
|
||||
temp = scnprintf(next, size, "\n");
|
||||
size -= temp;
|
||||
next += temp;
|
||||
}
|
||||
spin_unlock_irqrestore (&ehci->lock, flags);
|
||||
kfree (seen);
|
||||
spin_unlock_irqrestore(&ehci->lock, flags);
|
||||
kfree(seen);
|
||||
|
||||
return buf->alloc_size - size;
|
||||
}
|
||||
|
@ -789,19 +820,19 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)
|
|||
struct ehci_hcd *ehci;
|
||||
unsigned long flags;
|
||||
unsigned temp, size, i;
|
||||
char *next, scratch [80];
|
||||
static char fmt [] = "%*s\n";
|
||||
static char label [] = "";
|
||||
char *next, scratch[80];
|
||||
static char fmt[] = "%*s\n";
|
||||
static char label[] = "";
|
||||
|
||||
hcd = bus_to_hcd(buf->bus);
|
||||
ehci = hcd_to_ehci (hcd);
|
||||
ehci = hcd_to_ehci(hcd);
|
||||
next = buf->output_buf;
|
||||
size = buf->alloc_size;
|
||||
|
||||
spin_lock_irqsave (&ehci->lock, flags);
|
||||
spin_lock_irqsave(&ehci->lock, flags);
|
||||
|
||||
if (!HCD_HW_ACCESSIBLE(hcd)) {
|
||||
size = scnprintf (next, size,
|
||||
size = scnprintf(next, size,
|
||||
"bus %s, device %s\n"
|
||||
"%s\n"
|
||||
"SUSPENDED (no register access)\n",
|
||||
|
@ -813,7 +844,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)
|
|||
|
||||
/* Capability Registers */
|
||||
i = HC_VERSION(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
|
||||
temp = scnprintf (next, size,
|
||||
temp = scnprintf(next, size,
|
||||
"bus %s, device %s\n"
|
||||
"%s\n"
|
||||
"EHCI %x.%02x, rh state %s\n",
|
||||
|
@ -829,16 +860,16 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)
|
|||
if (dev_is_pci(hcd->self.controller)) {
|
||||
struct pci_dev *pdev;
|
||||
u32 offset, cap, cap2;
|
||||
unsigned count = 256/4;
|
||||
unsigned count = 256 / 4;
|
||||
|
||||
pdev = to_pci_dev(ehci_to_hcd(ehci)->self.controller);
|
||||
offset = HCC_EXT_CAPS(ehci_readl(ehci,
|
||||
&ehci->caps->hcc_params));
|
||||
while (offset && count--) {
|
||||
pci_read_config_dword (pdev, offset, &cap);
|
||||
pci_read_config_dword(pdev, offset, &cap);
|
||||
switch (cap & 0xff) {
|
||||
case 1:
|
||||
temp = scnprintf (next, size,
|
||||
temp = scnprintf(next, size,
|
||||
"ownership %08x%s%s\n", cap,
|
||||
(cap & (1 << 24)) ? " linux" : "",
|
||||
(cap & (1 << 16)) ? " firmware" : "");
|
||||
|
@ -846,8 +877,8 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)
|
|||
next += temp;
|
||||
|
||||
offset += 4;
|
||||
pci_read_config_dword (pdev, offset, &cap2);
|
||||
temp = scnprintf (next, size,
|
||||
pci_read_config_dword(pdev, offset, &cap2);
|
||||
temp = scnprintf(next, size,
|
||||
"SMI sts/enable 0x%08x\n", cap2);
|
||||
size -= temp;
|
||||
next += temp;
|
||||
|
@ -863,50 +894,50 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)
|
|||
}
|
||||
#endif
|
||||
|
||||
// FIXME interpret both types of params
|
||||
/* FIXME interpret both types of params */
|
||||
i = ehci_readl(ehci, &ehci->caps->hcs_params);
|
||||
temp = scnprintf (next, size, "structural params 0x%08x\n", i);
|
||||
temp = scnprintf(next, size, "structural params 0x%08x\n", i);
|
||||
size -= temp;
|
||||
next += temp;
|
||||
|
||||
i = ehci_readl(ehci, &ehci->caps->hcc_params);
|
||||
temp = scnprintf (next, size, "capability params 0x%08x\n", i);
|
||||
temp = scnprintf(next, size, "capability params 0x%08x\n", i);
|
||||
size -= temp;
|
||||
next += temp;
|
||||
|
||||
/* Operational Registers */
|
||||
temp = dbg_status_buf (scratch, sizeof scratch, label,
|
||||
temp = dbg_status_buf(scratch, sizeof(scratch), label,
|
||||
ehci_readl(ehci, &ehci->regs->status));
|
||||
temp = scnprintf (next, size, fmt, temp, scratch);
|
||||
temp = scnprintf(next, size, fmt, temp, scratch);
|
||||
size -= temp;
|
||||
next += temp;
|
||||
|
||||
temp = dbg_command_buf (scratch, sizeof scratch, label,
|
||||
temp = dbg_command_buf(scratch, sizeof(scratch), label,
|
||||
ehci_readl(ehci, &ehci->regs->command));
|
||||
temp = scnprintf (next, size, fmt, temp, scratch);
|
||||
temp = scnprintf(next, size, fmt, temp, scratch);
|
||||
size -= temp;
|
||||
next += temp;
|
||||
|
||||
temp = dbg_intr_buf (scratch, sizeof scratch, label,
|
||||
temp = dbg_intr_buf(scratch, sizeof(scratch), label,
|
||||
ehci_readl(ehci, &ehci->regs->intr_enable));
|
||||
temp = scnprintf (next, size, fmt, temp, scratch);
|
||||
temp = scnprintf(next, size, fmt, temp, scratch);
|
||||
size -= temp;
|
||||
next += temp;
|
||||
|
||||
temp = scnprintf (next, size, "uframe %04x\n",
|
||||
temp = scnprintf(next, size, "uframe %04x\n",
|
||||
ehci_read_frame_index(ehci));
|
||||
size -= temp;
|
||||
next += temp;
|
||||
|
||||
for (i = 1; i <= HCS_N_PORTS (ehci->hcs_params); i++) {
|
||||
temp = dbg_port_buf (scratch, sizeof scratch, label, i,
|
||||
for (i = 1; i <= HCS_N_PORTS(ehci->hcs_params); i++) {
|
||||
temp = dbg_port_buf(scratch, sizeof(scratch), label, i,
|
||||
ehci_readl(ehci,
|
||||
&ehci->regs->port_status[i - 1]));
|
||||
temp = scnprintf (next, size, fmt, temp, scratch);
|
||||
temp = scnprintf(next, size, fmt, temp, scratch);
|
||||
size -= temp;
|
||||
next += temp;
|
||||
if (i == HCS_DEBUG_PORT(ehci->hcs_params) && ehci->debug) {
|
||||
temp = scnprintf (next, size,
|
||||
temp = scnprintf(next, size,
|
||||
" debug control %08x\n",
|
||||
ehci_readl(ehci,
|
||||
&ehci->debug->control));
|
||||
|
@ -924,31 +955,31 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)
|
|||
}
|
||||
|
||||
#ifdef EHCI_STATS
|
||||
temp = scnprintf (next, size,
|
||||
temp = scnprintf(next, size,
|
||||
"irq normal %ld err %ld iaa %ld (lost %ld)\n",
|
||||
ehci->stats.normal, ehci->stats.error, ehci->stats.iaa,
|
||||
ehci->stats.lost_iaa);
|
||||
size -= temp;
|
||||
next += temp;
|
||||
|
||||
temp = scnprintf (next, size, "complete %ld unlink %ld\n",
|
||||
temp = scnprintf(next, size, "complete %ld unlink %ld\n",
|
||||
ehci->stats.complete, ehci->stats.unlink);
|
||||
size -= temp;
|
||||
next += temp;
|
||||
#endif
|
||||
|
||||
done:
|
||||
spin_unlock_irqrestore (&ehci->lock, flags);
|
||||
spin_unlock_irqrestore(&ehci->lock, flags);
|
||||
|
||||
return buf->alloc_size - size;
|
||||
}
|
||||
|
||||
static struct debug_buffer *alloc_buffer(struct usb_bus *bus,
|
||||
ssize_t (*fill_func)(struct debug_buffer *))
|
||||
ssize_t (*fill_func)(struct debug_buffer *))
|
||||
{
|
||||
struct debug_buffer *buf;
|
||||
|
||||
buf = kzalloc(sizeof(struct debug_buffer), GFP_KERNEL);
|
||||
buf = kzalloc(sizeof(*buf), GFP_KERNEL);
|
||||
|
||||
if (buf) {
|
||||
buf->bus = bus;
|
||||
|
@ -984,7 +1015,7 @@ out:
|
|||
}
|
||||
|
||||
static ssize_t debug_output(struct file *file, char __user *user_buf,
|
||||
size_t len, loff_t *offset)
|
||||
size_t len, loff_t *offset)
|
||||
{
|
||||
struct debug_buffer *buf = file->private_data;
|
||||
int ret = 0;
|
||||
|
@ -1004,7 +1035,6 @@ static ssize_t debug_output(struct file *file, char __user *user_buf,
|
|||
|
||||
out:
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static int debug_close(struct inode *inode, struct file *file)
|
||||
|
@ -1037,11 +1067,12 @@ static int debug_bandwidth_open(struct inode *inode, struct file *file)
|
|||
static int debug_periodic_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct debug_buffer *buf;
|
||||
|
||||
buf = alloc_buffer(inode->i_private, fill_periodic_buffer);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
buf->alloc_size = (sizeof(void *) == 4 ? 6 : 8)*PAGE_SIZE;
|
||||
buf->alloc_size = (sizeof(void *) == 4 ? 6 : 8) * PAGE_SIZE;
|
||||
file->private_data = buf;
|
||||
return 0;
|
||||
}
|
||||
|
@ -1054,7 +1085,7 @@ static int debug_registers_open(struct inode *inode, struct file *file)
|
|||
return file->private_data ? 0 : -ENOMEM;
|
||||
}
|
||||
|
||||
static inline void create_debug_files (struct ehci_hcd *ehci)
|
||||
static inline void create_debug_files(struct ehci_hcd *ehci)
|
||||
{
|
||||
struct usb_bus *bus = &ehci_to_hcd(ehci)->self;
|
||||
|
||||
|
@ -1084,9 +1115,9 @@ file_error:
|
|||
debugfs_remove_recursive(ehci->debug_dir);
|
||||
}
|
||||
|
||||
static inline void remove_debug_files (struct ehci_hcd *ehci)
|
||||
static inline void remove_debug_files(struct ehci_hcd *ehci)
|
||||
{
|
||||
debugfs_remove_recursive(ehci->debug_dir);
|
||||
}
|
||||
|
||||
#endif /* STUB_DEBUG_FILES */
|
||||
#endif /* CONFIG_DYNAMIC_DEBUG */
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue