diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-03-17 14:24:26 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-03-17 14:24:26 -0700 |
commit | 48d10bda1f2c69980601a61194015bb0790fb7ab (patch) | |
tree | e4ea2021560b1f18b335f6e8e20761fb9514cd1b | |
parent | 96b9b1c95660d4bc5510c5d798d3817ae9f0b391 (diff) | |
parent | ce53bfc4374cada8b645765e2b4ad5831e760932 (diff) | |
download | op-kernel-dev-48d10bda1f2c69980601a61194015bb0790fb7ab.zip op-kernel-dev-48d10bda1f2c69980601a61194015bb0790fb7ab.tar.gz |
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
...
192 files changed, 9545 insertions, 5918 deletions
diff --git a/Documentation/DocBook/usb.tmpl b/Documentation/DocBook/usb.tmpl index 4cd5b2c..bc776be0 100644 --- a/Documentation/DocBook/usb.tmpl +++ b/Documentation/DocBook/usb.tmpl @@ -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> diff --git a/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt b/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt index 2390e4e..eaf7e9b 100644 --- a/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt +++ b/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt @@ -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>; }; diff --git a/Documentation/devicetree/bindings/phy/rockchip-dp-phy.txt b/Documentation/devicetree/bindings/phy/rockchip-dp-phy.txt new file mode 100644 index 0000000..50c4f9b --- /dev/null +++ b/Documentation/devicetree/bindings/phy/rockchip-dp-phy.txt @@ -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>; +}; diff --git a/Documentation/devicetree/bindings/phy/rockchip-emmc-phy.txt b/Documentation/devicetree/bindings/phy/rockchip-emmc-phy.txt new file mode 100644 index 0000000..61916f1 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/rockchip-emmc-phy.txt @@ -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>; +}; diff --git a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt index 781296b..1084e2b 100644 --- a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt +++ b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt @@ -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 diff --git a/Documentation/devicetree/bindings/usb/dwc2.txt b/Documentation/devicetree/bindings/usb/dwc2.txt index 2213682..20a68bf 100644 --- a/Documentation/devicetree/bindings/usb/dwc2.txt +++ b/Documentation/devicetree/bindings/usb/dwc2.txt @@ -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 diff --git a/Documentation/devicetree/bindings/usb/usb-device.txt b/Documentation/devicetree/bindings/usb/usb-device.txt new file mode 100644 index 0000000..1c35e7b --- /dev/null +++ b/Documentation/devicetree/bindings/usb/usb-device.txt @@ -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>; + }; +} diff --git a/Documentation/devicetree/bindings/usb/usb-xhci.txt b/Documentation/devicetree/bindings/usb/usb-xhci.txt index 0825732..6a17aa8 100644 --- a/Documentation/devicetree/bindings/usb/usb-xhci.txt +++ b/Documentation/devicetree/bindings/usb/usb-xhci.txt @@ -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. diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 85b280d..0ee46a8 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -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. diff --git a/Documentation/usb/chipidea.txt b/Documentation/usb/chipidea.txt index 05f735a..678741b 100644 --- a/Documentation/usb/chipidea.txt +++ b/Documentation/usb/chipidea.txt @@ -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, diff --git a/Documentation/usb/usbdevfs-drop-permissions.c b/Documentation/usb/usbdevfs-drop-permissions.c new file mode 100644 index 0000000..6b8da6e --- /dev/null +++ b/Documentation/usb/usbdevfs-drop-permissions.c @@ -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; +} diff --git a/drivers/usb/usbip/usbip_protocol.txt b/Documentation/usb/usbip_protocol.txt index 16b6fe2..16b6fe2 100644 --- a/drivers/usb/usbip/usbip_protocol.txt +++ b/Documentation/usb/usbip_protocol.txt diff --git a/MAINTAINERS b/MAINTAINERS index 5dc9d90..89e9991 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -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/ diff --git a/arch/arm/mach-ixp4xx/include/mach/ixp4xx-regs.h b/arch/arm/mach-ixp4xx/include/mach/ixp4xx-regs.h index c5bae9c..b7ddd27 100644 --- a/arch/arm/mach-ixp4xx/include/mach/ixp4xx-regs.h +++ b/arch/arm/mach-ixp4xx/include/mach/ixp4xx-regs.h @@ -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 */ diff --git a/arch/arm/mach-pxa/include/mach/pxa25x-udc.h b/arch/arm/mach-pxa/include/mach/pxa25x-udc.h index 1b80a48..e69de29 100644 --- a/arch/arm/mach-pxa/include/mach/pxa25x-udc.h +++ b/arch/arm/mach-pxa/include/mach/pxa25x-udc.h @@ -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 diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 0124d17..26566db 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -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 diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index c80f09d..24596a9 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -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 diff --git a/drivers/phy/phy-dm816x-usb.c b/drivers/phy/phy-dm816x-usb.c index b4bbef6..cbcce7c 100644 --- a/drivers/phy/phy-dm816x-usb.c +++ b/drivers/phy/phy-dm816x-usb.c @@ -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; diff --git a/drivers/phy/phy-rcar-gen3-usb2.c b/drivers/phy/phy-rcar-gen3-usb2.c index ef332ef..bc4f7dd 100644 --- a/drivers/phy/phy-rcar-gen3-usb2.c +++ b/drivers/phy/phy-rcar-gen3-usb2.c @@ -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); */ diff --git a/drivers/phy/phy-rockchip-dp.c b/drivers/phy/phy-rockchip-dp.c new file mode 100644 index 0000000..77e2d02 --- /dev/null +++ b/drivers/phy/phy-rockchip-dp.c @@ -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"); diff --git a/drivers/phy/phy-rockchip-emmc.c b/drivers/phy/phy-rockchip-emmc.c new file mode 100644 index 0000000..887b4c2 --- /dev/null +++ b/drivers/phy/phy-rockchip-emmc.c @@ -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"); diff --git a/drivers/phy/phy-rockchip-usb.c b/drivers/phy/phy-rockchip-usb.c index 33a80eb..f62d899 100644 --- a/drivers/phy/phy-rockchip-usb.c +++ b/drivers/phy/phy-rockchip-usb.c @@ -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,30 +206,35 @@ 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; + 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; - } + 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 = 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) @@ -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"); diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c index 840f3ea..6b6af6c 100644 --- a/drivers/phy/phy-twl4030-usb.c +++ b/drivers/phy/phy-twl4030-usb.c @@ -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; diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index d5c57f1..dca7856 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -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/ diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c index 1173f9c..0a866e9 100644 --- a/drivers/usb/atm/cxacru.c +++ b/drivers/usb/atm/cxacru.c @@ -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 */ diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index f14f4ab..9ce8c9f 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -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; } diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 7404064..69426e6 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -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")) { diff --git a/drivers/usb/chipidea/debug.c b/drivers/usb/chipidea/debug.c index df47110..6d23eed 100644 --- a/drivers/usb/chipidea/debug.c +++ b/drivers/usb/chipidea/debug.c @@ -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), diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c index ba90dc6..de8e22e 100644 --- a/drivers/usb/chipidea/otg_fsm.c +++ b/drivers/usb/chipidea/otg_fsm.c @@ -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); diff --git a/drivers/usb/chipidea/otg_fsm.h b/drivers/usb/chipidea/otg_fsm.h index 262d6ef..6366fe3 100644 --- a/drivers/usb/chipidea/otg_fsm.h +++ b/drivers/usb/chipidea/otg_fsm.h @@ -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); diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 3eafa2c..065f5d9 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -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; diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index fa4e239..1d2c99a 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -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, diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index ccfaba9..05ce308 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -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; diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 7a11a82..917a55c 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -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); } diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c index e6ec125..49fbfe8 100644 --- a/drivers/usb/common/common.c +++ b/drivers/usb/common/common.c @@ -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) diff --git a/drivers/usb/common/usb-otg-fsm.c b/drivers/usb/common/usb-otg-fsm.c index 61d538a..504708f 100644 --- a/drivers/usb/common/usb-otg-fsm.c +++ b/drivers/usb/common/usb-otg-fsm.c @@ -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); diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile index 2f6f932..9780877 100644 --- a/drivers/usb/core/Makefile +++ b/drivers/usb/core/Makefile @@ -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 diff --git a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c index 89f2e77..2741566 100644 --- a/drivers/usb/core/buffer.c +++ b/drivers/usb/core/buffer.c @@ -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; } diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 5050760..5eb1a87 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -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; } diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index cffa0a0..ef04b50 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -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; } diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 59e7a33..52c4461 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -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, }; diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c index ea337a7..822ced9 100644 --- a/drivers/usb/core/file.c +++ b/drivers/usb/core/file.c @@ -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; diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index c3640f8..f9d42cf 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -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", diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index df0e3b9..2ca2cef 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -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: diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 51b43691..38cc4ba 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -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 diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index 45d070d..34c1a7e 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h @@ -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; diff --git a/drivers/usb/core/of.c b/drivers/usb/core/of.c new file mode 100644 index 0000000..2289700 --- /dev/null +++ b/drivers/usb/core/of.c @@ -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); + diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 65b6e6b..c953a0f 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -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); diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 3d27477..c601e25 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -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)) diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index ebb29ca..ffa5cf1 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -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); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 05b5e17..5331812 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -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; } diff --git a/drivers/usb/dwc2/Kconfig b/drivers/usb/dwc2/Kconfig index f0decc0..c1f29ca 100644 --- a/drivers/usb/dwc2/Kconfig +++ b/drivers/usb/dwc2/Kconfig @@ -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. diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index 46c4ba7..4135a5f 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -56,189 +56,6 @@ #include "core.h" #include "hcd.h" -#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) -/** - * dwc2_backup_host_registers() - Backup controller host 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 - */ -static int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg) -{ - struct dwc2_hregs_backup *hr; - int i; - - dev_dbg(hsotg->dev, "%s\n", __func__); - - /* Backup Host regs */ - hr = &hsotg->hr_backup; - hr->hcfg = dwc2_readl(hsotg->regs + HCFG); - hr->haintmsk = dwc2_readl(hsotg->regs + HAINTMSK); - for (i = 0; i < hsotg->core_params->host_channels; ++i) - hr->hcintmsk[i] = dwc2_readl(hsotg->regs + HCINTMSK(i)); - - hr->hprt0 = dwc2_read_hprt0(hsotg); - hr->hfir = dwc2_readl(hsotg->regs + HFIR); - hr->valid = true; - - return 0; -} - -/** - * dwc2_restore_host_registers() - Restore controller host registers. - * When resuming usb bus, device registers needs to be restored - * if controller power were disabled. - * - * @hsotg: Programming view of the DWC_otg controller - */ -static int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg) -{ - struct dwc2_hregs_backup *hr; - int i; - - dev_dbg(hsotg->dev, "%s\n", __func__); - - /* Restore host regs */ - hr = &hsotg->hr_backup; - if (!hr->valid) { - dev_err(hsotg->dev, "%s: no host registers to restore\n", - __func__); - return -EINVAL; - } - hr->valid = false; - - dwc2_writel(hr->hcfg, hsotg->regs + HCFG); - dwc2_writel(hr->haintmsk, hsotg->regs + HAINTMSK); - - for (i = 0; i < hsotg->core_params->host_channels; ++i) - dwc2_writel(hr->hcintmsk[i], hsotg->regs + HCINTMSK(i)); - - dwc2_writel(hr->hprt0, hsotg->regs + HPRT0); - dwc2_writel(hr->hfir, hsotg->regs + HFIR); - hsotg->frame_number = 0; - - return 0; -} -#else -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 - -#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \ - IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) -/** - * 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 - */ -static 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 - */ -static 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; -} -#else -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 - /** * dwc2_backup_global_registers() - Backup global controller registers. * When suspending usb bus, registers needs to be backuped @@ -421,62 +238,6 @@ int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg) return ret; } -/** - * dwc2_enable_common_interrupts() - Initializes the commmon interrupts, - * used in both device and host modes - * - * @hsotg: Programming view of the DWC_otg controller - */ -static void dwc2_enable_common_interrupts(struct dwc2_hsotg *hsotg) -{ - u32 intmsk; - - /* Clear any pending OTG Interrupts */ - dwc2_writel(0xffffffff, hsotg->regs + GOTGINT); - - /* Clear any pending interrupts */ - dwc2_writel(0xffffffff, hsotg->regs + GINTSTS); - - /* Enable the interrupts in the GINTMSK */ - intmsk = GINTSTS_MODEMIS | GINTSTS_OTGINT; - - if (hsotg->core_params->dma_enable <= 0) - intmsk |= GINTSTS_RXFLVL; - if (hsotg->core_params->external_id_pin_ctl <= 0) - intmsk |= GINTSTS_CONIDSTSCHNG; - - intmsk |= GINTSTS_WKUPINT | GINTSTS_USBSUSP | - GINTSTS_SESSREQINT; - - dwc2_writel(intmsk, hsotg->regs + GINTMSK); -} - -/* - * Initializes the FSLSPClkSel field of the HCFG register depending on the - * PHY type - */ -static void dwc2_init_fs_ls_pclk_sel(struct dwc2_hsotg *hsotg) -{ - u32 hcfg, val; - - if ((hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI && - hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED && - hsotg->core_params->ulpi_fs_ls > 0) || - hsotg->core_params->phy_type == DWC2_PHY_TYPE_PARAM_FS) { - /* Full speed PHY */ - val = HCFG_FSLSPCLKSEL_48_MHZ; - } else { - /* High speed PHY running at full speed or high speed */ - val = HCFG_FSLSPCLKSEL_30_60_MHZ; - } - - dev_dbg(hsotg->dev, "Initializing HCFG.FSLSPClkSel to %08x\n", val); - hcfg = dwc2_readl(hsotg->regs + HCFG); - hcfg &= ~HCFG_FSLSPCLKSEL_MASK; - hcfg |= val << HCFG_FSLSPCLKSEL_SHIFT; - dwc2_writel(hcfg, hsotg->regs + HCFG); -} - /* * Do core a soft reset of the core. Be careful with this because it * resets all the internal state machines of the core. @@ -646,1644 +407,6 @@ int dwc2_core_reset_and_force_dr_mode(struct dwc2_hsotg *hsotg) return 0; } -static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) -{ - u32 usbcfg, i2cctl; - int retval = 0; - - /* - * core_init() is now called on every switch so only call the - * following for the first time through - */ - if (select_phy) { - dev_dbg(hsotg->dev, "FS PHY selected\n"); - - usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); - if (!(usbcfg & GUSBCFG_PHYSEL)) { - usbcfg |= GUSBCFG_PHYSEL; - dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); - - /* Reset after a PHY select */ - retval = dwc2_core_reset_and_force_dr_mode(hsotg); - - if (retval) { - dev_err(hsotg->dev, - "%s: Reset failed, aborting", __func__); - return retval; - } - } - } - - /* - * Program DCFG.DevSpd or HCFG.FSLSPclkSel to 48Mhz in FS. Also - * do this on HNP Dev/Host mode switches (done in dev_init and - * host_init). - */ - if (dwc2_is_host_mode(hsotg)) - dwc2_init_fs_ls_pclk_sel(hsotg); - - if (hsotg->core_params->i2c_enable > 0) { - dev_dbg(hsotg->dev, "FS PHY enabling I2C\n"); - - /* Program GUSBCFG.OtgUtmiFsSel to I2C */ - usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); - usbcfg |= GUSBCFG_OTG_UTMI_FS_SEL; - dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); - - /* Program GI2CCTL.I2CEn */ - i2cctl = dwc2_readl(hsotg->regs + GI2CCTL); - i2cctl &= ~GI2CCTL_I2CDEVADDR_MASK; - i2cctl |= 1 << GI2CCTL_I2CDEVADDR_SHIFT; - i2cctl &= ~GI2CCTL_I2CEN; - dwc2_writel(i2cctl, hsotg->regs + GI2CCTL); - i2cctl |= GI2CCTL_I2CEN; - dwc2_writel(i2cctl, hsotg->regs + GI2CCTL); - } - - return retval; -} - -static int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) -{ - u32 usbcfg, usbcfg_old; - int retval = 0; - - if (!select_phy) - return 0; - - usbcfg = usbcfg_old = dwc2_readl(hsotg->regs + GUSBCFG); - - /* - * HS PHY parameters. These parameters are preserved during soft reset - * so only program the first time. Do a soft reset immediately after - * setting phyif. - */ - switch (hsotg->core_params->phy_type) { - case DWC2_PHY_TYPE_PARAM_ULPI: - /* ULPI interface */ - dev_dbg(hsotg->dev, "HS ULPI PHY selected\n"); - usbcfg |= GUSBCFG_ULPI_UTMI_SEL; - usbcfg &= ~(GUSBCFG_PHYIF16 | GUSBCFG_DDRSEL); - if (hsotg->core_params->phy_ulpi_ddr > 0) - usbcfg |= GUSBCFG_DDRSEL; - break; - case DWC2_PHY_TYPE_PARAM_UTMI: - /* UTMI+ interface */ - dev_dbg(hsotg->dev, "HS UTMI+ PHY selected\n"); - usbcfg &= ~(GUSBCFG_ULPI_UTMI_SEL | GUSBCFG_PHYIF16); - if (hsotg->core_params->phy_utmi_width == 16) - usbcfg |= GUSBCFG_PHYIF16; - break; - default: - dev_err(hsotg->dev, "FS PHY selected at HS!\n"); - break; - } - - if (usbcfg != usbcfg_old) { - dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); - - /* Reset after setting the PHY parameters */ - retval = dwc2_core_reset_and_force_dr_mode(hsotg); - if (retval) { - dev_err(hsotg->dev, - "%s: Reset failed, aborting", __func__); - return retval; - } - } - - return retval; -} - -static int dwc2_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) -{ - u32 usbcfg; - int retval = 0; - - if (hsotg->core_params->speed == DWC2_SPEED_PARAM_FULL && - hsotg->core_params->phy_type == DWC2_PHY_TYPE_PARAM_FS) { - /* If FS mode with FS PHY */ - retval = dwc2_fs_phy_init(hsotg, select_phy); - if (retval) - return retval; - } else { - /* High speed PHY */ - retval = dwc2_hs_phy_init(hsotg, select_phy); - if (retval) - return retval; - } - - if (hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI && - hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED && - hsotg->core_params->ulpi_fs_ls > 0) { - dev_dbg(hsotg->dev, "Setting ULPI FSLS\n"); - usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); - usbcfg |= GUSBCFG_ULPI_FS_LS; - usbcfg |= GUSBCFG_ULPI_CLK_SUSP_M; - dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); - } else { - usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); - usbcfg &= ~GUSBCFG_ULPI_FS_LS; - usbcfg &= ~GUSBCFG_ULPI_CLK_SUSP_M; - dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); - } - - return retval; -} - -static int dwc2_gahbcfg_init(struct dwc2_hsotg *hsotg) -{ - u32 ahbcfg = dwc2_readl(hsotg->regs + GAHBCFG); - - switch (hsotg->hw_params.arch) { - case GHWCFG2_EXT_DMA_ARCH: - dev_err(hsotg->dev, "External DMA Mode not supported\n"); - return -EINVAL; - - case GHWCFG2_INT_DMA_ARCH: - dev_dbg(hsotg->dev, "Internal DMA Mode\n"); - if (hsotg->core_params->ahbcfg != -1) { - ahbcfg &= GAHBCFG_CTRL_MASK; - ahbcfg |= hsotg->core_params->ahbcfg & - ~GAHBCFG_CTRL_MASK; - } - break; - - case GHWCFG2_SLAVE_ONLY_ARCH: - default: - dev_dbg(hsotg->dev, "Slave Only Mode\n"); - break; - } - - dev_dbg(hsotg->dev, "dma_enable:%d dma_desc_enable:%d\n", - hsotg->core_params->dma_enable, - hsotg->core_params->dma_desc_enable); - - if (hsotg->core_params->dma_enable > 0) { - if (hsotg->core_params->dma_desc_enable > 0) - dev_dbg(hsotg->dev, "Using Descriptor DMA mode\n"); - else - dev_dbg(hsotg->dev, "Using Buffer DMA mode\n"); - } else { - dev_dbg(hsotg->dev, "Using Slave mode\n"); - hsotg->core_params->dma_desc_enable = 0; - } - - if (hsotg->core_params->dma_enable > 0) - ahbcfg |= GAHBCFG_DMA_EN; - - dwc2_writel(ahbcfg, hsotg->regs + GAHBCFG); - - return 0; -} - -static void dwc2_gusbcfg_init(struct dwc2_hsotg *hsotg) -{ - u32 usbcfg; - - usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); - usbcfg &= ~(GUSBCFG_HNPCAP | GUSBCFG_SRPCAP); - - switch (hsotg->hw_params.op_mode) { - case GHWCFG2_OP_MODE_HNP_SRP_CAPABLE: - if (hsotg->core_params->otg_cap == - DWC2_CAP_PARAM_HNP_SRP_CAPABLE) - usbcfg |= GUSBCFG_HNPCAP; - if (hsotg->core_params->otg_cap != - DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE) - usbcfg |= GUSBCFG_SRPCAP; - break; - - case GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE: - case GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE: - case GHWCFG2_OP_MODE_SRP_CAPABLE_HOST: - if (hsotg->core_params->otg_cap != - DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE) - usbcfg |= GUSBCFG_SRPCAP; - break; - - case GHWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE: - case GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE: - case GHWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST: - default: - break; - } - - dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); -} - -/** - * dwc2_core_init() - Initializes the DWC_otg controller registers and - * prepares the core for device mode or host mode operation - * - * @hsotg: Programming view of the DWC_otg controller - * @initial_setup: If true then this is the first init for this instance. - */ -int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup) -{ - u32 usbcfg, otgctl; - int retval; - - dev_dbg(hsotg->dev, "%s(%p)\n", __func__, hsotg); - - usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); - - /* Set ULPI External VBUS bit if needed */ - usbcfg &= ~GUSBCFG_ULPI_EXT_VBUS_DRV; - if (hsotg->core_params->phy_ulpi_ext_vbus == - DWC2_PHY_ULPI_EXTERNAL_VBUS) - usbcfg |= GUSBCFG_ULPI_EXT_VBUS_DRV; - - /* Set external TS Dline pulsing bit if needed */ - usbcfg &= ~GUSBCFG_TERMSELDLPULSE; - if (hsotg->core_params->ts_dline > 0) - usbcfg |= GUSBCFG_TERMSELDLPULSE; - - dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); - - /* - * Reset the Controller - * - * We only need to reset the controller if this is a re-init. - * For the first init we know for sure that earlier code reset us (it - * needed to in order to properly detect various parameters). - */ - if (!initial_setup) { - retval = dwc2_core_reset_and_force_dr_mode(hsotg); - if (retval) { - dev_err(hsotg->dev, "%s(): Reset failed, aborting\n", - __func__); - return retval; - } - } - - /* - * This needs to happen in FS mode before any other programming occurs - */ - retval = dwc2_phy_init(hsotg, initial_setup); - if (retval) - return retval; - - /* Program the GAHBCFG Register */ - retval = dwc2_gahbcfg_init(hsotg); - if (retval) - return retval; - - /* Program the GUSBCFG register */ - dwc2_gusbcfg_init(hsotg); - - /* Program the GOTGCTL register */ - otgctl = dwc2_readl(hsotg->regs + GOTGCTL); - otgctl &= ~GOTGCTL_OTGVER; - if (hsotg->core_params->otg_ver > 0) - otgctl |= GOTGCTL_OTGVER; - dwc2_writel(otgctl, hsotg->regs + GOTGCTL); - dev_dbg(hsotg->dev, "OTG VER PARAM: %d\n", hsotg->core_params->otg_ver); - - /* Clear the SRP success bit for FS-I2c */ - hsotg->srp_success = 0; - - /* Enable common interrupts */ - dwc2_enable_common_interrupts(hsotg); - - /* - * Do device or host initialization based on mode during PCD and - * HCD initialization - */ - if (dwc2_is_host_mode(hsotg)) { - dev_dbg(hsotg->dev, "Host Mode\n"); - hsotg->op_state = OTG_STATE_A_HOST; - } else { - dev_dbg(hsotg->dev, "Device Mode\n"); - hsotg->op_state = OTG_STATE_B_PERIPHERAL; - } - - return 0; -} - -/** - * dwc2_enable_host_interrupts() - Enables the Host mode interrupts - * - * @hsotg: Programming view of DWC_otg controller - */ -void dwc2_enable_host_interrupts(struct dwc2_hsotg *hsotg) -{ - u32 intmsk; - - dev_dbg(hsotg->dev, "%s()\n", __func__); - - /* Disable all interrupts */ - dwc2_writel(0, hsotg->regs + GINTMSK); - dwc2_writel(0, hsotg->regs + HAINTMSK); - - /* Enable the common interrupts */ - dwc2_enable_common_interrupts(hsotg); - - /* Enable host mode interrupts without disturbing common interrupts */ - intmsk = dwc2_readl(hsotg->regs + GINTMSK); - intmsk |= GINTSTS_DISCONNINT | GINTSTS_PRTINT | GINTSTS_HCHINT; - dwc2_writel(intmsk, hsotg->regs + GINTMSK); -} - -/** - * dwc2_disable_host_interrupts() - Disables the Host Mode interrupts - * - * @hsotg: Programming view of DWC_otg controller - */ -void dwc2_disable_host_interrupts(struct dwc2_hsotg *hsotg) -{ - u32 intmsk = dwc2_readl(hsotg->regs + GINTMSK); - - /* Disable host mode interrupts without disturbing common interrupts */ - intmsk &= ~(GINTSTS_SOF | GINTSTS_PRTINT | GINTSTS_HCHINT | - GINTSTS_PTXFEMP | GINTSTS_NPTXFEMP | GINTSTS_DISCONNINT); - dwc2_writel(intmsk, hsotg->regs + GINTMSK); -} - -/* - * dwc2_calculate_dynamic_fifo() - Calculates the default fifo size - * For system that have a total fifo depth that is smaller than the default - * RX + TX fifo size. - * - * @hsotg: Programming view of DWC_otg controller - */ -static void dwc2_calculate_dynamic_fifo(struct dwc2_hsotg *hsotg) -{ - struct dwc2_core_params *params = hsotg->core_params; - struct dwc2_hw_params *hw = &hsotg->hw_params; - u32 rxfsiz, nptxfsiz, ptxfsiz, total_fifo_size; - - total_fifo_size = hw->total_fifo_size; - rxfsiz = params->host_rx_fifo_size; - nptxfsiz = params->host_nperio_tx_fifo_size; - ptxfsiz = params->host_perio_tx_fifo_size; - - /* - * Will use Method 2 defined in the DWC2 spec: minimum FIFO depth - * allocation with support for high bandwidth endpoints. Synopsys - * defines MPS(Max Packet size) for a periodic EP=1024, and for - * non-periodic as 512. - */ - if (total_fifo_size < (rxfsiz + nptxfsiz + ptxfsiz)) { - /* - * For Buffer DMA mode/Scatter Gather DMA mode - * 2 * ((Largest Packet size / 4) + 1 + 1) + n - * with n = number of host channel. - * 2 * ((1024/4) + 2) = 516 - */ - rxfsiz = 516 + hw->host_channels; - - /* - * min non-periodic tx fifo depth - * 2 * (largest non-periodic USB packet used / 4) - * 2 * (512/4) = 256 - */ - nptxfsiz = 256; - - /* - * min periodic tx fifo depth - * (largest packet size*MC)/4 - * (1024 * 3)/4 = 768 - */ - ptxfsiz = 768; - - params->host_rx_fifo_size = rxfsiz; - params->host_nperio_tx_fifo_size = nptxfsiz; - params->host_perio_tx_fifo_size = ptxfsiz; - } - - /* - * If the summation of RX, NPTX and PTX fifo sizes is still - * bigger than the total_fifo_size, then we have a problem. - * - * We won't be able to allocate as many endpoints. Right now, - * we're just printing an error message, but ideally this FIFO - * allocation algorithm would be improved in the future. - * - * FIXME improve this FIFO allocation algorithm. - */ - if (unlikely(total_fifo_size < (rxfsiz + nptxfsiz + ptxfsiz))) - dev_err(hsotg->dev, "invalid fifo sizes\n"); -} - -static void dwc2_config_fifos(struct dwc2_hsotg *hsotg) -{ - struct dwc2_core_params *params = hsotg->core_params; - u32 nptxfsiz, hptxfsiz, dfifocfg, grxfsiz; - - if (!params->enable_dynamic_fifo) - return; - - dwc2_calculate_dynamic_fifo(hsotg); - - /* Rx FIFO */ - grxfsiz = dwc2_readl(hsotg->regs + GRXFSIZ); - dev_dbg(hsotg->dev, "initial grxfsiz=%08x\n", grxfsiz); - grxfsiz &= ~GRXFSIZ_DEPTH_MASK; - grxfsiz |= params->host_rx_fifo_size << - GRXFSIZ_DEPTH_SHIFT & GRXFSIZ_DEPTH_MASK; - dwc2_writel(grxfsiz, hsotg->regs + GRXFSIZ); - dev_dbg(hsotg->dev, "new grxfsiz=%08x\n", - dwc2_readl(hsotg->regs + GRXFSIZ)); - - /* Non-periodic Tx FIFO */ - dev_dbg(hsotg->dev, "initial gnptxfsiz=%08x\n", - dwc2_readl(hsotg->regs + GNPTXFSIZ)); - nptxfsiz = params->host_nperio_tx_fifo_size << - FIFOSIZE_DEPTH_SHIFT & FIFOSIZE_DEPTH_MASK; - nptxfsiz |= params->host_rx_fifo_size << - FIFOSIZE_STARTADDR_SHIFT & FIFOSIZE_STARTADDR_MASK; - dwc2_writel(nptxfsiz, hsotg->regs + GNPTXFSIZ); - dev_dbg(hsotg->dev, "new gnptxfsiz=%08x\n", - dwc2_readl(hsotg->regs + GNPTXFSIZ)); - - /* Periodic Tx FIFO */ - dev_dbg(hsotg->dev, "initial hptxfsiz=%08x\n", - dwc2_readl(hsotg->regs + HPTXFSIZ)); - hptxfsiz = params->host_perio_tx_fifo_size << - FIFOSIZE_DEPTH_SHIFT & FIFOSIZE_DEPTH_MASK; - hptxfsiz |= (params->host_rx_fifo_size + - params->host_nperio_tx_fifo_size) << - FIFOSIZE_STARTADDR_SHIFT & FIFOSIZE_STARTADDR_MASK; - dwc2_writel(hptxfsiz, hsotg->regs + HPTXFSIZ); - dev_dbg(hsotg->dev, "new hptxfsiz=%08x\n", - dwc2_readl(hsotg->regs + HPTXFSIZ)); - - if (hsotg->core_params->en_multiple_tx_fifo > 0 && - hsotg->hw_params.snpsid <= DWC2_CORE_REV_2_94a) { - /* - * Global DFIFOCFG calculation for Host mode - - * include RxFIFO, NPTXFIFO and HPTXFIFO - */ - dfifocfg = dwc2_readl(hsotg->regs + GDFIFOCFG); - dfifocfg &= ~GDFIFOCFG_EPINFOBASE_MASK; - dfifocfg |= (params->host_rx_fifo_size + - params->host_nperio_tx_fifo_size + - params->host_perio_tx_fifo_size) << - GDFIFOCFG_EPINFOBASE_SHIFT & - GDFIFOCFG_EPINFOBASE_MASK; - dwc2_writel(dfifocfg, hsotg->regs + GDFIFOCFG); - } -} - -/** - * dwc2_core_host_init() - Initializes the DWC_otg controller registers for - * Host mode - * - * @hsotg: Programming view of DWC_otg controller - * - * This function flushes the Tx and Rx FIFOs and flushes any entries in the - * request queues. Host channels are reset to ensure that they are ready for - * performing transfers. - */ -void dwc2_core_host_init(struct dwc2_hsotg *hsotg) -{ - u32 hcfg, hfir, otgctl; - - dev_dbg(hsotg->dev, "%s(%p)\n", __func__, hsotg); - - /* Restart the Phy Clock */ - dwc2_writel(0, hsotg->regs + PCGCTL); - - /* Initialize Host Configuration Register */ - dwc2_init_fs_ls_pclk_sel(hsotg); - if (hsotg->core_params->speed == DWC2_SPEED_PARAM_FULL) { - hcfg = dwc2_readl(hsotg->regs + HCFG); - hcfg |= HCFG_FSLSSUPP; - dwc2_writel(hcfg, hsotg->regs + HCFG); - } - - /* - * This bit allows dynamic reloading of the HFIR register during - * runtime. This bit needs to be programmed during initial configuration - * and its value must not be changed during runtime. - */ - if (hsotg->core_params->reload_ctl > 0) { - hfir = dwc2_readl(hsotg->regs + HFIR); - hfir |= HFIR_RLDCTRL; - dwc2_writel(hfir, hsotg->regs + HFIR); - } - - if (hsotg->core_params->dma_desc_enable > 0) { - u32 op_mode = hsotg->hw_params.op_mode; - if (hsotg->hw_params.snpsid < DWC2_CORE_REV_2_90a || - !hsotg->hw_params.dma_desc_enable || - op_mode == GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE || - op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE || - op_mode == GHWCFG2_OP_MODE_UNDEFINED) { - dev_err(hsotg->dev, - "Hardware does not support descriptor DMA mode -\n"); - dev_err(hsotg->dev, - "falling back to buffer DMA mode.\n"); - hsotg->core_params->dma_desc_enable = 0; - } else { - hcfg = dwc2_readl(hsotg->regs + HCFG); - hcfg |= HCFG_DESCDMA; - dwc2_writel(hcfg, hsotg->regs + HCFG); - } - } - - /* Configure data FIFO sizes */ - dwc2_config_fifos(hsotg); - - /* TODO - check this */ - /* Clear Host Set HNP Enable in the OTG Control Register */ - otgctl = dwc2_readl(hsotg->regs + GOTGCTL); - otgctl &= ~GOTGCTL_HSTSETHNPEN; - dwc2_writel(otgctl, hsotg->regs + GOTGCTL); - - /* Make sure the FIFOs are flushed */ - dwc2_flush_tx_fifo(hsotg, 0x10 /* all TX FIFOs */); - dwc2_flush_rx_fifo(hsotg); - - /* Clear Host Set HNP Enable in the OTG Control Register */ - otgctl = dwc2_readl(hsotg->regs + GOTGCTL); - otgctl &= ~GOTGCTL_HSTSETHNPEN; - dwc2_writel(otgctl, hsotg->regs + GOTGCTL); - - if (hsotg->core_params->dma_desc_enable <= 0) { - int num_channels, i; - u32 hcchar; - - /* Flush out any leftover queued requests */ - num_channels = hsotg->core_params->host_channels; - for (i = 0; i < num_channels; i++) { - hcchar = dwc2_readl(hsotg->regs + HCCHAR(i)); - hcchar &= ~HCCHAR_CHENA; - hcchar |= HCCHAR_CHDIS; - hcchar &= ~HCCHAR_EPDIR; - dwc2_writel(hcchar, hsotg->regs + HCCHAR(i)); - } - - /* Halt all channels to put them into a known state */ - for (i = 0; i < num_channels; i++) { - int count = 0; - - hcchar = dwc2_readl(hsotg->regs + HCCHAR(i)); - hcchar |= HCCHAR_CHENA | HCCHAR_CHDIS; - hcchar &= ~HCCHAR_EPDIR; - dwc2_writel(hcchar, hsotg->regs + HCCHAR(i)); - dev_dbg(hsotg->dev, "%s: Halt channel %d\n", - __func__, i); - do { - hcchar = dwc2_readl(hsotg->regs + HCCHAR(i)); - if (++count > 1000) { - dev_err(hsotg->dev, - "Unable to clear enable on channel %d\n", - i); - break; - } - udelay(1); - } while (hcchar & HCCHAR_CHENA); - } - } - - /* Turn on the vbus power */ - dev_dbg(hsotg->dev, "Init: Port Power? op_state=%d\n", hsotg->op_state); - if (hsotg->op_state == OTG_STATE_A_HOST) { - u32 hprt0 = dwc2_read_hprt0(hsotg); - - dev_dbg(hsotg->dev, "Init: Power Port (%d)\n", - !!(hprt0 & HPRT0_PWR)); - if (!(hprt0 & HPRT0_PWR)) { - hprt0 |= HPRT0_PWR; - dwc2_writel(hprt0, hsotg->regs + HPRT0); - } - } - - dwc2_enable_host_interrupts(hsotg); -} - -static void dwc2_hc_enable_slave_ints(struct dwc2_hsotg *hsotg, - struct dwc2_host_chan *chan) -{ - u32 hcintmsk = HCINTMSK_CHHLTD; - - switch (chan->ep_type) { - case USB_ENDPOINT_XFER_CONTROL: - case USB_ENDPOINT_XFER_BULK: - dev_vdbg(hsotg->dev, "control/bulk\n"); - hcintmsk |= HCINTMSK_XFERCOMPL; - hcintmsk |= HCINTMSK_STALL; - hcintmsk |= HCINTMSK_XACTERR; - hcintmsk |= HCINTMSK_DATATGLERR; - if (chan->ep_is_in) { - hcintmsk |= HCINTMSK_BBLERR; - } else { - hcintmsk |= HCINTMSK_NAK; - hcintmsk |= HCINTMSK_NYET; - if (chan->do_ping) - hcintmsk |= HCINTMSK_ACK; - } - - if (chan->do_split) { - hcintmsk |= HCINTMSK_NAK; - if (chan->complete_split) - hcintmsk |= HCINTMSK_NYET; - else - hcintmsk |= HCINTMSK_ACK; - } - - if (chan->error_state) - hcintmsk |= HCINTMSK_ACK; - break; - - case USB_ENDPOINT_XFER_INT: - if (dbg_perio()) - dev_vdbg(hsotg->dev, "intr\n"); - hcintmsk |= HCINTMSK_XFERCOMPL; - hcintmsk |= HCINTMSK_NAK; - hcintmsk |= HCINTMSK_STALL; - hcintmsk |= HCINTMSK_XACTERR; - hcintmsk |= HCINTMSK_DATATGLERR; - hcintmsk |= HCINTMSK_FRMOVRUN; - - if (chan->ep_is_in) - hcintmsk |= HCINTMSK_BBLERR; - if (chan->error_state) - hcintmsk |= HCINTMSK_ACK; - if (chan->do_split) { - if (chan->complete_split) - hcintmsk |= HCINTMSK_NYET; - else - hcintmsk |= HCINTMSK_ACK; - } - break; - - case USB_ENDPOINT_XFER_ISOC: - if (dbg_perio()) - dev_vdbg(hsotg->dev, "isoc\n"); - hcintmsk |= HCINTMSK_XFERCOMPL; - hcintmsk |= HCINTMSK_FRMOVRUN; - hcintmsk |= HCINTMSK_ACK; - - if (chan->ep_is_in) { - hcintmsk |= HCINTMSK_XACTERR; - hcintmsk |= HCINTMSK_BBLERR; - } - break; - default: - dev_err(hsotg->dev, "## Unknown EP type ##\n"); - break; - } - - dwc2_writel(hcintmsk, hsotg->regs + HCINTMSK(chan->hc_num)); - if (dbg_hc(chan)) - dev_vdbg(hsotg->dev, "set HCINTMSK to %08x\n", hcintmsk); -} - -static void dwc2_hc_enable_dma_ints(struct dwc2_hsotg *hsotg, - struct dwc2_host_chan *chan) -{ - u32 hcintmsk = HCINTMSK_CHHLTD; - - /* - * For Descriptor DMA mode core halts the channel on AHB error. - * Interrupt is not required. - */ - if (hsotg->core_params->dma_desc_enable <= 0) { - if (dbg_hc(chan)) - dev_vdbg(hsotg->dev, "desc DMA disabled\n"); - hcintmsk |= HCINTMSK_AHBERR; - } else { - if (dbg_hc(chan)) - dev_vdbg(hsotg->dev, "desc DMA enabled\n"); - if (chan->ep_type == USB_ENDPOINT_XFER_ISOC) - hcintmsk |= HCINTMSK_XFERCOMPL; - } - - if (chan->error_state && !chan->do_split && - chan->ep_type != USB_ENDPOINT_XFER_ISOC) { - if (dbg_hc(chan)) - dev_vdbg(hsotg->dev, "setting ACK\n"); - hcintmsk |= HCINTMSK_ACK; - if (chan->ep_is_in) { - hcintmsk |= HCINTMSK_DATATGLERR; - if (chan->ep_type != USB_ENDPOINT_XFER_INT) - hcintmsk |= HCINTMSK_NAK; - } - } - - dwc2_writel(hcintmsk, hsotg->regs + HCINTMSK(chan->hc_num)); - if (dbg_hc(chan)) - dev_vdbg(hsotg->dev, "set HCINTMSK to %08x\n", hcintmsk); -} - -static void dwc2_hc_enable_ints(struct dwc2_hsotg *hsotg, - struct dwc2_host_chan *chan) -{ - u32 intmsk; - - if (hsotg->core_params->dma_enable > 0) { - if (dbg_hc(chan)) - dev_vdbg(hsotg->dev, "DMA enabled\n"); - dwc2_hc_enable_dma_ints(hsotg, chan); - } else { - if (dbg_hc(chan)) - dev_vdbg(hsotg->dev, "DMA disabled\n"); - dwc2_hc_enable_slave_ints(hsotg, chan); - } - - /* Enable the top level host channel interrupt */ - intmsk = dwc2_readl(hsotg->regs + HAINTMSK); - intmsk |= 1 << chan->hc_num; - dwc2_writel(intmsk, hsotg->regs + HAINTMSK); - if (dbg_hc(chan)) - dev_vdbg(hsotg->dev, "set HAINTMSK to %08x\n", intmsk); - - /* Make sure host channel interrupts are enabled */ - intmsk = dwc2_readl(hsotg->regs + GINTMSK); - intmsk |= GINTSTS_HCHINT; - dwc2_writel(intmsk, hsotg->regs + GINTMSK); - if (dbg_hc(chan)) - dev_vdbg(hsotg->dev, "set GINTMSK to %08x\n", intmsk); -} - -/** - * dwc2_hc_init() - Prepares a host channel for transferring packets to/from - * a specific endpoint - * - * @hsotg: Programming view of DWC_otg controller - * @chan: Information needed to initialize the host channel - * - * The HCCHARn register is set up with the characteristics specified in chan. - * Host channel interrupts that may need to be serviced while this transfer is - * in progress are enabled. - */ -void dwc2_hc_init(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan) -{ - u8 hc_num = chan->hc_num; - u32 hcintmsk; - u32 hcchar; - u32 hcsplt = 0; - - if (dbg_hc(chan)) - dev_vdbg(hsotg->dev, "%s()\n", __func__); - - /* Clear old interrupt conditions for this host channel */ - hcintmsk = 0xffffffff; - hcintmsk &= ~HCINTMSK_RESERVED14_31; - dwc2_writel(hcintmsk, hsotg->regs + HCINT(hc_num)); - - /* Enable channel interrupts required for this transfer */ - dwc2_hc_enable_ints(hsotg, chan); - - /* - * Program the HCCHARn register with the endpoint characteristics for - * the current transfer - */ - hcchar = chan->dev_addr << HCCHAR_DEVADDR_SHIFT & HCCHAR_DEVADDR_MASK; - hcchar |= chan->ep_num << HCCHAR_EPNUM_SHIFT & HCCHAR_EPNUM_MASK; - if (chan->ep_is_in) - hcchar |= HCCHAR_EPDIR; - if (chan->speed == USB_SPEED_LOW) - hcchar |= HCCHAR_LSPDDEV; - hcchar |= chan->ep_type << HCCHAR_EPTYPE_SHIFT & HCCHAR_EPTYPE_MASK; - hcchar |= chan->max_packet << HCCHAR_MPS_SHIFT & HCCHAR_MPS_MASK; - dwc2_writel(hcchar, hsotg->regs + HCCHAR(hc_num)); - if (dbg_hc(chan)) { - dev_vdbg(hsotg->dev, "set HCCHAR(%d) to %08x\n", - hc_num, hcchar); - - dev_vdbg(hsotg->dev, "%s: Channel %d\n", - __func__, hc_num); - dev_vdbg(hsotg->dev, " Dev Addr: %d\n", - chan->dev_addr); - dev_vdbg(hsotg->dev, " Ep Num: %d\n", - chan->ep_num); - dev_vdbg(hsotg->dev, " Is In: %d\n", - chan->ep_is_in); - dev_vdbg(hsotg->dev, " Is Low Speed: %d\n", - chan->speed == USB_SPEED_LOW); - dev_vdbg(hsotg->dev, " Ep Type: %d\n", - chan->ep_type); - dev_vdbg(hsotg->dev, " Max Pkt: %d\n", - chan->max_packet); - } - - /* Program the HCSPLT register for SPLITs */ - if (chan->do_split) { - if (dbg_hc(chan)) - dev_vdbg(hsotg->dev, - "Programming HC %d with split --> %s\n", - hc_num, - chan->complete_split ? "CSPLIT" : "SSPLIT"); - if (chan->complete_split) - hcsplt |= HCSPLT_COMPSPLT; - hcsplt |= chan->xact_pos << HCSPLT_XACTPOS_SHIFT & - HCSPLT_XACTPOS_MASK; - hcsplt |= chan->hub_addr << HCSPLT_HUBADDR_SHIFT & - HCSPLT_HUBADDR_MASK; - hcsplt |= chan->hub_port << HCSPLT_PRTADDR_SHIFT & - HCSPLT_PRTADDR_MASK; - if (dbg_hc(chan)) { - dev_vdbg(hsotg->dev, " comp split %d\n", - chan->complete_split); - dev_vdbg(hsotg->dev, " xact pos %d\n", - chan->xact_pos); - dev_vdbg(hsotg->dev, " hub addr %d\n", - chan->hub_addr); - dev_vdbg(hsotg->dev, " hub port %d\n", - chan->hub_port); - dev_vdbg(hsotg->dev, " is_in %d\n", - chan->ep_is_in); - dev_vdbg(hsotg->dev, " Max Pkt %d\n", - chan->max_packet); - dev_vdbg(hsotg->dev, " xferlen %d\n", - chan->xfer_len); - } - } - - dwc2_writel(hcsplt, hsotg->regs + HCSPLT(hc_num)); -} - -/** - * dwc2_hc_halt() - Attempts to halt a host channel - * - * @hsotg: Controller register interface - * @chan: Host channel to halt - * @halt_status: Reason for halting the channel - * - * This function should only be called in Slave mode or to abort a transfer in - * either Slave mode or DMA mode. Under normal circumstances in DMA mode, the - * controller halts the channel when the transfer is complete or a condition - * occurs that requires application intervention. - * - * In slave mode, checks for a free request queue entry, then sets the Channel - * Enable and Channel Disable bits of the Host Channel Characteristics - * register of the specified channel to intiate the halt. If there is no free - * request queue entry, sets only the Channel Disable bit of the HCCHARn - * register to flush requests for this channel. In the latter case, sets a - * flag to indicate that the host channel needs to be halted when a request - * queue slot is open. - * - * In DMA mode, always sets the Channel Enable and Channel Disable bits of the - * HCCHARn register. The controller ensures there is space in the request - * queue before submitting the halt request. - * - * Some time may elapse before the core flushes any posted requests for this - * host channel and halts. The Channel Halted interrupt handler completes the - * deactivation of the host channel. - */ -void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan, - enum dwc2_halt_status halt_status) -{ - u32 nptxsts, hptxsts, hcchar; - - if (dbg_hc(chan)) - dev_vdbg(hsotg->dev, "%s()\n", __func__); - if (halt_status == DWC2_HC_XFER_NO_HALT_STATUS) - dev_err(hsotg->dev, "!!! halt_status = %d !!!\n", halt_status); - - if (halt_status == DWC2_HC_XFER_URB_DEQUEUE || - halt_status == DWC2_HC_XFER_AHB_ERR) { - /* - * Disable all channel interrupts except Ch Halted. The QTD - * and QH state associated with this transfer has been cleared - * (in the case of URB_DEQUEUE), so the channel needs to be - * shut down carefully to prevent crashes. - */ - u32 hcintmsk = HCINTMSK_CHHLTD; - - dev_vdbg(hsotg->dev, "dequeue/error\n"); - dwc2_writel(hcintmsk, hsotg->regs + HCINTMSK(chan->hc_num)); - - /* - * Make sure no other interrupts besides halt are currently - * pending. Handling another interrupt could cause a crash due - * to the QTD and QH state. - */ - dwc2_writel(~hcintmsk, hsotg->regs + HCINT(chan->hc_num)); - - /* - * Make sure the halt status is set to URB_DEQUEUE or AHB_ERR - * even if the channel was already halted for some other - * reason - */ - chan->halt_status = halt_status; - - hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num)); - if (!(hcchar & HCCHAR_CHENA)) { - /* - * The channel is either already halted or it hasn't - * started yet. In DMA mode, the transfer may halt if - * it finishes normally or a condition occurs that - * requires driver intervention. Don't want to halt - * the channel again. In either Slave or DMA mode, - * it's possible that the transfer has been assigned - * to a channel, but not started yet when an URB is - * dequeued. Don't want to halt a channel that hasn't - * started yet. - */ - return; - } - } - if (chan->halt_pending) { - /* - * A halt has already been issued for this channel. This might - * happen when a transfer is aborted by a higher level in - * the stack. - */ - dev_vdbg(hsotg->dev, - "*** %s: Channel %d, chan->halt_pending already set ***\n", - __func__, chan->hc_num); - return; - } - - hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num)); - - /* No need to set the bit in DDMA for disabling the channel */ - /* TODO check it everywhere channel is disabled */ - if (hsotg->core_params->dma_desc_enable <= 0) { - if (dbg_hc(chan)) - dev_vdbg(hsotg->dev, "desc DMA disabled\n"); - hcchar |= HCCHAR_CHENA; - } else { - if (dbg_hc(chan)) - dev_dbg(hsotg->dev, "desc DMA enabled\n"); - } - hcchar |= HCCHAR_CHDIS; - - if (hsotg->core_params->dma_enable <= 0) { - if (dbg_hc(chan)) - dev_vdbg(hsotg->dev, "DMA not enabled\n"); - hcchar |= HCCHAR_CHENA; - - /* Check for space in the request queue to issue the halt */ - if (chan->ep_type == USB_ENDPOINT_XFER_CONTROL || - chan->ep_type == USB_ENDPOINT_XFER_BULK) { - dev_vdbg(hsotg->dev, "control/bulk\n"); - nptxsts = dwc2_readl(hsotg->regs + GNPTXSTS); - if ((nptxsts & TXSTS_QSPCAVAIL_MASK) == 0) { - dev_vdbg(hsotg->dev, "Disabling channel\n"); - hcchar &= ~HCCHAR_CHENA; - } - } else { - if (dbg_perio()) - dev_vdbg(hsotg->dev, "isoc/intr\n"); - hptxsts = dwc2_readl(hsotg->regs + HPTXSTS); - if ((hptxsts & TXSTS_QSPCAVAIL_MASK) == 0 || - hsotg->queuing_high_bandwidth) { - if (dbg_perio()) - dev_vdbg(hsotg->dev, "Disabling channel\n"); - hcchar &= ~HCCHAR_CHENA; - } - } - } else { - if (dbg_hc(chan)) - dev_vdbg(hsotg->dev, "DMA enabled\n"); - } - - dwc2_writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num)); - chan->halt_status = halt_status; - - if (hcchar & HCCHAR_CHENA) { - if (dbg_hc(chan)) - dev_vdbg(hsotg->dev, "Channel enabled\n"); - chan->halt_pending = 1; - chan->halt_on_queue = 0; - } else { - if (dbg_hc(chan)) - dev_vdbg(hsotg->dev, "Channel disabled\n"); - chan->halt_on_queue = 1; - } - - if (dbg_hc(chan)) { - dev_vdbg(hsotg->dev, "%s: Channel %d\n", __func__, - chan->hc_num); - dev_vdbg(hsotg->dev, " hcchar: 0x%08x\n", - hcchar); - dev_vdbg(hsotg->dev, " halt_pending: %d\n", - chan->halt_pending); - dev_vdbg(hsotg->dev, " halt_on_queue: %d\n", - chan->halt_on_queue); - dev_vdbg(hsotg->dev, " halt_status: %d\n", - chan->halt_status); - } -} - -/** - * dwc2_hc_cleanup() - Clears the transfer state for a host channel - * - * @hsotg: Programming view of DWC_otg controller - * @chan: Identifies the host channel to clean up - * - * This function is normally called after a transfer is done and the host - * channel is being released - */ -void dwc2_hc_cleanup(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan) -{ - u32 hcintmsk; - - chan->xfer_started = 0; - - /* - * Clear channel interrupt enables and any unhandled channel interrupt - * conditions - */ - dwc2_writel(0, hsotg->regs + HCINTMSK(chan->hc_num)); - hcintmsk = 0xffffffff; - hcintmsk &= ~HCINTMSK_RESERVED14_31; - dwc2_writel(hcintmsk, hsotg->regs + HCINT(chan->hc_num)); -} - -/** - * dwc2_hc_set_even_odd_frame() - Sets the channel property that indicates in - * which frame a periodic transfer should occur - * - * @hsotg: Programming view of DWC_otg controller - * @chan: Identifies the host channel to set up and its properties - * @hcchar: Current value of the HCCHAR register for the specified host channel - * - * This function has no effect on non-periodic transfers - */ -static void dwc2_hc_set_even_odd_frame(struct dwc2_hsotg *hsotg, - struct dwc2_host_chan *chan, u32 *hcchar) -{ - if (chan->ep_type == USB_ENDPOINT_XFER_INT || - chan->ep_type == USB_ENDPOINT_XFER_ISOC) { - /* 1 if _next_ frame is odd, 0 if it's even */ - if (!(dwc2_hcd_get_frame_number(hsotg) & 0x1)) - *hcchar |= HCCHAR_ODDFRM; - } -} - -static void dwc2_set_pid_isoc(struct dwc2_host_chan *chan) -{ - /* Set up the initial PID for the transfer */ - if (chan->speed == USB_SPEED_HIGH) { - if (chan->ep_is_in) { - if (chan->multi_count == 1) - chan->data_pid_start = DWC2_HC_PID_DATA0; - else if (chan->multi_count == 2) - chan->data_pid_start = DWC2_HC_PID_DATA1; - else - chan->data_pid_start = DWC2_HC_PID_DATA2; - } else { - if (chan->multi_count == 1) - chan->data_pid_start = DWC2_HC_PID_DATA0; - else - chan->data_pid_start = DWC2_HC_PID_MDATA; - } - } else { - chan->data_pid_start = DWC2_HC_PID_DATA0; - } -} - -/** - * dwc2_hc_write_packet() - Writes a packet into the Tx FIFO associated with - * the Host Channel - * - * @hsotg: Programming view of DWC_otg controller - * @chan: Information needed to initialize the host channel - * - * This function should only be called in Slave mode. For a channel associated - * with a non-periodic EP, the non-periodic Tx FIFO is written. For a channel - * associated with a periodic EP, the periodic Tx FIFO is written. - * - * Upon return the xfer_buf and xfer_count fields in chan are incremented by - * the number of bytes written to the Tx FIFO. - */ -static void dwc2_hc_write_packet(struct dwc2_hsotg *hsotg, - struct dwc2_host_chan *chan) -{ - u32 i; - u32 remaining_count; - u32 byte_count; - u32 dword_count; - u32 __iomem *data_fifo; - u32 *data_buf = (u32 *)chan->xfer_buf; - - if (dbg_hc(chan)) - dev_vdbg(hsotg->dev, "%s()\n", __func__); - - data_fifo = (u32 __iomem *)(hsotg->regs + HCFIFO(chan->hc_num)); - - remaining_count = chan->xfer_len - chan->xfer_count; - if (remaining_count > chan->max_packet) - byte_count = chan->max_packet; - else - byte_count = remaining_count; - - dword_count = (byte_count + 3) / 4; - - if (((unsigned long)data_buf & 0x3) == 0) { - /* xfer_buf is DWORD aligned */ - for (i = 0; i < dword_count; i++, data_buf++) - dwc2_writel(*data_buf, data_fifo); - } else { - /* xfer_buf is not DWORD aligned */ - for (i = 0; i < dword_count; i++, data_buf++) { - u32 data = data_buf[0] | data_buf[1] << 8 | - data_buf[2] << 16 | data_buf[3] << 24; - dwc2_writel(data, data_fifo); - } - } - - chan->xfer_count += byte_count; - chan->xfer_buf += byte_count; -} - -/** - * dwc2_hc_start_transfer() - Does the setup for a data transfer for a host - * channel and starts the transfer - * - * @hsotg: Programming view of DWC_otg controller - * @chan: Information needed to initialize the host channel. The xfer_len value - * may be reduced to accommodate the max widths of the XferSize and - * PktCnt fields in the HCTSIZn register. The multi_count value may be - * changed to reflect the final xfer_len value. - * - * This function may be called in either Slave mode or DMA mode. In Slave mode, - * the caller must ensure that there is sufficient space in the request queue - * and Tx Data FIFO. - * - * For an OUT transfer in Slave mode, it loads a data packet into the - * appropriate FIFO. If necessary, additional data packets are loaded in the - * Host ISR. - * - * For an IN transfer in Slave mode, a data packet is requested. The data - * packets are unloaded from the Rx FIFO in the Host ISR. If necessary, - * additional data packets are requested in the Host ISR. - * - * For a PING transfer in Slave mode, the Do Ping bit is set in the HCTSIZ - * register along with a packet count of 1 and the channel is enabled. This - * causes a single PING transaction to occur. Other fields in HCTSIZ are - * simply set to 0 since no data transfer occurs in this case. - * - * For a PING transfer in DMA mode, the HCTSIZ register is initialized with - * all the information required to perform the subsequent data transfer. In - * addition, the Do Ping bit is set in the HCTSIZ register. In this case, the - * controller performs the entire PING protocol, then starts the data - * transfer. - */ -void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg, - struct dwc2_host_chan *chan) -{ - u32 max_hc_xfer_size = hsotg->core_params->max_transfer_size; - u16 max_hc_pkt_count = hsotg->core_params->max_packet_count; - u32 hcchar; - u32 hctsiz = 0; - u16 num_packets; - u32 ec_mc; - - if (dbg_hc(chan)) - dev_vdbg(hsotg->dev, "%s()\n", __func__); - - if (chan->do_ping) { - if (hsotg->core_params->dma_enable <= 0) { - if (dbg_hc(chan)) - dev_vdbg(hsotg->dev, "ping, no DMA\n"); - dwc2_hc_do_ping(hsotg, chan); - chan->xfer_started = 1; - return; - } else { - if (dbg_hc(chan)) - dev_vdbg(hsotg->dev, "ping, DMA\n"); - hctsiz |= TSIZ_DOPNG; - } - } - - if (chan->do_split) { - if (dbg_hc(chan)) - dev_vdbg(hsotg->dev, "split\n"); - num_packets = 1; - - if (chan->complete_split && !chan->ep_is_in) - /* - * For CSPLIT OUT Transfer, set the size to 0 so the - * core doesn't expect any data written to the FIFO - */ - chan->xfer_len = 0; - else if (chan->ep_is_in || chan->xfer_len > chan->max_packet) - chan->xfer_len = chan->max_packet; - else if (!chan->ep_is_in && chan->xfer_len > 188) - chan->xfer_len = 188; - - hctsiz |= chan->xfer_len << TSIZ_XFERSIZE_SHIFT & - TSIZ_XFERSIZE_MASK; - - /* For split set ec_mc for immediate retries */ - if (chan->ep_type == USB_ENDPOINT_XFER_INT || - chan->ep_type == USB_ENDPOINT_XFER_ISOC) - ec_mc = 3; - else - ec_mc = 1; - } else { - if (dbg_hc(chan)) - dev_vdbg(hsotg->dev, "no split\n"); - /* - * Ensure that the transfer length and packet count will fit - * in the widths allocated for them in the HCTSIZn register - */ - if (chan->ep_type == USB_ENDPOINT_XFER_INT || - chan->ep_type == USB_ENDPOINT_XFER_ISOC) { - /* - * Make sure the transfer size is no larger than one - * (micro)frame's worth of data. (A check was done - * when the periodic transfer was accepted to ensure - * that a (micro)frame's worth of data can be - * programmed into a channel.) - */ - u32 max_periodic_len = - chan->multi_count * chan->max_packet; - - if (chan->xfer_len > max_periodic_len) - chan->xfer_len = max_periodic_len; - } else if (chan->xfer_len > max_hc_xfer_size) { - /* - * Make sure that xfer_len is a multiple of max packet - * size - */ - chan->xfer_len = - max_hc_xfer_size - chan->max_packet + 1; - } - - if (chan->xfer_len > 0) { - num_packets = (chan->xfer_len + chan->max_packet - 1) / - chan->max_packet; - if (num_packets > max_hc_pkt_count) { - num_packets = max_hc_pkt_count; - chan->xfer_len = num_packets * chan->max_packet; - } - } else { - /* Need 1 packet for transfer length of 0 */ - num_packets = 1; - } - - if (chan->ep_is_in) - /* - * Always program an integral # of max packets for IN - * transfers - */ - chan->xfer_len = num_packets * chan->max_packet; - - if (chan->ep_type == USB_ENDPOINT_XFER_INT || - chan->ep_type == USB_ENDPOINT_XFER_ISOC) - /* - * Make sure that the multi_count field matches the - * actual transfer length - */ - chan->multi_count = num_packets; - - if (chan->ep_type == USB_ENDPOINT_XFER_ISOC) - dwc2_set_pid_isoc(chan); - - hctsiz |= chan->xfer_len << TSIZ_XFERSIZE_SHIFT & - TSIZ_XFERSIZE_MASK; - - /* The ec_mc gets the multi_count for non-split */ - ec_mc = chan->multi_count; - } - - chan->start_pkt_count = num_packets; - hctsiz |= num_packets << TSIZ_PKTCNT_SHIFT & TSIZ_PKTCNT_MASK; - hctsiz |= chan->data_pid_start << TSIZ_SC_MC_PID_SHIFT & - TSIZ_SC_MC_PID_MASK; - dwc2_writel(hctsiz, hsotg->regs + HCTSIZ(chan->hc_num)); - if (dbg_hc(chan)) { - dev_vdbg(hsotg->dev, "Wrote %08x to HCTSIZ(%d)\n", - hctsiz, chan->hc_num); - - dev_vdbg(hsotg->dev, "%s: Channel %d\n", __func__, - chan->hc_num); - dev_vdbg(hsotg->dev, " Xfer Size: %d\n", - (hctsiz & TSIZ_XFERSIZE_MASK) >> - TSIZ_XFERSIZE_SHIFT); - dev_vdbg(hsotg->dev, " Num Pkts: %d\n", - (hctsiz & TSIZ_PKTCNT_MASK) >> - TSIZ_PKTCNT_SHIFT); - dev_vdbg(hsotg->dev, " Start PID: %d\n", - (hctsiz & TSIZ_SC_MC_PID_MASK) >> - TSIZ_SC_MC_PID_SHIFT); - } - - if (hsotg->core_params->dma_enable > 0) { - dma_addr_t dma_addr; - - if (chan->align_buf) { - if (dbg_hc(chan)) - dev_vdbg(hsotg->dev, "align_buf\n"); - dma_addr = chan->align_buf; - } else { - dma_addr = chan->xfer_dma; - } - dwc2_writel((u32)dma_addr, hsotg->regs + HCDMA(chan->hc_num)); - if (dbg_hc(chan)) - dev_vdbg(hsotg->dev, "Wrote %08lx to HCDMA(%d)\n", - (unsigned long)dma_addr, chan->hc_num); - } - - /* Start the split */ - if (chan->do_split) { - u32 hcsplt = dwc2_readl(hsotg->regs + HCSPLT(chan->hc_num)); - - hcsplt |= HCSPLT_SPLTENA; - dwc2_writel(hcsplt, hsotg->regs + HCSPLT(chan->hc_num)); - } - - hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num)); - hcchar &= ~HCCHAR_MULTICNT_MASK; - hcchar |= (ec_mc << HCCHAR_MULTICNT_SHIFT) & HCCHAR_MULTICNT_MASK; - dwc2_hc_set_even_odd_frame(hsotg, chan, &hcchar); - - if (hcchar & HCCHAR_CHDIS) - dev_warn(hsotg->dev, - "%s: chdis set, channel %d, hcchar 0x%08x\n", - __func__, chan->hc_num, hcchar); - - /* Set host channel enable after all other setup is complete */ - hcchar |= HCCHAR_CHENA; - hcchar &= ~HCCHAR_CHDIS; - - if (dbg_hc(chan)) - dev_vdbg(hsotg->dev, " Multi Cnt: %d\n", - (hcchar & HCCHAR_MULTICNT_MASK) >> - HCCHAR_MULTICNT_SHIFT); - - dwc2_writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num)); - if (dbg_hc(chan)) - dev_vdbg(hsotg->dev, "Wrote %08x to HCCHAR(%d)\n", hcchar, - chan->hc_num); - - chan->xfer_started = 1; - chan->requests++; - - if (hsotg->core_params->dma_enable <= 0 && - !chan->ep_is_in && chan->xfer_len > 0) - /* Load OUT packet into the appropriate Tx FIFO */ - dwc2_hc_write_packet(hsotg, chan); -} - -/** - * dwc2_hc_start_transfer_ddma() - Does the setup for a data transfer for a - * host channel and starts the transfer in Descriptor DMA mode - * - * @hsotg: Programming view of DWC_otg controller - * @chan: Information needed to initialize the host channel - * - * Initializes HCTSIZ register. For a PING transfer the Do Ping bit is set. - * Sets PID and NTD values. For periodic transfers initializes SCHED_INFO field - * with micro-frame bitmap. - * - * Initializes HCDMA register with descriptor list address and CTD value then - * starts the transfer via enabling the channel. - */ -void dwc2_hc_start_transfer_ddma(struct dwc2_hsotg *hsotg, - struct dwc2_host_chan *chan) -{ - u32 hcchar; - u32 hctsiz = 0; - - if (chan->do_ping) - hctsiz |= TSIZ_DOPNG; - - if (chan->ep_type == USB_ENDPOINT_XFER_ISOC) - dwc2_set_pid_isoc(chan); - - /* Packet Count and Xfer Size are not used in Descriptor DMA mode */ - hctsiz |= chan->data_pid_start << TSIZ_SC_MC_PID_SHIFT & - TSIZ_SC_MC_PID_MASK; - - /* 0 - 1 descriptor, 1 - 2 descriptors, etc */ - hctsiz |= (chan->ntd - 1) << TSIZ_NTD_SHIFT & TSIZ_NTD_MASK; - - /* Non-zero only for high-speed interrupt endpoints */ - hctsiz |= chan->schinfo << TSIZ_SCHINFO_SHIFT & TSIZ_SCHINFO_MASK; - - if (dbg_hc(chan)) { - dev_vdbg(hsotg->dev, "%s: Channel %d\n", __func__, - chan->hc_num); - dev_vdbg(hsotg->dev, " Start PID: %d\n", - chan->data_pid_start); - dev_vdbg(hsotg->dev, " NTD: %d\n", chan->ntd - 1); - } - - dwc2_writel(hctsiz, hsotg->regs + HCTSIZ(chan->hc_num)); - - dma_sync_single_for_device(hsotg->dev, chan->desc_list_addr, - chan->desc_list_sz, DMA_TO_DEVICE); - - dwc2_writel(chan->desc_list_addr, hsotg->regs + HCDMA(chan->hc_num)); - - if (dbg_hc(chan)) - dev_vdbg(hsotg->dev, "Wrote %pad to HCDMA(%d)\n", - &chan->desc_list_addr, chan->hc_num); - - hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num)); - hcchar &= ~HCCHAR_MULTICNT_MASK; - hcchar |= chan->multi_count << HCCHAR_MULTICNT_SHIFT & - HCCHAR_MULTICNT_MASK; - - if (hcchar & HCCHAR_CHDIS) - dev_warn(hsotg->dev, - "%s: chdis set, channel %d, hcchar 0x%08x\n", - __func__, chan->hc_num, hcchar); - - /* Set host channel enable after all other setup is complete */ - hcchar |= HCCHAR_CHENA; - hcchar &= ~HCCHAR_CHDIS; - - if (dbg_hc(chan)) - dev_vdbg(hsotg->dev, " Multi Cnt: %d\n", - (hcchar & HCCHAR_MULTICNT_MASK) >> - HCCHAR_MULTICNT_SHIFT); - - dwc2_writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num)); - if (dbg_hc(chan)) - dev_vdbg(hsotg->dev, "Wrote %08x to HCCHAR(%d)\n", hcchar, - chan->hc_num); - - chan->xfer_started = 1; - chan->requests++; -} - -/** - * dwc2_hc_continue_transfer() - Continues a data transfer that was started by - * a previous call to dwc2_hc_start_transfer() - * - * @hsotg: Programming view of DWC_otg controller - * @chan: Information needed to initialize the host channel - * - * The caller must ensure there is sufficient space in the request queue and Tx - * Data FIFO. This function should only be called in Slave mode. In DMA mode, - * the controller acts autonomously to complete transfers programmed to a host - * channel. - * - * For an OUT transfer, a new data packet is loaded into the appropriate FIFO - * if there is any data remaining to be queued. For an IN transfer, another - * data packet is always requested. For the SETUP phase of a control transfer, - * this function does nothing. - * - * Return: 1 if a new request is queued, 0 if no more requests are required - * for this transfer - */ -int dwc2_hc_continue_transfer(struct dwc2_hsotg *hsotg, - struct dwc2_host_chan *chan) -{ - if (dbg_hc(chan)) - dev_vdbg(hsotg->dev, "%s: Channel %d\n", __func__, - chan->hc_num); - - if (chan->do_split) - /* SPLITs always queue just once per channel */ - return 0; - - if (chan->data_pid_start == DWC2_HC_PID_SETUP) - /* SETUPs are queued only once since they can't be NAK'd */ - return 0; - - if (chan->ep_is_in) { - /* - * Always queue another request for other IN transfers. If - * back-to-back INs are issued and NAKs are received for both, - * the driver may still be processing the first NAK when the - * second NAK is received. When the interrupt handler clears - * the NAK interrupt for the first NAK, the second NAK will - * not be seen. So we can't depend on the NAK interrupt - * handler to requeue a NAK'd request. Instead, IN requests - * are issued each time this function is called. When the - * transfer completes, the extra requests for the channel will - * be flushed. - */ - u32 hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num)); - - dwc2_hc_set_even_odd_frame(hsotg, chan, &hcchar); - hcchar |= HCCHAR_CHENA; - hcchar &= ~HCCHAR_CHDIS; - if (dbg_hc(chan)) - dev_vdbg(hsotg->dev, " IN xfer: hcchar = 0x%08x\n", - hcchar); - dwc2_writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num)); - chan->requests++; - return 1; - } - - /* OUT transfers */ - - if (chan->xfer_count < chan->xfer_len) { - if (chan->ep_type == USB_ENDPOINT_XFER_INT || - chan->ep_type == USB_ENDPOINT_XFER_ISOC) { - u32 hcchar = dwc2_readl(hsotg->regs + - HCCHAR(chan->hc_num)); - - dwc2_hc_set_even_odd_frame(hsotg, chan, - &hcchar); - } - - /* Load OUT packet into the appropriate Tx FIFO */ - dwc2_hc_write_packet(hsotg, chan); - chan->requests++; - return 1; - } - - return 0; -} - -/** - * dwc2_hc_do_ping() - Starts a PING transfer - * - * @hsotg: Programming view of DWC_otg controller - * @chan: Information needed to initialize the host channel - * - * This function should only be called in Slave mode. The Do Ping bit is set in - * the HCTSIZ register, then the channel is enabled. - */ -void dwc2_hc_do_ping(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan) -{ - u32 hcchar; - u32 hctsiz; - - if (dbg_hc(chan)) - dev_vdbg(hsotg->dev, "%s: Channel %d\n", __func__, - chan->hc_num); - - - hctsiz = TSIZ_DOPNG; - hctsiz |= 1 << TSIZ_PKTCNT_SHIFT; - dwc2_writel(hctsiz, hsotg->regs + HCTSIZ(chan->hc_num)); - - hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num)); - hcchar |= HCCHAR_CHENA; - hcchar &= ~HCCHAR_CHDIS; - dwc2_writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num)); -} - -/** - * dwc2_calc_frame_interval() - Calculates the correct frame Interval value for - * the HFIR register according to PHY type and speed - * - * @hsotg: Programming view of DWC_otg controller - * - * NOTE: The caller can modify the value of the HFIR register only after the - * Port Enable bit of the Host Port Control and Status register (HPRT.EnaPort) - * has been set - */ -u32 dwc2_calc_frame_interval(struct dwc2_hsotg *hsotg) -{ - u32 usbcfg; - u32 hprt0; - int clock = 60; /* default value */ - - usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); - hprt0 = dwc2_readl(hsotg->regs + HPRT0); - - if (!(usbcfg & GUSBCFG_PHYSEL) && (usbcfg & GUSBCFG_ULPI_UTMI_SEL) && - !(usbcfg & GUSBCFG_PHYIF16)) - clock = 60; - if ((usbcfg & GUSBCFG_PHYSEL) && hsotg->hw_params.fs_phy_type == - GHWCFG2_FS_PHY_TYPE_SHARED_ULPI) - clock = 48; - if (!(usbcfg & GUSBCFG_PHY_LP_CLK_SEL) && !(usbcfg & GUSBCFG_PHYSEL) && - !(usbcfg & GUSBCFG_ULPI_UTMI_SEL) && (usbcfg & GUSBCFG_PHYIF16)) - clock = 30; - if (!(usbcfg & GUSBCFG_PHY_LP_CLK_SEL) && !(usbcfg & GUSBCFG_PHYSEL) && - !(usbcfg & GUSBCFG_ULPI_UTMI_SEL) && !(usbcfg & GUSBCFG_PHYIF16)) - clock = 60; - if ((usbcfg & GUSBCFG_PHY_LP_CLK_SEL) && !(usbcfg & GUSBCFG_PHYSEL) && - !(usbcfg & GUSBCFG_ULPI_UTMI_SEL) && (usbcfg & GUSBCFG_PHYIF16)) - clock = 48; - if ((usbcfg & GUSBCFG_PHYSEL) && !(usbcfg & GUSBCFG_PHYIF16) && - hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_SHARED_UTMI) - clock = 48; - if ((usbcfg & GUSBCFG_PHYSEL) && - hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED) - clock = 48; - - if ((hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT == HPRT0_SPD_HIGH_SPEED) - /* High speed case */ - return 125 * clock; - else - /* FS/LS case */ - return 1000 * clock; -} - -/** - * dwc2_read_packet() - Reads a packet from the Rx FIFO into the destination - * buffer - * - * @core_if: Programming view of DWC_otg controller - * @dest: Destination buffer for the packet - * @bytes: Number of bytes to copy to the destination - */ -void dwc2_read_packet(struct dwc2_hsotg *hsotg, u8 *dest, u16 bytes) -{ - u32 __iomem *fifo = hsotg->regs + HCFIFO(0); - u32 *data_buf = (u32 *)dest; - int word_count = (bytes + 3) / 4; - int i; - - /* - * Todo: Account for the case where dest is not dword aligned. This - * requires reading data from the FIFO into a u32 temp buffer, then - * moving it into the data buffer. - */ - - dev_vdbg(hsotg->dev, "%s(%p,%p,%d)\n", __func__, hsotg, dest, bytes); - - for (i = 0; i < word_count; i++, data_buf++) - *data_buf = dwc2_readl(fifo); -} - /** * dwc2_dump_host_registers() - Prints the host registers * @@ -3355,13 +1478,6 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg) width = (hwcfg3 & GHWCFG3_XFER_SIZE_CNTR_WIDTH_MASK) >> GHWCFG3_XFER_SIZE_CNTR_WIDTH_SHIFT; hw->max_transfer_size = (1 << (width + 11)) - 1; - /* - * Clip max_transfer_size to 65535. dwc2_hc_setup_align_buf() allocates - * coherent buffers with this size, and if it's too large we can - * exhaust the coherent DMA pool. - */ - if (hw->max_transfer_size > 65535) - hw->max_transfer_size = 65535; width = (hwcfg3 & GHWCFG3_PACKET_SIZE_CNTR_WIDTH_MASK) >> GHWCFG3_PACKET_SIZE_CNTR_WIDTH_SHIFT; hw->max_packet_count = (1 << (width + 4)) - 1; diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 7fb6434..3c58d63 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -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__ */ diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 422ab7d..e9940dd 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -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; +} diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 8847c72..1f62551 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -54,6 +54,535 @@ #include "core.h" #include "hcd.h" +/* + * ========================================================================= + * Host Core Layer Functions + * ========================================================================= + */ + +/** + * dwc2_enable_common_interrupts() - Initializes the commmon interrupts, + * used in both device and host modes + * + * @hsotg: Programming view of the DWC_otg controller + */ +static void dwc2_enable_common_interrupts(struct dwc2_hsotg *hsotg) +{ + u32 intmsk; + + /* Clear any pending OTG Interrupts */ + dwc2_writel(0xffffffff, hsotg->regs + GOTGINT); + + /* Clear any pending interrupts */ + dwc2_writel(0xffffffff, hsotg->regs + GINTSTS); + + /* Enable the interrupts in the GINTMSK */ + intmsk = GINTSTS_MODEMIS | GINTSTS_OTGINT; + + if (hsotg->core_params->dma_enable <= 0) + intmsk |= GINTSTS_RXFLVL; + if (hsotg->core_params->external_id_pin_ctl <= 0) + intmsk |= GINTSTS_CONIDSTSCHNG; + + intmsk |= GINTSTS_WKUPINT | GINTSTS_USBSUSP | + GINTSTS_SESSREQINT; + + dwc2_writel(intmsk, hsotg->regs + GINTMSK); +} + +/* + * Initializes the FSLSPClkSel field of the HCFG register depending on the + * PHY type + */ +static void dwc2_init_fs_ls_pclk_sel(struct dwc2_hsotg *hsotg) +{ + u32 hcfg, val; + + if ((hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI && + hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED && + hsotg->core_params->ulpi_fs_ls > 0) || + hsotg->core_params->phy_type == DWC2_PHY_TYPE_PARAM_FS) { + /* Full speed PHY */ + val = HCFG_FSLSPCLKSEL_48_MHZ; + } else { + /* High speed PHY running at full speed or high speed */ + val = HCFG_FSLSPCLKSEL_30_60_MHZ; + } + + dev_dbg(hsotg->dev, "Initializing HCFG.FSLSPClkSel to %08x\n", val); + hcfg = dwc2_readl(hsotg->regs + HCFG); + hcfg &= ~HCFG_FSLSPCLKSEL_MASK; + hcfg |= val << HCFG_FSLSPCLKSEL_SHIFT; + dwc2_writel(hcfg, hsotg->regs + HCFG); +} + +static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) +{ + u32 usbcfg, i2cctl; + int retval = 0; + + /* + * core_init() is now called on every switch so only call the + * following for the first time through + */ + if (select_phy) { + dev_dbg(hsotg->dev, "FS PHY selected\n"); + + usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); + if (!(usbcfg & GUSBCFG_PHYSEL)) { + usbcfg |= GUSBCFG_PHYSEL; + dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); + + /* Reset after a PHY select */ + retval = dwc2_core_reset_and_force_dr_mode(hsotg); + + if (retval) { + dev_err(hsotg->dev, + "%s: Reset failed, aborting", __func__); + return retval; + } + } + } + + /* + * Program DCFG.DevSpd or HCFG.FSLSPclkSel to 48Mhz in FS. Also + * do this on HNP Dev/Host mode switches (done in dev_init and + * host_init). + */ + if (dwc2_is_host_mode(hsotg)) + dwc2_init_fs_ls_pclk_sel(hsotg); + + if (hsotg->core_params->i2c_enable > 0) { + dev_dbg(hsotg->dev, "FS PHY enabling I2C\n"); + + /* Program GUSBCFG.OtgUtmiFsSel to I2C */ + usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); + usbcfg |= GUSBCFG_OTG_UTMI_FS_SEL; + dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); + + /* Program GI2CCTL.I2CEn */ + i2cctl = dwc2_readl(hsotg->regs + GI2CCTL); + i2cctl &= ~GI2CCTL_I2CDEVADDR_MASK; + i2cctl |= 1 << GI2CCTL_I2CDEVADDR_SHIFT; + i2cctl &= ~GI2CCTL_I2CEN; + dwc2_writel(i2cctl, hsotg->regs + GI2CCTL); + i2cctl |= GI2CCTL_I2CEN; + dwc2_writel(i2cctl, hsotg->regs + GI2CCTL); + } + + return retval; +} + +static int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) +{ + u32 usbcfg, usbcfg_old; + int retval = 0; + + if (!select_phy) + return 0; + + usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); + usbcfg_old = usbcfg; + + /* + * HS PHY parameters. These parameters are preserved during soft reset + * so only program the first time. Do a soft reset immediately after + * setting phyif. + */ + switch (hsotg->core_params->phy_type) { + case DWC2_PHY_TYPE_PARAM_ULPI: + /* ULPI interface */ + dev_dbg(hsotg->dev, "HS ULPI PHY selected\n"); + usbcfg |= GUSBCFG_ULPI_UTMI_SEL; + usbcfg &= ~(GUSBCFG_PHYIF16 | GUSBCFG_DDRSEL); + if (hsotg->core_params->phy_ulpi_ddr > 0) + usbcfg |= GUSBCFG_DDRSEL; + break; + case DWC2_PHY_TYPE_PARAM_UTMI: + /* UTMI+ interface */ + dev_dbg(hsotg->dev, "HS UTMI+ PHY selected\n"); + usbcfg &= ~(GUSBCFG_ULPI_UTMI_SEL | GUSBCFG_PHYIF16); + if (hsotg->core_params->phy_utmi_width == 16) + usbcfg |= GUSBCFG_PHYIF16; + break; + default: + dev_err(hsotg->dev, "FS PHY selected at HS!\n"); + break; + } + + if (usbcfg != usbcfg_old) { + dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); + + /* Reset after setting the PHY parameters */ + retval = dwc2_core_reset_and_force_dr_mode(hsotg); + if (retval) { + dev_err(hsotg->dev, + "%s: Reset failed, aborting", __func__); + return retval; + } + } + + return retval; +} + +static int dwc2_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) +{ + u32 usbcfg; + int retval = 0; + + if (hsotg->core_params->speed == DWC2_SPEED_PARAM_FULL && + hsotg->core_params->phy_type == DWC2_PHY_TYPE_PARAM_FS) { + /* If FS mode with FS PHY */ + retval = dwc2_fs_phy_init(hsotg, select_phy); + if (retval) + return retval; + } else { + /* High speed PHY */ + retval = dwc2_hs_phy_init(hsotg, select_phy); + if (retval) + return retval; + } + + if (hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI && + hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED && + hsotg->core_params->ulpi_fs_ls > 0) { + dev_dbg(hsotg->dev, "Setting ULPI FSLS\n"); + usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); + usbcfg |= GUSBCFG_ULPI_FS_LS; + usbcfg |= GUSBCFG_ULPI_CLK_SUSP_M; + dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); + } else { + usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); + usbcfg &= ~GUSBCFG_ULPI_FS_LS; + usbcfg &= ~GUSBCFG_ULPI_CLK_SUSP_M; + dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); + } + + return retval; +} + +static int dwc2_gahbcfg_init(struct dwc2_hsotg *hsotg) +{ + u32 ahbcfg = dwc2_readl(hsotg->regs + GAHBCFG); + + switch (hsotg->hw_params.arch) { + case GHWCFG2_EXT_DMA_ARCH: + dev_err(hsotg->dev, "External DMA Mode not supported\n"); + return -EINVAL; + + case GHWCFG2_INT_DMA_ARCH: + dev_dbg(hsotg->dev, "Internal DMA Mode\n"); + if (hsotg->core_params->ahbcfg != -1) { + ahbcfg &= GAHBCFG_CTRL_MASK; + ahbcfg |= hsotg->core_params->ahbcfg & + ~GAHBCFG_CTRL_MASK; + } + break; + + case GHWCFG2_SLAVE_ONLY_ARCH: + default: + dev_dbg(hsotg->dev, "Slave Only Mode\n"); + break; + } + + dev_dbg(hsotg->dev, "dma_enable:%d dma_desc_enable:%d\n", + hsotg->core_params->dma_enable, + hsotg->core_params->dma_desc_enable); + + if (hsotg->core_params->dma_enable > 0) { + if (hsotg->core_params->dma_desc_enable > 0) + dev_dbg(hsotg->dev, "Using Descriptor DMA mode\n"); + else + dev_dbg(hsotg->dev, "Using Buffer DMA mode\n"); + } else { + dev_dbg(hsotg->dev, "Using Slave mode\n"); + hsotg->core_params->dma_desc_enable = 0; + } + + if (hsotg->core_params->dma_enable > 0) + ahbcfg |= GAHBCFG_DMA_EN; + + dwc2_writel(ahbcfg, hsotg->regs + GAHBCFG); + + return 0; +} + +static void dwc2_gusbcfg_init(struct dwc2_hsotg *hsotg) +{ + u32 usbcfg; + + usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); + usbcfg &= ~(GUSBCFG_HNPCAP | GUSBCFG_SRPCAP); + + switch (hsotg->hw_params.op_mode) { + case GHWCFG2_OP_MODE_HNP_SRP_CAPABLE: + if (hsotg->core_params->otg_cap == + DWC2_CAP_PARAM_HNP_SRP_CAPABLE) + usbcfg |= GUSBCFG_HNPCAP; + if (hsotg->core_params->otg_cap != + DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE) + usbcfg |= GUSBCFG_SRPCAP; + break; + + case GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE: + case GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE: + case GHWCFG2_OP_MODE_SRP_CAPABLE_HOST: + if (hsotg->core_params->otg_cap != + DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE) + usbcfg |= GUSBCFG_SRPCAP; + break; + + case GHWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE: + case GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE: + case GHWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST: + default: + break; + } + + dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); +} + +/** + * dwc2_enable_host_interrupts() - Enables the Host mode interrupts + * + * @hsotg: Programming view of DWC_otg controller + */ +static void dwc2_enable_host_interrupts(struct dwc2_hsotg *hsotg) +{ + u32 intmsk; + + dev_dbg(hsotg->dev, "%s()\n", __func__); + + /* Disable all interrupts */ + dwc2_writel(0, hsotg->regs + GINTMSK); + dwc2_writel(0, hsotg->regs + HAINTMSK); + + /* Enable the common interrupts */ + dwc2_enable_common_interrupts(hsotg); + + /* Enable host mode interrupts without disturbing common interrupts */ + intmsk = dwc2_readl(hsotg->regs + GINTMSK); + intmsk |= GINTSTS_DISCONNINT | GINTSTS_PRTINT | GINTSTS_HCHINT; + dwc2_writel(intmsk, hsotg->regs + GINTMSK); +} + +/** + * dwc2_disable_host_interrupts() - Disables the Host Mode interrupts + * + * @hsotg: Programming view of DWC_otg controller + */ +static void dwc2_disable_host_interrupts(struct dwc2_hsotg *hsotg) +{ + u32 intmsk = dwc2_readl(hsotg->regs + GINTMSK); + + /* Disable host mode interrupts without disturbing common interrupts */ + intmsk &= ~(GINTSTS_SOF | GINTSTS_PRTINT | GINTSTS_HCHINT | + GINTSTS_PTXFEMP | GINTSTS_NPTXFEMP | GINTSTS_DISCONNINT); + dwc2_writel(intmsk, hsotg->regs + GINTMSK); +} + +/* + * dwc2_calculate_dynamic_fifo() - Calculates the default fifo size + * For system that have a total fifo depth that is smaller than the default + * RX + TX fifo size. + * + * @hsotg: Programming view of DWC_otg controller + */ +static void dwc2_calculate_dynamic_fifo(struct dwc2_hsotg *hsotg) +{ + struct dwc2_core_params *params = hsotg->core_params; + struct dwc2_hw_params *hw = &hsotg->hw_params; + u32 rxfsiz, nptxfsiz, ptxfsiz, total_fifo_size; + + total_fifo_size = hw->total_fifo_size; + rxfsiz = params->host_rx_fifo_size; + nptxfsiz = params->host_nperio_tx_fifo_size; + ptxfsiz = params->host_perio_tx_fifo_size; + + /* + * Will use Method 2 defined in the DWC2 spec: minimum FIFO depth + * allocation with support for high bandwidth endpoints. Synopsys + * defines MPS(Max Packet size) for a periodic EP=1024, and for + * non-periodic as 512. + */ + if (total_fifo_size < (rxfsiz + nptxfsiz + ptxfsiz)) { + /* + * For Buffer DMA mode/Scatter Gather DMA mode + * 2 * ((Largest Packet size / 4) + 1 + 1) + n + * with n = number of host channel. + * 2 * ((1024/4) + 2) = 516 + */ + rxfsiz = 516 + hw->host_channels; + + /* + * min non-periodic tx fifo depth + * 2 * (largest non-periodic USB packet used / 4) + * 2 * (512/4) = 256 + */ + nptxfsiz = 256; + + /* + * min periodic tx fifo depth + * (largest packet size*MC)/4 + * (1024 * 3)/4 = 768 + */ + ptxfsiz = 768; + + params->host_rx_fifo_size = rxfsiz; + params->host_nperio_tx_fifo_size = nptxfsiz; + params->host_perio_tx_fifo_size = ptxfsiz; + } + + /* + * If the summation of RX, NPTX and PTX fifo sizes is still + * bigger than the total_fifo_size, then we have a problem. + * + * We won't be able to allocate as many endpoints. Right now, + * we're just printing an error message, but ideally this FIFO + * allocation algorithm would be improved in the future. + * + * FIXME improve this FIFO allocation algorithm. + */ + if (unlikely(total_fifo_size < (rxfsiz + nptxfsiz + ptxfsiz))) + dev_err(hsotg->dev, "invalid fifo sizes\n"); +} + +static void dwc2_config_fifos(struct dwc2_hsotg *hsotg) +{ + struct dwc2_core_params *params = hsotg->core_params; + u32 nptxfsiz, hptxfsiz, dfifocfg, grxfsiz; + + if (!params->enable_dynamic_fifo) + return; + + dwc2_calculate_dynamic_fifo(hsotg); + + /* Rx FIFO */ + grxfsiz = dwc2_readl(hsotg->regs + GRXFSIZ); + dev_dbg(hsotg->dev, "initial grxfsiz=%08x\n", grxfsiz); + grxfsiz &= ~GRXFSIZ_DEPTH_MASK; + grxfsiz |= params->host_rx_fifo_size << + GRXFSIZ_DEPTH_SHIFT & GRXFSIZ_DEPTH_MASK; + dwc2_writel(grxfsiz, hsotg->regs + GRXFSIZ); + dev_dbg(hsotg->dev, "new grxfsiz=%08x\n", + dwc2_readl(hsotg->regs + GRXFSIZ)); + + /* Non-periodic Tx FIFO */ + dev_dbg(hsotg->dev, "initial gnptxfsiz=%08x\n", + dwc2_readl(hsotg->regs + GNPTXFSIZ)); + nptxfsiz = params->host_nperio_tx_fifo_size << + FIFOSIZE_DEPTH_SHIFT & FIFOSIZE_DEPTH_MASK; + nptxfsiz |= params->host_rx_fifo_size << + FIFOSIZE_STARTADDR_SHIFT & FIFOSIZE_STARTADDR_MASK; + dwc2_writel(nptxfsiz, hsotg->regs + GNPTXFSIZ); + dev_dbg(hsotg->dev, "new gnptxfsiz=%08x\n", + dwc2_readl(hsotg->regs + GNPTXFSIZ)); + + /* Periodic Tx FIFO */ + dev_dbg(hsotg->dev, "initial hptxfsiz=%08x\n", + dwc2_readl(hsotg->regs + HPTXFSIZ)); + hptxfsiz = params->host_perio_tx_fifo_size << + FIFOSIZE_DEPTH_SHIFT & FIFOSIZE_DEPTH_MASK; + hptxfsiz |= (params->host_rx_fifo_size + + params->host_nperio_tx_fifo_size) << + FIFOSIZE_STARTADDR_SHIFT & FIFOSIZE_STARTADDR_MASK; + dwc2_writel(hptxfsiz, hsotg->regs + HPTXFSIZ); + dev_dbg(hsotg->dev, "new hptxfsiz=%08x\n", + dwc2_readl(hsotg->regs + HPTXFSIZ)); + + if (hsotg->core_params->en_multiple_tx_fifo > 0 && + hsotg->hw_params.snpsid <= DWC2_CORE_REV_2_94a) { + /* + * Global DFIFOCFG calculation for Host mode - + * include RxFIFO, NPTXFIFO and HPTXFIFO + */ + dfifocfg = dwc2_readl(hsotg->regs + GDFIFOCFG); + dfifocfg &= ~GDFIFOCFG_EPINFOBASE_MASK; + dfifocfg |= (params->host_rx_fifo_size + + params->host_nperio_tx_fifo_size + + params->host_perio_tx_fifo_size) << + GDFIFOCFG_EPINFOBASE_SHIFT & + GDFIFOCFG_EPINFOBASE_MASK; + dwc2_writel(dfifocfg, hsotg->regs + GDFIFOCFG); + } +} + +/** + * dwc2_calc_frame_interval() - Calculates the correct frame Interval value for + * the HFIR register according to PHY type and speed + * + * @hsotg: Programming view of DWC_otg controller + * + * NOTE: The caller can modify the value of the HFIR register only after the + * Port Enable bit of the Host Port Control and Status register (HPRT.EnaPort) + * has been set + */ +u32 dwc2_calc_frame_interval(struct dwc2_hsotg *hsotg) +{ + u32 usbcfg; + u32 hprt0; + int clock = 60; /* default value */ + + usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); + hprt0 = dwc2_readl(hsotg->regs + HPRT0); + + if (!(usbcfg & GUSBCFG_PHYSEL) && (usbcfg & GUSBCFG_ULPI_UTMI_SEL) && + !(usbcfg & GUSBCFG_PHYIF16)) + clock = 60; + if ((usbcfg & GUSBCFG_PHYSEL) && hsotg->hw_params.fs_phy_type == + GHWCFG2_FS_PHY_TYPE_SHARED_ULPI) + clock = 48; + if (!(usbcfg & GUSBCFG_PHY_LP_CLK_SEL) && !(usbcfg & GUSBCFG_PHYSEL) && + !(usbcfg & GUSBCFG_ULPI_UTMI_SEL) && (usbcfg & GUSBCFG_PHYIF16)) + clock = 30; + if (!(usbcfg & GUSBCFG_PHY_LP_CLK_SEL) && !(usbcfg & GUSBCFG_PHYSEL) && + !(usbcfg & GUSBCFG_ULPI_UTMI_SEL) && !(usbcfg & GUSBCFG_PHYIF16)) + clock = 60; + if ((usbcfg & GUSBCFG_PHY_LP_CLK_SEL) && !(usbcfg & GUSBCFG_PHYSEL) && + !(usbcfg & GUSBCFG_ULPI_UTMI_SEL) && (usbcfg & GUSBCFG_PHYIF16)) + clock = 48; + if ((usbcfg & GUSBCFG_PHYSEL) && !(usbcfg & GUSBCFG_PHYIF16) && + hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_SHARED_UTMI) + clock = 48; + if ((usbcfg & GUSBCFG_PHYSEL) && + hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED) + clock = 48; + + if ((hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT == HPRT0_SPD_HIGH_SPEED) + /* High speed case */ + return 125 * clock - 1; + + /* FS/LS case */ + return 1000 * clock - 1; +} + +/** + * dwc2_read_packet() - Reads a packet from the Rx FIFO into the destination + * buffer + * + * @core_if: Programming view of DWC_otg controller + * @dest: Destination buffer for the packet + * @bytes: Number of bytes to copy to the destination + */ +void dwc2_read_packet(struct dwc2_hsotg *hsotg, u8 *dest, u16 bytes) +{ + u32 __iomem *fifo = hsotg->regs + HCFIFO(0); + u32 *data_buf = (u32 *)dest; + int word_count = (bytes + 3) / 4; + int i; + + /* + * Todo: Account for the case where dest is not dword aligned. This + * requires reading data from the FIFO into a u32 temp buffer, then + * moving it into the data buffer. + */ + + dev_vdbg(hsotg->dev, "%s(%p,%p,%d)\n", __func__, hsotg, dest, bytes); + + for (i = 0; i < word_count; i++, data_buf++) + *data_buf = dwc2_readl(fifo); +} + /** * dwc2_dump_channel_info() - Prints the state of a host channel * @@ -77,7 +606,7 @@ static void dwc2_dump_channel_info(struct dwc2_hsotg *hsotg, u32 hc_dma; int i; - if (chan == NULL) + if (!chan) return; hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num)); @@ -120,6 +649,1056 @@ static void dwc2_dump_channel_info(struct dwc2_hsotg *hsotg, } /* + * ========================================================================= + * Low Level Host Channel Access Functions + * ========================================================================= + */ + +static void dwc2_hc_enable_slave_ints(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan) +{ + u32 hcintmsk = HCINTMSK_CHHLTD; + + switch (chan->ep_type) { + case USB_ENDPOINT_XFER_CONTROL: + case USB_ENDPOINT_XFER_BULK: + dev_vdbg(hsotg->dev, "control/bulk\n"); + hcintmsk |= HCINTMSK_XFERCOMPL; + hcintmsk |= HCINTMSK_STALL; + hcintmsk |= HCINTMSK_XACTERR; + hcintmsk |= HCINTMSK_DATATGLERR; + if (chan->ep_is_in) { + hcintmsk |= HCINTMSK_BBLERR; + } else { + hcintmsk |= HCINTMSK_NAK; + hcintmsk |= HCINTMSK_NYET; + if (chan->do_ping) + hcintmsk |= HCINTMSK_ACK; + } + + if (chan->do_split) { + hcintmsk |= HCINTMSK_NAK; + if (chan->complete_split) + hcintmsk |= HCINTMSK_NYET; + else + hcintmsk |= HCINTMSK_ACK; + } + + if (chan->error_state) + hcintmsk |= HCINTMSK_ACK; + break; + + case USB_ENDPOINT_XFER_INT: + if (dbg_perio()) + dev_vdbg(hsotg->dev, "intr\n"); + hcintmsk |= HCINTMSK_XFERCOMPL; + hcintmsk |= HCINTMSK_NAK; + hcintmsk |= HCINTMSK_STALL; + hcintmsk |= HCINTMSK_XACTERR; + hcintmsk |= HCINTMSK_DATATGLERR; + hcintmsk |= HCINTMSK_FRMOVRUN; + + if (chan->ep_is_in) + hcintmsk |= HCINTMSK_BBLERR; + if (chan->error_state) + hcintmsk |= HCINTMSK_ACK; + if (chan->do_split) { + if (chan->complete_split) + hcintmsk |= HCINTMSK_NYET; + else + hcintmsk |= HCINTMSK_ACK; + } + break; + + case USB_ENDPOINT_XFER_ISOC: + if (dbg_perio()) + dev_vdbg(hsotg->dev, "isoc\n"); + hcintmsk |= HCINTMSK_XFERCOMPL; + hcintmsk |= HCINTMSK_FRMOVRUN; + hcintmsk |= HCINTMSK_ACK; + + if (chan->ep_is_in) { + hcintmsk |= HCINTMSK_XACTERR; + hcintmsk |= HCINTMSK_BBLERR; + } + break; + default: + dev_err(hsotg->dev, "## Unknown EP type ##\n"); + break; + } + + dwc2_writel(hcintmsk, hsotg->regs + HCINTMSK(chan->hc_num)); + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "set HCINTMSK to %08x\n", hcintmsk); +} + +static void dwc2_hc_enable_dma_ints(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan) +{ + u32 hcintmsk = HCINTMSK_CHHLTD; + + /* + * For Descriptor DMA mode core halts the channel on AHB error. + * Interrupt is not required. + */ + if (hsotg->core_params->dma_desc_enable <= 0) { + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "desc DMA disabled\n"); + hcintmsk |= HCINTMSK_AHBERR; + } else { + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "desc DMA enabled\n"); + if (chan->ep_type == USB_ENDPOINT_XFER_ISOC) + hcintmsk |= HCINTMSK_XFERCOMPL; + } + + if (chan->error_state && !chan->do_split && + chan->ep_type != USB_ENDPOINT_XFER_ISOC) { + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "setting ACK\n"); + hcintmsk |= HCINTMSK_ACK; + if (chan->ep_is_in) { + hcintmsk |= HCINTMSK_DATATGLERR; + if (chan->ep_type != USB_ENDPOINT_XFER_INT) + hcintmsk |= HCINTMSK_NAK; + } + } + + dwc2_writel(hcintmsk, hsotg->regs + HCINTMSK(chan->hc_num)); + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "set HCINTMSK to %08x\n", hcintmsk); +} + +static void dwc2_hc_enable_ints(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan) +{ + u32 intmsk; + + if (hsotg->core_params->dma_enable > 0) { + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "DMA enabled\n"); + dwc2_hc_enable_dma_ints(hsotg, chan); + } else { + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "DMA disabled\n"); + dwc2_hc_enable_slave_ints(hsotg, chan); + } + + /* Enable the top level host channel interrupt */ + intmsk = dwc2_readl(hsotg->regs + HAINTMSK); + intmsk |= 1 << chan->hc_num; + dwc2_writel(intmsk, hsotg->regs + HAINTMSK); + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "set HAINTMSK to %08x\n", intmsk); + + /* Make sure host channel interrupts are enabled */ + intmsk = dwc2_readl(hsotg->regs + GINTMSK); + intmsk |= GINTSTS_HCHINT; + dwc2_writel(intmsk, hsotg->regs + GINTMSK); + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "set GINTMSK to %08x\n", intmsk); +} + +/** + * dwc2_hc_init() - Prepares a host channel for transferring packets to/from + * a specific endpoint + * + * @hsotg: Programming view of DWC_otg controller + * @chan: Information needed to initialize the host channel + * + * The HCCHARn register is set up with the characteristics specified in chan. + * Host channel interrupts that may need to be serviced while this transfer is + * in progress are enabled. + */ +static void dwc2_hc_init(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan) +{ + u8 hc_num = chan->hc_num; + u32 hcintmsk; + u32 hcchar; + u32 hcsplt = 0; + + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "%s()\n", __func__); + + /* Clear old interrupt conditions for this host channel */ + hcintmsk = 0xffffffff; + hcintmsk &= ~HCINTMSK_RESERVED14_31; + dwc2_writel(hcintmsk, hsotg->regs + HCINT(hc_num)); + + /* Enable channel interrupts required for this transfer */ + dwc2_hc_enable_ints(hsotg, chan); + + /* + * Program the HCCHARn register with the endpoint characteristics for + * the current transfer + */ + hcchar = chan->dev_addr << HCCHAR_DEVADDR_SHIFT & HCCHAR_DEVADDR_MASK; + hcchar |= chan->ep_num << HCCHAR_EPNUM_SHIFT & HCCHAR_EPNUM_MASK; + if (chan->ep_is_in) + hcchar |= HCCHAR_EPDIR; + if (chan->speed == USB_SPEED_LOW) + hcchar |= HCCHAR_LSPDDEV; + hcchar |= chan->ep_type << HCCHAR_EPTYPE_SHIFT & HCCHAR_EPTYPE_MASK; + hcchar |= chan->max_packet << HCCHAR_MPS_SHIFT & HCCHAR_MPS_MASK; + dwc2_writel(hcchar, hsotg->regs + HCCHAR(hc_num)); + if (dbg_hc(chan)) { + dev_vdbg(hsotg->dev, "set HCCHAR(%d) to %08x\n", + hc_num, hcchar); + + dev_vdbg(hsotg->dev, "%s: Channel %d\n", + __func__, hc_num); + dev_vdbg(hsotg->dev, " Dev Addr: %d\n", + chan->dev_addr); + dev_vdbg(hsotg->dev, " Ep Num: %d\n", + chan->ep_num); + dev_vdbg(hsotg->dev, " Is In: %d\n", + chan->ep_is_in); + dev_vdbg(hsotg->dev, " Is Low Speed: %d\n", + chan->speed == USB_SPEED_LOW); + dev_vdbg(hsotg->dev, " Ep Type: %d\n", + chan->ep_type); + dev_vdbg(hsotg->dev, " Max Pkt: %d\n", + chan->max_packet); + } + + /* Program the HCSPLT register for SPLITs */ + if (chan->do_split) { + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, + "Programming HC %d with split --> %s\n", + hc_num, + chan->complete_split ? "CSPLIT" : "SSPLIT"); + if (chan->complete_split) + hcsplt |= HCSPLT_COMPSPLT; + hcsplt |= chan->xact_pos << HCSPLT_XACTPOS_SHIFT & + HCSPLT_XACTPOS_MASK; + hcsplt |= chan->hub_addr << HCSPLT_HUBADDR_SHIFT & + HCSPLT_HUBADDR_MASK; + hcsplt |= chan->hub_port << HCSPLT_PRTADDR_SHIFT & + HCSPLT_PRTADDR_MASK; + if (dbg_hc(chan)) { + dev_vdbg(hsotg->dev, " comp split %d\n", + chan->complete_split); + dev_vdbg(hsotg->dev, " xact pos %d\n", + chan->xact_pos); + dev_vdbg(hsotg->dev, " hub addr %d\n", + chan->hub_addr); + dev_vdbg(hsotg->dev, " hub port %d\n", + chan->hub_port); + dev_vdbg(hsotg->dev, " is_in %d\n", + chan->ep_is_in); + dev_vdbg(hsotg->dev, " Max Pkt %d\n", + chan->max_packet); + dev_vdbg(hsotg->dev, " xferlen %d\n", + chan->xfer_len); + } + } + + dwc2_writel(hcsplt, hsotg->regs + HCSPLT(hc_num)); +} + +/** + * dwc2_hc_halt() - Attempts to halt a host channel + * + * @hsotg: Controller register interface + * @chan: Host channel to halt + * @halt_status: Reason for halting the channel + * + * This function should only be called in Slave mode or to abort a transfer in + * either Slave mode or DMA mode. Under normal circumstances in DMA mode, the + * controller halts the channel when the transfer is complete or a condition + * occurs that requires application intervention. + * + * In slave mode, checks for a free request queue entry, then sets the Channel + * Enable and Channel Disable bits of the Host Channel Characteristics + * register of the specified channel to intiate the halt. If there is no free + * request queue entry, sets only the Channel Disable bit of the HCCHARn + * register to flush requests for this channel. In the latter case, sets a + * flag to indicate that the host channel needs to be halted when a request + * queue slot is open. + * + * In DMA mode, always sets the Channel Enable and Channel Disable bits of the + * HCCHARn register. The controller ensures there is space in the request + * queue before submitting the halt request. + * + * Some time may elapse before the core flushes any posted requests for this + * host channel and halts. The Channel Halted interrupt handler completes the + * deactivation of the host channel. + */ +void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan, + enum dwc2_halt_status halt_status) +{ + u32 nptxsts, hptxsts, hcchar; + + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "%s()\n", __func__); + if (halt_status == DWC2_HC_XFER_NO_HALT_STATUS) + dev_err(hsotg->dev, "!!! halt_status = %d !!!\n", halt_status); + + if (halt_status == DWC2_HC_XFER_URB_DEQUEUE || + halt_status == DWC2_HC_XFER_AHB_ERR) { + /* + * Disable all channel interrupts except Ch Halted. The QTD + * and QH state associated with this transfer has been cleared + * (in the case of URB_DEQUEUE), so the channel needs to be + * shut down carefully to prevent crashes. + */ + u32 hcintmsk = HCINTMSK_CHHLTD; + + dev_vdbg(hsotg->dev, "dequeue/error\n"); + dwc2_writel(hcintmsk, hsotg->regs + HCINTMSK(chan->hc_num)); + + /* + * Make sure no other interrupts besides halt are currently + * pending. Handling another interrupt could cause a crash due + * to the QTD and QH state. + */ + dwc2_writel(~hcintmsk, hsotg->regs + HCINT(chan->hc_num)); + + /* + * Make sure the halt status is set to URB_DEQUEUE or AHB_ERR + * even if the channel was already halted for some other + * reason + */ + chan->halt_status = halt_status; + + hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num)); + if (!(hcchar & HCCHAR_CHENA)) { + /* + * The channel is either already halted or it hasn't + * started yet. In DMA mode, the transfer may halt if + * it finishes normally or a condition occurs that + * requires driver intervention. Don't want to halt + * the channel again. In either Slave or DMA mode, + * it's possible that the transfer has been assigned + * to a channel, but not started yet when an URB is + * dequeued. Don't want to halt a channel that hasn't + * started yet. + */ + return; + } + } + if (chan->halt_pending) { + /* + * A halt has already been issued for this channel. This might + * happen when a transfer is aborted by a higher level in + * the stack. + */ + dev_vdbg(hsotg->dev, + "*** %s: Channel %d, chan->halt_pending already set ***\n", + __func__, chan->hc_num); + return; + } + + hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num)); + + /* No need to set the bit in DDMA for disabling the channel */ + /* TODO check it everywhere channel is disabled */ + if (hsotg->core_params->dma_desc_enable <= 0) { + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "desc DMA disabled\n"); + hcchar |= HCCHAR_CHENA; + } else { + if (dbg_hc(chan)) + dev_dbg(hsotg->dev, "desc DMA enabled\n"); + } + hcchar |= HCCHAR_CHDIS; + + if (hsotg->core_params->dma_enable <= 0) { + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "DMA not enabled\n"); + hcchar |= HCCHAR_CHENA; + + /* Check for space in the request queue to issue the halt */ + if (chan->ep_type == USB_ENDPOINT_XFER_CONTROL || + chan->ep_type == USB_ENDPOINT_XFER_BULK) { + dev_vdbg(hsotg->dev, "control/bulk\n"); + nptxsts = dwc2_readl(hsotg->regs + GNPTXSTS); + if ((nptxsts & TXSTS_QSPCAVAIL_MASK) == 0) { + dev_vdbg(hsotg->dev, "Disabling channel\n"); + hcchar &= ~HCCHAR_CHENA; + } + } else { + if (dbg_perio()) + dev_vdbg(hsotg->dev, "isoc/intr\n"); + hptxsts = dwc2_readl(hsotg->regs + HPTXSTS); + if ((hptxsts & TXSTS_QSPCAVAIL_MASK) == 0 || + hsotg->queuing_high_bandwidth) { + if (dbg_perio()) + dev_vdbg(hsotg->dev, "Disabling channel\n"); + hcchar &= ~HCCHAR_CHENA; + } + } + } else { + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "DMA enabled\n"); + } + + dwc2_writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num)); + chan->halt_status = halt_status; + + if (hcchar & HCCHAR_CHENA) { + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "Channel enabled\n"); + chan->halt_pending = 1; + chan->halt_on_queue = 0; + } else { + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "Channel disabled\n"); + chan->halt_on_queue = 1; + } + + if (dbg_hc(chan)) { + dev_vdbg(hsotg->dev, "%s: Channel %d\n", __func__, + chan->hc_num); + dev_vdbg(hsotg->dev, " hcchar: 0x%08x\n", + hcchar); + dev_vdbg(hsotg->dev, " halt_pending: %d\n", + chan->halt_pending); + dev_vdbg(hsotg->dev, " halt_on_queue: %d\n", + chan->halt_on_queue); + dev_vdbg(hsotg->dev, " halt_status: %d\n", + chan->halt_status); + } +} + +/** + * dwc2_hc_cleanup() - Clears the transfer state for a host channel + * + * @hsotg: Programming view of DWC_otg controller + * @chan: Identifies the host channel to clean up + * + * This function is normally called after a transfer is done and the host + * channel is being released + */ +void dwc2_hc_cleanup(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan) +{ + u32 hcintmsk; + + chan->xfer_started = 0; + + list_del_init(&chan->split_order_list_entry); + + /* + * Clear channel interrupt enables and any unhandled channel interrupt + * conditions + */ + dwc2_writel(0, hsotg->regs + HCINTMSK(chan->hc_num)); + hcintmsk = 0xffffffff; + hcintmsk &= ~HCINTMSK_RESERVED14_31; + dwc2_writel(hcintmsk, hsotg->regs + HCINT(chan->hc_num)); +} + +/** + * dwc2_hc_set_even_odd_frame() - Sets the channel property that indicates in + * which frame a periodic transfer should occur + * + * @hsotg: Programming view of DWC_otg controller + * @chan: Identifies the host channel to set up and its properties + * @hcchar: Current value of the HCCHAR register for the specified host channel + * + * This function has no effect on non-periodic transfers + */ +static void dwc2_hc_set_even_odd_frame(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, u32 *hcchar) +{ + if (chan->ep_type == USB_ENDPOINT_XFER_INT || + chan->ep_type == USB_ENDPOINT_XFER_ISOC) { + int host_speed; + int xfer_ns; + int xfer_us; + int bytes_in_fifo; + u16 fifo_space; + u16 frame_number; + u16 wire_frame; + + /* + * Try to figure out if we're an even or odd frame. If we set + * even and the current frame number is even the the transfer + * will happen immediately. Similar if both are odd. If one is + * even and the other is odd then the transfer will happen when + * the frame number ticks. + * + * There's a bit of a balancing act to get this right. + * Sometimes we may want to send data in the current frame (AK + * right away). We might want to do this if the frame number + * _just_ ticked, but we might also want to do this in order + * to continue a split transaction that happened late in a + * microframe (so we didn't know to queue the next transfer + * until the frame number had ticked). The problem is that we + * need a lot of knowledge to know if there's actually still + * time to send things or if it would be better to wait until + * the next frame. + * + * We can look at how much time is left in the current frame + * and make a guess about whether we'll have time to transfer. + * We'll do that. + */ + + /* Get speed host is running at */ + host_speed = (chan->speed != USB_SPEED_HIGH && + !chan->do_split) ? chan->speed : USB_SPEED_HIGH; + + /* See how many bytes are in the periodic FIFO right now */ + fifo_space = (dwc2_readl(hsotg->regs + HPTXSTS) & + TXSTS_FSPCAVAIL_MASK) >> TXSTS_FSPCAVAIL_SHIFT; + bytes_in_fifo = sizeof(u32) * + (hsotg->core_params->host_perio_tx_fifo_size - + fifo_space); + + /* + * Roughly estimate bus time for everything in the periodic + * queue + our new transfer. This is "rough" because we're + * using a function that makes takes into account IN/OUT + * and INT/ISO and we're just slamming in one value for all + * transfers. This should be an over-estimate and that should + * be OK, but we can probably tighten it. + */ + xfer_ns = usb_calc_bus_time(host_speed, false, false, + chan->xfer_len + bytes_in_fifo); + xfer_us = NS_TO_US(xfer_ns); + + /* See what frame number we'll be at by the time we finish */ + frame_number = dwc2_hcd_get_future_frame_number(hsotg, xfer_us); + + /* This is when we were scheduled to be on the wire */ + wire_frame = dwc2_frame_num_inc(chan->qh->next_active_frame, 1); + + /* + * If we'd finish _after_ the frame we're scheduled in then + * it's hopeless. Just schedule right away and hope for the + * best. Note that it _might_ be wise to call back into the + * scheduler to pick a better frame, but this is better than + * nothing. + */ + if (dwc2_frame_num_gt(frame_number, wire_frame)) { + dwc2_sch_vdbg(hsotg, + "QH=%p EO MISS fr=%04x=>%04x (%+d)\n", + chan->qh, wire_frame, frame_number, + dwc2_frame_num_dec(frame_number, + wire_frame)); + wire_frame = frame_number; + + /* + * We picked a different frame number; communicate this + * back to the scheduler so it doesn't try to schedule + * another in the same frame. + * + * Remember that next_active_frame is 1 before the wire + * frame. + */ + chan->qh->next_active_frame = + dwc2_frame_num_dec(frame_number, 1); + } + + if (wire_frame & 1) + *hcchar |= HCCHAR_ODDFRM; + else + *hcchar &= ~HCCHAR_ODDFRM; + } +} + +static void dwc2_set_pid_isoc(struct dwc2_host_chan *chan) +{ + /* Set up the initial PID for the transfer */ + if (chan->speed == USB_SPEED_HIGH) { + if (chan->ep_is_in) { + if (chan->multi_count == 1) + chan->data_pid_start = DWC2_HC_PID_DATA0; + else if (chan->multi_count == 2) + chan->data_pid_start = DWC2_HC_PID_DATA1; + else + chan->data_pid_start = DWC2_HC_PID_DATA2; + } else { + if (chan->multi_count == 1) + chan->data_pid_start = DWC2_HC_PID_DATA0; + else + chan->data_pid_start = DWC2_HC_PID_MDATA; + } + } else { + chan->data_pid_start = DWC2_HC_PID_DATA0; + } +} + +/** + * dwc2_hc_write_packet() - Writes a packet into the Tx FIFO associated with + * the Host Channel + * + * @hsotg: Programming view of DWC_otg controller + * @chan: Information needed to initialize the host channel + * + * This function should only be called in Slave mode. For a channel associated + * with a non-periodic EP, the non-periodic Tx FIFO is written. For a channel + * associated with a periodic EP, the periodic Tx FIFO is written. + * + * Upon return the xfer_buf and xfer_count fields in chan are incremented by + * the number of bytes written to the Tx FIFO. + */ +static void dwc2_hc_write_packet(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan) +{ + u32 i; + u32 remaining_count; + u32 byte_count; + u32 dword_count; + u32 __iomem *data_fifo; + u32 *data_buf = (u32 *)chan->xfer_buf; + + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "%s()\n", __func__); + + data_fifo = (u32 __iomem *)(hsotg->regs + HCFIFO(chan->hc_num)); + + remaining_count = chan->xfer_len - chan->xfer_count; + if (remaining_count > chan->max_packet) + byte_count = chan->max_packet; + else + byte_count = remaining_count; + + dword_count = (byte_count + 3) / 4; + + if (((unsigned long)data_buf & 0x3) == 0) { + /* xfer_buf is DWORD aligned */ + for (i = 0; i < dword_count; i++, data_buf++) + dwc2_writel(*data_buf, data_fifo); + } else { + /* xfer_buf is not DWORD aligned */ + for (i = 0; i < dword_count; i++, data_buf++) { + u32 data = data_buf[0] | data_buf[1] << 8 | + data_buf[2] << 16 | data_buf[3] << 24; + dwc2_writel(data, data_fifo); + } + } + + chan->xfer_count += byte_count; + chan->xfer_buf += byte_count; +} + +/** + * dwc2_hc_do_ping() - Starts a PING transfer + * + * @hsotg: Programming view of DWC_otg controller + * @chan: Information needed to initialize the host channel + * + * This function should only be called in Slave mode. The Do Ping bit is set in + * the HCTSIZ register, then the channel is enabled. + */ +static void dwc2_hc_do_ping(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan) +{ + u32 hcchar; + u32 hctsiz; + + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "%s: Channel %d\n", __func__, + chan->hc_num); + + hctsiz = TSIZ_DOPNG; + hctsiz |= 1 << TSIZ_PKTCNT_SHIFT; + dwc2_writel(hctsiz, hsotg->regs + HCTSIZ(chan->hc_num)); + + hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num)); + hcchar |= HCCHAR_CHENA; + hcchar &= ~HCCHAR_CHDIS; + dwc2_writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num)); +} + +/** + * dwc2_hc_start_transfer() - Does the setup for a data transfer for a host + * channel and starts the transfer + * + * @hsotg: Programming view of DWC_otg controller + * @chan: Information needed to initialize the host channel. The xfer_len value + * may be reduced to accommodate the max widths of the XferSize and + * PktCnt fields in the HCTSIZn register. The multi_count value may be + * changed to reflect the final xfer_len value. + * + * This function may be called in either Slave mode or DMA mode. In Slave mode, + * the caller must ensure that there is sufficient space in the request queue + * and Tx Data FIFO. + * + * For an OUT transfer in Slave mode, it loads a data packet into the + * appropriate FIFO. If necessary, additional data packets are loaded in the + * Host ISR. + * + * For an IN transfer in Slave mode, a data packet is requested. The data + * packets are unloaded from the Rx FIFO in the Host ISR. If necessary, + * additional data packets are requested in the Host ISR. + * + * For a PING transfer in Slave mode, the Do Ping bit is set in the HCTSIZ + * register along with a packet count of 1 and the channel is enabled. This + * causes a single PING transaction to occur. Other fields in HCTSIZ are + * simply set to 0 since no data transfer occurs in this case. + * + * For a PING transfer in DMA mode, the HCTSIZ register is initialized with + * all the information required to perform the subsequent data transfer. In + * addition, the Do Ping bit is set in the HCTSIZ register. In this case, the + * controller performs the entire PING protocol, then starts the data + * transfer. + */ +static void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan) +{ + u32 max_hc_xfer_size = hsotg->core_params->max_transfer_size; + u16 max_hc_pkt_count = hsotg->core_params->max_packet_count; + u32 hcchar; + u32 hctsiz = 0; + u16 num_packets; + u32 ec_mc; + + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "%s()\n", __func__); + + if (chan->do_ping) { + if (hsotg->core_params->dma_enable <= 0) { + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "ping, no DMA\n"); + dwc2_hc_do_ping(hsotg, chan); + chan->xfer_started = 1; + return; + } + + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "ping, DMA\n"); + + hctsiz |= TSIZ_DOPNG; + } + + if (chan->do_split) { + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "split\n"); + num_packets = 1; + + if (chan->complete_split && !chan->ep_is_in) + /* + * For CSPLIT OUT Transfer, set the size to 0 so the + * core doesn't expect any data written to the FIFO + */ + chan->xfer_len = 0; + else if (chan->ep_is_in || chan->xfer_len > chan->max_packet) + chan->xfer_len = chan->max_packet; + else if (!chan->ep_is_in && chan->xfer_len > 188) + chan->xfer_len = 188; + + hctsiz |= chan->xfer_len << TSIZ_XFERSIZE_SHIFT & + TSIZ_XFERSIZE_MASK; + + /* For split set ec_mc for immediate retries */ + if (chan->ep_type == USB_ENDPOINT_XFER_INT || + chan->ep_type == USB_ENDPOINT_XFER_ISOC) + ec_mc = 3; + else + ec_mc = 1; + } else { + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "no split\n"); + /* + * Ensure that the transfer length and packet count will fit + * in the widths allocated for them in the HCTSIZn register + */ + if (chan->ep_type == USB_ENDPOINT_XFER_INT || + chan->ep_type == USB_ENDPOINT_XFER_ISOC) { + /* + * Make sure the transfer size is no larger than one + * (micro)frame's worth of data. (A check was done + * when the periodic transfer was accepted to ensure + * that a (micro)frame's worth of data can be + * programmed into a channel.) + */ + u32 max_periodic_len = + chan->multi_count * chan->max_packet; + + if (chan->xfer_len > max_periodic_len) + chan->xfer_len = max_periodic_len; + } else if (chan->xfer_len > max_hc_xfer_size) { + /* + * Make sure that xfer_len is a multiple of max packet + * size + */ + chan->xfer_len = + max_hc_xfer_size - chan->max_packet + 1; + } + + if (chan->xfer_len > 0) { + num_packets = (chan->xfer_len + chan->max_packet - 1) / + chan->max_packet; + if (num_packets > max_hc_pkt_count) { + num_packets = max_hc_pkt_count; + chan->xfer_len = num_packets * chan->max_packet; + } + } else { + /* Need 1 packet for transfer length of 0 */ + num_packets = 1; + } + + if (chan->ep_is_in) + /* + * Always program an integral # of max packets for IN + * transfers + */ + chan->xfer_len = num_packets * chan->max_packet; + + if (chan->ep_type == USB_ENDPOINT_XFER_INT || + chan->ep_type == USB_ENDPOINT_XFER_ISOC) + /* + * Make sure that the multi_count field matches the + * actual transfer length + */ + chan->multi_count = num_packets; + + if (chan->ep_type == USB_ENDPOINT_XFER_ISOC) + dwc2_set_pid_isoc(chan); + + hctsiz |= chan->xfer_len << TSIZ_XFERSIZE_SHIFT & + TSIZ_XFERSIZE_MASK; + + /* The ec_mc gets the multi_count for non-split */ + ec_mc = chan->multi_count; + } + + chan->start_pkt_count = num_packets; + hctsiz |= num_packets << TSIZ_PKTCNT_SHIFT & TSIZ_PKTCNT_MASK; + hctsiz |= chan->data_pid_start << TSIZ_SC_MC_PID_SHIFT & + TSIZ_SC_MC_PID_MASK; + dwc2_writel(hctsiz, hsotg->regs + HCTSIZ(chan->hc_num)); + if (dbg_hc(chan)) { + dev_vdbg(hsotg->dev, "Wrote %08x to HCTSIZ(%d)\n", + hctsiz, chan->hc_num); + + dev_vdbg(hsotg->dev, "%s: Channel %d\n", __func__, + chan->hc_num); + dev_vdbg(hsotg->dev, " Xfer Size: %d\n", + (hctsiz & TSIZ_XFERSIZE_MASK) >> + TSIZ_XFERSIZE_SHIFT); + dev_vdbg(hsotg->dev, " Num Pkts: %d\n", + (hctsiz & TSIZ_PKTCNT_MASK) >> + TSIZ_PKTCNT_SHIFT); + dev_vdbg(hsotg->dev, " Start PID: %d\n", + (hctsiz & TSIZ_SC_MC_PID_MASK) >> + TSIZ_SC_MC_PID_SHIFT); + } + + if (hsotg->core_params->dma_enable > 0) { + dwc2_writel((u32)chan->xfer_dma, + hsotg->regs + HCDMA(chan->hc_num)); + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "Wrote %08lx to HCDMA(%d)\n", + (unsigned long)chan->xfer_dma, chan->hc_num); + } + + /* Start the split */ + if (chan->do_split) { + u32 hcsplt = dwc2_readl(hsotg->regs + HCSPLT(chan->hc_num)); + + hcsplt |= HCSPLT_SPLTENA; + dwc2_writel(hcsplt, hsotg->regs + HCSPLT(chan->hc_num)); + } + + hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num)); + hcchar &= ~HCCHAR_MULTICNT_MASK; + hcchar |= (ec_mc << HCCHAR_MULTICNT_SHIFT) & HCCHAR_MULTICNT_MASK; + dwc2_hc_set_even_odd_frame(hsotg, chan, &hcchar); + + if (hcchar & HCCHAR_CHDIS) + dev_warn(hsotg->dev, + "%s: chdis set, channel %d, hcchar 0x%08x\n", + __func__, chan->hc_num, hcchar); + + /* Set host channel enable after all other setup is complete */ + hcchar |= HCCHAR_CHENA; + hcchar &= ~HCCHAR_CHDIS; + + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, " Multi Cnt: %d\n", + (hcchar & HCCHAR_MULTICNT_MASK) >> + HCCHAR_MULTICNT_SHIFT); + + dwc2_writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num)); + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "Wrote %08x to HCCHAR(%d)\n", hcchar, + chan->hc_num); + + chan->xfer_started = 1; + chan->requests++; + + if (hsotg->core_params->dma_enable <= 0 && + !chan->ep_is_in && chan->xfer_len > 0) + /* Load OUT packet into the appropriate Tx FIFO */ + dwc2_hc_write_packet(hsotg, chan); +} + +/** + * dwc2_hc_start_transfer_ddma() - Does the setup for a data transfer for a + * host channel and starts the transfer in Descriptor DMA mode + * + * @hsotg: Programming view of DWC_otg controller + * @chan: Information needed to initialize the host channel + * + * Initializes HCTSIZ register. For a PING transfer the Do Ping bit is set. + * Sets PID and NTD values. For periodic transfers initializes SCHED_INFO field + * with micro-frame bitmap. + * + * Initializes HCDMA register with descriptor list address and CTD value then + * starts the transfer via enabling the channel. + */ +void dwc2_hc_start_transfer_ddma(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan) +{ + u32 hcchar; + u32 hctsiz = 0; + + if (chan->do_ping) + hctsiz |= TSIZ_DOPNG; + + if (chan->ep_type == USB_ENDPOINT_XFER_ISOC) + dwc2_set_pid_isoc(chan); + + /* Packet Count and Xfer Size are not used in Descriptor DMA mode */ + hctsiz |= chan->data_pid_start << TSIZ_SC_MC_PID_SHIFT & + TSIZ_SC_MC_PID_MASK; + + /* 0 - 1 descriptor, 1 - 2 descriptors, etc */ + hctsiz |= (chan->ntd - 1) << TSIZ_NTD_SHIFT & TSIZ_NTD_MASK; + + /* Non-zero only for high-speed interrupt endpoints */ + hctsiz |= chan->schinfo << TSIZ_SCHINFO_SHIFT & TSIZ_SCHINFO_MASK; + + if (dbg_hc(chan)) { + dev_vdbg(hsotg->dev, "%s: Channel %d\n", __func__, + chan->hc_num); + dev_vdbg(hsotg->dev, " Start PID: %d\n", + chan->data_pid_start); + dev_vdbg(hsotg->dev, " NTD: %d\n", chan->ntd - 1); + } + + dwc2_writel(hctsiz, hsotg->regs + HCTSIZ(chan->hc_num)); + + dma_sync_single_for_device(hsotg->dev, chan->desc_list_addr, + chan->desc_list_sz, DMA_TO_DEVICE); + + dwc2_writel(chan->desc_list_addr, hsotg->regs + HCDMA(chan->hc_num)); + + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "Wrote %pad to HCDMA(%d)\n", + &chan->desc_list_addr, chan->hc_num); + + hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num)); + hcchar &= ~HCCHAR_MULTICNT_MASK; + hcchar |= chan->multi_count << HCCHAR_MULTICNT_SHIFT & + HCCHAR_MULTICNT_MASK; + + if (hcchar & HCCHAR_CHDIS) + dev_warn(hsotg->dev, + "%s: chdis set, channel %d, hcchar 0x%08x\n", + __func__, chan->hc_num, hcchar); + + /* Set host channel enable after all other setup is complete */ + hcchar |= HCCHAR_CHENA; + hcchar &= ~HCCHAR_CHDIS; + + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, " Multi Cnt: %d\n", + (hcchar & HCCHAR_MULTICNT_MASK) >> + HCCHAR_MULTICNT_SHIFT); + + dwc2_writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num)); + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "Wrote %08x to HCCHAR(%d)\n", hcchar, + chan->hc_num); + + chan->xfer_started = 1; + chan->requests++; +} + +/** + * dwc2_hc_continue_transfer() - Continues a data transfer that was started by + * a previous call to dwc2_hc_start_transfer() + * + * @hsotg: Programming view of DWC_otg controller + * @chan: Information needed to initialize the host channel + * + * The caller must ensure there is sufficient space in the request queue and Tx + * Data FIFO. This function should only be called in Slave mode. In DMA mode, + * the controller acts autonomously to complete transfers programmed to a host + * channel. + * + * For an OUT transfer, a new data packet is loaded into the appropriate FIFO + * if there is any data remaining to be queued. For an IN transfer, another + * data packet is always requested. For the SETUP phase of a control transfer, + * this function does nothing. + * + * Return: 1 if a new request is queued, 0 if no more requests are required + * for this transfer + */ +static int dwc2_hc_continue_transfer(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan) +{ + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "%s: Channel %d\n", __func__, + chan->hc_num); + + if (chan->do_split) + /* SPLITs always queue just once per channel */ + return 0; + + if (chan->data_pid_start == DWC2_HC_PID_SETUP) + /* SETUPs are queued only once since they can't be NAK'd */ + return 0; + + if (chan->ep_is_in) { + /* + * Always queue another request for other IN transfers. If + * back-to-back INs are issued and NAKs are received for both, + * the driver may still be processing the first NAK when the + * second NAK is received. When the interrupt handler clears + * the NAK interrupt for the first NAK, the second NAK will + * not be seen. So we can't depend on the NAK interrupt + * handler to requeue a NAK'd request. Instead, IN requests + * are issued each time this function is called. When the + * transfer completes, the extra requests for the channel will + * be flushed. + */ + u32 hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num)); + + dwc2_hc_set_even_odd_frame(hsotg, chan, &hcchar); + hcchar |= HCCHAR_CHENA; + hcchar &= ~HCCHAR_CHDIS; + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, " IN xfer: hcchar = 0x%08x\n", + hcchar); + dwc2_writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num)); + chan->requests++; + return 1; + } + + /* OUT transfers */ + + if (chan->xfer_count < chan->xfer_len) { + if (chan->ep_type == USB_ENDPOINT_XFER_INT || + chan->ep_type == USB_ENDPOINT_XFER_ISOC) { + u32 hcchar = dwc2_readl(hsotg->regs + + HCCHAR(chan->hc_num)); + + dwc2_hc_set_even_odd_frame(hsotg, chan, + &hcchar); + } + + /* Load OUT packet into the appropriate Tx FIFO */ + dwc2_hc_write_packet(hsotg, chan); + chan->requests++; + return 1; + } + + return 0; +} + +/* + * ========================================================================= + * HCD + * ========================================================================= + */ + +/* * Processes all the URBs in a single list of QHs. Completes them with * -ETIMEDOUT and frees the QTD. * @@ -164,6 +1743,9 @@ static void dwc2_qh_list_free(struct dwc2_hsotg *hsotg, qtd_list_entry) dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh); + if (qh->channel && qh->channel->qh == qh) + qh->channel->qh = NULL; + spin_unlock_irqrestore(&hsotg->lock, flags); dwc2_hcd_qh_free(hsotg, qh); spin_lock_irqsave(&hsotg->lock, flags); @@ -554,7 +2136,12 @@ static int dwc2_hcd_endpoint_disable(struct dwc2_hsotg *hsotg, dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh); ep->hcpriv = NULL; + + if (qh->channel && qh->channel->qh == qh) + qh->channel->qh = NULL; + spin_unlock_irqrestore(&hsotg->lock, flags); + dwc2_hcd_qh_free(hsotg, qh); return 0; @@ -580,6 +2167,224 @@ static int dwc2_hcd_endpoint_reset(struct dwc2_hsotg *hsotg, return 0; } +/** + * dwc2_core_init() - Initializes the DWC_otg controller registers and + * prepares the core for device mode or host mode operation + * + * @hsotg: Programming view of the DWC_otg controller + * @initial_setup: If true then this is the first init for this instance. + */ +static int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup) +{ + u32 usbcfg, otgctl; + int retval; + + dev_dbg(hsotg->dev, "%s(%p)\n", __func__, hsotg); + + usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); + + /* Set ULPI External VBUS bit if needed */ + usbcfg &= ~GUSBCFG_ULPI_EXT_VBUS_DRV; + if (hsotg->core_params->phy_ulpi_ext_vbus == + DWC2_PHY_ULPI_EXTERNAL_VBUS) + usbcfg |= GUSBCFG_ULPI_EXT_VBUS_DRV; + + /* Set external TS Dline pulsing bit if needed */ + usbcfg &= ~GUSBCFG_TERMSELDLPULSE; + if (hsotg->core_params->ts_dline > 0) + usbcfg |= GUSBCFG_TERMSELDLPULSE; + + dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); + + /* + * Reset the Controller + * + * We only need to reset the controller if this is a re-init. + * For the first init we know for sure that earlier code reset us (it + * needed to in order to properly detect various parameters). + */ + if (!initial_setup) { + retval = dwc2_core_reset_and_force_dr_mode(hsotg); + if (retval) { + dev_err(hsotg->dev, "%s(): Reset failed, aborting\n", + __func__); + return retval; + } + } + + /* + * This needs to happen in FS mode before any other programming occurs + */ + retval = dwc2_phy_init(hsotg, initial_setup); + if (retval) + return retval; + + /* Program the GAHBCFG Register */ + retval = dwc2_gahbcfg_init(hsotg); + if (retval) + return retval; + + /* Program the GUSBCFG register */ + dwc2_gusbcfg_init(hsotg); + + /* Program the GOTGCTL register */ + otgctl = dwc2_readl(hsotg->regs + GOTGCTL); + otgctl &= ~GOTGCTL_OTGVER; + if (hsotg->core_params->otg_ver > 0) + otgctl |= GOTGCTL_OTGVER; + dwc2_writel(otgctl, hsotg->regs + GOTGCTL); + dev_dbg(hsotg->dev, "OTG VER PARAM: %d\n", hsotg->core_params->otg_ver); + + /* Clear the SRP success bit for FS-I2c */ + hsotg->srp_success = 0; + + /* Enable common interrupts */ + dwc2_enable_common_interrupts(hsotg); + + /* + * Do device or host initialization based on mode during PCD and + * HCD initialization + */ + if (dwc2_is_host_mode(hsotg)) { + dev_dbg(hsotg->dev, "Host Mode\n"); + hsotg->op_state = OTG_STATE_A_HOST; + } else { + dev_dbg(hsotg->dev, "Device Mode\n"); + hsotg->op_state = OTG_STATE_B_PERIPHERAL; + } + + return 0; +} + +/** + * dwc2_core_host_init() - Initializes the DWC_otg controller registers for + * Host mode + * + * @hsotg: Programming view of DWC_otg controller + * + * This function flushes the Tx and Rx FIFOs and flushes any entries in the + * request queues. Host channels are reset to ensure that they are ready for + * performing transfers. + */ +static void dwc2_core_host_init(struct dwc2_hsotg *hsotg) +{ + u32 hcfg, hfir, otgctl; + + dev_dbg(hsotg->dev, "%s(%p)\n", __func__, hsotg); + + /* Restart the Phy Clock */ + dwc2_writel(0, hsotg->regs + PCGCTL); + + /* Initialize Host Configuration Register */ + dwc2_init_fs_ls_pclk_sel(hsotg); + if (hsotg->core_params->speed == DWC2_SPEED_PARAM_FULL) { + hcfg = dwc2_readl(hsotg->regs + HCFG); + hcfg |= HCFG_FSLSSUPP; + dwc2_writel(hcfg, hsotg->regs + HCFG); + } + + /* + * This bit allows dynamic reloading of the HFIR register during + * runtime. This bit needs to be programmed during initial configuration + * and its value must not be changed during runtime. + */ + if (hsotg->core_params->reload_ctl > 0) { + hfir = dwc2_readl(hsotg->regs + HFIR); + hfir |= HFIR_RLDCTRL; + dwc2_writel(hfir, hsotg->regs + HFIR); + } + + if (hsotg->core_params->dma_desc_enable > 0) { + u32 op_mode = hsotg->hw_params.op_mode; + + if (hsotg->hw_params.snpsid < DWC2_CORE_REV_2_90a || + !hsotg->hw_params.dma_desc_enable || + op_mode == GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE || + op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE || + op_mode == GHWCFG2_OP_MODE_UNDEFINED) { + dev_err(hsotg->dev, + "Hardware does not support descriptor DMA mode -\n"); + dev_err(hsotg->dev, + "falling back to buffer DMA mode.\n"); + hsotg->core_params->dma_desc_enable = 0; + } else { + hcfg = dwc2_readl(hsotg->regs + HCFG); + hcfg |= HCFG_DESCDMA; + dwc2_writel(hcfg, hsotg->regs + HCFG); + } + } + + /* Configure data FIFO sizes */ + dwc2_config_fifos(hsotg); + + /* TODO - check this */ + /* Clear Host Set HNP Enable in the OTG Control Register */ + otgctl = dwc2_readl(hsotg->regs + GOTGCTL); + otgctl &= ~GOTGCTL_HSTSETHNPEN; + dwc2_writel(otgctl, hsotg->regs + GOTGCTL); + + /* Make sure the FIFOs are flushed */ + dwc2_flush_tx_fifo(hsotg, 0x10 /* all TX FIFOs */); + dwc2_flush_rx_fifo(hsotg); + + /* Clear Host Set HNP Enable in the OTG Control Register */ + otgctl = dwc2_readl(hsotg->regs + GOTGCTL); + otgctl &= ~GOTGCTL_HSTSETHNPEN; + dwc2_writel(otgctl, hsotg->regs + GOTGCTL); + + if (hsotg->core_params->dma_desc_enable <= 0) { + int num_channels, i; + u32 hcchar; + + /* Flush out any leftover queued requests */ + num_channels = hsotg->core_params->host_channels; + for (i = 0; i < num_channels; i++) { + hcchar = dwc2_readl(hsotg->regs + HCCHAR(i)); + hcchar &= ~HCCHAR_CHENA; + hcchar |= HCCHAR_CHDIS; + hcchar &= ~HCCHAR_EPDIR; + dwc2_writel(hcchar, hsotg->regs + HCCHAR(i)); + } + + /* Halt all channels to put them into a known state */ + for (i = 0; i < num_channels; i++) { + int count = 0; + + hcchar = dwc2_readl(hsotg->regs + HCCHAR(i)); + hcchar |= HCCHAR_CHENA | HCCHAR_CHDIS; + hcchar &= ~HCCHAR_EPDIR; + dwc2_writel(hcchar, hsotg->regs + HCCHAR(i)); + dev_dbg(hsotg->dev, "%s: Halt channel %d\n", + __func__, i); + do { + hcchar = dwc2_readl(hsotg->regs + HCCHAR(i)); + if (++count > 1000) { + dev_err(hsotg->dev, + "Unable to clear enable on channel %d\n", + i); + break; + } + udelay(1); + } while (hcchar & HCCHAR_CHENA); + } + } + + /* Turn on the vbus power */ + dev_dbg(hsotg->dev, "Init: Port Power? op_state=%d\n", hsotg->op_state); + if (hsotg->op_state == OTG_STATE_A_HOST) { + u32 hprt0 = dwc2_read_hprt0(hsotg); + + dev_dbg(hsotg->dev, "Init: Power Port (%d)\n", + !!(hprt0 & HPRT0_PWR)); + if (!(hprt0 & HPRT0_PWR)) { + hprt0 |= HPRT0_PWR; + dwc2_writel(hprt0, hsotg->regs + HPRT0); + } + } + + dwc2_enable_host_interrupts(hsotg); +} + /* * Initializes dynamic portions of the DWC_otg HCD state * @@ -635,9 +2440,9 @@ static void dwc2_hc_init_split(struct dwc2_hsotg *hsotg, chan->hub_port = (u8)hub_port; } -static void *dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg, - struct dwc2_host_chan *chan, - struct dwc2_qtd *qtd, void *bufptr) +static void dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg, + struct dwc2_host_chan *chan, + struct dwc2_qtd *qtd) { struct dwc2_hcd_urb *urb = qtd->urb; struct dwc2_hcd_iso_packet_desc *frame_desc; @@ -657,7 +2462,6 @@ static void *dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg, else chan->xfer_buf = urb->setup_packet; chan->xfer_len = 8; - bufptr = NULL; break; case DWC2_CONTROL_DATA: @@ -684,7 +2488,6 @@ static void *dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg, chan->xfer_dma = hsotg->status_buf_dma; else chan->xfer_buf = hsotg->status_buf; - bufptr = NULL; break; } break; @@ -717,14 +2520,6 @@ static void *dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg, chan->xfer_len = frame_desc->length - qtd->isoc_split_offset; - /* For non-dword aligned buffers */ - if (hsotg->core_params->dma_enable > 0 && - (chan->xfer_dma & 0x3)) - bufptr = (u8 *)urb->buf + frame_desc->offset + - qtd->isoc_split_offset; - else - bufptr = NULL; - if (chan->xact_pos == DWC2_HCSPLT_XACTPOS_ALL) { if (chan->xfer_len <= 188) chan->xact_pos = DWC2_HCSPLT_XACTPOS_ALL; @@ -733,63 +2528,93 @@ static void *dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg, } break; } +} + +#define DWC2_USB_DMA_ALIGN 4 + +struct dma_aligned_buffer { + void *kmalloc_ptr; + void *old_xfer_buffer; + u8 data[0]; +}; + +static void dwc2_free_dma_aligned_buffer(struct urb *urb) +{ + struct dma_aligned_buffer *temp; + + if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER)) + return; - return bufptr; + temp = container_of(urb->transfer_buffer, + struct dma_aligned_buffer, data); + + if (usb_urb_dir_in(urb)) + memcpy(temp->old_xfer_buffer, temp->data, + urb->transfer_buffer_length); + urb->transfer_buffer = temp->old_xfer_buffer; + kfree(temp->kmalloc_ptr); + + urb->transfer_flags &= ~URB_ALIGNED_TEMP_BUFFER; } -static int dwc2_hc_setup_align_buf(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, - struct dwc2_host_chan *chan, - struct dwc2_hcd_urb *urb, void *bufptr) +static int dwc2_alloc_dma_aligned_buffer(struct urb *urb, gfp_t mem_flags) { - u32 buf_size; - struct urb *usb_urb; - struct usb_hcd *hcd; + struct dma_aligned_buffer *temp, *kmalloc_ptr; + size_t kmalloc_size; - if (!qh->dw_align_buf) { - if (chan->ep_type != USB_ENDPOINT_XFER_ISOC) - buf_size = hsotg->core_params->max_transfer_size; - else - /* 3072 = 3 max-size Isoc packets */ - buf_size = 3072; + if (urb->num_sgs || urb->sg || + urb->transfer_buffer_length == 0 || + !((uintptr_t)urb->transfer_buffer & (DWC2_USB_DMA_ALIGN - 1))) + return 0; - qh->dw_align_buf = kmalloc(buf_size, GFP_ATOMIC | GFP_DMA); - if (!qh->dw_align_buf) - return -ENOMEM; - qh->dw_align_buf_size = buf_size; - } + /* Allocate a buffer with enough padding for alignment */ + kmalloc_size = urb->transfer_buffer_length + + sizeof(struct dma_aligned_buffer) + DWC2_USB_DMA_ALIGN - 1; - if (chan->xfer_len) { - dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); - usb_urb = urb->priv; + kmalloc_ptr = kmalloc(kmalloc_size, mem_flags); + if (!kmalloc_ptr) + return -ENOMEM; - if (usb_urb) { - if (usb_urb->transfer_flags & - (URB_SETUP_MAP_SINGLE | URB_DMA_MAP_SG | - URB_DMA_MAP_PAGE | URB_DMA_MAP_SINGLE)) { - hcd = dwc2_hsotg_to_hcd(hsotg); - usb_hcd_unmap_urb_for_dma(hcd, usb_urb); - } - if (!chan->ep_is_in) - memcpy(qh->dw_align_buf, bufptr, - chan->xfer_len); - } else { - dev_warn(hsotg->dev, "no URB in dwc2_urb\n"); - } - } + /* Position our struct dma_aligned_buffer such that data is aligned */ + temp = PTR_ALIGN(kmalloc_ptr + 1, DWC2_USB_DMA_ALIGN) - 1; + temp->kmalloc_ptr = kmalloc_ptr; + temp->old_xfer_buffer = urb->transfer_buffer; + if (usb_urb_dir_out(urb)) + memcpy(temp->data, urb->transfer_buffer, + urb->transfer_buffer_length); + urb->transfer_buffer = temp->data; - qh->dw_align_buf_dma = dma_map_single(hsotg->dev, - qh->dw_align_buf, qh->dw_align_buf_size, - chan->ep_is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE); - if (dma_mapping_error(hsotg->dev, qh->dw_align_buf_dma)) { - dev_err(hsotg->dev, "can't map align_buf\n"); - chan->align_buf = 0; - return -EINVAL; - } + urb->transfer_flags |= URB_ALIGNED_TEMP_BUFFER; - chan->align_buf = qh->dw_align_buf_dma; return 0; } +static int dwc2_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags) +{ + int ret; + + /* We assume setup_dma is always aligned; warn if not */ + WARN_ON_ONCE(urb->setup_dma && + (urb->setup_dma & (DWC2_USB_DMA_ALIGN - 1))); + + ret = dwc2_alloc_dma_aligned_buffer(urb, mem_flags); + if (ret) + return ret; + + ret = usb_hcd_map_urb_for_dma(hcd, urb, mem_flags); + if (ret) + dwc2_free_dma_aligned_buffer(urb); + + return ret; +} + +static void dwc2_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) +{ + usb_hcd_unmap_urb_for_dma(hcd, urb); + dwc2_free_dma_aligned_buffer(urb); +} + /** * dwc2_assign_and_init_hc() - Assigns transactions from a QTD to a free host * channel and initializes the host channel to perform the transactions. The @@ -804,7 +2629,6 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) struct dwc2_host_chan *chan; struct dwc2_hcd_urb *urb; struct dwc2_qtd *qtd; - void *bufptr = NULL; if (dbg_qh(qh)) dev_vdbg(hsotg->dev, "%s(%p,%p)\n", __func__, hsotg, qh); @@ -866,16 +2690,10 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) !dwc2_hcd_is_pipe_in(&urb->pipe_info)) urb->actual_length = urb->length; - if (hsotg->core_params->dma_enable > 0) { + if (hsotg->core_params->dma_enable > 0) chan->xfer_dma = urb->dma + urb->actual_length; - - /* For non-dword aligned case */ - if (hsotg->core_params->dma_desc_enable <= 0 && - (chan->xfer_dma & 0x3)) - bufptr = (u8 *)urb->buf + urb->actual_length; - } else { + else chan->xfer_buf = (u8 *)urb->buf + urb->actual_length; - } chan->xfer_len = urb->length - urb->actual_length; chan->xfer_count = 0; @@ -887,27 +2705,7 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) chan->do_split = 0; /* Set the transfer attributes */ - bufptr = dwc2_hc_init_xfer(hsotg, chan, qtd, bufptr); - - /* Non DWORD-aligned buffer case */ - if (bufptr) { - dev_vdbg(hsotg->dev, "Non-aligned buffer\n"); - if (dwc2_hc_setup_align_buf(hsotg, qh, chan, urb, bufptr)) { - dev_err(hsotg->dev, - "%s: Failed to allocate memory to handle non-dword aligned buffer\n", - __func__); - /* Add channel back to free list */ - chan->align_buf = 0; - chan->multi_count = 0; - list_add_tail(&chan->hc_list_entry, - &hsotg->free_hc_list); - qtd->in_process = 0; - qh->channel = NULL; - return -ENOMEM; - } - } else { - chan->align_buf = 0; - } + dwc2_hc_init_xfer(hsotg, chan, qtd); if (chan->ep_type == USB_ENDPOINT_XFER_INT || chan->ep_type == USB_ENDPOINT_XFER_ISOC) @@ -968,7 +2766,8 @@ enum dwc2_transaction_type dwc2_hcd_select_transactions( * periodic assigned schedule */ qh_ptr = qh_ptr->next; - list_move(&qh->qh_list_entry, &hsotg->periodic_sched_assigned); + list_move_tail(&qh->qh_list_entry, + &hsotg->periodic_sched_assigned); ret_val = DWC2_TRANSACTION_PERIODIC; } @@ -1001,8 +2800,8 @@ enum dwc2_transaction_type dwc2_hcd_select_transactions( * non-periodic active schedule */ qh_ptr = qh_ptr->next; - list_move(&qh->qh_list_entry, - &hsotg->non_periodic_sched_active); + list_move_tail(&qh->qh_list_entry, + &hsotg->non_periodic_sched_active); if (ret_val == DWC2_TRANSACTION_NONE) ret_val = DWC2_TRANSACTION_NON_PERIODIC; @@ -1043,6 +2842,11 @@ static int dwc2_queue_transaction(struct dwc2_hsotg *hsotg, { int retval = 0; + if (chan->do_split) + /* Put ourselves on the list to keep order straight */ + list_move_tail(&chan->split_order_list_entry, + &hsotg->split_order); + if (hsotg->core_params->dma_enable > 0) { if (hsotg->core_params->dma_desc_enable > 0) { if (!chan->xfer_started || @@ -1102,10 +2906,14 @@ static void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg) u32 fspcavail; u32 gintmsk; int status; - int no_queue_space = 0; - int no_fifo_space = 0; + bool no_queue_space = false; + bool no_fifo_space = false; u32 qspcavail; + /* If empty list then just adjust interrupt enables */ + if (list_empty(&hsotg->periodic_sched_assigned)) + goto exit; + if (dbg_perio()) dev_vdbg(hsotg->dev, "Queue periodic transactions\n"); @@ -1175,50 +2983,40 @@ static void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg) * Move the QH from the periodic assigned schedule to * the periodic queued schedule */ - list_move(&qh->qh_list_entry, - &hsotg->periodic_sched_queued); + list_move_tail(&qh->qh_list_entry, + &hsotg->periodic_sched_queued); /* done queuing high bandwidth */ hsotg->queuing_high_bandwidth = 0; } } - if (hsotg->core_params->dma_enable <= 0) { - tx_status = dwc2_readl(hsotg->regs + HPTXSTS); - qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >> - TXSTS_QSPCAVAIL_SHIFT; - fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >> - TXSTS_FSPCAVAIL_SHIFT; - if (dbg_perio()) { - dev_vdbg(hsotg->dev, - " P Tx Req Queue Space Avail (after queue): %d\n", - qspcavail); - dev_vdbg(hsotg->dev, - " P Tx FIFO Space Avail (after queue): %d\n", - fspcavail); - } - - if (!list_empty(&hsotg->periodic_sched_assigned) || - no_queue_space || no_fifo_space) { - /* - * May need to queue more transactions as the request - * queue or Tx FIFO empties. Enable the periodic Tx - * FIFO empty interrupt. (Always use the half-empty - * level to ensure that new requests are loaded as - * soon as possible.) - */ - gintmsk = dwc2_readl(hsotg->regs + GINTMSK); +exit: + if (no_queue_space || no_fifo_space || + (hsotg->core_params->dma_enable <= 0 && + !list_empty(&hsotg->periodic_sched_assigned))) { + /* + * May need to queue more transactions as the request + * queue or Tx FIFO empties. Enable the periodic Tx + * FIFO empty interrupt. (Always use the half-empty + * level to ensure that new requests are loaded as + * soon as possible.) + */ + gintmsk = dwc2_readl(hsotg->regs + GINTMSK); + if (!(gintmsk & GINTSTS_PTXFEMP)) { gintmsk |= GINTSTS_PTXFEMP; dwc2_writel(gintmsk, hsotg->regs + GINTMSK); - } else { - /* - * Disable the Tx FIFO empty interrupt since there are - * no more transactions that need to be queued right - * now. This function is called from interrupt - * handlers to queue more transactions as transfer - * states change. - */ - gintmsk = dwc2_readl(hsotg->regs + GINTMSK); + } + } else { + /* + * Disable the Tx FIFO empty interrupt since there are + * no more transactions that need to be queued right + * now. This function is called from interrupt + * handlers to queue more transactions as transfer + * states change. + */ + gintmsk = dwc2_readl(hsotg->regs + GINTMSK); + if (gintmsk & GINTSTS_PTXFEMP) { gintmsk &= ~GINTSTS_PTXFEMP; dwc2_writel(gintmsk, hsotg->regs + GINTMSK); } @@ -1365,9 +3163,8 @@ void dwc2_hcd_queue_transactions(struct dwc2_hsotg *hsotg, dev_vdbg(hsotg->dev, "Queue Transactions\n"); #endif /* Process host channels associated with periodic transfers */ - if ((tr_type == DWC2_TRANSACTION_PERIODIC || - tr_type == DWC2_TRANSACTION_ALL) && - !list_empty(&hsotg->periodic_sched_assigned)) + if (tr_type == DWC2_TRANSACTION_PERIODIC || + tr_type == DWC2_TRANSACTION_ALL) dwc2_process_periodic_channels(hsotg); /* Process host channels associated with non-periodic transfers */ @@ -1947,6 +3744,35 @@ int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg) return (hfnum & HFNUM_FRNUM_MASK) >> HFNUM_FRNUM_SHIFT; } +int dwc2_hcd_get_future_frame_number(struct dwc2_hsotg *hsotg, int us) +{ + u32 hprt = dwc2_readl(hsotg->regs + HPRT0); + u32 hfir = dwc2_readl(hsotg->regs + HFIR); + u32 hfnum = dwc2_readl(hsotg->regs + HFNUM); + unsigned int us_per_frame; + unsigned int frame_number; + unsigned int remaining; + unsigned int interval; + unsigned int phy_clks; + + /* High speed has 125 us per (micro) frame; others are 1 ms per */ + us_per_frame = (hprt & HPRT0_SPD_MASK) ? 1000 : 125; + + /* Extract fields */ + frame_number = (hfnum & HFNUM_FRNUM_MASK) >> HFNUM_FRNUM_SHIFT; + remaining = (hfnum & HFNUM_FRREM_MASK) >> HFNUM_FRREM_SHIFT; + interval = (hfir & HFIR_FRINT_MASK) >> HFIR_FRINT_SHIFT; + + /* + * Number of phy clocks since the last tick of the frame number after + * "us" has passed. + */ + phy_clks = (interval - remaining) + + DIV_ROUND_UP(interval * us, us_per_frame); + + return dwc2_frame_num_inc(frame_number, phy_clks / interval); +} + int dwc2_hcd_is_b_host(struct dwc2_hsotg *hsotg) { return hsotg->op_state == OTG_STATE_B_HOST; @@ -2223,6 +4049,90 @@ void dwc2_host_hub_info(struct dwc2_hsotg *hsotg, void *context, int *hub_addr, *hub_port = urb->dev->ttport; } +/** + * dwc2_host_get_tt_info() - Get the dwc2_tt associated with context + * + * This will get the dwc2_tt structure (and ttport) associated with the given + * context (which is really just a struct urb pointer). + * + * The first time this is called for a given TT we allocate memory for our + * structure. When everyone is done and has called dwc2_host_put_tt_info() + * then the refcount for the structure will go to 0 and we'll free it. + * + * @hsotg: The HCD state structure for the DWC OTG controller. + * @qh: The QH structure. + * @context: The priv pointer from a struct dwc2_hcd_urb. + * @mem_flags: Flags for allocating memory. + * @ttport: We'll return this device's port number here. That's used to + * reference into the bitmap if we're on a multi_tt hub. + * + * Return: a pointer to a struct dwc2_tt. Don't forget to call + * dwc2_host_put_tt_info()! Returns NULL upon memory alloc failure. + */ + +struct dwc2_tt *dwc2_host_get_tt_info(struct dwc2_hsotg *hsotg, void *context, + gfp_t mem_flags, int *ttport) +{ + struct urb *urb = context; + struct dwc2_tt *dwc_tt = NULL; + + if (urb->dev->tt) { + *ttport = urb->dev->ttport; + + dwc_tt = urb->dev->tt->hcpriv; + if (dwc_tt == NULL) { + size_t bitmap_size; + + /* + * For single_tt we need one schedule. For multi_tt + * we need one per port. + */ + bitmap_size = DWC2_ELEMENTS_PER_LS_BITMAP * + sizeof(dwc_tt->periodic_bitmaps[0]); + if (urb->dev->tt->multi) + bitmap_size *= urb->dev->tt->hub->maxchild; + + dwc_tt = kzalloc(sizeof(*dwc_tt) + bitmap_size, + mem_flags); + if (dwc_tt == NULL) + return NULL; + + dwc_tt->usb_tt = urb->dev->tt; + dwc_tt->usb_tt->hcpriv = dwc_tt; + } + + dwc_tt->refcount++; + } + + return dwc_tt; +} + +/** + * dwc2_host_put_tt_info() - Put the dwc2_tt from dwc2_host_get_tt_info() + * + * Frees resources allocated by dwc2_host_get_tt_info() if all current holders + * of the structure are done. + * + * It's OK to call this with NULL. + * + * @hsotg: The HCD state structure for the DWC OTG controller. + * @dwc_tt: The pointer returned by dwc2_host_get_tt_info. + */ +void dwc2_host_put_tt_info(struct dwc2_hsotg *hsotg, struct dwc2_tt *dwc_tt) +{ + /* Model kfree and make put of NULL a no-op */ + if (dwc_tt == NULL) + return; + + WARN_ON(dwc_tt->refcount < 1); + + dwc_tt->refcount--; + if (!dwc_tt->refcount) { + dwc_tt->usb_tt->hcpriv = NULL; + kfree(dwc_tt); + } +} + int dwc2_host_get_speed(struct dwc2_hsotg *hsotg, void *context) { struct urb *urb = context; @@ -2334,9 +4244,7 @@ void dwc2_host_complete(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd, kfree(qtd->urb); qtd->urb = NULL; - spin_unlock(&hsotg->lock); usb_hcd_giveback_urb(dwc2_hsotg_to_hcd(hsotg), urb, status); - spin_lock(&hsotg->lock); } /* @@ -2789,6 +4697,8 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, fail3: dwc2_urb->priv = NULL; usb_hcd_unlink_urb_from_ep(hcd, urb); + if (qh_allocated && qh->channel && qh->channel->qh == qh) + qh->channel->qh = NULL; fail2: spin_unlock_irqrestore(&hsotg->lock, flags); urb->hcpriv = NULL; @@ -2955,7 +4865,7 @@ static struct hc_driver dwc2_hc_driver = { .hcd_priv_size = sizeof(struct wrapper_priv_data), .irq = _dwc2_hcd_irq, - .flags = HCD_MEMORY | HCD_USB2, + .flags = HCD_MEMORY | HCD_USB2 | HCD_BH, .start = _dwc2_hcd_start, .stop = _dwc2_hcd_stop, @@ -2971,6 +4881,9 @@ static struct hc_driver dwc2_hc_driver = { .bus_suspend = _dwc2_hcd_suspend, .bus_resume = _dwc2_hcd_resume, + + .map_urb_for_dma = dwc2_map_urb_for_dma, + .unmap_urb_for_dma = dwc2_unmap_urb_for_dma, }; /* @@ -3081,8 +4994,8 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq) FRAME_NUM_ARRAY_SIZE, GFP_KERNEL); if (!hsotg->last_frame_num_array) goto error1; - hsotg->last_frame_num = HFNUM_MAX_FRNUM; #endif + hsotg->last_frame_num = HFNUM_MAX_FRNUM; /* Check if the bus driver or platform code has setup a dma_mask */ if (hsotg->core_params->dma_enable > 0 && @@ -3146,6 +5059,8 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq) INIT_LIST_HEAD(&hsotg->periodic_sched_assigned); INIT_LIST_HEAD(&hsotg->periodic_sched_queued); + INIT_LIST_HEAD(&hsotg->split_order); + /* * Create a host channel descriptor for each host channel implemented * in the controller. Initialize the channel descriptor array. @@ -3159,12 +5074,10 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq) if (channel == NULL) goto error3; channel->hc_num = i; + INIT_LIST_HEAD(&channel->split_order_list_entry); hsotg->hc_ptr_array[i] = channel; } - if (hsotg->core_params->uframe_sched > 0) - dwc2_hcd_init_usecs(hsotg); - /* Initialize hsotg start work */ INIT_DELAYED_WORK(&hsotg->start_work, dwc2_hcd_start_func); @@ -3317,3 +5230,67 @@ void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) kfree(hsotg->frame_num_array); #endif } + +/** + * dwc2_backup_host_registers() - Backup controller host 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_host_registers(struct dwc2_hsotg *hsotg) +{ + struct dwc2_hregs_backup *hr; + int i; + + dev_dbg(hsotg->dev, "%s\n", __func__); + + /* Backup Host regs */ + hr = &hsotg->hr_backup; + hr->hcfg = dwc2_readl(hsotg->regs + HCFG); + hr->haintmsk = dwc2_readl(hsotg->regs + HAINTMSK); + for (i = 0; i < hsotg->core_params->host_channels; ++i) + hr->hcintmsk[i] = dwc2_readl(hsotg->regs + HCINTMSK(i)); + + hr->hprt0 = dwc2_read_hprt0(hsotg); + hr->hfir = dwc2_readl(hsotg->regs + HFIR); + hr->valid = true; + + return 0; +} + +/** + * dwc2_restore_host_registers() - Restore controller host 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_host_registers(struct dwc2_hsotg *hsotg) +{ + struct dwc2_hregs_backup *hr; + int i; + + dev_dbg(hsotg->dev, "%s\n", __func__); + + /* Restore host regs */ + hr = &hsotg->hr_backup; + if (!hr->valid) { + dev_err(hsotg->dev, "%s: no host registers to restore\n", + __func__); + return -EINVAL; + } + hr->valid = false; + + dwc2_writel(hr->hcfg, hsotg->regs + HCFG); + dwc2_writel(hr->haintmsk, hsotg->regs + HAINTMSK); + + for (i = 0; i < hsotg->core_params->host_channels; ++i) + dwc2_writel(hr->hcintmsk[i], hsotg->regs + HCINTMSK(i)); + + dwc2_writel(hr->hprt0, hsotg->regs + HPRT0); + dwc2_writel(hr->hfir, hsotg->regs + HFIR); + hsotg->frame_number = 0; + + return 0; +} diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h index 8f0a29c..89fa26c 100644 --- a/drivers/usb/dwc2/hcd.h +++ b/drivers/usb/dwc2/hcd.h @@ -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: \ diff --git a/drivers/usb/dwc2/hcd_ddma.c b/drivers/usb/dwc2/hcd_ddma.c index a41274a..0e1d42b 100644 --- a/drivers/usb/dwc2/hcd_ddma.c +++ b/drivers/usb/dwc2/hcd_ddma.c @@ -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. diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c index cadba8b..906f223 100644 --- a/drivers/usb/dwc2/hcd_intr.c +++ b/drivers/usb/dwc2/hcd_intr.c @@ -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); diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c index 27d402f..7f634fd 100644 --- a/drivers/usb/dwc2/hcd_queue.c +++ b/drivers/usb/dwc2/hcd_queue.c @@ -38,6 +38,7 @@ * This file contains the functions to manage Queue Heads and Queue * Transfer Descriptors for Host mode */ +#include <linux/gcd.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/spinlock.h> @@ -53,194 +54,8 @@ #include "core.h" #include "hcd.h" -/** - * dwc2_qh_init() - Initializes a QH structure - * - * @hsotg: The HCD state structure for the DWC OTG controller - * @qh: The QH to init - * @urb: Holds the information about the device/endpoint needed to initialize - * the QH - */ -#define SCHEDULE_SLOP 10 -static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, - struct dwc2_hcd_urb *urb) -{ - int dev_speed, hub_addr, hub_port; - char *speed, *type; - - dev_vdbg(hsotg->dev, "%s()\n", __func__); - - /* Initialize QH */ - qh->ep_type = dwc2_hcd_get_pipe_type(&urb->pipe_info); - qh->ep_is_in = dwc2_hcd_is_pipe_in(&urb->pipe_info) ? 1 : 0; - - qh->data_toggle = DWC2_HC_PID_DATA0; - qh->maxp = dwc2_hcd_get_mps(&urb->pipe_info); - INIT_LIST_HEAD(&qh->qtd_list); - INIT_LIST_HEAD(&qh->qh_list_entry); - - /* FS/LS Endpoint on HS Hub, NOT virtual root hub */ - dev_speed = dwc2_host_get_speed(hsotg, urb->priv); - - dwc2_host_hub_info(hsotg, urb->priv, &hub_addr, &hub_port); - - if ((dev_speed == USB_SPEED_LOW || dev_speed == USB_SPEED_FULL) && - hub_addr != 0 && hub_addr != 1) { - dev_vdbg(hsotg->dev, - "QH init: EP %d: TT found at hub addr %d, for port %d\n", - dwc2_hcd_get_ep_num(&urb->pipe_info), hub_addr, - hub_port); - qh->do_split = 1; - } - - if (qh->ep_type == USB_ENDPOINT_XFER_INT || - qh->ep_type == USB_ENDPOINT_XFER_ISOC) { - /* Compute scheduling parameters once and save them */ - u32 hprt, prtspd; - - /* Todo: Account for split transfers in the bus time */ - int bytecount = - dwc2_hb_mult(qh->maxp) * dwc2_max_packet(qh->maxp); - - qh->usecs = NS_TO_US(usb_calc_bus_time(qh->do_split ? - USB_SPEED_HIGH : dev_speed, qh->ep_is_in, - qh->ep_type == USB_ENDPOINT_XFER_ISOC, - bytecount)); - - /* Ensure frame_number corresponds to the reality */ - hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg); - /* Start in a slightly future (micro)frame */ - qh->sched_frame = dwc2_frame_num_inc(hsotg->frame_number, - SCHEDULE_SLOP); - qh->interval = urb->interval; -#if 0 - /* Increase interrupt polling rate for debugging */ - if (qh->ep_type == USB_ENDPOINT_XFER_INT) - qh->interval = 8; -#endif - hprt = dwc2_readl(hsotg->regs + HPRT0); - prtspd = (hprt & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT; - if (prtspd == HPRT0_SPD_HIGH_SPEED && - (dev_speed == USB_SPEED_LOW || - dev_speed == USB_SPEED_FULL)) { - qh->interval *= 8; - qh->sched_frame |= 0x7; - qh->start_split_frame = qh->sched_frame; - } - dev_dbg(hsotg->dev, "interval=%d\n", qh->interval); - } - - dev_vdbg(hsotg->dev, "DWC OTG HCD QH Initialized\n"); - dev_vdbg(hsotg->dev, "DWC OTG HCD QH - qh = %p\n", qh); - dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Device Address = %d\n", - dwc2_hcd_get_dev_addr(&urb->pipe_info)); - dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Endpoint %d, %s\n", - dwc2_hcd_get_ep_num(&urb->pipe_info), - dwc2_hcd_is_pipe_in(&urb->pipe_info) ? "IN" : "OUT"); - - qh->dev_speed = dev_speed; - - switch (dev_speed) { - case USB_SPEED_LOW: - speed = "low"; - break; - case USB_SPEED_FULL: - speed = "full"; - break; - case USB_SPEED_HIGH: - speed = "high"; - break; - default: - speed = "?"; - break; - } - dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Speed = %s\n", speed); - - switch (qh->ep_type) { - case USB_ENDPOINT_XFER_ISOC: - type = "isochronous"; - break; - case USB_ENDPOINT_XFER_INT: - type = "interrupt"; - break; - case USB_ENDPOINT_XFER_CONTROL: - type = "control"; - break; - case USB_ENDPOINT_XFER_BULK: - type = "bulk"; - break; - default: - type = "?"; - break; - } - - dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Type = %s\n", type); - - if (qh->ep_type == USB_ENDPOINT_XFER_INT) { - dev_vdbg(hsotg->dev, "DWC OTG HCD QH - usecs = %d\n", - qh->usecs); - dev_vdbg(hsotg->dev, "DWC OTG HCD QH - interval = %d\n", - qh->interval); - } -} - -/** - * dwc2_hcd_qh_create() - Allocates and initializes a QH - * - * @hsotg: The HCD state structure for the DWC OTG controller - * @urb: Holds the information about the device/endpoint needed - * to initialize the QH - * @atomic_alloc: Flag to do atomic allocation if needed - * - * Return: Pointer to the newly allocated QH, or NULL on error - */ -struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg, - struct dwc2_hcd_urb *urb, - gfp_t mem_flags) -{ - struct dwc2_qh *qh; - - if (!urb->priv) - return NULL; - - /* Allocate memory */ - qh = kzalloc(sizeof(*qh), mem_flags); - if (!qh) - return NULL; - - dwc2_qh_init(hsotg, qh, urb); - - if (hsotg->core_params->dma_desc_enable > 0 && - dwc2_hcd_qh_init_ddma(hsotg, qh, mem_flags) < 0) { - dwc2_hcd_qh_free(hsotg, qh); - return NULL; - } - - return qh; -} - -/** - * dwc2_hcd_qh_free() - Frees the QH - * - * @hsotg: HCD instance - * @qh: The QH to free - * - * QH should already be removed from the list. QTD list should already be empty - * if called from URB Dequeue. - * - * Must NOT be called with interrupt disabled or spinlock held - */ -void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) -{ - if (qh->desc_list) { - dwc2_hcd_qh_free_ddma(hsotg, qh); - } else { - /* kfree(NULL) is safe */ - kfree(qh->dw_align_buf); - qh->dw_align_buf_dma = (dma_addr_t)0; - } - kfree(qh); -} +/* Wait this long before releasing periodic reservation */ +#define DWC2_UNRESERVE_DELAY (msecs_to_jiffies(5)) /** * dwc2_periodic_channel_available() - Checks that a channel is available for a @@ -301,19 +116,19 @@ static int dwc2_check_periodic_bandwidth(struct dwc2_hsotg *hsotg, * High speed mode * Max periodic usecs is 80% x 125 usec = 100 usec */ - max_claimed_usecs = 100 - qh->usecs; + max_claimed_usecs = 100 - qh->host_us; } else { /* * Full speed mode * Max periodic usecs is 90% x 1000 usec = 900 usec */ - max_claimed_usecs = 900 - qh->usecs; + max_claimed_usecs = 900 - qh->host_us; } if (hsotg->periodic_usecs > max_claimed_usecs) { dev_err(hsotg->dev, "%s: already claimed usecs %d, required usecs %d\n", - __func__, hsotg->periodic_usecs, qh->usecs); + __func__, hsotg->periodic_usecs, qh->host_us); status = -ENOSPC; } @@ -321,113 +136,1177 @@ static int dwc2_check_periodic_bandwidth(struct dwc2_hsotg *hsotg, } /** - * Microframe scheduler - * track the total use in hsotg->frame_usecs - * keep each qh use in qh->frame_usecs - * when surrendering the qh then donate the time back + * pmap_schedule() - Schedule time in a periodic bitmap (pmap). + * + * @map: The bitmap representing the schedule; will be updated + * upon success. + * @bits_per_period: The schedule represents several periods. This is how many + * bits are in each period. It's assumed that the beginning + * of the schedule will repeat after its end. + * @periods_in_map: The number of periods in the schedule. + * @num_bits: The number of bits we need per period we want to reserve + * in this function call. + * @interval: How often we need to be scheduled for the reservation this + * time. 1 means every period. 2 means every other period. + * ...you get the picture? + * @start: The bit number to start at. Normally 0. Must be within + * the interval or we return failure right away. + * @only_one_period: Normally we'll allow picking a start anywhere within the + * first interval, since we can still make all repetition + * requirements by doing that. However, if you pass true + * here then we'll return failure if we can't fit within + * the period that "start" is in. + * + * The idea here is that we want to schedule time for repeating events that all + * want the same resource. The resource is divided into fixed-sized periods + * and the events want to repeat every "interval" periods. The schedule + * granularity is one bit. + * + * To keep things "simple", we'll represent our schedule with a bitmap that + * contains a fixed number of periods. This gets rid of a lot of complexity + * but does mean that we need to handle things specially (and non-ideally) if + * the number of the periods in the schedule doesn't match well with the + * intervals that we're trying to schedule. + * + * Here's an explanation of the scheme we'll implement, assuming 8 periods. + * - If interval is 1, we need to take up space in each of the 8 + * periods we're scheduling. Easy. + * - If interval is 2, we need to take up space in half of the + * periods. Again, easy. + * - If interval is 3, we actually need to fall back to interval 1. + * Why? Because we might need time in any period. AKA for the + * first 8 periods, we'll be in slot 0, 3, 6. Then we'll be + * in slot 1, 4, 7. Then we'll be in 2, 5. Then we'll be back to + * 0, 3, and 6. Since we could be in any frame we need to reserve + * for all of them. Sucks, but that's what you gotta do. Note that + * if we were instead scheduling 8 * 3 = 24 we'd do much better, but + * then we need more memory and time to do scheduling. + * - If interval is 4, easy. + * - If interval is 5, we again need interval 1. The schedule will be + * 0, 5, 2, 7, 4, 1, 6, 3, 0 + * - If interval is 6, we need interval 2. 0, 6, 4, 2. + * - If interval is 7, we need interval 1. + * - If interval is 8, we need interval 8. + * + * If you do the math, you'll see that we need to pretend that interval is + * equal to the greatest_common_divisor(interval, periods_in_map). + * + * Note that at the moment this function tends to front-pack the schedule. + * In some cases that's really non-ideal (it's hard to schedule things that + * need to repeat every period). In other cases it's perfect (you can easily + * schedule bigger, less often repeating things). + * + * Here's the algorithm in action (8 periods, 5 bits per period): + * |** | |** | |** | |** | | OK 2 bits, intv 2 at 0 + * |*****| ***|*****| ***|*****| ***|*****| ***| OK 3 bits, intv 3 at 2 + * |*****|* ***|*****| ***|*****|* ***|*****| ***| OK 1 bits, intv 4 at 5 + * |** |* |** | |** |* |** | | Remv 3 bits, intv 3 at 2 + * |*** |* |*** | |*** |* |*** | | OK 1 bits, intv 6 at 2 + * |**** |* * |**** | * |**** |* * |**** | * | OK 1 bits, intv 1 at 3 + * |**** |**** |**** | *** |**** |**** |**** | *** | OK 2 bits, intv 2 at 6 + * |*****|*****|*****| ****|*****|*****|*****| ****| OK 1 bits, intv 1 at 4 + * |*****|*****|*****| ****|*****|*****|*****| ****| FAIL 1 bits, intv 1 + * | ***|*****| ***| ****| ***|*****| ***| ****| Remv 2 bits, intv 2 at 0 + * | ***| ****| ***| ****| ***| ****| ***| ****| Remv 1 bits, intv 4 at 5 + * | **| ****| **| ****| **| ****| **| ****| Remv 1 bits, intv 6 at 2 + * | *| ** *| *| ** *| *| ** *| *| ** *| Remv 1 bits, intv 1 at 3 + * | *| *| *| *| *| *| *| *| Remv 2 bits, intv 2 at 6 + * | | | | | | | | | Remv 1 bits, intv 1 at 4 + * |** | |** | |** | |** | | OK 2 bits, intv 2 at 0 + * |*** | |** | |*** | |** | | OK 1 bits, intv 4 at 2 + * |*****| |** **| |*****| |** **| | OK 2 bits, intv 2 at 3 + * |*****|* |** **| |*****|* |** **| | OK 1 bits, intv 4 at 5 + * |*****|*** |** **| ** |*****|*** |** **| ** | OK 2 bits, intv 2 at 6 + * |*****|*****|** **| ****|*****|*****|** **| ****| OK 2 bits, intv 2 at 8 + * |*****|*****|*****| ****|*****|*****|*****| ****| OK 1 bits, intv 4 at 12 + * + * This function is pretty generic and could be easily abstracted if anything + * needed similar scheduling. + * + * Returns either -ENOSPC or a >= 0 start bit which should be passed to the + * unschedule routine. The map bitmap will be updated on a non-error result. */ -static const unsigned short max_uframe_usecs[] = { - 100, 100, 100, 100, 100, 100, 30, 0 -}; +static int pmap_schedule(unsigned long *map, int bits_per_period, + int periods_in_map, int num_bits, + int interval, int start, bool only_one_period) +{ + int interval_bits; + int to_reserve; + int first_end; + int i; + + if (num_bits > bits_per_period) + return -ENOSPC; + + /* Adjust interval as per description */ + interval = gcd(interval, periods_in_map); + + interval_bits = bits_per_period * interval; + to_reserve = periods_in_map / interval; + + /* If start has gotten us past interval then we can't schedule */ + if (start >= interval_bits) + return -ENOSPC; + + if (only_one_period) + /* Must fit within same period as start; end at begin of next */ + first_end = (start / bits_per_period + 1) * bits_per_period; + else + /* Can fit anywhere in the first interval */ + first_end = interval_bits; + + /* + * We'll try to pick the first repetition, then see if that time + * is free for each of the subsequent repetitions. If it's not + * we'll adjust the start time for the next search of the first + * repetition. + */ + while (start + num_bits <= first_end) { + int end; + + /* Need to stay within this period */ + end = (start / bits_per_period + 1) * bits_per_period; + + /* Look for num_bits us in this microframe starting at start */ + start = bitmap_find_next_zero_area(map, end, start, num_bits, + 0); + + /* + * We should get start >= end if we fail. We might be + * able to check the next microframe depending on the + * interval, so continue on (start already updated). + */ + if (start >= end) { + start = end; + continue; + } + + /* At this point we have a valid point for first one */ + for (i = 1; i < to_reserve; i++) { + int ith_start = start + interval_bits * i; + int ith_end = end + interval_bits * i; + int ret; + + /* Use this as a dumb "check if bits are 0" */ + ret = bitmap_find_next_zero_area( + map, ith_start + num_bits, ith_start, num_bits, + 0); + + /* We got the right place, continue checking */ + if (ret == ith_start) + continue; + + /* Move start up for next time and exit for loop */ + ith_start = bitmap_find_next_zero_area( + map, ith_end, ith_start, num_bits, 0); + if (ith_start >= ith_end) + /* Need a while new period next time */ + start = end; + else + start = ith_start - interval_bits * i; + break; + } + + /* If didn't exit the for loop with a break, we have success */ + if (i == to_reserve) + break; + } -void dwc2_hcd_init_usecs(struct dwc2_hsotg *hsotg) + if (start + num_bits > first_end) + return -ENOSPC; + + for (i = 0; i < to_reserve; i++) { + int ith_start = start + interval_bits * i; + + bitmap_set(map, ith_start, num_bits); + } + + return start; +} + +/** + * pmap_unschedule() - Undo work done by pmap_schedule() + * + * @map: See pmap_schedule(). + * @bits_per_period: See pmap_schedule(). + * @periods_in_map: See pmap_schedule(). + * @num_bits: The number of bits that was passed to schedule. + * @interval: The interval that was passed to schedule. + * @start: The return value from pmap_schedule(). + */ +static void pmap_unschedule(unsigned long *map, int bits_per_period, + int periods_in_map, int num_bits, + int interval, int start) { + int interval_bits; + int to_release; int i; - for (i = 0; i < 8; i++) - hsotg->frame_usecs[i] = max_uframe_usecs[i]; + /* Adjust interval as per description in pmap_schedule() */ + interval = gcd(interval, periods_in_map); + + interval_bits = bits_per_period * interval; + to_release = periods_in_map / interval; + + for (i = 0; i < to_release; i++) { + int ith_start = start + interval_bits * i; + + bitmap_clear(map, ith_start, num_bits); + } } -static int dwc2_find_single_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) +/* + * cat_printf() - A printf() + strcat() helper + * + * This is useful for concatenating a bunch of strings where each string is + * constructed using printf. + * + * @buf: The destination buffer; will be updated to point after the printed + * data. + * @size: The number of bytes in the buffer (includes space for '\0'). + * @fmt: The format for printf. + * @...: The args for printf. + */ +static void cat_printf(char **buf, size_t *size, const char *fmt, ...) { - unsigned short utime = qh->usecs; + va_list args; int i; - for (i = 0; i < 8; i++) { - /* At the start hsotg->frame_usecs[i] = max_uframe_usecs[i] */ - if (utime <= hsotg->frame_usecs[i]) { - hsotg->frame_usecs[i] -= utime; - qh->frame_usecs[i] += utime; - return i; - } + if (*size == 0) + return; + + va_start(args, fmt); + i = vsnprintf(*buf, *size, fmt, args); + va_end(args); + + if (i >= *size) { + (*buf)[*size - 1] = '\0'; + *buf += *size; + *size = 0; + } else { + *buf += i; + *size -= i; } - return -ENOSPC; } /* - * use this for FS apps that can span multiple uframes + * pmap_print() - Print the given periodic map + * + * Will attempt to print out the periodic schedule. + * + * @map: See pmap_schedule(). + * @bits_per_period: See pmap_schedule(). + * @periods_in_map: See pmap_schedule(). + * @period_name: The name of 1 period, like "uFrame" + * @units: The name of the units, like "us". + * @print_fn: The function to call for printing. + * @print_data: Opaque data to pass to the print function. + */ +static void pmap_print(unsigned long *map, int bits_per_period, + int periods_in_map, const char *period_name, + const char *units, + void (*print_fn)(const char *str, void *data), + void *print_data) +{ + int period; + + for (period = 0; period < periods_in_map; period++) { + char tmp[64]; + char *buf = tmp; + size_t buf_size = sizeof(tmp); + int period_start = period * bits_per_period; + int period_end = period_start + bits_per_period; + int start = 0; + int count = 0; + bool printed = false; + int i; + + for (i = period_start; i < period_end + 1; i++) { + /* Handle case when ith bit is set */ + if (i < period_end && + bitmap_find_next_zero_area(map, i + 1, + i, 1, 0) != i) { + if (count == 0) + start = i - period_start; + count++; + continue; + } + + /* ith bit isn't set; don't care if count == 0 */ + if (count == 0) + continue; + + if (!printed) + cat_printf(&buf, &buf_size, "%s %d: ", + period_name, period); + else + cat_printf(&buf, &buf_size, ", "); + printed = true; + + cat_printf(&buf, &buf_size, "%d %s -%3d %s", start, + units, start + count - 1, units); + count = 0; + } + + if (printed) + print_fn(tmp, print_data); + } +} + +/** + * dwc2_get_ls_map() - Get the map used for the given qh + * + * @hsotg: The HCD state structure for the DWC OTG controller. + * @qh: QH for the periodic transfer. + * + * We'll always get the periodic map out of our TT. Note that even if we're + * running the host straight in low speed / full speed mode it appears as if + * a TT is allocated for us, so we'll use it. If that ever changes we can + * add logic here to get a map out of "hsotg" if !qh->do_split. + * + * Returns: the map or NULL if a map couldn't be found. */ -static int dwc2_find_multi_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) +static unsigned long *dwc2_get_ls_map(struct dwc2_hsotg *hsotg, + struct dwc2_qh *qh) { - unsigned short utime = qh->usecs; - unsigned short xtime; - int t_left; + unsigned long *map; + + /* Don't expect to be missing a TT and be doing low speed scheduling */ + if (WARN_ON(!qh->dwc_tt)) + return NULL; + + /* Get the map and adjust if this is a multi_tt hub */ + map = qh->dwc_tt->periodic_bitmaps; + if (qh->dwc_tt->usb_tt->multi) + map += DWC2_ELEMENTS_PER_LS_BITMAP * qh->ttport; + + return map; +} + +struct dwc2_qh_print_data { + struct dwc2_hsotg *hsotg; + struct dwc2_qh *qh; +}; + +/** + * dwc2_qh_print() - Helper function for dwc2_qh_schedule_print() + * + * @str: The string to print + * @data: A pointer to a struct dwc2_qh_print_data + */ +static void dwc2_qh_print(const char *str, void *data) +{ + struct dwc2_qh_print_data *print_data = data; + + dwc2_sch_dbg(print_data->hsotg, "QH=%p ...%s\n", print_data->qh, str); +} + +/** + * dwc2_qh_schedule_print() - Print the periodic schedule + * + * @hsotg: The HCD state structure for the DWC OTG controller. + * @qh: QH to print. + */ +static void dwc2_qh_schedule_print(struct dwc2_hsotg *hsotg, + struct dwc2_qh *qh) +{ + struct dwc2_qh_print_data print_data = { hsotg, qh }; int i; - int j; - int k; - for (i = 0; i < 8; i++) { - if (hsotg->frame_usecs[i] <= 0) + /* + * The printing functions are quite slow and inefficient. + * If we don't have tracing turned on, don't run unless the special + * define is turned on. + */ +#ifndef DWC2_PRINT_SCHEDULE + return; +#endif + + if (qh->schedule_low_speed) { + unsigned long *map = dwc2_get_ls_map(hsotg, qh); + + dwc2_sch_dbg(hsotg, "QH=%p LS/FS trans: %d=>%d us @ %d us", + qh, qh->device_us, + DWC2_ROUND_US_TO_SLICE(qh->device_us), + DWC2_US_PER_SLICE * qh->ls_start_schedule_slice); + + if (map) { + dwc2_sch_dbg(hsotg, + "QH=%p Whole low/full speed map %p now:\n", + qh, map); + pmap_print(map, DWC2_LS_PERIODIC_SLICES_PER_FRAME, + DWC2_LS_SCHEDULE_FRAMES, "Frame ", "slices", + dwc2_qh_print, &print_data); + } + } + + for (i = 0; i < qh->num_hs_transfers; i++) { + struct dwc2_hs_transfer_time *trans_time = qh->hs_transfers + i; + int uframe = trans_time->start_schedule_us / + DWC2_HS_PERIODIC_US_PER_UFRAME; + int rel_us = trans_time->start_schedule_us % + DWC2_HS_PERIODIC_US_PER_UFRAME; + + dwc2_sch_dbg(hsotg, + "QH=%p HS trans #%d: %d us @ uFrame %d + %d us\n", + qh, i, trans_time->duration_us, uframe, rel_us); + } + if (qh->num_hs_transfers) { + dwc2_sch_dbg(hsotg, "QH=%p Whole high speed map now:\n", qh); + pmap_print(hsotg->hs_periodic_bitmap, + DWC2_HS_PERIODIC_US_PER_UFRAME, + DWC2_HS_SCHEDULE_UFRAMES, "uFrame", "us", + dwc2_qh_print, &print_data); + } + +} + +/** + * dwc2_ls_pmap_schedule() - Schedule a low speed QH + * + * @hsotg: The HCD state structure for the DWC OTG controller. + * @qh: QH for the periodic transfer. + * @search_slice: We'll start trying to schedule at the passed slice. + * Remember that slices are the units of the low speed + * schedule (think 25us or so). + * + * Wraps pmap_schedule() with the right parameters for low speed scheduling. + * + * Normally we schedule low speed devices on the map associated with the TT. + * + * Returns: 0 for success or an error code. + */ +static int dwc2_ls_pmap_schedule(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, + int search_slice) +{ + int slices = DIV_ROUND_UP(qh->device_us, DWC2_US_PER_SLICE); + unsigned long *map = dwc2_get_ls_map(hsotg, qh); + int slice; + + if (map == NULL) + return -EINVAL; + + /* + * Schedule on the proper low speed map with our low speed scheduling + * parameters. Note that we use the "device_interval" here since + * we want the low speed interval and the only way we'd be in this + * function is if the device is low speed. + * + * If we happen to be doing low speed and high speed scheduling for the + * same transaction (AKA we have a split) we always do low speed first. + * That means we can always pass "false" for only_one_period (that + * parameters is only useful when we're trying to get one schedule to + * match what we already planned in the other schedule). + */ + slice = pmap_schedule(map, DWC2_LS_PERIODIC_SLICES_PER_FRAME, + DWC2_LS_SCHEDULE_FRAMES, slices, + qh->device_interval, search_slice, false); + + if (slice < 0) + return slice; + + qh->ls_start_schedule_slice = slice; + return 0; +} + +/** + * dwc2_ls_pmap_unschedule() - Undo work done by dwc2_ls_pmap_schedule() + * + * @hsotg: The HCD state structure for the DWC OTG controller. + * @qh: QH for the periodic transfer. + */ +static void dwc2_ls_pmap_unschedule(struct dwc2_hsotg *hsotg, + struct dwc2_qh *qh) +{ + int slices = DIV_ROUND_UP(qh->device_us, DWC2_US_PER_SLICE); + unsigned long *map = dwc2_get_ls_map(hsotg, qh); + + /* Schedule should have failed, so no worries about no error code */ + if (map == NULL) + return; + + pmap_unschedule(map, DWC2_LS_PERIODIC_SLICES_PER_FRAME, + DWC2_LS_SCHEDULE_FRAMES, slices, qh->device_interval, + qh->ls_start_schedule_slice); +} + +/** + * dwc2_hs_pmap_schedule - Schedule in the main high speed schedule + * + * This will schedule something on the main dwc2 schedule. + * + * We'll start looking in qh->hs_transfers[index].start_schedule_us. We'll + * update this with the result upon success. We also use the duration from + * the same structure. + * + * @hsotg: The HCD state structure for the DWC OTG controller. + * @qh: QH for the periodic transfer. + * @only_one_period: If true we will limit ourselves to just looking at + * one period (aka one 100us chunk). This is used if we have + * already scheduled something on the low speed schedule and + * need to find something that matches on the high speed one. + * @index: The index into qh->hs_transfers that we're working with. + * + * Returns: 0 for success or an error code. Upon success the + * dwc2_hs_transfer_time specified by "index" will be updated. + */ +static int dwc2_hs_pmap_schedule(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, + bool only_one_period, int index) +{ + struct dwc2_hs_transfer_time *trans_time = qh->hs_transfers + index; + int us; + + us = pmap_schedule(hsotg->hs_periodic_bitmap, + DWC2_HS_PERIODIC_US_PER_UFRAME, + DWC2_HS_SCHEDULE_UFRAMES, trans_time->duration_us, + qh->host_interval, trans_time->start_schedule_us, + only_one_period); + + if (us < 0) + return us; + + trans_time->start_schedule_us = us; + return 0; +} + +/** + * dwc2_ls_pmap_unschedule() - Undo work done by dwc2_hs_pmap_schedule() + * + * @hsotg: The HCD state structure for the DWC OTG controller. + * @qh: QH for the periodic transfer. + */ +static void dwc2_hs_pmap_unschedule(struct dwc2_hsotg *hsotg, + struct dwc2_qh *qh, int index) +{ + struct dwc2_hs_transfer_time *trans_time = qh->hs_transfers + index; + + pmap_unschedule(hsotg->hs_periodic_bitmap, + DWC2_HS_PERIODIC_US_PER_UFRAME, + DWC2_HS_SCHEDULE_UFRAMES, trans_time->duration_us, + qh->host_interval, trans_time->start_schedule_us); +} + +/** + * dwc2_uframe_schedule_split - Schedule a QH for a periodic split xfer. + * + * This is the most complicated thing in USB. We have to find matching time + * in both the global high speed schedule for the port and the low speed + * schedule for the TT associated with the given device. + * + * Being here means that the host must be running in high speed mode and the + * device is in low or full speed mode (and behind a hub). + * + * @hsotg: The HCD state structure for the DWC OTG controller. + * @qh: QH for the periodic transfer. + */ +static int dwc2_uframe_schedule_split(struct dwc2_hsotg *hsotg, + struct dwc2_qh *qh) +{ + int bytecount = dwc2_hb_mult(qh->maxp) * dwc2_max_packet(qh->maxp); + int ls_search_slice; + int err = 0; + int host_interval_in_sched; + + /* + * The interval (how often to repeat) in the actual host schedule. + * See pmap_schedule() for gcd() explanation. + */ + host_interval_in_sched = gcd(qh->host_interval, + DWC2_HS_SCHEDULE_UFRAMES); + + /* + * We always try to find space in the low speed schedule first, then + * try to find high speed time that matches. If we don't, we'll bump + * up the place we start searching in the low speed schedule and try + * again. To start we'll look right at the beginning of the low speed + * schedule. + * + * Note that this will tend to front-load the high speed schedule. + * We may eventually want to try to avoid this by either considering + * both schedules together or doing some sort of round robin. + */ + ls_search_slice = 0; + + while (ls_search_slice < DWC2_LS_SCHEDULE_SLICES) { + int start_s_uframe; + int ssplit_s_uframe; + int second_s_uframe; + int rel_uframe; + int first_count; + int middle_count; + int end_count; + int first_data_bytes; + int other_data_bytes; + int i; + + if (qh->schedule_low_speed) { + err = dwc2_ls_pmap_schedule(hsotg, qh, ls_search_slice); + + /* + * If we got an error here there's no other magic we + * can do, so bail. All the looping above is only + * helpful to redo things if we got a low speed slot + * and then couldn't find a matching high speed slot. + */ + if (err) + return err; + } else { + /* Must be missing the tt structure? Why? */ + WARN_ON_ONCE(1); + } + + /* + * This will give us a number 0 - 7 if + * DWC2_LS_SCHEDULE_FRAMES == 1, or 0 - 15 if == 2, or ... + */ + start_s_uframe = qh->ls_start_schedule_slice / + DWC2_SLICES_PER_UFRAME; + + /* Get a number that's always 0 - 7 */ + rel_uframe = (start_s_uframe % 8); + + /* + * If we were going to start in uframe 7 then we would need to + * issue a start split in uframe 6, which spec says is not OK. + * Move on to the next full frame (assuming there is one). + * + * See 11.18.4 Host Split Transaction Scheduling Requirements + * bullet 1. + */ + if (rel_uframe == 7) { + if (qh->schedule_low_speed) + dwc2_ls_pmap_unschedule(hsotg, qh); + ls_search_slice = + (qh->ls_start_schedule_slice / + DWC2_LS_PERIODIC_SLICES_PER_FRAME + 1) * + DWC2_LS_PERIODIC_SLICES_PER_FRAME; continue; + } /* - * we need n consecutive slots so use j as a start slot - * j plus j+1 must be enough time (for now) + * For ISOC in: + * - start split (frame -1) + * - complete split w/ data (frame +1) + * - complete split w/ data (frame +2) + * - ... + * - complete split w/ data (frame +num_data_packets) + * - complete split w/ data (frame +num_data_packets+1) + * - complete split w/ data (frame +num_data_packets+2, max 8) + * ...though if frame was "0" then max is 7... + * + * For ISOC out we might need to do: + * - start split w/ data (frame -1) + * - start split w/ data (frame +0) + * - ... + * - start split w/ data (frame +num_data_packets-2) + * + * For INTERRUPT in we might need to do: + * - start split (frame -1) + * - complete split w/ data (frame +1) + * - complete split w/ data (frame +2) + * - complete split w/ data (frame +3, max 8) + * + * For INTERRUPT out we might need to do: + * - start split w/ data (frame -1) + * - complete split (frame +1) + * - complete split (frame +2) + * - complete split (frame +3, max 8) + * + * Start adjusting! */ - xtime = hsotg->frame_usecs[i]; - for (j = i + 1; j < 8; j++) { - /* - * if we add this frame remaining time to xtime we may - * be OK, if not we need to test j for a complete frame - */ - if (xtime + hsotg->frame_usecs[j] < utime) { - if (hsotg->frame_usecs[j] < - max_uframe_usecs[j]) - continue; + ssplit_s_uframe = (start_s_uframe + + host_interval_in_sched - 1) % + host_interval_in_sched; + if (qh->ep_type == USB_ENDPOINT_XFER_ISOC && !qh->ep_is_in) + second_s_uframe = start_s_uframe; + else + second_s_uframe = start_s_uframe + 1; + + /* First data transfer might not be all 188 bytes. */ + first_data_bytes = 188 - + DIV_ROUND_UP(188 * (qh->ls_start_schedule_slice % + DWC2_SLICES_PER_UFRAME), + DWC2_SLICES_PER_UFRAME); + if (first_data_bytes > bytecount) + first_data_bytes = bytecount; + other_data_bytes = bytecount - first_data_bytes; + + /* + * For now, skip OUT xfers where first xfer is partial + * + * Main dwc2 code assumes: + * - INT transfers never get split in two. + * - ISOC transfers can always transfer 188 bytes the first + * time. + * + * Until that code is fixed, try again if the first transfer + * couldn't transfer everything. + * + * This code can be removed if/when the rest of dwc2 handles + * the above cases. Until it's fixed we just won't be able + * to schedule quite as tightly. + */ + if (!qh->ep_is_in && + (first_data_bytes != min_t(int, 188, bytecount))) { + dwc2_sch_dbg(hsotg, + "QH=%p avoiding broken 1st xfer (%d, %d)\n", + qh, first_data_bytes, bytecount); + if (qh->schedule_low_speed) + dwc2_ls_pmap_unschedule(hsotg, qh); + ls_search_slice = (start_s_uframe + 1) * + DWC2_SLICES_PER_UFRAME; + continue; + } + + /* Start by assuming transfers for the bytes */ + qh->num_hs_transfers = 1 + DIV_ROUND_UP(other_data_bytes, 188); + + /* + * Everything except ISOC OUT has extra transfers. Rules are + * complicated. See 11.18.4 Host Split Transaction Scheduling + * Requirements bullet 3. + */ + if (qh->ep_type == USB_ENDPOINT_XFER_INT) { + if (rel_uframe == 6) + qh->num_hs_transfers += 2; + else + qh->num_hs_transfers += 3; + + if (qh->ep_is_in) { + /* + * First is start split, middle/end is data. + * Allocate full data bytes for all data. + */ + first_count = 4; + middle_count = bytecount; + end_count = bytecount; + } else { + /* + * First is data, middle/end is complete. + * First transfer and second can have data. + * Rest should just have complete split. + */ + first_count = first_data_bytes; + middle_count = max_t(int, 4, other_data_bytes); + end_count = 4; } - if (xtime >= utime) { - t_left = utime; - for (k = i; k < 8; k++) { - t_left -= hsotg->frame_usecs[k]; - if (t_left <= 0) { - qh->frame_usecs[k] += - hsotg->frame_usecs[k] - + t_left; - hsotg->frame_usecs[k] = -t_left; - return i; - } else { - qh->frame_usecs[k] += - hsotg->frame_usecs[k]; - hsotg->frame_usecs[k] = 0; - } - } + } else { + if (qh->ep_is_in) { + int last; + + /* Account for the start split */ + qh->num_hs_transfers++; + + /* Calculate "L" value from spec */ + last = rel_uframe + qh->num_hs_transfers + 1; + + /* Start with basic case */ + if (last <= 6) + qh->num_hs_transfers += 2; + else + qh->num_hs_transfers += 1; + + /* Adjust downwards */ + if (last >= 6 && rel_uframe == 0) + qh->num_hs_transfers--; + + /* 1st = start; rest can contain data */ + first_count = 4; + middle_count = min_t(int, 188, bytecount); + end_count = middle_count; + } else { + /* All contain data, last might be smaller */ + first_count = first_data_bytes; + middle_count = min_t(int, 188, + other_data_bytes); + end_count = other_data_bytes % 188; } - /* add the frame time to x time */ - xtime += hsotg->frame_usecs[j]; - /* we must have a fully available next frame or break */ - if (xtime < utime && - hsotg->frame_usecs[j] == max_uframe_usecs[j]) - continue; } + + /* Assign durations per uFrame */ + qh->hs_transfers[0].duration_us = HS_USECS_ISO(first_count); + for (i = 1; i < qh->num_hs_transfers - 1; i++) + qh->hs_transfers[i].duration_us = + HS_USECS_ISO(middle_count); + if (qh->num_hs_transfers > 1) + qh->hs_transfers[qh->num_hs_transfers - 1].duration_us = + HS_USECS_ISO(end_count); + + /* + * Assign start us. The call below to dwc2_hs_pmap_schedule() + * will start with these numbers but may adjust within the same + * microframe. + */ + qh->hs_transfers[0].start_schedule_us = + ssplit_s_uframe * DWC2_HS_PERIODIC_US_PER_UFRAME; + for (i = 1; i < qh->num_hs_transfers; i++) + qh->hs_transfers[i].start_schedule_us = + ((second_s_uframe + i - 1) % + DWC2_HS_SCHEDULE_UFRAMES) * + DWC2_HS_PERIODIC_US_PER_UFRAME; + + /* Try to schedule with filled in hs_transfers above */ + for (i = 0; i < qh->num_hs_transfers; i++) { + err = dwc2_hs_pmap_schedule(hsotg, qh, true, i); + if (err) + break; + } + + /* If we scheduled all w/out breaking out then we're all good */ + if (i == qh->num_hs_transfers) + break; + + for (; i >= 0; i--) + dwc2_hs_pmap_unschedule(hsotg, qh, i); + + if (qh->schedule_low_speed) + dwc2_ls_pmap_unschedule(hsotg, qh); + + /* Try again starting in the next microframe */ + ls_search_slice = (start_s_uframe + 1) * DWC2_SLICES_PER_UFRAME; } - return -ENOSPC; + + if (ls_search_slice >= DWC2_LS_SCHEDULE_SLICES) + return -ENOSPC; + + return 0; +} + +/** + * dwc2_uframe_schedule_hs - Schedule a QH for a periodic high speed xfer. + * + * Basically this just wraps dwc2_hs_pmap_schedule() to provide a clean + * interface. + * + * @hsotg: The HCD state structure for the DWC OTG controller. + * @qh: QH for the periodic transfer. + */ +static int dwc2_uframe_schedule_hs(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) +{ + /* In non-split host and device time are the same */ + WARN_ON(qh->host_us != qh->device_us); + WARN_ON(qh->host_interval != qh->device_interval); + WARN_ON(qh->num_hs_transfers != 1); + + /* We'll have one transfer; init start to 0 before calling scheduler */ + qh->hs_transfers[0].start_schedule_us = 0; + qh->hs_transfers[0].duration_us = qh->host_us; + + return dwc2_hs_pmap_schedule(hsotg, qh, false, 0); +} + +/** + * dwc2_uframe_schedule_ls - Schedule a QH for a periodic low/full speed xfer. + * + * Basically this just wraps dwc2_ls_pmap_schedule() to provide a clean + * interface. + * + * @hsotg: The HCD state structure for the DWC OTG controller. + * @qh: QH for the periodic transfer. + */ +static int dwc2_uframe_schedule_ls(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) +{ + /* In non-split host and device time are the same */ + WARN_ON(qh->host_us != qh->device_us); + WARN_ON(qh->host_interval != qh->device_interval); + WARN_ON(!qh->schedule_low_speed); + + /* Run on the main low speed schedule (no split = no hub = no TT) */ + return dwc2_ls_pmap_schedule(hsotg, qh, 0); } -static int dwc2_find_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) +/** + * dwc2_uframe_schedule - Schedule a QH for a periodic xfer. + * + * Calls one of the 3 sub-function depending on what type of transfer this QH + * is for. Also adds some printing. + * + * @hsotg: The HCD state structure for the DWC OTG controller. + * @qh: QH for the periodic transfer. + */ +static int dwc2_uframe_schedule(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) { int ret; - if (qh->dev_speed == USB_SPEED_HIGH) { - /* if this is a hs transaction we need a full frame */ - ret = dwc2_find_single_uframe(hsotg, qh); + if (qh->dev_speed == USB_SPEED_HIGH) + ret = dwc2_uframe_schedule_hs(hsotg, qh); + else if (!qh->do_split) + ret = dwc2_uframe_schedule_ls(hsotg, qh); + else + ret = dwc2_uframe_schedule_split(hsotg, qh); + + if (ret) + dwc2_sch_dbg(hsotg, "QH=%p Failed to schedule %d\n", qh, ret); + else + dwc2_qh_schedule_print(hsotg, qh); + + return ret; +} + +/** + * dwc2_uframe_unschedule - Undoes dwc2_uframe_schedule(). + * + * @hsotg: The HCD state structure for the DWC OTG controller. + * @qh: QH for the periodic transfer. + */ +static void dwc2_uframe_unschedule(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) +{ + int i; + + for (i = 0; i < qh->num_hs_transfers; i++) + dwc2_hs_pmap_unschedule(hsotg, qh, i); + + if (qh->schedule_low_speed) + dwc2_ls_pmap_unschedule(hsotg, qh); + + dwc2_sch_dbg(hsotg, "QH=%p Unscheduled\n", qh); +} + +/** + * dwc2_pick_first_frame() - Choose 1st frame for qh that's already scheduled + * + * Takes a qh that has already been scheduled (which means we know we have the + * bandwdith reserved for us) and set the next_active_frame and the + * start_active_frame. + * + * This is expected to be called on qh's that weren't previously actively + * running. It just picks the next frame that we can fit into without any + * thought about the past. + * + * @hsotg: The HCD state structure for the DWC OTG controller + * @qh: QH for a periodic endpoint + * + */ +static void dwc2_pick_first_frame(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) +{ + u16 frame_number; + u16 earliest_frame; + u16 next_active_frame; + u16 relative_frame; + u16 interval; + + /* + * Use the real frame number rather than the cached value as of the + * last SOF to give us a little extra slop. + */ + frame_number = dwc2_hcd_get_frame_number(hsotg); + + /* + * We wouldn't want to start any earlier than the next frame just in + * case the frame number ticks as we're doing this calculation. + * + * NOTE: if we could quantify how long till we actually get scheduled + * we might be able to avoid the "+ 1" by looking at the upper part of + * HFNUM (the FRREM field). For now we'll just use the + 1 though. + */ + earliest_frame = dwc2_frame_num_inc(frame_number, 1); + next_active_frame = earliest_frame; + + /* Get the "no microframe schduler" out of the way... */ + if (hsotg->core_params->uframe_sched <= 0) { + if (qh->do_split) + /* Splits are active at microframe 0 minus 1 */ + next_active_frame |= 0x7; + goto exit; + } + + if (qh->dev_speed == USB_SPEED_HIGH || qh->do_split) { + /* + * We're either at high speed or we're doing a split (which + * means we're talking high speed to a hub). In any case + * the first frame should be based on when the first scheduled + * event is. + */ + WARN_ON(qh->num_hs_transfers < 1); + + relative_frame = qh->hs_transfers[0].start_schedule_us / + DWC2_HS_PERIODIC_US_PER_UFRAME; + + /* Adjust interval as per high speed schedule */ + interval = gcd(qh->host_interval, DWC2_HS_SCHEDULE_UFRAMES); + } else { /* - * if this is a fs transaction we may need a sequence - * of frames + * Low or full speed directly on dwc2. Just about the same + * as high speed but on a different schedule and with slightly + * different adjustments. Note that this works because when + * the host and device are both low speed then frames in the + * controller tick at low speed. */ - ret = dwc2_find_multi_uframe(hsotg, qh); + relative_frame = qh->ls_start_schedule_slice / + DWC2_LS_PERIODIC_SLICES_PER_FRAME; + interval = gcd(qh->host_interval, DWC2_LS_SCHEDULE_FRAMES); } - return ret; + + /* Scheduler messed up if frame is past interval */ + WARN_ON(relative_frame >= interval); + + /* + * We know interval must divide (HFNUM_MAX_FRNUM + 1) now that we've + * done the gcd(), so it's safe to move to the beginning of the current + * interval like this. + * + * After this we might be before earliest_frame, but don't worry, + * we'll fix it... + */ + next_active_frame = (next_active_frame / interval) * interval; + + /* + * Actually choose to start at the frame number we've been + * scheduled for. + */ + next_active_frame = dwc2_frame_num_inc(next_active_frame, + relative_frame); + + /* + * We actually need 1 frame before since the next_active_frame is + * the frame number we'll be put on the ready list and we won't be on + * the bus until 1 frame later. + */ + next_active_frame = dwc2_frame_num_dec(next_active_frame, 1); + + /* + * By now we might actually be before the earliest_frame. Let's move + * up intervals until we're not. + */ + while (dwc2_frame_num_gt(earliest_frame, next_active_frame)) + next_active_frame = dwc2_frame_num_inc(next_active_frame, + interval); + +exit: + qh->next_active_frame = next_active_frame; + qh->start_active_frame = next_active_frame; + + dwc2_sch_vdbg(hsotg, "QH=%p First fn=%04x nxt=%04x\n", + qh, frame_number, qh->next_active_frame); +} + +/** + * dwc2_do_reserve() - Make a periodic reservation + * + * Try to allocate space in the periodic schedule. Depending on parameters + * this might use the microframe scheduler or the dumb scheduler. + * + * @hsotg: The HCD state structure for the DWC OTG controller + * @qh: QH for the periodic transfer. + * + * Returns: 0 upon success; error upon failure. + */ +static int dwc2_do_reserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) +{ + int status; + + if (hsotg->core_params->uframe_sched > 0) { + status = dwc2_uframe_schedule(hsotg, qh); + } else { + status = dwc2_periodic_channel_available(hsotg); + if (status) { + dev_info(hsotg->dev, + "%s: No host channel available for periodic transfer\n", + __func__); + return status; + } + + status = dwc2_check_periodic_bandwidth(hsotg, qh); + } + + if (status) { + dev_dbg(hsotg->dev, + "%s: Insufficient periodic bandwidth for periodic transfer\n", + __func__); + return status; + } + + if (hsotg->core_params->uframe_sched <= 0) + /* Reserve periodic channel */ + hsotg->periodic_channels++; + + /* Update claimed usecs per (micro)frame */ + hsotg->periodic_usecs += qh->host_us; + + dwc2_pick_first_frame(hsotg, qh); + + return 0; +} + +/** + * dwc2_do_unreserve() - Actually release the periodic reservation + * + * This function actually releases the periodic bandwidth that was reserved + * by the given qh. + * + * @hsotg: The HCD state structure for the DWC OTG controller + * @qh: QH for the periodic transfer. + */ +static void dwc2_do_unreserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) +{ + assert_spin_locked(&hsotg->lock); + + WARN_ON(!qh->unreserve_pending); + + /* No more unreserve pending--we're doing it */ + qh->unreserve_pending = false; + + if (WARN_ON(!list_empty(&qh->qh_list_entry))) + list_del_init(&qh->qh_list_entry); + + /* Update claimed usecs per (micro)frame */ + hsotg->periodic_usecs -= qh->host_us; + + if (hsotg->core_params->uframe_sched > 0) { + dwc2_uframe_unschedule(hsotg, qh); + } else { + /* Release periodic channel reservation */ + hsotg->periodic_channels--; + } +} + +/** + * dwc2_unreserve_timer_fn() - Timer function to release periodic reservation + * + * According to the kernel doc for usb_submit_urb() (specifically the part about + * "Reserved Bandwidth Transfers"), we need to keep a reservation active as + * long as a device driver keeps submitting. Since we're using HCD_BH to give + * back the URB we need to give the driver a little bit of time before we + * release the reservation. This worker is called after the appropriate + * delay. + * + * @work: Pointer to a qh unreserve_work. + */ +static void dwc2_unreserve_timer_fn(unsigned long data) +{ + struct dwc2_qh *qh = (struct dwc2_qh *)data; + struct dwc2_hsotg *hsotg = qh->hsotg; + unsigned long flags; + + /* + * Wait for the lock, or for us to be scheduled again. We + * could be scheduled again if: + * - We started executing but didn't get the lock yet. + * - A new reservation came in, but cancel didn't take effect + * because we already started executing. + * - The timer has been kicked again. + * In that case cancel and wait for the next call. + */ + while (!spin_trylock_irqsave(&hsotg->lock, flags)) { + if (timer_pending(&qh->unreserve_timer)) + return; + } + + /* + * Might be no more unreserve pending if: + * - We started executing but didn't get the lock yet. + * - A new reservation came in, but cancel didn't take effect + * because we already started executing. + * + * We can't put this in the loop above because unreserve_pending needs + * to be accessed under lock, so we can only check it once we got the + * lock. + */ + if (qh->unreserve_pending) + dwc2_do_unreserve(hsotg, qh); + + spin_unlock_irqrestore(&hsotg->lock, flags); } /** @@ -474,42 +1353,6 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) { int status; - if (hsotg->core_params->uframe_sched > 0) { - int frame = -1; - - status = dwc2_find_uframe(hsotg, qh); - if (status == 0) - frame = 7; - else if (status > 0) - frame = status - 1; - - /* Set the new frame up */ - if (frame >= 0) { - qh->sched_frame &= ~0x7; - qh->sched_frame |= (frame & 7); - } - - if (status > 0) - status = 0; - } else { - status = dwc2_periodic_channel_available(hsotg); - if (status) { - dev_info(hsotg->dev, - "%s: No host channel available for periodic transfer\n", - __func__); - return status; - } - - status = dwc2_check_periodic_bandwidth(hsotg, qh); - } - - if (status) { - dev_dbg(hsotg->dev, - "%s: Insufficient periodic bandwidth for periodic transfer\n", - __func__); - return status; - } - status = dwc2_check_max_xfer_size(hsotg, qh); if (status) { dev_dbg(hsotg->dev, @@ -518,6 +1361,35 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) return status; } + /* Cancel pending unreserve; if canceled OK, unreserve was pending */ + if (del_timer(&qh->unreserve_timer)) + WARN_ON(!qh->unreserve_pending); + + /* + * Only need to reserve if there's not an unreserve pending, since if an + * unreserve is pending then by definition our old reservation is still + * valid. Unreserve might still be pending even if we didn't cancel if + * dwc2_unreserve_timer_fn() already started. Code in the timer handles + * that case. + */ + if (!qh->unreserve_pending) { + status = dwc2_do_reserve(hsotg, qh); + if (status) + return status; + } else { + /* + * It might have been a while, so make sure that frame_number + * is still good. Note: we could also try to use the similar + * dwc2_next_periodic_start() but that schedules much more + * tightly and we might need to hurry and queue things up. + */ + if (dwc2_frame_num_le(qh->next_active_frame, + hsotg->frame_number)) + dwc2_pick_first_frame(hsotg, qh); + } + + qh->unreserve_pending = 0; + if (hsotg->core_params->dma_desc_enable > 0) /* Don't rely on SOF and start in ready schedule */ list_add_tail(&qh->qh_list_entry, &hsotg->periodic_sched_ready); @@ -526,14 +1398,7 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) list_add_tail(&qh->qh_list_entry, &hsotg->periodic_sched_inactive); - if (hsotg->core_params->uframe_sched <= 0) - /* Reserve periodic channel */ - hsotg->periodic_channels++; - - /* Update claimed usecs per (micro)frame */ - hsotg->periodic_usecs += qh->usecs; - - return status; + return 0; } /** @@ -546,25 +1411,231 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) static void dwc2_deschedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) { - int i; + bool did_modify; + + assert_spin_locked(&hsotg->lock); + + /* + * Schedule the unreserve to happen in a little bit. Cases here: + * - Unreserve worker might be sitting there waiting to grab the lock. + * In this case it will notice it's been schedule again and will + * quit. + * - Unreserve worker might not be scheduled. + * + * We should never already be scheduled since dwc2_schedule_periodic() + * should have canceled the scheduled unreserve timer (hence the + * warning on did_modify). + * + * We add + 1 to the timer to guarantee that at least 1 jiffy has + * passed (otherwise if the jiffy counter might tick right after we + * read it and we'll get no delay). + */ + did_modify = mod_timer(&qh->unreserve_timer, + jiffies + DWC2_UNRESERVE_DELAY + 1); + WARN_ON(did_modify); + qh->unreserve_pending = 1; list_del_init(&qh->qh_list_entry); +} - /* Update claimed usecs per (micro)frame */ - hsotg->periodic_usecs -= qh->usecs; +/** + * dwc2_qh_init() - Initializes a QH structure + * + * @hsotg: The HCD state structure for the DWC OTG controller + * @qh: The QH to init + * @urb: Holds the information about the device/endpoint needed to initialize + * the QH + * @mem_flags: Flags for allocating memory. + */ +static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, + struct dwc2_hcd_urb *urb, gfp_t mem_flags) +{ + int dev_speed = dwc2_host_get_speed(hsotg, urb->priv); + u8 ep_type = dwc2_hcd_get_pipe_type(&urb->pipe_info); + bool ep_is_in = !!dwc2_hcd_is_pipe_in(&urb->pipe_info); + bool ep_is_isoc = (ep_type == USB_ENDPOINT_XFER_ISOC); + bool ep_is_int = (ep_type == USB_ENDPOINT_XFER_INT); + u32 hprt = dwc2_readl(hsotg->regs + HPRT0); + u32 prtspd = (hprt & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT; + bool do_split = (prtspd == HPRT0_SPD_HIGH_SPEED && + dev_speed != USB_SPEED_HIGH); + int maxp = dwc2_hcd_get_mps(&urb->pipe_info); + int bytecount = dwc2_hb_mult(maxp) * dwc2_max_packet(maxp); + char *speed, *type; - if (hsotg->core_params->uframe_sched > 0) { - for (i = 0; i < 8; i++) { - hsotg->frame_usecs[i] += qh->frame_usecs[i]; - qh->frame_usecs[i] = 0; + /* Initialize QH */ + qh->hsotg = hsotg; + setup_timer(&qh->unreserve_timer, dwc2_unreserve_timer_fn, + (unsigned long)qh); + qh->ep_type = ep_type; + qh->ep_is_in = ep_is_in; + + qh->data_toggle = DWC2_HC_PID_DATA0; + qh->maxp = maxp; + INIT_LIST_HEAD(&qh->qtd_list); + INIT_LIST_HEAD(&qh->qh_list_entry); + + qh->do_split = do_split; + qh->dev_speed = dev_speed; + + if (ep_is_int || ep_is_isoc) { + /* Compute scheduling parameters once and save them */ + int host_speed = do_split ? USB_SPEED_HIGH : dev_speed; + struct dwc2_tt *dwc_tt = dwc2_host_get_tt_info(hsotg, urb->priv, + mem_flags, + &qh->ttport); + int device_ns; + + qh->dwc_tt = dwc_tt; + + qh->host_us = NS_TO_US(usb_calc_bus_time(host_speed, ep_is_in, + ep_is_isoc, bytecount)); + device_ns = usb_calc_bus_time(dev_speed, ep_is_in, + ep_is_isoc, bytecount); + + if (do_split && dwc_tt) + device_ns += dwc_tt->usb_tt->think_time; + qh->device_us = NS_TO_US(device_ns); + + + qh->device_interval = urb->interval; + qh->host_interval = urb->interval * (do_split ? 8 : 1); + + /* + * Schedule low speed if we're running the host in low or + * full speed OR if we've got a "TT" to deal with to access this + * device. + */ + qh->schedule_low_speed = prtspd != HPRT0_SPD_HIGH_SPEED || + dwc_tt; + + if (do_split) { + /* We won't know num transfers until we schedule */ + qh->num_hs_transfers = -1; + } else if (dev_speed == USB_SPEED_HIGH) { + qh->num_hs_transfers = 1; + } else { + qh->num_hs_transfers = 0; } - } else { - /* Release periodic channel reservation */ - hsotg->periodic_channels--; + + /* We'll schedule later when we have something to do */ + } + + switch (dev_speed) { + case USB_SPEED_LOW: + speed = "low"; + break; + case USB_SPEED_FULL: + speed = "full"; + break; + case USB_SPEED_HIGH: + speed = "high"; + break; + default: + speed = "?"; + break; + } + + switch (qh->ep_type) { + case USB_ENDPOINT_XFER_ISOC: + type = "isochronous"; + break; + case USB_ENDPOINT_XFER_INT: + type = "interrupt"; + break; + case USB_ENDPOINT_XFER_CONTROL: + type = "control"; + break; + case USB_ENDPOINT_XFER_BULK: + type = "bulk"; + break; + default: + type = "?"; + break; + } + + dwc2_sch_dbg(hsotg, "QH=%p Init %s, %s speed, %d bytes:\n", qh, type, + speed, bytecount); + dwc2_sch_dbg(hsotg, "QH=%p ...addr=%d, ep=%d, %s\n", qh, + dwc2_hcd_get_dev_addr(&urb->pipe_info), + dwc2_hcd_get_ep_num(&urb->pipe_info), + ep_is_in ? "IN" : "OUT"); + if (ep_is_int || ep_is_isoc) { + dwc2_sch_dbg(hsotg, + "QH=%p ...duration: host=%d us, device=%d us\n", + qh, qh->host_us, qh->device_us); + dwc2_sch_dbg(hsotg, "QH=%p ...interval: host=%d, device=%d\n", + qh, qh->host_interval, qh->device_interval); + if (qh->schedule_low_speed) + dwc2_sch_dbg(hsotg, "QH=%p ...low speed schedule=%p\n", + qh, dwc2_get_ls_map(hsotg, qh)); } } /** + * dwc2_hcd_qh_create() - Allocates and initializes a QH + * + * @hsotg: The HCD state structure for the DWC OTG controller + * @urb: Holds the information about the device/endpoint needed + * to initialize the QH + * @atomic_alloc: Flag to do atomic allocation if needed + * + * Return: Pointer to the newly allocated QH, or NULL on error + */ +struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg, + struct dwc2_hcd_urb *urb, + gfp_t mem_flags) +{ + struct dwc2_qh *qh; + + if (!urb->priv) + return NULL; + + /* Allocate memory */ + qh = kzalloc(sizeof(*qh), mem_flags); + if (!qh) + return NULL; + + dwc2_qh_init(hsotg, qh, urb, mem_flags); + + if (hsotg->core_params->dma_desc_enable > 0 && + dwc2_hcd_qh_init_ddma(hsotg, qh, mem_flags) < 0) { + dwc2_hcd_qh_free(hsotg, qh); + return NULL; + } + + return qh; +} + +/** + * dwc2_hcd_qh_free() - Frees the QH + * + * @hsotg: HCD instance + * @qh: The QH to free + * + * QH should already be removed from the list. QTD list should already be empty + * if called from URB Dequeue. + * + * Must NOT be called with interrupt disabled or spinlock held + */ +void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) +{ + /* Make sure any unreserve work is finished. */ + if (del_timer_sync(&qh->unreserve_timer)) { + unsigned long flags; + + spin_lock_irqsave(&hsotg->lock, flags); + dwc2_do_unreserve(hsotg, qh); + spin_unlock_irqrestore(&hsotg->lock, flags); + } + dwc2_host_put_tt_info(hsotg, qh->dwc_tt); + + if (qh->desc_list) + dwc2_hcd_qh_free_ddma(hsotg, qh); + kfree(qh); +} + +/** * dwc2_hcd_qh_add() - Adds a QH to either the non periodic or periodic * schedule if it is not already in the schedule. If the QH is already in * the schedule, no action is taken. @@ -586,16 +1657,12 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) /* QH already in a schedule */ return 0; - if (!dwc2_frame_num_le(qh->sched_frame, hsotg->frame_number) && - !hsotg->frame_number) { - dev_dbg(hsotg->dev, - "reset frame number counter\n"); - qh->sched_frame = dwc2_frame_num_inc(hsotg->frame_number, - SCHEDULE_SLOP); - } - /* Add the new QH to the appropriate schedule */ if (dwc2_qh_is_non_per(qh)) { + /* Schedule right away */ + qh->start_active_frame = hsotg->frame_number; + qh->next_active_frame = qh->start_active_frame; + /* Always start in inactive schedule */ list_add_tail(&qh->qh_list_entry, &hsotg->non_periodic_sched_inactive); @@ -649,39 +1716,164 @@ void dwc2_hcd_qh_unlink(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) } } -/* - * Schedule the next continuing periodic split transfer +/** + * dwc2_next_for_periodic_split() - Set next_active_frame midway thru a split. + * + * This is called for setting next_active_frame for periodic splits for all but + * the first packet of the split. Confusing? I thought so... + * + * Periodic splits are single low/full speed transfers that we end up splitting + * up into several high speed transfers. They always fit into one full (1 ms) + * frame but might be split over several microframes (125 us each). We to put + * each of the parts on a very specific high speed frame. + * + * This function figures out where the next active uFrame needs to be. + * + * @hsotg: The HCD state structure + * @qh: QH for the periodic transfer. + * @frame_number: The current frame number. + * + * Return: number missed by (or 0 if we didn't miss). */ -static void dwc2_sched_periodic_split(struct dwc2_hsotg *hsotg, - struct dwc2_qh *qh, u16 frame_number, - int sched_next_periodic_split) +static int dwc2_next_for_periodic_split(struct dwc2_hsotg *hsotg, + struct dwc2_qh *qh, u16 frame_number) { + u16 old_frame = qh->next_active_frame; + u16 prev_frame_number = dwc2_frame_num_dec(frame_number, 1); + int missed = 0; u16 incr; - if (sched_next_periodic_split) { - qh->sched_frame = frame_number; - incr = dwc2_frame_num_inc(qh->start_split_frame, 1); - if (dwc2_frame_num_le(frame_number, incr)) { - /* - * Allow one frame to elapse after start split - * microframe before scheduling complete split, but - * DON'T if we are doing the next start split in the - * same frame for an ISOC out - */ - if (qh->ep_type != USB_ENDPOINT_XFER_ISOC || - qh->ep_is_in != 0) { - qh->sched_frame = - dwc2_frame_num_inc(qh->sched_frame, 1); - } - } - } else { - qh->sched_frame = dwc2_frame_num_inc(qh->start_split_frame, - qh->interval); - if (dwc2_frame_num_le(qh->sched_frame, frame_number)) - qh->sched_frame = frame_number; - qh->sched_frame |= 0x7; - qh->start_split_frame = qh->sched_frame; + /* + * See dwc2_uframe_schedule_split() for split scheduling. + * + * Basically: increment 1 normally, but 2 right after the start split + * (except for ISOC out). + */ + if (old_frame == qh->start_active_frame && + !(qh->ep_type == USB_ENDPOINT_XFER_ISOC && !qh->ep_is_in)) + incr = 2; + else + incr = 1; + + qh->next_active_frame = dwc2_frame_num_inc(old_frame, incr); + + /* + * Note that it's OK for frame_number to be 1 frame past + * next_active_frame. Remember that next_active_frame is supposed to + * be 1 frame _before_ when we want to be scheduled. If we're 1 frame + * past it just means schedule ASAP. + * + * It's _not_ OK, however, if we're more than one frame past. + */ + if (dwc2_frame_num_gt(prev_frame_number, qh->next_active_frame)) { + /* + * OOPS, we missed. That's actually pretty bad since + * the hub will be unhappy; try ASAP I guess. + */ + missed = dwc2_frame_num_dec(prev_frame_number, + qh->next_active_frame); + qh->next_active_frame = frame_number; } + + return missed; +} + +/** + * dwc2_next_periodic_start() - Set next_active_frame for next transfer start + * + * This is called for setting next_active_frame for a periodic transfer for + * all cases other than midway through a periodic split. This will also update + * start_active_frame. + * + * Since we _always_ keep start_active_frame as the start of the previous + * transfer this is normally pretty easy: we just add our interval to + * start_active_frame and we've got our answer. + * + * The tricks come into play if we miss. In that case we'll look for the next + * slot we can fit into. + * + * @hsotg: The HCD state structure + * @qh: QH for the periodic transfer. + * @frame_number: The current frame number. + * + * Return: number missed by (or 0 if we didn't miss). + */ +static int dwc2_next_periodic_start(struct dwc2_hsotg *hsotg, + struct dwc2_qh *qh, u16 frame_number) +{ + int missed = 0; + u16 interval = qh->host_interval; + u16 prev_frame_number = dwc2_frame_num_dec(frame_number, 1); + + qh->start_active_frame = dwc2_frame_num_inc(qh->start_active_frame, + interval); + + /* + * The dwc2_frame_num_gt() function used below won't work terribly well + * with if we just incremented by a really large intervals since the + * frame counter only goes to 0x3fff. It's terribly unlikely that we + * will have missed in this case anyway. Just go to exit. If we want + * to try to do better we'll need to keep track of a bigger counter + * somewhere in the driver and handle overflows. + */ + if (interval >= 0x1000) + goto exit; + + /* + * Test for misses, which is when it's too late to schedule. + * + * A few things to note: + * - We compare against prev_frame_number since start_active_frame + * and next_active_frame are always 1 frame before we want things + * to be active and we assume we can still get scheduled in the + * current frame number. + * - It's possible for start_active_frame (now incremented) to be + * next_active_frame if we got an EO MISS (even_odd miss) which + * basically means that we detected there wasn't enough time for + * the last packet and dwc2_hc_set_even_odd_frame() rescheduled us + * at the last second. We want to make sure we don't schedule + * another transfer for the same frame. My test webcam doesn't seem + * terribly upset by missing a transfer but really doesn't like when + * we do two transfers in the same frame. + * - Some misses are expected. Specifically, in order to work + * perfectly dwc2 really needs quite spectacular interrupt latency + * requirements. It needs to be able to handle its interrupts + * completely within 125 us of them being asserted. That not only + * means that the dwc2 interrupt handler needs to be fast but it + * means that nothing else in the system has to block dwc2 for a long + * time. We can help with the dwc2 parts of this, but it's hard to + * guarantee that a system will have interrupt latency < 125 us, so + * we have to be robust to some misses. + */ + if (qh->start_active_frame == qh->next_active_frame || + dwc2_frame_num_gt(prev_frame_number, qh->start_active_frame)) { + u16 ideal_start = qh->start_active_frame; + int periods_in_map; + + /* + * Adjust interval as per gcd with map size. + * See pmap_schedule() for more details here. + */ + if (qh->do_split || qh->dev_speed == USB_SPEED_HIGH) + periods_in_map = DWC2_HS_SCHEDULE_UFRAMES; + else + periods_in_map = DWC2_LS_SCHEDULE_FRAMES; + interval = gcd(interval, periods_in_map); + + do { + qh->start_active_frame = dwc2_frame_num_inc( + qh->start_active_frame, interval); + } while (dwc2_frame_num_gt(prev_frame_number, + qh->start_active_frame)); + + missed = dwc2_frame_num_dec(qh->start_active_frame, + ideal_start); + } + +exit: + qh->next_active_frame = qh->start_active_frame; + + return missed; } /* @@ -700,7 +1892,9 @@ static void dwc2_sched_periodic_split(struct dwc2_hsotg *hsotg, void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, int sched_next_periodic_split) { + u16 old_frame = qh->next_active_frame; u16 frame_number; + int missed; if (dbg_qh(qh)) dev_vdbg(hsotg->dev, "%s()\n", __func__); @@ -713,33 +1907,44 @@ void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, return; } + /* + * Use the real frame number rather than the cached value as of the + * last SOF just to get us a little closer to reality. Note that + * means we don't actually know if we've already handled the SOF + * interrupt for this frame. + */ frame_number = dwc2_hcd_get_frame_number(hsotg); - if (qh->do_split) { - dwc2_sched_periodic_split(hsotg, qh, frame_number, - sched_next_periodic_split); - } else { - qh->sched_frame = dwc2_frame_num_inc(qh->sched_frame, - qh->interval); - if (dwc2_frame_num_le(qh->sched_frame, frame_number)) - qh->sched_frame = frame_number; - } + if (sched_next_periodic_split) + missed = dwc2_next_for_periodic_split(hsotg, qh, frame_number); + else + missed = dwc2_next_periodic_start(hsotg, qh, frame_number); + + dwc2_sch_vdbg(hsotg, + "QH=%p next(%d) fn=%04x, sch=%04x=>%04x (%+d) miss=%d %s\n", + qh, sched_next_periodic_split, frame_number, old_frame, + qh->next_active_frame, + dwc2_frame_num_dec(qh->next_active_frame, old_frame), + missed, missed ? "MISS" : ""); if (list_empty(&qh->qtd_list)) { dwc2_hcd_qh_unlink(hsotg, qh); return; } + /* * Remove from periodic_sched_queued and move to * appropriate queue + * + * Note: we purposely use the frame_number from the "hsotg" structure + * since we know SOF interrupt will handle future frames. */ - if ((hsotg->core_params->uframe_sched > 0 && - dwc2_frame_num_le(qh->sched_frame, frame_number)) || - (hsotg->core_params->uframe_sched <= 0 && - qh->sched_frame == frame_number)) - list_move(&qh->qh_list_entry, &hsotg->periodic_sched_ready); + if (dwc2_frame_num_le(qh->next_active_frame, hsotg->frame_number)) + list_move_tail(&qh->qh_list_entry, + &hsotg->periodic_sched_ready); else - list_move(&qh->qh_list_entry, &hsotg->periodic_sched_inactive); + list_move_tail(&qh->qh_list_entry, + &hsotg->periodic_sched_inactive); } /** diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 690b9fd..88629be 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -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}, {}, diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index de5e01f..17fd814 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -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); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index e4f8b90..6254b2f 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -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); diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 8d6b75c..eca2e6d 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -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); diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 2363bad..3ac170f 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -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); diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 8b14c2a..a5c6209 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -54,6 +54,36 @@ static struct usb_gadget_strings **get_containers_gs( } /** + * 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)) diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c index 0fafa7a..e6c0542 100644 --- a/drivers/usb/gadget/config.c +++ b/drivers/usb/gadget/config.c @@ -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); diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 590c449..c6cc15e 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -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; diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c index 2fa1e80..a30766c 100644 --- a/drivers/usb/gadget/function/f_acm.c +++ b/drivers/usb/gadget/function/f_acm.c @@ -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, }; diff --git a/drivers/usb/gadget/function/f_ecm.c b/drivers/usb/gadget/function/f_ecm.c index 7ad60ee..4c488d1 100644 --- a/drivers/usb/gadget/function/f_ecm.c +++ b/drivers/usb/gadget/function/f_ecm.c @@ -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; diff --git a/drivers/usb/gadget/function/f_eem.c b/drivers/usb/gadget/function/f_eem.c index cad35a5..d58bfc3 100644 --- a/drivers/usb/gadget/function/f_eem.c +++ b/drivers/usb/gadget/function/f_eem.c @@ -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; diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index cf43e9e..8cfce10 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -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; - 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; + WARN(1, "%s: data_len == -EINVAL\n", __func__); + ret = -EINVAL; + } else if (!io_data->aio) { + DECLARE_COMPLETION_ONSTACK(done); + bool interrupted = false; - io_data->buf = data; - io_data->ep = ep->ep; - io_data->req = req; - io_data->ffs = epfile->ffs; + req = ep->req; + req->buf = data; + req->length = data_len; - req->context = io_data; - req->complete = ffs_epfile_async_io_complete; + req->context = &done; + req->complete = ffs_epfile_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; + ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC); + if (unlikely(ret < 0)) + goto error_lock; - spin_unlock_irq(&epfile->ffs->eps_lock); - } else { - DECLARE_COMPLETION_ONSTACK(done); + spin_unlock_irq(&epfile->ffs->eps_lock); - req = ep->req; - req->buf = data; - req->length = data_len; + 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; + } - req->context = &done; - req->complete = ffs_epfile_io_complete; + /* + * 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; - ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC); + io_data->buf = data; + io_data->ep = ep->ep; + io_data->req = req; + io_data->ffs = epfile->ffs; - spin_unlock_irq(&epfile->ffs->eps_lock); + req->context = io_data; + req->complete = ffs_epfile_async_io_complete; - 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 = usb_ep_queue(ep->ep, req, GFP_ATOMIC); + if (unlikely(ret)) { + usb_ep_free_request(ep->ep, req); + goto error_lock; } - } - mutex_unlock(&epfile->mutex); - return ret; + ret = -EIOCBQUEUED; + /* + * Do not kfree the buffer in this function. It will be freed + * by ffs_user_copy_worker. + */ + data = NULL; + } error_lock: spin_unlock_irq(&epfile->ffs->eps_lock); +error_mutex: mutex_unlock(&epfile->mutex); error: kfree(data); diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c index 99285b4..51980c5 100644 --- a/drivers/usb/gadget/function/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c @@ -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; diff --git a/drivers/usb/gadget/function/f_loopback.c b/drivers/usb/gadget/function/f_loopback.c index ddc3aad..3a9f8f9 100644 --- a/drivers/usb/gadget/function/f_loopback.c +++ b/drivers/usb/gadget/function/f_loopback.c @@ -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; diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index 223ccf8..ee9390b 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -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; diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index fb1fe96d..84c0ee5 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -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]; - - if (!port) - break; - - if (!port->active || !substream) - continue; - - snd_rawmidi_drop_output(substream); + 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); } } -static void f_midi_transmit(struct f_midi *midi) +static int f_midi_do_transmit(struct f_midi *midi, struct usb_ep *ep) { - struct usb_ep *ep = midi->in_ep; - bool active; - - /* We only care about USB requests if IN endpoint is enabled */ - if (!ep || !ep->enabled) - goto drop_out; + struct usb_request *req = NULL; + unsigned int len, i; + bool active = false; + int err; - do { - struct usb_request *req = NULL; - unsigned int len, i; + /* + * 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; + } - active = false; + /* + * 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; - /* 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__); - goto drop_out; - } + 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 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; + if (!port->active || !substream) + continue; - 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]; + while (req->length + 3 < midi->buflen) { + uint8_t b; - if (!port) { - /* Reset counter when we reach the last available port */ - midi->in_last_port = 0; + if (snd_rawmidi_transmit(substream, &b, 1) != 1) { + port->active = 0; break; } + f_midi_transmit_byte(req, port, b); + } - if (!port->active || !substream) - continue; + active = !!port->active; + if (active) + break; + } + midi->in_last_port = active ? i : 0; - while (req->length + 3 < midi->buflen) { - uint8_t b; + if (req->length <= 0) + goto done; - if (snd_rawmidi_transmit(substream, &b, 1) != 1) { - port->active = 0; - break; - } - f_midi_transmit_byte(req, port, b); - } + 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); + } - 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; - } - } +done: + return active; +} - if (req->length > 0) { - int err; +static void f_midi_transmit(struct f_midi *midi) +{ + struct usb_ep *ep = midi->in_ep; + int ret; - 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); + /* We only care about USB requests if IN endpoint is enabled */ + if (!ep || !ep->enabled) + goto drop_out; + + do { + ret = f_midi_do_transmit(midi, ep); + if (ret < 0) + goto drop_out; + } 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); } diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index 7ad798a..97f0a9b 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -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; diff --git a/drivers/usb/gadget/function/f_obex.c b/drivers/usb/gadget/function/f_obex.c index d6396e0..d43e86c 100644 --- a/drivers/usb/gadget/function/f_obex.c +++ b/drivers/usb/gadget/function/f_obex.c @@ -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; diff --git a/drivers/usb/gadget/function/f_phonet.c b/drivers/usb/gadget/function/f_phonet.c index 157441d..0473d61 100644 --- a/drivers/usb/gadget/function/f_phonet.c +++ b/drivers/usb/gadget/function/f_phonet.c @@ -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; diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c index 26ccad5..c45104e 100644 --- a/drivers/usb/gadget/function/f_printer.c +++ b/drivers/usb/gadget/function/f_printer.c @@ -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; diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index e587767..d99dd95 100644 --- a/drivers/usb/gadget/function/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -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; diff --git a/drivers/usb/gadget/function/f_serial.c b/drivers/usb/gadget/function/f_serial.c index 6bb44d61..cb00ada 100644 --- a/drivers/usb/gadget/function/f_serial.c +++ b/drivers/usb/gadget/function/f_serial.c @@ -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", diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c index 242ba5c..df0189d 100644 --- a/drivers/usb/gadget/function/f_sourcesink.c +++ b/drivers/usb/gadget/function/f_sourcesink.c @@ -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; diff --git a/drivers/usb/gadget/function/f_subset.c b/drivers/usb/gadget/function/f_subset.c index 829c78d..434b983 100644 --- a/drivers/usb/gadget/function/f_subset.c +++ b/drivers/usb/gadget/function/f_subset.c @@ -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; diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c index bad007b5..dfb7330 100644 --- a/drivers/usb/gadget/function/f_tcm.c +++ b/drivers/usb/gadget/function/f_tcm.c @@ -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; diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index 6a2346b..f2ac0cb 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -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; diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 044ca79..186d4b1 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -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; diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c index 70d3917..943c21a 100644 --- a/drivers/usb/gadget/function/rndis.c +++ b/drivers/usb/gadget/function/rndis.c @@ -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; } diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index 87fb0fd..5cdaf01 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -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; } /*----------------------------------------------------------------------*/ diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig index 753c29b..7c28941 100644 --- a/drivers/usb/gadget/udc/Kconfig +++ b/drivers/usb/gadget/udc/Kconfig @@ -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 diff --git a/drivers/usb/gadget/udc/bdc/bdc_udc.c b/drivers/usb/gadget/udc/bdc/bdc_udc.c index 7f77db5..aae7458 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_udc.c +++ b/drivers/usb/gadget/udc/bdc/bdc_udc.c @@ -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); } diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c index 79fe6b7..8f32b5e 100644 --- a/drivers/usb/gadget/udc/lpc32xx_udc.c +++ b/drivers/usb/gadget/udc/lpc32xx_udc.c @@ -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); diff --git a/drivers/usb/gadget/udc/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c index b82cb14..a238da9 100644 --- a/drivers/usb/gadget/udc/pxa25x_udc.c +++ b/drivers/usb/gadget/udc/pxa25x_udc.c @@ -48,18 +48,157 @@ #include <linux/usb/gadget.h> #include <linux/usb/otg.h> -/* - * This driver is PXA25x only. Grab the right register definitions. - */ -#ifdef CONFIG_ARCH_PXA -#include <mach/pxa25x-udc.h> -#include <mach/hardware.h> -#endif - #ifdef CONFIG_ARCH_LUBBOCK #include <mach/lubbock.h> #endif +#define UDCCR 0x0000 /* UDC Control Register */ +#define UDC_RES1 0x0004 /* UDC Undocumented - Reserved1 */ +#define UDC_RES2 0x0008 /* UDC Undocumented - Reserved2 */ +#define UDC_RES3 0x000C /* UDC Undocumented - Reserved3 */ +#define UDCCS0 0x0010 /* UDC Endpoint 0 Control/Status Register */ +#define UDCCS1 0x0014 /* UDC Endpoint 1 (IN) Control/Status Register */ +#define UDCCS2 0x0018 /* UDC Endpoint 2 (OUT) Control/Status Register */ +#define UDCCS3 0x001C /* UDC Endpoint 3 (IN) Control/Status Register */ +#define UDCCS4 0x0020 /* UDC Endpoint 4 (OUT) Control/Status Register */ +#define UDCCS5 0x0024 /* UDC Endpoint 5 (Interrupt) Control/Status Register */ +#define UDCCS6 0x0028 /* UDC Endpoint 6 (IN) Control/Status Register */ +#define UDCCS7 0x002C /* UDC Endpoint 7 (OUT) Control/Status Register */ +#define UDCCS8 0x0030 /* UDC Endpoint 8 (IN) Control/Status Register */ +#define UDCCS9 0x0034 /* UDC Endpoint 9 (OUT) Control/Status Register */ +#define UDCCS10 0x0038 /* UDC Endpoint 10 (Interrupt) Control/Status Register */ +#define UDCCS11 0x003C /* UDC Endpoint 11 (IN) Control/Status Register */ +#define UDCCS12 0x0040 /* UDC Endpoint 12 (OUT) Control/Status Register */ +#define UDCCS13 0x0044 /* UDC Endpoint 13 (IN) Control/Status Register */ +#define UDCCS14 0x0048 /* UDC Endpoint 14 (OUT) Control/Status Register */ +#define UDCCS15 0x004C /* UDC Endpoint 15 (Interrupt) Control/Status Register */ +#define UFNRH 0x0060 /* UDC Frame Number Register High */ +#define UFNRL 0x0064 /* UDC Frame Number Register Low */ +#define UBCR2 0x0068 /* UDC Byte Count Reg 2 */ +#define UBCR4 0x006c /* UDC Byte Count Reg 4 */ +#define UBCR7 0x0070 /* UDC Byte Count Reg 7 */ +#define UBCR9 0x0074 /* UDC Byte Count Reg 9 */ +#define UBCR12 0x0078 /* UDC Byte Count Reg 12 */ +#define UBCR14 0x007c /* UDC Byte Count Reg 14 */ +#define UDDR0 0x0080 /* UDC Endpoint 0 Data Register */ +#define UDDR1 0x0100 /* UDC Endpoint 1 Data Register */ +#define UDDR2 0x0180 /* UDC Endpoint 2 Data Register */ +#define UDDR3 0x0200 /* UDC Endpoint 3 Data Register */ +#define UDDR4 0x0400 /* UDC Endpoint 4 Data Register */ +#define UDDR5 0x00A0 /* UDC Endpoint 5 Data Register */ +#define UDDR6 0x0600 /* UDC Endpoint 6 Data Register */ +#define UDDR7 0x0680 /* UDC Endpoint 7 Data Register */ +#define UDDR8 0x0700 /* UDC Endpoint 8 Data Register */ +#define UDDR9 0x0900 /* UDC Endpoint 9 Data Register */ +#define UDDR10 0x00C0 /* UDC Endpoint 10 Data Register */ +#define UDDR11 0x0B00 /* UDC Endpoint 11 Data Register */ +#define UDDR12 0x0B80 /* UDC Endpoint 12 Data Register */ +#define UDDR13 0x0C00 /* UDC Endpoint 13 Data Register */ +#define UDDR14 0x0E00 /* UDC Endpoint 14 Data Register */ +#define UDDR15 0x00E0 /* UDC Endpoint 15 Data Register */ + +#define UICR0 0x0050 /* UDC Interrupt Control Register 0 */ +#define UICR1 0x0054 /* UDC Interrupt Control Register 1 */ + +#define USIR0 0x0058 /* UDC Status Interrupt Register 0 */ +#define USIR1 0x005C /* UDC Status Interrupt Register 1 */ + +#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 */ +#ifdef CONFIG_ARCH_IXP4XX /* FIXME: is this right?, datasheed says '2' */ +#define UDCCS_IO_ROF (1 << 3) /* Receive overflow */ +#endif +#ifdef CONFIG_ARCH_PXA +#define UDCCS_IO_ROF (1 << 2) /* Receive overflow */ +#endif +#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 */ + /* * This driver handles the USB Device Controller (UDC) in Intel's PXA 25x * series processors. The UDC for the IXP 4xx series is very similar. @@ -150,25 +289,61 @@ static void pullup_on(void) mach->udc_command(PXA2XX_UDC_CMD_CONNECT); } -static void pio_irq_enable(int bEndpointAddress) +#if defined(CONFIG_CPU_BIG_ENDIAN) +/* + * IXP4xx has its buses wired up in a way that relies on never doing any + * byte swaps, independent of whether it runs in big-endian or little-endian + * mode, as explained by Krzysztof HaÅ‚asa. + * + * We only support pxa25x in little-endian mode, but it is very likely + * that it works the same way. + */ +static inline void udc_set_reg(struct pxa25x_udc *dev, u32 reg, u32 val) +{ + iowrite32be(val, dev->regs + reg); +} + +static inline u32 udc_get_reg(struct pxa25x_udc *dev, u32 reg) { - bEndpointAddress &= 0xf; + return ioread32be(dev->regs + reg); +} +#else +static inline void udc_set_reg(struct pxa25x_udc *dev, u32 reg, u32 val) +{ + writel(val, dev->regs + reg); +} + +static inline u32 udc_get_reg(struct pxa25x_udc *dev, u32 reg) +{ + return readl(dev->regs + reg); +} +#endif + +static void pio_irq_enable(struct pxa25x_ep *ep) +{ + u32 bEndpointAddress = ep->bEndpointAddress & 0xf; + if (bEndpointAddress < 8) - UICR0 &= ~(1 << bEndpointAddress); + udc_set_reg(ep->dev, UICR0, udc_get_reg(ep->dev, UICR0) & + ~(1 << bEndpointAddress)); else { bEndpointAddress -= 8; - UICR1 &= ~(1 << bEndpointAddress); + udc_set_reg(ep->dev, UICR1, udc_get_reg(ep->dev, UICR1) & + ~(1 << bEndpointAddress)); } } -static void pio_irq_disable(int bEndpointAddress) +static void pio_irq_disable(struct pxa25x_ep *ep) { - bEndpointAddress &= 0xf; + u32 bEndpointAddress = ep->bEndpointAddress & 0xf; + if (bEndpointAddress < 8) - UICR0 |= 1 << bEndpointAddress; + udc_set_reg(ep->dev, UICR0, udc_get_reg(ep->dev, UICR0) | + (1 << bEndpointAddress)); else { bEndpointAddress -= 8; - UICR1 |= 1 << bEndpointAddress; + udc_set_reg(ep->dev, UICR1, udc_get_reg(ep->dev, UICR1) | + (1 << bEndpointAddress)); } } @@ -177,22 +352,61 @@ static void pio_irq_disable(int bEndpointAddress) */ #define UDCCR_MASK_BITS (UDCCR_REM | UDCCR_SRM | UDCCR_UDE) -static inline void udc_set_mask_UDCCR(int mask) +static inline void udc_set_mask_UDCCR(struct pxa25x_udc *dev, int mask) { - UDCCR = (UDCCR & UDCCR_MASK_BITS) | (mask & UDCCR_MASK_BITS); + u32 udccr = udc_get_reg(dev, UDCCR); + + udc_set_reg(dev, (udccr & UDCCR_MASK_BITS) | (mask & UDCCR_MASK_BITS), UDCCR); } -static inline void udc_clear_mask_UDCCR(int mask) +static inline void udc_clear_mask_UDCCR(struct pxa25x_udc *dev, int mask) { - UDCCR = (UDCCR & UDCCR_MASK_BITS) & ~(mask & UDCCR_MASK_BITS); + u32 udccr = udc_get_reg(dev, UDCCR); + + udc_set_reg(dev, (udccr & UDCCR_MASK_BITS) & ~(mask & UDCCR_MASK_BITS), UDCCR); } -static inline void udc_ack_int_UDCCR(int mask) +static inline void udc_ack_int_UDCCR(struct pxa25x_udc *dev, int mask) { /* udccr contains the bits we dont want to change */ - __u32 udccr = UDCCR & UDCCR_MASK_BITS; + u32 udccr = udc_get_reg(dev, UDCCR) & UDCCR_MASK_BITS; - UDCCR = udccr | (mask & ~UDCCR_MASK_BITS); + udc_set_reg(dev, udccr | (mask & ~UDCCR_MASK_BITS), UDCCR); +} + +static inline u32 udc_ep_get_UDCCS(struct pxa25x_ep *ep) +{ + return udc_get_reg(ep->dev, ep->regoff_udccs); +} + +static inline void udc_ep_set_UDCCS(struct pxa25x_ep *ep, u32 data) +{ + udc_set_reg(ep->dev, data, ep->regoff_udccs); +} + +static inline u32 udc_ep0_get_UDCCS(struct pxa25x_udc *dev) +{ + return udc_get_reg(dev, UDCCS0); +} + +static inline void udc_ep0_set_UDCCS(struct pxa25x_udc *dev, u32 data) +{ + udc_set_reg(dev, data, UDCCS0); +} + +static inline u32 udc_ep_get_UDDR(struct pxa25x_ep *ep) +{ + return udc_get_reg(ep->dev, ep->regoff_uddr); +} + +static inline void udc_ep_set_UDDR(struct pxa25x_ep *ep, u32 data) +{ + udc_set_reg(ep->dev, data, ep->regoff_uddr); +} + +static inline u32 udc_ep_get_UBCR(struct pxa25x_ep *ep) +{ + return udc_get_reg(ep->dev, ep->regoff_ubcr); } /* @@ -358,7 +572,7 @@ static inline void ep0_idle (struct pxa25x_udc *dev) } static int -write_packet(volatile u32 *uddr, struct pxa25x_request *req, unsigned max) +write_packet(struct pxa25x_ep *ep, struct pxa25x_request *req, unsigned max) { u8 *buf; unsigned length, count; @@ -372,7 +586,7 @@ write_packet(volatile u32 *uddr, struct pxa25x_request *req, unsigned max) count = length; while (likely(count--)) - *uddr = *buf++; + udc_ep_set_UDDR(ep, *buf++); return length; } @@ -392,7 +606,7 @@ write_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req) unsigned count; int is_last, is_short; - count = write_packet(ep->reg_uddr, req, max); + count = write_packet(ep, req, max); /* last packet is usually short (or a zlp) */ if (unlikely (count != max)) @@ -416,15 +630,15 @@ write_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req) * double buffering might work. TSP, TPC, and TFS * bit values are the same for all normal IN endpoints. */ - *ep->reg_udccs = UDCCS_BI_TPC; + udc_ep_set_UDCCS(ep, UDCCS_BI_TPC); if (is_short) - *ep->reg_udccs = UDCCS_BI_TSP; + udc_ep_set_UDCCS(ep, UDCCS_BI_TSP); /* requests complete when all IN data is in the FIFO */ if (is_last) { done (ep, req, 0); if (list_empty(&ep->queue)) - pio_irq_disable (ep->bEndpointAddress); + pio_irq_disable(ep); return 1; } @@ -432,7 +646,7 @@ write_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req) // double buffering is off in the default fifo mode, which // prevents TFS from being set here. - } while (*ep->reg_udccs & UDCCS_BI_TFS); + } while (udc_ep_get_UDCCS(ep) & UDCCS_BI_TFS); return 0; } @@ -442,20 +656,21 @@ write_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req) static inline void ep0start(struct pxa25x_udc *dev, u32 flags, const char *tag) { - UDCCS0 = flags|UDCCS0_SA|UDCCS0_OPR; - USIR0 = USIR0_IR0; + udc_ep0_set_UDCCS(dev, flags|UDCCS0_SA|UDCCS0_OPR); + udc_set_reg(dev, USIR0, USIR0_IR0); dev->req_pending = 0; DBG(DBG_VERY_NOISY, "%s %s, %02x/%02x\n", - __func__, tag, UDCCS0, flags); + __func__, tag, udc_ep0_get_UDCCS(dev), flags); } static int write_ep0_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req) { + struct pxa25x_udc *dev = ep->dev; unsigned count; int is_short; - count = write_packet(&UDDR0, req, EP0_FIFO_SIZE); + count = write_packet(&dev->ep[0], req, EP0_FIFO_SIZE); ep->dev->stats.write.bytes += count; /* last packet "must be" short (or a zlp) */ @@ -468,7 +683,7 @@ write_ep0_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req) if (ep->dev->req_pending) ep0start(ep->dev, UDCCS0_IPR, "short IN"); else - UDCCS0 = UDCCS0_IPR; + udc_ep0_set_UDCCS(dev, UDCCS0_IPR); count = req->req.length; done (ep, req, 0); @@ -484,9 +699,9 @@ write_ep0_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req) if (count >= EP0_FIFO_SIZE) { count = 100; do { - if ((UDCCS0 & UDCCS0_OPR) != 0) { + if ((udc_ep0_get_UDCCS(dev) & UDCCS0_OPR) != 0) { /* clear OPR, generate ack */ - UDCCS0 = UDCCS0_OPR; + udc_ep0_set_UDCCS(dev, UDCCS0_OPR); break; } count--; @@ -521,7 +736,7 @@ read_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req) * UDCCS_{BO,IO}_RPC are all the same bit value. * UDCCS_{BO,IO}_RNE are all the same bit value. */ - udccs = *ep->reg_udccs; + udccs = udc_ep_get_UDCCS(ep); if (unlikely ((udccs & UDCCS_BO_RPC) == 0)) break; buf = req->req.buf + req->req.actual; @@ -530,7 +745,7 @@ read_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req) /* read all bytes from this packet */ if (likely (udccs & UDCCS_BO_RNE)) { - count = 1 + (0x0ff & *ep->reg_ubcr); + count = 1 + (0x0ff & udc_ep_get_UBCR(ep)); req->req.actual += min (count, bufferspace); } else /* zlp */ count = 0; @@ -540,7 +755,7 @@ read_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req) is_short ? "/S" : "", req, req->req.actual, req->req.length); while (likely (count-- != 0)) { - u8 byte = (u8) *ep->reg_uddr; + u8 byte = (u8) udc_ep_get_UDDR(ep); if (unlikely (bufferspace == 0)) { /* this happens when the driver's buffer @@ -556,7 +771,7 @@ read_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req) bufferspace--; } } - *ep->reg_udccs = UDCCS_BO_RPC; + udc_ep_set_UDCCS(ep, UDCCS_BO_RPC); /* RPC/RSP/RNE could now reflect the other packet buffer */ /* iso is one request per packet */ @@ -571,7 +786,7 @@ read_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req) if (is_short || req->req.actual == req->req.length) { done (ep, req, 0); if (list_empty(&ep->queue)) - pio_irq_disable (ep->bEndpointAddress); + pio_irq_disable(ep); return 1; } @@ -595,7 +810,7 @@ read_ep0_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req) buf = req->req.buf + req->req.actual; bufferspace = req->req.length - req->req.actual; - while (UDCCS0 & UDCCS0_RNE) { + while (udc_ep_get_UDCCS(ep) & UDCCS0_RNE) { byte = (u8) UDDR0; if (unlikely (bufferspace == 0)) { @@ -613,7 +828,7 @@ read_ep0_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req) } } - UDCCS0 = UDCCS0_OPR | UDCCS0_IPR; + udc_ep_set_UDCCS(ep, UDCCS0_OPR | UDCCS0_IPR); /* completion */ if (req->req.actual >= req->req.length) @@ -687,8 +902,8 @@ pxa25x_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) DBG(DBG_VERBOSE, "ep0 config ack%s\n", dev->has_cfr ? "" : " raced"); if (dev->has_cfr) - UDCCFR = UDCCFR_AREN|UDCCFR_ACM - |UDCCFR_MB1; + udc_set_reg(dev, UDCCFR, UDCCFR_AREN | + UDCCFR_ACM | UDCCFR_MB1); done(ep, req, 0); dev->ep0state = EP0_END_XFER; local_irq_restore (flags); @@ -696,7 +911,7 @@ pxa25x_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) } if (dev->req_pending) ep0start(dev, UDCCS0_IPR, "OUT"); - if (length == 0 || ((UDCCS0 & UDCCS0_RNE) != 0 + if (length == 0 || ((udc_ep0_get_UDCCS(dev) & UDCCS0_RNE) != 0 && read_ep0_fifo(ep, req))) { ep0_idle(dev); done(ep, req, 0); @@ -711,16 +926,16 @@ pxa25x_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) } /* can the FIFO can satisfy the request immediately? */ } else if ((ep->bEndpointAddress & USB_DIR_IN) != 0) { - if ((*ep->reg_udccs & UDCCS_BI_TFS) != 0 + if ((udc_ep_get_UDCCS(ep) & UDCCS_BI_TFS) != 0 && write_fifo(ep, req)) req = NULL; - } else if ((*ep->reg_udccs & UDCCS_BO_RFS) != 0 + } else if ((udc_ep_get_UDCCS(ep) & UDCCS_BO_RFS) != 0 && read_fifo(ep, req)) { req = NULL; } if (likely(req && ep->ep.desc)) - pio_irq_enable(ep->bEndpointAddress); + pio_irq_enable(ep); } /* pio or dma irq handler advances the queue. */ @@ -747,7 +962,7 @@ static void nuke(struct pxa25x_ep *ep, int status) done(ep, req, status); } if (ep->ep.desc) - pio_irq_disable (ep->bEndpointAddress); + pio_irq_disable(ep); } @@ -807,14 +1022,14 @@ static int pxa25x_ep_set_halt(struct usb_ep *_ep, int value) local_irq_save(flags); if ((ep->bEndpointAddress & USB_DIR_IN) != 0 - && ((*ep->reg_udccs & UDCCS_BI_TFS) == 0 + && ((udc_ep_get_UDCCS(ep) & UDCCS_BI_TFS) == 0 || !list_empty(&ep->queue))) { local_irq_restore(flags); return -EAGAIN; } /* FST bit is the same for control, bulk in, bulk out, interrupt in */ - *ep->reg_udccs = UDCCS_BI_FST|UDCCS_BI_FTF; + udc_ep_set_UDCCS(ep, UDCCS_BI_FST|UDCCS_BI_FTF); /* ep0 needs special care */ if (!ep->ep.desc) { @@ -826,7 +1041,7 @@ static int pxa25x_ep_set_halt(struct usb_ep *_ep, int value) } else { unsigned i; for (i = 0; i < 1000; i += 20) { - if (*ep->reg_udccs & UDCCS_BI_SST) + if (udc_ep_get_UDCCS(ep) & UDCCS_BI_SST) break; udelay(20); } @@ -850,10 +1065,10 @@ static int pxa25x_ep_fifo_status(struct usb_ep *_ep) if ((ep->bEndpointAddress & USB_DIR_IN) != 0) return -EOPNOTSUPP; if (ep->dev->gadget.speed == USB_SPEED_UNKNOWN - || (*ep->reg_udccs & UDCCS_BO_RFS) == 0) + || (udc_ep_get_UDCCS(ep) & UDCCS_BO_RFS) == 0) return 0; else - return (*ep->reg_ubcr & 0xfff) + 1; + return (udc_ep_get_UBCR(ep) & 0xfff) + 1; } static void pxa25x_ep_fifo_flush(struct usb_ep *_ep) @@ -870,15 +1085,15 @@ static void pxa25x_ep_fifo_flush(struct usb_ep *_ep) /* for OUT, just read and discard the FIFO contents. */ if ((ep->bEndpointAddress & USB_DIR_IN) == 0) { - while (((*ep->reg_udccs) & UDCCS_BO_RNE) != 0) - (void) *ep->reg_uddr; + while (((udc_ep_get_UDCCS(ep)) & UDCCS_BO_RNE) != 0) + (void)udc_ep_get_UDDR(ep); return; } /* most IN status is the same, but ISO can't stall */ - *ep->reg_udccs = UDCCS_BI_TPC|UDCCS_BI_FTF|UDCCS_BI_TUR + udc_ep_set_UDCCS(ep, UDCCS_BI_TPC|UDCCS_BI_FTF|UDCCS_BI_TUR | (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC - ? 0 : UDCCS_BI_SST); + ? 0 : UDCCS_BI_SST)); } @@ -905,15 +1120,23 @@ static struct usb_ep_ops pxa25x_ep_ops = { static int pxa25x_udc_get_frame(struct usb_gadget *_gadget) { - return ((UFNRH & 0x07) << 8) | (UFNRL & 0xff); + struct pxa25x_udc *dev; + + dev = container_of(_gadget, struct pxa25x_udc, gadget); + return ((udc_get_reg(dev, UFNRH) & 0x07) << 8) | + (udc_get_reg(dev, UFNRL) & 0xff); } static int pxa25x_udc_wakeup(struct usb_gadget *_gadget) { + struct pxa25x_udc *udc; + + udc = container_of(_gadget, struct pxa25x_udc, gadget); + /* host may not have enabled remote wakeup */ - if ((UDCCS0 & UDCCS0_DRWF) == 0) + if ((udc_ep0_get_UDCCS(udc) & UDCCS0_DRWF) == 0) return -EHOSTUNREACH; - udc_set_mask_UDCCR(UDCCR_RSM); + udc_set_mask_UDCCR(udc, UDCCR_RSM); return 0; } @@ -1034,9 +1257,11 @@ udc_seq_show(struct seq_file *m, void *_d) /* registers for device and ep0 */ seq_printf(m, "uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n", - UICR1, UICR0, USIR1, USIR0, UFNRH, UFNRL); + udc_get_reg(dev, UICR1), udc_get_reg(dev, UICR0), + udc_get_reg(dev, USIR1), udc_get_reg(dev, USIR0), + udc_get_reg(dev, UFNRH), udc_get_reg(dev, UFNRL)); - tmp = UDCCR; + tmp = udc_get_reg(dev, UDCCR); seq_printf(m, "udccr %02X =%s%s%s%s%s%s%s%s\n", tmp, (tmp & UDCCR_REM) ? " rem" : "", @@ -1048,7 +1273,7 @@ udc_seq_show(struct seq_file *m, void *_d) (tmp & UDCCR_UDA) ? " uda" : "", (tmp & UDCCR_UDE) ? " ude" : ""); - tmp = UDCCS0; + tmp = udc_ep0_get_UDCCS(dev); seq_printf(m, "udccs0 %02X =%s%s%s%s%s%s%s%s\n", tmp, (tmp & UDCCS0_SA) ? " sa" : "", @@ -1061,7 +1286,7 @@ udc_seq_show(struct seq_file *m, void *_d) (tmp & UDCCS0_OPR) ? " opr" : ""); if (dev->has_cfr) { - tmp = UDCCFR; + tmp = udc_get_reg(dev, UDCCFR); seq_printf(m, "udccfr %02X =%s%s\n", tmp, (tmp & UDCCFR_AREN) ? " aren" : "", @@ -1087,7 +1312,7 @@ udc_seq_show(struct seq_file *m, void *_d) desc = ep->ep.desc; if (!desc) continue; - tmp = *dev->ep [i].reg_udccs; + tmp = udc_ep_get_UDCCS(&dev->ep[i]); seq_printf(m, "%s max %d %s udccs %02x irqs %lu\n", ep->ep.name, usb_endpoint_maxp(desc), @@ -1151,14 +1376,15 @@ static const struct file_operations debug_fops = { static void udc_disable(struct pxa25x_udc *dev) { /* block all irqs */ - udc_set_mask_UDCCR(UDCCR_SRM|UDCCR_REM); - UICR0 = UICR1 = 0xff; - UFNRH = UFNRH_SIM; + udc_set_mask_UDCCR(dev, UDCCR_SRM|UDCCR_REM); + udc_set_reg(dev, UICR0, 0xff); + udc_set_reg(dev, UICR1, 0xff); + udc_set_reg(dev, UFNRH, UFNRH_SIM); /* if hardware supports it, disconnect from usb */ pullup_off(); - udc_clear_mask_UDCCR(UDCCR_UDE); + udc_clear_mask_UDCCR(dev, UDCCR_UDE); ep0_idle (dev); dev->gadget.speed = USB_SPEED_UNKNOWN; @@ -1200,10 +1426,10 @@ static void udc_reinit(struct pxa25x_udc *dev) */ static void udc_enable (struct pxa25x_udc *dev) { - udc_clear_mask_UDCCR(UDCCR_UDE); + udc_clear_mask_UDCCR(dev, UDCCR_UDE); /* try to clear these bits before we enable the udc */ - udc_ack_int_UDCCR(UDCCR_SUSIR|/*UDCCR_RSTIR|*/UDCCR_RESIR); + udc_ack_int_UDCCR(dev, UDCCR_SUSIR|/*UDCCR_RSTIR|*/UDCCR_RESIR); ep0_idle(dev); dev->gadget.speed = USB_SPEED_UNKNOWN; @@ -1215,15 +1441,15 @@ static void udc_enable (struct pxa25x_udc *dev) * - if RESET is already in progress, ack interrupt * - unmask reset interrupt */ - udc_set_mask_UDCCR(UDCCR_UDE); - if (!(UDCCR & UDCCR_UDA)) - udc_ack_int_UDCCR(UDCCR_RSTIR); + udc_set_mask_UDCCR(dev, UDCCR_UDE); + if (!(udc_get_reg(dev, UDCCR) & UDCCR_UDA)) + udc_ack_int_UDCCR(dev, UDCCR_RSTIR); if (dev->has_cfr /* UDC_RES2 is defined */) { /* pxa255 (a0+) can avoid a set_config race that could * prevent gadget drivers from configuring correctly */ - UDCCFR = UDCCFR_ACM | UDCCFR_MB1; + udc_set_reg(dev, UDCCFR, UDCCFR_ACM | UDCCFR_MB1); } else { /* "USB test mode" for pxa250 errata 40-42 (stepping a0, a1) * which could result in missing packets and interrupts. @@ -1231,15 +1457,15 @@ static void udc_enable (struct pxa25x_udc *dev) * double buffers or not; ACM/AREN bits fit into the holes. * zero bits (like USIR0_IRx) disable double buffering. */ - UDC_RES1 = 0x00; - UDC_RES2 = 0x00; + udc_set_reg(dev, UDC_RES1, 0x00); + udc_set_reg(dev, UDC_RES2, 0x00); } /* enable suspend/resume and reset irqs */ - udc_clear_mask_UDCCR(UDCCR_SRM | UDCCR_REM); + udc_clear_mask_UDCCR(dev, UDCCR_SRM | UDCCR_REM); /* enable ep0 irqs */ - UICR0 &= ~UICR0_IM0; + udc_set_reg(dev, UICR0, udc_get_reg(dev, UICR0) & ~UICR0_IM0); /* if hardware supports it, pullup D+ and wait for reset */ pullup_on(); @@ -1408,9 +1634,9 @@ static void udc_watchdog(unsigned long _dev) local_irq_disable(); if (dev->ep0state == EP0_STALL - && (UDCCS0 & UDCCS0_FST) == 0 - && (UDCCS0 & UDCCS0_SST) == 0) { - UDCCS0 = UDCCS0_FST|UDCCS0_FTF; + && (udc_ep0_get_UDCCS(dev) & UDCCS0_FST) == 0 + && (udc_ep0_get_UDCCS(dev) & UDCCS0_SST) == 0) { + udc_ep0_set_UDCCS(dev, UDCCS0_FST|UDCCS0_FTF); DBG(DBG_VERBOSE, "ep0 re-stall\n"); start_watchdog(dev); } @@ -1419,7 +1645,7 @@ static void udc_watchdog(unsigned long _dev) static void handle_ep0 (struct pxa25x_udc *dev) { - u32 udccs0 = UDCCS0; + u32 udccs0 = udc_ep0_get_UDCCS(dev); struct pxa25x_ep *ep = &dev->ep [0]; struct pxa25x_request *req; union { @@ -1436,7 +1662,7 @@ static void handle_ep0 (struct pxa25x_udc *dev) /* clear stall status */ if (udccs0 & UDCCS0_SST) { nuke(ep, -EPIPE); - UDCCS0 = UDCCS0_SST; + udc_ep0_set_UDCCS(dev, UDCCS0_SST); del_timer(&dev->timer); ep0_idle(dev); } @@ -1451,7 +1677,7 @@ static void handle_ep0 (struct pxa25x_udc *dev) switch (dev->ep0state) { case EP0_IDLE: /* late-breaking status? */ - udccs0 = UDCCS0; + udccs0 = udc_ep0_get_UDCCS(dev); /* start control request? */ if (likely((udccs0 & (UDCCS0_OPR|UDCCS0_SA|UDCCS0_RNE)) @@ -1462,14 +1688,14 @@ static void handle_ep0 (struct pxa25x_udc *dev) /* read SETUP packet */ for (i = 0; i < 8; i++) { - if (unlikely(!(UDCCS0 & UDCCS0_RNE))) { + if (unlikely(!(udc_ep0_get_UDCCS(dev) & UDCCS0_RNE))) { bad_setup: DMSG("SETUP %d!\n", i); goto stall; } u.raw [i] = (u8) UDDR0; } - if (unlikely((UDCCS0 & UDCCS0_RNE) != 0)) + if (unlikely((udc_ep0_get_UDCCS(dev) & UDCCS0_RNE) != 0)) goto bad_setup; got_setup: @@ -1545,7 +1771,7 @@ config_change: */ } DBG(DBG_VERBOSE, "protocol STALL, " - "%02x err %d\n", UDCCS0, i); + "%02x err %d\n", udc_ep0_get_UDCCS(dev), i); stall: /* the watchdog timer helps deal with cases * where udc seems to clear FST wrongly, and @@ -1592,12 +1818,12 @@ stall: * - IPR cleared * - OPR got set, without SA (likely status stage) */ - UDCCS0 = udccs0 & (UDCCS0_SA|UDCCS0_OPR); + udc_ep0_set_UDCCS(dev, udccs0 & (UDCCS0_SA|UDCCS0_OPR)); } break; case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */ if (udccs0 & UDCCS0_OPR) { - UDCCS0 = UDCCS0_OPR|UDCCS0_FTF; + udc_ep0_set_UDCCS(dev, UDCCS0_OPR|UDCCS0_FTF); DBG(DBG_VERBOSE, "ep0in premature status\n"); if (req) done(ep, req, 0); @@ -1631,14 +1857,14 @@ stall: * also appears after some config change events. */ if (udccs0 & UDCCS0_OPR) - UDCCS0 = UDCCS0_OPR; + udc_ep0_set_UDCCS(dev, UDCCS0_OPR); ep0_idle(dev); break; case EP0_STALL: - UDCCS0 = UDCCS0_FST; + udc_ep0_set_UDCCS(dev, UDCCS0_FST); break; } - USIR0 = USIR0_IR0; + udc_set_reg(dev, USIR0, USIR0_IR0); } static void handle_ep(struct pxa25x_ep *ep) @@ -1658,14 +1884,14 @@ static void handle_ep(struct pxa25x_ep *ep) // TODO check FST handling - udccs = *ep->reg_udccs; + udccs = udc_ep_get_UDCCS(ep); if (unlikely(is_in)) { /* irq from TPC, SST, or (ISO) TUR */ tmp = UDCCS_BI_TUR; if (likely(ep->bmAttributes == USB_ENDPOINT_XFER_BULK)) tmp |= UDCCS_BI_SST; tmp &= udccs; if (likely (tmp)) - *ep->reg_udccs = tmp; + udc_ep_set_UDCCS(ep, tmp); if (req && likely ((udccs & UDCCS_BI_TFS) != 0)) completed = write_fifo(ep, req); @@ -1676,13 +1902,13 @@ static void handle_ep(struct pxa25x_ep *ep) tmp = UDCCS_IO_ROF | UDCCS_IO_DME; tmp &= udccs; if (likely(tmp)) - *ep->reg_udccs = tmp; + udc_ep_set_UDCCS(ep, tmp); /* fifos can hold packets, ready for reading... */ if (likely(req)) { completed = read_fifo(ep, req); } else - pio_irq_disable (ep->bEndpointAddress); + pio_irq_disable(ep); } ep->pio_irqs++; } while (completed); @@ -1703,13 +1929,13 @@ pxa25x_udc_irq(int irq, void *_dev) dev->stats.irqs++; do { - u32 udccr = UDCCR; + u32 udccr = udc_get_reg(dev, UDCCR); handled = 0; /* SUSpend Interrupt Request */ if (unlikely(udccr & UDCCR_SUSIR)) { - udc_ack_int_UDCCR(UDCCR_SUSIR); + udc_ack_int_UDCCR(dev, UDCCR_SUSIR); handled = 1; DBG(DBG_VERBOSE, "USB suspend\n"); @@ -1722,7 +1948,7 @@ pxa25x_udc_irq(int irq, void *_dev) /* RESume Interrupt Request */ if (unlikely(udccr & UDCCR_RESIR)) { - udc_ack_int_UDCCR(UDCCR_RESIR); + udc_ack_int_UDCCR(dev, UDCCR_RESIR); handled = 1; DBG(DBG_VERBOSE, "USB resume\n"); @@ -1734,10 +1960,10 @@ pxa25x_udc_irq(int irq, void *_dev) /* ReSeT Interrupt Request - USB reset */ if (unlikely(udccr & UDCCR_RSTIR)) { - udc_ack_int_UDCCR(UDCCR_RSTIR); + udc_ack_int_UDCCR(dev, UDCCR_RSTIR); handled = 1; - if ((UDCCR & UDCCR_UDA) == 0) { + if ((udc_get_reg(dev, UDCCR) & UDCCR_UDA) == 0) { DBG(DBG_VERBOSE, "USB reset start\n"); /* reset driver and endpoints, @@ -1753,8 +1979,10 @@ pxa25x_udc_irq(int irq, void *_dev) } } else { - u32 usir0 = USIR0 & ~UICR0; - u32 usir1 = USIR1 & ~UICR1; + u32 usir0 = udc_get_reg(dev, USIR0) & + ~udc_get_reg(dev, UICR0); + u32 usir1 = udc_get_reg(dev, USIR1) & + ~udc_get_reg(dev, UICR1); int i; if (unlikely (!usir0 && !usir1)) @@ -1775,13 +2003,15 @@ pxa25x_udc_irq(int irq, void *_dev) if (i && (usir0 & tmp)) { handle_ep(&dev->ep[i]); - USIR0 |= tmp; + udc_set_reg(dev, USIR0, + udc_get_reg(dev, USIR0) | tmp); handled = 1; } #ifndef CONFIG_USB_PXA25X_SMALL if (usir1 & tmp) { handle_ep(&dev->ep[i+8]); - USIR1 |= tmp; + udc_set_reg(dev, USIR1, + udc_get_reg(dev, USIR1) | tmp); handled = 1; } #endif @@ -1826,8 +2056,8 @@ static struct pxa25x_udc memory = { USB_EP_CAPS_DIR_ALL), }, .dev = &memory, - .reg_udccs = &UDCCS0, - .reg_uddr = &UDDR0, + .regoff_udccs = UDCCS0, + .regoff_uddr = UDDR0, }, /* first group of endpoints */ @@ -1843,8 +2073,8 @@ static struct pxa25x_udc memory = { .fifo_size = BULK_FIFO_SIZE, .bEndpointAddress = USB_DIR_IN | 1, .bmAttributes = USB_ENDPOINT_XFER_BULK, - .reg_udccs = &UDCCS1, - .reg_uddr = &UDDR1, + .regoff_udccs = UDCCS1, + .regoff_uddr = UDDR1, }, .ep[2] = { .ep = { @@ -1858,9 +2088,9 @@ static struct pxa25x_udc memory = { .fifo_size = BULK_FIFO_SIZE, .bEndpointAddress = 2, .bmAttributes = USB_ENDPOINT_XFER_BULK, - .reg_udccs = &UDCCS2, - .reg_ubcr = &UBCR2, - .reg_uddr = &UDDR2, + .regoff_udccs = UDCCS2, + .regoff_ubcr = UBCR2, + .regoff_uddr = UDDR2, }, #ifndef CONFIG_USB_PXA25X_SMALL .ep[3] = { @@ -1875,8 +2105,8 @@ static struct pxa25x_udc memory = { .fifo_size = ISO_FIFO_SIZE, .bEndpointAddress = USB_DIR_IN | 3, .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .reg_udccs = &UDCCS3, - .reg_uddr = &UDDR3, + .regoff_udccs = UDCCS3, + .regoff_uddr = UDDR3, }, .ep[4] = { .ep = { @@ -1890,9 +2120,9 @@ static struct pxa25x_udc memory = { .fifo_size = ISO_FIFO_SIZE, .bEndpointAddress = 4, .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .reg_udccs = &UDCCS4, - .reg_ubcr = &UBCR4, - .reg_uddr = &UDDR4, + .regoff_udccs = UDCCS4, + .regoff_ubcr = UBCR4, + .regoff_uddr = UDDR4, }, .ep[5] = { .ep = { @@ -1905,8 +2135,8 @@ static struct pxa25x_udc memory = { .fifo_size = INT_FIFO_SIZE, .bEndpointAddress = USB_DIR_IN | 5, .bmAttributes = USB_ENDPOINT_XFER_INT, - .reg_udccs = &UDCCS5, - .reg_uddr = &UDDR5, + .regoff_udccs = UDCCS5, + .regoff_uddr = UDDR5, }, /* second group of endpoints */ @@ -1922,8 +2152,8 @@ static struct pxa25x_udc memory = { .fifo_size = BULK_FIFO_SIZE, .bEndpointAddress = USB_DIR_IN | 6, .bmAttributes = USB_ENDPOINT_XFER_BULK, - .reg_udccs = &UDCCS6, - .reg_uddr = &UDDR6, + .regoff_udccs = UDCCS6, + .regoff_uddr = UDDR6, }, .ep[7] = { .ep = { @@ -1937,9 +2167,9 @@ static struct pxa25x_udc memory = { .fifo_size = BULK_FIFO_SIZE, .bEndpointAddress = 7, .bmAttributes = USB_ENDPOINT_XFER_BULK, - .reg_udccs = &UDCCS7, - .reg_ubcr = &UBCR7, - .reg_uddr = &UDDR7, + .regoff_udccs = UDCCS7, + .regoff_ubcr = UBCR7, + .regoff_uddr = UDDR7, }, .ep[8] = { .ep = { @@ -1953,8 +2183,8 @@ static struct pxa25x_udc memory = { .fifo_size = ISO_FIFO_SIZE, .bEndpointAddress = USB_DIR_IN | 8, .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .reg_udccs = &UDCCS8, - .reg_uddr = &UDDR8, + .regoff_udccs = UDCCS8, + .regoff_uddr = UDDR8, }, .ep[9] = { .ep = { @@ -1968,9 +2198,9 @@ static struct pxa25x_udc memory = { .fifo_size = ISO_FIFO_SIZE, .bEndpointAddress = 9, .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .reg_udccs = &UDCCS9, - .reg_ubcr = &UBCR9, - .reg_uddr = &UDDR9, + .regoff_udccs = UDCCS9, + .regoff_ubcr = UBCR9, + .regoff_uddr = UDDR9, }, .ep[10] = { .ep = { @@ -1983,8 +2213,8 @@ static struct pxa25x_udc memory = { .fifo_size = INT_FIFO_SIZE, .bEndpointAddress = USB_DIR_IN | 10, .bmAttributes = USB_ENDPOINT_XFER_INT, - .reg_udccs = &UDCCS10, - .reg_uddr = &UDDR10, + .regoff_udccs = UDCCS10, + .regoff_uddr = UDDR10, }, /* third group of endpoints */ @@ -2000,8 +2230,8 @@ static struct pxa25x_udc memory = { .fifo_size = BULK_FIFO_SIZE, .bEndpointAddress = USB_DIR_IN | 11, .bmAttributes = USB_ENDPOINT_XFER_BULK, - .reg_udccs = &UDCCS11, - .reg_uddr = &UDDR11, + .regoff_udccs = UDCCS11, + .regoff_uddr = UDDR11, }, .ep[12] = { .ep = { @@ -2015,9 +2245,9 @@ static struct pxa25x_udc memory = { .fifo_size = BULK_FIFO_SIZE, .bEndpointAddress = 12, .bmAttributes = USB_ENDPOINT_XFER_BULK, - .reg_udccs = &UDCCS12, - .reg_ubcr = &UBCR12, - .reg_uddr = &UDDR12, + .regoff_udccs = UDCCS12, + .regoff_ubcr = UBCR12, + .regoff_uddr = UDDR12, }, .ep[13] = { .ep = { @@ -2031,8 +2261,8 @@ static struct pxa25x_udc memory = { .fifo_size = ISO_FIFO_SIZE, .bEndpointAddress = USB_DIR_IN | 13, .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .reg_udccs = &UDCCS13, - .reg_uddr = &UDDR13, + .regoff_udccs = UDCCS13, + .regoff_uddr = UDDR13, }, .ep[14] = { .ep = { @@ -2046,9 +2276,9 @@ static struct pxa25x_udc memory = { .fifo_size = ISO_FIFO_SIZE, .bEndpointAddress = 14, .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .reg_udccs = &UDCCS14, - .reg_ubcr = &UBCR14, - .reg_uddr = &UDDR14, + .regoff_udccs = UDCCS14, + .regoff_ubcr = UBCR14, + .regoff_uddr = UDDR14, }, .ep[15] = { .ep = { @@ -2061,8 +2291,8 @@ static struct pxa25x_udc memory = { .fifo_size = INT_FIFO_SIZE, .bEndpointAddress = USB_DIR_IN | 15, .bmAttributes = USB_ENDPOINT_XFER_INT, - .reg_udccs = &UDCCS15, - .reg_uddr = &UDDR15, + .regoff_udccs = UDCCS15, + .regoff_uddr = UDDR15, }, #endif /* !CONFIG_USB_PXA25X_SMALL */ }; @@ -2109,6 +2339,7 @@ static int pxa25x_udc_probe(struct platform_device *pdev) struct pxa25x_udc *dev = &memory; int retval, irq; u32 chiprev; + struct resource *res; pr_info("%s: version %s\n", driver_name, DRIVER_VERSION); @@ -2154,6 +2385,11 @@ static int pxa25x_udc_probe(struct platform_device *pdev) if (irq < 0) return -ENODEV; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dev->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dev->regs)) + return PTR_ERR(dev->regs); + dev->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(dev->clk)) return PTR_ERR(dev->clk); diff --git a/drivers/usb/gadget/udc/pxa25x_udc.h b/drivers/usb/gadget/udc/pxa25x_udc.h index 3fe5931..4b8b72d 100644 --- a/drivers/usb/gadget/udc/pxa25x_udc.h +++ b/drivers/usb/gadget/udc/pxa25x_udc.h @@ -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])); } } diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c index b86a6f0..4151597 100644 --- a/drivers/usb/gadget/udc/udc-core.c +++ b/drivers/usb/gadget/udc/udc-core.c @@ -443,6 +443,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 * driver's device. diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 1f117c3..3050b18b 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -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. diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 65a06b4..a9ddd3c 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -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 diff --git a/drivers/usb/host/bcma-hcd.c b/drivers/usb/host/bcma-hcd.c index 291aaa2..963e2d0 100644 --- a/drivers/usb/host/bcma-hcd.c +++ b/drivers/usb/host/bcma-hcd.c @@ -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); diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c index be0964a..7440722 100644 --- a/drivers/usb/host/ehci-atmel.c +++ b/drivers/usb/host/ehci-atmel.c @@ -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[] = { diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index b7d623f..79d12b2 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -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 */ diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 3b6eb21..9f5ffb6 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -35,6 +35,7 @@ #include <linux/usb/otg.h> #include <linux/platform_device.h> #include <linux/fsl_devices.h> +#include <linux/of_platform.h> #include "ehci.h" #include "ehci-fsl.h" @@ -241,7 +242,8 @@ static int ehci_fsl_setup_phy(struct usb_hcd *hcd, * to portsc */ if (pdata->check_phy_clk_valid) { - if (!(in_be32(non_ehci + FSL_SOC_USB_CTRL) & PHY_CLK_VALID)) { + if (!(ioread32be(non_ehci + FSL_SOC_USB_CTRL) & + PHY_CLK_VALID)) { dev_warn(hcd->self.controller, "USB PHY clock invalid\n"); return -EINVAL; @@ -273,9 +275,11 @@ static int ehci_fsl_usb_setup(struct ehci_hcd *ehci) /* Setup Snooping for all the 4GB space */ /* SNOOP1 starts from 0x0, size 2G */ - out_be32(non_ehci + FSL_SOC_USB_SNOOP1, 0x0 | SNOOP_SIZE_2GB); + iowrite32be(0x0 | SNOOP_SIZE_2GB, + non_ehci + FSL_SOC_USB_SNOOP1); /* SNOOP2 starts from 0x80000000, size 2G */ - out_be32(non_ehci + FSL_SOC_USB_SNOOP2, 0x80000000 | SNOOP_SIZE_2GB); + iowrite32be(0x80000000 | SNOOP_SIZE_2GB, + non_ehci + FSL_SOC_USB_SNOOP2); } /* Deal with USB erratum A-005275 */ @@ -309,13 +313,13 @@ static int ehci_fsl_usb_setup(struct ehci_hcd *ehci) if (pdata->have_sysif_regs) { #ifdef CONFIG_FSL_SOC_BOOKE - out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x00000008); - out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000080); + iowrite32be(0x00000008, non_ehci + FSL_SOC_USB_PRICTRL); + iowrite32be(0x00000080, non_ehci + FSL_SOC_USB_AGECNTTHRSH); #else - out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x0000000c); - out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000040); + iowrite32be(0x0000000c, non_ehci + FSL_SOC_USB_PRICTRL); + iowrite32be(0x00000040, non_ehci + FSL_SOC_USB_AGECNTTHRSH); #endif - out_be32(non_ehci + FSL_SOC_USB_SICTRL, 0x00000001); + iowrite32be(0x00000001, non_ehci + FSL_SOC_USB_SICTRL); } return 0; @@ -554,7 +558,7 @@ static int ehci_fsl_drv_suspend(struct device *dev) if (!fsl_deep_sleep()) return 0; - ehci_fsl->usb_ctrl = in_be32(non_ehci + FSL_SOC_USB_CTRL); + ehci_fsl->usb_ctrl = ioread32be(non_ehci + FSL_SOC_USB_CTRL); return 0; } @@ -577,7 +581,7 @@ static int ehci_fsl_drv_resume(struct device *dev) usb_root_hub_lost_power(hcd->self.root_hub); /* Restore USB PHY settings and enable the controller. */ - out_be32(non_ehci + FSL_SOC_USB_CTRL, ehci_fsl->usb_ctrl); + iowrite32be(ehci_fsl->usb_ctrl, non_ehci + FSL_SOC_USB_CTRL); ehci_reset(ehci); ehci_fsl_reinit(ehci); diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 14178bb..ae1b6e6 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -306,9 +306,9 @@ static void ehci_quiesce (struct ehci_hcd *ehci) /*-------------------------------------------------------------------------*/ +static void end_iaa_cycle(struct ehci_hcd *ehci); static void end_unlink_async(struct ehci_hcd *ehci); static void unlink_empty_async(struct ehci_hcd *ehci); -static void unlink_empty_async_suspended(struct ehci_hcd *ehci); static void ehci_work(struct ehci_hcd *ehci); static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh); static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh); @@ -565,6 +565,9 @@ static int ehci_init(struct usb_hcd *hcd) /* Accept arbitrarily long scatter-gather lists */ if (!(hcd->driver->flags & HCD_LOCAL_MEM)) hcd->self.sg_tablesize = ~0; + + /* Prepare for unlinking active QHs */ + ehci->old_current = ~0; return 0; } @@ -675,8 +678,10 @@ int ehci_setup(struct usb_hcd *hcd) return retval; retval = ehci_halt(ehci); - if (retval) + if (retval) { + ehci_mem_cleanup(ehci); return retval; + } ehci_reset(ehci); @@ -756,7 +761,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) ehci_dbg(ehci, "IAA with IAAD still set?\n"); if (ehci->iaa_in_progress) COUNT(ehci->stats.iaa); - end_unlink_async(ehci); + end_iaa_cycle(ehci); } /* remote wakeup [4.3.1] */ @@ -909,7 +914,7 @@ static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) */ } else { qh = (struct ehci_qh *) urb->hcpriv; - qh->exception = 1; + qh->unlink_reason |= QH_UNLINK_REQUESTED; switch (qh->qh_state) { case QH_STATE_LINKED: if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) @@ -970,10 +975,13 @@ rescan: goto done; } - qh->exception = 1; + qh->unlink_reason |= QH_UNLINK_REQUESTED; switch (qh->qh_state) { case QH_STATE_LINKED: - WARN_ON(!list_empty(&qh->qtd_list)); + if (list_empty(&qh->qtd_list)) + qh->unlink_reason |= QH_UNLINK_QUEUE_EMPTY; + else + WARN_ON(1); if (usb_endpoint_type(&ep->desc) != USB_ENDPOINT_XFER_INT) start_unlink_async(ehci, qh); else @@ -1040,7 +1048,7 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) * re-linking will call qh_refresh(). */ usb_settoggle(qh->ps.udev, epnum, is_out, 0); - qh->exception = 1; + qh->unlink_reason |= QH_UNLINK_REQUESTED; if (eptype == USB_ENDPOINT_XFER_BULK) start_unlink_async(ehci, qh); else diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 086a711..ffc9029 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -33,6 +33,8 @@ #ifdef CONFIG_PM +static void unlink_empty_async_suspended(struct ehci_hcd *ehci); + static int persist_enabled_on_companion(struct usb_device *udev, void *unused) { return !udev->maxchild && udev->persist_enabled && @@ -347,8 +349,10 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) goto done; ehci->rh_state = EHCI_RH_SUSPENDED; - end_unlink_async(ehci); unlink_empty_async_suspended(ehci); + + /* Any IAA cycle that started before the suspend is now invalid */ + end_iaa_cycle(ehci); ehci_handle_start_intr_unlinks(ehci); ehci_handle_intr_unlinks(ehci); end_free_itds(ehci); diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c index c23e285..3e226ef 100644 --- a/drivers/usb/host/ehci-msm.c +++ b/drivers/usb/host/ehci-msm.c @@ -33,6 +33,7 @@ #include <linux/usb/msm_hsusb_hw.h> #include <linux/usb.h> #include <linux/usb/hcd.h> +#include <linux/acpi.h> #include "ehci.h" @@ -55,12 +56,16 @@ static int ehci_msm_reset(struct usb_hcd *hcd) if (retval) return retval; + /* select ULPI phy and clear other status/control bits in PORTSC */ + writel(PORTSC_PTS_ULPI, USB_PORTSC); /* bursts of unspecified length. */ writel(0, USB_AHBBURST); /* Use the AHB transactor, allow posted data writes */ writel(0x8, USB_AHBMODE); /* Disable streaming mode and select host mode */ writel(0x13, USB_USBMODE); + /* Disable ULPI_TX_PKT_EN_CLR_FIX which is valid only for HSIC */ + writel(readl(USB_GENCONFIG_2) & ~ULPI_TX_PKT_EN_CLR_FIX, USB_GENCONFIG_2); return 0; } @@ -104,9 +109,9 @@ static int ehci_msm_probe(struct platform_device *pdev) } /* - * OTG driver takes care of PHY initialization, clock management, - * powering up VBUS, mapping of registers address space and power - * management. + * If there is an OTG driver, let it take care of PHY initialization, + * clock management, powering up VBUS, mapping of registers address + * space and power management. */ if (pdev->dev.of_node) phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0); @@ -114,27 +119,35 @@ static int ehci_msm_probe(struct platform_device *pdev) phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); if (IS_ERR(phy)) { - dev_err(&pdev->dev, "unable to find transceiver\n"); - ret = -EPROBE_DEFER; - goto put_hcd; - } - - ret = otg_set_host(phy->otg, &hcd->self); - if (ret < 0) { - dev_err(&pdev->dev, "unable to register with transceiver\n"); - goto put_hcd; + if (PTR_ERR(phy) == -EPROBE_DEFER) { + dev_err(&pdev->dev, "unable to find transceiver\n"); + ret = -EPROBE_DEFER; + goto put_hcd; + } + phy = NULL; } hcd->usb_phy = phy; device_init_wakeup(&pdev->dev, 1); - /* - * OTG device parent of HCD takes care of putting - * hardware into low power mode. - */ - pm_runtime_no_callbacks(&pdev->dev); - pm_runtime_enable(&pdev->dev); - /* FIXME: need to call usb_add_hcd() here? */ + if (phy && phy->otg) { + /* + * MSM OTG driver takes care of adding the HCD and + * placing hardware into low power mode via runtime PM. + */ + ret = otg_set_host(phy->otg, &hcd->self); + if (ret < 0) { + dev_err(&pdev->dev, "unable to register with transceiver\n"); + goto put_hcd; + } + + pm_runtime_no_callbacks(&pdev->dev); + pm_runtime_enable(&pdev->dev); + } else { + ret = usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); + if (ret) + goto put_hcd; + } return 0; @@ -152,9 +165,10 @@ static int ehci_msm_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); - otg_set_host(hcd->usb_phy->otg, NULL); - - /* FIXME: need to call usb_remove_hcd() here? */ + if (hcd->usb_phy && hcd->usb_phy->otg) + otg_set_host(hcd->usb_phy->otg, NULL); + else + usb_remove_hcd(hcd); usb_put_hcd(hcd); @@ -191,6 +205,12 @@ static const struct dev_pm_ops ehci_msm_dev_pm_ops = { .resume = ehci_msm_pm_resume, }; +static const struct acpi_device_id msm_ehci_acpi_ids[] = { + { "QCOM8040", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, msm_ehci_acpi_ids); + static const struct of_device_id msm_ehci_dt_match[] = { { .compatible = "qcom,ehci-host", }, {} @@ -200,10 +220,12 @@ MODULE_DEVICE_TABLE(of, msm_ehci_dt_match); static struct platform_driver ehci_msm_driver = { .probe = ehci_msm_probe, .remove = ehci_msm_remove, + .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "msm_hsusb_host", .pm = &ehci_msm_dev_pm_ops, .of_match_table = msm_ehci_dt_match, + .acpi_match_table = ACPI_PTR(msm_ehci_acpi_ids), }, }; diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 2a5d2fd..3b3649d 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -377,6 +377,12 @@ static int ehci_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) return usb_hcd_pci_probe(pdev, id); } +static void ehci_pci_remove(struct pci_dev *pdev) +{ + pci_clear_mwi(pdev); + usb_hcd_pci_remove(pdev); +} + /* PCI driver selection metadata; PCI hotplugging uses this */ static const struct pci_device_id pci_ids [] = { { /* handle any USB 2.0 EHCI controller */ @@ -396,7 +402,7 @@ static struct pci_driver ehci_pci_driver = { .id_table = pci_ids, .probe = ehci_pci_probe, - .remove = usb_hcd_pci_remove, + .remove = ehci_pci_remove, .shutdown = usb_hcd_pci_shutdown, #ifdef CONFIG_PM diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index bd7082f2..1757ebb 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -345,8 +345,7 @@ static int ehci_platform_suspend(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); struct usb_ehci_pdata *pdata = dev_get_platdata(dev); - struct platform_device *pdev = - container_of(dev, struct platform_device, dev); + struct platform_device *pdev = to_platform_device(dev); bool do_wakeup = device_may_wakeup(dev); int ret; @@ -364,8 +363,7 @@ static int ehci_platform_resume(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); struct usb_ehci_pdata *pdata = dev_get_platdata(dev); - struct platform_device *pdev = - container_of(dev, struct platform_device, dev); + struct platform_device *pdev = to_platform_device(dev); struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); if (pdata->power_on) { diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index aad0777..eca3710 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -394,6 +394,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) goto retry_xacterr; } stopped = 1; + qh->unlink_reason |= QH_UNLINK_HALTED; /* magic dummy for some short reads; qh won't advance. * that silicon quirk can kick in with this dummy too. @@ -408,6 +409,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) && !(qtd->hw_alt_next & EHCI_LIST_END(ehci))) { stopped = 1; + qh->unlink_reason |= QH_UNLINK_SHORT_READ; } /* stop scanning when we reach qtds the hc is using */ @@ -420,8 +422,10 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) stopped = 1; /* cancel everything if we halt, suspend, etc */ - if (ehci->rh_state < EHCI_RH_RUNNING) + if (ehci->rh_state < EHCI_RH_RUNNING) { last_status = -ESHUTDOWN; + qh->unlink_reason |= QH_UNLINK_SHUTDOWN; + } /* this qtd is active; skip it unless a previous qtd * for its urb faulted, or its urb was canceled. @@ -538,10 +542,10 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) * except maybe high bandwidth ... */ if (stopped != 0 || hw->hw_qtd_next == EHCI_LIST_END(ehci)) - qh->exception = 1; + qh->unlink_reason |= QH_UNLINK_DUMMY_OVERLAY; /* Let the caller know if the QH needs to be unlinked. */ - return qh->exception; + return qh->unlink_reason; } /*-------------------------------------------------------------------------*/ @@ -1003,7 +1007,7 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) qh->qh_state = QH_STATE_LINKED; qh->xacterrs = 0; - qh->exception = 0; + qh->unlink_reason = 0; /* qtd completions reported later by interrupt */ enable_async(ehci); @@ -1279,17 +1283,13 @@ static void single_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh) static void start_iaa_cycle(struct ehci_hcd *ehci) { - /* Do nothing if an IAA cycle is already running */ - if (ehci->iaa_in_progress) - return; - ehci->iaa_in_progress = true; - /* If the controller isn't running, we don't have to wait for it */ if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) { end_unlink_async(ehci); - /* Otherwise start a new IAA cycle */ - } else if (likely(ehci->rh_state == EHCI_RH_RUNNING)) { + /* Otherwise start a new IAA cycle if one isn't already running */ + } else if (ehci->rh_state == EHCI_RH_RUNNING && + !ehci->iaa_in_progress) { /* Make sure the unlinks are all visible to the hardware */ wmb(); @@ -1297,17 +1297,13 @@ static void start_iaa_cycle(struct ehci_hcd *ehci) ehci_writel(ehci, ehci->command | CMD_IAAD, &ehci->regs->command); ehci_readl(ehci, &ehci->regs->command); + ehci->iaa_in_progress = true; ehci_enable_event(ehci, EHCI_HRTIMER_IAA_WATCHDOG, true); } } -/* the async qh for the qtds being unlinked are now gone from the HC */ - -static void end_unlink_async(struct ehci_hcd *ehci) +static void end_iaa_cycle(struct ehci_hcd *ehci) { - struct ehci_qh *qh; - bool early_exit; - if (ehci->has_synopsys_hc_bug) ehci_writel(ehci, (u32) ehci->async->qh_dma, &ehci->regs->async_next); @@ -1315,6 +1311,16 @@ static void end_unlink_async(struct ehci_hcd *ehci) /* The current IAA cycle has ended */ ehci->iaa_in_progress = false; + end_unlink_async(ehci); +} + +/* See if the async qh for the qtds being unlinked are now gone from the HC */ + +static void end_unlink_async(struct ehci_hcd *ehci) +{ + struct ehci_qh *qh; + bool early_exit; + if (list_empty(&ehci->async_unlink)) return; qh = list_first_entry(&ehci->async_unlink, struct ehci_qh, @@ -1335,14 +1341,60 @@ static void end_unlink_async(struct ehci_hcd *ehci) * after the IAA interrupt occurs. In self-defense, always go * through two IAA cycles for each QH. */ - else if (qh->qh_state == QH_STATE_UNLINK_WAIT) { + else if (qh->qh_state == QH_STATE_UNLINK) { + /* + * Second IAA cycle has finished. Process only the first + * waiting QH (NVIDIA (?) bug). + */ + list_move_tail(&qh->unlink_node, &ehci->async_idle); + } + + /* + * AMD/ATI (?) bug: The HC can continue to use an active QH long + * after the IAA interrupt occurs. To prevent problems, QHs that + * may still be active will wait until 2 ms have passed with no + * change to the hw_current and hw_token fields (this delay occurs + * between the two IAA cycles). + * + * The EHCI spec (4.8.2) says that active QHs must not be removed + * from the async schedule and recommends waiting until the QH + * goes inactive. This is ridiculous because the QH will _never_ + * become inactive if the endpoint NAKs indefinitely. + */ + + /* Some reasons for unlinking guarantee the QH can't be active */ + else if (qh->unlink_reason & (QH_UNLINK_HALTED | + QH_UNLINK_SHORT_READ | QH_UNLINK_DUMMY_OVERLAY)) + goto DelayDone; + + /* The QH can't be active if the queue was and still is empty... */ + else if ((qh->unlink_reason & QH_UNLINK_QUEUE_EMPTY) && + list_empty(&qh->qtd_list)) + goto DelayDone; + + /* ... or if the QH has halted */ + else if (qh->hw->hw_token & cpu_to_hc32(ehci, QTD_STS_HALT)) + goto DelayDone; + + /* Otherwise we have to wait until the QH stops changing */ + else { + __hc32 qh_current, qh_token; + + qh_current = qh->hw->hw_current; + qh_token = qh->hw->hw_token; + if (qh_current != ehci->old_current || + qh_token != ehci->old_token) { + ehci->old_current = qh_current; + ehci->old_token = qh_token; + ehci_enable_event(ehci, + EHCI_HRTIMER_ACTIVE_UNLINK, true); + return; + } + DelayDone: qh->qh_state = QH_STATE_UNLINK; early_exit = true; } - - /* Otherwise process only the first waiting QH (NVIDIA bug?) */ - else - list_move_tail(&qh->unlink_node, &ehci->async_idle); + ehci->old_current = ~0; /* Prepare for next QH */ /* Start a new IAA cycle if any QHs are waiting for it */ if (!list_empty(&ehci->async_unlink)) @@ -1395,6 +1447,7 @@ static void unlink_empty_async(struct ehci_hcd *ehci) /* If nothing else is being unlinked, unlink the last empty QH */ if (list_empty(&ehci->async_unlink) && qh_to_unlink) { + qh_to_unlink->unlink_reason |= QH_UNLINK_QUEUE_EMPTY; start_unlink_async(ehci, qh_to_unlink); --count; } @@ -1406,8 +1459,10 @@ static void unlink_empty_async(struct ehci_hcd *ehci) } } +#ifdef CONFIG_PM + /* The root hub is suspended; unlink all the async QHs */ -static void __maybe_unused unlink_empty_async_suspended(struct ehci_hcd *ehci) +static void unlink_empty_async_suspended(struct ehci_hcd *ehci) { struct ehci_qh *qh; @@ -1416,9 +1471,10 @@ static void __maybe_unused unlink_empty_async_suspended(struct ehci_hcd *ehci) WARN_ON(!list_empty(&qh->qtd_list)); single_unlink_async(ehci, qh); } - start_iaa_cycle(ehci); } +#endif + /* makes sure the async qh will become idle */ /* caller must own ehci->lock */ diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index f9a3327..1dfe54f 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -34,7 +34,7 @@ * pre-calculated schedule data to make appending to the queue be quick. */ -static int ehci_get_frame (struct usb_hcd *hcd); +static int ehci_get_frame(struct usb_hcd *hcd); /* * periodic_next_shadow - return "next" pointer on shadow list @@ -52,7 +52,7 @@ periodic_next_shadow(struct ehci_hcd *ehci, union ehci_shadow *periodic, return &periodic->fstn->fstn_next; case Q_TYPE_ITD: return &periodic->itd->itd_next; - // case Q_TYPE_SITD: + /* case Q_TYPE_SITD: */ default: return &periodic->sitd->sitd_next; } @@ -73,7 +73,7 @@ shadow_next_periodic(struct ehci_hcd *ehci, union ehci_shadow *periodic, } /* caller must hold ehci->lock */ -static void periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr) +static void periodic_unlink(struct ehci_hcd *ehci, unsigned frame, void *ptr) { union ehci_shadow *prev_p = &ehci->pshadow[frame]; __hc32 *hw_p = &ehci->periodic[frame]; @@ -296,10 +296,9 @@ static void compute_tt_budget(u8 budget_table[EHCI_BANDWIDTH_SIZE], if (x <= 125) { budget_line[uf] = x; break; - } else { - budget_line[uf] = 125; - x -= 125; } + budget_line[uf] = 125; + x -= 125; } } } @@ -330,7 +329,8 @@ static int __maybe_unused same_tt(struct usb_device *dev1, */ static inline unsigned char tt_start_uframe(struct ehci_hcd *ehci, __hc32 mask) { - unsigned char smask = QH_SMASK & hc32_to_cpu(ehci, mask); + unsigned char smask = hc32_to_cpu(ehci, mask) & QH_SMASK; + if (!smask) { ehci_err(ehci, "invalid empty smask!\n"); /* uframe 7 can't have bw so this will indicate failure */ @@ -346,7 +346,8 @@ max_tt_usecs[] = { 125, 125, 125, 125, 125, 125, 30, 0 }; static inline void carryover_tt_bandwidth(unsigned short tt_usecs[8]) { int i; - for (i=0; i<7; i++) { + + for (i = 0; i < 7; i++) { if (max_tt_usecs[i] < tt_usecs[i]) { tt_usecs[i+1] += tt_usecs[i] - max_tt_usecs[i]; tt_usecs[i] = max_tt_usecs[i]; @@ -375,7 +376,7 @@ static inline void carryover_tt_bandwidth(unsigned short tt_usecs[8]) * limit of 16, specified in USB 2.0 spec section 11.18.4 requirement #4, * since proper scheduling limits ssplits to less than 16 per uframe. */ -static int tt_available ( +static int tt_available( struct ehci_hcd *ehci, struct ehci_per_sched *ps, struct ehci_tt *tt, @@ -409,11 +410,11 @@ static int tt_available ( * must be empty, so as to not illegally delay * already scheduled transactions */ - if (125 < usecs) { + if (usecs > 125) { int ufs = (usecs / 125); for (i = uframe; i < (uframe + ufs) && i < 8; i++) - if (0 < tt_usecs[i]) + if (tt_usecs[i] > 0) return 0; } @@ -435,7 +436,7 @@ static int tt_available ( * for a periodic transfer starting at the specified frame, using * all the uframes in the mask. */ -static int tt_no_collision ( +static int tt_no_collision( struct ehci_hcd *ehci, unsigned period, struct usb_device *dev, @@ -455,8 +456,8 @@ static int tt_no_collision ( __hc32 type; struct ehci_qh_hw *hw; - here = ehci->pshadow [frame]; - type = Q_NEXT_TYPE(ehci, ehci->periodic [frame]); + here = ehci->pshadow[frame]; + type = Q_NEXT_TYPE(ehci, ehci->periodic[frame]); while (here.ptr) { switch (hc32_to_cpu(ehci, type)) { case Q_TYPE_ITD: @@ -479,7 +480,7 @@ static int tt_no_collision ( here = here.qh->qh_next; continue; case Q_TYPE_SITD: - if (same_tt (dev, here.sitd->urb->dev)) { + if (same_tt(dev, here.sitd->urb->dev)) { u16 mask; mask = hc32_to_cpu(ehci, here.sitd @@ -492,9 +493,9 @@ static int tt_no_collision ( type = Q_NEXT_TYPE(ehci, here.sitd->hw_next); here = here.sitd->sitd_next; continue; - // case Q_TYPE_FSTN: + /* case Q_TYPE_FSTN: */ default: - ehci_dbg (ehci, + ehci_dbg(ehci, "periodic frame %d bogus type %d\n", frame, type); } @@ -588,14 +589,14 @@ static void qh_link_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) qh->qh_next = here; if (here.qh) qh->hw->hw_next = *hw_p; - wmb (); + wmb(); prev->qh = qh; - *hw_p = QH_NEXT (ehci, qh->qh_dma); + *hw_p = QH_NEXT(ehci, qh->qh_dma); } } qh->qh_state = QH_STATE_LINKED; qh->xacterrs = 0; - qh->exception = 0; + qh->unlink_reason = 0; /* update per-qh bandwidth for debugfs */ ehci_to_hcd(ehci)->self.bandwidth_allocated += qh->ps.bw_period @@ -633,7 +634,7 @@ static void qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) period = qh->ps.period ? : 1; for (i = qh->ps.phase; i < ehci->periodic_size; i += period) - periodic_unlink (ehci, i, qh); + periodic_unlink(ehci, i, qh); /* update per-qh bandwidth for debugfs */ ehci_to_hcd(ehci)->self.bandwidth_allocated -= qh->ps.bw_period @@ -679,7 +680,7 @@ static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh) /* if the qh is waiting for unlink, cancel it now */ cancel_unlink_wait_intr(ehci, qh); - qh_unlink_periodic (ehci, qh); + qh_unlink_periodic(ehci, qh); /* Make sure the unlinks are visible before starting the timer */ wmb(); @@ -763,7 +764,7 @@ static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh) /*-------------------------------------------------------------------------*/ -static int check_period ( +static int check_period( struct ehci_hcd *ehci, unsigned frame, unsigned uframe, @@ -785,11 +786,11 @@ static int check_period ( return 0; } - // success! + /* success! */ return 1; } -static int check_intr_schedule ( +static int check_intr_schedule( struct ehci_hcd *ehci, unsigned frame, unsigned uframe, @@ -925,7 +926,7 @@ done: return status; } -static int intr_submit ( +static int intr_submit( struct ehci_hcd *ehci, struct urb *urb, struct list_head *qtd_list, @@ -940,7 +941,7 @@ static int intr_submit ( /* get endpoint and transfer/schedule data */ epnum = urb->ep->desc.bEndpointAddress; - spin_lock_irqsave (&ehci->lock, flags); + spin_lock_irqsave(&ehci->lock, flags); if (unlikely(!HCD_HW_ACCESSIBLE(ehci_to_hcd(ehci)))) { status = -ESHUTDOWN; @@ -951,20 +952,21 @@ static int intr_submit ( goto done_not_linked; /* get qh and force any scheduling errors */ - INIT_LIST_HEAD (&empty); + INIT_LIST_HEAD(&empty); qh = qh_append_tds(ehci, urb, &empty, epnum, &urb->ep->hcpriv); if (qh == NULL) { status = -ENOMEM; goto done; } if (qh->qh_state == QH_STATE_IDLE) { - if ((status = qh_schedule (ehci, qh)) != 0) + status = qh_schedule(ehci, qh); + if (status) goto done; } /* then queue the urb's tds to the qh */ qh = qh_append_tds(ehci, urb, qtd_list, epnum, &urb->ep->hcpriv); - BUG_ON (qh == NULL); + BUG_ON(qh == NULL); /* stuff into the periodic schedule */ if (qh->qh_state == QH_STATE_IDLE) { @@ -982,9 +984,9 @@ done: if (unlikely(status)) usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb); done_not_linked: - spin_unlock_irqrestore (&ehci->lock, flags); + spin_unlock_irqrestore(&ehci->lock, flags); if (status) - qtd_list_free (ehci, urb, qtd_list); + qtd_list_free(ehci, urb, qtd_list); return status; } @@ -1022,12 +1024,12 @@ static void scan_intr(struct ehci_hcd *ehci) /* ehci_iso_stream ops work with both ITD and SITD */ static struct ehci_iso_stream * -iso_stream_alloc (gfp_t mem_flags) +iso_stream_alloc(gfp_t mem_flags) { struct ehci_iso_stream *stream; - stream = kzalloc(sizeof *stream, mem_flags); - if (likely (stream != NULL)) { + stream = kzalloc(sizeof(*stream), mem_flags); + if (likely(stream != NULL)) { INIT_LIST_HEAD(&stream->td_list); INIT_LIST_HEAD(&stream->free_list); stream->next_uframe = NO_FRAME; @@ -1037,13 +1039,13 @@ iso_stream_alloc (gfp_t mem_flags) } static void -iso_stream_init ( +iso_stream_init( struct ehci_hcd *ehci, struct ehci_iso_stream *stream, struct urb *urb ) { - static const u8 smask_out [] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f }; + static const u8 smask_out[] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f }; struct usb_device *dev = urb->dev; u32 buf1; @@ -1058,11 +1060,7 @@ iso_stream_init ( epnum = usb_pipeendpoint(urb->pipe); is_input = usb_pipein(urb->pipe) ? USB_DIR_IN : 0; maxp = usb_endpoint_maxp(&urb->ep->desc); - if (is_input) { - buf1 = (1 << 11); - } else { - buf1 = 0; - } + buf1 = is_input ? 1 << 11 : 0; /* knows about ITD vs SITD */ if (dev->speed == USB_SPEED_HIGH) { @@ -1111,7 +1109,7 @@ iso_stream_init ( think_time = dev->tt ? dev->tt->think_time : 0; stream->ps.tt_usecs = NS_TO_US(think_time + usb_calc_bus_time( dev->speed, is_input, 1, maxp)); - hs_transfers = max (1u, (maxp + 187) / 188); + hs_transfers = max(1u, (maxp + 187) / 188); if (is_input) { u32 tmp; @@ -1151,7 +1149,7 @@ iso_stream_init ( } static struct ehci_iso_stream * -iso_stream_find (struct ehci_hcd *ehci, struct urb *urb) +iso_stream_find(struct ehci_hcd *ehci, struct urb *urb) { unsigned epnum; struct ehci_iso_stream *stream; @@ -1164,25 +1162,25 @@ iso_stream_find (struct ehci_hcd *ehci, struct urb *urb) else ep = urb->dev->ep_out[epnum]; - spin_lock_irqsave (&ehci->lock, flags); + spin_lock_irqsave(&ehci->lock, flags); stream = ep->hcpriv; - if (unlikely (stream == NULL)) { + if (unlikely(stream == NULL)) { stream = iso_stream_alloc(GFP_ATOMIC); - if (likely (stream != NULL)) { + if (likely(stream != NULL)) { ep->hcpriv = stream; iso_stream_init(ehci, stream, urb); } /* if dev->ep [epnum] is a QH, hw is set */ - } else if (unlikely (stream->hw != NULL)) { - ehci_dbg (ehci, "dev %s ep%d%s, not iso??\n", + } else if (unlikely(stream->hw != NULL)) { + ehci_dbg(ehci, "dev %s ep%d%s, not iso??\n", urb->dev->devpath, epnum, usb_pipein(urb->pipe) ? "in" : "out"); stream = NULL; } - spin_unlock_irqrestore (&ehci->lock, flags); + spin_unlock_irqrestore(&ehci->lock, flags); return stream; } @@ -1191,16 +1189,16 @@ iso_stream_find (struct ehci_hcd *ehci, struct urb *urb) /* ehci_iso_sched ops can be ITD-only or SITD-only */ static struct ehci_iso_sched * -iso_sched_alloc (unsigned packets, gfp_t mem_flags) +iso_sched_alloc(unsigned packets, gfp_t mem_flags) { struct ehci_iso_sched *iso_sched; - int size = sizeof *iso_sched; + int size = sizeof(*iso_sched); - size += packets * sizeof (struct ehci_iso_packet); + size += packets * sizeof(struct ehci_iso_packet); iso_sched = kzalloc(size, mem_flags); - if (likely (iso_sched != NULL)) { - INIT_LIST_HEAD (&iso_sched->td_list); - } + if (likely(iso_sched != NULL)) + INIT_LIST_HEAD(&iso_sched->td_list); + return iso_sched; } @@ -1222,17 +1220,17 @@ itd_sched_init( * when we fit new itds into the schedule. */ for (i = 0; i < urb->number_of_packets; i++) { - struct ehci_iso_packet *uframe = &iso_sched->packet [i]; + struct ehci_iso_packet *uframe = &iso_sched->packet[i]; unsigned length; dma_addr_t buf; u32 trans; - length = urb->iso_frame_desc [i].length; - buf = dma + urb->iso_frame_desc [i].offset; + length = urb->iso_frame_desc[i].length; + buf = dma + urb->iso_frame_desc[i].offset; trans = EHCI_ISOC_ACTIVE; trans |= buf & 0x0fff; - if (unlikely (((i + 1) == urb->number_of_packets)) + if (unlikely(((i + 1) == urb->number_of_packets)) && !(urb->transfer_flags & URB_NO_INTERRUPT)) trans |= EHCI_ITD_IOC; trans |= length << 16; @@ -1241,26 +1239,26 @@ itd_sched_init( /* might need to cross a buffer page within a uframe */ uframe->bufp = (buf & ~(u64)0x0fff); buf += length; - if (unlikely ((uframe->bufp != (buf & ~(u64)0x0fff)))) + if (unlikely((uframe->bufp != (buf & ~(u64)0x0fff)))) uframe->cross = 1; } } static void -iso_sched_free ( +iso_sched_free( struct ehci_iso_stream *stream, struct ehci_iso_sched *iso_sched ) { if (!iso_sched) return; - // caller must hold ehci->lock! - list_splice (&iso_sched->td_list, &stream->free_list); - kfree (iso_sched); + /* caller must hold ehci->lock! */ + list_splice(&iso_sched->td_list, &stream->free_list); + kfree(iso_sched); } static int -itd_urb_transaction ( +itd_urb_transaction( struct ehci_iso_stream *stream, struct ehci_hcd *ehci, struct urb *urb, @@ -1274,8 +1272,8 @@ itd_urb_transaction ( struct ehci_iso_sched *sched; unsigned long flags; - sched = iso_sched_alloc (urb->number_of_packets, mem_flags); - if (unlikely (sched == NULL)) + sched = iso_sched_alloc(urb->number_of_packets, mem_flags); + if (unlikely(sched == NULL)) return -ENOMEM; itd_sched_init(ehci, sched, stream, urb); @@ -1286,7 +1284,7 @@ itd_urb_transaction ( num_itds = urb->number_of_packets; /* allocate/init ITDs */ - spin_lock_irqsave (&ehci->lock, flags); + spin_lock_irqsave(&ehci->lock, flags); for (i = 0; i < num_itds; i++) { /* @@ -1298,14 +1296,14 @@ itd_urb_transaction ( struct ehci_itd, itd_list); if (itd->frame == ehci->now_frame) goto alloc_itd; - list_del (&itd->itd_list); + list_del(&itd->itd_list); itd_dma = itd->itd_dma; } else { alloc_itd: - spin_unlock_irqrestore (&ehci->lock, flags); - itd = dma_pool_alloc (ehci->itd_pool, mem_flags, + spin_unlock_irqrestore(&ehci->lock, flags); + itd = dma_pool_alloc(ehci->itd_pool, mem_flags, &itd_dma); - spin_lock_irqsave (&ehci->lock, flags); + spin_lock_irqsave(&ehci->lock, flags); if (!itd) { iso_sched_free(stream, sched); spin_unlock_irqrestore(&ehci->lock, flags); @@ -1313,12 +1311,12 @@ itd_urb_transaction ( } } - memset (itd, 0, sizeof *itd); + memset(itd, 0, sizeof(*itd)); itd->itd_dma = itd_dma; itd->frame = NO_FRAME; - list_add (&itd->itd_list, &sched->td_list); + list_add(&itd->itd_list, &sched->td_list); } - spin_unlock_irqrestore (&ehci->lock, flags); + spin_unlock_irqrestore(&ehci->lock, flags); /* temporarily store schedule info in hcpriv */ urb->hcpriv = sched; @@ -1385,7 +1383,7 @@ static void reserve_release_iso_bandwidth(struct ehci_hcd *ehci, } static inline int -itd_slot_ok ( +itd_slot_ok( struct ehci_hcd *ehci, struct ehci_iso_stream *stream, unsigned uframe @@ -1405,7 +1403,7 @@ itd_slot_ok ( } static inline int -sitd_slot_ok ( +sitd_slot_ok( struct ehci_hcd *ehci, struct ehci_iso_stream *stream, unsigned uframe, @@ -1492,7 +1490,7 @@ sitd_slot_ok ( */ static int -iso_stream_schedule ( +iso_stream_schedule( struct ehci_hcd *ehci, struct urb *urb, struct ehci_iso_stream *stream @@ -1693,9 +1691,9 @@ itd_init(struct ehci_hcd *ehci, struct ehci_iso_stream *stream, /* it's been recently zeroed */ itd->hw_next = EHCI_LIST_END(ehci); - itd->hw_bufp [0] = stream->buf0; - itd->hw_bufp [1] = stream->buf1; - itd->hw_bufp [2] = stream->buf2; + itd->hw_bufp[0] = stream->buf0; + itd->hw_bufp[1] = stream->buf1; + itd->hw_bufp[2] = stream->buf2; for (i = 0; i < 8; i++) itd->index[i] = -1; @@ -1712,13 +1710,13 @@ itd_patch( u16 uframe ) { - struct ehci_iso_packet *uf = &iso_sched->packet [index]; + struct ehci_iso_packet *uf = &iso_sched->packet[index]; unsigned pg = itd->pg; - // BUG_ON (pg == 6 && uf->cross); + /* BUG_ON(pg == 6 && uf->cross); */ uframe &= 0x07; - itd->index [uframe] = index; + itd->index[uframe] = index; itd->hw_transaction[uframe] = uf->transaction; itd->hw_transaction[uframe] |= cpu_to_hc32(ehci, pg << 12); @@ -1726,7 +1724,7 @@ itd_patch( itd->hw_bufp_hi[pg] |= cpu_to_hc32(ehci, (u32)(uf->bufp >> 32)); /* iso_frame_desc[].offset must be strictly increasing */ - if (unlikely (uf->cross)) { + if (unlikely(uf->cross)) { u64 bufp = uf->bufp + 4096; itd->pg = ++pg; @@ -1736,7 +1734,7 @@ itd_patch( } static inline void -itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd) +itd_link(struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd) { union ehci_shadow *prev = &ehci->pshadow[frame]; __hc32 *hw_p = &ehci->periodic[frame]; @@ -1757,7 +1755,7 @@ itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd) itd->hw_next = *hw_p; prev->itd = itd; itd->frame = frame; - wmb (); + wmb(); *hw_p = cpu_to_hc32(ehci, itd->itd_dma | Q_TYPE_ITD); } @@ -1776,7 +1774,7 @@ static void itd_link_urb( next_uframe = stream->next_uframe & (mod - 1); - if (unlikely (list_empty(&stream->td_list))) + if (unlikely(list_empty(&stream->td_list))) ehci_to_hcd(ehci)->self.bandwidth_allocated += stream->bandwidth; @@ -1792,16 +1790,16 @@ static void itd_link_urb( packet < urb->number_of_packets;) { if (itd == NULL) { /* ASSERT: we have all necessary itds */ - // BUG_ON (list_empty (&iso_sched->td_list)); + /* BUG_ON(list_empty(&iso_sched->td_list)); */ /* ASSERT: no itds for this endpoint in this uframe */ - itd = list_entry (iso_sched->td_list.next, + itd = list_entry(iso_sched->td_list.next, struct ehci_itd, itd_list); - list_move_tail (&itd->itd_list, &stream->td_list); + list_move_tail(&itd->itd_list, &stream->td_list); itd->stream = stream; itd->urb = urb; - itd_init (ehci, stream, itd); + itd_init(ehci, stream, itd); } uframe = next_uframe & 0x07; @@ -1823,7 +1821,7 @@ static void itd_link_urb( stream->next_uframe = next_uframe; /* don't need that schedule data any more */ - iso_sched_free (stream, iso_sched); + iso_sched_free(stream, iso_sched); urb->hcpriv = stream; ++ehci->isoc_count; @@ -1855,19 +1853,19 @@ static bool itd_complete(struct ehci_hcd *ehci, struct ehci_itd *itd) /* for each uframe with a packet */ for (uframe = 0; uframe < 8; uframe++) { - if (likely (itd->index[uframe] == -1)) + if (likely(itd->index[uframe] == -1)) continue; urb_index = itd->index[uframe]; - desc = &urb->iso_frame_desc [urb_index]; + desc = &urb->iso_frame_desc[urb_index]; - t = hc32_to_cpup(ehci, &itd->hw_transaction [uframe]); - itd->hw_transaction [uframe] = 0; + t = hc32_to_cpup(ehci, &itd->hw_transaction[uframe]); + itd->hw_transaction[uframe] = 0; /* report transfer status */ - if (unlikely (t & ISO_ERRS)) { + if (unlikely(t & ISO_ERRS)) { urb->error_count++; if (t & EHCI_ISOC_BUF_ERR) - desc->status = usb_pipein (urb->pipe) + desc->status = usb_pipein(urb->pipe) ? -ENOSR /* hc couldn't read */ : -ECOMM; /* hc couldn't write */ else if (t & EHCI_ISOC_BABBLE) @@ -1880,7 +1878,7 @@ static bool itd_complete(struct ehci_hcd *ehci, struct ehci_itd *itd) desc->actual_length = EHCI_ITD_LENGTH(t); urb->actual_length += desc->actual_length; } - } else if (likely ((t & EHCI_ISOC_ACTIVE) == 0)) { + } else if (likely((t & EHCI_ISOC_ACTIVE) == 0)) { desc->status = 0; desc->actual_length = EHCI_ITD_LENGTH(t); urb->actual_length += desc->actual_length; @@ -1891,12 +1889,13 @@ static bool itd_complete(struct ehci_hcd *ehci, struct ehci_itd *itd) } /* handle completion now? */ - if (likely ((urb_index + 1) != urb->number_of_packets)) + if (likely((urb_index + 1) != urb->number_of_packets)) goto done; - /* ASSERT: it's really the last itd for this urb - list_for_each_entry (itd, &stream->td_list, itd_list) - BUG_ON (itd->urb == urb); + /* + * ASSERT: it's really the last itd for this urb + * list_for_each_entry (itd, &stream->td_list, itd_list) + * BUG_ON(itd->urb == urb); */ /* give urb back to the driver; completion often (re)submits */ @@ -1936,7 +1935,7 @@ done: /*-------------------------------------------------------------------------*/ -static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, +static int itd_submit(struct ehci_hcd *ehci, struct urb *urb, gfp_t mem_flags) { int status = -EINVAL; @@ -1944,37 +1943,37 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, struct ehci_iso_stream *stream; /* Get iso_stream head */ - stream = iso_stream_find (ehci, urb); - if (unlikely (stream == NULL)) { - ehci_dbg (ehci, "can't get iso stream\n"); + stream = iso_stream_find(ehci, urb); + if (unlikely(stream == NULL)) { + ehci_dbg(ehci, "can't get iso stream\n"); return -ENOMEM; } if (unlikely(urb->interval != stream->uperiod)) { - ehci_dbg (ehci, "can't change iso interval %d --> %d\n", + ehci_dbg(ehci, "can't change iso interval %d --> %d\n", stream->uperiod, urb->interval); goto done; } #ifdef EHCI_URB_TRACE - ehci_dbg (ehci, + ehci_dbg(ehci, "%s %s urb %p ep%d%s len %d, %d pkts %d uframes [%p]\n", __func__, urb->dev->devpath, urb, - usb_pipeendpoint (urb->pipe), - usb_pipein (urb->pipe) ? "in" : "out", + usb_pipeendpoint(urb->pipe), + usb_pipein(urb->pipe) ? "in" : "out", urb->transfer_buffer_length, urb->number_of_packets, urb->interval, stream); #endif /* allocate ITDs w/o locking anything */ - status = itd_urb_transaction (stream, ehci, urb, mem_flags); - if (unlikely (status < 0)) { - ehci_dbg (ehci, "can't init itds\n"); + status = itd_urb_transaction(stream, ehci, urb, mem_flags); + if (unlikely(status < 0)) { + ehci_dbg(ehci, "can't init itds\n"); goto done; } /* schedule ... need to lock */ - spin_lock_irqsave (&ehci->lock, flags); + spin_lock_irqsave(&ehci->lock, flags); if (unlikely(!HCD_HW_ACCESSIBLE(ehci_to_hcd(ehci)))) { status = -ESHUTDOWN; goto done_not_linked; @@ -1984,7 +1983,7 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, goto done_not_linked; status = iso_stream_schedule(ehci, urb, stream); if (likely(status == 0)) { - itd_link_urb (ehci, urb, ehci->periodic_size << 3, stream); + itd_link_urb(ehci, urb, ehci->periodic_size << 3, stream); } else if (status > 0) { status = 0; ehci_urb_done(ehci, urb, 0); @@ -1992,7 +1991,7 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb); } done_not_linked: - spin_unlock_irqrestore (&ehci->lock, flags); + spin_unlock_irqrestore(&ehci->lock, flags); done: return status; } @@ -2022,13 +2021,13 @@ sitd_sched_init( * when we fit new sitds into the schedule. */ for (i = 0; i < urb->number_of_packets; i++) { - struct ehci_iso_packet *packet = &iso_sched->packet [i]; + struct ehci_iso_packet *packet = &iso_sched->packet[i]; unsigned length; dma_addr_t buf; u32 trans; - length = urb->iso_frame_desc [i].length & 0x03ff; - buf = dma + urb->iso_frame_desc [i].offset; + length = urb->iso_frame_desc[i].length & 0x03ff; + buf = dma + urb->iso_frame_desc[i].offset; trans = SITD_STS_ACTIVE; if (((i + 1) == urb->number_of_packets) @@ -2054,7 +2053,7 @@ sitd_sched_init( } static int -sitd_urb_transaction ( +sitd_urb_transaction( struct ehci_iso_stream *stream, struct ehci_hcd *ehci, struct urb *urb, @@ -2067,14 +2066,14 @@ sitd_urb_transaction ( struct ehci_iso_sched *iso_sched; unsigned long flags; - iso_sched = iso_sched_alloc (urb->number_of_packets, mem_flags); + iso_sched = iso_sched_alloc(urb->number_of_packets, mem_flags); if (iso_sched == NULL) return -ENOMEM; sitd_sched_init(ehci, iso_sched, stream, urb); /* allocate/init sITDs */ - spin_lock_irqsave (&ehci->lock, flags); + spin_lock_irqsave(&ehci->lock, flags); for (i = 0; i < urb->number_of_packets; i++) { /* NOTE: for now, we don't try to handle wraparound cases @@ -2091,14 +2090,14 @@ sitd_urb_transaction ( struct ehci_sitd, sitd_list); if (sitd->frame == ehci->now_frame) goto alloc_sitd; - list_del (&sitd->sitd_list); + list_del(&sitd->sitd_list); sitd_dma = sitd->sitd_dma; } else { alloc_sitd: - spin_unlock_irqrestore (&ehci->lock, flags); - sitd = dma_pool_alloc (ehci->sitd_pool, mem_flags, + spin_unlock_irqrestore(&ehci->lock, flags); + sitd = dma_pool_alloc(ehci->sitd_pool, mem_flags, &sitd_dma); - spin_lock_irqsave (&ehci->lock, flags); + spin_lock_irqsave(&ehci->lock, flags); if (!sitd) { iso_sched_free(stream, iso_sched); spin_unlock_irqrestore(&ehci->lock, flags); @@ -2106,17 +2105,17 @@ sitd_urb_transaction ( } } - memset (sitd, 0, sizeof *sitd); + memset(sitd, 0, sizeof(*sitd)); sitd->sitd_dma = sitd_dma; sitd->frame = NO_FRAME; - list_add (&sitd->sitd_list, &iso_sched->td_list); + list_add(&sitd->sitd_list, &iso_sched->td_list); } /* temporarily store schedule info in hcpriv */ urb->hcpriv = iso_sched; urb->error_count = 0; - spin_unlock_irqrestore (&ehci->lock, flags); + spin_unlock_irqrestore(&ehci->lock, flags); return 0; } @@ -2131,8 +2130,8 @@ sitd_patch( unsigned index ) { - struct ehci_iso_packet *uf = &iso_sched->packet [index]; - u64 bufp = uf->bufp; + struct ehci_iso_packet *uf = &iso_sched->packet[index]; + u64 bufp; sitd->hw_next = EHCI_LIST_END(ehci); sitd->hw_fullspeed_ep = stream->address; @@ -2152,14 +2151,14 @@ sitd_patch( } static inline void -sitd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_sitd *sitd) +sitd_link(struct ehci_hcd *ehci, unsigned frame, struct ehci_sitd *sitd) { /* note: sitd ordering could matter (CSPLIT then SSPLIT) */ - sitd->sitd_next = ehci->pshadow [frame]; - sitd->hw_next = ehci->periodic [frame]; - ehci->pshadow [frame].sitd = sitd; + sitd->sitd_next = ehci->pshadow[frame]; + sitd->hw_next = ehci->periodic[frame]; + ehci->pshadow[frame].sitd = sitd; sitd->frame = frame; - wmb (); + wmb(); ehci->periodic[frame] = cpu_to_hc32(ehci, sitd->sitd_dma | Q_TYPE_SITD); } @@ -2196,13 +2195,13 @@ static void sitd_link_urb( packet++) { /* ASSERT: we have all necessary sitds */ - BUG_ON (list_empty (&sched->td_list)); + BUG_ON(list_empty(&sched->td_list)); /* ASSERT: no itds for this endpoint in this frame */ - sitd = list_entry (sched->td_list.next, + sitd = list_entry(sched->td_list.next, struct ehci_sitd, sitd_list); - list_move_tail (&sitd->sitd_list, &stream->td_list); + list_move_tail(&sitd->sitd_list, &stream->td_list); sitd->stream = stream; sitd->urb = urb; @@ -2215,7 +2214,7 @@ static void sitd_link_urb( stream->next_uframe = next_uframe & (mod - 1); /* don't need that schedule data any more */ - iso_sched_free (stream, sched); + iso_sched_free(stream, sched); urb->hcpriv = stream; ++ehci->isoc_count; @@ -2242,20 +2241,20 @@ static bool sitd_complete(struct ehci_hcd *ehci, struct ehci_sitd *sitd) struct urb *urb = sitd->urb; struct usb_iso_packet_descriptor *desc; u32 t; - int urb_index = -1; + int urb_index; struct ehci_iso_stream *stream = sitd->stream; struct usb_device *dev; bool retval = false; urb_index = sitd->index; - desc = &urb->iso_frame_desc [urb_index]; + desc = &urb->iso_frame_desc[urb_index]; t = hc32_to_cpup(ehci, &sitd->hw_results); /* report transfer status */ if (unlikely(t & SITD_ERRS)) { urb->error_count++; if (t & SITD_STS_DBE) - desc->status = usb_pipein (urb->pipe) + desc->status = usb_pipein(urb->pipe) ? -ENOSR /* hc couldn't read */ : -ECOMM; /* hc couldn't write */ else if (t & SITD_STS_BABBLE) @@ -2275,9 +2274,10 @@ static bool sitd_complete(struct ehci_hcd *ehci, struct ehci_sitd *sitd) if ((urb_index + 1) != urb->number_of_packets) goto done; - /* ASSERT: it's really the last sitd for this urb - list_for_each_entry (sitd, &stream->td_list, sitd_list) - BUG_ON (sitd->urb == urb); + /* + * ASSERT: it's really the last sitd for this urb + * list_for_each_entry (sitd, &stream->td_list, sitd_list) + * BUG_ON(sitd->urb == urb); */ /* give urb back to the driver; completion often (re)submits */ @@ -2316,7 +2316,7 @@ done: } -static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, +static int sitd_submit(struct ehci_hcd *ehci, struct urb *urb, gfp_t mem_flags) { int status = -EINVAL; @@ -2324,35 +2324,35 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, struct ehci_iso_stream *stream; /* Get iso_stream head */ - stream = iso_stream_find (ehci, urb); + stream = iso_stream_find(ehci, urb); if (stream == NULL) { - ehci_dbg (ehci, "can't get iso stream\n"); + ehci_dbg(ehci, "can't get iso stream\n"); return -ENOMEM; } if (urb->interval != stream->ps.period) { - ehci_dbg (ehci, "can't change iso interval %d --> %d\n", + ehci_dbg(ehci, "can't change iso interval %d --> %d\n", stream->ps.period, urb->interval); goto done; } #ifdef EHCI_URB_TRACE - ehci_dbg (ehci, + ehci_dbg(ehci, "submit %p dev%s ep%d%s-iso len %d\n", urb, urb->dev->devpath, - usb_pipeendpoint (urb->pipe), - usb_pipein (urb->pipe) ? "in" : "out", + usb_pipeendpoint(urb->pipe), + usb_pipein(urb->pipe) ? "in" : "out", urb->transfer_buffer_length); #endif /* allocate SITDs */ - status = sitd_urb_transaction (stream, ehci, urb, mem_flags); + status = sitd_urb_transaction(stream, ehci, urb, mem_flags); if (status < 0) { - ehci_dbg (ehci, "can't init sitds\n"); + ehci_dbg(ehci, "can't init sitds\n"); goto done; } /* schedule ... need to lock */ - spin_lock_irqsave (&ehci->lock, flags); + spin_lock_irqsave(&ehci->lock, flags); if (unlikely(!HCD_HW_ACCESSIBLE(ehci_to_hcd(ehci)))) { status = -ESHUTDOWN; goto done_not_linked; @@ -2362,7 +2362,7 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, goto done_not_linked; status = iso_stream_schedule(ehci, urb, stream); if (likely(status == 0)) { - sitd_link_urb (ehci, urb, ehci->periodic_size << 3, stream); + sitd_link_urb(ehci, urb, ehci->periodic_size << 3, stream); } else if (status > 0) { status = 0; ehci_urb_done(ehci, urb, 0); @@ -2370,7 +2370,7 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb); } done_not_linked: - spin_unlock_irqrestore (&ehci->lock, flags); + spin_unlock_irqrestore(&ehci->lock, flags); done: return status; } @@ -2379,9 +2379,11 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, static void scan_isoc(struct ehci_hcd *ehci) { - unsigned uf, now_frame, frame; - unsigned fmask = ehci->periodic_size - 1; - bool modified, live; + unsigned uf, now_frame, frame; + unsigned fmask = ehci->periodic_size - 1; + bool modified, live; + union ehci_shadow q, *q_p; + __hc32 type, *hw_p; /* * When running, scan from last scan point up to "now" @@ -2399,119 +2401,117 @@ static void scan_isoc(struct ehci_hcd *ehci) ehci->now_frame = now_frame; frame = ehci->last_iso_frame; - for (;;) { - union ehci_shadow q, *q_p; - __hc32 type, *hw_p; restart: - /* scan each element in frame's queue for completions */ - q_p = &ehci->pshadow [frame]; - hw_p = &ehci->periodic [frame]; - q.ptr = q_p->ptr; - type = Q_NEXT_TYPE(ehci, *hw_p); - modified = false; - - while (q.ptr != NULL) { - switch (hc32_to_cpu(ehci, type)) { - case Q_TYPE_ITD: - /* If this ITD is still active, leave it for - * later processing ... check the next entry. - * No need to check for activity unless the - * frame is current. - */ - if (frame == now_frame && live) { - rmb(); - for (uf = 0; uf < 8; uf++) { - if (q.itd->hw_transaction[uf] & - ITD_ACTIVE(ehci)) - break; - } - if (uf < 8) { - q_p = &q.itd->itd_next; - hw_p = &q.itd->hw_next; - type = Q_NEXT_TYPE(ehci, - q.itd->hw_next); - q = *q_p; + /* Scan each element in frame's queue for completions */ + q_p = &ehci->pshadow[frame]; + hw_p = &ehci->periodic[frame]; + q.ptr = q_p->ptr; + type = Q_NEXT_TYPE(ehci, *hw_p); + modified = false; + + while (q.ptr != NULL) { + switch (hc32_to_cpu(ehci, type)) { + case Q_TYPE_ITD: + /* + * If this ITD is still active, leave it for + * later processing ... check the next entry. + * No need to check for activity unless the + * frame is current. + */ + if (frame == now_frame && live) { + rmb(); + for (uf = 0; uf < 8; uf++) { + if (q.itd->hw_transaction[uf] & + ITD_ACTIVE(ehci)) break; - } } - - /* Take finished ITDs out of the schedule - * and process them: recycle, maybe report - * URB completion. HC won't cache the - * pointer for much longer, if at all. - */ - *q_p = q.itd->itd_next; - if (!ehci->use_dummy_qh || - q.itd->hw_next != EHCI_LIST_END(ehci)) - *hw_p = q.itd->hw_next; - else - *hw_p = cpu_to_hc32(ehci, - ehci->dummy->qh_dma); - type = Q_NEXT_TYPE(ehci, q.itd->hw_next); - wmb(); - modified = itd_complete (ehci, q.itd); - q = *q_p; - break; - case Q_TYPE_SITD: - /* If this SITD is still active, leave it for - * later processing ... check the next entry. - * No need to check for activity unless the - * frame is current. - */ - if (((frame == now_frame) || - (((frame + 1) & fmask) == now_frame)) - && live - && (q.sitd->hw_results & - SITD_ACTIVE(ehci))) { - - q_p = &q.sitd->sitd_next; - hw_p = &q.sitd->hw_next; + if (uf < 8) { + q_p = &q.itd->itd_next; + hw_p = &q.itd->hw_next; type = Q_NEXT_TYPE(ehci, - q.sitd->hw_next); + q.itd->hw_next); q = *q_p; break; } + } + + /* + * Take finished ITDs out of the schedule + * and process them: recycle, maybe report + * URB completion. HC won't cache the + * pointer for much longer, if at all. + */ + *q_p = q.itd->itd_next; + if (!ehci->use_dummy_qh || + q.itd->hw_next != EHCI_LIST_END(ehci)) + *hw_p = q.itd->hw_next; + else + *hw_p = cpu_to_hc32(ehci, ehci->dummy->qh_dma); + type = Q_NEXT_TYPE(ehci, q.itd->hw_next); + wmb(); + modified = itd_complete(ehci, q.itd); + q = *q_p; + break; + case Q_TYPE_SITD: + /* + * If this SITD is still active, leave it for + * later processing ... check the next entry. + * No need to check for activity unless the + * frame is current. + */ + if (((frame == now_frame) || + (((frame + 1) & fmask) == now_frame)) + && live + && (q.sitd->hw_results & SITD_ACTIVE(ehci))) { - /* Take finished SITDs out of the schedule - * and process them: recycle, maybe report - * URB completion. - */ - *q_p = q.sitd->sitd_next; - if (!ehci->use_dummy_qh || - q.sitd->hw_next != EHCI_LIST_END(ehci)) - *hw_p = q.sitd->hw_next; - else - *hw_p = cpu_to_hc32(ehci, - ehci->dummy->qh_dma); + q_p = &q.sitd->sitd_next; + hw_p = &q.sitd->hw_next; type = Q_NEXT_TYPE(ehci, q.sitd->hw_next); - wmb(); - modified = sitd_complete (ehci, q.sitd); q = *q_p; break; - default: - ehci_dbg(ehci, "corrupt type %d frame %d shadow %p\n", - type, frame, q.ptr); - // BUG (); - /* FALL THROUGH */ - case Q_TYPE_QH: - case Q_TYPE_FSTN: - /* End of the iTDs and siTDs */ - q.ptr = NULL; - break; } - /* assume completion callbacks modify the queue */ - if (unlikely(modified && ehci->isoc_count > 0)) - goto restart; - } - - /* Stop when we have reached the current frame */ - if (frame == now_frame) + /* + * Take finished SITDs out of the schedule + * and process them: recycle, maybe report + * URB completion. + */ + *q_p = q.sitd->sitd_next; + if (!ehci->use_dummy_qh || + q.sitd->hw_next != EHCI_LIST_END(ehci)) + *hw_p = q.sitd->hw_next; + else + *hw_p = cpu_to_hc32(ehci, ehci->dummy->qh_dma); + type = Q_NEXT_TYPE(ehci, q.sitd->hw_next); + wmb(); + modified = sitd_complete(ehci, q.sitd); + q = *q_p; + break; + default: + ehci_dbg(ehci, "corrupt type %d frame %d shadow %p\n", + type, frame, q.ptr); + /* BUG(); */ + /* FALL THROUGH */ + case Q_TYPE_QH: + case Q_TYPE_FSTN: + /* End of the iTDs and siTDs */ + q.ptr = NULL; break; + } - /* The last frame may still have active siTDs */ - ehci->last_iso_frame = frame; - frame = (frame + 1) & fmask; + /* Assume completion callbacks modify the queue */ + if (unlikely(modified && ehci->isoc_count > 0)) + goto restart; } + + /* Stop when we have reached the current frame */ + if (frame == now_frame) + return; + + /* The last frame may still have active siTDs */ + ehci->last_iso_frame = frame; + frame = (frame + 1) & fmask; + + goto restart; } diff --git a/drivers/usb/host/ehci-st.c b/drivers/usb/host/ehci-st.c index b7c5cfa..a94ed67 100644 --- a/drivers/usb/host/ehci-st.c +++ b/drivers/usb/host/ehci-st.c @@ -287,8 +287,7 @@ static int st_ehci_suspend(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); struct usb_ehci_pdata *pdata = dev_get_platdata(dev); - struct platform_device *pdev = - container_of(dev, struct platform_device, dev); + struct platform_device *pdev = to_platform_device(dev); bool do_wakeup = device_may_wakeup(dev); int ret; @@ -308,8 +307,7 @@ static int st_ehci_resume(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); struct usb_ehci_pdata *pdata = dev_get_platdata(dev); - struct platform_device *pdev = - container_of(dev, struct platform_device, dev); + struct platform_device *pdev = to_platform_device(dev); int err; pinctrl_pm_select_default_state(dev); diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c index 424ac5d..69f50e6 100644 --- a/drivers/usb/host/ehci-timer.c +++ b/drivers/usb/host/ehci-timer.c @@ -72,6 +72,7 @@ static unsigned event_delays_ns[] = { 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_DEAD */ 1125 * NSEC_PER_USEC, /* EHCI_HRTIMER_UNLINK_INTR */ 2 * NSEC_PER_MSEC, /* EHCI_HRTIMER_FREE_ITDS */ + 2 * NSEC_PER_MSEC, /* EHCI_HRTIMER_ACTIVE_UNLINK */ 5 * NSEC_PER_MSEC, /* EHCI_HRTIMER_START_UNLINK_INTR */ 6 * NSEC_PER_MSEC, /* EHCI_HRTIMER_ASYNC_UNLINKS */ 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_IAA_WATCHDOG */ @@ -237,6 +238,7 @@ static void ehci_handle_start_intr_unlinks(struct ehci_hcd *ehci) ehci->intr_unlink_wait_cycle)) break; list_del_init(&qh->unlink_node); + qh->unlink_reason |= QH_UNLINK_QUEUE_EMPTY; start_unlink_intr(ehci, qh); } @@ -360,7 +362,7 @@ static void ehci_iaa_watchdog(struct ehci_hcd *ehci) } ehci_dbg(ehci, "IAA watchdog: status %x cmd %x\n", status, cmd); - end_unlink_async(ehci); + end_iaa_cycle(ehci); } @@ -394,6 +396,7 @@ static void (*event_handlers[])(struct ehci_hcd *) = { ehci_handle_controller_death, /* EHCI_HRTIMER_POLL_DEAD */ ehci_handle_intr_unlinks, /* EHCI_HRTIMER_UNLINK_INTR */ end_free_itds, /* EHCI_HRTIMER_FREE_ITDS */ + end_unlink_async, /* EHCI_HRTIMER_ACTIVE_UNLINK */ ehci_handle_start_intr_unlinks, /* EHCI_HRTIMER_START_UNLINK_INTR */ unlink_empty_async, /* EHCI_HRTIMER_ASYNC_UNLINKS */ ehci_iaa_watchdog, /* EHCI_HRTIMER_IAA_WATCHDOG */ diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index ec61aed..3f3b74a 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -110,6 +110,7 @@ enum ehci_hrtimer_event { EHCI_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */ EHCI_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */ EHCI_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */ + EHCI_HRTIMER_ACTIVE_UNLINK, /* Wait while unlinking an active QH */ EHCI_HRTIMER_START_UNLINK_INTR, /* Unlink empty interrupt QHs */ EHCI_HRTIMER_ASYNC_UNLINKS, /* Unlink empty async QHs */ EHCI_HRTIMER_IAA_WATCHDOG, /* Handle lost IAA interrupts */ @@ -156,6 +157,8 @@ struct ehci_hcd { /* one per controller */ struct list_head async_idle; unsigned async_unlink_cycle; unsigned async_count; /* async activity count */ + __hc32 old_current; /* Test for QH becoming */ + __hc32 old_token; /* inactive during unlink */ /* periodic schedule support */ #define DEFAULT_I_TDPS 1024 /* some HCs can do less */ @@ -185,7 +188,7 @@ struct ehci_hcd { /* one per controller */ struct ehci_sitd *last_sitd_to_free; /* per root hub port */ - unsigned long reset_done [EHCI_MAX_ROOT_PORTS]; + unsigned long reset_done[EHCI_MAX_ROOT_PORTS]; /* bit vectors (one bit per port) */ unsigned long bus_suspended; /* which ports were @@ -244,9 +247,9 @@ struct ehci_hcd { /* one per controller */ /* irq statistics */ #ifdef EHCI_STATS struct ehci_stats stats; -# define COUNT(x) do { (x)++; } while (0) +# define COUNT(x) ((x)++) #else -# define COUNT(x) do {} while (0) +# define COUNT(x) #endif /* debug files */ @@ -268,13 +271,13 @@ struct ehci_hcd { /* one per controller */ }; /* convert between an HCD pointer and the corresponding EHCI_HCD */ -static inline struct ehci_hcd *hcd_to_ehci (struct usb_hcd *hcd) +static inline struct ehci_hcd *hcd_to_ehci(struct usb_hcd *hcd) { return (struct ehci_hcd *) (hcd->hcd_priv); } -static inline struct usb_hcd *ehci_to_hcd (struct ehci_hcd *ehci) +static inline struct usb_hcd *ehci_to_hcd(struct ehci_hcd *ehci) { - return container_of ((void *) ehci, struct usb_hcd, hcd_priv); + return container_of((void *) ehci, struct usb_hcd, hcd_priv); } /*-------------------------------------------------------------------------*/ @@ -316,25 +319,25 @@ struct ehci_qtd { #define HALT_BIT(ehci) cpu_to_hc32(ehci, QTD_STS_HALT) #define STATUS_BIT(ehci) cpu_to_hc32(ehci, QTD_STS_STS) - __hc32 hw_buf [5]; /* see EHCI 3.5.4 */ - __hc32 hw_buf_hi [5]; /* Appendix B */ + __hc32 hw_buf[5]; /* see EHCI 3.5.4 */ + __hc32 hw_buf_hi[5]; /* Appendix B */ /* the rest is HCD-private */ dma_addr_t qtd_dma; /* qtd address */ struct list_head qtd_list; /* sw qtd list */ struct urb *urb; /* qtd's urb */ size_t length; /* length of buffer */ -} __attribute__ ((aligned (32))); +} __aligned(32); /* mask NakCnt+T in qh->hw_alt_next */ -#define QTD_MASK(ehci) cpu_to_hc32 (ehci, ~0x1f) +#define QTD_MASK(ehci) cpu_to_hc32(ehci, ~0x1f) -#define IS_SHORT_READ(token) (QTD_LENGTH (token) != 0 && QTD_PID (token) == 1) +#define IS_SHORT_READ(token) (QTD_LENGTH(token) != 0 && QTD_PID(token) == 1) /*-------------------------------------------------------------------------*/ /* type tag from {qh,itd,sitd,fstn}->hw_next */ -#define Q_NEXT_TYPE(ehci,dma) ((dma) & cpu_to_hc32(ehci, 3 << 1)) +#define Q_NEXT_TYPE(ehci, dma) ((dma) & cpu_to_hc32(ehci, 3 << 1)) /* * Now the following defines are not converted using the @@ -350,7 +353,8 @@ struct ehci_qtd { #define Q_TYPE_FSTN (3 << 1) /* next async queue entry, or pointer to interrupt/periodic QH */ -#define QH_NEXT(ehci,dma) (cpu_to_hc32(ehci, (((u32)dma)&~0x01f)|Q_TYPE_QH)) +#define QH_NEXT(ehci, dma) \ + (cpu_to_hc32(ehci, (((u32) dma) & ~0x01f) | Q_TYPE_QH)) /* for periodic/async schedules and qtd lists, mark end of list */ #define EHCI_LIST_END(ehci) cpu_to_hc32(ehci, 1) /* "null pointer" to hw */ @@ -405,9 +409,9 @@ struct ehci_qh_hw { __hc32 hw_qtd_next; __hc32 hw_alt_next; __hc32 hw_token; - __hc32 hw_buf [5]; - __hc32 hw_buf_hi [5]; -} __attribute__ ((aligned(32))); + __hc32 hw_buf[5]; + __hc32 hw_buf_hi[5]; +} __aligned(32); struct ehci_qh { struct ehci_qh_hw *hw; /* Must come first */ @@ -432,13 +436,19 @@ struct ehci_qh { u8 xacterrs; /* XactErr retry counter */ #define QH_XACTERR_MAX 32 /* XactErr retry limit */ + u8 unlink_reason; +#define QH_UNLINK_HALTED 0x01 /* Halt flag is set */ +#define QH_UNLINK_SHORT_READ 0x02 /* Recover from a short read */ +#define QH_UNLINK_DUMMY_OVERLAY 0x04 /* QH overlayed the dummy TD */ +#define QH_UNLINK_SHUTDOWN 0x08 /* The HC isn't running */ +#define QH_UNLINK_QUEUE_EMPTY 0x10 /* Reached end of the queue */ +#define QH_UNLINK_REQUESTED 0x20 /* Disable, reset, or dequeue */ + u8 gap_uf; /* uframes split/csplit gap */ unsigned is_out:1; /* bulk or intr OUT */ unsigned clearing_tt:1; /* Clear-TT-Buf in progress */ unsigned dequeue_during_giveback:1; - unsigned exception:1; /* got a fault, or an unlink - was requested */ unsigned should_be_inactive:1; }; @@ -462,7 +472,7 @@ struct ehci_iso_sched { struct list_head td_list; unsigned span; unsigned first_packet; - struct ehci_iso_packet packet [0]; + struct ehci_iso_packet packet[0]; }; /* @@ -510,7 +520,7 @@ struct ehci_iso_stream { struct ehci_itd { /* first part defined by EHCI spec */ __hc32 hw_next; /* see EHCI 3.3.1 */ - __hc32 hw_transaction [8]; /* see EHCI 3.3.2 */ + __hc32 hw_transaction[8]; /* see EHCI 3.3.2 */ #define EHCI_ISOC_ACTIVE (1<<31) /* activate transfer this slot */ #define EHCI_ISOC_BUF_ERR (1<<30) /* Data buffer error */ #define EHCI_ISOC_BABBLE (1<<29) /* babble detected */ @@ -520,8 +530,8 @@ struct ehci_itd { #define ITD_ACTIVE(ehci) cpu_to_hc32(ehci, EHCI_ISOC_ACTIVE) - __hc32 hw_bufp [7]; /* see EHCI 3.3.3 */ - __hc32 hw_bufp_hi [7]; /* Appendix B */ + __hc32 hw_bufp[7]; /* see EHCI 3.3.3 */ + __hc32 hw_bufp_hi[7]; /* Appendix B */ /* the rest is HCD-private */ dma_addr_t itd_dma; /* for this itd */ @@ -535,7 +545,7 @@ struct ehci_itd { unsigned frame; /* where scheduled */ unsigned pg; unsigned index[8]; /* in urb->iso_frame_desc */ -} __attribute__ ((aligned (32))); +} __aligned(32); /*-------------------------------------------------------------------------*/ @@ -554,7 +564,7 @@ struct ehci_sitd { __hc32 hw_results; /* EHCI table 3-11 */ #define SITD_IOC (1 << 31) /* interrupt on completion */ #define SITD_PAGE (1 << 30) /* buffer 0/1 */ -#define SITD_LENGTH(x) (0x3ff & ((x)>>16)) +#define SITD_LENGTH(x) (((x) >> 16) & 0x3ff) #define SITD_STS_ACTIVE (1 << 7) /* HC may execute this */ #define SITD_STS_ERR (1 << 6) /* error from TT */ #define SITD_STS_DBE (1 << 5) /* data buffer error (in HC) */ @@ -565,9 +575,9 @@ struct ehci_sitd { #define SITD_ACTIVE(ehci) cpu_to_hc32(ehci, SITD_STS_ACTIVE) - __hc32 hw_buf [2]; /* EHCI table 3-12 */ + __hc32 hw_buf[2]; /* EHCI table 3-12 */ __hc32 hw_backpointer; /* EHCI table 3-13 */ - __hc32 hw_buf_hi [2]; /* Appendix B */ + __hc32 hw_buf_hi[2]; /* Appendix B */ /* the rest is HCD-private */ dma_addr_t sitd_dma; @@ -578,7 +588,7 @@ struct ehci_sitd { struct list_head sitd_list; /* list of stream's sitds */ unsigned frame; unsigned index; -} __attribute__ ((aligned (32))); +} __aligned(32); /*-------------------------------------------------------------------------*/ @@ -598,7 +608,7 @@ struct ehci_fstn { /* the rest is HCD-private */ dma_addr_t fstn_dma; union ehci_shadow fstn_next; /* ptr to periodic q entry */ -} __attribute__ ((aligned (32))); +} __aligned(32); /*-------------------------------------------------------------------------*/ @@ -634,10 +644,10 @@ struct ehci_tt { /* Prepare the PORTSC wakeup flags during controller suspend/resume */ #define ehci_prepare_ports_for_controller_suspend(ehci, do_wakeup) \ - ehci_adjust_port_wakeup_flags(ehci, true, do_wakeup); + ehci_adjust_port_wakeup_flags(ehci, true, do_wakeup) #define ehci_prepare_ports_for_controller_resume(ehci) \ - ehci_adjust_port_wakeup_flags(ehci, false, false); + ehci_adjust_port_wakeup_flags(ehci, false, false) /*-------------------------------------------------------------------------*/ @@ -731,7 +741,7 @@ ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc) #endif static inline unsigned int ehci_readl(const struct ehci_hcd *ehci, - __u32 __iomem * regs) + __u32 __iomem *regs) { #ifdef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO return ehci_big_endian_mmio(ehci) ? @@ -806,7 +816,7 @@ static inline void set_ohci_hcfs(struct ehci_hcd *ehci, int operational) #define ehci_big_endian_desc(e) ((e)->big_endian_desc) /* cpu to ehci */ -static inline __hc32 cpu_to_hc32 (const struct ehci_hcd *ehci, const u32 x) +static inline __hc32 cpu_to_hc32(const struct ehci_hcd *ehci, const u32 x) { return ehci_big_endian_desc(ehci) ? (__force __hc32)cpu_to_be32(x) @@ -814,14 +824,14 @@ static inline __hc32 cpu_to_hc32 (const struct ehci_hcd *ehci, const u32 x) } /* ehci to cpu */ -static inline u32 hc32_to_cpu (const struct ehci_hcd *ehci, const __hc32 x) +static inline u32 hc32_to_cpu(const struct ehci_hcd *ehci, const __hc32 x) { return ehci_big_endian_desc(ehci) ? be32_to_cpu((__force __be32)x) : le32_to_cpu((__force __le32)x); } -static inline u32 hc32_to_cpup (const struct ehci_hcd *ehci, const __hc32 *x) +static inline u32 hc32_to_cpup(const struct ehci_hcd *ehci, const __hc32 *x) { return ehci_big_endian_desc(ehci) ? be32_to_cpup((__force __be32 *)x) @@ -831,18 +841,18 @@ static inline u32 hc32_to_cpup (const struct ehci_hcd *ehci, const __hc32 *x) #else /* cpu to ehci */ -static inline __hc32 cpu_to_hc32 (const struct ehci_hcd *ehci, const u32 x) +static inline __hc32 cpu_to_hc32(const struct ehci_hcd *ehci, const u32 x) { return cpu_to_le32(x); } /* ehci to cpu */ -static inline u32 hc32_to_cpu (const struct ehci_hcd *ehci, const __hc32 x) +static inline u32 hc32_to_cpu(const struct ehci_hcd *ehci, const __hc32 x) { return le32_to_cpu(x); } -static inline u32 hc32_to_cpup (const struct ehci_hcd *ehci, const __hc32 *x) +static inline u32 hc32_to_cpup(const struct ehci_hcd *ehci, const __hc32 *x) { return le32_to_cpup(x); } @@ -852,18 +862,13 @@ static inline u32 hc32_to_cpup (const struct ehci_hcd *ehci, const __hc32 *x) /*-------------------------------------------------------------------------*/ #define ehci_dbg(ehci, fmt, args...) \ - dev_dbg(ehci_to_hcd(ehci)->self.controller , fmt , ## args) + dev_dbg(ehci_to_hcd(ehci)->self.controller, fmt, ## args) #define ehci_err(ehci, fmt, args...) \ - dev_err(ehci_to_hcd(ehci)->self.controller , fmt , ## args) + dev_err(ehci_to_hcd(ehci)->self.controller, fmt, ## args) #define ehci_info(ehci, fmt, args...) \ - dev_info(ehci_to_hcd(ehci)->self.controller , fmt , ## args) + dev_info(ehci_to_hcd(ehci)->self.controller, fmt, ## args) #define ehci_warn(ehci, fmt, args...) \ - dev_warn(ehci_to_hcd(ehci)->self.controller , fmt , ## args) - - -#ifndef CONFIG_DYNAMIC_DEBUG -#define STUB_DEBUG_FILES -#endif + dev_warn(ehci_to_hcd(ehci)->self.controller, fmt, ## args) /*-------------------------------------------------------------------------*/ @@ -883,12 +888,10 @@ extern int ehci_handshake(struct ehci_hcd *ehci, void __iomem *ptr, u32 mask, u32 done, int usec); extern int ehci_reset(struct ehci_hcd *ehci); -#ifdef CONFIG_PM extern int ehci_suspend(struct usb_hcd *hcd, bool do_wakeup); extern int ehci_resume(struct usb_hcd *hcd, bool force_reset); extern void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, bool suspending, bool do_wakeup); -#endif /* CONFIG_PM */ extern int ehci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength); diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c index 2341af4..360a5e9 100644 --- a/drivers/usb/host/fotg210-hcd.c +++ b/drivers/usb/host/fotg210-hcd.c @@ -2267,7 +2267,7 @@ static unsigned qh_completions(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) { struct fotg210_qtd *last, *end = qh->dummy; - struct list_head *entry, *tmp; + struct fotg210_qtd *qtd, *tmp; int last_status; int stopped; unsigned count = 0; @@ -2301,12 +2301,10 @@ rescan: * then let the queue advance. * if queue is stopped, handles unlinks. */ - list_for_each_safe(entry, tmp, &qh->qtd_list) { - struct fotg210_qtd *qtd; + list_for_each_entry_safe(qtd, tmp, &qh->qtd_list, qtd_list) { struct urb *urb; u32 token = 0; - qtd = list_entry(entry, struct fotg210_qtd, qtd_list); urb = qtd->urb; /* clean up any state from previous QTD ...*/ @@ -2544,14 +2542,11 @@ retry_xacterr: * used for cleanup after errors, before HC sees an URB's TDs. */ static void qtd_list_free(struct fotg210_hcd *fotg210, struct urb *urb, - struct list_head *qtd_list) + struct list_head *head) { - struct list_head *entry, *temp; - - list_for_each_safe(entry, temp, qtd_list) { - struct fotg210_qtd *qtd; + struct fotg210_qtd *qtd, *temp; - qtd = list_entry(entry, struct fotg210_qtd, qtd_list); + list_for_each_entry_safe(qtd, temp, head, qtd_list) { list_del(&qtd->qtd_list); fotg210_qtd_free(fotg210, qtd); } diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c index 0c38265..1044b0f 100644 --- a/drivers/usb/host/fsl-mph-dr-of.c +++ b/drivers/usb/host/fsl-mph-dr-of.c @@ -17,6 +17,7 @@ #include <linux/of_platform.h> #include <linux/clk.h> #include <linux/module.h> +#include <linux/dma-mapping.h> struct fsl_usb2_dev_data { char *dr_mode; /* controller mode */ @@ -96,7 +97,11 @@ static struct platform_device *fsl_usb2_device_register( pdev->dev.parent = &ofdev->dev; pdev->dev.coherent_dma_mask = ofdev->dev.coherent_dma_mask; - *pdev->dev.dma_mask = *ofdev->dev.dma_mask; + + if (!pdev->dev.dma_mask) + pdev->dev.dma_mask = &ofdev->dev.coherent_dma_mask; + else + dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); retval = platform_device_add_data(pdev, pdata, sizeof(*pdata)); if (retval) diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c index bd98706..c369c29 100644 --- a/drivers/usb/host/max3421-hcd.c +++ b/drivers/usb/host/max3421-hcd.c @@ -797,19 +797,16 @@ max3421_check_unlink(struct usb_hcd *hcd) { struct spi_device *spi = to_spi_device(hcd->self.controller); struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd); - struct list_head *pos, *upos, *next_upos; struct max3421_ep *max3421_ep; struct usb_host_endpoint *ep; - struct urb *urb; + struct urb *urb, *next; unsigned long flags; int retval = 0; spin_lock_irqsave(&max3421_hcd->lock, flags); - list_for_each(pos, &max3421_hcd->ep_list) { - max3421_ep = container_of(pos, struct max3421_ep, ep_list); + list_for_each_entry(max3421_ep, &max3421_hcd->ep_list, ep_list) { ep = max3421_ep->ep; - list_for_each_safe(upos, next_upos, &ep->urb_list) { - urb = container_of(upos, struct urb, urb_list); + list_for_each_entry_safe(urb, next, &ep->urb_list, urb_list) { if (urb->unlinked) { retval = 1; dev_dbg(&spi->dev, "%s: URB %p unlinked=%d", @@ -1184,22 +1181,19 @@ dump_eps(struct usb_hcd *hcd) struct max3421_hcd *max3421_hcd = hcd_to_max3421(hcd); struct max3421_ep *max3421_ep; struct usb_host_endpoint *ep; - struct list_head *pos, *upos; char ubuf[512], *dp, *end; unsigned long flags; struct urb *urb; int epnum, ret; spin_lock_irqsave(&max3421_hcd->lock, flags); - list_for_each(pos, &max3421_hcd->ep_list) { - max3421_ep = container_of(pos, struct max3421_ep, ep_list); + list_for_each_entry(max3421_ep, &max3421_hcd->ep_list, ep_list) { ep = max3421_ep->ep; dp = ubuf; end = dp + sizeof(ubuf); *dp = '\0'; - list_for_each(upos, &ep->urb_list) { - urb = container_of(upos, struct urb, urb_list); + list_for_each_entry(urb, &ep->urb_list, urb_list) { ret = snprintf(dp, end - dp, " %p(%d.%s %d/%d)", urb, usb_pipetype(urb->pipe), usb_urb_dir_in(urb) ? "IN" : "OUT", diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index 8c6e15b..f789d29 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -583,9 +583,7 @@ static int ohci_hcd_at91_drv_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM - -static int +static int __maybe_unused ohci_hcd_at91_drv_suspend(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); @@ -630,7 +628,8 @@ ohci_hcd_at91_drv_suspend(struct device *dev) return ret; } -static int ohci_hcd_at91_drv_resume(struct device *dev) +static int __maybe_unused +ohci_hcd_at91_drv_resume(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); struct ohci_at91_priv *ohci_at91 = hcd_to_ohci_at91_priv(hcd); @@ -643,7 +642,6 @@ static int ohci_hcd_at91_drv_resume(struct device *dev) ohci_resume(hcd, false); return 0; } -#endif static SIMPLE_DEV_PM_OPS(ohci_hcd_at91_pm_ops, ohci_hcd_at91_drv_suspend, ohci_hcd_at91_drv_resume); diff --git a/drivers/usb/host/ohci-nxp.c b/drivers/usb/host/ohci-nxp.c index cfa9427..b7d4756 100644 --- a/drivers/usb/host/ohci-nxp.c +++ b/drivers/usb/host/ohci-nxp.c @@ -22,7 +22,6 @@ #include <linux/dma-mapping.h> #include <linux/io.h> #include <linux/i2c.h> -#include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> @@ -32,25 +31,9 @@ #include "ohci.h" - #include <mach/hardware.h> -#include <asm/mach-types.h> -#include <asm/io.h> - -#include <mach/platform.h> -#include <mach/irqs.h> #define USB_CONFIG_BASE 0x31020000 -#define PWRMAN_BASE 0x40004000 - -#define USB_CTRL IO_ADDRESS(PWRMAN_BASE + 0x64) - -/* USB_CTRL bit defines */ -#define USB_SLAVE_HCLK_EN (1 << 24) -#define USB_DEV_NEED_CLK_EN (1 << 22) -#define USB_HOST_NEED_CLK_EN (1 << 21) -#define PAD_CONTROL_LAST_DRIVEN (1 << 19) - #define USB_OTG_STAT_CONTROL IO_ADDRESS(USB_CONFIG_BASE + 0x110) /* USB_OTG_STAT_CONTROL bit defines */ @@ -75,9 +58,7 @@ static struct i2c_client *isp1301_i2c_client; extern int usb_disabled(void); -static struct clk *usb_pll_clk; -static struct clk *usb_dev_clk; -static struct clk *usb_otg_clk; +static struct clk *usb_host_clk; static void isp1301_configure_lpc32xx(void) { @@ -117,9 +98,6 @@ static void isp1301_configure_lpc32xx(void) i2c_smbus_write_byte_data(isp1301_i2c_client, ISP1301_I2C_INTERRUPT_RISING | ISP1301_I2C_REG_CLEAR_ADDR, ~0); - /* Enable usb_need_clk clock after transceiver is initialized */ - __raw_writel(__raw_readl(USB_CTRL) | USB_HOST_NEED_CLK_EN, USB_CTRL); - printk(KERN_INFO "ISP1301 Vendor ID : 0x%04x\n", i2c_smbus_read_word_data(isp1301_i2c_client, 0x00)); printk(KERN_INFO "ISP1301 Product ID : 0x%04x\n", @@ -192,59 +170,20 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev) goto fail_disable; } - /* Enable AHB slave USB clock, needed for further USB clock control */ - __raw_writel(USB_SLAVE_HCLK_EN | PAD_CONTROL_LAST_DRIVEN, USB_CTRL); - - /* Enable USB PLL */ - usb_pll_clk = devm_clk_get(&pdev->dev, "ck_pll5"); - if (IS_ERR(usb_pll_clk)) { - dev_err(&pdev->dev, "failed to acquire USB PLL\n"); - ret = PTR_ERR(usb_pll_clk); + /* Enable USB host clock */ + usb_host_clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(usb_host_clk)) { + dev_err(&pdev->dev, "failed to acquire USB OHCI clock\n"); + ret = PTR_ERR(usb_host_clk); goto fail_disable; } - ret = clk_prepare_enable(usb_pll_clk); + ret = clk_prepare_enable(usb_host_clk); if (ret < 0) { - dev_err(&pdev->dev, "failed to start USB PLL\n"); + dev_err(&pdev->dev, "failed to start USB OHCI clock\n"); goto fail_disable; } - ret = clk_set_rate(usb_pll_clk, 48000); - if (ret < 0) { - dev_err(&pdev->dev, "failed to set USB clock rate\n"); - goto fail_rate; - } - - /* Enable USB device clock */ - usb_dev_clk = devm_clk_get(&pdev->dev, "ck_usbd"); - if (IS_ERR(usb_dev_clk)) { - dev_err(&pdev->dev, "failed to acquire USB DEV Clock\n"); - ret = PTR_ERR(usb_dev_clk); - goto fail_rate; - } - - ret = clk_prepare_enable(usb_dev_clk); - if (ret < 0) { - dev_err(&pdev->dev, "failed to start USB DEV Clock\n"); - goto fail_rate; - } - - /* Enable USB otg clocks */ - usb_otg_clk = devm_clk_get(&pdev->dev, "ck_usb_otg"); - if (IS_ERR(usb_otg_clk)) { - dev_err(&pdev->dev, "failed to acquire USB DEV Clock\n"); - ret = PTR_ERR(usb_otg_clk); - goto fail_otg; - } - - __raw_writel(__raw_readl(USB_CTRL) | USB_HOST_NEED_CLK_EN, USB_CTRL); - - ret = clk_prepare_enable(usb_otg_clk); - if (ret < 0) { - dev_err(&pdev->dev, "failed to start USB DEV Clock\n"); - goto fail_otg; - } - isp1301_configure(); hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); @@ -283,11 +222,7 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev) fail_resource: usb_put_hcd(hcd); fail_hcd: - clk_disable_unprepare(usb_otg_clk); -fail_otg: - clk_disable_unprepare(usb_dev_clk); -fail_rate: - clk_disable_unprepare(usb_pll_clk); + clk_disable_unprepare(usb_host_clk); fail_disable: isp1301_i2c_client = NULL; return ret; @@ -300,9 +235,7 @@ static int ohci_hcd_nxp_remove(struct platform_device *pdev) usb_remove_hcd(hcd); ohci_nxp_stop_hc(); usb_put_hcd(hcd); - clk_disable_unprepare(usb_otg_clk); - clk_disable_unprepare(usb_dev_clk); - clk_disable_unprepare(usb_pll_clk); + clk_disable_unprepare(usb_host_clk); isp1301_i2c_client = NULL; return 0; diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c index c2669f18..ae1c988 100644 --- a/drivers/usb/host/ohci-platform.c +++ b/drivers/usb/host/ohci-platform.c @@ -310,8 +310,7 @@ static int ohci_platform_suspend(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); struct usb_ohci_pdata *pdata = dev->platform_data; - struct platform_device *pdev = - container_of(dev, struct platform_device, dev); + struct platform_device *pdev = to_platform_device(dev); bool do_wakeup = device_may_wakeup(dev); int ret; @@ -329,8 +328,7 @@ static int ohci_platform_resume(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); struct usb_ohci_pdata *pdata = dev_get_platdata(dev); - struct platform_device *pdev = - container_of(dev, struct platform_device, dev); + struct platform_device *pdev = to_platform_device(dev); if (pdata->power_on) { int err = pdata->power_on(pdev); diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c index e8c006e..a667cf2 100644 --- a/drivers/usb/host/ohci-pxa27x.c +++ b/drivers/usb/host/ohci-pxa27x.c @@ -435,7 +435,7 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device irq = platform_get_irq(pdev, 0); if (irq < 0) { pr_err("no resource of IORESOURCE_IRQ"); - return -ENXIO; + return irq; } usb_clk = devm_clk_get(&pdev->dev, NULL); diff --git a/drivers/usb/host/ohci-st.c b/drivers/usb/host/ohci-st.c index df9028e..acf2eb2 100644 --- a/drivers/usb/host/ohci-st.c +++ b/drivers/usb/host/ohci-st.c @@ -270,8 +270,7 @@ static int st_ohci_suspend(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); struct usb_ohci_pdata *pdata = dev->platform_data; - struct platform_device *pdev = - container_of(dev, struct platform_device, dev); + struct platform_device *pdev = to_platform_device(dev); bool do_wakeup = device_may_wakeup(dev); int ret; @@ -289,8 +288,7 @@ static int st_ohci_resume(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); struct usb_ohci_pdata *pdata = dev_get_platdata(dev); - struct platform_device *pdev = - container_of(dev, struct platform_device, dev); + struct platform_device *pdev = to_platform_device(dev); int err; if (pdata->power_on) { diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index bc46228..37f1725 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -735,10 +735,8 @@ extern void ohci_init_driver(struct hc_driver *drv, const struct ohci_driver_overrides *over); extern int ohci_restart(struct ohci_hcd *ohci); extern int ohci_setup(struct usb_hcd *hcd); -#ifdef CONFIG_PM extern int ohci_suspend(struct usb_hcd *hcd, bool do_wakeup); extern int ohci_resume(struct usb_hcd *hcd, bool hibernated); -#endif extern int ohci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength); extern int ohci_hub_status_data(struct usb_hcd *hcd, char *buf); diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c index bc74aca..4e4d601 100644 --- a/drivers/usb/host/oxu210hp-hcd.c +++ b/drivers/usb/host/oxu210hp-hcd.c @@ -981,7 +981,7 @@ static int qh_schedule(struct oxu_hcd *oxu, struct ehci_qh *qh); static unsigned qh_completions(struct oxu_hcd *oxu, struct ehci_qh *qh) { struct ehci_qtd *last = NULL, *end = qh->dummy; - struct list_head *entry, *tmp; + struct ehci_qtd *qtd, *tmp; int stopped; unsigned count = 0; int do_status = 0; @@ -1006,12 +1006,10 @@ static unsigned qh_completions(struct oxu_hcd *oxu, struct ehci_qh *qh) * then let the queue advance. * if queue is stopped, handles unlinks. */ - list_for_each_safe(entry, tmp, &qh->qtd_list) { - struct ehci_qtd *qtd; + list_for_each_entry_safe(qtd, tmp, &qh->qtd_list, qtd_list) { struct urb *urb; u32 token = 0; - qtd = list_entry(entry, struct ehci_qtd, qtd_list); urb = qtd->urb; /* Clean up any state from previous QTD ...*/ @@ -1174,14 +1172,11 @@ halt: * used for cleanup after errors, before HC sees an URB's TDs. */ static void qtd_list_free(struct oxu_hcd *oxu, - struct urb *urb, struct list_head *qtd_list) + struct urb *urb, struct list_head *head) { - struct list_head *entry, *temp; - - list_for_each_safe(entry, temp, qtd_list) { - struct ehci_qtd *qtd; + struct ehci_qtd *qtd, *temp; - qtd = list_entry(entry, struct ehci_qtd, qtd_list); + list_for_each_entry_safe(qtd, temp, head, qtd_list) { list_del(&qtd->qtd_list); oxu_qtd_free(oxu, qtd); } diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index 26cb8c8..35af362 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -992,7 +992,7 @@ static void quirk_usb_handoff_xhci(struct pci_dev *pdev) if ((ext_cap_offset + sizeof(val)) > len) { /* We're reading garbage from the controller */ dev_warn(&pdev->dev, "xHCI controller failing to respond"); - return; + goto iounmap; } val = readl(base + ext_cap_offset); @@ -1055,6 +1055,7 @@ hc_init: XHCI_MAX_HALT_USEC, val); } +iounmap: iounmap(base); } diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index 4cbd063..bfa7fa3 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -2099,16 +2099,13 @@ static void r8a66597_check_detect_child(struct r8a66597 *r8a66597, memset(now_map, 0, sizeof(now_map)); - list_for_each_entry(bus, &usb_bus_list, bus_list) { - if (!bus->root_hub) - continue; - - if (bus->busnum != hcd->self.busnum) - continue; - + mutex_lock(&usb_bus_idr_lock); + bus = idr_find(&usb_bus_idr, hcd->self.busnum); + if (bus && bus->root_hub) { collect_usb_address_map(bus->root_hub, now_map); update_usb_address_map(r8a66597, bus->root_hub, now_map); } + mutex_unlock(&usb_bus_idr_lock); } static int r8a66597_hub_status_data(struct usb_hcd *hcd, char *buf) diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c index 05c85c7..43d5293 100644 --- a/drivers/usb/host/u132-hcd.c +++ b/drivers/usb/host/u132-hcd.c @@ -1309,13 +1309,9 @@ static void u132_hcd_ring_work_scheduler(struct work_struct *work) u132_ring_put_kref(u132, ring); return; } else if (ring->curr_endp) { - struct u132_endp *last_endp = ring->curr_endp; - struct list_head *scan; - struct list_head *head = &last_endp->endp_ring; + struct u132_endp *endp, *last_endp = ring->curr_endp; unsigned long wakeup = 0; - list_for_each(scan, head) { - struct u132_endp *endp = list_entry(scan, - struct u132_endp, endp_ring); + list_for_each_entry(endp, &last_endp->endp_ring, endp_ring) { if (endp->queue_next == endp->queue_last) { } else if ((endp->delayed == 0) || time_after_eq(jiffies, endp->jiffies)) { @@ -2393,14 +2389,12 @@ static int u132_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, static int dequeue_from_overflow_chain(struct u132 *u132, struct u132_endp *endp, struct urb *urb) { - struct list_head *scan; - struct list_head *head = &endp->urb_more; - list_for_each(scan, head) { - struct u132_urbq *urbq = list_entry(scan, struct u132_urbq, - urb_more); + struct u132_urbq *urbq; + + list_for_each_entry(urbq, &endp->urb_more, urb_more) { if (urbq->urb == urb) { struct usb_hcd *hcd = u132_to_hcd(u132); - list_del(scan); + list_del(&urbq->urb_more); endp->queue_size -= 1; urb->error_count = 0; usb_hcd_giveback_urb(hcd, urb, 0); diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index b30b4ce..d61fcc4 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -50,14 +50,18 @@ static u8 usb_bos_descriptor [] = { 0x00, /* bU1DevExitLat, set later. */ 0x00, 0x00, /* __le16 bU2DevExitLat, set later. */ /* Second device capability, SuperSpeedPlus */ - 0x0c, /* bLength 12, will be adjusted later */ + 0x1c, /* bLength 28, will be adjusted later */ USB_DT_DEVICE_CAPABILITY, /* Device Capability */ USB_SSP_CAP_TYPE, /* bDevCapabilityType SUPERSPEED_PLUS */ 0x00, /* bReserved 0 */ - 0x00, 0x00, 0x00, 0x00, /* bmAttributes, get from xhci psic */ - 0x00, 0x00, /* wFunctionalitySupport */ + 0x23, 0x00, 0x00, 0x00, /* bmAttributes, SSAC=3 SSIC=1 */ + 0x01, 0x00, /* wFunctionalitySupport */ 0x00, 0x00, /* wReserved 0 */ - /* Sublink Speed Attributes are added in xhci_create_usb3_bos_desc() */ + /* Default Sublink Speed Attributes, overwrite if custom PSI exists */ + 0x34, 0x00, 0x05, 0x00, /* 5Gbps, symmetric, rx, ID = 4 */ + 0xb4, 0x00, 0x05, 0x00, /* 5Gbps, symmetric, tx, ID = 4 */ + 0x35, 0x40, 0x0a, 0x00, /* 10Gbps, SSP, symmetric, rx, ID = 5 */ + 0xb5, 0x40, 0x0a, 0x00, /* 10Gbps, SSP, symmetric, tx, ID = 5 */ }; static int xhci_create_usb3_bos_desc(struct xhci_hcd *xhci, char *buf, @@ -72,10 +76,14 @@ static int xhci_create_usb3_bos_desc(struct xhci_hcd *xhci, char *buf, ssp_cap_size = sizeof(usb_bos_descriptor) - desc_size; /* does xhci support USB 3.1 Enhanced SuperSpeed */ - if (xhci->usb3_rhub.min_rev >= 0x01 && xhci->usb3_rhub.psi_uid_count) { - /* two SSA entries for each unique PSI ID, one RX and one TX */ - ssa_count = xhci->usb3_rhub.psi_uid_count * 2; - ssa_size = ssa_count * sizeof(u32); + if (xhci->usb3_rhub.min_rev >= 0x01) { + /* does xhci provide a PSI table for SSA speed attributes? */ + if (xhci->usb3_rhub.psi_count) { + /* two SSA entries for each unique PSI ID, RX and TX */ + ssa_count = xhci->usb3_rhub.psi_uid_count * 2; + ssa_size = ssa_count * sizeof(u32); + ssp_cap_size -= 16; /* skip copying the default SSA */ + } desc_size += ssp_cap_size; usb3_1 = true; } @@ -102,7 +110,8 @@ static int xhci_create_usb3_bos_desc(struct xhci_hcd *xhci, char *buf, put_unaligned_le16(HCS_U2_LATENCY(temp), &buf[13]); } - if (usb3_1) { + /* If PSI table exists, add the custom speed attributes from it */ + if (usb3_1 && xhci->usb3_rhub.psi_count) { u32 ssp_cap_base, bm_attrib, psi; int offset; diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 5cd080e..80c1de2 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1070,7 +1070,7 @@ static u32 xhci_find_real_port_number(struct xhci_hcd *xhci, struct usb_device *top_dev; struct usb_hcd *hcd; - if (udev->speed == USB_SPEED_SUPER) + if (udev->speed >= USB_SPEED_SUPER) hcd = xhci->shared_hcd; else hcd = xhci->main_hcd; @@ -1105,6 +1105,10 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud /* 3) Only the control endpoint is valid - one endpoint context */ slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(1) | udev->route); switch (udev->speed) { + case USB_SPEED_SUPER_PLUS: + slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_SSP); + max_packets = MAX_PACKET(512); + break; case USB_SPEED_SUPER: slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_SS); max_packets = MAX_PACKET(512); @@ -1292,6 +1296,7 @@ static unsigned int xhci_get_endpoint_interval(struct usb_device *udev, } /* Fall through - SS and HS isoc/int have same decoding */ + case USB_SPEED_SUPER_PLUS: case USB_SPEED_SUPER: if (usb_endpoint_xfer_int(&ep->desc) || usb_endpoint_xfer_isoc(&ep->desc)) { @@ -1321,7 +1326,7 @@ static unsigned int xhci_get_endpoint_interval(struct usb_device *udev, default: BUG(); } - return EP_INTERVAL(interval); + return interval; } /* The "Mult" field in the endpoint context is only set for SuperSpeed isoc eps. @@ -1332,39 +1337,42 @@ static unsigned int xhci_get_endpoint_interval(struct usb_device *udev, static u32 xhci_get_endpoint_mult(struct usb_device *udev, struct usb_host_endpoint *ep) { - if (udev->speed != USB_SPEED_SUPER || + if (udev->speed < USB_SPEED_SUPER || !usb_endpoint_xfer_isoc(&ep->desc)) return 0; return ep->ss_ep_comp.bmAttributes; } +static u32 xhci_get_endpoint_max_burst(struct usb_device *udev, + struct usb_host_endpoint *ep) +{ + /* Super speed and Plus have max burst in ep companion desc */ + if (udev->speed >= USB_SPEED_SUPER) + return ep->ss_ep_comp.bMaxBurst; + + if (udev->speed == USB_SPEED_HIGH && + (usb_endpoint_xfer_isoc(&ep->desc) || + usb_endpoint_xfer_int(&ep->desc))) + return (usb_endpoint_maxp(&ep->desc) & 0x1800) >> 11; + + return 0; +} + static u32 xhci_get_endpoint_type(struct usb_host_endpoint *ep) { int in; - u32 type; in = usb_endpoint_dir_in(&ep->desc); - if (usb_endpoint_xfer_control(&ep->desc)) { - type = EP_TYPE(CTRL_EP); - } else if (usb_endpoint_xfer_bulk(&ep->desc)) { - if (in) - type = EP_TYPE(BULK_IN_EP); - else - type = EP_TYPE(BULK_OUT_EP); - } else if (usb_endpoint_xfer_isoc(&ep->desc)) { - if (in) - type = EP_TYPE(ISOC_IN_EP); - else - type = EP_TYPE(ISOC_OUT_EP); - } else if (usb_endpoint_xfer_int(&ep->desc)) { - if (in) - type = EP_TYPE(INT_IN_EP); - else - type = EP_TYPE(INT_OUT_EP); - } else { - type = 0; - } - return type; + + if (usb_endpoint_xfer_control(&ep->desc)) + return CTRL_EP; + if (usb_endpoint_xfer_bulk(&ep->desc)) + return in ? BULK_IN_EP : BULK_OUT_EP; + if (usb_endpoint_xfer_isoc(&ep->desc)) + return in ? ISOC_IN_EP : ISOC_OUT_EP; + if (usb_endpoint_xfer_int(&ep->desc)) + return in ? INT_IN_EP : INT_OUT_EP; + return 0; } /* Return the maximum endpoint service interval time (ESIT) payload. @@ -1382,7 +1390,12 @@ static u32 xhci_get_max_esit_payload(struct usb_device *udev, usb_endpoint_xfer_bulk(&ep->desc)) return 0; - if (udev->speed == USB_SPEED_SUPER) + /* SuperSpeedPlus Isoc ep sending over 48k per esit */ + if ((udev->speed >= USB_SPEED_SUPER_PLUS) && + USB_SS_SSP_ISOC_COMP(ep->ss_ep_comp.bmAttributes)) + return le32_to_cpu(ep->ssp_isoc_ep_comp.dwBytesPerInterval); + /* SuperSpeed or SuperSpeedPlus Isoc ep with less than 48k per esit */ + else if (udev->speed >= USB_SPEED_SUPER) return le16_to_cpu(ep->ss_ep_comp.wBytesPerInterval); max_packet = GET_MAX_PACKET(usb_endpoint_maxp(&ep->desc)); @@ -1404,10 +1417,14 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, struct xhci_ep_ctx *ep_ctx; struct xhci_ring *ep_ring; unsigned int max_packet; - unsigned int max_burst; - enum xhci_ring_type type; + enum xhci_ring_type ring_type; u32 max_esit_payload; u32 endpoint_type; + unsigned int max_burst; + unsigned int interval; + unsigned int mult; + unsigned int avg_trb_len; + unsigned int err_count = 0; ep_index = xhci_get_endpoint_index(&ep->desc); ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, ep_index); @@ -1415,12 +1432,11 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, endpoint_type = xhci_get_endpoint_type(ep); if (!endpoint_type) return -EINVAL; - ep_ctx->ep_info2 = cpu_to_le32(endpoint_type); - type = usb_endpoint_type(&ep->desc); + ring_type = usb_endpoint_type(&ep->desc); /* Set up the endpoint ring */ virt_dev->eps[ep_index].new_ring = - xhci_ring_alloc(xhci, 2, 1, type, mem_flags); + xhci_ring_alloc(xhci, 2, 1, ring_type, mem_flags); if (!virt_dev->eps[ep_index].new_ring) { /* Attempt to use the ring cache */ if (virt_dev->num_rings_cached == 0) @@ -1430,80 +1446,52 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, virt_dev->ring_cache[virt_dev->num_rings_cached]; virt_dev->ring_cache[virt_dev->num_rings_cached] = NULL; xhci_reinit_cached_ring(xhci, virt_dev->eps[ep_index].new_ring, - 1, type); + 1, ring_type); } virt_dev->eps[ep_index].skip = false; ep_ring = virt_dev->eps[ep_index].new_ring; - ep_ctx->deq = cpu_to_le64(ep_ring->first_seg->dma | ep_ring->cycle_state); - ep_ctx->ep_info = cpu_to_le32(xhci_get_endpoint_interval(udev, ep) - | EP_MULT(xhci_get_endpoint_mult(udev, ep))); + /* + * Get values to fill the endpoint context, mostly from ep descriptor. + * The average TRB buffer lengt for bulk endpoints is unclear as we + * have no clue on scatter gather list entry size. For Isoc and Int, + * set it to max available. See xHCI 1.1 spec 4.14.1.1 for details. + */ + max_esit_payload = xhci_get_max_esit_payload(udev, ep); + interval = xhci_get_endpoint_interval(udev, ep); + mult = xhci_get_endpoint_mult(udev, ep); + max_packet = GET_MAX_PACKET(usb_endpoint_maxp(&ep->desc)); + max_burst = xhci_get_endpoint_max_burst(udev, ep); + avg_trb_len = max_esit_payload; /* FIXME dig Mult and streams info out of ep companion desc */ - /* Allow 3 retries for everything but isoc; - * CErr shall be set to 0 for Isoch endpoints. - */ + /* Allow 3 retries for everything but isoc, set CErr = 3 */ if (!usb_endpoint_xfer_isoc(&ep->desc)) - ep_ctx->ep_info2 |= cpu_to_le32(ERROR_COUNT(3)); - else - ep_ctx->ep_info2 |= cpu_to_le32(ERROR_COUNT(0)); - - /* Set the max packet size and max burst */ - max_packet = GET_MAX_PACKET(usb_endpoint_maxp(&ep->desc)); - max_burst = 0; - switch (udev->speed) { - case USB_SPEED_SUPER: - /* dig out max burst from ep companion desc */ - max_burst = ep->ss_ep_comp.bMaxBurst; - break; - case USB_SPEED_HIGH: - /* Some devices get this wrong */ - if (usb_endpoint_xfer_bulk(&ep->desc)) - max_packet = 512; - /* bits 11:12 specify the number of additional transaction - * opportunities per microframe (USB 2.0, section 9.6.6) - */ - if (usb_endpoint_xfer_isoc(&ep->desc) || - usb_endpoint_xfer_int(&ep->desc)) { - max_burst = (usb_endpoint_maxp(&ep->desc) - & 0x1800) >> 11; - } - break; - case USB_SPEED_FULL: - case USB_SPEED_LOW: - break; - default: - BUG(); - } - ep_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(max_packet) | - MAX_BURST(max_burst)); - max_esit_payload = xhci_get_max_esit_payload(udev, ep); - ep_ctx->tx_info = cpu_to_le32(MAX_ESIT_PAYLOAD_FOR_EP(max_esit_payload)); - - /* - * XXX no idea how to calculate the average TRB buffer length for bulk - * endpoints, as the driver gives us no clue how big each scatter gather - * list entry (or buffer) is going to be. - * - * For isochronous and interrupt endpoints, we set it to the max - * available, until we have new API in the USB core to allow drivers to - * declare how much bandwidth they actually need. - * - * Normally, it would be calculated by taking the total of the buffer - * lengths in the TD and then dividing by the number of TRBs in a TD, - * including link TRBs, No-op TRBs, and Event data TRBs. Since we don't - * use Event Data TRBs, and we don't chain in a link TRB on short - * transfers, we're basically dividing by 1. - * - * xHCI 1.0 and 1.1 specification indicates that the Average TRB Length - * should be set to 8 for control endpoints. - */ + err_count = 3; + /* Some devices get this wrong */ + if (usb_endpoint_xfer_bulk(&ep->desc) && udev->speed == USB_SPEED_HIGH) + max_packet = 512; + /* xHCI 1.0 and 1.1 indicates that ctrl ep avg TRB Length should be 8 */ if (usb_endpoint_xfer_control(&ep->desc) && xhci->hci_version >= 0x100) - ep_ctx->tx_info |= cpu_to_le32(AVG_TRB_LENGTH_FOR_EP(8)); - else - ep_ctx->tx_info |= - cpu_to_le32(AVG_TRB_LENGTH_FOR_EP(max_esit_payload)); + avg_trb_len = 8; + /* xhci 1.1 with LEC support doesn't use mult field, use RsvdZ */ + if ((xhci->hci_version > 0x100) && HCC2_LEC(xhci->hcc_params2)) + mult = 0; + + /* Fill the endpoint context */ + ep_ctx->ep_info = cpu_to_le32(EP_MAX_ESIT_PAYLOAD_HI(max_esit_payload) | + EP_INTERVAL(interval) | + EP_MULT(mult)); + ep_ctx->ep_info2 = cpu_to_le32(EP_TYPE(endpoint_type) | + MAX_PACKET(max_packet) | + MAX_BURST(max_burst) | + ERROR_COUNT(err_count)); + ep_ctx->deq = cpu_to_le64(ep_ring->first_seg->dma | + ep_ring->cycle_state); + + ep_ctx->tx_info = cpu_to_le32(EP_MAX_ESIT_PAYLOAD_LO(max_esit_payload) | + EP_AVG_TRB_LENGTH(avg_trb_len)); /* FIXME Debug endpoint context */ return 0; diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c index 9532f5a..79959f1 100644 --- a/drivers/usb/host/xhci-mtk.c +++ b/drivers/usb/host/xhci-mtk.c @@ -695,7 +695,6 @@ static int xhci_mtk_remove(struct platform_device *dev) return 0; } -#ifdef CONFIG_PM_SLEEP /* * if ip sleep fails, and all clocks are disabled, access register will hang * AHB bus, so stop polling roothubs to avoid regs access on bus suspend. @@ -703,7 +702,7 @@ static int xhci_mtk_remove(struct platform_device *dev) * to wake up system immediately after system suspend complete if ip sleep * fails, it is what we wanted. */ -static int xhci_mtk_suspend(struct device *dev) +static int __maybe_unused xhci_mtk_suspend(struct device *dev) { struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev); struct usb_hcd *hcd = mtk->hcd; @@ -722,7 +721,7 @@ static int xhci_mtk_suspend(struct device *dev) return 0; } -static int xhci_mtk_resume(struct device *dev) +static int __maybe_unused xhci_mtk_resume(struct device *dev) { struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev); struct usb_hcd *hcd = mtk->hcd; @@ -744,10 +743,7 @@ static int xhci_mtk_resume(struct device *dev) static const struct dev_pm_ops xhci_mtk_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(xhci_mtk_suspend, xhci_mtk_resume) }; -#define DEV_PM_OPS (&xhci_mtk_pm_ops) -#else -#define DEV_PM_OPS NULL -#endif /* CONFIG_PM */ +#define DEV_PM_OPS IS_ENABLED(CONFIG_PM) ? &xhci_mtk_pm_ops : NULL #ifdef CONFIG_OF static const struct of_device_id mtk_xhci_of_match[] = { diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index d39d6bf..5c15e9b 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -110,7 +110,13 @@ static const struct of_device_id usb_xhci_of_match[] = { .compatible = "renesas,xhci-r8a7795", .data = &xhci_plat_renesas_rcar_gen3, }, { + .compatible = "renesas,rcar-gen2-xhci", + .data = &xhci_plat_renesas_rcar_gen2, + }, { + .compatible = "renesas,rcar-gen3-xhci", + .data = &xhci_plat_renesas_rcar_gen3, }, + {}, }; MODULE_DEVICE_TABLE(of, usb_xhci_of_match); #endif diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 3915657..7cf6621 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -3558,12 +3558,11 @@ static int count_isoc_trbs_needed(struct xhci_hcd *xhci, * zero. Only xHCI 1.0 host controllers support this field. */ static unsigned int xhci_get_burst_count(struct xhci_hcd *xhci, - struct usb_device *udev, struct urb *urb, unsigned int total_packet_count) { unsigned int max_burst; - if (xhci->hci_version < 0x100 || udev->speed != USB_SPEED_SUPER) + if (xhci->hci_version < 0x100 || urb->dev->speed < USB_SPEED_SUPER) return 0; max_burst = urb->ep->ss_ep_comp.bMaxBurst; @@ -3579,7 +3578,6 @@ static unsigned int xhci_get_burst_count(struct xhci_hcd *xhci, * contain 1 to (bMaxBurst + 1) packets. */ static unsigned int xhci_get_last_burst_packet_count(struct xhci_hcd *xhci, - struct usb_device *udev, struct urb *urb, unsigned int total_packet_count) { unsigned int max_burst; @@ -3588,8 +3586,7 @@ static unsigned int xhci_get_last_burst_packet_count(struct xhci_hcd *xhci, if (xhci->hci_version < 0x100) return 0; - switch (udev->speed) { - case USB_SPEED_SUPER: + if (urb->dev->speed >= USB_SPEED_SUPER) { /* bMaxBurst is zero based: 0 means 1 packet per burst */ max_burst = urb->ep->ss_ep_comp.bMaxBurst; residue = total_packet_count % (max_burst + 1); @@ -3599,11 +3596,10 @@ static unsigned int xhci_get_last_burst_packet_count(struct xhci_hcd *xhci, if (residue == 0) return max_burst; return residue - 1; - default: - if (total_packet_count == 0) - return 0; - return total_packet_count - 1; } + if (total_packet_count == 0) + return 0; + return total_packet_count - 1; } /* @@ -3714,6 +3710,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, int i, j; bool more_trbs_coming; struct xhci_virt_ep *xep; + int frame_id; xep = &xhci->devs[slot_id]->eps[ep_index]; ep_ring = xhci->devs[slot_id]->eps[ep_index].ring; @@ -3723,33 +3720,31 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, xhci_dbg(xhci, "Isoc URB with zero packets?\n"); return -EINVAL; } - start_addr = (u64) urb->transfer_dma; start_trb = &ep_ring->enqueue->generic; start_cycle = ep_ring->cycle_state; urb_priv = urb->hcpriv; - /* Queue the first TRB, even if it's zero-length */ + /* Queue the TRBs for each TD, even if they are zero-length */ for (i = 0; i < num_tds; i++) { - unsigned int total_packet_count; - unsigned int burst_count; - unsigned int residue; + unsigned int total_pkt_count, max_pkt; + unsigned int burst_count, last_burst_pkt_count; + u32 sia_frame_id; first_trb = true; running_total = 0; addr = start_addr + urb->iso_frame_desc[i].offset; td_len = urb->iso_frame_desc[i].length; td_remain_len = td_len; - total_packet_count = DIV_ROUND_UP(td_len, - GET_MAX_PACKET( - usb_endpoint_maxp(&urb->ep->desc))); + max_pkt = GET_MAX_PACKET(usb_endpoint_maxp(&urb->ep->desc)); + total_pkt_count = DIV_ROUND_UP(td_len, max_pkt); + /* A zero-length transfer still involves at least one packet. */ - if (total_packet_count == 0) - total_packet_count++; - burst_count = xhci_get_burst_count(xhci, urb->dev, urb, - total_packet_count); - residue = xhci_get_last_burst_packet_count(xhci, - urb->dev, urb, total_packet_count); + if (total_pkt_count == 0) + total_pkt_count++; + burst_count = xhci_get_burst_count(xhci, urb, total_pkt_count); + last_burst_pkt_count = xhci_get_last_burst_packet_count(xhci, + urb, total_pkt_count); trbs_per_td = count_isoc_trbs_needed(xhci, urb, i); @@ -3760,68 +3755,57 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, return ret; goto cleanup; } - td = urb_priv->td[i]; + + /* use SIA as default, if frame id is used overwrite it */ + sia_frame_id = TRB_SIA; + if (!(urb->transfer_flags & URB_ISO_ASAP) && + HCC_CFC(xhci->hcc_params)) { + frame_id = xhci_get_isoc_frame_id(xhci, urb, i); + if (frame_id >= 0) + sia_frame_id = TRB_FRAME_ID(frame_id); + } + /* + * Set isoc specific data for the first TRB in a TD. + * Prevent HW from getting the TRBs by keeping the cycle state + * inverted in the first TDs isoc TRB. + */ + field = TRB_TYPE(TRB_ISOC) | + TRB_TLBPC(last_burst_pkt_count) | + sia_frame_id | + (i ? ep_ring->cycle_state : !start_cycle); + + /* xhci 1.1 with ETE uses TD_Size field for TBC, old is Rsvdz */ + if (!xep->use_extended_tbc) + field |= TRB_TBC(burst_count); + + /* fill the rest of the TRB fields, and remaining normal TRBs */ for (j = 0; j < trbs_per_td; j++) { - int frame_id = 0; u32 remainder = 0; - field = 0; - - if (first_trb) { - field = TRB_TBC(burst_count) | - TRB_TLBPC(residue); - /* Queue the isoc TRB */ - field |= TRB_TYPE(TRB_ISOC); - - /* Calculate Frame ID and SIA fields */ - if (!(urb->transfer_flags & URB_ISO_ASAP) && - HCC_CFC(xhci->hcc_params)) { - frame_id = xhci_get_isoc_frame_id(xhci, - urb, - i); - if (frame_id >= 0) - field |= TRB_FRAME_ID(frame_id); - else - field |= TRB_SIA; - } else - field |= TRB_SIA; - - if (i == 0) { - if (start_cycle == 0) - field |= 0x1; - } else - field |= ep_ring->cycle_state; - first_trb = false; - } else { - /* Queue other normal TRBs */ - field |= TRB_TYPE(TRB_NORMAL); - field |= ep_ring->cycle_state; - } + + /* only first TRB is isoc, overwrite otherwise */ + if (!first_trb) + field = TRB_TYPE(TRB_NORMAL) | + ep_ring->cycle_state; /* Only set interrupt on short packet for IN EPs */ if (usb_urb_dir_in(urb)) field |= TRB_ISP; - /* Chain all the TRBs together; clear the chain bit in - * the last TRB to indicate it's the last TRB in the - * chain. - */ + /* Set the chain bit for all except the last TRB */ if (j < trbs_per_td - 1) { - field |= TRB_CHAIN; more_trbs_coming = true; + field |= TRB_CHAIN; } else { + more_trbs_coming = false; td->last_trb = ep_ring->enqueue; field |= TRB_IOC; - if (xhci->hci_version == 0x100 && - !(xhci->quirks & - XHCI_AVOID_BEI)) { - /* Set BEI bit except for the last td */ - if (i < num_tds - 1) - field |= TRB_BEI; - } - more_trbs_coming = false; + /* set BEI, except for the last TD */ + if (xhci->hci_version >= 0x100 && + !(xhci->quirks & XHCI_AVOID_BEI) && + i < num_tds - 1) + field |= TRB_BEI; } - /* Calculate TRB length */ trb_buff_len = TRB_MAX_BUFF_SIZE - (addr & ((1 << TRB_MAX_BUFF_SHIFT) - 1)); @@ -3834,9 +3818,15 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, urb, trbs_per_td - j - 1); length_field = TRB_LEN(trb_buff_len) | - TRB_TD_SIZE(remainder) | TRB_INTR_TARGET(0); + /* xhci 1.1 with ETE uses TD Size field for TBC */ + if (first_trb && xep->use_extended_tbc) + length_field |= TRB_TD_SIZE_TBC(burst_count); + else + length_field |= TRB_TD_SIZE(remainder); + first_trb = false; + queue_trb(xhci, ep_ring, more_trbs_coming, lower_32_bits(addr), upper_32_bits(addr), diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 0c8087d..d51ee0c 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -2086,6 +2086,7 @@ static unsigned int xhci_get_block_size(struct usb_device *udev) case USB_SPEED_HIGH: return HS_BLOCK; case USB_SPEED_SUPER: + case USB_SPEED_SUPER_PLUS: return SS_BLOCK; case USB_SPEED_UNKNOWN: case USB_SPEED_WIRELESS: @@ -2211,7 +2212,7 @@ static int xhci_check_bw_table(struct xhci_hcd *xhci, unsigned int packets_remaining = 0; unsigned int i; - if (virt_dev->udev->speed == USB_SPEED_SUPER) + if (virt_dev->udev->speed >= USB_SPEED_SUPER) return xhci_check_ss_bw(xhci, virt_dev); if (virt_dev->udev->speed == USB_SPEED_HIGH) { @@ -2412,7 +2413,7 @@ void xhci_drop_ep_from_interval_table(struct xhci_hcd *xhci, if (xhci_is_async_ep(ep_bw->type)) return; - if (udev->speed == USB_SPEED_SUPER) { + if (udev->speed >= USB_SPEED_SUPER) { if (xhci_is_sync_in_ep(ep_bw->type)) xhci->devs[udev->slot_id]->bw_table->ss_bw_in -= xhci_get_ss_bw_consumed(ep_bw); @@ -2450,6 +2451,7 @@ void xhci_drop_ep_from_interval_table(struct xhci_hcd *xhci, interval_bw->overhead[HS_OVERHEAD_TYPE] -= 1; break; case USB_SPEED_SUPER: + case USB_SPEED_SUPER_PLUS: case USB_SPEED_UNKNOWN: case USB_SPEED_WIRELESS: /* Should never happen because only LS/FS/HS endpoints will get @@ -2509,6 +2511,7 @@ static void xhci_add_ep_to_interval_table(struct xhci_hcd *xhci, interval_bw->overhead[HS_OVERHEAD_TYPE] += 1; break; case USB_SPEED_SUPER: + case USB_SPEED_SUPER_PLUS: case USB_SPEED_UNKNOWN: case USB_SPEED_WIRELESS: /* Should never happen because only LS/FS/HS endpoints will get @@ -4897,6 +4900,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) if (xhci->sbrn == 0x31) { xhci_info(xhci, "Host supports USB 3.1 Enhanced SuperSpeed\n"); hcd->speed = HCD_USB31; + hcd->self.root_hub->speed = USB_SPEED_SUPER_PLUS; } /* xHCI private pointer was set in xhci_pci_probe for the second * registered roothub. diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index cc65138..e293e09 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -232,7 +232,9 @@ struct xhci_op_regs { * disabled, or powered-off state. */ #define CMD_PM_INDEX (1 << 11) -/* bits 12:31 are reserved (and should be preserved on writes). */ +/* bit 14 Extended TBC Enable, changes Isoc TRB fields to support larger TBC */ +#define CMD_ETE (1 << 14) +/* bits 15:31 are reserved (and should be preserved on writes). */ /* IMAN - Interrupt Management Register */ #define IMAN_IE (1 << 1) @@ -343,6 +345,7 @@ struct xhci_op_regs { #define SLOT_SPEED_LS (XDEV_LS << 10) #define SLOT_SPEED_HS (XDEV_HS << 10) #define SLOT_SPEED_SS (XDEV_SS << 10) +#define SLOT_SPEED_SSP (XDEV_SSP << 10) /* Port Indicator Control */ #define PORT_LED_OFF (0 << 14) #define PORT_LED_AMBER (1 << 14) @@ -748,8 +751,9 @@ struct xhci_ep_ctx { #define GET_MAX_PACKET(p) ((p) & 0x7ff) /* tx_info bitmasks */ -#define AVG_TRB_LENGTH_FOR_EP(p) ((p) & 0xffff) -#define MAX_ESIT_PAYLOAD_FOR_EP(p) (((p) & 0xffff) << 16) +#define EP_AVG_TRB_LENGTH(p) ((p) & 0xffff) +#define EP_MAX_ESIT_PAYLOAD_LO(p) (((p) & 0xffff) << 16) +#define EP_MAX_ESIT_PAYLOAD_HI(p) ((((p) >> 16) & 0xff) << 24) #define CTX_TO_MAX_ESIT_PAYLOAD(p) (((p) >> 16) & 0xffff) /* deq bitmasks */ @@ -941,6 +945,8 @@ struct xhci_virt_ep { struct list_head bw_endpoint_list; /* Isoch Frame ID checking storage */ int next_frame_id; + /* Use new Isoch TRB layout needed for extended TBC support */ + bool use_extended_tbc; }; enum xhci_overhead_type { @@ -1182,9 +1188,12 @@ enum xhci_setup_dev { #define TRB_LEN(p) ((p) & 0x1ffff) /* TD Size, packets remaining in this TD, bits 21:17 (5 bits, so max 31) */ #define TRB_TD_SIZE(p) (min((p), (u32)31) << 17) +/* xhci 1.1 uses the TD_SIZE field for TBC if Extended TBC is enabled (ETE) */ +#define TRB_TD_SIZE_TBC(p) (min((p), (u32)31) << 17) /* Interrupter Target - which MSI-X vector to target the completion event at */ #define TRB_INTR_TARGET(p) (((p) & 0x3ff) << 22) #define GET_INTR_TARGET(p) (((p) >> 22) & 0x3ff) +/* Total burst count field, Rsvdz on xhci 1.1 with Extended TBC enabled (ETE) */ #define TRB_TBC(p) (((p) & 0x3) << 7) #define TRB_TLBPC(p) (((p) & 0xf) << 16) diff --git a/drivers/usb/misc/chaoskey.c b/drivers/usb/misc/chaoskey.c index 23c7948..76350e4 100644 --- a/drivers/usb/misc/chaoskey.c +++ b/drivers/usb/misc/chaoskey.c @@ -73,6 +73,8 @@ static const struct usb_device_id chaoskey_table[] = { }; MODULE_DEVICE_TABLE(usb, chaoskey_table); +static void chaos_read_callback(struct urb *urb); + /* Driver-local specific stuff */ struct chaoskey { struct usb_interface *interface; @@ -80,7 +82,8 @@ struct chaoskey { struct mutex lock; struct mutex rng_lock; int open; /* open count */ - int present; /* device not disconnected */ + bool present; /* device not disconnected */ + bool reading; /* ongoing IO */ int size; /* size of buf */ int valid; /* bytes of buf read */ int used; /* bytes of buf consumed */ @@ -88,15 +91,19 @@ struct chaoskey { struct hwrng hwrng; /* Embedded struct for hwrng */ int hwrng_registered; /* registered with hwrng API */ wait_queue_head_t wait_q; /* for timeouts */ + struct urb *urb; /* for performing IO */ char *buf; }; static void chaoskey_free(struct chaoskey *dev) { - usb_dbg(dev->interface, "free"); - kfree(dev->name); - kfree(dev->buf); - kfree(dev); + if (dev) { + usb_dbg(dev->interface, "free"); + usb_free_urb(dev->urb); + kfree(dev->name); + kfree(dev->buf); + kfree(dev); + } } static int chaoskey_probe(struct usb_interface *interface, @@ -107,7 +114,7 @@ static int chaoskey_probe(struct usb_interface *interface, int i; int in_ep = -1; struct chaoskey *dev; - int result; + int result = -ENOMEM; int size; usb_dbg(interface, "probe %s-%s", udev->product, udev->serial); @@ -142,14 +149,25 @@ static int chaoskey_probe(struct usb_interface *interface, dev = kzalloc(sizeof(struct chaoskey), GFP_KERNEL); if (dev == NULL) - return -ENOMEM; + goto out; dev->buf = kmalloc(size, GFP_KERNEL); - if (dev->buf == NULL) { - kfree(dev); - return -ENOMEM; - } + if (dev->buf == NULL) + goto out; + + dev->urb = usb_alloc_urb(0, GFP_KERNEL); + + if (!dev->urb) + goto out; + + usb_fill_bulk_urb(dev->urb, + udev, + usb_rcvbulkpipe(udev, in_ep), + dev->buf, + size, + chaos_read_callback, + dev); /* Construct a name using the product and serial values. Each * device needs a unique name for the hwrng code @@ -158,11 +176,8 @@ static int chaoskey_probe(struct usb_interface *interface, if (udev->product && udev->serial) { dev->name = kmalloc(strlen(udev->product) + 1 + strlen(udev->serial) + 1, GFP_KERNEL); - if (dev->name == NULL) { - kfree(dev->buf); - kfree(dev); - return -ENOMEM; - } + if (dev->name == NULL) + goto out; strcpy(dev->name, udev->product); strcat(dev->name, "-"); @@ -186,9 +201,7 @@ static int chaoskey_probe(struct usb_interface *interface, result = usb_register_dev(interface, &chaoskey_class); if (result) { usb_err(interface, "Unable to allocate minor number."); - usb_set_intfdata(interface, NULL); - chaoskey_free(dev); - return result; + goto out; } dev->hwrng.name = dev->name ? dev->name : chaoskey_driver.name; @@ -215,6 +228,11 @@ static int chaoskey_probe(struct usb_interface *interface, usb_dbg(interface, "chaoskey probe success, size %d", dev->size); return 0; + +out: + usb_set_intfdata(interface, NULL); + chaoskey_free(dev); + return result; } static void chaoskey_disconnect(struct usb_interface *interface) @@ -237,6 +255,7 @@ static void chaoskey_disconnect(struct usb_interface *interface) mutex_lock(&dev->lock); dev->present = 0; + usb_poison_urb(dev->urb); if (!dev->open) { mutex_unlock(&dev->lock); @@ -311,14 +330,33 @@ static int chaoskey_release(struct inode *inode, struct file *file) return 0; } +static void chaos_read_callback(struct urb *urb) +{ + struct chaoskey *dev = urb->context; + int status = urb->status; + + usb_dbg(dev->interface, "callback status (%d)", status); + + if (status == 0) + dev->valid = urb->actual_length; + else + dev->valid = 0; + + dev->used = 0; + + /* must be seen first before validity is announced */ + smp_wmb(); + + dev->reading = false; + wake_up(&dev->wait_q); +} + /* Fill the buffer. Called with dev->lock held */ static int _chaoskey_fill(struct chaoskey *dev) { DEFINE_WAIT(wait); int result; - int this_read; - struct usb_device *udev = interface_to_usbdev(dev->interface); usb_dbg(dev->interface, "fill"); @@ -343,21 +381,31 @@ static int _chaoskey_fill(struct chaoskey *dev) return result; } - result = usb_bulk_msg(udev, - usb_rcvbulkpipe(udev, dev->in_ep), - dev->buf, dev->size, &this_read, - NAK_TIMEOUT); + dev->reading = true; + result = usb_submit_urb(dev->urb, GFP_KERNEL); + if (result < 0) { + result = usb_translate_errors(result); + dev->reading = false; + goto out; + } + + result = wait_event_interruptible_timeout( + dev->wait_q, + !dev->reading, + NAK_TIMEOUT); + + if (result < 0) + goto out; + if (result == 0) + result = -ETIMEDOUT; + else + result = dev->valid; +out: /* Let the device go back to sleep eventually */ usb_autopm_put_interface(dev->interface); - if (result == 0) { - dev->valid = this_read; - dev->used = 0; - } - - usb_dbg(dev->interface, "bulk_msg result %d this_read %d", - result, this_read); + usb_dbg(dev->interface, "read %d bytes", dev->valid); return result; } @@ -395,13 +443,7 @@ static ssize_t chaoskey_read(struct file *file, goto bail; if (dev->valid == dev->used) { result = _chaoskey_fill(dev); - if (result) { - mutex_unlock(&dev->lock); - goto bail; - } - - /* Read returned zero bytes */ - if (dev->used == dev->valid) { + if (result < 0) { mutex_unlock(&dev->lock); goto bail; } @@ -435,6 +477,8 @@ bail: return read_count; } usb_dbg(dev->interface, "empty read, result %d", result); + if (result == -ETIMEDOUT) + result = -EAGAIN; return result; } diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c index 4e38683c..5105397 100644 --- a/drivers/usb/misc/idmouse.c +++ b/drivers/usb/misc/idmouse.c @@ -257,9 +257,9 @@ static int idmouse_open(struct inode *inode, struct file *file) if (result) goto error; result = idmouse_create_image (dev); + usb_autopm_put_interface(interface); if (result) goto error; - usb_autopm_put_interface(interface); /* increment our usage count for the driver */ ++dev->open; diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c index 8efbaba..a22de52 100644 --- a/drivers/usb/misc/sisusbvga/sisusb.c +++ b/drivers/usb/misc/sisusbvga/sisusb.c @@ -61,8 +61,8 @@ /* Forward declarations / clean-up routines */ #ifdef INCL_SISUSB_CON -static int sisusb_first_vc = 0; -static int sisusb_last_vc = 0; +static int sisusb_first_vc; +static int sisusb_last_vc; module_param_named(first, sisusb_first_vc, int, 0); module_param_named(last, sisusb_last_vc, int, 0); MODULE_PARM_DESC(first, "Number of first console to take over (1 - MAX_NR_CONSOLES)"); @@ -71,25 +71,19 @@ MODULE_PARM_DESC(last, "Number of last console to take over (1 - MAX_NR_CONSOLES static struct usb_driver sisusb_driver; -static void -sisusb_free_buffers(struct sisusb_usb_data *sisusb) +static void sisusb_free_buffers(struct sisusb_usb_data *sisusb) { int i; for (i = 0; i < NUMOBUFS; i++) { - if (sisusb->obuf[i]) { - kfree(sisusb->obuf[i]); - sisusb->obuf[i] = NULL; - } - } - if (sisusb->ibuf) { - kfree(sisusb->ibuf); - sisusb->ibuf = NULL; + kfree(sisusb->obuf[i]); + sisusb->obuf[i] = NULL; } + kfree(sisusb->ibuf); + sisusb->ibuf = NULL; } -static void -sisusb_free_urbs(struct sisusb_usb_data *sisusb) +static void sisusb_free_urbs(struct sisusb_usb_data *sisusb) { int i; @@ -108,8 +102,7 @@ sisusb_free_urbs(struct sisusb_usb_data *sisusb) /* out-urb management */ /* Return 1 if all free, 0 otherwise */ -static int -sisusb_all_free(struct sisusb_usb_data *sisusb) +static int sisusb_all_free(struct sisusb_usb_data *sisusb) { int i; @@ -124,8 +117,7 @@ sisusb_all_free(struct sisusb_usb_data *sisusb) } /* Kill all busy URBs */ -static void -sisusb_kill_all_busy(struct sisusb_usb_data *sisusb) +static void sisusb_kill_all_busy(struct sisusb_usb_data *sisusb) { int i; @@ -141,20 +133,17 @@ sisusb_kill_all_busy(struct sisusb_usb_data *sisusb) } /* Return 1 if ok, 0 if error (not all complete within timeout) */ -static int -sisusb_wait_all_out_complete(struct sisusb_usb_data *sisusb) +static int sisusb_wait_all_out_complete(struct sisusb_usb_data *sisusb) { int timeout = 5 * HZ, i = 1; - wait_event_timeout(sisusb->wait_q, - (i = sisusb_all_free(sisusb)), - timeout); + wait_event_timeout(sisusb->wait_q, (i = sisusb_all_free(sisusb)), + timeout); return i; } -static int -sisusb_outurb_available(struct sisusb_usb_data *sisusb) +static int sisusb_outurb_available(struct sisusb_usb_data *sisusb) { int i; @@ -168,20 +157,17 @@ sisusb_outurb_available(struct sisusb_usb_data *sisusb) return -1; } -static int -sisusb_get_free_outbuf(struct sisusb_usb_data *sisusb) +static int sisusb_get_free_outbuf(struct sisusb_usb_data *sisusb) { int i, timeout = 5 * HZ; wait_event_timeout(sisusb->wait_q, - ((i = sisusb_outurb_available(sisusb)) >= 0), - timeout); + ((i = sisusb_outurb_available(sisusb)) >= 0), timeout); return i; } -static int -sisusb_alloc_outbuf(struct sisusb_usb_data *sisusb) +static int sisusb_alloc_outbuf(struct sisusb_usb_data *sisusb) { int i; @@ -193,8 +179,7 @@ sisusb_alloc_outbuf(struct sisusb_usb_data *sisusb) return i; } -static void -sisusb_free_outbuf(struct sisusb_usb_data *sisusb, int index) +static void sisusb_free_outbuf(struct sisusb_usb_data *sisusb, int index) { if ((index >= 0) && (index < sisusb->numobufs)) sisusb->urbstatus[index] &= ~SU_URB_ALLOC; @@ -202,8 +187,7 @@ sisusb_free_outbuf(struct sisusb_usb_data *sisusb, int index) /* completion callback */ -static void -sisusb_bulk_completeout(struct urb *urb) +static void sisusb_bulk_completeout(struct urb *urb) { struct sisusb_urb_context *context = urb->context; struct sisusb_usb_data *sisusb; @@ -225,9 +209,9 @@ sisusb_bulk_completeout(struct urb *urb) wake_up(&sisusb->wait_q); } -static int -sisusb_bulkout_msg(struct sisusb_usb_data *sisusb, int index, unsigned int pipe, void *data, - int len, int *actual_length, int timeout, unsigned int tflags) +static int sisusb_bulkout_msg(struct sisusb_usb_data *sisusb, int index, + unsigned int pipe, void *data, int len, int *actual_length, + int timeout, unsigned int tflags) { struct urb *urb = sisusb->sisurbout[index]; int retval, byteswritten = 0; @@ -236,14 +220,15 @@ sisusb_bulkout_msg(struct sisusb_usb_data *sisusb, int index, unsigned int pipe, urb->transfer_flags = 0; usb_fill_bulk_urb(urb, sisusb->sisusb_dev, pipe, data, len, - sisusb_bulk_completeout, &sisusb->urbout_context[index]); + sisusb_bulk_completeout, + &sisusb->urbout_context[index]); urb->transfer_flags |= tflags; urb->actual_length = 0; /* Set up context */ sisusb->urbout_context[index].actual_length = (timeout) ? - NULL : actual_length; + NULL : actual_length; /* Declare this urb/buffer in use */ sisusb->urbstatus[index] |= SU_URB_BUSY; @@ -254,8 +239,8 @@ sisusb_bulkout_msg(struct sisusb_usb_data *sisusb, int index, unsigned int pipe, /* If OK, and if timeout > 0, wait for completion */ if ((retval == 0) && timeout) { wait_event_timeout(sisusb->wait_q, - (!(sisusb->urbstatus[index] & SU_URB_BUSY)), - timeout); + (!(sisusb->urbstatus[index] & SU_URB_BUSY)), + timeout); if (sisusb->urbstatus[index] & SU_URB_BUSY) { /* URB timed out... kill it and report error */ usb_kill_urb(urb); @@ -277,8 +262,7 @@ sisusb_bulkout_msg(struct sisusb_usb_data *sisusb, int index, unsigned int pipe, /* completion callback */ -static void -sisusb_bulk_completein(struct urb *urb) +static void sisusb_bulk_completein(struct urb *urb) { struct sisusb_usb_data *sisusb = urb->context; @@ -289,9 +273,9 @@ sisusb_bulk_completein(struct urb *urb) wake_up(&sisusb->wait_q); } -static int -sisusb_bulkin_msg(struct sisusb_usb_data *sisusb, unsigned int pipe, void *data, - int len, int *actual_length, int timeout, unsigned int tflags) +static int sisusb_bulkin_msg(struct sisusb_usb_data *sisusb, + unsigned int pipe, void *data, int len, + int *actual_length, int timeout, unsigned int tflags) { struct urb *urb = sisusb->sisurbin; int retval, readbytes = 0; @@ -375,7 +359,7 @@ static int sisusb_send_bulk_msg(struct sisusb_usb_data *sisusb, int ep, int len, do { passsize = thispass = (sisusb->obufsize < count) ? - sisusb->obufsize : count; + sisusb->obufsize : count; if (index < 0) index = sisusb_get_free_outbuf(sisusb); @@ -405,14 +389,9 @@ static int sisusb_send_bulk_msg(struct sisusb_usb_data *sisusb, int ep, int len, if (!sisusb->sisusb_dev) return -ENODEV; - result = sisusb_bulkout_msg(sisusb, - index, - pipe, - buffer, - thispass, - &transferred_len, - async ? 0 : 5 * HZ, - tflags); + result = sisusb_bulkout_msg(sisusb, index, pipe, + buffer, thispass, &transferred_len, + async ? 0 : 5 * HZ, tflags); if (result == -ETIMEDOUT) { @@ -500,13 +479,8 @@ static int sisusb_recv_bulk_msg(struct sisusb_usb_data *sisusb, int ep, int len, thispass = (bufsize < count) ? bufsize : count; - result = sisusb_bulkin_msg(sisusb, - pipe, - buffer, - thispass, - &transferred_len, - 5 * HZ, - tflags); + result = sisusb_bulkin_msg(sisusb, pipe, buffer, thispass, + &transferred_len, 5 * HZ, tflags); if (transferred_len) thispass = transferred_len; @@ -549,7 +523,7 @@ static int sisusb_recv_bulk_msg(struct sisusb_usb_data *sisusb, int ep, int len, } static int sisusb_send_packet(struct sisusb_usb_data *sisusb, int len, - struct sisusb_packet *packet) + struct sisusb_packet *packet) { int ret; ssize_t bytes_transferred = 0; @@ -585,8 +559,7 @@ static int sisusb_send_packet(struct sisusb_usb_data *sisusb, int len, } static int sisusb_send_bridge_packet(struct sisusb_usb_data *sisusb, int len, - struct sisusb_packet *packet, - unsigned int tflags) + struct sisusb_packet *packet, unsigned int tflags) { int ret; ssize_t bytes_transferred = 0; @@ -634,7 +607,7 @@ static int sisusb_send_bridge_packet(struct sisusb_usb_data *sisusb, int len, */ static int sisusb_write_memio_byte(struct sisusb_usb_data *sisusb, int type, - u32 addr, u8 data) + u32 addr, u8 data) { struct sisusb_packet packet; int ret; @@ -647,7 +620,7 @@ static int sisusb_write_memio_byte(struct sisusb_usb_data *sisusb, int type, } static int sisusb_write_memio_word(struct sisusb_usb_data *sisusb, int type, - u32 addr, u16 data) + u32 addr, u16 data) { struct sisusb_packet packet; int ret = 0; @@ -655,36 +628,36 @@ static int sisusb_write_memio_word(struct sisusb_usb_data *sisusb, int type, packet.address = addr & ~3; switch (addr & 3) { - case 0: - packet.header = (type << 6) | 0x0003; - packet.data = (u32)data; - ret = sisusb_send_packet(sisusb, 10, &packet); - break; - case 1: - packet.header = (type << 6) | 0x0006; - packet.data = (u32)data << 8; - ret = sisusb_send_packet(sisusb, 10, &packet); - break; - case 2: - packet.header = (type << 6) | 0x000c; - packet.data = (u32)data << 16; - ret = sisusb_send_packet(sisusb, 10, &packet); - break; - case 3: - packet.header = (type << 6) | 0x0008; - packet.data = (u32)data << 24; - ret = sisusb_send_packet(sisusb, 10, &packet); - packet.header = (type << 6) | 0x0001; - packet.address = (addr & ~3) + 4; - packet.data = (u32)data >> 8; - ret |= sisusb_send_packet(sisusb, 10, &packet); + case 0: + packet.header = (type << 6) | 0x0003; + packet.data = (u32)data; + ret = sisusb_send_packet(sisusb, 10, &packet); + break; + case 1: + packet.header = (type << 6) | 0x0006; + packet.data = (u32)data << 8; + ret = sisusb_send_packet(sisusb, 10, &packet); + break; + case 2: + packet.header = (type << 6) | 0x000c; + packet.data = (u32)data << 16; + ret = sisusb_send_packet(sisusb, 10, &packet); + break; + case 3: + packet.header = (type << 6) | 0x0008; + packet.data = (u32)data << 24; + ret = sisusb_send_packet(sisusb, 10, &packet); + packet.header = (type << 6) | 0x0001; + packet.address = (addr & ~3) + 4; + packet.data = (u32)data >> 8; + ret |= sisusb_send_packet(sisusb, 10, &packet); } return ret; } static int sisusb_write_memio_24bit(struct sisusb_usb_data *sisusb, int type, - u32 addr, u32 data) + u32 addr, u32 data) { struct sisusb_packet packet; int ret = 0; @@ -692,40 +665,40 @@ static int sisusb_write_memio_24bit(struct sisusb_usb_data *sisusb, int type, packet.address = addr & ~3; switch (addr & 3) { - case 0: - packet.header = (type << 6) | 0x0007; - packet.data = data & 0x00ffffff; - ret = sisusb_send_packet(sisusb, 10, &packet); - break; - case 1: - packet.header = (type << 6) | 0x000e; - packet.data = data << 8; - ret = sisusb_send_packet(sisusb, 10, &packet); - break; - case 2: - packet.header = (type << 6) | 0x000c; - packet.data = data << 16; - ret = sisusb_send_packet(sisusb, 10, &packet); - packet.header = (type << 6) | 0x0001; - packet.address = (addr & ~3) + 4; - packet.data = (data >> 16) & 0x00ff; - ret |= sisusb_send_packet(sisusb, 10, &packet); - break; - case 3: - packet.header = (type << 6) | 0x0008; - packet.data = data << 24; - ret = sisusb_send_packet(sisusb, 10, &packet); - packet.header = (type << 6) | 0x0003; - packet.address = (addr & ~3) + 4; - packet.data = (data >> 8) & 0xffff; - ret |= sisusb_send_packet(sisusb, 10, &packet); + case 0: + packet.header = (type << 6) | 0x0007; + packet.data = data & 0x00ffffff; + ret = sisusb_send_packet(sisusb, 10, &packet); + break; + case 1: + packet.header = (type << 6) | 0x000e; + packet.data = data << 8; + ret = sisusb_send_packet(sisusb, 10, &packet); + break; + case 2: + packet.header = (type << 6) | 0x000c; + packet.data = data << 16; + ret = sisusb_send_packet(sisusb, 10, &packet); + packet.header = (type << 6) | 0x0001; + packet.address = (addr & ~3) + 4; + packet.data = (data >> 16) & 0x00ff; + ret |= sisusb_send_packet(sisusb, 10, &packet); + break; + case 3: + packet.header = (type << 6) | 0x0008; + packet.data = data << 24; + ret = sisusb_send_packet(sisusb, 10, &packet); + packet.header = (type << 6) | 0x0003; + packet.address = (addr & ~3) + 4; + packet.data = (data >> 8) & 0xffff; + ret |= sisusb_send_packet(sisusb, 10, &packet); } return ret; } static int sisusb_write_memio_long(struct sisusb_usb_data *sisusb, int type, - u32 addr, u32 data) + u32 addr, u32 data) { struct sisusb_packet packet; int ret = 0; @@ -733,37 +706,37 @@ static int sisusb_write_memio_long(struct sisusb_usb_data *sisusb, int type, packet.address = addr & ~3; switch (addr & 3) { - case 0: - packet.header = (type << 6) | 0x000f; - packet.data = data; - ret = sisusb_send_packet(sisusb, 10, &packet); - break; - case 1: - packet.header = (type << 6) | 0x000e; - packet.data = data << 8; - ret = sisusb_send_packet(sisusb, 10, &packet); - packet.header = (type << 6) | 0x0001; - packet.address = (addr & ~3) + 4; - packet.data = data >> 24; - ret |= sisusb_send_packet(sisusb, 10, &packet); - break; - case 2: - packet.header = (type << 6) | 0x000c; - packet.data = data << 16; - ret = sisusb_send_packet(sisusb, 10, &packet); - packet.header = (type << 6) | 0x0003; - packet.address = (addr & ~3) + 4; - packet.data = data >> 16; - ret |= sisusb_send_packet(sisusb, 10, &packet); - break; - case 3: - packet.header = (type << 6) | 0x0008; - packet.data = data << 24; - ret = sisusb_send_packet(sisusb, 10, &packet); - packet.header = (type << 6) | 0x0007; - packet.address = (addr & ~3) + 4; - packet.data = data >> 8; - ret |= sisusb_send_packet(sisusb, 10, &packet); + case 0: + packet.header = (type << 6) | 0x000f; + packet.data = data; + ret = sisusb_send_packet(sisusb, 10, &packet); + break; + case 1: + packet.header = (type << 6) | 0x000e; + packet.data = data << 8; + ret = sisusb_send_packet(sisusb, 10, &packet); + packet.header = (type << 6) | 0x0001; + packet.address = (addr & ~3) + 4; + packet.data = data >> 24; + ret |= sisusb_send_packet(sisusb, 10, &packet); + break; + case 2: + packet.header = (type << 6) | 0x000c; + packet.data = data << 16; + ret = sisusb_send_packet(sisusb, 10, &packet); + packet.header = (type << 6) | 0x0003; + packet.address = (addr & ~3) + 4; + packet.data = data >> 16; + ret |= sisusb_send_packet(sisusb, 10, &packet); + break; + case 3: + packet.header = (type << 6) | 0x0008; + packet.data = data << 24; + ret = sisusb_send_packet(sisusb, 10, &packet); + packet.header = (type << 6) | 0x0007; + packet.address = (addr & ~3) + 4; + packet.data = data >> 8; + ret |= sisusb_send_packet(sisusb, 10, &packet); } return ret; @@ -780,13 +753,12 @@ static int sisusb_write_memio_long(struct sisusb_usb_data *sisusb, int type, */ static int sisusb_write_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, - char *kernbuffer, int length, - const char __user *userbuffer, int index, - ssize_t *bytes_written) + char *kernbuffer, int length, const char __user *userbuffer, + int index, ssize_t *bytes_written) { struct sisusb_packet packet; int ret = 0; - static int msgcount = 0; + static int msgcount; u8 swap8, fromkern = kernbuffer ? 1 : 0; u16 swap16; u32 swap32, flag = (length >> 28) & 1; @@ -803,9 +775,7 @@ static int sisusb_write_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, length &= 0x00ffffff; while (length) { - - switch (length) { - + switch (length) { case 1: if (userbuffer) { if (get_user(swap8, (u8 __user *)userbuffer)) @@ -813,9 +783,8 @@ static int sisusb_write_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, } else swap8 = kernbuffer[0]; - ret = sisusb_write_memio_byte(sisusb, - SISUSB_TYPE_MEM, - addr, swap8); + ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_MEM, + addr, swap8); if (!ret) (*bytes_written)++; @@ -829,10 +798,8 @@ static int sisusb_write_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, } else swap16 = *((u16 *)kernbuffer); - ret = sisusb_write_memio_word(sisusb, - SISUSB_TYPE_MEM, - addr, - swap16); + ret = sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM, + addr, swap16); if (!ret) (*bytes_written) += 2; @@ -863,10 +830,8 @@ static int sisusb_write_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, kernbuffer[0]; #endif - ret = sisusb_write_memio_24bit(sisusb, - SISUSB_TYPE_MEM, - addr, - swap32); + ret = sisusb_write_memio_24bit(sisusb, SISUSB_TYPE_MEM, + addr, swap32); if (!ret) (*bytes_written) += 3; @@ -880,10 +845,8 @@ static int sisusb_write_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, } else swap32 = *((u32 *)kernbuffer); - ret = sisusb_write_memio_long(sisusb, - SISUSB_TYPE_MEM, - addr, - swap32); + ret = sisusb_write_memio_long(sisusb, SISUSB_TYPE_MEM, + addr, swap32); if (!ret) (*bytes_written) += 4; @@ -892,103 +855,106 @@ static int sisusb_write_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, default: if ((length & ~3) > 0x10000) { - packet.header = 0x001f; - packet.address = 0x000001d4; - packet.data = addr; - ret = sisusb_send_bridge_packet(sisusb, 10, - &packet, 0); - packet.header = 0x001f; - packet.address = 0x000001d0; - packet.data = (length & ~3); - ret |= sisusb_send_bridge_packet(sisusb, 10, - &packet, 0); - packet.header = 0x001f; - packet.address = 0x000001c0; - packet.data = flag | 0x16; - ret |= sisusb_send_bridge_packet(sisusb, 10, - &packet, 0); - if (userbuffer) { - ret |= sisusb_send_bulk_msg(sisusb, + packet.header = 0x001f; + packet.address = 0x000001d4; + packet.data = addr; + ret = sisusb_send_bridge_packet(sisusb, 10, + &packet, 0); + packet.header = 0x001f; + packet.address = 0x000001d0; + packet.data = (length & ~3); + ret |= sisusb_send_bridge_packet(sisusb, 10, + &packet, 0); + packet.header = 0x001f; + packet.address = 0x000001c0; + packet.data = flag | 0x16; + ret |= sisusb_send_bridge_packet(sisusb, 10, + &packet, 0); + if (userbuffer) { + ret |= sisusb_send_bulk_msg(sisusb, SISUSB_EP_GFX_LBULK_OUT, (length & ~3), NULL, userbuffer, 0, bytes_written, 0, 1); - userbuffer += (*bytes_written); - } else if (fromkern) { - ret |= sisusb_send_bulk_msg(sisusb, + userbuffer += (*bytes_written); + } else if (fromkern) { + ret |= sisusb_send_bulk_msg(sisusb, SISUSB_EP_GFX_LBULK_OUT, (length & ~3), kernbuffer, NULL, 0, bytes_written, 0, 1); - kernbuffer += (*bytes_written); - } else { - ret |= sisusb_send_bulk_msg(sisusb, + kernbuffer += (*bytes_written); + } else { + ret |= sisusb_send_bulk_msg(sisusb, SISUSB_EP_GFX_LBULK_OUT, (length & ~3), NULL, NULL, index, bytes_written, 0, 1); - kernbuffer += ((*bytes_written) & - (sisusb->obufsize-1)); - } + kernbuffer += ((*bytes_written) & + (sisusb->obufsize-1)); + } } else { - packet.header = 0x001f; - packet.address = 0x00000194; - packet.data = addr; - ret = sisusb_send_bridge_packet(sisusb, 10, - &packet, 0); - packet.header = 0x001f; - packet.address = 0x00000190; - packet.data = (length & ~3); - ret |= sisusb_send_bridge_packet(sisusb, 10, - &packet, 0); - if (sisusb->flagb0 != 0x16) { packet.header = 0x001f; - packet.address = 0x00000180; - packet.data = flag | 0x16; + packet.address = 0x00000194; + packet.data = addr; + ret = sisusb_send_bridge_packet(sisusb, 10, + &packet, 0); + packet.header = 0x001f; + packet.address = 0x00000190; + packet.data = (length & ~3); ret |= sisusb_send_bridge_packet(sisusb, 10, - &packet, 0); - sisusb->flagb0 = 0x16; - } - if (userbuffer) { - ret |= sisusb_send_bulk_msg(sisusb, + &packet, 0); + if (sisusb->flagb0 != 0x16) { + packet.header = 0x001f; + packet.address = 0x00000180; + packet.data = flag | 0x16; + ret |= sisusb_send_bridge_packet(sisusb, + 10, &packet, 0); + sisusb->flagb0 = 0x16; + } + if (userbuffer) { + ret |= sisusb_send_bulk_msg(sisusb, SISUSB_EP_GFX_BULK_OUT, (length & ~3), NULL, userbuffer, 0, bytes_written, 0, 1); - userbuffer += (*bytes_written); - } else if (fromkern) { - ret |= sisusb_send_bulk_msg(sisusb, + userbuffer += (*bytes_written); + } else if (fromkern) { + ret |= sisusb_send_bulk_msg(sisusb, SISUSB_EP_GFX_BULK_OUT, (length & ~3), kernbuffer, NULL, 0, bytes_written, 0, 1); - kernbuffer += (*bytes_written); - } else { - ret |= sisusb_send_bulk_msg(sisusb, + kernbuffer += (*bytes_written); + } else { + ret |= sisusb_send_bulk_msg(sisusb, SISUSB_EP_GFX_BULK_OUT, (length & ~3), NULL, NULL, index, bytes_written, 0, 1); - kernbuffer += ((*bytes_written) & - (sisusb->obufsize-1)); - } + kernbuffer += ((*bytes_written) & + (sisusb->obufsize-1)); + } } if (ret) { msgcount++; if (msgcount < 500) - dev_err(&sisusb->sisusb_dev->dev, "Wrote %zd of %d bytes, error %d\n", - *bytes_written, length, ret); + dev_err(&sisusb->sisusb_dev->dev, + "Wrote %zd of %d bytes, error %d\n", + *bytes_written, length, + ret); else if (msgcount == 500) - dev_err(&sisusb->sisusb_dev->dev, "Too many errors, logging stopped\n"); + dev_err(&sisusb->sisusb_dev->dev, + "Too many errors, logging stopped\n"); } addr += (*bytes_written); length -= (*bytes_written); - } + } - if (ret) - break; + if (ret) + break; } @@ -1000,7 +966,7 @@ static int sisusb_write_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, */ static int sisusb_read_memio_byte(struct sisusb_usb_data *sisusb, int type, - u32 addr, u8 *data) + u32 addr, u8 *data) { struct sisusb_packet packet; int ret; @@ -1014,7 +980,7 @@ static int sisusb_read_memio_byte(struct sisusb_usb_data *sisusb, int type, } static int sisusb_read_memio_word(struct sisusb_usb_data *sisusb, int type, - u32 addr, u16 *data) + u32 addr, u16 *data) { struct sisusb_packet packet; int ret = 0; @@ -1024,36 +990,36 @@ static int sisusb_read_memio_word(struct sisusb_usb_data *sisusb, int type, packet.address = addr & ~3; switch (addr & 3) { - case 0: - packet.header = (type << 6) | 0x0003; - ret = sisusb_send_packet(sisusb, 6, &packet); - *data = (u16)(packet.data); - break; - case 1: - packet.header = (type << 6) | 0x0006; - ret = sisusb_send_packet(sisusb, 6, &packet); - *data = (u16)(packet.data >> 8); - break; - case 2: - packet.header = (type << 6) | 0x000c; - ret = sisusb_send_packet(sisusb, 6, &packet); - *data = (u16)(packet.data >> 16); - break; - case 3: - packet.header = (type << 6) | 0x0008; - ret = sisusb_send_packet(sisusb, 6, &packet); - *data = (u16)(packet.data >> 24); - packet.header = (type << 6) | 0x0001; - packet.address = (addr & ~3) + 4; - ret |= sisusb_send_packet(sisusb, 6, &packet); - *data |= (u16)(packet.data << 8); + case 0: + packet.header = (type << 6) | 0x0003; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = (u16)(packet.data); + break; + case 1: + packet.header = (type << 6) | 0x0006; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = (u16)(packet.data >> 8); + break; + case 2: + packet.header = (type << 6) | 0x000c; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = (u16)(packet.data >> 16); + break; + case 3: + packet.header = (type << 6) | 0x0008; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = (u16)(packet.data >> 24); + packet.header = (type << 6) | 0x0001; + packet.address = (addr & ~3) + 4; + ret |= sisusb_send_packet(sisusb, 6, &packet); + *data |= (u16)(packet.data << 8); } return ret; } static int sisusb_read_memio_24bit(struct sisusb_usb_data *sisusb, int type, - u32 addr, u32 *data) + u32 addr, u32 *data) { struct sisusb_packet packet; int ret = 0; @@ -1061,40 +1027,40 @@ static int sisusb_read_memio_24bit(struct sisusb_usb_data *sisusb, int type, packet.address = addr & ~3; switch (addr & 3) { - case 0: - packet.header = (type << 6) | 0x0007; - ret = sisusb_send_packet(sisusb, 6, &packet); - *data = packet.data & 0x00ffffff; - break; - case 1: - packet.header = (type << 6) | 0x000e; - ret = sisusb_send_packet(sisusb, 6, &packet); - *data = packet.data >> 8; - break; - case 2: - packet.header = (type << 6) | 0x000c; - ret = sisusb_send_packet(sisusb, 6, &packet); - *data = packet.data >> 16; - packet.header = (type << 6) | 0x0001; - packet.address = (addr & ~3) + 4; - ret |= sisusb_send_packet(sisusb, 6, &packet); - *data |= ((packet.data & 0xff) << 16); - break; - case 3: - packet.header = (type << 6) | 0x0008; - ret = sisusb_send_packet(sisusb, 6, &packet); - *data = packet.data >> 24; - packet.header = (type << 6) | 0x0003; - packet.address = (addr & ~3) + 4; - ret |= sisusb_send_packet(sisusb, 6, &packet); - *data |= ((packet.data & 0xffff) << 8); + case 0: + packet.header = (type << 6) | 0x0007; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = packet.data & 0x00ffffff; + break; + case 1: + packet.header = (type << 6) | 0x000e; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = packet.data >> 8; + break; + case 2: + packet.header = (type << 6) | 0x000c; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = packet.data >> 16; + packet.header = (type << 6) | 0x0001; + packet.address = (addr & ~3) + 4; + ret |= sisusb_send_packet(sisusb, 6, &packet); + *data |= ((packet.data & 0xff) << 16); + break; + case 3: + packet.header = (type << 6) | 0x0008; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = packet.data >> 24; + packet.header = (type << 6) | 0x0003; + packet.address = (addr & ~3) + 4; + ret |= sisusb_send_packet(sisusb, 6, &packet); + *data |= ((packet.data & 0xffff) << 8); } return ret; } static int sisusb_read_memio_long(struct sisusb_usb_data *sisusb, int type, - u32 addr, u32 *data) + u32 addr, u32 *data) { struct sisusb_packet packet; int ret = 0; @@ -1102,45 +1068,45 @@ static int sisusb_read_memio_long(struct sisusb_usb_data *sisusb, int type, packet.address = addr & ~3; switch (addr & 3) { - case 0: - packet.header = (type << 6) | 0x000f; - ret = sisusb_send_packet(sisusb, 6, &packet); - *data = packet.data; - break; - case 1: - packet.header = (type << 6) | 0x000e; - ret = sisusb_send_packet(sisusb, 6, &packet); - *data = packet.data >> 8; - packet.header = (type << 6) | 0x0001; - packet.address = (addr & ~3) + 4; - ret |= sisusb_send_packet(sisusb, 6, &packet); - *data |= (packet.data << 24); - break; - case 2: - packet.header = (type << 6) | 0x000c; - ret = sisusb_send_packet(sisusb, 6, &packet); - *data = packet.data >> 16; - packet.header = (type << 6) | 0x0003; - packet.address = (addr & ~3) + 4; - ret |= sisusb_send_packet(sisusb, 6, &packet); - *data |= (packet.data << 16); - break; - case 3: - packet.header = (type << 6) | 0x0008; - ret = sisusb_send_packet(sisusb, 6, &packet); - *data = packet.data >> 24; - packet.header = (type << 6) | 0x0007; - packet.address = (addr & ~3) + 4; - ret |= sisusb_send_packet(sisusb, 6, &packet); - *data |= (packet.data << 8); + case 0: + packet.header = (type << 6) | 0x000f; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = packet.data; + break; + case 1: + packet.header = (type << 6) | 0x000e; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = packet.data >> 8; + packet.header = (type << 6) | 0x0001; + packet.address = (addr & ~3) + 4; + ret |= sisusb_send_packet(sisusb, 6, &packet); + *data |= (packet.data << 24); + break; + case 2: + packet.header = (type << 6) | 0x000c; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = packet.data >> 16; + packet.header = (type << 6) | 0x0003; + packet.address = (addr & ~3) + 4; + ret |= sisusb_send_packet(sisusb, 6, &packet); + *data |= (packet.data << 16); + break; + case 3: + packet.header = (type << 6) | 0x0008; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = packet.data >> 24; + packet.header = (type << 6) | 0x0007; + packet.address = (addr & ~3) + 4; + ret |= sisusb_send_packet(sisusb, 6, &packet); + *data |= (packet.data << 8); } return ret; } static int sisusb_read_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, - char *kernbuffer, int length, - char __user *userbuffer, ssize_t *bytes_read) + char *kernbuffer, int length, char __user *userbuffer, + ssize_t *bytes_read) { int ret = 0; char buf[4]; @@ -1152,34 +1118,27 @@ static int sisusb_read_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, length &= 0x00ffffff; while (length) { - - switch (length) { - + switch (length) { case 1: - ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM, - addr, &buf[0]); + addr, &buf[0]); if (!ret) { (*bytes_read)++; if (userbuffer) { - if (put_user(buf[0], - (u8 __user *)userbuffer)) { + if (put_user(buf[0], (u8 __user *)userbuffer)) return -EFAULT; - } - } else { + } else kernbuffer[0] = buf[0]; - } } return ret; case 2: ret |= sisusb_read_memio_word(sisusb, SISUSB_TYPE_MEM, - addr, &swap16); + addr, &swap16); if (!ret) { (*bytes_read) += 2; if (userbuffer) { - if (put_user(swap16, - (u16 __user *)userbuffer)) + if (put_user(swap16, (u16 __user *)userbuffer)) return -EFAULT; } else { *((u16 *)kernbuffer) = swap16; @@ -1189,7 +1148,7 @@ static int sisusb_read_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, case 3: ret |= sisusb_read_memio_24bit(sisusb, SISUSB_TYPE_MEM, - addr, &swap32); + addr, &swap32); if (!ret) { (*bytes_read) += 3; #ifdef __BIG_ENDIAN @@ -1202,7 +1161,8 @@ static int sisusb_read_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, buf[0] = swap32 & 0xff; #endif if (userbuffer) { - if (copy_to_user(userbuffer, &buf[0], 3)) + if (copy_to_user(userbuffer, + &buf[0], 3)) return -EFAULT; } else { kernbuffer[0] = buf[0]; @@ -1214,12 +1174,11 @@ static int sisusb_read_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, default: ret |= sisusb_read_memio_long(sisusb, SISUSB_TYPE_MEM, - addr, &swap32); + addr, &swap32); if (!ret) { (*bytes_read) += 4; if (userbuffer) { - if (put_user(swap32, - (u32 __user *)userbuffer)) + if (put_user(swap32, (u32 __user *)userbuffer)) return -EFAULT; userbuffer += 4; @@ -1230,10 +1189,9 @@ static int sisusb_read_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, addr += 4; length -= 4; } - } - - if (ret) - break; + } + if (ret) + break; } return ret; @@ -1242,40 +1200,39 @@ static int sisusb_read_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, /* High level: Gfx (indexed) register access */ #ifdef INCL_SISUSB_CON -int -sisusb_setreg(struct sisusb_usb_data *sisusb, int port, u8 data) +int sisusb_setreg(struct sisusb_usb_data *sisusb, int port, u8 data) { return sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, data); } -int -sisusb_getreg(struct sisusb_usb_data *sisusb, int port, u8 *data) +int sisusb_getreg(struct sisusb_usb_data *sisusb, int port, u8 *data) { return sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port, data); } #endif -int -sisusb_setidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 data) +int sisusb_setidxreg(struct sisusb_usb_data *sisusb, int port, + u8 index, u8 data) { int ret; + ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, index); ret |= sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, data); return ret; } -int -sisusb_getidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 *data) +int sisusb_getidxreg(struct sisusb_usb_data *sisusb, int port, + u8 index, u8 *data) { int ret; + ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, index); ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, data); return ret; } -int -sisusb_setidxregandor(struct sisusb_usb_data *sisusb, int port, u8 idx, - u8 myand, u8 myor) +int sisusb_setidxregandor(struct sisusb_usb_data *sisusb, int port, u8 idx, + u8 myand, u8 myor) { int ret; u8 tmp; @@ -1288,12 +1245,12 @@ sisusb_setidxregandor(struct sisusb_usb_data *sisusb, int port, u8 idx, return ret; } -static int -sisusb_setidxregmask(struct sisusb_usb_data *sisusb, int port, u8 idx, - u8 data, u8 mask) +static int sisusb_setidxregmask(struct sisusb_usb_data *sisusb, + int port, u8 idx, u8 data, u8 mask) { int ret; u8 tmp; + ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, idx); ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, &tmp); tmp &= ~(mask); @@ -1302,75 +1259,76 @@ sisusb_setidxregmask(struct sisusb_usb_data *sisusb, int port, u8 idx, return ret; } -int -sisusb_setidxregor(struct sisusb_usb_data *sisusb, int port, u8 index, u8 myor) +int sisusb_setidxregor(struct sisusb_usb_data *sisusb, int port, + u8 index, u8 myor) { - return(sisusb_setidxregandor(sisusb, port, index, 0xff, myor)); + return sisusb_setidxregandor(sisusb, port, index, 0xff, myor); } -int -sisusb_setidxregand(struct sisusb_usb_data *sisusb, int port, u8 idx, u8 myand) +int sisusb_setidxregand(struct sisusb_usb_data *sisusb, int port, + u8 idx, u8 myand) { - return(sisusb_setidxregandor(sisusb, port, idx, myand, 0x00)); + return sisusb_setidxregandor(sisusb, port, idx, myand, 0x00); } /* Write/read video ram */ #ifdef INCL_SISUSB_CON -int -sisusb_writeb(struct sisusb_usb_data *sisusb, u32 adr, u8 data) +int sisusb_writeb(struct sisusb_usb_data *sisusb, u32 adr, u8 data) { - return(sisusb_write_memio_byte(sisusb, SISUSB_TYPE_MEM, adr, data)); + return sisusb_write_memio_byte(sisusb, SISUSB_TYPE_MEM, adr, data); } -int -sisusb_readb(struct sisusb_usb_data *sisusb, u32 adr, u8 *data) +int sisusb_readb(struct sisusb_usb_data *sisusb, u32 adr, u8 *data) { - return(sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM, adr, data)); + return sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM, adr, data); } -int -sisusb_copy_memory(struct sisusb_usb_data *sisusb, char *src, - u32 dest, int length, size_t *bytes_written) +int sisusb_copy_memory(struct sisusb_usb_data *sisusb, char *src, + u32 dest, int length, size_t *bytes_written) { - return(sisusb_write_mem_bulk(sisusb, dest, src, length, NULL, 0, bytes_written)); + return sisusb_write_mem_bulk(sisusb, dest, src, length, + NULL, 0, bytes_written); } #ifdef SISUSBENDIANTEST -int -sisusb_read_memory(struct sisusb_usb_data *sisusb, char *dest, - u32 src, int length, size_t *bytes_written) +int sisusb_read_memory(struct sisusb_usb_data *sisusb, char *dest, + u32 src, int length, size_t *bytes_written) { - return(sisusb_read_mem_bulk(sisusb, src, dest, length, NULL, bytes_written)); + return sisusb_read_mem_bulk(sisusb, src, dest, length, + NULL, bytes_written); } #endif #endif #ifdef SISUSBENDIANTEST -static void -sisusb_testreadwrite(struct sisusb_usb_data *sisusb) -{ - static char srcbuffer[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 }; - char destbuffer[10]; - size_t dummy; - int i,j; - - sisusb_copy_memory(sisusb, srcbuffer, sisusb->vrambase, 7, &dummy); - - for(i = 1; i <= 7; i++) { - dev_dbg(&sisusb->sisusb_dev->dev, "sisusb: rwtest %d bytes\n", i); - sisusb_read_memory(sisusb, destbuffer, sisusb->vrambase, i, &dummy); - for(j = 0; j < i; j++) { - dev_dbg(&sisusb->sisusb_dev->dev, "rwtest read[%d] = %x\n", j, destbuffer[j]); +static void sisusb_testreadwrite(struct sisusb_usb_data *sisusb) +{ + static char srcbuffer[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 }; + char destbuffer[10]; + size_t dummy; + int i, j; + + sisusb_copy_memory(sisusb, srcbuffer, sisusb->vrambase, 7, &dummy); + + for (i = 1; i <= 7; i++) { + dev_dbg(&sisusb->sisusb_dev->dev, + "sisusb: rwtest %d bytes\n", i); + sisusb_read_memory(sisusb, destbuffer, sisusb->vrambase, + i, &dummy); + for (j = 0; j < i; j++) { + dev_dbg(&sisusb->sisusb_dev->dev, + "rwtest read[%d] = %x\n", + j, destbuffer[j]); + } } - } } #endif /* access pci config registers (reg numbers 0, 4, 8, etc) */ -static int -sisusb_write_pci_config(struct sisusb_usb_data *sisusb, int regnum, u32 data) +static int sisusb_write_pci_config(struct sisusb_usb_data *sisusb, + int regnum, u32 data) { struct sisusb_packet packet; int ret; @@ -1382,8 +1340,8 @@ sisusb_write_pci_config(struct sisusb_usb_data *sisusb, int regnum, u32 data) return ret; } -static int -sisusb_read_pci_config(struct sisusb_usb_data *sisusb, int regnum, u32 *data) +static int sisusb_read_pci_config(struct sisusb_usb_data *sisusb, + int regnum, u32 *data) { struct sisusb_packet packet; int ret; @@ -1397,8 +1355,8 @@ sisusb_read_pci_config(struct sisusb_usb_data *sisusb, int regnum, u32 *data) /* Clear video RAM */ -static int -sisusb_clear_vram(struct sisusb_usb_data *sisusb, u32 address, int length) +static int sisusb_clear_vram(struct sisusb_usb_data *sisusb, + u32 address, int length) { int ret, i; ssize_t j; @@ -1416,7 +1374,8 @@ sisusb_clear_vram(struct sisusb_usb_data *sisusb, u32 address, int length) return 0; /* allocate free buffer/urb and clear the buffer */ - if ((i = sisusb_alloc_outbuf(sisusb)) < 0) + i = sisusb_alloc_outbuf(sisusb); + if (i < 0) return -EBUSY; memset(sisusb->obuf[i], 0, sisusb->obufsize); @@ -1437,20 +1396,19 @@ sisusb_clear_vram(struct sisusb_usb_data *sisusb, u32 address, int length) * a defined mode (640x480@60Hz) */ -#define GETREG(r,d) sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, r, d) -#define SETREG(r,d) sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, r, d) -#define SETIREG(r,i,d) sisusb_setidxreg(sisusb, r, i, d) -#define GETIREG(r,i,d) sisusb_getidxreg(sisusb, r, i, d) -#define SETIREGOR(r,i,o) sisusb_setidxregor(sisusb, r, i, o) -#define SETIREGAND(r,i,a) sisusb_setidxregand(sisusb, r, i, a) -#define SETIREGANDOR(r,i,a,o) sisusb_setidxregandor(sisusb, r, i, a, o) -#define READL(a,d) sisusb_read_memio_long(sisusb, SISUSB_TYPE_MEM, a, d) -#define WRITEL(a,d) sisusb_write_memio_long(sisusb, SISUSB_TYPE_MEM, a, d) -#define READB(a,d) sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM, a, d) -#define WRITEB(a,d) sisusb_write_memio_byte(sisusb, SISUSB_TYPE_MEM, a, d) - -static int -sisusb_triggersr16(struct sisusb_usb_data *sisusb, u8 ramtype) +#define GETREG(r, d) sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, r, d) +#define SETREG(r, d) sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, r, d) +#define SETIREG(r, i, d) sisusb_setidxreg(sisusb, r, i, d) +#define GETIREG(r, i, d) sisusb_getidxreg(sisusb, r, i, d) +#define SETIREGOR(r, i, o) sisusb_setidxregor(sisusb, r, i, o) +#define SETIREGAND(r, i, a) sisusb_setidxregand(sisusb, r, i, a) +#define SETIREGANDOR(r, i, a, o) sisusb_setidxregandor(sisusb, r, i, a, o) +#define READL(a, d) sisusb_read_memio_long(sisusb, SISUSB_TYPE_MEM, a, d) +#define WRITEL(a, d) sisusb_write_memio_long(sisusb, SISUSB_TYPE_MEM, a, d) +#define READB(a, d) sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM, a, d) +#define WRITEB(a, d) sisusb_write_memio_byte(sisusb, SISUSB_TYPE_MEM, a, d) + +static int sisusb_triggersr16(struct sisusb_usb_data *sisusb, u8 ramtype) { int ret; u8 tmp8; @@ -1480,8 +1438,8 @@ sisusb_triggersr16(struct sisusb_usb_data *sisusb, u8 ramtype) return ret; } -static int -sisusb_getbuswidth(struct sisusb_usb_data *sisusb, int *bw, int *chab) +static int sisusb_getbuswidth(struct sisusb_usb_data *sisusb, + int *bw, int *chab) { int ret; u8 ramtype, done = 0; @@ -1526,7 +1484,7 @@ sisusb_getbuswidth(struct sisusb_usb_data *sisusb, int *bw, int *chab) } if ((t1 != 0x456789ab) || (t0 != 0x01234567)) { *chab = 1; *bw = 64; - ret |= SETIREGANDOR(SISSR, 0x14, 0xfc,0x01); + ret |= SETIREGANDOR(SISSR, 0x14, 0xfc, 0x01); ret |= sisusb_triggersr16(sisusb, ramtype); ret |= WRITEL(ramptr + 0, 0x89abcdef); @@ -1593,8 +1551,7 @@ sisusb_getbuswidth(struct sisusb_usb_data *sisusb, int *bw, int *chab) return ret; } -static int -sisusb_verify_mclk(struct sisusb_usb_data *sisusb) +static int sisusb_verify_mclk(struct sisusb_usb_data *sisusb) { int ret = 0; u32 ramptr = SISUSB_PCI_MEMBASE; @@ -1622,10 +1579,8 @@ sisusb_verify_mclk(struct sisusb_usb_data *sisusb) return ret; } -static int -sisusb_set_rank(struct sisusb_usb_data *sisusb, int *iret, int index, - u8 rankno, u8 chab, const u8 dramtype[][5], - int bw) +static int sisusb_set_rank(struct sisusb_usb_data *sisusb, int *iret, + int index, u8 rankno, u8 chab, const u8 dramtype[][5], int bw) { int ret = 0, ranksize; u8 tmp; @@ -1641,7 +1596,9 @@ sisusb_set_rank(struct sisusb_usb_data *sisusb, int *iret, int index, return ret; tmp = 0; - while ((ranksize >>= 1) > 0) tmp += 0x10; + while ((ranksize >>= 1) > 0) + tmp += 0x10; + tmp |= ((rankno - 1) << 2); tmp |= ((bw / 64) & 0x02); tmp |= (chab & 0x01); @@ -1654,8 +1611,8 @@ sisusb_set_rank(struct sisusb_usb_data *sisusb, int *iret, int index, return ret; } -static int -sisusb_check_rbc(struct sisusb_usb_data *sisusb, int *iret, u32 inc, int testn) +static int sisusb_check_rbc(struct sisusb_usb_data *sisusb, int *iret, + u32 inc, int testn) { int ret = 0, i; u32 j, tmp; @@ -1669,7 +1626,9 @@ sisusb_check_rbc(struct sisusb_usb_data *sisusb, int *iret, u32 inc, int testn) for (i = 0, j = 0; i < testn; i++) { ret |= READL(sisusb->vrambase + j, &tmp); - if (tmp != j) return ret; + if (tmp != j) + return ret; + j += inc; } @@ -1677,9 +1636,8 @@ sisusb_check_rbc(struct sisusb_usb_data *sisusb, int *iret, u32 inc, int testn) return ret; } -static int -sisusb_check_ranks(struct sisusb_usb_data *sisusb, int *iret, int rankno, - int idx, int bw, const u8 rtype[][5]) +static int sisusb_check_ranks(struct sisusb_usb_data *sisusb, + int *iret, int rankno, int idx, int bw, const u8 rtype[][5]) { int ret = 0, i, i2ret; u32 inc; @@ -1687,10 +1645,8 @@ sisusb_check_ranks(struct sisusb_usb_data *sisusb, int *iret, int rankno, *iret = 0; for (i = rankno; i >= 1; i--) { - inc = 1 << (rtype[idx][2] + - rtype[idx][1] + - rtype[idx][0] + - bw / 64 + i); + inc = 1 << (rtype[idx][2] + rtype[idx][1] + rtype[idx][0] + + bw / 64 + i); ret |= sisusb_check_rbc(sisusb, &i2ret, inc, 2); if (!i2ret) return ret; @@ -1710,9 +1666,8 @@ sisusb_check_ranks(struct sisusb_usb_data *sisusb, int *iret, int rankno, return ret; } -static int -sisusb_get_sdram_size(struct sisusb_usb_data *sisusb, int *iret, int bw, - int chab) +static int sisusb_get_sdram_size(struct sisusb_usb_data *sisusb, int *iret, + int bw, int chab) { int ret = 0, i2ret = 0, i, j; static const u8 sdramtype[13][5] = { @@ -1736,13 +1691,13 @@ sisusb_get_sdram_size(struct sisusb_usb_data *sisusb, int *iret, int bw, for (i = 0; i < 13; i++) { ret |= SETIREGANDOR(SISSR, 0x13, 0x80, sdramtype[i][4]); for (j = 2; j > 0; j--) { - ret |= sisusb_set_rank(sisusb, &i2ret, i, j, - chab, sdramtype, bw); + ret |= sisusb_set_rank(sisusb, &i2ret, i, j, chab, + sdramtype, bw); if (!i2ret) continue; - ret |= sisusb_check_ranks(sisusb, &i2ret, j, i, - bw, sdramtype); + ret |= sisusb_check_ranks(sisusb, &i2ret, j, i, bw, + sdramtype); if (i2ret) { *iret = 0; /* ram size found */ return ret; @@ -1753,8 +1708,8 @@ sisusb_get_sdram_size(struct sisusb_usb_data *sisusb, int *iret, int bw, return ret; } -static int -sisusb_setup_screen(struct sisusb_usb_data *sisusb, int clrall, int drwfr) +static int sisusb_setup_screen(struct sisusb_usb_data *sisusb, + int clrall, int drwfr) { int ret = 0; u32 address; @@ -1775,47 +1730,47 @@ sisusb_setup_screen(struct sisusb_usb_data *sisusb, int clrall, int drwfr) for (i = 0; i < modex; i++) { address = sisusb->vrambase + (i * bpp); ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM, - address, 0xf100); + address, 0xf100); address += (modex * (modey-1) * bpp); ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM, - address, 0xf100); + address, 0xf100); } for (i = 0; i < modey; i++) { address = sisusb->vrambase + ((i * modex) * bpp); ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM, - address, 0xf100); + address, 0xf100); address += ((modex - 1) * bpp); ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM, - address, 0xf100); + address, 0xf100); } } return ret; } -static int -sisusb_set_default_mode(struct sisusb_usb_data *sisusb, int touchengines) +static int sisusb_set_default_mode(struct sisusb_usb_data *sisusb, + int touchengines) { int ret = 0, i, j, modex, modey, bpp, du; u8 sr31, cr63, tmp8; static const char attrdata[] = { - 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, - 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, - 0x01,0x00,0x00,0x00 + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x01, 0x00, 0x00, 0x00 }; static const char crtcrdata[] = { - 0x5f,0x4f,0x50,0x82,0x54,0x80,0x0b,0x3e, - 0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00, - 0xea,0x8c,0xdf,0x28,0x40,0xe7,0x04,0xa3, + 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0x0b, 0x3e, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xea, 0x8c, 0xdf, 0x28, 0x40, 0xe7, 0x04, 0xa3, 0xff }; static const char grcdata[] = { - 0x00,0x00,0x00,0x00,0x00,0x40,0x05,0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0f, 0xff }; static const char crtcdata[] = { - 0x5f,0x4f,0x4f,0x83,0x55,0x81,0x0b,0x3e, - 0xe9,0x8b,0xdf,0xe8,0x0c,0x00,0x00,0x05, + 0x5f, 0x4f, 0x4f, 0x83, 0x55, 0x81, 0x0b, 0x3e, + 0xe9, 0x8b, 0xdf, 0xe8, 0x0c, 0x00, 0x00, 0x05, 0x00 }; @@ -1858,28 +1813,32 @@ sisusb_set_default_mode(struct sisusb_usb_data *sisusb, int touchengines) SETIREGAND(SISSR, 0x37, 0xfe); SETREG(SISMISCW, 0xef); /* sync */ SETIREG(SISCR, 0x11, 0x00); /* crtc */ - for (j = 0x00, i = 0; i <= 7; i++, j++) { + for (j = 0x00, i = 0; i <= 7; i++, j++) SETIREG(SISCR, j, crtcdata[i]); - } - for (j = 0x10; i <= 10; i++, j++) { + + for (j = 0x10; i <= 10; i++, j++) SETIREG(SISCR, j, crtcdata[i]); - } - for (j = 0x15; i <= 12; i++, j++) { + + for (j = 0x15; i <= 12; i++, j++) SETIREG(SISCR, j, crtcdata[i]); - } - for (j = 0x0A; i <= 15; i++, j++) { + + for (j = 0x0A; i <= 15; i++, j++) SETIREG(SISSR, j, crtcdata[i]); - } + SETIREG(SISSR, 0x0E, (crtcdata[16] & 0xE0)); SETIREGANDOR(SISCR, 0x09, 0x5f, ((crtcdata[16] & 0x01) << 5)); SETIREG(SISCR, 0x14, 0x4f); du = (modex / 16) * (bpp * 2); /* offset/pitch */ - if (modex % 16) du += bpp; + if (modex % 16) + du += bpp; + SETIREGANDOR(SISSR, 0x0e, 0xf0, ((du >> 8) & 0x0f)); SETIREG(SISCR, 0x13, (du & 0xff)); du <<= 5; tmp8 = du >> 8; - if (du & 0xff) tmp8++; + if (du & 0xff) + tmp8++; + SETIREG(SISSR, 0x10, tmp8); SETIREG(SISSR, 0x31, 0x00); /* VCLK */ SETIREG(SISSR, 0x2b, 0x1b); @@ -1925,8 +1884,7 @@ sisusb_set_default_mode(struct sisusb_usb_data *sisusb, int touchengines) return ret; } -static int -sisusb_init_gfxcore(struct sisusb_usb_data *sisusb) +static int sisusb_init_gfxcore(struct sisusb_usb_data *sisusb) { int ret = 0, i, j, bw, chab, iret, retry = 3; u8 tmp8, ramtype; @@ -1970,7 +1928,8 @@ sisusb_init_gfxcore(struct sisusb_usb_data *sisusb) ret |= GETREG(SISMISCR, &tmp8); ret |= SETREG(SISMISCW, (tmp8 | 0x01)); - if (ret) continue; + if (ret) + continue; /* Reset registers */ ret |= SETIREGAND(SISCR, 0x5b, 0xdf); @@ -1979,23 +1938,23 @@ sisusb_init_gfxcore(struct sisusb_usb_data *sisusb) ret |= SETREG(SISMISCW, 0x67); - for (i = 0x06; i <= 0x1f; i++) { + for (i = 0x06; i <= 0x1f; i++) ret |= SETIREG(SISSR, i, 0x00); - } - for (i = 0x21; i <= 0x27; i++) { + + for (i = 0x21; i <= 0x27; i++) ret |= SETIREG(SISSR, i, 0x00); - } - for (i = 0x31; i <= 0x3d; i++) { + + for (i = 0x31; i <= 0x3d; i++) ret |= SETIREG(SISSR, i, 0x00); - } - for (i = 0x12; i <= 0x1b; i++) { + + for (i = 0x12; i <= 0x1b; i++) ret |= SETIREG(SISSR, i, 0x00); - } - for (i = 0x79; i <= 0x7c; i++) { + + for (i = 0x79; i <= 0x7c; i++) ret |= SETIREG(SISCR, i, 0x00); - } - if (ret) continue; + if (ret) + continue; ret |= SETIREG(SISCR, 0x63, 0x80); @@ -2013,13 +1972,16 @@ sisusb_init_gfxcore(struct sisusb_usb_data *sisusb) ret |= SETIREG(SISSR, 0x07, 0x18); ret |= SETIREG(SISSR, 0x11, 0x0f); - if (ret) continue; + if (ret) + continue; for (i = 0x15, j = 0; i <= 0x1b; i++, j++) { - ret |= SETIREG(SISSR, i, ramtypetable1[(j*4) + ramtype]); + ret |= SETIREG(SISSR, i, + ramtypetable1[(j*4) + ramtype]); } for (i = 0x40, j = 0; i <= 0x44; i++, j++) { - ret |= SETIREG(SISCR, i, ramtypetable2[(j*4) + ramtype]); + ret |= SETIREG(SISCR, i, + ramtypetable2[(j*4) + ramtype]); } ret |= SETIREG(SISCR, 0x49, 0xaa); @@ -2036,7 +1998,8 @@ sisusb_init_gfxcore(struct sisusb_usb_data *sisusb) ret |= SETIREGAND(SISCAP, 0x3f, 0xef); - if (ret) continue; + if (ret) + continue; ret |= SETIREG(SISPART1, 0x00, 0x00); @@ -2058,7 +2021,8 @@ sisusb_init_gfxcore(struct sisusb_usb_data *sisusb) ret |= SETIREG(SISSR, 0x32, 0x11); ret |= SETIREG(SISSR, 0x33, 0x00); - if (ret) continue; + if (ret) + continue; ret |= SETIREG(SISCR, 0x83, 0x00); @@ -2080,13 +2044,15 @@ sisusb_init_gfxcore(struct sisusb_usb_data *sisusb) if (ramtype <= 1) { ret |= sisusb_get_sdram_size(sisusb, &iret, bw, chab); if (iret) { - dev_err(&sisusb->sisusb_dev->dev,"RAM size detection failed, assuming 8MB video RAM\n"); - ret |= SETIREG(SISSR,0x14,0x31); + dev_err(&sisusb->sisusb_dev->dev, + "RAM size detection failed, assuming 8MB video RAM\n"); + ret |= SETIREG(SISSR, 0x14, 0x31); /* TODO */ } } else { - dev_err(&sisusb->sisusb_dev->dev, "DDR RAM device found, assuming 8MB video RAM\n"); - ret |= SETIREG(SISSR,0x14,0x31); + dev_err(&sisusb->sisusb_dev->dev, + "DDR RAM device found, assuming 8MB video RAM\n"); + ret |= SETIREG(SISSR, 0x14, 0x31); /* *** TODO *** */ } @@ -2117,8 +2083,7 @@ sisusb_init_gfxcore(struct sisusb_usb_data *sisusb) #undef READL #undef WRITEL -static void -sisusb_get_ramconfig(struct sisusb_usb_data *sisusb) +static void sisusb_get_ramconfig(struct sisusb_usb_data *sisusb) { u8 tmp8, tmp82, ramtype; int bw = 0; @@ -2127,7 +2092,7 @@ sisusb_get_ramconfig(struct sisusb_usb_data *sisusb) static const char ram_dynamictype[4] = {'D', 'G', 'D', 'G'}; static const int busSDR[4] = {64, 64, 128, 128}; static const int busDDR[4] = {32, 32, 64, 64}; - static const int busDDRA[4] = {64+32, 64+32 , (64+32)*2, (64+32)*2}; + static const int busDDRA[4] = {64+32, 64+32, (64+32)*2, (64+32)*2}; sisusb_getidxreg(sisusb, SISSR, 0x14, &tmp8); sisusb_getidxreg(sisusb, SISSR, 0x15, &tmp82); @@ -2135,35 +2100,38 @@ sisusb_get_ramconfig(struct sisusb_usb_data *sisusb) sisusb->vramsize = (1 << ((tmp8 & 0xf0) >> 4)) * 1024 * 1024; ramtype &= 0x03; switch ((tmp8 >> 2) & 0x03) { - case 0: ramtypetext1 = "1 ch/1 r"; - if (tmp82 & 0x10) { + case 0: + ramtypetext1 = "1 ch/1 r"; + if (tmp82 & 0x10) bw = 32; - } else { + else bw = busSDR[(tmp8 & 0x03)]; - } + break; - case 1: ramtypetext1 = "1 ch/2 r"; + case 1: + ramtypetext1 = "1 ch/2 r"; sisusb->vramsize <<= 1; bw = busSDR[(tmp8 & 0x03)]; break; - case 2: ramtypetext1 = "asymmeric"; + case 2: + ramtypetext1 = "asymmeric"; sisusb->vramsize += sisusb->vramsize/2; bw = busDDRA[(tmp8 & 0x03)]; break; - case 3: ramtypetext1 = "2 channel"; + case 3: + ramtypetext1 = "2 channel"; sisusb->vramsize <<= 1; bw = busDDR[(tmp8 & 0x03)]; break; } - - dev_info(&sisusb->sisusb_dev->dev, "%dMB %s %cDR S%cRAM, bus width %d\n", - sisusb->vramsize >> 20, ramtypetext1, - ram_datarate[ramtype], ram_dynamictype[ramtype], bw); + dev_info(&sisusb->sisusb_dev->dev, + "%dMB %s %cDR S%cRAM, bus width %d\n", + sisusb->vramsize >> 20, ramtypetext1, + ram_datarate[ramtype], ram_dynamictype[ramtype], bw); } -static int -sisusb_do_init_gfxdevice(struct sisusb_usb_data *sisusb) +static int sisusb_do_init_gfxdevice(struct sisusb_usb_data *sisusb) { struct sisusb_packet packet; int ret; @@ -2241,8 +2209,7 @@ sisusb_do_init_gfxdevice(struct sisusb_usb_data *sisusb) * of the graphics board. */ -static int -sisusb_init_gfxdevice(struct sisusb_usb_data *sisusb, int initscreen) +static int sisusb_init_gfxdevice(struct sisusb_usb_data *sisusb, int initscreen) { int ret = 0, test = 0; u32 tmp32; @@ -2250,16 +2217,25 @@ sisusb_init_gfxdevice(struct sisusb_usb_data *sisusb, int initscreen) if (sisusb->devinit == 1) { /* Read PCI BARs and see if they have been set up */ ret |= sisusb_read_pci_config(sisusb, 0x10, &tmp32); - if (ret) return ret; - if ((tmp32 & 0xfffffff0) == SISUSB_PCI_MEMBASE) test++; + if (ret) + return ret; + + if ((tmp32 & 0xfffffff0) == SISUSB_PCI_MEMBASE) + test++; ret |= sisusb_read_pci_config(sisusb, 0x14, &tmp32); - if (ret) return ret; - if ((tmp32 & 0xfffffff0) == SISUSB_PCI_MMIOBASE) test++; + if (ret) + return ret; + + if ((tmp32 & 0xfffffff0) == SISUSB_PCI_MMIOBASE) + test++; ret |= sisusb_read_pci_config(sisusb, 0x18, &tmp32); - if (ret) return ret; - if ((tmp32 & 0xfffffff0) == SISUSB_PCI_IOPORTBASE) test++; + if (ret) + return ret; + + if ((tmp32 & 0xfffffff0) == SISUSB_PCI_IOPORTBASE) + test++; } /* No? So reset the device */ @@ -2289,20 +2265,20 @@ sisusb_init_gfxdevice(struct sisusb_usb_data *sisusb, int initscreen) #ifdef INCL_SISUSB_CON /* Set up default text mode: - - Set text mode (0x03) - - Upload default font - - Upload user font (if available) -*/ + * - Set text mode (0x03) + * - Upload default font + * - Upload user font (if available) + */ -int -sisusb_reset_text_mode(struct sisusb_usb_data *sisusb, int init) +int sisusb_reset_text_mode(struct sisusb_usb_data *sisusb, int init) { int ret = 0, slot = sisusb->font_slot, i; const struct font_desc *myfont; u8 *tempbuf; u16 *tempbufb; size_t written; - static const char bootstring[] = "SiSUSB VGA text console, (C) 2005 Thomas Winischhofer."; + static const char bootstring[] = + "SiSUSB VGA text console, (C) 2005 Thomas Winischhofer."; static const char bootlogo[] = "(o_ //\\ V_/_"; /* sisusb->lock is down */ @@ -2328,7 +2304,8 @@ sisusb_reset_text_mode(struct sisusb_usb_data *sisusb, int init) memcpy(tempbuf + (i * 32), myfont->data + (i * 16), 16); /* Upload default font */ - ret = sisusbcon_do_font_op(sisusb, 1, 0, tempbuf, 8192, 0, 1, NULL, 16, 0); + ret = sisusbcon_do_font_op(sisusb, 1, 0, tempbuf, 8192, + 0, 1, NULL, 16, 0); vfree(tempbuf); @@ -2366,7 +2343,7 @@ sisusb_reset_text_mode(struct sisusb_usb_data *sisusb, int init) *(tempbufb++) = 0x0700 | bootstring[i++]; ret |= sisusb_copy_memory(sisusb, tempbuf, - sisusb->vrambase, 8192, &written); + sisusb->vrambase, 8192, &written); vfree(tempbuf); @@ -2375,12 +2352,13 @@ sisusb_reset_text_mode(struct sisusb_usb_data *sisusb, int init) } else if (sisusb->scrbuf) { ret |= sisusb_copy_memory(sisusb, (char *)sisusb->scrbuf, - sisusb->vrambase, sisusb->scrbuf_size, &written); + sisusb->vrambase, sisusb->scrbuf_size, + &written); } if (sisusb->sisusb_cursor_size_from >= 0 && - sisusb->sisusb_cursor_size_to >= 0) { + sisusb->sisusb_cursor_size_to >= 0) { sisusb_setidxreg(sisusb, SISCR, 0x0a, sisusb->sisusb_cursor_size_from); sisusb_setidxregandor(sisusb, SISCR, 0x0b, 0xe0, @@ -2392,7 +2370,8 @@ sisusb_reset_text_mode(struct sisusb_usb_data *sisusb, int init) } slot = sisusb->sisusb_cursor_loc; - if(slot < 0) slot = 0; + if (slot < 0) + slot = 0; sisusb->sisusb_cursor_loc = -1; sisusb->bad_cursor_pos = 1; @@ -2413,22 +2392,19 @@ sisusb_reset_text_mode(struct sisusb_usb_data *sisusb, int init) /* fops */ -static int -sisusb_open(struct inode *inode, struct file *file) +static int sisusb_open(struct inode *inode, struct file *file) { struct sisusb_usb_data *sisusb; struct usb_interface *interface; int subminor = iminor(inode); interface = usb_find_interface(&sisusb_driver, subminor); - if (!interface) { + if (!interface) return -ENODEV; - } sisusb = usb_get_intfdata(interface); - if (!sisusb) { + if (!sisusb) return -ENODEV; - } mutex_lock(&sisusb->lock); @@ -2444,15 +2420,17 @@ sisusb_open(struct inode *inode, struct file *file) if (!sisusb->devinit) { if (sisusb->sisusb_dev->speed == USB_SPEED_HIGH || - sisusb->sisusb_dev->speed == USB_SPEED_SUPER) { + sisusb->sisusb_dev->speed == USB_SPEED_SUPER) { if (sisusb_init_gfxdevice(sisusb, 0)) { mutex_unlock(&sisusb->lock); - dev_err(&sisusb->sisusb_dev->dev, "Failed to initialize device\n"); + dev_err(&sisusb->sisusb_dev->dev, + "Failed to initialize device\n"); return -EIO; } } else { mutex_unlock(&sisusb->lock); - dev_err(&sisusb->sisusb_dev->dev, "Device not attached to USB 2.0 hub\n"); + dev_err(&sisusb->sisusb_dev->dev, + "Device not attached to USB 2.0 hub\n"); return -EIO; } } @@ -2469,8 +2447,7 @@ sisusb_open(struct inode *inode, struct file *file) return 0; } -void -sisusb_delete(struct kref *kref) +void sisusb_delete(struct kref *kref) { struct sisusb_usb_data *sisusb = to_sisusb_dev(kref); @@ -2488,8 +2465,7 @@ sisusb_delete(struct kref *kref) kfree(sisusb); } -static int -sisusb_release(struct inode *inode, struct file *file) +static int sisusb_release(struct inode *inode, struct file *file) { struct sisusb_usb_data *sisusb; @@ -2516,8 +2492,8 @@ sisusb_release(struct inode *inode, struct file *file) return 0; } -static ssize_t -sisusb_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) +static ssize_t sisusb_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) { struct sisusb_usb_data *sisusb; ssize_t bytes_read = 0; @@ -2539,11 +2515,10 @@ sisusb_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) } if ((*ppos) >= SISUSB_PCI_PSEUDO_IOPORTBASE && - (*ppos) < SISUSB_PCI_PSEUDO_IOPORTBASE + 128) { + (*ppos) < SISUSB_PCI_PSEUDO_IOPORTBASE + 128) { - address = (*ppos) - - SISUSB_PCI_PSEUDO_IOPORTBASE + - SISUSB_PCI_IOPORTBASE; + address = (*ppos) - SISUSB_PCI_PSEUDO_IOPORTBASE + + SISUSB_PCI_IOPORTBASE; /* Read i/o ports * Byte, word and long(32) can be read. As this @@ -2551,82 +2526,77 @@ sisusb_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) * in machine-endianness. */ switch (count) { + case 1: + if (sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, + address, &buf8)) + errno = -EIO; + else if (put_user(buf8, (u8 __user *)buffer)) + errno = -EFAULT; + else + bytes_read = 1; - case 1: - if (sisusb_read_memio_byte(sisusb, - SISUSB_TYPE_IO, - address, &buf8)) - errno = -EIO; - else if (put_user(buf8, (u8 __user *)buffer)) - errno = -EFAULT; - else - bytes_read = 1; - - break; + break; - case 2: - if (sisusb_read_memio_word(sisusb, - SISUSB_TYPE_IO, - address, &buf16)) - errno = -EIO; - else if (put_user(buf16, (u16 __user *)buffer)) - errno = -EFAULT; - else - bytes_read = 2; + case 2: + if (sisusb_read_memio_word(sisusb, SISUSB_TYPE_IO, + address, &buf16)) + errno = -EIO; + else if (put_user(buf16, (u16 __user *)buffer)) + errno = -EFAULT; + else + bytes_read = 2; - break; + break; - case 4: - if (sisusb_read_memio_long(sisusb, - SISUSB_TYPE_IO, - address, &buf32)) - errno = -EIO; - else if (put_user(buf32, (u32 __user *)buffer)) - errno = -EFAULT; - else - bytes_read = 4; + case 4: + if (sisusb_read_memio_long(sisusb, SISUSB_TYPE_IO, + address, &buf32)) + errno = -EIO; + else if (put_user(buf32, (u32 __user *)buffer)) + errno = -EFAULT; + else + bytes_read = 4; - break; + break; - default: - errno = -EIO; + default: + errno = -EIO; } - } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MEMBASE && - (*ppos) < SISUSB_PCI_PSEUDO_MEMBASE + sisusb->vramsize) { + } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MEMBASE && (*ppos) < + SISUSB_PCI_PSEUDO_MEMBASE + sisusb->vramsize) { - address = (*ppos) - - SISUSB_PCI_PSEUDO_MEMBASE + - SISUSB_PCI_MEMBASE; + address = (*ppos) - SISUSB_PCI_PSEUDO_MEMBASE + + SISUSB_PCI_MEMBASE; /* Read video ram * Remember: Data delivered is never endian-corrected */ errno = sisusb_read_mem_bulk(sisusb, address, - NULL, count, buffer, &bytes_read); + NULL, count, buffer, &bytes_read); if (bytes_read) errno = bytes_read; } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MMIOBASE && - (*ppos) < SISUSB_PCI_PSEUDO_MMIOBASE + SISUSB_PCI_MMIOSIZE) { + (*ppos) < SISUSB_PCI_PSEUDO_MMIOBASE + + SISUSB_PCI_MMIOSIZE) { - address = (*ppos) - - SISUSB_PCI_PSEUDO_MMIOBASE + - SISUSB_PCI_MMIOBASE; + address = (*ppos) - SISUSB_PCI_PSEUDO_MMIOBASE + + SISUSB_PCI_MMIOBASE; /* Read MMIO * Remember: Data delivered is never endian-corrected */ errno = sisusb_read_mem_bulk(sisusb, address, - NULL, count, buffer, &bytes_read); + NULL, count, buffer, &bytes_read); if (bytes_read) errno = bytes_read; } else if ((*ppos) >= SISUSB_PCI_PSEUDO_PCIBASE && - (*ppos) <= SISUSB_PCI_PSEUDO_PCIBASE + 0x5c) { + (*ppos) <= SISUSB_PCI_PSEUDO_PCIBASE + 0x5c) { if (count != 4) { mutex_unlock(&sisusb->lock); @@ -2658,9 +2628,8 @@ sisusb_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) return errno ? errno : bytes_read; } -static ssize_t -sisusb_write(struct file *file, const char __user *buffer, size_t count, - loff_t *ppos) +static ssize_t sisusb_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) { struct sisusb_usb_data *sisusb; int errno = 0; @@ -2682,11 +2651,10 @@ sisusb_write(struct file *file, const char __user *buffer, size_t count, } if ((*ppos) >= SISUSB_PCI_PSEUDO_IOPORTBASE && - (*ppos) < SISUSB_PCI_PSEUDO_IOPORTBASE + 128) { + (*ppos) < SISUSB_PCI_PSEUDO_IOPORTBASE + 128) { - address = (*ppos) - - SISUSB_PCI_PSEUDO_IOPORTBASE + - SISUSB_PCI_IOPORTBASE; + address = (*ppos) - SISUSB_PCI_PSEUDO_IOPORTBASE + + SISUSB_PCI_IOPORTBASE; /* Write i/o ports * Byte, word and long(32) can be written. As this @@ -2694,53 +2662,49 @@ sisusb_write(struct file *file, const char __user *buffer, size_t count, * in machine-endianness. */ switch (count) { + case 1: + if (get_user(buf8, (u8 __user *)buffer)) + errno = -EFAULT; + else if (sisusb_write_memio_byte(sisusb, + SISUSB_TYPE_IO, address, buf8)) + errno = -EIO; + else + bytes_written = 1; - case 1: - if (get_user(buf8, (u8 __user *)buffer)) - errno = -EFAULT; - else if (sisusb_write_memio_byte(sisusb, - SISUSB_TYPE_IO, - address, buf8)) - errno = -EIO; - else - bytes_written = 1; - - break; + break; - case 2: - if (get_user(buf16, (u16 __user *)buffer)) - errno = -EFAULT; - else if (sisusb_write_memio_word(sisusb, - SISUSB_TYPE_IO, - address, buf16)) - errno = -EIO; - else - bytes_written = 2; + case 2: + if (get_user(buf16, (u16 __user *)buffer)) + errno = -EFAULT; + else if (sisusb_write_memio_word(sisusb, + SISUSB_TYPE_IO, address, buf16)) + errno = -EIO; + else + bytes_written = 2; - break; + break; - case 4: - if (get_user(buf32, (u32 __user *)buffer)) - errno = -EFAULT; - else if (sisusb_write_memio_long(sisusb, - SISUSB_TYPE_IO, - address, buf32)) - errno = -EIO; - else - bytes_written = 4; + case 4: + if (get_user(buf32, (u32 __user *)buffer)) + errno = -EFAULT; + else if (sisusb_write_memio_long(sisusb, + SISUSB_TYPE_IO, address, buf32)) + errno = -EIO; + else + bytes_written = 4; - break; + break; - default: - errno = -EIO; + default: + errno = -EIO; } } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MEMBASE && - (*ppos) < SISUSB_PCI_PSEUDO_MEMBASE + sisusb->vramsize) { + (*ppos) < SISUSB_PCI_PSEUDO_MEMBASE + + sisusb->vramsize) { - address = (*ppos) - - SISUSB_PCI_PSEUDO_MEMBASE + - SISUSB_PCI_MEMBASE; + address = (*ppos) - SISUSB_PCI_PSEUDO_MEMBASE + + SISUSB_PCI_MEMBASE; /* Write video ram. * Buffer is copied 1:1, therefore, on big-endian @@ -2749,17 +2713,17 @@ sisusb_write(struct file *file, const char __user *buffer, size_t count, * mode or if YUV data is being transferred). */ errno = sisusb_write_mem_bulk(sisusb, address, NULL, - count, buffer, 0, &bytes_written); + count, buffer, 0, &bytes_written); if (bytes_written) errno = bytes_written; } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MMIOBASE && - (*ppos) < SISUSB_PCI_PSEUDO_MMIOBASE + SISUSB_PCI_MMIOSIZE) { + (*ppos) < SISUSB_PCI_PSEUDO_MMIOBASE + + SISUSB_PCI_MMIOSIZE) { - address = (*ppos) - - SISUSB_PCI_PSEUDO_MMIOBASE + - SISUSB_PCI_MMIOBASE; + address = (*ppos) - SISUSB_PCI_PSEUDO_MMIOBASE + + SISUSB_PCI_MMIOBASE; /* Write MMIO. * Buffer is copied 1:1, therefore, on big-endian @@ -2767,13 +2731,14 @@ sisusb_write(struct file *file, const char __user *buffer, size_t count, * in advance. */ errno = sisusb_write_mem_bulk(sisusb, address, NULL, - count, buffer, 0, &bytes_written); + count, buffer, 0, &bytes_written); if (bytes_written) errno = bytes_written; } else if ((*ppos) >= SISUSB_PCI_PSEUDO_PCIBASE && - (*ppos) <= SISUSB_PCI_PSEUDO_PCIBASE + SISUSB_PCI_PCONFSIZE) { + (*ppos) <= SISUSB_PCI_PSEUDO_PCIBASE + + SISUSB_PCI_PCONFSIZE) { if (count != 4) { mutex_unlock(&sisusb->lock); @@ -2807,8 +2772,7 @@ sisusb_write(struct file *file, const char __user *buffer, size_t count, return errno ? errno : bytes_written; } -static loff_t -sisusb_lseek(struct file *file, loff_t offset, int orig) +static loff_t sisusb_lseek(struct file *file, loff_t offset, int orig) { struct sisusb_usb_data *sisusb; loff_t ret; @@ -2831,9 +2795,8 @@ sisusb_lseek(struct file *file, loff_t offset, int orig) return ret; } -static int -sisusb_handle_command(struct sisusb_usb_data *sisusb, struct sisusb_command *y, - unsigned long arg) +static int sisusb_handle_command(struct sisusb_usb_data *sisusb, + struct sisusb_command *y, unsigned long arg) { int retval, port, length; u32 address; @@ -2849,105 +2812,99 @@ sisusb_handle_command(struct sisusb_usb_data *sisusb, struct sisusb_command *y, SISUSB_PCI_IOPORTBASE; switch (y->operation) { - case SUCMD_GET: - retval = sisusb_getidxreg(sisusb, port, - y->data0, &y->data1); - if (!retval) { - if (copy_to_user((void __user *)arg, y, - sizeof(*y))) - retval = -EFAULT; - } - break; + case SUCMD_GET: + retval = sisusb_getidxreg(sisusb, port, y->data0, &y->data1); + if (!retval) { + if (copy_to_user((void __user *)arg, y, sizeof(*y))) + retval = -EFAULT; + } + break; - case SUCMD_SET: - retval = sisusb_setidxreg(sisusb, port, - y->data0, y->data1); - break; + case SUCMD_SET: + retval = sisusb_setidxreg(sisusb, port, y->data0, y->data1); + break; - case SUCMD_SETOR: - retval = sisusb_setidxregor(sisusb, port, - y->data0, y->data1); - break; + case SUCMD_SETOR: + retval = sisusb_setidxregor(sisusb, port, y->data0, y->data1); + break; - case SUCMD_SETAND: - retval = sisusb_setidxregand(sisusb, port, - y->data0, y->data1); - break; + case SUCMD_SETAND: + retval = sisusb_setidxregand(sisusb, port, y->data0, y->data1); + break; - case SUCMD_SETANDOR: - retval = sisusb_setidxregandor(sisusb, port, - y->data0, y->data1, y->data2); - break; + case SUCMD_SETANDOR: + retval = sisusb_setidxregandor(sisusb, port, y->data0, + y->data1, y->data2); + break; - case SUCMD_SETMASK: - retval = sisusb_setidxregmask(sisusb, port, - y->data0, y->data1, y->data2); - break; + case SUCMD_SETMASK: + retval = sisusb_setidxregmask(sisusb, port, y->data0, + y->data1, y->data2); + break; - case SUCMD_CLRSCR: - /* Gfx core must be initialized */ - if (!sisusb->gfxinit) - return -ENODEV; + case SUCMD_CLRSCR: + /* Gfx core must be initialized */ + if (!sisusb->gfxinit) + return -ENODEV; - length = (y->data0 << 16) | (y->data1 << 8) | y->data2; - address = y->data3 - - SISUSB_PCI_PSEUDO_MEMBASE + + length = (y->data0 << 16) | (y->data1 << 8) | y->data2; + address = y->data3 - SISUSB_PCI_PSEUDO_MEMBASE + SISUSB_PCI_MEMBASE; - retval = sisusb_clear_vram(sisusb, address, length); - break; + retval = sisusb_clear_vram(sisusb, address, length); + break; - case SUCMD_HANDLETEXTMODE: - retval = 0; + case SUCMD_HANDLETEXTMODE: + retval = 0; #ifdef INCL_SISUSB_CON - /* Gfx core must be initialized, SiS_Pr must exist */ - if (!sisusb->gfxinit || !sisusb->SiS_Pr) - return -ENODEV; + /* Gfx core must be initialized, SiS_Pr must exist */ + if (!sisusb->gfxinit || !sisusb->SiS_Pr) + return -ENODEV; - switch (y->data0) { - case 0: - retval = sisusb_reset_text_mode(sisusb, 0); - break; - case 1: - sisusb->textmodedestroyed = 1; - break; - } -#endif + switch (y->data0) { + case 0: + retval = sisusb_reset_text_mode(sisusb, 0); + break; + case 1: + sisusb->textmodedestroyed = 1; break; + } +#endif + break; #ifdef INCL_SISUSB_CON - case SUCMD_SETMODE: - /* Gfx core must be initialized, SiS_Pr must exist */ - if (!sisusb->gfxinit || !sisusb->SiS_Pr) - return -ENODEV; + case SUCMD_SETMODE: + /* Gfx core must be initialized, SiS_Pr must exist */ + if (!sisusb->gfxinit || !sisusb->SiS_Pr) + return -ENODEV; - retval = 0; + retval = 0; - sisusb->SiS_Pr->IOAddress = SISUSB_PCI_IOPORTBASE + 0x30; - sisusb->SiS_Pr->sisusb = (void *)sisusb; + sisusb->SiS_Pr->IOAddress = SISUSB_PCI_IOPORTBASE + 0x30; + sisusb->SiS_Pr->sisusb = (void *)sisusb; - if (SiSUSBSetMode(sisusb->SiS_Pr, y->data3)) - retval = -EINVAL; + if (SiSUSBSetMode(sisusb->SiS_Pr, y->data3)) + retval = -EINVAL; - break; + break; - case SUCMD_SETVESAMODE: - /* Gfx core must be initialized, SiS_Pr must exist */ - if (!sisusb->gfxinit || !sisusb->SiS_Pr) - return -ENODEV; + case SUCMD_SETVESAMODE: + /* Gfx core must be initialized, SiS_Pr must exist */ + if (!sisusb->gfxinit || !sisusb->SiS_Pr) + return -ENODEV; - retval = 0; + retval = 0; - sisusb->SiS_Pr->IOAddress = SISUSB_PCI_IOPORTBASE + 0x30; - sisusb->SiS_Pr->sisusb = (void *)sisusb; + sisusb->SiS_Pr->IOAddress = SISUSB_PCI_IOPORTBASE + 0x30; + sisusb->SiS_Pr->sisusb = (void *)sisusb; - if (SiSUSBSetVESAMode(sisusb->SiS_Pr, y->data3)) - retval = -EINVAL; + if (SiSUSBSetVESAMode(sisusb->SiS_Pr, y->data3)) + retval = -EINVAL; - break; + break; #endif - default: - retval = -EINVAL; + default: + retval = -EINVAL; } if (retval > 0) @@ -2956,8 +2913,7 @@ sisusb_handle_command(struct sisusb_usb_data *sisusb, struct sisusb_command *y, return retval; } -static long -sisusb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +static long sisusb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct sisusb_usb_data *sisusb; struct sisusb_info x; @@ -2978,52 +2934,51 @@ sisusb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } switch (cmd) { + case SISUSB_GET_CONFIG_SIZE: - case SISUSB_GET_CONFIG_SIZE: - - if (put_user(sizeof(x), argp)) - retval = -EFAULT; + if (put_user(sizeof(x), argp)) + retval = -EFAULT; - break; + break; - case SISUSB_GET_CONFIG: - - x.sisusb_id = SISUSB_ID; - x.sisusb_version = SISUSB_VERSION; - x.sisusb_revision = SISUSB_REVISION; - x.sisusb_patchlevel = SISUSB_PATCHLEVEL; - x.sisusb_gfxinit = sisusb->gfxinit; - x.sisusb_vrambase = SISUSB_PCI_PSEUDO_MEMBASE; - x.sisusb_mmiobase = SISUSB_PCI_PSEUDO_MMIOBASE; - x.sisusb_iobase = SISUSB_PCI_PSEUDO_IOPORTBASE; - x.sisusb_pcibase = SISUSB_PCI_PSEUDO_PCIBASE; - x.sisusb_vramsize = sisusb->vramsize; - x.sisusb_minor = sisusb->minor; - x.sisusb_fbdevactive= 0; + case SISUSB_GET_CONFIG: + + x.sisusb_id = SISUSB_ID; + x.sisusb_version = SISUSB_VERSION; + x.sisusb_revision = SISUSB_REVISION; + x.sisusb_patchlevel = SISUSB_PATCHLEVEL; + x.sisusb_gfxinit = sisusb->gfxinit; + x.sisusb_vrambase = SISUSB_PCI_PSEUDO_MEMBASE; + x.sisusb_mmiobase = SISUSB_PCI_PSEUDO_MMIOBASE; + x.sisusb_iobase = SISUSB_PCI_PSEUDO_IOPORTBASE; + x.sisusb_pcibase = SISUSB_PCI_PSEUDO_PCIBASE; + x.sisusb_vramsize = sisusb->vramsize; + x.sisusb_minor = sisusb->minor; + x.sisusb_fbdevactive = 0; #ifdef INCL_SISUSB_CON - x.sisusb_conactive = sisusb->haveconsole ? 1 : 0; + x.sisusb_conactive = sisusb->haveconsole ? 1 : 0; #else - x.sisusb_conactive = 0; + x.sisusb_conactive = 0; #endif - memset(x.sisusb_reserved, 0, sizeof(x.sisusb_reserved)); + memset(x.sisusb_reserved, 0, sizeof(x.sisusb_reserved)); - if (copy_to_user((void __user *)arg, &x, sizeof(x))) - retval = -EFAULT; + if (copy_to_user((void __user *)arg, &x, sizeof(x))) + retval = -EFAULT; - break; + break; - case SISUSB_COMMAND: + case SISUSB_COMMAND: - if (copy_from_user(&y, (void __user *)arg, sizeof(y))) - retval = -EFAULT; - else - retval = sisusb_handle_command(sisusb, &y, arg); + if (copy_from_user(&y, (void __user *)arg, sizeof(y))) + retval = -EFAULT; + else + retval = sisusb_handle_command(sisusb, &y, arg); - break; + break; - default: - retval = -ENOTTY; - break; + default: + retval = -ENOTTY; + break; } err_out: @@ -3032,20 +2987,20 @@ err_out: } #ifdef SISUSB_NEW_CONFIG_COMPAT -static long -sisusb_compat_ioctl(struct file *f, unsigned int cmd, unsigned long arg) +static long sisusb_compat_ioctl(struct file *f, unsigned int cmd, + unsigned long arg) { long retval; switch (cmd) { - case SISUSB_GET_CONFIG_SIZE: - case SISUSB_GET_CONFIG: - case SISUSB_COMMAND: - retval = sisusb_ioctl(f, cmd, arg); - return retval; + case SISUSB_GET_CONFIG_SIZE: + case SISUSB_GET_CONFIG: + case SISUSB_COMMAND: + retval = sisusb_ioctl(f, cmd, arg); + return retval; - default: - return -ENOIOCTLCMD; + default: + return -ENOIOCTLCMD; } } #endif @@ -3070,21 +3025,20 @@ static struct usb_class_driver usb_sisusb_class = { }; static int sisusb_probe(struct usb_interface *intf, - const struct usb_device_id *id) + const struct usb_device_id *id) { struct usb_device *dev = interface_to_usbdev(intf); struct sisusb_usb_data *sisusb; int retval = 0, i; dev_info(&dev->dev, "USB2VGA dongle found at address %d\n", - dev->devnum); + dev->devnum); /* Allocate memory for our private */ sisusb = kzalloc(sizeof(*sisusb), GFP_KERNEL); - if (!sisusb) { - dev_err(&dev->dev, "Failed to allocate memory for private data\n"); + if (!sisusb) return -ENOMEM; - } + kref_init(&sisusb->kref); mutex_init(&(sisusb->lock)); @@ -3092,8 +3046,9 @@ static int sisusb_probe(struct usb_interface *intf, /* Register device */ retval = usb_register_dev(intf, &usb_sisusb_class); if (retval) { - dev_err(&sisusb->sisusb_dev->dev, "Failed to get a minor for device %d\n", - dev->devnum); + dev_err(&sisusb->sisusb_dev->dev, + "Failed to get a minor for device %d\n", + dev->devnum); retval = -ENODEV; goto error_1; } @@ -3108,8 +3063,8 @@ static int sisusb_probe(struct usb_interface *intf, /* Allocate buffers */ sisusb->ibufsize = SISUSB_IBUF_SIZE; - if (!(sisusb->ibuf = kmalloc(SISUSB_IBUF_SIZE, GFP_KERNEL))) { - dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate memory for input buffer"); + sisusb->ibuf = kmalloc(SISUSB_IBUF_SIZE, GFP_KERNEL); + if (!sisusb->ibuf) { retval = -ENOMEM; goto error_2; } @@ -3117,20 +3072,20 @@ static int sisusb_probe(struct usb_interface *intf, sisusb->numobufs = 0; sisusb->obufsize = SISUSB_OBUF_SIZE; for (i = 0; i < NUMOBUFS; i++) { - if (!(sisusb->obuf[i] = kmalloc(SISUSB_OBUF_SIZE, GFP_KERNEL))) { + sisusb->obuf[i] = kmalloc(SISUSB_OBUF_SIZE, GFP_KERNEL); + if (!sisusb->obuf[i]) { if (i == 0) { - dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate memory for output buffer\n"); retval = -ENOMEM; goto error_3; } break; - } else - sisusb->numobufs++; - + } + sisusb->numobufs++; } /* Allocate URBs */ - if (!(sisusb->sisurbin = usb_alloc_urb(0, GFP_KERNEL))) { + sisusb->sisurbin = usb_alloc_urb(0, GFP_KERNEL); + if (!sisusb->sisurbin) { dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate URBs\n"); retval = -ENOMEM; goto error_3; @@ -3138,8 +3093,10 @@ static int sisusb_probe(struct usb_interface *intf, sisusb->completein = 1; for (i = 0; i < sisusb->numobufs; i++) { - if (!(sisusb->sisurbout[i] = usb_alloc_urb(0, GFP_KERNEL))) { - dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate URBs\n"); + sisusb->sisurbout[i] = usb_alloc_urb(0, GFP_KERNEL); + if (!sisusb->sisurbout[i]) { + dev_err(&sisusb->sisusb_dev->dev, + "Failed to allocate URBs\n"); retval = -ENOMEM; goto error_4; } @@ -3148,12 +3105,15 @@ static int sisusb_probe(struct usb_interface *intf, sisusb->urbstatus[i] = 0; } - dev_info(&sisusb->sisusb_dev->dev, "Allocated %d output buffers\n", sisusb->numobufs); + dev_info(&sisusb->sisusb_dev->dev, "Allocated %d output buffers\n", + sisusb->numobufs); #ifdef INCL_SISUSB_CON /* Allocate our SiS_Pr */ - if (!(sisusb->SiS_Pr = kmalloc(sizeof(struct SiS_Private), GFP_KERNEL))) { - dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate SiS_Pr\n"); + sisusb->SiS_Pr = kmalloc(sizeof(struct SiS_Private), GFP_KERNEL); + if (!sisusb->SiS_Pr) { + retval = -ENOMEM; + goto error_4; } #endif @@ -3170,17 +3130,18 @@ static int sisusb_probe(struct usb_interface *intf, if (dev->speed == USB_SPEED_HIGH || dev->speed == USB_SPEED_SUPER) { int initscreen = 1; #ifdef INCL_SISUSB_CON - if (sisusb_first_vc > 0 && - sisusb_last_vc > 0 && - sisusb_first_vc <= sisusb_last_vc && - sisusb_last_vc <= MAX_NR_CONSOLES) + if (sisusb_first_vc > 0 && sisusb_last_vc > 0 && + sisusb_first_vc <= sisusb_last_vc && + sisusb_last_vc <= MAX_NR_CONSOLES) initscreen = 0; #endif if (sisusb_init_gfxdevice(sisusb, initscreen)) - dev_err(&sisusb->sisusb_dev->dev, "Failed to early initialize device\n"); + dev_err(&sisusb->sisusb_dev->dev, + "Failed to early initialize device\n"); } else - dev_info(&sisusb->sisusb_dev->dev, "Not attached to USB 2.0 hub, deferring init\n"); + dev_info(&sisusb->sisusb_dev->dev, + "Not attached to USB 2.0 hub, deferring init\n"); sisusb->ready = 1; @@ -3254,7 +3215,7 @@ static const struct usb_device_id sisusb_table[] = { { } }; -MODULE_DEVICE_TABLE (usb, sisusb_table); +MODULE_DEVICE_TABLE(usb, sisusb_table); static struct usb_driver sisusb_driver = { .name = "sisusb", diff --git a/drivers/usb/mon/mon_main.c b/drivers/usb/mon/mon_main.c index fec3f11..33ff49c 100644 --- a/drivers/usb/mon/mon_main.c +++ b/drivers/usb/mon/mon_main.c @@ -349,7 +349,7 @@ struct mon_bus *mon_bus_lookup(unsigned int num) static int __init mon_init(void) { struct usb_bus *ubus; - int rc; + int rc, id; if ((rc = mon_text_init()) != 0) goto err_text; @@ -365,12 +365,11 @@ static int __init mon_init(void) } // MOD_INC_USE_COUNT(which_module?); - mutex_lock(&usb_bus_list_lock); - list_for_each_entry (ubus, &usb_bus_list, bus_list) { + mutex_lock(&usb_bus_idr_lock); + idr_for_each_entry(&usb_bus_idr, ubus, id) mon_bus_init(ubus); - } usb_register_notify(&mon_nb); - mutex_unlock(&usb_bus_list_lock); + mutex_unlock(&usb_bus_idr_lock); return 0; err_reg: diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 45c83ba..886526b 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -7,6 +7,7 @@ config USB_MUSB_HDRC tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, AW, ...)' depends on (USB || USB_GADGET) + depends on HAS_IOMEM help Say Y here if your system has a dual role high speed USB controller based on the Mentor Graphics silicon IP. Then @@ -85,6 +86,7 @@ config USB_MUSB_DA8XX config USB_MUSB_TUSB6010 tristate "TUSB6010" + depends on HAS_IOMEM depends on ARCH_OMAP2PLUS || COMPILE_TEST depends on NOP_USB_XCEIV = USB_MUSB_HDRC # both built-in or both modules diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index c3791a0..39fd958 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1901,7 +1901,7 @@ static void musb_recover_from_babble(struct musb *musb) */ static struct musb *allocate_instance(struct device *dev, - struct musb_hdrc_config *config, void __iomem *mbase) + const struct musb_hdrc_config *config, void __iomem *mbase) { struct musb *musb; struct musb_hw_ep *ep; diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index fd215fb..b6afe9e 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -438,7 +438,7 @@ struct musb { */ unsigned double_buffer_not_ok:1; - struct musb_hdrc_config *config; + const struct musb_hdrc_config *config; int xceiv_old_state; #ifdef CONFIG_DEBUG_FS diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c index 7539c31..8abfe4e 100644 --- a/drivers/usb/musb/musbhsdma.c +++ b/drivers/usb/musb/musbhsdma.c @@ -117,8 +117,8 @@ static void configure_channel(struct dma_channel *channel, u8 bchannel = musb_channel->idx; u16 csr = 0; - dev_dbg(musb->controller, "%p, pkt_sz %d, addr 0x%x, len %d, mode %d\n", - channel, packet_sz, dma_addr, len, mode); + dev_dbg(musb->controller, "%p, pkt_sz %d, addr %pad, len %d, mode %d\n", + channel, packet_sz, &dma_addr, len, mode); if (mode) { csr |= 1 << MUSB_HSDMA_MODE1_SHIFT; @@ -152,10 +152,10 @@ static int dma_channel_program(struct dma_channel *channel, struct musb_dma_controller *controller = musb_channel->controller; struct musb *musb = controller->private_data; - dev_dbg(musb->controller, "ep%d-%s pkt_sz %d, dma_addr 0x%x length %d, mode %d\n", + dev_dbg(musb->controller, "ep%d-%s pkt_sz %d, dma_addr %pad length %d, mode %d\n", musb_channel->epnum, musb_channel->transmit ? "Tx" : "Rx", - packet_sz, dma_addr, len, mode); + packet_sz, &dma_addr, len, mode); BUG_ON(channel->status == MUSB_DMA_STATUS_UNKNOWN || channel->status == MUSB_DMA_STATUS_BUSY); diff --git a/drivers/usb/musb/tusb6010_omap.c b/drivers/usb/musb/tusb6010_omap.c index 4c82077..e6959cc 100644 --- a/drivers/usb/musb/tusb6010_omap.c +++ b/drivers/usb/musb/tusb6010_omap.c @@ -310,9 +310,9 @@ static int tusb_omap_dma_program(struct dma_channel *channel, u16 packet_sz, dma_params.frame_count = chdat->transfer_len / 32; /* Burst sz frame */ - dev_dbg(musb->controller, "ep%i %s dma ch%i dma: %08x len: %u(%u) packet_sz: %i(%i)\n", + dev_dbg(musb->controller, "ep%i %s dma ch%i dma: %pad len: %u(%u) packet_sz: %i(%i)\n", chdat->epnum, chdat->tx ? "tx" : "rx", - ch, dma_addr, chdat->transfer_len, len, + ch, &dma_addr, chdat->transfer_len, len, chdat->transfer_packet_sz, packet_sz); /* diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c index d0b6a1c..c92a295 100644 --- a/drivers/usb/musb/ux500_dma.c +++ b/drivers/usb/musb/ux500_dma.c @@ -207,9 +207,6 @@ static int ux500_dma_channel_program(struct dma_channel *channel, BUG_ON(channel->status == MUSB_DMA_STATUS_UNKNOWN || channel->status == MUSB_DMA_STATUS_BUSY); - if (!ux500_dma_is_compatible(channel, packet_sz, (void *)dma_addr, len)) - return false; - channel->status = MUSB_DMA_STATUS_BUSY; channel->actual_len = 0; ret = ux500_configure_channel(channel, packet_sz, mode, dma_addr, len); diff --git a/drivers/usb/phy/phy-am335x.c b/drivers/usb/phy/phy-am335x.c index 39b424f..a262a43 100644 --- a/drivers/usb/phy/phy-am335x.c +++ b/drivers/usb/phy/phy-am335x.c @@ -5,7 +5,6 @@ #include <linux/usb/usb_phy_generic.h> #include <linux/slab.h> #include <linux/clk.h> -#include <linux/regulator/consumer.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/usb/of.h> diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c index 5320cb8..980c9de 100644 --- a/drivers/usb/phy/phy-generic.c +++ b/drivers/usb/phy/phy-generic.c @@ -118,7 +118,8 @@ static irqreturn_t nop_gpio_vbus_thread(int irq, void *data) status = USB_EVENT_VBUS; otg->state = OTG_STATE_B_PERIPHERAL; nop->phy.last_event = status; - usb_gadget_vbus_connect(otg->gadget); + if (otg->gadget) + usb_gadget_vbus_connect(otg->gadget); /* drawing a "unit load" is *always* OK, except for OTG */ nop_set_vbus_draw(nop, 100); @@ -128,7 +129,8 @@ static irqreturn_t nop_gpio_vbus_thread(int irq, void *data) } else { nop_set_vbus_draw(nop, 0); - usb_gadget_vbus_disconnect(otg->gadget); + if (otg->gadget) + usb_gadget_vbus_disconnect(otg->gadget); status = USB_EVENT_NONE; otg->state = OTG_STATE_B_IDLE; nop->phy.last_event = status; @@ -184,7 +186,10 @@ static int nop_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget) } otg->gadget = gadget; - otg->state = OTG_STATE_B_IDLE; + if (otg->state == OTG_STATE_B_PERIPHERAL) + usb_gadget_vbus_connect(gadget); + else + otg->state = OTG_STATE_B_IDLE; return 0; } diff --git a/drivers/usb/phy/phy-isp1301-omap.c b/drivers/usb/phy/phy-isp1301-omap.c index 3af263c..8d111ec 100644 --- a/drivers/usb/phy/phy-isp1301-omap.c +++ b/drivers/usb/phy/phy-isp1301-omap.c @@ -258,7 +258,7 @@ static void power_down(struct isp1301 *isp) isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0); } -static void power_up(struct isp1301 *isp) +static void __maybe_unused power_up(struct isp1301 *isp) { // isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_2, MC2_GLOBAL_PWR_DN); isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, MC1_SUSPEND); diff --git a/drivers/usb/renesas_usbhs/Kconfig b/drivers/usb/renesas_usbhs/Kconfig index ebc99ee..b26d7c3 100644 --- a/drivers/usb/renesas_usbhs/Kconfig +++ b/drivers/usb/renesas_usbhs/Kconfig @@ -5,7 +5,7 @@ config USB_RENESAS_USBHS tristate 'Renesas USBHS controller' depends on USB_GADGET - depends on ARCH_SHMOBILE || SUPERH || COMPILE_TEST + depends on ARCH_RENESAS || SUPERH || COMPILE_TEST depends on EXTCON || !EXTCON # if EXTCON=m, USBHS cannot be built-in default n help diff --git a/drivers/usb/renesas_usbhs/Makefile b/drivers/usb/renesas_usbhs/Makefile index 9e47f47..d787d05 100644 --- a/drivers/usb/renesas_usbhs/Makefile +++ b/drivers/usb/renesas_usbhs/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs.o -renesas_usbhs-y := common.o mod.o pipe.o fifo.o rcar2.o +renesas_usbhs-y := common.o mod.o pipe.o fifo.o rcar2.o rcar3.o ifneq ($(CONFIG_USB_RENESAS_USBHS_HCD),) renesas_usbhs-y += mod_host.o diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 5af9ca5..baeb7d2 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -25,6 +25,7 @@ #include <linux/sysfs.h> #include "common.h" #include "rcar2.h" +#include "rcar3.h" /* * image of renesas_usbhs @@ -477,18 +478,16 @@ static const struct of_device_id usbhs_of_match[] = { .data = (void *)USBHS_TYPE_RCAR_GEN2, }, { - /* Gen3 is compatible with Gen2 */ .compatible = "renesas,usbhs-r8a7795", - .data = (void *)USBHS_TYPE_RCAR_GEN2, + .data = (void *)USBHS_TYPE_RCAR_GEN3, }, { .compatible = "renesas,rcar-gen2-usbhs", .data = (void *)USBHS_TYPE_RCAR_GEN2, }, { - /* Gen3 is compatible with Gen2 */ .compatible = "renesas,rcar-gen3-usbhs", - .data = (void *)USBHS_TYPE_RCAR_GEN2, + .data = (void *)USBHS_TYPE_RCAR_GEN3, }, { }, }; @@ -578,6 +577,13 @@ static int usbhs_probe(struct platform_device *pdev) priv->dparam.pipe_size = ARRAY_SIZE(usbhsc_new_pipe); } break; + case USBHS_TYPE_RCAR_GEN3: + priv->pfunc = usbhs_rcar3_ops; + if (!priv->dparam.pipe_configs) { + priv->dparam.pipe_configs = usbhsc_new_pipe; + priv->dparam.pipe_size = ARRAY_SIZE(usbhsc_new_pipe); + } + break; default: if (!info->platform_callback.get_id) { dev_err(&pdev->dev, "no platform callbacks"); diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index c0f5c65..b4de70e 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -46,7 +46,7 @@ static int usbhsf_null_handle(struct usbhs_pkt *pkt, int *is_done) return -EINVAL; } -static struct usbhs_pkt_handle usbhsf_null_handler = { +static const struct usbhs_pkt_handle usbhsf_null_handler = { .prepare = usbhsf_null_handle, .try_run = usbhsf_null_handle, }; @@ -422,12 +422,12 @@ static int usbhs_dcp_dir_switch_done(struct usbhs_pkt *pkt, int *is_done) return 0; } -struct usbhs_pkt_handle usbhs_dcp_status_stage_in_handler = { +const struct usbhs_pkt_handle usbhs_dcp_status_stage_in_handler = { .prepare = usbhs_dcp_dir_switch_to_write, .try_run = usbhs_dcp_dir_switch_done, }; -struct usbhs_pkt_handle usbhs_dcp_status_stage_out_handler = { +const struct usbhs_pkt_handle usbhs_dcp_status_stage_out_handler = { .prepare = usbhs_dcp_dir_switch_to_read, .try_run = usbhs_dcp_dir_switch_done, }; @@ -449,7 +449,7 @@ static int usbhsf_dcp_data_stage_try_push(struct usbhs_pkt *pkt, int *is_done) return pkt->handler->prepare(pkt, is_done); } -struct usbhs_pkt_handle usbhs_dcp_data_stage_out_handler = { +const struct usbhs_pkt_handle usbhs_dcp_data_stage_out_handler = { .prepare = usbhsf_dcp_data_stage_try_push, }; @@ -488,7 +488,7 @@ static int usbhsf_dcp_data_stage_prepare_pop(struct usbhs_pkt *pkt, return pkt->handler->prepare(pkt, is_done); } -struct usbhs_pkt_handle usbhs_dcp_data_stage_in_handler = { +const struct usbhs_pkt_handle usbhs_dcp_data_stage_in_handler = { .prepare = usbhsf_dcp_data_stage_prepare_pop, }; @@ -600,7 +600,7 @@ static int usbhsf_pio_prepare_push(struct usbhs_pkt *pkt, int *is_done) return usbhsf_pio_try_push(pkt, is_done); } -struct usbhs_pkt_handle usbhs_fifo_pio_push_handler = { +const struct usbhs_pkt_handle usbhs_fifo_pio_push_handler = { .prepare = usbhsf_pio_prepare_push, .try_run = usbhsf_pio_try_push, }; @@ -730,7 +730,7 @@ usbhs_fifo_read_busy: return ret; } -struct usbhs_pkt_handle usbhs_fifo_pio_pop_handler = { +const struct usbhs_pkt_handle usbhs_fifo_pio_pop_handler = { .prepare = usbhsf_prepare_pop, .try_run = usbhsf_pio_try_pop, }; @@ -747,7 +747,7 @@ static int usbhsf_ctrl_stage_end(struct usbhs_pkt *pkt, int *is_done) return 0; } -struct usbhs_pkt_handle usbhs_ctrl_stage_end_handler = { +const struct usbhs_pkt_handle usbhs_ctrl_stage_end_handler = { .prepare = usbhsf_ctrl_stage_end, .try_run = usbhsf_ctrl_stage_end, }; @@ -934,7 +934,7 @@ static int usbhsf_dma_push_done(struct usbhs_pkt *pkt, int *is_done) return 0; } -struct usbhs_pkt_handle usbhs_fifo_dma_push_handler = { +const struct usbhs_pkt_handle usbhs_fifo_dma_push_handler = { .prepare = usbhsf_dma_prepare_push, .dma_done = usbhsf_dma_push_done, }; @@ -1182,7 +1182,7 @@ static int usbhsf_dma_pop_done(struct usbhs_pkt *pkt, int *is_done) return usbhsf_dma_pop_done_with_rx_irq(pkt, is_done); } -struct usbhs_pkt_handle usbhs_fifo_dma_pop_handler = { +const struct usbhs_pkt_handle usbhs_fifo_dma_pop_handler = { .prepare = usbhsf_dma_prepare_pop, .try_run = usbhsf_dma_try_pop, .dma_done = usbhsf_dma_pop_done diff --git a/drivers/usb/renesas_usbhs/fifo.h b/drivers/usb/renesas_usbhs/fifo.h index c7d9b86..8b98507 100644 --- a/drivers/usb/renesas_usbhs/fifo.h +++ b/drivers/usb/renesas_usbhs/fifo.h @@ -54,7 +54,7 @@ struct usbhs_pkt_handle; struct usbhs_pkt { struct list_head node; struct usbhs_pipe *pipe; - struct usbhs_pkt_handle *handler; + const struct usbhs_pkt_handle *handler; void (*done)(struct usbhs_priv *priv, struct usbhs_pkt *pkt); struct work_struct work; @@ -86,18 +86,18 @@ void usbhs_fifo_clear_dcp(struct usbhs_pipe *pipe); /* * packet info */ -extern struct usbhs_pkt_handle usbhs_fifo_pio_push_handler; -extern struct usbhs_pkt_handle usbhs_fifo_pio_pop_handler; -extern struct usbhs_pkt_handle usbhs_ctrl_stage_end_handler; +extern const struct usbhs_pkt_handle usbhs_fifo_pio_push_handler; +extern const struct usbhs_pkt_handle usbhs_fifo_pio_pop_handler; +extern const struct usbhs_pkt_handle usbhs_ctrl_stage_end_handler; -extern struct usbhs_pkt_handle usbhs_fifo_dma_push_handler; -extern struct usbhs_pkt_handle usbhs_fifo_dma_pop_handler; +extern const struct usbhs_pkt_handle usbhs_fifo_dma_push_handler; +extern const struct usbhs_pkt_handle usbhs_fifo_dma_pop_handler; -extern struct usbhs_pkt_handle usbhs_dcp_status_stage_in_handler; -extern struct usbhs_pkt_handle usbhs_dcp_status_stage_out_handler; +extern const struct usbhs_pkt_handle usbhs_dcp_status_stage_in_handler; +extern const struct usbhs_pkt_handle usbhs_dcp_status_stage_out_handler; -extern struct usbhs_pkt_handle usbhs_dcp_data_stage_in_handler; -extern struct usbhs_pkt_handle usbhs_dcp_data_stage_out_handler; +extern const struct usbhs_pkt_handle usbhs_dcp_data_stage_in_handler; +extern const struct usbhs_pkt_handle usbhs_dcp_data_stage_out_handler; void usbhs_pkt_init(struct usbhs_pkt *pkt); void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt, diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index 657f967..664b263 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -561,7 +561,7 @@ static int usbhsg_pipe_disable(struct usbhsg_uep *uep) if (!pkt) break; - usbhsg_queue_pop(uep, usbhsg_pkt_to_ureq(pkt), -ECONNRESET); + usbhsg_queue_pop(uep, usbhsg_pkt_to_ureq(pkt), -ESHUTDOWN); } usbhs_pipe_disable(pipe); diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index 0e95d29..78e9dba 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -241,7 +241,7 @@ static int usbhsp_pipe_barrier(struct usbhs_pipe *pipe) { struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); int timeout = 1024; - u16 val; + u16 mask = usbhs_mod_is_host(priv) ? (CSSTS | PID_MASK) : PID_MASK; /* * make sure.... @@ -265,9 +265,7 @@ static int usbhsp_pipe_barrier(struct usbhs_pipe *pipe) usbhs_pipe_disable(pipe); do { - val = usbhsp_pipectrl_get(pipe); - val &= CSSTS | PID_MASK; - if (!val) + if (!(usbhsp_pipectrl_get(pipe) & mask)) return 0; udelay(10); diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h index 3212ab5..7835747 100644 --- a/drivers/usb/renesas_usbhs/pipe.h +++ b/drivers/usb/renesas_usbhs/pipe.h @@ -38,7 +38,7 @@ struct usbhs_pipe { #define USBHS_PIPE_FLAGS_IS_DIR_HOST (1 << 2) #define USBHS_PIPE_FLAGS_IS_RUNNING (1 << 3) - struct usbhs_pkt_handle *handler; + const struct usbhs_pkt_handle *handler; void *mod_private; }; diff --git a/drivers/usb/renesas_usbhs/rcar3.c b/drivers/usb/renesas_usbhs/rcar3.c new file mode 100644 index 0000000..38b01f2 --- /dev/null +++ b/drivers/usb/renesas_usbhs/rcar3.c @@ -0,0 +1,54 @@ +/* + * Renesas USB driver R-Car Gen. 3 initialization and power control + * + * Copyright (C) 2016 Renesas Electronics Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/io.h> +#include "common.h" +#include "rcar3.h" + +#define LPSTS 0x102 +#define UGCTRL2 0x184 /* 32-bit register */ + +/* Low Power Status register (LPSTS) */ +#define LPSTS_SUSPM 0x4000 + +/* USB General control register 2 (UGCTRL2), bit[31:6] should be 0 */ +#define UGCTRL2_RESERVED_3 0x00000001 /* bit[3:0] should be B'0001 */ +#define UGCTRL2_USB0SEL_OTG 0x00000030 + +void usbhs_write32(struct usbhs_priv *priv, u32 reg, u32 data) +{ + iowrite32(data, priv->base + reg); +} + +static int usbhs_rcar3_power_ctrl(struct platform_device *pdev, + void __iomem *base, int enable) +{ + struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); + + usbhs_write32(priv, UGCTRL2, UGCTRL2_RESERVED_3 | UGCTRL2_USB0SEL_OTG); + + if (enable) + usbhs_bset(priv, LPSTS, LPSTS_SUSPM, LPSTS_SUSPM); + else + usbhs_bset(priv, LPSTS, LPSTS_SUSPM, 0); + + return 0; +} + +static int usbhs_rcar3_get_id(struct platform_device *pdev) +{ + return USBHS_GADGET; +} + +const struct renesas_usbhs_platform_callback usbhs_rcar3_ops = { + .power_ctrl = usbhs_rcar3_power_ctrl, + .get_id = usbhs_rcar3_get_id, +}; diff --git a/drivers/usb/renesas_usbhs/rcar3.h b/drivers/usb/renesas_usbhs/rcar3.h new file mode 100644 index 0000000..5f850b2 --- /dev/null +++ b/drivers/usb/renesas_usbhs/rcar3.h @@ -0,0 +1,3 @@ +#include "common.h" + +extern const struct renesas_usbhs_platform_callback usbhs_rcar3_ops; diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index c73808f..f139488 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -370,7 +370,7 @@ static void ch341_set_termios(struct tty_struct *tty, static void ch341_break_ctl(struct tty_struct *tty, int break_state) { const uint16_t ch341_break_reg = - CH341_REG_BREAK1 | ((uint16_t) CH341_REG_BREAK2 << 8); + ((uint16_t) CH341_REG_BREAK2 << 8) | CH341_REG_BREAK1; struct usb_serial_port *port = tty->driver_data; int r; uint16_t reg_contents; diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 73a366d..fbfe761 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -327,113 +327,169 @@ struct cp210x_comm_status { #define PURGE_ALL 0x000f /* - * cp210x_get_config - * Reads from the CP210x configuration registers - * 'size' is specified in bytes. - * 'data' is a pointer to a pre-allocated array of integers large - * enough to hold 'size' bytes (with 4 bytes to each integer) + * Reads a variable-sized block of CP210X_ registers, identified by req. + * Returns data into buf in native USB byte order. */ -static int cp210x_get_config(struct usb_serial_port *port, u8 request, - unsigned int *data, int size) +static int cp210x_read_reg_block(struct usb_serial_port *port, u8 req, + void *buf, int bufsize) { struct usb_serial *serial = port->serial; struct cp210x_port_private *port_priv = usb_get_serial_port_data(port); - __le32 *buf; - int result, i, length; - - /* Number of integers required to contain the array */ - length = (((size - 1) | 3) + 1) / 4; + void *dmabuf; + int result; - buf = kcalloc(length, sizeof(__le32), GFP_KERNEL); - if (!buf) + dmabuf = kmalloc(bufsize, GFP_KERNEL); + if (!dmabuf) { + /* + * FIXME Some callers don't bother to check for error, + * at least give them consistent junk until they are fixed + */ + memset(buf, 0, bufsize); return -ENOMEM; + } - /* Issue the request, attempting to read 'size' bytes */ result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), - request, REQTYPE_INTERFACE_TO_HOST, 0x0000, - port_priv->bInterfaceNumber, buf, size, - USB_CTRL_GET_TIMEOUT); + req, REQTYPE_INTERFACE_TO_HOST, 0, + port_priv->bInterfaceNumber, dmabuf, bufsize, + USB_CTRL_SET_TIMEOUT); + if (result == bufsize) { + memcpy(buf, dmabuf, bufsize); + result = 0; + } else { + dev_err(&port->dev, "failed get req 0x%x size %d status: %d\n", + req, bufsize, result); + if (result >= 0) + result = -EPROTO; - /* Convert data into an array of integers */ - for (i = 0; i < length; i++) - data[i] = le32_to_cpu(buf[i]); + /* + * FIXME Some callers don't bother to check for error, + * at least give them consistent junk until they are fixed + */ + memset(buf, 0, bufsize); + } - kfree(buf); + kfree(dmabuf); - if (result != size) { - dev_dbg(&port->dev, "%s - Unable to send config request, request=0x%x size=%d result=%d\n", - __func__, request, size, result); - if (result > 0) - result = -EPROTO; + return result; +} - return result; +/* + * Reads any 32-bit CP210X_ register identified by req. + */ +static int cp210x_read_u32_reg(struct usb_serial_port *port, u8 req, u32 *val) +{ + __le32 le32_val; + int err; + + err = cp210x_read_reg_block(port, req, &le32_val, sizeof(le32_val)); + if (err) { + /* + * FIXME Some callers don't bother to check for error, + * at least give them consistent junk until they are fixed + */ + *val = 0; + return err; } + *val = le32_to_cpu(le32_val); + + return 0; +} + +/* + * Reads any 16-bit CP210X_ register identified by req. + */ +static int cp210x_read_u16_reg(struct usb_serial_port *port, u8 req, u16 *val) +{ + __le16 le16_val; + int err; + + err = cp210x_read_reg_block(port, req, &le16_val, sizeof(le16_val)); + if (err) + return err; + + *val = le16_to_cpu(le16_val); + return 0; } /* - * cp210x_set_config - * Writes to the CP210x configuration registers - * Values less than 16 bits wide are sent directly - * 'size' is specified in bytes. + * Reads any 8-bit CP210X_ register identified by req. + */ +static int cp210x_read_u8_reg(struct usb_serial_port *port, u8 req, u8 *val) +{ + return cp210x_read_reg_block(port, req, val, sizeof(*val)); +} + +/* + * Writes any 16-bit CP210X_ register (req) whose value is passed + * entirely in the wValue field of the USB request. */ -static int cp210x_set_config(struct usb_serial_port *port, u8 request, - unsigned int *data, int size) +static int cp210x_write_u16_reg(struct usb_serial_port *port, u8 req, u16 val) { struct usb_serial *serial = port->serial; struct cp210x_port_private *port_priv = usb_get_serial_port_data(port); - __le32 *buf; - int result, i, length; + int result; + + result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + req, REQTYPE_HOST_TO_INTERFACE, val, + port_priv->bInterfaceNumber, NULL, 0, + USB_CTRL_SET_TIMEOUT); + if (result < 0) { + dev_err(&port->dev, "failed set request 0x%x status: %d\n", + req, result); + } + + return result; +} - /* Number of integers required to contain the array */ - length = (((size - 1) | 3) + 1) / 4; +/* + * Writes a variable-sized block of CP210X_ registers, identified by req. + * Data in buf must be in native USB byte order. + */ +static int cp210x_write_reg_block(struct usb_serial_port *port, u8 req, + void *buf, int bufsize) +{ + struct usb_serial *serial = port->serial; + struct cp210x_port_private *port_priv = usb_get_serial_port_data(port); + void *dmabuf; + int result; - buf = kmalloc(length * sizeof(__le32), GFP_KERNEL); - if (!buf) + dmabuf = kmalloc(bufsize, GFP_KERNEL); + if (!dmabuf) return -ENOMEM; - /* Array of integers into bytes */ - for (i = 0; i < length; i++) - buf[i] = cpu_to_le32(data[i]); + memcpy(dmabuf, buf, bufsize); - if (size > 2) { - result = usb_control_msg(serial->dev, - usb_sndctrlpipe(serial->dev, 0), - request, REQTYPE_HOST_TO_INTERFACE, 0x0000, - port_priv->bInterfaceNumber, buf, size, - USB_CTRL_SET_TIMEOUT); - } else { - result = usb_control_msg(serial->dev, - usb_sndctrlpipe(serial->dev, 0), - request, REQTYPE_HOST_TO_INTERFACE, data[0], - port_priv->bInterfaceNumber, NULL, 0, - USB_CTRL_SET_TIMEOUT); - } + result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + req, REQTYPE_HOST_TO_INTERFACE, 0, + port_priv->bInterfaceNumber, dmabuf, bufsize, + USB_CTRL_SET_TIMEOUT); - kfree(buf); + kfree(dmabuf); - if ((size > 2 && result != size) || result < 0) { - dev_dbg(&port->dev, "%s - Unable to send request, request=0x%x size=%d result=%d\n", - __func__, request, size, result); - if (result > 0) + if (result == bufsize) { + result = 0; + } else { + dev_err(&port->dev, "failed set req 0x%x size %d status: %d\n", + req, bufsize, result); + if (result >= 0) result = -EPROTO; - - return result; } - return 0; + return result; } /* - * cp210x_set_config_single - * Convenience function for calling cp210x_set_config on single data values - * without requiring an integer pointer + * Writes any 32-bit CP210X_ register identified by req. */ -static inline int cp210x_set_config_single(struct usb_serial_port *port, - u8 request, unsigned int data) +static int cp210x_write_u32_reg(struct usb_serial_port *port, u8 req, u32 val) { - return cp210x_set_config(port, request, &data, 2); + __le32 le32_val; + + le32_val = cpu_to_le32(val); + + return cp210x_write_reg_block(port, req, &le32_val, sizeof(le32_val)); } /* @@ -445,47 +501,46 @@ static inline int cp210x_set_config_single(struct usb_serial_port *port, static int cp210x_detect_swapped_line_ctl(struct usb_serial_port *port) { struct cp210x_port_private *port_priv = usb_get_serial_port_data(port); - unsigned int line_ctl_save; - unsigned int line_ctl_test; + u16 line_ctl_save; + u16 line_ctl_test; int err; - err = cp210x_get_config(port, CP210X_GET_LINE_CTL, &line_ctl_save, 2); + err = cp210x_read_u16_reg(port, CP210X_GET_LINE_CTL, &line_ctl_save); if (err) return err; - line_ctl_test = 0x800; - err = cp210x_set_config(port, CP210X_SET_LINE_CTL, &line_ctl_test, 2); + err = cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, 0x800); if (err) return err; - err = cp210x_get_config(port, CP210X_GET_LINE_CTL, &line_ctl_test, 2); + err = cp210x_read_u16_reg(port, CP210X_GET_LINE_CTL, &line_ctl_test); if (err) return err; if (line_ctl_test == 8) { port_priv->has_swapped_line_ctl = true; - line_ctl_save = swab16((u16)line_ctl_save); + line_ctl_save = swab16(line_ctl_save); } - return cp210x_set_config(port, CP210X_SET_LINE_CTL, &line_ctl_save, 2); + return cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, line_ctl_save); } /* - * Must always be called instead of cp210x_get_config(CP210X_GET_LINE_CTL) + * Must always be called instead of cp210x_read_u16_reg(CP210X_GET_LINE_CTL) * to workaround cp2108 bug and get correct value. */ -static int cp210x_get_line_ctl(struct usb_serial_port *port, unsigned int *ctl) +static int cp210x_get_line_ctl(struct usb_serial_port *port, u16 *ctl) { struct cp210x_port_private *port_priv = usb_get_serial_port_data(port); int err; - err = cp210x_get_config(port, CP210X_GET_LINE_CTL, ctl, 2); + err = cp210x_read_u16_reg(port, CP210X_GET_LINE_CTL, ctl); if (err) return err; /* Workaround swapped bytes in 16-bit value from CP210X_GET_LINE_CTL */ if (port_priv->has_swapped_line_ctl) - *ctl = swab16((u16)(*ctl)); + *ctl = swab16(*ctl); return 0; } @@ -536,8 +591,7 @@ static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port) { int result; - result = cp210x_set_config_single(port, CP210X_IFC_ENABLE, - UART_ENABLE); + result = cp210x_write_u16_reg(port, CP210X_IFC_ENABLE, UART_ENABLE); if (result) { dev_err(&port->dev, "%s - Unable to enable UART\n", __func__); return result; @@ -555,15 +609,12 @@ static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port) static void cp210x_close(struct usb_serial_port *port) { - unsigned int purge_ctl; - usb_serial_generic_close(port); /* Clear both queues; cp2108 needs this to avoid an occasional hang */ - purge_ctl = PURGE_ALL; - cp210x_set_config(port, CP210X_PURGE, &purge_ctl, 2); + cp210x_write_u16_reg(port, CP210X_PURGE, PURGE_ALL); - cp210x_set_config_single(port, CP210X_IFC_ENABLE, UART_DISABLE); + cp210x_write_u16_reg(port, CP210X_IFC_ENABLE, UART_DISABLE); } /* @@ -641,11 +692,12 @@ static void cp210x_get_termios_port(struct usb_serial_port *port, unsigned int *cflagp, unsigned int *baudp) { struct device *dev = &port->dev; - unsigned int cflag, modem_ctl[4]; - unsigned int baud; - unsigned int bits; + unsigned int cflag; + u8 modem_ctl[16]; + u32 baud; + u16 bits; - cp210x_get_config(port, CP210X_GET_BAUDRATE, &baud, 4); + cp210x_read_u32_reg(port, CP210X_GET_BAUDRATE, &baud); dev_dbg(dev, "%s - baud rate = %d\n", __func__, baud); *baudp = baud; @@ -676,14 +728,14 @@ static void cp210x_get_termios_port(struct usb_serial_port *port, cflag |= CS8; bits &= ~BITS_DATA_MASK; bits |= BITS_DATA_8; - cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2); + cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, bits); break; default: dev_dbg(dev, "%s - Unknown number of data bits, using 8\n", __func__); cflag |= CS8; bits &= ~BITS_DATA_MASK; bits |= BITS_DATA_8; - cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2); + cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, bits); break; } @@ -714,7 +766,7 @@ static void cp210x_get_termios_port(struct usb_serial_port *port, dev_dbg(dev, "%s - Unknown parity mode, disabling parity\n", __func__); cflag &= ~PARENB; bits &= ~BITS_PARITY_MASK; - cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2); + cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, bits); break; } @@ -726,7 +778,7 @@ static void cp210x_get_termios_port(struct usb_serial_port *port, case BITS_STOP_1_5: dev_dbg(dev, "%s - stop bits = 1.5 (not supported, using 1 stop bit)\n", __func__); bits &= ~BITS_STOP_MASK; - cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2); + cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, bits); break; case BITS_STOP_2: dev_dbg(dev, "%s - stop bits = 2\n", __func__); @@ -735,12 +787,13 @@ static void cp210x_get_termios_port(struct usb_serial_port *port, default: dev_dbg(dev, "%s - Unknown number of stop bits, using 1 stop bit\n", __func__); bits &= ~BITS_STOP_MASK; - cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2); + cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, bits); break; } - cp210x_get_config(port, CP210X_GET_FLOW, modem_ctl, 16); - if (modem_ctl[0] & 0x0008) { + cp210x_read_reg_block(port, CP210X_GET_FLOW, modem_ctl, + sizeof(modem_ctl)); + if (modem_ctl[0] & 0x08) { dev_dbg(dev, "%s - flow control = CRTSCTS\n", __func__); cflag |= CRTSCTS; } else { @@ -792,8 +845,7 @@ static void cp210x_change_speed(struct tty_struct *tty, baud = cp210x_quantise_baudrate(baud); dev_dbg(&port->dev, "%s - setting baud rate to %u\n", __func__, baud); - if (cp210x_set_config(port, CP210X_SET_BAUDRATE, &baud, - sizeof(baud))) { + if (cp210x_write_u32_reg(port, CP210X_SET_BAUDRATE, baud)) { dev_warn(&port->dev, "failed to set baud rate to %u\n", baud); if (old_termios) baud = old_termios->c_ospeed; @@ -809,8 +861,8 @@ static void cp210x_set_termios(struct tty_struct *tty, { struct device *dev = &port->dev; unsigned int cflag, old_cflag; - unsigned int bits; - unsigned int modem_ctl[4]; + u16 bits; + u8 modem_ctl[16]; cflag = tty->termios.c_cflag; old_cflag = old_termios->c_cflag; @@ -848,7 +900,7 @@ static void cp210x_set_termios(struct tty_struct *tty, bits |= BITS_DATA_8; break; } - if (cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2)) + if (cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, bits)) dev_dbg(dev, "Number of data bits requested not supported by device\n"); } @@ -875,7 +927,7 @@ static void cp210x_set_termios(struct tty_struct *tty, } } } - if (cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2)) + if (cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, bits)) dev_dbg(dev, "Parity mode not supported by device\n"); } @@ -889,32 +941,40 @@ static void cp210x_set_termios(struct tty_struct *tty, bits |= BITS_STOP_1; dev_dbg(dev, "%s - stop bits = 1\n", __func__); } - if (cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2)) + if (cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, bits)) dev_dbg(dev, "Number of stop bits requested not supported by device\n"); } if ((cflag & CRTSCTS) != (old_cflag & CRTSCTS)) { - cp210x_get_config(port, CP210X_GET_FLOW, modem_ctl, 16); - dev_dbg(dev, "%s - read modem controls = 0x%.4x 0x%.4x 0x%.4x 0x%.4x\n", - __func__, modem_ctl[0], modem_ctl[1], - modem_ctl[2], modem_ctl[3]); + + /* Only bytes 0, 4 and 7 out of first 8 have functional bits */ + + cp210x_read_reg_block(port, CP210X_GET_FLOW, modem_ctl, + sizeof(modem_ctl)); + dev_dbg(dev, "%s - read modem controls = %02x .. .. .. %02x .. .. %02x\n", + __func__, modem_ctl[0], modem_ctl[4], modem_ctl[7]); if (cflag & CRTSCTS) { modem_ctl[0] &= ~0x7B; modem_ctl[0] |= 0x09; - modem_ctl[1] = 0x80; + modem_ctl[4] = 0x80; + /* FIXME - why clear reserved bits just read? */ + modem_ctl[5] = 0; + modem_ctl[6] = 0; + modem_ctl[7] = 0; dev_dbg(dev, "%s - flow control = CRTSCTS\n", __func__); } else { modem_ctl[0] &= ~0x7B; modem_ctl[0] |= 0x01; - modem_ctl[1] |= 0x40; + /* FIXME - OR here instead of assignment looks wrong */ + modem_ctl[4] |= 0x40; dev_dbg(dev, "%s - flow control = NONE\n", __func__); } - dev_dbg(dev, "%s - write modem controls = 0x%.4x 0x%.4x 0x%.4x 0x%.4x\n", - __func__, modem_ctl[0], modem_ctl[1], - modem_ctl[2], modem_ctl[3]); - cp210x_set_config(port, CP210X_SET_FLOW, modem_ctl, 16); + dev_dbg(dev, "%s - write modem controls = %02x .. .. .. %02x .. .. %02x\n", + __func__, modem_ctl[0], modem_ctl[4], modem_ctl[7]); + cp210x_write_reg_block(port, CP210X_SET_FLOW, modem_ctl, + sizeof(modem_ctl)); } } @@ -929,7 +989,7 @@ static int cp210x_tiocmset(struct tty_struct *tty, static int cp210x_tiocmset_port(struct usb_serial_port *port, unsigned int set, unsigned int clear) { - unsigned int control = 0; + u16 control = 0; if (set & TIOCM_RTS) { control |= CONTROL_RTS; @@ -950,7 +1010,7 @@ static int cp210x_tiocmset_port(struct usb_serial_port *port, dev_dbg(&port->dev, "%s - control = 0x%.4x\n", __func__, control); - return cp210x_set_config(port, CP210X_SET_MHS, &control, 2); + return cp210x_write_u16_reg(port, CP210X_SET_MHS, control); } static void cp210x_dtr_rts(struct usb_serial_port *p, int on) @@ -964,10 +1024,10 @@ static void cp210x_dtr_rts(struct usb_serial_port *p, int on) static int cp210x_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; - unsigned int control; + u8 control; int result; - cp210x_get_config(port, CP210X_GET_MDMSTS, &control, 1); + cp210x_read_u8_reg(port, CP210X_GET_MDMSTS, &control); result = ((control & CONTROL_DTR) ? TIOCM_DTR : 0) |((control & CONTROL_RTS) ? TIOCM_RTS : 0) @@ -984,7 +1044,7 @@ static int cp210x_tiocmget(struct tty_struct *tty) static void cp210x_break_ctl(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; - unsigned int state; + u16 state; if (break_state == 0) state = BREAK_OFF; @@ -992,7 +1052,7 @@ static void cp210x_break_ctl(struct tty_struct *tty, int break_state) state = BREAK_ON; dev_dbg(&port->dev, "%s - turning break %s\n", __func__, state == BREAK_OFF ? "off" : "on"); - cp210x_set_config(port, CP210X_SET_BREAK, &state, 2); + cp210x_write_u16_reg(port, CP210X_SET_BREAK, state); } static int cp210x_port_probe(struct usb_serial_port *port) diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index 2916dea..5f17a3b 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -140,7 +140,6 @@ static int cyberjack_open(struct tty_struct *tty, { struct cyberjack_private *priv; unsigned long flags; - int result = 0; dev_dbg(&port->dev, "%s - usb_clear_halt\n", __func__); usb_clear_halt(port->serial->dev, port->write_urb->pipe); @@ -152,7 +151,7 @@ static int cyberjack_open(struct tty_struct *tty, priv->wrsent = 0; spin_unlock_irqrestore(&priv->lock, flags); - return result; + return 0; } static void cyberjack_close(struct usb_serial_port *port) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 8c660ae..427ae43 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1320,11 +1320,11 @@ static __u32 get_ftdi_divisor(struct tty_struct *tty, if (baud <= 3000000) { __u16 product_id = le16_to_cpu( port->serial->dev->descriptor.idProduct); - if (((FTDI_NDI_HUC_PID == product_id) || - (FTDI_NDI_SPECTRA_SCU_PID == product_id) || - (FTDI_NDI_FUTURE_2_PID == product_id) || - (FTDI_NDI_FUTURE_3_PID == product_id) || - (FTDI_NDI_AURORA_SCU_PID == product_id)) && + if (((product_id == FTDI_NDI_HUC_PID) || + (product_id == FTDI_NDI_SPECTRA_SCU_PID) || + (product_id == FTDI_NDI_FUTURE_2_PID) || + (product_id == FTDI_NDI_FUTURE_3_PID) || + (product_id == FTDI_NDI_AURORA_SCU_PID)) && (baud == 19200)) { baud = 1200000; } diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h index ed58c6f..bbcc13df 100644 --- a/drivers/usb/serial/ftdi_sio.h +++ b/drivers/usb/serial/ftdi_sio.h @@ -239,11 +239,11 @@ enum ftdi_sio_baudrate { */ #define FTDI_SIO_SET_DTR_MASK 0x1 -#define FTDI_SIO_SET_DTR_HIGH (1 | (FTDI_SIO_SET_DTR_MASK << 8)) -#define FTDI_SIO_SET_DTR_LOW (0 | (FTDI_SIO_SET_DTR_MASK << 8)) +#define FTDI_SIO_SET_DTR_HIGH ((FTDI_SIO_SET_DTR_MASK << 8) | 1) +#define FTDI_SIO_SET_DTR_LOW ((FTDI_SIO_SET_DTR_MASK << 8) | 0) #define FTDI_SIO_SET_RTS_MASK 0x2 -#define FTDI_SIO_SET_RTS_HIGH (2 | (FTDI_SIO_SET_RTS_MASK << 8)) -#define FTDI_SIO_SET_RTS_LOW (0 | (FTDI_SIO_SET_RTS_MASK << 8)) +#define FTDI_SIO_SET_RTS_HIGH ((FTDI_SIO_SET_RTS_MASK << 8) | 2) +#define FTDI_SIO_SET_RTS_LOW ((FTDI_SIO_SET_RTS_MASK << 8) | 0) /* * ControlValue diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index db591d1..97cabf8 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -237,10 +237,10 @@ static inline int getDataLength(const __u8 *usbPacket) */ static inline int isAbortTrfCmnd(const unsigned char *buf) { - if (0 == memcmp(buf, GARMIN_STOP_TRANSFER_REQ, - sizeof(GARMIN_STOP_TRANSFER_REQ)) || - 0 == memcmp(buf, GARMIN_STOP_TRANSFER_REQ_V2, - sizeof(GARMIN_STOP_TRANSFER_REQ_V2))) + if (memcmp(buf, GARMIN_STOP_TRANSFER_REQ, + sizeof(GARMIN_STOP_TRANSFER_REQ)) == 0 || + memcmp(buf, GARMIN_STOP_TRANSFER_REQ_V2, + sizeof(GARMIN_STOP_TRANSFER_REQ_V2)) == 0) return 1; else return 0; @@ -350,7 +350,7 @@ static int gsp_send_ack(struct garmin_data *garmin_data_p, __u8 pkt_id) unsigned l = 0; dev_dbg(&garmin_data_p->port->dev, "%s - pkt-id: 0x%X.\n", __func__, - 0xFF & pkt_id); + pkt_id); *ptr++ = DLE; *ptr++ = ACK; @@ -366,7 +366,7 @@ static int gsp_send_ack(struct garmin_data *garmin_data_p, __u8 pkt_id) *ptr++ = DLE; *ptr++ = 0; - *ptr++ = 0xFF & (-cksum); + *ptr++ = (-cksum) & 0xFF; *ptr++ = DLE; *ptr++ = ETX; @@ -423,9 +423,9 @@ static int gsp_rec_packet(struct garmin_data *garmin_data_p, int count) n++; } - if ((0xff & (cksum + *recpkt)) != 0) { + if (((cksum + *recpkt) & 0xff) != 0) { dev_dbg(dev, "%s - invalid checksum, expected %02x, got %02x\n", - __func__, 0xff & -cksum, 0xff & *recpkt); + __func__, -cksum & 0xff, *recpkt); return -EINVPKT; } @@ -528,7 +528,7 @@ static int gsp_receive(struct garmin_data *garmin_data_p, dev_dbg(dev, "NAK packet complete.\n"); } else { dev_dbg(dev, "packet complete - id=0x%X.\n", - 0xFF & data); + data); gsp_rec_packet(garmin_data_p, size); } @@ -636,7 +636,7 @@ static int gsp_send(struct garmin_data *garmin_data_p, garmin_data_p->outsize = 0; - if (GARMIN_LAYERID_APPL != getLayerId(garmin_data_p->outbuffer)) { + if (getLayerId(garmin_data_p->outbuffer) != GARMIN_LAYERID_APPL) { dev_dbg(dev, "not an application packet (%d)\n", getLayerId(garmin_data_p->outbuffer)); return -1; @@ -688,7 +688,7 @@ static int gsp_send(struct garmin_data *garmin_data_p, *dst++ = DLE; } - cksum = 0xFF & -cksum; + cksum = -cksum & 0xFF; *dst++ = cksum; if (cksum == DLE) *dst++ = DLE; @@ -860,7 +860,6 @@ static int process_resetdev_request(struct usb_serial_port *port) static int garmin_clear(struct garmin_data *garmin_data_p) { unsigned long flags; - int status = 0; /* flush all queued data */ pkt_clear(garmin_data_p); @@ -870,7 +869,7 @@ static int garmin_clear(struct garmin_data *garmin_data_p) garmin_data_p->outsize = 0; spin_unlock_irqrestore(&garmin_data_p->lock, flags); - return status; + return 0; } @@ -970,7 +969,7 @@ static void garmin_write_bulk_callback(struct urb *urb) struct garmin_data *garmin_data_p = usb_get_serial_port_data(port); - if (GARMIN_LAYERID_APPL == getLayerId(urb->transfer_buffer)) { + if (getLayerId(urb->transfer_buffer) == GARMIN_LAYERID_APPL) { if (garmin_data_p->mode == MODE_GARMIN_SERIAL) { gsp_send_ack(garmin_data_p, @@ -1025,7 +1024,7 @@ static int garmin_write_bulk(struct usb_serial_port *port, dismiss_ack ? NULL : port); urb->transfer_flags |= URB_ZERO_PACKET; - if (GARMIN_LAYERID_APPL == getLayerId(buffer)) { + if (getLayerId(buffer) == GARMIN_LAYERID_APPL) { spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->flags |= APP_REQ_SEEN; @@ -1077,9 +1076,9 @@ static int garmin_write(struct tty_struct *tty, struct usb_serial_port *port, pktsiz = getDataLength(garmin_data_p->privpkt); pktid = getPacketId(garmin_data_p->privpkt); - if (count == (GARMIN_PKTHDR_LENGTH+pktsiz) - && GARMIN_LAYERID_PRIVATE == - getLayerId(garmin_data_p->privpkt)) { + if (count == (GARMIN_PKTHDR_LENGTH + pktsiz) && + getLayerId(garmin_data_p->privpkt) == + GARMIN_LAYERID_PRIVATE) { dev_dbg(dev, "%s - processing private request %d\n", __func__, pktid); @@ -1192,7 +1191,7 @@ static void garmin_read_bulk_callback(struct urb *urb) garmin_read_process(garmin_data_p, data, urb->actual_length, 1); if (urb->actual_length == 0 && - 0 != (garmin_data_p->flags & FLAGS_BULK_IN_RESTART)) { + (garmin_data_p->flags & FLAGS_BULK_IN_RESTART) != 0) { spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->flags &= ~FLAGS_BULK_IN_RESTART; spin_unlock_irqrestore(&garmin_data_p->lock, flags); @@ -1203,7 +1202,7 @@ static void garmin_read_bulk_callback(struct urb *urb) __func__, retval); } else if (urb->actual_length > 0) { /* Continue trying to read until nothing more is received */ - if (0 == (garmin_data_p->flags & FLAGS_THROTTLED)) { + if ((garmin_data_p->flags & FLAGS_THROTTLED) == 0) { retval = usb_submit_urb(port->read_urb, GFP_ATOMIC); if (retval) dev_err(&port->dev, @@ -1249,12 +1248,12 @@ static void garmin_read_int_callback(struct urb *urb) urb->transfer_buffer); if (urb->actual_length == sizeof(GARMIN_BULK_IN_AVAIL_REPLY) && - 0 == memcmp(data, GARMIN_BULK_IN_AVAIL_REPLY, - sizeof(GARMIN_BULK_IN_AVAIL_REPLY))) { + memcmp(data, GARMIN_BULK_IN_AVAIL_REPLY, + sizeof(GARMIN_BULK_IN_AVAIL_REPLY)) == 0) { dev_dbg(&port->dev, "%s - bulk data available.\n", __func__); - if (0 == (garmin_data_p->flags & FLAGS_BULK_IN_ACTIVE)) { + if ((garmin_data_p->flags & FLAGS_BULK_IN_ACTIVE) == 0) { /* bulk data available */ retval = usb_submit_urb(port->read_urb, GFP_ATOMIC); @@ -1276,8 +1275,8 @@ static void garmin_read_int_callback(struct urb *urb) } } else if (urb->actual_length == (4+sizeof(GARMIN_START_SESSION_REPLY)) - && 0 == memcmp(data, GARMIN_START_SESSION_REPLY, - sizeof(GARMIN_START_SESSION_REPLY))) { + && memcmp(data, GARMIN_START_SESSION_REPLY, + sizeof(GARMIN_START_SESSION_REPLY)) == 0) { spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->flags |= FLAGS_SESSION_REPLY1_SEEN; @@ -1356,7 +1355,7 @@ static void garmin_unthrottle(struct tty_struct *tty) if (garmin_data_p->mode == MODE_NATIVE) garmin_flush_queue(garmin_data_p); - if (0 != (garmin_data_p->flags & FLAGS_BULK_IN_ACTIVE)) { + if ((garmin_data_p->flags & FLAGS_BULK_IN_ACTIVE) != 0) { status = usb_submit_urb(port->read_urb, GFP_KERNEL); if (status) dev_err(&port->dev, diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index 5ad4a0f..344b4ee 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -360,7 +360,7 @@ static void iuu_led_activity_on(struct urb *urb) int result; char *buf_ptr = port->write_urb->transfer_buffer; *buf_ptr++ = IUU_SET_LED; - if (xmas == 1) { + if (xmas) { get_random_bytes(buf_ptr, 6); *(buf_ptr+7) = 1; } else { @@ -380,7 +380,7 @@ static void iuu_led_activity_off(struct urb *urb) struct usb_serial_port *port = urb->context; int result; char *buf_ptr = port->write_urb->transfer_buffer; - if (xmas == 1) { + if (xmas) { iuu_rxcmd(urb); return; } else { diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index e07b15e..b6bd8e4 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -1963,7 +1963,7 @@ static int keyspan_usa49_send_setup(struct usb_serial *serial, if (d_details->product_id == keyspan_usa49wg_product_id) { dr = (void *)(s_priv->ctrl_buf); dr->bRequestType = USB_TYPE_VENDOR | USB_DIR_OUT; - dr->bRequest = 0xB0; /* 49wg control message */; + dr->bRequest = 0xB0; /* 49wg control message */ dr->wValue = 0; dr->wIndex = 0; dr->wLength = cpu_to_le16(sizeof(msg)); diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index e020ad2..fc5d3a7 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -472,7 +472,6 @@ static void klsi_105_set_termios(struct tty_struct *tty, /* maybe this should be simulated by sending read * disable and read enable messages? */ - ; #if 0 priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS); mct_u232_set_modem_ctrl(serial, priv->control_state); @@ -527,7 +526,6 @@ static void klsi_105_set_termios(struct tty_struct *tty, mct_u232_set_line_ctrl(serial, priv->last_lcr); #endif - ; } /* * Set flow control: well, I do not really now how to handle DTR/RTS. @@ -546,7 +544,6 @@ static void klsi_105_set_termios(struct tty_struct *tty, priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS); mct_u232_set_modem_ctrl(serial, priv->control_state); #endif - ; } memcpy(cfg, &priv->cfg, sizeof(*cfg)); spin_unlock_irqrestore(&priv->lock, flags); diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 02ea975..ed378fb 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -1842,7 +1842,7 @@ static void mos7840_change_port_settings(struct tty_struct *tty, Data = 0x0c; mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data); - if (mos7840_port->read_urb_busy == false) { + if (!mos7840_port->read_urb_busy) { mos7840_port->read_urb_busy = true; status = usb_submit_urb(mos7840_port->read_urb, GFP_KERNEL); if (status) { @@ -1906,7 +1906,7 @@ static void mos7840_set_termios(struct tty_struct *tty, return; } - if (mos7840_port->read_urb_busy == false) { + if (!mos7840_port->read_urb_busy) { mos7840_port->read_urb_busy = true; status = usb_submit_urb(mos7840_port->read_urb, GFP_KERNEL); if (status) { diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index 504f5bf..2df8ad5 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -973,7 +973,7 @@ static int qt2_write(struct tty_struct *tty, data = write_urb->transfer_buffer; spin_lock_irqsave(&port_priv->urb_lock, flags); - if (port_priv->urb_in_use == true) { + if (port_priv->urb_in_use) { dev_err(&port->dev, "qt2_write - urb is in use\n"); goto write_out; } diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c index b2dff0f..93c6c9b 100644 --- a/drivers/usb/serial/safe_serial.c +++ b/drivers/usb/serial/safe_serial.c @@ -76,13 +76,8 @@ #include <linux/usb.h> #include <linux/usb/serial.h> - -#ifndef CONFIG_USB_SERIAL_SAFE_PADDED -#define CONFIG_USB_SERIAL_SAFE_PADDED 0 -#endif - -static bool safe = 1; -static bool padded = CONFIG_USB_SERIAL_SAFE_PADDED; +static bool safe = true; +static bool padded = IS_ENABLED(CONFIG_USB_SERIAL_SAFE_PADDED); #define DRIVER_AUTHOR "sl@lineo.com, tbr@lineo.com, Johan Hovold <jhovold@gmail.com>" #define DRIVER_DESC "USB Safe Encapsulated Serial" @@ -278,7 +273,7 @@ static int safe_startup(struct usb_serial *serial) case LINEO_SAFESERIAL_CRC: break; case LINEO_SAFESERIAL_CRC_PADDED: - padded = 1; + padded = true; break; default: return -EINVAL; diff --git a/drivers/usb/storage/debug.c b/drivers/usb/storage/debug.c index 57bf3ad..5a12c03 100644 --- a/drivers/usb/storage/debug.c +++ b/drivers/usb/storage/debug.c @@ -57,7 +57,6 @@ void usb_stor_show_command(const struct us_data *us, struct scsi_cmnd *srb) { char *what = NULL; - int i; switch (srb->cmnd[0]) { case TEST_UNIT_READY: what = "TEST_UNIT_READY"; break; @@ -153,10 +152,8 @@ void usb_stor_show_command(const struct us_data *us, struct scsi_cmnd *srb) default: what = "(unknown command)"; break; } usb_stor_dbg(us, "Command %s (%d bytes)\n", what, srb->cmd_len); - usb_stor_dbg(us, "bytes: "); - for (i = 0; i < srb->cmd_len && i < 16; i++) - US_DEBUGPX(" %02x", srb->cmnd[i]); - US_DEBUGPX("\n"); + usb_stor_dbg(us, "bytes: %*ph\n", min_t(int, srb->cmd_len, 16), + (const unsigned char *)srb->cmnd); } void usb_stor_show_sense(const struct us_data *us, @@ -174,11 +171,10 @@ void usb_stor_show_sense(const struct us_data *us, if (what == NULL) what = "(unknown ASC/ASCQ)"; - usb_stor_dbg(us, "%s: ", keystr); if (fmt) - US_DEBUGPX("%s (%s%x)\n", what, fmt, ascq); + usb_stor_dbg(us, "%s: %s (%s%x)\n", keystr, what, fmt, ascq); else - US_DEBUGPX("%s\n", what); + usb_stor_dbg(us, "%s: %s\n", keystr, what); } void usb_stor_dbg(const struct us_data *us, const char *fmt, ...) diff --git a/drivers/usb/storage/debug.h b/drivers/usb/storage/debug.h index f525203..6b365ce 100644 --- a/drivers/usb/storage/debug.h +++ b/drivers/usb/storage/debug.h @@ -53,7 +53,6 @@ void usb_stor_show_sense(const struct us_data *us, unsigned char key, __printf(2, 3) void usb_stor_dbg(const struct us_data *us, const char *fmt, ...); -#define US_DEBUGPX(fmt, ...) printk(fmt, ##__VA_ARGS__) #define US_DEBUG(x) x #else __printf(2, 3) @@ -63,8 +62,6 @@ static inline void _usb_stor_dbg(const struct us_data *us, } #define usb_stor_dbg(us, fmt, ...) \ do { if (0) _usb_stor_dbg(us, fmt, ##__VA_ARGS__); } while (0) -#define US_DEBUGPX(fmt, ...) \ - do { if (0) printk(fmt, ##__VA_ARGS__); } while (0) #define US_DEBUG(x) #endif diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c index f3cf4ce..d3a17c6 100644 --- a/drivers/usb/storage/ene_ub6250.c +++ b/drivers/usb/storage/ene_ub6250.c @@ -1067,12 +1067,12 @@ static void ms_lib_free_writebuf(struct us_data *us) ms_lib_clear_pagemap(info); /* (pdx)->MS_Lib.pagemap memset 0 in ms.h */ if (info->MS_Lib.blkpag) { - kfree((u8 *)(info->MS_Lib.blkpag)); /* Arnold test ... */ + kfree(info->MS_Lib.blkpag); /* Arnold test ... */ info->MS_Lib.blkpag = NULL; } if (info->MS_Lib.blkext) { - kfree((u8 *)(info->MS_Lib.blkext)); /* Arnold test ... */ + kfree(info->MS_Lib.blkext); /* Arnold test ... */ info->MS_Lib.blkext = NULL; } } diff --git a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c index b746036..79224fc 100644 --- a/drivers/usb/storage/sddr09.c +++ b/drivers/usb/storage/sddr09.c @@ -1102,24 +1102,24 @@ static int sddr09_get_wp(struct us_data *us, struct sddr09_card_info *info) { int result; unsigned char status; + const char *wp_fmt; result = sddr09_read_status(us, &status); if (result) { usb_stor_dbg(us, "read_status fails\n"); return result; } - usb_stor_dbg(us, "status 0x%02X", status); if ((status & 0x80) == 0) { info->flags |= SDDR09_WP; /* write protected */ - US_DEBUGPX(" WP"); + wp_fmt = " WP"; + } else { + wp_fmt = ""; } - if (status & 0x40) - US_DEBUGPX(" Ready"); - if (status & LUNBITS) - US_DEBUGPX(" Suspended"); - if (status & 0x1) - US_DEBUGPX(" Error"); - US_DEBUGPX("\n"); + usb_stor_dbg(us, "status 0x%02X%s%s%s%s\n", status, wp_fmt, + status & 0x40 ? " Ready" : "", + status & LUNBITS ? " Suspended" : "", + status & 0x01 ? " Error" : ""); + return 0; } diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 9ff9404..44b096c 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -246,6 +246,29 @@ static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd, } } +static bool uas_evaluate_response_iu(struct response_iu *riu, struct scsi_cmnd *cmnd) +{ + u8 response_code = riu->response_code; + + switch (response_code) { + case RC_INCORRECT_LUN: + cmnd->result = DID_BAD_TARGET << 16; + break; + case RC_TMF_SUCCEEDED: + cmnd->result = DID_OK << 16; + break; + case RC_TMF_NOT_SUPPORTED: + cmnd->result = DID_TARGET_FAILURE << 16; + break; + default: + uas_log_cmd_state(cmnd, "response iu", response_code); + cmnd->result = DID_ERROR << 16; + break; + } + + return response_code == RC_TMF_SUCCEEDED; +} + static void uas_stat_cmplt(struct urb *urb) { struct iu *iu = urb->transfer_buffer; @@ -258,6 +281,7 @@ static void uas_stat_cmplt(struct urb *urb) unsigned long flags; unsigned int idx; int status = urb->status; + bool success; spin_lock_irqsave(&devinfo->lock, flags); @@ -313,13 +337,13 @@ static void uas_stat_cmplt(struct urb *urb) uas_xfer_data(urb, cmnd, SUBMIT_DATA_OUT_URB); break; case IU_ID_RESPONSE: - uas_log_cmd_state(cmnd, "unexpected response iu", - ((struct response_iu *)iu)->response_code); - /* Error, cancel data transfers */ - data_in_urb = usb_get_urb(cmdinfo->data_in_urb); - data_out_urb = usb_get_urb(cmdinfo->data_out_urb); cmdinfo->state &= ~COMMAND_INFLIGHT; - cmnd->result = DID_ERROR << 16; + success = uas_evaluate_response_iu((struct response_iu *)iu, cmnd); + if (!success) { + /* Error, cancel data transfers */ + data_in_urb = usb_get_urb(cmdinfo->data_in_urb); + data_out_urb = usb_get_urb(cmdinfo->data_out_urb); + } uas_try_complete(cmnd, __func__); break; default: diff --git a/drivers/usb/usbip/usbip_event.c b/drivers/usb/usbip/usbip_event.c index 64933b9..2580a32 100644 --- a/drivers/usb/usbip/usbip_event.c +++ b/drivers/usb/usbip/usbip_event.c @@ -117,11 +117,12 @@ EXPORT_SYMBOL_GPL(usbip_event_add); int usbip_event_happened(struct usbip_device *ud) { int happened = 0; + unsigned long flags; - spin_lock(&ud->lock); + spin_lock_irqsave(&ud->lock, flags); if (ud->event != 0) happened = 1; - spin_unlock(&ud->lock); + spin_unlock_irqrestore(&ud->lock, flags); return happened; } diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c index 7fbe19d..fca5110 100644 --- a/drivers/usb/usbip/vhci_hcd.c +++ b/drivers/usb/usbip/vhci_hcd.c @@ -121,9 +121,11 @@ static void dump_port_status_diff(u32 prev_status, u32 new_status) void rh_port_connect(int rhport, enum usb_device_speed speed) { + unsigned long flags; + usbip_dbg_vhci_rh("rh_port_connect %d\n", rhport); - spin_lock(&the_controller->lock); + spin_lock_irqsave(&the_controller->lock, flags); the_controller->port_status[rhport] |= USB_PORT_STAT_CONNECTION | (1 << USB_PORT_FEAT_C_CONNECTION); @@ -139,22 +141,24 @@ void rh_port_connect(int rhport, enum usb_device_speed speed) break; } - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); usb_hcd_poll_rh_status(vhci_to_hcd(the_controller)); } static void rh_port_disconnect(int rhport) { + unsigned long flags; + usbip_dbg_vhci_rh("rh_port_disconnect %d\n", rhport); - spin_lock(&the_controller->lock); + spin_lock_irqsave(&the_controller->lock, flags); the_controller->port_status[rhport] &= ~USB_PORT_STAT_CONNECTION; the_controller->port_status[rhport] |= (1 << USB_PORT_FEAT_C_CONNECTION); - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); usb_hcd_poll_rh_status(vhci_to_hcd(the_controller)); } @@ -182,13 +186,14 @@ static int vhci_hub_status(struct usb_hcd *hcd, char *buf) int retval; int rhport; int changed = 0; + unsigned long flags; retval = DIV_ROUND_UP(VHCI_NPORTS + 1, 8); memset(buf, 0, retval); vhci = hcd_to_vhci(hcd); - spin_lock(&vhci->lock); + spin_lock_irqsave(&vhci->lock, flags); if (!HCD_HW_ACCESSIBLE(hcd)) { usbip_dbg_vhci_rh("hw accessible flag not on?\n"); goto done; @@ -209,7 +214,7 @@ static int vhci_hub_status(struct usb_hcd *hcd, char *buf) usb_hcd_resume_root_hub(hcd); done: - spin_unlock(&vhci->lock); + spin_unlock_irqrestore(&vhci->lock, flags); return changed ? retval : 0; } @@ -231,6 +236,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, struct vhci_hcd *dum; int retval = 0; int rhport; + unsigned long flags; u32 prev_port_status[VHCI_NPORTS]; @@ -249,7 +255,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, dum = hcd_to_vhci(hcd); - spin_lock(&dum->lock); + spin_lock_irqsave(&dum->lock, flags); /* store old status and compare now and old later */ if (usbip_dbg_flag_vhci_rh) { @@ -403,7 +409,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, } usbip_dbg_vhci_rh(" bye\n"); - spin_unlock(&dum->lock); + spin_unlock_irqrestore(&dum->lock, flags); return retval; } @@ -426,6 +432,7 @@ static void vhci_tx_urb(struct urb *urb) { struct vhci_device *vdev = get_vdev(urb->dev); struct vhci_priv *priv; + unsigned long flags; if (!vdev) { pr_err("could not get virtual device"); @@ -438,7 +445,7 @@ static void vhci_tx_urb(struct urb *urb) return; } - spin_lock(&vdev->priv_lock); + spin_lock_irqsave(&vdev->priv_lock, flags); priv->seqnum = atomic_inc_return(&the_controller->seqnum); if (priv->seqnum == 0xffff) @@ -452,7 +459,7 @@ static void vhci_tx_urb(struct urb *urb) list_add_tail(&priv->list, &vdev->priv_tx); wake_up(&vdev->waitq_tx); - spin_unlock(&vdev->priv_lock); + spin_unlock_irqrestore(&vdev->priv_lock, flags); } static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, @@ -461,6 +468,7 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, struct device *dev = &urb->dev->dev; int ret = 0; struct vhci_device *vdev; + unsigned long flags; usbip_dbg_vhci_hc("enter, usb_hcd %p urb %p mem_flags %d\n", hcd, urb, mem_flags); @@ -468,11 +476,11 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, /* patch to usb_sg_init() is in 2.5.60 */ BUG_ON(!urb->transfer_buffer && urb->transfer_buffer_length); - spin_lock(&the_controller->lock); + spin_lock_irqsave(&the_controller->lock, flags); if (urb->status != -EINPROGRESS) { dev_err(dev, "URB already unlinked!, status %d\n", urb->status); - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); return urb->status; } @@ -484,7 +492,7 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, vdev->ud.status == VDEV_ST_ERROR) { dev_err(dev, "enqueue for inactive port %d\n", vdev->rhport); spin_unlock(&vdev->ud.lock); - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); return -ENODEV; } spin_unlock(&vdev->ud.lock); @@ -557,14 +565,14 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, out: vhci_tx_urb(urb); - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); return 0; no_need_xmit: usb_hcd_unlink_urb_from_ep(hcd, urb); no_need_unlink: - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); if (!ret) usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, urb->status); @@ -621,16 +629,17 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) { struct vhci_priv *priv; struct vhci_device *vdev; + unsigned long flags; pr_info("dequeue a urb %p\n", urb); - spin_lock(&the_controller->lock); + spin_lock_irqsave(&the_controller->lock, flags); priv = urb->hcpriv; if (!priv) { /* URB was never linked! or will be soon given back by * vhci_rx. */ - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); return -EIDRM; } @@ -639,7 +648,7 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) ret = usb_hcd_check_unlink_urb(hcd, urb, status); if (ret) { - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); return ret; } } @@ -667,10 +676,10 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) usb_hcd_unlink_urb_from_ep(hcd, urb); - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, urb->status); - spin_lock(&the_controller->lock); + spin_lock_irqsave(&the_controller->lock, flags); } else { /* tcp connection is alive */ @@ -682,7 +691,7 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) unlink = kzalloc(sizeof(struct vhci_unlink), GFP_ATOMIC); if (!unlink) { spin_unlock(&vdev->priv_lock); - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_MALLOC); return -ENOMEM; } @@ -703,7 +712,7 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) spin_unlock(&vdev->priv_lock); } - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); usbip_dbg_vhci_hc("leave\n"); return 0; @@ -712,8 +721,9 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) static void vhci_device_unlink_cleanup(struct vhci_device *vdev) { struct vhci_unlink *unlink, *tmp; + unsigned long flags; - spin_lock(&the_controller->lock); + spin_lock_irqsave(&the_controller->lock, flags); spin_lock(&vdev->priv_lock); list_for_each_entry_safe(unlink, tmp, &vdev->unlink_tx, list) { @@ -747,19 +757,19 @@ static void vhci_device_unlink_cleanup(struct vhci_device *vdev) list_del(&unlink->list); spin_unlock(&vdev->priv_lock); - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, urb->status); - spin_lock(&the_controller->lock); + spin_lock_irqsave(&the_controller->lock, flags); spin_lock(&vdev->priv_lock); kfree(unlink); } spin_unlock(&vdev->priv_lock); - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); } /* @@ -826,8 +836,9 @@ static void vhci_shutdown_connection(struct usbip_device *ud) static void vhci_device_reset(struct usbip_device *ud) { struct vhci_device *vdev = container_of(ud, struct vhci_device, ud); + unsigned long flags; - spin_lock(&ud->lock); + spin_lock_irqsave(&ud->lock, flags); vdev->speed = 0; vdev->devid = 0; @@ -841,14 +852,16 @@ static void vhci_device_reset(struct usbip_device *ud) } ud->status = VDEV_ST_NULL; - spin_unlock(&ud->lock); + spin_unlock_irqrestore(&ud->lock, flags); } static void vhci_device_unusable(struct usbip_device *ud) { - spin_lock(&ud->lock); + unsigned long flags; + + spin_lock_irqsave(&ud->lock, flags); ud->status = VDEV_ST_ERROR; - spin_unlock(&ud->lock); + spin_unlock_irqrestore(&ud->lock, flags); } static void vhci_device_init(struct vhci_device *vdev) @@ -938,12 +951,13 @@ static int vhci_get_frame_number(struct usb_hcd *hcd) static int vhci_bus_suspend(struct usb_hcd *hcd) { struct vhci_hcd *vhci = hcd_to_vhci(hcd); + unsigned long flags; dev_dbg(&hcd->self.root_hub->dev, "%s\n", __func__); - spin_lock(&vhci->lock); + spin_lock_irqsave(&vhci->lock, flags); hcd->state = HC_STATE_SUSPENDED; - spin_unlock(&vhci->lock); + spin_unlock_irqrestore(&vhci->lock, flags); return 0; } @@ -952,15 +966,16 @@ static int vhci_bus_resume(struct usb_hcd *hcd) { struct vhci_hcd *vhci = hcd_to_vhci(hcd); int rc = 0; + unsigned long flags; dev_dbg(&hcd->self.root_hub->dev, "%s\n", __func__); - spin_lock(&vhci->lock); + spin_lock_irqsave(&vhci->lock, flags); if (!HCD_HW_ACCESSIBLE(hcd)) rc = -ESHUTDOWN; else hcd->state = HC_STATE_RUNNING; - spin_unlock(&vhci->lock); + spin_unlock_irqrestore(&vhci->lock, flags); return rc; } @@ -1058,17 +1073,18 @@ static int vhci_hcd_suspend(struct platform_device *pdev, pm_message_t state) int rhport = 0; int connected = 0; int ret = 0; + unsigned long flags; hcd = platform_get_drvdata(pdev); - spin_lock(&the_controller->lock); + spin_lock_irqsave(&the_controller->lock, flags); for (rhport = 0; rhport < VHCI_NPORTS; rhport++) if (the_controller->port_status[rhport] & USB_PORT_STAT_CONNECTION) connected += 1; - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); if (connected > 0) { dev_info(&pdev->dev, diff --git a/drivers/usb/usbip/vhci_rx.c b/drivers/usb/usbip/vhci_rx.c index 00e4a54..d656e0e 100644 --- a/drivers/usb/usbip/vhci_rx.c +++ b/drivers/usb/usbip/vhci_rx.c @@ -72,10 +72,11 @@ static void vhci_recv_ret_submit(struct vhci_device *vdev, { struct usbip_device *ud = &vdev->ud; struct urb *urb; + unsigned long flags; - spin_lock(&vdev->priv_lock); + spin_lock_irqsave(&vdev->priv_lock, flags); urb = pickup_urb_and_free_priv(vdev, pdu->base.seqnum); - spin_unlock(&vdev->priv_lock); + spin_unlock_irqrestore(&vdev->priv_lock, flags); if (!urb) { pr_err("cannot find a urb of seqnum %u\n", pdu->base.seqnum); @@ -104,9 +105,9 @@ static void vhci_recv_ret_submit(struct vhci_device *vdev, usbip_dbg_vhci_rx("now giveback urb %p\n", urb); - spin_lock(&the_controller->lock); + spin_lock_irqsave(&the_controller->lock, flags); usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb); - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, urb->status); @@ -117,8 +118,9 @@ static struct vhci_unlink *dequeue_pending_unlink(struct vhci_device *vdev, struct usbip_header *pdu) { struct vhci_unlink *unlink, *tmp; + unsigned long flags; - spin_lock(&vdev->priv_lock); + spin_lock_irqsave(&vdev->priv_lock, flags); list_for_each_entry_safe(unlink, tmp, &vdev->unlink_rx, list) { pr_info("unlink->seqnum %lu\n", unlink->seqnum); @@ -127,12 +129,12 @@ static struct vhci_unlink *dequeue_pending_unlink(struct vhci_device *vdev, unlink->seqnum); list_del(&unlink->list); - spin_unlock(&vdev->priv_lock); + spin_unlock_irqrestore(&vdev->priv_lock, flags); return unlink; } } - spin_unlock(&vdev->priv_lock); + spin_unlock_irqrestore(&vdev->priv_lock, flags); return NULL; } @@ -142,6 +144,7 @@ static void vhci_recv_ret_unlink(struct vhci_device *vdev, { struct vhci_unlink *unlink; struct urb *urb; + unsigned long flags; usbip_dump_header(pdu); @@ -152,9 +155,9 @@ static void vhci_recv_ret_unlink(struct vhci_device *vdev, return; } - spin_lock(&vdev->priv_lock); + spin_lock_irqsave(&vdev->priv_lock, flags); urb = pickup_urb_and_free_priv(vdev, unlink->unlink_seqnum); - spin_unlock(&vdev->priv_lock); + spin_unlock_irqrestore(&vdev->priv_lock, flags); if (!urb) { /* @@ -171,9 +174,9 @@ static void vhci_recv_ret_unlink(struct vhci_device *vdev, urb->status = pdu->u.ret_unlink.status; pr_info("urb->status %d\n", urb->status); - spin_lock(&the_controller->lock); + spin_lock_irqsave(&the_controller->lock, flags); usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb); - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, urb->status); @@ -185,10 +188,11 @@ static void vhci_recv_ret_unlink(struct vhci_device *vdev, static int vhci_priv_tx_empty(struct vhci_device *vdev) { int empty = 0; + unsigned long flags; - spin_lock(&vdev->priv_lock); + spin_lock_irqsave(&vdev->priv_lock, flags); empty = list_empty(&vdev->priv_rx); - spin_unlock(&vdev->priv_lock); + spin_unlock_irqrestore(&vdev->priv_lock, flags); return empty; } diff --git a/drivers/usb/usbip/vhci_sysfs.c b/drivers/usb/usbip/vhci_sysfs.c index 211f43f..5b5462e 100644 --- a/drivers/usb/usbip/vhci_sysfs.c +++ b/drivers/usb/usbip/vhci_sysfs.c @@ -32,10 +32,11 @@ static ssize_t status_show(struct device *dev, struct device_attribute *attr, { char *s = out; int i = 0; + unsigned long flags; BUG_ON(!the_controller || !out); - spin_lock(&the_controller->lock); + spin_lock_irqsave(&the_controller->lock, flags); /* * output example: @@ -70,7 +71,7 @@ static ssize_t status_show(struct device *dev, struct device_attribute *attr, spin_unlock(&vdev->ud.lock); } - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); return out - s; } @@ -80,11 +81,12 @@ static DEVICE_ATTR_RO(status); static int vhci_port_disconnect(__u32 rhport) { struct vhci_device *vdev; + unsigned long flags; usbip_dbg_vhci_sysfs("enter\n"); /* lock */ - spin_lock(&the_controller->lock); + spin_lock_irqsave(&the_controller->lock, flags); vdev = port_to_vdev(rhport); @@ -94,14 +96,14 @@ static int vhci_port_disconnect(__u32 rhport) /* unlock */ spin_unlock(&vdev->ud.lock); - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); return -EINVAL; } /* unlock */ spin_unlock(&vdev->ud.lock); - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); usbip_event_add(&vdev->ud, VDEV_EVENT_DOWN); @@ -177,6 +179,7 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr, int sockfd = 0; __u32 rhport = 0, devid = 0, speed = 0; int err; + unsigned long flags; /* * @rhport: port number of vhci_hcd @@ -202,14 +205,14 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr, /* now need lock until setting vdev status as used */ /* begin a lock */ - spin_lock(&the_controller->lock); + spin_lock_irqsave(&the_controller->lock, flags); vdev = port_to_vdev(rhport); spin_lock(&vdev->ud.lock); if (vdev->ud.status != VDEV_ST_NULL) { /* end of the lock */ spin_unlock(&vdev->ud.lock); - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); sockfd_put(socket); @@ -227,7 +230,7 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr, vdev->ud.status = VDEV_ST_NOTASSIGNED; spin_unlock(&vdev->ud.lock); - spin_unlock(&the_controller->lock); + spin_unlock_irqrestore(&the_controller->lock, flags); /* end the lock */ vdev->ud.tcp_rx = kthread_get_run(vhci_rx_loop, &vdev->ud, "vhci_rx"); diff --git a/drivers/usb/usbip/vhci_tx.c b/drivers/usb/usbip/vhci_tx.c index 409fd99..3e7878f 100644 --- a/drivers/usb/usbip/vhci_tx.c +++ b/drivers/usb/usbip/vhci_tx.c @@ -47,16 +47,17 @@ static void setup_cmd_submit_pdu(struct usbip_header *pdup, struct urb *urb) static struct vhci_priv *dequeue_from_priv_tx(struct vhci_device *vdev) { struct vhci_priv *priv, *tmp; + unsigned long flags; - spin_lock(&vdev->priv_lock); + spin_lock_irqsave(&vdev->priv_lock, flags); list_for_each_entry_safe(priv, tmp, &vdev->priv_tx, list) { list_move_tail(&priv->list, &vdev->priv_rx); - spin_unlock(&vdev->priv_lock); + spin_unlock_irqrestore(&vdev->priv_lock, flags); return priv; } - spin_unlock(&vdev->priv_lock); + spin_unlock_irqrestore(&vdev->priv_lock, flags); return NULL; } @@ -136,16 +137,17 @@ static int vhci_send_cmd_submit(struct vhci_device *vdev) static struct vhci_unlink *dequeue_from_unlink_tx(struct vhci_device *vdev) { struct vhci_unlink *unlink, *tmp; + unsigned long flags; - spin_lock(&vdev->priv_lock); + spin_lock_irqsave(&vdev->priv_lock, flags); list_for_each_entry_safe(unlink, tmp, &vdev->unlink_tx, list) { list_move_tail(&unlink->list, &vdev->unlink_rx); - spin_unlock(&vdev->priv_lock); + spin_unlock_irqrestore(&vdev->priv_lock, flags); return unlink; } - spin_unlock(&vdev->priv_lock); + spin_unlock_irqrestore(&vdev->priv_lock, flags); return NULL; } diff --git a/drivers/usb/wusbcore/wusbhc.h b/drivers/usb/wusbcore/wusbhc.h index 41838db..8c5bd00 100644 --- a/drivers/usb/wusbcore/wusbhc.h +++ b/drivers/usb/wusbcore/wusbhc.h @@ -336,7 +336,7 @@ static inline struct usb_hcd *usb_hcd_get_by_usb_dev(struct usb_device *usb_dev) { struct usb_hcd *usb_hcd; - usb_hcd = container_of(usb_dev->bus, struct usb_hcd, self); + usb_hcd = bus_to_hcd(usb_dev->bus); return usb_get_hcd(usb_hcd); } diff --git a/include/linux/device.h b/include/linux/device.h index 6d6f1fe..2d0e6e5 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -958,6 +958,11 @@ static inline void device_lock(struct device *dev) mutex_lock(&dev->mutex); } +static inline int device_lock_interruptible(struct device *dev) +{ + return mutex_lock_interruptible(&dev->mutex); +} + static inline int device_trylock(struct device *dev) { return mutex_trylock(&dev->mutex); diff --git a/include/linux/usb.h b/include/linux/usb.h index 89533ba..6a9a0c2 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -50,6 +50,7 @@ struct ep_device; * struct usb_host_endpoint - host-side endpoint descriptor and queue * @desc: descriptor for this endpoint, wMaxPacketSize in native byteorder * @ss_ep_comp: SuperSpeed companion descriptor for this endpoint + * @ssp_isoc_ep_comp: SuperSpeedPlus isoc companion descriptor for this endpoint * @urb_list: urbs queued to this endpoint; maintained by usbcore * @hcpriv: for use by HCD; typically holds hardware dma queue head (QH) * with one or more transfer descriptors (TDs) per urb @@ -65,6 +66,7 @@ struct ep_device; struct usb_host_endpoint { struct usb_endpoint_descriptor desc; struct usb_ss_ep_comp_descriptor ss_ep_comp; + struct usb_ssp_isoc_ep_comp_descriptor ssp_isoc_ep_comp; struct list_head urb_list; void *hcpriv; struct ep_device *ep_dev; /* For sysfs info */ @@ -330,6 +332,7 @@ struct usb_host_bos { struct usb_ss_cap_descriptor *ss_cap; struct usb_ssp_cap_descriptor *ssp_cap; struct usb_ss_container_id_descriptor *ss_id; + struct usb_ptm_cap_descriptor *ptm_cap; }; int __usb_get_extra_descriptor(char *buffer, unsigned size, @@ -375,7 +378,6 @@ struct usb_bus { struct usb_devmap devmap; /* device address allocation map */ struct usb_device *root_hub; /* Root hub */ struct usb_bus *hs_companion; /* Companion EHCI bus, if any */ - struct list_head bus_list; /* list of busses */ struct mutex usb_address0_mutex; /* unaddressed device mutex */ @@ -642,9 +644,10 @@ extern struct usb_device *usb_hub_find_child(struct usb_device *hdev, if (!child) continue; else /* USB device locking */ -#define usb_lock_device(udev) device_lock(&(udev)->dev) -#define usb_unlock_device(udev) device_unlock(&(udev)->dev) -#define usb_trylock_device(udev) device_trylock(&(udev)->dev) +#define usb_lock_device(udev) device_lock(&(udev)->dev) +#define usb_unlock_device(udev) device_unlock(&(udev)->dev) +#define usb_lock_device_interruptible(udev) device_lock_interruptible(&(udev)->dev) +#define usb_trylock_device(udev) device_trylock(&(udev)->dev) extern int usb_lock_device_for_reset(struct usb_device *udev, const struct usb_interface *iface); diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 1074b89..2b81b24 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -126,6 +126,10 @@ struct usb_os_desc_table { * string identifiers assigned during @bind(). If this * pointer is null after initiation, the function will not * be available at super speed. + * @ssp_descriptors: Table of super speed plus descriptors, using + * interface and string identifiers assigned during @bind(). If + * this pointer is null after initiation, the function will not + * be available at super speed plus. * @config: assigned when @usb_add_function() is called; this is the * configuration with which this function is associated. * @os_desc_table: Table of (interface id, os descriptors) pairs. The function @@ -186,6 +190,7 @@ struct usb_function { struct usb_descriptor_header **fs_descriptors; struct usb_descriptor_header **hs_descriptors; struct usb_descriptor_header **ss_descriptors; + struct usb_descriptor_header **ssp_descriptors; struct usb_configuration *config; @@ -317,6 +322,7 @@ struct usb_configuration { unsigned superspeed:1; unsigned highspeed:1; unsigned fullspeed:1; + unsigned superspeed_plus:1; struct usb_function *interface[MAX_CONFIG_INTERFACES]; }; diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index d82d006..5d4e151 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -595,6 +595,10 @@ struct usb_gadget_ops { * only supports HNP on a different root port. * @b_hnp_enable: OTG device feature flag, indicating that the A-Host * enabled HNP support. + * @hnp_polling_support: OTG device feature flag, indicating if the OTG device + * in peripheral mode can support HNP polling. + * @host_request_flag: OTG device feature flag, indicating if A-Peripheral + * or B-Peripheral wants to take host role. * @quirk_ep_out_aligned_size: epout requires buffer size to be aligned to * MaxPacketSize. * @is_selfpowered: if the gadget is self-powered. @@ -642,6 +646,8 @@ struct usb_gadget { unsigned b_hnp_enable:1; unsigned a_hnp_support:1; unsigned a_alt_hnp_support:1; + unsigned hnp_polling_support:1; + unsigned host_request_flag:1; unsigned quirk_ep_out_aligned_size:1; unsigned quirk_altset_not_supp:1; unsigned quirk_stall_not_supp:1; @@ -729,6 +735,16 @@ static inline int gadget_is_superspeed(struct usb_gadget *g) } /** + * gadget_is_superspeed_plus() - return true if the hardware handles + * superspeed plus + * @g: controller that might support superspeed plus + */ +static inline int gadget_is_superspeed_plus(struct usb_gadget *g) +{ + return g->max_speed >= USB_SPEED_SUPER_PLUS; +} + +/** * gadget_is_otg - return true iff the hardware is OTG-ready * @g: controller that might have a Mini-AB connector * @@ -1126,6 +1142,7 @@ extern int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget, void (*release)(struct device *dev)); extern int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget); extern void usb_del_gadget_udc(struct usb_gadget *gadget); +extern char *usb_get_gadget_udc_name(void); /*-------------------------------------------------------------------------*/ @@ -1194,7 +1211,8 @@ struct usb_function; 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); void usb_free_all_descriptors(struct usb_function *f); struct usb_descriptor_header *usb_otg_descriptor_alloc( diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 4dcf844..b98f831 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -23,6 +23,7 @@ #include <linux/rwsem.h> #include <linux/interrupt.h> +#include <linux/idr.h> #define MAX_TOPO_LEVEL 6 @@ -630,8 +631,8 @@ extern void usb_set_device_state(struct usb_device *udev, /* exported only within usbcore */ -extern struct list_head usb_bus_list; -extern struct mutex usb_bus_list_lock; +extern struct idr usb_bus_idr; +extern struct mutex usb_bus_idr_lock; extern wait_queue_head_t usb_kill_urb_queue; diff --git a/include/linux/usb/msm_hsusb_hw.h b/include/linux/usb/msm_hsusb_hw.h index e159b39..974c379 100644 --- a/include/linux/usb/msm_hsusb_hw.h +++ b/include/linux/usb/msm_hsusb_hw.h @@ -22,6 +22,7 @@ #define USB_AHBBURST (MSM_USB_BASE + 0x0090) #define USB_AHBMODE (MSM_USB_BASE + 0x0098) #define USB_GENCONFIG_2 (MSM_USB_BASE + 0x00a0) +#define ULPI_TX_PKT_EN_CLR_FIX BIT(19) #define USB_CAPLENGTH (MSM_USB_BASE + 0x0100) /* 8 bit */ diff --git a/include/linux/usb/musb.h b/include/linux/usb/musb.h index 96ddfb7..0b3da40 100644 --- a/include/linux/usb/musb.h +++ b/include/linux/usb/musb.h @@ -124,7 +124,7 @@ struct musb_hdrc_platform_data { int (*set_power)(int state); /* MUSB configuration-specific details */ - struct musb_hdrc_config *config; + const struct musb_hdrc_config *config; /* Architecture specific board data */ void *board_data; diff --git a/include/linux/usb/of.h b/include/linux/usb/of.h index 974bce9..de3237f 100644 --- a/include/linux/usb/of.h +++ b/include/linux/usb/of.h @@ -16,6 +16,8 @@ enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *phy_np); bool of_usb_host_tpl_support(struct device_node *np); int of_usb_update_otg_caps(struct device_node *np, struct usb_otg_caps *otg_caps); +struct device_node *usb_of_get_child_node(struct device_node *parent, + int portnum); #else static inline enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *phy_np) @@ -31,6 +33,11 @@ static inline int of_usb_update_otg_caps(struct device_node *np, { return 0; } +static inline struct device_node *usb_of_get_child_node + (struct device_node *parent, int portnum) +{ + return NULL; +} #endif #if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_USB_SUPPORT) diff --git a/include/linux/usb/otg-fsm.h b/include/linux/usb/otg-fsm.h index f728f18..24198e1 100644 --- a/include/linux/usb/otg-fsm.h +++ b/include/linux/usb/otg-fsm.h @@ -40,6 +40,18 @@ #define PROTO_HOST (1) #define PROTO_GADGET (2) +#define OTG_STS_SELECTOR 0xF000 /* OTG status selector, according to + * OTG and EH 2.0 Chapter 6.2.3 + * Table:6-4 + */ + +#define HOST_REQUEST_FLAG 1 /* Host request flag, according to + * OTG and EH 2.0 Charpter 6.2.3 + * Table:6-5 + */ + +#define T_HOST_REQ_POLL (1500) /* 1500ms, HNP polling interval */ + enum otg_fsm_timer { /* Standard OTG timers */ A_WAIT_VRISE, @@ -48,6 +60,7 @@ enum otg_fsm_timer { A_AIDL_BDIS, B_ASE0_BRST, A_BIDL_ADIS, + B_AIDL_BDIS, /* Auxiliary timers */ B_SE0_SRP, @@ -119,6 +132,8 @@ struct otg_fsm { /* Current usb protocol used: 0:undefine; 1:host; 2:client */ int protocol; struct mutex lock; + u8 *host_req_flag; + struct delayed_work hnp_polling_work; }; struct otg_fsm_ops { diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h index 4db191f..00a47d0 100644 --- a/include/linux/usb/renesas_usbhs.h +++ b/include/linux/usb/renesas_usbhs.h @@ -184,6 +184,7 @@ struct renesas_usbhs_driver_param { }; #define USBHS_TYPE_RCAR_GEN2 1 +#define USBHS_TYPE_RCAR_GEN3 2 /* * option: diff --git a/include/linux/usb/storage.h b/include/linux/usb/storage.h index cb33fff..305ee8d 100644 --- a/include/linux/usb/storage.h +++ b/include/linux/usb/storage.h @@ -45,9 +45,9 @@ #define USB_PR_DEVICE 0xff /* Use device's value */ - /* - * Bulk only data structures - */ +/* + * Bulk only data structures + */ /* command block wrapper */ struct bulk_cb_wrap { @@ -56,18 +56,18 @@ struct bulk_cb_wrap { __le32 DataTransferLength; /* size of data */ __u8 Flags; /* direction in bit 0 */ __u8 Lun; /* LUN normally 0 */ - __u8 Length; /* of of the CDB */ + __u8 Length; /* length of the CDB */ __u8 CDB[16]; /* max command */ }; #define US_BULK_CB_WRAP_LEN 31 -#define US_BULK_CB_SIGN 0x43425355 /*spells out USBC */ +#define US_BULK_CB_SIGN 0x43425355 /* spells out 'USBC' */ #define US_BULK_FLAG_IN (1 << 7) #define US_BULK_FLAG_OUT 0 /* command status wrapper */ struct bulk_cs_wrap { - __le32 Signature; /* should = 'USBS' */ + __le32 Signature; /* contains 'USBS' */ __u32 Tag; /* same as original command */ __le32 Residue; /* amount not transferred */ __u8 Status; /* see below */ diff --git a/include/uapi/linux/usb/ch11.h b/include/uapi/linux/usb/ch11.h index 331499d..361297e 100644 --- a/include/uapi/linux/usb/ch11.h +++ b/include/uapi/linux/usb/ch11.h @@ -30,6 +30,14 @@ #define USB_RT_PORT (USB_TYPE_CLASS | USB_RECIP_OTHER) /* + * Port status type for GetPortStatus requests added in USB 3.1 + * See USB 3.1 spec Table 10-12 + */ +#define HUB_PORT_STATUS 0 +#define HUB_PORT_PD_STATUS 1 +#define HUB_EXT_PORT_STATUS 2 + +/* * Hub class requests * See USB 2.0 spec Table 11-16 */ @@ -97,10 +105,13 @@ /* * Hub Status and Hub Change results * See USB 2.0 spec Table 11-19 and Table 11-20 + * USB 3.1 extends the port status request and may return 4 additional bytes. + * See USB 3.1 spec section 10.16.2.6 Table 10-12 and 10-15 */ struct usb_port_status { __le16 wPortStatus; __le16 wPortChange; + __le32 dwExtPortStatus; } __attribute__ ((packed)); /* @@ -173,6 +184,16 @@ struct usb_port_status { #define USB_PORT_STAT_C_CONFIG_ERROR 0x0080 /* + * USB 3.1 dwExtPortStatus field masks + * See USB 3.1 spec 10.16.2.6.3 Table 10-15 + */ + +#define USB_EXT_PORT_STAT_RX_SPEED_ID 0x0000000f +#define USB_EXT_PORT_STAT_TX_SPEED_ID 0x000000f0 +#define USB_EXT_PORT_STAT_RX_LANES 0x00000f00 +#define USB_EXT_PORT_STAT_TX_LANES 0x0000f000 + +/* * wHubCharacteristics (masks) * See USB 2.0 spec Table 11-13, offset 3 */ diff --git a/include/uapi/linux/usb/ch9.h b/include/uapi/linux/usb/ch9.h index 4338eb7..06d6c62 100644 --- a/include/uapi/linux/usb/ch9.h +++ b/include/uapi/linux/usb/ch9.h @@ -234,6 +234,8 @@ struct usb_ctrlrequest { #define USB_DT_PIPE_USAGE 0x24 /* From the USB 3.0 spec */ #define USB_DT_SS_ENDPOINT_COMP 0x30 +/* From the USB 3.1 spec */ +#define USB_DT_SSP_ISOC_ENDPOINT_COMP 0x31 /* Conventional codes for class-specific descriptors. The convention is * defined in the USB "Common Class" Spec (3.11). Individual class specs @@ -613,6 +615,20 @@ static inline int usb_endpoint_interrupt_type( /*-------------------------------------------------------------------------*/ +/* USB_DT_SSP_ISOC_ENDPOINT_COMP: SuperSpeedPlus Isochronous Endpoint Companion + * descriptor + */ +struct usb_ssp_isoc_ep_comp_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __le16 wReseved; + __le32 dwBytesPerInterval; +} __attribute__ ((packed)); + +#define USB_DT_SSP_ISOC_EP_COMP_SIZE 8 + +/*-------------------------------------------------------------------------*/ + /* USB_DT_SS_ENDPOINT_COMP: SuperSpeed Endpoint Companion descriptor */ struct usb_ss_ep_comp_descriptor { __u8 bLength; @@ -646,6 +662,8 @@ usb_ss_max_streams(const struct usb_ss_ep_comp_descriptor *comp) /* Bits 1:0 of bmAttributes if this is an isoc endpoint */ #define USB_SS_MULT(p) (1 + ((p) & 0x3)) +/* Bit 7 of bmAttributes if a SSP isoc endpoint companion descriptor exists */ +#define USB_SS_SSP_ISOC_COMP(p) ((p) & (1 << 7)) /*-------------------------------------------------------------------------*/ @@ -690,6 +708,7 @@ struct usb_otg20_descriptor { #define USB_OTG_HNP (1 << 1) /* swap host/device roles */ #define USB_OTG_ADP (1 << 2) /* support ADP */ +#define OTG_STS_SELECTOR 0xF000 /* OTG status selector */ /*-------------------------------------------------------------------------*/ /* USB_DT_DEBUG: for special highspeed devices, replacing serial console */ @@ -894,6 +913,22 @@ struct usb_ssp_cap_descriptor { #define USB_SSP_SUBLINK_SPEED_LSM (0xff << 16) /* Lanespeed mantissa */ } __attribute__((packed)); +/* + * Precision time measurement capability descriptor: advertised by devices and + * hubs that support PTM + */ +#define USB_PTM_CAP_TYPE 0xb +struct usb_ptm_cap_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDevCapabilityType; +} __attribute__((packed)); + +/* + * The size of the descriptor for the Sublink Speed Attribute Count + * (SSAC) specified in bmAttributes[4:0]. + */ +#define USB_DT_USB_SSP_CAP_SIZE(ssac) (16 + ssac * 4) /*-------------------------------------------------------------------------*/ @@ -954,6 +989,7 @@ enum usb_device_speed { USB_SPEED_HIGH, /* usb 2.0 */ USB_SPEED_WIRELESS, /* wireless (usb 2.5) */ USB_SPEED_SUPER, /* usb 3.0 */ + USB_SPEED_SUPER_PLUS, /* usb 3.1 */ }; diff --git a/include/uapi/linux/usb/tmc.h b/include/uapi/linux/usb/tmc.h index c045ae1..2e59d9c 100644 --- a/include/uapi/linux/usb/tmc.h +++ b/include/uapi/linux/usb/tmc.h @@ -2,12 +2,14 @@ * Copyright (C) 2007 Stefan Kopp, Gechingen, Germany * Copyright (C) 2008 Novell, Inc. * Copyright (C) 2008 Greg Kroah-Hartman <gregkh@suse.de> + * Copyright (C) 2015 Dave Penkler <dpenkler@gmail.com> * * This file holds USB constants defined by the USB Device Class - * Definition for Test and Measurement devices published by the USB-IF. + * and USB488 Subclass Definitions for Test and Measurement devices + * published by the USB-IF. * - * It also has the ioctl definitions for the usbtmc kernel driver that - * userspace needs to know about. + * It also has the ioctl and capability definitions for the + * usbtmc kernel driver that userspace needs to know about. */ #ifndef __LINUX_USB_TMC_H @@ -30,6 +32,10 @@ #define USBTMC_REQUEST_CHECK_CLEAR_STATUS 6 #define USBTMC_REQUEST_GET_CAPABILITIES 7 #define USBTMC_REQUEST_INDICATOR_PULSE 64 +#define USBTMC488_REQUEST_READ_STATUS_BYTE 128 +#define USBTMC488_REQUEST_REN_CONTROL 160 +#define USBTMC488_REQUEST_GOTO_LOCAL 161 +#define USBTMC488_REQUEST_LOCAL_LOCKOUT 162 /* Request values for USBTMC driver's ioctl entry point */ #define USBTMC_IOC_NR 91 @@ -39,5 +45,22 @@ #define USBTMC_IOCTL_ABORT_BULK_IN _IO(USBTMC_IOC_NR, 4) #define USBTMC_IOCTL_CLEAR_OUT_HALT _IO(USBTMC_IOC_NR, 6) #define USBTMC_IOCTL_CLEAR_IN_HALT _IO(USBTMC_IOC_NR, 7) +#define USBTMC488_IOCTL_GET_CAPS _IOR(USBTMC_IOC_NR, 17, unsigned char) +#define USBTMC488_IOCTL_READ_STB _IOR(USBTMC_IOC_NR, 18, unsigned char) +#define USBTMC488_IOCTL_REN_CONTROL _IOW(USBTMC_IOC_NR, 19, unsigned char) +#define USBTMC488_IOCTL_GOTO_LOCAL _IO(USBTMC_IOC_NR, 20) +#define USBTMC488_IOCTL_LOCAL_LOCKOUT _IO(USBTMC_IOC_NR, 21) + +/* Driver encoded usb488 capabilities */ +#define USBTMC488_CAPABILITY_TRIGGER 1 +#define USBTMC488_CAPABILITY_SIMPLE 2 +#define USBTMC488_CAPABILITY_REN_CONTROL 2 +#define USBTMC488_CAPABILITY_GOTO_LOCAL 2 +#define USBTMC488_CAPABILITY_LOCAL_LOCKOUT 2 +#define USBTMC488_CAPABILITY_488_DOT_2 4 +#define USBTMC488_CAPABILITY_DT1 16 +#define USBTMC488_CAPABILITY_RL1 32 +#define USBTMC488_CAPABILITY_SR1 64 +#define USBTMC488_CAPABILITY_FULL_SCPI 128 #endif diff --git a/include/uapi/linux/usbdevice_fs.h b/include/uapi/linux/usbdevice_fs.h index 019ba1e..a8653a6 100644 --- a/include/uapi/linux/usbdevice_fs.h +++ b/include/uapi/linux/usbdevice_fs.h @@ -134,6 +134,8 @@ struct usbdevfs_hub_portinfo { #define USBDEVFS_CAP_NO_PACKET_SIZE_LIM 0x04 #define USBDEVFS_CAP_BULK_SCATTER_GATHER 0x08 #define USBDEVFS_CAP_REAP_AFTER_DISCONNECT 0x10 +#define USBDEVFS_CAP_MMAP 0x20 +#define USBDEVFS_CAP_DROP_PRIVILEGES 0x40 /* USBDEVFS_DISCONNECT_CLAIM flags & struct */ @@ -187,5 +189,6 @@ struct usbdevfs_streams { #define USBDEVFS_DISCONNECT_CLAIM _IOR('U', 27, struct usbdevfs_disconnect_claim) #define USBDEVFS_ALLOC_STREAMS _IOR('U', 28, struct usbdevfs_streams) #define USBDEVFS_FREE_STREAMS _IOR('U', 29, struct usbdevfs_streams) +#define USBDEVFS_DROP_PRIVILEGES _IOW('U', 30, __u32) #endif /* _UAPI_LINUX_USBDEVICE_FS_H */ |