Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next
Johan Hedberg says:
====================
pull request: bluetooth-next 2016-03-12
Here's the last bluetooth-next pull request for the 4.6 kernel.
- New USB ID for AR3012 in btusb
- New BCM2E55 ACPI ID
- Buffer overflow fix for the Add Advertising command
- Support for a new Bluetooth LE limited privacy mode
- Fix for firmware activation in btmrvl_sdio
- Cleanups to mac802154 & 6lowpan code
Please let me know if there are any issues pulling. Thanks.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
diff --git a/Documentation/devicetree/bindings/net/cavium-mdio.txt b/Documentation/devicetree/bindings/net/cavium-mdio.txt
index 04cb749..020df08 100644
--- a/Documentation/devicetree/bindings/net/cavium-mdio.txt
+++ b/Documentation/devicetree/bindings/net/cavium-mdio.txt
@@ -1,9 +1,12 @@
* System Management Interface (SMI) / MDIO
Properties:
-- compatible: "cavium,octeon-3860-mdio"
+- compatible: One of:
- Compatibility with all cn3XXX, cn5XXX and cn6XXX SOCs.
+ "cavium,octeon-3860-mdio": Compatibility with all cn3XXX, cn5XXX
+ and cn6XXX SOCs.
+
+ "cavium,thunder-8890-mdio": Compatibility with all cn8XXX SOCs.
- reg: The base address of the MDIO bus controller register bank.
@@ -25,3 +28,57 @@
reg = <0>;
};
};
+
+
+* System Management Interface (SMI) / MDIO Nexus
+
+ Several mdio buses may be gathered as children of a single PCI
+ device, this PCI device is the nexus of the buses.
+
+Properties:
+
+- compatible: "cavium,thunder-8890-mdio-nexus";
+
+- reg: The PCI device and function numbers of the nexus device.
+
+- #address-cells: Must be <2>.
+
+- #size-cells: Must be <2>.
+
+- ranges: As needed for mapping of the MDIO bus device registers.
+
+- assigned-addresses: As needed for mapping of the MDIO bus device registers.
+
+Example:
+
+ mdio-nexus@1,3 {
+ compatible = "cavium,thunder-8890-mdio-nexus";
+ #address-cells = <2>;
+ #size-cells = <2>;
+ reg = <0x0b00 0 0 0 0>; /* DEVFN = 0x0b (1:3) */
+ assigned-addresses = <0x03000000 0x87e0 0x05000000 0x0 0x800000>;
+ ranges = <0x87e0 0x05000000 0x03000000 0x87e0 0x05000000 0x0 0x800000>;
+
+ mdio0@87e0,05003800 {
+ compatible = "cavium,thunder-8890-mdio";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x87e0 0x05003800 0x0 0x30>;
+
+ ethernet-phy@0 {
+ ...
+ reg = <0>;
+ };
+ };
+ mdio0@87e0,05003880 {
+ compatible = "cavium,thunder-8890-mdio";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x87e0 0x05003880 0x0 0x30>;
+
+ ethernet-phy@0 {
+ ...
+ reg = <0>;
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt b/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt
index d0cb869..73be897 100644
--- a/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt
+++ b/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt
@@ -18,15 +18,30 @@
"core" for core clock and "bus" for the optional bus clock.
+Optional properties (valid only for Armada XP/38x):
+
+- buffer-manager: a phandle to a buffer manager node. Please refer to
+ Documentation/devicetree/bindings/net/marvell-neta-bm.txt
+- bm,pool-long: ID of a pool, that will accept all packets of a size
+ higher than 'short' pool's threshold (if set) and up to MTU value.
+ Obligatory, when the port is supposed to use hardware
+ buffer management.
+- bm,pool-short: ID of a pool, that will be used for accepting
+ packets of a size lower than given threshold. If not set, the port
+ will use a single 'long' pool for all packets, as defined above.
+
Example:
-ethernet@d0070000 {
+ethernet@70000 {
compatible = "marvell,armada-370-neta";
- reg = <0xd0070000 0x2500>;
+ reg = <0x70000 0x2500>;
interrupts = <8>;
clocks = <&gate_clk 4>;
tx-csum-limit = <9800>
status = "okay";
phy = <&phy0>;
phy-mode = "rgmii-id";
+ buffer-manager = <&bm>;
+ bm,pool-long = <0>;
+ bm,pool-short = <1>;
};
diff --git a/Documentation/devicetree/bindings/net/marvell-neta-bm.txt b/Documentation/devicetree/bindings/net/marvell-neta-bm.txt
new file mode 100644
index 0000000..c1b1d7c3
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/marvell-neta-bm.txt
@@ -0,0 +1,49 @@
+* Marvell Armada 380/XP Buffer Manager driver (BM)
+
+Required properties:
+
+- compatible: should be "marvell,armada-380-neta-bm".
+- reg: address and length of the register set for the device.
+- clocks: a pointer to the reference clock for this device.
+- internal-mem: a phandle to BM internal SRAM definition.
+
+Optional properties (port):
+
+- pool<0 : 3>,capacity: size of external buffer pointers' ring maintained
+ in DRAM. Can be set for each pool (id 0 : 3) separately. The value has
+ to be chosen between 128 and 16352 and it also has to be aligned to 32.
+ Otherwise the driver would adjust a given number or choose default if
+ not set.
+- pool<0 : 3>,pkt-size: maximum size of a packet accepted by a given buffer
+ pointers' pool (id 0 : 3). It will be taken into consideration only when pool
+ type is 'short'. For 'long' ones it would be overridden by port's MTU.
+ If not set a driver will choose a default value.
+
+In order to see how to hook the BM to a given ethernet port, please
+refer to Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt.
+
+Example:
+
+- main node:
+
+bm: bm@c8000 {
+ compatible = "marvell,armada-380-neta-bm";
+ reg = <0xc8000 0xac>;
+ clocks = <&gateclk 13>;
+ internal-mem = <&bm_bppi>;
+ status = "okay";
+ pool2,capacity = <4096>;
+ pool1,pkt-size = <512>;
+};
+
+- internal SRAM node:
+
+bm_bppi: bm-bppi {
+ compatible = "mmio-sram";
+ reg = <MBUS_ID(0x0c, 0x04) 0 0x100000>;
+ ranges = <0 MBUS_ID(0x0c, 0x04) 0 0x100000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ clocks = <&gateclk 13>;
+ status = "okay";
+};
diff --git a/Documentation/devicetree/bindings/net/mediatek-net.txt b/Documentation/devicetree/bindings/net/mediatek-net.txt
new file mode 100644
index 0000000..5ca7929
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/mediatek-net.txt
@@ -0,0 +1,77 @@
+MediaTek Frame Engine Ethernet controller
+=========================================
+
+The frame engine ethernet controller can be found on MediaTek SoCs. These SoCs
+have dual GMAC each represented by a child node..
+
+* Ethernet controller node
+
+Required properties:
+- compatible: Should be "mediatek,mt7623-eth"
+- reg: Address and length of the register set for the device
+- interrupts: Should contain the frame engines interrupt
+- clocks: the clock used by the core
+- clock-names: the names of the clock listed in the clocks property. These are
+ "ethif", "esw", "gp2", "gp1"
+- power-domains: phandle to the power domain that the ethernet is part of
+- resets: Should contain a phandle to the ethsys reset signal
+- reset-names: Should contain the reset signal name "eth"
+- mediatek,ethsys: phandle to the syscon node that handles the port setup
+- mediatek,pctl: phandle to the syscon node that handles the ports slew rate
+ and driver current
+
+Optional properties:
+- interrupt-parent: Should be the phandle for the interrupt controller
+ that services interrupts for this device
+
+
+* Ethernet MAC node
+
+Required properties:
+- compatible: Should be "mediatek,eth-mac"
+- reg: The number of the MAC
+- phy-handle: see ethernet.txt file in the same directory.
+
+Example:
+
+eth: ethernet@1b100000 {
+ compatible = "mediatek,mt7623-eth";
+ reg = <0 0x1b100000 0 0x20000>;
+ clocks = <&topckgen CLK_TOP_ETHIF_SEL>,
+ <ðsys CLK_ETHSYS_ESW>,
+ <ðsys CLK_ETHSYS_GP2>,
+ <ðsys CLK_ETHSYS_GP1>;
+ clock-names = "ethif", "esw", "gp2", "gp1";
+ interrupts = <GIC_SPI 200 IRQ_TYPE_LEVEL_LOW>;
+ power-domains = <&scpsys MT2701_POWER_DOMAIN_ETH>;
+ resets = <ðsys MT2701_ETHSYS_ETH_RST>;
+ reset-names = "eth";
+ mediatek,ethsys = <ðsys>;
+ mediatek,pctl = <&syscfg_pctl_a>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ gmac1: mac@0 {
+ compatible = "mediatek,eth-mac";
+ reg = <0>;
+ phy-handle = <&phy0>;
+ };
+
+ gmac2: mac@1 {
+ compatible = "mediatek,eth-mac";
+ reg = <1>;
+ phy-handle = <&phy1>;
+ };
+
+ mdio-bus {
+ phy0: ethernet-phy@0 {
+ reg = <0>;
+ phy-mode = "rgmii";
+ };
+
+ phy1: ethernet-phy@1 {
+ reg = <1>;
+ phy-mode = "rgmii";
+ };
+ };
+};
diff --git a/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.txt b/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.txt
index edefc26..96aae6b 100644
--- a/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.txt
+++ b/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.txt
@@ -1,17 +1,46 @@
* Qualcomm Atheros ath10k wireless devices
-For ath10k devices the calibration data can be provided through Device
-Tree. The node is a child node of the PCI controller.
-
Required properties:
--compatible : Should be "qcom,ath10k"
+- compatible: Should be one of the following:
+ * "qcom,ath10k"
+ * "qcom,ipq4019-wifi"
+
+PCI based devices uses compatible string "qcom,ath10k" and takes only
+calibration data via "qcom,ath10k-calibration-data". Rest of the properties
+are not applicable for PCI based devices.
+
+AHB based devices (i.e. ipq4019) uses compatible string "qcom,ipq4019-wifi"
+and also uses most of the properties defined in this doc.
Optional properties:
+- reg: Address and length of the register set for the device.
+- resets: Must contain an entry for each entry in reset-names.
+ See ../reset/reseti.txt for details.
+- reset-names: Must include the list of following reset names,
+ "wifi_cpu_init"
+ "wifi_radio_srif"
+ "wifi_radio_warm"
+ "wifi_radio_cold"
+ "wifi_core_warm"
+ "wifi_core_cold"
+- clocks: List of clock specifiers, must contain an entry for each required
+ entry in clock-names.
+- clock-names: Should contain the clock names "wifi_wcss_cmd", "wifi_wcss_ref",
+ "wifi_wcss_rtc".
+- interrupts: List of interrupt lines. Must contain an entry
+ for each entry in the interrupt-names property.
+- interrupt-names: Must include the entries for MSI interrupt
+ names ("msi0" to "msi15") and legacy interrupt
+ name ("legacy"),
+- qcom,msi_addr: MSI interrupt address.
+- qcom,msi_base: Base value to add before writing MSI data into
+ MSI address register.
- qcom,ath10k-calibration-data : calibration data as an array, the
length can vary between hw versions
+Example (to supply the calibration data alone):
-Example:
+In this example, the node is defined as child node of the PCI controller.
pci {
pcie@0 {
@@ -28,3 +57,53 @@
};
};
};
+
+Example (to supply ipq4019 SoC wifi block details):
+
+wifi0: wifi@a000000 {
+ compatible = "qcom,ipq4019-wifi";
+ reg = <0xa000000 0x200000>;
+ resets = <&gcc WIFI0_CPU_INIT_RESET>,
+ <&gcc WIFI0_RADIO_SRIF_RESET>,
+ <&gcc WIFI0_RADIO_WARM_RESET>,
+ <&gcc WIFI0_RADIO_COLD_RESET>,
+ <&gcc WIFI0_CORE_WARM_RESET>,
+ <&gcc WIFI0_CORE_COLD_RESET>;
+ reset-names = "wifi_cpu_init",
+ "wifi_radio_srif",
+ "wifi_radio_warm",
+ "wifi_radio_cold",
+ "wifi_core_warm",
+ "wifi_core_cold";
+ clocks = <&gcc GCC_WCSS2G_CLK>,
+ <&gcc GCC_WCSS2G_REF_CLK>,
+ <&gcc GCC_WCSS2G_RTC_CLK>;
+ clock-names = "wifi_wcss_cmd",
+ "wifi_wcss_ref",
+ "wifi_wcss_rtc";
+ interrupts = <0 0x20 0x1>,
+ <0 0x21 0x1>,
+ <0 0x22 0x1>,
+ <0 0x23 0x1>,
+ <0 0x24 0x1>,
+ <0 0x25 0x1>,
+ <0 0x26 0x1>,
+ <0 0x27 0x1>,
+ <0 0x28 0x1>,
+ <0 0x29 0x1>,
+ <0 0x2a 0x1>,
+ <0 0x2b 0x1>,
+ <0 0x2c 0x1>,
+ <0 0x2d 0x1>,
+ <0 0x2e 0x1>,
+ <0 0x2f 0x1>,
+ <0 0xa8 0x0>;
+ interrupt-names = "msi0", "msi1", "msi2", "msi3",
+ "msi4", "msi5", "msi6", "msi7",
+ "msi8", "msi9", "msi10", "msi11",
+ "msi12", "msi13", "msi14", "msi15",
+ "legacy";
+ qcom,msi_addr = <0x0b006040>;
+ qcom,msi_base = <0x40>;
+ qcom,ath10k-calibration-data = [ 01 02 03 ... ];
+};
diff --git a/Documentation/devicetree/bindings/sram/sram.txt b/Documentation/devicetree/bindings/sram/sram.txt
index 42ee943..227e3a3 100644
--- a/Documentation/devicetree/bindings/sram/sram.txt
+++ b/Documentation/devicetree/bindings/sram/sram.txt
@@ -25,6 +25,11 @@
- ranges : standard definition, should translate from local addresses
within the sram to bus addresses
+Optional properties in the sram node:
+
+- no-memory-wc : the flag indicating, that SRAM memory region has not to
+ be remapped as write combining. WC is used by default.
+
Required properties in the area nodes:
- reg : iomem address range, relative to the SRAM range
diff --git a/Documentation/networking/phy.txt b/Documentation/networking/phy.txt
index e839e7e..7ab9404 100644
--- a/Documentation/networking/phy.txt
+++ b/Documentation/networking/phy.txt
@@ -267,13 +267,23 @@
config_intr: Enable or disable interrupts
remove: Does any driver take-down
ts_info: Queries about the HW timestamping status
+ match_phy_device: used for Clause 45 capable PHYs to match devices
+ in package and ensure they are compatible
hwtstamp: Set the PHY HW timestamping configuration
rxtstamp: Requests a receive timestamp at the PHY level for a 'skb'
txtsamp: Requests a transmit timestamp at the PHY level for a 'skb'
set_wol: Enable Wake-on-LAN at the PHY level
get_wol: Get the Wake-on-LAN status at the PHY level
+ link_change_notify: called to inform the core is about to change the
+ link state, can be used to work around bogus PHY between state changes
read_mmd_indirect: Read PHY MMD indirect register
write_mmd_indirect: Write PHY MMD indirect register
+ module_info: Get the size and type of an EEPROM contained in an plug-in
+ module
+ module_eeprom: Get EEPROM information of a plug-in module
+ get_sset_count: Get number of strings sets that get_strings will count
+ get_strings: Get strings from requested objects (statistics)
+ get_stats: Get the extended statistics from the PHY device
Of these, only config_aneg and read_status are required to be
assigned by the driver code. The rest are optional. Also, it is
diff --git a/MAINTAINERS b/MAINTAINERS
index 2132c99..b70294e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7035,6 +7035,13 @@
F: include/uapi/linux/ivtv*
F: include/uapi/linux/uvcvideo.h
+MEDIATEK ETHERNET DRIVER
+M: Felix Fietkau <nbd@openwrt.org>
+M: John Crispin <blogic@openwrt.org>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/ethernet/mediatek/
+
MEDIATEK MT7601U WIRELESS LAN DRIVER
M: Jakub Kicinski <kubakici@wp.pl>
L: linux-wireless@vger.kernel.org
@@ -7659,7 +7666,6 @@
F: include/net/nfc/
F: include/uapi/linux/nfc.h
F: drivers/nfc/
-F: include/linux/platform_data/microread.h
F: include/linux/platform_data/nfcmrvl.h
F: include/linux/platform_data/nxp-nci.h
F: include/linux/platform_data/pn544.h
@@ -9502,6 +9508,7 @@
SAMSUNG S3FWRN5 NFC DRIVER
M: Robert Baldyga <r.baldyga@samsung.com>
+M: Krzysztof Opasiak <k.opasiak@samsung.com>
L: linux-nfc@lists.01.org (moderated for non-subscribers)
S: Supported
F: drivers/nfc/s3fwrn5
diff --git a/arch/alpha/include/asm/checksum.h b/arch/alpha/include/asm/checksum.h
index d3854bb..f2bbdd2 100644
--- a/arch/alpha/include/asm/checksum.h
+++ b/arch/alpha/include/asm/checksum.h
@@ -13,14 +13,11 @@
* computes the checksum of the TCP/UDP pseudo-header
* returns a 16-bit checksum, already complemented
*/
-extern __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
- unsigned short len,
- unsigned short proto,
- __wsum sum);
+__sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
+ __u32 len, __u8 proto, __wsum sum);
__wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
- unsigned short len, unsigned short proto,
- __wsum sum);
+ __u32 len, __u8 proto, __wsum sum);
/*
* computes the checksum of a memory block at buff, length len,
@@ -70,6 +67,5 @@
#define _HAVE_ARCH_IPV6_CSUM
extern __sum16 csum_ipv6_magic(const struct in6_addr *saddr,
const struct in6_addr *daddr,
- __u32 len, unsigned short proto,
- __wsum sum);
+ __u32 len, __u8 proto, __wsum sum);
#endif
diff --git a/arch/alpha/lib/checksum.c b/arch/alpha/lib/checksum.c
index 199f6ef..377f9e3 100644
--- a/arch/alpha/lib/checksum.c
+++ b/arch/alpha/lib/checksum.c
@@ -42,9 +42,7 @@
* returns a 16-bit checksum, already complemented.
*/
__sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
- unsigned short len,
- unsigned short proto,
- __wsum sum)
+ __u32 len, __u8 proto, __wsum sum)
{
return (__force __sum16)~from64to16(
(__force u64)saddr + (__force u64)daddr +
@@ -52,9 +50,7 @@
}
__wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
- unsigned short len,
- unsigned short proto,
- __wsum sum)
+ __u32 len, __u8 proto, __wsum sum)
{
unsigned long result;
diff --git a/arch/arc/include/asm/checksum.h b/arch/arc/include/asm/checksum.h
index 1095729..913eb4a 100644
--- a/arch/arc/include/asm/checksum.h
+++ b/arch/arc/include/asm/checksum.h
@@ -70,8 +70,8 @@
* SA [4], DA [4], zeroes [1], Proto[1], TCP Seg(hdr+data) Len [2]
*/
static inline __wsum
-csum_tcpudp_nofold(__be32 saddr, __be32 daddr, unsigned short len,
- unsigned short proto, __wsum sum)
+csum_tcpudp_nofold(__be32 saddr, __be32 daddr, __u32 len,
+ __u8 proto, __wsum sum)
{
__asm__ __volatile__(
" add.f %0, %0, %1 \n"
diff --git a/arch/arm/boot/dts/armada-385-db-ap.dts b/arch/arm/boot/dts/armada-385-db-ap.dts
index acd5b15..5f9451b 100644
--- a/arch/arm/boot/dts/armada-385-db-ap.dts
+++ b/arch/arm/boot/dts/armada-385-db-ap.dts
@@ -61,7 +61,8 @@
ranges = <MBUS_ID(0xf0, 0x01) 0 0xf1000000 0x100000
MBUS_ID(0x01, 0x1d) 0 0xfff00000 0x100000
MBUS_ID(0x09, 0x19) 0 0xf1100000 0x10000
- MBUS_ID(0x09, 0x15) 0 0xf1110000 0x10000>;
+ MBUS_ID(0x09, 0x15) 0 0xf1110000 0x10000
+ MBUS_ID(0x0c, 0x04) 0 0xf1200000 0x100000>;
internal-regs {
spi1: spi@10680 {
@@ -138,12 +139,18 @@
status = "okay";
phy = <&phy2>;
phy-mode = "sgmii";
+ buffer-manager = <&bm>;
+ bm,pool-long = <1>;
+ bm,pool-short = <3>;
};
ethernet@34000 {
status = "okay";
phy = <&phy1>;
phy-mode = "sgmii";
+ buffer-manager = <&bm>;
+ bm,pool-long = <2>;
+ bm,pool-short = <3>;
};
ethernet@70000 {
@@ -157,6 +164,13 @@
status = "okay";
phy = <&phy0>;
phy-mode = "rgmii-id";
+ buffer-manager = <&bm>;
+ bm,pool-long = <0>;
+ bm,pool-short = <3>;
+ };
+
+ bm@c8000 {
+ status = "okay";
};
nfc: flash@d0000 {
@@ -178,6 +192,10 @@
};
};
+ bm-bppi {
+ status = "okay";
+ };
+
pcie-controller {
status = "okay";
diff --git a/arch/arm/boot/dts/armada-388-clearfog.dts b/arch/arm/boot/dts/armada-388-clearfog.dts
index c6e180e..c60206e 100644
--- a/arch/arm/boot/dts/armada-388-clearfog.dts
+++ b/arch/arm/boot/dts/armada-388-clearfog.dts
@@ -78,6 +78,9 @@
internal-regs {
ethernet@30000 {
phy-mode = "sgmii";
+ buffer-manager = <&bm>;
+ bm,pool-long = <2>;
+ bm,pool-short = <1>;
status = "okay";
fixed-link {
@@ -88,6 +91,9 @@
ethernet@34000 {
phy-mode = "sgmii";
+ buffer-manager = <&bm>;
+ bm,pool-long = <3>;
+ bm,pool-short = <1>;
status = "okay";
fixed-link {
diff --git a/arch/arm/boot/dts/armada-388-db.dts b/arch/arm/boot/dts/armada-388-db.dts
index ff47af5..ea93ed7 100644
--- a/arch/arm/boot/dts/armada-388-db.dts
+++ b/arch/arm/boot/dts/armada-388-db.dts
@@ -66,7 +66,8 @@
ranges = <MBUS_ID(0xf0, 0x01) 0 0xf1000000 0x100000
MBUS_ID(0x01, 0x1d) 0 0xfff00000 0x100000
MBUS_ID(0x09, 0x19) 0 0xf1100000 0x10000
- MBUS_ID(0x09, 0x15) 0 0xf1110000 0x10000>;
+ MBUS_ID(0x09, 0x15) 0 0xf1110000 0x10000
+ MBUS_ID(0x0c, 0x04) 0 0xf1200000 0x100000>;
internal-regs {
spi@10600 {
@@ -99,6 +100,9 @@
status = "okay";
phy = <&phy1>;
phy-mode = "rgmii-id";
+ buffer-manager = <&bm>;
+ bm,pool-long = <2>;
+ bm,pool-short = <3>;
};
usb@58000 {
@@ -109,6 +113,9 @@
status = "okay";
phy = <&phy0>;
phy-mode = "rgmii-id";
+ buffer-manager = <&bm>;
+ bm,pool-long = <0>;
+ bm,pool-short = <1>;
};
mdio@72004 {
@@ -129,6 +136,10 @@
status = "okay";
};
+ bm@c8000 {
+ status = "okay";
+ };
+
flash@d0000 {
status = "okay";
num-cs = <1>;
@@ -169,6 +180,10 @@
};
};
+ bm-bppi {
+ status = "okay";
+ };
+
pcie-controller {
status = "okay";
/*
diff --git a/arch/arm/boot/dts/armada-388-gp.dts b/arch/arm/boot/dts/armada-388-gp.dts
index cd31602..466b01e 100644
--- a/arch/arm/boot/dts/armada-388-gp.dts
+++ b/arch/arm/boot/dts/armada-388-gp.dts
@@ -60,7 +60,8 @@
ranges = <MBUS_ID(0xf0, 0x01) 0 0xf1000000 0x100000
MBUS_ID(0x01, 0x1d) 0 0xfff00000 0x100000
MBUS_ID(0x09, 0x19) 0 0xf1100000 0x10000
- MBUS_ID(0x09, 0x15) 0 0xf1110000 0x10000>;
+ MBUS_ID(0x09, 0x15) 0 0xf1110000 0x10000
+ MBUS_ID(0x0c, 0x04) 0 0xf1200000 0x100000>;
internal-regs {
spi@10600 {
@@ -133,6 +134,9 @@
status = "okay";
phy = <&phy1>;
phy-mode = "rgmii-id";
+ buffer-manager = <&bm>;
+ bm,pool-long = <2>;
+ bm,pool-short = <3>;
};
/* CON4 */
@@ -152,6 +156,9 @@
status = "okay";
phy = <&phy0>;
phy-mode = "rgmii-id";
+ buffer-manager = <&bm>;
+ bm,pool-long = <0>;
+ bm,pool-short = <1>;
};
@@ -186,6 +193,10 @@
};
};
+ bm@c8000 {
+ status = "okay";
+ };
+
sata@e0000 {
pinctrl-names = "default";
pinctrl-0 = <&sata2_pins>, <&sata3_pins>;
@@ -240,6 +251,10 @@
};
};
+ bm-bppi {
+ status = "okay";
+ };
+
pcie-controller {
status = "okay";
/*
diff --git a/arch/arm/boot/dts/armada-38x-solidrun-microsom.dtsi b/arch/arm/boot/dts/armada-38x-solidrun-microsom.dtsi
index 3f792a5..8c98422 100644
--- a/arch/arm/boot/dts/armada-38x-solidrun-microsom.dtsi
+++ b/arch/arm/boot/dts/armada-38x-solidrun-microsom.dtsi
@@ -58,7 +58,8 @@
ranges = <MBUS_ID(0xf0, 0x01) 0 0xf1000000 0x100000
MBUS_ID(0x01, 0x1d) 0 0xfff00000 0x100000
MBUS_ID(0x09, 0x19) 0 0xf1100000 0x10000
- MBUS_ID(0x09, 0x15) 0 0xf1110000 0x10000>;
+ MBUS_ID(0x09, 0x15) 0 0xf1110000 0x10000
+ MBUS_ID(0x0c, 0x04) 0 0xf1200000 0x100000>;
internal-regs {
ethernet@70000 {
@@ -66,6 +67,9 @@
pinctrl-names = "default";
phy = <&phy_dedicated>;
phy-mode = "rgmii-id";
+ buffer-manager = <&bm>;
+ bm,pool-long = <0>;
+ bm,pool-short = <1>;
status = "okay";
};
@@ -110,6 +114,15 @@
pinctrl-names = "default";
status = "okay";
};
+
+ bm@c8000 {
+ status = "okay";
+ };
};
+
+ bm-bppi {
+ status = "okay";
+ };
+
};
};
diff --git a/arch/arm/boot/dts/armada-38x.dtsi b/arch/arm/boot/dts/armada-38x.dtsi
index e8b7f67..066a8f0 100644
--- a/arch/arm/boot/dts/armada-38x.dtsi
+++ b/arch/arm/boot/dts/armada-38x.dtsi
@@ -540,6 +540,14 @@
status = "disabled";
};
+ bm: bm@c8000 {
+ compatible = "marvell,armada-380-neta-bm";
+ reg = <0xc8000 0xac>;
+ clocks = <&gateclk 13>;
+ internal-mem = <&bm_bppi>;
+ status = "disabled";
+ };
+
sata@e0000 {
compatible = "marvell,armada-380-ahci";
reg = <0xe0000 0x2000>;
@@ -618,6 +626,17 @@
#size-cells = <1>;
ranges = <0 MBUS_ID(0x09, 0x15) 0 0x800>;
};
+
+ bm_bppi: bm-bppi {
+ compatible = "mmio-sram";
+ reg = <MBUS_ID(0x0c, 0x04) 0 0x100000>;
+ ranges = <0 MBUS_ID(0x0c, 0x04) 0 0x100000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ clocks = <&gateclk 13>;
+ no-memory-wc;
+ status = "disabled";
+ };
};
clocks {
diff --git a/arch/arm/boot/dts/armada-xp-db.dts b/arch/arm/boot/dts/armada-xp-db.dts
index f774101..3065730 100644
--- a/arch/arm/boot/dts/armada-xp-db.dts
+++ b/arch/arm/boot/dts/armada-xp-db.dts
@@ -77,7 +77,8 @@
MBUS_ID(0x01, 0x1d) 0 0 0xfff00000 0x100000
MBUS_ID(0x01, 0x2f) 0 0 0xf0000000 0x1000000
MBUS_ID(0x09, 0x09) 0 0 0xf8100000 0x10000
- MBUS_ID(0x09, 0x05) 0 0 0xf8110000 0x10000>;
+ MBUS_ID(0x09, 0x05) 0 0 0xf8110000 0x10000
+ MBUS_ID(0x0c, 0x04) 0 0 0xf1200000 0x100000>;
devbus-bootcs {
status = "okay";
@@ -181,21 +182,33 @@
status = "okay";
phy = <&phy0>;
phy-mode = "rgmii-id";
+ buffer-manager = <&bm>;
+ bm,pool-long = <0>;
};
ethernet@74000 {
status = "okay";
phy = <&phy1>;
phy-mode = "rgmii-id";
+ buffer-manager = <&bm>;
+ bm,pool-long = <1>;
};
ethernet@30000 {
status = "okay";
phy = <&phy2>;
phy-mode = "sgmii";
+ buffer-manager = <&bm>;
+ bm,pool-long = <2>;
};
ethernet@34000 {
status = "okay";
phy = <&phy3>;
phy-mode = "sgmii";
+ buffer-manager = <&bm>;
+ bm,pool-long = <3>;
+ };
+
+ bm@c0000 {
+ status = "okay";
};
mvsdio@d4000 {
@@ -230,5 +243,9 @@
};
};
};
+
+ bm-bppi {
+ status = "okay";
+ };
};
};
diff --git a/arch/arm/boot/dts/armada-xp-gp.dts b/arch/arm/boot/dts/armada-xp-gp.dts
index 4878d73..a1ded01 100644
--- a/arch/arm/boot/dts/armada-xp-gp.dts
+++ b/arch/arm/boot/dts/armada-xp-gp.dts
@@ -96,7 +96,8 @@
MBUS_ID(0x01, 0x1d) 0 0 0xfff00000 0x100000
MBUS_ID(0x01, 0x2f) 0 0 0xf0000000 0x1000000
MBUS_ID(0x09, 0x09) 0 0 0xf8100000 0x10000
- MBUS_ID(0x09, 0x05) 0 0 0xf8110000 0x10000>;
+ MBUS_ID(0x09, 0x05) 0 0 0xf8110000 0x10000
+ MBUS_ID(0x0c, 0x04) 0 0 0xf1200000 0x100000>;
devbus-bootcs {
status = "okay";
@@ -196,21 +197,29 @@
status = "okay";
phy = <&phy0>;
phy-mode = "qsgmii";
+ buffer-manager = <&bm>;
+ bm,pool-long = <0>;
};
ethernet@74000 {
status = "okay";
phy = <&phy1>;
phy-mode = "qsgmii";
+ buffer-manager = <&bm>;
+ bm,pool-long = <1>;
};
ethernet@30000 {
status = "okay";
phy = <&phy2>;
phy-mode = "qsgmii";
+ buffer-manager = <&bm>;
+ bm,pool-long = <2>;
};
ethernet@34000 {
status = "okay";
phy = <&phy3>;
phy-mode = "qsgmii";
+ buffer-manager = <&bm>;
+ bm,pool-long = <3>;
};
/* Front-side USB slot */
@@ -235,6 +244,10 @@
};
};
+ bm@c0000 {
+ status = "okay";
+ };
+
nand@d0000 {
status = "okay";
num-cs = <1>;
@@ -243,5 +256,9 @@
nand-on-flash-bbt;
};
};
+
+ bm-bppi {
+ status = "okay";
+ };
};
};
diff --git a/arch/arm/boot/dts/armada-xp-openblocks-ax3-4.dts b/arch/arm/boot/dts/armada-xp-openblocks-ax3-4.dts
index a5db177..3aa29a9 100644
--- a/arch/arm/boot/dts/armada-xp-openblocks-ax3-4.dts
+++ b/arch/arm/boot/dts/armada-xp-openblocks-ax3-4.dts
@@ -67,7 +67,8 @@
MBUS_ID(0x01, 0x1d) 0 0 0xfff00000 0x100000
MBUS_ID(0x01, 0x2f) 0 0 0xf0000000 0x8000000
MBUS_ID(0x09, 0x09) 0 0 0xf8100000 0x10000
- MBUS_ID(0x09, 0x05) 0 0 0xf8110000 0x10000>;
+ MBUS_ID(0x09, 0x05) 0 0 0xf8110000 0x10000
+ MBUS_ID(0x0c, 0x04) 0 0 0xd1200000 0x100000>;
devbus-bootcs {
status = "okay";
@@ -176,21 +177,29 @@
status = "okay";
phy = <&phy0>;
phy-mode = "sgmii";
+ buffer-manager = <&bm>;
+ bm,pool-long = <0>;
};
ethernet@74000 {
status = "okay";
phy = <&phy1>;
phy-mode = "sgmii";
+ buffer-manager = <&bm>;
+ bm,pool-long = <1>;
};
ethernet@30000 {
status = "okay";
phy = <&phy2>;
phy-mode = "sgmii";
+ buffer-manager = <&bm>;
+ bm,pool-long = <2>;
};
ethernet@34000 {
status = "okay";
phy = <&phy3>;
phy-mode = "sgmii";
+ buffer-manager = <&bm>;
+ bm,pool-long = <3>;
};
i2c@11000 {
status = "okay";
@@ -219,6 +228,14 @@
usb@51000 {
status = "okay";
};
+
+ bm@c0000 {
+ status = "okay";
+ };
+ };
+
+ bm-bppi {
+ status = "okay";
};
};
};
diff --git a/arch/arm/boot/dts/armada-xp.dtsi b/arch/arm/boot/dts/armada-xp.dtsi
index be23196..553349c 100644
--- a/arch/arm/boot/dts/armada-xp.dtsi
+++ b/arch/arm/boot/dts/armada-xp.dtsi
@@ -253,6 +253,14 @@
marvell,crypto-sram-size = <0x800>;
};
+ bm: bm@c0000 {
+ compatible = "marvell,armada-380-neta-bm";
+ reg = <0xc0000 0xac>;
+ clocks = <&gateclk 13>;
+ internal-mem = <&bm_bppi>;
+ status = "disabled";
+ };
+
xor@f0900 {
compatible = "marvell,orion-xor";
reg = <0xF0900 0x100
@@ -291,6 +299,17 @@
#size-cells = <1>;
ranges = <0 MBUS_ID(0x09, 0x05) 0 0x800>;
};
+
+ bm_bppi: bm-bppi {
+ compatible = "mmio-sram";
+ reg = <MBUS_ID(0x0c, 0x04) 0 0x100000>;
+ ranges = <0 MBUS_ID(0x0c, 0x04) 0 0x100000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ clocks = <&gateclk 13>;
+ no-memory-wc;
+ status = "disabled";
+ };
};
clocks {
diff --git a/arch/arm/configs/multi_v5_defconfig b/arch/arm/configs/multi_v5_defconfig
index 1f9ca47..afb1f62 100644
--- a/arch/arm/configs/multi_v5_defconfig
+++ b/arch/arm/configs/multi_v5_defconfig
@@ -91,7 +91,7 @@
CONFIG_NETDEVICES=y
CONFIG_NET_DSA_MV88E6060=y
CONFIG_NET_DSA_MV88E6131=y
-CONFIG_NET_DSA_MV88E6123_61_65=y
+CONFIG_NET_DSA_MV88E6123=y
CONFIG_NET_DSA_MV88E6171=y
CONFIG_NET_DSA_MV88E6352=y
CONFIG_MV643XX_ETH=y
diff --git a/arch/arm/configs/mvebu_v5_defconfig b/arch/arm/configs/mvebu_v5_defconfig
index af29780..6c4c540 100644
--- a/arch/arm/configs/mvebu_v5_defconfig
+++ b/arch/arm/configs/mvebu_v5_defconfig
@@ -92,7 +92,7 @@
CONFIG_NETDEVICES=y
CONFIG_NET_DSA_MV88E6060=y
CONFIG_NET_DSA_MV88E6131=y
-CONFIG_NET_DSA_MV88E6123_61_65=y
+CONFIG_NET_DSA_MV88E6123=y
CONFIG_NET_DSA_MV88E6171=y
CONFIG_NET_DSA_MV88E6352=y
CONFIG_MV643XX_ETH=y
diff --git a/arch/arm/configs/orion5x_defconfig b/arch/arm/configs/orion5x_defconfig
index 5876ce7..6a5bc27 100644
--- a/arch/arm/configs/orion5x_defconfig
+++ b/arch/arm/configs/orion5x_defconfig
@@ -86,7 +86,7 @@
CONFIG_NETDEVICES=y
CONFIG_MII=y
CONFIG_NET_DSA_MV88E6131=y
-CONFIG_NET_DSA_MV88E6123_61_65=y
+CONFIG_NET_DSA_MV88E6123=y
CONFIG_MV643XX_ETH=y
CONFIG_MARVELL_PHY=y
# CONFIG_INPUT_MOUSEDEV is not set
diff --git a/arch/arm/include/asm/checksum.h b/arch/arm/include/asm/checksum.h
index 5233151..524692f 100644
--- a/arch/arm/include/asm/checksum.h
+++ b/arch/arm/include/asm/checksum.h
@@ -84,10 +84,10 @@
}
static inline __wsum
-csum_tcpudp_nofold(__be32 saddr, __be32 daddr, unsigned short len,
- unsigned short proto, __wsum sum)
+csum_tcpudp_nofold(__be32 saddr, __be32 daddr, __u32 len,
+ __u8 proto, __wsum sum)
{
- u32 lenprot = len | proto << 16;
+ u32 lenprot = len + proto;
if (__builtin_constant_p(sum) && sum == 0) {
__asm__(
"adds %0, %1, %2 @ csum_tcpudp_nofold0 \n\t"
@@ -121,8 +121,8 @@
* returns a 16-bit checksum, already complemented
*/
static inline __sum16
-csum_tcpudp_magic(__be32 saddr, __be32 daddr, unsigned short len,
- unsigned short proto, __wsum sum)
+csum_tcpudp_magic(__be32 saddr, __be32 daddr, __u32 len,
+ __u8 proto, __wsum sum)
{
return csum_fold(csum_tcpudp_nofold(saddr, daddr, len, proto, sum));
}
@@ -144,8 +144,8 @@
__be32 proto, __wsum sum);
static inline __sum16
-csum_ipv6_magic(const struct in6_addr *saddr, const struct in6_addr *daddr, __u32 len,
- unsigned short proto, __wsum sum)
+csum_ipv6_magic(const struct in6_addr *saddr, const struct in6_addr *daddr,
+ __u32 len, __u8 proto, __wsum sum)
{
return csum_fold(__csum_ipv6_magic(saddr, daddr, htonl(len),
htonl(proto), sum));
diff --git a/arch/avr32/include/asm/checksum.h b/arch/avr32/include/asm/checksum.h
index 4ddbfd2..4ab7d5b 100644
--- a/arch/avr32/include/asm/checksum.h
+++ b/arch/avr32/include/asm/checksum.h
@@ -111,9 +111,8 @@
}
static inline __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
- unsigned short len,
- unsigned short proto,
- __wsum sum)
+ __u32 len, __u8 proto,
+ __wsum sum)
{
asm(" add %0, %1\n"
" adc %0, %0, %2\n"
@@ -132,9 +131,8 @@
* returns a 16-bit checksum, already complemented
*/
static inline __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
- unsigned short len,
- unsigned short proto,
- __wsum sum)
+ __u32 len, __u8 proto,
+ __wsum sum)
{
return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum));
}
diff --git a/arch/blackfin/include/asm/checksum.h b/arch/blackfin/include/asm/checksum.h
index 623cc7f..e7134bf 100644
--- a/arch/blackfin/include/asm/checksum.h
+++ b/arch/blackfin/include/asm/checksum.h
@@ -14,8 +14,8 @@
*/
static inline __wsum
-__csum_tcpudp_nofold(__be32 saddr, __be32 daddr, unsigned short len,
- unsigned short proto, __wsum sum)
+__csum_tcpudp_nofold(__be32 saddr, __be32 daddr, __u32 len,
+ __u8 proto, __wsum sum)
{
unsigned int carry;
diff --git a/arch/c6x/include/asm/checksum.h b/arch/c6x/include/asm/checksum.h
index 7246816..249b0e4 100644
--- a/arch/c6x/include/asm/checksum.h
+++ b/arch/c6x/include/asm/checksum.h
@@ -10,8 +10,8 @@
#define _ASM_C6X_CHECKSUM_H
static inline __wsum
-csum_tcpudp_nofold(__be32 saddr, __be32 daddr, unsigned short len,
- unsigned short proto, __wsum sum)
+csum_tcpudp_nofold(__be32 saddr, __be32 daddr, __u32 len,
+ __u8 proto, __wsum sum)
{
unsigned long long tmp;
diff --git a/arch/cris/include/arch-v10/arch/checksum.h b/arch/cris/include/arch-v10/arch/checksum.h
index b8000c5..d1d1bd9 100644
--- a/arch/cris/include/arch-v10/arch/checksum.h
+++ b/arch/cris/include/arch-v10/arch/checksum.h
@@ -9,8 +9,8 @@
*/
static inline __wsum
-csum_tcpudp_nofold(__be32 saddr, __be32 daddr, unsigned short len,
- unsigned short proto, __wsum sum)
+csum_tcpudp_nofold(__be32 saddr, __be32 daddr, __u32 len,
+ __u8 proto, __wsum sum)
{
__wsum res;
__asm__ ("add.d %2, %0\n\t"
diff --git a/arch/cris/include/arch-v32/arch/checksum.h b/arch/cris/include/arch-v32/arch/checksum.h
index e5dcfce..65cf205 100644
--- a/arch/cris/include/arch-v32/arch/checksum.h
+++ b/arch/cris/include/arch-v32/arch/checksum.h
@@ -11,7 +11,7 @@
*/
static inline __wsum
csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
- unsigned short len, unsigned short proto, __wsum sum)
+ __u32 len, __u8 proto, __wsum sum)
{
__wsum res;
diff --git a/arch/cris/include/asm/checksum.h b/arch/cris/include/asm/checksum.h
index 75dcb77d..ea949c6 100644
--- a/arch/cris/include/asm/checksum.h
+++ b/arch/cris/include/asm/checksum.h
@@ -63,9 +63,8 @@
*/
static inline __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
- unsigned short len,
- unsigned short proto,
- __wsum sum)
+ __u32 len, __u8 proto,
+ __wsum sum)
{
return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum));
}
diff --git a/arch/frv/include/asm/checksum.h b/arch/frv/include/asm/checksum.h
index 269da09..b77388c 100644
--- a/arch/frv/include/asm/checksum.h
+++ b/arch/frv/include/asm/checksum.h
@@ -105,8 +105,8 @@
* returns a 16-bit checksum, already complemented
*/
static inline __wsum
-csum_tcpudp_nofold(__be32 saddr, __be32 daddr, unsigned short len,
- unsigned short proto, __wsum sum)
+csum_tcpudp_nofold(__be32 saddr, __be32 daddr, __u32 len,
+ __u8 proto, __wsum sum)
{
asm(" addcc %1,%0,%0,icc0 \n"
" addxcc %2,%0,%0,icc0 \n"
@@ -120,8 +120,8 @@
}
static inline __sum16
-csum_tcpudp_magic(__be32 saddr, __be32 daddr, unsigned short len,
- unsigned short proto, __wsum sum)
+csum_tcpudp_magic(__be32 saddr, __be32 daddr, __u32 len,
+ __u8 proto, __wsum sum)
{
return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum));
}
@@ -135,7 +135,7 @@
#define _HAVE_ARCH_IPV6_CSUM
static inline __sum16
csum_ipv6_magic(const struct in6_addr *saddr, const struct in6_addr *daddr,
- __u32 len, unsigned short proto, __wsum sum)
+ __u32 len, __u8 proto, __wsum sum)
{
unsigned long tmp, tmp2;
diff --git a/arch/hexagon/include/asm/checksum.h b/arch/hexagon/include/asm/checksum.h
index 46ec8a7..d9f58d6 100644
--- a/arch/hexagon/include/asm/checksum.h
+++ b/arch/hexagon/include/asm/checksum.h
@@ -38,12 +38,12 @@
* returns a 16-bit checksum, already complemented
*/
#define csum_tcpudp_nofold csum_tcpudp_nofold
-__wsum csum_tcpudp_nofold(unsigned long saddr, unsigned long daddr,
- unsigned short len, unsigned short proto, __wsum sum);
+__wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
+ __u32 len, __u8 proto, __wsum sum);
#define csum_tcpudp_magic csum_tcpudp_magic
-__sum16 csum_tcpudp_magic(unsigned long saddr, unsigned long daddr,
- unsigned short len, unsigned short proto, __wsum sum);
+__sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
+ __u32 len, __u8 proto, __wsum sum);
#include <asm-generic/checksum.h>
diff --git a/arch/hexagon/lib/checksum.c b/arch/hexagon/lib/checksum.c
index 8169f78..617506d 100644
--- a/arch/hexagon/lib/checksum.c
+++ b/arch/hexagon/lib/checksum.c
@@ -60,18 +60,16 @@
* computes the checksum of the TCP/UDP pseudo-header
* returns a 16-bit checksum, already complemented.
*/
-__sum16 csum_tcpudp_magic(unsigned long saddr, unsigned long daddr,
- unsigned short len, unsigned short proto,
- __wsum sum)
+__sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
+ __u32 len, __u8 proto, __wsum sum)
{
return (__force __sum16)~from64to16(
(__force u64)saddr + (__force u64)daddr +
(__force u64)sum + ((len + proto) << 8));
}
-__wsum csum_tcpudp_nofold(unsigned long saddr, unsigned long daddr,
- unsigned short len, unsigned short proto,
- __wsum sum)
+__wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
+ __u32 len, __u8 proto, __wsum sum)
{
u64 result;
diff --git a/arch/ia64/include/asm/checksum.h b/arch/ia64/include/asm/checksum.h
index 97af155..7accf54 100644
--- a/arch/ia64/include/asm/checksum.h
+++ b/arch/ia64/include/asm/checksum.h
@@ -16,15 +16,11 @@
* Computes the checksum of the TCP/UDP pseudo-header returns a 16-bit
* checksum, already complemented
*/
-extern __sum16 csum_tcpudp_magic (__be32 saddr, __be32 daddr,
- unsigned short len,
- unsigned short proto,
- __wsum sum);
+extern __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
+ __u32 len, __u8 proto, __wsum sum);
-extern __wsum csum_tcpudp_nofold (__be32 saddr, __be32 daddr,
- unsigned short len,
- unsigned short proto,
- __wsum sum);
+extern __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
+ __u32 len, __u8 proto, __wsum sum);
/*
* Computes the checksum of a memory block at buff, length len,
@@ -73,7 +69,7 @@
#define _HAVE_ARCH_IPV6_CSUM 1
struct in6_addr;
extern __sum16 csum_ipv6_magic(const struct in6_addr *saddr,
- const struct in6_addr *daddr, __u32 len, unsigned short proto,
- __wsum csum);
+ const struct in6_addr *daddr,
+ __u32 len, __u8 proto, __wsum csum);
#endif /* _ASM_IA64_CHECKSUM_H */
diff --git a/arch/ia64/lib/checksum.c b/arch/ia64/lib/checksum.c
index 9fc9550..2cb23cb 100644
--- a/arch/ia64/lib/checksum.c
+++ b/arch/ia64/lib/checksum.c
@@ -34,8 +34,8 @@
* returns a 16-bit checksum, already complemented.
*/
__sum16
-csum_tcpudp_magic (__be32 saddr, __be32 daddr, unsigned short len,
- unsigned short proto, __wsum sum)
+csum_tcpudp_magic(__be32 saddr, __be32 daddr, __u32 len,
+ __u8 proto, __wsum sum)
{
return (__force __sum16)~from64to16(
(__force u64)saddr + (__force u64)daddr +
@@ -45,8 +45,8 @@
EXPORT_SYMBOL(csum_tcpudp_magic);
__wsum
-csum_tcpudp_nofold (__be32 saddr, __be32 daddr, unsigned short len,
- unsigned short proto, __wsum sum)
+csum_tcpudp_nofold(__be32 saddr, __be32 daddr, __u32 len,
+ __u8 proto, __wsum sum)
{
unsigned long result;
diff --git a/arch/m32r/include/asm/checksum.h b/arch/m32r/include/asm/checksum.h
index a7a7c4f..d68e93c 100644
--- a/arch/m32r/include/asm/checksum.h
+++ b/arch/m32r/include/asm/checksum.h
@@ -114,9 +114,8 @@
}
static inline __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
- unsigned short len,
- unsigned short proto,
- __wsum sum)
+ __u32 len, __u8 proto,
+ __wsum sum)
{
#if defined(__LITTLE_ENDIAN)
unsigned long len_proto = (proto + len) << 8;
@@ -145,9 +144,8 @@
* returns a 16-bit checksum, already complemented
*/
static inline __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
- unsigned short len,
- unsigned short proto,
- __wsum sum)
+ __u32 len, __u8 proto,
+ __wsum sum)
{
return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum));
}
diff --git a/arch/m68k/include/asm/checksum.h b/arch/m68k/include/asm/checksum.h
index 2f88d86..75e91f0 100644
--- a/arch/m68k/include/asm/checksum.h
+++ b/arch/m68k/include/asm/checksum.h
@@ -117,7 +117,7 @@
#define _HAVE_ARCH_IPV6_CSUM
static __inline__ __sum16
csum_ipv6_magic(const struct in6_addr *saddr, const struct in6_addr *daddr,
- __u32 len, unsigned short proto, __wsum sum)
+ __u32 len, __u8 proto, __wsum sum)
{
register unsigned long tmp;
__asm__("addl %2@,%0\n\t"
diff --git a/arch/metag/include/asm/checksum.h b/arch/metag/include/asm/checksum.h
index 08dd1cc..f65fe83 100644
--- a/arch/metag/include/asm/checksum.h
+++ b/arch/metag/include/asm/checksum.h
@@ -59,8 +59,7 @@
* returns a 16-bit checksum, already complemented
*/
static inline __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
- unsigned short len,
- unsigned short proto,
+ __u32 len, __u8 proto,
__wsum sum)
{
unsigned long len_proto = (proto + len) << 8;
@@ -78,8 +77,8 @@
}
static inline __sum16
-csum_tcpudp_magic(__be32 saddr, __be32 daddr, unsigned short len,
- unsigned short proto, __wsum sum)
+csum_tcpudp_magic(__be32 saddr, __be32 daddr, __u32 len,
+ __u8 proto, __wsum sum)
{
return csum_fold(csum_tcpudp_nofold(saddr, daddr, len, proto, sum));
}
diff --git a/arch/microblaze/include/asm/checksum.h b/arch/microblaze/include/asm/checksum.h
index 0185cbe..adeeceb 100644
--- a/arch/microblaze/include/asm/checksum.h
+++ b/arch/microblaze/include/asm/checksum.h
@@ -16,8 +16,8 @@
*/
#define csum_tcpudp_nofold csum_tcpudp_nofold
static inline __wsum
-csum_tcpudp_nofold(__be32 saddr, __be32 daddr, unsigned short len,
- unsigned short proto, __wsum sum)
+csum_tcpudp_nofold(__be32 saddr, __be32 daddr, __u32 len,
+ __u8 proto, __wsum sum)
{
__asm__("add %0, %0, %1\n\t"
"addc %0, %0, %2\n\t"
diff --git a/arch/mips/include/asm/checksum.h b/arch/mips/include/asm/checksum.h
index 3ceacde..bce1ce5 100644
--- a/arch/mips/include/asm/checksum.h
+++ b/arch/mips/include/asm/checksum.h
@@ -160,9 +160,9 @@
}
#define ip_fast_csum ip_fast_csum
-static inline __wsum csum_tcpudp_nofold(__be32 saddr,
- __be32 daddr, unsigned short len, unsigned short proto,
- __wsum sum)
+static inline __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
+ __u32 len, __u8 proto,
+ __wsum sum)
{
__asm__(
" .set push # csum_tcpudp_nofold\n"
@@ -215,7 +215,7 @@
#define _HAVE_ARCH_IPV6_CSUM
static __inline__ __sum16 csum_ipv6_magic(const struct in6_addr *saddr,
const struct in6_addr *daddr,
- __u32 len, unsigned short proto,
+ __u32 len, __u8 proto,
__wsum sum)
{
__wsum tmp;
diff --git a/arch/mn10300/include/asm/checksum.h b/arch/mn10300/include/asm/checksum.h
index 9fb2a8d..c80df5b 100644
--- a/arch/mn10300/include/asm/checksum.h
+++ b/arch/mn10300/include/asm/checksum.h
@@ -37,16 +37,11 @@
return (~sum) >> 16;
}
-static inline __wsum csum_tcpudp_nofold(unsigned long saddr,
- unsigned long daddr,
- unsigned short len,
- unsigned short proto,
+static inline __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
+ __u32 len, __u8 proto,
__wsum sum)
{
- __wsum tmp;
-
- tmp = (__wsum) ntohs(len) << 16;
- tmp += (__wsum) proto << 8;
+ __wsum tmp = (__wsum)((len + proto) << 8);
asm(
" add %1,%0 \n"
@@ -64,10 +59,8 @@
* computes the checksum of the TCP/UDP pseudo-header
* returns a 16-bit checksum, already complemented
*/
-static inline __sum16 csum_tcpudp_magic(unsigned long saddr,
- unsigned long daddr,
- unsigned short len,
- unsigned short proto,
+static inline __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
+ __u32 len, __u8 proto,
__wsum sum)
{
return csum_fold(csum_tcpudp_nofold(saddr, daddr, len, proto, sum));
diff --git a/arch/nios2/include/asm/checksum.h b/arch/nios2/include/asm/checksum.h
index 6bc1f0d..703c5ee 100644
--- a/arch/nios2/include/asm/checksum.h
+++ b/arch/nios2/include/asm/checksum.h
@@ -45,8 +45,7 @@
*/
#define csum_tcpudp_nofold csum_tcpudp_nofold
static inline __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
- unsigned short len,
- unsigned short proto,
+ __u32 len, __u8 proto,
__wsum sum)
{
__asm__ __volatile__(
@@ -60,7 +59,7 @@
"cmpltu r8, %0, %3\n"
"add %0, %0, r8\n" /* add carry */
: "=r" (sum), "=r" (saddr)
- : "r" (daddr), "r" ((ntohs(len) << 16) + (proto * 256)),
+ : "r" (daddr), "r" ((len + proto) << 8),
"0" (sum),
"1" (saddr)
: "r8");
@@ -69,8 +68,8 @@
}
static inline __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
- unsigned short len,
- unsigned short proto, __wsum sum)
+ __u32 len, __u8 proto,
+ __wsum sum)
{
return csum_fold(csum_tcpudp_nofold(saddr, daddr, len, proto, sum));
}
diff --git a/arch/parisc/include/asm/checksum.h b/arch/parisc/include/asm/checksum.h
index c84b2fc..60c2c42 100644
--- a/arch/parisc/include/asm/checksum.h
+++ b/arch/parisc/include/asm/checksum.h
@@ -85,9 +85,8 @@
}
static inline __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
- unsigned short len,
- unsigned short proto,
- __wsum sum)
+ __u32 len, __u8 proto,
+ __wsum sum)
{
__asm__(
" add %1, %0, %0\n"
@@ -104,9 +103,8 @@
* returns a 16-bit checksum, already complemented
*/
static inline __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
- unsigned short len,
- unsigned short proto,
- __wsum sum)
+ __u32 len, __u8 proto,
+ __wsum sum)
{
return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum));
}
@@ -124,7 +122,7 @@
#define _HAVE_ARCH_IPV6_CSUM
static __inline__ __sum16 csum_ipv6_magic(const struct in6_addr *saddr,
const struct in6_addr *daddr,
- __u32 len, unsigned short proto,
+ __u32 len, __u8 proto,
__wsum sum)
{
__asm__ __volatile__ (
diff --git a/arch/s390/include/asm/checksum.h b/arch/s390/include/asm/checksum.h
index 7403648..d7f100c 100644
--- a/arch/s390/include/asm/checksum.h
+++ b/arch/s390/include/asm/checksum.h
@@ -91,8 +91,7 @@
* returns a 32-bit checksum
*/
static inline __wsum
-csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
- unsigned short len, unsigned short proto,
+csum_tcpudp_nofold(__be32 saddr, __be32 daddr, __u32 len, __u8 proto,
__wsum sum)
{
__u32 csum = (__force __u32)sum;
@@ -118,8 +117,7 @@
*/
static inline __sum16
-csum_tcpudp_magic(__be32 saddr, __be32 daddr,
- unsigned short len, unsigned short proto,
+csum_tcpudp_magic(__be32 saddr, __be32 daddr, __u32 len, __u8 proto,
__wsum sum)
{
return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum));
diff --git a/arch/score/include/asm/checksum.h b/arch/score/include/asm/checksum.h
index 961bd64..539d9fd 100644
--- a/arch/score/include/asm/checksum.h
+++ b/arch/score/include/asm/checksum.h
@@ -127,10 +127,10 @@
}
static inline __wsum
-csum_tcpudp_nofold(__be32 saddr, __be32 daddr, unsigned short len,
- unsigned short proto, __wsum sum)
+csum_tcpudp_nofold(__be32 saddr, __be32 daddr, __u32 len,
+ __u8 proto, __wsum sum)
{
- unsigned long tmp = (ntohs(len) << 16) + proto * 256;
+ unsigned long tmp = (len + proto) << 8;
__asm__ __volatile__(
".set volatile\n\t"
"add\t%0, %0, %2\n\t"
@@ -161,8 +161,8 @@
* returns a 16-bit checksum, already complemented
*/
static inline __sum16
-csum_tcpudp_magic(__be32 saddr, __be32 daddr, unsigned short len,
- unsigned short proto, __wsum sum)
+csum_tcpudp_magic(__be32 saddr, __be32 daddr, __u32 len,
+ __u8 proto, __wsum sum)
{
return csum_fold(csum_tcpudp_nofold(saddr, daddr, len, proto, sum));
}
@@ -179,9 +179,8 @@
#define _HAVE_ARCH_IPV6_CSUM
static inline __sum16 csum_ipv6_magic(const struct in6_addr *saddr,
- const struct in6_addr *daddr,
- __u32 len, unsigned short proto,
- __wsum sum)
+ const struct in6_addr *daddr,
+ __u32 len, __u8 proto, __wsum sum)
{
__asm__ __volatile__(
".set\tvolatile\t\t\t# csum_ipv6_magic\n\t"
diff --git a/arch/sh/include/asm/checksum_32.h b/arch/sh/include/asm/checksum_32.h
index 14b7ac2..9c84386 100644
--- a/arch/sh/include/asm/checksum_32.h
+++ b/arch/sh/include/asm/checksum_32.h
@@ -115,8 +115,7 @@
}
static inline __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
- unsigned short len,
- unsigned short proto,
+ __u32 len, __u8 proto,
__wsum sum)
{
#ifdef __LITTLE_ENDIAN__
@@ -142,8 +141,7 @@
* returns a 16-bit checksum, already complemented
*/
static inline __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
- unsigned short len,
- unsigned short proto,
+ __u32 len, __u8 proto,
__wsum sum)
{
return csum_fold(csum_tcpudp_nofold(saddr, daddr, len, proto, sum));
@@ -161,8 +159,7 @@
#define _HAVE_ARCH_IPV6_CSUM
static inline __sum16 csum_ipv6_magic(const struct in6_addr *saddr,
const struct in6_addr *daddr,
- __u32 len, unsigned short proto,
- __wsum sum)
+ __u32 len, __u8 proto, __wsum sum)
{
unsigned int __dummy;
__asm__("clrt\n\t"
diff --git a/arch/sparc/include/asm/checksum_32.h b/arch/sparc/include/asm/checksum_32.h
index 426b238..eff748c 100644
--- a/arch/sparc/include/asm/checksum_32.h
+++ b/arch/sparc/include/asm/checksum_32.h
@@ -170,9 +170,8 @@
}
static inline __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
- unsigned short len,
- unsigned short proto,
- __wsum sum)
+ __u32 len, __u8 proto,
+ __wsum sum)
{
__asm__ __volatile__("addcc\t%1, %0, %0\n\t"
"addxcc\t%2, %0, %0\n\t"
@@ -190,9 +189,8 @@
* returns a 16-bit checksum, already complemented
*/
static inline __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
- unsigned short len,
- unsigned short proto,
- __wsum sum)
+ __u32 len, __u8 proto,
+ __wsum sum)
{
return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum));
}
@@ -201,8 +199,7 @@
static inline __sum16 csum_ipv6_magic(const struct in6_addr *saddr,
const struct in6_addr *daddr,
- __u32 len, unsigned short proto,
- __wsum sum)
+ __u32 len, __u8 proto, __wsum sum)
{
__asm__ __volatile__ (
"addcc %3, %4, %%g4\n\t"
diff --git a/arch/sparc/include/asm/checksum_64.h b/arch/sparc/include/asm/checksum_64.h
index b8779a6..0395d75 100644
--- a/arch/sparc/include/asm/checksum_64.h
+++ b/arch/sparc/include/asm/checksum_64.h
@@ -96,8 +96,7 @@
}
static inline __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
- unsigned int len,
- unsigned short proto,
+ __u32 len, __u8 proto,
__wsum sum)
{
__asm__ __volatile__(
@@ -116,8 +115,7 @@
* returns a 16-bit checksum, already complemented
*/
static inline __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
- unsigned short len,
- unsigned short proto,
+ __u32 len, __u8 proto,
__wsum sum)
{
return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum));
@@ -127,8 +125,7 @@
static inline __sum16 csum_ipv6_magic(const struct in6_addr *saddr,
const struct in6_addr *daddr,
- __u32 len, unsigned short proto,
- __wsum sum)
+ __u32 len, __u8 proto, __wsum sum)
{
__asm__ __volatile__ (
" addcc %3, %4, %%g7\n"
diff --git a/arch/tile/configs/tilegx_defconfig b/arch/tile/configs/tilegx_defconfig
index 37dc936..984fa00 100644
--- a/arch/tile/configs/tilegx_defconfig
+++ b/arch/tile/configs/tilegx_defconfig
@@ -222,7 +222,7 @@
CONFIG_VETH=m
CONFIG_NET_DSA_MV88E6060=y
CONFIG_NET_DSA_MV88E6131=y
-CONFIG_NET_DSA_MV88E6123_61_65=y
+CONFIG_NET_DSA_MV88E6123=y
CONFIG_SKY2=y
CONFIG_PTP_1588_CLOCK_TILEGX=y
# CONFIG_WLAN is not set
diff --git a/arch/tile/configs/tilepro_defconfig b/arch/tile/configs/tilepro_defconfig
index 76a2781..71ad9f7 100644
--- a/arch/tile/configs/tilepro_defconfig
+++ b/arch/tile/configs/tilepro_defconfig
@@ -341,7 +341,7 @@
CONFIG_VETH=m
CONFIG_NET_DSA_MV88E6060=y
CONFIG_NET_DSA_MV88E6131=y
-CONFIG_NET_DSA_MV88E6123_61_65=y
+CONFIG_NET_DSA_MV88E6123=y
# CONFIG_NET_VENDOR_3COM is not set
CONFIG_E1000E=y
# CONFIG_WLAN is not set
diff --git a/arch/unicore32/include/asm/checksum.h b/arch/unicore32/include/asm/checksum.h
index f55c3f9..23ceb9e 100644
--- a/arch/unicore32/include/asm/checksum.h
+++ b/arch/unicore32/include/asm/checksum.h
@@ -20,8 +20,8 @@
*/
static inline __wsum
-csum_tcpudp_nofold(__be32 saddr, __be32 daddr, unsigned short len,
- unsigned short proto, __wsum sum)
+csum_tcpudp_nofold(__be32 saddr, __be32 daddr, __u32 len,
+ __u8 proto, __wsum sum)
{
__asm__(
"add.a %0, %1, %2\n"
diff --git a/arch/x86/include/asm/checksum_32.h b/arch/x86/include/asm/checksum_32.h
index f50de69..532f85e 100644
--- a/arch/x86/include/asm/checksum_32.h
+++ b/arch/x86/include/asm/checksum_32.h
@@ -112,8 +112,7 @@
}
static inline __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
- unsigned short len,
- unsigned short proto,
+ __u32 len, __u8 proto,
__wsum sum)
{
asm("addl %1, %0 ;\n"
@@ -131,8 +130,7 @@
* returns a 16-bit checksum, already complemented
*/
static inline __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
- unsigned short len,
- unsigned short proto,
+ __u32 len, __u8 proto,
__wsum sum)
{
return csum_fold(csum_tcpudp_nofold(saddr, daddr, len, proto, sum));
@@ -151,8 +149,7 @@
#define _HAVE_ARCH_IPV6_CSUM
static inline __sum16 csum_ipv6_magic(const struct in6_addr *saddr,
const struct in6_addr *daddr,
- __u32 len, unsigned short proto,
- __wsum sum)
+ __u32 len, __u8 proto, __wsum sum)
{
asm("addl 0(%1), %0 ;\n"
"adcl 4(%1), %0 ;\n"
diff --git a/arch/x86/include/asm/checksum_64.h b/arch/x86/include/asm/checksum_64.h
index cd00e17..c020ee7 100644
--- a/arch/x86/include/asm/checksum_64.h
+++ b/arch/x86/include/asm/checksum_64.h
@@ -84,8 +84,8 @@
* 32bit unfolded.
*/
static inline __wsum
-csum_tcpudp_nofold(__be32 saddr, __be32 daddr, unsigned short len,
- unsigned short proto, __wsum sum)
+csum_tcpudp_nofold(__be32 saddr, __be32 daddr, __u32 len,
+ __u8 proto, __wsum sum)
{
asm(" addl %1, %0\n"
" adcl %2, %0\n"
@@ -110,8 +110,8 @@
* complemented and ready to be filled in.
*/
static inline __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
- unsigned short len,
- unsigned short proto, __wsum sum)
+ __u32 len, __u8 proto,
+ __wsum sum)
{
return csum_fold(csum_tcpudp_nofold(saddr, daddr, len, proto, sum));
}
@@ -177,7 +177,7 @@
#define _HAVE_ARCH_IPV6_CSUM 1
extern __sum16
csum_ipv6_magic(const struct in6_addr *saddr, const struct in6_addr *daddr,
- __u32 len, unsigned short proto, __wsum sum);
+ __u32 len, __u8 proto, __wsum sum);
static inline unsigned add32_with_carry(unsigned a, unsigned b)
{
diff --git a/arch/x86/lib/csum-wrappers_64.c b/arch/x86/lib/csum-wrappers_64.c
index 1318f75..28a6654 100644
--- a/arch/x86/lib/csum-wrappers_64.c
+++ b/arch/x86/lib/csum-wrappers_64.c
@@ -135,7 +135,7 @@
__sum16 csum_ipv6_magic(const struct in6_addr *saddr,
const struct in6_addr *daddr,
- __u32 len, unsigned short proto, __wsum sum)
+ __u32 len, __u8 proto, __wsum sum)
{
__u64 rest, sum64;
diff --git a/arch/x86/um/asm/checksum.h b/arch/x86/um/asm/checksum.h
index ee94018..54d96f1 100644
--- a/arch/x86/um/asm/checksum.h
+++ b/arch/x86/um/asm/checksum.h
@@ -87,8 +87,8 @@
* 32bit unfolded.
*/
static inline __wsum
-csum_tcpudp_nofold(__be32 saddr, __be32 daddr, unsigned short len,
- unsigned short proto, __wsum sum)
+csum_tcpudp_nofold(__be32 saddr, __be32 daddr, __u32 len,
+ __u8 proto, __wsum sum)
{
asm(" addl %1, %0\n"
" adcl %2, %0\n"
@@ -104,9 +104,8 @@
* returns a 16-bit checksum, already complemented
*/
static inline __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
- unsigned short len,
- unsigned short proto,
- __wsum sum)
+ __u32 len, __u8 proto,
+ __wsum sum)
{
return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum));
}
diff --git a/arch/x86/um/asm/checksum_32.h b/arch/x86/um/asm/checksum_32.h
index ab77b6f..83a75f8 100644
--- a/arch/x86/um/asm/checksum_32.h
+++ b/arch/x86/um/asm/checksum_32.h
@@ -13,7 +13,7 @@
#define _HAVE_ARCH_IPV6_CSUM
static __inline__ __sum16 csum_ipv6_magic(const struct in6_addr *saddr,
const struct in6_addr *daddr,
- __u32 len, unsigned short proto,
+ __u32 len, __u8 proto,
__wsum sum)
{
__asm__(
diff --git a/arch/xtensa/include/asm/checksum.h b/arch/xtensa/include/asm/checksum.h
index 0593de6..ec35074 100644
--- a/arch/xtensa/include/asm/checksum.h
+++ b/arch/xtensa/include/asm/checksum.h
@@ -123,9 +123,8 @@
}
static __inline__ __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
- unsigned short len,
- unsigned short proto,
- __wsum sum)
+ __u32 len, __u8 proto,
+ __wsum sum)
{
#ifdef __XTENSA_EL__
@@ -157,9 +156,8 @@
* returns a 16-bit checksum, already complemented
*/
static __inline__ __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
- unsigned short len,
- unsigned short proto,
- __wsum sum)
+ __u32 len, __u8 proto,
+ __wsum sum)
{
return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum));
}
@@ -177,7 +175,7 @@
#define _HAVE_ARCH_IPV6_CSUM
static __inline__ __sum16 csum_ipv6_magic(const struct in6_addr *saddr,
const struct in6_addr *daddr,
- __u32 len, unsigned short proto,
+ __u32 len, __u8 proto,
__wsum sum)
{
unsigned int __dummy;
diff --git a/drivers/bcma/Kconfig b/drivers/bcma/Kconfig
index 023d448..efdc2ae 100644
--- a/drivers/bcma/Kconfig
+++ b/drivers/bcma/Kconfig
@@ -70,6 +70,11 @@
If unsure, say N
+config BCMA_PFLASH
+ bool
+ depends on BCMA_DRIVER_MIPS
+ default y
+
config BCMA_SFLASH
bool
depends on BCMA_DRIVER_MIPS
diff --git a/drivers/bcma/Makefile b/drivers/bcma/Makefile
index f32af9b..087948a 100644
--- a/drivers/bcma/Makefile
+++ b/drivers/bcma/Makefile
@@ -1,6 +1,7 @@
bcma-y += main.o scan.o core.o sprom.o
bcma-y += driver_chipcommon.o driver_chipcommon_pmu.o
bcma-y += driver_chipcommon_b.o
+bcma-$(CONFIG_BCMA_PFLASH) += driver_chipcommon_pflash.o
bcma-$(CONFIG_BCMA_SFLASH) += driver_chipcommon_sflash.o
bcma-$(CONFIG_BCMA_NFLASH) += driver_chipcommon_nflash.o
bcma-$(CONFIG_BCMA_DRIVER_PCI) += driver_pci.o
diff --git a/drivers/bcma/bcma_private.h b/drivers/bcma/bcma_private.h
index 7e4ddfb..eda0909 100644
--- a/drivers/bcma/bcma_private.h
+++ b/drivers/bcma/bcma_private.h
@@ -47,9 +47,6 @@
void bcma_core_chipcommon_early_init(struct bcma_drv_cc *cc);
void bcma_core_chipcommon_init(struct bcma_drv_cc *cc);
void bcma_chipco_bcm4331_ext_pa_lines_ctl(struct bcma_drv_cc *cc, bool enable);
-#ifdef CONFIG_BCMA_DRIVER_MIPS
-extern struct platform_device bcma_pflash_dev;
-#endif /* CONFIG_BCMA_DRIVER_MIPS */
/* driver_chipcommon_b.c */
int bcma_core_chipcommon_b_init(struct bcma_drv_cc_b *ccb);
@@ -61,6 +58,21 @@
u32 bcma_pmu_get_alp_clock(struct bcma_drv_cc *cc);
u32 bcma_pmu_get_cpu_clock(struct bcma_drv_cc *cc);
+/**************************************************
+ * driver_chipcommon_sflash.c
+ **************************************************/
+
+#ifdef CONFIG_BCMA_PFLASH
+extern struct platform_device bcma_pflash_dev;
+int bcma_pflash_init(struct bcma_drv_cc *cc);
+#else
+static inline int bcma_pflash_init(struct bcma_drv_cc *cc)
+{
+ bcma_err(cc->core->bus, "Parallel flash not supported\n");
+ return 0;
+}
+#endif /* CONFIG_BCMA_PFLASH */
+
#ifdef CONFIG_BCMA_SFLASH
/* driver_chipcommon_sflash.c */
int bcma_sflash_init(struct bcma_drv_cc *cc);
diff --git a/drivers/bcma/driver_chipcommon.c b/drivers/bcma/driver_chipcommon.c
index b0f44a2..921ce18 100644
--- a/drivers/bcma/driver_chipcommon.c
+++ b/drivers/bcma/driver_chipcommon.c
@@ -115,6 +115,33 @@
return 0;
}
+static void bcma_core_chipcommon_flash_detect(struct bcma_drv_cc *cc)
+{
+ struct bcma_bus *bus = cc->core->bus;
+
+ switch (cc->capabilities & BCMA_CC_CAP_FLASHT) {
+ case BCMA_CC_FLASHT_STSER:
+ case BCMA_CC_FLASHT_ATSER:
+ bcma_debug(bus, "Found serial flash\n");
+ bcma_sflash_init(cc);
+ break;
+ case BCMA_CC_FLASHT_PARA:
+ bcma_debug(bus, "Found parallel flash\n");
+ bcma_pflash_init(cc);
+ break;
+ default:
+ bcma_err(bus, "Flash type not supported\n");
+ }
+
+ if (cc->core->id.rev == 38 ||
+ bus->chipinfo.id == BCMA_CHIP_ID_BCM4706) {
+ if (cc->capabilities & BCMA_CC_CAP_NFLASH) {
+ bcma_debug(bus, "Found NAND flash\n");
+ bcma_nflash_init(cc);
+ }
+ }
+}
+
void bcma_core_chipcommon_early_init(struct bcma_drv_cc *cc)
{
struct bcma_bus *bus = cc->core->bus;
@@ -136,6 +163,9 @@
if (IS_BUILTIN(CONFIG_BCM47XX) && bus->hosttype == BCMA_HOSTTYPE_SOC)
bcma_chipco_serial_init(cc);
+ if (bus->hosttype == BCMA_HOSTTYPE_SOC)
+ bcma_core_chipcommon_flash_detect(cc);
+
cc->early_setup_done = true;
}
diff --git a/drivers/bcma/driver_chipcommon_pflash.c b/drivers/bcma/driver_chipcommon_pflash.c
new file mode 100644
index 0000000..3b497c9
--- /dev/null
+++ b/drivers/bcma/driver_chipcommon_pflash.c
@@ -0,0 +1,49 @@
+/*
+ * Broadcom specific AMBA
+ * ChipCommon parallel flash
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "bcma_private.h"
+
+#include <linux/bcma/bcma.h>
+#include <linux/mtd/physmap.h>
+#include <linux/platform_device.h>
+
+static const char * const part_probes[] = { "bcm47xxpart", NULL };
+
+static struct physmap_flash_data bcma_pflash_data = {
+ .part_probe_types = part_probes,
+};
+
+static struct resource bcma_pflash_resource = {
+ .name = "bcma_pflash",
+ .flags = IORESOURCE_MEM,
+};
+
+struct platform_device bcma_pflash_dev = {
+ .name = "physmap-flash",
+ .dev = {
+ .platform_data = &bcma_pflash_data,
+ },
+ .resource = &bcma_pflash_resource,
+ .num_resources = 1,
+};
+
+int bcma_pflash_init(struct bcma_drv_cc *cc)
+{
+ struct bcma_pflash *pflash = &cc->pflash;
+
+ pflash->present = true;
+
+ if (!(bcma_read32(cc->core, BCMA_CC_FLASH_CFG) & BCMA_CC_FLASH_CFG_DS))
+ bcma_pflash_data.width = 1;
+ else
+ bcma_pflash_data.width = 2;
+
+ bcma_pflash_resource.start = BCMA_SOC_FLASH2;
+ bcma_pflash_resource.end = BCMA_SOC_FLASH2 + BCMA_SOC_FLASH2_SZ;
+
+ return 0;
+}
diff --git a/drivers/bcma/driver_mips.c b/drivers/bcma/driver_mips.c
index a40a203..96f1713 100644
--- a/drivers/bcma/driver_mips.c
+++ b/drivers/bcma/driver_mips.c
@@ -14,8 +14,6 @@
#include <linux/bcma/bcma.h>
-#include <linux/mtd/physmap.h>
-#include <linux/platform_device.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
#include <linux/serial_reg.h>
@@ -32,26 +30,6 @@
BCMA_BOOT_DEV_NAND,
};
-static const char * const part_probes[] = { "bcm47xxpart", NULL };
-
-static struct physmap_flash_data bcma_pflash_data = {
- .part_probe_types = part_probes,
-};
-
-static struct resource bcma_pflash_resource = {
- .name = "bcma_pflash",
- .flags = IORESOURCE_MEM,
-};
-
-struct platform_device bcma_pflash_dev = {
- .name = "physmap-flash",
- .dev = {
- .platform_data = &bcma_pflash_data,
- },
- .resource = &bcma_pflash_resource,
- .num_resources = 1,
-};
-
/* The 47162a0 hangs when reading MIPS DMP registers registers */
static inline bool bcma_core_mips_bcm47162a0_quirk(struct bcma_device *dev)
{
@@ -272,48 +250,11 @@
return BCMA_BOOT_DEV_SERIAL;
}
-static void bcma_core_mips_flash_detect(struct bcma_drv_mips *mcore)
+static void bcma_core_mips_nvram_init(struct bcma_drv_mips *mcore)
{
struct bcma_bus *bus = mcore->core->bus;
- struct bcma_drv_cc *cc = &bus->drv_cc;
- struct bcma_pflash *pflash = &cc->pflash;
enum bcma_boot_dev boot_dev;
- switch (cc->capabilities & BCMA_CC_CAP_FLASHT) {
- case BCMA_CC_FLASHT_STSER:
- case BCMA_CC_FLASHT_ATSER:
- bcma_debug(bus, "Found serial flash\n");
- bcma_sflash_init(cc);
- break;
- case BCMA_CC_FLASHT_PARA:
- bcma_debug(bus, "Found parallel flash\n");
- pflash->present = true;
- pflash->window = BCMA_SOC_FLASH2;
- pflash->window_size = BCMA_SOC_FLASH2_SZ;
-
- if ((bcma_read32(cc->core, BCMA_CC_FLASH_CFG) &
- BCMA_CC_FLASH_CFG_DS) == 0)
- pflash->buswidth = 1;
- else
- pflash->buswidth = 2;
-
- bcma_pflash_data.width = pflash->buswidth;
- bcma_pflash_resource.start = pflash->window;
- bcma_pflash_resource.end = pflash->window + pflash->window_size;
-
- break;
- default:
- bcma_err(bus, "Flash type not supported\n");
- }
-
- if (cc->core->id.rev == 38 ||
- bus->chipinfo.id == BCMA_CHIP_ID_BCM4706) {
- if (cc->capabilities & BCMA_CC_CAP_NFLASH) {
- bcma_debug(bus, "Found NAND flash\n");
- bcma_nflash_init(cc);
- }
- }
-
/* Determine flash type this SoC boots from */
boot_dev = bcma_boot_dev(bus);
switch (boot_dev) {
@@ -340,7 +281,7 @@
if (mcore->early_setup_done)
return;
- bcma_core_mips_flash_detect(mcore);
+ bcma_core_mips_nvram_init(mcore);
mcore->early_setup_done = true;
}
diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c
index c466f75..786be8f 100644
--- a/drivers/bcma/main.c
+++ b/drivers/bcma/main.c
@@ -350,7 +350,7 @@
bcma_register_core(bus, core);
}
-#ifdef CONFIG_BCMA_DRIVER_MIPS
+#ifdef CONFIG_BCMA_PFLASH
if (bus->drv_cc.pflash.present) {
err = platform_device_register(&bcma_pflash_dev);
if (err)
diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c
index c43c3d2..c2e5286 100644
--- a/drivers/bus/mvebu-mbus.c
+++ b/drivers/bus/mvebu-mbus.c
@@ -948,6 +948,58 @@
*res = mbus_state.pcie_io_aperture;
}
+int mvebu_mbus_get_dram_win_info(phys_addr_t phyaddr, u8 *target, u8 *attr)
+{
+ const struct mbus_dram_target_info *dram;
+ int i;
+
+ /* Get dram info */
+ dram = mv_mbus_dram_info();
+ if (!dram) {
+ pr_err("missing DRAM information\n");
+ return -ENODEV;
+ }
+
+ /* Try to find matching DRAM window for phyaddr */
+ for (i = 0; i < dram->num_cs; i++) {
+ const struct mbus_dram_window *cs = dram->cs + i;
+
+ if (cs->base <= phyaddr &&
+ phyaddr <= (cs->base + cs->size - 1)) {
+ *target = dram->mbus_dram_target_id;
+ *attr = cs->mbus_attr;
+ return 0;
+ }
+ }
+
+ pr_err("invalid dram address 0x%x\n", phyaddr);
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(mvebu_mbus_get_dram_win_info);
+
+int mvebu_mbus_get_io_win_info(phys_addr_t phyaddr, u32 *size, u8 *target,
+ u8 *attr)
+{
+ int win;
+
+ for (win = 0; win < mbus_state.soc->num_wins; win++) {
+ u64 wbase;
+ int enabled;
+
+ mvebu_mbus_read_window(&mbus_state, win, &enabled, &wbase,
+ size, target, attr, NULL);
+
+ if (!enabled)
+ continue;
+
+ if (wbase <= phyaddr && phyaddr <= wbase + *size)
+ return win;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(mvebu_mbus_get_io_win_info);
+
static __init int mvebu_mbus_debugfs_init(void)
{
struct mvebu_mbus_state *s = &mbus_state;
diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c
index 736dae7..69cdabe 100644
--- a/drivers/misc/sram.c
+++ b/drivers/misc/sram.c
@@ -360,7 +360,10 @@
return -EBUSY;
}
- sram->virt_base = devm_ioremap_wc(sram->dev, res->start, size);
+ if (of_property_read_bool(pdev->dev.of_node, "no-memory-wc"))
+ sram->virt_base = devm_ioremap(sram->dev, res->start, size);
+ else
+ sram->virt_base = devm_ioremap_wc(sram->dev, res->start, size);
if (IS_ERR(sram->virt_base))
return PTR_ERR(sram->virt_base);
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index f184fb5..2a1ba62b 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -193,6 +193,13 @@
To compile this driver as a module, choose M here: the module
will be called geneve.
+config MACSEC
+ tristate "IEEE 802.1AE MAC-level encryption (MACsec)"
+ select CRYPTO_AES
+ select CRYPTO_GCM
+ ---help---
+ MACsec is an encryption standard for Ethernet.
+
config NETCONSOLE
tristate "Network console logging support"
---help---
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 900b0c5..1aa7cb8 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -10,6 +10,7 @@
obj-$(CONFIG_DUMMY) += dummy.o
obj-$(CONFIG_EQUALIZER) += eql.o
obj-$(CONFIG_IFB) += ifb.o
+obj-$(CONFIG_MACSEC) += macsec.o
obj-$(CONFIG_MACVLAN) += macvlan.o
obj-$(CONFIG_MACVTAP) += macvtap.o
obj-$(CONFIG_MII) += mii.o
diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
index 4c483d9..90ba003 100644
--- a/drivers/net/dsa/Kconfig
+++ b/drivers/net/dsa/Kconfig
@@ -27,7 +27,7 @@
This enables support for the Marvell 88E6085/6095/6095F/6131
ethernet switch chips.
-config NET_DSA_MV88E6123_61_65
+config NET_DSA_MV88E6123
tristate "Marvell 88E6123/6161/6165 ethernet switch chip support"
depends on NET_DSA
select NET_DSA_MV88E6XXX
diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile
index e2d51c4..a6e0993 100644
--- a/drivers/net/dsa/Makefile
+++ b/drivers/net/dsa/Makefile
@@ -1,8 +1,8 @@
obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o
obj-$(CONFIG_NET_DSA_MV88E6XXX) += mv88e6xxx_drv.o
mv88e6xxx_drv-y += mv88e6xxx.o
-ifdef CONFIG_NET_DSA_MV88E6123_61_65
-mv88e6xxx_drv-y += mv88e6123_61_65.o
+ifdef CONFIG_NET_DSA_MV88E6123
+mv88e6xxx_drv-y += mv88e6123.o
endif
ifdef CONFIG_NET_DSA_MV88E6131
mv88e6xxx_drv-y += mv88e6131.o
diff --git a/drivers/net/dsa/mv88e6123_61_65.c b/drivers/net/dsa/mv88e6123.c
similarity index 84%
rename from drivers/net/dsa/mv88e6123_61_65.c
rename to drivers/net/dsa/mv88e6123.c
index d4fcf45..69a6f79 100644
--- a/drivers/net/dsa/mv88e6123_61_65.c
+++ b/drivers/net/dsa/mv88e6123.c
@@ -17,7 +17,7 @@
#include <net/dsa.h>
#include "mv88e6xxx.h"
-static const struct mv88e6xxx_switch_id mv88e6123_61_65_table[] = {
+static const struct mv88e6xxx_switch_id mv88e6123_table[] = {
{ PORT_SWITCH_ID_6123, "Marvell 88E6123" },
{ PORT_SWITCH_ID_6123_A1, "Marvell 88E6123 (A1)" },
{ PORT_SWITCH_ID_6123_A2, "Marvell 88E6123 (A2)" },
@@ -29,13 +29,13 @@
{ PORT_SWITCH_ID_6165_A2, "Marvell 88e6165 (A2)" },
};
-static char *mv88e6123_61_65_probe(struct device *host_dev, int sw_addr)
+static char *mv88e6123_probe(struct device *host_dev, int sw_addr)
{
- return mv88e6xxx_lookup_name(host_dev, sw_addr, mv88e6123_61_65_table,
- ARRAY_SIZE(mv88e6123_61_65_table));
+ return mv88e6xxx_lookup_name(host_dev, sw_addr, mv88e6123_table,
+ ARRAY_SIZE(mv88e6123_table));
}
-static int mv88e6123_61_65_setup_global(struct dsa_switch *ds)
+static int mv88e6123_setup_global(struct dsa_switch *ds)
{
u32 upstream_port = dsa_upstream_port(ds);
int ret;
@@ -68,7 +68,7 @@
return 0;
}
-static int mv88e6123_61_65_setup(struct dsa_switch *ds)
+static int mv88e6123_setup(struct dsa_switch *ds)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int ret;
@@ -93,18 +93,18 @@
if (ret < 0)
return ret;
- ret = mv88e6123_61_65_setup_global(ds);
+ ret = mv88e6123_setup_global(ds);
if (ret < 0)
return ret;
return mv88e6xxx_setup_ports(ds);
}
-struct dsa_switch_driver mv88e6123_61_65_switch_driver = {
+struct dsa_switch_driver mv88e6123_switch_driver = {
.tag_protocol = DSA_TAG_PROTO_EDSA,
.priv_size = sizeof(struct mv88e6xxx_priv_state),
- .probe = mv88e6123_61_65_probe,
- .setup = mv88e6123_61_65_setup,
+ .probe = mv88e6123_probe,
+ .setup = mv88e6123_setup,
.set_addr = mv88e6xxx_set_addr_indirect,
.phy_read = mv88e6xxx_phy_read,
.phy_write = mv88e6xxx_phy_write,
diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c
index d11c9d5..5309c738 100644
--- a/drivers/net/dsa/mv88e6xxx.c
+++ b/drivers/net/dsa/mv88e6xxx.c
@@ -1051,39 +1051,49 @@
return _mv88e6xxx_atu_move(ds, fid, port, 0x0f, static_too);
}
-static int mv88e6xxx_set_port_state(struct dsa_switch *ds, int port, u8 state)
+static const char * const mv88e6xxx_port_state_names[] = {
+ [PORT_CONTROL_STATE_DISABLED] = "Disabled",
+ [PORT_CONTROL_STATE_BLOCKING] = "Blocking/Listening",
+ [PORT_CONTROL_STATE_LEARNING] = "Learning",
+ [PORT_CONTROL_STATE_FORWARDING] = "Forwarding",
+};
+
+static int _mv88e6xxx_port_state(struct dsa_switch *ds, int port, u8 state)
{
- struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int reg, ret = 0;
u8 oldstate;
- mutex_lock(&ps->smi_mutex);
-
reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_CONTROL);
- if (reg < 0) {
- ret = reg;
- goto abort;
- }
+ if (reg < 0)
+ return reg;
oldstate = reg & PORT_CONTROL_STATE_MASK;
+
if (oldstate != state) {
/* Flush forwarding database if we're moving a port
* from Learning or Forwarding state to Disabled or
* Blocking or Listening state.
*/
- if (oldstate >= PORT_CONTROL_STATE_LEARNING &&
- state <= PORT_CONTROL_STATE_BLOCKING) {
+ if ((oldstate == PORT_CONTROL_STATE_LEARNING ||
+ oldstate == PORT_CONTROL_STATE_FORWARDING)
+ && (state == PORT_CONTROL_STATE_DISABLED ||
+ state == PORT_CONTROL_STATE_BLOCKING)) {
ret = _mv88e6xxx_atu_remove(ds, 0, port, false);
if (ret)
- goto abort;
+ return ret;
}
+
reg = (reg & ~PORT_CONTROL_STATE_MASK) | state;
ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_CONTROL,
reg);
+ if (ret)
+ return ret;
+
+ netdev_dbg(ds->ports[port], "PortState %s (was %s)\n",
+ mv88e6xxx_port_state_names[state],
+ mv88e6xxx_port_state_names[oldstate]);
}
-abort:
- mutex_unlock(&ps->smi_mutex);
return ret;
}
@@ -1146,35 +1156,55 @@
break;
}
- netdev_dbg(ds->ports[port], "port state %d [%d]\n", state, stp_state);
-
/* mv88e6xxx_port_stp_update may be called with softirqs disabled,
* so we can not update the port state directly but need to schedule it.
*/
ps->ports[port].state = stp_state;
- set_bit(port, &ps->port_state_update_mask);
+ set_bit(port, ps->port_state_update_mask);
schedule_work(&ps->bridge_work);
return 0;
}
-static int _mv88e6xxx_port_pvid_get(struct dsa_switch *ds, int port, u16 *pvid)
+static int _mv88e6xxx_port_pvid(struct dsa_switch *ds, int port, u16 *new,
+ u16 *old)
{
+ u16 pvid;
int ret;
ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_DEFAULT_VLAN);
if (ret < 0)
return ret;
- *pvid = ret & PORT_DEFAULT_VLAN_MASK;
+ pvid = ret & PORT_DEFAULT_VLAN_MASK;
+
+ if (new) {
+ ret &= ~PORT_DEFAULT_VLAN_MASK;
+ ret |= *new & PORT_DEFAULT_VLAN_MASK;
+
+ ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
+ PORT_DEFAULT_VLAN, ret);
+ if (ret < 0)
+ return ret;
+
+ netdev_dbg(ds->ports[port], "DefaultVID %d (was %d)\n", *new,
+ pvid);
+ }
+
+ if (old)
+ *old = pvid;
return 0;
}
+static int _mv88e6xxx_port_pvid_get(struct dsa_switch *ds, int port, u16 *pvid)
+{
+ return _mv88e6xxx_port_pvid(ds, port, NULL, pvid);
+}
+
static int _mv88e6xxx_port_pvid_set(struct dsa_switch *ds, int port, u16 pvid)
{
- return _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_DEFAULT_VLAN,
- pvid & PORT_DEFAULT_VLAN_MASK);
+ return _mv88e6xxx_port_pvid(ds, port, &pvid, NULL);
}
static int _mv88e6xxx_vtu_wait(struct dsa_switch *ds)
@@ -1735,16 +1765,21 @@
old = ret & PORT_CONTROL_2_8021Q_MASK;
- ret &= ~PORT_CONTROL_2_8021Q_MASK;
- ret |= new & PORT_CONTROL_2_8021Q_MASK;
+ if (new != old) {
+ ret &= ~PORT_CONTROL_2_8021Q_MASK;
+ ret |= new & PORT_CONTROL_2_8021Q_MASK;
- ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_CONTROL_2, ret);
- if (ret < 0)
- goto unlock;
+ ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_CONTROL_2,
+ ret);
+ if (ret < 0)
+ goto unlock;
- netdev_dbg(ds->ports[port], "802.1Q Mode: %s (was %s)\n",
- mv88e6xxx_port_8021q_mode_names[new],
- mv88e6xxx_port_8021q_mode_names[old]);
+ netdev_dbg(ds->ports[port], "802.1Q Mode %s (was %s)\n",
+ mv88e6xxx_port_8021q_mode_names[new],
+ mv88e6xxx_port_8021q_mode_names[old]);
+ }
+
+ ret = 0;
unlock:
mutex_unlock(&ps->smi_mutex);
@@ -2228,11 +2263,15 @@
ps = container_of(work, struct mv88e6xxx_priv_state, bridge_work);
ds = ((struct dsa_switch *)ps) - 1;
- while (ps->port_state_update_mask) {
- port = __ffs(ps->port_state_update_mask);
- clear_bit(port, &ps->port_state_update_mask);
- mv88e6xxx_set_port_state(ds, port, ps->ports[port].state);
- }
+ mutex_lock(&ps->smi_mutex);
+
+ for (port = 0; port < ps->num_ports; ++port)
+ if (test_and_clear_bit(port, ps->port_state_update_mask) &&
+ _mv88e6xxx_port_state(ds, port, ps->ports[port].state))
+ netdev_warn(ds->ports[port], "failed to update state to %s\n",
+ mv88e6xxx_port_state_names[ps->ports[port].state]);
+
+ mutex_unlock(&ps->smi_mutex);
}
static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port)
@@ -2950,8 +2989,8 @@
#if IS_ENABLED(CONFIG_NET_DSA_MV88E6131)
register_switch_driver(&mv88e6131_switch_driver);
#endif
-#if IS_ENABLED(CONFIG_NET_DSA_MV88E6123_61_65)
- register_switch_driver(&mv88e6123_61_65_switch_driver);
+#if IS_ENABLED(CONFIG_NET_DSA_MV88E6123)
+ register_switch_driver(&mv88e6123_switch_driver);
#endif
#if IS_ENABLED(CONFIG_NET_DSA_MV88E6352)
register_switch_driver(&mv88e6352_switch_driver);
@@ -2971,8 +3010,8 @@
#if IS_ENABLED(CONFIG_NET_DSA_MV88E6352)
unregister_switch_driver(&mv88e6352_switch_driver);
#endif
-#if IS_ENABLED(CONFIG_NET_DSA_MV88E6123_61_65)
- unregister_switch_driver(&mv88e6123_61_65_switch_driver);
+#if IS_ENABLED(CONFIG_NET_DSA_MV88E6123)
+ unregister_switch_driver(&mv88e6123_switch_driver);
#endif
#if IS_ENABLED(CONFIG_NET_DSA_MV88E6131)
unregister_switch_driver(&mv88e6131_switch_driver);
diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h
index d7b088d..281cefe 100644
--- a/drivers/net/dsa/mv88e6xxx.h
+++ b/drivers/net/dsa/mv88e6xxx.h
@@ -426,7 +426,7 @@
struct mv88e6xxx_priv_port ports[DSA_MAX_PORTS];
- unsigned long port_state_update_mask;
+ DECLARE_BITMAP(port_state_update_mask, DSA_MAX_PORTS);
struct work_struct bridge_work;
};
@@ -519,7 +519,7 @@
int reg, int val);
extern struct dsa_switch_driver mv88e6131_switch_driver;
-extern struct dsa_switch_driver mv88e6123_61_65_switch_driver;
+extern struct dsa_switch_driver mv88e6123_switch_driver;
extern struct dsa_switch_driver mv88e6352_switch_driver;
extern struct dsa_switch_driver mv88e6171_switch_driver;
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index 0b13af8..be67a19 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -106,6 +106,7 @@
Support for the MII0 inside the Lantiq SoC
source "drivers/net/ethernet/marvell/Kconfig"
+source "drivers/net/ethernet/mediatek/Kconfig"
source "drivers/net/ethernet/mellanox/Kconfig"
source "drivers/net/ethernet/micrel/Kconfig"
source "drivers/net/ethernet/microchip/Kconfig"
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index 38dc1a7..6ffcc80 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -46,6 +46,7 @@
obj-$(CONFIG_KORINA) += korina.o
obj-$(CONFIG_LANTIQ_ETOP) += lantiq_etop.o
obj-$(CONFIG_NET_VENDOR_MARVELL) += marvell/
+obj-$(CONFIG_NET_VENDOR_MEDIATEK) += mediatek/
obj-$(CONFIG_NET_VENDOR_MELLANOX) += mellanox/
obj-$(CONFIG_NET_VENDOR_MICREL) += micrel/
obj-$(CONFIG_NET_VENDOR_MICROCHIP) += microchip/
diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c
index 7ccf229..3ce6095 100644
--- a/drivers/net/ethernet/cadence/macb.c
+++ b/drivers/net/ethernet/cadence/macb.c
@@ -2440,9 +2440,9 @@
if (bp->phy_interface == PHY_INTERFACE_MODE_RGMII)
val = GEM_BIT(RGMII);
else if (bp->phy_interface == PHY_INTERFACE_MODE_RMII &&
- (bp->caps & MACB_CAPS_USRIO_DEFAULT_IS_MII))
+ (bp->caps & MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII))
val = MACB_BIT(RMII);
- else if (!(bp->caps & MACB_CAPS_USRIO_DEFAULT_IS_MII))
+ else if (!(bp->caps & MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII))
val = MACB_BIT(MII);
if (bp->caps & MACB_CAPS_USRIO_HAS_CLKEN)
@@ -2774,7 +2774,7 @@
}
static const struct macb_config at91sam9260_config = {
- .caps = MACB_CAPS_USRIO_HAS_CLKEN | MACB_CAPS_USRIO_DEFAULT_IS_MII,
+ .caps = MACB_CAPS_USRIO_HAS_CLKEN | MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII,
.clk_init = macb_clk_init,
.init = macb_init,
};
@@ -2787,21 +2787,22 @@
};
static const struct macb_config sama5d2_config = {
- .caps = 0,
+ .caps = MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII,
.dma_burst_length = 16,
.clk_init = macb_clk_init,
.init = macb_init,
};
static const struct macb_config sama5d3_config = {
- .caps = MACB_CAPS_SG_DISABLED | MACB_CAPS_GIGABIT_MODE_AVAILABLE,
+ .caps = MACB_CAPS_SG_DISABLED | MACB_CAPS_GIGABIT_MODE_AVAILABLE
+ | MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII,
.dma_burst_length = 16,
.clk_init = macb_clk_init,
.init = macb_init,
};
static const struct macb_config sama5d4_config = {
- .caps = 0,
+ .caps = MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII,
.dma_burst_length = 4,
.clk_init = macb_clk_init,
.init = macb_init,
diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h
index 9ba416d..8a13824 100644
--- a/drivers/net/ethernet/cadence/macb.h
+++ b/drivers/net/ethernet/cadence/macb.h
@@ -400,7 +400,7 @@
/* Capability mask bits */
#define MACB_CAPS_ISR_CLEAR_ON_WRITE 0x00000001
#define MACB_CAPS_USRIO_HAS_CLKEN 0x00000002
-#define MACB_CAPS_USRIO_DEFAULT_IS_MII 0x00000004
+#define MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII 0x00000004
#define MACB_CAPS_NO_GIGABIT_HALF 0x00000008
#define MACB_CAPS_USRIO_DISABLED 0x00000010
#define MACB_CAPS_FIFO_MODE 0x10000000
diff --git a/drivers/net/ethernet/cavium/thunder/nic.h b/drivers/net/ethernet/cavium/thunder/nic.h
index 092f097..83025bb 100644
--- a/drivers/net/ethernet/cavium/thunder/nic.h
+++ b/drivers/net/ethernet/cavium/thunder/nic.h
@@ -272,45 +272,54 @@
struct nicvf *pnicvf;
struct net_device *netdev;
struct pci_dev *pdev;
- u8 vf_id;
- u8 node;
- u8 tns_mode:1;
- u8 sqs_mode:1;
- u8 loopback_supported:1;
- bool hw_tso;
- u16 mtu;
+ void __iomem *reg_base;
struct queue_set *qs;
+ struct nicvf_cq_poll *napi[8];
+ u8 vf_id;
+ u8 sqs_id;
+ bool sqs_mode;
+ bool hw_tso;
+
+ /* Receive buffer alloc */
+ u32 rb_page_offset;
+ u16 rb_pageref;
+ bool rb_alloc_fail;
+ bool rb_work_scheduled;
+ struct page *rb_page;
+ struct delayed_work rbdr_work;
+ struct tasklet_struct rbdr_task;
+
+ /* Secondary Qset */
+ u8 sqs_count;
#define MAX_SQS_PER_VF_SINGLE_NODE 5
#define MAX_SQS_PER_VF 11
- u8 sqs_id;
- u8 sqs_count; /* Secondary Qset count */
struct nicvf *snicvf[MAX_SQS_PER_VF];
+
+ /* Queue count */
u8 rx_queues;
u8 tx_queues;
u8 max_queues;
- void __iomem *reg_base;
+
+ u8 node;
+ u8 cpi_alg;
+ u16 mtu;
bool link_up;
u8 duplex;
u32 speed;
- struct page *rb_page;
- u32 rb_page_offset;
- bool rb_alloc_fail;
- bool rb_work_scheduled;
- struct delayed_work rbdr_work;
- struct tasklet_struct rbdr_task;
- struct tasklet_struct qs_err_task;
- struct tasklet_struct cq_task;
- struct nicvf_cq_poll *napi[8];
+ bool tns_mode;
+ bool loopback_supported;
struct nicvf_rss_info rss_info;
- u8 cpi_alg;
+ struct tasklet_struct qs_err_task;
+ struct work_struct reset_task;
+
/* Interrupt coalescing settings */
u32 cq_coalesce_usecs;
-
u32 msg_enable;
+
+ /* Stats */
struct nicvf_hw_stats hw_stats;
struct nicvf_drv_stats drv_stats;
struct bgx_stats bgx_stats;
- struct work_struct reset_task;
/* MSI-X */
bool msix_enabled;
diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c
index 0dd1abf..fa05e34 100644
--- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c
+++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c
@@ -18,6 +18,15 @@
#include "q_struct.h"
#include "nicvf_queues.h"
+static void nicvf_get_page(struct nicvf *nic)
+{
+ if (!nic->rb_pageref || !nic->rb_page)
+ return;
+
+ atomic_add(nic->rb_pageref, &nic->rb_page->_count);
+ nic->rb_pageref = 0;
+}
+
/* Poll a register for a specific value */
static int nicvf_poll_reg(struct nicvf *nic, int qidx,
u64 reg, int bit_pos, int bits, int val)
@@ -81,16 +90,15 @@
int order = (PAGE_SIZE <= 4096) ? PAGE_ALLOC_COSTLY_ORDER : 0;
/* Check if request can be accomodated in previous allocated page */
- if (nic->rb_page) {
- if ((nic->rb_page_offset + buf_len + buf_len) >
- (PAGE_SIZE << order)) {
- nic->rb_page = NULL;
- } else {
- nic->rb_page_offset += buf_len;
- get_page(nic->rb_page);
- }
+ if (nic->rb_page &&
+ ((nic->rb_page_offset + buf_len) < (PAGE_SIZE << order))) {
+ nic->rb_pageref++;
+ goto ret;
}
+ nicvf_get_page(nic);
+ nic->rb_page = NULL;
+
/* Allocate a new page */
if (!nic->rb_page) {
nic->rb_page = alloc_pages(gfp | __GFP_COMP | __GFP_NOWARN,
@@ -102,7 +110,9 @@
nic->rb_page_offset = 0;
}
+ret:
*rbuf = (u64 *)((u64)page_address(nic->rb_page) + nic->rb_page_offset);
+ nic->rb_page_offset += buf_len;
return 0;
}
@@ -158,6 +168,9 @@
desc = GET_RBDR_DESC(rbdr, idx);
desc->buf_addr = virt_to_phys(rbuf) >> NICVF_RCV_BUF_ALIGN;
}
+
+ nicvf_get_page(nic);
+
return 0;
}
@@ -241,6 +254,8 @@
new_rb++;
}
+ nicvf_get_page(nic);
+
/* make sure all memory stores are done before ringing doorbell */
smp_wmb();
diff --git a/drivers/net/ethernet/cavium/thunder/thunder_bgx.c b/drivers/net/ethernet/cavium/thunder/thunder_bgx.c
index f8abdff..feed231 100644
--- a/drivers/net/ethernet/cavium/thunder/thunder_bgx.c
+++ b/drivers/net/ethernet/cavium/thunder/thunder_bgx.c
@@ -978,27 +978,37 @@
const char *mac;
device_for_each_child_node(&bgx->pdev->dev, fwn) {
+ struct phy_device *pd;
struct device_node *phy_np;
struct device_node *node = to_of_node(fwn);
- /* If it is not an OF node we cannot handle it yet, so
- * exit the loop.
+ /* Should always be an OF node. But if it is not, we
+ * cannot handle it, so exit the loop.
*/
if (!node)
break;
- phy_np = of_parse_phandle(node, "phy-handle", 0);
- if (!phy_np)
- continue;
-
- bgx->lmac[lmac].phydev = of_phy_find_device(phy_np);
-
mac = of_get_mac_address(node);
if (mac)
ether_addr_copy(bgx->lmac[lmac].mac, mac);
SET_NETDEV_DEV(&bgx->lmac[lmac].netdev, &bgx->pdev->dev);
bgx->lmac[lmac].lmacid = lmac;
+
+ phy_np = of_parse_phandle(node, "phy-handle", 0);
+ /* If there is no phy or defective firmware presents
+ * this cortina phy, for which there is no driver
+ * support, ignore it.
+ */
+ if (phy_np &&
+ !of_device_is_compatible(phy_np, "cortina,cs4223-slice")) {
+ /* Wait until the phy drivers are available */
+ pd = of_phy_find_device(phy_np);
+ if (!pd)
+ return -EPROBE_DEFER;
+ bgx->lmac[lmac].phydev = pd;
+ }
+
lmac++;
if (lmac == MAX_LMAC_PER_BGX) {
of_node_put(node);
@@ -1032,9 +1042,6 @@
struct bgx *bgx = NULL;
u8 lmac;
- /* Load octeon mdio driver */
- octeon_mdiobus_force_mod_depencency();
-
bgx = devm_kzalloc(dev, sizeof(*bgx), GFP_KERNEL);
if (!bgx)
return -ENOMEM;
diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c
index 91857b8..1cc8a7a 100644
--- a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c
@@ -790,10 +790,6 @@
/*
* Note that this interface is up and start everything up ...
*/
- netif_set_real_num_tx_queues(dev, pi->nqsets);
- err = netif_set_real_num_rx_queues(dev, pi->nqsets);
- if (err)
- goto err_unwind;
err = link_start(dev);
if (err)
goto err_unwind;
@@ -2176,6 +2172,73 @@
/* nothing to do */
}
+/* Figure out how many Ports and Queue Sets we can support. This depends on
+ * knowing our Virtual Function Resources and may be called a second time if
+ * we fall back from MSI-X to MSI Interrupt Mode.
+ */
+static void size_nports_qsets(struct adapter *adapter)
+{
+ struct vf_resources *vfres = &adapter->params.vfres;
+ unsigned int ethqsets, pmask_nports;
+
+ /* The number of "ports" which we support is equal to the number of
+ * Virtual Interfaces with which we've been provisioned.
+ */
+ adapter->params.nports = vfres->nvi;
+ if (adapter->params.nports > MAX_NPORTS) {
+ dev_warn(adapter->pdev_dev, "only using %d of %d maximum"
+ " allowed virtual interfaces\n", MAX_NPORTS,
+ adapter->params.nports);
+ adapter->params.nports = MAX_NPORTS;
+ }
+
+ /* We may have been provisioned with more VIs than the number of
+ * ports we're allowed to access (our Port Access Rights Mask).
+ * This is obviously a configuration conflict but we don't want to
+ * crash the kernel or anything silly just because of that.
+ */
+ pmask_nports = hweight32(adapter->params.vfres.pmask);
+ if (pmask_nports < adapter->params.nports) {
+ dev_warn(adapter->pdev_dev, "only using %d of %d provissioned"
+ " virtual interfaces; limited by Port Access Rights"
+ " mask %#x\n", pmask_nports, adapter->params.nports,
+ adapter->params.vfres.pmask);
+ adapter->params.nports = pmask_nports;
+ }
+
+ /* We need to reserve an Ingress Queue for the Asynchronous Firmware
+ * Event Queue. And if we're using MSI Interrupts, we'll also need to
+ * reserve an Ingress Queue for a Forwarded Interrupts.
+ *
+ * The rest of the FL/Intr-capable ingress queues will be matched up
+ * one-for-one with Ethernet/Control egress queues in order to form
+ * "Queue Sets" which will be aportioned between the "ports". For
+ * each Queue Set, we'll need the ability to allocate two Egress
+ * Contexts -- one for the Ingress Queue Free List and one for the TX
+ * Ethernet Queue.
+ *
+ * Note that even if we're currently configured to use MSI-X
+ * Interrupts (module variable msi == MSI_MSIX) we may get downgraded
+ * to MSI Interrupts if we can't get enough MSI-X Interrupts. If that
+ * happens we'll need to adjust things later.
+ */
+ ethqsets = vfres->niqflint - 1 - (msi == MSI_MSI);
+ if (vfres->nethctrl != ethqsets)
+ ethqsets = min(vfres->nethctrl, ethqsets);
+ if (vfres->neq < ethqsets*2)
+ ethqsets = vfres->neq/2;
+ if (ethqsets > MAX_ETH_QSETS)
+ ethqsets = MAX_ETH_QSETS;
+ adapter->sge.max_ethqsets = ethqsets;
+
+ if (adapter->sge.max_ethqsets < adapter->params.nports) {
+ dev_warn(adapter->pdev_dev, "only using %d of %d available"
+ " virtual interfaces (too few Queue Sets)\n",
+ adapter->sge.max_ethqsets, adapter->params.nports);
+ adapter->params.nports = adapter->sge.max_ethqsets;
+ }
+}
+
/*
* Perform early "adapter" initialization. This is where we discover what
* adapter parameters we're going to be using and initialize basic adapter
@@ -2183,10 +2246,8 @@
*/
static int adap_init0(struct adapter *adapter)
{
- struct vf_resources *vfres = &adapter->params.vfres;
struct sge_params *sge_params = &adapter->params.sge;
struct sge *s = &adapter->sge;
- unsigned int ethqsets;
int err;
u32 param, val = 0;
@@ -2295,69 +2356,23 @@
return err;
}
- /*
- * The number of "ports" which we support is equal to the number of
- * Virtual Interfaces with which we've been provisioned.
- */
- adapter->params.nports = vfres->nvi;
- if (adapter->params.nports > MAX_NPORTS) {
- dev_warn(adapter->pdev_dev, "only using %d of %d allowed"
- " virtual interfaces\n", MAX_NPORTS,
- adapter->params.nports);
- adapter->params.nports = MAX_NPORTS;
+ /* Check for various parameter sanity issues */
+ if (adapter->params.vfres.pmask == 0) {
+ dev_err(adapter->pdev_dev, "no port access configured\n"
+ "usable!\n");
+ return -EINVAL;
}
-
- /*
- * We need to reserve a number of the ingress queues with Free List
- * and Interrupt capabilities for special interrupt purposes (like
- * asynchronous firmware messages, or forwarded interrupts if we're
- * using MSI). The rest of the FL/Intr-capable ingress queues will be
- * matched up one-for-one with Ethernet/Control egress queues in order
- * to form "Queue Sets" which will be aportioned between the "ports".
- * For each Queue Set, we'll need the ability to allocate two Egress
- * Contexts -- one for the Ingress Queue Free List and one for the TX
- * Ethernet Queue.
- */
- ethqsets = vfres->niqflint - INGQ_EXTRAS;
- if (vfres->nethctrl != ethqsets) {
- dev_warn(adapter->pdev_dev, "unequal number of [available]"
- " ingress/egress queues (%d/%d); using minimum for"
- " number of Queue Sets\n", ethqsets, vfres->nethctrl);
- ethqsets = min(vfres->nethctrl, ethqsets);
- }
- if (vfres->neq < ethqsets*2) {
- dev_warn(adapter->pdev_dev, "Not enough Egress Contexts (%d)"
- " to support Queue Sets (%d); reducing allowed Queue"
- " Sets\n", vfres->neq, ethqsets);
- ethqsets = vfres->neq/2;
- }
- if (ethqsets > MAX_ETH_QSETS) {
- dev_warn(adapter->pdev_dev, "only using %d of %d allowed Queue"
- " Sets\n", MAX_ETH_QSETS, adapter->sge.max_ethqsets);
- ethqsets = MAX_ETH_QSETS;
- }
- if (vfres->niq != 0 || vfres->neq > ethqsets*2) {
- dev_warn(adapter->pdev_dev, "unused resources niq/neq (%d/%d)"
- " ignored\n", vfres->niq, vfres->neq - ethqsets*2);
- }
- adapter->sge.max_ethqsets = ethqsets;
-
- /*
- * Check for various parameter sanity issues. Most checks simply
- * result in us using fewer resources than our provissioning but we
- * do need at least one "port" with which to work ...
- */
- if (adapter->sge.max_ethqsets < adapter->params.nports) {
- dev_warn(adapter->pdev_dev, "only using %d of %d available"
- " virtual interfaces (too few Queue Sets)\n",
- adapter->sge.max_ethqsets, adapter->params.nports);
- adapter->params.nports = adapter->sge.max_ethqsets;
- }
- if (adapter->params.nports == 0) {
+ if (adapter->params.vfres.nvi == 0) {
dev_err(adapter->pdev_dev, "no virtual interfaces configured/"
"usable!\n");
return -EINVAL;
}
+
+ /* Initialize nports and max_ethqsets now that we have our Virtual
+ * Function Resources.
+ */
+ size_nports_qsets(adapter);
+
return 0;
}
@@ -2771,6 +2786,40 @@
}
}
+ /* See what interrupts we'll be using. If we've been configured to
+ * use MSI-X interrupts, try to enable them but fall back to using
+ * MSI interrupts if we can't enable MSI-X interrupts. If we can't
+ * get MSI interrupts we bail with the error.
+ */
+ if (msi == MSI_MSIX && enable_msix(adapter) == 0)
+ adapter->flags |= USING_MSIX;
+ else {
+ if (msi == MSI_MSIX) {
+ dev_info(adapter->pdev_dev,
+ "Unable to use MSI-X Interrupts; falling "
+ "back to MSI Interrupts\n");
+
+ /* We're going to need a Forwarded Interrupt Queue so
+ * that may cut into how many Queue Sets we can
+ * support.
+ */
+ msi = MSI_MSI;
+ size_nports_qsets(adapter);
+ }
+ err = pci_enable_msi(pdev);
+ if (err) {
+ dev_err(&pdev->dev, "Unable to allocate MSI Interrupts;"
+ " err=%d\n", err);
+ goto err_free_dev;
+ }
+ adapter->flags |= USING_MSI;
+ }
+
+ /* Now that we know how many "ports" we have and what interrupt
+ * mechanism we're going to use, we can configure our queue resources.
+ */
+ cfg_queues(adapter);
+
/*
* The "card" is now ready to go. If any errors occur during device
* registration we do not fail the whole "card" but rather proceed
@@ -2778,10 +2827,14 @@
* must register at least one net device.
*/
for_each_port(adapter, pidx) {
+ struct port_info *pi = netdev_priv(adapter->port[pidx]);
netdev = adapter->port[pidx];
if (netdev == NULL)
continue;
+ netif_set_real_num_tx_queues(netdev, pi->nqsets);
+ netif_set_real_num_rx_queues(netdev, pi->nqsets);
+
err = register_netdev(netdev);
if (err) {
dev_warn(&pdev->dev, "cannot register net device %s,"
@@ -2793,7 +2846,7 @@
}
if (adapter->registered_device_map == 0) {
dev_err(&pdev->dev, "could not register any net devices\n");
- goto err_free_dev;
+ goto err_disable_interrupts;
}
/*
@@ -2811,32 +2864,6 @@
}
/*
- * See what interrupts we'll be using. If we've been configured to
- * use MSI-X interrupts, try to enable them but fall back to using
- * MSI interrupts if we can't enable MSI-X interrupts. If we can't
- * get MSI interrupts we bail with the error.
- */
- if (msi == MSI_MSIX && enable_msix(adapter) == 0)
- adapter->flags |= USING_MSIX;
- else {
- err = pci_enable_msi(pdev);
- if (err) {
- dev_err(&pdev->dev, "Unable to allocate %s interrupts;"
- " err=%d\n",
- msi == MSI_MSIX ? "MSI-X or MSI" : "MSI", err);
- goto err_free_debugfs;
- }
- adapter->flags |= USING_MSI;
- }
-
- /*
- * Now that we know how many "ports" we have and what their types are,
- * and how many Queue Sets we can support, we can configure our queue
- * resources.
- */
- cfg_queues(adapter);
-
- /*
* Print a short notice on the existence and configuration of the new
* VF network device ...
*/
@@ -2856,11 +2883,13 @@
* Error recovery and exit code. Unwind state that's been created
* so far and return the error.
*/
-
-err_free_debugfs:
- if (!IS_ERR_OR_NULL(adapter->debugfs_root)) {
- cleanup_debugfs(adapter);
- debugfs_remove_recursive(adapter->debugfs_root);
+err_disable_interrupts:
+ if (adapter->flags & USING_MSIX) {
+ pci_disable_msix(adapter->pdev);
+ adapter->flags &= ~USING_MSIX;
+ } else if (adapter->flags & USING_MSI) {
+ pci_disable_msi(adapter->pdev);
+ adapter->flags &= ~USING_MSI;
}
err_free_dev:
diff --git a/drivers/net/ethernet/cisco/enic/enic.h b/drivers/net/ethernet/cisco/enic/enic.h
index 7ba6d53..130f910 100644
--- a/drivers/net/ethernet/cisco/enic/enic.h
+++ b/drivers/net/ethernet/cisco/enic/enic.h
@@ -201,16 +201,20 @@
}
/* wrappers function for kernel log
- * Make sure variable vdev of struct vnic_dev is available in the block where
- * these macros are used
*/
-#define vdev_info(args...) dev_info(&vdev->pdev->dev, args)
-#define vdev_warn(args...) dev_warn(&vdev->pdev->dev, args)
-#define vdev_err(args...) dev_err(&vdev->pdev->dev, args)
+#define vdev_err(vdev, fmt, ...) \
+ dev_err(&(vdev)->pdev->dev, fmt, ##__VA_ARGS__)
+#define vdev_warn(vdev, fmt, ...) \
+ dev_warn(&(vdev)->pdev->dev, fmt, ##__VA_ARGS__)
+#define vdev_info(vdev, fmt, ...) \
+ dev_info(&(vdev)->pdev->dev, fmt, ##__VA_ARGS__)
-#define vdev_netinfo(args...) netdev_info(vnic_get_netdev(vdev), args)
-#define vdev_netwarn(args...) netdev_warn(vnic_get_netdev(vdev), args)
-#define vdev_neterr(args...) netdev_err(vnic_get_netdev(vdev), args)
+#define vdev_neterr(vdev, fmt, ...) \
+ netdev_err(vnic_get_netdev(vdev), fmt, ##__VA_ARGS__)
+#define vdev_netwarn(vdev, fmt, ...) \
+ netdev_warn(vnic_get_netdev(vdev), fmt, ##__VA_ARGS__)
+#define vdev_netinfo(vdev, fmt, ...) \
+ netdev_info(vnic_get_netdev(vdev), fmt, ##__VA_ARGS__)
static inline struct device *enic_get_dev(struct enic *enic)
{
diff --git a/drivers/net/ethernet/cisco/enic/vnic_cq.c b/drivers/net/ethernet/cisco/enic/vnic_cq.c
index abeda2a..9c682af 100644
--- a/drivers/net/ethernet/cisco/enic/vnic_cq.c
+++ b/drivers/net/ethernet/cisco/enic/vnic_cq.c
@@ -43,7 +43,7 @@
cq->ctrl = vnic_dev_get_res(vdev, RES_TYPE_CQ, index);
if (!cq->ctrl) {
- vdev_err("Failed to hook CQ[%d] resource\n", index);
+ vdev_err(vdev, "Failed to hook CQ[%d] resource\n", index);
return -EINVAL;
}
diff --git a/drivers/net/ethernet/cisco/enic/vnic_dev.c b/drivers/net/ethernet/cisco/enic/vnic_dev.c
index 1fdf5fe..8f27df3 100644
--- a/drivers/net/ethernet/cisco/enic/vnic_dev.c
+++ b/drivers/net/ethernet/cisco/enic/vnic_dev.c
@@ -53,14 +53,14 @@
return -EINVAL;
if (bar->len < VNIC_MAX_RES_HDR_SIZE) {
- vdev_err("vNIC BAR0 res hdr length error\n");
+ vdev_err(vdev, "vNIC BAR0 res hdr length error\n");
return -EINVAL;
}
rh = bar->vaddr;
mrh = bar->vaddr;
if (!rh) {
- vdev_err("vNIC BAR0 res hdr not mem-mapped\n");
+ vdev_err(vdev, "vNIC BAR0 res hdr not mem-mapped\n");
return -EINVAL;
}
@@ -69,7 +69,7 @@
(ioread32(&rh->version) != VNIC_RES_VERSION)) {
if ((ioread32(&mrh->magic) != MGMTVNIC_MAGIC) ||
(ioread32(&mrh->version) != MGMTVNIC_VERSION)) {
- vdev_err("vNIC BAR0 res magic/version error exp (%lx/%lx) or (%lx/%lx), curr (%x/%x)\n",
+ vdev_err(vdev, "vNIC BAR0 res magic/version error exp (%lx/%lx) or (%lx/%lx), curr (%x/%x)\n",
VNIC_RES_MAGIC, VNIC_RES_VERSION,
MGMTVNIC_MAGIC, MGMTVNIC_VERSION,
ioread32(&rh->magic), ioread32(&rh->version));
@@ -106,7 +106,7 @@
/* each count is stride bytes long */
len = count * VNIC_RES_STRIDE;
if (len + bar_offset > bar[bar_num].len) {
- vdev_err("vNIC BAR0 resource %d out-of-bounds, offset 0x%x + size 0x%x > bar len 0x%lx\n",
+ vdev_err(vdev, "vNIC BAR0 resource %d out-of-bounds, offset 0x%x + size 0x%x > bar len 0x%lx\n",
type, bar_offset, len,
bar[bar_num].len);
return -EINVAL;
@@ -198,7 +198,7 @@
&ring->base_addr_unaligned);
if (!ring->descs_unaligned) {
- vdev_err("Failed to allocate ring (size=%d), aborting\n",
+ vdev_err(vdev, "Failed to allocate ring (size=%d), aborting\n",
(int)ring->size);
return -ENOMEM;
}
@@ -241,7 +241,7 @@
return -ENODEV;
}
if (status & STAT_BUSY) {
- vdev_neterr("Busy devcmd %d\n", _CMD_N(cmd));
+ vdev_neterr(vdev, "Busy devcmd %d\n", _CMD_N(cmd));
return -EBUSY;
}
@@ -275,7 +275,7 @@
return -err;
if (err != ERR_ECMDUNKNOWN ||
cmd != CMD_CAPABILITY)
- vdev_neterr("Error %d devcmd %d\n",
+ vdev_neterr(vdev, "Error %d devcmd %d\n",
err, _CMD_N(cmd));
return -err;
}
@@ -290,7 +290,7 @@
}
}
- vdev_neterr("Timedout devcmd %d\n", _CMD_N(cmd));
+ vdev_neterr(vdev, "Timedout devcmd %d\n", _CMD_N(cmd));
return -ETIMEDOUT;
}
@@ -313,7 +313,7 @@
new_posted = (posted + 1) % DEVCMD2_RING_SIZE;
if (new_posted == fetch_index) {
- vdev_neterr("devcmd2 %d: wq is full. fetch index: %u, posted index: %u\n",
+ vdev_neterr(vdev, "devcmd2 %d: wq is full. fetch index: %u, posted index: %u\n",
_CMD_N(cmd), fetch_index, posted);
return -EBUSY;
}
@@ -352,7 +352,7 @@
err = result->error;
if (err != ERR_ECMDUNKNOWN ||
cmd != CMD_CAPABILITY)
- vdev_neterr("Error %d devcmd %d\n",
+ vdev_neterr(vdev, "Error %d devcmd %d\n",
err, _CMD_N(cmd));
return -err;
}
@@ -365,7 +365,7 @@
udelay(100);
}
- vdev_neterr("devcmd %d timed out\n", _CMD_N(cmd));
+ vdev_neterr(vdev, "devcmd %d timed out\n", _CMD_N(cmd));
return -ETIMEDOUT;
}
@@ -401,7 +401,7 @@
fetch_index = ioread32(&vdev->devcmd2->wq.ctrl->fetch_index);
if (fetch_index == 0xFFFFFFFF) { /* check for hardware gone */
- vdev_err("Fatal error in devcmd2 init - hardware surprise removal");
+ vdev_err(vdev, "Fatal error in devcmd2 init - hardware surprise removal\n");
return -ENODEV;
}
@@ -474,8 +474,8 @@
err = (int)vdev->args[1];
if (err != ERR_ECMDUNKNOWN ||
cmd != CMD_CAPABILITY)
- vdev_neterr("Error %d proxy devcmd %d\n", err,
- _CMD_N(cmd));
+ vdev_neterr(vdev, "Error %d proxy devcmd %d\n",
+ err, _CMD_N(cmd));
return err;
}
@@ -768,7 +768,7 @@
err = vnic_dev_cmd(vdev, CMD_PACKET_FILTER, &a0, &a1, wait);
if (err)
- vdev_neterr("Can't set packet filter\n");
+ vdev_neterr(vdev, "Can't set packet filter\n");
return err;
}
@@ -785,7 +785,7 @@
err = vnic_dev_cmd(vdev, CMD_ADDR_ADD, &a0, &a1, wait);
if (err)
- vdev_neterr("Can't add addr [%pM], %d\n", addr, err);
+ vdev_neterr(vdev, "Can't add addr [%pM], %d\n", addr, err);
return err;
}
@@ -802,7 +802,7 @@
err = vnic_dev_cmd(vdev, CMD_ADDR_DEL, &a0, &a1, wait);
if (err)
- vdev_neterr("Can't del addr [%pM], %d\n", addr, err);
+ vdev_neterr(vdev, "Can't del addr [%pM], %d\n", addr, err);
return err;
}
@@ -846,7 +846,8 @@
dma_addr_t notify_pa;
if (vdev->notify || vdev->notify_pa) {
- vdev_neterr("notify block %p still allocated", vdev->notify);
+ vdev_neterr(vdev, "notify block %p still allocated\n",
+ vdev->notify);
return -EINVAL;
}
@@ -965,7 +966,7 @@
*/
if ((err == ERR_ECMDUNKNOWN) ||
(!err && !(vdev->args[0] && vdev->args[1] && vdev->args[2]))) {
- vdev_netwarn("Using default conversion factor for interrupt coalesce timer\n");
+ vdev_netwarn(vdev, "Using default conversion factor for interrupt coalesce timer\n");
vnic_dev_intr_coal_timer_info_default(vdev);
return 0;
}
@@ -1103,16 +1104,16 @@
if (res) {
err = vnic_dev_init_devcmd2(vdev);
if (err)
- vdev_warn("DEVCMD2 init failed: %d, Using DEVCMD1",
+ vdev_warn(vdev, "DEVCMD2 init failed: %d, Using DEVCMD1\n",
err);
else
return 0;
} else {
- vdev_warn("DEVCMD2 resource not found (old firmware?) Using DEVCMD1\n");
+ vdev_warn(vdev, "DEVCMD2 resource not found (old firmware?) Using DEVCMD1\n");
}
err = vnic_dev_init_devcmd1(vdev);
if (err)
- vdev_err("DEVCMD1 initialization failed: %d", err);
+ vdev_err(vdev, "DEVCMD1 initialization failed: %d\n", err);
return err;
}
diff --git a/drivers/net/ethernet/cisco/enic/vnic_intr.c b/drivers/net/ethernet/cisco/enic/vnic_intr.c
index 942759d..23604e3 100644
--- a/drivers/net/ethernet/cisco/enic/vnic_intr.c
+++ b/drivers/net/ethernet/cisco/enic/vnic_intr.c
@@ -40,7 +40,8 @@
intr->ctrl = vnic_dev_get_res(vdev, RES_TYPE_INTR_CTRL, index);
if (!intr->ctrl) {
- vdev_err("Failed to hook INTR[%d].ctrl resource\n", index);
+ vdev_err(vdev, "Failed to hook INTR[%d].ctrl resource\n",
+ index);
return -EINVAL;
}
diff --git a/drivers/net/ethernet/cisco/enic/vnic_rq.c b/drivers/net/ethernet/cisco/enic/vnic_rq.c
index cce2777..e572a52 100644
--- a/drivers/net/ethernet/cisco/enic/vnic_rq.c
+++ b/drivers/net/ethernet/cisco/enic/vnic_rq.c
@@ -92,7 +92,7 @@
rq->ctrl = vnic_dev_get_res(vdev, RES_TYPE_RQ, index);
if (!rq->ctrl) {
- vdev_err("Failed to hook RQ[%d] resource\n", index);
+ vdev_err(vdev, "Failed to hook RQ[%d] resource\n", index);
return -EINVAL;
}
@@ -179,7 +179,7 @@
udelay(10);
}
- vdev_neterr("Failed to disable RQ[%d]\n", rq->index);
+ vdev_neterr(vdev, "Failed to disable RQ[%d]\n", rq->index);
return -ETIMEDOUT;
}
diff --git a/drivers/net/ethernet/cisco/enic/vnic_wq.c b/drivers/net/ethernet/cisco/enic/vnic_wq.c
index 05ad16a..090cc65 100644
--- a/drivers/net/ethernet/cisco/enic/vnic_wq.c
+++ b/drivers/net/ethernet/cisco/enic/vnic_wq.c
@@ -95,7 +95,7 @@
wq->ctrl = vnic_dev_get_res(vdev, RES_TYPE_WQ, index);
if (!wq->ctrl) {
- vdev_err("Failed to hook WQ[%d] resource\n", index);
+ vdev_err(vdev, "Failed to hook WQ[%d] resource\n", index);
return -EINVAL;
}
@@ -187,7 +187,7 @@
udelay(10);
}
- vdev_neterr("Failed to disable WQ[%d]\n", wq->index);
+ vdev_neterr(vdev, "Failed to disable WQ[%d]\n", wq->index);
return -ETIMEDOUT;
}
diff --git a/drivers/net/ethernet/freescale/fman/fman_dtsec.c b/drivers/net/ethernet/freescale/fman/fman_dtsec.c
index 7c92eb8..c88918c 100644
--- a/drivers/net/ethernet/freescale/fman/fman_dtsec.c
+++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.c
@@ -932,15 +932,14 @@
if (!is_init_done(dtsec->dtsec_drv_param))
return -EINVAL;
- /* FM_BAD_TX_TS_IN_B_2_B_ERRATA_DTSEC_A003 Errata workaround */
- if (dtsec->fm_rev_info.major == 2)
- if (pause_time <= 320) {
+ if (pause_time) {
+ /* FM_BAD_TX_TS_IN_B_2_B_ERRATA_DTSEC_A003 Errata workaround */
+ if (dtsec->fm_rev_info.major == 2 && pause_time <= 320) {
pr_warn("pause-time: %d illegal.Should be > 320\n",
pause_time);
return -EINVAL;
}
- if (pause_time) {
ptv = ioread32be(®s->ptv);
ptv &= PTV_PTE_MASK;
ptv |= pause_time & PTV_PT_MASK;
diff --git a/drivers/net/ethernet/marvell/Kconfig b/drivers/net/ethernet/marvell/Kconfig
index a1c862b..62d80fd 100644
--- a/drivers/net/ethernet/marvell/Kconfig
+++ b/drivers/net/ethernet/marvell/Kconfig
@@ -40,6 +40,20 @@
This driver is used by the MV643XX_ETH and MVNETA drivers.
+config MVNETA_BM
+ tristate "Marvell Armada 38x/XP network interface BM support"
+ depends on MVNETA
+ select HWBM
+ ---help---
+ This driver supports auxiliary block of the network
+ interface units in the Marvell ARMADA XP and ARMADA 38x SoC
+ family, which is called buffer manager.
+
+ This driver, when enabled, strictly cooperates with mvneta
+ driver and is common for all network ports of the devices,
+ even for Armada 370 SoC, which doesn't support hardware
+ buffer management.
+
config MVNETA
tristate "Marvell Armada 370/38x/XP network interface support"
depends on PLAT_ORION
diff --git a/drivers/net/ethernet/marvell/Makefile b/drivers/net/ethernet/marvell/Makefile
index f6425bd..ff1bffa 100644
--- a/drivers/net/ethernet/marvell/Makefile
+++ b/drivers/net/ethernet/marvell/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_MVMDIO) += mvmdio.o
obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o
+obj-$(CONFIG_MVNETA_BM) += mvneta_bm.o
obj-$(CONFIG_MVNETA) += mvneta.o
obj-$(CONFIG_MVPP2) += mvpp2.o
obj-$(CONFIG_PXA168_ETH) += pxa168_eth.o
diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
index b0ae69f..be92668 100644
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -30,6 +30,8 @@
#include <linux/phy.h>
#include <linux/platform_device.h>
#include <linux/skbuff.h>
+#include <net/hwbm.h>
+#include "mvneta_bm.h"
#include <net/ip.h>
#include <net/ipv6.h>
#include <net/tso.h>
@@ -37,6 +39,10 @@
/* Registers */
#define MVNETA_RXQ_CONFIG_REG(q) (0x1400 + ((q) << 2))
#define MVNETA_RXQ_HW_BUF_ALLOC BIT(0)
+#define MVNETA_RXQ_SHORT_POOL_ID_SHIFT 4
+#define MVNETA_RXQ_SHORT_POOL_ID_MASK 0x30
+#define MVNETA_RXQ_LONG_POOL_ID_SHIFT 6
+#define MVNETA_RXQ_LONG_POOL_ID_MASK 0xc0
#define MVNETA_RXQ_PKT_OFFSET_ALL_MASK (0xf << 8)
#define MVNETA_RXQ_PKT_OFFSET_MASK(offs) ((offs) << 8)
#define MVNETA_RXQ_THRESHOLD_REG(q) (0x14c0 + ((q) << 2))
@@ -50,6 +56,9 @@
#define MVNETA_RXQ_STATUS_UPDATE_REG(q) (0x1500 + ((q) << 2))
#define MVNETA_RXQ_ADD_NON_OCCUPIED_SHIFT 16
#define MVNETA_RXQ_ADD_NON_OCCUPIED_MAX 255
+#define MVNETA_PORT_POOL_BUFFER_SZ_REG(pool) (0x1700 + ((pool) << 2))
+#define MVNETA_PORT_POOL_BUFFER_SZ_SHIFT 3
+#define MVNETA_PORT_POOL_BUFFER_SZ_MASK 0xfff8
#define MVNETA_PORT_RX_RESET 0x1cc0
#define MVNETA_PORT_RX_DMA_RESET BIT(0)
#define MVNETA_PHY_ADDR 0x2000
@@ -107,6 +116,7 @@
#define MVNETA_GMAC_CLOCK_DIVIDER 0x24f4
#define MVNETA_GMAC_1MS_CLOCK_ENABLE BIT(31)
#define MVNETA_ACC_MODE 0x2500
+#define MVNETA_BM_ADDRESS 0x2504
#define MVNETA_CPU_MAP(cpu) (0x2540 + ((cpu) << 2))
#define MVNETA_CPU_RXQ_ACCESS_ALL_MASK 0x000000ff
#define MVNETA_CPU_TXQ_ACCESS_ALL_MASK 0x0000ff00
@@ -253,7 +263,10 @@
#define MVNETA_CPU_D_CACHE_LINE_SIZE 32
#define MVNETA_TX_CSUM_DEF_SIZE 1600
#define MVNETA_TX_CSUM_MAX_SIZE 9800
-#define MVNETA_ACC_MODE_EXT 1
+#define MVNETA_ACC_MODE_EXT1 1
+#define MVNETA_ACC_MODE_EXT2 2
+
+#define MVNETA_MAX_DECODE_WIN 6
/* Timeout constants */
#define MVNETA_TX_DISABLE_TIMEOUT_MSEC 1000
@@ -293,7 +306,8 @@
((addr >= txq->tso_hdrs_phys) && \
(addr < txq->tso_hdrs_phys + txq->size * TSO_HEADER_SIZE))
-#define MVNETA_RX_BUF_SIZE(pkt_size) ((pkt_size) + NET_SKB_PAD)
+#define MVNETA_RX_GET_BM_POOL_ID(rxd) \
+ (((rxd)->status & MVNETA_RXD_BM_POOL_MASK) >> MVNETA_RXD_BM_POOL_SHIFT)
struct mvneta_statistic {
unsigned short offset;
@@ -359,6 +373,7 @@
};
struct mvneta_port {
+ u8 id;
struct mvneta_pcpu_port __percpu *ports;
struct mvneta_pcpu_stats __percpu *stats;
@@ -394,6 +409,11 @@
unsigned int tx_csum_limit;
unsigned int use_inband_status:1;
+ struct mvneta_bm *bm_priv;
+ struct mvneta_bm_pool *pool_long;
+ struct mvneta_bm_pool *pool_short;
+ int bm_win_id;
+
u64 ethtool_stats[ARRAY_SIZE(mvneta_statistics)];
u32 indir[MVNETA_RSS_LU_TABLE_SIZE];
@@ -419,6 +439,8 @@
#define MVNETA_TX_L4_CSUM_NOT BIT(31)
#define MVNETA_RXD_ERR_CRC 0x0
+#define MVNETA_RXD_BM_POOL_SHIFT 13
+#define MVNETA_RXD_BM_POOL_MASK (BIT(13) | BIT(14))
#define MVNETA_RXD_ERR_SUMMARY BIT(16)
#define MVNETA_RXD_ERR_OVERRUN BIT(17)
#define MVNETA_RXD_ERR_LEN BIT(18)
@@ -563,6 +585,9 @@
static int rx_copybreak __read_mostly = 256;
+/* HW BM need that each port be identify by a unique ID */
+static int global_port_id;
+
#define MVNETA_DRIVER_NAME "mvneta"
#define MVNETA_DRIVER_VERSION "1.0"
@@ -829,6 +854,215 @@
mvreg_write(pp, MVNETA_RXQ_CONFIG_REG(rxq->id), val);
}
+/* Enable buffer management (BM) */
+static void mvneta_rxq_bm_enable(struct mvneta_port *pp,
+ struct mvneta_rx_queue *rxq)
+{
+ u32 val;
+
+ val = mvreg_read(pp, MVNETA_RXQ_CONFIG_REG(rxq->id));
+ val |= MVNETA_RXQ_HW_BUF_ALLOC;
+ mvreg_write(pp, MVNETA_RXQ_CONFIG_REG(rxq->id), val);
+}
+
+/* Notify HW about port's assignment of pool for bigger packets */
+static void mvneta_rxq_long_pool_set(struct mvneta_port *pp,
+ struct mvneta_rx_queue *rxq)
+{
+ u32 val;
+
+ val = mvreg_read(pp, MVNETA_RXQ_CONFIG_REG(rxq->id));
+ val &= ~MVNETA_RXQ_LONG_POOL_ID_MASK;
+ val |= (pp->pool_long->id << MVNETA_RXQ_LONG_POOL_ID_SHIFT);
+
+ mvreg_write(pp, MVNETA_RXQ_CONFIG_REG(rxq->id), val);
+}
+
+/* Notify HW about port's assignment of pool for smaller packets */
+static void mvneta_rxq_short_pool_set(struct mvneta_port *pp,
+ struct mvneta_rx_queue *rxq)
+{
+ u32 val;
+
+ val = mvreg_read(pp, MVNETA_RXQ_CONFIG_REG(rxq->id));
+ val &= ~MVNETA_RXQ_SHORT_POOL_ID_MASK;
+ val |= (pp->pool_short->id << MVNETA_RXQ_SHORT_POOL_ID_SHIFT);
+
+ mvreg_write(pp, MVNETA_RXQ_CONFIG_REG(rxq->id), val);
+}
+
+/* Set port's receive buffer size for assigned BM pool */
+static inline void mvneta_bm_pool_bufsize_set(struct mvneta_port *pp,
+ int buf_size,
+ u8 pool_id)
+{
+ u32 val;
+
+ if (!IS_ALIGNED(buf_size, 8)) {
+ dev_warn(pp->dev->dev.parent,
+ "illegal buf_size value %d, round to %d\n",
+ buf_size, ALIGN(buf_size, 8));
+ buf_size = ALIGN(buf_size, 8);
+ }
+
+ val = mvreg_read(pp, MVNETA_PORT_POOL_BUFFER_SZ_REG(pool_id));
+ val |= buf_size & MVNETA_PORT_POOL_BUFFER_SZ_MASK;
+ mvreg_write(pp, MVNETA_PORT_POOL_BUFFER_SZ_REG(pool_id), val);
+}
+
+/* Configure MBUS window in order to enable access BM internal SRAM */
+static int mvneta_mbus_io_win_set(struct mvneta_port *pp, u32 base, u32 wsize,
+ u8 target, u8 attr)
+{
+ u32 win_enable, win_protect;
+ int i;
+
+ win_enable = mvreg_read(pp, MVNETA_BASE_ADDR_ENABLE);
+
+ if (pp->bm_win_id < 0) {
+ /* Find first not occupied window */
+ for (i = 0; i < MVNETA_MAX_DECODE_WIN; i++) {
+ if (win_enable & (1 << i)) {
+ pp->bm_win_id = i;
+ break;
+ }
+ }
+ if (i == MVNETA_MAX_DECODE_WIN)
+ return -ENOMEM;
+ } else {
+ i = pp->bm_win_id;
+ }
+
+ mvreg_write(pp, MVNETA_WIN_BASE(i), 0);
+ mvreg_write(pp, MVNETA_WIN_SIZE(i), 0);
+
+ if (i < 4)
+ mvreg_write(pp, MVNETA_WIN_REMAP(i), 0);
+
+ mvreg_write(pp, MVNETA_WIN_BASE(i), (base & 0xffff0000) |
+ (attr << 8) | target);
+
+ mvreg_write(pp, MVNETA_WIN_SIZE(i), (wsize - 1) & 0xffff0000);
+
+ win_protect = mvreg_read(pp, MVNETA_ACCESS_PROTECT_ENABLE);
+ win_protect |= 3 << (2 * i);
+ mvreg_write(pp, MVNETA_ACCESS_PROTECT_ENABLE, win_protect);
+
+ win_enable &= ~(1 << i);
+ mvreg_write(pp, MVNETA_BASE_ADDR_ENABLE, win_enable);
+
+ return 0;
+}
+
+/* Assign and initialize pools for port. In case of fail
+ * buffer manager will remain disabled for current port.
+ */
+static int mvneta_bm_port_init(struct platform_device *pdev,
+ struct mvneta_port *pp)
+{
+ struct device_node *dn = pdev->dev.of_node;
+ u32 long_pool_id, short_pool_id, wsize;
+ u8 target, attr;
+ int err;
+
+ /* Get BM window information */
+ err = mvebu_mbus_get_io_win_info(pp->bm_priv->bppi_phys_addr, &wsize,
+ &target, &attr);
+ if (err < 0)
+ return err;
+
+ pp->bm_win_id = -1;
+
+ /* Open NETA -> BM window */
+ err = mvneta_mbus_io_win_set(pp, pp->bm_priv->bppi_phys_addr, wsize,
+ target, attr);
+ if (err < 0) {
+ netdev_info(pp->dev, "fail to configure mbus window to BM\n");
+ return err;
+ }
+
+ if (of_property_read_u32(dn, "bm,pool-long", &long_pool_id)) {
+ netdev_info(pp->dev, "missing long pool id\n");
+ return -EINVAL;
+ }
+
+ /* Create port's long pool depending on mtu */
+ pp->pool_long = mvneta_bm_pool_use(pp->bm_priv, long_pool_id,
+ MVNETA_BM_LONG, pp->id,
+ MVNETA_RX_PKT_SIZE(pp->dev->mtu));
+ if (!pp->pool_long) {
+ netdev_info(pp->dev, "fail to obtain long pool for port\n");
+ return -ENOMEM;
+ }
+
+ pp->pool_long->port_map |= 1 << pp->id;
+
+ mvneta_bm_pool_bufsize_set(pp, pp->pool_long->buf_size,
+ pp->pool_long->id);
+
+ /* If short pool id is not defined, assume using single pool */
+ if (of_property_read_u32(dn, "bm,pool-short", &short_pool_id))
+ short_pool_id = long_pool_id;
+
+ /* Create port's short pool */
+ pp->pool_short = mvneta_bm_pool_use(pp->bm_priv, short_pool_id,
+ MVNETA_BM_SHORT, pp->id,
+ MVNETA_BM_SHORT_PKT_SIZE);
+ if (!pp->pool_short) {
+ netdev_info(pp->dev, "fail to obtain short pool for port\n");
+ mvneta_bm_pool_destroy(pp->bm_priv, pp->pool_long, 1 << pp->id);
+ return -ENOMEM;
+ }
+
+ if (short_pool_id != long_pool_id) {
+ pp->pool_short->port_map |= 1 << pp->id;
+ mvneta_bm_pool_bufsize_set(pp, pp->pool_short->buf_size,
+ pp->pool_short->id);
+ }
+
+ return 0;
+}
+
+/* Update settings of a pool for bigger packets */
+static void mvneta_bm_update_mtu(struct mvneta_port *pp, int mtu)
+{
+ struct mvneta_bm_pool *bm_pool = pp->pool_long;
+ struct hwbm_pool *hwbm_pool = &bm_pool->hwbm_pool;
+ int num;
+
+ /* Release all buffers from long pool */
+ mvneta_bm_bufs_free(pp->bm_priv, bm_pool, 1 << pp->id);
+ if (hwbm_pool->buf_num) {
+ WARN(1, "cannot free all buffers in pool %d\n",
+ bm_pool->id);
+ goto bm_mtu_err;
+ }
+
+ bm_pool->pkt_size = MVNETA_RX_PKT_SIZE(mtu);
+ bm_pool->buf_size = MVNETA_RX_BUF_SIZE(bm_pool->pkt_size);
+ hwbm_pool->frag_size = SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) +
+ SKB_DATA_ALIGN(MVNETA_RX_BUF_SIZE(bm_pool->pkt_size));
+
+ /* Fill entire long pool */
+ num = hwbm_pool_add(hwbm_pool, hwbm_pool->size, GFP_ATOMIC);
+ if (num != hwbm_pool->size) {
+ WARN(1, "pool %d: %d of %d allocated\n",
+ bm_pool->id, num, hwbm_pool->size);
+ goto bm_mtu_err;
+ }
+ mvneta_bm_pool_bufsize_set(pp, bm_pool->buf_size, bm_pool->id);
+
+ return;
+
+bm_mtu_err:
+ mvneta_bm_pool_destroy(pp->bm_priv, pp->pool_long, 1 << pp->id);
+ mvneta_bm_pool_destroy(pp->bm_priv, pp->pool_short, 1 << pp->id);
+
+ pp->bm_priv = NULL;
+ mvreg_write(pp, MVNETA_ACC_MODE, MVNETA_ACC_MODE_EXT1);
+ netdev_info(pp->dev, "fail to update MTU, fall back to software BM\n");
+}
+
/* Start the Ethernet port RX and TX activity */
static void mvneta_port_up(struct mvneta_port *pp)
{
@@ -1149,9 +1383,17 @@
mvreg_write(pp, MVNETA_PORT_RX_RESET, 0);
/* Set Port Acceleration Mode */
- val = MVNETA_ACC_MODE_EXT;
+ if (pp->bm_priv)
+ /* HW buffer management + legacy parser */
+ val = MVNETA_ACC_MODE_EXT2;
+ else
+ /* SW buffer management + legacy parser */
+ val = MVNETA_ACC_MODE_EXT1;
mvreg_write(pp, MVNETA_ACC_MODE, val);
+ if (pp->bm_priv)
+ mvreg_write(pp, MVNETA_BM_ADDRESS, pp->bm_priv->bppi_phys_addr);
+
/* Update val of portCfg register accordingly with all RxQueue types */
val = MVNETA_PORT_CONFIG_DEFL_VALUE(pp->rxq_def);
mvreg_write(pp, MVNETA_PORT_CONFIG, val);
@@ -1518,23 +1760,25 @@
}
}
-static void *mvneta_frag_alloc(const struct mvneta_port *pp)
+void *mvneta_frag_alloc(unsigned int frag_size)
{
- if (likely(pp->frag_size <= PAGE_SIZE))
- return netdev_alloc_frag(pp->frag_size);
+ if (likely(frag_size <= PAGE_SIZE))
+ return netdev_alloc_frag(frag_size);
else
- return kmalloc(pp->frag_size, GFP_ATOMIC);
+ return kmalloc(frag_size, GFP_ATOMIC);
}
+EXPORT_SYMBOL_GPL(mvneta_frag_alloc);
-static void mvneta_frag_free(const struct mvneta_port *pp, void *data)
+void mvneta_frag_free(unsigned int frag_size, void *data)
{
- if (likely(pp->frag_size <= PAGE_SIZE))
+ if (likely(frag_size <= PAGE_SIZE))
skb_free_frag(data);
else
kfree(data);
}
+EXPORT_SYMBOL_GPL(mvneta_frag_free);
-/* Refill processing */
+/* Refill processing for SW buffer management */
static int mvneta_rx_refill(struct mvneta_port *pp,
struct mvneta_rx_desc *rx_desc)
@@ -1542,7 +1786,7 @@
dma_addr_t phys_addr;
void *data;
- data = mvneta_frag_alloc(pp);
+ data = mvneta_frag_alloc(pp->frag_size);
if (!data)
return -ENOMEM;
@@ -1550,7 +1794,7 @@
MVNETA_RX_BUF_SIZE(pp->pkt_size),
DMA_FROM_DEVICE);
if (unlikely(dma_mapping_error(pp->dev->dev.parent, phys_addr))) {
- mvneta_frag_free(pp, data);
+ mvneta_frag_free(pp->frag_size, data);
return -ENOMEM;
}
@@ -1596,22 +1840,37 @@
int rx_done, i;
rx_done = mvneta_rxq_busy_desc_num_get(pp, rxq);
+ if (rx_done)
+ mvneta_rxq_desc_num_update(pp, rxq, rx_done, rx_done);
+
+ if (pp->bm_priv) {
+ for (i = 0; i < rx_done; i++) {
+ struct mvneta_rx_desc *rx_desc =
+ mvneta_rxq_next_desc_get(rxq);
+ u8 pool_id = MVNETA_RX_GET_BM_POOL_ID(rx_desc);
+ struct mvneta_bm_pool *bm_pool;
+
+ bm_pool = &pp->bm_priv->bm_pools[pool_id];
+ /* Return dropped buffer to the pool */
+ mvneta_bm_pool_put_bp(pp->bm_priv, bm_pool,
+ rx_desc->buf_phys_addr);
+ }
+ return;
+ }
+
for (i = 0; i < rxq->size; i++) {
struct mvneta_rx_desc *rx_desc = rxq->descs + i;
void *data = (void *)rx_desc->buf_cookie;
dma_unmap_single(pp->dev->dev.parent, rx_desc->buf_phys_addr,
MVNETA_RX_BUF_SIZE(pp->pkt_size), DMA_FROM_DEVICE);
- mvneta_frag_free(pp, data);
+ mvneta_frag_free(pp->frag_size, data);
}
-
- if (rx_done)
- mvneta_rxq_desc_num_update(pp, rxq, rx_done, rx_done);
}
-/* Main rx processing */
-static int mvneta_rx(struct mvneta_port *pp, int rx_todo,
- struct mvneta_rx_queue *rxq)
+/* Main rx processing when using software buffer management */
+static int mvneta_rx_swbm(struct mvneta_port *pp, int rx_todo,
+ struct mvneta_rx_queue *rxq)
{
struct mvneta_pcpu_port *port = this_cpu_ptr(pp->ports);
struct net_device *dev = pp->dev;
@@ -1633,7 +1892,7 @@
struct sk_buff *skb;
unsigned char *data;
dma_addr_t phys_addr;
- u32 rx_status;
+ u32 rx_status, frag_size;
int rx_bytes, err;
rx_done++;
@@ -1644,7 +1903,7 @@
if (!mvneta_rxq_desc_is_first_last(rx_status) ||
(rx_status & MVNETA_RXD_ERR_SUMMARY)) {
- err_drop_frame:
+err_drop_frame:
dev->stats.rx_errors++;
mvneta_rx_error(pp, rx_desc);
/* leave the descriptor untouched */
@@ -1652,16 +1911,16 @@
}
if (rx_bytes <= rx_copybreak) {
- /* better copy a small frame and not unmap the DMA region */
+ /* better copy a small frame and not unmap the DMA region */
skb = netdev_alloc_skb_ip_align(dev, rx_bytes);
if (unlikely(!skb))
goto err_drop_frame;
dma_sync_single_range_for_cpu(dev->dev.parent,
- rx_desc->buf_phys_addr,
- MVNETA_MH_SIZE + NET_SKB_PAD,
- rx_bytes,
- DMA_FROM_DEVICE);
+ rx_desc->buf_phys_addr,
+ MVNETA_MH_SIZE + NET_SKB_PAD,
+ rx_bytes,
+ DMA_FROM_DEVICE);
memcpy(skb_put(skb, rx_bytes),
data + MVNETA_MH_SIZE + NET_SKB_PAD,
rx_bytes);
@@ -1685,13 +1944,16 @@
goto err_drop_frame;
}
- skb = build_skb(data, pp->frag_size > PAGE_SIZE ? 0 : pp->frag_size);
+ frag_size = pp->frag_size;
+
+ skb = build_skb(data, frag_size > PAGE_SIZE ? 0 : frag_size);
/* After refill old buffer has to be unmapped regardless
* the skb is successfully built or not.
*/
dma_unmap_single(dev->dev.parent, phys_addr,
- MVNETA_RX_BUF_SIZE(pp->pkt_size), DMA_FROM_DEVICE);
+ MVNETA_RX_BUF_SIZE(pp->pkt_size),
+ DMA_FROM_DEVICE);
if (!skb)
goto err_drop_frame;
@@ -1725,6 +1987,135 @@
return rx_done;
}
+/* Main rx processing when using hardware buffer management */
+static int mvneta_rx_hwbm(struct mvneta_port *pp, int rx_todo,
+ struct mvneta_rx_queue *rxq)
+{
+ struct mvneta_pcpu_port *port = this_cpu_ptr(pp->ports);
+ struct net_device *dev = pp->dev;
+ int rx_done;
+ u32 rcvd_pkts = 0;
+ u32 rcvd_bytes = 0;
+
+ /* Get number of received packets */
+ rx_done = mvneta_rxq_busy_desc_num_get(pp, rxq);
+
+ if (rx_todo > rx_done)
+ rx_todo = rx_done;
+
+ rx_done = 0;
+
+ /* Fairness NAPI loop */
+ while (rx_done < rx_todo) {
+ struct mvneta_rx_desc *rx_desc = mvneta_rxq_next_desc_get(rxq);
+ struct mvneta_bm_pool *bm_pool = NULL;
+ struct sk_buff *skb;
+ unsigned char *data;
+ dma_addr_t phys_addr;
+ u32 rx_status, frag_size;
+ int rx_bytes, err;
+ u8 pool_id;
+
+ rx_done++;
+ rx_status = rx_desc->status;
+ rx_bytes = rx_desc->data_size - (ETH_FCS_LEN + MVNETA_MH_SIZE);
+ data = (unsigned char *)rx_desc->buf_cookie;
+ phys_addr = rx_desc->buf_phys_addr;
+ pool_id = MVNETA_RX_GET_BM_POOL_ID(rx_desc);
+ bm_pool = &pp->bm_priv->bm_pools[pool_id];
+
+ if (!mvneta_rxq_desc_is_first_last(rx_status) ||
+ (rx_status & MVNETA_RXD_ERR_SUMMARY)) {
+err_drop_frame_ret_pool:
+ /* Return the buffer to the pool */
+ mvneta_bm_pool_put_bp(pp->bm_priv, bm_pool,
+ rx_desc->buf_phys_addr);
+err_drop_frame:
+ dev->stats.rx_errors++;
+ mvneta_rx_error(pp, rx_desc);
+ /* leave the descriptor untouched */
+ continue;
+ }
+
+ if (rx_bytes <= rx_copybreak) {
+ /* better copy a small frame and not unmap the DMA region */
+ skb = netdev_alloc_skb_ip_align(dev, rx_bytes);
+ if (unlikely(!skb))
+ goto err_drop_frame_ret_pool;
+
+ dma_sync_single_range_for_cpu(dev->dev.parent,
+ rx_desc->buf_phys_addr,
+ MVNETA_MH_SIZE + NET_SKB_PAD,
+ rx_bytes,
+ DMA_FROM_DEVICE);
+ memcpy(skb_put(skb, rx_bytes),
+ data + MVNETA_MH_SIZE + NET_SKB_PAD,
+ rx_bytes);
+
+ skb->protocol = eth_type_trans(skb, dev);
+ mvneta_rx_csum(pp, rx_status, skb);
+ napi_gro_receive(&port->napi, skb);
+
+ rcvd_pkts++;
+ rcvd_bytes += rx_bytes;
+
+ /* Return the buffer to the pool */
+ mvneta_bm_pool_put_bp(pp->bm_priv, bm_pool,
+ rx_desc->buf_phys_addr);
+
+ /* leave the descriptor and buffer untouched */
+ continue;
+ }
+
+ /* Refill processing */
+ err = hwbm_pool_refill(&bm_pool->hwbm_pool, GFP_ATOMIC);
+ if (err) {
+ netdev_err(dev, "Linux processing - Can't refill\n");
+ rxq->missed++;
+ goto err_drop_frame_ret_pool;
+ }
+
+ frag_size = bm_pool->hwbm_pool.frag_size;
+
+ skb = build_skb(data, frag_size > PAGE_SIZE ? 0 : frag_size);
+
+ /* After refill old buffer has to be unmapped regardless
+ * the skb is successfully built or not.
+ */
+ dma_unmap_single(&pp->bm_priv->pdev->dev, phys_addr,
+ bm_pool->buf_size, DMA_FROM_DEVICE);
+ if (!skb)
+ goto err_drop_frame;
+
+ rcvd_pkts++;
+ rcvd_bytes += rx_bytes;
+
+ /* Linux processing */
+ skb_reserve(skb, MVNETA_MH_SIZE + NET_SKB_PAD);
+ skb_put(skb, rx_bytes);
+
+ skb->protocol = eth_type_trans(skb, dev);
+
+ mvneta_rx_csum(pp, rx_status, skb);
+
+ napi_gro_receive(&port->napi, skb);
+ }
+
+ if (rcvd_pkts) {
+ struct mvneta_pcpu_stats *stats = this_cpu_ptr(pp->stats);
+
+ u64_stats_update_begin(&stats->syncp);
+ stats->rx_packets += rcvd_pkts;
+ stats->rx_bytes += rcvd_bytes;
+ u64_stats_update_end(&stats->syncp);
+ }
+
+ /* Update rxq management counters */
+ mvneta_rxq_desc_num_update(pp, rxq, rx_done, rx_done);
+
+ return rx_done;
+}
+
static inline void
mvneta_tso_put_hdr(struct sk_buff *skb,
struct mvneta_port *pp, struct mvneta_tx_queue *txq)
@@ -2297,7 +2688,10 @@
if (rx_queue) {
rx_queue = rx_queue - 1;
- rx_done = mvneta_rx(pp, budget, &pp->rxqs[rx_queue]);
+ if (pp->bm_priv)
+ rx_done = mvneta_rx_hwbm(pp, budget, &pp->rxqs[rx_queue]);
+ else
+ rx_done = mvneta_rx_swbm(pp, budget, &pp->rxqs[rx_queue]);
}
budget -= rx_done;
@@ -2386,9 +2780,17 @@
mvneta_rx_pkts_coal_set(pp, rxq, rxq->pkts_coal);
mvneta_rx_time_coal_set(pp, rxq, rxq->time_coal);
- /* Fill RXQ with buffers from RX pool */
- mvneta_rxq_buf_size_set(pp, rxq, MVNETA_RX_BUF_SIZE(pp->pkt_size));
- mvneta_rxq_bm_disable(pp, rxq);
+ if (!pp->bm_priv) {
+ /* Fill RXQ with buffers from RX pool */
+ mvneta_rxq_buf_size_set(pp, rxq,
+ MVNETA_RX_BUF_SIZE(pp->pkt_size));
+ mvneta_rxq_bm_disable(pp, rxq);
+ } else {
+ mvneta_rxq_bm_enable(pp, rxq);
+ mvneta_rxq_long_pool_set(pp, rxq);
+ mvneta_rxq_short_pool_set(pp, rxq);
+ }
+
mvneta_rxq_fill(pp, rxq, rxq->size);
return 0;
@@ -2661,6 +3063,9 @@
dev->mtu = mtu;
if (!netif_running(dev)) {
+ if (pp->bm_priv)
+ mvneta_bm_update_mtu(pp, mtu);
+
netdev_update_features(dev);
return 0;
}
@@ -2673,6 +3078,9 @@
mvneta_cleanup_txqs(pp);
mvneta_cleanup_rxqs(pp);
+ if (pp->bm_priv)
+ mvneta_bm_update_mtu(pp, mtu);
+
pp->pkt_size = MVNETA_RX_PKT_SIZE(dev->mtu);
pp->frag_size = SKB_DATA_ALIGN(MVNETA_RX_BUF_SIZE(pp->pkt_size)) +
SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
@@ -2920,6 +3328,8 @@
switch (action) {
case CPU_ONLINE:
case CPU_ONLINE_FROZEN:
+ case CPU_DOWN_FAILED:
+ case CPU_DOWN_FAILED_FROZEN:
spin_lock(&pp->lock);
/* Configuring the driver for a new CPU while the
* driver is stopping is racy, so just avoid it.
@@ -3557,6 +3967,7 @@
struct resource *res;
struct device_node *dn = pdev->dev.of_node;
struct device_node *phy_node;
+ struct device_node *bm_node;
struct mvneta_port *pp;
struct net_device *dev;
const char *dt_mac_addr;
@@ -3690,26 +4101,39 @@
pp->tx_csum_limit = tx_csum_limit;
+ dram_target_info = mv_mbus_dram_info();
+ if (dram_target_info)
+ mvneta_conf_mbus_windows(pp, dram_target_info);
+
pp->tx_ring_size = MVNETA_MAX_TXD;
pp->rx_ring_size = MVNETA_MAX_RXD;
pp->dev = dev;
SET_NETDEV_DEV(dev, &pdev->dev);
+ pp->id = global_port_id++;
+
+ /* Obtain access to BM resources if enabled and already initialized */
+ bm_node = of_parse_phandle(dn, "buffer-manager", 0);
+ if (bm_node && bm_node->data) {
+ pp->bm_priv = bm_node->data;
+ err = mvneta_bm_port_init(pdev, pp);
+ if (err < 0) {
+ dev_info(&pdev->dev, "use SW buffer management\n");
+ pp->bm_priv = NULL;
+ }
+ }
+
err = mvneta_init(&pdev->dev, pp);
if (err < 0)
- goto err_free_stats;
+ goto err_netdev;
err = mvneta_port_power_up(pp, phy_mode);
if (err < 0) {
dev_err(&pdev->dev, "can't power up port\n");
- goto err_free_stats;
+ goto err_netdev;
}
- dram_target_info = mv_mbus_dram_info();
- if (dram_target_info)
- mvneta_conf_mbus_windows(pp, dram_target_info);
-
for_each_present_cpu(cpu) {
struct mvneta_pcpu_port *port = per_cpu_ptr(pp->ports, cpu);
@@ -3744,6 +4168,13 @@
return 0;
+err_netdev:
+ unregister_netdev(dev);
+ if (pp->bm_priv) {
+ mvneta_bm_pool_destroy(pp->bm_priv, pp->pool_long, 1 << pp->id);
+ mvneta_bm_pool_destroy(pp->bm_priv, pp->pool_short,
+ 1 << pp->id);
+ }
err_free_stats:
free_percpu(pp->stats);
err_free_ports:
@@ -3775,6 +4206,12 @@
of_node_put(pp->phy_node);
free_netdev(dev);
+ if (pp->bm_priv) {
+ mvneta_bm_pool_destroy(pp->bm_priv, pp->pool_long, 1 << pp->id);
+ mvneta_bm_pool_destroy(pp->bm_priv, pp->pool_short,
+ 1 << pp->id);
+ }
+
return 0;
}
diff --git a/drivers/net/ethernet/marvell/mvneta_bm.c b/drivers/net/ethernet/marvell/mvneta_bm.c
new file mode 100644
index 0000000..01fccec
--- /dev/null
+++ b/drivers/net/ethernet/marvell/mvneta_bm.c
@@ -0,0 +1,487 @@
+/*
+ * Driver for Marvell NETA network controller Buffer Manager.
+ *
+ * Copyright (C) 2015 Marvell
+ *
+ * Marcin Wojtas <mw@semihalf.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/genalloc.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mbus.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/skbuff.h>
+#include <net/hwbm.h>
+#include "mvneta_bm.h"
+
+#define MVNETA_BM_DRIVER_NAME "mvneta_bm"
+#define MVNETA_BM_DRIVER_VERSION "1.0"
+
+static void mvneta_bm_write(struct mvneta_bm *priv, u32 offset, u32 data)
+{
+ writel(data, priv->reg_base + offset);
+}
+
+static u32 mvneta_bm_read(struct mvneta_bm *priv, u32 offset)
+{
+ return readl(priv->reg_base + offset);
+}
+
+static void mvneta_bm_pool_enable(struct mvneta_bm *priv, int pool_id)
+{
+ u32 val;
+
+ val = mvneta_bm_read(priv, MVNETA_BM_POOL_BASE_REG(pool_id));
+ val |= MVNETA_BM_POOL_ENABLE_MASK;
+ mvneta_bm_write(priv, MVNETA_BM_POOL_BASE_REG(pool_id), val);
+
+ /* Clear BM cause register */
+ mvneta_bm_write(priv, MVNETA_BM_INTR_CAUSE_REG, 0);
+}
+
+static void mvneta_bm_pool_disable(struct mvneta_bm *priv, int pool_id)
+{
+ u32 val;
+
+ val = mvneta_bm_read(priv, MVNETA_BM_POOL_BASE_REG(pool_id));
+ val &= ~MVNETA_BM_POOL_ENABLE_MASK;
+ mvneta_bm_write(priv, MVNETA_BM_POOL_BASE_REG(pool_id), val);
+}
+
+static inline void mvneta_bm_config_set(struct mvneta_bm *priv, u32 mask)
+{
+ u32 val;
+
+ val = mvneta_bm_read(priv, MVNETA_BM_CONFIG_REG);
+ val |= mask;
+ mvneta_bm_write(priv, MVNETA_BM_CONFIG_REG, val);
+}
+
+static inline void mvneta_bm_config_clear(struct mvneta_bm *priv, u32 mask)
+{
+ u32 val;
+
+ val = mvneta_bm_read(priv, MVNETA_BM_CONFIG_REG);
+ val &= ~mask;
+ mvneta_bm_write(priv, MVNETA_BM_CONFIG_REG, val);
+}
+
+static void mvneta_bm_pool_target_set(struct mvneta_bm *priv, int pool_id,
+ u8 target_id, u8 attr)
+{
+ u32 val;
+
+ val = mvneta_bm_read(priv, MVNETA_BM_XBAR_POOL_REG(pool_id));
+ val &= ~MVNETA_BM_TARGET_ID_MASK(pool_id);
+ val &= ~MVNETA_BM_XBAR_ATTR_MASK(pool_id);
+ val |= MVNETA_BM_TARGET_ID_VAL(pool_id, target_id);
+ val |= MVNETA_BM_XBAR_ATTR_VAL(pool_id, attr);
+
+ mvneta_bm_write(priv, MVNETA_BM_XBAR_POOL_REG(pool_id), val);
+}
+
+int mvneta_bm_construct(struct hwbm_pool *hwbm_pool, void *buf)
+{
+ struct mvneta_bm_pool *bm_pool =
+ (struct mvneta_bm_pool *)hwbm_pool->priv;
+ struct mvneta_bm *priv = bm_pool->priv;
+ dma_addr_t phys_addr;
+
+ /* In order to update buf_cookie field of RX descriptor properly,
+ * BM hardware expects buf virtual address to be placed in the
+ * first four bytes of mapped buffer.
+ */
+ *(u32 *)buf = (u32)buf;
+ phys_addr = dma_map_single(&priv->pdev->dev, buf, bm_pool->buf_size,
+ DMA_FROM_DEVICE);
+ if (unlikely(dma_mapping_error(&priv->pdev->dev, phys_addr)))
+ return -ENOMEM;
+
+ mvneta_bm_pool_put_bp(priv, bm_pool, phys_addr);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mvneta_bm_construct);
+
+/* Create pool */
+static int mvneta_bm_pool_create(struct mvneta_bm *priv,
+ struct mvneta_bm_pool *bm_pool)
+{
+ struct platform_device *pdev = priv->pdev;
+ u8 target_id, attr;
+ int size_bytes, err;
+ size_bytes = sizeof(u32) * bm_pool->hwbm_pool.size;
+ bm_pool->virt_addr = dma_alloc_coherent(&pdev->dev, size_bytes,
+ &bm_pool->phys_addr,
+ GFP_KERNEL);
+ if (!bm_pool->virt_addr)
+ return -ENOMEM;
+
+ if (!IS_ALIGNED((u32)bm_pool->virt_addr, MVNETA_BM_POOL_PTR_ALIGN)) {
+ dma_free_coherent(&pdev->dev, size_bytes, bm_pool->virt_addr,
+ bm_pool->phys_addr);
+ dev_err(&pdev->dev, "BM pool %d is not %d bytes aligned\n",
+ bm_pool->id, MVNETA_BM_POOL_PTR_ALIGN);
+ return -ENOMEM;
+ }
+
+ err = mvebu_mbus_get_dram_win_info(bm_pool->phys_addr, &target_id,
+ &attr);
+ if (err < 0) {
+ dma_free_coherent(&pdev->dev, size_bytes, bm_pool->virt_addr,
+ bm_pool->phys_addr);
+ return err;
+ }
+
+ /* Set pool address */
+ mvneta_bm_write(priv, MVNETA_BM_POOL_BASE_REG(bm_pool->id),
+ bm_pool->phys_addr);
+
+ mvneta_bm_pool_target_set(priv, bm_pool->id, target_id, attr);
+ mvneta_bm_pool_enable(priv, bm_pool->id);
+
+ return 0;
+}
+
+/* Notify the driver that BM pool is being used as specific type and return the
+ * pool pointer on success
+ */
+struct mvneta_bm_pool *mvneta_bm_pool_use(struct mvneta_bm *priv, u8 pool_id,
+ enum mvneta_bm_type type, u8 port_id,
+ int pkt_size)
+{
+ struct mvneta_bm_pool *new_pool = &priv->bm_pools[pool_id];
+ int num, err;
+
+ if (new_pool->type == MVNETA_BM_LONG &&
+ new_pool->port_map != 1 << port_id) {
+ dev_err(&priv->pdev->dev,
+ "long pool cannot be shared by the ports\n");
+ return NULL;
+ }
+
+ if (new_pool->type == MVNETA_BM_SHORT && new_pool->type != type) {
+ dev_err(&priv->pdev->dev,
+ "mixing pools' types between the ports is forbidden\n");
+ return NULL;
+ }
+
+ if (new_pool->pkt_size == 0 || type != MVNETA_BM_SHORT)
+ new_pool->pkt_size = pkt_size;
+
+ /* Allocate buffers in case BM pool hasn't been used yet */
+ if (new_pool->type == MVNETA_BM_FREE) {
+ struct hwbm_pool *hwbm_pool = &new_pool->hwbm_pool;
+
+ new_pool->priv = priv;
+ new_pool->type = type;
+ new_pool->buf_size = MVNETA_RX_BUF_SIZE(new_pool->pkt_size);
+ hwbm_pool->frag_size =
+ SKB_DATA_ALIGN(MVNETA_RX_BUF_SIZE(new_pool->pkt_size)) +
+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+ hwbm_pool->construct = mvneta_bm_construct;
+ hwbm_pool->priv = new_pool;
+
+ /* Create new pool */
+ err = mvneta_bm_pool_create(priv, new_pool);
+ if (err) {
+ dev_err(&priv->pdev->dev, "fail to create pool %d\n",
+ new_pool->id);
+ return NULL;
+ }
+
+ /* Allocate buffers for this pool */
+ num = hwbm_pool_add(hwbm_pool, hwbm_pool->size, GFP_ATOMIC);
+ if (num != hwbm_pool->size) {
+ WARN(1, "pool %d: %d of %d allocated\n",
+ new_pool->id, num, hwbm_pool->size);
+ return NULL;
+ }
+ }
+
+ return new_pool;
+}
+EXPORT_SYMBOL_GPL(mvneta_bm_pool_use);
+
+/* Free all buffers from the pool */
+void mvneta_bm_bufs_free(struct mvneta_bm *priv, struct mvneta_bm_pool *bm_pool,
+ u8 port_map)
+{
+ int i;
+
+ bm_pool->port_map &= ~port_map;
+ if (bm_pool->port_map)
+ return;
+
+ mvneta_bm_config_set(priv, MVNETA_BM_EMPTY_LIMIT_MASK);
+
+ for (i = 0; i < bm_pool->hwbm_pool.buf_num; i++) {
+ dma_addr_t buf_phys_addr;
+ u32 *vaddr;
+
+ /* Get buffer physical address (indirect access) */
+ buf_phys_addr = mvneta_bm_pool_get_bp(priv, bm_pool);
+
+ /* Work-around to the problems when destroying the pool,
+ * when it occurs that a read access to BPPI returns 0.
+ */
+ if (buf_phys_addr == 0)
+ continue;
+
+ vaddr = phys_to_virt(buf_phys_addr);
+ if (!vaddr)
+ break;
+
+ dma_unmap_single(&priv->pdev->dev, buf_phys_addr,
+ bm_pool->buf_size, DMA_FROM_DEVICE);
+ hwbm_buf_free(&bm_pool->hwbm_pool, vaddr);
+ }
+
+ mvneta_bm_config_clear(priv, MVNETA_BM_EMPTY_LIMIT_MASK);
+
+ /* Update BM driver with number of buffers removed from pool */
+ bm_pool->hwbm_pool.buf_num -= i;
+}
+EXPORT_SYMBOL_GPL(mvneta_bm_bufs_free);
+
+/* Cleanup pool */
+void mvneta_bm_pool_destroy(struct mvneta_bm *priv,
+ struct mvneta_bm_pool *bm_pool, u8 port_map)
+{
+ struct hwbm_pool *hwbm_pool = &bm_pool->hwbm_pool;
+ bm_pool->port_map &= ~port_map;
+ if (bm_pool->port_map)
+ return;
+
+ bm_pool->type = MVNETA_BM_FREE;
+
+ mvneta_bm_bufs_free(priv, bm_pool, port_map);
+ if (hwbm_pool->buf_num)
+ WARN(1, "cannot free all buffers in pool %d\n", bm_pool->id);
+
+ if (bm_pool->virt_addr) {
+ dma_free_coherent(&priv->pdev->dev,
+ sizeof(u32) * hwbm_pool->size,
+ bm_pool->virt_addr, bm_pool->phys_addr);
+ bm_pool->virt_addr = NULL;
+ }
+
+ mvneta_bm_pool_disable(priv, bm_pool->id);
+}
+EXPORT_SYMBOL_GPL(mvneta_bm_pool_destroy);
+
+static void mvneta_bm_pools_init(struct mvneta_bm *priv)
+{
+ struct device_node *dn = priv->pdev->dev.of_node;
+ struct mvneta_bm_pool *bm_pool;
+ char prop[15];
+ u32 size;
+ int i;
+
+ /* Activate BM unit */
+ mvneta_bm_write(priv, MVNETA_BM_COMMAND_REG, MVNETA_BM_START_MASK);
+
+ /* Create all pools with maximum size */
+ for (i = 0; i < MVNETA_BM_POOLS_NUM; i++) {
+ bm_pool = &priv->bm_pools[i];
+ bm_pool->id = i;
+ bm_pool->type = MVNETA_BM_FREE;
+
+ /* Reset read pointer */
+ mvneta_bm_write(priv, MVNETA_BM_POOL_READ_PTR_REG(i), 0);
+
+ /* Reset write pointer */
+ mvneta_bm_write(priv, MVNETA_BM_POOL_WRITE_PTR_REG(i), 0);
+
+ /* Configure pool size according to DT or use default value */
+ sprintf(prop, "pool%d,capacity", i);
+ if (of_property_read_u32(dn, prop, &size)) {
+ size = MVNETA_BM_POOL_CAP_DEF;
+ } else if (size > MVNETA_BM_POOL_CAP_MAX) {
+ dev_warn(&priv->pdev->dev,
+ "Illegal pool %d capacity %d, set to %d\n",
+ i, size, MVNETA_BM_POOL_CAP_MAX);
+ size = MVNETA_BM_POOL_CAP_MAX;
+ } else if (size < MVNETA_BM_POOL_CAP_MIN) {
+ dev_warn(&priv->pdev->dev,
+ "Illegal pool %d capacity %d, set to %d\n",
+ i, size, MVNETA_BM_POOL_CAP_MIN);
+ size = MVNETA_BM_POOL_CAP_MIN;
+ } else if (!IS_ALIGNED(size, MVNETA_BM_POOL_CAP_ALIGN)) {
+ dev_warn(&priv->pdev->dev,
+ "Illegal pool %d capacity %d, round to %d\n",
+ i, size, ALIGN(size,
+ MVNETA_BM_POOL_CAP_ALIGN));
+ size = ALIGN(size, MVNETA_BM_POOL_CAP_ALIGN);
+ }
+ bm_pool->hwbm_pool.size = size;
+
+ mvneta_bm_write(priv, MVNETA_BM_POOL_SIZE_REG(i),
+ bm_pool->hwbm_pool.size);
+
+ /* Obtain custom pkt_size from DT */
+ sprintf(prop, "pool%d,pkt-size", i);
+ if (of_property_read_u32(dn, prop, &bm_pool->pkt_size))
+ bm_pool->pkt_size = 0;
+ }
+}
+
+static void mvneta_bm_default_set(struct mvneta_bm *priv)
+{
+ u32 val;
+
+ /* Mask BM all interrupts */
+ mvneta_bm_write(priv, MVNETA_BM_INTR_MASK_REG, 0);
+
+ /* Clear BM cause register */
+ mvneta_bm_write(priv, MVNETA_BM_INTR_CAUSE_REG, 0);
+
+ /* Set BM configuration register */
+ val = mvneta_bm_read(priv, MVNETA_BM_CONFIG_REG);
+
+ /* Reduce MaxInBurstSize from 32 BPs to 16 BPs */
+ val &= ~MVNETA_BM_MAX_IN_BURST_SIZE_MASK;
+ val |= MVNETA_BM_MAX_IN_BURST_SIZE_16BP;
+ mvneta_bm_write(priv, MVNETA_BM_CONFIG_REG, val);
+}
+
+static int mvneta_bm_init(struct mvneta_bm *priv)
+{
+ mvneta_bm_default_set(priv);
+
+ /* Allocate and initialize BM pools structures */
+ priv->bm_pools = devm_kcalloc(&priv->pdev->dev, MVNETA_BM_POOLS_NUM,
+ sizeof(struct mvneta_bm_pool),
+ GFP_KERNEL);
+ if (!priv->bm_pools)
+ return -ENOMEM;
+
+ mvneta_bm_pools_init(priv);
+
+ return 0;
+}
+
+static int mvneta_bm_get_sram(struct device_node *dn,
+ struct mvneta_bm *priv)
+{
+ priv->bppi_pool = of_gen_pool_get(dn, "internal-mem", 0);
+ if (!priv->bppi_pool)
+ return -ENOMEM;
+
+ priv->bppi_virt_addr = gen_pool_dma_alloc(priv->bppi_pool,
+ MVNETA_BM_BPPI_SIZE,
+ &priv->bppi_phys_addr);
+ if (!priv->bppi_virt_addr)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void mvneta_bm_put_sram(struct mvneta_bm *priv)
+{
+ gen_pool_free(priv->bppi_pool, priv->bppi_phys_addr,
+ MVNETA_BM_BPPI_SIZE);
+}
+
+static int mvneta_bm_probe(struct platform_device *pdev)
+{
+ struct device_node *dn = pdev->dev.of_node;
+ struct mvneta_bm *priv;
+ struct resource *res;
+ int err;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(struct mvneta_bm), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->reg_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->reg_base))
+ return PTR_ERR(priv->reg_base);
+
+ priv->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(priv->clk))
+ return PTR_ERR(priv->clk);
+ err = clk_prepare_enable(priv->clk);
+ if (err < 0)
+ return err;
+
+ err = mvneta_bm_get_sram(dn, priv);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to allocate internal memory\n");
+ goto err_clk;
+ }
+
+ priv->pdev = pdev;
+
+ /* Initialize buffer manager internals */
+ err = mvneta_bm_init(priv);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to initialize controller\n");
+ goto err_sram;
+ }
+
+ dn->data = priv;
+ platform_set_drvdata(pdev, priv);
+
+ dev_info(&pdev->dev, "Buffer Manager for network controller enabled\n");
+
+ return 0;
+
+err_sram:
+ mvneta_bm_put_sram(priv);
+err_clk:
+ clk_disable_unprepare(priv->clk);
+ return err;
+}
+
+static int mvneta_bm_remove(struct platform_device *pdev)
+{
+ struct mvneta_bm *priv = platform_get_drvdata(pdev);
+ u8 all_ports_map = 0xff;
+ int i = 0;
+
+ for (i = 0; i < MVNETA_BM_POOLS_NUM; i++) {
+ struct mvneta_bm_pool *bm_pool = &priv->bm_pools[i];
+
+ mvneta_bm_pool_destroy(priv, bm_pool, all_ports_map);
+ }
+
+ mvneta_bm_put_sram(priv);
+
+ /* Dectivate BM unit */
+ mvneta_bm_write(priv, MVNETA_BM_COMMAND_REG, MVNETA_BM_STOP_MASK);
+
+ clk_disable_unprepare(priv->clk);
+
+ return 0;
+}
+
+static const struct of_device_id mvneta_bm_match[] = {
+ { .compatible = "marvell,armada-380-neta-bm" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mvneta_bm_match);
+
+static struct platform_driver mvneta_bm_driver = {
+ .probe = mvneta_bm_probe,
+ .remove = mvneta_bm_remove,
+ .driver = {
+ .name = MVNETA_BM_DRIVER_NAME,
+ .of_match_table = mvneta_bm_match,
+ },
+};
+
+module_platform_driver(mvneta_bm_driver);
+
+MODULE_DESCRIPTION("Marvell NETA Buffer Manager Driver - www.marvell.com");
+MODULE_AUTHOR("Marcin Wojtas <mw@semihalf.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/ethernet/marvell/mvneta_bm.h b/drivers/net/ethernet/marvell/mvneta_bm.h
new file mode 100644
index 0000000..e74fd44
--- /dev/null
+++ b/drivers/net/ethernet/marvell/mvneta_bm.h
@@ -0,0 +1,182 @@
+/*
+ * Driver for Marvell NETA network controller Buffer Manager.
+ *
+ * Copyright (C) 2015 Marvell
+ *
+ * Marcin Wojtas <mw@semihalf.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef _MVNETA_BM_H_
+#define _MVNETA_BM_H_
+
+/* BM Configuration Register */
+#define MVNETA_BM_CONFIG_REG 0x0
+#define MVNETA_BM_STATUS_MASK 0x30
+#define MVNETA_BM_ACTIVE_MASK BIT(4)
+#define MVNETA_BM_MAX_IN_BURST_SIZE_MASK 0x60000
+#define MVNETA_BM_MAX_IN_BURST_SIZE_16BP BIT(18)
+#define MVNETA_BM_EMPTY_LIMIT_MASK BIT(19)
+
+/* BM Activation Register */
+#define MVNETA_BM_COMMAND_REG 0x4
+#define MVNETA_BM_START_MASK BIT(0)
+#define MVNETA_BM_STOP_MASK BIT(1)
+#define MVNETA_BM_PAUSE_MASK BIT(2)
+
+/* BM Xbar interface Register */
+#define MVNETA_BM_XBAR_01_REG 0x8
+#define MVNETA_BM_XBAR_23_REG 0xc
+#define MVNETA_BM_XBAR_POOL_REG(pool) \
+ (((pool) < 2) ? MVNETA_BM_XBAR_01_REG : MVNETA_BM_XBAR_23_REG)
+#define MVNETA_BM_TARGET_ID_OFFS(pool) (((pool) & 1) ? 16 : 0)
+#define MVNETA_BM_TARGET_ID_MASK(pool) \
+ (0xf << MVNETA_BM_TARGET_ID_OFFS(pool))
+#define MVNETA_BM_TARGET_ID_VAL(pool, id) \
+ ((id) << MVNETA_BM_TARGET_ID_OFFS(pool))
+#define MVNETA_BM_XBAR_ATTR_OFFS(pool) (((pool) & 1) ? 20 : 4)
+#define MVNETA_BM_XBAR_ATTR_MASK(pool) \
+ (0xff << MVNETA_BM_XBAR_ATTR_OFFS(pool))
+#define MVNETA_BM_XBAR_ATTR_VAL(pool, attr) \
+ ((attr) << MVNETA_BM_XBAR_ATTR_OFFS(pool))
+
+/* Address of External Buffer Pointers Pool Register */
+#define MVNETA_BM_POOL_BASE_REG(pool) (0x10 + ((pool) << 4))
+#define MVNETA_BM_POOL_ENABLE_MASK BIT(0)
+
+/* External Buffer Pointers Pool RD pointer Register */
+#define MVNETA_BM_POOL_READ_PTR_REG(pool) (0x14 + ((pool) << 4))
+#define MVNETA_BM_POOL_SET_READ_PTR_MASK 0xfffc
+#define MVNETA_BM_POOL_GET_READ_PTR_OFFS 16
+#define MVNETA_BM_POOL_GET_READ_PTR_MASK 0xfffc0000
+
+/* External Buffer Pointers Pool WR pointer */
+#define MVNETA_BM_POOL_WRITE_PTR_REG(pool) (0x18 + ((pool) << 4))
+#define MVNETA_BM_POOL_SET_WRITE_PTR_OFFS 0
+#define MVNETA_BM_POOL_SET_WRITE_PTR_MASK 0xfffc
+#define MVNETA_BM_POOL_GET_WRITE_PTR_OFFS 16
+#define MVNETA_BM_POOL_GET_WRITE_PTR_MASK 0xfffc0000
+
+/* External Buffer Pointers Pool Size Register */
+#define MVNETA_BM_POOL_SIZE_REG(pool) (0x1c + ((pool) << 4))
+#define MVNETA_BM_POOL_SIZE_MASK 0x3fff
+
+/* BM Interrupt Cause Register */
+#define MVNETA_BM_INTR_CAUSE_REG (0x50)
+
+/* BM interrupt Mask Register */
+#define MVNETA_BM_INTR_MASK_REG (0x54)
+
+/* Other definitions */
+#define MVNETA_BM_SHORT_PKT_SIZE 256
+#define MVNETA_BM_POOLS_NUM 4
+#define MVNETA_BM_POOL_CAP_MIN 128
+#define MVNETA_BM_POOL_CAP_DEF 2048
+#define MVNETA_BM_POOL_CAP_MAX \
+ (16 * 1024 - MVNETA_BM_POOL_CAP_ALIGN)
+#define MVNETA_BM_POOL_CAP_ALIGN 32
+#define MVNETA_BM_POOL_PTR_ALIGN 32
+
+#define MVNETA_BM_POOL_ACCESS_OFFS 8
+
+#define MVNETA_BM_BPPI_SIZE 0x100000
+
+#define MVNETA_RX_BUF_SIZE(pkt_size) ((pkt_size) + NET_SKB_PAD)
+
+enum mvneta_bm_type {
+ MVNETA_BM_FREE,
+ MVNETA_BM_LONG,
+ MVNETA_BM_SHORT
+};
+
+struct mvneta_bm {
+ void __iomem *reg_base;
+ struct clk *clk;
+ struct platform_device *pdev;
+
+ struct gen_pool *bppi_pool;
+ /* BPPI virtual base address */
+ void __iomem *bppi_virt_addr;
+ /* BPPI physical base address */
+ dma_addr_t bppi_phys_addr;
+
+ /* BM pools */
+ struct mvneta_bm_pool *bm_pools;
+};
+
+struct mvneta_bm_pool {
+ struct hwbm_pool hwbm_pool;
+ /* Pool number in the range 0-3 */
+ u8 id;
+ enum mvneta_bm_type type;
+
+ /* Packet size */
+ int pkt_size;
+ /* Size of the buffer acces through DMA*/
+ u32 buf_size;
+
+ /* BPPE virtual base address */
+ u32 *virt_addr;
+ /* BPPE physical base address */
+ dma_addr_t phys_addr;
+
+ /* Ports using BM pool */
+ u8 port_map;
+
+ struct mvneta_bm *priv;
+};
+
+/* Declarations and definitions */
+void *mvneta_frag_alloc(unsigned int frag_size);
+void mvneta_frag_free(unsigned int frag_size, void *data);
+
+#if defined(CONFIG_MVNETA_BM) || defined(CONFIG_MVNETA_BM_MODULE)
+void mvneta_bm_pool_destroy(struct mvneta_bm *priv,
+ struct mvneta_bm_pool *bm_pool, u8 port_map);
+void mvneta_bm_bufs_free(struct mvneta_bm *priv, struct mvneta_bm_pool *bm_pool,
+ u8 port_map);
+int mvneta_bm_construct(struct hwbm_pool *hwbm_pool, void *buf);
+int mvneta_bm_pool_refill(struct mvneta_bm *priv,
+ struct mvneta_bm_pool *bm_pool);
+struct mvneta_bm_pool *mvneta_bm_pool_use(struct mvneta_bm *priv, u8 pool_id,
+ enum mvneta_bm_type type, u8 port_id,
+ int pkt_size);
+
+static inline void mvneta_bm_pool_put_bp(struct mvneta_bm *priv,
+ struct mvneta_bm_pool *bm_pool,
+ dma_addr_t buf_phys_addr)
+{
+ writel_relaxed(buf_phys_addr, priv->bppi_virt_addr +
+ (bm_pool->id << MVNETA_BM_POOL_ACCESS_OFFS));
+}
+
+static inline u32 mvneta_bm_pool_get_bp(struct mvneta_bm *priv,
+ struct mvneta_bm_pool *bm_pool)
+{
+ return readl_relaxed(priv->bppi_virt_addr +
+ (bm_pool->id << MVNETA_BM_POOL_ACCESS_OFFS));
+}
+#else
+void mvneta_bm_pool_destroy(struct mvneta_bm *priv,
+ struct mvneta_bm_pool *bm_pool, u8 port_map) {}
+void mvneta_bm_bufs_free(struct mvneta_bm *priv, struct mvneta_bm_pool *bm_pool,
+ u8 port_map) {}
+int mvneta_bm_construct(struct hwbm_pool *hwbm_pool, void *buf) { return 0; }
+int mvneta_bm_pool_refill(struct mvneta_bm *priv,
+ struct mvneta_bm_pool *bm_pool) {return 0; }
+struct mvneta_bm_pool *mvneta_bm_pool_use(struct mvneta_bm *priv, u8 pool_id,
+ enum mvneta_bm_type type, u8 port_id,
+ int pkt_size) { return NULL; }
+
+static inline void mvneta_bm_pool_put_bp(struct mvneta_bm *priv,
+ struct mvneta_bm_pool *bm_pool,
+ dma_addr_t buf_phys_addr) {}
+
+static inline u32 mvneta_bm_pool_get_bp(struct mvneta_bm *priv,
+ struct mvneta_bm_pool *bm_pool)
+{ return 0; }
+#endif /* CONFIG_MVNETA_BM */
+#endif
diff --git a/drivers/net/ethernet/mediatek/Kconfig b/drivers/net/ethernet/mediatek/Kconfig
new file mode 100644
index 0000000..698bb89
--- /dev/null
+++ b/drivers/net/ethernet/mediatek/Kconfig
@@ -0,0 +1,17 @@
+config NET_VENDOR_MEDIATEK
+ bool "MediaTek ethernet driver"
+ depends on ARCH_MEDIATEK
+ ---help---
+ If you have a Mediatek SoC with ethernet, say Y.
+
+if NET_VENDOR_MEDIATEK
+
+config NET_MEDIATEK_SOC
+ tristate "MediaTek MT7623 Gigabit ethernet support"
+ depends on NET_VENDOR_MEDIATEK && (MACH_MT7623 || MACH_MT2701)
+ select PHYLIB
+ ---help---
+ This driver supports the gigabit ethernet MACs in the
+ MediaTek MT2701/MT7623 chipset family.
+
+endif #NET_VENDOR_MEDIATEK
diff --git a/drivers/net/ethernet/mediatek/Makefile b/drivers/net/ethernet/mediatek/Makefile
new file mode 100644
index 0000000..aa3f1c8
--- /dev/null
+++ b/drivers/net/ethernet/mediatek/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the Mediatek SoCs built-in ethernet macs
+#
+
+obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth_soc.o
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
new file mode 100644
index 0000000..1e6c549
--- /dev/null
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -0,0 +1,1806 @@
+/* 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; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Copyright (C) 2009-2016 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
+ */
+
+#include <linux/of_device.h>
+#include <linux/of_mdio.h>
+#include <linux/of_net.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/clk.h>
+#include <linux/if_vlan.h>
+#include <linux/reset.h>
+#include <linux/tcp.h>
+
+#include "mtk_eth_soc.h"
+
+static int mtk_msg_level = -1;
+module_param_named(msg_level, mtk_msg_level, int, 0);
+MODULE_PARM_DESC(msg_level, "Message level (-1=defaults,0=none,...,16=all)");
+
+#define MTK_ETHTOOL_STAT(x) { #x, \
+ offsetof(struct mtk_hw_stats, x) / sizeof(u64) }
+
+/* strings used by ethtool */
+static const struct mtk_ethtool_stats {
+ char str[ETH_GSTRING_LEN];
+ u32 offset;
+} mtk_ethtool_stats[] = {
+ MTK_ETHTOOL_STAT(tx_bytes),
+ MTK_ETHTOOL_STAT(tx_packets),
+ MTK_ETHTOOL_STAT(tx_skip),
+ MTK_ETHTOOL_STAT(tx_collisions),
+ MTK_ETHTOOL_STAT(rx_bytes),
+ MTK_ETHTOOL_STAT(rx_packets),
+ MTK_ETHTOOL_STAT(rx_overflow),
+ MTK_ETHTOOL_STAT(rx_fcs_errors),
+ MTK_ETHTOOL_STAT(rx_short_errors),
+ MTK_ETHTOOL_STAT(rx_long_errors),
+ MTK_ETHTOOL_STAT(rx_checksum_errors),
+ MTK_ETHTOOL_STAT(rx_flow_control_packets),
+};
+
+void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg)
+{
+ __raw_writel(val, eth->base + reg);
+}
+
+u32 mtk_r32(struct mtk_eth *eth, unsigned reg)
+{
+ return __raw_readl(eth->base + reg);
+}
+
+static int mtk_mdio_busy_wait(struct mtk_eth *eth)
+{
+ unsigned long t_start = jiffies;
+
+ while (1) {
+ if (!(mtk_r32(eth, MTK_PHY_IAC) & PHY_IAC_ACCESS))
+ return 0;
+ if (time_after(jiffies, t_start + PHY_IAC_TIMEOUT))
+ break;
+ usleep_range(10, 20);
+ }
+
+ dev_err(eth->dev, "mdio: MDIO timeout\n");
+ return -1;
+}
+
+u32 _mtk_mdio_write(struct mtk_eth *eth, u32 phy_addr,
+ u32 phy_register, u32 write_data)
+{
+ if (mtk_mdio_busy_wait(eth))
+ return -1;
+
+ write_data &= 0xffff;
+
+ mtk_w32(eth, PHY_IAC_ACCESS | PHY_IAC_START | PHY_IAC_WRITE |
+ (phy_register << PHY_IAC_REG_SHIFT) |
+ (phy_addr << PHY_IAC_ADDR_SHIFT) | write_data,
+ MTK_PHY_IAC);
+
+ if (mtk_mdio_busy_wait(eth))
+ return -1;
+
+ return 0;
+}
+
+u32 _mtk_mdio_read(struct mtk_eth *eth, int phy_addr, int phy_reg)
+{
+ u32 d;
+
+ if (mtk_mdio_busy_wait(eth))
+ return 0xffff;
+
+ mtk_w32(eth, PHY_IAC_ACCESS | PHY_IAC_START | PHY_IAC_READ |
+ (phy_reg << PHY_IAC_REG_SHIFT) |
+ (phy_addr << PHY_IAC_ADDR_SHIFT),
+ MTK_PHY_IAC);
+
+ if (mtk_mdio_busy_wait(eth))
+ return 0xffff;
+
+ d = mtk_r32(eth, MTK_PHY_IAC) & 0xffff;
+
+ return d;
+}
+
+static int mtk_mdio_write(struct mii_bus *bus, int phy_addr,
+ int phy_reg, u16 val)
+{
+ struct mtk_eth *eth = bus->priv;
+
+ return _mtk_mdio_write(eth, phy_addr, phy_reg, val);
+}
+
+static int mtk_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg)
+{
+ struct mtk_eth *eth = bus->priv;
+
+ return _mtk_mdio_read(eth, phy_addr, phy_reg);
+}
+
+static void mtk_phy_link_adjust(struct net_device *dev)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ u32 mcr = MAC_MCR_MAX_RX_1536 | MAC_MCR_IPG_CFG |
+ MAC_MCR_FORCE_MODE | MAC_MCR_TX_EN |
+ MAC_MCR_RX_EN | MAC_MCR_BACKOFF_EN |
+ MAC_MCR_BACKPR_EN;
+
+ switch (mac->phy_dev->speed) {
+ case SPEED_1000:
+ mcr |= MAC_MCR_SPEED_1000;
+ break;
+ case SPEED_100:
+ mcr |= MAC_MCR_SPEED_100;
+ break;
+ };
+
+ if (mac->phy_dev->link)
+ mcr |= MAC_MCR_FORCE_LINK;
+
+ if (mac->phy_dev->duplex)
+ mcr |= MAC_MCR_FORCE_DPX;
+
+ if (mac->phy_dev->pause)
+ mcr |= MAC_MCR_FORCE_RX_FC | MAC_MCR_FORCE_TX_FC;
+
+ mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id));
+
+ if (mac->phy_dev->link)
+ netif_carrier_on(dev);
+ else
+ netif_carrier_off(dev);
+}
+
+static int mtk_phy_connect_node(struct mtk_eth *eth, struct mtk_mac *mac,
+ struct device_node *phy_node)
+{
+ const __be32 *_addr = NULL;
+ struct phy_device *phydev;
+ int phy_mode, addr;
+
+ _addr = of_get_property(phy_node, "reg", NULL);
+
+ if (!_addr || (be32_to_cpu(*_addr) >= 0x20)) {
+ pr_err("%s: invalid phy address\n", phy_node->name);
+ return -EINVAL;
+ }
+ addr = be32_to_cpu(*_addr);
+ phy_mode = of_get_phy_mode(phy_node);
+ if (phy_mode < 0) {
+ dev_err(eth->dev, "incorrect phy-mode %d\n", phy_mode);
+ return -EINVAL;
+ }
+
+ phydev = of_phy_connect(eth->netdev[mac->id], phy_node,
+ mtk_phy_link_adjust, 0, phy_mode);
+ if (IS_ERR(phydev)) {
+ dev_err(eth->dev, "could not connect to PHY\n");
+ return PTR_ERR(phydev);
+ }
+
+ dev_info(eth->dev,
+ "connected mac %d to PHY at %s [uid=%08x, driver=%s]\n",
+ mac->id, phydev_name(phydev), phydev->phy_id,
+ phydev->drv->name);
+
+ mac->phy_dev = phydev;
+
+ return 0;
+}
+
+static int mtk_phy_connect(struct mtk_mac *mac)
+{
+ struct mtk_eth *eth = mac->hw;
+ struct device_node *np;
+ u32 val, ge_mode;
+
+ np = of_parse_phandle(mac->of_node, "phy-handle", 0);
+ if (!np)
+ return -ENODEV;
+
+ switch (of_get_phy_mode(np)) {
+ case PHY_INTERFACE_MODE_RGMII:
+ ge_mode = 0;
+ break;
+ case PHY_INTERFACE_MODE_MII:
+ ge_mode = 1;
+ break;
+ case PHY_INTERFACE_MODE_RMII:
+ ge_mode = 2;
+ break;
+ default:
+ dev_err(eth->dev, "invalid phy_mode\n");
+ return -1;
+ }
+
+ /* put the gmac into the right mode */
+ regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val);
+ val &= ~SYSCFG0_GE_MODE(SYSCFG0_GE_MASK, mac->id);
+ val |= SYSCFG0_GE_MODE(ge_mode, mac->id);
+ regmap_write(eth->ethsys, ETHSYS_SYSCFG0, val);
+
+ mtk_phy_connect_node(eth, mac, np);
+ mac->phy_dev->autoneg = AUTONEG_ENABLE;
+ mac->phy_dev->speed = 0;
+ mac->phy_dev->duplex = 0;
+ mac->phy_dev->supported &= PHY_BASIC_FEATURES;
+ mac->phy_dev->advertising = mac->phy_dev->supported |
+ ADVERTISED_Autoneg;
+ phy_start_aneg(mac->phy_dev);
+
+ return 0;
+}
+
+static int mtk_mdio_init(struct mtk_eth *eth)
+{
+ struct device_node *mii_np;
+ int err;
+
+ mii_np = of_get_child_by_name(eth->dev->of_node, "mdio-bus");
+ if (!mii_np) {
+ dev_err(eth->dev, "no %s child node found", "mdio-bus");
+ return -ENODEV;
+ }
+
+ if (!of_device_is_available(mii_np)) {
+ err = 0;
+ goto err_put_node;
+ }
+
+ eth->mii_bus = mdiobus_alloc();
+ if (!eth->mii_bus) {
+ err = -ENOMEM;
+ goto err_put_node;
+ }
+
+ eth->mii_bus->name = "mdio";
+ eth->mii_bus->read = mtk_mdio_read;
+ eth->mii_bus->write = mtk_mdio_write;
+ eth->mii_bus->priv = eth;
+ eth->mii_bus->parent = eth->dev;
+
+ snprintf(eth->mii_bus->id, MII_BUS_ID_SIZE, "%s", mii_np->name);
+ err = of_mdiobus_register(eth->mii_bus, mii_np);
+ if (err)
+ goto err_free_bus;
+
+ return 0;
+
+err_free_bus:
+ kfree(eth->mii_bus);
+
+err_put_node:
+ of_node_put(mii_np);
+ eth->mii_bus = NULL;
+ return err;
+}
+
+static void mtk_mdio_cleanup(struct mtk_eth *eth)
+{
+ if (!eth->mii_bus)
+ return;
+
+ mdiobus_unregister(eth->mii_bus);
+ of_node_put(eth->mii_bus->dev.of_node);
+ kfree(eth->mii_bus);
+}
+
+static inline void mtk_irq_disable(struct mtk_eth *eth, u32 mask)
+{
+ u32 val;
+
+ val = mtk_r32(eth, MTK_QDMA_INT_MASK);
+ mtk_w32(eth, val & ~mask, MTK_QDMA_INT_MASK);
+ /* flush write */
+ mtk_r32(eth, MTK_QDMA_INT_MASK);
+}
+
+static inline void mtk_irq_enable(struct mtk_eth *eth, u32 mask)
+{
+ u32 val;
+
+ val = mtk_r32(eth, MTK_QDMA_INT_MASK);
+ mtk_w32(eth, val | mask, MTK_QDMA_INT_MASK);
+ /* flush write */
+ mtk_r32(eth, MTK_QDMA_INT_MASK);
+}
+
+static int mtk_set_mac_address(struct net_device *dev, void *p)
+{
+ int ret = eth_mac_addr(dev, p);
+ struct mtk_mac *mac = netdev_priv(dev);
+ const char *macaddr = dev->dev_addr;
+ unsigned long flags;
+
+ if (ret)
+ return ret;
+
+ spin_lock_irqsave(&mac->hw->page_lock, flags);
+ mtk_w32(mac->hw, (macaddr[0] << 8) | macaddr[1],
+ MTK_GDMA_MAC_ADRH(mac->id));
+ mtk_w32(mac->hw, (macaddr[2] << 24) | (macaddr[3] << 16) |
+ (macaddr[4] << 8) | macaddr[5],
+ MTK_GDMA_MAC_ADRL(mac->id));
+ spin_unlock_irqrestore(&mac->hw->page_lock, flags);
+
+ return 0;
+}
+
+void mtk_stats_update_mac(struct mtk_mac *mac)
+{
+ struct mtk_hw_stats *hw_stats = mac->hw_stats;
+ unsigned int base = MTK_GDM1_TX_GBCNT;
+ u64 stats;
+
+ base += hw_stats->reg_offset;
+
+ u64_stats_update_begin(&hw_stats->syncp);
+
+ hw_stats->rx_bytes += mtk_r32(mac->hw, base);
+ stats = mtk_r32(mac->hw, base + 0x04);
+ if (stats)
+ hw_stats->rx_bytes += (stats << 32);
+ hw_stats->rx_packets += mtk_r32(mac->hw, base + 0x08);
+ hw_stats->rx_overflow += mtk_r32(mac->hw, base + 0x10);
+ hw_stats->rx_fcs_errors += mtk_r32(mac->hw, base + 0x14);
+ hw_stats->rx_short_errors += mtk_r32(mac->hw, base + 0x18);
+ hw_stats->rx_long_errors += mtk_r32(mac->hw, base + 0x1c);
+ hw_stats->rx_checksum_errors += mtk_r32(mac->hw, base + 0x20);
+ hw_stats->rx_flow_control_packets +=
+ mtk_r32(mac->hw, base + 0x24);
+ hw_stats->tx_skip += mtk_r32(mac->hw, base + 0x28);
+ hw_stats->tx_collisions += mtk_r32(mac->hw, base + 0x2c);
+ hw_stats->tx_bytes += mtk_r32(mac->hw, base + 0x30);
+ stats = mtk_r32(mac->hw, base + 0x34);
+ if (stats)
+ hw_stats->tx_bytes += (stats << 32);
+ hw_stats->tx_packets += mtk_r32(mac->hw, base + 0x38);
+ u64_stats_update_end(&hw_stats->syncp);
+}
+
+static void mtk_stats_update(struct mtk_eth *eth)
+{
+ int i;
+
+ for (i = 0; i < MTK_MAC_COUNT; i++) {
+ if (!eth->mac[i] || !eth->mac[i]->hw_stats)
+ continue;
+ if (spin_trylock(ð->mac[i]->hw_stats->stats_lock)) {
+ mtk_stats_update_mac(eth->mac[i]);
+ spin_unlock(ð->mac[i]->hw_stats->stats_lock);
+ }
+ }
+}
+
+static struct rtnl_link_stats64 *mtk_get_stats64(struct net_device *dev,
+ struct rtnl_link_stats64 *storage)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_hw_stats *hw_stats = mac->hw_stats;
+ unsigned int start;
+
+ if (netif_running(dev) && netif_device_present(dev)) {
+ if (spin_trylock(&hw_stats->stats_lock)) {
+ mtk_stats_update_mac(mac);
+ spin_unlock(&hw_stats->stats_lock);
+ }
+ }
+
+ do {
+ start = u64_stats_fetch_begin_irq(&hw_stats->syncp);
+ storage->rx_packets = hw_stats->rx_packets;
+ storage->tx_packets = hw_stats->tx_packets;
+ storage->rx_bytes = hw_stats->rx_bytes;
+ storage->tx_bytes = hw_stats->tx_bytes;
+ storage->collisions = hw_stats->tx_collisions;
+ storage->rx_length_errors = hw_stats->rx_short_errors +
+ hw_stats->rx_long_errors;
+ storage->rx_over_errors = hw_stats->rx_overflow;
+ storage->rx_crc_errors = hw_stats->rx_fcs_errors;
+ storage->rx_errors = hw_stats->rx_checksum_errors;
+ storage->tx_aborted_errors = hw_stats->tx_skip;
+ } while (u64_stats_fetch_retry_irq(&hw_stats->syncp, start));
+
+ storage->tx_errors = dev->stats.tx_errors;
+ storage->rx_dropped = dev->stats.rx_dropped;
+ storage->tx_dropped = dev->stats.tx_dropped;
+
+ return storage;
+}
+
+static inline int mtk_max_frag_size(int mtu)
+{
+ /* make sure buf_size will be at least MTK_MAX_RX_LENGTH */
+ if (mtu + MTK_RX_ETH_HLEN < MTK_MAX_RX_LENGTH)
+ mtu = MTK_MAX_RX_LENGTH - MTK_RX_ETH_HLEN;
+
+ return SKB_DATA_ALIGN(MTK_RX_HLEN + mtu) +
+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+}
+
+static inline int mtk_max_buf_size(int frag_size)
+{
+ int buf_size = frag_size - NET_SKB_PAD - NET_IP_ALIGN -
+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+
+ WARN_ON(buf_size < MTK_MAX_RX_LENGTH);
+
+ return buf_size;
+}
+
+static inline void mtk_rx_get_desc(struct mtk_rx_dma *rxd,
+ struct mtk_rx_dma *dma_rxd)
+{
+ rxd->rxd1 = READ_ONCE(dma_rxd->rxd1);
+ rxd->rxd2 = READ_ONCE(dma_rxd->rxd2);
+ rxd->rxd3 = READ_ONCE(dma_rxd->rxd3);
+ rxd->rxd4 = READ_ONCE(dma_rxd->rxd4);
+}
+
+/* the qdma core needs scratch memory to be setup */
+static int mtk_init_fq_dma(struct mtk_eth *eth)
+{
+ dma_addr_t phy_ring_head, phy_ring_tail;
+ int cnt = MTK_DMA_SIZE;
+ dma_addr_t dma_addr;
+ int i;
+
+ eth->scratch_ring = dma_alloc_coherent(eth->dev,
+ cnt * sizeof(struct mtk_tx_dma),
+ &phy_ring_head,
+ GFP_ATOMIC | __GFP_ZERO);
+ if (unlikely(!eth->scratch_ring))
+ return -ENOMEM;
+
+ eth->scratch_head = kcalloc(cnt, MTK_QDMA_PAGE_SIZE,
+ GFP_KERNEL);
+ dma_addr = dma_map_single(eth->dev,
+ eth->scratch_head, cnt * MTK_QDMA_PAGE_SIZE,
+ DMA_FROM_DEVICE);
+ if (unlikely(dma_mapping_error(eth->dev, dma_addr)))
+ return -ENOMEM;
+
+ memset(eth->scratch_ring, 0x0, sizeof(struct mtk_tx_dma) * cnt);
+ phy_ring_tail = phy_ring_head +
+ (sizeof(struct mtk_tx_dma) * (cnt - 1));
+
+ for (i = 0; i < cnt; i++) {
+ eth->scratch_ring[i].txd1 =
+ (dma_addr + (i * MTK_QDMA_PAGE_SIZE));
+ if (i < cnt - 1)
+ eth->scratch_ring[i].txd2 = (phy_ring_head +
+ ((i + 1) * sizeof(struct mtk_tx_dma)));
+ eth->scratch_ring[i].txd3 = TX_DMA_SDL(MTK_QDMA_PAGE_SIZE);
+ }
+
+ mtk_w32(eth, phy_ring_head, MTK_QDMA_FQ_HEAD);
+ mtk_w32(eth, phy_ring_tail, MTK_QDMA_FQ_TAIL);
+ mtk_w32(eth, (cnt << 16) | cnt, MTK_QDMA_FQ_CNT);
+ mtk_w32(eth, MTK_QDMA_PAGE_SIZE << 16, MTK_QDMA_FQ_BLEN);
+
+ return 0;
+}
+
+static inline void *mtk_qdma_phys_to_virt(struct mtk_tx_ring *ring, u32 desc)
+{
+ void *ret = ring->dma;
+
+ return ret + (desc - ring->phys);
+}
+
+static inline struct mtk_tx_buf *mtk_desc_to_tx_buf(struct mtk_tx_ring *ring,
+ struct mtk_tx_dma *txd)
+{
+ int idx = txd - ring->dma;
+
+ return &ring->buf[idx];
+}
+
+static void mtk_tx_unmap(struct device *dev, struct mtk_tx_buf *tx_buf)
+{
+ if (tx_buf->flags & MTK_TX_FLAGS_SINGLE0) {
+ dma_unmap_single(dev,
+ dma_unmap_addr(tx_buf, dma_addr0),
+ dma_unmap_len(tx_buf, dma_len0),
+ DMA_TO_DEVICE);
+ } else if (tx_buf->flags & MTK_TX_FLAGS_PAGE0) {
+ dma_unmap_page(dev,
+ dma_unmap_addr(tx_buf, dma_addr0),
+ dma_unmap_len(tx_buf, dma_len0),
+ DMA_TO_DEVICE);
+ }
+ tx_buf->flags = 0;
+ if (tx_buf->skb &&
+ (tx_buf->skb != (struct sk_buff *)MTK_DMA_DUMMY_DESC))
+ dev_kfree_skb_any(tx_buf->skb);
+ tx_buf->skb = NULL;
+}
+
+static int mtk_tx_map(struct sk_buff *skb, struct net_device *dev,
+ int tx_num, struct mtk_tx_ring *ring, bool gso)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+ struct mtk_tx_dma *itxd, *txd;
+ struct mtk_tx_buf *tx_buf;
+ unsigned long flags;
+ dma_addr_t mapped_addr;
+ unsigned int nr_frags;
+ int i, n_desc = 1;
+ u32 txd4 = 0;
+
+ itxd = ring->next_free;
+ if (itxd == ring->last_free)
+ return -ENOMEM;
+
+ /* set the forward port */
+ txd4 |= (mac->id + 1) << TX_DMA_FPORT_SHIFT;
+
+ tx_buf = mtk_desc_to_tx_buf(ring, itxd);
+ memset(tx_buf, 0, sizeof(*tx_buf));
+
+ if (gso)
+ txd4 |= TX_DMA_TSO;
+
+ /* TX Checksum offload */
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
+ txd4 |= TX_DMA_CHKSUM;
+
+ /* VLAN header offload */
+ if (skb_vlan_tag_present(skb))
+ txd4 |= TX_DMA_INS_VLAN | skb_vlan_tag_get(skb);
+
+ mapped_addr = dma_map_single(&dev->dev, skb->data,
+ skb_headlen(skb), DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(&dev->dev, mapped_addr)))
+ return -ENOMEM;
+
+ /* normally we can rely on the stack not calling this more than once,
+ * however we have 2 queues running ont he same ring so we need to lock
+ * the ring access
+ */
+ spin_lock_irqsave(ð->page_lock, flags);
+ WRITE_ONCE(itxd->txd1, mapped_addr);
+ tx_buf->flags |= MTK_TX_FLAGS_SINGLE0;
+ dma_unmap_addr_set(tx_buf, dma_addr0, mapped_addr);
+ dma_unmap_len_set(tx_buf, dma_len0, skb_headlen(skb));
+
+ /* TX SG offload */
+ txd = itxd;
+ nr_frags = skb_shinfo(skb)->nr_frags;
+ for (i = 0; i < nr_frags; i++) {
+ struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[i];
+ unsigned int offset = 0;
+ int frag_size = skb_frag_size(frag);
+
+ while (frag_size) {
+ bool last_frag = false;
+ unsigned int frag_map_size;
+
+ txd = mtk_qdma_phys_to_virt(ring, txd->txd2);
+ if (txd == ring->last_free)
+ goto err_dma;
+
+ n_desc++;
+ frag_map_size = min(frag_size, MTK_TX_DMA_BUF_LEN);
+ mapped_addr = skb_frag_dma_map(&dev->dev, frag, offset,
+ frag_map_size,
+ DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(&dev->dev, mapped_addr)))
+ goto err_dma;
+
+ if (i == nr_frags - 1 &&
+ (frag_size - frag_map_size) == 0)
+ last_frag = true;
+
+ WRITE_ONCE(txd->txd1, mapped_addr);
+ WRITE_ONCE(txd->txd3, (TX_DMA_SWC |
+ TX_DMA_PLEN0(frag_map_size) |
+ last_frag * TX_DMA_LS0) |
+ mac->id);
+ WRITE_ONCE(txd->txd4, 0);
+
+ tx_buf->skb = (struct sk_buff *)MTK_DMA_DUMMY_DESC;
+ tx_buf = mtk_desc_to_tx_buf(ring, txd);
+ memset(tx_buf, 0, sizeof(*tx_buf));
+
+ tx_buf->flags |= MTK_TX_FLAGS_PAGE0;
+ dma_unmap_addr_set(tx_buf, dma_addr0, mapped_addr);
+ dma_unmap_len_set(tx_buf, dma_len0, frag_map_size);
+ frag_size -= frag_map_size;
+ offset += frag_map_size;
+ }
+ }
+
+ /* store skb to cleanup */
+ tx_buf->skb = skb;
+
+ WRITE_ONCE(itxd->txd4, txd4);
+ WRITE_ONCE(itxd->txd3, (TX_DMA_SWC | TX_DMA_PLEN0(skb_headlen(skb)) |
+ (!nr_frags * TX_DMA_LS0)));
+
+ spin_unlock_irqrestore(ð->page_lock, flags);
+
+ netdev_sent_queue(dev, skb->len);
+ skb_tx_timestamp(skb);
+
+ ring->next_free = mtk_qdma_phys_to_virt(ring, txd->txd2);
+ atomic_sub(n_desc, &ring->free_count);
+
+ /* make sure that all changes to the dma ring are flushed before we
+ * continue
+ */
+ wmb();
+
+ if (netif_xmit_stopped(netdev_get_tx_queue(dev, 0)) || !skb->xmit_more)
+ mtk_w32(eth, txd->txd2, MTK_QTX_CTX_PTR);
+
+ return 0;
+
+err_dma:
+ do {
+ tx_buf = mtk_desc_to_tx_buf(ring, txd);
+
+ /* unmap dma */
+ mtk_tx_unmap(&dev->dev, tx_buf);
+
+ itxd->txd3 = TX_DMA_LS0 | TX_DMA_OWNER_CPU;
+ itxd = mtk_qdma_phys_to_virt(ring, itxd->txd2);
+ } while (itxd != txd);
+
+ return -ENOMEM;
+}
+
+static inline int mtk_cal_txd_req(struct sk_buff *skb)
+{
+ int i, nfrags;
+ struct skb_frag_struct *frag;
+
+ nfrags = 1;
+ if (skb_is_gso(skb)) {
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ frag = &skb_shinfo(skb)->frags[i];
+ nfrags += DIV_ROUND_UP(frag->size, MTK_TX_DMA_BUF_LEN);
+ }
+ } else {
+ nfrags += skb_shinfo(skb)->nr_frags;
+ }
+
+ return DIV_ROUND_UP(nfrags, 2);
+}
+
+static int mtk_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+ struct mtk_tx_ring *ring = ð->tx_ring;
+ struct net_device_stats *stats = &dev->stats;
+ bool gso = false;
+ int tx_num;
+
+ tx_num = mtk_cal_txd_req(skb);
+ if (unlikely(atomic_read(&ring->free_count) <= tx_num)) {
+ netif_stop_queue(dev);
+ netif_err(eth, tx_queued, dev,
+ "Tx Ring full when queue awake!\n");
+ return NETDEV_TX_BUSY;
+ }
+
+ /* TSO: fill MSS info in tcp checksum field */
+ if (skb_is_gso(skb)) {
+ if (skb_cow_head(skb, 0)) {
+ netif_warn(eth, tx_err, dev,
+ "GSO expand head fail.\n");
+ goto drop;
+ }
+
+ if (skb_shinfo(skb)->gso_type &
+ (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)) {
+ gso = true;
+ tcp_hdr(skb)->check = htons(skb_shinfo(skb)->gso_size);
+ }
+ }
+
+ if (mtk_tx_map(skb, dev, tx_num, ring, gso) < 0)
+ goto drop;
+
+ if (unlikely(atomic_read(&ring->free_count) <= ring->thresh)) {
+ netif_stop_queue(dev);
+ if (unlikely(atomic_read(&ring->free_count) >
+ ring->thresh))
+ netif_wake_queue(dev);
+ }
+
+ return NETDEV_TX_OK;
+
+drop:
+ stats->tx_dropped++;
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+}
+
+static int mtk_poll_rx(struct napi_struct *napi, int budget,
+ struct mtk_eth *eth, u32 rx_intr)
+{
+ struct mtk_rx_ring *ring = ð->rx_ring;
+ int idx = ring->calc_idx;
+ struct sk_buff *skb;
+ u8 *data, *new_data;
+ struct mtk_rx_dma *rxd, trxd;
+ int done = 0;
+
+ while (done < budget) {
+ struct net_device *netdev;
+ unsigned int pktlen;
+ dma_addr_t dma_addr;
+ int mac = 0;
+
+ idx = NEXT_RX_DESP_IDX(idx);
+ rxd = &ring->dma[idx];
+ data = ring->data[idx];
+
+ mtk_rx_get_desc(&trxd, rxd);
+ if (!(trxd.rxd2 & RX_DMA_DONE))
+ break;
+
+ /* find out which mac the packet come from. values start at 1 */
+ mac = (trxd.rxd4 >> RX_DMA_FPORT_SHIFT) &
+ RX_DMA_FPORT_MASK;
+ mac--;
+
+ netdev = eth->netdev[mac];
+
+ /* alloc new buffer */
+ new_data = napi_alloc_frag(ring->frag_size);
+ if (unlikely(!new_data)) {
+ netdev->stats.rx_dropped++;
+ goto release_desc;
+ }
+ dma_addr = dma_map_single(ð->netdev[mac]->dev,
+ new_data + NET_SKB_PAD,
+ ring->buf_size,
+ DMA_FROM_DEVICE);
+ if (unlikely(dma_mapping_error(&netdev->dev, dma_addr))) {
+ skb_free_frag(new_data);
+ goto release_desc;
+ }
+
+ /* receive data */
+ skb = build_skb(data, ring->frag_size);
+ if (unlikely(!skb)) {
+ put_page(virt_to_head_page(new_data));
+ goto release_desc;
+ }
+ skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN);
+
+ dma_unmap_single(&netdev->dev, trxd.rxd1,
+ ring->buf_size, DMA_FROM_DEVICE);
+ pktlen = RX_DMA_GET_PLEN0(trxd.rxd2);
+ skb->dev = netdev;
+ skb_put(skb, pktlen);
+ if (trxd.rxd4 & RX_DMA_L4_VALID)
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ else
+ skb_checksum_none_assert(skb);
+ skb->protocol = eth_type_trans(skb, netdev);
+
+ if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX &&
+ RX_DMA_VID(trxd.rxd3))
+ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
+ RX_DMA_VID(trxd.rxd3));
+ napi_gro_receive(napi, skb);
+
+ ring->data[idx] = new_data;
+ rxd->rxd1 = (unsigned int)dma_addr;
+
+release_desc:
+ rxd->rxd2 = RX_DMA_PLEN0(ring->buf_size);
+
+ ring->calc_idx = idx;
+ /* make sure that all changes to the dma ring are flushed before
+ * we continue
+ */
+ wmb();
+ mtk_w32(eth, ring->calc_idx, MTK_QRX_CRX_IDX0);
+ done++;
+ }
+
+ if (done < budget)
+ mtk_w32(eth, rx_intr, MTK_QMTK_INT_STATUS);
+
+ return done;
+}
+
+static int mtk_poll_tx(struct mtk_eth *eth, int budget, bool *tx_again)
+{
+ struct mtk_tx_ring *ring = ð->tx_ring;
+ struct mtk_tx_dma *desc;
+ struct sk_buff *skb;
+ struct mtk_tx_buf *tx_buf;
+ int total = 0, done[MTK_MAX_DEVS];
+ unsigned int bytes[MTK_MAX_DEVS];
+ u32 cpu, dma;
+ static int condition;
+ int i;
+
+ memset(done, 0, sizeof(done));
+ memset(bytes, 0, sizeof(bytes));
+
+ cpu = mtk_r32(eth, MTK_QTX_CRX_PTR);
+ dma = mtk_r32(eth, MTK_QTX_DRX_PTR);
+
+ desc = mtk_qdma_phys_to_virt(ring, cpu);
+
+ while ((cpu != dma) && budget) {
+ u32 next_cpu = desc->txd2;
+ int mac;
+
+ desc = mtk_qdma_phys_to_virt(ring, desc->txd2);
+ if ((desc->txd3 & TX_DMA_OWNER_CPU) == 0)
+ break;
+
+ mac = (desc->txd4 >> TX_DMA_FPORT_SHIFT) &
+ TX_DMA_FPORT_MASK;
+ mac--;
+
+ tx_buf = mtk_desc_to_tx_buf(ring, desc);
+ skb = tx_buf->skb;
+ if (!skb) {
+ condition = 1;
+ break;
+ }
+
+ if (skb != (struct sk_buff *)MTK_DMA_DUMMY_DESC) {
+ bytes[mac] += skb->len;
+ done[mac]++;
+ budget--;
+ }
+ mtk_tx_unmap(eth->dev, tx_buf);
+
+ ring->last_free->txd2 = next_cpu;
+ ring->last_free = desc;
+ atomic_inc(&ring->free_count);
+
+ cpu = next_cpu;
+ }
+
+ mtk_w32(eth, cpu, MTK_QTX_CRX_PTR);
+
+ for (i = 0; i < MTK_MAC_COUNT; i++) {
+ if (!eth->netdev[i] || !done[i])
+ continue;
+ netdev_completed_queue(eth->netdev[i], done[i], bytes[i]);
+ total += done[i];
+ }
+
+ /* read hw index again make sure no new tx packet */
+ if (cpu != dma || cpu != mtk_r32(eth, MTK_QTX_DRX_PTR))
+ *tx_again = true;
+ else
+ mtk_w32(eth, MTK_TX_DONE_INT, MTK_QMTK_INT_STATUS);
+
+ if (!total)
+ return 0;
+
+ for (i = 0; i < MTK_MAC_COUNT; i++) {
+ if (!eth->netdev[i] ||
+ unlikely(!netif_queue_stopped(eth->netdev[i])))
+ continue;
+ if (atomic_read(&ring->free_count) > ring->thresh)
+ netif_wake_queue(eth->netdev[i]);
+ }
+
+ return total;
+}
+
+static int mtk_poll(struct napi_struct *napi, int budget)
+{
+ struct mtk_eth *eth = container_of(napi, struct mtk_eth, rx_napi);
+ u32 status, status2, mask, tx_intr, rx_intr, status_intr;
+ int tx_done, rx_done;
+ bool tx_again = false;
+
+ status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
+ status2 = mtk_r32(eth, MTK_INT_STATUS2);
+ tx_intr = MTK_TX_DONE_INT;
+ rx_intr = MTK_RX_DONE_INT;
+ status_intr = (MTK_GDM1_AF | MTK_GDM2_AF);
+ tx_done = 0;
+ rx_done = 0;
+ tx_again = 0;
+
+ if (status & tx_intr)
+ tx_done = mtk_poll_tx(eth, budget, &tx_again);
+
+ if (status & rx_intr)
+ rx_done = mtk_poll_rx(napi, budget, eth, rx_intr);
+
+ if (unlikely(status2 & status_intr)) {
+ mtk_stats_update(eth);
+ mtk_w32(eth, status_intr, MTK_INT_STATUS2);
+ }
+
+ if (unlikely(netif_msg_intr(eth))) {
+ mask = mtk_r32(eth, MTK_QDMA_INT_MASK);
+ netdev_info(eth->netdev[0],
+ "done tx %d, rx %d, intr 0x%08x/0x%x\n",
+ tx_done, rx_done, status, mask);
+ }
+
+ if (tx_again || rx_done == budget)
+ return budget;
+
+ status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
+ if (status & (tx_intr | rx_intr))
+ return budget;
+
+ napi_complete(napi);
+ mtk_irq_enable(eth, tx_intr | rx_intr);
+
+ return rx_done;
+}
+
+static int mtk_tx_alloc(struct mtk_eth *eth)
+{
+ struct mtk_tx_ring *ring = ð->tx_ring;
+ int i, sz = sizeof(*ring->dma);
+
+ ring->buf = kcalloc(MTK_DMA_SIZE, sizeof(*ring->buf),
+ GFP_KERNEL);
+ if (!ring->buf)
+ goto no_tx_mem;
+
+ ring->dma = dma_alloc_coherent(eth->dev,
+ MTK_DMA_SIZE * sz,
+ &ring->phys,
+ GFP_ATOMIC | __GFP_ZERO);
+ if (!ring->dma)
+ goto no_tx_mem;
+
+ memset(ring->dma, 0, MTK_DMA_SIZE * sz);
+ for (i = 0; i < MTK_DMA_SIZE; i++) {
+ int next = (i + 1) % MTK_DMA_SIZE;
+ u32 next_ptr = ring->phys + next * sz;
+
+ ring->dma[i].txd2 = next_ptr;
+ ring->dma[i].txd3 = TX_DMA_LS0 | TX_DMA_OWNER_CPU;
+ }
+
+ atomic_set(&ring->free_count, MTK_DMA_SIZE - 2);
+ ring->next_free = &ring->dma[0];
+ ring->last_free = &ring->dma[MTK_DMA_SIZE - 2];
+ ring->thresh = max((unsigned long)MTK_DMA_SIZE >> 2,
+ MAX_SKB_FRAGS);
+
+ /* make sure that all changes to the dma ring are flushed before we
+ * continue
+ */
+ wmb();
+
+ mtk_w32(eth, ring->phys, MTK_QTX_CTX_PTR);
+ mtk_w32(eth, ring->phys, MTK_QTX_DTX_PTR);
+ mtk_w32(eth,
+ ring->phys + ((MTK_DMA_SIZE - 1) * sz),
+ MTK_QTX_CRX_PTR);
+ mtk_w32(eth,
+ ring->phys + ((MTK_DMA_SIZE - 1) * sz),
+ MTK_QTX_DRX_PTR);
+
+ return 0;
+
+no_tx_mem:
+ return -ENOMEM;
+}
+
+static void mtk_tx_clean(struct mtk_eth *eth)
+{
+ struct mtk_tx_ring *ring = ð->tx_ring;
+ int i;
+
+ if (ring->buf) {
+ for (i = 0; i < MTK_DMA_SIZE; i++)
+ mtk_tx_unmap(eth->dev, &ring->buf[i]);
+ kfree(ring->buf);
+ ring->buf = NULL;
+ }
+
+ if (ring->dma) {
+ dma_free_coherent(eth->dev,
+ MTK_DMA_SIZE * sizeof(*ring->dma),
+ ring->dma,
+ ring->phys);
+ ring->dma = NULL;
+ }
+}
+
+static int mtk_rx_alloc(struct mtk_eth *eth)
+{
+ struct mtk_rx_ring *ring = ð->rx_ring;
+ int i;
+
+ ring->frag_size = mtk_max_frag_size(ETH_DATA_LEN);
+ ring->buf_size = mtk_max_buf_size(ring->frag_size);
+ ring->data = kcalloc(MTK_DMA_SIZE, sizeof(*ring->data),
+ GFP_KERNEL);
+ if (!ring->data)
+ return -ENOMEM;
+
+ for (i = 0; i < MTK_DMA_SIZE; i++) {
+ ring->data[i] = netdev_alloc_frag(ring->frag_size);
+ if (!ring->data[i])
+ return -ENOMEM;
+ }
+
+ ring->dma = dma_alloc_coherent(eth->dev,
+ MTK_DMA_SIZE * sizeof(*ring->dma),
+ &ring->phys,
+ GFP_ATOMIC | __GFP_ZERO);
+ if (!ring->dma)
+ return -ENOMEM;
+
+ for (i = 0; i < MTK_DMA_SIZE; i++) {
+ dma_addr_t dma_addr = dma_map_single(eth->dev,
+ ring->data[i] + NET_SKB_PAD,
+ ring->buf_size,
+ DMA_FROM_DEVICE);
+ if (unlikely(dma_mapping_error(eth->dev, dma_addr)))
+ return -ENOMEM;
+ ring->dma[i].rxd1 = (unsigned int)dma_addr;
+
+ ring->dma[i].rxd2 = RX_DMA_PLEN0(ring->buf_size);
+ }
+ ring->calc_idx = MTK_DMA_SIZE - 1;
+ /* make sure that all changes to the dma ring are flushed before we
+ * continue
+ */
+ wmb();
+
+ mtk_w32(eth, eth->rx_ring.phys, MTK_QRX_BASE_PTR0);
+ mtk_w32(eth, MTK_DMA_SIZE, MTK_QRX_MAX_CNT0);
+ mtk_w32(eth, eth->rx_ring.calc_idx, MTK_QRX_CRX_IDX0);
+ mtk_w32(eth, MTK_PST_DRX_IDX0, MTK_QDMA_RST_IDX);
+ mtk_w32(eth, (QDMA_RES_THRES << 8) | QDMA_RES_THRES, MTK_QTX_CFG(0));
+
+ return 0;
+}
+
+static void mtk_rx_clean(struct mtk_eth *eth)
+{
+ struct mtk_rx_ring *ring = ð->rx_ring;
+ int i;
+
+ if (ring->data && ring->dma) {
+ for (i = 0; i < MTK_DMA_SIZE; i++) {
+ if (!ring->data[i])
+ continue;
+ if (!ring->dma[i].rxd1)
+ continue;
+ dma_unmap_single(eth->dev,
+ ring->dma[i].rxd1,
+ ring->buf_size,
+ DMA_FROM_DEVICE);
+ skb_free_frag(ring->data[i]);
+ }
+ kfree(ring->data);
+ ring->data = NULL;
+ }
+
+ if (ring->dma) {
+ dma_free_coherent(eth->dev,
+ MTK_DMA_SIZE * sizeof(*ring->dma),
+ ring->dma,
+ ring->phys);
+ ring->dma = NULL;
+ }
+}
+
+/* wait for DMA to finish whatever it is doing before we start using it again */
+static int mtk_dma_busy_wait(struct mtk_eth *eth)
+{
+ unsigned long t_start = jiffies;
+
+ while (1) {
+ if (!(mtk_r32(eth, MTK_QDMA_GLO_CFG) &
+ (MTK_RX_DMA_BUSY | MTK_TX_DMA_BUSY)))
+ return 0;
+ if (time_after(jiffies, t_start + MTK_DMA_BUSY_TIMEOUT))
+ break;
+ }
+
+ dev_err(eth->dev, "DMA init timeout\n");
+ return -1;
+}
+
+static int mtk_dma_init(struct mtk_eth *eth)
+{
+ int err;
+
+ if (mtk_dma_busy_wait(eth))
+ return -EBUSY;
+
+ /* QDMA needs scratch memory for internal reordering of the
+ * descriptors
+ */
+ err = mtk_init_fq_dma(eth);
+ if (err)
+ return err;
+
+ err = mtk_tx_alloc(eth);
+ if (err)
+ return err;
+
+ err = mtk_rx_alloc(eth);
+ if (err)
+ return err;
+
+ /* Enable random early drop and set drop threshold automatically */
+ mtk_w32(eth, FC_THRES_DROP_MODE | FC_THRES_DROP_EN | FC_THRES_MIN,
+ MTK_QDMA_FC_THRES);
+ mtk_w32(eth, 0x0, MTK_QDMA_HRED2);
+
+ return 0;
+}
+
+static void mtk_dma_free(struct mtk_eth *eth)
+{
+ int i;
+
+ for (i = 0; i < MTK_MAC_COUNT; i++)
+ if (eth->netdev[i])
+ netdev_reset_queue(eth->netdev[i]);
+ mtk_tx_clean(eth);
+ mtk_rx_clean(eth);
+ kfree(eth->scratch_head);
+}
+
+static void mtk_tx_timeout(struct net_device *dev)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+
+ eth->netdev[mac->id]->stats.tx_errors++;
+ netif_err(eth, tx_err, dev,
+ "transmit timed out\n");
+ schedule_work(&mac->pending_work);
+}
+
+static irqreturn_t mtk_handle_irq(int irq, void *_eth)
+{
+ struct mtk_eth *eth = _eth;
+ u32 status;
+
+ status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
+ if (unlikely(!status))
+ return IRQ_NONE;
+
+ if (likely(status & (MTK_RX_DONE_INT | MTK_TX_DONE_INT))) {
+ if (likely(napi_schedule_prep(ð->rx_napi)))
+ __napi_schedule(ð->rx_napi);
+ } else {
+ mtk_w32(eth, status, MTK_QMTK_INT_STATUS);
+ }
+ mtk_irq_disable(eth, (MTK_RX_DONE_INT | MTK_TX_DONE_INT));
+
+ return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void mtk_poll_controller(struct net_device *dev)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+ u32 int_mask = MTK_TX_DONE_INT | MTK_RX_DONE_INT;
+
+ mtk_irq_disable(eth, int_mask);
+ mtk_handle_irq(dev->irq, dev);
+ mtk_irq_enable(eth, int_mask);
+}
+#endif
+
+static int mtk_start_dma(struct mtk_eth *eth)
+{
+ int err;
+
+ err = mtk_dma_init(eth);
+ if (err) {
+ mtk_dma_free(eth);
+ return err;
+ }
+
+ mtk_w32(eth,
+ MTK_TX_WB_DDONE | MTK_RX_DMA_EN | MTK_TX_DMA_EN |
+ MTK_RX_2B_OFFSET | MTK_DMA_SIZE_16DWORDS |
+ MTK_RX_BT_32DWORDS,
+ MTK_QDMA_GLO_CFG);
+
+ return 0;
+}
+
+static int mtk_open(struct net_device *dev)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+
+ /* we run 2 netdevs on the same dma ring so we only bring it up once */
+ if (!atomic_read(ð->dma_refcnt)) {
+ int err = mtk_start_dma(eth);
+
+ if (err)
+ return err;
+
+ napi_enable(ð->rx_napi);
+ mtk_irq_enable(eth, MTK_TX_DONE_INT | MTK_RX_DONE_INT);
+ }
+ atomic_inc(ð->dma_refcnt);
+
+ phy_start(mac->phy_dev);
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+static void mtk_stop_dma(struct mtk_eth *eth, u32 glo_cfg)
+{
+ unsigned long flags;
+ u32 val;
+ int i;
+
+ /* stop the dma engine */
+ spin_lock_irqsave(ð->page_lock, flags);
+ val = mtk_r32(eth, glo_cfg);
+ mtk_w32(eth, val & ~(MTK_TX_WB_DDONE | MTK_RX_DMA_EN | MTK_TX_DMA_EN),
+ glo_cfg);
+ spin_unlock_irqrestore(ð->page_lock, flags);
+
+ /* wait for dma stop */
+ for (i = 0; i < 10; i++) {
+ val = mtk_r32(eth, glo_cfg);
+ if (val & (MTK_TX_DMA_BUSY | MTK_RX_DMA_BUSY)) {
+ msleep(20);
+ continue;
+ }
+ break;
+ }
+}
+
+static int mtk_stop(struct net_device *dev)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+
+ netif_tx_disable(dev);
+ phy_stop(mac->phy_dev);
+
+ /* only shutdown DMA if this is the last user */
+ if (!atomic_dec_and_test(ð->dma_refcnt))
+ return 0;
+
+ mtk_irq_disable(eth, MTK_TX_DONE_INT | MTK_RX_DONE_INT);
+ napi_disable(ð->rx_napi);
+
+ mtk_stop_dma(eth, MTK_QDMA_GLO_CFG);
+
+ mtk_dma_free(eth);
+
+ return 0;
+}
+
+static int __init mtk_hw_init(struct mtk_eth *eth)
+{
+ int err, i;
+
+ /* reset the frame engine */
+ reset_control_assert(eth->rstc);
+ usleep_range(10, 20);
+ reset_control_deassert(eth->rstc);
+ usleep_range(10, 20);
+
+ /* Set GE2 driving and slew rate */
+ regmap_write(eth->pctl, GPIO_DRV_SEL10, 0xa00);
+
+ /* set GE2 TDSEL */
+ regmap_write(eth->pctl, GPIO_OD33_CTRL8, 0x5);
+
+ /* set GE2 TUNE */
+ regmap_write(eth->pctl, GPIO_BIAS_CTRL, 0x0);
+
+ /* GE1, Force 1000M/FD, FC ON */
+ mtk_w32(eth, MAC_MCR_FIXED_LINK, MTK_MAC_MCR(0));
+
+ /* GE2, Force 1000M/FD, FC ON */
+ mtk_w32(eth, MAC_MCR_FIXED_LINK, MTK_MAC_MCR(1));
+
+ /* Enable RX VLan Offloading */
+ mtk_w32(eth, 1, MTK_CDMP_EG_CTRL);
+
+ err = devm_request_irq(eth->dev, eth->irq, mtk_handle_irq, 0,
+ dev_name(eth->dev), eth);
+ if (err)
+ return err;
+
+ err = mtk_mdio_init(eth);
+ if (err)
+ return err;
+
+ /* disable delay and normal interrupt */
+ mtk_w32(eth, 0, MTK_QDMA_DELAY_INT);
+ mtk_irq_disable(eth, MTK_TX_DONE_INT | MTK_RX_DONE_INT);
+ mtk_w32(eth, RST_GL_PSE, MTK_RST_GL);
+ mtk_w32(eth, 0, MTK_RST_GL);
+
+ /* FE int grouping */
+ mtk_w32(eth, 0, MTK_FE_INT_GRP);
+
+ for (i = 0; i < 2; i++) {
+ u32 val = mtk_r32(eth, MTK_GDMA_FWD_CFG(i));
+
+ /* setup the forward port to send frame to QDMA */
+ val &= ~0xffff;
+ val |= 0x5555;
+
+ /* Enable RX checksum */
+ val |= MTK_GDMA_ICS_EN | MTK_GDMA_TCS_EN | MTK_GDMA_UCS_EN;
+
+ /* setup the mac dma */
+ mtk_w32(eth, val, MTK_GDMA_FWD_CFG(i));
+ }
+
+ return 0;
+}
+
+static int __init mtk_init(struct net_device *dev)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+ const char *mac_addr;
+
+ mac_addr = of_get_mac_address(mac->of_node);
+ if (mac_addr)
+ ether_addr_copy(dev->dev_addr, mac_addr);
+
+ /* If the mac address is invalid, use random mac address */
+ if (!is_valid_ether_addr(dev->dev_addr)) {
+ random_ether_addr(dev->dev_addr);
+ dev_err(eth->dev, "generated random MAC address %pM\n",
+ dev->dev_addr);
+ dev->addr_assign_type = NET_ADDR_RANDOM;
+ }
+
+ return mtk_phy_connect(mac);
+}
+
+static void mtk_uninit(struct net_device *dev)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+
+ phy_disconnect(mac->phy_dev);
+ mtk_mdio_cleanup(eth);
+ mtk_irq_disable(eth, ~0);
+ free_irq(dev->irq, dev);
+}
+
+static int mtk_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+
+ switch (cmd) {
+ case SIOCGMIIPHY:
+ case SIOCGMIIREG:
+ case SIOCSMIIREG:
+ return phy_mii_ioctl(mac->phy_dev, ifr, cmd);
+ default:
+ break;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static void mtk_pending_work(struct work_struct *work)
+{
+ struct mtk_mac *mac = container_of(work, struct mtk_mac, pending_work);
+ struct mtk_eth *eth = mac->hw;
+ struct net_device *dev = eth->netdev[mac->id];
+ int err;
+
+ rtnl_lock();
+ mtk_stop(dev);
+
+ err = mtk_open(dev);
+ if (err) {
+ netif_alert(eth, ifup, dev,
+ "Driver up/down cycle failed, closing device.\n");
+ dev_close(dev);
+ }
+ rtnl_unlock();
+}
+
+static int mtk_cleanup(struct mtk_eth *eth)
+{
+ int i;
+
+ for (i = 0; i < MTK_MAC_COUNT; i++) {
+ struct mtk_mac *mac = netdev_priv(eth->netdev[i]);
+
+ if (!eth->netdev[i])
+ continue;
+
+ unregister_netdev(eth->netdev[i]);
+ free_netdev(eth->netdev[i]);
+ cancel_work_sync(&mac->pending_work);
+ }
+
+ return 0;
+}
+
+static int mtk_get_settings(struct net_device *dev,
+ struct ethtool_cmd *cmd)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ int err;
+
+ err = phy_read_status(mac->phy_dev);
+ if (err)
+ return -ENODEV;
+
+ return phy_ethtool_gset(mac->phy_dev, cmd);
+}
+
+static int mtk_set_settings(struct net_device *dev,
+ struct ethtool_cmd *cmd)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+
+ if (cmd->phy_address != mac->phy_dev->mdio.addr) {
+ mac->phy_dev = mdiobus_get_phy(mac->hw->mii_bus,
+ cmd->phy_address);
+ if (!mac->phy_dev)
+ return -ENODEV;
+ }
+
+ return phy_ethtool_sset(mac->phy_dev, cmd);
+}
+
+static void mtk_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+
+ strlcpy(info->driver, mac->hw->dev->driver->name, sizeof(info->driver));
+ strlcpy(info->bus_info, dev_name(mac->hw->dev), sizeof(info->bus_info));
+ info->n_stats = ARRAY_SIZE(mtk_ethtool_stats);
+}
+
+static u32 mtk_get_msglevel(struct net_device *dev)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+
+ return mac->hw->msg_enable;
+}
+
+static void mtk_set_msglevel(struct net_device *dev, u32 value)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+
+ mac->hw->msg_enable = value;
+}
+
+static int mtk_nway_reset(struct net_device *dev)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+
+ return genphy_restart_aneg(mac->phy_dev);
+}
+
+static u32 mtk_get_link(struct net_device *dev)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ int err;
+
+ err = genphy_update_link(mac->phy_dev);
+ if (err)
+ return ethtool_op_get_link(dev);
+
+ return mac->phy_dev->link;
+}
+
+static void mtk_get_strings(struct net_device *dev, u32 stringset, u8 *data)
+{
+ int i;
+
+ switch (stringset) {
+ case ETH_SS_STATS:
+ for (i = 0; i < ARRAY_SIZE(mtk_ethtool_stats); i++) {
+ memcpy(data, mtk_ethtool_stats[i].str, ETH_GSTRING_LEN);
+ data += ETH_GSTRING_LEN;
+ }
+ break;
+ }
+}
+
+static int mtk_get_sset_count(struct net_device *dev, int sset)
+{
+ switch (sset) {
+ case ETH_SS_STATS:
+ return ARRAY_SIZE(mtk_ethtool_stats);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static void mtk_get_ethtool_stats(struct net_device *dev,
+ struct ethtool_stats *stats, u64 *data)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_hw_stats *hwstats = mac->hw_stats;
+ u64 *data_src, *data_dst;
+ unsigned int start;
+ int i;
+
+ if (netif_running(dev) && netif_device_present(dev)) {
+ if (spin_trylock(&hwstats->stats_lock)) {
+ mtk_stats_update_mac(mac);
+ spin_unlock(&hwstats->stats_lock);
+ }
+ }
+
+ do {
+ data_src = (u64*)hwstats;
+ data_dst = data;
+ start = u64_stats_fetch_begin_irq(&hwstats->syncp);
+
+ for (i = 0; i < ARRAY_SIZE(mtk_ethtool_stats); i++)
+ *data_dst++ = *(data_src + mtk_ethtool_stats[i].offset);
+ } while (u64_stats_fetch_retry_irq(&hwstats->syncp, start));
+}
+
+static struct ethtool_ops mtk_ethtool_ops = {
+ .get_settings = mtk_get_settings,
+ .set_settings = mtk_set_settings,
+ .get_drvinfo = mtk_get_drvinfo,
+ .get_msglevel = mtk_get_msglevel,
+ .set_msglevel = mtk_set_msglevel,
+ .nway_reset = mtk_nway_reset,
+ .get_link = mtk_get_link,
+ .get_strings = mtk_get_strings,
+ .get_sset_count = mtk_get_sset_count,
+ .get_ethtool_stats = mtk_get_ethtool_stats,
+};
+
+static const struct net_device_ops mtk_netdev_ops = {
+ .ndo_init = mtk_init,
+ .ndo_uninit = mtk_uninit,
+ .ndo_open = mtk_open,
+ .ndo_stop = mtk_stop,
+ .ndo_start_xmit = mtk_start_xmit,
+ .ndo_set_mac_address = mtk_set_mac_address,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_do_ioctl = mtk_do_ioctl,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_tx_timeout = mtk_tx_timeout,
+ .ndo_get_stats64 = mtk_get_stats64,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = mtk_poll_controller,
+#endif
+};
+
+static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
+{
+ struct mtk_mac *mac;
+ const __be32 *_id = of_get_property(np, "reg", NULL);
+ int id, err;
+
+ if (!_id) {
+ dev_err(eth->dev, "missing mac id\n");
+ return -EINVAL;
+ }
+
+ id = be32_to_cpup(_id);
+ if (id >= MTK_MAC_COUNT) {
+ dev_err(eth->dev, "%d is not a valid mac id\n", id);
+ return -EINVAL;
+ }
+
+ if (eth->netdev[id]) {
+ dev_err(eth->dev, "duplicate mac id found: %d\n", id);
+ return -EINVAL;
+ }
+
+ eth->netdev[id] = alloc_etherdev(sizeof(*mac));
+ if (!eth->netdev[id]) {
+ dev_err(eth->dev, "alloc_etherdev failed\n");
+ return -ENOMEM;
+ }
+ mac = netdev_priv(eth->netdev[id]);
+ eth->mac[id] = mac;
+ mac->id = id;
+ mac->hw = eth;
+ mac->of_node = np;
+ INIT_WORK(&mac->pending_work, mtk_pending_work);
+
+ mac->hw_stats = devm_kzalloc(eth->dev,
+ sizeof(*mac->hw_stats),
+ GFP_KERNEL);
+ if (!mac->hw_stats) {
+ dev_err(eth->dev, "failed to allocate counter memory\n");
+ err = -ENOMEM;
+ goto free_netdev;
+ }
+ spin_lock_init(&mac->hw_stats->stats_lock);
+ mac->hw_stats->reg_offset = id * MTK_STAT_OFFSET;
+
+ SET_NETDEV_DEV(eth->netdev[id], eth->dev);
+ eth->netdev[id]->netdev_ops = &mtk_netdev_ops;
+ eth->netdev[id]->base_addr = (unsigned long)eth->base;
+ eth->netdev[id]->vlan_features = MTK_HW_FEATURES &
+ ~(NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX);
+ eth->netdev[id]->features |= MTK_HW_FEATURES;
+ eth->netdev[id]->ethtool_ops = &mtk_ethtool_ops;
+
+ err = register_netdev(eth->netdev[id]);
+ if (err) {
+ dev_err(eth->dev, "error bringing up device\n");
+ goto free_netdev;
+ }
+ eth->netdev[id]->irq = eth->irq;
+ netif_info(eth, probe, eth->netdev[id],
+ "mediatek frame engine at 0x%08lx, irq %d\n",
+ eth->netdev[id]->base_addr, eth->netdev[id]->irq);
+
+ return 0;
+
+free_netdev:
+ free_netdev(eth->netdev[id]);
+ return err;
+}
+
+static int mtk_probe(struct platform_device *pdev)
+{
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ struct device_node *mac_np;
+ const struct of_device_id *match;
+ struct mtk_soc_data *soc;
+ struct mtk_eth *eth;
+ int err;
+
+ err = device_reset(&pdev->dev);
+ if (err)
+ return err;
+
+ match = of_match_device(of_mtk_match, &pdev->dev);
+ soc = (struct mtk_soc_data *)match->data;
+
+ eth = devm_kzalloc(&pdev->dev, sizeof(*eth), GFP_KERNEL);
+ if (!eth)
+ return -ENOMEM;
+
+ eth->base = devm_ioremap_resource(&pdev->dev, res);
+ if (!eth->base)
+ return -EADDRNOTAVAIL;
+
+ spin_lock_init(ð->page_lock);
+
+ eth->ethsys = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+ "mediatek,ethsys");
+ if (IS_ERR(eth->ethsys)) {
+ dev_err(&pdev->dev, "no ethsys regmap found\n");
+ return PTR_ERR(eth->ethsys);
+ }
+
+ eth->pctl = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+ "mediatek,pctl");
+ if (IS_ERR(eth->pctl)) {
+ dev_err(&pdev->dev, "no pctl regmap found\n");
+ return PTR_ERR(eth->pctl);
+ }
+
+ eth->rstc = devm_reset_control_get(&pdev->dev, "eth");
+ if (IS_ERR(eth->rstc)) {
+ dev_err(&pdev->dev, "no eth reset found\n");
+ return PTR_ERR(eth->rstc);
+ }
+
+ eth->irq = platform_get_irq(pdev, 0);
+ if (eth->irq < 0) {
+ dev_err(&pdev->dev, "no IRQ resource found\n");
+ return -ENXIO;
+ }
+
+ eth->clk_ethif = devm_clk_get(&pdev->dev, "ethif");
+ eth->clk_esw = devm_clk_get(&pdev->dev, "esw");
+ eth->clk_gp1 = devm_clk_get(&pdev->dev, "gp1");
+ eth->clk_gp2 = devm_clk_get(&pdev->dev, "gp2");
+ if (IS_ERR(eth->clk_esw) || IS_ERR(eth->clk_gp1) ||
+ IS_ERR(eth->clk_gp2) || IS_ERR(eth->clk_ethif))
+ return -ENODEV;
+
+ clk_prepare_enable(eth->clk_ethif);
+ clk_prepare_enable(eth->clk_esw);
+ clk_prepare_enable(eth->clk_gp1);
+ clk_prepare_enable(eth->clk_gp2);
+
+ eth->dev = &pdev->dev;
+ eth->msg_enable = netif_msg_init(mtk_msg_level, MTK_DEFAULT_MSG_ENABLE);
+
+ err = mtk_hw_init(eth);
+ if (err)
+ return err;
+
+ for_each_child_of_node(pdev->dev.of_node, mac_np) {
+ if (!of_device_is_compatible(mac_np,
+ "mediatek,eth-mac"))
+ continue;
+
+ if (!of_device_is_available(mac_np))
+ continue;
+
+ err = mtk_add_mac(eth, mac_np);
+ if (err)
+ goto err_free_dev;
+ }
+
+ /* we run 2 devices on the same DMA ring so we need a dummy device
+ * for NAPI to work
+ */
+ init_dummy_netdev(ð->dummy_dev);
+ netif_napi_add(ð->dummy_dev, ð->rx_napi, mtk_poll,
+ MTK_NAPI_WEIGHT);
+
+ platform_set_drvdata(pdev, eth);
+
+ return 0;
+
+err_free_dev:
+ mtk_cleanup(eth);
+ return err;
+}
+
+static int mtk_remove(struct platform_device *pdev)
+{
+ struct mtk_eth *eth = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(eth->clk_ethif);
+ clk_disable_unprepare(eth->clk_esw);
+ clk_disable_unprepare(eth->clk_gp1);
+ clk_disable_unprepare(eth->clk_gp2);
+
+ netif_napi_del(ð->rx_napi);
+ mtk_cleanup(eth);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+const struct of_device_id of_mtk_match[] = {
+ { .compatible = "mediatek,mt7623-eth" },
+ {},
+};
+
+static struct platform_driver mtk_driver = {
+ .probe = mtk_probe,
+ .remove = mtk_remove,
+ .driver = {
+ .name = "mtk_soc_eth",
+ .owner = THIS_MODULE,
+ .of_match_table = of_mtk_match,
+ },
+};
+
+module_platform_driver(mtk_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
+MODULE_DESCRIPTION("Ethernet driver for MediaTek SoC");
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
new file mode 100644
index 0000000..48a5292
--- /dev/null
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
@@ -0,0 +1,421 @@
+/* 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; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Copyright (C) 2009-2016 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
+ */
+
+#ifndef MTK_ETH_H
+#define MTK_ETH_H
+
+#define MTK_QDMA_PAGE_SIZE 2048
+#define MTK_MAX_RX_LENGTH 1536
+#define MTK_TX_DMA_BUF_LEN 0x3fff
+#define MTK_DMA_SIZE 256
+#define MTK_NAPI_WEIGHT 64
+#define MTK_MAC_COUNT 2
+#define MTK_RX_ETH_HLEN (VLAN_ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN)
+#define MTK_RX_HLEN (NET_SKB_PAD + MTK_RX_ETH_HLEN + NET_IP_ALIGN)
+#define MTK_DMA_DUMMY_DESC 0xffffffff
+#define MTK_DEFAULT_MSG_ENABLE (NETIF_MSG_DRV | \
+ NETIF_MSG_PROBE | \
+ NETIF_MSG_LINK | \
+ NETIF_MSG_TIMER | \
+ NETIF_MSG_IFDOWN | \
+ NETIF_MSG_IFUP | \
+ NETIF_MSG_RX_ERR | \
+ NETIF_MSG_TX_ERR)
+#define MTK_HW_FEATURES (NETIF_F_IP_CSUM | \
+ NETIF_F_RXCSUM | \
+ NETIF_F_HW_VLAN_CTAG_TX | \
+ NETIF_F_HW_VLAN_CTAG_RX | \
+ NETIF_F_SG | NETIF_F_TSO | \
+ NETIF_F_TSO6 | \
+ NETIF_F_IPV6_CSUM)
+#define NEXT_RX_DESP_IDX(X) (((X) + 1) & (MTK_DMA_SIZE - 1))
+
+/* Frame Engine Global Reset Register */
+#define MTK_RST_GL 0x04
+#define RST_GL_PSE BIT(0)
+
+/* Frame Engine Interrupt Status Register */
+#define MTK_INT_STATUS2 0x08
+#define MTK_GDM1_AF BIT(28)
+#define MTK_GDM2_AF BIT(29)
+
+/* Frame Engine Interrupt Grouping Register */
+#define MTK_FE_INT_GRP 0x20
+
+/* CDMP Exgress Control Register */
+#define MTK_CDMP_EG_CTRL 0x404
+
+/* GDM Exgress Control Register */
+#define MTK_GDMA_FWD_CFG(x) (0x500 + (x * 0x1000))
+#define MTK_GDMA_ICS_EN BIT(22)
+#define MTK_GDMA_TCS_EN BIT(21)
+#define MTK_GDMA_UCS_EN BIT(20)
+
+/* Unicast Filter MAC Address Register - Low */
+#define MTK_GDMA_MAC_ADRL(x) (0x508 + (x * 0x1000))
+
+/* Unicast Filter MAC Address Register - High */
+#define MTK_GDMA_MAC_ADRH(x) (0x50C + (x * 0x1000))
+
+/* QDMA TX Queue Configuration Registers */
+#define MTK_QTX_CFG(x) (0x1800 + (x * 0x10))
+#define QDMA_RES_THRES 4
+
+/* QDMA TX Queue Scheduler Registers */
+#define MTK_QTX_SCH(x) (0x1804 + (x * 0x10))
+
+/* QDMA RX Base Pointer Register */
+#define MTK_QRX_BASE_PTR0 0x1900
+
+/* QDMA RX Maximum Count Register */
+#define MTK_QRX_MAX_CNT0 0x1904
+
+/* QDMA RX CPU Pointer Register */
+#define MTK_QRX_CRX_IDX0 0x1908
+
+/* QDMA RX DMA Pointer Register */
+#define MTK_QRX_DRX_IDX0 0x190C
+
+/* QDMA Global Configuration Register */
+#define MTK_QDMA_GLO_CFG 0x1A04
+#define MTK_RX_2B_OFFSET BIT(31)
+#define MTK_RX_BT_32DWORDS (3 << 11)
+#define MTK_TX_WB_DDONE BIT(6)
+#define MTK_DMA_SIZE_16DWORDS (2 << 4)
+#define MTK_RX_DMA_BUSY BIT(3)
+#define MTK_TX_DMA_BUSY BIT(1)
+#define MTK_RX_DMA_EN BIT(2)
+#define MTK_TX_DMA_EN BIT(0)
+#define MTK_DMA_BUSY_TIMEOUT HZ
+
+/* QDMA Reset Index Register */
+#define MTK_QDMA_RST_IDX 0x1A08
+#define MTK_PST_DRX_IDX0 BIT(16)
+
+/* QDMA Delay Interrupt Register */
+#define MTK_QDMA_DELAY_INT 0x1A0C
+
+/* QDMA Flow Control Register */
+#define MTK_QDMA_FC_THRES 0x1A10
+#define FC_THRES_DROP_MODE BIT(20)
+#define FC_THRES_DROP_EN (7 << 16)
+#define FC_THRES_MIN 0x4444
+
+/* QDMA Interrupt Status Register */
+#define MTK_QMTK_INT_STATUS 0x1A18
+#define MTK_RX_DONE_INT1 BIT(17)
+#define MTK_RX_DONE_INT0 BIT(16)
+#define MTK_TX_DONE_INT3 BIT(3)
+#define MTK_TX_DONE_INT2 BIT(2)
+#define MTK_TX_DONE_INT1 BIT(1)
+#define MTK_TX_DONE_INT0 BIT(0)
+#define MTK_RX_DONE_INT (MTK_RX_DONE_INT0 | MTK_RX_DONE_INT1)
+#define MTK_TX_DONE_INT (MTK_TX_DONE_INT0 | MTK_TX_DONE_INT1 | \
+ MTK_TX_DONE_INT2 | MTK_TX_DONE_INT3)
+
+/* QDMA Interrupt Status Register */
+#define MTK_QDMA_INT_MASK 0x1A1C
+
+/* QDMA Interrupt Mask Register */
+#define MTK_QDMA_HRED2 0x1A44
+
+/* QDMA TX Forward CPU Pointer Register */
+#define MTK_QTX_CTX_PTR 0x1B00
+
+/* QDMA TX Forward DMA Pointer Register */
+#define MTK_QTX_DTX_PTR 0x1B04
+
+/* QDMA TX Release CPU Pointer Register */
+#define MTK_QTX_CRX_PTR 0x1B10
+
+/* QDMA TX Release DMA Pointer Register */
+#define MTK_QTX_DRX_PTR 0x1B14
+
+/* QDMA FQ Head Pointer Register */
+#define MTK_QDMA_FQ_HEAD 0x1B20
+
+/* QDMA FQ Head Pointer Register */
+#define MTK_QDMA_FQ_TAIL 0x1B24
+
+/* QDMA FQ Free Page Counter Register */
+#define MTK_QDMA_FQ_CNT 0x1B28
+
+/* QDMA FQ Free Page Buffer Length Register */
+#define MTK_QDMA_FQ_BLEN 0x1B2C
+
+/* GMA1 Received Good Byte Count Register */
+#define MTK_GDM1_TX_GBCNT 0x2400
+#define MTK_STAT_OFFSET 0x40
+
+/* QDMA descriptor txd4 */
+#define TX_DMA_CHKSUM (0x7 << 29)
+#define TX_DMA_TSO BIT(28)
+#define TX_DMA_FPORT_SHIFT 25
+#define TX_DMA_FPORT_MASK 0x7
+#define TX_DMA_INS_VLAN BIT(16)
+
+/* QDMA descriptor txd3 */
+#define TX_DMA_OWNER_CPU BIT(31)
+#define TX_DMA_LS0 BIT(30)
+#define TX_DMA_PLEN0(_x) (((_x) & MTK_TX_DMA_BUF_LEN) << 16)
+#define TX_DMA_SWC BIT(14)
+#define TX_DMA_SDL(_x) (((_x) & 0x3fff) << 16)
+
+/* QDMA descriptor rxd2 */
+#define RX_DMA_DONE BIT(31)
+#define RX_DMA_PLEN0(_x) (((_x) & 0x3fff) << 16)
+#define RX_DMA_GET_PLEN0(_x) (((_x) >> 16) & 0x3fff)
+
+/* QDMA descriptor rxd3 */
+#define RX_DMA_VID(_x) ((_x) & 0xfff)
+
+/* QDMA descriptor rxd4 */
+#define RX_DMA_L4_VALID BIT(24)
+#define RX_DMA_FPORT_SHIFT 19
+#define RX_DMA_FPORT_MASK 0x7
+
+/* PHY Indirect Access Control registers */
+#define MTK_PHY_IAC 0x10004
+#define PHY_IAC_ACCESS BIT(31)
+#define PHY_IAC_READ BIT(19)
+#define PHY_IAC_WRITE BIT(18)
+#define PHY_IAC_START BIT(16)
+#define PHY_IAC_ADDR_SHIFT 20
+#define PHY_IAC_REG_SHIFT 25
+#define PHY_IAC_TIMEOUT HZ
+
+/* Mac control registers */
+#define MTK_MAC_MCR(x) (0x10100 + (x * 0x100))
+#define MAC_MCR_MAX_RX_1536 BIT(24)
+#define MAC_MCR_IPG_CFG (BIT(18) | BIT(16))
+#define MAC_MCR_FORCE_MODE BIT(15)
+#define MAC_MCR_TX_EN BIT(14)
+#define MAC_MCR_RX_EN BIT(13)
+#define MAC_MCR_BACKOFF_EN BIT(9)
+#define MAC_MCR_BACKPR_EN BIT(8)
+#define MAC_MCR_FORCE_RX_FC BIT(5)
+#define MAC_MCR_FORCE_TX_FC BIT(4)
+#define MAC_MCR_SPEED_1000 BIT(3)
+#define MAC_MCR_SPEED_100 BIT(2)
+#define MAC_MCR_FORCE_DPX BIT(1)
+#define MAC_MCR_FORCE_LINK BIT(0)
+#define MAC_MCR_FIXED_LINK (MAC_MCR_MAX_RX_1536 | MAC_MCR_IPG_CFG | \
+ MAC_MCR_FORCE_MODE | MAC_MCR_TX_EN | \
+ MAC_MCR_RX_EN | MAC_MCR_BACKOFF_EN | \
+ MAC_MCR_BACKPR_EN | MAC_MCR_FORCE_RX_FC | \
+ MAC_MCR_FORCE_TX_FC | MAC_MCR_SPEED_1000 | \
+ MAC_MCR_FORCE_DPX | MAC_MCR_FORCE_LINK)
+
+/* GPIO port control registers for GMAC 2*/
+#define GPIO_OD33_CTRL8 0x4c0
+#define GPIO_BIAS_CTRL 0xed0
+#define GPIO_DRV_SEL10 0xf00
+
+/* ethernet subsystem config register */
+#define ETHSYS_SYSCFG0 0x14
+#define SYSCFG0_GE_MASK 0x3
+#define SYSCFG0_GE_MODE(x, y) (x << (12 + (y * 2)))
+
+struct mtk_rx_dma {
+ unsigned int rxd1;
+ unsigned int rxd2;
+ unsigned int rxd3;
+ unsigned int rxd4;
+} __packed __aligned(4);
+
+struct mtk_tx_dma {
+ unsigned int txd1;
+ unsigned int txd2;
+ unsigned int txd3;
+ unsigned int txd4;
+} __packed __aligned(4);
+
+struct mtk_eth;
+struct mtk_mac;
+
+/* struct mtk_hw_stats - the structure that holds the traffic statistics.
+ * @stats_lock: make sure that stats operations are atomic
+ * @reg_offset: the status register offset of the SoC
+ * @syncp: the refcount
+ *
+ * All of the supported SoCs have hardware counters for traffic statistics.
+ * Whenever the status IRQ triggers we can read the latest stats from these
+ * counters and store them in this struct.
+ */
+struct mtk_hw_stats {
+ u64 tx_bytes;
+ u64 tx_packets;
+ u64 tx_skip;
+ u64 tx_collisions;
+ u64 rx_bytes;
+ u64 rx_packets;
+ u64 rx_overflow;
+ u64 rx_fcs_errors;
+ u64 rx_short_errors;
+ u64 rx_long_errors;
+ u64 rx_checksum_errors;
+ u64 rx_flow_control_packets;
+
+ spinlock_t stats_lock;
+ u32 reg_offset;
+ struct u64_stats_sync syncp;
+};
+
+/* PDMA descriptor can point at 1-2 segments. This enum allows us to track how
+ * memory was allocated so that it can be freed properly
+ */
+enum mtk_tx_flags {
+ MTK_TX_FLAGS_SINGLE0 = 0x01,
+ MTK_TX_FLAGS_PAGE0 = 0x02,
+};
+
+/* struct mtk_tx_buf - This struct holds the pointers to the memory pointed at
+ * by the TX descriptor s
+ * @skb: The SKB pointer of the packet being sent
+ * @dma_addr0: The base addr of the first segment
+ * @dma_len0: The length of the first segment
+ * @dma_addr1: The base addr of the second segment
+ * @dma_len1: The length of the second segment
+ */
+struct mtk_tx_buf {
+ struct sk_buff *skb;
+ u32 flags;
+ DEFINE_DMA_UNMAP_ADDR(dma_addr0);
+ DEFINE_DMA_UNMAP_LEN(dma_len0);
+ DEFINE_DMA_UNMAP_ADDR(dma_addr1);
+ DEFINE_DMA_UNMAP_LEN(dma_len1);
+};
+
+/* struct mtk_tx_ring - This struct holds info describing a TX ring
+ * @dma: The descriptor ring
+ * @buf: The memory pointed at by the ring
+ * @phys: The physical addr of tx_buf
+ * @next_free: Pointer to the next free descriptor
+ * @last_free: Pointer to the last free descriptor
+ * @thresh: The threshold of minimum amount of free descriptors
+ * @free_count: QDMA uses a linked list. Track how many free descriptors
+ * are present
+ */
+struct mtk_tx_ring {
+ struct mtk_tx_dma *dma;
+ struct mtk_tx_buf *buf;
+ dma_addr_t phys;
+ struct mtk_tx_dma *next_free;
+ struct mtk_tx_dma *last_free;
+ u16 thresh;
+ atomic_t free_count;
+};
+
+/* struct mtk_rx_ring - This struct holds info describing a RX ring
+ * @dma: The descriptor ring
+ * @data: The memory pointed at by the ring
+ * @phys: The physical addr of rx_buf
+ * @frag_size: How big can each fragment be
+ * @buf_size: The size of each packet buffer
+ * @calc_idx: The current head of ring
+ */
+struct mtk_rx_ring {
+ struct mtk_rx_dma *dma;
+ u8 **data;
+ dma_addr_t phys;
+ u16 frag_size;
+ u16 buf_size;
+ u16 calc_idx;
+};
+
+/* currently no SoC has more than 2 macs */
+#define MTK_MAX_DEVS 2
+
+/* struct mtk_eth - This is the main datasructure for holding the state
+ * of the driver
+ * @dev: The device pointer
+ * @base: The mapped register i/o base
+ * @page_lock: Make sure that register operations are atomic
+ * @dummy_dev: we run 2 netdevs on 1 physical DMA ring and need a
+ * dummy for NAPI to work
+ * @netdev: The netdev instances
+ * @mac: Each netdev is linked to a physical MAC
+ * @irq: The IRQ that we are using
+ * @msg_enable: Ethtool msg level
+ * @ethsys: The register map pointing at the range used to setup
+ * MII modes
+ * @pctl: The register map pointing at the range used to setup
+ * GMAC port drive/slew values
+ * @dma_refcnt: track how many netdevs are using the DMA engine
+ * @tx_ring: Pointer to the memore holding info about the TX ring
+ * @rx_ring: Pointer to the memore holding info about the RX ring
+ * @rx_napi: The NAPI struct
+ * @scratch_ring: Newer SoCs need memory for a second HW managed TX ring
+ * @scratch_head: The scratch memory that scratch_ring points to.
+ * @clk_ethif: The ethif clock
+ * @clk_esw: The switch clock
+ * @clk_gp1: The gmac1 clock
+ * @clk_gp2: The gmac2 clock
+ * @mii_bus: If there is a bus we need to create an instance for it
+ */
+
+struct mtk_eth {
+ struct device *dev;
+ void __iomem *base;
+ struct reset_control *rstc;
+ spinlock_t page_lock;
+ struct net_device dummy_dev;
+ struct net_device *netdev[MTK_MAX_DEVS];
+ struct mtk_mac *mac[MTK_MAX_DEVS];
+ int irq;
+ u32 msg_enable;
+ unsigned long sysclk;
+ struct regmap *ethsys;
+ struct regmap *pctl;
+ atomic_t dma_refcnt;
+ struct mtk_tx_ring tx_ring;
+ struct mtk_rx_ring rx_ring;
+ struct napi_struct rx_napi;
+ struct mtk_tx_dma *scratch_ring;
+ void *scratch_head;
+ struct clk *clk_ethif;
+ struct clk *clk_esw;
+ struct clk *clk_gp1;
+ struct clk *clk_gp2;
+ struct mii_bus *mii_bus;
+};
+
+/* struct mtk_mac - the structure that holds the info about the MACs of the
+ * SoC
+ * @id: The number of the MAC
+ * @of_node: Our devicetree node
+ * @hw: Backpointer to our main datastruture
+ * @hw_stats: Packet statistics counter
+ * @phy_dev: The attached PHY if available
+ * @pending_work: The workqueue used to reset the dma ring
+ */
+struct mtk_mac {
+ int id;
+ struct device_node *of_node;
+ struct mtk_eth *hw;
+ struct mtk_hw_stats *hw_stats;
+ struct phy_device *phy_dev;
+ struct work_struct pending_work;
+};
+
+/* the struct describing the SoC. these are declared in the soc_xyz.c files */
+extern const struct of_device_id of_mtk_match[];
+
+/* read the hardware status register */
+void mtk_stats_update_mac(struct mtk_mac *mac);
+
+void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg);
+u32 mtk_r32(struct mtk_eth *eth, unsigned reg);
+
+#endif /* MTK_ETH_H */
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c
index e0946ab..c0d7b72 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c
@@ -276,7 +276,8 @@
static u32 mlx4_en_free_tx_desc(struct mlx4_en_priv *priv,
struct mlx4_en_tx_ring *ring,
- int index, u8 owner, u64 timestamp)
+ int index, u8 owner, u64 timestamp,
+ int napi_mode)
{
struct mlx4_en_tx_info *tx_info = &ring->tx_info[index];
struct mlx4_en_tx_desc *tx_desc = ring->buf + index * TXBB_SIZE;
@@ -347,7 +348,8 @@
}
}
}
- dev_consume_skb_any(skb);
+ napi_consume_skb(skb, napi_mode);
+
return tx_info->nr_txbb;
}
@@ -371,7 +373,8 @@
while (ring->cons != ring->prod) {
ring->last_nr_txbb = mlx4_en_free_tx_desc(priv, ring,
ring->cons & ring->size_mask,
- !!(ring->cons & ring->size), 0);
+ !!(ring->cons & ring->size), 0,
+ 0 /* Non-NAPI caller */);
ring->cons += ring->last_nr_txbb;
cnt++;
}
@@ -385,7 +388,7 @@
}
static bool mlx4_en_process_tx_cq(struct net_device *dev,
- struct mlx4_en_cq *cq)
+ struct mlx4_en_cq *cq, int napi_budget)
{
struct mlx4_en_priv *priv = netdev_priv(dev);
struct mlx4_cq *mcq = &cq->mcq;
@@ -451,7 +454,7 @@
last_nr_txbb = mlx4_en_free_tx_desc(
priv, ring, ring_index,
!!((ring_cons + txbbs_skipped) &
- ring->size), timestamp);
+ ring->size), timestamp, napi_budget);
mlx4_en_stamp_wqe(priv, ring, stamp_index,
!!((ring_cons + txbbs_stamp) &
@@ -511,7 +514,7 @@
struct mlx4_en_priv *priv = netdev_priv(dev);
int clean_complete;
- clean_complete = mlx4_en_process_tx_cq(dev, cq);
+ clean_complete = mlx4_en_process_tx_cq(dev, cq, budget);
if (!clean_complete)
return budget;
diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
index 25ce1b0..cd9b2b2 100644
--- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
+++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
@@ -3141,7 +3141,7 @@
case QP_TRANS_RTS2RTS:
case QP_TRANS_SQD2SQD:
case QP_TRANS_SQD2RTS:
- if (slave != mlx4_master_func_num(dev))
+ if (slave != mlx4_master_func_num(dev)) {
if (optpar & MLX4_QP_OPTPAR_PRIMARY_ADDR_PATH) {
port = (qp_ctx->pri_path.sched_queue >> 6 & 1) + 1;
if (dev->caps.port_mask[port] != MLX4_PORT_TYPE_IB)
@@ -3160,6 +3160,7 @@
if (qp_ctx->alt_path.mgid_index >= num_gids)
return -EINVAL;
}
+ }
break;
default:
break;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/Makefile
index 11b592d..4fc45ee 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/Makefile
+++ b/drivers/net/ethernet/mellanox/mlx5/core/Makefile
@@ -6,6 +6,6 @@
mlx5_core-$(CONFIG_MLX5_CORE_EN) += wq.o eswitch.o \
en_main.o en_fs.o en_ethtool.o en_tx.o en_rx.o \
- en_txrx.o en_clock.o vxlan.o
+ en_txrx.o en_clock.o vxlan.o en_tc.o
mlx5_core-$(CONFIG_MLX5_CORE_EN_DCB) += en_dcbnl.o
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h
index dbc2fb8..0cb4a09 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h
@@ -43,6 +43,7 @@
#include <linux/mlx5/port.h>
#include <linux/mlx5/vport.h>
#include <linux/mlx5/transobj.h>
+#include <linux/rhashtable.h>
#include "wq.h"
#include "mlx5_core.h"
@@ -527,8 +528,16 @@
struct mlx5_flow_group **g;
};
+struct mlx5e_tc_flow_table {
+ struct mlx5_flow_table *t;
+
+ struct rhashtable_params ht_params;
+ struct rhashtable ht;
+};
+
struct mlx5e_flow_tables {
struct mlx5_flow_namespace *ns;
+ struct mlx5e_tc_flow_table tc;
struct mlx5e_flow_table vlan;
struct mlx5e_flow_table main;
};
@@ -620,7 +629,7 @@
void mlx5e_completion_event(struct mlx5_core_cq *mcq);
void mlx5e_cq_error_event(struct mlx5_core_cq *mcq, enum mlx5_event event);
int mlx5e_napi_poll(struct napi_struct *napi, int budget);
-bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq);
+bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget);
int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget);
bool mlx5e_post_rx_wqes(struct mlx5e_rq *rq);
struct mlx5_cqe64 *mlx5e_get_cqe(struct mlx5e_cq *cq);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c
index 80d81ab..d00a242 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c
@@ -1041,7 +1041,7 @@
int err;
ft->num_groups = 0;
- ft->t = mlx5_create_flow_table(priv->fts.ns, 0, MLX5E_MAIN_TABLE_SIZE);
+ ft->t = mlx5_create_flow_table(priv->fts.ns, 1, MLX5E_MAIN_TABLE_SIZE);
if (IS_ERR(ft->t)) {
err = PTR_ERR(ft->t);
@@ -1150,7 +1150,7 @@
int err;
ft->num_groups = 0;
- ft->t = mlx5_create_flow_table(priv->fts.ns, 0, MLX5E_VLAN_TABLE_SIZE);
+ ft->t = mlx5_create_flow_table(priv->fts.ns, 1, MLX5E_VLAN_TABLE_SIZE);
if (IS_ERR(ft->t)) {
err = PTR_ERR(ft->t);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
index 19e5dae..ac58078 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
@@ -30,9 +30,12 @@
* SOFTWARE.
*/
+#include <net/tc_act/tc_gact.h>
+#include <net/pkt_cls.h>
#include <linux/mlx5/fs.h>
#include <net/vxlan.h>
#include "en.h"
+#include "en_tc.h"
#include "eswitch.h"
#include "vxlan.h"
@@ -1883,7 +1886,25 @@
static int mlx5e_ndo_setup_tc(struct net_device *dev, u32 handle,
__be16 proto, struct tc_to_netdev *tc)
{
- if (handle != TC_H_ROOT || tc->type != TC_SETUP_MQPRIO)
+ struct mlx5e_priv *priv = netdev_priv(dev);
+
+ if (TC_H_MAJ(handle) != TC_H_MAJ(TC_H_INGRESS))
+ goto mqprio;
+
+ switch (tc->type) {
+ case TC_SETUP_CLSFLOWER:
+ switch (tc->cls_flower->command) {
+ case TC_CLSFLOWER_REPLACE:
+ return mlx5e_configure_flower(priv, proto, tc->cls_flower);
+ case TC_CLSFLOWER_DESTROY:
+ return mlx5e_delete_flower(priv, tc->cls_flower);
+ }
+ default:
+ return -EOPNOTSUPP;
+ }
+
+mqprio:
+ if (tc->type != TC_SETUP_MQPRIO)
return -EINVAL;
return mlx5e_setup_tc(dev, tc->tc);
@@ -1968,6 +1989,13 @@
mlx5e_disable_vlan_filter(priv);
}
+ if ((changes & NETIF_F_HW_TC) && !(features & NETIF_F_HW_TC) &&
+ mlx5e_tc_num_filters(priv)) {
+ netdev_err(netdev,
+ "Active offloaded tc filters, can't turn hw_tc_offload off\n");
+ return -EINVAL;
+ }
+
return err;
}
@@ -2375,6 +2403,13 @@
if (!priv->params.lro_en)
netdev->features &= ~NETIF_F_LRO;
+#define FT_CAP(f) MLX5_CAP_FLOWTABLE(mdev, flow_table_properties_nic_receive.f)
+ if (FT_CAP(flow_modify_en) &&
+ FT_CAP(modify_root) &&
+ FT_CAP(identified_miss_table_mode) &&
+ FT_CAP(flow_table_modify))
+ priv->netdev->hw_features |= NETIF_F_HW_TC;
+
netdev->features |= NETIF_F_HIGHDMA;
netdev->priv_flags |= IFF_UNICAST_FLT;
@@ -2496,6 +2531,10 @@
mlx5e_vxlan_init(priv);
+ err = mlx5e_tc_init(priv);
+ if (err)
+ goto err_destroy_flow_tables;
+
#ifdef CONFIG_MLX5_CORE_EN_DCB
mlx5e_dcbnl_ieee_setets_core(priv, &priv->params.ets);
#endif
@@ -2503,7 +2542,7 @@
err = register_netdev(netdev);
if (err) {
mlx5_core_err(mdev, "register_netdev failed, %d\n", err);
- goto err_destroy_flow_tables;
+ goto err_tc_cleanup;
}
if (mlx5e_vxlan_allowed(mdev))
@@ -2514,6 +2553,9 @@
return priv;
+err_tc_cleanup:
+ mlx5e_tc_cleanup(priv);
+
err_destroy_flow_tables:
mlx5e_destroy_flow_tables(priv);
@@ -2561,6 +2603,7 @@
mlx5e_disable_async_events(priv);
flush_scheduled_work();
unregister_netdev(netdev);
+ mlx5e_tc_cleanup(priv);
mlx5e_vxlan_cleanup(priv);
mlx5e_destroy_flow_tables(priv);
mlx5e_destroy_tirs(priv);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
index 884ed19..58d4e2f 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
@@ -35,6 +35,7 @@
#include <linux/tcp.h>
#include <net/busy_poll.h>
#include "en.h"
+#include "en_tc.h"
static inline bool mlx5e_rx_hw_stamp(struct mlx5e_tstamp *tstamp)
{
@@ -224,6 +225,8 @@
if (cqe_has_vlan(cqe))
__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
be16_to_cpu(cqe->vlan_info));
+
+ skb->mark = be32_to_cpu(cqe->sop_drop_qpn) & MLX5E_TC_FLOW_ID_MASK;
}
int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
new file mode 100644
index 0000000..b3de09f
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
@@ -0,0 +1,429 @@
+/*
+ * Copyright (c) 2016, Mellanox Technologies. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <net/flow_dissector.h>
+#include <net/pkt_cls.h>
+#include <net/tc_act/tc_gact.h>
+#include <net/tc_act/tc_skbedit.h>
+#include <linux/mlx5/fs.h>
+#include <linux/mlx5/device.h>
+#include <linux/rhashtable.h>
+#include "en.h"
+#include "en_tc.h"
+
+struct mlx5e_tc_flow {
+ struct rhash_head node;
+ u64 cookie;
+ struct mlx5_flow_rule *rule;
+};
+
+#define MLX5E_TC_FLOW_TABLE_NUM_ENTRIES 1024
+#define MLX5E_TC_FLOW_TABLE_NUM_GROUPS 4
+
+static struct mlx5_flow_rule *mlx5e_tc_add_flow(struct mlx5e_priv *priv,
+ u32 *match_c, u32 *match_v,
+ u32 action, u32 flow_tag)
+{
+ struct mlx5_flow_destination dest = {
+ .type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE,
+ {.ft = priv->fts.vlan.t},
+ };
+ struct mlx5_flow_rule *rule;
+ bool table_created = false;
+
+ if (IS_ERR_OR_NULL(priv->fts.tc.t)) {
+ priv->fts.tc.t =
+ mlx5_create_auto_grouped_flow_table(priv->fts.ns, 0,
+ MLX5E_TC_FLOW_TABLE_NUM_ENTRIES,
+ MLX5E_TC_FLOW_TABLE_NUM_GROUPS);
+ if (IS_ERR(priv->fts.tc.t)) {
+ netdev_err(priv->netdev,
+ "Failed to create tc offload table\n");
+ return ERR_CAST(priv->fts.tc.t);
+ }
+
+ table_created = true;
+ }
+
+ rule = mlx5_add_flow_rule(priv->fts.tc.t, MLX5_MATCH_OUTER_HEADERS,
+ match_c, match_v,
+ action, flow_tag,
+ action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST ? &dest : NULL);
+
+ if (IS_ERR(rule) && table_created) {
+ mlx5_destroy_flow_table(priv->fts.tc.t);
+ priv->fts.tc.t = NULL;
+ }
+
+ return rule;
+}
+
+static void mlx5e_tc_del_flow(struct mlx5e_priv *priv,
+ struct mlx5_flow_rule *rule)
+{
+ mlx5_del_flow_rule(rule);
+
+ if (!mlx5e_tc_num_filters(priv)) {
+ mlx5_destroy_flow_table(priv->fts.tc.t);
+ priv->fts.tc.t = NULL;
+ }
+}
+
+static int parse_cls_flower(struct mlx5e_priv *priv,
+ u32 *match_c, u32 *match_v,
+ struct tc_cls_flower_offload *f)
+{
+ void *headers_c = MLX5_ADDR_OF(fte_match_param, match_c, outer_headers);
+ void *headers_v = MLX5_ADDR_OF(fte_match_param, match_v, outer_headers);
+ u16 addr_type = 0;
+ u8 ip_proto = 0;
+
+ if (f->dissector->used_keys &
+ ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
+ BIT(FLOW_DISSECTOR_KEY_BASIC) |
+ BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
+ BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
+ BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
+ BIT(FLOW_DISSECTOR_KEY_PORTS))) {
+ netdev_warn(priv->netdev, "Unsupported key used: 0x%x\n",
+ f->dissector->used_keys);
+ return -EOPNOTSUPP;
+ }
+
+ if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_CONTROL)) {
+ struct flow_dissector_key_control *key =
+ skb_flow_dissector_target(f->dissector,
+ FLOW_DISSECTOR_KEY_BASIC,
+ f->key);
+ addr_type = key->addr_type;
+ }
+
+ if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
+ struct flow_dissector_key_basic *key =
+ skb_flow_dissector_target(f->dissector,
+ FLOW_DISSECTOR_KEY_BASIC,
+ f->key);
+ struct flow_dissector_key_basic *mask =
+ skb_flow_dissector_target(f->dissector,
+ FLOW_DISSECTOR_KEY_BASIC,
+ f->mask);
+ ip_proto = key->ip_proto;
+
+ MLX5_SET(fte_match_set_lyr_2_4, headers_c, ethertype,
+ ntohs(mask->n_proto));
+ MLX5_SET(fte_match_set_lyr_2_4, headers_v, ethertype,
+ ntohs(key->n_proto));
+
+ MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_protocol,
+ mask->ip_proto);
+ MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_protocol,
+ key->ip_proto);
+ }
+
+ if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
+ struct flow_dissector_key_eth_addrs *key =
+ skb_flow_dissector_target(f->dissector,
+ FLOW_DISSECTOR_KEY_ETH_ADDRS,
+ f->key);
+ struct flow_dissector_key_eth_addrs *mask =
+ skb_flow_dissector_target(f->dissector,
+ FLOW_DISSECTOR_KEY_ETH_ADDRS,
+ f->mask);
+
+ ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
+ dmac_47_16),
+ mask->dst);
+ ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
+ dmac_47_16),
+ key->dst);
+
+ ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
+ smac_47_16),
+ mask->src);
+ ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
+ smac_47_16),
+ key->src);
+ }
+
+ if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
+ struct flow_dissector_key_ipv4_addrs *key =
+ skb_flow_dissector_target(f->dissector,
+ FLOW_DISSECTOR_KEY_IPV4_ADDRS,
+ f->key);
+ struct flow_dissector_key_ipv4_addrs *mask =
+ skb_flow_dissector_target(f->dissector,
+ FLOW_DISSECTOR_KEY_IPV4_ADDRS,
+ f->mask);
+
+ memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
+ src_ipv4_src_ipv6.ipv4_layout.ipv4),
+ &mask->src, sizeof(mask->src));
+ memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
+ src_ipv4_src_ipv6.ipv4_layout.ipv4),
+ &key->src, sizeof(key->src));
+ memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
+ dst_ipv4_dst_ipv6.ipv4_layout.ipv4),
+ &mask->dst, sizeof(mask->dst));
+ memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
+ dst_ipv4_dst_ipv6.ipv4_layout.ipv4),
+ &key->dst, sizeof(key->dst));
+ }
+
+ if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
+ struct flow_dissector_key_ipv6_addrs *key =
+ skb_flow_dissector_target(f->dissector,
+ FLOW_DISSECTOR_KEY_IPV6_ADDRS,
+ f->key);
+ struct flow_dissector_key_ipv6_addrs *mask =
+ skb_flow_dissector_target(f->dissector,
+ FLOW_DISSECTOR_KEY_IPV6_ADDRS,
+ f->mask);
+
+ memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
+ src_ipv4_src_ipv6.ipv6_layout.ipv6),
+ &mask->src, sizeof(mask->src));
+ memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
+ src_ipv4_src_ipv6.ipv6_layout.ipv6),
+ &key->src, sizeof(key->src));
+
+ memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
+ dst_ipv4_dst_ipv6.ipv6_layout.ipv6),
+ &mask->dst, sizeof(mask->dst));
+ memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
+ dst_ipv4_dst_ipv6.ipv6_layout.ipv6),
+ &key->dst, sizeof(key->dst));
+ }
+
+ if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_PORTS)) {
+ struct flow_dissector_key_ports *key =
+ skb_flow_dissector_target(f->dissector,
+ FLOW_DISSECTOR_KEY_PORTS,
+ f->key);
+ struct flow_dissector_key_ports *mask =
+ skb_flow_dissector_target(f->dissector,
+ FLOW_DISSECTOR_KEY_PORTS,
+ f->mask);
+ switch (ip_proto) {
+ case IPPROTO_TCP:
+ MLX5_SET(fte_match_set_lyr_2_4, headers_c,
+ tcp_sport, ntohs(mask->src));
+ MLX5_SET(fte_match_set_lyr_2_4, headers_v,
+ tcp_sport, ntohs(key->src));
+
+ MLX5_SET(fte_match_set_lyr_2_4, headers_c,
+ tcp_dport, ntohs(mask->dst));
+ MLX5_SET(fte_match_set_lyr_2_4, headers_v,
+ tcp_dport, ntohs(key->dst));
+ break;
+
+ case IPPROTO_UDP:
+ MLX5_SET(fte_match_set_lyr_2_4, headers_c,
+ udp_sport, ntohs(mask->src));
+ MLX5_SET(fte_match_set_lyr_2_4, headers_v,
+ udp_sport, ntohs(key->src));
+
+ MLX5_SET(fte_match_set_lyr_2_4, headers_c,
+ udp_dport, ntohs(mask->dst));
+ MLX5_SET(fte_match_set_lyr_2_4, headers_v,
+ udp_dport, ntohs(key->dst));
+ break;
+ default:
+ netdev_err(priv->netdev,
+ "Only UDP and TCP transport are supported\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int parse_tc_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
+ u32 *action, u32 *flow_tag)
+{
+ const struct tc_action *a;
+
+ if (tc_no_actions(exts))
+ return -EINVAL;
+
+ *flow_tag = MLX5_FS_DEFAULT_FLOW_TAG;
+ *action = 0;
+
+ tc_for_each_action(a, exts) {
+ /* Only support a single action per rule */
+ if (*action)
+ return -EINVAL;
+
+ if (is_tcf_gact_shot(a)) {
+ *action |= MLX5_FLOW_CONTEXT_ACTION_DROP;
+ continue;
+ }
+
+ if (is_tcf_skbedit_mark(a)) {
+ u32 mark = tcf_skbedit_mark(a);
+
+ if (mark & ~MLX5E_TC_FLOW_ID_MASK) {
+ netdev_warn(priv->netdev, "Bad flow mark - only 16 bit is supported: 0x%x\n",
+ mark);
+ return -EINVAL;
+ }
+
+ *flow_tag = mark;
+ *action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
+ continue;
+ }
+
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int mlx5e_configure_flower(struct mlx5e_priv *priv, __be16 protocol,
+ struct tc_cls_flower_offload *f)
+{
+ struct mlx5e_tc_flow_table *tc = &priv->fts.tc;
+ u32 *match_c;
+ u32 *match_v;
+ int err = 0;
+ u32 flow_tag;
+ u32 action;
+ struct mlx5e_tc_flow *flow;
+ struct mlx5_flow_rule *old = NULL;
+
+ flow = rhashtable_lookup_fast(&tc->ht, &f->cookie,
+ tc->ht_params);
+ if (flow)
+ old = flow->rule;
+ else
+ flow = kzalloc(sizeof(*flow), GFP_KERNEL);
+
+ match_c = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL);
+ match_v = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL);
+ if (!match_c || !match_v || !flow) {
+ err = -ENOMEM;
+ goto err_free;
+ }
+
+ flow->cookie = f->cookie;
+
+ err = parse_cls_flower(priv, match_c, match_v, f);
+ if (err < 0)
+ goto err_free;
+
+ err = parse_tc_actions(priv, f->exts, &action, &flow_tag);
+ if (err < 0)
+ goto err_free;
+
+ err = rhashtable_insert_fast(&tc->ht, &flow->node,
+ tc->ht_params);
+ if (err)
+ goto err_free;
+
+ flow->rule = mlx5e_tc_add_flow(priv, match_c, match_v, action,
+ flow_tag);
+ if (IS_ERR(flow->rule)) {
+ err = PTR_ERR(flow->rule);
+ goto err_hash_del;
+ }
+
+ if (old)
+ mlx5e_tc_del_flow(priv, old);
+
+ goto out;
+
+err_hash_del:
+ rhashtable_remove_fast(&tc->ht, &flow->node, tc->ht_params);
+
+err_free:
+ if (!old)
+ kfree(flow);
+out:
+ kfree(match_c);
+ kfree(match_v);
+ return err;
+}
+
+int mlx5e_delete_flower(struct mlx5e_priv *priv,
+ struct tc_cls_flower_offload *f)
+{
+ struct mlx5e_tc_flow *flow;
+ struct mlx5e_tc_flow_table *tc = &priv->fts.tc;
+
+ flow = rhashtable_lookup_fast(&tc->ht, &f->cookie,
+ tc->ht_params);
+ if (!flow)
+ return -EINVAL;
+
+ rhashtable_remove_fast(&tc->ht, &flow->node, tc->ht_params);
+
+ mlx5e_tc_del_flow(priv, flow->rule);
+
+ kfree(flow);
+
+ return 0;
+}
+
+static const struct rhashtable_params mlx5e_tc_flow_ht_params = {
+ .head_offset = offsetof(struct mlx5e_tc_flow, node),
+ .key_offset = offsetof(struct mlx5e_tc_flow, cookie),
+ .key_len = sizeof(((struct mlx5e_tc_flow *)0)->cookie),
+ .automatic_shrinking = true,
+};
+
+int mlx5e_tc_init(struct mlx5e_priv *priv)
+{
+ struct mlx5e_tc_flow_table *tc = &priv->fts.tc;
+
+ tc->ht_params = mlx5e_tc_flow_ht_params;
+ return rhashtable_init(&tc->ht, &tc->ht_params);
+}
+
+static void _mlx5e_tc_del_flow(void *ptr, void *arg)
+{
+ struct mlx5e_tc_flow *flow = ptr;
+ struct mlx5e_priv *priv = arg;
+
+ mlx5e_tc_del_flow(priv, flow->rule);
+ kfree(flow);
+}
+
+void mlx5e_tc_cleanup(struct mlx5e_priv *priv)
+{
+ struct mlx5e_tc_flow_table *tc = &priv->fts.tc;
+
+ rhashtable_free_and_destroy(&tc->ht, _mlx5e_tc_del_flow, priv);
+
+ if (!IS_ERR_OR_NULL(priv->fts.tc.t)) {
+ mlx5_destroy_flow_table(priv->fts.tc.t);
+ priv->fts.tc.t = NULL;
+ }
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h
new file mode 100644
index 0000000..d677428
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2016, Mellanox Technologies. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __MLX5_EN_TC_H__
+#define __MLX5_EN_TC_H__
+
+#define MLX5E_TC_FLOW_ID_MASK 0x0000ffff
+
+int mlx5e_tc_init(struct mlx5e_priv *priv);
+void mlx5e_tc_cleanup(struct mlx5e_priv *priv);
+
+int mlx5e_configure_flower(struct mlx5e_priv *priv, __be16 protocol,
+ struct tc_cls_flower_offload *f);
+int mlx5e_delete_flower(struct mlx5e_priv *priv,
+ struct tc_cls_flower_offload *f);
+
+static inline int mlx5e_tc_num_filters(struct mlx5e_priv *priv)
+{
+ return atomic_read(&priv->fts.tc.ht.nelems);
+}
+
+#endif /* __MLX5_EN_TC_H__ */
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c
index 94a14f8..1ffc7cb 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c
@@ -339,7 +339,7 @@
return mlx5e_sq_xmit(sq, skb);
}
-bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq)
+bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget)
{
struct mlx5e_sq *sq;
u32 dma_fifo_cc;
@@ -411,7 +411,7 @@
npkts++;
nbytes += wi->num_bytes;
sqcc += wi->num_wqebbs;
- dev_kfree_skb(skb);
+ napi_consume_skb(skb, napi_budget);
} while (!last_wqe);
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c
index 66d51a7..9bb4395 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c
@@ -60,7 +60,7 @@
clear_bit(MLX5E_CHANNEL_NAPI_SCHED, &c->flags);
for (i = 0; i < c->num_tc; i++)
- busy |= mlx5e_poll_tx_cq(&c->sq[i].cq);
+ busy |= mlx5e_poll_tx_cq(&c->sq[i].cq, budget);
work_done = mlx5e_poll_rx_cq(&c->rq.cq, budget);
busy |= work_done == budget;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c
index a9894d2..f46f1db 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c
@@ -218,19 +218,22 @@
match_value);
memcpy(in_match_value, &fte->val, MLX5_ST_SZ_BYTES(fte_match_param));
- in_dests = MLX5_ADDR_OF(flow_context, in_flow_context, destination);
- list_for_each_entry(dst, &fte->node.children, node.list) {
- unsigned int id;
+ if (fte->action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) {
+ in_dests = MLX5_ADDR_OF(flow_context, in_flow_context, destination);
+ list_for_each_entry(dst, &fte->node.children, node.list) {
+ unsigned int id;
- MLX5_SET(dest_format_struct, in_dests, destination_type,
- dst->dest_attr.type);
- if (dst->dest_attr.type ==
- MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE)
- id = dst->dest_attr.ft->id;
- else
- id = dst->dest_attr.tir_num;
- MLX5_SET(dest_format_struct, in_dests, destination_id, id);
- in_dests += MLX5_ST_SZ_BYTES(dest_format_struct);
+ MLX5_SET(dest_format_struct, in_dests, destination_type,
+ dst->dest_attr.type);
+ if (dst->dest_attr.type ==
+ MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE) {
+ id = dst->dest_attr.ft->id;
+ } else {
+ id = dst->dest_attr.tir_num;
+ }
+ MLX5_SET(dest_format_struct, in_dests, destination_id, id);
+ in_dests += MLX5_ST_SZ_BYTES(dest_format_struct);
+ }
}
memset(out, 0, sizeof(out));
err = mlx5_cmd_exec_check_status(dev, in, inlen, out,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
index 6f68dba..e848d70 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
@@ -73,8 +73,8 @@
#define BY_PASS_MIN_LEVEL (KENREL_MIN_LEVEL + MLX5_BY_PASS_NUM_PRIOS +\
LEFTOVERS_MAX_FT)
-#define KERNEL_MAX_FT 2
-#define KERNEL_NUM_PRIOS 1
+#define KERNEL_MAX_FT 3
+#define KERNEL_NUM_PRIOS 2
#define KENREL_MIN_LEVEL 2
struct node_caps {
@@ -360,8 +360,8 @@
memcpy(match_value, fte->val, sizeof(fte->val));
fs_get_obj(ft, fg->node.parent);
list_del(&rule->node.list);
- fte->dests_size--;
- if (fte->dests_size) {
+ if ((fte->action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) &&
+ --fte->dests_size) {
err = mlx5_cmd_update_fte(dev, ft,
fg->id, fte);
if (err)
@@ -763,7 +763,8 @@
return NULL;
rule->node.type = FS_TYPE_FLOW_DEST;
- memcpy(&rule->dest_attr, dest, sizeof(*dest));
+ if (dest)
+ memcpy(&rule->dest_attr, dest, sizeof(*dest));
return rule;
}
@@ -785,8 +786,9 @@
/* Add dest to dests list- added as first element after the head */
tree_init_node(&rule->node, 1, del_rule);
list_add_tail(&rule->node.list, &fte->node.children);
- fte->dests_size++;
- if (fte->dests_size == 1)
+ if (dest)
+ fte->dests_size++;
+ if (fte->dests_size == 1 || !dest)
err = mlx5_cmd_create_fte(get_dev(&ft->node),
ft, fg->id, fte);
else
@@ -802,7 +804,8 @@
free_rule:
list_del(&rule->node.list);
kfree(rule);
- fte->dests_size--;
+ if (dest)
+ fte->dests_size--;
return ERR_PTR(err);
}
@@ -996,6 +999,9 @@
struct mlx5_flow_group *g;
struct mlx5_flow_rule *rule;
+ if ((action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) && !dest)
+ return ERR_PTR(-EINVAL);
+
nested_lock_ref_node(&ft->node, FS_MUTEX_GRANDPARENT);
fs_for_each_fg(g, ft)
if (compare_match_criteria(g->mask.match_criteria_enable,
diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.c b/drivers/net/ethernet/mellanox/mlxsw/pci.c
index 7992c55..7f4173c 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/pci.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/pci.c
@@ -1681,11 +1681,18 @@
static int mlxsw_pci_sw_reset(struct mlxsw_pci *mlxsw_pci)
{
+ unsigned long end;
+
mlxsw_pci_write32(mlxsw_pci, SW_RESET, MLXSW_PCI_SW_RESET_RST_BIT);
- /* Current firware does not let us know when the reset is done.
- * So we just wait here for constant time and hope for the best.
- */
- msleep(MLXSW_PCI_SW_RESET_TIMEOUT_MSECS);
+ wmb(); /* reset needs to be written before we read control register */
+ end = jiffies + msecs_to_jiffies(MLXSW_PCI_SW_RESET_TIMEOUT_MSECS);
+ do {
+ u32 val = mlxsw_pci_read32(mlxsw_pci, FW_READY);
+
+ if ((val & MLXSW_PCI_FW_READY_MASK) == MLXSW_PCI_FW_READY_MAGIC)
+ break;
+ cond_resched();
+ } while (time_before(jiffies, end));
return 0;
}
diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.h b/drivers/net/ethernet/mellanox/mlxsw/pci.h
index 9121060..d942a3e 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/pci.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/pci.h
@@ -61,6 +61,9 @@
#define MLXSW_PCI_SW_RESET 0xF0010
#define MLXSW_PCI_SW_RESET_RST_BIT BIT(0)
#define MLXSW_PCI_SW_RESET_TIMEOUT_MSECS 5000
+#define MLXSW_PCI_FW_READY 0xA1844
+#define MLXSW_PCI_FW_READY_MASK 0xFF
+#define MLXSW_PCI_FW_READY_MAGIC 0x5E
#define MLXSW_PCI_DOORBELL_SDQ_OFFSET 0x000
#define MLXSW_PCI_DOORBELL_RDQ_OFFSET 0x200
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
index 1b691d7..4b8abaf 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
@@ -123,6 +123,8 @@
#define MLXSW_SP_DEFAULT_LEARNING_INTERVAL 100
unsigned int interval; /* ms */
} fdb_notify;
+#define MLXSW_SP_MIN_AGEING_TIME 10
+#define MLXSW_SP_MAX_AGEING_TIME 1000000
#define MLXSW_SP_DEFAULT_AGEING_TIME 300
u32 ageing_time;
struct mlxsw_sp_upper master_bridge;
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
index 7b56098..e1c74ef 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
@@ -311,8 +311,13 @@
unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock_t);
u32 ageing_time = jiffies_to_msecs(ageing_jiffies) / 1000;
- if (switchdev_trans_ph_prepare(trans))
- return 0;
+ if (switchdev_trans_ph_prepare(trans)) {
+ if (ageing_time < MLXSW_SP_MIN_AGEING_TIME ||
+ ageing_time > MLXSW_SP_MAX_AGEING_TIME)
+ return -ERANGE;
+ else
+ return 0;
+ }
return mlxsw_sp_ageing_set(mlxsw_sp, ageing_time);
}
diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h
index e5604ee..fcb8e9b 100644
--- a/drivers/net/ethernet/qlogic/qed/qed.h
+++ b/drivers/net/ethernet/qlogic/qed/qed.h
@@ -303,6 +303,9 @@
bool b_int_enabled;
bool b_int_requested;
+ /* True if the driver requests for the link */
+ bool b_drv_link_init;
+
struct qed_mcp_info *mcp_info;
struct qed_hw_cid_data *p_tx_cids;
diff --git a/drivers/net/ethernet/qlogic/qed/qed_hsi.h b/drivers/net/ethernet/qlogic/qed/qed_hsi.h
index 236db8a..a368f5e 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_hsi.h
+++ b/drivers/net/ethernet/qlogic/qed/qed_hsi.h
@@ -3621,6 +3621,9 @@
u32 fc_npiv_nvram_tbl_addr;
u32 fc_npiv_nvram_tbl_size;
u32 transceiver_data;
+#define PMM_TRANSCEIVER_STATE_MASK 0x000000FF
+#define PMM_TRANSCEIVER_STATE_SHIFT 0x00000000
+#define PMM_TRANSCEIVER_STATE_PRESENT 0x00000001
};
/**************************************/
@@ -3955,6 +3958,14 @@
MFW_DRV_MSG_DCBX_REMOTE_MIB_UPDATED,
MFW_DRV_MSG_DCBX_OPERATIONAL_MIB_UPDATED,
MFW_DRV_MSG_ERROR_RECOVERY,
+ MFW_DRV_MSG_BW_UPDATE,
+ MFW_DRV_MSG_S_TAG_UPDATE,
+ MFW_DRV_MSG_GET_LAN_STATS,
+ MFW_DRV_MSG_GET_FCOE_STATS,
+ MFW_DRV_MSG_GET_ISCSI_STATS,
+ MFW_DRV_MSG_GET_RDMA_STATS,
+ MFW_DRV_MSG_FAILURE_DETECTED,
+ MFW_DRV_MSG_TRANSCEIVER_STATE_CHANGE,
MFW_DRV_MSG_MAX
};
diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c
index f23ce73..b89c9a8 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c
@@ -11,8 +11,8 @@
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/kernel.h>
-#include <linux/mutex.h>
#include <linux/slab.h>
+#include <linux/spinlock.h>
#include <linux/string.h>
#include "qed.h"
#include "qed_hsi.h"
@@ -168,8 +168,8 @@
if (!p_info->mfw_mb_shadow || !p_info->mfw_mb_addr)
goto err;
- /* Initialize the MFW mutex */
- mutex_init(&p_info->mutex);
+ /* Initialize the MFW spinlock */
+ spin_lock_init(&p_info->lock);
return 0;
@@ -179,6 +179,52 @@
return -ENOMEM;
}
+/* Locks the MFW mailbox of a PF to ensure a single access.
+ * The lock is achieved in most cases by holding a spinlock, causing other
+ * threads to wait till a previous access is done.
+ * In some cases (currently when a [UN]LOAD_REQ commands are sent), the single
+ * access is achieved by setting a blocking flag, which will fail other
+ * competing contexts to send their mailboxes.
+ */
+static int qed_mcp_mb_lock(struct qed_hwfn *p_hwfn,
+ u32 cmd)
+{
+ spin_lock_bh(&p_hwfn->mcp_info->lock);
+
+ /* The spinlock shouldn't be acquired when the mailbox command is
+ * [UN]LOAD_REQ, since the engine is locked by the MFW, and a parallel
+ * pending [UN]LOAD_REQ command of another PF together with a spinlock
+ * (i.e. interrupts are disabled) - can lead to a deadlock.
+ * It is assumed that for a single PF, no other mailbox commands can be
+ * sent from another context while sending LOAD_REQ, and that any
+ * parallel commands to UNLOAD_REQ can be cancelled.
+ */
+ if (cmd == DRV_MSG_CODE_LOAD_DONE || cmd == DRV_MSG_CODE_UNLOAD_DONE)
+ p_hwfn->mcp_info->block_mb_sending = false;
+
+ if (p_hwfn->mcp_info->block_mb_sending) {
+ DP_NOTICE(p_hwfn,
+ "Trying to send a MFW mailbox command [0x%x] in parallel to [UN]LOAD_REQ. Aborting.\n",
+ cmd);
+ spin_unlock_bh(&p_hwfn->mcp_info->lock);
+ return -EBUSY;
+ }
+
+ if (cmd == DRV_MSG_CODE_LOAD_REQ || cmd == DRV_MSG_CODE_UNLOAD_REQ) {
+ p_hwfn->mcp_info->block_mb_sending = true;
+ spin_unlock_bh(&p_hwfn->mcp_info->lock);
+ }
+
+ return 0;
+}
+
+static void qed_mcp_mb_unlock(struct qed_hwfn *p_hwfn,
+ u32 cmd)
+{
+ if (cmd != DRV_MSG_CODE_LOAD_REQ && cmd != DRV_MSG_CODE_UNLOAD_REQ)
+ spin_unlock_bh(&p_hwfn->mcp_info->lock);
+}
+
int qed_mcp_reset(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt)
{
@@ -187,6 +233,13 @@
u32 org_mcp_reset_seq, cnt = 0;
int rc = 0;
+ /* Ensure that only a single thread is accessing the mailbox at a
+ * certain time.
+ */
+ rc = qed_mcp_mb_lock(p_hwfn, DRV_MSG_CODE_MCP_RESET);
+ if (rc != 0)
+ return rc;
+
/* Set drv command along with the updated sequence */
org_mcp_reset_seq = qed_rd(p_hwfn, p_ptt, MISCS_REG_GENERIC_POR_0);
DRV_MB_WR(p_hwfn, p_ptt, drv_mb_header,
@@ -209,6 +262,8 @@
rc = -EAGAIN;
}
+ qed_mcp_mb_unlock(p_hwfn, DRV_MSG_CODE_MCP_RESET);
+
return rc;
}
@@ -275,14 +330,12 @@
return rc;
}
-int qed_mcp_cmd(struct qed_hwfn *p_hwfn,
- struct qed_ptt *p_ptt,
- u32 cmd,
- u32 param,
- u32 *o_mcp_resp,
- u32 *o_mcp_param)
+static int qed_mcp_cmd_and_union(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt,
+ struct qed_mcp_mb_params *p_mb_params)
{
- int rc = 0;
+ u32 union_data_addr;
+ int rc;
/* MCP not initialized */
if (!qed_mcp_is_init(p_hwfn)) {
@@ -290,28 +343,56 @@
return -EBUSY;
}
- /* Lock Mutex to ensure only single thread is
- * accessing the MCP at one time
+ union_data_addr = p_hwfn->mcp_info->drv_mb_addr +
+ offsetof(struct public_drv_mb, union_data);
+
+ /* Ensure that only a single thread is accessing the mailbox at a
+ * certain time.
*/
- mutex_lock(&p_hwfn->mcp_info->mutex);
- rc = qed_do_mcp_cmd(p_hwfn, p_ptt, cmd, param,
- o_mcp_resp, o_mcp_param);
- /* Release Mutex */
- mutex_unlock(&p_hwfn->mcp_info->mutex);
+ rc = qed_mcp_mb_lock(p_hwfn, p_mb_params->cmd);
+ if (rc)
+ return rc;
+
+ if (p_mb_params->p_data_src != NULL)
+ qed_memcpy_to(p_hwfn, p_ptt, union_data_addr,
+ p_mb_params->p_data_src,
+ sizeof(*p_mb_params->p_data_src));
+
+ rc = qed_do_mcp_cmd(p_hwfn, p_ptt, p_mb_params->cmd,
+ p_mb_params->param, &p_mb_params->mcp_resp,
+ &p_mb_params->mcp_param);
+
+ if (p_mb_params->p_data_dst != NULL)
+ qed_memcpy_from(p_hwfn, p_ptt, p_mb_params->p_data_dst,
+ union_data_addr,
+ sizeof(*p_mb_params->p_data_dst));
+
+ qed_mcp_mb_unlock(p_hwfn, p_mb_params->cmd);
return rc;
}
-static void qed_mcp_set_drv_ver(struct qed_dev *cdev,
- struct qed_hwfn *p_hwfn,
- struct qed_ptt *p_ptt)
+int qed_mcp_cmd(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt,
+ u32 cmd,
+ u32 param,
+ u32 *o_mcp_resp,
+ u32 *o_mcp_param)
{
- u32 i;
+ struct qed_mcp_mb_params mb_params;
+ int rc;
- /* Copy version string to MCP */
- for (i = 0; i < MCP_DRV_VER_STR_SIZE_DWORD; i++)
- DRV_MB_WR(p_hwfn, p_ptt, union_data.ver_str[i],
- *(u32 *)&cdev->ver_str[i * sizeof(u32)]);
+ memset(&mb_params, 0, sizeof(mb_params));
+ mb_params.cmd = cmd;
+ mb_params.param = param;
+ rc = qed_mcp_cmd_and_union(p_hwfn, p_ptt, &mb_params);
+ if (rc)
+ return rc;
+
+ *o_mcp_resp = mb_params.mcp_resp;
+ *o_mcp_param = mb_params.mcp_param;
+
+ return 0;
}
int qed_mcp_load_req(struct qed_hwfn *p_hwfn,
@@ -319,26 +400,18 @@
u32 *p_load_code)
{
struct qed_dev *cdev = p_hwfn->cdev;
- u32 param;
+ struct qed_mcp_mb_params mb_params;
+ union drv_union_data union_data;
int rc;
- if (!qed_mcp_is_init(p_hwfn)) {
- DP_NOTICE(p_hwfn, "MFW is not initialized !\n");
- return -EBUSY;
- }
-
- /* Save driver's version to shmem */
- qed_mcp_set_drv_ver(cdev, p_hwfn, p_ptt);
-
- DP_VERBOSE(p_hwfn, QED_MSG_SP, "fw_seq 0x%08x, drv_pulse 0x%x\n",
- p_hwfn->mcp_info->drv_mb_seq,
- p_hwfn->mcp_info->drv_pulse_seq);
-
+ memset(&mb_params, 0, sizeof(mb_params));
/* Load Request */
- rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_LOAD_REQ,
- (PDA_COMP | DRV_ID_MCP_HSI_VER_CURRENT |
- cdev->drv_type),
- p_load_code, ¶m);
+ mb_params.cmd = DRV_MSG_CODE_LOAD_REQ;
+ mb_params.param = PDA_COMP | DRV_ID_MCP_HSI_VER_CURRENT |
+ cdev->drv_type;
+ memcpy(&union_data.ver_str, cdev->ver_str, MCP_DRV_VER_STR_SIZE);
+ mb_params.p_data_src = &union_data;
+ rc = qed_mcp_cmd_and_union(p_hwfn, p_ptt, &mb_params);
/* if mcp fails to respond we must abort */
if (rc) {
@@ -346,6 +419,8 @@
return rc;
}
+ *p_load_code = mb_params.mcp_resp;
+
/* If MFW refused (e.g. other port is in diagnostic mode) we
* must abort. This can happen in the following cases:
* - Other port is in diagnostic mode
@@ -365,6 +440,33 @@
return 0;
}
+static void qed_mcp_handle_transceiver_change(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt)
+{
+ u32 transceiver_state;
+
+ transceiver_state = qed_rd(p_hwfn, p_ptt,
+ p_hwfn->mcp_info->port_addr +
+ offsetof(struct public_port,
+ transceiver_data));
+
+ DP_VERBOSE(p_hwfn,
+ (NETIF_MSG_HW | QED_MSG_SP),
+ "Received transceiver state update [0x%08x] from mfw [Addr 0x%x]\n",
+ transceiver_state,
+ (u32)(p_hwfn->mcp_info->port_addr +
+ offsetof(struct public_port,
+ transceiver_data)));
+
+ transceiver_state = GET_FIELD(transceiver_state,
+ PMM_TRANSCEIVER_STATE);
+
+ if (transceiver_state == PMM_TRANSCEIVER_STATE_PRESENT)
+ DP_NOTICE(p_hwfn, "Transceiver is present.\n");
+ else
+ DP_NOTICE(p_hwfn, "Transceiver is unplugged.\n");
+}
+
static void qed_mcp_handle_link_change(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
bool b_reset)
@@ -390,7 +492,10 @@
return;
}
- p_link->link_up = !!(status & LINK_STATUS_LINK_UP);
+ if (p_hwfn->b_drv_link_init)
+ p_link->link_up = !!(status & LINK_STATUS_LINK_UP);
+ else
+ p_link->link_up = false;
p_link->full_duplex = true;
switch ((status & LINK_STATUS_SPEED_AND_DUPLEX_MASK)) {
@@ -492,53 +597,43 @@
bool b_up)
{
struct qed_mcp_link_params *params = &p_hwfn->mcp_info->link_input;
- u32 param = 0, reply = 0, cmd;
- struct pmm_phy_cfg phy_cfg;
+ struct qed_mcp_mb_params mb_params;
+ union drv_union_data union_data;
+ struct pmm_phy_cfg *phy_cfg;
int rc = 0;
- u32 i;
-
- if (!qed_mcp_is_init(p_hwfn)) {
- DP_NOTICE(p_hwfn, "MFW is not initialized !\n");
- return -EBUSY;
- }
+ u32 cmd;
/* Set the shmem configuration according to params */
- memset(&phy_cfg, 0, sizeof(phy_cfg));
+ phy_cfg = &union_data.drv_phy_cfg;
+ memset(phy_cfg, 0, sizeof(*phy_cfg));
cmd = b_up ? DRV_MSG_CODE_INIT_PHY : DRV_MSG_CODE_LINK_RESET;
if (!params->speed.autoneg)
- phy_cfg.speed = params->speed.forced_speed;
- phy_cfg.pause |= (params->pause.autoneg) ? PMM_PAUSE_AUTONEG : 0;
- phy_cfg.pause |= (params->pause.forced_rx) ? PMM_PAUSE_RX : 0;
- phy_cfg.pause |= (params->pause.forced_tx) ? PMM_PAUSE_TX : 0;
- phy_cfg.adv_speed = params->speed.advertised_speeds;
- phy_cfg.loopback_mode = params->loopback_mode;
+ phy_cfg->speed = params->speed.forced_speed;
+ phy_cfg->pause |= (params->pause.autoneg) ? PMM_PAUSE_AUTONEG : 0;
+ phy_cfg->pause |= (params->pause.forced_rx) ? PMM_PAUSE_RX : 0;
+ phy_cfg->pause |= (params->pause.forced_tx) ? PMM_PAUSE_TX : 0;
+ phy_cfg->adv_speed = params->speed.advertised_speeds;
+ phy_cfg->loopback_mode = params->loopback_mode;
- /* Write the requested configuration to shmem */
- for (i = 0; i < sizeof(phy_cfg); i += 4)
- qed_wr(p_hwfn, p_ptt,
- p_hwfn->mcp_info->drv_mb_addr +
- offsetof(struct public_drv_mb, union_data) + i,
- ((u32 *)&phy_cfg)[i >> 2]);
+ p_hwfn->b_drv_link_init = b_up;
if (b_up) {
DP_VERBOSE(p_hwfn, NETIF_MSG_LINK,
"Configuring Link: Speed 0x%08x, Pause 0x%08x, adv_speed 0x%08x, loopback 0x%08x, features 0x%08x\n",
- phy_cfg.speed,
- phy_cfg.pause,
- phy_cfg.adv_speed,
- phy_cfg.loopback_mode,
- phy_cfg.feature_config_flags);
+ phy_cfg->speed,
+ phy_cfg->pause,
+ phy_cfg->adv_speed,
+ phy_cfg->loopback_mode,
+ phy_cfg->feature_config_flags);
} else {
DP_VERBOSE(p_hwfn, NETIF_MSG_LINK,
"Resetting link\n");
}
- DP_VERBOSE(p_hwfn, QED_MSG_SP, "fw_seq 0x%08x, drv_pulse 0x%x\n",
- p_hwfn->mcp_info->drv_mb_seq,
- p_hwfn->mcp_info->drv_pulse_seq);
-
- /* Load Request */
- rc = qed_mcp_cmd(p_hwfn, p_ptt, cmd, 0, &reply, ¶m);
+ memset(&mb_params, 0, sizeof(mb_params));
+ mb_params.cmd = cmd;
+ mb_params.p_data_src = &union_data;
+ rc = qed_mcp_cmd_and_union(p_hwfn, p_ptt, &mb_params);
/* if mcp fails to respond we must abort */
if (rc) {
@@ -581,6 +676,9 @@
case MFW_DRV_MSG_LINK_CHANGE:
qed_mcp_handle_link_change(p_hwfn, p_ptt, false);
break;
+ case MFW_DRV_MSG_TRANSCEIVER_STATE_CHANGE:
+ qed_mcp_handle_transceiver_change(p_hwfn, p_ptt);
+ break;
default:
DP_NOTICE(p_hwfn, "Unimplemented MFW message %d\n", i);
rc = -EINVAL;
@@ -801,11 +899,11 @@
int rc;
rc = qed_mcp_cmd(p_hwfn, p_ptt,
- DRV_MSG_CODE_NIG_DRAIN, 100,
+ DRV_MSG_CODE_NIG_DRAIN, 1000,
&resp, ¶m);
/* Wait for the drain to complete before returning */
- msleep(120);
+ msleep(1020);
return rc;
}
@@ -831,31 +929,28 @@
struct qed_ptt *p_ptt,
struct qed_mcp_drv_version *p_ver)
{
- int rc = 0;
- u32 param = 0, reply = 0, i;
+ struct drv_version_stc *p_drv_version;
+ struct qed_mcp_mb_params mb_params;
+ union drv_union_data union_data;
+ __be32 val;
+ u32 i;
+ int rc;
- if (!qed_mcp_is_init(p_hwfn)) {
- DP_NOTICE(p_hwfn, "MFW is not initialized !\n");
- return -EBUSY;
+ p_drv_version = &union_data.drv_version;
+ p_drv_version->version = p_ver->version;
+ for (i = 0; i < MCP_DRV_VER_STR_SIZE - 1; i += 4) {
+ val = cpu_to_be32(p_ver->name[i]);
+ *(u32 *)&p_drv_version->name[i * sizeof(u32)] = val;
}
- DRV_MB_WR(p_hwfn, p_ptt, union_data.drv_version.version,
- p_ver->version);
- /* Copy version string to shmem */
- for (i = 0; i < (MCP_DRV_VER_STR_SIZE - 4) / 4; i++) {
- DRV_MB_WR(p_hwfn, p_ptt,
- union_data.drv_version.name[i * sizeof(u32)],
- *(u32 *)&p_ver->name[i * sizeof(u32)]);
- }
-
- rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_SET_VERSION, 0, &reply,
- ¶m);
- if (rc) {
+ memset(&mb_params, 0, sizeof(mb_params));
+ mb_params.cmd = DRV_MSG_CODE_SET_VERSION;
+ mb_params.p_data_src = &union_data;
+ rc = qed_mcp_cmd_and_union(p_hwfn, p_ptt, &mb_params);
+ if (rc)
DP_ERR(p_hwfn, "MCP response failure, aborting\n");
- return rc;
- }
- return 0;
+ return rc;
}
int qed_mcp_set_led(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt,
diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.h b/drivers/net/ethernet/qlogic/qed/qed_mcp.h
index 506197d..50917a2 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_mcp.h
+++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.h
@@ -11,8 +11,8 @@
#include <linux/types.h>
#include <linux/delay.h>
-#include <linux/mutex.h>
#include <linux/slab.h>
+#include <linux/spinlock.h>
#include "qed_hsi.h"
struct qed_mcp_link_speed_params {
@@ -255,7 +255,8 @@
#define MFW_PORT(_p_hwfn) ((_p_hwfn)->abs_pf_id % \
((_p_hwfn)->cdev->num_ports_in_engines * 2))
struct qed_mcp_info {
- struct mutex mutex; /* MCP access lock */
+ spinlock_t lock;
+ bool block_mb_sending;
u32 public_base;
u32 drv_mb_addr;
u32 mfw_mb_addr;
@@ -272,6 +273,15 @@
u16 mcp_hist;
};
+struct qed_mcp_mb_params {
+ u32 cmd;
+ u32 param;
+ union drv_union_data *p_data_src;
+ union drv_union_data *p_data_dst;
+ u32 mcp_resp;
+ u32 mcp_param;
+};
+
/**
* @brief Initialize the interface with the MCP
*
diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c
index 5728625..518af32 100644
--- a/drivers/net/ethernet/qlogic/qede/qede_main.c
+++ b/drivers/net/ethernet/qlogic/qede/qede_main.c
@@ -1001,6 +1001,7 @@
}
}
+#ifdef CONFIG_INET
static void qede_gro_ip_csum(struct sk_buff *skb)
{
const struct iphdr *iph = ip_hdr(skb);
@@ -1029,12 +1030,14 @@
&iph->saddr, &iph->daddr, 0);
tcp_gro_complete(skb);
}
+#endif
static void qede_gro_receive(struct qede_dev *edev,
struct qede_fastpath *fp,
struct sk_buff *skb,
u16 vlan_tag)
{
+#ifdef CONFIG_INET
if (skb_shinfo(skb)->gso_size) {
switch (skb->protocol) {
case htons(ETH_P_IP):
@@ -1049,7 +1052,7 @@
ntohs(skb->protocol));
}
}
-
+#endif
skb_record_rx_queue(skb, fp->rss_id);
qede_skb_receive(edev, fp, skb, vlan_tag);
}
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
index 46bbea8..55007f1 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
@@ -566,6 +566,7 @@
u64 tx_dma_map_error;
u64 spurious_intr;
u64 mac_filter_limit_overrun;
+ u64 mbx_spurious_intr;
};
/*
@@ -1099,7 +1100,7 @@
unsigned long status;
spinlock_t queue_lock; /* Mailbox queue lock */
spinlock_t aen_lock; /* Mailbox response/AEN lock */
- atomic_t rsp_status;
+ u32 rsp_status;
u32 num_cmds;
};
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
index 37a731b..f9640d5ce 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
@@ -491,7 +491,7 @@
static inline void qlcnic_83xx_notify_mbx_response(struct qlcnic_mailbox *mbx)
{
- atomic_set(&mbx->rsp_status, QLC_83XX_MBX_RESPONSE_ARRIVED);
+ mbx->rsp_status = QLC_83XX_MBX_RESPONSE_ARRIVED;
complete(&mbx->completion);
}
@@ -510,7 +510,7 @@
if (event & QLCNIC_MBX_ASYNC_EVENT) {
__qlcnic_83xx_process_aen(adapter);
} else {
- if (atomic_read(&mbx->rsp_status) != rsp_status)
+ if (mbx->rsp_status != rsp_status)
qlcnic_83xx_notify_mbx_response(mbx);
}
out:
@@ -1023,7 +1023,7 @@
if (event & QLCNIC_MBX_ASYNC_EVENT) {
__qlcnic_83xx_process_aen(adapter);
} else {
- if (atomic_read(&mbx->rsp_status) != rsp_status)
+ if (mbx->rsp_status != rsp_status)
qlcnic_83xx_notify_mbx_response(mbx);
}
}
@@ -2338,9 +2338,9 @@
static irqreturn_t qlcnic_83xx_handle_aen(int irq, void *data)
{
+ u32 mask, resp, event, rsp_status = QLC_83XX_MBX_RESPONSE_ARRIVED;
struct qlcnic_adapter *adapter = data;
struct qlcnic_mailbox *mbx;
- u32 mask, resp, event;
unsigned long flags;
mbx = adapter->ahw->mailbox;
@@ -2350,10 +2350,14 @@
goto out;
event = readl(QLCNIC_MBX_FW(adapter->ahw, 0));
- if (event & QLCNIC_MBX_ASYNC_EVENT)
+ if (event & QLCNIC_MBX_ASYNC_EVENT) {
__qlcnic_83xx_process_aen(adapter);
- else
- qlcnic_83xx_notify_mbx_response(mbx);
+ } else {
+ if (mbx->rsp_status != rsp_status)
+ qlcnic_83xx_notify_mbx_response(mbx);
+ else
+ adapter->stats.mbx_spurious_intr++;
+ }
out:
mask = QLCRDX(adapter->ahw, QLCNIC_DEF_INT_MASK);
@@ -4050,10 +4054,10 @@
struct qlcnic_adapter *adapter = mbx->adapter;
const struct qlcnic_mbx_ops *mbx_ops = mbx->ops;
struct device *dev = &adapter->pdev->dev;
- atomic_t *rsp_status = &mbx->rsp_status;
struct list_head *head = &mbx->cmd_q;
struct qlcnic_hardware_context *ahw;
struct qlcnic_cmd_args *cmd = NULL;
+ unsigned long flags;
ahw = adapter->ahw;
@@ -4063,7 +4067,9 @@
return;
}
- atomic_set(rsp_status, QLC_83XX_MBX_RESPONSE_WAIT);
+ spin_lock_irqsave(&mbx->aen_lock, flags);
+ mbx->rsp_status = QLC_83XX_MBX_RESPONSE_WAIT;
+ spin_unlock_irqrestore(&mbx->aen_lock, flags);
spin_lock(&mbx->queue_lock);
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
index 494e810..0a2318c 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
@@ -59,7 +59,8 @@
QLC_OFF(stats.mac_filter_limit_overrun)},
{"spurious intr", QLC_SIZEOF(stats.spurious_intr),
QLC_OFF(stats.spurious_intr)},
-
+ {"mbx spurious intr", QLC_SIZEOF(stats.mbx_spurious_intr),
+ QLC_OFF(stats.mbx_spurious_intr)},
};
static const char qlcnic_device_gstrings_stats[][ETH_GSTRING_LEN] = {
diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c
index dd2cf37..94f08f1 100644
--- a/drivers/net/ethernet/realtek/r8169.c
+++ b/drivers/net/ethernet/realtek/r8169.c
@@ -1999,7 +1999,8 @@
goto out;
if (netif_running(dev) && (autoneg == AUTONEG_ENABLE) &&
- (advertising & ADVERTISED_1000baseT_Full)) {
+ (advertising & ADVERTISED_1000baseT_Full) &&
+ !pci_is_pcie(tp->pci_dev)) {
mod_timer(&tp->timer, jiffies + RTL8169_PHY_TIMEOUT);
}
out:
diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c
index 9c64489..2e9a781 100644
--- a/drivers/net/ethernet/renesas/sh_eth.c
+++ b/drivers/net/ethernet/renesas/sh_eth.c
@@ -1127,11 +1127,8 @@
break;
sh_eth_set_receive_align(skb);
- /* RX descriptor */
- rxdesc = &mdp->rx_ring[i];
/* The size of the buffer is a multiple of 32 bytes. */
buf_len = ALIGN(mdp->rx_buf_sz, 32);
- rxdesc->len = cpu_to_le32(buf_len << 16);
dma_addr = dma_map_single(&ndev->dev, skb->data, buf_len,
DMA_FROM_DEVICE);
if (dma_mapping_error(&ndev->dev, dma_addr)) {
@@ -1139,6 +1136,10 @@
break;
}
mdp->rx_skbuff[i] = skb;
+
+ /* RX descriptor */
+ rxdesc = &mdp->rx_ring[i];
+ rxdesc->len = cpu_to_le32(buf_len << 16);
rxdesc->addr = cpu_to_le32(dma_addr);
rxdesc->status = cpu_to_le32(RD_RACT | RD_RFP);
@@ -1154,7 +1155,8 @@
mdp->dirty_rx = (u32) (i - mdp->num_rx_ring);
/* Mark the last entry as wrapping the ring. */
- rxdesc->status |= cpu_to_le32(RD_RDLE);
+ if (rxdesc)
+ rxdesc->status |= cpu_to_le32(RD_RDLE);
memset(mdp->tx_ring, 0, tx_ringsize);
diff --git a/drivers/net/ethernet/rocker/rocker_ofdpa.c b/drivers/net/ethernet/rocker/rocker_ofdpa.c
index 07218c3..0e758bc 100644
--- a/drivers/net/ethernet/rocker/rocker_ofdpa.c
+++ b/drivers/net/ethernet/rocker/rocker_ofdpa.c
@@ -188,6 +188,7 @@
DECLARE_HASHTABLE(neigh_tbl, 16);
spinlock_t neigh_tbl_lock; /* for neigh tbl accesses */
u32 neigh_tbl_next_index;
+ unsigned long ageing_time;
};
struct ofdpa_port {
@@ -2105,7 +2106,7 @@
struct ofdpa_port *ofdpa_port;
struct ofdpa_fdb_tbl_entry *entry;
struct hlist_node *tmp;
- unsigned long next_timer = jiffies + BR_MIN_AGEING_TIME;
+ unsigned long next_timer = jiffies + ofdpa->ageing_time;
unsigned long expires;
unsigned long lock_flags;
int flags = OFDPA_OP_FLAG_NOWAIT | OFDPA_OP_FLAG_REMOVE |
@@ -2492,6 +2493,8 @@
(unsigned long) ofdpa);
mod_timer(&ofdpa->fdb_cleanup_timer, jiffies);
+ ofdpa->ageing_time = BR_DEFAULT_AGEING_TIME;
+
return 0;
}
@@ -2648,9 +2651,12 @@
struct switchdev_trans *trans)
{
struct ofdpa_port *ofdpa_port = rocker_port->wpriv;
+ struct ofdpa *ofdpa = ofdpa_port->ofdpa;
if (!switchdev_trans_ph_prepare(trans)) {
ofdpa_port->ageing_time = clock_t_to_jiffies(ageing_time);
+ if (ofdpa_port->ageing_time < ofdpa->ageing_time)
+ ofdpa->ageing_time = ofdpa_port->ageing_time;
mod_timer(&ofdpa_port->ofdpa->fdb_cleanup_timer, jiffies);
}
diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
index 6a0cbbe..192631a 100644
--- a/drivers/net/geneve.c
+++ b/drivers/net/geneve.c
@@ -68,6 +68,7 @@
u8 tos; /* TOS override */
union geneve_addr remote; /* IP address for link partner */
struct list_head next; /* geneve's per namespace list */
+ __be32 label; /* IPv6 flowlabel override */
__be16 dst_port;
bool collect_md;
struct gro_cells gro_cells;
@@ -462,8 +463,6 @@
goto out;
}
- flush = 0;
-
for (p = *head; p; p = p->next) {
if (!NAPI_GRO_CB(p)->same_flow)
continue;
@@ -480,14 +479,13 @@
rcu_read_lock();
ptype = gro_find_receive_by_type(type);
- if (!ptype) {
- flush = 1;
+ if (!ptype)
goto out_unlock;
- }
skb_gro_pull(skb, gh_len);
skb_gro_postpull_rcsum(skb, gh, gh_len);
pp = ptype->callbacks.gro_receive(head, skb);
+ flush = 0;
out_unlock:
rcu_read_unlock();
@@ -846,6 +844,7 @@
fl6->daddr = info->key.u.ipv6.dst;
fl6->saddr = info->key.u.ipv6.src;
fl6->flowi6_tos = RT_TOS(info->key.tos);
+ fl6->flowlabel = info->key.label;
dst_cache = &info->dst_cache;
} else {
prio = geneve->tos;
@@ -857,6 +856,7 @@
}
fl6->flowi6_tos = RT_TOS(prio);
+ fl6->flowlabel = geneve->label;
fl6->daddr = geneve->remote.sin6.sin6_addr;
dst_cache = &geneve->dst_cache;
}
@@ -998,6 +998,7 @@
struct flowi6 fl6;
__u8 prio, ttl;
__be16 sport;
+ __be32 label;
bool xnet = !net_eq(geneve->net, dev_net(geneve->dev));
u32 flags = geneve->flags;
@@ -1041,6 +1042,7 @@
prio = ip_tunnel_ecn_encap(key->tos, iip, skb);
ttl = key->ttl;
+ label = info->key.label;
} else {
err = geneve6_build_skb(dst, skb, 0, geneve->vni,
0, NULL, flags, xnet);
@@ -1052,9 +1054,11 @@
if (!ttl && ipv6_addr_is_multicast(&fl6.daddr))
ttl = 1;
ttl = ttl ? : ip6_dst_hoplimit(dst);
+ label = geneve->label;
}
+
udp_tunnel6_xmit_skb(dst, gs6->sock->sk, skb, dev,
- &fl6.saddr, &fl6.daddr, prio, ttl,
+ &fl6.saddr, &fl6.daddr, prio, ttl, label,
sport, geneve->dst_port,
!!(flags & GENEVE_F_UDP_ZERO_CSUM6_TX));
return NETDEV_TX_OK;
@@ -1238,6 +1242,7 @@
[IFLA_GENEVE_REMOTE6] = { .len = sizeof(struct in6_addr) },
[IFLA_GENEVE_TTL] = { .type = NLA_U8 },
[IFLA_GENEVE_TOS] = { .type = NLA_U8 },
+ [IFLA_GENEVE_LABEL] = { .type = NLA_U32 },
[IFLA_GENEVE_PORT] = { .type = NLA_U16 },
[IFLA_GENEVE_COLLECT_METADATA] = { .type = NLA_FLAG },
[IFLA_GENEVE_UDP_CSUM] = { .type = NLA_U8 },
@@ -1295,8 +1300,8 @@
static int geneve_configure(struct net *net, struct net_device *dev,
union geneve_addr *remote,
- __u32 vni, __u8 ttl, __u8 tos, __be16 dst_port,
- bool metadata, u32 flags)
+ __u32 vni, __u8 ttl, __u8 tos, __be32 label,
+ __be16 dst_port, bool metadata, u32 flags)
{
struct geneve_net *gn = net_generic(net, geneve_net_id);
struct geneve_dev *t, *geneve = netdev_priv(dev);
@@ -1306,7 +1311,7 @@
if (!remote)
return -EINVAL;
if (metadata &&
- (remote->sa.sa_family != AF_UNSPEC || vni || tos || ttl))
+ (remote->sa.sa_family != AF_UNSPEC || vni || tos || ttl || label))
return -EINVAL;
geneve->net = net;
@@ -1321,10 +1326,14 @@
(remote->sa.sa_family == AF_INET6 &&
ipv6_addr_is_multicast(&remote->sin6.sin6_addr)))
return -EINVAL;
+ if (label && remote->sa.sa_family != AF_INET6)
+ return -EINVAL;
+
geneve->remote = *remote;
geneve->ttl = ttl;
geneve->tos = tos;
+ geneve->label = label;
geneve->dst_port = dst_port;
geneve->collect_md = metadata;
geneve->flags = flags;
@@ -1367,6 +1376,7 @@
__u8 ttl = 0, tos = 0;
bool metadata = false;
union geneve_addr remote = geneve_remote_unspec;
+ __be32 label = 0;
__u32 vni = 0;
u32 flags = 0;
@@ -1403,6 +1413,10 @@
if (data[IFLA_GENEVE_TOS])
tos = nla_get_u8(data[IFLA_GENEVE_TOS]);
+ if (data[IFLA_GENEVE_LABEL])
+ label = nla_get_be32(data[IFLA_GENEVE_LABEL]) &
+ IPV6_FLOWLABEL_MASK;
+
if (data[IFLA_GENEVE_PORT])
dst_port = nla_get_be16(data[IFLA_GENEVE_PORT]);
@@ -1421,8 +1435,8 @@
nla_get_u8(data[IFLA_GENEVE_UDP_ZERO_CSUM6_RX]))
flags |= GENEVE_F_UDP_ZERO_CSUM6_RX;
- return geneve_configure(net, dev, &remote, vni, ttl, tos, dst_port,
- metadata, flags);
+ return geneve_configure(net, dev, &remote, vni, ttl, tos, label,
+ dst_port, metadata, flags);
}
static void geneve_dellink(struct net_device *dev, struct list_head *head)
@@ -1439,6 +1453,7 @@
nla_total_size(sizeof(struct in6_addr)) + /* IFLA_GENEVE_REMOTE{6} */
nla_total_size(sizeof(__u8)) + /* IFLA_GENEVE_TTL */
nla_total_size(sizeof(__u8)) + /* IFLA_GENEVE_TOS */
+ nla_total_size(sizeof(__be32)) + /* IFLA_GENEVE_LABEL */
nla_total_size(sizeof(__be16)) + /* IFLA_GENEVE_PORT */
nla_total_size(0) + /* IFLA_GENEVE_COLLECT_METADATA */
nla_total_size(sizeof(__u8)) + /* IFLA_GENEVE_UDP_CSUM */
@@ -1469,7 +1484,8 @@
}
if (nla_put_u8(skb, IFLA_GENEVE_TTL, geneve->ttl) ||
- nla_put_u8(skb, IFLA_GENEVE_TOS, geneve->tos))
+ nla_put_u8(skb, IFLA_GENEVE_TOS, geneve->tos) ||
+ nla_put_be32(skb, IFLA_GENEVE_LABEL, geneve->label))
goto nla_put_failure;
if (nla_put_be16(skb, IFLA_GENEVE_PORT, geneve->dst_port))
@@ -1521,7 +1537,7 @@
return dev;
err = geneve_configure(net, dev, &geneve_remote_unspec,
- 0, 0, 0, htons(dst_port), true,
+ 0, 0, 0, 0, htons(dst_port), true,
GENEVE_F_UDP_ZERO_CSUM6_RX);
if (err)
goto err;
diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c
new file mode 100644
index 0000000..84d3e5c
--- /dev/null
+++ b/drivers/net/macsec.c
@@ -0,0 +1,3297 @@
+/*
+ * drivers/net/macsec.c - MACsec device
+ *
+ * Copyright (c) 2015 Sabrina Dubroca <sd@queasysnail.net>
+ *
+ * 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/types.h>
+#include <linux/skbuff.h>
+#include <linux/socket.h>
+#include <linux/module.h>
+#include <crypto/aead.h>
+#include <linux/etherdevice.h>
+#include <linux/rtnetlink.h>
+#include <net/genetlink.h>
+#include <net/sock.h>
+
+#include <uapi/linux/if_macsec.h>
+
+typedef u64 __bitwise sci_t;
+
+#define MACSEC_SCI_LEN 8
+
+/* SecTAG length = macsec_eth_header without the optional SCI */
+#define MACSEC_TAG_LEN 6
+
+struct macsec_eth_header {
+ struct ethhdr eth;
+ /* SecTAG */
+ u8 tci_an;
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ u8 short_length:6,
+ unused:2;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ u8 unused:2,
+ short_length:6;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+ __be32 packet_number;
+ u8 secure_channel_id[8]; /* optional */
+} __packed;
+
+#define MACSEC_TCI_VERSION 0x80
+#define MACSEC_TCI_ES 0x40 /* end station */
+#define MACSEC_TCI_SC 0x20 /* SCI present */
+#define MACSEC_TCI_SCB 0x10 /* epon */
+#define MACSEC_TCI_E 0x08 /* encryption */
+#define MACSEC_TCI_C 0x04 /* changed text */
+#define MACSEC_AN_MASK 0x03 /* association number */
+#define MACSEC_TCI_CONFID (MACSEC_TCI_E | MACSEC_TCI_C)
+
+/* minimum secure data length deemed "not short", see IEEE 802.1AE-2006 9.7 */
+#define MIN_NON_SHORT_LEN 48
+
+#define GCM_AES_IV_LEN 12
+#define DEFAULT_ICV_LEN 16
+
+#define MACSEC_NUM_AN 4 /* 2 bits for the association number */
+
+#define for_each_rxsc(secy, sc) \
+ for (sc = rcu_dereference_bh(secy->rx_sc); \
+ sc; \
+ sc = rcu_dereference_bh(sc->next))
+#define for_each_rxsc_rtnl(secy, sc) \
+ for (sc = rtnl_dereference(secy->rx_sc); \
+ sc; \
+ sc = rtnl_dereference(sc->next))
+
+struct gcm_iv {
+ union {
+ u8 secure_channel_id[8];
+ sci_t sci;
+ };
+ __be32 pn;
+};
+
+/**
+ * struct macsec_key - SA key
+ * @id: user-provided key identifier
+ * @tfm: crypto struct, key storage
+ */
+struct macsec_key {
+ u64 id;
+ struct crypto_aead *tfm;
+};
+
+struct macsec_rx_sc_stats {
+ __u64 InOctetsValidated;
+ __u64 InOctetsDecrypted;
+ __u64 InPktsUnchecked;
+ __u64 InPktsDelayed;
+ __u64 InPktsOK;
+ __u64 InPktsInvalid;
+ __u64 InPktsLate;
+ __u64 InPktsNotValid;
+ __u64 InPktsNotUsingSA;
+ __u64 InPktsUnusedSA;
+};
+
+struct macsec_rx_sa_stats {
+ __u32 InPktsOK;
+ __u32 InPktsInvalid;
+ __u32 InPktsNotValid;
+ __u32 InPktsNotUsingSA;
+ __u32 InPktsUnusedSA;
+};
+
+struct macsec_tx_sa_stats {
+ __u32 OutPktsProtected;
+ __u32 OutPktsEncrypted;
+};
+
+struct macsec_tx_sc_stats {
+ __u64 OutPktsProtected;
+ __u64 OutPktsEncrypted;
+ __u64 OutOctetsProtected;
+ __u64 OutOctetsEncrypted;
+};
+
+struct macsec_dev_stats {
+ __u64 OutPktsUntagged;
+ __u64 InPktsUntagged;
+ __u64 OutPktsTooLong;
+ __u64 InPktsNoTag;
+ __u64 InPktsBadTag;
+ __u64 InPktsUnknownSCI;
+ __u64 InPktsNoSCI;
+ __u64 InPktsOverrun;
+};
+
+/**
+ * struct macsec_rx_sa - receive secure association
+ * @active:
+ * @next_pn: packet number expected for the next packet
+ * @lock: protects next_pn manipulations
+ * @key: key structure
+ * @stats: per-SA stats
+ */
+struct macsec_rx_sa {
+ struct macsec_key key;
+ spinlock_t lock;
+ u32 next_pn;
+ atomic_t refcnt;
+ bool active;
+ struct macsec_rx_sa_stats __percpu *stats;
+ struct macsec_rx_sc *sc;
+ struct rcu_head rcu;
+};
+
+struct pcpu_rx_sc_stats {
+ struct macsec_rx_sc_stats stats;
+ struct u64_stats_sync syncp;
+};
+
+/**
+ * struct macsec_rx_sc - receive secure channel
+ * @sci: secure channel identifier for this SC
+ * @active: channel is active
+ * @sa: array of secure associations
+ * @stats: per-SC stats
+ */
+struct macsec_rx_sc {
+ struct macsec_rx_sc __rcu *next;
+ sci_t sci;
+ bool active;
+ struct macsec_rx_sa __rcu *sa[MACSEC_NUM_AN];
+ struct pcpu_rx_sc_stats __percpu *stats;
+ atomic_t refcnt;
+ struct rcu_head rcu_head;
+};
+
+/**
+ * struct macsec_tx_sa - transmit secure association
+ * @active:
+ * @next_pn: packet number to use for the next packet
+ * @lock: protects next_pn manipulations
+ * @key: key structure
+ * @stats: per-SA stats
+ */
+struct macsec_tx_sa {
+ struct macsec_key key;
+ spinlock_t lock;
+ u32 next_pn;
+ atomic_t refcnt;
+ bool active;
+ struct macsec_tx_sa_stats __percpu *stats;
+ struct rcu_head rcu;
+};
+
+struct pcpu_tx_sc_stats {
+ struct macsec_tx_sc_stats stats;
+ struct u64_stats_sync syncp;
+};
+
+/**
+ * struct macsec_tx_sc - transmit secure channel
+ * @active:
+ * @encoding_sa: association number of the SA currently in use
+ * @encrypt: encrypt packets on transmit, or authenticate only
+ * @send_sci: always include the SCI in the SecTAG
+ * @end_station:
+ * @scb: single copy broadcast flag
+ * @sa: array of secure associations
+ * @stats: stats for this TXSC
+ */
+struct macsec_tx_sc {
+ bool active;
+ u8 encoding_sa;
+ bool encrypt;
+ bool send_sci;
+ bool end_station;
+ bool scb;
+ struct macsec_tx_sa __rcu *sa[MACSEC_NUM_AN];
+ struct pcpu_tx_sc_stats __percpu *stats;
+};
+
+#define MACSEC_VALIDATE_DEFAULT MACSEC_VALIDATE_STRICT
+
+/**
+ * struct macsec_secy - MACsec Security Entity
+ * @netdev: netdevice for this SecY
+ * @n_rx_sc: number of receive secure channels configured on this SecY
+ * @sci: secure channel identifier used for tx
+ * @key_len: length of keys used by the cipher suite
+ * @icv_len: length of ICV used by the cipher suite
+ * @validate_frames: validation mode
+ * @operational: MAC_Operational flag
+ * @protect_frames: enable protection for this SecY
+ * @replay_protect: enable packet number checks on receive
+ * @replay_window: size of the replay window
+ * @tx_sc: transmit secure channel
+ * @rx_sc: linked list of receive secure channels
+ */
+struct macsec_secy {
+ struct net_device *netdev;
+ unsigned int n_rx_sc;
+ sci_t sci;
+ u16 key_len;
+ u16 icv_len;
+ enum macsec_validation_type validate_frames;
+ bool operational;
+ bool protect_frames;
+ bool replay_protect;
+ u32 replay_window;
+ struct macsec_tx_sc tx_sc;
+ struct macsec_rx_sc __rcu *rx_sc;
+};
+
+struct pcpu_secy_stats {
+ struct macsec_dev_stats stats;
+ struct u64_stats_sync syncp;
+};
+
+/**
+ * struct macsec_dev - private data
+ * @secy: SecY config
+ * @real_dev: pointer to underlying netdevice
+ * @stats: MACsec device stats
+ * @secys: linked list of SecY's on the underlying device
+ */
+struct macsec_dev {
+ struct macsec_secy secy;
+ struct net_device *real_dev;
+ struct pcpu_secy_stats __percpu *stats;
+ struct list_head secys;
+};
+
+/**
+ * struct macsec_rxh_data - rx_handler private argument
+ * @secys: linked list of SecY's on this underlying device
+ */
+struct macsec_rxh_data {
+ struct list_head secys;
+};
+
+static struct macsec_dev *macsec_priv(const struct net_device *dev)
+{
+ return (struct macsec_dev *)netdev_priv(dev);
+}
+
+static struct macsec_rxh_data *macsec_data_rcu(const struct net_device *dev)
+{
+ return rcu_dereference_bh(dev->rx_handler_data);
+}
+
+static struct macsec_rxh_data *macsec_data_rtnl(const struct net_device *dev)
+{
+ return rtnl_dereference(dev->rx_handler_data);
+}
+
+struct macsec_cb {
+ struct aead_request *req;
+ union {
+ struct macsec_tx_sa *tx_sa;
+ struct macsec_rx_sa *rx_sa;
+ };
+ u8 assoc_num;
+ bool valid;
+ bool has_sci;
+};
+
+static struct macsec_rx_sa *macsec_rxsa_get(struct macsec_rx_sa __rcu *ptr)
+{
+ struct macsec_rx_sa *sa = rcu_dereference_bh(ptr);
+
+ if (!sa || !sa->active)
+ return NULL;
+
+ if (!atomic_inc_not_zero(&sa->refcnt))
+ return NULL;
+
+ return sa;
+}
+
+static void free_rx_sc_rcu(struct rcu_head *head)
+{
+ struct macsec_rx_sc *rx_sc = container_of(head, struct macsec_rx_sc, rcu_head);
+
+ free_percpu(rx_sc->stats);
+ kfree(rx_sc);
+}
+
+static struct macsec_rx_sc *macsec_rxsc_get(struct macsec_rx_sc *sc)
+{
+ return atomic_inc_not_zero(&sc->refcnt) ? sc : NULL;
+}
+
+static void macsec_rxsc_put(struct macsec_rx_sc *sc)
+{
+ if (atomic_dec_and_test(&sc->refcnt))
+ call_rcu(&sc->rcu_head, free_rx_sc_rcu);
+}
+
+static void free_rxsa(struct rcu_head *head)
+{
+ struct macsec_rx_sa *sa = container_of(head, struct macsec_rx_sa, rcu);
+
+ crypto_free_aead(sa->key.tfm);
+ free_percpu(sa->stats);
+ macsec_rxsc_put(sa->sc);
+ kfree(sa);
+}
+
+static void macsec_rxsa_put(struct macsec_rx_sa *sa)
+{
+ if (atomic_dec_and_test(&sa->refcnt))
+ call_rcu(&sa->rcu, free_rxsa);
+}
+
+static struct macsec_tx_sa *macsec_txsa_get(struct macsec_tx_sa __rcu *ptr)
+{
+ struct macsec_tx_sa *sa = rcu_dereference_bh(ptr);
+
+ if (!sa || !sa->active)
+ return NULL;
+
+ if (!atomic_inc_not_zero(&sa->refcnt))
+ return NULL;
+
+ return sa;
+}
+
+static void free_txsa(struct rcu_head *head)
+{
+ struct macsec_tx_sa *sa = container_of(head, struct macsec_tx_sa, rcu);
+
+ crypto_free_aead(sa->key.tfm);
+ free_percpu(sa->stats);
+ kfree(sa);
+}
+
+static void macsec_txsa_put(struct macsec_tx_sa *sa)
+{
+ if (atomic_dec_and_test(&sa->refcnt))
+ call_rcu(&sa->rcu, free_txsa);
+}
+
+static struct macsec_cb *macsec_skb_cb(struct sk_buff *skb)
+{
+ BUILD_BUG_ON(sizeof(struct macsec_cb) > sizeof(skb->cb));
+ return (struct macsec_cb *)skb->cb;
+}
+
+#define MACSEC_PORT_ES (htons(0x0001))
+#define MACSEC_PORT_SCB (0x0000)
+#define MACSEC_UNDEF_SCI ((__force sci_t)0xffffffffffffffffULL)
+
+#define DEFAULT_SAK_LEN 16
+#define DEFAULT_SEND_SCI true
+#define DEFAULT_ENCRYPT false
+#define DEFAULT_ENCODING_SA 0
+
+static sci_t make_sci(u8 *addr, __be16 port)
+{
+ sci_t sci;
+
+ memcpy(&sci, addr, ETH_ALEN);
+ memcpy(((char *)&sci) + ETH_ALEN, &port, sizeof(port));
+
+ return sci;
+}
+
+static sci_t macsec_frame_sci(struct macsec_eth_header *hdr, bool sci_present)
+{
+ sci_t sci;
+
+ if (sci_present)
+ memcpy(&sci, hdr->secure_channel_id,
+ sizeof(hdr->secure_channel_id));
+ else
+ sci = make_sci(hdr->eth.h_source, MACSEC_PORT_ES);
+
+ return sci;
+}
+
+static unsigned int macsec_sectag_len(bool sci_present)
+{
+ return MACSEC_TAG_LEN + (sci_present ? MACSEC_SCI_LEN : 0);
+}
+
+static unsigned int macsec_hdr_len(bool sci_present)
+{
+ return macsec_sectag_len(sci_present) + ETH_HLEN;
+}
+
+static unsigned int macsec_extra_len(bool sci_present)
+{
+ return macsec_sectag_len(sci_present) + sizeof(__be16);
+}
+
+/* Fill SecTAG according to IEEE 802.1AE-2006 10.5.3 */
+static void macsec_fill_sectag(struct macsec_eth_header *h,
+ const struct macsec_secy *secy, u32 pn)
+{
+ const struct macsec_tx_sc *tx_sc = &secy->tx_sc;
+
+ memset(&h->tci_an, 0, macsec_sectag_len(tx_sc->send_sci));
+ h->eth.h_proto = htons(ETH_P_MACSEC);
+
+ if (tx_sc->send_sci ||
+ (secy->n_rx_sc > 1 && !tx_sc->end_station && !tx_sc->scb)) {
+ h->tci_an |= MACSEC_TCI_SC;
+ memcpy(&h->secure_channel_id, &secy->sci,
+ sizeof(h->secure_channel_id));
+ } else {
+ if (tx_sc->end_station)
+ h->tci_an |= MACSEC_TCI_ES;
+ if (tx_sc->scb)
+ h->tci_an |= MACSEC_TCI_SCB;
+ }
+
+ h->packet_number = htonl(pn);
+
+ /* with GCM, C/E clear for !encrypt, both set for encrypt */
+ if (tx_sc->encrypt)
+ h->tci_an |= MACSEC_TCI_CONFID;
+ else if (secy->icv_len != DEFAULT_ICV_LEN)
+ h->tci_an |= MACSEC_TCI_C;
+
+ h->tci_an |= tx_sc->encoding_sa;
+}
+
+static void macsec_set_shortlen(struct macsec_eth_header *h, size_t data_len)
+{
+ if (data_len < MIN_NON_SHORT_LEN)
+ h->short_length = data_len;
+}
+
+/* validate MACsec packet according to IEEE 802.1AE-2006 9.12 */
+static bool macsec_validate_skb(struct sk_buff *skb, u16 icv_len)
+{
+ struct macsec_eth_header *h = (struct macsec_eth_header *)skb->data;
+ int len = skb->len - 2 * ETH_ALEN;
+ int extra_len = macsec_extra_len(!!(h->tci_an & MACSEC_TCI_SC)) + icv_len;
+
+ /* a) It comprises at least 17 octets */
+ if (skb->len <= 16)
+ return false;
+
+ /* b) MACsec EtherType: already checked */
+
+ /* c) V bit is clear */
+ if (h->tci_an & MACSEC_TCI_VERSION)
+ return false;
+
+ /* d) ES or SCB => !SC */
+ if ((h->tci_an & MACSEC_TCI_ES || h->tci_an & MACSEC_TCI_SCB) &&
+ (h->tci_an & MACSEC_TCI_SC))
+ return false;
+
+ /* e) Bits 7 and 8 of octet 4 of the SecTAG are clear */
+ if (h->unused)
+ return false;
+
+ /* rx.pn != 0 (figure 10-5) */
+ if (!h->packet_number)
+ return false;
+
+ /* length check, f) g) h) i) */
+ if (h->short_length)
+ return len == extra_len + h->short_length;
+ return len >= extra_len + MIN_NON_SHORT_LEN;
+}
+
+#define MACSEC_NEEDED_HEADROOM (macsec_extra_len(true))
+#define MACSEC_NEEDED_TAILROOM MACSEC_MAX_ICV_LEN
+
+static void macsec_fill_iv(unsigned char *iv, sci_t sci, u32 pn)
+{
+ struct gcm_iv *gcm_iv = (struct gcm_iv *)iv;
+
+ gcm_iv->sci = sci;
+ gcm_iv->pn = htonl(pn);
+}
+
+static struct macsec_eth_header *macsec_ethhdr(struct sk_buff *skb)
+{
+ return (struct macsec_eth_header *)skb_mac_header(skb);
+}
+
+static u32 tx_sa_update_pn(struct macsec_tx_sa *tx_sa, struct macsec_secy *secy)
+{
+ u32 pn;
+
+ spin_lock_bh(&tx_sa->lock);
+ pn = tx_sa->next_pn;
+
+ tx_sa->next_pn++;
+ if (tx_sa->next_pn == 0) {
+ pr_debug("PN wrapped, transitioning to !oper\n");
+ tx_sa->active = false;
+ if (secy->protect_frames)
+ secy->operational = false;
+ }
+ spin_unlock_bh(&tx_sa->lock);
+
+ return pn;
+}
+
+static void macsec_encrypt_finish(struct sk_buff *skb, struct net_device *dev)
+{
+ struct macsec_dev *macsec = netdev_priv(dev);
+
+ skb->dev = macsec->real_dev;
+ skb_reset_mac_header(skb);
+ skb->protocol = eth_hdr(skb)->h_proto;
+}
+
+static void macsec_count_tx(struct sk_buff *skb, struct macsec_tx_sc *tx_sc,
+ struct macsec_tx_sa *tx_sa)
+{
+ struct pcpu_tx_sc_stats *txsc_stats = this_cpu_ptr(tx_sc->stats);
+
+ u64_stats_update_begin(&txsc_stats->syncp);
+ if (tx_sc->encrypt) {
+ txsc_stats->stats.OutOctetsEncrypted += skb->len;
+ txsc_stats->stats.OutPktsEncrypted++;
+ this_cpu_inc(tx_sa->stats->OutPktsEncrypted);
+ } else {
+ txsc_stats->stats.OutOctetsProtected += skb->len;
+ txsc_stats->stats.OutPktsProtected++;
+ this_cpu_inc(tx_sa->stats->OutPktsProtected);
+ }
+ u64_stats_update_end(&txsc_stats->syncp);
+}
+
+static void count_tx(struct net_device *dev, int ret, int len)
+{
+ if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) {
+ struct pcpu_sw_netstats *stats = this_cpu_ptr(dev->tstats);
+
+ u64_stats_update_begin(&stats->syncp);
+ stats->tx_packets++;
+ stats->tx_bytes += len;
+ u64_stats_update_end(&stats->syncp);
+ } else {
+ dev->stats.tx_dropped++;
+ }
+}
+
+static void macsec_encrypt_done(struct crypto_async_request *base, int err)
+{
+ struct sk_buff *skb = base->data;
+ struct net_device *dev = skb->dev;
+ struct macsec_dev *macsec = macsec_priv(dev);
+ struct macsec_tx_sa *sa = macsec_skb_cb(skb)->tx_sa;
+ int len, ret;
+
+ aead_request_free(macsec_skb_cb(skb)->req);
+
+ rcu_read_lock_bh();
+ macsec_encrypt_finish(skb, dev);
+ macsec_count_tx(skb, &macsec->secy.tx_sc, macsec_skb_cb(skb)->tx_sa);
+ len = skb->len;
+ ret = dev_queue_xmit(skb);
+ count_tx(dev, ret, len);
+ rcu_read_unlock_bh();
+
+ macsec_txsa_put(sa);
+ dev_put(dev);
+}
+
+static struct sk_buff *macsec_encrypt(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ int ret;
+ struct scatterlist sg[MAX_SKB_FRAGS + 1];
+ unsigned char iv[GCM_AES_IV_LEN];
+ struct ethhdr *eth;
+ struct macsec_eth_header *hh;
+ size_t unprotected_len;
+ struct aead_request *req;
+ struct macsec_secy *secy;
+ struct macsec_tx_sc *tx_sc;
+ struct macsec_tx_sa *tx_sa;
+ struct macsec_dev *macsec = macsec_priv(dev);
+ u32 pn;
+
+ secy = &macsec->secy;
+ tx_sc = &secy->tx_sc;
+
+ /* 10.5.1 TX SA assignment */
+ tx_sa = macsec_txsa_get(tx_sc->sa[tx_sc->encoding_sa]);
+ if (!tx_sa) {
+ secy->operational = false;
+ kfree_skb(skb);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (unlikely(skb_headroom(skb) < MACSEC_NEEDED_HEADROOM ||
+ skb_tailroom(skb) < MACSEC_NEEDED_TAILROOM)) {
+ struct sk_buff *nskb = skb_copy_expand(skb,
+ MACSEC_NEEDED_HEADROOM,
+ MACSEC_NEEDED_TAILROOM,
+ GFP_ATOMIC);
+ if (likely(nskb)) {
+ consume_skb(skb);
+ skb = nskb;
+ } else {
+ macsec_txsa_put(tx_sa);
+ kfree_skb(skb);
+ return ERR_PTR(-ENOMEM);
+ }
+ } else {
+ skb = skb_unshare(skb, GFP_ATOMIC);
+ if (!skb) {
+ macsec_txsa_put(tx_sa);
+ return ERR_PTR(-ENOMEM);
+ }
+ }
+
+ unprotected_len = skb->len;
+ eth = eth_hdr(skb);
+ hh = (struct macsec_eth_header *)skb_push(skb, macsec_extra_len(tx_sc->send_sci));
+ memmove(hh, eth, 2 * ETH_ALEN);
+
+ pn = tx_sa_update_pn(tx_sa, secy);
+ if (pn == 0) {
+ macsec_txsa_put(tx_sa);
+ kfree_skb(skb);
+ return ERR_PTR(-ENOLINK);
+ }
+ macsec_fill_sectag(hh, secy, pn);
+ macsec_set_shortlen(hh, unprotected_len - 2 * ETH_ALEN);
+
+ macsec_fill_iv(iv, secy->sci, pn);
+
+ skb_put(skb, secy->icv_len);
+
+ if (skb->len - ETH_HLEN > macsec_priv(dev)->real_dev->mtu) {
+ struct pcpu_secy_stats *secy_stats = this_cpu_ptr(macsec->stats);
+
+ u64_stats_update_begin(&secy_stats->syncp);
+ secy_stats->stats.OutPktsTooLong++;
+ u64_stats_update_end(&secy_stats->syncp);
+
+ macsec_txsa_put(tx_sa);
+ kfree_skb(skb);
+ return ERR_PTR(-EINVAL);
+ }
+
+ req = aead_request_alloc(tx_sa->key.tfm, GFP_ATOMIC);
+ if (!req) {
+ macsec_txsa_put(tx_sa);
+ kfree_skb(skb);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ sg_init_table(sg, MAX_SKB_FRAGS + 1);
+ skb_to_sgvec(skb, sg, 0, skb->len);
+
+ if (tx_sc->encrypt) {
+ int len = skb->len - macsec_hdr_len(tx_sc->send_sci) -
+ secy->icv_len;
+ aead_request_set_crypt(req, sg, sg, len, iv);
+ aead_request_set_ad(req, macsec_hdr_len(tx_sc->send_sci));
+ } else {
+ aead_request_set_crypt(req, sg, sg, 0, iv);
+ aead_request_set_ad(req, skb->len - secy->icv_len);
+ }
+
+ macsec_skb_cb(skb)->req = req;
+ macsec_skb_cb(skb)->tx_sa = tx_sa;
+ aead_request_set_callback(req, 0, macsec_encrypt_done, skb);
+
+ dev_hold(skb->dev);
+ ret = crypto_aead_encrypt(req);
+ if (ret == -EINPROGRESS) {
+ return ERR_PTR(ret);
+ } else if (ret != 0) {
+ dev_put(skb->dev);
+ kfree_skb(skb);
+ aead_request_free(req);
+ macsec_txsa_put(tx_sa);
+ return ERR_PTR(-EINVAL);
+ }
+
+ dev_put(skb->dev);
+ aead_request_free(req);
+ macsec_txsa_put(tx_sa);
+
+ return skb;
+}
+
+static bool macsec_post_decrypt(struct sk_buff *skb, struct macsec_secy *secy, u32 pn)
+{
+ struct macsec_rx_sa *rx_sa = macsec_skb_cb(skb)->rx_sa;
+ struct pcpu_rx_sc_stats *rxsc_stats = this_cpu_ptr(rx_sa->sc->stats);
+ struct macsec_eth_header *hdr = macsec_ethhdr(skb);
+ u32 lowest_pn = 0;
+
+ spin_lock(&rx_sa->lock);
+ if (rx_sa->next_pn >= secy->replay_window)
+ lowest_pn = rx_sa->next_pn - secy->replay_window;
+
+ /* Now perform replay protection check again
+ * (see IEEE 802.1AE-2006 figure 10-5)
+ */
+ if (secy->replay_protect && pn < lowest_pn) {
+ spin_unlock(&rx_sa->lock);
+ u64_stats_update_begin(&rxsc_stats->syncp);
+ rxsc_stats->stats.InPktsLate++;
+ u64_stats_update_end(&rxsc_stats->syncp);
+ return false;
+ }
+
+ if (secy->validate_frames != MACSEC_VALIDATE_DISABLED) {
+ u64_stats_update_begin(&rxsc_stats->syncp);
+ if (hdr->tci_an & MACSEC_TCI_E)
+ rxsc_stats->stats.InOctetsDecrypted += skb->len;
+ else
+ rxsc_stats->stats.InOctetsValidated += skb->len;
+ u64_stats_update_end(&rxsc_stats->syncp);
+ }
+
+ if (!macsec_skb_cb(skb)->valid) {
+ spin_unlock(&rx_sa->lock);
+
+ /* 10.6.5 */
+ if (hdr->tci_an & MACSEC_TCI_C ||
+ secy->validate_frames == MACSEC_VALIDATE_STRICT) {
+ u64_stats_update_begin(&rxsc_stats->syncp);
+ rxsc_stats->stats.InPktsNotValid++;
+ u64_stats_update_end(&rxsc_stats->syncp);
+ return false;
+ }
+
+ u64_stats_update_begin(&rxsc_stats->syncp);
+ if (secy->validate_frames == MACSEC_VALIDATE_CHECK) {
+ rxsc_stats->stats.InPktsInvalid++;
+ this_cpu_inc(rx_sa->stats->InPktsInvalid);
+ } else if (pn < lowest_pn) {
+ rxsc_stats->stats.InPktsDelayed++;
+ } else {
+ rxsc_stats->stats.InPktsUnchecked++;
+ }
+ u64_stats_update_end(&rxsc_stats->syncp);
+ } else {
+ u64_stats_update_begin(&rxsc_stats->syncp);
+ if (pn < lowest_pn) {
+ rxsc_stats->stats.InPktsDelayed++;
+ } else {
+ rxsc_stats->stats.InPktsOK++;
+ this_cpu_inc(rx_sa->stats->InPktsOK);
+ }
+ u64_stats_update_end(&rxsc_stats->syncp);
+
+ if (pn >= rx_sa->next_pn)
+ rx_sa->next_pn = pn + 1;
+ spin_unlock(&rx_sa->lock);
+ }
+
+ return true;
+}
+
+static void macsec_reset_skb(struct sk_buff *skb, struct net_device *dev)
+{
+ skb->pkt_type = PACKET_HOST;
+ skb->protocol = eth_type_trans(skb, dev);
+
+ skb_reset_network_header(skb);
+ if (!skb_transport_header_was_set(skb))
+ skb_reset_transport_header(skb);
+ skb_reset_mac_len(skb);
+}
+
+static void macsec_finalize_skb(struct sk_buff *skb, u8 icv_len, u8 hdr_len)
+{
+ memmove(skb->data + hdr_len, skb->data, 2 * ETH_ALEN);
+ skb_pull(skb, hdr_len);
+ pskb_trim_unique(skb, skb->len - icv_len);
+}
+
+static void count_rx(struct net_device *dev, int len)
+{
+ struct pcpu_sw_netstats *stats = this_cpu_ptr(dev->tstats);
+
+ u64_stats_update_begin(&stats->syncp);
+ stats->rx_packets++;
+ stats->rx_bytes += len;
+ u64_stats_update_end(&stats->syncp);
+}
+
+static void macsec_decrypt_done(struct crypto_async_request *base, int err)
+{
+ struct sk_buff *skb = base->data;
+ struct net_device *dev = skb->dev;
+ struct macsec_dev *macsec = macsec_priv(dev);
+ struct macsec_rx_sa *rx_sa = macsec_skb_cb(skb)->rx_sa;
+ int len, ret;
+ u32 pn;
+
+ aead_request_free(macsec_skb_cb(skb)->req);
+
+ rcu_read_lock_bh();
+ pn = ntohl(macsec_ethhdr(skb)->packet_number);
+ if (!macsec_post_decrypt(skb, &macsec->secy, pn)) {
+ rcu_read_unlock_bh();
+ kfree_skb(skb);
+ goto out;
+ }
+
+ macsec_finalize_skb(skb, macsec->secy.icv_len,
+ macsec_extra_len(macsec_skb_cb(skb)->has_sci));
+ macsec_reset_skb(skb, macsec->secy.netdev);
+
+ len = skb->len;
+ ret = netif_rx(skb);
+ if (ret == NET_RX_SUCCESS)
+ count_rx(dev, len);
+ else
+ macsec->secy.netdev->stats.rx_dropped++;
+
+ rcu_read_unlock_bh();
+
+out:
+ macsec_rxsa_put(rx_sa);
+ dev_put(dev);
+ return;
+}
+
+static struct sk_buff *macsec_decrypt(struct sk_buff *skb,
+ struct net_device *dev,
+ struct macsec_rx_sa *rx_sa,
+ sci_t sci,
+ struct macsec_secy *secy)
+{
+ int ret;
+ struct scatterlist sg[MAX_SKB_FRAGS + 1];
+ unsigned char iv[GCM_AES_IV_LEN];
+ struct aead_request *req;
+ struct macsec_eth_header *hdr;
+ u16 icv_len = secy->icv_len;
+
+ macsec_skb_cb(skb)->valid = false;
+ skb = skb_share_check(skb, GFP_ATOMIC);
+ if (!skb)
+ return NULL;
+
+ req = aead_request_alloc(rx_sa->key.tfm, GFP_ATOMIC);
+ if (!req) {
+ kfree_skb(skb);
+ return NULL;
+ }
+
+ hdr = (struct macsec_eth_header *)skb->data;
+ macsec_fill_iv(iv, sci, ntohl(hdr->packet_number));
+
+ sg_init_table(sg, MAX_SKB_FRAGS + 1);
+ skb_to_sgvec(skb, sg, 0, skb->len);
+
+ if (hdr->tci_an & MACSEC_TCI_E) {
+ /* confidentiality: ethernet + macsec header
+ * authenticated, encrypted payload
+ */
+ int len = skb->len - macsec_hdr_len(macsec_skb_cb(skb)->has_sci);
+
+ aead_request_set_crypt(req, sg, sg, len, iv);
+ aead_request_set_ad(req, macsec_hdr_len(macsec_skb_cb(skb)->has_sci));
+ skb = skb_unshare(skb, GFP_ATOMIC);
+ if (!skb) {
+ aead_request_free(req);
+ return NULL;
+ }
+ } else {
+ /* integrity only: all headers + data authenticated */
+ aead_request_set_crypt(req, sg, sg, icv_len, iv);
+ aead_request_set_ad(req, skb->len - icv_len);
+ }
+
+ macsec_skb_cb(skb)->req = req;
+ macsec_skb_cb(skb)->rx_sa = rx_sa;
+ skb->dev = dev;
+ aead_request_set_callback(req, 0, macsec_decrypt_done, skb);
+
+ dev_hold(dev);
+ ret = crypto_aead_decrypt(req);
+ if (ret == -EINPROGRESS) {
+ return NULL;
+ } else if (ret != 0) {
+ /* decryption/authentication failed
+ * 10.6 if validateFrames is disabled, deliver anyway
+ */
+ if (ret != -EBADMSG) {
+ kfree_skb(skb);
+ skb = NULL;
+ }
+ } else {
+ macsec_skb_cb(skb)->valid = true;
+ }
+ dev_put(dev);
+
+ aead_request_free(req);
+
+ return skb;
+}
+
+static struct macsec_rx_sc *find_rx_sc(struct macsec_secy *secy, sci_t sci)
+{
+ struct macsec_rx_sc *rx_sc;
+
+ for_each_rxsc(secy, rx_sc) {
+ if (rx_sc->sci == sci)
+ return rx_sc;
+ }
+
+ return NULL;
+}
+
+static struct macsec_rx_sc *find_rx_sc_rtnl(struct macsec_secy *secy, sci_t sci)
+{
+ struct macsec_rx_sc *rx_sc;
+
+ for_each_rxsc_rtnl(secy, rx_sc) {
+ if (rx_sc->sci == sci)
+ return rx_sc;
+ }
+
+ return NULL;
+}
+
+static void handle_not_macsec(struct sk_buff *skb)
+{
+ struct macsec_rxh_data *rxd;
+ struct macsec_dev *macsec;
+
+ rcu_read_lock();
+ rxd = macsec_data_rcu(skb->dev);
+
+ /* 10.6 If the management control validateFrames is not
+ * Strict, frames without a SecTAG are received, counted, and
+ * delivered to the Controlled Port
+ */
+ list_for_each_entry_rcu(macsec, &rxd->secys, secys) {
+ struct sk_buff *nskb;
+ int ret;
+ struct pcpu_secy_stats *secy_stats = this_cpu_ptr(macsec->stats);
+
+ if (macsec->secy.validate_frames == MACSEC_VALIDATE_STRICT) {
+ u64_stats_update_begin(&secy_stats->syncp);
+ secy_stats->stats.InPktsNoTag++;
+ u64_stats_update_end(&secy_stats->syncp);
+ continue;
+ }
+
+ /* deliver on this port */
+ nskb = skb_clone(skb, GFP_ATOMIC);
+ if (!nskb)
+ break;
+
+ nskb->dev = macsec->secy.netdev;
+
+ ret = netif_rx(nskb);
+ if (ret == NET_RX_SUCCESS) {
+ u64_stats_update_begin(&secy_stats->syncp);
+ secy_stats->stats.InPktsUntagged++;
+ u64_stats_update_end(&secy_stats->syncp);
+ } else {
+ macsec->secy.netdev->stats.rx_dropped++;
+ }
+ }
+
+ rcu_read_unlock();
+}
+
+static rx_handler_result_t macsec_handle_frame(struct sk_buff **pskb)
+{
+ struct sk_buff *skb = *pskb;
+ struct net_device *dev = skb->dev;
+ struct macsec_eth_header *hdr;
+ struct macsec_secy *secy = NULL;
+ struct macsec_rx_sc *rx_sc;
+ struct macsec_rx_sa *rx_sa;
+ struct macsec_rxh_data *rxd;
+ struct macsec_dev *macsec;
+ sci_t sci;
+ u32 pn;
+ bool cbit;
+ struct pcpu_rx_sc_stats *rxsc_stats;
+ struct pcpu_secy_stats *secy_stats;
+ bool pulled_sci;
+
+ if (skb_headroom(skb) < ETH_HLEN)
+ goto drop_direct;
+
+ hdr = macsec_ethhdr(skb);
+ if (hdr->eth.h_proto != htons(ETH_P_MACSEC)) {
+ handle_not_macsec(skb);
+
+ /* and deliver to the uncontrolled port */
+ return RX_HANDLER_PASS;
+ }
+
+ skb = skb_unshare(skb, GFP_ATOMIC);
+ if (!skb) {
+ *pskb = NULL;
+ return RX_HANDLER_CONSUMED;
+ }
+
+ pulled_sci = pskb_may_pull(skb, macsec_extra_len(true));
+ if (!pulled_sci) {
+ if (!pskb_may_pull(skb, macsec_extra_len(false)))
+ goto drop_direct;
+ }
+
+ hdr = macsec_ethhdr(skb);
+
+ /* Frames with a SecTAG that has the TCI E bit set but the C
+ * bit clear are discarded, as this reserved encoding is used
+ * to identify frames with a SecTAG that are not to be
+ * delivered to the Controlled Port.
+ */
+ if ((hdr->tci_an & (MACSEC_TCI_C | MACSEC_TCI_E)) == MACSEC_TCI_E)
+ return RX_HANDLER_PASS;
+
+ /* now, pull the extra length */
+ if (hdr->tci_an & MACSEC_TCI_SC) {
+ if (!pulled_sci)
+ goto drop_direct;
+ }
+
+ /* ethernet header is part of crypto processing */
+ skb_push(skb, ETH_HLEN);
+
+ macsec_skb_cb(skb)->has_sci = !!(hdr->tci_an & MACSEC_TCI_SC);
+ macsec_skb_cb(skb)->assoc_num = hdr->tci_an & MACSEC_AN_MASK;
+ sci = macsec_frame_sci(hdr, macsec_skb_cb(skb)->has_sci);
+
+ rcu_read_lock();
+ rxd = macsec_data_rcu(skb->dev);
+
+ list_for_each_entry_rcu(macsec, &rxd->secys, secys) {
+ struct macsec_rx_sc *sc = find_rx_sc(&macsec->secy, sci);
+
+ if (sc) {
+ secy = &macsec->secy;
+ rx_sc = sc;
+ break;
+ }
+ }
+
+ if (!secy)
+ goto nosci;
+
+ dev = secy->netdev;
+ macsec = macsec_priv(dev);
+ secy_stats = this_cpu_ptr(macsec->stats);
+ rxsc_stats = this_cpu_ptr(rx_sc->stats);
+
+ if (!macsec_validate_skb(skb, secy->icv_len)) {
+ u64_stats_update_begin(&secy_stats->syncp);
+ secy_stats->stats.InPktsBadTag++;
+ u64_stats_update_end(&secy_stats->syncp);
+ goto drop_nosa;
+ }
+
+ rx_sa = macsec_rxsa_get(rx_sc->sa[macsec_skb_cb(skb)->assoc_num]);
+ if (!rx_sa) {
+ /* 10.6.1 if the SA is not in use */
+
+ /* If validateFrames is Strict or the C bit in the
+ * SecTAG is set, discard
+ */
+ if (hdr->tci_an & MACSEC_TCI_C ||
+ secy->validate_frames == MACSEC_VALIDATE_STRICT) {
+ u64_stats_update_begin(&rxsc_stats->syncp);
+ rxsc_stats->stats.InPktsNotUsingSA++;
+ u64_stats_update_end(&rxsc_stats->syncp);
+ goto drop_nosa;
+ }
+
+ /* not Strict, the frame (with the SecTAG and ICV
+ * removed) is delivered to the Controlled Port.
+ */
+ u64_stats_update_begin(&rxsc_stats->syncp);
+ rxsc_stats->stats.InPktsUnusedSA++;
+ u64_stats_update_end(&rxsc_stats->syncp);
+ goto deliver;
+ }
+
+ /* First, PN check to avoid decrypting obviously wrong packets */
+ pn = ntohl(hdr->packet_number);
+ if (secy->replay_protect) {
+ bool late;
+
+ spin_lock(&rx_sa->lock);
+ late = rx_sa->next_pn >= secy->replay_window &&
+ pn < (rx_sa->next_pn - secy->replay_window);
+ spin_unlock(&rx_sa->lock);
+
+ if (late) {
+ u64_stats_update_begin(&rxsc_stats->syncp);
+ rxsc_stats->stats.InPktsLate++;
+ u64_stats_update_end(&rxsc_stats->syncp);
+ goto drop;
+ }
+ }
+
+ /* Disabled && !changed text => skip validation */
+ if (hdr->tci_an & MACSEC_TCI_C ||
+ secy->validate_frames != MACSEC_VALIDATE_DISABLED)
+ skb = macsec_decrypt(skb, dev, rx_sa, sci, secy);
+
+ if (!skb) {
+ macsec_rxsa_put(rx_sa);
+ rcu_read_unlock();
+ *pskb = NULL;
+ return RX_HANDLER_CONSUMED;
+ }
+
+ if (!macsec_post_decrypt(skb, secy, pn))
+ goto drop;
+
+deliver:
+ macsec_finalize_skb(skb, secy->icv_len,
+ macsec_extra_len(macsec_skb_cb(skb)->has_sci));
+ macsec_reset_skb(skb, secy->netdev);
+
+ macsec_rxsa_put(rx_sa);
+ count_rx(dev, skb->len);
+
+ rcu_read_unlock();
+
+ *pskb = skb;
+ return RX_HANDLER_ANOTHER;
+
+drop:
+ macsec_rxsa_put(rx_sa);
+drop_nosa:
+ rcu_read_unlock();
+drop_direct:
+ kfree_skb(skb);
+ *pskb = NULL;
+ return RX_HANDLER_CONSUMED;
+
+nosci:
+ /* 10.6.1 if the SC is not found */
+ cbit = !!(hdr->tci_an & MACSEC_TCI_C);
+ if (!cbit)
+ macsec_finalize_skb(skb, DEFAULT_ICV_LEN,
+ macsec_extra_len(macsec_skb_cb(skb)->has_sci));
+
+ list_for_each_entry_rcu(macsec, &rxd->secys, secys) {
+ struct sk_buff *nskb;
+ int ret;
+
+ secy_stats = this_cpu_ptr(macsec->stats);
+
+ /* If validateFrames is Strict or the C bit in the
+ * SecTAG is set, discard
+ */
+ if (cbit ||
+ macsec->secy.validate_frames == MACSEC_VALIDATE_STRICT) {
+ u64_stats_update_begin(&secy_stats->syncp);
+ secy_stats->stats.InPktsNoSCI++;
+ u64_stats_update_end(&secy_stats->syncp);
+ continue;
+ }
+
+ /* not strict, the frame (with the SecTAG and ICV
+ * removed) is delivered to the Controlled Port.
+ */
+ nskb = skb_clone(skb, GFP_ATOMIC);
+ if (!nskb)
+ break;
+
+ macsec_reset_skb(nskb, macsec->secy.netdev);
+
+ ret = netif_rx(nskb);
+ if (ret == NET_RX_SUCCESS) {
+ u64_stats_update_begin(&secy_stats->syncp);
+ secy_stats->stats.InPktsUnknownSCI++;
+ u64_stats_update_end(&secy_stats->syncp);
+ } else {
+ macsec->secy.netdev->stats.rx_dropped++;
+ }
+ }
+
+ rcu_read_unlock();
+ *pskb = skb;
+ return RX_HANDLER_PASS;
+}
+
+static struct crypto_aead *macsec_alloc_tfm(char *key, int key_len, int icv_len)
+{
+ struct crypto_aead *tfm;
+ int ret;
+
+ tfm = crypto_alloc_aead("gcm(aes)", 0, CRYPTO_ALG_ASYNC);
+ if (!tfm || IS_ERR(tfm))
+ return NULL;
+
+ ret = crypto_aead_setkey(tfm, key, key_len);
+ if (ret < 0) {
+ crypto_free_aead(tfm);
+ return NULL;
+ }
+
+ ret = crypto_aead_setauthsize(tfm, icv_len);
+ if (ret < 0) {
+ crypto_free_aead(tfm);
+ return NULL;
+ }
+
+ return tfm;
+}
+
+static int init_rx_sa(struct macsec_rx_sa *rx_sa, char *sak, int key_len,
+ int icv_len)
+{
+ rx_sa->stats = alloc_percpu(struct macsec_rx_sa_stats);
+ if (!rx_sa->stats)
+ return -1;
+
+ rx_sa->key.tfm = macsec_alloc_tfm(sak, key_len, icv_len);
+ if (!rx_sa->key.tfm) {
+ free_percpu(rx_sa->stats);
+ return -1;
+ }
+
+ rx_sa->active = false;
+ rx_sa->next_pn = 1;
+ atomic_set(&rx_sa->refcnt, 1);
+ spin_lock_init(&rx_sa->lock);
+
+ return 0;
+}
+
+static void clear_rx_sa(struct macsec_rx_sa *rx_sa)
+{
+ rx_sa->active = false;
+
+ macsec_rxsa_put(rx_sa);
+}
+
+static void free_rx_sc(struct macsec_rx_sc *rx_sc)
+{
+ int i;
+
+ for (i = 0; i < MACSEC_NUM_AN; i++) {
+ struct macsec_rx_sa *sa = rtnl_dereference(rx_sc->sa[i]);
+
+ RCU_INIT_POINTER(rx_sc->sa[i], NULL);
+ if (sa)
+ clear_rx_sa(sa);
+ }
+
+ macsec_rxsc_put(rx_sc);
+}
+
+static struct macsec_rx_sc *del_rx_sc(struct macsec_secy *secy, sci_t sci)
+{
+ struct macsec_rx_sc *rx_sc, __rcu **rx_scp;
+
+ for (rx_scp = &secy->rx_sc, rx_sc = rtnl_dereference(*rx_scp);
+ rx_sc;
+ rx_scp = &rx_sc->next, rx_sc = rtnl_dereference(*rx_scp)) {
+ if (rx_sc->sci == sci) {
+ if (rx_sc->active)
+ secy->n_rx_sc--;
+ rcu_assign_pointer(*rx_scp, rx_sc->next);
+ return rx_sc;
+ }
+ }
+
+ return NULL;
+}
+
+static struct macsec_rx_sc *create_rx_sc(struct net_device *dev, sci_t sci)
+{
+ struct macsec_rx_sc *rx_sc;
+ struct macsec_dev *macsec;
+ struct net_device *real_dev = macsec_priv(dev)->real_dev;
+ struct macsec_rxh_data *rxd = macsec_data_rtnl(real_dev);
+ struct macsec_secy *secy;
+
+ list_for_each_entry(macsec, &rxd->secys, secys) {
+ if (find_rx_sc_rtnl(&macsec->secy, sci))
+ return ERR_PTR(-EEXIST);
+ }
+
+ rx_sc = kzalloc(sizeof(*rx_sc), GFP_KERNEL);
+ if (!rx_sc)
+ return ERR_PTR(-ENOMEM);
+
+ rx_sc->stats = netdev_alloc_pcpu_stats(struct pcpu_rx_sc_stats);
+ if (!rx_sc->stats) {
+ kfree(rx_sc);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ rx_sc->sci = sci;
+ rx_sc->active = true;
+ atomic_set(&rx_sc->refcnt, 1);
+
+ secy = &macsec_priv(dev)->secy;
+ rcu_assign_pointer(rx_sc->next, secy->rx_sc);
+ rcu_assign_pointer(secy->rx_sc, rx_sc);
+
+ if (rx_sc->active)
+ secy->n_rx_sc++;
+
+ return rx_sc;
+}
+
+static int init_tx_sa(struct macsec_tx_sa *tx_sa, char *sak, int key_len,
+ int icv_len)
+{
+ tx_sa->stats = alloc_percpu(struct macsec_tx_sa_stats);
+ if (!tx_sa->stats)
+ return -1;
+
+ tx_sa->key.tfm = macsec_alloc_tfm(sak, key_len, icv_len);
+ if (!tx_sa->key.tfm) {
+ free_percpu(tx_sa->stats);
+ return -1;
+ }
+
+ tx_sa->active = false;
+ atomic_set(&tx_sa->refcnt, 1);
+ spin_lock_init(&tx_sa->lock);
+
+ return 0;
+}
+
+static void clear_tx_sa(struct macsec_tx_sa *tx_sa)
+{
+ tx_sa->active = false;
+
+ macsec_txsa_put(tx_sa);
+}
+
+static struct genl_family macsec_fam = {
+ .id = GENL_ID_GENERATE,
+ .name = MACSEC_GENL_NAME,
+ .hdrsize = 0,
+ .version = MACSEC_GENL_VERSION,
+ .maxattr = MACSEC_ATTR_MAX,
+ .netnsok = true,
+};
+
+static struct net_device *get_dev_from_nl(struct net *net,
+ struct nlattr **attrs)
+{
+ int ifindex = nla_get_u32(attrs[MACSEC_ATTR_IFINDEX]);
+ struct net_device *dev;
+
+ dev = __dev_get_by_index(net, ifindex);
+ if (!dev)
+ return ERR_PTR(-ENODEV);
+
+ if (!netif_is_macsec(dev))
+ return ERR_PTR(-ENODEV);
+
+ return dev;
+}
+
+static sci_t nla_get_sci(const struct nlattr *nla)
+{
+ return (__force sci_t)nla_get_u64(nla);
+}
+
+static int nla_put_sci(struct sk_buff *skb, int attrtype, sci_t value)
+{
+ return nla_put_u64(skb, attrtype, (__force u64)value);
+}
+
+static struct macsec_tx_sa *get_txsa_from_nl(struct net *net,
+ struct nlattr **attrs,
+ struct nlattr **tb_sa,
+ struct net_device **devp,
+ struct macsec_secy **secyp,
+ struct macsec_tx_sc **scp,
+ u8 *assoc_num)
+{
+ struct net_device *dev;
+ struct macsec_secy *secy;
+ struct macsec_tx_sc *tx_sc;
+ struct macsec_tx_sa *tx_sa;
+
+ if (!tb_sa[MACSEC_SA_ATTR_AN])
+ return ERR_PTR(-EINVAL);
+
+ *assoc_num = nla_get_u8(tb_sa[MACSEC_SA_ATTR_AN]);
+
+ dev = get_dev_from_nl(net, attrs);
+ if (IS_ERR(dev))
+ return ERR_CAST(dev);
+
+ if (*assoc_num >= MACSEC_NUM_AN)
+ return ERR_PTR(-EINVAL);
+
+ secy = &macsec_priv(dev)->secy;
+ tx_sc = &secy->tx_sc;
+
+ tx_sa = rtnl_dereference(tx_sc->sa[*assoc_num]);
+ if (!tx_sa)
+ return ERR_PTR(-ENODEV);
+
+ *devp = dev;
+ *scp = tx_sc;
+ *secyp = secy;
+ return tx_sa;
+}
+
+static struct macsec_rx_sc *get_rxsc_from_nl(struct net *net,
+ struct nlattr **attrs,
+ struct nlattr **tb_rxsc,
+ struct net_device **devp,
+ struct macsec_secy **secyp)
+{
+ struct net_device *dev;
+ struct macsec_secy *secy;
+ struct macsec_rx_sc *rx_sc;
+ sci_t sci;
+
+ dev = get_dev_from_nl(net, attrs);
+ if (IS_ERR(dev))
+ return ERR_CAST(dev);
+
+ secy = &macsec_priv(dev)->secy;
+
+ if (!tb_rxsc[MACSEC_RXSC_ATTR_SCI])
+ return ERR_PTR(-EINVAL);
+
+ sci = nla_get_sci(tb_rxsc[MACSEC_RXSC_ATTR_SCI]);
+ rx_sc = find_rx_sc_rtnl(secy, sci);
+ if (!rx_sc)
+ return ERR_PTR(-ENODEV);
+
+ *secyp = secy;
+ *devp = dev;
+
+ return rx_sc;
+}
+
+static struct macsec_rx_sa *get_rxsa_from_nl(struct net *net,
+ struct nlattr **attrs,
+ struct nlattr **tb_rxsc,
+ struct nlattr **tb_sa,
+ struct net_device **devp,
+ struct macsec_secy **secyp,
+ struct macsec_rx_sc **scp,
+ u8 *assoc_num)
+{
+ struct macsec_rx_sc *rx_sc;
+ struct macsec_rx_sa *rx_sa;
+
+ if (!tb_sa[MACSEC_SA_ATTR_AN])
+ return ERR_PTR(-EINVAL);
+
+ *assoc_num = nla_get_u8(tb_sa[MACSEC_SA_ATTR_AN]);
+ if (*assoc_num >= MACSEC_NUM_AN)
+ return ERR_PTR(-EINVAL);
+
+ rx_sc = get_rxsc_from_nl(net, attrs, tb_rxsc, devp, secyp);
+ if (IS_ERR(rx_sc))
+ return ERR_CAST(rx_sc);
+
+ rx_sa = rtnl_dereference(rx_sc->sa[*assoc_num]);
+ if (!rx_sa)
+ return ERR_PTR(-ENODEV);
+
+ *scp = rx_sc;
+ return rx_sa;
+}
+
+
+static const struct nla_policy macsec_genl_policy[NUM_MACSEC_ATTR] = {
+ [MACSEC_ATTR_IFINDEX] = { .type = NLA_U32 },
+ [MACSEC_ATTR_RXSC_CONFIG] = { .type = NLA_NESTED },
+ [MACSEC_ATTR_SA_CONFIG] = { .type = NLA_NESTED },
+};
+
+static const struct nla_policy macsec_genl_rxsc_policy[NUM_MACSEC_RXSC_ATTR] = {
+ [MACSEC_RXSC_ATTR_SCI] = { .type = NLA_U64 },
+ [MACSEC_RXSC_ATTR_ACTIVE] = { .type = NLA_U8 },
+};
+
+static const struct nla_policy macsec_genl_sa_policy[NUM_MACSEC_SA_ATTR] = {
+ [MACSEC_SA_ATTR_AN] = { .type = NLA_U8 },
+ [MACSEC_SA_ATTR_ACTIVE] = { .type = NLA_U8 },
+ [MACSEC_SA_ATTR_PN] = { .type = NLA_U32 },
+ [MACSEC_SA_ATTR_KEYID] = { .type = NLA_U64 },
+ [MACSEC_SA_ATTR_KEY] = { .type = NLA_BINARY,
+ .len = MACSEC_MAX_KEY_LEN, },
+};
+
+static int parse_sa_config(struct nlattr **attrs, struct nlattr **tb_sa)
+{
+ if (!attrs[MACSEC_ATTR_SA_CONFIG])
+ return -EINVAL;
+
+ if (nla_parse_nested(tb_sa, MACSEC_SA_ATTR_MAX, attrs[MACSEC_ATTR_SA_CONFIG],
+ macsec_genl_sa_policy))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int parse_rxsc_config(struct nlattr **attrs, struct nlattr **tb_rxsc)
+{
+ if (!attrs[MACSEC_ATTR_RXSC_CONFIG])
+ return -EINVAL;
+
+ if (nla_parse_nested(tb_rxsc, MACSEC_RXSC_ATTR_MAX, attrs[MACSEC_ATTR_RXSC_CONFIG],
+ macsec_genl_rxsc_policy))
+ return -EINVAL;
+
+ return 0;
+}
+
+static bool validate_add_rxsa(struct nlattr **attrs)
+{
+ if (!attrs[MACSEC_SA_ATTR_AN] ||
+ !attrs[MACSEC_SA_ATTR_KEY] ||
+ !attrs[MACSEC_SA_ATTR_KEYID])
+ return false;
+
+ if (nla_get_u8(attrs[MACSEC_SA_ATTR_AN]) >= MACSEC_NUM_AN)
+ return false;
+
+ if (attrs[MACSEC_SA_ATTR_PN] && nla_get_u32(attrs[MACSEC_SA_ATTR_PN]) == 0)
+ return false;
+
+ if (attrs[MACSEC_SA_ATTR_ACTIVE]) {
+ if (nla_get_u8(attrs[MACSEC_SA_ATTR_ACTIVE]) > 1)
+ return false;
+ }
+
+ return true;
+}
+
+static int macsec_add_rxsa(struct sk_buff *skb, struct genl_info *info)
+{
+ struct net_device *dev;
+ struct nlattr **attrs = info->attrs;
+ struct macsec_secy *secy;
+ struct macsec_rx_sc *rx_sc;
+ struct macsec_rx_sa *rx_sa;
+ unsigned char assoc_num;
+ struct nlattr *tb_rxsc[MACSEC_RXSC_ATTR_MAX + 1];
+ struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1];
+
+ if (!attrs[MACSEC_ATTR_IFINDEX])
+ return -EINVAL;
+
+ if (parse_sa_config(attrs, tb_sa))
+ return -EINVAL;
+
+ if (parse_rxsc_config(attrs, tb_rxsc))
+ return -EINVAL;
+
+ if (!validate_add_rxsa(tb_sa))
+ return -EINVAL;
+
+ rtnl_lock();
+ rx_sc = get_rxsc_from_nl(genl_info_net(info), attrs, tb_rxsc, &dev, &secy);
+ if (IS_ERR(rx_sc) || !macsec_rxsc_get(rx_sc)) {
+ rtnl_unlock();
+ return PTR_ERR(rx_sc);
+ }
+
+ assoc_num = nla_get_u8(tb_sa[MACSEC_SA_ATTR_AN]);
+
+ if (nla_len(tb_sa[MACSEC_SA_ATTR_KEY]) != secy->key_len) {
+ pr_notice("macsec: nl: add_rxsa: bad key length: %d != %d\n",
+ nla_len(tb_sa[MACSEC_SA_ATTR_KEY]), secy->key_len);
+ rtnl_unlock();
+ return -EINVAL;
+ }
+
+ rx_sa = rtnl_dereference(rx_sc->sa[assoc_num]);
+ if (rx_sa) {
+ rtnl_unlock();
+ return -EBUSY;
+ }
+
+ rx_sa = kmalloc(sizeof(*rx_sa), GFP_KERNEL);
+ if (init_rx_sa(rx_sa, nla_data(tb_sa[MACSEC_SA_ATTR_KEY]), secy->key_len,
+ secy->icv_len)) {
+ rtnl_unlock();
+ return -ENOMEM;
+ }
+
+ if (tb_sa[MACSEC_SA_ATTR_PN]) {
+ spin_lock_bh(&rx_sa->lock);
+ rx_sa->next_pn = nla_get_u32(tb_sa[MACSEC_SA_ATTR_PN]);
+ spin_unlock_bh(&rx_sa->lock);
+ }
+
+ if (tb_sa[MACSEC_SA_ATTR_ACTIVE])
+ rx_sa->active = !!nla_get_u8(tb_sa[MACSEC_SA_ATTR_ACTIVE]);
+
+ rx_sa->key.id = nla_get_u64(tb_sa[MACSEC_SA_ATTR_KEYID]);
+ rx_sa->sc = rx_sc;
+ rcu_assign_pointer(rx_sc->sa[assoc_num], rx_sa);
+
+ rtnl_unlock();
+
+ return 0;
+}
+
+static bool validate_add_rxsc(struct nlattr **attrs)
+{
+ if (!attrs[MACSEC_RXSC_ATTR_SCI])
+ return false;
+
+ if (attrs[MACSEC_RXSC_ATTR_ACTIVE]) {
+ if (nla_get_u8(attrs[MACSEC_RXSC_ATTR_ACTIVE]) > 1)
+ return false;
+ }
+
+ return true;
+}
+
+static int macsec_add_rxsc(struct sk_buff *skb, struct genl_info *info)
+{
+ struct net_device *dev;
+ sci_t sci = MACSEC_UNDEF_SCI;
+ struct nlattr **attrs = info->attrs;
+ struct macsec_rx_sc *rx_sc;
+ struct nlattr *tb_rxsc[MACSEC_RXSC_ATTR_MAX + 1];
+
+ if (!attrs[MACSEC_ATTR_IFINDEX])
+ return -EINVAL;
+
+ if (parse_rxsc_config(attrs, tb_rxsc))
+ return -EINVAL;
+
+ if (!validate_add_rxsc(tb_rxsc))
+ return -EINVAL;
+
+ rtnl_lock();
+ dev = get_dev_from_nl(genl_info_net(info), attrs);
+ if (IS_ERR(dev)) {
+ rtnl_unlock();
+ return PTR_ERR(dev);
+ }
+
+ sci = nla_get_sci(tb_rxsc[MACSEC_RXSC_ATTR_SCI]);
+
+ rx_sc = create_rx_sc(dev, sci);
+ if (IS_ERR(rx_sc)) {
+ rtnl_unlock();
+ return PTR_ERR(rx_sc);
+ }
+
+ if (tb_rxsc[MACSEC_RXSC_ATTR_ACTIVE])
+ rx_sc->active = !!nla_get_u8(tb_rxsc[MACSEC_RXSC_ATTR_ACTIVE]);
+
+ rtnl_unlock();
+
+ return 0;
+}
+
+static bool validate_add_txsa(struct nlattr **attrs)
+{
+ if (!attrs[MACSEC_SA_ATTR_AN] ||
+ !attrs[MACSEC_SA_ATTR_PN] ||
+ !attrs[MACSEC_SA_ATTR_KEY] ||
+ !attrs[MACSEC_SA_ATTR_KEYID])
+ return false;
+
+ if (nla_get_u8(attrs[MACSEC_SA_ATTR_AN]) >= MACSEC_NUM_AN)
+ return false;
+
+ if (nla_get_u32(attrs[MACSEC_SA_ATTR_PN]) == 0)
+ return false;
+
+ if (attrs[MACSEC_SA_ATTR_ACTIVE]) {
+ if (nla_get_u8(attrs[MACSEC_SA_ATTR_ACTIVE]) > 1)
+ return false;
+ }
+
+ return true;
+}
+
+static int macsec_add_txsa(struct sk_buff *skb, struct genl_info *info)
+{
+ struct net_device *dev;
+ struct nlattr **attrs = info->attrs;
+ struct macsec_secy *secy;
+ struct macsec_tx_sc *tx_sc;
+ struct macsec_tx_sa *tx_sa;
+ unsigned char assoc_num;
+ struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1];
+
+ if (!attrs[MACSEC_ATTR_IFINDEX])
+ return -EINVAL;
+
+ if (parse_sa_config(attrs, tb_sa))
+ return -EINVAL;
+
+ if (!validate_add_txsa(tb_sa))
+ return -EINVAL;
+
+ rtnl_lock();
+ dev = get_dev_from_nl(genl_info_net(info), attrs);
+ if (IS_ERR(dev)) {
+ rtnl_unlock();
+ return PTR_ERR(dev);
+ }
+
+ secy = &macsec_priv(dev)->secy;
+ tx_sc = &secy->tx_sc;
+
+ assoc_num = nla_get_u8(tb_sa[MACSEC_SA_ATTR_AN]);
+
+ if (nla_len(tb_sa[MACSEC_SA_ATTR_KEY]) != secy->key_len) {
+ pr_notice("macsec: nl: add_txsa: bad key length: %d != %d\n",
+ nla_len(tb_sa[MACSEC_SA_ATTR_KEY]), secy->key_len);
+ rtnl_unlock();
+ return -EINVAL;
+ }
+
+ tx_sa = rtnl_dereference(tx_sc->sa[assoc_num]);
+ if (tx_sa) {
+ rtnl_unlock();
+ return -EBUSY;
+ }
+
+ tx_sa = kmalloc(sizeof(*tx_sa), GFP_KERNEL);
+ if (!tx_sa || init_tx_sa(tx_sa, nla_data(tb_sa[MACSEC_SA_ATTR_KEY]),
+ secy->key_len, secy->icv_len)) {
+ rtnl_unlock();
+ return -ENOMEM;
+ }
+
+ tx_sa->key.id = nla_get_u64(tb_sa[MACSEC_SA_ATTR_KEYID]);
+
+ spin_lock_bh(&tx_sa->lock);
+ tx_sa->next_pn = nla_get_u32(tb_sa[MACSEC_SA_ATTR_PN]);
+ spin_unlock_bh(&tx_sa->lock);
+
+ if (tb_sa[MACSEC_SA_ATTR_ACTIVE])
+ tx_sa->active = !!nla_get_u8(tb_sa[MACSEC_SA_ATTR_ACTIVE]);
+
+ if (assoc_num == tx_sc->encoding_sa && tx_sa->active)
+ secy->operational = true;
+
+ rcu_assign_pointer(tx_sc->sa[assoc_num], tx_sa);
+
+ rtnl_unlock();
+
+ return 0;
+}
+
+static int macsec_del_rxsa(struct sk_buff *skb, struct genl_info *info)
+{
+ struct nlattr **attrs = info->attrs;
+ struct net_device *dev;
+ struct macsec_secy *secy;
+ struct macsec_rx_sc *rx_sc;
+ struct macsec_rx_sa *rx_sa;
+ u8 assoc_num;
+ struct nlattr *tb_rxsc[MACSEC_RXSC_ATTR_MAX + 1];
+ struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1];
+
+ if (!attrs[MACSEC_ATTR_IFINDEX])
+ return -EINVAL;
+
+ if (parse_sa_config(attrs, tb_sa))
+ return -EINVAL;
+
+ if (parse_rxsc_config(attrs, tb_rxsc))
+ return -EINVAL;
+
+ rtnl_lock();
+ rx_sa = get_rxsa_from_nl(genl_info_net(info), attrs, tb_rxsc, tb_sa,
+ &dev, &secy, &rx_sc, &assoc_num);
+ if (IS_ERR(rx_sa)) {
+ rtnl_unlock();
+ return PTR_ERR(rx_sa);
+ }
+
+ if (rx_sa->active) {
+ rtnl_unlock();
+ return -EBUSY;
+ }
+
+ RCU_INIT_POINTER(rx_sc->sa[assoc_num], NULL);
+ clear_rx_sa(rx_sa);
+
+ rtnl_unlock();
+
+ return 0;
+}
+
+static int macsec_del_rxsc(struct sk_buff *skb, struct genl_info *info)
+{
+ struct nlattr **attrs = info->attrs;
+ struct net_device *dev;
+ struct macsec_secy *secy;
+ struct macsec_rx_sc *rx_sc;
+ sci_t sci;
+ struct nlattr *tb_rxsc[MACSEC_RXSC_ATTR_MAX + 1];
+
+ if (!attrs[MACSEC_ATTR_IFINDEX])
+ return -EINVAL;
+
+ if (parse_rxsc_config(attrs, tb_rxsc))
+ return -EINVAL;
+
+ if (!tb_rxsc[MACSEC_RXSC_ATTR_SCI])
+ return -EINVAL;
+
+ rtnl_lock();
+ dev = get_dev_from_nl(genl_info_net(info), info->attrs);
+ if (IS_ERR(dev)) {
+ rtnl_unlock();
+ return PTR_ERR(dev);
+ }
+
+ secy = &macsec_priv(dev)->secy;
+ sci = nla_get_sci(tb_rxsc[MACSEC_RXSC_ATTR_SCI]);
+
+ rx_sc = del_rx_sc(secy, sci);
+ if (!rx_sc) {
+ rtnl_unlock();
+ return -ENODEV;
+ }
+
+ free_rx_sc(rx_sc);
+ rtnl_unlock();
+
+ return 0;
+}
+
+static int macsec_del_txsa(struct sk_buff *skb, struct genl_info *info)
+{
+ struct nlattr **attrs = info->attrs;
+ struct net_device *dev;
+ struct macsec_secy *secy;
+ struct macsec_tx_sc *tx_sc;
+ struct macsec_tx_sa *tx_sa;
+ u8 assoc_num;
+ struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1];
+
+ if (!attrs[MACSEC_ATTR_IFINDEX])
+ return -EINVAL;
+
+ if (parse_sa_config(attrs, tb_sa))
+ return -EINVAL;
+
+ rtnl_lock();
+ tx_sa = get_txsa_from_nl(genl_info_net(info), attrs, tb_sa,
+ &dev, &secy, &tx_sc, &assoc_num);
+ if (IS_ERR(tx_sa)) {
+ rtnl_unlock();
+ return PTR_ERR(tx_sa);
+ }
+
+ if (tx_sa->active) {
+ rtnl_unlock();
+ return -EBUSY;
+ }
+
+ RCU_INIT_POINTER(tx_sc->sa[assoc_num], NULL);
+ clear_tx_sa(tx_sa);
+
+ rtnl_unlock();
+
+ return 0;
+}
+
+static bool validate_upd_sa(struct nlattr **attrs)
+{
+ if (!attrs[MACSEC_SA_ATTR_AN] ||
+ attrs[MACSEC_SA_ATTR_KEY] ||
+ attrs[MACSEC_SA_ATTR_KEYID])
+ return false;
+
+ if (nla_get_u8(attrs[MACSEC_SA_ATTR_AN]) >= MACSEC_NUM_AN)
+ return false;
+
+ if (attrs[MACSEC_SA_ATTR_PN] && nla_get_u32(attrs[MACSEC_SA_ATTR_PN]) == 0)
+ return false;
+
+ if (attrs[MACSEC_SA_ATTR_ACTIVE]) {
+ if (nla_get_u8(attrs[MACSEC_SA_ATTR_ACTIVE]) > 1)
+ return false;
+ }
+
+ return true;
+}
+
+static int macsec_upd_txsa(struct sk_buff *skb, struct genl_info *info)
+{
+ struct nlattr **attrs = info->attrs;
+ struct net_device *dev;
+ struct macsec_secy *secy;
+ struct macsec_tx_sc *tx_sc;
+ struct macsec_tx_sa *tx_sa;
+ u8 assoc_num;
+ struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1];
+
+ if (!attrs[MACSEC_ATTR_IFINDEX])
+ return -EINVAL;
+
+ if (parse_sa_config(attrs, tb_sa))
+ return -EINVAL;
+
+ if (!validate_upd_sa(tb_sa))
+ return -EINVAL;
+
+ rtnl_lock();
+ tx_sa = get_txsa_from_nl(genl_info_net(info), attrs, tb_sa,
+ &dev, &secy, &tx_sc, &assoc_num);
+ if (IS_ERR(tx_sa)) {
+ rtnl_unlock();
+ return PTR_ERR(tx_sa);
+ }
+
+ if (tb_sa[MACSEC_SA_ATTR_PN]) {
+ spin_lock_bh(&tx_sa->lock);
+ tx_sa->next_pn = nla_get_u32(tb_sa[MACSEC_SA_ATTR_PN]);
+ spin_unlock_bh(&tx_sa->lock);
+ }
+
+ if (tb_sa[MACSEC_SA_ATTR_ACTIVE])
+ tx_sa->active = nla_get_u8(tb_sa[MACSEC_SA_ATTR_ACTIVE]);
+
+ if (assoc_num == tx_sc->encoding_sa)
+ secy->operational = tx_sa->active;
+
+ rtnl_unlock();
+
+ return 0;
+}
+
+static int macsec_upd_rxsa(struct sk_buff *skb, struct genl_info *info)
+{
+ struct nlattr **attrs = info->attrs;
+ struct net_device *dev;
+ struct macsec_secy *secy;
+ struct macsec_rx_sc *rx_sc;
+ struct macsec_rx_sa *rx_sa;
+ u8 assoc_num;
+ struct nlattr *tb_rxsc[MACSEC_RXSC_ATTR_MAX + 1];
+ struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1];
+
+ if (!attrs[MACSEC_ATTR_IFINDEX])
+ return -EINVAL;
+
+ if (parse_rxsc_config(attrs, tb_rxsc))
+ return -EINVAL;
+
+ if (parse_sa_config(attrs, tb_sa))
+ return -EINVAL;
+
+ if (!validate_upd_sa(tb_sa))
+ return -EINVAL;
+
+ rtnl_lock();
+ rx_sa = get_rxsa_from_nl(genl_info_net(info), attrs, tb_rxsc, tb_sa,
+ &dev, &secy, &rx_sc, &assoc_num);
+ if (IS_ERR(rx_sa)) {
+ rtnl_unlock();
+ return PTR_ERR(rx_sa);
+ }
+
+ if (tb_sa[MACSEC_SA_ATTR_PN]) {
+ spin_lock_bh(&rx_sa->lock);
+ rx_sa->next_pn = nla_get_u32(tb_sa[MACSEC_SA_ATTR_PN]);
+ spin_unlock_bh(&rx_sa->lock);
+ }
+
+ if (tb_sa[MACSEC_SA_ATTR_ACTIVE])
+ rx_sa->active = nla_get_u8(tb_sa[MACSEC_SA_ATTR_ACTIVE]);
+
+ rtnl_unlock();
+ return 0;
+}
+
+static int macsec_upd_rxsc(struct sk_buff *skb, struct genl_info *info)
+{
+ struct nlattr **attrs = info->attrs;
+ struct net_device *dev;
+ struct macsec_secy *secy;
+ struct macsec_rx_sc *rx_sc;
+ struct nlattr *tb_rxsc[MACSEC_RXSC_ATTR_MAX + 1];
+
+ if (!attrs[MACSEC_ATTR_IFINDEX])
+ return -EINVAL;
+
+ if (parse_rxsc_config(attrs, tb_rxsc))
+ return -EINVAL;
+
+ if (!validate_add_rxsc(tb_rxsc))
+ return -EINVAL;
+
+ rtnl_lock();
+ rx_sc = get_rxsc_from_nl(genl_info_net(info), attrs, tb_rxsc, &dev, &secy);
+ if (IS_ERR(rx_sc)) {
+ rtnl_unlock();
+ return PTR_ERR(rx_sc);
+ }
+
+ if (tb_rxsc[MACSEC_RXSC_ATTR_ACTIVE]) {
+ bool new = !!nla_get_u8(tb_rxsc[MACSEC_RXSC_ATTR_ACTIVE]);
+
+ if (rx_sc->active != new)
+ secy->n_rx_sc += new ? 1 : -1;
+
+ rx_sc->active = new;
+ }
+
+ rtnl_unlock();
+
+ return 0;
+}
+
+static int copy_tx_sa_stats(struct sk_buff *skb,
+ struct macsec_tx_sa_stats __percpu *pstats)
+{
+ struct macsec_tx_sa_stats sum = {0, };
+ int cpu;
+
+ for_each_possible_cpu(cpu) {
+ const struct macsec_tx_sa_stats *stats = per_cpu_ptr(pstats, cpu);
+
+ sum.OutPktsProtected += stats->OutPktsProtected;
+ sum.OutPktsEncrypted += stats->OutPktsEncrypted;
+ }
+
+ if (nla_put_u32(skb, MACSEC_SA_STATS_ATTR_OUT_PKTS_PROTECTED, sum.OutPktsProtected) ||
+ nla_put_u32(skb, MACSEC_SA_STATS_ATTR_OUT_PKTS_ENCRYPTED, sum.OutPktsEncrypted))
+ return -EMSGSIZE;
+
+ return 0;
+}
+
+static int copy_rx_sa_stats(struct sk_buff *skb,
+ struct macsec_rx_sa_stats __percpu *pstats)
+{
+ struct macsec_rx_sa_stats sum = {0, };
+ int cpu;
+
+ for_each_possible_cpu(cpu) {
+ const struct macsec_rx_sa_stats *stats = per_cpu_ptr(pstats, cpu);
+
+ sum.InPktsOK += stats->InPktsOK;
+ sum.InPktsInvalid += stats->InPktsInvalid;
+ sum.InPktsNotValid += stats->InPktsNotValid;
+ sum.InPktsNotUsingSA += stats->InPktsNotUsingSA;
+ sum.InPktsUnusedSA += stats->InPktsUnusedSA;
+ }
+
+ if (nla_put_u32(skb, MACSEC_SA_STATS_ATTR_IN_PKTS_OK, sum.InPktsOK) ||
+ nla_put_u32(skb, MACSEC_SA_STATS_ATTR_IN_PKTS_INVALID, sum.InPktsInvalid) ||
+ nla_put_u32(skb, MACSEC_SA_STATS_ATTR_IN_PKTS_NOT_VALID, sum.InPktsNotValid) ||
+ nla_put_u32(skb, MACSEC_SA_STATS_ATTR_IN_PKTS_NOT_USING_SA, sum.InPktsNotUsingSA) ||
+ nla_put_u32(skb, MACSEC_SA_STATS_ATTR_IN_PKTS_UNUSED_SA, sum.InPktsUnusedSA))
+ return -EMSGSIZE;
+
+ return 0;
+}
+
+static int copy_rx_sc_stats(struct sk_buff *skb,
+ struct pcpu_rx_sc_stats __percpu *pstats)
+{
+ struct macsec_rx_sc_stats sum = {0, };
+ int cpu;
+
+ for_each_possible_cpu(cpu) {
+ const struct pcpu_rx_sc_stats *stats;
+ struct macsec_rx_sc_stats tmp;
+ unsigned int start;
+
+ stats = per_cpu_ptr(pstats, cpu);
+ do {
+ start = u64_stats_fetch_begin_irq(&stats->syncp);
+ memcpy(&tmp, &stats->stats, sizeof(tmp));
+ } while (u64_stats_fetch_retry_irq(&stats->syncp, start));
+
+ sum.InOctetsValidated += tmp.InOctetsValidated;
+ sum.InOctetsDecrypted += tmp.InOctetsDecrypted;
+ sum.InPktsUnchecked += tmp.InPktsUnchecked;
+ sum.InPktsDelayed += tmp.InPktsDelayed;
+ sum.InPktsOK += tmp.InPktsOK;
+ sum.InPktsInvalid += tmp.InPktsInvalid;
+ sum.InPktsLate += tmp.InPktsLate;
+ sum.InPktsNotValid += tmp.InPktsNotValid;
+ sum.InPktsNotUsingSA += tmp.InPktsNotUsingSA;
+ sum.InPktsUnusedSA += tmp.InPktsUnusedSA;
+ }
+
+ if (nla_put_u64(skb, MACSEC_RXSC_STATS_ATTR_IN_OCTETS_VALIDATED, sum.InOctetsValidated) ||
+ nla_put_u64(skb, MACSEC_RXSC_STATS_ATTR_IN_OCTETS_DECRYPTED, sum.InOctetsDecrypted) ||
+ nla_put_u64(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_UNCHECKED, sum.InPktsUnchecked) ||
+ nla_put_u64(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_DELAYED, sum.InPktsDelayed) ||
+ nla_put_u64(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_OK, sum.InPktsOK) ||
+ nla_put_u64(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_INVALID, sum.InPktsInvalid) ||
+ nla_put_u64(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_LATE, sum.InPktsLate) ||
+ nla_put_u64(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_NOT_VALID, sum.InPktsNotValid) ||
+ nla_put_u64(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_NOT_USING_SA, sum.InPktsNotUsingSA) ||
+ nla_put_u64(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_UNUSED_SA, sum.InPktsUnusedSA))
+ return -EMSGSIZE;
+
+ return 0;
+}
+
+static int copy_tx_sc_stats(struct sk_buff *skb,
+ struct pcpu_tx_sc_stats __percpu *pstats)
+{
+ struct macsec_tx_sc_stats sum = {0, };
+ int cpu;
+
+ for_each_possible_cpu(cpu) {
+ const struct pcpu_tx_sc_stats *stats;
+ struct macsec_tx_sc_stats tmp;
+ unsigned int start;
+
+ stats = per_cpu_ptr(pstats, cpu);
+ do {
+ start = u64_stats_fetch_begin_irq(&stats->syncp);
+ memcpy(&tmp, &stats->stats, sizeof(tmp));
+ } while (u64_stats_fetch_retry_irq(&stats->syncp, start));
+
+ sum.OutPktsProtected += tmp.OutPktsProtected;
+ sum.OutPktsEncrypted += tmp.OutPktsEncrypted;
+ sum.OutOctetsProtected += tmp.OutOctetsProtected;
+ sum.OutOctetsEncrypted += tmp.OutOctetsEncrypted;
+ }
+
+ if (nla_put_u64(skb, MACSEC_TXSC_STATS_ATTR_OUT_PKTS_PROTECTED, sum.OutPktsProtected) ||
+ nla_put_u64(skb, MACSEC_TXSC_STATS_ATTR_OUT_PKTS_ENCRYPTED, sum.OutPktsEncrypted) ||
+ nla_put_u64(skb, MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_PROTECTED, sum.OutOctetsProtected) ||
+ nla_put_u64(skb, MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_ENCRYPTED, sum.OutOctetsEncrypted))
+ return -EMSGSIZE;
+
+ return 0;
+}
+
+static int copy_secy_stats(struct sk_buff *skb,
+ struct pcpu_secy_stats __percpu *pstats)
+{
+ struct macsec_dev_stats sum = {0, };
+ int cpu;
+
+ for_each_possible_cpu(cpu) {
+ const struct pcpu_secy_stats *stats;
+ struct macsec_dev_stats tmp;
+ unsigned int start;
+
+ stats = per_cpu_ptr(pstats, cpu);
+ do {
+ start = u64_stats_fetch_begin_irq(&stats->syncp);
+ memcpy(&tmp, &stats->stats, sizeof(tmp));
+ } while (u64_stats_fetch_retry_irq(&stats->syncp, start));
+
+ sum.OutPktsUntagged += tmp.OutPktsUntagged;
+ sum.InPktsUntagged += tmp.InPktsUntagged;
+ sum.OutPktsTooLong += tmp.OutPktsTooLong;
+ sum.InPktsNoTag += tmp.InPktsNoTag;
+ sum.InPktsBadTag += tmp.InPktsBadTag;
+ sum.InPktsUnknownSCI += tmp.InPktsUnknownSCI;
+ sum.InPktsNoSCI += tmp.InPktsNoSCI;
+ sum.InPktsOverrun += tmp.InPktsOverrun;
+ }
+
+ if (nla_put_u64(skb, MACSEC_SECY_STATS_ATTR_OUT_PKTS_UNTAGGED, sum.OutPktsUntagged) ||
+ nla_put_u64(skb, MACSEC_SECY_STATS_ATTR_IN_PKTS_UNTAGGED, sum.InPktsUntagged) ||
+ nla_put_u64(skb, MACSEC_SECY_STATS_ATTR_OUT_PKTS_TOO_LONG, sum.OutPktsTooLong) ||
+ nla_put_u64(skb, MACSEC_SECY_STATS_ATTR_IN_PKTS_NO_TAG, sum.InPktsNoTag) ||
+ nla_put_u64(skb, MACSEC_SECY_STATS_ATTR_IN_PKTS_BAD_TAG, sum.InPktsBadTag) ||
+ nla_put_u64(skb, MACSEC_SECY_STATS_ATTR_IN_PKTS_UNKNOWN_SCI, sum.InPktsUnknownSCI) ||
+ nla_put_u64(skb, MACSEC_SECY_STATS_ATTR_IN_PKTS_NO_SCI, sum.InPktsNoSCI) ||
+ nla_put_u64(skb, MACSEC_SECY_STATS_ATTR_IN_PKTS_OVERRUN, sum.InPktsOverrun))
+ return -EMSGSIZE;
+
+ return 0;
+}
+
+static int nla_put_secy(struct macsec_secy *secy, struct sk_buff *skb)
+{
+ struct macsec_tx_sc *tx_sc = &secy->tx_sc;
+ struct nlattr *secy_nest = nla_nest_start(skb, MACSEC_ATTR_SECY);
+
+ if (!secy_nest)
+ return 1;
+
+ if (nla_put_sci(skb, MACSEC_SECY_ATTR_SCI, secy->sci) ||
+ nla_put_u64(skb, MACSEC_SECY_ATTR_CIPHER_SUITE, DEFAULT_CIPHER_ID) ||
+ nla_put_u8(skb, MACSEC_SECY_ATTR_ICV_LEN, secy->icv_len) ||
+ nla_put_u8(skb, MACSEC_SECY_ATTR_OPER, secy->operational) ||
+ nla_put_u8(skb, MACSEC_SECY_ATTR_PROTECT, secy->protect_frames) ||
+ nla_put_u8(skb, MACSEC_SECY_ATTR_REPLAY, secy->replay_protect) ||
+ nla_put_u8(skb, MACSEC_SECY_ATTR_VALIDATE, secy->validate_frames) ||
+ nla_put_u8(skb, MACSEC_SECY_ATTR_ENCRYPT, tx_sc->encrypt) ||
+ nla_put_u8(skb, MACSEC_SECY_ATTR_INC_SCI, tx_sc->send_sci) ||
+ nla_put_u8(skb, MACSEC_SECY_ATTR_ES, tx_sc->end_station) ||
+ nla_put_u8(skb, MACSEC_SECY_ATTR_SCB, tx_sc->scb) ||
+ nla_put_u8(skb, MACSEC_SECY_ATTR_ENCODING_SA, tx_sc->encoding_sa))
+ goto cancel;
+
+ if (secy->replay_protect) {
+ if (nla_put_u32(skb, MACSEC_SECY_ATTR_WINDOW, secy->replay_window))
+ goto cancel;
+ }
+
+ nla_nest_end(skb, secy_nest);
+ return 0;
+
+cancel:
+ nla_nest_cancel(skb, secy_nest);
+ return 1;
+}
+
+static int dump_secy(struct macsec_secy *secy, struct net_device *dev,
+ struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct macsec_rx_sc *rx_sc;
+ struct macsec_tx_sc *tx_sc = &secy->tx_sc;
+ struct nlattr *txsa_list, *rxsc_list;
+ int i, j;
+ void *hdr;
+ struct nlattr *attr;
+
+ hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
+ &macsec_fam, NLM_F_MULTI, MACSEC_CMD_GET_TXSC);
+ if (!hdr)
+ return -EMSGSIZE;
+
+ rtnl_lock();
+
+ if (nla_put_u32(skb, MACSEC_ATTR_IFINDEX, dev->ifindex))
+ goto nla_put_failure;
+
+ if (nla_put_secy(secy, skb))
+ goto nla_put_failure;
+
+ attr = nla_nest_start(skb, MACSEC_ATTR_TXSC_STATS);
+ if (!attr)
+ goto nla_put_failure;
+ if (copy_tx_sc_stats(skb, tx_sc->stats)) {
+ nla_nest_cancel(skb, attr);
+ goto nla_put_failure;
+ }
+ nla_nest_end(skb, attr);
+
+ attr = nla_nest_start(skb, MACSEC_ATTR_SECY_STATS);
+ if (!attr)
+ goto nla_put_failure;
+ if (copy_secy_stats(skb, macsec_priv(dev)->stats)) {
+ nla_nest_cancel(skb, attr);
+ goto nla_put_failure;
+ }
+ nla_nest_end(skb, attr);
+
+ txsa_list = nla_nest_start(skb, MACSEC_ATTR_TXSA_LIST);
+ if (!txsa_list)
+ goto nla_put_failure;
+ for (i = 0, j = 1; i < MACSEC_NUM_AN; i++) {
+ struct macsec_tx_sa *tx_sa = rtnl_dereference(tx_sc->sa[i]);
+ struct nlattr *txsa_nest;
+
+ if (!tx_sa)
+ continue;
+
+ txsa_nest = nla_nest_start(skb, j++);
+ if (!txsa_nest) {
+ nla_nest_cancel(skb, txsa_list);
+ goto nla_put_failure;
+ }
+
+ if (nla_put_u8(skb, MACSEC_SA_ATTR_AN, i) ||
+ nla_put_u32(skb, MACSEC_SA_ATTR_PN, tx_sa->next_pn) ||
+ nla_put_u64(skb, MACSEC_SA_ATTR_KEYID, tx_sa->key.id) ||
+ nla_put_u8(skb, MACSEC_SA_ATTR_ACTIVE, tx_sa->active)) {
+ nla_nest_cancel(skb, txsa_nest);
+ nla_nest_cancel(skb, txsa_list);
+ goto nla_put_failure;
+ }
+
+ attr = nla_nest_start(skb, MACSEC_SA_ATTR_STATS);
+ if (!attr) {
+ nla_nest_cancel(skb, txsa_nest);
+ nla_nest_cancel(skb, txsa_list);
+ goto nla_put_failure;
+ }
+ if (copy_tx_sa_stats(skb, tx_sa->stats)) {
+ nla_nest_cancel(skb, attr);
+ nla_nest_cancel(skb, txsa_nest);
+ nla_nest_cancel(skb, txsa_list);
+ goto nla_put_failure;
+ }
+ nla_nest_end(skb, attr);
+
+ nla_nest_end(skb, txsa_nest);
+ }
+ nla_nest_end(skb, txsa_list);
+
+ rxsc_list = nla_nest_start(skb, MACSEC_ATTR_RXSC_LIST);
+ if (!rxsc_list)
+ goto nla_put_failure;
+
+ j = 1;
+ for_each_rxsc_rtnl(secy, rx_sc) {
+ int k;
+ struct nlattr *rxsa_list;
+ struct nlattr *rxsc_nest = nla_nest_start(skb, j++);
+
+ if (!rxsc_nest) {
+ nla_nest_cancel(skb, rxsc_list);
+ goto nla_put_failure;
+ }
+
+ if (nla_put_u8(skb, MACSEC_RXSC_ATTR_ACTIVE, rx_sc->active) ||
+ nla_put_sci(skb, MACSEC_RXSC_ATTR_SCI, rx_sc->sci)) {
+ nla_nest_cancel(skb, rxsc_nest);
+ nla_nest_cancel(skb, rxsc_list);
+ goto nla_put_failure;
+ }
+
+ attr = nla_nest_start(skb, MACSEC_RXSC_ATTR_STATS);
+ if (!attr) {
+ nla_nest_cancel(skb, rxsc_nest);
+ nla_nest_cancel(skb, rxsc_list);
+ goto nla_put_failure;
+ }
+ if (copy_rx_sc_stats(skb, rx_sc->stats)) {
+ nla_nest_cancel(skb, attr);
+ nla_nest_cancel(skb, rxsc_nest);
+ nla_nest_cancel(skb, rxsc_list);
+ goto nla_put_failure;
+ }
+ nla_nest_end(skb, attr);
+
+ rxsa_list = nla_nest_start(skb, MACSEC_RXSC_ATTR_SA_LIST);
+ if (!rxsa_list) {
+ nla_nest_cancel(skb, rxsc_nest);
+ nla_nest_cancel(skb, rxsc_list);
+ goto nla_put_failure;
+ }
+
+ for (i = 0, k = 1; i < MACSEC_NUM_AN; i++) {
+ struct macsec_rx_sa *rx_sa = rtnl_dereference(rx_sc->sa[i]);
+ struct nlattr *rxsa_nest;
+
+ if (!rx_sa)
+ continue;
+
+ rxsa_nest = nla_nest_start(skb, k++);
+ if (!rxsa_nest) {
+ nla_nest_cancel(skb, rxsa_list);
+ nla_nest_cancel(skb, rxsc_nest);
+ nla_nest_cancel(skb, rxsc_list);
+ goto nla_put_failure;
+ }
+
+ attr = nla_nest_start(skb, MACSEC_SA_ATTR_STATS);
+ if (!attr) {
+ nla_nest_cancel(skb, rxsa_list);
+ nla_nest_cancel(skb, rxsc_nest);
+ nla_nest_cancel(skb, rxsc_list);
+ goto nla_put_failure;
+ }
+ if (copy_rx_sa_stats(skb, rx_sa->stats)) {
+ nla_nest_cancel(skb, attr);
+ nla_nest_cancel(skb, rxsa_list);
+ nla_nest_cancel(skb, rxsc_nest);
+ nla_nest_cancel(skb, rxsc_list);
+ goto nla_put_failure;
+ }
+ nla_nest_end(skb, attr);
+
+ if (nla_put_u8(skb, MACSEC_SA_ATTR_AN, i) ||
+ nla_put_u32(skb, MACSEC_SA_ATTR_PN, rx_sa->next_pn) ||
+ nla_put_u64(skb, MACSEC_SA_ATTR_KEYID, rx_sa->key.id) ||
+ nla_put_u8(skb, MACSEC_SA_ATTR_ACTIVE, rx_sa->active)) {
+ nla_nest_cancel(skb, rxsa_nest);
+ nla_nest_cancel(skb, rxsc_nest);
+ nla_nest_cancel(skb, rxsc_list);
+ goto nla_put_failure;
+ }
+ nla_nest_end(skb, rxsa_nest);
+ }
+
+ nla_nest_end(skb, rxsa_list);
+ nla_nest_end(skb, rxsc_nest);
+ }
+
+ nla_nest_end(skb, rxsc_list);
+
+ rtnl_unlock();
+
+ genlmsg_end(skb, hdr);
+
+ return 0;
+
+nla_put_failure:
+ rtnl_unlock();
+ genlmsg_cancel(skb, hdr);
+ return -EMSGSIZE;
+}
+
+static int macsec_dump_txsc(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct net *net = sock_net(skb->sk);
+ struct net_device *dev;
+ int dev_idx, d;
+
+ dev_idx = cb->args[0];
+
+ d = 0;
+ for_each_netdev(net, dev) {
+ struct macsec_secy *secy;
+
+ if (d < dev_idx)
+ goto next;
+
+ if (!netif_is_macsec(dev))
+ goto next;
+
+ secy = &macsec_priv(dev)->secy;
+ if (dump_secy(secy, dev, skb, cb) < 0)
+ goto done;
+next:
+ d++;
+ }
+
+done:
+ cb->args[0] = d;
+ return skb->len;
+}
+
+static const struct genl_ops macsec_genl_ops[] = {
+ {
+ .cmd = MACSEC_CMD_GET_TXSC,
+ .dumpit = macsec_dump_txsc,
+ .policy = macsec_genl_policy,
+ },
+ {
+ .cmd = MACSEC_CMD_ADD_RXSC,
+ .doit = macsec_add_rxsc,
+ .policy = macsec_genl_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = MACSEC_CMD_DEL_RXSC,
+ .doit = macsec_del_rxsc,
+ .policy = macsec_genl_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = MACSEC_CMD_UPD_RXSC,
+ .doit = macsec_upd_rxsc,
+ .policy = macsec_genl_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = MACSEC_CMD_ADD_TXSA,
+ .doit = macsec_add_txsa,
+ .policy = macsec_genl_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = MACSEC_CMD_DEL_TXSA,
+ .doit = macsec_del_txsa,
+ .policy = macsec_genl_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = MACSEC_CMD_UPD_TXSA,
+ .doit = macsec_upd_txsa,
+ .policy = macsec_genl_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = MACSEC_CMD_ADD_RXSA,
+ .doit = macsec_add_rxsa,
+ .policy = macsec_genl_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = MACSEC_CMD_DEL_RXSA,
+ .doit = macsec_del_rxsa,
+ .policy = macsec_genl_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = MACSEC_CMD_UPD_RXSA,
+ .doit = macsec_upd_rxsa,
+ .policy = macsec_genl_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+};
+
+static netdev_tx_t macsec_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct macsec_dev *macsec = netdev_priv(dev);
+ struct macsec_secy *secy = &macsec->secy;
+ struct pcpu_secy_stats *secy_stats;
+ int ret, len;
+
+ /* 10.5 */
+ if (!secy->protect_frames) {
+ secy_stats = this_cpu_ptr(macsec->stats);
+ u64_stats_update_begin(&secy_stats->syncp);
+ secy_stats->stats.OutPktsUntagged++;
+ u64_stats_update_end(&secy_stats->syncp);
+ len = skb->len;
+ ret = dev_queue_xmit(skb);
+ count_tx(dev, ret, len);
+ return ret;
+ }
+
+ if (!secy->operational) {
+ kfree_skb(skb);
+ dev->stats.tx_dropped++;
+ return NETDEV_TX_OK;
+ }
+
+ skb = macsec_encrypt(skb, dev);
+ if (IS_ERR(skb)) {
+ if (PTR_ERR(skb) != -EINPROGRESS)
+ dev->stats.tx_dropped++;
+ return NETDEV_TX_OK;
+ }
+
+ macsec_count_tx(skb, &macsec->secy.tx_sc, macsec_skb_cb(skb)->tx_sa);
+
+ macsec_encrypt_finish(skb, dev);
+ len = skb->len;
+ ret = dev_queue_xmit(skb);
+ count_tx(dev, ret, len);
+ return ret;
+}
+
+#define MACSEC_FEATURES \
+ (NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST)
+static int macsec_dev_init(struct net_device *dev)
+{
+ struct macsec_dev *macsec = macsec_priv(dev);
+ struct net_device *real_dev = macsec->real_dev;
+
+ dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
+ if (!dev->tstats)
+ return -ENOMEM;
+
+ dev->features = real_dev->features & MACSEC_FEATURES;
+ dev->features |= NETIF_F_LLTX | NETIF_F_GSO_SOFTWARE;
+
+ dev->needed_headroom = real_dev->needed_headroom +
+ MACSEC_NEEDED_HEADROOM;
+ dev->needed_tailroom = real_dev->needed_tailroom +
+ MACSEC_NEEDED_TAILROOM;
+
+ if (is_zero_ether_addr(dev->dev_addr))
+ eth_hw_addr_inherit(dev, real_dev);
+ if (is_zero_ether_addr(dev->broadcast))
+ memcpy(dev->broadcast, real_dev->broadcast, dev->addr_len);
+
+ return 0;
+}
+
+static void macsec_dev_uninit(struct net_device *dev)
+{
+ free_percpu(dev->tstats);
+}
+
+static netdev_features_t macsec_fix_features(struct net_device *dev,
+ netdev_features_t features)
+{
+ struct macsec_dev *macsec = macsec_priv(dev);
+ struct net_device *real_dev = macsec->real_dev;
+
+ features &= real_dev->features & MACSEC_FEATURES;
+ features |= NETIF_F_LLTX | NETIF_F_GSO_SOFTWARE;
+
+ return features;
+}
+
+static int macsec_dev_open(struct net_device *dev)
+{
+ struct macsec_dev *macsec = macsec_priv(dev);
+ struct net_device *real_dev = macsec->real_dev;
+ int err;
+
+ if (!(real_dev->flags & IFF_UP))
+ return -ENETDOWN;
+
+ err = dev_uc_add(real_dev, dev->dev_addr);
+ if (err < 0)
+ return err;
+
+ if (dev->flags & IFF_ALLMULTI) {
+ err = dev_set_allmulti(real_dev, 1);
+ if (err < 0)
+ goto del_unicast;
+ }
+
+ if (dev->flags & IFF_PROMISC) {
+ err = dev_set_promiscuity(real_dev, 1);
+ if (err < 0)
+ goto clear_allmulti;
+ }
+
+ if (netif_carrier_ok(real_dev))
+ netif_carrier_on(dev);
+
+ return 0;
+clear_allmulti:
+ if (dev->flags & IFF_ALLMULTI)
+ dev_set_allmulti(real_dev, -1);
+del_unicast:
+ dev_uc_del(real_dev, dev->dev_addr);
+ netif_carrier_off(dev);
+ return err;
+}
+
+static int macsec_dev_stop(struct net_device *dev)
+{
+ struct macsec_dev *macsec = macsec_priv(dev);
+ struct net_device *real_dev = macsec->real_dev;
+
+ netif_carrier_off(dev);
+
+ dev_mc_unsync(real_dev, dev);
+ dev_uc_unsync(real_dev, dev);
+
+ if (dev->flags & IFF_ALLMULTI)
+ dev_set_allmulti(real_dev, -1);
+
+ if (dev->flags & IFF_PROMISC)
+ dev_set_promiscuity(real_dev, -1);
+
+ dev_uc_del(real_dev, dev->dev_addr);
+
+ return 0;
+}
+
+static void macsec_dev_change_rx_flags(struct net_device *dev, int change)
+{
+ struct net_device *real_dev = macsec_priv(dev)->real_dev;
+
+ if (!(dev->flags & IFF_UP))
+ return;
+
+ if (change & IFF_ALLMULTI)
+ dev_set_allmulti(real_dev, dev->flags & IFF_ALLMULTI ? 1 : -1);
+
+ if (change & IFF_PROMISC)
+ dev_set_promiscuity(real_dev,
+ dev->flags & IFF_PROMISC ? 1 : -1);
+}
+
+static void macsec_dev_set_rx_mode(struct net_device *dev)
+{
+ struct net_device *real_dev = macsec_priv(dev)->real_dev;
+
+ dev_mc_sync(real_dev, dev);
+ dev_uc_sync(real_dev, dev);
+}
+
+static int macsec_set_mac_address(struct net_device *dev, void *p)
+{
+ struct macsec_dev *macsec = macsec_priv(dev);
+ struct net_device *real_dev = macsec->real_dev;
+ struct sockaddr *addr = p;
+ int err;
+
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EADDRNOTAVAIL;
+
+ if (!(dev->flags & IFF_UP))
+ goto out;
+
+ err = dev_uc_add(real_dev, addr->sa_data);
+ if (err < 0)
+ return err;
+
+ dev_uc_del(real_dev, dev->dev_addr);
+
+out:
+ ether_addr_copy(dev->dev_addr, addr->sa_data);
+ return 0;
+}
+
+static int macsec_change_mtu(struct net_device *dev, int new_mtu)
+{
+ struct macsec_dev *macsec = macsec_priv(dev);
+ unsigned int extra = macsec->secy.icv_len + macsec_extra_len(true);
+
+ if (macsec->real_dev->mtu - extra < new_mtu)
+ return -ERANGE;
+
+ dev->mtu = new_mtu;
+
+ return 0;
+}
+
+static struct rtnl_link_stats64 *macsec_get_stats64(struct net_device *dev,
+ struct rtnl_link_stats64 *s)
+{
+ int cpu;
+
+ if (!dev->tstats)
+ return s;
+
+ for_each_possible_cpu(cpu) {
+ struct pcpu_sw_netstats *stats;
+ struct pcpu_sw_netstats tmp;
+ int start;
+
+ stats = per_cpu_ptr(dev->tstats, cpu);
+ do {
+ start = u64_stats_fetch_begin_irq(&stats->syncp);
+ tmp.rx_packets = stats->rx_packets;
+ tmp.rx_bytes = stats->rx_bytes;
+ tmp.tx_packets = stats->tx_packets;
+ tmp.tx_bytes = stats->tx_bytes;
+ } while (u64_stats_fetch_retry_irq(&stats->syncp, start));
+
+ s->rx_packets += tmp.rx_packets;
+ s->rx_bytes += tmp.rx_bytes;
+ s->tx_packets += tmp.tx_packets;
+ s->tx_bytes += tmp.tx_bytes;
+ }
+
+ s->rx_dropped = dev->stats.rx_dropped;
+ s->tx_dropped = dev->stats.tx_dropped;
+
+ return s;
+}
+
+static int macsec_get_iflink(const struct net_device *dev)
+{
+ return macsec_priv(dev)->real_dev->ifindex;
+}
+
+static const struct net_device_ops macsec_netdev_ops = {
+ .ndo_init = macsec_dev_init,
+ .ndo_uninit = macsec_dev_uninit,
+ .ndo_open = macsec_dev_open,
+ .ndo_stop = macsec_dev_stop,
+ .ndo_fix_features = macsec_fix_features,
+ .ndo_change_mtu = macsec_change_mtu,
+ .ndo_set_rx_mode = macsec_dev_set_rx_mode,
+ .ndo_change_rx_flags = macsec_dev_change_rx_flags,
+ .ndo_set_mac_address = macsec_set_mac_address,
+ .ndo_start_xmit = macsec_start_xmit,
+ .ndo_get_stats64 = macsec_get_stats64,
+ .ndo_get_iflink = macsec_get_iflink,
+};
+
+static const struct device_type macsec_type = {
+ .name = "macsec",
+};
+
+static const struct nla_policy macsec_rtnl_policy[IFLA_MACSEC_MAX + 1] = {
+ [IFLA_MACSEC_SCI] = { .type = NLA_U64 },
+ [IFLA_MACSEC_ICV_LEN] = { .type = NLA_U8 },
+ [IFLA_MACSEC_CIPHER_SUITE] = { .type = NLA_U64 },
+ [IFLA_MACSEC_WINDOW] = { .type = NLA_U32 },
+ [IFLA_MACSEC_ENCODING_SA] = { .type = NLA_U8 },
+ [IFLA_MACSEC_ENCRYPT] = { .type = NLA_U8 },
+ [IFLA_MACSEC_PROTECT] = { .type = NLA_U8 },
+ [IFLA_MACSEC_INC_SCI] = { .type = NLA_U8 },
+ [IFLA_MACSEC_ES] = { .type = NLA_U8 },
+ [IFLA_MACSEC_SCB] = { .type = NLA_U8 },
+ [IFLA_MACSEC_REPLAY_PROTECT] = { .type = NLA_U8 },
+ [IFLA_MACSEC_VALIDATION] = { .type = NLA_U8 },
+};
+
+static void macsec_free_netdev(struct net_device *dev)
+{
+ struct macsec_dev *macsec = macsec_priv(dev);
+ struct net_device *real_dev = macsec->real_dev;
+
+ free_percpu(macsec->stats);
+ free_percpu(macsec->secy.tx_sc.stats);
+
+ dev_put(real_dev);
+ free_netdev(dev);
+}
+
+static void macsec_setup(struct net_device *dev)
+{
+ ether_setup(dev);
+ dev->tx_queue_len = 0;
+ dev->netdev_ops = &macsec_netdev_ops;
+ dev->destructor = macsec_free_netdev;
+
+ eth_zero_addr(dev->broadcast);
+}
+
+static void macsec_changelink_common(struct net_device *dev,
+ struct nlattr *data[])
+{
+ struct macsec_secy *secy;
+ struct macsec_tx_sc *tx_sc;
+
+ secy = &macsec_priv(dev)->secy;
+ tx_sc = &secy->tx_sc;
+
+ if (data[IFLA_MACSEC_ENCODING_SA]) {
+ struct macsec_tx_sa *tx_sa;
+
+ tx_sc->encoding_sa = nla_get_u8(data[IFLA_MACSEC_ENCODING_SA]);
+ tx_sa = rtnl_dereference(tx_sc->sa[tx_sc->encoding_sa]);
+
+ secy->operational = tx_sa && tx_sa->active;
+ }
+
+ if (data[IFLA_MACSEC_WINDOW])
+ secy->replay_window = nla_get_u32(data[IFLA_MACSEC_WINDOW]);
+
+ if (data[IFLA_MACSEC_ENCRYPT])
+ tx_sc->encrypt = !!nla_get_u8(data[IFLA_MACSEC_ENCRYPT]);
+
+ if (data[IFLA_MACSEC_PROTECT])
+ secy->protect_frames = !!nla_get_u8(data[IFLA_MACSEC_PROTECT]);
+
+ if (data[IFLA_MACSEC_INC_SCI])
+ tx_sc->send_sci = !!nla_get_u8(data[IFLA_MACSEC_INC_SCI]);
+
+ if (data[IFLA_MACSEC_ES])
+ tx_sc->end_station = !!nla_get_u8(data[IFLA_MACSEC_ES]);
+
+ if (data[IFLA_MACSEC_SCB])
+ tx_sc->scb = !!nla_get_u8(data[IFLA_MACSEC_SCB]);
+
+ if (data[IFLA_MACSEC_REPLAY_PROTECT])
+ secy->replay_protect = !!nla_get_u8(data[IFLA_MACSEC_REPLAY_PROTECT]);
+
+ if (data[IFLA_MACSEC_VALIDATION])
+ secy->validate_frames = nla_get_u8(data[IFLA_MACSEC_VALIDATION]);
+}
+
+static int macsec_changelink(struct net_device *dev, struct nlattr *tb[],
+ struct nlattr *data[])
+{
+ if (!data)
+ return 0;
+
+ if (data[IFLA_MACSEC_CIPHER_SUITE] ||
+ data[IFLA_MACSEC_ICV_LEN] ||
+ data[IFLA_MACSEC_SCI] ||
+ data[IFLA_MACSEC_PORT])
+ return -EINVAL;
+
+ macsec_changelink_common(dev, data);
+
+ return 0;
+}
+
+static void macsec_del_dev(struct macsec_dev *macsec)
+{
+ int i;
+
+ while (macsec->secy.rx_sc) {
+ struct macsec_rx_sc *rx_sc = rtnl_dereference(macsec->secy.rx_sc);
+
+ rcu_assign_pointer(macsec->secy.rx_sc, rx_sc->next);
+ free_rx_sc(rx_sc);
+ }
+
+ for (i = 0; i < MACSEC_NUM_AN; i++) {
+ struct macsec_tx_sa *sa = rtnl_dereference(macsec->secy.tx_sc.sa[i]);
+
+ if (sa) {
+ RCU_INIT_POINTER(macsec->secy.tx_sc.sa[i], NULL);
+ clear_tx_sa(sa);
+ }
+ }
+}
+
+static void macsec_dellink(struct net_device *dev, struct list_head *head)
+{
+ struct macsec_dev *macsec = macsec_priv(dev);
+ struct net_device *real_dev = macsec->real_dev;
+ struct macsec_rxh_data *rxd = macsec_data_rtnl(real_dev);
+
+ unregister_netdevice_queue(dev, head);
+ list_del_rcu(&macsec->secys);
+ if (list_empty(&rxd->secys))
+ netdev_rx_handler_unregister(real_dev);
+
+ macsec_del_dev(macsec);
+}
+
+static int register_macsec_dev(struct net_device *real_dev,
+ struct net_device *dev)
+{
+ struct macsec_dev *macsec = macsec_priv(dev);
+ struct macsec_rxh_data *rxd = macsec_data_rtnl(real_dev);
+
+ if (!rxd) {
+ int err;
+
+ rxd = kmalloc(sizeof(*rxd), GFP_KERNEL);
+ if (!rxd)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&rxd->secys);
+
+ err = netdev_rx_handler_register(real_dev, macsec_handle_frame,
+ rxd);
+ if (err < 0)
+ return err;
+ }
+
+ list_add_tail_rcu(&macsec->secys, &rxd->secys);
+ return 0;
+}
+
+static bool sci_exists(struct net_device *dev, sci_t sci)
+{
+ struct macsec_rxh_data *rxd = macsec_data_rtnl(dev);
+ struct macsec_dev *macsec;
+
+ list_for_each_entry(macsec, &rxd->secys, secys) {
+ if (macsec->secy.sci == sci)
+ return true;
+ }
+
+ return false;
+}
+
+static sci_t dev_to_sci(struct net_device *dev, __be16 port)
+{
+ return make_sci(dev->dev_addr, port);
+}
+
+static int macsec_add_dev(struct net_device *dev, sci_t sci, u8 icv_len)
+{
+ struct macsec_dev *macsec = macsec_priv(dev);
+ struct macsec_secy *secy = &macsec->secy;
+
+ macsec->stats = netdev_alloc_pcpu_stats(struct pcpu_secy_stats);
+ if (!macsec->stats)
+ return -ENOMEM;
+
+ secy->tx_sc.stats = netdev_alloc_pcpu_stats(struct pcpu_tx_sc_stats);
+ if (!secy->tx_sc.stats) {
+ free_percpu(macsec->stats);
+ return -ENOMEM;
+ }
+
+ if (sci == MACSEC_UNDEF_SCI)
+ sci = dev_to_sci(dev, MACSEC_PORT_ES);
+
+ secy->netdev = dev;
+ secy->operational = true;
+ secy->key_len = DEFAULT_SAK_LEN;
+ secy->icv_len = icv_len;
+ secy->validate_frames = MACSEC_VALIDATE_DEFAULT;
+ secy->protect_frames = true;
+ secy->replay_protect = false;
+
+ secy->sci = sci;
+ secy->tx_sc.active = true;
+ secy->tx_sc.encoding_sa = DEFAULT_ENCODING_SA;
+ secy->tx_sc.encrypt = DEFAULT_ENCRYPT;
+ secy->tx_sc.send_sci = DEFAULT_SEND_SCI;
+ secy->tx_sc.end_station = false;
+ secy->tx_sc.scb = false;
+
+ return 0;
+}
+
+static int macsec_newlink(struct net *net, struct net_device *dev,
+ struct nlattr *tb[], struct nlattr *data[])
+{
+ struct macsec_dev *macsec = macsec_priv(dev);
+ struct net_device *real_dev;
+ int err;
+ sci_t sci;
+ u8 icv_len = DEFAULT_ICV_LEN;
+ rx_handler_func_t *rx_handler;
+
+ if (!tb[IFLA_LINK])
+ return -EINVAL;
+ real_dev = __dev_get_by_index(net, nla_get_u32(tb[IFLA_LINK]));
+ if (!real_dev)
+ return -ENODEV;
+
+ dev->priv_flags |= IFF_MACSEC;
+
+ macsec->real_dev = real_dev;
+
+ if (data && data[IFLA_MACSEC_ICV_LEN])
+ icv_len = nla_get_u8(data[IFLA_MACSEC_ICV_LEN]);
+ dev->mtu = real_dev->mtu - icv_len - macsec_extra_len(true);
+
+ rx_handler = rtnl_dereference(real_dev->rx_handler);
+ if (rx_handler && rx_handler != macsec_handle_frame)
+ return -EBUSY;
+
+ err = register_netdevice(dev);
+ if (err < 0)
+ return err;
+
+ /* need to be already registered so that ->init has run and
+ * the MAC addr is set
+ */
+ if (data && data[IFLA_MACSEC_SCI])
+ sci = nla_get_sci(data[IFLA_MACSEC_SCI]);
+ else if (data && data[IFLA_MACSEC_PORT])
+ sci = dev_to_sci(dev, nla_get_be16(data[IFLA_MACSEC_PORT]));
+ else
+ sci = dev_to_sci(dev, MACSEC_PORT_ES);
+
+ if (rx_handler && sci_exists(real_dev, sci)) {
+ err = -EBUSY;
+ goto unregister;
+ }
+
+ err = macsec_add_dev(dev, sci, icv_len);
+ if (err)
+ goto unregister;
+
+ if (data)
+ macsec_changelink_common(dev, data);
+
+ err = register_macsec_dev(real_dev, dev);
+ if (err < 0)
+ goto del_dev;
+
+ dev_hold(real_dev);
+
+ return 0;
+
+del_dev:
+ macsec_del_dev(macsec);
+unregister:
+ unregister_netdevice(dev);
+ return err;
+}
+
+static int macsec_validate_attr(struct nlattr *tb[], struct nlattr *data[])
+{
+ u64 csid = DEFAULT_CIPHER_ID;
+ u8 icv_len = DEFAULT_ICV_LEN;
+ int flag;
+ bool es, scb, sci;
+
+ if (!data)
+ return 0;
+
+ if (data[IFLA_MACSEC_CIPHER_SUITE])
+ csid = nla_get_u64(data[IFLA_MACSEC_CIPHER_SUITE]);
+
+ if (data[IFLA_MACSEC_ICV_LEN])
+ icv_len = nla_get_u8(data[IFLA_MACSEC_ICV_LEN]);
+
+ switch (csid) {
+ case DEFAULT_CIPHER_ID:
+ case DEFAULT_CIPHER_ALT:
+ if (icv_len < MACSEC_MIN_ICV_LEN ||
+ icv_len > MACSEC_MAX_ICV_LEN)
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (data[IFLA_MACSEC_ENCODING_SA]) {
+ if (nla_get_u8(data[IFLA_MACSEC_ENCODING_SA]) >= MACSEC_NUM_AN)
+ return -EINVAL;
+ }
+
+ for (flag = IFLA_MACSEC_ENCODING_SA + 1;
+ flag < IFLA_MACSEC_VALIDATION;
+ flag++) {
+ if (data[flag]) {
+ if (nla_get_u8(data[flag]) > 1)
+ return -EINVAL;
+ }
+ }
+
+ es = data[IFLA_MACSEC_ES] ? nla_get_u8(data[IFLA_MACSEC_ES]) : false;
+ sci = data[IFLA_MACSEC_INC_SCI] ? nla_get_u8(data[IFLA_MACSEC_INC_SCI]) : false;
+ scb = data[IFLA_MACSEC_SCB] ? nla_get_u8(data[IFLA_MACSEC_SCB]) : false;
+
+ if ((sci && (scb || es)) || (scb && es))
+ return -EINVAL;
+
+ if (data[IFLA_MACSEC_VALIDATION] &&
+ nla_get_u8(data[IFLA_MACSEC_VALIDATION]) > MACSEC_VALIDATE_MAX)
+ return -EINVAL;
+
+ if ((data[IFLA_MACSEC_PROTECT] &&
+ nla_get_u8(data[IFLA_MACSEC_PROTECT])) &&
+ !data[IFLA_MACSEC_WINDOW])
+ return -EINVAL;
+
+ return 0;
+}
+
+static struct net *macsec_get_link_net(const struct net_device *dev)
+{
+ return dev_net(macsec_priv(dev)->real_dev);
+}
+
+static size_t macsec_get_size(const struct net_device *dev)
+{
+ return 0 +
+ nla_total_size(8) + /* SCI */
+ nla_total_size(1) + /* ICV_LEN */
+ nla_total_size(8) + /* CIPHER_SUITE */
+ nla_total_size(4) + /* WINDOW */
+ nla_total_size(1) + /* ENCODING_SA */
+ nla_total_size(1) + /* ENCRYPT */
+ nla_total_size(1) + /* PROTECT */
+ nla_total_size(1) + /* INC_SCI */
+ nla_total_size(1) + /* ES */
+ nla_total_size(1) + /* SCB */
+ nla_total_size(1) + /* REPLAY_PROTECT */
+ nla_total_size(1) + /* VALIDATION */
+ 0;
+}
+
+static int macsec_fill_info(struct sk_buff *skb,
+ const struct net_device *dev)
+{
+ struct macsec_secy *secy = &macsec_priv(dev)->secy;
+ struct macsec_tx_sc *tx_sc = &secy->tx_sc;
+
+ if (nla_put_sci(skb, IFLA_MACSEC_SCI, secy->sci) ||
+ nla_put_u8(skb, IFLA_MACSEC_ICV_LEN, secy->icv_len) ||
+ nla_put_u64(skb, IFLA_MACSEC_CIPHER_SUITE, DEFAULT_CIPHER_ID) ||
+ nla_put_u8(skb, IFLA_MACSEC_ENCODING_SA, tx_sc->encoding_sa) ||
+ nla_put_u8(skb, IFLA_MACSEC_ENCRYPT, tx_sc->encrypt) ||
+ nla_put_u8(skb, IFLA_MACSEC_PROTECT, secy->protect_frames) ||
+ nla_put_u8(skb, IFLA_MACSEC_INC_SCI, tx_sc->send_sci) ||
+ nla_put_u8(skb, IFLA_MACSEC_ES, tx_sc->end_station) ||
+ nla_put_u8(skb, IFLA_MACSEC_SCB, tx_sc->scb) ||
+ nla_put_u8(skb, IFLA_MACSEC_REPLAY_PROTECT, secy->replay_protect) ||
+ nla_put_u8(skb, IFLA_MACSEC_VALIDATION, secy->validate_frames) ||
+ 0)
+ goto nla_put_failure;
+
+ if (secy->replay_protect) {
+ if (nla_put_u32(skb, IFLA_MACSEC_WINDOW, secy->replay_window))
+ goto nla_put_failure;
+ }
+
+ return 0;
+
+nla_put_failure:
+ return -EMSGSIZE;
+}
+
+static struct rtnl_link_ops macsec_link_ops __read_mostly = {
+ .kind = "macsec",
+ .priv_size = sizeof(struct macsec_dev),
+ .maxtype = IFLA_MACSEC_MAX,
+ .policy = macsec_rtnl_policy,
+ .setup = macsec_setup,
+ .validate = macsec_validate_attr,
+ .newlink = macsec_newlink,
+ .changelink = macsec_changelink,
+ .dellink = macsec_dellink,
+ .get_size = macsec_get_size,
+ .fill_info = macsec_fill_info,
+ .get_link_net = macsec_get_link_net,
+};
+
+static bool is_macsec_master(struct net_device *dev)
+{
+ return rcu_access_pointer(dev->rx_handler) == macsec_handle_frame;
+}
+
+static int macsec_notify(struct notifier_block *this, unsigned long event,
+ void *ptr)
+{
+ struct net_device *real_dev = netdev_notifier_info_to_dev(ptr);
+ LIST_HEAD(head);
+
+ if (!is_macsec_master(real_dev))
+ return NOTIFY_DONE;
+
+ switch (event) {
+ case NETDEV_UNREGISTER: {
+ struct macsec_dev *m, *n;
+ struct macsec_rxh_data *rxd;
+
+ rxd = macsec_data_rtnl(real_dev);
+ list_for_each_entry_safe(m, n, &rxd->secys, secys) {
+ macsec_dellink(m->secy.netdev, &head);
+ }
+ unregister_netdevice_many(&head);
+ break;
+ }
+ case NETDEV_CHANGEMTU: {
+ struct macsec_dev *m;
+ struct macsec_rxh_data *rxd;
+
+ rxd = macsec_data_rtnl(real_dev);
+ list_for_each_entry(m, &rxd->secys, secys) {
+ struct net_device *dev = m->secy.netdev;
+ unsigned int mtu = real_dev->mtu - (m->secy.icv_len +
+ macsec_extra_len(true));
+
+ if (dev->mtu > mtu)
+ dev_set_mtu(dev, mtu);
+ }
+ }
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block macsec_notifier = {
+ .notifier_call = macsec_notify,
+};
+
+static int __init macsec_init(void)
+{
+ int err;
+
+ pr_info("MACsec IEEE 802.1AE\n");
+ err = register_netdevice_notifier(&macsec_notifier);
+ if (err)
+ return err;
+
+ err = rtnl_link_register(&macsec_link_ops);
+ if (err)
+ goto notifier;
+
+ err = genl_register_family_with_ops(&macsec_fam, macsec_genl_ops);
+ if (err)
+ goto rtnl;
+
+ return 0;
+
+rtnl:
+ rtnl_link_unregister(&macsec_link_ops);
+notifier:
+ unregister_netdevice_notifier(&macsec_notifier);
+ return err;
+}
+
+static void __exit macsec_exit(void)
+{
+ genl_unregister_family(&macsec_fam);
+ rtnl_link_unregister(&macsec_link_ops);
+ unregister_netdevice_notifier(&macsec_notifier);
+}
+
+module_init(macsec_init);
+module_exit(macsec_exit);
+
+MODULE_ALIAS_RTNL_LINK("macsec");
+
+MODULE_DESCRIPTION("MACsec IEEE 802.1AE");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c
index d636d05..95394ed 100644
--- a/drivers/net/macvtap.c
+++ b/drivers/net/macvtap.c
@@ -760,6 +760,8 @@
macvtap16_to_cpu(q, vnet_hdr.hdr_len) : GOODCOPY_LEN;
if (copylen > good_linear)
copylen = good_linear;
+ else if (copylen < ETH_HLEN)
+ copylen = ETH_HLEN;
linear = copylen;
i = *from;
iov_iter_advance(&i, copylen);
@@ -769,10 +771,11 @@
if (!zerocopy) {
copylen = len;
- if (macvtap16_to_cpu(q, vnet_hdr.hdr_len) > good_linear)
+ linear = macvtap16_to_cpu(q, vnet_hdr.hdr_len);
+ if (linear > good_linear)
linear = good_linear;
- else
- linear = macvtap16_to_cpu(q, vnet_hdr.hdr_len);
+ else if (linear < ETH_HLEN)
+ linear = ETH_HLEN;
}
skb = macvtap_alloc_skb(&q->sk, MACVTAP_RESERVE, copylen,
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index f0a77020..075a4cc 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -183,15 +183,29 @@
To compile this driver as a module, choose M here: the module
will be called mdio-gpio.
+config MDIO_CAVIUM
+ tristate
+
config MDIO_OCTEON
- tristate "Support for MDIO buses on Octeon and ThunderX SOCs"
+ tristate "Support for MDIO buses on Octeon and some ThunderX SOCs"
depends on 64BIT
depends on HAS_IOMEM
+ select MDIO_CAVIUM
help
-
This module provides a driver for the Octeon and ThunderX MDIO
- busses. It is required by the Octeon and ThunderX ethernet device
- drivers.
+ buses. It is required by the Octeon and ThunderX ethernet device
+ drivers on some systems.
+
+config MDIO_THUNDER
+ tristate "Support for MDIO buses on on ThunderX SOCs"
+ depends on 64BIT
+ depends on PCI
+ select MDIO_CAVIUM
+ help
+ This driver supports the MDIO interfaces found on Cavium
+ ThunderX SoCs when the MDIO bus device appears on as a PCI
+ device.
+
config MDIO_SUN4I
tristate "Allwinner sun4i MDIO interface support"
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 680e88f9..fcdbb92 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -31,6 +31,8 @@
obj-$(CONFIG_STE10XP) += ste10Xp.o
obj-$(CONFIG_MICREL_PHY) += micrel.o
obj-$(CONFIG_MDIO_OCTEON) += mdio-octeon.o
+obj-$(CONFIG_MDIO_THUNDER) += mdio-thunder.o
+obj-$(CONFIG_MDIO_CAVIUM) += mdio-cavium.o
obj-$(CONFIG_MICREL_KS8995MA) += spi_ks8995.o
obj-$(CONFIG_AT803X_PHY) += at803x.o
obj-$(CONFIG_AMD_PHY) += amd.o
diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c
index 2174ec9..1e901c7 100644
--- a/drivers/net/phy/at803x.c
+++ b/drivers/net/phy/at803x.c
@@ -52,6 +52,9 @@
#define AT803X_DEBUG_REG_5 0x05
#define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8)
+#define AT803X_REG_CHIP_CONFIG 0x1f
+#define AT803X_BT_BX_REG_SEL 0x8000
+
#define ATH8030_PHY_ID 0x004dd076
#define ATH8031_PHY_ID 0x004dd074
#define ATH8035_PHY_ID 0x004dd072
@@ -206,6 +209,7 @@
{
int value;
int wol_enabled;
+ int ccr;
mutex_lock(&phydev->lock);
@@ -221,6 +225,16 @@
phy_write(phydev, MII_BMCR, value);
+ if (phydev->interface != PHY_INTERFACE_MODE_SGMII)
+ goto done;
+
+ /* also power-down SGMII interface */
+ ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG);
+ phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr & ~AT803X_BT_BX_REG_SEL);
+ phy_write(phydev, MII_BMCR, phy_read(phydev, MII_BMCR) | BMCR_PDOWN);
+ phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr | AT803X_BT_BX_REG_SEL);
+
+done:
mutex_unlock(&phydev->lock);
return 0;
@@ -229,6 +243,7 @@
static int at803x_resume(struct phy_device *phydev)
{
int value;
+ int ccr;
mutex_lock(&phydev->lock);
@@ -236,6 +251,17 @@
value &= ~(BMCR_PDOWN | BMCR_ISOLATE);
phy_write(phydev, MII_BMCR, value);
+ if (phydev->interface != PHY_INTERFACE_MODE_SGMII)
+ goto done;
+
+ /* also power-up SGMII interface */
+ ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG);
+ phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr & ~AT803X_BT_BX_REG_SEL);
+ value = phy_read(phydev, MII_BMCR) & ~(BMCR_PDOWN | BMCR_ISOLATE);
+ phy_write(phydev, MII_BMCR, value);
+ phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr | AT803X_BT_BX_REG_SEL);
+
+done:
mutex_unlock(&phydev->lock);
return 0;
diff --git a/drivers/net/phy/fixed_phy.c b/drivers/net/phy/fixed_phy.c
index ab9c473..fc07a88 100644
--- a/drivers/net/phy/fixed_phy.c
+++ b/drivers/net/phy/fixed_phy.c
@@ -285,7 +285,7 @@
}
EXPORT_SYMBOL_GPL(fixed_phy_add);
-void fixed_phy_del(int phy_addr)
+static void fixed_phy_del(int phy_addr)
{
struct fixed_mdio_bus *fmb = &platform_fmb;
struct fixed_phy *fp, *tmp;
@@ -300,7 +300,6 @@
}
}
}
-EXPORT_SYMBOL_GPL(fixed_phy_del);
static int phy_fixed_addr;
static DEFINE_SPINLOCK(phy_fixed_addr_lock);
@@ -371,6 +370,14 @@
}
EXPORT_SYMBOL_GPL(fixed_phy_register);
+void fixed_phy_unregister(struct phy_device *phy)
+{
+ phy_device_remove(phy);
+
+ fixed_phy_del(phy->mdio.addr);
+}
+EXPORT_SYMBOL_GPL(fixed_phy_unregister);
+
static int __init fixed_mdio_bus_init(void)
{
struct fixed_mdio_bus *fmb = &platform_fmb;
diff --git a/drivers/net/phy/mdio-cavium.c b/drivers/net/phy/mdio-cavium.c
new file mode 100644
index 0000000..e796ee1
--- /dev/null
+++ b/drivers/net/phy/mdio-cavium.c
@@ -0,0 +1,149 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2009-2016 Cavium, Inc.
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/phy.h>
+#include <linux/io.h>
+
+#include "mdio-cavium.h"
+
+static void cavium_mdiobus_set_mode(struct cavium_mdiobus *p,
+ enum cavium_mdiobus_mode m)
+{
+ union cvmx_smix_clk smi_clk;
+
+ if (m == p->mode)
+ return;
+
+ smi_clk.u64 = oct_mdio_readq(p->register_base + SMI_CLK);
+ smi_clk.s.mode = (m == C45) ? 1 : 0;
+ smi_clk.s.preamble = 1;
+ oct_mdio_writeq(smi_clk.u64, p->register_base + SMI_CLK);
+ p->mode = m;
+}
+
+static int cavium_mdiobus_c45_addr(struct cavium_mdiobus *p,
+ int phy_id, int regnum)
+{
+ union cvmx_smix_cmd smi_cmd;
+ union cvmx_smix_wr_dat smi_wr;
+ int timeout = 1000;
+
+ cavium_mdiobus_set_mode(p, C45);
+
+ smi_wr.u64 = 0;
+ smi_wr.s.dat = regnum & 0xffff;
+ oct_mdio_writeq(smi_wr.u64, p->register_base + SMI_WR_DAT);
+
+ regnum = (regnum >> 16) & 0x1f;
+
+ smi_cmd.u64 = 0;
+ smi_cmd.s.phy_op = 0; /* MDIO_CLAUSE_45_ADDRESS */
+ smi_cmd.s.phy_adr = phy_id;
+ smi_cmd.s.reg_adr = regnum;
+ oct_mdio_writeq(smi_cmd.u64, p->register_base + SMI_CMD);
+
+ do {
+ /* Wait 1000 clocks so we don't saturate the RSL bus
+ * doing reads.
+ */
+ __delay(1000);
+ smi_wr.u64 = oct_mdio_readq(p->register_base + SMI_WR_DAT);
+ } while (smi_wr.s.pending && --timeout);
+
+ if (timeout <= 0)
+ return -EIO;
+ return 0;
+}
+
+int cavium_mdiobus_read(struct mii_bus *bus, int phy_id, int regnum)
+{
+ struct cavium_mdiobus *p = bus->priv;
+ union cvmx_smix_cmd smi_cmd;
+ union cvmx_smix_rd_dat smi_rd;
+ unsigned int op = 1; /* MDIO_CLAUSE_22_READ */
+ int timeout = 1000;
+
+ if (regnum & MII_ADDR_C45) {
+ int r = cavium_mdiobus_c45_addr(p, phy_id, regnum);
+
+ if (r < 0)
+ return r;
+
+ regnum = (regnum >> 16) & 0x1f;
+ op = 3; /* MDIO_CLAUSE_45_READ */
+ } else {
+ cavium_mdiobus_set_mode(p, C22);
+ }
+
+ smi_cmd.u64 = 0;
+ smi_cmd.s.phy_op = op;
+ smi_cmd.s.phy_adr = phy_id;
+ smi_cmd.s.reg_adr = regnum;
+ oct_mdio_writeq(smi_cmd.u64, p->register_base + SMI_CMD);
+
+ do {
+ /* Wait 1000 clocks so we don't saturate the RSL bus
+ * doing reads.
+ */
+ __delay(1000);
+ smi_rd.u64 = oct_mdio_readq(p->register_base + SMI_RD_DAT);
+ } while (smi_rd.s.pending && --timeout);
+
+ if (smi_rd.s.val)
+ return smi_rd.s.dat;
+ else
+ return -EIO;
+}
+EXPORT_SYMBOL(cavium_mdiobus_read);
+
+int cavium_mdiobus_write(struct mii_bus *bus, int phy_id, int regnum, u16 val)
+{
+ struct cavium_mdiobus *p = bus->priv;
+ union cvmx_smix_cmd smi_cmd;
+ union cvmx_smix_wr_dat smi_wr;
+ unsigned int op = 0; /* MDIO_CLAUSE_22_WRITE */
+ int timeout = 1000;
+
+ if (regnum & MII_ADDR_C45) {
+ int r = cavium_mdiobus_c45_addr(p, phy_id, regnum);
+
+ if (r < 0)
+ return r;
+
+ regnum = (regnum >> 16) & 0x1f;
+ op = 1; /* MDIO_CLAUSE_45_WRITE */
+ } else {
+ cavium_mdiobus_set_mode(p, C22);
+ }
+
+ smi_wr.u64 = 0;
+ smi_wr.s.dat = val;
+ oct_mdio_writeq(smi_wr.u64, p->register_base + SMI_WR_DAT);
+
+ smi_cmd.u64 = 0;
+ smi_cmd.s.phy_op = op;
+ smi_cmd.s.phy_adr = phy_id;
+ smi_cmd.s.reg_adr = regnum;
+ oct_mdio_writeq(smi_cmd.u64, p->register_base + SMI_CMD);
+
+ do {
+ /* Wait 1000 clocks so we don't saturate the RSL bus
+ * doing reads.
+ */
+ __delay(1000);
+ smi_wr.u64 = oct_mdio_readq(p->register_base + SMI_WR_DAT);
+ } while (smi_wr.s.pending && --timeout);
+
+ if (timeout <= 0)
+ return -EIO;
+
+ return 0;
+}
+EXPORT_SYMBOL(cavium_mdiobus_write);
diff --git a/drivers/net/phy/mdio-cavium.h b/drivers/net/phy/mdio-cavium.h
new file mode 100644
index 0000000..4bccd45
--- /dev/null
+++ b/drivers/net/phy/mdio-cavium.h
@@ -0,0 +1,119 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2009-2016 Cavium, Inc.
+ */
+
+enum cavium_mdiobus_mode {
+ UNINIT = 0,
+ C22,
+ C45
+};
+
+#define SMI_CMD 0x0
+#define SMI_WR_DAT 0x8
+#define SMI_RD_DAT 0x10
+#define SMI_CLK 0x18
+#define SMI_EN 0x20
+
+#ifdef __BIG_ENDIAN_BITFIELD
+#define OCT_MDIO_BITFIELD_FIELD(field, more) \
+ field; \
+ more
+
+#else
+#define OCT_MDIO_BITFIELD_FIELD(field, more) \
+ more \
+ field;
+
+#endif
+
+union cvmx_smix_clk {
+ u64 u64;
+ struct cvmx_smix_clk_s {
+ OCT_MDIO_BITFIELD_FIELD(u64 reserved_25_63:39,
+ OCT_MDIO_BITFIELD_FIELD(u64 mode:1,
+ OCT_MDIO_BITFIELD_FIELD(u64 reserved_21_23:3,
+ OCT_MDIO_BITFIELD_FIELD(u64 sample_hi:5,
+ OCT_MDIO_BITFIELD_FIELD(u64 sample_mode:1,
+ OCT_MDIO_BITFIELD_FIELD(u64 reserved_14_14:1,
+ OCT_MDIO_BITFIELD_FIELD(u64 clk_idle:1,
+ OCT_MDIO_BITFIELD_FIELD(u64 preamble:1,
+ OCT_MDIO_BITFIELD_FIELD(u64 sample:4,
+ OCT_MDIO_BITFIELD_FIELD(u64 phase:8,
+ ;))))))))))
+ } s;
+};
+
+union cvmx_smix_cmd {
+ u64 u64;
+ struct cvmx_smix_cmd_s {
+ OCT_MDIO_BITFIELD_FIELD(u64 reserved_18_63:46,
+ OCT_MDIO_BITFIELD_FIELD(u64 phy_op:2,
+ OCT_MDIO_BITFIELD_FIELD(u64 reserved_13_15:3,
+ OCT_MDIO_BITFIELD_FIELD(u64 phy_adr:5,
+ OCT_MDIO_BITFIELD_FIELD(u64 reserved_5_7:3,
+ OCT_MDIO_BITFIELD_FIELD(u64 reg_adr:5,
+ ;))))))
+ } s;
+};
+
+union cvmx_smix_en {
+ u64 u64;
+ struct cvmx_smix_en_s {
+ OCT_MDIO_BITFIELD_FIELD(u64 reserved_1_63:63,
+ OCT_MDIO_BITFIELD_FIELD(u64 en:1,
+ ;))
+ } s;
+};
+
+union cvmx_smix_rd_dat {
+ u64 u64;
+ struct cvmx_smix_rd_dat_s {
+ OCT_MDIO_BITFIELD_FIELD(u64 reserved_18_63:46,
+ OCT_MDIO_BITFIELD_FIELD(u64 pending:1,
+ OCT_MDIO_BITFIELD_FIELD(u64 val:1,
+ OCT_MDIO_BITFIELD_FIELD(u64 dat:16,
+ ;))))
+ } s;
+};
+
+union cvmx_smix_wr_dat {
+ u64 u64;
+ struct cvmx_smix_wr_dat_s {
+ OCT_MDIO_BITFIELD_FIELD(u64 reserved_18_63:46,
+ OCT_MDIO_BITFIELD_FIELD(u64 pending:1,
+ OCT_MDIO_BITFIELD_FIELD(u64 val:1,
+ OCT_MDIO_BITFIELD_FIELD(u64 dat:16,
+ ;))))
+ } s;
+};
+
+struct cavium_mdiobus {
+ struct mii_bus *mii_bus;
+ u64 register_base;
+ enum cavium_mdiobus_mode mode;
+};
+
+#ifdef CONFIG_CAVIUM_OCTEON_SOC
+
+#include <asm/octeon/octeon.h>
+
+static inline void oct_mdio_writeq(u64 val, u64 addr)
+{
+ cvmx_write_csr(addr, val);
+}
+
+static inline u64 oct_mdio_readq(u64 addr)
+{
+ return cvmx_read_csr(addr);
+}
+#else
+#define oct_mdio_writeq(val, addr) writeq(val, (void *)addr)
+#define oct_mdio_readq(addr) readq((void *)addr)
+#endif
+
+int cavium_mdiobus_read(struct mii_bus *bus, int phy_id, int regnum);
+int cavium_mdiobus_write(struct mii_bus *bus, int phy_id, int regnum, u16 val);
diff --git a/drivers/net/phy/mdio-octeon.c b/drivers/net/phy/mdio-octeon.c
index 47d4f2f..ab6914f 100644
--- a/drivers/net/phy/mdio-octeon.c
+++ b/drivers/net/phy/mdio-octeon.c
@@ -3,272 +3,26 @@
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
- * Copyright (C) 2009-2012 Cavium, Inc.
+ * Copyright (C) 2009-2015 Cavium, Inc.
*/
#include <linux/platform_device.h>
#include <linux/of_address.h>
#include <linux/of_mdio.h>
-#include <linux/delay.h>
#include <linux/module.h>
#include <linux/gfp.h>
#include <linux/phy.h>
#include <linux/io.h>
-#ifdef CONFIG_CAVIUM_OCTEON_SOC
-#include <asm/octeon/octeon.h>
-#endif
-
-#define DRV_VERSION "1.1"
-#define DRV_DESCRIPTION "Cavium Networks Octeon/ThunderX SMI/MDIO driver"
-
-#define SMI_CMD 0x0
-#define SMI_WR_DAT 0x8
-#define SMI_RD_DAT 0x10
-#define SMI_CLK 0x18
-#define SMI_EN 0x20
-
-#ifdef __BIG_ENDIAN_BITFIELD
-#define OCT_MDIO_BITFIELD_FIELD(field, more) \
- field; \
- more
-
-#else
-#define OCT_MDIO_BITFIELD_FIELD(field, more) \
- more \
- field;
-
-#endif
-
-union cvmx_smix_clk {
- u64 u64;
- struct cvmx_smix_clk_s {
- OCT_MDIO_BITFIELD_FIELD(u64 reserved_25_63:39,
- OCT_MDIO_BITFIELD_FIELD(u64 mode:1,
- OCT_MDIO_BITFIELD_FIELD(u64 reserved_21_23:3,
- OCT_MDIO_BITFIELD_FIELD(u64 sample_hi:5,
- OCT_MDIO_BITFIELD_FIELD(u64 sample_mode:1,
- OCT_MDIO_BITFIELD_FIELD(u64 reserved_14_14:1,
- OCT_MDIO_BITFIELD_FIELD(u64 clk_idle:1,
- OCT_MDIO_BITFIELD_FIELD(u64 preamble:1,
- OCT_MDIO_BITFIELD_FIELD(u64 sample:4,
- OCT_MDIO_BITFIELD_FIELD(u64 phase:8,
- ;))))))))))
- } s;
-};
-
-union cvmx_smix_cmd {
- u64 u64;
- struct cvmx_smix_cmd_s {
- OCT_MDIO_BITFIELD_FIELD(u64 reserved_18_63:46,
- OCT_MDIO_BITFIELD_FIELD(u64 phy_op:2,
- OCT_MDIO_BITFIELD_FIELD(u64 reserved_13_15:3,
- OCT_MDIO_BITFIELD_FIELD(u64 phy_adr:5,
- OCT_MDIO_BITFIELD_FIELD(u64 reserved_5_7:3,
- OCT_MDIO_BITFIELD_FIELD(u64 reg_adr:5,
- ;))))))
- } s;
-};
-
-union cvmx_smix_en {
- u64 u64;
- struct cvmx_smix_en_s {
- OCT_MDIO_BITFIELD_FIELD(u64 reserved_1_63:63,
- OCT_MDIO_BITFIELD_FIELD(u64 en:1,
- ;))
- } s;
-};
-
-union cvmx_smix_rd_dat {
- u64 u64;
- struct cvmx_smix_rd_dat_s {
- OCT_MDIO_BITFIELD_FIELD(u64 reserved_18_63:46,
- OCT_MDIO_BITFIELD_FIELD(u64 pending:1,
- OCT_MDIO_BITFIELD_FIELD(u64 val:1,
- OCT_MDIO_BITFIELD_FIELD(u64 dat:16,
- ;))))
- } s;
-};
-
-union cvmx_smix_wr_dat {
- u64 u64;
- struct cvmx_smix_wr_dat_s {
- OCT_MDIO_BITFIELD_FIELD(u64 reserved_18_63:46,
- OCT_MDIO_BITFIELD_FIELD(u64 pending:1,
- OCT_MDIO_BITFIELD_FIELD(u64 val:1,
- OCT_MDIO_BITFIELD_FIELD(u64 dat:16,
- ;))))
- } s;
-};
-
-enum octeon_mdiobus_mode {
- UNINIT = 0,
- C22,
- C45
-};
-
-struct octeon_mdiobus {
- struct mii_bus *mii_bus;
- u64 register_base;
- resource_size_t mdio_phys;
- resource_size_t regsize;
- enum octeon_mdiobus_mode mode;
-};
-
-#ifdef CONFIG_CAVIUM_OCTEON_SOC
-static void oct_mdio_writeq(u64 val, u64 addr)
-{
- cvmx_write_csr(addr, val);
-}
-
-static u64 oct_mdio_readq(u64 addr)
-{
- return cvmx_read_csr(addr);
-}
-#else
-#define oct_mdio_writeq(val, addr) writeq_relaxed(val, (void *)addr)
-#define oct_mdio_readq(addr) readq_relaxed((void *)addr)
-#endif
-
-static void octeon_mdiobus_set_mode(struct octeon_mdiobus *p,
- enum octeon_mdiobus_mode m)
-{
- union cvmx_smix_clk smi_clk;
-
- if (m == p->mode)
- return;
-
- smi_clk.u64 = oct_mdio_readq(p->register_base + SMI_CLK);
- smi_clk.s.mode = (m == C45) ? 1 : 0;
- smi_clk.s.preamble = 1;
- oct_mdio_writeq(smi_clk.u64, p->register_base + SMI_CLK);
- p->mode = m;
-}
-
-static int octeon_mdiobus_c45_addr(struct octeon_mdiobus *p,
- int phy_id, int regnum)
-{
- union cvmx_smix_cmd smi_cmd;
- union cvmx_smix_wr_dat smi_wr;
- int timeout = 1000;
-
- octeon_mdiobus_set_mode(p, C45);
-
- smi_wr.u64 = 0;
- smi_wr.s.dat = regnum & 0xffff;
- oct_mdio_writeq(smi_wr.u64, p->register_base + SMI_WR_DAT);
-
- regnum = (regnum >> 16) & 0x1f;
-
- smi_cmd.u64 = 0;
- smi_cmd.s.phy_op = 0; /* MDIO_CLAUSE_45_ADDRESS */
- smi_cmd.s.phy_adr = phy_id;
- smi_cmd.s.reg_adr = regnum;
- oct_mdio_writeq(smi_cmd.u64, p->register_base + SMI_CMD);
-
- do {
- /* Wait 1000 clocks so we don't saturate the RSL bus
- * doing reads.
- */
- __delay(1000);
- smi_wr.u64 = oct_mdio_readq(p->register_base + SMI_WR_DAT);
- } while (smi_wr.s.pending && --timeout);
-
- if (timeout <= 0)
- return -EIO;
- return 0;
-}
-
-static int octeon_mdiobus_read(struct mii_bus *bus, int phy_id, int regnum)
-{
- struct octeon_mdiobus *p = bus->priv;
- union cvmx_smix_cmd smi_cmd;
- union cvmx_smix_rd_dat smi_rd;
- unsigned int op = 1; /* MDIO_CLAUSE_22_READ */
- int timeout = 1000;
-
- if (regnum & MII_ADDR_C45) {
- int r = octeon_mdiobus_c45_addr(p, phy_id, regnum);
- if (r < 0)
- return r;
-
- regnum = (regnum >> 16) & 0x1f;
- op = 3; /* MDIO_CLAUSE_45_READ */
- } else {
- octeon_mdiobus_set_mode(p, C22);
- }
-
-
- smi_cmd.u64 = 0;
- smi_cmd.s.phy_op = op;
- smi_cmd.s.phy_adr = phy_id;
- smi_cmd.s.reg_adr = regnum;
- oct_mdio_writeq(smi_cmd.u64, p->register_base + SMI_CMD);
-
- do {
- /* Wait 1000 clocks so we don't saturate the RSL bus
- * doing reads.
- */
- __delay(1000);
- smi_rd.u64 = oct_mdio_readq(p->register_base + SMI_RD_DAT);
- } while (smi_rd.s.pending && --timeout);
-
- if (smi_rd.s.val)
- return smi_rd.s.dat;
- else
- return -EIO;
-}
-
-static int octeon_mdiobus_write(struct mii_bus *bus, int phy_id,
- int regnum, u16 val)
-{
- struct octeon_mdiobus *p = bus->priv;
- union cvmx_smix_cmd smi_cmd;
- union cvmx_smix_wr_dat smi_wr;
- unsigned int op = 0; /* MDIO_CLAUSE_22_WRITE */
- int timeout = 1000;
-
-
- if (regnum & MII_ADDR_C45) {
- int r = octeon_mdiobus_c45_addr(p, phy_id, regnum);
- if (r < 0)
- return r;
-
- regnum = (regnum >> 16) & 0x1f;
- op = 1; /* MDIO_CLAUSE_45_WRITE */
- } else {
- octeon_mdiobus_set_mode(p, C22);
- }
-
- smi_wr.u64 = 0;
- smi_wr.s.dat = val;
- oct_mdio_writeq(smi_wr.u64, p->register_base + SMI_WR_DAT);
-
- smi_cmd.u64 = 0;
- smi_cmd.s.phy_op = op;
- smi_cmd.s.phy_adr = phy_id;
- smi_cmd.s.reg_adr = regnum;
- oct_mdio_writeq(smi_cmd.u64, p->register_base + SMI_CMD);
-
- do {
- /* Wait 1000 clocks so we don't saturate the RSL bus
- * doing reads.
- */
- __delay(1000);
- smi_wr.u64 = oct_mdio_readq(p->register_base + SMI_WR_DAT);
- } while (smi_wr.s.pending && --timeout);
-
- if (timeout <= 0)
- return -EIO;
-
- return 0;
-}
+#include "mdio-cavium.h"
static int octeon_mdiobus_probe(struct platform_device *pdev)
{
- struct octeon_mdiobus *bus;
+ struct cavium_mdiobus *bus;
struct mii_bus *mii_bus;
struct resource *res_mem;
+ resource_size_t mdio_phys;
+ resource_size_t regsize;
union cvmx_smix_en smi_en;
int err = -ENOENT;
@@ -284,17 +38,17 @@
bus = mii_bus->priv;
bus->mii_bus = mii_bus;
- bus->mdio_phys = res_mem->start;
- bus->regsize = resource_size(res_mem);
+ mdio_phys = res_mem->start;
+ regsize = resource_size(res_mem);
- if (!devm_request_mem_region(&pdev->dev, bus->mdio_phys, bus->regsize,
+ if (!devm_request_mem_region(&pdev->dev, mdio_phys, regsize,
res_mem->name)) {
dev_err(&pdev->dev, "request_mem_region failed\n");
return -ENXIO;
}
bus->register_base =
- (u64)devm_ioremap(&pdev->dev, bus->mdio_phys, bus->regsize);
+ (u64)devm_ioremap(&pdev->dev, mdio_phys, regsize);
if (!bus->register_base) {
dev_err(&pdev->dev, "dev_ioremap failed\n");
return -ENOMEM;
@@ -304,13 +58,12 @@
smi_en.s.en = 1;
oct_mdio_writeq(smi_en.u64, bus->register_base + SMI_EN);
- bus->mii_bus->priv = bus;
- bus->mii_bus->name = "mdio-octeon";
+ bus->mii_bus->name = KBUILD_MODNAME;
snprintf(bus->mii_bus->id, MII_BUS_ID_SIZE, "%llx", bus->register_base);
bus->mii_bus->parent = &pdev->dev;
- bus->mii_bus->read = octeon_mdiobus_read;
- bus->mii_bus->write = octeon_mdiobus_write;
+ bus->mii_bus->read = cavium_mdiobus_read;
+ bus->mii_bus->write = cavium_mdiobus_write;
platform_set_drvdata(pdev, bus);
@@ -318,7 +71,7 @@
if (err)
goto fail_register;
- dev_info(&pdev->dev, "Version " DRV_VERSION "\n");
+ dev_info(&pdev->dev, "Probed\n");
return 0;
fail_register:
@@ -330,7 +83,7 @@
static int octeon_mdiobus_remove(struct platform_device *pdev)
{
- struct octeon_mdiobus *bus;
+ struct cavium_mdiobus *bus;
union cvmx_smix_en smi_en;
bus = platform_get_drvdata(pdev);
@@ -352,7 +105,7 @@
static struct platform_driver octeon_mdiobus_driver = {
.driver = {
- .name = "mdio-octeon",
+ .name = KBUILD_MODNAME,
.of_match_table = octeon_mdiobus_match,
},
.probe = octeon_mdiobus_probe,
@@ -367,7 +120,6 @@
module_platform_driver(octeon_mdiobus_driver);
-MODULE_DESCRIPTION(DRV_DESCRIPTION);
-MODULE_VERSION(DRV_VERSION);
+MODULE_DESCRIPTION("Cavium OCTEON MDIO bus driver");
MODULE_AUTHOR("David Daney");
MODULE_LICENSE("GPL");
diff --git a/drivers/net/phy/mdio-thunder.c b/drivers/net/phy/mdio-thunder.c
new file mode 100644
index 0000000..5646169
--- /dev/null
+++ b/drivers/net/phy/mdio-thunder.c
@@ -0,0 +1,154 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2009-2016 Cavium, Inc.
+ */
+
+#include <linux/of_address.h>
+#include <linux/of_mdio.h>
+#include <linux/module.h>
+#include <linux/gfp.h>
+#include <linux/phy.h>
+#include <linux/io.h>
+#include <linux/acpi.h>
+#include <linux/pci.h>
+
+#include "mdio-cavium.h"
+
+struct thunder_mdiobus_nexus {
+ void __iomem *bar0;
+ struct cavium_mdiobus *buses[4];
+};
+
+static int thunder_mdiobus_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct device_node *node;
+ struct fwnode_handle *fwn;
+ struct thunder_mdiobus_nexus *nexus;
+ int err;
+ int i;
+
+ nexus = devm_kzalloc(&pdev->dev, sizeof(*nexus), GFP_KERNEL);
+ if (!nexus)
+ return -ENOMEM;
+
+ pci_set_drvdata(pdev, nexus);
+
+ err = pcim_enable_device(pdev);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to enable PCI device\n");
+ pci_set_drvdata(pdev, NULL);
+ return err;
+ }
+
+ err = pci_request_regions(pdev, KBUILD_MODNAME);
+ if (err) {
+ dev_err(&pdev->dev, "pci_request_regions failed\n");
+ goto err_disable_device;
+ }
+
+ nexus->bar0 = pcim_iomap(pdev, 0, pci_resource_len(pdev, 0));
+ if (!nexus->bar0) {
+ err = -ENOMEM;
+ goto err_release_regions;
+ }
+
+ i = 0;
+ device_for_each_child_node(&pdev->dev, fwn) {
+ struct resource r;
+ struct mii_bus *mii_bus;
+ struct cavium_mdiobus *bus;
+ union cvmx_smix_en smi_en;
+
+ /* If it is not an OF node we cannot handle it yet, so
+ * exit the loop.
+ */
+ node = to_of_node(fwn);
+ if (!node)
+ break;
+
+ err = of_address_to_resource(node, 0, &r);
+ if (err) {
+ dev_err(&pdev->dev,
+ "Couldn't translate address for \"%s\"\n",
+ node->name);
+ break;
+ }
+
+ mii_bus = devm_mdiobus_alloc_size(&pdev->dev, sizeof(*bus));
+ if (!mii_bus)
+ break;
+ bus = mii_bus->priv;
+ bus->mii_bus = mii_bus;
+
+ nexus->buses[i] = bus;
+ i++;
+
+ bus->register_base = (u64)nexus->bar0 +
+ r.start - pci_resource_start(pdev, 0);
+
+ smi_en.u64 = 0;
+ smi_en.s.en = 1;
+ oct_mdio_writeq(smi_en.u64, bus->register_base + SMI_EN);
+ bus->mii_bus->name = KBUILD_MODNAME;
+ snprintf(bus->mii_bus->id, MII_BUS_ID_SIZE, "%llx", r.start);
+ bus->mii_bus->parent = &pdev->dev;
+ bus->mii_bus->read = cavium_mdiobus_read;
+ bus->mii_bus->write = cavium_mdiobus_write;
+
+ err = of_mdiobus_register(bus->mii_bus, node);
+ if (err)
+ dev_err(&pdev->dev, "of_mdiobus_register failed\n");
+
+ dev_info(&pdev->dev, "Added bus at %llx\n", r.start);
+ if (i >= ARRAY_SIZE(nexus->buses))
+ break;
+ }
+ return 0;
+
+err_release_regions:
+ pci_release_regions(pdev);
+
+err_disable_device:
+ pci_set_drvdata(pdev, NULL);
+ return err;
+}
+
+static void thunder_mdiobus_pci_remove(struct pci_dev *pdev)
+{
+ int i;
+ struct thunder_mdiobus_nexus *nexus = pci_get_drvdata(pdev);
+
+ for (i = 0; i < ARRAY_SIZE(nexus->buses); i++) {
+ struct cavium_mdiobus *bus = nexus->buses[i];
+
+ if (!bus)
+ continue;
+
+ mdiobus_unregister(bus->mii_bus);
+ mdiobus_free(bus->mii_bus);
+ oct_mdio_writeq(0, bus->register_base + SMI_EN);
+ }
+ pci_set_drvdata(pdev, NULL);
+}
+
+static const struct pci_device_id thunder_mdiobus_id_table[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xa02b) },
+ { 0, } /* End of table. */
+};
+MODULE_DEVICE_TABLE(pci, thunder_mdiobus_id_table);
+
+static struct pci_driver thunder_mdiobus_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = thunder_mdiobus_id_table,
+ .probe = thunder_mdiobus_pci_probe,
+ .remove = thunder_mdiobus_pci_remove,
+};
+
+module_pci_driver(thunder_mdiobus_driver);
+
+MODULE_DESCRIPTION("Cavium ThunderX MDIO bus driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c
index fc895d0..b2348f6 100644
--- a/drivers/net/vmxnet3/vmxnet3_drv.c
+++ b/drivers/net/vmxnet3/vmxnet3_drv.c
@@ -1022,14 +1022,16 @@
if (ctx.mss) {
if (unlikely(ctx.eth_ip_hdr_size + ctx.l4_hdr_size >
VMXNET3_MAX_TX_BUF_SIZE)) {
- goto hdr_too_big;
+ tq->stats.drop_oversized_hdr++;
+ goto drop_pkt;
}
} else {
if (skb->ip_summed == CHECKSUM_PARTIAL) {
if (unlikely(ctx.eth_ip_hdr_size +
skb->csum_offset >
VMXNET3_MAX_CSUM_OFFSET)) {
- goto hdr_too_big;
+ tq->stats.drop_oversized_hdr++;
+ goto drop_pkt;
}
}
}
@@ -1123,8 +1125,6 @@
return NETDEV_TX_OK;
-hdr_too_big:
- tq->stats.drop_oversized_hdr++;
unlock_drop_pkt:
spin_unlock_irqrestore(&tq->tx_lock, flags);
drop_pkt:
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index 2399099..800106a7 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -591,8 +591,6 @@
skb_gro_pull(skb, sizeof(struct vxlanhdr)); /* pull vxlan header */
- flush = 0;
-
for (p = *head; p; p = p->next) {
if (!NAPI_GRO_CB(p)->same_flow)
continue;
@@ -606,6 +604,7 @@
}
pp = eth_gro_receive(head, skb);
+ flush = 0;
out:
skb_gro_remcsum_cleanup(skb, &grc);
@@ -1790,6 +1789,7 @@
#if IS_ENABLED(CONFIG_IPV6)
static struct dst_entry *vxlan6_get_route(struct vxlan_dev *vxlan,
struct sk_buff *skb, int oif, u8 tos,
+ __be32 label,
const struct in6_addr *daddr,
struct in6_addr *saddr,
struct dst_cache *dst_cache,
@@ -1813,6 +1813,7 @@
fl6.flowi6_tos = RT_TOS(tos);
fl6.daddr = *daddr;
fl6.saddr = vxlan->cfg.saddr.sin6.sin6_addr;
+ fl6.flowlabel = label;
fl6.flowi6_mark = skb->mark;
fl6.flowi6_proto = IPPROTO_UDP;
@@ -1888,7 +1889,7 @@
struct vxlan_metadata _md;
struct vxlan_metadata *md = &_md;
__be16 src_port = 0, dst_port;
- __be32 vni;
+ __be32 vni, label;
__be16 df = 0;
__u8 tos, ttl;
int err;
@@ -1939,12 +1940,14 @@
if (tos == 1)
tos = ip_tunnel_get_dsfield(old_iph, skb);
+ label = vxlan->cfg.label;
src_port = udp_flow_src_port(dev_net(dev), skb, vxlan->cfg.port_min,
vxlan->cfg.port_max, true);
if (info) {
ttl = info->key.ttl;
tos = info->key.tos;
+ label = info->key.label;
udp_sum = !!(info->key.tun_flags & TUNNEL_CSUM);
if (info->options_len)
@@ -2020,7 +2023,7 @@
ndst = vxlan6_get_route(vxlan, skb,
rdst ? rdst->remote_ifindex : 0, tos,
- &dst->sin6.sin6_addr, &saddr,
+ label, &dst->sin6.sin6_addr, &saddr,
dst_cache, info);
if (IS_ERR(ndst)) {
netdev_dbg(dev, "no route to %pI6\n",
@@ -2067,7 +2070,7 @@
}
udp_tunnel6_xmit_skb(ndst, sk, skb, dev,
&saddr, &dst->sin6.sin6_addr, tos, ttl,
- src_port, dst_port, !udp_sum);
+ label, src_port, dst_port, !udp_sum);
#endif
}
@@ -2390,7 +2393,7 @@
if (!vxlan->vn6_sock)
return -EINVAL;
ndst = vxlan6_get_route(vxlan, skb, 0, info->key.tos,
- &info->key.u.ipv6.dst,
+ info->key.label, &info->key.u.ipv6.dst,
&info->key.u.ipv6.src, NULL, info);
if (IS_ERR(ndst))
return PTR_ERR(ndst);
@@ -2505,6 +2508,7 @@
[IFLA_VXLAN_LOCAL6] = { .len = sizeof(struct in6_addr) },
[IFLA_VXLAN_TOS] = { .type = NLA_U8 },
[IFLA_VXLAN_TTL] = { .type = NLA_U8 },
+ [IFLA_VXLAN_LABEL] = { .type = NLA_U32 },
[IFLA_VXLAN_LEARNING] = { .type = NLA_U8 },
[IFLA_VXLAN_AGEING] = { .type = NLA_U32 },
[IFLA_VXLAN_LIMIT] = { .type = NLA_U32 },
@@ -2739,6 +2743,11 @@
vxlan->flags |= VXLAN_F_IPV6;
}
+ if (conf->label && !use_ipv6) {
+ pr_info("label only supported in use with IPv6\n");
+ return -EINVAL;
+ }
+
if (conf->remote_ifindex) {
lowerdev = __dev_get_by_index(src_net, conf->remote_ifindex);
dst->remote_ifindex = conf->remote_ifindex;
@@ -2887,6 +2896,10 @@
if (data[IFLA_VXLAN_TTL])
conf.ttl = nla_get_u8(data[IFLA_VXLAN_TTL]);
+ if (data[IFLA_VXLAN_LABEL])
+ conf.label = nla_get_be32(data[IFLA_VXLAN_LABEL]) &
+ IPV6_FLOWLABEL_MASK;
+
if (!data[IFLA_VXLAN_LEARNING] || nla_get_u8(data[IFLA_VXLAN_LEARNING]))
conf.flags |= VXLAN_F_LEARN;
@@ -2990,6 +3003,7 @@
nla_total_size(sizeof(struct in6_addr)) + /* IFLA_VXLAN_LOCAL{6} */
nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_TTL */
nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_TOS */
+ nla_total_size(sizeof(__be32)) + /* IFLA_VXLAN_LABEL */
nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_LEARNING */
nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_PROXY */
nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_RSC */
@@ -3053,6 +3067,7 @@
if (nla_put_u8(skb, IFLA_VXLAN_TTL, vxlan->cfg.ttl) ||
nla_put_u8(skb, IFLA_VXLAN_TOS, vxlan->cfg.tos) ||
+ nla_put_be32(skb, IFLA_VXLAN_LABEL, vxlan->cfg.label) ||
nla_put_u8(skb, IFLA_VXLAN_LEARNING,
!!(vxlan->flags & VXLAN_F_LEARN)) ||
nla_put_u8(skb, IFLA_VXLAN_PROXY,
diff --git a/drivers/net/wan/farsync.c b/drivers/net/wan/farsync.c
index 44541db..69b994f 100644
--- a/drivers/net/wan/farsync.c
+++ b/drivers/net/wan/farsync.c
@@ -2516,7 +2516,7 @@
dev->mem_start = card->phys_mem
+ BUF_OFFSET ( txBuffer[i][0][0]);
dev->mem_end = card->phys_mem
- + BUF_OFFSET ( txBuffer[i][NUM_TX_BUFFER][0]);
+ + BUF_OFFSET ( txBuffer[i][NUM_TX_BUFFER - 1][LEN_RX_BUFFER - 1]);
dev->base_addr = card->pci_conf;
dev->irq = card->irq;
diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig
index 03aa35f..db1ca62 100644
--- a/drivers/net/wireless/ath/ath10k/Kconfig
+++ b/drivers/net/wireless/ath/ath10k/Kconfig
@@ -15,6 +15,12 @@
---help---
This module adds support for PCIE bus
+config ATH10K_AHB
+ bool "Atheros ath10k AHB support"
+ depends on ATH10K_PCI && OF && RESET_CONTROLLER
+ ---help---
+ This module adds support for AHB bus
+
config ATH10K_DEBUG
bool "Atheros ath10k debugging"
depends on ATH10K
diff --git a/drivers/net/wireless/ath/ath10k/Makefile b/drivers/net/wireless/ath/ath10k/Makefile
index c04fb00..930fadd 100644
--- a/drivers/net/wireless/ath/ath10k/Makefile
+++ b/drivers/net/wireless/ath/ath10k/Makefile
@@ -25,5 +25,7 @@
ath10k_pci-y += pci.o \
ce.o
+ath10k_pci-$(CONFIG_ATH10K_AHB) += ahb.o
+
# for tracing framework to find trace.h
CFLAGS_trace.o := -I$(src)
diff --git a/drivers/net/wireless/ath/ath10k/ahb.c b/drivers/net/wireless/ath/ath10k/ahb.c
new file mode 100644
index 0000000..bd62bc1
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/ahb.c
@@ -0,0 +1,933 @@
+/*
+ * Copyright (c) 2016 Qualcomm Atheros, Inc. All rights reserved.
+ * Copyright (c) 2015 The Linux Foundation. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+#include "core.h"
+#include "debug.h"
+#include "pci.h"
+#include "ahb.h"
+
+static const struct of_device_id ath10k_ahb_of_match[] = {
+ /* TODO: enable this entry once everything in place.
+ * { .compatible = "qcom,ipq4019-wifi",
+ * .data = (void *)ATH10K_HW_QCA4019 },
+ */
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, ath10k_ahb_of_match);
+
+static inline struct ath10k_ahb *ath10k_ahb_priv(struct ath10k *ar)
+{
+ return &((struct ath10k_pci *)ar->drv_priv)->ahb[0];
+}
+
+static void ath10k_ahb_write32(struct ath10k *ar, u32 offset, u32 value)
+{
+ struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar);
+
+ iowrite32(value, ar_ahb->mem + offset);
+}
+
+static u32 ath10k_ahb_read32(struct ath10k *ar, u32 offset)
+{
+ struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar);
+
+ return ioread32(ar_ahb->mem + offset);
+}
+
+static u32 ath10k_ahb_gcc_read32(struct ath10k *ar, u32 offset)
+{
+ struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar);
+
+ return ioread32(ar_ahb->gcc_mem + offset);
+}
+
+static void ath10k_ahb_tcsr_write32(struct ath10k *ar, u32 offset, u32 value)
+{
+ struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar);
+
+ iowrite32(value, ar_ahb->tcsr_mem + offset);
+}
+
+static u32 ath10k_ahb_tcsr_read32(struct ath10k *ar, u32 offset)
+{
+ struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar);
+
+ return ioread32(ar_ahb->tcsr_mem + offset);
+}
+
+static u32 ath10k_ahb_soc_read32(struct ath10k *ar, u32 addr)
+{
+ return ath10k_ahb_read32(ar, RTC_SOC_BASE_ADDRESS + addr);
+}
+
+static int ath10k_ahb_get_num_banks(struct ath10k *ar)
+{
+ if (ar->hw_rev == ATH10K_HW_QCA4019)
+ return 1;
+
+ ath10k_warn(ar, "unknown number of banks, assuming 1\n");
+ return 1;
+}
+
+static int ath10k_ahb_clock_init(struct ath10k *ar)
+{
+ struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar);
+ struct device *dev;
+ int ret;
+
+ dev = &ar_ahb->pdev->dev;
+
+ ar_ahb->cmd_clk = clk_get(dev, "wifi_wcss_cmd");
+ if (IS_ERR_OR_NULL(ar_ahb->cmd_clk)) {
+ ath10k_err(ar, "failed to get cmd clk: %ld\n",
+ PTR_ERR(ar_ahb->cmd_clk));
+ ret = ar_ahb->cmd_clk ? PTR_ERR(ar_ahb->cmd_clk) : -ENODEV;
+ goto out;
+ }
+
+ ar_ahb->ref_clk = clk_get(dev, "wifi_wcss_ref");
+ if (IS_ERR_OR_NULL(ar_ahb->ref_clk)) {
+ ath10k_err(ar, "failed to get ref clk: %ld\n",
+ PTR_ERR(ar_ahb->ref_clk));
+ ret = ar_ahb->ref_clk ? PTR_ERR(ar_ahb->ref_clk) : -ENODEV;
+ goto err_cmd_clk_put;
+ }
+
+ ar_ahb->rtc_clk = clk_get(dev, "wifi_wcss_rtc");
+ if (IS_ERR_OR_NULL(ar_ahb->rtc_clk)) {
+ ath10k_err(ar, "failed to get rtc clk: %ld\n",
+ PTR_ERR(ar_ahb->rtc_clk));
+ ret = ar_ahb->rtc_clk ? PTR_ERR(ar_ahb->rtc_clk) : -ENODEV;
+ goto err_ref_clk_put;
+ }
+
+ return 0;
+
+err_ref_clk_put:
+ clk_put(ar_ahb->ref_clk);
+
+err_cmd_clk_put:
+ clk_put(ar_ahb->cmd_clk);
+
+out:
+ return ret;
+}
+
+static void ath10k_ahb_clock_deinit(struct ath10k *ar)
+{
+ struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar);
+
+ if (!IS_ERR_OR_NULL(ar_ahb->cmd_clk))
+ clk_put(ar_ahb->cmd_clk);
+
+ if (!IS_ERR_OR_NULL(ar_ahb->ref_clk))
+ clk_put(ar_ahb->ref_clk);
+
+ if (!IS_ERR_OR_NULL(ar_ahb->rtc_clk))
+ clk_put(ar_ahb->rtc_clk);
+
+ ar_ahb->cmd_clk = NULL;
+ ar_ahb->ref_clk = NULL;
+ ar_ahb->rtc_clk = NULL;
+}
+
+static int ath10k_ahb_clock_enable(struct ath10k *ar)
+{
+ struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar);
+ struct device *dev;
+ int ret;
+
+ dev = &ar_ahb->pdev->dev;
+
+ if (IS_ERR_OR_NULL(ar_ahb->cmd_clk) ||
+ IS_ERR_OR_NULL(ar_ahb->ref_clk) ||
+ IS_ERR_OR_NULL(ar_ahb->rtc_clk)) {
+ ath10k_err(ar, "clock(s) is/are not initialized\n");
+ ret = -EIO;
+ goto out;
+ }
+
+ ret = clk_prepare_enable(ar_ahb->cmd_clk);
+ if (ret) {
+ ath10k_err(ar, "failed to enable cmd clk: %d\n", ret);
+ goto out;
+ }
+
+ ret = clk_prepare_enable(ar_ahb->ref_clk);
+ if (ret) {
+ ath10k_err(ar, "failed to enable ref clk: %d\n", ret);
+ goto err_cmd_clk_disable;
+ }
+
+ ret = clk_prepare_enable(ar_ahb->rtc_clk);
+ if (ret) {
+ ath10k_err(ar, "failed to enable rtc clk: %d\n", ret);
+ goto err_ref_clk_disable;
+ }
+
+ return 0;
+
+err_ref_clk_disable:
+ clk_disable_unprepare(ar_ahb->ref_clk);
+
+err_cmd_clk_disable:
+ clk_disable_unprepare(ar_ahb->cmd_clk);
+
+out:
+ return ret;
+}
+
+static void ath10k_ahb_clock_disable(struct ath10k *ar)
+{
+ struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar);
+
+ if (!IS_ERR_OR_NULL(ar_ahb->cmd_clk))
+ clk_disable_unprepare(ar_ahb->cmd_clk);
+
+ if (!IS_ERR_OR_NULL(ar_ahb->ref_clk))
+ clk_disable_unprepare(ar_ahb->ref_clk);
+
+ if (!IS_ERR_OR_NULL(ar_ahb->rtc_clk))
+ clk_disable_unprepare(ar_ahb->rtc_clk);
+}
+
+static int ath10k_ahb_rst_ctrl_init(struct ath10k *ar)
+{
+ struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar);
+ struct device *dev;
+ int ret;
+
+ dev = &ar_ahb->pdev->dev;
+
+ ar_ahb->core_cold_rst = reset_control_get(dev, "wifi_core_cold");
+ if (IS_ERR_OR_NULL(ar_ahb->core_cold_rst)) {
+ ath10k_err(ar, "failed to get core cold rst ctrl: %ld\n",
+ PTR_ERR(ar_ahb->core_cold_rst));
+ ret = ar_ahb->core_cold_rst ?
+ PTR_ERR(ar_ahb->core_cold_rst) : -ENODEV;
+ goto out;
+ }
+
+ ar_ahb->radio_cold_rst = reset_control_get(dev, "wifi_radio_cold");
+ if (IS_ERR_OR_NULL(ar_ahb->radio_cold_rst)) {
+ ath10k_err(ar, "failed to get radio cold rst ctrl: %ld\n",
+ PTR_ERR(ar_ahb->radio_cold_rst));
+ ret = ar_ahb->radio_cold_rst ?
+ PTR_ERR(ar_ahb->radio_cold_rst) : -ENODEV;
+ goto err_core_cold_rst_put;
+ }
+
+ ar_ahb->radio_warm_rst = reset_control_get(dev, "wifi_radio_warm");
+ if (IS_ERR_OR_NULL(ar_ahb->radio_warm_rst)) {
+ ath10k_err(ar, "failed to get radio warm rst ctrl: %ld\n",
+ PTR_ERR(ar_ahb->radio_warm_rst));
+ ret = ar_ahb->radio_warm_rst ?
+ PTR_ERR(ar_ahb->radio_warm_rst) : -ENODEV;
+ goto err_radio_cold_rst_put;
+ }
+
+ ar_ahb->radio_srif_rst = reset_control_get(dev, "wifi_radio_srif");
+ if (IS_ERR_OR_NULL(ar_ahb->radio_srif_rst)) {
+ ath10k_err(ar, "failed to get radio srif rst ctrl: %ld\n",
+ PTR_ERR(ar_ahb->radio_srif_rst));
+ ret = ar_ahb->radio_srif_rst ?
+ PTR_ERR(ar_ahb->radio_srif_rst) : -ENODEV;
+ goto err_radio_warm_rst_put;
+ }
+
+ ar_ahb->cpu_init_rst = reset_control_get(dev, "wifi_cpu_init");
+ if (IS_ERR_OR_NULL(ar_ahb->cpu_init_rst)) {
+ ath10k_err(ar, "failed to get cpu init rst ctrl: %ld\n",
+ PTR_ERR(ar_ahb->cpu_init_rst));
+ ret = ar_ahb->cpu_init_rst ?
+ PTR_ERR(ar_ahb->cpu_init_rst) : -ENODEV;
+ goto err_radio_srif_rst_put;
+ }
+
+ return 0;
+
+err_radio_srif_rst_put:
+ reset_control_put(ar_ahb->radio_srif_rst);
+
+err_radio_warm_rst_put:
+ reset_control_put(ar_ahb->radio_warm_rst);
+
+err_radio_cold_rst_put:
+ reset_control_put(ar_ahb->radio_cold_rst);
+
+err_core_cold_rst_put:
+ reset_control_put(ar_ahb->core_cold_rst);
+
+out:
+ return ret;
+}
+
+static void ath10k_ahb_rst_ctrl_deinit(struct ath10k *ar)
+{
+ struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar);
+
+ if (!IS_ERR_OR_NULL(ar_ahb->core_cold_rst))
+ reset_control_put(ar_ahb->core_cold_rst);
+
+ if (!IS_ERR_OR_NULL(ar_ahb->radio_cold_rst))
+ reset_control_put(ar_ahb->radio_cold_rst);
+
+ if (!IS_ERR_OR_NULL(ar_ahb->radio_warm_rst))
+ reset_control_put(ar_ahb->radio_warm_rst);
+
+ if (!IS_ERR_OR_NULL(ar_ahb->radio_srif_rst))
+ reset_control_put(ar_ahb->radio_srif_rst);
+
+ if (!IS_ERR_OR_NULL(ar_ahb->cpu_init_rst))
+ reset_control_put(ar_ahb->cpu_init_rst);
+
+ ar_ahb->core_cold_rst = NULL;
+ ar_ahb->radio_cold_rst = NULL;
+ ar_ahb->radio_warm_rst = NULL;
+ ar_ahb->radio_srif_rst = NULL;
+ ar_ahb->cpu_init_rst = NULL;
+}
+
+static int ath10k_ahb_release_reset(struct ath10k *ar)
+{
+ struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar);
+ int ret;
+
+ if (IS_ERR_OR_NULL(ar_ahb->radio_cold_rst) ||
+ IS_ERR_OR_NULL(ar_ahb->radio_warm_rst) ||
+ IS_ERR_OR_NULL(ar_ahb->radio_srif_rst) ||
+ IS_ERR_OR_NULL(ar_ahb->cpu_init_rst)) {
+ ath10k_err(ar, "rst ctrl(s) is/are not initialized\n");
+ return -EINVAL;
+ }
+
+ ret = reset_control_deassert(ar_ahb->radio_cold_rst);
+ if (ret) {
+ ath10k_err(ar, "failed to deassert radio cold rst: %d\n", ret);
+ return ret;
+ }
+
+ ret = reset_control_deassert(ar_ahb->radio_warm_rst);
+ if (ret) {
+ ath10k_err(ar, "failed to deassert radio warm rst: %d\n", ret);
+ return ret;
+ }
+
+ ret = reset_control_deassert(ar_ahb->radio_srif_rst);
+ if (ret) {
+ ath10k_err(ar, "failed to deassert radio srif rst: %d\n", ret);
+ return ret;
+ }
+
+ ret = reset_control_deassert(ar_ahb->cpu_init_rst);
+ if (ret) {
+ ath10k_err(ar, "failed to deassert cpu init rst: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void ath10k_ahb_halt_axi_bus(struct ath10k *ar, u32 haltreq_reg,
+ u32 haltack_reg)
+{
+ unsigned long timeout;
+ u32 val;
+
+ /* Issue halt axi bus request */
+ val = ath10k_ahb_tcsr_read32(ar, haltreq_reg);
+ val |= AHB_AXI_BUS_HALT_REQ;
+ ath10k_ahb_tcsr_write32(ar, haltreq_reg, val);
+
+ /* Wait for axi bus halted ack */
+ timeout = jiffies + msecs_to_jiffies(ATH10K_AHB_AXI_BUS_HALT_TIMEOUT);
+ do {
+ val = ath10k_ahb_tcsr_read32(ar, haltack_reg);
+ if (val & AHB_AXI_BUS_HALT_ACK)
+ break;
+
+ mdelay(1);
+ } while (time_before(jiffies, timeout));
+
+ if (!(val & AHB_AXI_BUS_HALT_ACK)) {
+ ath10k_err(ar, "failed to halt axi bus: %d\n", val);
+ return;
+ }
+
+ ath10k_dbg(ar, ATH10K_DBG_AHB, "axi bus halted\n");
+}
+
+static void ath10k_ahb_halt_chip(struct ath10k *ar)
+{
+ struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar);
+ u32 core_id, glb_cfg_reg, haltreq_reg, haltack_reg;
+ u32 val;
+ int ret;
+
+ if (IS_ERR_OR_NULL(ar_ahb->core_cold_rst) ||
+ IS_ERR_OR_NULL(ar_ahb->radio_cold_rst) ||
+ IS_ERR_OR_NULL(ar_ahb->radio_warm_rst) ||
+ IS_ERR_OR_NULL(ar_ahb->radio_srif_rst) ||
+ IS_ERR_OR_NULL(ar_ahb->cpu_init_rst)) {
+ ath10k_err(ar, "rst ctrl(s) is/are not initialized\n");
+ return;
+ }
+
+ core_id = ath10k_ahb_read32(ar, ATH10K_AHB_WLAN_CORE_ID_REG);
+
+ switch (core_id) {
+ case 0:
+ glb_cfg_reg = ATH10K_AHB_TCSR_WIFI0_GLB_CFG;
+ haltreq_reg = ATH10K_AHB_TCSR_WCSS0_HALTREQ;
+ haltack_reg = ATH10K_AHB_TCSR_WCSS0_HALTACK;
+ break;
+ case 1:
+ glb_cfg_reg = ATH10K_AHB_TCSR_WIFI1_GLB_CFG;
+ haltreq_reg = ATH10K_AHB_TCSR_WCSS1_HALTREQ;
+ haltack_reg = ATH10K_AHB_TCSR_WCSS1_HALTACK;
+ break;
+ default:
+ ath10k_err(ar, "invalid core id %d found, skipping reset sequence\n",
+ core_id);
+ return;
+ }
+
+ ath10k_ahb_halt_axi_bus(ar, haltreq_reg, haltack_reg);
+
+ val = ath10k_ahb_tcsr_read32(ar, glb_cfg_reg);
+ val |= TCSR_WIFIX_GLB_CFG_DISABLE_CORE_CLK;
+ ath10k_ahb_tcsr_write32(ar, glb_cfg_reg, val);
+
+ ret = reset_control_assert(ar_ahb->core_cold_rst);
+ if (ret)
+ ath10k_err(ar, "failed to assert core cold rst: %d\n", ret);
+ msleep(1);
+
+ ret = reset_control_assert(ar_ahb->radio_cold_rst);
+ if (ret)
+ ath10k_err(ar, "failed to assert radio cold rst: %d\n", ret);
+ msleep(1);
+
+ ret = reset_control_assert(ar_ahb->radio_warm_rst);
+ if (ret)
+ ath10k_err(ar, "failed to assert radio warm rst: %d\n", ret);
+ msleep(1);
+
+ ret = reset_control_assert(ar_ahb->radio_srif_rst);
+ if (ret)
+ ath10k_err(ar, "failed to assert radio srif rst: %d\n", ret);
+ msleep(1);
+
+ ret = reset_control_assert(ar_ahb->cpu_init_rst);
+ if (ret)
+ ath10k_err(ar, "failed to assert cpu init rst: %d\n", ret);
+ msleep(10);
+
+ /* Clear halt req and core clock disable req before
+ * deasserting wifi core reset.
+ */
+ val = ath10k_ahb_tcsr_read32(ar, haltreq_reg);
+ val &= ~AHB_AXI_BUS_HALT_REQ;
+ ath10k_ahb_tcsr_write32(ar, haltreq_reg, val);
+
+ val = ath10k_ahb_tcsr_read32(ar, glb_cfg_reg);
+ val &= ~TCSR_WIFIX_GLB_CFG_DISABLE_CORE_CLK;
+ ath10k_ahb_tcsr_write32(ar, glb_cfg_reg, val);
+
+ ret = reset_control_deassert(ar_ahb->core_cold_rst);
+ if (ret)
+ ath10k_err(ar, "failed to deassert core cold rst: %d\n", ret);
+
+ ath10k_dbg(ar, ATH10K_DBG_AHB, "core %d reset done\n", core_id);
+}
+
+static irqreturn_t ath10k_ahb_interrupt_handler(int irq, void *arg)
+{
+ struct ath10k *ar = arg;
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+ if (!ath10k_pci_irq_pending(ar))
+ return IRQ_NONE;
+
+ ath10k_pci_disable_and_clear_legacy_irq(ar);
+ tasklet_schedule(&ar_pci->intr_tq);
+
+ return IRQ_HANDLED;
+}
+
+static int ath10k_ahb_request_irq_legacy(struct ath10k *ar)
+{
+ struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar);
+ int ret;
+
+ ret = request_irq(ar_ahb->irq,
+ ath10k_ahb_interrupt_handler,
+ IRQF_SHARED, "ath10k_ahb", ar);
+ if (ret) {
+ ath10k_warn(ar, "failed to request legacy irq %d: %d\n",
+ ar_ahb->irq, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void ath10k_ahb_release_irq_legacy(struct ath10k *ar)
+{
+ struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar);
+
+ free_irq(ar_ahb->irq, ar);
+}
+
+static void ath10k_ahb_irq_disable(struct ath10k *ar)
+{
+ ath10k_ce_disable_interrupts(ar);
+ ath10k_pci_disable_and_clear_legacy_irq(ar);
+}
+
+static int ath10k_ahb_resource_init(struct ath10k *ar)
+{
+ struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar);
+ struct platform_device *pdev;
+ struct device *dev;
+ struct resource *res;
+ int ret;
+
+ pdev = ar_ahb->pdev;
+ dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ ath10k_err(ar, "failed to get memory resource\n");
+ ret = -ENXIO;
+ goto out;
+ }
+
+ ar_ahb->mem = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(ar_ahb->mem)) {
+ ath10k_err(ar, "mem ioremap error\n");
+ ret = PTR_ERR(ar_ahb->mem);
+ goto out;
+ }
+
+ ar_ahb->mem_len = resource_size(res);
+
+ ar_ahb->gcc_mem = ioremap_nocache(ATH10K_GCC_REG_BASE,
+ ATH10K_GCC_REG_SIZE);
+ if (!ar_ahb->gcc_mem) {
+ ath10k_err(ar, "gcc mem ioremap error\n");
+ ret = -ENOMEM;
+ goto err_mem_unmap;
+ }
+
+ ar_ahb->tcsr_mem = ioremap_nocache(ATH10K_TCSR_REG_BASE,
+ ATH10K_TCSR_REG_SIZE);
+ if (!ar_ahb->tcsr_mem) {
+ ath10k_err(ar, "tcsr mem ioremap error\n");
+ ret = -ENOMEM;
+ goto err_gcc_mem_unmap;
+ }
+
+ ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
+ if (ret) {
+ ath10k_err(ar, "failed to set 32-bit dma mask: %d\n", ret);
+ goto err_tcsr_mem_unmap;
+ }
+
+ ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
+ if (ret) {
+ ath10k_err(ar, "failed to set 32-bit consistent dma: %d\n",
+ ret);
+ goto err_tcsr_mem_unmap;
+ }
+
+ ret = ath10k_ahb_clock_init(ar);
+ if (ret)
+ goto err_tcsr_mem_unmap;
+
+ ret = ath10k_ahb_rst_ctrl_init(ar);
+ if (ret)
+ goto err_clock_deinit;
+
+ ar_ahb->irq = platform_get_irq_byname(pdev, "legacy");
+ if (ar_ahb->irq < 0) {
+ ath10k_err(ar, "failed to get irq number: %d\n", ar_ahb->irq);
+ goto err_clock_deinit;
+ }
+
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "irq: %d\n", ar_ahb->irq);
+
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "mem: 0x%p mem_len: %lu gcc mem: 0x%p tcsr_mem: 0x%p\n",
+ ar_ahb->mem, ar_ahb->mem_len,
+ ar_ahb->gcc_mem, ar_ahb->tcsr_mem);
+ return 0;
+
+err_clock_deinit:
+ ath10k_ahb_clock_deinit(ar);
+
+err_tcsr_mem_unmap:
+ iounmap(ar_ahb->tcsr_mem);
+
+err_gcc_mem_unmap:
+ ar_ahb->tcsr_mem = NULL;
+ iounmap(ar_ahb->gcc_mem);
+
+err_mem_unmap:
+ ar_ahb->gcc_mem = NULL;
+ devm_iounmap(&pdev->dev, ar_ahb->mem);
+
+out:
+ ar_ahb->mem = NULL;
+ return ret;
+}
+
+static void ath10k_ahb_resource_deinit(struct ath10k *ar)
+{
+ struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar);
+ struct device *dev;
+
+ dev = &ar_ahb->pdev->dev;
+
+ if (ar_ahb->mem)
+ devm_iounmap(dev, ar_ahb->mem);
+
+ if (ar_ahb->gcc_mem)
+ iounmap(ar_ahb->gcc_mem);
+
+ if (ar_ahb->tcsr_mem)
+ iounmap(ar_ahb->tcsr_mem);
+
+ ar_ahb->mem = NULL;
+ ar_ahb->gcc_mem = NULL;
+ ar_ahb->tcsr_mem = NULL;
+
+ ath10k_ahb_clock_deinit(ar);
+ ath10k_ahb_rst_ctrl_deinit(ar);
+}
+
+static int ath10k_ahb_prepare_device(struct ath10k *ar)
+{
+ u32 val;
+ int ret;
+
+ ret = ath10k_ahb_clock_enable(ar);
+ if (ret) {
+ ath10k_err(ar, "failed to enable clocks\n");
+ return ret;
+ }
+
+ /* Clock for the target is supplied from outside of target (ie,
+ * external clock module controlled by the host). Target needs
+ * to know what frequency target cpu is configured which is needed
+ * for target internal use. Read target cpu frequency info from
+ * gcc register and write into target's scratch register where
+ * target expects this information.
+ */
+ val = ath10k_ahb_gcc_read32(ar, ATH10K_AHB_GCC_FEPLL_PLL_DIV);
+ ath10k_ahb_write32(ar, ATH10K_AHB_WIFI_SCRATCH_5_REG, val);
+
+ ret = ath10k_ahb_release_reset(ar);
+ if (ret)
+ goto err_clk_disable;
+
+ ath10k_ahb_irq_disable(ar);
+
+ ath10k_ahb_write32(ar, FW_INDICATOR_ADDRESS, FW_IND_HOST_READY);
+
+ ret = ath10k_pci_wait_for_target_init(ar);
+ if (ret)
+ goto err_halt_chip;
+
+ return 0;
+
+err_halt_chip:
+ ath10k_ahb_halt_chip(ar);
+
+err_clk_disable:
+ ath10k_ahb_clock_disable(ar);
+
+ return ret;
+}
+
+static int ath10k_ahb_chip_reset(struct ath10k *ar)
+{
+ int ret;
+
+ ath10k_ahb_halt_chip(ar);
+ ath10k_ahb_clock_disable(ar);
+
+ ret = ath10k_ahb_prepare_device(ar);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int ath10k_ahb_wake_target_cpu(struct ath10k *ar)
+{
+ u32 addr, val;
+
+ addr = SOC_CORE_BASE_ADDRESS | CORE_CTRL_ADDRESS;
+ val = ath10k_ahb_read32(ar, addr);
+ val |= ATH10K_AHB_CORE_CTRL_CPU_INTR_MASK;
+ ath10k_ahb_write32(ar, addr, val);
+
+ return 0;
+}
+
+static int ath10k_ahb_hif_start(struct ath10k *ar)
+{
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot ahb hif start\n");
+
+ ath10k_ce_enable_interrupts(ar);
+ ath10k_pci_enable_legacy_irq(ar);
+
+ ath10k_pci_rx_post(ar);
+
+ return 0;
+}
+
+static void ath10k_ahb_hif_stop(struct ath10k *ar)
+{
+ struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar);
+
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot ahb hif stop\n");
+
+ ath10k_ahb_irq_disable(ar);
+ synchronize_irq(ar_ahb->irq);
+
+ ath10k_pci_flush(ar);
+}
+
+static int ath10k_ahb_hif_power_up(struct ath10k *ar)
+{
+ int ret;
+
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot ahb hif power up\n");
+
+ ret = ath10k_ahb_chip_reset(ar);
+ if (ret) {
+ ath10k_err(ar, "failed to reset chip: %d\n", ret);
+ goto out;
+ }
+
+ ret = ath10k_pci_init_pipes(ar);
+ if (ret) {
+ ath10k_err(ar, "failed to initialize CE: %d\n", ret);
+ goto out;
+ }
+
+ ret = ath10k_pci_init_config(ar);
+ if (ret) {
+ ath10k_err(ar, "failed to setup init config: %d\n", ret);
+ goto err_ce_deinit;
+ }
+
+ ret = ath10k_ahb_wake_target_cpu(ar);
+ if (ret) {
+ ath10k_err(ar, "could not wake up target CPU: %d\n", ret);
+ goto err_ce_deinit;
+ }
+
+ return 0;
+
+err_ce_deinit:
+ ath10k_pci_ce_deinit(ar);
+out:
+ return ret;
+}
+
+static const struct ath10k_hif_ops ath10k_ahb_hif_ops = {
+ .tx_sg = ath10k_pci_hif_tx_sg,
+ .diag_read = ath10k_pci_hif_diag_read,
+ .diag_write = ath10k_pci_diag_write_mem,
+ .exchange_bmi_msg = ath10k_pci_hif_exchange_bmi_msg,
+ .start = ath10k_ahb_hif_start,
+ .stop = ath10k_ahb_hif_stop,
+ .map_service_to_pipe = ath10k_pci_hif_map_service_to_pipe,
+ .get_default_pipe = ath10k_pci_hif_get_default_pipe,
+ .send_complete_check = ath10k_pci_hif_send_complete_check,
+ .get_free_queue_number = ath10k_pci_hif_get_free_queue_number,
+ .power_up = ath10k_ahb_hif_power_up,
+ .power_down = ath10k_pci_hif_power_down,
+ .read32 = ath10k_ahb_read32,
+ .write32 = ath10k_ahb_write32,
+};
+
+static const struct ath10k_bus_ops ath10k_ahb_bus_ops = {
+ .read32 = ath10k_ahb_read32,
+ .write32 = ath10k_ahb_write32,
+ .get_num_banks = ath10k_ahb_get_num_banks,
+};
+
+static int ath10k_ahb_probe(struct platform_device *pdev)
+{
+ struct ath10k *ar;
+ struct ath10k_ahb *ar_ahb;
+ struct ath10k_pci *ar_pci;
+ const struct of_device_id *of_id;
+ enum ath10k_hw_rev hw_rev;
+ size_t size;
+ int ret;
+ u32 chip_id;
+
+ of_id = of_match_device(ath10k_ahb_of_match, &pdev->dev);
+ if (!of_id) {
+ dev_err(&pdev->dev, "failed to find matching device tree id\n");
+ return -EINVAL;
+ }
+
+ hw_rev = (enum ath10k_hw_rev)of_id->data;
+
+ size = sizeof(*ar_pci) + sizeof(*ar_ahb);
+ ar = ath10k_core_create(size, &pdev->dev, ATH10K_BUS_AHB,
+ hw_rev, &ath10k_ahb_hif_ops);
+ if (!ar) {
+ dev_err(&pdev->dev, "failed to allocate core\n");
+ return -ENOMEM;
+ }
+
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "ahb probe\n");
+
+ ar_pci = ath10k_pci_priv(ar);
+ ar_ahb = ath10k_ahb_priv(ar);
+
+ ar_ahb->pdev = pdev;
+ platform_set_drvdata(pdev, ar);
+
+ ret = ath10k_ahb_resource_init(ar);
+ if (ret)
+ goto err_core_destroy;
+
+ ar->dev_id = 0;
+ ar_pci->mem = ar_ahb->mem;
+ ar_pci->mem_len = ar_ahb->mem_len;
+ ar_pci->ar = ar;
+ ar_pci->bus_ops = &ath10k_ahb_bus_ops;
+
+ ret = ath10k_pci_setup_resource(ar);
+ if (ret) {
+ ath10k_err(ar, "failed to setup resource: %d\n", ret);
+ goto err_resource_deinit;
+ }
+
+ ath10k_pci_init_irq_tasklets(ar);
+
+ ret = ath10k_ahb_request_irq_legacy(ar);
+ if (ret)
+ goto err_free_pipes;
+
+ ret = ath10k_ahb_prepare_device(ar);
+ if (ret)
+ goto err_free_irq;
+
+ ath10k_pci_ce_deinit(ar);
+
+ chip_id = ath10k_ahb_soc_read32(ar, SOC_CHIP_ID_ADDRESS);
+ if (chip_id == 0xffffffff) {
+ ath10k_err(ar, "failed to get chip id\n");
+ goto err_halt_device;
+ }
+
+ ret = ath10k_core_register(ar, chip_id);
+ if (ret) {
+ ath10k_err(ar, "failed to register driver core: %d\n", ret);
+ goto err_halt_device;
+ }
+
+ return 0;
+
+err_halt_device:
+ ath10k_ahb_halt_chip(ar);
+ ath10k_ahb_clock_disable(ar);
+
+err_free_irq:
+ ath10k_ahb_release_irq_legacy(ar);
+
+err_free_pipes:
+ ath10k_pci_free_pipes(ar);
+
+err_resource_deinit:
+ ath10k_ahb_resource_deinit(ar);
+
+err_core_destroy:
+ ath10k_core_destroy(ar);
+ platform_set_drvdata(pdev, NULL);
+
+ return ret;
+}
+
+static int ath10k_ahb_remove(struct platform_device *pdev)
+{
+ struct ath10k *ar = platform_get_drvdata(pdev);
+ struct ath10k_ahb *ar_ahb;
+
+ if (!ar)
+ return -EINVAL;
+
+ ar_ahb = ath10k_ahb_priv(ar);
+
+ if (!ar_ahb)
+ return -EINVAL;
+
+ ath10k_dbg(ar, ATH10K_DBG_AHB, "ahb remove\n");
+
+ ath10k_core_unregister(ar);
+ ath10k_ahb_irq_disable(ar);
+ ath10k_ahb_release_irq_legacy(ar);
+ ath10k_pci_release_resource(ar);
+ ath10k_ahb_halt_chip(ar);
+ ath10k_ahb_clock_disable(ar);
+ ath10k_ahb_resource_deinit(ar);
+ ath10k_core_destroy(ar);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver ath10k_ahb_driver = {
+ .driver = {
+ .name = "ath10k_ahb",
+ .of_match_table = ath10k_ahb_of_match,
+ },
+ .probe = ath10k_ahb_probe,
+ .remove = ath10k_ahb_remove,
+};
+
+int ath10k_ahb_init(void)
+{
+ int ret;
+
+ printk(KERN_ERR "AHB support is still work in progress\n");
+
+ ret = platform_driver_register(&ath10k_ahb_driver);
+ if (ret)
+ printk(KERN_ERR "failed to register ath10k ahb driver: %d\n",
+ ret);
+ return ret;
+}
+
+void ath10k_ahb_exit(void)
+{
+ platform_driver_unregister(&ath10k_ahb_driver);
+}
diff --git a/drivers/net/wireless/ath/ath10k/ahb.h b/drivers/net/wireless/ath/ath10k/ahb.h
new file mode 100644
index 0000000..d43e375
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/ahb.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2016 Qualcomm Atheros, Inc. All rights reserved.
+ * Copyright (c) 2015 The Linux Foundation. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _AHB_H_
+#define _AHB_H_
+
+#include <linux/platform_device.h>
+
+struct ath10k_ahb {
+ struct platform_device *pdev;
+ void __iomem *mem;
+ unsigned long mem_len;
+ void __iomem *gcc_mem;
+ void __iomem *tcsr_mem;
+
+ int irq;
+
+ struct clk *cmd_clk;
+ struct clk *ref_clk;
+ struct clk *rtc_clk;
+
+ struct reset_control *core_cold_rst;
+ struct reset_control *radio_cold_rst;
+ struct reset_control *radio_warm_rst;
+ struct reset_control *radio_srif_rst;
+ struct reset_control *cpu_init_rst;
+};
+
+#ifdef CONFIG_ATH10K_AHB
+
+#define ATH10K_GCC_REG_BASE 0x1800000
+#define ATH10K_GCC_REG_SIZE 0x60000
+
+#define ATH10K_TCSR_REG_BASE 0x1900000
+#define ATH10K_TCSR_REG_SIZE 0x80000
+
+#define ATH10K_AHB_GCC_FEPLL_PLL_DIV 0x2f020
+#define ATH10K_AHB_WIFI_SCRATCH_5_REG 0x4f014
+
+#define ATH10K_AHB_WLAN_CORE_ID_REG 0x82030
+
+#define ATH10K_AHB_TCSR_WIFI0_GLB_CFG 0x49000
+#define ATH10K_AHB_TCSR_WIFI1_GLB_CFG 0x49004
+#define TCSR_WIFIX_GLB_CFG_DISABLE_CORE_CLK BIT(25)
+
+#define ATH10K_AHB_TCSR_WCSS0_HALTREQ 0x52000
+#define ATH10K_AHB_TCSR_WCSS1_HALTREQ 0x52010
+#define ATH10K_AHB_TCSR_WCSS0_HALTACK 0x52004
+#define ATH10K_AHB_TCSR_WCSS1_HALTACK 0x52014
+
+#define ATH10K_AHB_AXI_BUS_HALT_TIMEOUT 10 /* msec */
+#define AHB_AXI_BUS_HALT_REQ 1
+#define AHB_AXI_BUS_HALT_ACK 1
+
+#define ATH10K_AHB_CORE_CTRL_CPU_INTR_MASK 1
+
+int ath10k_ahb_init(void);
+void ath10k_ahb_exit(void);
+
+#else /* CONFIG_ATH10K_AHB */
+
+static inline int ath10k_ahb_init(void)
+{
+ return 0;
+}
+
+static inline void ath10k_ahb_exit(void)
+{
+}
+
+#endif /* CONFIG_ATH10K_AHB */
+
+#endif /* _AHB_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index b41eb3f..c84c2d3 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -156,6 +156,11 @@
.channel_counters_freq_hz = 150000,
.max_probe_resp_desc_thres = 24,
.hw_4addr_pad = ATH10K_HW_4ADDR_PAD_BEFORE,
+ .num_msdu_desc = 1424,
+ .qcache_active_peers = 50,
+ .tx_chain_mask = 0xf,
+ .rx_chain_mask = 0xf,
+ .max_spatial_stream = 4,
.fw = {
.dir = QCA99X0_HW_2_0_FW_DIR,
.fw = QCA99X0_HW_2_0_FW_FILE,
@@ -201,6 +206,31 @@
.board_ext_size = QCA9377_BOARD_EXT_DATA_SZ,
},
},
+ {
+ .id = QCA4019_HW_1_0_DEV_VERSION,
+ .dev_id = 0,
+ .name = "qca4019 hw1.0",
+ .patch_load_addr = QCA4019_HW_1_0_PATCH_LOAD_ADDR,
+ .uart_pin = 7,
+ .otp_exe_param = 0x0010000,
+ .continuous_frag_desc = true,
+ .channel_counters_freq_hz = 125000,
+ .max_probe_resp_desc_thres = 24,
+ .hw_4addr_pad = ATH10K_HW_4ADDR_PAD_BEFORE,
+ .num_msdu_desc = 2500,
+ .qcache_active_peers = 35,
+ .tx_chain_mask = 0x3,
+ .rx_chain_mask = 0x3,
+ .max_spatial_stream = 2,
+ .fw = {
+ .dir = QCA4019_HW_1_0_FW_DIR,
+ .fw = QCA4019_HW_1_0_FW_FILE,
+ .otp = QCA4019_HW_1_0_OTP_FILE,
+ .board = QCA4019_HW_1_0_BOARD_DATA_FILE,
+ .board_size = QCA4019_BOARD_DATA_SZ,
+ .board_ext_size = QCA4019_BOARD_EXT_DATA_SZ,
+ },
+ },
};
static const char *const ath10k_core_fw_feature_str[] = {
@@ -217,6 +247,7 @@
[ATH10K_FW_FEATURE_RAW_MODE_SUPPORT] = "raw-mode",
[ATH10K_FW_FEATURE_SUPPORTS_ADAPTIVE_CCA] = "adaptive-cca",
[ATH10K_FW_FEATURE_MFP_SUPPORT] = "mfp",
+ [ATH10K_FW_FEATURE_PEER_FLOW_CONTROL] = "peer-flow-ctrl",
};
static unsigned int ath10k_core_get_fw_feature_str(char *buf,
@@ -1478,8 +1509,13 @@
case ATH10K_FW_WMI_OP_VERSION_10_1:
case ATH10K_FW_WMI_OP_VERSION_10_2:
case ATH10K_FW_WMI_OP_VERSION_10_2_4:
- ar->max_num_peers = TARGET_10X_NUM_PEERS;
- ar->max_num_stations = TARGET_10X_NUM_STATIONS;
+ if (test_bit(WMI_SERVICE_PEER_STATS, ar->wmi.svc_map)) {
+ ar->max_num_peers = TARGET_10X_TX_STATS_NUM_PEERS;
+ ar->max_num_stations = TARGET_10X_TX_STATS_NUM_STATIONS;
+ } else {
+ ar->max_num_peers = TARGET_10X_NUM_PEERS;
+ ar->max_num_stations = TARGET_10X_NUM_STATIONS;
+ }
ar->max_num_vdevs = TARGET_10X_NUM_VDEVS;
ar->htt.max_num_pending_tx = TARGET_10X_NUM_MSDU_DESC;
ar->fw_stats_req_mask = WMI_STAT_PEER;
@@ -1502,9 +1538,9 @@
ar->num_active_peers = TARGET_10_4_ACTIVE_PEERS;
ar->max_num_vdevs = TARGET_10_4_NUM_VDEVS;
ar->num_tids = TARGET_10_4_TGT_NUM_TIDS;
- ar->htt.max_num_pending_tx = TARGET_10_4_NUM_MSDU_DESC;
+ ar->htt.max_num_pending_tx = ar->hw_params.num_msdu_desc;
ar->fw_stats_req_mask = WMI_STAT_PEER;
- ar->max_spatial_stream = WMI_10_4_MAX_SPATIAL_STREAM;
+ ar->max_spatial_stream = ar->hw_params.max_spatial_stream;
break;
case ATH10K_FW_WMI_OP_VERSION_UNSET:
case ATH10K_FW_WMI_OP_VERSION_MAX:
@@ -1979,6 +2015,10 @@
ar->regs = &qca99x0_regs;
ar->hw_values = &qca99x0_values;
break;
+ case ATH10K_HW_QCA4019:
+ ar->regs = &qca4019_regs;
+ ar->hw_values = &qca4019_values;
+ break;
default:
ath10k_err(ar, "unsupported core hardware revision %d\n",
hw_rev);
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 7840cf3..a62b62a 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -69,6 +69,7 @@
enum ath10k_bus {
ATH10K_BUS_PCI,
+ ATH10K_BUS_AHB,
};
static inline const char *ath10k_bus_str(enum ath10k_bus bus)
@@ -76,6 +77,8 @@
switch (bus) {
case ATH10K_BUS_PCI:
return "pci";
+ case ATH10K_BUS_AHB:
+ return "ahb";
}
return "unknown";
@@ -159,6 +162,7 @@
u32 peer_rssi;
u32 peer_tx_rate;
u32 peer_rx_rate; /* 10x only */
+ u32 rx_duration;
};
struct ath10k_fw_stats_vdev {
@@ -315,6 +319,7 @@
#ifdef CONFIG_MAC80211_DEBUGFS
/* protected by conf_mutex */
bool aggr_mode;
+ u64 rx_duration;
#endif
};
@@ -510,6 +515,15 @@
/* Firmware supports management frame protection */
ATH10K_FW_FEATURE_MFP_SUPPORT = 12,
+ /* Firmware supports pull-push model where host shares it's software
+ * queue state with firmware and firmware generates fetch requests
+ * telling host which queues to dequeue tx from.
+ *
+ * Primary function of this is improved MU-MIMO performance with
+ * multiple clients.
+ */
+ ATH10K_FW_FEATURE_PEER_FLOW_CONTROL = 13,
+
/* keep last */
ATH10K_FW_FEATURE_COUNT,
};
@@ -666,6 +680,12 @@
/* The padding bytes's location is different on various chips */
enum ath10k_hw_4addr_pad hw_4addr_pad;
+ u32 num_msdu_desc;
+ u32 qcache_active_peers;
+ u32 tx_chain_mask;
+ u32 rx_chain_mask;
+ u32 max_spatial_stream;
+
struct ath10k_hw_params_fw {
const char *dir;
const char *fw;
diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
index 2bdf540..076d29b 100644
--- a/drivers/net/wireless/ath/ath10k/debug.c
+++ b/drivers/net/wireless/ath/ath10k/debug.c
@@ -276,7 +276,7 @@
.llseek = default_llseek,
};
-static void ath10k_debug_fw_stats_pdevs_free(struct list_head *head)
+static void ath10k_fw_stats_pdevs_free(struct list_head *head)
{
struct ath10k_fw_stats_pdev *i, *tmp;
@@ -286,7 +286,7 @@
}
}
-static void ath10k_debug_fw_stats_vdevs_free(struct list_head *head)
+static void ath10k_fw_stats_vdevs_free(struct list_head *head)
{
struct ath10k_fw_stats_vdev *i, *tmp;
@@ -296,7 +296,7 @@
}
}
-static void ath10k_debug_fw_stats_peers_free(struct list_head *head)
+static void ath10k_fw_stats_peers_free(struct list_head *head)
{
struct ath10k_fw_stats_peer *i, *tmp;
@@ -310,16 +310,16 @@
{
spin_lock_bh(&ar->data_lock);
ar->debug.fw_stats_done = false;
- ath10k_debug_fw_stats_pdevs_free(&ar->debug.fw_stats.pdevs);
- ath10k_debug_fw_stats_vdevs_free(&ar->debug.fw_stats.vdevs);
- ath10k_debug_fw_stats_peers_free(&ar->debug.fw_stats.peers);
+ ath10k_fw_stats_pdevs_free(&ar->debug.fw_stats.pdevs);
+ ath10k_fw_stats_vdevs_free(&ar->debug.fw_stats.vdevs);
+ ath10k_fw_stats_peers_free(&ar->debug.fw_stats.peers);
spin_unlock_bh(&ar->data_lock);
}
void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb)
{
struct ath10k_fw_stats stats = {};
- bool is_start, is_started, is_end;
+ bool is_start, is_started, is_end, peer_stats_svc;
size_t num_peers;
size_t num_vdevs;
int ret;
@@ -347,8 +347,14 @@
* delivered which is treated as end-of-data and is itself discarded
*/
+ peer_stats_svc = test_bit(WMI_SERVICE_PEER_STATS, ar->wmi.svc_map);
+ if (peer_stats_svc)
+ ath10k_sta_update_rx_duration(ar, &stats.peers);
+
if (ar->debug.fw_stats_done) {
- ath10k_warn(ar, "received unsolicited stats update event\n");
+ if (!peer_stats_svc)
+ ath10k_warn(ar, "received unsolicited stats update event\n");
+
goto free;
}
@@ -372,11 +378,13 @@
/* Although this is unlikely impose a sane limit to
* prevent firmware from DoS-ing the host.
*/
+ ath10k_fw_stats_peers_free(&ar->debug.fw_stats.peers);
ath10k_warn(ar, "dropping fw peer stats\n");
goto free;
}
if (num_vdevs >= BITS_PER_LONG) {
+ ath10k_fw_stats_vdevs_free(&ar->debug.fw_stats.vdevs);
ath10k_warn(ar, "dropping fw vdev stats\n");
goto free;
}
@@ -391,9 +399,9 @@
/* In some cases lists have been spliced and cleared. Free up
* resources if that is not the case.
*/
- ath10k_debug_fw_stats_pdevs_free(&stats.pdevs);
- ath10k_debug_fw_stats_vdevs_free(&stats.vdevs);
- ath10k_debug_fw_stats_peers_free(&stats.peers);
+ ath10k_fw_stats_pdevs_free(&stats.pdevs);
+ ath10k_fw_stats_vdevs_free(&stats.vdevs);
+ ath10k_fw_stats_peers_free(&stats.peers);
spin_unlock_bh(&ar->data_lock);
}
@@ -2106,6 +2114,7 @@
struct ath10k *ar = file->private_data;
char buf[32];
size_t buf_size;
+ int ret = 0;
bool val;
buf_size = min(count, (sizeof(buf) - 1));
@@ -2119,6 +2128,12 @@
mutex_lock(&ar->conf_mutex);
+ if (ar->state != ATH10K_STATE_ON &&
+ ar->state != ATH10K_STATE_RESTARTED) {
+ ret = -ENETDOWN;
+ goto exit;
+ }
+
if (!(test_bit(ATH10K_FLAG_BTCOEX, &ar->dev_flags) ^ val))
goto exit;
@@ -2127,17 +2142,15 @@
else
clear_bit(ATH10K_FLAG_BTCOEX, &ar->dev_flags);
- if (ar->state != ATH10K_STATE_ON)
- goto exit;
-
ath10k_info(ar, "restarting firmware due to btcoex change");
queue_work(ar->workqueue, &ar->restart_work);
+ ret = count;
exit:
mutex_unlock(&ar->conf_mutex);
- return count;
+ return ret;
}
static ssize_t ath10k_read_btcoex(struct file *file, char __user *ubuf,
@@ -2176,9 +2189,6 @@
mutex_lock(&ar->conf_mutex);
- if (len > buf_len)
- len = buf_len;
-
len += scnprintf(buf + len, buf_len - len,
"firmware-N.bin\t\t%08x\n",
crc32_le(0, ar->firmware->data, ar->firmware->size));
diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h
index 814719c..6206edd 100644
--- a/drivers/net/wireless/ath/ath10k/debug.h
+++ b/drivers/net/wireless/ath/ath10k/debug.h
@@ -37,6 +37,7 @@
ATH10K_DBG_TESTMODE = 0x00001000,
ATH10K_DBG_WMI_PRINT = 0x00002000,
ATH10K_DBG_PCI_PS = 0x00004000,
+ ATH10K_DBG_AHB = 0x00008000,
ATH10K_DBG_ANY = 0xffffffff,
};
@@ -153,6 +154,12 @@
#ifdef CONFIG_MAC80211_DEBUGFS
void ath10k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, struct dentry *dir);
+void ath10k_sta_update_rx_duration(struct ath10k *ar, struct list_head *peer);
+#else
+static inline void ath10k_sta_update_rx_duration(struct ath10k *ar,
+ struct list_head *peer)
+{
+}
#endif /* CONFIG_MAC80211_DEBUGFS */
#ifdef CONFIG_ATH10K_DEBUG
diff --git a/drivers/net/wireless/ath/ath10k/debugfs_sta.c b/drivers/net/wireless/ath/ath10k/debugfs_sta.c
index 95b5c49..67ef75b 100644
--- a/drivers/net/wireless/ath/ath10k/debugfs_sta.c
+++ b/drivers/net/wireless/ath/ath10k/debugfs_sta.c
@@ -18,6 +18,23 @@
#include "wmi-ops.h"
#include "debug.h"
+void ath10k_sta_update_rx_duration(struct ath10k *ar, struct list_head *head)
+{ struct ieee80211_sta *sta;
+ struct ath10k_fw_stats_peer *peer;
+ struct ath10k_sta *arsta;
+
+ rcu_read_lock();
+ list_for_each_entry(peer, head, list) {
+ sta = ieee80211_find_sta_by_ifaddr(ar->hw, peer->peer_macaddr,
+ NULL);
+ if (!sta)
+ continue;
+ arsta = (struct ath10k_sta *)sta->drv_priv;
+ arsta->rx_duration += (u64)peer->rx_duration;
+ }
+ rcu_read_unlock();
+}
+
static ssize_t ath10k_dbg_sta_read_aggr_mode(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
@@ -232,6 +249,28 @@
.llseek = default_llseek,
};
+static ssize_t ath10k_dbg_sta_read_rx_duration(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ieee80211_sta *sta = file->private_data;
+ struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
+ char buf[100];
+ int len = 0;
+
+ len = scnprintf(buf, sizeof(buf),
+ "%llu usecs\n", arsta->rx_duration);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static const struct file_operations fops_rx_duration = {
+ .read = ath10k_dbg_sta_read_rx_duration,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
void ath10k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, struct dentry *dir)
{
@@ -240,4 +279,6 @@
debugfs_create_file("addba", S_IWUSR, dir, sta, &fops_addba);
debugfs_create_file("addba_resp", S_IWUSR, dir, sta, &fops_addba_resp);
debugfs_create_file("delba", S_IWUSR, dir, sta, &fops_delba);
+ debugfs_create_file("rx_duration", S_IRUGO, dir, sta,
+ &fops_rx_duration);
}
diff --git a/drivers/net/wireless/ath/ath10k/htt.c b/drivers/net/wireless/ath/ath10k/htt.c
index 3e6ba63..7561f22 100644
--- a/drivers/net/wireless/ath/ath10k/htt.c
+++ b/drivers/net/wireless/ath/ath10k/htt.c
@@ -131,12 +131,12 @@
[HTT_10_4_T2H_MSG_TYPE_AGGR_CONF] = HTT_T2H_MSG_TYPE_AGGR_CONF,
[HTT_10_4_T2H_MSG_TYPE_TX_FETCH_IND] =
HTT_T2H_MSG_TYPE_TX_FETCH_IND,
- [HTT_10_4_T2H_MSG_TYPE_TX_FETCH_CONF] =
- HTT_T2H_MSG_TYPE_TX_FETCH_CONF,
+ [HTT_10_4_T2H_MSG_TYPE_TX_FETCH_CONFIRM] =
+ HTT_T2H_MSG_TYPE_TX_FETCH_CONFIRM,
[HTT_10_4_T2H_MSG_TYPE_STATS_NOUPLOAD] =
HTT_T2H_MSG_TYPE_STATS_NOUPLOAD,
- [HTT_10_4_T2H_MSG_TYPE_TX_LOW_LATENCY_IND] =
- HTT_T2H_MSG_TYPE_TX_LOW_LATENCY_IND,
+ [HTT_10_4_T2H_MSG_TYPE_TX_MODE_SWITCH_IND] =
+ HTT_T2H_MSG_TYPE_TX_MODE_SWITCH_IND,
};
int ath10k_htt_connect(struct ath10k_htt *htt)
diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h
index 47ca048..13391ea 100644
--- a/drivers/net/wireless/ath/ath10k/htt.h
+++ b/drivers/net/wireless/ath/ath10k/htt.h
@@ -52,6 +52,7 @@
/* This command is used for sending management frames in HTT < 3.0.
* HTT >= 3.0 uses TX_FRM for everything. */
HTT_H2T_MSG_TYPE_MGMT_TX = 7,
+ HTT_H2T_MSG_TYPE_TX_FETCH_RESP = 11,
HTT_H2T_NUM_MSGS /* keep this last */
};
@@ -413,10 +414,10 @@
HTT_10_4_T2H_MSG_TYPE_EN_STATS = 0x14,
HTT_10_4_T2H_MSG_TYPE_AGGR_CONF = 0x15,
HTT_10_4_T2H_MSG_TYPE_TX_FETCH_IND = 0x16,
- HTT_10_4_T2H_MSG_TYPE_TX_FETCH_CONF = 0x17,
+ HTT_10_4_T2H_MSG_TYPE_TX_FETCH_CONFIRM = 0x17,
HTT_10_4_T2H_MSG_TYPE_STATS_NOUPLOAD = 0x18,
/* 0x19 to 0x2f are reserved */
- HTT_10_4_T2H_MSG_TYPE_TX_LOW_LATENCY_IND = 0x30,
+ HTT_10_4_T2H_MSG_TYPE_TX_MODE_SWITCH_IND = 0x30,
/* keep this last */
HTT_10_4_T2H_NUM_MSGS
};
@@ -449,8 +450,8 @@
HTT_T2H_MSG_TYPE_TEST,
HTT_T2H_MSG_TYPE_EN_STATS,
HTT_T2H_MSG_TYPE_TX_FETCH_IND,
- HTT_T2H_MSG_TYPE_TX_FETCH_CONF,
- HTT_T2H_MSG_TYPE_TX_LOW_LATENCY_IND,
+ HTT_T2H_MSG_TYPE_TX_FETCH_CONFIRM,
+ HTT_T2H_MSG_TYPE_TX_MODE_SWITCH_IND,
/* keep this last */
HTT_T2H_NUM_MSGS
};
@@ -1306,9 +1307,43 @@
* so we use a conservatively safe value for now */
#define HTT_FRAG_DESC_BANK_MAX 4
-#define HTT_FRAG_DESC_BANK_CFG_INFO_PDEV_ID_MASK 0x03
-#define HTT_FRAG_DESC_BANK_CFG_INFO_PDEV_ID_LSB 0
-#define HTT_FRAG_DESC_BANK_CFG_INFO_SWAP (1 << 2)
+#define HTT_FRAG_DESC_BANK_CFG_INFO_PDEV_ID_MASK 0x03
+#define HTT_FRAG_DESC_BANK_CFG_INFO_PDEV_ID_LSB 0
+#define HTT_FRAG_DESC_BANK_CFG_INFO_SWAP BIT(2)
+#define HTT_FRAG_DESC_BANK_CFG_INFO_Q_STATE_VALID BIT(3)
+#define HTT_FRAG_DESC_BANK_CFG_INFO_Q_STATE_DEPTH_TYPE_MASK BIT(4)
+#define HTT_FRAG_DESC_BANK_CFG_INFO_Q_STATE_DEPTH_TYPE_LSB 4
+
+enum htt_q_depth_type {
+ HTT_Q_DEPTH_TYPE_BYTES = 0,
+ HTT_Q_DEPTH_TYPE_MSDUS = 1,
+};
+
+#define HTT_TX_Q_STATE_NUM_PEERS (TARGET_10_4_NUM_QCACHE_PEERS_MAX + \
+ TARGET_10_4_NUM_VDEVS)
+#define HTT_TX_Q_STATE_NUM_TIDS 8
+#define HTT_TX_Q_STATE_ENTRY_SIZE 1
+#define HTT_TX_Q_STATE_ENTRY_MULTIPLIER 0
+
+/**
+ * htt_q_state_conf - part of htt_frag_desc_bank_cfg for host q state config
+ *
+ * Defines host q state format and behavior. See htt_q_state.
+ *
+ * @record_size: Defines the size of each host q entry in bytes. In practice
+ * however firmware (at least 10.4.3-00191) ignores this host
+ * configuration value and uses hardcoded value of 1.
+ * @record_multiplier: This is valid only when q depth type is MSDUs. It
+ * defines the exponent for the power of 2 multiplication.
+ */
+struct htt_q_state_conf {
+ __le32 paddr;
+ __le16 num_peers;
+ __le16 num_tids;
+ u8 record_size;
+ u8 record_multiplier;
+ u8 pad[2];
+} __packed;
struct htt_frag_desc_bank_cfg {
u8 info; /* HTT_FRAG_DESC_BANK_CFG_INFO_ */
@@ -1316,6 +1351,114 @@
u8 desc_size;
__le32 bank_base_addrs[HTT_FRAG_DESC_BANK_MAX];
struct htt_frag_desc_bank_id bank_id[HTT_FRAG_DESC_BANK_MAX];
+ struct htt_q_state_conf q_state;
+} __packed;
+
+#define HTT_TX_Q_STATE_ENTRY_COEFFICIENT 128
+#define HTT_TX_Q_STATE_ENTRY_FACTOR_MASK 0x3f
+#define HTT_TX_Q_STATE_ENTRY_FACTOR_LSB 0
+#define HTT_TX_Q_STATE_ENTRY_EXP_MASK 0xc0
+#define HTT_TX_Q_STATE_ENTRY_EXP_LSB 6
+
+/**
+ * htt_q_state - shared between host and firmware via DMA
+ *
+ * This structure is used for the host to expose it's software queue state to
+ * firmware so that its rate control can schedule fetch requests for optimized
+ * performance. This is most notably used for MU-MIMO aggregation when multiple
+ * MU clients are connected.
+ *
+ * @count: Each element defines the host queue depth. When q depth type was
+ * configured as HTT_Q_DEPTH_TYPE_BYTES then each entry is defined as:
+ * FACTOR * 128 * 8^EXP (see HTT_TX_Q_STATE_ENTRY_FACTOR_MASK and
+ * HTT_TX_Q_STATE_ENTRY_EXP_MASK). When q depth type was configured as
+ * HTT_Q_DEPTH_TYPE_MSDUS the number of packets is scaled by 2 **
+ * record_multiplier (see htt_q_state_conf).
+ * @map: Used by firmware to quickly check which host queues are not empty. It
+ * is a bitmap simply saying.
+ * @seq: Used by firmware to quickly check if the host queues were updated
+ * since it last checked.
+ *
+ * FIXME: Is the q_state map[] size calculation really correct?
+ */
+struct htt_q_state {
+ u8 count[HTT_TX_Q_STATE_NUM_TIDS][HTT_TX_Q_STATE_NUM_PEERS];
+ u32 map[HTT_TX_Q_STATE_NUM_TIDS][(HTT_TX_Q_STATE_NUM_PEERS + 31) / 32];
+ __le32 seq;
+} __packed;
+
+#define HTT_TX_FETCH_RECORD_INFO_PEER_ID_MASK 0x0fff
+#define HTT_TX_FETCH_RECORD_INFO_PEER_ID_LSB 0
+#define HTT_TX_FETCH_RECORD_INFO_TID_MASK 0xf000
+#define HTT_TX_FETCH_RECORD_INFO_TID_LSB 12
+
+struct htt_tx_fetch_record {
+ __le16 info; /* HTT_TX_FETCH_IND_RECORD_INFO_ */
+ __le16 num_msdus;
+ __le32 num_bytes;
+} __packed;
+
+struct htt_tx_fetch_ind {
+ u8 pad0;
+ __le16 fetch_seq_num;
+ __le32 token;
+ __le16 num_resp_ids;
+ __le16 num_records;
+ struct htt_tx_fetch_record records[0];
+ __le32 resp_ids[0]; /* ath10k_htt_get_tx_fetch_ind_resp_ids() */
+} __packed;
+
+static inline void *
+ath10k_htt_get_tx_fetch_ind_resp_ids(struct htt_tx_fetch_ind *ind)
+{
+ return (void *)&ind->records[le16_to_cpu(ind->num_records)];
+}
+
+struct htt_tx_fetch_resp {
+ u8 pad0;
+ __le16 resp_id;
+ __le16 fetch_seq_num;
+ __le16 num_records;
+ __le32 token;
+ struct htt_tx_fetch_record records[0];
+} __packed;
+
+struct htt_tx_fetch_confirm {
+ u8 pad0;
+ __le16 num_resp_ids;
+ __le32 resp_ids[0];
+} __packed;
+
+enum htt_tx_mode_switch_mode {
+ HTT_TX_MODE_SWITCH_PUSH = 0,
+ HTT_TX_MODE_SWITCH_PUSH_PULL = 1,
+};
+
+#define HTT_TX_MODE_SWITCH_IND_INFO0_ENABLE BIT(0)
+#define HTT_TX_MODE_SWITCH_IND_INFO0_NUM_RECORDS_MASK 0xfffe
+#define HTT_TX_MODE_SWITCH_IND_INFO0_NUM_RECORDS_LSB 1
+
+#define HTT_TX_MODE_SWITCH_IND_INFO1_MODE_MASK 0x0003
+#define HTT_TX_MODE_SWITCH_IND_INFO1_MODE_LSB 0
+#define HTT_TX_MODE_SWITCH_IND_INFO1_THRESHOLD_MASK 0xfffc
+#define HTT_TX_MODE_SWITCH_IND_INFO1_THRESHOLD_LSB 2
+
+#define HTT_TX_MODE_SWITCH_RECORD_INFO0_PEER_ID_MASK 0x0fff
+#define HTT_TX_MODE_SWITCH_RECORD_INFO0_PEER_ID_LSB 0
+#define HTT_TX_MODE_SWITCH_RECORD_INFO0_TID_MASK 0xf000
+#define HTT_TX_MODE_SWITCH_RECORD_INFO0_TID_LSB 12
+
+struct htt_tx_mode_switch_record {
+ __le16 info0; /* HTT_TX_MODE_SWITCH_RECORD_INFO0_ */
+ __le16 num_max_msdus;
+} __packed;
+
+struct htt_tx_mode_switch_ind {
+ u8 pad0;
+ __le16 info0; /* HTT_TX_MODE_SWITCH_IND_INFO0_ */
+ __le16 info1; /* HTT_TX_MODE_SWITCH_IND_INFO1_ */
+ u8 pad1[2];
+ struct htt_tx_mode_switch_record records[0];
} __packed;
union htt_rx_pn_t {
@@ -1340,6 +1483,7 @@
struct htt_oob_sync_req oob_sync_req;
struct htt_aggr_conf aggr_conf;
struct htt_frag_desc_bank_cfg frag_desc_bank_cfg;
+ struct htt_tx_fetch_resp tx_fetch_resp;
};
} __packed;
@@ -1364,6 +1508,9 @@
struct htt_rx_pn_ind rx_pn_ind;
struct htt_rx_offload_ind rx_offload_ind;
struct htt_rx_in_ord_ind rx_in_ord_ind;
+ struct htt_tx_fetch_ind tx_fetch_ind;
+ struct htt_tx_fetch_confirm tx_fetch_confirm;
+ struct htt_tx_mode_switch_ind tx_mode_switch_ind;
};
} __packed;
@@ -1518,6 +1665,14 @@
dma_addr_t paddr;
struct ath10k_htt_txbuf *vaddr;
} txbuf;
+
+ struct {
+ struct htt_q_state *vaddr;
+ dma_addr_t paddr;
+ u16 num_peers;
+ u16 num_tids;
+ enum htt_q_depth_type type;
+ } tx_q_state;
};
#define RX_HTT_HDR_STATUS_LEN 64
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index 91afa3a..ae9b686 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -2011,9 +2011,7 @@
break;
}
case HTT_T2H_MSG_TYPE_RX_IND:
- spin_lock_bh(&htt->rx_ring.lock);
- __skb_queue_tail(&htt->rx_compl_q, skb);
- spin_unlock_bh(&htt->rx_ring.lock);
+ skb_queue_tail(&htt->rx_compl_q, skb);
tasklet_schedule(&htt->txrx_compl_task);
return;
case HTT_T2H_MSG_TYPE_PEER_MAP: {
@@ -2111,9 +2109,7 @@
break;
}
case HTT_T2H_MSG_TYPE_RX_IN_ORD_PADDR_IND: {
- spin_lock_bh(&htt->rx_ring.lock);
- __skb_queue_tail(&htt->rx_in_ord_compl_q, skb);
- spin_unlock_bh(&htt->rx_ring.lock);
+ skb_queue_tail(&htt->rx_in_ord_compl_q, skb);
tasklet_schedule(&htt->txrx_compl_task);
return;
}
@@ -2123,10 +2119,12 @@
break;
case HTT_T2H_MSG_TYPE_AGGR_CONF:
break;
- case HTT_T2H_MSG_TYPE_EN_STATS:
case HTT_T2H_MSG_TYPE_TX_FETCH_IND:
- case HTT_T2H_MSG_TYPE_TX_FETCH_CONF:
- case HTT_T2H_MSG_TYPE_TX_LOW_LATENCY_IND:
+ case HTT_T2H_MSG_TYPE_TX_FETCH_CONFIRM:
+ case HTT_T2H_MSG_TYPE_TX_MODE_SWITCH_IND:
+ /* TODO: Implement pull-push logic */
+ break;
+ case HTT_T2H_MSG_TYPE_EN_STATS:
default:
ath10k_warn(ar, "htt event (%d) not handled\n",
resp->hdr.msg_type);
@@ -2143,11 +2141,7 @@
void ath10k_htt_rx_pktlog_completion_handler(struct ath10k *ar,
struct sk_buff *skb)
{
- struct ath10k_pktlog_10_4_hdr *hdr =
- (struct ath10k_pktlog_10_4_hdr *)skb->data;
-
- trace_ath10k_htt_pktlog(ar, hdr->payload,
- sizeof(*hdr) + __le16_to_cpu(hdr->size));
+ trace_ath10k_htt_pktlog(ar, skb->data, skb->len);
dev_kfree_skb_any(skb);
}
EXPORT_SYMBOL(ath10k_htt_rx_pktlog_completion_handler);
@@ -2156,24 +2150,46 @@
{
struct ath10k_htt *htt = (struct ath10k_htt *)ptr;
struct ath10k *ar = htt->ar;
+ struct sk_buff_head tx_q;
+ struct sk_buff_head rx_q;
+ struct sk_buff_head rx_ind_q;
struct htt_resp *resp;
struct sk_buff *skb;
+ unsigned long flags;
- while ((skb = skb_dequeue(&htt->tx_compl_q))) {
+ __skb_queue_head_init(&tx_q);
+ __skb_queue_head_init(&rx_q);
+ __skb_queue_head_init(&rx_ind_q);
+
+ spin_lock_irqsave(&htt->tx_compl_q.lock, flags);
+ skb_queue_splice_init(&htt->tx_compl_q, &tx_q);
+ spin_unlock_irqrestore(&htt->tx_compl_q.lock, flags);
+
+ spin_lock_irqsave(&htt->rx_compl_q.lock, flags);
+ skb_queue_splice_init(&htt->rx_compl_q, &rx_q);
+ spin_unlock_irqrestore(&htt->rx_compl_q.lock, flags);
+
+ spin_lock_irqsave(&htt->rx_in_ord_compl_q.lock, flags);
+ skb_queue_splice_init(&htt->rx_in_ord_compl_q, &rx_ind_q);
+ spin_unlock_irqrestore(&htt->rx_in_ord_compl_q.lock, flags);
+
+ while ((skb = __skb_dequeue(&tx_q))) {
ath10k_htt_rx_frm_tx_compl(htt->ar, skb);
dev_kfree_skb_any(skb);
}
- spin_lock_bh(&htt->rx_ring.lock);
- while ((skb = __skb_dequeue(&htt->rx_compl_q))) {
+ while ((skb = __skb_dequeue(&rx_q))) {
resp = (struct htt_resp *)skb->data;
+ spin_lock_bh(&htt->rx_ring.lock);
ath10k_htt_rx_handler(htt, &resp->rx_ind);
+ spin_unlock_bh(&htt->rx_ring.lock);
dev_kfree_skb_any(skb);
}
- while ((skb = __skb_dequeue(&htt->rx_in_ord_compl_q))) {
+ while ((skb = __skb_dequeue(&rx_ind_q))) {
+ spin_lock_bh(&htt->rx_ring.lock);
ath10k_htt_rx_in_ord_ind(ar, skb);
+ spin_unlock_bh(&htt->rx_ring.lock);
dev_kfree_skb_any(skb);
}
- spin_unlock_bh(&htt->rx_ring.lock);
}
diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c
index b3adadb..95acb72 100644
--- a/drivers/net/wireless/ath/ath10k/htt_tx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_tx.c
@@ -97,6 +97,85 @@
idr_remove(&htt->pending_tx, msdu_id);
}
+static void ath10k_htt_tx_free_cont_frag_desc(struct ath10k_htt *htt)
+{
+ size_t size;
+
+ if (!htt->frag_desc.vaddr)
+ return;
+
+ size = htt->max_num_pending_tx * sizeof(struct htt_msdu_ext_desc);
+
+ dma_free_coherent(htt->ar->dev,
+ size,
+ htt->frag_desc.vaddr,
+ htt->frag_desc.paddr);
+}
+
+static int ath10k_htt_tx_alloc_cont_frag_desc(struct ath10k_htt *htt)
+{
+ struct ath10k *ar = htt->ar;
+ size_t size;
+
+ if (!ar->hw_params.continuous_frag_desc)
+ return 0;
+
+ size = htt->max_num_pending_tx * sizeof(struct htt_msdu_ext_desc);
+ htt->frag_desc.vaddr = dma_alloc_coherent(ar->dev, size,
+ &htt->frag_desc.paddr,
+ GFP_KERNEL);
+ if (!htt->frag_desc.vaddr) {
+ ath10k_err(ar, "failed to alloc fragment desc memory\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void ath10k_htt_tx_free_txq(struct ath10k_htt *htt)
+{
+ struct ath10k *ar = htt->ar;
+ size_t size;
+
+ if (!test_bit(ATH10K_FW_FEATURE_PEER_FLOW_CONTROL, ar->fw_features))
+ return;
+
+ size = sizeof(*htt->tx_q_state.vaddr);
+
+ dma_unmap_single(ar->dev, htt->tx_q_state.paddr, size, DMA_TO_DEVICE);
+ kfree(htt->tx_q_state.vaddr);
+}
+
+static int ath10k_htt_tx_alloc_txq(struct ath10k_htt *htt)
+{
+ struct ath10k *ar = htt->ar;
+ size_t size;
+ int ret;
+
+ if (!test_bit(ATH10K_FW_FEATURE_PEER_FLOW_CONTROL, ar->fw_features))
+ return 0;
+
+ htt->tx_q_state.num_peers = HTT_TX_Q_STATE_NUM_PEERS;
+ htt->tx_q_state.num_tids = HTT_TX_Q_STATE_NUM_TIDS;
+ htt->tx_q_state.type = HTT_Q_DEPTH_TYPE_BYTES;
+
+ size = sizeof(*htt->tx_q_state.vaddr);
+ htt->tx_q_state.vaddr = kzalloc(size, GFP_KERNEL);
+ if (!htt->tx_q_state.vaddr)
+ return -ENOMEM;
+
+ htt->tx_q_state.paddr = dma_map_single(ar->dev, htt->tx_q_state.vaddr,
+ size, DMA_TO_DEVICE);
+ ret = dma_mapping_error(ar->dev, htt->tx_q_state.paddr);
+ if (ret) {
+ ath10k_warn(ar, "failed to dma map tx_q_state: %d\n", ret);
+ kfree(htt->tx_q_state.vaddr);
+ return -EIO;
+ }
+
+ return 0;
+}
+
int ath10k_htt_tx_alloc(struct ath10k_htt *htt)
{
struct ath10k *ar = htt->ar;
@@ -118,29 +197,32 @@
goto free_idr_pending_tx;
}
- if (!ar->hw_params.continuous_frag_desc)
- goto skip_frag_desc_alloc;
-
- size = htt->max_num_pending_tx * sizeof(struct htt_msdu_ext_desc);
- htt->frag_desc.vaddr = dma_alloc_coherent(ar->dev, size,
- &htt->frag_desc.paddr,
- GFP_KERNEL);
- if (!htt->frag_desc.vaddr) {
- ath10k_warn(ar, "failed to alloc fragment desc memory\n");
- ret = -ENOMEM;
+ ret = ath10k_htt_tx_alloc_cont_frag_desc(htt);
+ if (ret) {
+ ath10k_err(ar, "failed to alloc cont frag desc: %d\n", ret);
goto free_txbuf;
}
-skip_frag_desc_alloc:
+ ret = ath10k_htt_tx_alloc_txq(htt);
+ if (ret) {
+ ath10k_err(ar, "failed to alloc txq: %d\n", ret);
+ goto free_frag_desc;
+ }
+
return 0;
+free_frag_desc:
+ ath10k_htt_tx_free_cont_frag_desc(htt);
+
free_txbuf:
size = htt->max_num_pending_tx *
sizeof(struct ath10k_htt_txbuf);
dma_free_coherent(htt->ar->dev, size, htt->txbuf.vaddr,
htt->txbuf.paddr);
+
free_idr_pending_tx:
idr_destroy(&htt->pending_tx);
+
return ret;
}
@@ -174,12 +256,8 @@
htt->txbuf.paddr);
}
- if (htt->frag_desc.vaddr) {
- size = htt->max_num_pending_tx *
- sizeof(struct htt_msdu_ext_desc);
- dma_free_coherent(htt->ar->dev, size, htt->frag_desc.vaddr,
- htt->frag_desc.paddr);
- }
+ ath10k_htt_tx_free_txq(htt);
+ ath10k_htt_tx_free_cont_frag_desc(htt);
}
void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb)
@@ -268,7 +346,9 @@
struct ath10k *ar = htt->ar;
struct sk_buff *skb;
struct htt_cmd *cmd;
+ struct htt_frag_desc_bank_cfg *cfg;
int ret, size;
+ u8 info;
if (!ar->hw_params.continuous_frag_desc)
return 0;
@@ -286,14 +366,30 @@
skb_put(skb, size);
cmd = (struct htt_cmd *)skb->data;
cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_FRAG_DESC_BANK_CFG;
- cmd->frag_desc_bank_cfg.info = 0;
- cmd->frag_desc_bank_cfg.num_banks = 1;
- cmd->frag_desc_bank_cfg.desc_size = sizeof(struct htt_msdu_ext_desc);
- cmd->frag_desc_bank_cfg.bank_base_addrs[0] =
- __cpu_to_le32(htt->frag_desc.paddr);
- cmd->frag_desc_bank_cfg.bank_id[0].bank_min_id = 0;
- cmd->frag_desc_bank_cfg.bank_id[0].bank_max_id =
- __cpu_to_le16(htt->max_num_pending_tx - 1);
+
+ info = 0;
+ info |= SM(htt->tx_q_state.type,
+ HTT_FRAG_DESC_BANK_CFG_INFO_Q_STATE_DEPTH_TYPE);
+
+ if (test_bit(ATH10K_FW_FEATURE_PEER_FLOW_CONTROL, ar->fw_features))
+ info |= HTT_FRAG_DESC_BANK_CFG_INFO_Q_STATE_VALID;
+
+ cfg = &cmd->frag_desc_bank_cfg;
+ cfg->info = info;
+ cfg->num_banks = 1;
+ cfg->desc_size = sizeof(struct htt_msdu_ext_desc);
+ cfg->bank_base_addrs[0] = __cpu_to_le32(htt->frag_desc.paddr);
+ cfg->bank_id[0].bank_min_id = 0;
+ cfg->bank_id[0].bank_max_id = __cpu_to_le16(htt->max_num_pending_tx -
+ 1);
+
+ cfg->q_state.paddr = cpu_to_le32(htt->tx_q_state.paddr);
+ cfg->q_state.num_peers = cpu_to_le16(htt->tx_q_state.num_peers);
+ cfg->q_state.num_tids = cpu_to_le16(htt->tx_q_state.num_tids);
+ cfg->q_state.record_size = HTT_TX_Q_STATE_ENTRY_SIZE;
+ cfg->q_state.record_multiplier = HTT_TX_Q_STATE_ENTRY_MULTIPLIER;
+
+ ath10k_dbg(ar, ATH10K_DBG_HTT, "htt frag desc bank cmd\n");
ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb);
if (ret) {
diff --git a/drivers/net/wireless/ath/ath10k/hw.c b/drivers/net/wireless/ath/ath10k/hw.c
index 7b84d08..f544d48 100644
--- a/drivers/net/wireless/ath/ath10k/hw.c
+++ b/drivers/net/wireless/ath/ath10k/hw.c
@@ -109,6 +109,38 @@
.pcie_intr_clr_address = 0x00000010,
};
+const struct ath10k_hw_regs qca4019_regs = {
+ .rtc_soc_base_address = 0x00080000,
+ .soc_core_base_address = 0x00082000,
+ .ce_wrapper_base_address = 0x0004d000,
+ .ce0_base_address = 0x0004a000,
+ .ce1_base_address = 0x0004a400,
+ .ce2_base_address = 0x0004a800,
+ .ce3_base_address = 0x0004ac00,
+ .ce4_base_address = 0x0004b000,
+ .ce5_base_address = 0x0004b400,
+ .ce6_base_address = 0x0004b800,
+ .ce7_base_address = 0x0004bc00,
+ /* qca4019 supports upto 12 copy engines. Since base address
+ * of ce8 to ce11 are not directly referred in the code,
+ * no need have them in separate members in this table.
+ * Copy Engine Address
+ * CE8 0x0004c000
+ * CE9 0x0004c400
+ * CE10 0x0004c800
+ * CE11 0x0004cc00
+ */
+ .soc_reset_control_si0_rst_mask = 0x00000001,
+ .soc_reset_control_ce_rst_mask = 0x00000100,
+ .soc_chip_id_address = 0x000000ec,
+ .fw_indicator_address = 0x0004f00c,
+ .ce_wrap_intr_sum_host_msi_lsb = 0x0000000c,
+ .ce_wrap_intr_sum_host_msi_mask = 0x00fff000,
+ .pcie_intr_fw_mask = 0x00100000,
+ .pcie_intr_ce_mask_all = 0x000fff00,
+ .pcie_intr_clr_address = 0x00000010,
+};
+
const struct ath10k_hw_values qca988x_values = {
.rtc_state_val_on = 3,
.ce_count = 8,
@@ -136,6 +168,13 @@
.ce_desc_meta_data_lsb = 4,
};
+const struct ath10k_hw_values qca4019_values = {
+ .ce_count = 12,
+ .num_target_ce_config_wlan = 10,
+ .ce_desc_meta_data_mask = 0xFFF0,
+ .ce_desc_meta_data_lsb = 4,
+};
+
void ath10k_hw_fill_survey_time(struct ath10k *ar, struct survey_info *survey,
u32 cc, u32 rcc, u32 cc_prev, u32 rcc_prev)
{
diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h
index 0678831..f0cfbc7 100644
--- a/drivers/net/wireless/ath/ath10k/hw.h
+++ b/drivers/net/wireless/ath/ath10k/hw.h
@@ -106,6 +106,14 @@
#define QCA9377_HW_1_0_BOARD_DATA_FILE "board.bin"
#define QCA9377_HW_1_0_PATCH_LOAD_ADDR 0x1234
+/* QCA4019 1.0 definitions */
+#define QCA4019_HW_1_0_DEV_VERSION 0x01000000
+#define QCA4019_HW_1_0_FW_DIR ATH10K_FW_DIR "/QCA4019/hw1.0"
+#define QCA4019_HW_1_0_FW_FILE "firmware.bin"
+#define QCA4019_HW_1_0_OTP_FILE "otp.bin"
+#define QCA4019_HW_1_0_BOARD_DATA_FILE "board.bin"
+#define QCA4019_HW_1_0_PATCH_LOAD_ADDR 0x1234
+
#define ATH10K_FW_API2_FILE "firmware-2.bin"
#define ATH10K_FW_API3_FILE "firmware-3.bin"
@@ -200,6 +208,7 @@
ATH10K_HW_QCA6174,
ATH10K_HW_QCA99X0,
ATH10K_HW_QCA9377,
+ ATH10K_HW_QCA4019,
};
struct ath10k_hw_regs {
@@ -232,6 +241,7 @@
extern const struct ath10k_hw_regs qca988x_regs;
extern const struct ath10k_hw_regs qca6174_regs;
extern const struct ath10k_hw_regs qca99x0_regs;
+extern const struct ath10k_hw_regs qca4019_regs;
struct ath10k_hw_values {
u32 rtc_state_val_on;
@@ -245,6 +255,7 @@
extern const struct ath10k_hw_values qca988x_values;
extern const struct ath10k_hw_values qca6174_values;
extern const struct ath10k_hw_values qca99x0_values;
+extern const struct ath10k_hw_values qca4019_values;
void ath10k_hw_fill_survey_time(struct ath10k *ar, struct survey_info *survey,
u32 cc, u32 rcc, u32 cc_prev, u32 rcc_prev);
@@ -253,6 +264,7 @@
#define QCA_REV_6174(ar) ((ar)->hw_rev == ATH10K_HW_QCA6174)
#define QCA_REV_99X0(ar) ((ar)->hw_rev == ATH10K_HW_QCA99X0)
#define QCA_REV_9377(ar) ((ar)->hw_rev == ATH10K_HW_QCA9377)
+#define QCA_REV_40XX(ar) ((ar)->hw_rev == ATH10K_HW_QCA4019)
/* Known pecularities:
* - raw appears in nwifi decap, raw and nwifi appear in ethernet decap
@@ -363,14 +375,19 @@
#define TARGET_10X_MAC_AGGR_DELIM 0
#define TARGET_10X_AST_SKID_LIMIT 128
#define TARGET_10X_NUM_STATIONS 128
+#define TARGET_10X_TX_STATS_NUM_STATIONS 118
#define TARGET_10X_NUM_PEERS ((TARGET_10X_NUM_STATIONS) + \
(TARGET_10X_NUM_VDEVS))
+#define TARGET_10X_TX_STATS_NUM_PEERS ((TARGET_10X_TX_STATS_NUM_STATIONS) + \
+ (TARGET_10X_NUM_VDEVS))
#define TARGET_10X_NUM_OFFLOAD_PEERS 0
#define TARGET_10X_NUM_OFFLOAD_REORDER_BUFS 0
#define TARGET_10X_NUM_PEER_KEYS 2
#define TARGET_10X_NUM_TIDS_MAX 256
#define TARGET_10X_NUM_TIDS min((TARGET_10X_NUM_TIDS_MAX), \
(TARGET_10X_NUM_PEERS) * 2)
+#define TARGET_10X_TX_STATS_NUM_TIDS min((TARGET_10X_NUM_TIDS_MAX), \
+ (TARGET_10X_TX_STATS_NUM_PEERS) * 2)
#define TARGET_10X_TX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2))
#define TARGET_10X_RX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2))
#define TARGET_10X_RX_TIMEOUT_LO_PRI 100
@@ -414,16 +431,11 @@
#define TARGET_10_4_ACTIVE_PEERS 0
#define TARGET_10_4_NUM_QCACHE_PEERS_MAX 512
-#define TARGET_10_4_QCACHE_ACTIVE_PEERS 50
#define TARGET_10_4_NUM_OFFLOAD_PEERS 0
#define TARGET_10_4_NUM_OFFLOAD_REORDER_BUFFS 0
#define TARGET_10_4_NUM_PEER_KEYS 2
#define TARGET_10_4_TGT_NUM_TIDS ((TARGET_10_4_NUM_PEERS) * 2)
#define TARGET_10_4_AST_SKID_LIMIT 32
-#define TARGET_10_4_TX_CHAIN_MASK (BIT(0) | BIT(1) | \
- BIT(2) | BIT(3))
-#define TARGET_10_4_RX_CHAIN_MASK (BIT(0) | BIT(1) | \
- BIT(2) | BIT(3))
/* 100 ms for video, best-effort, and background */
#define TARGET_10_4_RX_TIMEOUT_LO_PRI 100
@@ -449,7 +461,6 @@
#define TARGET_10_4_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK 1
#define TARGET_10_4_VOW_CONFIG 0
#define TARGET_10_4_GTK_OFFLOAD_MAX_VDEV 3
-#define TARGET_10_4_NUM_MSDU_DESC (1024 + 400)
#define TARGET_10_4_11AC_TX_MAX_FRAGS 2
#define TARGET_10_4_MAX_PEER_EXT_STATS 16
#define TARGET_10_4_SMART_ANT_CAP 0
@@ -601,6 +612,7 @@
#define FW_INDICATOR_ADDRESS ar->regs->fw_indicator_address
#define FW_IND_EVENT_PENDING 1
#define FW_IND_INITIALIZED 2
+#define FW_IND_HOST_READY 0x80000000
/* HOST_REG interrupt from firmware */
#define PCIE_INTR_FIRMWARE_MASK ar->regs->pcie_intr_fw_mask
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 368de5e..78999c9 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -1358,10 +1358,7 @@
const u8 *p2p_ie;
int ret;
- if (arvif->vdev_type != WMI_VDEV_TYPE_AP)
- return 0;
-
- if (arvif->vdev_subtype != WMI_VDEV_SUBTYPE_P2P_GO)
+ if (arvif->vif->type != NL80211_IFTYPE_AP || !arvif->vif->p2p)
return 0;
mgmt = (void *)bcn->data;
@@ -3259,8 +3256,7 @@
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
/* This is case only for P2P_GO */
- if (arvif->vdev_type != WMI_VDEV_TYPE_AP ||
- arvif->vdev_subtype != WMI_VDEV_SUBTYPE_P2P_GO)
+ if (vif->type != NL80211_IFTYPE_AP || !vif->p2p)
return;
if (unlikely(ieee80211_is_probe_resp(hdr->frame_control))) {
@@ -3988,7 +3984,7 @@
static int ath10k_start(struct ieee80211_hw *hw)
{
struct ath10k *ar = hw->priv;
- u32 burst_enable;
+ u32 param;
int ret = 0;
/*
@@ -4031,13 +4027,15 @@
goto err_power_down;
}
- ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->pmf_qos, 1);
+ param = ar->wmi.pdev_param->pmf_qos;
+ ret = ath10k_wmi_pdev_set_param(ar, param, 1);
if (ret) {
ath10k_warn(ar, "failed to enable PMF QOS: %d\n", ret);
goto err_core_stop;
}
- ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->dynamic_bw, 1);
+ param = ar->wmi.pdev_param->dynamic_bw;
+ ret = ath10k_wmi_pdev_set_param(ar, param, 1);
if (ret) {
ath10k_warn(ar, "failed to enable dynamic BW: %d\n", ret);
goto err_core_stop;
@@ -4053,8 +4051,8 @@
}
if (test_bit(WMI_SERVICE_BURST, ar->wmi.svc_map)) {
- burst_enable = ar->wmi.pdev_param->burst_enable;
- ret = ath10k_wmi_pdev_set_param(ar, burst_enable, 0);
+ param = ar->wmi.pdev_param->burst_enable;
+ ret = ath10k_wmi_pdev_set_param(ar, param, 0);
if (ret) {
ath10k_warn(ar, "failed to disable burst: %d\n", ret);
goto err_core_stop;
@@ -4072,8 +4070,8 @@
* this problem.
*/
- ret = ath10k_wmi_pdev_set_param(ar,
- ar->wmi.pdev_param->arp_ac_override, 0);
+ param = ar->wmi.pdev_param->arp_ac_override;
+ ret = ath10k_wmi_pdev_set_param(ar, param, 0);
if (ret) {
ath10k_warn(ar, "failed to set arp ac override parameter: %d\n",
ret);
@@ -4092,8 +4090,8 @@
}
}
- ret = ath10k_wmi_pdev_set_param(ar,
- ar->wmi.pdev_param->ani_enable, 1);
+ param = ar->wmi.pdev_param->ani_enable;
+ ret = ath10k_wmi_pdev_set_param(ar, param, 1);
if (ret) {
ath10k_warn(ar, "failed to enable ani by default: %d\n",
ret);
@@ -4102,6 +4100,18 @@
ar->ani_enabled = true;
+ if (test_bit(WMI_SERVICE_PEER_STATS, ar->wmi.svc_map)) {
+ param = ar->wmi.pdev_param->peer_stats_update_period;
+ ret = ath10k_wmi_pdev_set_param(ar, param,
+ PEER_DEFAULT_STATS_UPDATE_PERIOD);
+ if (ret) {
+ ath10k_warn(ar,
+ "failed to set peer stats period : %d\n",
+ ret);
+ goto err_core_stop;
+ }
+ }
+
ar->num_started_vdevs = 0;
ath10k_regd_update(ar);
@@ -4349,25 +4359,29 @@
bit, ar->free_vdev_map);
arvif->vdev_id = bit;
- arvif->vdev_subtype = WMI_VDEV_SUBTYPE_NONE;
+ arvif->vdev_subtype =
+ ath10k_wmi_get_vdev_subtype(ar, WMI_VDEV_SUBTYPE_NONE);
switch (vif->type) {
case NL80211_IFTYPE_P2P_DEVICE:
arvif->vdev_type = WMI_VDEV_TYPE_STA;
- arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_DEVICE;
+ arvif->vdev_subtype = ath10k_wmi_get_vdev_subtype
+ (ar, WMI_VDEV_SUBTYPE_P2P_DEVICE);
break;
case NL80211_IFTYPE_UNSPECIFIED:
case NL80211_IFTYPE_STATION:
arvif->vdev_type = WMI_VDEV_TYPE_STA;
if (vif->p2p)
- arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_CLIENT;
+ arvif->vdev_subtype = ath10k_wmi_get_vdev_subtype
+ (ar, WMI_VDEV_SUBTYPE_P2P_CLIENT);
break;
case NL80211_IFTYPE_ADHOC:
arvif->vdev_type = WMI_VDEV_TYPE_IBSS;
break;
case NL80211_IFTYPE_MESH_POINT:
- if (test_bit(WMI_SERVICE_MESH, ar->wmi.svc_map)) {
- arvif->vdev_subtype = WMI_VDEV_SUBTYPE_MESH;
+ if (test_bit(WMI_SERVICE_MESH_11S, ar->wmi.svc_map)) {
+ arvif->vdev_subtype = ath10k_wmi_get_vdev_subtype
+ (ar, WMI_VDEV_SUBTYPE_MESH_11S);
} else if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) {
ret = -EINVAL;
ath10k_warn(ar, "must load driver with rawmode=1 to add mesh interfaces\n");
@@ -4379,7 +4393,8 @@
arvif->vdev_type = WMI_VDEV_TYPE_AP;
if (vif->p2p)
- arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_GO;
+ arvif->vdev_subtype = ath10k_wmi_get_vdev_subtype
+ (ar, WMI_VDEV_SUBTYPE_P2P_GO);
break;
case NL80211_IFTYPE_MONITOR:
arvif->vdev_type = WMI_VDEV_TYPE_MONITOR;
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index ee925c6..b3cff1d 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -94,7 +94,6 @@
static void ath10k_pci_buffer_cleanup(struct ath10k *ar);
static int ath10k_pci_cold_reset(struct ath10k *ar);
static int ath10k_pci_safe_chip_reset(struct ath10k *ar);
-static int ath10k_pci_wait_for_target_init(struct ath10k *ar);
static int ath10k_pci_init_irq(struct ath10k *ar);
static int ath10k_pci_deinit_irq(struct ath10k *ar);
static int ath10k_pci_request_irq(struct ath10k *ar);
@@ -620,7 +619,7 @@
spin_unlock_irqrestore(&ar_pci->ps_lock, flags);
}
-void ath10k_pci_write32(struct ath10k *ar, u32 offset, u32 value)
+static void ath10k_bus_pci_write32(struct ath10k *ar, u32 offset, u32 value)
{
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
int ret;
@@ -642,7 +641,7 @@
ath10k_pci_sleep(ar);
}
-u32 ath10k_pci_read32(struct ath10k *ar, u32 offset)
+static u32 ath10k_bus_pci_read32(struct ath10k *ar, u32 offset)
{
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
u32 val;
@@ -667,6 +666,20 @@
return val;
}
+inline void ath10k_pci_write32(struct ath10k *ar, u32 offset, u32 value)
+{
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+ ar_pci->bus_ops->write32(ar, offset, value);
+}
+
+inline u32 ath10k_pci_read32(struct ath10k *ar, u32 offset)
+{
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+ return ar_pci->bus_ops->read32(ar, offset);
+}
+
u32 ath10k_pci_soc_read32(struct ath10k *ar, u32 addr)
{
return ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS + addr);
@@ -687,7 +700,7 @@
ath10k_pci_write32(ar, PCIE_LOCAL_BASE_ADDRESS + addr, val);
}
-static bool ath10k_pci_irq_pending(struct ath10k *ar)
+bool ath10k_pci_irq_pending(struct ath10k *ar)
{
u32 cause;
@@ -700,7 +713,7 @@
return false;
}
-static void ath10k_pci_disable_and_clear_legacy_irq(struct ath10k *ar)
+void ath10k_pci_disable_and_clear_legacy_irq(struct ath10k *ar)
{
/* IMPORTANT: INTR_CLR register has to be set after
* INTR_ENABLE is set to 0, otherwise interrupt can not be
@@ -716,7 +729,7 @@
PCIE_INTR_ENABLE_ADDRESS);
}
-static void ath10k_pci_enable_legacy_irq(struct ath10k *ar)
+void ath10k_pci_enable_legacy_irq(struct ath10k *ar)
{
ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS +
PCIE_INTR_ENABLE_ADDRESS,
@@ -809,7 +822,7 @@
}
}
-static void ath10k_pci_rx_post(struct ath10k *ar)
+void ath10k_pci_rx_post(struct ath10k *ar)
{
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
int i;
@@ -818,7 +831,7 @@
ath10k_pci_rx_post_pipe(&ar_pci->pipe_info[i]);
}
-static void ath10k_pci_rx_replenish_retry(unsigned long ptr)
+void ath10k_pci_rx_replenish_retry(unsigned long ptr)
{
struct ath10k *ar = (void *)ptr;
@@ -838,6 +851,7 @@
0x7ff) << 21;
break;
case ATH10K_HW_QCA99X0:
+ case ATH10K_HW_QCA4019:
val = ath10k_pci_read32(ar, PCIE_BAR_REG_ADDRESS);
break;
}
@@ -1007,8 +1021,8 @@
#define ath10k_pci_diag_read_hi(ar, dest, src, len) \
__ath10k_pci_diag_read_hi(ar, dest, HI_ITEM(src), len)
-static int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address,
- const void *data, int nbytes)
+int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address,
+ const void *data, int nbytes)
{
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
int ret = 0;
@@ -1263,8 +1277,8 @@
ath10k_pci_process_rx_cb(ce_state, ath10k_pci_htt_rx_deliver);
}
-static int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
- struct ath10k_hif_sg_item *items, int n_items)
+int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
+ struct ath10k_hif_sg_item *items, int n_items)
{
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
struct ath10k_pci_pipe *pci_pipe = &ar_pci->pipe_info[pipe_id];
@@ -1332,13 +1346,13 @@
return err;
}
-static int ath10k_pci_hif_diag_read(struct ath10k *ar, u32 address, void *buf,
- size_t buf_len)
+int ath10k_pci_hif_diag_read(struct ath10k *ar, u32 address, void *buf,
+ size_t buf_len)
{
return ath10k_pci_diag_read_mem(ar, address, buf, buf_len);
}
-static u16 ath10k_pci_hif_get_free_queue_number(struct ath10k *ar, u8 pipe)
+u16 ath10k_pci_hif_get_free_queue_number(struct ath10k *ar, u8 pipe)
{
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
@@ -1406,8 +1420,8 @@
queue_work(ar->workqueue, &ar->restart_work);
}
-static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe,
- int force)
+void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe,
+ int force)
{
ath10k_dbg(ar, ATH10K_DBG_PCI, "pci hif send complete check\n");
@@ -1432,7 +1446,7 @@
ath10k_ce_per_engine_service(ar, pipe);
}
-static void ath10k_pci_kill_tasklet(struct ath10k *ar)
+void ath10k_pci_kill_tasklet(struct ath10k *ar)
{
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
int i;
@@ -1446,8 +1460,8 @@
del_timer_sync(&ar_pci->rx_post_retry);
}
-static int ath10k_pci_hif_map_service_to_pipe(struct ath10k *ar, u16 service_id,
- u8 *ul_pipe, u8 *dl_pipe)
+int ath10k_pci_hif_map_service_to_pipe(struct ath10k *ar, u16 service_id,
+ u8 *ul_pipe, u8 *dl_pipe)
{
const struct service_to_pipe *entry;
bool ul_set = false, dl_set = false;
@@ -1491,8 +1505,8 @@
return 0;
}
-static void ath10k_pci_hif_get_default_pipe(struct ath10k *ar,
- u8 *ul_pipe, u8 *dl_pipe)
+void ath10k_pci_hif_get_default_pipe(struct ath10k *ar,
+ u8 *ul_pipe, u8 *dl_pipe)
{
ath10k_dbg(ar, ATH10K_DBG_PCI, "pci hif get default pipe\n");
@@ -1516,6 +1530,7 @@
CORE_CTRL_ADDRESS, val);
break;
case ATH10K_HW_QCA99X0:
+ case ATH10K_HW_QCA4019:
/* TODO: Find appropriate register configuration for QCA99X0
* to mask irq/MSI.
*/
@@ -1538,6 +1553,7 @@
CORE_CTRL_ADDRESS, val);
break;
case ATH10K_HW_QCA99X0:
+ case ATH10K_HW_QCA4019:
/* TODO: Find appropriate register configuration for QCA99X0
* to unmask irq/MSI.
*/
@@ -1668,7 +1684,7 @@
}
}
-static void ath10k_pci_ce_deinit(struct ath10k *ar)
+void ath10k_pci_ce_deinit(struct ath10k *ar)
{
int i;
@@ -1676,7 +1692,7 @@
ath10k_ce_deinit_pipe(ar, i);
}
-static void ath10k_pci_flush(struct ath10k *ar)
+void ath10k_pci_flush(struct ath10k *ar)
{
ath10k_pci_kill_tasklet(ar);
ath10k_pci_buffer_cleanup(ar);
@@ -1711,9 +1727,9 @@
spin_unlock_irqrestore(&ar_pci->ps_lock, flags);
}
-static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar,
- void *req, u32 req_len,
- void *resp, u32 *resp_len)
+int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar,
+ void *req, u32 req_len,
+ void *resp, u32 *resp_len)
{
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
struct ath10k_pci_pipe *pci_tx = &ar_pci->pipe_info[BMI_CE_NUM_TO_TARG];
@@ -1756,7 +1772,7 @@
DMA_FROM_DEVICE);
ret = dma_mapping_error(ar->dev, resp_paddr);
if (ret) {
- ret = EIO;
+ ret = -EIO;
goto err_req;
}
@@ -1907,7 +1923,14 @@
return 1;
}
-static int ath10k_pci_init_config(struct ath10k *ar)
+static int ath10k_bus_get_num_banks(struct ath10k *ar)
+{
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+ return ar_pci->bus_ops->get_num_banks(ar);
+}
+
+int ath10k_pci_init_config(struct ath10k *ar)
{
u32 interconnect_targ_addr;
u32 pcie_state_targ_addr = 0;
@@ -2018,7 +2041,7 @@
/* first bank is switched to IRAM */
ealloc_value |= ((HI_EARLY_ALLOC_MAGIC << HI_EARLY_ALLOC_MAGIC_SHIFT) &
HI_EARLY_ALLOC_MAGIC_MASK);
- ealloc_value |= ((ath10k_pci_get_num_banks(ar) <<
+ ealloc_value |= ((ath10k_bus_get_num_banks(ar) <<
HI_EARLY_ALLOC_IRAM_BANKS_SHIFT) &
HI_EARLY_ALLOC_IRAM_BANKS_MASK);
@@ -2071,7 +2094,7 @@
target_service_to_ce_map_wlan[15].pipenum = __cpu_to_le32(1);
}
-static int ath10k_pci_alloc_pipes(struct ath10k *ar)
+int ath10k_pci_alloc_pipes(struct ath10k *ar)
{
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
struct ath10k_pci_pipe *pipe;
@@ -2102,7 +2125,7 @@
return 0;
}
-static void ath10k_pci_free_pipes(struct ath10k *ar)
+void ath10k_pci_free_pipes(struct ath10k *ar)
{
int i;
@@ -2110,7 +2133,7 @@
ath10k_ce_free_pipe(ar, i);
}
-static int ath10k_pci_init_pipes(struct ath10k *ar)
+int ath10k_pci_init_pipes(struct ath10k *ar)
{
int i, ret;
@@ -2453,7 +2476,7 @@
return ret;
}
-static void ath10k_pci_hif_power_down(struct ath10k *ar)
+void ath10k_pci_hif_power_down(struct ath10k *ar)
{
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif power down\n");
@@ -2722,7 +2745,7 @@
free_irq(ar_pci->pdev->irq + i, ar);
}
-static void ath10k_pci_init_irq_tasklets(struct ath10k *ar)
+void ath10k_pci_init_irq_tasklets(struct ath10k *ar)
{
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
int i;
@@ -2808,7 +2831,7 @@
return 0;
}
-static int ath10k_pci_wait_for_target_init(struct ath10k *ar)
+int ath10k_pci_wait_for_target_init(struct ath10k *ar)
{
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
unsigned long timeout;
@@ -2989,6 +3012,43 @@
return false;
}
+int ath10k_pci_setup_resource(struct ath10k *ar)
+{
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+ int ret;
+
+ spin_lock_init(&ar_pci->ce_lock);
+ spin_lock_init(&ar_pci->ps_lock);
+
+ setup_timer(&ar_pci->rx_post_retry, ath10k_pci_rx_replenish_retry,
+ (unsigned long)ar);
+
+ if (QCA_REV_6174(ar))
+ ath10k_pci_override_ce_config(ar);
+
+ ret = ath10k_pci_alloc_pipes(ar);
+ if (ret) {
+ ath10k_err(ar, "failed to allocate copy engine pipes: %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+void ath10k_pci_release_resource(struct ath10k *ar)
+{
+ ath10k_pci_kill_tasklet(ar);
+ ath10k_pci_ce_deinit(ar);
+ ath10k_pci_free_pipes(ar);
+}
+
+static const struct ath10k_bus_ops ath10k_pci_bus_ops = {
+ .read32 = ath10k_bus_pci_read32,
+ .write32 = ath10k_bus_pci_write32,
+ .get_num_banks = ath10k_pci_get_num_banks,
+};
+
static int ath10k_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *pci_dev)
{
@@ -3039,40 +3099,32 @@
ar_pci->ar = ar;
ar->dev_id = pci_dev->device;
ar_pci->pci_ps = pci_ps;
+ ar_pci->bus_ops = &ath10k_pci_bus_ops;
ar->id.vendor = pdev->vendor;
ar->id.device = pdev->device;
ar->id.subsystem_vendor = pdev->subsystem_vendor;
ar->id.subsystem_device = pdev->subsystem_device;
- spin_lock_init(&ar_pci->ce_lock);
- spin_lock_init(&ar_pci->ps_lock);
-
- setup_timer(&ar_pci->rx_post_retry, ath10k_pci_rx_replenish_retry,
- (unsigned long)ar);
setup_timer(&ar_pci->ps_timer, ath10k_pci_ps_timer,
(unsigned long)ar);
+ ret = ath10k_pci_setup_resource(ar);
+ if (ret) {
+ ath10k_err(ar, "failed to setup resource: %d\n", ret);
+ goto err_core_destroy;
+ }
+
ret = ath10k_pci_claim(ar);
if (ret) {
ath10k_err(ar, "failed to claim device: %d\n", ret);
- goto err_core_destroy;
- }
-
- if (QCA_REV_6174(ar))
- ath10k_pci_override_ce_config(ar);
-
- ret = ath10k_pci_alloc_pipes(ar);
- if (ret) {
- ath10k_err(ar, "failed to allocate copy engine pipes: %d\n",
- ret);
- goto err_sleep;
+ goto err_free_pipes;
}
ret = ath10k_pci_force_wake(ar);
if (ret) {
ath10k_warn(ar, "failed to wake up device : %d\n", ret);
- goto err_free_pipes;
+ goto err_sleep;
}
ath10k_pci_ce_deinit(ar);
@@ -3081,7 +3133,7 @@
ret = ath10k_pci_init_irq(ar);
if (ret) {
ath10k_err(ar, "failed to init irqs: %d\n", ret);
- goto err_free_pipes;
+ goto err_sleep;
}
ath10k_info(ar, "pci irq %s interrupts %d irq_mode %d reset_mode %d\n",
@@ -3127,13 +3179,13 @@
err_deinit_irq:
ath10k_pci_deinit_irq(ar);
-err_free_pipes:
- ath10k_pci_free_pipes(ar);
-
err_sleep:
ath10k_pci_sleep_sync(ar);
ath10k_pci_release(ar);
+err_free_pipes:
+ ath10k_pci_free_pipes(ar);
+
err_core_destroy:
ath10k_core_destroy(ar);
@@ -3157,10 +3209,8 @@
ath10k_core_unregister(ar);
ath10k_pci_free_irq(ar);
- ath10k_pci_kill_tasklet(ar);
ath10k_pci_deinit_irq(ar);
- ath10k_pci_ce_deinit(ar);
- ath10k_pci_free_pipes(ar);
+ ath10k_pci_release_resource(ar);
ath10k_pci_sleep_sync(ar);
ath10k_pci_release(ar);
ath10k_core_destroy(ar);
@@ -3184,6 +3234,10 @@
printk(KERN_ERR "failed to register ath10k pci driver: %d\n",
ret);
+ ret = ath10k_ahb_init();
+ if (ret)
+ printk(KERN_ERR "ahb init failed: %d\n", ret);
+
return ret;
}
module_init(ath10k_pci_init);
@@ -3191,6 +3245,7 @@
static void __exit ath10k_pci_exit(void)
{
pci_unregister_driver(&ath10k_pci_driver);
+ ath10k_ahb_exit();
}
module_exit(ath10k_pci_exit);
diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h
index f91bf33..249c73a 100644
--- a/drivers/net/wireless/ath/ath10k/pci.h
+++ b/drivers/net/wireless/ath/ath10k/pci.h
@@ -22,6 +22,7 @@
#include "hw.h"
#include "ce.h"
+#include "ahb.h"
/*
* maximum number of bytes that can be handled atomically by DiagRead/DiagWrite
@@ -157,6 +158,12 @@
u32 rev_id;
};
+struct ath10k_bus_ops {
+ u32 (*read32)(struct ath10k *ar, u32 offset);
+ void (*write32)(struct ath10k *ar, u32 offset, u32 value);
+ int (*get_num_banks)(struct ath10k *ar);
+};
+
struct ath10k_pci {
struct pci_dev *pdev;
struct device *dev;
@@ -225,6 +232,14 @@
* on MMIO read/write.
*/
bool pci_ps;
+
+ const struct ath10k_bus_ops *bus_ops;
+
+ /* Keep this entry in the last, memory for struct ath10k_ahb is
+ * allocated (ahb support enabled case) in the continuation of
+ * this struct.
+ */
+ struct ath10k_ahb ahb[0];
};
static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar)
@@ -253,6 +268,40 @@
u32 ath10k_pci_soc_read32(struct ath10k *ar, u32 addr);
u32 ath10k_pci_reg_read32(struct ath10k *ar, u32 addr);
+int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
+ struct ath10k_hif_sg_item *items, int n_items);
+int ath10k_pci_hif_diag_read(struct ath10k *ar, u32 address, void *buf,
+ size_t buf_len);
+int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address,
+ const void *data, int nbytes);
+int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar, void *req, u32 req_len,
+ void *resp, u32 *resp_len);
+int ath10k_pci_hif_map_service_to_pipe(struct ath10k *ar, u16 service_id,
+ u8 *ul_pipe, u8 *dl_pipe);
+void ath10k_pci_hif_get_default_pipe(struct ath10k *ar, u8 *ul_pipe,
+ u8 *dl_pipe);
+void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe,
+ int force);
+u16 ath10k_pci_hif_get_free_queue_number(struct ath10k *ar, u8 pipe);
+void ath10k_pci_hif_power_down(struct ath10k *ar);
+int ath10k_pci_alloc_pipes(struct ath10k *ar);
+void ath10k_pci_free_pipes(struct ath10k *ar);
+void ath10k_pci_free_pipes(struct ath10k *ar);
+void ath10k_pci_rx_replenish_retry(unsigned long ptr);
+void ath10k_pci_ce_deinit(struct ath10k *ar);
+void ath10k_pci_init_irq_tasklets(struct ath10k *ar);
+void ath10k_pci_kill_tasklet(struct ath10k *ar);
+int ath10k_pci_init_pipes(struct ath10k *ar);
+int ath10k_pci_init_config(struct ath10k *ar);
+void ath10k_pci_rx_post(struct ath10k *ar);
+void ath10k_pci_flush(struct ath10k *ar);
+void ath10k_pci_enable_legacy_irq(struct ath10k *ar);
+bool ath10k_pci_irq_pending(struct ath10k *ar);
+void ath10k_pci_disable_and_clear_legacy_irq(struct ath10k *ar);
+int ath10k_pci_wait_for_target_init(struct ath10k *ar);
+int ath10k_pci_setup_resource(struct ath10k *ar);
+void ath10k_pci_release_resource(struct ath10k *ar);
+
/* QCA6174 is known to have Tx/Rx issues when SOC_WAKE register is poked too
* frequently. To avoid this put SoC to sleep after a very conservative grace
* period. Adjust with great care.
diff --git a/drivers/net/wireless/ath/ath10k/targaddrs.h b/drivers/net/wireless/ath/ath10k/targaddrs.h
index 05a421b..361f143 100644
--- a/drivers/net/wireless/ath/ath10k/targaddrs.h
+++ b/drivers/net/wireless/ath/ath10k/targaddrs.h
@@ -456,4 +456,7 @@
#define QCA99X0_BOARD_DATA_SZ 12288
#define QCA99X0_BOARD_EXT_DATA_SZ 0
+#define QCA4019_BOARD_DATA_SZ 12064
+#define QCA4019_BOARD_EXT_DATA_SZ 0
+
#endif /* __TARGADDRS_H__ */
diff --git a/drivers/net/wireless/ath/ath10k/trace.h b/drivers/net/wireless/ath/ath10k/trace.h
index 71bdb36..e0d00ce 100644
--- a/drivers/net/wireless/ath/ath10k/trace.h
+++ b/drivers/net/wireless/ath/ath10k/trace.h
@@ -250,6 +250,7 @@
TP_STRUCT__entry(
__string(device, dev_name(ar->dev))
__string(driver, dev_driver_string(ar->dev))
+ __field(u8, hw_type);
__field(size_t, buf_len)
__dynamic_array(u8, buf, buf_len)
),
@@ -257,14 +258,16 @@
TP_fast_assign(
__assign_str(device, dev_name(ar->dev));
__assign_str(driver, dev_driver_string(ar->dev));
+ __entry->hw_type = ar->hw_rev;
__entry->buf_len = buf_len;
memcpy(__get_dynamic_array(buf), buf, buf_len);
),
TP_printk(
- "%s %s len %zu",
+ "%s %s %d len %zu",
__get_str(driver),
__get_str(device),
+ __entry->hw_type,
__entry->buf_len
)
);
@@ -277,6 +280,7 @@
TP_STRUCT__entry(
__string(device, dev_name(ar->dev))
__string(driver, dev_driver_string(ar->dev))
+ __field(u8, hw_type);
__field(u16, buf_len)
__dynamic_array(u8, pktlog, buf_len)
),
@@ -284,14 +288,16 @@
TP_fast_assign(
__assign_str(device, dev_name(ar->dev));
__assign_str(driver, dev_driver_string(ar->dev));
+ __entry->hw_type = ar->hw_rev;
__entry->buf_len = buf_len;
memcpy(__get_dynamic_array(pktlog), buf, buf_len);
),
TP_printk(
- "%s %s size %hu",
+ "%s %s %d size %hu",
__get_str(driver),
__get_str(device),
+ __entry->hw_type,
__entry->buf_len
)
);
@@ -440,6 +446,7 @@
TP_STRUCT__entry(
__string(device, dev_name(ar->dev))
__string(driver, dev_driver_string(ar->dev))
+ __field(u8, hw_type);
__field(u16, len)
__dynamic_array(u8, rxdesc, len)
),
@@ -447,14 +454,16 @@
TP_fast_assign(
__assign_str(device, dev_name(ar->dev));
__assign_str(driver, dev_driver_string(ar->dev));
+ __entry->hw_type = ar->hw_rev;
__entry->len = len;
memcpy(__get_dynamic_array(rxdesc), data, len);
),
TP_printk(
- "%s %s rxdesc len %d",
+ "%s %s %d rxdesc len %d",
__get_str(driver),
__get_str(device),
+ __entry->hw_type,
__entry->len
)
);
diff --git a/drivers/net/wireless/ath/ath10k/wmi-ops.h b/drivers/net/wireless/ath/ath10k/wmi-ops.h
index 8f4f6a8..32ab34e 100644
--- a/drivers/net/wireless/ath/ath10k/wmi-ops.h
+++ b/drivers/net/wireless/ath/ath10k/wmi-ops.h
@@ -186,6 +186,8 @@
u8 enable,
u32 detect_level,
u32 detect_margin);
+ int (*get_vdev_subtype)(struct ath10k *ar,
+ enum wmi_vdev_subtype subtype);
};
int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id);
@@ -1327,4 +1329,13 @@
ar->wmi.cmd->pdev_enable_adaptive_cca_cmdid);
}
+static inline int
+ath10k_wmi_get_vdev_subtype(struct ath10k *ar, enum wmi_vdev_subtype subtype)
+{
+ if (!ar->wmi.ops->get_vdev_subtype)
+ return -EOPNOTSUPP;
+
+ return ar->wmi.ops->get_vdev_subtype(ar, subtype);
+}
+
#endif
diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c
index 3b3a27b..1085932 100644
--- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c
+++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c
@@ -3483,6 +3483,7 @@
.gen_tdls_peer_update = ath10k_wmi_tlv_op_gen_tdls_peer_update,
.gen_adaptive_qcs = ath10k_wmi_tlv_op_gen_adaptive_qcs,
.fw_stats_fill = ath10k_wmi_main_op_fw_stats_fill,
+ .get_vdev_subtype = ath10k_wmi_op_get_vdev_subtype,
};
static const struct wmi_peer_flags_map wmi_tlv_peer_flags_map = {
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index a7c3d29..7026138 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -2862,11 +2862,20 @@
/* fw doesn't implement vdev stats */
for (i = 0; i < num_peer_stats; i++) {
- const struct wmi_10_2_4_peer_stats *src;
+ const struct wmi_10_2_4_ext_peer_stats *src;
struct ath10k_fw_stats_peer *dst;
+ int stats_len;
+ bool ext_peer_stats_support;
+
+ ext_peer_stats_support = test_bit(WMI_SERVICE_PEER_STATS,
+ ar->wmi.svc_map);
+ if (ext_peer_stats_support)
+ stats_len = sizeof(struct wmi_10_2_4_ext_peer_stats);
+ else
+ stats_len = sizeof(struct wmi_10_2_4_peer_stats);
src = (void *)skb->data;
- if (!skb_pull(skb, sizeof(*src)))
+ if (!skb_pull(skb, stats_len))
return -EPROTO;
dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
@@ -2876,6 +2885,9 @@
ath10k_wmi_pull_peer_stats(&src->common.old, dst);
dst->peer_rx_rate = __le32_to_cpu(src->common.peer_rx_rate);
+
+ if (ext_peer_stats_support)
+ dst->rx_duration = __le32_to_cpu(src->rx_duration);
/* FIXME: expose 10.2 specific values */
list_add_tail(&dst->list, &stats->peers);
@@ -3184,7 +3196,7 @@
struct sk_buff *bcn,
const struct wmi_p2p_noa_info *noa)
{
- if (arvif->vdev_subtype != WMI_VDEV_SUBTYPE_P2P_GO)
+ if (!arvif->vif->p2p)
return;
ath10k_dbg(ar, ATH10K_DBG_MGMT, "noa changed: %d\n", noa->changed);
@@ -3244,6 +3256,50 @@
return 0;
}
+static int ath10k_wmi_10_2_4_op_pull_swba_ev(struct ath10k *ar,
+ struct sk_buff *skb,
+ struct wmi_swba_ev_arg *arg)
+{
+ struct wmi_10_2_4_host_swba_event *ev = (void *)skb->data;
+ u32 map;
+ size_t i;
+
+ if (skb->len < sizeof(*ev))
+ return -EPROTO;
+
+ skb_pull(skb, sizeof(*ev));
+ arg->vdev_map = ev->vdev_map;
+
+ for (i = 0, map = __le32_to_cpu(ev->vdev_map); map; map >>= 1) {
+ if (!(map & BIT(0)))
+ continue;
+
+ /* If this happens there were some changes in firmware and
+ * ath10k should update the max size of tim_info array.
+ */
+ if (WARN_ON_ONCE(i == ARRAY_SIZE(arg->tim_info)))
+ break;
+
+ if (__le32_to_cpu(ev->bcn_info[i].tim_info.tim_len) >
+ sizeof(ev->bcn_info[i].tim_info.tim_bitmap)) {
+ ath10k_warn(ar, "refusing to parse invalid swba structure\n");
+ return -EPROTO;
+ }
+
+ arg->tim_info[i].tim_len = ev->bcn_info[i].tim_info.tim_len;
+ arg->tim_info[i].tim_mcast = ev->bcn_info[i].tim_info.tim_mcast;
+ arg->tim_info[i].tim_bitmap =
+ ev->bcn_info[i].tim_info.tim_bitmap;
+ arg->tim_info[i].tim_changed =
+ ev->bcn_info[i].tim_info.tim_changed;
+ arg->tim_info[i].tim_num_ps_pending =
+ ev->bcn_info[i].tim_info.tim_num_ps_pending;
+ i++;
+ }
+
+ return 0;
+}
+
static int ath10k_wmi_10_4_op_pull_swba_ev(struct ath10k *ar,
struct sk_buff *skb,
struct wmi_swba_ev_arg *arg)
@@ -4562,9 +4618,9 @@
if (test_bit(WMI_SERVICE_PEER_CACHING, ar->wmi.svc_map)) {
ar->max_num_peers = TARGET_10_4_NUM_QCACHE_PEERS_MAX +
- TARGET_10_4_NUM_VDEVS;
- ar->num_active_peers = TARGET_10_4_QCACHE_ACTIVE_PEERS +
- TARGET_10_4_NUM_VDEVS;
+ ar->max_num_vdevs;
+ ar->num_active_peers = ar->hw_params.qcache_active_peers +
+ ar->max_num_vdevs;
ar->num_tids = ar->num_active_peers * 2;
ar->max_num_stations = TARGET_10_4_NUM_QCACHE_PEERS_MAX;
}
@@ -5460,9 +5516,15 @@
u32 len, val, features;
config.num_vdevs = __cpu_to_le32(TARGET_10X_NUM_VDEVS);
- config.num_peers = __cpu_to_le32(TARGET_10X_NUM_PEERS);
config.num_peer_keys = __cpu_to_le32(TARGET_10X_NUM_PEER_KEYS);
- config.num_tids = __cpu_to_le32(TARGET_10X_NUM_TIDS);
+ if (test_bit(WMI_SERVICE_PEER_STATS, ar->wmi.svc_map)) {
+ config.num_peers = __cpu_to_le32(TARGET_10X_TX_STATS_NUM_PEERS);
+ config.num_tids = __cpu_to_le32(TARGET_10X_TX_STATS_NUM_TIDS);
+ } else {
+ config.num_peers = __cpu_to_le32(TARGET_10X_NUM_PEERS);
+ config.num_tids = __cpu_to_le32(TARGET_10X_NUM_TIDS);
+ }
+
config.ast_skid_limit = __cpu_to_le32(TARGET_10X_AST_SKID_LIMIT);
config.tx_chain_mask = __cpu_to_le32(TARGET_10X_TX_CHAIN_MASK);
config.rx_chain_mask = __cpu_to_le32(TARGET_10X_RX_CHAIN_MASK);
@@ -5517,6 +5579,9 @@
test_bit(WMI_SERVICE_COEX_GPIO, ar->wmi.svc_map))
features |= WMI_10_2_COEX_GPIO;
+ if (test_bit(WMI_SERVICE_PEER_STATS, ar->wmi.svc_map))
+ features |= WMI_10_2_PEER_STATS;
+
cmd->resource_config.feature_mask = __cpu_to_le32(features);
memcpy(&cmd->resource_config.common, &config, sizeof(config));
@@ -5543,8 +5608,8 @@
__cpu_to_le32(TARGET_10_4_NUM_OFFLOAD_REORDER_BUFFS);
config.num_peer_keys = __cpu_to_le32(TARGET_10_4_NUM_PEER_KEYS);
config.ast_skid_limit = __cpu_to_le32(TARGET_10_4_AST_SKID_LIMIT);
- config.tx_chain_mask = __cpu_to_le32(TARGET_10_4_TX_CHAIN_MASK);
- config.rx_chain_mask = __cpu_to_le32(TARGET_10_4_RX_CHAIN_MASK);
+ config.tx_chain_mask = __cpu_to_le32(ar->hw_params.tx_chain_mask);
+ config.rx_chain_mask = __cpu_to_le32(ar->hw_params.rx_chain_mask);
config.rx_timeout_pri[0] = __cpu_to_le32(TARGET_10_4_RX_TIMEOUT_LO_PRI);
config.rx_timeout_pri[1] = __cpu_to_le32(TARGET_10_4_RX_TIMEOUT_LO_PRI);
@@ -5575,7 +5640,7 @@
config.vow_config = __cpu_to_le32(TARGET_10_4_VOW_CONFIG);
config.gtk_offload_max_vdev =
__cpu_to_le32(TARGET_10_4_GTK_OFFLOAD_MAX_VDEV);
- config.num_msdu_desc = __cpu_to_le32(TARGET_10_4_NUM_MSDU_DESC);
+ config.num_msdu_desc = __cpu_to_le32(ar->htt.max_num_pending_tx);
config.max_frag_entries = __cpu_to_le32(TARGET_10_4_11AC_TX_MAX_FRAGS);
config.max_peer_ext_stats =
__cpu_to_le32(TARGET_10_4_MAX_PEER_EXT_STATS);
@@ -7126,6 +7191,9 @@
"Peer TX rate", peer->peer_tx_rate);
len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
"Peer RX rate", peer->peer_rx_rate);
+ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+ "Peer RX duration", peer->rx_duration);
+
len += scnprintf(buf + len, buf_len - len, "\n");
*length = len;
}
@@ -7351,6 +7419,71 @@
buf[len] = 0;
}
+int ath10k_wmi_op_get_vdev_subtype(struct ath10k *ar,
+ enum wmi_vdev_subtype subtype)
+{
+ switch (subtype) {
+ case WMI_VDEV_SUBTYPE_NONE:
+ return WMI_VDEV_SUBTYPE_LEGACY_NONE;
+ case WMI_VDEV_SUBTYPE_P2P_DEVICE:
+ return WMI_VDEV_SUBTYPE_LEGACY_P2P_DEV;
+ case WMI_VDEV_SUBTYPE_P2P_CLIENT:
+ return WMI_VDEV_SUBTYPE_LEGACY_P2P_CLI;
+ case WMI_VDEV_SUBTYPE_P2P_GO:
+ return WMI_VDEV_SUBTYPE_LEGACY_P2P_GO;
+ case WMI_VDEV_SUBTYPE_PROXY_STA:
+ return WMI_VDEV_SUBTYPE_LEGACY_PROXY_STA;
+ case WMI_VDEV_SUBTYPE_MESH_11S:
+ case WMI_VDEV_SUBTYPE_MESH_NON_11S:
+ return -ENOTSUPP;
+ }
+ return -ENOTSUPP;
+}
+
+static int ath10k_wmi_10_2_4_op_get_vdev_subtype(struct ath10k *ar,
+ enum wmi_vdev_subtype subtype)
+{
+ switch (subtype) {
+ case WMI_VDEV_SUBTYPE_NONE:
+ return WMI_VDEV_SUBTYPE_10_2_4_NONE;
+ case WMI_VDEV_SUBTYPE_P2P_DEVICE:
+ return WMI_VDEV_SUBTYPE_10_2_4_P2P_DEV;
+ case WMI_VDEV_SUBTYPE_P2P_CLIENT:
+ return WMI_VDEV_SUBTYPE_10_2_4_P2P_CLI;
+ case WMI_VDEV_SUBTYPE_P2P_GO:
+ return WMI_VDEV_SUBTYPE_10_2_4_P2P_GO;
+ case WMI_VDEV_SUBTYPE_PROXY_STA:
+ return WMI_VDEV_SUBTYPE_10_2_4_PROXY_STA;
+ case WMI_VDEV_SUBTYPE_MESH_11S:
+ return WMI_VDEV_SUBTYPE_10_2_4_MESH_11S;
+ case WMI_VDEV_SUBTYPE_MESH_NON_11S:
+ return -ENOTSUPP;
+ }
+ return -ENOTSUPP;
+}
+
+static int ath10k_wmi_10_4_op_get_vdev_subtype(struct ath10k *ar,
+ enum wmi_vdev_subtype subtype)
+{
+ switch (subtype) {
+ case WMI_VDEV_SUBTYPE_NONE:
+ return WMI_VDEV_SUBTYPE_10_4_NONE;
+ case WMI_VDEV_SUBTYPE_P2P_DEVICE:
+ return WMI_VDEV_SUBTYPE_10_4_P2P_DEV;
+ case WMI_VDEV_SUBTYPE_P2P_CLIENT:
+ return WMI_VDEV_SUBTYPE_10_4_P2P_CLI;
+ case WMI_VDEV_SUBTYPE_P2P_GO:
+ return WMI_VDEV_SUBTYPE_10_4_P2P_GO;
+ case WMI_VDEV_SUBTYPE_PROXY_STA:
+ return WMI_VDEV_SUBTYPE_10_4_PROXY_STA;
+ case WMI_VDEV_SUBTYPE_MESH_11S:
+ return WMI_VDEV_SUBTYPE_10_4_MESH_11S;
+ case WMI_VDEV_SUBTYPE_MESH_NON_11S:
+ return WMI_VDEV_SUBTYPE_10_4_MESH_NON_11S;
+ }
+ return -ENOTSUPP;
+}
+
static const struct wmi_ops wmi_ops = {
.rx = ath10k_wmi_op_rx,
.map_svc = wmi_main_svc_map,
@@ -7410,6 +7543,7 @@
.gen_addba_set_resp = ath10k_wmi_op_gen_addba_set_resp,
.gen_delba_send = ath10k_wmi_op_gen_delba_send,
.fw_stats_fill = ath10k_wmi_main_op_fw_stats_fill,
+ .get_vdev_subtype = ath10k_wmi_op_get_vdev_subtype,
/* .gen_bcn_tmpl not implemented */
/* .gen_prb_tmpl not implemented */
/* .gen_p2p_go_bcn_ie not implemented */
@@ -7477,6 +7611,7 @@
.gen_addba_set_resp = ath10k_wmi_op_gen_addba_set_resp,
.gen_delba_send = ath10k_wmi_op_gen_delba_send,
.fw_stats_fill = ath10k_wmi_10x_op_fw_stats_fill,
+ .get_vdev_subtype = ath10k_wmi_op_get_vdev_subtype,
/* .gen_bcn_tmpl not implemented */
/* .gen_prb_tmpl not implemented */
/* .gen_p2p_go_bcn_ie not implemented */
@@ -7545,6 +7680,7 @@
.gen_addba_set_resp = ath10k_wmi_op_gen_addba_set_resp,
.gen_delba_send = ath10k_wmi_op_gen_delba_send,
.fw_stats_fill = ath10k_wmi_10x_op_fw_stats_fill,
+ .get_vdev_subtype = ath10k_wmi_op_get_vdev_subtype,
/* .gen_pdev_enable_adaptive_cca not implemented */
};
@@ -7566,7 +7702,7 @@
.pull_ch_info = ath10k_wmi_op_pull_ch_info_ev,
.pull_vdev_start = ath10k_wmi_op_pull_vdev_start_ev,
.pull_peer_kick = ath10k_wmi_op_pull_peer_kick_ev,
- .pull_swba = ath10k_wmi_op_pull_swba_ev,
+ .pull_swba = ath10k_wmi_10_2_4_op_pull_swba_ev,
.pull_phyerr_hdr = ath10k_wmi_op_pull_phyerr_ev_hdr,
.pull_phyerr = ath10k_wmi_op_pull_phyerr_ev,
.pull_rdy = ath10k_wmi_op_pull_rdy_ev,
@@ -7611,6 +7747,7 @@
.fw_stats_fill = ath10k_wmi_10x_op_fw_stats_fill,
.gen_pdev_enable_adaptive_cca =
ath10k_wmi_op_gen_pdev_enable_adaptive_cca,
+ .get_vdev_subtype = ath10k_wmi_10_2_4_op_get_vdev_subtype,
/* .gen_bcn_tmpl not implemented */
/* .gen_prb_tmpl not implemented */
/* .gen_p2p_go_bcn_ie not implemented */
@@ -7677,6 +7814,7 @@
/* shared with 10.2 */
.gen_request_stats = ath10k_wmi_op_gen_request_stats,
.gen_pdev_get_temperature = ath10k_wmi_10_2_op_gen_pdev_get_temperature,
+ .get_vdev_subtype = ath10k_wmi_10_4_op_get_vdev_subtype,
};
int ath10k_wmi_attach(struct ath10k *ar)
diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h
index d85ad78..4d3cbc4 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.h
+++ b/drivers/net/wireless/ath/ath10k/wmi.h
@@ -176,7 +176,10 @@
WMI_SERVICE_AUX_CHAN_LOAD_INTF,
WMI_SERVICE_BSS_CHANNEL_INFO_64,
WMI_SERVICE_EXT_RES_CFG_SUPPORT,
- WMI_SERVICE_MESH,
+ WMI_SERVICE_MESH_11S,
+ WMI_SERVICE_MESH_NON_11S,
+ WMI_SERVICE_PEER_STATS,
+ WMI_SERVICE_RESTRT_CHNL_SUPPORT,
/* keep last */
WMI_SERVICE_MAX,
@@ -213,6 +216,7 @@
WMI_10X_SERVICE_BSS_CHANNEL_INFO_64,
WMI_10X_SERVICE_MESH,
WMI_10X_SERVICE_EXT_RES_CFG_SUPPORT,
+ WMI_10X_SERVICE_PEER_STATS,
};
enum wmi_main_service {
@@ -294,7 +298,10 @@
WMI_10_4_SERVICE_AUX_CHAN_LOAD_INTF,
WMI_10_4_SERVICE_BSS_CHANNEL_INFO_64,
WMI_10_4_SERVICE_EXT_RES_CFG_SUPPORT,
- WMI_10_4_SERVICE_MESH,
+ WMI_10_4_SERVICE_MESH_NON_11S,
+ WMI_10_4_SERVICE_RESTRT_CHNL_SUPPORT,
+ WMI_10_4_SERVICE_PEER_STATS,
+ WMI_10_4_SERVICE_MESH_11S,
};
static inline char *wmi_service_name(int service_id)
@@ -385,7 +392,10 @@
SVCSTR(WMI_SERVICE_AUX_CHAN_LOAD_INTF);
SVCSTR(WMI_SERVICE_BSS_CHANNEL_INFO_64);
SVCSTR(WMI_SERVICE_EXT_RES_CFG_SUPPORT);
- SVCSTR(WMI_SERVICE_MESH);
+ SVCSTR(WMI_SERVICE_MESH_11S);
+ SVCSTR(WMI_SERVICE_MESH_NON_11S);
+ SVCSTR(WMI_SERVICE_PEER_STATS);
+ SVCSTR(WMI_SERVICE_RESTRT_CHNL_SUPPORT);
default:
return NULL;
}
@@ -460,9 +470,11 @@
SVCMAP(WMI_10X_SERVICE_BSS_CHANNEL_INFO_64,
WMI_SERVICE_BSS_CHANNEL_INFO_64, len);
SVCMAP(WMI_10X_SERVICE_MESH,
- WMI_SERVICE_MESH, len);
+ WMI_SERVICE_MESH_11S, len);
SVCMAP(WMI_10X_SERVICE_EXT_RES_CFG_SUPPORT,
WMI_SERVICE_EXT_RES_CFG_SUPPORT, len);
+ SVCMAP(WMI_10X_SERVICE_PEER_STATS,
+ WMI_SERVICE_PEER_STATS, len);
}
static inline void wmi_main_svc_map(const __le32 *in, unsigned long *out,
@@ -623,8 +635,14 @@
WMI_SERVICE_BSS_CHANNEL_INFO_64, len);
SVCMAP(WMI_10_4_SERVICE_EXT_RES_CFG_SUPPORT,
WMI_SERVICE_EXT_RES_CFG_SUPPORT, len);
- SVCMAP(WMI_10_4_SERVICE_MESH,
- WMI_SERVICE_MESH, len);
+ SVCMAP(WMI_10_4_SERVICE_MESH_NON_11S,
+ WMI_SERVICE_MESH_NON_11S, len);
+ SVCMAP(WMI_10_4_SERVICE_RESTRT_CHNL_SUPPORT,
+ WMI_SERVICE_RESTRT_CHNL_SUPPORT, len);
+ SVCMAP(WMI_10_4_SERVICE_PEER_STATS,
+ WMI_SERVICE_PEER_STATS, len);
+ SVCMAP(WMI_10_4_SERVICE_MESH_11S,
+ WMI_SERVICE_MESH_11S, len);
}
#undef SVCMAP
@@ -1800,7 +1818,6 @@
#define WMI_CHANNEL_CHANGE_CAUSE_CSA (1 << 13)
#define WMI_MAX_SPATIAL_STREAM 3 /* default max ss */
-#define WMI_10_4_MAX_SPATIAL_STREAM 4
/* HT Capabilities*/
#define WMI_HT_CAP_ENABLED 0x0001 /* HT Enabled/ disabled */
@@ -2417,6 +2434,7 @@
WMI_10_2_RX_BATCH_MODE = BIT(0),
WMI_10_2_ATF_CONFIG = BIT(1),
WMI_10_2_COEX_GPIO = BIT(3),
+ WMI_10_2_PEER_STATS = BIT(7),
};
struct wmi_resource_config_10_2 {
@@ -4227,7 +4245,13 @@
struct wmi_10_2_4_peer_stats {
struct wmi_10_2_peer_stats common;
- __le32 unknown_value; /* FIXME: what is this word? */
+ __le32 peer_rssi_changed;
+} __packed;
+
+struct wmi_10_2_4_ext_peer_stats {
+ struct wmi_10_2_peer_stats common;
+ __le32 peer_rssi_changed;
+ __le32 rx_duration;
} __packed;
struct wmi_10_4_peer_stats {
@@ -4270,12 +4294,40 @@
};
enum wmi_vdev_subtype {
- WMI_VDEV_SUBTYPE_NONE = 0,
- WMI_VDEV_SUBTYPE_P2P_DEVICE = 1,
- WMI_VDEV_SUBTYPE_P2P_CLIENT = 2,
- WMI_VDEV_SUBTYPE_P2P_GO = 3,
- WMI_VDEV_SUBTYPE_PROXY_STA = 4,
- WMI_VDEV_SUBTYPE_MESH = 5,
+ WMI_VDEV_SUBTYPE_NONE,
+ WMI_VDEV_SUBTYPE_P2P_DEVICE,
+ WMI_VDEV_SUBTYPE_P2P_CLIENT,
+ WMI_VDEV_SUBTYPE_P2P_GO,
+ WMI_VDEV_SUBTYPE_PROXY_STA,
+ WMI_VDEV_SUBTYPE_MESH_11S,
+ WMI_VDEV_SUBTYPE_MESH_NON_11S,
+};
+
+enum wmi_vdev_subtype_legacy {
+ WMI_VDEV_SUBTYPE_LEGACY_NONE = 0,
+ WMI_VDEV_SUBTYPE_LEGACY_P2P_DEV = 1,
+ WMI_VDEV_SUBTYPE_LEGACY_P2P_CLI = 2,
+ WMI_VDEV_SUBTYPE_LEGACY_P2P_GO = 3,
+ WMI_VDEV_SUBTYPE_LEGACY_PROXY_STA = 4,
+};
+
+enum wmi_vdev_subtype_10_2_4 {
+ WMI_VDEV_SUBTYPE_10_2_4_NONE = 0,
+ WMI_VDEV_SUBTYPE_10_2_4_P2P_DEV = 1,
+ WMI_VDEV_SUBTYPE_10_2_4_P2P_CLI = 2,
+ WMI_VDEV_SUBTYPE_10_2_4_P2P_GO = 3,
+ WMI_VDEV_SUBTYPE_10_2_4_PROXY_STA = 4,
+ WMI_VDEV_SUBTYPE_10_2_4_MESH_11S = 5,
+};
+
+enum wmi_vdev_subtype_10_4 {
+ WMI_VDEV_SUBTYPE_10_4_NONE = 0,
+ WMI_VDEV_SUBTYPE_10_4_P2P_DEV = 1,
+ WMI_VDEV_SUBTYPE_10_4_P2P_CLI = 2,
+ WMI_VDEV_SUBTYPE_10_4_P2P_GO = 3,
+ WMI_VDEV_SUBTYPE_10_4_PROXY_STA = 4,
+ WMI_VDEV_SUBTYPE_10_4_MESH_NON_11S = 5,
+ WMI_VDEV_SUBTYPE_10_4_MESH_11S = 6,
};
/* values for vdev_subtype */
@@ -5442,6 +5494,16 @@
struct wmi_bcn_info bcn_info[0];
} __packed;
+struct wmi_10_2_4_bcn_info {
+ struct wmi_tim_info tim_info;
+ /* The 10.2.4 FW doesn't have p2p NOA info */
+} __packed;
+
+struct wmi_10_2_4_host_swba_event {
+ __le32 vdev_map;
+ struct wmi_10_2_4_bcn_info bcn_info[0];
+} __packed;
+
/* 16 words = 512 client + 1 word = for guard */
#define WMI_10_4_TIM_BITMAP_ARRAY_SIZE 17
@@ -6436,5 +6498,7 @@
void ath10k_wmi_10_4_op_fw_stats_fill(struct ath10k *ar,
struct ath10k_fw_stats *fw_stats,
char *buf);
+int ath10k_wmi_op_get_vdev_subtype(struct ath10k *ar,
+ enum wmi_vdev_subtype subtype);
#endif /* _WMI_H_ */
diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c
index 25e45e4..815efe9 100644
--- a/drivers/net/wireless/ath/ath9k/ani.c
+++ b/drivers/net/wireless/ath/ath9k/ani.c
@@ -126,12 +126,8 @@
static void ath9k_ani_restart(struct ath_hw *ah)
{
- struct ar5416AniState *aniState;
+ struct ar5416AniState *aniState = &ah->ani;
- if (!ah->curchan)
- return;
-
- aniState = &ah->ani;
aniState->listenTime = 0;
ENABLE_REGWRITE_BUFFER(ah);
@@ -221,12 +217,7 @@
static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hw *ah)
{
- struct ar5416AniState *aniState;
-
- if (!ah->curchan)
- return;
-
- aniState = &ah->ani;
+ struct ar5416AniState *aniState = &ah->ani;
if (aniState->ofdmNoiseImmunityLevel < ATH9K_ANI_OFDM_MAX_LEVEL)
ath9k_hw_set_ofdm_nil(ah, aniState->ofdmNoiseImmunityLevel + 1, false);
@@ -281,12 +272,7 @@
static void ath9k_hw_ani_cck_err_trigger(struct ath_hw *ah)
{
- struct ar5416AniState *aniState;
-
- if (!ah->curchan)
- return;
-
- aniState = &ah->ani;
+ struct ar5416AniState *aniState = &ah->ani;
if (aniState->cckNoiseImmunityLevel < ATH9K_ANI_CCK_MAX_LEVEL)
ath9k_hw_set_cck_nil(ah, aniState->cckNoiseImmunityLevel + 1,
@@ -299,9 +285,7 @@
*/
static void ath9k_hw_ani_lower_immunity(struct ath_hw *ah)
{
- struct ar5416AniState *aniState;
-
- aniState = &ah->ani;
+ struct ar5416AniState *aniState = &ah->ani;
/* lower OFDM noise immunity */
if (aniState->ofdmNoiseImmunityLevel > 0 &&
@@ -329,7 +313,7 @@
struct ath_common *common = ath9k_hw_common(ah);
int ofdm_nil, cck_nil;
- if (!ah->curchan)
+ if (!chan)
return;
BUG_ON(aniState == NULL);
@@ -416,14 +400,10 @@
void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan)
{
- struct ar5416AniState *aniState;
+ struct ar5416AniState *aniState = &ah->ani;
struct ath_common *common = ath9k_hw_common(ah);
u32 ofdmPhyErrRate, cckPhyErrRate;
- if (!ah->curchan)
- return;
-
- aniState = &ah->ani;
if (!ath9k_hw_ani_read_counters(ah))
return;
@@ -450,7 +430,9 @@
} else if (cckPhyErrRate > ah->config.cck_trig_high) {
ath9k_hw_ani_cck_err_trigger(ah);
aniState->ofdmsTurn = true;
- }
+ } else
+ return;
+
ath9k_ani_restart(ah);
}
}
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_aic.c b/drivers/net/wireless/ath/ath9k/ar9003_aic.c
index 1db119d..547cd46 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_aic.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_aic.c
@@ -53,19 +53,19 @@
return true;
}
-static int16_t ar9003_aic_find_valid(struct ath_aic_sram_info *cal_sram,
+static int16_t ar9003_aic_find_valid(bool *cal_sram_valid,
bool dir, u8 index)
{
int16_t i;
if (dir) {
for (i = index + 1; i < ATH_AIC_MAX_BT_CHANNEL; i++) {
- if (cal_sram[i].valid)
+ if (cal_sram_valid[i])
break;
}
} else {
for (i = index - 1; i >= 0; i--) {
- if (cal_sram[i].valid)
+ if (cal_sram_valid[i])
break;
}
}
@@ -264,7 +264,7 @@
static bool ar9003_aic_cal_post_process(struct ath_hw *ah)
{
struct ath9k_hw_aic *aic = &ah->btcoex_hw.aic;
- struct ath_aic_sram_info cal_sram[ATH_AIC_MAX_BT_CHANNEL];
+ bool cal_sram_valid[ATH_AIC_MAX_BT_CHANNEL];
struct ath_aic_out_info aic_sram[ATH_AIC_MAX_BT_CHANNEL];
u32 dir_path_gain_idx, quad_path_gain_idx, value;
u32 fixed_com_att_db;
@@ -272,33 +272,34 @@
int16_t i;
bool ret = true;
- memset(&cal_sram, 0, sizeof(cal_sram));
+ memset(&cal_sram_valid, 0, sizeof(cal_sram_valid));
memset(&aic_sram, 0, sizeof(aic_sram));
for (i = 0; i < ATH_AIC_MAX_BT_CHANNEL; i++) {
+ struct ath_aic_sram_info sram;
value = aic->aic_sram[i];
- cal_sram[i].valid =
+ cal_sram_valid[i] = sram.valid =
MS(value, AR_PHY_AIC_SRAM_VALID);
- cal_sram[i].rot_quad_att_db =
+ sram.rot_quad_att_db =
MS(value, AR_PHY_AIC_SRAM_ROT_QUAD_ATT_DB);
- cal_sram[i].vga_quad_sign =
+ sram.vga_quad_sign =
MS(value, AR_PHY_AIC_SRAM_VGA_QUAD_SIGN);
- cal_sram[i].rot_dir_att_db =
+ sram.rot_dir_att_db =
MS(value, AR_PHY_AIC_SRAM_ROT_DIR_ATT_DB);
- cal_sram[i].vga_dir_sign =
+ sram.vga_dir_sign =
MS(value, AR_PHY_AIC_SRAM_VGA_DIR_SIGN);
- cal_sram[i].com_att_6db =
+ sram.com_att_6db =
MS(value, AR_PHY_AIC_SRAM_COM_ATT_6DB);
- if (cal_sram[i].valid) {
- dir_path_gain_idx = cal_sram[i].rot_dir_att_db +
- com_att_db_table[cal_sram[i].com_att_6db];
- quad_path_gain_idx = cal_sram[i].rot_quad_att_db +
- com_att_db_table[cal_sram[i].com_att_6db];
+ if (sram.valid) {
+ dir_path_gain_idx = sram.rot_dir_att_db +
+ com_att_db_table[sram.com_att_6db];
+ quad_path_gain_idx = sram.rot_quad_att_db +
+ com_att_db_table[sram.com_att_6db];
- dir_path_sign = (cal_sram[i].vga_dir_sign) ? 1 : -1;
- quad_path_sign = (cal_sram[i].vga_quad_sign) ? 1 : -1;
+ dir_path_sign = (sram.vga_dir_sign) ? 1 : -1;
+ quad_path_sign = (sram.vga_quad_sign) ? 1 : -1;
aic_sram[i].dir_path_gain_lin = dir_path_sign *
aic_lin_table[dir_path_gain_idx];
@@ -310,16 +311,16 @@
for (i = 0; i < ATH_AIC_MAX_BT_CHANNEL; i++) {
int16_t start_idx, end_idx;
- if (cal_sram[i].valid)
+ if (cal_sram_valid[i])
continue;
- start_idx = ar9003_aic_find_valid(cal_sram, 0, i);
- end_idx = ar9003_aic_find_valid(cal_sram, 1, i);
+ start_idx = ar9003_aic_find_valid(cal_sram_valid, 0, i);
+ end_idx = ar9003_aic_find_valid(cal_sram_valid, 1, i);
if (start_idx < 0) {
/* extrapolation */
start_idx = end_idx;
- end_idx = ar9003_aic_find_valid(cal_sram, 1, start_idx);
+ end_idx = ar9003_aic_find_valid(cal_sram_valid, 1, start_idx);
if (end_idx < 0) {
ret = false;
@@ -342,7 +343,7 @@
if (end_idx < 0) {
/* extrapolation */
- end_idx = ar9003_aic_find_valid(cal_sram, 0, start_idx);
+ end_idx = ar9003_aic_find_valid(cal_sram_valid, 0, start_idx);
if (end_idx < 0) {
ret = false;
@@ -378,19 +379,21 @@
}
/* From dir/quad_path_gain_lin to sram. */
- i = ar9003_aic_find_valid(cal_sram, 1, 0);
+ i = ar9003_aic_find_valid(cal_sram_valid, 1, 0);
if (i < 0) {
i = 0;
ret = false;
}
- fixed_com_att_db = com_att_db_table[cal_sram[i].com_att_6db];
+ fixed_com_att_db = com_att_db_table[MS(aic->aic_sram[i],
+ AR_PHY_AIC_SRAM_COM_ATT_6DB)];
for (i = 0; i < ATH_AIC_MAX_BT_CHANNEL; i++) {
int16_t rot_dir_path_att_db, rot_quad_path_att_db;
+ struct ath_aic_sram_info sram;
- aic_sram[i].sram.vga_dir_sign =
+ sram.vga_dir_sign =
(aic_sram[i].dir_path_gain_lin >= 0) ? 1 : 0;
- aic_sram[i].sram.vga_quad_sign=
+ sram.vga_quad_sign =
(aic_sram[i].quad_path_gain_lin >= 0) ? 1 : 0;
rot_dir_path_att_db =
@@ -400,33 +403,31 @@
ar9003_aic_find_index(0, abs(aic_sram[i].quad_path_gain_lin)) -
fixed_com_att_db;
- aic_sram[i].sram.com_att_6db =
+ sram.com_att_6db =
ar9003_aic_find_index(1, fixed_com_att_db);
- aic_sram[i].sram.valid = 1;
+ sram.valid = 1;
- aic_sram[i].sram.rot_dir_att_db =
+ sram.rot_dir_att_db =
min(max(rot_dir_path_att_db,
(int16_t)ATH_AIC_MIN_ROT_DIR_ATT_DB),
ATH_AIC_MAX_ROT_DIR_ATT_DB);
- aic_sram[i].sram.rot_quad_att_db =
+ sram.rot_quad_att_db =
min(max(rot_quad_path_att_db,
(int16_t)ATH_AIC_MIN_ROT_QUAD_ATT_DB),
ATH_AIC_MAX_ROT_QUAD_ATT_DB);
- }
- for (i = 0; i < ATH_AIC_MAX_BT_CHANNEL; i++) {
- aic->aic_sram[i] = (SM(aic_sram[i].sram.vga_dir_sign,
+ aic->aic_sram[i] = (SM(sram.vga_dir_sign,
AR_PHY_AIC_SRAM_VGA_DIR_SIGN) |
- SM(aic_sram[i].sram.vga_quad_sign,
+ SM(sram.vga_quad_sign,
AR_PHY_AIC_SRAM_VGA_QUAD_SIGN) |
- SM(aic_sram[i].sram.com_att_6db,
+ SM(sram.com_att_6db,
AR_PHY_AIC_SRAM_COM_ATT_6DB) |
- SM(aic_sram[i].sram.valid,
+ SM(sram.valid,
AR_PHY_AIC_SRAM_VALID) |
- SM(aic_sram[i].sram.rot_dir_att_db,
+ SM(sram.rot_dir_att_db,
AR_PHY_AIC_SRAM_ROT_DIR_ATT_DB) |
- SM(aic_sram[i].sram.rot_quad_att_db,
+ SM(sram.rot_quad_att_db,
AR_PHY_AIC_SRAM_ROT_QUAD_ATT_DB));
}
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_aic.h b/drivers/net/wireless/ath/ath9k/ar9003_aic.h
index 86f4064..9512c63 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_aic.h
+++ b/drivers/net/wireless/ath/ath9k/ar9003_aic.h
@@ -50,7 +50,6 @@
struct ath_aic_out_info {
int16_t dir_path_gain_lin;
int16_t quad_path_gain_lin;
- struct ath_aic_sram_info sram;
};
u8 ar9003_aic_calibration(struct ath_hw *ah);
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
index 8b4561e..54ed2f7 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
@@ -5485,11 +5485,11 @@
AR9300_PAPRD_SCALE_1);
else {
if (chan->channel >= 5700)
- return MS(le32_to_cpu(eep->modalHeader5G.papdRateMaskHt20),
- AR9300_PAPRD_SCALE_1);
+ return MS(le32_to_cpu(eep->modalHeader5G.papdRateMaskHt20),
+ AR9300_PAPRD_SCALE_1);
else if (chan->channel >= 5400)
return MS(le32_to_cpu(eep->modalHeader5G.papdRateMaskHt40),
- AR9300_PAPRD_SCALE_2);
+ AR9300_PAPRD_SCALE_2);
else
return MS(le32_to_cpu(eep->modalHeader5G.papdRateMaskHt40),
AR9300_PAPRD_SCALE_1);
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c
index 8b238c1..2fe12b0 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c
@@ -698,6 +698,9 @@
else if (AR_SREV_9340(ah))
INIT_INI_ARRAY(&ah->iniModesTxGain,
ar9340Modes_low_ob_db_tx_gain_table_1p0);
+ else if (AR_SREV_9531_11(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ qca953x_1p1_modes_no_xpa_low_power_tx_gain_table);
else if (AR_SREV_9485_11_OR_LATER(ah))
INIT_INI_ARRAY(&ah->iniModesTxGain,
ar9485Modes_low_ob_db_tx_gain_1_1);
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
index 201425e..06c1ca6 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
@@ -976,9 +976,14 @@
/*
* JAPAN regulatory.
*/
- if (chan->channel == 2484)
+ if (chan->channel == 2484) {
ar9003_hw_prog_ini(ah, &ah->iniCckfirJapan2484, 1);
+ if (AR_SREV_9531(ah))
+ REG_RMW_FIELD(ah, AR_PHY_FCAL_2_0,
+ AR_PHY_FLC_PWR_THRESH, 0);
+ }
+
ah->modes_index = modesIndex;
ar9003_hw_override_ini(ah);
ar9003_hw_set_channel_regs(ah, chan);
@@ -2071,7 +2076,8 @@
* to be disabled.
*
* 0x04000409: Packet stuck on receive.
- * Full chip reset is required for all chips except AR9340.
+ * Full chip reset is required for all chips except
+ * AR9340, AR9531 and AR9561.
*/
/*
@@ -2100,7 +2106,7 @@
case 0x04000b09:
return true;
case 0x04000409:
- if (AR_SREV_9340(ah) || AR_SREV_9531(ah))
+ if (AR_SREV_9340(ah) || AR_SREV_9531(ah) || AR_SREV_9561(ah))
return false;
else
return true;
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.h b/drivers/net/wireless/ath/ath9k/ar9003_phy.h
index c5f8bc4..566da78 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.h
+++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.h
@@ -487,6 +487,9 @@
#define AR_PHY_ADDAC_PARA_CTL (AR_SM_BASE + 0x150)
#define AR_PHY_XPA_CFG (AR_SM_BASE + 0x158)
+#define AR_PHY_FLC_PWR_THRESH 7
+#define AR_PHY_FLC_PWR_THRESH_S 0
+
#define AR_PHY_FRAME_CTL_CF_OVERLAP_WINDOW 3
#define AR_PHY_FRAME_CTL_CF_OVERLAP_WINDOW_S 0
diff --git a/drivers/net/wireless/ath/ath9k/ar953x_initvals.h b/drivers/net/wireless/ath/ath9k/ar953x_initvals.h
index 6fc0d07..c0b90da 100644
--- a/drivers/net/wireless/ath/ath9k/ar953x_initvals.h
+++ b/drivers/net/wireless/ath/ath9k/ar953x_initvals.h
@@ -757,6 +757,71 @@
{0x00016448, 0x6c927a70},
};
+static const u32 qca953x_1p1_modes_no_xpa_low_power_tx_gain_table[][2] = {
+ /* Addr allmodes */
+ {0x0000a2dc, 0xfff55592},
+ {0x0000a2e0, 0xfff99924},
+ {0x0000a2e4, 0xfffe1e00},
+ {0x0000a2e8, 0xffffe000},
+ {0x0000a410, 0x000050d6},
+ {0x0000a500, 0x00000069},
+ {0x0000a504, 0x0400006b},
+ {0x0000a508, 0x0800006d},
+ {0x0000a50c, 0x0c000269},
+ {0x0000a510, 0x1000026b},
+ {0x0000a514, 0x1400026d},
+ {0x0000a518, 0x18000669},
+ {0x0000a51c, 0x1c00066b},
+ {0x0000a520, 0x1d000a68},
+ {0x0000a524, 0x21000a6a},
+ {0x0000a528, 0x25000a6c},
+ {0x0000a52c, 0x29000a6e},
+ {0x0000a530, 0x2d0012a9},
+ {0x0000a534, 0x310012ab},
+ {0x0000a538, 0x350012ad},
+ {0x0000a53c, 0x39001b0a},
+ {0x0000a540, 0x3d001b0c},
+ {0x0000a544, 0x41001b0e},
+ {0x0000a548, 0x43001bae},
+ {0x0000a54c, 0x45001914},
+ {0x0000a550, 0x47001916},
+ {0x0000a554, 0x49001b96},
+ {0x0000a558, 0x49001b96},
+ {0x0000a55c, 0x49001b96},
+ {0x0000a560, 0x49001b96},
+ {0x0000a564, 0x49001b96},
+ {0x0000a568, 0x49001b96},
+ {0x0000a56c, 0x49001b96},
+ {0x0000a570, 0x49001b96},
+ {0x0000a574, 0x49001b96},
+ {0x0000a578, 0x49001b96},
+ {0x0000a57c, 0x49001b96},
+ {0x0000a600, 0x00000000},
+ {0x0000a604, 0x00000000},
+ {0x0000a608, 0x00000000},
+ {0x0000a60c, 0x00000000},
+ {0x0000a610, 0x00000000},
+ {0x0000a614, 0x00000000},
+ {0x0000a618, 0x00804201},
+ {0x0000a61c, 0x01408201},
+ {0x0000a620, 0x01408502},
+ {0x0000a624, 0x01408502},
+ {0x0000a628, 0x01408502},
+ {0x0000a62c, 0x01408502},
+ {0x0000a630, 0x01408502},
+ {0x0000a634, 0x01408502},
+ {0x0000a638, 0x01408502},
+ {0x0000a63c, 0x01408502},
+ {0x0000b2dc, 0xfff55592},
+ {0x0000b2e0, 0xfff99924},
+ {0x0000b2e4, 0xfffe1e00},
+ {0x0000b2e8, 0xffffe000},
+ {0x00016044, 0x044922db},
+ {0x00016048, 0x6c927a70},
+ {0x00016444, 0x044922db},
+ {0x00016448, 0x6c927a70},
+};
+
static const u32 qca953x_2p0_baseband_core[][2] = {
/* Addr allmodes */
{0x00009800, 0xafe68e30},
diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c
index 3e2e24e..37f6d66 100644
--- a/drivers/net/wireless/ath/ath9k/calib.c
+++ b/drivers/net/wireless/ath/ath9k/calib.c
@@ -241,6 +241,7 @@
u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask;
struct ath_common *common = ath9k_hw_common(ah);
s16 default_nf = ath9k_hw_get_default_nf(ah, chan);
+ u32 bb_agc_ctl = REG_READ(ah, AR_PHY_AGC_CONTROL);
if (ah->caldata)
h = ah->caldata->nfCalHist;
@@ -264,6 +265,16 @@
}
/*
+ * stop NF cal if ongoing to ensure NF load completes immediately
+ * (or after end rx/tx frame if ongoing)
+ */
+ if (bb_agc_ctl & AR_PHY_AGC_CONTROL_NF) {
+ REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
+ REG_RMW_BUFFER_FLUSH(ah);
+ ENABLE_REG_RMW_BUFFER(ah);
+ }
+
+ /*
* Load software filtered NF value into baseband internal minCCApwr
* variable.
*/
@@ -276,18 +287,33 @@
/*
* Wait for load to complete, should be fast, a few 10s of us.
- * The max delay was changed from an original 250us to 10000us
- * since 250us often results in NF load timeout and causes deaf
- * condition during stress testing 12/12/2009
+ * The max delay was changed from an original 250us to 22.2 msec.
+ * This would increase timeout to the longest possible frame
+ * (11n max length 22.1 msec)
*/
- for (j = 0; j < 10000; j++) {
+ for (j = 0; j < 22200; j++) {
if ((REG_READ(ah, AR_PHY_AGC_CONTROL) &
- AR_PHY_AGC_CONTROL_NF) == 0)
+ AR_PHY_AGC_CONTROL_NF) == 0)
break;
udelay(10);
}
/*
+ * Restart NF so it can continue.
+ */
+ if (bb_agc_ctl & AR_PHY_AGC_CONTROL_NF) {
+ ENABLE_REG_RMW_BUFFER(ah);
+ if (bb_agc_ctl & AR_PHY_AGC_CONTROL_ENABLE_NF)
+ REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
+ AR_PHY_AGC_CONTROL_ENABLE_NF);
+ if (bb_agc_ctl & AR_PHY_AGC_CONTROL_NO_UPDATE_NF)
+ REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
+ AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
+ REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
+ REG_RMW_BUFFER_FLUSH(ah);
+ }
+
+ /*
* We timed out waiting for the noisefloor to load, probably due to an
* in-progress rx. Simply return here and allow the load plenty of time
* to complete before the next calibration interval. We need to avoid
@@ -296,7 +322,7 @@
* here, the baseband nf cal will just be capped by our present
* noisefloor until the next calibration timer.
*/
- if (j == 10000) {
+ if (j == 22200) {
ath_dbg(common, ANY,
"Timeout while waiting for nf to load: AR_PHY_AGC_CONTROL=0x%x\n",
REG_READ(ah, AR_PHY_AGC_CONTROL));
diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c
index 50e614b..319cb5f 100644
--- a/drivers/net/wireless/ath/ath9k/channel.c
+++ b/drivers/net/wireless/ath/ath9k/channel.c
@@ -226,7 +226,7 @@
}
}
-static const u32 chanctx_event_delta(struct ath_softc *sc)
+static u32 chanctx_event_delta(struct ath_softc *sc)
{
u64 ms;
struct timespec ts, *old;
@@ -1454,7 +1454,7 @@
if (!sc->p2p_ps_timer)
return;
- if (vif->type != NL80211_IFTYPE_STATION || !vif->p2p)
+ if (vif->type != NL80211_IFTYPE_STATION)
return;
sc->p2p_ps_vif = avp;
diff --git a/drivers/net/wireless/ath/ath9k/eeprom.c b/drivers/net/wireless/ath/ath9k/eeprom.c
index 73fb423..a794157 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom.c
@@ -477,10 +477,9 @@
if (match) {
if (AR_SREV_9287(ah)) {
- /* FIXME: array overrun? */
for (i = 0; i < numXpdGains; i++) {
minPwrT4[i] = data_9287[idxL].pwrPdg[i][0];
- maxPwrT4[i] = data_9287[idxL].pwrPdg[i][4];
+ maxPwrT4[i] = data_9287[idxL].pwrPdg[i][intercepts - 1];
ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i],
data_9287[idxL].pwrPdg[i],
data_9287[idxL].vpdPdg[i],
@@ -490,7 +489,7 @@
} else if (eeprom_4k) {
for (i = 0; i < numXpdGains; i++) {
minPwrT4[i] = data_4k[idxL].pwrPdg[i][0];
- maxPwrT4[i] = data_4k[idxL].pwrPdg[i][4];
+ maxPwrT4[i] = data_4k[idxL].pwrPdg[i][intercepts - 1];
ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i],
data_4k[idxL].pwrPdg[i],
data_4k[idxL].vpdPdg[i],
@@ -500,7 +499,7 @@
} else {
for (i = 0; i < numXpdGains; i++) {
minPwrT4[i] = data_def[idxL].pwrPdg[i][0];
- maxPwrT4[i] = data_def[idxL].pwrPdg[i][4];
+ maxPwrT4[i] = data_def[idxL].pwrPdg[i][intercepts - 1];
ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i],
data_def[idxL].pwrPdg[i],
data_def[idxL].vpdPdg[i],
diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c
index 165dd20..8cbf490 100644
--- a/drivers/net/wireless/ath/ath9k/hif_usb.c
+++ b/drivers/net/wireless/ath/ath9k/hif_usb.c
@@ -55,6 +55,8 @@
.driver_info = AR9280_USB }, /* Buffalo WLI-UV-AG300P */
{ USB_DEVICE(0x04da, 0x3904),
.driver_info = AR9280_USB },
+ { USB_DEVICE(0x0930, 0x0a08),
+ .driver_info = AR9280_USB }, /* Toshiba WLM-20U2 and GN-1080 */
{ USB_DEVICE(0x0cf3, 0x20ff),
.driver_info = STORAGE_DEVICE },
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 257f46e..e7a3101 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -1368,6 +1368,16 @@
if (ath9k_hw_mci_is_enabled(ah))
ar9003_mci_check_gpm_offset(ah);
+ /* DMA HALT added to resolve ar9300 and ar9580 bus error during
+ * RTC_RC reg read
+ */
+ if (AR_SREV_9300(ah) || AR_SREV_9580(ah)) {
+ REG_SET_BIT(ah, AR_CFG, AR_CFG_HALT_REQ);
+ ath9k_hw_wait(ah, AR_CFG, AR_CFG_HALT_ACK, AR_CFG_HALT_ACK,
+ 20 * AH_WAIT_TIMEOUT);
+ REG_CLR_BIT(ah, AR_CFG, AR_CFG_HALT_REQ);
+ }
+
REG_WRITE(ah, AR_RTC_RC, rst_flags);
REGWRITE_BUFFER_FLUSH(ah);
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index ab7a1ac..1c226d6 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -751,14 +751,6 @@
#endif /* CONFIG_ATH9K_CHANNEL_CONTEXT */
-static const struct ieee80211_iface_limit if_dfs_limits[] = {
- { .max = 1, .types = BIT(NL80211_IFTYPE_AP) |
-#ifdef CONFIG_MAC80211_MESH
- BIT(NL80211_IFTYPE_MESH_POINT) |
-#endif
- BIT(NL80211_IFTYPE_ADHOC) },
-};
-
static const struct ieee80211_iface_combination if_comb[] = {
{
.limits = if_limits,
@@ -766,6 +758,11 @@
.max_interfaces = 2048,
.num_different_channels = 1,
.beacon_int_infra_match = true,
+#ifdef CONFIG_ATH9K_DFS_CERTIFIED
+ .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
+ BIT(NL80211_CHAN_WIDTH_20) |
+ BIT(NL80211_CHAN_WIDTH_40),
+#endif
},
{
.limits = wds_limits,
@@ -774,18 +771,6 @@
.num_different_channels = 1,
.beacon_int_infra_match = true,
},
-#ifdef CONFIG_ATH9K_DFS_CERTIFIED
- {
- .limits = if_dfs_limits,
- .n_limits = ARRAY_SIZE(if_dfs_limits),
- .max_interfaces = 1,
- .num_different_channels = 1,
- .beacon_int_infra_match = true,
- .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
- BIT(NL80211_CHAN_WIDTH_20) |
- BIT(NL80211_CHAN_WIDTH_40),
- }
-#endif
};
#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT
@@ -863,8 +848,8 @@
hw->wiphy->interface_modes |=
BIT(NL80211_IFTYPE_P2P_DEVICE);
- hw->wiphy->iface_combinations = if_comb;
- hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
+ hw->wiphy->iface_combinations = if_comb;
+ hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
}
hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index cf58a30..3aed43a 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -978,7 +978,7 @@
if (ctx->nvifs_assigned != 1)
continue;
- if (!avp->vif->p2p || !iter_data->has_hw_macaddr)
+ if (!iter_data->has_hw_macaddr)
continue;
ether_addr_copy(common->curbssid, avp->bssid);
@@ -1255,6 +1255,9 @@
ath_dbg(common, CONFIG, "Attach a VIF of type: %d\n", vif->type);
sc->cur_chan->nvifs++;
+ if (vif->type == NL80211_IFTYPE_STATION && ath9k_is_chanctx_enabled())
+ vif->driver_flags |= IEEE80211_VIF_GET_NOA_UPDATE;
+
if (ath9k_uses_beacons(vif->type))
ath9k_beacon_assign_slot(sc, vif);
diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h
index caba54d..c8d35fe 100644
--- a/drivers/net/wireless/ath/ath9k/reg.h
+++ b/drivers/net/wireless/ath/ath9k/reg.h
@@ -34,8 +34,10 @@
#define AR_CFG_SWRG 0x00000010
#define AR_CFG_AP_ADHOC_INDICATION 0x00000020
#define AR_CFG_PHOK 0x00000100
-#define AR_CFG_CLK_GATE_DIS 0x00000400
#define AR_CFG_EEBS 0x00000200
+#define AR_CFG_CLK_GATE_DIS 0x00000400
+#define AR_CFG_HALT_REQ 0x00000800
+#define AR_CFG_HALT_ACK 0x00001000
#define AR_CFG_PCI_MASTER_REQ_Q_THRESH 0x00060000
#define AR_CFG_PCI_MASTER_REQ_Q_THRESH_S 17
diff --git a/drivers/net/wireless/ath/carl9170/fwcmd.h b/drivers/net/wireless/ath/carl9170/fwcmd.h
index 9111d4f..ea1d80f 100644
--- a/drivers/net/wireless/ath/carl9170/fwcmd.h
+++ b/drivers/net/wireless/ath/carl9170/fwcmd.h
@@ -56,6 +56,7 @@
CARL9170_CMD_RX_FILTER = 0x07,
CARL9170_CMD_WOL = 0x08,
CARL9170_CMD_TALLY = 0x09,
+ CARL9170_CMD_WREGB = 0x0a,
/* CAM */
CARL9170_CMD_EKEY = 0x10,
@@ -123,6 +124,12 @@
} regs[0] __packed;
} __packed;
+struct carl9170_write_reg_byte {
+ __le32 addr;
+ __le32 count;
+ u8 val[0];
+} __packed;
+
#define CARL9170FW_PHY_HT_ENABLE 0x4
#define CARL9170FW_PHY_HT_DYN2040 0x8
#define CARL9170FW_PHY_HT_EXT_CHAN_OFF 0x3
@@ -226,6 +233,7 @@
struct carl9170_u32_list echo;
struct carl9170_reg_list rreg;
struct carl9170_write_reg wreg;
+ struct carl9170_write_reg_byte wregb;
struct carl9170_rf_init rf_init;
struct carl9170_psm psm;
struct carl9170_wol_cmd wol;
diff --git a/drivers/net/wireless/ath/carl9170/fwdesc.h b/drivers/net/wireless/ath/carl9170/fwdesc.h
index 66848d4..0533f79c 100644
--- a/drivers/net/wireless/ath/carl9170/fwdesc.h
+++ b/drivers/net/wireless/ath/carl9170/fwdesc.h
@@ -81,6 +81,12 @@
/* Firmware will pass BA when BARs are queued */
CARL9170FW_RX_BA_FILTER,
+ /* Firmware has support to write a byte at a time */
+ CARL9170FW_HAS_WREGB_CMD,
+
+ /* Pattern generator */
+ CARL9170FW_PATTERN_GENERATOR,
+
/* KEEP LAST */
__CARL9170FW_FEATURE_NUM
};
diff --git a/drivers/net/wireless/ath/carl9170/hw.h b/drivers/net/wireless/ath/carl9170/hw.h
index 0db874a..08e0ae9 100644
--- a/drivers/net/wireless/ath/carl9170/hw.h
+++ b/drivers/net/wireless/ath/carl9170/hw.h
@@ -453,9 +453,74 @@
#define AR9170_MC_REG_BASE 0x1d1000
#define AR9170_MC_REG_FLASH_WAIT_STATE (AR9170_MC_REG_BASE + 0x000)
-#define AR9170_MC_REG_SEEPROM_WP0 (AR9170_MC_REG_BASE + 0x400)
-#define AR9170_MC_REG_SEEPROM_WP1 (AR9170_MC_REG_BASE + 0x404)
-#define AR9170_MC_REG_SEEPROM_WP2 (AR9170_MC_REG_BASE + 0x408)
+
+#define AR9170_SPI_REG_BASE (AR9170_MC_REG_BASE + 0x200)
+#define AR9170_SPI_REG_CONTROL0 (AR9170_SPI_REG_BASE + 0x000)
+#define AR9170_SPI_CONTROL0_BUSY BIT(0)
+#define AR9170_SPI_CONTROL0_CMD_GO BIT(1)
+#define AR9170_SPI_CONTROL0_PAGE_WR BIT(2)
+#define AR9170_SPI_CONTROL0_SEQ_RD BIT(3)
+#define AR9170_SPI_CONTROL0_CMD_ABORT BIT(4)
+#define AR9170_SPI_CONTROL0_CMD_LEN_S 8
+#define AR9170_SPI_CONTROL0_CMD_LEN 0x00000f00
+#define AR9170_SPI_CONTROL0_RD_LEN_S 12
+#define AR9170_SPI_CONTROL0_RD_LEN 0x00007000
+
+#define AR9170_SPI_REG_CONTROL1 (AR9170_SPI_REG_BASE + 0x004)
+#define AR9170_SPI_CONTROL1_SCK_RATE BIT(0)
+#define AR9170_SPI_CONTROL1_DRIVE_SDO BIT(1)
+#define AR9170_SPI_CONTROL1_MODE_SEL_S 2
+#define AR9170_SPI_CONTROL1_MODE_SEL 0x000000c0
+#define AR9170_SPI_CONTROL1_WRITE_PROTECT BIT(4)
+
+#define AR9170_SPI_REG_COMMAND_PORT0 (AR9170_SPI_REG_BASE + 0x008)
+#define AR9170_SPI_COMMAND_PORT0_CMD0_S 0
+#define AR9170_SPI_COMMAND_PORT0_CMD0 0x000000ff
+#define AR9170_SPI_COMMAND_PORT0_CMD1_S 8
+#define AR9170_SPI_COMMAND_PORT0_CMD1 0x0000ff00
+#define AR9170_SPI_COMMAND_PORT0_CMD2_S 16
+#define AR9170_SPI_COMMAND_PORT0_CMD2 0x00ff0000
+#define AR9170_SPI_COMMAND_PORT0_CMD3_S 24
+#define AR9170_SPI_COMMAND_PORT0_CMD3 0xff000000
+
+#define AR9170_SPI_REG_COMMAND_PORT1 (AR9170_SPI_REG_BASE + 0x00C)
+#define AR9170_SPI_COMMAND_PORT1_CMD4_S 0
+#define AR9170_SPI_COMMAND_PORT1_CMD4 0x000000ff
+#define AR9170_SPI_COMMAND_PORT1_CMD5_S 8
+#define AR9170_SPI_COMMAND_PORT1_CMD5 0x0000ff00
+#define AR9170_SPI_COMMAND_PORT1_CMD6_S 16
+#define AR9170_SPI_COMMAND_PORT1_CMD6 0x00ff0000
+#define AR9170_SPI_COMMAND_PORT1_CMD7_S 24
+#define AR9170_SPI_COMMAND_PORT1_CMD7 0xff000000
+
+#define AR9170_SPI_REG_DATA_PORT (AR9170_SPI_REG_BASE + 0x010)
+#define AR9170_SPI_REG_PAGE_WRITE_LEN (AR9170_SPI_REG_BASE + 0x014)
+
+#define AR9170_EEPROM_REG_BASE (AR9170_MC_REG_BASE + 0x400)
+#define AR9170_EEPROM_REG_WP_MAGIC1 (AR9170_EEPROM_REG_BASE + 0x000)
+#define AR9170_EEPROM_WP_MAGIC1 0x12345678
+
+#define AR9170_EEPROM_REG_WP_MAGIC2 (AR9170_EEPROM_REG_BASE + 0x004)
+#define AR9170_EEPROM_WP_MAGIC2 0x55aa00ff
+
+#define AR9170_EEPROM_REG_WP_MAGIC3 (AR9170_EEPROM_REG_BASE + 0x008)
+#define AR9170_EEPROM_WP_MAGIC3 0x13579ace
+
+#define AR9170_EEPROM_REG_CLOCK_DIV (AR9170_EEPROM_REG_BASE + 0x00C)
+#define AR9170_EEPROM_CLOCK_DIV_FAC_S 0
+#define AR9170_EEPROM_CLOCK_DIV_FAC 0x000001ff
+#define AR9170_EEPROM_CLOCK_DIV_FAC_39KHZ 0xff
+#define AR9170_EEPROM_CLOCK_DIV_FAC_78KHZ 0x7f
+#define AR9170_EEPROM_CLOCK_DIV_FAC_312KHZ 0x1f
+#define AR9170_EEPROM_CLOCK_DIV_FAC_10MHZ 0x0
+#define AR9170_EEPROM_CLOCK_DIV_SOFT_RST BIT(9)
+
+#define AR9170_EEPROM_REG_MODE (AR9170_EEPROM_REG_BASE + 0x010)
+#define AR9170_EEPROM_MODE_EEPROM_SIZE_16K_PLUS BIT(31)
+
+#define AR9170_EEPROM_REG_WRITE_PROTECT (AR9170_EEPROM_REG_BASE + 0x014)
+#define AR9170_EEPROM_WRITE_PROTECT_WP_STATUS BIT(0)
+#define AR9170_EEPROM_WRITE_PROTECT_WP_SET BIT(8)
/* Interrupt Controller */
#define AR9170_MAX_INT_SRC 9
@@ -589,11 +654,13 @@
#define AR9170_USB_REG_EP10_MAP (AR9170_USB_REG_BASE + 0x039)
#define AR9170_USB_REG_EP_IN_MAX_SIZE_HIGH (AR9170_USB_REG_BASE + 0x03f)
+#define AR9170_USB_EP_IN_STALL 0x8
#define AR9170_USB_EP_IN_TOGGLE 0x10
#define AR9170_USB_REG_EP_IN_MAX_SIZE_LOW (AR9170_USB_REG_BASE + 0x03e)
#define AR9170_USB_REG_EP_OUT_MAX_SIZE_HIGH (AR9170_USB_REG_BASE + 0x05f)
+#define AR9170_USB_EP_OUT_STALL 0x8
#define AR9170_USB_EP_OUT_TOGGLE 0x10
#define AR9170_USB_REG_EP_OUT_MAX_SIZE_LOW (AR9170_USB_REG_BASE + 0x05e)
diff --git a/drivers/net/wireless/ath/carl9170/version.h b/drivers/net/wireless/ath/carl9170/version.h
index 2282847..a0410fe 100644
--- a/drivers/net/wireless/ath/carl9170/version.h
+++ b/drivers/net/wireless/ath/carl9170/version.h
@@ -1,7 +1,7 @@
#ifndef __CARL9170_SHARED_VERSION_H
#define __CARL9170_SHARED_VERSION_H
-#define CARL9170FW_VERSION_YEAR 12
-#define CARL9170FW_VERSION_MONTH 12
+#define CARL9170FW_VERSION_YEAR 16
+#define CARL9170FW_VERSION_MONTH 2
#define CARL9170FW_VERSION_DAY 15
-#define CARL9170FW_VERSION_GIT "1.9.7"
+#define CARL9170FW_VERSION_GIT "1.9.9"
#endif /* __CARL9170_SHARED_VERSION_H */
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index 1f231cd..11f1bb8 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2015 Qualcomm Atheros, Inc.
+ * Copyright (c) 2012-2016 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -540,7 +540,18 @@
wil_dbg_misc(wil, "%s(reason=%d)\n", __func__, reason_code);
- rc = wmi_send(wil, WMI_DISCONNECT_CMDID, NULL, 0);
+ if (!(test_bit(wil_status_fwconnecting, wil->status) ||
+ test_bit(wil_status_fwconnected, wil->status))) {
+ wil_err(wil, "%s: Disconnect was called while disconnected\n",
+ __func__);
+ return 0;
+ }
+
+ rc = wmi_call(wil, WMI_DISCONNECT_CMDID, NULL, 0,
+ WMI_DISCONNECT_EVENTID, NULL, 0,
+ WIL6210_DISCONNECT_TO_MS);
+ if (rc)
+ wil_err(wil, "%s: disconnect error %d\n", __func__, rc);
return rc;
}
@@ -701,6 +712,79 @@
return rc;
}
+/**
+ * find a specific IE in a list of IEs
+ * return a pointer to the beginning of IE in the list
+ * or NULL if not found
+ */
+static const u8 *_wil_cfg80211_find_ie(const u8 *ies, u16 ies_len, const u8 *ie,
+ u16 ie_len)
+{
+ struct ieee80211_vendor_ie *vie;
+ u32 oui;
+
+ /* IE tag at offset 0, length at offset 1 */
+ if (ie_len < 2 || 2 + ie[1] > ie_len)
+ return NULL;
+
+ if (ie[0] != WLAN_EID_VENDOR_SPECIFIC)
+ return cfg80211_find_ie(ie[0], ies, ies_len);
+
+ /* make sure there is room for 3 bytes OUI + 1 byte OUI type */
+ if (ie[1] < 4)
+ return NULL;
+ vie = (struct ieee80211_vendor_ie *)ie;
+ oui = vie->oui[0] << 16 | vie->oui[1] << 8 | vie->oui[2];
+ return cfg80211_find_vendor_ie(oui, vie->oui_type, ies,
+ ies_len);
+}
+
+/**
+ * merge the IEs in two lists into a single list.
+ * do not include IEs from the second list which exist in the first list.
+ * add only vendor specific IEs from second list to keep
+ * the merged list sorted (since vendor-specific IE has the
+ * highest tag number)
+ * caller must free the allocated memory for merged IEs
+ */
+static int _wil_cfg80211_merge_extra_ies(const u8 *ies1, u16 ies1_len,
+ const u8 *ies2, u16 ies2_len,
+ u8 **merged_ies, u16 *merged_len)
+{
+ u8 *buf, *dpos;
+ const u8 *spos;
+
+ if (ies1_len == 0 && ies2_len == 0) {
+ *merged_ies = NULL;
+ *merged_len = 0;
+ return 0;
+ }
+
+ buf = kmalloc(ies1_len + ies2_len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ memcpy(buf, ies1, ies1_len);
+ dpos = buf + ies1_len;
+ spos = ies2;
+ while (spos + 1 < ies2 + ies2_len) {
+ /* IE tag at offset 0, length at offset 1 */
+ u16 ielen = 2 + spos[1];
+
+ if (spos + ielen > ies2 + ies2_len)
+ break;
+ if (spos[0] == WLAN_EID_VENDOR_SPECIFIC &&
+ !_wil_cfg80211_find_ie(ies1, ies1_len, spos, ielen)) {
+ memcpy(dpos, spos, ielen);
+ dpos += ielen;
+ }
+ spos += ielen;
+ }
+
+ *merged_ies = buf;
+ *merged_len = dpos - buf;
+ return 0;
+}
+
static void wil_print_bcon_data(struct cfg80211_beacon_data *b)
{
print_hex_dump_bytes("head ", DUMP_PREFIX_OFFSET,
@@ -717,49 +801,49 @@
b->assocresp_ies, b->assocresp_ies_len);
}
-static int wil_fix_bcon(struct wil6210_priv *wil,
- struct cfg80211_beacon_data *bcon)
-{
- struct ieee80211_mgmt *f = (struct ieee80211_mgmt *)bcon->probe_resp;
- size_t hlen = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
-
- if (bcon->probe_resp_len <= hlen)
- return 0;
-
-/* always use IE's from full probe frame, they has more info
- * notable RSN
- */
- bcon->proberesp_ies = f->u.probe_resp.variable;
- bcon->proberesp_ies_len = bcon->probe_resp_len - hlen;
- if (!bcon->assocresp_ies) {
- bcon->assocresp_ies = bcon->proberesp_ies;
- bcon->assocresp_ies_len = bcon->proberesp_ies_len;
- }
-
- return 1;
-}
-
/* internal functions for device reset and starting AP */
static int _wil_cfg80211_set_ies(struct wiphy *wiphy,
struct cfg80211_beacon_data *bcon)
{
int rc;
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+ u16 len = 0, proberesp_len = 0;
+ u8 *ies = NULL, *proberesp = NULL;
- rc = wmi_set_ie(wil, WMI_FRAME_PROBE_RESP, bcon->proberesp_ies_len,
- bcon->proberesp_ies);
+ if (bcon->probe_resp) {
+ struct ieee80211_mgmt *f =
+ (struct ieee80211_mgmt *)bcon->probe_resp;
+ size_t hlen = offsetof(struct ieee80211_mgmt,
+ u.probe_resp.variable);
+ proberesp = f->u.probe_resp.variable;
+ proberesp_len = bcon->probe_resp_len - hlen;
+ }
+ rc = _wil_cfg80211_merge_extra_ies(proberesp,
+ proberesp_len,
+ bcon->proberesp_ies,
+ bcon->proberesp_ies_len,
+ &ies, &len);
+
if (rc)
- return rc;
+ goto out;
- rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_RESP, bcon->assocresp_ies_len,
- bcon->assocresp_ies);
+ rc = wmi_set_ie(wil, WMI_FRAME_PROBE_RESP, len, ies);
+ if (rc)
+ goto out;
+
+ if (bcon->assocresp_ies)
+ rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_RESP,
+ bcon->assocresp_ies_len, bcon->assocresp_ies);
+ else
+ rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_RESP, len, ies);
#if 0 /* to use beacon IE's, remove this #if 0 */
if (rc)
- return rc;
+ goto out;
rc = wmi_set_ie(wil, WMI_FRAME_BEACON, bcon->tail_len, bcon->tail);
#endif
-
+out:
+ kfree(ies);
return rc;
}
@@ -828,14 +912,9 @@
wil_dbg_misc(wil, "%s()\n", __func__);
wil_print_bcon_data(bcon);
- if (wil_fix_bcon(wil, bcon)) {
- wil_dbg_misc(wil, "Fixed bcon\n");
- wil_print_bcon_data(bcon);
- }
-
- if (bcon->proberesp_ies &&
- cfg80211_find_ie(WLAN_EID_RSN, bcon->proberesp_ies,
- bcon->proberesp_ies_len))
+ if (bcon->tail &&
+ cfg80211_find_ie(WLAN_EID_RSN, bcon->tail,
+ bcon->tail_len))
privacy = 1;
/* in case privacy has changed, need to restart the AP */
@@ -910,11 +989,6 @@
wil_print_bcon_data(bcon);
wil_print_crypto(wil, crypto);
- if (wil_fix_bcon(wil, bcon)) {
- wil_dbg_misc(wil, "Fixed bcon\n");
- wil_print_bcon_data(bcon);
- }
-
rc = _wil_cfg80211_start_ap(wiphy, ndev,
info->ssid, info->ssid_len, info->privacy,
info->beacon_interval, channel->hw_value,
diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c
index a1d10b8..3bbe73b 100644
--- a/drivers/net/wireless/ath/wil6210/debugfs.c
+++ b/drivers/net/wireless/ath/wil6210/debugfs.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2015 Qualcomm Atheros, Inc.
+ * Copyright (c) 2012-2016 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -68,13 +68,13 @@
seq_puts(s, "???\n");
}
- if (vring->va && (vring->size < 1025)) {
+ if (vring->va && (vring->size <= (1 << WIL_RING_SIZE_ORDER_MAX))) {
uint i;
for (i = 0; i < vring->size; i++) {
volatile struct vring_tx_desc *d = &vring->va[i].tx;
- if ((i % 64) == 0 && (i != 0))
+ if ((i % 128) == 0 && (i != 0))
seq_puts(s, "\n");
seq_printf(s, "%c", (d->dma.status & BIT(0)) ?
_s : (vring->ctx[i].skb ? _h : 'h'));
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index b39f0bf..78ba6e0 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2015 Qualcomm Atheros, Inc.
+ * Copyright (c) 2012-2016 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -23,9 +23,6 @@
#include "wmi.h"
#include "boot_loader.h"
-#define WAIT_FOR_DISCONNECT_TIMEOUT_MS 2000
-#define WAIT_FOR_DISCONNECT_INTERVAL_MS 10
-
bool debug_fw; /* = false; */
module_param(debug_fw, bool, S_IRUGO);
MODULE_PARM_DESC(debug_fw, " do not perform card reset. For FW debug");
@@ -155,7 +152,7 @@
if (sta->status != wil_sta_unused) {
if (!from_event)
- wmi_disconnect_sta(wil, sta->addr, reason_code);
+ wmi_disconnect_sta(wil, sta->addr, reason_code, true);
switch (wdev->iftype) {
case NL80211_IFTYPE_AP:
@@ -195,8 +192,8 @@
struct wireless_dev *wdev = wil->wdev;
might_sleep();
- wil_dbg_misc(wil, "%s(bssid=%pM, reason=%d, ev%s)\n", __func__, bssid,
- reason_code, from_event ? "+" : "-");
+ wil_info(wil, "%s(bssid=%pM, reason=%d, ev%s)\n", __func__, bssid,
+ reason_code, from_event ? "+" : "-");
/* Cases are:
* - disconnect single STA, still connected
@@ -258,13 +255,16 @@
static void wil_connect_timer_fn(ulong x)
{
struct wil6210_priv *wil = (void *)x;
+ bool q;
- wil_dbg_misc(wil, "Connect timeout\n");
+ wil_err(wil, "Connect timeout detected, disconnect station\n");
/* reschedule to thread context - disconnect won't
- * run from atomic context
+ * run from atomic context.
+ * queue on wmi_wq to prevent race with connect event.
*/
- schedule_work(&wil->disconnect_worker);
+ q = queue_work(wil->wmi_wq, &wil->disconnect_worker);
+ wil_dbg_wmi(wil, "queue_work of disconnect_worker -> %d\n", q);
}
static void wil_scan_timer_fn(ulong x)
@@ -369,6 +369,32 @@
return -EINVAL;
}
+int wil_tx_init(struct wil6210_priv *wil, int cid)
+{
+ int rc = -EINVAL, ringid;
+
+ if (cid < 0) {
+ wil_err(wil, "No connection pending\n");
+ goto out;
+ }
+ ringid = wil_find_free_vring(wil);
+ if (ringid < 0) {
+ wil_err(wil, "No free vring found\n");
+ goto out;
+ }
+
+ wil_dbg_wmi(wil, "Configure for connection CID %d vring %d\n",
+ cid, ringid);
+
+ rc = wil_vring_init_tx(wil, ringid, 1 << tx_ring_order, cid, 0);
+ if (rc)
+ wil_err(wil, "wil_vring_init_tx for CID %d vring %d failed\n",
+ cid, ringid);
+
+out:
+ return rc;
+}
+
int wil_bcast_init(struct wil6210_priv *wil)
{
int ri = wil->bcast_vring, rc;
@@ -399,41 +425,6 @@
wil_vring_fini_tx(wil, ri);
}
-static void wil_connect_worker(struct work_struct *work)
-{
- int rc, cid, ringid;
- struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
- connect_worker);
- struct net_device *ndev = wil_to_ndev(wil);
-
- mutex_lock(&wil->mutex);
-
- cid = wil->pending_connect_cid;
- if (cid < 0) {
- wil_err(wil, "No connection pending\n");
- goto out;
- }
- ringid = wil_find_free_vring(wil);
- if (ringid < 0) {
- wil_err(wil, "No free vring found\n");
- goto out;
- }
-
- wil_dbg_wmi(wil, "Configure for connection CID %d vring %d\n",
- cid, ringid);
-
- rc = wil_vring_init_tx(wil, ringid, 1 << tx_ring_order, cid, 0);
- wil->pending_connect_cid = -1;
- if (rc == 0) {
- wil->sta[cid].status = wil_sta_connected;
- netif_tx_wake_all_queues(ndev);
- } else {
- wil_disconnect_cid(wil, cid, WLAN_REASON_UNSPECIFIED, true);
- }
-out:
- mutex_unlock(&wil->mutex);
-}
-
int wil_priv_init(struct wil6210_priv *wil)
{
uint i;
@@ -444,6 +435,9 @@
for (i = 0; i < WIL6210_MAX_CID; i++)
spin_lock_init(&wil->sta[i].tid_rx_lock);
+ for (i = 0; i < WIL6210_MAX_TX_RINGS; i++)
+ spin_lock_init(&wil->vring_tx_data[i].lock);
+
mutex_init(&wil->mutex);
mutex_init(&wil->wmi_mutex);
mutex_init(&wil->back_rx_mutex);
@@ -453,12 +447,10 @@
init_completion(&wil->wmi_ready);
init_completion(&wil->wmi_call);
- wil->pending_connect_cid = -1;
wil->bcast_vring = -1;
setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil);
setup_timer(&wil->scan_timer, wil_scan_timer_fn, (ulong)wil);
- INIT_WORK(&wil->connect_worker, wil_connect_worker);
INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker);
INIT_WORK(&wil->wmi_event_worker, wmi_event_worker);
INIT_WORK(&wil->fw_error_worker, wil_fw_error_worker);
@@ -844,7 +836,6 @@
}
/* init after reset */
- wil->pending_connect_cid = -1;
wil->ap_isolate = 0;
reinit_completion(&wil->wmi_ready);
reinit_completion(&wil->wmi_call);
@@ -948,8 +939,7 @@
int __wil_down(struct wil6210_priv *wil)
{
- int iter = WAIT_FOR_DISCONNECT_TIMEOUT_MS /
- WAIT_FOR_DISCONNECT_INTERVAL_MS;
+ int rc;
WARN_ON(!mutex_is_locked(&wil->mutex));
@@ -973,22 +963,16 @@
}
if (test_bit(wil_status_fwconnected, wil->status) ||
- test_bit(wil_status_fwconnecting, wil->status))
- wmi_send(wil, WMI_DISCONNECT_CMDID, NULL, 0);
+ test_bit(wil_status_fwconnecting, wil->status)) {
- /* make sure wil is idle (not connected) */
- mutex_unlock(&wil->mutex);
- while (iter--) {
- int idle = !test_bit(wil_status_fwconnected, wil->status) &&
- !test_bit(wil_status_fwconnecting, wil->status);
- if (idle)
- break;
- msleep(WAIT_FOR_DISCONNECT_INTERVAL_MS);
+ mutex_unlock(&wil->mutex);
+ rc = wmi_call(wil, WMI_DISCONNECT_CMDID, NULL, 0,
+ WMI_DISCONNECT_EVENTID, NULL, 0,
+ WIL6210_DISCONNECT_TO_MS);
+ mutex_lock(&wil->mutex);
+ if (rc)
+ wil_err(wil, "timeout waiting for disconnect\n");
}
- mutex_lock(&wil->mutex);
-
- if (iter < 0)
- wil_err(wil, "timeout waiting for idle FW/HW\n");
wil_reset(wil, false);
diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c
index 56aaa2d..ecc3c1b 100644
--- a/drivers/net/wireless/ath/wil6210/netdev.c
+++ b/drivers/net/wireless/ath/wil6210/netdev.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2015 Qualcomm Atheros, Inc.
+ * Copyright (c) 2012-2016 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -108,8 +108,9 @@
/* always process ALL Tx complete, regardless budget - it is fast */
for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) {
struct vring *vring = &wil->vring_tx[i];
+ struct vring_tx_data *txdata = &wil->vring_tx_data[i];
- if (!vring->va)
+ if (!vring->va || !txdata->enabled)
continue;
tx_done += wil_tx_complete(wil, i);
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index 7887e6c..6af2090 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2015 Qualcomm Atheros, Inc.
+ * Copyright (c) 2012-2016 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -717,6 +717,21 @@
wil_vring_free(wil, vring, 0);
}
+static inline void wil_tx_data_init(struct vring_tx_data *txdata)
+{
+ spin_lock_bh(&txdata->lock);
+ txdata->dot1x_open = 0;
+ txdata->enabled = 0;
+ txdata->idle = 0;
+ txdata->last_idle = 0;
+ txdata->begin = 0;
+ txdata->agg_wsize = 0;
+ txdata->agg_timeout = 0;
+ txdata->agg_amsdu = 0;
+ txdata->addba_in_progress = false;
+ spin_unlock_bh(&txdata->lock);
+}
+
int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
int cid, int tid)
{
@@ -758,8 +773,7 @@
goto out;
}
- memset(txdata, 0, sizeof(*txdata));
- spin_lock_init(&txdata->lock);
+ wil_tx_data_init(txdata);
vring->size = size;
rc = wil_vring_alloc(wil, vring);
if (rc)
@@ -791,9 +805,14 @@
return 0;
out_free:
+ spin_lock_bh(&txdata->lock);
txdata->dot1x_open = false;
txdata->enabled = 0;
+ spin_unlock_bh(&txdata->lock);
wil_vring_free(wil, vring, 1);
+ wil->vring2cid_tid[id][0] = WIL6210_MAX_CID;
+ wil->vring2cid_tid[id][1] = 0;
+
out:
return rc;
@@ -831,8 +850,7 @@
goto out;
}
- memset(txdata, 0, sizeof(*txdata));
- spin_lock_init(&txdata->lock);
+ wil_tx_data_init(txdata);
vring->size = size;
rc = wil_vring_alloc(wil, vring);
if (rc)
@@ -862,8 +880,10 @@
return 0;
out_free:
+ spin_lock_bh(&txdata->lock);
txdata->enabled = 0;
txdata->dot1x_open = false;
+ spin_unlock_bh(&txdata->lock);
wil_vring_free(wil, vring, 1);
out:
@@ -891,7 +911,6 @@
napi_synchronize(&wil->napi_tx);
wil_vring_free(wil, vring, 1);
- memset(txdata, 0, sizeof(*txdata));
}
static struct vring *wil_find_tx_ucast(struct wil6210_priv *wil,
@@ -911,10 +930,11 @@
continue;
if (wil->vring2cid_tid[i][0] == cid) {
struct vring *v = &wil->vring_tx[i];
+ struct vring_tx_data *txdata = &wil->vring_tx_data[i];
wil_dbg_txrx(wil, "%s(%pM) -> [%d]\n",
__func__, eth->h_dest, i);
- if (v->va) {
+ if (v->va && txdata->enabled) {
return v;
} else {
wil_dbg_txrx(wil, "vring[%d] not valid\n", i);
@@ -935,6 +955,7 @@
struct vring *v;
int i;
u8 cid;
+ struct vring_tx_data *txdata;
/* In the STA mode, it is expected to have only 1 VRING
* for the AP we connected to.
@@ -942,7 +963,8 @@
*/
for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) {
v = &wil->vring_tx[i];
- if (!v->va)
+ txdata = &wil->vring_tx_data[i];
+ if (!v->va || !txdata->enabled)
continue;
cid = wil->vring2cid_tid[i][0];
@@ -978,12 +1000,14 @@
struct sk_buff *skb)
{
struct vring *v;
+ struct vring_tx_data *txdata;
int i = wil->bcast_vring;
if (i < 0)
return NULL;
v = &wil->vring_tx[i];
- if (!v->va)
+ txdata = &wil->vring_tx_data[i];
+ if (!v->va || !txdata->enabled)
return NULL;
if (!wil->vring_tx_data[i].dot1x_open &&
(skb->protocol != cpu_to_be16(ETH_P_PAE)))
@@ -1010,11 +1034,13 @@
u8 cid;
struct ethhdr *eth = (void *)skb->data;
char *src = eth->h_source;
+ struct vring_tx_data *txdata;
/* find 1-st vring eligible for data */
for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) {
v = &wil->vring_tx[i];
- if (!v->va)
+ txdata = &wil->vring_tx_data[i];
+ if (!v->va || !txdata->enabled)
continue;
cid = wil->vring2cid_tid[i][0];
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index 235e205..8427d68 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2015 Qualcomm Atheros, Inc.
+ * Copyright (c) 2012-2016 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -51,7 +51,7 @@
#define WIL_TX_Q_LEN_DEFAULT (4000)
#define WIL_RX_RING_SIZE_ORDER_DEFAULT (10)
-#define WIL_TX_RING_SIZE_ORDER_DEFAULT (10)
+#define WIL_TX_RING_SIZE_ORDER_DEFAULT (12)
#define WIL_BCAST_RING_SIZE_ORDER_DEFAULT (7)
#define WIL_BCAST_MCS0_LIMIT (1024) /* limit for MCS0 frame size */
/* limit ring size in range [32..32k] */
@@ -92,6 +92,7 @@
#define WIL6210_FW_RECOVERY_RETRIES (5) /* try to recover this many times */
#define WIL6210_FW_RECOVERY_TO msecs_to_jiffies(5000)
#define WIL6210_SCAN_TO msecs_to_jiffies(10000)
+#define WIL6210_DISCONNECT_TO_MS (2000)
#define WIL6210_RX_HIGH_TRSH_INIT (0)
#define WIL6210_RX_HIGH_TRSH_DEFAULT \
(1 << (WIL_RX_RING_SIZE_ORDER_DEFAULT - 3))
@@ -581,12 +582,10 @@
struct workqueue_struct *wmi_wq; /* for deferred calls */
struct work_struct wmi_event_worker;
struct workqueue_struct *wq_service;
- struct work_struct connect_worker;
struct work_struct disconnect_worker;
struct work_struct fw_error_worker; /* for FW error recovery */
struct timer_list connect_timer;
struct timer_list scan_timer; /* detect scan timeout */
- int pending_connect_cid;
struct list_head pending_wmi_ev;
/*
* protect pending_wmi_ev
@@ -756,7 +755,8 @@
int wmi_p2p_cfg(struct wil6210_priv *wil, int channel);
int wmi_rxon(struct wil6210_priv *wil, bool on);
int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_m, u32 *t_r);
-int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac, u16 reason);
+int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac, u16 reason,
+ bool full_disconnect);
int wmi_addba(struct wil6210_priv *wil, u8 ringid, u8 size, u16 timeout);
int wmi_delba_tx(struct wil6210_priv *wil, u8 ringid, u16 reason);
int wmi_delba_rx(struct wil6210_priv *wil, u8 cidxtid, u16 reason);
@@ -807,6 +807,7 @@
int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
int cid, int tid);
void wil_vring_fini_tx(struct wil6210_priv *wil, int id);
+int wil_tx_init(struct wil6210_priv *wil, int cid);
int wil_vring_init_bcast(struct wil6210_priv *wil, int id, int size);
int wil_bcast_init(struct wil6210_priv *wil);
void wil_bcast_fini(struct wil6210_priv *wil);
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index e3ea74c..493e721 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2015 Qualcomm Atheros, Inc.
+ * Copyright (c) 2012-2016 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -426,6 +426,7 @@
const size_t assoc_req_ie_offset = sizeof(u16) * 2;
/* capinfo(u16) + status_code(u16) + associd(u16) + IEs */
const size_t assoc_resp_ie_offset = sizeof(u16) * 3;
+ int rc;
if (len < sizeof(*evt)) {
wil_err(wil, "Connect event too short : %d bytes\n", len);
@@ -445,8 +446,8 @@
}
ch = evt->channel + 1;
- wil_dbg_wmi(wil, "Connect %pM channel [%d] cid %d\n",
- evt->bssid, ch, evt->cid);
+ wil_info(wil, "Connect %pM channel [%d] cid %d\n",
+ evt->bssid, ch, evt->cid);
wil_hex_dump_wmi("connect AI : ", DUMP_PREFIX_OFFSET, 16, 1,
evt->assoc_info, len - sizeof(*evt), true);
@@ -468,20 +469,67 @@
assoc_resp_ielen = 0;
}
+ mutex_lock(&wil->mutex);
+ if (test_bit(wil_status_resetting, wil->status) ||
+ !test_bit(wil_status_fwready, wil->status)) {
+ wil_err(wil, "status_resetting, cancel connect event, CID %d\n",
+ evt->cid);
+ mutex_unlock(&wil->mutex);
+ /* no need for cleanup, wil_reset will do that */
+ return;
+ }
+
if ((wdev->iftype == NL80211_IFTYPE_STATION) ||
(wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) {
if (!test_bit(wil_status_fwconnecting, wil->status)) {
wil_err(wil, "Not in connecting state\n");
+ mutex_unlock(&wil->mutex);
return;
}
del_timer_sync(&wil->connect_timer);
- cfg80211_connect_result(ndev, evt->bssid,
- assoc_req_ie, assoc_req_ielen,
- assoc_resp_ie, assoc_resp_ielen,
- WLAN_STATUS_SUCCESS, GFP_KERNEL);
+ }
+ /* FIXME FW can transmit only ucast frames to peer */
+ /* FIXME real ring_id instead of hard coded 0 */
+ ether_addr_copy(wil->sta[evt->cid].addr, evt->bssid);
+ wil->sta[evt->cid].status = wil_sta_conn_pending;
+
+ rc = wil_tx_init(wil, evt->cid);
+ if (rc) {
+ wil_err(wil, "%s: config tx vring failed for CID %d, rc (%d)\n",
+ __func__, evt->cid, rc);
+ wmi_disconnect_sta(wil, wil->sta[evt->cid].addr,
+ WLAN_REASON_UNSPECIFIED, false);
+ } else {
+ wil_info(wil, "%s: successful connection to CID %d\n",
+ __func__, evt->cid);
+ }
+
+ if ((wdev->iftype == NL80211_IFTYPE_STATION) ||
+ (wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) {
+ if (rc) {
+ netif_tx_stop_all_queues(ndev);
+ netif_carrier_off(ndev);
+ wil_err(wil,
+ "%s: cfg80211_connect_result with failure\n",
+ __func__);
+ cfg80211_connect_result(ndev, evt->bssid, NULL, 0,
+ NULL, 0,
+ WLAN_STATUS_UNSPECIFIED_FAILURE,
+ GFP_KERNEL);
+ goto out;
+ } else {
+ cfg80211_connect_result(ndev, evt->bssid,
+ assoc_req_ie, assoc_req_ielen,
+ assoc_resp_ie, assoc_resp_ielen,
+ WLAN_STATUS_SUCCESS,
+ GFP_KERNEL);
+ }
} else if ((wdev->iftype == NL80211_IFTYPE_AP) ||
(wdev->iftype == NL80211_IFTYPE_P2P_GO)) {
+ if (rc)
+ goto out;
+
memset(&sinfo, 0, sizeof(sinfo));
sinfo.generation = wil->sinfo_gen++;
@@ -492,17 +540,21 @@
}
cfg80211_new_sta(ndev, evt->bssid, &sinfo, GFP_KERNEL);
+ } else {
+ wil_err(wil, "%s: unhandled iftype %d for CID %d\n",
+ __func__, wdev->iftype, evt->cid);
+ goto out;
}
- clear_bit(wil_status_fwconnecting, wil->status);
+
+ wil->sta[evt->cid].status = wil_sta_connected;
set_bit(wil_status_fwconnected, wil->status);
+ netif_tx_wake_all_queues(ndev);
- /* FIXME FW can transmit only ucast frames to peer */
- /* FIXME real ring_id instead of hard coded 0 */
- ether_addr_copy(wil->sta[evt->cid].addr, evt->bssid);
- wil->sta[evt->cid].status = wil_sta_conn_pending;
-
- wil->pending_connect_cid = evt->cid;
- queue_work(wil->wq_service, &wil->connect_worker);
+out:
+ if (rc)
+ wil->sta[evt->cid].status = wil_sta_unused;
+ clear_bit(wil_status_fwconnecting, wil->status);
+ mutex_unlock(&wil->mutex);
}
static void wmi_evt_disconnect(struct wil6210_priv *wil, int id,
@@ -511,8 +563,8 @@
struct wmi_disconnect_event *evt = d;
u16 reason_code = le16_to_cpu(evt->protocol_reason_status);
- wil_dbg_wmi(wil, "Disconnect %pM reason [proto %d wmi %d]\n",
- evt->bssid, reason_code, evt->disconnect_reason);
+ wil_info(wil, "Disconnect %pM reason [proto %d wmi %d]\n",
+ evt->bssid, reason_code, evt->disconnect_reason);
wil->sinfo_gen++;
@@ -727,6 +779,7 @@
void __iomem *src;
ulong flags;
unsigned n;
+ unsigned int num_immed_reply = 0;
if (!test_bit(wil_status_mbox_ready, wil->status)) {
wil_err(wil, "Reset in progress. Cannot handle WMI event\n");
@@ -736,6 +789,7 @@
for (n = 0;; n++) {
u16 len;
bool q;
+ bool immed_reply = false;
r->head = wil_r(wil, RGF_MBOX +
offsetof(struct wil6210_mbox_ctl, rx.head));
@@ -784,6 +838,15 @@
struct wil6210_mbox_hdr_wmi *wmi = &evt->event.wmi;
u16 id = le16_to_cpu(wmi->id);
u32 tstamp = le32_to_cpu(wmi->timestamp);
+ spin_lock_irqsave(&wil->wmi_ev_lock, flags);
+ if (wil->reply_id && wil->reply_id == id) {
+ if (wil->reply_buf) {
+ memcpy(wil->reply_buf, wmi,
+ min(len, wil->reply_size));
+ immed_reply = true;
+ }
+ }
+ spin_unlock_irqrestore(&wil->wmi_ev_lock, flags);
wil_dbg_wmi(wil, "WMI event 0x%04x MID %d @%d msec\n",
id, wmi->mid, tstamp);
@@ -799,15 +862,24 @@
wil_w(wil, RGF_MBOX +
offsetof(struct wil6210_mbox_ctl, rx.tail), r->tail);
- /* add to the pending list */
- spin_lock_irqsave(&wil->wmi_ev_lock, flags);
- list_add_tail(&evt->list, &wil->pending_wmi_ev);
- spin_unlock_irqrestore(&wil->wmi_ev_lock, flags);
- q = queue_work(wil->wmi_wq, &wil->wmi_event_worker);
- wil_dbg_wmi(wil, "queue_work -> %d\n", q);
+ if (immed_reply) {
+ wil_dbg_wmi(wil, "%s: Complete WMI 0x%04x\n",
+ __func__, wil->reply_id);
+ kfree(evt);
+ num_immed_reply++;
+ complete(&wil->wmi_call);
+ } else {
+ /* add to the pending list */
+ spin_lock_irqsave(&wil->wmi_ev_lock, flags);
+ list_add_tail(&evt->list, &wil->pending_wmi_ev);
+ spin_unlock_irqrestore(&wil->wmi_ev_lock, flags);
+ q = queue_work(wil->wmi_wq, &wil->wmi_event_worker);
+ wil_dbg_wmi(wil, "queue_work -> %d\n", q);
+ }
}
/* normally, 1 event per IRQ should be processed */
- wil_dbg_wmi(wil, "%s -> %d events queued\n", __func__, n);
+ wil_dbg_wmi(wil, "%s -> %d events queued, %d completed\n", __func__,
+ n - num_immed_reply, num_immed_reply);
}
int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len,
@@ -818,13 +890,16 @@
mutex_lock(&wil->wmi_mutex);
+ spin_lock(&wil->wmi_ev_lock);
+ wil->reply_id = reply_id;
+ wil->reply_buf = reply;
+ wil->reply_size = reply_size;
+ spin_unlock(&wil->wmi_ev_lock);
+
rc = __wmi_send(wil, cmdid, buf, len);
if (rc)
goto out;
- wil->reply_id = reply_id;
- wil->reply_buf = reply;
- wil->reply_size = reply_size;
remain = wait_for_completion_timeout(&wil->wmi_call,
msecs_to_jiffies(to_msec));
if (0 == remain) {
@@ -837,10 +912,14 @@
cmdid, reply_id,
to_msec - jiffies_to_msecs(remain));
}
+
+out:
+ spin_lock(&wil->wmi_ev_lock);
wil->reply_id = 0;
wil->reply_buf = NULL;
wil->reply_size = 0;
- out:
+ spin_unlock(&wil->wmi_ev_lock);
+
mutex_unlock(&wil->wmi_mutex);
return rc;
@@ -1184,7 +1263,8 @@
return 0;
}
-int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac, u16 reason)
+int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac, u16 reason,
+ bool full_disconnect)
{
int rc;
u16 reason_code;
@@ -1208,19 +1288,20 @@
return rc;
}
- /* call event handler manually after processing wmi_call,
- * to avoid deadlock - disconnect event handler acquires wil->mutex
- * while it is already held here
- */
- reason_code = le16_to_cpu(reply.evt.protocol_reason_status);
+ if (full_disconnect) {
+ /* call event handler manually after processing wmi_call,
+ * to avoid deadlock - disconnect event handler acquires
+ * wil->mutex while it is already held here
+ */
+ reason_code = le16_to_cpu(reply.evt.protocol_reason_status);
- wil_dbg_wmi(wil, "Disconnect %pM reason [proto %d wmi %d]\n",
- reply.evt.bssid, reason_code,
- reply.evt.disconnect_reason);
+ wil_dbg_wmi(wil, "Disconnect %pM reason [proto %d wmi %d]\n",
+ reply.evt.bssid, reason_code,
+ reply.evt.disconnect_reason);
- wil->sinfo_gen++;
- wil6210_disconnect(wil, reply.evt.bssid, reason_code, true);
-
+ wil->sinfo_gen++;
+ wil6210_disconnect(wil, reply.evt.bssid, reason_code, true);
+ }
return 0;
}
@@ -1348,14 +1429,11 @@
id, wil->reply_id);
/* check if someone waits for this event */
if (wil->reply_id && wil->reply_id == id) {
- if (wil->reply_buf) {
- memcpy(wil->reply_buf, wmi,
- min(len, wil->reply_size));
- } else {
- wmi_evt_call_handler(wil, id, evt_data,
- len - sizeof(*wmi));
- }
- wil_dbg_wmi(wil, "Complete WMI 0x%04x\n", id);
+ WARN_ON(wil->reply_buf);
+ wmi_evt_call_handler(wil, id, evt_data,
+ len - sizeof(*wmi));
+ wil_dbg_wmi(wil, "%s: Complete WMI 0x%04x\n",
+ __func__, id);
complete(&wil->wmi_call);
return;
}
diff --git a/drivers/net/wireless/atmel/at76c50x-usb.c b/drivers/net/wireless/atmel/at76c50x-usb.c
index dab2513..1efb1d6 100644
--- a/drivers/net/wireless/atmel/at76c50x-usb.c
+++ b/drivers/net/wireless/atmel/at76c50x-usb.c
@@ -2481,9 +2481,7 @@
dev_err(&interface->dev,
"error %d downloading internal firmware\n",
ret);
- goto exit;
}
- usb_put_dev(udev);
goto exit;
}
diff --git a/drivers/net/wireless/broadcom/b43/main.c b/drivers/net/wireless/broadcom/b43/main.c
index c279211..72380af 100644
--- a/drivers/net/wireless/broadcom/b43/main.c
+++ b/drivers/net/wireless/broadcom/b43/main.c
@@ -4375,12 +4375,10 @@
/* Synchronize and free the interrupt handlers. Unlock to avoid deadlocks. */
orig_dev = dev;
mutex_unlock(&wl->mutex);
- if (b43_bus_host_is_sdio(dev->dev)) {
+ if (b43_bus_host_is_sdio(dev->dev))
b43_sdio_free_irq(dev);
- } else {
- synchronize_irq(dev->dev->irq);
+ else
free_irq(dev->dev->irq, dev);
- }
mutex_lock(&wl->mutex);
dev = wl->current_dev;
if (!dev)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
index b98db8a..da0cdd3 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
@@ -27,8 +27,6 @@
#include <linux/mmc/sdio_func.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
-#include <linux/platform_device.h>
-#include <linux/platform_data/brcmfmac-sdio.h>
#include <linux/pm_runtime.h>
#include <linux/suspend.h>
#include <linux/errno.h>
@@ -46,7 +44,6 @@
#include "bus.h"
#include "debug.h"
#include "sdio.h"
-#include "of.h"
#include "core.h"
#include "common.h"
@@ -106,18 +103,18 @@
int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev)
{
+ struct brcmfmac_sdio_pd *pdata;
int ret = 0;
u8 data;
u32 addr, gpiocontrol;
unsigned long flags;
- if ((sdiodev->pdata) && (sdiodev->pdata->oob_irq_supported)) {
+ pdata = &sdiodev->settings->bus.sdio;
+ if (pdata->oob_irq_supported) {
brcmf_dbg(SDIO, "Enter, register OOB IRQ %d\n",
- sdiodev->pdata->oob_irq_nr);
- ret = request_irq(sdiodev->pdata->oob_irq_nr,
- brcmf_sdiod_oob_irqhandler,
- sdiodev->pdata->oob_irq_flags,
- "brcmf_oob_intr",
+ pdata->oob_irq_nr);
+ ret = request_irq(pdata->oob_irq_nr, brcmf_sdiod_oob_irqhandler,
+ pdata->oob_irq_flags, "brcmf_oob_intr",
&sdiodev->func[1]->dev);
if (ret != 0) {
brcmf_err("request_irq failed %d\n", ret);
@@ -129,7 +126,7 @@
sdiodev->irq_en = true;
spin_unlock_irqrestore(&sdiodev->irq_en_lock, flags);
- ret = enable_irq_wake(sdiodev->pdata->oob_irq_nr);
+ ret = enable_irq_wake(pdata->oob_irq_nr);
if (ret != 0) {
brcmf_err("enable_irq_wake failed %d\n", ret);
return ret;
@@ -158,7 +155,7 @@
/* redirect, configure and enable io for interrupt signal */
data = SDIO_SEPINT_MASK | SDIO_SEPINT_OE;
- if (sdiodev->pdata->oob_irq_flags & IRQF_TRIGGER_HIGH)
+ if (pdata->oob_irq_flags & IRQF_TRIGGER_HIGH)
data |= SDIO_SEPINT_ACT_HI;
brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, data, &ret);
@@ -176,9 +173,12 @@
int brcmf_sdiod_intr_unregister(struct brcmf_sdio_dev *sdiodev)
{
+ struct brcmfmac_sdio_pd *pdata;
+
brcmf_dbg(SDIO, "Entering\n");
- if ((sdiodev->pdata) && (sdiodev->pdata->oob_irq_supported)) {
+ pdata = &sdiodev->settings->bus.sdio;
+ if (pdata->oob_irq_supported) {
sdio_claim_host(sdiodev->func[1]);
brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, 0, NULL);
brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_IENx, 0, NULL);
@@ -187,11 +187,10 @@
if (sdiodev->oob_irq_requested) {
sdiodev->oob_irq_requested = false;
if (sdiodev->irq_wake) {
- disable_irq_wake(sdiodev->pdata->oob_irq_nr);
+ disable_irq_wake(pdata->oob_irq_nr);
sdiodev->irq_wake = false;
}
- free_irq(sdiodev->pdata->oob_irq_nr,
- &sdiodev->func[1]->dev);
+ free_irq(pdata->oob_irq_nr, &sdiodev->func[1]->dev);
sdiodev->irq_en = false;
}
} else {
@@ -523,7 +522,7 @@
target_list = pktlist;
/* for host with broken sg support, prepare a page aligned list */
__skb_queue_head_init(&local_list);
- if (sdiodev->pdata && sdiodev->pdata->broken_sg_support && !write) {
+ if (!write && sdiodev->settings->bus.sdio.broken_sg_support) {
req_sz = 0;
skb_queue_walk(pktlist, pkt_next)
req_sz += pkt_next->len;
@@ -630,7 +629,7 @@
}
}
- if (sdiodev->pdata && sdiodev->pdata->broken_sg_support && !write) {
+ if (!write && sdiodev->settings->bus.sdio.broken_sg_support) {
local_pkt_next = local_list.next;
orig_offset = 0;
skb_queue_walk(pktlist, pkt_next) {
@@ -901,7 +900,7 @@
return;
nents = max_t(uint, BRCMF_DEFAULT_RXGLOM_SIZE,
- sdiodev->bus_if->drvr->settings->sdiod_txglomsz);
+ sdiodev->settings->bus.sdio.txglomsz);
nents += (nents >> 4) + 1;
WARN_ON(nents > sdiodev->max_segment_count);
@@ -913,7 +912,7 @@
sdiodev->sg_support = false;
}
- sdiodev->txglomsz = sdiodev->bus_if->drvr->settings->sdiod_txglomsz;
+ sdiodev->txglomsz = sdiodev->settings->bus.sdio.txglomsz;
}
#ifdef CONFIG_PM_SLEEP
@@ -1103,8 +1102,6 @@
};
MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids);
-static struct brcmfmac_sdio_platform_data *brcmfmac_sdio_pdata;
-
static void brcmf_sdiod_acpi_set_power_manageable(struct device *dev,
int val)
@@ -1167,20 +1164,6 @@
dev_set_drvdata(&func->dev, bus_if);
dev_set_drvdata(&sdiodev->func[1]->dev, bus_if);
sdiodev->dev = &sdiodev->func[1]->dev;
- sdiodev->pdata = brcmfmac_sdio_pdata;
-
- if (!sdiodev->pdata)
- brcmf_of_probe(sdiodev);
-
-#ifdef CONFIG_PM_SLEEP
- /* wowl can be supported when KEEP_POWER is true and (WAKE_SDIO_IRQ
- * is true or when platform data OOB irq is true).
- */
- if ((sdio_get_host_pm_caps(sdiodev->func[1]) & MMC_PM_KEEP_POWER) &&
- ((sdio_get_host_pm_caps(sdiodev->func[1]) & MMC_PM_WAKE_SDIO_IRQ) ||
- (sdiodev->pdata && sdiodev->pdata->oob_irq_supported)))
- bus_if->wowl_supported = true;
-#endif
brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_DOWN);
@@ -1263,8 +1246,8 @@
sdio_flags = MMC_PM_KEEP_POWER;
if (sdiodev->wowl_enabled) {
- if (sdiodev->pdata->oob_irq_supported)
- enable_irq_wake(sdiodev->pdata->oob_irq_nr);
+ if (sdiodev->settings->bus.sdio.oob_irq_supported)
+ enable_irq_wake(sdiodev->settings->bus.sdio.oob_irq_nr);
else
sdio_flags |= MMC_PM_WAKE_SDIO_IRQ;
}
@@ -1296,7 +1279,7 @@
static struct sdio_driver brcmf_sdmmc_driver = {
.probe = brcmf_ops_sdio_probe,
.remove = brcmf_ops_sdio_remove,
- .name = BRCMFMAC_SDIO_PDATA_NAME,
+ .name = KBUILD_MODNAME,
.id_table = brcmf_sdmmc_ids,
.drv = {
.owner = THIS_MODULE,
@@ -1306,37 +1289,6 @@
},
};
-static int __init brcmf_sdio_pd_probe(struct platform_device *pdev)
-{
- brcmf_dbg(SDIO, "Enter\n");
-
- brcmfmac_sdio_pdata = dev_get_platdata(&pdev->dev);
-
- if (brcmfmac_sdio_pdata->power_on)
- brcmfmac_sdio_pdata->power_on();
-
- return 0;
-}
-
-static int brcmf_sdio_pd_remove(struct platform_device *pdev)
-{
- brcmf_dbg(SDIO, "Enter\n");
-
- if (brcmfmac_sdio_pdata->power_off)
- brcmfmac_sdio_pdata->power_off();
-
- sdio_unregister_driver(&brcmf_sdmmc_driver);
-
- return 0;
-}
-
-static struct platform_driver brcmf_sdio_pd = {
- .remove = brcmf_sdio_pd_remove,
- .driver = {
- .name = BRCMFMAC_SDIO_PDATA_NAME,
- }
-};
-
void brcmf_sdio_register(void)
{
int ret;
@@ -1350,19 +1302,6 @@
{
brcmf_dbg(SDIO, "Enter\n");
- if (brcmfmac_sdio_pdata)
- platform_driver_unregister(&brcmf_sdio_pd);
- else
- sdio_unregister_driver(&brcmf_sdmmc_driver);
+ sdio_unregister_driver(&brcmf_sdmmc_driver);
}
-void __init brcmf_sdio_init(void)
-{
- int ret;
-
- brcmf_dbg(SDIO, "Enter\n");
-
- ret = platform_driver_probe(&brcmf_sdio_pd, brcmf_sdio_pd_probe);
- if (ret == -ENODEV)
- brcmf_dbg(SDIO, "No platform data available.\n");
-}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
index 36093f9..8e02a47 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
@@ -43,6 +43,8 @@
BRCMF_PROTO_MSGBUF
};
+struct brcmf_mp_device;
+
struct brcmf_bus_dcmd {
char *name;
char *param;
@@ -217,7 +219,7 @@
void brcmf_rx_frame(struct device *dev, struct sk_buff *rxp);
/* Indication from bus module regarding presence/insertion of dongle. */
-int brcmf_attach(struct device *dev);
+int brcmf_attach(struct device *dev, struct brcmf_mp_device *settings);
/* Indication from bus module regarding removal/absence of dongle */
void brcmf_detach(struct device *dev);
/* Indication from bus module that dongle should be reset */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index d00c5c1..d5c2a27 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -72,8 +72,13 @@
#define RSN_AKM_NONE 0 /* None (IBSS) */
#define RSN_AKM_UNSPECIFIED 1 /* Over 802.1x */
#define RSN_AKM_PSK 2 /* Pre-shared Key */
+#define RSN_AKM_SHA256_1X 5 /* SHA256, 802.1X */
+#define RSN_AKM_SHA256_PSK 6 /* SHA256, Pre-shared Key */
#define RSN_CAP_LEN 2 /* Length of RSN capabilities */
-#define RSN_CAP_PTK_REPLAY_CNTR_MASK 0x000C
+#define RSN_CAP_PTK_REPLAY_CNTR_MASK (BIT(2) | BIT(3))
+#define RSN_CAP_MFPR_MASK BIT(6)
+#define RSN_CAP_MFPC_MASK BIT(7)
+#define RSN_PMKID_COUNT_LEN 2
#define VNDR_IE_CMD_LEN 4 /* length of the set command
* string :"add", "del" (+ NUL)
@@ -211,12 +216,19 @@
REG_RULE(5470-10, 5850+10, 80, 6, 20, 0), }
};
-static const u32 __wl_cipher_suites[] = {
+/* Note: brcmf_cipher_suites is an array of int defining which cipher suites
+ * are supported. A pointer to this array and the number of entries is passed
+ * on to upper layers. AES_CMAC defines whether or not the driver supports MFP.
+ * So the cipher suite AES_CMAC has to be the last one in the array, and when
+ * device does not support MFP then the number of suites will be decreased by 1
+ */
+static const u32 brcmf_cipher_suites[] = {
WLAN_CIPHER_SUITE_WEP40,
WLAN_CIPHER_SUITE_WEP104,
WLAN_CIPHER_SUITE_TKIP,
WLAN_CIPHER_SUITE_CCMP,
- WLAN_CIPHER_SUITE_AES_CMAC,
+ /* Keep as last entry: */
+ WLAN_CIPHER_SUITE_AES_CMAC
};
/* Vendor specific ie. id = 221, oui and type defines exact ie */
@@ -456,7 +468,7 @@
}
static s32
-brcmf_configure_arp_offload(struct brcmf_if *ifp, bool enable)
+brcmf_configure_arp_nd_offload(struct brcmf_if *ifp, bool enable)
{
s32 err;
u32 mode;
@@ -484,6 +496,15 @@
enable, mode);
}
+ err = brcmf_fil_iovar_int_set(ifp, "ndoe", enable);
+ if (err) {
+ brcmf_dbg(TRACE, "failed to configure (%d) ND offload err = %d\n",
+ enable, err);
+ err = 0;
+ } else
+ brcmf_dbg(TRACE, "successfully configured (%d) ND offload to 0x%x\n",
+ enable, mode);
+
return err;
}
@@ -564,8 +585,8 @@
}
/* wait for firmware event */
- err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_ADD,
- BRCMF_VIF_EVENT_TIMEOUT);
+ err = brcmf_cfg80211_wait_vif_event(cfg, BRCMF_E_IF_ADD,
+ BRCMF_VIF_EVENT_TIMEOUT);
brcmf_cfg80211_arm_vif_event(cfg, NULL);
if (!err) {
brcmf_err("timeout occurred\n");
@@ -1125,7 +1146,7 @@
/* Arm scan timeout timer */
mod_timer(&cfg->escan_timeout, jiffies +
- WL_ESCAN_TIMER_INTERVAL_MS * HZ / 1000);
+ BRCMF_ESCAN_TIMER_INTERVAL_MS * HZ / 1000);
return 0;
@@ -1524,7 +1545,7 @@
static s32
brcmf_set_wsec_mode(struct net_device *ndev,
- struct cfg80211_connect_params *sme, bool mfp)
+ struct cfg80211_connect_params *sme)
{
struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
struct brcmf_cfg80211_security *sec;
@@ -1583,10 +1604,7 @@
sme->privacy)
pval = AES_ENABLED;
- if (mfp)
- wsec = pval | gval | MFP_CAPABLE;
- else
- wsec = pval | gval;
+ wsec = pval | gval;
err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wsec", wsec);
if (err) {
brcmf_err("error (%d)\n", err);
@@ -1603,56 +1621,100 @@
static s32
brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
{
- struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
- struct brcmf_cfg80211_security *sec;
- s32 val = 0;
- s32 err = 0;
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ s32 val;
+ s32 err;
+ const struct brcmf_tlv *rsn_ie;
+ const u8 *ie;
+ u32 ie_len;
+ u32 offset;
+ u16 rsn_cap;
+ u32 mfp;
+ u16 count;
- if (sme->crypto.n_akm_suites) {
- err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev),
- "wpa_auth", &val);
- if (err) {
- brcmf_err("could not get wpa_auth (%d)\n", err);
- return err;
- }
- if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
- switch (sme->crypto.akm_suites[0]) {
- case WLAN_AKM_SUITE_8021X:
- val = WPA_AUTH_UNSPECIFIED;
- break;
- case WLAN_AKM_SUITE_PSK:
- val = WPA_AUTH_PSK;
- break;
- default:
- brcmf_err("invalid cipher group (%d)\n",
- sme->crypto.cipher_group);
- return -EINVAL;
- }
- } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
- switch (sme->crypto.akm_suites[0]) {
- case WLAN_AKM_SUITE_8021X:
- val = WPA2_AUTH_UNSPECIFIED;
- break;
- case WLAN_AKM_SUITE_PSK:
- val = WPA2_AUTH_PSK;
- break;
- default:
- brcmf_err("invalid cipher group (%d)\n",
- sme->crypto.cipher_group);
- return -EINVAL;
- }
- }
+ if (!sme->crypto.n_akm_suites)
+ return 0;
- brcmf_dbg(CONN, "setting wpa_auth to %d\n", val);
- err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev),
- "wpa_auth", val);
- if (err) {
- brcmf_err("could not set wpa_auth (%d)\n", err);
- return err;
+ err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev), "wpa_auth", &val);
+ if (err) {
+ brcmf_err("could not get wpa_auth (%d)\n", err);
+ return err;
+ }
+ if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
+ switch (sme->crypto.akm_suites[0]) {
+ case WLAN_AKM_SUITE_8021X:
+ val = WPA_AUTH_UNSPECIFIED;
+ break;
+ case WLAN_AKM_SUITE_PSK:
+ val = WPA_AUTH_PSK;
+ break;
+ default:
+ brcmf_err("invalid cipher group (%d)\n",
+ sme->crypto.cipher_group);
+ return -EINVAL;
+ }
+ } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
+ switch (sme->crypto.akm_suites[0]) {
+ case WLAN_AKM_SUITE_8021X:
+ val = WPA2_AUTH_UNSPECIFIED;
+ break;
+ case WLAN_AKM_SUITE_8021X_SHA256:
+ val = WPA2_AUTH_1X_SHA256;
+ break;
+ case WLAN_AKM_SUITE_PSK_SHA256:
+ val = WPA2_AUTH_PSK_SHA256;
+ break;
+ case WLAN_AKM_SUITE_PSK:
+ val = WPA2_AUTH_PSK;
+ break;
+ default:
+ brcmf_err("invalid cipher group (%d)\n",
+ sme->crypto.cipher_group);
+ return -EINVAL;
}
}
- sec = &profile->sec;
- sec->wpa_auth = sme->crypto.akm_suites[0];
+
+ if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP))
+ goto skip_mfp_config;
+ /* The MFP mode (1 or 2) needs to be determined, parse IEs. The
+ * IE will not be verified, just a quick search for MFP config
+ */
+ rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie, sme->ie_len,
+ WLAN_EID_RSN);
+ if (!rsn_ie)
+ goto skip_mfp_config;
+ ie = (const u8 *)rsn_ie;
+ ie_len = rsn_ie->len + TLV_HDR_LEN;
+ /* Skip unicast suite */
+ offset = TLV_HDR_LEN + WPA_IE_VERSION_LEN + WPA_IE_MIN_OUI_LEN;
+ if (offset + WPA_IE_SUITE_COUNT_LEN >= ie_len)
+ goto skip_mfp_config;
+ /* Skip multicast suite */
+ count = ie[offset] + (ie[offset + 1] << 8);
+ offset += WPA_IE_SUITE_COUNT_LEN + (count * WPA_IE_MIN_OUI_LEN);
+ if (offset + WPA_IE_SUITE_COUNT_LEN >= ie_len)
+ goto skip_mfp_config;
+ /* Skip auth key management suite(s) */
+ count = ie[offset] + (ie[offset + 1] << 8);
+ offset += WPA_IE_SUITE_COUNT_LEN + (count * WPA_IE_MIN_OUI_LEN);
+ if (offset + WPA_IE_SUITE_COUNT_LEN > ie_len)
+ goto skip_mfp_config;
+ /* Ready to read capabilities */
+ mfp = BRCMF_MFP_NONE;
+ rsn_cap = ie[offset] + (ie[offset + 1] << 8);
+ if (rsn_cap & RSN_CAP_MFPR_MASK)
+ mfp = BRCMF_MFP_REQUIRED;
+ else if (rsn_cap & RSN_CAP_MFPC_MASK)
+ mfp = BRCMF_MFP_CAPABLE;
+ brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "mfp", mfp);
+
+skip_mfp_config:
+ brcmf_dbg(CONN, "setting wpa_auth to %d\n", val);
+ err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val);
+ if (err) {
+ brcmf_err("could not set wpa_auth (%d)\n", err);
+ return err;
+ }
return err;
}
@@ -1818,7 +1880,7 @@
goto done;
}
- err = brcmf_set_wsec_mode(ndev, sme, sme->mfp == NL80211_MFP_REQUIRED);
+ err = brcmf_set_wsec_mode(ndev, sme);
if (err) {
brcmf_err("wl_set_set_cipher failed (%d)\n", err);
goto done;
@@ -2064,98 +2126,54 @@
}
static s32
-brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev,
- u8 key_idx, const u8 *mac_addr, struct key_params *params)
+brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
+ u8 key_idx, bool pairwise, const u8 *mac_addr)
{
struct brcmf_if *ifp = netdev_priv(ndev);
- struct brcmf_wsec_key key;
- s32 err = 0;
- u8 keybuf[8];
+ struct brcmf_wsec_key *key;
+ s32 err;
- memset(&key, 0, sizeof(key));
- key.index = (u32) key_idx;
- /* Instead of bcast for ea address for default wep keys,
- driver needs it to be Null */
- if (!is_multicast_ether_addr(mac_addr))
- memcpy((char *)&key.ea, (void *)mac_addr, ETH_ALEN);
- key.len = (u32) params->key_len;
- /* check for key index change */
- if (key.len == 0) {
- /* key delete */
- err = send_key_to_dongle(ifp, &key);
- if (err)
- brcmf_err("key delete error (%d)\n", err);
- } else {
- if (key.len > sizeof(key.data)) {
- brcmf_err("Invalid key length (%d)\n", key.len);
- return -EINVAL;
- }
+ brcmf_dbg(TRACE, "Enter\n");
+ brcmf_dbg(CONN, "key index (%d)\n", key_idx);
- brcmf_dbg(CONN, "Setting the key index %d\n", key.index);
- memcpy(key.data, params->key, key.len);
+ if (!check_vif_up(ifp->vif))
+ return -EIO;
- if (!brcmf_is_apmode(ifp->vif) &&
- (params->cipher == WLAN_CIPHER_SUITE_TKIP)) {
- brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
- memcpy(keybuf, &key.data[24], sizeof(keybuf));
- memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
- memcpy(&key.data[16], keybuf, sizeof(keybuf));
- }
-
- /* if IW_ENCODE_EXT_RX_SEQ_VALID set */
- if (params->seq && params->seq_len == 6) {
- /* rx iv */
- u8 *ivptr;
- ivptr = (u8 *) params->seq;
- key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) |
- (ivptr[3] << 8) | ivptr[2];
- key.rxiv.lo = (ivptr[1] << 8) | ivptr[0];
- key.iv_initialized = true;
- }
-
- switch (params->cipher) {
- case WLAN_CIPHER_SUITE_WEP40:
- key.algo = CRYPTO_ALGO_WEP1;
- brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
- break;
- case WLAN_CIPHER_SUITE_WEP104:
- key.algo = CRYPTO_ALGO_WEP128;
- brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
- break;
- case WLAN_CIPHER_SUITE_TKIP:
- key.algo = CRYPTO_ALGO_TKIP;
- brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
- break;
- case WLAN_CIPHER_SUITE_AES_CMAC:
- key.algo = CRYPTO_ALGO_AES_CCM;
- brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
- break;
- case WLAN_CIPHER_SUITE_CCMP:
- key.algo = CRYPTO_ALGO_AES_CCM;
- brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
- break;
- default:
- brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
- return -EINVAL;
- }
- err = send_key_to_dongle(ifp, &key);
- if (err)
- brcmf_err("wsec_key error (%d)\n", err);
+ if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
+ /* we ignore this key index in this case */
+ return -EINVAL;
}
+
+ key = &ifp->vif->profile.key[key_idx];
+
+ if (key->algo == CRYPTO_ALGO_OFF) {
+ brcmf_dbg(CONN, "Ignore clearing of (never configured) key\n");
+ return -EINVAL;
+ }
+
+ memset(key, 0, sizeof(*key));
+ key->index = (u32)key_idx;
+ key->flags = BRCMF_PRIMARY_KEY;
+
+ /* Clear the key/index */
+ err = send_key_to_dongle(ifp, key);
+
+ brcmf_dbg(TRACE, "Exit\n");
return err;
}
static s32
brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
- u8 key_idx, bool pairwise, const u8 *mac_addr,
- struct key_params *params)
+ u8 key_idx, bool pairwise, const u8 *mac_addr,
+ struct key_params *params)
{
struct brcmf_if *ifp = netdev_priv(ndev);
struct brcmf_wsec_key *key;
s32 val;
s32 wsec;
- s32 err = 0;
+ s32 err;
u8 keybuf[8];
+ bool ext_key;
brcmf_dbg(TRACE, "Enter\n");
brcmf_dbg(CONN, "key index (%d)\n", key_idx);
@@ -2168,27 +2186,32 @@
return -EINVAL;
}
- if (mac_addr &&
- (params->cipher != WLAN_CIPHER_SUITE_WEP40) &&
- (params->cipher != WLAN_CIPHER_SUITE_WEP104)) {
- brcmf_dbg(TRACE, "Exit");
- return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params);
+ if (params->key_len == 0)
+ return brcmf_cfg80211_del_key(wiphy, ndev, key_idx, pairwise,
+ mac_addr);
+
+ if (params->key_len > sizeof(key->data)) {
+ brcmf_err("Too long key length (%u)\n", params->key_len);
+ return -EINVAL;
+ }
+
+ ext_key = false;
+ if (mac_addr && (params->cipher != WLAN_CIPHER_SUITE_WEP40) &&
+ (params->cipher != WLAN_CIPHER_SUITE_WEP104)) {
+ brcmf_dbg(TRACE, "Ext key, mac %pM", mac_addr);
+ ext_key = true;
}
key = &ifp->vif->profile.key[key_idx];
memset(key, 0, sizeof(*key));
-
- if (params->key_len > sizeof(key->data)) {
- brcmf_err("Too long key length (%u)\n", params->key_len);
- err = -EINVAL;
- goto done;
- }
+ if ((ext_key) && (!is_multicast_ether_addr(mac_addr)))
+ memcpy((char *)&key->ea, (void *)mac_addr, ETH_ALEN);
key->len = params->key_len;
key->index = key_idx;
-
memcpy(key->data, params->key, key->len);
+ if (!ext_key)
+ key->flags = BRCMF_PRIMARY_KEY;
- key->flags = BRCMF_PRIMARY_KEY;
switch (params->cipher) {
case WLAN_CIPHER_SUITE_WEP40:
key->algo = CRYPTO_ALGO_WEP1;
@@ -2228,7 +2251,7 @@
}
err = send_key_to_dongle(ifp, key);
- if (err)
+ if (ext_key || err)
goto done;
err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
@@ -2249,41 +2272,10 @@
}
static s32
-brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
- u8 key_idx, bool pairwise, const u8 *mac_addr)
-{
- struct brcmf_if *ifp = netdev_priv(ndev);
- struct brcmf_wsec_key key;
- s32 err = 0;
-
- brcmf_dbg(TRACE, "Enter\n");
- if (!check_vif_up(ifp->vif))
- return -EIO;
-
- if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
- /* we ignore this key index in this case */
- return -EINVAL;
- }
-
- memset(&key, 0, sizeof(key));
-
- key.index = (u32) key_idx;
- key.flags = BRCMF_PRIMARY_KEY;
- key.algo = CRYPTO_ALGO_OFF;
-
- brcmf_dbg(CONN, "key index (%d)\n", key_idx);
-
- /* Set the new key/index */
- err = send_key_to_dongle(ifp, &key);
-
- brcmf_dbg(TRACE, "Exit\n");
- return err;
-}
-
-static s32
-brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
- u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie,
- void (*callback) (void *cookie, struct key_params * params))
+brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev, u8 key_idx,
+ bool pairwise, const u8 *mac_addr, void *cookie,
+ void (*callback)(void *cookie,
+ struct key_params *params))
{
struct key_params params;
struct brcmf_if *ifp = netdev_priv(ndev);
@@ -2335,8 +2327,15 @@
static s32
brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,
- struct net_device *ndev, u8 key_idx)
+ struct net_device *ndev, u8 key_idx)
{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+
+ brcmf_dbg(TRACE, "Enter key_idx %d\n", key_idx);
+
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP))
+ return 0;
+
brcmf_dbg(INFO, "Not supported\n");
return -EOPNOTSUPP;
@@ -3020,7 +3019,7 @@
list = (struct brcmf_scan_results *)
cfg->escan_info.escan_buf;
- if (bi_length > WL_ESCAN_BUF_SIZE - list->buflen) {
+ if (bi_length > BRCMF_ESCAN_BUF_SIZE - list->buflen) {
brcmf_err("Buffer is too small: ignoring\n");
goto exit;
}
@@ -3033,8 +3032,8 @@
bss_info_le))
goto exit;
}
- memcpy(&(cfg->escan_info.escan_buf[list->buflen]),
- bss_info_le, bi_length);
+ memcpy(&cfg->escan_info.escan_buf[list->buflen], bss_info_le,
+ bi_length);
list->version = le32_to_cpu(bss_info_le->version);
list->buflen += bi_length;
list->count++;
@@ -3092,6 +3091,11 @@
brcmf_dbg(SCAN, "Enter\n");
+ if (e->datalen < (sizeof(*pfn_result) + sizeof(*netinfo))) {
+ brcmf_dbg(SCAN, "Event data to small. Ignore\n");
+ return 0;
+ }
+
if (e->event_code == BRCMF_E_PFN_NET_LOST) {
brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n");
return 0;
@@ -3415,6 +3419,11 @@
brcmf_dbg(SCAN, "Enter\n");
+ if (e->datalen < (sizeof(*pfn_result) + sizeof(*netinfo))) {
+ brcmf_dbg(SCAN, "Event data to small. Ignore\n");
+ return 0;
+ }
+
pfn_result = (struct brcmf_pno_scanresults_le *)data;
if (e->event_code == BRCMF_E_PFN_NET_LOST) {
@@ -3507,6 +3516,10 @@
else
wakeup_data.net_detect = cfg->wowl.nd_info;
}
+ if (wakeind & BRCMF_WOWL_GTK_FAILURE) {
+ brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_GTK_FAILURE\n");
+ wakeup_data.gtk_rekey_failure = true;
+ }
} else {
wakeup = NULL;
}
@@ -3533,7 +3546,8 @@
brcmf_report_wowl_wakeind(wiphy, ifp);
brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0);
brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0);
- brcmf_configure_arp_offload(ifp, true);
+ if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ARP_ND))
+ brcmf_configure_arp_nd_offload(ifp, true);
brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM,
cfg->wowl.pre_pmmode);
cfg->wowl.active = false;
@@ -3557,7 +3571,8 @@
brcmf_dbg(TRACE, "Suspend, wowl config.\n");
- brcmf_configure_arp_offload(ifp, false);
+ if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ARP_ND))
+ brcmf_configure_arp_nd_offload(ifp, false);
brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->wowl.pre_pmmode);
brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX);
@@ -3588,6 +3603,8 @@
brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
brcmf_wowl_nd_results);
}
+ if (wowl->gtk_rekey_failure)
+ wowl_config |= BRCMF_WOWL_GTK_FAILURE;
if (!test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))
wowl_config |= BRCMF_WOWL_UNASSOC;
@@ -3818,7 +3835,7 @@
u32 auth = 0; /* d11 open authentication */
u16 count;
s32 err = 0;
- s32 len = 0;
+ s32 len;
u32 i;
u32 wsec;
u32 pval = 0;
@@ -3828,6 +3845,7 @@
u8 *data;
u16 rsn_cap;
u32 wme_bss_disable;
+ u32 mfp;
brcmf_dbg(TRACE, "Enter\n");
if (wpa_ie == NULL)
@@ -3942,19 +3960,53 @@
is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) :
(wpa_auth |= WPA_AUTH_PSK);
break;
+ case RSN_AKM_SHA256_PSK:
+ brcmf_dbg(TRACE, "RSN_AKM_MFP_PSK\n");
+ wpa_auth |= WPA2_AUTH_PSK_SHA256;
+ break;
+ case RSN_AKM_SHA256_1X:
+ brcmf_dbg(TRACE, "RSN_AKM_MFP_1X\n");
+ wpa_auth |= WPA2_AUTH_1X_SHA256;
+ break;
default:
brcmf_err("Ivalid key mgmt info\n");
}
offset++;
}
+ mfp = BRCMF_MFP_NONE;
if (is_rsn_ie) {
wme_bss_disable = 1;
if ((offset + RSN_CAP_LEN) <= len) {
rsn_cap = data[offset] + (data[offset + 1] << 8);
if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK)
wme_bss_disable = 0;
+ if (rsn_cap & RSN_CAP_MFPR_MASK) {
+ brcmf_dbg(TRACE, "MFP Required\n");
+ mfp = BRCMF_MFP_REQUIRED;
+ /* Firmware only supports mfp required in
+ * combination with WPA2_AUTH_PSK_SHA256 or
+ * WPA2_AUTH_1X_SHA256.
+ */
+ if (!(wpa_auth & (WPA2_AUTH_PSK_SHA256 |
+ WPA2_AUTH_1X_SHA256))) {
+ err = -EINVAL;
+ goto exit;
+ }
+ /* Firmware has requirement that WPA2_AUTH_PSK/
+ * WPA2_AUTH_UNSPECIFIED be set, if SHA256 OUI
+ * is to be included in the rsn ie.
+ */
+ if (wpa_auth & WPA2_AUTH_PSK_SHA256)
+ wpa_auth |= WPA2_AUTH_PSK;
+ else if (wpa_auth & WPA2_AUTH_1X_SHA256)
+ wpa_auth |= WPA2_AUTH_UNSPECIFIED;
+ } else if (rsn_cap & RSN_CAP_MFPC_MASK) {
+ brcmf_dbg(TRACE, "MFP Capable\n");
+ mfp = BRCMF_MFP_CAPABLE;
+ }
}
+ offset += RSN_CAP_LEN;
/* set wme_bss_disable to sync RSN Capabilities */
err = brcmf_fil_bsscfg_int_set(ifp, "wme_bss_disable",
wme_bss_disable);
@@ -3962,6 +4014,21 @@
brcmf_err("wme_bss_disable error %d\n", err);
goto exit;
}
+
+ /* Skip PMKID cnt as it is know to be 0 for AP. */
+ offset += RSN_PMKID_COUNT_LEN;
+
+ /* See if there is BIP wpa suite left for MFP */
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP) &&
+ ((offset + WPA_IE_MIN_OUI_LEN) <= len)) {
+ err = brcmf_fil_bsscfg_data_set(ifp, "bip",
+ &data[offset],
+ WPA_IE_MIN_OUI_LEN);
+ if (err < 0) {
+ brcmf_err("bip error %d\n", err);
+ goto exit;
+ }
+ }
}
/* FOR WPS , set SES_OW_ENABLED */
wsec = (pval | gval | SES_OW_ENABLED);
@@ -3978,6 +4045,16 @@
brcmf_err("wsec error %d\n", err);
goto exit;
}
+ /* Configure MFP, this needs to go after wsec otherwise the wsec command
+ * will overwrite the values set by MFP
+ */
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP)) {
+ err = brcmf_fil_bsscfg_int_set(ifp, "mfp", mfp);
+ if (err < 0) {
+ brcmf_err("mfp error %d\n", err);
+ goto exit;
+ }
+ }
/* set upper-layer auth */
err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_auth);
if (err < 0) {
@@ -4326,7 +4403,7 @@
if (!mbss) {
brcmf_set_mpc(ifp, 0);
- brcmf_configure_arp_offload(ifp, false);
+ brcmf_configure_arp_nd_offload(ifp, false);
}
/* find the RSN_IE */
@@ -4472,7 +4549,7 @@
exit:
if ((err) && (!mbss)) {
brcmf_set_mpc(ifp, 1);
- brcmf_configure_arp_offload(ifp, true);
+ brcmf_configure_arp_nd_offload(ifp, true);
}
return err;
}
@@ -4530,7 +4607,7 @@
brcmf_err("bss_enable config failed %d\n", err);
}
brcmf_set_mpc(ifp, 1);
- brcmf_configure_arp_offload(ifp, true);
+ brcmf_configure_arp_nd_offload(ifp, true);
clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
brcmf_net_setcarrier(ifp, false);
@@ -4855,7 +4932,32 @@
return ret;
}
-static struct cfg80211_ops wl_cfg80211_ops = {
+#ifdef CONFIG_PM
+static int
+brcmf_cfg80211_set_rekey_data(struct wiphy *wiphy, struct net_device *ndev,
+ struct cfg80211_gtk_rekey_data *gtk)
+{
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_gtk_keyinfo_le gtk_le;
+ int ret;
+
+ brcmf_dbg(TRACE, "Enter, bssidx=%d\n", ifp->bsscfgidx);
+
+ memcpy(gtk_le.kck, gtk->kck, sizeof(gtk_le.kck));
+ memcpy(gtk_le.kek, gtk->kek, sizeof(gtk_le.kek));
+ memcpy(gtk_le.replay_counter, gtk->replay_ctr,
+ sizeof(gtk_le.replay_counter));
+
+ ret = brcmf_fil_iovar_data_set(ifp, "gtk_key_info", >k_le,
+ sizeof(gtk_le));
+ if (ret < 0)
+ brcmf_err("gtk_key_info iovar failed: ret=%d\n", ret);
+
+ return ret;
+}
+#endif
+
+static struct cfg80211_ops brcmf_cfg80211_ops = {
.add_virtual_intf = brcmf_cfg80211_add_iface,
.del_virtual_intf = brcmf_cfg80211_del_iface,
.change_virtual_intf = brcmf_cfg80211_change_iface,
@@ -5402,14 +5504,14 @@
{
kfree(cfg->conf);
cfg->conf = NULL;
- kfree(cfg->escan_ioctl_buf);
- cfg->escan_ioctl_buf = NULL;
kfree(cfg->extra_buf);
cfg->extra_buf = NULL;
kfree(cfg->wowl.nd);
cfg->wowl.nd = NULL;
kfree(cfg->wowl.nd_info);
cfg->wowl.nd_info = NULL;
+ kfree(cfg->escan_info.escan_buf);
+ cfg->escan_info.escan_buf = NULL;
}
static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg)
@@ -5417,9 +5519,6 @@
cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL);
if (!cfg->conf)
goto init_priv_mem_out;
- cfg->escan_ioctl_buf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
- if (!cfg->escan_ioctl_buf)
- goto init_priv_mem_out;
cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
if (!cfg->extra_buf)
goto init_priv_mem_out;
@@ -5431,6 +5530,9 @@
GFP_KERNEL);
if (!cfg->wowl.nd_info)
goto init_priv_mem_out;
+ cfg->escan_info.escan_buf = kzalloc(BRCMF_ESCAN_BUF_SIZE, GFP_KERNEL);
+ if (!cfg->escan_info.escan_buf)
+ goto init_priv_mem_out;
return 0;
@@ -6120,19 +6222,18 @@
{
#ifdef CONFIG_PM
struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
- s32 err;
- u32 wowl_cap;
if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) {
- err = brcmf_fil_iovar_int_get(ifp, "wowl_cap", &wowl_cap);
- if (!err) {
- if (wowl_cap & BRCMF_WOWL_PFN_FOUND) {
- brcmf_wowlan_support.flags |=
- WIPHY_WOWLAN_NET_DETECT;
- init_waitqueue_head(&cfg->wowl.nd_data_wait);
- }
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ND)) {
+ brcmf_wowlan_support.flags |= WIPHY_WOWLAN_NET_DETECT;
+ init_waitqueue_head(&cfg->wowl.nd_data_wait);
}
}
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK)) {
+ brcmf_wowlan_support.flags |= WIPHY_WOWLAN_SUPPORTS_GTK_REKEY;
+ brcmf_wowlan_support.flags |= WIPHY_WOWLAN_GTK_REKEY_FAILURE;
+ }
+
wiphy->wowlan = &brcmf_wowlan_support;
#endif
}
@@ -6174,8 +6275,10 @@
wiphy->n_addresses = i;
wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
- wiphy->cipher_suites = __wl_cipher_suites;
- wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
+ wiphy->cipher_suites = brcmf_cipher_suites;
+ wiphy->n_cipher_suites = ARRAY_SIZE(brcmf_cipher_suites);
+ if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP))
+ wiphy->n_cipher_suites--;
wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT |
WIPHY_FLAG_OFFCHAN_TX |
WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
@@ -6277,7 +6380,7 @@
if (err)
goto default_conf_out;
- brcmf_configure_arp_offload(ifp, true);
+ brcmf_configure_arp_nd_offload(ifp, true);
cfg->dongle_up = true;
default_conf_out:
@@ -6395,8 +6498,9 @@
return armed;
}
-int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg,
- u8 action, ulong timeout)
+
+int brcmf_cfg80211_wait_vif_event(struct brcmf_cfg80211_info *cfg,
+ u8 action, ulong timeout)
{
struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
@@ -6404,28 +6508,85 @@
vif_event_equals(event, action), timeout);
}
+static s32 brcmf_translate_country_code(struct brcmf_pub *drvr, char alpha2[2],
+ struct brcmf_fil_country_le *ccreq)
+{
+ struct brcmfmac_pd_cc *country_codes;
+ struct brcmfmac_pd_cc_entry *cc;
+ s32 found_index;
+ int i;
+
+ country_codes = drvr->settings->country_codes;
+ if (!country_codes) {
+ brcmf_dbg(TRACE, "No country codes configured for device\n");
+ return -EINVAL;
+ }
+
+ if ((alpha2[0] == ccreq->country_abbrev[0]) &&
+ (alpha2[1] == ccreq->country_abbrev[1])) {
+ brcmf_dbg(TRACE, "Country code already set\n");
+ return -EAGAIN;
+ }
+
+ found_index = -1;
+ for (i = 0; i < country_codes->table_size; i++) {
+ cc = &country_codes->table[i];
+ if ((cc->iso3166[0] == '\0') && (found_index == -1))
+ found_index = i;
+ if ((cc->iso3166[0] == alpha2[0]) &&
+ (cc->iso3166[1] == alpha2[1])) {
+ found_index = i;
+ break;
+ }
+ }
+ if (found_index == -1) {
+ brcmf_dbg(TRACE, "No country code match found\n");
+ return -EINVAL;
+ }
+ memset(ccreq, 0, sizeof(*ccreq));
+ ccreq->rev = cpu_to_le32(country_codes->table[found_index].rev);
+ memcpy(ccreq->ccode, country_codes->table[found_index].cc,
+ BRCMF_COUNTRY_BUF_SZ);
+ ccreq->country_abbrev[0] = alpha2[0];
+ ccreq->country_abbrev[1] = alpha2[1];
+ ccreq->country_abbrev[2] = 0;
+
+ return 0;
+}
+
static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy,
struct regulatory_request *req)
{
struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
struct brcmf_fil_country_le ccreq;
+ s32 err;
int i;
- brcmf_dbg(TRACE, "enter: initiator=%d, alpha=%c%c\n", req->initiator,
- req->alpha2[0], req->alpha2[1]);
-
/* ignore non-ISO3166 country codes */
for (i = 0; i < sizeof(req->alpha2); i++)
if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') {
- brcmf_err("not a ISO3166 code\n");
+ brcmf_err("not a ISO3166 code (0x%02x 0x%02x)\n",
+ req->alpha2[0], req->alpha2[1]);
return;
}
- memset(&ccreq, 0, sizeof(ccreq));
- ccreq.rev = cpu_to_le32(-1);
- memcpy(ccreq.ccode, req->alpha2, sizeof(req->alpha2));
- if (brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq))) {
- brcmf_err("firmware rejected country setting\n");
+
+ brcmf_dbg(TRACE, "Enter: initiator=%d, alpha=%c%c\n", req->initiator,
+ req->alpha2[0], req->alpha2[1]);
+
+ err = brcmf_fil_iovar_data_get(ifp, "country", &ccreq, sizeof(ccreq));
+ if (err) {
+ brcmf_err("Country code iovar returned err = %d\n", err);
+ return;
+ }
+
+ err = brcmf_translate_country_code(ifp->drvr, req->alpha2, &ccreq);
+ if (err)
+ return;
+
+ err = brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq));
+ if (err) {
+ brcmf_err("Firmware rejected country setting\n");
return;
}
brcmf_setup_wiphybands(wiphy);
@@ -6461,6 +6622,7 @@
struct net_device *ndev = brcmf_get_ifp(drvr, 0)->ndev;
struct brcmf_cfg80211_info *cfg;
struct wiphy *wiphy;
+ struct cfg80211_ops *ops;
struct brcmf_cfg80211_vif *vif;
struct brcmf_if *ifp;
s32 err = 0;
@@ -6472,8 +6634,17 @@
return NULL;
}
+ ops = kzalloc(sizeof(*ops), GFP_KERNEL);
+ if (!ops)
+ return NULL;
+
+ memcpy(ops, &brcmf_cfg80211_ops, sizeof(*ops));
ifp = netdev_priv(ndev);
- wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info));
+#ifdef CONFIG_PM
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK))
+ ops->set_rekey_data = brcmf_cfg80211_set_rekey_data;
+#endif
+ wiphy = wiphy_new(ops, sizeof(struct brcmf_cfg80211_info));
if (!wiphy) {
brcmf_err("Could not allocate wiphy device\n");
return NULL;
@@ -6483,6 +6654,7 @@
cfg = wiphy_priv(wiphy);
cfg->wiphy = wiphy;
+ cfg->ops = ops;
cfg->pub = drvr;
init_vif_event(&cfg->vif_event);
INIT_LIST_HEAD(&cfg->vif_list);
@@ -6593,7 +6765,8 @@
if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_RANDOM_MAC)) {
wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR;
#ifdef CONFIG_PM
- if (wiphy->wowlan->flags & WIPHY_WOWLAN_NET_DETECT)
+ if (wiphy->wowlan &&
+ wiphy->wowlan->flags & WIPHY_WOWLAN_NET_DETECT)
wiphy->features |= NL80211_FEATURE_ND_RANDOM_MAC_ADDR;
#endif
}
@@ -6608,6 +6781,7 @@
ifp->vif = NULL;
wiphy_out:
brcmf_free_wiphy(wiphy);
+ kfree(ops);
return NULL;
}
@@ -6618,6 +6792,7 @@
brcmf_btcoex_detach(cfg);
wiphy_unregister(cfg->wiphy);
+ kfree(cfg->ops);
wl_deinit_priv(cfg);
brcmf_free_wiphy(cfg->wiphy);
}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
index 40efb53..95e35bc 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
@@ -28,8 +28,11 @@
#define WL_ROAM_TRIGGER_LEVEL -75
#define WL_ROAM_DELTA 20
-#define WL_ESCAN_BUF_SIZE (1024 * 64)
-#define WL_ESCAN_TIMER_INTERVAL_MS 10000 /* E-Scan timeout */
+/* Keep BRCMF_ESCAN_BUF_SIZE below 64K (65536). Allocing over 64K can be
+ * problematic on some systems and should be avoided.
+ */
+#define BRCMF_ESCAN_BUF_SIZE 65000
+#define BRCMF_ESCAN_TIMER_INTERVAL_MS 10000 /* E-Scan timeout */
#define WL_ESCAN_ACTION_START 1
#define WL_ESCAN_ACTION_CONTINUE 2
@@ -69,7 +72,7 @@
#define BRCMF_VNDR_IE_P2PAF_SHIFT 12
-#define BRCMF_MAX_DEFAULT_KEYS 4
+#define BRCMF_MAX_DEFAULT_KEYS 6
/* beacon loss timeout defaults */
#define BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_ON 2
@@ -104,7 +107,6 @@
u32 auth_type;
u32 cipher_pairwise;
u32 cipher_group;
- u32 wpa_auth;
};
/**
@@ -205,7 +207,7 @@
struct escan_info {
u32 escan_state;
- u8 escan_buf[WL_ESCAN_BUF_SIZE];
+ u8 *escan_buf;
struct wiphy *wiphy;
struct brcmf_if *ifp;
s32 (*run)(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
@@ -253,6 +255,7 @@
* struct brcmf_cfg80211_info - dongle private data of cfg80211 interface
*
* @wiphy: wiphy object for cfg80211 interface.
+ * @ops: pointer to copy of ops as registered with wiphy object.
* @conf: dongle configuration.
* @p2p: peer-to-peer specific information.
* @btcoex: Bluetooth coexistence information.
@@ -278,7 +281,6 @@
* @escan_info: escan information.
* @escan_timeout: Timer for catch scan timeout.
* @escan_timeout_work: scan timeout worker.
- * @escan_ioctl_buf: dongle command buffer for escan commands.
* @vif_list: linked list of vif instances.
* @vif_cnt: number of vif instances.
* @vif_event: vif event signalling.
@@ -286,6 +288,7 @@
*/
struct brcmf_cfg80211_info {
struct wiphy *wiphy;
+ struct cfg80211_ops *ops;
struct brcmf_cfg80211_conf *conf;
struct brcmf_p2p_info p2p;
struct brcmf_btcoex_info *btcoex;
@@ -309,7 +312,6 @@
struct escan_info escan_info;
struct timer_list escan_timeout;
struct work_struct escan_timeout_work;
- u8 *escan_ioctl_buf;
struct list_head vif_list;
struct brcmf_cfg80211_vif_event vif_event;
struct completion vif_disabled;
@@ -402,8 +404,8 @@
void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg,
struct brcmf_cfg80211_vif *vif);
bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg);
-int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg,
- u8 action, ulong timeout);
+int brcmf_cfg80211_wait_vif_event(struct brcmf_cfg80211_info *cfg,
+ u8 action, ulong timeout);
s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
struct brcmf_if *ifp, bool aborted,
bool fw_abort);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
index cfee477..9e909e3 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
@@ -27,6 +27,11 @@
#include "fwil_types.h"
#include "tracepoint.h"
#include "common.h"
+#include "of.h"
+
+MODULE_AUTHOR("Broadcom Corporation");
+MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver.");
+MODULE_LICENSE("Dual BSD/GPL");
const u8 ALLFFMAC[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
@@ -75,6 +80,7 @@
MODULE_PARM_DESC(ignore_probe_fail, "always succeed probe for debugging");
#endif
+static struct brcmfmac_platform_data *brcmfmac_pdata;
struct brcmf_mp_global_t brcmf_mp_global;
int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
@@ -221,33 +227,147 @@
}
#endif
-void brcmf_mp_attach(void)
+static void brcmf_mp_attach(void)
{
+ /* If module param firmware path is set then this will always be used,
+ * if not set then if available use the platform data version. To make
+ * sure it gets initialized at all, always copy the module param version
+ */
strlcpy(brcmf_mp_global.firmware_path, brcmf_firmware_path,
BRCMF_FW_ALTPATH_LEN);
+ if ((brcmfmac_pdata) && (brcmfmac_pdata->fw_alternative_path) &&
+ (brcmf_mp_global.firmware_path[0] == '\0')) {
+ strlcpy(brcmf_mp_global.firmware_path,
+ brcmfmac_pdata->fw_alternative_path,
+ BRCMF_FW_ALTPATH_LEN);
+ }
}
-int brcmf_mp_device_attach(struct brcmf_pub *drvr)
+struct brcmf_mp_device *brcmf_get_module_param(struct device *dev,
+ enum brcmf_bus_type bus_type,
+ u32 chip, u32 chiprev)
{
- drvr->settings = kzalloc(sizeof(*drvr->settings), GFP_ATOMIC);
- if (!drvr->settings) {
- brcmf_err("Failed to alloca storage space for settings\n");
- return -ENOMEM;
- }
+ struct brcmf_mp_device *settings;
+ struct brcmfmac_pd_device *device_pd;
+ bool found;
+ int i;
- drvr->settings->sdiod_txglomsz = brcmf_sdiod_txglomsz;
- drvr->settings->p2p_enable = !!brcmf_p2p_enable;
- drvr->settings->feature_disable = brcmf_feature_disable;
- drvr->settings->fcmode = brcmf_fcmode;
- drvr->settings->roamoff = !!brcmf_roamoff;
+ brcmf_dbg(INFO, "Enter, bus=%d, chip=%d, rev=%d\n", bus_type, chip,
+ chiprev);
+ settings = kzalloc(sizeof(*settings), GFP_ATOMIC);
+ if (!settings)
+ return NULL;
+
+ /* start by using the module paramaters */
+ settings->p2p_enable = !!brcmf_p2p_enable;
+ settings->feature_disable = brcmf_feature_disable;
+ settings->fcmode = brcmf_fcmode;
+ settings->roamoff = !!brcmf_roamoff;
#ifdef DEBUG
- drvr->settings->ignore_probe_fail = !!brcmf_ignore_probe_fail;
+ settings->ignore_probe_fail = !!brcmf_ignore_probe_fail;
#endif
+
+ if (bus_type == BRCMF_BUSTYPE_SDIO)
+ settings->bus.sdio.txglomsz = brcmf_sdiod_txglomsz;
+
+ /* See if there is any device specific platform data configured */
+ found = false;
+ if (brcmfmac_pdata) {
+ for (i = 0; i < brcmfmac_pdata->device_count; i++) {
+ device_pd = &brcmfmac_pdata->devices[i];
+ if ((device_pd->bus_type == bus_type) &&
+ (device_pd->id == chip) &&
+ ((device_pd->rev == chiprev) ||
+ (device_pd->rev == -1))) {
+ brcmf_dbg(INFO, "Platform data for device found\n");
+ settings->country_codes =
+ device_pd->country_codes;
+ if (device_pd->bus_type == BRCMF_BUSTYPE_SDIO)
+ memcpy(&settings->bus.sdio,
+ &device_pd->bus.sdio,
+ sizeof(settings->bus.sdio));
+ found = true;
+ break;
+ }
+ }
+ }
+ if ((bus_type == BRCMF_BUSTYPE_SDIO) && (!found)) {
+ /* No platform data for this device. In case of SDIO try OF
+ * (Open Firwmare) Device Tree.
+ */
+ brcmf_of_probe(dev, &settings->bus.sdio);
+ }
+ return settings;
+}
+
+void brcmf_release_module_param(struct brcmf_mp_device *module_param)
+{
+ kfree(module_param);
+}
+
+static int __init brcmf_common_pd_probe(struct platform_device *pdev)
+{
+ brcmf_dbg(INFO, "Enter\n");
+
+ brcmfmac_pdata = dev_get_platdata(&pdev->dev);
+
+ if (brcmfmac_pdata->power_on)
+ brcmfmac_pdata->power_on();
+
return 0;
}
-void brcmf_mp_device_detach(struct brcmf_pub *drvr)
+static int brcmf_common_pd_remove(struct platform_device *pdev)
{
- kfree(drvr->settings);
+ brcmf_dbg(INFO, "Enter\n");
+
+ if (brcmfmac_pdata->power_off)
+ brcmfmac_pdata->power_off();
+
+ return 0;
}
+static struct platform_driver brcmf_pd = {
+ .remove = brcmf_common_pd_remove,
+ .driver = {
+ .name = BRCMFMAC_PDATA_NAME,
+ }
+};
+
+static int __init brcmfmac_module_init(void)
+{
+ int err;
+
+ /* Initialize debug system first */
+ brcmf_debugfs_init();
+
+ /* Get the platform data (if available) for our devices */
+ err = platform_driver_probe(&brcmf_pd, brcmf_common_pd_probe);
+ if (err == -ENODEV)
+ brcmf_dbg(INFO, "No platform data available.\n");
+
+ /* Initialize global module paramaters */
+ brcmf_mp_attach();
+
+ /* Continue the initialization by registering the different busses */
+ err = brcmf_core_init();
+ if (err) {
+ brcmf_debugfs_exit();
+ if (brcmfmac_pdata)
+ platform_driver_unregister(&brcmf_pd);
+ }
+
+ return err;
+}
+
+static void __exit brcmfmac_module_exit(void)
+{
+ brcmf_core_exit();
+ if (brcmfmac_pdata)
+ platform_driver_unregister(&brcmf_pd);
+ brcmf_debugfs_exit();
+}
+
+module_init(brcmfmac_module_init);
+module_exit(brcmfmac_module_exit);
+
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h
index 3b0a63b..bd095ab 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h
@@ -15,6 +15,10 @@
#ifndef BRCMFMAC_COMMON_H
#define BRCMFMAC_COMMON_H
+#include <linux/platform_device.h>
+#include <linux/platform_data/brcmfmac.h>
+#include "fwil_types.h"
+
extern const u8 ALLFFMAC[ETH_ALEN];
#define BRCMF_FW_ALTPATH_LEN 256
@@ -41,37 +45,30 @@
/**
* struct brcmf_mp_device - Device module paramaters.
*
- * @sdiod_txglomsz: SDIO txglom size.
- * @joinboost_5g_rssi: 5g rssi booost for preferred join selection.
* @p2p_enable: Legacy P2P0 enable (old wpa_supplicant).
* @feature_disable: Feature_disable bitmask.
* @fcmode: FWS flow control.
* @roamoff: Firmware roaming off?
+ * @ignore_probe_fail: Ignore probe failure.
+ * @country_codes: If available, pointer to struct for translating country codes
+ * @bus: Bus specific platform data. Only SDIO at the mmoment.
*/
struct brcmf_mp_device {
- int sdiod_txglomsz;
- int joinboost_5g_rssi;
- bool p2p_enable;
- int feature_disable;
- int fcmode;
- bool roamoff;
- bool ignore_probe_fail;
+ bool p2p_enable;
+ unsigned int feature_disable;
+ int fcmode;
+ bool roamoff;
+ bool ignore_probe_fail;
+ struct brcmfmac_pd_cc *country_codes;
+ union {
+ struct brcmfmac_sdio_pd sdio;
+ } bus;
};
-void brcmf_mp_attach(void);
-int brcmf_mp_device_attach(struct brcmf_pub *drvr);
-void brcmf_mp_device_detach(struct brcmf_pub *drvr);
-#ifdef DEBUG
-static inline bool brcmf_ignoring_probe_fail(struct brcmf_pub *drvr)
-{
- return drvr->settings->ignore_probe_fail;
-}
-#else
-static inline bool brcmf_ignoring_probe_fail(struct brcmf_pub *drvr)
-{
- return false;
-}
-#endif
+struct brcmf_mp_device *brcmf_get_module_param(struct device *dev,
+ enum brcmf_bus_type bus_type,
+ u32 chip, u32 chiprev);
+void brcmf_release_module_param(struct brcmf_mp_device *module_param);
/* Sets dongle media info (drv_version, mac address). */
int brcmf_c_preinit_dcmds(struct brcmf_if *ifp);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
index ed9998b..ff825cd 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
@@ -20,6 +20,8 @@
#include <linux/inetdevice.h>
#include <net/cfg80211.h>
#include <net/rtnetlink.h>
+#include <net/addrconf.h>
+#include <net/ipv6.h>
#include <brcmu_utils.h>
#include <brcmu_wifi.h>
@@ -36,11 +38,7 @@
#include "pcie.h"
#include "common.h"
-MODULE_AUTHOR("Broadcom Corporation");
-MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver.");
-MODULE_LICENSE("Dual BSD/GPL");
-
-#define MAX_WAIT_FOR_8021X_TX msecs_to_jiffies(50)
+#define MAX_WAIT_FOR_8021X_TX msecs_to_jiffies(950)
/* AMPDU rx reordering definitions */
#define BRCMF_RXREORDER_FLOWID_OFFSET 0
@@ -172,6 +170,35 @@
}
}
+#if IS_ENABLED(CONFIG_IPV6)
+static void _brcmf_update_ndtable(struct work_struct *work)
+{
+ struct brcmf_if *ifp;
+ int i, ret;
+
+ ifp = container_of(work, struct brcmf_if, ndoffload_work);
+
+ /* clear the table in firmware */
+ ret = brcmf_fil_iovar_data_set(ifp, "nd_hostip_clear", NULL, 0);
+ if (ret) {
+ brcmf_dbg(TRACE, "fail to clear nd ip table err:%d\n", ret);
+ return;
+ }
+
+ for (i = 0; i < ifp->ipv6addr_idx; i++) {
+ ret = brcmf_fil_iovar_data_set(ifp, "nd_hostip",
+ &ifp->ipv6_addr_tbl[i],
+ sizeof(struct in6_addr));
+ if (ret)
+ brcmf_err("add nd ip err %d\n", ret);
+ }
+}
+#else
+static void _brcmf_update_ndtable(struct work_struct *work)
+{
+}
+#endif
+
static int brcmf_netdev_set_mac_address(struct net_device *ndev, void *addr)
{
struct brcmf_if *ifp = netdev_priv(ndev);
@@ -685,6 +712,7 @@
INIT_WORK(&ifp->setmacaddr_work, _brcmf_set_mac_address);
INIT_WORK(&ifp->multicast_work, _brcmf_set_multicast_list);
+ INIT_WORK(&ifp->ndoffload_work, _brcmf_update_ndtable);
if (rtnl_locked)
err = register_netdevice(ndev);
@@ -884,6 +912,7 @@
if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) {
cancel_work_sync(&ifp->setmacaddr_work);
cancel_work_sync(&ifp->multicast_work);
+ cancel_work_sync(&ifp->ndoffload_work);
}
brcmf_net_detach(ifp->ndev);
} else {
@@ -1006,14 +1035,14 @@
return NOTIFY_OK;
}
for (i = 0; i < ARPOL_MAX_ENTRIES; i++) {
- if (addr_table[i] != 0) {
- brcmf_fil_iovar_data_set(ifp,
- "arp_hostip", &addr_table[i],
- sizeof(addr_table[i]));
- if (ret)
- brcmf_err("add arp ip err %d\n",
- ret);
- }
+ if (addr_table[i] == 0)
+ continue;
+ ret = brcmf_fil_iovar_data_set(ifp, "arp_hostip",
+ &addr_table[i],
+ sizeof(addr_table[i]));
+ if (ret)
+ brcmf_err("add arp ip err %d\n",
+ ret);
}
}
break;
@@ -1025,7 +1054,57 @@
}
#endif
-int brcmf_attach(struct device *dev)
+#if IS_ENABLED(CONFIG_IPV6)
+static int brcmf_inet6addr_changed(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct brcmf_pub *drvr = container_of(nb, struct brcmf_pub,
+ inet6addr_notifier);
+ struct inet6_ifaddr *ifa = data;
+ struct brcmf_if *ifp;
+ int i;
+ struct in6_addr *table;
+
+ /* Only handle primary interface */
+ ifp = drvr->iflist[0];
+ if (!ifp)
+ return NOTIFY_DONE;
+ if (ifp->ndev != ifa->idev->dev)
+ return NOTIFY_DONE;
+
+ table = ifp->ipv6_addr_tbl;
+ for (i = 0; i < NDOL_MAX_ENTRIES; i++)
+ if (ipv6_addr_equal(&ifa->addr, &table[i]))
+ break;
+
+ switch (action) {
+ case NETDEV_UP:
+ if (i == NDOL_MAX_ENTRIES) {
+ if (ifp->ipv6addr_idx < NDOL_MAX_ENTRIES) {
+ table[ifp->ipv6addr_idx++] = ifa->addr;
+ } else {
+ for (i = 0; i < NDOL_MAX_ENTRIES - 1; i++)
+ table[i] = table[i + 1];
+ table[NDOL_MAX_ENTRIES - 1] = ifa->addr;
+ }
+ }
+ break;
+ case NETDEV_DOWN:
+ if (i < NDOL_MAX_ENTRIES)
+ for (; i < ifp->ipv6addr_idx; i++)
+ table[i] = table[i + 1];
+ break;
+ default:
+ break;
+ }
+
+ schedule_work(&ifp->ndoffload_work);
+
+ return NOTIFY_OK;
+}
+#endif
+
+int brcmf_attach(struct device *dev, struct brcmf_mp_device *settings)
{
struct brcmf_pub *drvr = NULL;
int ret = 0;
@@ -1047,10 +1126,7 @@
drvr->hdrlen = 0;
drvr->bus_if = dev_get_drvdata(dev);
drvr->bus_if->drvr = drvr;
-
- /* Initialize device specific settings */
- if (brcmf_mp_device_attach(drvr))
- goto fail;
+ drvr->settings = settings;
/* attach debug facilities */
brcmf_debug_attach(drvr);
@@ -1164,30 +1240,41 @@
#ifdef CONFIG_INET
drvr->inetaddr_notifier.notifier_call = brcmf_inetaddr_changed;
ret = register_inetaddr_notifier(&drvr->inetaddr_notifier);
+ if (ret)
+ goto fail;
+
+#if IS_ENABLED(CONFIG_IPV6)
+ drvr->inet6addr_notifier.notifier_call = brcmf_inet6addr_changed;
+ ret = register_inet6addr_notifier(&drvr->inet6addr_notifier);
+ if (ret) {
+ unregister_inetaddr_notifier(&drvr->inetaddr_notifier);
+ goto fail;
+ }
#endif
+#endif /* CONFIG_INET */
+
+ return 0;
fail:
- if (ret < 0) {
- brcmf_err("failed: %d\n", ret);
- if (drvr->config) {
- brcmf_cfg80211_detach(drvr->config);
- drvr->config = NULL;
- }
- if (drvr->fws) {
- brcmf_fws_del_interface(ifp);
- brcmf_fws_deinit(drvr);
- }
- if (ifp)
- brcmf_net_detach(ifp->ndev);
- if (p2p_ifp)
- brcmf_net_detach(p2p_ifp->ndev);
- drvr->iflist[0] = NULL;
- drvr->iflist[1] = NULL;
- if (brcmf_ignoring_probe_fail(drvr))
- ret = 0;
- return ret;
+ brcmf_err("failed: %d\n", ret);
+ if (drvr->config) {
+ brcmf_cfg80211_detach(drvr->config);
+ drvr->config = NULL;
}
- return 0;
+ if (drvr->fws) {
+ brcmf_fws_del_interface(ifp);
+ brcmf_fws_deinit(drvr);
+ }
+ if (ifp)
+ brcmf_net_detach(ifp->ndev);
+ if (p2p_ifp)
+ brcmf_net_detach(p2p_ifp->ndev);
+ drvr->iflist[0] = NULL;
+ drvr->iflist[1] = NULL;
+ if (drvr->settings->ignore_probe_fail)
+ ret = 0;
+
+ return ret;
}
void brcmf_bus_add_txhdrlen(struct device *dev, uint len)
@@ -1237,6 +1324,10 @@
unregister_inetaddr_notifier(&drvr->inetaddr_notifier);
#endif
+#if IS_ENABLED(CONFIG_IPV6)
+ unregister_inet6addr_notifier(&drvr->inet6addr_notifier);
+#endif
+
/* stop firmware event handling */
brcmf_fweh_detach(drvr);
if (drvr->config)
@@ -1256,8 +1347,6 @@
brcmf_proto_detach(drvr);
- brcmf_mp_device_detach(drvr);
-
brcmf_debug_detach(drvr);
bus_if->drvr = NULL;
kfree(drvr);
@@ -1324,19 +1413,15 @@
}
static DECLARE_WORK(brcmf_driver_work, brcmf_driver_register);
-static int __init brcmfmac_module_init(void)
+int __init brcmf_core_init(void)
{
- brcmf_debugfs_init();
-#ifdef CONFIG_BRCMFMAC_SDIO
- brcmf_sdio_init();
-#endif
if (!schedule_work(&brcmf_driver_work))
return -EBUSY;
return 0;
}
-static void __exit brcmfmac_module_exit(void)
+void __exit brcmf_core_exit(void)
{
cancel_work_sync(&brcmf_driver_work);
@@ -1349,8 +1434,5 @@
#ifdef CONFIG_BRCMFMAC_PCIE
brcmf_pcie_exit();
#endif
- brcmf_debugfs_exit();
}
-module_init(brcmfmac_module_init);
-module_exit(brcmfmac_module_exit);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
index 8f39435..7bdb6fe 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
@@ -48,6 +48,8 @@
*/
#define BRCMF_DRIVER_FIRMWARE_VERSION_LEN 32
+#define NDOL_MAX_ENTRIES 8
+
/**
* struct brcmf_ampdu_rx_reorder - AMPDU receive reorder info
*
@@ -143,6 +145,7 @@
#endif
struct notifier_block inetaddr_notifier;
+ struct notifier_block inet6addr_notifier;
struct brcmf_mp_device *settings;
};
@@ -175,6 +178,7 @@
* @stats: interface specific network statistics.
* @setmacaddr_work: worker object for setting mac address.
* @multicast_work: worker object for multicast provisioning.
+ * @ndoffload_work: worker object for neighbor discovery offload configuration.
* @fws_desc: interface specific firmware-signalling descriptor.
* @ifidx: interface index in device firmware.
* @bsscfgidx: index of bss associated with this interface.
@@ -191,6 +195,7 @@
struct net_device_stats stats;
struct work_struct setmacaddr_work;
struct work_struct multicast_work;
+ struct work_struct ndoffload_work;
struct brcmf_fws_mac_descriptor *fws_desc;
int ifidx;
s32 bsscfgidx;
@@ -199,6 +204,8 @@
spinlock_t netif_stop_lock;
atomic_t pend_8021x_cnt;
wait_queue_head_t pend_8021x_wait;
+ struct in6_addr ipv6_addr_tbl[NDOL_MAX_ENTRIES];
+ u8 ipv6addr_idx;
};
struct brcmf_skb_reorder_data {
@@ -220,5 +227,7 @@
void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success);
void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb);
void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on);
+int __init brcmf_core_init(void);
+void __exit brcmf_core_exit(void);
#endif /* BRCMFMAC_CORE_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
index 1ffa95f..62985f2 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
@@ -136,6 +136,7 @@
{
struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
struct brcmf_pno_macaddr_le pfn_mac;
+ u32 wowl_cap;
s32 err;
brcmf_feat_firmware_capabilities(ifp);
@@ -143,11 +144,24 @@
brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_PNO, "pfn");
if (drvr->bus_if->wowl_supported)
brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl");
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL)) {
+ err = brcmf_fil_iovar_int_get(ifp, "wowl_cap", &wowl_cap);
+ if (!err) {
+ ifp->drvr->feat_flags |= BIT(BRCMF_FEAT_WOWL_ARP_ND);
+ if (wowl_cap & BRCMF_WOWL_PFN_FOUND)
+ ifp->drvr->feat_flags |=
+ BIT(BRCMF_FEAT_WOWL_ND);
+ if (wowl_cap & BRCMF_WOWL_GTK_FAILURE)
+ ifp->drvr->feat_flags |=
+ BIT(BRCMF_FEAT_WOWL_GTK);
+ }
+ }
/* MBSS does not work for 43362 */
if (drvr->bus_if->chip == BRCM_CC_43362_CHIP_ID)
ifp->drvr->feat_flags &= ~BIT(BRCMF_FEAT_MBSS);
brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_RSDB, "rsdb_mode");
brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_TDLS, "tdls_enable");
+ brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MFP, "mfp");
pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER;
err = brcmf_fil_iovar_data_get(ifp, "pfn_macaddr", &pfn_mac,
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h
index 2e2479d..db4733a 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h
@@ -27,6 +27,10 @@
* RSDB: Real Simultaneous Dual Band
* TDLS: Tunneled Direct Link Setup
* SCAN_RANDOM_MAC: Random MAC during (net detect) scheduled scan.
+ * WOWL_ND: WOWL net detect (PNO)
+ * WOWL_GTK: (WOWL) GTK rekeying offload
+ * WOWL_ARP_ND: ARP and Neighbor Discovery offload support during WOWL.
+ * MFP: 802.11w Management Frame Protection.
*/
#define BRCMF_FEAT_LIST \
BRCMF_FEAT_DEF(MBSS) \
@@ -36,7 +40,11 @@
BRCMF_FEAT_DEF(P2P) \
BRCMF_FEAT_DEF(RSDB) \
BRCMF_FEAT_DEF(TDLS) \
- BRCMF_FEAT_DEF(SCAN_RANDOM_MAC)
+ BRCMF_FEAT_DEF(SCAN_RANDOM_MAC) \
+ BRCMF_FEAT_DEF(WOWL_ND) \
+ BRCMF_FEAT_DEF(WOWL_GTK) \
+ BRCMF_FEAT_DEF(WOWL_ARP_ND) \
+ BRCMF_FEAT_DEF(MFP)
/*
* Quirks:
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c
index 2ca783f..7e269f9 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c
@@ -32,7 +32,7 @@
#define BRCMF_FLOWRING_LOW (BRCMF_FLOWRING_HIGH - 256)
#define BRCMF_FLOWRING_INVALID_IFIDX 0xff
-#define BRCMF_FLOWRING_HASH_AP(da, fifo, ifidx) (da[5] + fifo + ifidx * 16)
+#define BRCMF_FLOWRING_HASH_AP(da, fifo, ifidx) (da[5] * 2 + fifo + ifidx * 16)
#define BRCMF_FLOWRING_HASH_STA(fifo, ifidx) (fifo + ifidx * 16)
static const u8 brcmf_flowring_prio2fifo[] = {
@@ -68,7 +68,7 @@
u8 prio, u8 ifidx)
{
struct brcmf_flowring_hash *hash;
- u8 hash_idx;
+ u16 hash_idx;
u32 i;
bool found;
bool sta;
@@ -88,6 +88,7 @@
}
hash_idx = sta ? BRCMF_FLOWRING_HASH_STA(fifo, ifidx) :
BRCMF_FLOWRING_HASH_AP(mac, fifo, ifidx);
+ hash_idx &= (BRCMF_FLOWRING_HASHSIZE - 1);
found = false;
hash = flow->hash;
for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) {
@@ -98,6 +99,7 @@
break;
}
hash_idx++;
+ hash_idx &= (BRCMF_FLOWRING_HASHSIZE - 1);
}
if (found)
return hash[hash_idx].flowid;
@@ -111,7 +113,7 @@
{
struct brcmf_flowring_ring *ring;
struct brcmf_flowring_hash *hash;
- u8 hash_idx;
+ u16 hash_idx;
u32 i;
bool found;
u8 fifo;
@@ -131,6 +133,7 @@
}
hash_idx = sta ? BRCMF_FLOWRING_HASH_STA(fifo, ifidx) :
BRCMF_FLOWRING_HASH_AP(mac, fifo, ifidx);
+ hash_idx &= (BRCMF_FLOWRING_HASHSIZE - 1);
found = false;
hash = flow->hash;
for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) {
@@ -140,6 +143,7 @@
break;
}
hash_idx++;
+ hash_idx &= (BRCMF_FLOWRING_HASHSIZE - 1);
}
if (found) {
for (i = 0; i < flow->nrofrings; i++) {
@@ -169,7 +173,7 @@
}
-u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u8 flowid)
+u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u16 flowid)
{
struct brcmf_flowring_ring *ring;
@@ -179,7 +183,7 @@
}
-static void brcmf_flowring_block(struct brcmf_flowring *flow, u8 flowid,
+static void brcmf_flowring_block(struct brcmf_flowring *flow, u16 flowid,
bool blocked)
{
struct brcmf_flowring_ring *ring;
@@ -228,10 +232,10 @@
}
-void brcmf_flowring_delete(struct brcmf_flowring *flow, u8 flowid)
+void brcmf_flowring_delete(struct brcmf_flowring *flow, u16 flowid)
{
struct brcmf_flowring_ring *ring;
- u8 hash_idx;
+ u16 hash_idx;
struct sk_buff *skb;
ring = flow->rings[flowid];
@@ -253,7 +257,7 @@
}
-u32 brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid,
+u32 brcmf_flowring_enqueue(struct brcmf_flowring *flow, u16 flowid,
struct sk_buff *skb)
{
struct brcmf_flowring_ring *ring;
@@ -279,7 +283,7 @@
}
-struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u8 flowid)
+struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u16 flowid)
{
struct brcmf_flowring_ring *ring;
struct sk_buff *skb;
@@ -300,7 +304,7 @@
}
-void brcmf_flowring_reinsert(struct brcmf_flowring *flow, u8 flowid,
+void brcmf_flowring_reinsert(struct brcmf_flowring *flow, u16 flowid,
struct sk_buff *skb)
{
struct brcmf_flowring_ring *ring;
@@ -311,7 +315,7 @@
}
-u32 brcmf_flowring_qlen(struct brcmf_flowring *flow, u8 flowid)
+u32 brcmf_flowring_qlen(struct brcmf_flowring *flow, u16 flowid)
{
struct brcmf_flowring_ring *ring;
@@ -326,7 +330,7 @@
}
-void brcmf_flowring_open(struct brcmf_flowring *flow, u8 flowid)
+void brcmf_flowring_open(struct brcmf_flowring *flow, u16 flowid)
{
struct brcmf_flowring_ring *ring;
@@ -340,10 +344,10 @@
}
-u8 brcmf_flowring_ifidx_get(struct brcmf_flowring *flow, u8 flowid)
+u8 brcmf_flowring_ifidx_get(struct brcmf_flowring *flow, u16 flowid)
{
struct brcmf_flowring_ring *ring;
- u8 hash_idx;
+ u16 hash_idx;
ring = flow->rings[flowid];
hash_idx = ring->hash_id;
@@ -384,7 +388,7 @@
struct brcmf_pub *drvr = bus_if->drvr;
struct brcmf_flowring_tdls_entry *search;
struct brcmf_flowring_tdls_entry *remove;
- u8 flowid;
+ u16 flowid;
for (flowid = 0; flowid < flow->nrofrings; flowid++) {
if (flow->rings[flowid])
@@ -408,7 +412,7 @@
struct brcmf_bus *bus_if = dev_get_drvdata(flow->dev);
struct brcmf_pub *drvr = bus_if->drvr;
u32 i;
- u8 flowid;
+ u16 flowid;
if (flow->addr_mode[ifidx] != addr_mode) {
for (i = 0; i < ARRAY_SIZE(flow->hash); i++) {
@@ -434,7 +438,7 @@
struct brcmf_flowring_tdls_entry *prev;
struct brcmf_flowring_tdls_entry *search;
u32 i;
- u8 flowid;
+ u16 flowid;
bool sta;
sta = (flow->addr_mode[ifidx] == ADDR_INDIRECT);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h
index 95fd1c9..068e68d 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h
@@ -16,7 +16,7 @@
#define BRCMFMAC_FLOWRING_H
-#define BRCMF_FLOWRING_HASHSIZE 256
+#define BRCMF_FLOWRING_HASHSIZE 512 /* has to be 2^x */
#define BRCMF_FLOWRING_INVALID_ID 0xFFFFFFFF
@@ -24,7 +24,7 @@
u8 mac[ETH_ALEN];
u8 fifo;
u8 ifidx;
- u8 flowid;
+ u16 flowid;
};
enum ring_status {
@@ -61,16 +61,16 @@
u8 prio, u8 ifidx);
u32 brcmf_flowring_create(struct brcmf_flowring *flow, u8 da[ETH_ALEN],
u8 prio, u8 ifidx);
-void brcmf_flowring_delete(struct brcmf_flowring *flow, u8 flowid);
-void brcmf_flowring_open(struct brcmf_flowring *flow, u8 flowid);
-u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u8 flowid);
-u32 brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid,
+void brcmf_flowring_delete(struct brcmf_flowring *flow, u16 flowid);
+void brcmf_flowring_open(struct brcmf_flowring *flow, u16 flowid);
+u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u16 flowid);
+u32 brcmf_flowring_enqueue(struct brcmf_flowring *flow, u16 flowid,
struct sk_buff *skb);
-struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u8 flowid);
-void brcmf_flowring_reinsert(struct brcmf_flowring *flow, u8 flowid,
+struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u16 flowid);
+void brcmf_flowring_reinsert(struct brcmf_flowring *flow, u16 flowid,
struct sk_buff *skb);
-u32 brcmf_flowring_qlen(struct brcmf_flowring *flow, u8 flowid);
-u8 brcmf_flowring_ifidx_get(struct brcmf_flowring *flow, u8 flowid);
+u32 brcmf_flowring_qlen(struct brcmf_flowring *flow, u16 flowid);
+u8 brcmf_flowring_ifidx_get(struct brcmf_flowring *flow, u16 flowid);
struct brcmf_flowring *brcmf_flowring_attach(struct device *dev, u16 nrofrings);
void brcmf_flowring_detach(struct brcmf_flowring *flow);
void brcmf_flowring_configure_addr_mode(struct brcmf_flowring *flow, int ifidx,
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c
index 7b26fb1..d414fbb 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c
@@ -26,50 +26,6 @@
#include "fwil.h"
/**
- * struct brcm_ethhdr - broadcom specific ether header.
- *
- * @subtype: subtype for this packet.
- * @length: TODO: length of appended data.
- * @version: version indication.
- * @oui: OUI of this packet.
- * @usr_subtype: subtype for this OUI.
- */
-struct brcm_ethhdr {
- __be16 subtype;
- __be16 length;
- u8 version;
- u8 oui[3];
- __be16 usr_subtype;
-} __packed;
-
-struct brcmf_event_msg_be {
- __be16 version;
- __be16 flags;
- __be32 event_type;
- __be32 status;
- __be32 reason;
- __be32 auth_type;
- __be32 datalen;
- u8 addr[ETH_ALEN];
- char ifname[IFNAMSIZ];
- u8 ifidx;
- u8 bsscfgidx;
-} __packed;
-
-/**
- * struct brcmf_event - contents of broadcom event packet.
- *
- * @eth: standard ether header.
- * @hdr: broadcom specific ether header.
- * @msg: common part of the actual event message.
- */
-struct brcmf_event {
- struct ethhdr eth;
- struct brcm_ethhdr hdr;
- struct brcmf_event_msg_be msg;
-} __packed;
-
-/**
* struct brcmf_fweh_queue_item - event item on event queue.
*
* @q: list element for queuing.
@@ -85,6 +41,7 @@
u8 ifidx;
u8 ifaddr[ETH_ALEN];
struct brcmf_event_msg_be emsg;
+ u32 datalen;
u8 data[0];
};
@@ -294,6 +251,11 @@
brcmf_dbg_hex_dump(BRCMF_EVENT_ON(), event->data,
min_t(u32, emsg.datalen, 64),
"event payload, len=%d\n", emsg.datalen);
+ if (emsg.datalen > event->datalen) {
+ brcmf_err("event invalid length header=%d, msg=%d\n",
+ event->datalen, emsg.datalen);
+ goto event_free;
+ }
/* special handling of interface event */
if (event->code == BRCMF_E_IF) {
@@ -439,7 +401,8 @@
* dispatch the event to a registered handler (using worker).
*/
void brcmf_fweh_process_event(struct brcmf_pub *drvr,
- struct brcmf_event *event_packet)
+ struct brcmf_event *event_packet,
+ u32 packet_len)
{
enum brcmf_fweh_event_code code;
struct brcmf_fweh_info *fweh = &drvr->fweh;
@@ -459,6 +422,9 @@
if (code != BRCMF_E_IF && !fweh->evt_handler[code])
return;
+ if (datalen > BRCMF_DCMD_MAXLEN)
+ return;
+
if (in_interrupt())
alloc_flag = GFP_ATOMIC;
@@ -472,6 +438,7 @@
/* use memcpy to get aligned event message */
memcpy(&event->emsg, &event_packet->msg, sizeof(event->emsg));
memcpy(event->data, data, datalen);
+ event->datalen = datalen;
memcpy(event->ifaddr, event_packet->eth.h_dest, ETH_ALEN);
brcmf_fweh_queue_event(fweh, event);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h
index 5e39e2a..26ff5a9 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h
@@ -27,7 +27,6 @@
struct brcmf_pub;
struct brcmf_if;
struct brcmf_cfg80211_info;
-struct brcmf_event;
/* list of firmware events */
#define BRCMF_FWEH_EVENT_ENUM_DEFLIST \
@@ -180,13 +179,55 @@
/**
* definitions for event packet validation.
*/
-#define BRCMF_EVENT_OUI_OFFSET 19
-#define BRCM_OUI "\x00\x10\x18"
-#define DOT11_OUI_LEN 3
-#define BCMILCP_BCM_SUBTYPE_EVENT 1
+#define BRCM_OUI "\x00\x10\x18"
+#define BCMILCP_BCM_SUBTYPE_EVENT 1
/**
+ * struct brcm_ethhdr - broadcom specific ether header.
+ *
+ * @subtype: subtype for this packet.
+ * @length: TODO: length of appended data.
+ * @version: version indication.
+ * @oui: OUI of this packet.
+ * @usr_subtype: subtype for this OUI.
+ */
+struct brcm_ethhdr {
+ __be16 subtype;
+ __be16 length;
+ u8 version;
+ u8 oui[3];
+ __be16 usr_subtype;
+} __packed;
+
+struct brcmf_event_msg_be {
+ __be16 version;
+ __be16 flags;
+ __be32 event_type;
+ __be32 status;
+ __be32 reason;
+ __be32 auth_type;
+ __be32 datalen;
+ u8 addr[ETH_ALEN];
+ char ifname[IFNAMSIZ];
+ u8 ifidx;
+ u8 bsscfgidx;
+} __packed;
+
+/**
+ * struct brcmf_event - contents of broadcom event packet.
+ *
+ * @eth: standard ether header.
+ * @hdr: broadcom specific ether header.
+ * @msg: common part of the actual event message.
+ */
+struct brcmf_event {
+ struct ethhdr eth;
+ struct brcm_ethhdr hdr;
+ struct brcmf_event_msg_be msg;
+} __packed;
+
+/**
* struct brcmf_event_msg - firmware event message.
*
* @version: version information.
@@ -256,34 +297,35 @@
enum brcmf_fweh_event_code code);
int brcmf_fweh_activate_events(struct brcmf_if *ifp);
void brcmf_fweh_process_event(struct brcmf_pub *drvr,
- struct brcmf_event *event_packet);
+ struct brcmf_event *event_packet,
+ u32 packet_len);
void brcmf_fweh_p2pdev_setup(struct brcmf_if *ifp, bool ongoing);
static inline void brcmf_fweh_process_skb(struct brcmf_pub *drvr,
struct sk_buff *skb)
{
struct brcmf_event *event_packet;
- u8 *data;
u16 usr_stype;
/* only process events when protocol matches */
if (skb->protocol != cpu_to_be16(ETH_P_LINK_CTL))
return;
+ if ((skb->len + ETH_HLEN) < sizeof(*event_packet))
+ return;
+
/* check for BRCM oui match */
event_packet = (struct brcmf_event *)skb_mac_header(skb);
- data = (u8 *)event_packet;
- data += BRCMF_EVENT_OUI_OFFSET;
- if (memcmp(BRCM_OUI, data, DOT11_OUI_LEN))
+ if (memcmp(BRCM_OUI, &event_packet->hdr.oui[0],
+ sizeof(event_packet->hdr.oui)))
return;
/* final match on usr_subtype */
- data += DOT11_OUI_LEN;
- usr_stype = get_unaligned_be16(data);
+ usr_stype = get_unaligned_be16(&event_packet->hdr.usr_subtype);
if (usr_stype != BCMILCP_BCM_SUBTYPE_EVENT)
return;
- brcmf_fweh_process_event(drvr, event_packet);
+ brcmf_fweh_process_event(drvr, event_packet, skb->len + ETH_HLEN);
}
#endif /* FWEH_H_ */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
index 1afc2ad..a4118c0 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
@@ -111,7 +111,9 @@
/* Wakeup if received matched secured pattern: */
#define BRCMF_WOWL_SECURE (1 << 25)
/* Wakeup on finding preferred network */
-#define BRCMF_WOWL_PFN_FOUND (1 << 26)
+#define BRCMF_WOWL_PFN_FOUND (1 << 27)
+/* Wakeup on receiving pairwise key EAP packets: */
+#define WIPHY_WOWL_EAP_PK (1 << 28)
/* Link Down indication in WoWL mode: */
#define BRCMF_WOWL_LINKDOWN (1 << 31)
@@ -134,6 +136,16 @@
#define BRCMF_PFN_MAC_OUI_ONLY BIT(0)
#define BRCMF_PFN_SET_MAC_UNASSOC BIT(1)
+#define BRCMF_MCSSET_LEN 16
+
+#define BRCMF_RSN_KCK_LENGTH 16
+#define BRCMF_RSN_KEK_LENGTH 16
+#define BRCMF_RSN_REPLAY_LEN 8
+
+#define BRCMF_MFP_NONE 0
+#define BRCMF_MFP_CAPABLE 1
+#define BRCMF_MFP_REQUIRED 2
+
/* join preference types for join_pref iovar */
enum brcmf_join_pref_types {
BRCMF_JOIN_PREF_RSSI = 1,
@@ -279,7 +291,7 @@
__le32 reserved32[1]; /* Reserved for expansion of BSS properties */
u8 flags; /* flags */
u8 reserved[3]; /* Reserved for expansion of BSS properties */
- u8 basic_mcs[MCSSET_LEN]; /* 802.11N BSS required MCS set */
+ u8 basic_mcs[BRCMF_MCSSET_LEN]; /* 802.11N BSS required MCS set */
__le16 ie_offset; /* offset at which IEs start, from beginning */
__le32 ie_length; /* byte length of Information Elements */
@@ -787,4 +799,17 @@
__le32 rx_ocast_good_pkt;
};
+/**
+ * struct brcmf_gtk_keyinfo_le - GTP rekey data
+ *
+ * @kck: key confirmation key.
+ * @kek: key encryption key.
+ * @replay_counter: replay counter.
+ */
+struct brcmf_gtk_keyinfo_le {
+ u8 kck[BRCMF_RSN_KCK_LENGTH];
+ u8 kek[BRCMF_RSN_KEK_LENGTH];
+ u8 replay_counter[BRCMF_RSN_REPLAY_LEN];
+};
+
#endif /* FWIL_TYPES_H_ */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
index c2bdb91..9229667 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
@@ -677,7 +677,7 @@
}
-static void brcmf_msgbuf_txflow(struct brcmf_msgbuf *msgbuf, u8 flowid)
+static void brcmf_msgbuf_txflow(struct brcmf_msgbuf *msgbuf, u16 flowid)
{
struct brcmf_flowring *flow = msgbuf->flow;
struct brcmf_commonring *commonring;
@@ -1310,7 +1310,7 @@
}
-void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u8 flowid)
+void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u16 flowid)
{
struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
struct msgbuf_tx_flowring_delete_req *delete;
@@ -1415,6 +1415,13 @@
u32 count;
if_msgbuf = drvr->bus_if->msgbuf;
+
+ if (if_msgbuf->nrof_flowrings >= BRCMF_FLOWRING_HASHSIZE) {
+ brcmf_err("driver not configured for this many flowrings %d\n",
+ if_msgbuf->nrof_flowrings);
+ if_msgbuf->nrof_flowrings = BRCMF_FLOWRING_HASHSIZE - 1;
+ }
+
msgbuf = kzalloc(sizeof(*msgbuf), GFP_KERNEL);
if (!msgbuf)
goto fail;
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h
index 3d513e4..ee6906a 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h
@@ -33,7 +33,7 @@
int brcmf_proto_msgbuf_rx_trigger(struct device *dev);
-void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u8 flowid);
+void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u16 flowid);
int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr);
void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr);
#else
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c
index 03f35e0..425c41d 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c
@@ -16,17 +16,15 @@
#include <linux/init.h>
#include <linux/of.h>
#include <linux/of_irq.h>
-#include <linux/mmc/card.h>
-#include <linux/platform_data/brcmfmac-sdio.h>
-#include <linux/mmc/sdio_func.h>
#include <defs.h>
#include "debug.h"
-#include "sdio.h"
+#include "core.h"
+#include "common.h"
+#include "of.h"
-void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev)
+void brcmf_of_probe(struct device *dev, struct brcmfmac_sdio_pd *sdio)
{
- struct device *dev = sdiodev->dev;
struct device_node *np = dev->of_node;
int irq;
u32 irqf;
@@ -35,12 +33,8 @@
if (!np || !of_device_is_compatible(np, "brcm,bcm4329-fmac"))
return;
- sdiodev->pdata = devm_kzalloc(dev, sizeof(*sdiodev->pdata), GFP_KERNEL);
- if (!sdiodev->pdata)
- return;
-
if (of_property_read_u32(np, "brcm,drive-strength", &val) == 0)
- sdiodev->pdata->drive_strength = val;
+ sdio->drive_strength = val;
/* make sure there are interrupts defined in the node */
if (!of_find_property(np, "interrupts", NULL))
@@ -53,7 +47,7 @@
}
irqf = irqd_get_trigger_type(irq_get_irq_data(irq));
- sdiodev->pdata->oob_irq_supported = true;
- sdiodev->pdata->oob_irq_nr = irq;
- sdiodev->pdata->oob_irq_flags = irqf;
+ sdio->oob_irq_supported = true;
+ sdio->oob_irq_nr = irq;
+ sdio->oob_irq_flags = irqf;
}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h
index 5f7c355..a9d94c1 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h
@@ -14,9 +14,9 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifdef CONFIG_OF
-void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev);
+void brcmf_of_probe(struct device *dev, struct brcmfmac_sdio_pd *sdio);
#else
-static void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev)
+static void brcmf_of_probe(struct device *dev, struct brcmfmac_sdio_pd *sdio)
{
}
#endif /* CONFIG_OF */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
index 821b649..b5a49e5 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
@@ -1361,6 +1361,11 @@
u16 mgmt_type;
u8 action;
+ if (e->datalen < sizeof(*rxframe)) {
+ brcmf_dbg(SCAN, "Event data to small. Ignore\n");
+ return 0;
+ }
+
ch.chspec = be16_to_cpu(rxframe->chanspec);
cfg->d11inf.decchspec(&ch);
/* Check if wpa_supplicant has registered for this frame */
@@ -1858,6 +1863,11 @@
brcmf_dbg(INFO, "Enter: event %d reason %d\n", e->event_code,
e->reason);
+ if (e->datalen < sizeof(*rxframe)) {
+ brcmf_dbg(SCAN, "Event data to small. Ignore\n");
+ return 0;
+ }
+
ch.chspec = be16_to_cpu(rxframe->chanspec);
cfg->d11inf.decchspec(&ch);
@@ -1988,8 +1998,8 @@
brcmf_cfg80211_arm_vif_event(cfg, NULL);
return err;
}
- err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_CHANGE,
- BRCMF_VIF_EVENT_TIMEOUT);
+ err = brcmf_cfg80211_wait_vif_event(cfg, BRCMF_E_IF_CHANGE,
+ BRCMF_VIF_EVENT_TIMEOUT);
brcmf_cfg80211_arm_vif_event(cfg, NULL);
if (!err) {
brcmf_err("No BRCMF_E_IF_CHANGE event received\n");
@@ -2090,8 +2100,8 @@
}
/* wait for firmware event */
- err = brcmf_cfg80211_wait_vif_event_timeout(p2p->cfg, BRCMF_E_IF_ADD,
- BRCMF_VIF_EVENT_TIMEOUT);
+ err = brcmf_cfg80211_wait_vif_event(p2p->cfg, BRCMF_E_IF_ADD,
+ BRCMF_VIF_EVENT_TIMEOUT);
brcmf_cfg80211_arm_vif_event(p2p->cfg, NULL);
brcmf_fweh_p2pdev_setup(pri_ifp, false);
if (!err) {
@@ -2180,8 +2190,8 @@
}
/* wait for firmware event */
- err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_ADD,
- BRCMF_VIF_EVENT_TIMEOUT);
+ err = brcmf_cfg80211_wait_vif_event(cfg, BRCMF_E_IF_ADD,
+ BRCMF_VIF_EVENT_TIMEOUT);
brcmf_cfg80211_arm_vif_event(cfg, NULL);
if (!err) {
brcmf_err("timeout occurred\n");
@@ -2272,8 +2282,8 @@
}
if (!err) {
/* wait for firmware event */
- err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_DEL,
- BRCMF_VIF_EVENT_TIMEOUT);
+ err = brcmf_cfg80211_wait_vif_event(cfg, BRCMF_E_IF_DEL,
+ BRCMF_VIF_EVENT_TIMEOUT);
if (!err)
err = -EIO;
else
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
index d5f9ef4..0af8db8 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
@@ -37,6 +37,8 @@
#include "pcie.h"
#include "firmware.h"
#include "chip.h"
+#include "core.h"
+#include "common.h"
enum brcmf_pcie_state {
@@ -53,6 +55,7 @@
BRCMF_FW_NVRAM_DEF(4359, "brcmfmac4359-pcie.bin", "brcmfmac4359-pcie.txt");
BRCMF_FW_NVRAM_DEF(4365B, "brcmfmac4365b-pcie.bin", "brcmfmac4365b-pcie.txt");
BRCMF_FW_NVRAM_DEF(4366B, "brcmfmac4366b-pcie.bin", "brcmfmac4366b-pcie.txt");
+BRCMF_FW_NVRAM_DEF(4366C, "brcmfmac4366c-pcie.bin", "brcmfmac4366c-pcie.txt");
BRCMF_FW_NVRAM_DEF(4371, "brcmfmac4371-pcie.bin", "brcmfmac4371-pcie.txt");
static struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = {
@@ -66,13 +69,13 @@
BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4358_CHIP_ID, 0xFFFFFFFF, 4358),
BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4359_CHIP_ID, 0xFFFFFFFF, 4359),
BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4365_CHIP_ID, 0xFFFFFFFF, 4365B),
- BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4366_CHIP_ID, 0xFFFFFFFF, 4366B),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4366_CHIP_ID, 0x0000000F, 4366B),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4366_CHIP_ID, 0xFFFFFFF0, 4366C),
BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4371_CHIP_ID, 0xFFFFFFFF, 4371),
};
#define BRCMF_PCIE_FW_UP_TIMEOUT 2000 /* msec */
-#define BRCMF_PCIE_TCM_MAP_SIZE (4096 * 1024)
#define BRCMF_PCIE_REG_MAP_SIZE (32 * 1024)
/* backplane addres space accessed by BAR0 */
@@ -99,9 +102,6 @@
#define BRCMF_PCIE_PCIE2REG_CONFIGDATA 0x124
#define BRCMF_PCIE_PCIE2REG_H2D_MAILBOX 0x140
-#define BRCMF_PCIE_GENREV1 1
-#define BRCMF_PCIE_GENREV2 2
-
#define BRCMF_PCIE2_INTA 0x01
#define BRCMF_PCIE2_INTB 0x02
@@ -207,6 +207,10 @@
#define BRCMF_PCIE_CFGREG_REG_BAR3_CONFIG 0x4F4
#define BRCMF_PCIE_LINK_STATUS_CTRL_ASPM_ENAB 3
+/* Magic number at a magic location to find RAM size */
+#define BRCMF_RAMSIZE_MAGIC 0x534d4152 /* SMAR */
+#define BRCMF_RAMSIZE_OFFSET 0x6c
+
struct brcmf_pcie_console {
u32 base_addr;
@@ -248,14 +252,11 @@
char nvram_name[BRCMF_FW_NAME_LEN];
void __iomem *regs;
void __iomem *tcm;
- u32 tcm_size;
u32 ram_base;
u32 ram_size;
struct brcmf_chip *ci;
u32 coreid;
- u32 generic_corerev;
struct brcmf_pcie_shared_info shared;
- void (*ringbell)(struct brcmf_pciedev_info *devinfo);
wait_queue_head_t mbdata_resp_wait;
bool mbdata_completed;
bool irq_allocated;
@@ -267,6 +268,7 @@
u16 (*read_ptr)(struct brcmf_pciedev_info *devinfo, u32 mem_offset);
void (*write_ptr)(struct brcmf_pciedev_info *devinfo, u32 mem_offset,
u16 value);
+ struct brcmf_mp_device *settings;
};
struct brcmf_pcie_ringbuf {
@@ -675,10 +677,8 @@
brcmf_dbg(PCIE, "D2H_MB_DATA: DEEP SLEEP EXIT\n");
if (dtoh_mb_data & BRCMF_D2H_DEV_D3_ACK) {
brcmf_dbg(PCIE, "D2H_MB_DATA: D3 ACK\n");
- if (waitqueue_active(&devinfo->mbdata_resp_wait)) {
- devinfo->mbdata_completed = true;
- wake_up(&devinfo->mbdata_resp_wait);
- }
+ devinfo->mbdata_completed = true;
+ wake_up(&devinfo->mbdata_resp_wait);
}
}
@@ -742,68 +742,22 @@
}
-static __used void brcmf_pcie_ringbell_v1(struct brcmf_pciedev_info *devinfo)
-{
- u32 reg_value;
-
- brcmf_dbg(PCIE, "RING !\n");
- reg_value = brcmf_pcie_read_reg32(devinfo,
- BRCMF_PCIE_PCIE2REG_MAILBOXINT);
- reg_value |= BRCMF_PCIE2_INTB;
- brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT,
- reg_value);
-}
-
-
-static void brcmf_pcie_ringbell_v2(struct brcmf_pciedev_info *devinfo)
-{
- brcmf_dbg(PCIE, "RING !\n");
- /* Any arbitrary value will do, lets use 1 */
- brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_H2D_MAILBOX, 1);
-}
-
-
static void brcmf_pcie_intr_disable(struct brcmf_pciedev_info *devinfo)
{
- if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1)
- pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_INTMASK,
- 0);
- else
- brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXMASK,
- 0);
+ brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXMASK, 0);
}
static void brcmf_pcie_intr_enable(struct brcmf_pciedev_info *devinfo)
{
- if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1)
- pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_INTMASK,
- BRCMF_PCIE_INT_DEF);
- else
- brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXMASK,
- BRCMF_PCIE_MB_INT_D2H_DB |
- BRCMF_PCIE_MB_INT_FN0_0 |
- BRCMF_PCIE_MB_INT_FN0_1);
+ brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXMASK,
+ BRCMF_PCIE_MB_INT_D2H_DB |
+ BRCMF_PCIE_MB_INT_FN0_0 |
+ BRCMF_PCIE_MB_INT_FN0_1);
}
-static irqreturn_t brcmf_pcie_quick_check_isr_v1(int irq, void *arg)
-{
- struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg;
- u32 status;
-
- status = 0;
- pci_read_config_dword(devinfo->pdev, BRCMF_PCIE_REG_INTSTATUS, &status);
- if (status) {
- brcmf_pcie_intr_disable(devinfo);
- brcmf_dbg(PCIE, "Enter\n");
- return IRQ_WAKE_THREAD;
- }
- return IRQ_NONE;
-}
-
-
-static irqreturn_t brcmf_pcie_quick_check_isr_v2(int irq, void *arg)
+static irqreturn_t brcmf_pcie_quick_check_isr(int irq, void *arg)
{
struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg;
@@ -816,29 +770,7 @@
}
-static irqreturn_t brcmf_pcie_isr_thread_v1(int irq, void *arg)
-{
- struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg;
- const struct pci_dev *pdev = devinfo->pdev;
- u32 status;
-
- devinfo->in_irq = true;
- status = 0;
- pci_read_config_dword(pdev, BRCMF_PCIE_REG_INTSTATUS, &status);
- brcmf_dbg(PCIE, "Enter %x\n", status);
- if (status) {
- pci_write_config_dword(pdev, BRCMF_PCIE_REG_INTSTATUS, status);
- if (devinfo->state == BRCMFMAC_PCIE_STATE_UP)
- brcmf_proto_msgbuf_rx_trigger(&devinfo->pdev->dev);
- }
- if (devinfo->state == BRCMFMAC_PCIE_STATE_UP)
- brcmf_pcie_intr_enable(devinfo);
- devinfo->in_irq = false;
- return IRQ_HANDLED;
-}
-
-
-static irqreturn_t brcmf_pcie_isr_thread_v2(int irq, void *arg)
+static irqreturn_t brcmf_pcie_isr_thread(int irq, void *arg)
{
struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg;
u32 status;
@@ -875,28 +807,14 @@
brcmf_pcie_intr_disable(devinfo);
brcmf_dbg(PCIE, "Enter\n");
- /* is it a v1 or v2 implementation */
+
pci_enable_msi(pdev);
- if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1) {
- if (request_threaded_irq(pdev->irq,
- brcmf_pcie_quick_check_isr_v1,
- brcmf_pcie_isr_thread_v1,
- IRQF_SHARED, "brcmf_pcie_intr",
- devinfo)) {
- pci_disable_msi(pdev);
- brcmf_err("Failed to request IRQ %d\n", pdev->irq);
- return -EIO;
- }
- } else {
- if (request_threaded_irq(pdev->irq,
- brcmf_pcie_quick_check_isr_v2,
- brcmf_pcie_isr_thread_v2,
- IRQF_SHARED, "brcmf_pcie_intr",
- devinfo)) {
- pci_disable_msi(pdev);
- brcmf_err("Failed to request IRQ %d\n", pdev->irq);
- return -EIO;
- }
+ if (request_threaded_irq(pdev->irq, brcmf_pcie_quick_check_isr,
+ brcmf_pcie_isr_thread, IRQF_SHARED,
+ "brcmf_pcie_intr", devinfo)) {
+ pci_disable_msi(pdev);
+ brcmf_err("Failed to request IRQ %d\n", pdev->irq);
+ return -EIO;
}
devinfo->irq_allocated = true;
return 0;
@@ -927,16 +845,9 @@
if (devinfo->in_irq)
brcmf_err("Still in IRQ (processing) !!!\n");
- if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1) {
- status = 0;
- pci_read_config_dword(pdev, BRCMF_PCIE_REG_INTSTATUS, &status);
- pci_write_config_dword(pdev, BRCMF_PCIE_REG_INTSTATUS, status);
- } else {
- status = brcmf_pcie_read_reg32(devinfo,
- BRCMF_PCIE_PCIE2REG_MAILBOXINT);
- brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT,
- status);
- }
+ status = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT);
+ brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT, status);
+
devinfo->irq_allocated = false;
}
@@ -985,7 +896,9 @@
if (devinfo->state != BRCMFMAC_PCIE_STATE_UP)
return -EIO;
- devinfo->ringbell(devinfo);
+ brcmf_dbg(PCIE, "RING !\n");
+ /* Any arbitrary value will do, lets use 1 */
+ brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_H2D_MAILBOX, 1);
return 0;
}
@@ -1412,6 +1325,28 @@
};
+static void
+brcmf_pcie_adjust_ramsize(struct brcmf_pciedev_info *devinfo, u8 *data,
+ u32 data_len)
+{
+ __le32 *field;
+ u32 newsize;
+
+ if (data_len < BRCMF_RAMSIZE_OFFSET + 8)
+ return;
+
+ field = (__le32 *)&data[BRCMF_RAMSIZE_OFFSET];
+ if (le32_to_cpup(field) != BRCMF_RAMSIZE_MAGIC)
+ return;
+ field++;
+ newsize = le32_to_cpup(field);
+
+ brcmf_dbg(PCIE, "Found ramsize info in FW, adjusting to 0x%x\n",
+ newsize);
+ devinfo->ci->ramsize = newsize;
+}
+
+
static int
brcmf_pcie_init_share_ram_info(struct brcmf_pciedev_info *devinfo,
u32 sharedram_addr)
@@ -1477,9 +1412,6 @@
u32 address;
u32 resetintr;
- devinfo->ringbell = brcmf_pcie_ringbell_v2;
- devinfo->generic_corerev = BRCMF_PCIE_GENREV2;
-
brcmf_dbg(PCIE, "Halt ARM.\n");
err = brcmf_pcie_enter_download_state(devinfo);
if (err)
@@ -1566,8 +1498,7 @@
}
devinfo->regs = ioremap_nocache(bar0_addr, BRCMF_PCIE_REG_MAP_SIZE);
- devinfo->tcm = ioremap_nocache(bar1_addr, BRCMF_PCIE_TCM_MAP_SIZE);
- devinfo->tcm_size = BRCMF_PCIE_TCM_MAP_SIZE;
+ devinfo->tcm = ioremap_nocache(bar1_addr, bar1_size);
if (!devinfo->regs || !devinfo->tcm) {
brcmf_err("ioremap() failed (%p,%p)\n", devinfo->regs,
@@ -1576,8 +1507,9 @@
}
brcmf_dbg(PCIE, "Phys addr : reg space = %p base addr %#016llx\n",
devinfo->regs, (unsigned long long)bar0_addr);
- brcmf_dbg(PCIE, "Phys addr : mem space = %p base addr %#016llx\n",
- devinfo->tcm, (unsigned long long)bar1_addr);
+ brcmf_dbg(PCIE, "Phys addr : mem space = %p base addr %#016llx size 0x%x\n",
+ devinfo->tcm, (unsigned long long)bar1_addr,
+ (unsigned int)bar1_size);
return 0;
}
@@ -1594,16 +1526,16 @@
}
-static int brcmf_pcie_attach_bus(struct device *dev)
+static int brcmf_pcie_attach_bus(struct brcmf_pciedev_info *devinfo)
{
int ret;
/* Attach to the common driver interface */
- ret = brcmf_attach(dev);
+ ret = brcmf_attach(&devinfo->pdev->dev, devinfo->settings);
if (ret) {
brcmf_err("brcmf_attach failed\n");
} else {
- ret = brcmf_bus_start(dev);
+ ret = brcmf_bus_start(&devinfo->pdev->dev);
if (ret)
brcmf_err("dongle is not responding\n");
}
@@ -1694,6 +1626,13 @@
brcmf_pcie_attach(devinfo);
+ /* Some of the firmwares have the size of the memory of the device
+ * defined inside the firmware. This is because part of the memory in
+ * the device is shared and the devision is determined by FW. Parse
+ * the firmware and adjust the chip memory size now.
+ */
+ brcmf_pcie_adjust_ramsize(devinfo, (u8 *)fw->data, fw->size);
+
ret = brcmf_pcie_download_fw_nvram(devinfo, fw, nvram, nvram_len);
if (ret)
goto fail;
@@ -1734,7 +1673,7 @@
init_waitqueue_head(&devinfo->mbdata_resp_wait);
brcmf_pcie_intr_enable(devinfo);
- if (brcmf_pcie_attach_bus(bus->dev) == 0)
+ if (brcmf_pcie_attach_bus(devinfo) == 0)
return;
brcmf_pcie_bus_console_read(devinfo);
@@ -1778,6 +1717,15 @@
goto fail;
}
+ devinfo->settings = brcmf_get_module_param(&devinfo->pdev->dev,
+ BRCMF_BUSTYPE_PCIE,
+ devinfo->ci->chip,
+ devinfo->ci->chiprev);
+ if (!devinfo->settings) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
bus = kzalloc(sizeof(*bus), GFP_KERNEL);
if (!bus) {
ret = -ENOMEM;
@@ -1822,6 +1770,8 @@
brcmf_pcie_release_resource(devinfo);
if (devinfo->ci)
brcmf_chip_detach(devinfo->ci);
+ if (devinfo->settings)
+ brcmf_release_module_param(devinfo->settings);
kfree(pcie_bus_dev);
kfree(devinfo);
return ret;
@@ -1861,6 +1811,8 @@
if (devinfo->ci)
brcmf_chip_detach(devinfo->ci);
+ if (devinfo->settings)
+ brcmf_release_module_param(devinfo->settings);
kfree(devinfo);
dev_set_drvdata(&pdev->dev, NULL);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
index c790fa8..43fd3f4 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
@@ -33,8 +33,6 @@
#include <linux/bcma/bcma.h>
#include <linux/debugfs.h>
#include <linux/vmalloc.h>
-#include <linux/platform_data/brcmfmac-sdio.h>
-#include <linux/moduleparam.h>
#include <asm/unaligned.h>
#include <defs.h>
#include <brcmu_wifi.h>
@@ -44,6 +42,8 @@
#include "sdio.h"
#include "chip.h"
#include "firmware.h"
+#include "core.h"
+#include "common.h"
#define DCMD_RESP_TIMEOUT msecs_to_jiffies(2500)
#define CTL_DONE_TIMEOUT msecs_to_jiffies(2500)
@@ -2442,15 +2442,17 @@
static inline void brcmf_sdio_clrintr(struct brcmf_sdio *bus)
{
+ struct brcmf_sdio_dev *sdiodev;
unsigned long flags;
- if (bus->sdiodev->oob_irq_requested) {
- spin_lock_irqsave(&bus->sdiodev->irq_en_lock, flags);
- if (!bus->sdiodev->irq_en && !atomic_read(&bus->ipend)) {
- enable_irq(bus->sdiodev->pdata->oob_irq_nr);
- bus->sdiodev->irq_en = true;
+ sdiodev = bus->sdiodev;
+ if (sdiodev->oob_irq_requested) {
+ spin_lock_irqsave(&sdiodev->irq_en_lock, flags);
+ if (!sdiodev->irq_en && !atomic_read(&bus->ipend)) {
+ enable_irq(sdiodev->settings->bus.sdio.oob_irq_nr);
+ sdiodev->irq_en = true;
}
- spin_unlock_irqrestore(&bus->sdiodev->irq_en_lock, flags);
+ spin_unlock_irqrestore(&sdiodev->irq_en_lock, flags);
}
}
@@ -3394,9 +3396,7 @@
sizeof(u32));
} else {
/* otherwise, set txglomalign */
- value = 4;
- if (sdiodev->pdata)
- value = sdiodev->pdata->sd_sgentry_align;
+ value = sdiodev->settings->bus.sdio.sd_sgentry_align;
/* SDIO ADMA requires at least 32 bit alignment */
value = max_t(u32, value, 4);
err = brcmf_iovar_data_set(dev, "bus:txglomalign", &value,
@@ -3775,26 +3775,28 @@
static bool
brcmf_sdio_probe_attach(struct brcmf_sdio *bus)
{
+ struct brcmf_sdio_dev *sdiodev;
u8 clkctl = 0;
int err = 0;
int reg_addr;
u32 reg_val;
u32 drivestrength;
- sdio_claim_host(bus->sdiodev->func[1]);
+ sdiodev = bus->sdiodev;
+ sdio_claim_host(sdiodev->func[1]);
pr_debug("F1 signature read @0x18000000=0x%4x\n",
- brcmf_sdiod_regrl(bus->sdiodev, SI_ENUM_BASE, NULL));
+ brcmf_sdiod_regrl(sdiodev, SI_ENUM_BASE, NULL));
/*
* Force PLL off until brcmf_chip_attach()
* programs PLL control regs
*/
- brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
+ brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
BRCMF_INIT_CLKCTL1, &err);
if (!err)
- clkctl = brcmf_sdiod_regrb(bus->sdiodev,
+ clkctl = brcmf_sdiod_regrb(sdiodev,
SBSDIO_FUNC1_CHIPCLKCSR, &err);
if (err || ((clkctl & ~SBSDIO_AVBITS) != BRCMF_INIT_CLKCTL1)) {
@@ -3803,50 +3805,81 @@
goto fail;
}
- bus->ci = brcmf_chip_attach(bus->sdiodev, &brcmf_sdio_buscore_ops);
+ bus->ci = brcmf_chip_attach(sdiodev, &brcmf_sdio_buscore_ops);
if (IS_ERR(bus->ci)) {
brcmf_err("brcmf_chip_attach failed!\n");
bus->ci = NULL;
goto fail;
}
+ sdiodev->settings = brcmf_get_module_param(sdiodev->dev,
+ BRCMF_BUSTYPE_SDIO,
+ bus->ci->chip,
+ bus->ci->chiprev);
+ if (!sdiodev->settings) {
+ brcmf_err("Failed to get device parameters\n");
+ goto fail;
+ }
+ /* platform specific configuration:
+ * alignments must be at least 4 bytes for ADMA
+ */
+ bus->head_align = ALIGNMENT;
+ bus->sgentry_align = ALIGNMENT;
+ if (sdiodev->settings->bus.sdio.sd_head_align > ALIGNMENT)
+ bus->head_align = sdiodev->settings->bus.sdio.sd_head_align;
+ if (sdiodev->settings->bus.sdio.sd_sgentry_align > ALIGNMENT)
+ bus->sgentry_align =
+ sdiodev->settings->bus.sdio.sd_sgentry_align;
+
+ /* allocate scatter-gather table. sg support
+ * will be disabled upon allocation failure.
+ */
+ brcmf_sdiod_sgtable_alloc(sdiodev);
+
+#ifdef CONFIG_PM_SLEEP
+ /* wowl can be supported when KEEP_POWER is true and (WAKE_SDIO_IRQ
+ * is true or when platform data OOB irq is true).
+ */
+ if ((sdio_get_host_pm_caps(sdiodev->func[1]) & MMC_PM_KEEP_POWER) &&
+ ((sdio_get_host_pm_caps(sdiodev->func[1]) & MMC_PM_WAKE_SDIO_IRQ) ||
+ (sdiodev->settings->bus.sdio.oob_irq_supported)))
+ sdiodev->bus_if->wowl_supported = true;
+#endif
if (brcmf_sdio_kso_init(bus)) {
brcmf_err("error enabling KSO\n");
goto fail;
}
- if ((bus->sdiodev->pdata) && (bus->sdiodev->pdata->drive_strength))
- drivestrength = bus->sdiodev->pdata->drive_strength;
+ if (sdiodev->settings->bus.sdio.drive_strength)
+ drivestrength = sdiodev->settings->bus.sdio.drive_strength;
else
drivestrength = DEFAULT_SDIO_DRIVE_STRENGTH;
- brcmf_sdio_drivestrengthinit(bus->sdiodev, bus->ci, drivestrength);
+ brcmf_sdio_drivestrengthinit(sdiodev, bus->ci, drivestrength);
/* Set card control so an SDIO card reset does a WLAN backplane reset */
- reg_val = brcmf_sdiod_regrb(bus->sdiodev,
- SDIO_CCCR_BRCM_CARDCTRL, &err);
+ reg_val = brcmf_sdiod_regrb(sdiodev, SDIO_CCCR_BRCM_CARDCTRL, &err);
if (err)
goto fail;
reg_val |= SDIO_CCCR_BRCM_CARDCTRL_WLANRESET;
- brcmf_sdiod_regwb(bus->sdiodev,
- SDIO_CCCR_BRCM_CARDCTRL, reg_val, &err);
+ brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_BRCM_CARDCTRL, reg_val, &err);
if (err)
goto fail;
/* set PMUControl so a backplane reset does PMU state reload */
reg_addr = CORE_CC_REG(brcmf_chip_get_pmu(bus->ci)->base, pmucontrol);
- reg_val = brcmf_sdiod_regrl(bus->sdiodev, reg_addr, &err);
+ reg_val = brcmf_sdiod_regrl(sdiodev, reg_addr, &err);
if (err)
goto fail;
reg_val |= (BCMA_CC_PMU_CTL_RES_RELOAD << BCMA_CC_PMU_CTL_RES_SHIFT);
- brcmf_sdiod_regwl(bus->sdiodev, reg_addr, reg_val, &err);
+ brcmf_sdiod_regwl(sdiodev, reg_addr, reg_val, &err);
if (err)
goto fail;
- sdio_release_host(bus->sdiodev->func[1]);
+ sdio_release_host(sdiodev->func[1]);
brcmu_pktq_init(&bus->txq, (PRIOMASK + 1), TXQLEN);
@@ -3867,7 +3900,7 @@
return true;
fail:
- sdio_release_host(bus->sdiodev->func[1]);
+ sdio_release_host(sdiodev->func[1]);
return false;
}
@@ -4045,18 +4078,6 @@
bus->txminmax = BRCMF_TXMINMAX;
bus->tx_seq = SDPCM_SEQ_WRAP - 1;
- /* platform specific configuration:
- * alignments must be at least 4 bytes for ADMA
- */
- bus->head_align = ALIGNMENT;
- bus->sgentry_align = ALIGNMENT;
- if (sdiodev->pdata) {
- if (sdiodev->pdata->sd_head_align > ALIGNMENT)
- bus->head_align = sdiodev->pdata->sd_head_align;
- if (sdiodev->pdata->sd_sgentry_align > ALIGNMENT)
- bus->sgentry_align = sdiodev->pdata->sd_sgentry_align;
- }
-
/* single-threaded workqueue */
wq = alloc_ordered_workqueue("brcmf_wq/%s", WQ_MEM_RECLAIM,
dev_name(&sdiodev->func[1]->dev));
@@ -4107,7 +4128,7 @@
bus->tx_hdrlen = SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN;
/* Attach to the common layer, reserve hdr space */
- ret = brcmf_attach(bus->sdiodev->dev);
+ ret = brcmf_attach(bus->sdiodev->dev, bus->sdiodev->settings);
if (ret != 0) {
brcmf_err("brcmf_attach failed\n");
goto fail;
@@ -4211,6 +4232,8 @@
}
brcmf_chip_detach(bus->ci);
}
+ if (bus->sdiodev->settings)
+ brcmf_release_module_param(bus->sdiodev->settings);
kfree(bus->rxbuf);
kfree(bus->hdrbuf);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
index 23f2231..dcf0ce8 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
@@ -184,7 +184,7 @@
struct brcmf_sdio *bus;
struct device *dev;
struct brcmf_bus *bus_if;
- struct brcmfmac_sdio_platform_data *pdata;
+ struct brcmf_mp_device *settings;
bool oob_irq_requested;
bool irq_en; /* irq enable flags */
spinlock_t irq_en_lock;
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
index c72b7b3..869eb82 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
@@ -27,6 +27,8 @@
#include "debug.h"
#include "firmware.h"
#include "usb.h"
+#include "core.h"
+#include "common.h"
#define IOCTL_RESP_TIMEOUT msecs_to_jiffies(2000)
@@ -171,6 +173,7 @@
struct urb *bulk_urb; /* used for FW download */
bool wowl_enabled;
+ struct brcmf_mp_device *settings;
};
static void brcmf_usb_rx_refill(struct brcmf_usbdev_info *devinfo,
@@ -1027,6 +1030,9 @@
kfree(devinfo->tx_reqs);
kfree(devinfo->rx_reqs);
+
+ if (devinfo->settings)
+ brcmf_release_module_param(devinfo->settings);
}
@@ -1136,7 +1142,7 @@
int ret;
/* Attach to the common driver interface */
- ret = brcmf_attach(devinfo->dev);
+ ret = brcmf_attach(devinfo->dev, devinfo->settings);
if (ret) {
brcmf_err("brcmf_attach failed\n");
return ret;
@@ -1223,6 +1229,14 @@
bus->wowl_supported = true;
#endif
+ devinfo->settings = brcmf_get_module_param(bus->dev, BRCMF_BUSTYPE_USB,
+ bus_pub->devid,
+ bus_pub->chiprev);
+ if (!devinfo->settings) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
if (!brcmf_usb_dlneeded(devinfo)) {
ret = brcmf_usb_bus_setup(devinfo);
if (ret)
diff --git a/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h
index 3f68dd5..7b9a779 100644
--- a/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h
+++ b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h
@@ -236,6 +236,8 @@
#define WPA2_AUTH_RESERVED3 0x0200
#define WPA2_AUTH_RESERVED4 0x0400
#define WPA2_AUTH_RESERVED5 0x0800
+#define WPA2_AUTH_1X_SHA256 0x1000 /* 1X with SHA256 key derivation */
+#define WPA2_AUTH_PSK_SHA256 0x8000 /* PSK with SHA256 key derivation */
#define DOT11_DEFAULT_RTS_LEN 2347
#define DOT11_DEFAULT_FRAG_LEN 2346
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/main.c b/drivers/net/wireless/intel/iwlwifi/dvm/main.c
index f62c2d7..85628127 100644
--- a/drivers/net/wireless/intel/iwlwifi/dvm/main.c
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/main.c
@@ -1652,10 +1652,10 @@
trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low,
table.data1, table.data2, table.line,
- table.blink1, table.blink2, table.ilink1,
- table.ilink2, table.bcon_time, table.gp1,
- table.gp2, table.gp3, table.ucode_ver,
- table.hw_ver, 0, table.brd_ver);
+ table.blink2, table.ilink1, table.ilink2,
+ table.bcon_time, table.gp1, table.gp2,
+ table.gp3, table.ucode_ver, table.hw_ver,
+ 0, table.brd_ver);
IWL_ERR(priv, "0x%08X | %-28s\n", table.error_id,
desc_lookup(table.error_id));
IWL_ERR(priv, "0x%08X | uPc\n", table.pc);
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-7000.c b/drivers/net/wireless/intel/iwlwifi/iwl-7000.c
index fa41a5e..fc475ce 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-7000.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-7000.c
@@ -73,8 +73,8 @@
/* Highest firmware API version supported */
#define IWL7260_UCODE_API_MAX 17
#define IWL7265_UCODE_API_MAX 17
-#define IWL7265D_UCODE_API_MAX 20
-#define IWL3168_UCODE_API_MAX 20
+#define IWL7265D_UCODE_API_MAX 21
+#define IWL3168_UCODE_API_MAX 21
/* Oldest version we won't warn about */
#define IWL7260_UCODE_API_OK 13
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-8000.c b/drivers/net/wireless/intel/iwlwifi/iwl-8000.c
index bce9b3420..97be104 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-8000.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-8000.c
@@ -70,8 +70,8 @@
#include "iwl-agn-hw.h"
/* Highest firmware API version supported */
-#define IWL8000_UCODE_API_MAX 20
-#define IWL8265_UCODE_API_MAX 20
+#define IWL8000_UCODE_API_MAX 21
+#define IWL8265_UCODE_API_MAX 21
/* Oldest version we won't warn about */
#define IWL8000_UCODE_API_OK 13
@@ -217,6 +217,7 @@
.nvm_ver = IWL8000_NVM_VERSION,
.nvm_calib_ver = IWL8000_TX_POWER_VERSION,
.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
+ .vht_mu_mimo_supported = true,
};
const struct iwl_cfg iwl4165_2ac_cfg = {
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-9000.c b/drivers/net/wireless/intel/iwlwifi/iwl-9000.c
index 4b93404..318b1dc 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-9000.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-9000.c
@@ -55,7 +55,7 @@
#include "iwl-agn-hw.h"
/* Highest firmware API version supported */
-#define IWL9000_UCODE_API_MAX 20
+#define IWL9000_UCODE_API_MAX 21
/* Oldest version we won't warn about */
#define IWL9000_UCODE_API_OK 13
@@ -139,7 +139,9 @@
.smem_len = IWL9000_SMEM_LEN, \
.thermal_params = &iwl9000_tt_params, \
.apmg_not_supported = true, \
- .mq_rx_supported = true
+ .mq_rx_supported = true, \
+ .vht_mu_mimo_supported = true, \
+ .mac_addr_from_csr = true
const struct iwl_cfg iwl9260_2ac_cfg = {
.name = "Intel(R) Dual Band Wireless AC 9260",
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h
index dad5570..3e4d346 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h
@@ -297,6 +297,7 @@
* @host_interrupt_operation_mode: device needs host interrupt operation
* mode set
* @nvm_hw_section_num: the ID of the HW NVM section
+ * @mac_addr_from_csr: read HW address from CSR registers
* @features: hw features, any combination of feature_whitelist
* @pwr_tx_backoffs: translation table between power limits and backoffs
* @max_rx_agg_size: max RX aggregation size of the ADDBA request/response
@@ -312,6 +313,7 @@
* @smem_offset: offset from which the SMEM begins
* @smem_len: the length of SMEM
* @mq_rx_supported: multi-queue rx support
+ * @vht_mu_mimo_supported: VHT MU-MIMO support
*
* We enable the driver to be backward compatible wrt. hardware features.
* API differences in uCode shouldn't be handled here but through TLVs
@@ -344,6 +346,7 @@
const bool host_interrupt_operation_mode;
bool high_temp;
u8 nvm_hw_section_num;
+ bool mac_addr_from_csr;
bool lp_xtal_workaround;
const struct iwl_pwr_tx_backoff *pwr_tx_backoffs;
bool no_power_up_nic_in_init;
@@ -364,6 +367,7 @@
const struct iwl_tt_params *thermal_params;
bool apmg_not_supported;
bool mq_rx_supported;
+ bool vht_mu_mimo_supported;
};
/*
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h
index 163b21b..b978f6c 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h
@@ -7,6 +7,7 @@
*
* Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -549,4 +550,62 @@
DTS_DIODE_REG_FLAGS_PASS_ONCE = 0x00000080, /* bits [7:7] */
};
+/*****************************************************************************
+ * MSIX related registers *
+ *****************************************************************************/
+
+#define CSR_MSIX_BASE (0x2000)
+#define CSR_MSIX_FH_INT_CAUSES_AD (CSR_MSIX_BASE + 0x800)
+#define CSR_MSIX_FH_INT_MASK_AD (CSR_MSIX_BASE + 0x804)
+#define CSR_MSIX_HW_INT_CAUSES_AD (CSR_MSIX_BASE + 0x808)
+#define CSR_MSIX_HW_INT_MASK_AD (CSR_MSIX_BASE + 0x80C)
+#define CSR_MSIX_AUTOMASK_ST_AD (CSR_MSIX_BASE + 0x810)
+#define CSR_MSIX_RX_IVAR_AD_REG (CSR_MSIX_BASE + 0x880)
+#define CSR_MSIX_IVAR_AD_REG (CSR_MSIX_BASE + 0x890)
+#define CSR_MSIX_PENDING_PBA_AD (CSR_MSIX_BASE + 0x1000)
+#define CSR_MSIX_RX_IVAR(cause) (CSR_MSIX_RX_IVAR_AD_REG + (cause))
+#define CSR_MSIX_IVAR(cause) (CSR_MSIX_IVAR_AD_REG + (cause))
+
+#define MSIX_FH_INT_CAUSES_Q(q) (q)
+
+/*
+ * Causes for the FH register interrupts
+ */
+enum msix_fh_int_causes {
+ MSIX_FH_INT_CAUSES_D2S_CH0_NUM = BIT(16),
+ MSIX_FH_INT_CAUSES_D2S_CH1_NUM = BIT(17),
+ MSIX_FH_INT_CAUSES_S2D = BIT(19),
+ MSIX_FH_INT_CAUSES_FH_ERR = BIT(21),
+};
+
+/*
+ * Causes for the HW register interrupts
+ */
+enum msix_hw_int_causes {
+ MSIX_HW_INT_CAUSES_REG_ALIVE = BIT(0),
+ MSIX_HW_INT_CAUSES_REG_WAKEUP = BIT(1),
+ MSIX_HW_INT_CAUSES_REG_CT_KILL = BIT(6),
+ MSIX_HW_INT_CAUSES_REG_RF_KILL = BIT(7),
+ MSIX_HW_INT_CAUSES_REG_PERIODIC = BIT(8),
+ MSIX_HW_INT_CAUSES_REG_SW_ERR = BIT(25),
+ MSIX_HW_INT_CAUSES_REG_SCD = BIT(26),
+ MSIX_HW_INT_CAUSES_REG_FH_TX = BIT(27),
+ MSIX_HW_INT_CAUSES_REG_HW_ERR = BIT(29),
+ MSIX_HW_INT_CAUSES_REG_HAP = BIT(30),
+};
+
+#define MSIX_MIN_INTERRUPT_VECTORS 2
+#define MSIX_AUTO_CLEAR_CAUSE 0
+#define MSIX_NON_AUTO_CLEAR_CAUSE BIT(7)
+
+/*****************************************************************************
+ * HW address related registers *
+ *****************************************************************************/
+
+#define CSR_ADDR_BASE (0x380)
+#define CSR_MAC_ADDR0_OTP (CSR_ADDR_BASE)
+#define CSR_MAC_ADDR1_OTP (CSR_ADDR_BASE + 4)
+#define CSR_MAC_ADDR0_STRAP (CSR_ADDR_BASE + 8)
+#define CSR_MAC_ADDR1_STRAP (CSR_ADDR_BASE + 0xC)
+
#endif /* !__iwl_csr_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-iwlwifi.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-iwlwifi.h
index 22786d7..f02e2c8 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-iwlwifi.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-iwlwifi.h
@@ -2,6 +2,7 @@
*
* Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as
@@ -73,12 +74,12 @@
TP_ARGS(dev, trans, pkt, len),
TP_STRUCT__entry(
DEV_ENTRY
- __field(u8, cmd)
+ __field(u16, cmd)
__dynamic_array(u8, rxbuf, iwl_rx_trace_len(trans, pkt, len))
),
TP_fast_assign(
DEV_ASSIGN;
- __entry->cmd = pkt->hdr.cmd;
+ __entry->cmd = WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd);
memcpy(__get_dynamic_array(rxbuf), pkt,
iwl_rx_trace_len(trans, pkt, len));
),
@@ -121,13 +122,12 @@
TRACE_EVENT(iwlwifi_dev_ucode_error,
TP_PROTO(const struct device *dev, u32 desc, u32 tsf_low,
- u32 data1, u32 data2, u32 line, u32 blink1,
- u32 blink2, u32 ilink1, u32 ilink2, u32 bcon_time,
- u32 gp1, u32 gp2, u32 gp3, u32 major, u32 minor, u32 hw_ver,
- u32 brd_ver),
+ u32 data1, u32 data2, u32 line, u32 blink2, u32 ilink1,
+ u32 ilink2, u32 bcon_time, u32 gp1, u32 gp2, u32 rev_type,
+ u32 major, u32 minor, u32 hw_ver, u32 brd_ver),
TP_ARGS(dev, desc, tsf_low, data1, data2, line,
- blink1, blink2, ilink1, ilink2, bcon_time, gp1, gp2,
- gp3, major, minor, hw_ver, brd_ver),
+ blink2, ilink1, ilink2, bcon_time, gp1, gp2,
+ rev_type, major, minor, hw_ver, brd_ver),
TP_STRUCT__entry(
DEV_ENTRY
__field(u32, desc)
@@ -135,14 +135,13 @@
__field(u32, data1)
__field(u32, data2)
__field(u32, line)
- __field(u32, blink1)
__field(u32, blink2)
__field(u32, ilink1)
__field(u32, ilink2)
__field(u32, bcon_time)
__field(u32, gp1)
__field(u32, gp2)
- __field(u32, gp3)
+ __field(u32, rev_type)
__field(u32, major)
__field(u32, minor)
__field(u32, hw_ver)
@@ -155,29 +154,27 @@
__entry->data1 = data1;
__entry->data2 = data2;
__entry->line = line;
- __entry->blink1 = blink1;
__entry->blink2 = blink2;
__entry->ilink1 = ilink1;
__entry->ilink2 = ilink2;
__entry->bcon_time = bcon_time;
__entry->gp1 = gp1;
__entry->gp2 = gp2;
- __entry->gp3 = gp3;
+ __entry->rev_type = rev_type;
__entry->major = major;
__entry->minor = minor;
__entry->hw_ver = hw_ver;
__entry->brd_ver = brd_ver;
),
TP_printk("[%s] #%02d %010u data 0x%08X 0x%08X line %u, "
- "blink 0x%05X 0x%05X ilink 0x%05X 0x%05X "
- "bcon_tm %010u gp 0x%08X 0x%08X 0x%08X major 0x%08X "
+ "blink2 0x%05X ilink 0x%05X 0x%05X "
+ "bcon_tm %010u gp 0x%08X 0x%08X rev_type 0x%08X major 0x%08X "
"minor 0x%08X hw 0x%08X brd 0x%08X",
__get_str(dev), __entry->desc, __entry->tsf_low,
- __entry->data1,
- __entry->data2, __entry->line, __entry->blink1,
+ __entry->data1, __entry->data2, __entry->line,
__entry->blink2, __entry->ilink1, __entry->ilink2,
__entry->bcon_time, __entry->gp1, __entry->gp2,
- __entry->gp3, __entry->major, __entry->minor,
+ __entry->rev_type, __entry->major, __entry->minor,
__entry->hw_ver, __entry->brd_ver)
);
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
index ab4c2a0..f899666 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
@@ -7,6 +7,7 @@
*
* Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -33,6 +34,7 @@
*
* Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -374,15 +376,12 @@
return 0;
}
-static int iwl_store_gscan_capa(struct iwl_fw *fw, const u8 *data,
- const u32 len)
+static void iwl_store_gscan_capa(struct iwl_fw *fw, const u8 *data,
+ const u32 len)
{
struct iwl_fw_gscan_capabilities *fw_capa = (void *)data;
struct iwl_gscan_capabilities *capa = &fw->gscan_capa;
- if (len < sizeof(*fw_capa))
- return -EINVAL;
-
capa->max_scan_cache_size = le32_to_cpu(fw_capa->max_scan_cache_size);
capa->max_scan_buckets = le32_to_cpu(fw_capa->max_scan_buckets);
capa->max_ap_cache_per_scan =
@@ -395,7 +394,15 @@
le32_to_cpu(fw_capa->max_significant_change_aps);
capa->max_bssid_history_entries =
le32_to_cpu(fw_capa->max_bssid_history_entries);
- return 0;
+ capa->max_hotlist_ssids = le32_to_cpu(fw_capa->max_hotlist_ssids);
+ capa->max_number_epno_networks =
+ le32_to_cpu(fw_capa->max_number_epno_networks);
+ capa->max_number_epno_networks_by_ssid =
+ le32_to_cpu(fw_capa->max_number_epno_networks_by_ssid);
+ capa->max_number_of_white_listed_ssid =
+ le32_to_cpu(fw_capa->max_number_of_white_listed_ssid);
+ capa->max_number_of_black_listed_ssid =
+ le32_to_cpu(fw_capa->max_number_of_black_listed_ssid);
}
/*
@@ -1023,8 +1030,15 @@
le32_to_cpup((__le32 *)tlv_data);
break;
case IWL_UCODE_TLV_FW_GSCAN_CAPA:
- if (iwl_store_gscan_capa(&drv->fw, tlv_data, tlv_len))
- goto invalid_tlv_len;
+ /*
+ * Don't return an error in case of a shorter tlv_len
+ * to enable loading of FW that has an old format
+ * of GSCAN capabilities TLV.
+ */
+ if (tlv_len < sizeof(struct iwl_fw_gscan_capabilities))
+ break;
+
+ iwl_store_gscan_capa(&drv->fw, tlv_data, tlv_len);
gscan_capa = true;
break;
default:
@@ -1033,7 +1047,8 @@
}
}
- if (usniffer_req && !*usniffer_images) {
+ if (!fw_has_capa(capa, IWL_UCODE_TLV_CAPA_USNIFFER_UNIFIED) &&
+ usniffer_req && !*usniffer_images) {
IWL_ERR(drv,
"user selected to work with usniffer but usniffer image isn't available in ucode package\n");
return -EINVAL;
@@ -1045,12 +1060,8 @@
return -EINVAL;
}
- /*
- * If ucode advertises that it supports GSCAN but GSCAN
- * capabilities TLV is not present, warn and continue without GSCAN.
- */
- if (fw_has_capa(capa, IWL_UCODE_TLV_CAPA_GSCAN_SUPPORT) &&
- WARN(!gscan_capa,
+ if (WARN(fw_has_capa(capa, IWL_UCODE_TLV_CAPA_GSCAN_SUPPORT) &&
+ !gscan_capa,
"GSCAN is supported but capabilities TLV is unavailable\n"))
__clear_bit((__force long)IWL_UCODE_TLV_CAPA_GSCAN_SUPPORT,
capa->_capa);
@@ -1718,3 +1729,7 @@
module_param_named(d0i3_timeout, iwlwifi_mod_params.d0i3_entry_delay,
uint, S_IRUGO);
MODULE_PARM_DESC(d0i3_timeout, "Timeout to D0i3 entry when idle (ms)");
+
+module_param_named(disable_11ac, iwlwifi_mod_params.disable_11ac, bool,
+ S_IRUGO);
+MODULE_PARM_DESC(disable_11ac, "Disable VHT capabilities");
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fh.h b/drivers/net/wireless/intel/iwlwifi/iwl-fh.h
index 4ab6682..582008a 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-fh.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-fh.h
@@ -6,7 +6,7 @@
* GPL LICENSE SUMMARY
*
* Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2015 Intel Deutschland GmbH
+ * Copyright(c) 2015 - 2016 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -32,7 +32,7 @@
* BSD LICENSE
*
* Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2015 Intel Deutschland GmbH
+ * Copyright(c) 2015 - 2016 Intel Deutschland GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -368,20 +368,24 @@
#define RFH_RXF_DMA_RBDCB_SIZE_512 (0x9 << RFH_RXF_DMA_RBDCB_SIZE_POS)
#define RFH_RXF_DMA_RBDCB_SIZE_1024 (0xA << RFH_RXF_DMA_RBDCB_SIZE_POS)
#define RFH_RXF_DMA_RBDCB_SIZE_2048 (0xB << RFH_RXF_DMA_RBDCB_SIZE_POS)
-#define RFH_RXF_DMA_MIN_RB_SIZE_MASK (0x03000000) /* bit 24-25 */
+#define RFH_RXF_DMA_MIN_RB_SIZE_MASK (0x03000000) /* bit 24-25 */
#define RFH_RXF_DMA_MIN_RB_SIZE_POS 24
-#define RFH_RXF_DMA_MIN_RB_4_8 (3 << RFH_RXF_DMA_MIN_RB_SIZE_POS)
-#define RFH_RXF_DMA_SINGLE_FRAME_MASK (0x20000000) /* bit 29 */
-#define RFH_DMA_EN_MASK (0xC0000000) /* bits 30-31*/
-#define RFH_DMA_EN_ENABLE_VAL BIT(31)
+#define RFH_RXF_DMA_MIN_RB_4_8 (3 << RFH_RXF_DMA_MIN_RB_SIZE_POS)
+#define RFH_RXF_DMA_DROP_TOO_LARGE_MASK (0x04000000) /* bit 26 */
+#define RFH_RXF_DMA_SINGLE_FRAME_MASK (0x20000000) /* bit 29 */
+#define RFH_DMA_EN_MASK (0xC0000000) /* bits 30-31*/
+#define RFH_DMA_EN_ENABLE_VAL BIT(31)
#define RFH_RXF_RXQ_ACTIVE 0xA0980C
#define RFH_GEN_CFG 0xA09800
+#define RFH_GEN_CFG_SERVICE_DMA_SNOOP BIT(0)
+#define RFH_GEN_CFG_RFH_DMA_SNOOP BIT(1)
+#define RFH_GEN_CFG_RB_CHUNK_SIZE BIT(4) /* 0 - 64B, 1- 128B */
#define RFH_GEN_CFG_DEFAULT_RXQ_NUM_MASK 0xF00
-#define RFH_GEN_CFG_SERVICE_DMA_SNOOP BIT(0)
-#define RFH_GEN_CFG_RFH_DMA_SNOOP BIT(1)
-#define DEFAULT_RXQ_NUM 8
+#define RFH_GEN_CFG_DEFAULT_RXQ_NUM_POS 8
+
+#define DEFAULT_RXQ_NUM 0
/* end of 9000 rx series registers */
@@ -507,9 +511,12 @@
*/
#define FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN (0x00000002)
-#define MQ_RX_TABLE_SIZE 512
-#define MQ_RX_TABLE_MASK (MQ_RX_TABLE_SIZE - 1)
-#define MQ_RX_POOL_SIZE MQ_RX_TABLE_MASK
+#define MQ_RX_TABLE_SIZE 512
+#define MQ_RX_TABLE_MASK (MQ_RX_TABLE_SIZE - 1)
+#define MQ_RX_NUM_RBDS (MQ_RX_TABLE_SIZE - 1)
+#define RX_POOL_SIZE (MQ_RX_NUM_RBDS + \
+ IWL_MAX_RX_HW_QUEUES * \
+ (RX_CLAIM_REQ_ALLOC - RX_POST_REQ_ALLOC))
#define RX_QUEUE_SIZE 256
#define RX_QUEUE_MASK 255
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h
index e2dbc67..15ec4e2 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h
@@ -318,6 +318,12 @@
* @IWL_UCODE_TLV_CAPA_BEACON_STORING: firmware will store the latest beacon
* from AP and will send it upon d0i3 exit.
* @IWL_UCODE_TLV_CAPA_LAR_SUPPORT_V2: support LAR API V2
+ * @IWL_UCODE_TLV_CAPA_CT_KILL_BY_FW: firmware responsible for CT-kill
+ * @IWL_UCODE_TLV_CAPA_TEMP_THS_REPORT_SUPPORT: supports temperature
+ * thresholds reporting
+ * @IWL_UCODE_TLV_CAPA_CTDP_SUPPORT: supports cTDP command
+ * @IWL_UCODE_TLV_CAPA_USNIFFER_UNIFIED: supports usniffer enabled in
+ * regular image.
*
* @NUM_IWL_UCODE_TLV_CAPA: number of bits used
*/
@@ -351,6 +357,10 @@
IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION = (__force iwl_ucode_tlv_capa_t)71,
IWL_UCODE_TLV_CAPA_BEACON_STORING = (__force iwl_ucode_tlv_capa_t)72,
IWL_UCODE_TLV_CAPA_LAR_SUPPORT_V2 = (__force iwl_ucode_tlv_capa_t)73,
+ IWL_UCODE_TLV_CAPA_CT_KILL_BY_FW = (__force iwl_ucode_tlv_capa_t)74,
+ IWL_UCODE_TLV_CAPA_TEMP_THS_REPORT_SUPPORT = (__force iwl_ucode_tlv_capa_t)75,
+ IWL_UCODE_TLV_CAPA_CTDP_SUPPORT = (__force iwl_ucode_tlv_capa_t)76,
+ IWL_UCODE_TLV_CAPA_USNIFFER_UNIFIED = (__force iwl_ucode_tlv_capa_t)77,
NUM_IWL_UCODE_TLV_CAPA
#ifdef __CHECKER__
@@ -799,6 +809,12 @@
* change APs.
* @max_bssid_history_entries: number of BSSID/RSSI entries that the device can
* hold.
+ * @max_hotlist_ssids: maximum number of entries for hotlist SSIDs.
+ * @max_number_epno_networks: max number of epno entries.
+ * @max_number_epno_networks_by_ssid: max number of epno entries if ssid is
+ * specified.
+ * @max_number_of_white_listed_ssid: max number of white listed SSIDs.
+ * @max_number_of_black_listed_ssid: max number of black listed SSIDs.
*/
struct iwl_fw_gscan_capabilities {
__le32 max_scan_cache_size;
@@ -809,6 +825,11 @@
__le32 max_hotlist_aps;
__le32 max_significant_change_aps;
__le32 max_bssid_history_entries;
+ __le32 max_hotlist_ssids;
+ __le32 max_number_epno_networks;
+ __le32 max_number_epno_networks_by_ssid;
+ __le32 max_number_of_white_listed_ssid;
+ __le32 max_number_of_black_listed_ssid;
} __packed;
#endif /* __iwl_fw_file_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw.h
index 85d6d6d..2942571 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-fw.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw.h
@@ -7,6 +7,7 @@
*
* Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -33,6 +34,7 @@
*
* Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -205,6 +207,12 @@
* change APs.
* @max_bssid_history_entries: number of BSSID/RSSI entries that the device can
* hold.
+ * @max_hotlist_ssids: maximum number of entries for hotlist SSIDs.
+ * @max_number_epno_networks: max number of epno entries.
+ * @max_number_epno_networks_by_ssid: max number of epno entries if ssid is
+ * specified.
+ * @max_number_of_white_listed_ssid: max number of white listed SSIDs.
+ * @max_number_of_black_listed_ssid: max number of black listed SSIDs.
*/
struct iwl_gscan_capabilities {
u32 max_scan_cache_size;
@@ -215,6 +223,11 @@
u32 max_hotlist_aps;
u32 max_significant_change_aps;
u32 max_bssid_history_entries;
+ u32 max_hotlist_ssids;
+ u32 max_number_epno_networks;
+ u32 max_number_epno_networks_by_ssid;
+ u32 max_number_of_white_listed_ssid;
+ u32 max_number_of_black_listed_ssid;
};
/**
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h b/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h
index b88ecc7..d1a5dd1 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h
@@ -115,6 +115,7 @@
* entering D0i3 (in msecs)
* @lar_disable: disable LAR (regulatory), default = 0
* @fw_monitor: allow to use firmware monitor
+ * @disable_11ac: disable VHT capabilities, default = false.
*/
struct iwl_mod_params {
int sw_crypto;
@@ -135,6 +136,7 @@
unsigned int d0i3_entry_delay;
bool lar_disable;
bool fw_monitor;
+ bool disable_11ac;
};
#endif /* #__iwl_modparams_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
index 50f4cc6..93a6895 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
@@ -7,6 +7,7 @@
*
* Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -69,6 +70,9 @@
#include "iwl-drv.h"
#include "iwl-modparams.h"
#include "iwl-nvm-parse.h"
+#include "iwl-prph.h"
+#include "iwl-io.h"
+#include "iwl-csr.h"
/* NVM offsets (in words) definitions */
enum wkp_nvm_offsets {
@@ -366,6 +370,9 @@
max_ampdu_exponent <<
IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
+ if (cfg->vht_mu_mimo_supported)
+ vht_cap->cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
+
if (cfg->ht_params->ldpc)
vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC;
@@ -449,7 +456,7 @@
IEEE80211_BAND_5GHZ);
iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ,
tx_chains, rx_chains);
- if (data->sku_cap_11ac_enable)
+ if (data->sku_cap_11ac_enable && !iwlwifi_mod_params.disable_11ac)
iwl_init_vht_hw_capab(cfg, data, &sband->vht_cap,
tx_chains, rx_chains);
@@ -519,27 +526,41 @@
data->valid_rx_ant = NVM_RF_CFG_RX_ANT_MSK_FAMILY_8000(radio_cfg);
}
-static void iwl_set_hw_address(const struct iwl_cfg *cfg,
- struct iwl_nvm_data *data,
- const __le16 *nvm_sec)
+static void iwl_flip_hw_address(__le32 mac_addr0, __le32 mac_addr1, u8 *dest)
{
- const u8 *hw_addr = (const u8 *)(nvm_sec + HW_ADDR);
+ const u8 *hw_addr;
- /* The byte order is little endian 16 bit, meaning 214365 */
- data->hw_addr[0] = hw_addr[1];
- data->hw_addr[1] = hw_addr[0];
- data->hw_addr[2] = hw_addr[3];
- data->hw_addr[3] = hw_addr[2];
- data->hw_addr[4] = hw_addr[5];
- data->hw_addr[5] = hw_addr[4];
+ hw_addr = (const u8 *)&mac_addr0;
+ dest[0] = hw_addr[3];
+ dest[1] = hw_addr[2];
+ dest[2] = hw_addr[1];
+ dest[3] = hw_addr[0];
+
+ hw_addr = (const u8 *)&mac_addr1;
+ dest[4] = hw_addr[1];
+ dest[5] = hw_addr[0];
}
-static void iwl_set_hw_address_family_8000(struct device *dev,
+static void iwl_set_hw_address_from_csr(struct iwl_trans *trans,
+ struct iwl_nvm_data *data)
+{
+ __le32 mac_addr0 = cpu_to_le32(iwl_read32(trans, CSR_MAC_ADDR0_STRAP));
+ __le32 mac_addr1 = cpu_to_le32(iwl_read32(trans, CSR_MAC_ADDR1_STRAP));
+
+ /* If OEM did not fuse address - get it from OTP */
+ if (!mac_addr0 && !mac_addr1) {
+ mac_addr0 = cpu_to_le32(iwl_read32(trans, CSR_MAC_ADDR0_OTP));
+ mac_addr1 = cpu_to_le32(iwl_read32(trans, CSR_MAC_ADDR1_OTP));
+ }
+
+ iwl_flip_hw_address(mac_addr0, mac_addr1, data->hw_addr);
+}
+
+static void iwl_set_hw_address_family_8000(struct iwl_trans *trans,
const struct iwl_cfg *cfg,
struct iwl_nvm_data *data,
const __le16 *mac_override,
- const __le16 *nvm_hw,
- __le32 mac_addr0, __le32 mac_addr1)
+ const __le16 *nvm_hw)
{
const u8 *hw_addr;
@@ -565,45 +586,68 @@
memcmp(reserved_mac, hw_addr, ETH_ALEN) != 0)
return;
- IWL_ERR_DEV(dev,
- "mac address from nvm override section is not valid\n");
+ IWL_ERR(trans,
+ "mac address from nvm override section is not valid\n");
}
if (nvm_hw) {
- /* read the MAC address from HW resisters */
- hw_addr = (const u8 *)&mac_addr0;
- data->hw_addr[0] = hw_addr[3];
- data->hw_addr[1] = hw_addr[2];
- data->hw_addr[2] = hw_addr[1];
- data->hw_addr[3] = hw_addr[0];
+ /* read the mac address from WFMP registers */
+ __le32 mac_addr0 = cpu_to_le32(iwl_trans_read_prph(trans,
+ WFMP_MAC_ADDR_0));
+ __le32 mac_addr1 = cpu_to_le32(iwl_trans_read_prph(trans,
+ WFMP_MAC_ADDR_1));
- hw_addr = (const u8 *)&mac_addr1;
- data->hw_addr[4] = hw_addr[1];
- data->hw_addr[5] = hw_addr[0];
-
- if (!is_valid_ether_addr(data->hw_addr))
- IWL_ERR_DEV(dev,
- "mac address (%pM) from hw section is not valid\n",
- data->hw_addr);
+ iwl_flip_hw_address(mac_addr0, mac_addr1, data->hw_addr);
return;
}
- IWL_ERR_DEV(dev, "mac address is not found\n");
+ IWL_ERR(trans, "mac address is not found\n");
+}
+
+static int iwl_set_hw_address(struct iwl_trans *trans,
+ const struct iwl_cfg *cfg,
+ struct iwl_nvm_data *data, const __le16 *nvm_hw,
+ const __le16 *mac_override)
+{
+ if (cfg->mac_addr_from_csr) {
+ iwl_set_hw_address_from_csr(trans, data);
+ } else if (cfg->device_family != IWL_DEVICE_FAMILY_8000) {
+ const u8 *hw_addr = (const u8 *)(nvm_hw + HW_ADDR);
+
+ /* The byte order is little endian 16 bit, meaning 214365 */
+ data->hw_addr[0] = hw_addr[1];
+ data->hw_addr[1] = hw_addr[0];
+ data->hw_addr[2] = hw_addr[3];
+ data->hw_addr[3] = hw_addr[2];
+ data->hw_addr[4] = hw_addr[5];
+ data->hw_addr[5] = hw_addr[4];
+ } else {
+ iwl_set_hw_address_family_8000(trans, cfg, data,
+ mac_override, nvm_hw);
+ }
+
+ if (!is_valid_ether_addr(data->hw_addr)) {
+ IWL_ERR(trans, "no valid mac address was found\n");
+ return -EINVAL;
+ }
+
+ return 0;
}
struct iwl_nvm_data *
-iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
+iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg,
const __le16 *nvm_hw, const __le16 *nvm_sw,
const __le16 *nvm_calib, const __le16 *regulatory,
const __le16 *mac_override, const __le16 *phy_sku,
- u8 tx_chains, u8 rx_chains, bool lar_fw_supported,
- __le32 mac_addr0, __le32 mac_addr1)
+ u8 tx_chains, u8 rx_chains, bool lar_fw_supported)
{
+ struct device *dev = trans->dev;
struct iwl_nvm_data *data;
- u32 sku;
- u32 radio_cfg;
+ bool lar_enabled;
+ u32 sku, radio_cfg;
u16 lar_config;
+ const __le16 *ch_section;
if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
data = kzalloc(sizeof(*data) +
@@ -642,21 +686,16 @@
if (cfg->device_family != IWL_DEVICE_FAMILY_8000) {
/* Checking for required sections */
if (!nvm_calib) {
- IWL_ERR_DEV(dev,
- "Can't parse empty Calib NVM sections\n");
+ IWL_ERR(trans,
+ "Can't parse empty Calib NVM sections\n");
kfree(data);
return NULL;
}
/* in family 8000 Xtal calibration values moved to OTP */
data->xtal_calib[0] = *(nvm_calib + XTAL_CALIB);
data->xtal_calib[1] = *(nvm_calib + XTAL_CALIB + 1);
- }
-
- if (cfg->device_family != IWL_DEVICE_FAMILY_8000) {
- iwl_set_hw_address(cfg, data, nvm_hw);
-
- iwl_init_sbands(dev, cfg, data, nvm_sw,
- tx_chains, rx_chains, lar_fw_supported);
+ lar_enabled = true;
+ ch_section = nvm_sw;
} else {
u16 lar_offset = data->nvm_version < 0xE39 ?
NVM_LAR_OFFSET_FAMILY_8000_OLD :
@@ -665,16 +704,18 @@
lar_config = le16_to_cpup(regulatory + lar_offset);
data->lar_enabled = !!(lar_config &
NVM_LAR_ENABLED_FAMILY_8000);
-
- /* MAC address in family 8000 */
- iwl_set_hw_address_family_8000(dev, cfg, data, mac_override,
- nvm_hw, mac_addr0, mac_addr1);
-
- iwl_init_sbands(dev, cfg, data, regulatory,
- tx_chains, rx_chains,
- lar_fw_supported && data->lar_enabled);
+ lar_enabled = data->lar_enabled;
+ ch_section = regulatory;
}
+ /* If no valid mac address was found - bail out */
+ if (iwl_set_hw_address(trans, cfg, data, nvm_hw, mac_override)) {
+ kfree(data);
+ return NULL;
+ }
+
+ iwl_init_sbands(dev, cfg, data, ch_section, tx_chains, rx_chains,
+ lar_fw_supported && lar_enabled);
data->calib_version = 255;
return data;
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h
index 4e8e0dc..d704d52 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h
@@ -74,12 +74,11 @@
* later with iwl_free_nvm_data().
*/
struct iwl_nvm_data *
-iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
+iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg,
const __le16 *nvm_hw, const __le16 *nvm_sw,
const __le16 *nvm_calib, const __le16 *regulatory,
const __le16 *mac_override, const __le16 *phy_sku,
- u8 tx_chains, u8 rx_chains, bool lar_fw_supported,
- __le32 mac_addr0, __le32 mac_addr1);
+ u8 tx_chains, u8 rx_chains, bool lar_fw_supported);
/**
* iwl_parse_mcc_info - parse MCC (mobile country code) info coming from FW
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h
index 5bde23a..c46e596 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h
@@ -404,4 +404,6 @@
LMPM_PAGE_PASS_NOTIF_POS = BIT(20),
};
+#define UREG_CHICK (0xA05C00)
+#define UREG_CHICK_MSIX_ENABLE BIT(25)
#endif /* __iwl_prph_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
index 0ca0f13..91d74b3 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
@@ -836,6 +836,7 @@
enum iwl_plat_pm_mode system_pm_mode;
enum iwl_plat_pm_mode runtime_pm_mode;
+ bool suspending;
/* pointer to trans specific struct */
/*Ensure that this pointer will always be aligned to sizeof pointer */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
index 5214482..c1a3131 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
@@ -816,8 +816,7 @@
{
iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_REGULAR, true);
- iwl_trans_stop_device(mvm->trans);
-
+ iwl_mvm_stop_device(mvm);
/*
* Set the HW restart bit -- this is mostly true as we're
* going to load new firmware and reprogram that, though
@@ -856,8 +855,7 @@
wowlan_config_cmd->is_11n_connection =
ap_sta->ht_cap.ht_supported;
wowlan_config_cmd->flags = ENABLE_L3_FILTERING |
- ENABLE_NBNS_FILTERING | ENABLE_DHCP_FILTERING |
- ENABLE_STORE_BEACON;
+ ENABLE_NBNS_FILTERING | ENABLE_DHCP_FILTERING;
/* Query the last used seqno and set it */
ret = iwl_mvm_get_last_nonqos_seq(mvm, vif);
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c
index c529e53..a43b392 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c
@@ -64,6 +64,7 @@
*
*****************************************************************************/
#include <linux/vmalloc.h>
+#include <linux/ieee80211.h>
#include "mvm.h"
#include "fw-dbg.h"
@@ -72,6 +73,44 @@
#include "debugfs.h"
#include "iwl-fw-error-dump.h"
+static ssize_t iwl_dbgfs_ctdp_budget_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm *mvm = file->private_data;
+ char buf[16];
+ int pos, budget;
+
+ if (!mvm->ucode_loaded || mvm->cur_ucode != IWL_UCODE_REGULAR)
+ return -EIO;
+
+ mutex_lock(&mvm->mutex);
+ budget = iwl_mvm_ctdp_command(mvm, CTDP_CMD_OPERATION_REPORT, 0);
+ mutex_unlock(&mvm->mutex);
+
+ if (budget < 0)
+ return budget;
+
+ pos = scnprintf(buf, sizeof(buf), "%d\n", budget);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_stop_ctdp_write(struct iwl_mvm *mvm, char *buf,
+ size_t count, loff_t *ppos)
+{
+ int ret;
+
+ if (!mvm->ucode_loaded || mvm->cur_ucode != IWL_UCODE_REGULAR)
+ return -EIO;
+
+ mutex_lock(&mvm->mutex);
+ ret = iwl_mvm_ctdp_command(mvm, CTDP_CMD_OPERATION_STOP, 0);
+ mutex_unlock(&mvm->mutex);
+
+ return ret ?: count;
+}
+
static ssize_t iwl_dbgfs_tx_flush_write(struct iwl_mvm *mvm, char *buf,
size_t count, loff_t *ppos)
{
@@ -976,7 +1015,7 @@
memcpy(&cmd.indirection_table[i * nbytes], cmd.indirection_table,
ARRAY_SIZE(cmd.indirection_table) % nbytes);
- memcpy(cmd.secret_key, mvm->secret_key, ARRAY_SIZE(cmd.secret_key));
+ memcpy(cmd.secret_key, mvm->secret_key, sizeof(cmd.secret_key));
mutex_lock(&mvm->mutex);
ret = iwl_mvm_send_cmd_pdu(mvm, RSS_CONFIG_CMD, 0, sizeof(cmd), &cmd);
@@ -1080,6 +1119,22 @@
return count;
}
+static ssize_t iwl_dbgfs_max_amsdu_len_write(struct iwl_mvm *mvm,
+ char *buf, size_t count,
+ loff_t *ppos)
+{
+ unsigned int max_amsdu_len;
+ int ret;
+
+ ret = kstrtouint(buf, 0, &max_amsdu_len);
+
+ if (max_amsdu_len > IEEE80211_MAX_MPDU_LEN_VHT_11454)
+ return -EINVAL;
+ mvm->max_amsdu_len = max_amsdu_len;
+
+ return count;
+}
+
#define ADD_TEXT(...) pos += scnprintf(buf + pos, bufsz - pos, __VA_ARGS__)
#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
static ssize_t iwl_dbgfs_bcast_filters_read(struct file *file,
@@ -1476,6 +1531,8 @@
MVM_DEBUGFS_READ_WRITE_FILE_OPS(prph_reg, 64);
/* Device wide debugfs entries */
+MVM_DEBUGFS_READ_FILE_OPS(ctdp_budget);
+MVM_DEBUGFS_WRITE_FILE_OPS(stop_ctdp, 8);
MVM_DEBUGFS_WRITE_FILE_OPS(tx_flush, 16);
MVM_DEBUGFS_WRITE_FILE_OPS(sta_drain, 8);
MVM_DEBUGFS_WRITE_FILE_OPS(send_echo_cmd, 8);
@@ -1497,7 +1554,9 @@
MVM_DEBUGFS_READ_WRITE_FILE_OPS(fw_dbg_conf, 8);
MVM_DEBUGFS_WRITE_FILE_OPS(fw_dbg_collect, 64);
MVM_DEBUGFS_WRITE_FILE_OPS(cont_recording, 8);
-MVM_DEBUGFS_WRITE_FILE_OPS(indirection_tbl, 16);
+MVM_DEBUGFS_WRITE_FILE_OPS(max_amsdu_len, 8);
+MVM_DEBUGFS_WRITE_FILE_OPS(indirection_tbl,
+ (IWL_RSS_INDIRECTION_TABLE_SIZE * 2));
#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters, 256);
@@ -1523,6 +1582,8 @@
MVM_DEBUGFS_ADD_FILE(set_nic_temperature, mvm->debugfs_dir,
S_IWUSR | S_IRUSR);
MVM_DEBUGFS_ADD_FILE(nic_temp, dbgfs_dir, S_IRUSR);
+ MVM_DEBUGFS_ADD_FILE(ctdp_budget, dbgfs_dir, S_IRUSR);
+ MVM_DEBUGFS_ADD_FILE(stop_ctdp, dbgfs_dir, S_IWUSR);
MVM_DEBUGFS_ADD_FILE(stations, dbgfs_dir, S_IRUSR);
MVM_DEBUGFS_ADD_FILE(bt_notif, dbgfs_dir, S_IRUSR);
MVM_DEBUGFS_ADD_FILE(bt_cmd, dbgfs_dir, S_IRUSR);
@@ -1540,6 +1601,7 @@
MVM_DEBUGFS_ADD_FILE(d0i3_refs, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
MVM_DEBUGFS_ADD_FILE(fw_dbg_conf, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
MVM_DEBUGFS_ADD_FILE(fw_dbg_collect, mvm->debugfs_dir, S_IWUSR);
+ MVM_DEBUGFS_ADD_FILE(max_amsdu_len, mvm->debugfs_dir, S_IWUSR);
MVM_DEBUGFS_ADD_FILE(send_echo_cmd, mvm->debugfs_dir, S_IWUSR);
MVM_DEBUGFS_ADD_FILE(cont_recording, mvm->debugfs_dir, S_IWUSR);
MVM_DEBUGFS_ADD_FILE(indirection_tbl, mvm->debugfs_dir, S_IWUSR);
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h
index df939f5..7a16e55 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h
@@ -264,9 +264,8 @@
};
enum iwl_rx_mpdu_amsdu_info {
- IWL_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK = 0x3f,
- IWL_RX_MPDU_AMSDU_LAST_SUBFRAME = 0x40,
- /* 0x80 bit reserved for now */
+ IWL_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK = 0x7f,
+ IWL_RX_MPDU_AMSDU_LAST_SUBFRAME = 0x80,
};
enum iwl_rx_l3l4_flags {
@@ -391,4 +390,56 @@
u8 indirection_table[IWL_RSS_INDIRECTION_TABLE_SIZE];
} __packed; /* RSS_CONFIG_CMD_API_S_VER_1 */
+#define IWL_MULTI_QUEUE_SYNC_MSG_MAX_SIZE 128
+#define IWL_MULTI_QUEUE_SYNC_SENDER_POS 0
+#define IWL_MULTI_QUEUE_SYNC_SENDER_MSK 0xf
+
+/**
+ * struct iwl_rxq_sync_cmd - RXQ notification trigger
+ *
+ * @flags: flags of the notification. bit 0:3 are the sender queue
+ * @rxq_mask: rx queues to send the notification on
+ * @count: number of bytes in payload, should be DWORD aligned
+ * @payload: data to send to rx queues
+ */
+struct iwl_rxq_sync_cmd {
+ __le32 flags;
+ __le32 rxq_mask;
+ __le32 count;
+ u8 payload[];
+} __packed; /* MULTI_QUEUE_DRV_SYNC_HDR_CMD_API_S_VER_1 */
+
+/**
+ * struct iwl_rxq_sync_notification - Notification triggered by RXQ
+ * sync command
+ *
+ * @count: number of bytes in payload
+ * @payload: data to send to rx queues
+ */
+struct iwl_rxq_sync_notification {
+ __le32 count;
+ u8 payload[];
+} __packed; /* MULTI_QUEUE_DRV_SYNC_HDR_CMD_API_S_VER_1 */
+
+/**
+* Internal message identifier
+*
+* @IWL_MVM_RXQ_NOTIF_DEL_BA: notify RSS queues of delBA
+*/
+enum iwl_mvm_rxq_notif_type {
+ IWL_MVM_RXQ_NOTIF_DEL_BA,
+};
+
+/**
+* struct iwl_mvm_internal_rxq_notif - Internal representation of the data sent
+* in &iwl_rxq_sync_cmd. Should be DWORD aligned.
+*
+* @type: value from &iwl_mvm_rxq_notif_type
+* @data: payload
+*/
+struct iwl_mvm_internal_rxq_notif {
+ u32 type;
+ u8 data[];
+} __packed;
+
#endif /* __fw_api_rx_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h
index f332497..4a0fc47 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h
@@ -119,6 +119,8 @@
SCAN_ABORT_UMAC = 0xe,
SCAN_COMPLETE_UMAC = 0xf,
+ BA_WINDOW_STATUS_NOTIFICATION_ID = 0x13,
+
/* station table */
ADD_STA_KEY = 0x17,
ADD_STA = 0x18,
@@ -279,9 +281,19 @@
*/
enum iwl_phy_ops_subcmd_ids {
CMD_DTS_MEASUREMENT_TRIGGER_WIDE = 0x0,
+ CTDP_CONFIG_CMD = 0x03,
+ TEMP_REPORTING_THRESHOLDS_CMD = 0x04,
+ CT_KILL_NOTIFICATION = 0xFE,
DTS_MEASUREMENT_NOTIF_WIDE = 0xFF,
};
+enum iwl_data_path_subcmd_ids {
+ UPDATE_MU_GROUPS_CMD = 0x1,
+ TRIGGER_RX_QUEUES_NOTIF_CMD = 0x2,
+ MU_GROUP_MGMT_NOTIF = 0xFE,
+ RX_QUEUES_NOTIFICATION = 0xFF,
+};
+
enum iwl_prot_offload_subcmd_ids {
STORED_BEACON_NTF = 0xFF,
};
@@ -291,6 +303,7 @@
LEGACY_GROUP = 0x0,
LONG_GROUP = 0x1,
PHY_OPS_GROUP = 0x4,
+ DATA_PATH_GROUP = 0x5,
PROT_OFFLOAD_GROUP = 0xb,
};
@@ -1278,6 +1291,26 @@
struct iwl_fw_bcast_filter_attr attrs[MAX_BCAST_FILTER_ATTRS];
} __packed; /* BCAST_FILTER_S_VER_1 */
+#define BA_WINDOW_STREAMS_MAX 16
+#define BA_WINDOW_STATUS_TID_MSK 0x000F
+#define BA_WINDOW_STATUS_STA_ID_POS 4
+#define BA_WINDOW_STATUS_STA_ID_MSK 0x01F0
+#define BA_WINDOW_STATUS_VALID_MSK BIT(9)
+
+/**
+ * struct iwl_ba_window_status_notif - reordering window's status notification
+ * @bitmap: bitmap of received frames [start_seq_num + 0]..[start_seq_num + 63]
+ * @ra_tid: bit 3:0 - TID, bit 8:4 - STA_ID, bit 9 - valid
+ * @start_seq_num: the start sequence number of the bitmap
+ * @mpdu_rx_count: the number of received MPDUs since entering D0i3
+ */
+struct iwl_ba_window_status_notif {
+ __le64 bitmap[BA_WINDOW_STREAMS_MAX];
+ __le16 ra_tid[BA_WINDOW_STREAMS_MAX];
+ __le32 start_seq_num[BA_WINDOW_STREAMS_MAX];
+ __le16 mpdu_rx_count[BA_WINDOW_STREAMS_MAX];
+} __packed; /* BA_WINDOW_STATUS_NTFY_API_S_VER_1 */
+
/**
* struct iwl_fw_bcast_mac - per-mac broadcast filtering configuration.
* @default_discard: default action for this mac (discard (1) / pass (0)).
@@ -1675,15 +1708,77 @@
} __packed; /* XVT_FW_DTS_CONTROL_MEASUREMENT_REQUEST_API_S */
/**
- * iwl_dts_measurement_notif - notification received with the measurements
+ * struct iwl_dts_measurement_notif_v1 - measurements notification
*
* @temp: the measured temperature
* @voltage: the measured voltage
*/
-struct iwl_dts_measurement_notif {
+struct iwl_dts_measurement_notif_v1 {
__le32 temp;
__le32 voltage;
-} __packed; /* TEMPERATURE_MEASUREMENT_TRIGGER_NTFY_S */
+} __packed; /* TEMPERATURE_MEASUREMENT_TRIGGER_NTFY_S_VER_1*/
+
+/**
+ * struct iwl_dts_measurement_notif_v2 - measurements notification
+ *
+ * @temp: the measured temperature
+ * @voltage: the measured voltage
+ * @threshold_idx: the trip index that was crossed
+ */
+struct iwl_dts_measurement_notif_v2 {
+ __le32 temp;
+ __le32 voltage;
+ __le32 threshold_idx;
+} __packed; /* TEMPERATURE_MEASUREMENT_TRIGGER_NTFY_S_VER_2 */
+
+/**
+ * struct ct_kill_notif - CT-kill entry notification
+ *
+ * @temperature: the current temperature in celsius
+ * @reserved: reserved
+ */
+struct ct_kill_notif {
+ __le16 temperature;
+ __le16 reserved;
+} __packed; /* GRP_PHY_CT_KILL_NTF */
+
+/**
+* enum ctdp_cmd_operation - CTDP command operations
+* @CTDP_CMD_OPERATION_START: update the current budget
+* @CTDP_CMD_OPERATION_STOP: stop ctdp
+* @CTDP_CMD_OPERATION_REPORT: get the avgerage budget
+*/
+enum iwl_mvm_ctdp_cmd_operation {
+ CTDP_CMD_OPERATION_START = 0x1,
+ CTDP_CMD_OPERATION_STOP = 0x2,
+ CTDP_CMD_OPERATION_REPORT = 0x4,
+};/* CTDP_CMD_OPERATION_TYPE_E */
+
+/**
+ * struct iwl_mvm_ctdp_cmd - track and manage the FW power consumption budget
+ *
+ * @operation: see &enum iwl_mvm_ctdp_cmd_operation
+ * @budget: the budget in milliwatt
+ * @window_size: defined in API but not used
+ */
+struct iwl_mvm_ctdp_cmd {
+ __le32 operation;
+ __le32 budget;
+ __le32 window_size;
+} __packed;
+
+#define IWL_MAX_DTS_TRIPS 8
+
+/**
+ * struct iwl_temp_report_ths_cmd - set temperature thresholds
+ *
+ * @num_temps: number of temperature thresholds passed
+ * @thresholds: array with the thresholds to be configured
+ */
+struct temp_report_ths_cmd {
+ __le32 num_temps;
+ __le16 thresholds[IWL_MAX_DTS_TRIPS];
+} __packed; /* GRP_PHY_TEMP_REPORTING_THRESHOLDS_CMD */
/***********************************
* TDLS API
@@ -1858,6 +1953,31 @@
__le32 page_buff_size;
} __packed; /* SHARED_MEM_ALLOC_API_S_VER_1 */
+/**
+ * VHT MU-MIMO group configuration
+ *
+ * @membership_status: a bitmap of MU groups
+ * @user_position:the position of station in a group. If the station is in the
+ * group then bits (group * 2) is the position -1
+ */
+struct iwl_mu_group_mgmt_cmd {
+ __le32 reserved;
+ __le32 membership_status[2];
+ __le32 user_position[4];
+} __packed; /* MU_GROUP_ID_MNG_TABLE_API_S_VER_1 */
+
+/**
+ * struct iwl_mu_group_mgmt_notif - VHT MU-MIMO group id notification
+ *
+ * @membership_status: a bitmap of MU groups
+ * @user_position: the position of station in a group. If the station is in the
+ * group then bits (group * 2) is the position -1
+ */
+struct iwl_mu_group_mgmt_notif {
+ __le32 membership_status[2];
+ __le32 user_position[4];
+} __packed; /* MU_GROUP_MNG_NTFY_API_S_VER_1 */
+
#define MAX_STORED_BEACON_SIZE 600
/**
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
index 62ae43d..594cd0d 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
@@ -121,7 +121,7 @@
for (i = 0; i < ARRAY_SIZE(cmd.indirection_table); i++)
cmd.indirection_table[i] = i % mvm->trans->num_rx_queues;
- memcpy(cmd.secret_key, mvm->secret_key, ARRAY_SIZE(cmd.secret_key));
+ memcpy(cmd.secret_key, mvm->secret_key, sizeof(cmd.secret_key));
return iwl_mvm_send_cmd_pdu(mvm, RSS_CONFIG_CMD, 0, sizeof(cmd), &cmd);
}
@@ -539,7 +539,9 @@
struct iwl_sf_region st_fwrd_space;
if (ucode_type == IWL_UCODE_REGULAR &&
- iwl_fw_dbg_conf_usniffer(mvm->fw, FW_DBG_START_FROM_ALIVE))
+ iwl_fw_dbg_conf_usniffer(mvm->fw, FW_DBG_START_FROM_ALIVE) &&
+ !(fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_USNIFFER_UNIFIED)))
fw = iwl_get_ucode_image(mvm, IWL_UCODE_REGULAR_USNIFFER);
else
fw = iwl_get_ucode_image(mvm, ucode_type);
@@ -954,8 +956,26 @@
goto error;
}
+#ifdef CONFIG_THERMAL
+ if (iwl_mvm_is_tt_in_fw(mvm)) {
+ /* in order to give the responsibility of ct-kill and
+ * TX backoff to FW we need to send empty temperature reporting
+ * cmd during init time
+ */
+ iwl_mvm_send_temp_report_ths_cmd(mvm);
+ } else {
+ /* Initialize tx backoffs to the minimal possible */
+ iwl_mvm_tt_tx_backoff(mvm, 0);
+ }
+
+ /* TODO: read the budget from BIOS / Platform NVM */
+ if (iwl_mvm_is_ctdp_supported(mvm) && mvm->cooling_dev.cur_state > 0)
+ ret = iwl_mvm_ctdp_command(mvm, CTDP_CMD_OPERATION_START,
+ mvm->cooling_dev.cur_state);
+#else
/* Initialize tx backoffs to the minimal possible */
iwl_mvm_tt_tx_backoff(mvm, 0);
+#endif
WARN_ON(iwl_mvm_config_ltr(mvm));
@@ -991,7 +1011,7 @@
IWL_DEBUG_INFO(mvm, "RT uCode started.\n");
return 0;
error:
- iwl_trans_stop_device(mvm->trans);
+ iwl_mvm_stop_device(mvm);
return ret;
}
@@ -1035,7 +1055,7 @@
return 0;
error:
- iwl_trans_stop_device(mvm->trans);
+ iwl_mvm_stop_device(mvm);
return ret;
}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c
index 535134d..e885db3 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c
@@ -1484,6 +1484,8 @@
/* update rx_status according to the notification's metadata */
memset(&rx_status, 0, sizeof(rx_status));
rx_status.mactime = le64_to_cpu(sb->tsf);
+ /* TSF as indicated by the firmware is at INA time */
+ rx_status.flag |= RX_FLAG_MACTIME_PLCP_START;
rx_status.device_timestamp = le32_to_cpu(sb->system_time);
rx_status.band =
(sb->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_BAND_24)) ?
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
index 5315681..76e649c 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
@@ -7,6 +7,7 @@
*
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -69,7 +70,6 @@
#include <linux/etherdevice.h>
#include <linux/ip.h>
#include <linux/if_arp.h>
-#include <linux/devcoredump.h>
#include <linux/time.h>
#include <net/mac80211.h>
#include <net/ieee80211_radiotap.h>
@@ -85,7 +85,6 @@
#include "testmode.h"
#include "iwl-fw-error-dump.h"
#include "iwl-prph.h"
-#include "iwl-csr.h"
#include "iwl-nvm-parse.h"
#include "fw-dbg.h"
@@ -847,6 +846,7 @@
u16 tid = params->tid;
u16 *ssn = ¶ms->ssn;
u8 buf_size = params->buf_size;
+ bool amsdu = params->amsdu;
IWL_DEBUG_HT(mvm, "A-MPDU action on addr %pM tid %d: action %d\n",
sta->addr, tid, action);
@@ -907,7 +907,8 @@
ret = iwl_mvm_sta_tx_agg_flush(mvm, vif, sta, tid);
break;
case IEEE80211_AMPDU_TX_OPERATIONAL:
- ret = iwl_mvm_sta_tx_agg_oper(mvm, vif, sta, tid, buf_size);
+ ret = iwl_mvm_sta_tx_agg_oper(mvm, vif, sta, tid,
+ buf_size, amsdu);
break;
default:
WARN_ON_ONCE(1);
@@ -969,7 +970,7 @@
*/
iwl_mvm_unref_all_except(mvm, IWL_MVM_REF_UCODE_DOWN);
- iwl_trans_stop_device(mvm->trans);
+ iwl_mvm_stop_device(mvm);
mvm->scan_status = 0;
mvm->ps_disabled = false;
@@ -1138,7 +1139,7 @@
*/
flush_work(&mvm->roc_done_wk);
- iwl_trans_stop_device(mvm->trans);
+ iwl_mvm_stop_device(mvm);
iwl_mvm_async_handlers_purge(mvm);
/* async_handlers_list is empty and will stay empty: HW is stopped */
@@ -1169,8 +1170,6 @@
mvm->scan_uid_status[i] = 0;
}
}
-
- mvm->ucode_loaded = false;
}
static void iwl_mvm_mac_stop(struct ieee80211_hw *hw)
@@ -1762,6 +1761,50 @@
}
#endif
+static int iwl_mvm_update_mu_groups(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mu_group_mgmt_cmd cmd = {};
+
+ memcpy(cmd.membership_status, vif->bss_conf.mu_group.membership,
+ WLAN_MEMBERSHIP_LEN);
+ memcpy(cmd.user_position, vif->bss_conf.mu_group.position,
+ WLAN_USER_POSITION_LEN);
+
+ return iwl_mvm_send_cmd_pdu(mvm,
+ WIDE_ID(DATA_PATH_GROUP,
+ UPDATE_MU_GROUPS_CMD),
+ 0, sizeof(cmd), &cmd);
+}
+
+static void iwl_mvm_mu_mimo_iface_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ if (vif->mu_mimo_owner) {
+ struct iwl_mu_group_mgmt_notif *notif = _data;
+
+ /*
+ * MU-MIMO Group Id action frame is little endian. We treat
+ * the data received from firmware as if it came from the
+ * action frame, so no conversion is needed.
+ */
+ ieee80211_update_mu_groups(vif,
+ (u8 *)¬if->membership_status,
+ (u8 *)¬if->user_position);
+ }
+}
+
+void iwl_mvm_mu_mimo_grp_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_mu_group_mgmt_notif *notif = (void *)pkt->data;
+
+ ieee80211_iterate_active_interfaces_atomic(
+ mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_mu_mimo_iface_iterator, notif);
+}
+
static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *bss_conf,
@@ -1870,6 +1913,18 @@
vif->addr);
}
+ /*
+ * The firmware tracks the MU-MIMO group on its own.
+ * However, on HW restart we should restore this data.
+ */
+ if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&
+ (changes & BSS_CHANGED_MU_GROUPS) && vif->mu_mimo_owner) {
+ ret = iwl_mvm_update_mu_groups(mvm, vif);
+ if (ret)
+ IWL_ERR(mvm,
+ "failed to update VHT MU_MIMO groups\n");
+ }
+
iwl_mvm_recalc_multicast(mvm);
iwl_mvm_configure_bcast_filter(mvm);
@@ -1896,7 +1951,12 @@
WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0));
}
- if (changes & (BSS_CHANGED_PS | BSS_CHANGED_P2P_PS | BSS_CHANGED_QOS)) {
+ if (changes & (BSS_CHANGED_PS | BSS_CHANGED_P2P_PS | BSS_CHANGED_QOS |
+ /*
+ * Send power command on every beacon change,
+ * because we may have not enabled beacon abort yet.
+ */
+ BSS_CHANGED_BEACON_INFO)) {
ret = iwl_mvm_power_update_mac(mvm);
if (ret)
IWL_ERR(mvm, "failed to update power mode\n");
@@ -2083,7 +2143,6 @@
bss_conf->txpower);
iwl_mvm_set_tx_power(mvm, vif, bss_conf->txpower);
}
-
}
static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw,
@@ -2276,6 +2335,11 @@
if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT))
return;
+ if (vif->p2p && !iwl_mvm_is_p2p_standalone_uapsd_supported(mvm)) {
+ vif->driver_flags &= ~IEEE80211_VIF_SUPPORTS_UAPSD;
+ return;
+ }
+
if (iwlwifi_mod_params.uapsd_disable) {
vif->driver_flags &= ~IEEE80211_VIF_SUPPORTS_UAPSD;
return;
@@ -2490,10 +2554,8 @@
struct ieee80211_vif *vif)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- u32 duration = min(IWL_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS,
- 200 + vif->bss_conf.beacon_int);
- u32 min_duration = min(IWL_MVM_TE_SESSION_PROTECTION_MIN_TIME_MS,
- 100 + vif->bss_conf.beacon_int);
+ u32 duration = IWL_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS;
+ u32 min_duration = IWL_MVM_TE_SESSION_PROTECTION_MIN_TIME_MS;
if (WARN_ON_ONCE(vif->bss_conf.assoc))
return;
@@ -2624,8 +2686,12 @@
* GTK on AP interface is a TX-only key, return 0;
* on IBSS they're per-station and because we're lazy
* we don't support them for RX, so do the same.
+ * CMAC in AP/IBSS modes must be done in software.
*/
- ret = 0;
+ if (key->cipher == WLAN_CIPHER_SUITE_AES_CMAC)
+ ret = -EOPNOTSUPP;
+ else
+ ret = 0;
key->hw_key_idx = STA_KEY_IDX_INVALID;
break;
}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
index cc279e0..9abbc93 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
@@ -7,6 +7,7 @@
*
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -33,6 +34,7 @@
*
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -71,6 +73,10 @@
#include <linux/leds.h>
#include <linux/in6.h>
+#ifdef CONFIG_THERMAL
+#include <linux/thermal.h>
+#endif
+
#include "iwl-op-mode.h"
#include "iwl-trans.h"
#include "iwl-notif-wait.h"
@@ -487,6 +493,12 @@
IWL_SCAN_TYPE_FRAGMENTED,
};
+enum iwl_mvm_sched_scan_pass_all_states {
+ SCHED_SCAN_PASS_ALL_DISABLED,
+ SCHED_SCAN_PASS_ALL_ENABLED,
+ SCHED_SCAN_PASS_ALL_FOUND,
+};
+
/**
* struct iwl_nvm_section - describes an NVM section in memory.
*
@@ -517,6 +529,30 @@
bool throttle;
};
+#ifdef CONFIG_THERMAL
+/**
+ *struct iwl_mvm_thermal_device - thermal zone related data
+ * @temp_trips: temperature thresholds for report
+ * @fw_trips_index: keep indexes to original array - temp_trips
+ * @tzone: thermal zone device data
+*/
+struct iwl_mvm_thermal_device {
+ s16 temp_trips[IWL_MAX_DTS_TRIPS];
+ u8 fw_trips_index[IWL_MAX_DTS_TRIPS];
+ struct thermal_zone_device *tzone;
+};
+
+/*
+ * struct iwl_mvm_cooling_device
+ * @cur_state: current state
+ * @cdev: struct thermal cooling device
+ */
+struct iwl_mvm_cooling_device {
+ u32 cur_state;
+ struct thermal_cooling_device *cdev;
+};
+#endif
+
#define IWL_MVM_NUM_LAST_FRAMES_UCODE_RATES 8
struct iwl_mvm_frame_stats {
@@ -657,6 +693,7 @@
void *scan_cmd;
struct iwl_mcast_filter_cmd *mcast_filter_cmd;
enum iwl_mvm_scan_type scan_type;
+ enum iwl_mvm_sched_scan_pass_all_states sched_scan_pass_all;
/* max number of simultaneous scans the FW supports */
unsigned int max_scans;
@@ -797,6 +834,11 @@
/* Thermal Throttling and CTkill */
struct iwl_mvm_tt_mgmt thermal_throttle;
+#ifdef CONFIG_THERMAL
+ struct iwl_mvm_thermal_device tz_device;
+ struct iwl_mvm_cooling_device cooling_dev;
+#endif
+
s32 temperature; /* Celsius */
/*
* Debug option to set the NIC temperature. This option makes the
@@ -819,6 +861,7 @@
/* Indicate if device power save is allowed */
u8 ps_disabled; /* u8 instead of bool to ease debugfs_create_* usage */
+ unsigned int max_amsdu_len; /* used for debugfs only */
struct ieee80211_vif __rcu *csa_vif;
struct ieee80211_vif __rcu *csa_tx_blocked_vif;
@@ -943,8 +986,9 @@
static inline bool iwl_mvm_is_dqa_supported(struct iwl_mvm *mvm)
{
- return fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_DQA_SUPPORT);
+ /* Make sure DQA isn't allowed in driver until feature is complete */
+ return false && fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_DQA_SUPPORT);
}
static inline bool iwl_mvm_enter_d0i3_on_suspend(struct iwl_mvm *mvm)
@@ -1028,6 +1072,28 @@
IWL_UCODE_TLV_CAPA_MULTI_QUEUE_RX_SUPPORT);
}
+static inline bool iwl_mvm_is_tt_in_fw(struct iwl_mvm *mvm)
+{
+#ifdef CONFIG_THERMAL
+ /* these two TLV are redundant since the responsibility to CT-kill by
+ * FW happens only after we send at least one command of
+ * temperature THs report.
+ */
+ return fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_CT_KILL_BY_FW) &&
+ fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_TEMP_THS_REPORT_SUPPORT);
+#else /* CONFIG_THERMAL */
+ return false;
+#endif /* CONFIG_THERMAL */
+}
+
+static inline bool iwl_mvm_is_ctdp_supported(struct iwl_mvm *mvm)
+{
+ return fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_CTDP_SUPPORT);
+}
+
extern const u8 iwl_mvm_ac_to_tx_fifo[];
struct iwl_rate_info {
@@ -1160,6 +1226,10 @@
struct iwl_rx_cmd_buffer *rxb, int queue);
void iwl_mvm_rx_frame_release(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb, int queue);
+int iwl_mvm_notify_rx_queue(struct iwl_mvm *mvm, u32 rxq_mask,
+ const u8 *data, u32 count);
+void iwl_mvm_rx_queue_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+ int queue);
void iwl_mvm_rx_tx_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb);
void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb);
void iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm,
@@ -1203,6 +1273,10 @@
struct iwl_rx_cmd_buffer *rxb);
void iwl_mvm_rx_stored_beacon_notif(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb);
+void iwl_mvm_mu_mimo_grp_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb);
+void iwl_mvm_window_status_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb);
void iwl_mvm_mac_ctxt_recalc_tsf_id(struct iwl_mvm *mvm,
struct ieee80211_vif *vif);
unsigned long iwl_mvm_get_used_hw_queues(struct iwl_mvm *mvm,
@@ -1479,32 +1553,29 @@
iwl_mvm_enable_txq(mvm, queue, mac80211_queue, ssn, &cfg, wdg_timeout);
}
-static inline void iwl_mvm_enable_agg_txq(struct iwl_mvm *mvm, int queue,
- int mac80211_queue, int fifo,
- int sta_id, int tid, int frame_limit,
- u16 ssn, unsigned int wdg_timeout)
+static inline void iwl_mvm_stop_device(struct iwl_mvm *mvm)
{
- struct iwl_trans_txq_scd_cfg cfg = {
- .fifo = fifo,
- .sta_id = sta_id,
- .tid = tid,
- .frame_limit = frame_limit,
- .aggregate = true,
- };
-
- iwl_mvm_enable_txq(mvm, queue, mac80211_queue, ssn, &cfg, wdg_timeout);
+ mvm->ucode_loaded = false;
+ iwl_trans_stop_device(mvm->trans);
}
+/* Stop/start all mac queues in a given bitmap */
+void iwl_mvm_start_mac_queues(struct iwl_mvm *mvm, unsigned long mq);
+void iwl_mvm_stop_mac_queues(struct iwl_mvm *mvm, unsigned long mq);
+
/* Thermal management and CT-kill */
void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff);
void iwl_mvm_tt_temp_changed(struct iwl_mvm *mvm, u32 temp);
void iwl_mvm_temp_notif(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb);
void iwl_mvm_tt_handler(struct iwl_mvm *mvm);
-void iwl_mvm_tt_initialize(struct iwl_mvm *mvm, u32 min_backoff);
-void iwl_mvm_tt_exit(struct iwl_mvm *mvm);
+void iwl_mvm_thermal_initialize(struct iwl_mvm *mvm, u32 min_backoff);
+void iwl_mvm_thermal_exit(struct iwl_mvm *mvm);
void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state);
int iwl_mvm_get_temp(struct iwl_mvm *mvm, s32 *temp);
+void iwl_mvm_ct_kill_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb);
+int iwl_mvm_send_temp_report_ths_cmd(struct iwl_mvm *mvm);
+int iwl_mvm_ctdp_command(struct iwl_mvm *mvm, u32 op, u32 budget);
/* Location Aware Regulatory */
struct iwl_mcc_update_resp *
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c
index c446e0d..25a9840 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c
@@ -300,7 +300,6 @@
struct iwl_nvm_section *sections = mvm->nvm_sections;
const __le16 *hw, *sw, *calib, *regulatory, *mac_override, *phy_sku;
bool lar_enabled;
- __le32 mac_addr0, mac_addr1;
/* Checking for required sections */
if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) {
@@ -336,12 +335,6 @@
if (WARN_ON(!mvm->cfg))
return NULL;
- /* read the mac address from WFMP registers */
- mac_addr0 = cpu_to_le32(iwl_trans_read_prph(mvm->trans,
- WFMP_MAC_ADDR_0));
- mac_addr1 = cpu_to_le32(iwl_trans_read_prph(mvm->trans,
- WFMP_MAC_ADDR_1));
-
hw = (const __le16 *)sections[mvm->cfg->nvm_hw_section_num].data;
sw = (const __le16 *)sections[NVM_SECTION_TYPE_SW].data;
calib = (const __le16 *)sections[NVM_SECTION_TYPE_CALIBRATION].data;
@@ -354,10 +347,10 @@
fw_has_capa(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_LAR_SUPPORT);
- return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib,
+ return iwl_parse_nvm_data(mvm->trans, mvm->cfg, hw, sw, calib,
regulatory, mac_override, phy_sku,
mvm->fw->valid_tx_ant, mvm->fw->valid_rx_ant,
- lar_enabled, mac_addr0, mac_addr1);
+ lar_enabled);
}
#define MAX_NVM_FILE_LEN 16384
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
index fd8f4a8..5e8ab79 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
@@ -205,72 +205,107 @@
~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS);
}
+/**
+ * enum iwl_rx_handler_context context for Rx handler
+ * @RX_HANDLER_SYNC : this means that it will be called in the Rx path
+ * which can't acquire mvm->mutex.
+ * @RX_HANDLER_ASYNC_LOCKED : If the handler needs to hold mvm->mutex
+ * (and only in this case!), it should be set as ASYNC. In that case,
+ * it will be called from a worker with mvm->mutex held.
+ * @RX_HANDLER_ASYNC_UNLOCKED : in case the handler needs to lock the
+ * mutex itself, it will be called from a worker without mvm->mutex held.
+ */
+enum iwl_rx_handler_context {
+ RX_HANDLER_SYNC,
+ RX_HANDLER_ASYNC_LOCKED,
+ RX_HANDLER_ASYNC_UNLOCKED,
+};
+
+/**
+ * struct iwl_rx_handlers handler for FW notification
+ * @cmd_id: command id
+ * @context: see &iwl_rx_handler_context
+ * @fn: the function is called when notification is received
+ */
struct iwl_rx_handlers {
u16 cmd_id;
- bool async;
+ enum iwl_rx_handler_context context;
void (*fn)(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb);
};
-#define RX_HANDLER(_cmd_id, _fn, _async) \
- { .cmd_id = _cmd_id , .fn = _fn , .async = _async }
-#define RX_HANDLER_GRP(_grp, _cmd, _fn, _async) \
- { .cmd_id = WIDE_ID(_grp, _cmd), .fn = _fn, .async = _async }
+#define RX_HANDLER(_cmd_id, _fn, _context) \
+ { .cmd_id = _cmd_id, .fn = _fn, .context = _context }
+#define RX_HANDLER_GRP(_grp, _cmd, _fn, _context) \
+ { .cmd_id = WIDE_ID(_grp, _cmd), .fn = _fn, .context = _context }
/*
* Handlers for fw notifications
* Convention: RX_HANDLER(CMD_NAME, iwl_mvm_rx_CMD_NAME
* This list should be in order of frequency for performance purposes.
*
- * The handler can be SYNC - this means that it will be called in the Rx path
- * which can't acquire mvm->mutex. If the handler needs to hold mvm->mutex (and
- * only in this case!), it should be set as ASYNC. In that case, it will be
- * called from a worker with mvm->mutex held.
+ * The handler can be one from three contexts, see &iwl_rx_handler_context
*/
static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
- RX_HANDLER(TX_CMD, iwl_mvm_rx_tx_cmd, false),
- RX_HANDLER(BA_NOTIF, iwl_mvm_rx_ba_notif, false),
+ RX_HANDLER(TX_CMD, iwl_mvm_rx_tx_cmd, RX_HANDLER_SYNC),
+ RX_HANDLER(BA_NOTIF, iwl_mvm_rx_ba_notif, RX_HANDLER_SYNC),
- RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif, true),
- RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, true),
- RX_HANDLER(STATISTICS_NOTIFICATION, iwl_mvm_rx_statistics, true),
+ RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif,
+ RX_HANDLER_ASYNC_LOCKED),
+ RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif,
+ RX_HANDLER_ASYNC_LOCKED),
+ RX_HANDLER(STATISTICS_NOTIFICATION, iwl_mvm_rx_statistics,
+ RX_HANDLER_ASYNC_LOCKED),
RX_HANDLER(ANTENNA_COUPLING_NOTIFICATION,
- iwl_mvm_rx_ant_coupling_notif, true),
+ iwl_mvm_rx_ant_coupling_notif, RX_HANDLER_ASYNC_LOCKED),
- RX_HANDLER(TIME_EVENT_NOTIFICATION, iwl_mvm_rx_time_event_notif, false),
- RX_HANDLER(MCC_CHUB_UPDATE_CMD, iwl_mvm_rx_chub_update_mcc, true),
+ RX_HANDLER(BA_WINDOW_STATUS_NOTIFICATION_ID,
+ iwl_mvm_window_status_notif, RX_HANDLER_SYNC),
- RX_HANDLER(EOSP_NOTIFICATION, iwl_mvm_rx_eosp_notif, false),
+ RX_HANDLER(TIME_EVENT_NOTIFICATION, iwl_mvm_rx_time_event_notif,
+ RX_HANDLER_SYNC),
+ RX_HANDLER(MCC_CHUB_UPDATE_CMD, iwl_mvm_rx_chub_update_mcc,
+ RX_HANDLER_ASYNC_LOCKED),
+
+ RX_HANDLER(EOSP_NOTIFICATION, iwl_mvm_rx_eosp_notif, RX_HANDLER_SYNC),
RX_HANDLER(SCAN_ITERATION_COMPLETE,
- iwl_mvm_rx_lmac_scan_iter_complete_notif, false),
+ iwl_mvm_rx_lmac_scan_iter_complete_notif, RX_HANDLER_SYNC),
RX_HANDLER(SCAN_OFFLOAD_COMPLETE,
- iwl_mvm_rx_lmac_scan_complete_notif, true),
+ iwl_mvm_rx_lmac_scan_complete_notif,
+ RX_HANDLER_ASYNC_LOCKED),
RX_HANDLER(MATCH_FOUND_NOTIFICATION, iwl_mvm_rx_scan_match_found,
- false),
+ RX_HANDLER_SYNC),
RX_HANDLER(SCAN_COMPLETE_UMAC, iwl_mvm_rx_umac_scan_complete_notif,
- true),
+ RX_HANDLER_ASYNC_LOCKED),
RX_HANDLER(SCAN_ITERATION_COMPLETE_UMAC,
- iwl_mvm_rx_umac_scan_iter_complete_notif, false),
+ iwl_mvm_rx_umac_scan_iter_complete_notif, RX_HANDLER_SYNC),
- RX_HANDLER(CARD_STATE_NOTIFICATION, iwl_mvm_rx_card_state_notif, false),
+ RX_HANDLER(CARD_STATE_NOTIFICATION, iwl_mvm_rx_card_state_notif,
+ RX_HANDLER_SYNC),
RX_HANDLER(MISSED_BEACONS_NOTIFICATION, iwl_mvm_rx_missed_beacons_notif,
- false),
+ RX_HANDLER_SYNC),
- RX_HANDLER(REPLY_ERROR, iwl_mvm_rx_fw_error, false),
+ RX_HANDLER(REPLY_ERROR, iwl_mvm_rx_fw_error, RX_HANDLER_SYNC),
RX_HANDLER(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION,
- iwl_mvm_power_uapsd_misbehaving_ap_notif, false),
- RX_HANDLER(DTS_MEASUREMENT_NOTIFICATION, iwl_mvm_temp_notif, true),
+ iwl_mvm_power_uapsd_misbehaving_ap_notif, RX_HANDLER_SYNC),
+ RX_HANDLER(DTS_MEASUREMENT_NOTIFICATION, iwl_mvm_temp_notif,
+ RX_HANDLER_ASYNC_LOCKED),
RX_HANDLER_GRP(PHY_OPS_GROUP, DTS_MEASUREMENT_NOTIF_WIDE,
- iwl_mvm_temp_notif, true),
+ iwl_mvm_temp_notif, RX_HANDLER_ASYNC_LOCKED),
+ RX_HANDLER_GRP(PHY_OPS_GROUP, CT_KILL_NOTIFICATION,
+ iwl_mvm_ct_kill_notif, RX_HANDLER_SYNC),
RX_HANDLER(TDLS_CHANNEL_SWITCH_NOTIFICATION, iwl_mvm_rx_tdls_notif,
- true),
- RX_HANDLER(MFUART_LOAD_NOTIFICATION, iwl_mvm_rx_mfuart_notif, false),
- RX_HANDLER(TOF_NOTIFICATION, iwl_mvm_tof_resp_handler, true),
+ RX_HANDLER_ASYNC_LOCKED),
+ RX_HANDLER(MFUART_LOAD_NOTIFICATION, iwl_mvm_rx_mfuart_notif,
+ RX_HANDLER_SYNC),
+ RX_HANDLER(TOF_NOTIFICATION, iwl_mvm_tof_resp_handler,
+ RX_HANDLER_ASYNC_LOCKED),
RX_HANDLER_GRP(PROT_OFFLOAD_GROUP, STORED_BEACON_NTF,
- iwl_mvm_rx_stored_beacon_notif, false),
-
+ iwl_mvm_rx_stored_beacon_notif, RX_HANDLER_SYNC),
+ RX_HANDLER_GRP(DATA_PATH_GROUP, MU_GROUP_MGMT_NOTIF,
+ iwl_mvm_mu_mimo_grp_notif, RX_HANDLER_SYNC),
};
#undef RX_HANDLER
#undef RX_HANDLER_GRP
@@ -292,6 +327,7 @@
HCMD_NAME(SCAN_COMPLETE_UMAC),
HCMD_NAME(TOF_CMD),
HCMD_NAME(TOF_NOTIFICATION),
+ HCMD_NAME(BA_WINDOW_STATUS_NOTIFICATION_ID),
HCMD_NAME(ADD_STA_KEY),
HCMD_NAME(ADD_STA),
HCMD_NAME(REMOVE_STA),
@@ -387,12 +423,25 @@
*/
static const struct iwl_hcmd_names iwl_mvm_phy_names[] = {
HCMD_NAME(CMD_DTS_MEASUREMENT_TRIGGER_WIDE),
+ HCMD_NAME(CTDP_CONFIG_CMD),
+ HCMD_NAME(TEMP_REPORTING_THRESHOLDS_CMD),
+ HCMD_NAME(CT_KILL_NOTIFICATION),
HCMD_NAME(DTS_MEASUREMENT_NOTIF_WIDE),
};
/* Please keep this array *SORTED* by hex value.
* Access is done through binary search
*/
+static const struct iwl_hcmd_names iwl_mvm_data_path_names[] = {
+ HCMD_NAME(UPDATE_MU_GROUPS_CMD),
+ HCMD_NAME(TRIGGER_RX_QUEUES_NOTIF_CMD),
+ HCMD_NAME(MU_GROUP_MGMT_NOTIF),
+ HCMD_NAME(RX_QUEUES_NOTIFICATION),
+};
+
+/* Please keep this array *SORTED* by hex value.
+ * Access is done through binary search
+ */
static const struct iwl_hcmd_names iwl_mvm_prot_offload_names[] = {
HCMD_NAME(STORED_BEACON_NTF),
};
@@ -401,6 +450,7 @@
[LEGACY_GROUP] = HCMD_ARR(iwl_mvm_legacy_names),
[LONG_GROUP] = HCMD_ARR(iwl_mvm_legacy_names),
[PHY_OPS_GROUP] = HCMD_ARR(iwl_mvm_phy_names),
+ [DATA_PATH_GROUP] = HCMD_ARR(iwl_mvm_data_path_names),
[PROT_OFFLOAD_GROUP] = HCMD_ARR(iwl_mvm_prot_offload_names),
};
@@ -474,8 +524,11 @@
if (iwl_mvm_has_new_rx_api(mvm)) {
op_mode->ops = &iwl_mvm_ops_mq;
+ trans->rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_desc);
} else {
op_mode->ops = &iwl_mvm_ops;
+ trans->rx_mpdu_cmd_hdr_size =
+ sizeof(struct iwl_rx_mpdu_res_start);
if (WARN_ON(trans->num_rx_queues > 1))
goto out_free;
@@ -567,7 +620,6 @@
iwl_trans_configure(mvm->trans, &trans_cfg);
trans->rx_mpdu_cmd = REPLY_RX_MPDU_CMD;
- trans->rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_res_start);
trans->dbg_dest_tlv = mvm->fw->dbg_dest_tlv;
trans->dbg_dest_reg_num = mvm->fw->dbg_dest_reg_num;
memcpy(trans->dbg_conf_tlv, mvm->fw->dbg_conf_tlv,
@@ -587,9 +639,6 @@
IWL_INFO(mvm, "Detected %s, REV=0x%X\n",
mvm->cfg->name, mvm->trans->hw_rev);
- min_backoff = calc_min_backoff(trans, cfg);
- iwl_mvm_tt_initialize(mvm, min_backoff);
-
if (iwlwifi_mod_params.nvm_file)
mvm->nvm_file_name = iwlwifi_mod_params.nvm_file;
else
@@ -619,7 +668,7 @@
iwl_mvm_ref(mvm, IWL_MVM_REF_INIT_UCODE);
err = iwl_run_init_mvm_ucode(mvm, true);
if (!err || !iwlmvm_mod_params.init_dbg)
- iwl_trans_stop_device(trans);
+ iwl_mvm_stop_device(mvm);
iwl_mvm_unref(mvm, IWL_MVM_REF_INIT_UCODE);
mutex_unlock(&mvm->mutex);
/* returns 0 if successful, 1 if success but in rfkill */
@@ -642,25 +691,31 @@
if (err)
goto out_free;
+ min_backoff = calc_min_backoff(trans, cfg);
+ iwl_mvm_thermal_initialize(mvm, min_backoff);
+
err = iwl_mvm_dbgfs_register(mvm, dbgfs_dir);
if (err)
goto out_unregister;
memset(&mvm->rx_stats, 0, sizeof(struct mvm_statistics_rx));
- /* rpm starts with a taken reference, we can release it now */
- iwl_trans_unref(mvm->trans);
+ /* The transport always starts with a taken reference, we can
+ * release it now if d0i3 is supported */
+ if (iwl_mvm_is_d0i3_supported(mvm))
+ iwl_trans_unref(mvm->trans);
iwl_mvm_tof_init(mvm);
/* init RSS hash key */
- get_random_bytes(mvm->secret_key, ARRAY_SIZE(mvm->secret_key));
+ get_random_bytes(mvm->secret_key, sizeof(mvm->secret_key));
return op_mode;
out_unregister:
ieee80211_unregister_hw(mvm->hw);
iwl_mvm_leds_exit(mvm);
+ iwl_mvm_thermal_exit(mvm);
out_free:
flush_delayed_work(&mvm->fw_dump_wk);
iwl_phy_db_free(mvm->phy_db);
@@ -676,9 +731,16 @@
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
int i;
+ /* If d0i3 is supported, we have released the reference that
+ * the transport started with, so we should take it back now
+ * that we are leaving.
+ */
+ if (iwl_mvm_is_d0i3_supported(mvm))
+ iwl_trans_ref(mvm->trans);
+
iwl_mvm_leds_exit(mvm);
- iwl_mvm_tt_exit(mvm);
+ iwl_mvm_thermal_exit(mvm);
ieee80211_unregister_hw(mvm->hw);
@@ -709,6 +771,7 @@
struct iwl_async_handler_entry {
struct list_head list;
struct iwl_rx_cmd_buffer rxb;
+ enum iwl_rx_handler_context context;
void (*fn)(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb);
};
@@ -735,7 +798,6 @@
INIT_LIST_HEAD(&local_list);
/* Ensure that we are not in stop flow (check iwl_mvm_mac_stop) */
- mutex_lock(&mvm->mutex);
/*
* Sync with Rx path with a lock. Remove all the entries from this list,
@@ -746,12 +808,15 @@
spin_unlock_bh(&mvm->async_handlers_lock);
list_for_each_entry_safe(entry, tmp, &local_list, list) {
+ if (entry->context == RX_HANDLER_ASYNC_LOCKED)
+ mutex_lock(&mvm->mutex);
entry->fn(mvm, &entry->rxb);
iwl_free_rxb(&entry->rxb);
list_del(&entry->list);
+ if (entry->context == RX_HANDLER_ASYNC_LOCKED)
+ mutex_unlock(&mvm->mutex);
kfree(entry);
}
- mutex_unlock(&mvm->mutex);
}
static inline void iwl_mvm_rx_check_trigger(struct iwl_mvm *mvm,
@@ -808,7 +873,7 @@
if (rx_h->cmd_id != WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd))
continue;
- if (!rx_h->async) {
+ if (rx_h->context == RX_HANDLER_SYNC) {
rx_h->fn(mvm, rxb);
return;
}
@@ -822,6 +887,7 @@
entry->rxb._offset = rxb->_offset;
entry->rxb._rx_page_order = rxb->_rx_page_order;
entry->fn = rx_h->fn;
+ entry->context = rx_h->context;
spin_lock(&mvm->async_handlers_lock);
list_add_tail(&entry->list, &mvm->async_handlers_list);
spin_unlock(&mvm->async_handlers_lock);
@@ -858,28 +924,24 @@
iwl_mvm_rx_mpdu_mq(mvm, napi, rxb, 0);
else if (pkt->hdr.cmd == REPLY_RX_PHY_CMD)
iwl_mvm_rx_phy_cmd_mq(mvm, rxb);
+ else if (unlikely(pkt->hdr.group_id == DATA_PATH_GROUP &&
+ pkt->hdr.cmd == RX_QUEUES_NOTIFICATION))
+ iwl_mvm_rx_queue_notif(mvm, rxb, 0);
else
iwl_mvm_rx_common(mvm, rxb, pkt);
}
-static void iwl_mvm_stop_sw_queue(struct iwl_op_mode *op_mode, int queue)
+void iwl_mvm_stop_mac_queues(struct iwl_mvm *mvm, unsigned long mq)
{
- struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
- unsigned long mq;
int q;
- spin_lock_bh(&mvm->queue_info_lock);
- mq = mvm->queue_info[queue].hw_queue_to_mac80211;
- spin_unlock_bh(&mvm->queue_info_lock);
-
if (WARN_ON_ONCE(!mq))
return;
for_each_set_bit(q, &mq, IEEE80211_MAX_QUEUES) {
if (atomic_inc_return(&mvm->mac80211_queue_stop_count[q]) > 1) {
IWL_DEBUG_TX_QUEUES(mvm,
- "queue %d (mac80211 %d) already stopped\n",
- queue, q);
+ "mac80211 %d already stopped\n", q);
continue;
}
@@ -899,24 +961,29 @@
iwl_trans_block_txq_ptrs(mvm->trans, false);
}
-static void iwl_mvm_wake_sw_queue(struct iwl_op_mode *op_mode, int queue)
+static void iwl_mvm_stop_sw_queue(struct iwl_op_mode *op_mode, int hw_queue)
{
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
unsigned long mq;
- int q;
spin_lock_bh(&mvm->queue_info_lock);
- mq = mvm->queue_info[queue].hw_queue_to_mac80211;
+ mq = mvm->queue_info[hw_queue].hw_queue_to_mac80211;
spin_unlock_bh(&mvm->queue_info_lock);
+ iwl_mvm_stop_mac_queues(mvm, mq);
+}
+
+void iwl_mvm_start_mac_queues(struct iwl_mvm *mvm, unsigned long mq)
+{
+ int q;
+
if (WARN_ON_ONCE(!mq))
return;
for_each_set_bit(q, &mq, IEEE80211_MAX_QUEUES) {
if (atomic_dec_return(&mvm->mac80211_queue_stop_count[q]) > 0) {
IWL_DEBUG_TX_QUEUES(mvm,
- "queue %d (mac80211 %d) still stopped\n",
- queue, q);
+ "mac80211 %d still stopped\n", q);
continue;
}
@@ -924,6 +991,18 @@
}
}
+static void iwl_mvm_wake_sw_queue(struct iwl_op_mode *op_mode, int hw_queue)
+{
+ struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+ unsigned long mq;
+
+ spin_lock_bh(&mvm->queue_info_lock);
+ mq = mvm->queue_info[hw_queue].hw_queue_to_mac80211;
+ spin_unlock_bh(&mvm->queue_info_lock);
+
+ iwl_mvm_start_mac_queues(mvm, mq);
+}
+
void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state)
{
if (state)
@@ -1530,6 +1609,9 @@
if (unlikely(pkt->hdr.cmd == FRAME_RELEASE))
iwl_mvm_rx_frame_release(mvm, rxb, queue);
+ else if (unlikely(pkt->hdr.cmd == RX_QUEUES_NOTIFICATION &&
+ pkt->hdr.group_id == DATA_PATH_GROUP))
+ iwl_mvm_rx_queue_notif(mvm, rxb, queue);
else
iwl_mvm_rx_mpdu_mq(mvm, napi, rxb, queue);
}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c
index 6e7e78a..61d0a8cd 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c
@@ -556,6 +556,7 @@
if (is_type_legacy(rate->type) && (rate->index <= IWL_RATE_54M_INDEX))
rate_str = legacy_rates[rate->index];
else if ((is_type_ht(rate->type) || is_type_vht(rate->type)) &&
+ (rate->index >= IWL_RATE_MCS_0_INDEX) &&
(rate->index <= IWL_RATE_MCS_9_INDEX))
rate_str = ht_vht_rates[rate->index];
else
@@ -1672,6 +1673,20 @@
}
}
+static void rs_set_amsdu_len(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
+ struct iwl_scale_tbl_info *tbl,
+ enum rs_action scale_action)
+{
+ struct iwl_mvm_sta *sta_priv = iwl_mvm_sta_from_mac80211(sta);
+
+ if ((!is_vht(&tbl->rate) && !is_ht(&tbl->rate)) ||
+ tbl->rate.index < IWL_RATE_MCS_5_INDEX ||
+ scale_action == RS_ACTION_DOWNSCALE)
+ sta_priv->tlc_amsdu = false;
+ else
+ sta_priv->tlc_amsdu = true;
+}
+
/*
* setup rate table in uCode
*/
@@ -2415,6 +2430,7 @@
tbl->rate.index = index;
if (IWL_MVM_RS_80_20_FAR_RANGE_TWEAK)
rs_tweak_rate_tbl(mvm, sta, lq_sta, tbl, scale_action);
+ rs_set_amsdu_len(mvm, sta, tbl, scale_action);
rs_update_rate_tbl(mvm, sta, lq_sta, tbl);
}
@@ -3098,6 +3114,7 @@
sband = hw->wiphy->bands[band];
lq_sta->lq.sta_id = sta_priv->sta_id;
+ sta_priv->tlc_amsdu = false;
for (j = 0; j < LQ_SIZE; j++)
rs_rate_scale_clear_tbl_windows(mvm, &lq_sta->lq_info[j]);
@@ -3657,10 +3674,13 @@
ssize_t ret;
struct iwl_lq_sta *lq_sta = file->private_data;
+ struct iwl_mvm_sta *mvmsta =
+ container_of(lq_sta, struct iwl_mvm_sta, lq_sta);
struct iwl_mvm *mvm;
struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
struct rs_rate *rate = &tbl->rate;
u32 ss_params;
+
mvm = lq_sta->pers.drv;
buff = kmalloc(2048, GFP_KERNEL);
if (!buff)
@@ -3686,10 +3706,11 @@
(is_ht20(rate)) ? "20MHz" :
(is_ht40(rate)) ? "40MHz" :
(is_ht80(rate)) ? "80Mhz" : "BAD BW");
- desc += sprintf(buff + desc, " %s %s %s\n",
+ desc += sprintf(buff + desc, " %s %s %s %s\n",
(rate->sgi) ? "SGI" : "NGI",
(rate->ldpc) ? "LDPC" : "BCC",
- (lq_sta->is_agg) ? "AGG on" : "");
+ (lq_sta->is_agg) ? "AGG on" : "",
+ (mvmsta->tlc_amsdu) ? "AMSDU on" : "");
}
desc += sprintf(buff+desc, "last tx rate=0x%X\n",
lq_sta->last_rate_n_flags);
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
index 145ec68..485cfc1 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
@@ -7,6 +7,7 @@
*
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -322,11 +323,9 @@
rx_status->freq =
ieee80211_channel_to_frequency(le16_to_cpu(phy_info->channel),
rx_status->band);
- /*
- * TSF as indicated by the fw is at INA time, but mac80211 expects the
- * TSF at the beginning of the MPDU.
- */
- /*rx_status->flag |= RX_FLAG_MACTIME_MPDU;*/
+
+ /* TSF as indicated by the firmware is at INA time */
+ rx_status->flag |= RX_FLAG_MACTIME_PLCP_START;
iwl_mvm_get_signal_strength(mvm, phy_info, rx_status);
@@ -448,6 +447,12 @@
iwl_mvm_update_frame_stats(mvm, rate_n_flags,
rx_status->flag & RX_FLAG_AMPDU_DETAILS);
#endif
+
+ if (unlikely((ieee80211_is_beacon(hdr->frame_control) ||
+ ieee80211_is_probe_resp(hdr->frame_control)) &&
+ mvm->sched_scan_pass_all == SCHED_SCAN_PASS_ALL_ENABLED))
+ mvm->sched_scan_pass_all = SCHED_SCAN_PASS_ALL_FOUND;
+
iwl_mvm_pass_packet_to_mac80211(mvm, napi, skb, hdr, len, ampdu_status,
crypt_len, rxb);
}
@@ -622,3 +627,51 @@
{
iwl_mvm_handle_rx_statistics(mvm, rxb_addr(rxb));
}
+
+void iwl_mvm_window_status_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_ba_window_status_notif *notif = (void *)pkt->data;
+ int i;
+ u32 pkt_len = iwl_rx_packet_payload_len(pkt);
+
+ if (WARN_ONCE(pkt_len != sizeof(*notif),
+ "Received window status notification of wrong size (%u)\n",
+ pkt_len))
+ return;
+
+ rcu_read_lock();
+ for (i = 0; i < BA_WINDOW_STREAMS_MAX; i++) {
+ struct ieee80211_sta *sta;
+ u8 sta_id, tid;
+ u64 bitmap;
+ u32 ssn;
+ u16 ratid;
+ u16 received_mpdu;
+
+ ratid = le16_to_cpu(notif->ra_tid[i]);
+ /* check that this TID is valid */
+ if (!(ratid & BA_WINDOW_STATUS_VALID_MSK))
+ continue;
+
+ received_mpdu = le16_to_cpu(notif->mpdu_rx_count[i]);
+ if (received_mpdu == 0)
+ continue;
+
+ tid = ratid & BA_WINDOW_STATUS_TID_MSK;
+ /* get the station */
+ sta_id = (ratid & BA_WINDOW_STATUS_STA_ID_MSK)
+ >> BA_WINDOW_STATUS_STA_ID_POS;
+ sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
+ if (IS_ERR_OR_NULL(sta))
+ continue;
+ bitmap = le64_to_cpu(notif->bitmap[i]);
+ ssn = le32_to_cpu(notif->start_seq_num[i]);
+
+ /* update mac80211 with the bitmap for the reordering buffer */
+ ieee80211_mark_rx_ba_filtered_frames(sta, tid, ssn, bitmap,
+ received_mpdu);
+ }
+ rcu_read_unlock();
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
index 615dea1..9a54f2d 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
@@ -7,7 +7,7 @@
*
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- * Copyright(c) 2015 Intel Deutschland GmbH
+ * Copyright(c) 2015 - 2016 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -29,7 +29,7 @@
*
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- * Copyright(c) 2015 Intel Deutschland GmbH
+ * Copyright(c) 2015 - 2016 Intel Deutschland GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -156,7 +156,14 @@
u16 len, u8 crypt_len,
struct iwl_rx_cmd_buffer *rxb)
{
- unsigned int hdrlen, fraglen;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_rx_mpdu_desc *desc = (void *)pkt->data;
+ unsigned int headlen, fraglen, pad_len = 0;
+ unsigned int hdrlen = ieee80211_hdrlen(hdr->frame_control);
+
+ if (desc->mac_flags2 & IWL_RX_MPDU_MFLG2_PAD)
+ pad_len = 2;
+ len -= pad_len;
/* If frame is small enough to fit in skb->head, pull it completely.
* If not, only pull ieee80211_hdr (including crypto if present, and
@@ -170,14 +177,23 @@
* If the latter changes (there are efforts in the standards group
* to do so) we should revisit this and ieee80211_data_to_8023().
*/
- hdrlen = (len <= skb_tailroom(skb)) ? len :
- sizeof(*hdr) + crypt_len + 8;
+ headlen = (len <= skb_tailroom(skb)) ? len :
+ hdrlen + crypt_len + 8;
+ /* The firmware may align the packet to DWORD.
+ * The padding is inserted after the IV.
+ * After copying the header + IV skip the padding if
+ * present before copying packet data.
+ */
+ hdrlen += crypt_len;
memcpy(skb_put(skb, hdrlen), hdr, hdrlen);
- fraglen = len - hdrlen;
+ memcpy(skb_put(skb, headlen - hdrlen), (u8 *)hdr + hdrlen + pad_len,
+ headlen - hdrlen);
+
+ fraglen = len - headlen;
if (fraglen) {
- int offset = (void *)hdr + hdrlen -
+ int offset = (void *)hdr + headlen + pad_len -
rxb_addr(rxb) + rxb_offset(rxb);
skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset,
@@ -285,6 +301,114 @@
skb->ip_summed = CHECKSUM_UNNECESSARY;
}
+/*
+ * returns true if a packet outside BA session is a duplicate and
+ * should be dropped
+ */
+static bool iwl_mvm_is_nonagg_dup(struct ieee80211_sta *sta, int queue,
+ struct ieee80211_rx_status *rx_status,
+ struct ieee80211_hdr *hdr,
+ struct iwl_rx_mpdu_desc *desc)
+{
+ struct iwl_mvm_sta *mvm_sta;
+ struct iwl_mvm_rxq_dup_data *dup_data;
+ u8 baid, tid, sub_frame_idx;
+
+ if (WARN_ON(IS_ERR_OR_NULL(sta)))
+ return false;
+
+ baid = (le32_to_cpu(desc->reorder_data) &
+ IWL_RX_MPDU_REORDER_BAID_MASK) >>
+ IWL_RX_MPDU_REORDER_BAID_SHIFT;
+
+ if (baid != IWL_RX_REORDER_DATA_INVALID_BAID)
+ return false;
+
+ mvm_sta = iwl_mvm_sta_from_mac80211(sta);
+ dup_data = &mvm_sta->dup_data[queue];
+
+ /*
+ * Drop duplicate 802.11 retransmissions
+ * (IEEE 802.11-2012: 9.3.2.10 "Duplicate detection and recovery")
+ */
+ if (ieee80211_is_ctl(hdr->frame_control) ||
+ ieee80211_is_qos_nullfunc(hdr->frame_control) ||
+ is_multicast_ether_addr(hdr->addr1)) {
+ rx_status->flag |= RX_FLAG_DUP_VALIDATED;
+ return false;
+ }
+
+ if (ieee80211_is_data_qos(hdr->frame_control))
+ /* frame has qos control */
+ tid = *ieee80211_get_qos_ctl(hdr) &
+ IEEE80211_QOS_CTL_TID_MASK;
+ else
+ tid = IWL_MAX_TID_COUNT;
+
+ /* If this wasn't a part of an A-MSDU the sub-frame index will be 0 */
+ sub_frame_idx = desc->amsdu_info & IWL_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK;
+
+ if (unlikely(ieee80211_has_retry(hdr->frame_control) &&
+ dup_data->last_seq[tid] == hdr->seq_ctrl &&
+ dup_data->last_sub_frame[tid] >= sub_frame_idx))
+ return true;
+
+ dup_data->last_seq[tid] = hdr->seq_ctrl;
+ dup_data->last_sub_frame[tid] = sub_frame_idx;
+
+ rx_status->flag |= RX_FLAG_DUP_VALIDATED;
+
+ return false;
+}
+
+int iwl_mvm_notify_rx_queue(struct iwl_mvm *mvm, u32 rxq_mask,
+ const u8 *data, u32 count)
+{
+ struct iwl_rxq_sync_cmd *cmd;
+ u32 data_size = sizeof(*cmd) + count;
+ int ret;
+
+ /* should be DWORD aligned */
+ if (WARN_ON(count & 3 || count > IWL_MULTI_QUEUE_SYNC_MSG_MAX_SIZE))
+ return -EINVAL;
+
+ cmd = kzalloc(data_size, GFP_KERNEL);
+ if (!cmd)
+ return -ENOMEM;
+
+ cmd->rxq_mask = cpu_to_le32(rxq_mask);
+ cmd->count = cpu_to_le32(count);
+ cmd->flags = 0;
+ memcpy(cmd->payload, data, count);
+
+ ret = iwl_mvm_send_cmd_pdu(mvm,
+ WIDE_ID(DATA_PATH_GROUP,
+ TRIGGER_RX_QUEUES_NOTIF_CMD),
+ 0, data_size, cmd);
+
+ kfree(cmd);
+ return ret;
+}
+
+void iwl_mvm_rx_queue_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+ int queue)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_rxq_sync_notification *notif;
+ struct iwl_mvm_internal_rxq_notif *internal_notif;
+
+ notif = (void *)pkt->data;
+ internal_notif = (void *)notif->payload;
+
+ switch (internal_notif->type) {
+ case IWL_MVM_RXQ_NOTIF_DEL_BA:
+ /* TODO */
+ break;
+ default:
+ WARN_ONCE(1, "Invalid identifier %d", internal_notif->type);
+ }
+}
+
void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
struct iwl_rx_cmd_buffer *rxb, int queue)
{
@@ -332,6 +456,8 @@
rx_status->freq = ieee80211_channel_to_frequency(desc->channel,
rx_status->band);
iwl_mvm_get_signal_strength(mvm, desc, rx_status);
+ /* TSF as indicated by the firmware is at INA time */
+ rx_status->flag |= RX_FLAG_MACTIME_PLCP_START;
rcu_read_lock();
@@ -387,6 +513,24 @@
if (ieee80211_is_data(hdr->frame_control))
iwl_mvm_rx_csum(sta, skb, desc);
+
+ if (iwl_mvm_is_nonagg_dup(sta, queue, rx_status, hdr, desc)) {
+ kfree_skb(skb);
+ rcu_read_unlock();
+ return;
+ }
+
+ /*
+ * Our hardware de-aggregates AMSDUs but copies the mac header
+ * as it to the de-aggregated MPDUs. We need to turn off the
+ * AMSDU bit in the QoS control ourselves.
+ */
+ if ((desc->mac_flags2 & IWL_RX_MPDU_MFLG2_AMSDU) &&
+ !WARN_ON(!ieee80211_is_data_qos(hdr->frame_control))) {
+ u8 *qc = ieee80211_get_qos_ctl(hdr);
+
+ *qc &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
+ }
}
/*
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c
index aa6d807..09eb72c 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c
@@ -7,6 +7,7 @@
*
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -33,6 +34,7 @@
*
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -297,6 +299,12 @@
iwl_mvm_dump_channel_list(notif->results,
notif->scanned_channels, buf,
sizeof(buf)));
+
+ if (mvm->sched_scan_pass_all == SCHED_SCAN_PASS_ALL_FOUND) {
+ IWL_DEBUG_SCAN(mvm, "Pass all scheduled scan results found\n");
+ ieee80211_sched_scan_results(mvm->hw);
+ mvm->sched_scan_pass_all = SCHED_SCAN_PASS_ALL_ENABLED;
+ }
}
void iwl_mvm_rx_scan_match_found(struct iwl_mvm *mvm,
@@ -380,6 +388,7 @@
mvm->scan_status &= ~IWL_MVM_SCAN_SCHED;
ieee80211_sched_scan_stopped(mvm->hw);
+ mvm->sched_scan_pass_all = SCHED_SCAN_PASS_ALL_DISABLED;
} else if (mvm->scan_status & IWL_MVM_SCAN_REGULAR) {
IWL_DEBUG_SCAN(mvm, "Regular scan %s, EBS status %s (FW)\n",
aborted ? "aborted" : "completed",
@@ -533,10 +542,13 @@
IWL_DEBUG_SCAN(mvm,
"Sending scheduled scan with filtering, n_match_sets %d\n",
req->n_match_sets);
+ mvm->sched_scan_pass_all = SCHED_SCAN_PASS_ALL_DISABLED;
return false;
}
IWL_DEBUG_SCAN(mvm, "Sending Scheduled scan without filtering\n");
+
+ mvm->sched_scan_pass_all = SCHED_SCAN_PASS_ALL_ENABLED;
return true;
}
@@ -788,6 +800,9 @@
flags |= IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE;
#endif
+ if (mvm->sched_scan_pass_all == SCHED_SCAN_PASS_ALL_ENABLED)
+ flags |= IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE;
+
if (iwl_mvm_is_regular_scan(params) &&
vif->type != NL80211_IFTYPE_P2P_DEVICE &&
params->type != IWL_SCAN_TYPE_FRAGMENTED)
@@ -1074,6 +1089,9 @@
flags |= IWL_UMAC_SCAN_GEN_FLAGS_ITER_COMPLETE;
#endif
+ if (mvm->sched_scan_pass_all == SCHED_SCAN_PASS_ALL_ENABLED)
+ flags |= IWL_UMAC_SCAN_GEN_FLAGS_ITER_COMPLETE;
+
if (iwl_mvm_is_regular_scan(params) &&
vif->type != NL80211_IFTYPE_P2P_DEVICE &&
params->type != IWL_SCAN_TYPE_FRAGMENTED)
@@ -1301,10 +1319,6 @@
return -EBUSY;
}
- /* we don't support "match all" in the firmware */
- if (!req->n_match_sets)
- return -EOPNOTSUPP;
-
ret = iwl_mvm_check_running_scans(mvm, type);
if (ret)
return ret;
@@ -1400,6 +1414,7 @@
iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
} else if (mvm->scan_uid_status[uid] == IWL_MVM_SCAN_SCHED) {
ieee80211_sched_scan_stopped(mvm->hw);
+ mvm->sched_scan_pass_all = SCHED_SCAN_PASS_ALL_DISABLED;
}
mvm->scan_status &= ~mvm->scan_uid_status[uid];
@@ -1434,6 +1449,12 @@
iwl_mvm_dump_channel_list(notif->results,
notif->scanned_channels, buf,
sizeof(buf)));
+
+ if (mvm->sched_scan_pass_all == SCHED_SCAN_PASS_ALL_FOUND) {
+ IWL_DEBUG_SCAN(mvm, "Pass all scheduled scan results found\n");
+ ieee80211_sched_scan_results(mvm->hw);
+ mvm->sched_scan_pass_all = SCHED_SCAN_PASS_ALL_ENABLED;
+ }
}
static int iwl_mvm_umac_scan_abort(struct iwl_mvm *mvm, int type)
@@ -1528,6 +1549,7 @@
uid = iwl_mvm_scan_uid_by_status(mvm, IWL_MVM_SCAN_SCHED);
if (uid >= 0 && !mvm->restart_fw) {
ieee80211_sched_scan_stopped(mvm->hw);
+ mvm->sched_scan_pass_all = SCHED_SCAN_PASS_ALL_DISABLED;
mvm->scan_uid_status[uid] = 0;
}
@@ -1549,8 +1571,11 @@
* restart_hw, so do not report if FW is about to be
* restarted.
*/
- if ((mvm->scan_status & IWL_MVM_SCAN_SCHED) && !mvm->restart_fw)
+ if ((mvm->scan_status & IWL_MVM_SCAN_SCHED) &&
+ !mvm->restart_fw) {
ieee80211_sched_scan_stopped(mvm->hw);
+ mvm->sched_scan_pass_all = SCHED_SCAN_PASS_ALL_DISABLED;
+ }
}
}
@@ -1586,6 +1611,7 @@
ieee80211_scan_completed(mvm->hw, true);
} else if (notify) {
ieee80211_sched_scan_stopped(mvm->hw);
+ mvm->sched_scan_pass_all = SCHED_SCAN_PASS_ALL_DISABLED;
}
return ret;
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c
index 4854e79..ef99942 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c
@@ -280,6 +280,7 @@
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
+ struct iwl_mvm_rxq_dup_data *dup_data;
int i, ret, sta_id;
lockdep_assert_held(&mvm->mutex);
@@ -327,6 +328,16 @@
}
mvm_sta->agg_tids = 0;
+ if (iwl_mvm_has_new_rx_api(mvm) &&
+ !test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
+ dup_data = kcalloc(mvm->trans->num_rx_queues,
+ sizeof(*dup_data),
+ GFP_KERNEL);
+ if (!dup_data)
+ return -ENOMEM;
+ mvm_sta->dup_data = dup_data;
+ }
+
ret = iwl_mvm_sta_send_to_fw(mvm, sta, false);
if (ret)
goto err;
@@ -508,6 +519,9 @@
lockdep_assert_held(&mvm->mutex);
+ if (iwl_mvm_has_new_rx_api(mvm))
+ kfree(mvm_sta->dup_data);
+
if (vif->type == NL80211_IFTYPE_STATION &&
mvmvif->ap_sta_id == mvm_sta->sta_id) {
ret = iwl_mvm_drain_sta(mvm, mvm_sta, true);
@@ -1031,15 +1045,23 @@
}
int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, u16 tid, u8 buf_size)
+ struct ieee80211_sta *sta, u16 tid, u8 buf_size,
+ bool amsdu)
{
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
unsigned int wdg_timeout =
iwl_mvm_get_wd_timeout(mvm, vif, sta->tdls, false);
- int queue, fifo, ret;
+ int queue, ret;
u16 ssn;
+ struct iwl_trans_txq_scd_cfg cfg = {
+ .sta_id = mvmsta->sta_id,
+ .tid = tid,
+ .frame_limit = buf_size,
+ .aggregate = true,
+ };
+
BUILD_BUG_ON((sizeof(mvmsta->agg_tids) * BITS_PER_BYTE)
!= IWL_MAX_TID_COUNT);
@@ -1051,13 +1073,13 @@
tid_data->state = IWL_AGG_ON;
mvmsta->agg_tids |= BIT(tid);
tid_data->ssn = 0xffff;
+ tid_data->amsdu_in_ampdu_allowed = amsdu;
spin_unlock_bh(&mvmsta->lock);
- fifo = iwl_mvm_ac_to_tx_fifo[tid_to_mac80211_ac[tid]];
+ cfg.fifo = iwl_mvm_ac_to_tx_fifo[tid_to_mac80211_ac[tid]];
- iwl_mvm_enable_agg_txq(mvm, queue,
- vif->hw_queue[tid_to_mac80211_ac[tid]], fifo,
- mvmsta->sta_id, tid, buf_size, ssn, wdg_timeout);
+ iwl_mvm_enable_txq(mvm, queue, vif->hw_queue[tid_to_mac80211_ac[tid]],
+ ssn, &cfg, wdg_timeout);
ret = iwl_mvm_sta_tx_agg(mvm, sta, tid, queue, true);
if (ret)
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h
index e3b9446..1a8f69a 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h
@@ -258,8 +258,7 @@
* This is basically (last acked packet++).
* @rate_n_flags: Rate at which Tx was attempted. Holds the data between the
* Tx response (TX_CMD), and the block ack notification (COMPRESSED_BA).
- * @reduced_tpc: Reduced tx power. Holds the data between the
- * Tx response (TX_CMD), and the block ack notification (COMPRESSED_BA).
+ * @amsdu_in_ampdu_allowed: true if A-MSDU in A-MPDU is allowed.
* @state: state of the BA agreement establishment / tear down.
* @txq_id: Tx queue used by the BA session
* @ssn: the first packet to be sent in AGG HW queue in Tx AGG start flow, or
@@ -273,7 +272,7 @@
u16 next_reclaimed;
/* The rest is Tx AGG related */
u32 rate_n_flags;
- u8 reduced_tpc;
+ bool amsdu_in_ampdu_allowed;
enum iwl_mvm_agg_state state;
u16 txq_id;
u16 ssn;
@@ -294,6 +293,16 @@
};
/**
+ * struct iwl_mvm_rxq_dup_data - per station per rx queue data
+ * @last_seq: last sequence per tid for duplicate packet detection
+ * @last_sub_frame: last subframe packet
+ */
+struct iwl_mvm_rxq_dup_data {
+ __le16 last_seq[IWL_MAX_TID_COUNT + 1];
+ u8 last_sub_frame[IWL_MAX_TID_COUNT + 1];
+} ____cacheline_aligned_in_smp;
+
+/**
* struct iwl_mvm_sta - representation of a station in the driver
* @sta_id: the index of the station in the fw (will be replaced by id_n_color)
* @tfd_queue_msk: the tfd queues used by the station
@@ -311,6 +320,7 @@
* @tx_protection: reference counter for controlling the Tx protection.
* @tt_tx_protection: is thermal throttling enable Tx protection?
* @disable_tx: is tx to this STA disabled?
+ * @tlc_amsdu: true if A-MSDU is allowed
* @agg_tids: bitmap of tids whose status is operational aggregated (IWL_AGG_ON)
* @sleep_tx_count: the number of frames that we told the firmware to let out
* even when that station is asleep. This is useful in case the queue
@@ -318,6 +328,7 @@
* we are sending frames from an AMPDU queue and there was a hole in
* the BA window. To be used for UAPSD only.
* @ptk_pn: per-queue PTK PN data structures
+ * @dup_data: per queue duplicate packet detection data
*
* When mac80211 creates a station it reserves some space (hw->sta_data_size)
* in the structure for use by driver. This structure is placed in that
@@ -337,14 +348,15 @@
struct iwl_mvm_tid_data tid_data[IWL_MAX_TID_COUNT];
struct iwl_lq_sta lq_sta;
struct ieee80211_vif *vif;
-
struct iwl_mvm_key_pn __rcu *ptk_pn[4];
+ struct iwl_mvm_rxq_dup_data *dup_data;
/* Temporary, until the new TLC will control the Tx protection */
s8 tx_protection;
bool tt_tx_protection;
bool disable_tx;
+ bool tlc_amsdu;
u8 agg_tids;
u8 sleep_tx_count;
};
@@ -405,7 +417,8 @@
int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, u16 tid, u16 *ssn);
int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, u16 tid, u8 buf_size);
+ struct ieee80211_sta *sta, u16 tid, u8 buf_size,
+ bool amsdu);
int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, u16 tid);
int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c
index 924dd6a..2c12789 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c
@@ -371,20 +371,13 @@
iwl_mvm_te_check_trigger(mvm, notif, te_data);
- if (!le32_to_cpu(notif->status)) {
- IWL_DEBUG_TE(mvm,
- "ERROR: Aux ROC Time Event %s notification failure\n",
- (le32_to_cpu(notif->action) &
- TE_V2_NOTIF_HOST_EVENT_START) ? "start" : "end");
- return -EINVAL;
- }
-
IWL_DEBUG_TE(mvm,
- "Aux ROC time event notification - UID = 0x%x action %d\n",
+ "Aux ROC time event notification - UID = 0x%x action %d (error = %d)\n",
le32_to_cpu(notif->unique_id),
- le32_to_cpu(notif->action));
+ le32_to_cpu(notif->action), le32_to_cpu(notif->status));
- if (le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_END) {
+ if (!le32_to_cpu(notif->status) ||
+ le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_END) {
/* End TE, notify mac80211 */
ieee80211_remain_on_channel_expired(mvm->hw);
iwl_mvm_roc_finished(mvm); /* flush aux queue */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.h b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.h
index 99d9a35..3d2e8b6 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.h
@@ -115,7 +115,7 @@
* needed by the driver.
*/
-#define IWL_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS 500
+#define IWL_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS 600
#define IWL_MVM_TE_SESSION_PROTECTION_MIN_TIME_MS 400
/**
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c
index 758d05a..f1f2825 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c
@@ -7,6 +7,7 @@
*
* Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 - 2016 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -33,7 +34,7 @@
*
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- * Copyright(c) 2015 Intel Deutschland GmbH
+ * Copyright(c) 2015 - 2016 Intel Deutschland GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -64,6 +65,8 @@
*
*****************************************************************************/
+#include <linux/sort.h>
+
#include "mvm.h"
#define IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT HZ
@@ -79,8 +82,10 @@
IWL_ERR(mvm, "Enter CT Kill\n");
iwl_mvm_set_hw_ctkill_state(mvm, true);
- tt->throttle = false;
- tt->dynamic_smps = false;
+ if (!iwl_mvm_is_tt_in_fw(mvm)) {
+ tt->throttle = false;
+ tt->dynamic_smps = false;
+ }
/* Don't schedule an exit work if we're in test mode, since
* the temperature will not change unless we manually set it
@@ -116,18 +121,21 @@
static int iwl_mvm_temp_notif_parse(struct iwl_mvm *mvm,
struct iwl_rx_packet *pkt)
{
- struct iwl_dts_measurement_notif *notif;
+ struct iwl_dts_measurement_notif_v1 *notif_v1;
int len = iwl_rx_packet_payload_len(pkt);
int temp;
- if (WARN_ON_ONCE(len < sizeof(*notif))) {
+ /* we can use notif_v1 only, because v2 only adds an additional
+ * parameter, which is not used in this function.
+ */
+ if (WARN_ON_ONCE(len < sizeof(*notif_v1))) {
IWL_ERR(mvm, "Invalid DTS_MEASUREMENT_NOTIFICATION\n");
return -EINVAL;
}
- notif = (void *)pkt->data;
+ notif_v1 = (void *)pkt->data;
- temp = le32_to_cpu(notif->temp);
+ temp = le32_to_cpu(notif_v1->temp);
/* shouldn't be negative, but since it's s32, make sure it isn't */
if (WARN_ON_ONCE(temp < 0))
@@ -158,17 +166,78 @@
void iwl_mvm_temp_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_dts_measurement_notif_v2 *notif_v2;
+ int len = iwl_rx_packet_payload_len(pkt);
int temp;
+ u32 ths_crossed;
/* the notification is handled synchronously in ctkill, so skip here */
if (test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status))
return;
temp = iwl_mvm_temp_notif_parse(mvm, pkt);
- if (temp < 0)
+
+ if (!iwl_mvm_is_tt_in_fw(mvm)) {
+ if (temp >= 0)
+ iwl_mvm_tt_temp_changed(mvm, temp);
+ return;
+ }
+
+ if (WARN_ON_ONCE(len < sizeof(*notif_v2))) {
+ IWL_ERR(mvm, "Invalid DTS_MEASUREMENT_NOTIFICATION\n");
+ return;
+ }
+
+ notif_v2 = (void *)pkt->data;
+ ths_crossed = le32_to_cpu(notif_v2->threshold_idx);
+
+ /* 0xFF in ths_crossed means the notification is not related
+ * to a trip, so we can ignore it here.
+ */
+ if (ths_crossed == 0xFF)
return;
- iwl_mvm_tt_temp_changed(mvm, temp);
+ IWL_DEBUG_TEMP(mvm, "Temp = %d Threshold crossed = %d\n",
+ temp, ths_crossed);
+
+#ifdef CONFIG_THERMAL
+ if (WARN_ON(ths_crossed >= IWL_MAX_DTS_TRIPS))
+ return;
+
+ /*
+ * We are now handling a temperature notification from the firmware
+ * in ASYNC and hold the mutex. thermal_notify_framework will call
+ * us back through get_temp() which ought to send a SYNC command to
+ * the firmware and hence to take the mutex.
+ * Avoid the deadlock by unlocking the mutex here.
+ */
+ if (mvm->tz_device.tzone) {
+ struct iwl_mvm_thermal_device *tz_dev = &mvm->tz_device;
+
+ mutex_unlock(&mvm->mutex);
+ thermal_notify_framework(tz_dev->tzone,
+ tz_dev->fw_trips_index[ths_crossed]);
+ mutex_lock(&mvm->mutex);
+ }
+#endif /* CONFIG_THERMAL */
+}
+
+void iwl_mvm_ct_kill_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct ct_kill_notif *notif;
+ int len = iwl_rx_packet_payload_len(pkt);
+
+ if (WARN_ON_ONCE(len != sizeof(*notif))) {
+ IWL_ERR(mvm, "Invalid CT_KILL_NOTIFICATION\n");
+ return;
+ }
+
+ notif = (struct ct_kill_notif *)pkt->data;
+ IWL_DEBUG_TEMP(mvm, "CT Kill notification temperature = %d\n",
+ notif->temperature);
+
+ iwl_mvm_enter_ctkill(mvm);
}
static int iwl_mvm_get_temp_cmd(struct iwl_mvm *mvm)
@@ -236,6 +305,12 @@
tt = container_of(work, struct iwl_mvm_tt_mgmt, ct_kill_exit.work);
mvm = container_of(tt, struct iwl_mvm, thermal_throttle);
+ if (iwl_mvm_is_tt_in_fw(mvm)) {
+ iwl_mvm_exit_ctkill(mvm);
+
+ return;
+ }
+
duration = tt->params.ct_kill_duration;
mutex_lock(&mvm->mutex);
@@ -435,7 +510,378 @@
.support_tx_backoff = true,
};
-void iwl_mvm_tt_initialize(struct iwl_mvm *mvm, u32 min_backoff)
+/* budget in mWatt */
+static const u32 iwl_mvm_cdev_budgets[] = {
+ 2000, /* cooling state 0 */
+ 1800, /* cooling state 1 */
+ 1600, /* cooling state 2 */
+ 1400, /* cooling state 3 */
+ 1200, /* cooling state 4 */
+ 1000, /* cooling state 5 */
+ 900, /* cooling state 6 */
+ 800, /* cooling state 7 */
+ 700, /* cooling state 8 */
+ 650, /* cooling state 9 */
+ 600, /* cooling state 10 */
+ 550, /* cooling state 11 */
+ 500, /* cooling state 12 */
+ 450, /* cooling state 13 */
+ 400, /* cooling state 14 */
+ 350, /* cooling state 15 */
+ 300, /* cooling state 16 */
+ 250, /* cooling state 17 */
+ 200, /* cooling state 18 */
+ 150, /* cooling state 19 */
+};
+
+int iwl_mvm_ctdp_command(struct iwl_mvm *mvm, u32 op, u32 state)
+{
+ struct iwl_mvm_ctdp_cmd cmd = {
+ .operation = cpu_to_le32(op),
+ .budget = cpu_to_le32(iwl_mvm_cdev_budgets[state]),
+ .window_size = 0,
+ };
+ int ret;
+ u32 status;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ ret = iwl_mvm_send_cmd_pdu_status(mvm, WIDE_ID(PHY_OPS_GROUP,
+ CTDP_CONFIG_CMD),
+ sizeof(cmd), &cmd, &status);
+
+ if (ret) {
+ IWL_ERR(mvm, "cTDP command failed (err=%d)\n", ret);
+ return ret;
+ }
+
+ switch (op) {
+ case CTDP_CMD_OPERATION_START:
+#ifdef CONFIG_THERMAL
+ mvm->cooling_dev.cur_state = state;
+#endif /* CONFIG_THERMAL */
+ break;
+ case CTDP_CMD_OPERATION_REPORT:
+ IWL_DEBUG_TEMP(mvm, "cTDP avg energy in mWatt = %d\n", status);
+ /* when the function is called with CTDP_CMD_OPERATION_REPORT
+ * option the function should return the average budget value
+ * that is received from the FW.
+ * The budget can't be less or equal to 0, so it's possible
+ * to distinguish between error values and budgets.
+ */
+ return status;
+ case CTDP_CMD_OPERATION_STOP:
+ IWL_DEBUG_TEMP(mvm, "cTDP stopped successfully\n");
+ break;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_THERMAL
+static int compare_temps(const void *a, const void *b)
+{
+ return ((s16)le16_to_cpu(*(__le16 *)a) -
+ (s16)le16_to_cpu(*(__le16 *)b));
+}
+
+int iwl_mvm_send_temp_report_ths_cmd(struct iwl_mvm *mvm)
+{
+ struct temp_report_ths_cmd cmd = {0};
+ int ret, i, j, idx = 0;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (!mvm->tz_device.tzone)
+ return -EINVAL;
+
+ /* The driver holds array of temperature trips that are unsorted
+ * and uncompressed, the FW should get it compressed and sorted
+ */
+
+ /* compress temp_trips to cmd array, remove uninitialized values*/
+ for (i = 0; i < IWL_MAX_DTS_TRIPS; i++) {
+ if (mvm->tz_device.temp_trips[i] != S16_MIN) {
+ cmd.thresholds[idx++] =
+ cpu_to_le16(mvm->tz_device.temp_trips[i]);
+ }
+ }
+ cmd.num_temps = cpu_to_le32(idx);
+
+ if (!idx)
+ goto send;
+
+ /*sort cmd array*/
+ sort(cmd.thresholds, idx, sizeof(s16), compare_temps, NULL);
+
+ /* we should save the indexes of trips because we sort
+ * and compress the orginal array
+ */
+ for (i = 0; i < idx; i++) {
+ for (j = 0; j < IWL_MAX_DTS_TRIPS; j++) {
+ if (le16_to_cpu(cmd.thresholds[i]) ==
+ mvm->tz_device.temp_trips[j])
+ mvm->tz_device.fw_trips_index[i] = j;
+ }
+ }
+
+send:
+ ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(PHY_OPS_GROUP,
+ TEMP_REPORTING_THRESHOLDS_CMD),
+ 0, sizeof(cmd), &cmd);
+ if (ret)
+ IWL_ERR(mvm, "TEMP_REPORT_THS_CMD command failed (err=%d)\n",
+ ret);
+
+ return ret;
+}
+
+static int iwl_mvm_tzone_get_temp(struct thermal_zone_device *device,
+ int *temperature)
+{
+ struct iwl_mvm *mvm = (struct iwl_mvm *)device->devdata;
+ int ret;
+ int temp;
+
+ mutex_lock(&mvm->mutex);
+
+ if (!mvm->ucode_loaded || !(mvm->cur_ucode == IWL_UCODE_REGULAR)) {
+ ret = -EIO;
+ goto out;
+ }
+
+ ret = iwl_mvm_get_temp(mvm, &temp);
+ if (ret)
+ goto out;
+
+ *temperature = temp * 1000;
+
+out:
+ mutex_unlock(&mvm->mutex);
+ return ret;
+}
+
+static int iwl_mvm_tzone_get_trip_temp(struct thermal_zone_device *device,
+ int trip, int *temp)
+{
+ struct iwl_mvm *mvm = (struct iwl_mvm *)device->devdata;
+
+ if (trip < 0 || trip >= IWL_MAX_DTS_TRIPS)
+ return -EINVAL;
+
+ *temp = mvm->tz_device.temp_trips[trip] * 1000;
+
+ return 0;
+}
+
+static int iwl_mvm_tzone_get_trip_type(struct thermal_zone_device *device,
+ int trip, enum thermal_trip_type *type)
+{
+ if (trip < 0 || trip >= IWL_MAX_DTS_TRIPS)
+ return -EINVAL;
+
+ *type = THERMAL_TRIP_PASSIVE;
+
+ return 0;
+}
+
+static int iwl_mvm_tzone_set_trip_temp(struct thermal_zone_device *device,
+ int trip, int temp)
+{
+ struct iwl_mvm *mvm = (struct iwl_mvm *)device->devdata;
+ struct iwl_mvm_thermal_device *tzone;
+ int i, ret;
+ s16 temperature;
+
+ mutex_lock(&mvm->mutex);
+
+ if (!mvm->ucode_loaded || !(mvm->cur_ucode == IWL_UCODE_REGULAR)) {
+ ret = -EIO;
+ goto out;
+ }
+
+ if (trip < 0 || trip >= IWL_MAX_DTS_TRIPS) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if ((temp / 1000) > S16_MAX) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ temperature = (s16)(temp / 1000);
+ tzone = &mvm->tz_device;
+
+ if (!tzone) {
+ ret = -EIO;
+ goto out;
+ }
+
+ /* no updates*/
+ if (tzone->temp_trips[trip] == temperature) {
+ ret = 0;
+ goto out;
+ }
+
+ /* already existing temperature */
+ for (i = 0; i < IWL_MAX_DTS_TRIPS; i++) {
+ if (tzone->temp_trips[i] == temperature) {
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ tzone->temp_trips[trip] = temperature;
+
+ ret = iwl_mvm_send_temp_report_ths_cmd(mvm);
+out:
+ mutex_unlock(&mvm->mutex);
+ return ret;
+}
+
+static struct thermal_zone_device_ops tzone_ops = {
+ .get_temp = iwl_mvm_tzone_get_temp,
+ .get_trip_temp = iwl_mvm_tzone_get_trip_temp,
+ .get_trip_type = iwl_mvm_tzone_get_trip_type,
+ .set_trip_temp = iwl_mvm_tzone_set_trip_temp,
+};
+
+/* make all trips writable */
+#define IWL_WRITABLE_TRIPS_MSK (BIT(IWL_MAX_DTS_TRIPS) - 1)
+
+static void iwl_mvm_thermal_zone_register(struct iwl_mvm *mvm)
+{
+ int i;
+ char name[] = "iwlwifi";
+
+ if (!iwl_mvm_is_tt_in_fw(mvm)) {
+ mvm->tz_device.tzone = NULL;
+
+ return;
+ }
+
+ BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH);
+
+ mvm->tz_device.tzone = thermal_zone_device_register(name,
+ IWL_MAX_DTS_TRIPS,
+ IWL_WRITABLE_TRIPS_MSK,
+ mvm, &tzone_ops,
+ NULL, 0, 0);
+ if (IS_ERR(mvm->tz_device.tzone)) {
+ IWL_DEBUG_TEMP(mvm,
+ "Failed to register to thermal zone (err = %ld)\n",
+ PTR_ERR(mvm->tz_device.tzone));
+ mvm->tz_device.tzone = NULL;
+ return;
+ }
+
+ /* 0 is a valid temperature,
+ * so initialize the array with S16_MIN which invalid temperature
+ */
+ for (i = 0 ; i < IWL_MAX_DTS_TRIPS; i++)
+ mvm->tz_device.temp_trips[i] = S16_MIN;
+}
+
+static int iwl_mvm_tcool_get_max_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ *state = ARRAY_SIZE(iwl_mvm_cdev_budgets) - 1;
+
+ return 0;
+}
+
+static int iwl_mvm_tcool_get_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ struct iwl_mvm *mvm = (struct iwl_mvm *)(cdev->devdata);
+
+ if (test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status))
+ return -EBUSY;
+
+ *state = mvm->cooling_dev.cur_state;
+
+ return 0;
+}
+
+static int iwl_mvm_tcool_set_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long new_state)
+{
+ struct iwl_mvm *mvm = (struct iwl_mvm *)(cdev->devdata);
+ int ret;
+
+ if (!mvm->ucode_loaded || !(mvm->cur_ucode == IWL_UCODE_REGULAR))
+ return -EIO;
+
+ if (test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status))
+ return -EBUSY;
+
+ mutex_lock(&mvm->mutex);
+
+ if (new_state >= ARRAY_SIZE(iwl_mvm_cdev_budgets)) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ ret = iwl_mvm_ctdp_command(mvm, CTDP_CMD_OPERATION_START,
+ new_state);
+
+unlock:
+ mutex_unlock(&mvm->mutex);
+ return ret;
+}
+
+static struct thermal_cooling_device_ops tcooling_ops = {
+ .get_max_state = iwl_mvm_tcool_get_max_state,
+ .get_cur_state = iwl_mvm_tcool_get_cur_state,
+ .set_cur_state = iwl_mvm_tcool_set_cur_state,
+};
+
+static void iwl_mvm_cooling_device_register(struct iwl_mvm *mvm)
+{
+ char name[] = "iwlwifi";
+
+ if (!iwl_mvm_is_ctdp_supported(mvm))
+ return;
+
+ BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH);
+
+ mvm->cooling_dev.cdev =
+ thermal_cooling_device_register(name,
+ mvm,
+ &tcooling_ops);
+
+ if (IS_ERR(mvm->cooling_dev.cdev)) {
+ IWL_DEBUG_TEMP(mvm,
+ "Failed to register to cooling device (err = %ld)\n",
+ PTR_ERR(mvm->cooling_dev.cdev));
+ mvm->cooling_dev.cdev = NULL;
+ return;
+ }
+}
+
+static void iwl_mvm_thermal_zone_unregister(struct iwl_mvm *mvm)
+{
+ if (!iwl_mvm_is_tt_in_fw(mvm) || !mvm->tz_device.tzone)
+ return;
+
+ IWL_DEBUG_TEMP(mvm, "Thermal zone device unregister\n");
+ thermal_zone_device_unregister(mvm->tz_device.tzone);
+ mvm->tz_device.tzone = NULL;
+}
+
+static void iwl_mvm_cooling_device_unregister(struct iwl_mvm *mvm)
+{
+ if (!iwl_mvm_is_ctdp_supported(mvm) || !mvm->cooling_dev.cdev)
+ return;
+
+ IWL_DEBUG_TEMP(mvm, "Cooling device unregister\n");
+ thermal_cooling_device_unregister(mvm->cooling_dev.cdev);
+ mvm->cooling_dev.cdev = NULL;
+}
+#endif /* CONFIG_THERMAL */
+
+void iwl_mvm_thermal_initialize(struct iwl_mvm *mvm, u32 min_backoff)
{
struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
@@ -450,10 +896,20 @@
tt->dynamic_smps = false;
tt->min_backoff = min_backoff;
INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill);
+
+#ifdef CONFIG_THERMAL
+ iwl_mvm_cooling_device_register(mvm);
+ iwl_mvm_thermal_zone_register(mvm);
+#endif
}
-void iwl_mvm_tt_exit(struct iwl_mvm *mvm)
+void iwl_mvm_thermal_exit(struct iwl_mvm *mvm)
{
cancel_delayed_work_sync(&mvm->thermal_throttle.ct_kill_exit);
IWL_DEBUG_TEMP(mvm, "Exit Thermal Throttling\n");
+
+#ifdef CONFIG_THERMAL
+ iwl_mvm_cooling_device_unregister(mvm);
+ iwl_mvm_thermal_zone_unregister(mvm);
+#endif
}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
index dae2c40..75870e6 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
@@ -7,6 +7,7 @@
*
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -65,6 +66,7 @@
#include <linux/ieee80211.h>
#include <linux/etherdevice.h>
#include <linux/tcp.h>
+#include <net/ip.h>
#include "iwl-trans.h"
#include "iwl-eeprom-parse.h"
@@ -182,7 +184,8 @@
tx_cmd->tx_flags = cpu_to_le32(tx_flags);
/* Total # bytes to be transmitted */
- tx_cmd->len = cpu_to_le16((u16)skb->len);
+ tx_cmd->len = cpu_to_le16((u16)skb->len +
+ (uintptr_t)info->driver_data[0]);
tx_cmd->next_frame_len = 0;
tx_cmd->life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE);
tx_cmd->sta_id = sta_id;
@@ -372,6 +375,9 @@
info->hw_queue != info->control.vif->cab_queue)))
return -1;
+ /* This holds the amsdu headers length */
+ info->driver_data[0] = (void *)(uintptr_t)0;
+
/*
* IWL_MVM_OFFCHANNEL_QUEUE is used for ROC packets that can be used
* in 2 different types of vifs, P2P & STATION. P2P uses the offchannel
@@ -437,33 +443,194 @@
return 0;
}
-static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb_gso,
+#ifdef CONFIG_INET
+static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb,
struct ieee80211_sta *sta,
struct sk_buff_head *mpdus_skb)
{
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hdr *hdr = (void *)skb->data;
+ unsigned int mss = skb_shinfo(skb)->gso_size;
struct sk_buff *tmp, *next;
- char cb[sizeof(skb_gso->cb)];
+ char cb[sizeof(skb->cb)];
+ unsigned int num_subframes, tcp_payload_len, subf_len, max_amsdu_len;
+ bool ipv4 = (skb->protocol == htons(ETH_P_IP));
+ u16 ip_base_id = ipv4 ? ntohs(ip_hdr(skb)->id) : 0;
+ u16 amsdu_add, snap_ip_tcp, pad, i = 0;
+ unsigned int dbg_max_amsdu_len;
+ u8 *qc, tid, txf;
- memcpy(cb, skb_gso->cb, sizeof(cb));
- next = skb_gso_segment(skb_gso, 0);
- if (IS_ERR(next))
+ snap_ip_tcp = 8 + skb_transport_header(skb) - skb_network_header(skb) +
+ tcp_hdrlen(skb);
+
+ qc = ieee80211_get_qos_ctl(hdr);
+ tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
+ if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT))
+ return -EINVAL;
+
+ if (!sta->max_amsdu_len ||
+ !ieee80211_is_data_qos(hdr->frame_control) ||
+ !mvmsta->tlc_amsdu) {
+ num_subframes = 1;
+ pad = 0;
+ goto segment;
+ }
+
+ /*
+ * No need to lock amsdu_in_ampdu_allowed since it can't be modified
+ * during an BA session.
+ */
+ if (info->flags & IEEE80211_TX_CTL_AMPDU &&
+ !mvmsta->tid_data[tid].amsdu_in_ampdu_allowed) {
+ num_subframes = 1;
+ pad = 0;
+ goto segment;
+ }
+
+ max_amsdu_len = sta->max_amsdu_len;
+ dbg_max_amsdu_len = ACCESS_ONCE(mvm->max_amsdu_len);
+
+ /* the Tx FIFO to which this A-MSDU will be routed */
+ txf = iwl_mvm_ac_to_tx_fifo[tid_to_mac80211_ac[tid]];
+
+ /*
+ * Don't send an AMSDU that will be longer than the TXF.
+ * Add a security margin of 256 for the TX command + headers.
+ * We also want to have the start of the next packet inside the
+ * fifo to be able to send bursts.
+ */
+ max_amsdu_len = min_t(unsigned int, max_amsdu_len,
+ mvm->shared_mem_cfg.txfifo_size[txf] - 256);
+
+ if (dbg_max_amsdu_len)
+ max_amsdu_len = min_t(unsigned int, max_amsdu_len,
+ dbg_max_amsdu_len);
+
+ /*
+ * Limit A-MSDU in A-MPDU to 4095 bytes when VHT is not
+ * supported. This is a spec requirement (IEEE 802.11-2015
+ * section 8.7.3 NOTE 3).
+ */
+ if (info->flags & IEEE80211_TX_CTL_AMPDU &&
+ !sta->vht_cap.vht_supported)
+ max_amsdu_len = min_t(unsigned int, max_amsdu_len, 4095);
+
+ /* Sub frame header + SNAP + IP header + TCP header + MSS */
+ subf_len = sizeof(struct ethhdr) + snap_ip_tcp + mss;
+ pad = (4 - subf_len) & 0x3;
+
+ /*
+ * If we have N subframes in the A-MSDU, then the A-MSDU's size is
+ * N * subf_len + (N - 1) * pad.
+ */
+ num_subframes = (max_amsdu_len + pad) / (subf_len + pad);
+ if (num_subframes > 1)
+ *qc |= IEEE80211_QOS_CTL_A_MSDU_PRESENT;
+
+ tcp_payload_len = skb_tail_pointer(skb) - skb_transport_header(skb) -
+ tcp_hdrlen(skb) + skb->data_len;
+
+ /*
+ * Make sure we have enough TBs for the A-MSDU:
+ * 2 for each subframe
+ * 1 more for each fragment
+ * 1 more for the potential data in the header
+ */
+ num_subframes =
+ min_t(unsigned int, num_subframes,
+ (mvm->trans->max_skb_frags - 1 -
+ skb_shinfo(skb)->nr_frags) / 2);
+
+ /* This skb fits in one single A-MSDU */
+ if (num_subframes * mss >= tcp_payload_len) {
+ /*
+ * Compute the length of all the data added for the A-MSDU.
+ * This will be used to compute the length to write in the TX
+ * command. We have: SNAP + IP + TCP for n -1 subframes and
+ * ETH header for n subframes. Note that the original skb
+ * already had one set of SNAP / IP / TCP headers.
+ */
+ num_subframes = DIV_ROUND_UP(tcp_payload_len, mss);
+ info = IEEE80211_SKB_CB(skb);
+ amsdu_add = num_subframes * sizeof(struct ethhdr) +
+ (num_subframes - 1) * (snap_ip_tcp + pad);
+ /* This holds the amsdu headers length */
+ info->driver_data[0] = (void *)(uintptr_t)amsdu_add;
+
+ __skb_queue_tail(mpdus_skb, skb);
+ return 0;
+ }
+
+ /*
+ * Trick the segmentation function to make it
+ * create SKBs that can fit into one A-MSDU.
+ */
+segment:
+ skb_shinfo(skb)->gso_size = num_subframes * mss;
+ memcpy(cb, skb->cb, sizeof(cb));
+
+ next = skb_gso_segment(skb, NETIF_F_CSUM_MASK | NETIF_F_SG);
+ skb_shinfo(skb)->gso_size = mss;
+ if (WARN_ON_ONCE(IS_ERR(next)))
return -EINVAL;
else if (next)
- consume_skb(skb_gso);
+ consume_skb(skb);
while (next) {
tmp = next;
next = tmp->next;
+
memcpy(tmp->cb, cb, sizeof(tmp->cb));
+ /*
+ * Compute the length of all the data added for the A-MSDU.
+ * This will be used to compute the length to write in the TX
+ * command. We have: SNAP + IP + TCP for n -1 subframes and
+ * ETH header for n subframes.
+ */
+ tcp_payload_len = skb_tail_pointer(tmp) -
+ skb_transport_header(tmp) -
+ tcp_hdrlen(tmp) + tmp->data_len;
+
+ if (ipv4)
+ ip_hdr(tmp)->id = htons(ip_base_id + i * num_subframes);
+
+ if (tcp_payload_len > mss) {
+ num_subframes = DIV_ROUND_UP(tcp_payload_len, mss);
+ info = IEEE80211_SKB_CB(tmp);
+ amsdu_add = num_subframes * sizeof(struct ethhdr) +
+ (num_subframes - 1) * (snap_ip_tcp + pad);
+ info->driver_data[0] = (void *)(uintptr_t)amsdu_add;
+ skb_shinfo(tmp)->gso_size = mss;
+ } else {
+ qc = ieee80211_get_qos_ctl((void *)tmp->data);
+
+ if (ipv4)
+ ip_send_check(ip_hdr(tmp));
+ *qc &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
+ skb_shinfo(tmp)->gso_size = 0;
+ }
tmp->prev = NULL;
tmp->next = NULL;
__skb_queue_tail(mpdus_skb, tmp);
+ i++;
}
return 0;
}
+#else /* CONFIG_INET */
+static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb,
+ struct ieee80211_sta *sta,
+ struct sk_buff_head *mpdus_skb)
+{
+ /* Impossible to get TSO with CONFIG_INET */
+ WARN_ON(1);
+
+ return -1;
+}
+#endif
/*
* Sets the fields in the Tx cmd that are crypto related
@@ -569,6 +736,7 @@
struct ieee80211_sta *sta)
{
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct sk_buff_head mpdus_skbs;
unsigned int payload_len;
int ret;
@@ -579,6 +747,9 @@
if (WARN_ON_ONCE(mvmsta->sta_id == IWL_MVM_STATION_COUNT))
return -1;
+ /* This holds the amsdu headers length */
+ info->driver_data[0] = (void *)(uintptr_t)0;
+
if (!skb_is_gso(skb))
return iwl_mvm_tx_mpdu(mvm, skb, sta);
@@ -598,7 +769,7 @@
return ret;
while (!skb_queue_empty(&mpdus_skbs)) {
- struct sk_buff *skb = __skb_dequeue(&mpdus_skbs);
+ skb = __skb_dequeue(&mpdus_skbs);
ret = iwl_mvm_tx_mpdu(mvm, skb, sta);
if (ret) {
@@ -793,6 +964,7 @@
struct sk_buff_head skbs;
u8 skb_freed = 0;
u16 next_reclaimed, seq_ctl;
+ bool is_ndp = false;
__skb_queue_head_init(&skbs);
@@ -846,6 +1018,20 @@
seq_ctl = le16_to_cpu(hdr->seq_ctrl);
}
+ if (unlikely(!seq_ctl)) {
+ struct ieee80211_hdr *hdr = (void *)skb->data;
+
+ /*
+ * If it is an NDP, we can't update next_reclaim since
+ * its sequence control is 0. Note that for that same
+ * reason, NDPs are never sent to A-MPDU'able queues
+ * so that we can never have more than one freed frame
+ * for a single Tx resonse (see WARN_ON below).
+ */
+ if (ieee80211_is_qos_nullfunc(hdr->frame_control))
+ is_ndp = true;
+ }
+
/*
* TODO: this is not accurate if we are freeing more than one
* packet.
@@ -909,9 +1095,16 @@
bool send_eosp_ndp = false;
spin_lock_bh(&mvmsta->lock);
- tid_data->next_reclaimed = next_reclaimed;
- IWL_DEBUG_TX_REPLY(mvm, "Next reclaimed packet:%d\n",
- next_reclaimed);
+ if (!is_ndp) {
+ tid_data->next_reclaimed = next_reclaimed;
+ IWL_DEBUG_TX_REPLY(mvm,
+ "Next reclaimed packet:%d\n",
+ next_reclaimed);
+ } else {
+ IWL_DEBUG_TX_REPLY(mvm,
+ "NDP - don't update next_reclaimed\n");
+ }
+
iwl_mvm_check_ratid_empty(mvm, sta, tid);
if (mvmsta->sleep_tx_count) {
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c
index 59453c1..53cdc57 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c
@@ -376,8 +376,8 @@
struct iwl_error_event_table {
u32 valid; /* (nonzero) valid, (0) log is empty */
u32 error_id; /* type of error */
- u32 pc; /* program counter */
- u32 blink1; /* branch link */
+ u32 trm_hw_status0; /* TRM HW status */
+ u32 trm_hw_status1; /* TRM HW status */
u32 blink2; /* branch link */
u32 ilink1; /* interrupt link */
u32 ilink2; /* interrupt link */
@@ -389,7 +389,7 @@
u32 tsf_hi; /* network timestamp function timer */
u32 gp1; /* GP1 timer register */
u32 gp2; /* GP2 timer register */
- u32 gp3; /* GP3 timer register */
+ u32 fw_rev_type; /* firmware revision type */
u32 major; /* uCode version major */
u32 minor; /* uCode version minor */
u32 hw_ver; /* HW Silicon version */
@@ -408,7 +408,7 @@
* time_flag */
u32 isr4; /* isr status register LMPM_NIC_ISR4:
* wico interrupt */
- u32 isr_pref; /* isr status register LMPM_NIC_PREF_STAT */
+ u32 last_cmd_id; /* last HCMD id handled by the firmware */
u32 wait_event; /* wait event() caller address */
u32 l2p_control; /* L2pControlField */
u32 l2p_duration; /* L2pDurationField */
@@ -419,7 +419,7 @@
u32 u_timestamp; /* indicate when the date and time of the
* compilation */
u32 flow_handler; /* FH read/write pointers, RX credit */
-} __packed /* LOG_ERROR_TABLE_API_S_VER_2 */;
+} __packed /* LOG_ERROR_TABLE_API_S_VER_3 */;
/*
* UMAC error struct - relevant starting from family 8000 chip.
@@ -529,9 +529,9 @@
trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low,
table.data1, table.data2, table.data3,
- table.blink1, table.blink2, table.ilink1,
- table.ilink2, table.bcon_time, table.gp1,
- table.gp2, table.gp3, table.ucode_ver, 0,
+ table.blink2, table.ilink1, table.ilink2,
+ table.bcon_time, table.gp1, table.gp2,
+ table.gp3, table.ucode_ver, 0,
table.hw_ver, table.brd_ver);
IWL_ERR(mvm, "0x%08X | %-28s\n", table.error_id,
desc_lookup(table.error_id));
@@ -615,14 +615,14 @@
trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low,
table.data1, table.data2, table.data3,
- table.blink1, table.blink2, table.ilink1,
+ table.blink2, table.ilink1,
table.ilink2, table.bcon_time, table.gp1,
- table.gp2, table.gp3, table.major,
+ table.gp2, table.fw_rev_type, table.major,
table.minor, table.hw_ver, table.brd_ver);
IWL_ERR(mvm, "0x%08X | %-28s\n", table.error_id,
desc_lookup(table.error_id));
- IWL_ERR(mvm, "0x%08X | uPc\n", table.pc);
- IWL_ERR(mvm, "0x%08X | branchlink1\n", table.blink1);
+ IWL_ERR(mvm, "0x%08X | trm_hw_status0\n", table.trm_hw_status0);
+ IWL_ERR(mvm, "0x%08X | trm_hw_status1\n", table.trm_hw_status1);
IWL_ERR(mvm, "0x%08X | branchlink2\n", table.blink2);
IWL_ERR(mvm, "0x%08X | interruptlink1\n", table.ilink1);
IWL_ERR(mvm, "0x%08X | interruptlink2\n", table.ilink2);
@@ -634,7 +634,7 @@
IWL_ERR(mvm, "0x%08X | tsf hi\n", table.tsf_hi);
IWL_ERR(mvm, "0x%08X | time gp1\n", table.gp1);
IWL_ERR(mvm, "0x%08X | time gp2\n", table.gp2);
- IWL_ERR(mvm, "0x%08X | time gp3\n", table.gp3);
+ IWL_ERR(mvm, "0x%08X | uCode revision type\n", table.fw_rev_type);
IWL_ERR(mvm, "0x%08X | uCode version major\n", table.major);
IWL_ERR(mvm, "0x%08X | uCode version minor\n", table.minor);
IWL_ERR(mvm, "0x%08X | hw version\n", table.hw_ver);
@@ -645,7 +645,7 @@
IWL_ERR(mvm, "0x%08X | isr2\n", table.isr2);
IWL_ERR(mvm, "0x%08X | isr3\n", table.isr3);
IWL_ERR(mvm, "0x%08X | isr4\n", table.isr4);
- IWL_ERR(mvm, "0x%08X | isr_pref\n", table.isr_pref);
+ IWL_ERR(mvm, "0x%08X | last cmd Id\n", table.last_cmd_id);
IWL_ERR(mvm, "0x%08X | wait_event\n", table.wait_event);
IWL_ERR(mvm, "0x%08X | l2p_control\n", table.l2p_control);
IWL_ERR(mvm, "0x%08X | l2p_duration\n", table.l2p_duration);
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c
index 753ec67..05b9685 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c
@@ -631,13 +631,31 @@
/* if RTPM is in use, enable it in our device */
if (iwl_trans->runtime_pm_mode != IWL_PLAT_PM_MODE_DISABLED) {
+ /* We explicitly set the device to active here to
+ * clear contingent errors.
+ */
pm_runtime_set_active(&pdev->dev);
+
pm_runtime_set_autosuspend_delay(&pdev->dev,
iwlwifi_mod_params.d0i3_entry_delay);
pm_runtime_use_autosuspend(&pdev->dev);
+
+ /* We are not supposed to call pm_runtime_allow() by
+ * ourselves, but let userspace enable runtime PM via
+ * sysfs. However, since we don't enable this from
+ * userspace yet, we need to allow/forbid() ourselves.
+ */
pm_runtime_allow(&pdev->dev);
}
+ /* The PCI device starts with a reference taken and we are
+ * supposed to release it here. But to simplify the
+ * interaction with the opmode, we don't do it now, but let
+ * the opmode release it when it's ready. To account for this
+ * reference, we start with ref_count set to 1.
+ */
+ trans_pcie->ref_count = 1;
+
return 0;
out_free_drv:
@@ -652,7 +670,17 @@
struct iwl_trans *trans = pci_get_drvdata(pdev);
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ /* if RTPM was in use, restore it to the state before probe */
+ if (trans->runtime_pm_mode != IWL_PLAT_PM_MODE_DISABLED) {
+ /* We should not call forbid here, but we do for now.
+ * Check the comment to pm_runtime_allow() in
+ * iwl_pci_probe().
+ */
+ pm_runtime_forbid(trans->dev);
+ }
+
iwl_drv_stop(trans_pcie->drv);
+
iwl_trans_pcie_free(trans);
}
@@ -811,6 +839,45 @@
return 0;
}
+
+static int iwl_pci_system_prepare(struct device *device)
+{
+ struct pci_dev *pdev = to_pci_dev(device);
+ struct iwl_trans *trans = pci_get_drvdata(pdev);
+
+ IWL_DEBUG_RPM(trans, "preparing for system suspend\n");
+
+ /* This is called before entering system suspend and before
+ * the runtime resume is called. Set the suspending flag to
+ * prevent the wakelock from being taken.
+ */
+ trans->suspending = true;
+
+ /* Wake the device up from runtime suspend before going to
+ * platform suspend. This is needed because we don't know
+ * whether wowlan any is set and, if it's not, mac80211 will
+ * disconnect (in which case, we can't be in D0i3).
+ */
+ pm_runtime_resume(device);
+
+ return 0;
+}
+
+static void iwl_pci_system_complete(struct device *device)
+{
+ struct pci_dev *pdev = to_pci_dev(device);
+ struct iwl_trans *trans = pci_get_drvdata(pdev);
+
+ IWL_DEBUG_RPM(trans, "completing system suspend\n");
+
+ /* This is called as a counterpart to the prepare op. It is
+ * called either when suspending fails or when suspend
+ * completed successfully. Now there's no risk of grabbing
+ * the wakelock anymore, so we can release the suspending
+ * flag.
+ */
+ trans->suspending = false;
+}
#endif /* CONFIG_IWLWIFI_PCIE_RTPM */
static const struct dev_pm_ops iwl_dev_pm_ops = {
@@ -820,6 +887,8 @@
SET_RUNTIME_PM_OPS(iwl_pci_runtime_suspend,
iwl_pci_runtime_resume,
NULL)
+ .prepare = iwl_pci_system_prepare,
+ .complete = iwl_pci_system_complete,
#endif /* CONFIG_IWLWIFI_PCIE_RTPM */
};
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h
index 542bbc5..dadafbd 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h
@@ -336,10 +336,18 @@
* @fw_mon_phys: physical address of the buffer for the firmware monitor
* @fw_mon_page: points to the first page of the buffer for the firmware monitor
* @fw_mon_size: size of the buffer for the firmware monitor
+ * @msix_entries: array of MSI-X entries
+ * @msix_enabled: true if managed to enable MSI-X
+ * @allocated_vector: the number of interrupt vector allocated by the OS
+ * @default_irq_num: default irq for non rx interrupt
+ * @fh_init_mask: initial unmasked fh causes
+ * @hw_init_mask: initial unmasked hw causes
+ * @fh_mask: current unmasked fh causes
+ * @hw_mask: current unmasked hw causes
*/
struct iwl_trans_pcie {
struct iwl_rxq *rxq;
- struct iwl_rx_mem_buffer rx_pool[MQ_RX_POOL_SIZE];
+ struct iwl_rx_mem_buffer rx_pool[RX_POOL_SIZE];
struct iwl_rx_mem_buffer *global_table[MQ_RX_TABLE_SIZE];
struct iwl_rb_allocator rba;
struct iwl_trans *trans;
@@ -402,6 +410,15 @@
dma_addr_t fw_mon_phys;
struct page *fw_mon_page;
u32 fw_mon_size;
+
+ struct msix_entry msix_entries[IWL_MAX_RX_HW_QUEUES];
+ bool msix_enabled;
+ u32 allocated_vector;
+ u32 default_irq_num;
+ u32 fh_init_mask;
+ u32 hw_init_mask;
+ u32 fh_mask;
+ u32 hw_mask;
};
static inline struct iwl_trans_pcie *
@@ -430,7 +447,10 @@
* RX
******************************************************/
int iwl_pcie_rx_init(struct iwl_trans *trans);
+irqreturn_t iwl_pcie_msix_isr(int irq, void *data);
irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id);
+irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id);
+irqreturn_t iwl_pcie_irq_rx_msix_handler(int irq, void *dev_id);
int iwl_pcie_rx_stop(struct iwl_trans *trans);
void iwl_pcie_rx_free(struct iwl_trans *trans);
@@ -485,15 +505,24 @@
******************************************************/
static inline void iwl_disable_interrupts(struct iwl_trans *trans)
{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
clear_bit(STATUS_INT_ENABLED, &trans->status);
+ if (!trans_pcie->msix_enabled) {
+ /* disable interrupts from uCode/NIC to host */
+ iwl_write32(trans, CSR_INT_MASK, 0x00000000);
- /* disable interrupts from uCode/NIC to host */
- iwl_write32(trans, CSR_INT_MASK, 0x00000000);
-
- /* acknowledge/clear/reset any interrupts still pending
- * from uCode or flow handler (Rx/Tx DMA) */
- iwl_write32(trans, CSR_INT, 0xffffffff);
- iwl_write32(trans, CSR_FH_INT_STATUS, 0xffffffff);
+ /* acknowledge/clear/reset any interrupts still pending
+ * from uCode or flow handler (Rx/Tx DMA) */
+ iwl_write32(trans, CSR_INT, 0xffffffff);
+ iwl_write32(trans, CSR_FH_INT_STATUS, 0xffffffff);
+ } else {
+ /* disable all the interrupt we might use */
+ iwl_write32(trans, CSR_MSIX_FH_INT_MASK_AD,
+ trans_pcie->fh_init_mask);
+ iwl_write32(trans, CSR_MSIX_HW_INT_MASK_AD,
+ trans_pcie->hw_init_mask);
+ }
IWL_DEBUG_ISR(trans, "Disabled interrupts\n");
}
@@ -503,8 +532,37 @@
IWL_DEBUG_ISR(trans, "Enabling interrupts\n");
set_bit(STATUS_INT_ENABLED, &trans->status);
- trans_pcie->inta_mask = CSR_INI_SET_MASK;
- iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask);
+ if (!trans_pcie->msix_enabled) {
+ trans_pcie->inta_mask = CSR_INI_SET_MASK;
+ iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask);
+ } else {
+ /*
+ * fh/hw_mask keeps all the unmasked causes.
+ * Unlike msi, in msix cause is enabled when it is unset.
+ */
+ trans_pcie->hw_mask = trans_pcie->hw_init_mask;
+ trans_pcie->fh_mask = trans_pcie->fh_init_mask;
+ iwl_write32(trans, CSR_MSIX_FH_INT_MASK_AD,
+ ~trans_pcie->fh_mask);
+ iwl_write32(trans, CSR_MSIX_HW_INT_MASK_AD,
+ ~trans_pcie->hw_mask);
+ }
+}
+
+static inline void iwl_enable_hw_int_msk_msix(struct iwl_trans *trans, u32 msk)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ iwl_write32(trans, CSR_MSIX_HW_INT_MASK_AD, ~msk);
+ trans_pcie->hw_mask = msk;
+}
+
+static inline void iwl_enable_fh_int_msk_msix(struct iwl_trans *trans, u32 msk)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ iwl_write32(trans, CSR_MSIX_FH_INT_MASK_AD, ~msk);
+ trans_pcie->fh_mask = msk;
}
static inline void iwl_enable_fw_load_int(struct iwl_trans *trans)
@@ -512,8 +570,15 @@
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
IWL_DEBUG_ISR(trans, "Enabling FW load interrupt\n");
- trans_pcie->inta_mask = CSR_INT_BIT_FH_TX;
- iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask);
+ if (!trans_pcie->msix_enabled) {
+ trans_pcie->inta_mask = CSR_INT_BIT_FH_TX;
+ iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask);
+ } else {
+ iwl_write32(trans, CSR_MSIX_HW_INT_MASK_AD,
+ trans_pcie->hw_init_mask);
+ iwl_enable_fh_int_msk_msix(trans,
+ MSIX_FH_INT_CAUSES_D2S_CH0_NUM);
+ }
}
static inline void iwl_enable_rfkill_int(struct iwl_trans *trans)
@@ -521,8 +586,15 @@
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
IWL_DEBUG_ISR(trans, "Enabling rfkill interrupt\n");
- trans_pcie->inta_mask = CSR_INT_BIT_RF_KILL;
- iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask);
+ if (!trans_pcie->msix_enabled) {
+ trans_pcie->inta_mask = CSR_INT_BIT_RF_KILL;
+ iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask);
+ } else {
+ iwl_write32(trans, CSR_MSIX_FH_INT_MASK_AD,
+ trans_pcie->fh_init_mask);
+ iwl_enable_hw_int_msk_msix(trans,
+ MSIX_HW_INT_CAUSES_REG_RF_KILL);
+ }
}
static inline void iwl_wake_queue(struct iwl_trans *trans,
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c
index 07973ef..4be3c35 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c
@@ -231,6 +231,9 @@
}
}
+/*
+ * iwl_pcie_rxq_mq_restock - restock implementation for multi-queue rx
+ */
static void iwl_pcie_rxq_mq_restock(struct iwl_trans *trans,
struct iwl_rxq *rxq)
{
@@ -277,17 +280,10 @@
}
/*
- * iwl_pcie_rxq_restock - refill RX queue from pre-allocated pool
- *
- * If there are slots in the RX queue that need to be restocked,
- * and we have free pre-allocated buffers, fill the ranks as much
- * as we can, pulling from rx_free.
- *
- * This moves the 'write' index forward to catch up with 'processed', and
- * also updates the memory address in the firmware to reference the new
- * target buffer.
+ * iwl_pcie_rxq_sq_restock - restock implementation for single queue rx
*/
-static void iwl_pcie_rxq_restock(struct iwl_trans *trans, struct iwl_rxq *rxq)
+static void iwl_pcie_rxq_sq_restock(struct iwl_trans *trans,
+ struct iwl_rxq *rxq)
{
struct iwl_rx_mem_buffer *rxb;
@@ -332,6 +328,26 @@
}
/*
+ * iwl_pcie_rxq_restock - refill RX queue from pre-allocated pool
+ *
+ * If there are slots in the RX queue that need to be restocked,
+ * and we have free pre-allocated buffers, fill the ranks as much
+ * as we can, pulling from rx_free.
+ *
+ * This moves the 'write' index forward to catch up with 'processed', and
+ * also updates the memory address in the firmware to reference the new
+ * target buffer.
+ */
+static
+void iwl_pcie_rxq_restock(struct iwl_trans *trans, struct iwl_rxq *rxq)
+{
+ if (trans->cfg->mq_rx_supported)
+ iwl_pcie_rxq_mq_restock(trans, rxq);
+ else
+ iwl_pcie_rxq_sq_restock(trans, rxq);
+}
+
+/*
* iwl_pcie_rx_alloc_page - allocates and returns a page.
*
*/
@@ -434,7 +450,7 @@
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
int i;
- for (i = 0; i < MQ_RX_POOL_SIZE; i++) {
+ for (i = 0; i < RX_POOL_SIZE; i++) {
if (!trans_pcie->rx_pool[i].page)
continue;
dma_unmap_page(trans->dev, trans_pcie->rx_pool[i].page_dma,
@@ -539,40 +555,46 @@
}
/*
- * iwl_pcie_rx_allocator_get - Returns the pre-allocated pages
+ * iwl_pcie_rx_allocator_get - returns the pre-allocated pages
.*
.* Called by queue when the queue posted allocation request and
* has freed 8 RBDs in order to restock itself.
+ * This function directly moves the allocated RBs to the queue's ownership
+ * and updates the relevant counters.
*/
-static int iwl_pcie_rx_allocator_get(struct iwl_trans *trans,
- struct iwl_rx_mem_buffer
- *out[RX_CLAIM_REQ_ALLOC])
+static void iwl_pcie_rx_allocator_get(struct iwl_trans *trans,
+ struct iwl_rxq *rxq)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
struct iwl_rb_allocator *rba = &trans_pcie->rba;
int i;
+ lockdep_assert_held(&rxq->lock);
+
/*
* atomic_dec_if_positive returns req_ready - 1 for any scenario.
* If req_ready is 0 atomic_dec_if_positive will return -1 and this
- * function will return -ENOMEM, as there are no ready requests.
+ * function will return early, as there are no ready requests.
* atomic_dec_if_positive will perofrm the *actual* decrement only if
* req_ready > 0, i.e. - there are ready requests and the function
* hands one request to the caller.
*/
if (atomic_dec_if_positive(&rba->req_ready) < 0)
- return -ENOMEM;
+ return;
spin_lock(&rba->lock);
for (i = 0; i < RX_CLAIM_REQ_ALLOC; i++) {
/* Get next free Rx buffer, remove it from free list */
- out[i] = list_first_entry(&rba->rbd_allocated,
- struct iwl_rx_mem_buffer, list);
- list_del(&out[i]->list);
+ struct iwl_rx_mem_buffer *rxb =
+ list_first_entry(&rba->rbd_allocated,
+ struct iwl_rx_mem_buffer, list);
+
+ list_move(&rxb->list, &rxq->rx_free);
}
spin_unlock(&rba->lock);
- return 0;
+ rxq->used_count -= RX_CLAIM_REQ_ALLOC;
+ rxq->free_count += RX_CLAIM_REQ_ALLOC;
}
static void iwl_pcie_rx_allocator_work(struct work_struct *data)
@@ -783,16 +805,25 @@
* Single frame mode
* Rx buffer size 4 or 8k or 12k
* Min RB size 4 or 8
+ * Drop frames that exceed RB size
* 512 RBDs
*/
iwl_write_prph(trans, RFH_RXF_DMA_CFG,
RFH_DMA_EN_ENABLE_VAL |
rb_size | RFH_RXF_DMA_SINGLE_FRAME_MASK |
RFH_RXF_DMA_MIN_RB_4_8 |
+ RFH_RXF_DMA_DROP_TOO_LARGE_MASK |
RFH_RXF_DMA_RBDCB_SIZE_512);
+ /*
+ * Activate DMA snooping.
+ * Set RX DMA chunk size to 64B
+ * Default queue is 0
+ */
iwl_write_prph(trans, RFH_GEN_CFG, RFH_GEN_CFG_RFH_DMA_SNOOP |
- RFH_GEN_CFG_SERVICE_DMA_SNOOP);
+ (DEFAULT_RXQ_NUM << RFH_GEN_CFG_DEFAULT_RXQ_NUM_POS) |
+ RFH_GEN_CFG_SERVICE_DMA_SNOOP);
+ /* Enable the relevant rx queues */
iwl_write_prph(trans, RFH_RXF_RXQ_ACTIVE, enabled);
/* Set interrupt coalescing timer to default (2048 usecs) */
@@ -820,7 +851,7 @@
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
struct iwl_rxq *def_rxq;
struct iwl_rb_allocator *rba = &trans_pcie->rba;
- int i, err, num_rbds, allocator_pool_size;
+ int i, err, queue_size, allocator_pool_size, num_alloc;
if (!trans_pcie->rxq) {
err = iwl_pcie_rx_alloc(trans);
@@ -872,11 +903,12 @@
}
/* move the pool to the default queue and allocator ownerships */
- num_rbds = trans->cfg->mq_rx_supported ?
- MQ_RX_POOL_SIZE : RX_QUEUE_SIZE;
+ queue_size = trans->cfg->mq_rx_supported ?
+ MQ_RX_NUM_RBDS : RX_QUEUE_SIZE;
allocator_pool_size = trans->num_rx_queues *
(RX_CLAIM_REQ_ALLOC - RX_POST_REQ_ALLOC);
- for (i = 0; i < num_rbds; i++) {
+ num_alloc = queue_size + allocator_pool_size;
+ for (i = 0; i < num_alloc; i++) {
struct iwl_rx_mem_buffer *rxb = &trans_pcie->rx_pool[i];
if (i < allocator_pool_size)
@@ -891,7 +923,7 @@
if (trans->cfg->mq_rx_supported) {
iwl_pcie_rx_mq_hw_init(trans);
} else {
- iwl_pcie_rxq_restock(trans, def_rxq);
+ iwl_pcie_rxq_sq_restock(trans, def_rxq);
iwl_pcie_rx_hw_init(trans, def_rxq);
}
@@ -1135,11 +1167,11 @@
/*
* iwl_pcie_rx_handle - Main entry function for receiving responses from fw
*/
-static void iwl_pcie_rx_handle(struct iwl_trans *trans)
+static void iwl_pcie_rx_handle(struct iwl_trans *trans, int queue)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwl_rxq *rxq = &trans_pcie->rxq[0];
- u32 r, i, j, count = 0;
+ struct iwl_rxq *rxq = &trans_pcie->rxq[queue];
+ u32 r, i, count = 0;
bool emergency = false;
restart:
@@ -1149,9 +1181,12 @@
r = le16_to_cpu(ACCESS_ONCE(rxq->rb_stts->closed_rb_num)) & 0x0FFF;
i = rxq->read;
+ /* W/A 9000 device step A0 wrap-around bug */
+ r &= (rxq->queue_size - 1);
+
/* Rx interrupt, but nothing sent from uCode */
if (i == r)
- IWL_DEBUG_RX(trans, "HW = SW = %d\n", r);
+ IWL_DEBUG_RX(trans, "Q %d: HW = SW = %d\n", rxq->id, r);
while (i != r) {
struct iwl_rx_mem_buffer *rxb;
@@ -1164,78 +1199,55 @@
* used_bd is a 32 bit but only 12 are used to retrieve
* the vid
*/
- u16 vid = (u16)le32_to_cpu(rxq->used_bd[i]);
+ u16 vid = le32_to_cpu(rxq->used_bd[i]) & 0x0FFF;
+ if (WARN(vid >= ARRAY_SIZE(trans_pcie->global_table),
+ "Invalid rxb index from HW %u\n", (u32)vid))
+ goto out;
rxb = trans_pcie->global_table[vid];
} else {
rxb = rxq->queue[i];
rxq->queue[i] = NULL;
}
- IWL_DEBUG_RX(trans, "rxbuf: HW = %d, SW = %d\n", r, i);
+ IWL_DEBUG_RX(trans, "Q %d: HW = %d, SW = %d\n", rxq->id, r, i);
iwl_pcie_rx_handle_rb(trans, rxq, rxb, emergency);
i = (i + 1) & (rxq->queue_size - 1);
- /* If we have RX_CLAIM_REQ_ALLOC released rx buffers -
- * try to claim the pre-allocated buffers from the allocator */
- if (rxq->used_count >= RX_CLAIM_REQ_ALLOC) {
+ /*
+ * If we have RX_CLAIM_REQ_ALLOC released rx buffers -
+ * try to claim the pre-allocated buffers from the allocator.
+ * If not ready - will try to reclaim next time.
+ * There is no need to reschedule work - allocator exits only
+ * on success
+ */
+ if (rxq->used_count >= RX_CLAIM_REQ_ALLOC)
+ iwl_pcie_rx_allocator_get(trans, rxq);
+
+ if (rxq->used_count % RX_CLAIM_REQ_ALLOC == 0 && !emergency) {
struct iwl_rb_allocator *rba = &trans_pcie->rba;
- struct iwl_rx_mem_buffer *out[RX_CLAIM_REQ_ALLOC];
- if (rxq->used_count % RX_CLAIM_REQ_ALLOC == 0 &&
- !emergency) {
- /* Add the remaining 6 empty RBDs
- * for allocator use
- */
- spin_lock(&rba->lock);
- list_splice_tail_init(&rxq->rx_used,
- &rba->rbd_empty);
- spin_unlock(&rba->lock);
- }
-
- /* If not ready - continue, will try to reclaim later.
- * No need to reschedule work - allocator exits only on
- * success */
- if (!iwl_pcie_rx_allocator_get(trans, out)) {
- /* If success - then RX_CLAIM_REQ_ALLOC
- * buffers were retrieved and should be added
- * to free list */
- rxq->used_count -= RX_CLAIM_REQ_ALLOC;
- for (j = 0; j < RX_CLAIM_REQ_ALLOC; j++) {
- list_add_tail(&out[j]->list,
- &rxq->rx_free);
- rxq->free_count++;
- }
- }
- }
- if (emergency) {
+ /* Add the remaining empty RBDs for allocator use */
+ spin_lock(&rba->lock);
+ list_splice_tail_init(&rxq->rx_used, &rba->rbd_empty);
+ spin_unlock(&rba->lock);
+ } else if (emergency) {
count++;
if (count == 8) {
count = 0;
if (rxq->used_count < rxq->queue_size / 3)
emergency = false;
+
+ rxq->read = i;
spin_unlock(&rxq->lock);
iwl_pcie_rxq_alloc_rbs(trans, GFP_ATOMIC, rxq);
- spin_lock(&rxq->lock);
+ iwl_pcie_rxq_restock(trans, rxq);
+ goto restart;
}
}
- /* handle restock for three cases, can be all of them at once:
- * - we just pulled buffers from the allocator
- * - we have 8+ unstolen pages accumulated
- * - we are in emergency and allocated buffers
- */
- if (rxq->free_count >= RX_CLAIM_REQ_ALLOC) {
- rxq->read = i;
- spin_unlock(&rxq->lock);
- if (trans->cfg->mq_rx_supported)
- iwl_pcie_rxq_mq_restock(trans, rxq);
- else
- iwl_pcie_rxq_restock(trans, rxq);
- goto restart;
- }
}
-
+out:
/* Backtrack one entry */
rxq->read = i;
spin_unlock(&rxq->lock);
@@ -1257,6 +1269,56 @@
if (rxq->napi.poll)
napi_gro_flush(&rxq->napi, false);
+
+ iwl_pcie_rxq_restock(trans, rxq);
+}
+
+static struct iwl_trans_pcie *iwl_pcie_get_trans_pcie(struct msix_entry *entry)
+{
+ u8 queue = entry->entry;
+ struct msix_entry *entries = entry - queue;
+
+ return container_of(entries, struct iwl_trans_pcie, msix_entries[0]);
+}
+
+static inline void iwl_pcie_clear_irq(struct iwl_trans *trans,
+ struct msix_entry *entry)
+{
+ /*
+ * Before sending the interrupt the HW disables it to prevent
+ * a nested interrupt. This is done by writing 1 to the corresponding
+ * bit in the mask register. After handling the interrupt, it should be
+ * re-enabled by clearing this bit. This register is defined as
+ * write 1 clear (W1C) register, meaning that it's being clear
+ * by writing 1 to the bit.
+ */
+ iwl_write_direct32(trans, CSR_MSIX_AUTOMASK_ST_AD, BIT(entry->entry));
+}
+
+/*
+ * iwl_pcie_rx_msix_handle - Main entry function for receiving responses from fw
+ * This interrupt handler should be used with RSS queue only.
+ */
+irqreturn_t iwl_pcie_irq_rx_msix_handler(int irq, void *dev_id)
+{
+ struct msix_entry *entry = dev_id;
+ struct iwl_trans_pcie *trans_pcie = iwl_pcie_get_trans_pcie(entry);
+ struct iwl_trans *trans = trans_pcie->trans;
+
+ if (WARN_ON(entry->entry >= trans->num_rx_queues))
+ return IRQ_NONE;
+
+ lock_map_acquire(&trans->sync_cmd_lockdep_map);
+
+ local_bh_disable();
+ iwl_pcie_rx_handle(trans, entry->entry);
+ local_bh_enable();
+
+ iwl_pcie_clear_irq(trans, entry);
+
+ lock_map_release(&trans->sync_cmd_lockdep_map);
+
+ return IRQ_HANDLED;
}
/*
@@ -1589,7 +1651,7 @@
isr_stats->rx++;
local_bh_disable();
- iwl_pcie_rx_handle(trans);
+ iwl_pcie_rx_handle(trans, 0);
local_bh_enable();
}
@@ -1732,3 +1794,129 @@
return IRQ_WAKE_THREAD;
}
+
+irqreturn_t iwl_pcie_msix_isr(int irq, void *data)
+{
+ return IRQ_WAKE_THREAD;
+}
+
+irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id)
+{
+ struct msix_entry *entry = dev_id;
+ struct iwl_trans_pcie *trans_pcie = iwl_pcie_get_trans_pcie(entry);
+ struct iwl_trans *trans = trans_pcie->trans;
+ struct isr_statistics *isr_stats = isr_stats = &trans_pcie->isr_stats;
+ u32 inta_fh, inta_hw;
+
+ lock_map_acquire(&trans->sync_cmd_lockdep_map);
+
+ spin_lock(&trans_pcie->irq_lock);
+ inta_fh = iwl_read_direct32(trans, CSR_MSIX_FH_INT_CAUSES_AD);
+ inta_hw = iwl_read_direct32(trans, CSR_MSIX_HW_INT_CAUSES_AD);
+ /*
+ * Clear causes registers to avoid being handling the same cause.
+ */
+ iwl_write_direct32(trans, CSR_MSIX_FH_INT_CAUSES_AD, inta_fh);
+ iwl_write_direct32(trans, CSR_MSIX_HW_INT_CAUSES_AD, inta_hw);
+ spin_unlock(&trans_pcie->irq_lock);
+
+ if (unlikely(!(inta_fh | inta_hw))) {
+ IWL_DEBUG_ISR(trans, "Ignore interrupt, inta == 0\n");
+ lock_map_release(&trans->sync_cmd_lockdep_map);
+ return IRQ_NONE;
+ }
+
+ if (iwl_have_debug_level(IWL_DL_ISR))
+ IWL_DEBUG_ISR(trans, "ISR inta_fh 0x%08x, enabled 0x%08x\n",
+ inta_fh,
+ iwl_read32(trans, CSR_MSIX_FH_INT_MASK_AD));
+
+ /* This "Tx" DMA channel is used only for loading uCode */
+ if (inta_fh & MSIX_FH_INT_CAUSES_D2S_CH0_NUM) {
+ IWL_DEBUG_ISR(trans, "uCode load interrupt\n");
+ isr_stats->tx++;
+ /*
+ * Wake up uCode load routine,
+ * now that load is complete
+ */
+ trans_pcie->ucode_write_complete = true;
+ wake_up(&trans_pcie->ucode_write_waitq);
+ }
+
+ /* Error detected by uCode */
+ if ((inta_fh & MSIX_FH_INT_CAUSES_FH_ERR) ||
+ (inta_hw & MSIX_HW_INT_CAUSES_REG_SW_ERR)) {
+ IWL_ERR(trans,
+ "Microcode SW error detected. Restarting 0x%X.\n",
+ inta_fh);
+ isr_stats->sw++;
+ iwl_pcie_irq_handle_error(trans);
+ }
+
+ /* After checking FH register check HW register */
+ if (iwl_have_debug_level(IWL_DL_ISR))
+ IWL_DEBUG_ISR(trans,
+ "ISR inta_hw 0x%08x, enabled 0x%08x\n",
+ inta_hw,
+ iwl_read32(trans, CSR_MSIX_HW_INT_MASK_AD));
+
+ /* Alive notification via Rx interrupt will do the real work */
+ if (inta_hw & MSIX_HW_INT_CAUSES_REG_ALIVE) {
+ IWL_DEBUG_ISR(trans, "Alive interrupt\n");
+ isr_stats->alive++;
+ }
+
+ /* uCode wakes up after power-down sleep */
+ if (inta_hw & MSIX_HW_INT_CAUSES_REG_WAKEUP) {
+ IWL_DEBUG_ISR(trans, "Wakeup interrupt\n");
+ iwl_pcie_rxq_check_wrptr(trans);
+ iwl_pcie_txq_check_wrptrs(trans);
+
+ isr_stats->wakeup++;
+ }
+
+ /* Chip got too hot and stopped itself */
+ if (inta_hw & MSIX_HW_INT_CAUSES_REG_CT_KILL) {
+ IWL_ERR(trans, "Microcode CT kill error detected.\n");
+ isr_stats->ctkill++;
+ }
+
+ /* HW RF KILL switch toggled */
+ if (inta_hw & MSIX_HW_INT_CAUSES_REG_RF_KILL) {
+ bool hw_rfkill;
+
+ hw_rfkill = iwl_is_rfkill_set(trans);
+ IWL_WARN(trans, "RF_KILL bit toggled to %s.\n",
+ hw_rfkill ? "disable radio" : "enable radio");
+
+ isr_stats->rfkill++;
+
+ mutex_lock(&trans_pcie->mutex);
+ iwl_trans_pcie_rf_kill(trans, hw_rfkill);
+ mutex_unlock(&trans_pcie->mutex);
+ if (hw_rfkill) {
+ set_bit(STATUS_RFKILL, &trans->status);
+ if (test_and_clear_bit(STATUS_SYNC_HCMD_ACTIVE,
+ &trans->status))
+ IWL_DEBUG_RF_KILL(trans,
+ "Rfkill while SYNC HCMD in flight\n");
+ wake_up(&trans_pcie->wait_command_queue);
+ } else {
+ clear_bit(STATUS_RFKILL, &trans->status);
+ }
+ }
+
+ if (inta_hw & MSIX_HW_INT_CAUSES_REG_HW_ERR) {
+ IWL_ERR(trans,
+ "Hardware error detected. Restarting.\n");
+
+ isr_stats->hw++;
+ iwl_pcie_irq_handle_error(trans);
+ }
+
+ iwl_pcie_clear_irq(trans, entry);
+
+ lock_map_release(&trans->sync_cmd_lockdep_map);
+
+ return IRQ_HANDLED;
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c
index 58591ca..eb39c7e 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c
@@ -616,38 +616,38 @@
dma_addr_t phy_addr, u32 byte_cnt)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ unsigned long flags;
int ret;
trans_pcie->ucode_write_complete = false;
- iwl_write_direct32(trans,
- FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL),
- FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE);
+ if (!iwl_trans_grab_nic_access(trans, &flags))
+ return -EIO;
- iwl_write_direct32(trans,
- FH_SRVC_CHNL_SRAM_ADDR_REG(FH_SRVC_CHNL),
- dst_addr);
+ iwl_write32(trans, FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL),
+ FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE);
- iwl_write_direct32(trans,
- FH_TFDIB_CTRL0_REG(FH_SRVC_CHNL),
- phy_addr & FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK);
+ iwl_write32(trans, FH_SRVC_CHNL_SRAM_ADDR_REG(FH_SRVC_CHNL),
+ dst_addr);
- iwl_write_direct32(trans,
- FH_TFDIB_CTRL1_REG(FH_SRVC_CHNL),
- (iwl_get_dma_hi_addr(phy_addr)
- << FH_MEM_TFDIB_REG1_ADDR_BITSHIFT) | byte_cnt);
+ iwl_write32(trans, FH_TFDIB_CTRL0_REG(FH_SRVC_CHNL),
+ phy_addr & FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK);
- iwl_write_direct32(trans,
- FH_TCSR_CHNL_TX_BUF_STS_REG(FH_SRVC_CHNL),
- 1 << FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM |
- 1 << FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX |
- FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID);
+ iwl_write32(trans, FH_TFDIB_CTRL1_REG(FH_SRVC_CHNL),
+ (iwl_get_dma_hi_addr(phy_addr)
+ << FH_MEM_TFDIB_REG1_ADDR_BITSHIFT) | byte_cnt);
- iwl_write_direct32(trans,
- FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL),
- FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE |
- FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE |
- FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD);
+ iwl_write32(trans, FH_TCSR_CHNL_TX_BUF_STS_REG(FH_SRVC_CHNL),
+ BIT(FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM) |
+ BIT(FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX) |
+ FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID);
+
+ iwl_write32(trans, FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL),
+ FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE |
+ FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE |
+ FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD);
+
+ iwl_trans_release_nic_access(trans, &flags);
ret = wait_event_timeout(trans_pcie->ucode_write_waitq,
trans_pcie->ucode_write_complete, 5 * HZ);
@@ -1123,6 +1123,20 @@
iwl_pcie_prepare_card_hw(trans);
}
+static void iwl_pcie_synchronize_irqs(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ if (trans_pcie->msix_enabled) {
+ int i;
+
+ for (i = 0; i < trans_pcie->allocated_vector; i++)
+ synchronize_irq(trans_pcie->msix_entries[i].vector);
+ } else {
+ synchronize_irq(trans_pcie->pci_dev->irq);
+ }
+}
+
static int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
const struct fw_img *fw, bool run_in_rfkill)
{
@@ -1149,7 +1163,7 @@
iwl_disable_interrupts(trans);
/* Make sure it finished running */
- synchronize_irq(trans_pcie->pci_dev->irq);
+ iwl_pcie_synchronize_irqs(trans);
mutex_lock(&trans_pcie->mutex);
@@ -1252,8 +1266,6 @@
static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test,
bool reset)
{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-
if (!reset) {
/* Enable persistence mode to avoid reset */
iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG,
@@ -1271,7 +1283,7 @@
iwl_pcie_disable_ict(trans);
- synchronize_irq(trans_pcie->pci_dev->irq);
+ iwl_pcie_synchronize_irqs(trans);
iwl_clear_bit(trans, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
@@ -1350,6 +1362,153 @@
return 0;
}
+struct iwl_causes_list {
+ u32 cause_num;
+ u32 mask_reg;
+ u8 addr;
+};
+
+static struct iwl_causes_list causes_list[] = {
+ {MSIX_FH_INT_CAUSES_D2S_CH0_NUM, CSR_MSIX_FH_INT_MASK_AD, 0},
+ {MSIX_FH_INT_CAUSES_D2S_CH1_NUM, CSR_MSIX_FH_INT_MASK_AD, 0x1},
+ {MSIX_FH_INT_CAUSES_S2D, CSR_MSIX_FH_INT_MASK_AD, 0x3},
+ {MSIX_FH_INT_CAUSES_FH_ERR, CSR_MSIX_FH_INT_MASK_AD, 0x5},
+ {MSIX_HW_INT_CAUSES_REG_ALIVE, CSR_MSIX_HW_INT_MASK_AD, 0x10},
+ {MSIX_HW_INT_CAUSES_REG_WAKEUP, CSR_MSIX_HW_INT_MASK_AD, 0x11},
+ {MSIX_HW_INT_CAUSES_REG_CT_KILL, CSR_MSIX_HW_INT_MASK_AD, 0x16},
+ {MSIX_HW_INT_CAUSES_REG_RF_KILL, CSR_MSIX_HW_INT_MASK_AD, 0x17},
+ {MSIX_HW_INT_CAUSES_REG_PERIODIC, CSR_MSIX_HW_INT_MASK_AD, 0x18},
+ {MSIX_HW_INT_CAUSES_REG_SW_ERR, CSR_MSIX_HW_INT_MASK_AD, 0x29},
+ {MSIX_HW_INT_CAUSES_REG_SCD, CSR_MSIX_HW_INT_MASK_AD, 0x2A},
+ {MSIX_HW_INT_CAUSES_REG_FH_TX, CSR_MSIX_HW_INT_MASK_AD, 0x2B},
+ {MSIX_HW_INT_CAUSES_REG_HW_ERR, CSR_MSIX_HW_INT_MASK_AD, 0x2D},
+ {MSIX_HW_INT_CAUSES_REG_HAP, CSR_MSIX_HW_INT_MASK_AD, 0x2E},
+};
+
+static void iwl_pcie_init_msix(struct iwl_trans_pcie *trans_pcie)
+{
+ u32 val, max_rx_vector, i;
+ struct iwl_trans *trans = trans_pcie->trans;
+
+ max_rx_vector = trans_pcie->allocated_vector - 1;
+
+ if (!trans_pcie->msix_enabled)
+ return;
+
+ iwl_write_prph(trans, UREG_CHICK, UREG_CHICK_MSIX_ENABLE);
+
+ /*
+ * Each cause from the list above and the RX causes is represented as
+ * a byte in the IVAR table. We access the first (N - 1) bytes and map
+ * them to the (N - 1) vectors so these vectors will be used as rx
+ * vectors. Then access all non rx causes and map them to the
+ * default queue (N'th queue).
+ */
+ for (i = 0; i < max_rx_vector; i++) {
+ iwl_write8(trans, CSR_MSIX_RX_IVAR(i), MSIX_FH_INT_CAUSES_Q(i));
+ iwl_clear_bit(trans, CSR_MSIX_FH_INT_MASK_AD,
+ BIT(MSIX_FH_INT_CAUSES_Q(i)));
+ }
+
+ for (i = 0; i < ARRAY_SIZE(causes_list); i++) {
+ val = trans_pcie->default_irq_num |
+ MSIX_NON_AUTO_CLEAR_CAUSE;
+ iwl_write8(trans, CSR_MSIX_IVAR(causes_list[i].addr), val);
+ iwl_clear_bit(trans, causes_list[i].mask_reg,
+ causes_list[i].cause_num);
+ }
+ trans_pcie->fh_init_mask =
+ ~iwl_read32(trans, CSR_MSIX_FH_INT_MASK_AD);
+ trans_pcie->fh_mask = trans_pcie->fh_init_mask;
+ trans_pcie->hw_init_mask =
+ ~iwl_read32(trans, CSR_MSIX_HW_INT_MASK_AD);
+ trans_pcie->hw_mask = trans_pcie->hw_init_mask;
+}
+
+static void iwl_pcie_set_interrupt_capa(struct pci_dev *pdev,
+ struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ u16 pci_cmd;
+ int max_vector;
+ int ret, i;
+
+ if (trans->cfg->mq_rx_supported) {
+ max_vector = min_t(u32, (num_possible_cpus() + 1),
+ IWL_MAX_RX_HW_QUEUES);
+ for (i = 0; i < max_vector; i++)
+ trans_pcie->msix_entries[i].entry = i;
+
+ ret = pci_enable_msix_range(pdev, trans_pcie->msix_entries,
+ MSIX_MIN_INTERRUPT_VECTORS,
+ max_vector);
+ if (ret > 1) {
+ IWL_DEBUG_INFO(trans,
+ "Enable MSI-X allocate %d interrupt vector\n",
+ ret);
+ trans_pcie->allocated_vector = ret;
+ trans_pcie->default_irq_num =
+ trans_pcie->allocated_vector - 1;
+ trans_pcie->trans->num_rx_queues =
+ trans_pcie->allocated_vector - 1;
+ trans_pcie->msix_enabled = true;
+
+ return;
+ }
+ IWL_DEBUG_INFO(trans,
+ "ret = %d %s move to msi mode\n", ret,
+ (ret == 1) ?
+ "can't allocate more than 1 interrupt vector" :
+ "failed to enable msi-x mode");
+ pci_disable_msix(pdev);
+ }
+
+ ret = pci_enable_msi(pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "pci_enable_msi failed(0X%x)\n", ret);
+ /* enable rfkill interrupt: hw bug w/a */
+ pci_read_config_word(pdev, PCI_COMMAND, &pci_cmd);
+ if (pci_cmd & PCI_COMMAND_INTX_DISABLE) {
+ pci_cmd &= ~PCI_COMMAND_INTX_DISABLE;
+ pci_write_config_word(pdev, PCI_COMMAND, pci_cmd);
+ }
+ }
+}
+
+static int iwl_pcie_init_msix_handler(struct pci_dev *pdev,
+ struct iwl_trans_pcie *trans_pcie)
+{
+ int i, last_vector;
+
+ last_vector = trans_pcie->trans->num_rx_queues;
+
+ for (i = 0; i < trans_pcie->allocated_vector; i++) {
+ int ret;
+
+ ret = request_threaded_irq(trans_pcie->msix_entries[i].vector,
+ iwl_pcie_msix_isr,
+ (i == last_vector) ?
+ iwl_pcie_irq_msix_handler :
+ iwl_pcie_irq_rx_msix_handler,
+ IRQF_SHARED,
+ DRV_NAME,
+ &trans_pcie->msix_entries[i]);
+ if (ret) {
+ int j;
+
+ IWL_ERR(trans_pcie->trans,
+ "Error allocating IRQ %d\n", i);
+ for (j = 0; j < i; j++)
+ free_irq(trans_pcie->msix_entries[i].vector,
+ &trans_pcie->msix_entries[i]);
+ pci_disable_msix(pdev);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
static int _iwl_trans_pcie_start_hw(struct iwl_trans *trans, bool low_power)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
@@ -1371,6 +1530,7 @@
iwl_pcie_apm_init(trans);
+ iwl_pcie_init_msix(trans_pcie);
/* From now on, the op_mode will be kept updated about RF kill state */
iwl_enable_rfkill_int(trans);
@@ -1425,7 +1585,7 @@
mutex_unlock(&trans_pcie->mutex);
- synchronize_irq(trans_pcie->pci_dev->irq);
+ iwl_pcie_synchronize_irqs(trans);
}
static void iwl_trans_pcie_write8(struct iwl_trans *trans, u32 ofs, u8 val)
@@ -1486,9 +1646,6 @@
trans->command_groups = trans_cfg->command_groups;
trans->command_groups_size = trans_cfg->command_groups_size;
- /* init ref_count to 1 (should be cleared when ucode is loaded) */
- trans_pcie->ref_count = 1;
-
/* Initialize NAPI here - it should be before registering to mac80211
* in the opmode but after the HW struct is allocated.
* As this function may be called again in some corner cases don't
@@ -1503,18 +1660,25 @@
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
int i;
- /* TODO: check if this is really needed */
- pm_runtime_disable(trans->dev);
-
- synchronize_irq(trans_pcie->pci_dev->irq);
+ iwl_pcie_synchronize_irqs(trans);
iwl_pcie_tx_free(trans);
iwl_pcie_rx_free(trans);
- free_irq(trans_pcie->pci_dev->irq, trans);
- iwl_pcie_free_ict(trans);
+ if (trans_pcie->msix_enabled) {
+ for (i = 0; i < trans_pcie->allocated_vector; i++)
+ free_irq(trans_pcie->msix_entries[i].vector,
+ &trans_pcie->msix_entries[i]);
- pci_disable_msi(trans_pcie->pci_dev);
+ pci_disable_msix(trans_pcie->pci_dev);
+ trans_pcie->msix_enabled = false;
+ } else {
+ free_irq(trans_pcie->pci_dev->irq, trans);
+
+ iwl_pcie_free_ict(trans);
+
+ pci_disable_msi(trans_pcie->pci_dev);
+ }
iounmap(trans_pcie->hw_base);
pci_release_regions(trans_pcie->pci_dev);
pci_disable_device(trans_pcie->pci_dev);
@@ -2069,7 +2233,7 @@
} else {
pos += scnprintf(buf + pos, bufsz - pos,
"\tclosed_rb_num: Not Allocated\n");
- }
+ }
}
ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
kfree(buf);
@@ -2615,7 +2779,6 @@
{
struct iwl_trans_pcie *trans_pcie;
struct iwl_trans *trans;
- u16 pci_cmd;
int ret, addr_size;
trans = iwl_trans_alloc(sizeof(struct iwl_trans_pcie),
@@ -2698,17 +2861,6 @@
trans_pcie->pci_dev = pdev;
iwl_disable_interrupts(trans);
- ret = pci_enable_msi(pdev);
- if (ret) {
- dev_err(&pdev->dev, "pci_enable_msi failed(0X%x)\n", ret);
- /* enable rfkill interrupt: hw bug w/a */
- pci_read_config_word(pdev, PCI_COMMAND, &pci_cmd);
- if (pci_cmd & PCI_COMMAND_INTX_DISABLE) {
- pci_cmd &= ~PCI_COMMAND_INTX_DISABLE;
- pci_write_config_word(pdev, PCI_COMMAND, pci_cmd);
- }
- }
-
trans->hw_rev = iwl_read32(trans, CSR_HW_REV);
/*
* In the 8000 HW family the format of the 4 bytes of CSR_HW_REV have
@@ -2760,6 +2912,7 @@
}
}
+ iwl_pcie_set_interrupt_capa(pdev, trans);
trans->hw_id = (pdev->device << 16) + pdev->subsystem_device;
snprintf(trans->hw_id_str, sizeof(trans->hw_id_str),
"PCI ID: 0x%04X:0x%04X", pdev->device, pdev->subsystem_device);
@@ -2769,19 +2922,23 @@
init_waitqueue_head(&trans_pcie->d0i3_waitq);
- ret = iwl_pcie_alloc_ict(trans);
- if (ret)
- goto out_pci_disable_msi;
+ if (trans_pcie->msix_enabled) {
+ if (iwl_pcie_init_msix_handler(pdev, trans_pcie))
+ goto out_pci_release_regions;
+ } else {
+ ret = iwl_pcie_alloc_ict(trans);
+ if (ret)
+ goto out_pci_disable_msi;
- ret = request_threaded_irq(pdev->irq, iwl_pcie_isr,
- iwl_pcie_irq_handler,
- IRQF_SHARED, DRV_NAME, trans);
- if (ret) {
- IWL_ERR(trans, "Error allocating IRQ %d\n", pdev->irq);
- goto out_free_ict;
- }
-
- trans_pcie->inta_mask = CSR_INI_SET_MASK;
+ ret = request_threaded_irq(pdev->irq, iwl_pcie_isr,
+ iwl_pcie_irq_handler,
+ IRQF_SHARED, DRV_NAME, trans);
+ if (ret) {
+ IWL_ERR(trans, "Error allocating IRQ %d\n", pdev->irq);
+ goto out_free_ict;
+ }
+ trans_pcie->inta_mask = CSR_INI_SET_MASK;
+ }
#ifdef CONFIG_IWLWIFI_PCIE_RTPM
trans->runtime_pm_mode = IWL_PLAT_PM_MODE_D0I3;
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c
index 837a7d5..16ad820 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c
@@ -1062,10 +1062,10 @@
if (iwl_queue_space(&txq->q) > txq->q.low_mark &&
test_bit(txq_id, trans_pcie->queue_stopped)) {
- struct sk_buff_head skbs;
+ struct sk_buff_head overflow_skbs;
- __skb_queue_head_init(&skbs);
- skb_queue_splice_init(&txq->overflow_q, &skbs);
+ __skb_queue_head_init(&overflow_skbs);
+ skb_queue_splice_init(&txq->overflow_q, &overflow_skbs);
/*
* This is tricky: we are in reclaim path which is non
@@ -1076,8 +1076,8 @@
*/
spin_unlock_bh(&txq->lock);
- while (!skb_queue_empty(&skbs)) {
- struct sk_buff *skb = __skb_dequeue(&skbs);
+ while (!skb_queue_empty(&overflow_skbs)) {
+ struct sk_buff *skb = __skb_dequeue(&overflow_skbs);
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
u8 dev_cmd_idx = IWL_TRANS_FIRST_DRIVER_DATA + 1;
struct iwl_device_cmd *dev_cmd =
diff --git a/drivers/net/wireless/marvell/libertas/main.c b/drivers/net/wireless/marvell/libertas/main.c
index b35b8bc..8541cbe 100644
--- a/drivers/net/wireless/marvell/libertas/main.c
+++ b/drivers/net/wireless/marvell/libertas/main.c
@@ -1118,7 +1118,8 @@
else
pr_info("%s: mesh disabled\n", dev->name);
- if (lbs_cfg_register(priv)) {
+ ret = lbs_cfg_register(priv);
+ if (ret) {
pr_err("cannot register device\n");
goto done;
}
diff --git a/drivers/net/wireless/marvell/mwifiex/11h.c b/drivers/net/wireless/marvell/mwifiex/11h.c
index 71a1b58..81c60d0 100644
--- a/drivers/net/wireless/marvell/mwifiex/11h.c
+++ b/drivers/net/wireless/marvell/mwifiex/11h.c
@@ -123,8 +123,7 @@
void mwifiex_dfs_cac_work_queue(struct work_struct *work)
{
struct cfg80211_chan_def chandef;
- struct delayed_work *delayed_work =
- container_of(work, struct delayed_work, work);
+ struct delayed_work *delayed_work = to_delayed_work(work);
struct mwifiex_private *priv =
container_of(delayed_work, struct mwifiex_private,
dfs_cac_work);
@@ -289,8 +288,7 @@
void mwifiex_dfs_chan_sw_work_queue(struct work_struct *work)
{
struct mwifiex_uap_bss_param *bss_cfg;
- struct delayed_work *delayed_work =
- container_of(work, struct delayed_work, work);
+ struct delayed_work *delayed_work = to_delayed_work(work);
struct mwifiex_private *priv =
container_of(delayed_work, struct mwifiex_private,
dfs_chan_sw_work);
diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
index f2dce81..bb7235e 100644
--- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c
+++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
@@ -20,6 +20,7 @@
#include "cfg80211.h"
#include "main.h"
#include "11n.h"
+#include "wmm.h"
static char *reg_alpha2;
module_param(reg_alpha2, charp, 0);
@@ -676,7 +677,7 @@
}
break;
- case MWIFIEX_BSS_ROLE_STA:
+ case MWIFIEX_BSS_ROLE_STA:
if (priv->media_connected) {
mwifiex_dbg(adapter, ERROR,
"cannot change wiphy params when connected");
@@ -3259,7 +3260,7 @@
{
struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy);
struct mwifiex_ds_hs_cfg hs_cfg;
- int i, ret = 0;
+ int i, ret = 0, retry_num = 10;
struct mwifiex_private *priv;
for (i = 0; i < adapter->priv_num; i++) {
@@ -3269,6 +3270,21 @@
mwifiex_cancel_all_pending_cmd(adapter);
+ for (i = 0; i < adapter->priv_num; i++) {
+ priv = adapter->priv[i];
+ if (priv && priv->netdev)
+ mwifiex_stop_net_dev_queue(priv->netdev, adapter);
+ }
+
+ for (i = 0; i < retry_num; i++) {
+ if (!mwifiex_wmm_lists_empty(adapter) ||
+ !mwifiex_bypass_txlist_empty(adapter) ||
+ !skb_queue_empty(&adapter->tx_data_q))
+ usleep_range(10000, 15000);
+ else
+ break;
+ }
+
if (!wowlan) {
mwifiex_dbg(adapter, ERROR,
"None of the WOWLAN triggers enabled\n");
@@ -3321,12 +3337,18 @@
static int mwifiex_cfg80211_resume(struct wiphy *wiphy)
{
struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy);
- struct mwifiex_private *priv =
- mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA);
+ struct mwifiex_private *priv;
struct mwifiex_ds_wakeup_reason wakeup_reason;
struct cfg80211_wowlan_wakeup wakeup_report;
int i;
+ for (i = 0; i < adapter->priv_num; i++) {
+ priv = adapter->priv[i];
+ if (priv && priv->netdev)
+ mwifiex_wake_up_net_dev_queue(priv->netdev, adapter);
+ }
+
+ priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA);
mwifiex_get_wakeup_reason(priv, HostCmd_ACT_GEN_GET, MWIFIEX_SYNC_CMD,
&wakeup_reason);
memset(&wakeup_report, 0, sizeof(struct cfg80211_wowlan_wakeup));
diff --git a/drivers/net/wireless/marvell/mwifiex/ioctl.h b/drivers/net/wireless/marvell/mwifiex/ioctl.h
index 14cfa37..a5a48c1 100644
--- a/drivers/net/wireless/marvell/mwifiex/ioctl.h
+++ b/drivers/net/wireless/marvell/mwifiex/ioctl.h
@@ -184,6 +184,7 @@
};
#define DBG_CMD_NUM 5
+#define MWIFIEX_DBG_SDIO_MP_NUM 10
struct tdls_peer_info {
u8 peer_addr[ETH_ALEN];
@@ -235,6 +236,11 @@
u8 cmd_sent;
u8 cmd_resp_received;
u8 event_received;
+ u32 last_mp_wr_bitmap[MWIFIEX_DBG_SDIO_MP_NUM];
+ u32 last_mp_wr_ports[MWIFIEX_DBG_SDIO_MP_NUM];
+ u32 last_mp_wr_len[MWIFIEX_DBG_SDIO_MP_NUM];
+ u32 last_mp_curr_wr_port[MWIFIEX_DBG_SDIO_MP_NUM];
+ u8 last_sdio_mp_index;
};
#define MWIFIEX_KEY_INDEX_UNICAST 0x40000000
diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h
index aea7aee..aafc4ab 100644
--- a/drivers/net/wireless/marvell/mwifiex/main.h
+++ b/drivers/net/wireless/marvell/mwifiex/main.h
@@ -223,6 +223,11 @@
u16 last_cmd_resp_index;
u16 last_event[DBG_CMD_NUM];
u16 last_event_index;
+ u32 last_mp_wr_bitmap[MWIFIEX_DBG_SDIO_MP_NUM];
+ u32 last_mp_wr_ports[MWIFIEX_DBG_SDIO_MP_NUM];
+ u32 last_mp_wr_len[MWIFIEX_DBG_SDIO_MP_NUM];
+ u32 last_mp_curr_wr_port[MWIFIEX_DBG_SDIO_MP_NUM];
+ u8 last_sdio_mp_index;
};
enum MWIFIEX_HARDWARE_STATUS {
diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c
index cc07214..de36438 100644
--- a/drivers/net/wireless/marvell/mwifiex/pcie.c
+++ b/drivers/net/wireless/marvell/mwifiex/pcie.c
@@ -1401,7 +1401,7 @@
return -1;
}
- if (mwifiex_map_pci_memory(adapter, skb, skb->len , PCI_DMA_TODEVICE))
+ if (mwifiex_map_pci_memory(adapter, skb, skb->len, PCI_DMA_TODEVICE))
return -1;
buf_pa = MWIFIEX_SKB_DMA_ADDR(skb);
@@ -2452,7 +2452,7 @@
for (i = 0; i < 4; i++) {
mwifiex_read_reg_byte(adapter, reg, &read_reg);
memory_size |= (read_reg << (i * 8));
- reg++;
+ reg++;
}
} else {
memory_size = MWIFIEX_FW_DUMP_MAX_MEMSIZE;
diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c
index abf15db..b2c839a 100644
--- a/drivers/net/wireless/marvell/mwifiex/sdio.c
+++ b/drivers/net/wireless/marvell/mwifiex/sdio.c
@@ -1355,6 +1355,9 @@
card->mpa_rx.start_port;
}
+ if (card->mpa_rx.pkt_cnt == 1)
+ mport = adapter->ioport + port;
+
if (mwifiex_read_data_sync(adapter, card->mpa_rx.buf,
card->mpa_rx.buf_len, mport, 1))
goto error;
@@ -1684,6 +1687,7 @@
s32 f_precopy_cur_buf = 0;
s32 f_postcopy_cur_buf = 0;
u32 mport;
+ int index;
if (!card->mpa_tx.enabled ||
(card->has_control_mask && (port == CTRL_PORT)) ||
@@ -1785,9 +1789,21 @@
card->mpa_tx.start_port;
}
+ if (card->mpa_tx.pkt_cnt == 1)
+ mport = adapter->ioport + port;
+
ret = mwifiex_write_data_to_card(adapter, card->mpa_tx.buf,
card->mpa_tx.buf_len, mport);
+ /* Save the last multi port tx aggreagation info to debug log */
+ index = adapter->dbg.last_sdio_mp_index;
+ index = (index + 1) % MWIFIEX_DBG_SDIO_MP_NUM;
+ adapter->dbg.last_sdio_mp_index = index;
+ adapter->dbg.last_mp_wr_ports[index] = mport;
+ adapter->dbg.last_mp_wr_bitmap[index] = card->mp_wr_bitmap;
+ adapter->dbg.last_mp_wr_len[index] = card->mpa_tx.buf_len;
+ adapter->dbg.last_mp_curr_wr_port[index] = card->curr_wr_port;
+
MP_TX_AGGR_BUF_RESET(card);
}
diff --git a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c
index 5cbee58..d5c56eb 100644
--- a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c
+++ b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c
@@ -314,6 +314,7 @@
mwifiex_dbg(adapter, ERROR,
"Attempt to reconnect on csa closed chan(%d)\n",
bss_desc->channel);
+ ret = -1;
goto done;
}
diff --git a/drivers/net/wireless/marvell/mwifiex/tdls.c b/drivers/net/wireless/marvell/mwifiex/tdls.c
index 9275f9c..1506496 100644
--- a/drivers/net/wireless/marvell/mwifiex/tdls.c
+++ b/drivers/net/wireless/marvell/mwifiex/tdls.c
@@ -680,6 +680,13 @@
__net_timestamp(skb);
mwifiex_queue_tx_pkt(priv, skb);
+ /* Delay 10ms to make sure tdls setup confirm/teardown frame
+ * is received by peer
+ */
+ if (action_code == WLAN_TDLS_SETUP_CONFIRM ||
+ action_code == WLAN_TDLS_TEARDOWN)
+ msleep_interruptible(10);
+
return 0;
}
diff --git a/drivers/net/wireless/marvell/mwifiex/uap_cmd.c b/drivers/net/wireless/marvell/mwifiex/uap_cmd.c
index e791166..16d95b2 100644
--- a/drivers/net/wireless/marvell/mwifiex/uap_cmd.c
+++ b/drivers/net/wireless/marvell/mwifiex/uap_cmd.c
@@ -192,7 +192,7 @@
}
priv->ap_11n_enabled = 1;
} else {
- memset(&bss_cfg->ht_cap , 0, sizeof(struct ieee80211_ht_cap));
+ memset(&bss_cfg->ht_cap, 0, sizeof(struct ieee80211_ht_cap));
bss_cfg->ht_cap.cap_info = cpu_to_le16(MWIFIEX_DEF_HT_CAP);
bss_cfg->ht_cap.ampdu_params_info = MWIFIEX_DEF_AMPDU;
}
diff --git a/drivers/net/wireless/marvell/mwifiex/usb.c b/drivers/net/wireless/marvell/mwifiex/usb.c
index e43aff9..0510861 100644
--- a/drivers/net/wireless/marvell/mwifiex/usb.c
+++ b/drivers/net/wireless/marvell/mwifiex/usb.c
@@ -244,9 +244,9 @@
if (card->rx_cmd_ep == context->ep) {
mwifiex_usb_submit_rx_urb(context, size);
} else {
- if (atomic_read(&adapter->rx_pending) <= HIGH_RX_PENDING){
+ if (atomic_read(&adapter->rx_pending) <= HIGH_RX_PENDING) {
mwifiex_usb_submit_rx_urb(context, size);
- }else{
+ } else {
context->skb = NULL;
}
}
diff --git a/drivers/net/wireless/marvell/mwifiex/util.c b/drivers/net/wireless/marvell/mwifiex/util.c
index 0cec8a6..6681be0 100644
--- a/drivers/net/wireless/marvell/mwifiex/util.c
+++ b/drivers/net/wireless/marvell/mwifiex/util.c
@@ -78,6 +78,16 @@
item_addr(last_event), DBG_CMD_NUM},
{"last_event_index", item_size(last_event_index),
item_addr(last_event_index), 1},
+ {"last_mp_wr_bitmap", item_size(last_mp_wr_bitmap),
+ item_addr(last_mp_wr_bitmap), MWIFIEX_DBG_SDIO_MP_NUM},
+ {"last_mp_wr_ports", item_size(last_mp_wr_ports),
+ item_addr(last_mp_wr_ports), MWIFIEX_DBG_SDIO_MP_NUM},
+ {"last_mp_wr_len", item_size(last_mp_wr_len),
+ item_addr(last_mp_wr_len), MWIFIEX_DBG_SDIO_MP_NUM},
+ {"last_mp_curr_wr_port", item_size(last_mp_curr_wr_port),
+ item_addr(last_mp_curr_wr_port), MWIFIEX_DBG_SDIO_MP_NUM},
+ {"last_sdio_mp_index", item_size(last_sdio_mp_index),
+ item_addr(last_sdio_mp_index), 1},
{"num_cmd_h2c_fail", item_size(num_cmd_host_to_card_failure),
item_addr(num_cmd_host_to_card_failure), 1},
{"num_cmd_sleep_cfm_fail",
@@ -233,6 +243,16 @@
memcpy(info->last_event, adapter->dbg.last_event,
sizeof(adapter->dbg.last_event));
info->last_event_index = adapter->dbg.last_event_index;
+ memcpy(info->last_mp_wr_bitmap, adapter->dbg.last_mp_wr_bitmap,
+ sizeof(adapter->dbg.last_mp_wr_bitmap));
+ memcpy(info->last_mp_wr_ports, adapter->dbg.last_mp_wr_ports,
+ sizeof(adapter->dbg.last_mp_wr_ports));
+ memcpy(info->last_mp_curr_wr_port,
+ adapter->dbg.last_mp_curr_wr_port,
+ sizeof(adapter->dbg.last_mp_curr_wr_port));
+ memcpy(info->last_mp_wr_len, adapter->dbg.last_mp_wr_len,
+ sizeof(adapter->dbg.last_mp_wr_len));
+ info->last_sdio_mp_index = adapter->dbg.last_sdio_mp_index;
info->data_sent = adapter->data_sent;
info->cmd_sent = adapter->cmd_sent;
info->cmd_resp_received = adapter->cmd_resp_received;
diff --git a/drivers/net/wireless/mediatek/mt7601u/mcu.c b/drivers/net/wireless/mediatek/mt7601u/mcu.c
index fbb1986..91c4b34 100644
--- a/drivers/net/wireless/mediatek/mt7601u/mcu.c
+++ b/drivers/net/wireless/mediatek/mt7601u/mcu.c
@@ -362,7 +362,9 @@
int i, ret;
ivb = kmemdup(fw->ivb, sizeof(fw->ivb), GFP_KERNEL);
- if (!ivb || mt7601u_usb_alloc_buf(dev, MCU_FW_URB_SIZE, &dma_buf)) {
+ if (!ivb)
+ return -ENOMEM;
+ if (mt7601u_usb_alloc_buf(dev, MCU_FW_URB_SIZE, &dma_buf)) {
ret = -ENOMEM;
goto error;
}
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
index bf9afbf..4b0bb6b 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
@@ -1026,6 +1026,7 @@
{ USB_DEVICE(0x0411, 0x01a2) },
{ USB_DEVICE(0x0411, 0x01ee) },
{ USB_DEVICE(0x0411, 0x01a8) },
+ { USB_DEVICE(0x0411, 0x01fd) },
/* Corega */
{ USB_DEVICE(0x07aa, 0x002f) },
{ USB_DEVICE(0x07aa, 0x003c) },
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00debug.c b/drivers/net/wireless/ralink/rt2x00/rt2x00debug.c
index 25ee3cb..72ae530 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00debug.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00debug.c
@@ -478,7 +478,7 @@
{ \
struct rt2x00debug_intf *intf = file->private_data; \
const struct rt2x00debug *debug = intf->debug; \
- char line[16]; \
+ char line[17]; \
size_t size; \
unsigned int index = intf->offset_##__name; \
__type value; \
@@ -494,7 +494,8 @@
\
if (copy_from_user(line, buf, length)) \
return -EFAULT; \
- \
+ line[16] = 0; \
+ \
size = strlen(line); \
value = simple_strtoul(line, NULL, 0); \
\
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/Kconfig b/drivers/net/wireless/realtek/rtl8xxxu/Kconfig
index dd4d626..8f053c3 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/Kconfig
+++ b/drivers/net/wireless/realtek/rtl8xxxu/Kconfig
@@ -13,7 +13,7 @@
This driver is under development and has a limited feature
set. In particular it does not yet support 40MHz channels
and power management. However it should have a smaller
- memory footprint than the vendor drivers and benetifs
+ memory footprint than the vendor drivers and benefits
from the in kernel mac80211 stack.
It can coexist with drivers from drivers/staging/rtl8723au,
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.c
index 7d820c3..abdff45 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.c
@@ -42,7 +42,7 @@
#define DRIVER_NAME "rtl8xxxu"
-static int rtl8xxxu_debug;
+static int rtl8xxxu_debug = RTL8XXXU_DEBUG_EFUSE;
static bool rtl8xxxu_ht40_2g;
MODULE_AUTHOR("Jes Sorensen <Jes.Sorensen@redhat.com>");
@@ -54,6 +54,9 @@
MODULE_FIRMWARE("rtlwifi/rtl8192cufw_A.bin");
MODULE_FIRMWARE("rtlwifi/rtl8192cufw_B.bin");
MODULE_FIRMWARE("rtlwifi/rtl8192cufw_TMSC.bin");
+MODULE_FIRMWARE("rtlwifi/rtl8192eu_nic.bin");
+MODULE_FIRMWARE("rtlwifi/rtl8723bu_nic.bin");
+MODULE_FIRMWARE("rtlwifi/rtl8723bu_bt.bin");
module_param_named(debug, rtl8xxxu_debug, int, 0600);
MODULE_PARM_DESC(debug, "Set debug mask");
@@ -150,6 +153,37 @@
{0x70a, 0x65}, {0x70b, 0x87}, {0xffff, 0xff},
};
+static struct rtl8xxxu_reg8val rtl8723b_mac_init_table[] = {
+ {0x02f, 0x30}, {0x035, 0x00}, {0x039, 0x08}, {0x04e, 0xe0},
+ {0x064, 0x00}, {0x067, 0x20}, {0x428, 0x0a}, {0x429, 0x10},
+ {0x430, 0x00}, {0x431, 0x00},
+ {0x432, 0x00}, {0x433, 0x01}, {0x434, 0x04}, {0x435, 0x05},
+ {0x436, 0x07}, {0x437, 0x08}, {0x43c, 0x04}, {0x43d, 0x05},
+ {0x43e, 0x07}, {0x43f, 0x08}, {0x440, 0x5d}, {0x441, 0x01},
+ {0x442, 0x00}, {0x444, 0x10}, {0x445, 0x00}, {0x446, 0x00},
+ {0x447, 0x00}, {0x448, 0x00}, {0x449, 0xf0}, {0x44a, 0x0f},
+ {0x44b, 0x3e}, {0x44c, 0x10}, {0x44d, 0x00}, {0x44e, 0x00},
+ {0x44f, 0x00}, {0x450, 0x00}, {0x451, 0xf0}, {0x452, 0x0f},
+ {0x453, 0x00}, {0x456, 0x5e}, {0x460, 0x66}, {0x461, 0x66},
+ {0x4c8, 0xff}, {0x4c9, 0x08}, {0x4cc, 0xff},
+ {0x4cd, 0xff}, {0x4ce, 0x01}, {0x500, 0x26}, {0x501, 0xa2},
+ {0x502, 0x2f}, {0x503, 0x00}, {0x504, 0x28}, {0x505, 0xa3},
+ {0x506, 0x5e}, {0x507, 0x00}, {0x508, 0x2b}, {0x509, 0xa4},
+ {0x50a, 0x5e}, {0x50b, 0x00}, {0x50c, 0x4f}, {0x50d, 0xa4},
+ {0x50e, 0x00}, {0x50f, 0x00}, {0x512, 0x1c}, {0x514, 0x0a},
+ {0x516, 0x0a}, {0x525, 0x4f},
+ {0x550, 0x10}, {0x551, 0x10}, {0x559, 0x02}, {0x55c, 0x50},
+ {0x55d, 0xff}, {0x605, 0x30}, {0x608, 0x0e}, {0x609, 0x2a},
+ {0x620, 0xff}, {0x621, 0xff}, {0x622, 0xff}, {0x623, 0xff},
+ {0x624, 0xff}, {0x625, 0xff}, {0x626, 0xff}, {0x627, 0xff},
+ {0x638, 0x50}, {0x63c, 0x0a}, {0x63d, 0x0a}, {0x63e, 0x0e},
+ {0x63f, 0x0e}, {0x640, 0x40}, {0x642, 0x40}, {0x643, 0x00},
+ {0x652, 0xc8}, {0x66e, 0x05}, {0x700, 0x21}, {0x701, 0x43},
+ {0x702, 0x65}, {0x703, 0x87}, {0x708, 0x21}, {0x709, 0x43},
+ {0x70a, 0x65}, {0x70b, 0x87}, {0x765, 0x18}, {0x76e, 0x04},
+ {0xffff, 0xff},
+};
+
static struct rtl8xxxu_reg32val rtl8723a_phy_1t_init_table[] = {
{0x800, 0x80040000}, {0x804, 0x00000003},
{0x808, 0x0000fc00}, {0x80c, 0x0000000a},
@@ -248,6 +282,107 @@
{0xffff, 0xffffffff},
};
+static struct rtl8xxxu_reg32val rtl8723b_phy_1t_init_table[] = {
+ {0x800, 0x80040000}, {0x804, 0x00000003},
+ {0x808, 0x0000fc00}, {0x80c, 0x0000000a},
+ {0x810, 0x10001331}, {0x814, 0x020c3d10},
+ {0x818, 0x02200385}, {0x81c, 0x00000000},
+ {0x820, 0x01000100}, {0x824, 0x00190204},
+ {0x828, 0x00000000}, {0x82c, 0x00000000},
+ {0x830, 0x00000000}, {0x834, 0x00000000},
+ {0x838, 0x00000000}, {0x83c, 0x00000000},
+ {0x840, 0x00010000}, {0x844, 0x00000000},
+ {0x848, 0x00000000}, {0x84c, 0x00000000},
+ {0x850, 0x00000000}, {0x854, 0x00000000},
+ {0x858, 0x569a11a9}, {0x85c, 0x01000014},
+ {0x860, 0x66f60110}, {0x864, 0x061f0649},
+ {0x868, 0x00000000}, {0x86c, 0x27272700},
+ {0x870, 0x07000760}, {0x874, 0x25004000},
+ {0x878, 0x00000808}, {0x87c, 0x00000000},
+ {0x880, 0xb0000c1c}, {0x884, 0x00000001},
+ {0x888, 0x00000000}, {0x88c, 0xccc000c0},
+ {0x890, 0x00000800}, {0x894, 0xfffffffe},
+ {0x898, 0x40302010}, {0x89c, 0x00706050},
+ {0x900, 0x00000000}, {0x904, 0x00000023},
+ {0x908, 0x00000000}, {0x90c, 0x81121111},
+ {0x910, 0x00000002}, {0x914, 0x00000201},
+ {0xa00, 0x00d047c8}, {0xa04, 0x80ff800c},
+ {0xa08, 0x8c838300}, {0xa0c, 0x2e7f120f},
+ {0xa10, 0x9500bb78}, {0xa14, 0x1114d028},
+ {0xa18, 0x00881117}, {0xa1c, 0x89140f00},
+ {0xa20, 0x1a1b0000}, {0xa24, 0x090e1317},
+ {0xa28, 0x00000204}, {0xa2c, 0x00d30000},
+ {0xa70, 0x101fbf00}, {0xa74, 0x00000007},
+ {0xa78, 0x00000900}, {0xa7c, 0x225b0606},
+ {0xa80, 0x21806490}, {0xb2c, 0x00000000},
+ {0xc00, 0x48071d40}, {0xc04, 0x03a05611},
+ {0xc08, 0x000000e4}, {0xc0c, 0x6c6c6c6c},
+ {0xc10, 0x08800000}, {0xc14, 0x40000100},
+ {0xc18, 0x08800000}, {0xc1c, 0x40000100},
+ {0xc20, 0x00000000}, {0xc24, 0x00000000},
+ {0xc28, 0x00000000}, {0xc2c, 0x00000000},
+ {0xc30, 0x69e9ac44}, {0xc34, 0x469652af},
+ {0xc38, 0x49795994}, {0xc3c, 0x0a97971c},
+ {0xc40, 0x1f7c403f}, {0xc44, 0x000100b7},
+ {0xc48, 0xec020107}, {0xc4c, 0x007f037f},
+ {0xc50, 0x69553420}, {0xc54, 0x43bc0094},
+ {0xc58, 0x00013149}, {0xc5c, 0x00250492},
+ {0xc60, 0x00000000}, {0xc64, 0x7112848b},
+ {0xc68, 0x47c00bff}, {0xc6c, 0x00000036},
+ {0xc70, 0x2c7f000d}, {0xc74, 0x020610db},
+ {0xc78, 0x0000001f}, {0xc7c, 0x00b91612},
+ {0xc80, 0x390000e4}, {0xc84, 0x20f60000},
+ {0xc88, 0x40000100}, {0xc8c, 0x20200000},
+ {0xc90, 0x00020e1a}, {0xc94, 0x00000000},
+ {0xc98, 0x00020e1a}, {0xc9c, 0x00007f7f},
+ {0xca0, 0x00000000}, {0xca4, 0x000300a0},
+ {0xca8, 0x00000000}, {0xcac, 0x00000000},
+ {0xcb0, 0x00000000}, {0xcb4, 0x00000000},
+ {0xcb8, 0x00000000}, {0xcbc, 0x28000000},
+ {0xcc0, 0x00000000}, {0xcc4, 0x00000000},
+ {0xcc8, 0x00000000}, {0xccc, 0x00000000},
+ {0xcd0, 0x00000000}, {0xcd4, 0x00000000},
+ {0xcd8, 0x64b22427}, {0xcdc, 0x00766932},
+ {0xce0, 0x00222222}, {0xce4, 0x00000000},
+ {0xce8, 0x37644302}, {0xcec, 0x2f97d40c},
+ {0xd00, 0x00000740}, {0xd04, 0x40020401},
+ {0xd08, 0x0000907f}, {0xd0c, 0x20010201},
+ {0xd10, 0xa0633333}, {0xd14, 0x3333bc53},
+ {0xd18, 0x7a8f5b6f}, {0xd2c, 0xcc979975},
+ {0xd30, 0x00000000}, {0xd34, 0x80608000},
+ {0xd38, 0x00000000}, {0xd3c, 0x00127353},
+ {0xd40, 0x00000000}, {0xd44, 0x00000000},
+ {0xd48, 0x00000000}, {0xd4c, 0x00000000},
+ {0xd50, 0x6437140a}, {0xd54, 0x00000000},
+ {0xd58, 0x00000282}, {0xd5c, 0x30032064},
+ {0xd60, 0x4653de68}, {0xd64, 0x04518a3c},
+ {0xd68, 0x00002101}, {0xd6c, 0x2a201c16},
+ {0xd70, 0x1812362e}, {0xd74, 0x322c2220},
+ {0xd78, 0x000e3c24}, {0xe00, 0x2d2d2d2d},
+ {0xe04, 0x2d2d2d2d}, {0xe08, 0x0390272d},
+ {0xe10, 0x2d2d2d2d}, {0xe14, 0x2d2d2d2d},
+ {0xe18, 0x2d2d2d2d}, {0xe1c, 0x2d2d2d2d},
+ {0xe28, 0x00000000}, {0xe30, 0x1000dc1f},
+ {0xe34, 0x10008c1f}, {0xe38, 0x02140102},
+ {0xe3c, 0x681604c2}, {0xe40, 0x01007c00},
+ {0xe44, 0x01004800}, {0xe48, 0xfb000000},
+ {0xe4c, 0x000028d1}, {0xe50, 0x1000dc1f},
+ {0xe54, 0x10008c1f}, {0xe58, 0x02140102},
+ {0xe5c, 0x28160d05}, {0xe60, 0x00000008},
+ {0xe68, 0x001b2556}, {0xe6c, 0x00c00096},
+ {0xe70, 0x00c00096}, {0xe74, 0x01000056},
+ {0xe78, 0x01000014}, {0xe7c, 0x01000056},
+ {0xe80, 0x01000014}, {0xe84, 0x00c00096},
+ {0xe88, 0x01000056}, {0xe8c, 0x00c00096},
+ {0xed0, 0x00c00096}, {0xed4, 0x00c00096},
+ {0xed8, 0x00c00096}, {0xedc, 0x000000d6},
+ {0xee0, 0x000000d6}, {0xeec, 0x01c00016},
+ {0xf14, 0x00000003}, {0xf4c, 0x00000000},
+ {0xf00, 0x00000300},
+ {0x820, 0x01000100}, {0x800, 0x83040000},
+ {0xffff, 0xffffffff},
+};
+
static struct rtl8xxxu_reg32val rtl8192cu_phy_2t_init_table[] = {
{0x024, 0x0011800f}, {0x028, 0x00ffdb83},
{0x800, 0x80040002}, {0x804, 0x00000003},
@@ -613,6 +748,77 @@
{0xffff, 0xffffffff}
};
+static struct rtl8xxxu_reg32val rtl8xxx_agc_8723bu_table[] = {
+ {0xc78, 0xfd000001}, {0xc78, 0xfc010001},
+ {0xc78, 0xfb020001}, {0xc78, 0xfa030001},
+ {0xc78, 0xf9040001}, {0xc78, 0xf8050001},
+ {0xc78, 0xf7060001}, {0xc78, 0xf6070001},
+ {0xc78, 0xf5080001}, {0xc78, 0xf4090001},
+ {0xc78, 0xf30a0001}, {0xc78, 0xf20b0001},
+ {0xc78, 0xf10c0001}, {0xc78, 0xf00d0001},
+ {0xc78, 0xef0e0001}, {0xc78, 0xee0f0001},
+ {0xc78, 0xed100001}, {0xc78, 0xec110001},
+ {0xc78, 0xeb120001}, {0xc78, 0xea130001},
+ {0xc78, 0xe9140001}, {0xc78, 0xe8150001},
+ {0xc78, 0xe7160001}, {0xc78, 0xe6170001},
+ {0xc78, 0xe5180001}, {0xc78, 0xe4190001},
+ {0xc78, 0xe31a0001}, {0xc78, 0xa51b0001},
+ {0xc78, 0xa41c0001}, {0xc78, 0xa31d0001},
+ {0xc78, 0x671e0001}, {0xc78, 0x661f0001},
+ {0xc78, 0x65200001}, {0xc78, 0x64210001},
+ {0xc78, 0x63220001}, {0xc78, 0x4a230001},
+ {0xc78, 0x49240001}, {0xc78, 0x48250001},
+ {0xc78, 0x47260001}, {0xc78, 0x46270001},
+ {0xc78, 0x45280001}, {0xc78, 0x44290001},
+ {0xc78, 0x432a0001}, {0xc78, 0x422b0001},
+ {0xc78, 0x292c0001}, {0xc78, 0x282d0001},
+ {0xc78, 0x272e0001}, {0xc78, 0x262f0001},
+ {0xc78, 0x0a300001}, {0xc78, 0x09310001},
+ {0xc78, 0x08320001}, {0xc78, 0x07330001},
+ {0xc78, 0x06340001}, {0xc78, 0x05350001},
+ {0xc78, 0x04360001}, {0xc78, 0x03370001},
+ {0xc78, 0x02380001}, {0xc78, 0x01390001},
+ {0xc78, 0x013a0001}, {0xc78, 0x013b0001},
+ {0xc78, 0x013c0001}, {0xc78, 0x013d0001},
+ {0xc78, 0x013e0001}, {0xc78, 0x013f0001},
+ {0xc78, 0xfc400001}, {0xc78, 0xfb410001},
+ {0xc78, 0xfa420001}, {0xc78, 0xf9430001},
+ {0xc78, 0xf8440001}, {0xc78, 0xf7450001},
+ {0xc78, 0xf6460001}, {0xc78, 0xf5470001},
+ {0xc78, 0xf4480001}, {0xc78, 0xf3490001},
+ {0xc78, 0xf24a0001}, {0xc78, 0xf14b0001},
+ {0xc78, 0xf04c0001}, {0xc78, 0xef4d0001},
+ {0xc78, 0xee4e0001}, {0xc78, 0xed4f0001},
+ {0xc78, 0xec500001}, {0xc78, 0xeb510001},
+ {0xc78, 0xea520001}, {0xc78, 0xe9530001},
+ {0xc78, 0xe8540001}, {0xc78, 0xe7550001},
+ {0xc78, 0xe6560001}, {0xc78, 0xe5570001},
+ {0xc78, 0xe4580001}, {0xc78, 0xe3590001},
+ {0xc78, 0xa65a0001}, {0xc78, 0xa55b0001},
+ {0xc78, 0xa45c0001}, {0xc78, 0xa35d0001},
+ {0xc78, 0x675e0001}, {0xc78, 0x665f0001},
+ {0xc78, 0x65600001}, {0xc78, 0x64610001},
+ {0xc78, 0x63620001}, {0xc78, 0x62630001},
+ {0xc78, 0x61640001}, {0xc78, 0x48650001},
+ {0xc78, 0x47660001}, {0xc78, 0x46670001},
+ {0xc78, 0x45680001}, {0xc78, 0x44690001},
+ {0xc78, 0x436a0001}, {0xc78, 0x426b0001},
+ {0xc78, 0x286c0001}, {0xc78, 0x276d0001},
+ {0xc78, 0x266e0001}, {0xc78, 0x256f0001},
+ {0xc78, 0x24700001}, {0xc78, 0x09710001},
+ {0xc78, 0x08720001}, {0xc78, 0x07730001},
+ {0xc78, 0x06740001}, {0xc78, 0x05750001},
+ {0xc78, 0x04760001}, {0xc78, 0x03770001},
+ {0xc78, 0x02780001}, {0xc78, 0x01790001},
+ {0xc78, 0x017a0001}, {0xc78, 0x017b0001},
+ {0xc78, 0x017c0001}, {0xc78, 0x017d0001},
+ {0xc78, 0x017e0001}, {0xc78, 0x017f0001},
+ {0xc50, 0x69553422},
+ {0xc50, 0x69553420},
+ {0x824, 0x00390204},
+ {0xffff, 0xffffffff}
+};
+
static struct rtl8xxxu_rfregval rtl8723au_radioa_1t_init_table[] = {
{0x00, 0x00030159}, {0x01, 0x00031284},
{0x02, 0x00098000}, {0x03, 0x00039c63},
@@ -688,6 +894,75 @@
{0xff, 0xffffffff}
};
+static struct rtl8xxxu_rfregval rtl8723bu_radioa_1t_init_table[] = {
+ {0x00, 0x00010000}, {0xb0, 0x000dffe0},
+ {0xfe, 0x00000000}, {0xfe, 0x00000000},
+ {0xfe, 0x00000000}, {0xb1, 0x00000018},
+ {0xfe, 0x00000000}, {0xfe, 0x00000000},
+ {0xfe, 0x00000000}, {0xb2, 0x00084c00},
+ {0xb5, 0x0000d2cc}, {0xb6, 0x000925aa},
+ {0xb7, 0x00000010}, {0xb8, 0x0000907f},
+ {0x5c, 0x00000002}, {0x7c, 0x00000002},
+ {0x7e, 0x00000005}, {0x8b, 0x0006fc00},
+ {0xb0, 0x000ff9f0}, {0x1c, 0x000739d2},
+ {0x1e, 0x00000000}, {0xdf, 0x00000780},
+ {0x50, 0x00067435},
+ /*
+ * The 8723bu vendor driver indicates that bit 8 should be set in
+ * 0x51 for package types TFBGA90, TFBGA80, and TFBGA79. However
+ * they never actually check the package type - and just default
+ * to not setting it.
+ */
+ {0x51, 0x0006b04e},
+ {0x52, 0x000007d2}, {0x53, 0x00000000},
+ {0x54, 0x00050400}, {0x55, 0x0004026e},
+ {0xdd, 0x0000004c}, {0x70, 0x00067435},
+ /*
+ * 0x71 has same package type condition as for register 0x51
+ */
+ {0x71, 0x0006b04e},
+ {0x72, 0x000007d2}, {0x73, 0x00000000},
+ {0x74, 0x00050400}, {0x75, 0x0004026e},
+ {0xef, 0x00000100}, {0x34, 0x0000add7},
+ {0x35, 0x00005c00}, {0x34, 0x00009dd4},
+ {0x35, 0x00005000}, {0x34, 0x00008dd1},
+ {0x35, 0x00004400}, {0x34, 0x00007dce},
+ {0x35, 0x00003800}, {0x34, 0x00006cd1},
+ {0x35, 0x00004400}, {0x34, 0x00005cce},
+ {0x35, 0x00003800}, {0x34, 0x000048ce},
+ {0x35, 0x00004400}, {0x34, 0x000034ce},
+ {0x35, 0x00003800}, {0x34, 0x00002451},
+ {0x35, 0x00004400}, {0x34, 0x0000144e},
+ {0x35, 0x00003800}, {0x34, 0x00000051},
+ {0x35, 0x00004400}, {0xef, 0x00000000},
+ {0xef, 0x00000100}, {0xed, 0x00000010},
+ {0x44, 0x0000add7}, {0x44, 0x00009dd4},
+ {0x44, 0x00008dd1}, {0x44, 0x00007dce},
+ {0x44, 0x00006cc1}, {0x44, 0x00005cce},
+ {0x44, 0x000044d1}, {0x44, 0x000034ce},
+ {0x44, 0x00002451}, {0x44, 0x0000144e},
+ {0x44, 0x00000051}, {0xef, 0x00000000},
+ {0xed, 0x00000000}, {0x7f, 0x00020080},
+ {0xef, 0x00002000}, {0x3b, 0x000380ef},
+ {0x3b, 0x000302fe}, {0x3b, 0x00028ce6},
+ {0x3b, 0x000200bc}, {0x3b, 0x000188a5},
+ {0x3b, 0x00010fbc}, {0x3b, 0x00008f71},
+ {0x3b, 0x00000900}, {0xef, 0x00000000},
+ {0xed, 0x00000001}, {0x40, 0x000380ef},
+ {0x40, 0x000302fe}, {0x40, 0x00028ce6},
+ {0x40, 0x000200bc}, {0x40, 0x000188a5},
+ {0x40, 0x00010fbc}, {0x40, 0x00008f71},
+ {0x40, 0x00000900}, {0xed, 0x00000000},
+ {0x82, 0x00080000}, {0x83, 0x00008000},
+ {0x84, 0x00048d80}, {0x85, 0x00068000},
+ {0xa2, 0x00080000}, {0xa3, 0x00008000},
+ {0xa4, 0x00048d80}, {0xa5, 0x00068000},
+ {0xed, 0x00000002}, {0xef, 0x00000002},
+ {0x56, 0x00000032}, {0x76, 0x00000032},
+ {0x01, 0x00000780},
+ {0xff, 0xffffffff}
+};
+
static struct rtl8xxxu_rfregval rtl8192cu_radioa_2t_init_table[] = {
{0x00, 0x00030159}, {0x01, 0x00031284},
{0x02, 0x00098000}, {0x03, 0x00018c63},
@@ -1166,6 +1441,11 @@
return retval;
}
+/*
+ * The RTL8723BU driver indicates that registers 0xb2 and 0xb6 can
+ * have write issues in high temperature conditions. We may have to
+ * retry writing them.
+ */
static int rtl8xxxu_write_rfreg(struct rtl8xxxu_priv *priv,
enum rtl8xxxu_rfpath path, u8 reg, u32 data)
{
@@ -1191,7 +1471,8 @@
return retval;
}
-static int rtl8723a_h2c_cmd(struct rtl8xxxu_priv *priv, struct h2c_cmd *h2c)
+static int rtl8723a_h2c_cmd(struct rtl8xxxu_priv *priv,
+ struct h2c_cmd *h2c, int len)
{
struct device *dev = &priv->udev->dev;
int mbox_nr, retry, retval = 0;
@@ -1202,7 +1483,8 @@
mbox_nr = priv->next_mbox;
mbox_reg = REG_HMBOX_0 + (mbox_nr * 4);
- mbox_ext_reg = REG_HMBOX_EXT_0 + (mbox_nr * 2);
+ mbox_ext_reg = priv->fops->mbox_ext_reg +
+ (mbox_nr * priv->fops->mbox_ext_width);
/*
* MBOX ready?
@@ -1215,7 +1497,7 @@
} while (retry--);
if (!retry) {
- dev_dbg(dev, "%s: Mailbox busy\n", __func__);
+ dev_info(dev, "%s: Mailbox busy\n", __func__);
retval = -EBUSY;
goto error;
}
@@ -1223,12 +1505,20 @@
/*
* Need to swap as it's being swapped again by rtl8xxxu_write16/32()
*/
- if (h2c->cmd.cmd & H2C_EXT) {
- rtl8xxxu_write16(priv, mbox_ext_reg,
- le16_to_cpu(h2c->raw.ext));
- if (rtl8xxxu_debug & RTL8XXXU_DEBUG_H2C)
- dev_info(dev, "H2C_EXT %04x\n",
- le16_to_cpu(h2c->raw.ext));
+ if (len > sizeof(u32)) {
+ if (priv->fops->mbox_ext_width == 4) {
+ rtl8xxxu_write32(priv, mbox_ext_reg,
+ le32_to_cpu(h2c->raw_wide.ext));
+ if (rtl8xxxu_debug & RTL8XXXU_DEBUG_H2C)
+ dev_info(dev, "H2C_EXT %08x\n",
+ le32_to_cpu(h2c->raw_wide.ext));
+ } else {
+ rtl8xxxu_write16(priv, mbox_ext_reg,
+ le16_to_cpu(h2c->raw.ext));
+ if (rtl8xxxu_debug & RTL8XXXU_DEBUG_H2C)
+ dev_info(dev, "H2C_EXT %04x\n",
+ le16_to_cpu(h2c->raw.ext));
+ }
}
rtl8xxxu_write32(priv, mbox_reg, le32_to_cpu(h2c->raw.data));
if (rtl8xxxu_debug & RTL8XXXU_DEBUG_H2C)
@@ -1241,6 +1531,27 @@
return retval;
}
+static void rtl8723bu_write_btreg(struct rtl8xxxu_priv *priv, u8 reg, u8 data)
+{
+ struct h2c_cmd h2c;
+ int reqnum = 0;
+
+ memset(&h2c, 0, sizeof(struct h2c_cmd));
+ h2c.bt_mp_oper.cmd = H2C_8723B_BT_MP_OPER;
+ h2c.bt_mp_oper.operreq = 0 | (reqnum << 4);
+ h2c.bt_mp_oper.opcode = BT_MP_OP_WRITE_REG_VALUE;
+ h2c.bt_mp_oper.data = data;
+ rtl8723a_h2c_cmd(priv, &h2c, sizeof(h2c.bt_mp_oper));
+
+ reqnum++;
+ memset(&h2c, 0, sizeof(struct h2c_cmd));
+ h2c.bt_mp_oper.cmd = H2C_8723B_BT_MP_OPER;
+ h2c.bt_mp_oper.operreq = 0 | (reqnum << 4);
+ h2c.bt_mp_oper.opcode = BT_MP_OP_WRITE_REG_VALUE;
+ h2c.bt_mp_oper.addr = reg;
+ rtl8723a_h2c_cmd(priv, &h2c, sizeof(h2c.bt_mp_oper));
+}
+
static void rtl8723a_enable_rf(struct rtl8xxxu_priv *priv)
{
u8 val8;
@@ -1365,6 +1676,24 @@
return group;
}
+static int rtl8723b_channel_to_group(int channel)
+{
+ int group;
+
+ if (channel < 3)
+ group = 0;
+ else if (channel < 6)
+ group = 1;
+ else if (channel < 9)
+ group = 2;
+ else if (channel < 12)
+ group = 3;
+ else
+ group = 4;
+
+ return group;
+}
+
static void rtl8723au_config_channel(struct ieee80211_hw *hw)
{
struct rtl8xxxu_priv *priv = hw->priv;
@@ -1487,6 +1816,136 @@
}
}
+static void rtl8723bu_config_channel(struct ieee80211_hw *hw)
+{
+ struct rtl8xxxu_priv *priv = hw->priv;
+ u32 val32, rsr;
+ u8 val8, subchannel;
+ u16 rf_mode_bw;
+ bool ht = true;
+ int sec_ch_above, channel;
+ int i;
+
+ rf_mode_bw = rtl8xxxu_read16(priv, REG_WMAC_TRXPTCL_CTL);
+ rf_mode_bw &= ~WMAC_TRXPTCL_CTL_BW_MASK;
+ rsr = rtl8xxxu_read32(priv, REG_RESPONSE_RATE_SET);
+ channel = hw->conf.chandef.chan->hw_value;
+
+/* Hack */
+ subchannel = 0;
+
+ switch (hw->conf.chandef.width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ ht = false;
+ case NL80211_CHAN_WIDTH_20:
+ rf_mode_bw |= WMAC_TRXPTCL_CTL_BW_20;
+ subchannel = 0;
+
+ val32 = rtl8xxxu_read32(priv, REG_FPGA0_RF_MODE);
+ val32 &= ~FPGA_RF_MODE;
+ rtl8xxxu_write32(priv, REG_FPGA0_RF_MODE, val32);
+
+ val32 = rtl8xxxu_read32(priv, REG_FPGA1_RF_MODE);
+ val32 &= ~FPGA_RF_MODE;
+ rtl8xxxu_write32(priv, REG_FPGA1_RF_MODE, val32);
+
+ val32 = rtl8xxxu_read32(priv, REG_OFDM0_TX_PSDO_NOISE_WEIGHT);
+ val32 &= ~(BIT(30) | BIT(31));
+ rtl8xxxu_write32(priv, REG_OFDM0_TX_PSDO_NOISE_WEIGHT, val32);
+
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ rf_mode_bw |= WMAC_TRXPTCL_CTL_BW_40;
+
+ if (hw->conf.chandef.center_freq1 >
+ hw->conf.chandef.chan->center_freq) {
+ sec_ch_above = 1;
+ channel += 2;
+ } else {
+ sec_ch_above = 0;
+ channel -= 2;
+ }
+
+ val32 = rtl8xxxu_read32(priv, REG_FPGA0_RF_MODE);
+ val32 |= FPGA_RF_MODE;
+ rtl8xxxu_write32(priv, REG_FPGA0_RF_MODE, val32);
+
+ val32 = rtl8xxxu_read32(priv, REG_FPGA1_RF_MODE);
+ val32 |= FPGA_RF_MODE;
+ rtl8xxxu_write32(priv, REG_FPGA1_RF_MODE, val32);
+
+ /*
+ * Set Control channel to upper or lower. These settings
+ * are required only for 40MHz
+ */
+ val32 = rtl8xxxu_read32(priv, REG_CCK0_SYSTEM);
+ val32 &= ~CCK0_SIDEBAND;
+ if (!sec_ch_above)
+ val32 |= CCK0_SIDEBAND;
+ rtl8xxxu_write32(priv, REG_CCK0_SYSTEM, val32);
+
+ val32 = rtl8xxxu_read32(priv, REG_OFDM1_LSTF);
+ val32 &= ~OFDM_LSTF_PRIME_CH_MASK; /* 0xc00 */
+ if (sec_ch_above)
+ val32 |= OFDM_LSTF_PRIME_CH_LOW;
+ else
+ val32 |= OFDM_LSTF_PRIME_CH_HIGH;
+ rtl8xxxu_write32(priv, REG_OFDM1_LSTF, val32);
+
+ val32 = rtl8xxxu_read32(priv, REG_FPGA0_POWER_SAVE);
+ val32 &= ~(FPGA0_PS_LOWER_CHANNEL | FPGA0_PS_UPPER_CHANNEL);
+ if (sec_ch_above)
+ val32 |= FPGA0_PS_UPPER_CHANNEL;
+ else
+ val32 |= FPGA0_PS_LOWER_CHANNEL;
+ rtl8xxxu_write32(priv, REG_FPGA0_POWER_SAVE, val32);
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ rf_mode_bw |= WMAC_TRXPTCL_CTL_BW_80;
+ break;
+ default:
+ break;
+ }
+
+ for (i = RF_A; i < priv->rf_paths; i++) {
+ val32 = rtl8xxxu_read_rfreg(priv, i, RF6052_REG_MODE_AG);
+ val32 &= ~MODE_AG_CHANNEL_MASK;
+ val32 |= channel;
+ rtl8xxxu_write_rfreg(priv, i, RF6052_REG_MODE_AG, val32);
+ }
+
+ rtl8xxxu_write16(priv, REG_WMAC_TRXPTCL_CTL, rf_mode_bw);
+ rtl8xxxu_write8(priv, REG_DATA_SUBCHANNEL, subchannel);
+
+ if (ht)
+ val8 = 0x0e;
+ else
+ val8 = 0x0a;
+
+ rtl8xxxu_write8(priv, REG_SIFS_CCK + 1, val8);
+ rtl8xxxu_write8(priv, REG_SIFS_OFDM + 1, val8);
+
+ rtl8xxxu_write16(priv, REG_R2T_SIFS, 0x0808);
+ rtl8xxxu_write16(priv, REG_T2T_SIFS, 0x0a0a);
+
+ for (i = RF_A; i < priv->rf_paths; i++) {
+ val32 = rtl8xxxu_read_rfreg(priv, i, RF6052_REG_MODE_AG);
+ val32 &= ~MODE_AG_BW_MASK;
+ switch(hw->conf.chandef.width) {
+ case NL80211_CHAN_WIDTH_80:
+ val32 |= MODE_AG_BW_80MHZ_8723B;
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ val32 |= MODE_AG_BW_40MHZ_8723B;
+ break;
+ default:
+ val32 |= MODE_AG_BW_20MHZ_8723B;
+ break;
+ }
+ rtl8xxxu_write_rfreg(priv, i, RF6052_REG_MODE_AG, val32);
+ }
+}
+
static void
rtl8723a_set_tx_power(struct rtl8xxxu_priv *priv, int channel, bool ht40)
{
@@ -1596,12 +2055,51 @@
}
}
+static void
+rtl8723b_set_tx_power(struct rtl8xxxu_priv *priv, int channel, bool ht40)
+{
+ u32 val32, ofdm, mcs;
+ u8 cck, ofdmbase, mcsbase;
+ int group, tx_idx;
+
+ tx_idx = 0;
+ group = rtl8723b_channel_to_group(channel);
+
+ cck = priv->cck_tx_power_index_B[group];
+ val32 = rtl8xxxu_read32(priv, REG_TX_AGC_A_CCK1_MCS32);
+ val32 &= 0xffff00ff;
+ val32 |= (cck << 8);
+ rtl8xxxu_write32(priv, REG_TX_AGC_A_CCK1_MCS32, val32);
+
+ val32 = rtl8xxxu_read32(priv, REG_TX_AGC_B_CCK11_A_CCK2_11);
+ val32 &= 0xff;
+ val32 |= ((cck << 8) | (cck << 16) | (cck << 24));
+ rtl8xxxu_write32(priv, REG_TX_AGC_B_CCK11_A_CCK2_11, val32);
+
+ ofdmbase = priv->ht40_1s_tx_power_index_B[group];
+ ofdmbase += priv->ofdm_tx_power_diff[tx_idx].b;
+ ofdm = ofdmbase | ofdmbase << 8 | ofdmbase << 16 | ofdmbase << 24;
+
+ rtl8xxxu_write32(priv, REG_TX_AGC_A_RATE18_06, ofdm);
+ rtl8xxxu_write32(priv, REG_TX_AGC_A_RATE54_24, ofdm);
+
+ mcsbase = priv->ht40_1s_tx_power_index_B[group];
+ if (ht40)
+ mcsbase += priv->ht40_tx_power_diff[tx_idx++].b;
+ else
+ mcsbase += priv->ht20_tx_power_diff[tx_idx++].b;
+ mcs = mcsbase | mcsbase << 8 | mcsbase << 16 | mcsbase << 24;
+
+ rtl8xxxu_write32(priv, REG_TX_AGC_A_MCS03_MCS00, mcs);
+ rtl8xxxu_write32(priv, REG_TX_AGC_A_MCS07_MCS04, mcs);
+}
+
static void rtl8xxxu_set_linktype(struct rtl8xxxu_priv *priv,
enum nl80211_iftype linktype)
{
- u16 val8;
+ u8 val8;
- val8 = rtl8xxxu_read16(priv, REG_MSR);
+ val8 = rtl8xxxu_read8(priv, REG_MSR);
val8 &= ~MSR_LINKTYPE_MASK;
switch (linktype) {
@@ -1662,16 +2160,24 @@
case 1:
cut = "B";
break;
+ case 2:
+ cut = "C";
+ break;
+ case 3:
+ cut = "D";
+ break;
+ case 4:
+ cut = "E";
+ break;
default:
cut = "unknown";
}
dev_info(dev,
"RTL%s rev %s (%s) %iT%iR, TX queues %i, WiFi=%i, BT=%i, GPS=%i, HI PA=%i\n",
- priv->chip_name, cut, priv->vendor_umc ? "UMC" : "TSMC",
- priv->tx_paths, priv->rx_paths, priv->ep_tx_count,
- priv->has_wifi, priv->has_bluetooth, priv->has_gps,
- priv->hi_pa);
+ priv->chip_name, cut, priv->chip_vendor, priv->tx_paths,
+ priv->rx_paths, priv->ep_tx_count, priv->has_wifi,
+ priv->has_bluetooth, priv->has_gps, priv->hi_pa);
dev_info(dev, "RTL%s MAC: %pM\n", priv->chip_name, priv->mac_addr);
}
@@ -1691,11 +2197,18 @@
}
if (val32 & SYS_CFG_BT_FUNC) {
- sprintf(priv->chip_name, "8723AU");
+ if (priv->chip_cut >= 3) {
+ sprintf(priv->chip_name, "8723BU");
+ priv->rtlchip = 0x8723b;
+ } else {
+ sprintf(priv->chip_name, "8723AU");
+ priv->usb_interrupts = 1;
+ priv->rtlchip = 0x8723a;
+ }
+
priv->rf_paths = 1;
priv->rx_paths = 1;
priv->tx_paths = 1;
- priv->rtlchip = 0x8723a;
val32 = rtl8xxxu_read32(priv, REG_MULTI_FUNC_CTRL);
if (val32 & MULTI_WIFI_FUNC_EN)
@@ -1704,20 +2217,37 @@
priv->has_bluetooth = 1;
if (val32 & MULTI_GPS_FUNC_EN)
priv->has_gps = 1;
+ priv->is_multi_func = 1;
} else if (val32 & SYS_CFG_TYPE_ID) {
bonding = rtl8xxxu_read32(priv, REG_HPON_FSM);
bonding &= HPON_FSM_BONDING_MASK;
- if (bonding == HPON_FSM_BONDING_1T2R) {
+ if (priv->chip_cut >= 3) {
+ if (bonding == HPON_FSM_BONDING_1T2R) {
+ sprintf(priv->chip_name, "8191EU");
+ priv->rf_paths = 2;
+ priv->rx_paths = 2;
+ priv->tx_paths = 1;
+ priv->rtlchip = 0x8191e;
+ } else {
+ sprintf(priv->chip_name, "8192EU");
+ priv->rf_paths = 2;
+ priv->rx_paths = 2;
+ priv->tx_paths = 2;
+ priv->rtlchip = 0x8192e;
+ }
+ } else if (bonding == HPON_FSM_BONDING_1T2R) {
sprintf(priv->chip_name, "8191CU");
priv->rf_paths = 2;
priv->rx_paths = 2;
priv->tx_paths = 1;
+ priv->usb_interrupts = 1;
priv->rtlchip = 0x8191c;
} else {
sprintf(priv->chip_name, "8192CU");
priv->rf_paths = 2;
priv->rx_paths = 2;
priv->tx_paths = 2;
+ priv->usb_interrupts = 1;
priv->rtlchip = 0x8192c;
}
priv->has_wifi = 1;
@@ -1727,11 +2257,38 @@
priv->rx_paths = 1;
priv->tx_paths = 1;
priv->rtlchip = 0x8188c;
+ priv->usb_interrupts = 1;
priv->has_wifi = 1;
}
- if (val32 & SYS_CFG_VENDOR_ID)
- priv->vendor_umc = 1;
+ switch (priv->rtlchip) {
+ case 0x8188e:
+ case 0x8192e:
+ case 0x8723b:
+ switch (val32 & SYS_CFG_VENDOR_EXT_MASK) {
+ case SYS_CFG_VENDOR_ID_TSMC:
+ sprintf(priv->chip_vendor, "TSMC");
+ break;
+ case SYS_CFG_VENDOR_ID_SMIC:
+ sprintf(priv->chip_vendor, "SMIC");
+ priv->vendor_smic = 1;
+ break;
+ case SYS_CFG_VENDOR_ID_UMC:
+ sprintf(priv->chip_vendor, "UMC");
+ priv->vendor_umc = 1;
+ break;
+ default:
+ sprintf(priv->chip_vendor, "unknown");
+ }
+ break;
+ default:
+ if (val32 & SYS_CFG_VENDOR_ID) {
+ sprintf(priv->chip_vendor, "UMC");
+ priv->vendor_umc = 1;
+ } else {
+ sprintf(priv->chip_vendor, "TSMC");
+ }
+ }
val32 = rtl8xxxu_read32(priv, REG_GPIO_OUTSTS);
priv->rom_rev = (val32 & GPIO_RF_RL_ID) >> 28;
@@ -1757,6 +2314,7 @@
*/
if (!priv->ep_tx_count) {
switch (priv->nr_out_eps) {
+ case 4:
case 3:
priv->ep_tx_low_queue = 1;
priv->ep_tx_count++;
@@ -1778,43 +2336,126 @@
static int rtl8723au_parse_efuse(struct rtl8xxxu_priv *priv)
{
- if (priv->efuse_wifi.efuse8723.rtl_id != cpu_to_le16(0x8129))
+ struct rtl8723au_efuse *efuse = &priv->efuse_wifi.efuse8723;
+
+ if (efuse->rtl_id != cpu_to_le16(0x8129))
return -EINVAL;
- ether_addr_copy(priv->mac_addr, priv->efuse_wifi.efuse8723.mac_addr);
+ ether_addr_copy(priv->mac_addr, efuse->mac_addr);
memcpy(priv->cck_tx_power_index_A,
- priv->efuse_wifi.efuse8723.cck_tx_power_index_A,
- sizeof(priv->cck_tx_power_index_A));
+ efuse->cck_tx_power_index_A,
+ sizeof(efuse->cck_tx_power_index_A));
memcpy(priv->cck_tx_power_index_B,
- priv->efuse_wifi.efuse8723.cck_tx_power_index_B,
- sizeof(priv->cck_tx_power_index_B));
+ efuse->cck_tx_power_index_B,
+ sizeof(efuse->cck_tx_power_index_B));
memcpy(priv->ht40_1s_tx_power_index_A,
- priv->efuse_wifi.efuse8723.ht40_1s_tx_power_index_A,
- sizeof(priv->ht40_1s_tx_power_index_A));
+ efuse->ht40_1s_tx_power_index_A,
+ sizeof(efuse->ht40_1s_tx_power_index_A));
memcpy(priv->ht40_1s_tx_power_index_B,
- priv->efuse_wifi.efuse8723.ht40_1s_tx_power_index_B,
- sizeof(priv->ht40_1s_tx_power_index_B));
+ efuse->ht40_1s_tx_power_index_B,
+ sizeof(efuse->ht40_1s_tx_power_index_B));
memcpy(priv->ht20_tx_power_index_diff,
- priv->efuse_wifi.efuse8723.ht20_tx_power_index_diff,
- sizeof(priv->ht20_tx_power_index_diff));
+ efuse->ht20_tx_power_index_diff,
+ sizeof(efuse->ht20_tx_power_index_diff));
memcpy(priv->ofdm_tx_power_index_diff,
- priv->efuse_wifi.efuse8723.ofdm_tx_power_index_diff,
- sizeof(priv->ofdm_tx_power_index_diff));
+ efuse->ofdm_tx_power_index_diff,
+ sizeof(efuse->ofdm_tx_power_index_diff));
memcpy(priv->ht40_max_power_offset,
- priv->efuse_wifi.efuse8723.ht40_max_power_offset,
- sizeof(priv->ht40_max_power_offset));
+ efuse->ht40_max_power_offset,
+ sizeof(efuse->ht40_max_power_offset));
memcpy(priv->ht20_max_power_offset,
- priv->efuse_wifi.efuse8723.ht20_max_power_offset,
- sizeof(priv->ht20_max_power_offset));
+ efuse->ht20_max_power_offset,
+ sizeof(efuse->ht20_max_power_offset));
+ if (priv->efuse_wifi.efuse8723.version >= 0x01) {
+ priv->has_xtalk = 1;
+ priv->xtalk = priv->efuse_wifi.efuse8723.xtal_k & 0x3f;
+ }
dev_info(&priv->udev->dev, "Vendor: %.7s\n",
- priv->efuse_wifi.efuse8723.vendor_name);
+ efuse->vendor_name);
dev_info(&priv->udev->dev, "Product: %.41s\n",
- priv->efuse_wifi.efuse8723.device_name);
+ efuse->device_name);
+ return 0;
+}
+
+static int rtl8723bu_parse_efuse(struct rtl8xxxu_priv *priv)
+{
+ struct rtl8723bu_efuse *efuse = &priv->efuse_wifi.efuse8723bu;
+ int i;
+
+ if (efuse->rtl_id != cpu_to_le16(0x8129))
+ return -EINVAL;
+
+ ether_addr_copy(priv->mac_addr, efuse->mac_addr);
+
+ memcpy(priv->cck_tx_power_index_A, efuse->tx_power_index_A.cck_base,
+ sizeof(efuse->tx_power_index_A.cck_base));
+ memcpy(priv->cck_tx_power_index_B, efuse->tx_power_index_B.cck_base,
+ sizeof(efuse->tx_power_index_B.cck_base));
+
+ memcpy(priv->ht40_1s_tx_power_index_A,
+ efuse->tx_power_index_A.ht40_base,
+ sizeof(efuse->tx_power_index_A.ht40_base));
+ memcpy(priv->ht40_1s_tx_power_index_B,
+ efuse->tx_power_index_B.ht40_base,
+ sizeof(efuse->tx_power_index_B.ht40_base));
+
+ priv->ofdm_tx_power_diff[0].a =
+ efuse->tx_power_index_A.ht20_ofdm_1s_diff.a;
+ priv->ofdm_tx_power_diff[0].b =
+ efuse->tx_power_index_B.ht20_ofdm_1s_diff.a;
+
+ priv->ht20_tx_power_diff[0].a =
+ efuse->tx_power_index_A.ht20_ofdm_1s_diff.b;
+ priv->ht20_tx_power_diff[0].b =
+ efuse->tx_power_index_B.ht20_ofdm_1s_diff.b;
+
+ priv->ht40_tx_power_diff[0].a = 0;
+ priv->ht40_tx_power_diff[0].b = 0;
+
+ for (i = 1; i < RTL8723B_TX_COUNT; i++) {
+ priv->ofdm_tx_power_diff[i].a =
+ efuse->tx_power_index_A.pwr_diff[i - 1].ofdm;
+ priv->ofdm_tx_power_diff[i].b =
+ efuse->tx_power_index_B.pwr_diff[i - 1].ofdm;
+
+ priv->ht20_tx_power_diff[i].a =
+ efuse->tx_power_index_A.pwr_diff[i - 1].ht20;
+ priv->ht20_tx_power_diff[i].b =
+ efuse->tx_power_index_B.pwr_diff[i - 1].ht20;
+
+ priv->ht40_tx_power_diff[i].a =
+ efuse->tx_power_index_A.pwr_diff[i - 1].ht40;
+ priv->ht40_tx_power_diff[i].b =
+ efuse->tx_power_index_B.pwr_diff[i - 1].ht40;
+ }
+
+ priv->has_xtalk = 1;
+ priv->xtalk = priv->efuse_wifi.efuse8723bu.xtal_k & 0x3f;
+
+ dev_info(&priv->udev->dev, "Vendor: %.7s\n", efuse->vendor_name);
+ dev_info(&priv->udev->dev, "Product: %.41s\n", efuse->device_name);
+
+ if (rtl8xxxu_debug & RTL8XXXU_DEBUG_EFUSE) {
+ int i;
+ unsigned char *raw = priv->efuse_wifi.raw;
+
+ dev_info(&priv->udev->dev,
+ "%s: dumping efuse (0x%02zx bytes):\n",
+ __func__, sizeof(struct rtl8723bu_efuse));
+ for (i = 0; i < sizeof(struct rtl8723bu_efuse); i += 8) {
+ dev_info(&priv->udev->dev, "%02x: "
+ "%02x %02x %02x %02x %02x %02x %02x %02x\n", i,
+ raw[i], raw[i + 1], raw[i + 2],
+ raw[i + 3], raw[i + 4], raw[i + 5],
+ raw[i + 6], raw[i + 7]);
+ }
+ }
+
return 0;
}
@@ -1822,50 +2463,51 @@
static int rtl8192cu_parse_efuse(struct rtl8xxxu_priv *priv)
{
+ struct rtl8192cu_efuse *efuse = &priv->efuse_wifi.efuse8192;
int i;
- if (priv->efuse_wifi.efuse8192.rtl_id != cpu_to_le16(0x8129))
+ if (efuse->rtl_id != cpu_to_le16(0x8129))
return -EINVAL;
- ether_addr_copy(priv->mac_addr, priv->efuse_wifi.efuse8192.mac_addr);
+ ether_addr_copy(priv->mac_addr, efuse->mac_addr);
memcpy(priv->cck_tx_power_index_A,
- priv->efuse_wifi.efuse8192.cck_tx_power_index_A,
- sizeof(priv->cck_tx_power_index_A));
+ efuse->cck_tx_power_index_A,
+ sizeof(efuse->cck_tx_power_index_A));
memcpy(priv->cck_tx_power_index_B,
- priv->efuse_wifi.efuse8192.cck_tx_power_index_B,
- sizeof(priv->cck_tx_power_index_B));
+ efuse->cck_tx_power_index_B,
+ sizeof(efuse->cck_tx_power_index_B));
memcpy(priv->ht40_1s_tx_power_index_A,
- priv->efuse_wifi.efuse8192.ht40_1s_tx_power_index_A,
- sizeof(priv->ht40_1s_tx_power_index_A));
+ efuse->ht40_1s_tx_power_index_A,
+ sizeof(efuse->ht40_1s_tx_power_index_A));
memcpy(priv->ht40_1s_tx_power_index_B,
- priv->efuse_wifi.efuse8192.ht40_1s_tx_power_index_B,
- sizeof(priv->ht40_1s_tx_power_index_B));
+ efuse->ht40_1s_tx_power_index_B,
+ sizeof(efuse->ht40_1s_tx_power_index_B));
memcpy(priv->ht40_2s_tx_power_index_diff,
- priv->efuse_wifi.efuse8192.ht40_2s_tx_power_index_diff,
- sizeof(priv->ht40_2s_tx_power_index_diff));
+ efuse->ht40_2s_tx_power_index_diff,
+ sizeof(efuse->ht40_2s_tx_power_index_diff));
memcpy(priv->ht20_tx_power_index_diff,
- priv->efuse_wifi.efuse8192.ht20_tx_power_index_diff,
- sizeof(priv->ht20_tx_power_index_diff));
+ efuse->ht20_tx_power_index_diff,
+ sizeof(efuse->ht20_tx_power_index_diff));
memcpy(priv->ofdm_tx_power_index_diff,
- priv->efuse_wifi.efuse8192.ofdm_tx_power_index_diff,
- sizeof(priv->ofdm_tx_power_index_diff));
+ efuse->ofdm_tx_power_index_diff,
+ sizeof(efuse->ofdm_tx_power_index_diff));
memcpy(priv->ht40_max_power_offset,
- priv->efuse_wifi.efuse8192.ht40_max_power_offset,
- sizeof(priv->ht40_max_power_offset));
+ efuse->ht40_max_power_offset,
+ sizeof(efuse->ht40_max_power_offset));
memcpy(priv->ht20_max_power_offset,
- priv->efuse_wifi.efuse8192.ht20_max_power_offset,
- sizeof(priv->ht20_max_power_offset));
+ efuse->ht20_max_power_offset,
+ sizeof(efuse->ht20_max_power_offset));
dev_info(&priv->udev->dev, "Vendor: %.7s\n",
- priv->efuse_wifi.efuse8192.vendor_name);
+ efuse->vendor_name);
dev_info(&priv->udev->dev, "Product: %.20s\n",
- priv->efuse_wifi.efuse8192.device_name);
+ efuse->device_name);
- if (priv->efuse_wifi.efuse8192.rf_regulatory & 0x20) {
+ if (efuse->rf_regulatory & 0x20) {
sprintf(priv->chip_name, "8188RU");
priv->hi_pa = 1;
}
@@ -1889,6 +2531,44 @@
#endif
+static int rtl8192eu_parse_efuse(struct rtl8xxxu_priv *priv)
+{
+ struct rtl8192eu_efuse *efuse = &priv->efuse_wifi.efuse8192eu;
+ int i;
+
+ if (efuse->rtl_id != cpu_to_le16(0x8129))
+ return -EINVAL;
+
+ ether_addr_copy(priv->mac_addr, efuse->mac_addr);
+
+ priv->has_xtalk = 1;
+ priv->xtalk = priv->efuse_wifi.efuse8192eu.xtal_k & 0x3f;
+
+ dev_info(&priv->udev->dev, "Vendor: %.7s\n", efuse->vendor_name);
+ dev_info(&priv->udev->dev, "Product: %.11s\n", efuse->device_name);
+ dev_info(&priv->udev->dev, "Serial: %.11s\n", efuse->serial);
+
+ if (rtl8xxxu_debug & RTL8XXXU_DEBUG_EFUSE) {
+ unsigned char *raw = priv->efuse_wifi.raw;
+
+ dev_info(&priv->udev->dev,
+ "%s: dumping efuse (0x%02zx bytes):\n",
+ __func__, sizeof(struct rtl8192eu_efuse));
+ for (i = 0; i < sizeof(struct rtl8192eu_efuse); i += 8) {
+ dev_info(&priv->udev->dev, "%02x: "
+ "%02x %02x %02x %02x %02x %02x %02x %02x\n", i,
+ raw[i], raw[i + 1], raw[i + 2],
+ raw[i + 3], raw[i + 4], raw[i + 5],
+ raw[i + 6], raw[i + 7]);
+ }
+ }
+ /*
+ * Temporarily disable 8192eu support
+ */
+ return -EINVAL;
+ return 0;
+}
+
static int
rtl8xxxu_read_efuse8(struct rtl8xxxu_priv *priv, u16 offset, u8 *data)
{
@@ -1938,9 +2618,11 @@
if (val16 & EEPROM_BOOT)
priv->boot_eeprom = 1;
- val32 = rtl8xxxu_read32(priv, REG_EFUSE_TEST);
- val32 = (val32 & ~EFUSE_SELECT_MASK) | EFUSE_WIFI_SELECT;
- rtl8xxxu_write32(priv, REG_EFUSE_TEST, val32);
+ if (priv->is_multi_func) {
+ val32 = rtl8xxxu_read32(priv, REG_EFUSE_TEST);
+ val32 = (val32 & ~EFUSE_SELECT_MASK) | EFUSE_WIFI_SELECT;
+ rtl8xxxu_write32(priv, REG_EFUSE_TEST, val32);
+ }
dev_dbg(dev, "Booting from %s\n",
priv->boot_eeprom ? "EEPROM" : "EFUSE");
@@ -1970,10 +2652,12 @@
}
/* Default value is 0xff */
- memset(priv->efuse_wifi.raw, 0xff, EFUSE_MAP_LEN_8723A);
+ memset(priv->efuse_wifi.raw, 0xff, EFUSE_MAP_LEN);
efuse_addr = 0;
while (efuse_addr < EFUSE_REAL_CONTENT_LEN_8723A) {
+ u16 map_addr;
+
ret = rtl8xxxu_read_efuse8(priv, efuse_addr++, &header);
if (ret || header == 0xff)
goto exit;
@@ -1996,45 +2680,34 @@
word_mask = header & 0x0f;
}
- if (offset < EFUSE_MAX_SECTION_8723A) {
- u16 map_addr;
- /* Get word enable value from PG header */
+ /* Get word enable value from PG header */
- /* We have 8 bits to indicate validity */
- map_addr = offset * 8;
- if (map_addr >= EFUSE_MAP_LEN_8723A) {
- dev_warn(dev, "%s: Illegal map_addr (%04x), "
- "efuse corrupt!\n",
- __func__, map_addr);
- ret = -EINVAL;
- goto exit;
- }
- for (i = 0; i < EFUSE_MAX_WORD_UNIT; i++) {
- /* Check word enable condition in the section */
- if (!(word_mask & BIT(i))) {
- ret = rtl8xxxu_read_efuse8(priv,
- efuse_addr++,
- &val8);
- if (ret)
- goto exit;
- priv->efuse_wifi.raw[map_addr++] = val8;
-
- ret = rtl8xxxu_read_efuse8(priv,
- efuse_addr++,
- &val8);
- if (ret)
- goto exit;
- priv->efuse_wifi.raw[map_addr++] = val8;
- } else
- map_addr += 2;
- }
- } else {
- dev_warn(dev,
- "%s: Illegal offset (%04x), efuse corrupt!\n",
- __func__, offset);
+ /* We have 8 bits to indicate validity */
+ map_addr = offset * 8;
+ if (map_addr >= EFUSE_MAP_LEN) {
+ dev_warn(dev, "%s: Illegal map_addr (%04x), "
+ "efuse corrupt!\n",
+ __func__, map_addr);
ret = -EINVAL;
goto exit;
}
+ for (i = 0; i < EFUSE_MAX_WORD_UNIT; i++) {
+ /* Check word enable condition in the section */
+ if (word_mask & BIT(i)) {
+ map_addr += 2;
+ continue;
+ }
+
+ ret = rtl8xxxu_read_efuse8(priv, efuse_addr++, &val8);
+ if (ret)
+ goto exit;
+ priv->efuse_wifi.raw[map_addr++] = val8;
+
+ ret = rtl8xxxu_read_efuse8(priv, efuse_addr++, &val8);
+ if (ret)
+ goto exit;
+ priv->efuse_wifi.raw[map_addr++] = val8;
+ }
}
exit:
@@ -2043,6 +2716,56 @@
return ret;
}
+static void rtl8xxxu_reset_8051(struct rtl8xxxu_priv *priv)
+{
+ u8 val8;
+ u16 sys_func;
+
+ val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL + 1);
+ val8 &= ~BIT(0);
+ rtl8xxxu_write8(priv, REG_RSV_CTRL + 1, val8);
+
+ sys_func = rtl8xxxu_read16(priv, REG_SYS_FUNC);
+ sys_func &= ~SYS_FUNC_CPU_ENABLE;
+ rtl8xxxu_write16(priv, REG_SYS_FUNC, sys_func);
+
+ val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL + 1);
+ val8 |= BIT(0);
+ rtl8xxxu_write8(priv, REG_RSV_CTRL + 1, val8);
+
+ sys_func |= SYS_FUNC_CPU_ENABLE;
+ rtl8xxxu_write16(priv, REG_SYS_FUNC, sys_func);
+}
+
+static void rtl8723bu_reset_8051(struct rtl8xxxu_priv *priv)
+{
+ u8 val8;
+ u16 sys_func;
+
+ val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL);
+ val8 &= ~BIT(1);
+ rtl8xxxu_write8(priv, REG_RSV_CTRL, val8);
+
+ val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL + 1);
+ val8 &= ~BIT(0);
+ rtl8xxxu_write8(priv, REG_RSV_CTRL + 1, val8);
+
+ sys_func = rtl8xxxu_read16(priv, REG_SYS_FUNC);
+ sys_func &= ~SYS_FUNC_CPU_ENABLE;
+ rtl8xxxu_write16(priv, REG_SYS_FUNC, sys_func);
+
+ val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL);
+ val8 &= ~BIT(1);
+ rtl8xxxu_write8(priv, REG_RSV_CTRL, val8);
+
+ val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL + 1);
+ val8 |= BIT(0);
+ rtl8xxxu_write8(priv, REG_RSV_CTRL + 1, val8);
+
+ sys_func |= SYS_FUNC_CPU_ENABLE;
+ rtl8xxxu_write16(priv, REG_SYS_FUNC, sys_func);
+}
+
static int rtl8xxxu_start_firmware(struct rtl8xxxu_priv *priv)
{
struct device *dev = &priv->udev->dev;
@@ -2067,6 +2790,12 @@
val32 &= ~MCU_WINT_INIT_READY;
rtl8xxxu_write32(priv, REG_MCU_FW_DL, val32);
+ /*
+ * Reset the 8051 in order for the firmware to start running,
+ * otherwise it won't come up on the 8192eu
+ */
+ priv->fops->reset_8051(priv);
+
/* Wait for firmware to become ready */
for (i = 0; i < RTL8XXXU_FIRMWARE_POLL_MAX; i++) {
val32 = rtl8xxxu_read32(priv, REG_MCU_FW_DL);
@@ -2082,6 +2811,11 @@
goto exit;
}
+ /*
+ * Init H2C command
+ */
+ if (priv->rtlchip == 0x8723b)
+ rtl8xxxu_write8(priv, REG_HMTFR, 0x0f);
exit:
return ret;
}
@@ -2100,19 +2834,30 @@
/* 8051 enable */
val16 = rtl8xxxu_read16(priv, REG_SYS_FUNC);
- rtl8xxxu_write16(priv, REG_SYS_FUNC, val16 | SYS_FUNC_CPU_ENABLE);
+ val16 |= SYS_FUNC_CPU_ENABLE;
+ rtl8xxxu_write16(priv, REG_SYS_FUNC, val16);
+
+ val8 = rtl8xxxu_read8(priv, REG_MCU_FW_DL);
+ if (val8 & MCU_FW_RAM_SEL) {
+ pr_info("do the RAM reset\n");
+ rtl8xxxu_write8(priv, REG_MCU_FW_DL, 0x00);
+ priv->fops->reset_8051(priv);
+ }
/* MCU firmware download enable */
val8 = rtl8xxxu_read8(priv, REG_MCU_FW_DL);
- rtl8xxxu_write8(priv, REG_MCU_FW_DL, val8 | MCU_FW_DL_ENABLE);
+ val8 |= MCU_FW_DL_ENABLE;
+ rtl8xxxu_write8(priv, REG_MCU_FW_DL, val8);
/* 8051 reset */
val32 = rtl8xxxu_read32(priv, REG_MCU_FW_DL);
- rtl8xxxu_write32(priv, REG_MCU_FW_DL, val32 & ~BIT(19));
+ val32 &= ~BIT(19);
+ rtl8xxxu_write32(priv, REG_MCU_FW_DL, val32);
/* Reset firmware download checksum */
val8 = rtl8xxxu_read8(priv, REG_MCU_FW_DL);
- rtl8xxxu_write8(priv, REG_MCU_FW_DL, val8 | MCU_FW_DL_CSUM_REPORT);
+ val8 |= MCU_FW_DL_CSUM_REPORT;
+ rtl8xxxu_write8(priv, REG_MCU_FW_DL, val8);
pages = priv->fw_size / RTL_FW_PAGE_SIZE;
remainder = priv->fw_size % RTL_FW_PAGE_SIZE;
@@ -2121,7 +2866,8 @@
for (i = 0; i < pages; i++) {
val8 = rtl8xxxu_read8(priv, REG_MCU_FW_DL + 2) & 0xF8;
- rtl8xxxu_write8(priv, REG_MCU_FW_DL + 2, val8 | i);
+ val8 |= i;
+ rtl8xxxu_write8(priv, REG_MCU_FW_DL + 2, val8);
ret = rtl8xxxu_writeN(priv, REG_FW_START_ADDRESS,
fwptr, RTL_FW_PAGE_SIZE);
@@ -2135,7 +2881,8 @@
if (remainder) {
val8 = rtl8xxxu_read8(priv, REG_MCU_FW_DL + 2) & 0xF8;
- rtl8xxxu_write8(priv, REG_MCU_FW_DL + 2, val8 | i);
+ val8 |= i;
+ rtl8xxxu_write8(priv, REG_MCU_FW_DL + 2, val8);
ret = rtl8xxxu_writeN(priv, REG_FW_START_ADDRESS,
fwptr, remainder);
if (ret != remainder) {
@@ -2148,8 +2895,8 @@
fw_abort:
/* MCU firmware download disable */
val16 = rtl8xxxu_read16(priv, REG_MCU_FW_DL);
- rtl8xxxu_write16(priv, REG_MCU_FW_DL,
- val16 & (~MCU_FW_DL_ENABLE & 0xff));
+ val16 &= ~MCU_FW_DL_ENABLE;
+ rtl8xxxu_write16(priv, REG_MCU_FW_DL, val16);
return ret;
}
@@ -2174,12 +2921,18 @@
}
priv->fw_data = kmemdup(fw->data, fw->size, GFP_KERNEL);
+ if (!priv->fw_data) {
+ ret = -ENOMEM;
+ goto exit;
+ }
priv->fw_size = fw->size - sizeof(struct rtl8xxxu_firmware_header);
signature = le16_to_cpu(priv->fw_data->signature);
switch (signature & 0xfff0) {
+ case 0x92e0:
case 0x92c0:
case 0x88c0:
+ case 0x5300:
case 0x2300:
break;
default:
@@ -2221,6 +2974,20 @@
return ret;
}
+static int rtl8723bu_load_firmware(struct rtl8xxxu_priv *priv)
+{
+ char *fw_name;
+ int ret;
+
+ if (priv->enable_bluetooth)
+ fw_name = "rtlwifi/rtl8723bu_bt.bin";
+ else
+ fw_name = "rtlwifi/rtl8723bu_nic.bin";
+
+ ret = rtl8xxxu_load_firmware(priv, fw_name);
+ return ret;
+}
+
#ifdef CONFIG_RTL8XXXU_UNTESTED
static int rtl8192cu_load_firmware(struct rtl8xxxu_priv *priv)
@@ -2242,6 +3009,18 @@
#endif
+static int rtl8192eu_load_firmware(struct rtl8xxxu_priv *priv)
+{
+ char *fw_name;
+ int ret;
+
+ fw_name = "rtlwifi/rtl8192eu_nic.bin";
+
+ ret = rtl8xxxu_load_firmware(priv, fw_name);
+
+ return ret;
+}
+
static void rtl8xxxu_firmware_self_reset(struct rtl8xxxu_priv *priv)
{
u16 val16;
@@ -2269,6 +3048,44 @@
}
}
+static void rtl8723bu_phy_init_antenna_selection(struct rtl8xxxu_priv *priv)
+{
+ u32 val32;
+
+ val32 = rtl8xxxu_read32(priv, 0x64);
+ val32 &= ~(BIT(20) | BIT(24));
+ rtl8xxxu_write32(priv, 0x64, val32);
+
+ val32 = rtl8xxxu_read32(priv, REG_GPIO_MUXCFG);
+ val32 &= ~BIT(4);
+ rtl8xxxu_write32(priv, REG_GPIO_MUXCFG, val32);
+
+ val32 = rtl8xxxu_read32(priv, REG_GPIO_MUXCFG);
+ val32 |= BIT(3);
+ rtl8xxxu_write32(priv, REG_GPIO_MUXCFG, val32);
+
+ val32 = rtl8xxxu_read32(priv, REG_LEDCFG0);
+ val32 |= BIT(24);
+ rtl8xxxu_write32(priv, REG_LEDCFG0, val32);
+
+ val32 = rtl8xxxu_read32(priv, REG_LEDCFG0);
+ val32 &= ~BIT(23);
+ rtl8xxxu_write32(priv, REG_LEDCFG0, val32);
+
+ val32 = rtl8xxxu_read32(priv, REG_RFE_BUFFER);
+ val32 |= (BIT(0) | BIT(1));
+ rtl8xxxu_write32(priv, REG_RFE_BUFFER, val32);
+
+ val32 = rtl8xxxu_read32(priv, REG_RFE_CTRL_ANTA_SRC);
+ val32 &= 0xffffff00;
+ val32 |= 0x77;
+ rtl8xxxu_write32(priv, REG_RFE_CTRL_ANTA_SRC, val32);
+
+ val32 = rtl8xxxu_read32(priv, REG_PWR_DATA);
+ val32 |= PWR_DATA_EEPRPAD_RFE_CTRL_EN;
+ rtl8xxxu_write32(priv, REG_PWR_DATA, val32);
+}
+
static int
rtl8xxxu_init_mac(struct rtl8xxxu_priv *priv, struct rtl8xxxu_reg8val *array)
{
@@ -2291,7 +3108,8 @@
}
}
- rtl8xxxu_write8(priv, REG_MAX_AGGR_NUM, 0x0a);
+ if (priv->rtlchip != 0x8723b)
+ rtl8xxxu_write8(priv, REG_MAX_AGGR_NUM, 0x0a);
return 0;
}
@@ -2328,6 +3146,7 @@
static int rtl8xxxu_init_phy_bb(struct rtl8xxxu_priv *priv)
{
u8 val8, ldoa15, ldov12d, lpldo, ldohci12;
+ u16 val16;
u32 val32;
/*
@@ -2335,25 +3154,36 @@
* addresses, which is initialized here. Do we need this?
*/
- val8 = rtl8xxxu_read8(priv, REG_AFE_PLL_CTRL);
- udelay(2);
- val8 |= AFE_PLL_320_ENABLE;
- rtl8xxxu_write8(priv, REG_AFE_PLL_CTRL, val8);
- udelay(2);
+ if (priv->rtlchip == 0x8723b) {
+ val16 = rtl8xxxu_read16(priv, REG_SYS_FUNC);
+ val16 |= SYS_FUNC_BB_GLB_RSTN | SYS_FUNC_BBRSTB |
+ SYS_FUNC_DIO_RF;
+ rtl8xxxu_write16(priv, REG_SYS_FUNC, val16);
- rtl8xxxu_write8(priv, REG_AFE_PLL_CTRL + 1, 0xff);
- udelay(2);
+ rtl8xxxu_write32(priv, REG_S0S1_PATH_SWITCH, 0x00);
+ } else {
+ val8 = rtl8xxxu_read8(priv, REG_AFE_PLL_CTRL);
+ udelay(2);
+ val8 |= AFE_PLL_320_ENABLE;
+ rtl8xxxu_write8(priv, REG_AFE_PLL_CTRL, val8);
+ udelay(2);
- val8 = rtl8xxxu_read8(priv, REG_SYS_FUNC);
- val8 |= SYS_FUNC_BB_GLB_RSTN | SYS_FUNC_BBRSTB;
- rtl8xxxu_write8(priv, REG_SYS_FUNC, val8);
+ rtl8xxxu_write8(priv, REG_AFE_PLL_CTRL + 1, 0xff);
+ udelay(2);
- /* AFE_XTAL_RF_GATE (bit 14) if addressing as 32 bit register */
- val32 = rtl8xxxu_read32(priv, REG_AFE_XTAL_CTRL);
- val32 &= ~AFE_XTAL_RF_GATE;
- if (priv->has_bluetooth)
- val32 &= ~AFE_XTAL_BT_GATE;
- rtl8xxxu_write32(priv, REG_AFE_XTAL_CTRL, val32);
+ val16 = rtl8xxxu_read16(priv, REG_SYS_FUNC);
+ val16 |= SYS_FUNC_BB_GLB_RSTN | SYS_FUNC_BBRSTB;
+ rtl8xxxu_write16(priv, REG_SYS_FUNC, val16);
+ }
+
+ if (priv->rtlchip != 0x8723b) {
+ /* AFE_XTAL_RF_GATE (bit 14) if addressing as 32 bit register */
+ val32 = rtl8xxxu_read32(priv, REG_AFE_XTAL_CTRL);
+ val32 &= ~AFE_XTAL_RF_GATE;
+ if (priv->has_bluetooth)
+ val32 &= ~AFE_XTAL_BT_GATE;
+ rtl8xxxu_write32(priv, REG_AFE_XTAL_CTRL, val32);
+ }
/* 6. 0x1f[7:0] = 0x07 */
val8 = RF_ENABLE | RF_RSTB | RF_SDMRSTB;
@@ -2363,7 +3193,14 @@
rtl8xxxu_init_phy_regs(priv, rtl8188ru_phy_1t_highpa_table);
else if (priv->tx_paths == 2)
rtl8xxxu_init_phy_regs(priv, rtl8192cu_phy_2t_init_table);
- else
+ else if (priv->rtlchip == 0x8723b) {
+ /*
+ * Why?
+ */
+ rtl8xxxu_write8(priv, REG_SYS_FUNC, 0xe3);
+ rtl8xxxu_write8(priv, REG_AFE_XTAL_CTRL + 1, 0x80);
+ rtl8xxxu_init_phy_regs(priv, rtl8723b_phy_1t_init_table);
+ } else
rtl8xxxu_init_phy_regs(priv, rtl8723a_phy_1t_init_table);
@@ -2429,29 +3266,33 @@
rtl8xxxu_write32(priv, REG_TX_TO_TX, val32);
}
- if (priv->hi_pa)
+ if (priv->rtlchip == 0x8723b)
+ rtl8xxxu_init_phy_regs(priv, rtl8xxx_agc_8723bu_table);
+ else if (priv->hi_pa)
rtl8xxxu_init_phy_regs(priv, rtl8xxx_agc_highpa_table);
else
rtl8xxxu_init_phy_regs(priv, rtl8xxx_agc_standard_table);
- if (priv->rtlchip == 0x8723a &&
- priv->efuse_wifi.efuse8723.version >= 0x01) {
+ if (priv->has_xtalk) {
val32 = rtl8xxxu_read32(priv, REG_MAC_PHY_CTRL);
- val8 = priv->efuse_wifi.efuse8723.xtal_k & 0x3f;
+ val8 = priv->xtalk;
val32 &= 0xff000fff;
val32 |= ((val8 | (val8 << 6)) << 12);
rtl8xxxu_write32(priv, REG_MAC_PHY_CTRL, val32);
}
- ldoa15 = LDOA15_ENABLE | LDOA15_OBUF;
- ldov12d = LDOV12D_ENABLE | BIT(2) | (2 << LDOV12D_VADJ_SHIFT);
- ldohci12 = 0x57;
- lpldo = 1;
- val32 = (lpldo << 24) | (ldohci12 << 16) | (ldov12d << 8) | ldoa15;
+ if (priv->rtlchip != 0x8723bu) {
+ ldoa15 = LDOA15_ENABLE | LDOA15_OBUF;
+ ldov12d = LDOV12D_ENABLE | BIT(2) | (2 << LDOV12D_VADJ_SHIFT);
+ ldohci12 = 0x57;
+ lpldo = 1;
+ val32 = (lpldo << 24) | (ldohci12 << 16) |
+ (ldov12d << 8) | ldoa15;
- rtl8xxxu_write32(priv, REG_LDOA15_CTRL, val32);
+ rtl8xxxu_write32(priv, REG_LDOA15_CTRL, val32);
+ }
return 0;
}
@@ -2492,8 +3333,6 @@
continue;
}
- reg &= 0x3f;
-
ret = rtl8xxxu_write_rfreg(priv, path, reg, val);
if (ret) {
dev_warn(&priv->udev->dev,
@@ -2623,6 +3462,31 @@
return ret;
}
+static int rtl8xxxu_auto_llt_table(struct rtl8xxxu_priv *priv, u8 last_tx_page)
+{
+ u32 val32;
+ int ret = 0;
+ int i;
+
+ val32 = rtl8xxxu_read32(priv, REG_AUTO_LLT);
+ val32 |= AUTO_LLT_INIT_LLT;
+ rtl8xxxu_write32(priv, REG_AUTO_LLT, val32);
+
+ for (i = 500; i; i--) {
+ val32 = rtl8xxxu_read32(priv, REG_AUTO_LLT);
+ if (!(val32 & AUTO_LLT_INIT_LLT))
+ break;
+ usleep_range(2, 4);
+ }
+
+ if (!i) {
+ ret = -EBUSY;
+ dev_warn(&priv->udev->dev, "LLT table init failed\n");
+ }
+
+ return ret;
+}
+
static int rtl8xxxu_init_queue_priority(struct rtl8xxxu_priv *priv)
{
u16 val16, hi, lo;
@@ -2954,6 +3818,91 @@
return false;
}
+static bool rtl8723bu_simularity_compare(struct rtl8xxxu_priv *priv,
+ int result[][8], int c1, int c2)
+{
+ u32 i, j, diff, simubitmap, bound = 0;
+ int candidate[2] = {-1, -1}; /* for path A and path B */
+ int tmp1, tmp2;
+ bool retval = true;
+
+ if (priv->tx_paths > 1)
+ bound = 8;
+ else
+ bound = 4;
+
+ simubitmap = 0;
+
+ for (i = 0; i < bound; i++) {
+ if (i & 1) {
+ if ((result[c1][i] & 0x00000200))
+ tmp1 = result[c1][i] | 0xfffffc00;
+ else
+ tmp1 = result[c1][i];
+
+ if ((result[c2][i]& 0x00000200))
+ tmp2 = result[c2][i] | 0xfffffc00;
+ else
+ tmp2 = result[c2][i];
+ } else {
+ tmp1 = result[c1][i];
+ tmp2 = result[c2][i];
+ }
+
+ diff = (tmp1 > tmp2) ? (tmp1 - tmp2) : (tmp2 - tmp1);
+
+ if (diff > MAX_TOLERANCE) {
+ if ((i == 2 || i == 6) && !simubitmap) {
+ if (result[c1][i] + result[c1][i + 1] == 0)
+ candidate[(i / 4)] = c2;
+ else if (result[c2][i] + result[c2][i + 1] == 0)
+ candidate[(i / 4)] = c1;
+ else
+ simubitmap = simubitmap | (1 << i);
+ } else {
+ simubitmap = simubitmap | (1 << i);
+ }
+ }
+ }
+
+ if (simubitmap == 0) {
+ for (i = 0; i < (bound / 4); i++) {
+ if (candidate[i] >= 0) {
+ for (j = i * 4; j < (i + 1) * 4 - 2; j++)
+ result[3][j] = result[candidate[i]][j];
+ retval = false;
+ }
+ }
+ return retval;
+ } else {
+ if (!(simubitmap & 0x03)) {
+ /* path A TX OK */
+ for (i = 0; i < 2; i++)
+ result[3][i] = result[c1][i];
+ }
+
+ if (!(simubitmap & 0x0c)) {
+ /* path A RX OK */
+ for (i = 2; i < 4; i++)
+ result[3][i] = result[c1][i];
+ }
+
+ if (!(simubitmap & 0x30) && priv->tx_paths > 1) {
+ /* path B RX OK */
+ for (i = 4; i < 6; i++)
+ result[3][i] = result[c1][i];
+ }
+
+ if (!(simubitmap & 0x30) && priv->tx_paths > 1) {
+ /* path B RX OK */
+ for (i = 6; i < 8; i++)
+ result[3][i] = result[c1][i];
+ }
+ }
+
+ return false;
+}
+
static void
rtl8xxxu_save_mac_regs(struct rtl8xxxu_priv *priv, const u32 *reg, u32 *backup)
{
@@ -3001,11 +3950,13 @@
u32 path_on;
int i;
- path_on = path_a_on ? 0x04db25a4 : 0x0b1b25a4;
if (priv->tx_paths == 1) {
- path_on = 0x0bdb25a0;
- rtl8xxxu_write32(priv, regs[0], 0x0b1b25a0);
+ path_on = priv->fops->adda_1t_path_on;
+ rtl8xxxu_write32(priv, regs[0], priv->fops->adda_1t_init);
} else {
+ path_on = path_a_on ? priv->fops->adda_2t_path_on_a :
+ priv->fops->adda_2t_path_on_b;
+
rtl8xxxu_write32(priv, regs[0], path_on);
}
@@ -3119,6 +4070,369 @@
return result;
}
+static int rtl8723bu_iqk_path_a(struct rtl8xxxu_priv *priv)
+{
+ u32 reg_eac, reg_e94, reg_e9c, path_sel, val32;
+ int result = 0;
+
+ path_sel = rtl8xxxu_read32(priv, REG_S0S1_PATH_SWITCH);
+
+ /*
+ * Leave IQK mode
+ */
+ val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK);
+ val32 &= 0x000000ff;
+ rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32);
+
+ /*
+ * Enable path A PA in TX IQK mode
+ */
+ val32 = rtl8xxxu_read_rfreg(priv, RF_A, RF6052_REG_WE_LUT);
+ val32 |= 0x80000;
+ rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_WE_LUT, val32);
+ rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_RCK_OS, 0x20000);
+ rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G1, 0x0003f);
+ rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G2, 0xc7f87);
+
+ /*
+ * Tx IQK setting
+ */
+ rtl8xxxu_write32(priv, REG_TX_IQK, 0x01007c00);
+ rtl8xxxu_write32(priv, REG_RX_IQK, 0x01004800);
+
+ /* path-A IQK setting */
+ rtl8xxxu_write32(priv, REG_TX_IQK_TONE_A, 0x18008c1c);
+ rtl8xxxu_write32(priv, REG_RX_IQK_TONE_A, 0x38008c1c);
+ rtl8xxxu_write32(priv, REG_TX_IQK_TONE_B, 0x38008c1c);
+ rtl8xxxu_write32(priv, REG_RX_IQK_TONE_B, 0x38008c1c);
+
+ rtl8xxxu_write32(priv, REG_TX_IQK_PI_A, 0x821403ea);
+ rtl8xxxu_write32(priv, REG_RX_IQK_PI_A, 0x28110000);
+ rtl8xxxu_write32(priv, REG_TX_IQK_PI_B, 0x82110000);
+ rtl8xxxu_write32(priv, REG_RX_IQK_PI_B, 0x28110000);
+
+ /* LO calibration setting */
+ rtl8xxxu_write32(priv, REG_IQK_AGC_RSP, 0x00462911);
+
+ /*
+ * Enter IQK mode
+ */
+ val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK);
+ val32 &= 0x000000ff;
+ val32 |= 0x80800000;
+ rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32);
+
+ /*
+ * The vendor driver indicates the USB module is always using
+ * S0S1 path 1 for the 8723bu. This may be different for 8192eu
+ */
+ if (priv->rf_paths > 1)
+ rtl8xxxu_write32(priv, REG_S0S1_PATH_SWITCH, 0x00000000);
+ else
+ rtl8xxxu_write32(priv, REG_S0S1_PATH_SWITCH, 0x00000280);
+
+ /*
+ * Bit 12 seems to be BT_GRANT, and is only found in the 8723bu.
+ * No trace of this in the 8192eu or 8188eu vendor drivers.
+ */
+ rtl8xxxu_write32(priv, REG_BT_CONTROL_8723BU, 0x00000800);
+
+ /* One shot, path A LOK & IQK */
+ rtl8xxxu_write32(priv, REG_IQK_AGC_PTS, 0xf9000000);
+ rtl8xxxu_write32(priv, REG_IQK_AGC_PTS, 0xf8000000);
+
+ mdelay(1);
+
+ /* Restore Ant Path */
+ rtl8xxxu_write32(priv, REG_S0S1_PATH_SWITCH, path_sel);
+#ifdef RTL8723BU_BT
+ /* GNT_BT = 1 */
+ rtl8xxxu_write32(priv, REG_BT_CONTROL_8723BU, 0x00001800);
+#endif
+
+ /*
+ * Leave IQK mode
+ */
+ val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK);
+ val32 &= 0x000000ff;
+ rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32);
+
+ /* Check failed */
+ reg_eac = rtl8xxxu_read32(priv, REG_RX_POWER_AFTER_IQK_A_2);
+ reg_e94 = rtl8xxxu_read32(priv, REG_TX_POWER_BEFORE_IQK_A);
+ reg_e9c = rtl8xxxu_read32(priv, REG_TX_POWER_AFTER_IQK_A);
+
+ val32 = (reg_e9c >> 16) & 0x3ff;
+ if (val32 & 0x200)
+ val32 = 0x400 - val32;
+
+ if (!(reg_eac & BIT(28)) &&
+ ((reg_e94 & 0x03ff0000) != 0x01420000) &&
+ ((reg_e9c & 0x03ff0000) != 0x00420000) &&
+ ((reg_e94 & 0x03ff0000) < 0x01100000) &&
+ ((reg_e94 & 0x03ff0000) > 0x00f00000) &&
+ val32 < 0xf)
+ result |= 0x01;
+ else /* If TX not OK, ignore RX */
+ goto out;
+
+out:
+ return result;
+}
+
+static int rtl8723bu_rx_iqk_path_a(struct rtl8xxxu_priv *priv)
+{
+ u32 reg_ea4, reg_eac, reg_e94, reg_e9c, path_sel, val32;
+ int result = 0;
+
+ path_sel = rtl8xxxu_read32(priv, REG_S0S1_PATH_SWITCH);
+
+ /*
+ * Leave IQK mode
+ */
+ val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK);
+ val32 &= 0x000000ff;
+ rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32);
+
+ /*
+ * Enable path A PA in TX IQK mode
+ */
+ val32 = rtl8xxxu_read_rfreg(priv, RF_A, RF6052_REG_WE_LUT);
+ val32 |= 0x80000;
+ rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_WE_LUT, val32);
+ rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_RCK_OS, 0x30000);
+ rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G1, 0x0001f);
+ rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G2, 0xf7fb7);
+
+ /*
+ * Tx IQK setting
+ */
+ rtl8xxxu_write32(priv, REG_TX_IQK, 0x01007c00);
+ rtl8xxxu_write32(priv, REG_RX_IQK, 0x01004800);
+
+ /* path-A IQK setting */
+ rtl8xxxu_write32(priv, REG_TX_IQK_TONE_A, 0x18008c1c);
+ rtl8xxxu_write32(priv, REG_RX_IQK_TONE_A, 0x38008c1c);
+ rtl8xxxu_write32(priv, REG_TX_IQK_TONE_B, 0x38008c1c);
+ rtl8xxxu_write32(priv, REG_RX_IQK_TONE_B, 0x38008c1c);
+
+ rtl8xxxu_write32(priv, REG_TX_IQK_PI_A, 0x82160ff0);
+ rtl8xxxu_write32(priv, REG_RX_IQK_PI_A, 0x28110000);
+ rtl8xxxu_write32(priv, REG_TX_IQK_PI_B, 0x82110000);
+ rtl8xxxu_write32(priv, REG_RX_IQK_PI_B, 0x28110000);
+
+ /* LO calibration setting */
+ rtl8xxxu_write32(priv, REG_IQK_AGC_RSP, 0x0046a911);
+
+ /*
+ * Enter IQK mode
+ */
+ val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK);
+ val32 &= 0x000000ff;
+ val32 |= 0x80800000;
+ rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32);
+
+ /*
+ * The vendor driver indicates the USB module is always using
+ * S0S1 path 1 for the 8723bu. This may be different for 8192eu
+ */
+ if (priv->rf_paths > 1)
+ rtl8xxxu_write32(priv, REG_S0S1_PATH_SWITCH, 0x00000000);
+ else
+ rtl8xxxu_write32(priv, REG_S0S1_PATH_SWITCH, 0x00000280);
+
+ /*
+ * Bit 12 seems to be BT_GRANT, and is only found in the 8723bu.
+ * No trace of this in the 8192eu or 8188eu vendor drivers.
+ */
+ rtl8xxxu_write32(priv, REG_BT_CONTROL_8723BU, 0x00000800);
+
+ /* One shot, path A LOK & IQK */
+ rtl8xxxu_write32(priv, REG_IQK_AGC_PTS, 0xf9000000);
+ rtl8xxxu_write32(priv, REG_IQK_AGC_PTS, 0xf8000000);
+
+ mdelay(1);
+
+ /* Restore Ant Path */
+ rtl8xxxu_write32(priv, REG_S0S1_PATH_SWITCH, path_sel);
+#ifdef RTL8723BU_BT
+ /* GNT_BT = 1 */
+ rtl8xxxu_write32(priv, REG_BT_CONTROL_8723BU, 0x00001800);
+#endif
+
+ /*
+ * Leave IQK mode
+ */
+ val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK);
+ val32 &= 0x000000ff;
+ rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32);
+
+ /* Check failed */
+ reg_eac = rtl8xxxu_read32(priv, REG_RX_POWER_AFTER_IQK_A_2);
+ reg_e94 = rtl8xxxu_read32(priv, REG_TX_POWER_BEFORE_IQK_A);
+ reg_e9c = rtl8xxxu_read32(priv, REG_TX_POWER_AFTER_IQK_A);
+
+ val32 = (reg_e9c >> 16) & 0x3ff;
+ if (val32 & 0x200)
+ val32 = 0x400 - val32;
+
+ if (!(reg_eac & BIT(28)) &&
+ ((reg_e94 & 0x03ff0000) != 0x01420000) &&
+ ((reg_e9c & 0x03ff0000) != 0x00420000) &&
+ ((reg_e94 & 0x03ff0000) < 0x01100000) &&
+ ((reg_e94 & 0x03ff0000) > 0x00f00000) &&
+ val32 < 0xf)
+ result |= 0x01;
+ else /* If TX not OK, ignore RX */
+ goto out;
+
+ val32 = 0x80007c00 | (reg_e94 &0x3ff0000) |
+ ((reg_e9c & 0x3ff0000) >> 16);
+ rtl8xxxu_write32(priv, REG_TX_IQK, val32);
+
+ /*
+ * Modify RX IQK mode
+ */
+ val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK);
+ val32 &= 0x000000ff;
+ rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32);
+ val32 = rtl8xxxu_read_rfreg(priv, RF_A, RF6052_REG_WE_LUT);
+ val32 |= 0x80000;
+ rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_WE_LUT, val32);
+ rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_RCK_OS, 0x30000);
+ rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G1, 0x0001f);
+ rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G2, 0xf7d77);
+
+ /*
+ * PA, PAD setting
+ */
+ rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_UNKNOWN_DF, 0xf80);
+ rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_UNKNOWN_55, 0x4021f);
+
+ /*
+ * RX IQK setting
+ */
+ rtl8xxxu_write32(priv, REG_RX_IQK, 0x01004800);
+
+ /* path-A IQK setting */
+ rtl8xxxu_write32(priv, REG_TX_IQK_TONE_A, 0x38008c1c);
+ rtl8xxxu_write32(priv, REG_RX_IQK_TONE_A, 0x18008c1c);
+ rtl8xxxu_write32(priv, REG_TX_IQK_TONE_B, 0x38008c1c);
+ rtl8xxxu_write32(priv, REG_RX_IQK_TONE_B, 0x38008c1c);
+
+ rtl8xxxu_write32(priv, REG_TX_IQK_PI_A, 0x82110000);
+ rtl8xxxu_write32(priv, REG_RX_IQK_PI_A, 0x2816001f);
+ rtl8xxxu_write32(priv, REG_TX_IQK_PI_B, 0x82110000);
+ rtl8xxxu_write32(priv, REG_RX_IQK_PI_B, 0x28110000);
+
+ /* LO calibration setting */
+ rtl8xxxu_write32(priv, REG_IQK_AGC_RSP, 0x0046a8d1);
+
+ /*
+ * Enter IQK mode
+ */
+ val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK);
+ val32 &= 0x000000ff;
+ val32 |= 0x80800000;
+ rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32);
+
+ if (priv->rf_paths > 1)
+ rtl8xxxu_write32(priv, REG_S0S1_PATH_SWITCH, 0x00000000);
+ else
+ rtl8xxxu_write32(priv, REG_S0S1_PATH_SWITCH, 0x00000280);
+
+ /*
+ * Disable BT
+ */
+ rtl8xxxu_write32(priv, REG_BT_CONTROL_8723BU, 0x00000800);
+
+ /* One shot, path A LOK & IQK */
+ rtl8xxxu_write32(priv, REG_IQK_AGC_PTS, 0xf9000000);
+ rtl8xxxu_write32(priv, REG_IQK_AGC_PTS, 0xf8000000);
+
+ mdelay(1);
+
+ /* Restore Ant Path */
+ rtl8xxxu_write32(priv, REG_S0S1_PATH_SWITCH, path_sel);
+#ifdef RTL8723BU_BT
+ /* GNT_BT = 1 */
+ rtl8xxxu_write32(priv, REG_BT_CONTROL_8723BU, 0x00001800);
+#endif
+
+ /*
+ * Leave IQK mode
+ */
+ val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK);
+ val32 &= 0x000000ff;
+ rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32);
+
+ /* Check failed */
+ reg_eac = rtl8xxxu_read32(priv, REG_RX_POWER_AFTER_IQK_A_2);
+ reg_ea4 = rtl8xxxu_read32(priv, REG_RX_POWER_BEFORE_IQK_A_2);
+
+ rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_UNKNOWN_DF, 0x780);
+
+ val32 = (reg_eac >> 16) & 0x3ff;
+ if (val32 & 0x200)
+ val32 = 0x400 - val32;
+
+ if (!(reg_eac & BIT(27)) &&
+ ((reg_ea4 & 0x03ff0000) != 0x01320000) &&
+ ((reg_eac & 0x03ff0000) != 0x00360000) &&
+ ((reg_ea4 & 0x03ff0000) < 0x01100000) &&
+ ((reg_ea4 & 0x03ff0000) > 0x00f00000) &&
+ val32 < 0xf)
+ result |= 0x02;
+ else /* If TX not OK, ignore RX */
+ goto out;
+out:
+ return result;
+}
+
+#ifdef RTL8723BU_PATH_B
+static int rtl8723bu_iqk_path_b(struct rtl8xxxu_priv *priv)
+{
+ u32 reg_eac, reg_eb4, reg_ebc, reg_ec4, reg_ecc, path_sel;
+ int result = 0;
+
+ path_sel = rtl8xxxu_read32(priv, REG_S0S1_PATH_SWITCH);
+
+ val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK);
+ val32 &= 0x000000ff;
+ rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32);
+
+ /* One shot, path B LOK & IQK */
+ rtl8xxxu_write32(priv, REG_IQK_AGC_CONT, 0x00000002);
+ rtl8xxxu_write32(priv, REG_IQK_AGC_CONT, 0x00000000);
+
+ mdelay(1);
+
+ /* Check failed */
+ reg_eac = rtl8xxxu_read32(priv, REG_RX_POWER_AFTER_IQK_A_2);
+ reg_eb4 = rtl8xxxu_read32(priv, REG_TX_POWER_BEFORE_IQK_B);
+ reg_ebc = rtl8xxxu_read32(priv, REG_TX_POWER_AFTER_IQK_B);
+ reg_ec4 = rtl8xxxu_read32(priv, REG_RX_POWER_BEFORE_IQK_B_2);
+ reg_ecc = rtl8xxxu_read32(priv, REG_RX_POWER_AFTER_IQK_B_2);
+
+ if (!(reg_eac & BIT(31)) &&
+ ((reg_eb4 & 0x03ff0000) != 0x01420000) &&
+ ((reg_ebc & 0x03ff0000) != 0x00420000))
+ result |= 0x01;
+ else
+ goto out;
+
+ if (!(reg_eac & BIT(30)) &&
+ (((reg_ec4 & 0x03ff0000) >> 16) != 0x132) &&
+ (((reg_ecc & 0x03ff0000) >> 16) != 0x36))
+ result |= 0x02;
+ else
+ dev_warn(&priv->udev->dev, "%s: Path B RX IQK failed!\n",
+ __func__);
+out:
+ return result;
+}
+#endif
+
static void rtl8xxxu_phy_iqcalibrate(struct rtl8xxxu_priv *priv,
int result[][8], int t)
{
@@ -3321,7 +4635,249 @@
}
}
-static void rtl8723a_phy_iq_calibrate(struct rtl8xxxu_priv *priv)
+static void rtl8723bu_phy_iqcalibrate(struct rtl8xxxu_priv *priv,
+ int result[][8], int t)
+{
+ struct device *dev = &priv->udev->dev;
+ u32 i, val32;
+ int path_a_ok /*, path_b_ok */;
+ int retry = 2;
+ const u32 adda_regs[RTL8XXXU_ADDA_REGS] = {
+ REG_FPGA0_XCD_SWITCH_CTRL, REG_BLUETOOTH,
+ REG_RX_WAIT_CCA, REG_TX_CCK_RFON,
+ REG_TX_CCK_BBON, REG_TX_OFDM_RFON,
+ REG_TX_OFDM_BBON, REG_TX_TO_RX,
+ REG_TX_TO_TX, REG_RX_CCK,
+ REG_RX_OFDM, REG_RX_WAIT_RIFS,
+ REG_RX_TO_RX, REG_STANDBY,
+ REG_SLEEP, REG_PMPD_ANAEN
+ };
+ const u32 iqk_mac_regs[RTL8XXXU_MAC_REGS] = {
+ REG_TXPAUSE, REG_BEACON_CTRL,
+ REG_BEACON_CTRL_1, REG_GPIO_MUXCFG
+ };
+ const u32 iqk_bb_regs[RTL8XXXU_BB_REGS] = {
+ REG_OFDM0_TRX_PATH_ENABLE, REG_OFDM0_TR_MUX_PAR,
+ REG_FPGA0_XCD_RF_SW_CTRL, REG_CONFIG_ANT_A, REG_CONFIG_ANT_B,
+ REG_FPGA0_XAB_RF_SW_CTRL, REG_FPGA0_XA_RF_INT_OE,
+ REG_FPGA0_XB_RF_INT_OE, REG_FPGA0_RF_MODE
+ };
+ u8 xa_agc = rtl8xxxu_read32(priv, REG_OFDM0_XA_AGC_CORE1) & 0xff;
+ u8 xb_agc = rtl8xxxu_read32(priv, REG_OFDM0_XB_AGC_CORE1) & 0xff;
+
+ /*
+ * Note: IQ calibration must be performed after loading
+ * PHY_REG.txt , and radio_a, radio_b.txt
+ */
+
+ if (t == 0) {
+ /* Save ADDA parameters, turn Path A ADDA on */
+ rtl8xxxu_save_regs(priv, adda_regs, priv->adda_backup,
+ RTL8XXXU_ADDA_REGS);
+ rtl8xxxu_save_mac_regs(priv, iqk_mac_regs, priv->mac_backup);
+ rtl8xxxu_save_regs(priv, iqk_bb_regs,
+ priv->bb_backup, RTL8XXXU_BB_REGS);
+ }
+
+ rtl8xxxu_path_adda_on(priv, adda_regs, true);
+
+ /* MAC settings */
+ rtl8xxxu_mac_calibration(priv, iqk_mac_regs, priv->mac_backup);
+
+ val32 = rtl8xxxu_read32(priv, REG_CCK0_AFE_SETTING);
+ val32 |= 0x0f000000;
+ rtl8xxxu_write32(priv, REG_CCK0_AFE_SETTING, val32);
+
+ rtl8xxxu_write32(priv, REG_OFDM0_TRX_PATH_ENABLE, 0x03a05600);
+ rtl8xxxu_write32(priv, REG_OFDM0_TR_MUX_PAR, 0x000800e4);
+ rtl8xxxu_write32(priv, REG_FPGA0_XCD_RF_SW_CTRL, 0x22204000);
+
+#ifdef RTL8723BU_PATH_B
+ /* Set RF mode to standby Path B */
+ if (priv->tx_paths > 1)
+ rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_AC, 0x10000);
+#endif
+
+#if 0
+ /* Page B init */
+ rtl8xxxu_write32(priv, REG_CONFIG_ANT_A, 0x0f600000);
+
+ if (priv->tx_paths > 1)
+ rtl8xxxu_write32(priv, REG_CONFIG_ANT_B, 0x0f600000);
+#endif
+
+ /*
+ * RX IQ calibration setting for 8723B D cut large current issue
+ * when leaving IPS
+ */
+ val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK);
+ val32 &= 0x000000ff;
+ rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32);
+
+ val32 = rtl8xxxu_read_rfreg(priv, RF_A, RF6052_REG_WE_LUT);
+ val32 |= 0x80000;
+ rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_WE_LUT, val32);
+
+ rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_RCK_OS, 0x30000);
+ rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G1, 0x0001f);
+ rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G2, 0xf7fb7);
+
+ val32 = rtl8xxxu_read_rfreg(priv, RF_A, RF6052_REG_UNKNOWN_ED);
+ val32 |= 0x20;
+ rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_UNKNOWN_ED, val32);
+
+ rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_UNKNOWN_43, 0x60fbd);
+
+ for (i = 0; i < retry; i++) {
+ path_a_ok = rtl8723bu_iqk_path_a(priv);
+ if (path_a_ok == 0x01) {
+ val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK);
+ val32 &= 0x000000ff;
+ rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32);
+
+#if 0 /* Only needed in restore case, we may need this when going to suspend */
+ priv->RFCalibrateInfo.TxLOK[RF_A] =
+ rtl8xxxu_read_rfreg(priv, RF_A,
+ RF6052_REG_TXM_IDAC);
+#endif
+
+ val32 = rtl8xxxu_read32(priv,
+ REG_TX_POWER_BEFORE_IQK_A);
+ result[t][0] = (val32 >> 16) & 0x3ff;
+ val32 = rtl8xxxu_read32(priv,
+ REG_TX_POWER_AFTER_IQK_A);
+ result[t][1] = (val32 >> 16) & 0x3ff;
+
+ break;
+ }
+ }
+
+ if (!path_a_ok)
+ dev_dbg(dev, "%s: Path A TX IQK failed!\n", __func__);
+
+ for (i = 0; i < retry; i++) {
+ path_a_ok = rtl8723bu_rx_iqk_path_a(priv);
+ if (path_a_ok == 0x03) {
+ val32 = rtl8xxxu_read32(priv,
+ REG_RX_POWER_BEFORE_IQK_A_2);
+ result[t][2] = (val32 >> 16) & 0x3ff;
+ val32 = rtl8xxxu_read32(priv,
+ REG_RX_POWER_AFTER_IQK_A_2);
+ result[t][3] = (val32 >> 16) & 0x3ff;
+
+ break;
+ }
+ }
+
+ if (!path_a_ok)
+ dev_dbg(dev, "%s: Path A RX IQK failed!\n", __func__);
+
+ if (priv->tx_paths > 1) {
+#if 1
+ dev_warn(dev, "%s: Path B not supported\n", __func__);
+#else
+
+ /*
+ * Path A into standby
+ */
+ val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK);
+ val32 &= 0x000000ff;
+ rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32);
+ rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_AC, 0x10000);
+
+ val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK);
+ val32 &= 0x000000ff;
+ val32 |= 0x80800000;
+ rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32);
+
+ /* Turn Path B ADDA on */
+ rtl8xxxu_path_adda_on(priv, adda_regs, false);
+
+ for (i = 0; i < retry; i++) {
+ path_b_ok = rtl8xxxu_iqk_path_b(priv);
+ if (path_b_ok == 0x03) {
+ val32 = rtl8xxxu_read32(priv, REG_TX_POWER_BEFORE_IQK_B);
+ result[t][4] = (val32 >> 16) & 0x3ff;
+ val32 = rtl8xxxu_read32(priv, REG_TX_POWER_AFTER_IQK_B);
+ result[t][5] = (val32 >> 16) & 0x3ff;
+ break;
+ }
+ }
+
+ if (!path_b_ok)
+ dev_dbg(dev, "%s: Path B IQK failed!\n", __func__);
+
+ for (i = 0; i < retry; i++) {
+ path_b_ok = rtl8723bu_rx_iqk_path_b(priv);
+ if (path_a_ok == 0x03) {
+ val32 = rtl8xxxu_read32(priv,
+ REG_RX_POWER_BEFORE_IQK_B_2);
+ result[t][6] = (val32 >> 16) & 0x3ff;
+ val32 = rtl8xxxu_read32(priv,
+ REG_RX_POWER_AFTER_IQK_B_2);
+ result[t][7] = (val32 >> 16) & 0x3ff;
+ break;
+ }
+ }
+
+ if (!path_b_ok)
+ dev_dbg(dev, "%s: Path B RX IQK failed!\n", __func__);
+#endif
+ }
+
+ /* Back to BB mode, load original value */
+ val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK);
+ val32 &= 0x000000ff;
+ rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32);
+
+ if (t) {
+ /* Reload ADDA power saving parameters */
+ rtl8xxxu_restore_regs(priv, adda_regs, priv->adda_backup,
+ RTL8XXXU_ADDA_REGS);
+
+ /* Reload MAC parameters */
+ rtl8xxxu_restore_mac_regs(priv, iqk_mac_regs, priv->mac_backup);
+
+ /* Reload BB parameters */
+ rtl8xxxu_restore_regs(priv, iqk_bb_regs,
+ priv->bb_backup, RTL8XXXU_BB_REGS);
+
+ /* Restore RX initial gain */
+ val32 = rtl8xxxu_read32(priv, REG_OFDM0_XA_AGC_CORE1);
+ val32 &= 0xffffff00;
+ rtl8xxxu_write32(priv, REG_OFDM0_XA_AGC_CORE1, val32 | 0x50);
+ rtl8xxxu_write32(priv, REG_OFDM0_XA_AGC_CORE1, val32 | xa_agc);
+
+ if (priv->tx_paths > 1) {
+ val32 = rtl8xxxu_read32(priv, REG_OFDM0_XB_AGC_CORE1);
+ val32 &= 0xffffff00;
+ rtl8xxxu_write32(priv, REG_OFDM0_XB_AGC_CORE1,
+ val32 | 0x50);
+ rtl8xxxu_write32(priv, REG_OFDM0_XB_AGC_CORE1,
+ val32 | xb_agc);
+ }
+
+ /* Load 0xe30 IQC default value */
+ rtl8xxxu_write32(priv, REG_TX_IQK_TONE_A, 0x01008c00);
+ rtl8xxxu_write32(priv, REG_RX_IQK_TONE_A, 0x01008c00);
+ }
+}
+
+static void rtl8xxxu_prepare_calibrate(struct rtl8xxxu_priv *priv, u8 start)
+{
+ struct h2c_cmd h2c;
+
+ if (priv->fops->mbox_ext_width < 4)
+ return;
+
+ memset(&h2c, 0, sizeof(struct h2c_cmd));
+ h2c.bt_wlan_calibration.cmd = H2C_8723B_BT_WLAN_CALIBRATION;
+ h2c.bt_wlan_calibration.data = start;
+
+ rtl8723a_h2c_cmd(priv, &h2c, sizeof(h2c.bt_wlan_calibration));
+}
+
+static void rtl8723au_phy_iq_calibrate(struct rtl8xxxu_priv *priv)
{
struct device *dev = &priv->udev->dev;
int result[4][8]; /* last is final result */
@@ -3332,6 +4888,8 @@
s32 reg_tmp = 0;
bool simu;
+ rtl8xxxu_prepare_calibrate(priv, 1);
+
memset(result, 0, sizeof(result));
candidate = -1;
@@ -3419,6 +4977,135 @@
rtl8xxxu_save_regs(priv, rtl8723au_iqk_phy_iq_bb_reg,
priv->bb_recovery_backup, RTL8XXXU_BB_REGS);
+
+ rtl8xxxu_prepare_calibrate(priv, 0);
+}
+
+static void rtl8723bu_phy_iq_calibrate(struct rtl8xxxu_priv *priv)
+{
+ struct device *dev = &priv->udev->dev;
+ int result[4][8]; /* last is final result */
+ int i, candidate;
+ bool path_a_ok, path_b_ok;
+ u32 reg_e94, reg_e9c, reg_ea4, reg_eac;
+ u32 reg_eb4, reg_ebc, reg_ec4, reg_ecc;
+ u32 val32, bt_control;
+ s32 reg_tmp = 0;
+ bool simu;
+
+ rtl8xxxu_prepare_calibrate(priv, 1);
+
+ memset(result, 0, sizeof(result));
+ candidate = -1;
+
+ path_a_ok = false;
+ path_b_ok = false;
+
+ bt_control = rtl8xxxu_read32(priv, REG_BT_CONTROL_8723BU);
+
+ for (i = 0; i < 3; i++) {
+ rtl8723bu_phy_iqcalibrate(priv, result, i);
+
+ if (i == 1) {
+ simu = rtl8723bu_simularity_compare(priv, result, 0, 1);
+ if (simu) {
+ candidate = 0;
+ break;
+ }
+ }
+
+ if (i == 2) {
+ simu = rtl8723bu_simularity_compare(priv, result, 0, 2);
+ if (simu) {
+ candidate = 0;
+ break;
+ }
+
+ simu = rtl8723bu_simularity_compare(priv, result, 1, 2);
+ if (simu) {
+ candidate = 1;
+ } else {
+ for (i = 0; i < 8; i++)
+ reg_tmp += result[3][i];
+
+ if (reg_tmp)
+ candidate = 3;
+ else
+ candidate = -1;
+ }
+ }
+ }
+
+ for (i = 0; i < 4; i++) {
+ reg_e94 = result[i][0];
+ reg_e9c = result[i][1];
+ reg_ea4 = result[i][2];
+ reg_eac = result[i][3];
+ reg_eb4 = result[i][4];
+ reg_ebc = result[i][5];
+ reg_ec4 = result[i][6];
+ reg_ecc = result[i][7];
+ }
+
+ if (candidate >= 0) {
+ reg_e94 = result[candidate][0];
+ priv->rege94 = reg_e94;
+ reg_e9c = result[candidate][1];
+ priv->rege9c = reg_e9c;
+ reg_ea4 = result[candidate][2];
+ reg_eac = result[candidate][3];
+ reg_eb4 = result[candidate][4];
+ priv->regeb4 = reg_eb4;
+ reg_ebc = result[candidate][5];
+ priv->regebc = reg_ebc;
+ reg_ec4 = result[candidate][6];
+ reg_ecc = result[candidate][7];
+ dev_dbg(dev, "%s: candidate is %x\n", __func__, candidate);
+ dev_dbg(dev,
+ "%s: e94 =%x e9c=%x ea4=%x eac=%x eb4=%x ebc=%x ec4=%x "
+ "ecc=%x\n ", __func__, reg_e94, reg_e9c,
+ reg_ea4, reg_eac, reg_eb4, reg_ebc, reg_ec4, reg_ecc);
+ path_a_ok = true;
+ path_b_ok = true;
+ } else {
+ reg_e94 = reg_eb4 = priv->rege94 = priv->regeb4 = 0x100;
+ reg_e9c = reg_ebc = priv->rege9c = priv->regebc = 0x0;
+ }
+
+ if (reg_e94 && candidate >= 0)
+ rtl8xxxu_fill_iqk_matrix_a(priv, path_a_ok, result,
+ candidate, (reg_ea4 == 0));
+
+ if (priv->tx_paths > 1 && reg_eb4)
+ rtl8xxxu_fill_iqk_matrix_b(priv, path_b_ok, result,
+ candidate, (reg_ec4 == 0));
+
+ rtl8xxxu_save_regs(priv, rtl8723au_iqk_phy_iq_bb_reg,
+ priv->bb_recovery_backup, RTL8XXXU_BB_REGS);
+
+ rtl8xxxu_write32(priv, REG_BT_CONTROL_8723BU, bt_control);
+
+ val32 = rtl8xxxu_read_rfreg(priv, RF_A, RF6052_REG_WE_LUT);
+ val32 |= 0x80000;
+ rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_WE_LUT, val32);
+ rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_RCK_OS, 0x18000);
+ rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G1, 0x0001f);
+ rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G2, 0xe6177);
+ val32 = rtl8xxxu_read_rfreg(priv, RF_A, RF6052_REG_UNKNOWN_ED);
+ val32 |= 0x20;
+ rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_UNKNOWN_ED, val32);
+ rtl8xxxu_write_rfreg(priv, RF_A, 0x43, 0x300bd);
+
+ if (priv->rf_paths > 1) {
+ dev_dbg(dev, "%s: beware 2T not yet supported\n", __func__);
+#ifdef RTL8723BU_PATH_B
+ if (RF_Path == 0x0) //S1
+ ODM_SetIQCbyRFpath(pDM_Odm, 0);
+ else //S0
+ ODM_SetIQCbyRFpath(pDM_Odm, 1);
+#endif
+ }
+ rtl8xxxu_prepare_calibrate(priv, 0);
}
static void rtl8723a_phy_lc_calibrate(struct rtl8xxxu_priv *priv)
@@ -3456,12 +5143,17 @@
}
/* Start LC calibration */
+ if (priv->fops->has_s0s1)
+ rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_S0S1, 0xdfbe0);
val32 = rtl8xxxu_read_rfreg(priv, RF_A, RF6052_REG_MODE_AG);
val32 |= 0x08000;
rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_MODE_AG, val32);
msleep(100);
+ if (priv->fops->has_s0s1)
+ rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_S0S1, 0xdffe0);
+
/* Restore original parameters */
if (lstf & OFDM_LSTF_MASK) {
/* Path-A */
@@ -3584,6 +5276,64 @@
return ret;
}
+static int rtl8723bu_active_to_emu(struct rtl8xxxu_priv *priv)
+{
+ u8 val8;
+ u16 val16;
+ u32 val32;
+ int count, ret;
+
+ /* Turn off RF */
+ rtl8xxxu_write8(priv, REG_RF_CTRL, 0);
+
+ /* Enable rising edge triggering interrupt */
+ val16 = rtl8xxxu_read16(priv, REG_GPIO_INTM);
+ val16 &= ~GPIO_INTM_EDGE_TRIG_IRQ;
+ rtl8xxxu_write16(priv, REG_GPIO_INTM, val16);
+
+ /* Release WLON reset 0x04[16]= 1*/
+ val32 = rtl8xxxu_read32(priv, REG_GPIO_INTM);
+ val32 |= APS_FSMCO_WLON_RESET;
+ rtl8xxxu_write32(priv, REG_GPIO_INTM, val32);
+
+ /* 0x0005[1] = 1 turn off MAC by HW state machine*/
+ val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1);
+ val8 |= BIT(1);
+ rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8);
+
+ for (count = RTL8XXXU_MAX_REG_POLL; count; count--) {
+ val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1);
+ if ((val8 & BIT(1)) == 0)
+ break;
+ udelay(10);
+ }
+
+ if (!count) {
+ dev_warn(&priv->udev->dev, "%s: Disabling MAC timed out\n",
+ __func__);
+ ret = -EBUSY;
+ goto exit;
+ }
+
+ /* Enable BT control XTAL setting */
+ val8 = rtl8xxxu_read8(priv, REG_AFE_MISC);
+ val8 &= ~AFE_MISC_WL_XTAL_CTRL;
+ rtl8xxxu_write8(priv, REG_AFE_MISC, val8);
+
+ /* 0x0000[5] = 1 analog Ips to digital, 1:isolation */
+ val8 = rtl8xxxu_read8(priv, REG_SYS_ISO_CTRL);
+ val8 |= SYS_ISO_ANALOG_IPS;
+ rtl8xxxu_write8(priv, REG_SYS_ISO_CTRL, val8);
+
+ /* 0x0020[0] = 0 disable LDOA12 MACRO block*/
+ val8 = rtl8xxxu_read8(priv, REG_LDOA15_CTRL);
+ val8 &= ~LDOA15_ENABLE;
+ rtl8xxxu_write8(priv, REG_LDOA15_CTRL, val8);
+
+exit:
+ return ret;
+}
+
static int rtl8xxxu_active_to_lps(struct rtl8xxxu_priv *priv)
{
u8 val8;
@@ -3640,7 +5390,7 @@
return ret;
}
-static void rtl8xxxu_disabled_to_emu(struct rtl8xxxu_priv *priv)
+static void rtl8723a_disabled_to_emu(struct rtl8xxxu_priv *priv)
{
u8 val8;
@@ -3660,7 +5410,82 @@
rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8);
}
-static int rtl8xxxu_emu_to_active(struct rtl8xxxu_priv *priv)
+static void rtl8192e_disabled_to_emu(struct rtl8xxxu_priv *priv)
+{
+ u8 val8;
+
+ /* Clear suspend enable and power down enable*/
+ val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1);
+ val8 &= ~(BIT(3) | BIT(4));
+ rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8);
+}
+
+static int rtl8192e_emu_to_active(struct rtl8xxxu_priv *priv)
+{
+ u8 val8;
+ u32 val32;
+ int count, ret = 0;
+
+ /* disable HWPDN 0x04[15]=0*/
+ val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1);
+ val8 &= ~BIT(7);
+ rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8);
+
+ /* disable SW LPS 0x04[10]= 0 */
+ val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1);
+ val8 &= ~BIT(2);
+ rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8);
+
+ /* disable WL suspend*/
+ val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1);
+ val8 &= ~(BIT(3) | BIT(4));
+ rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8);
+
+ /* wait till 0x04[17] = 1 power ready*/
+ for (count = RTL8XXXU_MAX_REG_POLL; count; count--) {
+ val32 = rtl8xxxu_read32(priv, REG_APS_FSMCO);
+ if (val32 & BIT(17))
+ break;
+
+ udelay(10);
+ }
+
+ if (!count) {
+ ret = -EBUSY;
+ goto exit;
+ }
+
+ /* We should be able to optimize the following three entries into one */
+
+ /* release WLON reset 0x04[16]= 1*/
+ val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 2);
+ val8 |= BIT(0);
+ rtl8xxxu_write8(priv, REG_APS_FSMCO + 2, val8);
+
+ /* set, then poll until 0 */
+ val32 = rtl8xxxu_read32(priv, REG_APS_FSMCO);
+ val32 |= APS_FSMCO_MAC_ENABLE;
+ rtl8xxxu_write32(priv, REG_APS_FSMCO, val32);
+
+ for (count = RTL8XXXU_MAX_REG_POLL; count; count--) {
+ val32 = rtl8xxxu_read32(priv, REG_APS_FSMCO);
+ if ((val32 & APS_FSMCO_MAC_ENABLE) == 0) {
+ ret = 0;
+ break;
+ }
+ udelay(10);
+ }
+
+ if (!count) {
+ ret = -EBUSY;
+ goto exit;
+ }
+
+exit:
+ return ret;
+}
+
+static int rtl8723a_emu_to_active(struct rtl8xxxu_priv *priv)
{
u8 val8;
u32 val32;
@@ -3752,6 +5577,127 @@
return ret;
}
+static int rtl8723b_emu_to_active(struct rtl8xxxu_priv *priv)
+{
+ u8 val8;
+ u32 val32;
+ int count, ret = 0;
+
+ /* 0x20[0] = 1 enable LDOA12 MACRO block for all interface */
+ val8 = rtl8xxxu_read8(priv, REG_LDOA15_CTRL);
+ val8 |= LDOA15_ENABLE;
+ rtl8xxxu_write8(priv, REG_LDOA15_CTRL, val8);
+
+ /* 0x67[0] = 0 to disable BT_GPS_SEL pins*/
+ val8 = rtl8xxxu_read8(priv, 0x0067);
+ val8 &= ~BIT(4);
+ rtl8xxxu_write8(priv, 0x0067, val8);
+
+ mdelay(1);
+
+ /* 0x00[5] = 0 release analog Ips to digital, 1:isolation */
+ val8 = rtl8xxxu_read8(priv, REG_SYS_ISO_CTRL);
+ val8 &= ~SYS_ISO_ANALOG_IPS;
+ rtl8xxxu_write8(priv, REG_SYS_ISO_CTRL, val8);
+
+ /* Disable SW LPS 0x04[10]= 0 */
+ val32 = rtl8xxxu_read8(priv, REG_APS_FSMCO);
+ val32 &= ~APS_FSMCO_SW_LPS;
+ rtl8xxxu_write32(priv, REG_APS_FSMCO, val32);
+
+ /* Wait until 0x04[17] = 1 power ready */
+ for (count = RTL8XXXU_MAX_REG_POLL; count; count--) {
+ val32 = rtl8xxxu_read32(priv, REG_APS_FSMCO);
+ if (val32 & BIT(17))
+ break;
+
+ udelay(10);
+ }
+
+ if (!count) {
+ ret = -EBUSY;
+ goto exit;
+ }
+
+ /* We should be able to optimize the following three entries into one */
+
+ /* Release WLON reset 0x04[16]= 1*/
+ val32 = rtl8xxxu_read32(priv, REG_APS_FSMCO);
+ val32 |= APS_FSMCO_WLON_RESET;
+ rtl8xxxu_write32(priv, REG_APS_FSMCO, val32);
+
+ /* Disable HWPDN 0x04[15]= 0*/
+ val32 = rtl8xxxu_read32(priv, REG_APS_FSMCO);
+ val32 &= ~APS_FSMCO_HW_POWERDOWN;
+ rtl8xxxu_write32(priv, REG_APS_FSMCO, val32);
+
+ /* Disable WL suspend*/
+ val32 = rtl8xxxu_read32(priv, REG_APS_FSMCO);
+ val32 &= ~(APS_FSMCO_HW_SUSPEND | APS_FSMCO_PCIE);
+ rtl8xxxu_write32(priv, REG_APS_FSMCO, val32);
+
+ /* Set, then poll until 0 */
+ val32 = rtl8xxxu_read32(priv, REG_APS_FSMCO);
+ val32 |= APS_FSMCO_MAC_ENABLE;
+ rtl8xxxu_write32(priv, REG_APS_FSMCO, val32);
+
+ for (count = RTL8XXXU_MAX_REG_POLL; count; count--) {
+ val32 = rtl8xxxu_read32(priv, REG_APS_FSMCO);
+ if ((val32 & APS_FSMCO_MAC_ENABLE) == 0) {
+ ret = 0;
+ break;
+ }
+ udelay(10);
+ }
+
+ if (!count) {
+ ret = -EBUSY;
+ goto exit;
+ }
+
+ /* Enable WL control XTAL setting */
+ val8 = rtl8xxxu_read8(priv, REG_AFE_MISC);
+ val8 |= AFE_MISC_WL_XTAL_CTRL;
+ rtl8xxxu_write8(priv, REG_AFE_MISC, val8);
+
+ /* Enable falling edge triggering interrupt */
+ val8 = rtl8xxxu_read8(priv, REG_GPIO_INTM + 1);
+ val8 |= BIT(1);
+ rtl8xxxu_write8(priv, REG_GPIO_INTM + 1, val8);
+
+ /* Enable GPIO9 interrupt mode */
+ val8 = rtl8xxxu_read8(priv, REG_GPIO_IO_SEL_2 + 1);
+ val8 |= BIT(1);
+ rtl8xxxu_write8(priv, REG_GPIO_IO_SEL_2 + 1, val8);
+
+ /* Enable GPIO9 input mode */
+ val8 = rtl8xxxu_read8(priv, REG_GPIO_IO_SEL_2);
+ val8 &= ~BIT(1);
+ rtl8xxxu_write8(priv, REG_GPIO_IO_SEL_2, val8);
+
+ /* Enable HSISR GPIO[C:0] interrupt */
+ val8 = rtl8xxxu_read8(priv, REG_HSIMR);
+ val8 |= BIT(0);
+ rtl8xxxu_write8(priv, REG_HSIMR, val8);
+
+ /* Enable HSISR GPIO9 interrupt */
+ val8 = rtl8xxxu_read8(priv, REG_HSIMR + 2);
+ val8 |= BIT(1);
+ rtl8xxxu_write8(priv, REG_HSIMR + 2, val8);
+
+ val8 = rtl8xxxu_read8(priv, REG_MULTI_FUNC_CTRL);
+ val8 |= MULTI_WIFI_HW_ROF_EN;
+ rtl8xxxu_write8(priv, REG_MULTI_FUNC_CTRL, val8);
+
+ /* For GPIO9 internal pull high setting BIT(14) */
+ val8 = rtl8xxxu_read8(priv, REG_MULTI_FUNC_CTRL + 1);
+ val8 |= BIT(6);
+ rtl8xxxu_write8(priv, REG_MULTI_FUNC_CTRL + 1, val8);
+
+exit:
+ return ret;
+}
+
static int rtl8xxxu_emu_to_disabled(struct rtl8xxxu_priv *priv)
{
u8 val8;
@@ -3777,6 +5723,39 @@
return 0;
}
+static int rtl8xxxu_flush_fifo(struct rtl8xxxu_priv *priv)
+{
+ struct device *dev = &priv->udev->dev;
+ u32 val32;
+ int retry, retval;
+
+ rtl8xxxu_write8(priv, REG_TXPAUSE, 0xff);
+
+ val32 = rtl8xxxu_read32(priv, REG_RXPKT_NUM);
+ val32 |= RXPKT_NUM_RW_RELEASE_EN;
+ rtl8xxxu_write32(priv, REG_RXPKT_NUM, val32);
+
+ retry = 100;
+ retval = -EBUSY;
+
+ do {
+ val32 = rtl8xxxu_read32(priv, REG_RXPKT_NUM);
+ if (val32 & RXPKT_NUM_RXDMA_IDLE) {
+ retval = 0;
+ break;
+ }
+ } while (retry--);
+
+ rtl8xxxu_write16(priv, REG_RQPN_NPQ, 0);
+ rtl8xxxu_write32(priv, REG_RQPN, 0x80000000);
+ mdelay(2);
+
+ if (!retry)
+ dev_warn(dev, "Failed to flush FIFO\n");
+
+ return retval;
+}
+
static int rtl8723au_power_on(struct rtl8xxxu_priv *priv)
{
u8 val8;
@@ -3789,9 +5768,9 @@
*/
rtl8xxxu_write8(priv, REG_RSV_CTRL, 0x0);
- rtl8xxxu_disabled_to_emu(priv);
+ rtl8723a_disabled_to_emu(priv);
- ret = rtl8xxxu_emu_to_active(priv);
+ ret = rtl8723a_emu_to_active(priv);
if (ret)
goto exit;
@@ -3823,6 +5802,62 @@
return ret;
}
+static int rtl8723bu_power_on(struct rtl8xxxu_priv *priv)
+{
+ u8 val8;
+ u16 val16;
+ u32 val32;
+ int ret;
+
+ rtl8723a_disabled_to_emu(priv);
+
+ ret = rtl8723b_emu_to_active(priv);
+ if (ret)
+ goto exit;
+
+ /*
+ * Enable MAC DMA/WMAC/SCHEDULE/SEC block
+ * Set CR bit10 to enable 32k calibration.
+ */
+ val16 = rtl8xxxu_read16(priv, REG_CR);
+ val16 |= (CR_HCI_TXDMA_ENABLE | CR_HCI_RXDMA_ENABLE |
+ CR_TXDMA_ENABLE | CR_RXDMA_ENABLE |
+ CR_PROTOCOL_ENABLE | CR_SCHEDULE_ENABLE |
+ CR_MAC_TX_ENABLE | CR_MAC_RX_ENABLE |
+ CR_SECURITY_ENABLE | CR_CALTIMER_ENABLE);
+ rtl8xxxu_write16(priv, REG_CR, val16);
+
+ /*
+ * BT coexist power on settings. This is identical for 1 and 2
+ * antenna parts.
+ */
+ rtl8xxxu_write8(priv, REG_PAD_CTRL1 + 3, 0x20);
+
+ val16 = rtl8xxxu_read16(priv, REG_SYS_FUNC);
+ val16 |= SYS_FUNC_BBRSTB | SYS_FUNC_BB_GLB_RSTN;
+ rtl8xxxu_write16(priv, REG_SYS_FUNC, val16);
+
+ rtl8xxxu_write8(priv, REG_BT_CONTROL_8723BU + 1, 0x18);
+ rtl8xxxu_write8(priv, REG_WLAN_ACT_CONTROL_8723B, 0x04);
+ rtl8xxxu_write32(priv, REG_S0S1_PATH_SWITCH, 0x00);
+ /* Antenna inverse */
+ rtl8xxxu_write8(priv, 0xfe08, 0x01);
+
+ val16 = rtl8xxxu_read16(priv, REG_PWR_DATA);
+ val16 |= PWR_DATA_EEPRPAD_RFE_CTRL_EN;
+ rtl8xxxu_write16(priv, REG_PWR_DATA, val16);
+
+ val32 = rtl8xxxu_read32(priv, REG_LEDCFG0);
+ val32 |= LEDCFG0_DPDT_SELECT;
+ rtl8xxxu_write32(priv, REG_LEDCFG0, val32);
+
+ val8 = rtl8xxxu_read8(priv, REG_PAD_CTRL1);
+ val8 &= ~PAD_CTRL1_SW_DPDT_SEL_DATA;
+ rtl8xxxu_write8(priv, REG_PAD_CTRL1, val8);
+exit:
+ return ret;
+}
+
#ifdef CONFIG_RTL8XXXU_UNTESTED
static int rtl8192cu_power_on(struct rtl8xxxu_priv *priv)
@@ -3930,6 +5965,52 @@
#endif
+static int rtl8192eu_power_on(struct rtl8xxxu_priv *priv)
+{
+ u16 val16;
+ u32 val32;
+ int ret;
+
+ ret = 0;
+
+ val32 = rtl8xxxu_read32(priv, REG_SYS_CFG);
+ if (val32 & SYS_CFG_SPS_LDO_SEL) {
+ rtl8xxxu_write8(priv, REG_LDO_SW_CTRL, 0xc3);
+ } else {
+ /*
+ * Raise 1.2V voltage
+ */
+ val32 = rtl8xxxu_read32(priv, REG_8192E_LDOV12_CTRL);
+ val32 &= 0xff0fffff;
+ val32 |= 0x00500000;
+ rtl8xxxu_write32(priv, REG_8192E_LDOV12_CTRL, val32);
+ rtl8xxxu_write8(priv, REG_LDO_SW_CTRL, 0x83);
+ }
+
+ rtl8192e_disabled_to_emu(priv);
+
+ ret = rtl8192e_emu_to_active(priv);
+ if (ret)
+ goto exit;
+
+ rtl8xxxu_write16(priv, REG_CR, 0x0000);
+
+ /*
+ * Enable MAC DMA/WMAC/SCHEDULE/SEC block
+ * Set CR bit10 to enable 32k calibration.
+ */
+ val16 = rtl8xxxu_read16(priv, REG_CR);
+ val16 |= (CR_HCI_TXDMA_ENABLE | CR_HCI_RXDMA_ENABLE |
+ CR_TXDMA_ENABLE | CR_RXDMA_ENABLE |
+ CR_PROTOCOL_ENABLE | CR_SCHEDULE_ENABLE |
+ CR_MAC_TX_ENABLE | CR_MAC_RX_ENABLE |
+ CR_SECURITY_ENABLE | CR_CALTIMER_ENABLE);
+ rtl8xxxu_write16(priv, REG_CR, val16);
+
+exit:
+ return ret;
+}
+
static void rtl8xxxu_power_off(struct rtl8xxxu_priv *priv)
{
u8 val8;
@@ -3945,6 +6026,8 @@
rtl8xxxu_write32(priv, REG_FPGA0_XCD_RF_PARM, val32);
}
+ rtl8xxxu_flush_fifo(priv);
+
rtl8xxxu_active_to_lps(priv);
/* Turn off RF */
@@ -3978,10 +6061,215 @@
rtl8xxxu_write8(priv, REG_RSV_CTRL, 0x0e);
}
-static void rtl8xxxu_init_bt(struct rtl8xxxu_priv *priv)
+static void rtl8723bu_power_off(struct rtl8xxxu_priv *priv)
{
- if (!priv->has_bluetooth)
- return;
+ u8 val8;
+ u16 val16;
+
+ rtl8xxxu_flush_fifo(priv);
+
+ /*
+ * Disable TX report timer
+ */
+ val8 = rtl8xxxu_read8(priv, REG_TX_REPORT_CTRL);
+ val8 &= ~TX_REPORT_CTRL_TIMER_ENABLE;
+ rtl8xxxu_write8(priv, REG_TX_REPORT_CTRL, val8);
+
+ rtl8xxxu_write16(priv, REG_CR, 0x0000);
+
+ rtl8xxxu_active_to_lps(priv);
+
+ /* Reset Firmware if running in RAM */
+ if (rtl8xxxu_read8(priv, REG_MCU_FW_DL) & MCU_FW_RAM_SEL)
+ rtl8xxxu_firmware_self_reset(priv);
+
+ /* Reset MCU */
+ val16 = rtl8xxxu_read16(priv, REG_SYS_FUNC);
+ val16 &= ~SYS_FUNC_CPU_ENABLE;
+ rtl8xxxu_write16(priv, REG_SYS_FUNC, val16);
+
+ /* Reset MCU ready status */
+ rtl8xxxu_write8(priv, REG_MCU_FW_DL, 0x00);
+
+ rtl8723bu_active_to_emu(priv);
+ rtl8xxxu_emu_to_disabled(priv);
+}
+
+#ifdef NEED_PS_TDMA
+static void rtl8723bu_set_ps_tdma(struct rtl8xxxu_priv *priv,
+ u8 arg1, u8 arg2, u8 arg3, u8 arg4, u8 arg5)
+{
+ struct h2c_cmd h2c;
+
+ memset(&h2c, 0, sizeof(struct h2c_cmd));
+ h2c.b_type_dma.cmd = H2C_8723B_B_TYPE_TDMA;
+ h2c.b_type_dma.data1 = arg1;
+ h2c.b_type_dma.data2 = arg2;
+ h2c.b_type_dma.data3 = arg3;
+ h2c.b_type_dma.data4 = arg4;
+ h2c.b_type_dma.data5 = arg5;
+ rtl8723a_h2c_cmd(priv, &h2c, sizeof(h2c.b_type_dma));
+}
+#endif
+
+static void rtl8723b_enable_rf(struct rtl8xxxu_priv *priv)
+{
+ struct h2c_cmd h2c;
+ u32 val32;
+ u8 val8;
+
+ /*
+ * No indication anywhere as to what 0x0790 does. The 2 antenna
+ * vendor code preserves bits 6-7 here.
+ */
+ rtl8xxxu_write8(priv, 0x0790, 0x05);
+ /*
+ * 0x0778 seems to be related to enabling the number of antennas
+ * In the vendor driver halbtc8723b2ant_InitHwConfig() sets it
+ * to 0x03, while halbtc8723b1ant_InitHwConfig() sets it to 0x01
+ */
+ rtl8xxxu_write8(priv, 0x0778, 0x01);
+
+ val8 = rtl8xxxu_read8(priv, REG_GPIO_MUXCFG);
+ val8 |= BIT(5);
+ rtl8xxxu_write8(priv, REG_GPIO_MUXCFG, val8);
+
+ rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_IQADJ_G1, 0x780);
+
+ rtl8723bu_write_btreg(priv, 0x3c, 0x15); /* BT TRx Mask on */
+
+ /*
+ * Set BT grant to low
+ */
+ memset(&h2c, 0, sizeof(struct h2c_cmd));
+ h2c.bt_grant.cmd = H2C_8723B_BT_GRANT;
+ h2c.bt_grant.data = 0;
+ rtl8723a_h2c_cmd(priv, &h2c, sizeof(h2c.bt_grant));
+
+ /*
+ * WLAN action by PTA
+ */
+ rtl8xxxu_write8(priv, REG_WLAN_ACT_CONTROL_8723B, 0x04);
+
+ /*
+ * BT select S0/S1 controlled by WiFi
+ */
+ val8 = rtl8xxxu_read8(priv, 0x0067);
+ val8 |= BIT(5);
+ rtl8xxxu_write8(priv, 0x0067, val8);
+
+ val32 = rtl8xxxu_read32(priv, REG_PWR_DATA);
+ val32 |= PWR_DATA_EEPRPAD_RFE_CTRL_EN;
+ rtl8xxxu_write32(priv, REG_PWR_DATA, val32);
+
+ /*
+ * Bits 6/7 are marked in/out ... but for what?
+ */
+ rtl8xxxu_write8(priv, 0x0974, 0xff);
+
+ val32 = rtl8xxxu_read32(priv, REG_RFE_BUFFER);
+ val32 |= (BIT(0) | BIT(1));
+ rtl8xxxu_write32(priv, REG_RFE_BUFFER, val32);
+
+ rtl8xxxu_write8(priv, REG_RFE_CTRL_ANTA_SRC, 0x77);
+
+ val32 = rtl8xxxu_read32(priv, REG_LEDCFG0);
+ val32 &= ~BIT(24);
+ val32 |= BIT(23);
+ rtl8xxxu_write32(priv, REG_LEDCFG0, val32);
+
+ /*
+ * Fix external switch Main->S1, Aux->S0
+ */
+ val8 = rtl8xxxu_read8(priv, REG_PAD_CTRL1);
+ val8 &= ~BIT(0);
+ rtl8xxxu_write8(priv, REG_PAD_CTRL1, val8);
+
+ memset(&h2c, 0, sizeof(struct h2c_cmd));
+ h2c.ant_sel_rsv.cmd = H2C_8723B_ANT_SEL_RSV;
+ h2c.ant_sel_rsv.ant_inverse = 1;
+ h2c.ant_sel_rsv.int_switch_type = 0;
+ rtl8723a_h2c_cmd(priv, &h2c, sizeof(h2c.ant_sel_rsv));
+
+ /*
+ * 0x280, 0x00, 0x200, 0x80 - not clear
+ */
+ rtl8xxxu_write32(priv, REG_S0S1_PATH_SWITCH, 0x00);
+
+ /*
+ * Software control, antenna at WiFi side
+ */
+#ifdef NEED_PS_TDMA
+ rtl8723bu_set_ps_tdma(priv, 0x08, 0x00, 0x00, 0x00, 0x00);
+#endif
+
+ rtl8xxxu_write32(priv, REG_BT_COEX_TABLE1, 0x55555555);
+ rtl8xxxu_write32(priv, REG_BT_COEX_TABLE2, 0x55555555);
+ rtl8xxxu_write32(priv, REG_BT_COEX_TABLE3, 0x00ffffff);
+ rtl8xxxu_write8(priv, REG_BT_COEX_TABLE4, 0x03);
+
+ memset(&h2c, 0, sizeof(struct h2c_cmd));
+ h2c.bt_info.cmd = H2C_8723B_BT_INFO;
+ h2c.bt_info.data = BIT(0);
+ rtl8723a_h2c_cmd(priv, &h2c, sizeof(h2c.bt_info));
+
+ memset(&h2c, 0, sizeof(struct h2c_cmd));
+ h2c.ignore_wlan.cmd = H2C_8723B_BT_IGNORE_WLANACT;
+ h2c.ignore_wlan.data = 0;
+ rtl8723a_h2c_cmd(priv, &h2c, sizeof(h2c.ignore_wlan));
+}
+
+static void rtl8723b_disable_rf(struct rtl8xxxu_priv *priv)
+{
+ u32 val32;
+
+ rtl8xxxu_write8(priv, REG_TXPAUSE, 0xff);
+
+ val32 = rtl8xxxu_read32(priv, REG_RX_WAIT_CCA);
+ val32 &= ~(BIT(22) | BIT(23));
+ rtl8xxxu_write32(priv, REG_RX_WAIT_CCA, val32);
+}
+
+static void rtl8723bu_init_aggregation(struct rtl8xxxu_priv *priv)
+{
+ u32 agg_rx;
+ u8 agg_ctrl;
+
+ /*
+ * For now simply disable RX aggregation
+ */
+ agg_ctrl = rtl8xxxu_read8(priv, REG_TRXDMA_CTRL);
+ agg_ctrl &= ~TRXDMA_CTRL_RXDMA_AGG_EN;
+
+ agg_rx = rtl8xxxu_read32(priv, REG_RXDMA_AGG_PG_TH);
+ agg_rx &= ~RXDMA_USB_AGG_ENABLE;
+ agg_rx &= ~0xff0f;
+
+ rtl8xxxu_write8(priv, REG_TRXDMA_CTRL, agg_ctrl);
+ rtl8xxxu_write32(priv, REG_RXDMA_AGG_PG_TH, agg_rx);
+}
+
+static void rtl8723bu_init_statistics(struct rtl8xxxu_priv *priv)
+{
+ u32 val32;
+
+ /* Time duration for NHM unit: 4us, 0x2710=40ms */
+ rtl8xxxu_write16(priv, REG_NHM_TIMER_8723B + 2, 0x2710);
+ rtl8xxxu_write16(priv, REG_NHM_TH9_TH10_8723B + 2, 0xffff);
+ rtl8xxxu_write32(priv, REG_NHM_TH3_TO_TH0_8723B, 0xffffff52);
+ rtl8xxxu_write32(priv, REG_NHM_TH7_TO_TH4_8723B, 0xffffffff);
+ /* TH8 */
+ val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK);
+ val32 |= 0xff;
+ rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32);
+ /* Enable CCK */
+ val32 = rtl8xxxu_read32(priv, REG_NHM_TH9_TH10_8723B);
+ val32 |= BIT(8) | BIT(9) | BIT(10);
+ rtl8xxxu_write32(priv, REG_NHM_TH9_TH10_8723B, val32);
+ /* Max power amongst all RX antennas */
+ val32 = rtl8xxxu_read32(priv, REG_OFDM0_FA_RSTC);
+ val32 |= BIT(7);
+ rtl8xxxu_write32(priv, REG_OFDM0_FA_RSTC, val32);
}
static int rtl8xxxu_init_device(struct ieee80211_hw *hw)
@@ -4015,11 +6303,30 @@
dev_dbg(dev, "%s: macpower %i\n", __func__, macpower);
if (!macpower) {
- ret = rtl8xxxu_init_llt_table(priv, TX_TOTAL_PAGE_NUM);
+ ret = priv->fops->llt_init(priv, TX_TOTAL_PAGE_NUM);
if (ret) {
dev_warn(dev, "%s: LLT table init failed\n", __func__);
goto exit;
}
+
+ /*
+ * Presumably this is for 8188EU as well
+ * Enable TX report and TX report timer
+ */
+ if (priv->rtlchip == 0x8723bu) {
+ val8 = rtl8xxxu_read8(priv, REG_TX_REPORT_CTRL);
+ val8 |= TX_REPORT_CTRL_TIMER_ENABLE;
+ rtl8xxxu_write8(priv, REG_TX_REPORT_CTRL, val8);
+ /* Set MAX RPT MACID */
+ rtl8xxxu_write8(priv, REG_TX_REPORT_CTRL + 1, 0x02);
+ /* TX report Timer. Unit: 32us */
+ rtl8xxxu_write16(priv, REG_TX_REPORT_TIME, 0xcdf0);
+
+ /* tmp ps ? */
+ val8 = rtl8xxxu_read8(priv, 0xa3);
+ val8 &= 0xf8;
+ rtl8xxxu_write8(priv, 0xa3, val8);
+ }
}
ret = rtl8xxxu_download_firmware(priv);
@@ -4031,7 +6338,42 @@
if (ret)
goto exit;
- ret = rtl8xxxu_init_mac(priv, rtl8723a_mac_init_table);
+ /* Solve too many protocol error on USB bus */
+ /* Can't do this for 8188/8192 UMC A cut parts */
+ if (priv->rtlchip == 0x8723a ||
+ ((priv->rtlchip == 0x8192c || priv->rtlchip == 0x8191c ||
+ priv->rtlchip == 0x8188c) &&
+ (priv->chip_cut || !priv->vendor_umc))) {
+ rtl8xxxu_write8(priv, 0xfe40, 0xe6);
+ rtl8xxxu_write8(priv, 0xfe41, 0x94);
+ rtl8xxxu_write8(priv, 0xfe42, 0x80);
+
+ rtl8xxxu_write8(priv, 0xfe40, 0xe0);
+ rtl8xxxu_write8(priv, 0xfe41, 0x19);
+ rtl8xxxu_write8(priv, 0xfe42, 0x80);
+
+ rtl8xxxu_write8(priv, 0xfe40, 0xe5);
+ rtl8xxxu_write8(priv, 0xfe41, 0x91);
+ rtl8xxxu_write8(priv, 0xfe42, 0x80);
+
+ rtl8xxxu_write8(priv, 0xfe40, 0xe2);
+ rtl8xxxu_write8(priv, 0xfe41, 0x81);
+ rtl8xxxu_write8(priv, 0xfe42, 0x80);
+ }
+
+ if (priv->rtlchip == 0x8192e) {
+ rtl8xxxu_write32(priv, REG_HIMR0, 0x00);
+ rtl8xxxu_write32(priv, REG_HIMR1, 0x00);
+ }
+
+ if (priv->fops->phy_init_antenna_selection)
+ priv->fops->phy_init_antenna_selection(priv);
+
+ if (priv->rtlchip == 0x8723b)
+ ret = rtl8xxxu_init_mac(priv, rtl8723b_mac_init_table);
+ else
+ ret = rtl8xxxu_init_mac(priv, rtl8723a_mac_init_table);
+
dev_dbg(dev, "%s: init_mac %i\n", __func__, ret);
if (ret)
goto exit;
@@ -4046,6 +6388,17 @@
rftable = rtl8723au_radioa_1t_init_table;
ret = rtl8xxxu_init_phy_rf(priv, rftable, RF_A);
break;
+ case 0x8723b:
+ rftable = rtl8723bu_radioa_1t_init_table;
+ ret = rtl8xxxu_init_phy_rf(priv, rftable, RF_A);
+ /*
+ * PHY LCK
+ */
+ rtl8xxxu_write_rfreg(priv, RF_A, 0xb0, 0xdfbe0);
+ rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_MODE_AG, 0x8c01);
+ msleep(200);
+ rtl8xxxu_write_rfreg(priv, RF_A, 0xb0, 0xdffe0);
+ break;
case 0x8188c:
if (priv->hi_pa)
rftable = rtl8188ru_radioa_1t_highpa_table;
@@ -4072,27 +6425,27 @@
if (ret)
goto exit;
- /* Reduce 80M spur */
- rtl8xxxu_write32(priv, REG_AFE_XTAL_CTRL, 0x0381808d);
- rtl8xxxu_write32(priv, REG_AFE_PLL_CTRL, 0xf0ffff83);
- rtl8xxxu_write32(priv, REG_AFE_PLL_CTRL, 0xf0ffff82);
- rtl8xxxu_write32(priv, REG_AFE_PLL_CTRL, 0xf0ffff83);
+ /*
+ * Chip specific quirks
+ */
+ if (priv->rtlchip == 0x8723a) {
+ /* Fix USB interface interference issue */
+ rtl8xxxu_write8(priv, 0xfe40, 0xe0);
+ rtl8xxxu_write8(priv, 0xfe41, 0x8d);
+ rtl8xxxu_write8(priv, 0xfe42, 0x80);
+ rtl8xxxu_write32(priv, REG_TXDMA_OFFSET_CHK, 0xfd0320);
- /* RFSW Control - clear bit 14 ?? */
- rtl8xxxu_write32(priv, REG_FPGA0_TX_INFO, 0x00000003);
- /* 0x07000760 */
- val32 = FPGA0_RF_TRSW | FPGA0_RF_TRSWB | FPGA0_RF_ANTSW |
- FPGA0_RF_ANTSWB | FPGA0_RF_PAPE |
- ((FPGA0_RF_ANTSW | FPGA0_RF_ANTSWB | FPGA0_RF_PAPE) <<
- FPGA0_RF_BD_CTRL_SHIFT);
- rtl8xxxu_write32(priv, REG_FPGA0_XAB_RF_SW_CTRL, val32);
- /* 0x860[6:5]= 00 - why? - this sets antenna B */
- rtl8xxxu_write32(priv, REG_FPGA0_XA_RF_INT_OE, 0x66F60210);
+ /* Reduce 80M spur */
+ rtl8xxxu_write32(priv, REG_AFE_XTAL_CTRL, 0x0381808d);
+ rtl8xxxu_write32(priv, REG_AFE_PLL_CTRL, 0xf0ffff83);
+ rtl8xxxu_write32(priv, REG_AFE_PLL_CTRL, 0xf0ffff82);
+ rtl8xxxu_write32(priv, REG_AFE_PLL_CTRL, 0xf0ffff83);
+ } else {
+ val32 = rtl8xxxu_read32(priv, REG_TXDMA_OFFSET_CHK);
+ val32 |= TXDMA_OFFSET_DROP_DATA_EN;
+ rtl8xxxu_write32(priv, REG_TXDMA_OFFSET_CHK, val32);
+ }
- priv->rf_mode_ag[0] = rtl8xxxu_read_rfreg(priv, RF_A,
- RF6052_REG_MODE_AG);
-
- dev_dbg(dev, "%s: macpower %i\n", __func__, macpower);
if (!macpower) {
if (priv->ep_tx_normal_queue)
val8 = TX_PAGE_NUM_NORM_PQ;
@@ -4114,6 +6467,10 @@
* Set TX buffer boundary
*/
val8 = TX_TOTAL_PAGE_NUM + 1;
+
+ if (priv->rtlchip == 0x8723b)
+ val8 -= 1;
+
rtl8xxxu_write8(priv, REG_TXPKTBUF_BCNQ_BDNY, val8);
rtl8xxxu_write8(priv, REG_TXPKTBUF_MGQ_BDNY, val8);
rtl8xxxu_write8(priv, REG_TXPKTBUF_WMAC_LBK_BF_HD, val8);
@@ -4126,15 +6483,37 @@
if (ret)
goto exit;
+ /* RFSW Control - clear bit 14 ?? */
+ if (priv->rtlchip != 0x8723b)
+ rtl8xxxu_write32(priv, REG_FPGA0_TX_INFO, 0x00000003);
+ /* 0x07000760 */
+ val32 = FPGA0_RF_TRSW | FPGA0_RF_TRSWB | FPGA0_RF_ANTSW |
+ FPGA0_RF_ANTSWB | FPGA0_RF_PAPE |
+ ((FPGA0_RF_ANTSW | FPGA0_RF_ANTSWB | FPGA0_RF_PAPE) <<
+ FPGA0_RF_BD_CTRL_SHIFT);
+ rtl8xxxu_write32(priv, REG_FPGA0_XAB_RF_SW_CTRL, val32);
+ /* 0x860[6:5]= 00 - why? - this sets antenna B */
+ rtl8xxxu_write32(priv, REG_FPGA0_XA_RF_INT_OE, 0x66F60210);
+
+ priv->rf_mode_ag[0] = rtl8xxxu_read_rfreg(priv, RF_A,
+ RF6052_REG_MODE_AG);
+
/*
* Set RX page boundary
*/
- rtl8xxxu_write16(priv, REG_TRXFF_BNDY + 2, 0x27ff);
+ if (priv->rtlchip == 0x8723b)
+ rtl8xxxu_write16(priv, REG_TRXFF_BNDY + 2, 0x3f7f);
+ else
+ rtl8xxxu_write16(priv, REG_TRXFF_BNDY + 2, 0x27ff);
/*
* Transfer page size is always 128
*/
- val8 = (PBP_PAGE_SIZE_128 << PBP_PAGE_SIZE_RX_SHIFT) |
- (PBP_PAGE_SIZE_128 << PBP_PAGE_SIZE_TX_SHIFT);
+ if (priv->rtlchip == 0x8723b)
+ val8 = (PBP_PAGE_SIZE_256 << PBP_PAGE_SIZE_RX_SHIFT) |
+ (PBP_PAGE_SIZE_256 << PBP_PAGE_SIZE_TX_SHIFT);
+ else
+ val8 = (PBP_PAGE_SIZE_128 << PBP_PAGE_SIZE_RX_SHIFT) |
+ (PBP_PAGE_SIZE_128 << PBP_PAGE_SIZE_TX_SHIFT);
rtl8xxxu_write8(priv, REG_PBP, val8);
/*
@@ -4155,7 +6534,6 @@
* Configure initial WMAC settings
*/
val32 = RCR_ACCEPT_PHYS_MATCH | RCR_ACCEPT_MCAST | RCR_ACCEPT_BCAST |
- /* RCR_CHECK_BSSID_MATCH | RCR_CHECK_BSSID_BEACON | */
RCR_ACCEPT_MGMT_FRAME | RCR_HTC_LOC_CTRL |
RCR_APPEND_PHYSTAT | RCR_APPEND_ICV | RCR_APPEND_MIC;
rtl8xxxu_write32(priv, REG_RCR, val32);
@@ -4220,6 +6598,42 @@
rtl8xxxu_write16(priv, REG_BEACON_TCFG, 0x660F);
/*
+ * Initialize burst parameters
+ */
+ if (priv->rtlchip == 0x8723b) {
+ /*
+ * For USB high speed set 512B packets
+ */
+ val8 = rtl8xxxu_read8(priv, REG_RXDMA_PRO_8723B);
+ val8 &= ~(BIT(4) | BIT(5));
+ val8 |= BIT(4);
+ val8 |= BIT(1) | BIT(2) | BIT(3);
+ rtl8xxxu_write8(priv, REG_RXDMA_PRO_8723B, val8);
+
+ /*
+ * For USB high speed set 512B packets
+ */
+ val8 = rtl8xxxu_read8(priv, REG_HT_SINGLE_AMPDU_8723B);
+ val8 |= BIT(7);
+ rtl8xxxu_write8(priv, REG_HT_SINGLE_AMPDU_8723B, val8);
+
+ rtl8xxxu_write16(priv, REG_MAX_AGGR_NUM, 0x0c14);
+ rtl8xxxu_write8(priv, REG_AMPDU_MAX_TIME_8723B, 0x5e);
+ rtl8xxxu_write32(priv, REG_AGGLEN_LMT, 0xffffffff);
+ rtl8xxxu_write8(priv, REG_RX_PKT_LIMIT, 0x18);
+ rtl8xxxu_write8(priv, REG_PIFS, 0x00);
+ rtl8xxxu_write8(priv, REG_USTIME_TSF_8723B, 0x50);
+ rtl8xxxu_write8(priv, REG_USTIME_EDCA, 0x50);
+
+ val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL);
+ val8 |= BIT(5) | BIT(6);
+ rtl8xxxu_write8(priv, REG_RSV_CTRL, val8);
+ }
+
+ if (priv->fops->init_aggregation)
+ priv->fops->init_aggregation(priv);
+
+ /*
* Enable CCK and OFDM block
*/
val32 = rtl8xxxu_read32(priv, REG_FPGA0_RF_MODE);
@@ -4234,7 +6648,7 @@
/*
* Start out with default power levels for channel 6, 20MHz
*/
- rtl8723a_set_tx_power(priv, 1, false);
+ priv->fops->set_tx_power(priv, 1, false);
/* Let the 8051 take control of antenna setting */
val8 = rtl8xxxu_read8(priv, REG_LEDCFG2);
@@ -4248,78 +6662,37 @@
rtl8xxxu_write16(priv, REG_FAST_EDCA_CTRL, 0);
- /*
- * Not sure if we should get into this at all
- */
- if (priv->iqk_initialized) {
- rtl8xxxu_restore_regs(priv, rtl8723au_iqk_phy_iq_bb_reg,
- priv->bb_recovery_backup,
- RTL8XXXU_BB_REGS);
- } else {
- rtl8723a_phy_iq_calibrate(priv);
- priv->iqk_initialized = true;
- }
+ if (priv->fops->init_statistics)
+ priv->fops->init_statistics(priv);
+
+ rtl8723a_phy_lc_calibrate(priv);
+
+ priv->fops->phy_iq_calibrate(priv);
/*
* This should enable thermal meter
*/
- rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_T_METER, 0x60);
-
- rtl8723a_phy_lc_calibrate(priv);
-
- /* fix USB interface interference issue */
- rtl8xxxu_write8(priv, 0xfe40, 0xe0);
- rtl8xxxu_write8(priv, 0xfe41, 0x8d);
- rtl8xxxu_write8(priv, 0xfe42, 0x80);
- rtl8xxxu_write32(priv, REG_TXDMA_OFFSET_CHK, 0xfd0320);
-
- /* Solve too many protocol error on USB bus */
- /* Can't do this for 8188/8192 UMC A cut parts */
- rtl8xxxu_write8(priv, 0xfe40, 0xe6);
- rtl8xxxu_write8(priv, 0xfe41, 0x94);
- rtl8xxxu_write8(priv, 0xfe42, 0x80);
-
- rtl8xxxu_write8(priv, 0xfe40, 0xe0);
- rtl8xxxu_write8(priv, 0xfe41, 0x19);
- rtl8xxxu_write8(priv, 0xfe42, 0x80);
-
- rtl8xxxu_write8(priv, 0xfe40, 0xe5);
- rtl8xxxu_write8(priv, 0xfe41, 0x91);
- rtl8xxxu_write8(priv, 0xfe42, 0x80);
-
- rtl8xxxu_write8(priv, 0xfe40, 0xe2);
- rtl8xxxu_write8(priv, 0xfe41, 0x81);
- rtl8xxxu_write8(priv, 0xfe42, 0x80);
-
- /* Init BT hw config. */
- rtl8xxxu_init_bt(priv);
-
- /*
- * Not sure if we really need to save these parameters, but the
- * vendor driver does
- */
- val32 = rtl8xxxu_read32(priv, REG_FPGA0_XA_HSSI_PARM2);
- if (val32 & FPGA0_HSSI_PARM2_CCK_HIGH_PWR)
- priv->path_a_hi_power = 1;
-
- val32 = rtl8xxxu_read32(priv, REG_OFDM0_TRX_PATH_ENABLE);
- priv->path_a_rf_paths = val32 & OFDM_RF_PATH_RX_MASK;
-
- val32 = rtl8xxxu_read32(priv, REG_OFDM0_XA_AGC_CORE1);
- priv->path_a_ig_value = val32 & OFDM0_X_AGC_CORE1_IGI_MASK;
+ if (priv->fops->has_s0s1)
+ rtl8xxxu_write_rfreg(priv,
+ RF_A, RF6052_REG_T_METER_8723B, 0x37cf8);
+ else
+ rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_T_METER, 0x60);
/* Set NAV_UPPER to 30000us */
val8 = ((30000 + NAV_UPPER_UNIT - 1) / NAV_UPPER_UNIT);
rtl8xxxu_write8(priv, REG_NAV_UPPER, val8);
- /*
- * 2011/03/09 MH debug only, UMC-B cut pass 2500 S5 test,
- * but we need to fin root cause.
- */
- val32 = rtl8xxxu_read32(priv, REG_FPGA0_RF_MODE);
- if ((val32 & 0xff000000) != 0x83000000) {
- val32 |= FPGA_RF_MODE_CCK;
- rtl8xxxu_write32(priv, REG_FPGA0_RF_MODE, val32);
+ if (priv->rtlchip == 0x8723a) {
+ /*
+ * 2011/03/09 MH debug only, UMC-B cut pass 2500 S5 test,
+ * but we need to find root cause.
+ * This is 8723au only.
+ */
+ val32 = rtl8xxxu_read32(priv, REG_FPGA0_RF_MODE);
+ if ((val32 & 0xff000000) != 0x83000000) {
+ val32 |= FPGA_RF_MODE_CCK;
+ rtl8xxxu_write32(priv, REG_FPGA0_RF_MODE, val32);
+ }
}
val32 = rtl8xxxu_read32(priv, REG_FWHW_TXQ_CTRL);
@@ -4335,7 +6708,7 @@
{
struct rtl8xxxu_priv *priv = hw->priv;
- rtl8xxxu_power_off(priv);
+ priv->fops->power_off(priv);
}
static void rtl8xxxu_cam_write(struct rtl8xxxu_priv *priv,
@@ -4381,7 +6754,7 @@
}
static void rtl8xxxu_sw_scan_start(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif, const u8* mac)
+ struct ieee80211_vif *vif, const u8 *mac)
{
struct rtl8xxxu_priv *priv = hw->priv;
u8 val8;
@@ -4402,11 +6775,13 @@
rtl8xxxu_write8(priv, REG_BEACON_CTRL, val8);
}
-static void rtl8xxxu_update_rate_mask(struct rtl8xxxu_priv *priv,
- u32 ramask, int sgi)
+static void rtl8723au_update_rate_mask(struct rtl8xxxu_priv *priv,
+ u32 ramask, int sgi)
{
struct h2c_cmd h2c;
+ memset(&h2c, 0, sizeof(struct h2c_cmd));
+
h2c.ramask.cmd = H2C_SET_RATE_MASK;
h2c.ramask.mask_lo = cpu_to_le16(ramask & 0xffff);
h2c.ramask.mask_hi = cpu_to_le16(ramask >> 16);
@@ -4415,9 +6790,68 @@
if (sgi)
h2c.ramask.arg |= 0x20;
- dev_dbg(&priv->udev->dev, "%s: rate mask %08x, arg %02x\n", __func__,
- ramask, h2c.ramask.arg);
- rtl8723a_h2c_cmd(priv, &h2c);
+ dev_dbg(&priv->udev->dev, "%s: rate mask %08x, arg %02x, size %zi\n",
+ __func__, ramask, h2c.ramask.arg, sizeof(h2c.ramask));
+ rtl8723a_h2c_cmd(priv, &h2c, sizeof(h2c.ramask));
+}
+
+static void rtl8723bu_update_rate_mask(struct rtl8xxxu_priv *priv,
+ u32 ramask, int sgi)
+{
+ struct h2c_cmd h2c;
+ u8 bw = 0;
+
+ memset(&h2c, 0, sizeof(struct h2c_cmd));
+
+ h2c.b_macid_cfg.cmd = H2C_8723B_MACID_CFG_RAID;
+ h2c.b_macid_cfg.ramask0 = ramask & 0xff;
+ h2c.b_macid_cfg.ramask1 = (ramask >> 8) & 0xff;
+ h2c.b_macid_cfg.ramask2 = (ramask >> 16) & 0xff;
+ h2c.b_macid_cfg.ramask3 = (ramask >> 24) & 0xff;
+
+ h2c.ramask.arg = 0x80;
+ h2c.b_macid_cfg.data1 = 0;
+ if (sgi)
+ h2c.b_macid_cfg.data1 |= BIT(7);
+
+ h2c.b_macid_cfg.data2 = bw;
+
+ dev_dbg(&priv->udev->dev, "%s: rate mask %08x, arg %02x, size %zi\n",
+ __func__, ramask, h2c.ramask.arg, sizeof(h2c.b_macid_cfg));
+ rtl8723a_h2c_cmd(priv, &h2c, sizeof(h2c.b_macid_cfg));
+}
+
+static void rtl8723au_report_connect(struct rtl8xxxu_priv *priv,
+ u8 macid, bool connect)
+{
+ struct h2c_cmd h2c;
+
+ memset(&h2c, 0, sizeof(struct h2c_cmd));
+
+ h2c.joinbss.cmd = H2C_JOIN_BSS_REPORT;
+
+ if (connect)
+ h2c.joinbss.data = H2C_JOIN_BSS_CONNECT;
+ else
+ h2c.joinbss.data = H2C_JOIN_BSS_DISCONNECT;
+
+ rtl8723a_h2c_cmd(priv, &h2c, sizeof(h2c.joinbss));
+}
+
+static void rtl8723bu_report_connect(struct rtl8xxxu_priv *priv,
+ u8 macid, bool connect)
+{
+ struct h2c_cmd h2c;
+
+ memset(&h2c, 0, sizeof(struct h2c_cmd));
+
+ h2c.media_status_rpt.cmd = H2C_8723B_MEDIA_STATUS_RPT;
+ if (connect)
+ h2c.media_status_rpt.parm |= BIT(0);
+ else
+ h2c.media_status_rpt.parm &= ~BIT(0);
+
+ rtl8723a_h2c_cmd(priv, &h2c, sizeof(h2c.media_status_rpt));
}
static void rtl8xxxu_set_basic_rates(struct rtl8xxxu_priv *priv, u32 rate_cfg)
@@ -4452,11 +6886,8 @@
u8 val8;
if (changed & BSS_CHANGED_ASSOC) {
- struct h2c_cmd h2c;
-
dev_dbg(dev, "Changed ASSOC: %i!\n", bss_conf->assoc);
- memset(&h2c, 0, sizeof(struct h2c_cmd));
rtl8xxxu_set_linktype(priv, vif->type);
if (bss_conf->assoc) {
@@ -4486,14 +6917,7 @@
sgi = 1;
rcu_read_unlock();
- rtl8xxxu_update_rate_mask(priv, ramask, sgi);
-
- val32 = rtl8xxxu_read32(priv, REG_RCR);
- val32 |= RCR_CHECK_BSSID_MATCH | RCR_CHECK_BSSID_BEACON;
- rtl8xxxu_write32(priv, REG_RCR, val32);
-
- /* Enable RX of data frames */
- rtl8xxxu_write16(priv, REG_RXFLTMAP2, 0xffff);
+ priv->fops->update_rate_mask(priv, ramask, sgi);
rtl8xxxu_write8(priv, REG_BCN_MAX_ERR, 0xff);
@@ -4503,23 +6927,14 @@
rtl8xxxu_write16(priv, REG_BCN_PSR_RPT,
0xc000 | bss_conf->aid);
- h2c.joinbss.data = H2C_JOIN_BSS_CONNECT;
+ priv->fops->report_connect(priv, 0, true);
} else {
- val32 = rtl8xxxu_read32(priv, REG_RCR);
- val32 &= ~(RCR_CHECK_BSSID_MATCH |
- RCR_CHECK_BSSID_BEACON);
- rtl8xxxu_write32(priv, REG_RCR, val32);
-
val8 = rtl8xxxu_read8(priv, REG_BEACON_CTRL);
val8 |= BEACON_DISABLE_TSF_UPDATE;
rtl8xxxu_write8(priv, REG_BEACON_CTRL, val8);
- /* Disable RX of data frames */
- rtl8xxxu_write16(priv, REG_RXFLTMAP2, 0x0000);
- h2c.joinbss.data = H2C_JOIN_BSS_DISCONNECT;
+ priv->fops->report_connect(priv, 0, false);
}
- h2c.joinbss.cmd = H2C_JOIN_BSS_REPORT;
- rtl8723a_h2c_cmd(priv, &h2c);
}
if (changed & BSS_CHANGED_ERP_PREAMBLE) {
@@ -4594,7 +7009,12 @@
return queue;
}
-static void rtl8xxxu_calc_tx_desc_csum(struct rtl8xxxu_tx_desc *tx_desc)
+/*
+ * Despite newer chips 8723b/8812/8821 having a larger TX descriptor
+ * format. The descriptor checksum is still only calculated over the
+ * initial 32 bytes of the descriptor!
+ */
+static void rtl8xxxu_calc_tx_desc_csum(struct rtl8723au_tx_desc *tx_desc)
{
__le16 *ptr = (__le16 *)tx_desc;
u16 csum = 0;
@@ -4606,7 +7026,7 @@
*/
tx_desc->csum = cpu_to_le16(0);
- for (i = 0; i < (sizeof(struct rtl8xxxu_tx_desc) / sizeof(u16)); i++)
+ for (i = 0; i < (sizeof(struct rtl8723au_tx_desc) / sizeof(u16)); i++)
csum = csum ^ le16_to_cpu(ptr[i]);
tx_desc->csum |= cpu_to_le16(csum);
@@ -4675,13 +7095,15 @@
struct sk_buff *skb = (struct sk_buff *)urb->context;
struct ieee80211_tx_info *tx_info;
struct ieee80211_hw *hw;
+ struct rtl8xxxu_priv *priv;
struct rtl8xxxu_tx_urb *tx_urb =
container_of(urb, struct rtl8xxxu_tx_urb, urb);
tx_info = IEEE80211_SKB_CB(skb);
hw = tx_info->rate_driver_data[0];
+ priv = hw->priv;
- skb_pull(skb, sizeof(struct rtl8xxxu_tx_desc));
+ skb_pull(skb, priv->fops->tx_desc_size);
ieee80211_tx_info_clear_status(tx_info);
tx_info->status.rates[0].idx = -1;
@@ -4692,7 +7114,7 @@
ieee80211_tx_status_irqsafe(hw, skb);
- rtl8xxxu_free_tx_urb(hw->priv, tx_urb);
+ rtl8xxxu_free_tx_urb(priv, tx_urb);
}
static void rtl8xxxu_dump_action(struct device *dev,
@@ -4742,7 +7164,8 @@
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
struct ieee80211_rate *tx_rate = ieee80211_get_tx_rate(hw, tx_info);
struct rtl8xxxu_priv *priv = hw->priv;
- struct rtl8xxxu_tx_desc *tx_desc;
+ struct rtl8723au_tx_desc *tx_desc;
+ struct rtl8723bu_tx_desc *tx_desc40;
struct rtl8xxxu_tx_urb *tx_urb;
struct ieee80211_sta *sta = NULL;
struct ieee80211_vif *vif = tx_info->control.vif;
@@ -4751,16 +7174,18 @@
u16 pktlen = skb->len;
u16 seq_number;
u16 rate_flag = tx_info->control.rates[0].flags;
+ int tx_desc_size = priv->fops->tx_desc_size;
int ret;
+ bool usedesc40, ampdu_enable;
- if (skb_headroom(skb) < sizeof(struct rtl8xxxu_tx_desc)) {
+ if (skb_headroom(skb) < tx_desc_size) {
dev_warn(dev,
"%s: Not enough headroom (%i) for tx descriptor\n",
__func__, skb_headroom(skb));
goto error;
}
- if (unlikely(skb->len > (65535 - sizeof(struct rtl8xxxu_tx_desc)))) {
+ if (unlikely(skb->len > (65535 - tx_desc_size))) {
dev_warn(dev, "%s: Trying to send over-sized skb (%i)\n",
__func__, skb->len);
goto error;
@@ -4779,17 +7204,17 @@
if (ieee80211_is_action(hdr->frame_control))
rtl8xxxu_dump_action(dev, hdr);
+ usedesc40 = (tx_desc_size == 40);
tx_info->rate_driver_data[0] = hw;
if (control && control->sta)
sta = control->sta;
- tx_desc = (struct rtl8xxxu_tx_desc *)
- skb_push(skb, sizeof(struct rtl8xxxu_tx_desc));
+ tx_desc = (struct rtl8723au_tx_desc *)skb_push(skb, tx_desc_size);
- memset(tx_desc, 0, sizeof(struct rtl8xxxu_tx_desc));
+ memset(tx_desc, 0, tx_desc_size);
tx_desc->pkt_size = cpu_to_le16(pktlen);
- tx_desc->pkt_offset = sizeof(struct rtl8xxxu_tx_desc);
+ tx_desc->pkt_offset = tx_desc_size;
tx_desc->txdw0 =
TXDESC_OWN | TXDESC_FIRST_SEGMENT | TXDESC_LAST_SEGMENT;
@@ -4815,19 +7240,8 @@
}
}
- seq_number = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl));
- tx_desc->txdw3 = cpu_to_le32((u32)seq_number << TXDESC_SEQ_SHIFT);
-
- if (rate_flag & IEEE80211_TX_RC_MCS)
- rate = tx_info->control.rates[0].idx + DESC_RATE_MCS0;
- else
- rate = tx_rate->hw_value;
- tx_desc->txdw5 = cpu_to_le32(rate);
-
- if (ieee80211_is_data(hdr->frame_control))
- tx_desc->txdw5 |= cpu_to_le32(0x0001ff00);
-
/* (tx_info->flags & IEEE80211_TX_CTL_AMPDU) && */
+ ampdu_enable = false;
if (ieee80211_is_data_qos(hdr->frame_control) && sta) {
if (sta->ht_cap.ht_supported) {
u32 ampdu, val32;
@@ -4835,35 +7249,118 @@
ampdu = (u32)sta->ht_cap.ampdu_density;
val32 = ampdu << TXDESC_AMPDU_DENSITY_SHIFT;
tx_desc->txdw2 |= cpu_to_le32(val32);
- tx_desc->txdw1 |= cpu_to_le32(TXDESC_AGG_ENABLE);
- } else
- tx_desc->txdw1 |= cpu_to_le32(TXDESC_BK);
- } else
- tx_desc->txdw1 |= cpu_to_le32(TXDESC_BK);
- if (ieee80211_is_data_qos(hdr->frame_control))
- tx_desc->txdw4 |= cpu_to_le32(TXDESC_QOS);
- if (rate_flag & IEEE80211_TX_RC_USE_SHORT_PREAMBLE ||
- (sta && vif && vif->bss_conf.use_short_preamble))
- tx_desc->txdw4 |= cpu_to_le32(TXDESC_SHORT_PREAMBLE);
- if (rate_flag & IEEE80211_TX_RC_SHORT_GI ||
- (ieee80211_is_data_qos(hdr->frame_control) &&
- sta && sta->ht_cap.cap &
- (IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_SGI_20))) {
- tx_desc->txdw5 |= cpu_to_le32(TXDESC_SHORT_GI);
- }
- if (ieee80211_is_mgmt(hdr->frame_control)) {
- tx_desc->txdw5 = cpu_to_le32(tx_rate->hw_value);
- tx_desc->txdw4 |= cpu_to_le32(TXDESC_USE_DRIVER_RATE);
- tx_desc->txdw5 |= cpu_to_le32(6 << TXDESC_RETRY_LIMIT_SHIFT);
- tx_desc->txdw5 |= cpu_to_le32(TXDESC_RETRY_LIMIT_ENABLE);
+ ampdu_enable = true;
+ }
}
- if (rate_flag & IEEE80211_TX_RC_USE_RTS_CTS) {
- /* Use RTS rate 24M - does the mac80211 tell us which to use? */
- tx_desc->txdw4 |= cpu_to_le32(DESC_RATE_24M);
- tx_desc->txdw4 |= cpu_to_le32(TXDESC_RTS_CTS_ENABLE);
- tx_desc->txdw4 |= cpu_to_le32(TXDESC_HW_RTS_ENABLE);
+ if (rate_flag & IEEE80211_TX_RC_MCS)
+ rate = tx_info->control.rates[0].idx + DESC_RATE_MCS0;
+ else
+ rate = tx_rate->hw_value;
+
+ seq_number = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl));
+ if (!usedesc40) {
+ tx_desc->txdw5 = cpu_to_le32(rate);
+
+ if (ieee80211_is_data(hdr->frame_control))
+ tx_desc->txdw5 |= cpu_to_le32(0x0001ff00);
+
+ tx_desc->txdw3 =
+ cpu_to_le32((u32)seq_number << TXDESC_SEQ_SHIFT_8723A);
+
+ if (ampdu_enable)
+ tx_desc->txdw1 |= cpu_to_le32(TXDESC_AGG_ENABLE_8723A);
+ else
+ tx_desc->txdw1 |= cpu_to_le32(TXDESC_AGG_BREAK_8723A);
+
+ if (ieee80211_is_mgmt(hdr->frame_control)) {
+ tx_desc->txdw5 = cpu_to_le32(tx_rate->hw_value);
+ tx_desc->txdw4 |=
+ cpu_to_le32(TXDESC_USE_DRIVER_RATE_8723A);
+ tx_desc->txdw5 |=
+ cpu_to_le32(6 <<
+ TXDESC_RETRY_LIMIT_SHIFT_8723A);
+ tx_desc->txdw5 |=
+ cpu_to_le32(TXDESC_RETRY_LIMIT_ENABLE_8723A);
+ }
+
+ if (ieee80211_is_data_qos(hdr->frame_control))
+ tx_desc->txdw4 |= cpu_to_le32(TXDESC_QOS_8723A);
+
+ if (rate_flag & IEEE80211_TX_RC_USE_SHORT_PREAMBLE ||
+ (sta && vif && vif->bss_conf.use_short_preamble))
+ tx_desc->txdw4 |=
+ cpu_to_le32(TXDESC_SHORT_PREAMBLE_8723A);
+
+ if (rate_flag & IEEE80211_TX_RC_SHORT_GI ||
+ (ieee80211_is_data_qos(hdr->frame_control) &&
+ sta && sta->ht_cap.cap &
+ (IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_SGI_20))) {
+ tx_desc->txdw5 |= cpu_to_le32(TXDESC_SHORT_GI);
+ }
+
+ if (rate_flag & IEEE80211_TX_RC_USE_RTS_CTS) {
+ /*
+ * Use RTS rate 24M - does the mac80211 tell
+ * us which to use?
+ */
+ tx_desc->txdw4 |=
+ cpu_to_le32(DESC_RATE_24M <<
+ TXDESC_RTS_RATE_SHIFT_8723A);
+ tx_desc->txdw4 |=
+ cpu_to_le32(TXDESC_RTS_CTS_ENABLE_8723A);
+ tx_desc->txdw4 |=
+ cpu_to_le32(TXDESC_HW_RTS_ENABLE_8723A);
+ }
+ } else {
+ tx_desc40 = (struct rtl8723bu_tx_desc *)tx_desc;
+
+ tx_desc40->txdw4 = cpu_to_le32(rate);
+ if (ieee80211_is_data(hdr->frame_control)) {
+ tx_desc->txdw4 |=
+ cpu_to_le32(0x1f <<
+ TXDESC_DATA_RATE_FB_SHIFT_8723B);
+ }
+
+ tx_desc40->txdw9 =
+ cpu_to_le32((u32)seq_number << TXDESC_SEQ_SHIFT_8723B);
+
+ if (ampdu_enable)
+ tx_desc40->txdw2 |=
+ cpu_to_le32(TXDESC_AGG_ENABLE_8723B);
+ else
+ tx_desc40->txdw2 |= cpu_to_le32(TXDESC_AGG_BREAK_8723B);
+
+ if (ieee80211_is_mgmt(hdr->frame_control)) {
+ tx_desc40->txdw4 = cpu_to_le32(tx_rate->hw_value);
+ tx_desc40->txdw3 |=
+ cpu_to_le32(TXDESC_USE_DRIVER_RATE_8723B);
+ tx_desc40->txdw4 |=
+ cpu_to_le32(6 <<
+ TXDESC_RETRY_LIMIT_SHIFT_8723B);
+ tx_desc40->txdw4 |=
+ cpu_to_le32(TXDESC_RETRY_LIMIT_ENABLE_8723B);
+ }
+
+ if (rate_flag & IEEE80211_TX_RC_USE_SHORT_PREAMBLE ||
+ (sta && vif && vif->bss_conf.use_short_preamble))
+ tx_desc40->txdw5 |=
+ cpu_to_le32(TXDESC_SHORT_PREAMBLE_8723B);
+
+ if (rate_flag & IEEE80211_TX_RC_USE_RTS_CTS) {
+ /*
+ * Use RTS rate 24M - does the mac80211 tell
+ * us which to use?
+ */
+ tx_desc->txdw4 |=
+ cpu_to_le32(DESC_RATE_24M <<
+ TXDESC_RTS_RATE_SHIFT_8723B);
+ tx_desc->txdw3 |=
+ cpu_to_le32(TXDESC_RTS_CTS_ENABLE_8723B);
+ tx_desc->txdw3 |=
+ cpu_to_le32(TXDESC_HW_RTS_ENABLE_8723B);
+ }
}
rtl8xxxu_calc_tx_desc_csum(tx_desc);
@@ -4885,13 +7382,13 @@
static void rtl8xxxu_rx_parse_phystats(struct rtl8xxxu_priv *priv,
struct ieee80211_rx_status *rx_status,
- struct rtl8xxxu_rx_desc *rx_desc,
- struct rtl8723au_phy_stats *phy_stats)
+ struct rtl8723au_phy_stats *phy_stats,
+ u32 rxmcs)
{
if (phy_stats->sgi_en)
rx_status->flag |= RX_FLAG_SHORT_GI;
- if (rx_desc->rxmcs < DESC_RATE_6M) {
+ if (rxmcs < DESC_RATE_6M) {
/*
* Handle PHY stats for CCK rates
*/
@@ -5002,6 +7499,138 @@
}
}
+static int rtl8723au_parse_rx_desc(struct rtl8xxxu_priv *priv,
+ struct sk_buff *skb,
+ struct ieee80211_rx_status *rx_status)
+{
+ struct rtl8xxxu_rx_desc *rx_desc = (struct rtl8xxxu_rx_desc *)skb->data;
+ struct rtl8723au_phy_stats *phy_stats;
+ int drvinfo_sz, desc_shift;
+
+ skb_pull(skb, sizeof(struct rtl8xxxu_rx_desc));
+
+ phy_stats = (struct rtl8723au_phy_stats *)skb->data;
+
+ drvinfo_sz = rx_desc->drvinfo_sz * 8;
+ desc_shift = rx_desc->shift;
+ skb_pull(skb, drvinfo_sz + desc_shift);
+
+ if (rx_desc->phy_stats)
+ rtl8xxxu_rx_parse_phystats(priv, rx_status, phy_stats,
+ rx_desc->rxmcs);
+
+ rx_status->mactime = le32_to_cpu(rx_desc->tsfl);
+ rx_status->flag |= RX_FLAG_MACTIME_START;
+
+ if (!rx_desc->swdec)
+ rx_status->flag |= RX_FLAG_DECRYPTED;
+ if (rx_desc->crc32)
+ rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
+ if (rx_desc->bw)
+ rx_status->flag |= RX_FLAG_40MHZ;
+
+ if (rx_desc->rxht) {
+ rx_status->flag |= RX_FLAG_HT;
+ rx_status->rate_idx = rx_desc->rxmcs - DESC_RATE_MCS0;
+ } else {
+ rx_status->rate_idx = rx_desc->rxmcs;
+ }
+
+ return RX_TYPE_DATA_PKT;
+}
+
+static int rtl8723bu_parse_rx_desc(struct rtl8xxxu_priv *priv,
+ struct sk_buff *skb,
+ struct ieee80211_rx_status *rx_status)
+{
+ struct rtl8723bu_rx_desc *rx_desc =
+ (struct rtl8723bu_rx_desc *)skb->data;
+ struct rtl8723au_phy_stats *phy_stats;
+ int drvinfo_sz, desc_shift;
+
+ skb_pull(skb, sizeof(struct rtl8723bu_rx_desc));
+
+ phy_stats = (struct rtl8723au_phy_stats *)skb->data;
+
+ drvinfo_sz = rx_desc->drvinfo_sz * 8;
+ desc_shift = rx_desc->shift;
+ skb_pull(skb, drvinfo_sz + desc_shift);
+
+ if (rx_desc->rpt_sel) {
+ struct device *dev = &priv->udev->dev;
+ dev_dbg(dev, "%s: C2H packet\n", __func__);
+ return RX_TYPE_C2H;
+ }
+
+ if (rx_desc->phy_stats)
+ rtl8xxxu_rx_parse_phystats(priv, rx_status, phy_stats,
+ rx_desc->rxmcs);
+
+ rx_status->mactime = le32_to_cpu(rx_desc->tsfl);
+ rx_status->flag |= RX_FLAG_MACTIME_START;
+
+ if (!rx_desc->swdec)
+ rx_status->flag |= RX_FLAG_DECRYPTED;
+ if (rx_desc->crc32)
+ rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
+ if (rx_desc->bw)
+ rx_status->flag |= RX_FLAG_40MHZ;
+
+ if (rx_desc->rxmcs >= DESC_RATE_MCS0) {
+ rx_status->flag |= RX_FLAG_HT;
+ rx_status->rate_idx = rx_desc->rxmcs - DESC_RATE_MCS0;
+ } else {
+ rx_status->rate_idx = rx_desc->rxmcs;
+ }
+
+ return RX_TYPE_DATA_PKT;
+}
+
+static void rtl8723bu_handle_c2h(struct rtl8xxxu_priv *priv,
+ struct sk_buff *skb)
+{
+ struct rtl8723bu_c2h *c2h = (struct rtl8723bu_c2h *)skb->data;
+ struct device *dev = &priv->udev->dev;
+ int len;
+
+ len = skb->len - 2;
+
+ dev_dbg(dev, "C2H ID %02x seq %02x, len %02x source %02x\n",
+ c2h->id, c2h->seq, len, c2h->bt_info.response_source);
+
+ switch(c2h->id) {
+ case C2H_8723B_BT_INFO:
+ if (c2h->bt_info.response_source >
+ BT_INFO_SRC_8723B_BT_ACTIVE_SEND)
+ dev_dbg(dev, "C2H_BT_INFO WiFi only firmware\n");
+ else
+ dev_dbg(dev, "C2H_BT_INFO BT/WiFi coexist firmware\n");
+
+ if (c2h->bt_info.bt_has_reset)
+ dev_dbg(dev, "BT has been reset\n");
+ if (c2h->bt_info.tx_rx_mask)
+ dev_dbg(dev, "BT TRx mask\n");
+
+ break;
+ case C2H_8723B_BT_MP_INFO:
+ dev_dbg(dev, "C2H_MP_INFO ext ID %02x, status %02x\n",
+ c2h->bt_mp_info.ext_id, c2h->bt_mp_info.status);
+ break;
+ case C2H_8723B_RA_REPORT:
+ dev_dbg(dev,
+ "C2H RA RPT: rate %02x, unk %i, macid %02x, noise %i\n",
+ c2h->ra_report.rate, c2h->ra_report.dummy0_0,
+ c2h->ra_report.macid, c2h->ra_report.noisy_state);
+ break;
+ default:
+ dev_info(dev, "Unhandled C2H event %02x seq %02x\n",
+ c2h->id, c2h->seq);
+ print_hex_dump(KERN_INFO, "C2H content: ", DUMP_PREFIX_NONE,
+ 16, 1, c2h->raw.payload, len, false);
+ break;
+ }
+}
+
static void rtl8xxxu_rx_complete(struct urb *urb)
{
struct rtl8xxxu_rx_urb *rx_urb =
@@ -5009,59 +7638,32 @@
struct ieee80211_hw *hw = rx_urb->hw;
struct rtl8xxxu_priv *priv = hw->priv;
struct sk_buff *skb = (struct sk_buff *)urb->context;
- struct rtl8xxxu_rx_desc *rx_desc = (struct rtl8xxxu_rx_desc *)skb->data;
- struct rtl8723au_phy_stats *phy_stats;
struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
- struct ieee80211_mgmt *mgmt;
struct device *dev = &priv->udev->dev;
__le32 *_rx_desc_le = (__le32 *)skb->data;
u32 *_rx_desc = (u32 *)skb->data;
- int cnt, len, drvinfo_sz, desc_shift, i;
+ int rx_type, i;
for (i = 0; i < (sizeof(struct rtl8xxxu_rx_desc) / sizeof(u32)); i++)
_rx_desc[i] = le32_to_cpu(_rx_desc_le[i]);
- cnt = rx_desc->frag;
- len = rx_desc->pktlen;
- drvinfo_sz = rx_desc->drvinfo_sz * 8;
- desc_shift = rx_desc->shift;
skb_put(skb, urb->actual_length);
if (urb->status == 0) {
- skb_pull(skb, sizeof(struct rtl8xxxu_rx_desc));
- phy_stats = (struct rtl8723au_phy_stats *)skb->data;
-
- skb_pull(skb, drvinfo_sz + desc_shift);
-
- mgmt = (struct ieee80211_mgmt *)skb->data;
-
memset(rx_status, 0, sizeof(struct ieee80211_rx_status));
- if (rx_desc->phy_stats)
- rtl8xxxu_rx_parse_phystats(priv, rx_status,
- rx_desc, phy_stats);
+ rx_type = priv->fops->parse_rx_desc(priv, skb, rx_status);
rx_status->freq = hw->conf.chandef.chan->center_freq;
rx_status->band = hw->conf.chandef.chan->band;
- rx_status->mactime = le32_to_cpu(rx_desc->tsfl);
- rx_status->flag |= RX_FLAG_MACTIME_START;
-
- if (!rx_desc->swdec)
- rx_status->flag |= RX_FLAG_DECRYPTED;
- if (rx_desc->crc32)
- rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
- if (rx_desc->bw)
- rx_status->flag |= RX_FLAG_40MHZ;
-
- if (rx_desc->rxht) {
- rx_status->flag |= RX_FLAG_HT;
- rx_status->rate_idx = rx_desc->rxmcs - DESC_RATE_MCS0;
- } else {
- rx_status->rate_idx = rx_desc->rxmcs;
+ if (rx_type == RX_TYPE_DATA_PKT)
+ ieee80211_rx_irqsafe(hw, skb);
+ else {
+ rtl8723bu_handle_c2h(priv, skb);
+ dev_kfree_skb(skb);
}
- ieee80211_rx_irqsafe(hw, skb);
skb = NULL;
rx_urb->urb.context = NULL;
rtl8xxxu_queue_rx_urb(priv, rx_urb);
@@ -5218,9 +7820,9 @@
channel = hw->conf.chandef.chan->hw_value;
- rtl8723a_set_tx_power(priv, channel, ht40);
+ priv->fops->set_tx_power(priv, channel, ht40);
- rtl8723au_config_channel(hw);
+ priv->fops->config_channel(hw);
}
exit:
@@ -5284,11 +7886,56 @@
unsigned int *total_flags, u64 multicast)
{
struct rtl8xxxu_priv *priv = hw->priv;
+ u32 rcr = rtl8xxxu_read32(priv, REG_RCR);
dev_dbg(&priv->udev->dev, "%s: changed_flags %08x, total_flags %08x\n",
__func__, changed_flags, *total_flags);
- *total_flags &= (FIF_ALLMULTI | FIF_CONTROL | FIF_BCN_PRBRESP_PROMISC);
+ /*
+ * FIF_ALLMULTI ignored as all multicast frames are accepted (REG_MAR)
+ */
+
+ if (*total_flags & FIF_FCSFAIL)
+ rcr |= RCR_ACCEPT_CRC32;
+ else
+ rcr &= ~RCR_ACCEPT_CRC32;
+
+ /*
+ * FIF_PLCPFAIL not supported?
+ */
+
+ if (*total_flags & FIF_BCN_PRBRESP_PROMISC)
+ rcr &= ~RCR_CHECK_BSSID_BEACON;
+ else
+ rcr |= RCR_CHECK_BSSID_BEACON;
+
+ if (*total_flags & FIF_CONTROL)
+ rcr |= RCR_ACCEPT_CTRL_FRAME;
+ else
+ rcr &= ~RCR_ACCEPT_CTRL_FRAME;
+
+ if (*total_flags & FIF_OTHER_BSS) {
+ rcr |= RCR_ACCEPT_AP;
+ rcr &= ~RCR_CHECK_BSSID_MATCH;
+ } else {
+ rcr &= ~RCR_ACCEPT_AP;
+ rcr |= RCR_CHECK_BSSID_MATCH;
+ }
+
+ if (*total_flags & FIF_PSPOLL)
+ rcr |= RCR_ACCEPT_PM;
+ else
+ rcr &= ~RCR_ACCEPT_PM;
+
+ /*
+ * FIF_PROBE_REQ ignored as probe requests always seem to be accepted
+ */
+
+ rtl8xxxu_write32(priv, REG_RCR, rcr);
+
+ *total_flags &= (FIF_ALLMULTI | FIF_FCSFAIL | FIF_BCN_PRBRESP_PROMISC |
+ FIF_CONTROL | FIF_OTHER_BSS | FIF_PSPOLL |
+ FIF_PROBE_REQ);
}
static int rtl8xxxu_set_rts_threshold(struct ieee80211_hw *hw, u32 rts)
@@ -5431,10 +8078,12 @@
init_usb_anchor(&priv->tx_anchor);
init_usb_anchor(&priv->int_anchor);
- rtl8723a_enable_rf(priv);
- ret = rtl8xxxu_submit_int_urb(hw);
- if (ret)
- goto exit;
+ priv->fops->enable_rf(priv);
+ if (priv->usb_interrupts) {
+ ret = rtl8xxxu_submit_int_urb(hw);
+ if (ret)
+ goto exit;
+ }
for (i = 0; i < RTL8XXXU_TX_URBS; i++) {
tx_urb = kmalloc(sizeof(struct rtl8xxxu_tx_urb), GFP_KERNEL);
@@ -5473,12 +8122,9 @@
}
exit:
/*
- * Disable all data frames
+ * Accept all data and mgmt frames
*/
- rtl8xxxu_write16(priv, REG_RXFLTMAP2, 0x0000);
- /*
- * Accept all mgmt frames
- */
+ rtl8xxxu_write16(priv, REG_RXFLTMAP2, 0xffff);
rtl8xxxu_write16(priv, REG_RXFLTMAP0, 0xffff);
rtl8xxxu_write32(priv, REG_OFDM0_XA_AGC_CORE1, 0x6954341e);
@@ -5512,14 +8158,16 @@
usb_kill_anchored_urbs(&priv->rx_anchor);
usb_kill_anchored_urbs(&priv->tx_anchor);
- usb_kill_anchored_urbs(&priv->int_anchor);
+ if (priv->usb_interrupts)
+ usb_kill_anchored_urbs(&priv->int_anchor);
- rtl8723a_disable_rf(priv);
+ priv->fops->disable_rf(priv);
/*
* Disable interrupts
*/
- rtl8xxxu_write32(priv, REG_USB_HIMR, 0);
+ if (priv->usb_interrupts)
+ rtl8xxxu_write32(priv, REG_USB_HIMR, 0);
rtl8xxxu_free_rx_resources(priv);
rtl8xxxu_free_tx_resources(priv);
@@ -5650,7 +8298,7 @@
}
if (untested) {
- rtl8xxxu_debug = RTL8XXXU_DEBUG_EFUSE;
+ rtl8xxxu_debug |= RTL8XXXU_DEBUG_EFUSE;
dev_info(&udev->dev,
"This Realtek USB WiFi dongle (0x%04x:0x%04x) is untested!\n",
id->idVendor, id->idProduct);
@@ -5744,7 +8392,7 @@
SET_IEEE80211_DEV(priv->hw, &interface->dev);
SET_IEEE80211_PERM_ADDR(hw, priv->mac_addr);
- hw->extra_tx_headroom = sizeof(struct rtl8xxxu_tx_desc);
+ hw->extra_tx_headroom = priv->fops->tx_desc_size;
ieee80211_hw_set(hw, SIGNAL_DBM);
/*
* The firmware handles rate control
@@ -5792,7 +8440,54 @@
.parse_efuse = rtl8723au_parse_efuse,
.load_firmware = rtl8723au_load_firmware,
.power_on = rtl8723au_power_on,
+ .power_off = rtl8xxxu_power_off,
+ .reset_8051 = rtl8xxxu_reset_8051,
+ .llt_init = rtl8xxxu_init_llt_table,
+ .phy_iq_calibrate = rtl8723au_phy_iq_calibrate,
+ .config_channel = rtl8723au_config_channel,
+ .parse_rx_desc = rtl8723au_parse_rx_desc,
+ .enable_rf = rtl8723a_enable_rf,
+ .disable_rf = rtl8723a_disable_rf,
+ .set_tx_power = rtl8723a_set_tx_power,
+ .update_rate_mask = rtl8723au_update_rate_mask,
+ .report_connect = rtl8723au_report_connect,
.writeN_block_size = 1024,
+ .mbox_ext_reg = REG_HMBOX_EXT_0,
+ .mbox_ext_width = 2,
+ .tx_desc_size = sizeof(struct rtl8723au_tx_desc),
+ .adda_1t_init = 0x0b1b25a0,
+ .adda_1t_path_on = 0x0bdb25a0,
+ .adda_2t_path_on_a = 0x04db25a4,
+ .adda_2t_path_on_b = 0x0b1b25a4,
+};
+
+static struct rtl8xxxu_fileops rtl8723bu_fops = {
+ .parse_efuse = rtl8723bu_parse_efuse,
+ .load_firmware = rtl8723bu_load_firmware,
+ .power_on = rtl8723bu_power_on,
+ .power_off = rtl8723bu_power_off,
+ .reset_8051 = rtl8723bu_reset_8051,
+ .llt_init = rtl8xxxu_auto_llt_table,
+ .phy_init_antenna_selection = rtl8723bu_phy_init_antenna_selection,
+ .phy_iq_calibrate = rtl8723bu_phy_iq_calibrate,
+ .config_channel = rtl8723bu_config_channel,
+ .parse_rx_desc = rtl8723bu_parse_rx_desc,
+ .init_aggregation = rtl8723bu_init_aggregation,
+ .init_statistics = rtl8723bu_init_statistics,
+ .enable_rf = rtl8723b_enable_rf,
+ .disable_rf = rtl8723b_disable_rf,
+ .set_tx_power = rtl8723b_set_tx_power,
+ .update_rate_mask = rtl8723bu_update_rate_mask,
+ .report_connect = rtl8723bu_report_connect,
+ .writeN_block_size = 1024,
+ .mbox_ext_reg = REG_HMBOX_EXT0_8723B,
+ .mbox_ext_width = 4,
+ .tx_desc_size = sizeof(struct rtl8723bu_tx_desc),
+ .has_s0s1 = 1,
+ .adda_1t_init = 0x01c00014,
+ .adda_1t_path_on = 0x01c00014,
+ .adda_2t_path_on_a = 0x01c00014,
+ .adda_2t_path_on_b = 0x01c00014,
};
#ifdef CONFIG_RTL8XXXU_UNTESTED
@@ -5801,11 +8496,55 @@
.parse_efuse = rtl8192cu_parse_efuse,
.load_firmware = rtl8192cu_load_firmware,
.power_on = rtl8192cu_power_on,
+ .power_off = rtl8xxxu_power_off,
+ .reset_8051 = rtl8xxxu_reset_8051,
+ .llt_init = rtl8xxxu_init_llt_table,
+ .phy_iq_calibrate = rtl8723au_phy_iq_calibrate,
+ .config_channel = rtl8723au_config_channel,
+ .parse_rx_desc = rtl8723au_parse_rx_desc,
+ .enable_rf = rtl8723a_enable_rf,
+ .disable_rf = rtl8723a_disable_rf,
+ .set_tx_power = rtl8723a_set_tx_power,
+ .update_rate_mask = rtl8723au_update_rate_mask,
+ .report_connect = rtl8723au_report_connect,
.writeN_block_size = 128,
+ .mbox_ext_reg = REG_HMBOX_EXT_0,
+ .mbox_ext_width = 2,
+ .tx_desc_size = sizeof(struct rtl8723au_tx_desc),
+ .adda_1t_init = 0x0b1b25a0,
+ .adda_1t_path_on = 0x0bdb25a0,
+ .adda_2t_path_on_a = 0x04db25a4,
+ .adda_2t_path_on_b = 0x0b1b25a4,
};
#endif
+static struct rtl8xxxu_fileops rtl8192eu_fops = {
+ .parse_efuse = rtl8192eu_parse_efuse,
+ .load_firmware = rtl8192eu_load_firmware,
+ .power_on = rtl8192eu_power_on,
+ .power_off = rtl8xxxu_power_off,
+ .reset_8051 = rtl8xxxu_reset_8051,
+ .llt_init = rtl8xxxu_auto_llt_table,
+ .phy_iq_calibrate = rtl8723bu_phy_iq_calibrate,
+ .config_channel = rtl8723bu_config_channel,
+ .parse_rx_desc = rtl8723bu_parse_rx_desc,
+ .enable_rf = rtl8723b_enable_rf,
+ .disable_rf = rtl8723b_disable_rf,
+ .set_tx_power = rtl8723b_set_tx_power,
+ .update_rate_mask = rtl8723au_update_rate_mask,
+ .report_connect = rtl8723au_report_connect,
+ .writeN_block_size = 128,
+ .mbox_ext_reg = REG_HMBOX_EXT0_8723B,
+ .mbox_ext_width = 4,
+ .tx_desc_size = sizeof(struct rtl8723au_tx_desc),
+ .has_s0s1 = 1,
+ .adda_1t_init = 0x0fc01616,
+ .adda_1t_path_on = 0x0fc01616,
+ .adda_2t_path_on_a = 0x0fc01616,
+ .adda_2t_path_on_b = 0x0fc01616,
+};
+
static struct usb_device_id dev_table[] = {
{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x8724, 0xff, 0xff, 0xff),
.driver_info = (unsigned long)&rtl8723au_fops},
@@ -5813,6 +8552,10 @@
.driver_info = (unsigned long)&rtl8723au_fops},
{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x0724, 0xff, 0xff, 0xff),
.driver_info = (unsigned long)&rtl8723au_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x818b, 0xff, 0xff, 0xff),
+ .driver_info = (unsigned long)&rtl8192eu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0xb720, 0xff, 0xff, 0xff),
+ .driver_info = (unsigned long)&rtl8723bu_fops},
#ifdef CONFIG_RTL8XXXU_UNTESTED
/* Still supported by rtlwifi */
{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x8176, 0xff, 0xff, 0xff),
@@ -5891,8 +8634,6 @@
.driver_info = (unsigned long)&rtl8192cu_fops},
{USB_DEVICE_AND_INTERFACE_INFO(0xcdab, 0x8010, 0xff, 0xff, 0xff),
.driver_info = (unsigned long)&rtl8192cu_fops},
-{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x317f, 0xff, 0xff, 0xff),
- .driver_info = (unsigned long)&rtl8192cu_fops}, /* Netcore 8188RU */
{USB_DEVICE_AND_INTERFACE_INFO(0x04f2, 0xaff7, 0xff, 0xff, 0xff),
.driver_info = (unsigned long)&rtl8192cu_fops},
{USB_DEVICE_AND_INTERFACE_INFO(0x04f2, 0xaff9, 0xff, 0xff, 0xff),
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
index f2a1bac..7b73654 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
@@ -34,7 +34,7 @@
#define RTL8XXXU_MAX_REG_POLL 500
#define USB_INTR_CONTENT_LENGTH 56
-#define RTL8XXXU_OUT_ENDPOINTS 3
+#define RTL8XXXU_OUT_ENDPOINTS 4
#define REALTEK_USB_READ 0xc0
#define REALTEK_USB_WRITE 0x40
@@ -53,14 +53,24 @@
#define RTL8723A_CHANNEL_GROUPS 3
#define RTL8723A_MAX_RF_PATHS 2
+#define RTL8723B_CHANNEL_GROUPS 6
+#define RTL8723B_TX_COUNT 4
+#define RTL8723B_MAX_RF_PATHS 4
+#define RTL8XXXU_MAX_CHANNEL_GROUPS 6
#define RF6052_MAX_TX_PWR 0x3f
-#define EFUSE_MAP_LEN_8723A 256
-#define EFUSE_MAX_SECTION_8723A 32
+#define EFUSE_MAP_LEN 512
+#define EFUSE_MAX_SECTION_8723A 64
#define EFUSE_REAL_CONTENT_LEN_8723A 512
#define EFUSE_BT_MAP_LEN_8723A 1024
#define EFUSE_MAX_WORD_UNIT 4
+enum rtl8xxxu_rx_type {
+ RX_TYPE_DATA_PKT = 0,
+ RX_TYPE_C2H = 1,
+ RX_TYPE_ERROR = -1
+};
+
struct rtl8xxxu_rx_desc {
#ifdef __LITTLE_ENDIAN
u32 pktlen:14;
@@ -197,7 +207,132 @@
#endif
};
-struct rtl8xxxu_tx_desc {
+struct rtl8723bu_rx_desc {
+#ifdef __LITTLE_ENDIAN
+ u32 pktlen:14;
+ u32 crc32:1;
+ u32 icverr:1;
+ u32 drvinfo_sz:4;
+ u32 security:3;
+ u32 qos:1;
+ u32 shift:2;
+ u32 phy_stats:1;
+ u32 swdec:1;
+ u32 ls:1;
+ u32 fs:1;
+ u32 eor:1;
+ u32 own:1;
+
+ u32 macid:7;
+ u32 dummy1_0:1;
+ u32 tid:4;
+ u32 dummy1_1:1;
+ u32 amsdu:1;
+ u32 rxid_match:1;
+ u32 paggr:1;
+ u32 a1fit:4; /* 16 */
+ u32 chkerr:1;
+ u32 ipver:1;
+ u32 tcpudp:1;
+ u32 chkvld:1;
+ u32 pam:1;
+ u32 pwr:1;
+ u32 more_data:1;
+ u32 more_frag:1;
+ u32 type:2;
+ u32 mc:1;
+ u32 bc:1;
+
+ u32 seq:12;
+ u32 frag:4;
+ u32 rx_is_qos:1; /* 16 */
+ u32 dummy2_0:1;
+ u32 wlanhd_iv_len:6;
+ u32 dummy2_1:4;
+ u32 rpt_sel:1;
+ u32 dummy2_2:3;
+
+ u32 rxmcs:7;
+ u32 dummy3_0:3;
+ u32 htc:1;
+ u32 eosp:1;
+ u32 bssidfit:2;
+ u32 dummy3_1:2;
+ u32 usb_agg_pktnum:8; /* 16 */
+ u32 dummy3_2:5;
+ u32 pattern_match:1;
+ u32 unicast_match:1;
+ u32 magic_match:1;
+
+ u32 splcp:1;
+ u32 ldcp:1;
+ u32 stbc:1;
+ u32 dummy4_0:1;
+ u32 bw:2;
+ u32 dummy4_1:26;
+#else
+ u32 own:1;
+ u32 eor:1;
+ u32 fs:1;
+ u32 ls:1;
+ u32 swdec:1;
+ u32 phy_stats:1;
+ u32 shift:2;
+ u32 qos:1;
+ u32 security:3;
+ u32 drvinfo_sz:4;
+ u32 icverr:1;
+ u32 crc32:1;
+ u32 pktlen:14;
+
+ u32 bc:1;
+ u32 mc:1;
+ u32 type:2;
+ u32 mf:1;
+ u32 md:1;
+ u32 pwr:1;
+ u32 pam:1;
+ u32 a2fit:4;
+ u32 a1fit:4;
+ u32 faggr:1;
+ u32 paggr:1;
+ u32 amsdu:1;
+ u32 hwrsvd:4;
+ u32 tid:4;
+ u32 macid:5;
+
+ u32 dummy2_2:3;
+ u32 rpt_sel:1;
+ u32 dummy2_1:4;
+ u32 wlanhd_iv_len:6;
+ u32 dummy2_0:1;
+ u32 rx_is_qos:1;
+ u32 frag:4; /* 16 */
+ u32 seq:12;
+
+ u32 magic_match:1;
+ u32 unicast_match:1;
+ u32 pattern_match:1;
+ u32 dummy3_2:5;
+ u32 usb_agg_pktnum:8;
+ u32 dummy3_1:2; /* 16 */
+ u32 bssidfit:2;
+ u32 eosp:1;
+ u32 htc:1;
+ u32 dummy3_0:3;
+ u32 rxmcs:7;
+
+ u32 dumm4_1:26;
+ u32 bw:2;
+ u32 dummy4_0:1;
+ u32 stbc:1;
+ u32 ldcp:1;
+ u32 splcp:1;
+#endif
+ __le32 tsfl;
+};
+
+struct rtl8723au_tx_desc {
__le16 pkt_size;
u8 pkt_offset;
u8 txdw0;
@@ -211,6 +346,22 @@
__le16 txdw7;
};
+struct rtl8723bu_tx_desc {
+ __le16 pkt_size;
+ u8 pkt_offset;
+ u8 txdw0;
+ __le32 txdw1;
+ __le32 txdw2;
+ __le32 txdw3;
+ __le32 txdw4;
+ __le32 txdw5;
+ __le32 txdw6;
+ __le16 csum;
+ __le16 txdw7;
+ __le32 txdw8;
+ __le32 txdw9;
+};
+
/* CCK Rates, TxHT = 0 */
#define DESC_RATE_1M 0x00
#define DESC_RATE_2M 0x01
@@ -256,15 +407,25 @@
#define TXDESC_OWN BIT(31)
#else
#define TXDESC_BROADMULTICAST BIT(0)
+#define TXDESC_HTC BIT(1)
#define TXDESC_LAST_SEGMENT BIT(2)
#define TXDESC_FIRST_SEGMENT BIT(3)
+#define TXDESC_LINIP BIT(4)
+#define TXDESC_NO_ACM BIT(5)
+#define TXDESC_GF BIT(6)
#define TXDESC_OWN BIT(7)
#endif
/* Word 1 */
+/*
+ * Bits 0-7 differ dependent on chip generation. For 8723au bits 5/6 are
+ * aggregation enable and break respectively. For 8723bu, bits 0-7 are macid.
+ */
#define TXDESC_PKT_OFFSET_SZ 0
-#define TXDESC_AGG_ENABLE BIT(5)
-#define TXDESC_BK BIT(6)
+#define TXDESC_AGG_ENABLE_8723A BIT(5)
+#define TXDESC_AGG_BREAK_8723A BIT(6)
+#define TXDESC_MACID_SHIFT_8723B 0
+#define TXDESC_MACID_MASK_8723B 0x00f0
#define TXDESC_QUEUE_SHIFT 8
#define TXDESC_QUEUE_MASK 0x1f00
#define TXDESC_QUEUE_BK 0x2
@@ -276,6 +437,9 @@
#define TXDESC_QUEUE_MGNT 0x12
#define TXDESC_QUEUE_CMD 0x13
#define TXDESC_QUEUE_MAX (TXDESC_QUEUE_CMD + 1)
+#define TXDESC_RDG_NAV_EXT_8723B BIT(13)
+#define TXDESC_LSIG_TXOP_ENABLE_8723B BIT(14)
+#define TXDESC_PIFS_8723B BIT(15)
#define DESC_RATE_ID_SHIFT 16
#define DESC_RATE_ID_MASK 0xf
@@ -287,41 +451,72 @@
#define TXDESC_HWPC BIT(31)
/* Word 2 */
-#define TXDESC_ACK_REPORT BIT(19)
+#define TXDESC_PAID_SHIFT_8723B 0
+#define TXDESC_PAID_MASK_8723B 0x1ff
+#define TXDESC_CCA_RTS_SHIFT_8723B 10
+#define TXDESC_CCA_RTS_MASK_8723B 0xc00
+#define TXDESC_AGG_ENABLE_8723B BIT(12)
+#define TXDESC_RDG_ENABLE_8723B BIT(13)
+#define TXDESC_AGG_BREAK_8723B BIT(16)
+#define TXDESC_MORE_FRAG_8723B BIT(17)
+#define TXDESC_RAW_8723B BIT(18)
+#define TXDESC_ACK_REPORT_8723A BIT(19)
+#define TXDESC_SPE_RPT_8723B BIT(19)
#define TXDESC_AMPDU_DENSITY_SHIFT 20
+#define TXDESC_BT_INT_8723B BIT(23)
+#define TXDESC_GID_8723B BIT(24)
/* Word 3 */
-#define TXDESC_SEQ_SHIFT 16
-#define TXDESC_SEQ_MASK 0x0fff0000
+#define TXDESC_USE_DRIVER_RATE_8723B BIT(8)
+#define TXDESC_CTS_SELF_ENABLE_8723B BIT(11)
+#define TXDESC_RTS_CTS_ENABLE_8723B BIT(12)
+#define TXDESC_HW_RTS_ENABLE_8723B BIT(13)
+#define TXDESC_SEQ_SHIFT_8723A 16
+#define TXDESC_SEQ_MASK_8723A 0x0fff0000
/* Word 4 */
-#define TXDESC_QOS BIT(6)
-#define TXDESC_HW_SEQ_ENABLE BIT(7)
-#define TXDESC_USE_DRIVER_RATE BIT(8)
+#define TXDESC_RTS_RATE_SHIFT_8723A 0
+#define TXDESC_RTS_RATE_MASK_8723A 0x3f
+#define TXDESC_QOS_8723A BIT(6)
+#define TXDESC_HW_SEQ_ENABLE_8723A BIT(7)
+#define TXDESC_USE_DRIVER_RATE_8723A BIT(8)
#define TXDESC_DISABLE_DATA_FB BIT(10)
-#define TXDESC_CTS_SELF_ENABLE BIT(11)
-#define TXDESC_RTS_CTS_ENABLE BIT(12)
-#define TXDESC_HW_RTS_ENABLE BIT(13)
+#define TXDESC_CTS_SELF_ENABLE_8723A BIT(11)
+#define TXDESC_RTS_CTS_ENABLE_8723A BIT(12)
+#define TXDESC_HW_RTS_ENABLE_8723A BIT(13)
#define TXDESC_PRIME_CH_OFF_LOWER BIT(20)
#define TXDESC_PRIME_CH_OFF_UPPER BIT(21)
-#define TXDESC_SHORT_PREAMBLE BIT(24)
+#define TXDESC_SHORT_PREAMBLE_8723A BIT(24)
#define TXDESC_DATA_BW BIT(25)
#define TXDESC_RTS_DATA_BW BIT(27)
#define TXDESC_RTS_PRIME_CH_OFF_LOWER BIT(28)
#define TXDESC_RTS_PRIME_CH_OFF_UPPER BIT(29)
+#define TXDESC_DATA_RATE_FB_SHIFT_8723B 8
+#define TXDESC_DATA_RATE_FB_MASK_8723B 0x00001f00
+#define TXDESC_RETRY_LIMIT_ENABLE_8723B BIT(17)
+#define TXDESC_RETRY_LIMIT_SHIFT_8723B 18
+#define TXDESC_RETRY_LIMIT_MASK_8723B 0x00fc0000
+#define TXDESC_RTS_RATE_SHIFT_8723B 24
+#define TXDESC_RTS_RATE_MASK_8723B 0x3f000000
/* Word 5 */
-#define TXDESC_RTS_RATE_SHIFT 0
-#define TXDESC_RTS_RATE_MASK 0x3f
+#define TXDESC_SHORT_PREAMBLE_8723B BIT(4)
#define TXDESC_SHORT_GI BIT(6)
#define TXDESC_CCX_TAG BIT(7)
-#define TXDESC_RETRY_LIMIT_ENABLE BIT(17)
-#define TXDESC_RETRY_LIMIT_SHIFT 18
-#define TXDESC_RETRY_LIMIT_MASK 0x00fc0000
+#define TXDESC_RETRY_LIMIT_ENABLE_8723A BIT(17)
+#define TXDESC_RETRY_LIMIT_SHIFT_8723A 18
+#define TXDESC_RETRY_LIMIT_MASK_8723A 0x00fc0000
/* Word 6 */
#define TXDESC_MAX_AGG_SHIFT 11
+/* Word 8 */
+#define TXDESC_HW_SEQ_ENABLE_8723B BIT(15)
+
+/* Word 9 */
+#define TXDESC_SEQ_SHIFT_8723B 12
+#define TXDESC_SEQ_MASK_8723B 0x00fff000
+
struct phy_rx_agc_info {
#ifdef __LITTLE_ENDIAN
u8 gain:7, trsw:1;
@@ -500,6 +695,125 @@
u8 customer_id;
};
+struct rtl8723bu_pwr_idx {
+#ifdef __LITTLE_ENDIAN
+ int ht20:4;
+ int ht40:4;
+ int ofdm:4;
+ int cck:4;
+#else
+ int cck:4;
+ int ofdm:4;
+ int ht40:4;
+ int ht20:4;
+#endif
+} __attribute__((packed));
+
+struct rtl8723bu_efuse_tx_power {
+ u8 cck_base[6];
+ u8 ht40_base[5];
+ struct rtl8723au_idx ht20_ofdm_1s_diff;
+ struct rtl8723bu_pwr_idx pwr_diff[3];
+ u8 dummy5g[24]; /* max channel group (14) + power diff offset (10) */
+};
+
+struct rtl8723bu_efuse {
+ __le16 rtl_id;
+ u8 res0[0x0e];
+ struct rtl8723bu_efuse_tx_power tx_power_index_A; /* 0x10 */
+ struct rtl8723bu_efuse_tx_power tx_power_index_B; /* 0x3a */
+ struct rtl8723bu_efuse_tx_power tx_power_index_C; /* 0x64 */
+ struct rtl8723bu_efuse_tx_power tx_power_index_D; /* 0x8e */
+ u8 channel_plan; /* 0xb8 */
+ u8 xtal_k;
+ u8 thermal_meter;
+ u8 iqk_lck;
+ u8 pa_type; /* 0xbc */
+ u8 lna_type_2g; /* 0xbd */
+ u8 res2[3];
+ u8 rf_board_option;
+ u8 rf_feature_option;
+ u8 rf_bt_setting;
+ u8 eeprom_version;
+ u8 eeprom_customer_id;
+ u8 res3[2];
+ u8 tx_pwr_calibrate_rate;
+ u8 rf_antenna_option; /* 0xc9 */
+ u8 rfe_option;
+ u8 res4[9];
+ u8 usb_optional_function;
+ u8 res5[0x1e];
+ u8 res6[2];
+ u8 serial[0x0b]; /* 0xf5 */
+ u8 vid; /* 0x100 */
+ u8 res7;
+ u8 pid;
+ u8 res8[4];
+ u8 mac_addr[ETH_ALEN]; /* 0x107 */
+ u8 res9[2];
+ u8 vendor_name[0x07];
+ u8 res10[2];
+ u8 device_name[0x14];
+ u8 res11[0xcf];
+ u8 package_type; /* 0x1fb */
+ u8 res12[0x4];
+};
+
+struct rtl8192eu_efuse_tx_power {
+ u8 cck_base[6];
+ u8 ht40_base[5];
+ struct rtl8723au_idx ht20_ofdm_1s_diff;
+ struct rtl8723au_idx ht40_ht20_2s_diff;
+ struct rtl8723au_idx ofdm_cck_2s_diff; /* not used */
+ struct rtl8723au_idx ht40_ht20_3s_diff;
+ struct rtl8723au_idx ofdm_cck_3s_diff; /* not used */
+ struct rtl8723au_idx ht40_ht20_4s_diff;
+ struct rtl8723au_idx ofdm_cck_4s_diff; /* not used */
+};
+
+struct rtl8192eu_efuse {
+ __le16 rtl_id;
+ u8 res0[0x0e];
+ struct rtl8192eu_efuse_tx_power tx_power_index_A; /* 0x10 */
+ struct rtl8192eu_efuse_tx_power tx_power_index_B; /* 0x22 */
+ struct rtl8192eu_efuse_tx_power tx_power_index_C; /* 0x34 */
+ struct rtl8192eu_efuse_tx_power tx_power_index_D; /* 0x46 */
+ u8 res1[0x60];
+ u8 channel_plan; /* 0xb8 */
+ u8 xtal_k;
+ u8 thermal_meter;
+ u8 iqk_lck;
+ u8 pa_type; /* 0xbc */
+ u8 lna_type_2g; /* 0xbd */
+ u8 res2[1];
+ u8 lna_type_5g; /* 0xbf */
+ u8 res13[1];
+ u8 rf_board_option;
+ u8 rf_feature_option;
+ u8 rf_bt_setting;
+ u8 eeprom_version;
+ u8 eeprom_customer_id;
+ u8 res3[3];
+ u8 rf_antenna_option; /* 0xc9 */
+ u8 res4[6];
+ u8 vid; /* 0xd0 */
+ u8 res5[1];
+ u8 pid; /* 0xd2 */
+ u8 res6[1];
+ u8 usb_optional_function;
+ u8 res7[2];
+ u8 mac_addr[ETH_ALEN]; /* 0xd7 */
+ u8 res8[2];
+ u8 vendor_name[7];
+ u8 res9[2];
+ u8 device_name[0x0b]; /* 0xe8 */
+ u8 res10[2];
+ u8 serial[0x0b]; /* 0xf5 */
+ u8 res11[0x30];
+ u8 unknown[0x0d]; /* 0x130 */
+ u8 res12[0xc3];
+};
+
struct rtl8xxxu_reg8val {
u16 reg;
u8 val;
@@ -531,27 +845,110 @@
#define H2C_MAX_MBOX 4
#define H2C_EXT BIT(7)
-#define H2C_SET_POWER_MODE 1
-#define H2C_JOIN_BSS_REPORT 2
#define H2C_JOIN_BSS_DISCONNECT 0
#define H2C_JOIN_BSS_CONNECT 1
-#define H2C_SET_RSSI 5
-#define H2C_SET_RATE_MASK (6 | H2C_EXT)
+
+/*
+ * H2C (firmware) commands differ between the older generation chips
+ * 8188[cr]u, 819[12]cu, and 8723au, and the more recent chips 8723bu,
+ * 8192[de]u, 8192eu, and 8812.
+ */
+enum h2c_cmd_8723a {
+ H2C_SET_POWER_MODE = 1,
+ H2C_JOIN_BSS_REPORT = 2,
+ H2C_SET_RSSI = 5,
+ H2C_SET_RATE_MASK = (6 | H2C_EXT),
+};
+
+enum h2c_cmd_8723b {
+ /*
+ * Common Class: 000
+ */
+ H2C_8723B_RSVD_PAGE = 0x00,
+ H2C_8723B_MEDIA_STATUS_RPT = 0x01,
+ H2C_8723B_SCAN_ENABLE = 0x02,
+ H2C_8723B_KEEP_ALIVE = 0x03,
+ H2C_8723B_DISCON_DECISION = 0x04,
+ H2C_8723B_PSD_OFFLOAD = 0x05,
+ H2C_8723B_AP_OFFLOAD = 0x08,
+ H2C_8723B_BCN_RSVDPAGE = 0x09,
+ H2C_8723B_PROBERSP_RSVDPAGE = 0x0A,
+ H2C_8723B_FCS_RSVDPAGE = 0x10,
+ H2C_8723B_FCS_INFO = 0x11,
+ H2C_8723B_AP_WOW_GPIO_CTRL = 0x13,
+
+ /*
+ * PoweSave Class: 001
+ */
+ H2C_8723B_SET_PWR_MODE = 0x20,
+ H2C_8723B_PS_TUNING_PARA = 0x21,
+ H2C_8723B_PS_TUNING_PARA2 = 0x22,
+ H2C_8723B_P2P_LPS_PARAM = 0x23,
+ H2C_8723B_P2P_PS_OFFLOAD = 0x24,
+ H2C_8723B_PS_SCAN_ENABLE = 0x25,
+ H2C_8723B_SAP_PS_ = 0x26,
+ H2C_8723B_INACTIVE_PS_ = 0x27,
+ H2C_8723B_FWLPS_IN_IPS_ = 0x28,
+
+ /*
+ * Dynamic Mechanism Class: 010
+ */
+ H2C_8723B_MACID_CFG_RAID = 0x40,
+ H2C_8723B_TXBF = 0x41,
+ H2C_8723B_RSSI_SETTING = 0x42,
+ H2C_8723B_AP_REQ_TXRPT = 0x43,
+ H2C_8723B_INIT_RATE_COLLECT = 0x44,
+
+ /*
+ * BT Class: 011
+ */
+ H2C_8723B_B_TYPE_TDMA = 0x60,
+ H2C_8723B_BT_INFO = 0x61,
+ H2C_8723B_FORCE_BT_TXPWR = 0x62,
+ H2C_8723B_BT_IGNORE_WLANACT = 0x63,
+ H2C_8723B_DAC_SWING_VALUE = 0x64,
+ H2C_8723B_ANT_SEL_RSV = 0x65,
+ H2C_8723B_WL_OPMODE = 0x66,
+ H2C_8723B_BT_MP_OPER = 0x67,
+ H2C_8723B_BT_CONTROL = 0x68,
+ H2C_8723B_BT_WIFI_CTRL = 0x69,
+ H2C_8723B_BT_FW_PATCH = 0x6a,
+ H2C_8723B_BT_WLAN_CALIBRATION = 0x6d,
+ H2C_8723B_BT_GRANT = 0x6e,
+
+ /*
+ * WOWLAN Class: 100
+ */
+ H2C_8723B_WOWLAN = 0x80,
+ H2C_8723B_REMOTE_WAKE_CTRL = 0x81,
+ H2C_8723B_AOAC_GLOBAL_INFO = 0x82,
+ H2C_8723B_AOAC_RSVD_PAGE = 0x83,
+ H2C_8723B_AOAC_RSVD_PAGE2 = 0x84,
+ H2C_8723B_D0_SCAN_OFFLOAD_CTRL = 0x85,
+ H2C_8723B_D0_SCAN_OFFLOAD_INFO = 0x86,
+ H2C_8723B_CHNL_SWITCH_OFFLOAD = 0x87,
+
+ H2C_8723B_RESET_TSF = 0xC0,
+};
+
struct h2c_cmd {
union {
struct {
u8 cmd;
- u8 data[5];
+ u8 data[7];
} __packed cmd;
struct {
__le32 data;
__le16 ext;
} __packed raw;
struct {
+ __le32 data;
+ __le32 ext;
+ } __packed raw_wide;
+ struct {
u8 cmd;
u8 data;
- u8 pad[4];
} __packed joinbss;
struct {
u8 cmd;
@@ -559,6 +956,182 @@
u8 arg;
__le16 mask_lo;
} __packed ramask;
+ struct {
+ u8 cmd;
+ u8 parm;
+ u8 macid;
+ u8 macid_end;
+ } __packed media_status_rpt;
+ struct {
+ u8 cmd;
+ u8 macid;
+ /*
+ * [0:4] - RAID
+ * [7] - SGI
+ */
+ u8 data1;
+ /*
+ * [0:1] - Bandwidth
+ * [3] - No Update
+ * [4:5] - VHT enable
+ * [6] - DISPT
+ * [7] - DISRA
+ */
+ u8 data2;
+ u8 ramask0;
+ u8 ramask1;
+ u8 ramask2;
+ u8 ramask3;
+ } __packed b_macid_cfg;
+ struct {
+ u8 cmd;
+ u8 data1;
+ u8 data2;
+ u8 data3;
+ u8 data4;
+ u8 data5;
+ } __packed b_type_dma;
+ struct {
+ u8 cmd;
+ u8 data;
+ } __packed bt_info;
+ struct {
+ u8 cmd;
+ u8 operreq;
+ u8 opcode;
+ u8 data;
+ u8 addr;
+ } __packed bt_mp_oper;
+ struct {
+ u8 cmd;
+ u8 data;
+ } __packed bt_wlan_calibration;
+ struct {
+ u8 cmd;
+ u8 data;
+ } __packed ignore_wlan;
+ struct {
+ u8 cmd;
+ u8 ant_inverse;
+ u8 int_switch_type;
+ } __packed ant_sel_rsv;
+ struct {
+ u8 cmd;
+ u8 data;
+ } __packed bt_grant;
+ };
+};
+
+enum c2h_evt_8723b {
+ C2H_8723B_DEBUG = 0,
+ C2H_8723B_TSF = 1,
+ C2H_8723B_AP_RPT_RSP = 2,
+ C2H_8723B_CCX_TX_RPT = 3,
+ C2H_8723B_BT_RSSI = 4,
+ C2H_8723B_BT_OP_MODE = 5,
+ C2H_8723B_EXT_RA_RPT = 6,
+ C2H_8723B_BT_INFO = 9,
+ C2H_8723B_HW_INFO_EXCH = 0x0a,
+ C2H_8723B_BT_MP_INFO = 0x0b,
+ C2H_8723B_RA_REPORT = 0x0c,
+ C2H_8723B_FW_DEBUG = 0xff,
+};
+
+enum bt_info_src_8723b {
+ BT_INFO_SRC_8723B_WIFI_FW = 0x0,
+ BT_INFO_SRC_8723B_BT_RSP = 0x1,
+ BT_INFO_SRC_8723B_BT_ACTIVE_SEND = 0x2,
+};
+
+enum bt_mp_oper_opcode_8723b {
+ BT_MP_OP_GET_BT_VERSION = 0x00,
+ BT_MP_OP_RESET = 0x01,
+ BT_MP_OP_TEST_CTRL = 0x02,
+ BT_MP_OP_SET_BT_MODE = 0x03,
+ BT_MP_OP_SET_CHNL_TX_GAIN = 0x04,
+ BT_MP_OP_SET_PKT_TYPE_LEN = 0x05,
+ BT_MP_OP_SET_PKT_CNT_L_PL_TYPE = 0x06,
+ BT_MP_OP_SET_PKT_CNT_H_PKT_INTV = 0x07,
+ BT_MP_OP_SET_PKT_HEADER = 0x08,
+ BT_MP_OP_SET_WHITENCOEFF = 0x09,
+ BT_MP_OP_SET_BD_ADDR_L = 0x0a,
+ BT_MP_OP_SET_BD_ADDR_H = 0x0b,
+ BT_MP_OP_WRITE_REG_ADDR = 0x0c,
+ BT_MP_OP_WRITE_REG_VALUE = 0x0d,
+ BT_MP_OP_GET_BT_STATUS = 0x0e,
+ BT_MP_OP_GET_BD_ADDR_L = 0x0f,
+ BT_MP_OP_GET_BD_ADDR_H = 0x10,
+ BT_MP_OP_READ_REG = 0x11,
+ BT_MP_OP_SET_TARGET_BD_ADDR_L = 0x12,
+ BT_MP_OP_SET_TARGET_BD_ADDR_H = 0x13,
+ BT_MP_OP_SET_TX_POWER_CALIBRATION = 0x14,
+ BT_MP_OP_GET_RX_PKT_CNT_L = 0x15,
+ BT_MP_OP_GET_RX_PKT_CNT_H = 0x16,
+ BT_MP_OP_GET_RX_ERROR_BITS_L = 0x17,
+ BT_MP_OP_GET_RX_ERROR_BITS_H = 0x18,
+ BT_MP_OP_GET_RSSI = 0x19,
+ BT_MP_OP_GET_CFO_HDR_QUALITY_L = 0x1a,
+ BT_MP_OP_GET_CFO_HDR_QUALITY_H = 0x1b,
+ BT_MP_OP_GET_TARGET_BD_ADDR_L = 0x1c,
+ BT_MP_OP_GET_TARGET_BD_ADDR_H = 0x1d,
+ BT_MP_OP_GET_AFH_MAP_L = 0x1e,
+ BT_MP_OP_GET_AFH_MAP_M = 0x1f,
+ BT_MP_OP_GET_AFH_MAP_H = 0x20,
+ BT_MP_OP_GET_AFH_STATUS = 0x21,
+ BT_MP_OP_SET_TRACKING_INTERVAL = 0x22,
+ BT_MP_OP_SET_THERMAL_METER = 0x23,
+ BT_MP_OP_ENABLE_CFO_TRACKING = 0x24,
+};
+
+struct rtl8723bu_c2h {
+ u8 id;
+ u8 seq;
+ union {
+ struct {
+ u8 payload[0];
+ } __packed raw;
+ struct {
+ u8 ext_id;
+ u8 status:4;
+ u8 retlen:4;
+ u8 opcode_ver:4;
+ u8 req_num:4;
+ u8 payload[2];
+ } __packed bt_mp_info;
+ struct {
+ u8 response_source:4;
+ u8 dummy0_0:4;
+
+ u8 bt_info;
+
+ u8 retry_count:4;
+ u8 dummy2_0:1;
+ u8 bt_page:1;
+ u8 tx_rx_mask:1;
+ u8 dummy2_2:1;
+
+ u8 rssi;
+
+ u8 basic_rate:1;
+ u8 bt_has_reset:1;
+ u8 dummy4_1:1;;
+ u8 ignore_wlan:1;
+ u8 auto_report:1;
+ u8 dummy4_2:3;
+
+ u8 a4;
+ u8 a5;
+ } __packed bt_info;
+ struct {
+ u8 rate:7;
+ u8 dummy0_0:1;
+ u8 macid;
+ u8 ldpc:1;
+ u8 txbf:1;
+ u8 noisy_state:1;
+ u8 dummy2_0:5;
+ u8 dummy3_0;
+ } __packed ra_report;
};
};
@@ -582,40 +1155,51 @@
u8 mac_addr[ETH_ALEN];
char chip_name[8];
- u8 cck_tx_power_index_A[3]; /* 0x10 */
- u8 cck_tx_power_index_B[3];
- u8 ht40_1s_tx_power_index_A[3]; /* 0x16 */
- u8 ht40_1s_tx_power_index_B[3];
+ char chip_vendor[8];
+ u8 cck_tx_power_index_A[RTL8XXXU_MAX_CHANNEL_GROUPS];
+ u8 cck_tx_power_index_B[RTL8XXXU_MAX_CHANNEL_GROUPS];
+ u8 ht40_1s_tx_power_index_A[RTL8XXXU_MAX_CHANNEL_GROUPS];
+ u8 ht40_1s_tx_power_index_B[RTL8XXXU_MAX_CHANNEL_GROUPS];
/*
* The following entries are half-bytes split as:
* bits 0-3: path A, bits 4-7: path B, all values 4 bits signed
*/
- struct rtl8723au_idx ht40_2s_tx_power_index_diff[3];
- struct rtl8723au_idx ht20_tx_power_index_diff[3];
- struct rtl8723au_idx ofdm_tx_power_index_diff[3];
- struct rtl8723au_idx ht40_max_power_offset[3];
- struct rtl8723au_idx ht20_max_power_offset[3];
+ struct rtl8723au_idx ht40_2s_tx_power_index_diff[
+ RTL8723A_CHANNEL_GROUPS];
+ struct rtl8723au_idx ht20_tx_power_index_diff[RTL8723A_CHANNEL_GROUPS];
+ struct rtl8723au_idx ofdm_tx_power_index_diff[RTL8723A_CHANNEL_GROUPS];
+ struct rtl8723au_idx ht40_max_power_offset[RTL8723A_CHANNEL_GROUPS];
+ struct rtl8723au_idx ht20_max_power_offset[RTL8723A_CHANNEL_GROUPS];
+ /*
+ * Newer generation chips only keep power diffs per TX count,
+ * not per channel group.
+ */
+ struct rtl8723au_idx ofdm_tx_power_diff[RTL8723B_TX_COUNT];
+ struct rtl8723au_idx ht20_tx_power_diff[RTL8723B_TX_COUNT];
+ struct rtl8723au_idx ht40_tx_power_diff[RTL8723B_TX_COUNT];
u32 chip_cut:4;
u32 rom_rev:4;
+ u32 is_multi_func:1;
u32 has_wifi:1;
u32 has_bluetooth:1;
u32 enable_bluetooth:1;
u32 has_gps:1;
u32 hi_pa:1;
u32 vendor_umc:1;
+ u32 vendor_smic:1;
u32 has_polarity_ctrl:1;
u32 has_eeprom:1;
u32 boot_eeprom:1;
+ u32 usb_interrupts:1;
u32 ep_tx_high_queue:1;
u32 ep_tx_normal_queue:1;
u32 ep_tx_low_queue:1;
- u32 path_a_hi_power:1;
- u32 path_a_rf_paths:4;
+ u32 has_xtalk:1;
+ u8 xtalk;
unsigned int pipe_interrupt;
unsigned int pipe_in;
unsigned int pipe_out[TXDESC_QUEUE_MAX];
u8 out_ep[RTL8XXXU_OUT_ENDPOINTS];
- u8 path_a_ig_value;
u8 ep_tx_count;
u8 rf_paths;
u8 rx_paths;
@@ -642,9 +1226,11 @@
u8 val8;
} usb_buf;
union {
- u8 raw[EFUSE_MAP_LEN_8723A];
+ u8 raw[EFUSE_MAP_LEN];
struct rtl8723au_efuse efuse8723;
+ struct rtl8723bu_efuse efuse8723bu;
struct rtl8192cu_efuse efuse8192;
+ struct rtl8192eu_efuse efuse8192eu;
} efuse_wifi;
u32 adda_backup[RTL8XXXU_ADDA_REGS];
u32 mac_backup[RTL8XXXU_MAC_REGS];
@@ -652,7 +1238,6 @@
u32 bb_recovery_backup[RTL8XXXU_BB_REGS];
u32 rtlchip;
u8 pi_enabled:1;
- u8 iqk_initialized:1;
u8 int_buf[USB_INTR_CONTENT_LENGTH];
};
@@ -672,5 +1257,31 @@
int (*parse_efuse) (struct rtl8xxxu_priv *priv);
int (*load_firmware) (struct rtl8xxxu_priv *priv);
int (*power_on) (struct rtl8xxxu_priv *priv);
+ void (*power_off) (struct rtl8xxxu_priv *priv);
+ void (*reset_8051) (struct rtl8xxxu_priv *priv);
+ int (*llt_init) (struct rtl8xxxu_priv *priv, u8 last_tx_page);
+ void (*phy_init_antenna_selection) (struct rtl8xxxu_priv *priv);
+ void (*phy_iq_calibrate) (struct rtl8xxxu_priv *priv);
+ void (*config_channel) (struct ieee80211_hw *hw);
+ int (*parse_rx_desc) (struct rtl8xxxu_priv *priv, struct sk_buff *skb,
+ struct ieee80211_rx_status *rx_status);
+ void (*init_aggregation) (struct rtl8xxxu_priv *priv);
+ void (*init_statistics) (struct rtl8xxxu_priv *priv);
+ void (*enable_rf) (struct rtl8xxxu_priv *priv);
+ void (*disable_rf) (struct rtl8xxxu_priv *priv);
+ void (*set_tx_power) (struct rtl8xxxu_priv *priv, int channel,
+ bool ht40);
+ void (*update_rate_mask) (struct rtl8xxxu_priv *priv,
+ u32 ramask, int sgi);
+ void (*report_connect) (struct rtl8xxxu_priv *priv,
+ u8 macid, bool connect);
int writeN_block_size;
+ u16 mbox_ext_reg;
+ char mbox_ext_width;
+ char tx_desc_size;
+ char has_s0s1;
+ u32 adda_1t_init;
+ u32 adda_1t_path_on;
+ u32 adda_2t_path_on_a;
+ u32 adda_2t_path_on_b;
};
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h
index 23208f7..e545e84 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h
+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h
@@ -45,6 +45,7 @@
#define APS_FSMCO_ENABLE_POWERDOWN BIT(4)
#define APS_FSMCO_MAC_ENABLE BIT(8)
#define APS_FSMCO_MAC_OFF BIT(9)
+#define APS_FSMCO_SW_LPS BIT(10)
#define APS_FSMCO_HW_SUSPEND BIT(11)
#define APS_FSMCO_PCIE BIT(12)
#define APS_FSMCO_HW_POWERDOWN BIT(15)
@@ -69,8 +70,11 @@
#define REG_EE_VPD 0x000c
#define REG_AFE_MISC 0x0010
+#define AFE_MISC_WL_XTAL_CTRL BIT(6)
+
#define REG_SPS0_CTRL 0x0011
#define REG_SPS_OCP_CFG 0x0018
+#define REG_8192E_LDOV12_CTRL 0x0014
#define REG_RSV_CTRL 0x001c
#define REG_RF_CTRL 0x001f
@@ -131,6 +135,8 @@
#define EFUSE_ACCESS_DISABLE 0x00 /* RTL8723 only */
#define REG_PWR_DATA 0x0038
+#define PWR_DATA_EEPRPAD_RFE_CTRL_EN BIT(11)
+
#define REG_CAL_TIMER 0x003c
#define REG_ACLK_MON 0x003e
#define REG_GPIO_MUXCFG 0x0040
@@ -138,7 +144,10 @@
#define REG_MAC_PINMUX_CFG 0x0043
#define REG_GPIO_PIN_CTRL 0x0044
#define REG_GPIO_INTM 0x0048
+#define GPIO_INTM_EDGE_TRIG_IRQ BIT(9)
+
#define REG_LEDCFG0 0x004c
+#define LEDCFG0_DPDT_SELECT BIT(23)
#define REG_LEDCFG1 0x004d
#define REG_LEDCFG2 0x004e
#define LEDCFG2_DPDT_SELECT BIT(7)
@@ -152,6 +161,12 @@
#define REG_GPIO_PIN_CTRL_2 0x0060
/* RTL8723 WIFI/BT/GPS Multi-Function GPIO Select. */
#define REG_GPIO_IO_SEL_2 0x0062
+#define GPIO_IO_SEL_2_GPIO09_INPUT BIT(1)
+#define GPIO_IO_SEL_2_GPIO09_IRQ BIT(9)
+
+/* RTL8723B */
+#define REG_PAD_CTRL1 0x0064
+#define PAD_CTRL1_SW_DPDT_SEL_DATA BIT(0)
/* RTL8723 only WIFI/BT/GPS Multi-Function control source. */
#define REG_MULTI_FUNC_CTRL 0x0068
@@ -177,6 +192,8 @@
control */
#define MULTI_GPS_FUNC_EN BIT(22) /* GPS function enable */
+#define REG_LDO_SW_CTRL 0x007c /* 8192eu */
+
#define REG_MCU_FW_DL 0x0080
#define MCU_FW_DL_ENABLE BIT(0)
#define MCU_FW_DL_READY BIT(1)
@@ -192,6 +209,12 @@
#define REG_HMBOX_EXT_1 0x008a
#define REG_HMBOX_EXT_2 0x008c
#define REG_HMBOX_EXT_3 0x008e
+/* Interrupt registers for 8192e/8723bu/8812 */
+#define REG_HIMR0 0x00b0
+#define REG_HISR0 0x00b4
+#define REG_HIMR1 0x00b8
+#define REG_HISR1 0x00bc
+
/* Host suspend counter on FPGA platform */
#define REG_HOST_SUSP_CNT 0x00bc
/* Efuse access protection for RTL8723 */
@@ -213,6 +236,7 @@
#define SYS_CFG_PCIRSTB BIT(4)
#define SYS_CFG_V15_VLD BIT(5)
#define SYS_CFG_TRP_B15V_EN BIT(7)
+#define SYS_CFG_SW_OFFLOAD_EN BIT(7) /* For chips with IOL support */
#define SYS_CFG_SIC_IDLE BIT(8)
#define SYS_CFG_BD_MAC2 BIT(9)
#define SYS_CFG_BD_MAC1 BIT(10)
@@ -220,9 +244,14 @@
#define SYS_CFG_CHIP_VER (BIT(12) | BIT(13) | BIT(14) | BIT(15))
#define SYS_CFG_BT_FUNC BIT(16)
#define SYS_CFG_VENDOR_ID BIT(19)
+#define SYS_CFG_VENDOR_EXT_MASK (BIT(18) | BIT(19))
+#define SYS_CFG_VENDOR_ID_TSMC 0
+#define SYS_CFG_VENDOR_ID_SMIC BIT(18)
+#define SYS_CFG_VENDOR_ID_UMC BIT(19)
#define SYS_CFG_PAD_HWPD_IDN BIT(22)
#define SYS_CFG_TRP_VAUX_EN BIT(23)
#define SYS_CFG_TRP_BT_EN BIT(24)
+#define SYS_CFG_SPS_LDO_SEL BIT(24) /* 8192eu */
#define SYS_CFG_BD_PKG_SEL BIT(25)
#define SYS_CFG_BD_HCI_SEL BIT(26)
#define SYS_CFG_TYPE_ID BIT(27)
@@ -255,6 +284,8 @@
#define GPIO_USB_SUSEN BIT(23)
#define GPIO_RF_RL_ID (BIT(31) | BIT(30) | BIT(29) | BIT(28))
+#define REG_SYS_CFG2 0x00fc /* 8192eu */
+
/* 0x0100 ~ 0x01FF MACTOP General Configuration */
#define REG_CR 0x0100
#define CR_HCI_TXDMA_ENABLE BIT(0)
@@ -287,6 +318,7 @@
#define PBP_PAGE_SIZE_1024 0x4
#define REG_TRXDMA_CTRL 0x010c
+#define TRXDMA_CTRL_RXDMA_AGG_EN BIT(2)
#define TRXDMA_CTRL_VOQ_SHIFT 4
#define TRXDMA_CTRL_VIQ_SHIFT 6
#define TRXDMA_CTRL_BEQ_SHIFT 8
@@ -321,6 +353,8 @@
#define REG_MBIST_DONE 0x0178
#define REG_MBIST_FAIL 0x017c
#define REG_C2HEVT_MSG_NORMAL 0x01a0
+/* 8192EU/8723BU/8812 */
+#define REG_C2HEVT_CMD_ID_8723B 0x01ae
#define REG_C2HEVT_CLEAR 0x01af
#define REG_C2HEVT_MSG_TEST 0x01b8
#define REG_MCUTST_1 0x01c0
@@ -340,6 +374,11 @@
#define REG_BB_ACCEESS_CTRL 0x01e8
#define REG_BB_ACCESS_DATA 0x01ec
+#define REG_HMBOX_EXT0_8723B 0x01f0
+#define REG_HMBOX_EXT1_8723B 0x01f4
+#define REG_HMBOX_EXT2_8723B 0x01f8
+#define REG_HMBOX_EXT3_8723B 0x01fc
+
/* 0x0200 ~ 0x027F TXDMA Configuration */
#define REG_RQPN 0x0200
#define RQPN_HI_PQ_SHIFT 0
@@ -350,14 +389,29 @@
#define REG_FIFOPAGE 0x0204
#define REG_TDECTRL 0x0208
#define REG_TXDMA_OFFSET_CHK 0x020c
+#define TXDMA_OFFSET_DROP_DATA_EN BIT(9)
#define REG_TXDMA_STATUS 0x0210
#define REG_RQPN_NPQ 0x0214
+#define RQPN_NPQ_SHIFT 0
+#define RQPN_EPQ_SHIFT 16
+
+#define REG_AUTO_LLT 0x0224
+#define AUTO_LLT_INIT_LLT BIT(16)
+
+#define REG_DWBCN1_CTRL_8723B 0x0228
/* 0x0280 ~ 0x02FF RXDMA Configuration */
#define REG_RXDMA_AGG_PG_TH 0x0280
+#define RXDMA_USB_AGG_ENABLE BIT(31)
#define REG_RXPKT_NUM 0x0284
+#define RXPKT_NUM_RXDMA_IDLE BIT(17)
+#define RXPKT_NUM_RW_RELEASE_EN BIT(18)
#define REG_RXDMA_STATUS 0x0288
+/* Presumably only found on newer chips such as 8723bu */
+#define REG_RX_DMA_CTRL_8723B 0x0286
+#define REG_RXDMA_PRO_8723B 0x0290
+
#define REG_RF_BB_CMD_ADDR 0x02c0
#define REG_RF_BB_CMD_DATA 0x02c4
@@ -429,20 +483,26 @@
#define REG_ARFR1 0x0448
#define REG_ARFR2 0x044c
#define REG_ARFR3 0x0450
+#define REG_AMPDU_MAX_TIME_8723B 0x0456
#define REG_AGGLEN_LMT 0x0458
#define REG_AMPDU_MIN_SPACE 0x045c
#define REG_TXPKTBUF_WMAC_LBK_BF_HD 0x045d
#define REG_FAST_EDCA_CTRL 0x0460
#define REG_RD_RESP_PKT_TH 0x0463
#define REG_INIRTS_RATE_SEL 0x0480
+/* 8723bu */
+#define REG_DATA_SUBCHANNEL 0x0483
+/* 8723au */
#define REG_INIDATA_RATE_SEL 0x0484
#define REG_POWER_STATUS 0x04a4
#define REG_POWER_STAGE1 0x04b4
#define REG_POWER_STAGE2 0x04b8
+#define REG_AMPDU_BURST_MODE_8723B 0x04bc
#define REG_PKT_VO_VI_LIFE_TIME 0x04c0
#define REG_PKT_BE_BK_LIFE_TIME 0x04c2
#define REG_STBC_SETTING 0x04c4
+#define REG_HT_SINGLE_AMPDU_8723B 0x04c7
#define REG_PROT_MODE_CTRL 0x04c8
#define REG_MAX_AGGR_NUM 0x04ca
#define REG_RTS_MAX_AGGR_NUM 0x04cb
@@ -453,6 +513,10 @@
#define REG_NEED_CPU_HANDLE 0x04e0
#define REG_PKT_LOSE_RPT 0x04e1
#define REG_PTCL_ERR_STATUS 0x04e2
+#define REG_TX_REPORT_CTRL 0x04ec
+#define TX_REPORT_CTRL_TIMER_ENABLE BIT(1)
+
+#define REG_TX_REPORT_TIME 0x04f0
#define REG_DUMMY 0x04fc
/* 0x0500 ~ 0x05FF EDCA Configuration */
@@ -505,6 +569,7 @@
#define BEACON_DMA_ATIME_INT_TIME 2
#define REG_ATIMWND 0x055a
+#define REG_USTIME_TSF_8723B 0x055c
#define REG_BCN_MAX_ERR 0x055d
#define REG_RXTSF_OFFSET_CCK 0x055e
#define REG_RXTSF_OFFSET_OFDM 0x055f
@@ -559,13 +624,25 @@
(Rx beacon, probe rsp) */
#define RCR_ACCEPT_CRC32 BIT(8) /* Accept CRC32 error packet */
#define RCR_ACCEPT_ICV BIT(9) /* Accept ICV error packet */
-#define RCR_ACCEPT_DATA_FRAME BIT(11)
-#define RCR_ACCEPT_CTRL_FRAME BIT(12)
-#define RCR_ACCEPT_MGMT_FRAME BIT(13)
+#define RCR_ACCEPT_DATA_FRAME BIT(11) /* Accept all data pkt or use
+ REG_RXFLTMAP2 */
+#define RCR_ACCEPT_CTRL_FRAME BIT(12) /* Accept all control pkt or use
+ REG_RXFLTMAP1 */
+#define RCR_ACCEPT_MGMT_FRAME BIT(13) /* Accept all mgmt pkt or use
+ REG_RXFLTMAP0 */
#define RCR_HTC_LOC_CTRL BIT(14) /* MFC<--HTC=1 MFC-->HTC=0 */
+#define RCR_UC_DATA_PKT_INT_ENABLE BIT(16) /* Enable unicast data packet
+ interrupt */
+#define RCR_BM_DATA_PKT_INT_ENABLE BIT(17) /* Enable broadcast data packet
+ interrupt */
+#define RCR_TIM_PARSER_ENABLE BIT(18) /* Enable RX beacon TIM parser*/
#define RCR_MFBEN BIT(22)
-#define RCR_LSIGEN BIT(23)
+#define RCR_LSIG_ENABLE BIT(23) /* Enable LSIG TXOP Protection
+ function. Search KEYCAM for
+ each rx packet to check if
+ LSIGEN bit is set. */
#define RCR_MULTI_BSSID_ENABLE BIT(24) /* Enable Multiple BssId */
+#define RCR_FORCE_ACK BIT(26)
#define RCR_ACCEPT_BA_SSN BIT(27) /* Accept BA SSN */
#define RCR_APPEND_PHYSTAT BIT(28)
#define RCR_APPEND_ICV BIT(29)
@@ -605,6 +682,10 @@
#define REG_FWDLY 0x0661
#define REG_RXERR_RPT 0x0664
#define REG_WMAC_TRXPTCL_CTL 0x0668
+#define WMAC_TRXPTCL_CTL_BW_MASK (BIT(7) | BIT(8))
+#define WMAC_TRXPTCL_CTL_BW_20 0
+#define WMAC_TRXPTCL_CTL_BW_40 BIT(7)
+#define WMAC_TRXPTCL_CTL_BW_80 BIT(8)
/* Security */
#define REG_CAM_CMD 0x0670
@@ -632,18 +713,40 @@
#define REG_LPNAV_CTRL 0x0694
#define REG_WKFMCAM_CMD 0x0698
#define REG_WKFMCAM_RWD 0x069c
-#define REG_RXFLTMAP0 0x06a0
-#define REG_RXFLTMAP1 0x06a2
-#define REG_RXFLTMAP2 0x06a4
+
+/*
+ * RX Filters: each bit corresponds to the numerical value of the subtype.
+ * If it is set the subtype frame type is passed. The filter is only used when
+ * the RCR_ACCEPT_DATA_FRAME, RCR_ACCEPT_CTRL_FRAME, RCR_ACCEPT_MGMT_FRAME bit
+ * in the RCR are low.
+ *
+ * Example: Beacon subtype is binary 1000 which is decimal 8 so we have to set
+ * bit 8 (0x100) in REG_RXFLTMAP0 to enable reception.
+ */
+#define REG_RXFLTMAP0 0x06a0 /* Management frames */
+#define REG_RXFLTMAP1 0x06a2 /* Control frames */
+#define REG_RXFLTMAP2 0x06a4 /* Data frames */
+
#define REG_BCN_PSR_RPT 0x06a8
#define REG_CALB32K_CTRL 0x06ac
#define REG_PKT_MON_CTRL 0x06b4
-#define REG_BT_COEX_TABLE 0x06c0
+#define REG_BT_COEX_TABLE1 0x06c0
+#define REG_BT_COEX_TABLE2 0x06c4
+#define REG_BT_COEX_TABLE3 0x06c8
+#define REG_BT_COEX_TABLE4 0x06cc
#define REG_WMAC_RESP_TXINFO 0x06d8
#define REG_MACID1 0x0700
#define REG_BSSID1 0x0708
+/*
+ * This seems to be 8723bu specific
+ */
+#define REG_BT_CONTROL_8723BU 0x0764
+#define BT_CONTROL_BT_GRANT BIT(12)
+
+#define REG_WLAN_ACT_CONTROL_8723B 0x076e
+
#define REG_FPGA0_RF_MODE 0x0800
#define FPGA_RF_MODE BIT(0)
#define FPGA_RF_MODE_JAPAN BIT(1)
@@ -734,6 +837,11 @@
#define REG_FPGA0_ANALOG3 0x0888
#define REG_FPGA0_ANALOG4 0x088c
+#define REG_NHM_TH9_TH10_8723B 0x0890
+#define REG_NHM_TIMER_8723B 0x0894
+#define REG_NHM_TH3_TO_TH0_8723B 0x0898
+#define REG_NHM_TH7_TO_TH4_8723B 0x089c
+
#define REG_FPGA0_XA_LSSI_READBACK 0x08a0 /* Tranceiver LSSI Readback */
#define REG_FPGA0_XB_LSSI_READBACK 0x08a4
#define REG_HSPI_XA_READBACK 0x08b8 /* Transceiver A HSPI read */
@@ -742,6 +850,11 @@
#define REG_FPGA1_RF_MODE 0x0900
#define REG_FPGA1_TX_INFO 0x090c
+#define REG_DPDT_CTRL 0x092c /* 8723BU */
+#define REG_RFE_CTRL_ANTA_SRC 0x0930 /* 8723BU */
+#define REG_RFE_PATH_SELECT 0x0940 /* 8723BU */
+#define REG_RFE_BUFFER 0x0944 /* 8723BU */
+#define REG_S0S1_PATH_SWITCH 0x0948 /* 8723BU */
#define REG_CCK0_SYSTEM 0x0a00
#define CCK0_SIDEBAND BIT(4)
@@ -765,11 +878,16 @@
#define REG_OFDM0_TR_MUX_PAR 0x0c08
+#define REG_OFDM0_FA_RSTC 0x0c0c
+
#define REG_OFDM0_XA_RX_IQ_IMBALANCE 0x0c14
#define REG_OFDM0_XB_RX_IQ_IMBALANCE 0x0c1c
#define REG_OFDM0_ENERGY_CCA_THRES 0x0c4c
+#define REG_OFDM0_RX_D_SYNC_PATH 0x0c40
+#define OFDM0_SYNC_PATH_NOTCH_FILTER BIT(1)
+
#define REG_OFDM0_XA_AGC_CORE1 0x0c50
#define REG_OFDM0_XA_AGC_CORE2 0x0c54
#define REG_OFDM0_XB_AGC_CORE1 0x0c58
@@ -794,6 +912,9 @@
#define REG_OFDM0_RX_IQ_EXT_ANTA 0x0ca0
+/* 8723bu */
+#define REG_OFDM0_TX_PSDO_NOISE_WEIGHT 0x0ce4
+
#define REG_OFDM1_LSTF 0x0d00
#define OFDM_LSTF_PRIME_CH_LOW BIT(10)
#define OFDM_LSTF_PRIME_CH_HIGH BIT(11)
@@ -952,6 +1073,10 @@
#define RF6052_REG_MODE_AG 0x18 /* RF channel and BW switch */
#define MODE_AG_CHANNEL_MASK 0x3ff
#define MODE_AG_CHANNEL_20MHZ BIT(10)
+#define MODE_AG_BW_MASK (BIT(10) | BIT(11))
+#define MODE_AG_BW_20MHZ_8723B (BIT(10) | BIT(11))
+#define MODE_AG_BW_40MHZ_8723B BIT(10)
+#define MODE_AG_BW_80MHZ_8723B 0
#define RF6052_REG_TOP 0x19
#define RF6052_REG_RX_G1 0x1a
@@ -979,3 +1104,14 @@
#define RF6052_REG_TXPA_G1 0x31 /* RF TX PA control */
#define RF6052_REG_TXPA_G2 0x32 /* RF TX PA control */
#define RF6052_REG_TXPA_G3 0x33 /* RF TX PA control */
+
+/*
+ * NextGen regs: 8723BU
+ */
+#define RF6052_REG_T_METER_8723B 0x42
+#define RF6052_REG_UNKNOWN_43 0x43
+#define RF6052_REG_UNKNOWN_55 0x55
+#define RF6052_REG_S0S1 0xb0
+#define RF6052_REG_UNKNOWN_DF 0xdf
+#define RF6052_REG_UNKNOWN_ED 0xed
+#define RF6052_REG_WE_LUT 0xef
diff --git a/drivers/net/wireless/realtek/rtlwifi/core.c b/drivers/net/wireless/realtek/rtlwifi/core.c
index f250761..0f48048 100644
--- a/drivers/net/wireless/realtek/rtlwifi/core.c
+++ b/drivers/net/wireless/realtek/rtlwifi/core.c
@@ -35,62 +35,58 @@
#include <linux/export.h>
#include <net/cfg80211.h>
+u8 channel5g[CHANNEL_MAX_NUMBER_5G] = {
+ 36, 38, 40, 42, 44, 46, 48, /* Band 1 */
+ 52, 54, 56, 58, 60, 62, 64, /* Band 2 */
+ 100, 102, 104, 106, 108, 110, 112, /* Band 3 */
+ 116, 118, 120, 122, 124, 126, 128, /* Band 3 */
+ 132, 134, 136, 138, 140, 142, 144, /* Band 3 */
+ 149, 151, 153, 155, 157, 159, 161, /* Band 4 */
+ 165, 167, 169, 171, 173, 175, 177 /* Band 4 */
+};
+EXPORT_SYMBOL(channel5g);
+
+u8 channel5g_80m[CHANNEL_MAX_NUMBER_5G_80M] = {
+ 42, 58, 106, 122, 138, 155, 171
+};
+EXPORT_SYMBOL(channel5g_80m);
+
void rtl_addr_delay(u32 addr)
{
if (addr == 0xfe)
- mdelay(50);
+ msleep(50);
else if (addr == 0xfd)
- mdelay(5);
+ msleep(5);
else if (addr == 0xfc)
- mdelay(1);
+ msleep(1);
else if (addr == 0xfb)
- udelay(50);
+ usleep_range(50, 100);
else if (addr == 0xfa)
- udelay(5);
+ usleep_range(5, 10);
else if (addr == 0xf9)
- udelay(1);
+ usleep_range(1, 2);
}
EXPORT_SYMBOL(rtl_addr_delay);
void rtl_rfreg_delay(struct ieee80211_hw *hw, enum radio_path rfpath, u32 addr,
u32 mask, u32 data)
{
- if (addr == 0xfe) {
- mdelay(50);
- } else if (addr == 0xfd) {
- mdelay(5);
- } else if (addr == 0xfc) {
- mdelay(1);
- } else if (addr == 0xfb) {
- udelay(50);
- } else if (addr == 0xfa) {
- udelay(5);
- } else if (addr == 0xf9) {
- udelay(1);
+ if (addr >= 0xf9 && addr <= 0xfe) {
+ rtl_addr_delay(addr);
} else {
rtl_set_rfreg(hw, rfpath, addr, mask, data);
- udelay(1);
+ usleep_range(1, 2);
}
}
EXPORT_SYMBOL(rtl_rfreg_delay);
void rtl_bb_delay(struct ieee80211_hw *hw, u32 addr, u32 data)
{
- if (addr == 0xfe) {
- mdelay(50);
- } else if (addr == 0xfd) {
- mdelay(5);
- } else if (addr == 0xfc) {
- mdelay(1);
- } else if (addr == 0xfb) {
- udelay(50);
- } else if (addr == 0xfa) {
- udelay(5);
- } else if (addr == 0xf9) {
- udelay(1);
+ if (addr >= 0xf9 && addr <= 0xfe) {
+ rtl_addr_delay(addr);
} else {
rtl_set_bbreg(hw, addr, MASKDWORD, data);
- udelay(1);
+ usleep_range(1, 2);
}
}
EXPORT_SYMBOL(rtl_bb_delay);
diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c
index 7f471bf..283d608 100644
--- a/drivers/net/wireless/realtek/rtlwifi/pci.c
+++ b/drivers/net/wireless/realtek/rtlwifi/pci.c
@@ -855,7 +855,7 @@
}
/* handle command packet here */
if (rtlpriv->cfg->ops->rx_command_packet &&
- rtlpriv->cfg->ops->rx_command_packet(hw, stats, skb)) {
+ rtlpriv->cfg->ops->rx_command_packet(hw, &stats, skb)) {
dev_kfree_skb_any(skb);
goto new_trx_end;
}
@@ -2392,7 +2392,6 @@
rtlpriv->cfg->ops->deinit_sw_vars(hw);
if (rtlpci->irq_alloc) {
- synchronize_irq(rtlpci->pdev->irq);
free_irq(rtlpci->pdev->irq, hw);
rtlpci->irq_alloc = 0;
}
diff --git a/drivers/net/wireless/realtek/rtlwifi/rc.c b/drivers/net/wireless/realtek/rtlwifi/rc.c
index 28f7010..1aca777 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rc.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rc.c
@@ -41,7 +41,7 @@
struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
struct rtl_phy *rtlphy = &(rtlpriv->phy);
struct rtl_sta_info *sta_entry = NULL;
- u8 wireless_mode = 0;
+ u16 wireless_mode = 0;
/*
*this rate is no use for true rate, firmware
@@ -99,7 +99,7 @@
{
struct rtl_mac *mac = rtl_mac(rtlpriv);
struct rtl_sta_info *sta_entry = NULL;
- u8 wireless_mode = 0;
+ u16 wireless_mode = 0;
u8 sgi_20 = 0, sgi_40 = 0, sgi_80 = 0;
if (sta) {
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/trx.c
index 791efbe..1170106 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/trx.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/trx.c
@@ -851,7 +851,7 @@
}
u32 rtl88ee_rx_command_packet(struct ieee80211_hw *hw,
- struct rtl_stats status,
+ const struct rtl_stats *status,
struct sk_buff *skb)
{
return 0;
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/trx.h b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/trx.h
index eab5ae0..5a24d19 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/trx.h
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/trx.h
@@ -790,7 +790,7 @@
bool firstseg, bool lastseg,
struct sk_buff *skb);
u32 rtl88ee_rx_command_packet(struct ieee80211_hw *hw,
- struct rtl_stats status,
+ const struct rtl_stats *status,
struct sk_buff *skb);
#endif
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.c
index bb06fe8..7810fe8 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.c
@@ -924,19 +924,11 @@
static u8 _rtl92c_phy_get_rightchnlplace(u8 chnl)
{
- u8 channel_5g[59] = {
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
- 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58,
- 60, 62, 64, 100, 102, 104, 106, 108, 110, 112,
- 114, 116, 118, 120, 122, 124, 126, 128,
- 130, 132, 134, 136, 138, 140, 149, 151,
- 153, 155, 157, 159, 161, 163, 165
- };
u8 place = chnl;
if (chnl > 14) {
- for (place = 14; place < sizeof(channel_5g); place++) {
- if (channel_5g[place] == chnl) {
+ for (place = 14; place < sizeof(channel5g); place++) {
+ if (channel5g[place] == chnl) {
place++;
break;
}
@@ -2471,16 +2463,9 @@
{
int i;
- u8 channel_5g[45] = {
- 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58,
- 60, 62, 64, 100, 102, 104, 106, 108, 110, 112,
- 114, 116, 118, 120, 122, 124, 126, 128, 130, 132,
- 134, 136, 138, 140, 149, 151, 153, 155, 157, 159,
- 161, 163, 165
- };
- for (i = 0; i < sizeof(channel_5g); i++)
- if (channel == channel_5g[i])
+ for (i = 0; i < sizeof(channel5g); i++)
+ if (channel == channel5g[i])
return true;
return false;
}
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c
index 5f14308..9fd3f1b6 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c
@@ -2018,18 +2018,6 @@
struct rtl_efuse *efu = rtl_efuse(rtl_priv(hw));
struct txpower_info_2g pwr2g;
struct txpower_info_5g pwr5g;
- u8 channel5g[CHANNEL_MAX_NUMBER_5G] = {
- 36, 38, 40, 42, 44, 46, 48, 50, 52, 54,
- 56, 58, 60, 62, 64, 100, 102, 104, 106,
- 108, 110, 112, 114, 116, 118, 120, 122,
- 124, 126, 128, 130, 132, 134, 136, 138,
- 140, 142, 144, 149, 151, 153, 155, 157,
- 159, 161, 163, 165, 167, 168, 169, 171,
- 173, 175, 177
- };
- u8 channel5g_80m[CHANNEL_MAX_NUMBER_5G_80M] = {
- 42, 58, 106, 122, 138, 155, 171
- };
u8 rf, idx;
u8 i;
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c
index d39ee67..24eff8e 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c
@@ -1105,13 +1105,13 @@
}
u32 rtl92ee_rx_command_packet(struct ieee80211_hw *hw,
- struct rtl_stats status,
+ const struct rtl_stats *status,
struct sk_buff *skb)
{
u32 result = 0;
struct rtl_priv *rtlpriv = rtl_priv(hw);
- switch (status.packet_report_type) {
+ switch (status->packet_report_type) {
case NORMAL_RX:
result = 0;
break;
@@ -1121,7 +1121,7 @@
break;
default:
RT_TRACE(rtlpriv, COMP_RECV, DBG_TRACE,
- "Unknown packet type %d\n", status.packet_report_type);
+ "Unknown packet type %d\n", status->packet_report_type);
break;
}
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.h
index 8f78ac9..a4c3834 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.h
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.h
@@ -857,6 +857,6 @@
bool firstseg, bool lastseg,
struct sk_buff *skb);
u32 rtl92ee_rx_command_packet(struct ieee80211_hw *hw,
- struct rtl_stats status,
+ const struct rtl_stats *status,
struct sk_buff *skb);
#endif
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/trx.c
index 2f7c144..7b4a9b6 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/trx.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/trx.c
@@ -710,7 +710,7 @@
}
u32 rtl8723e_rx_command_packet(struct ieee80211_hw *hw,
- struct rtl_stats status,
+ const struct rtl_stats *status,
struct sk_buff *skb)
{
return 0;
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/trx.h b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/trx.h
index 017da7e..32970bf 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/trx.h
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/trx.h
@@ -716,6 +716,6 @@
bool firstseg, bool lastseg,
struct sk_buff *skb);
u32 rtl8723e_rx_command_packet(struct ieee80211_hw *hw,
- struct rtl_stats status,
+ const struct rtl_stats *status,
struct sk_buff *skb);
#endif
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/trx.c
index 338ec9a..6034597 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/trx.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/trx.c
@@ -758,13 +758,13 @@
}
u32 rtl8723be_rx_command_packet(struct ieee80211_hw *hw,
- struct rtl_stats status,
+ const struct rtl_stats *status,
struct sk_buff *skb)
{
u32 result = 0;
struct rtl_priv *rtlpriv = rtl_priv(hw);
- switch (status.packet_report_type) {
+ switch (status->packet_report_type) {
case NORMAL_RX:
result = 0;
break;
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/trx.h b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/trx.h
index 45949ac..40c36607 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/trx.h
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/trx.h
@@ -620,6 +620,6 @@
bool firstseg, bool lastseg,
struct sk_buff *skb);
u32 rtl8723be_rx_command_packet(struct ieee80211_hw *hw,
- struct rtl_stats status,
+ const struct rtl_stats *status,
struct sk_buff *skb);
#endif
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/fw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/fw.c
index 525eb23..a4fc70e 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/fw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/fw.c
@@ -271,7 +271,7 @@
err = _rtl8821ae_fw_free_to_go(hw);
if (err) {
- RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_DMESG,
"Firmware is not ready to run!\n");
} else {
RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD,
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c
index bbb789f..fe900ba 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c
@@ -2786,14 +2786,6 @@
struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
struct txpower_info_2g pwrinfo24g;
struct txpower_info_5g pwrinfo5g;
- u8 channel5g[CHANNEL_MAX_NUMBER_5G] = {
- 36, 38, 40, 42, 44, 46, 48, 50, 52, 54,
- 56, 58, 60, 62, 64, 100, 102, 104, 106,
- 108, 110, 112, 114, 116, 118, 120, 122,
- 124, 126, 128, 130, 132, 134, 136, 138,
- 140, 142, 144, 149, 151, 153, 155, 157,
- 159, 161, 163, 165, 167, 168, 169, 171, 173, 175, 177};
- u8 channel5g_80m[CHANNEL_MAX_NUMBER_5G_80M] = {42, 58, 106, 122, 138, 155, 171};
u8 rf_path, index;
u8 i;
@@ -2872,16 +2864,6 @@
struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
struct txpower_info_2g pwrinfo24g;
struct txpower_info_5g pwrinfo5g;
- u8 channel5g[CHANNEL_MAX_NUMBER_5G] = {
- 36, 38, 40, 42, 44, 46, 48, 50, 52, 54,
- 56, 58, 60, 62, 64, 100, 102, 104, 106,
- 108, 110, 112, 114, 116, 118, 120, 122,
- 124, 126, 128, 130, 132, 134, 136, 138,
- 140, 142, 144, 149, 151, 153, 155, 157,
- 159, 161, 163, 165, 167, 168, 169, 171,
- 173, 175, 177};
- u8 channel5g_80m[CHANNEL_MAX_NUMBER_5G_80M] = {
- 42, 58, 106, 122, 138, 155, 171};
u8 rf_path, index;
u8 i;
@@ -3855,7 +3837,7 @@
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
- u8 wireless_mode = mac->mode;
+ u16 wireless_mode = mac->mode;
u8 sifs_timer, r2t_sifs;
rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SLOT_TIME,
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c
index 9b4d8a6..74165b3 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c
@@ -1472,18 +1472,13 @@
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
char channel_index = -1;
- u8 channel_5g[CHANNEL_MAX_NUMBER_5G] = {
- 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64,
- 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122,
- 124, 126, 128, 130, 132, 134, 136, 138, 140, 142, 144, 149,
- 151, 153, 155, 157, 159, 161, 163, 165, 167, 168, 169, 171,
- 173, 175, 177};
u8 i = 0;
+
if (band == BAND_ON_2_4G)
channel_index = channel - 1;
else if (band == BAND_ON_5G) {
- for (i = 0; i < sizeof(channel_5g)/sizeof(u8); ++i) {
- if (channel_5g[i] == channel)
+ for (i = 0; i < sizeof(channel5g)/sizeof(u8); ++i) {
+ if (channel5g[i] == channel)
channel_index = i;
}
} else
@@ -2240,13 +2235,6 @@
static bool _rtl8821ae_phy_get_chnl_index(u8 channel, u8 *chnl_index)
{
- u8 channel_5g[CHANNEL_MAX_NUMBER_5G] = {
- 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62,
- 64, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118,
- 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140,
- 142, 144, 149, 151, 153, 155, 157, 159, 161, 163, 165,
- 167, 168, 169, 171, 173, 175, 177
- };
u8 i = 0;
bool in_24g = true;
@@ -2257,7 +2245,7 @@
in_24g = false;
for (i = 0; i < CHANNEL_MAX_NUMBER_5G; ++i) {
- if (channel_5g[i] == channel) {
+ if (channel5g[i] == channel) {
*chnl_index = i;
return in_24g;
}
@@ -2728,13 +2716,10 @@
rate <= DESC_RATEVHT2SS_MCS9))
txpower += rtlefuse->txpwr_5g_bw40diff[path][TX_2S];
} else if (bandwidth == HT_CHANNEL_WIDTH_80) {
- u8 channel_5g_80m[CHANNEL_MAX_NUMBER_5G_80M] = {
- 42, 58, 106, 122, 138, 155, 171
- };
u8 i;
- for (i = 0; i < sizeof(channel_5g_80m) / sizeof(u8); ++i)
- if (channel_5g_80m[i] == channel)
+ for (i = 0; i < sizeof(channel5g_80m) / sizeof(u8); ++i)
+ if (channel5g_80m[i] == channel)
index = i;
if ((DESC_RATEMCS0 <= rate && rate <= DESC_RATEMCS15) ||
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.c
index 174743a..41efaa1 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.c
@@ -998,13 +998,13 @@
}
u32 rtl8821ae_rx_command_packet(struct ieee80211_hw *hw,
- struct rtl_stats status,
+ const struct rtl_stats *status,
struct sk_buff *skb)
{
u32 result = 0;
struct rtl_priv *rtlpriv = rtl_priv(hw);
- switch (status.packet_report_type) {
+ switch (status->packet_report_type) {
case NORMAL_RX:
result = 0;
break;
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h
index 3140904..ad565be 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h
@@ -615,6 +615,6 @@
bool firstseg, bool lastseg,
struct sk_buff *skb);
u32 rtl8821ae_rx_command_packet(struct ieee80211_hw *hw,
- struct rtl_stats status,
+ const struct rtl_stats *status,
struct sk_buff *skb);
#endif
diff --git a/drivers/net/wireless/realtek/rtlwifi/wifi.h b/drivers/net/wireless/realtek/rtlwifi/wifi.h
index 4544752..554d814 100644
--- a/drivers/net/wireless/realtek/rtlwifi/wifi.h
+++ b/drivers/net/wireless/realtek/rtlwifi/wifi.h
@@ -116,17 +116,12 @@
#define CHANNEL_MAX_NUMBER (14 + 24 + 21) /* 14 is the max channel no */
#define CHANNEL_MAX_NUMBER_2G 14
-#define CHANNEL_MAX_NUMBER_5G 54 /* Please refer to
+#define CHANNEL_MAX_NUMBER_5G 49 /* Please refer to
*"phy_GetChnlGroup8812A" and
* "Hal_ReadTxPowerInfo8812A"
*/
#define CHANNEL_MAX_NUMBER_5G_80M 7
#define CHANNEL_GROUP_MAX (3 + 9) /* ch1~3, 4~9, 10~14 = three groups */
-#define CHANNEL_MAX_NUMBER_5G 54 /* Please refer to
- *"phy_GetChnlGroup8812A" and
- * "Hal_ReadTxPowerInfo8812A"
- */
-#define CHANNEL_MAX_NUMBER_5G_80M 7
#define MAX_PG_GROUP 13
#define CHANNEL_GROUP_MAX_2G 3
#define CHANNEL_GROUP_IDX_5GL 3
@@ -1323,14 +1318,13 @@
struct rtl_sta_info {
struct list_head list;
- u8 ratr_index;
- u8 wireless_mode;
- u8 mimo_ps;
- u8 mac_addr[ETH_ALEN];
struct rtl_tid_data tids[MAX_TID_COUNT];
-
/* just used for ap adhoc or mesh*/
struct rssi_sta rssi_stat;
+ u16 wireless_mode;
+ u8 ratr_index;
+ u8 mimo_ps;
+ u8 mac_addr[ETH_ALEN];
} __packed;
struct rtl_priv;
@@ -2194,7 +2188,7 @@
bool (*get_btc_status) (void);
bool (*is_fw_header)(struct rtlwifi_firmware_header *hdr);
u32 (*rx_command_packet)(struct ieee80211_hw *hw,
- struct rtl_stats status, struct sk_buff *skb);
+ const struct rtl_stats *status, struct sk_buff *skb);
void (*add_wowlan_pattern)(struct ieee80211_hw *hw,
struct rtl_wow_pattern *rtl_pattern,
u8 index);
@@ -2904,6 +2898,10 @@
#define STBC_VHT_TEST_TX_ENABLE BIT(2)
#define STBC_VHT_CAP_TX BIT(3)
+extern u8 channel5g[CHANNEL_MAX_NUMBER_5G];
+
+extern u8 channel5g_80m[CHANNEL_MAX_NUMBER_5G_80M];
+
static inline u8 rtl_read_byte(struct rtl_priv *rtlpriv, u32 addr)
{
return rtlpriv->io.read8_sync(rtlpriv, addr);
diff --git a/drivers/net/wireless/st/cw1200/cw1200_spi.c b/drivers/net/wireless/st/cw1200/cw1200_spi.c
index a740083..63f95e9 100644
--- a/drivers/net/wireless/st/cw1200/cw1200_spi.c
+++ b/drivers/net/wireless/st/cw1200/cw1200_spi.c
@@ -446,8 +446,7 @@
return 0;
}
-#ifdef CONFIG_PM
-static int cw1200_spi_suspend(struct device *dev)
+static int __maybe_unused cw1200_spi_suspend(struct device *dev)
{
struct hwbus_priv *self = spi_get_drvdata(to_spi_device(dev));
@@ -460,16 +459,12 @@
static SIMPLE_DEV_PM_OPS(cw1200_pm_ops, cw1200_spi_suspend, NULL);
-#endif
-
static struct spi_driver spi_driver = {
.probe = cw1200_spi_probe,
.remove = cw1200_spi_disconnect,
.driver = {
.name = "cw1200_wlan_spi",
-#ifdef CONFIG_PM
- .pm = &cw1200_pm_ops,
-#endif
+ .pm = IS_ENABLED(CONFIG_PM) ? &cw1200_pm_ops : NULL,
},
};
diff --git a/drivers/net/wireless/st/cw1200/pm.h b/drivers/net/wireless/st/cw1200/pm.h
index 3ed90ff..5345484 100644
--- a/drivers/net/wireless/st/cw1200/pm.h
+++ b/drivers/net/wireless/st/cw1200/pm.h
@@ -31,13 +31,18 @@
void cw1200_pm_deinit(struct cw1200_pm_state *pm);
int cw1200_wow_suspend(struct ieee80211_hw *hw,
struct cfg80211_wowlan *wowlan);
-int cw1200_wow_resume(struct ieee80211_hw *hw);
int cw1200_can_suspend(struct cw1200_common *priv);
+int cw1200_wow_resume(struct ieee80211_hw *hw);
void cw1200_pm_stay_awake(struct cw1200_pm_state *pm,
unsigned long tmo);
#else
static inline void cw1200_pm_stay_awake(struct cw1200_pm_state *pm,
- unsigned long tmo) {
+ unsigned long tmo)
+{
+}
+static inline int cw1200_can_suspend(struct cw1200_common *priv)
+{
+ return 0;
}
#endif
#endif
diff --git a/drivers/net/wireless/ti/wl18xx/debugfs.c b/drivers/net/wireless/ti/wl18xx/debugfs.c
index 4edfe28..86ccf84 100644
--- a/drivers/net/wireless/ti/wl18xx/debugfs.c
+++ b/drivers/net/wireless/ti/wl18xx/debugfs.c
@@ -345,6 +345,69 @@
.llseek = default_llseek,
};
+#ifdef CONFIG_CFG80211_CERTIFICATION_ONUS
+static ssize_t radar_debug_mode_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct wl1271 *wl = file->private_data;
+ struct wl12xx_vif *wlvif;
+ unsigned long value;
+ int ret;
+
+ ret = kstrtoul_from_user(user_buf, count, 10, &value);
+ if (ret < 0) {
+ wl1271_warning("illegal radar_debug_mode value!");
+ return -EINVAL;
+ }
+
+ /* valid values: 0/1 */
+ if (!(value == 0 || value == 1)) {
+ wl1271_warning("value is not in valid!");
+ return -EINVAL;
+ }
+
+ mutex_lock(&wl->mutex);
+
+ wl->radar_debug_mode = value;
+
+ if (unlikely(wl->state != WLCORE_STATE_ON))
+ goto out;
+
+ ret = wl1271_ps_elp_wakeup(wl);
+ if (ret < 0)
+ goto out;
+
+ wl12xx_for_each_wlvif_ap(wl, wlvif) {
+ wlcore_cmd_generic_cfg(wl, wlvif,
+ WLCORE_CFG_FEATURE_RADAR_DEBUG,
+ wl->radar_debug_mode, 0);
+ }
+
+ wl1271_ps_elp_sleep(wl);
+out:
+ mutex_unlock(&wl->mutex);
+ return count;
+}
+
+static ssize_t radar_debug_mode_read(struct file *file,
+ char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct wl1271 *wl = file->private_data;
+
+ return wl1271_format_buffer(userbuf, count, ppos,
+ "%d\n", wl->radar_debug_mode);
+}
+
+static const struct file_operations radar_debug_mode_ops = {
+ .write = radar_debug_mode_write,
+ .read = radar_debug_mode_read,
+ .open = simple_open,
+ .llseek = default_llseek,
+};
+#endif /* CFG80211_CERTIFICATION_ONUS */
+
int wl18xx_debugfs_add_files(struct wl1271 *wl,
struct dentry *rootdir)
{
@@ -510,6 +573,9 @@
DEBUGFS_ADD(conf, moddir);
DEBUGFS_ADD(radar_detection, moddir);
+#ifdef CONFIG_CFG80211_CERTIFICATION_ONUS
+ DEBUGFS_ADD(radar_debug_mode, moddir);
+#endif
DEBUGFS_ADD(dynamic_fw_traces, moddir);
return 0;
diff --git a/drivers/net/wireless/ti/wl18xx/event.c b/drivers/net/wireless/ti/wl18xx/event.c
index 719907a..ff6e46d 100644
--- a/drivers/net/wireless/ti/wl18xx/event.c
+++ b/drivers/net/wireless/ti/wl18xx/event.c
@@ -146,7 +146,8 @@
mbox->radar_channel,
wl18xx_radar_type_decode(mbox->radar_type));
- ieee80211_radar_detected(wl->hw);
+ if (!wl->radar_debug_mode)
+ ieee80211_radar_detected(wl->hw);
}
if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) {
diff --git a/drivers/net/wireless/ti/wlcore/init.c b/drivers/net/wireless/ti/wlcore/init.c
index e92f263..d0b7734 100644
--- a/drivers/net/wireless/ti/wlcore/init.c
+++ b/drivers/net/wireless/ti/wlcore/init.c
@@ -558,6 +558,11 @@
if (ret < 0)
return ret;
+ if (wl->radar_debug_mode)
+ wlcore_cmd_generic_cfg(wl, wlvif,
+ WLCORE_CFG_FEATURE_RADAR_DEBUG,
+ wl->radar_debug_mode, 0);
+
return 0;
}
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index 45662cf..dde3620 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -5495,7 +5495,7 @@
{
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
struct wl1271 *wl = hw->priv;
- int channel, ret = 0;
+ int channel, active_roc, ret = 0;
channel = ieee80211_frequency_to_channel(chan->center_freq);
@@ -5508,9 +5508,9 @@
goto out;
/* return EBUSY if we can't ROC right now */
- if (WARN_ON(wl->roc_vif ||
- find_first_bit(wl->roc_map,
- WL12XX_MAX_ROLES) < WL12XX_MAX_ROLES)) {
+ active_roc = find_first_bit(wl->roc_map, WL12XX_MAX_ROLES);
+ if (wl->roc_vif || active_roc < WL12XX_MAX_ROLES) {
+ wl1271_warning("active roc on role %d", active_roc);
ret = -EBUSY;
goto out;
}
diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h
index dda01b1..72c31a8 100644
--- a/drivers/net/wireless/ti/wlcore/wlcore.h
+++ b/drivers/net/wireless/ti/wlcore/wlcore.h
@@ -463,6 +463,7 @@
/* the current dfs region */
enum nl80211_dfs_regions dfs_region;
+ bool radar_debug_mode;
/* size of the private FW status data */
size_t fw_status_len;
diff --git a/drivers/net/xen-netback/common.h b/drivers/net/xen-netback/common.h
index 1128252..f44b388 100644
--- a/drivers/net/xen-netback/common.h
+++ b/drivers/net/xen-netback/common.h
@@ -52,6 +52,7 @@
struct pending_tx_info {
struct xen_netif_tx_request req; /* tx request */
+ unsigned int extra_count;
/* Callback data for released SKBs. The callback is always
* xenvif_zerocopy_callback, desc contains the pending_idx, which is
* also an index in pending_tx_info array. It is initialized in
diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c
index 61b97c3..b42f260 100644
--- a/drivers/net/xen-netback/netback.c
+++ b/drivers/net/xen-netback/netback.c
@@ -95,6 +95,7 @@
static void make_tx_response(struct xenvif_queue *queue,
struct xen_netif_tx_request *txp,
+ unsigned int extra_count,
s8 st);
static void push_tx_responses(struct xenvif_queue *queue);
@@ -696,14 +697,15 @@
}
static void xenvif_tx_err(struct xenvif_queue *queue,
- struct xen_netif_tx_request *txp, RING_IDX end)
+ struct xen_netif_tx_request *txp,
+ unsigned int extra_count, RING_IDX end)
{
RING_IDX cons = queue->tx.req_cons;
unsigned long flags;
do {
spin_lock_irqsave(&queue->response_lock, flags);
- make_tx_response(queue, txp, XEN_NETIF_RSP_ERROR);
+ make_tx_response(queue, txp, extra_count, XEN_NETIF_RSP_ERROR);
push_tx_responses(queue);
spin_unlock_irqrestore(&queue->response_lock, flags);
if (cons == end)
@@ -724,6 +726,7 @@
static int xenvif_count_requests(struct xenvif_queue *queue,
struct xen_netif_tx_request *first,
+ unsigned int extra_count,
struct xen_netif_tx_request *txp,
int work_to_do)
{
@@ -812,7 +815,7 @@
} while (more_data);
if (drop_err) {
- xenvif_tx_err(queue, first, cons + slots);
+ xenvif_tx_err(queue, first, extra_count, cons + slots);
return drop_err;
}
@@ -827,9 +830,10 @@
#define XENVIF_TX_CB(skb) ((struct xenvif_tx_cb *)(skb)->cb)
static inline void xenvif_tx_create_map_op(struct xenvif_queue *queue,
- u16 pending_idx,
- struct xen_netif_tx_request *txp,
- struct gnttab_map_grant_ref *mop)
+ u16 pending_idx,
+ struct xen_netif_tx_request *txp,
+ unsigned int extra_count,
+ struct gnttab_map_grant_ref *mop)
{
queue->pages_to_map[mop-queue->tx_map_ops] = queue->mmap_pages[pending_idx];
gnttab_set_map_op(mop, idx_to_kaddr(queue, pending_idx),
@@ -838,6 +842,7 @@
memcpy(&queue->pending_tx_info[pending_idx].req, txp,
sizeof(*txp));
+ queue->pending_tx_info[pending_idx].extra_count = extra_count;
}
static inline struct sk_buff *xenvif_alloc_skb(unsigned int size)
@@ -880,7 +885,7 @@
shinfo->nr_frags++, txp++, gop++) {
index = pending_index(queue->pending_cons++);
pending_idx = queue->pending_ring[index];
- xenvif_tx_create_map_op(queue, pending_idx, txp, gop);
+ xenvif_tx_create_map_op(queue, pending_idx, txp, 0, gop);
frag_set_pending_idx(&frags[shinfo->nr_frags], pending_idx);
}
@@ -893,7 +898,8 @@
shinfo->nr_frags++, txp++, gop++) {
index = pending_index(queue->pending_cons++);
pending_idx = queue->pending_ring[index];
- xenvif_tx_create_map_op(queue, pending_idx, txp, gop);
+ xenvif_tx_create_map_op(queue, pending_idx, txp, 0,
+ gop);
frag_set_pending_idx(&frags[shinfo->nr_frags],
pending_idx);
}
@@ -1095,8 +1101,9 @@
}
static int xenvif_get_extras(struct xenvif_queue *queue,
- struct xen_netif_extra_info *extras,
- int work_to_do)
+ struct xen_netif_extra_info *extras,
+ unsigned int *extra_count,
+ int work_to_do)
{
struct xen_netif_extra_info extra;
RING_IDX cons = queue->tx.req_cons;
@@ -1109,9 +1116,12 @@
}
RING_COPY_REQUEST(&queue->tx, cons, &extra);
+
+ queue->tx.req_cons = ++cons;
+ (*extra_count)++;
+
if (unlikely(!extra.type ||
extra.type >= XEN_NETIF_EXTRA_TYPE_MAX)) {
- queue->tx.req_cons = ++cons;
netdev_err(queue->vif->dev,
"Invalid extra type: %d\n", extra.type);
xenvif_fatal_tx_err(queue->vif);
@@ -1119,7 +1129,6 @@
}
memcpy(&extras[extra.type - 1], &extra, sizeof(extra));
- queue->tx.req_cons = ++cons;
} while (extra.flags & XEN_NETIF_EXTRA_FLAG_MORE);
return work_to_do;
@@ -1294,6 +1303,7 @@
struct xen_netif_tx_request txreq;
struct xen_netif_tx_request txfrags[XEN_NETBK_LEGACY_SLOTS_MAX];
struct xen_netif_extra_info extras[XEN_NETIF_EXTRA_TYPE_MAX-1];
+ unsigned int extra_count;
u16 pending_idx;
RING_IDX idx;
int work_to_do;
@@ -1330,8 +1340,10 @@
queue->tx.req_cons = ++idx;
memset(extras, 0, sizeof(extras));
+ extra_count = 0;
if (txreq.flags & XEN_NETTXF_extra_info) {
work_to_do = xenvif_get_extras(queue, extras,
+ &extra_count,
work_to_do);
idx = queue->tx.req_cons;
if (unlikely(work_to_do < 0))
@@ -1344,7 +1356,7 @@
extra = &extras[XEN_NETIF_EXTRA_TYPE_MCAST_ADD - 1];
ret = xenvif_mcast_add(queue->vif, extra->u.mcast.addr);
- make_tx_response(queue, &txreq,
+ make_tx_response(queue, &txreq, extra_count,
(ret == 0) ?
XEN_NETIF_RSP_OKAY :
XEN_NETIF_RSP_ERROR);
@@ -1358,12 +1370,14 @@
extra = &extras[XEN_NETIF_EXTRA_TYPE_MCAST_DEL - 1];
xenvif_mcast_del(queue->vif, extra->u.mcast.addr);
- make_tx_response(queue, &txreq, XEN_NETIF_RSP_OKAY);
+ make_tx_response(queue, &txreq, extra_count,
+ XEN_NETIF_RSP_OKAY);
push_tx_responses(queue);
continue;
}
- ret = xenvif_count_requests(queue, &txreq, txfrags, work_to_do);
+ ret = xenvif_count_requests(queue, &txreq, extra_count,
+ txfrags, work_to_do);
if (unlikely(ret < 0))
break;
@@ -1372,7 +1386,7 @@
if (unlikely(txreq.size < ETH_HLEN)) {
netdev_dbg(queue->vif->dev,
"Bad packet size: %d\n", txreq.size);
- xenvif_tx_err(queue, &txreq, idx);
+ xenvif_tx_err(queue, &txreq, extra_count, idx);
break;
}
@@ -1397,7 +1411,7 @@
if (unlikely(skb == NULL)) {
netdev_dbg(queue->vif->dev,
"Can't allocate a skb in start_xmit.\n");
- xenvif_tx_err(queue, &txreq, idx);
+ xenvif_tx_err(queue, &txreq, extra_count, idx);
break;
}
@@ -1416,7 +1430,7 @@
nskb = xenvif_alloc_skb(0);
if (unlikely(nskb == NULL)) {
kfree_skb(skb);
- xenvif_tx_err(queue, &txreq, idx);
+ xenvif_tx_err(queue, &txreq, extra_count, idx);
if (net_ratelimit())
netdev_err(queue->vif->dev,
"Can't allocate the frag_list skb.\n");
@@ -1457,13 +1471,16 @@
if (data_len < txreq.size) {
frag_set_pending_idx(&skb_shinfo(skb)->frags[0],
pending_idx);
- xenvif_tx_create_map_op(queue, pending_idx, &txreq, gop);
+ xenvif_tx_create_map_op(queue, pending_idx, &txreq,
+ extra_count, gop);
gop++;
} else {
frag_set_pending_idx(&skb_shinfo(skb)->frags[0],
INVALID_PENDING_IDX);
- memcpy(&queue->pending_tx_info[pending_idx].req, &txreq,
- sizeof(txreq));
+ memcpy(&queue->pending_tx_info[pending_idx].req,
+ &txreq, sizeof(txreq));
+ queue->pending_tx_info[pending_idx].extra_count =
+ extra_count;
}
queue->pending_cons++;
@@ -1804,7 +1821,8 @@
spin_lock_irqsave(&queue->response_lock, flags);
- make_tx_response(queue, &pending_tx_info->req, status);
+ make_tx_response(queue, &pending_tx_info->req,
+ pending_tx_info->extra_count, status);
/* Release the pending index before pusing the Tx response so
* its available before a new Tx request is pushed by the
@@ -1821,6 +1839,7 @@
static void make_tx_response(struct xenvif_queue *queue,
struct xen_netif_tx_request *txp,
+ unsigned int extra_count,
s8 st)
{
RING_IDX i = queue->tx.rsp_prod_pvt;
@@ -1830,7 +1849,7 @@
resp->id = txp->id;
resp->status = st;
- if (txp->flags & XEN_NETTXF_extra_info)
+ while (extra_count-- != 0)
RING_GET_RESPONSE(&queue->tx, ++i)->status = XEN_NETIF_RSP_NULL;
queue->tx.rsp_prod_pvt = ++i;
diff --git a/drivers/net/xen-netback/xenbus.c b/drivers/net/xen-netback/xenbus.c
index 39a303d..bd182cd 100644
--- a/drivers/net/xen-netback/xenbus.c
+++ b/drivers/net/xen-netback/xenbus.c
@@ -511,8 +511,6 @@
switch (state) {
case XenbusStateInitWait:
case XenbusStateConnected:
- pr_info("%s: prepare for reconnect\n",
- be->dev->nodename);
backend_switch_state(be, XenbusStateInitWait);
break;
case XenbusStateClosing:
diff --git a/drivers/nfc/microread/i2c.c b/drivers/nfc/microread/i2c.c
index 918e8f2..e0e8afd 100644
--- a/drivers/nfc/microread/i2c.c
+++ b/drivers/nfc/microread/i2c.c
@@ -246,18 +246,10 @@
const struct i2c_device_id *id)
{
struct microread_i2c_phy *phy;
- struct microread_nfc_platform_data *pdata =
- dev_get_platdata(&client->dev);
int r;
dev_dbg(&client->dev, "client %p\n", client);
- if (!pdata) {
- nfc_err(&client->dev, "client %p: missing platform data\n",
- client);
- return -EINVAL;
- }
-
phy = devm_kzalloc(&client->dev, sizeof(struct microread_i2c_phy),
GFP_KERNEL);
if (!phy)
diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c
index 76c3184..45d0e66 100644
--- a/drivers/nfc/pn544/i2c.c
+++ b/drivers/nfc/pn544/i2c.c
@@ -877,20 +877,8 @@
static int pn544_hci_i2c_acpi_request_resources(struct i2c_client *client)
{
struct pn544_i2c_phy *phy = i2c_get_clientdata(client);
- const struct acpi_device_id *id;
struct gpio_desc *gpiod_en, *gpiod_fw;
- struct device *dev;
-
- if (!client)
- return -EINVAL;
-
- dev = &client->dev;
-
- /* Match the struct device against a given list of ACPI IDs */
- id = acpi_match_device(dev->driver->acpi_match_table, dev);
-
- if (!id)
- return -ENODEV;
+ struct device *dev = &client->dev;
/* Get EN GPIO from ACPI */
gpiod_en = devm_gpiod_get_index(dev, PN544_GPIO_NAME_EN, 1,
diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c
index 5e78382..8453f08 100644
--- a/drivers/of/of_mdio.c
+++ b/drivers/of/of_mdio.c
@@ -56,7 +56,7 @@
phy = phy_device_create(mdio, addr, phy_id, 0, NULL);
else
phy = get_phy_device(mdio, addr, is_c45);
- if (!phy || IS_ERR(phy))
+ if (IS_ERR_OR_NULL(phy))
return 1;
rc = irq_of_parse_and_map(child, 0);
@@ -98,7 +98,7 @@
int rc;
mdiodev = mdio_device_create(mdio, addr);
- if (!mdiodev || IS_ERR(mdiodev))
+ if (IS_ERR(mdiodev))
return 1;
/* Associate the OF node with the device structure so it
@@ -412,7 +412,7 @@
if (strcmp(managed, "in-band-status") == 0) {
/* status is zeroed, namely its .link member */
phy = fixed_phy_register(PHY_POLL, &status, -1, np);
- return IS_ERR(phy) ? PTR_ERR(phy) : 0;
+ return PTR_ERR_OR_ZERO(phy);
}
}
@@ -434,7 +434,7 @@
return -EPROBE_DEFER;
phy = fixed_phy_register(PHY_POLL, &status, link_gpio, np);
- return IS_ERR(phy) ? PTR_ERR(phy) : 0;
+ return PTR_ERR_OR_ZERO(phy);
}
/* Old binding */
@@ -446,7 +446,7 @@
status.pause = be32_to_cpu(fixed_link_prop[3]);
status.asym_pause = be32_to_cpu(fixed_link_prop[4]);
phy = fixed_phy_register(PHY_POLL, &status, -1, np);
- return IS_ERR(phy) ? PTR_ERR(phy) : 0;
+ return PTR_ERR_OR_ZERO(phy);
}
return -ENODEV;
diff --git a/include/asm-generic/checksum.h b/include/asm-generic/checksum.h
index 59811df..3150cbd 100644
--- a/include/asm-generic/checksum.h
+++ b/include/asm-generic/checksum.h
@@ -65,14 +65,14 @@
* returns a 16-bit checksum, already complemented
*/
extern __wsum
-csum_tcpudp_nofold(__be32 saddr, __be32 daddr, unsigned short len,
- unsigned short proto, __wsum sum);
+csum_tcpudp_nofold(__be32 saddr, __be32 daddr, __u32 len,
+ __u8 proto, __wsum sum);
#endif
#ifndef csum_tcpudp_magic
static inline __sum16
-csum_tcpudp_magic(__be32 saddr, __be32 daddr, unsigned short len,
- unsigned short proto, __wsum sum)
+csum_tcpudp_magic(__be32 saddr, __be32 daddr, __u32 len,
+ __u8 proto, __wsum sum)
{
return csum_fold(csum_tcpudp_nofold(saddr, daddr, len, proto, sum));
}
diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h
index 700d0c6..846513c 100644
--- a/include/linux/bcma/bcma_driver_chipcommon.h
+++ b/include/linux/bcma/bcma_driver_chipcommon.h
@@ -576,13 +576,11 @@
u32 crystalfreq; /* The active crystal frequency (in kHz) */
};
-#ifdef CONFIG_BCMA_DRIVER_MIPS
+#ifdef CONFIG_BCMA_PFLASH
struct bcma_pflash {
bool present;
- u8 buswidth;
- u32 window;
- u32 window_size;
};
+#endif
#ifdef CONFIG_BCMA_SFLASH
struct mtd_info;
@@ -606,6 +604,7 @@
};
#endif
+#ifdef CONFIG_BCMA_DRIVER_MIPS
struct bcma_serial_port {
void *regs;
unsigned long clockspeed;
@@ -625,8 +624,9 @@
/* Fast Powerup Delay constant */
u16 fast_pwrup_delay;
struct bcma_chipcommon_pmu pmu;
-#ifdef CONFIG_BCMA_DRIVER_MIPS
+#ifdef CONFIG_BCMA_PFLASH
struct bcma_pflash pflash;
+#endif
#ifdef CONFIG_BCMA_SFLASH
struct bcma_sflash sflash;
#endif
@@ -634,6 +634,7 @@
struct bcma_nflash nflash;
#endif
+#ifdef CONFIG_BCMA_DRIVER_MIPS
int nr_serial_ports;
struct bcma_serial_port serial_ports[4];
#endif /* CONFIG_BCMA_DRIVER_MIPS */
diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h
index a338a68..dcb89e3 100644
--- a/include/linux/if_bridge.h
+++ b/include/linux/if_bridge.h
@@ -46,10 +46,6 @@
#define BR_LEARNING_SYNC BIT(9)
#define BR_PROXYARP_WIFI BIT(10)
-/* values as per ieee8021QBridgeFdbAgingTime */
-#define BR_MIN_AGEING_TIME (10 * HZ)
-#define BR_MAX_AGEING_TIME (1000000 * HZ)
-
#define BR_DEFAULT_AGEING_TIME (300 * HZ)
extern void brioctl_set(int (*ioctl_hook)(struct net *, unsigned int, void __user *));
diff --git a/include/linux/mbus.h b/include/linux/mbus.h
index 1f7bc63..ea34a86 100644
--- a/include/linux/mbus.h
+++ b/include/linux/mbus.h
@@ -69,6 +69,9 @@
int mvebu_mbus_save_cpu_target(u32 *store_addr);
void mvebu_mbus_get_pcie_mem_aperture(struct resource *res);
void mvebu_mbus_get_pcie_io_aperture(struct resource *res);
+int mvebu_mbus_get_dram_win_info(phys_addr_t phyaddr, u8 *target, u8 *attr);
+int mvebu_mbus_get_io_win_info(phys_addr_t phyaddr, u32 *size, u8 *target,
+ u8 *attr);
int mvebu_mbus_add_window_remap_by_id(unsigned int target,
unsigned int attribute,
phys_addr_t base, size_t size,
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index fd30cb5..be693b3 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -786,6 +786,7 @@
enum {
TC_SETUP_MQPRIO,
TC_SETUP_CLSU32,
+ TC_SETUP_CLSFLOWER,
};
struct tc_cls_u32_offload;
@@ -795,6 +796,7 @@
union {
u8 tc;
struct tc_cls_u32_offload *cls_u32;
+ struct tc_cls_flower_offload *cls_flower;
};
};
@@ -1326,6 +1328,7 @@
* @IFF_RXFH_CONFIGURED: device has had Rx Flow indirection table configured
* @IFF_PHONY_HEADROOM: the headroom value is controlled by an external
* entity (i.e. the master device for bridged veth)
+ * @IFF_MACSEC: device is a MACsec device
*/
enum netdev_priv_flags {
IFF_802_1Q_VLAN = 1<<0,
@@ -1355,6 +1358,7 @@
IFF_TEAM = 1<<24,
IFF_RXFH_CONFIGURED = 1<<25,
IFF_PHONY_HEADROOM = 1<<26,
+ IFF_MACSEC = 1<<27,
};
#define IFF_802_1Q_VLAN IFF_802_1Q_VLAN
@@ -1383,6 +1387,7 @@
#define IFF_L3MDEV_SLAVE IFF_L3MDEV_SLAVE
#define IFF_TEAM IFF_TEAM
#define IFF_RXFH_CONFIGURED IFF_RXFH_CONFIGURED
+#define IFF_MACSEC IFF_MACSEC
/**
* struct net_device - The DEVICE structure.
@@ -4043,6 +4048,11 @@
skb->mac_len = mac_len;
}
+static inline bool netif_is_macsec(const struct net_device *dev)
+{
+ return dev->priv_flags & IFF_MACSEC;
+}
+
static inline bool netif_is_macvlan(const struct net_device *dev)
{
return dev->priv_flags & IFF_MACVLAN;
diff --git a/include/linux/phy.h b/include/linux/phy.h
index d6f3641..2abd791 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -327,8 +327,6 @@
/* phy_device: An instance of a PHY
*
* drv: Pointer to the driver for this PHY instance
- * bus: Pointer to the bus this PHY is on
- * dev: driver model device structure for this PHY
* phy_id: UID for this device found during discovery
* c45_ids: 802.3-c45 Device Identifers if is_c45.
* is_c45: Set to true if this phy uses clause 45 addressing.
@@ -338,7 +336,6 @@
* suspended: Set to true if this phy has been suspended successfully.
* state: state of the PHY for management purposes
* dev_flags: Device-specific flags used by the PHY driver.
- * addr: Bus address of PHY
* link_timeout: The number of timer firings to wait before the
* giving up on the current attempt at acquiring a link
* irq: IRQ number of the PHY's interrupt (-1 if none)
diff --git a/include/linux/phy_fixed.h b/include/linux/phy_fixed.h
index 2400d2e..1d41ec4 100644
--- a/include/linux/phy_fixed.h
+++ b/include/linux/phy_fixed.h
@@ -19,7 +19,7 @@
struct fixed_phy_status *status,
int link_gpio,
struct device_node *np);
-extern void fixed_phy_del(int phy_addr);
+extern void fixed_phy_unregister(struct phy_device *phydev);
extern int fixed_phy_set_link_update(struct phy_device *phydev,
int (*link_update)(struct net_device *,
struct fixed_phy_status *));
@@ -40,9 +40,8 @@
{
return ERR_PTR(-ENODEV);
}
-static inline int fixed_phy_del(int phy_addr)
+static inline void fixed_phy_unregister(struct phy_device *phydev)
{
- return -ENODEV;
}
static inline int fixed_phy_set_link_update(struct phy_device *phydev,
int (*link_update)(struct net_device *,
diff --git a/include/linux/platform_data/brcmfmac-sdio.h b/include/linux/platform_data/brcmfmac-sdio.h
deleted file mode 100644
index e75dcbf..0000000
--- a/include/linux/platform_data/brcmfmac-sdio.h
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (c) 2013 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _LINUX_BRCMFMAC_PLATFORM_H
-#define _LINUX_BRCMFMAC_PLATFORM_H
-
-/*
- * Platform specific driver functions and data. Through the platform specific
- * device data functions can be provided to help the brcmfmac driver to
- * operate with the device in combination with the used platform.
- *
- * Use the platform data in the following (similar) way:
- *
- *
-#include <brcmfmac_platform.h>
-
-
-static void brcmfmac_power_on(void)
-{
-}
-
-static void brcmfmac_power_off(void)
-{
-}
-
-static void brcmfmac_reset(void)
-{
-}
-
-static struct brcmfmac_sdio_platform_data brcmfmac_sdio_pdata = {
- .power_on = brcmfmac_power_on,
- .power_off = brcmfmac_power_off,
- .reset = brcmfmac_reset
-};
-
-static struct platform_device brcmfmac_device = {
- .name = BRCMFMAC_SDIO_PDATA_NAME,
- .id = PLATFORM_DEVID_NONE,
- .dev.platform_data = &brcmfmac_sdio_pdata
-};
-
-void __init brcmfmac_init_pdata(void)
-{
- brcmfmac_sdio_pdata.oob_irq_supported = true;
- brcmfmac_sdio_pdata.oob_irq_nr = gpio_to_irq(GPIO_BRCMF_SDIO_OOB);
- brcmfmac_sdio_pdata.oob_irq_flags = IORESOURCE_IRQ |
- IORESOURCE_IRQ_HIGHLEVEL;
- platform_device_register(&brcmfmac_device);
-}
- *
- *
- * Note: the brcmfmac can be loaded as module or be statically built-in into
- * the kernel. If built-in then do note that it uses module_init (and
- * module_exit) routines which equal device_initcall. So if you intend to
- * create a module with the platform specific data for the brcmfmac and have
- * it built-in to the kernel then use a higher initcall then device_initcall
- * (see init.h). If this is not done then brcmfmac will load without problems
- * but will not pickup the platform data.
- *
- * When the driver does not "detect" platform driver data then it will continue
- * without reporting anything and just assume there is no data needed. Which is
- * probably true for most platforms.
- *
- * Explanation of the platform_data fields:
- *
- * drive_strength: is the preferred drive_strength to be used for the SDIO
- * pins. If 0 then a default value will be used. This is the target drive
- * strength, the exact drive strength which will be used depends on the
- * capabilities of the device.
- *
- * oob_irq_supported: does the board have support for OOB interrupts. SDIO
- * in-band interrupts are relatively slow and for having less overhead on
- * interrupt processing an out of band interrupt can be used. If the HW
- * supports this then enable this by setting this field to true and configure
- * the oob related fields.
- *
- * oob_irq_nr, oob_irq_flags: the OOB interrupt information. The values are
- * used for registering the irq using request_irq function.
- *
- * broken_sg_support: flag for broken sg list support of SDIO host controller.
- * Set this to true if the SDIO host controller has higher align requirement
- * than 32 bytes for each scatterlist item.
- *
- * sd_head_align: alignment requirement for start of data buffer
- *
- * sd_sgentry_align: length alignment requirement for each sg entry
- *
- * power_on: This function is called by the brcmfmac when the module gets
- * loaded. This can be particularly useful for low power devices. The platform
- * spcific routine may for example decide to power up the complete device.
- * If there is no use-case for this function then provide NULL.
- *
- * power_off: This function is called by the brcmfmac when the module gets
- * unloaded. At this point the device can be powered down or otherwise be reset.
- * So if an actual power_off is not supported but reset is then reset the device
- * when this function gets called. This can be particularly useful for low power
- * devices. If there is no use-case for this function (either power-down or
- * reset) then provide NULL.
- *
- * reset: This function can get called if the device communication broke down.
- * This functionality is particularly useful in case of SDIO type devices. It is
- * possible to reset a dongle via sdio data interface, but it requires that
- * this is fully functional. This function is chip/module specific and this
- * function should return only after the complete reset has completed.
- */
-
-#define BRCMFMAC_SDIO_PDATA_NAME "brcmfmac_sdio"
-
-struct brcmfmac_sdio_platform_data {
- unsigned int drive_strength;
- bool oob_irq_supported;
- unsigned int oob_irq_nr;
- unsigned long oob_irq_flags;
- bool broken_sg_support;
- unsigned short sd_head_align;
- unsigned short sd_sgentry_align;
- void (*power_on)(void);
- void (*power_off)(void);
- void (*reset)(void);
-};
-
-#endif /* _LINUX_BRCMFMAC_PLATFORM_H */
diff --git a/include/linux/platform_data/brcmfmac.h b/include/linux/platform_data/brcmfmac.h
new file mode 100644
index 0000000..1d30bf2
--- /dev/null
+++ b/include/linux/platform_data/brcmfmac.h
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 201 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _LINUX_BRCMFMAC_PLATFORM_H
+#define _LINUX_BRCMFMAC_PLATFORM_H
+
+
+#define BRCMFMAC_PDATA_NAME "brcmfmac"
+
+#define BRCMFMAC_COUNTRY_BUF_SZ 4
+
+
+/*
+ * Platform specific driver functions and data. Through the platform specific
+ * device data functions and data can be provided to help the brcmfmac driver to
+ * operate with the device in combination with the used platform.
+ */
+
+
+/**
+ * Note: the brcmfmac can be loaded as module or be statically built-in into
+ * the kernel. If built-in then do note that it uses module_init (and
+ * module_exit) routines which equal device_initcall. So if you intend to
+ * create a module with the platform specific data for the brcmfmac and have
+ * it built-in to the kernel then use a higher initcall then device_initcall
+ * (see init.h). If this is not done then brcmfmac will load without problems
+ * but will not pickup the platform data.
+ *
+ * When the driver does not "detect" platform driver data then it will continue
+ * without reporting anything and just assume there is no data needed. Which is
+ * probably true for most platforms.
+ */
+
+/**
+ * enum brcmf_bus_type - Bus type identifier. Currently SDIO, USB and PCIE are
+ * supported.
+ */
+enum brcmf_bus_type {
+ BRCMF_BUSTYPE_SDIO,
+ BRCMF_BUSTYPE_USB,
+ BRCMF_BUSTYPE_PCIE
+};
+
+
+/**
+ * struct brcmfmac_sdio_pd - SDIO Device specific platform data.
+ *
+ * @txglomsz: SDIO txglom size. Use 0 if default of driver is to be
+ * used.
+ * @drive_strength: is the preferred drive_strength to be used for the SDIO
+ * pins. If 0 then a default value will be used. This is
+ * the target drive strength, the exact drive strength
+ * which will be used depends on the capabilities of the
+ * device.
+ * @oob_irq_supported: does the board have support for OOB interrupts. SDIO
+ * in-band interrupts are relatively slow and for having
+ * less overhead on interrupt processing an out of band
+ * interrupt can be used. If the HW supports this then
+ * enable this by setting this field to true and configure
+ * the oob related fields.
+ * @oob_irq_nr,
+ * @oob_irq_flags: the OOB interrupt information. The values are used for
+ * registering the irq using request_irq function.
+ * @broken_sg_support: flag for broken sg list support of SDIO host controller.
+ * Set this to true if the SDIO host controller has higher
+ * align requirement than 32 bytes for each scatterlist
+ * item.
+ * @sd_head_align: alignment requirement for start of data buffer.
+ * @sd_sgentry_align: length alignment requirement for each sg entry.
+ * @reset: This function can get called if the device communication
+ * broke down. This functionality is particularly useful in
+ * case of SDIO type devices. It is possible to reset a
+ * dongle via sdio data interface, but it requires that
+ * this is fully functional. This function is chip/module
+ * specific and this function should return only after the
+ * complete reset has completed.
+ */
+struct brcmfmac_sdio_pd {
+ int txglomsz;
+ unsigned int drive_strength;
+ bool oob_irq_supported;
+ unsigned int oob_irq_nr;
+ unsigned long oob_irq_flags;
+ bool broken_sg_support;
+ unsigned short sd_head_align;
+ unsigned short sd_sgentry_align;
+ void (*reset)(void);
+};
+
+/**
+ * struct brcmfmac_pd_cc_entry - Struct for translating user space country code
+ * (iso3166) to firmware country code and
+ * revision.
+ *
+ * @iso3166: iso3166 alpha 2 country code string.
+ * @cc: firmware country code string.
+ * @rev: firmware country code revision.
+ */
+struct brcmfmac_pd_cc_entry {
+ char iso3166[BRCMFMAC_COUNTRY_BUF_SZ];
+ char cc[BRCMFMAC_COUNTRY_BUF_SZ];
+ s32 rev;
+};
+
+/**
+ * struct brcmfmac_pd_cc - Struct for translating country codes as set by user
+ * space to a country code and rev which can be used by
+ * firmware.
+ *
+ * @table_size: number of entries in table (> 0)
+ * @table: array of 1 or more elements with translation information.
+ */
+struct brcmfmac_pd_cc {
+ int table_size;
+ struct brcmfmac_pd_cc_entry table[0];
+};
+
+/**
+ * struct brcmfmac_pd_device - Device specific platform data. (id/rev/bus_type)
+ * is the unique identifier of the device.
+ *
+ * @id: ID of the device for which this data is. In case of SDIO
+ * or PCIE this is the chipid as identified by chip.c In
+ * case of USB this is the chipid as identified by the
+ * device query.
+ * @rev: chip revision, see id.
+ * @bus_type: The type of bus. Some chipid/rev exist for different bus
+ * types. Each bus type has its own set of settings.
+ * @feature_disable: Bitmask of features to disable (override), See feature.c
+ * in brcmfmac for details.
+ * @country_codes: If available, pointer to struct for translating country
+ * codes.
+ * @bus: Bus specific (union) device settings. Currently only
+ * SDIO.
+ */
+struct brcmfmac_pd_device {
+ unsigned int id;
+ unsigned int rev;
+ enum brcmf_bus_type bus_type;
+ unsigned int feature_disable;
+ struct brcmfmac_pd_cc *country_codes;
+ union {
+ struct brcmfmac_sdio_pd sdio;
+ } bus;
+};
+
+/**
+ * struct brcmfmac_platform_data - BRCMFMAC specific platform data.
+ *
+ * @power_on: This function is called by the brcmfmac driver when the module
+ * gets loaded. This can be particularly useful for low power
+ * devices. The platform spcific routine may for example decide to
+ * power up the complete device. If there is no use-case for this
+ * function then provide NULL.
+ * @power_off: This function is called by the brcmfmac when the module gets
+ * unloaded. At this point the devices can be powered down or
+ * otherwise be reset. So if an actual power_off is not supported
+ * but reset is supported by the devices then reset the devices
+ * when this function gets called. This can be particularly useful
+ * for low power devices. If there is no use-case for this
+ * function then provide NULL.
+ */
+struct brcmfmac_platform_data {
+ void (*power_on)(void);
+ void (*power_off)(void);
+ char *fw_alternative_path;
+ int device_count;
+ struct brcmfmac_pd_device devices[0];
+};
+
+
+#endif /* _LINUX_BRCMFMAC_PLATFORM_H */
diff --git a/include/linux/platform_data/microread.h b/include/linux/platform_data/microread.h
deleted file mode 100644
index ca13992..0000000
--- a/include/linux/platform_data/microread.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Driver include for the Inside Secure microread NFC Chip.
- *
- * Copyright (C) 2011 Tieto Poland
- * Copyright (C) 2012 Intel Corporation. All rights reserved.
- *
- * 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 program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#ifndef _MICROREAD_H
-#define _MICROREAD_H
-
-#include <linux/i2c.h>
-
-#define MICROREAD_DRIVER_NAME "microread"
-
-/* board config platform data for microread */
-struct microread_nfc_platform_data {
- unsigned int rst_gpio;
- unsigned int irq_gpio;
- unsigned int ioh_gpio;
-};
-
-#endif /* _MICROREAD_H */
diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index bcbf51d..7be9b12 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -158,6 +158,9 @@
u32 segs_in; /* RFC4898 tcpEStatsPerfSegsIn
* total number of segments in.
*/
+ u32 data_segs_in; /* RFC4898 tcpEStatsPerfDataSegsIn
+ * total number of data segments in.
+ */
u32 rcv_nxt; /* What we want to receive next */
u32 copied_seq; /* Head of yet unread data */
u32 rcv_wup; /* rcv_nxt on last window update sent */
@@ -165,6 +168,9 @@
u32 segs_out; /* RFC4898 tcpEStatsPerfSegsOut
* The total number of segments sent.
*/
+ u32 data_segs_out; /* RFC4898 tcpEStatsPerfDataSegsOut
+ * total number of data segments sent.
+ */
u64 bytes_acked; /* RFC4898 tcpEStatsAppHCThruOctetsAcked
* sum(delta(snd_una)), or how many bytes
* were acked.
diff --git a/include/net/act_api.h b/include/net/act_api.h
index 342be6c..2a19fe1 100644
--- a/include/net/act_api.h
+++ b/include/net/act_api.h
@@ -78,11 +78,6 @@
tm->lastuse = now;
}
-#ifdef CONFIG_NET_CLS_ACT
-
-#define ACT_P_CREATED 1
-#define ACT_P_DELETED 1
-
struct tc_action {
void *priv;
const struct tc_action_ops *ops;
@@ -92,6 +87,11 @@
struct tcf_hashinfo *hinfo;
};
+#ifdef CONFIG_NET_CLS_ACT
+
+#define ACT_P_CREATED 1
+#define ACT_P_DELETED 1
+
struct tc_action_ops {
struct list_head head;
char kind[IFNAMSIZ];
@@ -171,5 +171,16 @@
int tcf_action_dump_old(struct sk_buff *skb, struct tc_action *a, int, int);
int tcf_action_dump_1(struct sk_buff *skb, struct tc_action *a, int, int);
int tcf_action_copy_stats(struct sk_buff *, struct tc_action *, int);
+
+#define tc_no_actions(_exts) \
+ (list_empty(&(_exts)->actions))
+
+#define tc_for_each_action(_a, _exts) \
+ list_for_each_entry(a, &(_exts)->actions, list)
+#else /* CONFIG_NET_CLS_ACT */
+
+#define tc_no_actions(_exts) true
+#define tc_for_each_action(_a, _exts) while (0)
+
#endif /* CONFIG_NET_CLS_ACT */
#endif
diff --git a/include/net/checksum.h b/include/net/checksum.h
index abffc64..5c30891 100644
--- a/include/net/checksum.h
+++ b/include/net/checksum.h
@@ -88,8 +88,11 @@
csum_block_add(__wsum csum, __wsum csum2, int offset)
{
u32 sum = (__force u32)csum2;
- if (offset&1)
- sum = ((sum&0xFF00FF)<<8)+((sum>>8)&0xFF00FF);
+
+ /* rotate sum to align it with a 16b boundary */
+ if (offset & 1)
+ sum = ror32(sum, 8);
+
return csum_add(csum, (__force __wsum)sum);
}
@@ -102,10 +105,7 @@
static inline __wsum
csum_block_sub(__wsum csum, __wsum csum2, int offset)
{
- u32 sum = (__force u32)csum2;
- if (offset&1)
- sum = ((sum&0xFF00FF)<<8)+((sum>>8)&0xFF00FF);
- return csum_sub(csum, (__force __wsum)sum);
+ return csum_block_add(csum, ~csum2, offset);
}
static inline __wsum csum_unfold(__sum16 n)
diff --git a/include/net/dst_metadata.h b/include/net/dst_metadata.h
index 84b833a..5db9f59 100644
--- a/include/net/dst_metadata.h
+++ b/include/net/dst_metadata.h
@@ -126,7 +126,7 @@
ip_tunnel_key_init(&tun_dst->u.tun_info.key,
iph->saddr, iph->daddr, iph->tos, iph->ttl,
- 0, 0, tunnel_id, flags);
+ 0, 0, 0, tunnel_id, flags);
return tun_dst;
}
@@ -152,8 +152,11 @@
info->key.u.ipv6.src = ip6h->saddr;
info->key.u.ipv6.dst = ip6h->daddr;
+
info->key.tos = ipv6_get_dsfield(ip6h);
info->key.ttl = ip6h->hop_limit;
+ info->key.label = ip6_flowlabel(ip6h);
+
return tun_dst;
}
diff --git a/include/net/flow_dissector.h b/include/net/flow_dissector.h
index 8c8548c..d3d60dc 100644
--- a/include/net/flow_dissector.h
+++ b/include/net/flow_dissector.h
@@ -184,4 +184,17 @@
u32 flow_hash_from_keys(struct flow_keys *keys);
+static inline bool dissector_uses_key(const struct flow_dissector *flow_dissector,
+ enum flow_dissector_key_id key_id)
+{
+ return flow_dissector->used_keys & (1 << key_id);
+}
+
+static inline void *skb_flow_dissector_target(struct flow_dissector *flow_dissector,
+ enum flow_dissector_key_id key_id,
+ void *target_container)
+{
+ return ((char *)target_container) + flow_dissector->offset[key_id];
+}
+
#endif
diff --git a/include/net/hwbm.h b/include/net/hwbm.h
new file mode 100644
index 0000000..47d0866
--- /dev/null
+++ b/include/net/hwbm.h
@@ -0,0 +1,28 @@
+#ifndef _HWBM_H
+#define _HWBM_H
+
+struct hwbm_pool {
+ /* Capacity of the pool */
+ int size;
+ /* Size of the buffers managed */
+ int frag_size;
+ /* Number of buffers currently used by this pool */
+ int buf_num;
+ /* constructor called during alocation */
+ int (*construct)(struct hwbm_pool *bm_pool, void *buf);
+ /* protect acces to the buffer counter*/
+ spinlock_t lock;
+ /* private data */
+ void *priv;
+};
+#ifdef CONFIG_HWBM
+void hwbm_buf_free(struct hwbm_pool *bm_pool, void *buf);
+int hwbm_pool_refill(struct hwbm_pool *bm_pool, gfp_t gfp);
+int hwbm_pool_add(struct hwbm_pool *bm_pool, unsigned int buf_num, gfp_t gfp);
+#else
+void hwbm_buf_free(struct hwbm_pool *bm_pool, void *buf) {}
+int hwbm_pool_refill(struct hwbm_pool *bm_pool, gfp_t gfp) { return 0; }
+int hwbm_pool_add(struct hwbm_pool *bm_pool, unsigned int buf_num, gfp_t gfp)
+{ return 0; }
+#endif /* CONFIG_HWBM */
+#endif /* _HWBM_H */
diff --git a/include/net/ip6_checksum.h b/include/net/ip6_checksum.h
index 1a49b73..cca8405 100644
--- a/include/net/ip6_checksum.h
+++ b/include/net/ip6_checksum.h
@@ -37,8 +37,7 @@
#ifndef _HAVE_ARCH_IPV6_CSUM
__sum16 csum_ipv6_magic(const struct in6_addr *saddr,
const struct in6_addr *daddr,
- __u32 len, unsigned short proto,
- __wsum csum);
+ __u32 len, __u8 proto, __wsum csum);
#endif
static inline __wsum ip6_compute_pseudo(struct sk_buff *skb, int proto)
diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h
index 0acd80f..5dc2e45 100644
--- a/include/net/ip_tunnels.h
+++ b/include/net/ip_tunnels.h
@@ -48,6 +48,7 @@
__be16 tun_flags;
u8 tos; /* TOS for IPv4, TC for IPv6 */
u8 ttl; /* TTL for IPv4, HL for IPv6 */
+ __be32 label; /* Flow Label for IPv6 */
__be16 tp_src;
__be16 tp_dst;
};
@@ -181,7 +182,7 @@
static inline void ip_tunnel_key_init(struct ip_tunnel_key *key,
__be32 saddr, __be32 daddr,
- u8 tos, u8 ttl,
+ u8 tos, u8 ttl, __be32 label,
__be16 tp_src, __be16 tp_dst,
__be64 tun_id, __be16 tun_flags)
{
@@ -192,6 +193,7 @@
0, IP_TUNNEL_KEY_IPV4_PAD_LEN);
key->tos = tos;
key->ttl = ttl;
+ key->label = label;
key->tun_flags = tun_flags;
/* For the tunnel types on the top of IPsec, the tp_src and tp_dst of
diff --git a/include/net/kcm.h b/include/net/kcm.h
index 95c425c..2840b58 100644
--- a/include/net/kcm.h
+++ b/include/net/kcm.h
@@ -180,8 +180,8 @@
int kcm_proc_init(void);
void kcm_proc_exit(void);
#else
-static int kcm_proc_init(void) { return 0; }
-static void kcm_proc_exit(void) { }
+static inline int kcm_proc_init(void) { return 0; }
+static inline void kcm_proc_exit(void) { }
#endif
static inline void aggregate_psock_stats(struct kcm_psock_stats *stats,
diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h
index bea14ee..caa5e18 100644
--- a/include/net/pkt_cls.h
+++ b/include/net/pkt_cls.h
@@ -409,4 +409,18 @@
return true;
}
+enum tc_fl_command {
+ TC_CLSFLOWER_REPLACE,
+ TC_CLSFLOWER_DESTROY,
+};
+
+struct tc_cls_flower_offload {
+ enum tc_fl_command command;
+ unsigned long cookie;
+ struct flow_dissector *dissector;
+ struct fl_flow_key *mask;
+ struct fl_flow_key *key;
+ struct tcf_exts *exts;
+};
+
#endif
diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h
index 487ef34..efc0174 100644
--- a/include/net/sctp/sm.h
+++ b/include/net/sctp/sm.h
@@ -201,7 +201,7 @@
struct sctp_chunk * sctp_make_datafrag_empty(struct sctp_association *,
const struct sctp_sndrcvinfo *sinfo,
int len, const __u8 flags,
- __u16 ssn);
+ __u16 ssn, gfp_t gfp);
struct sctp_chunk *sctp_make_ecne(const struct sctp_association *,
const __u32);
struct sctp_chunk *sctp_make_sack(const struct sctp_association *);
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index d05b566..9d23766 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -655,7 +655,7 @@
void *sctp_addto_chunk(struct sctp_chunk *, int len, const void *data);
struct sctp_chunk *sctp_chunkify(struct sk_buff *,
const struct sctp_association *,
- struct sock *);
+ struct sock *, gfp_t gfp);
void sctp_init_addrs(struct sctp_chunk *, union sctp_addr *,
union sctp_addr *);
const union sctp_addr *sctp_source(const struct sctp_chunk *chunk);
@@ -717,10 +717,10 @@
__u16 sport, __u16 dport);
struct sctp_packet *sctp_packet_config(struct sctp_packet *, __u32 vtag, int);
sctp_xmit_t sctp_packet_transmit_chunk(struct sctp_packet *,
- struct sctp_chunk *, int);
+ struct sctp_chunk *, int, gfp_t);
sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *,
struct sctp_chunk *);
-int sctp_packet_transmit(struct sctp_packet *);
+int sctp_packet_transmit(struct sctp_packet *, gfp_t);
void sctp_packet_free(struct sctp_packet *);
static inline int sctp_packet_empty(struct sctp_packet *packet)
@@ -1053,7 +1053,7 @@
void sctp_outq_init(struct sctp_association *, struct sctp_outq *);
void sctp_outq_teardown(struct sctp_outq *);
void sctp_outq_free(struct sctp_outq*);
-int sctp_outq_tail(struct sctp_outq *, struct sctp_chunk *chunk);
+int sctp_outq_tail(struct sctp_outq *, struct sctp_chunk *chunk, gfp_t);
int sctp_outq_sack(struct sctp_outq *, struct sctp_chunk *);
int sctp_outq_is_empty(const struct sctp_outq *);
void sctp_outq_restart(struct sctp_outq *);
@@ -1061,7 +1061,7 @@
void sctp_retransmit(struct sctp_outq *, struct sctp_transport *,
sctp_retransmit_reason_t);
void sctp_retransmit_mark(struct sctp_outq *, struct sctp_transport *, __u8);
-int sctp_outq_uncork(struct sctp_outq *);
+int sctp_outq_uncork(struct sctp_outq *, gfp_t gfp);
/* Uncork and flush an outqueue. */
static inline void sctp_outq_cork(struct sctp_outq *q)
{
diff --git a/include/net/tc_act/tc_gact.h b/include/net/tc_act/tc_gact.h
index 04a3183..93c520b 100644
--- a/include/net/tc_act/tc_gact.h
+++ b/include/net/tc_act/tc_gact.h
@@ -16,9 +16,9 @@
#define to_gact(a) \
container_of(a->priv, struct tcf_gact, common)
-#ifdef CONFIG_NET_CLS_ACT
static inline bool is_tcf_gact_shot(const struct tc_action *a)
{
+#ifdef CONFIG_NET_CLS_ACT
struct tcf_gact *gact;
if (a->ops && a->ops->type != TCA_ACT_GACT)
@@ -28,7 +28,7 @@
if (gact->tcf_action == TC_ACT_SHOT)
return true;
+#endif
return false;
}
-#endif
#endif /* __NET_TC_GACT_H */
diff --git a/include/net/tc_act/tc_skbedit.h b/include/net/tc_act/tc_skbedit.h
index 0df9a0d..b496d5a 100644
--- a/include/net/tc_act/tc_skbedit.h
+++ b/include/net/tc_act/tc_skbedit.h
@@ -20,6 +20,7 @@
#define __NET_TC_SKBEDIT_H
#include <net/act_api.h>
+#include <linux/tc_act/tc_skbedit.h>
struct tcf_skbedit {
struct tcf_common common;
@@ -32,4 +33,19 @@
#define to_skbedit(a) \
container_of(a->priv, struct tcf_skbedit, common)
+/* Return true iff action is mark */
+static inline bool is_tcf_skbedit_mark(const struct tc_action *a)
+{
+#ifdef CONFIG_NET_CLS_ACT
+ if (a->ops && a->ops->type == TCA_ACT_SKBEDIT)
+ return to_skbedit(a)->flags == SKBEDIT_F_MARK;
+#endif
+ return false;
+}
+
+static inline u32 tcf_skbedit_mark(const struct tc_action *a)
+{
+ return to_skbedit(a)->mark;
+}
+
#endif /* __NET_TC_SKBEDIT_H */
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 0302636..c8dbd29 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -1840,4 +1840,14 @@
return answ;
}
+static inline void tcp_segs_in(struct tcp_sock *tp, const struct sk_buff *skb)
+{
+ u16 segs_in;
+
+ segs_in = max_t(u16, 1, skb_shinfo(skb)->gso_segs);
+ tp->segs_in += segs_in;
+ if (skb->len > tcp_hdrlen(skb))
+ tp->data_segs_in += segs_in;
+}
+
#endif /* _TCP_H */
diff --git a/include/net/udp_tunnel.h b/include/net/udp_tunnel.h
index 97f5adb..b831140 100644
--- a/include/net/udp_tunnel.h
+++ b/include/net/udp_tunnel.h
@@ -88,8 +88,8 @@
struct sk_buff *skb,
struct net_device *dev, struct in6_addr *saddr,
struct in6_addr *daddr,
- __u8 prio, __u8 ttl, __be16 src_port,
- __be16 dst_port, bool nocheck);
+ __u8 prio, __u8 ttl, __be32 label,
+ __be16 src_port, __be16 dst_port, bool nocheck);
#endif
void udp_tunnel_sock_release(struct socket *sock);
diff --git a/include/net/vxlan.h b/include/net/vxlan.h
index 6eda4ed..a763c96 100644
--- a/include/net/vxlan.h
+++ b/include/net/vxlan.h
@@ -162,6 +162,7 @@
u16 port_max;
u8 tos;
u8 ttl;
+ __be32 label;
u32 flags;
unsigned long age_interval;
unsigned int addrmax;
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index ebd10e6..e25ebcf 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -173,6 +173,7 @@
header-y += if_infiniband.h
header-y += if_link.h
header-y += if_ltalk.h
+header-y += if_macsec.h
header-y += if_packet.h
header-y += if_phonet.h
header-y += if_plip.h
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 0e30b19..924f537 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -375,6 +375,7 @@
};
__u8 tunnel_tos;
__u8 tunnel_ttl;
+ __u32 tunnel_label;
};
#endif /* _UAPI__LINUX_BPF_H__ */
diff --git a/include/uapi/linux/if_ether.h b/include/uapi/linux/if_ether.h
index ea9221b..4a93051 100644
--- a/include/uapi/linux/if_ether.h
+++ b/include/uapi/linux/if_ether.h
@@ -83,6 +83,7 @@
#define ETH_P_8021AD 0x88A8 /* 802.1ad Service VLAN */
#define ETH_P_802_EX1 0x88B5 /* 802.1 Local Experimental 1. */
#define ETH_P_TIPC 0x88CA /* TIPC */
+#define ETH_P_MACSEC 0x88E5 /* 802.1ae MACsec */
#define ETH_P_8021AH 0x88E7 /* 802.1ah Backbone Service Tag */
#define ETH_P_MVRP 0x88F5 /* 802.1Q MVRP */
#define ETH_P_1588 0x88F7 /* IEEE 1588 Timesync */
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index d452cea..8e3f88f 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -413,6 +413,35 @@
#define IFLA_VRF_PORT_MAX (__IFLA_VRF_PORT_MAX - 1)
+/* MACSEC section */
+enum {
+ IFLA_MACSEC_UNSPEC,
+ IFLA_MACSEC_SCI,
+ IFLA_MACSEC_PORT,
+ IFLA_MACSEC_ICV_LEN,
+ IFLA_MACSEC_CIPHER_SUITE,
+ IFLA_MACSEC_WINDOW,
+ IFLA_MACSEC_ENCODING_SA,
+ IFLA_MACSEC_ENCRYPT,
+ IFLA_MACSEC_PROTECT,
+ IFLA_MACSEC_INC_SCI,
+ IFLA_MACSEC_ES,
+ IFLA_MACSEC_SCB,
+ IFLA_MACSEC_REPLAY_PROTECT,
+ IFLA_MACSEC_VALIDATION,
+ __IFLA_MACSEC_MAX,
+};
+
+#define IFLA_MACSEC_MAX (__IFLA_MACSEC_MAX - 1)
+
+enum macsec_validation_type {
+ MACSEC_VALIDATE_DISABLED = 0,
+ MACSEC_VALIDATE_CHECK = 1,
+ MACSEC_VALIDATE_STRICT = 2,
+ __MACSEC_VALIDATE_END,
+ MACSEC_VALIDATE_MAX = __MACSEC_VALIDATE_END - 1,
+};
+
/* IPVLAN section */
enum {
IFLA_IPVLAN_UNSPEC,
@@ -456,6 +485,7 @@
IFLA_VXLAN_GBP,
IFLA_VXLAN_REMCSUM_NOPARTIAL,
IFLA_VXLAN_COLLECT_METADATA,
+ IFLA_VXLAN_LABEL,
__IFLA_VXLAN_MAX
};
#define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1)
@@ -478,6 +508,7 @@
IFLA_GENEVE_UDP_CSUM,
IFLA_GENEVE_UDP_ZERO_CSUM6_TX,
IFLA_GENEVE_UDP_ZERO_CSUM6_RX,
+ IFLA_GENEVE_LABEL,
__IFLA_GENEVE_MAX
};
#define IFLA_GENEVE_MAX (__IFLA_GENEVE_MAX - 1)
diff --git a/include/uapi/linux/if_macsec.h b/include/uapi/linux/if_macsec.h
new file mode 100644
index 0000000..26b0d1e
--- /dev/null
+++ b/include/uapi/linux/if_macsec.h
@@ -0,0 +1,161 @@
+/*
+ * include/uapi/linux/if_macsec.h - MACsec device
+ *
+ * Copyright (c) 2015 Sabrina Dubroca <sd@queasysnail.net>
+ *
+ * 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.
+ */
+
+#ifndef _UAPI_MACSEC_H
+#define _UAPI_MACSEC_H
+
+#include <linux/types.h>
+
+#define MACSEC_GENL_NAME "macsec"
+#define MACSEC_GENL_VERSION 1
+
+#define MACSEC_MAX_KEY_LEN 128
+
+#define DEFAULT_CIPHER_ID 0x0080020001000001ULL
+#define DEFAULT_CIPHER_ALT 0x0080C20001000001ULL
+
+#define MACSEC_MIN_ICV_LEN 8
+#define MACSEC_MAX_ICV_LEN 32
+
+enum macsec_attrs {
+ MACSEC_ATTR_UNSPEC,
+ MACSEC_ATTR_IFINDEX, /* u32, ifindex of the MACsec netdevice */
+ MACSEC_ATTR_RXSC_CONFIG, /* config, nested macsec_rxsc_attrs */
+ MACSEC_ATTR_SA_CONFIG, /* config, nested macsec_sa_attrs */
+ MACSEC_ATTR_SECY, /* dump, nested macsec_secy_attrs */
+ MACSEC_ATTR_TXSA_LIST, /* dump, nested, macsec_sa_attrs for each TXSA */
+ MACSEC_ATTR_RXSC_LIST, /* dump, nested, macsec_rxsc_attrs for each RXSC */
+ MACSEC_ATTR_TXSC_STATS, /* dump, nested, macsec_txsc_stats_attr */
+ MACSEC_ATTR_SECY_STATS, /* dump, nested, macsec_secy_stats_attr */
+ __MACSEC_ATTR_END,
+ NUM_MACSEC_ATTR = __MACSEC_ATTR_END,
+ MACSEC_ATTR_MAX = __MACSEC_ATTR_END - 1,
+};
+
+enum macsec_secy_attrs {
+ MACSEC_SECY_ATTR_UNSPEC,
+ MACSEC_SECY_ATTR_SCI,
+ MACSEC_SECY_ATTR_ENCODING_SA,
+ MACSEC_SECY_ATTR_WINDOW,
+ MACSEC_SECY_ATTR_CIPHER_SUITE,
+ MACSEC_SECY_ATTR_ICV_LEN,
+ MACSEC_SECY_ATTR_PROTECT,
+ MACSEC_SECY_ATTR_REPLAY,
+ MACSEC_SECY_ATTR_OPER,
+ MACSEC_SECY_ATTR_VALIDATE,
+ MACSEC_SECY_ATTR_ENCRYPT,
+ MACSEC_SECY_ATTR_INC_SCI,
+ MACSEC_SECY_ATTR_ES,
+ MACSEC_SECY_ATTR_SCB,
+ __MACSEC_SECY_ATTR_END,
+ NUM_MACSEC_SECY_ATTR = __MACSEC_SECY_ATTR_END,
+ MACSEC_SECY_ATTR_MAX = __MACSEC_SECY_ATTR_END - 1,
+};
+
+enum macsec_rxsc_attrs {
+ MACSEC_RXSC_ATTR_UNSPEC,
+ MACSEC_RXSC_ATTR_SCI, /* config/dump, u64 */
+ MACSEC_RXSC_ATTR_ACTIVE, /* config/dump, u8 0..1 */
+ MACSEC_RXSC_ATTR_SA_LIST, /* dump, nested */
+ MACSEC_RXSC_ATTR_STATS, /* dump, nested, macsec_rxsc_stats_attr */
+ __MACSEC_RXSC_ATTR_END,
+ NUM_MACSEC_RXSC_ATTR = __MACSEC_RXSC_ATTR_END,
+ MACSEC_RXSC_ATTR_MAX = __MACSEC_RXSC_ATTR_END - 1,
+};
+
+enum macsec_sa_attrs {
+ MACSEC_SA_ATTR_UNSPEC,
+ MACSEC_SA_ATTR_AN, /* config/dump, u8 0..3 */
+ MACSEC_SA_ATTR_ACTIVE, /* config/dump, u8 0..1 */
+ MACSEC_SA_ATTR_PN, /* config/dump, u32 */
+ MACSEC_SA_ATTR_KEY, /* config, data */
+ MACSEC_SA_ATTR_KEYID, /* config/dump, u64 */
+ MACSEC_SA_ATTR_STATS, /* dump, nested, macsec_sa_stats_attr */
+ __MACSEC_SA_ATTR_END,
+ NUM_MACSEC_SA_ATTR = __MACSEC_SA_ATTR_END,
+ MACSEC_SA_ATTR_MAX = __MACSEC_SA_ATTR_END - 1,
+};
+
+enum macsec_nl_commands {
+ MACSEC_CMD_GET_TXSC,
+ MACSEC_CMD_ADD_RXSC,
+ MACSEC_CMD_DEL_RXSC,
+ MACSEC_CMD_UPD_RXSC,
+ MACSEC_CMD_ADD_TXSA,
+ MACSEC_CMD_DEL_TXSA,
+ MACSEC_CMD_UPD_TXSA,
+ MACSEC_CMD_ADD_RXSA,
+ MACSEC_CMD_DEL_RXSA,
+ MACSEC_CMD_UPD_RXSA,
+};
+
+/* u64 per-RXSC stats */
+enum macsec_rxsc_stats_attr {
+ MACSEC_RXSC_STATS_ATTR_UNSPEC,
+ MACSEC_RXSC_STATS_ATTR_IN_OCTETS_VALIDATED,
+ MACSEC_RXSC_STATS_ATTR_IN_OCTETS_DECRYPTED,
+ MACSEC_RXSC_STATS_ATTR_IN_PKTS_UNCHECKED,
+ MACSEC_RXSC_STATS_ATTR_IN_PKTS_DELAYED,
+ MACSEC_RXSC_STATS_ATTR_IN_PKTS_OK,
+ MACSEC_RXSC_STATS_ATTR_IN_PKTS_INVALID,
+ MACSEC_RXSC_STATS_ATTR_IN_PKTS_LATE,
+ MACSEC_RXSC_STATS_ATTR_IN_PKTS_NOT_VALID,
+ MACSEC_RXSC_STATS_ATTR_IN_PKTS_NOT_USING_SA,
+ MACSEC_RXSC_STATS_ATTR_IN_PKTS_UNUSED_SA,
+ __MACSEC_RXSC_STATS_ATTR_END,
+ NUM_MACSEC_RXSC_STATS_ATTR = __MACSEC_RXSC_STATS_ATTR_END,
+ MACSEC_RXSC_STATS_ATTR_MAX = __MACSEC_RXSC_STATS_ATTR_END - 1,
+};
+
+/* u32 per-{RX,TX}SA stats */
+enum macsec_sa_stats_attr {
+ MACSEC_SA_STATS_ATTR_UNSPEC,
+ MACSEC_SA_STATS_ATTR_IN_PKTS_OK,
+ MACSEC_SA_STATS_ATTR_IN_PKTS_INVALID,
+ MACSEC_SA_STATS_ATTR_IN_PKTS_NOT_VALID,
+ MACSEC_SA_STATS_ATTR_IN_PKTS_NOT_USING_SA,
+ MACSEC_SA_STATS_ATTR_IN_PKTS_UNUSED_SA,
+ MACSEC_SA_STATS_ATTR_OUT_PKTS_PROTECTED,
+ MACSEC_SA_STATS_ATTR_OUT_PKTS_ENCRYPTED,
+ __MACSEC_SA_STATS_ATTR_END,
+ NUM_MACSEC_SA_STATS_ATTR = __MACSEC_SA_STATS_ATTR_END,
+ MACSEC_SA_STATS_ATTR_MAX = __MACSEC_SA_STATS_ATTR_END - 1,
+};
+
+/* u64 per-TXSC stats */
+enum macsec_txsc_stats_attr {
+ MACSEC_TXSC_STATS_ATTR_UNSPEC,
+ MACSEC_TXSC_STATS_ATTR_OUT_PKTS_PROTECTED,
+ MACSEC_TXSC_STATS_ATTR_OUT_PKTS_ENCRYPTED,
+ MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_PROTECTED,
+ MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_ENCRYPTED,
+ __MACSEC_TXSC_STATS_ATTR_END,
+ NUM_MACSEC_TXSC_STATS_ATTR = __MACSEC_TXSC_STATS_ATTR_END,
+ MACSEC_TXSC_STATS_ATTR_MAX = __MACSEC_TXSC_STATS_ATTR_END - 1,
+};
+
+/* u64 per-SecY stats */
+enum macsec_secy_stats_attr {
+ MACSEC_SECY_STATS_ATTR_UNSPEC,
+ MACSEC_SECY_STATS_ATTR_OUT_PKTS_UNTAGGED,
+ MACSEC_SECY_STATS_ATTR_IN_PKTS_UNTAGGED,
+ MACSEC_SECY_STATS_ATTR_OUT_PKTS_TOO_LONG,
+ MACSEC_SECY_STATS_ATTR_IN_PKTS_NO_TAG,
+ MACSEC_SECY_STATS_ATTR_IN_PKTS_BAD_TAG,
+ MACSEC_SECY_STATS_ATTR_IN_PKTS_UNKNOWN_SCI,
+ MACSEC_SECY_STATS_ATTR_IN_PKTS_NO_SCI,
+ MACSEC_SECY_STATS_ATTR_IN_PKTS_OVERRUN,
+ __MACSEC_SECY_STATS_ATTR_END,
+ NUM_MACSEC_SECY_STATS_ATTR = __MACSEC_SECY_STATS_ATTR_END,
+ MACSEC_SECY_STATS_ATTR_MAX = __MACSEC_SECY_STATS_ATTR_END - 1,
+};
+
+#endif /* _UAPI_MACSEC_H */
diff --git a/include/uapi/linux/netconf.h b/include/uapi/linux/netconf.h
index 23cbd34..45dfad5 100644
--- a/include/uapi/linux/netconf.h
+++ b/include/uapi/linux/netconf.h
@@ -19,6 +19,7 @@
__NETCONFA_MAX
};
#define NETCONFA_MAX (__NETCONFA_MAX - 1)
+#define NETCONFA_ALL -1
#define NETCONFA_IFINDEX_ALL -1
#define NETCONFA_IFINDEX_DEFAULT -2
diff --git a/include/uapi/linux/pkt_cls.h b/include/uapi/linux/pkt_cls.h
index 9874f568..c43c5f7 100644
--- a/include/uapi/linux/pkt_cls.h
+++ b/include/uapi/linux/pkt_cls.h
@@ -417,6 +417,8 @@
TCA_FLOWER_KEY_TCP_DST, /* be16 */
TCA_FLOWER_KEY_UDP_SRC, /* be16 */
TCA_FLOWER_KEY_UDP_DST, /* be16 */
+
+ TCA_FLOWER_FLAGS,
__TCA_FLOWER_MAX,
};
diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h
index fe95446..53e8e3f 100644
--- a/include/uapi/linux/tcp.h
+++ b/include/uapi/linux/tcp.h
@@ -199,6 +199,8 @@
__u32 tcpi_notsent_bytes;
__u32 tcpi_min_rtt;
+ __u32 tcpi_data_segs_in; /* RFC4898 tcpEStatsDataSegsIn */
+ __u32 tcpi_data_segs_out; /* RFC4898 tcpEStatsDataSegsOut */
};
/* for TCP_MD5SIG socket option */
diff --git a/include/xen/interface/io/netif.h b/include/xen/interface/io/netif.h
index 252ffd4..4f20dbc 100644
--- a/include/xen/interface/io/netif.h
+++ b/include/xen/interface/io/netif.h
@@ -1,16 +1,34 @@
/******************************************************************************
- * netif.h
+ * xen_netif.h
*
* Unified network-device I/O interface for Xen guest OSes.
*
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
* Copyright (c) 2003-2004, Keir Fraser
*/
-#ifndef __XEN_PUBLIC_IO_NETIF_H__
-#define __XEN_PUBLIC_IO_NETIF_H__
+#ifndef __XEN_PUBLIC_IO_XEN_NETIF_H__
+#define __XEN_PUBLIC_IO_XEN_NETIF_H__
-#include <xen/interface/io/ring.h>
-#include <xen/interface/grant_table.h>
+#include "ring.h"
+#include "../grant_table.h"
/*
* Older implementation of Xen network frontend / backend has an
@@ -38,10 +56,10 @@
* that it cannot safely queue packets (as it may not be kicked to send them).
*/
- /*
+/*
* "feature-split-event-channels" is introduced to separate guest TX
- * and RX notificaion. Backend either doesn't support this feature or
- * advertise it via xenstore as 0 (disabled) or 1 (enabled).
+ * and RX notification. Backend either doesn't support this feature or
+ * advertises it via xenstore as 0 (disabled) or 1 (enabled).
*
* To make use of this feature, frontend should allocate two event
* channels for TX and RX, advertise them to backend as
@@ -118,151 +136,804 @@
*/
/*
- * This is the 'wire' format for packets:
- * Request 1: xen_netif_tx_request -- XEN_NETTXF_* (any flags)
- * [Request 2: xen_netif_extra_info] (only if request 1 has XEN_NETTXF_extra_info)
- * [Request 3: xen_netif_extra_info] (only if request 2 has XEN_NETIF_EXTRA_MORE)
- * Request 4: xen_netif_tx_request -- XEN_NETTXF_more_data
- * Request 5: xen_netif_tx_request -- XEN_NETTXF_more_data
+ * "feature-multicast-control" and "feature-dynamic-multicast-control"
+ * advertise the capability to filter ethernet multicast packets in the
+ * backend. If the frontend wishes to take advantage of this feature then
+ * it may set "request-multicast-control". If the backend only advertises
+ * "feature-multicast-control" then "request-multicast-control" must be set
+ * before the frontend moves into the connected state. The backend will
+ * sample the value on this state transition and any subsequent change in
+ * value will have no effect. However, if the backend also advertises
+ * "feature-dynamic-multicast-control" then "request-multicast-control"
+ * may be set by the frontend at any time. In this case, the backend will
+ * watch the value and re-sample on watch events.
+ *
+ * If the sampled value of "request-multicast-control" is set then the
+ * backend transmit side should no longer flood multicast packets to the
+ * frontend, it should instead drop any multicast packet that does not
+ * match in a filter list.
+ * The list is amended by the frontend by sending dummy transmit requests
+ * containing XEN_NETIF_EXTRA_TYPE_MCAST_{ADD,DEL} extra-info fragments as
+ * specified below.
+ * Note that the filter list may be amended even if the sampled value of
+ * "request-multicast-control" is not set, however the filter should only
+ * be applied if it is set.
+ */
+
+/*
+ * Control ring
+ * ============
+ *
+ * Some features, such as hashing (detailed below), require a
+ * significant amount of out-of-band data to be passed from frontend to
+ * backend. Use of xenstore is not suitable for large quantities of data
+ * because of quota limitations and so a dedicated 'control ring' is used.
+ * The ability of the backend to use a control ring is advertised by
+ * setting:
+ *
+ * /local/domain/X/backend/<domid>/<vif>/feature-ctrl-ring = "1"
+ *
+ * The frontend provides a control ring to the backend by setting:
+ *
+ * /local/domain/<domid>/device/vif/<vif>/ctrl-ring-ref = <gref>
+ * /local/domain/<domid>/device/vif/<vif>/event-channel-ctrl = <port>
+ *
+ * where <gref> is the grant reference of the shared page used to
+ * implement the control ring and <port> is an event channel to be used
+ * as a mailbox interrupt. These keys must be set before the frontend
+ * moves into the connected state.
+ *
+ * The control ring uses a fixed request/response message size and is
+ * balanced (i.e. one request to one response), so operationally it is much
+ * the same as a transmit or receive ring.
+ * Note that there is no requirement that responses are issued in the same
+ * order as requests.
+ */
+
+/*
+ * Hash types
+ * ==========
+ *
+ * For the purposes of the definitions below, 'Packet[]' is an array of
+ * octets containing an IP packet without options, 'Array[X..Y]' means a
+ * sub-array of 'Array' containing bytes X thru Y inclusive, and '+' is
+ * used to indicate concatenation of arrays.
+ */
+
+/*
+ * A hash calculated over an IP version 4 header as follows:
+ *
+ * Buffer[0..8] = Packet[12..15] (source address) +
+ * Packet[16..19] (destination address)
+ *
+ * Result = Hash(Buffer, 8)
+ */
+#define _XEN_NETIF_CTRL_HASH_TYPE_IPV4 0
+#define XEN_NETIF_CTRL_HASH_TYPE_IPV4 \
+ (1 << _XEN_NETIF_CTRL_HASH_TYPE_IPV4)
+
+/*
+ * A hash calculated over an IP version 4 header and TCP header as
+ * follows:
+ *
+ * Buffer[0..12] = Packet[12..15] (source address) +
+ * Packet[16..19] (destination address) +
+ * Packet[20..21] (source port) +
+ * Packet[22..23] (destination port)
+ *
+ * Result = Hash(Buffer, 12)
+ */
+#define _XEN_NETIF_CTRL_HASH_TYPE_IPV4_TCP 1
+#define XEN_NETIF_CTRL_HASH_TYPE_IPV4_TCP \
+ (1 << _XEN_NETIF_CTRL_HASH_TYPE_IPV4_TCP)
+
+/*
+ * A hash calculated over an IP version 6 header as follows:
+ *
+ * Buffer[0..32] = Packet[8..23] (source address ) +
+ * Packet[24..39] (destination address)
+ *
+ * Result = Hash(Buffer, 32)
+ */
+#define _XEN_NETIF_CTRL_HASH_TYPE_IPV6 2
+#define XEN_NETIF_CTRL_HASH_TYPE_IPV6 \
+ (1 << _XEN_NETIF_CTRL_HASH_TYPE_IPV6)
+
+/*
+ * A hash calculated over an IP version 6 header and TCP header as
+ * follows:
+ *
+ * Buffer[0..36] = Packet[8..23] (source address) +
+ * Packet[24..39] (destination address) +
+ * Packet[40..41] (source port) +
+ * Packet[42..43] (destination port)
+ *
+ * Result = Hash(Buffer, 36)
+ */
+#define _XEN_NETIF_CTRL_HASH_TYPE_IPV6_TCP 3
+#define XEN_NETIF_CTRL_HASH_TYPE_IPV6_TCP \
+ (1 << _XEN_NETIF_CTRL_HASH_TYPE_IPV6_TCP)
+
+/*
+ * Hash algorithms
+ * ===============
+ */
+
+#define XEN_NETIF_CTRL_HASH_ALGORITHM_NONE 0
+
+/*
+ * Toeplitz hash:
+ */
+
+#define XEN_NETIF_CTRL_HASH_ALGORITHM_TOEPLITZ 1
+
+/*
+ * This algorithm uses a 'key' as well as the data buffer itself.
+ * (Buffer[] and Key[] are treated as shift-registers where the MSB of
+ * Buffer/Key[0] is considered 'left-most' and the LSB of Buffer/Key[N-1]
+ * is the 'right-most').
+ *
+ * Value = 0
+ * For number of bits in Buffer[]
+ * If (left-most bit of Buffer[] is 1)
+ * Value ^= left-most 32 bits of Key[]
+ * Key[] << 1
+ * Buffer[] << 1
+ *
+ * The code below is provided for convenience where an operating system
+ * does not already provide an implementation.
+ */
+#ifdef XEN_NETIF_DEFINE_TOEPLITZ
+static uint32_t xen_netif_toeplitz_hash(const uint8_t *key,
+ unsigned int keylen,
+ const uint8_t *buf, unsigned int buflen)
+{
+ unsigned int keyi, bufi;
+ uint64_t prefix = 0;
+ uint64_t hash = 0;
+
+ /* Pre-load prefix with the first 8 bytes of the key */
+ for (keyi = 0; keyi < 8; keyi++) {
+ prefix <<= 8;
+ prefix |= (keyi < keylen) ? key[keyi] : 0;
+ }
+
+ for (bufi = 0; bufi < buflen; bufi++) {
+ uint8_t byte = buf[bufi];
+ unsigned int bit;
+
+ for (bit = 0; bit < 8; bit++) {
+ if (byte & 0x80)
+ hash ^= prefix;
+ prefix <<= 1;
+ byte <<= 1;
+ }
+
+ /*
+ * 'prefix' has now been left-shifted by 8, so
+ * OR in the next byte.
+ */
+ prefix |= (keyi < keylen) ? key[keyi] : 0;
+ keyi++;
+ }
+
+ /* The valid part of the hash is in the upper 32 bits. */
+ return hash >> 32;
+}
+#endif /* XEN_NETIF_DEFINE_TOEPLITZ */
+
+/*
+ * Control requests (struct xen_netif_ctrl_request)
+ * ================================================
+ *
+ * All requests have the following format:
+ *
+ * 0 1 2 3 4 5 6 7 octet
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ * | id | type | data[0] |
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ * | data[1] | data[2] |
+ * +-----+-----+-----+-----+-----------------------+
+ *
+ * id: the request identifier, echoed in response.
+ * type: the type of request (see below)
+ * data[]: any data associated with the request (determined by type)
+ */
+
+struct xen_netif_ctrl_request {
+ uint16_t id;
+ uint16_t type;
+
+#define XEN_NETIF_CTRL_TYPE_INVALID 0
+#define XEN_NETIF_CTRL_TYPE_GET_HASH_FLAGS 1
+#define XEN_NETIF_CTRL_TYPE_SET_HASH_FLAGS 2
+#define XEN_NETIF_CTRL_TYPE_SET_HASH_KEY 3
+#define XEN_NETIF_CTRL_TYPE_GET_HASH_MAPPING_SIZE 4
+#define XEN_NETIF_CTRL_TYPE_SET_HASH_MAPPING_SIZE 5
+#define XEN_NETIF_CTRL_TYPE_SET_HASH_MAPPING 6
+#define XEN_NETIF_CTRL_TYPE_SET_HASH_ALGORITHM 7
+
+ uint32_t data[3];
+};
+
+/*
+ * Control responses (struct xen_netif_ctrl_response)
+ * ==================================================
+ *
+ * All responses have the following format:
+ *
+ * 0 1 2 3 4 5 6 7 octet
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ * | id | type | status |
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ * | data |
+ * +-----+-----+-----+-----+
+ *
+ * id: the corresponding request identifier
+ * type: the type of the corresponding request
+ * status: the status of request processing
+ * data: any data associated with the response (determined by type and
+ * status)
+ */
+
+struct xen_netif_ctrl_response {
+ uint16_t id;
+ uint16_t type;
+ uint32_t status;
+
+#define XEN_NETIF_CTRL_STATUS_SUCCESS 0
+#define XEN_NETIF_CTRL_STATUS_NOT_SUPPORTED 1
+#define XEN_NETIF_CTRL_STATUS_INVALID_PARAMETER 2
+#define XEN_NETIF_CTRL_STATUS_BUFFER_OVERFLOW 3
+
+ uint32_t data;
+};
+
+/*
+ * Control messages
+ * ================
+ *
+ * XEN_NETIF_CTRL_TYPE_SET_HASH_ALGORITHM
+ * --------------------------------------
+ *
+ * This is sent by the frontend to set the desired hash algorithm.
+ *
+ * Request:
+ *
+ * type = XEN_NETIF_CTRL_TYPE_SET_HASH_ALGORITHM
+ * data[0] = a XEN_NETIF_CTRL_HASH_ALGORITHM_* value
+ * data[1] = 0
+ * data[2] = 0
+ *
+ * Response:
+ *
+ * status = XEN_NETIF_CTRL_STATUS_NOT_SUPPORTED - Operation not
+ * supported
+ * XEN_NETIF_CTRL_STATUS_INVALID_PARAMETER - The algorithm is not
+ * supported
+ * XEN_NETIF_CTRL_STATUS_SUCCESS - Operation successful
+ *
+ * NOTE: Setting data[0] to XEN_NETIF_CTRL_HASH_ALGORITHM_NONE disables
+ * hashing and the backend is free to choose how it steers packets
+ * to queues (which is the default behaviour).
+ *
+ * XEN_NETIF_CTRL_TYPE_GET_HASH_FLAGS
+ * ----------------------------------
+ *
+ * This is sent by the frontend to query the types of hash supported by
+ * the backend.
+ *
+ * Request:
+ *
+ * type = XEN_NETIF_CTRL_TYPE_GET_HASH_FLAGS
+ * data[0] = 0
+ * data[1] = 0
+ * data[2] = 0
+ *
+ * Response:
+ *
+ * status = XEN_NETIF_CTRL_STATUS_NOT_SUPPORTED - Operation not supported
+ * XEN_NETIF_CTRL_STATUS_SUCCESS - Operation successful
+ * data = supported hash types (if operation was successful)
+ *
+ * NOTE: A valid hash algorithm must be selected before this operation can
+ * succeed.
+ *
+ * XEN_NETIF_CTRL_TYPE_SET_HASH_FLAGS
+ * ----------------------------------
+ *
+ * This is sent by the frontend to set the types of hash that the backend
+ * should calculate. (See above for hash type definitions).
+ * Note that the 'maximal' type of hash should always be chosen. For
+ * example, if the frontend sets both IPV4 and IPV4_TCP hash types then
+ * the latter hash type should be calculated for any TCP packet and the
+ * former only calculated for non-TCP packets.
+ *
+ * Request:
+ *
+ * type = XEN_NETIF_CTRL_TYPE_SET_HASH_FLAGS
+ * data[0] = bitwise OR of XEN_NETIF_CTRL_HASH_TYPE_* values
+ * data[1] = 0
+ * data[2] = 0
+ *
+ * Response:
+ *
+ * status = XEN_NETIF_CTRL_STATUS_NOT_SUPPORTED - Operation not
+ * supported
+ * XEN_NETIF_CTRL_STATUS_INVALID_PARAMETER - One or more flag
+ * value is invalid or
+ * unsupported
+ * XEN_NETIF_CTRL_STATUS_SUCCESS - Operation successful
+ * data = 0
+ *
+ * NOTE: A valid hash algorithm must be selected before this operation can
+ * succeed.
+ * Also, setting data[0] to zero disables hashing and the backend
+ * is free to choose how it steers packets to queues.
+ *
+ * XEN_NETIF_CTRL_TYPE_SET_HASH_KEY
+ * --------------------------------
+ *
+ * This is sent by the frontend to set the key of the hash if the algorithm
+ * requires it. (See hash algorithms above).
+ *
+ * Request:
+ *
+ * type = XEN_NETIF_CTRL_TYPE_SET_HASH_KEY
+ * data[0] = grant reference of page containing the key (assumed to
+ * start at beginning of grant)
+ * data[1] = size of key in octets
+ * data[2] = 0
+ *
+ * Response:
+ *
+ * status = XEN_NETIF_CTRL_STATUS_NOT_SUPPORTED - Operation not
+ * supported
+ * XEN_NETIF_CTRL_STATUS_INVALID_PARAMETER - Key size is invalid
+ * XEN_NETIF_CTRL_STATUS_BUFFER_OVERFLOW - Key size is larger
+ * than the backend
+ * supports
+ * XEN_NETIF_CTRL_STATUS_SUCCESS - Operation successful
+ * data = 0
+ *
+ * NOTE: Any key octets not specified are assumed to be zero (the key
+ * is assumed to be empty by default) and specifying a new key
+ * invalidates any previous key, hence specifying a key size of
+ * zero will clear the key (which ensures that the calculated hash
+ * will always be zero).
+ * The maximum size of key is algorithm and backend specific, but
+ * is also limited by the single grant reference.
+ * The grant reference may be read-only and must remain valid until
+ * the response has been processed.
+ *
+ * XEN_NETIF_CTRL_TYPE_GET_HASH_MAPPING_SIZE
+ * -----------------------------------------
+ *
+ * This is sent by the frontend to query the maximum size of mapping
+ * table supported by the backend. The size is specified in terms of
+ * table entries.
+ *
+ * Request:
+ *
+ * type = XEN_NETIF_CTRL_TYPE_GET_HASH_MAPPING_SIZE
+ * data[0] = 0
+ * data[1] = 0
+ * data[2] = 0
+ *
+ * Response:
+ *
+ * status = XEN_NETIF_CTRL_STATUS_NOT_SUPPORTED - Operation not supported
+ * XEN_NETIF_CTRL_STATUS_SUCCESS - Operation successful
+ * data = maximum number of entries allowed in the mapping table
+ * (if operation was successful) or zero if a mapping table is
+ * not supported (i.e. hash mapping is done only by modular
+ * arithmetic).
+ *
+ * XEN_NETIF_CTRL_TYPE_SET_HASH_MAPPING_SIZE
+ * -------------------------------------
+ *
+ * This is sent by the frontend to set the actual size of the mapping
+ * table to be used by the backend. The size is specified in terms of
+ * table entries.
+ * Any previous table is invalidated by this message and any new table
+ * is assumed to be zero filled.
+ *
+ * Request:
+ *
+ * type = XEN_NETIF_CTRL_TYPE_SET_HASH_MAPPING_SIZE
+ * data[0] = number of entries in mapping table
+ * data[1] = 0
+ * data[2] = 0
+ *
+ * Response:
+ *
+ * status = XEN_NETIF_CTRL_STATUS_NOT_SUPPORTED - Operation not
+ * supported
+ * XEN_NETIF_CTRL_STATUS_INVALID_PARAMETER - Table size is invalid
+ * XEN_NETIF_CTRL_STATUS_SUCCESS - Operation successful
+ * data = 0
+ *
+ * NOTE: Setting data[0] to 0 means that hash mapping should be done
+ * using modular arithmetic.
+ *
+ * XEN_NETIF_CTRL_TYPE_SET_HASH_MAPPING
+ * ------------------------------------
+ *
+ * This is sent by the frontend to set the content of the table mapping
+ * hash value to queue number. The backend should calculate the hash from
+ * the packet header, use it as an index into the table (modulo the size
+ * of the table) and then steer the packet to the queue number found at
+ * that index.
+ *
+ * Request:
+ *
+ * type = XEN_NETIF_CTRL_TYPE_SET_HASH_MAPPING
+ * data[0] = grant reference of page containing the mapping (sub-)table
+ * (assumed to start at beginning of grant)
+ * data[1] = size of (sub-)table in entries
+ * data[2] = offset, in entries, of sub-table within overall table
+ *
+ * Response:
+ *
+ * status = XEN_NETIF_CTRL_STATUS_NOT_SUPPORTED - Operation not
+ * supported
+ * XEN_NETIF_CTRL_STATUS_INVALID_PARAMETER - Table size or content
+ * is invalid
+ * XEN_NETIF_CTRL_STATUS_BUFFER_OVERFLOW - Table size is larger
+ * than the backend
+ * supports
+ * XEN_NETIF_CTRL_STATUS_SUCCESS - Operation successful
+ * data = 0
+ *
+ * NOTE: The overall table has the following format:
+ *
+ * 0 1 2 3 4 5 6 7 octet
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ * | mapping[0] | mapping[1] |
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ * | . |
+ * | . |
+ * | . |
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ * | mapping[N-2] | mapping[N-1] |
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ *
+ * where N is specified by a XEN_NETIF_CTRL_TYPE_SET_HASH_MAPPING_SIZE
+ * message and each mapping must specifies a queue between 0 and
+ * "multi-queue-num-queues" (see above).
+ * The backend may support a mapping table larger than can be
+ * mapped by a single grant reference. Thus sub-tables within a
+ * larger table can be individually set by sending multiple messages
+ * with differing offset values. Specifying a new sub-table does not
+ * invalidate any table data outside that range.
+ * The grant reference may be read-only and must remain valid until
+ * the response has been processed.
+ */
+
+DEFINE_RING_TYPES(xen_netif_ctrl,
+ struct xen_netif_ctrl_request,
+ struct xen_netif_ctrl_response);
+
+/*
+ * Guest transmit
+ * ==============
+ *
+ * This is the 'wire' format for transmit (frontend -> backend) packets:
+ *
+ * Fragment 1: xen_netif_tx_request_t - flags = XEN_NETTXF_*
+ * size = total packet size
+ * [Extra 1: xen_netif_extra_info_t] - (only if fragment 1 flags include
+ * XEN_NETTXF_extra_info)
* ...
- * Request N: xen_netif_tx_request -- 0
+ * [Extra N: xen_netif_extra_info_t] - (only if extra N-1 flags include
+ * XEN_NETIF_EXTRA_MORE)
+ * ...
+ * Fragment N: xen_netif_tx_request_t - (only if fragment N-1 flags include
+ * XEN_NETTXF_more_data - flags on preceding
+ * extras are not relevant here)
+ * flags = 0
+ * size = fragment size
+ *
+ * NOTE:
+ *
+ * This format slightly is different from that used for receive
+ * (backend -> frontend) packets. Specifically, in a multi-fragment
+ * packet the actual size of fragment 1 can only be determined by
+ * subtracting the sizes of fragments 2..N from the total packet size.
+ *
+ * Ring slot size is 12 octets, however not all request/response
+ * structs use the full size.
+ *
+ * tx request data (xen_netif_tx_request_t)
+ * ------------------------------------
+ *
+ * 0 1 2 3 4 5 6 7 octet
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ * | grant ref | offset | flags |
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ * | id | size |
+ * +-----+-----+-----+-----+
+ *
+ * grant ref: Reference to buffer page.
+ * offset: Offset within buffer page.
+ * flags: XEN_NETTXF_*.
+ * id: request identifier, echoed in response.
+ * size: packet size in bytes.
+ *
+ * tx response (xen_netif_tx_response_t)
+ * ---------------------------------
+ *
+ * 0 1 2 3 4 5 6 7 octet
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ * | id | status | unused |
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ * | unused |
+ * +-----+-----+-----+-----+
+ *
+ * id: reflects id in transmit request
+ * status: XEN_NETIF_RSP_*
+ *
+ * Guest receive
+ * =============
+ *
+ * This is the 'wire' format for receive (backend -> frontend) packets:
+ *
+ * Fragment 1: xen_netif_rx_request_t - flags = XEN_NETRXF_*
+ * size = fragment size
+ * [Extra 1: xen_netif_extra_info_t] - (only if fragment 1 flags include
+ * XEN_NETRXF_extra_info)
+ * ...
+ * [Extra N: xen_netif_extra_info_t] - (only if extra N-1 flags include
+ * XEN_NETIF_EXTRA_MORE)
+ * ...
+ * Fragment N: xen_netif_rx_request_t - (only if fragment N-1 flags include
+ * XEN_NETRXF_more_data - flags on preceding
+ * extras are not relevant here)
+ * flags = 0
+ * size = fragment size
+ *
+ * NOTE:
+ *
+ * This format slightly is different from that used for transmit
+ * (frontend -> backend) packets. Specifically, in a multi-fragment
+ * packet the size of the packet can only be determined by summing the
+ * sizes of fragments 1..N.
+ *
+ * Ring slot size is 8 octets.
+ *
+ * rx request (xen_netif_rx_request_t)
+ * -------------------------------
+ *
+ * 0 1 2 3 4 5 6 7 octet
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ * | id | pad | gref |
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ *
+ * id: request identifier, echoed in response.
+ * gref: reference to incoming granted frame.
+ *
+ * rx response (xen_netif_rx_response_t)
+ * ---------------------------------
+ *
+ * 0 1 2 3 4 5 6 7 octet
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ * | id | offset | flags | status |
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ *
+ * id: reflects id in receive request
+ * offset: offset in page of start of received packet
+ * flags: XEN_NETRXF_*
+ * status: -ve: XEN_NETIF_RSP_*; +ve: Rx'ed pkt size.
+ *
+ * NOTE: Historically, to support GSO on the frontend receive side, Linux
+ * netfront does not make use of the rx response id (because, as
+ * described below, extra info structures overlay the id field).
+ * Instead it assumes that responses always appear in the same ring
+ * slot as their corresponding request. Thus, to maintain
+ * compatibility, backends must make sure this is the case.
+ *
+ * Extra Info
+ * ==========
+ *
+ * Can be present if initial request or response has NET{T,R}XF_extra_info,
+ * or previous extra request has XEN_NETIF_EXTRA_MORE.
+ *
+ * The struct therefore needs to fit into either a tx or rx slot and
+ * is therefore limited to 8 octets.
+ *
+ * NOTE: Because extra info data overlays the usual request/response
+ * structures, there is no id information in the opposite direction.
+ * So, if an extra info overlays an rx response the frontend can
+ * assume that it is in the same ring slot as the request that was
+ * consumed to make the slot available, and the backend must ensure
+ * this assumption is true.
+ *
+ * extra info (xen_netif_extra_info_t)
+ * -------------------------------
+ *
+ * General format:
+ *
+ * 0 1 2 3 4 5 6 7 octet
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ * |type |flags| type specific data |
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ * | padding for tx |
+ * +-----+-----+-----+-----+
+ *
+ * type: XEN_NETIF_EXTRA_TYPE_*
+ * flags: XEN_NETIF_EXTRA_FLAG_*
+ * padding for tx: present only in the tx case due to 8 octet limit
+ * from rx case. Not shown in type specific entries
+ * below.
+ *
+ * XEN_NETIF_EXTRA_TYPE_GSO:
+ *
+ * 0 1 2 3 4 5 6 7 octet
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ * |type |flags| size |type | pad | features |
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ *
+ * type: Must be XEN_NETIF_EXTRA_TYPE_GSO
+ * flags: XEN_NETIF_EXTRA_FLAG_*
+ * size: Maximum payload size of each segment. For example,
+ * for TCP this is just the path MSS.
+ * type: XEN_NETIF_GSO_TYPE_*: This determines the protocol of
+ * the packet and any extra features required to segment the
+ * packet properly.
+ * features: EN_XEN_NETIF_GSO_FEAT_*: This specifies any extra GSO
+ * features required to process this packet, such as ECN
+ * support for TCPv4.
+ *
+ * XEN_NETIF_EXTRA_TYPE_MCAST_{ADD,DEL}:
+ *
+ * 0 1 2 3 4 5 6 7 octet
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ * |type |flags| addr |
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ *
+ * type: Must be XEN_NETIF_EXTRA_TYPE_MCAST_{ADD,DEL}
+ * flags: XEN_NETIF_EXTRA_FLAG_*
+ * addr: address to add/remove
+ *
+ * XEN_NETIF_EXTRA_TYPE_HASH:
+ *
+ * A backend that supports teoplitz hashing is assumed to accept
+ * this type of extra info in transmit packets.
+ * A frontend that enables hashing is assumed to accept
+ * this type of extra info in receive packets.
+ *
+ * 0 1 2 3 4 5 6 7 octet
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ * |type |flags|htype| alg |LSB ---- value ---- MSB|
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ *
+ * type: Must be XEN_NETIF_EXTRA_TYPE_HASH
+ * flags: XEN_NETIF_EXTRA_FLAG_*
+ * htype: Hash type (one of _XEN_NETIF_CTRL_HASH_TYPE_* - see above)
+ * alg: The algorithm used to calculate the hash (one of
+ * XEN_NETIF_CTRL_HASH_TYPE_ALGORITHM_* - see above)
+ * value: Hash value
*/
/* Protocol checksum field is blank in the packet (hardware offload)? */
-#define _XEN_NETTXF_csum_blank (0)
-#define XEN_NETTXF_csum_blank (1U<<_XEN_NETTXF_csum_blank)
+#define _XEN_NETTXF_csum_blank (0)
+#define XEN_NETTXF_csum_blank (1U<<_XEN_NETTXF_csum_blank)
/* Packet data has been validated against protocol checksum. */
-#define _XEN_NETTXF_data_validated (1)
-#define XEN_NETTXF_data_validated (1U<<_XEN_NETTXF_data_validated)
+#define _XEN_NETTXF_data_validated (1)
+#define XEN_NETTXF_data_validated (1U<<_XEN_NETTXF_data_validated)
/* Packet continues in the next request descriptor. */
-#define _XEN_NETTXF_more_data (2)
-#define XEN_NETTXF_more_data (1U<<_XEN_NETTXF_more_data)
+#define _XEN_NETTXF_more_data (2)
+#define XEN_NETTXF_more_data (1U<<_XEN_NETTXF_more_data)
/* Packet to be followed by extra descriptor(s). */
-#define _XEN_NETTXF_extra_info (3)
-#define XEN_NETTXF_extra_info (1U<<_XEN_NETTXF_extra_info)
+#define _XEN_NETTXF_extra_info (3)
+#define XEN_NETTXF_extra_info (1U<<_XEN_NETTXF_extra_info)
#define XEN_NETIF_MAX_TX_SIZE 0xFFFF
struct xen_netif_tx_request {
- grant_ref_t gref; /* Reference to buffer page */
- uint16_t offset; /* Offset within buffer page */
- uint16_t flags; /* XEN_NETTXF_* */
- uint16_t id; /* Echoed in response message. */
- uint16_t size; /* Packet size in bytes. */
+ grant_ref_t gref;
+ uint16_t offset;
+ uint16_t flags;
+ uint16_t id;
+ uint16_t size;
};
/* Types of xen_netif_extra_info descriptors. */
-#define XEN_NETIF_EXTRA_TYPE_NONE (0) /* Never used - invalid */
-#define XEN_NETIF_EXTRA_TYPE_GSO (1) /* u.gso */
-#define XEN_NETIF_EXTRA_TYPE_MCAST_ADD (2) /* u.mcast */
-#define XEN_NETIF_EXTRA_TYPE_MCAST_DEL (3) /* u.mcast */
-#define XEN_NETIF_EXTRA_TYPE_MAX (4)
+#define XEN_NETIF_EXTRA_TYPE_NONE (0) /* Never used - invalid */
+#define XEN_NETIF_EXTRA_TYPE_GSO (1) /* u.gso */
+#define XEN_NETIF_EXTRA_TYPE_MCAST_ADD (2) /* u.mcast */
+#define XEN_NETIF_EXTRA_TYPE_MCAST_DEL (3) /* u.mcast */
+#define XEN_NETIF_EXTRA_TYPE_HASH (4) /* u.hash */
+#define XEN_NETIF_EXTRA_TYPE_MAX (5)
-/* xen_netif_extra_info flags. */
-#define _XEN_NETIF_EXTRA_FLAG_MORE (0)
-#define XEN_NETIF_EXTRA_FLAG_MORE (1U<<_XEN_NETIF_EXTRA_FLAG_MORE)
+/* xen_netif_extra_info_t flags. */
+#define _XEN_NETIF_EXTRA_FLAG_MORE (0)
+#define XEN_NETIF_EXTRA_FLAG_MORE (1U<<_XEN_NETIF_EXTRA_FLAG_MORE)
/* GSO types */
-#define XEN_NETIF_GSO_TYPE_NONE (0)
-#define XEN_NETIF_GSO_TYPE_TCPV4 (1)
-#define XEN_NETIF_GSO_TYPE_TCPV6 (2)
+#define XEN_NETIF_GSO_TYPE_NONE (0)
+#define XEN_NETIF_GSO_TYPE_TCPV4 (1)
+#define XEN_NETIF_GSO_TYPE_TCPV6 (2)
/*
- * This structure needs to fit within both netif_tx_request and
- * netif_rx_response for compatibility.
+ * This structure needs to fit within both xen_netif_tx_request_t and
+ * xen_netif_rx_response_t for compatibility.
*/
struct xen_netif_extra_info {
- uint8_t type; /* XEN_NETIF_EXTRA_TYPE_* */
- uint8_t flags; /* XEN_NETIF_EXTRA_FLAG_* */
-
+ uint8_t type;
+ uint8_t flags;
union {
struct {
- /*
- * Maximum payload size of each segment. For
- * example, for TCP this is just the path MSS.
- */
uint16_t size;
-
- /*
- * GSO type. This determines the protocol of
- * the packet and any extra features required
- * to segment the packet properly.
- */
- uint8_t type; /* XEN_NETIF_GSO_TYPE_* */
-
- /* Future expansion. */
+ uint8_t type;
uint8_t pad;
-
- /*
- * GSO features. This specifies any extra GSO
- * features required to process this packet,
- * such as ECN support for TCPv4.
- */
- uint16_t features; /* XEN_NETIF_GSO_FEAT_* */
+ uint16_t features;
} gso;
-
struct {
- uint8_t addr[6]; /* Address to add/remove. */
+ uint8_t addr[6];
} mcast;
-
+ struct {
+ uint8_t type;
+ uint8_t algorithm;
+ uint8_t value[4];
+ } hash;
uint16_t pad[3];
} u;
};
struct xen_netif_tx_response {
uint16_t id;
- int16_t status; /* XEN_NETIF_RSP_* */
+ int16_t status;
};
struct xen_netif_rx_request {
- uint16_t id; /* Echoed in response message. */
- grant_ref_t gref; /* Reference to incoming granted frame */
+ uint16_t id; /* Echoed in response message. */
+ uint16_t pad;
+ grant_ref_t gref;
};
/* Packet data has been validated against protocol checksum. */
-#define _XEN_NETRXF_data_validated (0)
-#define XEN_NETRXF_data_validated (1U<<_XEN_NETRXF_data_validated)
+#define _XEN_NETRXF_data_validated (0)
+#define XEN_NETRXF_data_validated (1U<<_XEN_NETRXF_data_validated)
/* Protocol checksum field is blank in the packet (hardware offload)? */
-#define _XEN_NETRXF_csum_blank (1)
-#define XEN_NETRXF_csum_blank (1U<<_XEN_NETRXF_csum_blank)
+#define _XEN_NETRXF_csum_blank (1)
+#define XEN_NETRXF_csum_blank (1U<<_XEN_NETRXF_csum_blank)
/* Packet continues in the next request descriptor. */
-#define _XEN_NETRXF_more_data (2)
-#define XEN_NETRXF_more_data (1U<<_XEN_NETRXF_more_data)
+#define _XEN_NETRXF_more_data (2)
+#define XEN_NETRXF_more_data (1U<<_XEN_NETRXF_more_data)
/* Packet to be followed by extra descriptor(s). */
-#define _XEN_NETRXF_extra_info (3)
-#define XEN_NETRXF_extra_info (1U<<_XEN_NETRXF_extra_info)
+#define _XEN_NETRXF_extra_info (3)
+#define XEN_NETRXF_extra_info (1U<<_XEN_NETRXF_extra_info)
-/* GSO Prefix descriptor. */
-#define _XEN_NETRXF_gso_prefix (4)
-#define XEN_NETRXF_gso_prefix (1U<<_XEN_NETRXF_gso_prefix)
+/* Packet has GSO prefix. Deprecated but included for compatibility */
+#define _XEN_NETRXF_gso_prefix (4)
+#define XEN_NETRXF_gso_prefix (1U<<_XEN_NETRXF_gso_prefix)
struct xen_netif_rx_response {
- uint16_t id;
- uint16_t offset; /* Offset in page of start of received packet */
- uint16_t flags; /* XEN_NETRXF_* */
- int16_t status; /* -ve: BLKIF_RSP_* ; +ve: Rx'ed pkt size. */
+ uint16_t id;
+ uint16_t offset;
+ uint16_t flags;
+ int16_t status;
};
/*
- * Generate netif ring structures and types.
+ * Generate xen_netif ring structures and types.
*/
-DEFINE_RING_TYPES(xen_netif_tx,
- struct xen_netif_tx_request,
+DEFINE_RING_TYPES(xen_netif_tx, struct xen_netif_tx_request,
struct xen_netif_tx_response);
-DEFINE_RING_TYPES(xen_netif_rx,
- struct xen_netif_rx_request,
+DEFINE_RING_TYPES(xen_netif_rx, struct xen_netif_rx_request,
struct xen_netif_rx_response);
-#define XEN_NETIF_RSP_DROPPED -2
-#define XEN_NETIF_RSP_ERROR -1
-#define XEN_NETIF_RSP_OKAY 0
-/* No response: used for auxiliary requests (e.g., xen_netif_extra_info). */
-#define XEN_NETIF_RSP_NULL 1
+#define XEN_NETIF_RSP_DROPPED -2
+#define XEN_NETIF_RSP_ERROR -1
+#define XEN_NETIF_RSP_OKAY 0
+/* No response: used for auxiliary requests (e.g., xen_netif_extra_info_t). */
+#define XEN_NETIF_RSP_NULL 1
#endif
diff --git a/lib/checksum.c b/lib/checksum.c
index 8b39e86..d3ec93f 100644
--- a/lib/checksum.c
+++ b/lib/checksum.c
@@ -191,9 +191,7 @@
}
__wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
- unsigned short len,
- unsigned short proto,
- __wsum sum)
+ __u32 len, __u8 proto, __wsum sum)
{
unsigned long long s = (__force u32)sum;
diff --git a/net/Kconfig b/net/Kconfig
index 10640d5..e134498 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -253,6 +253,9 @@
depends on SMP
default y
+config HWBM
+ bool
+
config SOCK_CGROUP_DATA
bool
default n
diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c
index c22816a..e234490 100644
--- a/net/bridge/br_stp.c
+++ b/net/bridge/br_stp.c
@@ -562,6 +562,14 @@
}
+/* Set time interval that dynamic forwarding entries live
+ * For pure software bridge, allow values outside the 802.1
+ * standard specification for special cases:
+ * 0 - entry never ages (all permanant)
+ * 1 - entry disappears (no persistance)
+ *
+ * Offloaded switch entries maybe more restrictive
+ */
int br_set_ageing_time(struct net_bridge *br, u32 ageing_time)
{
struct switchdev_attr attr = {
@@ -573,9 +581,6 @@
unsigned long t = clock_t_to_jiffies(ageing_time);
int err;
- if (t < BR_MIN_AGEING_TIME || t > BR_MAX_AGEING_TIME)
- return -ERANGE;
-
err = switchdev_port_attr_set(br->dev, &attr);
if (err)
return err;
diff --git a/net/caif/cfpkt_skbuff.c b/net/caif/cfpkt_skbuff.c
index f6c3b21..59ce1fc 100644
--- a/net/caif/cfpkt_skbuff.c
+++ b/net/caif/cfpkt_skbuff.c
@@ -286,7 +286,7 @@
else
skb_trim(skb, len);
- return cfpkt_getlen(pkt);
+ return cfpkt_getlen(pkt);
}
/* Need to expand SKB */
diff --git a/net/core/Makefile b/net/core/Makefile
index 014422e..d6508c2 100644
--- a/net/core/Makefile
+++ b/net/core/Makefile
@@ -25,4 +25,5 @@
obj-$(CONFIG_CGROUP_NET_CLASSID) += netclassid_cgroup.o
obj-$(CONFIG_LWTUNNEL) += lwtunnel.o
obj-$(CONFIG_DST_CACHE) += dst_cache.o
+obj-$(CONFIG_HWBM) += hwbm.o
obj-$(CONFIG_NET_DEVLINK) += devlink.o
diff --git a/net/core/filter.c b/net/core/filter.c
index a66dc03..6fc3893 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -1770,12 +1770,15 @@
return -EPROTO;
if (unlikely(size != sizeof(struct bpf_tunnel_key))) {
switch (size) {
+ case offsetof(struct bpf_tunnel_key, tunnel_label):
+ goto set_compat;
case offsetof(struct bpf_tunnel_key, remote_ipv6[1]):
/* Fixup deprecated structure layouts here, so we have
* a common path later on.
*/
if (ip_tunnel_info_af(info) != AF_INET)
return -EINVAL;
+set_compat:
to = (struct bpf_tunnel_key *)compat;
break;
default:
@@ -1787,11 +1790,13 @@
to->tunnel_tos = info->key.tos;
to->tunnel_ttl = info->key.ttl;
- if (flags & BPF_F_TUNINFO_IPV6)
+ if (flags & BPF_F_TUNINFO_IPV6) {
memcpy(to->remote_ipv6, &info->key.u.ipv6.src,
sizeof(to->remote_ipv6));
- else
+ to->tunnel_label = be32_to_cpu(info->key.label);
+ } else {
to->remote_ipv4 = be32_to_cpu(info->key.u.ipv4.src);
+ }
if (unlikely(size != sizeof(struct bpf_tunnel_key)))
memcpy((void *)(long) r2, to, size);
@@ -1850,6 +1855,7 @@
return -EINVAL;
if (unlikely(size != sizeof(struct bpf_tunnel_key))) {
switch (size) {
+ case offsetof(struct bpf_tunnel_key, tunnel_label):
case offsetof(struct bpf_tunnel_key, remote_ipv6[1]):
/* Fixup deprecated structure layouts here, so we have
* a common path later on.
@@ -1862,6 +1868,8 @@
return -EINVAL;
}
}
+ if (unlikely(!(flags & BPF_F_TUNINFO_IPV6) && from->tunnel_label))
+ return -EINVAL;
skb_dst_drop(skb);
dst_hold((struct dst_entry *) md);
@@ -1882,6 +1890,8 @@
info->mode |= IP_TUNNEL_INFO_IPV6;
memcpy(&info->key.u.ipv6.dst, from->remote_ipv6,
sizeof(from->remote_ipv6));
+ info->key.label = cpu_to_be32(from->tunnel_label) &
+ IPV6_FLOWLABEL_MASK;
} else {
info->key.u.ipv4.dst = cpu_to_be32(from->remote_ipv4);
if (flags & BPF_F_ZERO_CSUM_TX)
diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c
index 7c7b873..a669dea 100644
--- a/net/core/flow_dissector.c
+++ b/net/core/flow_dissector.c
@@ -19,25 +19,12 @@
#include <net/flow_dissector.h>
#include <scsi/fc/fc_fcoe.h>
-static bool dissector_uses_key(const struct flow_dissector *flow_dissector,
- enum flow_dissector_key_id key_id)
-{
- return flow_dissector->used_keys & (1 << key_id);
-}
-
static void dissector_set_key(struct flow_dissector *flow_dissector,
enum flow_dissector_key_id key_id)
{
flow_dissector->used_keys |= (1 << key_id);
}
-static void *skb_flow_dissector_target(struct flow_dissector *flow_dissector,
- enum flow_dissector_key_id key_id,
- void *target_container)
-{
- return ((char *) target_container) + flow_dissector->offset[key_id];
-}
-
void skb_flow_dissector_init(struct flow_dissector *flow_dissector,
const struct flow_dissector_key *key,
unsigned int key_count)
diff --git a/net/core/hwbm.c b/net/core/hwbm.c
new file mode 100644
index 0000000..941c284
--- /dev/null
+++ b/net/core/hwbm.c
@@ -0,0 +1,87 @@
+/* Support for hardware buffer manager.
+ *
+ * Copyright (C) 2016 Marvell
+ *
+ * Gregory CLEMENT <gregory.clement@free-electrons.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/kernel.h>
+#include <linux/printk.h>
+#include <linux/skbuff.h>
+#include <net/hwbm.h>
+
+void hwbm_buf_free(struct hwbm_pool *bm_pool, void *buf)
+{
+ if (likely(bm_pool->frag_size <= PAGE_SIZE))
+ skb_free_frag(buf);
+ else
+ kfree(buf);
+}
+EXPORT_SYMBOL_GPL(hwbm_buf_free);
+
+/* Refill processing for HW buffer management */
+int hwbm_pool_refill(struct hwbm_pool *bm_pool, gfp_t gfp)
+{
+ int frag_size = bm_pool->frag_size;
+ void *buf;
+
+ if (likely(frag_size <= PAGE_SIZE))
+ buf = netdev_alloc_frag(frag_size);
+ else
+ buf = kmalloc(frag_size, gfp);
+
+ if (!buf)
+ return -ENOMEM;
+
+ if (bm_pool->construct)
+ if (bm_pool->construct(bm_pool, buf)) {
+ hwbm_buf_free(bm_pool, buf);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hwbm_pool_refill);
+
+int hwbm_pool_add(struct hwbm_pool *bm_pool, unsigned int buf_num, gfp_t gfp)
+{
+ int err, i;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bm_pool->lock, flags);
+ if (bm_pool->buf_num == bm_pool->size) {
+ pr_warn("pool already filled\n");
+ return bm_pool->buf_num;
+ }
+
+ if (buf_num + bm_pool->buf_num > bm_pool->size) {
+ pr_warn("cannot allocate %d buffers for pool\n",
+ buf_num);
+ return 0;
+ }
+
+ if ((buf_num + bm_pool->buf_num) < bm_pool->buf_num) {
+ pr_warn("Adding %d buffers to the %d current buffers will overflow\n",
+ buf_num, bm_pool->buf_num);
+ return 0;
+ }
+
+ for (i = 0; i < buf_num; i++) {
+ err = hwbm_pool_refill(bm_pool, gfp);
+ if (err < 0)
+ break;
+ }
+
+ /* Update BM driver with number of buffers added to pool */
+ bm_pool->buf_num += i;
+
+ pr_debug("hwpm pool: %d of %d buffers added\n", i, buf_num);
+ spin_unlock_irqrestore(&bm_pool->lock, flags);
+
+ return i;
+}
+EXPORT_SYMBOL_GPL(hwbm_pool_add);
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 51d768e..f044f97 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -801,9 +801,9 @@
if (unlikely(!skb))
return;
- /* if budget is 0 assume netpoll w/ IRQs disabled */
+ /* Zero budget indicate non-NAPI context called us, like netpoll */
if (unlikely(!budget)) {
- dev_consume_skb_irq(skb);
+ dev_consume_skb_any(skb);
return;
}
diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c
index fa4daba..c28c474 100644
--- a/net/dsa/dsa.c
+++ b/net/dsa/dsa.c
@@ -430,24 +430,6 @@
hwmon_device_unregister(ds->hwmon_dev);
#endif
- /* Disable configuration of the CPU and DSA ports */
- for (port = 0; port < DSA_MAX_PORTS; port++) {
- if (!(dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)))
- continue;
-
- port_dn = cd->port_dn[port];
- if (of_phy_is_fixed_link(port_dn)) {
- phydev = of_phy_find_device(port_dn);
- if (phydev) {
- int addr = phydev->mdio.addr;
-
- phy_device_free(phydev);
- of_node_put(port_dn);
- fixed_phy_del(addr);
- }
- }
- }
-
/* Destroy network devices for physical switch ports. */
for (port = 0; port < DSA_MAX_PORTS; port++) {
if (!(ds->phys_port_mask & (1 << port)))
@@ -459,6 +441,19 @@
dsa_slave_destroy(ds->ports[port]);
}
+ /* Remove any fixed link PHYs */
+ for (port = 0; port < DSA_MAX_PORTS; port++) {
+ port_dn = cd->port_dn[port];
+ if (of_phy_is_fixed_link(port_dn)) {
+ phydev = of_phy_find_device(port_dn);
+ if (phydev) {
+ phy_device_free(phydev);
+ of_node_put(port_dn);
+ fixed_phy_unregister(phydev);
+ }
+ }
+ }
+
mdiobus_unregister(ds->slave_mii_bus);
}
@@ -935,6 +930,14 @@
{
int i;
+ dst->master_netdev->dsa_ptr = NULL;
+
+ /* If we used a tagging format that doesn't have an ethertype
+ * field, make sure that all packets from this point get sent
+ * without the tag and go through the regular receive path.
+ */
+ wmb();
+
for (i = 0; i < dst->pd->nr_chips; i++) {
struct dsa_switch *ds = dst->ds[i];
@@ -988,14 +991,6 @@
struct dsa_switch_tree *dst = platform_get_drvdata(pdev);
int i, ret = 0;
- dst->master_netdev->dsa_ptr = NULL;
-
- /* If we used a tagging format that doesn't have an ethertype
- * field, make sure that all packets from this point get sent
- * without the tag and go through the regular receive path.
- */
- wmb();
-
for (i = 0; i < dst->pd->nr_chips; i++) {
struct dsa_switch *ds = dst->ds[i];
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 27bf03d..49056d9 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -896,11 +896,15 @@
static int dsa_slave_fixed_link_update(struct net_device *dev,
struct fixed_phy_status *status)
{
- struct dsa_slave_priv *p = netdev_priv(dev);
- struct dsa_switch *ds = p->parent;
+ struct dsa_slave_priv *p;
+ struct dsa_switch *ds;
- if (ds->drv->fixed_link_update)
- ds->drv->fixed_link_update(ds, p->port, status);
+ if (dev) {
+ p = netdev_priv(dev);
+ ds = p->parent;
+ if (ds->drv->fixed_link_update)
+ ds->drv->fixed_link_update(ds, p->port, status);
+ }
return 0;
}
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index 8c3df2c..e333bc8 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -334,6 +334,9 @@
ASSERT_RTNL();
+ if (in_dev->dead)
+ goto no_promotions;
+
/* 1. Deleting primary ifaddr forces deletion all secondaries
* unless alias promotion is set
**/
@@ -380,6 +383,7 @@
fib_del_ifaddr(ifa, ifa1);
}
+no_promotions:
/* 2. Unlink it */
*ifap = ifa1->ifa_next;
@@ -1753,17 +1757,20 @@
{
int size = NLMSG_ALIGN(sizeof(struct netconfmsg))
+ nla_total_size(4); /* NETCONFA_IFINDEX */
+ bool all = false;
- /* type -1 is used for ALL */
- if (type == -1 || type == NETCONFA_FORWARDING)
+ if (type == NETCONFA_ALL)
+ all = true;
+
+ if (all || type == NETCONFA_FORWARDING)
size += nla_total_size(4);
- if (type == -1 || type == NETCONFA_RP_FILTER)
+ if (all || type == NETCONFA_RP_FILTER)
size += nla_total_size(4);
- if (type == -1 || type == NETCONFA_MC_FORWARDING)
+ if (all || type == NETCONFA_MC_FORWARDING)
size += nla_total_size(4);
- if (type == -1 || type == NETCONFA_PROXY_NEIGH)
+ if (all || type == NETCONFA_PROXY_NEIGH)
size += nla_total_size(4);
- if (type == -1 || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN)
+ if (all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN)
size += nla_total_size(4);
return size;
@@ -1776,36 +1783,39 @@
{
struct nlmsghdr *nlh;
struct netconfmsg *ncm;
+ bool all = false;
nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg),
flags);
if (!nlh)
return -EMSGSIZE;
+ if (type == NETCONFA_ALL)
+ all = true;
+
ncm = nlmsg_data(nlh);
ncm->ncm_family = AF_INET;
if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0)
goto nla_put_failure;
- /* type -1 is used for ALL */
- if ((type == -1 || type == NETCONFA_FORWARDING) &&
+ if ((all || type == NETCONFA_FORWARDING) &&
nla_put_s32(skb, NETCONFA_FORWARDING,
IPV4_DEVCONF(*devconf, FORWARDING)) < 0)
goto nla_put_failure;
- if ((type == -1 || type == NETCONFA_RP_FILTER) &&
+ if ((all || type == NETCONFA_RP_FILTER) &&
nla_put_s32(skb, NETCONFA_RP_FILTER,
IPV4_DEVCONF(*devconf, RP_FILTER)) < 0)
goto nla_put_failure;
- if ((type == -1 || type == NETCONFA_MC_FORWARDING) &&
+ if ((all || type == NETCONFA_MC_FORWARDING) &&
nla_put_s32(skb, NETCONFA_MC_FORWARDING,
IPV4_DEVCONF(*devconf, MC_FORWARDING)) < 0)
goto nla_put_failure;
- if ((type == -1 || type == NETCONFA_PROXY_NEIGH) &&
+ if ((all || type == NETCONFA_PROXY_NEIGH) &&
nla_put_s32(skb, NETCONFA_PROXY_NEIGH,
IPV4_DEVCONF(*devconf, PROXY_ARP)) < 0)
goto nla_put_failure;
- if ((type == -1 || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) &&
+ if ((all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) &&
nla_put_s32(skb, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
IPV4_DEVCONF(*devconf, IGNORE_ROUTES_WITH_LINKDOWN)) < 0)
goto nla_put_failure;
@@ -1893,14 +1903,14 @@
}
err = -ENOBUFS;
- skb = nlmsg_new(inet_netconf_msgsize_devconf(-1), GFP_ATOMIC);
+ skb = nlmsg_new(inet_netconf_msgsize_devconf(NETCONFA_ALL), GFP_ATOMIC);
if (!skb)
goto errout;
err = inet_netconf_fill_devconf(skb, ifindex, devconf,
NETLINK_CB(in_skb).portid,
nlh->nlmsg_seq, RTM_NEWNETCONF, 0,
- -1);
+ NETCONFA_ALL);
if (err < 0) {
/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
WARN_ON(err == -EMSGSIZE);
@@ -1944,7 +1954,7 @@
cb->nlh->nlmsg_seq,
RTM_NEWNETCONF,
NLM_F_MULTI,
- -1) < 0) {
+ NETCONFA_ALL) < 0) {
rcu_read_unlock();
goto done;
}
@@ -1960,7 +1970,7 @@
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq,
RTM_NEWNETCONF, NLM_F_MULTI,
- -1) < 0)
+ NETCONFA_ALL) < 0)
goto done;
else
h++;
@@ -1971,7 +1981,7 @@
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq,
RTM_NEWNETCONF, NLM_F_MULTI,
- -1) < 0)
+ NETCONFA_ALL) < 0)
goto done;
else
h++;
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index 4734475..21add55 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c
@@ -922,6 +922,9 @@
subnet = 1;
}
+ if (in_dev->dead)
+ goto no_promotions;
+
/* Deletion is more complicated than add.
* We should take care of not to delete too much :-)
*
@@ -997,6 +1000,7 @@
}
}
+no_promotions:
if (!(ok & BRD_OK))
fib_magic(RTM_DELROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, prim);
if (subnet && ifa->ifa_prefixlen < 31) {
diff --git a/net/ipv4/fou.c b/net/ipv4/fou.c
index 88dab0c..7804842 100644
--- a/net/ipv4/fou.c
+++ b/net/ipv4/fou.c
@@ -319,8 +319,6 @@
skb_gro_pull(skb, hdrlen);
- flush = 0;
-
for (p = *head; p; p = p->next) {
const struct guehdr *guehdr2;
@@ -352,6 +350,7 @@
goto out_unlock;
pp = ops->callbacks.gro_receive(head, skb);
+ flush = 0;
out_unlock:
rcu_read_unlock();
diff --git a/net/ipv4/gre_offload.c b/net/ipv4/gre_offload.c
index 47f4c54..540866d 100644
--- a/net/ipv4/gre_offload.c
+++ b/net/ipv4/gre_offload.c
@@ -175,8 +175,6 @@
null_compute_pseudo);
}
- flush = 0;
-
for (p = *head; p; p = p->next) {
const struct gre_base_hdr *greh2;
@@ -213,6 +211,7 @@
skb_gro_postpull_rcsum(skb, greh, grehlen);
pp = ptype->callbacks.gro_receive(head, skb);
+ flush = 0;
out_unlock:
rcu_read_unlock();
diff --git a/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c b/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c
index c6eb421..ea91058 100644
--- a/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c
+++ b/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c
@@ -108,10 +108,18 @@
unsigned long event,
void *ptr)
{
- struct net_device *dev = ((struct in_ifaddr *)ptr)->ifa_dev->dev;
+ struct in_device *idev = ((struct in_ifaddr *)ptr)->ifa_dev;
struct netdev_notifier_info info;
- netdev_notifier_info_init(&info, dev);
+ /* The masq_dev_notifier will catch the case of the device going
+ * down. So if the inetdev is dead and being destroyed we have
+ * no work to do. Otherwise this is an individual address removal
+ * and we have to perform the flush.
+ */
+ if (idev->dead)
+ return NOTIFY_DONE;
+
+ netdev_notifier_info_init(&info, idev->dev);
return masq_device_event(this, event, &info);
}
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index a265f00..992b310 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -2715,6 +2715,8 @@
info->tcpi_notsent_bytes = max(0, notsent_bytes);
info->tcpi_min_rtt = tcp_min_rtt(tp);
+ info->tcpi_data_segs_in = tp->data_segs_in;
+ info->tcpi_data_segs_out = tp->data_segs_out;
}
EXPORT_SYMBOL_GPL(tcp_get_info);
diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c
index fdb286d..4fc0061 100644
--- a/net/ipv4/tcp_fastopen.c
+++ b/net/ipv4/tcp_fastopen.c
@@ -140,6 +140,14 @@
return;
skb_dst_drop(skb);
+ /* segs_in has been initialized to 1 in tcp_create_openreq_child().
+ * Hence, reset segs_in to 0 before calling tcp_segs_in()
+ * to avoid double counting. Also, tcp_segs_in() expects
+ * skb->len to include the tcp_hdrlen. Hence, it should
+ * be called before __skb_pull().
+ */
+ tp->segs_in = 0;
+ tcp_segs_in(tp, skb);
__skb_pull(skb, tcp_hdrlen(skb));
skb_set_owner_r(skb, sk);
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 4c8d58d..0b02ef7 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1650,7 +1650,7 @@
sk_incoming_cpu_update(sk);
bh_lock_sock_nested(sk);
- tcp_sk(sk)->segs_in += max_t(u16, 1, skb_shinfo(skb)->gso_segs);
+ tcp_segs_in(tcp_sk(sk), skb);
ret = 0;
if (!sock_owned_by_user(sk)) {
if (!tcp_prequeue(sk, skb))
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index ae90e4b..acb366d 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -812,7 +812,7 @@
int ret = 0;
int state = child->sk_state;
- tcp_sk(child)->segs_in += max_t(u16, 1, skb_shinfo(skb)->gso_segs);
+ tcp_segs_in(tcp_sk(child), skb);
if (!sock_owned_by_user(child)) {
ret = tcp_rcv_state_process(child, skb);
/* Wakeup parent, send SIGIO */
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 7d2c7a4..7d2dc01 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -1003,8 +1003,10 @@
if (likely(tcb->tcp_flags & TCPHDR_ACK))
tcp_event_ack_sent(sk, tcp_skb_pcount(skb));
- if (skb->len != tcp_header_size)
+ if (skb->len != tcp_header_size) {
tcp_event_data_sent(tp, sk);
+ tp->data_segs_out += tcp_skb_pcount(skb);
+ }
if (after(tcb->end_seq, tp->snd_nxt) || tcb->seq == tcb->end_seq)
TCP_ADD_STATS(sock_net(sk), TCP_MIB_OUTSEGS,
diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c
index f5abb1a..8a3405a 100644
--- a/net/ipv4/udp_offload.c
+++ b/net/ipv4/udp_offload.c
@@ -40,13 +40,19 @@
__be16 protocol = skb->protocol;
u16 mac_len = skb->mac_len;
int udp_offset, outer_hlen;
- u32 partial;
+ __wsum partial;
if (unlikely(!pskb_may_pull(skb, tnl_hlen)))
goto out;
- /* adjust partial header checksum to negate old length */
- partial = (__force u32)uh->check + (__force u16)~uh->len;
+ /* Adjust partial header checksum to negate old length.
+ * We cannot rely on the value contained in uh->len as it is
+ * possible that the actual value exceeds the boundaries of the
+ * 16 bit length field due to the header being added outside of an
+ * IP or IPv6 frame that was already limited to 64K - 1.
+ */
+ partial = csum_sub(csum_unfold(uh->check),
+ (__force __wsum)htonl(skb->len));
/* setup inner skb. */
skb->encapsulation = 0;
@@ -119,8 +125,7 @@
if (!need_csum)
continue;
- uh->check = ~csum_fold((__force __wsum)
- ((__force u32)len + partial));
+ uh->check = ~csum_fold(csum_add(partial, (__force __wsum)len));
if (skb->encapsulation || !offload_csum) {
uh->check = gso_make_checksum(skb, ~uh->check);
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 8c0dab2..27aed1a 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -473,18 +473,21 @@
{
int size = NLMSG_ALIGN(sizeof(struct netconfmsg))
+ nla_total_size(4); /* NETCONFA_IFINDEX */
+ bool all = false;
- /* type -1 is used for ALL */
- if (type == -1 || type == NETCONFA_FORWARDING)
+ if (type == NETCONFA_ALL)
+ all = true;
+
+ if (all || type == NETCONFA_FORWARDING)
size += nla_total_size(4);
#ifdef CONFIG_IPV6_MROUTE
- if (type == -1 || type == NETCONFA_MC_FORWARDING)
+ if (all || type == NETCONFA_MC_FORWARDING)
size += nla_total_size(4);
#endif
- if (type == -1 || type == NETCONFA_PROXY_NEIGH)
+ if (all || type == NETCONFA_PROXY_NEIGH)
size += nla_total_size(4);
- if (type == -1 || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN)
+ if (all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN)
size += nla_total_size(4);
return size;
@@ -497,33 +500,36 @@
{
struct nlmsghdr *nlh;
struct netconfmsg *ncm;
+ bool all = false;
nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg),
flags);
if (!nlh)
return -EMSGSIZE;
+ if (type == NETCONFA_ALL)
+ all = true;
+
ncm = nlmsg_data(nlh);
ncm->ncm_family = AF_INET6;
if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0)
goto nla_put_failure;
- /* type -1 is used for ALL */
- if ((type == -1 || type == NETCONFA_FORWARDING) &&
+ if ((all || type == NETCONFA_FORWARDING) &&
nla_put_s32(skb, NETCONFA_FORWARDING, devconf->forwarding) < 0)
goto nla_put_failure;
#ifdef CONFIG_IPV6_MROUTE
- if ((type == -1 || type == NETCONFA_MC_FORWARDING) &&
+ if ((all || type == NETCONFA_MC_FORWARDING) &&
nla_put_s32(skb, NETCONFA_MC_FORWARDING,
devconf->mc_forwarding) < 0)
goto nla_put_failure;
#endif
- if ((type == -1 || type == NETCONFA_PROXY_NEIGH) &&
+ if ((all || type == NETCONFA_PROXY_NEIGH) &&
nla_put_s32(skb, NETCONFA_PROXY_NEIGH, devconf->proxy_ndp) < 0)
goto nla_put_failure;
- if ((type == -1 || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) &&
+ if ((all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) &&
nla_put_s32(skb, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
devconf->ignore_routes_with_linkdown) < 0)
goto nla_put_failure;
@@ -609,14 +615,14 @@
}
err = -ENOBUFS;
- skb = nlmsg_new(inet6_netconf_msgsize_devconf(-1), GFP_ATOMIC);
+ skb = nlmsg_new(inet6_netconf_msgsize_devconf(NETCONFA_ALL), GFP_ATOMIC);
if (!skb)
goto errout;
err = inet6_netconf_fill_devconf(skb, ifindex, devconf,
NETLINK_CB(in_skb).portid,
nlh->nlmsg_seq, RTM_NEWNETCONF, 0,
- -1);
+ NETCONFA_ALL);
if (err < 0) {
/* -EMSGSIZE implies BUG in inet6_netconf_msgsize_devconf() */
WARN_ON(err == -EMSGSIZE);
@@ -660,7 +666,7 @@
cb->nlh->nlmsg_seq,
RTM_NEWNETCONF,
NLM_F_MULTI,
- -1) < 0) {
+ NETCONFA_ALL) < 0) {
rcu_read_unlock();
goto done;
}
@@ -676,7 +682,7 @@
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq,
RTM_NEWNETCONF, NLM_F_MULTI,
- -1) < 0)
+ NETCONFA_ALL) < 0)
goto done;
else
h++;
@@ -687,7 +693,7 @@
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq,
RTM_NEWNETCONF, NLM_F_MULTI,
- -1) < 0)
+ NETCONFA_ALL) < 0)
goto done;
else
h++;
diff --git a/net/ipv6/ip6_checksum.c b/net/ipv6/ip6_checksum.c
index 8f92058..b2025bf 100644
--- a/net/ipv6/ip6_checksum.c
+++ b/net/ipv6/ip6_checksum.c
@@ -6,8 +6,7 @@
#ifndef _HAVE_ARCH_IPV6_CSUM
__sum16 csum_ipv6_magic(const struct in6_addr *saddr,
const struct in6_addr *daddr,
- __u32 len, unsigned short proto,
- __wsum csum)
+ __u32 len, __u8 proto, __wsum csum)
{
int carry;
diff --git a/net/ipv6/ip6_udp_tunnel.c b/net/ipv6/ip6_udp_tunnel.c
index 14dacf1..a752052 100644
--- a/net/ipv6/ip6_udp_tunnel.c
+++ b/net/ipv6/ip6_udp_tunnel.c
@@ -73,8 +73,8 @@
struct sk_buff *skb,
struct net_device *dev, struct in6_addr *saddr,
struct in6_addr *daddr,
- __u8 prio, __u8 ttl, __be16 src_port,
- __be16 dst_port, bool nocheck)
+ __u8 prio, __u8 ttl, __be32 label,
+ __be16 src_port, __be16 dst_port, bool nocheck)
{
struct udphdr *uh;
struct ipv6hdr *ip6h;
@@ -98,7 +98,7 @@
__skb_push(skb, sizeof(*ip6h));
skb_reset_network_header(skb);
ip6h = ipv6_hdr(skb);
- ip6_flow_hdr(ip6h, prio, htonl(0));
+ ip6_flow_hdr(ip6h, prio, label);
ip6h->payload_len = htons(skb->len);
ip6h->nexthdr = IPPROTO_UDP;
ip6h->hop_limit = ttl;
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 33f2820..9c16565 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -1443,7 +1443,7 @@
sk_incoming_cpu_update(sk);
bh_lock_sock_nested(sk);
- tcp_sk(sk)->segs_in += max_t(u16, 1, skb_shinfo(skb)->gso_segs);
+ tcp_segs_in(tcp_sk(sk), skb);
ret = 0;
if (!sock_owned_by_user(sk)) {
if (!tcp_prequeue(sk, skb))
diff --git a/net/nfc/llcp_commands.c b/net/nfc/llcp_commands.c
index 3621a90..3425532 100644
--- a/net/nfc/llcp_commands.c
+++ b/net/nfc/llcp_commands.c
@@ -663,7 +663,7 @@
return -ENOBUFS;
}
- msg_data = kzalloc(len, GFP_KERNEL);
+ msg_data = kmalloc(len, GFP_USER | __GFP_NOWARN);
if (msg_data == NULL)
return -ENOMEM;
@@ -729,7 +729,7 @@
if (local == NULL)
return -ENODEV;
- msg_data = kzalloc(len, GFP_KERNEL);
+ msg_data = kmalloc(len, GFP_USER | __GFP_NOWARN);
if (msg_data == NULL)
return -ENOMEM;
diff --git a/net/nfc/llcp_sock.c b/net/nfc/llcp_sock.c
index ecf0a01..b9edf5f 100644
--- a/net/nfc/llcp_sock.c
+++ b/net/nfc/llcp_sock.c
@@ -509,6 +509,11 @@
memset(llcp_addr, 0, sizeof(*llcp_addr));
*len = sizeof(struct sockaddr_nfc_llcp);
+ lock_sock(sk);
+ if (!llcp_sock->dev) {
+ release_sock(sk);
+ return -EBADFD;
+ }
llcp_addr->sa_family = AF_NFC;
llcp_addr->dev_idx = llcp_sock->dev->idx;
llcp_addr->target_idx = llcp_sock->target_idx;
@@ -518,6 +523,7 @@
llcp_addr->service_name_len = llcp_sock->service_name_len;
memcpy(llcp_addr->service_name, llcp_sock->service_name,
llcp_addr->service_name_len);
+ release_sock(sk);
return 0;
}
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
index e6a7d49..0cc66a4 100644
--- a/net/openvswitch/datapath.c
+++ b/net/openvswitch/datapath.c
@@ -1096,26 +1096,32 @@
struct sw_flow_match match;
struct sw_flow_id sfid;
u32 ufid_flags = ovs_nla_get_ufid_flags(a[OVS_FLOW_ATTR_UFID_FLAGS]);
- int error;
+ int error = 0;
bool log = !a[OVS_FLOW_ATTR_PROBE];
bool ufid_present;
- /* Extract key. */
- error = -EINVAL;
- if (!a[OVS_FLOW_ATTR_KEY]) {
- OVS_NLERR(log, "Flow key attribute not present in set flow.");
- goto error;
- }
-
ufid_present = ovs_nla_get_ufid(&sfid, a[OVS_FLOW_ATTR_UFID], log);
- ovs_match_init(&match, &key, &mask);
- error = ovs_nla_get_match(net, &match, a[OVS_FLOW_ATTR_KEY],
- a[OVS_FLOW_ATTR_MASK], log);
+ if (a[OVS_FLOW_ATTR_KEY]) {
+ ovs_match_init(&match, &key, &mask);
+ error = ovs_nla_get_match(net, &match, a[OVS_FLOW_ATTR_KEY],
+ a[OVS_FLOW_ATTR_MASK], log);
+ } else if (!ufid_present) {
+ OVS_NLERR(log,
+ "Flow set message rejected, Key attribute missing.");
+ error = -EINVAL;
+ }
if (error)
goto error;
/* Validate actions. */
if (a[OVS_FLOW_ATTR_ACTIONS]) {
+ if (!a[OVS_FLOW_ATTR_KEY]) {
+ OVS_NLERR(log,
+ "Flow key attribute not present in set flow.");
+ error = -EINVAL;
+ goto error;
+ }
+
acts = get_flow_actions(net, a[OVS_FLOW_ATTR_ACTIONS], &key,
&mask, log);
if (IS_ERR(acts)) {
diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c
index a765017..9d935fa 100644
--- a/net/rxrpc/af_rxrpc.c
+++ b/net/rxrpc/af_rxrpc.c
@@ -81,7 +81,7 @@
struct sockaddr_rxrpc *srx,
int len)
{
- unsigned tail;
+ unsigned int tail;
if (len < sizeof(struct sockaddr_rxrpc))
return -EINVAL;
diff --git a/net/rxrpc/ar-ack.c b/net/rxrpc/ar-ack.c
index 20f3f00..16d9670 100644
--- a/net/rxrpc/ar-ack.c
+++ b/net/rxrpc/ar-ack.c
@@ -23,7 +23,7 @@
* How long to wait before scheduling ACK generation after seeing a
* packet with RXRPC_REQUEST_ACK set (in jiffies).
*/
-unsigned rxrpc_requested_ack_delay = 1;
+unsigned int rxrpc_requested_ack_delay = 1;
/*
* How long to wait before scheduling an ACK with subtype DELAY (in jiffies).
@@ -32,7 +32,7 @@
* all consumed within this time we will send a DELAY ACK if an ACK was not
* requested to let the sender know it doesn't need to resend.
*/
-unsigned rxrpc_soft_ack_delay = 1 * HZ;
+unsigned int rxrpc_soft_ack_delay = 1 * HZ;
/*
* How long to wait before scheduling an ACK with subtype IDLE (in jiffies).
@@ -41,7 +41,7 @@
* further packets aren't immediately received to decide when to send an IDLE
* ACK let the other end know that it can free up its Tx buffer space.
*/
-unsigned rxrpc_idle_ack_delay = 0.5 * HZ;
+unsigned int rxrpc_idle_ack_delay = 0.5 * HZ;
/*
* Receive window size in packets. This indicates the maximum number of
@@ -49,19 +49,19 @@
* limit is hit, we should generate an EXCEEDS_WINDOW ACK and discard further
* packets.
*/
-unsigned rxrpc_rx_window_size = 32;
+unsigned int rxrpc_rx_window_size = 32;
/*
* Maximum Rx MTU size. This indicates to the sender the size of jumbo packet
* made by gluing normal packets together that we're willing to handle.
*/
-unsigned rxrpc_rx_mtu = 5692;
+unsigned int rxrpc_rx_mtu = 5692;
/*
* The maximum number of fragments in a received jumbo packet that we tell the
* sender that we're willing to handle.
*/
-unsigned rxrpc_rx_jumbo_max = 4;
+unsigned int rxrpc_rx_jumbo_max = 4;
static const char *rxrpc_acks(u8 reason)
{
diff --git a/net/rxrpc/ar-call.c b/net/rxrpc/ar-call.c
index 4a499e0..7c8d300 100644
--- a/net/rxrpc/ar-call.c
+++ b/net/rxrpc/ar-call.c
@@ -21,12 +21,12 @@
/*
* Maximum lifetime of a call (in jiffies).
*/
-unsigned rxrpc_max_call_lifetime = 60 * HZ;
+unsigned int rxrpc_max_call_lifetime = 60 * HZ;
/*
* Time till dead call expires after last use (in jiffies).
*/
-unsigned rxrpc_dead_call_expiry = 2 * HZ;
+unsigned int rxrpc_dead_call_expiry = 2 * HZ;
const char *const rxrpc_call_states[NR__RXRPC_CALL_STATES] = {
[RXRPC_CALL_CLIENT_SEND_REQUEST] = "ClSndReq",
diff --git a/net/rxrpc/ar-connection.c b/net/rxrpc/ar-connection.c
index 53df14c..9942da1 100644
--- a/net/rxrpc/ar-connection.c
+++ b/net/rxrpc/ar-connection.c
@@ -21,7 +21,7 @@
/*
* Time till a connection expires after last use (in seconds).
*/
-unsigned rxrpc_connection_expiry = 10 * 60;
+unsigned int rxrpc_connection_expiry = 10 * 60;
static void rxrpc_connection_reaper(struct work_struct *work);
diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
index 8b495ae..a3002f4 100644
--- a/net/rxrpc/ar-internal.h
+++ b/net/rxrpc/ar-internal.h
@@ -478,12 +478,12 @@
/*
* ar-ack.c
*/
-extern unsigned rxrpc_requested_ack_delay;
-extern unsigned rxrpc_soft_ack_delay;
-extern unsigned rxrpc_idle_ack_delay;
-extern unsigned rxrpc_rx_window_size;
-extern unsigned rxrpc_rx_mtu;
-extern unsigned rxrpc_rx_jumbo_max;
+extern unsigned int rxrpc_requested_ack_delay;
+extern unsigned int rxrpc_soft_ack_delay;
+extern unsigned int rxrpc_idle_ack_delay;
+extern unsigned int rxrpc_rx_window_size;
+extern unsigned int rxrpc_rx_mtu;
+extern unsigned int rxrpc_rx_jumbo_max;
void __rxrpc_propose_ACK(struct rxrpc_call *, u8, u32, bool);
void rxrpc_propose_ACK(struct rxrpc_call *, u8, u32, bool);
@@ -492,8 +492,8 @@
/*
* ar-call.c
*/
-extern unsigned rxrpc_max_call_lifetime;
-extern unsigned rxrpc_dead_call_expiry;
+extern unsigned int rxrpc_max_call_lifetime;
+extern unsigned int rxrpc_dead_call_expiry;
extern struct kmem_cache *rxrpc_call_jar;
extern struct list_head rxrpc_calls;
extern rwlock_t rxrpc_call_lock;
@@ -516,7 +516,7 @@
/*
* ar-connection.c
*/
-extern unsigned rxrpc_connection_expiry;
+extern unsigned int rxrpc_connection_expiry;
extern struct list_head rxrpc_connections;
extern rwlock_t rxrpc_connection_lock;
@@ -579,7 +579,7 @@
/*
* ar-output.c
*/
-extern unsigned rxrpc_resend_timeout;
+extern unsigned int rxrpc_resend_timeout;
int rxrpc_send_packet(struct rxrpc_transport *, struct sk_buff *);
int rxrpc_client_sendmsg(struct rxrpc_sock *, struct rxrpc_transport *,
@@ -627,7 +627,7 @@
/*
* ar-transport.c
*/
-extern unsigned rxrpc_transport_expiry;
+extern unsigned int rxrpc_transport_expiry;
struct rxrpc_transport *rxrpc_get_transport(struct rxrpc_local *,
struct rxrpc_peer *, gfp_t);
diff --git a/net/rxrpc/ar-output.c b/net/rxrpc/ar-output.c
index 14c8df6..d36fb6e 100644
--- a/net/rxrpc/ar-output.c
+++ b/net/rxrpc/ar-output.c
@@ -21,7 +21,7 @@
/*
* Time till packet resend (in jiffies).
*/
-unsigned rxrpc_resend_timeout = 4 * HZ;
+unsigned int rxrpc_resend_timeout = 4 * HZ;
static int rxrpc_send_data(struct rxrpc_sock *rx,
struct rxrpc_call *call,
diff --git a/net/rxrpc/ar-transport.c b/net/rxrpc/ar-transport.c
index 5f9b9d4..66a1a56 100644
--- a/net/rxrpc/ar-transport.c
+++ b/net/rxrpc/ar-transport.c
@@ -20,7 +20,7 @@
/*
* Time after last use at which transport record is cleaned up.
*/
-unsigned rxrpc_transport_expiry = 3600 * 24;
+unsigned int rxrpc_transport_expiry = 3600 * 24;
static void rxrpc_transport_reaper(struct work_struct *work);
diff --git a/net/rxrpc/sysctl.c b/net/rxrpc/sysctl.c
index 093547a..d20ed57 100644
--- a/net/rxrpc/sysctl.c
+++ b/net/rxrpc/sysctl.c
@@ -15,11 +15,11 @@
#include "ar-internal.h"
static struct ctl_table_header *rxrpc_sysctl_reg_table;
-static const unsigned zero = 0;
-static const unsigned one = 1;
-static const unsigned four = 4;
-static const unsigned n_65535 = 65535;
-static const unsigned n_max_acks = RXRPC_MAXACKS;
+static const unsigned int zero = 0;
+static const unsigned int one = 1;
+static const unsigned int four = 4;
+static const unsigned int n_65535 = 65535;
+static const unsigned int n_max_acks = RXRPC_MAXACKS;
/*
* RxRPC operating parameters.
@@ -32,7 +32,7 @@
{
.procname = "req_ack_delay",
.data = &rxrpc_requested_ack_delay,
- .maxlen = sizeof(unsigned),
+ .maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = proc_dointvec_ms_jiffies,
.extra1 = (void *)&zero,
@@ -40,7 +40,7 @@
{
.procname = "soft_ack_delay",
.data = &rxrpc_soft_ack_delay,
- .maxlen = sizeof(unsigned),
+ .maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = proc_dointvec_ms_jiffies,
.extra1 = (void *)&one,
@@ -48,7 +48,7 @@
{
.procname = "idle_ack_delay",
.data = &rxrpc_idle_ack_delay,
- .maxlen = sizeof(unsigned),
+ .maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = proc_dointvec_ms_jiffies,
.extra1 = (void *)&one,
@@ -56,7 +56,7 @@
{
.procname = "resend_timeout",
.data = &rxrpc_resend_timeout,
- .maxlen = sizeof(unsigned),
+ .maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = proc_dointvec_ms_jiffies,
.extra1 = (void *)&one,
@@ -66,7 +66,7 @@
{
.procname = "max_call_lifetime",
.data = &rxrpc_max_call_lifetime,
- .maxlen = sizeof(unsigned),
+ .maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = proc_dointvec_jiffies,
.extra1 = (void *)&one,
@@ -74,7 +74,7 @@
{
.procname = "dead_call_expiry",
.data = &rxrpc_dead_call_expiry,
- .maxlen = sizeof(unsigned),
+ .maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = proc_dointvec_jiffies,
.extra1 = (void *)&one,
@@ -84,7 +84,7 @@
{
.procname = "connection_expiry",
.data = &rxrpc_connection_expiry,
- .maxlen = sizeof(unsigned),
+ .maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
.extra1 = (void *)&one,
@@ -92,7 +92,7 @@
{
.procname = "transport_expiry",
.data = &rxrpc_transport_expiry,
- .maxlen = sizeof(unsigned),
+ .maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
.extra1 = (void *)&one,
@@ -102,7 +102,7 @@
{
.procname = "rx_window_size",
.data = &rxrpc_rx_window_size,
- .maxlen = sizeof(unsigned),
+ .maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
.extra1 = (void *)&one,
@@ -111,7 +111,7 @@
{
.procname = "rx_mtu",
.data = &rxrpc_rx_mtu,
- .maxlen = sizeof(unsigned),
+ .maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
.extra1 = (void *)&one,
@@ -120,7 +120,7 @@
{
.procname = "rx_jumbo_max",
.data = &rxrpc_rx_jumbo_max,
- .maxlen = sizeof(unsigned),
+ .maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
.extra1 = (void *)&one,
diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c
index 95b0212..2181ffc 100644
--- a/net/sched/cls_flower.c
+++ b/net/sched/cls_flower.c
@@ -165,6 +165,51 @@
kfree(f);
}
+static void fl_hw_destroy_filter(struct tcf_proto *tp, unsigned long cookie)
+{
+ struct net_device *dev = tp->q->dev_queue->dev;
+ struct tc_cls_flower_offload offload = {0};
+ struct tc_to_netdev tc;
+
+ if (!tc_should_offload(dev, 0))
+ return;
+
+ offload.command = TC_CLSFLOWER_DESTROY;
+ offload.cookie = cookie;
+
+ tc.type = TC_SETUP_CLSFLOWER;
+ tc.cls_flower = &offload;
+
+ dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, tp->protocol, &tc);
+}
+
+static void fl_hw_replace_filter(struct tcf_proto *tp,
+ struct flow_dissector *dissector,
+ struct fl_flow_key *mask,
+ struct fl_flow_key *key,
+ struct tcf_exts *actions,
+ unsigned long cookie, u32 flags)
+{
+ struct net_device *dev = tp->q->dev_queue->dev;
+ struct tc_cls_flower_offload offload = {0};
+ struct tc_to_netdev tc;
+
+ if (!tc_should_offload(dev, flags))
+ return;
+
+ offload.command = TC_CLSFLOWER_REPLACE;
+ offload.cookie = cookie;
+ offload.dissector = dissector;
+ offload.mask = mask;
+ offload.key = key;
+ offload.exts = actions;
+
+ tc.type = TC_SETUP_CLSFLOWER;
+ tc.cls_flower = &offload;
+
+ dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, tp->protocol, &tc);
+}
+
static bool fl_destroy(struct tcf_proto *tp, bool force)
{
struct cls_fl_head *head = rtnl_dereference(tp->root);
@@ -174,6 +219,7 @@
return false;
list_for_each_entry_safe(f, next, &head->filters, list) {
+ fl_hw_destroy_filter(tp, (unsigned long)f);
list_del_rcu(&f->list);
call_rcu(&f->rcu, fl_destroy_filter);
}
@@ -459,6 +505,7 @@
struct cls_fl_filter *fnew;
struct nlattr *tb[TCA_FLOWER_MAX + 1];
struct fl_flow_mask mask = {};
+ u32 flags = 0;
int err;
if (!tca[TCA_OPTIONS])
@@ -486,6 +533,9 @@
}
fnew->handle = handle;
+ if (tb[TCA_FLOWER_FLAGS])
+ flags = nla_get_u32(tb[TCA_FLOWER_FLAGS]);
+
err = fl_set_parms(net, tp, fnew, &mask, base, tb, tca[TCA_RATE], ovr);
if (err)
goto errout;
@@ -498,9 +548,20 @@
head->ht_params);
if (err)
goto errout;
- if (fold)
+
+ fl_hw_replace_filter(tp,
+ &head->dissector,
+ &mask.key,
+ &fnew->key,
+ &fnew->exts,
+ (unsigned long)fnew,
+ flags);
+
+ if (fold) {
rhashtable_remove_fast(&head->ht, &fold->ht_node,
head->ht_params);
+ fl_hw_destroy_filter(tp, (unsigned long)fold);
+ }
*arg = (unsigned long) fnew;
@@ -527,6 +588,7 @@
rhashtable_remove_fast(&head->ht, &f->ht_node,
head->ht_params);
list_del_rcu(&f->list);
+ fl_hw_destroy_filter(tp, (unsigned long)f);
tcf_unbind_filter(tp, &f->res);
call_rcu(&f->rcu, fl_destroy_filter);
return 0;
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index 2bf8ec9..a19b3e6 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -1263,7 +1263,7 @@
if (score_curr > score_best)
return curr;
else if (score_curr == score_best)
- return sctp_trans_elect_tie(curr, best);
+ return sctp_trans_elect_tie(best, curr);
else
return best;
}
@@ -1493,7 +1493,7 @@
asoc->peer.sack_needed = 0;
- sctp_outq_tail(&asoc->outqueue, sack);
+ sctp_outq_tail(&asoc->outqueue, sack, GFP_ATOMIC);
/* Stop the SACK timer. */
timer = &asoc->timers[SCTP_EVENT_TIMEOUT_SACK];
diff --git a/net/sctp/chunk.c b/net/sctp/chunk.c
index 3aa4307..958ef5f 100644
--- a/net/sctp/chunk.c
+++ b/net/sctp/chunk.c
@@ -260,7 +260,8 @@
frag |= SCTP_DATA_SACK_IMM;
}
- chunk = sctp_make_datafrag_empty(asoc, sinfo, len, frag, 0);
+ chunk = sctp_make_datafrag_empty(asoc, sinfo, len, frag,
+ 0, GFP_KERNEL);
if (!chunk) {
err = -ENOMEM;
@@ -296,7 +297,8 @@
(sinfo->sinfo_flags & SCTP_SACK_IMMEDIATELY))
frag |= SCTP_DATA_SACK_IMM;
- chunk = sctp_make_datafrag_empty(asoc, sinfo, over, frag, 0);
+ chunk = sctp_make_datafrag_empty(asoc, sinfo, over, frag,
+ 0, GFP_KERNEL);
if (!chunk) {
err = -ENOMEM;
diff --git a/net/sctp/input.c b/net/sctp/input.c
index 21a2d6b..db76f1a 100644
--- a/net/sctp/input.c
+++ b/net/sctp/input.c
@@ -221,7 +221,7 @@
goto discard_release;
/* Create an SCTP packet structure. */
- chunk = sctp_chunkify(skb, asoc, sk);
+ chunk = sctp_chunkify(skb, asoc, sk, GFP_ATOMIC);
if (!chunk)
goto discard_release;
SCTP_INPUT_CB(skb)->chunk = chunk;
diff --git a/net/sctp/output.c b/net/sctp/output.c
index 9d610ed..736c004 100644
--- a/net/sctp/output.c
+++ b/net/sctp/output.c
@@ -153,7 +153,7 @@
*/
sctp_xmit_t sctp_packet_transmit_chunk(struct sctp_packet *packet,
struct sctp_chunk *chunk,
- int one_packet)
+ int one_packet, gfp_t gfp)
{
sctp_xmit_t retval;
int error = 0;
@@ -163,7 +163,7 @@
switch ((retval = (sctp_packet_append_chunk(packet, chunk)))) {
case SCTP_XMIT_PMTU_FULL:
if (!packet->has_cookie_echo) {
- error = sctp_packet_transmit(packet);
+ error = sctp_packet_transmit(packet, gfp);
if (error < 0)
chunk->skb->sk->sk_err = -error;
@@ -376,7 +376,7 @@
*
* The return value is a normal kernel error return value.
*/
-int sctp_packet_transmit(struct sctp_packet *packet)
+int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp)
{
struct sctp_transport *tp = packet->transport;
struct sctp_association *asoc = tp->asoc;
diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c
index c0380cf..f03541d 100644
--- a/net/sctp/outqueue.c
+++ b/net/sctp/outqueue.c
@@ -68,7 +68,7 @@
static void sctp_generate_fwdtsn(struct sctp_outq *q, __u32 sack_ctsn);
-static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout);
+static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp);
/* Add data to the front of the queue. */
static inline void sctp_outq_head_data(struct sctp_outq *q,
@@ -285,7 +285,7 @@
}
/* Put a new chunk in an sctp_outq. */
-int sctp_outq_tail(struct sctp_outq *q, struct sctp_chunk *chunk)
+int sctp_outq_tail(struct sctp_outq *q, struct sctp_chunk *chunk, gfp_t gfp)
{
struct net *net = sock_net(q->asoc->base.sk);
int error = 0;
@@ -341,7 +341,7 @@
return error;
if (!q->cork)
- error = sctp_outq_flush(q, 0);
+ error = sctp_outq_flush(q, 0, gfp);
return error;
}
@@ -510,7 +510,7 @@
* will be flushed at the end.
*/
if (reason != SCTP_RTXR_FAST_RTX)
- error = sctp_outq_flush(q, /* rtx_timeout */ 1);
+ error = sctp_outq_flush(q, /* rtx_timeout */ 1, GFP_ATOMIC);
if (error)
q->asoc->base.sk->sk_err = -error;
@@ -601,12 +601,12 @@
* control chunks are already freed so there
* is nothing we can do.
*/
- sctp_packet_transmit(pkt);
+ sctp_packet_transmit(pkt, GFP_ATOMIC);
goto redo;
}
/* Send this packet. */
- error = sctp_packet_transmit(pkt);
+ error = sctp_packet_transmit(pkt, GFP_ATOMIC);
/* If we are retransmitting, we should only
* send a single packet.
@@ -622,7 +622,7 @@
case SCTP_XMIT_RWND_FULL:
/* Send this packet. */
- error = sctp_packet_transmit(pkt);
+ error = sctp_packet_transmit(pkt, GFP_ATOMIC);
/* Stop sending DATA as there is no more room
* at the receiver.
@@ -632,7 +632,7 @@
case SCTP_XMIT_DELAY:
/* Send this packet. */
- error = sctp_packet_transmit(pkt);
+ error = sctp_packet_transmit(pkt, GFP_ATOMIC);
/* Stop sending DATA because of nagle delay. */
done = 1;
@@ -685,12 +685,12 @@
}
/* Cork the outqueue so queued chunks are really queued. */
-int sctp_outq_uncork(struct sctp_outq *q)
+int sctp_outq_uncork(struct sctp_outq *q, gfp_t gfp)
{
if (q->cork)
q->cork = 0;
- return sctp_outq_flush(q, 0);
+ return sctp_outq_flush(q, 0, gfp);
}
@@ -703,7 +703,7 @@
* locking concerns must be made. Today we use the sock lock to protect
* this function.
*/
-static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
+static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp)
{
struct sctp_packet *packet;
struct sctp_packet singleton;
@@ -825,7 +825,7 @@
sctp_packet_init(&singleton, transport, sport, dport);
sctp_packet_config(&singleton, vtag, 0);
sctp_packet_append_chunk(&singleton, chunk);
- error = sctp_packet_transmit(&singleton);
+ error = sctp_packet_transmit(&singleton, gfp);
if (error < 0)
return error;
break;
@@ -856,7 +856,7 @@
case SCTP_CID_ASCONF:
case SCTP_CID_FWD_TSN:
status = sctp_packet_transmit_chunk(packet, chunk,
- one_packet);
+ one_packet, gfp);
if (status != SCTP_XMIT_OK) {
/* put the chunk back */
list_add(&chunk->list, &q->control_chunk_list);
@@ -1011,7 +1011,7 @@
atomic_read(&chunk->skb->users) : -1);
/* Add the chunk to the packet. */
- status = sctp_packet_transmit_chunk(packet, chunk, 0);
+ status = sctp_packet_transmit_chunk(packet, chunk, 0, gfp);
switch (status) {
case SCTP_XMIT_PMTU_FULL:
@@ -1088,7 +1088,7 @@
send_ready);
packet = &t->packet;
if (!sctp_packet_empty(packet))
- error = sctp_packet_transmit(packet);
+ error = sctp_packet_transmit(packet, gfp);
/* Clear the burst limited state, if any */
sctp_transport_burst_reset(t);
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index 5d6a03f..8449ca2 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -62,11 +62,13 @@
#include <net/sctp/sm.h>
static struct sctp_chunk *sctp_make_control(const struct sctp_association *asoc,
- __u8 type, __u8 flags, int paylen);
+ __u8 type, __u8 flags, int paylen,
+ gfp_t gfp);
static struct sctp_chunk *sctp_make_data(const struct sctp_association *asoc,
- __u8 flags, int paylen);
+ __u8 flags, int paylen, gfp_t gfp);
static struct sctp_chunk *_sctp_make_chunk(const struct sctp_association *asoc,
- __u8 type, __u8 flags, int paylen);
+ __u8 type, __u8 flags, int paylen,
+ gfp_t gfp);
static sctp_cookie_param_t *sctp_pack_cookie(const struct sctp_endpoint *ep,
const struct sctp_association *asoc,
const struct sctp_chunk *init_chunk,
@@ -318,7 +320,7 @@
* PLEASE DO NOT FIXME [This version does not support Host Name.]
*/
- retval = sctp_make_control(asoc, SCTP_CID_INIT, 0, chunksize);
+ retval = sctp_make_control(asoc, SCTP_CID_INIT, 0, chunksize, gfp);
if (!retval)
goto nodata;
@@ -465,7 +467,7 @@
num_ext);
/* Now allocate and fill out the chunk. */
- retval = sctp_make_control(asoc, SCTP_CID_INIT_ACK, 0, chunksize);
+ retval = sctp_make_control(asoc, SCTP_CID_INIT_ACK, 0, chunksize, gfp);
if (!retval)
goto nomem_chunk;
@@ -570,7 +572,8 @@
cookie_len = asoc->peer.cookie_len;
/* Build a cookie echo chunk. */
- retval = sctp_make_control(asoc, SCTP_CID_COOKIE_ECHO, 0, cookie_len);
+ retval = sctp_make_control(asoc, SCTP_CID_COOKIE_ECHO, 0,
+ cookie_len, GFP_ATOMIC);
if (!retval)
goto nodata;
retval->subh.cookie_hdr =
@@ -615,7 +618,7 @@
{
struct sctp_chunk *retval;
- retval = sctp_make_control(asoc, SCTP_CID_COOKIE_ACK, 0, 0);
+ retval = sctp_make_control(asoc, SCTP_CID_COOKIE_ACK, 0, 0, GFP_ATOMIC);
/* RFC 2960 6.4 Multi-homed SCTP Endpoints
*
@@ -664,7 +667,7 @@
cwr.lowest_tsn = htonl(lowest_tsn);
retval = sctp_make_control(asoc, SCTP_CID_ECN_CWR, 0,
- sizeof(sctp_cwrhdr_t));
+ sizeof(sctp_cwrhdr_t), GFP_ATOMIC);
if (!retval)
goto nodata;
@@ -698,7 +701,7 @@
ecne.lowest_tsn = htonl(lowest_tsn);
retval = sctp_make_control(asoc, SCTP_CID_ECN_ECNE, 0,
- sizeof(sctp_ecnehdr_t));
+ sizeof(sctp_ecnehdr_t), GFP_ATOMIC);
if (!retval)
goto nodata;
retval->subh.ecne_hdr =
@@ -713,7 +716,8 @@
*/
struct sctp_chunk *sctp_make_datafrag_empty(struct sctp_association *asoc,
const struct sctp_sndrcvinfo *sinfo,
- int data_len, __u8 flags, __u16 ssn)
+ int data_len, __u8 flags, __u16 ssn,
+ gfp_t gfp)
{
struct sctp_chunk *retval;
struct sctp_datahdr dp;
@@ -734,7 +738,7 @@
dp.ssn = htons(ssn);
chunk_len = sizeof(dp) + data_len;
- retval = sctp_make_data(asoc, flags, chunk_len);
+ retval = sctp_make_data(asoc, flags, chunk_len, gfp);
if (!retval)
goto nodata;
@@ -781,7 +785,7 @@
+ sizeof(__u32) * num_dup_tsns;
/* Create the chunk. */
- retval = sctp_make_control(asoc, SCTP_CID_SACK, 0, len);
+ retval = sctp_make_control(asoc, SCTP_CID_SACK, 0, len, GFP_ATOMIC);
if (!retval)
goto nodata;
@@ -861,7 +865,7 @@
shut.cum_tsn_ack = htonl(ctsn);
retval = sctp_make_control(asoc, SCTP_CID_SHUTDOWN, 0,
- sizeof(sctp_shutdownhdr_t));
+ sizeof(sctp_shutdownhdr_t), GFP_ATOMIC);
if (!retval)
goto nodata;
@@ -879,7 +883,8 @@
{
struct sctp_chunk *retval;
- retval = sctp_make_control(asoc, SCTP_CID_SHUTDOWN_ACK, 0, 0);
+ retval = sctp_make_control(asoc, SCTP_CID_SHUTDOWN_ACK, 0, 0,
+ GFP_ATOMIC);
/* RFC 2960 6.4 Multi-homed SCTP Endpoints
*
@@ -908,7 +913,8 @@
*/
flags |= asoc ? 0 : SCTP_CHUNK_FLAG_T;
- retval = sctp_make_control(asoc, SCTP_CID_SHUTDOWN_COMPLETE, flags, 0);
+ retval = sctp_make_control(asoc, SCTP_CID_SHUTDOWN_COMPLETE, flags,
+ 0, GFP_ATOMIC);
/* RFC 2960 6.4 Multi-homed SCTP Endpoints
*
@@ -947,7 +953,8 @@
flags = SCTP_CHUNK_FLAG_T;
}
- retval = sctp_make_control(asoc, SCTP_CID_ABORT, flags, hint);
+ retval = sctp_make_control(asoc, SCTP_CID_ABORT, flags, hint,
+ GFP_ATOMIC);
/* RFC 2960 6.4 Multi-homed SCTP Endpoints
*
@@ -1139,7 +1146,8 @@
struct sctp_chunk *retval;
sctp_sender_hb_info_t hbinfo;
- retval = sctp_make_control(asoc, SCTP_CID_HEARTBEAT, 0, sizeof(hbinfo));
+ retval = sctp_make_control(asoc, SCTP_CID_HEARTBEAT, 0,
+ sizeof(hbinfo), GFP_ATOMIC);
if (!retval)
goto nodata;
@@ -1167,7 +1175,8 @@
{
struct sctp_chunk *retval;
- retval = sctp_make_control(asoc, SCTP_CID_HEARTBEAT_ACK, 0, paylen);
+ retval = sctp_make_control(asoc, SCTP_CID_HEARTBEAT_ACK, 0, paylen,
+ GFP_ATOMIC);
if (!retval)
goto nodata;
@@ -1200,7 +1209,7 @@
struct sctp_chunk *retval;
retval = sctp_make_control(asoc, SCTP_CID_ERROR, 0,
- sizeof(sctp_errhdr_t) + size);
+ sizeof(sctp_errhdr_t) + size, GFP_ATOMIC);
if (!retval)
goto nodata;
@@ -1271,7 +1280,8 @@
return NULL;
retval = sctp_make_control(asoc, SCTP_CID_AUTH, 0,
- hmac_desc->hmac_len + sizeof(sctp_authhdr_t));
+ hmac_desc->hmac_len + sizeof(sctp_authhdr_t),
+ GFP_ATOMIC);
if (!retval)
return NULL;
@@ -1309,11 +1319,11 @@
*/
struct sctp_chunk *sctp_chunkify(struct sk_buff *skb,
const struct sctp_association *asoc,
- struct sock *sk)
+ struct sock *sk, gfp_t gfp)
{
struct sctp_chunk *retval;
- retval = kmem_cache_zalloc(sctp_chunk_cachep, GFP_ATOMIC);
+ retval = kmem_cache_zalloc(sctp_chunk_cachep, gfp);
if (!retval)
goto nodata;
@@ -1361,7 +1371,8 @@
* arguments, reserving enough space for a 'paylen' byte payload.
*/
static struct sctp_chunk *_sctp_make_chunk(const struct sctp_association *asoc,
- __u8 type, __u8 flags, int paylen)
+ __u8 type, __u8 flags, int paylen,
+ gfp_t gfp)
{
struct sctp_chunk *retval;
sctp_chunkhdr_t *chunk_hdr;
@@ -1369,8 +1380,7 @@
struct sock *sk;
/* No need to allocate LL here, as this is only a chunk. */
- skb = alloc_skb(WORD_ROUND(sizeof(sctp_chunkhdr_t) + paylen),
- GFP_ATOMIC);
+ skb = alloc_skb(WORD_ROUND(sizeof(sctp_chunkhdr_t) + paylen), gfp);
if (!skb)
goto nodata;
@@ -1381,7 +1391,7 @@
chunk_hdr->length = htons(sizeof(sctp_chunkhdr_t));
sk = asoc ? asoc->base.sk : NULL;
- retval = sctp_chunkify(skb, asoc, sk);
+ retval = sctp_chunkify(skb, asoc, sk, gfp);
if (!retval) {
kfree_skb(skb);
goto nodata;
@@ -1400,16 +1410,18 @@
}
static struct sctp_chunk *sctp_make_data(const struct sctp_association *asoc,
- __u8 flags, int paylen)
+ __u8 flags, int paylen, gfp_t gfp)
{
- return _sctp_make_chunk(asoc, SCTP_CID_DATA, flags, paylen);
+ return _sctp_make_chunk(asoc, SCTP_CID_DATA, flags, paylen, gfp);
}
static struct sctp_chunk *sctp_make_control(const struct sctp_association *asoc,
- __u8 type, __u8 flags, int paylen)
+ __u8 type, __u8 flags, int paylen,
+ gfp_t gfp)
{
- struct sctp_chunk *chunk = _sctp_make_chunk(asoc, type, flags, paylen);
+ struct sctp_chunk *chunk;
+ chunk = _sctp_make_chunk(asoc, type, flags, paylen, gfp);
if (chunk)
sctp_control_set_owner_w(chunk);
@@ -2756,7 +2768,8 @@
length += addrlen;
/* Create the chunk. */
- retval = sctp_make_control(asoc, SCTP_CID_ASCONF, 0, length);
+ retval = sctp_make_control(asoc, SCTP_CID_ASCONF, 0, length,
+ GFP_ATOMIC);
if (!retval)
return NULL;
@@ -2940,7 +2953,8 @@
int length = sizeof(asconf) + vparam_len;
/* Create the chunk. */
- retval = sctp_make_control(asoc, SCTP_CID_ASCONF_ACK, 0, length);
+ retval = sctp_make_control(asoc, SCTP_CID_ASCONF_ACK, 0, length,
+ GFP_ATOMIC);
if (!retval)
return NULL;
@@ -3500,7 +3514,7 @@
hint = (nstreams + 1) * sizeof(__u32);
- retval = sctp_make_control(asoc, SCTP_CID_FWD_TSN, 0, hint);
+ retval = sctp_make_control(asoc, SCTP_CID_FWD_TSN, 0, hint, GFP_ATOMIC);
if (!retval)
return NULL;
diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
index b5327bb..3c22c41 100644
--- a/net/sctp/sm_sideeffect.c
+++ b/net/sctp/sm_sideeffect.c
@@ -1019,13 +1019,13 @@
* encouraged for small fragments.
*/
static int sctp_cmd_send_msg(struct sctp_association *asoc,
- struct sctp_datamsg *msg)
+ struct sctp_datamsg *msg, gfp_t gfp)
{
struct sctp_chunk *chunk;
int error = 0;
list_for_each_entry(chunk, &msg->chunks, frag_list) {
- error = sctp_outq_tail(&asoc->outqueue, chunk);
+ error = sctp_outq_tail(&asoc->outqueue, chunk, gfp);
if (error)
break;
}
@@ -1249,7 +1249,7 @@
case SCTP_CMD_NEW_ASOC:
/* Register a new association. */
if (local_cork) {
- sctp_outq_uncork(&asoc->outqueue);
+ sctp_outq_uncork(&asoc->outqueue, gfp);
local_cork = 0;
}
@@ -1269,7 +1269,7 @@
case SCTP_CMD_DELETE_TCB:
if (local_cork) {
- sctp_outq_uncork(&asoc->outqueue);
+ sctp_outq_uncork(&asoc->outqueue, gfp);
local_cork = 0;
}
/* Delete the current association. */
@@ -1423,13 +1423,14 @@
local_cork = 1;
}
/* Send a chunk to our peer. */
- error = sctp_outq_tail(&asoc->outqueue, cmd->obj.chunk);
+ error = sctp_outq_tail(&asoc->outqueue, cmd->obj.chunk,
+ gfp);
break;
case SCTP_CMD_SEND_PKT:
/* Send a full packet to our peer. */
packet = cmd->obj.packet;
- sctp_packet_transmit(packet);
+ sctp_packet_transmit(packet, gfp);
sctp_ootb_pkt_free(packet);
break;
@@ -1639,7 +1640,7 @@
*/
chunk->pdiscard = 1;
if (asoc) {
- sctp_outq_uncork(&asoc->outqueue);
+ sctp_outq_uncork(&asoc->outqueue, gfp);
local_cork = 0;
}
break;
@@ -1677,7 +1678,7 @@
case SCTP_CMD_FORCE_PRIM_RETRAN:
t = asoc->peer.retran_path;
asoc->peer.retran_path = asoc->peer.primary_path;
- error = sctp_outq_uncork(&asoc->outqueue);
+ error = sctp_outq_uncork(&asoc->outqueue, gfp);
local_cork = 0;
asoc->peer.retran_path = t;
break;
@@ -1704,7 +1705,7 @@
sctp_outq_cork(&asoc->outqueue);
local_cork = 1;
}
- error = sctp_cmd_send_msg(asoc, cmd->obj.msg);
+ error = sctp_cmd_send_msg(asoc, cmd->obj.msg, gfp);
break;
case SCTP_CMD_SEND_NEXT_ASCONF:
sctp_cmd_send_asconf(asoc);
@@ -1734,9 +1735,9 @@
*/
if (asoc && SCTP_EVENT_T_CHUNK == event_type && chunk) {
if (chunk->end_of_packet || chunk->singleton)
- error = sctp_outq_uncork(&asoc->outqueue);
+ error = sctp_outq_uncork(&asoc->outqueue, gfp);
} else if (local_cork)
- error = sctp_outq_uncork(&asoc->outqueue);
+ error = sctp_outq_uncork(&asoc->outqueue, gfp);
return error;
nomem:
error = -ENOMEM;
diff --git a/net/sctp/transport.c b/net/sctp/transport.c
index a431c14..d517153 100644
--- a/net/sctp/transport.c
+++ b/net/sctp/transport.c
@@ -72,7 +72,7 @@
*/
peer->rto = msecs_to_jiffies(net->sctp.rto_initial);
- peer->last_time_heard = ktime_get();
+ peer->last_time_heard = ktime_set(0, 0);
peer->last_time_ecne_reduced = jiffies;
peer->param_flags = SPP_HB_DISABLE |
diff --git a/net/socket.c b/net/socket.c
index 886649c..5f77a8e 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -1107,12 +1107,8 @@
deadlock in module load.
*/
if (family == PF_INET && type == SOCK_PACKET) {
- static int warned;
- if (!warned) {
- warned = 1;
- pr_info("%s uses obsolete (PF_INET,SOCK_PACKET)\n",
- current->comm);
- }
+ pr_info_once("%s uses obsolete (PF_INET,SOCK_PACKET)\n",
+ current->comm);
family = PF_PACKET;
}
@@ -2248,31 +2244,31 @@
cond_resched();
}
+ if (err == 0)
+ goto out_put;
+
+ if (datagrams == 0) {
+ datagrams = err;
+ goto out_put;
+ }
+
+ /*
+ * We may return less entries than requested (vlen) if the
+ * sock is non block and there aren't enough datagrams...
+ */
+ if (err != -EAGAIN) {
+ /*
+ * ... or if recvmsg returns an error after we
+ * received some datagrams, where we record the
+ * error to return on the next call or if the
+ * app asks about it using getsockopt(SO_ERROR).
+ */
+ sock->sk->sk_err = -err;
+ }
out_put:
fput_light(sock->file, fput_needed);
- if (err == 0)
- return datagrams;
-
- if (datagrams != 0) {
- /*
- * We may return less entries than requested (vlen) if the
- * sock is non block and there aren't enough datagrams...
- */
- if (err != -EAGAIN) {
- /*
- * ... or if recvmsg returns an error after we
- * received some datagrams, where we record the
- * error to return on the next call or if the
- * app asks about it using getsockopt(SO_ERROR).
- */
- sock->sk->sk_err = -err;
- }
-
- return datagrams;
- }
-
- return err;
+ return datagrams;
}
SYSCALL_DEFINE5(recvmmsg, int, fd, struct mmsghdr __user *, mmsg,
diff --git a/net/tipc/udp_media.c b/net/tipc/udp_media.c
index 49b3c2e..c9cf2be 100644
--- a/net/tipc/udp_media.c
+++ b/net/tipc/udp_media.c
@@ -53,7 +53,7 @@
/* IANA assigned UDP port */
#define UDP_PORT_DEFAULT 6118
-#define UDP_MIN_HEADROOM 28
+#define UDP_MIN_HEADROOM 48
/**
* struct udp_media_addr - IP/UDP addressing information
@@ -196,7 +196,7 @@
ttl = ip6_dst_hoplimit(ndst);
err = udp_tunnel6_xmit_skb(ndst, ub->ubsock->sk, skb,
ndst->dev, &src->ipv6,
- &dst->ipv6, 0, ttl, src->udp_port,
+ &dst->ipv6, 0, ttl, 0, src->udp_port,
dst->udp_port, false);
#endif
}