Felipe writes: USB: changes for v5.7 merge window Lots of changes on dwc3 this time, most of them from Thinh fixing a bunch of really old mishaps on the driver. DWC2 got support for STM32MP15 and a couple RockChip SoCs while DWC3 learned about Amlogic A1 family. Apart from these, we have a few spelling fixes and other minor non-critical fixes all over the place. Signed-off-by: Felipe Balbi <balbi@kernel.org> * tag 'usb-for-v5.7' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb: (41 commits) dt-bindings: usb: add documentation for aspeed usb-vhub ARM: dts: aspeed-g4: add vhub port and endpoint properties ARM: dts: aspeed-g5: add vhub port and endpoint properties ARM: dts: aspeed-g6: add usb functions usb: gadget: aspeed: add ast2600 vhub support usb: gadget: aspeed: read vhub properties from device tree usb: gadget: aspeed: support per-vhub usb descriptors usb: gadget: f_phonet: Replace zero-length array with flexible-array member usb: gadget: composite: Inform controller driver of self-powered usb: gadget: amd5536udc: fix spelling mistake "reserverd" -> "reserved" udc: s3c-hsudc: Silence warning about supplies during deferred probe usb: dwc2: Silence warning about supplies during deferred probe dt-bindings: usb: dwc2: add compatible property for rk3368 usb dt-bindings: usb: dwc2: add compatible property for rk3328 usb usb: gadget: add raw-gadget interface usb: dwc2: Implement set_selfpowered() usb: dwc3: qcom: Replace <linux/clk-provider.h> by <linux/of_clk.h> usb: dwc3: core: don't do suspend for device mode if already suspended usb: dwc3: Rework resets initialization to be more flexible usb: dwc3: Rework clock initialization to be more flexible ...
Unified · Split
| @@ -22,10 +22,14 @@ description: | · 21 unmodified lines | ||
| 22 | 22 | The DWC3 Glue controls the PHY routing and power, an interrupt line is |
| 23 | 23 | connected to the Glue to serve as OTG ID change detection. |
| 24 | 24 | |
| 25 | + The Amlogic A1 embeds a DWC3 USB IP Core configured for USB2 in | |
| 26 | + host-only mode. | |
| 27 | + | |
| 25 | 28 | properties: |
| 26 | 29 | compatible: |
| 27 | 30 | enum: |
| 28 | 31 | - amlogic,meson-g12a-usb-ctrl |
| 32 | + - amlogic,meson-a1-usb-ctrl | |
| 29 | 33 | |
| 30 | 34 | ranges: true |
| 31 | 35 | |
| @@ -84,6 +88,25 @@ required: · 52 unmodified lines | ||
| 84 | 88 | - phys |
| 85 | 89 | - dr_mode |
| 86 | 90 | |
| 91 | +allOf: | |
| 92 | + - if: | |
| 93 | + properties: | |
| 94 | + compatible: | |
| 95 | + enum: | |
| 96 | + - amlogic,meson-a1-usb-ctrl | |
| 97 | + | |
| 98 | + then: | |
| 99 | + properties: | |
| 100 | + clocks: | |
| 101 | + minItems: 3 | |
| 102 | + clock-names: | |
| 103 | + items: | |
| 104 | + - const: usb_ctrl | |
| 105 | + - const: usb_bus | |
| 106 | + - const: xtal_usb_ctrl | |
| 107 | + required: | |
| 108 | + - clock-names | |
| 109 | + | |
| 87 | 110 | examples: |
| 88 | 111 | - | |
| 89 | 112 | usb: usb@ffe09000 { |
| @@ -0,0 +1,77 @@ | ||
| 1 | +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) | |
| 2 | +# Copyright (c) 2020 Facebook Inc. | |
| 3 | +%YAML 1.2 | |
| 4 | +--- | |
| 5 | +$id: http://devicetree.org/schemas/usb/aspeed,usb-vhub.yaml# | |
| 6 | +$schema: http://devicetree.org/meta-schemas/core.yaml# | |
| 7 | + | |
| 8 | +title: ASPEED USB 2.0 Virtual Hub Controller | |
| 9 | + | |
| 10 | +maintainers: | |
| 11 | + - Benjamin Herrenschmidt <benh@kernel.crashing.org> | |
| 12 | + | |
| 13 | +description: |+ | |
| 14 | + The ASPEED USB 2.0 Virtual Hub Controller implements 1 set of USB Hub | |
| 15 | + register and several sets of Device and Endpoint registers to support | |
| 16 | + the Virtual Hub's downstream USB devices. | |
| 17 | + | |
| 18 | + Supported number of devices and endpoints vary depending on hardware | |
| 19 | + revisions. AST2400 and AST2500 Virtual Hub supports 5 downstream devices | |
| 20 | + and 15 generic endpoints, while AST2600 Virtual Hub supports 7 downstream | |
| 21 | + devices and 21 generic endpoints. | |
| 22 | + | |
| 23 | +properties: | |
| 24 | + compatible: | |
| 25 | + enum: | |
| 26 | + - aspeed,ast2400-usb-vhub | |
| 27 | + - aspeed,ast2500-usb-vhub | |
| 28 | + - aspeed,ast2600-usb-vhub | |
| 29 | + | |
| 30 | + reg: | |
| 31 | + maxItems: 1 | |
| 32 | + | |
| 33 | + clocks: | |
| 34 | + maxItems: 1 | |
| 35 | + | |
| 36 | + interrupts: | |
| 37 | + maxItems: 1 | |
| 38 | + | |
| 39 | + aspeed,vhub-downstream-ports: | |
| 40 | + description: Number of downstream ports supported by the Virtual Hub | |
| 41 | + allOf: | |
| 42 | + - $ref: /schemas/types.yaml#/definitions/uint32 | |
| 43 | + - default: 5 | |
| 44 | + minimum: 1 | |
| 45 | + maximum: 7 | |
| 46 | + | |
| 47 | + aspeed,vhub-generic-endpoints: | |
| 48 | + description: Number of generic endpoints supported by the Virtual Hub | |
| 49 | + allOf: | |
| 50 | + - $ref: /schemas/types.yaml#/definitions/uint32 | |
| 51 | + - default: 15 | |
| 52 | + minimum: 1 | |
| 53 | + maximum: 21 | |
| 54 | + | |
| 55 | +required: | |
| 56 | + - compatible | |
| 57 | + - reg | |
| 58 | + - clocks | |
| 59 | + - interrupts | |
| 60 | + - aspeed,vhub-downstream-ports | |
| 61 | + - aspeed,vhub-generic-endpoints | |
| 62 | + | |
| 63 | +additionalProperties: false | |
| 64 | + | |
| 65 | +examples: | |
| 66 | + - | | |
| 67 | + #include <dt-bindings/clock/aspeed-clock.h> | |
| 68 | + vhub: usb-vhub@1e6a0000 { | |
| 69 | + compatible = "aspeed,ast2500-usb-vhub"; | |
| 70 | + reg = <0x1e6a0000 0x300>; | |
| 71 | + interrupts = <5>; | |
| 72 | + clocks = <&syscon ASPEED_CLK_GATE_USBPORT1CLK>; | |
| 73 | + aspeed,vhub-downstream-ports = <5>; | |
| 74 | + aspeed,vhub-generic-endpoints = <15>; | |
| 75 | + pinctrl-names = "default"; | |
| 76 | + pinctrl-0 = <&pinctrl_usb2ad_default>; | |
| 77 | + }; | |
| @@ -18,27 +18,15 @@ properties: · 17 unmodified lines | ||
| 18 | 18 | - const: rockchip,rk3066-usb |
| 19 | 19 | - const: snps,dwc2 |
| 20 | 20 | - items: |
| 21 | − - const: rockchip,px30-usb | |
| 22 | − - const: rockchip,rk3066-usb | |
| 23 | − - const: snps,dwc2 | |
| 24 | − - items: | |
| 25 | − - const: rockchip,rk3036-usb | |
| 26 | − - const: rockchip,rk3066-usb | |
| 27 | − - const: snps,dwc2 | |
| 28 | − - items: | |
| 29 | − - const: rockchip,rv1108-usb | |
| 30 | − - const: rockchip,rk3066-usb | |
| 31 | − - const: snps,dwc2 | |
| 32 | − - items: | |
| 33 | − - const: rockchip,rk3188-usb | |
| 34 | − - const: rockchip,rk3066-usb | |
| 35 | − - const: snps,dwc2 | |
| 36 | − - items: | |
| 37 | − - const: rockchip,rk3228-usb | |
| 38 | − - const: rockchip,rk3066-usb | |
| 39 | − - const: snps,dwc2 | |
| 40 | − - items: | |
| 41 | − - const: rockchip,rk3288-usb | |
| 21 | + - enum: | |
| 22 | + - rockchip,px30-usb | |
| 23 | + - rockchip,rk3036-usb | |
| 24 | + - rockchip,rk3188-usb | |
| 25 | + - rockchip,rk3228-usb | |
| 26 | + - rockchip,rk3288-usb | |
| 27 | + - rockchip,rk3328-usb | |
| 28 | + - rockchip,rk3368-usb | |
| 29 | + - rockchip,rv1108-usb | |
| 42 | 30 | - const: rockchip,rk3066-usb |
| 43 | 31 | - const: snps,dwc2 |
| 44 | 32 | - const: lantiq,arx100-usb |
| @@ -7,7 +7,8 @@ Required properties: · 6 unmodified lines | ||
| 7 | 7 | - compatible: must be "snps,dwc3" |
| 8 | 8 | - reg : Address and length of the register set for the device |
| 9 | 9 | - interrupts: Interrupts used by the dwc3 controller. |
| 10 | − - clock-names: should contain "ref", "bus_early", "suspend" | |
| 10 | + - clock-names: list of clock names. Ideally should be "ref", | |
| 11 | + "bus_early", "suspend" but may be less or more. | |
| 11 | 12 | - clocks: list of phandle and clock specifier pairs corresponding to |
| 12 | 13 | entries in the clock-names property. |
| 13 | 14 | |
| @@ -36,7 +37,7 @@ Optional properties: · 22 unmodified lines | ||
| 36 | 37 | - phys: from the *Generic PHY* bindings |
| 37 | 38 | - phy-names: from the *Generic PHY* bindings; supported names are "usb2-phy" |
| 38 | 39 | or "usb3-phy". |
| 39 | − - resets: a single pair of phandle and reset specifier | |
| 40 | + - resets: set of phandle and reset specifier pairs | |
| 40 | 41 | - snps,usb2-lpm-disable: indicate if we don't want to enable USB2 HW LPM |
| 41 | 42 | - snps,usb3_lpm_capable: determines if platform is USB3 LPM capable |
| 42 | 43 | - snps,dis-start-transfer-quirk: when set, disable isoc START TRANSFER command |
| @@ -75,6 +76,8 @@ Optional properties: · 32 unmodified lines | ||
| 75 | 76 | from P0 to P1/P2/P3 without delay. |
| 76 | 77 | - snps,dis-tx-ipgap-linecheck-quirk: when set, disable u2mac linestate check |
| 77 | 78 | during HS transmit. |
| 79 | + - snps,parkmode-disable-ss-quirk: when set, all SuperSpeed bus instances in | |
| 80 | + park mode are disabled. | |
| 78 | 81 | - snps,dis_metastability_quirk: when set, disable metastability workaround. |
| 79 | 82 | CAUTION: use only if you are absolutely sure of it. |
| 80 | 83 | - snps,is-utmi-l1-suspend: true when DWC3 asserts output signal |
| @@ -35,6 +35,12 @@ Optional properties: · 34 unmodified lines | ||
| 35 | 35 | the USB data role (USB host or USB device) for a given |
| 36 | 36 | USB connector, such as Type-C, Type-B(micro). |
| 37 | 37 | see connector/usb-connector.txt. |
| 38 | + - role-switch-default-mode: indicating if usb-role-switch is enabled, the | |
| 39 | + device default operation mode of controller while usb | |
| 40 | + role is USB_ROLE_NONE. Valid arguments are "host" and | |
| 41 | + "peripheral". Defaults to "peripheral" if not | |
| 42 | + specified. | |
| 43 | + | |
| 38 | 44 | |
| 39 | 45 | This is an attribute to a USB controller such as: |
| 40 | 46 | |
| @@ -0,0 +1,69 @@ | ||
| 1 | +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) | |
| 2 | +%YAML 1.2 | |
| 3 | +--- | |
| 4 | +$id: http://devicetree.org/schemas/usb/maxim,max3420-udc.yaml# | |
| 5 | +$schema: http://devicetree.org/meta-schemas/core.yaml# | |
| 6 | + | |
| 7 | +title: MAXIM MAX3420/1 USB Peripheral Controller | |
| 8 | + | |
| 9 | +maintainers: | |
| 10 | + - Jassi Brar <jaswinder.singh@linaro.org> | |
| 11 | + | |
| 12 | +description: | | |
| 13 | + The controller provices USB2.0 compliant FullSpeed peripheral | |
| 14 | + implementation over the SPI interface. | |
| 15 | + | |
| 16 | + Specifications about the part can be found at: | |
| 17 | + http://datasheets.maximintegrated.com/en/ds/MAX3420E.pdf | |
| 18 | + | |
| 19 | +properties: | |
| 20 | + compatible: | |
| 21 | + enum: | |
| 22 | + - maxim,max3420-udc | |
| 23 | + - maxim,max3421-udc | |
| 24 | + | |
| 25 | + reg: | |
| 26 | + maxItems: 1 | |
| 27 | + | |
| 28 | + interrupts: | |
| 29 | + items: | |
| 30 | + - description: usb irq from max3420 | |
| 31 | + - description: vbus detection irq | |
| 32 | + minItems: 1 | |
| 33 | + maxItems: 2 | |
| 34 | + | |
| 35 | + interrupt-names: | |
| 36 | + items: | |
| 37 | + - const: udc | |
| 38 | + - const: vbus | |
| 39 | + minItems: 1 | |
| 40 | + maxItems: 2 | |
| 41 | + | |
| 42 | + spi-max-frequency: | |
| 43 | + maximum: 26000000 | |
| 44 | + | |
| 45 | +required: | |
| 46 | + - compatible | |
| 47 | + - reg | |
| 48 | + - interrupts | |
| 49 | + - interrupt-names | |
| 50 | + | |
| 51 | +additionalProperties: false | |
| 52 | + | |
| 53 | +examples: | |
| 54 | + - | | |
| 55 | + #include <dt-bindings/gpio/gpio.h> | |
| 56 | + #include <dt-bindings/interrupt-controller/irq.h> | |
| 57 | + spi0 { | |
| 58 | + #address-cells = <1>; | |
| 59 | + #size-cells = <0>; | |
| 60 | + | |
| 61 | + udc@0 { | |
| 62 | + compatible = "maxim,max3420-udc"; | |
| 63 | + reg = <0>; | |
| 64 | + interrupt-parent = <&gpio>; | |
| 65 | + interrupts = <0 IRQ_TYPE_EDGE_FALLING>, <10 IRQ_TYPE_EDGE_BOTH>; | |
| 66 | + interrupt-names = "udc", "vbus"; | |
| 67 | + spi-max-frequency = <12500000>; | |
| 68 | + }; | |
| 69 | + }; | |
| @@ -22,6 +22,7 @@ USB support · 21 unmodified lines | ||
| 22 | 22 | misc_usbsevseg |
| 23 | 23 | mtouchusb |
| 24 | 24 | ohci |
| 25 | + raw-gadget | |
| 25 | 26 | usbip_protocol |
| 26 | 27 | usbmon |
| 27 | 28 | usb-serial |
| @@ -0,0 +1,61 @@ | ||
| 1 | +============== | |
| 2 | +USB Raw Gadget | |
| 3 | +============== | |
| 4 | + | |
| 5 | +USB Raw Gadget is a kernel module that provides a userspace interface for | |
| 6 | +the USB Gadget subsystem. Essentially it allows to emulate USB devices | |
| 7 | +from userspace. Enabled with CONFIG_USB_RAW_GADGET. Raw Gadget is | |
| 8 | +currently a strictly debugging feature and shouldn't be used in | |
| 9 | +production, use GadgetFS instead. | |
| 10 | + | |
| 11 | +Comparison to GadgetFS | |
| 12 | +~~~~~~~~~~~~~~~~~~~~~~ | |
| 13 | + | |
| 14 | +Raw Gadget is similar to GadgetFS, but provides a more low-level and | |
| 15 | +direct access to the USB Gadget layer for the userspace. The key | |
| 16 | +differences are: | |
| 17 | + | |
| 18 | +1. Every USB request is passed to the userspace to get a response, while | |
| 19 | + GadgetFS responds to some USB requests internally based on the provided | |
| 20 | + descriptors. However note, that the UDC driver might respond to some | |
| 21 | + requests on its own and never forward them to the Gadget layer. | |
| 22 | + | |
| 23 | +2. GadgetFS performs some sanity checks on the provided USB descriptors, | |
| 24 | + while Raw Gadget allows you to provide arbitrary data as responses to | |
| 25 | + USB requests. | |
| 26 | + | |
| 27 | +3. Raw Gadget provides a way to select a UDC device/driver to bind to, | |
| 28 | + while GadgetFS currently binds to the first available UDC. | |
| 29 | + | |
| 30 | +4. Raw Gadget uses predictable endpoint names (handles) across different | |
| 31 | + UDCs (as long as UDCs have enough endpoints of each required transfer | |
| 32 | + type). | |
| 33 | + | |
| 34 | +5. Raw Gadget has ioctl-based interface instead of a filesystem-based one. | |
| 35 | + | |
| 36 | +Userspace interface | |
| 37 | +~~~~~~~~~~~~~~~~~~~ | |
| 38 | + | |
| 39 | +To create a Raw Gadget instance open /dev/raw-gadget. Multiple raw-gadget | |
| 40 | +instances (bound to different UDCs) can be used at the same time. The | |
| 41 | +interaction with the opened file happens through the ioctl() calls, see | |
| 42 | +comments in include/uapi/linux/usb/raw_gadget.h for details. | |
| 43 | + | |
| 44 | +The typical usage of Raw Gadget looks like: | |
| 45 | + | |
| 46 | +1. Open Raw Gadget instance via /dev/raw-gadget. | |
| 47 | +2. Initialize the instance via USB_RAW_IOCTL_INIT. | |
| 48 | +3. Launch the instance with USB_RAW_IOCTL_RUN. | |
| 49 | +4. In a loop issue USB_RAW_IOCTL_EVENT_FETCH calls to receive events from | |
| 50 | + Raw Gadget and react to those depending on what kind of USB device | |
| 51 | + needs to be emulated. | |
| 52 | + | |
| 53 | +Potential future improvements | |
| 54 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| 55 | + | |
| 56 | +- Implement ioctl's for setting/clearing halt status on endpoints. | |
| 57 | + | |
| 58 | +- Reporting more events (suspend, resume, etc.) through | |
| 59 | + USB_RAW_IOCTL_EVENT_FETCH. | |
| 60 | + | |
| 61 | +- Support O_NONBLOCK I/O. | |
| @@ -164,6 +164,8 @@ · 163 unmodified lines | ||
| 164 | 164 | reg = <0x1e6a0000 0x300>; |
| 165 | 165 | interrupts = <5>; |
| 166 | 166 | clocks = <&syscon ASPEED_CLK_GATE_USBPORT1CLK>; |
| 167 | + aspeed,vhub-downstream-ports = <5>; | |
| 168 | + aspeed,vhub-generic-endpoints = <15>; | |
| 167 | 169 | pinctrl-names = "default"; |
| 168 | 170 | pinctrl-0 = <&pinctrl_usb2d_default>; |
| 169 | 171 | status = "disabled"; |
| @@ -195,6 +195,8 @@ · 194 unmodified lines | ||
| 195 | 195 | reg = <0x1e6a0000 0x300>; |
| 196 | 196 | interrupts = <5>; |
| 197 | 197 | clocks = <&syscon ASPEED_CLK_GATE_USBPORT1CLK>; |
| 198 | + aspeed,vhub-downstream-ports = <5>; | |
| 199 | + aspeed,vhub-generic-endpoints = <15>; | |
| 198 | 200 | pinctrl-names = "default"; |
| 199 | 201 | pinctrl-0 = <&pinctrl_usb2ad_default>; |
| 200 | 202 | status = "disabled"; |
| @@ -1112,6 +1112,31 @@ · 1111 unmodified lines | ||
| 1112 | 1112 | groups = "UART9"; |
| 1113 | 1113 | }; |
| 1114 | 1114 | |
| 1115 | + pinctrl_usb2ah_default: usb2ah_default { | |
| 1116 | + function = "USB2AH"; | |
| 1117 | + groups = "USBA"; | |
| 1118 | + }; | |
| 1119 | + | |
| 1120 | + pinctrl_usb2ad_default: usb2ad_default { | |
| 1121 | + function = "USB2AD"; | |
| 1122 | + groups = "USBA"; | |
| 1123 | + }; | |
| 1124 | + | |
| 1125 | + pinctrl_usb2bh_default: usb2bh_default { | |
| 1126 | + function = "USB2BH"; | |
| 1127 | + groups = "USBB"; | |
| 1128 | + }; | |
| 1129 | + | |
| 1130 | + pinctrl_usb2bd_default: usb2bd_default { | |
| 1131 | + function = "USB2BD"; | |
| 1132 | + groups = "USBB"; | |
| 1133 | + }; | |
| 1134 | + | |
| 1135 | + pinctrl_usb11bhid_default: usb11bhid_default { | |
| 1136 | + function = "USB11BHID"; | |
| 1137 | + groups = "USBB"; | |
| 1138 | + }; | |
| 1139 | + | |
| 1115 | 1140 | pinctrl_vb_default: vb_default { |
| 1116 | 1141 | function = "VB"; |
| 1117 | 1142 | groups = "VB"; |
| @@ -245,6 +245,51 @@ · 244 unmodified lines | ||
| 245 | 245 | status = "disabled"; |
| 246 | 246 | }; |
| 247 | 247 | |
| 248 | + ehci0: usb@1e6a1000 { | |
| 249 | + compatible = "aspeed,ast2600-ehci", "generic-ehci"; | |
| 250 | + reg = <0x1e6a1000 0x100>; | |
| 251 | + interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>; | |
| 252 | + clocks = <&syscon ASPEED_CLK_GATE_USBPORT1CLK>; | |
| 253 | + pinctrl-names = "default"; | |
| 254 | + pinctrl-0 = <&pinctrl_usb2ah_default>; | |
| 255 | + status = "disabled"; | |
| 256 | + }; | |
| 257 | + | |
| 258 | + ehci1: usb@1e6a3000 { | |
| 259 | + compatible = "aspeed,ast2600-ehci", "generic-ehci"; | |
| 260 | + reg = <0x1e6a3000 0x100>; | |
| 261 | + interrupts = <GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>; | |
| 262 | + clocks = <&syscon ASPEED_CLK_GATE_USBPORT2CLK>; | |
| 263 | + pinctrl-names = "default"; | |
| 264 | + pinctrl-0 = <&pinctrl_usb2bh_default>; | |
| 265 | + status = "disabled"; | |
| 266 | + }; | |
| 267 | + | |
| 268 | + uhci: usb@1e6b0000 { | |
| 269 | + compatible = "aspeed,ast2600-uhci", "generic-uhci"; | |
| 270 | + reg = <0x1e6b0000 0x100>; | |
| 271 | + interrupts = <GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>; | |
| 272 | + #ports = <2>; | |
| 273 | + clocks = <&syscon ASPEED_CLK_GATE_USBUHCICLK>; | |
| 274 | + status = "disabled"; | |
| 275 | + /* | |
| 276 | + * No default pinmux, it will follow EHCI, use an | |
| 277 | + * explicit pinmux override if EHCI is not enabled. | |
| 278 | + */ | |
| 279 | + }; | |
| 280 | + | |
| 281 | + vhub: usb-vhub@1e6a0000 { | |
| 282 | + compatible = "aspeed,ast2600-usb-vhub"; | |
| 283 | + reg = <0x1e6a0000 0x350>; | |
| 284 | + interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>; | |
| 285 | + clocks = <&syscon ASPEED_CLK_GATE_USBPORT1CLK>; | |
| 286 | + aspeed,vhub-downstream-ports = <7>; | |
| 287 | + aspeed,vhub-generic-endpoints = <21>; | |
| 288 | + pinctrl-names = "default"; | |
| 289 | + pinctrl-0 = <&pinctrl_usb2ad_default>; | |
| 290 | + status = "disabled"; | |
| 291 | + }; | |
| 292 | + | |
| 248 | 293 | apb { |
| 249 | 294 | compatible = "simple-bus"; |
| 250 | 295 | #address-cells = <1>; |
| @@ -411,6 +411,10 @@ enum dwc2_ep0_state { · 410 unmodified lines | ||
| 411 | 411 | * register. |
| 412 | 412 | * 0 - Deactivate the transceiver (default) |
| 413 | 413 | * 1 - Activate the transceiver |
| 414 | + * @activate_stm_id_vb_detection: Activate external ID pin and Vbus level | |
| 415 | + * detection using GGPIO register. | |
| 416 | + * 0 - Deactivate the external level detection (default) | |
| 417 | + * 1 - Activate the external level detection | |
| 414 | 418 | * @g_dma: Enables gadget dma usage (default: autodetect). |
| 415 | 419 | * @g_dma_desc: Enables gadget descriptor DMA (default: autodetect). |
| 416 | 420 | * @g_rx_fifo_size: The periodic rx fifo size for the device, in |
| @@ -481,6 +485,7 @@ struct dwc2_core_params { · 64 unmodified lines | ||
| 481 | 485 | bool service_interval; |
| 482 | 486 | u8 hird_threshold; |
| 483 | 487 | bool activate_stm_fs_transceiver; |
| 488 | + bool activate_stm_id_vb_detection; | |
| 484 | 489 | bool ipg_isoc_en; |
| 485 | 490 | u16 max_packet_count; |
| 486 | 491 | u32 max_transfer_size; |
| @@ -874,6 +879,8 @@ struct dwc2_hregs_backup { · 387 unmodified lines | ||
| 874 | 879 | * removed once all SoCs support usb transceiver. |
| 875 | 880 | * @supplies: Definition of USB power supplies |
| 876 | 881 | * @vbus_supply: Regulator supplying vbus. |
| 882 | + * @usb33d: Optional 3.3v regulator used on some stm32 devices to | |
| 883 | + * supply ID and VBUS detection hardware. | |
| 877 | 884 | * @lock: Spinlock that protects all the driver data structures |
| 878 | 885 | * @priv: Stores a pointer to the struct usb_hcd |
| 879 | 886 | * @queuing_high_bandwidth: True if multiple packets of a high-bandwidth |
| @@ -1061,6 +1068,7 @@ struct dwc2_hsotg { · 181 unmodified lines | ||
| 1061 | 1068 | struct dwc2_hsotg_plat *plat; |
| 1062 | 1069 | struct regulator_bulk_data supplies[DWC2_NUM_SUPPLIES]; |
| 1063 | 1070 | struct regulator *vbus_supply; |
| 1071 | + struct regulator *usb33d; | |
| 1064 | 1072 | |
| 1065 | 1073 | spinlock_t lock; |
| 1066 | 1074 | void *priv; |
| @@ -1646,7 +1646,8 @@ static int dwc2_hsotg_process_req_status(struct dwc2_hsotg *hsotg, · 1645 unmodified lines | ||
| 1646 | 1646 | |
| 1647 | 1647 | switch (ctrl->bRequestType & USB_RECIP_MASK) { |
| 1648 | 1648 | case USB_RECIP_DEVICE: |
| 1649 | − status = 1 << USB_DEVICE_SELF_POWERED; | |
| 1649 | + status = hsotg->gadget.is_selfpowered << | |
| 1650 | + USB_DEVICE_SELF_POWERED; | |
| 1650 | 1651 | status |= hsotg->remote_wakeup_allowed << |
| 1651 | 1652 | USB_DEVICE_REMOTE_WAKEUP; |
| 1652 | 1653 | reply = cpu_to_le16(status); |
| @@ -4527,6 +4528,26 @@ static int dwc2_hsotg_gadget_getframe(struct usb_gadget *gadget) · 2874 unmodified lines | ||
| 4527 | 4528 | return dwc2_hsotg_read_frameno(to_hsotg(gadget)); |
| 4528 | 4529 | } |
| 4529 | 4530 | |
| 4531 | +/** | |
| 4532 | + * dwc2_hsotg_set_selfpowered - set if device is self/bus powered | |
| 4533 | + * @gadget: The usb gadget state | |
| 4534 | + * @is_selfpowered: Whether the device is self-powered | |
| 4535 | + * | |
| 4536 | + * Set if the device is self or bus powered. | |
| 4537 | + */ | |
| 4538 | +static int dwc2_hsotg_set_selfpowered(struct usb_gadget *gadget, | |
| 4539 | + int is_selfpowered) | |
| 4540 | +{ | |
| 4541 | + struct dwc2_hsotg *hsotg = to_hsotg(gadget); | |
| 4542 | + unsigned long flags; | |
| 4543 | + | |
| 4544 | + spin_lock_irqsave(&hsotg->lock, flags); | |
| 4545 | + gadget->is_selfpowered = !!is_selfpowered; | |
| 4546 | + spin_unlock_irqrestore(&hsotg->lock, flags); | |
| 4547 | + | |
| 4548 | + return 0; | |
| 4549 | +} | |
| 4550 | + | |
| 4530 | 4551 | /** |
| 4531 | 4552 | * dwc2_hsotg_pullup - connect/disconnect the USB PHY |
| 4532 | 4553 | * @gadget: The usb gadget state |
| @@ -4618,6 +4639,7 @@ static int dwc2_hsotg_vbus_draw(struct usb_gadget *gadget, unsigned int mA) · 85 unmodified lines | ||
| 4618 | 4639 | |
| 4619 | 4640 | static const struct usb_gadget_ops dwc2_hsotg_gadget_ops = { |
| 4620 | 4641 | .get_frame = dwc2_hsotg_gadget_getframe, |
| 4642 | + .set_selfpowered = dwc2_hsotg_set_selfpowered, | |
| 4621 | 4643 | .udc_start = dwc2_hsotg_udc_start, |
| 4622 | 4644 | .udc_stop = dwc2_hsotg_udc_stop, |
| 4623 | 4645 | .pullup = dwc2_hsotg_pullup, |
| @@ -54,6 +54,12 @@ · 53 unmodified lines | ||
| 54 | 54 | #define GOTGCTL_HSTSETHNPEN BIT(10) |
| 55 | 55 | #define GOTGCTL_HNPREQ BIT(9) |
| 56 | 56 | #define GOTGCTL_HSTNEGSCS BIT(8) |
| 57 | +#define GOTGCTL_BVALOVAL BIT(7) | |
| 58 | +#define GOTGCTL_BVALOEN BIT(6) | |
| 59 | +#define GOTGCTL_AVALOVAL BIT(5) | |
| 60 | +#define GOTGCTL_AVALOEN BIT(4) | |
| 61 | +#define GOTGCTL_VBVALOVAL BIT(3) | |
| 62 | +#define GOTGCTL_VBVALOEN BIT(2) | |
| 57 | 63 | #define GOTGCTL_SESREQ BIT(1) |
| 58 | 64 | #define GOTGCTL_SESREQSCS BIT(0) |
| 59 | 65 | |
| @@ -227,6 +233,8 @@ · 167 unmodified lines | ||
| 227 | 233 | #define GPVNDCTL HSOTG_REG(0x0034) |
| 228 | 234 | #define GGPIO HSOTG_REG(0x0038) |
| 229 | 235 | #define GGPIO_STM32_OTG_GCCFG_PWRDWN BIT(16) |
| 236 | +#define GGPIO_STM32_OTG_GCCFG_VBDEN BIT(21) | |
| 237 | +#define GGPIO_STM32_OTG_GCCFG_IDEN BIT(22) | |
| 230 | 238 | |
| 231 | 239 | #define GUID HSOTG_REG(0x003c) |
| 232 | 240 | #define GSNPSID HSOTG_REG(0x0040) |
| @@ -163,6 +163,35 @@ static void dwc2_set_stm32f7_hsotg_params(struct dwc2_hsotg *hsotg) · 162 unmodified lines | ||
| 163 | 163 | p->host_perio_tx_fifo_size = 256; |
| 164 | 164 | } |
| 165 | 165 | |
| 166 | +static void dwc2_set_stm32mp15_fsotg_params(struct dwc2_hsotg *hsotg) | |
| 167 | +{ | |
| 168 | + struct dwc2_core_params *p = &hsotg->params; | |
| 169 | + | |
| 170 | + p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE; | |
| 171 | + p->speed = DWC2_SPEED_PARAM_FULL; | |
| 172 | + p->host_rx_fifo_size = 128; | |
| 173 | + p->host_nperio_tx_fifo_size = 96; | |
| 174 | + p->host_perio_tx_fifo_size = 96; | |
| 175 | + p->max_packet_count = 256; | |
| 176 | + p->phy_type = DWC2_PHY_TYPE_PARAM_FS; | |
| 177 | + p->i2c_enable = false; | |
| 178 | + p->activate_stm_fs_transceiver = true; | |
| 179 | + p->activate_stm_id_vb_detection = true; | |
| 180 | + p->power_down = DWC2_POWER_DOWN_PARAM_NONE; | |
| 181 | +} | |
| 182 | + | |
| 183 | +static void dwc2_set_stm32mp15_hsotg_params(struct dwc2_hsotg *hsotg) | |
| 184 | +{ | |
| 185 | + struct dwc2_core_params *p = &hsotg->params; | |
| 186 | + | |
| 187 | + p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE; | |
| 188 | + p->activate_stm_id_vb_detection = true; | |
| 189 | + p->host_rx_fifo_size = 440; | |
| 190 | + p->host_nperio_tx_fifo_size = 256; | |
| 191 | + p->host_perio_tx_fifo_size = 256; | |
| 192 | + p->power_down = DWC2_POWER_DOWN_PARAM_NONE; | |
| 193 | +} | |
| 194 | + | |
| 166 | 195 | const struct of_device_id dwc2_of_match_table[] = { |
| 167 | 196 | { .compatible = "brcm,bcm2835-usb", .data = dwc2_set_bcm_params }, |
| 168 | 197 | { .compatible = "hisilicon,hi6220-usb", .data = dwc2_set_his_params }, |
| @@ -186,6 +215,10 @@ const struct of_device_id dwc2_of_match_table[] = { · 17 unmodified lines | ||
| 186 | 215 | { .compatible = "st,stm32f4x9-hsotg" }, |
| 187 | 216 | { .compatible = "st,stm32f7-hsotg", |
| 188 | 217 | .data = dwc2_set_stm32f7_hsotg_params }, |
| 218 | + { .compatible = "st,stm32mp15-fsotg", | |
| 219 | + .data = dwc2_set_stm32mp15_fsotg_params }, | |
| 220 | + { .compatible = "st,stm32mp15-hsotg", | |
| 221 | + .data = dwc2_set_stm32mp15_hsotg_params }, | |
| 189 | 222 | {}, |
| 190 | 223 | }; |
| 191 | 224 | MODULE_DEVICE_TABLE(of, dwc2_of_match_table); |
| @@ -285,7 +285,9 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg) · 284 unmodified lines | ||
| 285 | 285 | ret = devm_regulator_bulk_get(hsotg->dev, ARRAY_SIZE(hsotg->supplies), |
| 286 | 286 | hsotg->supplies); |
| 287 | 287 | if (ret) { |
| 288 | − dev_err(hsotg->dev, "failed to request supplies: %d\n", ret); | |
| 288 | + if (ret != -EPROBE_DEFER) | |
| 289 | + dev_err(hsotg->dev, "failed to request supplies: %d\n", | |
| 290 | + ret); | |
| 289 | 291 | return ret; |
| 290 | 292 | } |
| 291 | 293 | return 0; |
| @@ -312,6 +314,9 @@ static int dwc2_driver_remove(struct platform_device *dev) · 20 unmodified lines | ||
| 312 | 314 | if (hsotg->gadget_enabled) |
| 313 | 315 | dwc2_hsotg_remove(hsotg); |
| 314 | 316 | |
| 317 | + if (hsotg->params.activate_stm_id_vb_detection) | |
| 318 | + regulator_disable(hsotg->usb33d); | |
| 319 | + | |
| 315 | 320 | if (hsotg->ll_hw_enabled) |
| 316 | 321 | dwc2_lowlevel_hw_disable(hsotg); |
| 317 | 322 | |
| @@ -464,10 +469,35 @@ static int dwc2_driver_probe(struct platform_device *dev) · 146 unmodified lines | ||
| 464 | 469 | if (retval) |
| 465 | 470 | goto error; |
| 466 | 471 | |
| 472 | + if (hsotg->params.activate_stm_id_vb_detection) { | |
| 473 | + u32 ggpio; | |
| 474 | + | |
| 475 | + hsotg->usb33d = devm_regulator_get(hsotg->dev, "usb33d"); | |
| 476 | + if (IS_ERR(hsotg->usb33d)) { | |
| 477 | + retval = PTR_ERR(hsotg->usb33d); | |
| 478 | + if (retval != -EPROBE_DEFER) | |
| 479 | + dev_err(hsotg->dev, | |
| 480 | + "failed to request usb33d supply: %d\n", | |
| 481 | + retval); | |
| 482 | + goto error; | |
| 483 | + } | |
| 484 | + retval = regulator_enable(hsotg->usb33d); | |
| 485 | + if (retval) { | |
| 486 | + dev_err(hsotg->dev, | |
| 487 | + "failed to enable usb33d supply: %d\n", retval); | |
| 488 | + goto error; | |
| 489 | + } | |
| 490 | + | |
| 491 | + ggpio = dwc2_readl(hsotg, GGPIO); | |
| 492 | + ggpio |= GGPIO_STM32_OTG_GCCFG_IDEN; | |
| 493 | + ggpio |= GGPIO_STM32_OTG_GCCFG_VBDEN; | |
| 494 | + dwc2_writel(hsotg, ggpio, GGPIO); | |
| 495 | + } | |
| 496 | + | |
| 467 | 497 | if (hsotg->dr_mode != USB_DR_MODE_HOST) { |
| 468 | 498 | retval = dwc2_gadget_init(hsotg); |
| 469 | 499 | if (retval) |
| 470 | − goto error; | |
| 500 | + goto error_init; | |
| 471 | 501 | hsotg->gadget_enabled = 1; |
| 472 | 502 | } |
| 473 | 503 | |
| @@ -493,7 +523,7 @@ static int dwc2_driver_probe(struct platform_device *dev) · 19 unmodified lines | ||
| 493 | 523 | if (retval) { |
| 494 | 524 | if (hsotg->gadget_enabled) |
| 495 | 525 | dwc2_hsotg_remove(hsotg); |
| 496 | − goto error; | |
| 526 | + goto error_init; | |
| 497 | 527 | } |
| 498 | 528 | hsotg->hcd_enabled = 1; |
| 499 | 529 | } |
| @@ -509,6 +539,9 @@ static int dwc2_driver_probe(struct platform_device *dev) · 9 unmodified lines | ||
| 509 | 539 | |
| 510 | 540 | return 0; |
| 511 | 541 | |
| 542 | +error_init: | |
| 543 | + if (hsotg->params.activate_stm_id_vb_detection) | |
| 544 | + regulator_disable(hsotg->usb33d); | |
| 512 | 545 | error: |
| 513 | 546 | dwc2_lowlevel_hw_disable(hsotg); |
| 514 | 547 | return retval; |
| @@ -523,6 +556,37 @@ static int __maybe_unused dwc2_suspend(struct device *dev) · 8 unmodified lines | ||
| 523 | 556 | if (is_device_mode) |
| 524 | 557 | dwc2_hsotg_suspend(dwc2); |
| 525 | 558 | |
| 559 | + if (dwc2->params.activate_stm_id_vb_detection) { | |
| 560 | + unsigned long flags; | |
| 561 | + u32 ggpio, gotgctl; | |
| 562 | + | |
| 563 | + /* | |
| 564 | + * Need to force the mode to the current mode to avoid Mode | |
| 565 | + * Mismatch Interrupt when ID detection will be disabled. | |
| 566 | + */ | |
| 567 | + dwc2_force_mode(dwc2, !is_device_mode); | |
| 568 | + | |
| 569 | + spin_lock_irqsave(&dwc2->lock, flags); | |
| 570 | + gotgctl = dwc2_readl(dwc2, GOTGCTL); | |
| 571 | + /* bypass debounce filter, enable overrides */ | |
| 572 | + gotgctl |= GOTGCTL_DBNCE_FLTR_BYPASS; | |
| 573 | + gotgctl |= GOTGCTL_BVALOEN | GOTGCTL_AVALOEN; | |
| 574 | + /* Force A / B session if needed */ | |
| 575 | + if (gotgctl & GOTGCTL_ASESVLD) | |
| 576 | + gotgctl |= GOTGCTL_AVALOVAL; | |
| 577 | + if (gotgctl & GOTGCTL_BSESVLD) | |
| 578 | + gotgctl |= GOTGCTL_BVALOVAL; | |
| 579 | + dwc2_writel(dwc2, gotgctl, GOTGCTL); | |
| 580 | + spin_unlock_irqrestore(&dwc2->lock, flags); | |
| 581 | + | |
| 582 | + ggpio = dwc2_readl(dwc2, GGPIO); | |
| 583 | + ggpio &= ~GGPIO_STM32_OTG_GCCFG_IDEN; | |
| 584 | + ggpio &= ~GGPIO_STM32_OTG_GCCFG_VBDEN; | |
| 585 | + dwc2_writel(dwc2, ggpio, GGPIO); | |
| 586 | + | |
| 587 | + regulator_disable(dwc2->usb33d); | |
| 588 | + } | |
| 589 | + | |
| 526 | 590 | if (dwc2->ll_hw_enabled && |
| 527 | 591 | (is_device_mode || dwc2_host_can_poweroff_phy(dwc2))) { |
| 528 | 592 | ret = __dwc2_lowlevel_hw_disable(dwc2); |
| @@ -544,6 +608,34 @@ static int __maybe_unused dwc2_resume(struct device *dev) · 15 unmodified lines | ||
| 544 | 608 | } |
| 545 | 609 | dwc2->phy_off_for_suspend = false; |
| 546 | 610 | |
| 611 | + if (dwc2->params.activate_stm_id_vb_detection) { | |
| 612 | + unsigned long flags; | |
| 613 | + u32 ggpio, gotgctl; | |
| 614 | + | |
| 615 | + ret = regulator_enable(dwc2->usb33d); | |
| 616 | + if (ret) | |
| 617 | + return ret; | |
| 618 | + | |
| 619 | + ggpio = dwc2_readl(dwc2, GGPIO); | |
| 620 | + ggpio |= GGPIO_STM32_OTG_GCCFG_IDEN; | |
| 621 | + ggpio |= GGPIO_STM32_OTG_GCCFG_VBDEN; | |
| 622 | + dwc2_writel(dwc2, ggpio, GGPIO); | |
| 623 | + | |
| 624 | + /* ID/VBUS detection startup time */ | |
| 625 | + usleep_range(5000, 7000); | |
| 626 | + | |
| 627 | + spin_lock_irqsave(&dwc2->lock, flags); | |
| 628 | + gotgctl = dwc2_readl(dwc2, GOTGCTL); | |
| 629 | + gotgctl &= ~GOTGCTL_DBNCE_FLTR_BYPASS; | |
| 630 | + gotgctl &= ~(GOTGCTL_BVALOEN | GOTGCTL_AVALOEN | | |
| 631 | + GOTGCTL_BVALOVAL | GOTGCTL_AVALOVAL); | |
| 632 | + dwc2_writel(dwc2, gotgctl, GOTGCTL); | |
| 633 | + spin_unlock_irqrestore(&dwc2->lock, flags); | |
| 634 | + } | |
| 635 | + | |
| 636 | + /* Need to restore FORCEDEVMODE/FORCEHOSTMODE */ | |
| 637 | + dwc2_force_dr_mode(dwc2); | |
| 638 | + | |
| 547 | 639 | if (dwc2_is_device_mode(dwc2)) |
| 548 | 640 | ret = dwc2_hsotg_resume(dwc2); |
| 549 | 641 | |
| @@ -289,12 +289,6 @@ done: · 288 unmodified lines | ||
| 289 | 289 | return 0; |
| 290 | 290 | } |
| 291 | 291 | |
| 292 | −static const struct clk_bulk_data dwc3_core_clks[] = { | |
| 293 | − { .id = "ref" }, | |
| 294 | − { .id = "bus_early" }, | |
| 295 | − { .id = "suspend" }, | |
| 296 | −}; | |
| 297 | − | |
| 298 | 292 | /* |
| 299 | 293 | * dwc3_frame_length_adjustment - Adjusts frame length if required |
| 300 | 294 | * @dwc3: Pointer to our controller context structure |
| @@ -1029,6 +1023,9 @@ static int dwc3_core_init(struct dwc3 *dwc) · 728 unmodified lines | ||
| 1029 | 1023 | if (dwc->dis_tx_ipgap_linecheck_quirk) |
| 1030 | 1024 | reg |= DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS; |
| 1031 | 1025 | |
| 1026 | + if (dwc->parkmode_disable_ss_quirk) | |
| 1027 | + reg |= DWC3_GUCTL1_PARKMODE_DISABLE_SS; | |
| 1028 | + | |
| 1032 | 1029 | dwc3_writel(dwc->regs, DWC3_GUCTL1, reg); |
| 1033 | 1030 | } |
| 1034 | 1031 | |
| @@ -1342,6 +1339,8 @@ static void dwc3_get_properties(struct dwc3 *dwc) · 307 unmodified lines | ||
| 1342 | 1339 | "snps,dis-del-phy-power-chg-quirk"); |
| 1343 | 1340 | dwc->dis_tx_ipgap_linecheck_quirk = device_property_read_bool(dev, |
| 1344 | 1341 | "snps,dis-tx-ipgap-linecheck-quirk"); |
| 1342 | + dwc->parkmode_disable_ss_quirk = device_property_read_bool(dev, | |
| 1343 | + "snps,parkmode-disable-ss-quirk"); | |
| 1345 | 1344 | |
| 1346 | 1345 | dwc->tx_de_emphasis_quirk = device_property_read_bool(dev, |
| 1347 | 1346 | "snps,tx_de_emphasis_quirk"); |
| @@ -1441,11 +1440,6 @@ static int dwc3_probe(struct platform_device *pdev) · 93 unmodified lines | ||
| 1441 | 1440 | if (!dwc) |
| 1442 | 1441 | return -ENOMEM; |
| 1443 | 1442 | |
| 1444 | − dwc->clks = devm_kmemdup(dev, dwc3_core_clks, sizeof(dwc3_core_clks), | |
| 1445 | − GFP_KERNEL); | |
| 1446 | − if (!dwc->clks) | |
| 1447 | − return -ENOMEM; | |
| 1448 | − | |
| 1449 | 1443 | dwc->dev = dev; |
| 1450 | 1444 | |
| 1451 | 1445 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| @@ -1476,22 +1470,23 @@ static int dwc3_probe(struct platform_device *pdev) · 24 unmodified lines | ||
| 1476 | 1470 | |
| 1477 | 1471 | dwc3_get_properties(dwc); |
| 1478 | 1472 | |
| 1479 | − dwc->reset = devm_reset_control_get_optional_shared(dev, NULL); | |
| 1473 | + dwc->reset = devm_reset_control_array_get(dev, true, true); | |
| 1480 | 1474 | if (IS_ERR(dwc->reset)) |
| 1481 | 1475 | return PTR_ERR(dwc->reset); |
| 1482 | 1476 | |
| 1483 | 1477 | if (dev->of_node) { |
| 1484 | − dwc->num_clks = ARRAY_SIZE(dwc3_core_clks); | |
| 1485 | − | |
| 1486 | − ret = devm_clk_bulk_get(dev, dwc->num_clks, dwc->clks); | |
| 1478 | + ret = devm_clk_bulk_get_all(dev, &dwc->clks); | |
| 1487 | 1479 | if (ret == -EPROBE_DEFER) |
| 1488 | 1480 | return ret; |
| 1489 | 1481 | /* |
| 1490 | 1482 | * Clocks are optional, but new DT platforms should support all |
| 1491 | 1483 | * clocks as required by the DT-binding. |
| 1492 | 1484 | */ |
| 1493 | − if (ret) | |
| 1485 | + if (ret < 0) | |
| 1494 | 1486 | dwc->num_clks = 0; |
| 1487 | + else | |
| 1488 | + dwc->num_clks = ret; | |
| 1489 | + | |
| 1495 | 1490 | } |
| 1496 | 1491 | |
| 1497 | 1492 | ret = reset_control_deassert(dwc->reset); |
| @@ -1637,6 +1632,8 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg) · 139 unmodified lines | ||
| 1637 | 1632 | |
| 1638 | 1633 | switch (dwc->current_dr_role) { |
| 1639 | 1634 | case DWC3_GCTL_PRTCAP_DEVICE: |
| 1635 | + if (pm_runtime_suspended(dwc->dev)) | |
| 1636 | + break; | |
| 1640 | 1637 | spin_lock_irqsave(&dwc->lock, flags); |
| 1641 | 1638 | dwc3_gadget_suspend(dwc); |
| 1642 | 1639 | spin_unlock_irqrestore(&dwc->lock, flags); |
| @@ -25,6 +25,7 @@ · 24 unmodified lines | ||
| 25 | 25 | #include <linux/usb/ch9.h> |
| 26 | 26 | #include <linux/usb/gadget.h> |
| 27 | 27 | #include <linux/usb/otg.h> |
| 28 | +#include <linux/usb/role.h> | |
| 28 | 29 | #include <linux/ulpi/interface.h> |
| 29 | 30 | |
| 30 | 31 | #include <linux/phy/phy.h> |
| @@ -249,6 +250,7 @@ · 218 unmodified lines | ||
| 249 | 250 | #define DWC3_GUCTL_HSTINAUTORETRY BIT(14) |
| 250 | 251 | |
| 251 | 252 | /* Global User Control 1 Register */ |
| 253 | +#define DWC3_GUCTL1_PARKMODE_DISABLE_SS BIT(17) | |
| 252 | 254 | #define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28) |
| 253 | 255 | #define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24) |
| 254 | 256 | |
| @@ -953,6 +955,9 @@ struct dwc3_scratchpad_array { · 698 unmodified lines | ||
| 953 | 955 | * @hsphy_mode: UTMI phy mode, one of following: |
| 954 | 956 | * - USBPHY_INTERFACE_MODE_UTMI |
| 955 | 957 | * - USBPHY_INTERFACE_MODE_UTMIW |
| 958 | + * @role_sw: usb_role_switch handle | |
| 959 | + * @role_switch_default_mode: default operation mode of controller while | |
| 960 | + * usb role is USB_ROLE_NONE. | |
| 956 | 961 | * @usb2_phy: pointer to USB2 PHY |
| 957 | 962 | * @usb3_phy: pointer to USB3 PHY |
| 958 | 963 | * @usb2_generic_phy: pointer to USB2 PHY |
| @@ -1024,6 +1029,8 @@ struct dwc3_scratchpad_array { · 65 unmodified lines | ||
| 1024 | 1029 | * change quirk. |
| 1025 | 1030 | * @dis_tx_ipgap_linecheck_quirk: set if we disable u2mac linestate |
| 1026 | 1031 | * check during HS transmit. |
| 1032 | + * @parkmode_disable_ss_quirk: set if we need to disable all SuperSpeed | |
| 1033 | + * instances in park mode. | |
| 1027 | 1034 | * @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk |
| 1028 | 1035 | * @tx_de_emphasis: Tx de-emphasis value |
| 1029 | 1036 | * 0 - -6dB de-emphasis |
| @@ -1086,6 +1093,8 @@ struct dwc3 { · 56 unmodified lines | ||
| 1086 | 1093 | struct extcon_dev *edev; |
| 1087 | 1094 | struct notifier_block edev_nb; |
| 1088 | 1095 | enum usb_phy_interface hsphy_mode; |
| 1096 | + struct usb_role_switch *role_sw; | |
| 1097 | + enum usb_dr_mode role_switch_default_mode; | |
| 1089 | 1098 | |
| 1090 | 1099 | u32 fladj; |
| 1091 | 1100 | u32 irq_gadget; |
| @@ -1215,6 +1224,7 @@ struct dwc3 { · 123 unmodified lines | ||
| 1215 | 1224 | unsigned dis_u2_freeclk_exists_quirk:1; |
| 1216 | 1225 | unsigned dis_del_phy_power_chg_quirk:1; |
| 1217 | 1226 | unsigned dis_tx_ipgap_linecheck_quirk:1; |
| 1227 | + unsigned parkmode_disable_ss_quirk:1; | |
| 1218 | 1228 | |
| 1219 | 1229 | unsigned tx_de_emphasis_quirk:1; |
| 1220 | 1230 | unsigned tx_de_emphasis:2; |
| @@ -476,6 +476,92 @@ static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc) · 475 unmodified lines | ||
| 476 | 476 | return edev; |
| 477 | 477 | } |
| 478 | 478 | |
| 479 | +#if IS_ENABLED(CONFIG_USB_ROLE_SWITCH) | |
| 480 | +#define ROLE_SWITCH 1 | |
| 481 | +static int dwc3_usb_role_switch_set(struct device *dev, enum usb_role role) | |
| 482 | +{ | |
| 483 | + struct dwc3 *dwc = dev_get_drvdata(dev); | |
| 484 | + u32 mode; | |
| 485 | + | |
| 486 | + switch (role) { | |
| 487 | + case USB_ROLE_HOST: | |
| 488 | + mode = DWC3_GCTL_PRTCAP_HOST; | |
| 489 | + break; | |
| 490 | + case USB_ROLE_DEVICE: | |
| 491 | + mode = DWC3_GCTL_PRTCAP_DEVICE; | |
| 492 | + break; | |
| 493 | + default: | |
| 494 | + if (dwc->role_switch_default_mode == USB_DR_MODE_HOST) | |
| 495 | + mode = DWC3_GCTL_PRTCAP_HOST; | |
| 496 | + else | |
| 497 | + mode = DWC3_GCTL_PRTCAP_DEVICE; | |
| 498 | + break; | |
| 499 | + } | |
| 500 | + | |
| 501 | + dwc3_set_mode(dwc, mode); | |
| 502 | + return 0; | |
| 503 | +} | |
| 504 | + | |
| 505 | +static enum usb_role dwc3_usb_role_switch_get(struct device *dev) | |
| 506 | +{ | |
| 507 | + struct dwc3 *dwc = dev_get_drvdata(dev); | |
| 508 | + unsigned long flags; | |
| 509 | + enum usb_role role; | |
| 510 | + | |
| 511 | + spin_lock_irqsave(&dwc->lock, flags); | |
| 512 | + switch (dwc->current_dr_role) { | |
| 513 | + case DWC3_GCTL_PRTCAP_HOST: | |
| 514 | + role = USB_ROLE_HOST; | |
| 515 | + break; | |
| 516 | + case DWC3_GCTL_PRTCAP_DEVICE: | |
| 517 | + role = USB_ROLE_DEVICE; | |
| 518 | + break; | |
| 519 | + case DWC3_GCTL_PRTCAP_OTG: | |
| 520 | + role = dwc->current_otg_role; | |
| 521 | + break; | |
| 522 | + default: | |
| 523 | + if (dwc->role_switch_default_mode == USB_DR_MODE_HOST) | |
| 524 | + role = USB_ROLE_HOST; | |
| 525 | + else | |
| 526 | + role = USB_ROLE_DEVICE; | |
| 527 | + break; | |
| 528 | + } | |
| 529 | + spin_unlock_irqrestore(&dwc->lock, flags); | |
| 530 | + return role; | |
| 531 | +} | |
| 532 | + | |
| 533 | +static int dwc3_setup_role_switch(struct dwc3 *dwc) | |
| 534 | +{ | |
| 535 | + struct usb_role_switch_desc dwc3_role_switch = {NULL}; | |
| 536 | + const char *str; | |
| 537 | + u32 mode; | |
| 538 | + int ret; | |
| 539 | + | |
| 540 | + ret = device_property_read_string(dwc->dev, "role-switch-default-mode", | |
| 541 | + &str); | |
| 542 | + if (ret >= 0 && !strncmp(str, "host", strlen("host"))) { | |
| 543 | + dwc->role_switch_default_mode = USB_DR_MODE_HOST; | |
| 544 | + mode = DWC3_GCTL_PRTCAP_HOST; | |
| 545 | + } else { | |
| 546 | + dwc->role_switch_default_mode = USB_DR_MODE_PERIPHERAL; | |
| 547 | + mode = DWC3_GCTL_PRTCAP_DEVICE; | |
| 548 | + } | |
| 549 | + | |
| 550 | + dwc3_role_switch.fwnode = dev_fwnode(dwc->dev); | |
| 551 | + dwc3_role_switch.set = dwc3_usb_role_switch_set; | |
| 552 | + dwc3_role_switch.get = dwc3_usb_role_switch_get; | |
| 553 | + dwc->role_sw = usb_role_switch_register(dwc->dev, &dwc3_role_switch); | |
| 554 | + if (IS_ERR(dwc->role_sw)) | |
| 555 | + return PTR_ERR(dwc->role_sw); | |
| 556 | + | |
| 557 | + dwc3_set_mode(dwc, mode); | |
| 558 | + return 0; | |
| 559 | +} | |
| 560 | +#else | |
| 561 | +#define ROLE_SWITCH 0 | |
| 562 | +#define dwc3_setup_role_switch(x) 0 | |
| 563 | +#endif | |
| 564 | + | |
| 479 | 565 | int dwc3_drd_init(struct dwc3 *dwc) |
| 480 | 566 | { |
| 481 | 567 | int ret, irq; |
| @@ -484,7 +570,12 @@ int dwc3_drd_init(struct dwc3 *dwc) · 2 unmodified lines | ||
| 484 | 570 | if (IS_ERR(dwc->edev)) |
| 485 | 571 | return PTR_ERR(dwc->edev); |
| 486 | 572 | |
| 487 | − if (dwc->edev) { | |
| 573 | + if (ROLE_SWITCH && | |
| 574 | + device_property_read_bool(dwc->dev, "usb-role-switch")) { | |
| 575 | + ret = dwc3_setup_role_switch(dwc); | |
| 576 | + if (ret < 0) | |
| 577 | + return ret; | |
| 578 | + } else if (dwc->edev) { | |
| 488 | 579 | dwc->edev_nb.notifier_call = dwc3_drd_notifier; |
| 489 | 580 | ret = extcon_register_notifier(dwc->edev, EXTCON_USB_HOST, |
| 490 | 581 | &dwc->edev_nb); |
| @@ -531,6 +622,9 @@ void dwc3_drd_exit(struct dwc3 *dwc) · 40 unmodified lines | ||
| 531 | 622 | { |
| 532 | 623 | unsigned long flags; |
| 533 | 624 | |
| 625 | + if (dwc->role_sw) | |
| 626 | + usb_role_switch_unregister(dwc->role_sw); | |
| 627 | + | |
| 534 | 628 | if (dwc->edev) |
| 535 | 629 | extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST, |
| 536 | 630 | &dwc->edev_nb); |
| @@ -162,6 +162,12 @@ static const struct dwc3_exynos_driverdata exynos5250_drvdata = { · 161 unmodified lines | ||
| 162 | 162 | .suspend_clk_idx = -1, |
| 163 | 163 | }; |
| 164 | 164 | |
| 165 | +static const struct dwc3_exynos_driverdata exynos5420_drvdata = { | |
| 166 | + .clk_names = { "usbdrd30", "usbdrd30_susp_clk"}, | |
| 167 | + .num_clks = 2, | |
| 168 | + .suspend_clk_idx = 1, | |
| 169 | +}; | |
| 170 | + | |
| 165 | 171 | static const struct dwc3_exynos_driverdata exynos5433_drvdata = { |
| 166 | 172 | .clk_names = { "aclk", "susp_clk", "pipe_pclk", "phyclk" }, |
| 167 | 173 | .num_clks = 4, |
| @@ -178,6 +184,9 @@ static const struct of_device_id exynos_dwc3_match[] = { · 10 unmodified lines | ||
| 178 | 184 | { |
| 179 | 185 | .compatible = "samsung,exynos5250-dwusb3", |
| 180 | 186 | .data = &exynos5250_drvdata, |
| 187 | + }, { | |
| 188 | + .compatible = "samsung,exynos5420-dwusb3", | |
| 189 | + .data = &exynos5420_drvdata, | |
| 181 | 190 | }, { |
| 182 | 191 | .compatible = "samsung,exynos5433-dwusb3", |
| 183 | 192 | .data = &exynos5433_drvdata, |
| @@ -107,10 +107,37 @@ static const char *phy_names[PHY_COUNT] = { · 106 unmodified lines | ||
| 107 | 107 | "usb2-phy0", "usb2-phy1", "usb3-phy0", |
| 108 | 108 | }; |
| 109 | 109 | |
| 110 | +static struct clk_bulk_data meson_g12a_clocks[] = { | |
| 111 | + { .id = NULL }, | |
| 112 | +}; | |
| 113 | + | |
| 114 | +static struct clk_bulk_data meson_a1_clocks[] = { | |
| 115 | + { .id = "usb_ctrl" }, | |
| 116 | + { .id = "usb_bus" }, | |
| 117 | + { .id = "xtal_usb_ctrl" }, | |
| 118 | +}; | |
| 119 | + | |
| 120 | +struct dwc3_meson_g12a_drvdata { | |
| 121 | + bool otg_switch_supported; | |
| 122 | + struct clk_bulk_data *clks; | |
| 123 | + int num_clks; | |
| 124 | +}; | |
| 125 | + | |
| 126 | +static struct dwc3_meson_g12a_drvdata g12a_drvdata = { | |
| 127 | + .otg_switch_supported = true, | |
| 128 | + .clks = meson_g12a_clocks, | |
| 129 | + .num_clks = ARRAY_SIZE(meson_g12a_clocks), | |
| 130 | +}; | |
| 131 | + | |
| 132 | +static struct dwc3_meson_g12a_drvdata a1_drvdata = { | |
| 133 | + .otg_switch_supported = false, | |
| 134 | + .clks = meson_a1_clocks, | |
| 135 | + .num_clks = ARRAY_SIZE(meson_a1_clocks), | |
| 136 | +}; | |
| 137 | + | |
| 110 | 138 | struct dwc3_meson_g12a { |
| 111 | 139 | struct device *dev; |
| 112 | 140 | struct regmap *regmap; |
| 113 | − struct clk *clk; | |
| 114 | 141 | struct reset_control *reset; |
| 115 | 142 | struct phy *phys[PHY_COUNT]; |
| 116 | 143 | enum usb_dr_mode otg_mode; |
| @@ -120,6 +147,7 @@ struct dwc3_meson_g12a { · 3 unmodified lines | ||
| 120 | 147 | struct regulator *vbus; |
| 121 | 148 | struct usb_role_switch_desc switch_desc; |
| 122 | 149 | struct usb_role_switch *role_switch; |
| 150 | + const struct dwc3_meson_g12a_drvdata *drvdata; | |
| 123 | 151 | }; |
| 124 | 152 | |
| 125 | 153 | static void dwc3_meson_g12a_usb2_set_mode(struct dwc3_meson_g12a *priv, |
| @@ -151,7 +179,7 @@ static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv) · 25 unmodified lines | ||
| 151 | 179 | U2P_R0_POWER_ON_RESET, |
| 152 | 180 | U2P_R0_POWER_ON_RESET); |
| 153 | 181 | |
| 154 | − if (i == USB2_OTG_PHY) { | |
| 182 | + if (priv->drvdata->otg_switch_supported && i == USB2_OTG_PHY) { | |
| 155 | 183 | regmap_update_bits(priv->regmap, |
| 156 | 184 | U2P_R0 + (U2P_REG_SIZE * i), |
| 157 | 185 | U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS, |
| @@ -295,7 +323,7 @@ static int dwc3_meson_g12a_otg_mode_set(struct dwc3_meson_g12a *priv, · 137 unmodified lines | ||
| 295 | 323 | { |
| 296 | 324 | int ret; |
| 297 | 325 | |
| 298 | − if (!priv->phys[USB2_OTG_PHY]) | |
| 326 | + if (!priv->drvdata->otg_switch_supported || !priv->phys[USB2_OTG_PHY]) | |
| 299 | 327 | return -EINVAL; |
| 300 | 328 | |
| 301 | 329 | if (mode == PHY_MODE_USB_HOST) |
| @@ -381,14 +409,61 @@ static struct device *dwc3_meson_g12_find_child(struct device *dev, · 79 unmodified lines | ||
| 381 | 409 | return &pdev->dev; |
| 382 | 410 | } |
| 383 | 411 | |
| 412 | +static int dwc3_meson_g12a_otg_init(struct platform_device *pdev, | |
| 413 | + struct dwc3_meson_g12a *priv) | |
| 414 | +{ | |
| 415 | + enum phy_mode otg_id; | |
| 416 | + int ret, irq; | |
| 417 | + struct device *dev = &pdev->dev; | |
| 418 | + | |
| 419 | + if (!priv->drvdata->otg_switch_supported) | |
| 420 | + return 0; | |
| 421 | + | |
| 422 | + if (priv->otg_mode == USB_DR_MODE_OTG) { | |
| 423 | + /* Ack irq before registering */ | |
| 424 | + regmap_update_bits(priv->regmap, USB_R5, | |
| 425 | + USB_R5_ID_DIG_IRQ, 0); | |
| 426 | + | |
| 427 | + irq = platform_get_irq(pdev, 0); | |
| 428 | + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, | |
| 429 | + dwc3_meson_g12a_irq_thread, | |
| 430 | + IRQF_ONESHOT, pdev->name, priv); | |
| 431 | + if (ret) | |
| 432 | + return ret; | |
| 433 | + } | |
| 434 | + | |
| 435 | + /* Setup OTG mode corresponding to the ID pin */ | |
| 436 | + if (priv->otg_mode == USB_DR_MODE_OTG) { | |
| 437 | + otg_id = dwc3_meson_g12a_get_id(priv); | |
| 438 | + if (otg_id != priv->otg_phy_mode) { | |
| 439 | + if (dwc3_meson_g12a_otg_mode_set(priv, otg_id)) | |
| 440 | + dev_warn(dev, "Failed to switch OTG mode\n"); | |
| 441 | + } | |
| 442 | + } | |
| 443 | + | |
| 444 | + /* Setup role switcher */ | |
| 445 | + priv->switch_desc.usb2_port = dwc3_meson_g12_find_child(dev, | |
| 446 | + "snps,dwc3"); | |
| 447 | + priv->switch_desc.udc = dwc3_meson_g12_find_child(dev, "snps,dwc2"); | |
| 448 | + priv->switch_desc.allow_userspace_control = true; | |
| 449 | + priv->switch_desc.set = dwc3_meson_g12a_role_set; | |
| 450 | + priv->switch_desc.get = dwc3_meson_g12a_role_get; | |
| 451 | + priv->switch_desc.driver_data = priv; | |
| 452 | + | |
| 453 | + priv->role_switch = usb_role_switch_register(dev, &priv->switch_desc); | |
| 454 | + if (IS_ERR(priv->role_switch)) | |
| 455 | + dev_warn(dev, "Unable to register Role Switch\n"); | |
| 456 | + | |
| 457 | + return 0; | |
| 458 | +} | |
| 459 | + | |
| 384 | 460 | static int dwc3_meson_g12a_probe(struct platform_device *pdev) |
| 385 | 461 | { |
| 386 | 462 | struct dwc3_meson_g12a *priv; |
| 387 | 463 | struct device *dev = &pdev->dev; |
| 388 | 464 | struct device_node *np = dev->of_node; |
| 389 | 465 | void __iomem *base; |
| 390 | − enum phy_mode otg_id; | |
| 391 | − int ret, i, irq; | |
| 466 | + int ret, i; | |
| 392 | 467 | |
| 393 | 468 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); |
| 394 | 469 | if (!priv) |
| @@ -410,17 +485,18 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev) · 15 unmodified lines | ||
| 410 | 485 | priv->vbus = NULL; |
| 411 | 486 | } |
| 412 | 487 | |
| 413 | − priv->clk = devm_clk_get(dev, NULL); | |
| 414 | − if (IS_ERR(priv->clk)) | |
| 415 | − return PTR_ERR(priv->clk); | |
| 488 | + priv->drvdata = of_device_get_match_data(&pdev->dev); | |
| 416 | 489 | |
| 417 | − ret = clk_prepare_enable(priv->clk); | |
| 490 | + ret = devm_clk_bulk_get(dev, | |
| 491 | + priv->drvdata->num_clks, | |
| 492 | + priv->drvdata->clks); | |
| 418 | 493 | if (ret) |
| 419 | 494 | return ret; |
| 420 | 495 | |
| 421 | − devm_add_action_or_reset(dev, | |
| 422 | − (void(*)(void *))clk_disable_unprepare, | |
| 423 | − priv->clk); | |
| 496 | + ret = clk_bulk_prepare_enable(priv->drvdata->num_clks, | |
| 497 | + priv->drvdata->clks); | |
| 498 | + if (ret) | |
| 499 | + return ret; | |
| 424 | 500 | |
| 425 | 501 | platform_set_drvdata(pdev, priv); |
| 426 | 502 | priv->dev = dev; |
| @@ -434,41 +510,28 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev) · 7 unmodified lines | ||
| 434 | 510 | |
| 435 | 511 | ret = reset_control_reset(priv->reset); |
| 436 | 512 | if (ret) |
| 437 | − return ret; | |
| 513 | + goto err_disable_clks; | |
| 438 | 514 | |
| 439 | 515 | ret = dwc3_meson_g12a_get_phys(priv); |
| 440 | 516 | if (ret) |
| 441 | − return ret; | |
| 517 | + goto err_disable_clks; | |
| 442 | 518 | |
| 443 | 519 | if (priv->vbus) { |
| 444 | 520 | ret = regulator_enable(priv->vbus); |
| 445 | 521 | if (ret) |
| 446 | − return ret; | |
| 522 | + goto err_disable_clks; | |
| 447 | 523 | } |
| 448 | 524 | |
| 449 | 525 | /* Get dr_mode */ |
| 450 | 526 | priv->otg_mode = usb_get_dr_mode(dev); |
| 451 | 527 | |
| 452 | − if (priv->otg_mode == USB_DR_MODE_OTG) { | |
| 453 | − /* Ack irq before registering */ | |
| 454 | − regmap_update_bits(priv->regmap, USB_R5, | |
| 455 | − USB_R5_ID_DIG_IRQ, 0); | |
| 456 | − | |
| 457 | − irq = platform_get_irq(pdev, 0); | |
| 458 | − ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, | |
| 459 | − dwc3_meson_g12a_irq_thread, | |
| 460 | − IRQF_ONESHOT, pdev->name, priv); | |
| 461 | − if (ret) | |
| 462 | − return ret; | |
| 463 | − } | |
| 464 | − | |
| 465 | 528 | dwc3_meson_g12a_usb_init(priv); |
| 466 | 529 | |
| 467 | 530 | /* Init PHYs */ |
| 468 | 531 | for (i = 0 ; i < PHY_COUNT ; ++i) { |
| 469 | 532 | ret = phy_init(priv->phys[i]); |
| 470 | 533 | if (ret) |
| 471 | − return ret; | |
| 534 | + goto err_disable_clks; | |
| 472 | 535 | } |
| 473 | 536 | |
| 474 | 537 | /* Set PHY Power */ |
| @@ -479,32 +542,12 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev) · 4 unmodified lines | ||
| 479 | 542 | } |
| 480 | 543 | |
| 481 | 544 | ret = of_platform_populate(np, NULL, NULL, dev); |
| 482 | − if (ret) { | |
| 483 | − clk_disable_unprepare(priv->clk); | |
| 545 | + if (ret) | |
| 484 | 546 | goto err_phys_power; |
| 485 | − } | |
| 486 | 547 | |
| 487 | − /* Setup OTG mode corresponding to the ID pin */ | |
| 488 | − if (priv->otg_mode == USB_DR_MODE_OTG) { | |
| 489 | − otg_id = dwc3_meson_g12a_get_id(priv); | |
| 490 | − if (otg_id != priv->otg_phy_mode) { | |
| 491 | − if (dwc3_meson_g12a_otg_mode_set(priv, otg_id)) | |
| 492 | − dev_warn(dev, "Failed to switch OTG mode\n"); | |
| 493 | − } | |
| 494 | − } | |
| 495 | − | |
| 496 | − /* Setup role switcher */ | |
| 497 | − priv->switch_desc.usb2_port = dwc3_meson_g12_find_child(dev, | |
| 498 | − "snps,dwc3"); | |
| 499 | − priv->switch_desc.udc = dwc3_meson_g12_find_child(dev, "snps,dwc2"); | |
| 500 | − priv->switch_desc.allow_userspace_control = true; | |
| 501 | − priv->switch_desc.set = dwc3_meson_g12a_role_set; | |
| 502 | − priv->switch_desc.get = dwc3_meson_g12a_role_get; | |
| 503 | − priv->switch_desc.driver_data = priv; | |
| 504 | − | |
| 505 | − priv->role_switch = usb_role_switch_register(dev, &priv->switch_desc); | |
| 506 | − if (IS_ERR(priv->role_switch)) | |
| 507 | − dev_warn(dev, "Unable to register Role Switch\n"); | |
| 548 | + ret = dwc3_meson_g12a_otg_init(pdev, priv); | |
| 549 | + if (ret) | |
| 550 | + goto err_phys_power; | |
| 508 | 551 | |
| 509 | 552 | pm_runtime_set_active(dev); |
| 510 | 553 | pm_runtime_enable(dev); |
| @@ -520,6 +563,10 @@ err_phys_exit: · 9 unmodified lines | ||
| 520 | 563 | for (i = 0 ; i < PHY_COUNT ; ++i) |
| 521 | 564 | phy_exit(priv->phys[i]); |
| 522 | 565 | |
| 566 | +err_disable_clks: | |
| 567 | + clk_bulk_disable_unprepare(priv->drvdata->num_clks, | |
| 568 | + priv->drvdata->clks); | |
| 569 | + | |
| 523 | 570 | return ret; |
| 524 | 571 | } |
| 525 | 572 | |
| @@ -529,7 +576,8 @@ static int dwc3_meson_g12a_remove(struct platform_device *pdev) · 3 unmodified lines | ||
| 529 | 576 | struct device *dev = &pdev->dev; |
| 530 | 577 | int i; |
| 531 | 578 | |
| 532 | − usb_role_switch_unregister(priv->role_switch); | |
| 579 | + if (priv->drvdata->otg_switch_supported) | |
| 580 | + usb_role_switch_unregister(priv->role_switch); | |
| 533 | 581 | |
| 534 | 582 | of_platform_depopulate(dev); |
| 535 | 583 | |
| @@ -542,6 +590,9 @@ static int dwc3_meson_g12a_remove(struct platform_device *pdev) · 6 unmodified lines | ||
| 542 | 590 | pm_runtime_put_noidle(dev); |
| 543 | 591 | pm_runtime_set_suspended(dev); |
| 544 | 592 | |
| 593 | + clk_bulk_disable_unprepare(priv->drvdata->num_clks, | |
| 594 | + priv->drvdata->clks); | |
| 595 | + | |
| 545 | 596 | return 0; |
| 546 | 597 | } |
| 547 | 598 | |
| @@ -549,7 +600,8 @@ static int __maybe_unused dwc3_meson_g12a_runtime_suspend(struct device *dev) · 1 unmodified line | ||
| 549 | 600 | { |
| 550 | 601 | struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); |
| 551 | 602 | |
| 552 | − clk_disable(priv->clk); | |
| 603 | + clk_bulk_disable_unprepare(priv->drvdata->num_clks, | |
| 604 | + priv->drvdata->clks); | |
| 553 | 605 | |
| 554 | 606 | return 0; |
| 555 | 607 | } |
| @@ -558,7 +610,8 @@ static int __maybe_unused dwc3_meson_g12a_runtime_resume(struct device *dev) · 2 unmodified lines | ||
| 558 | 610 | { |
| 559 | 611 | struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); |
| 560 | 612 | |
| 561 | − return clk_enable(priv->clk); | |
| 613 | + return clk_bulk_prepare_enable(priv->drvdata->num_clks, | |
| 614 | + priv->drvdata->clks); | |
| 562 | 615 | } |
| 563 | 616 | |
| 564 | 617 | static int __maybe_unused dwc3_meson_g12a_suspend(struct device *dev) |
| @@ -621,7 +674,14 @@ static const struct dev_pm_ops dwc3_meson_g12a_dev_pm_ops = { · 56 unmodified lines | ||
| 621 | 674 | }; |
| 622 | 675 | |
| 623 | 676 | static const struct of_device_id dwc3_meson_g12a_match[] = { |
| 624 | − { .compatible = "amlogic,meson-g12a-usb-ctrl" }, | |
| 677 | + { | |
| 678 | + .compatible = "amlogic,meson-g12a-usb-ctrl", | |
| 679 | + .data = &g12a_drvdata, | |
| 680 | + }, | |
| 681 | + { | |
| 682 | + .compatible = "amlogic,meson-a1-usb-ctrl", | |
| 683 | + .data = &a1_drvdata, | |
| 684 | + }, | |
| 625 | 685 | { /* Sentinel */ } |
| 626 | 686 | }; |
| 627 | 687 | MODULE_DEVICE_TABLE(of, dwc3_meson_g12a_match); |
| @@ -1521,7 +1521,7 @@ static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, struct dwc3_request *r · 1520 unmodified lines | ||
| 1521 | 1521 | for (i = 0; i < req->num_trbs; i++) { |
| 1522 | 1522 | struct dwc3_trb *trb; |
| 1523 | 1523 | |
| 1524 | − trb = req->trb + i; | |
| 1524 | + trb = &dep->trb_pool[dep->trb_dequeue]; | |
| 1525 | 1525 | trb->ctrl &= ~DWC3_TRB_CTRL_HWO; |
| 1526 | 1526 | dwc3_ep_inc_deq(dep); |
| 1527 | 1527 | } |
| @@ -2570,10 +2570,8 @@ static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep, · 1042 unmodified lines | ||
| 2570 | 2570 | |
| 2571 | 2571 | dwc3_gadget_ep_cleanup_completed_requests(dep, event, status); |
| 2572 | 2572 | |
| 2573 | − if (stop) { | |
| 2573 | + if (stop) | |
| 2574 | 2574 | dwc3_stop_active_transfer(dep, true, true); |
| 2575 | − dep->flags = DWC3_EP_ENABLED; | |
| 2576 | − } | |
| 2577 | 2575 | |
| 2578 | 2576 | /* |
| 2579 | 2577 | * WORKAROUND: This is the 2nd half of U1/U2 -> U0 workaround. |
| @@ -1,5 +1,5 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | −/** | |
| 2 | +/* | |
| 3 | 3 | * host.c - DesignWare USB3 DRD Controller Host Glue |
| 4 | 4 | * |
| 5 | 5 | * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com |
| @@ -7,6 +7,7 @@ · 1 unmodified line | ||
| 7 | 7 | * Authors: Felipe Balbi <balbi@ti.com>, |
| 8 | 8 | */ |
| 9 | 9 | |
| 10 | +#include <linux/acpi.h> | |
| 10 | 11 | #include <linux/platform_device.h> |
| 11 | 12 | |
| 12 | 13 | #include "core.h" |
| @@ -75,6 +76,7 @@ int dwc3_host_init(struct dwc3 *dwc) · 62 unmodified lines | ||
| 75 | 76 | } |
| 76 | 77 | |
| 77 | 78 | xhci->dev.parent = dwc->dev; |
| 79 | + ACPI_COMPANION_SET(&xhci->dev, ACPI_COMPANION(dwc->dev)); | |
| 78 | 80 | |
| 79 | 81 | dwc->xhci = xhci; |
| 80 | 82 | |
| @@ -227,6 +227,8 @@ DECLARE_EVENT_CLASS(dwc3_log_trb, · 226 unmodified lines | ||
| 227 | 227 | __field(u32, size) |
| 228 | 228 | __field(u32, ctrl) |
| 229 | 229 | __field(u32, type) |
| 230 | + __field(u32, enqueue) | |
| 231 | + __field(u32, dequeue) | |
| 230 | 232 | ), |
| 231 | 233 | TP_fast_assign( |
| 232 | 234 | __assign_str(name, dep->name); |
| @@ -236,9 +238,12 @@ DECLARE_EVENT_CLASS(dwc3_log_trb, · 3 unmodified lines | ||
| 236 | 238 | __entry->size = trb->size; |
| 237 | 239 | __entry->ctrl = trb->ctrl; |
| 238 | 240 | __entry->type = usb_endpoint_type(dep->endpoint.desc); |
| 241 | + __entry->enqueue = dep->trb_enqueue; | |
| 242 | + __entry->dequeue = dep->trb_dequeue; | |
| 239 | 243 | ), |
| 240 | − TP_printk("%s: trb %p buf %08x%08x size %s%d ctrl %08x (%c%c%c%c:%c%c:%s)", | |
| 241 | − __get_str(name), __entry->trb, __entry->bph, __entry->bpl, | |
| 244 | + TP_printk("%s: trb %p (E%d:D%d) buf %08x%08x size %s%d ctrl %08x (%c%c%c%c:%c%c:%s)", | |
| 245 | + __get_str(name), __entry->trb, __entry->enqueue, | |
| 246 | + __entry->dequeue, __entry->bph, __entry->bpl, | |
| 242 | 247 | ({char *s; |
| 243 | 248 | int pcm = ((__entry->size >> 24) & 3) + 1; |
| 244 | 249 | switch (__entry->type) { |
| @@ -861,6 +861,11 @@ static int set_config(struct usb_composite_dev *cdev, · 860 unmodified lines | ||
| 861 | 861 | else |
| 862 | 862 | power = min(power, 900U); |
| 863 | 863 | done: |
| 864 | + if (power <= USB_SELF_POWER_VBUS_MAX_DRAW) | |
| 865 | + usb_gadget_set_selfpowered(gadget); | |
| 866 | + else | |
| 867 | + usb_gadget_clear_selfpowered(gadget); | |
| 868 | + | |
| 864 | 869 | usb_gadget_vbus_draw(gadget, power); |
| 865 | 870 | if (result >= 0 && cdev->delayed_status) |
| 866 | 871 | result = USB_GADGET_DELAYED_STATUS; |
| @@ -2279,6 +2284,7 @@ void composite_suspend(struct usb_gadget *gadget) · 1412 unmodified lines | ||
| 2279 | 2284 | |
| 2280 | 2285 | cdev->suspended = 1; |
| 2281 | 2286 | |
| 2287 | + usb_gadget_set_selfpowered(gadget); | |
| 2282 | 2288 | usb_gadget_vbus_draw(gadget, 2); |
| 2283 | 2289 | } |
| 2284 | 2290 | |
| @@ -2307,6 +2313,9 @@ void composite_resume(struct usb_gadget *gadget) · 22 unmodified lines | ||
| 2307 | 2313 | else |
| 2308 | 2314 | maxpower = min(maxpower, 900U); |
| 2309 | 2315 | |
| 2316 | + if (maxpower > USB_SELF_POWER_VBUS_MAX_DRAW) | |
| 2317 | + usb_gadget_clear_selfpowered(gadget); | |
| 2318 | + | |
| 2310 | 2319 | usb_gadget_vbus_draw(gadget, maxpower); |
| 2311 | 2320 | } |
| 2312 | 2321 | |
| @@ -516,4 +516,15 @@ config USB_G_WEBCAM · 515 unmodified lines | ||
| 516 | 516 | Say "y" to link the driver statically, or "m" to build a |
| 517 | 517 | dynamically linked module called "g_webcam". |
| 518 | 518 | |
| 519 | +config USB_RAW_GADGET | |
| 520 | + tristate "USB Raw Gadget" | |
| 521 | + help | |
| 522 | + USB Raw Gadget is a kernel module that provides a userspace interface | |
| 523 | + for the USB Gadget subsystem. Essentially it allows to emulate USB | |
| 524 | + devices from userspace. See Documentation/usb/raw-gadget.rst for | |
| 525 | + details. | |
| 526 | + | |
| 527 | + Say "y" to link the driver statically, or "m" to build a | |
| 528 | + dynamically linked module called "raw_gadget". | |
| 529 | + | |
| 519 | 530 | endchoice |
| @@ -43,3 +43,4 @@ obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o · 42 unmodified lines | ||
| 43 | 43 | obj-$(CONFIG_USB_G_NCM) += g_ncm.o |
| 44 | 44 | obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o |
| 45 | 45 | obj-$(CONFIG_USB_GADGET_TARGET) += tcm_usb_gadget.o |
| 46 | +obj-$(CONFIG_USB_RAW_GADGET) += raw_gadget.o | |
| @@ -0,0 +1,1078 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +/* | |
| 3 | + * USB Raw Gadget driver. | |
| 4 | + * See Documentation/usb/raw-gadget.rst for more details. | |
| 5 | + * | |
| 6 | + * Andrey Konovalov <andreyknvl@gmail.com> | |
| 7 | + */ | |
| 8 | + | |
| 9 | +#include <linux/compiler.h> | |
| 10 | +#include <linux/debugfs.h> | |
| 11 | +#include <linux/delay.h> | |
| 12 | +#include <linux/kref.h> | |
| 13 | +#include <linux/miscdevice.h> | |
| 14 | +#include <linux/module.h> | |
| 15 | +#include <linux/semaphore.h> | |
| 16 | +#include <linux/sched.h> | |
| 17 | +#include <linux/slab.h> | |
| 18 | +#include <linux/uaccess.h> | |
| 19 | +#include <linux/wait.h> | |
| 20 | + | |
| 21 | +#include <linux/usb.h> | |
| 22 | +#include <linux/usb/ch9.h> | |
| 23 | +#include <linux/usb/ch11.h> | |
| 24 | +#include <linux/usb/gadget.h> | |
| 25 | + | |
| 26 | +#include <uapi/linux/usb/raw_gadget.h> | |
| 27 | + | |
| 28 | +#define DRIVER_DESC "USB Raw Gadget" | |
| 29 | +#define DRIVER_NAME "raw-gadget" | |
| 30 | + | |
| 31 | +MODULE_DESCRIPTION(DRIVER_DESC); | |
| 32 | +MODULE_AUTHOR("Andrey Konovalov"); | |
| 33 | +MODULE_LICENSE("GPL"); | |
| 34 | + | |
| 35 | +/*----------------------------------------------------------------------*/ | |
| 36 | + | |
| 37 | +#define RAW_EVENT_QUEUE_SIZE 16 | |
| 38 | + | |
| 39 | +struct raw_event_queue { | |
| 40 | + /* See the comment in raw_event_queue_fetch() for locking details. */ | |
| 41 | + spinlock_t lock; | |
| 42 | + struct semaphore sema; | |
| 43 | + struct usb_raw_event *events[RAW_EVENT_QUEUE_SIZE]; | |
| 44 | + int size; | |
| 45 | +}; | |
| 46 | + | |
| 47 | +static void raw_event_queue_init(struct raw_event_queue *queue) | |
| 48 | +{ | |
| 49 | + spin_lock_init(&queue->lock); | |
| 50 | + sema_init(&queue->sema, 0); | |
| 51 | + queue->size = 0; | |
| 52 | +} | |
| 53 | + | |
| 54 | +static int raw_event_queue_add(struct raw_event_queue *queue, | |
| 55 | + enum usb_raw_event_type type, size_t length, const void *data) | |
| 56 | +{ | |
| 57 | + unsigned long flags; | |
| 58 | + struct usb_raw_event *event; | |
| 59 | + | |
| 60 | + spin_lock_irqsave(&queue->lock, flags); | |
| 61 | + if (WARN_ON(queue->size >= RAW_EVENT_QUEUE_SIZE)) { | |
| 62 | + spin_unlock_irqrestore(&queue->lock, flags); | |
| 63 | + return -ENOMEM; | |
| 64 | + } | |
| 65 | + event = kmalloc(sizeof(*event) + length, GFP_ATOMIC); | |
| 66 | + if (!event) { | |
| 67 | + spin_unlock_irqrestore(&queue->lock, flags); | |
| 68 | + return -ENOMEM; | |
| 69 | + } | |
| 70 | + event->type = type; | |
| 71 | + event->length = length; | |
| 72 | + if (event->length) | |
| 73 | + memcpy(&event->data[0], data, length); | |
| 74 | + queue->events[queue->size] = event; | |
| 75 | + queue->size++; | |
| 76 | + up(&queue->sema); | |
| 77 | + spin_unlock_irqrestore(&queue->lock, flags); | |
| 78 | + return 0; | |
| 79 | +} | |
| 80 | + | |
| 81 | +static struct usb_raw_event *raw_event_queue_fetch( | |
| 82 | + struct raw_event_queue *queue) | |
| 83 | +{ | |
| 84 | + unsigned long flags; | |
| 85 | + struct usb_raw_event *event; | |
| 86 | + | |
| 87 | + /* | |
| 88 | + * This function can be called concurrently. We first check that | |
| 89 | + * there's at least one event queued by decrementing the semaphore, | |
| 90 | + * and then take the lock to protect queue struct fields. | |
| 91 | + */ | |
| 92 | + if (down_interruptible(&queue->sema)) | |
| 93 | + return NULL; | |
| 94 | + spin_lock_irqsave(&queue->lock, flags); | |
| 95 | + if (WARN_ON(!queue->size)) | |
| 96 | + return NULL; | |
| 97 | + event = queue->events[0]; | |
| 98 | + queue->size--; | |
| 99 | + memmove(&queue->events[0], &queue->events[1], | |
| 100 | + queue->size * sizeof(queue->events[0])); | |
| 101 | + spin_unlock_irqrestore(&queue->lock, flags); | |
| 102 | + return event; | |
| 103 | +} | |
| 104 | + | |
| 105 | +static void raw_event_queue_destroy(struct raw_event_queue *queue) | |
| 106 | +{ | |
| 107 | + int i; | |
| 108 | + | |
| 109 | + for (i = 0; i < queue->size; i++) | |
| 110 | + kfree(queue->events[i]); | |
| 111 | + queue->size = 0; | |
| 112 | +} | |
| 113 | + | |
| 114 | +/*----------------------------------------------------------------------*/ | |
| 115 | + | |
| 116 | +struct raw_dev; | |
| 117 | + | |
| 118 | +#define USB_RAW_MAX_ENDPOINTS 32 | |
| 119 | + | |
| 120 | +enum ep_state { | |
| 121 | + STATE_EP_DISABLED, | |
| 122 | + STATE_EP_ENABLED, | |
| 123 | +}; | |
| 124 | + | |
| 125 | +struct raw_ep { | |
| 126 | + struct raw_dev *dev; | |
| 127 | + enum ep_state state; | |
| 128 | + struct usb_ep *ep; | |
| 129 | + struct usb_request *req; | |
| 130 | + bool urb_queued; | |
| 131 | + bool disabling; | |
| 132 | + ssize_t status; | |
| 133 | +}; | |
| 134 | + | |
| 135 | +enum dev_state { | |
| 136 | + STATE_DEV_INVALID = 0, | |
| 137 | + STATE_DEV_OPENED, | |
| 138 | + STATE_DEV_INITIALIZED, | |
| 139 | + STATE_DEV_RUNNING, | |
| 140 | + STATE_DEV_CLOSED, | |
| 141 | + STATE_DEV_FAILED | |
| 142 | +}; | |
| 143 | + | |
| 144 | +struct raw_dev { | |
| 145 | + struct kref count; | |
| 146 | + spinlock_t lock; | |
| 147 | + | |
| 148 | + const char *udc_name; | |
| 149 | + struct usb_gadget_driver driver; | |
| 150 | + | |
| 151 | + /* Reference to misc device: */ | |
| 152 | + struct device *dev; | |
| 153 | + | |
| 154 | + /* Protected by lock: */ | |
| 155 | + enum dev_state state; | |
| 156 | + bool gadget_registered; | |
| 157 | + struct usb_gadget *gadget; | |
| 158 | + struct usb_request *req; | |
| 159 | + bool ep0_in_pending; | |
| 160 | + bool ep0_out_pending; | |
| 161 | + bool ep0_urb_queued; | |
| 162 | + ssize_t ep0_status; | |
| 163 | + struct raw_ep eps[USB_RAW_MAX_ENDPOINTS]; | |
| 164 | + | |
| 165 | + struct completion ep0_done; | |
| 166 | + struct raw_event_queue queue; | |
| 167 | +}; | |
| 168 | + | |
| 169 | +static struct raw_dev *dev_new(void) | |
| 170 | +{ | |
| 171 | + struct raw_dev *dev; | |
| 172 | + | |
| 173 | + dev = kzalloc(sizeof(*dev), GFP_KERNEL); | |
| 174 | + if (!dev) | |
| 175 | + return NULL; | |
| 176 | + /* Matches kref_put() in raw_release(). */ | |
| 177 | + kref_init(&dev->count); | |
| 178 | + spin_lock_init(&dev->lock); | |
| 179 | + init_completion(&dev->ep0_done); | |
| 180 | + raw_event_queue_init(&dev->queue); | |
| 181 | + return dev; | |
| 182 | +} | |
| 183 | + | |
| 184 | +static void dev_free(struct kref *kref) | |
| 185 | +{ | |
| 186 | + struct raw_dev *dev = container_of(kref, struct raw_dev, count); | |
| 187 | + int i; | |
| 188 | + | |
| 189 | + kfree(dev->udc_name); | |
| 190 | + kfree(dev->driver.udc_name); | |
| 191 | + if (dev->req) { | |
| 192 | + if (dev->ep0_urb_queued) | |
| 193 | + usb_ep_dequeue(dev->gadget->ep0, dev->req); | |
| 194 | + usb_ep_free_request(dev->gadget->ep0, dev->req); | |
| 195 | + } | |
| 196 | + raw_event_queue_destroy(&dev->queue); | |
| 197 | + for (i = 0; i < USB_RAW_MAX_ENDPOINTS; i++) { | |
| 198 | + if (dev->eps[i].state != STATE_EP_ENABLED) | |
| 199 | + continue; | |
| 200 | + usb_ep_disable(dev->eps[i].ep); | |
| 201 | + usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req); | |
| 202 | + kfree(dev->eps[i].ep->desc); | |
| 203 | + dev->eps[i].state = STATE_EP_DISABLED; | |
| 204 | + } | |
| 205 | + kfree(dev); | |
| 206 | +} | |
| 207 | + | |
| 208 | +/*----------------------------------------------------------------------*/ | |
| 209 | + | |
| 210 | +static int raw_queue_event(struct raw_dev *dev, | |
| 211 | + enum usb_raw_event_type type, size_t length, const void *data) | |
| 212 | +{ | |
| 213 | + int ret = 0; | |
| 214 | + unsigned long flags; | |
| 215 | + | |
| 216 | + ret = raw_event_queue_add(&dev->queue, type, length, data); | |
| 217 | + if (ret < 0) { | |
| 218 | + spin_lock_irqsave(&dev->lock, flags); | |
| 219 | + dev->state = STATE_DEV_FAILED; | |
| 220 | + spin_unlock_irqrestore(&dev->lock, flags); | |
| 221 | + } | |
| 222 | + return ret; | |
| 223 | +} | |
| 224 | + | |
| 225 | +static void gadget_ep0_complete(struct usb_ep *ep, struct usb_request *req) | |
| 226 | +{ | |
| 227 | + struct raw_dev *dev = req->context; | |
| 228 | + unsigned long flags; | |
| 229 | + | |
| 230 | + spin_lock_irqsave(&dev->lock, flags); | |
| 231 | + if (req->status) | |
| 232 | + dev->ep0_status = req->status; | |
| 233 | + else | |
| 234 | + dev->ep0_status = req->actual; | |
| 235 | + if (dev->ep0_in_pending) | |
| 236 | + dev->ep0_in_pending = false; | |
| 237 | + else | |
| 238 | + dev->ep0_out_pending = false; | |
| 239 | + spin_unlock_irqrestore(&dev->lock, flags); | |
| 240 | + | |
| 241 | + complete(&dev->ep0_done); | |
| 242 | +} | |
| 243 | + | |
| 244 | +static int gadget_bind(struct usb_gadget *gadget, | |
| 245 | + struct usb_gadget_driver *driver) | |
| 246 | +{ | |
| 247 | + int ret = 0; | |
| 248 | + struct raw_dev *dev = container_of(driver, struct raw_dev, driver); | |
| 249 | + struct usb_request *req; | |
| 250 | + unsigned long flags; | |
| 251 | + | |
| 252 | + if (strcmp(gadget->name, dev->udc_name) != 0) | |
| 253 | + return -ENODEV; | |
| 254 | + | |
| 255 | + set_gadget_data(gadget, dev); | |
| 256 | + req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); | |
| 257 | + if (!req) { | |
| 258 | + dev_err(&gadget->dev, "usb_ep_alloc_request failed\n"); | |
| 259 | + set_gadget_data(gadget, NULL); | |
| 260 | + return -ENOMEM; | |
| 261 | + } | |
| 262 | + | |
| 263 | + spin_lock_irqsave(&dev->lock, flags); | |
| 264 | + dev->req = req; | |
| 265 | + dev->req->context = dev; | |
| 266 | + dev->req->complete = gadget_ep0_complete; | |
| 267 | + dev->gadget = gadget; | |
| 268 | + spin_unlock_irqrestore(&dev->lock, flags); | |
| 269 | + | |
| 270 | + /* Matches kref_put() in gadget_unbind(). */ | |
| 271 | + kref_get(&dev->count); | |
| 272 | + | |
| 273 | + ret = raw_queue_event(dev, USB_RAW_EVENT_CONNECT, 0, NULL); | |
| 274 | + if (ret < 0) | |
| 275 | + dev_err(&gadget->dev, "failed to queue event\n"); | |
| 276 | + | |
| 277 | + return ret; | |
| 278 | +} | |
| 279 | + | |
| 280 | +static void gadget_unbind(struct usb_gadget *gadget) | |
| 281 | +{ | |
| 282 | + struct raw_dev *dev = get_gadget_data(gadget); | |
| 283 | + | |
| 284 | + set_gadget_data(gadget, NULL); | |
| 285 | + /* Matches kref_get() in gadget_bind(). */ | |
| 286 | + kref_put(&dev->count, dev_free); | |
| 287 | +} | |
| 288 | + | |
| 289 | +static int gadget_setup(struct usb_gadget *gadget, | |
| 290 | + const struct usb_ctrlrequest *ctrl) | |
| 291 | +{ | |
| 292 | + int ret = 0; | |
| 293 | + struct raw_dev *dev = get_gadget_data(gadget); | |
| 294 | + unsigned long flags; | |
| 295 | + | |
| 296 | + spin_lock_irqsave(&dev->lock, flags); | |
| 297 | + if (dev->state != STATE_DEV_RUNNING) { | |
| 298 | + dev_err(&gadget->dev, "ignoring, device is not running\n"); | |
| 299 | + ret = -ENODEV; | |
| 300 | + goto out_unlock; | |
| 301 | + } | |
| 302 | + if (dev->ep0_in_pending || dev->ep0_out_pending) { | |
| 303 | + dev_dbg(&gadget->dev, "stalling, request already pending\n"); | |
| 304 | + ret = -EBUSY; | |
| 305 | + goto out_unlock; | |
| 306 | + } | |
| 307 | + if ((ctrl->bRequestType & USB_DIR_IN) && ctrl->wLength) | |
| 308 | + dev->ep0_in_pending = true; | |
| 309 | + else | |
| 310 | + dev->ep0_out_pending = true; | |
| 311 | + spin_unlock_irqrestore(&dev->lock, flags); | |
| 312 | + | |
| 313 | + ret = raw_queue_event(dev, USB_RAW_EVENT_CONTROL, sizeof(*ctrl), ctrl); | |
| 314 | + if (ret < 0) | |
| 315 | + dev_err(&gadget->dev, "failed to queue event\n"); | |
| 316 | + goto out; | |
| 317 | + | |
| 318 | +out_unlock: | |
| 319 | + spin_unlock_irqrestore(&dev->lock, flags); | |
| 320 | +out: | |
| 321 | + return ret; | |
| 322 | +} | |
| 323 | + | |
| 324 | +/* These are currently unused but present in case UDC driver requires them. */ | |
| 325 | +static void gadget_disconnect(struct usb_gadget *gadget) { } | |
| 326 | +static void gadget_suspend(struct usb_gadget *gadget) { } | |
| 327 | +static void gadget_resume(struct usb_gadget *gadget) { } | |
| 328 | +static void gadget_reset(struct usb_gadget *gadget) { } | |
| 329 | + | |
| 330 | +/*----------------------------------------------------------------------*/ | |
| 331 | + | |
| 332 | +static struct miscdevice raw_misc_device; | |
| 333 | + | |
| 334 | +static int raw_open(struct inode *inode, struct file *fd) | |
| 335 | +{ | |
| 336 | + struct raw_dev *dev; | |
| 337 | + | |
| 338 | + /* Nonblocking I/O is not supported yet. */ | |
| 339 | + if (fd->f_flags & O_NONBLOCK) | |
| 340 | + return -EINVAL; | |
| 341 | + | |
| 342 | + dev = dev_new(); | |
| 343 | + if (!dev) | |
| 344 | + return -ENOMEM; | |
| 345 | + fd->private_data = dev; | |
| 346 | + dev->state = STATE_DEV_OPENED; | |
| 347 | + dev->dev = raw_misc_device.this_device; | |
| 348 | + return 0; | |
| 349 | +} | |
| 350 | + | |
| 351 | +static int raw_release(struct inode *inode, struct file *fd) | |
| 352 | +{ | |
| 353 | + int ret = 0; | |
| 354 | + struct raw_dev *dev = fd->private_data; | |
| 355 | + unsigned long flags; | |
| 356 | + bool unregister = false; | |
| 357 | + | |
| 358 | + spin_lock_irqsave(&dev->lock, flags); | |
| 359 | + dev->state = STATE_DEV_CLOSED; | |
| 360 | + if (!dev->gadget) { | |
| 361 | + spin_unlock_irqrestore(&dev->lock, flags); | |
| 362 | + goto out_put; | |
| 363 | + } | |
| 364 | + if (dev->gadget_registered) | |
| 365 | + unregister = true; | |
| 366 | + dev->gadget_registered = false; | |
| 367 | + spin_unlock_irqrestore(&dev->lock, flags); | |
| 368 | + | |
| 369 | + if (unregister) { | |
| 370 | + ret = usb_gadget_unregister_driver(&dev->driver); | |
| 371 | + if (ret != 0) | |
| 372 | + dev_err(dev->dev, | |
| 373 | + "usb_gadget_unregister_driver() failed with %d\n", | |
| 374 | + ret); | |
| 375 | + /* Matches kref_get() in raw_ioctl_run(). */ | |
| 376 | + kref_put(&dev->count, dev_free); | |
| 377 | + } | |
| 378 | + | |
| 379 | +out_put: | |
| 380 | + /* Matches dev_new() in raw_open(). */ | |
| 381 | + kref_put(&dev->count, dev_free); | |
| 382 | + return ret; | |
| 383 | +} | |
| 384 | + | |
| 385 | +/*----------------------------------------------------------------------*/ | |
| 386 | + | |
| 387 | +static int raw_ioctl_init(struct raw_dev *dev, unsigned long value) | |
| 388 | +{ | |
| 389 | + int ret = 0; | |
| 390 | + struct usb_raw_init arg; | |
| 391 | + char *udc_driver_name; | |
| 392 | + char *udc_device_name; | |
| 393 | + unsigned long flags; | |
| 394 | + | |
| 395 | + ret = copy_from_user(&arg, (void __user *)value, sizeof(arg)); | |
| 396 | + if (ret) | |
| 397 | + return ret; | |
| 398 | + | |
| 399 | + switch (arg.speed) { | |
| 400 | + case USB_SPEED_UNKNOWN: | |
| 401 | + arg.speed = USB_SPEED_HIGH; | |
| 402 | + break; | |
| 403 | + case USB_SPEED_LOW: | |
| 404 | + case USB_SPEED_FULL: | |
| 405 | + case USB_SPEED_HIGH: | |
| 406 | + case USB_SPEED_SUPER: | |
| 407 | + break; | |
| 408 | + default: | |
| 409 | + return -EINVAL; | |
| 410 | + } | |
| 411 | + | |
| 412 | + udc_driver_name = kmalloc(UDC_NAME_LENGTH_MAX, GFP_KERNEL); | |
| 413 | + if (!udc_driver_name) | |
| 414 | + return -ENOMEM; | |
| 415 | + ret = strscpy(udc_driver_name, &arg.driver_name[0], | |
| 416 | + UDC_NAME_LENGTH_MAX); | |
| 417 | + if (ret < 0) { | |
| 418 | + kfree(udc_driver_name); | |
| 419 | + return ret; | |
| 420 | + } | |
| 421 | + ret = 0; | |
| 422 | + | |
| 423 | + udc_device_name = kmalloc(UDC_NAME_LENGTH_MAX, GFP_KERNEL); | |
| 424 | + if (!udc_device_name) { | |
| 425 | + kfree(udc_driver_name); | |
| 426 | + return -ENOMEM; | |
| 427 | + } | |
| 428 | + ret = strscpy(udc_device_name, &arg.device_name[0], | |
| 429 | + UDC_NAME_LENGTH_MAX); | |
| 430 | + if (ret < 0) { | |
| 431 | + kfree(udc_driver_name); | |
| 432 | + kfree(udc_device_name); | |
| 433 | + return ret; | |
| 434 | + } | |
| 435 | + ret = 0; | |
| 436 | + | |
| 437 | + spin_lock_irqsave(&dev->lock, flags); | |
| 438 | + if (dev->state != STATE_DEV_OPENED) { | |
| 439 | + dev_dbg(dev->dev, "fail, device is not opened\n"); | |
| 440 | + kfree(udc_driver_name); | |
| 441 | + kfree(udc_device_name); | |
| 442 | + ret = -EINVAL; | |
| 443 | + goto out_unlock; | |
| 444 | + } | |
| 445 | + dev->udc_name = udc_driver_name; | |
| 446 | + | |
| 447 | + dev->driver.function = DRIVER_DESC; | |
| 448 | + dev->driver.max_speed = arg.speed; | |
| 449 | + dev->driver.setup = gadget_setup; | |
| 450 | + dev->driver.disconnect = gadget_disconnect; | |
| 451 | + dev->driver.bind = gadget_bind; | |
| 452 | + dev->driver.unbind = gadget_unbind; | |
| 453 | + dev->driver.suspend = gadget_suspend; | |
| 454 | + dev->driver.resume = gadget_resume; | |
| 455 | + dev->driver.reset = gadget_reset; | |
| 456 | + dev->driver.driver.name = DRIVER_NAME; | |
| 457 | + dev->driver.udc_name = udc_device_name; | |
| 458 | + dev->driver.match_existing_only = 1; | |
| 459 | + | |
| 460 | + dev->state = STATE_DEV_INITIALIZED; | |
| 461 | + | |
| 462 | +out_unlock: | |
| 463 | + spin_unlock_irqrestore(&dev->lock, flags); | |
| 464 | + return ret; | |
| 465 | +} | |
| 466 | + | |
| 467 | +static int raw_ioctl_run(struct raw_dev *dev, unsigned long value) | |
| 468 | +{ | |
| 469 | + int ret = 0; | |
| 470 | + unsigned long flags; | |
| 471 | + | |
| 472 | + if (value) | |
| 473 | + return -EINVAL; | |
| 474 | + | |
| 475 | + spin_lock_irqsave(&dev->lock, flags); | |
| 476 | + if (dev->state != STATE_DEV_INITIALIZED) { | |
| 477 | + dev_dbg(dev->dev, "fail, device is not initialized\n"); | |
| 478 | + ret = -EINVAL; | |
| 479 | + goto out_unlock; | |
| 480 | + } | |
| 481 | + spin_unlock_irqrestore(&dev->lock, flags); | |
| 482 | + | |
| 483 | + ret = usb_gadget_probe_driver(&dev->driver); | |
| 484 | + | |
| 485 | + spin_lock_irqsave(&dev->lock, flags); | |
| 486 | + if (ret) { | |
| 487 | + dev_err(dev->dev, | |
| 488 | + "fail, usb_gadget_probe_driver returned %d\n", ret); | |
| 489 | + dev->state = STATE_DEV_FAILED; | |
| 490 | + goto out_unlock; | |
| 491 | + } | |
| 492 | + dev->gadget_registered = true; | |
| 493 | + dev->state = STATE_DEV_RUNNING; | |
| 494 | + /* Matches kref_put() in raw_release(). */ | |
| 495 | + kref_get(&dev->count); | |
| 496 | + | |
| 497 | +out_unlock: | |
| 498 | + spin_unlock_irqrestore(&dev->lock, flags); | |
| 499 | + return ret; | |
| 500 | +} | |
| 501 | + | |
| 502 | +static int raw_ioctl_event_fetch(struct raw_dev *dev, unsigned long value) | |
| 503 | +{ | |
| 504 | + int ret = 0; | |
| 505 | + struct usb_raw_event arg; | |
| 506 | + unsigned long flags; | |
| 507 | + struct usb_raw_event *event; | |
| 508 | + uint32_t length; | |
| 509 | + | |
| 510 | + ret = copy_from_user(&arg, (void __user *)value, sizeof(arg)); | |
| 511 | + if (ret) | |
| 512 | + return ret; | |
| 513 | + | |
| 514 | + spin_lock_irqsave(&dev->lock, flags); | |
| 515 | + if (dev->state != STATE_DEV_RUNNING) { | |
| 516 | + dev_dbg(dev->dev, "fail, device is not running\n"); | |
| 517 | + spin_unlock_irqrestore(&dev->lock, flags); | |
| 518 | + return -EINVAL; | |
| 519 | + } | |
| 520 | + if (!dev->gadget) { | |
| 521 | + dev_dbg(dev->dev, "fail, gadget is not bound\n"); | |
| 522 | + spin_unlock_irqrestore(&dev->lock, flags); | |
| 523 | + return -EBUSY; | |
| 524 | + } | |
| 525 | + spin_unlock_irqrestore(&dev->lock, flags); | |
| 526 | + | |
| 527 | + event = raw_event_queue_fetch(&dev->queue); | |
| 528 | + if (!event) { | |
| 529 | + dev_dbg(&dev->gadget->dev, "event fetching interrupted\n"); | |
| 530 | + return -EINTR; | |
| 531 | + } | |
| 532 | + length = min(arg.length, event->length); | |
| 533 | + ret = copy_to_user((void __user *)value, event, | |
| 534 | + sizeof(*event) + length); | |
| 535 | + return ret; | |
| 536 | +} | |
| 537 | + | |
| 538 | +static void *raw_alloc_io_data(struct usb_raw_ep_io *io, void __user *ptr, | |
| 539 | + bool get_from_user) | |
| 540 | +{ | |
| 541 | + int ret; | |
| 542 | + void *data; | |
| 543 | + | |
| 544 | + ret = copy_from_user(io, ptr, sizeof(*io)); | |
| 545 | + if (ret) | |
| 546 | + return ERR_PTR(ret); | |
| 547 | + if (io->ep >= USB_RAW_MAX_ENDPOINTS) | |
| 548 | + return ERR_PTR(-EINVAL); | |
| 549 | + if (!usb_raw_io_flags_valid(io->flags)) | |
| 550 | + return ERR_PTR(-EINVAL); | |
| 551 | + if (io->length > PAGE_SIZE) | |
| 552 | + return ERR_PTR(-EINVAL); | |
| 553 | + if (get_from_user) | |
| 554 | + data = memdup_user(ptr + sizeof(*io), io->length); | |
| 555 | + else { | |
| 556 | + data = kmalloc(io->length, GFP_KERNEL); | |
| 557 | + if (!data) | |
| 558 | + data = ERR_PTR(-ENOMEM); | |
| 559 | + } | |
| 560 | + return data; | |
| 561 | +} | |
| 562 | + | |
| 563 | +static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io, | |
| 564 | + void *data, bool in) | |
| 565 | +{ | |
| 566 | + int ret = 0; | |
| 567 | + unsigned long flags; | |
| 568 | + | |
| 569 | + spin_lock_irqsave(&dev->lock, flags); | |
| 570 | + if (dev->state != STATE_DEV_RUNNING) { | |
| 571 | + dev_dbg(dev->dev, "fail, device is not running\n"); | |
| 572 | + ret = -EINVAL; | |
| 573 | + goto out_unlock; | |
| 574 | + } | |
| 575 | + if (!dev->gadget) { | |
| 576 | + dev_dbg(dev->dev, "fail, gadget is not bound\n"); | |
| 577 | + ret = -EBUSY; | |
| 578 | + goto out_unlock; | |
| 579 | + } | |
| 580 | + if (dev->ep0_urb_queued) { | |
| 581 | + dev_dbg(&dev->gadget->dev, "fail, urb already queued\n"); | |
| 582 | + ret = -EBUSY; | |
| 583 | + goto out_unlock; | |
| 584 | + } | |
| 585 | + if ((in && !dev->ep0_in_pending) || | |
| 586 | + (!in && !dev->ep0_out_pending)) { | |
| 587 | + dev_dbg(&dev->gadget->dev, "fail, wrong direction\n"); | |
| 588 | + ret = -EBUSY; | |
| 589 | + goto out_unlock; | |
| 590 | + } | |
| 591 | + if (WARN_ON(in && dev->ep0_out_pending)) { | |
| 592 | + ret = -ENODEV; | |
| 593 | + dev->state = STATE_DEV_FAILED; | |
| 594 | + goto out_done; | |
| 595 | + } | |
| 596 | + if (WARN_ON(!in && dev->ep0_in_pending)) { | |
| 597 | + ret = -ENODEV; | |
| 598 | + dev->state = STATE_DEV_FAILED; | |
| 599 | + goto out_done; | |
| 600 | + } | |
| 601 | + | |
| 602 | + dev->req->buf = data; | |
| 603 | + dev->req->length = io->length; | |
| 604 | + dev->req->zero = usb_raw_io_flags_zero(io->flags); | |
| 605 | + dev->ep0_urb_queued = true; | |
| 606 | + spin_unlock_irqrestore(&dev->lock, flags); | |
| 607 | + | |
| 608 | + ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL); | |
| 609 | + if (ret) { | |
| 610 | + dev_err(&dev->gadget->dev, | |
| 611 | + "fail, usb_ep_queue returned %d\n", ret); | |
| 612 | + spin_lock_irqsave(&dev->lock, flags); | |
| 613 | + dev->state = STATE_DEV_FAILED; | |
| 614 | + goto out_done; | |
| 615 | + } | |
| 616 | + | |
| 617 | + ret = wait_for_completion_interruptible(&dev->ep0_done); | |
| 618 | + if (ret) { | |
| 619 | + dev_dbg(&dev->gadget->dev, "wait interrupted\n"); | |
| 620 | + usb_ep_dequeue(dev->gadget->ep0, dev->req); | |
| 621 | + wait_for_completion(&dev->ep0_done); | |
| 622 | + spin_lock_irqsave(&dev->lock, flags); | |
| 623 | + goto out_done; | |
| 624 | + } | |
| 625 | + | |
| 626 | + spin_lock_irqsave(&dev->lock, flags); | |
| 627 | + ret = dev->ep0_status; | |
| 628 | + | |
| 629 | +out_done: | |
| 630 | + dev->ep0_urb_queued = false; | |
| 631 | +out_unlock: | |
| 632 | + spin_unlock_irqrestore(&dev->lock, flags); | |
| 633 | + return ret; | |
| 634 | +} | |
| 635 | + | |
| 636 | +static int raw_ioctl_ep0_write(struct raw_dev *dev, unsigned long value) | |
| 637 | +{ | |
| 638 | + int ret = 0; | |
| 639 | + void *data; | |
| 640 | + struct usb_raw_ep_io io; | |
| 641 | + | |
| 642 | + data = raw_alloc_io_data(&io, (void __user *)value, true); | |
| 643 | + if (IS_ERR(data)) | |
| 644 | + return PTR_ERR(data); | |
| 645 | + ret = raw_process_ep0_io(dev, &io, data, true); | |
| 646 | + kfree(data); | |
| 647 | + return ret; | |
| 648 | +} | |
| 649 | + | |
| 650 | +static int raw_ioctl_ep0_read(struct raw_dev *dev, unsigned long value) | |
| 651 | +{ | |
| 652 | + int ret = 0; | |
| 653 | + void *data; | |
| 654 | + struct usb_raw_ep_io io; | |
| 655 | + unsigned int length; | |
| 656 | + | |
| 657 | + data = raw_alloc_io_data(&io, (void __user *)value, false); | |
| 658 | + if (IS_ERR(data)) | |
| 659 | + return PTR_ERR(data); | |
| 660 | + ret = raw_process_ep0_io(dev, &io, data, false); | |
| 661 | + if (ret < 0) { | |
| 662 | + kfree(data); | |
| 663 | + return ret; | |
| 664 | + } | |
| 665 | + length = min(io.length, (unsigned int)ret); | |
| 666 | + ret = copy_to_user((void __user *)(value + sizeof(io)), data, length); | |
| 667 | + kfree(data); | |
| 668 | + return ret; | |
| 669 | +} | |
| 670 | + | |
| 671 | +static bool check_ep_caps(struct usb_ep *ep, | |
| 672 | + struct usb_endpoint_descriptor *desc) | |
| 673 | +{ | |
| 674 | + switch (usb_endpoint_type(desc)) { | |
| 675 | + case USB_ENDPOINT_XFER_ISOC: | |
| 676 | + if (!ep->caps.type_iso) | |
| 677 | + return false; | |
| 678 | + break; | |
| 679 | + case USB_ENDPOINT_XFER_BULK: | |
| 680 | + if (!ep->caps.type_bulk) | |
| 681 | + return false; | |
| 682 | + break; | |
| 683 | + case USB_ENDPOINT_XFER_INT: | |
| 684 | + if (!ep->caps.type_int) | |
| 685 | + return false; | |
| 686 | + break; | |
| 687 | + default: | |
| 688 | + return false; | |
| 689 | + } | |
| 690 | + | |
| 691 | + if (usb_endpoint_dir_in(desc) && !ep->caps.dir_in) | |
| 692 | + return false; | |
| 693 | + if (usb_endpoint_dir_out(desc) && !ep->caps.dir_out) | |
| 694 | + return false; | |
| 695 | + | |
| 696 | + return true; | |
| 697 | +} | |
| 698 | + | |
| 699 | +static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value) | |
| 700 | +{ | |
| 701 | + int ret = 0, i; | |
| 702 | + unsigned long flags; | |
| 703 | + struct usb_endpoint_descriptor *desc; | |
| 704 | + struct usb_ep *ep = NULL; | |
| 705 | + | |
| 706 | + desc = memdup_user((void __user *)value, sizeof(*desc)); | |
| 707 | + if (IS_ERR(desc)) | |
| 708 | + return PTR_ERR(desc); | |
| 709 | + | |
| 710 | + /* | |
| 711 | + * Endpoints with a maxpacket length of 0 can cause crashes in UDC | |
| 712 | + * drivers. | |
| 713 | + */ | |
| 714 | + if (usb_endpoint_maxp(desc) == 0) { | |
| 715 | + dev_dbg(dev->dev, "fail, bad endpoint maxpacket\n"); | |
| 716 | + kfree(desc); | |
| 717 | + return -EINVAL; | |
| 718 | + } | |
| 719 | + | |
| 720 | + spin_lock_irqsave(&dev->lock, flags); | |
| 721 | + if (dev->state != STATE_DEV_RUNNING) { | |
| 722 | + dev_dbg(dev->dev, "fail, device is not running\n"); | |
| 723 | + ret = -EINVAL; | |
| 724 | + goto out_free; | |
| 725 | + } | |
| 726 | + if (!dev->gadget) { | |
| 727 | + dev_dbg(dev->dev, "fail, gadget is not bound\n"); | |
| 728 | + ret = -EBUSY; | |
| 729 | + goto out_free; | |
| 730 | + } | |
| 731 | + | |
| 732 | + for (i = 0; i < USB_RAW_MAX_ENDPOINTS; i++) { | |
| 733 | + if (dev->eps[i].state == STATE_EP_ENABLED) | |
| 734 | + continue; | |
| 735 | + break; | |
| 736 | + } | |
| 737 | + if (i == USB_RAW_MAX_ENDPOINTS) { | |
| 738 | + dev_dbg(&dev->gadget->dev, | |
| 739 | + "fail, no device endpoints available\n"); | |
| 740 | + ret = -EBUSY; | |
| 741 | + goto out_free; | |
| 742 | + } | |
| 743 | + | |
| 744 | + gadget_for_each_ep(ep, dev->gadget) { | |
| 745 | + if (ep->enabled) | |
| 746 | + continue; | |
| 747 | + if (!check_ep_caps(ep, desc)) | |
| 748 | + continue; | |
| 749 | + ep->desc = desc; | |
| 750 | + ret = usb_ep_enable(ep); | |
| 751 | + if (ret < 0) { | |
| 752 | + dev_err(&dev->gadget->dev, | |
| 753 | + "fail, usb_ep_enable returned %d\n", ret); | |
| 754 | + goto out_free; | |
| 755 | + } | |
| 756 | + dev->eps[i].req = usb_ep_alloc_request(ep, GFP_ATOMIC); | |
| 757 | + if (!dev->eps[i].req) { | |
| 758 | + dev_err(&dev->gadget->dev, | |
| 759 | + "fail, usb_ep_alloc_request failed\n"); | |
| 760 | + usb_ep_disable(ep); | |
| 761 | + ret = -ENOMEM; | |
| 762 | + goto out_free; | |
| 763 | + } | |
| 764 | + dev->eps[i].ep = ep; | |
| 765 | + dev->eps[i].state = STATE_EP_ENABLED; | |
| 766 | + ep->driver_data = &dev->eps[i]; | |
| 767 | + ret = i; | |
| 768 | + goto out_unlock; | |
| 769 | + } | |
| 770 | + | |
| 771 | + dev_dbg(&dev->gadget->dev, "fail, no gadget endpoints available\n"); | |
| 772 | + ret = -EBUSY; | |
| 773 | + | |
| 774 | +out_free: | |
| 775 | + kfree(desc); | |
| 776 | +out_unlock: | |
| 777 | + spin_unlock_irqrestore(&dev->lock, flags); | |
| 778 | + return ret; | |
| 779 | +} | |
| 780 | + | |
| 781 | +static int raw_ioctl_ep_disable(struct raw_dev *dev, unsigned long value) | |
| 782 | +{ | |
| 783 | + int ret = 0, i = value; | |
| 784 | + unsigned long flags; | |
| 785 | + const void *desc; | |
| 786 | + | |
| 787 | + if (i < 0 || i >= USB_RAW_MAX_ENDPOINTS) | |
| 788 | + return -EINVAL; | |
| 789 | + | |
| 790 | + spin_lock_irqsave(&dev->lock, flags); | |
| 791 | + if (dev->state != STATE_DEV_RUNNING) { | |
| 792 | + dev_dbg(dev->dev, "fail, device is not running\n"); | |
| 793 | + ret = -EINVAL; | |
| 794 | + goto out_unlock; | |
| 795 | + } | |
| 796 | + if (!dev->gadget) { | |
| 797 | + dev_dbg(dev->dev, "fail, gadget is not bound\n"); | |
| 798 | + ret = -EBUSY; | |
| 799 | + goto out_unlock; | |
| 800 | + } | |
| 801 | + if (dev->eps[i].state != STATE_EP_ENABLED) { | |
| 802 | + dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n"); | |
| 803 | + ret = -EINVAL; | |
| 804 | + goto out_unlock; | |
| 805 | + } | |
| 806 | + if (dev->eps[i].disabling) { | |
| 807 | + dev_dbg(&dev->gadget->dev, | |
| 808 | + "fail, disable already in progress\n"); | |
| 809 | + ret = -EINVAL; | |
| 810 | + goto out_unlock; | |
| 811 | + } | |
| 812 | + if (dev->eps[i].urb_queued) { | |
| 813 | + dev_dbg(&dev->gadget->dev, | |
| 814 | + "fail, waiting for urb completion\n"); | |
| 815 | + ret = -EINVAL; | |
| 816 | + goto out_unlock; | |
| 817 | + } | |
| 818 | + dev->eps[i].disabling = true; | |
| 819 | + spin_unlock_irqrestore(&dev->lock, flags); | |
| 820 | + | |
| 821 | + usb_ep_disable(dev->eps[i].ep); | |
| 822 | + | |
| 823 | + spin_lock_irqsave(&dev->lock, flags); | |
| 824 | + usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req); | |
| 825 | + desc = dev->eps[i].ep->desc; | |
| 826 | + dev->eps[i].ep = NULL; | |
| 827 | + dev->eps[i].state = STATE_EP_DISABLED; | |
| 828 | + kfree(desc); | |
| 829 | + dev->eps[i].disabling = false; | |
| 830 | + | |
| 831 | +out_unlock: | |
| 832 | + spin_unlock_irqrestore(&dev->lock, flags); | |
| 833 | + return ret; | |
| 834 | +} | |
| 835 | + | |
| 836 | +static void gadget_ep_complete(struct usb_ep *ep, struct usb_request *req) | |
| 837 | +{ | |
| 838 | + struct raw_ep *r_ep = (struct raw_ep *)ep->driver_data; | |
| 839 | + struct raw_dev *dev = r_ep->dev; | |
| 840 | + unsigned long flags; | |
| 841 | + | |
| 842 | + spin_lock_irqsave(&dev->lock, flags); | |
| 843 | + if (req->status) | |
| 844 | + r_ep->status = req->status; | |
| 845 | + else | |
| 846 | + r_ep->status = req->actual; | |
| 847 | + spin_unlock_irqrestore(&dev->lock, flags); | |
| 848 | + | |
| 849 | + complete((struct completion *)req->context); | |
| 850 | +} | |
| 851 | + | |
| 852 | +static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io, | |
| 853 | + void *data, bool in) | |
| 854 | +{ | |
| 855 | + int ret = 0; | |
| 856 | + unsigned long flags; | |
| 857 | + struct raw_ep *ep = &dev->eps[io->ep]; | |
| 858 | + DECLARE_COMPLETION_ONSTACK(done); | |
| 859 | + | |
| 860 | + spin_lock_irqsave(&dev->lock, flags); | |
| 861 | + if (dev->state != STATE_DEV_RUNNING) { | |
| 862 | + dev_dbg(dev->dev, "fail, device is not running\n"); | |
| 863 | + ret = -EINVAL; | |
| 864 | + goto out_unlock; | |
| 865 | + } | |
| 866 | + if (!dev->gadget) { | |
| 867 | + dev_dbg(dev->dev, "fail, gadget is not bound\n"); | |
| 868 | + ret = -EBUSY; | |
| 869 | + goto out_unlock; | |
| 870 | + } | |
| 871 | + if (ep->state != STATE_EP_ENABLED) { | |
| 872 | + dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n"); | |
| 873 | + ret = -EBUSY; | |
| 874 | + goto out_unlock; | |
| 875 | + } | |
| 876 | + if (ep->disabling) { | |
| 877 | + dev_dbg(&dev->gadget->dev, | |
| 878 | + "fail, endpoint is already being disabled\n"); | |
| 879 | + ret = -EBUSY; | |
| 880 | + goto out_unlock; | |
| 881 | + } | |
| 882 | + if (ep->urb_queued) { | |
| 883 | + dev_dbg(&dev->gadget->dev, "fail, urb already queued\n"); | |
| 884 | + ret = -EBUSY; | |
| 885 | + goto out_unlock; | |
| 886 | + } | |
| 887 | + if ((in && !ep->ep->caps.dir_in) || (!in && ep->ep->caps.dir_in)) { | |
| 888 | + dev_dbg(&dev->gadget->dev, "fail, wrong direction\n"); | |
| 889 | + ret = -EINVAL; | |
| 890 | + goto out_unlock; | |
| 891 | + } | |
| 892 | + | |
| 893 | + ep->dev = dev; | |
| 894 | + ep->req->context = &done; | |
| 895 | + ep->req->complete = gadget_ep_complete; | |
| 896 | + ep->req->buf = data; | |
| 897 | + ep->req->length = io->length; | |
| 898 | + ep->req->zero = usb_raw_io_flags_zero(io->flags); | |
| 899 | + ep->urb_queued = true; | |
| 900 | + spin_unlock_irqrestore(&dev->lock, flags); | |
| 901 | + | |
| 902 | + ret = usb_ep_queue(ep->ep, ep->req, GFP_KERNEL); | |
| 903 | + if (ret) { | |
| 904 | + dev_err(&dev->gadget->dev, | |
| 905 | + "fail, usb_ep_queue returned %d\n", ret); | |
| 906 | + spin_lock_irqsave(&dev->lock, flags); | |
| 907 | + dev->state = STATE_DEV_FAILED; | |
| 908 | + goto out_done; | |
| 909 | + } | |
| 910 | + | |
| 911 | + ret = wait_for_completion_interruptible(&done); | |
| 912 | + if (ret) { | |
| 913 | + dev_dbg(&dev->gadget->dev, "wait interrupted\n"); | |
| 914 | + usb_ep_dequeue(ep->ep, ep->req); | |
| 915 | + wait_for_completion(&done); | |
| 916 | + spin_lock_irqsave(&dev->lock, flags); | |
| 917 | + goto out_done; | |
| 918 | + } | |
| 919 | + | |
| 920 | + spin_lock_irqsave(&dev->lock, flags); | |
| 921 | + ret = ep->status; | |
| 922 | + | |
| 923 | +out_done: | |
| 924 | + ep->urb_queued = false; | |
| 925 | +out_unlock: | |
| 926 | + spin_unlock_irqrestore(&dev->lock, flags); | |
| 927 | + return ret; | |
| 928 | +} | |
| 929 | + | |
| 930 | +static int raw_ioctl_ep_write(struct raw_dev *dev, unsigned long value) | |
| 931 | +{ | |
| 932 | + int ret = 0; | |
| 933 | + char *data; | |
| 934 | + struct usb_raw_ep_io io; | |
| 935 | + | |
| 936 | + data = raw_alloc_io_data(&io, (void __user *)value, true); | |
| 937 | + if (IS_ERR(data)) | |
| 938 | + return PTR_ERR(data); | |
| 939 | + ret = raw_process_ep_io(dev, &io, data, true); | |
| 940 | + kfree(data); | |
| 941 | + return ret; | |
| 942 | +} | |
| 943 | + | |
| 944 | +static int raw_ioctl_ep_read(struct raw_dev *dev, unsigned long value) | |
| 945 | +{ | |
| 946 | + int ret = 0; | |
| 947 | + char *data; | |
| 948 | + struct usb_raw_ep_io io; | |
| 949 | + unsigned int length; | |
| 950 | + | |
| 951 | + data = raw_alloc_io_data(&io, (void __user *)value, false); | |
| 952 | + if (IS_ERR(data)) | |
| 953 | + return PTR_ERR(data); | |
| 954 | + ret = raw_process_ep_io(dev, &io, data, false); | |
| 955 | + if (ret < 0) { | |
| 956 | + kfree(data); | |
| 957 | + return ret; | |
| 958 | + } | |
| 959 | + length = min(io.length, (unsigned int)ret); | |
| 960 | + ret = copy_to_user((void __user *)(value + sizeof(io)), data, length); | |
| 961 | + kfree(data); | |
| 962 | + return ret; | |
| 963 | +} | |
| 964 | + | |
| 965 | +static int raw_ioctl_configure(struct raw_dev *dev, unsigned long value) | |
| 966 | +{ | |
| 967 | + int ret = 0; | |
| 968 | + unsigned long flags; | |
| 969 | + | |
| 970 | + if (value) | |
| 971 | + return -EINVAL; | |
| 972 | + spin_lock_irqsave(&dev->lock, flags); | |
| 973 | + if (dev->state != STATE_DEV_RUNNING) { | |
| 974 | + dev_dbg(dev->dev, "fail, device is not running\n"); | |
| 975 | + ret = -EINVAL; | |
| 976 | + goto out_unlock; | |
| 977 | + } | |
| 978 | + if (!dev->gadget) { | |
| 979 | + dev_dbg(dev->dev, "fail, gadget is not bound\n"); | |
| 980 | + ret = -EBUSY; | |
| 981 | + goto out_unlock; | |
| 982 | + } | |
| 983 | + usb_gadget_set_state(dev->gadget, USB_STATE_CONFIGURED); | |
| 984 | + | |
| 985 | +out_unlock: | |
| 986 | + spin_unlock_irqrestore(&dev->lock, flags); | |
| 987 | + return ret; | |
| 988 | +} | |
| 989 | + | |
| 990 | +static int raw_ioctl_vbus_draw(struct raw_dev *dev, unsigned long value) | |
| 991 | +{ | |
| 992 | + int ret = 0; | |
| 993 | + unsigned long flags; | |
| 994 | + | |
| 995 | + spin_lock_irqsave(&dev->lock, flags); | |
| 996 | + if (dev->state != STATE_DEV_RUNNING) { | |
| 997 | + dev_dbg(dev->dev, "fail, device is not running\n"); | |
| 998 | + ret = -EINVAL; | |
| 999 | + goto out_unlock; | |
| 1000 | + } | |
| 1001 | + if (!dev->gadget) { | |
| 1002 | + dev_dbg(dev->dev, "fail, gadget is not bound\n"); | |
| 1003 | + ret = -EBUSY; | |
| 1004 | + goto out_unlock; | |
| 1005 | + } | |
| 1006 | + usb_gadget_vbus_draw(dev->gadget, 2 * value); | |
| 1007 | + | |
| 1008 | +out_unlock: | |
| 1009 | + spin_unlock_irqrestore(&dev->lock, flags); | |
| 1010 | + return ret; | |
| 1011 | +} | |
| 1012 | + | |
| 1013 | +static long raw_ioctl(struct file *fd, unsigned int cmd, unsigned long value) | |
| 1014 | +{ | |
| 1015 | + struct raw_dev *dev = fd->private_data; | |
| 1016 | + int ret = 0; | |
| 1017 | + | |
| 1018 | + if (!dev) | |
| 1019 | + return -EBUSY; | |
| 1020 | + | |
| 1021 | + switch (cmd) { | |
| 1022 | + case USB_RAW_IOCTL_INIT: | |
| 1023 | + ret = raw_ioctl_init(dev, value); | |
| 1024 | + break; | |
| 1025 | + case USB_RAW_IOCTL_RUN: | |
| 1026 | + ret = raw_ioctl_run(dev, value); | |
| 1027 | + break; | |
| 1028 | + case USB_RAW_IOCTL_EVENT_FETCH: | |
| 1029 | + ret = raw_ioctl_event_fetch(dev, value); | |
| 1030 | + break; | |
| 1031 | + case USB_RAW_IOCTL_EP0_WRITE: | |
| 1032 | + ret = raw_ioctl_ep0_write(dev, value); | |
| 1033 | + break; | |
| 1034 | + case USB_RAW_IOCTL_EP0_READ: | |
| 1035 | + ret = raw_ioctl_ep0_read(dev, value); | |
| 1036 | + break; | |
| 1037 | + case USB_RAW_IOCTL_EP_ENABLE: | |
| 1038 | + ret = raw_ioctl_ep_enable(dev, value); | |
| 1039 | + break; | |
| 1040 | + case USB_RAW_IOCTL_EP_DISABLE: | |
| 1041 | + ret = raw_ioctl_ep_disable(dev, value); | |
| 1042 | + break; | |
| 1043 | + case USB_RAW_IOCTL_EP_WRITE: | |
| 1044 | + ret = raw_ioctl_ep_write(dev, value); | |
| 1045 | + break; | |
| 1046 | + case USB_RAW_IOCTL_EP_READ: | |
| 1047 | + ret = raw_ioctl_ep_read(dev, value); | |
| 1048 | + break; | |
| 1049 | + case USB_RAW_IOCTL_CONFIGURE: | |
| 1050 | + ret = raw_ioctl_configure(dev, value); | |
| 1051 | + break; | |
| 1052 | + case USB_RAW_IOCTL_VBUS_DRAW: | |
| 1053 | + ret = raw_ioctl_vbus_draw(dev, value); | |
| 1054 | + break; | |
| 1055 | + default: | |
| 1056 | + ret = -EINVAL; | |
| 1057 | + } | |
| 1058 | + | |
| 1059 | + return ret; | |
| 1060 | +} | |
| 1061 | + | |
| 1062 | +/*----------------------------------------------------------------------*/ | |
| 1063 | + | |
| 1064 | +static const struct file_operations raw_fops = { | |
| 1065 | + .open = raw_open, | |
| 1066 | + .unlocked_ioctl = raw_ioctl, | |
| 1067 | + .compat_ioctl = raw_ioctl, | |
| 1068 | + .release = raw_release, | |
| 1069 | + .llseek = no_llseek, | |
| 1070 | +}; | |
| 1071 | + | |
| 1072 | +static struct miscdevice raw_misc_device = { | |
| 1073 | + .minor = MISC_DYNAMIC_MINOR, | |
| 1074 | + .name = DRIVER_NAME, | |
| 1075 | + .fops = &raw_fops, | |
| 1076 | +}; | |
| 1077 | + | |
| 1078 | +module_misc_device(raw_misc_device); | |
| @@ -441,6 +441,16 @@ config USB_GADGET_XILINX · 440 unmodified lines | ||
| 441 | 441 | dynamically linked module called "udc-xilinx" and force all |
| 442 | 442 | gadget drivers to also be dynamically linked. |
| 443 | 443 | |
| 444 | +config USB_MAX3420_UDC | |
| 445 | + tristate "MAX3420 (USB-over-SPI) support" | |
| 446 | + depends on SPI | |
| 447 | + help | |
| 448 | + The Maxim MAX3420 chip supports USB2.0 full-speed peripheral mode. | |
| 449 | + The MAX3420 is run by SPI interface, and hence the dependency. | |
| 450 | + | |
| 451 | + To compile this driver as a module, choose M here: the module will | |
| 452 | + be called max3420_udc | |
| 453 | + | |
| 444 | 454 | config USB_TEGRA_XUDC |
| 445 | 455 | tristate "NVIDIA Tegra Superspeed USB 3.0 Device Controller" |
| 446 | 456 | depends on ARCH_TEGRA || COMPILE_TEST |
| @@ -42,3 +42,4 @@ obj-$(CONFIG_USB_GADGET_XILINX) += udc-xilinx.o · 41 unmodified lines | ||
| 42 | 42 | obj-$(CONFIG_USB_SNP_UDC_PLAT) += snps_udc_plat.o |
| 43 | 43 | obj-$(CONFIG_USB_ASPEED_VHUB) += aspeed-vhub/ |
| 44 | 44 | obj-$(CONFIG_USB_BDC_UDC) += bdc/ |
| 45 | +obj-$(CONFIG_USB_MAX3420_UDC) += max3420_udc.o | |
| @@ -4,5 +4,5 @@ config USB_ASPEED_VHUB · 3 unmodified lines | ||
| 4 | 4 | depends on ARCH_ASPEED || COMPILE_TEST |
| 5 | 5 | depends on USB_LIBCOMPOSITE |
| 6 | 6 | help |
| 7 | − USB peripheral controller for the Aspeed AST2500 family | |
| 8 | − SoCs supporting the "vHub" functionality and USB2.0 | |
| 7 | + USB peripheral controller for the Aspeed AST2400, AST2500 and | |
| 8 | + AST2600 family SoCs supporting the "vHub" functionality and USB2.0 | |
| @@ -99,7 +99,7 @@ static irqreturn_t ast_vhub_irq(int irq, void *data) · 98 unmodified lines | ||
| 99 | 99 | { |
| 100 | 100 | struct ast_vhub *vhub = data; |
| 101 | 101 | irqreturn_t iret = IRQ_NONE; |
| 102 | − u32 istat; | |
| 102 | + u32 i, istat; | |
| 103 | 103 | |
| 104 | 104 | /* Stale interrupt while tearing down */ |
| 105 | 105 | if (!vhub->ep0_bufs) |
| @@ -121,10 +121,10 @@ static irqreturn_t ast_vhub_irq(int irq, void *data) · 15 unmodified lines | ||
| 121 | 121 | |
| 122 | 122 | /* Handle generic EPs first */ |
| 123 | 123 | if (istat & VHUB_IRQ_EP_POOL_ACK_STALL) { |
| 124 | − u32 i, ep_acks = readl(vhub->regs + AST_VHUB_EP_ACK_ISR); | |
| 124 | + u32 ep_acks = readl(vhub->regs + AST_VHUB_EP_ACK_ISR); | |
| 125 | 125 | writel(ep_acks, vhub->regs + AST_VHUB_EP_ACK_ISR); |
| 126 | 126 | |
| 127 | − for (i = 0; ep_acks && i < AST_VHUB_NUM_GEN_EPs; i++) { | |
| 127 | + for (i = 0; ep_acks && i < vhub->max_epns; i++) { | |
| 128 | 128 | u32 mask = VHUB_EP_IRQ(i); |
| 129 | 129 | if (ep_acks & mask) { |
| 130 | 130 | ast_vhub_epn_ack_irq(&vhub->epns[i]); |
| @@ -134,21 +134,11 @@ static irqreturn_t ast_vhub_irq(int irq, void *data) · 3 unmodified lines | ||
| 134 | 134 | } |
| 135 | 135 | |
| 136 | 136 | /* Handle device interrupts */ |
| 137 | − if (istat & (VHUB_IRQ_DEVICE1 | | |
| 138 | − VHUB_IRQ_DEVICE2 | | |
| 139 | − VHUB_IRQ_DEVICE3 | | |
| 140 | − VHUB_IRQ_DEVICE4 | | |
| 141 | − VHUB_IRQ_DEVICE5)) { | |
| 142 | − if (istat & VHUB_IRQ_DEVICE1) | |
| 143 | − ast_vhub_dev_irq(&vhub->ports[0].dev); | |
| 144 | − if (istat & VHUB_IRQ_DEVICE2) | |
| 145 | − ast_vhub_dev_irq(&vhub->ports[1].dev); | |
| 146 | − if (istat & VHUB_IRQ_DEVICE3) | |
| 147 | − ast_vhub_dev_irq(&vhub->ports[2].dev); | |
| 148 | − if (istat & VHUB_IRQ_DEVICE4) | |
| 149 | − ast_vhub_dev_irq(&vhub->ports[3].dev); | |
| 150 | − if (istat & VHUB_IRQ_DEVICE5) | |
| 151 | − ast_vhub_dev_irq(&vhub->ports[4].dev); | |
| 137 | + for (i = 0; i < vhub->max_ports; i++) { | |
| 138 | + u32 dev_mask = VHUB_IRQ_DEVICE1 << i; | |
| 139 | + | |
| 140 | + if (istat & dev_mask) | |
| 141 | + ast_vhub_dev_irq(&vhub->ports[i].dev); | |
| 152 | 142 | } |
| 153 | 143 | |
| 154 | 144 | /* Handle top-level vHub EP0 interrupts */ |
| @@ -182,7 +172,7 @@ static irqreturn_t ast_vhub_irq(int irq, void *data) · 27 unmodified lines | ||
| 182 | 172 | |
| 183 | 173 | void ast_vhub_init_hw(struct ast_vhub *vhub) |
| 184 | 174 | { |
| 185 | − u32 ctrl; | |
| 175 | + u32 ctrl, port_mask, epn_mask; | |
| 186 | 176 | |
| 187 | 177 | UDCDBG(vhub,"(Re)Starting HW ...\n"); |
| 188 | 178 | |
| @@ -222,15 +212,20 @@ void ast_vhub_init_hw(struct ast_vhub *vhub) · 33 unmodified lines | ||
| 222 | 212 | } |
| 223 | 213 | |
| 224 | 214 | /* Reset all devices */ |
| 225 | − writel(VHUB_SW_RESET_ALL, vhub->regs + AST_VHUB_SW_RESET); | |
| 215 | + port_mask = GENMASK(vhub->max_ports, 1); | |
| 216 | + writel(VHUB_SW_RESET_ROOT_HUB | | |
| 217 | + VHUB_SW_RESET_DMA_CONTROLLER | | |
| 218 | + VHUB_SW_RESET_EP_POOL | | |
| 219 | + port_mask, vhub->regs + AST_VHUB_SW_RESET); | |
| 226 | 220 | udelay(1); |
| 227 | 221 | writel(0, vhub->regs + AST_VHUB_SW_RESET); |
| 228 | 222 | |
| 229 | 223 | /* Disable and cleanup EP ACK/NACK interrupts */ |
| 224 | + epn_mask = GENMASK(vhub->max_epns - 1, 0); | |
| 230 | 225 | writel(0, vhub->regs + AST_VHUB_EP_ACK_IER); |
| 231 | 226 | writel(0, vhub->regs + AST_VHUB_EP_NACK_IER); |
| 232 | − writel(VHUB_EP_IRQ_ALL, vhub->regs + AST_VHUB_EP_ACK_ISR); | |
| 233 | − writel(VHUB_EP_IRQ_ALL, vhub->regs + AST_VHUB_EP_NACK_ISR); | |
| 227 | + writel(epn_mask, vhub->regs + AST_VHUB_EP_ACK_ISR); | |
| 228 | + writel(epn_mask, vhub->regs + AST_VHUB_EP_NACK_ISR); | |
| 234 | 229 | |
| 235 | 230 | /* Default settings for EP0, enable HW hub EP1 */ |
| 236 | 231 | writel(0, vhub->regs + AST_VHUB_EP0_CTRL); |
| @@ -273,7 +268,7 @@ static int ast_vhub_remove(struct platform_device *pdev) · 36 unmodified lines | ||
| 273 | 268 | return 0; |
| 274 | 269 | |
| 275 | 270 | /* Remove devices */ |
| 276 | − for (i = 0; i < AST_VHUB_NUM_PORTS; i++) | |
| 271 | + for (i = 0; i < vhub->max_ports; i++) | |
| 277 | 272 | ast_vhub_del_dev(&vhub->ports[i].dev); |
| 278 | 273 | |
| 279 | 274 | spin_lock_irqsave(&vhub->lock, flags); |
| @@ -295,7 +290,7 @@ static int ast_vhub_remove(struct platform_device *pdev) · 15 unmodified lines | ||
| 295 | 290 | if (vhub->ep0_bufs) |
| 296 | 291 | dma_free_coherent(&pdev->dev, |
| 297 | 292 | AST_VHUB_EP0_MAX_PACKET * |
| 298 | − (AST_VHUB_NUM_PORTS + 1), | |
| 293 | + (vhub->max_ports + 1), | |
| 299 | 294 | vhub->ep0_bufs, |
| 300 | 295 | vhub->ep0_bufs_dma); |
| 301 | 296 | vhub->ep0_bufs = NULL; |
| @@ -309,11 +304,32 @@ static int ast_vhub_probe(struct platform_device *pdev) · 7 unmodified lines | ||
| 309 | 304 | struct ast_vhub *vhub; |
| 310 | 305 | struct resource *res; |
| 311 | 306 | int i, rc = 0; |
| 307 | + const struct device_node *np = pdev->dev.of_node; | |
| 312 | 308 | |
| 313 | 309 | vhub = devm_kzalloc(&pdev->dev, sizeof(*vhub), GFP_KERNEL); |
| 314 | 310 | if (!vhub) |
| 315 | 311 | return -ENOMEM; |
| 316 | 312 | |
| 313 | + rc = of_property_read_u32(np, "aspeed,vhub-downstream-ports", | |
| 314 | + &vhub->max_ports); | |
| 315 | + if (rc < 0) | |
| 316 | + vhub->max_ports = AST_VHUB_NUM_PORTS; | |
| 317 | + | |
| 318 | + vhub->ports = devm_kcalloc(&pdev->dev, vhub->max_ports, | |
| 319 | + sizeof(*vhub->ports), GFP_KERNEL); | |
| 320 | + if (!vhub->ports) | |
| 321 | + return -ENOMEM; | |
| 322 | + | |
| 323 | + rc = of_property_read_u32(np, "aspeed,vhub-generic-endpoints", | |
| 324 | + &vhub->max_epns); | |
| 325 | + if (rc < 0) | |
| 326 | + vhub->max_epns = AST_VHUB_NUM_GEN_EPs; | |
| 327 | + | |
| 328 | + vhub->epns = devm_kcalloc(&pdev->dev, vhub->max_epns, | |
| 329 | + sizeof(*vhub->epns), GFP_KERNEL); | |
| 330 | + if (!vhub->epns) | |
| 331 | + return -ENOMEM; | |
| 332 | + | |
| 317 | 333 | spin_lock_init(&vhub->lock); |
| 318 | 334 | vhub->pdev = pdev; |
| 319 | 335 | |
| @@ -366,7 +382,7 @@ static int ast_vhub_probe(struct platform_device *pdev) · 46 unmodified lines | ||
| 366 | 382 | */ |
| 367 | 383 | vhub->ep0_bufs = dma_alloc_coherent(&pdev->dev, |
| 368 | 384 | AST_VHUB_EP0_MAX_PACKET * |
| 369 | − (AST_VHUB_NUM_PORTS + 1), | |
| 385 | + (vhub->max_ports + 1), | |
| 370 | 386 | &vhub->ep0_bufs_dma, GFP_KERNEL); |
| 371 | 387 | if (!vhub->ep0_bufs) { |
| 372 | 388 | dev_err(&pdev->dev, "Failed to allocate EP0 DMA buffers\n"); |
| @@ -380,7 +396,7 @@ static int ast_vhub_probe(struct platform_device *pdev) · 7 unmodified lines | ||
| 380 | 396 | ast_vhub_init_ep0(vhub, &vhub->ep0, NULL); |
| 381 | 397 | |
| 382 | 398 | /* Init devices */ |
| 383 | − for (i = 0; i < AST_VHUB_NUM_PORTS && rc == 0; i++) | |
| 399 | + for (i = 0; i < vhub->max_ports && rc == 0; i++) | |
| 384 | 400 | rc = ast_vhub_init_dev(vhub, i); |
| 385 | 401 | if (rc) |
| 386 | 402 | goto err; |
| @@ -407,6 +423,9 @@ static const struct of_device_id ast_vhub_dt_ids[] = { · 20 unmodified lines | ||
| 407 | 423 | { |
| 408 | 424 | .compatible = "aspeed,ast2500-usb-vhub", |
| 409 | 425 | }, |
| 426 | + { | |
| 427 | + .compatible = "aspeed,ast2600-usb-vhub", | |
| 428 | + }, | |
| 410 | 429 | { } |
| 411 | 430 | }; |
| 412 | 431 | MODULE_DEVICE_TABLE(of, ast_vhub_dt_ids); |
| @@ -77,7 +77,7 @@ static void ast_vhub_dev_enable(struct ast_vhub_dev *d) · 76 unmodified lines | ||
| 77 | 77 | writel(d->ep0.buf_dma, d->regs + AST_VHUB_DEV_EP0_DATA); |
| 78 | 78 | |
| 79 | 79 | /* Clear stall on all EPs */ |
| 80 | − for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++) { | |
| 80 | + for (i = 0; i < d->max_epns; i++) { | |
| 81 | 81 | struct ast_vhub_ep *ep = d->epns[i]; |
| 82 | 82 | |
| 83 | 83 | if (ep && (ep->epn.stalled || ep->epn.wedged)) { |
| @@ -137,7 +137,7 @@ static int ast_vhub_ep_feature(struct ast_vhub_dev *d, · 53 unmodified lines | ||
| 137 | 137 | is_set ? "SET" : "CLEAR", ep_num, wValue); |
| 138 | 138 | if (ep_num == 0) |
| 139 | 139 | return std_req_complete; |
| 140 | − if (ep_num >= AST_VHUB_NUM_GEN_EPs || !d->epns[ep_num - 1]) | |
| 140 | + if (ep_num >= d->max_epns || !d->epns[ep_num - 1]) | |
| 141 | 141 | return std_req_stall; |
| 142 | 142 | if (wValue != USB_ENDPOINT_HALT) |
| 143 | 143 | return std_req_driver; |
| @@ -181,7 +181,7 @@ static int ast_vhub_ep_status(struct ast_vhub_dev *d, · 37 unmodified lines | ||
| 181 | 181 | |
| 182 | 182 | DDBG(d, "GET_STATUS(ep%d)\n", ep_num); |
| 183 | 183 | |
| 184 | − if (ep_num >= AST_VHUB_NUM_GEN_EPs) | |
| 184 | + if (ep_num >= d->max_epns) | |
| 185 | 185 | return std_req_stall; |
| 186 | 186 | if (ep_num != 0) { |
| 187 | 187 | ep = d->epns[ep_num - 1]; |
| @@ -299,7 +299,7 @@ static void ast_vhub_dev_nuke(struct ast_vhub_dev *d) · 111 unmodified lines | ||
| 299 | 299 | { |
| 300 | 300 | unsigned int i; |
| 301 | 301 | |
| 302 | − for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++) { | |
| 302 | + for (i = 0; i < d->max_epns; i++) { | |
| 303 | 303 | if (!d->epns[i]) |
| 304 | 304 | continue; |
| 305 | 305 | ast_vhub_nuke(d->epns[i], -ESHUTDOWN); |
| @@ -416,10 +416,10 @@ static struct usb_ep *ast_vhub_udc_match_ep(struct usb_gadget *gadget, · 110 unmodified lines | ||
| 416 | 416 | * that will allow the generic code to use our |
| 417 | 417 | * assigned address. |
| 418 | 418 | */ |
| 419 | − for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++) | |
| 419 | + for (i = 0; i < d->max_epns; i++) | |
| 420 | 420 | if (d->epns[i] == NULL) |
| 421 | 421 | break; |
| 422 | − if (i >= AST_VHUB_NUM_GEN_EPs) | |
| 422 | + if (i >= d->max_epns) | |
| 423 | 423 | return NULL; |
| 424 | 424 | addr = i + 1; |
| 425 | 425 | |
| @@ -526,6 +526,7 @@ void ast_vhub_del_dev(struct ast_vhub_dev *d) · 100 unmodified lines | ||
| 526 | 526 | |
| 527 | 527 | usb_del_gadget_udc(&d->gadget); |
| 528 | 528 | device_unregister(d->port_dev); |
| 529 | + kfree(d->epns); | |
| 529 | 530 | } |
| 530 | 531 | |
| 531 | 532 | static void ast_vhub_dev_release(struct device *dev) |
| @@ -546,14 +547,25 @@ int ast_vhub_init_dev(struct ast_vhub *vhub, unsigned int idx) · 14 unmodified lines | ||
| 546 | 547 | |
| 547 | 548 | ast_vhub_init_ep0(vhub, &d->ep0, d); |
| 548 | 549 | |
| 550 | + /* | |
| 551 | + * A USB device can have up to 30 endpoints besides control | |
| 552 | + * endpoint 0. | |
| 553 | + */ | |
| 554 | + d->max_epns = min_t(u32, vhub->max_epns, 30); | |
| 555 | + d->epns = kcalloc(d->max_epns, sizeof(*d->epns), GFP_KERNEL); | |
| 556 | + if (!d->epns) | |
| 557 | + return -ENOMEM; | |
| 558 | + | |
| 549 | 559 | /* |
| 550 | 560 | * The UDC core really needs us to have separate and uniquely |
| 551 | 561 | * named "parent" devices for each port so we create a sub device |
| 552 | 562 | * here for that purpose |
| 553 | 563 | */ |
| 554 | 564 | d->port_dev = kzalloc(sizeof(struct device), GFP_KERNEL); |
| 555 | − if (!d->port_dev) | |
| 556 | − return -ENOMEM; | |
| 565 | + if (!d->port_dev) { | |
| 566 | + rc = -ENOMEM; | |
| 567 | + goto fail_alloc; | |
| 568 | + } | |
| 557 | 569 | device_initialize(d->port_dev); |
| 558 | 570 | d->port_dev->release = ast_vhub_dev_release; |
| 559 | 571 | d->port_dev->parent = parent; |
| @@ -584,6 +596,8 @@ int ast_vhub_init_dev(struct ast_vhub *vhub, unsigned int idx) · 24 unmodified lines | ||
| 584 | 596 | device_del(d->port_dev); |
| 585 | 597 | fail_add: |
| 586 | 598 | put_device(d->port_dev); |
| 599 | + fail_alloc: | |
| 600 | + kfree(d->epns); | |
| 587 | 601 | |
| 588 | 602 | return rc; |
| 589 | 603 | } |
| @@ -800,10 +800,10 @@ struct ast_vhub_ep *ast_vhub_alloc_epn(struct ast_vhub_dev *d, u8 addr) · 799 unmodified lines | ||
| 800 | 800 | |
| 801 | 801 | /* Find a free one (no device) */ |
| 802 | 802 | spin_lock_irqsave(&vhub->lock, flags); |
| 803 | − for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++) | |
| 803 | + for (i = 0; i < vhub->max_epns; i++) | |
| 804 | 804 | if (vhub->epns[i].dev == NULL) |
| 805 | 805 | break; |
| 806 | − if (i >= AST_VHUB_NUM_GEN_EPs) { | |
| 806 | + if (i >= vhub->max_epns) { | |
| 807 | 807 | spin_unlock_irqrestore(&vhub->lock, flags); |
| 808 | 808 | return NULL; |
| 809 | 809 | } |
| @@ -93,11 +93,7 @@ static void ast_vhub_patch_dev_desc_usb1(struct usb_device_descriptor *desc) · 92 unmodified lines | ||
| 93 | 93 | USB_DT_INTERFACE_SIZE + \ |
| 94 | 94 | USB_DT_ENDPOINT_SIZE) |
| 95 | 95 | |
| 96 | −static const struct ast_vhub_full_cdesc { | |
| 97 | − struct usb_config_descriptor cfg; | |
| 98 | − struct usb_interface_descriptor intf; | |
| 99 | − struct usb_endpoint_descriptor ep; | |
| 100 | −} __attribute__ ((packed)) ast_vhub_conf_desc = { | |
| 96 | +static const struct ast_vhub_full_cdesc ast_vhub_conf_desc = { | |
| 101 | 97 | .cfg = { |
| 102 | 98 | .bLength = USB_DT_CONFIG_SIZE, |
| 103 | 99 | .bDescriptorType = USB_DT_CONFIG, |
| @@ -266,6 +262,7 @@ static int ast_vhub_rep_desc(struct ast_vhub_ep *ep, · 162 unmodified lines | ||
| 266 | 262 | u8 desc_type, u16 len) |
| 267 | 263 | { |
| 268 | 264 | size_t dsize; |
| 265 | + struct ast_vhub *vhub = ep->vhub; | |
| 269 | 266 | |
| 270 | 267 | EPDBG(ep, "GET_DESCRIPTOR(type:%d)\n", desc_type); |
| 271 | 268 | |
| @@ -281,20 +278,20 @@ static int ast_vhub_rep_desc(struct ast_vhub_ep *ep, · 9 unmodified lines | ||
| 281 | 278 | switch(desc_type) { |
| 282 | 279 | case USB_DT_DEVICE: |
| 283 | 280 | dsize = USB_DT_DEVICE_SIZE; |
| 284 | − memcpy(ep->buf, &ast_vhub_dev_desc, dsize); | |
| 285 | − BUILD_BUG_ON(dsize > sizeof(ast_vhub_dev_desc)); | |
| 281 | + memcpy(ep->buf, &vhub->vhub_dev_desc, dsize); | |
| 282 | + BUILD_BUG_ON(dsize > sizeof(vhub->vhub_dev_desc)); | |
| 286 | 283 | BUILD_BUG_ON(USB_DT_DEVICE_SIZE >= AST_VHUB_EP0_MAX_PACKET); |
| 287 | 284 | break; |
| 288 | 285 | case USB_DT_CONFIG: |
| 289 | 286 | dsize = AST_VHUB_CONF_DESC_SIZE; |
| 290 | − memcpy(ep->buf, &ast_vhub_conf_desc, dsize); | |
| 291 | − BUILD_BUG_ON(dsize > sizeof(ast_vhub_conf_desc)); | |
| 287 | + memcpy(ep->buf, &vhub->vhub_conf_desc, dsize); | |
| 288 | + BUILD_BUG_ON(dsize > sizeof(vhub->vhub_conf_desc)); | |
| 292 | 289 | BUILD_BUG_ON(AST_VHUB_CONF_DESC_SIZE >= AST_VHUB_EP0_MAX_PACKET); |
| 293 | 290 | break; |
| 294 | 291 | case USB_DT_HUB: |
| 295 | 292 | dsize = AST_VHUB_HUB_DESC_SIZE; |
| 296 | − memcpy(ep->buf, &ast_vhub_hub_desc, dsize); | |
| 297 | − BUILD_BUG_ON(dsize > sizeof(ast_vhub_hub_desc)); | |
| 293 | + memcpy(ep->buf, &vhub->vhub_hub_desc, dsize); | |
| 294 | + BUILD_BUG_ON(dsize > sizeof(vhub->vhub_hub_desc)); | |
| 298 | 295 | BUILD_BUG_ON(AST_VHUB_HUB_DESC_SIZE >= AST_VHUB_EP0_MAX_PACKET); |
| 299 | 296 | break; |
| 300 | 297 | default: |
| @@ -317,7 +314,8 @@ static int ast_vhub_rep_string(struct ast_vhub_ep *ep, · 16 unmodified lines | ||
| 317 | 314 | u8 string_id, u16 lang_id, |
| 318 | 315 | u16 len) |
| 319 | 316 | { |
| 320 | − int rc = usb_gadget_get_string (&ast_vhub_strings, string_id, ep->buf); | |
| 317 | + int rc = usb_gadget_get_string(&ep->vhub->vhub_str_desc, | |
| 318 | + string_id, ep->buf); | |
| 321 | 319 | |
| 322 | 320 | /* |
| 323 | 321 | * This should never happen unless we put too big strings in |
| @@ -504,7 +502,7 @@ static void ast_vhub_wake_work(struct work_struct *work) · 180 unmodified lines | ||
| 504 | 502 | * we let the normal host wake path deal with it later. |
| 505 | 503 | */ |
| 506 | 504 | spin_lock_irqsave(&vhub->lock, flags); |
| 507 | − for (i = 0; i < AST_VHUB_NUM_PORTS; i++) { | |
| 505 | + for (i = 0; i < vhub->max_ports; i++) { | |
| 508 | 506 | struct ast_vhub_port *p = &vhub->ports[i]; |
| 509 | 507 | |
| 510 | 508 | if (!(p->status & USB_PORT_STAT_SUSPEND)) |
| @@ -587,7 +585,7 @@ static enum std_req_rc ast_vhub_set_port_feature(struct ast_vhub_ep *ep, · 76 unmodified lines | ||
| 587 | 585 | struct ast_vhub *vhub = ep->vhub; |
| 588 | 586 | struct ast_vhub_port *p; |
| 589 | 587 | |
| 590 | − if (port == 0 || port > AST_VHUB_NUM_PORTS) | |
| 588 | + if (port == 0 || port > vhub->max_ports) | |
| 591 | 589 | return std_req_stall; |
| 592 | 590 | port--; |
| 593 | 591 | p = &vhub->ports[port]; |
| @@ -630,7 +628,7 @@ static enum std_req_rc ast_vhub_clr_port_feature(struct ast_vhub_ep *ep, · 36 unmodified lines | ||
| 630 | 628 | struct ast_vhub *vhub = ep->vhub; |
| 631 | 629 | struct ast_vhub_port *p; |
| 632 | 630 | |
| 633 | − if (port == 0 || port > AST_VHUB_NUM_PORTS) | |
| 631 | + if (port == 0 || port > vhub->max_ports) | |
| 634 | 632 | return std_req_stall; |
| 635 | 633 | port--; |
| 636 | 634 | p = &vhub->ports[port]; |
| @@ -676,7 +674,7 @@ static enum std_req_rc ast_vhub_get_port_stat(struct ast_vhub_ep *ep, · 39 unmodified lines | ||
| 676 | 674 | struct ast_vhub *vhub = ep->vhub; |
| 677 | 675 | u16 stat, chg; |
| 678 | 676 | |
| 679 | − if (port == 0 || port > AST_VHUB_NUM_PORTS) | |
| 677 | + if (port == 0 || port > vhub->max_ports) | |
| 680 | 678 | return std_req_stall; |
| 681 | 679 | port--; |
| 682 | 680 | |
| @@ -757,7 +755,7 @@ void ast_vhub_hub_suspend(struct ast_vhub *vhub) · 74 unmodified lines | ||
| 757 | 755 | * Forward to unsuspended ports without changing |
| 758 | 756 | * their connection status. |
| 759 | 757 | */ |
| 760 | − for (i = 0; i < AST_VHUB_NUM_PORTS; i++) { | |
| 758 | + for (i = 0; i < vhub->max_ports; i++) { | |
| 761 | 759 | struct ast_vhub_port *p = &vhub->ports[i]; |
| 762 | 760 | |
| 763 | 761 | if (!(p->status & USB_PORT_STAT_SUSPEND)) |
| @@ -780,7 +778,7 @@ void ast_vhub_hub_resume(struct ast_vhub *vhub) · 16 unmodified lines | ||
| 780 | 778 | * Forward to unsuspended ports without changing |
| 781 | 779 | * their connection status. |
| 782 | 780 | */ |
| 783 | − for (i = 0; i < AST_VHUB_NUM_PORTS; i++) { | |
| 781 | + for (i = 0; i < vhub->max_ports; i++) { | |
| 784 | 782 | struct ast_vhub_port *p = &vhub->ports[i]; |
| 785 | 783 | |
| 786 | 784 | if (!(p->status & USB_PORT_STAT_SUSPEND)) |
| @@ -814,7 +812,7 @@ void ast_vhub_hub_reset(struct ast_vhub *vhub) · 27 unmodified lines | ||
| 814 | 812 | * Clear all port status, disable gadgets and "suspend" |
| 815 | 813 | * them. They will be woken up by a port reset. |
| 816 | 814 | */ |
| 817 | − for (i = 0; i < AST_VHUB_NUM_PORTS; i++) { | |
| 815 | + for (i = 0; i < vhub->max_ports; i++) { | |
| 818 | 816 | struct ast_vhub_port *p = &vhub->ports[i]; |
| 819 | 817 | |
| 820 | 818 | /* Only keep the connected flag */ |
| @@ -834,9 +832,31 @@ void ast_vhub_hub_reset(struct ast_vhub *vhub) · 13 unmodified lines | ||
| 834 | 832 | writel(0, vhub->regs + AST_VHUB_EP1_STS_CHG); |
| 835 | 833 | } |
| 836 | 834 | |
| 835 | +static void ast_vhub_init_desc(struct ast_vhub *vhub) | |
| 836 | +{ | |
| 837 | + /* Initialize vhub Device Descriptor. */ | |
| 838 | + memcpy(&vhub->vhub_dev_desc, &ast_vhub_dev_desc, | |
| 839 | + sizeof(vhub->vhub_dev_desc)); | |
| 840 | + | |
| 841 | + /* Initialize vhub Configuration Descriptor. */ | |
| 842 | + memcpy(&vhub->vhub_conf_desc, &ast_vhub_conf_desc, | |
| 843 | + sizeof(vhub->vhub_conf_desc)); | |
| 844 | + | |
| 845 | + /* Initialize vhub Hub Descriptor. */ | |
| 846 | + memcpy(&vhub->vhub_hub_desc, &ast_vhub_hub_desc, | |
| 847 | + sizeof(vhub->vhub_hub_desc)); | |
| 848 | + vhub->vhub_hub_desc.bNbrPorts = vhub->max_ports; | |
| 849 | + | |
| 850 | + /* Initialize vhub String Descriptors. */ | |
| 851 | + memcpy(&vhub->vhub_str_desc, &ast_vhub_strings, | |
| 852 | + sizeof(vhub->vhub_str_desc)); | |
| 853 | +} | |
| 854 | + | |
| 837 | 855 | void ast_vhub_init_hub(struct ast_vhub *vhub) |
| 838 | 856 | { |
| 839 | 857 | vhub->speed = USB_SPEED_UNKNOWN; |
| 840 | 858 | INIT_WORK(&vhub->wake_work, ast_vhub_wake_work); |
| 859 | + | |
| 860 | + ast_vhub_init_desc(vhub); | |
| 841 | 861 | } |
| 842 | 862 | |
| @@ -2,6 +2,9 @@ · 1 unmodified line | ||
| 2 | 2 | #ifndef __ASPEED_VHUB_H |
| 3 | 3 | #define __ASPEED_VHUB_H |
| 4 | 4 | |
| 5 | +#include <linux/usb.h> | |
| 6 | +#include <linux/usb/ch11.h> | |
| 7 | + | |
| 5 | 8 | /***************************** |
| 6 | 9 | * * |
| 7 | 10 | * VHUB register definitions * |
| @@ -76,17 +79,9 @@ · 68 unmodified lines | ||
| 76 | 79 | #define VHUB_SW_RESET_DEVICE2 (1 << 2) |
| 77 | 80 | #define VHUB_SW_RESET_DEVICE1 (1 << 1) |
| 78 | 81 | #define VHUB_SW_RESET_ROOT_HUB (1 << 0) |
| 79 | −#define VHUB_SW_RESET_ALL (VHUB_SW_RESET_EP_POOL | \ | |
| 80 | − VHUB_SW_RESET_DMA_CONTROLLER | \ | |
| 81 | − VHUB_SW_RESET_DEVICE5 | \ | |
| 82 | − VHUB_SW_RESET_DEVICE4 | \ | |
| 83 | − VHUB_SW_RESET_DEVICE3 | \ | |
| 84 | − VHUB_SW_RESET_DEVICE2 | \ | |
| 85 | − VHUB_SW_RESET_DEVICE1 | \ | |
| 86 | − VHUB_SW_RESET_ROOT_HUB) | |
| 82 | + | |
| 87 | 83 | /* EP ACK/NACK IRQ masks */ |
| 88 | 84 | #define VHUB_EP_IRQ(n) (1 << (n)) |
| 89 | −#define VHUB_EP_IRQ_ALL 0x7fff /* 15 EPs */ | |
| 90 | 85 | |
| 91 | 86 | /* USB status reg */ |
| 92 | 87 | #define VHUB_USBSTS_HISPEED (1 << 27) |
| @@ -210,6 +205,11 @@ · 117 unmodified lines | ||
| 210 | 205 | * * |
| 211 | 206 | ****************************************/ |
| 212 | 207 | |
| 208 | +/* | |
| 209 | + * AST_VHUB_NUM_GEN_EPs and AST_VHUB_NUM_PORTS are kept to avoid breaking | |
| 210 | + * existing AST2400/AST2500 platforms. AST2600 and future vhub revisions | |
| 211 | + * should define number of downstream ports and endpoints in device tree. | |
| 212 | + */ | |
| 213 | 213 | #define AST_VHUB_NUM_GEN_EPs 15 /* Generic non-0 EPs */ |
| 214 | 214 | #define AST_VHUB_NUM_PORTS 5 /* vHub ports */ |
| 215 | 215 | #define AST_VHUB_EP0_MAX_PACKET 64 /* EP0's max packet size */ |
| @@ -312,7 +312,7 @@ struct ast_vhub_ep { · 96 unmodified lines | ||
| 312 | 312 | /* Registers */ |
| 313 | 313 | void __iomem *regs; |
| 314 | 314 | |
| 315 | − /* Index in global pool (0..14) */ | |
| 315 | + /* Index in global pool (zero-based) */ | |
| 316 | 316 | unsigned int g_idx; |
| 317 | 317 | |
| 318 | 318 | /* DMA Descriptors */ |
| @@ -342,7 +342,7 @@ struct ast_vhub_dev { · 23 unmodified lines | ||
| 342 | 342 | struct ast_vhub *vhub; |
| 343 | 343 | void __iomem *regs; |
| 344 | 344 | |
| 345 | − /* Device index (0...4) and name string */ | |
| 345 | + /* Device index (zero-based) and name string */ | |
| 346 | 346 | unsigned int index; |
| 347 | 347 | const char *name; |
| 348 | 348 | |
| @@ -358,7 +358,8 @@ struct ast_vhub_dev { · 9 unmodified lines | ||
| 358 | 358 | |
| 359 | 359 | /* Endpoint structures */ |
| 360 | 360 | struct ast_vhub_ep ep0; |
| 361 | − struct ast_vhub_ep *epns[AST_VHUB_NUM_GEN_EPs]; | |
| 361 | + struct ast_vhub_ep **epns; | |
| 362 | + u32 max_epns; | |
| 362 | 363 | |
| 363 | 364 | }; |
| 364 | 365 | #define to_ast_dev(__g) container_of(__g, struct ast_vhub_dev, gadget) |
| @@ -373,6 +374,12 @@ struct ast_vhub_port { · 8 unmodified lines | ||
| 373 | 374 | struct ast_vhub_dev dev; |
| 374 | 375 | }; |
| 375 | 376 | |
| 377 | +struct ast_vhub_full_cdesc { | |
| 378 | + struct usb_config_descriptor cfg; | |
| 379 | + struct usb_interface_descriptor intf; | |
| 380 | + struct usb_endpoint_descriptor ep; | |
| 381 | +} __packed; | |
| 382 | + | |
| 376 | 383 | /* Global vhub structure */ |
| 377 | 384 | struct ast_vhub { |
| 378 | 385 | struct platform_device *pdev; |
| @@ -393,10 +400,12 @@ struct ast_vhub { · 14 unmodified lines | ||
| 393 | 400 | bool ep1_stalled : 1; |
| 394 | 401 | |
| 395 | 402 | /* Per-port info */ |
| 396 | − struct ast_vhub_port ports[AST_VHUB_NUM_PORTS]; | |
| 403 | + struct ast_vhub_port *ports; | |
| 404 | + u32 max_ports; | |
| 397 | 405 | |
| 398 | 406 | /* Generic EP data structures */ |
| 399 | − struct ast_vhub_ep epns[AST_VHUB_NUM_GEN_EPs]; | |
| 407 | + struct ast_vhub_ep *epns; | |
| 408 | + u32 max_epns; | |
| 400 | 409 | |
| 401 | 410 | /* Upstream bus is suspended ? */ |
| 402 | 411 | bool suspended : 1; |
| @@ -409,6 +418,12 @@ struct ast_vhub { · 6 unmodified lines | ||
| 409 | 418 | |
| 410 | 419 | /* Upstream bus speed captured at bus reset */ |
| 411 | 420 | unsigned int speed; |
| 421 | + | |
| 422 | + /* Standard USB Descriptors of the vhub. */ | |
| 423 | + struct usb_device_descriptor vhub_dev_desc; | |
| 424 | + struct ast_vhub_full_cdesc vhub_conf_desc; | |
| 425 | + struct usb_hub_descriptor vhub_hub_desc; | |
| 426 | + struct usb_gadget_strings vhub_str_desc; | |
| 412 | 427 | }; |
| 413 | 428 | |
| 414 | 429 | /* Standard request handlers result codes */ |
| @@ -0,0 +1,1331 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0+ | |
| 2 | +/* | |
| 3 | + * MAX3420 Device Controller driver for USB. | |
| 4 | + * | |
| 5 | + * Author: Jaswinder Singh Brar <jaswinder.singh@linaro.org> | |
| 6 | + * (C) Copyright 2019-2020 Linaro Ltd | |
| 7 | + * | |
| 8 | + * Based on: | |
| 9 | + * o MAX3420E datasheet | |
| 10 | + * http://datasheets.maximintegrated.com/en/ds/MAX3420E.pdf | |
| 11 | + * o MAX342{0,1}E Programming Guides | |
| 12 | + * https://pdfserv.maximintegrated.com/en/an/AN3598.pdf | |
| 13 | + * https://pdfserv.maximintegrated.com/en/an/AN3785.pdf | |
| 14 | + */ | |
| 15 | + | |
| 16 | +#include <linux/delay.h> | |
| 17 | +#include <linux/device.h> | |
| 18 | +#include <linux/interrupt.h> | |
| 19 | +#include <linux/io.h> | |
| 20 | +#include <linux/module.h> | |
| 21 | +#include <linux/bitfield.h> | |
| 22 | +#include <linux/of_address.h> | |
| 23 | +#include <linux/of_device.h> | |
| 24 | +#include <linux/of_platform.h> | |
| 25 | +#include <linux/of_irq.h> | |
| 26 | +#include <linux/prefetch.h> | |
| 27 | +#include <linux/usb/ch9.h> | |
| 28 | +#include <linux/usb/gadget.h> | |
| 29 | +#include <linux/spi/spi.h> | |
| 30 | +#include <linux/gpio/consumer.h> | |
| 31 | + | |
| 32 | +#define MAX3420_MAX_EPS 4 | |
| 33 | +#define MAX3420_EP_MAX_PACKET 64 /* Same for all Endpoints */ | |
| 34 | +#define MAX3420_EPNAME_SIZE 16 /* Buffer size for endpoint name */ | |
| 35 | + | |
| 36 | +#define MAX3420_ACKSTAT BIT(0) | |
| 37 | + | |
| 38 | +#define MAX3420_SPI_DIR_RD 0 /* read register from MAX3420 */ | |
| 39 | +#define MAX3420_SPI_DIR_WR 1 /* write register to MAX3420 */ | |
| 40 | + | |
| 41 | +/* SPI commands: */ | |
| 42 | +#define MAX3420_SPI_DIR_SHIFT 1 | |
| 43 | +#define MAX3420_SPI_REG_SHIFT 3 | |
| 44 | + | |
| 45 | +#define MAX3420_REG_EP0FIFO 0 | |
| 46 | +#define MAX3420_REG_EP1FIFO 1 | |
| 47 | +#define MAX3420_REG_EP2FIFO 2 | |
| 48 | +#define MAX3420_REG_EP3FIFO 3 | |
| 49 | +#define MAX3420_REG_SUDFIFO 4 | |
| 50 | +#define MAX3420_REG_EP0BC 5 | |
| 51 | +#define MAX3420_REG_EP1BC 6 | |
| 52 | +#define MAX3420_REG_EP2BC 7 | |
| 53 | +#define MAX3420_REG_EP3BC 8 | |
| 54 | + | |
| 55 | +#define MAX3420_REG_EPSTALLS 9 | |
| 56 | + #define ACKSTAT BIT(6) | |
| 57 | + #define STLSTAT BIT(5) | |
| 58 | + #define STLEP3IN BIT(4) | |
| 59 | + #define STLEP2IN BIT(3) | |
| 60 | + #define STLEP1OUT BIT(2) | |
| 61 | + #define STLEP0OUT BIT(1) | |
| 62 | + #define STLEP0IN BIT(0) | |
| 63 | + | |
| 64 | +#define MAX3420_REG_CLRTOGS 10 | |
| 65 | + #define EP3DISAB BIT(7) | |
| 66 | + #define EP2DISAB BIT(6) | |
| 67 | + #define EP1DISAB BIT(5) | |
| 68 | + #define CTGEP3IN BIT(4) | |
| 69 | + #define CTGEP2IN BIT(3) | |
| 70 | + #define CTGEP1OUT BIT(2) | |
| 71 | + | |
| 72 | +#define MAX3420_REG_EPIRQ 11 | |
| 73 | +#define MAX3420_REG_EPIEN 12 | |
| 74 | + #define SUDAVIRQ BIT(5) | |
| 75 | + #define IN3BAVIRQ BIT(4) | |
| 76 | + #define IN2BAVIRQ BIT(3) | |
| 77 | + #define OUT1DAVIRQ BIT(2) | |
| 78 | + #define OUT0DAVIRQ BIT(1) | |
| 79 | + #define IN0BAVIRQ BIT(0) | |
| 80 | + | |
| 81 | +#define MAX3420_REG_USBIRQ 13 | |
| 82 | +#define MAX3420_REG_USBIEN 14 | |
| 83 | + #define OSCOKIRQ BIT(0) | |
| 84 | + #define RWUDNIRQ BIT(1) | |
| 85 | + #define BUSACTIRQ BIT(2) | |
| 86 | + #define URESIRQ BIT(3) | |
| 87 | + #define SUSPIRQ BIT(4) | |
| 88 | + #define NOVBUSIRQ BIT(5) | |
| 89 | + #define VBUSIRQ BIT(6) | |
| 90 | + #define URESDNIRQ BIT(7) | |
| 91 | + | |
| 92 | +#define MAX3420_REG_USBCTL 15 | |
| 93 | + #define HOSCSTEN BIT(7) | |
| 94 | + #define VBGATE BIT(6) | |
| 95 | + #define CHIPRES BIT(5) | |
| 96 | + #define PWRDOWN BIT(4) | |
| 97 | + #define CONNECT BIT(3) | |
| 98 | + #define SIGRWU BIT(2) | |
| 99 | + | |
| 100 | +#define MAX3420_REG_CPUCTL 16 | |
| 101 | + #define IE BIT(0) | |
| 102 | + | |
| 103 | +#define MAX3420_REG_PINCTL 17 | |
| 104 | + #define EP3INAK BIT(7) | |
| 105 | + #define EP2INAK BIT(6) | |
| 106 | + #define EP0INAK BIT(5) | |
| 107 | + #define FDUPSPI BIT(4) | |
| 108 | + #define INTLEVEL BIT(3) | |
| 109 | + #define POSINT BIT(2) | |
| 110 | + #define GPXB BIT(1) | |
| 111 | + #define GPXA BIT(0) | |
| 112 | + | |
| 113 | +#define MAX3420_REG_REVISION 18 | |
| 114 | + | |
| 115 | +#define MAX3420_REG_FNADDR 19 | |
| 116 | + #define FNADDR_MASK 0x7f | |
| 117 | + | |
| 118 | +#define MAX3420_REG_IOPINS 20 | |
| 119 | +#define MAX3420_REG_IOPINS2 21 | |
| 120 | +#define MAX3420_REG_GPINIRQ 22 | |
| 121 | +#define MAX3420_REG_GPINIEN 23 | |
| 122 | +#define MAX3420_REG_GPINPOL 24 | |
| 123 | +#define MAX3420_REG_HIRQ 25 | |
| 124 | +#define MAX3420_REG_HIEN 26 | |
| 125 | +#define MAX3420_REG_MODE 27 | |
| 126 | +#define MAX3420_REG_PERADDR 28 | |
| 127 | +#define MAX3420_REG_HCTL 29 | |
| 128 | +#define MAX3420_REG_HXFR 30 | |
| 129 | +#define MAX3420_REG_HRSL 31 | |
| 130 | + | |
| 131 | +#define ENABLE_IRQ BIT(0) | |
| 132 | +#define IOPIN_UPDATE BIT(1) | |
| 133 | +#define REMOTE_WAKEUP BIT(2) | |
| 134 | +#define CONNECT_HOST GENMASK(4, 3) | |
| 135 | +#define HCONNECT (1 << 3) | |
| 136 | +#define HDISCONNECT (3 << 3) | |
| 137 | +#define UDC_START GENMASK(6, 5) | |
| 138 | +#define START (1 << 5) | |
| 139 | +#define STOP (3 << 5) | |
| 140 | +#define ENABLE_EP GENMASK(8, 7) | |
| 141 | +#define ENABLE (1 << 7) | |
| 142 | +#define DISABLE (3 << 7) | |
| 143 | +#define STALL_EP GENMASK(10, 9) | |
| 144 | +#define STALL (1 << 9) | |
| 145 | +#define UNSTALL (3 << 9) | |
| 146 | + | |
| 147 | +#define MAX3420_CMD(c) FIELD_PREP(GENMASK(7, 3), c) | |
| 148 | +#define MAX3420_SPI_CMD_RD(c) (MAX3420_CMD(c) | (0 << 1)) | |
| 149 | +#define MAX3420_SPI_CMD_WR(c) (MAX3420_CMD(c) | (1 << 1)) | |
| 150 | + | |
| 151 | +struct max3420_req { | |
| 152 | + struct usb_request usb_req; | |
| 153 | + struct list_head queue; | |
| 154 | + struct max3420_ep *ep; | |
| 155 | +}; | |
| 156 | + | |
| 157 | +struct max3420_ep { | |
| 158 | + struct usb_ep ep_usb; | |
| 159 | + struct max3420_udc *udc; | |
| 160 | + struct list_head queue; | |
| 161 | + char name[MAX3420_EPNAME_SIZE]; | |
| 162 | + unsigned int maxpacket; | |
| 163 | + spinlock_t lock; | |
| 164 | + int halted; | |
| 165 | + u32 todo; | |
| 166 | + int id; | |
| 167 | +}; | |
| 168 | + | |
| 169 | +struct max3420_udc { | |
| 170 | + struct usb_gadget gadget; | |
| 171 | + struct max3420_ep ep[MAX3420_MAX_EPS]; | |
| 172 | + struct usb_gadget_driver *driver; | |
| 173 | + struct task_struct *thread_task; | |
| 174 | + int remote_wkp, is_selfpowered; | |
| 175 | + bool vbus_active, softconnect; | |
| 176 | + struct usb_ctrlrequest setup; | |
| 177 | + struct mutex spi_bus_mutex; | |
| 178 | + struct max3420_req ep0req; | |
| 179 | + struct spi_device *spi; | |
| 180 | + struct device *dev; | |
| 181 | + spinlock_t lock; | |
| 182 | + bool suspended; | |
| 183 | + u8 ep0buf[64]; | |
| 184 | + u32 todo; | |
| 185 | +}; | |
| 186 | + | |
| 187 | +#define to_max3420_req(r) container_of((r), struct max3420_req, usb_req) | |
| 188 | +#define to_max3420_ep(e) container_of((e), struct max3420_ep, ep_usb) | |
| 189 | +#define to_udc(g) container_of((g), struct max3420_udc, gadget) | |
| 190 | + | |
| 191 | +#define DRIVER_DESC "MAX3420 USB Device-Mode Driver" | |
| 192 | +static const char driver_name[] = "max3420-udc"; | |
| 193 | + | |
| 194 | +/* Control endpoint configuration.*/ | |
| 195 | +static const struct usb_endpoint_descriptor ep0_desc = { | |
| 196 | + .bEndpointAddress = USB_DIR_OUT, | |
| 197 | + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, | |
| 198 | + .wMaxPacketSize = cpu_to_le16(MAX3420_EP_MAX_PACKET), | |
| 199 | +}; | |
| 200 | + | |
| 201 | +static void spi_ack_ctrl(struct max3420_udc *udc) | |
| 202 | +{ | |
| 203 | + struct spi_device *spi = udc->spi; | |
| 204 | + struct spi_transfer transfer; | |
| 205 | + struct spi_message msg; | |
| 206 | + u8 txdata[1]; | |
| 207 | + | |
| 208 | + memset(&transfer, 0, sizeof(transfer)); | |
| 209 | + | |
| 210 | + spi_message_init(&msg); | |
| 211 | + | |
| 212 | + txdata[0] = MAX3420_ACKSTAT; | |
| 213 | + transfer.tx_buf = txdata; | |
| 214 | + transfer.len = 1; | |
| 215 | + | |
| 216 | + spi_message_add_tail(&transfer, &msg); | |
| 217 | + spi_sync(spi, &msg); | |
| 218 | +} | |
| 219 | + | |
| 220 | +static u8 spi_rd8_ack(struct max3420_udc *udc, u8 reg, int actstat) | |
| 221 | +{ | |
| 222 | + struct spi_device *spi = udc->spi; | |
| 223 | + struct spi_transfer transfer; | |
| 224 | + struct spi_message msg; | |
| 225 | + u8 txdata[2], rxdata[2]; | |
| 226 | + | |
| 227 | + memset(&transfer, 0, sizeof(transfer)); | |
| 228 | + | |
| 229 | + spi_message_init(&msg); | |
| 230 | + | |
| 231 | + txdata[0] = MAX3420_SPI_CMD_RD(reg) | (actstat ? MAX3420_ACKSTAT : 0); | |
| 232 | + transfer.tx_buf = txdata; | |
| 233 | + transfer.rx_buf = rxdata; | |
| 234 | + transfer.len = 2; | |
| 235 | + | |
| 236 | + spi_message_add_tail(&transfer, &msg); | |
| 237 | + spi_sync(spi, &msg); | |
| 238 | + | |
| 239 | + return rxdata[1]; | |
| 240 | +} | |
| 241 | + | |
| 242 | +static u8 spi_rd8(struct max3420_udc *udc, u8 reg) | |
| 243 | +{ | |
| 244 | + return spi_rd8_ack(udc, reg, 0); | |
| 245 | +} | |
| 246 | + | |
| 247 | +static void spi_wr8_ack(struct max3420_udc *udc, u8 reg, u8 val, int actstat) | |
| 248 | +{ | |
| 249 | + struct spi_device *spi = udc->spi; | |
| 250 | + struct spi_transfer transfer; | |
| 251 | + struct spi_message msg; | |
| 252 | + u8 txdata[2]; | |
| 253 | + | |
| 254 | + memset(&transfer, 0, sizeof(transfer)); | |
| 255 | + | |
| 256 | + spi_message_init(&msg); | |
| 257 | + | |
| 258 | + txdata[0] = MAX3420_SPI_CMD_WR(reg) | (actstat ? MAX3420_ACKSTAT : 0); | |
| 259 | + txdata[1] = val; | |
| 260 | + | |
| 261 | + transfer.tx_buf = txdata; | |
| 262 | + transfer.len = 2; | |
| 263 | + | |
| 264 | + spi_message_add_tail(&transfer, &msg); | |
| 265 | + spi_sync(spi, &msg); | |
| 266 | +} | |
| 267 | + | |
| 268 | +static void spi_wr8(struct max3420_udc *udc, u8 reg, u8 val) | |
| 269 | +{ | |
| 270 | + spi_wr8_ack(udc, reg, val, 0); | |
| 271 | +} | |
| 272 | + | |
| 273 | +static void spi_rd_buf(struct max3420_udc *udc, u8 reg, void *buf, u8 len) | |
| 274 | +{ | |
| 275 | + struct spi_device *spi = udc->spi; | |
| 276 | + struct spi_transfer transfer; | |
| 277 | + struct spi_message msg; | |
| 278 | + u8 local_buf[MAX3420_EP_MAX_PACKET + 1] = {}; | |
| 279 | + | |
| 280 | + memset(&transfer, 0, sizeof(transfer)); | |
| 281 | + | |
| 282 | + spi_message_init(&msg); | |
| 283 | + | |
| 284 | + local_buf[0] = MAX3420_SPI_CMD_RD(reg); | |
| 285 | + transfer.tx_buf = &local_buf[0]; | |
| 286 | + transfer.rx_buf = &local_buf[0]; | |
| 287 | + transfer.len = len + 1; | |
| 288 | + | |
| 289 | + spi_message_add_tail(&transfer, &msg); | |
| 290 | + spi_sync(spi, &msg); | |
| 291 | + | |
| 292 | + memcpy(buf, &local_buf[1], len); | |
| 293 | +} | |
| 294 | + | |
| 295 | +static void spi_wr_buf(struct max3420_udc *udc, u8 reg, void *buf, u8 len) | |
| 296 | +{ | |
| 297 | + struct spi_device *spi = udc->spi; | |
| 298 | + struct spi_transfer transfer; | |
| 299 | + struct spi_message msg; | |
| 300 | + u8 local_buf[MAX3420_EP_MAX_PACKET + 1] = {}; | |
| 301 | + | |
| 302 | + memset(&transfer, 0, sizeof(transfer)); | |
| 303 | + | |
| 304 | + spi_message_init(&msg); | |
| 305 | + | |
| 306 | + local_buf[0] = MAX3420_SPI_CMD_WR(reg); | |
| 307 | + memcpy(&local_buf[1], buf, len); | |
| 308 | + | |
| 309 | + transfer.tx_buf = local_buf; | |
| 310 | + transfer.len = len + 1; | |
| 311 | + | |
| 312 | + spi_message_add_tail(&transfer, &msg); | |
| 313 | + spi_sync(spi, &msg); | |
| 314 | +} | |
| 315 | + | |
| 316 | +static int spi_max3420_enable(struct max3420_ep *ep) | |
| 317 | +{ | |
| 318 | + struct max3420_udc *udc = ep->udc; | |
| 319 | + unsigned long flags; | |
| 320 | + u8 epdis, epien; | |
| 321 | + int todo; | |
| 322 | + | |
| 323 | + spin_lock_irqsave(&ep->lock, flags); | |
| 324 | + todo = ep->todo & ENABLE_EP; | |
| 325 | + ep->todo &= ~ENABLE_EP; | |
| 326 | + spin_unlock_irqrestore(&ep->lock, flags); | |
| 327 | + | |
| 328 | + if (!todo || ep->id == 0) | |
| 329 | + return false; | |
| 330 | + | |
| 331 | + epien = spi_rd8(udc, MAX3420_REG_EPIEN); | |
| 332 | + epdis = spi_rd8(udc, MAX3420_REG_CLRTOGS); | |
| 333 | + | |
| 334 | + if (todo == ENABLE) { | |
| 335 | + epdis &= ~BIT(ep->id + 4); | |
| 336 | + epien |= BIT(ep->id + 1); | |
| 337 | + } else { | |
| 338 | + epdis |= BIT(ep->id + 4); | |
| 339 | + epien &= ~BIT(ep->id + 1); | |
| 340 | + } | |
| 341 | + | |
| 342 | + spi_wr8(udc, MAX3420_REG_CLRTOGS, epdis); | |
| 343 | + spi_wr8(udc, MAX3420_REG_EPIEN, epien); | |
| 344 | + | |
| 345 | + return true; | |
| 346 | +} | |
| 347 | + | |
| 348 | +static int spi_max3420_stall(struct max3420_ep *ep) | |
| 349 | +{ | |
| 350 | + struct max3420_udc *udc = ep->udc; | |
| 351 | + unsigned long flags; | |
| 352 | + u8 epstalls; | |
| 353 | + int todo; | |
| 354 | + | |
| 355 | + spin_lock_irqsave(&ep->lock, flags); | |
| 356 | + todo = ep->todo & STALL_EP; | |
| 357 | + ep->todo &= ~STALL_EP; | |
| 358 | + spin_unlock_irqrestore(&ep->lock, flags); | |
| 359 | + | |
| 360 | + if (!todo || ep->id == 0) | |
| 361 | + return false; | |
| 362 | + | |
| 363 | + epstalls = spi_rd8(udc, MAX3420_REG_EPSTALLS); | |
| 364 | + if (todo == STALL) { | |
| 365 | + ep->halted = 1; | |
| 366 | + epstalls |= BIT(ep->id + 1); | |
| 367 | + } else { | |
| 368 | + u8 clrtogs; | |
| 369 | + | |
| 370 | + ep->halted = 0; | |
| 371 | + epstalls &= ~BIT(ep->id + 1); | |
| 372 | + clrtogs = spi_rd8(udc, MAX3420_REG_CLRTOGS); | |
| 373 | + clrtogs |= BIT(ep->id + 1); | |
| 374 | + spi_wr8(udc, MAX3420_REG_CLRTOGS, clrtogs); | |
| 375 | + } | |
| 376 | + spi_wr8(udc, MAX3420_REG_EPSTALLS, epstalls | ACKSTAT); | |
| 377 | + | |
| 378 | + return true; | |
| 379 | +} | |
| 380 | + | |
| 381 | +static int spi_max3420_rwkup(struct max3420_udc *udc) | |
| 382 | +{ | |
| 383 | + unsigned long flags; | |
| 384 | + int wake_remote; | |
| 385 | + u8 usbctl; | |
| 386 | + | |
| 387 | + spin_lock_irqsave(&udc->lock, flags); | |
| 388 | + wake_remote = udc->todo & REMOTE_WAKEUP; | |
| 389 | + udc->todo &= ~REMOTE_WAKEUP; | |
| 390 | + spin_unlock_irqrestore(&udc->lock, flags); | |
| 391 | + | |
| 392 | + if (!wake_remote || !udc->suspended) | |
| 393 | + return false; | |
| 394 | + | |
| 395 | + /* Set Remote-WkUp Signal*/ | |
| 396 | + usbctl = spi_rd8(udc, MAX3420_REG_USBCTL); | |
| 397 | + usbctl |= SIGRWU; | |
| 398 | + spi_wr8(udc, MAX3420_REG_USBCTL, usbctl); | |
| 399 | + | |
| 400 | + msleep_interruptible(5); | |
| 401 | + | |
| 402 | + /* Clear Remote-WkUp Signal*/ | |
| 403 | + usbctl = spi_rd8(udc, MAX3420_REG_USBCTL); | |
| 404 | + usbctl &= ~SIGRWU; | |
| 405 | + spi_wr8(udc, MAX3420_REG_USBCTL, usbctl); | |
| 406 | + | |
| 407 | + udc->suspended = false; | |
| 408 | + | |
| 409 | + return true; | |
| 410 | +} | |
| 411 | + | |
| 412 | +static void max3420_nuke(struct max3420_ep *ep, int status); | |
| 413 | +static void __max3420_stop(struct max3420_udc *udc) | |
| 414 | +{ | |
| 415 | + u8 val; | |
| 416 | + int i; | |
| 417 | + | |
| 418 | + /* clear all pending requests */ | |
| 419 | + for (i = 1; i < MAX3420_MAX_EPS; i++) | |
| 420 | + max3420_nuke(&udc->ep[i], -ECONNRESET); | |
| 421 | + | |
| 422 | + /* Disable IRQ to CPU */ | |
| 423 | + spi_wr8(udc, MAX3420_REG_CPUCTL, 0); | |
| 424 | + | |
| 425 | + val = spi_rd8(udc, MAX3420_REG_USBCTL); | |
| 426 | + val |= PWRDOWN; | |
| 427 | + if (udc->is_selfpowered) | |
| 428 | + val &= ~HOSCSTEN; | |
| 429 | + else | |
| 430 | + val |= HOSCSTEN; | |
| 431 | + spi_wr8(udc, MAX3420_REG_USBCTL, val); | |
| 432 | +} | |
| 433 | + | |
| 434 | +static void __max3420_start(struct max3420_udc *udc) | |
| 435 | +{ | |
| 436 | + u8 val; | |
| 437 | + | |
| 438 | + /* Need this delay if bus-powered, | |
| 439 | + * but even for self-powered it helps stability | |
| 440 | + */ | |
| 441 | + msleep_interruptible(250); | |
| 442 | + | |
| 443 | + /* configure SPI */ | |
| 444 | + spi_wr8(udc, MAX3420_REG_PINCTL, FDUPSPI); | |
| 445 | + | |
| 446 | + /* Chip Reset */ | |
| 447 | + spi_wr8(udc, MAX3420_REG_USBCTL, CHIPRES); | |
| 448 | + msleep_interruptible(5); | |
| 449 | + spi_wr8(udc, MAX3420_REG_USBCTL, 0); | |
| 450 | + | |
| 451 | + /* Poll for OSC to stabilize */ | |
| 452 | + while (1) { | |
| 453 | + val = spi_rd8(udc, MAX3420_REG_USBIRQ); | |
| 454 | + if (val & OSCOKIRQ) | |
| 455 | + break; | |
| 456 | + cond_resched(); | |
| 457 | + } | |
| 458 | + | |
| 459 | + /* Enable PULL-UP only when Vbus detected */ | |
| 460 | + val = spi_rd8(udc, MAX3420_REG_USBCTL); | |
| 461 | + val |= VBGATE | CONNECT; | |
| 462 | + spi_wr8(udc, MAX3420_REG_USBCTL, val); | |
| 463 | + | |
| 464 | + val = URESDNIRQ | URESIRQ; | |
| 465 | + if (udc->is_selfpowered) | |
| 466 | + val |= NOVBUSIRQ; | |
| 467 | + spi_wr8(udc, MAX3420_REG_USBIEN, val); | |
| 468 | + | |
| 469 | + /* Enable only EP0 interrupts */ | |
| 470 | + val = IN0BAVIRQ | OUT0DAVIRQ | SUDAVIRQ; | |
| 471 | + spi_wr8(udc, MAX3420_REG_EPIEN, val); | |
| 472 | + | |
| 473 | + /* Enable IRQ to CPU */ | |
| 474 | + spi_wr8(udc, MAX3420_REG_CPUCTL, IE); | |
| 475 | +} | |
| 476 | + | |
| 477 | +static int max3420_start(struct max3420_udc *udc) | |
| 478 | +{ | |
| 479 | + unsigned long flags; | |
| 480 | + int todo; | |
| 481 | + | |
| 482 | + spin_lock_irqsave(&udc->lock, flags); | |
| 483 | + todo = udc->todo & UDC_START; | |
| 484 | + udc->todo &= ~UDC_START; | |
| 485 | + spin_unlock_irqrestore(&udc->lock, flags); | |
| 486 | + | |
| 487 | + if (!todo) | |
| 488 | + return false; | |
| 489 | + | |
| 490 | + if (udc->vbus_active && udc->softconnect) | |
| 491 | + __max3420_start(udc); | |
| 492 | + else | |
| 493 | + __max3420_stop(udc); | |
| 494 | + | |
| 495 | + return true; | |
| 496 | +} | |
| 497 | + | |
| 498 | +static irqreturn_t max3420_vbus_handler(int irq, void *dev_id) | |
| 499 | +{ | |
| 500 | + struct max3420_udc *udc = dev_id; | |
| 501 | + unsigned long flags; | |
| 502 | + | |
| 503 | + spin_lock_irqsave(&udc->lock, flags); | |
| 504 | + /* its a vbus change interrupt */ | |
| 505 | + udc->vbus_active = !udc->vbus_active; | |
| 506 | + udc->todo |= UDC_START; | |
| 507 | + usb_udc_vbus_handler(&udc->gadget, udc->vbus_active); | |
| 508 | + usb_gadget_set_state(&udc->gadget, udc->vbus_active | |
| 509 | + ? USB_STATE_POWERED : USB_STATE_NOTATTACHED); | |
| 510 | + spin_unlock_irqrestore(&udc->lock, flags); | |
| 511 | + | |
| 512 | + if (udc->thread_task && | |
| 513 | + udc->thread_task->state != TASK_RUNNING) | |
| 514 | + wake_up_process(udc->thread_task); | |
| 515 | + | |
| 516 | + return IRQ_HANDLED; | |
| 517 | +} | |
| 518 | + | |
| 519 | +static irqreturn_t max3420_irq_handler(int irq, void *dev_id) | |
| 520 | +{ | |
| 521 | + struct max3420_udc *udc = dev_id; | |
| 522 | + struct spi_device *spi = udc->spi; | |
| 523 | + unsigned long flags; | |
| 524 | + | |
| 525 | + spin_lock_irqsave(&udc->lock, flags); | |
| 526 | + if ((udc->todo & ENABLE_IRQ) == 0) { | |
| 527 | + disable_irq_nosync(spi->irq); | |
| 528 | + udc->todo |= ENABLE_IRQ; | |
| 529 | + } | |
| 530 | + spin_unlock_irqrestore(&udc->lock, flags); | |
| 531 | + | |
| 532 | + if (udc->thread_task && | |
| 533 | + udc->thread_task->state != TASK_RUNNING) | |
| 534 | + wake_up_process(udc->thread_task); | |
| 535 | + | |
| 536 | + return IRQ_HANDLED; | |
| 537 | +} | |
| 538 | + | |
| 539 | +static void max3420_getstatus(struct max3420_udc *udc) | |
| 540 | +{ | |
| 541 | + struct max3420_ep *ep; | |
| 542 | + u16 status = 0; | |
| 543 | + | |
| 544 | + switch (udc->setup.bRequestType & USB_RECIP_MASK) { | |
| 545 | + case USB_RECIP_DEVICE: | |
| 546 | + /* Get device status */ | |
| 547 | + status = udc->gadget.is_selfpowered << USB_DEVICE_SELF_POWERED; | |
| 548 | + status |= (udc->remote_wkp << USB_DEVICE_REMOTE_WAKEUP); | |
| 549 | + break; | |
| 550 | + case USB_RECIP_INTERFACE: | |
| 551 | + if (udc->driver->setup(&udc->gadget, &udc->setup) < 0) | |
| 552 | + goto stall; | |
| 553 | + break; | |
| 554 | + case USB_RECIP_ENDPOINT: | |
| 555 | + ep = &udc->ep[udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK]; | |
| 556 | + if (udc->setup.wIndex & USB_DIR_IN) { | |
| 557 | + if (!ep->ep_usb.caps.dir_in) | |
| 558 | + goto stall; | |
| 559 | + } else { | |
| 560 | + if (!ep->ep_usb.caps.dir_out) | |
| 561 | + goto stall; | |
| 562 | + } | |
| 563 | + if (ep->halted) | |
| 564 | + status = 1 << USB_ENDPOINT_HALT; | |
| 565 | + break; | |
| 566 | + default: | |
| 567 | + goto stall; | |
| 568 | + } | |
| 569 | + | |
| 570 | + status = cpu_to_le16(status); | |
| 571 | + spi_wr_buf(udc, MAX3420_REG_EP0FIFO, &status, 2); | |
| 572 | + spi_wr8_ack(udc, MAX3420_REG_EP0BC, 2, 1); | |
| 573 | + return; | |
| 574 | +stall: | |
| 575 | + dev_err(udc->dev, "Can't respond to getstatus request\n"); | |
| 576 | + spi_wr8(udc, MAX3420_REG_EPSTALLS, STLEP0IN | STLEP0OUT | STLSTAT); | |
| 577 | +} | |
| 578 | + | |
| 579 | +static void max3420_set_clear_feature(struct max3420_udc *udc) | |
| 580 | +{ | |
| 581 | + struct max3420_ep *ep; | |
| 582 | + int set = udc->setup.bRequest == USB_REQ_SET_FEATURE; | |
| 583 | + unsigned long flags; | |
| 584 | + int id; | |
| 585 | + | |
| 586 | + switch (udc->setup.bRequestType) { | |
| 587 | + case USB_RECIP_DEVICE: | |
| 588 | + if (udc->setup.wValue != USB_DEVICE_REMOTE_WAKEUP) | |
| 589 | + break; | |
| 590 | + | |
| 591 | + if (udc->setup.bRequest == USB_REQ_SET_FEATURE) | |
| 592 | + udc->remote_wkp = 1; | |
| 593 | + else | |
| 594 | + udc->remote_wkp = 0; | |
| 595 | + | |
| 596 | + return spi_ack_ctrl(udc); | |
| 597 | + | |
| 598 | + case USB_RECIP_ENDPOINT: | |
| 599 | + if (udc->setup.wValue != USB_ENDPOINT_HALT) | |
| 600 | + break; | |
| 601 | + | |
| 602 | + id = udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK; | |
| 603 | + ep = &udc->ep[id]; | |
| 604 | + | |
| 605 | + spin_lock_irqsave(&ep->lock, flags); | |
| 606 | + ep->todo &= ~STALL_EP; | |
| 607 | + if (set) | |
| 608 | + ep->todo |= STALL; | |
| 609 | + else | |
| 610 | + ep->todo |= UNSTALL; | |
| 611 | + spin_unlock_irqrestore(&ep->lock, flags); | |
| 612 | + | |
| 613 | + spi_max3420_stall(ep); | |
| 614 | + return; | |
| 615 | + default: | |
| 616 | + break; | |
| 617 | + } | |
| 618 | + | |
| 619 | + dev_err(udc->dev, "Can't respond to SET/CLEAR FEATURE\n"); | |
| 620 | + spi_wr8(udc, MAX3420_REG_EPSTALLS, STLEP0IN | STLEP0OUT | STLSTAT); | |
| 621 | +} | |
| 622 | + | |
| 623 | +static void max3420_handle_setup(struct max3420_udc *udc) | |
| 624 | +{ | |
| 625 | + struct usb_ctrlrequest setup; | |
| 626 | + u8 addr; | |
| 627 | + | |
| 628 | + spi_rd_buf(udc, MAX3420_REG_SUDFIFO, (void *)&setup, 8); | |
| 629 | + | |
| 630 | + udc->setup = setup; | |
| 631 | + udc->setup.wValue = cpu_to_le16(setup.wValue); | |
| 632 | + udc->setup.wIndex = cpu_to_le16(setup.wIndex); | |
| 633 | + udc->setup.wLength = cpu_to_le16(setup.wLength); | |
| 634 | + | |
| 635 | + switch (udc->setup.bRequest) { | |
| 636 | + case USB_REQ_GET_STATUS: | |
| 637 | + /* Data+Status phase form udc */ | |
| 638 | + if ((udc->setup.bRequestType & | |
| 639 | + (USB_DIR_IN | USB_TYPE_MASK)) != | |
| 640 | + (USB_DIR_IN | USB_TYPE_STANDARD)) { | |
| 641 | + break; | |
| 642 | + } | |
| 643 | + return max3420_getstatus(udc); | |
| 644 | + case USB_REQ_SET_ADDRESS: | |
| 645 | + /* Status phase from udc */ | |
| 646 | + if (udc->setup.bRequestType != (USB_DIR_OUT | | |
| 647 | + USB_TYPE_STANDARD | USB_RECIP_DEVICE)) { | |
| 648 | + break; | |
| 649 | + } | |
| 650 | + addr = spi_rd8_ack(udc, MAX3420_REG_FNADDR, 1); | |
| 651 | + dev_dbg(udc->dev, "Assigned Address=%d\n", udc->setup.wValue); | |
| 652 | + return; | |
| 653 | + case USB_REQ_CLEAR_FEATURE: | |
| 654 | + case USB_REQ_SET_FEATURE: | |
| 655 | + /* Requests with no data phase, status phase from udc */ | |
| 656 | + if ((udc->setup.bRequestType & USB_TYPE_MASK) | |
| 657 | + != USB_TYPE_STANDARD) | |
| 658 | + break; | |
| 659 | + return max3420_set_clear_feature(udc); | |
| 660 | + default: | |
| 661 | + break; | |
| 662 | + } | |
| 663 | + | |
| 664 | + if (udc->driver->setup(&udc->gadget, &setup) < 0) { | |
| 665 | + /* Stall EP0 */ | |
| 666 | + spi_wr8(udc, MAX3420_REG_EPSTALLS, | |
| 667 | + STLEP0IN | STLEP0OUT | STLSTAT); | |
| 668 | + } | |
| 669 | +} | |
| 670 | + | |
| 671 | +static void max3420_req_done(struct max3420_req *req, int status) | |
| 672 | +{ | |
| 673 | + struct max3420_ep *ep = req->ep; | |
| 674 | + struct max3420_udc *udc = ep->udc; | |
| 675 | + | |
| 676 | + if (req->usb_req.status == -EINPROGRESS) | |
| 677 | + req->usb_req.status = status; | |
| 678 | + else | |
| 679 | + status = req->usb_req.status; | |
| 680 | + | |
| 681 | + if (status && status != -ESHUTDOWN) | |
| 682 | + dev_err(udc->dev, "%s done %p, status %d\n", | |
| 683 | + ep->ep_usb.name, req, status); | |
| 684 | + | |
| 685 | + if (req->usb_req.complete) | |
| 686 | + req->usb_req.complete(&ep->ep_usb, &req->usb_req); | |
| 687 | +} | |
| 688 | + | |
| 689 | +static int max3420_do_data(struct max3420_udc *udc, int ep_id, int in) | |
| 690 | +{ | |
| 691 | + struct max3420_ep *ep = &udc->ep[ep_id]; | |
| 692 | + struct max3420_req *req; | |
| 693 | + int done, length, psz; | |
| 694 | + void *buf; | |
| 695 | + | |
| 696 | + if (list_empty(&ep->queue)) | |
| 697 | + return false; | |
| 698 | + | |
| 699 | + req = list_first_entry(&ep->queue, struct max3420_req, queue); | |
| 700 | + buf = req->usb_req.buf + req->usb_req.actual; | |
| 701 | + | |
| 702 | + psz = ep->ep_usb.maxpacket; | |
| 703 | + length = req->usb_req.length - req->usb_req.actual; | |
| 704 | + length = min(length, psz); | |
| 705 | + | |
| 706 | + if (length == 0) { | |
| 707 | + done = 1; | |
| 708 | + goto xfer_done; | |
| 709 | + } | |
| 710 | + | |
| 711 | + done = 0; | |
| 712 | + if (in) { | |
| 713 | + prefetch(buf); | |
| 714 | + spi_wr_buf(udc, MAX3420_REG_EP0FIFO + ep_id, buf, length); | |
| 715 | + spi_wr8(udc, MAX3420_REG_EP0BC + ep_id, length); | |
| 716 | + if (length < psz) | |
| 717 | + done = 1; | |
| 718 | + } else { | |
| 719 | + psz = spi_rd8(udc, MAX3420_REG_EP0BC + ep_id); | |
| 720 | + length = min(length, psz); | |
| 721 | + prefetchw(buf); | |
| 722 | + spi_rd_buf(udc, MAX3420_REG_EP0FIFO + ep_id, buf, length); | |
| 723 | + if (length < ep->ep_usb.maxpacket) | |
| 724 | + done = 1; | |
| 725 | + } | |
| 726 | + | |
| 727 | + req->usb_req.actual += length; | |
| 728 | + | |
| 729 | + if (req->usb_req.actual == req->usb_req.length) | |
| 730 | + done = 1; | |
| 731 | + | |
| 732 | +xfer_done: | |
| 733 | + if (done) { | |
| 734 | + unsigned long flags; | |
| 735 | + | |
| 736 | + spin_lock_irqsave(&ep->lock, flags); | |
| 737 | + list_del_init(&req->queue); | |
| 738 | + spin_unlock_irqrestore(&ep->lock, flags); | |
| 739 | + | |
| 740 | + if (ep_id == 0) | |
| 741 | + spi_ack_ctrl(udc); | |
| 742 | + | |
| 743 | + max3420_req_done(req, 0); | |
| 744 | + } | |
| 745 | + | |
| 746 | + return true; | |
| 747 | +} | |
| 748 | + | |
| 749 | +static int max3420_handle_irqs(struct max3420_udc *udc) | |
| 750 | +{ | |
| 751 | + u8 epien, epirq, usbirq, usbien, reg[4]; | |
| 752 | + bool ret = false; | |
| 753 | + | |
| 754 | + spi_rd_buf(udc, MAX3420_REG_EPIRQ, reg, 4); | |
| 755 | + epirq = reg[0]; | |
| 756 | + epien = reg[1]; | |
| 757 | + usbirq = reg[2]; | |
| 758 | + usbien = reg[3]; | |
| 759 | + | |
| 760 | + usbirq &= usbien; | |
| 761 | + epirq &= epien; | |
| 762 | + | |
| 763 | + if (epirq & SUDAVIRQ) { | |
| 764 | + spi_wr8(udc, MAX3420_REG_EPIRQ, SUDAVIRQ); | |
| 765 | + max3420_handle_setup(udc); | |
| 766 | + return true; | |
| 767 | + } | |
| 768 | + | |
| 769 | + if (usbirq & VBUSIRQ) { | |
| 770 | + spi_wr8(udc, MAX3420_REG_USBIRQ, VBUSIRQ); | |
| 771 | + dev_dbg(udc->dev, "Cable plugged in\n"); | |
| 772 | + return true; | |
| 773 | + } | |
| 774 | + | |
| 775 | + if (usbirq & NOVBUSIRQ) { | |
| 776 | + spi_wr8(udc, MAX3420_REG_USBIRQ, NOVBUSIRQ); | |
| 777 | + dev_dbg(udc->dev, "Cable pulled out\n"); | |
| 778 | + return true; | |
| 779 | + } | |
| 780 | + | |
| 781 | + if (usbirq & URESIRQ) { | |
| 782 | + spi_wr8(udc, MAX3420_REG_USBIRQ, URESIRQ); | |
| 783 | + dev_dbg(udc->dev, "USB Reset - Start\n"); | |
| 784 | + return true; | |
| 785 | + } | |
| 786 | + | |
| 787 | + if (usbirq & URESDNIRQ) { | |
| 788 | + spi_wr8(udc, MAX3420_REG_USBIRQ, URESDNIRQ); | |
| 789 | + dev_dbg(udc->dev, "USB Reset - END\n"); | |
| 790 | + spi_wr8(udc, MAX3420_REG_USBIEN, URESDNIRQ | URESIRQ); | |
| 791 | + spi_wr8(udc, MAX3420_REG_EPIEN, SUDAVIRQ | IN0BAVIRQ | |
| 792 | + | OUT0DAVIRQ); | |
| 793 | + return true; | |
| 794 | + } | |
| 795 | + | |
| 796 | + if (usbirq & SUSPIRQ) { | |
| 797 | + spi_wr8(udc, MAX3420_REG_USBIRQ, SUSPIRQ); | |
| 798 | + dev_dbg(udc->dev, "USB Suspend - Enter\n"); | |
| 799 | + udc->suspended = true; | |
| 800 | + return true; | |
| 801 | + } | |
| 802 | + | |
| 803 | + if (usbirq & BUSACTIRQ) { | |
| 804 | + spi_wr8(udc, MAX3420_REG_USBIRQ, BUSACTIRQ); | |
| 805 | + dev_dbg(udc->dev, "USB Suspend - Exit\n"); | |
| 806 | + udc->suspended = false; | |
| 807 | + return true; | |
| 808 | + } | |
| 809 | + | |
| 810 | + if (usbirq & RWUDNIRQ) { | |
| 811 | + spi_wr8(udc, MAX3420_REG_USBIRQ, RWUDNIRQ); | |
| 812 | + dev_dbg(udc->dev, "Asked Host to wakeup\n"); | |
| 813 | + return true; | |
| 814 | + } | |
| 815 | + | |
| 816 | + if (usbirq & OSCOKIRQ) { | |
| 817 | + spi_wr8(udc, MAX3420_REG_USBIRQ, OSCOKIRQ); | |
| 818 | + dev_dbg(udc->dev, "Osc stabilized, start work\n"); | |
| 819 | + return true; | |
| 820 | + } | |
| 821 | + | |
| 822 | + if (epirq & OUT0DAVIRQ && max3420_do_data(udc, 0, 0)) { | |
| 823 | + spi_wr8_ack(udc, MAX3420_REG_EPIRQ, OUT0DAVIRQ, 1); | |
| 824 | + ret = true; | |
| 825 | + } | |
| 826 | + | |
| 827 | + if (epirq & IN0BAVIRQ && max3420_do_data(udc, 0, 1)) | |
| 828 | + ret = true; | |
| 829 | + | |
| 830 | + if (epirq & OUT1DAVIRQ && max3420_do_data(udc, 1, 0)) { | |
| 831 | + spi_wr8_ack(udc, MAX3420_REG_EPIRQ, OUT1DAVIRQ, 1); | |
| 832 | + ret = true; | |
| 833 | + } | |
| 834 | + | |
| 835 | + if (epirq & IN2BAVIRQ && max3420_do_data(udc, 2, 1)) | |
| 836 | + ret = true; | |
| 837 | + | |
| 838 | + if (epirq & IN3BAVIRQ && max3420_do_data(udc, 3, 1)) | |
| 839 | + ret = true; | |
| 840 | + | |
| 841 | + return ret; | |
| 842 | +} | |
| 843 | + | |
| 844 | +static int max3420_thread(void *dev_id) | |
| 845 | +{ | |
| 846 | + struct max3420_udc *udc = dev_id; | |
| 847 | + struct spi_device *spi = udc->spi; | |
| 848 | + int i, loop_again = 1; | |
| 849 | + unsigned long flags; | |
| 850 | + | |
| 851 | + while (!kthread_should_stop()) { | |
| 852 | + if (!loop_again) { | |
| 853 | + ktime_t kt = ns_to_ktime(1000 * 1000 * 250); /* 250ms */ | |
| 854 | + | |
| 855 | + set_current_state(TASK_INTERRUPTIBLE); | |
| 856 | + | |
| 857 | + spin_lock_irqsave(&udc->lock, flags); | |
| 858 | + if (udc->todo & ENABLE_IRQ) { | |
| 859 | + enable_irq(spi->irq); | |
| 860 | + udc->todo &= ~ENABLE_IRQ; | |
| 861 | + } | |
| 862 | + spin_unlock_irqrestore(&udc->lock, flags); | |
| 863 | + | |
| 864 | + schedule_hrtimeout(&kt, HRTIMER_MODE_REL); | |
| 865 | + } | |
| 866 | + loop_again = 0; | |
| 867 | + | |
| 868 | + mutex_lock(&udc->spi_bus_mutex); | |
| 869 | + | |
| 870 | + /* If bus-vbus_active and disconnected */ | |
| 871 | + if (!udc->vbus_active || !udc->softconnect) | |
| 872 | + goto loop; | |
| 873 | + | |
| 874 | + if (max3420_start(udc)) { | |
| 875 | + loop_again = 1; | |
| 876 | + goto loop; | |
| 877 | + } | |
| 878 | + | |
| 879 | + if (max3420_handle_irqs(udc)) { | |
| 880 | + loop_again = 1; | |
| 881 | + goto loop; | |
| 882 | + } | |
| 883 | + | |
| 884 | + if (spi_max3420_rwkup(udc)) { | |
| 885 | + loop_again = 1; | |
| 886 | + goto loop; | |
| 887 | + } | |
| 888 | + | |
| 889 | + max3420_do_data(udc, 0, 1); /* get done with the EP0 ZLP */ | |
| 890 | + | |
| 891 | + for (i = 1; i < MAX3420_MAX_EPS; i++) { | |
| 892 | + struct max3420_ep *ep = &udc->ep[i]; | |
| 893 | + | |
| 894 | + if (spi_max3420_enable(ep)) | |
| 895 | + loop_again = 1; | |
| 896 | + if (spi_max3420_stall(ep)) | |
| 897 | + loop_again = 1; | |
| 898 | + } | |
| 899 | +loop: | |
| 900 | + mutex_unlock(&udc->spi_bus_mutex); | |
| 901 | + } | |
| 902 | + | |
| 903 | + set_current_state(TASK_RUNNING); | |
| 904 | + dev_info(udc->dev, "SPI thread exiting"); | |
| 905 | + return 0; | |
| 906 | +} | |
| 907 | + | |
| 908 | +static int max3420_ep_set_halt(struct usb_ep *_ep, int stall) | |
| 909 | +{ | |
| 910 | + struct max3420_ep *ep = to_max3420_ep(_ep); | |
| 911 | + struct max3420_udc *udc = ep->udc; | |
| 912 | + unsigned long flags; | |
| 913 | + | |
| 914 | + spin_lock_irqsave(&ep->lock, flags); | |
| 915 | + | |
| 916 | + ep->todo &= ~STALL_EP; | |
| 917 | + if (stall) | |
| 918 | + ep->todo |= STALL; | |
| 919 | + else | |
| 920 | + ep->todo |= UNSTALL; | |
| 921 | + | |
| 922 | + spin_unlock_irqrestore(&ep->lock, flags); | |
| 923 | + | |
| 924 | + wake_up_process(udc->thread_task); | |
| 925 | + | |
| 926 | + dev_dbg(udc->dev, "%sStall %s\n", stall ? "" : "Un", ep->name); | |
| 927 | + return 0; | |
| 928 | +} | |
| 929 | + | |
| 930 | +static int __max3420_ep_enable(struct max3420_ep *ep, | |
| 931 | + const struct usb_endpoint_descriptor *desc) | |
| 932 | +{ | |
| 933 | + unsigned int maxp = usb_endpoint_maxp(desc); | |
| 934 | + unsigned long flags; | |
| 935 | + | |
| 936 | + spin_lock_irqsave(&ep->lock, flags); | |
| 937 | + ep->ep_usb.desc = desc; | |
| 938 | + ep->ep_usb.maxpacket = maxp; | |
| 939 | + | |
| 940 | + ep->todo &= ~ENABLE_EP; | |
| 941 | + ep->todo |= ENABLE; | |
| 942 | + spin_unlock_irqrestore(&ep->lock, flags); | |
| 943 | + | |
| 944 | + return 0; | |
| 945 | +} | |
| 946 | + | |
| 947 | +static int max3420_ep_enable(struct usb_ep *_ep, | |
| 948 | + const struct usb_endpoint_descriptor *desc) | |
| 949 | +{ | |
| 950 | + struct max3420_ep *ep = to_max3420_ep(_ep); | |
| 951 | + struct max3420_udc *udc = ep->udc; | |
| 952 | + | |
| 953 | + __max3420_ep_enable(ep, desc); | |
| 954 | + | |
| 955 | + wake_up_process(udc->thread_task); | |
| 956 | + | |
| 957 | + return 0; | |
| 958 | +} | |
| 959 | + | |
| 960 | +static void max3420_nuke(struct max3420_ep *ep, int status) | |
| 961 | +{ | |
| 962 | + struct max3420_req *req, *r; | |
| 963 | + unsigned long flags; | |
| 964 | + | |
| 965 | + spin_lock_irqsave(&ep->lock, flags); | |
| 966 | + | |
| 967 | + list_for_each_entry_safe(req, r, &ep->queue, queue) { | |
| 968 | + list_del_init(&req->queue); | |
| 969 | + | |
| 970 | + spin_unlock_irqrestore(&ep->lock, flags); | |
| 971 | + max3420_req_done(req, status); | |
| 972 | + spin_lock_irqsave(&ep->lock, flags); | |
| 973 | + } | |
| 974 | + | |
| 975 | + spin_unlock_irqrestore(&ep->lock, flags); | |
| 976 | +} | |
| 977 | + | |
| 978 | +static void __max3420_ep_disable(struct max3420_ep *ep) | |
| 979 | +{ | |
| 980 | + struct max3420_udc *udc = ep->udc; | |
| 981 | + unsigned long flags; | |
| 982 | + | |
| 983 | + spin_lock_irqsave(&ep->lock, flags); | |
| 984 | + | |
| 985 | + ep->ep_usb.desc = NULL; | |
| 986 | + | |
| 987 | + ep->todo &= ~ENABLE_EP; | |
| 988 | + ep->todo |= DISABLE; | |
| 989 | + | |
| 990 | + spin_unlock_irqrestore(&ep->lock, flags); | |
| 991 | + | |
| 992 | + dev_dbg(udc->dev, "Disabled %s\n", ep->name); | |
| 993 | +} | |
| 994 | + | |
| 995 | +static int max3420_ep_disable(struct usb_ep *_ep) | |
| 996 | +{ | |
| 997 | + struct max3420_ep *ep = to_max3420_ep(_ep); | |
| 998 | + struct max3420_udc *udc = ep->udc; | |
| 999 | + | |
| 1000 | + max3420_nuke(ep, -ESHUTDOWN); | |
| 1001 | + | |
| 1002 | + __max3420_ep_disable(ep); | |
| 1003 | + | |
| 1004 | + wake_up_process(udc->thread_task); | |
| 1005 | + | |
| 1006 | + return 0; | |
| 1007 | +} | |
| 1008 | + | |
| 1009 | +static struct usb_request *max3420_alloc_request(struct usb_ep *_ep, | |
| 1010 | + gfp_t gfp_flags) | |
| 1011 | +{ | |
| 1012 | + struct max3420_ep *ep = to_max3420_ep(_ep); | |
| 1013 | + struct max3420_req *req; | |
| 1014 | + | |
| 1015 | + req = kzalloc(sizeof(*req), gfp_flags); | |
| 1016 | + if (!req) | |
| 1017 | + return NULL; | |
| 1018 | + | |
| 1019 | + req->ep = ep; | |
| 1020 | + | |
| 1021 | + return &req->usb_req; | |
| 1022 | +} | |
| 1023 | + | |
| 1024 | +static void max3420_free_request(struct usb_ep *_ep, struct usb_request *_req) | |
| 1025 | +{ | |
| 1026 | + kfree(to_max3420_req(_req)); | |
| 1027 | +} | |
| 1028 | + | |
| 1029 | +static int max3420_ep_queue(struct usb_ep *_ep, struct usb_request *_req, | |
| 1030 | + gfp_t ignored) | |
| 1031 | +{ | |
| 1032 | + struct max3420_req *req = to_max3420_req(_req); | |
| 1033 | + struct max3420_ep *ep = to_max3420_ep(_ep); | |
| 1034 | + struct max3420_udc *udc = ep->udc; | |
| 1035 | + unsigned long flags; | |
| 1036 | + | |
| 1037 | + _req->status = -EINPROGRESS; | |
| 1038 | + _req->actual = 0; | |
| 1039 | + | |
| 1040 | + spin_lock_irqsave(&ep->lock, flags); | |
| 1041 | + list_add_tail(&req->queue, &ep->queue); | |
| 1042 | + spin_unlock_irqrestore(&ep->lock, flags); | |
| 1043 | + | |
| 1044 | + wake_up_process(udc->thread_task); | |
| 1045 | + return 0; | |
| 1046 | +} | |
| 1047 | + | |
| 1048 | +static int max3420_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) | |
| 1049 | +{ | |
| 1050 | + struct max3420_req *t, *req = to_max3420_req(_req); | |
| 1051 | + struct max3420_ep *ep = to_max3420_ep(_ep); | |
| 1052 | + unsigned long flags; | |
| 1053 | + | |
| 1054 | + spin_lock_irqsave(&ep->lock, flags); | |
| 1055 | + | |
| 1056 | + /* Pluck the descriptor from queue */ | |
| 1057 | + list_for_each_entry(t, &ep->queue, queue) | |
| 1058 | + if (t == req) { | |
| 1059 | + list_del_init(&req->queue); | |
| 1060 | + break; | |
| 1061 | + } | |
| 1062 | + | |
| 1063 | + spin_unlock_irqrestore(&ep->lock, flags); | |
| 1064 | + | |
| 1065 | + if (t == req) | |
| 1066 | + max3420_req_done(req, -ECONNRESET); | |
| 1067 | + | |
| 1068 | + return 0; | |
| 1069 | +} | |
| 1070 | + | |
| 1071 | +static const struct usb_ep_ops max3420_ep_ops = { | |
| 1072 | + .enable = max3420_ep_enable, | |
| 1073 | + .disable = max3420_ep_disable, | |
| 1074 | + .alloc_request = max3420_alloc_request, | |
| 1075 | + .free_request = max3420_free_request, | |
| 1076 | + .queue = max3420_ep_queue, | |
| 1077 | + .dequeue = max3420_ep_dequeue, | |
| 1078 | + .set_halt = max3420_ep_set_halt, | |
| 1079 | +}; | |
| 1080 | + | |
| 1081 | +static int max3420_wakeup(struct usb_gadget *gadget) | |
| 1082 | +{ | |
| 1083 | + struct max3420_udc *udc = to_udc(gadget); | |
| 1084 | + unsigned long flags; | |
| 1085 | + int ret = -EINVAL; | |
| 1086 | + | |
| 1087 | + spin_lock_irqsave(&udc->lock, flags); | |
| 1088 | + | |
| 1089 | + /* Only if wakeup allowed by host */ | |
| 1090 | + if (udc->remote_wkp) { | |
| 1091 | + udc->todo |= REMOTE_WAKEUP; | |
| 1092 | + ret = 0; | |
| 1093 | + } | |
| 1094 | + | |
| 1095 | + spin_unlock_irqrestore(&udc->lock, flags); | |
| 1096 | + | |
| 1097 | + if (udc->thread_task && | |
| 1098 | + udc->thread_task->state != TASK_RUNNING) | |
| 1099 | + wake_up_process(udc->thread_task); | |
| 1100 | + return ret; | |
| 1101 | +} | |
| 1102 | + | |
| 1103 | +static int max3420_udc_start(struct usb_gadget *gadget, | |
| 1104 | + struct usb_gadget_driver *driver) | |
| 1105 | +{ | |
| 1106 | + struct max3420_udc *udc = to_udc(gadget); | |
| 1107 | + unsigned long flags; | |
| 1108 | + | |
| 1109 | + spin_lock_irqsave(&udc->lock, flags); | |
| 1110 | + /* hook up the driver */ | |
| 1111 | + driver->driver.bus = NULL; | |
| 1112 | + udc->driver = driver; | |
| 1113 | + udc->gadget.speed = USB_SPEED_FULL; | |
| 1114 | + | |
| 1115 | + udc->gadget.is_selfpowered = udc->is_selfpowered; | |
| 1116 | + udc->remote_wkp = 0; | |
| 1117 | + udc->softconnect = true; | |
| 1118 | + udc->todo |= UDC_START; | |
| 1119 | + spin_unlock_irqrestore(&udc->lock, flags); | |
| 1120 | + | |
| 1121 | + if (udc->thread_task && | |
| 1122 | + udc->thread_task->state != TASK_RUNNING) | |
| 1123 | + wake_up_process(udc->thread_task); | |
| 1124 | + | |
| 1125 | + return 0; | |
| 1126 | +} | |
| 1127 | + | |
| 1128 | +static int max3420_udc_stop(struct usb_gadget *gadget) | |
| 1129 | +{ | |
| 1130 | + struct max3420_udc *udc = to_udc(gadget); | |
| 1131 | + unsigned long flags; | |
| 1132 | + | |
| 1133 | + spin_lock_irqsave(&udc->lock, flags); | |
| 1134 | + udc->is_selfpowered = udc->gadget.is_selfpowered; | |
| 1135 | + udc->gadget.speed = USB_SPEED_UNKNOWN; | |
| 1136 | + udc->driver = NULL; | |
| 1137 | + udc->softconnect = false; | |
| 1138 | + udc->todo |= UDC_START; | |
| 1139 | + spin_unlock_irqrestore(&udc->lock, flags); | |
| 1140 | + | |
| 1141 | + if (udc->thread_task && | |
| 1142 | + udc->thread_task->state != TASK_RUNNING) | |
| 1143 | + wake_up_process(udc->thread_task); | |
| 1144 | + | |
| 1145 | + return 0; | |
| 1146 | +} | |
| 1147 | + | |
| 1148 | +static const struct usb_gadget_ops max3420_udc_ops = { | |
| 1149 | + .udc_start = max3420_udc_start, | |
| 1150 | + .udc_stop = max3420_udc_stop, | |
| 1151 | + .wakeup = max3420_wakeup, | |
| 1152 | +}; | |
| 1153 | + | |
| 1154 | +static void max3420_eps_init(struct max3420_udc *udc) | |
| 1155 | +{ | |
| 1156 | + int idx; | |
| 1157 | + | |
| 1158 | + INIT_LIST_HEAD(&udc->gadget.ep_list); | |
| 1159 | + | |
| 1160 | + for (idx = 0; idx < MAX3420_MAX_EPS; idx++) { | |
| 1161 | + struct max3420_ep *ep = &udc->ep[idx]; | |
| 1162 | + | |
| 1163 | + spin_lock_init(&ep->lock); | |
| 1164 | + INIT_LIST_HEAD(&ep->queue); | |
| 1165 | + | |
| 1166 | + ep->udc = udc; | |
| 1167 | + ep->id = idx; | |
| 1168 | + ep->halted = 0; | |
| 1169 | + ep->maxpacket = 0; | |
| 1170 | + ep->ep_usb.name = ep->name; | |
| 1171 | + ep->ep_usb.ops = &max3420_ep_ops; | |
| 1172 | + usb_ep_set_maxpacket_limit(&ep->ep_usb, MAX3420_EP_MAX_PACKET); | |
| 1173 | + | |
| 1174 | + if (idx == 0) { /* For EP0 */ | |
| 1175 | + ep->ep_usb.desc = &ep0_desc; | |
| 1176 | + ep->ep_usb.maxpacket = usb_endpoint_maxp(&ep0_desc); | |
| 1177 | + ep->ep_usb.caps.type_control = true; | |
| 1178 | + ep->ep_usb.caps.dir_in = true; | |
| 1179 | + ep->ep_usb.caps.dir_out = true; | |
| 1180 | + snprintf(ep->name, MAX3420_EPNAME_SIZE, "ep0"); | |
| 1181 | + continue; | |
| 1182 | + } | |
| 1183 | + | |
| 1184 | + if (idx == 1) { /* EP1 is OUT */ | |
| 1185 | + ep->ep_usb.caps.dir_in = false; | |
| 1186 | + ep->ep_usb.caps.dir_out = true; | |
| 1187 | + snprintf(ep->name, MAX3420_EPNAME_SIZE, "ep1-bulk-out"); | |
| 1188 | + } else { /* EP2 & EP3 are IN */ | |
| 1189 | + ep->ep_usb.caps.dir_in = true; | |
| 1190 | + ep->ep_usb.caps.dir_out = false; | |
| 1191 | + snprintf(ep->name, MAX3420_EPNAME_SIZE, | |
| 1192 | + "ep%d-bulk-in", idx); | |
| 1193 | + } | |
| 1194 | + ep->ep_usb.caps.type_iso = false; | |
| 1195 | + ep->ep_usb.caps.type_int = false; | |
| 1196 | + ep->ep_usb.caps.type_bulk = true; | |
| 1197 | + | |
| 1198 | + list_add_tail(&ep->ep_usb.ep_list, | |
| 1199 | + &udc->gadget.ep_list); | |
| 1200 | + } | |
| 1201 | +} | |
| 1202 | + | |
| 1203 | +static int max3420_probe(struct spi_device *spi) | |
| 1204 | +{ | |
| 1205 | + struct max3420_udc *udc; | |
| 1206 | + int err, irq; | |
| 1207 | + u8 reg[8]; | |
| 1208 | + | |
| 1209 | + if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) { | |
| 1210 | + dev_err(&spi->dev, "UDC needs full duplex to work\n"); | |
| 1211 | + return -EINVAL; | |
| 1212 | + } | |
| 1213 | + | |
| 1214 | + spi->mode = SPI_MODE_3; | |
| 1215 | + spi->bits_per_word = 8; | |
| 1216 | + | |
| 1217 | + err = spi_setup(spi); | |
| 1218 | + if (err) { | |
| 1219 | + dev_err(&spi->dev, "Unable to setup SPI bus\n"); | |
| 1220 | + return -EFAULT; | |
| 1221 | + } | |
| 1222 | + | |
| 1223 | + udc = devm_kzalloc(&spi->dev, sizeof(*udc), GFP_KERNEL); | |
| 1224 | + if (!udc) | |
| 1225 | + return -ENOMEM; | |
| 1226 | + | |
| 1227 | + udc->spi = spi; | |
| 1228 | + | |
| 1229 | + udc->remote_wkp = 0; | |
| 1230 | + | |
| 1231 | + /* Setup gadget structure */ | |
| 1232 | + udc->gadget.ops = &max3420_udc_ops; | |
| 1233 | + udc->gadget.max_speed = USB_SPEED_FULL; | |
| 1234 | + udc->gadget.speed = USB_SPEED_UNKNOWN; | |
| 1235 | + udc->gadget.ep0 = &udc->ep[0].ep_usb; | |
| 1236 | + udc->gadget.name = driver_name; | |
| 1237 | + | |
| 1238 | + spin_lock_init(&udc->lock); | |
| 1239 | + mutex_init(&udc->spi_bus_mutex); | |
| 1240 | + | |
| 1241 | + udc->ep0req.ep = &udc->ep[0]; | |
| 1242 | + udc->ep0req.usb_req.buf = udc->ep0buf; | |
| 1243 | + INIT_LIST_HEAD(&udc->ep0req.queue); | |
| 1244 | + | |
| 1245 | + /* setup Endpoints */ | |
| 1246 | + max3420_eps_init(udc); | |
| 1247 | + | |
| 1248 | + /* configure SPI */ | |
| 1249 | + spi_rd_buf(udc, MAX3420_REG_EPIRQ, reg, 8); | |
| 1250 | + spi_wr8(udc, MAX3420_REG_PINCTL, FDUPSPI); | |
| 1251 | + | |
| 1252 | + err = usb_add_gadget_udc(&spi->dev, &udc->gadget); | |
| 1253 | + if (err) | |
| 1254 | + return err; | |
| 1255 | + | |
| 1256 | + udc->dev = &udc->gadget.dev; | |
| 1257 | + | |
| 1258 | + spi_set_drvdata(spi, udc); | |
| 1259 | + | |
| 1260 | + irq = of_irq_get_byname(spi->dev.of_node, "udc"); | |
| 1261 | + err = devm_request_irq(&spi->dev, irq, max3420_irq_handler, 0, | |
| 1262 | + "max3420", udc); | |
| 1263 | + if (err < 0) | |
| 1264 | + return err; | |
| 1265 | + | |
| 1266 | + udc->thread_task = kthread_create(max3420_thread, udc, | |
| 1267 | + "max3420-thread"); | |
| 1268 | + if (IS_ERR(udc->thread_task)) | |
| 1269 | + return PTR_ERR(udc->thread_task); | |
| 1270 | + | |
| 1271 | + irq = of_irq_get_byname(spi->dev.of_node, "vbus"); | |
| 1272 | + if (irq <= 0) { /* no vbus irq implies self-powered design */ | |
| 1273 | + udc->is_selfpowered = 1; | |
| 1274 | + udc->vbus_active = true; | |
| 1275 | + udc->todo |= UDC_START; | |
| 1276 | + usb_udc_vbus_handler(&udc->gadget, udc->vbus_active); | |
| 1277 | + usb_gadget_set_state(&udc->gadget, USB_STATE_POWERED); | |
| 1278 | + max3420_start(udc); | |
| 1279 | + } else { | |
| 1280 | + udc->is_selfpowered = 0; | |
| 1281 | + /* Detect current vbus status */ | |
| 1282 | + spi_rd_buf(udc, MAX3420_REG_EPIRQ, reg, 8); | |
| 1283 | + if (reg[7] != 0xff) | |
| 1284 | + udc->vbus_active = true; | |
| 1285 | + | |
| 1286 | + err = devm_request_irq(&spi->dev, irq, | |
| 1287 | + max3420_vbus_handler, 0, "vbus", udc); | |
| 1288 | + if (err < 0) | |
| 1289 | + return err; | |
| 1290 | + } | |
| 1291 | + | |
| 1292 | + return 0; | |
| 1293 | +} | |
| 1294 | + | |
| 1295 | +static int max3420_remove(struct spi_device *spi) | |
| 1296 | +{ | |
| 1297 | + struct max3420_udc *udc = spi_get_drvdata(spi); | |
| 1298 | + unsigned long flags; | |
| 1299 | + | |
| 1300 | + usb_del_gadget_udc(&udc->gadget); | |
| 1301 | + | |
| 1302 | + spin_lock_irqsave(&udc->lock, flags); | |
| 1303 | + | |
| 1304 | + kthread_stop(udc->thread_task); | |
| 1305 | + | |
| 1306 | + spin_unlock_irqrestore(&udc->lock, flags); | |
| 1307 | + | |
| 1308 | + return 0; | |
| 1309 | +} | |
| 1310 | + | |
| 1311 | +static const struct of_device_id max3420_udc_of_match[] = { | |
| 1312 | + { .compatible = "maxim,max3420-udc"}, | |
| 1313 | + { .compatible = "maxim,max3421-udc"}, | |
| 1314 | + {}, | |
| 1315 | +}; | |
| 1316 | +MODULE_DEVICE_TABLE(of, max3420_udc_of_match); | |
| 1317 | + | |
| 1318 | +static struct spi_driver max3420_driver = { | |
| 1319 | + .driver = { | |
| 1320 | + .name = "max3420-udc", | |
| 1321 | + .of_match_table = of_match_ptr(max3420_udc_of_match), | |
| 1322 | + }, | |
| 1323 | + .probe = max3420_probe, | |
| 1324 | + .remove = max3420_remove, | |
| 1325 | +}; | |
| 1326 | + | |
| 1327 | +module_spi_driver(max3420_driver); | |
| 1328 | + | |
| 1329 | +MODULE_DESCRIPTION(DRIVER_DESC); | |
| 1330 | +MODULE_AUTHOR("Jassi Brar <jaswinder.singh@linaro.org>"); | |
| 1331 | +MODULE_LICENSE("GPL"); | |
| @@ -3493,11 +3493,8 @@ static int tegra_xudc_probe(struct platform_device *pdev) · 3492 unmodified lines | ||
| 3493 | 3493 | } |
| 3494 | 3494 | |
| 3495 | 3495 | xudc->irq = platform_get_irq(pdev, 0); |
| 3496 | − if (xudc->irq < 0) { | |
| 3497 | − dev_err(xudc->dev, "failed to get IRQ: %d\n", | |
| 3498 | − xudc->irq); | |
| 3496 | + if (xudc->irq < 0) | |
| 3499 | 3497 | return xudc->irq; |
| 3500 | − } | |
| 3501 | 3498 | |
| 3502 | 3499 | err = devm_request_irq(&pdev->dev, xudc->irq, tegra_xudc_irq, 0, |
| 3503 | 3500 | dev_name(&pdev->dev), xudc); |
| @@ -0,0 +1,167 @@ | ||
| 1 | +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ | |
| 2 | +/* | |
| 3 | + * USB Raw Gadget driver. | |
| 4 | + * | |
| 5 | + * See Documentation/usb/raw-gadget.rst for more details. | |
| 6 | + */ | |
| 7 | + | |
| 8 | +#ifndef _UAPI__LINUX_USB_RAW_GADGET_H | |
| 9 | +#define _UAPI__LINUX_USB_RAW_GADGET_H | |
| 10 | + | |
| 11 | +#include <asm/ioctl.h> | |
| 12 | +#include <linux/types.h> | |
| 13 | +#include <linux/usb/ch9.h> | |
| 14 | + | |
| 15 | +/* Maximum length of driver_name/device_name in the usb_raw_init struct. */ | |
| 16 | +#define UDC_NAME_LENGTH_MAX 128 | |
| 17 | + | |
| 18 | +/* | |
| 19 | + * struct usb_raw_init - argument for USB_RAW_IOCTL_INIT ioctl. | |
| 20 | + * @speed: The speed of the emulated USB device, takes the same values as | |
| 21 | + * the usb_device_speed enum: USB_SPEED_FULL, USB_SPEED_HIGH, etc. | |
| 22 | + * @driver_name: The name of the UDC driver. | |
| 23 | + * @device_name: The name of a UDC instance. | |
| 24 | + * | |
| 25 | + * The last two fields identify a UDC the gadget driver should bind to. | |
| 26 | + * For example, Dummy UDC has "dummy_udc" as its driver_name and "dummy_udc.N" | |
| 27 | + * as its device_name, where N in the index of the Dummy UDC instance. | |
| 28 | + * At the same time the dwc2 driver that is used on Raspberry Pi Zero, has | |
| 29 | + * "20980000.usb" as both driver_name and device_name. | |
| 30 | + */ | |
| 31 | +struct usb_raw_init { | |
| 32 | + __u8 driver_name[UDC_NAME_LENGTH_MAX]; | |
| 33 | + __u8 device_name[UDC_NAME_LENGTH_MAX]; | |
| 34 | + __u8 speed; | |
| 35 | +}; | |
| 36 | + | |
| 37 | +/* The type of event fetched with the USB_RAW_IOCTL_EVENT_FETCH ioctl. */ | |
| 38 | +enum usb_raw_event_type { | |
| 39 | + USB_RAW_EVENT_INVALID = 0, | |
| 40 | + | |
| 41 | + /* This event is queued when the driver has bound to a UDC. */ | |
| 42 | + USB_RAW_EVENT_CONNECT = 1, | |
| 43 | + | |
| 44 | + /* This event is queued when a new control request arrived to ep0. */ | |
| 45 | + USB_RAW_EVENT_CONTROL = 2, | |
| 46 | + | |
| 47 | + /* The list might grow in the future. */ | |
| 48 | +}; | |
| 49 | + | |
| 50 | +/* | |
| 51 | + * struct usb_raw_event - argument for USB_RAW_IOCTL_EVENT_FETCH ioctl. | |
| 52 | + * @type: The type of the fetched event. | |
| 53 | + * @length: Length of the data buffer. Updated by the driver and set to the | |
| 54 | + * actual length of the fetched event data. | |
| 55 | + * @data: A buffer to store the fetched event data. | |
| 56 | + * | |
| 57 | + * Currently the fetched data buffer is empty for USB_RAW_EVENT_CONNECT, | |
| 58 | + * and contains struct usb_ctrlrequest for USB_RAW_EVENT_CONTROL. | |
| 59 | + */ | |
| 60 | +struct usb_raw_event { | |
| 61 | + __u32 type; | |
| 62 | + __u32 length; | |
| 63 | + __u8 data[0]; | |
| 64 | +}; | |
| 65 | + | |
| 66 | +#define USB_RAW_IO_FLAGS_ZERO 0x0001 | |
| 67 | +#define USB_RAW_IO_FLAGS_MASK 0x0001 | |
| 68 | + | |
| 69 | +static int usb_raw_io_flags_valid(__u16 flags) | |
| 70 | +{ | |
| 71 | + return (flags & ~USB_RAW_IO_FLAGS_MASK) == 0; | |
| 72 | +} | |
| 73 | + | |
| 74 | +static int usb_raw_io_flags_zero(__u16 flags) | |
| 75 | +{ | |
| 76 | + return (flags & USB_RAW_IO_FLAGS_ZERO); | |
| 77 | +} | |
| 78 | + | |
| 79 | +/* | |
| 80 | + * struct usb_raw_ep_io - argument for USB_RAW_IOCTL_EP0/EP_WRITE/READ ioctls. | |
| 81 | + * @ep: Endpoint handle as returned by USB_RAW_IOCTL_EP_ENABLE for | |
| 82 | + * USB_RAW_IOCTL_EP_WRITE/READ. Ignored for USB_RAW_IOCTL_EP0_WRITE/READ. | |
| 83 | + * @flags: When USB_RAW_IO_FLAGS_ZERO is specified, the zero flag is set on | |
| 84 | + * the submitted USB request, see include/linux/usb/gadget.h for details. | |
| 85 | + * @length: Length of data. | |
| 86 | + * @data: Data to send for USB_RAW_IOCTL_EP0/EP_WRITE. Buffer to store received | |
| 87 | + * data for USB_RAW_IOCTL_EP0/EP_READ. | |
| 88 | + */ | |
| 89 | +struct usb_raw_ep_io { | |
| 90 | + __u16 ep; | |
| 91 | + __u16 flags; | |
| 92 | + __u32 length; | |
| 93 | + __u8 data[0]; | |
| 94 | +}; | |
| 95 | + | |
| 96 | +/* | |
| 97 | + * Initializes a Raw Gadget instance. | |
| 98 | + * Accepts a pointer to the usb_raw_init struct as an argument. | |
| 99 | + * Returns 0 on success or negative error code on failure. | |
| 100 | + */ | |
| 101 | +#define USB_RAW_IOCTL_INIT _IOW('U', 0, struct usb_raw_init) | |
| 102 | + | |
| 103 | +/* | |
| 104 | + * Instructs Raw Gadget to bind to a UDC and start emulating a USB device. | |
| 105 | + * Returns 0 on success or negative error code on failure. | |
| 106 | + */ | |
| 107 | +#define USB_RAW_IOCTL_RUN _IO('U', 1) | |
| 108 | + | |
| 109 | +/* | |
| 110 | + * A blocking ioctl that waits for an event and returns fetched event data to | |
| 111 | + * the user. | |
| 112 | + * Accepts a pointer to the usb_raw_event struct. | |
| 113 | + * Returns 0 on success or negative error code on failure. | |
| 114 | + */ | |
| 115 | +#define USB_RAW_IOCTL_EVENT_FETCH _IOR('U', 2, struct usb_raw_event) | |
| 116 | + | |
| 117 | +/* | |
| 118 | + * Queues an IN (OUT for READ) urb as a response to the last control request | |
| 119 | + * received on endpoint 0, provided that was an IN (OUT for READ) request and | |
| 120 | + * waits until the urb is completed. Copies received data to user for READ. | |
| 121 | + * Accepts a pointer to the usb_raw_ep_io struct as an argument. | |
| 122 | + * Returns length of trasferred data on success or negative error code on | |
| 123 | + * failure. | |
| 124 | + */ | |
| 125 | +#define USB_RAW_IOCTL_EP0_WRITE _IOW('U', 3, struct usb_raw_ep_io) | |
| 126 | +#define USB_RAW_IOCTL_EP0_READ _IOWR('U', 4, struct usb_raw_ep_io) | |
| 127 | + | |
| 128 | +/* | |
| 129 | + * Finds an endpoint that supports the transfer type specified in the | |
| 130 | + * descriptor and enables it. | |
| 131 | + * Accepts a pointer to the usb_endpoint_descriptor struct as an argument. | |
| 132 | + * Returns enabled endpoint handle on success or negative error code on failure. | |
| 133 | + */ | |
| 134 | +#define USB_RAW_IOCTL_EP_ENABLE _IOW('U', 5, struct usb_endpoint_descriptor) | |
| 135 | + | |
| 136 | +/* Disables specified endpoint. | |
| 137 | + * Accepts endpoint handle as an argument. | |
| 138 | + * Returns 0 on success or negative error code on failure. | |
| 139 | + */ | |
| 140 | +#define USB_RAW_IOCTL_EP_DISABLE _IOW('U', 6, __u32) | |
| 141 | + | |
| 142 | +/* | |
| 143 | + * Queues an IN (OUT for READ) urb as a response to the last control request | |
| 144 | + * received on endpoint usb_raw_ep_io.ep, provided that was an IN (OUT for READ) | |
| 145 | + * request and waits until the urb is completed. Copies received data to user | |
| 146 | + * for READ. | |
| 147 | + * Accepts a pointer to the usb_raw_ep_io struct as an argument. | |
| 148 | + * Returns length of trasferred data on success or negative error code on | |
| 149 | + * failure. | |
| 150 | + */ | |
| 151 | +#define USB_RAW_IOCTL_EP_WRITE _IOW('U', 7, struct usb_raw_ep_io) | |
| 152 | +#define USB_RAW_IOCTL_EP_READ _IOWR('U', 8, struct usb_raw_ep_io) | |
| 153 | + | |
| 154 | +/* | |
| 155 | + * Switches the gadget into the configured state. | |
| 156 | + * Returns 0 on success or negative error code on failure. | |
| 157 | + */ | |
| 158 | +#define USB_RAW_IOCTL_CONFIGURE _IO('U', 9) | |
| 159 | + | |
| 160 | +/* | |
| 161 | + * Constrains UDC VBUS power usage. | |
| 162 | + * Accepts current limit in 2 mA units as an argument. | |
| 163 | + * Returns 0 on success or negative error code on failure. | |
| 164 | + */ | |
| 165 | +#define USB_RAW_IOCTL_VBUS_DRAW _IOW('U', 10, __u32) | |
| 166 | + | |
| 167 | +#endif /* _UAPI__LINUX_USB_RAW_GADGET_H */ | |