aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKees Cook <keescook@google.com>2016-03-08 09:56:09 -0800
committerKees Cook <keescook@google.com>2016-03-08 09:56:09 -0800
commit2ad1a30bbdffd6f53c33bb8562a8eef7f4732310 (patch)
tree19a0625964fd70e51ac8ee4c1041a65b6c24d3be
parentd12b59f861a0eb9b4313ea5a8993989cf6e80f2a (diff)
parentbabe638e91d0f2804b3d7357e4aa575f8fa2248b (diff)
downloadv4.1-2ad1a30bbdffd6f53c33bb8562a8eef7f4732310.tar.gz
Merge branch 'master' into update
-rw-r--r--Documentation/devicetree/bindings/misc/uboot-bootcount.txt34
-rw-r--r--Documentation/devicetree/bindings/net/ieee802154/cc2520.txt4
-rw-r--r--Documentation/devicetree/bindings/power/twl-charger.txt10
-rw-r--r--Documentation/devicetree/bindings/usb/dwc3-st.txt7
-rw-r--r--Documentation/devicetree/bindings/usb/dwc3.txt2
-rw-r--r--Documentation/devicetree/bindings/usb/msm-hsusb.txt11
-rw-r--r--Documentation/devicetree/bindings/usb/renesas_usbhs.txt1
-rw-r--r--Documentation/devicetree/bindings/usb/twlxxxx-usb.txt3
-rw-r--r--Documentation/usb/gadget-testing.txt4
-rw-r--r--MAINTAINERS7
-rw-r--r--arch/mips/boot/dts/pistachio/Makefile2
-rw-r--r--arch/mips/boot/dts/pistachio/pistachio.dtsi12
-rw-r--r--arch/mips/boot/dts/pistachio/pistachio_beetle.dtsi27
-rw-r--r--arch/mips/boot/dts/pistachio/pistachio_beetle_boardtest.dts99
-rw-r--r--arch/mips/boot/dts/pistachio/pistachio_beetle_mbub.dts31
-rw-r--r--arch/mips/boot/dts/pistachio/pistachio_board_uboot.dtsi15
-rw-r--r--arch/mips/boot/dts/pistachio/pistachio_bub.dts3
-rw-r--r--arch/mips/boot/dts/pistachio/pistachio_concerto.dtsi40
-rw-r--r--arch/mips/boot/dts/pistachio/pistachio_concerto_mbub.dts69
-rw-r--r--arch/mips/boot/dts/pistachio/pistachio_marduk.dts34
-rw-r--r--arch/mips/configs/pistachio_defconfig14
-rw-r--r--arch/mips/configs/pistachio_prodtest_defconfig390
-rw-r--r--arch/mips/include/asm/syscall.h4
-rw-r--r--arch/mips/kernel/scall32-o32.S39
-rw-r--r--arch/mips/kernel/scall64-64.S38
-rw-r--r--arch/mips/kernel/scall64-n32.S19
-rw-r--r--arch/mips/kernel/scall64-o32.S19
-rw-r--r--arch/mips/pistachio/init.c25
-rw-r--r--drivers/clk/pistachio/clk-pistachio.c11
-rw-r--r--drivers/clocksource/time-pistachio.c2
-rw-r--r--drivers/i2c/busses/i2c-img-scb.c185
-rw-r--r--drivers/misc/Kconfig7
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/atu/atu_clk_maintainer.c1
-rw-r--r--drivers/misc/uboot_bootcount.c132
-rw-r--r--drivers/mtd/devices/m25p80.c112
-rw-r--r--drivers/mtd/spi-nor/Kconfig8
-rw-r--r--drivers/mtd/spi-nor/Makefile1
-rw-r--r--drivers/mtd/spi-nor/spi-nor-common.h8
-rw-r--r--drivers/mtd/spi-nor/spi-nor.c14
-rw-r--r--drivers/mtd/spi-nor/winbond-otp.c482
-rw-r--r--drivers/mtd/spi-nor/winbond-otp.h20
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c4
-rw-r--r--drivers/net/ieee802154/cc2520.c56
-rw-r--r--drivers/net/wireless/uccp420wlan/inc/core.h75
-rw-r--r--drivers/net/wireless/uccp420wlan/inc/hal.h6
-rw-r--r--drivers/net/wireless/uccp420wlan/inc/host_umac_if.h35
-rw-r--r--drivers/net/wireless/uccp420wlan/inc/umac_if.h6
-rw-r--r--drivers/net/wireless/uccp420wlan/inc/version.h2
-rw-r--r--drivers/net/wireless/uccp420wlan/src/80211_if.c1125
-rw-r--r--drivers/net/wireless/uccp420wlan/src/core.c229
-rw-r--r--drivers/net/wireless/uccp420wlan/src/hal_hostport.c50
-rw-r--r--drivers/net/wireless/uccp420wlan/src/hal_hostport.h2
-rw-r--r--drivers/net/wireless/uccp420wlan/src/tx.c1121
-rw-r--r--drivers/net/wireless/uccp420wlan/src/umac_if.c120
-rw-r--r--drivers/phy/Kconfig7
-rw-r--r--drivers/phy/Makefile1
-rw-r--r--drivers/phy/phy-sun4i-usb.c9
-rw-r--r--drivers/phy/phy-tusb1210.c153
-rw-r--r--drivers/phy/ulpi_phy.h31
-rw-r--r--drivers/power/twl4030_charger.c21
-rw-r--r--drivers/rtc/rtc-pistachio.c2
-rw-r--r--drivers/soc/img/connectivity/img-hostport.c11
-rw-r--r--drivers/spi/spi-img-spfi.c34
-rw-r--r--drivers/usb/common/Makefile1
-rw-r--r--drivers/usb/common/ulpi.c255
-rw-r--r--drivers/usb/core/Kconfig20
-rw-r--r--drivers/usb/dwc2/Kconfig8
-rw-r--r--drivers/usb/dwc2/Makefile9
-rw-r--r--drivers/usb/dwc2/core.c439
-rw-r--r--drivers/usb/dwc2/core.h120
-rw-r--r--drivers/usb/dwc2/core_intr.c45
-rw-r--r--drivers/usb/dwc2/debug.h27
-rw-r--r--drivers/usb/dwc2/debugfs.c771
-rw-r--r--drivers/usb/dwc2/gadget.c459
-rw-r--r--drivers/usb/dwc2/hcd.c66
-rw-r--r--drivers/usb/dwc2/hcd.h7
-rw-r--r--drivers/usb/dwc2/hcd_intr.c66
-rw-r--r--drivers/usb/dwc2/hcd_queue.c10
-rw-r--r--drivers/usb/dwc2/platform.c25
-rw-r--r--drivers/usb/dwc3/Kconfig7
-rw-r--r--drivers/usb/dwc3/Makefile4
-rw-r--r--drivers/usb/dwc3/core.c108
-rw-r--r--drivers/usb/dwc3/core.h26
-rw-r--r--drivers/usb/dwc3/dwc3-pci.c36
-rw-r--r--drivers/usb/dwc3/gadget.c10
-rw-r--r--drivers/usb/dwc3/platform_data.h2
-rw-r--r--drivers/usb/dwc3/ulpi.c91
-rw-r--r--drivers/usb/gadget/epautoconf.c24
-rw-r--r--drivers/usb/gadget/function/f_rndis.c62
-rw-r--r--drivers/usb/gadget/function/rndis.c366
-rw-r--r--drivers/usb/gadget/function/rndis.h35
-rw-r--r--drivers/usb/gadget/function/u_rndis.h2
-rw-r--r--drivers/usb/gadget/function/uvc.h1
-rw-r--r--drivers/usb/gadget/udc/atmel_usba_udc.c6
-rw-r--r--drivers/usb/gadget/udc/net2280.c140
-rw-r--r--drivers/usb/gadget/udc/s3c2410_udc.c28
-rw-r--r--drivers/usb/musb/am35x.c8
-rw-r--r--drivers/usb/musb/blackfin.c5
-rw-r--r--drivers/usb/musb/cppi_dma.c9
-rw-r--r--drivers/usb/musb/da8xx.c6
-rw-r--r--drivers/usb/musb/davinci.c7
-rw-r--r--drivers/usb/musb/jz4740.c6
-rw-r--r--drivers/usb/musb/musb_core.c81
-rw-r--r--drivers/usb/musb/musb_core.h28
-rw-r--r--drivers/usb/musb/musb_cppi41.c8
-rw-r--r--drivers/usb/musb/musb_debugfs.c100
-rw-r--r--drivers/usb/musb/musb_dma.h67
-rw-r--r--drivers/usb/musb/musb_dsps.c6
-rw-r--r--drivers/usb/musb/musb_gadget.c24
-rw-r--r--drivers/usb/musb/musb_host.c536
-rw-r--r--drivers/usb/musb/musb_io.h2
-rw-r--r--drivers/usb/musb/musb_regs.h80
-rw-r--r--drivers/usb/musb/musb_virthub.c2
-rw-r--r--drivers/usb/musb/musbhsdma.c9
-rw-r--r--drivers/usb/musb/omap2430.c5
-rw-r--r--drivers/usb/musb/tusb6010.c8
-rw-r--r--drivers/usb/musb/tusb6010.h6
-rw-r--r--drivers/usb/musb/tusb6010_omap.c9
-rw-r--r--drivers/usb/musb/ux500.c8
-rw-r--r--drivers/usb/musb/ux500_dma.c8
-rw-r--r--drivers/usb/phy/Kconfig16
-rw-r--r--drivers/usb/phy/Makefile1
-rw-r--r--drivers/usb/phy/phy-ab8500-usb.c2
-rw-r--r--drivers/usb/phy/phy-msm-usb.c110
-rw-r--r--drivers/usb/phy/phy-rcar-gen2-usb.c246
-rw-r--r--drivers/usb/phy/phy.c97
-rw-r--r--drivers/usb/renesas_usbhs/common.c19
-rw-r--r--drivers/usb/renesas_usbhs/fifo.h9
-rw-r--r--drivers/usb/renesas_usbhs/mod.c63
-rw-r--r--include/dt-bindings/clock/pistachio-clk.h1
-rw-r--r--include/linux/mod_devicetable.h6
-rw-r--r--include/linux/mtd/spi-nor.h4
-rw-r--r--include/linux/phy/phy-sun4i-usb.h26
-rw-r--r--include/linux/platform_data/usb-rcar-gen2-phy.h22
-rw-r--r--include/linux/spi/cc2520.h1
-rw-r--r--include/linux/ulpi/driver.h60
-rw-r--r--include/linux/ulpi/interface.h23
-rw-r--r--include/linux/ulpi/regs.h130
-rw-r--r--include/linux/usb/msm_hsusb.h22
-rw-r--r--include/linux/usb/msm_hsusb_hw.h9
-rw-r--r--include/linux/usb/net2280.h3
-rw-r--r--include/linux/usb/phy.h8
-rw-r--r--include/linux/usb/renesas_usbhs.h3
-rw-r--r--include/linux/usb/ulpi.h134
-rw-r--r--include/linux/usb/usb338x.h4
-rw-r--r--scripts/mod/devicetable-offsets.c4
-rw-r--r--scripts/mod/file2alias.c13
-rw-r--r--sound/soc/img/pistachio.c17
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 = <&parallel_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";
};
-&parallel_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 = <&reg_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;