diff options
author | Kees Cook <keescook@google.com> | 2016-03-08 09:56:09 -0800 |
---|---|---|
committer | Kees Cook <keescook@google.com> | 2016-03-08 09:56:09 -0800 |
commit | 2ad1a30bbdffd6f53c33bb8562a8eef7f4732310 (patch) | |
tree | 19a0625964fd70e51ac8ee4c1041a65b6c24d3be | |
parent | d12b59f861a0eb9b4313ea5a8993989cf6e80f2a (diff) | |
parent | babe638e91d0f2804b3d7357e4aa575f8fa2248b (diff) | |
download | v4.1-2ad1a30bbdffd6f53c33bb8562a8eef7f4732310.tar.gz |
Merge branch 'master' into update
149 files changed, 7215 insertions, 3189 deletions
diff --git a/Documentation/devicetree/bindings/misc/uboot-bootcount.txt b/Documentation/devicetree/bindings/misc/uboot-bootcount.txt new file mode 100644 index 00000000000..7ab1134c3f3 --- /dev/null +++ b/Documentation/devicetree/bindings/misc/uboot-bootcount.txt @@ -0,0 +1,34 @@ +U-Boot bootcount driver + +This driver implements the Linux kernel half of the boot count feature - +the boot counter can only be reset after it is clear that the +application has been started and is running correctly, which usually +can only be determined by the application code itself. Thus the reset +of the boot counter must be done by application code, which thus needs +an appropriate driver. + +Required feature by the Carrier Grade Linux Requirements Definition; +see for example document "Carrier Grade Linux Requirements Definition +Overview V3.0" at + +http://www.linuxfoundation.org/collaborate/workgroups/cgl/requirements#SMM.6.0_Boot_Cycle_Detection + + Description: OSDL CGL specifies that carrier grade Linux + shall provide support for detecting a repeating reboot cycle + due to recurring failures. This detection should happen in + user space before system services are started. + +This driver provides read/write access to the U-Boot bootcounter +through sysfs file. + +Required properties: + + - compatible : should be "uboot,bootcount" + - reg: the address of the bootcounter + +Example: + +bootcount@1c23000 { + compatible = "uboot,bootcount"; + reg = <0x23060 0x1>; +}; diff --git a/Documentation/devicetree/bindings/net/ieee802154/cc2520.txt b/Documentation/devicetree/bindings/net/ieee802154/cc2520.txt index 3e1356b6fca..338a01571f6 100644 --- a/Documentation/devicetree/bindings/net/ieee802154/cc2520.txt +++ b/Documentation/devicetree/bindings/net/ieee802154/cc2520.txt @@ -15,11 +15,12 @@ Required properties: - reset-gpio: GPIO spec for the RESET pin Optional properties: - amplified: include if the CC2520 is connected to a CC2591 amplifier + - #clock-cells from common clock binding; shall be set to 0 + required if extclock-freq is specified - extclock-freq: frequency setting of external clock generator, should be between 1000000-16000000 (check datasheet for supported values) extclock is disabled if extclock-freq = <0>, if not specified defaults to 1MHz (reset value) - Example: cc2520@0 { compatible = "ti,cc2520"; @@ -34,5 +35,6 @@ Example: cca-gpio = <&gpio1 16 0>; vreg-gpio = <&gpio0 31 0>; reset-gpio = <&gpio1 12 0>; + #clock-cells = <0>; extclock-freq = <16000000>; }; diff --git a/Documentation/devicetree/bindings/power/twl-charger.txt b/Documentation/devicetree/bindings/power/twl-charger.txt index d5c706216df..3b4ea1b73b3 100644 --- a/Documentation/devicetree/bindings/power/twl-charger.txt +++ b/Documentation/devicetree/bindings/power/twl-charger.txt @@ -1,5 +1,15 @@ TWL BCI (Battery Charger Interface) +The battery charger needs to interact with the USB phy in order +to know when charging is permissible, and when there is a connection +or disconnection. + +The choice of phy cannot be configured at a hardware level, so there +is no value in explicit configuration in device-tree. Rather +if there is a sibling of the BCI node which is compatible with +"ti,twl4030-usb", then that is used to determine when and how +use USB power for charging. + Required properties: - compatible: - "ti,twl4030-bci" diff --git a/Documentation/devicetree/bindings/usb/dwc3-st.txt b/Documentation/devicetree/bindings/usb/dwc3-st.txt index f9d70252bbb..01c71b1258f 100644 --- a/Documentation/devicetree/bindings/usb/dwc3-st.txt +++ b/Documentation/devicetree/bindings/usb/dwc3-st.txt @@ -49,8 +49,7 @@ st_dwc3: dwc3@8f94000 { st,syscfg = <&syscfg_core>; resets = <&powerdown STIH407_USB3_POWERDOWN>, <&softreset STIH407_MIPHY2_SOFTRESET>; - reset-names = "powerdown", - "softreset"; + reset-names = "powerdown", "softreset"; #address-cells = <1>; #size-cells = <1>; pinctrl-names = "default"; @@ -62,7 +61,7 @@ st_dwc3: dwc3@8f94000 { reg = <0x09900000 0x100000>; interrupts = <GIC_SPI 155 IRQ_TYPE_NONE>; dr_mode = "host"; - phys-names = "usb2-phy", "usb3-phy"; - phys = <&usb2_picophy2>, <&phy_port2 MIPHY_TYPE_USB>; + phy-names = "usb2-phy", "usb3-phy"; + phys = <&usb2_picophy2>, <&phy_port2 PHY_TYPE_USB3>; }; }; diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt index 5cc364309ed..0815eac5b18 100644 --- a/Documentation/devicetree/bindings/usb/dwc3.txt +++ b/Documentation/devicetree/bindings/usb/dwc3.txt @@ -38,6 +38,8 @@ Optional properties: - snps,is-utmi-l1-suspend: true when DWC3 asserts output signal utmi_l1_suspend_n, false when asserts utmi_sleep_n - snps,hird-threshold: HIRD threshold + - snps,hsphy_interface: High-Speed PHY interface selection between "utmi" for + UTMI+ and "ulpi" for ULPI when the DWC_USB3_HSPHY_INTERFACE has value 3. This is usually a subnode to DWC3 glue to which it is connected. diff --git a/Documentation/devicetree/bindings/usb/msm-hsusb.txt b/Documentation/devicetree/bindings/usb/msm-hsusb.txt index 2826f2af503..bd8d9e75302 100644 --- a/Documentation/devicetree/bindings/usb/msm-hsusb.txt +++ b/Documentation/devicetree/bindings/usb/msm-hsusb.txt @@ -69,6 +69,17 @@ Optional properties: (no, min, max) where each value represents either a voltage in microvolts or a value corresponding to voltage corner. +- qcom,manual-pullup: If present, vbus is not routed to USB controller/phy + and controller driver therefore enables pull-up explicitly + before starting controller using usbcmd run/stop bit. + +- extcon: phandles to external connector devices. First phandle + should point to external connector, which provide "USB" + cable events, the second should point to external connector + device, which provide "USB-HOST" cable events. If one of + the external connector devices is not required empty <0> + phandle should be specified. + Example HSUSB OTG controller device node: usb@f9a55000 { diff --git a/Documentation/devicetree/bindings/usb/renesas_usbhs.txt b/Documentation/devicetree/bindings/usb/renesas_usbhs.txt index ddbe304beb2..64a4ca6cf96 100644 --- a/Documentation/devicetree/bindings/usb/renesas_usbhs.txt +++ b/Documentation/devicetree/bindings/usb/renesas_usbhs.txt @@ -4,6 +4,7 @@ Required properties: - compatible: Must contain one of the following: - "renesas,usbhs-r8a7790" - "renesas,usbhs-r8a7791" + - "renesas,usbhs-r8a7794" - reg: Base address and length of the register for the USBHS - interrupts: Interrupt specifier for the USBHS - clocks: A list of phandle + clock specifier pairs diff --git a/Documentation/devicetree/bindings/usb/twlxxxx-usb.txt b/Documentation/devicetree/bindings/usb/twlxxxx-usb.txt index 0aee0ad3f03..17327a29611 100644 --- a/Documentation/devicetree/bindings/usb/twlxxxx-usb.txt +++ b/Documentation/devicetree/bindings/usb/twlxxxx-usb.txt @@ -30,6 +30,9 @@ TWL4030 USB PHY AND COMPARATOR - usb_mode : The mode used by the phy to connect to the controller. "1" specifies "ULPI" mode and "2" specifies "CEA2011_3PIN" mode. +If a sibling node is compatible "ti,twl4030-bci", then it will find +this device and query it for USB power status. + twl4030-usb { compatible = "ti,twl4030-usb"; interrupts = < 10 4 >; diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index f45b2bf4b41..592678009c1 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -526,8 +526,6 @@ Except for ifname they can be written to until the function is linked to a configuration. The ifname is read-only and contains the name of the interface which was assigned by the net core, e. g. usb0. -By default there can be only 1 RNDIS interface in the system. - Testing the RNDIS function -------------------------- @@ -629,7 +627,7 @@ Function-specific configfs interface The function name to use when creating the function directory is "uac2". The uac2 function provides these attributes in its function directory: - chmask - capture channel mask + c_chmask - capture channel mask c_srate - capture sampling rate c_ssize - capture sample size (bytes) p_chmask - playback channel mask diff --git a/MAINTAINERS b/MAINTAINERS index d8afd295367..af08d2ca4a2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10493,6 +10493,13 @@ S: Maintained F: Documentation/video4linux/zr364xx.txt F: drivers/media/usb/zr364xx/ +ULPI BUS +M: Heikki Krogerus <heikki.krogerus@linux.intel.com> +L: linux-usb@vger.kernel.org +S: Maintained +F: drivers/usb/common/ulpi.c +F: include/linux/ulpi/ + USER-MODE LINUX (UML) M: Jeff Dike <jdike@addtoit.com> M: Richard Weinberger <richard@nod.at> diff --git a/arch/mips/boot/dts/pistachio/Makefile b/arch/mips/boot/dts/pistachio/Makefile index c3f4b766351..3a18b2cde42 100644 --- a/arch/mips/boot/dts/pistachio/Makefile +++ b/arch/mips/boot/dts/pistachio/Makefile @@ -1,4 +1,4 @@ -dtb-$(CONFIG_MACH_PISTACHIO) += pistachio_bub.dtb pistachio_fpga.dtb pistachio_marduk.dtb pistachio_concerto_mbub.dtb pistachio_beetle_mbub.dtb +dtb-$(CONFIG_MACH_PISTACHIO) += pistachio_bub.dtb pistachio_fpga.dtb pistachio_marduk.dtb obj-y += $(patsubst %.dtb, %.dtb.o, $(dtb-y)) diff --git a/arch/mips/boot/dts/pistachio/pistachio.dtsi b/arch/mips/boot/dts/pistachio/pistachio.dtsi index ffaddc3ad92..96f06efb904 100644 --- a/arch/mips/boot/dts/pistachio/pistachio.dtsi +++ b/arch/mips/boot/dts/pistachio/pistachio.dtsi @@ -717,7 +717,7 @@ slew-rate = <1>; drive-strength = <4>; }; - enet-phy-clk { + pin_enet_phy_clk: enet-phy-clk { pins = "mfio71"; function = "eth"; slew-rate = <1>; @@ -797,14 +797,14 @@ }; dac_clk_pin: dac-clk-pin { - dac-clk { + pin_dac_clk: dac-clk { pins = "mfio45"; function = "i2s_dac_clk"; }; }; i2s_mclk_pin: i2s-mclk-pin { - i2s-mclk { + pin_i2s_mclk: i2s-mclk { pins = "mfio36"; function = "i2s_out"; }; @@ -826,7 +826,7 @@ }; i2s_out_pins: i2s-out-pins { - i2s-out { + pins_i2s_out: i2s-out { pins = "mfio37", "mfio38", "mfio39", "mfio40", "mfio41", "mfio42", "mfio43", "mfio44"; @@ -878,7 +878,7 @@ wdt: watchdog@18102100 { compatible = "img,pdc-wdt"; - reg = <0x18102100 0x100>; + reg = <0x18102100 0x20>; interrupts = <GIC_SHARED 52 IRQ_TYPE_LEVEL_HIGH>; clocks = <&clk_periph PERIPH_CLK_WD>, <&cr_periph SYS_CLK_WD>; clock-names = "wdt", "sys"; @@ -938,7 +938,7 @@ pinctrl-names = "default"; fifo-depth = <0x20>; num-slots = <1>; - clock-frequency = <200000000>; + clock-frequency = <50000000>; bus-width = <8>; cap-mmc-highspeed; cap-sd-highspeed; diff --git a/arch/mips/boot/dts/pistachio/pistachio_beetle.dtsi b/arch/mips/boot/dts/pistachio/pistachio_beetle.dtsi index 6b7d4b9c0bc..4cff3fd40ca 100644 --- a/arch/mips/boot/dts/pistachio/pistachio_beetle.dtsi +++ b/arch/mips/boot/dts/pistachio/pistachio_beetle.dtsi @@ -43,26 +43,39 @@ reg = <1>; spi-max-frequency = <50000000>; nand-on-flash-bbt; - spi-rx-bus-width = <4>; + spi-rx-bus-width = <2>; #address-cells = <1>; #size-cells = <1>; linux,mtd-name = "spi-nand"; }; }; -&uart0 { - pinctrl-0 = <&uart0_pins>, <&uart0_rts_cts_pins>; - pinctrl-names = "default"; +&uccpsystem { + status = "okay"; +}; + +&uccphostport { + status = "okay"; +}; + +&uccpbthp { + status = "okay"; +}; +&uccpdummyhp { status = "okay"; }; -&uart1 { +&uart0 { status = "okay"; + assigned-clock-rates = <114278400>, <1843200>; }; -&usb { +&sdhost { status = "okay"; + + bus-width = <4>; + disable-wp; }; &i2c3 { @@ -85,7 +98,7 @@ status = "okay"; mac-address0 = [0123456789AC]; mac-address1 = [0123456789AD]; - rf-params = [1E00000000002426292A2C2E3237393F454A52576066000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F00000000002426292A2C2E3237393F454A52576066000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F0808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808]; + rf-params = [101F24292E33383C4045484D5157595D6165686C7076262D3135393C41454A4D505356595B5D5F6164676A23282C3135393D4145494D53565A5D616466686B6F24282C3134373C4144484D52555A5E62666A6F747923282C31363A3E4245494E51575B5F63666B71767A1A1F24292E33383C4045484D5055585B5E62666A7223272C3034383C41454A4D5156595C6064686D7277252A2E32373B3F43474B4F55585B5D606467696C7224282C3135393D4044494E52565A5D6165696D7274252A2F33383C4043474C5054595D6165696D73787C1212121212121212121212121210101010101010101010101010121212121212110F0E0A0A1010101010100F0D0C08080F0F0F0F0F0F0E0C0B0707121212121212110F0E0A0A1010101010100F0D0C08080F0F0F0F0F0F0E0C0B0707121212121212110F0E0A0A1010101010100F0D0C08080F0F0F0F0F0F0E0C0B0707121212121212110F0E0A0A1010101010100F0D0C08080F0F0F0F0F0F0E0C0B0707]; num_streams = [02]; io-channels = <&adc 4>; }; diff --git a/arch/mips/boot/dts/pistachio/pistachio_beetle_boardtest.dts b/arch/mips/boot/dts/pistachio/pistachio_beetle_boardtest.dts index bd9bbd30da1..fea3fb8d763 100644 --- a/arch/mips/boot/dts/pistachio/pistachio_beetle_boardtest.dts +++ b/arch/mips/boot/dts/pistachio/pistachio_beetle_boardtest.dts @@ -7,102 +7,41 @@ * published by the Free Software Foundation. */ -/dts-v1/; +#include "pistachio_beetle_mbub.dts" -#include "pistachio_beetle.dtsi" -#include <dt-bindings/sound/pistachio-audio.h> - -/ { - model = "IMG Pistachio Concerto mBuB with a Beetle module"; - compatible = "img,pistachio-bub", "img,pistachio"; - - aliases { - serial0 = &uart0; - serial1 = &uart1; - }; - - chosen { - bootargs = "console=ttyS1,115200n8 earlycon=uart8250,mmio32,0x18101500,115200 root=/dev/sda1 rootwait ro"; - }; - - reg_1v8: fixed-regulator { - compatible = "regulator-fixed"; - regulator-name = "aux_adc_vref"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-boot-on; - }; - - supply5v0: supply5v0@0 { - compatible = "regulator-fixed"; - regulator-name = "5V-supply"; - regulator-min-microvolt = <5000000>; - regulator-max-microvolt = <5000000>; - }; - - pistachio_audio_card { - compatible = "img,pistachio-audio"; - - clocks = <&clk_core CLK_AUDIO_PLL>, - <&clk_core CLK_I2S>, - <&clk_core CLK_AUDIO>; - clock-names = "audio_pll", "i2s_mclk", "dac_clk"; - - img,cr-periph = <&cr_periph>; - img,event-timer = <&event_timer>; - - parallel-out { - cpu-dai = <¶llel_out>; - sound-dai = <&internal_dac>; - }; - - }; - -}; - -&uccpsystem { - status = "okay"; +&ir { + status = "disabled"; }; -&uccphostport { - status = "okay"; +&pwm { + status = "disabled"; }; -&uccpbthp { - status = "okay"; +&i2c0 { + status = "disabled"; }; -&uccpdummyhp { - status = "okay"; +&i2c1 { + status = "disabled"; }; -&uart0 { - status = "okay"; - assigned-clock-rates = <114278400>, <1843200>; +&i2c2 { + status = "disabled"; }; -&uart1 { - status = "okay"; +&i2s_out { + status = "disabled"; }; -&usb { - status = "okay"; +&i2s_in { + status = "disabled"; }; -&enet { - status = "okay"; - - mac-address = [0123456789AB]; -}; - -&sdhost { - status = "okay"; - - bus-width = <4>; - disable-wp; +&spdif_out { + status = "disabled"; }; -¶llel_out { - status = "okay"; +&spdif_in { + status = "disabled"; }; diff --git a/arch/mips/boot/dts/pistachio/pistachio_beetle_mbub.dts b/arch/mips/boot/dts/pistachio/pistachio_beetle_mbub.dts index 49be49ca189..ad152b5900a 100644 --- a/arch/mips/boot/dts/pistachio/pistachio_beetle_mbub.dts +++ b/arch/mips/boot/dts/pistachio/pistachio_beetle_mbub.dts @@ -11,9 +11,10 @@ #include "pistachio_beetle.dtsi" #include <dt-bindings/sound/pistachio-audio.h> +#include "pistachio_board_uboot.dtsi" / { - model = "IMG Pistachio Concerto mBuB with a Beetle module"; + model = "Beetle Bring-Up Board"; compatible = "img,pistachio-bub", "img,pistachio"; aliases { @@ -95,25 +96,16 @@ }; }; -&uccpsystem { - status = "okay"; +&pin_i2s_mclk { + drive-strength = <2>; }; -&uccphostport { - status = "okay"; +&pin_dac_clk { + drive-strength = <2>; }; -&uccpbthp { - status = "okay"; -}; - -&uccpdummyhp { - status = "okay"; -}; - -&uart0 { - status = "okay"; - assigned-clock-rates = <114278400>, <1843200>; +&pins_i2s_out { + drive-strength = <2>; }; &uart1 { @@ -136,13 +128,6 @@ mac-address = [0123456789AB]; }; -&sdhost { - status = "okay"; - - bus-width = <4>; - disable-wp; -}; - &ir { status = "okay"; }; diff --git a/arch/mips/boot/dts/pistachio/pistachio_board_uboot.dtsi b/arch/mips/boot/dts/pistachio/pistachio_board_uboot.dtsi new file mode 100644 index 00000000000..2fde5435d42 --- /dev/null +++ b/arch/mips/boot/dts/pistachio/pistachio_board_uboot.dtsi @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2015 Imagination Technologies Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + + +/ { + uboot-count { + compatible = "uboot,bootcount"; + reg = <0x18102120 0x4>; + }; +}; diff --git a/arch/mips/boot/dts/pistachio/pistachio_bub.dts b/arch/mips/boot/dts/pistachio/pistachio_bub.dts index 0750ca8c21a..eb88d6a0a29 100644 --- a/arch/mips/boot/dts/pistachio/pistachio_bub.dts +++ b/arch/mips/boot/dts/pistachio/pistachio_bub.dts @@ -10,6 +10,7 @@ /dts-v1/; #include "pistachio_bub_audio_no_daughterboard_example.dtsi" +#include "pistachio_board_uboot.dtsi" / { model = "IMG Pistachio BuB"; @@ -58,7 +59,7 @@ reg = <1>; spi-max-frequency = <50000000>; nand-on-flash-bbt; - spi-rx-bus-width = <4>; + spi-rx-bus-width = <2>; #address-cells = <1>; #size-cells = <1>; linux,mtd-name = "spi-nand"; diff --git a/arch/mips/boot/dts/pistachio/pistachio_concerto.dtsi b/arch/mips/boot/dts/pistachio/pistachio_concerto.dtsi index 5e35f4d8ef5..bdf7c627318 100644 --- a/arch/mips/boot/dts/pistachio/pistachio_concerto.dtsi +++ b/arch/mips/boot/dts/pistachio/pistachio_concerto.dtsi @@ -31,9 +31,10 @@ cs-gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>, <&gpio0 1 GPIO_ACTIVE_HIGH>; flash@0 { - compatible = "spidev"; + compatible = "jedec,spi-nor"; reg = <0>; spi-max-frequency = <50000000>; + linux,mtd-name = "spi-nor"; }; flash@1 { @@ -41,42 +42,53 @@ reg = <1>; spi-max-frequency = <50000000>; nand-on-flash-bbt; - spi-rx-bus-width = <4>; + spi-rx-bus-width = <2>; #address-cells = <1>; #size-cells = <1>; + linux,mtd-name = "spi-nand"; }; }; -&uart0 { - pinctrl-0 = <&uart0_pins>, <&uart0_rts_cts_pins>; - pinctrl-names = "default"; +&uccpsystem { + status = "okay"; +}; +&uccphostport { status = "okay"; }; -&uart1 { +&uccpbthp { status = "okay"; }; -&usb { +&uccpdummyhp { status = "okay"; }; +&uart0 { + status = "okay"; + assigned-clock-rates = <114278400>, <1843200>; +}; + &i2c3 { status = "okay"; clock-frequency = <400000>; }; -//&i2s_out { -// status = "okay"; -//}; - -//&i2s_in { -// status = "okay"; -//}; +&sdhost { + status = "okay"; +}; &adc { status = "okay"; vref-supply = <&adc_1v8>; }; + +&wifi { + status = "okay"; + mac-address0 = [0123456789AC]; + mac-address1 = [0123456789AD]; + rf-params = [1E00000000002426292A2C2E3237393F454A52576066000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F00000000002426292A2C2E3237393F454A52576066000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F0808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808]; + num_streams = [02]; +}; diff --git a/arch/mips/boot/dts/pistachio/pistachio_concerto_mbub.dts b/arch/mips/boot/dts/pistachio/pistachio_concerto_mbub.dts index b0d8689a6cc..afddda8bc2d 100644 --- a/arch/mips/boot/dts/pistachio/pistachio_concerto_mbub.dts +++ b/arch/mips/boot/dts/pistachio/pistachio_concerto_mbub.dts @@ -9,11 +9,12 @@ /dts-v1/; -#include "pistachio.dtsi" +#include "pistachio_concerto.dtsi" #include <dt-bindings/sound/pistachio-audio.h> +#include "pistachio_board_uboot.dtsi" / { - model = "IMG Pistachio Concerto mBuB"; + model = "Concerto Bring-Up-Board"; compatible = "img,pistachio-bub", "img,pistachio"; aliases { @@ -101,53 +102,6 @@ }; }; -&spfi1 { - status = "okay"; - - pinctrl-0 = <&spim1_pins>, <&spim1_quad_pins>; - pinctrl-names = "default"; - cs-gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>, <&gpio0 1 GPIO_ACTIVE_HIGH>; - - flash@0 { - compatible = "jedec,spi-nor"; - reg = <0>; - spi-max-frequency = <50000000>; - linux,mtd-name = "spi-nor"; - }; - - flash@1 { - compatible = "gigadevice,gd5f"; - reg = <1>; - spi-max-frequency = <50000000>; - nand-on-flash-bbt; - spi-rx-bus-width = <4>; - #address-cells = <1>; - #size-cells = <1>; - linux,mtd-name = "spi-nand"; - }; -}; - -&uccpsystem { - status = "okay"; -}; - -&uccphostport { - status = "okay"; -}; - -&uccpbthp { - status = "okay"; -}; - -&uccpdummyhp { - status = "okay"; -}; - -&uart0 { - status = "okay"; - assigned-clock-rates = <114278400>, <1843200>; -}; - &uart1 { status = "okay"; }; @@ -162,10 +116,6 @@ mac-address = [0123456789AB]; }; -&sdhost { - status = "okay"; -}; - &ir { status = "okay"; }; @@ -198,19 +148,6 @@ clock-frequency = <400000>; }; -&i2c3 { - status = "okay"; - clock-frequency = <400000>; -}; - -&wifi { - status = "okay"; - mac-address0 = [0123456789AC]; - mac-address1 = [0123456789AD]; - rf-params = [1E00000000002426292A2C2E3237393F454A52576066000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F00000000002426292A2C2E3237393F454A52576066000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F0808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808]; - num_streams = [02]; -}; - &i2s_out { status = "okay"; }; diff --git a/arch/mips/boot/dts/pistachio/pistachio_marduk.dts b/arch/mips/boot/dts/pistachio/pistachio_marduk.dts index 511386e4cac..e855156e526 100644 --- a/arch/mips/boot/dts/pistachio/pistachio_marduk.dts +++ b/arch/mips/boot/dts/pistachio/pistachio_marduk.dts @@ -10,6 +10,7 @@ #include "pistachio.dtsi" #include <dt-bindings/sound/pistachio-audio.h> +#include "pistachio_board_uboot.dtsi" / { model = "IMG Marduk"; @@ -19,6 +20,8 @@ serial0 = &uart0; serial1 = &uart1; ethernet0 = &enet; + spi0 = &spfi0; + spi1 = &spfi1; }; chosen { @@ -38,14 +41,6 @@ regulator-boot-on; }; - /* EXT clock from cc2520 is fed to sc16is752 */ - cc2520_ext_clk: cc2520-ext-clk { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <16000000>; - clock-output-names = "cc2520_ext_clock"; - }; - pistachio_audio_card { compatible = "img,pistachio-audio"; @@ -81,6 +76,13 @@ i2s-in { cpu-dai = <&i2s_in>; format = "i2s"; + + ak5720vt { + mclk = <PISTACHIO_MCLK_I2S>; + mclk-fs = <256>; + mclk-min-freq = <8192000>; + mclk-max-freq = <24576000>; + }; }; }; @@ -148,7 +150,7 @@ cs-gpios = <&gpio1 14 GPIO_ACTIVE_HIGH>, <&gpio0 2 GPIO_ACTIVE_HIGH>, <&gpio1 12 GPIO_ACTIVE_HIGH>, <&gpio1 13 GPIO_ACTIVE_HIGH>; - cc2520@0 { + cc2520: cc2520@0 { compatible = "ti,cc2520"; reg = <0>; spi-max-frequency = <4000000>; @@ -158,13 +160,14 @@ cca-gpio = <&gpio3 6 GPIO_ACTIVE_HIGH>; vreg-gpio = <&gpio2 12 GPIO_ACTIVE_HIGH>; reset-gpio = <&gpio0 12 GPIO_ACTIVE_HIGH>; + #clock-cells = <0>; extclock-freq = <16000000>; }; sc16is752: sc16is752@1 { compatible = "nxp,sc16is752"; reg = <1>; - clocks = <&cc2520_ext_clk>; + clocks = <&cc2520 0>; spi-max-frequency = <4000000>; interrupt-parent = <&gpio0>; interrupts = <11 IRQ_TYPE_EDGE_FALLING>; @@ -204,7 +207,7 @@ reg = <1>; spi-max-frequency = <50000000>; nand-on-flash-bbt; - spi-rx-bus-width = <4>; + spi-rx-bus-width = <2>; #address-cells = <1>; #size-cells = <1>; linux,mtd-name = "spi-nand"; @@ -244,9 +247,13 @@ mac-address = [0019f5ffff00]; }; +&pin_enet_phy_clk { + drive-strength = <2>; +}; + &sdhost { status = "okay"; - bus-width = <4>; + bus-width = <4>; disable-wp; }; @@ -265,6 +272,7 @@ &adc { status = "okay"; vref-supply = <®_1v8>; + adc-reserved-channels = <0x10>; }; &i2c2 { @@ -281,7 +289,7 @@ status = "okay"; mac-address0 = [0019f5ffff01]; mac-address1 = [0019f5ffff02]; - rf-params = [1E00000000002426292A2C2E3237393F454A52576066000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F00000000002426292A2C2E3237393F454A52576066000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F0808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808]; + rf-params = [1A1c1f24292e33383c4045484d5157595d6165686d712023263d3135393c41454a4d505356595c5f6164672023282c3135393d4145494d53565a5e626567696c1c20252b3134373c4144484d52555a5e62666a6f742023282c31363a3e4245494e51575b5f63666b71761c1f24292e33383c4045494d5155585b5e62666a6f1e22262d3135393d42464b4e52575a5c6064676b702023282c3135393d4145494d5356595b5e606366692023272a31363b3f44484c5054575a5e6165696d711f2225292f33383c4043474c5054595d6165696d730B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B]; num_streams = [02]; }; diff --git a/arch/mips/configs/pistachio_defconfig b/arch/mips/configs/pistachio_defconfig index 8c995b4b472..0d3786bba43 100644 --- a/arch/mips/configs/pistachio_defconfig +++ b/arch/mips/configs/pistachio_defconfig @@ -37,6 +37,8 @@ CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y CONFIG_MODULE_FORCE_UNLOAD=y CONFIG_PARTITION_ADVANCED=y +CONFIG_PM_AUTOSLEEP=y +CONFIG_PM_WAKELOCKS=y CONFIG_PM_DEBUG=y CONFIG_PM_ADVANCED_DEBUG=y CONFIG_CPU_FREQ=y @@ -162,13 +164,14 @@ CONFIG_MTD_BLOCK=y CONFIG_MTD_M25P80=y CONFIG_MTD_SPI_NAND=y CONFIG_MTD_SPI_NOR=y +CONFIG_MTD_SPI_NOR_WINBOND_OTP=y CONFIG_MTD_UBI=y CONFIG_MTD_UBI_BLOCK=y CONFIG_ZRAM=m CONFIG_BLK_DEV_LOOP=y CONFIG_SRAM=y +CONFIG_UBOOT_BOOTCOUNT=y CONFIG_ATU=y -CONFIG_SND_SOC_IMG_PISTACHIO_EVENT_TIMER_ATU=y CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y CONFIG_BLK_DEV_SR=m @@ -223,7 +226,7 @@ CONFIG_SERIAL_8250=y CONFIG_SERIAL_8250_CONSOLE=y CONFIG_SERIAL_8250_DW=y CONFIG_SERIAL_OF_PLATFORM=y -CONFIG_SERIAL_SC16IS7XX=m +CONFIG_SERIAL_SC16IS7XX=y # CONFIG_SERIAL_SC16IS7XX_I2C is not set CONFIG_SERIAL_SC16IS7XX_SPI=y CONFIG_HW_RANDOM=y @@ -334,7 +337,7 @@ CONFIG_ISO9660_FS=m CONFIG_JOLIET=y CONFIG_ZISOFS=y CONFIG_UDF_FS=m -CONFIG_VFAT_FS=m +CONFIG_VFAT_FS=y CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y CONFIG_ECRYPT_FS=y @@ -349,9 +352,9 @@ CONFIG_PSTORE_RAM=y CONFIG_NFS_FS=y CONFIG_ROOT_NFS=y CONFIG_NLS_DEFAULT="utf8" -CONFIG_NLS_CODEPAGE_437=m +CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ASCII=m -CONFIG_NLS_ISO8859_1=m +CONFIG_NLS_ISO8859_1=y CONFIG_PRINTK_TIME=y CONFIG_DEBUG_INFO=y CONFIG_MAGIC_SYSRQ=y @@ -384,3 +387,4 @@ CONFIG_CRC_T10DIF=m CONFIG_CRC7=m CONFIG_LIBCRC32C=m # CONFIG_XZ_DEC_X86 is not set +CONFIG_MAC80211_RC_MINSTREL_VHT=y diff --git a/arch/mips/configs/pistachio_prodtest_defconfig b/arch/mips/configs/pistachio_prodtest_defconfig new file mode 100644 index 00000000000..74bea909d06 --- /dev/null +++ b/arch/mips/configs/pistachio_prodtest_defconfig @@ -0,0 +1,390 @@ +CONFIG_MACH_PISTACHIO=y +CONFIG_PISTACHIO_GPTIMER_CLKSRC=y +CONFIG_MIPS_MT_SMP=y +CONFIG_MIPS_CPS=y +# CONFIG_COMPACTION is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 +CONFIG_CMA=y +CONFIG_ZSMALLOC=y +CONFIG_NR_CPUS=4 +CONFIG_PREEMPT_VOLUNTARY=y +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_KERNEL_LZO=y +CONFIG_DEFAULT_HOSTNAME="localhost" +CONFIG_SYSVIPC=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_IKCONFIG=m +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=18 +CONFIG_CGROUPS=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_SCHED=y +CONFIG_CFS_BANDWIDTH=y +CONFIG_NAMESPACES=y +CONFIG_USER_NS=y +CONFIG_BLK_DEV_INITRD=y +# CONFIG_RD_BZIP2 is not set +# CONFIG_RD_LZMA is not set +# CONFIG_RD_LZO is not set +# CONFIG_RD_LZ4 is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_EMBEDDED=y +# CONFIG_COMPAT_BRK is not set +CONFIG_PROFILING=y +CONFIG_CC_STACKPROTECTOR_STRONG=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_PM_AUTOSLEEP=y +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_DEBUG=y +CONFIG_PM_ADVANCED_DEBUG=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT_DETAILS=y +CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPUFREQ_DT=y +CONFIG_CPU_IDLE=y +# CONFIG_MIPS_CPS_CPUIDLE is not set +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_NET_KEY=m +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_MROUTE=y +CONFIG_IP_PIMSM_V1=y +CONFIG_IP_PIMSM_V2=y +CONFIG_SYN_COOKIES=y +CONFIG_INET_AH=m +CONFIG_INET_ESP=m +CONFIG_INET_IPCOMP=m +CONFIG_INET_XFRM_MODE_TRANSPORT=m +CONFIG_INET_XFRM_MODE_TUNNEL=m +CONFIG_INET_XFRM_MODE_BEET=m +# CONFIG_INET_DIAG is not set +CONFIG_TCP_CONG_ADVANCED=y +# CONFIG_TCP_CONG_BIC is not set +# CONFIG_TCP_CONG_WESTWOOD is not set +# CONFIG_TCP_CONG_HTCP is not set +CONFIG_TCP_CONG_LP=m +CONFIG_TCP_MD5SIG=y +CONFIG_IPV6=y +CONFIG_INET6_AH=m +CONFIG_INET6_ESP=m +CONFIG_INET6_XFRM_MODE_TRANSPORT=m +CONFIG_INET6_XFRM_MODE_TUNNEL=m +CONFIG_INET6_XFRM_MODE_BEET=m +CONFIG_IPV6_SIT=m +CONFIG_NETWORK_SECMARK=y +CONFIG_NETFILTER=y +# CONFIG_BRIDGE_NETFILTER is not set +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_XT_MARK=m +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_DSCP=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_SECMARK=y +CONFIG_NETFILTER_XT_TARGET_TCPMSS=m +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_DSCP=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_NF_NAT_IPV4=m +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_NF_CONNTRACK_IPV6=m +CONFIG_NF_NAT_IPV6=m +CONFIG_IP6_NF_IPTABLES=m +CONFIG_IP6_NF_MATCH_IPV6HEADER=m +CONFIG_IP6_NF_FILTER=m +CONFIG_IP6_NF_TARGET_REJECT=m +CONFIG_IP6_NF_MANGLE=m +CONFIG_BRIDGE=m +CONFIG_VLAN_8021Q=m +CONFIG_6LOWPAN=y +CONFIG_IEEE802154=y +CONFIG_IEEE802154_6LOWPAN=y +CONFIG_MAC802154=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_HTB=m +CONFIG_NET_SCH_CODEL=m +CONFIG_NET_SCH_FQ_CODEL=m +CONFIG_NET_CLS_U32=m +CONFIG_CLS_U32_MARK=y +CONFIG_NET_PKTGEN=y +CONFIG_BT=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=y +CONFIG_BT_HCIBTUSB=y +CONFIG_BT_HCIBTSDIO=y +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_BCSP=y +CONFIG_BT_HCIBFUSB=y +CONFIG_BT_HCIVHCI=y +CONFIG_BT_IMG=m +CONFIG_CFG80211=m +CONFIG_NL80211_TESTMODE=y +# CONFIG_CFG80211_DEFAULT_PS is not set +CONFIG_CFG80211_DEBUGFS=y +CONFIG_CFG80211_WEXT=y +CONFIG_MAC80211=m +CONFIG_MAC80211_LEDS=y +CONFIG_MAC80211_DEBUGFS=y +CONFIG_MAC80211_DEBUG_MENU=y +CONFIG_MAC80211_VERBOSE_DEBUG=y +CONFIG_RFKILL=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_DEBUG_DEVRES=y +CONFIG_CONNECTOR=y +CONFIG_MTD=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_M25P80=y +CONFIG_MTD_SPI_NAND=y +CONFIG_MTD_SPI_NOR=y +CONFIG_MTD_SPI_NOR_WINBOND_OTP=y +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_BLOCK=y +CONFIG_ZRAM=m +CONFIG_BLK_DEV_LOOP=y +CONFIG_SRAM=y +CONFIG_UBOOT_BOOTCOUNT=y +CONFIG_ATU=y +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_BLK_DEV_SR=m +CONFIG_SCSI_SPI_ATTRS=y +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_CRYPT=y +CONFIG_DM_VERITY=y +CONFIG_NETDEVICES=y +CONFIG_TUN=m +CONFIG_VETH=m +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_MICROCHIP is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_NET_VENDOR_SEEQ is not set +# CONFIG_NET_VENDOR_SMSC is not set +CONFIG_STMMAC_ETH=y +# CONFIG_NET_VENDOR_VIA is not set +CONFIG_PPP=m +CONFIG_PPP_ASYNC=m +CONFIG_USB_PEGASUS=m +CONFIG_USB_RTL8150=m +CONFIG_USB_RTL8152=m +CONFIG_USB_NET_DM9601=m +CONFIG_USB_NET_SMSC75XX=m +CONFIG_USB_NET_SMSC95XX=m +CONFIG_USB_NET_MCS7830=m +# CONFIG_USB_NET_CDC_SUBSET is not set +# CONFIG_USB_NET_ZAURUS is not set +CONFIG_LIBERTAS_THINFIRM=m +CONFIG_USB_NET_RNDIS_WLAN=m +CONFIG_MAC80211_HWSIM=m +CONFIG_HOSTAP=m +CONFIG_HOSTAP_FIRMWARE=y +CONFIG_HOSTAP_FIRMWARE_NVRAM=y +CONFIG_RT2X00=m +CONFIG_RT2800USB=m +CONFIG_UCCP420WLAN=m +CONFIG_IEEE802154_CC2520=y +# CONFIG_INPUT_MOUSEDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_KEYBOARD_ATKBD is not set +CONFIG_KEYBOARD_GPIO=y +# CONFIG_INPUT_MOUSE is not set +# CONFIG_SERIO is not set +# CONFIG_VT is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_DEVKMEM is not set +CONFIG_SERIAL_8250=y +# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set +# CONFIG_SERIAL_8250_CONSOLE is not set +CONFIG_SERIAL_8250_DW=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIAL_SC16IS7XX=y +# CONFIG_SERIAL_SC16IS7XX_I2C is not set +CONFIG_SERIAL_SC16IS7XX_SPI=y +CONFIG_HW_RANDOM=y +CONFIG_TCG_TPM=y +CONFIG_TCG_TIS_I2C_INFINEON=y +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=m +CONFIG_I2C_IMG=y +CONFIG_I2C_STUB=m +CONFIG_SPI=y +CONFIG_SPI_BITBANG=m +CONFIG_SPI_IMG_SPFI=y +CONFIG_SPI_SPIDEV=y +CONFIG_DEBUG_GPIO=y +CONFIG_GPIO_SYSFS=y +CONFIG_POWER_SUPPLY=y +CONFIG_THERMAL=y +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_CORE=y +CONFIG_IMGPDC_WDT=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_GPIO=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_RC_SUPPORT=y +# CONFIG_RC_DECODERS is not set +CONFIG_RC_DEVICES=y +CONFIG_IR_IMG=y +CONFIG_IR_IMG_NEC=y +CONFIG_IR_IMG_JVC=y +CONFIG_IR_IMG_SONY=y +CONFIG_IR_IMG_SHARP=y +CONFIG_IR_IMG_SANYO=y +CONFIG_IR_IMG_RC5=y +CONFIG_IR_IMG_RC6=y +# CONFIG_DVB_TUNER_DIB0070 is not set +# CONFIG_DVB_TUNER_DIB0090 is not set +CONFIG_FB=y +CONFIG_FB_MODE_HELPERS=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +# CONFIG_LCD_CLASS_DEVICE is not set +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_SEQUENCER=m +CONFIG_SND_SEQ_DUMMY=m +CONFIG_SND_HRTIMER=m +CONFIG_SND_DYNAMIC_MINORS=y +# CONFIG_SND_SPI is not set +CONFIG_SND_USB_AUDIO=m +CONFIG_SND_SOC=y +CONFIG_SND_SOC_IMG=y +CONFIG_SND_SOC_IMG_PISTACHIO_BUB=y +CONFIG_USB=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_OTG=y +CONFIG_USB_MON=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +CONFIG_USB_ACM=y +CONFIG_USB_STORAGE=y +CONFIG_USB_DWC2=y +CONFIG_USB_DWC2_PLATFORM=y +CONFIG_USB_SERIAL=y +CONFIG_USB_SERIAL_GENERIC=y +CONFIG_USB_SERIAL_CP210X=m +CONFIG_USB_SERIAL_FTDI_SIO=m +CONFIG_USB_SERIAL_KEYSPAN=m +CONFIG_USB_SERIAL_PL2303=m +CONFIG_USB_SERIAL_OTI6858=m +CONFIG_USB_SERIAL_QUALCOMM=m +CONFIG_USB_SERIAL_SIERRAWIRELESS=m +CONFIG_USB_SERIAL_OPTION=m +CONFIG_USB_GADGET=y +CONFIG_USB_MASS_STORAGE=m +CONFIG_USB_G_SERIAL=m +CONFIG_MMC=y +CONFIG_MMC_BLOCK_MINORS=16 +CONFIG_MMC_TEST=m +CONFIG_MMC_DW=y +CONFIG_MMC_DW_IDMAC=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_GPIO=m +CONFIG_LEDS_TRIGGER_HEARTBEAT=m +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_PISTACHIO=y +CONFIG_DMADEVICES=y +CONFIG_IMG_MDC_DMA=y +CONFIG_STAGING=y +CONFIG_ASHMEM=y +# CONFIG_ANDROID_TIMED_OUTPUT is not set +# CONFIG_IOMMU_SUPPORT is not set +CONFIG_SOC_IMG=y +CONFIG_MEMORY=y +CONFIG_IIO=y +CONFIG_CC10001_ADC=y +CONFIG_PWM=y +CONFIG_PWM_IMG=y +CONFIG_PHY_PISTACHIO_USB=y +CONFIG_ANDROID=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +# CONFIG_DNOTIFY is not set +CONFIG_FUSE_FS=m +CONFIG_OVERLAY_FS=y +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_UDF_FS=m +CONFIG_VFAT_FS=m +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_ECRYPT_FS=y +CONFIG_HFSPLUS_FS=m +CONFIG_UBIFS_FS=y +CONFIG_SQUASHFS=y +CONFIG_SQUASHFS_FILE_DIRECT=y +CONFIG_SQUASHFS_LZO=y +CONFIG_PSTORE=y +CONFIG_PSTORE_CONSOLE=y +CONFIG_PSTORE_RAM=y +CONFIG_NFS_FS=y +CONFIG_ROOT_NFS=y +CONFIG_NLS_DEFAULT="utf8" +CONFIG_NLS_CODEPAGE_437=m +CONFIG_NLS_ASCII=m +CONFIG_NLS_ISO8859_1=m +CONFIG_PRINTK_TIME=y +CONFIG_DEBUG_INFO=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0 +CONFIG_LOCKUP_DETECTOR=y +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y +CONFIG_BOOTPARAM_HUNG_TASK_PANIC=y +# CONFIG_SCHED_DEBUG is not set +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +CONFIG_DEBUG_SPINLOCK=y +CONFIG_DEBUG_CREDENTIALS=y +CONFIG_FUNCTION_TRACER=y +CONFIG_BLK_DEV_IO_TRACE=y +CONFIG_LKDTM=y +CONFIG_TEST_UDELAY=m +CONFIG_KEYS=y +CONFIG_SECURITY=y +CONFIG_SECURITY_NETWORK=y +CONFIG_SECURITY_YAMA=y +CONFIG_SECURITY_YAMA_STACKED=y +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_SHA512=m +CONFIG_CRYPTO_ARC4=y +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_ANSI_CPRNG is not set +CONFIG_CRYPTO_DEV_IMGTEC_HASH=y +CONFIG_CRC_T10DIF=m +CONFIG_CRC7=m +CONFIG_LIBCRC32C=m +# CONFIG_XZ_DEC_X86 is not set +CONFIG_MAC80211_RC_MINSTREL_VHT=y diff --git a/arch/mips/include/asm/syscall.h b/arch/mips/include/asm/syscall.h index 6499d93ae68..47bc45a67e9 100644 --- a/arch/mips/include/asm/syscall.h +++ b/arch/mips/include/asm/syscall.h @@ -101,10 +101,8 @@ static inline void syscall_get_arguments(struct task_struct *task, /* O32 ABI syscall() - Either 64-bit with O32 or 32-bit */ if ((config_enabled(CONFIG_32BIT) || test_tsk_thread_flag(task, TIF_32BIT_REGS)) && - (regs->regs[2] == __NR_syscall)) { + (regs->regs[2] == __NR_syscall)) i++; - n++; - } while (n--) ret |= mips_get_syscall_arg(args++, task, regs, i++); diff --git a/arch/mips/kernel/scall32-o32.S b/arch/mips/kernel/scall32-o32.S index 4cc13508d96..aa2df3e8dba 100644 --- a/arch/mips/kernel/scall32-o32.S +++ b/arch/mips/kernel/scall32-o32.S @@ -36,16 +36,8 @@ NESTED(handle_sys, PT_SIZE, sp) lw t1, PT_EPC(sp) # skip syscall on return subu v0, v0, __NR_O32_Linux # check syscall number - sltiu t0, v0, __NR_O32_Linux_syscalls + 1 addiu t1, 4 # skip to next instruction sw t1, PT_EPC(sp) - beqz t0, illegal_syscall - - sll t0, v0, 2 - la t1, sys_call_table - addu t1, t0 - lw t2, (t1) # syscall routine - beqz t2, illegal_syscall sw a3, PT_R26(sp) # save a3 for syscall restarting @@ -96,6 +88,16 @@ loads_done: li t1, _TIF_WORK_SYSCALL_ENTRY and t0, t1 bnez t0, syscall_trace_entry # -> yes +syscall_common: + sltiu t0, v0, __NR_O32_Linux_syscalls + 1 + beqz t0, illegal_syscall + + sll t0, v0, 2 + la t1, sys_call_table + addu t1, t0 + lw t2, (t1) # syscall routine + + beqz t2, illegal_syscall jalr t2 # Do The Real Thing (TM) @@ -116,7 +118,7 @@ o32_syscall_exit: syscall_trace_entry: SAVE_STATIC - move s0, t2 + move s0, v0 move a0, sp /* @@ -129,27 +131,18 @@ syscall_trace_entry: 1: jal syscall_trace_enter - bltz v0, 2f # seccomp failed? Skip syscall + bltz v0, 1f # seccomp failed? Skip syscall + + move v0, s0 # restore syscall - move t0, s0 RESTORE_STATIC lw a0, PT_R4(sp) # Restore argument registers lw a1, PT_R5(sp) lw a2, PT_R6(sp) lw a3, PT_R7(sp) - jalr t0 - - li t0, -EMAXERRNO - 1 # error? - sltu t0, t0, v0 - sw t0, PT_R7(sp) # set error flag - beqz t0, 1f - - lw t1, PT_R2(sp) # syscall number - negu v0 # error - sw t1, PT_R0(sp) # save it for syscall restarting -1: sw v0, PT_R2(sp) # result + j syscall_common -2: j syscall_exit +1: j syscall_exit /* ------------------------------------------------------------------------ */ diff --git a/arch/mips/kernel/scall64-64.S b/arch/mips/kernel/scall64-64.S index ad4d44635c7..676fda7dc0a 100644 --- a/arch/mips/kernel/scall64-64.S +++ b/arch/mips/kernel/scall64-64.S @@ -39,18 +39,11 @@ NESTED(handle_sys64, PT_SIZE, sp) .set at #endif - dsubu t0, v0, __NR_64_Linux # check syscall number - sltiu t0, t0, __NR_64_Linux_syscalls + 1 #if !defined(CONFIG_MIPS32_O32) && !defined(CONFIG_MIPS32_N32) ld t1, PT_EPC(sp) # skip syscall on return daddiu t1, 4 # skip to next instruction sd t1, PT_EPC(sp) #endif - beqz t0, illegal_syscall - - dsll t0, v0, 3 # offset into table - ld t2, (sys_call_table - (__NR_64_Linux * 8))(t0) - # syscall routine sd a3, PT_R26(sp) # save a3 for syscall restarting @@ -59,6 +52,17 @@ NESTED(handle_sys64, PT_SIZE, sp) and t0, t1, t0 bnez t0, syscall_trace_entry +syscall_common: + dsubu t2, v0, __NR_64_Linux + sltiu t0, t2, __NR_64_Linux_syscalls + 1 + beqz t0, illegal_syscall + + dsll t0, t2, 3 # offset into table + dla t2, sys_call_table + daddu t0, t2, t0 + ld t2, (t0) # syscall routine + beqz t2, illegal_syscall + jalr t2 # Do The Real Thing (TM) li t0, -EMAXERRNO - 1 # error? @@ -78,14 +82,14 @@ n64_syscall_exit: syscall_trace_entry: SAVE_STATIC - move s0, t2 + move s0, v0 move a0, sp daddiu a1, v0, __NR_64_Linux jal syscall_trace_enter - bltz v0, 2f # seccomp failed? Skip syscall + bltz v0, 1f # seccomp failed? Skip syscall - move t0, s0 + move v0, s0 RESTORE_STATIC ld a0, PT_R4(sp) # Restore argument registers ld a1, PT_R5(sp) @@ -93,19 +97,9 @@ syscall_trace_entry: ld a3, PT_R7(sp) ld a4, PT_R8(sp) ld a5, PT_R9(sp) - jalr t0 - - li t0, -EMAXERRNO - 1 # error? - sltu t0, t0, v0 - sd t0, PT_R7(sp) # set error flag - beqz t0, 1f - - ld t1, PT_R2(sp) # syscall number - dnegu v0 # error - sd t1, PT_R0(sp) # save it for syscall restarting -1: sd v0, PT_R2(sp) # result + j syscall_common -2: j syscall_exit +1: j syscall_exit illegal_syscall: /* This also isn't a 64-bit syscall, throw an error. */ diff --git a/arch/mips/kernel/scall64-n32.S b/arch/mips/kernel/scall64-n32.S index 446cc654da5..611ba3bdbb8 100644 --- a/arch/mips/kernel/scall64-n32.S +++ b/arch/mips/kernel/scall64-n32.S @@ -52,6 +52,7 @@ NESTED(handle_sysn32, PT_SIZE, sp) and t0, t1, t0 bnez t0, n32_syscall_trace_entry +syscall_common: jalr t2 # Do The Real Thing (TM) li t0, -EMAXERRNO - 1 # error? @@ -75,9 +76,9 @@ n32_syscall_trace_entry: daddiu a1, v0, __NR_N32_Linux jal syscall_trace_enter - bltz v0, 2f # seccomp failed? Skip syscall + bltz v0, 1f # seccomp failed? Skip syscall - move t0, s0 + move t2, s0 RESTORE_STATIC ld a0, PT_R4(sp) # Restore argument registers ld a1, PT_R5(sp) @@ -85,19 +86,9 @@ n32_syscall_trace_entry: ld a3, PT_R7(sp) ld a4, PT_R8(sp) ld a5, PT_R9(sp) - jalr t0 + j syscall_common - li t0, -EMAXERRNO - 1 # error? - sltu t0, t0, v0 - sd t0, PT_R7(sp) # set error flag - beqz t0, 1f - - ld t1, PT_R2(sp) # syscall number - dnegu v0 # error - sd t1, PT_R0(sp) # save it for syscall restarting -1: sd v0, PT_R2(sp) # result - -2: j syscall_exit +1: j syscall_exit not_n32_scall: /* This is not an n32 compatibility syscall, pass it on to diff --git a/arch/mips/kernel/scall64-o32.S b/arch/mips/kernel/scall64-o32.S index 66d618bb2fa..64397d266b7 100644 --- a/arch/mips/kernel/scall64-o32.S +++ b/arch/mips/kernel/scall64-o32.S @@ -87,6 +87,7 @@ loads_done: and t0, t1, t0 bnez t0, trace_a_syscall +syscall_common: jalr t2 # Do The Real Thing (TM) li t0, -EMAXERRNO - 1 # error? @@ -130,9 +131,9 @@ trace_a_syscall: 1: jal syscall_trace_enter - bltz v0, 2f # seccomp failed? Skip syscall + bltz v0, 1f # seccomp failed? Skip syscall - move t0, s0 + move t2, s0 RESTORE_STATIC ld a0, PT_R4(sp) # Restore argument registers ld a1, PT_R5(sp) @@ -142,19 +143,9 @@ trace_a_syscall: ld a5, PT_R9(sp) ld a6, PT_R10(sp) ld a7, PT_R11(sp) # For indirect syscalls - jalr t0 + j syscall_common - li t0, -EMAXERRNO - 1 # error? - sltu t0, t0, v0 - sd t0, PT_R7(sp) # set error flag - beqz t0, 1f - - ld t1, PT_R2(sp) # syscall number - dnegu v0 # error - sd t1, PT_R0(sp) # save it for syscall restarting -1: sd v0, PT_R2(sp) # result - -2: j syscall_exit +1: j syscall_exit /* ------------------------------------------------------------------------ */ diff --git a/arch/mips/pistachio/init.c b/arch/mips/pistachio/init.c index 890ac8ad903..1f90fd9097b 100644 --- a/arch/mips/pistachio/init.c +++ b/arch/mips/pistachio/init.c @@ -2,6 +2,7 @@ * Pistachio platform setup * * Copyright (C) 2014 Google, Inc. + * Copyright (C) 2016 Imagination Technologies * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -9,6 +10,7 @@ */ #include <linux/init.h> +#include <linux/io.h> #include <linux/kernel.h> #include <linux/of_address.h> #include <linux/of_fdt.h> @@ -26,9 +28,28 @@ #include <asm/traps.h> #include <asm/reboot.h> +/* + * Core revision register decoding + * Bits 23 to 20: Major rev + * Bits 15 to 8: Minor rev + * Bits 7 to 0: Maintenance rev + */ +#define PISTACHIO_CORE_REV_REG 0xB81483D0 +#define PISTACHIO_CORE_REV_A1 0x00100006 +#define PISTACHIO_CORE_REV_B0 0x00100106 + const char *get_system_type(void) { - return "IMG Pistachio SoC"; + u32 core_rev; + + core_rev = __raw_readl((const void *)PISTACHIO_CORE_REV_REG); + + if (core_rev == PISTACHIO_CORE_REV_B0) + return "IMG Pistachio SoC (B0)"; + else if (core_rev == PISTACHIO_CORE_REV_A1) + return "IMG_Pistachio SoC (A1)"; + else + return "IMG_Pistachio SoC"; } static void __init plat_setup_iocoherency(void) @@ -130,6 +151,8 @@ void __init prom_init(void) mips_cm_probe(); mips_cpc_probe(); register_cps_smp_ops(); + + pr_info("SoC Type: %s\n", get_system_type()); } void __init prom_free_prom_memory(void) diff --git a/drivers/clk/pistachio/clk-pistachio.c b/drivers/clk/pistachio/clk-pistachio.c index 87e5b6ef551..7271c4eba75 100644 --- a/drivers/clk/pistachio/clk-pistachio.c +++ b/drivers/clk/pistachio/clk-pistachio.c @@ -44,7 +44,7 @@ static struct pistachio_gate pistachio_gates[] __initdata = { GATE(CLK_AUX_ADC_INTERNAL, "aux_adc_internal", "sys_internal_div", 0x104, 22), GATE(CLK_AUX_ADC, "aux_adc", "aux_adc_div", 0x104, 23), - GATE(CLK_SD_HOST, "sd_host", "sd_host_div", 0x104, 24), + GATE(CLK_SD_HOST, "sd_host", "sd_host_div4", 0x104, 24), GATE(CLK_BT, "bt", "bt_div", 0x104, 25), GATE(CLK_BT_DIV4, "bt_div4", "bt_div4_div", 0x104, 26), GATE(CLK_BT_DIV8, "bt_div8", "bt_div8_div", 0x104, 27), @@ -54,6 +54,7 @@ static struct pistachio_gate pistachio_gates[] __initdata = { static struct pistachio_fixed_factor pistachio_ffs[] __initdata = { FIXED_FACTOR(CLK_WIFI_DIV4, "wifi_div4", "wifi_pll", 4), FIXED_FACTOR(CLK_WIFI_DIV8, "wifi_div8", "wifi_pll", 8), + FIXED_FACTOR(CLK_SDHOST_DIV4, "sd_host_div4", "sd_host_div", 4), }; static struct pistachio_div pistachio_divs[] __initdata = { @@ -286,10 +287,10 @@ static struct pistachio_div pistachio_periph_divs[] __initdata = { DIV(PERIPH_CLK_ROM_DIV, "rom_div", "periph_sys", 0x10c, 7), DIV(PERIPH_CLK_COUNTER_FAST_DIV, "counter_fast_div", "periph_sys", 0x110, 7), - DIV(PERIPH_CLK_COUNTER_SLOW_PRE_DIV, "counter_slow_pre_div", - "periph_sys", 0x114, 7), - DIV(PERIPH_CLK_COUNTER_SLOW_DIV, "counter_slow_div", - "counter_slow_pre_div", 0x118, 7), + DIV_F(PERIPH_CLK_COUNTER_SLOW_PRE_DIV, "counter_slow_pre_div", + "periph_sys", 0x114, 7, 0, CLK_DIVIDER_ROUND_CLOSEST), + DIV_F(PERIPH_CLK_COUNTER_SLOW_DIV, "counter_slow_div", + "counter_slow_pre_div", 0x118, 7, 0, CLK_DIVIDER_ROUND_CLOSEST), DIV_F(PERIPH_CLK_IR_PRE_DIV, "ir_pre_div", "periph_sys", 0x11c, 7, 0, CLK_DIVIDER_ROUND_CLOSEST), DIV_F(PERIPH_CLK_IR_DIV, "ir_div", "ir_pre_div", 0x120, 7, diff --git a/drivers/clocksource/time-pistachio.c b/drivers/clocksource/time-pistachio.c index 4540d2b2c02..a29a0934ff8 100644 --- a/drivers/clocksource/time-pistachio.c +++ b/drivers/clocksource/time-pistachio.c @@ -146,7 +146,7 @@ static void __init pistachio_clksrc_of_init(struct device_node *node) /* Switch to using the fast counter clock */ ret = regmap_update_bits(periph_regs, PERIP_TIMER_CONTROL, - 0xf, 0x0); + 0x1, 0x0); if (ret) return; diff --git a/drivers/i2c/busses/i2c-img-scb.c b/drivers/i2c/busses/i2c-img-scb.c index bb98088402e..379ef9c3166 100644 --- a/drivers/i2c/busses/i2c-img-scb.c +++ b/drivers/i2c/busses/i2c-img-scb.c @@ -280,8 +280,6 @@ #define ISR_COMPLETE(err) (ISR_COMPLETE_M | (ISR_STATUS_M & (err))) #define ISR_FATAL(err) (ISR_COMPLETE(err) | ISR_FATAL_M) -#define REL_SOC_IP_SCB_2_2_1 0x00020201 - enum img_i2c_mode { MODE_INACTIVE, MODE_RAW, @@ -511,22 +509,8 @@ static void img_i2c_soft_reset(struct img_i2c *i2c) { i2c->t_halt = false; img_i2c_writel(i2c, SCB_CONTROL_REG, 0); - - /* Disable all interrupts */ - img_i2c_writel(i2c, SCB_INT_MASK_REG, 0); - - /* Clear all interrupts */ - img_i2c_writel(i2c, SCB_INT_CLEAR_REG, ~0); - - /* Clear the scb_line_status events */ - img_i2c_writel(i2c, SCB_CLEAR_REG, ~0); - img_i2c_writel(i2c, SCB_CONTROL_REG, SCB_CONTROL_CLK_ENABLE | SCB_CONTROL_SOFT_RESET); - - /* Enable interrupts */ - img_i2c_switch_mode(i2c, MODE_INACTIVE); - img_i2c_writel(i2c, SCB_INT_MASK_REG, i2c->int_enable); } /* @@ -638,10 +622,7 @@ static void img_i2c_complete_transaction(struct img_i2c *i2c, int status) img_i2c_switch_mode(i2c, MODE_INACTIVE); if (status) { i2c->msg_status = status; - img_i2c_soft_reset(i2c); - } else { - img_i2c_writel(i2c, SCB_INT_CLEAR_REG, ~0); - img_i2c_writel(i2c, SCB_CLEAR_REG, ~0); + img_i2c_transaction_halt(i2c, false); } complete(&i2c->msg_complete); } @@ -780,8 +761,8 @@ static unsigned int img_i2c_atomic(struct img_i2c *i2c, break; case CMD_RET_ACK: if (i2c->line_status & LINESTAT_ACK_DET || - (i2c->line_status & LINESTAT_NACK_DET - && i2c->msg.flags & I2C_M_IGNORE_NAK)) { + (i2c->line_status & LINESTAT_NACK_DET && + i2c->msg.flags & I2C_M_IGNORE_NAK)) { if (i2c->msg.len == 0) { next_cmd = CMD_GEN_STOP; } else if (i2c->msg.flags & I2C_M_RD) { @@ -888,87 +869,42 @@ static unsigned int img_i2c_auto(struct img_i2c *i2c, } /* Enable transaction halt on start bit */ - if (i2c->line_status & LINESTAT_START_BIT_DET) { - if (!i2c->last_msg) { - img_i2c_transaction_halt(i2c, true); - /* we're no longer interested in the slave event */ - i2c->int_enable &= ~INT_SLAVE_EVENT; - } - /* - * Remove start bit detected status after it is handled, - * doing so will prevent this condition being hit for - * every interrupt on a particular transfer. - */ - i2c->line_status &= ~LINESTAT_START_BIT_DET; + if (!i2c->last_msg && line_status & LINESTAT_START_BIT_DET) { + img_i2c_transaction_halt(i2c, !i2c->last_msg); + /* we're no longer interested in the slave event */ + i2c->int_enable &= ~INT_SLAVE_EVENT; } mod_timer(&i2c->check_timer, jiffies + msecs_to_jiffies(1)); + if (int_status & INT_STOP_DETECTED) { + /* Drain remaining data in FIFO and complete transaction */ + if (i2c->msg.flags & I2C_M_RD) + img_i2c_read_fifo(i2c); + return ISR_COMPLETE(0); + } + if (i2c->msg.flags & I2C_M_RD) { - if (int_status & INT_MASTER_HALTED) { + if (int_status & (INT_FIFO_FULL_FILLING | INT_MASTER_HALTED)) { img_i2c_read_fifo(i2c); if (i2c->msg.len == 0) - return ISR_COMPLETE(0); - /* - * By releasing and then enabling transaction halt, - * trying to allow only a single byte to proceed. - */ - img_i2c_transaction_halt(i2c, false); - img_i2c_transaction_halt(i2c, !i2c->last_msg); - } - if (int_status & INT_FIFO_FULL_FILLING) { - img_i2c_read_fifo(i2c); - if (i2c->msg.len == 0) { - if (i2c->last_msg) - return ISR_WAITSTOP; - return ISR_COMPLETE(0); - } - } - if (int_status & INT_STOP_DETECTED) { - int ret; - /* - * Stop bit indicates the end of the transfer, it means - * we should read all the data (or drain the FIFO). We - * must signal completion for this transaction. - */ - img_i2c_transaction_halt(i2c, false); - img_i2c_read_fifo(i2c); - ret = (i2c->msg.len == 0) ? 0 : EIO; - return ISR_COMPLETE(ret); + return ISR_WAITSTOP; } } else { - if (int_status & INT_MASTER_HALTED) { + if (int_status & (INT_FIFO_EMPTY | INT_MASTER_HALTED)) { if ((int_status & INT_FIFO_EMPTY) && - i2c->msg.len == 0) - return ISR_COMPLETE(0); - img_i2c_write_fifo(i2c); - /* - * By releasing and then enabling transaction halt, - * trying to allow only a single byte to proceed. - */ - img_i2c_transaction_halt(i2c, false); - img_i2c_transaction_halt(i2c, !i2c->last_msg); - } - if (int_status & INT_FIFO_EMPTY) { - if (i2c->msg.len == 0) { - if (i2c->last_msg) - return ISR_WAITSTOP; - return ISR_COMPLETE(0); - } + i2c->msg.len == 0) + return ISR_WAITSTOP; img_i2c_write_fifo(i2c); } - if (int_status & INT_STOP_DETECTED) { - int ret; - - img_i2c_transaction_halt(i2c, false); - /* - * Stop bit indicates the end of a transfer and if the - * transfer has finished before all data is written to - * the fifo return error with transfer complete signal. - */ - ret = (i2c->msg.len == 0) ? 0 : EIO; - return ISR_COMPLETE(ret); - } + } + if (int_status & INT_MASTER_HALTED) { + /* + * Release and then enable transaction halt, to + * allow only a single byte to proceed. + */ + img_i2c_transaction_halt(i2c, false); + img_i2c_transaction_halt(i2c, !i2c->last_msg); } return 0; @@ -1147,6 +1083,15 @@ static int img_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, i2c->last_msg = (i == num - 1); reinit_completion(&i2c->msg_complete); + /* + * Clear line status and all interrupts before starting a + * transfer, as we may have unserviced interrupts from + * previous transfers that might be handled in the context + * of the new transfer. + */ + img_i2c_writel(i2c, SCB_INT_CLEAR_REG, ~0); + img_i2c_writel(i2c, SCB_CLEAR_REG, ~0); + if (atomic) { img_i2c_atomic_start(i2c); } else { @@ -1162,8 +1107,8 @@ static int img_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, img_i2c_write(i2c); /* - * By releasing and then enabling transaction halt, - * trying to allow only a single byte to proceed. + * Release and then enable transaction halt, to + * allow only a single byte to proceed. * This doesn't have an effect on the initial transfer * but will allow the following transfers to start * processing if the previous transfer was marked as @@ -1181,7 +1126,6 @@ static int img_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, if (time_left == 0) { dev_err(adap->dev.parent, "i2c transfer timed out\n"); i2c->msg_status = -ETIMEDOUT; - img_i2c_soft_reset(i2c); break; } @@ -1225,13 +1169,8 @@ static int img_i2c_init(struct img_i2c *i2c) return -EINVAL; } - if (rev >= REL_SOC_IP_SCB_2_2_1) { - i2c->need_wr_rd_fence = true; - dev_info(i2c->adap.dev.parent, "fence quirk enabled"); - } - - bitrate_khz = i2c->bitrate / 1000; - clk_khz = clk_get_rate(i2c->scb_clk) / 1000; + /* Fencing enabled by default. */ + i2c->need_wr_rd_fence = true; /* Determine what mode we're in from the bitrate */ timing = timings[0]; @@ -1241,13 +1180,18 @@ static int img_i2c_init(struct img_i2c *i2c) break; } } - if (i2c->bitrate > timing.max_bitrate) { - dev_err(i2c->adap.dev.parent, - "requested bitrate (%d) not supported\n", - i2c->bitrate); - return -EINVAL; + if (i2c->bitrate > timings[ARRAY_SIZE(timings) - 1].max_bitrate) { + dev_warn(i2c->adap.dev.parent, + "requested bitrate (%u) is higher than the max bitrate supported (%u)\n", + i2c->bitrate, + timings[ARRAY_SIZE(timings) - 1].max_bitrate); + timing = timings[ARRAY_SIZE(timings) - 1]; + i2c->bitrate = timing.max_bitrate; } + bitrate_khz = i2c->bitrate / 1000; + clk_khz = clk_get_rate(i2c->scb_clk) / 1000; + /* Find the prescale that would give us that inc (approx delay = 0) */ prescale = SCB_OPT_INC * clk_khz / (256 * 16 * bitrate_khz); prescale = clamp_t(unsigned int, prescale, 1, 8); @@ -1297,14 +1241,11 @@ static int img_i2c_init(struct img_i2c *i2c) * Setup clock duty cycle, start with 50% and adjust TCKH and TCKL * values from there if they don't meet minimum timing requirements */ - tckh = tckl = int_bitrate / 2; - if (int_bitrate % 2) - tckl++; + tckh = int_bitrate / 2; + tckl = int_bitrate - tckh; /* Adjust TCKH and TCKL values */ - data = timing.tckl / clk_period; - if (timing.tckl % clk_period) - data++; + data = DIV_ROUND_UP(timing.tckl, clk_period); if (tckl < data) { tckl = data; @@ -1312,18 +1253,16 @@ static int img_i2c_init(struct img_i2c *i2c) } if (tckh > 0) - tckh -= 1; + --tckh; if (tckl > 0) - tckl -= 1; + --tckl; img_i2c_writel(i2c, SCB_TIME_TCKH_REG, tckh); img_i2c_writel(i2c, SCB_TIME_TCKL_REG, tckl); /* Setup TSDH value */ - tsdh = timing.tsdh / clk_period; - if (timing.tsdh % clk_period) - tsdh++; + tsdh = DIV_ROUND_UP(timing.tsdh, clk_period); if (tsdh > 1) data = tsdh - 1; @@ -1362,6 +1301,18 @@ static int img_i2c_init(struct img_i2c *i2c) /* Take module out of soft reset and enable clocks */ img_i2c_soft_reset(i2c); + /* Disable all interrupts */ + img_i2c_writel(i2c, SCB_INT_MASK_REG, 0); + + /* Clear all interrupts */ + img_i2c_writel(i2c, SCB_INT_CLEAR_REG, ~0); + + /* Clear the scb_line_status events */ + img_i2c_writel(i2c, SCB_CLEAR_REG, ~0); + + /* Enable interrupts */ + img_i2c_writel(i2c, SCB_INT_MASK_REG, i2c->int_enable); + /* Perform a synchronous sequence to reset the bus */ ret = img_i2c_reset_bus(i2c); diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 2c2719a3fde..a2f25b83411 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -538,6 +538,13 @@ config IMG_PDM To compile this driver as a module, choose M here: the module will be called img-pdm. +config UBOOT_BOOTCOUNT + tristate "U-Boot Bootcount driver" + depends on OF + help + The U-Boot Bootcount driver allows to access the + bootcounter through sysfs file. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index f6e02a39296..4b26b2eaccf 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -47,6 +47,7 @@ obj-y += ti-st/ obj-y += lis3lv02d/ obj-y += carma/ obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o +obj-$(CONFIG_UBOOT_BOOTCOUNT) += uboot_bootcount.o obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/ obj-$(CONFIG_INTEL_MEI) += mei/ obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/ diff --git a/drivers/misc/atu/atu_clk_maintainer.c b/drivers/misc/atu/atu_clk_maintainer.c index 2589fffff24..297dff8a5a5 100644 --- a/drivers/misc/atu/atu_clk_maintainer.c +++ b/drivers/misc/atu/atu_clk_maintainer.c @@ -639,7 +639,6 @@ static int atu_adjtimex(struct timex *txc) int dir; freq = txc->freq; - freq = (freq * NSEC_PER_USEC) >> 16; if (freq < 0) { dir = -1; diff --git a/drivers/misc/uboot_bootcount.c b/drivers/misc/uboot_bootcount.c new file mode 100644 index 00000000000..684993a9a77 --- /dev/null +++ b/drivers/misc/uboot_bootcount.c @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2015 Imagination Technologies Ltd. + * + * This driver gives access(read/write) to the bootcounter used by u-boot. + * Access is supported via sysfs. + * + * Based on work from: Steffen Rumler <Steffen.Rumler@siemens.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. +*/ + +#include <linux/fs.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/slab.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> + +#define UBOOT_BOOTCOUNT_MAGIC 0xB001C041 /* magic number value */ +#define UBOOT_BOOTCOUNT_MAGIC_MASK 0xFFFF0000 /* magic, when combined */ +#define UBOOT_BOOTCOUNT_COUNT_MASK 0x0000FFFF /* value, when combined */ + + +struct bootcount_device { + void __iomem *reg; +}; + +static int bootcount_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long bootcount; + struct bootcount_device *priv = dev_get_drvdata(dev); + + bootcount = readl(priv->reg); + if ((bootcount & UBOOT_BOOTCOUNT_MAGIC_MASK) != + (UBOOT_BOOTCOUNT_MAGIC & UBOOT_BOOTCOUNT_MAGIC_MASK)) { + return -EINVAL; + } + bootcount &= UBOOT_BOOTCOUNT_COUNT_MASK; + return sprintf(buf, "%lu\n", bootcount); +} + +static int bootcount_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int ret; + u32 value; + struct bootcount_device *priv = dev_get_drvdata(dev); + + ret = kstrtou32(buf, 0, &value); + if (ret < 0) + return ret; + + value = (UBOOT_BOOTCOUNT_MAGIC & UBOOT_BOOTCOUNT_MAGIC_MASK) | + (value & UBOOT_BOOTCOUNT_COUNT_MASK); + writel(value, priv->reg); + + return count; +} + +static DEVICE_ATTR_RW(bootcount); + +static int bootcount_probe(struct platform_device *ofdev) +{ + unsigned long magic; + struct bootcount_device *priv; + struct resource *res; + int status; + + priv = devm_kzalloc(&ofdev->dev, sizeof(struct bootcount_device), GFP_KERNEL); + if (!priv) { + dev_err(&ofdev->dev, "Unable to allocate device private data\n"); + return -ENOMEM; + } + + res = platform_get_resource(ofdev, IORESOURCE_MEM, 0); + priv->reg = devm_ioremap_resource(&ofdev->dev, res); + if (IS_ERR(priv->reg)) { + dev_err(&ofdev->dev, "unable to map register\n"); + return PTR_ERR(priv->reg); + } + + magic = readl(priv->reg); + if ((magic & UBOOT_BOOTCOUNT_MAGIC_MASK) != + (UBOOT_BOOTCOUNT_MAGIC & UBOOT_BOOTCOUNT_MAGIC_MASK)) { + dev_err(&ofdev->dev, "bad magic\n"); + return -EINVAL; + } + + status = device_create_file(&ofdev->dev, &dev_attr_bootcount); + if (status) { + dev_err(&ofdev->dev, "unable to register sysfs entry\n"); + return status; + } + dev_set_drvdata(&ofdev->dev, priv); + return 0; +} + +static int bootcount_remove(struct platform_device *ofdev) +{ + device_remove_file(&ofdev->dev, &dev_attr_bootcount); + return 0; +} + +static const struct of_device_id bootcount_match[] = { + { .compatible = "uboot,bootcount", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, bootcount_match); + +static struct platform_driver bootcount_driver = { + .driver = { + .name = "bootcount", + .of_match_table = bootcount_match, + }, + .probe = bootcount_probe, + .remove = bootcount_remove, +}; + +module_platform_driver(bootcount_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Avinash Tahakik <avinash.tahakik@imgtec.com>"); +MODULE_DESCRIPTION("Provide (read/write) access to the U-Boot bootcounter via sysfs"); diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 3af137f49ac..90002419c2c 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -48,13 +48,15 @@ static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len) return ret; } -static void m25p_addr2cmd(struct spi_nor *nor, unsigned int addr, u8 *cmd) +static void m25p_addr2cmd(unsigned int addr, unsigned int addr_width, u8 *cmd) { - /* opcode is in cmd[0] */ - cmd[1] = addr >> (nor->addr_width * 8 - 8); - cmd[2] = addr >> (nor->addr_width * 8 - 16); - cmd[3] = addr >> (nor->addr_width * 8 - 24); - cmd[4] = addr >> (nor->addr_width * 8 - 32); + if (addr_width) { + /* opcode is in cmd[0] */ + cmd[1] = addr >> (addr_width * 8 - 8); + cmd[2] = addr >> (addr_width * 8 - 16); + cmd[3] = addr >> (addr_width * 8 - 24); + cmd[4] = addr >> (addr_width * 8 - 32); + } } static int m25p_cmdsz(struct spi_nor *nor) @@ -90,7 +92,7 @@ static void m25p80_write(struct spi_nor *nor, loff_t to, size_t len, cmd_sz = 1; flash->command[0] = nor->program_opcode; - m25p_addr2cmd(nor, to, flash->command); + m25p_addr2cmd(to, nor->addr_width, flash->command); t[0].tx_buf = flash->command; t[0].len = cmd_sz; @@ -105,9 +107,9 @@ static void m25p80_write(struct spi_nor *nor, loff_t to, size_t len, *retlen += m.actual_length - cmd_sz; } -static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor) +static inline unsigned int m25p80_rx_nbits(enum read_mode mode) { - switch (nor->flash_read) { + switch (mode) { case SPI_NOR_DUAL: return 2; case SPI_NOR_QUAD: @@ -137,14 +139,14 @@ static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len, memset(t, 0, (sizeof t)); flash->command[0] = nor->read_opcode; - m25p_addr2cmd(nor, from, flash->command); + m25p_addr2cmd(from, nor->addr_width, flash->command); t[0].tx_buf = flash->command; t[0].len = m25p_cmdsz(nor) + dummy; spi_message_add_tail(&t[0], &m); t[1].rx_buf = buf; - t[1].rx_nbits = m25p80_rx_nbits(nor); + t[1].rx_nbits = m25p80_rx_nbits(nor->flash_read); t[1].len = len; spi_message_add_tail(&t[1], &m); @@ -163,13 +165,97 @@ static int m25p80_erase(struct spi_nor *nor, loff_t offset) /* Set up command buffer. */ flash->command[0] = nor->erase_opcode; - m25p_addr2cmd(nor, offset, flash->command); + m25p_addr2cmd(offset, nor->addr_width, flash->command); spi_write(flash->spi, flash->command, m25p_cmdsz(nor)); return 0; } +/* From spi_nor_xfer_cfg, this call ignores cmd_pins, addr_pins, so single I/O + * line is used for cmd and addr + * mode_pins, mode_cycles are ignored, decides nbits based on mode + */ +static int m25p80_read_xfer(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg, + u8 *buf, size_t len, size_t *retlen) +{ + struct m25p *flash = nor->priv; + struct spi_device *spi = flash->spi; + struct spi_transfer t[2]; + struct spi_message m; + u32 dummy = cfg->dummy_cycles/8; /* convert dummy cycles into bytes */ + u32 cmd_sz = (1 + cfg->addr_width + dummy); + int ret; + + if (cfg->addr_width > 4 || cmd_sz > MAX_CMD_SIZE) + return -EINVAL; + + spi_message_init(&m); + memset(t, 0, sizeof (t)); + + memset(flash->command, 0, MAX_CMD_SIZE); + flash->command[0] = cfg->cmd; + m25p_addr2cmd(cfg->addr, cfg->addr_width, flash->command); + + t[0].tx_buf = flash->command; + t[0].len = cmd_sz; + spi_message_add_tail(&t[0], &m); + + if (len) { + t[1].rx_buf = buf; + t[1].rx_nbits = m25p80_rx_nbits(cfg->mode); + t[1].len = len; + spi_message_add_tail(&t[1], &m); + } + + ret = spi_sync(spi, &m); + + if (!ret) + *retlen += (m.actual_length - cmd_sz); + return ret; +} + +/* From spi_nor_xfer_cfg, this call ignores cmd_pins, addr_pins, mode, mode_pins, + * mode_cycles, so single I/O line is used for cmd, addr and data + */ +static int m25p80_write_xfer(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg, + u8 *buf, size_t len, size_t *retlen) +{ + struct m25p *flash = nor->priv; + struct spi_device *spi = flash->spi; + struct spi_transfer t[2]; + struct spi_message m; + u32 dummy = cfg->dummy_cycles/8; /* convert dummy cycles into bytes */ + u32 cmd_sz = (1 + cfg->addr_width + dummy); + int ret; + + if (cfg->addr_width > 4 || cmd_sz > MAX_CMD_SIZE) + return -EINVAL; + + spi_message_init(&m); + memset(t, 0, sizeof(t)); + + memset(flash->command, 0, MAX_CMD_SIZE); + flash->command[0] = cfg->cmd; + m25p_addr2cmd(cfg->addr, cfg->addr_width, flash->command); + + t[0].tx_buf = flash->command; + t[0].len = cmd_sz; + spi_message_add_tail(&t[0], &m); + + if (len) { + t[1].tx_buf = buf; + t[1].len = len; + spi_message_add_tail(&t[1], &m); + } + + ret = spi_sync(spi, &m); + + if (!ret) + *retlen += (m.actual_length - cmd_sz); + return ret; +} + /* * board specific setup should have ensured the SPI clock used here * matches what the READ command supports, at least until this driver @@ -199,6 +285,8 @@ static int m25p_probe(struct spi_device *spi) nor->erase = m25p80_erase; nor->write_reg = m25p80_write_reg; nor->read_reg = m25p80_read_reg; + nor->read_xfer = m25p80_read_xfer; + nor->write_xfer = m25p80_write_xfer; nor->dev = &spi->dev; nor->mtd = &flash->mtd; diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index 64a4f0edabc..e157c33a089 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -28,4 +28,12 @@ config SPI_FSL_QUADSPI This enables support for the Quad SPI controller in master mode. We only connect the NOR to this controller now. +config MTD_SPI_NOR_WINBOND_OTP + bool "Support for winbond security register and unique ID" + depends on MTD_SPI_NOR + default n + help + This enables support for read/write of winbond security registers + as user OTP and also reading of NOR unique ID as factory OTP. + endif # MTD_SPI_NOR diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index 6a7ce146224..6aba941280c 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o +obj-$(CONFIG_MTD_SPI_NOR_WINBOND_OTP) += winbond-otp.o obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o diff --git a/drivers/mtd/spi-nor/spi-nor-common.h b/drivers/mtd/spi-nor/spi-nor-common.h new file mode 100644 index 00000000000..ff15d4225a1 --- /dev/null +++ b/drivers/mtd/spi-nor/spi-nor-common.h @@ -0,0 +1,8 @@ +#ifndef SPI_NOR_COMMON_H +#define SPI_NOR_COMMON_H + +int spi_nor_wait_till_ready(struct spi_nor *nor); +int spi_nor_lock_and_prep(struct spi_nor *nor, enum spi_nor_ops ops); +void spi_nor_unlock_and_unprep(struct spi_nor *nor, enum spi_nor_ops ops); + +#endif diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 1194f33589d..46d2f06ccf1 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -22,6 +22,8 @@ #include <linux/of_platform.h> #include <linux/spi/flash.h> #include <linux/mtd/spi-nor.h> +#include "winbond-otp.h" +#include "spi-nor-common.h" /* Define max times to check status register before we give up. */ #define MAX_READY_WAIT_JIFFIES (40 * HZ) /* M25P16 specs 40s max chip erase */ @@ -55,6 +57,7 @@ struct flash_info { #define SPI_NOR_DUAL_READ 0x20 /* Flash supports Dual Read */ #define SPI_NOR_QUAD_READ 0x40 /* Flash supports Quad Read */ #define USE_FSR 0x80 /* use flag status register */ +#define WINBOND_OTP 0x100 /* use winbond security reg as OTP */ }; #define JEDEC_MFR(info) ((info)->id[0]) @@ -231,7 +234,7 @@ static int spi_nor_ready(struct spi_nor *nor) * Service routine to read status register until ready, or timeout occurs. * Returns non-zero if error. */ -static int spi_nor_wait_till_ready(struct spi_nor *nor) +int spi_nor_wait_till_ready(struct spi_nor *nor) { unsigned long deadline; int timeout = 0, ret; @@ -268,7 +271,7 @@ static int erase_chip(struct spi_nor *nor) return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0, 0); } -static int spi_nor_lock_and_prep(struct spi_nor *nor, enum spi_nor_ops ops) +int spi_nor_lock_and_prep(struct spi_nor *nor, enum spi_nor_ops ops) { int ret = 0; @@ -285,7 +288,7 @@ static int spi_nor_lock_and_prep(struct spi_nor *nor, enum spi_nor_ops ops) return ret; } -static void spi_nor_unlock_and_unprep(struct spi_nor *nor, enum spi_nor_ops ops) +void spi_nor_unlock_and_unprep(struct spi_nor *nor, enum spi_nor_ops ops) { if (nor->unprepare) nor->unprepare(nor, ops); @@ -611,7 +614,7 @@ static const struct spi_device_id spi_nor_ids[] = { { "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) }, { "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) }, { "s25fl008k", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) }, - { "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K) }, + { "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K | WINBOND_OTP) }, { "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, { "s25fl132k", INFO(0x014016, 0, 64 * 1024, 64, 0) }, @@ -1078,6 +1081,9 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) mtd->_unlock = spi_nor_unlock; } + if (info->flags & WINBOND_OTP) + winbond_otp_register(mtd); + /* sst nor chips use AAI word program */ if (info->flags & SST_WRITE) mtd->_write = sst_write; diff --git a/drivers/mtd/spi-nor/winbond-otp.c b/drivers/mtd/spi-nor/winbond-otp.c new file mode 100644 index 00000000000..a9db7708df5 --- /dev/null +++ b/drivers/mtd/spi-nor/winbond-otp.c @@ -0,0 +1,482 @@ +/* + * Imagination Technologies + * + * Copyright (c) 2015 Imagination Technologies Ltd. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This driver provides read/write access to the 3 x 256 bytes security + * registers as user OTP and unique ID of the NOR can be read as factory OTP + * + */ + +#include <linux/types.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/spi-nor.h> +#include <linux/mutex.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include "spi-nor-common.h" + +#define SECURITY_REG_START_ADDR 0x1000 /*first security register addr*/ +#define SECURITY_REG_ADDR_OFFSET 0x1000 /*diff between consecutive reg*/ +#define SECURITY_REG_NUM 3 /* number of security registers */ +#define SECURITY_REG_SIZE 256 /* bytes per security register */ +#define SECURITY_REG_TOTAL_SIZE (SECURITY_REG_NUM * SECURITY_REG_SIZE) +#define SPI_NOR_UNIQUE_ID_LEN 8 /*number of bytes of unique ID */ + +/* SPI FLASH opcodes */ +#define SPINOR_OP_RD_SR2 0x35 /* Read status register 2 */ +#define SPINOR_OP_PR_SECURITY_REG 0x42 /* Program security register */ +#define SPINOR_OP_ER_SECURITY_REG 0x44 /* Erase security register */ +#define SPINOR_OP_RD_SECURITY_REG 0x48 /* Read security register */ +#define SPINOR_OP_RD_UNIQUE_ID 0x4B /* Read unique id */ + +/* Status register 2 */ +#define SR2_LB1_BIT 3 /* security register lock bit 1 */ + +/* Get start addr of the security reg*/ +#define SEC_REG_START_ADDR(addr) (addr & 0x3000) + +static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd) +{ + return mtd->priv; +} + +static inline int write_enable(struct spi_nor *nor) +{ + return nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0); +} + +static inline int write_disable(struct spi_nor *nor) +{ + return nor->write_reg(nor, SPINOR_OP_WRDI, NULL, 0, 0); +} + +static int read_sr(struct spi_nor *nor, u8 opcode, u8 *val) +{ + int ret; + + ret = nor->read_reg(nor, opcode, val, 1); + if (ret < 0) + pr_err("error %d reading SR\n", ret); + return ret; +} + +/* + * Converts address range + * 0 - 0xFF -> 0x1000 - 0x10FF + * 0x100 - 0x1FF -> 0x2000 - 0x20FF + * 0x200 - 0x2FF -> 0x3000 - 0x30FF + * + * This func assumes that sanity checks on addr are done and is in valid range + */ +static loff_t translate_addr(loff_t addr) +{ + int i; + loff_t new_addr = SECURITY_REG_START_ADDR; + + for (i = 0; i < SECURITY_REG_NUM; i++) { + if (addr < ((i+1)*SECURITY_REG_SIZE)) { + new_addr |= addr & (SECURITY_REG_SIZE-1); + break; + } + new_addr += SECURITY_REG_ADDR_OFFSET; + } + + return new_addr; +} + +/* + * Return 3 blocks of 256 bytes security register as user OTP, + * address of these blocks will be 0, 0x100, 0x200 + * driver will convert these address to actual address while doing + * read/write + */ +static int winbond_get_user_otp_info(struct mtd_info *mtd, size_t len, + size_t *retlen, + struct otp_info *otpinfo) +{ + u8 val; + int i, ret; + struct spi_nor *nor = mtd_to_spi_nor(mtd); + + mutex_lock(&nor->lock); + ret = read_sr(nor, SPINOR_OP_RD_SR2, &val); + mutex_unlock(&nor->lock); + + if (ret < 0) + return ret; + + for (i = 0; i < SECURITY_REG_NUM; i++) { + otpinfo[i].start = i * SECURITY_REG_SIZE; + otpinfo[i].length = SECURITY_REG_SIZE; + otpinfo[i].locked = !!(val & BIT(SR2_LB1_BIT + i)); + } + + *retlen = SECURITY_REG_NUM * sizeof(*otpinfo); + + return 0; +} + +static int spi_otp_read(struct spi_nor *nor, loff_t from, + size_t len, size_t *retlen, u_char *buf) +{ + struct spi_nor_xfer_cfg cfg = { + .cmd = SPINOR_OP_RD_SECURITY_REG, + .addr = from, + .addr_width = nor->addr_width, + .mode = SPI_NOR_NORMAL, + .dummy_cycles = 8, + }; + + return nor->read_xfer(nor, &cfg, buf, len, retlen); +} + +static int spi_otp_write(struct spi_nor *nor, loff_t to, + size_t len, size_t *retlen, u_char *buf) +{ + struct spi_nor_xfer_cfg cfg = { + .cmd = SPINOR_OP_PR_SECURITY_REG, + .addr = to, + .addr_width = nor->addr_width, + .mode = SPI_NOR_NORMAL, + }; + + return nor->write_xfer(nor, &cfg, buf, len, retlen); +} + +static int spi_otp_erase(struct spi_nor *nor, loff_t offs) +{ + size_t temp_retlen; + struct spi_nor_xfer_cfg cfg = { + .cmd = SPINOR_OP_ER_SECURITY_REG, + .addr = offs, + .addr_width = nor->addr_width, + .mode = SPI_NOR_NORMAL, + }; + + return nor->write_xfer(nor, &cfg, NULL, 0, &temp_retlen); +} + +static int spi_read_uniqueid(struct spi_nor *nor, u8 *buf) +{ + size_t temp_retlen; + struct spi_nor_xfer_cfg cfg = { + .cmd = SPINOR_OP_RD_UNIQUE_ID, + .addr_width = 0, + .mode = SPI_NOR_NORMAL, + .dummy_cycles = 32, + }; + + return nor->read_xfer(nor, &cfg, buf, SPI_NOR_UNIQUE_ID_LEN, + &temp_retlen); +} + + +static int winbond_read_user_otp(struct mtd_info *mtd, loff_t from, + size_t len, size_t *retlen, u_char *buf) +{ + int ret; + u32 i, read_len, end_addr, sreg_offset; + loff_t temp_addr; + struct spi_nor *nor = mtd_to_spi_nor(mtd); + + *retlen = 0; + + if (from < 0 || from >= SECURITY_REG_TOTAL_SIZE + || (from + len) > SECURITY_REG_TOTAL_SIZE) + return -EINVAL; + + if (!len) + return 0; + + end_addr = from + len; + + ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_READ); + if (ret) + return ret; + + for (i = from; i < end_addr; i += read_len) { + sreg_offset = i & (SECURITY_REG_SIZE-1); + /* if offset not on boundary, read first few bytes */ + if (sreg_offset) { + /* check if everything has to be read from 1 reg */ + if ((sreg_offset + len) <= SECURITY_REG_SIZE) + read_len = len; + else + read_len = SECURITY_REG_SIZE - sreg_offset; + } + /* if it is last chunk, read the remaining bytes */ + else if ((end_addr - i) < SECURITY_REG_SIZE) + read_len = end_addr - i; + else + read_len = SECURITY_REG_SIZE; + + temp_addr = translate_addr(i); + ret = spi_otp_read(nor, temp_addr, read_len, retlen, + buf + (i-from)); + if (ret < 0) + goto error; + } +error: + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_READ); + return ret; +} + +/* + * This func assumes that offset is within valid range of security registers, + * valid offset are 0x1000, 0x2000 or 0x3000 + */ +static int winbond_erase_security_reg(struct spi_nor *nor, loff_t offset) +{ + int ret; + + ret = write_enable(nor); + if (ret < 0) + return ret; + + ret = spi_nor_wait_till_ready(nor); + if (ret) + return ret; + + ret = spi_otp_erase(nor, offset); + if (ret < 0) + return ret; + + ret = spi_nor_wait_till_ready(nor); + + return ret; +} + +/* + * This function does read, modify locally, erase and write to the register to + * be written + * It doesn't do any range checks on reg_addr, sreg_offset, len + */ +static int winbond_write_security_reg(struct spi_nor *nor, loff_t reg_addr, + u32 sreg_offset, size_t len, + size_t *retlen, u_char *buf) +{ + int ret; + size_t temp_retlen = 0; + u8 *reg_buffer; + + if (unlikely(sreg_offset + len > SECURITY_REG_SIZE)) + return -EINVAL; + + reg_buffer = kmalloc(SECURITY_REG_SIZE, GFP_KERNEL); + if (!reg_buffer) + return -ENOMEM; + + /* read the security register */ + ret = spi_otp_read(nor, reg_addr, SECURITY_REG_SIZE, &temp_retlen, + reg_buffer); + if (ret < 0 || temp_retlen != SECURITY_REG_SIZE) + goto error; + + /* modify the part to be written */ + memcpy(reg_buffer + sreg_offset, buf, len); + + /* erase the security register */ + ret = winbond_erase_security_reg(nor, reg_addr); + if (ret < 0) + goto error; + + /* write the security reg*/ + ret = write_enable(nor); + if (ret < 0) + goto error; + + ret = spi_nor_wait_till_ready(nor); + if (ret) + goto error; + + temp_retlen = 0; + + ret = spi_otp_write(nor, reg_addr, SECURITY_REG_SIZE, &temp_retlen, + reg_buffer); + if (ret < 0 || temp_retlen != SECURITY_REG_SIZE) + goto error; + + ret = spi_nor_wait_till_ready(nor); + + *retlen += len; + +error: + kfree(reg_buffer); + return ret; +} + +static int winbond_write_user_otp(struct mtd_info *mtd, loff_t to, + size_t len, size_t *retlen, u_char *buf) +{ + int ret; + u32 i, write_len, end_addr, sreg_offset; + loff_t temp_addr; + struct spi_nor *nor = mtd_to_spi_nor(mtd); + + *retlen = 0; + + if (to < 0 || to >= SECURITY_REG_TOTAL_SIZE + || (to + len) > SECURITY_REG_TOTAL_SIZE) + return -EINVAL; + + if (!len) + return 0; + + end_addr = to + len; + + ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_WRITE); + if (ret) + return ret; + + for (i = to; i < end_addr; i += write_len) { + sreg_offset = i & (SECURITY_REG_SIZE-1); + /* if offset not on boundary, write first few bytes */ + if (sreg_offset) { + /* check if everything has to be written in 1 reg */ + if ((sreg_offset + len) <= SECURITY_REG_SIZE) + write_len = len; + else + write_len = SECURITY_REG_SIZE - sreg_offset; + } + /* if it is last chunk, write the remaining bytes */ + else if ((end_addr - i) < SECURITY_REG_SIZE) + write_len = end_addr - i; + else + write_len = SECURITY_REG_SIZE; + + temp_addr = translate_addr(i); + ret = winbond_write_security_reg(nor, + SEC_REG_START_ADDR(temp_addr), + sreg_offset, write_len, + retlen, buf + (i-to)); + if (ret < 0) + goto error; + } + +error: + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE); + return ret; +} + +static int winbond_lock_user_otp(struct mtd_info *mtd, loff_t from, size_t len) +{ + int ret; + u8 sr1, sr2, security_reg_num; + struct spi_nor *nor = mtd_to_spi_nor(mtd); + + /* allow locking 1 register at a time, + * so ensure that len is 256 + * also check if address is on security register boundary + */ + if (len != SECURITY_REG_SIZE || from < 0 + || from >= SECURITY_REG_TOTAL_SIZE + || from & (SECURITY_REG_SIZE - 1)) + return -EINVAL; + + /* find out the security reg to set */ + security_reg_num = from / SECURITY_REG_SIZE; + + if (unlikely(security_reg_num > (SECURITY_REG_NUM-1))) + return -EINVAL; + + ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_LOCK); + if (ret) + return ret; + + /* read status registers */ + ret = read_sr(nor, SPINOR_OP_RDSR, &sr1); + if (ret < 0) + goto error; + + ret = read_sr(nor, SPINOR_OP_RD_SR2, &sr2); + if (ret < 0) + goto error; + + ret = write_enable(nor); + if (ret < 0) + goto error; + + /* set the corresponding LB bit in security register 2 */ + sr2 |= BIT(SR2_LB1_BIT + security_reg_num); + + /* write status registers */ + nor->cmd_buf[0] = sr1; + nor->cmd_buf[1] = sr2; + ret = nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 2, 0); + + write_disable(nor); + +error: + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK); + return ret; +} + +/* + * Unique ID of NOR device will be reported as factory OTP + */ +static int winbond_get_fact_otp_info(struct mtd_info *mtd, size_t len, + size_t *retlen, + struct otp_info *otpinfo) +{ + otpinfo->start = 0; + otpinfo->length = SPI_NOR_UNIQUE_ID_LEN; + otpinfo->locked = 1; + + *retlen = sizeof(*otpinfo); + + return 0; +} + +static int winbond_read_fact_otp(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + int ret; + + char unique_id[SPI_NOR_UNIQUE_ID_LEN] = {0}; + struct spi_nor *nor = mtd_to_spi_nor(mtd); + + *retlen = 0; + + if (from < 0 || from >= SPI_NOR_UNIQUE_ID_LEN + || (from + len) > SPI_NOR_UNIQUE_ID_LEN) + return -EINVAL; + + if (!len) + return 0; + + ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_READ); + if (ret) + return ret; + + ret = spi_read_uniqueid(nor, unique_id); + if (ret < 0) + goto error; + + /* Read complete unique ID,but just copy whatever is requested */ + memcpy(buf, unique_id + from, len); + *retlen = len; +error: + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_READ); + return ret; +} + +void winbond_otp_register(struct mtd_info *mtd) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + + if (nor->read_xfer && nor->write_xfer) { + mtd->_get_user_prot_info = winbond_get_user_otp_info; + mtd->_read_user_prot_reg = winbond_read_user_otp; + mtd->_write_user_prot_reg = winbond_write_user_otp; + mtd->_lock_user_prot_reg = winbond_lock_user_otp; + mtd->_get_fact_prot_info = winbond_get_fact_otp_info; + mtd->_read_fact_prot_reg = winbond_read_fact_otp; + } else + dev_err(nor->dev, "Required nor interfaces " + "(read_xfer, write_xfer) not defined\n"); +} diff --git a/drivers/mtd/spi-nor/winbond-otp.h b/drivers/mtd/spi-nor/winbond-otp.h new file mode 100644 index 00000000000..29cbbcc25d9 --- /dev/null +++ b/drivers/mtd/spi-nor/winbond-otp.h @@ -0,0 +1,20 @@ +/* + * Imagination Technologies + * + * Copyright (c) 2015 Imagination Technologies Ltd. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#ifndef WINBOND_OTP_H +#define WINBOND_OTP_H + +#ifdef CONFIG_MTD_SPI_NOR_WINBOND_OTP +void winbond_otp_register(struct mtd_info *mtd); +#else +static inline void winbond_otp_register(struct mtd_info *mtd) { return; } +#endif + +#endif diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 7a084a2efd1..483112dedb2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -3029,6 +3029,10 @@ int stmmac_suspend(struct net_device *ndev) stmmac_clear_descriptors(priv); + /* Release the DMA TX/RX socket buffers */ + dma_free_rx_skbufs(priv); + dma_free_tx_skbufs(priv); + /* Enable Power down mode by programming the PMT regs */ if (device_may_wakeup(priv->device)) { priv->hw->mac->pmt(priv->hw, priv->wolopts); diff --git a/drivers/net/ieee802154/cc2520.c b/drivers/net/ieee802154/cc2520.c index d25eb390dbf..2759fb3409d 100644 --- a/drivers/net/ieee802154/cc2520.c +++ b/drivers/net/ieee802154/cc2520.c @@ -21,6 +21,7 @@ #include <linux/skbuff.h> #include <linux/of_gpio.h> #include <linux/ieee802154.h> +#include <linux/clk-provider.h> #include <net/mac802154.h> #include <net/cfg802154.h> @@ -212,6 +213,7 @@ struct cc2520_private { bool disable_tx; /* don't send any packets */ bool disable_rx; /* disable rx */ bool started; /* Flag to know if device is up */ + struct clk *clk; /* external clock */ }; /* Generic Functions */ @@ -789,11 +791,45 @@ static int cc2520_get_platform_data(struct spi_device *spi, * default to 1MHz(reset value) */ pdata->extclockfreq = CC2520_EXTCLOCK_DEFAULT_FREQ; - } + } else + pdata->registerclk = true; return 0; } +static int cc2520_register_clk(struct spi_device *spi, + struct cc2520_platform_data *pdata) +{ + struct device_node *np = spi->dev.of_node; + struct cc2520_private *priv = spi_get_drvdata(spi); + int ret = 0; + + if (pdata->registerclk) { + if (np) { + priv->clk = clk_register_fixed_rate(&spi->dev, np->name, + NULL, CLK_IS_ROOT, pdata->extclockfreq); + + if (!IS_ERR(priv->clk)) { + ret = of_clk_add_provider(np, + of_clk_src_simple_get, + priv->clk); + if (ret) { + clk_unregister(priv->clk); + dev_err(&spi->dev, + "Failed to add clk provider\n"); + } + } else { + dev_err(&spi->dev, "Failed to register clk\n"); + ret = PTR_ERR(priv->clk); + } + } else + dev_err(&spi->dev, "No device node found, ext-clk won't" + " be registered\n"); + } + + return ret; +} + static int cc2520_hw_init(struct cc2520_private *priv) { u8 status = 0, state = 0xff; @@ -1153,10 +1189,21 @@ static int cc2520_probe(struct spi_device *spi) ret = sysfs_create_group(&spi->dev.kobj, &dev_attr_group); if (ret) - goto err_hw_init; + goto err_free_device; + + ret = cc2520_register_clk(spi, &pdata); + if (ret) + goto err_free_sysfs; return 0; +err_free_sysfs: + sysfs_remove_group(&spi->dev.kobj, &dev_attr_group); + +err_free_device: + ieee802154_unregister_hw(priv->hw); + ieee802154_free_hw(priv->hw); + err_hw_init: mutex_destroy(&priv->buffer_mutex); flush_work(&priv->fifop_irqwork); @@ -1167,6 +1214,11 @@ static int cc2520_remove(struct spi_device *spi) { struct cc2520_private *priv = spi_get_drvdata(spi); + if (priv->clk) { + of_clk_del_provider(spi->dev.of_node); + clk_unregister(priv->clk); + } + sysfs_remove_group(&spi->dev.kobj, &dev_attr_group); mutex_destroy(&priv->buffer_mutex); flush_work(&priv->fifop_irqwork); diff --git a/drivers/net/wireless/uccp420wlan/inc/core.h b/drivers/net/wireless/uccp420wlan/inc/core.h index abb9fa3b181..0791fedadca 100644 --- a/drivers/net/wireless/uccp420wlan/inc/core.h +++ b/drivers/net/wireless/uccp420wlan/inc/core.h @@ -88,7 +88,8 @@ extern spinlock_t tsf_lock; #define TX_COMPLETE_TIMEOUT_TICKS msecs_to_jiffies(TX_COMPLETE_TIMEOUT) #define SCAN_ABORT_TIMEOUT 1000 #define SCAN_ABORT_TIMEOUT_TICKS msecs_to_jiffies(SCAN_ABORT_TIMEOUT) - +#define CANCEL_HW_ROC_TIMEOUT 1000 +#define CANCEL_HW_ROC_TIMEOUT_TICKS msecs_to_jiffies(CANCEL_HW_ROC_TIMEOUT) #define DEFAULT_TX_ANT_SELECT 3 /* bitmap of antennas for tx, 3=> both first and * second antenna to be used @@ -199,9 +200,12 @@ struct wifi_params { unsigned int bt_state; unsigned int antenna_sel; int pkt_gen_val; + int init_pkt_gen; int payload_length; int start_prod_mode; int init_prod; + unsigned char bypass_vpd; + unsigned int cont_tx; }; struct cmd_send_recv_cnt { @@ -334,6 +338,10 @@ struct wifi_stats { unsigned int cts_received_mcp_cnt; /*MAC Stats*/ + unsigned int roc_start; + unsigned int roc_stop; + unsigned int roc_complete; + unsigned int roc_stop_complete; /* TX related */ unsigned int tx_cmd_cnt; /* Num of TX commands received from host */ unsigned int tx_done_cnt; /* Num of Tx done events sent to host */ @@ -388,10 +396,12 @@ struct tx_pkt_info { struct sk_buff_head pkt; unsigned int hdr_len; unsigned int queue; + unsigned int vif_index; unsigned int rate[4]; unsigned int retries[4]; unsigned int curr_retries; unsigned int max_retries; + int roc_peer_id; bool adjusted_rates; }; @@ -410,11 +420,13 @@ struct tx_config { unsigned int next_spare_token_ac; /* Used to store the address of pending skbs per ac */ - struct sk_buff_head pending_pkt[MAX_PEND_Q_PER_AC][NUM_ACS]; + struct sk_buff_head pending_pkt[MAX_UMAC_VIF_CHANCTX_TYPES] + [MAX_PEND_Q_PER_AC] + [NUM_ACS]; #ifdef MULTI_CHAN_SUPPORT /* Peer which has the opportunity to xmit next on a queue */ - unsigned int curr_peer_opp[MAX_CHANCTX][NUM_ACS]; + unsigned int curr_peer_opp[MAX_CHANCTX + MAX_OFF_CHANCTX][NUM_ACS]; #else unsigned int curr_peer_opp[NUM_ACS]; #endif @@ -423,8 +435,9 @@ struct tx_config { * it will be used in tx complete. */ #ifdef MULTI_CHAN_SUPPORT - unsigned char desc_chan_map[NUM_TX_DESCS]; - struct tx_pkt_info pkt_info[MAX_CHANCTX][NUM_TX_DESCS]; + int desc_chan_map[NUM_TX_DESCS]; + struct tx_pkt_info pkt_info[MAX_CHANCTX + MAX_OFF_CHANCTX] + [NUM_TX_DESCS]; #else struct tx_pkt_info pkt_info[NUM_TX_DESCS]; #endif @@ -462,17 +475,17 @@ struct econ_ps_cfg_status { #endif struct current_channel { + unsigned int pri_chnl_num; unsigned int center_freq1; unsigned int center_freq2; unsigned int freq_band; unsigned int ch_width; - unsigned int pri_chnl_num; }; struct roc_params { unsigned char roc_in_progress; - unsigned char roc_ps_changed; - unsigned char roc_chan_changed; + unsigned int roc_type; + bool need_offchan; atomic_t roc_mgmt_tx_count; }; @@ -480,11 +493,12 @@ struct mac80211_dev { struct proc_dir_entry *umac_proc_dir_entry; struct device *dev; struct mac_address if_mac_addresses[MAX_VIFS]; + unsigned int current_vif_count; unsigned int active_vifs; struct mutex mutex; int state; int txpower; - unsigned char mc_filters[MCST_ADDR_LIMIT][6]; + unsigned char mc_filters[MCST_ADDR_LIMIT][6]; int mc_filter_count; struct tasklet_struct proc_tx_tasklet; @@ -504,6 +518,8 @@ struct mac80211_dev { struct wifi_stats *stats; char name[20]; char scan_abort_done; + char cancel_hw_roc_done; + char cancel_roc; char chan_prog_done; char reset_complete; int power_save; /* Will be set only when a single VIF in @@ -517,12 +533,15 @@ struct mac80211_dev { * when transmitting bcast frames in AP in IBSS * modes */ + spinlock_t roc_lock; unsigned char tx_antenna; unsigned char tx_last_beacon; unsigned int rts_threshold; #ifdef MULTI_CHAN_SUPPORT spinlock_t chanctx_lock; struct ieee80211_chanctx_conf *chanctx[MAX_CHANCTX]; + struct umac_chanctx *off_chanctx[MAX_OFF_CHANCTX]; + int roc_off_chanctx_idx; int curr_chanctx_idx; int num_active_chanctx; #endif @@ -564,6 +583,7 @@ struct umac_vif { #ifdef MULTI_CHAN_SUPPORT struct list_head list; struct umac_chanctx *chanctx; + struct umac_chanctx *off_chanctx; #endif }; @@ -585,15 +605,23 @@ struct umac_chanctx { #endif +struct curr_peer_info { + int id; + int op_chan_idx; +}; + -extern int wait_for_scan_abort(struct mac80211_dev *dev); -extern int wait_for_channel_prog_complete(struct mac80211_dev *dev); -extern int uccp420wlan_prog_nw_selection(unsigned int nw_select_enabled, - unsigned char *mac_addr); #ifdef MULTI_CHAN_SUPPORT void uccp420wlan_proc_ch_sw_event(struct umac_event_ch_switch *ch_sw_info, void *context); #endif +extern int wait_for_cancel_hw_roc(struct mac80211_dev *dev); +extern int wait_for_scan_abort(struct mac80211_dev *dev); +extern int wait_for_channel_prog_complete(struct mac80211_dev *dev); +extern int wait_for_tx_queue_flush_complete(struct mac80211_dev *dev, + unsigned int token); +extern int uccp420wlan_prog_nw_selection(unsigned int nw_select_enabled, + unsigned char *mac_addr); extern int uccp420wlan_core_init(struct mac80211_dev *dev, unsigned int ftm); extern void uccp420wlan_core_deinit(struct mac80211_dev *dev, unsigned int ftm); extern void uccp420wlan_vif_add(struct umac_vif *uvif); @@ -622,30 +650,43 @@ extern int __uccp420wlan_tx_frame(struct mac80211_dev *dev, bool retry); extern void uccp420wlan_tx_init(struct mac80211_dev *dev); extern void uccp420wlan_tx_deinit(struct mac80211_dev *dev); - +void uccp420wlan_tx_proc_send_pend_frms_all(struct mac80211_dev *dev, + int chan_id); extern void proc_bss_info_changed(unsigned char *mac_addr, int value); extern void packet_generation(unsigned long data); extern int wait_for_reset_complete(struct mac80211_dev *dev); -extern void uccp420wlan_tx_proc_pend_frms(struct mac80211_dev *dev, +extern int uccp420wlan_tx_proc_pend_frms(struct mac80211_dev *dev, int queue, #ifdef MULTI_CHAN_SUPPORT int curr_chanctx_idx, #endif - int peer_id, int token_id); -int get_curr_peer_opp(struct mac80211_dev *dev, +void free_token(struct mac80211_dev *dev, + int token_id, + int queue); + +struct curr_peer_info get_curr_peer_opp(struct mac80211_dev *dev, #ifdef MULTI_CHAN_SUPPORT int curr_chanctx_idx, #endif int queue); +int uccp420_flush_vif_queues(struct mac80211_dev *dev, + struct umac_vif *uvif, + int chanctx_idx, + unsigned int hw_queue_map, + enum UMAC_VIF_CHANCTX_TYPE vif_chanctx_type); + /* Beacon TimeStamp */ __s32 __attribute__((weak)) frc_to_atu(__u32 frccnt, __u64 *patu, s32 dir); int __attribute__((weak)) get_evt_timer_freq(unsigned int *mask, unsigned int *num, unsigned int *denom); +int tx_queue_map(int queue); +int tx_queue_unmap(int queue); + extern unsigned char *rf_params_vpd; extern int num_streams_vpd; diff --git a/drivers/net/wireless/uccp420wlan/inc/hal.h b/drivers/net/wireless/uccp420wlan/inc/hal.h index 4b850e27c38..b1268250aec 100644 --- a/drivers/net/wireless/uccp420wlan/inc/hal.h +++ b/drivers/net/wireless/uccp420wlan/inc/hal.h @@ -37,8 +37,8 @@ typedef int (*msg_handler)(void *, unsigned char); struct hal_ops_tag { int (*init)(void *); int (*deinit)(void *); - int (*start)(struct proc_dir_entry *); - int (*stop)(struct proc_dir_entry *); + int (*start)(void); + int (*stop)(void); void (*register_callback)(msg_handler, unsigned char); void (*send)(void*, unsigned char, unsigned char, void*); int (*init_bufs)(unsigned int, unsigned int, unsigned int, @@ -50,6 +50,8 @@ struct hal_ops_tag { void (*set_mem_region)(unsigned int); void (*request_mem_regions)(unsigned char **, unsigned char **, unsigned char **); + void (*enable_irq_wake)(void); + void (*disable_irq_wake)(void); }; extern struct hal_ops_tag hal_ops; diff --git a/drivers/net/wireless/uccp420wlan/inc/host_umac_if.h b/drivers/net/wireless/uccp420wlan/inc/host_umac_if.h index 5d41ad9792b..66cb2a89c8f 100644 --- a/drivers/net/wireless/uccp420wlan/inc/host_umac_if.h +++ b/drivers/net/wireless/uccp420wlan/inc/host_umac_if.h @@ -40,7 +40,9 @@ #define MAX_PEND_Q_PER_AC (MAX_PEERS + MAX_VIFS) #ifdef MULTI_CHAN_SUPPORT -#define MAX_CHANCTX 2 +#define MAX_CHANCTX MAX_VIFS +#define MAX_OFF_CHANCTX MAX_VIFS +#define OFF_CHANCTX_IDX_BASE MAX_CHANCTX #endif #define WEP40_KEYLEN 5 @@ -246,6 +248,10 @@ struct umac_event_mib_stats { struct umac_event_mac_stats { struct host_mac_msg_hdr hdr; + unsigned int roc_start; + unsigned int roc_stop; + unsigned int roc_complete; + unsigned int roc_stop_complete; /* TX related */ unsigned int tx_cmd_cnt; /* Num of TX commands received from host */ unsigned int tx_done_cnt; /* Num of Tx done events sent to host */ @@ -342,12 +348,6 @@ enum UMAC_PS_ECON_WAKE_TRIG { TRIG_DISCONNECT }; -struct umac_event_roc_status { - struct host_mac_msg_hdr hdr; - unsigned int roc_status; -} __packed; - - struct umac_event_ps_econ_wake { struct host_mac_msg_hdr hdr; enum UMAC_PS_ECON_WAKE_TRIG trigger; @@ -402,6 +402,7 @@ enum UMAC_CMD_TAG { #ifdef MULTI_CHAN_SUPPORT UMAC_CMD_CHANCTX_TIME_INFO, #endif + UMAC_CMD_CONT_TX, }; enum UMAC_EVENT_TAG { @@ -510,7 +511,10 @@ struct cmd_tx_ctrl { #define AMPDU_AGGR_DISABLED 0x00000000 unsigned char aggregate_mpdu; - unsigned char force_encrypt; +#define ENCRYPT_DISABLE 0 +#define ENCRYPT_ENABLE 1 + unsigned char encrypt; + #define MAC_HDR_SIZE 52 unsigned int pkt_gram_payload_len; @@ -709,7 +713,9 @@ struct cmd_roc { unsigned int roc_ctrl; unsigned int roc_channel; unsigned int roc_duration; - +#define ROC_TYPE_NORMAL 0 +#define ROC_TYPE_OFFCHANNEL_TX 1 + unsigned int roc_type; } __packed; enum POWER_SAVE_TAG { @@ -932,6 +938,12 @@ struct cmd_aux_adc_chain_sel { unsigned int chain_id; } __packed; +struct cmd_cont_tx { + struct host_mac_msg_hdr hdr; + unsigned int op; +} __packed; + + /* DFS SUPPORT */ /* Command to start/stop Radar detection operation */ @@ -1127,4 +1139,9 @@ struct cmd_bt_info { unsigned int bt_state; } __packed; +struct umac_event_roc_status { + struct host_mac_msg_hdr hdr; + unsigned int roc_status; +} __packed; + #endif /*_UCCP420HOST_UMAC_IF_H_*/ diff --git a/drivers/net/wireless/uccp420wlan/inc/umac_if.h b/drivers/net/wireless/uccp420wlan/inc/umac_if.h index cd6ba74be3e..4810619d02a 100644 --- a/drivers/net/wireless/uccp420wlan/inc/umac_if.h +++ b/drivers/net/wireless/uccp420wlan/inc/umac_if.h @@ -33,6 +33,8 @@ #include "hal.h" #include "host_umac_if.h" +#define UMAC_ROC_AC WLAN_AC_VO + struct umac_key { unsigned char *peer_mac; unsigned char *tx_mic; @@ -184,6 +186,7 @@ extern int uccp420wlan_prog_mcast_filter_control(unsigned int extern int uccp420wlan_prog_rcv_bcn_mode(unsigned int bcn_rcv_mode); extern int uccp420wlan_prog_aux_adc_chain(unsigned int chain_id); +extern int uccp420wlan_prog_cont_tx(int val); extern int uccp420wlan_prog_txq_params(int index, unsigned char *vif_addr, unsigned int queue, @@ -235,7 +238,8 @@ extern int uccp420wlan_prog_vht_bform(unsigned int vht_beamform_status, extern int uccp420wlan_prog_roc(unsigned int roc_status, unsigned int roc_channel, - unsigned int roc_duration); + unsigned int roc_duration, + unsigned int roc_type); #ifdef CONFIG_PM extern int uccp420wlan_prog_econ_ps_state(int if_index, diff --git a/drivers/net/wireless/uccp420wlan/inc/version.h b/drivers/net/wireless/uccp420wlan/inc/version.h index c2a476ce921..637d566076d 100644 --- a/drivers/net/wireless/uccp420wlan/inc/version.h +++ b/drivers/net/wireless/uccp420wlan/inc/version.h @@ -23,7 +23,7 @@ */ #ifndef _UCCP420WLAN_VERSION_H #define _UCCP420WLAN_VERSION_H -#define UCCP_DRIVER_VERSION "4_5_8" +#define UCCP_DRIVER_VERSION "6_0_3" #define UCCP_DRIVER_NAME "UCCP420WIFI" #endif /* _UCCP420WLAN_VERSION_H */ diff --git a/drivers/net/wireless/uccp420wlan/src/80211_if.c b/drivers/net/wireless/uccp420wlan/src/80211_if.c index b6f13ff8e7e..2c42d35f95e 100644 --- a/drivers/net/wireless/uccp420wlan/src/80211_if.c +++ b/drivers/net/wireless/uccp420wlan/src/80211_if.c @@ -24,7 +24,6 @@ #include <linux/kernel.h> #include <linux/moduleparam.h> -#include <linux/proc_fs.h> #include <linux/version.h> #include <linux/device.h> @@ -37,6 +36,7 @@ #include <linux/etherdevice.h> #include <linux/platform_device.h> #include <linux/interrupt.h> +#include <linux/proc_fs.h> #include "version.h" #include "core.h" @@ -62,6 +62,7 @@ unsigned int system_rev = 0x494D47; /*ASCII: IMG*/ static void uccp420_roc_complete_work(struct work_struct *work); static void uccp420wlan_exit(void); static int load_fw(struct ieee80211_hw *hw); +int uccp_reinit; #define CHAN2G(_freq, _idx) { \ .band = IEEE80211_BAND_2GHZ, \ @@ -178,7 +179,7 @@ static struct ieee80211_supported_band band_5ghz = { }; -/* Interface combinations for Virtual interfaces*/ +/* Interface combinations for Virtual interfaces */ static const struct ieee80211_iface_limit if_limit1[] = { { .max = 2, .types = BIT(NL80211_IFTYPE_STATION)} }; @@ -203,7 +204,9 @@ static const struct ieee80211_iface_limit if_limit4[] = { #ifdef MULTI_CHAN_SUPPORT static const struct ieee80211_iface_limit if_limit5[] = { { .max = 1, .types = BIT(NL80211_IFTYPE_STATION)}, - { .max = 1, .types = BIT(NL80211_IFTYPE_AP)} + { .max = 1, .types = BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_P2P_CLIENT)} }; #endif @@ -225,6 +228,10 @@ static const struct ieee80211_iface_combination if_comb[] = { .n_limits = ARRAY_SIZE(if_limit5), .max_interfaces = 2, .num_different_channels = 2}, + { .limits = if_limit1, + .n_limits = ARRAY_SIZE(if_limit1), + .max_interfaces = 2, + .num_different_channels = 2}, #endif { .limits = if_limit4, .n_limits = ARRAY_SIZE(if_limit4), @@ -274,6 +281,111 @@ static int conv_str_to_byte(unsigned char *byte, } +static void uccp420_roc_complete_work(struct work_struct *work) +{ + struct delayed_work *dwork = NULL; + struct mac80211_dev *dev = NULL; + unsigned long flags; + struct umac_chanctx *off_chanctx = NULL; + struct umac_vif *uvif = NULL, *tmp = NULL; + struct tx_config *tx = NULL; + u32 roc_queue = 0; + bool need_offchan; + int roc_off_chanctx_idx = -1; + int chan_id = 0; + + dwork = container_of(work, struct delayed_work, work); + dev = container_of(dwork, struct mac80211_dev, roc_complete_work); + tx = &dev->tx; + + mutex_lock(&dev->mutex); + need_offchan = dev->roc_params.need_offchan; + + roc_queue = tx_queue_unmap(UMAC_ROC_AC); + roc_off_chanctx_idx = dev->roc_off_chanctx_idx; + + /* Stop the ROC queue */ + ieee80211_stop_queue(dev->hw, roc_queue); + /* Unlock RCU immediately as we are freeing off_chanctx in this funciton + * only and because flush_vif_queues sleep + */ + rcu_read_lock(); + off_chanctx = rcu_dereference(dev->off_chanctx[roc_off_chanctx_idx]); + rcu_read_unlock(); + + list_for_each_entry_safe(uvif, tmp, &off_chanctx->vifs, list) { + if (uvif == NULL || uvif->off_chanctx == NULL) + continue; + /* Flush the TX queues */ + uccp420_flush_vif_queues(dev, + uvif, + uvif->off_chanctx->index, + BIT(UMAC_ROC_AC), + UMAC_VIF_CHANCTX_TYPE_OFF); + + + spin_lock_irqsave(&tx->lock, flags); + spin_lock(&dev->chanctx_lock); + + /* ROC DONE: Move the channel context */ + if (uvif->chanctx) + dev->curr_chanctx_idx = uvif->chanctx->index; + else + dev->curr_chanctx_idx = -1; + + spin_unlock(&dev->chanctx_lock); + spin_unlock_irqrestore(&tx->lock, flags); + + if (need_offchan) { + /* DEL from OFF chan list */ + list_del_init(&uvif->list); + if (uvif->chanctx) { + /* Add it back to OP chan list */ + list_add_tail(&uvif->list, + &uvif->chanctx->vifs); + + /* !need_offchan: In this case, the frames are + * transmitted, so trigger is not needed. + * + * need_offchan: In this case, frames are + * buffered so we need trigger in case no frames + * come from mac80211. + */ + /* Process OPER pending frames only. + * TXQ is flushed before start of ROC + */ + chan_id = uvif->chanctx->index; + uccp420wlan_tx_proc_send_pend_frms_all(dev, + chan_id); + } + off_chanctx->nvifs--; + } + uvif->off_chanctx = NULL; + } + + if (need_offchan) + kfree(off_chanctx); + + + rcu_assign_pointer(dev->off_chanctx[roc_off_chanctx_idx], NULL); + dev->roc_off_chanctx_idx = -1; + dev->roc_params.roc_in_progress = 0; + + if (dev->cancel_roc == 0) { + ieee80211_remain_on_channel_expired(dev->hw); + DEBUG_LOG("%s-80211IF: ROC STOPPED..\n", dev->name); + } else { + dev->cancel_hw_roc_done = 1; + dev->cancel_roc = 0; + DEBUG_LOG("%s-80211IF: ROC CANCELLED..\n", dev->name); + } + + /* Start the ROC queue */ + ieee80211_wake_queue(dev->hw, roc_queue); + mutex_unlock(&dev->mutex); +} + + static void tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *txctl, struct sk_buff *skb) @@ -377,10 +489,13 @@ static int start(struct ieee80211_hw *hw) mutex_unlock(&dev->mutex); return -ENODEV; } + INIT_DELAYED_WORK(&dev->roc_complete_work, uccp420_roc_complete_work); + dev->state = STARTED; memset(dev->params->pdout_voltage, 0, sizeof(char) * MAX_AUX_ADC_SAMPLES); + dev->roc_off_chanctx_idx = -1; mutex_unlock(&dev->mutex); return 0; @@ -408,20 +523,29 @@ static int add_interface(struct ieee80211_hw *hw, struct umac_vif *uvif; int vif_index, iftype; + mutex_lock(&dev->mutex); iftype = vif->type; v = vif; vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER; vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD; + if (dev->current_vif_count == wifi->params.num_vifs) { + pr_err("%s: Exceeded Maximum supported VIF's cur:%d max: %d.\n", + __func__, + dev->current_vif_count, + wifi->params.num_vifs); + + mutex_unlock(&dev->mutex); + return -ENOTSUPP; + } + if (!(iftype == NL80211_IFTYPE_STATION || - iftype == NL80211_IFTYPE_ADHOC || - iftype == NL80211_IFTYPE_AP)) { + iftype == NL80211_IFTYPE_ADHOC || + iftype == NL80211_IFTYPE_AP)) { pr_err("Invalid Interface type\n"); return -ENOTSUPP; } - mutex_lock(&dev->mutex); - if (wifi->params.production_test) { if (dev->active_vifs || iftype != NL80211_IFTYPE_ADHOC) { mutex_unlock(&dev->mutex); @@ -448,6 +572,7 @@ static int add_interface(struct ieee80211_hw *hw, uvif->seq_no = 0; uccp420wlan_vif_add(uvif); dev->active_vifs |= (1 << vif_index); + dev->current_vif_count++; if (iftype == NL80211_IFTYPE_ADHOC) dev->tx_last_beacon = 0; @@ -467,16 +592,16 @@ static void remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *v; int vif_index; + mutex_lock(&dev->mutex); v = vif; vif_index = ((struct umac_vif *)&v->drv_priv)->vif_index; - mutex_lock(&dev->mutex); - uccp420wlan_vif_remove((struct umac_vif *)&v->drv_priv); dev->active_vifs &= ~(1 << vif_index); rcu_assign_pointer(dev->vifs[vif_index], NULL); synchronize_rcu(); + dev->current_vif_count--; mutex_unlock(&dev->mutex); } @@ -587,9 +712,9 @@ static int config(struct ieee80211_hw *hw, if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) { DEBUG_LOG("%s-80211IF:Retry Limits changed to %d and %d\n", - dev->name, - conf->short_frame_max_tx_count, - conf->long_frame_max_tx_count); + dev->name, + conf->short_frame_max_tx_count, + conf->long_frame_max_tx_count); } for (i = 0; i < MAX_VIFS; i++) { @@ -1136,6 +1261,7 @@ static void init_hw(struct ieee80211_hw *hw) hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; hw->max_listen_interval = 10; hw->wiphy->max_remain_on_channel_duration = 5000; /*ROC*/ + hw->offchannel_tx_hw_queue = WLAN_AC_VO; hw->max_rates = 4; hw->max_rate_tries = 5; hw->queues = 4; @@ -1271,193 +1397,160 @@ static int set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) } -static void uccp420_roc_complete_work(struct work_struct *work) -{ - struct delayed_work *dwork; - int i; - struct mac80211_dev *dev; - - dwork = container_of(work, struct delayed_work, work); - dev = container_of(dwork, struct mac80211_dev, roc_complete_work); - - if (atomic_read(&dev->roc_params.roc_mgmt_tx_count) != 0) { - DEBUG_LOG("%s:%d but %d off channel tx frames pending\n", - __func__, - __LINE__, - atomic_read(&dev->roc_params.roc_mgmt_tx_count)); - return; - } - - /* ROC Completed */ - mutex_lock(&dev->mutex); - - /* Put the chip back to its original state */ - for (i = 0; i < MAX_VIFS; i++) { - - if (!dev->roc_params.roc_ps_changed) - break; - - if (!(dev->active_vifs & (1 << i))) - continue; - - uccp420wlan_prog_ps_state(i, - dev->if_mac_addresses[i].addr, - dev->power_save); - } - - dev->roc_params.roc_ps_changed = 0; - - if (dev->roc_params.roc_chan_changed) { - dev->chan_prog_done = 0; - - uccp420wlan_prog_channel(dev->cur_chan.pri_chnl_num, - dev->cur_chan.center_freq1, - dev->cur_chan.center_freq2, - dev->cur_chan.ch_width, -#ifdef MULTI_CHAN_SUPPORT - 0, -#endif - dev->cur_chan.freq_band); - - if (wait_for_channel_prog_complete(dev)) { - pr_err("%s:%d ROC Complete: Programming the Channel %d Timed-out (500ms)\n", - __func__, __LINE__, dev->cur_chan.pri_chnl_num); - dev->roc_params.roc_in_progress = 0; - dev->roc_params.roc_chan_changed = 0; - ieee80211_remain_on_channel_expired(dev->hw); - mutex_unlock(&dev->mutex); - - /* Unable to go back to Home channel, what next?? */ - return; - } - - dev->roc_params.roc_chan_changed = 0; - } - - /* Inform FW that ROC is started */ - uccp420wlan_prog_roc(ROC_START, dev->cur_chan.pri_chnl_num, 0); - - ieee80211_remain_on_channel_expired(dev->hw); - dev->roc_params.roc_in_progress = 0; - - DEBUG_LOG("%s:%d Coming back to Orig: %d\n", - __func__, - __LINE__, - dev->power_save); - - mutex_unlock(&dev->mutex); -} - - static int remain_on_channel(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_channel *channel, int duration, enum ieee80211_roc_type type) - { - int i; struct mac80211_dev *dev = (struct mac80211_dev *)hw->priv; - unsigned int pri_chnl_num = 0; - unsigned int chnl_num1 = 0; - unsigned int freq_band = channel->band; - unsigned int ch_width = 0; /* 20MHz */ -#ifdef MULTI_CHAN_SUPPORT + unsigned int pri_chnl_num = + ieee80211_frequency_to_channel(channel->center_freq); struct umac_vif *uvif = (struct umac_vif *)vif->drv_priv; -#endif - - pri_chnl_num = ieee80211_frequency_to_channel(channel->center_freq); - chnl_num1 = ieee80211_frequency_to_channel(channel->center_freq); + struct umac_chanctx *off_chanctx = NULL; + int off_chanctx_id = 0, i = 0; + unsigned long flags; + struct tx_config *tx = &dev->tx; + u32 hw_queue_map = 0; + struct ieee80211_chanctx_conf *vif_chanctx; + bool need_offchan = true; mutex_lock(&dev->mutex); - DEBUG_LOG("%s:%d orig_ps: %d The Params are: channel:%d\n", - __func__, __LINE__, - dev->power_save, - pri_chnl_num); - DEBUG_LOG(" duration:%d type: %d c1:%d band:%d\n", + DEBUG_LOG("%s-80211IF: Params are Chan:%d Dur:%d Type: %d\n", + dev->name, + ieee80211_frequency_to_channel(channel->center_freq), duration, - type, - chnl_num1, - freq_band); - - /* Put the chip in powersave */ - for (i = 0; i < MAX_VIFS; i++) { - if (dev->power_save == PWRSAVE_STATE_AWAKE) - break; + type); - dev->roc_params.roc_ps_changed = 1; + if (dev->roc_params.roc_in_progress) { + DEBUG_LOG("%s-80211IF: Dropping roc...Busy\n", dev->name); + mutex_unlock(&dev->mutex); + return -EBUSY; + } - if (!(dev->active_vifs & (1 << i))) - continue; + if (dev->num_active_chanctx == 2) { + DEBUG_LOG("%s-80211IF: ROC is not supported in TSMC Mode\n", + dev->name); - uccp420wlan_prog_ps_state(i, - dev->if_mac_addresses[i].addr, - PWRSAVE_STATE_AWAKE); + mutex_unlock(&dev->mutex); + return -ENOTSUPP; } - do { - if (dev->cur_chan.pri_chnl_num == pri_chnl_num) - break; + /* Inform FW that ROC is started: + * For pure TX we send OFFCHANNEL_TX so that driver can terminate ROC + * For Tx + Rx we use NORMAL, FW will terminate ROC based on duration. + */ + if (duration != 10 && type == ROC_TYPE_OFFCHANNEL_TX) + type = ROC_TYPE_NORMAL; - DEBUG_LOG("%s:%d Programming the Channel\n", - __func__, __LINE__); + /* uvif is in connected state + */ + if (uvif->chanctx) { + rcu_read_lock(); - dev->chan_prog_done = 0; + vif_chanctx = + rcu_dereference(dev->chanctx[uvif->chanctx->index]); - uccp420wlan_prog_channel(dev->cur_chan.pri_chnl_num, - channel->center_freq, - 0, - ch_width, -#ifdef MULTI_CHAN_SUPPORT - uvif->vif_index, -#endif - freq_band); + /* AS ROC frames are MGMT frames, checking only for Primary + * Channel. + */ + if (vif_chanctx->def.chan->center_freq == channel->center_freq) + need_offchan = false; - if (!wait_for_channel_prog_complete(dev)) { - dev->roc_params.roc_chan_changed = 1; - break; - } + rcu_read_unlock(); + } - pr_err("%s:%d ROC Start: Programming the Channel %d Timed-out (500ms)\n", - __func__, __LINE__, pri_chnl_num); + DEBUG_LOG("%s-80211IF: need_offchan: %d\n", dev->name, need_offchan); + dev->roc_params.need_offchan = need_offchan; - /* Put the chip back to its orig state*/ - for (i = 0; i < MAX_VIFS; i++) { - if (!dev->roc_params.roc_ps_changed) - break; + if (need_offchan) { + /* Different chan context than the uvif */ + off_chanctx = kmalloc(sizeof(struct umac_chanctx), + GFP_KERNEL); - if (!(dev->active_vifs & (1 << i))) - continue; + if (!off_chanctx) { + pr_err("%s: Unable to alloc mem for channel context\n", + __func__); + mutex_unlock(&dev->mutex); + return -ENOMEM; + } - uccp420wlan_prog_ps_state(i, - dev->if_mac_addresses[i].addr, - dev->power_save); + /** Currently OFFCHAN is limited to handling ROC case + * but it is meant for a generic case. + * ideally we should look for existing offchan context + * and re-use/create. + */ + for (i = 0; i < MAX_OFF_CHANCTX; i++) { + if (!dev->off_chanctx[i]) { + off_chanctx_id = i; + break; + } } - dev->roc_params.roc_ps_changed = 0; + if (uvif->chanctx) { + ieee80211_stop_queues(hw); - ieee80211_remain_on_channel_expired(dev->hw); - mutex_unlock(&dev->mutex); + hw_queue_map = BIT(WLAN_AC_BK) | + BIT(WLAN_AC_BE) | + BIT(WLAN_AC_VI) | + BIT(WLAN_AC_VO) | + BIT(WLAN_AC_BCN); - return 0; + uccp420_flush_vif_queues(dev, + uvif, + uvif->chanctx->index, + hw_queue_map, + UMAC_VIF_CHANCTX_TYPE_OPER); + } + + + off_chanctx->index = OFF_CHANCTX_IDX_BASE + off_chanctx_id; + dev->roc_off_chanctx_idx = off_chanctx_id; + INIT_LIST_HEAD(&off_chanctx->vifs); + off_chanctx->nvifs = 0; - } while (0); + if (uvif->chanctx) { + /* Delete the uvif from OP channel list */ + list_del_init(&uvif->list); + } + /* Add the vif to the off_chanctx */ + list_add_tail(&uvif->list, &off_chanctx->vifs); + off_chanctx->nvifs++; + rcu_assign_pointer(dev->off_chanctx[off_chanctx_id], + off_chanctx); + synchronize_rcu(); - DEBUG_LOG("%s:%d Programming the Channel Success:%d\n", - __func__, __LINE__, - dev->chan_prog_done); - /* Inform FW that ROC is started */ - uccp420wlan_prog_roc(ROC_START, pri_chnl_num, duration); + /* Move the channel context */ + spin_lock_bh(&dev->chanctx_lock); + dev->curr_chanctx_idx = off_chanctx->index; + spin_unlock_bh(&dev->chanctx_lock); + } else { + /* Same channel context, just update off_chanctx + * to chanctx + */ + off_chanctx = uvif->chanctx; - ieee80211_queue_delayed_work(hw, - &dev->roc_complete_work, - msecs_to_jiffies(duration)); + for (i = 0; i < MAX_OFF_CHANCTX; i++) { + if (!dev->off_chanctx[i]) { + off_chanctx_id = i; + break; + } + } + dev->roc_off_chanctx_idx = off_chanctx->index; + rcu_assign_pointer(dev->off_chanctx[off_chanctx_id], + off_chanctx); + synchronize_rcu(); + } + spin_lock_irqsave(&tx->lock, flags); + uvif->off_chanctx = off_chanctx; + spin_unlock_irqrestore(&tx->lock, flags); - dev->roc_params.roc_in_progress = 1; + uccp420wlan_prog_roc(ROC_START, pri_chnl_num, duration, type); - ieee80211_ready_on_channel(dev->hw); + if (uvif->chanctx) + ieee80211_wake_queues(hw); mutex_unlock(&dev->mutex); @@ -1467,34 +1560,34 @@ static int remain_on_channel(struct ieee80211_hw *hw, static int cancel_remain_on_channel(struct ieee80211_hw *hw) { - int i = 0; struct mac80211_dev *dev = (struct mac80211_dev *)hw->priv; + int ret = 0; mutex_lock(&dev->mutex); if (dev->roc_params.roc_in_progress) { - cancel_delayed_work_sync(&dev->roc_complete_work); - - /* Put the chip back to its original state */ - for (i = 0; i < MAX_VIFS; i++) { - if (!(dev->active_vifs & (1 << i))) - continue; + dev->cancel_hw_roc_done = 0; + dev->cancel_roc = 1; + DEBUG_LOG("%s-80211IF: Cancelling HW ROC....\n", dev->name); - uccp420wlan_prog_ps_state(i, - dev->if_mac_addresses[i].addr, - dev->power_save); - } + uccp420wlan_prog_roc(ROC_STOP, 0, 0, 0); - DEBUG_LOG("%s:%d Coming back to Orig:%d\n", - __func__, __LINE__, - dev->power_save); + mutex_unlock(&dev->mutex); - dev->roc_params.roc_in_progress = 0; + if (!wait_for_cancel_hw_roc(dev)) { + DEBUG_LOG("%s-80211IF: Cancel HW ROC....done\n", + dev->name); + ret = 0; + } else { + DEBUG_LOG("%s-80211IF: Cancel HW ROC..timedout\n", + dev->name); + ret = -1; + } + } else { + mutex_unlock(&dev->mutex); } - mutex_unlock(&dev->mutex); - - return 0; + return ret; } @@ -1563,7 +1656,7 @@ static int img_resume(struct ieee80211_hw *hw) if (uccp420wlan_prog_econ_ps_state(active_vif_index, PWRSAVE_STATE_AWAKE)) { - pr_err(" %s : Error Occured\n", + pr_err("%s : Error Occured\n", __func__); mutex_unlock(&dev->mutex); return -1; @@ -1576,7 +1669,7 @@ static int img_resume(struct ieee80211_hw *hw) dev->power_save = PWRSAVE_STATE_AWAKE; pr_debug("%s: Successful\n", __func__); - + hal_ops.disable_irq_wake(); return 0; } } @@ -1617,7 +1710,7 @@ static int img_suspend(struct ieee80211_hw *hw, } if (count != 1) { - pr_err("%s: Economy mode supported only for single VIF in STA mode\n", + pr_err("%s: Economy mode supp only for single VIF(STA mode)\n", __func__); mutex_unlock(&dev->mutex); return -1; @@ -1650,6 +1743,7 @@ static int img_suspend(struct ieee80211_hw *hw, dev->power_save = PWRSAVE_STATE_DOZE; pr_debug("%s: Successful\n", __func__); + hal_ops.enable_irq_wake(); return 0; } } @@ -1731,6 +1825,9 @@ void uccp420wlan_scan_complete(void *context, unsigned int len) { struct mac80211_dev *dev = (struct mac80211_dev *)context; + int i = 0; + struct ieee80211_vif *vif = NULL; + const char ra[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; /* DO NOT update the scan results through cfg80211 API's we just pass * the beacons and probe responses up and mac80211 will inform cfg80211 @@ -1750,6 +1847,25 @@ void uccp420wlan_scan_complete(void *context, dev->stats->umac_scan_complete++; ieee80211_scan_completed(dev->hw, false); + /* WAR for TT_PRB0164. To be removed after patch + * submitted to kernel + */ + for (i = 0; i < MAX_VIFS; i++) { + + if (!(dev->active_vifs & (1 << i))) + continue; + + rcu_read_lock(); + vif = rcu_dereference(dev->vifs[i]); + rcu_read_unlock(); + + if (vif->type != NL80211_IFTYPE_AP) + continue; + + ieee80211_stop_tx_ba_cb_irqsafe(vif, + ra, IEEE80211_NUM_TIDS); + } + /* Keep track of HW Scan requests and compeltes */ wifi->params.hw_scan_status = HW_SCAN_STATUS_NONE; } @@ -1987,8 +2103,8 @@ static int add_chanctx(struct ieee80211_hw *hw, } DEBUG_LOG("%s: %d MHz\n", - __func__, - conf->def.chan->center_freq); + __func__, + conf->def.chan->center_freq); mutex_lock(&dev->mutex); @@ -2117,20 +2233,37 @@ static void unassign_vif_chanctx(struct ieee80211_hw *hw, struct mac80211_dev *dev = NULL; struct umac_vif *uvif = NULL; struct umac_chanctx *ctx = NULL; + u32 hw_queue_map = 0; + int i = 0; dev = hw->priv; uvif = (struct umac_vif *)vif->drv_priv; ctx = (struct umac_chanctx *)conf->drv_priv; DEBUG_LOG("%s: addr: %pM, type: %d, p2p: %d chan: %d MHz\n", - __func__, - vif->addr, - vif->type, - vif->p2p, - conf->def.chan->center_freq); + __func__, + vif->addr, + vif->type, + vif->p2p, + conf->def.chan->center_freq); mutex_lock(&dev->mutex); + /* We need to specifically handle flushing tx queues for the AP VIF + * here (for STA VIF, mac80211 handles this via flush_queues) + */ + if (vif->type == NL80211_IFTYPE_AP) { + /* Flush all queues for this VIF */ + for (i = 0; i < NUM_ACS; i++) + hw_queue_map |= BIT(i); + + uccp420_flush_vif_queues(dev, + uvif, + uvif->chanctx->index, + hw_queue_map, + UMAC_VIF_CHANCTX_TYPE_OPER); + } + uvif->chanctx = NULL; list_del(&uvif->list); @@ -2154,25 +2287,13 @@ static void flush_queues(struct ieee80211_hw *hw, { struct mac80211_dev *dev = NULL; struct umac_vif *uvif = NULL; - struct umac_chanctx *ctx = NULL; - unsigned int chan_ctx_id = 0; - unsigned int queue = 0; - unsigned int pending = 0; - int count = 0; - int peer_id = -1; + u32 hw_queue_map = 0; int i = 0; - unsigned long flags = 0; - struct sk_buff_head *pend_pkt_q = NULL; - struct tx_config *tx = NULL; - struct ieee80211_sta *sta = NULL; - struct umac_sta *usta = NULL; dev = hw->priv; mutex_lock(&dev->mutex); - tx = &dev->tx; - if (!vif) goto out; @@ -2181,75 +2302,21 @@ static void flush_queues(struct ieee80211_hw *hw, if (!uvif->chanctx) goto out; - if (dev->num_active_chanctx != 2) { - DEBUG_LOG("%s-80211IF: Flush is only supported for TSMC case\n", - __func__); - goto out; - } - - ctx = uvif->chanctx; - chan_ctx_id = ctx->index; - - for (queue = 0; queue < WLAN_AC_MAX_CNT; queue++) { - if (!((1 << queue) & queues)) - continue; - -check_tokens_flush_complete: - pending = 0; - - spin_lock_irqsave(&tx->lock, flags); - rcu_read_lock(); - - for (i = 0; i < MAX_PEND_Q_PER_AC; i++) { - if (i < MAX_PEERS) { - sta = rcu_dereference(dev->peers[i]); - - if (!sta) - continue; - - usta = (struct umac_sta *)(sta->drv_priv); - - if (usta->vif_index == uvif->vif_index) - peer_id = i; - else - continue; - } else if (i == uvif->vif_index) { - peer_id = uvif->vif_index; - } else - continue; - - pend_pkt_q = &tx->pending_pkt[peer_id][queue]; - - /* Assuming all packets for the peer have same channel - * context - */ - pending = skb_queue_len(pend_pkt_q); - } - - rcu_read_unlock(); - spin_unlock_irqrestore(&tx->lock, flags); - - if (pending && (count < QUEUE_FLUSH_TIMEOUT_TICKS)) { - current->state = TASK_INTERRUPTIBLE; - - if (0 == schedule_timeout(1)) - count++; - - goto check_tokens_flush_complete; - } - - if (pending) - DEBUG_LOG("%s: failed for VIF: %d and Queue: %d, pending: %d\n", - __func__, - uvif->vif_index, - queue, - pending); - else - DEBUG_LOG("%s: Flush for VIF: %d and Queue: %d success\n", - __func__, - uvif->vif_index, - queue); + /* Convert the mac80211 queue map to our hw queue map */ + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + if (queues & BIT(i)) + hw_queue_map |= BIT(tx_queue_map(i)); } + /* This op should not get called during ROC operation, so we can assume + * that the vif_chanctx_type will be UMAC_VIF_CHANCTX_TYPE_OPER. As for + * TSMC operation the VIF can only be associated to one channel context, + * so we pass uvif->chanctx->index as the parameter for chanctx_idx + */ + uccp420_flush_vif_queues(dev, + uvif, + uvif->chanctx->index, + hw_queue_map, + UMAC_VIF_CHANCTX_TYPE_OPER); out: mutex_unlock(&dev->mutex); @@ -2305,11 +2372,13 @@ static void uccp420wlan_exit(void) /* DEV Release */ struct mac80211_dev *dev = (struct mac80211_dev *)wifi->hw->priv; - ieee80211_unregister_hw(wifi->hw); - device_release_driver(dev->dev); - device_destroy(hwsim_class, 0); - ieee80211_free_hw(wifi->hw); - wifi->hw = NULL; + if (wifi->hw) { + ieee80211_unregister_hw(wifi->hw); + device_release_driver(dev->dev); + device_destroy(hwsim_class, 0); + ieee80211_free_hw(wifi->hw); + wifi->hw = NULL; + } class_destroy(hwsim_class); } @@ -2331,6 +2400,7 @@ static int uccp420wlan_init(void) } dev = (struct mac80211_dev *)hw->priv; + memset(dev, 0, sizeof(struct mac80211_dev)); hwsim_class = class_create(THIS_MODULE, "uccp420"); @@ -2374,6 +2444,7 @@ static int uccp420wlan_init(void) spin_lock_init(&dev->chanctx_lock); #endif + spin_lock_init(&dev->roc_lock); dev->state = STOPPED; dev->active_vifs = 0; dev->txpower = DEFAULT_TX_POWER; @@ -2391,6 +2462,7 @@ static int uccp420wlan_init(void) dev->params = &wifi->params; dev->stats = &wifi->stats; dev->umac_proc_dir_entry = wifi->umac_proc_dir_entry; + dev->current_vif_count = 0; dev->stats->system_rev = system_rev; #ifdef MULTI_CHAN_SUPPORT dev->num_active_chanctx = 0; @@ -2462,6 +2534,7 @@ static int proc_read_config(struct seq_file *m, void *v) seq_puts(m, "\n"); seq_printf(m, "production_test = %d\n", wifi->params.production_test); + seq_printf(m, "bypass_vpd = %d\n", wifi->params.bypass_vpd); seq_printf(m, "tx_fixed_mcs_indx = %d (%s)\n", wifi->params.tx_fixed_mcs_indx, (wifi->params.prod_mode_rate_flag & @@ -2511,6 +2584,15 @@ static int proc_read_config(struct seq_file *m, void *v) seq_printf(m, "num_vifs = %d\n", wifi->params.num_vifs); + seq_puts(m, "vif_macs ="); + for (i = 0; i < wifi->params.num_vifs; i++) { + seq_printf(m, " %02x:%02x:%02x:%02x:%02x:%02x", + vif_macs[i][0], vif_macs[i][1], vif_macs[i][2], + vif_macs[i][3], vif_macs[i][4], vif_macs[i][5] + ); + } + seq_puts(m, "\n"); + seq_printf(m, "chnl_bw = %d\n", wifi->params.chnl_bw); @@ -2588,34 +2670,44 @@ static int proc_read_config(struct seq_file *m, void *v) seq_printf(m, "bt_state = %d\n", wifi->params.bt_state); /* Beacon Time Stamp */ - for (cnt = 0; cnt < MAX_VIFS; cnt++) { - unsigned long long ts1; - unsigned long long bssid, atu; - int status; - char dev_name[10]; - unsigned int t2; - - spin_lock_bh(&tsf_lock); - ts1 = get_unaligned_le64(wifi->params.sync[cnt].ts1); - bssid = get_unaligned_le64(wifi->params.sync[cnt].bssid); - status = wifi->params.sync[cnt].status; - sprintf(dev_name, "%s%d", "wlan", cnt); - atu = wifi->params.sync[cnt].atu; - t2 = wifi->params.sync[cnt].ts2; - spin_unlock_bh(&tsf_lock); - if (status && wifi->params.sync[cnt].name) - seq_printf(m, "sync=%s %d %llu %llu %llx t2=%u\n", - dev_name, status, (unsigned long long)ts1, - atu, (unsigned long long) bssid, t2); + if (dev->state == STARTED) { + for (cnt = 0; cnt < MAX_VIFS; cnt++) { + unsigned long long ts1; + unsigned long long bssid, atu; + int status; + char dev_name[10]; + unsigned int t2; + + spin_lock_bh(&tsf_lock); + ts1 = get_unaligned_le64(wifi->params.sync[cnt].ts1); + bssid = + get_unaligned_le64(wifi->params.sync[cnt].bssid); + status = wifi->params.sync[cnt].status; + sprintf(dev_name, "%s%d", "wlan", cnt); + atu = wifi->params.sync[cnt].atu; + t2 = wifi->params.sync[cnt].ts2; + spin_unlock_bh(&tsf_lock); + if (status && wifi->params.sync[cnt].name) + seq_printf(m, + "sync=%s %d %llu %llu %llx t2=%u\n", + dev_name, + status, + (unsigned long long)ts1, + atu, + (unsigned long long)bssid, + t2); + } } seq_puts(m, "****** Production Test (or) FTM Parameters *******\n"); - seq_printf(m, "pkt_gen_val = %d (-1: Infinite loop)\n", + seq_printf(m, "start_packet_gen = %d (-1: Infinite loop)\n", wifi->params.pkt_gen_val); seq_printf(m, "payload_length = %d bytes\n", wifi->params.payload_length); seq_printf(m, "start_prod_mode = channel: %d\n", wifi->params.start_prod_mode); + seq_printf(m, "continuous_tx = %d\n", + wifi->params.cont_tx); if (ftm || wifi->params.production_test) seq_printf(m, "set_tx_power = %d dB\n", @@ -2915,6 +3007,14 @@ static int proc_read_mac_stats(struct seq_file *m, void *v) total_rssi_samples); seq_puts(m, "************* LMAC STATS ***********\n"); + seq_printf(m, "roc_start =%d\n", + wifi->stats.roc_start); + seq_printf(m, "roc_stop =%d\n", + wifi->stats.roc_stop); + seq_printf(m, "roc_complete =%d\n", + wifi->stats.roc_complete); + seq_printf(m, "roc_stop_complete =%d\n", + wifi->stats.roc_stop_complete); /* TX related */ seq_printf(m, "tx_cmd_cnt =%d\n", wifi->stats.tx_cmd_cnt); @@ -3023,7 +3123,14 @@ static long param_get_match(unsigned char *buf, unsigned char *str) else return 0; } +void uccp420wlan_reinit(void) +{ + if (wifi->hw) + uccp420wlan_exit(); + uccp420wlan_init(); + uccp_reinit = 1; +} static ssize_t proc_write_config(struct file *file, const char __user *buffer, size_t count, @@ -3032,6 +3139,10 @@ static ssize_t proc_write_config(struct file *file, char buf[(RF_PARAMS_SIZE * 2) + 50]; unsigned long val; long sval; + unsigned int rate = wifi->params.prod_mode_rate_flag; + unsigned int b40 = wifi->params.prod_mode_chnl_bw_40_mhz; + unsigned int b80 = wifi->params.prod_mode_chnl_bw_80_mhz; + struct mac80211_dev *dev = wifi->hw->priv; if (count >= sizeof(buf)) count = sizeof(buf) - 1; @@ -3050,18 +3161,13 @@ static ssize_t proc_write_config(struct file *file, (wifi->params.dot11a_support == 0)) { pr_err("Invalid parameter value. Both bands can't be disabled, at least 1 is needed\n"); } else { - if (wifi->hw) { - uccp420wlan_exit(); - wifi->hw = NULL; - } - - pr_info("Re-initializing UMAC ..with 2.4GHz support %s and 5GHz support %s\n", + uccp420wlan_reinit(); + pr_info("Re-initializing UMAC ..with 2.4GHz support %s and 5GHz support %s\n", wifi->params.dot11g_support == 0 ? "disabled" : "enabled", wifi->params.dot11a_support == 0 ? "disabled" : "enabled"); - uccp420wlan_init(); } } else pr_err("Invalid parameter value\n"); @@ -3074,18 +3180,13 @@ static ssize_t proc_write_config(struct file *file, (wifi->params.dot11a_support == 0)) { pr_err("Invalid parameter value. Both bands can't be disabled, at least 1 is needed\n"); } else { - if (wifi->hw) { - uccp420wlan_exit(); - wifi->hw = NULL; - } - - pr_info("Re-initializing UMAC ..with 2.4GHz support %s and 5GHz support %s\n", + uccp420wlan_reinit(); + pr_info("Re-initializing UMAC ..with 2.4GHz support %s and 5GHz support %s\n", wifi->params.dot11g_support == 0 ? "disabled" : "enabled", wifi->params.dot11a_support == 0 ? "disabled" : "enabled"); - uccp420wlan_init(); } } else pr_err("Invalid parameter value\n"); @@ -3107,28 +3208,23 @@ static ssize_t proc_write_config(struct file *file, wifi->params.production_test = val; - if (wifi->hw) { - uccp420wlan_exit(); - wifi->hw = NULL; - } - + uccp420wlan_reinit(); pr_err("Re-initializing UMAC ..\n"); - uccp420wlan_init(); } } else pr_err("Invalid parameter value\n"); + } else if (param_get_val(buf, "bypass_vpd=", &val)) { + if ((val == 0) || (val == 1)) { + if (wifi->params.bypass_vpd != val) + wifi->params.bypass_vpd = val; + } else + pr_err("Invalid parameter value\n"); } else if (param_get_val(buf, "num_vifs=", &val)) { if (val > 0 && val <= MAX_VIFS) { if (wifi->params.num_vifs != val) { - if (wifi->hw) { - uccp420wlan_exit(); - wifi->hw = NULL; - } - + uccp420wlan_reinit(); pr_err("Re-initializing UMAC ..\n"); wifi->params.num_vifs = val; - - uccp420wlan_init(); } } } else if (param_get_match(buf, "rf_params=")) { @@ -3142,6 +3238,10 @@ static ssize_t proc_write_config(struct file *file, } else if (param_get_val(buf, "pdout_val=", &val)) { wifi->stats.pdout_val = val; } else if (param_get_val(buf, "get_stats=", &val)) { + if (dev->state != STARTED) { + pr_err("Interface is not initialized\n"); + goto error; + } uccp420wlan_prog_mib_stats(); } else if (param_get_val(buf, "max_data_size=", &val)) { if (wifi->params.max_data_size != val) { @@ -3149,15 +3249,10 @@ static ssize_t proc_write_config(struct file *file, (wifi->params.max_data_size <= (12 * 1024))) { wifi->params.max_data_size = val; - if (wifi->hw) { - uccp420wlan_exit(); - wifi->hw = NULL; - } - + uccp420wlan_reinit(); pr_err("Re-initalizing UCCP420 with %ld as max data size\n", val); - uccp420wlan_init(); } else pr_err("Invalid Value for max data size: should be (2K-12K)\n"); } @@ -3169,15 +3264,9 @@ static ssize_t proc_write_config(struct file *file, if (val != wifi->params.disable_power_save) { wifi->params.disable_power_save = val; - if (wifi->hw) { - uccp420wlan_exit(); - wifi->hw = NULL; - } - + uccp420wlan_reinit(); pr_err("Re-initalizing UCCP420 with global powerave %s\n", val ? "DISABLED" : "ENABLED"); - - uccp420wlan_init(); } } } else if (param_get_val(buf, "disable_sm_power_save=", &val)) { @@ -3185,15 +3274,10 @@ static ssize_t proc_write_config(struct file *file, if (val != wifi->params.disable_sm_power_save) { wifi->params.disable_sm_power_save = val; - if (wifi->hw) { - uccp420wlan_exit(); - wifi->hw = NULL; - } - + uccp420wlan_reinit(); pr_err("Re-initalizing UCCP420 with smps %s\n", val ? "DISABLED" : "ENABLED"); - uccp420wlan_init(); } } } else if (param_get_val(buf, "uccp_num_spatial_streams=", &val)) { @@ -3203,13 +3287,9 @@ static ssize_t proc_write_config(struct file *file, wifi->params.num_spatial_streams = val; wifi->params.max_tx_streams = val; wifi->params.max_rx_streams = val; - if (wifi->hw) { - uccp420wlan_exit(); - wifi->hw = NULL; - } + uccp420wlan_reinit(); pr_err("Re-initalizing UCCP420 with %ld spatial streams\n", val); - uccp420wlan_init(); } } else pr_err("Invalid parameter value: Allowed Range: 1 to %d\n", @@ -3218,13 +3298,9 @@ static ssize_t proc_write_config(struct file *file, if (val == 1 || val == 2) { if (val != wifi->params.antenna_sel) { wifi->params.antenna_sel = val; - if (wifi->hw) { - uccp420wlan_exit(); - wifi->hw = NULL; - } + uccp420wlan_reinit(); pr_err("Re-initalizing UCCP420 with %ld antenna selection\n", val); - uccp420wlan_init(); } } else pr_err("Invalid parameter value: Allowed Values: 1 or 2\n"); @@ -3295,89 +3371,106 @@ static ssize_t proc_write_config(struct file *file, } else pr_err("MCS data rate(index) is currently set\n"); } else if (param_get_sval(buf, "tx_fixed_mcs_indx=", &sval)) { + if (wifi->params.production_test != 1) { + pr_err("Only can be set in production mode.\n"); + goto error; + } - do { - if (wifi->params.production_test != 1) { - pr_err("Only can be set in production mode\n"); - break; - } - - if ((wifi->params.num_spatial_streams == 2) && - (sval >= -1) && (sval <= 15)) - wifi->params.tx_fixed_mcs_indx = sval; - else - pr_err("Invalid MIMO HT MCS: %ld\n", sval); - - if ((wifi->params.num_spatial_streams == 1) && - (sval >= -1) && (sval <= 7)) - wifi->params.tx_fixed_mcs_indx = sval; - else - pr_err("Invalid SISO HT MCS: %ld\n", sval); - - } while (0); - - if (wifi->params.production_test == 1 && - wifi->params.tx_fixed_rate == -1 && - vht_support && (wifi->params.prod_mode_rate_flag & - ENABLE_VHT_FORMAT)) { + if (sval == -1) { + wifi->params.tx_fixed_mcs_indx = -1; + goto error; + } - if (!((sval >= -1) && (sval <= 9))) + if (wifi->params.tx_fixed_rate != -1) { + pr_err("Fixed rate other than MCS index is currently set\n"); + goto error; + } + if (vht_support && (rate & ENABLE_VHT_FORMAT)) { + if ((sval >= -1) && (sval <= 9)) { + if ((b40 == 0) && (b80 == 0) && (sval == 9)) { + pr_err("Invalid VHT MCS: 20MHZ-MCS9.\n"); + /*Reset to Default*/ + wifi->params.tx_fixed_mcs_indx = 7; + } else + wifi->params.tx_fixed_mcs_indx = sval; + } else pr_err("Invalid parameter value.\n"); - - if ((sval >= -1) && (sval <= 9)) - wifi->params.tx_fixed_mcs_indx = sval; - - if ((wifi->params.prod_mode_chnl_bw_40_mhz == 0) && - (wifi->params.prod_mode_chnl_bw_80_mhz == 0) && - (sval == 9)) { - pr_err("Invalid VHT MCS: 20MHZ-MCS9.\n"); - - /*Reset to Default*/ - wifi->params.tx_fixed_mcs_indx = 7; + } else if (vht_support && (rate & ENABLE_11N_FORMAT)) { + if (wifi->params.num_spatial_streams == 2) { + if ((sval >= -1) && (sval <= 15)) + wifi->params.tx_fixed_mcs_indx = sval; + else + pr_err("Invalid MIMO HT MCS: %ld\n", + sval); + } else if (wifi->params.num_spatial_streams == 1) { + if ((sval >= -1) && (sval <= 7)) + wifi->params.tx_fixed_mcs_indx = sval; + else + pr_err("Invalid SISO HT MCS: %ld\n", + sval); } - } + } else + pr_err("MCS Setting is invalid for Legacy, please set prod_mode_rate_flag first.\n"); } else if (param_get_sval(buf, "tx_fixed_rate=", &sval)) { - if (wifi->params.production_test == 1) { - if (wifi->params.tx_fixed_mcs_indx == -1) { - if ((wifi->params.dot11g_support == 1) && - ((sval == 1) || - (sval == 2) || - (sval == 55) || - (sval == 11))) { - wifi->params.tx_fixed_rate = sval; - } else if ((sval == 6) || - (sval == 9) || - (sval == 12) || - (sval == 18) || - (sval == 24) || - (sval == 36) || - (sval == 48) || - (sval == 54) || - (sval == -1)) { - wifi->params.tx_fixed_rate = sval; - } else { - pr_err("Invalid parameter value.\n"); - return count; - } - } else - pr_err("MCS data rate(index) is currently set\n"); - } else + if (wifi->params.production_test != 1) { pr_err("Only can be set in production mode.\n"); + goto error; + } + + if (sval == -1) { + wifi->params.tx_fixed_rate = -1; + goto error; + } + if (wifi->params.tx_fixed_mcs_indx != -1) { + pr_err("MCS Index is currently set.\n"); + goto error; + } + + if ((wifi->params.dot11g_support == 1) && + ((sval == 1) || + (sval == 2) || + (sval == 55) || + (sval == 11))) { + wifi->params.tx_fixed_rate = sval; + } else if ((sval == 6) || + (sval == 9) || + (sval == 12) || + (sval == 18) || + (sval == 24) || + (sval == 36) || + (sval == 48) || + (sval == 54) || + (sval == -1)) { + wifi->params.tx_fixed_rate = sval; + } else { + pr_err("Invalid parameter value: tx_fixed_rate=%ld\n", + sval); + goto error; + } } else if (param_get_val(buf, "chnl_bw=", &val)) { if (((val == 0) || (vht_support && (val == 2)) || (val == 1))) { wifi->params.chnl_bw = val; - if (wifi->hw) { - uccp420wlan_exit(); - wifi->hw = NULL; + uccp420wlan_reinit(); + pr_err("Re-initializing UMAC ..\n"); + } else + pr_err("Invalid parameter value.\n"); + } else if (param_get_match(buf, "vif_macs=")) { + char *macdata = strstr(buf, "=") + 1; + int dataok = 1; + int i; + for (i = 0; i < wifi->params.num_vifs; i++, macdata += ETH_ALEN*2) { + if (conv_str_to_byte(vif_macs[i], macdata, ETH_ALEN) != 0) { + dataok = 0; + break; } - + } + if (dataok) { + uccp420wlan_reinit(); pr_err("Re-initializing UMAC ..\n"); - - uccp420wlan_init(); } else pr_err("Invalid parameter value.\n"); } else if (param_get_val(buf, "prod_mode_chnl_bw_40_mhz=", &val)) { @@ -3418,6 +3511,11 @@ static ssize_t proc_write_config(struct file *file, break; } + if (val == 0) { + wifi->params.sec_ch_offset_40_plus = 0; + goto error; + } + if (!((wifi->params.prod_mode_chnl_bw_40_mhz == 1) || (vht_support && (wifi->params.prod_mode_chnl_bw_80_mhz == 1)) @@ -3448,6 +3546,11 @@ static ssize_t proc_write_config(struct file *file, break; } + if (val == 0) { + wifi->params.sec_ch_offset_40_minus = 0; + goto error; + } + if (!((wifi->params.prod_mode_chnl_bw_40_mhz == 1) || (vht_support && (wifi->params.prod_mode_chnl_bw_80_mhz == 1)) @@ -3479,8 +3582,13 @@ static ssize_t proc_write_config(struct file *file, break; } + if (val == 0) { + wifi->params.sec_40_ch_offset_80_plus = 0; + goto error; + } + if (!(wifi->params.prod_mode_chnl_bw_80_mhz == 1)) { - pr_err("Can be set if prod_mode_chnl_bw_80_mhz is set\n"); + pr_err("Can be set only when prod_mode_chnl_bw_80_mhz is set\n"); break; } @@ -3507,6 +3615,10 @@ static ssize_t proc_write_config(struct file *file, break; } + if (val == 0) { + wifi->params.sec_40_ch_offset_80_minus = 0; + goto error; + } if (!(wifi->params.prod_mode_chnl_bw_80_mhz == 1)) { pr_err("Can be set if prod_mode_chnl_bw_80_mhz is set\n"); break; @@ -3577,8 +3689,7 @@ static ssize_t proc_write_config(struct file *file, else pr_err("Invalid parameter value\n"); } else if (param_get_val(buf, "reset_hal_params=", &val)) { - if (((struct mac80211_dev *) - (wifi->hw->priv))->state != STARTED) { + if (dev->state != STARTED) { if (val != 1) pr_err("Invalid parameter value\n"); else @@ -3608,6 +3719,11 @@ static ssize_t proc_write_config(struct file *file, vht_beamform_period = wifi->params.vht_beamform_period; + if (dev->state != STARTED) { + pr_err("Interface is not initialized\n"); + goto error; + } + uccp420wlan_prog_vht_bform(val, vht_beamform_period); } while (0); @@ -3640,6 +3756,11 @@ static ssize_t proc_write_config(struct file *file, vht_beamform_enable = wifi->params.vht_beamform_period; + if (dev->state != STARTED) { + pr_err("Interface is not initialized\n"); + goto error; + } + uccp420wlan_prog_vht_bform(vht_beamform_enable, val); } while (0); @@ -3647,12 +3768,9 @@ static ssize_t proc_write_config(struct file *file, if (wifi->params.bg_scan_enable != val) { if ((val == 1) || (val == 0)) { wifi->params.bg_scan_enable = val; - if (wifi->hw) { - uccp420wlan_exit(); - wifi->hw = NULL; - } + + uccp420wlan_reinit(); pr_err("Re-initializing UMAC ..\n"); - uccp420wlan_init(); } else pr_err("Invalid bg_scan_enable value should be 1 or 0\n"); } @@ -3685,6 +3803,12 @@ static ssize_t proc_write_config(struct file *file, } else if (param_get_val(buf, "bg_scan_num_channels=", &val)) { wifi->params.bg_scan_num_channels = val; } else if (param_get_val(buf, "nw_selection=", &val)) { + + if (dev->state != STARTED) { + pr_err("Interface is not initialized\n"); + goto error; + } + if ((val == 1) || (val == 0)) { wifi->params.nw_selection = val; pr_err("in nw_selection\n"); @@ -3698,6 +3822,12 @@ static ssize_t proc_write_config(struct file *file, pr_err("Invalid scan type value %d, should be 0 or 1\n", (unsigned int)val); } else if (ftm && param_get_val(buf, "aux_adc_chain_id=", &val)) { + + if (dev->state != STARTED) { + pr_err("Interface is not initialized\n"); + goto error; + } + memset(wifi->params.pdout_voltage, 0, sizeof(char) * MAX_AUX_ADC_SAMPLES); if ((val == AUX_ADC_CHAIN1) || (val == AUX_ADC_CHAIN2)) { @@ -3708,12 +3838,36 @@ static ssize_t proc_write_config(struct file *file, (unsigned int) val, AUX_ADC_CHAIN1, AUX_ADC_CHAIN2); - } else if ((wifi->params.production_test) && - param_get_val(buf, "start_prod_mode=", &val)) { + } else if (param_get_val(buf, "continuous_tx=", &val)) { + if (wifi->params.production_test != 1) { + pr_err("continuous_tx: Can be set in only in production mode.\n"); + goto error; + } + + if (dev->state != STARTED) { + pr_err("Interface is not initialized\n"); + goto error; + } + + if (val == 0 || val == 1) { + wifi->params.cont_tx = val; + uccp420wlan_prog_cont_tx(val); + } else + pr_err("Invalid tx_continuous parameter\n"); + } else if (param_get_val(buf, "start_prod_mode=", &val)) { unsigned int pri_chnl_num = 0; unsigned int freq_band = IEEE80211_BAND_5GHZ; int center_freq = 0; - struct mac80211_dev *dev = wifi->hw->priv; + + if (wifi->params.production_test != 1) { + pr_err("start_prod_mode: Can be set in only in production mode.\n"); + goto error; + } + + if (wifi->params.init_prod) { + pr_err("Production Test is already initialized.\n"); + goto error; + } pri_chnl_num = val; wifi->params.start_prod_mode = val; @@ -3733,12 +3887,14 @@ static ssize_t proc_write_config(struct file *file, pr_err("%s: Firmware loading failed\n", dev->name); goto error; - } + } + if (!uccp420wlan_core_init(dev, ftm)) { uccp420wlan_prog_vif_ctrl(0, dev->if_mac_addresses[0].addr, IF_MODE_STA_IBSS, IF_ADD); + proc_bss_info_changed( dev->if_mac_addresses[0].addr, val); @@ -3754,14 +3910,19 @@ static ssize_t proc_write_config(struct file *file, freq_band); skb_queue_head_init(&dev->tx.proc_tx_list[0]); wifi->params.init_prod = 1; + dev->state = STARTED; + uccp_reinit = 0; } else { - pr_err("LMAC Initialization Failed\n"); + pr_err("RPU Initialization Failed\n"); wifi->params.init_prod = 0; } - } else if ((wifi->params.production_test) && (wifi->params.init_prod) - && param_get_sval(buf, "stop_prod_mode=", &sval)) { - struct mac80211_dev *dev = wifi->hw->priv; + } else if (param_get_sval(buf, "stop_prod_mode=", &sval)) { + + if (!wifi->params.init_prod) { + DEBUG_LOG("Prod mode is not initialized\n"); + goto error; + } tasklet_kill(&dev->proc_tx_tasklet); #if 0 @@ -3773,30 +3934,61 @@ static ssize_t proc_write_config(struct file *file, IF_MODE_STA_IBSS, IF_REM); #endif - uccp420wlan_core_deinit(dev, 0); + if (!uccp_reinit) + stop(wifi->hw); + wifi->params.start_prod_mode = 0; wifi->params.pkt_gen_val = 1; - hal_ops.reset_hal_params(); wifi->params.init_prod = 0; - } else if ((wifi->params.production_test) && (wifi->params.init_prod) - && param_get_sval(buf, "start_packet_gen=", &sval)) { - struct mac80211_dev *dev = wifi->hw->priv; + wifi->params.init_pkt_gen = 0; + } else if (param_get_sval(buf, "start_packet_gen=", &sval)) { + + + if (!wifi->params.init_prod) { + pr_err("NEW Production Mode is not Initialized\n"); + goto error; + } + + if (wifi->params.init_pkt_gen) { + pr_err("packet gen is already running\n"); + goto error; + } + + if (wifi->params.tx_fixed_mcs_indx == -1 && + wifi->params.tx_fixed_rate == -1) { + pr_err("Either tx_fixed_mcs_index Or tx_fixed_rate should be set, both can't be NULL.\n"); + goto error; + } + + wifi->params.init_pkt_gen = 1; wifi->params.pkt_gen_val = sval; if (sval != 0) tasklet_schedule(&dev->proc_tx_tasklet); - } else if ((wifi->params.production_test) && (wifi->params.init_prod) - && param_get_sval(buf, "stop_packet_gen=", &sval)) { - struct mac80211_dev *dev = wifi->hw->priv; + } else if (param_get_sval(buf, "stop_packet_gen=", &sval)) { + + if (!wifi->params.init_prod) { + DEBUG_LOG("NEW Production Mode is not Initialized\n"); + goto error; + } + + wifi->params.pkt_gen_val = 1; + wifi->params.init_pkt_gen = 0; + tasklet_kill(&dev->proc_tx_tasklet); + } else if (param_get_val(buf, "payload_length=", &val)) { + wifi->params.payload_length = val; + } else if (param_get_sval(buf, "set_tx_power=", &sval)) { + if (wifi->params.production_test != 1 && !ftm) { + pr_err("set_tx_power: Can be set in only in FTM/production mode.\n"); + goto error; + } + + if (!wifi->params.init_prod) { + DEBUG_LOG("NEW Production Mode is not Initialized\n"); + goto error; + } - wifi->params.pkt_gen_val = 1; - tasklet_kill(&dev->proc_tx_tasklet); - } else if ((wifi->params.production_test) && - param_get_val(buf, "payload_length=", &val)) { - wifi->params.payload_length = val; - } else if ((ftm || wifi->params.production_test) && - param_get_sval(buf, "set_tx_power=", &sval)) { memset(wifi->params.pdout_voltage, 0, sizeof(char) * MAX_AUX_ADC_SAMPLES); wifi->params.set_tx_power = sval; @@ -3811,6 +4003,11 @@ static ssize_t proc_write_config(struct file *file, } else if (param_get_val(buf, "fw_loading=", &val)) { wifi->params.fw_loading = val; } else if (param_get_val(buf, "bt_state=", &val)) { + if (dev->state != STARTED) { + pr_err("Interface is not initialized\n"); + goto error; + } + if (val == 0 || val == 1) { if (val != wifi->params.bt_state) { wifi->params.bt_state = val; @@ -3819,6 +4016,10 @@ static ssize_t proc_write_config(struct file *file, } else pr_err("Invalid parameter value: Allowed values: 0 or 1\n"); } else if (param_get_val(buf, "clear_stats=", &val)) { + if (dev->state != STARTED) { + pr_err("Interface is not initialized\n"); + goto error; + } uccp420wlan_prog_clear_stats(); } else if (param_get_val(buf, "disable_beacon_ibss=", &val)) { if ((val == 1) || (val == 0)) @@ -3872,7 +4073,7 @@ static const struct file_operations params_fops_mac_stats = { .write = NULL, .release = single_release }; -static int proc_init(void) +static int proc_init(struct proc_dir_entry ***main_dir_entry) { struct proc_dir_entry *entry; int err = 0; @@ -4010,6 +4211,7 @@ static int proc_init(void) wifi->params.disable_beacon_ibss = 0; wifi->params.pkt_gen_val = -1; + wifi->params.init_pkt_gen = 0; wifi->params.payload_length = 4000; wifi->params.start_prod_mode = 0; wifi->params.init_prod = 0; @@ -4021,6 +4223,7 @@ static int proc_init(void) wifi->params.hw_scan_status = HW_SCAN_STATUS_NONE; wifi->params.fw_loading = 1; + **main_dir_entry = wifi->umac_proc_dir_entry; return err; proc_entry3_fail: @@ -4038,6 +4241,8 @@ out: static void proc_exit(void) { + /* This is created in hal_init */ + remove_proc_entry("hal_stats", wifi->umac_proc_dir_entry); remove_proc_entry("mac_stats", wifi->umac_proc_dir_entry); remove_proc_entry("phy_stats", wifi->umac_proc_dir_entry); remove_proc_entry("params", wifi->umac_proc_dir_entry); @@ -4046,11 +4251,11 @@ static void proc_exit(void) } -int _uccp420wlan_80211if_init(void) +int _uccp420wlan_80211if_init(struct proc_dir_entry **main_dir_entry) { int error; - error = proc_init(); + error = proc_init(&main_dir_entry); if (error) return error; @@ -4061,7 +4266,15 @@ int _uccp420wlan_80211if_init(void) void _uccp420wlan_80211if_exit(void) { + struct mac80211_dev *dev = (struct mac80211_dev *)wifi->hw->priv; + if (wifi && wifi->hw) { + /* We can safely call stop as mac80211 + * will not call stop because of new + * production mode. + */ + if (dev && wifi->params.init_prod) + stop(wifi->hw); uccp420wlan_exit(); proc_exit(); } diff --git a/drivers/net/wireless/uccp420wlan/src/core.c b/drivers/net/wireless/uccp420wlan/src/core.c index 9df0fdae2aa..6281597e823 100644 --- a/drivers/net/wireless/uccp420wlan/src/core.c +++ b/drivers/net/wireless/uccp420wlan/src/core.c @@ -117,6 +117,31 @@ check_scan_abort_complete: } +int wait_for_cancel_hw_roc(struct mac80211_dev *dev) +{ + int count = 0; + +check_cancel_hw_roc_complete: + if (!dev->cancel_hw_roc_done && (count < CANCEL_HW_ROC_TIMEOUT_TICKS)) { + current->state = TASK_INTERRUPTIBLE; + if (0 == schedule_timeout(1)) + count++; + goto check_cancel_hw_roc_complete; + } + + if (!dev->cancel_hw_roc_done) { + pr_err("%s-UMAC: Warning: Didn't get CANCEL_HW_ROC_DONE after %ld timer ticks\n", + dev->name, + CANCEL_HW_ROC_TIMEOUT_TICKS); + return -1; + } + + DEBUG_LOG("%s-UMAC: Cancel HW RoC complete after %d timer ticks\n", + dev->name, count); + + return 0; + +} int wait_for_channel_prog_complete(struct mac80211_dev *dev) { @@ -147,6 +172,38 @@ check_ch_prog_complete: } +int wait_for_tx_queue_flush_complete(struct mac80211_dev *dev, + unsigned int queue) +{ + int count = 0; + +check_tx_queue_flush_complete: + if (dev->tx.outstanding_tokens[queue] && + (count < QUEUE_FLUSH_TIMEOUT_TICKS)) { + current->state = TASK_INTERRUPTIBLE; + if (0 == schedule_timeout(1)) + count++; + goto check_tx_queue_flush_complete; + } + + if (dev->tx.outstanding_tokens[queue]) { + pr_err("%s-UMAC: Warning: Tx Queue %d flush failed pending: %d after %ld timer ticks\n", + dev->name, + queue, + dev->tx.outstanding_tokens[queue], + QUEUE_FLUSH_TIMEOUT_TICKS); + return -1; + } + + DEBUG_LOG("%s-UMAC: Flushed Tx queue %d in %d timer ticks\n", + dev->name, + queue, + count); + + return 0; + +} + int wait_for_reset_complete(struct mac80211_dev *dev) { @@ -351,7 +408,7 @@ int uccp420wlan_core_init(struct mac80211_dev *dev, unsigned int ftm) UMAC_PRINT("%s-UMAC: Reset (ENABLE)\n", dev->name); - if (hal_ops.start(dev->umac_proc_dir_entry)) + if (hal_ops.start()) goto lmac_deinit; if (ftm) uccp420wlan_prog_reset(LMAC_ENABLE, LMAC_MODE_FTM); @@ -379,7 +436,7 @@ int uccp420wlan_core_init(struct mac80211_dev *dev, unsigned int ftm) return 0; hal_stop: - hal_ops.stop(dev->umac_proc_dir_entry); + hal_ops.stop(); lmac_deinit: uccp420wlan_lmac_if_deinit(); return -1; @@ -407,7 +464,7 @@ void uccp420wlan_core_deinit(struct mac80211_dev *dev, unsigned int ftm) uccp420_lmac_if_free_outstnding(); - hal_ops.stop(dev->umac_proc_dir_entry); + hal_ops.stop(); hal_ops.deinit_bufs(); uccp420wlan_lmac_if_deinit(); @@ -880,6 +937,10 @@ void uccp420wlan_mac_stats(struct umac_event_mac_stats *mac_stats, struct mac80211_dev *dev = (struct mac80211_dev *)context; /* TX related */ + dev->stats->roc_start = mac_stats->roc_start; + dev->stats->roc_stop = mac_stats->roc_stop; + dev->stats->roc_complete = mac_stats->roc_complete; + dev->stats->roc_stop_complete = mac_stats->roc_stop_complete; dev->stats->tx_cmd_cnt = mac_stats->tx_cmd_cnt; dev->stats->tx_done_cnt = mac_stats->tx_done_cnt; dev->stats->tx_edca_trigger_cnt = mac_stats->tx_edca_trigger_cnt; @@ -1045,168 +1106,6 @@ static unsigned int get_real_ts2(unsigned int t2, unsigned int delta) #endif -#ifdef MULTI_CHAN_SUPPORT -void uccp420wlan_proc_ch_sw_event(struct umac_event_ch_switch *ch_sw_info, - void *context) -{ - struct mac80211_dev *dev = NULL; - int chan = 0; - int curr_freq = 0; - int chan_id = 0; - struct sk_buff_head *txq = NULL; - int txq_len = 0; - int i = 0; - int queue = 0; - unsigned long flags = 0; - int curr_bit = 0; - int pool_id = 0; - int ret = 0; - int peer_id = -1; - int ac = 0; - struct ieee80211_chanctx_conf *curr_chanctx = NULL; - struct tx_config *tx = NULL; - - if (!ch_sw_info || !context) { - pr_err("%s: Invalid Parameters\n", __func__); - return; - } - - dev = (struct mac80211_dev *)context; - chan = ch_sw_info->chan; - tx = &dev->tx; - - rcu_read_lock(); - - for (i = 0; i < MAX_CHANCTX; i++) { - curr_chanctx = rcu_dereference(dev->chanctx[i]); - - if (curr_chanctx) { - curr_freq = curr_chanctx->def.chan->center_freq; - - if (ieee80211_frequency_to_channel(curr_freq) == chan) { - chan_id = i; - break; - } - } - } - - rcu_read_unlock(); - - if (i == MAX_CHANCTX) { - pr_err("%s: Invalid Channel Context: chan: %d\n", - __func__, - chan); - return; - } - - - /* Switch to the new channel context */ - spin_lock(&dev->chanctx_lock); - dev->curr_chanctx_idx = chan_id; - spin_unlock(&dev->chanctx_lock); - - /* We now try to xmit any frames whose xmission got cancelled due to a - * previous channel switch - */ - for (i = 0; i < NUM_TX_DESCS; i++) { - spin_lock_irqsave(&tx->lock, flags); - - curr_bit = (i % TX_DESC_BUCKET_BOUND); - pool_id = (i / TX_DESC_BUCKET_BOUND); - - if (test_and_set_bit(curr_bit, &tx->buf_pool_bmp[pool_id])) { - spin_unlock_irqrestore(&tx->lock, flags); - continue; - } - - txq = &tx->pkt_info[chan_id][i].pkt; - txq_len = skb_queue_len(txq); - queue = tx->pkt_info[chan_id][i].queue; - - if (!txq_len) { - /* Reserved token */ - if (i < (NUM_TX_DESCS_PER_AC * NUM_ACS)) { - queue = (i % NUM_ACS); - peer_id = get_curr_peer_opp(dev, - chan_id, - queue); - - if (peer_id == -1) { - /* Mark the token as available */ - __clear_bit(curr_bit, - &tx->buf_pool_bmp[pool_id]); - - spin_unlock_irqrestore(&tx->lock, - flags); - continue; - } - - /* Spare token */ - } else { - for (ac = WLAN_AC_VO; ac >= 0; ac--) { - peer_id = get_curr_peer_opp(dev, - chan_id, - ac); - - if (peer_id != -1) { - queue = ac; - break; - } - } - - if (ac < 0) { - /* Mark the token as available */ - __clear_bit(curr_bit, - &tx->buf_pool_bmp[pool_id]); - - spin_unlock_irqrestore(&tx->lock, - flags); - continue; - } - } - - uccp420wlan_tx_proc_pend_frms(dev, - queue, - chan_id, - peer_id, - i); - - tx->outstanding_tokens[queue]++; - - } - - spin_unlock_irqrestore(&tx->lock, flags); - - ret = __uccp420wlan_tx_frame(dev, - queue, - i, - chan_id, - 0, - 0); /* TODO: Currently sending 0 - since this param is not used - as expected in the orig - code for multiple frames etc - Need to set this - properly when the orig code - logic is corrected - */ - if (ret < 0) { - /* SDK: Check if we need to clear the TX bitmap and - * desc_chan_map here - */ - pr_err("%s: Queueing of TX frame to FW failed\n", - __func__); - } else { - spin_lock_irqsave(&tx->lock, flags); - tx->desc_chan_map[i] = chan_id; - spin_unlock_irqrestore(&tx->lock, flags); - } - - } -} -#endif - - void uccp420wlan_rx_frame(struct sk_buff *skb, void *context) { struct mac80211_dev *dev = (struct mac80211_dev *)context; diff --git a/drivers/net/wireless/uccp420wlan/src/hal_hostport.c b/drivers/net/wireless/uccp420wlan/src/hal_hostport.c index b00a6a99458..10f9bafcc9a 100644 --- a/drivers/net/wireless/uccp420wlan/src/hal_hostport.c +++ b/drivers/net/wireless/uccp420wlan/src/hal_hostport.c @@ -29,6 +29,7 @@ #include <linux/skbuff.h> #include <linux/slab.h> #include <linux/netdevice.h> +#include <linux/proc_fs.h> #include <asm/unaligned.h> @@ -954,21 +955,14 @@ static void stats_timer_expiry(unsigned long data) #endif -int hal_start(struct proc_dir_entry *main_dir_entry) +int hal_start(void) { - int err = 0; - #ifdef PERF_PROFILING init_timer(&stats_timer); stats_timer.function = stats_timer_expiry; stats_timer.data = (unsigned long) NULL; mod_timer(&stats_timer, jiffies + msecs_to_jiffies(1000)); #endif - err = hal_proc_init(main_dir_entry); - - if (err) - return err; - hpriv->hal_disabled = 0; /* Enable host_int and uccp_int */ @@ -978,10 +972,8 @@ int hal_start(struct proc_dir_entry *main_dir_entry) } -int hal_stop(struct proc_dir_entry *main_dir_entry) +int hal_stop(void) { - /* This is created in hal_start */ - remove_proc_entry("hal_stats", main_dir_entry); /* Disable host_int and uccp_irq */ hal_disable_int(NULL); @@ -1169,6 +1161,13 @@ static int uccp420_pltfr_probe(struct platform_device *pdev) clk_prepare_enable(devm_clk_get(&pdev->dev, "aux_adc")); clk_prepare_enable(devm_clk_get(&pdev->dev, "aux_adc_internal")); + /* To support suspend/resume (economy mode) + * during probe a wake up capable device will invoke + * the below routine with second parameter("can_wakeup" flag) + * set to 1. + */ + device_init_wakeup(&pdev->dev, 1); + ret = hal_ops.init(&pdev->dev); @@ -1192,6 +1191,13 @@ static int uccp420_pltfr_remove(struct platform_device *pdev) clk_disable_unprepare(devm_clk_get(&pdev->dev, "aux_adc")); clk_disable_unprepare(devm_clk_get(&pdev->dev, "aux_adc_internal")); + /* To support suspend/resume feature (economy mode) + * during remove a wake up capable device will invoke + * the below routine with second parameter("can_wakeup" flag) + * set to 0. + */ + device_init_wakeup(&pdev->dev, 0); + return 0; } @@ -1247,6 +1253,9 @@ static int hal_deinit(void *dev) static int hal_init(void *dev) { + struct proc_dir_entry *main_dir_entry; + int err = 0; + (void) (dev); hpriv->shm_offset = shm_offset; @@ -1437,12 +1446,17 @@ static int hal_init(void *dev) spin_lock_init(&timing_lock); #endif - if (_uccp420wlan_80211if_init() < 0) { + if (_uccp420wlan_80211if_init(&main_dir_entry) < 0) { pr_err("%s: wlan_init failed\n", hal_name); hal_deinit(NULL); return -ENOMEM; } + err = hal_proc_init(main_dir_entry); + + if (err) + return err; + hpriv->cmd_cnt = COMMAND_START_MAGIC; hpriv->event_cnt = 0; return 0; @@ -1827,6 +1841,16 @@ void hal_request_mem_regions(unsigned char **gram_addr, *gram_b4_addr = (unsigned char *)hpriv->gram_b4_addr; } +void hal_enable_irq_wake(void) +{ + enable_irq_wake(hpriv->irq); +} + +void hal_disable_irq_wake(void) +{ + disable_irq_wake(hpriv->irq); +} + struct hal_ops_tag hal_ops = { .init = hal_init, @@ -1842,6 +1866,8 @@ struct hal_ops_tag hal_ops = { .reset_hal_params = hal_reset_hal_params, .set_mem_region = hal_set_mem_region, .request_mem_regions = hal_request_mem_regions, + .enable_irq_wake = hal_enable_irq_wake, + .disable_irq_wake = hal_disable_irq_wake, }; diff --git a/drivers/net/wireless/uccp420wlan/src/hal_hostport.h b/drivers/net/wireless/uccp420wlan/src/hal_hostport.h index 46adc8891be..4509a8c34f5 100644 --- a/drivers/net/wireless/uccp420wlan/src/hal_hostport.h +++ b/drivers/net/wireless/uccp420wlan/src/hal_hostport.h @@ -148,7 +148,7 @@ struct event_hal { } _PACKED_; -int _uccp420wlan_80211if_init(void); +int _uccp420wlan_80211if_init(struct proc_dir_entry **); void _uccp420wlan_80211if_exit(void); /*Porting information: diff --git a/drivers/net/wireless/uccp420wlan/src/tx.c b/drivers/net/wireless/uccp420wlan/src/tx.c index 1d435788693..465d87de5dd 100644 --- a/drivers/net/wireless/uccp420wlan/src/tx.c +++ b/drivers/net/wireless/uccp420wlan/src/tx.c @@ -61,8 +61,7 @@ static void wait_for_tx_complete(struct tx_config *tx) } } - -static inline int tx_queue_map(int queue) +int tx_queue_map(int queue) { unsigned int ac[4] = {WLAN_AC_VO, WLAN_AC_VI, WLAN_AC_BE, WLAN_AC_BK}; @@ -72,8 +71,7 @@ static inline int tx_queue_map(int queue) return WLAN_AC_VO; } - -static inline int tx_queue_unmap(int queue) +int tx_queue_unmap(int queue) { unsigned int ac[4] = {3, 2, 1, 0}; @@ -105,6 +103,9 @@ static void tx_status(struct sk_buff *skb, int tx_fixed_mcs_idx = 0; int tx_fixed_rate = 0; struct ieee80211_supported_band *band = NULL; + struct umac_vif *uvif = NULL; + + uvif = (struct umac_vif *)(tx_info->control.vif->drv_priv); /* Rate info will be retained, except the count*/ ieee80211_tx_info_clear_status(tx_info); @@ -207,13 +208,18 @@ static void tx_status(struct sk_buff *skb, index++; } - if ((tx_info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) && + if (((tx_info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) || + (uvif->chanctx && + (uvif->chanctx->index == dev->roc_off_chanctx_idx))) && (atomic_dec_return(&dev->roc_params.roc_mgmt_tx_count) == 0)) { - if (dev->roc_params.roc_in_progress) { - /* Reuse the delayed workqueue with 1ms delay */ - ieee80211_queue_delayed_work(dev->hw, - &dev->roc_complete_work, - msecs_to_jiffies(1)); + DEBUG_LOG("%s-UMACTX: TXDONE Frame: %d\n", + dev->name, + atomic_read(&dev->roc_params.roc_mgmt_tx_count)); + if (dev->roc_params.roc_in_progress && + dev->roc_params.roc_type == ROC_TYPE_OFFCHANNEL_TX) { + uccp420wlan_prog_roc(ROC_STOP, 0, 0, 0); + DEBUG_LOG("%s-UMACTX: all offchan pend frames clear\n", + dev->name); } } @@ -242,6 +248,7 @@ static int get_token(struct mac80211_dev *dev, if (!test_and_set_bit(curr_bit, &tx->buf_pool_bmp[pool_id])) { token_id = queue + (NUM_ACS * cnt); + tx->outstanding_tokens[queue]++; break; } } @@ -258,29 +265,40 @@ static int get_token(struct mac80211_dev *dev, /* Do not set, we will queue to the same token */ if (!test_and_set_bit(curr_bit, &tx->buf_pool_bmp[pool_id])) { + tx->outstanding_tokens[queue]++; break; } } } - if (token_id != NUM_TX_DESCS) { - tx->outstanding_tokens[queue]++; -#ifdef MULTI_CHAN_SUPPORT - tx->desc_chan_map[token_id] = curr_chanctx_idx; -#endif - } - return token_id; } +void free_token(struct mac80211_dev *dev, + int token_id, + int queue) +{ + struct tx_config *tx = &dev->tx; + int bit = -1; + int pool_id = -1; + + bit = (token_id % TX_DESC_BUCKET_BOUND); + pool_id = (token_id / TX_DESC_BUCKET_BOUND); -int get_curr_peer_opp(struct mac80211_dev *dev, + __clear_bit(bit, &tx->buf_pool_bmp[pool_id]); + + tx->outstanding_tokens[queue]--; +} + + +struct curr_peer_info get_curr_peer_opp(struct mac80211_dev *dev, #ifdef MULTI_CHAN_SUPPORT - int curr_chanctx_idx, + int curr_chanctx_idx, #endif - int queue) -{ + int ac) + { unsigned int curr_peer_opp = 0; + unsigned int curr_vif_op_chan = UMAC_VIF_CHANCTX_TYPE_OPER; unsigned int i = 0; struct tx_config *tx = NULL; #ifdef MULTI_CHAN_SUPPORT @@ -291,21 +309,28 @@ int get_curr_peer_opp(struct mac80211_dev *dev, int vif_index = -1; #endif unsigned int init_peer_opp = 0; + struct curr_peer_info peer_info; + unsigned int pend_q_len; + struct sk_buff_head *pend_q = NULL; tx = &dev->tx; #ifdef MULTI_CHAN_SUPPORT - init_peer_opp = tx->curr_peer_opp[curr_chanctx_idx][queue]; + init_peer_opp = tx->curr_peer_opp[curr_chanctx_idx][ac]; #else - init_peer_opp = tx->curr_peer_opp[queue]; + init_peer_opp = tx->curr_peer_opp[ac]; #endif - + /*TODO: Optimize this loop for BCN_Q + */ for (i = 0; i < MAX_PEND_Q_PER_AC; i++) { curr_peer_opp = (init_peer_opp + i) % MAX_PEND_Q_PER_AC; #ifdef MULTI_CHAN_SUPPORT rcu_read_lock(); + /* RoC Frame do not have a "sta" entry. + * so we need not handle RoC stuff here + */ if (curr_peer_opp < MAX_PEERS) { sta = rcu_dereference(dev->peers[curr_peer_opp]); @@ -316,16 +341,43 @@ int get_curr_peer_opp(struct mac80211_dev *dev, usta = (struct umac_sta *)(sta->drv_priv); - if (!usta->chanctx) { + vif = rcu_dereference(dev->vifs[usta->vif_index]); + + if (!vif) { rcu_read_unlock(); continue; } - if (usta->chanctx->index != curr_chanctx_idx) { + + uvif = (struct umac_vif *)(vif->drv_priv); + + if (!uvif->chanctx && !uvif->off_chanctx) { rcu_read_unlock(); continue; } + if ((uvif->chanctx && + (uvif->chanctx->index != curr_chanctx_idx)) || + !uvif->chanctx) { + if ((uvif->off_chanctx && + (uvif->off_chanctx->index != + curr_chanctx_idx)) || + !uvif->off_chanctx) { + rcu_read_unlock(); + continue; + } else { + curr_vif_op_chan = + UMAC_VIF_CHANCTX_TYPE_OFF; + } + } else { + if (dev->roc_params.roc_in_progress && + !dev->roc_params.need_offchan) + curr_vif_op_chan = + UMAC_VIF_CHANCTX_TYPE_OFF; + else + curr_vif_op_chan = + UMAC_VIF_CHANCTX_TYPE_OPER; + } } else { vif_index = (curr_peer_opp - MAX_PEERS); @@ -338,46 +390,177 @@ int get_curr_peer_opp(struct mac80211_dev *dev, uvif = (struct umac_vif *)(vif->drv_priv); - if (!uvif->chanctx) { + if (!uvif->chanctx && !uvif->off_chanctx) { rcu_read_unlock(); continue; } - if (uvif->chanctx->index != curr_chanctx_idx) { - rcu_read_unlock(); - continue; + /* For a beacon queue we will process the frames + * irrespective of the current channel context. + * The FW will take care of transmitting them in the + * appropriate channel. + */ + + if (ac != WLAN_AC_BCN && + ((uvif->chanctx && + (uvif->chanctx->index != curr_chanctx_idx)) || + !uvif->chanctx)) { + if ((uvif->off_chanctx && + (uvif->off_chanctx->index != + curr_chanctx_idx)) || + !uvif->off_chanctx) { + rcu_read_unlock(); + continue; + } else { + curr_vif_op_chan = + UMAC_VIF_CHANCTX_TYPE_OFF; + } + } else { + if (dev->roc_params.roc_in_progress && + !dev->roc_params.need_offchan) + curr_vif_op_chan = + UMAC_VIF_CHANCTX_TYPE_OFF; + else + curr_vif_op_chan = + UMAC_VIF_CHANCTX_TYPE_OPER; } } rcu_read_unlock(); #endif + pend_q = &tx->pending_pkt[curr_vif_op_chan][curr_peer_opp][ac]; + pend_q_len = skb_queue_len(pend_q); - if (skb_queue_len(&tx->pending_pkt[curr_peer_opp][queue])) { + if (pend_q_len) { #ifdef MULTI_CHAN_SUPPORT - tx->curr_peer_opp[curr_chanctx_idx][queue] = + tx->curr_peer_opp[curr_chanctx_idx][ac] = (curr_peer_opp + 1) % MAX_PEND_Q_PER_AC; #else - tx->curr_peer_opp[queue] = + tx->curr_peer_opp[ac] = (curr_peer_opp + 1) % MAX_PEND_Q_PER_AC; #endif break; } } - if (i == MAX_PEND_Q_PER_AC) - return -1; + if (i == MAX_PEND_Q_PER_AC) { + peer_info.id = -1; + peer_info.op_chan_idx = -1; + } else { + peer_info.id = curr_peer_opp; + peer_info.op_chan_idx = curr_vif_op_chan; + DEBUG_LOG("%s-UMACTX: Queue: %d Peer: %d op_chan: %d ", + dev->name, + ac, + curr_peer_opp, + curr_vif_op_chan); + DEBUG_LOG("chanctx: %d got opportunity, pending: %d\n", + curr_chanctx_idx, + pend_q_len); + } - return curr_peer_opp; + return peer_info; } -void uccp420wlan_tx_proc_pend_frms(struct mac80211_dev *dev, - int queue, #ifdef MULTI_CHAN_SUPPORT - int curr_chanctx_idx, +void uccp420wlan_tx_proc_send_pend_frms_all(struct mac80211_dev *dev, + int ch_id) +{ + int txq_len = 0; + int i = 0, cnt = 0; + int queue = 0; + unsigned long flags = 0; + int curr_bit = 0; + int pool_id = 0; + int ret = 0; + int start_ac, end_ac; + unsigned int pkts_pend = 0; + struct tx_config *tx = NULL; + struct sk_buff_head *txq = NULL; + + tx = &dev->tx; + + for (i = 0; i < NUM_TX_DESCS; i++) { + spin_lock_irqsave(&tx->lock, flags); + + curr_bit = (i % TX_DESC_BUCKET_BOUND); + pool_id = (i / TX_DESC_BUCKET_BOUND); + + if (test_and_set_bit(curr_bit, &tx->buf_pool_bmp[pool_id])) { + spin_unlock_irqrestore(&tx->lock, flags); + continue; + } + + txq = &tx->pkt_info[ch_id][i].pkt; + txq_len = skb_queue_len(txq); + + /* Not valid when txq len is 0 */ + queue = tx->pkt_info[ch_id][i].queue; + + if (!txq_len) { + /* Reserved token */ + if (i < (NUM_TX_DESCS_PER_AC * NUM_ACS)) { + queue = (i % NUM_ACS); + start_ac = end_ac = queue; + } else { + /* Spare token: + * Loop through all AC's + */ + start_ac = WLAN_AC_VO; + end_ac = WLAN_AC_BK; + } + + for (cnt = start_ac; cnt >= end_ac; cnt--) { + pkts_pend = uccp420wlan_tx_proc_pend_frms(dev, + cnt, + ch_id, + i); + if (pkts_pend) { + queue = cnt; + break; + } + } + + if (pkts_pend == 0) { + __clear_bit(curr_bit, + &tx->buf_pool_bmp[pool_id]); + spin_unlock_irqrestore(&tx->lock, flags); + continue; + } + } + + tx->outstanding_tokens[queue]++; + spin_unlock_irqrestore(&tx->lock, flags); + + ret = __uccp420wlan_tx_frame(dev, + queue, + i, + ch_id, + 0, + 0); /* TODO: Currently sending 0 + since this param is not used + as expected in the orig + code for multiple frames etc + Need to set this + properly when the orig code + logic is corrected + */ + if (ret < 0) { + pr_err("%s: Queueing of TX frame to FW failed\n", + __func__); + } + } +} #endif - int peer_id, - int token_id) + + +int uccp420wlan_tx_proc_pend_frms(struct mac80211_dev *dev, + int ac, +#ifdef MULTI_CHAN_SUPPORT + int curr_chanctx_idx, +#endif + int token_id) { struct tx_config *tx = &dev->tx; unsigned long ampdu_len = 0; @@ -394,8 +577,22 @@ void uccp420wlan_tx_proc_pend_frms(struct mac80211_dev *dev, unsigned int max_tx_cmds = dev->params->max_tx_cmds; struct sk_buff_head *txq = NULL; struct sk_buff_head *pend_pkt_q = NULL; + unsigned int total_pending_processed = 0; + int pend_pkt_q_len = 0; + struct curr_peer_info peer_info; + + peer_info = get_curr_peer_opp(dev, +#ifdef MULTI_CHAN_SUPPORT + curr_chanctx_idx, +#endif + ac); - pend_pkt_q = &tx->pending_pkt[peer_id][queue]; + /* No pending frames for any peer in that AC. + */ + if (peer_info.id == -1) + return 0; + + pend_pkt_q = &tx->pending_pkt[peer_info.op_chan_idx][peer_info.id][ac]; #ifdef MULTI_CHAN_SUPPORT txq = &dev->tx.pkt_info[curr_chanctx_idx][token_id].pkt; @@ -405,6 +602,12 @@ void uccp420wlan_tx_proc_pend_frms(struct mac80211_dev *dev, skb_first = skb_peek(pend_pkt_q); + if (skb_first == NULL) + pr_err("%s:%d Null SKB: peer: %d\n", + __func__, + __LINE__, + peer_info.id); + mac_hdr_first = (struct ieee80211_hdr *)skb_first->data; tx_info_first = IEEE80211_SKB_CB(skb_first); @@ -443,8 +646,18 @@ void uccp420wlan_tx_proc_pend_frms(struct mac80211_dev *dev, IEEE80211_TX_MAX_RATES) != 0) || (tx_info_first->flags != tx_info->flags) || #endif + /* RPU has a limitation, it expects A1-A2-A3 to be same + * for all MPDU's within an AMPDU. This is a temporary + * solution, remove it when RPU has fix for this. + */ (memcmp(mac_hdr->addr1, mac_hdr_first->addr1, + ETH_ALEN) != 0) || + (memcmp(mac_hdr->addr2, + mac_hdr_first->addr2, + ETH_ALEN) != 0) || + (memcmp(mac_hdr->addr3, + mac_hdr_first->addr3, ETH_ALEN) != 0)) break; @@ -457,115 +670,120 @@ void uccp420wlan_tx_proc_pend_frms(struct mac80211_dev *dev, if (!skb_queue_len(txq)) skb_queue_tail(txq, skb_dequeue(pend_pkt_q)); - DEBUG_LOG("%s-UMACTX:Max_pkt_thresh: send spare: %d with %d\n", + total_pending_processed = skb_queue_len(txq); + + pend_pkt_q_len = skb_queue_len(pend_pkt_q); + if ((ac != WLAN_AC_BCN) && + (tx->queue_stopped_bmp & (1 << ac)) && + pend_pkt_q_len < (MAX_TX_QUEUE_LEN / 2)) { + ieee80211_wake_queue(dev->hw, tx_queue_unmap(ac)); + tx->queue_stopped_bmp &= ~(1 << (ac)); + } + + DEBUG_LOG("%s-UMACTX: token_id: %d total_pending_packets_process: %d\n", dev->name, token_id, skb_queue_len(txq)); + + return total_pending_processed; } -int uccp420wlan_tx_alloc_buff_req(struct mac80211_dev *dev, - int queue, +int uccp420wlan_tx_alloc_token(struct mac80211_dev *dev, + int ac, #ifdef MULTI_CHAN_SUPPORT - struct umac_vif *uvif, - int curr_chanctx_idx, + int off_chanctx_idx, + int curr_chanctx_idx, #endif - int peer_id, - struct sk_buff *skb) + int peer_id, + struct sk_buff *skb) { int token_id = NUM_TX_DESCS; struct tx_config *tx = &dev->tx; - struct sk_buff_head *txq = NULL; - unsigned long flags = 0; + unsigned long flags; struct sk_buff_head *pend_pkt_q = NULL; - int tx_peer_id = 0; - struct ieee80211_hdr *mac_hdr = NULL; + unsigned int pkts_pend = 0; spin_lock_irqsave(&tx->lock, flags); - pend_pkt_q = &tx->pending_pkt[peer_id][queue]; + pend_pkt_q = &tx->pending_pkt[off_chanctx_idx][peer_id][ac]; - DEBUG_LOG("%s-UMACTX:Alloc buf Req q = %d,\n", dev->name, queue); + DEBUG_LOG("%s-UMACTX:Alloc buf Req q = %d off_chan: %d peerid: %d,\n", + dev->name, + ac, + off_chanctx_idx, + peer_id); -#ifdef MULTI_CHAN_SUPPORT - if (uvif->chanctx->index == curr_chanctx_idx) - token_id = get_token(dev, - curr_chanctx_idx, - queue); -#else - token_id = get_token(dev, - queue); -#endif + /* Queue the frame to the pending frames queue */ + skb_queue_tail(pend_pkt_q, skb); + /* If the number of outstanding Tx tokens is greater than + * NUM_TX_DESCS_PER_AC we try and encourage aggregation to the max size + * supported (dev->params->max_tx_cmds) + */ + if (tx->outstanding_tokens[ac] >= NUM_TX_DESCS_PER_AC) { + if ((skb_queue_len(pend_pkt_q) < dev->params->max_tx_cmds) || + ac == WLAN_AC_BCN) + goto out; + } - /* If we got a reserved token, then queue frame to the Xmit queue */ - if (token_id < NUM_TX_DESCS_PER_AC * NUM_ACS) { - DEBUG_LOG("%s-UMACTX:Reserved Token, Sending single\n", - dev->name); + /* Take steps to stop the TX traffic if we have reached + * the queueing limit. + * We dont this for the ROC queue to avoid the case where we are in the + * OFF channel but there is lot of traffic for the operating channel on + * the shared ROC queue (which is VO right now), since this would block + * ROC traffic too. + */ + if (skb_queue_len(pend_pkt_q) >= MAX_TX_QUEUE_LEN) { + if ((!dev->roc_params.roc_in_progress) || + (dev->roc_params.roc_in_progress && + (ac != UMAC_ROC_AC))) { + ieee80211_stop_queue(dev->hw, + skb->queue_mapping); + tx->queue_stopped_bmp |= (1 << ac); + } + } + + token_id = get_token(dev, #ifdef MULTI_CHAN_SUPPORT - txq = &dev->tx.pkt_info[curr_chanctx_idx][token_id].pkt; -#else - txq = &dev->tx.pkt_info[token_id].pkt; + curr_chanctx_idx, #endif - skb_queue_tail(txq, skb); - } else { - /* A frame for a beacon queue should never get a reserved - * token - */ - mac_hdr = (struct ieee80211_hdr *)(skb->data); - - if ((queue == WLAN_AC_BCN) && - (ieee80211_is_beacon(mac_hdr->frame_control))) { - if (net_ratelimit()) - pr_warn("Did not get rsvd token for beacon\n"); - } + ac); - /* Queue the frame to the pending frames queue */ - skb_queue_tail(pend_pkt_q, skb); + DEBUG_LOG("%s-UMACTX:Alloc buf Result *id= %d q = %d peerid: %d,\n", + dev->name, + token_id, + ac, + peer_id); - if (queue != WLAN_AC_BCN) { - /* Take steps to stop the TX traffic if we have reached - * the queueing limit - */ - if (skb_queue_len(pend_pkt_q) >= MAX_TX_QUEUE_LEN) { - ieee80211_stop_queue(dev->hw, - skb->queue_mapping); - tx->queue_stopped_bmp |= (1 << queue); - } + if (token_id == NUM_TX_DESCS) + goto out; - /* If we got a spare token, try sending out pending - * frames - */ - if (token_id < NUM_TX_DESCS) { - tx_peer_id = get_curr_peer_opp(dev, + pkts_pend = uccp420wlan_tx_proc_pend_frms(dev, + ac, #ifdef MULTI_CHAN_SUPPORT - curr_chanctx_idx, + curr_chanctx_idx, #endif - queue); + token_id); - uccp420wlan_tx_proc_pend_frms(dev, - queue, -#ifdef MULTI_CHAN_SUPPORT - curr_chanctx_idx, -#endif - tx_peer_id, - token_id); - } - } - } + /* We have just added a frame to pending_q but channel context is + * mismatch. + */ - DEBUG_LOG("%s-UMACTX:Alloc buf Result *id = %d q = %d peer_id = %d\n", - dev->name, - token_id - queue, - peer_id); + if (!pkts_pend) { + free_token(dev, token_id, ac); + token_id = NUM_TX_DESCS; + } +out: spin_unlock_irqrestore(&tx->lock, flags); + DEBUG_LOG("%s-UMACTX:Alloc buf Result *id= %d\n", dev->name, token_id); /* If token is available, just return tokenid, list will be sent*/ return token_id; } + int get_band_chanctx(struct mac80211_dev *dev, struct umac_vif *uvif) { struct ieee80211_chanctx_conf *chanctx = NULL; @@ -586,9 +804,10 @@ int get_band_chanctx(struct mac80211_dev *dev, struct umac_vif *uvif) return band; } + int uccp420wlan_tx_free_buff_req(struct mac80211_dev *dev, struct umac_event_tx_done *tx_done, - unsigned char *queue, + unsigned char *ac, #ifdef MULTI_CHAN_SUPPORT int curr_chanctx_idx, #endif @@ -606,18 +825,15 @@ int uccp420wlan_tx_free_buff_req(struct mac80211_dev *dev, int vif_index = -1; unsigned int pkt = 0; int cnt = 0; - int bit = 0; - int pool_id = 0; unsigned int desc_id = tx_done->descriptor_id; - unsigned int max_tx_cmds = dev->params->max_tx_cmds; struct umac_vif *uvif = NULL; struct ieee80211_vif *ivif = NULL; unsigned long bcn_int = 0; - int pend_pkt_q_len = 0; - int peer_id = 0; #ifdef MULTI_CHAN_SUPPORT int chanctx_idx = 0; + struct tx_pkt_info *pkt_info = NULL; #endif + int start_ac, end_ac; skb_queue_head_init(&tx_done_list); @@ -628,67 +844,17 @@ int uccp420wlan_tx_free_buff_req(struct mac80211_dev *dev, spin_lock_irqsave(&tx->lock, flags); - tx->outstanding_tokens[tx_done->queue]--; - #ifdef MULTI_CHAN_SUPPORT chanctx_idx = tx->desc_chan_map[desc_id]; -#endif - - bit = (desc_id % TX_DESC_BUCKET_BOUND); - pool_id = (desc_id / TX_DESC_BUCKET_BOUND); - - /* Reserved token */ - if (desc_id < (NUM_TX_DESCS_PER_AC * NUM_ACS)) { - *queue = tx_done->queue; - - peer_id = get_curr_peer_opp(dev, -#ifdef MULTI_CHAN_SUPPORT - curr_chanctx_idx, -#endif - *queue); - - if (peer_id == -1) { - __clear_bit(bit, &tx->buf_pool_bmp[pool_id]); -#ifdef MULTI_CHAN_SUPPORT - tx->desc_chan_map[desc_id] = -1; -#endif - } - /* Spare token */ - } else { - for (cnt = WLAN_AC_VO; cnt >= 0; cnt--) { - peer_id = get_curr_peer_opp(dev, -#ifdef MULTI_CHAN_SUPPORT - curr_chanctx_idx, -#endif - cnt); - - if (peer_id != -1) { - *queue = cnt; - break; - } - } - - /* If beacon queue has pending and no other AC - * has pending - */ - if (peer_id == -1) { - __clear_bit(bit, &tx->buf_pool_bmp[pool_id]); -#ifdef MULTI_CHAN_SUPPORT - tx->desc_chan_map[desc_id] = -1; -#endif - } + if (chanctx_idx == -1) { + spin_unlock_irqrestore(&tx->lock, flags); + pr_err("%s: Unexpected channel context\n", __func__); + goto out; } + pkt_info = &dev->tx.pkt_info[chanctx_idx][desc_id]; +#endif - if (peer_id != -1) - pkts_pend = skb_queue_len(&tx->pending_pkt[peer_id][*queue]); - DEBUG_LOG("%s-UMACTX:%s pend_q = %d, sta_id = %d desc_id: %d pend:%d\n", - dev->name, - __func__, - *queue, - peer_id, - desc_id, - pkts_pend); /* Defer Tx Done Processsing */ #ifdef MULTI_CHAN_SUPPORT @@ -705,48 +871,28 @@ int uccp420wlan_tx_free_buff_req(struct mac80211_dev *dev, dev->name, skb_list); } - if (pkts_pend > 0) { - /* For a beacon queue we will process the frames irrespective - * of the current channel context. The FW will take care of - * transmitting them in the appropriate channel. Hence pass the - * interfaces channel context instead of the actual current - * channel context. + /* Reserved token */ + if (desc_id < (NUM_TX_DESCS_PER_AC * NUM_ACS)) { + start_ac = end_ac = tx_done->queue; + } else { + /* Spare token: + * Loop through all AC's */ - if (*queue == WLAN_AC_BCN) { - rcu_read_lock(); - - vif_index = (peer_id - MAX_PEERS); - ivif = rcu_dereference(dev->vifs[vif_index]); - uvif = (struct umac_vif *)(ivif->drv_priv); - curr_chanctx_idx = uvif->chanctx->index; - rcu_read_unlock(); - } - - uccp420wlan_tx_proc_pend_frms(dev, - *queue, + start_ac = WLAN_AC_VO; + end_ac = WLAN_AC_BK; + } + for (cnt = start_ac; cnt >= end_ac; cnt--) { + pkts_pend = uccp420wlan_tx_proc_pend_frms(dev, + cnt, #ifdef MULTI_CHAN_SUPPORT curr_chanctx_idx, #endif - peer_id, desc_id); - tx->outstanding_tokens[*queue]++; - - DEBUG_LOG("%s-UMACTX:Pending packets: %d, Total: %d\n", - dev->name, - pkts_pend, - skb_queue_len(skb_list)); - } else { - DEBUG_LOG("%s-UMACTX:No Pending Packets\n", dev->name); - } - - pend_pkt_q_len = skb_queue_len(&tx->pending_pkt[peer_id][*queue]); - - if ((*queue != WLAN_AC_BCN) && - (tx->queue_stopped_bmp & (1 << *queue)) && - pend_pkt_q_len < (MAX_TX_QUEUE_LEN / 2)) { - ieee80211_wake_queue(dev->hw, tx_queue_unmap(*queue)); - tx->queue_stopped_bmp &= ~(1 << (*queue)); + if (pkts_pend) { + *ac = cnt; + break; + } } /* Unmap here before release lock to avoid race */ @@ -879,35 +1025,81 @@ int uccp420wlan_tx_free_buff_req(struct mac80211_dev *dev, pkt++; } out: - return min(pkts_pend, max_tx_cmds); + return pkts_pend; } #ifdef MULTI_CHAN_SUPPORT -void uccp420wlan_proc_tx_discard_chsw(struct mac80211_dev *dev, - int curr_chanctx_idx, - struct umac_event_tx_done *tx_done) +void uccp420wlan_proc_ch_sw_event(struct umac_event_ch_switch *ch_sw_info, + void *context) +{ + struct mac80211_dev *dev = NULL; + int chan = 0; + int curr_freq = 0; + int chan_id = 0; + struct ieee80211_chanctx_conf *curr_chanctx = NULL; + int i = 0; + + if (!ch_sw_info || !context) { + pr_err("%s: Invalid Parameters:\n", __func__); + return; + } + + dev = (struct mac80211_dev *)context; + chan = ch_sw_info->chan; + + rcu_read_lock(); + + for (i = 0; i < MAX_CHANCTX; i++) { + curr_chanctx = rcu_dereference(dev->chanctx[i]); + + if (curr_chanctx) { + curr_freq = curr_chanctx->def.chan->center_freq; + + if (ieee80211_frequency_to_channel(curr_freq) == chan) { + chan_id = i; + break; + } + } + } + + rcu_read_unlock(); + + if (i == MAX_CHANCTX) { + pr_err("%s: Invalid Channel Context: chan: %d\n", + __func__, + chan); + return; + } + + /* Switch to the new channel context */ + spin_lock(&dev->chanctx_lock); + dev->curr_chanctx_idx = chan_id; + spin_unlock(&dev->chanctx_lock); + + /* We now try to xmit any frames whose xmission got cancelled due to a + * previous channel switch + */ + uccp420wlan_tx_proc_send_pend_frms_all(dev, chan_id); +} + + +unsigned int uccp420wlan_proc_tx_dscrd_chsw(struct mac80211_dev *dev, + int curr_chanctx_idx, + struct umac_event_tx_done *tx_done) { struct tx_config *tx = &dev->tx; - struct sk_buff_head *txq = NULL; - struct sk_buff_head tx_done_list; + struct sk_buff_head *txq = NULL, tx_done_list; int chanctx_idx = -1; int pkt = 0; -#ifdef notyet - int i = 0; -#endif unsigned long flags; int txq_len = 0; struct sk_buff *skb = NULL; struct sk_buff *skb_first = NULL; struct sk_buff *tmp = NULL; - int curr_bit = 0; - int pool_id = 0; int queue = 0; - int ret = 0; + int ret = 0, cnt = 0; unsigned int desc_id = 0; - int peer_id = -1; - int ac = -1; unsigned int *curr_retries = NULL; unsigned int max_retries = 0; struct ieee80211_tx_info tx_info_1st_mpdu; @@ -915,6 +1107,8 @@ void uccp420wlan_proc_tx_discard_chsw(struct mac80211_dev *dev, bool retries_exceeded = false; unsigned int *rate = NULL; unsigned int *retries = NULL; + int start_ac, end_ac; + unsigned int pkts_pend = 0; skb_queue_head_init(&tx_done_list); @@ -928,8 +1122,11 @@ void uccp420wlan_proc_tx_discard_chsw(struct mac80211_dev *dev, */ chanctx_idx = tx->desc_chan_map[desc_id]; - if (chanctx_idx == -1) { - pr_err("%s: Unexpected channel context\n", __func__); + if ((chanctx_idx == -1) || + (chanctx_idx > (MAX_CHANCTX + MAX_OFF_CHANCTX))) { + pr_err("%s: Unexpected channel context: %d\n", + __func__, + chanctx_idx); goto out; } @@ -937,7 +1134,10 @@ void uccp420wlan_proc_tx_discard_chsw(struct mac80211_dev *dev, txq_len = skb_queue_len(txq); if (!txq_len) { - pr_err("%s: TX_DONE received for empty queue\n", __func__); + pr_err("%s: TX_DONE received for empty queue: chan: %d desc_id: %d\n", + __func__, + chanctx_idx, + desc_id); goto out; } @@ -951,34 +1151,25 @@ void uccp420wlan_proc_tx_discard_chsw(struct mac80211_dev *dev, pkt = 0; skb_first = skb_peek(txq); + + if (!skb_first) { + pr_err("%s: Empty txq: chan: %d desc_id: %d\n", + __func__, + chanctx_idx, + desc_id); + goto out; + } + curr_retries = &tx->pkt_info[chanctx_idx][desc_id].curr_retries; max_retries = tx->pkt_info[chanctx_idx][desc_id].max_retries; retries = tx->pkt_info[chanctx_idx][desc_id].retries; rate = tx->pkt_info[chanctx_idx][desc_id].rate; tx->pkt_info[chanctx_idx][desc_id].adjusted_rates = true; - if ((tx_done->retries_num[0] + *curr_retries) > max_retries) { + if ((tx_done->retries_num[0] + *curr_retries) > max_retries) retries_exceeded = true; - } else { + else *curr_retries += tx_done->retries_num[0]; -#ifdef notyet - /* Adjust the counters here */ - for (i = 0; i < 4; i++) { - if (tx_done->rate[0] != rate[i]) - retries[i] = 0; - else - retries[i] -= tx_done->retries_num[0]; - - DEBUG_LOG("%s-UMACTX: %s: %d %s %d == %d retries\n", - dev->name, - __func__, - __LINE__, - "adjusted indices are", - i, - retries[i]); - } -#endif - } memcpy(&tx_info_1st_mpdu, (struct ieee80211_tx_info *)IEEE80211_SKB_CB(skb_first), @@ -1026,79 +1217,69 @@ void uccp420wlan_proc_tx_discard_chsw(struct mac80211_dev *dev, pkt++; } - if (chanctx_idx != curr_chanctx_idx) { - /* First check if there is a packet in the txq of the current - * chanctx that needs to be transmitted - */ - txq = &tx->pkt_info[curr_chanctx_idx][desc_id].pkt; - txq_len = skb_queue_len(txq); - queue = tx->pkt_info[curr_chanctx_idx][desc_id].queue; + /* First check if there is a packet in the txq of the current + * chanctx that needs to be transmitted + */ + txq = &tx->pkt_info[curr_chanctx_idx][desc_id].pkt; + txq_len = skb_queue_len(txq); + queue = tx->pkt_info[curr_chanctx_idx][desc_id].queue; + pkts_pend = txq_len; - if (txq_len) { - spin_unlock_irqrestore(&tx->lock, flags); + if (txq_len) { + spin_unlock_irqrestore(&tx->lock, flags); - /* TODO: Currently sending 0 since this param is not - * used as expected in the orig code for multiple - * frames etc Need to set this properly when the orig - * code logic is corrected + /* TODO: Currently sending 0 since this param is not + * used as expected in the orig code for multiple + * frames etc Need to set this properly when the orig + * code logic is corrected + */ + ret = __uccp420wlan_tx_frame(dev, + queue, + desc_id, + curr_chanctx_idx, + 0, + 1); + if (ret < 0) { + /* TODO: Check if we need to clear the TX bitmap + * and desc_chan_map here */ - ret = __uccp420wlan_tx_frame(dev, - queue, - desc_id, - curr_chanctx_idx, - 0, - 1); - if (ret < 0) { - /* TODO: Check if we need to clear the TX bitmap - * and desc_chan_map here - */ - pr_err("%s: Queueing of TX frame to FW failed\n", - __func__); - } else { - spin_lock_irqsave(&tx->lock, flags); - tx->desc_chan_map[desc_id] = curr_chanctx_idx; - spin_unlock_irqrestore(&tx->lock, flags); - } - - goto tx_done; - } else { - /* Check pending queue */ - /* Reserved token */ - if (desc_id < (NUM_TX_DESCS_PER_AC * NUM_ACS)) { - queue = (desc_id % NUM_ACS); - - peer_id = get_curr_peer_opp(dev, - curr_chanctx_idx, - queue); - - if (peer_id == -1) - goto done; + pr_err("%s: Queueing of TX frame to FW failed\n", + __func__); + } - /* Spare token */ - } else { - for (ac = WLAN_AC_VO; ac >= 0; ac--) { - peer_id = get_curr_peer_opp(dev, - curr_chanctx_idx, - ac); - - if (peer_id != -1) { - queue = ac; - break; - } - } + /* This is needed to avoid freeing up the token + */ + pkts_pend = 1; - if (ac < 0) - goto done; - } + goto tx_done; + } else { + /* Check pending queue */ + /* Reserved token */ + if (desc_id < (NUM_TX_DESCS_PER_AC * NUM_ACS)) { + queue = (desc_id % NUM_ACS); + start_ac = end_ac = queue; + } else { + /* Spare token: + * Loop through all AC's + */ + start_ac = WLAN_AC_VO; + end_ac = WLAN_AC_BK; + } - uccp420wlan_tx_proc_pend_frms(dev, - queue, + for (cnt = start_ac; cnt >= end_ac; cnt--) { + pkts_pend = uccp420wlan_tx_proc_pend_frms(dev, + cnt, curr_chanctx_idx, - peer_id, desc_id); + if (pkts_pend) { + queue = cnt; + break; + } + } - spin_unlock_irqrestore(&tx->lock, flags); + spin_unlock_irqrestore(&tx->lock, flags); + if (pkts_pend > 0) { /* TODO: Currently sending 0 since this param is not * used as expected in the orig code for multiple * frames etc. Need to set this properly when the orig @@ -1112,32 +1293,13 @@ void uccp420wlan_proc_tx_discard_chsw(struct mac80211_dev *dev, 0); if (ret < 0) { - /* SDK: Check if we need to clear the TX bitmap - * and desc_chan_map here - */ pr_err("%s: Queueing of TX frame to FW failed\n", __func__); - } else { - spin_lock_irqsave(&tx->lock, flags); - tx->desc_chan_map[desc_id] = curr_chanctx_idx; - spin_unlock_irqrestore(&tx->lock, flags); } - - goto tx_done; } + goto tx_done; } -done: - curr_bit = (desc_id % TX_DESC_BUCKET_BOUND); - pool_id = (desc_id / TX_DESC_BUCKET_BOUND); - - /* Mark the token as available */ - __clear_bit(curr_bit, &tx->buf_pool_bmp[pool_id]); - - tx->desc_chan_map[desc_id] = -1; - - tx->outstanding_tokens[tx_done->queue]--; - if (txq_len == 1) dev->stats->tx_cmd_send_count_single--; else @@ -1146,6 +1308,8 @@ done: out: spin_unlock_irqrestore(&tx->lock, flags); + return pkts_pend; + tx_done: skb_queue_walk_safe(&tx_done_list, skb, tmp) { tx_status(skb, @@ -1154,6 +1318,8 @@ tx_done: dev, tx_info_1st_mpdu); } + + return pkts_pend; } #endif @@ -1187,6 +1353,7 @@ void uccp420wlan_tx_init(struct mac80211_dev *dev) { int i = 0; int j = 0; + int k = 0; struct tx_config *tx = &dev->tx; memset(&tx->buf_pool_bmp, @@ -1197,8 +1364,10 @@ void uccp420wlan_tx_init(struct mac80211_dev *dev) tx->next_spare_token_ac = WLAN_AC_BE; for (i = 0; i < NUM_ACS; i++) { - for (j = 0; j < MAX_PEND_Q_PER_AC; j++) - skb_queue_head_init(&tx->pending_pkt[j][i]); + for (j = 0; j < MAX_PEND_Q_PER_AC; j++) { + for (k = 0; k < MAX_UMAC_VIF_CHANCTX_TYPES; k++) + skb_queue_head_init(&tx->pending_pkt[k][j][i]); + } tx->outstanding_tokens[i] = 0; } @@ -1207,7 +1376,7 @@ void uccp420wlan_tx_init(struct mac80211_dev *dev) #ifdef MULTI_CHAN_SUPPORT tx->desc_chan_map[i] = -1; - for (j = 0; j < MAX_CHANCTX; j++) + for (j = 0; j < MAX_CHANCTX + MAX_OFF_CHANCTX ; j++) skb_queue_head_init(&tx->pkt_info[j][i].pkt); #else skb_queue_head_init(&tx->pkt_info[i].pkt); @@ -1228,10 +1397,12 @@ void uccp420wlan_tx_init(struct mac80211_dev *dev) tx->persec_timer.function = print_persec_stats; mod_timer(&tx->persec_timer, jiffies + msecs_to_jiffies(1000)); #endif + dev->curr_chanctx_idx = -1; spin_lock_init(&tx->lock); ieee80211_wake_queues(dev->hw); - DEBUG_LOG("%s-UMACTX:Initialization successful\n", dev->name); + DEBUG_LOG("%s-UMACTX: initialization successful\n", + UMACTX_TO_MACDEV(tx)->name); } @@ -1239,9 +1410,12 @@ void uccp420wlan_tx_deinit(struct mac80211_dev *dev) { int i = 0; int j = 0; - unsigned long flags = 0; + int k = 0; + unsigned long flags; struct tx_config *tx = &dev->tx; - struct sk_buff *skb; + struct sk_buff *skb = NULL; + unsigned int qlen = 0; + struct sk_buff_head *pend_q = NULL; ieee80211_stop_queues(dev->hw); @@ -1251,10 +1425,17 @@ void uccp420wlan_tx_deinit(struct mac80211_dev *dev) for (i = 0; i < NUM_TX_DESCS; i++) { #ifdef MULTI_CHAN_SUPPORT - for (j = 0; j < MAX_CHANCTX; j++) - while ((skb = skb_dequeue(&tx->pkt_info[j][i].pkt)) != - NULL) - dev_kfree_skb_any(skb); + for (j = 0; j < MAX_CHANCTX + MAX_OFF_CHANCTX; j++) { + qlen = skb_queue_len(&tx->pkt_info[j][i].pkt); + + if (qlen) { + while ((skb = + skb_dequeue(&tx->pkt_info[j][i].pkt)) != + NULL) { + dev_kfree_skb_any(skb); + } + } + } #else while ((skb = skb_dequeue(&tx->pkt_info[i].pkt)) != NULL) dev_kfree_skb_any(skb); @@ -1263,16 +1444,19 @@ void uccp420wlan_tx_deinit(struct mac80211_dev *dev) for (i = 0; i < NUM_ACS; i++) { for (j = 0; j < MAX_PEND_Q_PER_AC; j++) { - while ((skb = - skb_dequeue(&tx->pending_pkt[j][i])) != - NULL) - dev_kfree_skb_any(skb); + for (k = 0; k < MAX_UMAC_VIF_CHANCTX_TYPES; k++) { + pend_q = &tx->pending_pkt[k][j][i]; + + while ((skb = skb_dequeue(pend_q)) != NULL) + dev_kfree_skb_any(skb); + } } } spin_unlock_irqrestore(&tx->lock, flags); - DEBUG_LOG("%s-UMACTX:Deinitialization successful\n", dev->name); + DEBUG_LOG("%s-UMACTX: deinitialization successful\n", + UMACTX_TO_MACDEV(tx)->name); } @@ -1304,6 +1488,7 @@ int __uccp420wlan_tx_frame(struct mac80211_dev *dev, tx_done.descriptor_id = token_id; tx_done.queue = queue; + dev->tx.desc_chan_map[token_id] = curr_chanctx_idx; #ifdef MULTI_CHAN_SUPPORT txq = &dev->tx.pkt_info[curr_chanctx_idx][token_id].pkt; @@ -1344,6 +1529,9 @@ int uccp420wlan_tx_frame(struct sk_buff *skb, struct umac_vif *uvif = NULL; struct umac_sta *usta = NULL; int peer_id = -1; +#ifdef MULTI_CHAN_SUPPORT + int off_chanctx_idx; +#endif uvif = (struct umac_vif *)(tx_info->control.vif->drv_priv); @@ -1357,21 +1545,27 @@ int uccp420wlan_tx_frame(struct sk_buff *skb, if (bcast == false) { queue = tx_queue_map(skb->queue_mapping); more_frames = 0; + dev->stats->tx_cmds_from_stack++; } else { queue = WLAN_AC_BCN; /* Hack: skb->priority is used to indicate more frames */ more_frames = skb->priority; } - dev->stats->tx_cmds_from_stack++; if (dev->params->production_test == 1) tx_info->flags |= IEEE80211_TX_CTL_AMPDU; - if (tx_info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) { - /* These are high priority frames, send them in VO */ - queue = WLAN_AC_VO; + if ((tx_info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) || + (uvif->chanctx && + uvif->chanctx->index == dev->roc_off_chanctx_idx)) { atomic_inc(&dev->roc_params.roc_mgmt_tx_count); + off_chanctx_idx = UMAC_VIF_CHANCTX_TYPE_OFF; + DEBUG_LOG("%s-UMACTX: Sending OFFCHAN Frame: %d\n", + dev->name, + atomic_read(&dev->roc_params.roc_mgmt_tx_count)); + } else { + off_chanctx_idx = UMAC_VIF_CHANCTX_TYPE_OPER; } mac_hdr = (struct ieee80211_hdr *)(skb->data); @@ -1385,10 +1579,10 @@ int uccp420wlan_tx_frame(struct sk_buff *skb, skb->queue_mapping, ieee80211_is_beacon(mac_hdr->frame_control)); - token_id = uccp420wlan_tx_alloc_buff_req(dev, + token_id = uccp420wlan_tx_alloc_token(dev, queue, #ifdef MULTI_CHAN_SUPPORT - uvif, + off_chanctx_idx, curr_chanctx_idx, #endif peer_id, @@ -1465,19 +1659,23 @@ void uccp420wlan_tx_complete(struct umac_event_tx_done *tx_done, qlen = skb_queue_len(&dev->tx.pkt_info[token_id].pkt); #endif - DEBUG_LOG("%s-UMACTX:TX Done Rx for desc_id: %d qlen: %d\n", + DEBUG_LOG("%s-UMACTX:TX Done Rx for desc_id: %d Q: %d qlen: %d ", dev->name, tx_done->descriptor_id, - qlen); + tx_done->queue, qlen); + DEBUG_LOG("status: %d chactx: %d out_tok: %d\n", + tx_done->frm_status[0], + curr_chanctx_idx, + dev->tx.outstanding_tokens[tx_done->queue]); update_aux_adc_voltage(dev, tx_done->pdout_voltage); #ifdef MULTI_CHAN_SUPPORT if (tx_done->frm_status[0] == TX_DONE_STAT_DISCARD_CHSW) { - uccp420wlan_proc_tx_discard_chsw(dev, - curr_chanctx_idx, - tx_done); - return; + pkts_pending = uccp420wlan_proc_tx_dscrd_chsw(dev, + curr_chanctx_idx, + tx_done); + goto out; } #endif pkts_pending = uccp420wlan_tx_free_buff_req(dev, @@ -1490,12 +1688,6 @@ void uccp420wlan_tx_complete(struct umac_event_tx_done *tx_done, if (pkts_pending) { /*TODO..Do we need to check each skb for more_frames??*/ -#if 0 - if ((queue == WLAN_AC_BCN) && (skb->priority == 1)) - more_frames = 1; - else - more_frames = 0; -#endif more_frames = 0; DEBUG_LOG("%s-UMACTX:%s:%d Transfer Pending Frames:\n", @@ -1515,6 +1707,13 @@ void uccp420wlan_tx_complete(struct umac_event_tx_done *tx_done, DEBUG_LOG("%s-UMACTX:No Pending Packets\n", dev->name); } +out: + if (!pkts_pending) { + /* Mark the token as available */ + free_token(dev, token_id, tx_done->queue); + dev->tx.desc_chan_map[token_id] = -1; + } + for (vif_index = 0; vif_index < MAX_VIFS; vif_index++) { if (vif_index_bitmap & (1 << vif_index)) { memset(&noa_event, 0, sizeof(noa_event)); @@ -1526,3 +1725,191 @@ void uccp420wlan_tx_complete(struct umac_event_tx_done *tx_done, } } } + + +static int uccp420_flush_vif_all_pend_q(struct mac80211_dev *dev, + struct umac_vif *uvif, + unsigned int hw_queue_map, + enum UMAC_VIF_CHANCTX_TYPE chanctx_type) +{ + unsigned int pending = 0; + int count = 0; + int peer_id = -1; + unsigned int queue = 0; + int pend_q = 0; + unsigned long flags; + struct sk_buff_head *pend_pkt_q = NULL; + struct tx_config *tx = NULL; + struct ieee80211_sta *sta = NULL; + struct umac_sta *usta = NULL; + + tx = &dev->tx; + + if (!uvif->chanctx) { + DEBUG_LOG("%s-UMACTX: Chanctx NULL, returning\n", dev->name); + return -1; + } + + for (queue = 0; queue < NUM_ACS; queue++) { + if (!(BIT(queue) & hw_queue_map)) + continue; + + for (pend_q = 0; pend_q < MAX_PEND_Q_PER_AC; pend_q++) { + if (pend_q < MAX_PEERS) { + rcu_read_lock(); + sta = rcu_dereference(dev->peers[pend_q]); + + if (!sta) { + rcu_read_unlock(); + continue; + } + + usta = (struct umac_sta *)(sta->drv_priv); + + if (usta->vif_index == uvif->vif_index) + peer_id = pend_q; + else { + rcu_read_unlock(); + continue; + } + + rcu_read_unlock(); + } else if (pend_q == uvif->vif_index) + peer_id = uvif->vif_index; + else + continue; + + while (1) { + spin_lock_irqsave(&tx->lock, flags); + + pend_pkt_q = + &tx->pending_pkt[chanctx_type] + [peer_id] + [queue]; + + /* Assuming all packets for the peer have same + * channel context + */ + pending = skb_queue_len(pend_pkt_q); + + spin_unlock_irqrestore(&tx->lock, flags); + + if (!pending) + break; + + if (count >= QUEUE_FLUSH_TIMEOUT_TICKS) + break; + + current->state = TASK_INTERRUPTIBLE; + + if (0 == schedule_timeout(1)) + count++; + + } + + if (pending) { + pr_err("%s-UMACTX: Failed for VIF: %d and Queue: %d, pending: %d\n", + dev->name, + uvif->vif_index, + queue, + pending); + + return -1; + } + } + } + + DEBUG_LOG("%s-UMACTX: Success for VIF: %d and Queue: %d\n", + dev->name, + uvif->vif_index, + queue); + return 0; +} + + +static int uccp420_flush_vif_tx_queues(struct mac80211_dev *dev, + struct umac_vif *uvif, + int chanctx_idx, + unsigned int hw_queue_map) +{ + unsigned int tokens = 0; + unsigned int i = 0; + unsigned long buf_pool_bmp = 0; + unsigned long flags; + struct tx_pkt_info *pkt_info = NULL; + struct tx_config *tx = NULL; + int count = 0; + + tx = &dev->tx; + + spin_lock_irqsave(&tx->lock, flags); + + for (i = 0; i < NUM_TX_DESCS; i++) { + pkt_info = &tx->pkt_info[chanctx_idx][i]; + + if ((pkt_info->vif_index == uvif->vif_index) && + (BIT(pkt_info->queue) & hw_queue_map)) + tokens |= BIT(i); + } + + spin_unlock_irqrestore(&tx->lock, flags); + + if (!tokens) + return 0; + + while (1) { + spin_lock_irqsave(&tx->lock, flags); + buf_pool_bmp = tx->buf_pool_bmp[0]; + spin_unlock_irqrestore(&tx->lock, flags); + + if (!(buf_pool_bmp & tokens)) + break; + + if (count >= QUEUE_FLUSH_TIMEOUT_TICKS) + break; + + current->state = TASK_INTERRUPTIBLE; + + if (0 == schedule_timeout(1)) + count++; + } + + if (buf_pool_bmp & tokens) { + pr_err("%s-UMACTX: Failed for VIF: %d, buf_pool_bmp : 0x%lx\n", + dev->name, + uvif->vif_index, + buf_pool_bmp); + + return -1; + } + + DEBUG_LOG("%s-UMACTX: Success for VIF: %d, buf_pool_bmp : 0x%lx\n", + dev->name, + uvif->vif_index, + buf_pool_bmp); + return 0; +} + + +int uccp420_flush_vif_queues(struct mac80211_dev *dev, + struct umac_vif *uvif, + int chanctx_idx, + unsigned int hw_queue_map, + enum UMAC_VIF_CHANCTX_TYPE vif_chanctx_type) +{ + int result = -1; + + result = uccp420_flush_vif_all_pend_q(dev, + uvif, + hw_queue_map, + vif_chanctx_type); + + if (result == 0) { + result = uccp420_flush_vif_tx_queues(dev, + uvif, + chanctx_idx, + hw_queue_map); + } + + return result; +} diff --git a/drivers/net/wireless/uccp420wlan/src/umac_if.c b/drivers/net/wireless/uccp420wlan/src/umac_if.c index f37aa309ea8..5bacb072177 100644 --- a/drivers/net/wireless/uccp420wlan/src/umac_if.c +++ b/drivers/net/wireless/uccp420wlan/src/umac_if.c @@ -502,10 +502,14 @@ static int uccp420wlan_send_cmd(unsigned char *buf, unsigned long irq_flags; rcu_read_lock(); + p = (struct lmac_if_data *)(rcu_dereference(lmac_if)); if (!p) { + pr_err("%s: Unable to retrieve lmac_if\n", __func__); +#ifdef DRIVER_DEBUG WARN_ON(1); +#endif rcu_read_unlock(); return -1; } @@ -581,7 +585,8 @@ int uccp420wlan_prog_reset(unsigned int reset_type, unsigned int lmac_mode) reset.lmac_mode = lmac_mode; reset.antenna_sel = dev->params->antenna_sel; - if (dev->params->production_test == 0) { + if (dev->params->production_test == 0 && + dev->params->bypass_vpd == 0) { memcpy(reset.rf_params, dev->params->rf_params_vpd, RF_PARAMS_SIZE); } else { @@ -697,6 +702,10 @@ int uccp420wlan_proc_tx(void) tx_cmd.bcc_or_ldpc = 0; tx_cmd.stbc_enabled = 0; tx_cmd.num_rates++; + } else { + WARN_ON(1); + rcu_read_unlock(); + return -90; } nbuf = alloc_skb(sizeof(struct cmd_tx_ctrl) + @@ -839,15 +848,17 @@ int uccp420wlan_prog_vht_bform(unsigned int vht_beamform_status, int uccp420wlan_prog_roc(unsigned int roc_ctrl, unsigned int roc_channel, - unsigned int roc_duration) + unsigned int roc_duration, + unsigned int roc_type) { struct cmd_roc cmd_roc; memset(&cmd_roc, 0, sizeof(struct cmd_roc)); - cmd_roc.roc_ctrl = roc_ctrl; + cmd_roc.roc_ctrl = roc_ctrl; cmd_roc.roc_channel = roc_channel; - cmd_roc.roc_duration = roc_duration; + cmd_roc.roc_duration = roc_duration; + cmd_roc.roc_type = roc_type; return uccp420wlan_send_cmd((unsigned char *) &cmd_roc, sizeof(struct cmd_roc), UMAC_CMD_ROC_CTRL); @@ -1302,6 +1313,8 @@ int uccp420wlan_prog_channel(unsigned int prim_ch, dev->cur_chan.freq_band = freq_band; dev->chan_prog_done = 0; + rcu_read_unlock(); + return uccp420wlan_send_cmd((unsigned char *) &channel, sizeof(struct cmd_channel), UMAC_CMD_CHANNEL); @@ -1404,6 +1417,7 @@ int uccp420wlan_prog_tx(unsigned int queue, unsigned long irq_flags, tx_irq_flags; #ifdef MULTI_CHAN_SUPPORT struct tx_pkt_info *pkt_info = NULL; + struct tx_config *tx; #endif memset(&tx_cmd, 0, sizeof(struct cmd_tx_ctrl)); @@ -1419,6 +1433,7 @@ int uccp420wlan_prog_tx(unsigned int queue, dev = p->context; spin_lock_irqsave(&dev->tx.lock, tx_irq_flags); + tx = &dev->tx; #ifdef MULTI_CHAN_SUPPORT txq = &dev->tx.pkt_info[curr_chanctx_idx][descriptor_id].pkt; pkt_info = &dev->tx.pkt_info[curr_chanctx_idx][descriptor_id]; @@ -1447,20 +1462,33 @@ int uccp420wlan_prog_tx(unsigned int queue, if ((ieee80211_is_data(fc) || ieee80211_is_data_qos(fc)) && ieee80211_has_protected(fc)) { - DEBUG_LOG("%s:cipher: %d,icv_len: %d,iv_len: %d,keylen:%d\n", - __func__, - tx_info_first->control.hw_key->cipher, - tx_info_first->control.hw_key->icv_len, - tx_info_first->control.hw_key->iv_len, - tx_info_first->control.hw_key->keylen); - - /* iv_len is always the header ahd - * icv_len is always the trailer - * include only iv_len + /* hw_key == NULL: Encrypted in SW (injected frames) + * iv_len = 0: treat as SW encryption. */ - hdrlen += tx_info_first->control.hw_key->iv_len; + if (tx_info_first->control.hw_key == NULL || + !tx_info_first->control.hw_key->iv_len) { + DEBUG_LOG("%s: hw_key is %s and iv_len: 0\n", + __func__, + tx_info_first->control.hw_key?"valid":"NULL"); + tx_cmd.encrypt = ENCRYPT_DISABLE; + } else { + DEBUG_LOG("%s: cipher: %d, icv: %d, iv: %d, key: %d\n", + __func__, + tx_info_first->control.hw_key->cipher, + tx_info_first->control.hw_key->icv_len, + tx_info_first->control.hw_key->iv_len, + tx_info_first->control.hw_key->keylen); + /* iv_len is always the header and icv_len is always + * the trailer include only iv_len + */ + hdrlen += tx_info_first->control.hw_key->iv_len; + tx_cmd.encrypt = ENCRYPT_ENABLE; + } } + if (tx_info_first->flags & IEEE80211_TX_CTL_TX_OFFCHAN) + tx_cmd.tx_flags |= (1 << UMAC_TX_FLAG_OFFCHAN_FRM); + /* For injected frames (wlantest) hw_key is not set,as PMF uses * CCMP always so hardcode this to CCMP IV LEN 8. * For Auth3: It is completely handled in SW (mac80211). @@ -1468,7 +1496,7 @@ int uccp420wlan_prog_tx(unsigned int queue, if (ieee80211_is_unicast_robust_mgmt_frame(skb_first) && ieee80211_has_protected(fc)) { hdrlen += 8; - tx_cmd.force_encrypt = 1; + tx_cmd.encrypt = ENCRYPT_ENABLE; } /* separate in to up to TSF and From TSF*/ @@ -1493,6 +1521,14 @@ int uccp420wlan_prog_tx(unsigned int queue, tx_cmd.pkt_gram_payload_len = hdrlen; tx_cmd.aggregate_mpdu = AMPDU_AGGR_DISABLED; +#ifdef MULTI_CHAN_SUPPORT + dev->tx.pkt_info[curr_chanctx_idx][descriptor_id].vif_index = vif_index; + dev->tx.pkt_info[curr_chanctx_idx][descriptor_id].queue = queue; +#else + dev->tx.pkt_info[descriptor_id].vif_index = vif_index; + dev->tx.pkt_info[descriptor_id].queue = queue; +#endif + uvif = (struct umac_vif *) (tx_info_first->control.vif->drv_priv); nbuf = alloc_skb(sizeof(struct cmd_tx_ctrl) + @@ -1541,6 +1577,10 @@ int uccp420wlan_prog_tx(unsigned int queue, tx_cmd.rate_retries[2], tx_cmd.rate_retries[3]); +#ifdef MULTI_CHAN_SUPPORT + tx->desc_chan_map[descriptor_id] = curr_chanctx_idx; +#endif + skb_queue_walk_safe(txq, skb, tmp) { if (!skb || (pkt > tx_cmd.num_frames_per_desc)) break; @@ -1566,11 +1606,8 @@ int uccp420wlan_prog_tx(unsigned int queue, #ifdef MULTI_CHAN_SUPPORT dev->tx.pkt_info[curr_chanctx_idx][descriptor_id].hdr_len = hdrlen; - dev->tx.pkt_info[curr_chanctx_idx][descriptor_id].queue = - queue; #else dev->tx.pkt_info[descriptor_id].hdr_len = hdrlen; - dev->tx.pkt_info[descriptor_id].queue = queue; #endif /* Complete packet length */ @@ -2018,6 +2055,19 @@ int uccp420wlan_prog_aux_adc_chain(unsigned int chain_id) UMAC_CMD_AUX_ADC_CHAIN_SEL); } +int uccp420wlan_prog_cont_tx(int val) +{ + struct cmd_cont_tx status; + + memset(&status, 0, sizeof(struct cmd_cont_tx)); + status.op = val; + + return uccp420wlan_send_cmd((unsigned char *)&status, + sizeof(struct cmd_cont_tx), + UMAC_CMD_CONT_TX); +} + + int uccp420wlan_prog_mib_stats(void) { @@ -2280,13 +2330,37 @@ int uccp420wlan_msg_handler(void *nbuff, uccp420wlan_ch_prog_complete(event, (struct umac_event_ch_prog_complete *)buff, p->context); } else if (event == UMAC_EVENT_RF_CALIB_DATA) { - struct umac_event_rf_calib_data *rf_data = (void *) buff; + struct umac_event_rf_calib_data *rf_data = (void *)buff; uccp420wlan_rf_calib_data(rf_data, p->context); + } else if (event == UMAC_EVENT_ROC_STATUS) { + struct umac_event_roc_status *roc_status = (void *)buff; + struct delayed_work *work = NULL; + + DEBUG_LOG("%s-UMACIF: ROC status is %d\n", + dev->name, + roc_status->roc_status); + + switch (roc_status->roc_status) { + case UMAC_ROC_STAT_STARTED: + if (dev->roc_params.roc_in_progress == 0) { + dev->roc_params.roc_in_progress = 1; + ieee80211_ready_on_channel(dev->hw); + DEBUG_LOG("%s-UMACIF: ROC READY..\n", + dev->name); + } + break; + case UMAC_ROC_STAT_DONE: + case UMAC_ROC_STAT_STOPPED: + if (dev->roc_params.roc_in_progress == 1) { + work = &dev->roc_complete_work; + ieee80211_queue_delayed_work(dev->hw, + work, + 0); + } + break; + } #ifdef MULTI_CHAN_SUPPORT - /* SDK: Need to see if this will work in tasklet context (due to - * scheduling latencies) - */ } else if (event == UMAC_EVENT_CHAN_SWITCH) { uccp420wlan_proc_ch_sw_event((void *)buff, p->context); diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index e8402600fe9..62a72fa9530 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -320,4 +320,11 @@ config PHY_QCOM_UFS help Support for UFS PHY on QCOM chipsets. +config PHY_TUSB1210 + tristate "TI TUSB1210 ULPI PHY module" + depends on USB_ULPI_BUS + select GENERIC_PHY + help + Support for TI TUSB1210 USB ULPI PHY. + endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 75a37dc952f..d27d9c43ad3 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -41,3 +41,4 @@ obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o +obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c index a2b08f3ccb0..e17c539e4f6 100644 --- a/drivers/phy/phy-sun4i-usb.c +++ b/drivers/phy/phy-sun4i-usb.c @@ -30,6 +30,7 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/phy/phy.h> +#include <linux/phy/phy-sun4i-usb.h> #include <linux/platform_device.h> #include <linux/regulator/consumer.h> #include <linux/reset.h> @@ -58,6 +59,7 @@ #define PHY_OTG_FUNC_EN 0x28 #define PHY_VBUS_DET_EN 0x29 #define PHY_DISCON_TH_SEL 0x2a +#define PHY_SQUELCH_DETECT 0x3c #define MAX_PHYS 3 @@ -204,6 +206,13 @@ static int sun4i_usb_phy_power_off(struct phy *_phy) return 0; } +void sun4i_usb_phy_set_squelch_detect(struct phy *_phy, bool enabled) +{ + struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); + + sun4i_usb_phy_write(phy, PHY_SQUELCH_DETECT, enabled ? 0 : 2, 2); +} + static struct phy_ops sun4i_usb_phy_ops = { .init = sun4i_usb_phy_init, .exit = sun4i_usb_phy_exit, diff --git a/drivers/phy/phy-tusb1210.c b/drivers/phy/phy-tusb1210.c new file mode 100644 index 00000000000..07efdd318bd --- /dev/null +++ b/drivers/phy/phy-tusb1210.c @@ -0,0 +1,153 @@ +/** + * tusb1210.c - TUSB1210 USB ULPI PHY driver + * + * Copyright (C) 2015 Intel Corporation + * + * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/module.h> +#include <linux/ulpi/driver.h> +#include <linux/gpio/consumer.h> + +#include "ulpi_phy.h" + +#define TUSB1210_VENDOR_SPECIFIC2 0x80 +#define TUSB1210_VENDOR_SPECIFIC2_IHSTX_SHIFT 0 +#define TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_SHIFT 4 +#define TUSB1210_VENDOR_SPECIFIC2_DP_SHIFT 6 + +struct tusb1210 { + struct ulpi *ulpi; + struct phy *phy; + struct gpio_desc *gpio_reset; + struct gpio_desc *gpio_cs; + u8 vendor_specific2; +}; + +static int tusb1210_power_on(struct phy *phy) +{ + struct tusb1210 *tusb = phy_get_drvdata(phy); + + gpiod_set_value_cansleep(tusb->gpio_reset, 1); + gpiod_set_value_cansleep(tusb->gpio_cs, 1); + + /* Restore the optional eye diagram optimization value */ + if (tusb->vendor_specific2) + ulpi_write(tusb->ulpi, TUSB1210_VENDOR_SPECIFIC2, + tusb->vendor_specific2); + + return 0; +} + +static int tusb1210_power_off(struct phy *phy) +{ + struct tusb1210 *tusb = phy_get_drvdata(phy); + + gpiod_set_value_cansleep(tusb->gpio_reset, 0); + gpiod_set_value_cansleep(tusb->gpio_cs, 0); + + return 0; +} + +static struct phy_ops phy_ops = { + .power_on = tusb1210_power_on, + .power_off = tusb1210_power_off, + .owner = THIS_MODULE, +}; + +static int tusb1210_probe(struct ulpi *ulpi) +{ + struct gpio_desc *gpio; + struct tusb1210 *tusb; + u8 val, reg; + int ret; + + tusb = devm_kzalloc(&ulpi->dev, sizeof(*tusb), GFP_KERNEL); + if (!tusb) + return -ENOMEM; + + gpio = devm_gpiod_get(&ulpi->dev, "reset"); + if (!IS_ERR(gpio)) { + ret = gpiod_direction_output(gpio, 0); + if (ret) + return ret; + gpiod_set_value_cansleep(gpio, 1); + tusb->gpio_reset = gpio; + } + + gpio = devm_gpiod_get(&ulpi->dev, "cs"); + if (!IS_ERR(gpio)) { + ret = gpiod_direction_output(gpio, 0); + if (ret) + return ret; + gpiod_set_value_cansleep(gpio, 1); + tusb->gpio_cs = gpio; + } + + /* + * VENDOR_SPECIFIC2 register in TUSB1210 can be used for configuring eye + * diagram optimization and DP/DM swap. + */ + + /* High speed output drive strength configuration */ + device_property_read_u8(&ulpi->dev, "ihstx", &val); + reg = val << TUSB1210_VENDOR_SPECIFIC2_IHSTX_SHIFT; + + /* High speed output impedance configuration */ + device_property_read_u8(&ulpi->dev, "zhsdrv", &val); + reg |= val << TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_SHIFT; + + /* DP/DM swap control */ + device_property_read_u8(&ulpi->dev, "datapolarity", &val); + reg |= val << TUSB1210_VENDOR_SPECIFIC2_DP_SHIFT; + + if (reg) { + ulpi_write(ulpi, TUSB1210_VENDOR_SPECIFIC2, reg); + tusb->vendor_specific2 = reg; + } + + tusb->phy = ulpi_phy_create(ulpi, &phy_ops); + if (IS_ERR(tusb->phy)) + return PTR_ERR(tusb->phy); + + tusb->ulpi = ulpi; + + phy_set_drvdata(tusb->phy, tusb); + ulpi_set_drvdata(ulpi, tusb); + return 0; +} + +static void tusb1210_remove(struct ulpi *ulpi) +{ + struct tusb1210 *tusb = ulpi_get_drvdata(ulpi); + + ulpi_phy_destroy(ulpi, tusb->phy); +} + +#define TI_VENDOR_ID 0x0451 + +static const struct ulpi_device_id tusb1210_ulpi_id[] = { + { TI_VENDOR_ID, 0x1507, }, + { }, +}; +MODULE_DEVICE_TABLE(ulpi, tusb1210_ulpi_id); + +static struct ulpi_driver tusb1210_driver = { + .id_table = tusb1210_ulpi_id, + .probe = tusb1210_probe, + .remove = tusb1210_remove, + .driver = { + .name = "tusb1210", + .owner = THIS_MODULE, + }, +}; + +module_ulpi_driver(tusb1210_driver); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("TUSB1210 ULPI PHY driver"); diff --git a/drivers/phy/ulpi_phy.h b/drivers/phy/ulpi_phy.h new file mode 100644 index 00000000000..ac49fb6285e --- /dev/null +++ b/drivers/phy/ulpi_phy.h @@ -0,0 +1,31 @@ +#include <linux/phy/phy.h> + +/** + * Helper that registers PHY for a ULPI device and adds a lookup for binding it + * and it's controller, which is always the parent. + */ +static inline struct phy +*ulpi_phy_create(struct ulpi *ulpi, struct phy_ops *ops) +{ + struct phy *phy; + int ret; + + phy = phy_create(&ulpi->dev, NULL, ops); + if (IS_ERR(phy)) + return phy; + + ret = phy_create_lookup(phy, "usb2-phy", dev_name(ulpi->dev.parent)); + if (ret) { + phy_destroy(phy); + return ERR_PTR(ret); + } + + return phy; +} + +/* Remove a PHY that was created with ulpi_phy_create() and it's lookup. */ +static inline void ulpi_phy_destroy(struct ulpi *ulpi, struct phy *phy) +{ + phy_remove_lookup(phy, "usb2-phy", dev_name(ulpi->dev.parent)); + phy_destroy(phy); +} diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index 02a522cb775..022b8910e44 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -638,10 +638,15 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) INIT_WORK(&bci->work, twl4030_bci_usb_work); - bci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); - if (!IS_ERR_OR_NULL(bci->transceiver)) { - bci->usb_nb.notifier_call = twl4030_bci_usb_ncb; - usb_register_notifier(bci->transceiver, &bci->usb_nb); + bci->usb_nb.notifier_call = twl4030_bci_usb_ncb; + if (bci->dev->of_node) { + struct device_node *phynode; + + phynode = of_find_compatible_node(bci->dev->of_node->parent, + NULL, "ti,twl4030-usb"); + if (phynode) + bci->transceiver = devm_usb_get_phy_by_node( + bci->dev, phynode, &bci->usb_nb); } /* Enable interrupts now. */ @@ -671,10 +676,6 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) return 0; fail_unmask_interrupts: - if (!IS_ERR_OR_NULL(bci->transceiver)) { - usb_unregister_notifier(bci->transceiver, &bci->usb_nb); - usb_put_phy(bci->transceiver); - } free_irq(bci->irq_bci, bci); fail_bci_irq: free_irq(bci->irq_chg, bci); @@ -703,10 +704,6 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev) twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff, TWL4030_INTERRUPTS_BCIIMR2A); - if (!IS_ERR_OR_NULL(bci->transceiver)) { - usb_unregister_notifier(bci->transceiver, &bci->usb_nb); - usb_put_phy(bci->transceiver); - } free_irq(bci->irq_bci, bci); free_irq(bci->irq_chg, bci); power_supply_unregister(bci->usb); diff --git a/drivers/rtc/rtc-pistachio.c b/drivers/rtc/rtc-pistachio.c index 1ebbcc8f7ea..8d8794fbbd1 100644 --- a/drivers/rtc/rtc-pistachio.c +++ b/drivers/rtc/rtc-pistachio.c @@ -315,7 +315,7 @@ static irqreturn_t pistachio_alarm_handler(int irq, void *dev) return IRQ_HANDLED; } -static int __init pistachio_rtc_probe(struct platform_device *pdev) +static int pistachio_rtc_probe(struct platform_device *pdev) { struct pistachio_rtc *priv; struct regmap *periph_regs; diff --git a/drivers/soc/img/connectivity/img-hostport.c b/drivers/soc/img/connectivity/img-hostport.c index ee2b8ba8243..0c8c496884e 100644 --- a/drivers/soc/img/connectivity/img-hostport.c +++ b/drivers/soc/img/connectivity/img-hostport.c @@ -179,7 +179,7 @@ static u8 id_to_field(int id) static void notify_common(u16 user_data, int user_id, gen_handler poke_ready, void *poke_ready_arg) { - dbgn("snd -- %d:%d:%02X", user_id, user_id, user_data); + trace_printk("img-hostport: snd -- %d:%d:%02X\n", user_id, user_id, user_data); if (poke_ready) poke_ready(poke_ready_arg); iowrite32(0x87 << 24 | user_data << 8 | id_to_field(user_id), @@ -201,27 +201,28 @@ static irqreturn_t hal_irq_handler(int irq, void *p) /* TODO: need to change that to support platforms other that 32 bit */ first_bit = (reg_value & (1 << 31)) >> 31; if (0 == first_bit) { - err("unexpected spurious interrupt detected!\n"); + trace_printk("img-hostport: unexpected spurious interrupt detected (0x%08X)!\n", + reg_value); goto exit; } callee_id = CALLEE(reg_value); caller_id = CALLER(reg_value); user_message = USERMSG(reg_value); - dbgn("rcv -- %d:%d:%02X", callee_id, caller_id, user_message); + trace_printk("img-hostport: rcv -%c %d:%d:%02X\n", first_bit ? '-' : '*', callee_id, caller_id, user_message); /* * callee_id is tainted, therefore must be checked. */ if (callee_id > MAX_ENDPOINT_ID) { - errn("endpoint with id = %u doesn't exist", callee_id); + trace_printk("img-hostport: endpoint with id = %u doesn't exist\n", callee_id); goto deassert; } handler = module->endpoints.f[callee_id]; handler_in_use = module->endpoints.in_use + callee_id; if (NULL == handler) { - errn("endpoint with id = %u not registered", callee_id); + trace_printk("img-hostport: endpoint with id = %u not registered\n", callee_id); goto deassert; } spin_lock_irqsave(handler_in_use, flags); diff --git a/drivers/spi/spi-img-spfi.c b/drivers/spi/spi-img-spfi.c index 5dba219db4b..e11522d0ec7 100644 --- a/drivers/spi/spi-img-spfi.c +++ b/drivers/spi/spi-img-spfi.c @@ -436,15 +436,23 @@ static int img_spfi_prepare(struct spi_master *master, struct spi_message *msg) struct img_spfi *spfi = spi_master_get_devdata(master); u32 val; + /* + * The chip select line is controlled externally so + * we can use the CS0 configuration for all devices + */ val = spfi_readl(spfi, SPFI_PORT_STATE); + + /* 0 for device selection */ + val &= ~(SPFI_PORT_STATE_DEV_SEL_MASK << + SPFI_PORT_STATE_DEV_SEL_SHIFT); if (msg->spi->mode & SPI_CPHA) - val |= SPFI_PORT_STATE_CK_PHASE(msg->spi->chip_select); + val |= SPFI_PORT_STATE_CK_PHASE(0); else - val &= ~SPFI_PORT_STATE_CK_PHASE(msg->spi->chip_select); + val &= ~SPFI_PORT_STATE_CK_PHASE(0); if (msg->spi->mode & SPI_CPOL) - val |= SPFI_PORT_STATE_CK_POL(msg->spi->chip_select); + val |= SPFI_PORT_STATE_CK_POL(0); else - val &= ~SPFI_PORT_STATE_CK_POL(msg->spi->chip_select); + val &= ~SPFI_PORT_STATE_CK_POL(0); spfi_writel(spfi, val, SPFI_PORT_STATE); return 0; @@ -544,15 +552,25 @@ static void img_spfi_config(struct spi_master *master, struct spi_device *spi, div = DIV_ROUND_UP(clk_get_rate(spfi->spfi_clk), xfer->speed_hz); div = clamp(512 / (1 << get_count_order(div)), 1, 128); - val = spfi_readl(spfi, SPFI_DEVICE_PARAMETER(spi->chip_select)); + /* + * The chip select line is controlled externally so + * we can use the CS0 parameters for all devices + */ + val = spfi_readl(spfi, SPFI_DEVICE_PARAMETER(0)); val &= ~(SPFI_DEVICE_PARAMETER_BITCLK_MASK << SPFI_DEVICE_PARAMETER_BITCLK_SHIFT); val |= div << SPFI_DEVICE_PARAMETER_BITCLK_SHIFT; - spfi_writel(spfi, val, SPFI_DEVICE_PARAMETER(spi->chip_select)); + spfi_writel(spfi, val, SPFI_DEVICE_PARAMETER(0)); if (!list_is_last(&xfer->transfer_list, &master->cur_msg->transfers) && - (xfer->tx_buf) && (xfer->len <= SPFI_DATA_REQUEST_MAX_SIZE) - && !is_pending) { + /* + * For duplex mode (both the tx and rx buffers are !NULL) the + * CMD, ADDR, and DUMMY byte parts of the transaction register + * should always be 0 and therefore the pending transfer + * technique cannot be used. + */ + (xfer->tx_buf) && (!xfer->rx_buf) && + (xfer->len <= SPFI_DATA_REQUEST_MAX_SIZE) && !is_pending) { transact = (1 & SPFI_TRANSACTION_CMD_MASK) << SPFI_TRANSACTION_CMD_SHIFT; transact |= ((xfer->len - 1) & SPFI_TRANSACTION_ADDR_MASK) << diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile index ca2f8bd0e43..6bbb3ec1701 100644 --- a/drivers/usb/common/Makefile +++ b/drivers/usb/common/Makefile @@ -7,3 +7,4 @@ usb-common-y += common.o usb-common-$(CONFIG_USB_LED_TRIG) += led.o obj-$(CONFIG_USB_OTG_FSM) += usb-otg-fsm.o +obj-$(CONFIG_USB_ULPI_BUS) += ulpi.o diff --git a/drivers/usb/common/ulpi.c b/drivers/usb/common/ulpi.c new file mode 100644 index 00000000000..0e6f968e93f --- /dev/null +++ b/drivers/usb/common/ulpi.c @@ -0,0 +1,255 @@ +/** + * ulpi.c - USB ULPI PHY bus + * + * Copyright (C) 2015 Intel Corporation + * + * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/ulpi/interface.h> +#include <linux/ulpi/driver.h> +#include <linux/ulpi/regs.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/acpi.h> + +/* -------------------------------------------------------------------------- */ + +int ulpi_read(struct ulpi *ulpi, u8 addr) +{ + return ulpi->ops->read(ulpi->ops, addr); +} +EXPORT_SYMBOL_GPL(ulpi_read); + +int ulpi_write(struct ulpi *ulpi, u8 addr, u8 val) +{ + return ulpi->ops->write(ulpi->ops, addr, val); +} +EXPORT_SYMBOL_GPL(ulpi_write); + +/* -------------------------------------------------------------------------- */ + +static int ulpi_match(struct device *dev, struct device_driver *driver) +{ + struct ulpi_driver *drv = to_ulpi_driver(driver); + struct ulpi *ulpi = to_ulpi_dev(dev); + const struct ulpi_device_id *id; + + for (id = drv->id_table; id->vendor; id++) + if (id->vendor == ulpi->id.vendor && + id->product == ulpi->id.product) + return 1; + + return 0; +} + +static int ulpi_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct ulpi *ulpi = to_ulpi_dev(dev); + + if (add_uevent_var(env, "MODALIAS=ulpi:v%04xp%04x", + ulpi->id.vendor, ulpi->id.product)) + return -ENOMEM; + return 0; +} + +static int ulpi_probe(struct device *dev) +{ + struct ulpi_driver *drv = to_ulpi_driver(dev->driver); + + return drv->probe(to_ulpi_dev(dev)); +} + +static int ulpi_remove(struct device *dev) +{ + struct ulpi_driver *drv = to_ulpi_driver(dev->driver); + + if (drv->remove) + drv->remove(to_ulpi_dev(dev)); + + return 0; +} + +static struct bus_type ulpi_bus = { + .name = "ulpi", + .match = ulpi_match, + .uevent = ulpi_uevent, + .probe = ulpi_probe, + .remove = ulpi_remove, +}; + +/* -------------------------------------------------------------------------- */ + +static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ulpi *ulpi = to_ulpi_dev(dev); + + return sprintf(buf, "ulpi:v%04xp%04x\n", + ulpi->id.vendor, ulpi->id.product); +} +static DEVICE_ATTR_RO(modalias); + +static struct attribute *ulpi_dev_attrs[] = { + &dev_attr_modalias.attr, + NULL +}; + +static struct attribute_group ulpi_dev_attr_group = { + .attrs = ulpi_dev_attrs, +}; + +static const struct attribute_group *ulpi_dev_attr_groups[] = { + &ulpi_dev_attr_group, + NULL +}; + +static void ulpi_dev_release(struct device *dev) +{ + kfree(to_ulpi_dev(dev)); +} + +static struct device_type ulpi_dev_type = { + .name = "ulpi_device", + .groups = ulpi_dev_attr_groups, + .release = ulpi_dev_release, +}; + +/* -------------------------------------------------------------------------- */ + +/** + * ulpi_register_driver - register a driver with the ULPI bus + * @drv: driver being registered + * + * Registers a driver with the ULPI bus. + */ +int ulpi_register_driver(struct ulpi_driver *drv) +{ + if (!drv->probe) + return -EINVAL; + + drv->driver.bus = &ulpi_bus; + + return driver_register(&drv->driver); +} +EXPORT_SYMBOL_GPL(ulpi_register_driver); + +/** + * ulpi_unregister_driver - unregister a driver with the ULPI bus + * @drv: driver to unregister + * + * Unregisters a driver with the ULPI bus. + */ +void ulpi_unregister_driver(struct ulpi_driver *drv) +{ + driver_unregister(&drv->driver); +} +EXPORT_SYMBOL_GPL(ulpi_unregister_driver); + +/* -------------------------------------------------------------------------- */ + +static int ulpi_register(struct device *dev, struct ulpi *ulpi) +{ + int ret; + + /* Test the interface */ + ret = ulpi_write(ulpi, ULPI_SCRATCH, 0xaa); + if (ret < 0) + return ret; + + ret = ulpi_read(ulpi, ULPI_SCRATCH); + if (ret < 0) + return ret; + + if (ret != 0xaa) + return -ENODEV; + + ulpi->id.vendor = ulpi_read(ulpi, ULPI_VENDOR_ID_LOW); + ulpi->id.vendor |= ulpi_read(ulpi, ULPI_VENDOR_ID_HIGH) << 8; + + ulpi->id.product = ulpi_read(ulpi, ULPI_PRODUCT_ID_LOW); + ulpi->id.product |= ulpi_read(ulpi, ULPI_PRODUCT_ID_HIGH) << 8; + + ulpi->dev.parent = dev; + ulpi->dev.bus = &ulpi_bus; + ulpi->dev.type = &ulpi_dev_type; + dev_set_name(&ulpi->dev, "%s.ulpi", dev_name(dev)); + + ACPI_COMPANION_SET(&ulpi->dev, ACPI_COMPANION(dev)); + + request_module("ulpi:v%04xp%04x", ulpi->id.vendor, ulpi->id.product); + + ret = device_register(&ulpi->dev); + if (ret) + return ret; + + dev_dbg(&ulpi->dev, "registered ULPI PHY: vendor %04x, product %04x\n", + ulpi->id.vendor, ulpi->id.product); + + return 0; +} + +/** + * ulpi_register_interface - instantiate new ULPI device + * @dev: USB controller's device interface + * @ops: ULPI register access + * + * Allocates and registers a ULPI device and an interface for it. Called from + * the USB controller that provides the ULPI interface. + */ +struct ulpi *ulpi_register_interface(struct device *dev, struct ulpi_ops *ops) +{ + struct ulpi *ulpi; + int ret; + + ulpi = kzalloc(sizeof(*ulpi), GFP_KERNEL); + if (!ulpi) + return ERR_PTR(-ENOMEM); + + ulpi->ops = ops; + ops->dev = dev; + + ret = ulpi_register(dev, ulpi); + if (ret) { + kfree(ulpi); + return ERR_PTR(ret); + } + + return ulpi; +} +EXPORT_SYMBOL_GPL(ulpi_register_interface); + +/** + * ulpi_unregister_interface - unregister ULPI interface + * @intrf: struct ulpi_interface + * + * Unregisters a ULPI device and it's interface that was created with + * ulpi_create_interface(). + */ +void ulpi_unregister_interface(struct ulpi *ulpi) +{ + device_unregister(&ulpi->dev); +} +EXPORT_SYMBOL_GPL(ulpi_unregister_interface); + +/* -------------------------------------------------------------------------- */ + +static int __init ulpi_init(void) +{ + return bus_register(&ulpi_bus); +} +module_init(ulpi_init); + +static void __exit ulpi_exit(void) +{ + bus_unregister(&ulpi_bus); +} +module_exit(ulpi_exit); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("USB ULPI PHY bus"); diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index cc0ced08bae..a99c89e7812 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig @@ -84,3 +84,23 @@ config USB_OTG_FSM Implements OTG Finite State Machine as specified in On-The-Go and Embedded Host Supplement to the USB Revision 2.0 Specification. +config USB_ULPI_BUS + tristate "USB ULPI PHY interface support" + depends on USB_SUPPORT + help + UTMI+ Low Pin Interface (ULPI) is specification for a commonly used + USB 2.0 PHY interface. The ULPI specification defines a standard set + of registers that can be used to detect the vendor and product which + allows ULPI to be handled as a bus. This module is the driver for that + bus. + + The ULPI interfaces (the buses) are registered by the drivers for USB + controllers which support ULPI register access and have ULPI PHY + attached to them. The ULPI PHY drivers themselves are normal PHY + drivers. + + ULPI PHYs provide often functions such as ADP sensing/probing (OTG + protocol) and USB charger detection. + + To compile this driver as a module, choose M here: the module will + be called ulpi. diff --git a/drivers/usb/dwc2/Kconfig b/drivers/usb/dwc2/Kconfig index 1bcb36ae650..fd95ba6ec31 100644 --- a/drivers/usb/dwc2/Kconfig +++ b/drivers/usb/dwc2/Kconfig @@ -50,18 +50,10 @@ config USB_DWC2_DUAL_ROLE option requires USB_GADGET to be enabled. endchoice -config USB_DWC2_PLATFORM - tristate "DWC2 Platform" - default USB_DWC2_HOST || USB_DWC2_PERIPHERAL - help - The Designware USB2.0 platform interface module for - controllers directly connected to the CPU. - config USB_DWC2_PCI tristate "DWC2 PCI" depends on PCI default n - select USB_DWC2_PLATFORM select NOP_USB_XCEIV help The Designware USB2.0 PCI interface module for controllers diff --git a/drivers/usb/dwc2/Makefile b/drivers/usb/dwc2/Makefile index f07b425eaff..50fdaace1e7 100644 --- a/drivers/usb/dwc2/Makefile +++ b/drivers/usb/dwc2/Makefile @@ -2,7 +2,7 @@ ccflags-$(CONFIG_USB_DWC2_DEBUG) += -DDEBUG ccflags-$(CONFIG_USB_DWC2_VERBOSE) += -DVERBOSE_DEBUG obj-$(CONFIG_USB_DWC2) += dwc2.o -dwc2-y := core.o core_intr.o +dwc2-y := core.o core_intr.o platform.o ifneq ($(filter y,$(CONFIG_USB_DWC2_HOST) $(CONFIG_USB_DWC2_DUAL_ROLE)),) dwc2-y += hcd.o hcd_intr.o @@ -13,6 +13,10 @@ ifneq ($(filter y,$(CONFIG_USB_DWC2_PERIPHERAL) $(CONFIG_USB_DWC2_DUAL_ROLE)),) dwc2-y += gadget.o endif +ifneq ($(CONFIG_DEBUG_FS),) + dwc2-y += debugfs.o +endif + # NOTE: The previous s3c-hsotg peripheral mode only driver has been moved to # this location and renamed gadget.c. When building for dynamically linked # modules, dwc2.ko will get built for host mode, peripheral mode, and dual-role @@ -21,6 +25,3 @@ endif obj-$(CONFIG_USB_DWC2_PCI) += dwc2_pci.o dwc2_pci-y := pci.o - -obj-$(CONFIG_USB_DWC2_PLATFORM) += dwc2_platform.o -dwc2_platform-y := platform.o diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index d5197d492e2..e5b546f1152 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -56,6 +56,389 @@ #include "core.h" #include "hcd.h" +#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) +/** + * dwc2_backup_host_registers() - Backup controller host registers. + * When suspending usb bus, registers needs to be backuped + * if controller power is disabled once suspended. + * + * @hsotg: Programming view of the DWC_otg controller + */ +static int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg) +{ + struct dwc2_hregs_backup *hr; + int i; + + dev_dbg(hsotg->dev, "%s\n", __func__); + + /* Backup Host regs */ + hr = hsotg->hr_backup; + if (!hr) { + hr = devm_kzalloc(hsotg->dev, sizeof(*hr), GFP_KERNEL); + if (!hr) { + dev_err(hsotg->dev, "%s: can't allocate host regs\n", + __func__); + return -ENOMEM; + } + + hsotg->hr_backup = hr; + } + hr->hcfg = readl(hsotg->regs + HCFG); + hr->haintmsk = readl(hsotg->regs + HAINTMSK); + for (i = 0; i < hsotg->core_params->host_channels; ++i) + hr->hcintmsk[i] = readl(hsotg->regs + HCINTMSK(i)); + + hr->hprt0 = readl(hsotg->regs + HPRT0); + hr->hfir = readl(hsotg->regs + HFIR); + + return 0; +} + +/** + * dwc2_restore_host_registers() - Restore controller host registers. + * When resuming usb bus, device registers needs to be restored + * if controller power were disabled. + * + * @hsotg: Programming view of the DWC_otg controller + */ +static int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg) +{ + struct dwc2_hregs_backup *hr; + int i; + + dev_dbg(hsotg->dev, "%s\n", __func__); + + /* Restore host regs */ + hr = hsotg->hr_backup; + if (!hr) { + dev_err(hsotg->dev, "%s: no host registers to restore\n", + __func__); + return -EINVAL; + } + + writel(hr->hcfg, hsotg->regs + HCFG); + writel(hr->haintmsk, hsotg->regs + HAINTMSK); + + for (i = 0; i < hsotg->core_params->host_channels; ++i) + writel(hr->hcintmsk[i], hsotg->regs + HCINTMSK(i)); + + writel(hr->hprt0, hsotg->regs + HPRT0); + writel(hr->hfir, hsotg->regs + HFIR); + + return 0; +} +#else +static inline int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg) +{ return 0; } + +static inline int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg) +{ return 0; } +#endif + +#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \ + IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) +/** + * dwc2_backup_device_registers() - Backup controller device registers. + * When suspending usb bus, registers needs to be backuped + * if controller power is disabled once suspended. + * + * @hsotg: Programming view of the DWC_otg controller + */ +static int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg) +{ + struct dwc2_dregs_backup *dr; + int i; + + dev_dbg(hsotg->dev, "%s\n", __func__); + + /* Backup dev regs */ + dr = hsotg->dr_backup; + if (!dr) { + dr = devm_kzalloc(hsotg->dev, sizeof(*dr), GFP_KERNEL); + if (!dr) { + dev_err(hsotg->dev, "%s: can't allocate device regs\n", + __func__); + return -ENOMEM; + } + + hsotg->dr_backup = dr; + } + + dr->dcfg = readl(hsotg->regs + DCFG); + dr->dctl = readl(hsotg->regs + DCTL); + dr->daintmsk = readl(hsotg->regs + DAINTMSK); + dr->diepmsk = readl(hsotg->regs + DIEPMSK); + dr->doepmsk = readl(hsotg->regs + DOEPMSK); + + for (i = 0; i < hsotg->num_of_eps; i++) { + /* Backup IN EPs */ + dr->diepctl[i] = readl(hsotg->regs + DIEPCTL(i)); + + /* Ensure DATA PID is correctly configured */ + if (dr->diepctl[i] & DXEPCTL_DPID) + dr->diepctl[i] |= DXEPCTL_SETD1PID; + else + dr->diepctl[i] |= DXEPCTL_SETD0PID; + + dr->dieptsiz[i] = readl(hsotg->regs + DIEPTSIZ(i)); + dr->diepdma[i] = readl(hsotg->regs + DIEPDMA(i)); + + /* Backup OUT EPs */ + dr->doepctl[i] = readl(hsotg->regs + DOEPCTL(i)); + + /* Ensure DATA PID is correctly configured */ + if (dr->doepctl[i] & DXEPCTL_DPID) + dr->doepctl[i] |= DXEPCTL_SETD1PID; + else + dr->doepctl[i] |= DXEPCTL_SETD0PID; + + dr->doeptsiz[i] = readl(hsotg->regs + DOEPTSIZ(i)); + dr->doepdma[i] = readl(hsotg->regs + DOEPDMA(i)); + } + + return 0; +} + +/** + * dwc2_restore_device_registers() - Restore controller device registers. + * When resuming usb bus, device registers needs to be restored + * if controller power were disabled. + * + * @hsotg: Programming view of the DWC_otg controller + */ +static int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg) +{ + struct dwc2_dregs_backup *dr; + u32 dctl; + int i; + + dev_dbg(hsotg->dev, "%s\n", __func__); + + /* Restore dev regs */ + dr = hsotg->dr_backup; + if (!dr) { + dev_err(hsotg->dev, "%s: no device registers to restore\n", + __func__); + return -EINVAL; + } + + writel(dr->dcfg, hsotg->regs + DCFG); + writel(dr->dctl, hsotg->regs + DCTL); + writel(dr->daintmsk, hsotg->regs + DAINTMSK); + writel(dr->diepmsk, hsotg->regs + DIEPMSK); + writel(dr->doepmsk, hsotg->regs + DOEPMSK); + + for (i = 0; i < hsotg->num_of_eps; i++) { + /* Restore IN EPs */ + writel(dr->diepctl[i], hsotg->regs + DIEPCTL(i)); + writel(dr->dieptsiz[i], hsotg->regs + DIEPTSIZ(i)); + writel(dr->diepdma[i], hsotg->regs + DIEPDMA(i)); + + /* Restore OUT EPs */ + writel(dr->doepctl[i], hsotg->regs + DOEPCTL(i)); + writel(dr->doeptsiz[i], hsotg->regs + DOEPTSIZ(i)); + writel(dr->doepdma[i], hsotg->regs + DOEPDMA(i)); + } + + /* Set the Power-On Programming done bit */ + dctl = readl(hsotg->regs + DCTL); + dctl |= DCTL_PWRONPRGDONE; + writel(dctl, hsotg->regs + DCTL); + + return 0; +} +#else +static inline int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg) +{ return 0; } + +static inline int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg) +{ return 0; } +#endif + +/** + * dwc2_backup_global_registers() - Backup global controller registers. + * When suspending usb bus, registers needs to be backuped + * if controller power is disabled once suspended. + * + * @hsotg: Programming view of the DWC_otg controller + */ +static int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg) +{ + struct dwc2_gregs_backup *gr; + int i; + + /* Backup global regs */ + gr = hsotg->gr_backup; + if (!gr) { + gr = devm_kzalloc(hsotg->dev, sizeof(*gr), GFP_KERNEL); + if (!gr) { + dev_err(hsotg->dev, "%s: can't allocate global regs\n", + __func__); + return -ENOMEM; + } + + hsotg->gr_backup = gr; + } + + gr->gotgctl = readl(hsotg->regs + GOTGCTL); + gr->gintmsk = readl(hsotg->regs + GINTMSK); + gr->gahbcfg = readl(hsotg->regs + GAHBCFG); + gr->gusbcfg = readl(hsotg->regs + GUSBCFG); + gr->grxfsiz = readl(hsotg->regs + GRXFSIZ); + gr->gnptxfsiz = readl(hsotg->regs + GNPTXFSIZ); + gr->hptxfsiz = readl(hsotg->regs + HPTXFSIZ); + gr->gdfifocfg = readl(hsotg->regs + GDFIFOCFG); + for (i = 0; i < MAX_EPS_CHANNELS; i++) + gr->dtxfsiz[i] = readl(hsotg->regs + DPTXFSIZN(i)); + + return 0; +} + +/** + * dwc2_restore_global_registers() - Restore controller global registers. + * When resuming usb bus, device registers needs to be restored + * if controller power were disabled. + * + * @hsotg: Programming view of the DWC_otg controller + */ +static int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg) +{ + struct dwc2_gregs_backup *gr; + int i; + + dev_dbg(hsotg->dev, "%s\n", __func__); + + /* Restore global regs */ + gr = hsotg->gr_backup; + if (!gr) { + dev_err(hsotg->dev, "%s: no global registers to restore\n", + __func__); + return -EINVAL; + } + + writel(0xffffffff, hsotg->regs + GINTSTS); + writel(gr->gotgctl, hsotg->regs + GOTGCTL); + writel(gr->gintmsk, hsotg->regs + GINTMSK); + writel(gr->gusbcfg, hsotg->regs + GUSBCFG); + writel(gr->gahbcfg, hsotg->regs + GAHBCFG); + writel(gr->grxfsiz, hsotg->regs + GRXFSIZ); + writel(gr->gnptxfsiz, hsotg->regs + GNPTXFSIZ); + writel(gr->hptxfsiz, hsotg->regs + HPTXFSIZ); + writel(gr->gdfifocfg, hsotg->regs + GDFIFOCFG); + for (i = 0; i < MAX_EPS_CHANNELS; i++) + writel(gr->dtxfsiz[i], hsotg->regs + DPTXFSIZN(i)); + + return 0; +} + +/** + * dwc2_exit_hibernation() - Exit controller from Partial Power Down. + * + * @hsotg: Programming view of the DWC_otg controller + * @restore: Controller registers need to be restored + */ +int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore) +{ + u32 pcgcctl; + int ret = 0; + + if (!hsotg->core_params->hibernation) + return -ENOTSUPP; + + pcgcctl = readl(hsotg->regs + PCGCTL); + pcgcctl &= ~PCGCTL_STOPPCLK; + writel(pcgcctl, hsotg->regs + PCGCTL); + + pcgcctl = readl(hsotg->regs + PCGCTL); + pcgcctl &= ~PCGCTL_PWRCLMP; + writel(pcgcctl, hsotg->regs + PCGCTL); + + pcgcctl = readl(hsotg->regs + PCGCTL); + pcgcctl &= ~PCGCTL_RSTPDWNMODULE; + writel(pcgcctl, hsotg->regs + PCGCTL); + + udelay(100); + if (restore) { + ret = dwc2_restore_global_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to restore registers\n", + __func__); + return ret; + } + if (dwc2_is_host_mode(hsotg)) { + ret = dwc2_restore_host_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to restore host registers\n", + __func__); + return ret; + } + } else { + ret = dwc2_restore_device_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to restore device registers\n", + __func__); + return ret; + } + } + } + + return ret; +} + +/** + * dwc2_enter_hibernation() - Put controller in Partial Power Down. + * + * @hsotg: Programming view of the DWC_otg controller + */ +int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg) +{ + u32 pcgcctl; + int ret = 0; + + if (!hsotg->core_params->hibernation) + return -ENOTSUPP; + + /* Backup all registers */ + ret = dwc2_backup_global_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to backup global registers\n", + __func__); + return ret; + } + + if (dwc2_is_host_mode(hsotg)) { + ret = dwc2_backup_host_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to backup host registers\n", + __func__); + return ret; + } + } else { + ret = dwc2_backup_device_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to backup device registers\n", + __func__); + return ret; + } + } + + /* Put the controller in low power state */ + pcgcctl = readl(hsotg->regs + PCGCTL); + + pcgcctl |= PCGCTL_PWRCLMP; + writel(pcgcctl, hsotg->regs + PCGCTL); + ndelay(20); + + pcgcctl |= PCGCTL_RSTPDWNMODULE; + writel(pcgcctl, hsotg->regs + PCGCTL); + ndelay(20); + + pcgcctl |= PCGCTL_STOPPCLK; + writel(pcgcctl, hsotg->regs + PCGCTL); + + return ret; +} + /** * dwc2_enable_common_interrupts() - Initializes the commmon interrupts, * used in both device and host modes @@ -77,8 +460,10 @@ static void dwc2_enable_common_interrupts(struct dwc2_hsotg *hsotg) if (hsotg->core_params->dma_enable <= 0) intmsk |= GINTSTS_RXFLVL; + if (hsotg->core_params->external_id_pin_ctl <= 0) + intmsk |= GINTSTS_CONIDSTSCHNG; - intmsk |= GINTSTS_CONIDSTSCHNG | GINTSTS_WKUPINT | GINTSTS_USBSUSP | + intmsk |= GINTSTS_WKUPINT | GINTSTS_USBSUSP | GINTSTS_SESSREQINT; writel(intmsk, hsotg->regs + GINTMSK); @@ -2602,6 +2987,40 @@ static void dwc2_set_param_uframe_sched(struct dwc2_hsotg *hsotg, int val) hsotg->core_params->uframe_sched = val; } +static void dwc2_set_param_external_id_pin_ctl(struct dwc2_hsotg *hsotg, + int val) +{ + if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) { + if (val >= 0) { + dev_err(hsotg->dev, + "'%d' invalid for parameter external_id_pin_ctl\n", + val); + dev_err(hsotg->dev, "external_id_pin_ctl must be 0 or 1\n"); + } + val = 0; + dev_dbg(hsotg->dev, "Setting external_id_pin_ctl to %d\n", val); + } + + hsotg->core_params->external_id_pin_ctl = val; +} + +static void dwc2_set_param_hibernation(struct dwc2_hsotg *hsotg, + int val) +{ + if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) { + if (val >= 0) { + dev_err(hsotg->dev, + "'%d' invalid for parameter hibernation\n", + val); + dev_err(hsotg->dev, "hibernation must be 0 or 1\n"); + } + val = 0; + dev_dbg(hsotg->dev, "Setting hibernation to %d\n", val); + } + + hsotg->core_params->hibernation = val; +} + /* * This function is called during module intialization to pass module parameters * for the DWC_otg core. @@ -2646,6 +3065,8 @@ void dwc2_set_parameters(struct dwc2_hsotg *hsotg, dwc2_set_param_ahbcfg(hsotg, params->ahbcfg); dwc2_set_param_otg_ver(hsotg, params->otg_ver); dwc2_set_param_uframe_sched(hsotg, params->uframe_sched); + dwc2_set_param_external_id_pin_ctl(hsotg, params->external_id_pin_ctl); + dwc2_set_param_hibernation(hsotg, params->hibernation); } /** @@ -2814,6 +3235,22 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg) return 0; } +/* + * Sets all parameters to the given value. + * + * Assumes that the dwc2_core_params struct contains only integers. + */ +void dwc2_set_all_params(struct dwc2_core_params *params, int value) +{ + int *p = (int *)params; + size_t size = sizeof(*params) / sizeof(*p); + int i; + + for (i = 0; i < size; i++) + p[i] = value; +} + + u16 dwc2_get_otg_version(struct dwc2_hsotg *hsotg) { return hsotg->core_params->otg_ver == 1 ? 0x0200 : 0x0103; diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 836c012c770..53b8de03f10 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -331,6 +331,17 @@ enum dwc2_ep0_state { * by the driver and are ignored in this * configuration value. * @uframe_sched: True to enable the microframe scheduler + * @external_id_pin_ctl: Specifies whether ID pin is handled externally. + * Disable CONIDSTSCHNG controller interrupt in such + * case. + * 0 - No (default) + * 1 - Yes + * @hibernation: Specifies whether the controller support hibernation. + * If hibernation is enabled, the controller will enter + * hibernation in both peripheral and host mode when + * needed. + * 0 - No (default) + * 1 - Yes * * The following parameters may be specified when starting the module. These * parameters define how the DWC_otg controller should be configured. A @@ -368,6 +379,8 @@ struct dwc2_core_params { int reload_ctl; int ahbcfg; int uframe_sched; + int external_id_pin_ctl; + int hibernation; }; /** @@ -452,6 +465,82 @@ struct dwc2_hw_params { #define DWC2_CTRL_BUFF_SIZE 8 /** + * struct dwc2_gregs_backup - Holds global registers state before entering partial + * power down + * @gotgctl: Backup of GOTGCTL register + * @gintmsk: Backup of GINTMSK register + * @gahbcfg: Backup of GAHBCFG register + * @gusbcfg: Backup of GUSBCFG register + * @grxfsiz: Backup of GRXFSIZ register + * @gnptxfsiz: Backup of GNPTXFSIZ register + * @gi2cctl: Backup of GI2CCTL register + * @hptxfsiz: Backup of HPTXFSIZ register + * @gdfifocfg: Backup of GDFIFOCFG register + * @dtxfsiz: Backup of DTXFSIZ registers for each endpoint + * @gpwrdn: Backup of GPWRDN register + */ +struct dwc2_gregs_backup { + u32 gotgctl; + u32 gintmsk; + u32 gahbcfg; + u32 gusbcfg; + u32 grxfsiz; + u32 gnptxfsiz; + u32 gi2cctl; + u32 hptxfsiz; + u32 pcgcctl; + u32 gdfifocfg; + u32 dtxfsiz[MAX_EPS_CHANNELS]; + u32 gpwrdn; +}; + +/** + * struct dwc2_dregs_backup - Holds device registers state before entering partial + * power down + * @dcfg: Backup of DCFG register + * @dctl: Backup of DCTL register + * @daintmsk: Backup of DAINTMSK register + * @diepmsk: Backup of DIEPMSK register + * @doepmsk: Backup of DOEPMSK register + * @diepctl: Backup of DIEPCTL register + * @dieptsiz: Backup of DIEPTSIZ register + * @diepdma: Backup of DIEPDMA register + * @doepctl: Backup of DOEPCTL register + * @doeptsiz: Backup of DOEPTSIZ register + * @doepdma: Backup of DOEPDMA register + */ +struct dwc2_dregs_backup { + u32 dcfg; + u32 dctl; + u32 daintmsk; + u32 diepmsk; + u32 doepmsk; + u32 diepctl[MAX_EPS_CHANNELS]; + u32 dieptsiz[MAX_EPS_CHANNELS]; + u32 diepdma[MAX_EPS_CHANNELS]; + u32 doepctl[MAX_EPS_CHANNELS]; + u32 doeptsiz[MAX_EPS_CHANNELS]; + u32 doepdma[MAX_EPS_CHANNELS]; +}; + +/** + * struct dwc2_hregs_backup - Holds host registers state before entering partial + * power down + * @hcfg: Backup of HCFG register + * @haintmsk: Backup of HAINTMSK register + * @hcintmsk: Backup of HCINTMSK register + * @hptr0: Backup of HPTR0 register + * @hfir: Backup of HFIR register + */ +struct dwc2_hregs_backup { + u32 hcfg; + u32 haintmsk; + u32 hcintmsk[MAX_EPS_CHANNELS]; + u32 hprt0; + u32 hfir; +}; + +/** * struct dwc2_hsotg - Holds the state of the driver, including the non-periodic * and periodic schedules * @@ -481,6 +570,9 @@ struct dwc2_hw_params { * interrupt * @wkp_timer: Timer object for handling Wakeup Detected interrupt * @lx_state: Lx state of connected device + * @gregs_backup: Backup of global registers during suspend + * @dregs_backup: Backup of device registers during suspend + * @hregs_backup: Backup of host registers during suspend * * These are for host mode: * @@ -613,11 +705,12 @@ struct dwc2_hsotg { struct work_struct wf_otg; struct timer_list wkp_timer; enum dwc2_lx_state lx_state; + struct dwc2_gregs_backup *gr_backup; + struct dwc2_dregs_backup *dr_backup; + struct dwc2_hregs_backup *hr_backup; struct dentry *debug_root; - struct dentry *debug_file; - struct dentry *debug_testmode; - struct dentry *debug_fifo; + struct debugfs_regset32 *regset; /* DWC OTG HW Release versions */ #define DWC2_CORE_REV_2_71a 0x4f54271a @@ -751,6 +844,8 @@ enum dwc2_halt_status { * and the DWC_otg controller */ extern void dwc2_core_host_init(struct dwc2_hsotg *hsotg); +extern int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg); +extern int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore); /* * Host core Functions. @@ -983,6 +1078,15 @@ extern void dwc2_set_param_ahbcfg(struct dwc2_hsotg *hsotg, int val); extern void dwc2_set_param_otg_ver(struct dwc2_hsotg *hsotg, int val); +extern void dwc2_set_parameters(struct dwc2_hsotg *hsotg, + const struct dwc2_core_params *params); + +extern void dwc2_set_all_params(struct dwc2_core_params *params, int value); + +extern int dwc2_get_hwparams(struct dwc2_hsotg *hsotg); + + + /* * Dump core registers and SPRAM */ @@ -1005,6 +1109,8 @@ extern void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2, bool reset); extern void s3c_hsotg_core_connect(struct dwc2_hsotg *hsotg); extern void s3c_hsotg_disconnect(struct dwc2_hsotg *dwc2); +extern int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode); +#define dwc2_is_device_connected(hsotg) (hsotg->connected) #else static inline int s3c_hsotg_remove(struct dwc2_hsotg *dwc2) { return 0; } @@ -1018,6 +1124,10 @@ static inline void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2, bool reset) {} static inline void s3c_hsotg_core_connect(struct dwc2_hsotg *hsotg) {} static inline void s3c_hsotg_disconnect(struct dwc2_hsotg *dwc2) {} +static inline int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, + int testmode) +{ return 0; } +#define dwc2_is_device_connected(hsotg) (0) #endif #if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) @@ -1025,14 +1135,12 @@ extern int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg); extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg); extern void dwc2_hcd_start(struct dwc2_hsotg *hsotg); #else -static inline void dwc2_set_all_params(struct dwc2_core_params *params, int value) {} static inline int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg) { return 0; } static inline void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg) {} static inline void dwc2_hcd_start(struct dwc2_hsotg *hsotg) {} static inline void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) {} -static inline int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq, - const struct dwc2_core_params *params) +static inline int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq) { return 0; } #endif diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index 6cf047878db..927be1e8b3d 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -334,6 +334,7 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg) */ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) { + int ret; dev_dbg(hsotg->dev, "++Resume or Remote Wakeup Detected Interrupt++\n"); dev_dbg(hsotg->dev, "%s lxstate = %d\n", __func__, hsotg->lx_state); @@ -345,6 +346,11 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) /* Clear Remote Wakeup Signaling */ dctl &= ~DCTL_RMTWKUPSIG; writel(dctl, hsotg->regs + DCTL); + ret = dwc2_exit_hibernation(hsotg, true); + if (ret && (ret != -ENOTSUPP)) + dev_err(hsotg->dev, "exit hibernation failed\n"); + + call_gadget(hsotg, resume); } /* Change to L0 state */ hsotg->lx_state = DWC2_L0; @@ -397,6 +403,7 @@ static void dwc2_handle_disconnect_intr(struct dwc2_hsotg *hsotg) static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) { u32 dsts; + int ret; dev_dbg(hsotg->dev, "USB SUSPEND\n"); @@ -411,10 +418,43 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) "DSTS.Suspend Status=%d HWCFG4.Power Optimize=%d\n", !!(dsts & DSTS_SUSPSTS), hsotg->hw_params.power_optimized); + if ((dsts & DSTS_SUSPSTS) && hsotg->hw_params.power_optimized) { + /* Ignore suspend request before enumeration */ + if (!dwc2_is_device_connected(hsotg)) { + dev_dbg(hsotg->dev, + "ignore suspend request before enumeration\n"); + goto clear_int; + } + + ret = dwc2_enter_hibernation(hsotg); + if (ret) { + if (ret != -ENOTSUPP) + dev_err(hsotg->dev, + "enter hibernation failed\n"); + goto skip_power_saving; + } + + udelay(100); + + /* Ask phy to be suspended */ + if (!IS_ERR_OR_NULL(hsotg->uphy)) + usb_phy_set_suspend(hsotg->uphy, true); +skip_power_saving: + /* + * Change to L2 (suspend) state before releasing + * spinlock + */ + hsotg->lx_state = DWC2_L2; + + /* Call gadget suspend callback */ + call_gadget(hsotg, suspend); + } } else { if (hsotg->op_state == OTG_STATE_A_PERIPHERAL) { dev_dbg(hsotg->dev, "a_peripheral->a_host\n"); + /* Change to L2 (suspend) state */ + hsotg->lx_state = DWC2_L2; /* Clear the a_peripheral flag, back to a_host */ spin_unlock(&hsotg->lock); dwc2_hcd_start(hsotg); @@ -423,9 +463,7 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) } } - /* Change to L2 (suspend) state */ - hsotg->lx_state = DWC2_L2; - +clear_int: /* Clear interrupt */ writel(GINTSTS_USBSUSP, hsotg->regs + GINTSTS); } @@ -522,4 +560,3 @@ out: spin_unlock(&hsotg->lock); return retval; } -EXPORT_SYMBOL_GPL(dwc2_handle_common_intr); diff --git a/drivers/usb/dwc2/debug.h b/drivers/usb/dwc2/debug.h new file mode 100644 index 00000000000..12dbd1daec8 --- /dev/null +++ b/drivers/usb/dwc2/debug.h @@ -0,0 +1,27 @@ +/** + * debug.h - Designware USB2 DRD controller debug header + * + * Copyright (C) 2015 Intel Corporation + * Mian Yousaf Kaukab <yousaf.kaukab@intel.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "core.h" + +#ifdef CONFIG_DEBUG_FS +extern int dwc2_debugfs_init(struct dwc2_hsotg *); +extern void dwc2_debugfs_exit(struct dwc2_hsotg *); +#else +static inline int dwc2_debugfs_init(struct dwc2_hsotg *hsotg) +{ return 0; } +static inline void dwc2_debugfs_exit(struct dwc2_hsotg *hsotg) +{ } +#endif diff --git a/drivers/usb/dwc2/debugfs.c b/drivers/usb/dwc2/debugfs.c new file mode 100644 index 00000000000..ef2ee3d9a25 --- /dev/null +++ b/drivers/usb/dwc2/debugfs.c @@ -0,0 +1,771 @@ +/** + * debugfs.c - Designware USB2 DRD controller debugfs + * + * Copyright (C) 2015 Intel Corporation + * Mian Yousaf Kaukab <yousaf.kaukab@intel.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/spinlock.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/uaccess.h> + +#include "core.h" +#include "debug.h" + +#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \ + IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) +/** + * testmode_write - debugfs: change usb test mode + * @seq: The seq file to write to. + * @v: Unused parameter. + * + * This debugfs entry modify the current usb test mode. + */ +static ssize_t testmode_write(struct file *file, const char __user *ubuf, size_t + count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct dwc2_hsotg *hsotg = s->private; + unsigned long flags; + u32 testmode = 0; + char buf[32]; + + if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) + return -EFAULT; + + if (!strncmp(buf, "test_j", 6)) + testmode = TEST_J; + else if (!strncmp(buf, "test_k", 6)) + testmode = TEST_K; + else if (!strncmp(buf, "test_se0_nak", 12)) + testmode = TEST_SE0_NAK; + else if (!strncmp(buf, "test_packet", 11)) + testmode = TEST_PACKET; + else if (!strncmp(buf, "test_force_enable", 17)) + testmode = TEST_FORCE_EN; + else + testmode = 0; + + spin_lock_irqsave(&hsotg->lock, flags); + s3c_hsotg_set_test_mode(hsotg, testmode); + spin_unlock_irqrestore(&hsotg->lock, flags); + return count; +} + +/** + * testmode_show - debugfs: show usb test mode state + * @seq: The seq file to write to. + * @v: Unused parameter. + * + * This debugfs entry shows which usb test mode is currently enabled. + */ +static int testmode_show(struct seq_file *s, void *unused) +{ + struct dwc2_hsotg *hsotg = s->private; + unsigned long flags; + int dctl; + + spin_lock_irqsave(&hsotg->lock, flags); + dctl = readl(hsotg->regs + DCTL); + dctl &= DCTL_TSTCTL_MASK; + dctl >>= DCTL_TSTCTL_SHIFT; + spin_unlock_irqrestore(&hsotg->lock, flags); + + switch (dctl) { + case 0: + seq_puts(s, "no test\n"); + break; + case TEST_J: + seq_puts(s, "test_j\n"); + break; + case TEST_K: + seq_puts(s, "test_k\n"); + break; + case TEST_SE0_NAK: + seq_puts(s, "test_se0_nak\n"); + break; + case TEST_PACKET: + seq_puts(s, "test_packet\n"); + break; + case TEST_FORCE_EN: + seq_puts(s, "test_force_enable\n"); + break; + default: + seq_printf(s, "UNKNOWN %d\n", dctl); + } + + return 0; +} + +static int testmode_open(struct inode *inode, struct file *file) +{ + return single_open(file, testmode_show, inode->i_private); +} + +static const struct file_operations testmode_fops = { + .owner = THIS_MODULE, + .open = testmode_open, + .write = testmode_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/** + * state_show - debugfs: show overall driver and device state. + * @seq: The seq file to write to. + * @v: Unused parameter. + * + * This debugfs entry shows the overall state of the hardware and + * some general information about each of the endpoints available + * to the system. + */ +static int state_show(struct seq_file *seq, void *v) +{ + struct dwc2_hsotg *hsotg = seq->private; + void __iomem *regs = hsotg->regs; + int idx; + + seq_printf(seq, "DCFG=0x%08x, DCTL=0x%08x, DSTS=0x%08x\n", + readl(regs + DCFG), + readl(regs + DCTL), + readl(regs + DSTS)); + + seq_printf(seq, "DIEPMSK=0x%08x, DOEPMASK=0x%08x\n", + readl(regs + DIEPMSK), readl(regs + DOEPMSK)); + + seq_printf(seq, "GINTMSK=0x%08x, GINTSTS=0x%08x\n", + readl(regs + GINTMSK), + readl(regs + GINTSTS)); + + seq_printf(seq, "DAINTMSK=0x%08x, DAINT=0x%08x\n", + readl(regs + DAINTMSK), + readl(regs + DAINT)); + + seq_printf(seq, "GNPTXSTS=0x%08x, GRXSTSR=%08x\n", + readl(regs + GNPTXSTS), + readl(regs + GRXSTSR)); + + seq_puts(seq, "\nEndpoint status:\n"); + + for (idx = 0; idx < hsotg->num_of_eps; idx++) { + u32 in, out; + + in = readl(regs + DIEPCTL(idx)); + out = readl(regs + DOEPCTL(idx)); + + seq_printf(seq, "ep%d: DIEPCTL=0x%08x, DOEPCTL=0x%08x", + idx, in, out); + + in = readl(regs + DIEPTSIZ(idx)); + out = readl(regs + DOEPTSIZ(idx)); + + seq_printf(seq, ", DIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x", + in, out); + + seq_puts(seq, "\n"); + } + + return 0; +} + +static int state_open(struct inode *inode, struct file *file) +{ + return single_open(file, state_show, inode->i_private); +} + +static const struct file_operations state_fops = { + .owner = THIS_MODULE, + .open = state_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/** + * fifo_show - debugfs: show the fifo information + * @seq: The seq_file to write data to. + * @v: Unused parameter. + * + * Show the FIFO information for the overall fifo and all the + * periodic transmission FIFOs. + */ +static int fifo_show(struct seq_file *seq, void *v) +{ + struct dwc2_hsotg *hsotg = seq->private; + void __iomem *regs = hsotg->regs; + u32 val; + int idx; + + seq_puts(seq, "Non-periodic FIFOs:\n"); + seq_printf(seq, "RXFIFO: Size %d\n", readl(regs + GRXFSIZ)); + + val = readl(regs + GNPTXFSIZ); + seq_printf(seq, "NPTXFIFO: Size %d, Start 0x%08x\n", + val >> FIFOSIZE_DEPTH_SHIFT, + val & FIFOSIZE_DEPTH_MASK); + + seq_puts(seq, "\nPeriodic TXFIFOs:\n"); + + for (idx = 1; idx < hsotg->num_of_eps; idx++) { + val = readl(regs + DPTXFSIZN(idx)); + + seq_printf(seq, "\tDPTXFIFO%2d: Size %d, Start 0x%08x\n", idx, + val >> FIFOSIZE_DEPTH_SHIFT, + val & FIFOSIZE_STARTADDR_MASK); + } + + return 0; +} + +static int fifo_open(struct inode *inode, struct file *file) +{ + return single_open(file, fifo_show, inode->i_private); +} + +static const struct file_operations fifo_fops = { + .owner = THIS_MODULE, + .open = fifo_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static const char *decode_direction(int is_in) +{ + return is_in ? "in" : "out"; +} + +/** + * ep_show - debugfs: show the state of an endpoint. + * @seq: The seq_file to write data to. + * @v: Unused parameter. + * + * This debugfs entry shows the state of the given endpoint (one is + * registered for each available). + */ +static int ep_show(struct seq_file *seq, void *v) +{ + struct s3c_hsotg_ep *ep = seq->private; + struct dwc2_hsotg *hsotg = ep->parent; + struct s3c_hsotg_req *req; + void __iomem *regs = hsotg->regs; + int index = ep->index; + int show_limit = 15; + unsigned long flags; + + seq_printf(seq, "Endpoint index %d, named %s, dir %s:\n", + ep->index, ep->ep.name, decode_direction(ep->dir_in)); + + /* first show the register state */ + + seq_printf(seq, "\tDIEPCTL=0x%08x, DOEPCTL=0x%08x\n", + readl(regs + DIEPCTL(index)), + readl(regs + DOEPCTL(index))); + + seq_printf(seq, "\tDIEPDMA=0x%08x, DOEPDMA=0x%08x\n", + readl(regs + DIEPDMA(index)), + readl(regs + DOEPDMA(index))); + + seq_printf(seq, "\tDIEPINT=0x%08x, DOEPINT=0x%08x\n", + readl(regs + DIEPINT(index)), + readl(regs + DOEPINT(index))); + + seq_printf(seq, "\tDIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x\n", + readl(regs + DIEPTSIZ(index)), + readl(regs + DOEPTSIZ(index))); + + seq_puts(seq, "\n"); + seq_printf(seq, "mps %d\n", ep->ep.maxpacket); + seq_printf(seq, "total_data=%ld\n", ep->total_data); + + seq_printf(seq, "request list (%p,%p):\n", + ep->queue.next, ep->queue.prev); + + spin_lock_irqsave(&hsotg->lock, flags); + + list_for_each_entry(req, &ep->queue, queue) { + if (--show_limit < 0) { + seq_puts(seq, "not showing more requests...\n"); + break; + } + + seq_printf(seq, "%c req %p: %d bytes @%p, ", + req == ep->req ? '*' : ' ', + req, req->req.length, req->req.buf); + seq_printf(seq, "%d done, res %d\n", + req->req.actual, req->req.status); + } + + spin_unlock_irqrestore(&hsotg->lock, flags); + + return 0; +} + +static int ep_open(struct inode *inode, struct file *file) +{ + return single_open(file, ep_show, inode->i_private); +} + +static const struct file_operations ep_fops = { + .owner = THIS_MODULE, + .open = ep_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/** + * s3c_hsotg_create_debug - create debugfs directory and files + * @hsotg: The driver state + * + * Create the debugfs files to allow the user to get information + * about the state of the system. The directory name is created + * with the same name as the device itself, in case we end up + * with multiple blocks in future systems. + */ +static void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg) +{ + struct dentry *root; + struct dentry *file; + unsigned epidx; + + root = hsotg->debug_root; + + /* create general state file */ + + file = debugfs_create_file("state", S_IRUGO, root, hsotg, &state_fops); + if (IS_ERR(file)) + dev_err(hsotg->dev, "%s: failed to create state\n", __func__); + + file = debugfs_create_file("testmode", S_IRUGO | S_IWUSR, root, hsotg, + &testmode_fops); + if (IS_ERR(file)) + dev_err(hsotg->dev, "%s: failed to create testmode\n", + __func__); + + file = debugfs_create_file("fifo", S_IRUGO, root, hsotg, &fifo_fops); + if (IS_ERR(file)) + dev_err(hsotg->dev, "%s: failed to create fifo\n", __func__); + + /* Create one file for each out endpoint */ + for (epidx = 0; epidx < hsotg->num_of_eps; epidx++) { + struct s3c_hsotg_ep *ep; + + ep = hsotg->eps_out[epidx]; + if (ep) { + file = debugfs_create_file(ep->name, S_IRUGO, + root, ep, &ep_fops); + if (IS_ERR(file)) + dev_err(hsotg->dev, "failed to create %s debug file\n", + ep->name); + } + } + /* Create one file for each in endpoint. EP0 is handled with out eps */ + for (epidx = 1; epidx < hsotg->num_of_eps; epidx++) { + struct s3c_hsotg_ep *ep; + + ep = hsotg->eps_in[epidx]; + if (ep) { + file = debugfs_create_file(ep->name, S_IRUGO, + root, ep, &ep_fops); + if (IS_ERR(file)) + dev_err(hsotg->dev, "failed to create %s debug file\n", + ep->name); + } + } +} +#else +static inline void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg) {} +#endif + +/* s3c_hsotg_delete_debug is removed as cleanup in done in dwc2_debugfs_exit */ + +#define dump_register(nm) \ +{ \ + .name = #nm, \ + .offset = nm, \ +} + +static const struct debugfs_reg32 dwc2_regs[] = { + /* + * Accessing registers like this can trigger mode mismatch interrupt. + * However, according to dwc2 databook, the register access, in this + * case, is completed on the processor bus but is ignored by the core + * and does not affect its operation. + */ + dump_register(GOTGCTL), + dump_register(GOTGINT), + dump_register(GAHBCFG), + dump_register(GUSBCFG), + dump_register(GRSTCTL), + dump_register(GINTSTS), + dump_register(GINTMSK), + dump_register(GRXSTSR), + dump_register(GRXSTSP), + dump_register(GRXFSIZ), + dump_register(GNPTXFSIZ), + dump_register(GNPTXSTS), + dump_register(GI2CCTL), + dump_register(GPVNDCTL), + dump_register(GGPIO), + dump_register(GUID), + dump_register(GSNPSID), + dump_register(GHWCFG1), + dump_register(GHWCFG2), + dump_register(GHWCFG3), + dump_register(GHWCFG4), + dump_register(GLPMCFG), + dump_register(GPWRDN), + dump_register(GDFIFOCFG), + dump_register(ADPCTL), + dump_register(HPTXFSIZ), + dump_register(DPTXFSIZN(1)), + dump_register(DPTXFSIZN(2)), + dump_register(DPTXFSIZN(3)), + dump_register(DPTXFSIZN(4)), + dump_register(DPTXFSIZN(5)), + dump_register(DPTXFSIZN(6)), + dump_register(DPTXFSIZN(7)), + dump_register(DPTXFSIZN(8)), + dump_register(DPTXFSIZN(9)), + dump_register(DPTXFSIZN(10)), + dump_register(DPTXFSIZN(11)), + dump_register(DPTXFSIZN(12)), + dump_register(DPTXFSIZN(13)), + dump_register(DPTXFSIZN(14)), + dump_register(DPTXFSIZN(15)), + dump_register(DCFG), + dump_register(DCTL), + dump_register(DSTS), + dump_register(DIEPMSK), + dump_register(DOEPMSK), + dump_register(DAINT), + dump_register(DAINTMSK), + dump_register(DTKNQR1), + dump_register(DTKNQR2), + dump_register(DTKNQR3), + dump_register(DTKNQR4), + dump_register(DVBUSDIS), + dump_register(DVBUSPULSE), + dump_register(DIEPCTL(0)), + dump_register(DIEPCTL(1)), + dump_register(DIEPCTL(2)), + dump_register(DIEPCTL(3)), + dump_register(DIEPCTL(4)), + dump_register(DIEPCTL(5)), + dump_register(DIEPCTL(6)), + dump_register(DIEPCTL(7)), + dump_register(DIEPCTL(8)), + dump_register(DIEPCTL(9)), + dump_register(DIEPCTL(10)), + dump_register(DIEPCTL(11)), + dump_register(DIEPCTL(12)), + dump_register(DIEPCTL(13)), + dump_register(DIEPCTL(14)), + dump_register(DIEPCTL(15)), + dump_register(DOEPCTL(0)), + dump_register(DOEPCTL(1)), + dump_register(DOEPCTL(2)), + dump_register(DOEPCTL(3)), + dump_register(DOEPCTL(4)), + dump_register(DOEPCTL(5)), + dump_register(DOEPCTL(6)), + dump_register(DOEPCTL(7)), + dump_register(DOEPCTL(8)), + dump_register(DOEPCTL(9)), + dump_register(DOEPCTL(10)), + dump_register(DOEPCTL(11)), + dump_register(DOEPCTL(12)), + dump_register(DOEPCTL(13)), + dump_register(DOEPCTL(14)), + dump_register(DOEPCTL(15)), + dump_register(DIEPINT(0)), + dump_register(DIEPINT(1)), + dump_register(DIEPINT(2)), + dump_register(DIEPINT(3)), + dump_register(DIEPINT(4)), + dump_register(DIEPINT(5)), + dump_register(DIEPINT(6)), + dump_register(DIEPINT(7)), + dump_register(DIEPINT(8)), + dump_register(DIEPINT(9)), + dump_register(DIEPINT(10)), + dump_register(DIEPINT(11)), + dump_register(DIEPINT(12)), + dump_register(DIEPINT(13)), + dump_register(DIEPINT(14)), + dump_register(DIEPINT(15)), + dump_register(DOEPINT(0)), + dump_register(DOEPINT(1)), + dump_register(DOEPINT(2)), + dump_register(DOEPINT(3)), + dump_register(DOEPINT(4)), + dump_register(DOEPINT(5)), + dump_register(DOEPINT(6)), + dump_register(DOEPINT(7)), + dump_register(DOEPINT(8)), + dump_register(DOEPINT(9)), + dump_register(DOEPINT(10)), + dump_register(DOEPINT(11)), + dump_register(DOEPINT(12)), + dump_register(DOEPINT(13)), + dump_register(DOEPINT(14)), + dump_register(DOEPINT(15)), + dump_register(DIEPTSIZ(0)), + dump_register(DIEPTSIZ(1)), + dump_register(DIEPTSIZ(2)), + dump_register(DIEPTSIZ(3)), + dump_register(DIEPTSIZ(4)), + dump_register(DIEPTSIZ(5)), + dump_register(DIEPTSIZ(6)), + dump_register(DIEPTSIZ(7)), + dump_register(DIEPTSIZ(8)), + dump_register(DIEPTSIZ(9)), + dump_register(DIEPTSIZ(10)), + dump_register(DIEPTSIZ(11)), + dump_register(DIEPTSIZ(12)), + dump_register(DIEPTSIZ(13)), + dump_register(DIEPTSIZ(14)), + dump_register(DIEPTSIZ(15)), + dump_register(DOEPTSIZ(0)), + dump_register(DOEPTSIZ(1)), + dump_register(DOEPTSIZ(2)), + dump_register(DOEPTSIZ(3)), + dump_register(DOEPTSIZ(4)), + dump_register(DOEPTSIZ(5)), + dump_register(DOEPTSIZ(6)), + dump_register(DOEPTSIZ(7)), + dump_register(DOEPTSIZ(8)), + dump_register(DOEPTSIZ(9)), + dump_register(DOEPTSIZ(10)), + dump_register(DOEPTSIZ(11)), + dump_register(DOEPTSIZ(12)), + dump_register(DOEPTSIZ(13)), + dump_register(DOEPTSIZ(14)), + dump_register(DOEPTSIZ(15)), + dump_register(DIEPDMA(0)), + dump_register(DIEPDMA(1)), + dump_register(DIEPDMA(2)), + dump_register(DIEPDMA(3)), + dump_register(DIEPDMA(4)), + dump_register(DIEPDMA(5)), + dump_register(DIEPDMA(6)), + dump_register(DIEPDMA(7)), + dump_register(DIEPDMA(8)), + dump_register(DIEPDMA(9)), + dump_register(DIEPDMA(10)), + dump_register(DIEPDMA(11)), + dump_register(DIEPDMA(12)), + dump_register(DIEPDMA(13)), + dump_register(DIEPDMA(14)), + dump_register(DIEPDMA(15)), + dump_register(DOEPDMA(0)), + dump_register(DOEPDMA(1)), + dump_register(DOEPDMA(2)), + dump_register(DOEPDMA(3)), + dump_register(DOEPDMA(4)), + dump_register(DOEPDMA(5)), + dump_register(DOEPDMA(6)), + dump_register(DOEPDMA(7)), + dump_register(DOEPDMA(8)), + dump_register(DOEPDMA(9)), + dump_register(DOEPDMA(10)), + dump_register(DOEPDMA(11)), + dump_register(DOEPDMA(12)), + dump_register(DOEPDMA(13)), + dump_register(DOEPDMA(14)), + dump_register(DOEPDMA(15)), + dump_register(DTXFSTS(0)), + dump_register(DTXFSTS(1)), + dump_register(DTXFSTS(2)), + dump_register(DTXFSTS(3)), + dump_register(DTXFSTS(4)), + dump_register(DTXFSTS(5)), + dump_register(DTXFSTS(6)), + dump_register(DTXFSTS(7)), + dump_register(DTXFSTS(8)), + dump_register(DTXFSTS(9)), + dump_register(DTXFSTS(10)), + dump_register(DTXFSTS(11)), + dump_register(DTXFSTS(12)), + dump_register(DTXFSTS(13)), + dump_register(DTXFSTS(14)), + dump_register(DTXFSTS(15)), + dump_register(PCGCTL), + dump_register(HCFG), + dump_register(HFIR), + dump_register(HFNUM), + dump_register(HPTXSTS), + dump_register(HAINT), + dump_register(HAINTMSK), + dump_register(HFLBADDR), + dump_register(HPRT0), + dump_register(HCCHAR(0)), + dump_register(HCCHAR(1)), + dump_register(HCCHAR(2)), + dump_register(HCCHAR(3)), + dump_register(HCCHAR(4)), + dump_register(HCCHAR(5)), + dump_register(HCCHAR(6)), + dump_register(HCCHAR(7)), + dump_register(HCCHAR(8)), + dump_register(HCCHAR(9)), + dump_register(HCCHAR(10)), + dump_register(HCCHAR(11)), + dump_register(HCCHAR(12)), + dump_register(HCCHAR(13)), + dump_register(HCCHAR(14)), + dump_register(HCCHAR(15)), + dump_register(HCSPLT(0)), + dump_register(HCSPLT(1)), + dump_register(HCSPLT(2)), + dump_register(HCSPLT(3)), + dump_register(HCSPLT(4)), + dump_register(HCSPLT(5)), + dump_register(HCSPLT(6)), + dump_register(HCSPLT(7)), + dump_register(HCSPLT(8)), + dump_register(HCSPLT(9)), + dump_register(HCSPLT(10)), + dump_register(HCSPLT(11)), + dump_register(HCSPLT(12)), + dump_register(HCSPLT(13)), + dump_register(HCSPLT(14)), + dump_register(HCSPLT(15)), + dump_register(HCINT(0)), + dump_register(HCINT(1)), + dump_register(HCINT(2)), + dump_register(HCINT(3)), + dump_register(HCINT(4)), + dump_register(HCINT(5)), + dump_register(HCINT(6)), + dump_register(HCINT(7)), + dump_register(HCINT(8)), + dump_register(HCINT(9)), + dump_register(HCINT(10)), + dump_register(HCINT(11)), + dump_register(HCINT(12)), + dump_register(HCINT(13)), + dump_register(HCINT(14)), + dump_register(HCINT(15)), + dump_register(HCINTMSK(0)), + dump_register(HCINTMSK(1)), + dump_register(HCINTMSK(2)), + dump_register(HCINTMSK(3)), + dump_register(HCINTMSK(4)), + dump_register(HCINTMSK(5)), + dump_register(HCINTMSK(6)), + dump_register(HCINTMSK(7)), + dump_register(HCINTMSK(8)), + dump_register(HCINTMSK(9)), + dump_register(HCINTMSK(10)), + dump_register(HCINTMSK(11)), + dump_register(HCINTMSK(12)), + dump_register(HCINTMSK(13)), + dump_register(HCINTMSK(14)), + dump_register(HCINTMSK(15)), + dump_register(HCTSIZ(0)), + dump_register(HCTSIZ(1)), + dump_register(HCTSIZ(2)), + dump_register(HCTSIZ(3)), + dump_register(HCTSIZ(4)), + dump_register(HCTSIZ(5)), + dump_register(HCTSIZ(6)), + dump_register(HCTSIZ(7)), + dump_register(HCTSIZ(8)), + dump_register(HCTSIZ(9)), + dump_register(HCTSIZ(10)), + dump_register(HCTSIZ(11)), + dump_register(HCTSIZ(12)), + dump_register(HCTSIZ(13)), + dump_register(HCTSIZ(14)), + dump_register(HCTSIZ(15)), + dump_register(HCDMA(0)), + dump_register(HCDMA(1)), + dump_register(HCDMA(2)), + dump_register(HCDMA(3)), + dump_register(HCDMA(4)), + dump_register(HCDMA(5)), + dump_register(HCDMA(6)), + dump_register(HCDMA(7)), + dump_register(HCDMA(8)), + dump_register(HCDMA(9)), + dump_register(HCDMA(10)), + dump_register(HCDMA(11)), + dump_register(HCDMA(12)), + dump_register(HCDMA(13)), + dump_register(HCDMA(14)), + dump_register(HCDMA(15)), + dump_register(HCDMAB(0)), + dump_register(HCDMAB(1)), + dump_register(HCDMAB(2)), + dump_register(HCDMAB(3)), + dump_register(HCDMAB(4)), + dump_register(HCDMAB(5)), + dump_register(HCDMAB(6)), + dump_register(HCDMAB(7)), + dump_register(HCDMAB(8)), + dump_register(HCDMAB(9)), + dump_register(HCDMAB(10)), + dump_register(HCDMAB(11)), + dump_register(HCDMAB(12)), + dump_register(HCDMAB(13)), + dump_register(HCDMAB(14)), + dump_register(HCDMAB(15)), +}; + +int dwc2_debugfs_init(struct dwc2_hsotg *hsotg) +{ + int ret; + struct dentry *file; + + hsotg->debug_root = debugfs_create_dir(dev_name(hsotg->dev), NULL); + if (!hsotg->debug_root) { + ret = -ENOMEM; + goto err0; + } + + /* Add gadget debugfs nodes */ + s3c_hsotg_create_debug(hsotg); + + hsotg->regset = devm_kzalloc(hsotg->dev, sizeof(*hsotg->regset), + GFP_KERNEL); + if (!hsotg->regset) { + ret = -ENOMEM; + goto err1; + } + + hsotg->regset->regs = dwc2_regs; + hsotg->regset->nregs = ARRAY_SIZE(dwc2_regs); + hsotg->regset->base = hsotg->regs; + + file = debugfs_create_regset32("regdump", S_IRUGO, hsotg->debug_root, + hsotg->regset); + if (!file) { + ret = -ENOMEM; + goto err1; + } + + return 0; +err1: + debugfs_remove_recursive(hsotg->debug_root); +err0: + return ret; +} + +void dwc2_debugfs_exit(struct dwc2_hsotg *hsotg) +{ + debugfs_remove_recursive(hsotg->debug_root); + hsotg->debug_root = NULL; +} diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 6a30887082c..4d47b7c0923 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -20,7 +20,6 @@ #include <linux/interrupt.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> -#include <linux/debugfs.h> #include <linux/mutex.h> #include <linux/seq_file.h> #include <linux/delay.h> @@ -35,7 +34,6 @@ #include <linux/usb/gadget.h> #include <linux/usb/phy.h> #include <linux/platform_data/s3c-hsotg.h> -#include <linux/uaccess.h> #include "core.h" #include "hw.h" @@ -792,6 +790,13 @@ static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req, ep->name, req, req->length, req->buf, req->no_interrupt, req->zero, req->short_not_ok); + /* Prevent new request submission when controller is suspended */ + if (hs->lx_state == DWC2_L2) { + dev_dbg(hs->dev, "%s: don't submit request while suspended\n", + __func__); + return -EAGAIN; + } + /* initialise status of the request */ INIT_LIST_HEAD(&hs_req->queue); req->actual = 0; @@ -894,7 +899,7 @@ static struct s3c_hsotg_ep *ep_from_windex(struct dwc2_hsotg *hsotg, * @testmode: requested usb test mode * Enable usb Test Mode requested by the Host. */ -static int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode) +int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode) { int dctl = readl(hsotg->regs + DCTL); @@ -2185,7 +2190,6 @@ void s3c_hsotg_disconnect(struct dwc2_hsotg *hsotg) call_gadget(hsotg, disconnect); } -EXPORT_SYMBOL_GPL(s3c_hsotg_disconnect); /** * s3c_hsotg_irq_fifoempty - TX FIFO empty interrupt handler @@ -2310,8 +2314,9 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, writel(GINTSTS_ERLYSUSP | GINTSTS_SESSREQINT | GINTSTS_GOUTNAKEFF | GINTSTS_GINNAKEFF | GINTSTS_CONIDSTSCHNG | GINTSTS_USBRST | - GINTSTS_ENUMDONE | GINTSTS_OTGINT | - GINTSTS_USBSUSP | GINTSTS_WKUPINT, + GINTSTS_RESETDET | GINTSTS_ENUMDONE | + GINTSTS_OTGINT | GINTSTS_USBSUSP | + GINTSTS_WKUPINT, hsotg->regs + GINTMSK); if (using_dma(hsotg)) @@ -2477,7 +2482,19 @@ irq_retry: } } - if (gintsts & GINTSTS_USBRST) { + if (gintsts & GINTSTS_RESETDET) { + dev_dbg(hsotg->dev, "%s: USBRstDet\n", __func__); + + writel(GINTSTS_RESETDET, hsotg->regs + GINTSTS); + + /* This event must be used only if controller is suspended */ + if (hsotg->lx_state == DWC2_L2) { + dwc2_exit_hibernation(hsotg, true); + hsotg->lx_state = DWC2_L0; + } + } + + if (gintsts & (GINTSTS_USBRST | GINTSTS_RESETDET)) { u32 usb_status = readl(hsotg->regs + GOTGCTL); @@ -2497,6 +2514,7 @@ irq_retry: kill_all_requests(hsotg, hsotg->eps_out[0], -ECONNRESET); + hsotg->lx_state = DWC2_L0; s3c_hsotg_core_init_disconnected(hsotg, true); } } @@ -2745,7 +2763,7 @@ error: * s3c_hsotg_ep_disable - disable given endpoint * @ep: The endpoint to disable. */ -static int s3c_hsotg_ep_disable_force(struct usb_ep *ep, bool force) +static int s3c_hsotg_ep_disable(struct usb_ep *ep) { struct s3c_hsotg_ep *hs_ep = our_ep(ep); struct dwc2_hsotg *hsotg = hs_ep->parent; @@ -2788,10 +2806,6 @@ static int s3c_hsotg_ep_disable_force(struct usb_ep *ep, bool force) return 0; } -static int s3c_hsotg_ep_disable(struct usb_ep *ep) -{ - return s3c_hsotg_ep_disable_force(ep, false); -} /** * on_list - check request is on the given endpoint * @ep: The endpoint to check. @@ -3187,6 +3201,14 @@ static int s3c_hsotg_vbus_session(struct usb_gadget *gadget, int is_active) spin_lock_irqsave(&hsotg->lock, flags); if (is_active) { + /* + * If controller is hibernated, it must exit from hibernation + * before being initialized + */ + if (hsotg->lx_state == DWC2_L2) { + dwc2_exit_hibernation(hsotg, false); + hsotg->lx_state = DWC2_L0; + } /* Kill any ep0 requests as controller will be reinitialized */ kill_all_requests(hsotg, hsotg->eps_out[0], -ECONNRESET); s3c_hsotg_core_init_disconnected(hsotg, false); @@ -3391,404 +3413,6 @@ static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg) #endif } -/** - * testmode_write - debugfs: change usb test mode - * @seq: The seq file to write to. - * @v: Unused parameter. - * - * This debugfs entry modify the current usb test mode. - */ -static ssize_t testmode_write(struct file *file, const char __user *ubuf, size_t - count, loff_t *ppos) -{ - struct seq_file *s = file->private_data; - struct dwc2_hsotg *hsotg = s->private; - unsigned long flags; - u32 testmode = 0; - char buf[32]; - - if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) - return -EFAULT; - - if (!strncmp(buf, "test_j", 6)) - testmode = TEST_J; - else if (!strncmp(buf, "test_k", 6)) - testmode = TEST_K; - else if (!strncmp(buf, "test_se0_nak", 12)) - testmode = TEST_SE0_NAK; - else if (!strncmp(buf, "test_packet", 11)) - testmode = TEST_PACKET; - else if (!strncmp(buf, "test_force_enable", 17)) - testmode = TEST_FORCE_EN; - else - testmode = 0; - - spin_lock_irqsave(&hsotg->lock, flags); - s3c_hsotg_set_test_mode(hsotg, testmode); - spin_unlock_irqrestore(&hsotg->lock, flags); - return count; -} - -/** - * testmode_show - debugfs: show usb test mode state - * @seq: The seq file to write to. - * @v: Unused parameter. - * - * This debugfs entry shows which usb test mode is currently enabled. - */ -static int testmode_show(struct seq_file *s, void *unused) -{ - struct dwc2_hsotg *hsotg = s->private; - unsigned long flags; - int dctl; - - spin_lock_irqsave(&hsotg->lock, flags); - dctl = readl(hsotg->regs + DCTL); - dctl &= DCTL_TSTCTL_MASK; - dctl >>= DCTL_TSTCTL_SHIFT; - spin_unlock_irqrestore(&hsotg->lock, flags); - - switch (dctl) { - case 0: - seq_puts(s, "no test\n"); - break; - case TEST_J: - seq_puts(s, "test_j\n"); - break; - case TEST_K: - seq_puts(s, "test_k\n"); - break; - case TEST_SE0_NAK: - seq_puts(s, "test_se0_nak\n"); - break; - case TEST_PACKET: - seq_puts(s, "test_packet\n"); - break; - case TEST_FORCE_EN: - seq_puts(s, "test_force_enable\n"); - break; - default: - seq_printf(s, "UNKNOWN %d\n", dctl); - } - - return 0; -} - -static int testmode_open(struct inode *inode, struct file *file) -{ - return single_open(file, testmode_show, inode->i_private); -} - -static const struct file_operations testmode_fops = { - .owner = THIS_MODULE, - .open = testmode_open, - .write = testmode_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -/** - * state_show - debugfs: show overall driver and device state. - * @seq: The seq file to write to. - * @v: Unused parameter. - * - * This debugfs entry shows the overall state of the hardware and - * some general information about each of the endpoints available - * to the system. - */ -static int state_show(struct seq_file *seq, void *v) -{ - struct dwc2_hsotg *hsotg = seq->private; - void __iomem *regs = hsotg->regs; - int idx; - - seq_printf(seq, "DCFG=0x%08x, DCTL=0x%08x, DSTS=0x%08x\n", - readl(regs + DCFG), - readl(regs + DCTL), - readl(regs + DSTS)); - - seq_printf(seq, "DIEPMSK=0x%08x, DOEPMASK=0x%08x\n", - readl(regs + DIEPMSK), readl(regs + DOEPMSK)); - - seq_printf(seq, "GINTMSK=0x%08x, GINTSTS=0x%08x\n", - readl(regs + GINTMSK), - readl(regs + GINTSTS)); - - seq_printf(seq, "DAINTMSK=0x%08x, DAINT=0x%08x\n", - readl(regs + DAINTMSK), - readl(regs + DAINT)); - - seq_printf(seq, "GNPTXSTS=0x%08x, GRXSTSR=%08x\n", - readl(regs + GNPTXSTS), - readl(regs + GRXSTSR)); - - seq_puts(seq, "\nEndpoint status:\n"); - - for (idx = 0; idx < hsotg->num_of_eps; idx++) { - u32 in, out; - - in = readl(regs + DIEPCTL(idx)); - out = readl(regs + DOEPCTL(idx)); - - seq_printf(seq, "ep%d: DIEPCTL=0x%08x, DOEPCTL=0x%08x", - idx, in, out); - - in = readl(regs + DIEPTSIZ(idx)); - out = readl(regs + DOEPTSIZ(idx)); - - seq_printf(seq, ", DIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x", - in, out); - - seq_puts(seq, "\n"); - } - - return 0; -} - -static int state_open(struct inode *inode, struct file *file) -{ - return single_open(file, state_show, inode->i_private); -} - -static const struct file_operations state_fops = { - .owner = THIS_MODULE, - .open = state_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -/** - * fifo_show - debugfs: show the fifo information - * @seq: The seq_file to write data to. - * @v: Unused parameter. - * - * Show the FIFO information for the overall fifo and all the - * periodic transmission FIFOs. - */ -static int fifo_show(struct seq_file *seq, void *v) -{ - struct dwc2_hsotg *hsotg = seq->private; - void __iomem *regs = hsotg->regs; - u32 val; - int idx; - - seq_puts(seq, "Non-periodic FIFOs:\n"); - seq_printf(seq, "RXFIFO: Size %d\n", readl(regs + GRXFSIZ)); - - val = readl(regs + GNPTXFSIZ); - seq_printf(seq, "NPTXFIFO: Size %d, Start 0x%08x\n", - val >> FIFOSIZE_DEPTH_SHIFT, - val & FIFOSIZE_DEPTH_MASK); - - seq_puts(seq, "\nPeriodic TXFIFOs:\n"); - - for (idx = 1; idx < hsotg->num_of_eps; idx++) { - val = readl(regs + DPTXFSIZN(idx)); - - seq_printf(seq, "\tDPTXFIFO%2d: Size %d, Start 0x%08x\n", idx, - val >> FIFOSIZE_DEPTH_SHIFT, - val & FIFOSIZE_STARTADDR_MASK); - } - - return 0; -} - -static int fifo_open(struct inode *inode, struct file *file) -{ - return single_open(file, fifo_show, inode->i_private); -} - -static const struct file_operations fifo_fops = { - .owner = THIS_MODULE, - .open = fifo_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - - -static const char *decode_direction(int is_in) -{ - return is_in ? "in" : "out"; -} - -/** - * ep_show - debugfs: show the state of an endpoint. - * @seq: The seq_file to write data to. - * @v: Unused parameter. - * - * This debugfs entry shows the state of the given endpoint (one is - * registered for each available). - */ -static int ep_show(struct seq_file *seq, void *v) -{ - struct s3c_hsotg_ep *ep = seq->private; - struct dwc2_hsotg *hsotg = ep->parent; - struct s3c_hsotg_req *req; - void __iomem *regs = hsotg->regs; - int index = ep->index; - int show_limit = 15; - unsigned long flags; - - seq_printf(seq, "Endpoint index %d, named %s, dir %s:\n", - ep->index, ep->ep.name, decode_direction(ep->dir_in)); - - /* first show the register state */ - - seq_printf(seq, "\tDIEPCTL=0x%08x, DOEPCTL=0x%08x\n", - readl(regs + DIEPCTL(index)), - readl(regs + DOEPCTL(index))); - - seq_printf(seq, "\tDIEPDMA=0x%08x, DOEPDMA=0x%08x\n", - readl(regs + DIEPDMA(index)), - readl(regs + DOEPDMA(index))); - - seq_printf(seq, "\tDIEPINT=0x%08x, DOEPINT=0x%08x\n", - readl(regs + DIEPINT(index)), - readl(regs + DOEPINT(index))); - - seq_printf(seq, "\tDIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x\n", - readl(regs + DIEPTSIZ(index)), - readl(regs + DOEPTSIZ(index))); - - seq_puts(seq, "\n"); - seq_printf(seq, "mps %d\n", ep->ep.maxpacket); - seq_printf(seq, "total_data=%ld\n", ep->total_data); - - seq_printf(seq, "request list (%p,%p):\n", - ep->queue.next, ep->queue.prev); - - spin_lock_irqsave(&hsotg->lock, flags); - - list_for_each_entry(req, &ep->queue, queue) { - if (--show_limit < 0) { - seq_puts(seq, "not showing more requests...\n"); - break; - } - - seq_printf(seq, "%c req %p: %d bytes @%p, ", - req == ep->req ? '*' : ' ', - req, req->req.length, req->req.buf); - seq_printf(seq, "%d done, res %d\n", - req->req.actual, req->req.status); - } - - spin_unlock_irqrestore(&hsotg->lock, flags); - - return 0; -} - -static int ep_open(struct inode *inode, struct file *file) -{ - return single_open(file, ep_show, inode->i_private); -} - -static const struct file_operations ep_fops = { - .owner = THIS_MODULE, - .open = ep_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -/** - * s3c_hsotg_create_debug - create debugfs directory and files - * @hsotg: The driver state - * - * Create the debugfs files to allow the user to get information - * about the state of the system. The directory name is created - * with the same name as the device itself, in case we end up - * with multiple blocks in future systems. - */ -static void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg) -{ - struct dentry *root; - unsigned epidx; - - root = debugfs_create_dir(dev_name(hsotg->dev), NULL); - hsotg->debug_root = root; - if (IS_ERR(root)) { - dev_err(hsotg->dev, "cannot create debug root\n"); - return; - } - - /* create general state file */ - - hsotg->debug_file = debugfs_create_file("state", S_IRUGO, root, - hsotg, &state_fops); - - if (IS_ERR(hsotg->debug_file)) - dev_err(hsotg->dev, "%s: failed to create state\n", __func__); - - hsotg->debug_testmode = debugfs_create_file("testmode", - S_IRUGO | S_IWUSR, root, - hsotg, &testmode_fops); - - if (IS_ERR(hsotg->debug_testmode)) - dev_err(hsotg->dev, "%s: failed to create testmode\n", - __func__); - - hsotg->debug_fifo = debugfs_create_file("fifo", S_IRUGO, root, - hsotg, &fifo_fops); - - if (IS_ERR(hsotg->debug_fifo)) - dev_err(hsotg->dev, "%s: failed to create fifo\n", __func__); - - /* Create one file for each out endpoint */ - for (epidx = 0; epidx < hsotg->num_of_eps; epidx++) { - struct s3c_hsotg_ep *ep; - - ep = hsotg->eps_out[epidx]; - if (ep) { - ep->debugfs = debugfs_create_file(ep->name, S_IRUGO, - root, ep, &ep_fops); - - if (IS_ERR(ep->debugfs)) - dev_err(hsotg->dev, "failed to create %s debug file\n", - ep->name); - } - } - /* Create one file for each in endpoint. EP0 is handled with out eps */ - for (epidx = 1; epidx < hsotg->num_of_eps; epidx++) { - struct s3c_hsotg_ep *ep; - - ep = hsotg->eps_in[epidx]; - if (ep) { - ep->debugfs = debugfs_create_file(ep->name, S_IRUGO, - root, ep, &ep_fops); - - if (IS_ERR(ep->debugfs)) - dev_err(hsotg->dev, "failed to create %s debug file\n", - ep->name); - } - } -} - -/** - * s3c_hsotg_delete_debug - cleanup debugfs entries - * @hsotg: The driver state - * - * Cleanup (remove) the debugfs files for use on module exit. - */ -static void s3c_hsotg_delete_debug(struct dwc2_hsotg *hsotg) -{ - unsigned epidx; - - for (epidx = 0; epidx < hsotg->num_of_eps; epidx++) { - if (hsotg->eps_in[epidx]) - debugfs_remove(hsotg->eps_in[epidx]->debugfs); - if (hsotg->eps_out[epidx]) - debugfs_remove(hsotg->eps_out[epidx]->debugfs); - } - - debugfs_remove(hsotg->debug_file); - debugfs_remove(hsotg->debug_testmode); - debugfs_remove(hsotg->debug_fifo); - debugfs_remove(hsotg->debug_root); -} - #ifdef CONFIG_OF static void s3c_hsotg_of_probe(struct dwc2_hsotg *hsotg) { @@ -3896,6 +3520,8 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) hsotg->gadget.max_speed = USB_SPEED_HIGH; hsotg->gadget.ops = &s3c_hsotg_gadget_ops; hsotg->gadget.name = dev_name(dev); + if (hsotg->dr_mode == USB_DR_MODE_OTG) + hsotg->gadget.is_otg = 1; /* reset the system */ @@ -4028,8 +3654,6 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) if (ret) goto err_supplies; - s3c_hsotg_create_debug(hsotg); - s3c_hsotg_dump(hsotg); return 0; @@ -4041,7 +3665,6 @@ err_clk: return ret; } -EXPORT_SYMBOL_GPL(dwc2_gadget_init); /** * s3c_hsotg_remove - remove function for hsotg driver @@ -4050,18 +3673,19 @@ EXPORT_SYMBOL_GPL(dwc2_gadget_init); int s3c_hsotg_remove(struct dwc2_hsotg *hsotg) { usb_del_gadget_udc(&hsotg->gadget); - s3c_hsotg_delete_debug(hsotg); clk_disable_unprepare(hsotg->clk); return 0; } -EXPORT_SYMBOL_GPL(s3c_hsotg_remove); int s3c_hsotg_suspend(struct dwc2_hsotg *hsotg) { unsigned long flags; int ret = 0; + if (hsotg->lx_state != DWC2_L0) + return ret; + mutex_lock(&hsotg->init_mutex); if (hsotg->driver) { @@ -4095,13 +3719,15 @@ int s3c_hsotg_suspend(struct dwc2_hsotg *hsotg) return ret; } -EXPORT_SYMBOL_GPL(s3c_hsotg_suspend); int s3c_hsotg_resume(struct dwc2_hsotg *hsotg) { unsigned long flags; int ret = 0; + if (hsotg->lx_state == DWC2_L2) + return ret; + mutex_lock(&hsotg->init_mutex); if (hsotg->driver) { @@ -4124,4 +3750,3 @@ int s3c_hsotg_resume(struct dwc2_hsotg *hsotg) return ret; } -EXPORT_SYMBOL_GPL(s3c_hsotg_resume); diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 2b42851eac2..35a5b372af2 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -721,9 +721,7 @@ static int dwc2_hc_setup_align_buf(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, /* 3072 = 3 max-size Isoc packets */ buf_size = 3072; - qh->dw_align_buf = dma_alloc_coherent(hsotg->dev, buf_size, - &qh->dw_align_buf_dma, - GFP_ATOMIC); + qh->dw_align_buf = kmalloc(buf_size, GFP_ATOMIC | GFP_DMA); if (!qh->dw_align_buf) return -ENOMEM; qh->dw_align_buf_size = buf_size; @@ -748,6 +746,15 @@ static int dwc2_hc_setup_align_buf(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, } } + qh->dw_align_buf_dma = dma_map_single(hsotg->dev, + qh->dw_align_buf, qh->dw_align_buf_size, + chan->ep_is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (dma_mapping_error(hsotg->dev, qh->dw_align_buf_dma)) { + dev_err(hsotg->dev, "can't map align_buf\n"); + chan->align_buf = 0; + return -EINVAL; + } + chan->align_buf = qh->dw_align_buf_dma; return 0; } @@ -1774,6 +1781,15 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, /* Not supported */ break; + case USB_PORT_FEAT_TEST: + hprt0 = dwc2_read_hprt0(hsotg); + dev_dbg(hsotg->dev, + "SetPortFeature - USB_PORT_FEAT_TEST\n"); + hprt0 &= ~HPRT0_TSTCTL_MASK; + hprt0 |= (windex >> 8) << HPRT0_TSTCTL_SHIFT; + writel(hprt0, hsotg->regs + HPRT0); + break; + default: retval = -EINVAL; dev_err(hsotg->dev, @@ -2767,8 +2783,6 @@ static void dwc2_hcd_free(struct dwc2_hsotg *hsotg) destroy_workqueue(hsotg->wq_otg); } - kfree(hsotg->core_params); - hsotg->core_params = NULL; del_timer(&hsotg->wkp_timer); } @@ -2781,29 +2795,12 @@ static void dwc2_hcd_release(struct dwc2_hsotg *hsotg) } /* - * Sets all parameters to the given value. - * - * Assumes that the dwc2_core_params struct contains only integers. - */ -void dwc2_set_all_params(struct dwc2_core_params *params, int value) -{ - int *p = (int *)params; - size_t size = sizeof(*params) / sizeof(*p); - int i; - - for (i = 0; i < size; i++) - p[i] = value; -} -EXPORT_SYMBOL_GPL(dwc2_set_all_params); - -/* * Initializes the HCD. This function allocates memory for and initializes the * static parts of the usb_hcd and dwc2_hsotg structures. It also registers the * USB bus with the core and calls the hc_driver->start() function. It returns * a negative error on failure. */ -int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq, - const struct dwc2_core_params *params) +int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq) { struct usb_hcd *hcd; struct dwc2_host_chan *channel; @@ -2816,12 +2813,6 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq, dev_dbg(hsotg->dev, "DWC OTG HCD INIT\n"); - /* Detect config values from hardware */ - retval = dwc2_get_hwparams(hsotg); - - if (retval) - return retval; - retval = -ENOMEM; hcfg = readl(hsotg->regs + HCFG); @@ -2840,15 +2831,6 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq, hsotg->last_frame_num = HFNUM_MAX_FRNUM; #endif - hsotg->core_params = kzalloc(sizeof(*hsotg->core_params), GFP_KERNEL); - if (!hsotg->core_params) - goto error1; - - dwc2_set_all_params(hsotg->core_params, -1); - - /* Validate parameter values */ - dwc2_set_parameters(hsotg, params); - /* Check if the bus driver or platform code has setup a dma_mask */ if (hsotg->core_params->dma_enable > 0 && hsotg->dev->dma_mask == NULL) { @@ -2966,6 +2948,9 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq, /* Don't support SG list at this point */ hcd->self.sg_tablesize = 0; + if (!IS_ERR_OR_NULL(hsotg->uphy)) + otg_set_host(hsotg->uphy->otg, &hcd->self); + /* * Finish generic HCD initialization and start the HCD. This function * allocates the DMA buffer pool, registers the USB bus, requests the @@ -2998,7 +2983,6 @@ error1: dev_err(hsotg->dev, "%s() FAILED, returning %d\n", __func__, retval); return retval; } -EXPORT_SYMBOL_GPL(dwc2_hcd_init); /* * Removes the HCD. @@ -3019,6 +3003,9 @@ void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) return; } + if (!IS_ERR_OR_NULL(hsotg->uphy)) + otg_set_host(hsotg->uphy->otg, NULL); + usb_remove_hcd(hcd); hsotg->priv = NULL; dwc2_hcd_release(hsotg); @@ -3029,4 +3016,3 @@ void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) kfree(hsotg->frame_num_array); #endif } -EXPORT_SYMBOL_GPL(dwc2_hcd_remove); diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h index e69a843d892..7b5841c4003 100644 --- a/drivers/usb/dwc2/hcd.h +++ b/drivers/usb/dwc2/hcd.h @@ -451,13 +451,8 @@ static inline u8 dwc2_hcd_is_pipe_out(struct dwc2_hcd_pipe_info *pipe) return !dwc2_hcd_is_pipe_in(pipe); } -extern int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq, - const struct dwc2_core_params *params); +extern int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq); extern void dwc2_hcd_remove(struct dwc2_hsotg *hsotg); -extern void dwc2_set_parameters(struct dwc2_hsotg *hsotg, - const struct dwc2_core_params *params); -extern void dwc2_set_all_params(struct dwc2_core_params *params, int value); -extern int dwc2_get_hwparams(struct dwc2_hsotg *hsotg); /* Transaction Execution Functions */ extern enum dwc2_transaction_type dwc2_hcd_select_transactions( diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c index 551ba878b00..4cc95df4262 100644 --- a/drivers/usb/dwc2/hcd_intr.c +++ b/drivers/usb/dwc2/hcd_intr.c @@ -350,6 +350,9 @@ static void dwc2_port_intr(struct dwc2_hsotg *hsotg) dev_vdbg(hsotg->dev, "--Port Interrupt HPRT0=0x%08x Port Connect Detected--\n", hprt0); + if (hsotg->lx_state != DWC2_L0) + usb_hcd_resume_root_hub(hsotg->priv); + hsotg->flags.b.port_connect_status_change = 1; hsotg->flags.b.port_connect_status = 1; hprt0_modify |= HPRT0_CONNDET; @@ -463,10 +466,15 @@ static int dwc2_update_urb_state(struct dwc2_hsotg *hsotg, } /* Non DWORD-aligned buffer case handling */ - if (chan->align_buf && xfer_length && chan->ep_is_in) { + if (chan->align_buf && xfer_length) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); - memcpy(urb->buf + urb->actual_length, chan->qh->dw_align_buf, - xfer_length); + dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma, + chan->qh->dw_align_buf_size, + chan->ep_is_in ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (chan->ep_is_in) + memcpy(urb->buf + urb->actual_length, + chan->qh->dw_align_buf, xfer_length); } dev_vdbg(hsotg->dev, "urb->actual_length=%d xfer_length=%d\n", @@ -552,13 +560,18 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state( chan, chnum, qtd, halt_status, NULL); /* Non DWORD-aligned buffer case handling */ - if (chan->align_buf && frame_desc->actual_length && - chan->ep_is_in) { + if (chan->align_buf && frame_desc->actual_length) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); - memcpy(urb->buf + frame_desc->offset + - qtd->isoc_split_offset, chan->qh->dw_align_buf, - frame_desc->actual_length); + dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma, + chan->qh->dw_align_buf_size, + chan->ep_is_in ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (chan->ep_is_in) + memcpy(urb->buf + frame_desc->offset + + qtd->isoc_split_offset, + chan->qh->dw_align_buf, + frame_desc->actual_length); } break; case DWC2_HC_XFER_FRAME_OVERRUN: @@ -581,13 +594,18 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state( chan, chnum, qtd, halt_status, NULL); /* Non DWORD-aligned buffer case handling */ - if (chan->align_buf && frame_desc->actual_length && - chan->ep_is_in) { + if (chan->align_buf && frame_desc->actual_length) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); - memcpy(urb->buf + frame_desc->offset + - qtd->isoc_split_offset, chan->qh->dw_align_buf, - frame_desc->actual_length); + dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma, + chan->qh->dw_align_buf_size, + chan->ep_is_in ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (chan->ep_is_in) + memcpy(urb->buf + frame_desc->offset + + qtd->isoc_split_offset, + chan->qh->dw_align_buf, + frame_desc->actual_length); } /* Skip whole frame */ @@ -923,6 +941,8 @@ static int dwc2_xfercomp_isoc_split_in(struct dwc2_hsotg *hsotg, if (chan->align_buf) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); + dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma, + chan->qh->dw_align_buf_size, DMA_FROM_DEVICE); memcpy(qtd->urb->buf + frame_desc->offset + qtd->isoc_split_offset, chan->qh->dw_align_buf, len); } @@ -1152,8 +1172,14 @@ static void dwc2_update_urb_state_abn(struct dwc2_hsotg *hsotg, /* Non DWORD-aligned buffer case handling */ if (chan->align_buf && xfer_length && chan->ep_is_in) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); - memcpy(urb->buf + urb->actual_length, chan->qh->dw_align_buf, - xfer_length); + dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma, + chan->qh->dw_align_buf_size, + chan->ep_is_in ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (chan->ep_is_in) + memcpy(urb->buf + urb->actual_length, + chan->qh->dw_align_buf, + xfer_length); } urb->actual_length += xfer_length; @@ -1182,6 +1208,16 @@ static void dwc2_hc_nak_intr(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan, int chnum, struct dwc2_qtd *qtd) { + if (!qtd) { + dev_dbg(hsotg->dev, "%s: qtd is NULL\n", __func__); + return; + } + + if (!qtd->urb) { + dev_dbg(hsotg->dev, "%s: qtd->urb is NULL\n", __func__); + return; + } + if (dbg_hc(chan)) dev_vdbg(hsotg->dev, "--Host Channel %d Interrupt: NAK Received--\n", chnum); diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c index bb97838bc6c..46f26ee4493 100644 --- a/drivers/usb/dwc2/hcd_queue.c +++ b/drivers/usb/dwc2/hcd_queue.c @@ -229,11 +229,13 @@ static struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg, */ void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) { - if (hsotg->core_params->dma_desc_enable > 0) + if (hsotg->core_params->dma_desc_enable > 0) { dwc2_hcd_qh_free_ddma(hsotg, qh); - else if (qh->dw_align_buf) - dma_free_coherent(hsotg->dev, qh->dw_align_buf_size, - qh->dw_align_buf, qh->dw_align_buf_dma); + } else { + /* kfree(NULL) is safe */ + kfree(qh->dw_align_buf); + qh->dw_align_buf_dma = (dma_addr_t)0; + } kfree(qh); } diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 185663e0b5f..90935304185 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -47,6 +47,7 @@ #include "core.h" #include "hcd.h" +#include "debug.h" static const char dwc2_driver_name[] = "dwc2"; @@ -76,6 +77,8 @@ static const struct dwc2_core_params params_bcm2835 = { .reload_ctl = 0, .ahbcfg = 0x10, .uframe_sched = 0, + .external_id_pin_ctl = -1, + .hibernation = -1, }; static const struct dwc2_core_params params_rk3066 = { @@ -104,6 +107,8 @@ static const struct dwc2_core_params params_rk3066 = { .reload_ctl = -1, .ahbcfg = 0x7, /* INCR16 */ .uframe_sched = -1, + .external_id_pin_ctl = -1, + .hibernation = -1, }; /** @@ -121,6 +126,7 @@ static int dwc2_driver_remove(struct platform_device *dev) { struct dwc2_hsotg *hsotg = platform_get_drvdata(dev); + dwc2_debugfs_exit(hsotg); if (hsotg->hcd_enabled) dwc2_hcd_remove(hsotg); if (hsotg->gadget_enabled) @@ -237,6 +243,21 @@ static int dwc2_driver_probe(struct platform_device *dev) spin_lock_init(&hsotg->lock); mutex_init(&hsotg->init_mutex); + /* Detect config values from hardware */ + retval = dwc2_get_hwparams(hsotg); + if (retval) + return retval; + + hsotg->core_params = devm_kzalloc(&dev->dev, + sizeof(*hsotg->core_params), GFP_KERNEL); + if (!hsotg->core_params) + return -ENOMEM; + + dwc2_set_all_params(hsotg->core_params, -1); + + /* Validate parameter values */ + dwc2_set_parameters(hsotg, params); + if (hsotg->dr_mode != USB_DR_MODE_HOST) { retval = dwc2_gadget_init(hsotg, irq); if (retval) @@ -245,7 +266,7 @@ static int dwc2_driver_probe(struct platform_device *dev) } if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) { - retval = dwc2_hcd_init(hsotg, irq, params); + retval = dwc2_hcd_init(hsotg, irq); if (retval) { if (hsotg->gadget_enabled) s3c_hsotg_remove(hsotg); @@ -256,6 +277,8 @@ static int dwc2_driver_probe(struct platform_device *dev) platform_set_drvdata(dev, hsotg); + dwc2_debugfs_init(hsotg); + return retval; } diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 827c4f80379..473bfaa96c3 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -11,6 +11,13 @@ config USB_DWC3 if USB_DWC3 +config USB_DWC3_ULPI + bool "Register ULPI PHY Interface" + depends on USB_ULPI_BUS=y ||Â USB_ULPI_BUS=USB_DWC3 + help + Select this if you have ULPI type PHY attached to your DWC3 + controller. + choice bool "DWC3 Mode Selection" default USB_DWC3_DUAL_ROLE if (USB && USB_GADGET) diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 46172f47f02..c7076e37c4e 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -15,6 +15,10 @@ ifneq ($(filter y,$(CONFIG_USB_DWC3_GADGET) $(CONFIG_USB_DWC3_DUAL_ROLE)),) dwc3-y += gadget.o ep0.o endif +ifneq ($(CONFIG_USB_DWC3_ULPI),) + dwc3-y += ulpi.o +endif + ifneq ($(CONFIG_DEBUG_FS),) dwc3-y += debugfs.o endif diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 2bbab3d86ff..5c110d8e293 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -117,6 +117,33 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc) } /** + * dwc3_soft_reset - Issue soft reset + * @dwc: Pointer to our controller context structure + */ +static int dwc3_soft_reset(struct dwc3 *dwc) +{ + unsigned long timeout; + u32 reg; + + timeout = jiffies + msecs_to_jiffies(500); + dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST); + do { + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + if (!(reg & DWC3_DCTL_CSFTRST)) + break; + + if (time_after(jiffies, timeout)) { + dev_err(dwc->dev, "Reset Timed Out\n"); + return -ETIMEDOUT; + } + + cpu_relax(); + } while (true); + + return 0; +} + +/** * dwc3_free_one_event_buffer - Frees one event buffer * @dwc: Pointer to our controller context structure * @evt: Pointer to event buffer to be freed @@ -367,10 +394,15 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc) /** * dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core * @dwc: Pointer to our controller context structure + * + * Returns 0 on success. The USB PHY interfaces are configured but not + * initialized. The PHY interfaces and the PHYs get initialized together with + * the core in dwc3_core_init. */ -static void dwc3_phy_setup(struct dwc3 *dwc) +static int dwc3_phy_setup(struct dwc3 *dwc) { u32 reg; + int ret; reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); @@ -409,10 +441,41 @@ static void dwc3_phy_setup(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); - mdelay(100); - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + /* Select the HS PHY interface */ + switch (DWC3_GHWPARAMS3_HSPHY_IFC(dwc->hwparams.hwparams3)) { + case DWC3_GHWPARAMS3_HSPHY_IFC_UTMI_ULPI: + if (!strncmp(dwc->hsphy_interface, "utmi", 4)) { + reg &= ~DWC3_GUSB2PHYCFG_ULPI_UTMI; + break; + } else if (!strncmp(dwc->hsphy_interface, "ulpi", 4)) { + reg |= DWC3_GUSB2PHYCFG_ULPI_UTMI; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + } else { + dev_warn(dwc->dev, "HSPHY Interface not defined\n"); + + /* Relying on default value. */ + if (!(reg & DWC3_GUSB2PHYCFG_ULPI_UTMI)) + break; + } + /* FALLTHROUGH */ + case DWC3_GHWPARAMS3_HSPHY_IFC_ULPI: + /* Making sure the interface and PHY are operational */ + ret = dwc3_soft_reset(dwc); + if (ret) + return ret; + + udelay(1); + + ret = dwc3_ulpi_init(dwc); + if (ret) + return ret; + /* FALLTHROUGH */ + default: + break; + } + /* * Above 1.94a, it is recommended to set DWC3_GUSB2PHYCFG_SUSPHY to * '0' during coreConsultant configuration. So default value will @@ -427,7 +490,7 @@ static void dwc3_phy_setup(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); - mdelay(100); + return 0; } /** @@ -438,7 +501,6 @@ static void dwc3_phy_setup(struct dwc3 *dwc) */ static int dwc3_core_init(struct dwc3 *dwc) { - unsigned long timeout; u32 hwparams4 = dwc->hwparams.hwparams4; u32 reg; int ret; @@ -466,21 +528,9 @@ static int dwc3_core_init(struct dwc3 *dwc) } /* issue device SoftReset too */ - timeout = jiffies + msecs_to_jiffies(500); - dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST); - do { - reg = dwc3_readl(dwc->regs, DWC3_DCTL); - if (!(reg & DWC3_DCTL_CSFTRST)) - break; - - if (time_after(jiffies, timeout)) { - dev_err(dwc->dev, "Reset Timed Out\n"); - ret = -ETIMEDOUT; - goto err0; - } - - cpu_relax(); - } while (true); + ret = dwc3_soft_reset(dwc); + if (ret) + goto err0; ret = dwc3_core_soft_reset(dwc); if (ret) @@ -555,8 +605,6 @@ static int dwc3_core_init(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_GCTL, reg); - dwc3_phy_setup(dwc); - ret = dwc3_alloc_scratch_buffers(dwc); if (ret) goto err1; @@ -836,6 +884,8 @@ static int dwc3_probe(struct platform_device *pdev) "snps,tx_de_emphasis_quirk"); of_property_read_u8(node, "snps,tx_de_emphasis", &tx_de_emphasis); + of_property_read_string(node, "snps,hsphy_interface", + &dwc->hsphy_interface); } else if (pdata) { dwc->maximum_speed = pdata->maximum_speed; dwc->has_lpm_erratum = pdata->has_lpm_erratum; @@ -863,6 +913,8 @@ static int dwc3_probe(struct platform_device *pdev) dwc->tx_de_emphasis_quirk = pdata->tx_de_emphasis_quirk; if (pdata->tx_de_emphasis) tx_de_emphasis = pdata->tx_de_emphasis; + + dwc->hsphy_interface = pdata->hsphy_interface; } /* default to superspeed if no maximum_speed passed */ @@ -875,12 +927,18 @@ static int dwc3_probe(struct platform_device *pdev) dwc->hird_threshold = hird_threshold | (dwc->is_utmi_l1_suspend << 4); + platform_set_drvdata(pdev, dwc); + dwc3_cache_hwparams(dwc); + + ret = dwc3_phy_setup(dwc); + if (ret) + goto err0; + ret = dwc3_core_get_phy(dwc); if (ret) goto err0; spin_lock_init(&dwc->lock); - platform_set_drvdata(pdev, dwc); if (!dev->dma_mask) { dev->dma_mask = dev->parent->dma_mask; @@ -892,8 +950,6 @@ static int dwc3_probe(struct platform_device *pdev) pm_runtime_get_sync(dev); pm_runtime_forbid(dev); - dwc3_cache_hwparams(dwc); - ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE); if (ret) { dev_err(dwc->dev, "failed to allocate event buffers\n"); @@ -964,6 +1020,7 @@ err2: err1: dwc3_free_event_buffers(dwc); + dwc3_ulpi_exit(dwc); err0: /* @@ -999,6 +1056,7 @@ static int dwc3_remove(struct platform_device *pdev) phy_power_off(dwc->usb3_generic_phy); dwc3_core_exit(dwc); + dwc3_ulpi_exit(dwc); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index c0eafa6fd40..04477888458 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -30,6 +30,7 @@ #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> #include <linux/usb/otg.h> +#include <linux/ulpi/interface.h> #include <linux/phy/phy.h> @@ -173,6 +174,15 @@ /* Global USB2 PHY Configuration Register */ #define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31) #define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6) +#define DWC3_GUSB2PHYCFG_ULPI_UTMI (1 << 4) + +/* Global USB2 PHY Vendor Control Register */ +#define DWC3_GUSB2PHYACC_NEWREGREQ (1 << 25) +#define DWC3_GUSB2PHYACC_BUSY (1 << 23) +#define DWC3_GUSB2PHYACC_WRITE (1 << 22) +#define DWC3_GUSB2PHYACC_ADDR(n) (n << 16) +#define DWC3_GUSB2PHYACC_EXTEND_ADDR(n) (n << 8) +#define DWC3_GUSB2PHYACC_DATA(n) (n & 0xff) /* Global USB3 PIPE Control Register */ #define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31) @@ -652,6 +662,7 @@ struct dwc3_scratchpad_array { * @usb3_phy: pointer to USB3 PHY * @usb2_generic_phy: pointer to USB2 PHY * @usb3_generic_phy: pointer to USB3 PHY + * @ulpi: pointer to ulpi interface * @dcfg: saved contents of DCFG register * @gctl: saved contents of GCTL register * @isoch_delay: wValue from Set Isochronous Delay request; @@ -673,6 +684,7 @@ struct dwc3_scratchpad_array { * @test_mode_nr: test feature selector * @lpm_nyet_threshold: LPM NYET response threshold * @hird_threshold: HIRD threshold + * @hsphy_interface: "utmi" or "ulpi" * @delayed_status: true when gadget driver asks for delayed status * @ep0_bounced: true when we used bounce buffer * @ep0_expect_in: true when we expect a DATA IN transfer @@ -739,6 +751,8 @@ struct dwc3 { struct phy *usb2_generic_phy; struct phy *usb3_generic_phy; + struct ulpi *ulpi; + void __iomem *regs; size_t regs_size; @@ -800,6 +814,8 @@ struct dwc3 { u8 lpm_nyet_threshold; u8 hird_threshold; + const char *hsphy_interface; + unsigned delayed_status:1; unsigned ep0_bounced:1; unsigned ep0_expect_in:1; @@ -1035,4 +1051,14 @@ static inline int dwc3_gadget_resume(struct dwc3 *dwc) } #endif /* !IS_ENABLED(CONFIG_USB_DWC3_HOST) */ +#if IS_ENABLED(CONFIG_USB_DWC3_ULPI) +int dwc3_ulpi_init(struct dwc3 *dwc); +void dwc3_ulpi_exit(struct dwc3 *dwc); +#else +static inline int dwc3_ulpi_init(struct dwc3 *dwc) +{ return 0; } +static inline void dwc3_ulpi_exit(struct dwc3 *dwc) +{ } +#endif + #endif /* __DRIVERS_USB_DWC3_CORE_H */ diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index b773fb53d6a..27e4fc896e9 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -21,6 +21,8 @@ #include <linux/slab.h> #include <linux/pci.h> #include <linux/platform_device.h> +#include <linux/gpio/consumer.h> +#include <linux/acpi.h> #include "platform_data.h" @@ -31,6 +33,15 @@ #define PCI_DEVICE_ID_INTEL_SPTLP 0x9d30 #define PCI_DEVICE_ID_INTEL_SPTH 0xa130 +static const struct acpi_gpio_params reset_gpios = { 0, 0, false }; +static const struct acpi_gpio_params cs_gpios = { 1, 0, false }; + +static const struct acpi_gpio_mapping acpi_dwc3_byt_gpios[] = { + { "reset-gpios", &reset_gpios, 1 }, + { "cs-gpios", &cs_gpios, 1 }, + { }, +}; + static int dwc3_pci_quirks(struct pci_dev *pdev) { if (pdev->vendor == PCI_VENDOR_ID_AMD && @@ -65,6 +76,30 @@ static int dwc3_pci_quirks(struct pci_dev *pdev) sizeof(pdata)); } + if (pdev->vendor == PCI_VENDOR_ID_INTEL && + pdev->device == PCI_DEVICE_ID_INTEL_BYT) { + struct gpio_desc *gpio; + + acpi_dev_add_driver_gpios(ACPI_COMPANION(&pdev->dev), + acpi_dwc3_byt_gpios); + + /* These GPIOs will turn on the USB2 PHY */ + gpio = gpiod_get(&pdev->dev, "cs"); + if (!IS_ERR(gpio)) { + gpiod_direction_output(gpio, 0); + gpiod_set_value_cansleep(gpio, 1); + gpiod_put(gpio); + } + + gpio = gpiod_get(&pdev->dev, "reset"); + if (!IS_ERR(gpio)) { + gpiod_direction_output(gpio, 0); + gpiod_set_value_cansleep(gpio, 1); + gpiod_put(gpio); + usleep_range(10000, 11000); + } + } + return 0; } @@ -128,6 +163,7 @@ err: static void dwc3_pci_remove(struct pci_dev *pci) { + acpi_dev_remove_driver_gpios(ACPI_COMPANION(&pci->dev)); platform_device_unregister(pci_get_drvdata(pci)); } diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 8946c32047e..333a7c0078f 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -291,6 +291,8 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param) dwc3_trace(trace_dwc3_gadget, "Command Complete --> %d", DWC3_DGCMD_STATUS(reg)); + if (DWC3_DGCMD_STATUS(reg)) + return -EINVAL; return 0; } @@ -328,6 +330,8 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, dwc3_trace(trace_dwc3_gadget, "Command Complete --> %d", DWC3_DEPCMD_STATUS(reg)); + if (DWC3_DEPCMD_STATUS(reg)) + return -EINVAL; return 0; } @@ -1902,12 +1906,16 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc, { unsigned status = 0; int clean_busy; + u32 is_xfer_complete; + + is_xfer_complete = (event->endpoint_event == DWC3_DEPEVT_XFERCOMPLETE); if (event->status & DEPEVT_STATUS_BUSERR) status = -ECONNRESET; clean_busy = dwc3_cleanup_done_reqs(dwc, dep, event, status); - if (clean_busy) + if (clean_busy && (is_xfer_complete || + usb_endpoint_xfer_isoc(dep->endpoint.desc))) dep->flags &= ~DWC3_EP_BUSY; /* diff --git a/drivers/usb/dwc3/platform_data.h b/drivers/usb/dwc3/platform_data.h index a2bd464be82..d3614ecbb9c 100644 --- a/drivers/usb/dwc3/platform_data.h +++ b/drivers/usb/dwc3/platform_data.h @@ -45,4 +45,6 @@ struct dwc3_platform_data { unsigned tx_de_emphasis_quirk:1; unsigned tx_de_emphasis:2; + + const char *hsphy_interface; }; diff --git a/drivers/usb/dwc3/ulpi.c b/drivers/usb/dwc3/ulpi.c new file mode 100644 index 00000000000..ec004c6d76f --- /dev/null +++ b/drivers/usb/dwc3/ulpi.c @@ -0,0 +1,91 @@ +/** + * ulpi.c - DesignWare USB3 Controller's ULPI PHY interface + * + * Copyright (C) 2015 Intel Corporation + * + * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/ulpi/regs.h> + +#include "core.h" +#include "io.h" + +#define DWC3_ULPI_ADDR(a) \ + ((a >= ULPI_EXT_VENDOR_SPECIFIC) ? \ + DWC3_GUSB2PHYACC_ADDR(ULPI_ACCESS_EXTENDED) | \ + DWC3_GUSB2PHYACC_EXTEND_ADDR(a) : DWC3_GUSB2PHYACC_ADDR(a)) + +static int dwc3_ulpi_busyloop(struct dwc3 *dwc) +{ + unsigned count = 1000; + u32 reg; + + while (count--) { + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0)); + if (!(reg & DWC3_GUSB2PHYACC_BUSY)) + return 0; + cpu_relax(); + } + + return -ETIMEDOUT; +} + +static int dwc3_ulpi_read(struct ulpi_ops *ops, u8 addr) +{ + struct dwc3 *dwc = dev_get_drvdata(ops->dev); + u32 reg; + int ret; + + reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr); + dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg); + + ret = dwc3_ulpi_busyloop(dwc); + if (ret) + return ret; + + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0)); + + return DWC3_GUSB2PHYACC_DATA(reg); +} + +static int dwc3_ulpi_write(struct ulpi_ops *ops, u8 addr, u8 val) +{ + struct dwc3 *dwc = dev_get_drvdata(ops->dev); + u32 reg; + + reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr); + reg |= DWC3_GUSB2PHYACC_WRITE | val; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg); + + return dwc3_ulpi_busyloop(dwc); +} + +static struct ulpi_ops dwc3_ulpi_ops = { + .read = dwc3_ulpi_read, + .write = dwc3_ulpi_write, +}; + +int dwc3_ulpi_init(struct dwc3 *dwc) +{ + /* Register the interface */ + dwc->ulpi = ulpi_register_interface(dwc->dev, &dwc3_ulpi_ops); + if (IS_ERR(dwc->ulpi)) { + dev_err(dwc->dev, "failed to register ULPI interface"); + return PTR_ERR(dwc->ulpi); + } + + return 0; +} + +void dwc3_ulpi_exit(struct dwc3 *dwc) +{ + if (dwc->ulpi) { + ulpi_unregister_interface(dwc->ulpi); + dwc->ulpi = NULL; + } +} diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index 0567cca1465..919cdfdda78 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -258,15 +258,25 @@ struct usb_ep *usb_ep_autoconfig_ss( /* First, apply chip-specific "best usage" knowledge. * This might make a good usb_gadget_ops hook ... */ - if (gadget_is_net2280 (gadget) && type == USB_ENDPOINT_XFER_INT) { - /* ep-e, ep-f are PIO with only 64 byte fifos */ - ep = find_ep (gadget, "ep-e"); - if (ep && ep_matches(gadget, ep, desc, ep_comp)) - goto found_ep; - ep = find_ep (gadget, "ep-f"); + if (gadget_is_net2280(gadget)) { + char name[8]; + + if (type == USB_ENDPOINT_XFER_INT) { + /* ep-e, ep-f are PIO with only 64 byte fifos */ + ep = find_ep(gadget, "ep-e"); + if (ep && ep_matches(gadget, ep, desc, ep_comp)) + goto found_ep; + ep = find_ep(gadget, "ep-f"); + if (ep && ep_matches(gadget, ep, desc, ep_comp)) + goto found_ep; + } + + /* USB3380: use same address for usb and hardware endpoints */ + snprintf(name, sizeof(name), "ep%d%s", usb_endpoint_num(desc), + usb_endpoint_dir_in(desc) ? "in" : "out"); + ep = find_ep(gadget, name); if (ep && ep_matches(gadget, ep, desc, ep_comp)) goto found_ep; - } else if (gadget_is_goku (gadget)) { if (USB_ENDPOINT_XFER_INT == type) { /* single buffering is enough */ diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index 042619ae47c..abc817f4a1a 100644 --- a/drivers/usb/gadget/function/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -86,7 +86,7 @@ struct f_rndis { u8 ethaddr[ETH_ALEN]; u32 vendorID; const char *manufacturer; - int config; + struct rndis_params *params; struct usb_ep *notify; struct usb_request *notify_req; @@ -464,7 +464,7 @@ static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req) /* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */ // spin_lock(&dev->lock); - status = rndis_msg_parser(rndis->config, (u8 *) req->buf); + status = rndis_msg_parser(rndis->params, (u8 *) req->buf); if (status < 0) pr_err("RNDIS command error %d, %d/%d\n", status, req->actual, req->length); @@ -525,12 +525,12 @@ rndis_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) u32 n; /* return the result */ - buf = rndis_get_next_response(rndis->config, &n); + buf = rndis_get_next_response(rndis->params, &n); if (buf) { memcpy(req->buf, buf, n); req->complete = rndis_response_complete; req->context = rndis; - rndis_free_response(rndis->config, buf); + rndis_free_response(rndis->params, buf); value = n; } /* else stalls ... spec says to avoid that */ @@ -623,7 +623,7 @@ static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (IS_ERR(net)) return PTR_ERR(net); - rndis_set_param_dev(rndis->config, net, + rndis_set_param_dev(rndis->params, net, &rndis->port.cdc_filter); } else goto fail; @@ -643,7 +643,7 @@ static void rndis_disable(struct usb_function *f) DBG(cdev, "rndis deactivated\n"); - rndis_uninit(rndis->config); + rndis_uninit(rndis->params); gether_disconnect(&rndis->port); usb_ep_disable(rndis->notify); @@ -666,9 +666,9 @@ static void rndis_open(struct gether *geth) DBG(cdev, "%s\n", __func__); - rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, + rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3, bitrate(cdev->gadget) / 100); - rndis_signal_connect(rndis->config); + rndis_signal_connect(rndis->params); } static void rndis_close(struct gether *geth) @@ -677,8 +677,8 @@ static void rndis_close(struct gether *geth) DBG(geth->func.config->cdev, "%s\n", __func__); - rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0); - rndis_signal_disconnect(rndis->config); + rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3, 0); + rndis_signal_disconnect(rndis->params); } /*-------------------------------------------------------------------------*/ @@ -822,12 +822,12 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) rndis->port.open = rndis_open; rndis->port.close = rndis_close; - rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0); - rndis_set_host_mac(rndis->config, rndis->ethaddr); - rndis_set_max_pkt_xfer(rndis->config, rndis_ul_max_pkt_per_xfer); + rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3, 0); + rndis_set_host_mac(rndis->params, rndis->ethaddr); + rndis_set_max_pkt_xfer(rndis->params, rndis_ul_max_pkt_per_xfer); if (rndis->manufacturer && rndis->vendorID && - rndis_set_param_vendor(rndis->config, rndis->vendorID, + rndis_set_param_vendor(rndis->params, rndis->vendorID, rndis->manufacturer)) { status = -EINVAL; goto fail_free_descs; @@ -971,7 +971,7 @@ static void rndis_free(struct usb_function *f) struct f_rndis_opts *opts; rndis = func_to_rndis(f); - rndis_deregister(rndis->config); + rndis_deregister(rndis->params); opts = container_of(f->fi, struct f_rndis_opts, func_inst); kfree(rndis); mutex_lock(&opts->lock); @@ -995,7 +995,7 @@ static struct usb_function *rndis_alloc(struct usb_function_instance *fi) { struct f_rndis *rndis; struct f_rndis_opts *opts; - int status; + struct rndis_params *params; /* allocate and initialize one new instance */ rndis = kzalloc(sizeof(*rndis), GFP_KERNEL); @@ -1031,36 +1031,16 @@ static struct usb_function *rndis_alloc(struct usb_function_instance *fi) rndis->port.func.disable = rndis_disable; rndis->port.func.free_func = rndis_free; - status = rndis_register(rndis_response_available, rndis); - if (status < 0) { + params = rndis_register(rndis_response_available, rndis); + if (IS_ERR(params)) { kfree(rndis); - return ERR_PTR(status); + return ERR_CAST(params); } - rndis->config = status; + rndis->params = params; return &rndis->port.func; } -DECLARE_USB_FUNCTION(rndis, rndis_alloc_inst, rndis_alloc); - -static int __init rndis_mod_init(void) -{ - int ret; - - ret = rndis_init(); - if (ret) - return ret; - - return usb_function_register(&rndisusb_func); -} -module_init(rndis_mod_init); - -static void __exit rndis_mod_exit(void) -{ - usb_function_unregister(&rndisusb_func); - rndis_exit(); -} -module_exit(rndis_mod_exit); - +DECLARE_USB_FUNCTION_INIT(rndis, rndis_alloc_inst, rndis_alloc); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Brownell"); diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c index 1b468a8297b..dfdc41c5811 100644 --- a/drivers/usb/gadget/function/rndis.c +++ b/drivers/usb/gadget/function/rndis.c @@ -25,6 +25,7 @@ #include <linux/moduleparam.h> #include <linux/kernel.h> #include <linux/errno.h> +#include <linux/idr.h> #include <linux/list.h> #include <linux/proc_fs.h> #include <linux/slab.h> @@ -57,7 +58,7 @@ MODULE_PARM_DESC (rndis_debug, "enable debugging"); #define rndis_debug 0 #endif -#define RNDIS_MAX_CONFIGS 1 +#ifdef CONFIG_USB_GADGET_DEBUG_FILES int rndis_ul_max_pkt_per_xfer_rcvd; module_param(rndis_ul_max_pkt_per_xfer_rcvd, int, S_IRUGO); @@ -69,15 +70,24 @@ module_param(rndis_ul_max_xfer_size_rcvd, int, S_IRUGO); MODULE_PARM_DESC(rndis_ul_max_xfer_size_rcvd, "Max size of bus transfer received"); +#define NAME_TEMPLATE "driver/rndis-%03d" -static rndis_params rndis_per_dev_params[RNDIS_MAX_CONFIGS]; +#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ + +static DEFINE_IDA(rndis_ida); /* Driver Version */ static const __le32 rndis_driver_version = cpu_to_le32(1); /* Function Prototypes */ -static rndis_resp_t *rndis_add_response(int configNr, u32 length); +static rndis_resp_t *rndis_add_response(struct rndis_params *params, + u32 length); + +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + +static const struct file_operations rndis_proc_fops; +#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ /* supported OIDs */ static const u32 oid_supported_list[] = @@ -170,7 +180,7 @@ static const u32 oid_supported_list[] = /* NDIS Functions */ -static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, +static int gen_ndis_query_resp(struct rndis_params *params, u32 OID, u8 *buf, unsigned buf_len, rndis_resp_t *r) { int retval = -ENOTSUPP; @@ -202,7 +212,7 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, outbuf = (__le32 *)&resp[1]; resp->InformationBufferOffset = cpu_to_le32(16); - net = rndis_per_dev_params[configNr].dev; + net = params->dev; stats = dev_get_stats(net, &temp); switch (OID) { @@ -235,7 +245,7 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, /* mandatory */ case RNDIS_OID_GEN_MEDIA_SUPPORTED: pr_debug("%s: RNDIS_OID_GEN_MEDIA_SUPPORTED\n", __func__); - *outbuf = cpu_to_le32(rndis_per_dev_params[configNr].medium); + *outbuf = cpu_to_le32(params->medium); retval = 0; break; @@ -243,16 +253,15 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, case RNDIS_OID_GEN_MEDIA_IN_USE: pr_debug("%s: RNDIS_OID_GEN_MEDIA_IN_USE\n", __func__); /* one medium, one transport... (maybe you do it better) */ - *outbuf = cpu_to_le32(rndis_per_dev_params[configNr].medium); + *outbuf = cpu_to_le32(params->medium); retval = 0; break; /* mandatory */ case RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE: pr_debug("%s: RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE\n", __func__); - if (rndis_per_dev_params[configNr].dev) { - *outbuf = cpu_to_le32( - rndis_per_dev_params[configNr].dev->mtu); + if (params->dev) { + *outbuf = cpu_to_le32(params->dev->mtu); retval = 0; } break; @@ -261,21 +270,18 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, case RNDIS_OID_GEN_LINK_SPEED: if (rndis_debug > 1) pr_debug("%s: RNDIS_OID_GEN_LINK_SPEED\n", __func__); - if (rndis_per_dev_params[configNr].media_state - == RNDIS_MEDIA_STATE_DISCONNECTED) + if (params->media_state == RNDIS_MEDIA_STATE_DISCONNECTED) *outbuf = cpu_to_le32(0); else - *outbuf = cpu_to_le32( - rndis_per_dev_params[configNr].speed); + *outbuf = cpu_to_le32(params->speed); retval = 0; break; /* mandatory */ case RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE: pr_debug("%s: RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE\n", __func__); - if (rndis_per_dev_params[configNr].dev) { - *outbuf = cpu_to_le32( - rndis_per_dev_params[configNr].dev->mtu); + if (params->dev) { + *outbuf = cpu_to_le32(params->dev->mtu); retval = 0; } break; @@ -283,9 +289,8 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, /* mandatory */ case RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE: pr_debug("%s: RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE\n", __func__); - if (rndis_per_dev_params[configNr].dev) { - *outbuf = cpu_to_le32( - rndis_per_dev_params[configNr].dev->mtu); + if (params->dev) { + *outbuf = cpu_to_le32(params->dev->mtu); retval = 0; } break; @@ -293,20 +298,16 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, /* mandatory */ case RNDIS_OID_GEN_VENDOR_ID: pr_debug("%s: RNDIS_OID_GEN_VENDOR_ID\n", __func__); - *outbuf = cpu_to_le32( - rndis_per_dev_params[configNr].vendorID); + *outbuf = cpu_to_le32(params->vendorID); retval = 0; break; /* mandatory */ case RNDIS_OID_GEN_VENDOR_DESCRIPTION: pr_debug("%s: RNDIS_OID_GEN_VENDOR_DESCRIPTION\n", __func__); - if (rndis_per_dev_params[configNr].vendorDescr) { - length = strlen(rndis_per_dev_params[configNr]. - vendorDescr); - memcpy(outbuf, - rndis_per_dev_params[configNr].vendorDescr, - length); + if (params->vendorDescr) { + length = strlen(params->vendorDescr); + memcpy(outbuf, params->vendorDescr, length); } else { outbuf[0] = 0; } @@ -323,7 +324,7 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, /* mandatory */ case RNDIS_OID_GEN_CURRENT_PACKET_FILTER: pr_debug("%s: RNDIS_OID_GEN_CURRENT_PACKET_FILTER\n", __func__); - *outbuf = cpu_to_le32(*rndis_per_dev_params[configNr].filter); + *outbuf = cpu_to_le32(*params->filter); retval = 0; break; @@ -338,8 +339,7 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, case RNDIS_OID_GEN_MEDIA_CONNECT_STATUS: if (rndis_debug > 1) pr_debug("%s: RNDIS_OID_GEN_MEDIA_CONNECT_STATUS\n", __func__); - *outbuf = cpu_to_le32(rndis_per_dev_params[configNr] - .media_state); + *outbuf = cpu_to_le32(params->media_state); retval = 0; break; @@ -419,11 +419,9 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, /* mandatory */ case RNDIS_OID_802_3_PERMANENT_ADDRESS: pr_debug("%s: RNDIS_OID_802_3_PERMANENT_ADDRESS\n", __func__); - if (rndis_per_dev_params[configNr].dev) { + if (params->dev) { length = ETH_ALEN; - memcpy(outbuf, - rndis_per_dev_params[configNr].host_mac, - length); + memcpy(outbuf, params->host_mac, length); retval = 0; } break; @@ -431,11 +429,9 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, /* mandatory */ case RNDIS_OID_802_3_CURRENT_ADDRESS: pr_debug("%s: RNDIS_OID_802_3_CURRENT_ADDRESS\n", __func__); - if (rndis_per_dev_params[configNr].dev) { + if (params->dev) { length = ETH_ALEN; - memcpy(outbuf, - rndis_per_dev_params [configNr].host_mac, - length); + memcpy(outbuf, params->host_mac, length); retval = 0; } break; @@ -500,12 +496,11 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, return retval; } -static int gen_ndis_set_resp(u8 configNr, u32 OID, u8 *buf, u32 buf_len, - rndis_resp_t *r) +static int gen_ndis_set_resp(struct rndis_params *params, u32 OID, + u8 *buf, u32 buf_len, rndis_resp_t *r) { rndis_set_cmplt_type *resp; int i, retval = -ENOTSUPP; - struct rndis_params *params; if (!r) return -ENOMEM; @@ -524,7 +519,6 @@ static int gen_ndis_set_resp(u8 configNr, u32 OID, u8 *buf, u32 buf_len, } } - params = &rndis_per_dev_params[configNr]; switch (OID) { case RNDIS_OID_GEN_CURRENT_PACKET_FILTER: @@ -573,16 +567,16 @@ static int gen_ndis_set_resp(u8 configNr, u32 OID, u8 *buf, u32 buf_len, * Response Functions */ -static int rndis_init_response(int configNr, rndis_init_msg_type *buf) +static int rndis_init_response(struct rndis_params *params, + rndis_init_msg_type *buf) { rndis_init_cmplt_type *resp; rndis_resp_t *r; - struct rndis_params *params = rndis_per_dev_params + configNr; if (!params->dev) return -ENOTSUPP; - r = rndis_add_response(configNr, sizeof(rndis_init_cmplt_type)); + r = rndis_add_response(params, sizeof(rndis_init_cmplt_type)); if (!r) return -ENOMEM; resp = (rndis_init_cmplt_type *)r->buf; @@ -609,11 +603,11 @@ static int rndis_init_response(int configNr, rndis_init_msg_type *buf) return 0; } -static int rndis_query_response(int configNr, rndis_query_msg_type *buf) +static int rndis_query_response(struct rndis_params *params, + rndis_query_msg_type *buf) { rndis_query_cmplt_type *resp; rndis_resp_t *r; - struct rndis_params *params = rndis_per_dev_params + configNr; /* pr_debug("%s: OID = %08X\n", __func__, cpu_to_le32(buf->OID)); */ if (!params->dev) @@ -625,7 +619,7 @@ static int rndis_query_response(int configNr, rndis_query_msg_type *buf) * rndis_query_cmplt_type followed by data. * oid_supported_list is the largest data reply */ - r = rndis_add_response(configNr, + r = rndis_add_response(params, sizeof(oid_supported_list) + sizeof(rndis_query_cmplt_type)); if (!r) return -ENOMEM; @@ -634,7 +628,7 @@ static int rndis_query_response(int configNr, rndis_query_msg_type *buf) resp->MessageType = cpu_to_le32(RNDIS_MSG_QUERY_C); resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ - if (gen_ndis_query_resp(configNr, le32_to_cpu(buf->OID), + if (gen_ndis_query_resp(params, le32_to_cpu(buf->OID), le32_to_cpu(buf->InformationBufferOffset) + 8 + (u8 *)buf, le32_to_cpu(buf->InformationBufferLength), @@ -651,14 +645,14 @@ static int rndis_query_response(int configNr, rndis_query_msg_type *buf) return 0; } -static int rndis_set_response(int configNr, rndis_set_msg_type *buf) +static int rndis_set_response(struct rndis_params *params, + rndis_set_msg_type *buf) { u32 BufLength, BufOffset; rndis_set_cmplt_type *resp; rndis_resp_t *r; - struct rndis_params *params = rndis_per_dev_params + configNr; - r = rndis_add_response(configNr, sizeof(rndis_set_cmplt_type)); + r = rndis_add_response(params, sizeof(rndis_set_cmplt_type)); if (!r) return -ENOMEM; resp = (rndis_set_cmplt_type *)r->buf; @@ -681,7 +675,7 @@ static int rndis_set_response(int configNr, rndis_set_msg_type *buf) resp->MessageType = cpu_to_le32(RNDIS_MSG_SET_C); resp->MessageLength = cpu_to_le32(16); resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ - if (gen_ndis_set_resp(configNr, le32_to_cpu(buf->OID), + if (gen_ndis_set_resp(params, le32_to_cpu(buf->OID), ((u8 *)buf) + 8 + BufOffset, BufLength, r)) resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED); else @@ -691,19 +685,19 @@ static int rndis_set_response(int configNr, rndis_set_msg_type *buf) return 0; } -static int rndis_reset_response(int configNr, rndis_reset_msg_type *buf) +static int rndis_reset_response(struct rndis_params *params, + rndis_reset_msg_type *buf) { rndis_reset_cmplt_type *resp; rndis_resp_t *r; - struct rndis_params *params = rndis_per_dev_params + configNr; u32 length; u8 *xbuf; /* drain the response queue */ - while ((xbuf = rndis_get_next_response(configNr, &length))) - rndis_free_response(configNr, xbuf); + while ((xbuf = rndis_get_next_response(params, &length))) + rndis_free_response(params, xbuf); - r = rndis_add_response(configNr, sizeof(rndis_reset_cmplt_type)); + r = rndis_add_response(params, sizeof(rndis_reset_cmplt_type)); if (!r) return -ENOMEM; resp = (rndis_reset_cmplt_type *)r->buf; @@ -718,16 +712,15 @@ static int rndis_reset_response(int configNr, rndis_reset_msg_type *buf) return 0; } -static int rndis_keepalive_response(int configNr, +static int rndis_keepalive_response(struct rndis_params *params, rndis_keepalive_msg_type *buf) { rndis_keepalive_cmplt_type *resp; rndis_resp_t *r; - struct rndis_params *params = rndis_per_dev_params + configNr; /* host "should" check only in RNDIS_DATA_INITIALIZED state */ - r = rndis_add_response(configNr, sizeof(rndis_keepalive_cmplt_type)); + r = rndis_add_response(params, sizeof(rndis_keepalive_cmplt_type)); if (!r) return -ENOMEM; resp = (rndis_keepalive_cmplt_type *)r->buf; @@ -745,17 +738,15 @@ static int rndis_keepalive_response(int configNr, /* * Device to Host Comunication */ -static int rndis_indicate_status_msg(int configNr, u32 status) +static int rndis_indicate_status_msg(struct rndis_params *params, u32 status) { rndis_indicate_status_msg_type *resp; rndis_resp_t *r; - struct rndis_params *params = rndis_per_dev_params + configNr; if (params->state == RNDIS_UNINITIALIZED) return -ENOTSUPP; - r = rndis_add_response(configNr, - sizeof(rndis_indicate_status_msg_type)); + r = rndis_add_response(params, sizeof(rndis_indicate_status_msg_type)); if (!r) return -ENOMEM; resp = (rndis_indicate_status_msg_type *)r->buf; @@ -770,53 +761,48 @@ static int rndis_indicate_status_msg(int configNr, u32 status) return 0; } -int rndis_signal_connect(int configNr) +int rndis_signal_connect(struct rndis_params *params) { - rndis_per_dev_params[configNr].media_state - = RNDIS_MEDIA_STATE_CONNECTED; - return rndis_indicate_status_msg(configNr, - RNDIS_STATUS_MEDIA_CONNECT); + params->media_state = RNDIS_MEDIA_STATE_CONNECTED; + return rndis_indicate_status_msg(params, RNDIS_STATUS_MEDIA_CONNECT); } EXPORT_SYMBOL_GPL(rndis_signal_connect); -int rndis_signal_disconnect(int configNr) +int rndis_signal_disconnect(struct rndis_params *params) { - rndis_per_dev_params[configNr].media_state - = RNDIS_MEDIA_STATE_DISCONNECTED; - return rndis_indicate_status_msg(configNr, - RNDIS_STATUS_MEDIA_DISCONNECT); + params->media_state = RNDIS_MEDIA_STATE_DISCONNECTED; + return rndis_indicate_status_msg(params, RNDIS_STATUS_MEDIA_DISCONNECT); } EXPORT_SYMBOL_GPL(rndis_signal_disconnect); -void rndis_uninit(int configNr) +void rndis_uninit(struct rndis_params *params) { u8 *buf; u32 length; - if (configNr >= RNDIS_MAX_CONFIGS) + if (!params) return; - rndis_per_dev_params[configNr].state = RNDIS_UNINITIALIZED; + params->state = RNDIS_UNINITIALIZED; /* drain the response queue */ - while ((buf = rndis_get_next_response(configNr, &length))) - rndis_free_response(configNr, buf); + while ((buf = rndis_get_next_response(params, &length))) + rndis_free_response(params, buf); } EXPORT_SYMBOL_GPL(rndis_uninit); -void rndis_set_host_mac(int configNr, const u8 *addr) +void rndis_set_host_mac(struct rndis_params *params, const u8 *addr) { - rndis_per_dev_params[configNr].host_mac = addr; + params->host_mac = addr; } EXPORT_SYMBOL_GPL(rndis_set_host_mac); /* * Message Parser */ -int rndis_msg_parser(u8 configNr, u8 *buf) +int rndis_msg_parser(struct rndis_params *params, u8 *buf) { u32 MsgType, MsgLength; __le32 *tmp; - struct rndis_params *params; if (!buf) return -ENOMEM; @@ -825,9 +811,8 @@ int rndis_msg_parser(u8 configNr, u8 *buf) MsgType = get_unaligned_le32(tmp++); MsgLength = get_unaligned_le32(tmp++); - if (configNr >= RNDIS_MAX_CONFIGS) + if (!params) return -ENOTSUPP; - params = &rndis_per_dev_params[configNr]; /* NOTE: RNDIS is *EXTREMELY* chatty ... Windows constantly polls for * rx/tx statistics and link status, in addition to KEEPALIVE traffic @@ -840,8 +825,7 @@ int rndis_msg_parser(u8 configNr, u8 *buf) pr_debug("%s: RNDIS_MSG_INIT\n", __func__); params->state = RNDIS_INITIALIZED; - return rndis_init_response(configNr, - (rndis_init_msg_type *)buf); + return rndis_init_response(params, (rndis_init_msg_type *)buf); case RNDIS_MSG_HALT: pr_debug("%s: RNDIS_MSG_HALT\n", @@ -854,17 +838,16 @@ int rndis_msg_parser(u8 configNr, u8 *buf) return 0; case RNDIS_MSG_QUERY: - return rndis_query_response(configNr, + return rndis_query_response(params, (rndis_query_msg_type *)buf); case RNDIS_MSG_SET: - return rndis_set_response(configNr, - (rndis_set_msg_type *)buf); + return rndis_set_response(params, (rndis_set_msg_type *)buf); case RNDIS_MSG_RESET: pr_debug("%s: RNDIS_MSG_RESET\n", __func__); - return rndis_reset_response(configNr, + return rndis_reset_response(params, (rndis_reset_msg_type *)buf); case RNDIS_MSG_KEEPALIVE: @@ -872,7 +855,7 @@ int rndis_msg_parser(u8 configNr, u8 *buf) if (rndis_debug > 1) pr_debug("%s: RNDIS_MSG_KEEPALIVE\n", __func__); - return rndis_keepalive_response(configNr, + return rndis_keepalive_response(params, (rndis_keepalive_msg_type *) buf); @@ -892,83 +875,145 @@ int rndis_msg_parser(u8 configNr, u8 *buf) } EXPORT_SYMBOL_GPL(rndis_msg_parser); -int rndis_register(void (*resp_avail)(void *v), void *v) +static inline int rndis_get_nr(void) +{ + return ida_simple_get(&rndis_ida, 0, 0, GFP_KERNEL); +} + +static inline void rndis_put_nr(int nr) +{ + ida_simple_remove(&rndis_ida, nr); +} + +struct rndis_params *rndis_register(void (*resp_avail)(void *v), void *v) { - u8 i; + struct rndis_params *params; + int i; if (!resp_avail) - return -EINVAL; + return ERR_PTR(-EINVAL); + + i = rndis_get_nr(); + if (i < 0) { + pr_debug("failed\n"); + + return ERR_PTR(-ENODEV); + } + + params = kzalloc(sizeof(*params), GFP_KERNEL); + if (!params) { + rndis_put_nr(i); + + return ERR_PTR(-ENOMEM); + } - for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { - if (!rndis_per_dev_params[i].used) { - rndis_per_dev_params[i].used = 1; - rndis_per_dev_params[i].resp_avail = resp_avail; - rndis_per_dev_params[i].v = v; - pr_debug("%s: configNr = %d\n", __func__, i); - return i; +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + { + struct proc_dir_entry *proc_entry; + char name[20]; + + sprintf(name, NAME_TEMPLATE, i); + proc_entry = proc_create_data(name, 0660, NULL, + &rndis_proc_fops, params); + if (!proc_entry) { + kfree(params); + rndis_put_nr(i); + + return ERR_PTR(-EIO); } } - pr_debug("failed\n"); +#endif + + params->confignr = i; + params->used = 1; + params->state = RNDIS_UNINITIALIZED; + params->media_state = RNDIS_MEDIA_STATE_DISCONNECTED; + params->resp_avail = resp_avail; + params->v = v; + INIT_LIST_HEAD(&(params->resp_queue)); + pr_debug("%s: configNr = %d\n", __func__, i); - return -ENODEV; + return params; } EXPORT_SYMBOL_GPL(rndis_register); -void rndis_deregister(int configNr) +void rndis_deregister(struct rndis_params *params) { + int i; + pr_debug("%s:\n", __func__); - if (configNr >= RNDIS_MAX_CONFIGS) return; - rndis_per_dev_params[configNr].used = 0; + if (!params) + return; + + i = params->confignr; + +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + { + char name[20]; + + sprintf(name, NAME_TEMPLATE, i); + remove_proc_entry(name, NULL); + } +#endif + + kfree(params); + rndis_put_nr(i); } EXPORT_SYMBOL_GPL(rndis_deregister); - -int rndis_set_param_dev(u8 configNr, struct net_device *dev, u16 *cdc_filter) +int rndis_set_param_dev(struct rndis_params *params, struct net_device *dev, + u16 *cdc_filter) { pr_debug("%s:\n", __func__); if (!dev) return -EINVAL; - if (configNr >= RNDIS_MAX_CONFIGS) return -1; + if (!params) + return -1; - rndis_per_dev_params[configNr].dev = dev; - rndis_per_dev_params[configNr].filter = cdc_filter; + params->dev = dev; + params->filter = cdc_filter; +#ifdef CONFIG_USB_GADGET_DEBUG_FILES rndis_ul_max_xfer_size_rcvd = 0; rndis_ul_max_pkt_per_xfer_rcvd = 0; +#endif return 0; } EXPORT_SYMBOL_GPL(rndis_set_param_dev); -int rndis_set_param_vendor(u8 configNr, u32 vendorID, const char *vendorDescr) +int rndis_set_param_vendor(struct rndis_params *params, u32 vendorID, + const char *vendorDescr) { pr_debug("%s:\n", __func__); if (!vendorDescr) return -1; - if (configNr >= RNDIS_MAX_CONFIGS) return -1; + if (!params) + return -1; - rndis_per_dev_params[configNr].vendorID = vendorID; - rndis_per_dev_params[configNr].vendorDescr = vendorDescr; + params->vendorID = vendorID; + params->vendorDescr = vendorDescr; return 0; } EXPORT_SYMBOL_GPL(rndis_set_param_vendor); -int rndis_set_param_medium(u8 configNr, u32 medium, u32 speed) +int rndis_set_param_medium(struct rndis_params *params, u32 medium, u32 speed) { pr_debug("%s: %u %u\n", __func__, medium, speed); - if (configNr >= RNDIS_MAX_CONFIGS) return -1; + if (!params) + return -1; - rndis_per_dev_params[configNr].medium = medium; - rndis_per_dev_params[configNr].speed = speed; + params->medium = medium; + params->speed = speed; return 0; } EXPORT_SYMBOL_GPL(rndis_set_param_medium); -void rndis_set_max_pkt_xfer(u8 configNr, u8 max_pkt_per_xfer) +void rndis_set_max_pkt_xfer(struct rndis_params *params, u8 max_pkt_per_xfer) { pr_debug("%s:\n", __func__); - rndis_per_dev_params[configNr].max_pkt_per_xfer = max_pkt_per_xfer; + params->max_pkt_per_xfer = max_pkt_per_xfer; } void rndis_add_hdr(struct sk_buff *skb) @@ -986,13 +1031,12 @@ void rndis_add_hdr(struct sk_buff *skb) } EXPORT_SYMBOL_GPL(rndis_add_hdr); -void rndis_free_response(int configNr, u8 *buf) +void rndis_free_response(struct rndis_params *params, u8 *buf) { rndis_resp_t *r; struct list_head *act, *tmp; - list_for_each_safe(act, tmp, - &(rndis_per_dev_params[configNr].resp_queue)) + list_for_each_safe(act, tmp, &(params->resp_queue)) { r = list_entry(act, rndis_resp_t, list); if (r && r->buf == buf) { @@ -1003,15 +1047,14 @@ void rndis_free_response(int configNr, u8 *buf) } EXPORT_SYMBOL_GPL(rndis_free_response); -u8 *rndis_get_next_response(int configNr, u32 *length) +u8 *rndis_get_next_response(struct rndis_params *params, u32 *length) { rndis_resp_t *r; struct list_head *act, *tmp; if (!length) return NULL; - list_for_each_safe(act, tmp, - &(rndis_per_dev_params[configNr].resp_queue)) + list_for_each_safe(act, tmp, &(params->resp_queue)) { r = list_entry(act, rndis_resp_t, list); if (!r->send) { @@ -1025,7 +1068,7 @@ u8 *rndis_get_next_response(int configNr, u32 *length) } EXPORT_SYMBOL_GPL(rndis_get_next_response); -static rndis_resp_t *rndis_add_response(int configNr, u32 length) +static rndis_resp_t *rndis_add_response(struct rndis_params *params, u32 length) { rndis_resp_t *r; @@ -1037,8 +1080,7 @@ static rndis_resp_t *rndis_add_response(int configNr, u32 length) r->length = length; r->send = 0; - list_add_tail(&r->list, - &(rndis_per_dev_params[configNr].resp_queue)); + list_add_tail(&r->list, &(params->resp_queue)); return r; } @@ -1048,8 +1090,10 @@ int rndis_rm_hdr(struct gether *port, { int num_pkts = 1; +#ifdef CONFIG_USB_GADGET_DEBUG_FILES if (skb->len > rndis_ul_max_xfer_size_rcvd) rndis_ul_max_xfer_size_rcvd = skb->len; +#endif while (skb->len) { struct rndis_packet_msg_type *hdr; @@ -1111,8 +1155,10 @@ int rndis_rm_hdr(struct gether *port, num_pkts++; } +#ifdef CONFIG_USB_GADGET_DEBUG_FILES if (num_pkts > rndis_ul_max_pkt_per_xfer_rcvd) rndis_ul_max_pkt_per_xfer_rcvd = num_pkts; +#endif skb_queue_tail(list, skb); return 0; @@ -1178,11 +1224,11 @@ static ssize_t rndis_proc_write(struct file *file, const char __user *buffer, break; case 'C': case 'c': - rndis_signal_connect(p->confignr); + rndis_signal_connect(p); break; case 'D': case 'd': - rndis_signal_disconnect(p->confignr); + rndis_signal_disconnect(p); break; default: if (fl_speed) p->speed = speed; @@ -1212,54 +1258,4 @@ static const struct file_operations rndis_proc_fops = { #define NAME_TEMPLATE "driver/rndis-%03d" -static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS]; - #endif /* CONFIG_USB_GADGET_DEBUG_FILES */ - - -int rndis_init(void) -{ - u8 i; - - for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { -#ifdef CONFIG_USB_GADGET_DEBUG_FILES - char name [20]; - - sprintf(name, NAME_TEMPLATE, i); - rndis_connect_state[i] = proc_create_data(name, 0660, NULL, - &rndis_proc_fops, - (void *)(rndis_per_dev_params + i)); - if (!rndis_connect_state[i]) { - pr_debug("%s: remove entries", __func__); - while (i) { - sprintf(name, NAME_TEMPLATE, --i); - remove_proc_entry(name, NULL); - } - pr_debug("\n"); - return -EIO; - } -#endif - rndis_per_dev_params[i].confignr = i; - rndis_per_dev_params[i].used = 0; - rndis_per_dev_params[i].state = RNDIS_UNINITIALIZED; - rndis_per_dev_params[i].media_state - = RNDIS_MEDIA_STATE_DISCONNECTED; - INIT_LIST_HEAD(&(rndis_per_dev_params[i].resp_queue)); - } - - return 0; -} - -void rndis_exit(void) -{ -#ifdef CONFIG_USB_GADGET_DEBUG_FILES - u8 i; - char name[20]; - - for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { - sprintf(name, NAME_TEMPLATE, i); - remove_proc_entry(name, NULL); - } -#endif -} - diff --git a/drivers/usb/gadget/function/rndis.h b/drivers/usb/gadget/function/rndis.h index 145f01b1819..310cac3f088 100644 --- a/drivers/usb/gadget/function/rndis.h +++ b/drivers/usb/gadget/function/rndis.h @@ -177,7 +177,7 @@ typedef struct rndis_resp_t typedef struct rndis_params { - u8 confignr; + int confignr; u8 used; u16 saved_filter; enum rndis_state state; @@ -198,25 +198,26 @@ typedef struct rndis_params } rndis_params; /* RNDIS Message parser and other useless functions */ -int rndis_msg_parser (u8 configNr, u8 *buf); -int rndis_register(void (*resp_avail)(void *v), void *v); -void rndis_deregister (int configNr); -int rndis_set_param_dev (u8 configNr, struct net_device *dev, +int rndis_msg_parser(struct rndis_params *params, u8 *buf); +struct rndis_params *rndis_register(void (*resp_avail)(void *v), void *v); +void rndis_deregister(struct rndis_params *params); +int rndis_set_param_dev(struct rndis_params *params, struct net_device *dev, u16 *cdc_filter); -int rndis_set_param_vendor (u8 configNr, u32 vendorID, +int rndis_set_param_vendor(struct rndis_params *params, u32 vendorID, const char *vendorDescr); -int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed); -void rndis_set_max_pkt_xfer(u8 configNr, u8 max_pkt_per_xfer); -void rndis_add_hdr (struct sk_buff *skb); +int rndis_set_param_medium(struct rndis_params *params, u32 medium, + u32 speed); +void rndis_set_max_pkt_xfer(struct rndis_params *params, u8 max_pkt_per_xfer); +void rndis_add_hdr(struct sk_buff *skb); int rndis_rm_hdr(struct gether *port, struct sk_buff *skb, struct sk_buff_head *list); -u8 *rndis_get_next_response (int configNr, u32 *length); -void rndis_free_response (int configNr, u8 *buf); - -void rndis_uninit (int configNr); -int rndis_signal_connect (int configNr); -int rndis_signal_disconnect (int configNr); -int rndis_state (int configNr); -extern void rndis_set_host_mac (int configNr, const u8 *addr); +u8 *rndis_get_next_response(struct rndis_params *params, u32 *length); +void rndis_free_response(struct rndis_params *params, u8 *buf); + +void rndis_uninit(struct rndis_params *params); +int rndis_signal_connect(struct rndis_params *params); +int rndis_signal_disconnect(struct rndis_params *params); +int rndis_state(struct rndis_params *params); +extern void rndis_set_host_mac(struct rndis_params *params, const u8 *addr); #endif /* _LINUX_RNDIS_H */ diff --git a/drivers/usb/gadget/function/u_rndis.h b/drivers/usb/gadget/function/u_rndis.h index e902aa42a29..4eafd505054 100644 --- a/drivers/usb/gadget/function/u_rndis.h +++ b/drivers/usb/gadget/function/u_rndis.h @@ -39,8 +39,6 @@ struct f_rndis_opts { int refcnt; }; -int rndis_init(void); -void rndis_exit(void); void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net); #endif /* U_RNDIS_H */ diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index ebe409b9e41..7d3bb6272e0 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -56,7 +56,6 @@ struct uvc_event #include <linux/usb/composite.h> #include <linux/usb/gadget.h> #include <linux/videodev2.h> -#include <linux/version.h> #include <media/v4l2-fh.h> #include <media/v4l2-device.h> diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 351d48550c3..4095cce05e6 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -704,8 +704,8 @@ static int queue_dma(struct usba_udc *udc, struct usba_ep *ep, unsigned long flags; int ret; - DBG(DBG_DMA, "%s: req l/%u d/%08x %c%c%c\n", - ep->ep.name, req->req.length, req->req.dma, + DBG(DBG_DMA, "%s: req l/%u d/%pad %c%c%c\n", + ep->ep.name, req->req.length, &req->req.dma, req->req.zero ? 'Z' : 'z', req->req.short_not_ok ? 'S' : 's', req->req.no_interrupt ? 'I' : 'i'); @@ -2203,7 +2203,7 @@ static int usba_udc_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int usba_udc_suspend(struct device *dev) { struct usba_udc *udc = dev_get_drvdata(dev); diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index 9871b90195a..2bee912ca65 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -123,6 +123,11 @@ static char *type_string(u8 bmAttributes) #define valid_bit cpu_to_le32(BIT(VALID_BIT)) #define dma_done_ie cpu_to_le32(BIT(DMA_DONE_INTERRUPT_ENABLE)) +static void ep_clear_seqnum(struct net2280_ep *ep); +static void stop_activity(struct net2280 *dev, + struct usb_gadget_driver *driver); +static void ep0_start(struct net2280 *dev); + /*-------------------------------------------------------------------------*/ static inline void enable_pciirqenb(struct net2280_ep *ep) { @@ -142,7 +147,9 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) { struct net2280 *dev; struct net2280_ep *ep; - u32 max, tmp; + u32 max; + u32 tmp = 0; + u32 type; unsigned long flags; static const u32 ep_key[9] = { 1, 0, 1, 0, 1, 1, 0, 1, 0 }; int ret = 0; @@ -198,15 +205,29 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) /* set type, direction, address; reset fifo counters */ writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat); - tmp = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK); - if (tmp == USB_ENDPOINT_XFER_INT) { + + if ((dev->quirks & PLX_SUPERSPEED) && dev->enhanced_mode) { + tmp = readl(&ep->cfg->ep_cfg); + /* If USB ep number doesn't match hardware ep number */ + if ((tmp & 0xf) != usb_endpoint_num(desc)) { + ret = -EINVAL; + spin_unlock_irqrestore(&dev->lock, flags); + goto print_err; + } + if (ep->is_in) + tmp &= ~USB3380_EP_CFG_MASK_IN; + else + tmp &= ~USB3380_EP_CFG_MASK_OUT; + } + type = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK); + if (type == USB_ENDPOINT_XFER_INT) { /* erratum 0105 workaround prevents hs NYET */ if (dev->chiprev == 0100 && dev->gadget.speed == USB_SPEED_HIGH && !(desc->bEndpointAddress & USB_DIR_IN)) writel(BIT(CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp); - } else if (tmp == USB_ENDPOINT_XFER_BULK) { + } else if (type == USB_ENDPOINT_XFER_BULK) { /* catch some particularly blatant driver bugs */ if ((dev->gadget.speed == USB_SPEED_SUPER && max != 1024) || (dev->gadget.speed == USB_SPEED_HIGH && max != 512) || @@ -216,10 +237,10 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) goto print_err; } } - ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC); + ep->is_iso = (type == USB_ENDPOINT_XFER_ISOC); /* Enable this endpoint */ if (dev->quirks & PLX_LEGACY) { - tmp <<= ENDPOINT_TYPE; + tmp |= type << ENDPOINT_TYPE; tmp |= desc->bEndpointAddress; /* default full fifo lines */ tmp |= (4 << ENDPOINT_BYTE_COUNT); @@ -228,17 +249,17 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) } else { /* In Legacy mode, only OUT endpoints are used */ if (dev->enhanced_mode && ep->is_in) { - tmp <<= IN_ENDPOINT_TYPE; + tmp |= type << IN_ENDPOINT_TYPE; tmp |= BIT(IN_ENDPOINT_ENABLE); - /* Not applicable to Legacy */ - tmp |= BIT(ENDPOINT_DIRECTION); } else { - tmp <<= OUT_ENDPOINT_TYPE; + tmp |= type << OUT_ENDPOINT_TYPE; tmp |= BIT(OUT_ENDPOINT_ENABLE); tmp |= (ep->is_in << ENDPOINT_DIRECTION); } - tmp |= usb_endpoint_num(desc); + tmp |= (4 << ENDPOINT_BYTE_COUNT); + if (!dev->enhanced_mode) + tmp |= usb_endpoint_num(desc); tmp |= (ep->ep.maxburst << MAX_BURST_SIZE); } @@ -256,6 +277,8 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) BIT(CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp); } + if (dev->quirks & PLX_SUPERSPEED) + ep_clear_seqnum(ep); writel(tmp, &ep->cfg->ep_cfg); /* enable irqs */ @@ -441,6 +464,13 @@ static void ep_reset_338x(struct net2280_regs __iomem *regs, BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) | BIT(DATA_OUT_PING_TOKEN_INTERRUPT) | BIT(DATA_IN_TOKEN_INTERRUPT), &ep->regs->ep_stat); + + tmp = readl(&ep->cfg->ep_cfg); + if (ep->is_in) + tmp &= ~USB3380_EP_CFG_MASK_IN; + else + tmp &= ~USB3380_EP_CFG_MASK_OUT; + writel(tmp, &ep->cfg->ep_cfg); } static void nuke(struct net2280_ep *); @@ -1468,11 +1498,14 @@ static int net2280_pullup(struct usb_gadget *_gadget, int is_on) spin_lock_irqsave(&dev->lock, flags); tmp = readl(&dev->usb->usbctl); dev->softconnect = (is_on != 0); - if (is_on) - tmp |= BIT(USB_DETECT_ENABLE); - else - tmp &= ~BIT(USB_DETECT_ENABLE); - writel(tmp, &dev->usb->usbctl); + if (is_on) { + ep0_start(dev); + writel(tmp | BIT(USB_DETECT_ENABLE), &dev->usb->usbctl); + } else { + writel(tmp & ~BIT(USB_DETECT_ENABLE), &dev->usb->usbctl); + stop_activity(dev, dev->driver); + } + spin_unlock_irqrestore(&dev->lock, flags); return 0; @@ -1860,8 +1893,8 @@ static void defect7374_enable_data_eps_zero(struct net2280 *dev) tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_DIRECTION) | (2 << OUT_ENDPOINT_TYPE) | (2 << IN_ENDPOINT_TYPE) | ((dev->enhanced_mode) ? - BIT(OUT_ENDPOINT_ENABLE) : BIT(ENDPOINT_ENABLE)) | - BIT(IN_ENDPOINT_ENABLE)); + BIT(OUT_ENDPOINT_ENABLE) | BIT(IN_ENDPOINT_ENABLE) : + BIT(ENDPOINT_ENABLE))); for (i = 1; i < 5; i++) writel(tmp, &dev->ep[i].cfg->ep_cfg); @@ -1975,9 +2008,15 @@ static void usb_reset_338x(struct net2280 *dev) /* clear old dma and irq state */ for (tmp = 0; tmp < 4; tmp++) { struct net2280_ep *ep = &dev->ep[tmp + 1]; + struct net2280_dma_regs __iomem *dma; - if (ep->dma) + if (ep->dma) { abort_dma(ep); + } else { + dma = &dev->dma[tmp]; + writel(BIT(DMA_ABORT), &dma->dmastat); + writel(0, &dma->dmactl); + } } writel(~0, &dev->regs->irqstat0), writel(~0, &dev->regs->irqstat1); @@ -2065,6 +2104,12 @@ static void usb_reinit_338x(struct net2280 *dev) if (dev->enhanced_mode) { ep->cfg = &dev->epregs[ne[i]]; + /* + * Set USB endpoint number, hardware allows same number + * in both directions. + */ + if (i > 0 && i < 5) + writel(ne[i], &ep->cfg->ep_cfg); ep->regs = (struct net2280_ep_regs __iomem *) (((void __iomem *)&dev->epregs[ne[i]]) + ep_reg_addr[i]); @@ -2874,6 +2919,26 @@ next_endpoints3: return; } +static void usb338x_handle_ep_intr(struct net2280 *dev, u32 stat0) +{ + u32 index; + u32 bit; + + for (index = 0; index < ARRAY_SIZE(ep_bit); index++) { + bit = BIT(ep_bit[index]); + + if (!stat0) + break; + + if (!(stat0 & bit)) + continue; + + stat0 &= ~bit; + + handle_ep_small(&dev->ep[index]); + } +} + static void handle_stat0_irqs(struct net2280 *dev, u32 stat) { struct net2280_ep *ep; @@ -3098,20 +3163,31 @@ do_stall: #undef w_length next_endpoints: - /* endpoint data irq ? */ - scratch = stat & 0x7f; - stat &= ~0x7f; - for (num = 0; scratch; num++) { - u32 t; - - /* do this endpoint's FIFO and queue need tending? */ - t = BIT(num); - if ((scratch & t) == 0) - continue; - scratch ^= t; + if ((dev->quirks & PLX_SUPERSPEED) && dev->enhanced_mode) { + u32 mask = (BIT(ENDPOINT_0_INTERRUPT) | + USB3380_IRQSTAT0_EP_INTR_MASK_IN | + USB3380_IRQSTAT0_EP_INTR_MASK_OUT); + + if (stat & mask) { + usb338x_handle_ep_intr(dev, stat & mask); + stat &= ~mask; + } + } else { + /* endpoint data irq ? */ + scratch = stat & 0x7f; + stat &= ~0x7f; + for (num = 0; scratch; num++) { + u32 t; + + /* do this endpoint's FIFO and queue need tending? */ + t = BIT(num); + if ((scratch & t) == 0) + continue; + scratch ^= t; - ep = &dev->ep[num]; - handle_ep_small(ep); + ep = &dev->ep[num]; + handle_ep_small(ep); + } } if (stat) diff --git a/drivers/usb/gadget/udc/s3c2410_udc.c b/drivers/usb/gadget/udc/s3c2410_udc.c index 99fd9a5667d..5d9aa81969b 100644 --- a/drivers/usb/gadget/udc/s3c2410_udc.c +++ b/drivers/usb/gadget/udc/s3c2410_udc.c @@ -92,40 +92,38 @@ static struct s3c2410_udc_mach_info *udc_info; static uint32_t s3c2410_ticks = 0; -static int dprintk(int level, const char *fmt, ...) +__printf(2, 3) +static void dprintk(int level, const char *fmt, ...) { - static char printk_buf[1024]; static long prevticks; static int invocation; + struct va_format vaf; va_list args; - int len; if (level > USB_S3C2410_DEBUG_LEVEL) - return 0; + return; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; if (s3c2410_ticks != prevticks) { prevticks = s3c2410_ticks; invocation = 0; } - len = scnprintf(printk_buf, - sizeof(printk_buf), "%1lu.%02d USB: ", - prevticks, invocation++); + pr_debug("%1lu.%02d USB: %pV", prevticks, invocation++, &vaf); - va_start(args, fmt); - len = vscnprintf(printk_buf+len, - sizeof(printk_buf)-len, fmt, args); va_end(args); - - pr_debug("%s", printk_buf); - return len; } #else -static int dprintk(int level, const char *fmt, ...) +__printf(2, 3) +static void dprintk(int level, const char *fmt, ...) { - return 0; } #endif + static int s3c2410_udc_debugfs_seq_show(struct seq_file *m, void *p) { u32 addr_reg, pwr_reg, ep_int_reg, usb_int_reg; diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c index 220fd4d3b41..c41fe588d14 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -438,11 +438,15 @@ static void am35x_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) } static const struct musb_platform_ops am35x_ops = { - .quirks = MUSB_INDEXED_EP, + .quirks = MUSB_DMA_INVENTRA | MUSB_INDEXED_EP, .init = am35x_musb_init, .exit = am35x_musb_exit, .read_fifo = am35x_read_fifo, +#ifdef CONFIG_USB_INVENTRA_DMA + .dma_init = musbhs_dma_controller_create, + .dma_exit = musbhs_dma_controller_destroy, +#endif .enable = am35x_musb_enable, .disable = am35x_musb_disable, @@ -565,7 +569,7 @@ static int am35x_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int am35x_suspend(struct device *dev) { struct am35x_glue *glue = dev_get_drvdata(dev); diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 6123b748d26..310238c6b5c 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -465,6 +465,7 @@ static int bfin_musb_exit(struct musb *musb) } static const struct musb_platform_ops bfin_ops = { + .quirks = MUSB_DMA_INVENTRA, .init = bfin_musb_init, .exit = bfin_musb_exit, @@ -477,6 +478,10 @@ static const struct musb_platform_ops bfin_ops = { .fifo_mode = 2, .read_fifo = bfin_read_fifo, .write_fifo = bfin_write_fifo, +#ifdef CONFIG_USB_INVENTRA_DMA + .dma_init = musbhs_dma_controller_create, + .dma_exit = musbhs_dma_controller_destroy, +#endif .enable = bfin_musb_enable, .disable = bfin_musb_disable, diff --git a/drivers/usb/musb/cppi_dma.c b/drivers/usb/musb/cppi_dma.c index 904fb85d85a..cc134109b05 100644 --- a/drivers/usb/musb/cppi_dma.c +++ b/drivers/usb/musb/cppi_dma.c @@ -1297,7 +1297,8 @@ irqreturn_t cppi_interrupt(int irq, void *dev_id) EXPORT_SYMBOL_GPL(cppi_interrupt); /* Instantiate a software object representing a DMA controller. */ -struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *mregs) +struct dma_controller * +cppi_dma_controller_create(struct musb *musb, void __iomem *mregs) { struct cppi *controller; struct device *dev = musb->controller; @@ -1334,7 +1335,7 @@ struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *mr if (irq > 0) { if (request_irq(irq, cppi_interrupt, 0, "cppi-dma", musb)) { dev_err(dev, "request_irq %d failed!\n", irq); - dma_controller_destroy(&controller->controller); + musb_dma_controller_destroy(&controller->controller); return NULL; } controller->irq = irq; @@ -1343,11 +1344,12 @@ struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *mr cppi_controller_start(controller); return &controller->controller; } +EXPORT_SYMBOL_GPL(cppi_dma_controller_create); /* * Destroy a previously-instantiated DMA controller. */ -void dma_controller_destroy(struct dma_controller *c) +void cppi_dma_controller_destroy(struct dma_controller *c) { struct cppi *cppi; @@ -1363,6 +1365,7 @@ void dma_controller_destroy(struct dma_controller *c) kfree(cppi); } +EXPORT_SYMBOL_GPL(cppi_dma_controller_destroy); /* * Context: controller irqlocked, endpoint selected diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 9a9c82a4d35..b03d3b867fc 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -458,11 +458,15 @@ static int da8xx_musb_exit(struct musb *musb) } static const struct musb_platform_ops da8xx_ops = { - .quirks = MUSB_INDEXED_EP, + .quirks = MUSB_DMA_CPPI | MUSB_INDEXED_EP, .init = da8xx_musb_init, .exit = da8xx_musb_exit, .fifo_mode = 2, +#ifdef CONFIG_USB_TI_CPPI_DMA + .dma_init = cppi_dma_controller_create, + .dma_exit = cppi_dma_controller_destroy, +#endif .enable = da8xx_musb_enable, .disable = da8xx_musb_disable, diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index 3c1d9b211b5..cee61a51645 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -284,7 +284,7 @@ static irqreturn_t davinci_musb_interrupt(int irq, void *__hci) * mask, state, "vector", and EOI registers. */ cppi = container_of(musb->dma_controller, struct cppi, controller); - if (is_cppi_enabled() && musb->dma_controller && !cppi->irq) + if (is_cppi_enabled(musb) && musb->dma_controller && !cppi->irq) retval = cppi_interrupt(irq, __hci); /* ack and handle non-CPPI interrupts */ @@ -491,9 +491,14 @@ static int davinci_musb_exit(struct musb *musb) } static const struct musb_platform_ops davinci_ops = { + .quirks = MUSB_DMA_CPPI, .init = davinci_musb_init, .exit = davinci_musb_exit, +#ifdef CONFIG_USB_TI_CPPI_DMA + .dma_init = cppi_dma_controller_create, + .dma_exit = cppi_dma_controller_destroy, +#endif .enable = davinci_musb_enable, .disable = davinci_musb_disable, diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c index bb7b26325a7..5e5a8fa005f 100644 --- a/drivers/usb/musb/jz4740.c +++ b/drivers/usb/musb/jz4740.c @@ -105,8 +105,12 @@ static int jz4740_musb_exit(struct musb *musb) return 0; } +/* + * DMA has not been confirmed to work with CONFIG_USB_INVENTRA_DMA, + * so let's not set up the dma function pointers yet. + */ static const struct musb_platform_ops jz4740_musb_ops = { - .quirks = MUSB_INDEXED_EP, + .quirks = MUSB_DMA_INVENTRA | MUSB_INDEXED_EP, .fifo_mode = 2, .init = jz4740_musb_init, .exit = jz4740_musb_exit, diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 6dca3d794ce..514a6cdaeff 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -251,6 +251,11 @@ static u32 musb_indexed_ep_offset(u8 epnum, u16 offset) return 0x10 + offset; } +static u32 musb_default_busctl_offset(u8 epnum, u16 offset) +{ + return 0x80 + (0x08 * epnum) + offset; +} + static u8 musb_default_readb(const void __iomem *addr, unsigned offset) { return __raw_readb(addr + offset); @@ -309,7 +314,7 @@ static void musb_default_write_fifo(struct musb_hw_ep *hw_ep, u16 len, index += len & ~0x03; } if (len & 0x02) { - musb_writew(fifo, 0, *(u16 *)&src[index]); + __raw_writew(*(u16 *)&src[index], fifo); index += 2; } } else { @@ -319,7 +324,7 @@ static void musb_default_write_fifo(struct musb_hw_ep *hw_ep, u16 len, } } if (len & 0x01) - musb_writeb(fifo, 0, src[index]); + __raw_writeb(src[index], fifo); } else { /* byte aligned */ iowrite8_rep(fifo, src, len); @@ -351,7 +356,7 @@ static void musb_default_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) index = len & ~0x03; } if (len & 0x02) { - *(u16 *)&dst[index] = musb_readw(fifo, 0); + *(u16 *)&dst[index] = __raw_readw(fifo); index += 2; } } else { @@ -361,7 +366,7 @@ static void musb_default_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) } } if (len & 0x01) - dst[index] = musb_readb(fifo, 0); + dst[index] = __raw_readb(fifo); } else { /* byte aligned */ ioread8_rep(fifo, dst, len); @@ -389,6 +394,15 @@ EXPORT_SYMBOL_GPL(musb_readl); void (*musb_writel)(void __iomem *addr, unsigned offset, u32 data); EXPORT_SYMBOL_GPL(musb_writel); +#ifndef CONFIG_MUSB_PIO_ONLY +struct dma_controller * +(*musb_dma_controller_create)(struct musb *musb, void __iomem *base); +EXPORT_SYMBOL(musb_dma_controller_create); + +void (*musb_dma_controller_destroy)(struct dma_controller *c); +EXPORT_SYMBOL(musb_dma_controller_destroy); +#endif + /* * New style IO functions */ @@ -1535,7 +1549,6 @@ static int musb_core_init(u16 musb_type, struct musb *musb) #endif hw_ep->regs = musb->io.ep_offset(i, 0) + mbase; - hw_ep->target_regs = musb_read_target_reg_base(i, mbase); hw_ep->rx_reinit = 1; hw_ep->tx_reinit = 1; @@ -1658,15 +1671,13 @@ void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit) /* called with controller lock already held */ if (!epnum) { -#ifndef CONFIG_USB_TUSB_OMAP_DMA - if (!is_cppi_enabled()) { + if (!is_cppi_enabled(musb)) { /* endpoint 0 */ if (is_host_active(musb)) musb_h_ep0_irq(musb); else musb_g_ep0_irq(musb); } -#endif } else { /* endpoints 1..15 */ if (transmit) { @@ -2029,6 +2040,11 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) musb->io.ep_offset = musb_flat_ep_offset; musb->io.ep_select = musb_flat_ep_select; } + /* And override them with platform specific ops if specified. */ + if (musb->ops->ep_offset) + musb->io.ep_offset = musb->ops->ep_offset; + if (musb->ops->ep_select) + musb->io.ep_select = musb->ops->ep_select; /* At least tusb6010 has its own offsets */ if (musb->ops->ep_offset) @@ -2046,6 +2062,11 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) else musb->io.fifo_offset = musb_default_fifo_offset; + if (musb->ops->busctl_offset) + musb->io.busctl_offset = musb->ops->busctl_offset; + else + musb->io.busctl_offset = musb_default_busctl_offset; + if (musb->ops->readb) musb_readb = musb->ops->readb; if (musb->ops->writeb) @@ -2059,6 +2080,15 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) if (musb->ops->writel) musb_writel = musb->ops->writel; +#ifndef CONFIG_MUSB_PIO_ONLY + if (!musb->ops->dma_init || !musb->ops->dma_exit) { + dev_err(dev, "DMA controller not set\n"); + goto fail2; + } + musb_dma_controller_create = musb->ops->dma_init; + musb_dma_controller_destroy = musb->ops->dma_exit; +#endif + if (musb->ops->read_fifo) musb->io.read_fifo = musb->ops->read_fifo; else @@ -2078,7 +2108,8 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) pm_runtime_get_sync(musb->controller); if (use_dma && dev->dma_mask) { - musb->dma_controller = dma_controller_create(musb, musb->mregs); + musb->dma_controller = + musb_dma_controller_create(musb, musb->mregs); if (IS_ERR(musb->dma_controller)) { status = PTR_ERR(musb->dma_controller); goto fail2_5; @@ -2189,7 +2220,7 @@ fail3: cancel_delayed_work_sync(&musb->finish_resume_work); cancel_delayed_work_sync(&musb->deassert_reset_work); if (musb->dma_controller) - dma_controller_destroy(musb->dma_controller); + musb_dma_controller_destroy(musb->dma_controller); fail2_5: pm_runtime_put_sync(musb->controller); @@ -2248,7 +2279,7 @@ static int musb_remove(struct platform_device *pdev) musb_shutdown(pdev); if (musb->dma_controller) - dma_controller_destroy(musb->dma_controller); + musb_dma_controller_destroy(musb->dma_controller); cancel_work_sync(&musb->irq_work); cancel_delayed_work_sync(&musb->finish_resume_work); @@ -2316,18 +2347,18 @@ static void musb_save_context(struct musb *musb) musb_readb(epio, MUSB_RXINTERVAL); musb->context.index_regs[i].txfunaddr = - musb_read_txfunaddr(musb_base, i); + musb_read_txfunaddr(musb, i); musb->context.index_regs[i].txhubaddr = - musb_read_txhubaddr(musb_base, i); + musb_read_txhubaddr(musb, i); musb->context.index_regs[i].txhubport = - musb_read_txhubport(musb_base, i); + musb_read_txhubport(musb, i); musb->context.index_regs[i].rxfunaddr = - musb_read_rxfunaddr(musb_base, i); + musb_read_rxfunaddr(musb, i); musb->context.index_regs[i].rxhubaddr = - musb_read_rxhubaddr(musb_base, i); + musb_read_rxhubaddr(musb, i); musb->context.index_regs[i].rxhubport = - musb_read_rxhubport(musb_base, i); + musb_read_rxhubport(musb, i); } } @@ -2335,7 +2366,6 @@ static void musb_restore_context(struct musb *musb) { int i; void __iomem *musb_base = musb->mregs; - void __iomem *ep_target_regs; void __iomem *epio; u8 power; @@ -2396,21 +2426,18 @@ static void musb_restore_context(struct musb *musb) musb_writeb(epio, MUSB_RXINTERVAL, musb->context.index_regs[i].rxinterval); - musb_write_txfunaddr(musb_base, i, + musb_write_txfunaddr(musb, i, musb->context.index_regs[i].txfunaddr); - musb_write_txhubaddr(musb_base, i, + musb_write_txhubaddr(musb, i, musb->context.index_regs[i].txhubaddr); - musb_write_txhubport(musb_base, i, + musb_write_txhubport(musb, i, musb->context.index_regs[i].txhubport); - ep_target_regs = - musb_read_target_reg_base(i, musb_base); - - musb_write_rxfunaddr(ep_target_regs, + musb_write_rxfunaddr(musb, i, musb->context.index_regs[i].rxfunaddr); - musb_write_rxhubaddr(ep_target_regs, + musb_write_rxhubaddr(musb, i, musb->context.index_regs[i].rxhubaddr); - musb_write_rxhubport(ep_target_regs, + musb_write_rxhubport(musb, i, musb->context.index_regs[i].rxhubport); } musb_writeb(musb_base, MUSB_INDEX, musb->context.index); diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 3877249a8b2..4b886d0f6bd 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -67,7 +67,6 @@ struct musb_ep; #include "musb_dma.h" #include "musb_io.h" -#include "musb_regs.h" #include "musb_gadget.h" #include <linux/usb/hcd.h> @@ -157,6 +156,8 @@ struct musb_io; * @writel: write 32 bits * @read_fifo: reads the fifo * @write_fifo: writes to fifo + * @dma_init: platform specific dma init function + * @dma_exit: platform specific dma exit function * @init: turns on clocks, sets up platform-specific registers, etc * @exit: undoes @init * @set_mode: forcefully changes operating mode @@ -165,6 +166,8 @@ struct musb_io; * @vbus_status: returns vbus status if possible * @set_vbus: forces vbus status * @adjust_channel_params: pre check for standard dma channel_program func + * @pre_root_reset_end: called before the root usb port reset flag gets cleared + * @post_root_reset_end: called after the root usb port reset flag gets cleared */ struct musb_platform_ops { @@ -187,6 +190,7 @@ struct musb_platform_ops { void (*ep_select)(void __iomem *mbase, u8 epnum); u16 fifo_mode; u32 (*fifo_offset)(u8 epnum); + u32 (*busctl_offset)(u8 epnum, u16 offset); u8 (*readb)(const void __iomem *addr, unsigned offset); void (*writeb)(void __iomem *addr, unsigned offset, u8 data); u16 (*readw)(const void __iomem *addr, unsigned offset); @@ -195,6 +199,9 @@ struct musb_platform_ops { void (*writel)(void __iomem *addr, unsigned offset, u32 data); void (*read_fifo)(struct musb_hw_ep *hw_ep, u16 len, u8 *buf); void (*write_fifo)(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf); + struct dma_controller * + (*dma_init) (struct musb *musb, void __iomem *base); + void (*dma_exit)(struct dma_controller *c); int (*set_mode)(struct musb *musb, u8 mode); void (*try_idle)(struct musb *musb, unsigned long timeout); int (*recover)(struct musb *musb); @@ -205,6 +212,8 @@ struct musb_platform_ops { int (*adjust_channel_params)(struct dma_channel *channel, u16 packet_sz, u8 *mode, dma_addr_t *dma_addr, u32 *len); + void (*pre_root_reset_end)(struct musb *musb); + void (*post_root_reset_end)(struct musb *musb); }; /* @@ -241,8 +250,6 @@ struct musb_hw_ep { void __iomem *fifo_sync_va; #endif - void __iomem *target_regs; - /* currently scheduled peripheral endpoint */ struct musb_qh *in_qh; struct musb_qh *out_qh; @@ -437,6 +444,9 @@ struct musb { #endif }; +/* This must be included after struct musb is defined */ +#include "musb_regs.h" + static inline struct musb *gadget_to_musb(struct usb_gadget *g) { return container_of(g, struct musb, g); @@ -590,4 +600,16 @@ static inline int musb_platform_exit(struct musb *musb) return musb->ops->exit(musb); } +static inline void musb_platform_pre_root_reset_end(struct musb *musb) +{ + if (musb->ops->pre_root_reset_end) + musb->ops->pre_root_reset_end(musb); +} + +static inline void musb_platform_post_root_reset_end(struct musb *musb) +{ + if (musb->ops->post_root_reset_end) + musb->ops->post_root_reset_end(musb); +} + #endif /* __MUSB_CORE_H__ */ diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c index 8bd8c5e2692..4d1b44c232e 100644 --- a/drivers/usb/musb/musb_cppi41.c +++ b/drivers/usb/musb/musb_cppi41.c @@ -678,7 +678,7 @@ err: return ret; } -void dma_controller_destroy(struct dma_controller *c) +void cppi41_dma_controller_destroy(struct dma_controller *c) { struct cppi41_dma_controller *controller = container_of(c, struct cppi41_dma_controller, controller); @@ -687,9 +687,10 @@ void dma_controller_destroy(struct dma_controller *c) cppi41_dma_controller_stop(controller); kfree(controller); } +EXPORT_SYMBOL_GPL(cppi41_dma_controller_destroy); -struct dma_controller *dma_controller_create(struct musb *musb, - void __iomem *base) +struct dma_controller * +cppi41_dma_controller_create(struct musb *musb, void __iomem *base) { struct cppi41_dma_controller *controller; int ret = 0; @@ -726,3 +727,4 @@ kzalloc_fail: return ERR_PTR(ret); return NULL; } +EXPORT_SYMBOL_GPL(cppi41_dma_controller_create); diff --git a/drivers/usb/musb/musb_debugfs.c b/drivers/usb/musb/musb_debugfs.c index 78a283e9ce4..9b22d946c08 100644 --- a/drivers/usb/musb/musb_debugfs.c +++ b/drivers/usb/musb/musb_debugfs.c @@ -191,9 +191,16 @@ static ssize_t musb_test_mode_write(struct file *file, { struct seq_file *s = file->private_data; struct musb *musb = s->private; - u8 test = 0; + u8 test; char buf[18]; + test = musb_readb(musb->mregs, MUSB_TESTMODE); + if (test) { + dev_err(musb->controller, "Error: test mode is already set. " + "Please do USB Bus Reset to start a new test.\n"); + return count; + } + memset(buf, 0x00, sizeof(buf)); if (copy_from_user(buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) @@ -238,6 +245,90 @@ static const struct file_operations musb_test_mode_fops = { .release = single_release, }; +static int musb_softconnect_show(struct seq_file *s, void *unused) +{ + struct musb *musb = s->private; + u8 reg; + int connect; + + switch (musb->xceiv->otg->state) { + case OTG_STATE_A_HOST: + case OTG_STATE_A_WAIT_BCON: + reg = musb_readb(musb->mregs, MUSB_DEVCTL); + connect = reg & MUSB_DEVCTL_SESSION ? 1 : 0; + break; + default: + connect = -1; + } + + seq_printf(s, "%d\n", connect); + + return 0; +} + +static int musb_softconnect_open(struct inode *inode, struct file *file) +{ + return single_open(file, musb_softconnect_show, inode->i_private); +} + +static ssize_t musb_softconnect_write(struct file *file, + const char __user *ubuf, size_t count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct musb *musb = s->private; + char buf[2]; + u8 reg; + + memset(buf, 0x00, sizeof(buf)); + + if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) + return -EFAULT; + + if (!strncmp(buf, "0", 1)) { + switch (musb->xceiv->otg->state) { + case OTG_STATE_A_HOST: + musb_root_disconnect(musb); + reg = musb_readb(musb->mregs, MUSB_DEVCTL); + reg &= ~MUSB_DEVCTL_SESSION; + musb_writeb(musb->mregs, MUSB_DEVCTL, reg); + break; + default: + break; + } + } else if (!strncmp(buf, "1", 1)) { + switch (musb->xceiv->otg->state) { + case OTG_STATE_A_WAIT_BCON: + /* + * musb_save_context() called in musb_runtime_suspend() + * might cache devctl with SESSION bit cleared during + * soft-disconnect, so specifically set SESSION bit + * here to preserve it for musb_runtime_resume(). + */ + musb->context.devctl |= MUSB_DEVCTL_SESSION; + reg = musb_readb(musb->mregs, MUSB_DEVCTL); + reg |= MUSB_DEVCTL_SESSION; + musb_writeb(musb->mregs, MUSB_DEVCTL, reg); + break; + default: + break; + } + } + + return count; +} + +/* + * In host mode, connect/disconnect the bus without physically + * remove the devices. + */ +static const struct file_operations musb_softconnect_fops = { + .open = musb_softconnect_open, + .write = musb_softconnect_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + int musb_init_debugfs(struct musb *musb) { struct dentry *root; @@ -264,6 +355,13 @@ int musb_init_debugfs(struct musb *musb) goto err1; } + file = debugfs_create_file("softconnect", S_IRUGO | S_IWUSR, + root, musb, &musb_softconnect_fops); + if (!file) { + ret = -ENOMEM; + goto err1; + } + musb->debugfs_root = root; return 0; diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h index 1d44faa8625..46357e183b4 100644 --- a/drivers/usb/musb/musb_dma.h +++ b/drivers/usb/musb/musb_dma.h @@ -68,16 +68,41 @@ struct musb_hw_ep; #define is_dma_capable() (1) #endif -#if defined(CONFIG_USB_TI_CPPI_DMA) || defined(CONFIG_USB_TI_CPPI41_DMA) -#define is_cppi_enabled() 1 +#ifdef CONFIG_USB_UX500_DMA +#define musb_dma_ux500(musb) (musb->io.quirks & MUSB_DMA_UX500) +#else +#define musb_dma_ux500(musb) 0 +#endif + +#ifdef CONFIG_USB_TI_CPPI41_DMA +#define musb_dma_cppi41(musb) (musb->io.quirks & MUSB_DMA_CPPI41) +#else +#define musb_dma_cppi41(musb) 0 +#endif + +#ifdef CONFIG_USB_TI_CPPI_DMA +#define musb_dma_cppi(musb) (musb->io.quirks & MUSB_DMA_CPPI) #else -#define is_cppi_enabled() 0 +#define musb_dma_cppi(musb) 0 #endif #ifdef CONFIG_USB_TUSB_OMAP_DMA -#define tusb_dma_omap() 1 +#define tusb_dma_omap(musb) (musb->io.quirks & MUSB_DMA_TUSB_OMAP) +#else +#define tusb_dma_omap(musb) 0 +#endif + +#ifdef CONFIG_USB_INVENTRA_DMA +#define musb_dma_inventra(musb) (musb->io.quirks & MUSB_DMA_INVENTRA) #else -#define tusb_dma_omap() 0 +#define musb_dma_inventra(musb) 0 +#endif + +#if defined(CONFIG_USB_TI_CPPI_DMA) || defined(CONFIG_USB_TI_CPPI41_DMA) +#define is_cppi_enabled(musb) \ + (musb_dma_cppi(musb) || musb_dma_cppi41(musb)) +#else +#define is_cppi_enabled(musb) 0 #endif /* Anomaly 05000456 - USB Receive Interrupt Is Not Generated in DMA Mode 1 @@ -177,19 +202,41 @@ struct dma_controller { extern void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit); #ifdef CONFIG_MUSB_PIO_ONLY -static inline struct dma_controller *dma_controller_create(struct musb *m, - void __iomem *io) +static inline struct dma_controller * +musb_dma_controller_create(struct musb *m, void __iomem *io) { return NULL; } -static inline void dma_controller_destroy(struct dma_controller *d) { } +static inline void musb_dma_controller_destroy(struct dma_controller *d) { } #else -extern struct dma_controller *dma_controller_create(struct musb *, void __iomem *); +extern struct dma_controller * +(*musb_dma_controller_create)(struct musb *, void __iomem *); -extern void dma_controller_destroy(struct dma_controller *); +extern void (*musb_dma_controller_destroy)(struct dma_controller *); #endif +/* Platform specific DMA functions */ +extern struct dma_controller * +musbhs_dma_controller_create(struct musb *musb, void __iomem *base); +extern void musbhs_dma_controller_destroy(struct dma_controller *c); + +extern struct dma_controller * +tusb_dma_controller_create(struct musb *musb, void __iomem *base); +extern void tusb_dma_controller_destroy(struct dma_controller *c); + +extern struct dma_controller * +cppi_dma_controller_create(struct musb *musb, void __iomem *base); +extern void cppi_dma_controller_destroy(struct dma_controller *c); + +extern struct dma_controller * +cppi41_dma_controller_create(struct musb *musb, void __iomem *base); +extern void cppi41_dma_controller_destroy(struct dma_controller *c); + +extern struct dma_controller * +ux500_dma_controller_create(struct musb *musb, void __iomem *base); +extern void ux500_dma_controller_destroy(struct dma_controller *c); + #endif /* __MUSB_DMA_H__ */ diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 65d931a28a1..1334a3de31b 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -634,10 +634,14 @@ static void dsps_read_fifo32(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) } static struct musb_platform_ops dsps_ops = { - .quirks = MUSB_INDEXED_EP, + .quirks = MUSB_DMA_CPPI41 | MUSB_INDEXED_EP, .init = dsps_musb_init, .exit = dsps_musb_exit, +#ifdef CONFIG_USB_TI_CPPI41_DMA + .dma_init = cppi41_dma_controller_create, + .dma_exit = cppi41_dma_controller_destroy, +#endif .enable = dsps_musb_enable, .disable = dsps_musb_disable, diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 4c481cd66c7..625d482f1a9 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -366,7 +366,7 @@ static void txstate(struct musb *musb, struct musb_request *req) } #endif - if (is_cppi_enabled()) { + if (is_cppi_enabled(musb)) { /* program endpoint CSR first, then setup DMA */ csr &= ~(MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_TXPKTRDY); csr |= MUSB_TXCSR_DMAENAB | MUSB_TXCSR_DMAMODE | @@ -402,7 +402,7 @@ static void txstate(struct musb *musb, struct musb_request *req) musb_writew(epio, MUSB_TXCSR, csr); /* invariant: prequest->buf is non-null */ } - } else if (tusb_dma_omap()) + } else if (tusb_dma_omap(musb)) use_dma = use_dma && c->channel_program( musb_ep->dma, musb_ep->packet_sz, request->zero, @@ -489,6 +489,7 @@ void musb_g_tx(struct musb *musb, u8 epnum) if (request) { u8 is_dma = 0; + bool short_packet = false; if (dma && (csr & MUSB_TXCSR_DMAENAB)) { is_dma = 1; @@ -507,15 +508,18 @@ void musb_g_tx(struct musb *musb, u8 epnum) * First, maybe a terminating short packet. Some DMA * engines might handle this by themselves. */ - if ((request->zero && request->length + if ((request->zero && request->length) && (request->length % musb_ep->packet_sz == 0) && (request->actual == request->length)) -#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) - || (is_dma && (!dma->desired_mode || + short_packet = true; + + if ((musb_dma_inventra(musb) || musb_dma_ux500(musb)) && + (is_dma && (!dma->desired_mode || (request->actual & - (musb_ep->packet_sz - 1)))) -#endif - ) { + (musb_ep->packet_sz - 1))))) + short_packet = true; + + if (short_packet) { /* * On DMA completion, FIFO may not be * available yet... @@ -595,7 +599,7 @@ static void rxstate(struct musb *musb, struct musb_request *req) return; } - if (is_cppi_enabled() && is_buffer_mapped(req)) { + if (is_cppi_enabled(musb) && is_buffer_mapped(req)) { struct dma_controller *c = musb->dma_controller; struct dma_channel *channel = musb_ep->dma; @@ -772,7 +776,7 @@ static void rxstate(struct musb *musb, struct musb_request *req) fifo_count = min_t(unsigned, len, fifo_count); #ifdef CONFIG_USB_TUSB_OMAP_DMA - if (tusb_dma_omap() && is_buffer_mapped(req)) { + if (tusb_dma_omap(musb) && is_buffer_mapped(req)) { struct dma_controller *c = musb->dma_controller; struct dma_channel *channel = musb_ep->dma; u32 dma_addr = request->dma + request->actual; diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index c3d5fc9dfb5..26c65e66cc0 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -181,7 +181,7 @@ static inline void musb_h_tx_dma_start(struct musb_hw_ep *ep) /* NOTE: no locks here; caller should lock and select EP */ txcsr = musb_readw(ep->regs, MUSB_TXCSR); txcsr |= MUSB_TXCSR_DMAENAB | MUSB_TXCSR_H_WZC_BITS; - if (is_cppi_enabled()) + if (is_cppi_enabled(ep->musb)) txcsr |= MUSB_TXCSR_DMAMODE; musb_writew(ep->regs, MUSB_TXCSR, txcsr); } @@ -294,7 +294,7 @@ start: if (!hw_ep->tx_channel) musb_h_tx_start(hw_ep); - else if (is_cppi_enabled() || tusb_dma_omap()) + else if (is_cppi_enabled(musb) || tusb_dma_omap(musb)) musb_h_tx_dma_start(hw_ep); } } @@ -555,8 +555,9 @@ musb_host_packet_rx(struct musb *musb, struct urb *urb, u8 epnum, u8 iso_err) * the busy/not-empty tests are basically paranoia. */ static void -musb_rx_reinit(struct musb *musb, struct musb_qh *qh, struct musb_hw_ep *ep) +musb_rx_reinit(struct musb *musb, struct musb_qh *qh, u8 epnum) { + struct musb_hw_ep *ep = musb->endpoints + epnum; u16 csr; /* NOTE: we know the "rx" fifo reinit never triggers for ep0. @@ -594,10 +595,9 @@ musb_rx_reinit(struct musb *musb, struct musb_qh *qh, struct musb_hw_ep *ep) /* target addr and (for multipoint) hub addr/port */ if (musb->is_multipoint) { - musb_write_rxfunaddr(ep->target_regs, qh->addr_reg); - musb_write_rxhubaddr(ep->target_regs, qh->h_addr_reg); - musb_write_rxhubport(ep->target_regs, qh->h_port_reg); - + musb_write_rxfunaddr(musb, epnum, qh->addr_reg); + musb_write_rxhubaddr(musb, epnum, qh->h_addr_reg); + musb_write_rxhubport(musb, epnum, qh->h_port_reg); } else musb_writeb(musb->mregs, MUSB_FADDR, qh->addr_reg); @@ -617,23 +617,22 @@ musb_rx_reinit(struct musb *musb, struct musb_qh *qh, struct musb_hw_ep *ep) ep->rx_reinit = 0; } -static bool musb_tx_dma_program(struct dma_controller *dma, +static int musb_tx_dma_set_mode_mentor(struct dma_controller *dma, struct musb_hw_ep *hw_ep, struct musb_qh *qh, - struct urb *urb, u32 offset, u32 length) + struct urb *urb, u32 offset, + u32 *length, u8 *mode) { struct dma_channel *channel = hw_ep->tx_channel; void __iomem *epio = hw_ep->regs; u16 pkt_size = qh->maxpacket; u16 csr; - u8 mode; -#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) - if (length > channel->max_len) - length = channel->max_len; + if (*length > channel->max_len) + *length = channel->max_len; csr = musb_readw(epio, MUSB_TXCSR); - if (length > pkt_size) { - mode = 1; + if (*length > pkt_size) { + *mode = 1; csr |= MUSB_TXCSR_DMAMODE | MUSB_TXCSR_DMAENAB; /* autoset shouldn't be set in high bandwidth */ /* @@ -649,15 +648,28 @@ static bool musb_tx_dma_program(struct dma_controller *dma, can_bulk_split(hw_ep->musb, qh->type))) csr |= MUSB_TXCSR_AUTOSET; } else { - mode = 0; + *mode = 0; csr &= ~(MUSB_TXCSR_AUTOSET | MUSB_TXCSR_DMAMODE); csr |= MUSB_TXCSR_DMAENAB; /* against programmer's guide */ } channel->desired_mode = mode; musb_writew(epio, MUSB_TXCSR, csr); -#else - if (!is_cppi_enabled() && !tusb_dma_omap()) - return false; + + return 0; +} + +static int musb_tx_dma_set_mode_cppi_tusb(struct dma_controller *dma, + struct musb_hw_ep *hw_ep, + struct musb_qh *qh, + struct urb *urb, + u32 offset, + u32 *length, + u8 *mode) +{ + struct dma_channel *channel = hw_ep->tx_channel; + + if (!is_cppi_enabled(hw_ep->musb) && !tusb_dma_omap(hw_ep->musb)) + return -ENODEV; channel->actual_len = 0; @@ -665,8 +677,28 @@ static bool musb_tx_dma_program(struct dma_controller *dma, * TX uses "RNDIS" mode automatically but needs help * to identify the zero-length-final-packet case. */ - mode = (urb->transfer_flags & URB_ZERO_PACKET) ? 1 : 0; -#endif + *mode = (urb->transfer_flags & URB_ZERO_PACKET) ? 1 : 0; + + return 0; +} + +static bool musb_tx_dma_program(struct dma_controller *dma, + struct musb_hw_ep *hw_ep, struct musb_qh *qh, + struct urb *urb, u32 offset, u32 length) +{ + struct dma_channel *channel = hw_ep->tx_channel; + u16 pkt_size = qh->maxpacket; + u8 mode; + int res; + + if (musb_dma_inventra(hw_ep->musb) || musb_dma_ux500(hw_ep->musb)) + res = musb_tx_dma_set_mode_mentor(dma, hw_ep, qh, urb, + offset, &length, &mode); + else + res = musb_tx_dma_set_mode_cppi_tusb(dma, hw_ep, qh, urb, + offset, &length, &mode); + if (res) + return false; qh->segsize = length; @@ -678,6 +710,9 @@ static bool musb_tx_dma_program(struct dma_controller *dma, if (!dma->channel_program(channel, pkt_size, mode, urb->transfer_dma + offset, length)) { + void __iomem *epio = hw_ep->regs; + u16 csr; + dma->channel_release(channel); hw_ep->tx_channel = NULL; @@ -801,9 +836,9 @@ static void musb_ep_program(struct musb *musb, u8 epnum, /* target addr and (for multipoint) hub addr/port */ if (musb->is_multipoint) { - musb_write_txfunaddr(mbase, epnum, qh->addr_reg); - musb_write_txhubaddr(mbase, epnum, qh->h_addr_reg); - musb_write_txhubport(mbase, epnum, qh->h_port_reg); + musb_write_txfunaddr(musb, epnum, qh->addr_reg); + musb_write_txhubaddr(musb, epnum, qh->h_addr_reg); + musb_write_txhubport(musb, epnum, qh->h_port_reg); /* FIXME if !epnum, do the same for RX ... */ } else musb_writeb(mbase, MUSB_FADDR, qh->addr_reg); @@ -875,7 +910,7 @@ finish: u16 csr; if (hw_ep->rx_reinit) { - musb_rx_reinit(musb, qh, hw_ep); + musb_rx_reinit(musb, qh, epnum); /* init new state: toggle and NYET, maybe DMA later */ if (usb_gettoggle(urb->dev, qh->epnum, 0)) @@ -901,7 +936,7 @@ finish: /* kick things off */ - if ((is_cppi_enabled() || tusb_dma_omap()) && dma_channel) { + if ((is_cppi_enabled(musb) || tusb_dma_omap(musb)) && dma_channel) { /* Candidate for DMA */ dma_channel->actual_len = 0L; qh->segsize = len; @@ -1441,7 +1476,7 @@ done: } else if ((usb_pipeisoc(pipe) || transfer_pending) && dma) { if (musb_tx_dma_program(musb->dma_controller, hw_ep, qh, urb, offset, length)) { - if (is_cppi_enabled() || tusb_dma_omap()) + if (is_cppi_enabled(musb) || tusb_dma_omap(musb)) musb_h_tx_dma_start(hw_ep); return; } @@ -1498,9 +1533,47 @@ done: MUSB_TXCSR_H_WZC_BITS | MUSB_TXCSR_TXPKTRDY); } +#ifdef CONFIG_USB_TI_CPPI41_DMA +/* Seems to set up ISO for cppi41 and not advance len. See commit c57c41d */ +static int musb_rx_dma_iso_cppi41(struct dma_controller *dma, + struct musb_hw_ep *hw_ep, + struct musb_qh *qh, + struct urb *urb, + size_t len) +{ + struct dma_channel *channel = hw_ep->tx_channel; + void __iomem *epio = hw_ep->regs; + dma_addr_t *buf; + u32 length, res; + u16 val; -#ifdef CONFIG_USB_INVENTRA_DMA + buf = (void *)urb->iso_frame_desc[qh->iso_idx].offset + + (u32)urb->transfer_dma; + + length = urb->iso_frame_desc[qh->iso_idx].length; + val = musb_readw(epio, MUSB_RXCSR); + val |= MUSB_RXCSR_DMAENAB; + musb_writew(hw_ep->regs, MUSB_RXCSR, val); + + res = dma->channel_program(channel, qh->maxpacket, 0, + (u32)buf, length); + + return res; +} +#else +static inline int musb_rx_dma_iso_cppi41(struct dma_controller *dma, + struct musb_hw_ep *hw_ep, + struct musb_qh *qh, + struct urb *urb, + size_t len) +{ + return false; +} +#endif + +#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) || \ + defined(CONFIG_USB_TI_CPPI41_DMA) /* Host side RX (IN) using Mentor DMA works as follows: submit_urb -> - if queue was empty, ProgramEndpoint @@ -1535,7 +1608,194 @@ done: * thus be a great candidate for using mode 1 ... for all but the * last packet of one URB's transfer. */ +static int musb_rx_dma_inventra_cppi41(struct dma_controller *dma, + struct musb_hw_ep *hw_ep, + struct musb_qh *qh, + struct urb *urb, + size_t len) +{ + struct dma_channel *channel = hw_ep->rx_channel; + void __iomem *epio = hw_ep->regs; + u16 val; + int pipe; + bool done; + + pipe = urb->pipe; + + if (usb_pipeisoc(pipe)) { + struct usb_iso_packet_descriptor *d; + + d = urb->iso_frame_desc + qh->iso_idx; + d->actual_length = len; + + /* even if there was an error, we did the dma + * for iso_frame_desc->length + */ + if (d->status != -EILSEQ && d->status != -EOVERFLOW) + d->status = 0; + + if (++qh->iso_idx >= urb->number_of_packets) { + done = true; + } else { + /* REVISIT: Why ignore return value here? */ + if (musb_dma_cppi41(hw_ep->musb)) + done = musb_rx_dma_iso_cppi41(dma, hw_ep, qh, + urb, len); + done = false; + } + + } else { + /* done if urb buffer is full or short packet is recd */ + done = (urb->actual_length + len >= + urb->transfer_buffer_length + || channel->actual_len < qh->maxpacket + || channel->rx_packet_done); + } + + /* send IN token for next packet, without AUTOREQ */ + if (!done) { + val = musb_readw(epio, MUSB_RXCSR); + val |= MUSB_RXCSR_H_REQPKT; + musb_writew(epio, MUSB_RXCSR, MUSB_RXCSR_H_WZC_BITS | val); + } + + return done; +} + +/* Disadvantage of using mode 1: + * It's basically usable only for mass storage class; essentially all + * other protocols also terminate transfers on short packets. + * + * Details: + * An extra IN token is sent at the end of the transfer (due to AUTOREQ) + * If you try to use mode 1 for (transfer_buffer_length - 512), and try + * to use the extra IN token to grab the last packet using mode 0, then + * the problem is that you cannot be sure when the device will send the + * last packet and RxPktRdy set. Sometimes the packet is recd too soon + * such that it gets lost when RxCSR is re-set at the end of the mode 1 + * transfer, while sometimes it is recd just a little late so that if you + * try to configure for mode 0 soon after the mode 1 transfer is + * completed, you will find rxcount 0. Okay, so you might think why not + * wait for an interrupt when the pkt is recd. Well, you won't get any! + */ +static int musb_rx_dma_in_inventra_cppi41(struct dma_controller *dma, + struct musb_hw_ep *hw_ep, + struct musb_qh *qh, + struct urb *urb, + size_t len, + u8 iso_err) +{ + struct musb *musb = hw_ep->musb; + void __iomem *epio = hw_ep->regs; + struct dma_channel *channel = hw_ep->rx_channel; + u16 rx_count, val; + int length, pipe, done; + dma_addr_t buf; + + rx_count = musb_readw(epio, MUSB_RXCOUNT); + pipe = urb->pipe; + + if (usb_pipeisoc(pipe)) { + int d_status = 0; + struct usb_iso_packet_descriptor *d; + + d = urb->iso_frame_desc + qh->iso_idx; + if (iso_err) { + d_status = -EILSEQ; + urb->error_count++; + } + if (rx_count > d->length) { + if (d_status == 0) { + d_status = -EOVERFLOW; + urb->error_count++; + } + dev_dbg(musb->controller, "** OVERFLOW %d into %d\n", + rx_count, d->length); + + length = d->length; + } else + length = rx_count; + d->status = d_status; + buf = urb->transfer_dma + d->offset; + } else { + length = rx_count; + buf = urb->transfer_dma + urb->actual_length; + } + + channel->desired_mode = 0; +#ifdef USE_MODE1 + /* because of the issue below, mode 1 will + * only rarely behave with correct semantics. + */ + if ((urb->transfer_flags & URB_SHORT_NOT_OK) + && (urb->transfer_buffer_length - urb->actual_length) + > qh->maxpacket) + channel->desired_mode = 1; + if (rx_count < hw_ep->max_packet_sz_rx) { + length = rx_count; + channel->desired_mode = 0; + } else { + length = urb->transfer_buffer_length; + } +#endif + + /* See comments above on disadvantages of using mode 1 */ + val = musb_readw(epio, MUSB_RXCSR); + val &= ~MUSB_RXCSR_H_REQPKT; + + if (channel->desired_mode == 0) + val &= ~MUSB_RXCSR_H_AUTOREQ; + else + val |= MUSB_RXCSR_H_AUTOREQ; + val |= MUSB_RXCSR_DMAENAB; + + /* autoclear shouldn't be set in high bandwidth */ + if (qh->hb_mult == 1) + val |= MUSB_RXCSR_AUTOCLEAR; + + musb_writew(epio, MUSB_RXCSR, MUSB_RXCSR_H_WZC_BITS | val); + + /* REVISIT if when actual_length != 0, + * transfer_buffer_length needs to be + * adjusted first... + */ + done = dma->channel_program(channel, qh->maxpacket, + channel->desired_mode, + buf, length); + + if (!done) { + dma->channel_release(channel); + hw_ep->rx_channel = NULL; + channel = NULL; + val = musb_readw(epio, MUSB_RXCSR); + val &= ~(MUSB_RXCSR_DMAENAB + | MUSB_RXCSR_H_AUTOREQ + | MUSB_RXCSR_AUTOCLEAR); + musb_writew(epio, MUSB_RXCSR, val); + } + + return done; +} +#else +static inline int musb_rx_dma_inventra_cppi41(struct dma_controller *dma, + struct musb_hw_ep *hw_ep, + struct musb_qh *qh, + struct urb *urb, + size_t len) +{ + return false; +} + +static inline int musb_rx_dma_in_inventra_cppi41(struct dma_controller *dma, + struct musb_hw_ep *hw_ep, + struct musb_qh *qh, + struct urb *urb, + size_t len, + u8 iso_err) +{ + return false; +} #endif /* @@ -1546,6 +1806,7 @@ void musb_host_rx(struct musb *musb, u8 epnum) { struct urb *urb; struct musb_hw_ep *hw_ep = musb->endpoints + epnum; + struct dma_controller *c = musb->dma_controller; void __iomem *epio = hw_ep->regs; struct musb_qh *qh = hw_ep->in_qh; size_t xfer_len; @@ -1661,9 +1922,8 @@ void musb_host_rx(struct musb *musb, u8 epnum) */ /* FIXME this is _way_ too much in-line logic for Mentor DMA */ - -#if !defined(CONFIG_USB_INVENTRA_DMA) && !defined(CONFIG_USB_UX500_DMA) - if (rx_csr & MUSB_RXCSR_H_REQPKT) { + if (!musb_dma_inventra(musb) && !musb_dma_ux500(musb) && + (rx_csr & MUSB_RXCSR_H_REQPKT)) { /* REVISIT this happened for a while on some short reads... * the cleanup still needs investigation... looks bad... * and also duplicates dma cleanup code above ... plus, @@ -1684,7 +1944,7 @@ void musb_host_rx(struct musb *musb, u8 epnum) musb_writew(epio, MUSB_RXCSR, MUSB_RXCSR_H_WZC_BITS | rx_csr); } -#endif + if (dma && (rx_csr & MUSB_RXCSR_DMAENAB)) { xfer_len = dma->actual_len; @@ -1694,67 +1954,18 @@ void musb_host_rx(struct musb *musb, u8 epnum) | MUSB_RXCSR_RXPKTRDY); musb_writew(hw_ep->regs, MUSB_RXCSR, val); -#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) || \ - defined(CONFIG_USB_TI_CPPI41_DMA) - if (usb_pipeisoc(pipe)) { - struct usb_iso_packet_descriptor *d; - - d = urb->iso_frame_desc + qh->iso_idx; - d->actual_length = xfer_len; - - /* even if there was an error, we did the dma - * for iso_frame_desc->length - */ - if (d->status != -EILSEQ && d->status != -EOVERFLOW) - d->status = 0; - - if (++qh->iso_idx >= urb->number_of_packets) { - done = true; - } else { -#if defined(CONFIG_USB_TI_CPPI41_DMA) - struct dma_controller *c; - dma_addr_t *buf; - u32 length, ret; - - c = musb->dma_controller; - buf = (void *) - urb->iso_frame_desc[qh->iso_idx].offset - + (u32)urb->transfer_dma; - - length = - urb->iso_frame_desc[qh->iso_idx].length; - - val |= MUSB_RXCSR_DMAENAB; - musb_writew(hw_ep->regs, MUSB_RXCSR, val); - - ret = c->channel_program(dma, qh->maxpacket, - 0, (u32) buf, length); -#endif - done = false; - } - - } else { - /* done if urb buffer is full or short packet is recd */ - done = (urb->actual_length + xfer_len >= - urb->transfer_buffer_length - || dma->actual_len < qh->maxpacket - || dma->rx_packet_done); - } - - /* send IN token for next packet, without AUTOREQ */ - if (!done) { - val |= MUSB_RXCSR_H_REQPKT; - musb_writew(epio, MUSB_RXCSR, - MUSB_RXCSR_H_WZC_BITS | val); + if (musb_dma_inventra(musb) || musb_dma_ux500(musb) || + musb_dma_cppi41(musb)) { + done = musb_rx_dma_inventra_cppi41(c, hw_ep, qh, urb, xfer_len); + dev_dbg(hw_ep->musb->controller, + "ep %d dma %s, rxcsr %04x, rxcount %d\n", + epnum, done ? "off" : "reset", + musb_readw(epio, MUSB_RXCSR), + musb_readw(epio, MUSB_RXCOUNT)); + } else { + done = true; } - dev_dbg(musb->controller, "ep %d dma %s, rxcsr %04x, rxcount %d\n", epnum, - done ? "off" : "reset", - musb_readw(epio, MUSB_RXCSR), - musb_readw(epio, MUSB_RXCOUNT)); -#else - done = true; -#endif } else if (urb->status == -EINPROGRESS) { /* if no errors, be sure a packet is ready for unloading */ if (unlikely(!(rx_csr & MUSB_RXCSR_RXPKTRDY))) { @@ -1772,126 +1983,24 @@ void musb_host_rx(struct musb *musb, u8 epnum) } /* we are expecting IN packets */ -#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) || \ - defined(CONFIG_USB_TI_CPPI41_DMA) - if (dma) { - struct dma_controller *c; - u16 rx_count; - int ret, length; - dma_addr_t buf; - - rx_count = musb_readw(epio, MUSB_RXCOUNT); - - dev_dbg(musb->controller, "RX%d count %d, buffer 0x%llx len %d/%d\n", - epnum, rx_count, - (unsigned long long) urb->transfer_dma - + urb->actual_length, - qh->offset, - urb->transfer_buffer_length); - - c = musb->dma_controller; - - if (usb_pipeisoc(pipe)) { - int d_status = 0; - struct usb_iso_packet_descriptor *d; - - d = urb->iso_frame_desc + qh->iso_idx; - - if (iso_err) { - d_status = -EILSEQ; - urb->error_count++; - } - if (rx_count > d->length) { - if (d_status == 0) { - d_status = -EOVERFLOW; - urb->error_count++; - } - dev_dbg(musb->controller, "** OVERFLOW %d into %d\n",\ - rx_count, d->length); - - length = d->length; - } else - length = rx_count; - d->status = d_status; - buf = urb->transfer_dma + d->offset; - } else { - length = rx_count; - buf = urb->transfer_dma + - urb->actual_length; - } - - dma->desired_mode = 0; -#ifdef USE_MODE1 - /* because of the issue below, mode 1 will - * only rarely behave with correct semantics. - */ - if ((urb->transfer_flags & - URB_SHORT_NOT_OK) - && (urb->transfer_buffer_length - - urb->actual_length) - > qh->maxpacket) - dma->desired_mode = 1; - if (rx_count < hw_ep->max_packet_sz_rx) { - length = rx_count; - dma->desired_mode = 0; - } else { - length = urb->transfer_buffer_length; - } -#endif - -/* Disadvantage of using mode 1: - * It's basically usable only for mass storage class; essentially all - * other protocols also terminate transfers on short packets. - * - * Details: - * An extra IN token is sent at the end of the transfer (due to AUTOREQ) - * If you try to use mode 1 for (transfer_buffer_length - 512), and try - * to use the extra IN token to grab the last packet using mode 0, then - * the problem is that you cannot be sure when the device will send the - * last packet and RxPktRdy set. Sometimes the packet is recd too soon - * such that it gets lost when RxCSR is re-set at the end of the mode 1 - * transfer, while sometimes it is recd just a little late so that if you - * try to configure for mode 0 soon after the mode 1 transfer is - * completed, you will find rxcount 0. Okay, so you might think why not - * wait for an interrupt when the pkt is recd. Well, you won't get any! - */ - - val = musb_readw(epio, MUSB_RXCSR); - val &= ~MUSB_RXCSR_H_REQPKT; - - if (dma->desired_mode == 0) - val &= ~MUSB_RXCSR_H_AUTOREQ; + if ((musb_dma_inventra(musb) || musb_dma_ux500(musb) || + musb_dma_cppi41(musb)) && dma) { + dev_dbg(hw_ep->musb->controller, + "RX%d count %d, buffer 0x%llx len %d/%d\n", + epnum, musb_readw(epio, MUSB_RXCOUNT), + (unsigned long long) urb->transfer_dma + + urb->actual_length, + qh->offset, + urb->transfer_buffer_length); + + done = musb_rx_dma_in_inventra_cppi41(c, hw_ep, qh, + urb, xfer_len, + iso_err); + if (done) + goto finish; else - val |= MUSB_RXCSR_H_AUTOREQ; - val |= MUSB_RXCSR_DMAENAB; - - /* autoclear shouldn't be set in high bandwidth */ - if (qh->hb_mult == 1) - val |= MUSB_RXCSR_AUTOCLEAR; - - musb_writew(epio, MUSB_RXCSR, - MUSB_RXCSR_H_WZC_BITS | val); - - /* REVISIT if when actual_length != 0, - * transfer_buffer_length needs to be - * adjusted first... - */ - ret = c->channel_program( - dma, qh->maxpacket, - dma->desired_mode, buf, length); - - if (!ret) { - c->channel_release(dma); - hw_ep->rx_channel = NULL; - dma = NULL; - val = musb_readw(epio, MUSB_RXCSR); - val &= ~(MUSB_RXCSR_DMAENAB - | MUSB_RXCSR_H_AUTOREQ - | MUSB_RXCSR_AUTOCLEAR); - musb_writew(epio, MUSB_RXCSR, val); - } + dev_err(musb->controller, "error: rx_dma failed\n"); } -#endif /* Mentor DMA */ if (!dma) { unsigned int received_len; @@ -2512,6 +2621,7 @@ static void musb_free_temp_buffer(struct urb *urb) { enum dma_data_direction dir; struct musb_temp_buffer *temp; + size_t length; if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER)) return; @@ -2522,8 +2632,12 @@ static void musb_free_temp_buffer(struct urb *urb) data); if (dir == DMA_FROM_DEVICE) { - memcpy(temp->old_xfer_buffer, temp->data, - urb->transfer_buffer_length); + if (usb_pipeisoc(urb->pipe)) + length = urb->transfer_buffer_length; + else + length = urb->actual_length; + + memcpy(temp->old_xfer_buffer, temp->data, length); } urb->transfer_buffer = temp->old_xfer_buffer; kfree(temp->kmalloc_ptr); diff --git a/drivers/usb/musb/musb_io.h b/drivers/usb/musb/musb_io.h index 8a57a6f4b3a..17a80ae2067 100644 --- a/drivers/usb/musb/musb_io.h +++ b/drivers/usb/musb/musb_io.h @@ -47,6 +47,7 @@ * @fifo_offset: platform specific function to get fifo offset * @read_fifo: platform specific function to read fifo * @write_fifo: platform specific function to write fifo + * @busctl_offset: platform specific function to get busctl offset */ struct musb_io { u32 quirks; @@ -55,6 +56,7 @@ struct musb_io { u32 (*fifo_offset)(u8 epnum); void (*read_fifo)(struct musb_hw_ep *hw_ep, u16 len, u8 *buf); void (*write_fifo)(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf); + u32 (*busctl_offset)(u8 epnum, u16 offset); }; /* Do not add new entries here, add them the struct musb_io instead */ diff --git a/drivers/usb/musb/musb_regs.h b/drivers/usb/musb/musb_regs.h index 11f0be07491..cff5bcf0d00 100644 --- a/drivers/usb/musb/musb_regs.h +++ b/drivers/usb/musb/musb_regs.h @@ -300,9 +300,6 @@ #define MUSB_RXHUBADDR 0x06 #define MUSB_RXHUBPORT 0x07 -#define MUSB_BUSCTL_OFFSET(_epnum, _offset) \ - (0x80 + (8*(_epnum)) + (_offset)) - static inline void musb_write_txfifosz(void __iomem *mbase, u8 c_size) { musb_writeb(mbase, MUSB_TXFIFOSZ, c_size); @@ -364,78 +361,84 @@ static inline u16 musb_read_hwvers(void __iomem *mbase) return musb_readw(mbase, MUSB_HWVERS); } -static inline void __iomem *musb_read_target_reg_base(u8 i, void __iomem *mbase) -{ - return (MUSB_BUSCTL_OFFSET(i, 0) + mbase); -} - -static inline void musb_write_rxfunaddr(void __iomem *ep_target_regs, +static inline void musb_write_rxfunaddr(struct musb *musb, u8 epnum, u8 qh_addr_reg) { - musb_writeb(ep_target_regs, MUSB_RXFUNCADDR, qh_addr_reg); + musb_writeb(musb->mregs, + musb->io.busctl_offset(epnum, MUSB_RXFUNCADDR), + qh_addr_reg); } -static inline void musb_write_rxhubaddr(void __iomem *ep_target_regs, +static inline void musb_write_rxhubaddr(struct musb *musb, u8 epnum, u8 qh_h_addr_reg) { - musb_writeb(ep_target_regs, MUSB_RXHUBADDR, qh_h_addr_reg); + musb_writeb(musb->mregs, musb->io.busctl_offset(epnum, MUSB_RXHUBADDR), + qh_h_addr_reg); } -static inline void musb_write_rxhubport(void __iomem *ep_target_regs, +static inline void musb_write_rxhubport(struct musb *musb, u8 epnum, u8 qh_h_port_reg) { - musb_writeb(ep_target_regs, MUSB_RXHUBPORT, qh_h_port_reg); + musb_writeb(musb->mregs, musb->io.busctl_offset(epnum, MUSB_RXHUBPORT), + qh_h_port_reg); } -static inline void musb_write_txfunaddr(void __iomem *mbase, u8 epnum, +static inline void musb_write_txfunaddr(struct musb *musb, u8 epnum, u8 qh_addr_reg) { - musb_writeb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXFUNCADDR), - qh_addr_reg); + musb_writeb(musb->mregs, + musb->io.busctl_offset(epnum, MUSB_TXFUNCADDR), + qh_addr_reg); } -static inline void musb_write_txhubaddr(void __iomem *mbase, u8 epnum, +static inline void musb_write_txhubaddr(struct musb *musb, u8 epnum, u8 qh_addr_reg) { - musb_writeb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBADDR), + musb_writeb(musb->mregs, musb->io.busctl_offset(epnum, MUSB_TXHUBADDR), qh_addr_reg); } -static inline void musb_write_txhubport(void __iomem *mbase, u8 epnum, +static inline void musb_write_txhubport(struct musb *musb, u8 epnum, u8 qh_h_port_reg) { - musb_writeb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBPORT), + musb_writeb(musb->mregs, musb->io.busctl_offset(epnum, MUSB_TXHUBPORT), qh_h_port_reg); } -static inline u8 musb_read_rxfunaddr(void __iomem *mbase, u8 epnum) +static inline u8 musb_read_rxfunaddr(struct musb *musb, u8 epnum) { - return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_RXFUNCADDR)); + return musb_readb(musb->mregs, + musb->io.busctl_offset(epnum, MUSB_RXFUNCADDR)); } -static inline u8 musb_read_rxhubaddr(void __iomem *mbase, u8 epnum) +static inline u8 musb_read_rxhubaddr(struct musb *musb, u8 epnum) { - return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_RXHUBADDR)); + return musb_readb(musb->mregs, + musb->io.busctl_offset(epnum, MUSB_RXHUBADDR)); } -static inline u8 musb_read_rxhubport(void __iomem *mbase, u8 epnum) +static inline u8 musb_read_rxhubport(struct musb *musb, u8 epnum) { - return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_RXHUBPORT)); + return musb_readb(musb->mregs, + musb->io.busctl_offset(epnum, MUSB_RXHUBPORT)); } -static inline u8 musb_read_txfunaddr(void __iomem *mbase, u8 epnum) +static inline u8 musb_read_txfunaddr(struct musb *musb, u8 epnum) { - return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXFUNCADDR)); + return musb_readb(musb->mregs, + musb->io.busctl_offset(epnum, MUSB_TXFUNCADDR)); } -static inline u8 musb_read_txhubaddr(void __iomem *mbase, u8 epnum) +static inline u8 musb_read_txhubaddr(struct musb *musb, u8 epnum) { - return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBADDR)); + return musb_readb(musb->mregs, + musb->io.busctl_offset(epnum, MUSB_TXHUBADDR)); } -static inline u8 musb_read_txhubport(void __iomem *mbase, u8 epnum) +static inline u8 musb_read_txhubport(struct musb *musb, u8 epnum) { - return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBPORT)); + return musb_readb(musb->mregs, + musb->io.busctl_offset(epnum, MUSB_TXHUBPORT)); } #else /* CONFIG_BLACKFIN */ @@ -556,22 +559,17 @@ static inline u16 musb_read_hwvers(void __iomem *mbase) return MUSB_HWVERS_1900; } -static inline void __iomem *musb_read_target_reg_base(u8 i, void __iomem *mbase) -{ - return NULL; -} - -static inline void musb_write_rxfunaddr(void __iomem *ep_target_regs, +static inline void musb_write_rxfunaddr(void __iomem *mbase, u8 epnum, u8 qh_addr_req) { } -static inline void musb_write_rxhubaddr(void __iomem *ep_target_regs, +static inline void musb_write_rxhubaddr(void __iomem *mbase, u8 epnum, u8 qh_h_addr_reg) { } -static inline void musb_write_rxhubport(void __iomem *ep_target_regs, +static inline void musb_write_rxhubport(void __iomem *mbase, u8 epnum, u8 qh_h_port_reg) { } diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c index 86c4b533e90..30842bc195f 100644 --- a/drivers/usb/musb/musb_virthub.c +++ b/drivers/usb/musb/musb_virthub.c @@ -195,8 +195,10 @@ void musb_port_reset(struct musb *musb, bool do_reset) msecs_to_jiffies(50)); } else { dev_dbg(musb->controller, "root port reset stopped\n"); + musb_platform_pre_root_reset_end(musb); musb_writeb(mbase, MUSB_POWER, power & ~MUSB_POWER_RESET); + musb_platform_post_root_reset_end(musb); power = musb_readb(mbase, MUSB_POWER); if (power & MUSB_POWER_HSMODE) { diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c index ab7ec09a8af..7539c3188ff 100644 --- a/drivers/usb/musb/musbhsdma.c +++ b/drivers/usb/musb/musbhsdma.c @@ -357,7 +357,7 @@ done: return retval; } -void dma_controller_destroy(struct dma_controller *c) +void musbhs_dma_controller_destroy(struct dma_controller *c) { struct musb_dma_controller *controller = container_of(c, struct musb_dma_controller, controller); @@ -369,8 +369,10 @@ void dma_controller_destroy(struct dma_controller *c) kfree(controller); } +EXPORT_SYMBOL_GPL(musbhs_dma_controller_destroy); -struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *base) +struct dma_controller *musbhs_dma_controller_create(struct musb *musb, + void __iomem *base) { struct musb_dma_controller *controller; struct device *dev = musb->controller; @@ -398,7 +400,7 @@ struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *ba if (request_irq(irq, dma_controller_irq, 0, dev_name(musb->controller), &controller->controller)) { dev_err(dev, "request_irq %d failed!\n", irq); - dma_controller_destroy(&controller->controller); + musb_dma_controller_destroy(&controller->controller); return NULL; } @@ -407,3 +409,4 @@ struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *ba return &controller->controller; } +EXPORT_SYMBOL_GPL(musbhs_dma_controller_create); diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index cc752d8c777..70f2b8a2e6c 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -493,6 +493,11 @@ static int omap2430_musb_exit(struct musb *musb) } static const struct musb_platform_ops omap2430_ops = { + .quirks = MUSB_DMA_INVENTRA, +#ifdef CONFIG_USB_INVENTRA_DMA + .dma_init = musbhs_dma_controller_create, + .dma_exit = musbhs_dma_controller_destroy, +#endif .init = omap2430_musb_init, .exit = omap2430_musb_exit, diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index 3a5ffd57543..df7c9f46be5 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -890,7 +890,7 @@ static irqreturn_t tusb_musb_interrupt(int irq, void *__hci) dev_dbg(musb->controller, "DMA IRQ %08x\n", dma_src); real_dma_src = ~real_dma_src & dma_src; - if (tusb_dma_omap() && real_dma_src) { + if (tusb_dma_omap(musb) && real_dma_src) { int tx_source = (real_dma_src & 0xffff); int i; @@ -1181,7 +1181,7 @@ static int tusb_musb_exit(struct musb *musb) } static const struct musb_platform_ops tusb_ops = { - .quirks = MUSB_IN_TUSB, + .quirks = MUSB_DMA_TUSB_OMAP | MUSB_IN_TUSB, .init = tusb_musb_init, .exit = tusb_musb_exit, @@ -1192,6 +1192,10 @@ static const struct musb_platform_ops tusb_ops = { .writeb = tusb_writeb, .read_fifo = tusb_read_fifo, .write_fifo = tusb_write_fifo, +#ifdef CONFIG_USB_TUSB_OMAP_DMA + .dma_init = tusb_dma_controller_create, + .dma_exit = tusb_dma_controller_destroy, +#endif .enable = tusb_musb_enable, .disable = tusb_musb_disable, diff --git a/drivers/usb/musb/tusb6010.h b/drivers/usb/musb/tusb6010.h index aec86c86ce3..72cdad23ced 100644 --- a/drivers/usb/musb/tusb6010.h +++ b/drivers/usb/musb/tusb6010.h @@ -12,12 +12,6 @@ #ifndef __TUSB6010_H__ #define __TUSB6010_H__ -#ifdef CONFIG_USB_TUSB_OMAP_DMA -#define tusb_dma_omap() 1 -#else -#define tusb_dma_omap() 0 -#endif - /* VLYNQ control register. 32-bit at offset 0x000 */ #define TUSB_VLYNQ_CTRL 0x004 diff --git a/drivers/usb/musb/tusb6010_omap.c b/drivers/usb/musb/tusb6010_omap.c index 3ce152c0408..4c82077da47 100644 --- a/drivers/usb/musb/tusb6010_omap.c +++ b/drivers/usb/musb/tusb6010_omap.c @@ -625,7 +625,7 @@ static void tusb_omap_dma_release(struct dma_channel *channel) channel = NULL; } -void dma_controller_destroy(struct dma_controller *c) +void tusb_dma_controller_destroy(struct dma_controller *c) { struct tusb_omap_dma *tusb_dma; int i; @@ -644,8 +644,10 @@ void dma_controller_destroy(struct dma_controller *c) kfree(tusb_dma); } +EXPORT_SYMBOL_GPL(tusb_dma_controller_destroy); -struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *base) +struct dma_controller * +tusb_dma_controller_create(struct musb *musb, void __iomem *base) { void __iomem *tbase = musb->ctrl_base; struct tusb_omap_dma *tusb_dma; @@ -701,7 +703,8 @@ struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *ba return &tusb_dma->controller; cleanup: - dma_controller_destroy(&tusb_dma->controller); + musb_dma_controller_destroy(&tusb_dma->controller); out: return NULL; } +EXPORT_SYMBOL_GPL(tusb_dma_controller_create); diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index abf72728825..39168fe9b40 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -188,7 +188,11 @@ static int ux500_musb_exit(struct musb *musb) } static const struct musb_platform_ops ux500_ops = { - .quirks = MUSB_INDEXED_EP, + .quirks = MUSB_DMA_UX500 | MUSB_INDEXED_EP, +#ifdef CONFIG_USB_UX500_DMA + .dma_init = ux500_dma_controller_create, + .dma_exit = ux500_dma_controller_destroy, +#endif .init = ux500_musb_init, .exit = ux500_musb_exit, .fifo_mode = 5, @@ -338,7 +342,7 @@ static int ux500_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int ux500_suspend(struct device *dev) { struct ux500_glue *glue = dev_get_drvdata(dev); diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c index e93845c26bd..d0b6a1cd7f6 100644 --- a/drivers/usb/musb/ux500_dma.c +++ b/drivers/usb/musb/ux500_dma.c @@ -359,7 +359,7 @@ static int ux500_dma_controller_start(struct ux500_dma_controller *controller) return 0; } -void dma_controller_destroy(struct dma_controller *c) +void ux500_dma_controller_destroy(struct dma_controller *c) { struct ux500_dma_controller *controller = container_of(c, struct ux500_dma_controller, controller); @@ -367,9 +367,10 @@ void dma_controller_destroy(struct dma_controller *c) ux500_dma_controller_stop(controller); kfree(controller); } +EXPORT_SYMBOL_GPL(ux500_dma_controller_destroy); -struct dma_controller *dma_controller_create(struct musb *musb, - void __iomem *base) +struct dma_controller * +ux500_dma_controller_create(struct musb *musb, void __iomem *base) { struct ux500_dma_controller *controller; struct platform_device *pdev = to_platform_device(musb->controller); @@ -407,3 +408,4 @@ plat_get_fail: kzalloc_fail: return NULL; } +EXPORT_SYMBOL_GPL(ux500_dma_controller_create); diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 35b9f22c7a4..ba0dc03cb14 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -99,7 +99,7 @@ config TWL6030_USB config USB_GPIO_VBUS tristate "GPIO based peripheral-only VBUS sensing 'transceiver'" - depends on GPIOLIB + depends on GPIOLIB || COMPILE_TEST select USB_PHY help Provides simple GPIO VBUS sensing for controllers with an @@ -149,6 +149,7 @@ config USB_MSM_OTG tristate "Qualcomm on-chip USB OTG controller support" depends on (USB || USB_GADGET) && (ARCH_QCOM || COMPILE_TEST) depends on RESET_CONTROLLER + depends on EXTCON select USB_PHY help Enable this to support the USB OTG transceiver on Qualcomm chips. It @@ -194,19 +195,6 @@ config USB_RCAR_PHY To compile this driver as a module, choose M here: the module will be called phy-rcar-usb. -config USB_RCAR_GEN2_PHY - tristate "Renesas R-Car Gen2 USB PHY support" - depends on ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST - select USB_PHY - help - Say Y here to add support for the Renesas R-Car Gen2 USB PHY driver. - It is typically used to control internal USB PHY for USBHS, - and to configure shared USB channels 0 and 2. - This driver supports R8A7790 and R8A7791. - - To compile this driver as a module, choose M here: the - module will be called phy-rcar-gen2-usb. - config USB_ULPI bool "Generic ULPI Transceiver Driver" depends on ARM || ARM64 diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index 5cd78717b13..f9e386d7904 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -25,7 +25,6 @@ obj-$(CONFIG_USB_MSM_OTG) += phy-msm-usb.o obj-$(CONFIG_USB_MV_OTG) += phy-mv-usb.o obj-$(CONFIG_USB_MXS_PHY) += phy-mxs-usb.o obj-$(CONFIG_USB_RCAR_PHY) += phy-rcar-usb.o -obj-$(CONFIG_USB_RCAR_GEN2_PHY) += phy-rcar-gen2-usb.o obj-$(CONFIG_USB_ULPI) += phy-ulpi.o obj-$(CONFIG_USB_ULPI_VIEWPORT) += phy-ulpi-viewport.o obj-$(CONFIG_KEYSTONE_USB_PHY) += phy-keystone.o diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index 03ab0c699f7..0c912d3950a 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -1504,7 +1504,7 @@ static int ab8500_usb_remove(struct platform_device *pdev) return 0; } -static struct platform_device_id ab8500_usb_devtype[] = { +static const struct platform_device_id ab8500_usb_devtype[] = { { .name = "ab8500-usb", }, { .name = "ab8540-usb", }, { .name = "ab9540-usb", }, diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index c9156beeade..00c49bb1bd2 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -240,8 +240,14 @@ static void ulpi_init(struct msm_otg *motg) static int msm_phy_notify_disconnect(struct usb_phy *phy, enum usb_device_speed speed) { + struct msm_otg *motg = container_of(phy, struct msm_otg, phy); int val; + if (motg->manual_pullup) { + val = ULPI_MISC_A_VBUSVLDEXT | ULPI_MISC_A_VBUSVLDEXTSEL; + usb_phy_io_write(phy, val, ULPI_CLR(ULPI_MISC_A)); + } + /* * Put the transceiver in non-driving mode. Otherwise host * may not detect soft-disconnection. @@ -422,6 +428,24 @@ static int msm_phy_init(struct usb_phy *phy) ulpi_write(phy, ulpi_val, ULPI_USB_INT_EN_FALL); } + if (motg->manual_pullup) { + val = ULPI_MISC_A_VBUSVLDEXTSEL | ULPI_MISC_A_VBUSVLDEXT; + ulpi_write(phy, val, ULPI_SET(ULPI_MISC_A)); + + val = readl(USB_GENCONFIG_2); + val |= GENCONFIG_2_SESS_VLD_CTRL_EN; + writel(val, USB_GENCONFIG_2); + + val = readl(USB_USBCMD); + val |= USBCMD_SESS_VLD_CTRL; + writel(val, USB_USBCMD); + + val = ulpi_read(phy, ULPI_FUNC_CTRL); + val &= ~ULPI_FUNC_CTRL_OPMODE_MASK; + val |= ULPI_FUNC_CTRL_OPMODE_NORMAL; + ulpi_write(phy, val, ULPI_FUNC_CTRL); + } + if (motg->phy_number) writel(readl(USB_PHY_CTRL2) | BIT(16), USB_PHY_CTRL2); @@ -1436,9 +1460,42 @@ static const struct of_device_id msm_otg_dt_match[] = { }; MODULE_DEVICE_TABLE(of, msm_otg_dt_match); +static int msm_otg_vbus_notifier(struct notifier_block *nb, unsigned long event, + void *ptr) +{ + struct msm_usb_cable *vbus = container_of(nb, struct msm_usb_cable, nb); + struct msm_otg *motg = container_of(vbus, struct msm_otg, vbus); + + if (event) + set_bit(B_SESS_VLD, &motg->inputs); + else + clear_bit(B_SESS_VLD, &motg->inputs); + + schedule_work(&motg->sm_work); + + return NOTIFY_DONE; +} + +static int msm_otg_id_notifier(struct notifier_block *nb, unsigned long event, + void *ptr) +{ + struct msm_usb_cable *id = container_of(nb, struct msm_usb_cable, nb); + struct msm_otg *motg = container_of(id, struct msm_otg, id); + + if (event) + clear_bit(ID, &motg->inputs); + else + set_bit(ID, &motg->inputs); + + schedule_work(&motg->sm_work); + + return NOTIFY_DONE; +} + static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg) { struct msm_otg_platform_data *pdata; + struct extcon_dev *ext_id, *ext_vbus; const struct of_device_id *id; struct device_node *node = pdev->dev.of_node; struct property *prop; @@ -1487,6 +1544,54 @@ static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg) motg->vdd_levels[VDD_LEVEL_MAX] = tmp[VDD_LEVEL_MAX]; } + motg->manual_pullup = of_property_read_bool(node, "qcom,manual-pullup"); + + ext_id = ERR_PTR(-ENODEV); + ext_vbus = ERR_PTR(-ENODEV); + if (of_property_read_bool(node, "extcon")) { + + /* Each one of them is not mandatory */ + ext_vbus = extcon_get_edev_by_phandle(&pdev->dev, 0); + if (IS_ERR(ext_vbus) && PTR_ERR(ext_vbus) != -ENODEV) + return PTR_ERR(ext_vbus); + + ext_id = extcon_get_edev_by_phandle(&pdev->dev, 1); + if (IS_ERR(ext_id) && PTR_ERR(ext_id) != -ENODEV) + return PTR_ERR(ext_id); + } + + if (!IS_ERR(ext_vbus)) { + motg->vbus.nb.notifier_call = msm_otg_vbus_notifier; + ret = extcon_register_interest(&motg->vbus.conn, ext_vbus->name, + "USB", &motg->vbus.nb); + if (ret < 0) { + dev_err(&pdev->dev, "register VBUS notifier failed\n"); + return ret; + } + + ret = extcon_get_cable_state(ext_vbus, "USB"); + if (ret) + set_bit(B_SESS_VLD, &motg->inputs); + else + clear_bit(B_SESS_VLD, &motg->inputs); + } + + if (!IS_ERR(ext_id)) { + motg->id.nb.notifier_call = msm_otg_id_notifier; + ret = extcon_register_interest(&motg->id.conn, ext_id->name, + "USB-HOST", &motg->id.nb); + if (ret < 0) { + dev_err(&pdev->dev, "register ID notifier failed\n"); + return ret; + } + + ret = extcon_get_cable_state(ext_id, "USB-HOST"); + if (ret) + clear_bit(ID, &motg->inputs); + else + set_bit(ID, &motg->inputs); + } + prop = of_find_property(node, "qcom,phy-init-sequence", &len); if (!prop || !len) return 0; @@ -1700,6 +1805,11 @@ static int msm_otg_remove(struct platform_device *pdev) if (phy->otg->host || phy->otg->gadget) return -EBUSY; + if (motg->id.conn.edev) + extcon_unregister_interest(&motg->id.conn); + if (motg->vbus.conn.edev) + extcon_unregister_interest(&motg->vbus.conn); + msm_otg_debugfs_cleanup(); cancel_delayed_work_sync(&motg->chg_work); cancel_work_sync(&motg->sm_work); diff --git a/drivers/usb/phy/phy-rcar-gen2-usb.c b/drivers/usb/phy/phy-rcar-gen2-usb.c deleted file mode 100644 index f81800b6562..00000000000 --- a/drivers/usb/phy/phy-rcar-gen2-usb.c +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Renesas R-Car Gen2 USB phy driver - * - * Copyright (C) 2013 Renesas Solutions Corp. - * Copyright (C) 2013 Cogent Embedded, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include <linux/clk.h> -#include <linux/delay.h> -#include <linux/io.h> -#include <linux/module.h> -#include <linux/platform_data/usb-rcar-gen2-phy.h> -#include <linux/platform_device.h> -#include <linux/spinlock.h> -#include <linux/usb/otg.h> - -struct rcar_gen2_usb_phy_priv { - struct usb_phy phy; - void __iomem *base; - struct clk *clk; - spinlock_t lock; - int usecount; - u32 ugctrl2; -}; - -#define usb_phy_to_priv(p) container_of(p, struct rcar_gen2_usb_phy_priv, phy) - -/* Low Power Status register */ -#define USBHS_LPSTS_REG 0x02 -#define USBHS_LPSTS_SUSPM (1 << 14) - -/* USB General control register */ -#define USBHS_UGCTRL_REG 0x80 -#define USBHS_UGCTRL_CONNECT (1 << 2) -#define USBHS_UGCTRL_PLLRESET (1 << 0) - -/* USB General control register 2 */ -#define USBHS_UGCTRL2_REG 0x84 -#define USBHS_UGCTRL2_USB0_PCI (1 << 4) -#define USBHS_UGCTRL2_USB0_HS (3 << 4) -#define USBHS_UGCTRL2_USB2_PCI (0 << 31) -#define USBHS_UGCTRL2_USB2_SS (1 << 31) - -/* USB General status register */ -#define USBHS_UGSTS_REG 0x88 -#define USBHS_UGSTS_LOCK (1 << 8) - -/* Enable USBHS internal phy */ -static int __rcar_gen2_usbhs_phy_enable(void __iomem *base) -{ - u32 val; - int i; - - /* USBHS PHY power on */ - val = ioread32(base + USBHS_UGCTRL_REG); - val &= ~USBHS_UGCTRL_PLLRESET; - iowrite32(val, base + USBHS_UGCTRL_REG); - - val = ioread16(base + USBHS_LPSTS_REG); - val |= USBHS_LPSTS_SUSPM; - iowrite16(val, base + USBHS_LPSTS_REG); - - for (i = 0; i < 20; i++) { - val = ioread32(base + USBHS_UGSTS_REG); - if ((val & USBHS_UGSTS_LOCK) == USBHS_UGSTS_LOCK) { - val = ioread32(base + USBHS_UGCTRL_REG); - val |= USBHS_UGCTRL_CONNECT; - iowrite32(val, base + USBHS_UGCTRL_REG); - return 0; - } - udelay(1); - } - - /* Timed out waiting for the PLL lock */ - return -ETIMEDOUT; -} - -/* Disable USBHS internal phy */ -static int __rcar_gen2_usbhs_phy_disable(void __iomem *base) -{ - u32 val; - - /* USBHS PHY power off */ - val = ioread32(base + USBHS_UGCTRL_REG); - val &= ~USBHS_UGCTRL_CONNECT; - iowrite32(val, base + USBHS_UGCTRL_REG); - - val = ioread16(base + USBHS_LPSTS_REG); - val &= ~USBHS_LPSTS_SUSPM; - iowrite16(val, base + USBHS_LPSTS_REG); - - val = ioread32(base + USBHS_UGCTRL_REG); - val |= USBHS_UGCTRL_PLLRESET; - iowrite32(val, base + USBHS_UGCTRL_REG); - return 0; -} - -/* Setup USB channels */ -static void __rcar_gen2_usb_phy_init(struct rcar_gen2_usb_phy_priv *priv) -{ - u32 val; - - clk_prepare_enable(priv->clk); - - /* Set USB channels in the USBHS UGCTRL2 register */ - val = ioread32(priv->base + USBHS_UGCTRL2_REG); - val &= ~(USBHS_UGCTRL2_USB0_HS | USBHS_UGCTRL2_USB2_SS); - val |= priv->ugctrl2; - iowrite32(val, priv->base + USBHS_UGCTRL2_REG); -} - -/* Shutdown USB channels */ -static void __rcar_gen2_usb_phy_shutdown(struct rcar_gen2_usb_phy_priv *priv) -{ - __rcar_gen2_usbhs_phy_disable(priv->base); - clk_disable_unprepare(priv->clk); -} - -static int rcar_gen2_usb_phy_set_suspend(struct usb_phy *phy, int suspend) -{ - struct rcar_gen2_usb_phy_priv *priv = usb_phy_to_priv(phy); - unsigned long flags; - int retval; - - spin_lock_irqsave(&priv->lock, flags); - retval = suspend ? __rcar_gen2_usbhs_phy_disable(priv->base) : - __rcar_gen2_usbhs_phy_enable(priv->base); - spin_unlock_irqrestore(&priv->lock, flags); - return retval; -} - -static int rcar_gen2_usb_phy_init(struct usb_phy *phy) -{ - struct rcar_gen2_usb_phy_priv *priv = usb_phy_to_priv(phy); - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - /* - * Enable the clock and setup USB channels - * if it's the first user - */ - if (!priv->usecount++) - __rcar_gen2_usb_phy_init(priv); - spin_unlock_irqrestore(&priv->lock, flags); - return 0; -} - -static void rcar_gen2_usb_phy_shutdown(struct usb_phy *phy) -{ - struct rcar_gen2_usb_phy_priv *priv = usb_phy_to_priv(phy); - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - if (!priv->usecount) { - dev_warn(phy->dev, "Trying to disable phy with 0 usecount\n"); - goto out; - } - - /* Disable everything if it's the last user */ - if (!--priv->usecount) - __rcar_gen2_usb_phy_shutdown(priv); -out: - spin_unlock_irqrestore(&priv->lock, flags); -} - -static int rcar_gen2_usb_phy_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct rcar_gen2_phy_platform_data *pdata; - struct rcar_gen2_usb_phy_priv *priv; - struct resource *res; - void __iomem *base; - struct clk *clk; - int retval; - - pdata = dev_get_platdata(dev); - if (!pdata) { - dev_err(dev, "No platform data\n"); - return -EINVAL; - } - - clk = devm_clk_get(dev, "usbhs"); - if (IS_ERR(clk)) { - dev_err(dev, "Can't get the clock\n"); - return PTR_ERR(clk); - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - spin_lock_init(&priv->lock); - priv->clk = clk; - priv->base = base; - priv->ugctrl2 = pdata->chan0_pci ? - USBHS_UGCTRL2_USB0_PCI : USBHS_UGCTRL2_USB0_HS; - priv->ugctrl2 |= pdata->chan2_pci ? - USBHS_UGCTRL2_USB2_PCI : USBHS_UGCTRL2_USB2_SS; - priv->phy.dev = dev; - priv->phy.label = dev_name(dev); - priv->phy.init = rcar_gen2_usb_phy_init; - priv->phy.shutdown = rcar_gen2_usb_phy_shutdown; - priv->phy.set_suspend = rcar_gen2_usb_phy_set_suspend; - - retval = usb_add_phy_dev(&priv->phy); - if (retval < 0) { - dev_err(dev, "Failed to add USB phy\n"); - return retval; - } - - platform_set_drvdata(pdev, priv); - - return retval; -} - -static int rcar_gen2_usb_phy_remove(struct platform_device *pdev) -{ - struct rcar_gen2_usb_phy_priv *priv = platform_get_drvdata(pdev); - - usb_remove_phy(&priv->phy); - - return 0; -} - -static struct platform_driver rcar_gen2_usb_phy_driver = { - .driver = { - .name = "usb_phy_rcar_gen2", - }, - .probe = rcar_gen2_usb_phy_probe, - .remove = rcar_gen2_usb_phy_remove, -}; - -module_platform_driver(rcar_gen2_usb_phy_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Renesas R-Car Gen2 USB phy"); -MODULE_AUTHOR("Valentine Barshak <valentine.barshak@cogentembedded.com>"); diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c index d1cd6b50f52..98f75d2842b 100644 --- a/drivers/usb/phy/phy.c +++ b/drivers/usb/phy/phy.c @@ -22,6 +22,11 @@ static LIST_HEAD(phy_list); static LIST_HEAD(phy_bind_list); static DEFINE_SPINLOCK(phy_lock); +struct phy_devm { + struct usb_phy *phy; + struct notifier_block *nb; +}; + static struct usb_phy *__usb_find_phy(struct list_head *list, enum usb_phy_type type) { @@ -79,6 +84,15 @@ static void devm_usb_phy_release(struct device *dev, void *res) usb_put_phy(phy); } +static void devm_usb_phy_release2(struct device *dev, void *_res) +{ + struct phy_devm *res = _res; + + if (res->nb) + usb_unregister_notifier(res->phy, res->nb); + usb_put_phy(res->phy); +} + static int devm_usb_phy_match(struct device *dev, void *res, void *match_data) { struct usb_phy **phy = res; @@ -153,40 +167,30 @@ err0: EXPORT_SYMBOL_GPL(usb_get_phy); /** - * devm_usb_get_phy_by_phandle - find the USB PHY by phandle + * devm_usb_get_phy_by_node - find the USB PHY by device_node * @dev - device that requests this phy - * @phandle - name of the property holding the phy phandle value - * @index - the index of the phy + * @node - the device_node for the phy device. + * @nb - a notifier_block to register with the phy. * - * Returns the phy driver associated with the given phandle value, + * Returns the phy driver associated with the given device_node, * after getting a refcount to it, -ENODEV if there is no such phy or - * -EPROBE_DEFER if there is a phandle to the phy, but the device is - * not yet loaded. While at that, it also associates the device with + * -EPROBE_DEFER if the device is not yet loaded. While at that, it + * also associates the device with * the phy using devres. On driver detach, release function is invoked * on the devres data, then, devres data is freed. * - * For use by USB host and peripheral drivers. + * For use by peripheral drivers for devices related to a phy, + * such as a charger. */ -struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, - const char *phandle, u8 index) +struct usb_phy *devm_usb_get_phy_by_node(struct device *dev, + struct device_node *node, + struct notifier_block *nb) { - struct usb_phy *phy = ERR_PTR(-ENOMEM), **ptr; + struct usb_phy *phy = ERR_PTR(-ENOMEM); + struct phy_devm *ptr; unsigned long flags; - struct device_node *node; - if (!dev->of_node) { - dev_dbg(dev, "device does not have a device node entry\n"); - return ERR_PTR(-EINVAL); - } - - node = of_parse_phandle(dev->of_node, phandle, index); - if (!node) { - dev_dbg(dev, "failed to get %s phandle in %s node\n", phandle, - dev->of_node->full_name); - return ERR_PTR(-ENODEV); - } - - ptr = devres_alloc(devm_usb_phy_release, sizeof(*ptr), GFP_KERNEL); + ptr = devres_alloc(devm_usb_phy_release2, sizeof(*ptr), GFP_KERNEL); if (!ptr) { dev_dbg(dev, "failed to allocate memory for devres\n"); goto err0; @@ -205,8 +209,10 @@ struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, devres_free(ptr); goto err1; } - - *ptr = phy; + if (nb) + usb_register_notifier(phy, nb); + ptr->phy = phy; + ptr->nb = nb; devres_add(dev, ptr); get_device(phy->dev); @@ -215,10 +221,47 @@ err1: spin_unlock_irqrestore(&phy_lock, flags); err0: - of_node_put(node); return phy; } +EXPORT_SYMBOL_GPL(devm_usb_get_phy_by_node); + +/** + * devm_usb_get_phy_by_phandle - find the USB PHY by phandle + * @dev - device that requests this phy + * @phandle - name of the property holding the phy phandle value + * @index - the index of the phy + * + * Returns the phy driver associated with the given phandle value, + * after getting a refcount to it, -ENODEV if there is no such phy or + * -EPROBE_DEFER if there is a phandle to the phy, but the device is + * not yet loaded. While at that, it also associates the device with + * the phy using devres. On driver detach, release function is invoked + * on the devres data, then, devres data is freed. + * + * For use by USB host and peripheral drivers. + */ +struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, + const char *phandle, u8 index) +{ + struct device_node *node; + struct usb_phy *phy; + + if (!dev->of_node) { + dev_dbg(dev, "device does not have a device node entry\n"); + return ERR_PTR(-EINVAL); + } + + node = of_parse_phandle(dev->of_node, phandle, index); + if (!node) { + dev_dbg(dev, "failed to get %s phandle in %s node\n", phandle, + dev->of_node->full_name); + return ERR_PTR(-ENODEV); + } + phy = devm_usb_get_phy_by_node(dev, node, NULL); + of_node_put(node); + return phy; +} EXPORT_SYMBOL_GPL(devm_usb_get_phy_by_phandle); /** diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 0f7e850fd4a..e8bf40808b3 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -466,11 +466,15 @@ static int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev) static const struct of_device_id usbhs_of_match[] = { { .compatible = "renesas,usbhs-r8a7790", - .data = (void *)USBHS_TYPE_R8A7790, + .data = (void *)USBHS_TYPE_RCAR_GEN2, }, { .compatible = "renesas,usbhs-r8a7791", - .data = (void *)USBHS_TYPE_R8A7791, + .data = (void *)USBHS_TYPE_RCAR_GEN2, + }, + { + .compatible = "renesas,usbhs-r8a7794", + .data = (void *)USBHS_TYPE_RCAR_GEN2, }, { }, }; @@ -497,14 +501,8 @@ static struct renesas_usbhs_platform_info *usbhs_parse_dt(struct device *dev) if (gpio > 0) dparam->enable_gpio = gpio; - switch (dparam->type) { - case USBHS_TYPE_R8A7790: - case USBHS_TYPE_R8A7791: + if (dparam->type == USBHS_TYPE_RCAR_GEN2) dparam->has_usb_dmac = 1; - break; - default: - break; - } return info; } @@ -559,8 +557,7 @@ static int usbhs_probe(struct platform_device *pdev) sizeof(struct renesas_usbhs_driver_param)); switch (priv->dparam.type) { - case USBHS_TYPE_R8A7790: - case USBHS_TYPE_R8A7791: + case USBHS_TYPE_RCAR_GEN2: priv->pfunc = usbhs_rcar2_ops; if (!priv->dparam.pipe_type) { priv->dparam.pipe_type = usbhsc_new_pipe_type; diff --git a/drivers/usb/renesas_usbhs/fifo.h b/drivers/usb/renesas_usbhs/fifo.h index 04d3f8abad9..c7d9b86d51b 100644 --- a/drivers/usb/renesas_usbhs/fifo.h +++ b/drivers/usb/renesas_usbhs/fifo.h @@ -44,10 +44,11 @@ struct usbhs_fifo_info { struct usbhs_fifo dfifo[USBHS_MAX_NUM_DFIFO]; }; #define usbhsf_get_dnfifo(p, n) (&((p)->fifo_info.dfifo[n])) -#define usbhs_for_each_dfifo(priv, dfifo, i) \ - for ((i) = 0, dfifo = usbhsf_get_dnfifo(priv, (i)); \ - ((i) < USBHS_MAX_NUM_DFIFO); \ - (i)++, dfifo = usbhsf_get_dnfifo(priv, (i))) +#define usbhs_for_each_dfifo(priv, dfifo, i) \ + for ((i) = 0; \ + ((i) < USBHS_MAX_NUM_DFIFO) && \ + ((dfifo) = usbhsf_get_dnfifo(priv, (i))); \ + (i)++) struct usbhs_pkt_handle; struct usbhs_pkt { diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c index 9a705b15b3a..d4be5d59489 100644 --- a/drivers/usb/renesas_usbhs/mod.c +++ b/drivers/usb/renesas_usbhs/mod.c @@ -218,10 +218,14 @@ static int usbhs_status_get_each_irq(struct usbhs_priv *priv, /******************** spin lock ********************/ usbhs_lock(priv, flags); state->intsts0 = usbhs_read(priv, INTSTS0); - state->intsts1 = usbhs_read(priv, INTSTS1); - intenb0 = usbhs_read(priv, INTENB0); - intenb1 = usbhs_read(priv, INTENB1); + + if (usbhs_mod_is_host(priv)) { + state->intsts1 = usbhs_read(priv, INTSTS1); + intenb1 = usbhs_read(priv, INTENB1); + } else { + state->intsts1 = intenb1 = 0; + } /* mask */ if (mod) { @@ -275,7 +279,8 @@ static irqreturn_t usbhs_interrupt(int irq, void *data) * - Function :: VALID bit should 0 */ usbhs_write(priv, INTSTS0, ~irq_state.intsts0 & INTSTS0_MAGIC); - usbhs_write(priv, INTSTS1, ~irq_state.intsts1 & INTSTS1_MAGIC); + if (usbhs_mod_is_host(priv)) + usbhs_write(priv, INTSTS1, ~irq_state.intsts1 & INTSTS1_MAGIC); usbhs_write(priv, BRDYSTS, ~irq_state.brdysts); usbhs_write(priv, NRDYSTS, ~irq_state.nrdysts); @@ -303,19 +308,20 @@ static irqreturn_t usbhs_interrupt(int irq, void *data) if (irq_state.intsts0 & BRDY) usbhs_mod_call(priv, irq_ready, priv, &irq_state); - /* INTSTS1 */ - if (irq_state.intsts1 & ATTCH) - usbhs_mod_call(priv, irq_attch, priv, &irq_state); - - if (irq_state.intsts1 & DTCH) - usbhs_mod_call(priv, irq_dtch, priv, &irq_state); + if (usbhs_mod_is_host(priv)) { + /* INTSTS1 */ + if (irq_state.intsts1 & ATTCH) + usbhs_mod_call(priv, irq_attch, priv, &irq_state); - if (irq_state.intsts1 & SIGN) - usbhs_mod_call(priv, irq_sign, priv, &irq_state); + if (irq_state.intsts1 & DTCH) + usbhs_mod_call(priv, irq_dtch, priv, &irq_state); - if (irq_state.intsts1 & SACK) - usbhs_mod_call(priv, irq_sack, priv, &irq_state); + if (irq_state.intsts1 & SIGN) + usbhs_mod_call(priv, irq_sign, priv, &irq_state); + if (irq_state.intsts1 & SACK) + usbhs_mod_call(priv, irq_sack, priv, &irq_state); + } return IRQ_HANDLED; } @@ -334,7 +340,8 @@ void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod) * - update INTSTS0 */ usbhs_write(priv, INTENB0, 0); - usbhs_write(priv, INTENB1, 0); + if (usbhs_mod_is_host(priv)) + usbhs_write(priv, INTENB1, 0); usbhs_write(priv, BEMPENB, 0); usbhs_write(priv, BRDYENB, 0); @@ -368,25 +375,27 @@ void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod) intenb0 |= BRDYE; } - /* - * INTSTS1 - */ - if (mod->irq_attch) - intenb1 |= ATTCHE; + if (usbhs_mod_is_host(priv)) { + /* + * INTSTS1 + */ + if (mod->irq_attch) + intenb1 |= ATTCHE; - if (mod->irq_dtch) - intenb1 |= DTCHE; + if (mod->irq_dtch) + intenb1 |= DTCHE; - if (mod->irq_sign) - intenb1 |= SIGNE; + if (mod->irq_sign) + intenb1 |= SIGNE; - if (mod->irq_sack) - intenb1 |= SACKE; + if (mod->irq_sack) + intenb1 |= SACKE; + } } if (intenb0) usbhs_write(priv, INTENB0, intenb0); - if (intenb1) + if (usbhs_mod_is_host(priv) && intenb1) usbhs_write(priv, INTENB1, intenb1); } diff --git a/include/dt-bindings/clock/pistachio-clk.h b/include/dt-bindings/clock/pistachio-clk.h index dfda0c330d2..bfb915dfe92 100644 --- a/include/dt-bindings/clock/pistachio-clk.h +++ b/include/dt-bindings/clock/pistachio-clk.h @@ -21,6 +21,7 @@ /* Fixed-factor clocks */ #define CLK_WIFI_DIV4 16 #define CLK_WIFI_DIV8 17 +#define CLK_SDHOST_DIV4 18 /* Gate clocks */ #define CLK_MIPS 32 diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 3bfd56778c2..7ab00d61d30 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -629,4 +629,10 @@ struct mcb_device_id { kernel_ulong_t driver_data; }; +struct ulpi_device_id { + __u16 vendor; + __u16 product; + kernel_ulong_t driver_data; +}; + #endif /* LINUX_MOD_DEVICETABLE_H */ diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index e5409524bb0..6f01653d377 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -178,9 +178,9 @@ struct spi_nor { int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops); void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops); int (*read_xfer)(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg, - u8 *buf, size_t len); + u8 *buf, size_t len, size_t *retlen); int (*write_xfer)(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg, - u8 *buf, size_t len); + u8 *buf, size_t len, size_t *retlen); int (*read_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len); int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len, int write_enable); diff --git a/include/linux/phy/phy-sun4i-usb.h b/include/linux/phy/phy-sun4i-usb.h new file mode 100644 index 00000000000..50aed92ea89 --- /dev/null +++ b/include/linux/phy/phy-sun4i-usb.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2015 Hans de Goede <hdegoede@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef PHY_SUN4I_USB_H_ +#define PHY_SUN4I_USB_H_ + +#include "phy.h" + +/** + * sun4i_usb_phy_set_squelch_detect() - Enable/disable squelch detect + * @phy: reference to a sun4i usb phy + * @enabled: wether to enable or disable squelch detect + */ +void sun4i_usb_phy_set_squelch_detect(struct phy *phy, bool enabled); + +#endif diff --git a/include/linux/platform_data/usb-rcar-gen2-phy.h b/include/linux/platform_data/usb-rcar-gen2-phy.h deleted file mode 100644 index dd3ba46c0d9..00000000000 --- a/include/linux/platform_data/usb-rcar-gen2-phy.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2013 Renesas Solutions Corp. - * Copyright (C) 2013 Cogent Embedded, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef __USB_RCAR_GEN2_PHY_H -#define __USB_RCAR_GEN2_PHY_H - -#include <linux/types.h> - -struct rcar_gen2_phy_platform_data { - /* USB channel 0 configuration */ - bool chan0_pci:1; /* true: PCI USB host 0, false: USBHS */ - /* USB channel 2 configuration */ - bool chan2_pci:1; /* true: PCI USB host 2, false: USBSS */ -}; - -#endif diff --git a/include/linux/spi/cc2520.h b/include/linux/spi/cc2520.h index 14137e33c79..a591179e422 100644 --- a/include/linux/spi/cc2520.h +++ b/include/linux/spi/cc2520.h @@ -22,6 +22,7 @@ struct cc2520_platform_data { int reset; int vreg; unsigned int extclockfreq; + bool registerclk; bool amplified; }; diff --git a/include/linux/ulpi/driver.h b/include/linux/ulpi/driver.h new file mode 100644 index 00000000000..388f6e08b9d --- /dev/null +++ b/include/linux/ulpi/driver.h @@ -0,0 +1,60 @@ +#ifndef __LINUX_ULPI_DRIVER_H +#define __LINUX_ULPI_DRIVER_H + +#include <linux/mod_devicetable.h> + +#include <linux/device.h> + +struct ulpi_ops; + +/** + * struct ulpi - describes ULPI PHY device + * @id: vendor and product ids for ULPI device + * @ops: I/O access + * @dev: device interface + */ +struct ulpi { + struct ulpi_device_id id; + struct ulpi_ops *ops; + struct device dev; +}; + +#define to_ulpi_dev(d) container_of(d, struct ulpi, dev) + +static inline void ulpi_set_drvdata(struct ulpi *ulpi, void *data) +{ + dev_set_drvdata(&ulpi->dev, data); +} + +static inline void *ulpi_get_drvdata(struct ulpi *ulpi) +{ + return dev_get_drvdata(&ulpi->dev); +} + +/** + * struct ulpi_driver - describes a ULPI PHY driver + * @id_table: array of device identifiers supported by this driver + * @probe: binds this driver to ULPI device + * @remove: unbinds this driver from ULPI device + * @driver: the name and owner members must be initialized by the drivers + */ +struct ulpi_driver { + const struct ulpi_device_id *id_table; + int (*probe)(struct ulpi *ulpi); + void (*remove)(struct ulpi *ulpi); + struct device_driver driver; +}; + +#define to_ulpi_driver(d) container_of(d, struct ulpi_driver, driver) + +int ulpi_register_driver(struct ulpi_driver *drv); +void ulpi_unregister_driver(struct ulpi_driver *drv); + +#define module_ulpi_driver(__ulpi_driver) \ + module_driver(__ulpi_driver, ulpi_register_driver, \ + ulpi_unregister_driver) + +int ulpi_read(struct ulpi *ulpi, u8 addr); +int ulpi_write(struct ulpi *ulpi, u8 addr, u8 val); + +#endif /* __LINUX_ULPI_DRIVER_H */ diff --git a/include/linux/ulpi/interface.h b/include/linux/ulpi/interface.h new file mode 100644 index 00000000000..4de8ab49103 --- /dev/null +++ b/include/linux/ulpi/interface.h @@ -0,0 +1,23 @@ +#ifndef __LINUX_ULPI_INTERFACE_H +#define __LINUX_ULPI_INTERFACE_H + +#include <linux/types.h> + +struct ulpi; + +/** + * struct ulpi_ops - ULPI register access + * @dev: the interface provider + * @read: read operation for ULPI register access + * @write: write operation for ULPI register access + */ +struct ulpi_ops { + struct device *dev; + int (*read)(struct ulpi_ops *ops, u8 addr); + int (*write)(struct ulpi_ops *ops, u8 addr, u8 val); +}; + +struct ulpi *ulpi_register_interface(struct device *, struct ulpi_ops *); +void ulpi_unregister_interface(struct ulpi *); + +#endif /* __LINUX_ULPI_INTERFACE_H */ diff --git a/include/linux/ulpi/regs.h b/include/linux/ulpi/regs.h new file mode 100644 index 00000000000..b5b8b880456 --- /dev/null +++ b/include/linux/ulpi/regs.h @@ -0,0 +1,130 @@ +#ifndef __LINUX_ULPI_REGS_H +#define __LINUX_ULPI_REGS_H + +/* + * Macros for Set and Clear + * See ULPI 1.1 specification to find the registers with Set and Clear offsets + */ +#define ULPI_SET(a) (a + 1) +#define ULPI_CLR(a) (a + 2) + +/* + * Register Map + */ +#define ULPI_VENDOR_ID_LOW 0x00 +#define ULPI_VENDOR_ID_HIGH 0x01 +#define ULPI_PRODUCT_ID_LOW 0x02 +#define ULPI_PRODUCT_ID_HIGH 0x03 +#define ULPI_FUNC_CTRL 0x04 +#define ULPI_IFC_CTRL 0x07 +#define ULPI_OTG_CTRL 0x0a +#define ULPI_USB_INT_EN_RISE 0x0d +#define ULPI_USB_INT_EN_FALL 0x10 +#define ULPI_USB_INT_STS 0x13 +#define ULPI_USB_INT_LATCH 0x14 +#define ULPI_DEBUG 0x15 +#define ULPI_SCRATCH 0x16 +/* Optional Carkit Registers */ +#define ULPI_CARKIT_CTRL 0x19 +#define ULPI_CARKIT_INT_DELAY 0x1c +#define ULPI_CARKIT_INT_EN 0x1d +#define ULPI_CARKIT_INT_STS 0x20 +#define ULPI_CARKIT_INT_LATCH 0x21 +#define ULPI_CARKIT_PLS_CTRL 0x22 +/* Other Optional Registers */ +#define ULPI_TX_POS_WIDTH 0x25 +#define ULPI_TX_NEG_WIDTH 0x26 +#define ULPI_POLARITY_RECOVERY 0x27 +/* Access Extended Register Set */ +#define ULPI_ACCESS_EXTENDED 0x2f +/* Vendor Specific */ +#define ULPI_VENDOR_SPECIFIC 0x30 +/* Extended Registers */ +#define ULPI_EXT_VENDOR_SPECIFIC 0x80 + +/* + * Register Bits + */ + +/* Function Control */ +#define ULPI_FUNC_CTRL_XCVRSEL BIT(0) +#define ULPI_FUNC_CTRL_XCVRSEL_MASK 0x3 +#define ULPI_FUNC_CTRL_HIGH_SPEED 0x0 +#define ULPI_FUNC_CTRL_FULL_SPEED 0x1 +#define ULPI_FUNC_CTRL_LOW_SPEED 0x2 +#define ULPI_FUNC_CTRL_FS4LS 0x3 +#define ULPI_FUNC_CTRL_TERMSELECT BIT(2) +#define ULPI_FUNC_CTRL_OPMODE BIT(3) +#define ULPI_FUNC_CTRL_OPMODE_MASK (0x3 << 3) +#define ULPI_FUNC_CTRL_OPMODE_NORMAL (0x0 << 3) +#define ULPI_FUNC_CTRL_OPMODE_NONDRIVING (0x1 << 3) +#define ULPI_FUNC_CTRL_OPMODE_DISABLE_NRZI (0x2 << 3) +#define ULPI_FUNC_CTRL_OPMODE_NOSYNC_NOEOP (0x3 << 3) +#define ULPI_FUNC_CTRL_RESET BIT(5) +#define ULPI_FUNC_CTRL_SUSPENDM BIT(6) + +/* Interface Control */ +#define ULPI_IFC_CTRL_6_PIN_SERIAL_MODE BIT(0) +#define ULPI_IFC_CTRL_3_PIN_SERIAL_MODE BIT(1) +#define ULPI_IFC_CTRL_CARKITMODE BIT(2) +#define ULPI_IFC_CTRL_CLOCKSUSPENDM BIT(3) +#define ULPI_IFC_CTRL_AUTORESUME BIT(4) +#define ULPI_IFC_CTRL_EXTERNAL_VBUS BIT(5) +#define ULPI_IFC_CTRL_PASSTHRU BIT(6) +#define ULPI_IFC_CTRL_PROTECT_IFC_DISABLE BIT(7) + +/* OTG Control */ +#define ULPI_OTG_CTRL_ID_PULLUP BIT(0) +#define ULPI_OTG_CTRL_DP_PULLDOWN BIT(1) +#define ULPI_OTG_CTRL_DM_PULLDOWN BIT(2) +#define ULPI_OTG_CTRL_DISCHRGVBUS BIT(3) +#define ULPI_OTG_CTRL_CHRGVBUS BIT(4) +#define ULPI_OTG_CTRL_DRVVBUS BIT(5) +#define ULPI_OTG_CTRL_DRVVBUS_EXT BIT(6) +#define ULPI_OTG_CTRL_EXTVBUSIND BIT(7) + +/* USB Interrupt Enable Rising, + * USB Interrupt Enable Falling, + * USB Interrupt Status and + * USB Interrupt Latch + */ +#define ULPI_INT_HOST_DISCONNECT BIT(0) +#define ULPI_INT_VBUS_VALID BIT(1) +#define ULPI_INT_SESS_VALID BIT(2) +#define ULPI_INT_SESS_END BIT(3) +#define ULPI_INT_IDGRD BIT(4) + +/* Debug */ +#define ULPI_DEBUG_LINESTATE0 BIT(0) +#define ULPI_DEBUG_LINESTATE1 BIT(1) + +/* Carkit Control */ +#define ULPI_CARKIT_CTRL_CARKITPWR BIT(0) +#define ULPI_CARKIT_CTRL_IDGNDDRV BIT(1) +#define ULPI_CARKIT_CTRL_TXDEN BIT(2) +#define ULPI_CARKIT_CTRL_RXDEN BIT(3) +#define ULPI_CARKIT_CTRL_SPKLEFTEN BIT(4) +#define ULPI_CARKIT_CTRL_SPKRIGHTEN BIT(5) +#define ULPI_CARKIT_CTRL_MICEN BIT(6) + +/* Carkit Interrupt Enable */ +#define ULPI_CARKIT_INT_EN_IDFLOAT_RISE BIT(0) +#define ULPI_CARKIT_INT_EN_IDFLOAT_FALL BIT(1) +#define ULPI_CARKIT_INT_EN_CARINTDET BIT(2) +#define ULPI_CARKIT_INT_EN_DP_RISE BIT(3) +#define ULPI_CARKIT_INT_EN_DP_FALL BIT(4) + +/* Carkit Interrupt Status and + * Carkit Interrupt Latch + */ +#define ULPI_CARKIT_INT_IDFLOAT BIT(0) +#define ULPI_CARKIT_INT_CARINTDET BIT(1) +#define ULPI_CARKIT_INT_DP BIT(2) + +/* Carkit Pulse Control*/ +#define ULPI_CARKIT_PLS_CTRL_TXPLSEN BIT(0) +#define ULPI_CARKIT_PLS_CTRL_RXPLSEN BIT(1) +#define ULPI_CARKIT_PLS_CTRL_SPKRLEFT_BIASEN BIT(2) +#define ULPI_CARKIT_PLS_CTRL_SPKRRIGHT_BIASEN BIT(3) + +#endif /* __LINUX_ULPI_REGS_H */ diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h index 7dbecf9a465..e55a1504266 100644 --- a/include/linux/usb/msm_hsusb.h +++ b/include/linux/usb/msm_hsusb.h @@ -18,6 +18,7 @@ #ifndef __ASM_ARCH_MSM_HSUSB_H #define __ASM_ARCH_MSM_HSUSB_H +#include <linux/extcon.h> #include <linux/types.h> #include <linux/usb/otg.h> #include <linux/clk.h> @@ -120,6 +121,17 @@ struct msm_otg_platform_data { }; /** + * struct msm_usb_cable - structure for exteternal connector cable + * state tracking + * @nb: hold event notification callback + * @conn: used for notification registration + */ +struct msm_usb_cable { + struct notifier_block nb; + struct extcon_specific_cable_nb conn; +}; + +/** * struct msm_otg: OTG driver data. Shared by HCD and DCD. * @otg: USB OTG Transceiver structure. * @pdata: otg device platform data. @@ -138,6 +150,11 @@ struct msm_otg_platform_data { * @chg_type: The type of charger attached. * @dcd_retires: The retry count used to track Data contact * detection process. + * @manual_pullup: true if VBUS is not routed to USB controller/phy + * and controller driver therefore enables pull-up explicitly before + * starting controller using usbcmd run/stop bit. + * @vbus: VBUS signal state trakining, using extcon framework + * @id: ID signal state trakining, using extcon framework */ struct msm_otg { struct usb_phy phy; @@ -166,6 +183,11 @@ struct msm_otg { struct reset_control *phy_rst; struct reset_control *link_rst; int vdd_levels[3]; + + bool manual_pullup; + + struct msm_usb_cable vbus; + struct msm_usb_cable id; }; #endif diff --git a/include/linux/usb/msm_hsusb_hw.h b/include/linux/usb/msm_hsusb_hw.h index a29f6030afb..e159b39f67a 100644 --- a/include/linux/usb/msm_hsusb_hw.h +++ b/include/linux/usb/msm_hsusb_hw.h @@ -21,6 +21,8 @@ #define USB_AHBBURST (MSM_USB_BASE + 0x0090) #define USB_AHBMODE (MSM_USB_BASE + 0x0098) +#define USB_GENCONFIG_2 (MSM_USB_BASE + 0x00a0) + #define USB_CAPLENGTH (MSM_USB_BASE + 0x0100) /* 8 bit */ #define USB_USBCMD (MSM_USB_BASE + 0x0140) @@ -30,6 +32,9 @@ #define USB_PHY_CTRL (MSM_USB_BASE + 0x0240) #define USB_PHY_CTRL2 (MSM_USB_BASE + 0x0278) +#define GENCONFIG_2_SESS_VLD_CTRL_EN BIT(7) +#define USBCMD_SESS_VLD_CTRL BIT(25) + #define USBCMD_RESET 2 #define USB_USBINTR (MSM_USB_BASE + 0x0148) @@ -50,6 +55,10 @@ #define ULPI_PWR_CLK_MNG_REG 0x88 #define OTG_COMP_DISABLE BIT(0) +#define ULPI_MISC_A 0x96 +#define ULPI_MISC_A_VBUSVLDEXTSEL BIT(1) +#define ULPI_MISC_A_VBUSVLDEXT BIT(0) + #define ASYNC_INTR_CTRL (1 << 29) /* Enable async interrupt */ #define ULPI_STP_CTRL (1 << 30) /* Block communication with PHY */ #define PHY_RETEN (1 << 1) /* PHY retention enable/disable */ diff --git a/include/linux/usb/net2280.h b/include/linux/usb/net2280.h index 148b8fa5b1a..72512022447 100644 --- a/include/linux/usb/net2280.h +++ b/include/linux/usb/net2280.h @@ -168,6 +168,9 @@ struct net2280_regs { #define ENDPOINT_B_INTERRUPT 2 #define ENDPOINT_A_INTERRUPT 1 #define ENDPOINT_0_INTERRUPT 0 +#define USB3380_IRQSTAT0_EP_INTR_MASK_IN (0xF << 17) +#define USB3380_IRQSTAT0_EP_INTR_MASK_OUT (0xF << 1) + u32 irqstat1; #define POWER_STATE_CHANGE_INTERRUPT 27 #define PCI_ARBITER_TIMEOUT_INTERRUPT 26 diff --git a/include/linux/usb/phy.h b/include/linux/usb/phy.h index bc91b5d380f..e39f251cf86 100644 --- a/include/linux/usb/phy.h +++ b/include/linux/usb/phy.h @@ -205,6 +205,8 @@ extern struct usb_phy *usb_get_phy_dev(struct device *dev, u8 index); extern struct usb_phy *devm_usb_get_phy_dev(struct device *dev, u8 index); extern struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, const char *phandle, u8 index); +extern struct usb_phy *devm_usb_get_phy_by_node(struct device *dev, + struct device_node *node, struct notifier_block *nb); extern void usb_put_phy(struct usb_phy *); extern void devm_usb_put_phy(struct device *dev, struct usb_phy *x); extern int usb_bind_phy(const char *dev_name, u8 index, @@ -238,6 +240,12 @@ static inline struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, return ERR_PTR(-ENXIO); } +static inline struct usb_phy *devm_usb_get_phy_by_node(struct device *dev, + struct device_node *node, struct notifier_block *nb) +{ + return ERR_PTR(-ENXIO); +} + static inline void usb_put_phy(struct usb_phy *x) { } diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h index f06529c1414..3dd5a781da9 100644 --- a/include/linux/usb/renesas_usbhs.h +++ b/include/linux/usb/renesas_usbhs.h @@ -169,8 +169,7 @@ struct renesas_usbhs_driver_param { #define USBHS_USB_DMAC_XFER_SIZE 32 /* hardcode the xfer size */ }; -#define USBHS_TYPE_R8A7790 1 -#define USBHS_TYPE_R8A7791 2 +#define USBHS_TYPE_RCAR_GEN2 1 /* * option: diff --git a/include/linux/usb/ulpi.h b/include/linux/usb/ulpi.h index 5c295c26ad3..5f07407a367 100644 --- a/include/linux/usb/ulpi.h +++ b/include/linux/usb/ulpi.h @@ -12,6 +12,8 @@ #define __LINUX_USB_ULPI_H #include <linux/usb/otg.h> +#include <linux/ulpi/regs.h> + /*-------------------------------------------------------------------------*/ /* @@ -49,138 +51,6 @@ /*-------------------------------------------------------------------------*/ -/* - * Macros for Set and Clear - * See ULPI 1.1 specification to find the registers with Set and Clear offsets - */ -#define ULPI_SET(a) (a + 1) -#define ULPI_CLR(a) (a + 2) - -/*-------------------------------------------------------------------------*/ - -/* - * Register Map - */ -#define ULPI_VENDOR_ID_LOW 0x00 -#define ULPI_VENDOR_ID_HIGH 0x01 -#define ULPI_PRODUCT_ID_LOW 0x02 -#define ULPI_PRODUCT_ID_HIGH 0x03 -#define ULPI_FUNC_CTRL 0x04 -#define ULPI_IFC_CTRL 0x07 -#define ULPI_OTG_CTRL 0x0a -#define ULPI_USB_INT_EN_RISE 0x0d -#define ULPI_USB_INT_EN_FALL 0x10 -#define ULPI_USB_INT_STS 0x13 -#define ULPI_USB_INT_LATCH 0x14 -#define ULPI_DEBUG 0x15 -#define ULPI_SCRATCH 0x16 -/* Optional Carkit Registers */ -#define ULPI_CARCIT_CTRL 0x19 -#define ULPI_CARCIT_INT_DELAY 0x1c -#define ULPI_CARCIT_INT_EN 0x1d -#define ULPI_CARCIT_INT_STS 0x20 -#define ULPI_CARCIT_INT_LATCH 0x21 -#define ULPI_CARCIT_PLS_CTRL 0x22 -/* Other Optional Registers */ -#define ULPI_TX_POS_WIDTH 0x25 -#define ULPI_TX_NEG_WIDTH 0x26 -#define ULPI_POLARITY_RECOVERY 0x27 -/* Access Extended Register Set */ -#define ULPI_ACCESS_EXTENDED 0x2f -/* Vendor Specific */ -#define ULPI_VENDOR_SPECIFIC 0x30 -/* Extended Registers */ -#define ULPI_EXT_VENDOR_SPECIFIC 0x80 - -/*-------------------------------------------------------------------------*/ - -/* - * Register Bits - */ - -/* Function Control */ -#define ULPI_FUNC_CTRL_XCVRSEL (1 << 0) -#define ULPI_FUNC_CTRL_XCVRSEL_MASK (3 << 0) -#define ULPI_FUNC_CTRL_HIGH_SPEED (0 << 0) -#define ULPI_FUNC_CTRL_FULL_SPEED (1 << 0) -#define ULPI_FUNC_CTRL_LOW_SPEED (2 << 0) -#define ULPI_FUNC_CTRL_FS4LS (3 << 0) -#define ULPI_FUNC_CTRL_TERMSELECT (1 << 2) -#define ULPI_FUNC_CTRL_OPMODE (1 << 3) -#define ULPI_FUNC_CTRL_OPMODE_MASK (3 << 3) -#define ULPI_FUNC_CTRL_OPMODE_NORMAL (0 << 3) -#define ULPI_FUNC_CTRL_OPMODE_NONDRIVING (1 << 3) -#define ULPI_FUNC_CTRL_OPMODE_DISABLE_NRZI (2 << 3) -#define ULPI_FUNC_CTRL_OPMODE_NOSYNC_NOEOP (3 << 3) -#define ULPI_FUNC_CTRL_RESET (1 << 5) -#define ULPI_FUNC_CTRL_SUSPENDM (1 << 6) - -/* Interface Control */ -#define ULPI_IFC_CTRL_6_PIN_SERIAL_MODE (1 << 0) -#define ULPI_IFC_CTRL_3_PIN_SERIAL_MODE (1 << 1) -#define ULPI_IFC_CTRL_CARKITMODE (1 << 2) -#define ULPI_IFC_CTRL_CLOCKSUSPENDM (1 << 3) -#define ULPI_IFC_CTRL_AUTORESUME (1 << 4) -#define ULPI_IFC_CTRL_EXTERNAL_VBUS (1 << 5) -#define ULPI_IFC_CTRL_PASSTHRU (1 << 6) -#define ULPI_IFC_CTRL_PROTECT_IFC_DISABLE (1 << 7) - -/* OTG Control */ -#define ULPI_OTG_CTRL_ID_PULLUP (1 << 0) -#define ULPI_OTG_CTRL_DP_PULLDOWN (1 << 1) -#define ULPI_OTG_CTRL_DM_PULLDOWN (1 << 2) -#define ULPI_OTG_CTRL_DISCHRGVBUS (1 << 3) -#define ULPI_OTG_CTRL_CHRGVBUS (1 << 4) -#define ULPI_OTG_CTRL_DRVVBUS (1 << 5) -#define ULPI_OTG_CTRL_DRVVBUS_EXT (1 << 6) -#define ULPI_OTG_CTRL_EXTVBUSIND (1 << 7) - -/* USB Interrupt Enable Rising, - * USB Interrupt Enable Falling, - * USB Interrupt Status and - * USB Interrupt Latch - */ -#define ULPI_INT_HOST_DISCONNECT (1 << 0) -#define ULPI_INT_VBUS_VALID (1 << 1) -#define ULPI_INT_SESS_VALID (1 << 2) -#define ULPI_INT_SESS_END (1 << 3) -#define ULPI_INT_IDGRD (1 << 4) - -/* Debug */ -#define ULPI_DEBUG_LINESTATE0 (1 << 0) -#define ULPI_DEBUG_LINESTATE1 (1 << 1) - -/* Carkit Control */ -#define ULPI_CARKIT_CTRL_CARKITPWR (1 << 0) -#define ULPI_CARKIT_CTRL_IDGNDDRV (1 << 1) -#define ULPI_CARKIT_CTRL_TXDEN (1 << 2) -#define ULPI_CARKIT_CTRL_RXDEN (1 << 3) -#define ULPI_CARKIT_CTRL_SPKLEFTEN (1 << 4) -#define ULPI_CARKIT_CTRL_SPKRIGHTEN (1 << 5) -#define ULPI_CARKIT_CTRL_MICEN (1 << 6) - -/* Carkit Interrupt Enable */ -#define ULPI_CARKIT_INT_EN_IDFLOAT_RISE (1 << 0) -#define ULPI_CARKIT_INT_EN_IDFLOAT_FALL (1 << 1) -#define ULPI_CARKIT_INT_EN_CARINTDET (1 << 2) -#define ULPI_CARKIT_INT_EN_DP_RISE (1 << 3) -#define ULPI_CARKIT_INT_EN_DP_FALL (1 << 4) - -/* Carkit Interrupt Status and - * Carkit Interrupt Latch - */ -#define ULPI_CARKIT_INT_IDFLOAT (1 << 0) -#define ULPI_CARKIT_INT_CARINTDET (1 << 1) -#define ULPI_CARKIT_INT_DP (1 << 2) - -/* Carkit Pulse Control*/ -#define ULPI_CARKIT_PLS_CTRL_TXPLSEN (1 << 0) -#define ULPI_CARKIT_PLS_CTRL_RXPLSEN (1 << 1) -#define ULPI_CARKIT_PLS_CTRL_SPKRLEFT_BIASEN (1 << 2) -#define ULPI_CARKIT_PLS_CTRL_SPKRRIGHT_BIASEN (1 << 3) - -/*-------------------------------------------------------------------------*/ - #if IS_ENABLED(CONFIG_USB_ULPI) struct usb_phy *otg_ulpi_create(struct usb_phy_io_ops *ops, unsigned int flags); diff --git a/include/linux/usb/usb338x.h b/include/linux/usb/usb338x.h index f92eb635b9d..11525d8d89a 100644 --- a/include/linux/usb/usb338x.h +++ b/include/linux/usb/usb338x.h @@ -43,6 +43,10 @@ #define IN_ENDPOINT_TYPE 12 #define OUT_ENDPOINT_ENABLE 10 #define OUT_ENDPOINT_TYPE 8 +#define USB3380_EP_CFG_MASK_IN ((0x3 << IN_ENDPOINT_TYPE) | \ + BIT(IN_ENDPOINT_ENABLE)) +#define USB3380_EP_CFG_MASK_OUT ((0x3 << OUT_ENDPOINT_TYPE) | \ + BIT(OUT_ENDPOINT_ENABLE)) struct usb338x_usb_ext_regs { u32 usbclass; diff --git a/scripts/mod/devicetable-offsets.c b/scripts/mod/devicetable-offsets.c index fce36d0f689..ada8417362c 100644 --- a/scripts/mod/devicetable-offsets.c +++ b/scripts/mod/devicetable-offsets.c @@ -189,5 +189,9 @@ int main(void) DEVID_FIELD(rio_device_id, asm_did); DEVID_FIELD(rio_device_id, asm_vid); + DEVID(ulpi_device_id); + DEVID_FIELD(ulpi_device_id, vendor); + DEVID_FIELD(ulpi_device_id, product); + return 0; } diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 78691d51a47..a7a8560db44 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -1192,6 +1192,19 @@ static int do_rio_entry(const char *filename, } ADD_TO_DEVTABLE("rapidio", rio_device_id, do_rio_entry); +/* Looks like: ulpi:vNpN */ +static int do_ulpi_entry(const char *filename, void *symval, + char *alias) +{ + DEF_FIELD(symval, ulpi_device_id, vendor); + DEF_FIELD(symval, ulpi_device_id, product); + + sprintf(alias, "ulpi:v%04xp%04x", vendor, product); + + return 1; +} +ADD_TO_DEVTABLE("ulpi", ulpi_device_id, do_ulpi_entry); + /* Does namelen bytes of name exactly match the symbol? */ static bool sym_is(const char *name, unsigned namelen, const char *symbol) { diff --git a/sound/soc/img/pistachio.c b/sound/soc/img/pistachio.c index 1a8a6d70028..e4b6546f450 100644 --- a/sound/soc/img/pistachio.c +++ b/sound/soc/img/pistachio.c @@ -30,10 +30,10 @@ #include "pistachio-event-timer.h" -#define PLL_RATE_8000_16000_32000_48000_96000_192000 147456000 -#define PLL_RATE_11025_22050_44100_64000_88200_176400 135475200 -#define PISTACHIO_MAX_DIV 256 -#define PISTACHIO_MIN_MCLK_FREQ (135475200 / 256) +#define PISTACHIO_PLL_RATE_A 147456000 +#define PISTACHIO_PLL_RATE_B 135475200 +#define PISTACHIO_MAX_DIV 256 +#define PISTACHIO_MIN_MCLK_FREQ (135475200 / 256) #define PISTACHIO_CLOCK_MASTER_EXT -1 #define PISTACHIO_CLOCK_MASTER_LOOPBACK -2 @@ -398,16 +398,16 @@ static inline int pistachio_card_get_pll_rate(unsigned int rate) case 16000: case 32000: case 48000: + case 64000: case 96000: case 192000: - return PLL_RATE_8000_16000_32000_48000_96000_192000; + return PISTACHIO_PLL_RATE_A; case 11025: case 22050: case 44100: - case 64000: case 88200: case 176400: - return PLL_RATE_11025_22050_44100_64000_88200_176400; + return PISTACHIO_PLL_RATE_B; default: return -EINVAL; } @@ -455,6 +455,7 @@ static int pistachio_card_change_rate(struct pistachio_card *pbc, int ret; mutex_lock(&pbc->rate_mutex); + *active_rate = 0; ret = _pistachio_card_change_rate(pbc, rate, i2s); if (!ret) *active_rate = rate; @@ -1543,7 +1544,7 @@ static int pistachio_card_init_rates(struct pistachio_card *pbc) unsigned int rate; int ret; - rate = PLL_RATE_11025_22050_44100_64000_88200_176400; + rate = PISTACHIO_PLL_RATE_B; ret = clk_set_rate(pbc->audio_pll, rate); if (ret) return ret; |