diff options
110 files changed, 18107 insertions, 1321 deletions
diff --git a/Documentation/devicetree/bindings/net/can/sun4i_can.txt b/Documentation/devicetree/bindings/net/can/sun4i_can.txt new file mode 100644 index 000000000000..84ed1909df76 --- /dev/null +++ b/Documentation/devicetree/bindings/net/can/sun4i_can.txt @@ -0,0 +1,36 @@ +Allwinner A10/A20 CAN controller Device Tree Bindings +----------------------------------------------------- + +Required properties: +- compatible: "allwinner,sun4i-a10-can" +- reg: physical base address and size of the Allwinner A10/A20 CAN register map. +- interrupts: interrupt specifier for the sole interrupt. +- clock: phandle and clock specifier. + +Example +------- + +SoC common .dtsi file: + + can0_pins_a: can0@0 { + allwinner,pins = "PH20","PH21"; + allwinner,function = "can"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; +... + can0: can@01c2bc00 { + compatible = "allwinner,sun4i-a10-can"; + reg = <0x01c2bc00 0x400>; + interrupts = <0 26 4>; + clocks = <&apb1_gates 4>; + status = "disabled"; + }; + +Board specific .dts file: + + can0: can@01c2bc00 { + pinctrl-names = "default"; + pinctrl-0 = <&can0_pins_a>; + status = "okay"; + }; diff --git a/Documentation/devicetree/bindings/net/hisilicon-hip04-net.txt b/Documentation/devicetree/bindings/net/hisilicon-hip04-net.txt index 988fc694b663..d1df8a00e1f3 100644 --- a/Documentation/devicetree/bindings/net/hisilicon-hip04-net.txt +++ b/Documentation/devicetree/bindings/net/hisilicon-hip04-net.txt @@ -32,13 +32,13 @@ Required properties: Required properties: -- compatible: should be "hisilicon,hip04-mdio". +- compatible: should be "hisilicon,mdio". - Inherits from MDIO bus node binding [2] [2] Documentation/devicetree/bindings/net/phy.txt Example: mdio { - compatible = "hisilicon,hip04-mdio"; + compatible = "hisilicon,mdio"; reg = <0x28f1000 0x1000>; #address-cells = <1>; #size-cells = <0>; diff --git a/Documentation/devicetree/bindings/net/hisilicon-hns-dsaf.txt b/Documentation/devicetree/bindings/net/hisilicon-hns-dsaf.txt new file mode 100644 index 000000000000..80411b2f0490 --- /dev/null +++ b/Documentation/devicetree/bindings/net/hisilicon-hns-dsaf.txt @@ -0,0 +1,49 @@ +Hisilicon DSA Fabric device controller + +Required properties: +- compatible: should be "hisilicon,hns-dsaf-v1" or "hisilicon,hns-dsaf-v2". + "hisilicon,hns-dsaf-v1" is for hip05. + "hisilicon,hns-dsaf-v2" is for Hi1610 and Hi1612. +- dsa-name: dsa fabric name who provide this interface. + should be "dsafX", X is the dsaf id. +- mode: dsa fabric mode string. only support one of dsaf modes like these: + "2port-64vf", + "6port-16rss", + "6port-16vf". +- interrupt-parent: the interrupt parent of this device. +- interrupts: should contain the DSA Fabric and rcb interrupt. +- reg: specifies base physical address(es) and size of the device registers. + The first region is external interface control register base and size. + The second region is SerDes base register and size. + The third region is the PPE register base and size. + The fourth region is dsa fabric base register and size. + The fifth region is cpld base register and size, it is not required if do not use cpld. +- phy-handle: phy handle of physicl port, 0 if not any phy device. see ethernet.txt [1]. +- buf-size: rx buffer size, should be 16-1024. +- desc-num: number of description in TX and RX queue, should be 512, 1024, 2048 or 4096. + +[1] Documentation/devicetree/bindings/net/phy.txt + +Example: + +dsa: dsa@c7000000 { + compatible = "hisilicon,hns-dsaf-v1"; + dsa_name = "dsaf0"; + mode = "6port-16rss"; + interrupt-parent = <&mbigen_dsa>; + reg = <0x0 0xC0000000 0x0 0x420000 + 0x0 0xC2000000 0x0 0x300000 + 0x0 0xc5000000 0x0 0x890000 + 0x0 0xc7000000 0x0 0x60000>; + phy-handle = <0 0 0 0 &soc0_phy4 &soc0_phy5 0 0>; + interrupts = <131 4>,<132 4>, <133 4>,<134 4>, + <135 4>,<136 4>, <137 4>,<138 4>, + <139 4>,<140 4>, <141 4>,<142 4>, + <143 4>,<144 4>, <145 4>,<146 4>, + <147 4>,<148 4>, <384 1>,<385 1>, + <386 1>,<387 1>, <388 1>,<389 1>, + <390 1>,<391 1>, + buf-size = <4096>; + desc-num = <1024>; + dma-coherent; +}; diff --git a/Documentation/devicetree/bindings/net/hisilicon-hns-mdio.txt b/Documentation/devicetree/bindings/net/hisilicon-hns-mdio.txt new file mode 100644 index 000000000000..9940aa02b8bd --- /dev/null +++ b/Documentation/devicetree/bindings/net/hisilicon-hns-mdio.txt @@ -0,0 +1,22 @@ +Hisilicon MDIO bus controller + +Properties: +- compatible: "hisilicon,mdio","hisilicon,hns-mdio". +- reg: The base address of the MDIO bus controller register bank. +- #address-cells: Must be <1>. +- #size-cells: Must be <0>. MDIO addresses have no size component. + +Typically an MDIO bus might have several children. + +Example: + mdio@803c0000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "hisilicon,mdio","hisilicon,hns-mdio"; + reg = <0x0 0x803c0000 0x0 0x10000>; + + ethernet-phy@0 { + ... + reg = <0>; + }; + }; diff --git a/Documentation/devicetree/bindings/net/hisilicon-hns-nic.txt b/Documentation/devicetree/bindings/net/hisilicon-hns-nic.txt new file mode 100644 index 000000000000..41d19be7011e --- /dev/null +++ b/Documentation/devicetree/bindings/net/hisilicon-hns-nic.txt @@ -0,0 +1,47 @@ +Hisilicon Network Subsystem NIC controller + +Required properties: +- compatible: "hisilicon,hns-nic-v1" or "hisilicon,hns-nic-v2". + "hisilicon,hns-nic-v1" is for hip05. + "hisilicon,hns-nic-v2" is for Hi1610 and Hi1612. +- ae-name: accelerator name who provides this interface, + is simply a name referring to the name of name in the accelerator node. +- port-id: is the index of port provided by DSAF (the accelerator). DSAF can + connect to 8 PHYs. Port 0 to 1 are both used for adminstration purpose. They + are called debug ports. + + The remaining 6 PHYs are taken according to the mode of DSAF. + + In NIC mode of DSAF, all 6 PHYs are taken as ethernet ports to the CPU. The + port-id can be 2 to 7. Here is the diagram: + +-----+---------------+ + | CPU | + +-+-+-+---+-+-+-+-+-+-+ + | | | | | | | | + debug service + port port + (0,1) (2-7) + + In Switch mode of DSAF, all 6 PHYs are taken as physical ports connect to a + LAN Switch while the CPU side assume itself have one single NIC connect to + this switch. In this case, the port-id will be 2 only. + +-----+---------------+ + | CPU | + +-+-+-+---+-+-+-+-+-+-+ + | | service| port(2) + debug +------------+ + port | switch | + (0,1) +-+-+-+-+-+-++ + | | | | | | + external port + +- local-mac-address: mac addr of the ethernet interface + +Example: + + ethernet@0{ + compatible = "hisilicon,hns-nic-v1"; + ae-name = "dsaf0"; + port-id = <0>; + local-mac-address = [a2 14 e4 4b 56 76]; + }; diff --git a/Documentation/networking/ieee802154.txt b/Documentation/networking/ieee802154.txt index 1700756af057..aa69ccc481db 100644 --- a/Documentation/networking/ieee802154.txt +++ b/Documentation/networking/ieee802154.txt @@ -7,11 +7,11 @@ Introduction The IEEE 802.15.4 working group focuses on standardization of bottom two layers: Medium Access Control (MAC) and Physical (PHY). And there are mainly two options available for upper layers: - - ZigBee - proprietary protocol from ZigBee Alliance - - 6LowPAN - IPv6 networking over low rate personal area networks + - ZigBee - proprietary protocol from the ZigBee Alliance + - 6LoWPAN - IPv6 networking over low rate personal area networks -The Linux-ZigBee project goal is to provide complete implementation -of IEEE 802.15.4 and 6LoWPAN protocols. IEEE 802.15.4 is a stack +The linux-wpan project goal is to provide a complete implementation +of the IEEE 802.15.4 and 6LoWPAN protocols. IEEE 802.15.4 is a stack of protocols for organizing Low-Rate Wireless Personal Area Networks. The stack is composed of three main parts: diff --git a/arch/arm/boot/dts/am4372.dtsi b/arch/arm/boot/dts/am4372.dtsi index 0447c04a40cc..d83ff9c9701e 100644 --- a/arch/arm/boot/dts/am4372.dtsi +++ b/arch/arm/boot/dts/am4372.dtsi @@ -591,6 +591,7 @@ cpts_clock_mult = <0x80000000>; cpts_clock_shift = <29>; ranges; + syscon = <&scm_conf>; davinci_mdio: mdio@4a101000 { compatible = "ti,am4372-mdio","ti,davinci_mdio"; diff --git a/arch/arm/boot/dts/dra7.dtsi b/arch/arm/boot/dts/dra7.dtsi index 5d65db9ebc2b..76c739d3da75 100644 --- a/arch/arm/boot/dts/dra7.dtsi +++ b/arch/arm/boot/dts/dra7.dtsi @@ -1447,6 +1447,7 @@ <GIC_SPI 336 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 337 IRQ_TYPE_LEVEL_HIGH>; ranges; + syscon = <&scm_conf>; status = "disabled"; davinci_mdio: mdio@48485000 { diff --git a/arch/arm64/boot/dts/hisilicon/hip05_hns.dtsi b/arch/arm64/boot/dts/hisilicon/hip05_hns.dtsi new file mode 100644 index 000000000000..35005866a34e --- /dev/null +++ b/arch/arm64/boot/dts/hisilicon/hip05_hns.dtsi @@ -0,0 +1,193 @@ +soc0: soc@000000000 { + #address-cells = <2>; + #size-cells = <2>; + device_type = "soc"; + compatible = "simple-bus"; + ranges = <0x0 0x0 0x0 0x0 0x1 0x0>; + chip-id = <0>; + + soc0_mdio0: mdio@803c0000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "hisilicon,hns-mdio"; + reg = <0x0 0x803c0000 0x0 0x10000 + 0x0 0x80000000 0x0 0x10000>; + + soc0_phy4: ethernet-phy@4 { + reg = <0x0>; + device_type = "ethernet-phy"; + compatible = "ethernet-phy-ieee802.3-c22"; + }; + soc0_phy5: ethernet-phy@5 { + reg = <0x1>; + device_type = "ethernet-phy"; + compatible = "ethernet-phy-ieee802.3-c22"; + }; + }; + + dsa: dsa@c7000000 { + compatible = "hisilicon,hns-dsaf-v1"; + dsa_name = "dsaf0"; + mode = "6port-16rss"; + interrupt-parent = <&mbigen_dsa>; + + reg = <0x0 0xC0000000 0x0 0x420000 + 0x0 0xC2000000 0x0 0x300000 + 0x0 0xc5000000 0x0 0x890000 + 0x0 0xc7000000 0x0 0x60000 + >; + + phy-handle = <0 0 0 0 &soc0_phy4 &soc0_phy5 0 0>; + interrupts = < + /* [14] ge fifo err 8 / xge 6**/ + 149 0x4 150 0x4 151 0x4 152 0x4 + 153 0x4 154 0x4 26 0x4 27 0x4 + 155 0x4 156 0x4 157 0x4 158 0x4 159 0x4 160 0x4 + /* [12] rcb com 4*3**/ + 0x6 0x4 0x7 0x4 0x8 0x4 0x9 0x4 + 16 0x4 17 0x4 18 0x4 19 0x4 + 22 0x4 23 0x4 24 0x4 25 0x4 + /* [8] ppe tnl 0-7***/ + 0x0 0x4 0x1 0x4 0x2 0x4 0x3 0x4 + 0x4 0x4 0x5 0x4 12 0x4 13 0x4 + /* [21] dsaf event int 3+18**/ + 128 0x4 129 0x4 130 0x4 + 0x83 0x4 0x84 0x4 0x85 0x4 0x86 0x4 0x87 0x4 0x88 0x4 + 0x89 0x4 0x8a 0x4 0x8b 0x4 0x8c 0x4 0x8d 0x4 0x8e 0x4 + 0x8f 0x4 0x90 0x4 0x91 0x4 0x92 0x4 0x93 0x4 0x94 0x4 + /* [4] debug rcb 2*2*/ + 0xe 0x1 0xf 0x1 0x14 0x1 0x15 0x1 + /* [256] sevice rcb 2*128*/ + 0x180 0x1 0x181 0x1 0x182 0x1 0x183 0x1 + 0x184 0x1 0x185 0x1 0x186 0x1 0x187 0x1 + 0x188 0x1 0x189 0x1 0x18a 0x1 0x18b 0x1 + 0x18c 0x1 0x18d 0x1 0x18e 0x1 0x18f 0x1 + 0x190 0x1 0x191 0x1 0x192 0x1 0x193 0x1 + 0x194 0x1 0x195 0x1 0x196 0x1 0x197 0x1 + 0x198 0x1 0x199 0x1 0x19a 0x1 0x19b 0x1 + 0x19c 0x1 0x19d 0x1 0x19e 0x1 0x19f 0x1 + 0x1a0 0x1 0x1a1 0x1 0x1a2 0x1 0x1a3 0x1 + 0x1a4 0x1 0x1a5 0x1 0x1a6 0x1 0x1a7 0x1 + 0x1a8 0x1 0x1a9 0x1 0x1aa 0x1 0x1ab 0x1 + 0x1ac 0x1 0x1ad 0x1 0x1ae 0x1 0x1af 0x1 + 0x1b0 0x1 0x1b1 0x1 0x1b2 0x1 0x1b3 0x1 + 0x1b4 0x1 0x1b5 0x1 0x1b6 0x1 0x1b7 0x1 + 0x1b8 0x1 0x1b9 0x1 0x1ba 0x1 0x1bb 0x1 + 0x1bc 0x1 0x1bd 0x1 0x1be 0x1 0x1bf 0x1 + 0x1c0 0x1 0x1c1 0x1 0x1c2 0x1 0x1c3 0x1 + 0x1c4 0x1 0x1c5 0x1 0x1c6 0x1 0x1c7 0x1 + 0x1c8 0x1 0x1c9 0x1 0x1ca 0x1 0x1cb 0x1 + 0x1cc 0x1 0x1cd 0x1 0x1ce 0x1 0x1cf 0x1 + 0x1d0 0x1 0x1d1 0x1 0x1d2 0x1 0x1d3 0x1 + 0x1d4 0x1 0x1d5 0x1 0x1d6 0x1 0x1d7 0x1 + 0x1d8 0x1 0x1d9 0x1 0x1da 0x1 0x1db 0x1 + 0x1dc 0x1 0x1dd 0x1 0x1de 0x1 0x1df 0x1 + 0x1e0 0x1 0x1e1 0x1 0x1e2 0x1 0x1e3 0x1 + 0x1e4 0x1 0x1e5 0x1 0x1e6 0x1 0x1e7 0x1 + 0x1e8 0x1 0x1e9 0x1 0x1ea 0x1 0x1eb 0x1 + 0x1ec 0x1 0x1ed 0x1 0x1ee 0x1 0x1ef 0x1 + 0x1f0 0x1 0x1f1 0x1 0x1f2 0x1 0x1f3 0x1 + 0x1f4 0x1 0x1f5 0x1 0x1f6 0x1 0x1f7 0x1 + 0x1f8 0x1 0x1f9 0x1 0x1fa 0x1 0x1fb 0x1 + 0x1fc 0x1 0x1fd 0x1 0x1fe 0x1 0x1ff 0x1 + 0x200 0x1 0x201 0x1 0x202 0x1 0x203 0x1 + 0x204 0x1 0x205 0x1 0x206 0x1 0x207 0x1 + 0x208 0x1 0x209 0x1 0x20a 0x1 0x20b 0x1 + 0x20c 0x1 0x20d 0x1 0x20e 0x1 0x20f 0x1 + 0x210 0x1 0x211 0x1 0x212 0x1 0x213 0x1 + 0x214 0x1 0x215 0x1 0x216 0x1 0x217 0x1 + 0x218 0x1 0x219 0x1 0x21a 0x1 0x21b 0x1 + 0x21c 0x1 0x21d 0x1 0x21e 0x1 0x21f 0x1 + 0x220 0x1 0x221 0x1 0x222 0x1 0x223 0x1 + 0x224 0x1 0x225 0x1 0x226 0x1 0x227 0x1 + 0x228 0x1 0x229 0x1 0x22a 0x1 0x22b 0x1 + 0x22c 0x1 0x22d 0x1 0x22e 0x1 0x22f 0x1 + 0x230 0x1 0x231 0x1 0x232 0x1 0x233 0x1 + 0x234 0x1 0x235 0x1 0x236 0x1 0x237 0x1 + 0x238 0x1 0x239 0x1 0x23a 0x1 0x23b 0x1 + 0x23c 0x1 0x23d 0x1 0x23e 0x1 0x23f 0x1 + 0x240 0x1 0x241 0x1 0x242 0x1 0x243 0x1 + 0x244 0x1 0x245 0x1 0x246 0x1 0x247 0x1 + 0x248 0x1 0x249 0x1 0x24a 0x1 0x24b 0x1 + 0x24c 0x1 0x24d 0x1 0x24e 0x1 0x24f 0x1 + 0x250 0x1 0x251 0x1 0x252 0x1 0x253 0x1 + 0x254 0x1 0x255 0x1 0x256 0x1 0x257 0x1 + 0x258 0x1 0x259 0x1 0x25a 0x1 0x25b 0x1 + 0x25c 0x1 0x25d 0x1 0x25e 0x1 0x25f 0x1 + 0x260 0x1 0x261 0x1 0x262 0x1 0x263 0x1 + 0x264 0x1 0x265 0x1 0x266 0x1 0x267 0x1 + 0x268 0x1 0x269 0x1 0x26a 0x1 0x26b 0x1 + 0x26c 0x1 0x26d 0x1 0x26e 0x1 0x26f 0x1 + 0x270 0x1 0x271 0x1 0x272 0x1 0x273 0x1 + 0x274 0x1 0x275 0x1 0x276 0x1 0x277 0x1 + 0x278 0x1 0x279 0x1 0x27a 0x1 0x27b 0x1 + 0x27c 0x1 0x27d 0x1 0x27e 0x1 0x27f 0x1>; + buf-size = <4096>; + desc-num = <1024>; + dma-coherent; + }; + + eth0: ethernet@0{ + compatible = "hisilicon,hns-nic-v1"; + ae-name = "dsaf0"; + port-id = <0>; + local-mac-address = [00 00 00 01 00 58]; + status = "disabled"; + dma-coherent; + }; + eth1: ethernet@1{ + compatible = "hisilicon,hns-nic-v1"; + ae-name = "dsaf0"; + port-id = <1>; + status = "disabled"; + dma-coherent; + }; + eth2: ethernet@2{ + compatible = "hisilicon,hns-nic-v1"; + ae-name = "dsaf0"; + port-id = <2>; + local-mac-address = [00 00 00 01 00 5a]; + status = "disabled"; + dma-coherent; + }; + eth3: ethernet@3{ + compatible = "hisilicon,hns-nic-v1"; + ae-name = "dsaf0"; + port-id = <3>; + local-mac-address = [00 00 00 01 00 5b]; + status = "disabled"; + dma-coherent; + }; + eth4: ethernet@4{ + compatible = "hisilicon,hns-nic-v1"; + ae-name = "dsaf0"; + port-id = <4>; + local-mac-address = [00 00 00 01 00 5c]; + status = "disabled"; + dma-coherent; + }; + eth5: ethernet@5{ + compatible = "hisilicon,hns-nic-v1"; + ae-name = "dsaf0"; + port-id = <5>; + local-mac-address = [00 00 00 01 00 5d]; + status = "disabled"; + dma-coherent; + }; + eth6: ethernet@6{ + compatible = "hisilicon,hns-nic-v1"; + ae-name = "dsaf0"; + port-id = <6>; + local-mac-address = [00 00 00 01 00 5e]; + status = "disabled"; + dma-coherent; + }; + eth7: ethernet@7{ + compatible = "hisilicon,hns-nic-v1"; + ae-name = "dsaf0"; + port-id = <7>; + local-mac-address = [00 00 00 01 00 5f]; + status = "disabled"; + dma-coherent; + }; +}; diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c index a00bb82eb7c6..772a2770710c 100644 --- a/drivers/bluetooth/bt3c_cs.c +++ b/drivers/bluetooth/bt3c_cs.c @@ -453,7 +453,8 @@ static int bt3c_load_firmware(struct bt3c_info *info, { char *ptr = (char *) firmware; char b[9]; - unsigned int iobase, size, addr, fcs, tmp; + unsigned int iobase, tmp; + unsigned long size, addr, fcs; int i, err = 0; iobase = info->p_dev->resource[0]->start; @@ -478,15 +479,18 @@ static int bt3c_load_firmware(struct bt3c_info *info, memset(b, 0, sizeof(b)); memcpy(b, ptr + 2, 2); - size = simple_strtoul(b, NULL, 16); + if (kstrtoul(b, 16, &size) < 0) + return -EINVAL; memset(b, 0, sizeof(b)); memcpy(b, ptr + 4, 8); - addr = simple_strtoul(b, NULL, 16); + if (kstrtoul(b, 16, &addr) < 0) + return -EINVAL; memset(b, 0, sizeof(b)); memcpy(b, ptr + (size * 2) + 2, 2); - fcs = simple_strtoul(b, NULL, 16); + if (kstrtoul(b, 16, &fcs) < 0) + return -EINVAL; memset(b, 0, sizeof(b)); for (tmp = 0, i = 0; i < size; i++) { diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c index 048423fd83bf..9e18988375eb 100644 --- a/drivers/bluetooth/btintel.c +++ b/drivers/bluetooth/btintel.c @@ -22,6 +22,7 @@ */ #include <linux/module.h> +#include <linux/firmware.h> #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> @@ -169,6 +170,51 @@ int btintel_secure_send(struct hci_dev *hdev, u8 fragment_type, u32 plen, } EXPORT_SYMBOL_GPL(btintel_secure_send); +int btintel_load_ddc_config(struct hci_dev *hdev, const char *ddc_name) +{ + const struct firmware *fw; + struct sk_buff *skb; + const u8 *fw_ptr; + int err; + + err = request_firmware_direct(&fw, ddc_name, &hdev->dev); + if (err < 0) { + bt_dev_err(hdev, "Failed to load Intel DDC file %s (%d)", + ddc_name, err); + return err; + } + + bt_dev_info(hdev, "Found Intel DDC parameters: %s", ddc_name); + + fw_ptr = fw->data; + + /* DDC file contains one or more DDC structure which has + * Length (1 byte), DDC ID (2 bytes), and DDC value (Length - 2). + */ + while (fw->size > fw_ptr - fw->data) { + u8 cmd_plen = fw_ptr[0] + sizeof(u8); + + skb = __hci_cmd_sync(hdev, 0xfc8b, cmd_plen, fw_ptr, + HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + bt_dev_err(hdev, "Failed to send Intel_Write_DDC (%ld)", + PTR_ERR(skb)); + release_firmware(fw); + return PTR_ERR(skb); + } + + fw_ptr += cmd_plen; + kfree_skb(skb); + } + + release_firmware(fw); + + bt_dev_info(hdev, "Applying Intel DDC parameters completed"); + + return 0; +} +EXPORT_SYMBOL_GPL(btintel_load_ddc_config); + MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); MODULE_DESCRIPTION("Bluetooth support for Intel devices ver " VERSION); MODULE_VERSION(VERSION); diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h index b278d14758d5..52deaf2817cf 100644 --- a/drivers/bluetooth/btintel.h +++ b/drivers/bluetooth/btintel.h @@ -78,6 +78,7 @@ void btintel_hw_error(struct hci_dev *hdev, u8 code); void btintel_version_info(struct hci_dev *hdev, struct intel_version *ver); int btintel_secure_send(struct hci_dev *hdev, u8 fragment_type, u32 plen, const void *param); +int btintel_load_ddc_config(struct hci_dev *hdev, const char *ddc_name); #else @@ -95,7 +96,8 @@ static inline void btintel_hw_error(struct hci_dev *hdev, u8 code) { } -static void btintel_version_info(struct hci_dev *hdev, struct intel_version *ver) +static inline void btintel_version_info(struct hci_dev *hdev, + struct intel_version *ver) { } @@ -105,4 +107,10 @@ static inline int btintel_secure_send(struct hci_dev *hdev, u8 fragment_type, return -EOPNOTSUPP; } +static inline int btintel_load_ddc_config(struct hci_dev *hdev, + const char *ddc_name) +{ + return -EOPNOTSUPP; +} + #endif diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c index de05deb444ce..bc110f61c8b1 100644 --- a/drivers/bluetooth/btmrvl_main.c +++ b/drivers/bluetooth/btmrvl_main.c @@ -377,20 +377,6 @@ static int btmrvl_tx_pkt(struct btmrvl_private *priv, struct sk_buff *skb) return -EINVAL; } - if (skb_headroom(skb) < BTM_HEADER_LEN) { - struct sk_buff *tmp = skb; - - skb = skb_realloc_headroom(skb, BTM_HEADER_LEN); - if (!skb) { - BT_ERR("Tx Error: realloc_headroom failed %d", - BTM_HEADER_LEN); - skb = tmp; - return -EINVAL; - } - - kfree_skb(tmp); - } - skb_push(skb, BTM_HEADER_LEN); /* header type: byte[3] diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index b6aceaf82aa8..dfaaea2efecb 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -1277,6 +1277,20 @@ static void btusb_work(struct work_struct *work) clear_bit(BTUSB_ISOC_RUNNING, &data->flags); usb_kill_anchored_urbs(&data->isoc_anchor); + /* When isochronous alternate setting needs to be + * changed, because SCO connection has been added + * or removed, a packet fragment may be left in the + * reassembling state. This could lead to wrongly + * assembled fragments. + * + * Clear outstanding fragment when selecting a new + * alternate setting. + */ + spin_lock(&data->rxlock); + kfree_skb(data->sco_skb); + data->sco_skb = NULL; + spin_unlock(&data->rxlock); + if (__set_isoc_interface(hdev, new_alts) < 0) return; } @@ -1348,7 +1362,9 @@ static int btusb_setup_csr(struct hci_dev *hdev) rp = (struct hci_rp_read_local_version *)skb->data; - if (le16_to_cpu(rp->manufacturer) != 10) { + /* Detect controllers which aren't real CSR ones. */ + if (le16_to_cpu(rp->manufacturer) != 10 || + le16_to_cpu(rp->lmp_subver) == 0x0c5c) { /* Clear the reset quirk since this is not an actual * early Bluetooth 1.1 device from CSR. */ @@ -2217,36 +2233,7 @@ done: * The device can work without DDC parameters, so even if it fails * to load the file, no need to fail the setup. */ - err = request_firmware_direct(&fw, fwname, &hdev->dev); - if (err < 0) - return 0; - - BT_INFO("%s: Found Intel DDC parameters: %s", hdev->name, fwname); - - fw_ptr = fw->data; - - /* DDC file contains one or more DDC structure which has - * Length (1 byte), DDC ID (2 bytes), and DDC value (Length - 2). - */ - while (fw->size > fw_ptr - fw->data) { - u8 cmd_plen = fw_ptr[0] + sizeof(u8); - - skb = __hci_cmd_sync(hdev, 0xfc8b, cmd_plen, fw_ptr, - HCI_INIT_TIMEOUT); - if (IS_ERR(skb)) { - BT_ERR("%s: Failed to send Intel_Write_DDC (%ld)", - hdev->name, PTR_ERR(skb)); - release_firmware(fw); - return PTR_ERR(skb); - } - - fw_ptr += cmd_plen; - kfree_skb(skb); - } - - release_firmware(fw); - - BT_INFO("%s: Applying Intel DDC parameters completed", hdev->name); + btintel_load_ddc_config(hdev, fwname); return 0; } @@ -2782,7 +2769,7 @@ static int btusb_probe(struct usb_interface *intf, set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); /* Fake CSR devices with broken commands */ - if (bcdDevice <= 0x100) + if (bcdDevice <= 0x100 || bcdDevice == 0x134) hdev->setup = btusb_setup_csr; set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks); diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index 835bfab88ef5..f30654149c63 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -31,6 +31,7 @@ #include <linux/clk.h> #include <linux/gpio/consumer.h> #include <linux/tty.h> +#include <linux/interrupt.h> #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> @@ -51,6 +52,8 @@ struct bcm_device { bool clk_enabled; u32 init_speed; + int irq; + u8 irq_polarity; #ifdef CONFIG_PM_SLEEP struct hci_uart *hu; @@ -66,7 +69,7 @@ struct bcm_data { }; /* List of BCM BT UART devices */ -static DEFINE_SPINLOCK(bcm_device_lock); +static DEFINE_MUTEX(bcm_device_lock); static LIST_HEAD(bcm_device_list); static int bcm_set_baudrate(struct hci_uart *hu, unsigned int speed) @@ -80,7 +83,7 @@ static int bcm_set_baudrate(struct hci_uart *hu, unsigned int speed) clock.type = BCM_UART_CLOCK_48MHZ; - BT_DBG("%s: Set Controller clock (%d)", hdev->name, clock.type); + bt_dev_dbg(hdev, "Set Controller clock (%d)", clock.type); /* This Broadcom specific command changes the UART's controller * clock for baud rate > 3000000. @@ -88,15 +91,15 @@ static int bcm_set_baudrate(struct hci_uart *hu, unsigned int speed) skb = __hci_cmd_sync(hdev, 0xfc45, 1, &clock, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { int err = PTR_ERR(skb); - BT_ERR("%s: BCM: failed to write clock command (%d)", - hdev->name, err); + bt_dev_err(hdev, "BCM: failed to write clock (%d)", + err); return err; } kfree_skb(skb); } - BT_DBG("%s: Set Controller UART speed to %d bit/s", hdev->name, speed); + bt_dev_dbg(hdev, "Set Controller UART speed to %d bit/s", speed); param.zero = cpu_to_le16(0); param.baud_rate = cpu_to_le32(speed); @@ -108,8 +111,8 @@ static int bcm_set_baudrate(struct hci_uart *hu, unsigned int speed) HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { int err = PTR_ERR(skb); - BT_ERR("%s: BCM: failed to write update baudrate command (%d)", - hdev->name, err); + bt_dev_err(hdev, "BCM: failed to write update baudrate (%d)", + err); return err; } @@ -149,12 +152,92 @@ static int bcm_gpio_set_power(struct bcm_device *dev, bool powered) return 0; } +#ifdef CONFIG_PM_SLEEP +static irqreturn_t bcm_host_wake(int irq, void *data) +{ + struct bcm_device *bdev = data; + + bt_dev_dbg(bdev, "Host wake IRQ"); + + return IRQ_HANDLED; +} + +static int bcm_request_irq(struct bcm_data *bcm) +{ + struct bcm_device *bdev = bcm->dev; + int err = 0; + + /* If this is not a platform device, do not enable PM functionalities */ + mutex_lock(&bcm_device_lock); + if (!bcm_device_exists(bdev)) { + err = -ENODEV; + goto unlock; + } + + if (bdev->irq > 0) { + err = devm_request_irq(&bdev->pdev->dev, bdev->irq, + bcm_host_wake, IRQF_TRIGGER_RISING, + "host_wake", bdev); + if (err) + goto unlock; + + device_init_wakeup(&bdev->pdev->dev, true); + } + +unlock: + mutex_unlock(&bcm_device_lock); + + return err; +} + +static const struct bcm_set_sleep_mode default_sleep_params = { + .sleep_mode = 1, /* 0=Disabled, 1=UART, 2=Reserved, 3=USB */ + .idle_host = 2, /* idle threshold HOST, in 300ms */ + .idle_dev = 2, /* idle threshold device, in 300ms */ + .bt_wake_active = 1, /* BT_WAKE active mode: 1 = high, 0 = low */ + .host_wake_active = 0, /* HOST_WAKE active mode: 1 = high, 0 = low */ + .allow_host_sleep = 1, /* Allow host sleep in SCO flag */ + .combine_modes = 0, /* Combine sleep and LPM flag */ + .tristate_control = 0, /* Allow tri-state control of UART tx flag */ + /* Irrelevant USB flags */ + .usb_auto_sleep = 0, + .usb_resume_timeout = 0, + .pulsed_host_wake = 0, + .break_to_host = 0 +}; + +static int bcm_setup_sleep(struct hci_uart *hu) +{ + struct bcm_data *bcm = hu->priv; + struct sk_buff *skb; + struct bcm_set_sleep_mode sleep_params = default_sleep_params; + + sleep_params.host_wake_active = !bcm->dev->irq_polarity; + + skb = __hci_cmd_sync(hu->hdev, 0xfc27, sizeof(sleep_params), + &sleep_params, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + int err = PTR_ERR(skb); + bt_dev_err(hu->hdev, "Sleep VSC failed (%d)", err); + return err; + } + kfree_skb(skb); + + bt_dev_dbg(hu->hdev, "Set Sleep Parameters VSC succeeded"); + + return 0; +} +#else +static inline int bcm_request_irq(struct bcm_data *bcm) { return 0; } +static inline int bcm_setup_sleep(struct hci_uart *hu) { return 0; } +#endif + static int bcm_open(struct hci_uart *hu) { struct bcm_data *bcm; struct list_head *p; - BT_DBG("hu %p", hu); + bt_dev_dbg(hu->hdev, "hu %p", hu); bcm = kzalloc(sizeof(*bcm), GFP_KERNEL); if (!bcm) @@ -164,7 +247,7 @@ static int bcm_open(struct hci_uart *hu) hu->priv = bcm; - spin_lock(&bcm_device_lock); + mutex_lock(&bcm_device_lock); list_for_each(p, &bcm_device_list) { struct bcm_device *dev = list_entry(p, struct bcm_device, list); @@ -178,14 +261,12 @@ static int bcm_open(struct hci_uart *hu) #ifdef CONFIG_PM_SLEEP dev->hu = hu; #endif + bcm_gpio_set_power(bcm->dev, true); break; } } - if (bcm->dev) - bcm_gpio_set_power(bcm->dev, true); - - spin_unlock(&bcm_device_lock); + mutex_unlock(&bcm_device_lock); return 0; } @@ -193,18 +274,24 @@ static int bcm_open(struct hci_uart *hu) static int bcm_close(struct hci_uart *hu) { struct bcm_data *bcm = hu->priv; + struct bcm_device *bdev = bcm->dev; - BT_DBG("hu %p", hu); + bt_dev_dbg(hu->hdev, "hu %p", hu); /* Protect bcm->dev against removal of the device or driver */ - spin_lock(&bcm_device_lock); - if (bcm_device_exists(bcm->dev)) { - bcm_gpio_set_power(bcm->dev, false); + mutex_lock(&bcm_device_lock); + if (bcm_device_exists(bdev)) { + bcm_gpio_set_power(bdev, false); #ifdef CONFIG_PM_SLEEP - bcm->dev->hu = NULL; + if (device_can_wakeup(&bdev->pdev->dev)) { + devm_free_irq(&bdev->pdev->dev, bdev->irq, bdev); + device_init_wakeup(&bdev->pdev->dev, false); + } + + bdev->hu = NULL; #endif } - spin_unlock(&bcm_device_lock); + mutex_unlock(&bcm_device_lock); skb_queue_purge(&bcm->txq); kfree_skb(bcm->rx_skb); @@ -218,7 +305,7 @@ static int bcm_flush(struct hci_uart *hu) { struct bcm_data *bcm = hu->priv; - BT_DBG("hu %p", hu); + bt_dev_dbg(hu->hdev, "hu %p", hu); skb_queue_purge(&bcm->txq); @@ -227,12 +314,13 @@ static int bcm_flush(struct hci_uart *hu) static int bcm_setup(struct hci_uart *hu) { + struct bcm_data *bcm = hu->priv; char fw_name[64]; const struct firmware *fw; unsigned int speed; int err; - BT_DBG("hu %p", hu); + bt_dev_dbg(hu->hdev, "hu %p", hu); hu->hdev->set_bdaddr = btbcm_set_bdaddr; @@ -242,13 +330,13 @@ static int bcm_setup(struct hci_uart *hu) err = request_firmware(&fw, fw_name, &hu->hdev->dev); if (err < 0) { - BT_INFO("%s: BCM: Patch %s not found", hu->hdev->name, fw_name); + bt_dev_info(hu->hdev, "BCM: Patch %s not found", fw_name); return 0; } err = btbcm_patchram(hu->hdev, fw); if (err) { - BT_INFO("%s: BCM: Patch failed (%d)", hu->hdev->name, err); + bt_dev_info(hu->hdev, "BCM: Patch failed (%d)", err); goto finalize; } @@ -281,6 +369,12 @@ finalize: release_firmware(fw); err = btbcm_finalize(hu->hdev); + if (err) + return err; + + err = bcm_request_irq(bcm); + if (!err) + err = bcm_setup_sleep(hu); return err; } @@ -302,7 +396,7 @@ static int bcm_recv(struct hci_uart *hu, const void *data, int count) bcm_recv_pkts, ARRAY_SIZE(bcm_recv_pkts)); if (IS_ERR(bcm->rx_skb)) { int err = PTR_ERR(bcm->rx_skb); - BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err); + bt_dev_err(hu->hdev, "Frame reassembly failed (%d)", err); bcm->rx_skb = NULL; return err; } @@ -314,7 +408,7 @@ static int bcm_enqueue(struct hci_uart *hu, struct sk_buff *skb) { struct bcm_data *bcm = hu->priv; - BT_DBG("hu %p skb %p", hu, skb); + bt_dev_dbg(hu->hdev, "hu %p skb %p", hu, skb); /* Prepend skb with frame type */ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); @@ -335,10 +429,11 @@ static struct sk_buff *bcm_dequeue(struct hci_uart *hu) static int bcm_suspend(struct device *dev) { struct bcm_device *bdev = platform_get_drvdata(to_platform_device(dev)); + int error; - BT_DBG("suspend (%p): is_suspended %d", bdev, bdev->is_suspended); + bt_dev_dbg(bdev, "suspend: is_suspended %d", bdev->is_suspended); - spin_lock(&bcm_device_lock); + mutex_lock(&bcm_device_lock); if (!bdev->hu) goto unlock; @@ -353,12 +448,18 @@ static int bcm_suspend(struct device *dev) /* Suspend the device */ if (bdev->device_wakeup) { gpiod_set_value(bdev->device_wakeup, false); - BT_DBG("suspend, delaying 15 ms"); + bt_dev_dbg(bdev, "suspend, delaying 15 ms"); mdelay(15); } + if (device_may_wakeup(&bdev->pdev->dev)) { + error = enable_irq_wake(bdev->irq); + if (!error) + bt_dev_dbg(bdev, "BCM irq: enabled"); + } + unlock: - spin_unlock(&bcm_device_lock); + mutex_unlock(&bcm_device_lock); return 0; } @@ -368,16 +469,21 @@ static int bcm_resume(struct device *dev) { struct bcm_device *bdev = platform_get_drvdata(to_platform_device(dev)); - BT_DBG("resume (%p): is_suspended %d", bdev, bdev->is_suspended); + bt_dev_dbg(bdev, "resume: is_suspended %d", bdev->is_suspended); - spin_lock(&bcm_device_lock); + mutex_lock(&bcm_device_lock); if (!bdev->hu) goto unlock; + if (device_may_wakeup(&bdev->pdev->dev)) { + disable_irq_wake(bdev->irq); + bt_dev_dbg(bdev, "BCM irq: disabled"); + } + if (bdev->device_wakeup) { gpiod_set_value(bdev->device_wakeup, true); - BT_DBG("resume, delaying 15 ms"); + bt_dev_dbg(bdev, "resume, delaying 15 ms"); mdelay(15); } @@ -389,7 +495,7 @@ static int bcm_resume(struct device *dev) } unlock: - spin_unlock(&bcm_device_lock); + mutex_unlock(&bcm_device_lock); return 0; } @@ -397,10 +503,12 @@ unlock: static const struct acpi_gpio_params device_wakeup_gpios = { 0, 0, false }; static const struct acpi_gpio_params shutdown_gpios = { 1, 0, false }; +static const struct acpi_gpio_params host_wakeup_gpios = { 2, 0, false }; static const struct acpi_gpio_mapping acpi_bcm_default_gpios[] = { { "device-wakeup-gpios", &device_wakeup_gpios, 1 }, { "shutdown-gpios", &shutdown_gpios, 1 }, + { "host-wakeup-gpios", &host_wakeup_gpios, 1 }, { }, }; @@ -408,13 +516,30 @@ static const struct acpi_gpio_mapping acpi_bcm_default_gpios[] = { static int bcm_resource(struct acpi_resource *ares, void *data) { struct bcm_device *dev = data; - - if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) { - struct acpi_resource_uart_serialbus *sb; - + struct acpi_resource_extended_irq *irq; + struct acpi_resource_gpio *gpio; + struct acpi_resource_uart_serialbus *sb; + + switch (ares->type) { + case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: + irq = &ares->data.extended_irq; + dev->irq_polarity = irq->polarity; + break; + + case ACPI_RESOURCE_TYPE_GPIO: + gpio = &ares->data.gpio; + if (gpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT) + dev->irq_polarity = gpio->polarity; + break; + + case ACPI_RESOURCE_TYPE_SERIAL_BUS: sb = &ares->data.uart_serial_bus; if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_UART) dev->init_speed = sb->default_baud_rate; + break; + + default: + break; } /* Always tell the ACPI core to skip this resource */ @@ -453,6 +578,21 @@ static int bcm_acpi_probe(struct bcm_device *dev) if (IS_ERR(dev->shutdown)) return PTR_ERR(dev->shutdown); + /* IRQ can be declared in ACPI table as Interrupt or GpioInt */ + dev->irq = platform_get_irq(pdev, 0); + if (dev->irq <= 0) { + struct gpio_desc *gpio; + + gpio = devm_gpiod_get_optional(&pdev->dev, "host-wakeup", + GPIOD_IN); + if (IS_ERR(gpio)) + return PTR_ERR(gpio); + + dev->irq = gpiod_to_irq(gpio); + } + + dev_info(&pdev->dev, "BCM irq: %d\n", dev->irq); + /* Make sure at-least one of the GPIO is defined and that * a name is specified for this instance */ @@ -504,9 +644,9 @@ static int bcm_probe(struct platform_device *pdev) dev_info(&pdev->dev, "%s device registered.\n", dev->name); /* Place this instance on the device list */ - spin_lock(&bcm_device_lock); + mutex_lock(&bcm_device_lock); list_add_tail(&dev->list, &bcm_device_list); - spin_unlock(&bcm_device_lock); + mutex_unlock(&bcm_device_lock); bcm_gpio_set_power(dev, false); @@ -517,9 +657,9 @@ static int bcm_remove(struct platform_device *pdev) { struct bcm_device *dev = platform_get_drvdata(pdev); - spin_lock(&bcm_device_lock); + mutex_lock(&bcm_device_lock); list_del(&dev->list); - spin_unlock(&bcm_device_lock); + mutex_unlock(&bcm_device_lock); acpi_dev_remove_driver_gpios(ACPI_COMPANION(&pdev->dev)); diff --git a/drivers/bluetooth/hci_intel.c b/drivers/bluetooth/hci_intel.c index cf07d1121956..49e25409de67 100644 --- a/drivers/bluetooth/hci_intel.c +++ b/drivers/bluetooth/hci_intel.c @@ -31,6 +31,8 @@ #include <linux/platform_device.h> #include <linux/gpio/consumer.h> #include <linux/acpi.h> +#include <linux/interrupt.h> +#include <linux/pm_runtime.h> #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> @@ -43,19 +45,45 @@ #define STATE_FIRMWARE_LOADED 2 #define STATE_FIRMWARE_FAILED 3 #define STATE_BOOTING 4 +#define STATE_LPM_ENABLED 5 +#define STATE_TX_ACTIVE 6 +#define STATE_SUSPENDED 7 +#define STATE_LPM_TRANSACTION 8 + +#define HCI_LPM_WAKE_PKT 0xf0 +#define HCI_LPM_PKT 0xf1 +#define HCI_LPM_MAX_SIZE 10 +#define HCI_LPM_HDR_SIZE HCI_EVENT_HDR_SIZE + +#define LPM_OP_TX_NOTIFY 0x00 +#define LPM_OP_SUSPEND_ACK 0x02 +#define LPM_OP_RESUME_ACK 0x03 + +#define LPM_SUSPEND_DELAY_MS 1000 + +struct hci_lpm_pkt { + __u8 opcode; + __u8 dlen; + __u8 data[0]; +} __packed; struct intel_device { struct list_head list; struct platform_device *pdev; struct gpio_desc *reset; + struct hci_uart *hu; + struct mutex hu_lock; + int irq; }; static LIST_HEAD(intel_device_list); -static DEFINE_SPINLOCK(intel_device_list_lock); +static DEFINE_MUTEX(intel_device_list_lock); struct intel_data { struct sk_buff *rx_skb; struct sk_buff_head txq; + struct work_struct busy_work; + struct hci_uart *hu; unsigned long flags; }; @@ -101,24 +129,185 @@ static int intel_wait_booting(struct hci_uart *hu) msecs_to_jiffies(1000)); if (err == 1) { - BT_ERR("%s: Device boot interrupted", hu->hdev->name); + bt_dev_err(hu->hdev, "Device boot interrupted"); return -EINTR; } if (err) { - BT_ERR("%s: Device boot timeout", hu->hdev->name); + bt_dev_err(hu->hdev, "Device boot timeout"); return -ETIMEDOUT; } return err; } +#ifdef CONFIG_PM +static int intel_wait_lpm_transaction(struct hci_uart *hu) +{ + struct intel_data *intel = hu->priv; + int err; + + err = wait_on_bit_timeout(&intel->flags, STATE_LPM_TRANSACTION, + TASK_INTERRUPTIBLE, + msecs_to_jiffies(1000)); + + if (err == 1) { + bt_dev_err(hu->hdev, "LPM transaction interrupted"); + return -EINTR; + } + + if (err) { + bt_dev_err(hu->hdev, "LPM transaction timeout"); + return -ETIMEDOUT; + } + + return err; +} + +static int intel_lpm_suspend(struct hci_uart *hu) +{ + static const u8 suspend[] = { 0x01, 0x01, 0x01 }; + struct intel_data *intel = hu->priv; + struct sk_buff *skb; + + if (!test_bit(STATE_LPM_ENABLED, &intel->flags) || + test_bit(STATE_SUSPENDED, &intel->flags)) + return 0; + + if (test_bit(STATE_TX_ACTIVE, &intel->flags)) + return -EAGAIN; + + bt_dev_dbg(hu->hdev, "Suspending"); + + skb = bt_skb_alloc(sizeof(suspend), GFP_KERNEL); + if (!skb) { + bt_dev_err(hu->hdev, "Failed to alloc memory for LPM packet"); + return -ENOMEM; + } + + memcpy(skb_put(skb, sizeof(suspend)), suspend, sizeof(suspend)); + bt_cb(skb)->pkt_type = HCI_LPM_PKT; + + set_bit(STATE_LPM_TRANSACTION, &intel->flags); + + /* LPM flow is a priority, enqueue packet at list head */ + skb_queue_head(&intel->txq, skb); + hci_uart_tx_wakeup(hu); + + intel_wait_lpm_transaction(hu); + /* Even in case of failure, continue and test the suspended flag */ + + clear_bit(STATE_LPM_TRANSACTION, &intel->flags); + + if (!test_bit(STATE_SUSPENDED, &intel->flags)) { + bt_dev_err(hu->hdev, "Device suspend error"); + return -EINVAL; + } + + bt_dev_dbg(hu->hdev, "Suspended"); + + hci_uart_set_flow_control(hu, true); + + return 0; +} + +static int intel_lpm_resume(struct hci_uart *hu) +{ + struct intel_data *intel = hu->priv; + struct sk_buff *skb; + + if (!test_bit(STATE_LPM_ENABLED, &intel->flags) || + !test_bit(STATE_SUSPENDED, &intel->flags)) + return 0; + + bt_dev_dbg(hu->hdev, "Resuming"); + + hci_uart_set_flow_control(hu, false); + + skb = bt_skb_alloc(0, GFP_KERNEL); + if (!skb) { + bt_dev_err(hu->hdev, "Failed to alloc memory for LPM packet"); + return -ENOMEM; + } + + bt_cb(skb)->pkt_type = HCI_LPM_WAKE_PKT; + + set_bit(STATE_LPM_TRANSACTION, &intel->flags); + + /* LPM flow is a priority, enqueue packet at list head */ + skb_queue_head(&intel->txq, skb); + hci_uart_tx_wakeup(hu); + + intel_wait_lpm_transaction(hu); + /* Even in case of failure, continue and test the suspended flag */ + + clear_bit(STATE_LPM_TRANSACTION, &intel->flags); + + if (test_bit(STATE_SUSPENDED, &intel->flags)) { + bt_dev_err(hu->hdev, "Device resume error"); + return -EINVAL; + } + + bt_dev_dbg(hu->hdev, "Resumed"); + + return 0; +} +#endif /* CONFIG_PM */ + +static int intel_lpm_host_wake(struct hci_uart *hu) +{ + static const u8 lpm_resume_ack[] = { LPM_OP_RESUME_ACK, 0x00 }; + struct intel_data *intel = hu->priv; + struct sk_buff *skb; + + hci_uart_set_flow_control(hu, false); + + clear_bit(STATE_SUSPENDED, &intel->flags); + + skb = bt_skb_alloc(sizeof(lpm_resume_ack), GFP_KERNEL); + if (!skb) { + bt_dev_err(hu->hdev, "Failed to alloc memory for LPM packet"); + return -ENOMEM; + } + + memcpy(skb_put(skb, sizeof(lpm_resume_ack)), lpm_resume_ack, + sizeof(lpm_resume_ack)); + bt_cb(skb)->pkt_type = HCI_LPM_PKT; + + /* LPM flow is a priority, enqueue packet at list head */ + skb_queue_head(&intel->txq, skb); + hci_uart_tx_wakeup(hu); + + bt_dev_dbg(hu->hdev, "Resumed by controller"); + + return 0; +} + +static irqreturn_t intel_irq(int irq, void *dev_id) +{ + struct intel_device *idev = dev_id; + + dev_info(&idev->pdev->dev, "hci_intel irq\n"); + + mutex_lock(&idev->hu_lock); + if (idev->hu) + intel_lpm_host_wake(idev->hu); + mutex_unlock(&idev->hu_lock); + + /* Host/Controller are now LPM resumed, trigger a new delayed suspend */ + pm_runtime_get(&idev->pdev->dev); + pm_runtime_mark_last_busy(&idev->pdev->dev); + pm_runtime_put_autosuspend(&idev->pdev->dev); + + return IRQ_HANDLED; +} + static int intel_set_power(struct hci_uart *hu, bool powered) { struct list_head *p; int err = -ENODEV; - spin_lock(&intel_device_list_lock); + mutex_lock(&intel_device_list_lock); list_for_each(p, &intel_device_list) { struct intel_device *idev = list_entry(p, struct intel_device, @@ -139,13 +328,73 @@ static int intel_set_power(struct hci_uart *hu, bool powered) hu, dev_name(&idev->pdev->dev), powered); gpiod_set_value(idev->reset, powered); + + /* Provide to idev a hu reference which is used to run LPM + * transactions (lpm suspend/resume) from PM callbacks. + * hu needs to be protected against concurrent removing during + * these PM ops. + */ + mutex_lock(&idev->hu_lock); + idev->hu = powered ? hu : NULL; + mutex_unlock(&idev->hu_lock); + + if (idev->irq < 0) + break; + + if (powered && device_can_wakeup(&idev->pdev->dev)) { + err = devm_request_threaded_irq(&idev->pdev->dev, + idev->irq, NULL, + intel_irq, + IRQF_ONESHOT, + "bt-host-wake", idev); + if (err) { + BT_ERR("hu %p, unable to allocate irq-%d", + hu, idev->irq); + break; + } + + device_wakeup_enable(&idev->pdev->dev); + + pm_runtime_set_active(&idev->pdev->dev); + pm_runtime_use_autosuspend(&idev->pdev->dev); + pm_runtime_set_autosuspend_delay(&idev->pdev->dev, + LPM_SUSPEND_DELAY_MS); + pm_runtime_enable(&idev->pdev->dev); + } else if (!powered && device_may_wakeup(&idev->pdev->dev)) { + devm_free_irq(&idev->pdev->dev, idev->irq, idev); + device_wakeup_disable(&idev->pdev->dev); + + pm_runtime_disable(&idev->pdev->dev); + } } - spin_unlock(&intel_device_list_lock); + mutex_unlock(&intel_device_list_lock); return err; } +static void intel_busy_work(struct work_struct *work) +{ + struct list_head *p; + struct intel_data *intel = container_of(work, struct intel_data, + busy_work); + + /* Link is busy, delay the suspend */ + mutex_lock(&intel_device_list_lock); + list_for_each(p, &intel_device_list) { + struct intel_device *idev = list_entry(p, struct intel_device, + list); + + if (intel->hu->tty->dev->parent == idev->pdev->dev.parent) { + pm_runtime_get(&idev->pdev->dev); + pm_runtime_mark_last_busy(&idev->pdev->dev); + pm_runtime_put_autosuspend(&idev->pdev->dev); + break; + } + } + mutex_unlock(&intel_device_list_lock); +} + static int intel_open(struct hci_uart *hu) { struct intel_data *intel; @@ -157,6 +406,9 @@ static int intel_open(struct hci_uart *hu) return -ENOMEM; skb_queue_head_init(&intel->txq); + INIT_WORK(&intel->busy_work, intel_busy_work); + + intel->hu = hu; hu->priv = intel; @@ -172,6 +424,8 @@ static int intel_close(struct hci_uart *hu) BT_DBG("hu %p", hu); + cancel_work_sync(&intel->busy_work); + intel_set_power(hu, false); skb_queue_purge(&intel->txq); @@ -237,11 +491,11 @@ static int intel_set_baudrate(struct hci_uart *hu, unsigned int speed) if (err && err != ETIMEDOUT) return err; - BT_INFO("%s: Change controller speed to %d", hdev->name, speed); + bt_dev_info(hdev, "Change controller speed to %d", speed); speed_cmd[3] = intel_convert_speed(speed); if (speed_cmd[3] == 0xff) { - BT_ERR("%s: Unsupported speed", hdev->name); + bt_dev_err(hdev, "Unsupported speed"); return -EINVAL; } @@ -250,16 +504,15 @@ static int intel_set_baudrate(struct hci_uart *hu, unsigned int speed) */ skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { - BT_ERR("%s: Reading Intel version information failed (%ld)", - hdev->name, PTR_ERR(skb)); + bt_dev_err(hdev, "Reading Intel version information failed (%ld)", + PTR_ERR(skb)); return PTR_ERR(skb); } kfree_skb(skb); skb = bt_skb_alloc(sizeof(speed_cmd), GFP_KERNEL); if (!skb) { - BT_ERR("%s: Failed to allocate memory for baudrate packet", - hdev->name); + bt_dev_err(hdev, "Failed to alloc memory for baudrate packet"); return -ENOMEM; } @@ -284,11 +537,14 @@ static int intel_setup(struct hci_uart *hu) { static const u8 reset_param[] = { 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x04, 0x00 }; + static const u8 lpm_param[] = { 0x03, 0x07, 0x01, 0x0b }; struct intel_data *intel = hu->priv; + struct intel_device *idev = NULL; struct hci_dev *hdev = hu->hdev; struct sk_buff *skb; struct intel_version *ver; struct intel_boot_params *params; + struct list_head *p; const struct firmware *fw; const u8 *fw_ptr; char fwname[64]; @@ -299,7 +555,7 @@ static int intel_setup(struct hci_uart *hu) int speed_change = 0; int err; - BT_DBG("%s", hdev->name); + bt_dev_dbg(hdev, "start intel_setup"); hu->hdev->set_bdaddr = btintel_set_bdaddr; @@ -335,21 +591,21 @@ static int intel_setup(struct hci_uart *hu) */ skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { - BT_ERR("%s: Reading Intel version information failed (%ld)", - hdev->name, PTR_ERR(skb)); + bt_dev_err(hdev, "Reading Intel version information failed (%ld)", + PTR_ERR(skb)); return PTR_ERR(skb); } if (skb->len != sizeof(*ver)) { - BT_ERR("%s: Intel version event size mismatch", hdev->name); + bt_dev_err(hdev, "Intel version event size mismatch"); kfree_skb(skb); return -EILSEQ; } ver = (struct intel_version *)skb->data; if (ver->status) { - BT_ERR("%s: Intel version command failure (%02x)", - hdev->name, ver->status); + bt_dev_err(hdev, "Intel version command failure (%02x)", + ver->status); err = -bt_to_errno(ver->status); kfree_skb(skb); return err; @@ -359,8 +615,8 @@ static int intel_setup(struct hci_uart *hu) * for now only accept this single value. */ if (ver->hw_platform != 0x37) { - BT_ERR("%s: Unsupported Intel hardware platform (%u)", - hdev->name, ver->hw_platform); + bt_dev_err(hdev, "Unsupported Intel hardware platform (%u)", + ver->hw_platform); kfree_skb(skb); return -EINVAL; } @@ -371,8 +627,8 @@ static int intel_setup(struct hci_uart *hu) * when newer hardware variants come along. */ if (ver->hw_variant != 0x0b) { - BT_ERR("%s: Unsupported Intel hardware variant (%u)", - hdev->name, ver->hw_variant); + bt_dev_err(hdev, "Unsupported Intel hardware variant (%u)", + ver->hw_variant); kfree_skb(skb); return -EINVAL; } @@ -403,8 +659,8 @@ static int intel_setup(struct hci_uart *hu) * choice is to return an error and abort the device initialization. */ if (ver->fw_variant != 0x06) { - BT_ERR("%s: Unsupported Intel firmware variant (%u)", - hdev->name, ver->fw_variant); + bt_dev_err(hdev, "Unsupported Intel firmware variant (%u)", + ver->fw_variant); kfree_skb(skb); return -ENODEV; } @@ -416,33 +672,33 @@ static int intel_setup(struct hci_uart *hu) */ skb = __hci_cmd_sync(hdev, 0xfc0d, 0, NULL, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { - BT_ERR("%s: Reading Intel boot parameters failed (%ld)", - hdev->name, PTR_ERR(skb)); + bt_dev_err(hdev, "Reading Intel boot parameters failed (%ld)", + PTR_ERR(skb)); return PTR_ERR(skb); } if (skb->len != sizeof(*params)) { - BT_ERR("%s: Intel boot parameters size mismatch", hdev->name); + bt_dev_err(hdev, "Intel boot parameters size mismatch"); kfree_skb(skb); return -EILSEQ; } params = (struct intel_boot_params *)skb->data; if (params->status) { - BT_ERR("%s: Intel boot parameters command failure (%02x)", - hdev->name, params->status); + bt_dev_err(hdev, "Intel boot parameters command failure (%02x)", + params->status); err = -bt_to_errno(params->status); kfree_skb(skb); return err; } - BT_INFO("%s: Device revision is %u", hdev->name, - le16_to_cpu(params->dev_revid)); + bt_dev_info(hdev, "Device revision is %u", + le16_to_cpu(params->dev_revid)); - BT_INFO("%s: Secure boot is %s", hdev->name, - params->secure_boot ? "enabled" : "disabled"); + bt_dev_info(hdev, "Secure boot is %s", + params->secure_boot ? "enabled" : "disabled"); - BT_INFO("%s: Minimum firmware build %u week %u %u", hdev->name, + bt_dev_info(hdev, "Minimum firmware build %u week %u %u", params->min_fw_build_nn, params->min_fw_build_cw, 2000 + params->min_fw_build_yy); @@ -451,8 +707,8 @@ static int intel_setup(struct hci_uart *hu) * that this bootloader does not send them, then abort the setup. */ if (params->limited_cce != 0x00) { - BT_ERR("%s: Unsupported Intel firmware loading method (%u)", - hdev->name, params->limited_cce); + bt_dev_err(hdev, "Unsupported Intel firmware loading method (%u)", + params->limited_cce); kfree_skb(skb); return -EINVAL; } @@ -461,7 +717,7 @@ static int intel_setup(struct hci_uart *hu) * also be no valid address for the operational firmware. */ if (!bacmp(¶ms->otp_bdaddr, BDADDR_ANY)) { - BT_INFO("%s: No device address configured", hdev->name); + bt_dev_info(hdev, "No device address configured"); set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks); } @@ -476,19 +732,23 @@ static int intel_setup(struct hci_uart *hu) err = request_firmware(&fw, fwname, &hdev->dev); if (err < 0) { - BT_ERR("%s: Failed to load Intel firmware file (%d)", - hdev->name, err); + bt_dev_err(hdev, "Failed to load Intel firmware file (%d)", + err); kfree_skb(skb); return err; } - BT_INFO("%s: Found device firmware: %s", hdev->name, fwname); + bt_dev_info(hdev, "Found device firmware: %s", fwname); + + /* Save the DDC file name for later */ + snprintf(fwname, sizeof(fwname), "intel/ibt-11-%u.ddc", + le16_to_cpu(params->dev_revid)); kfree_skb(skb); if (fw->size < 644) { - BT_ERR("%s: Invalid size of firmware file (%zu)", - hdev->name, fw->size); + bt_dev_err(hdev, "Invalid size of firmware file (%zu)", + fw->size); err = -EBADF; goto done; } @@ -500,8 +760,7 @@ static int intel_setup(struct hci_uart *hu) */ err = btintel_secure_send(hdev, 0x00, 128, fw->data); if (err < 0) { - BT_ERR("%s: Failed to send firmware header (%d)", - hdev->name, err); + bt_dev_err(hdev, "Failed to send firmware header (%d)", err); goto done; } @@ -510,8 +769,8 @@ static int intel_setup(struct hci_uart *hu) */ err = btintel_secure_send(hdev, 0x03, 256, fw->data + 128); if (err < 0) { - BT_ERR("%s: Failed to send firmware public key (%d)", - hdev->name, err); + bt_dev_err(hdev, "Failed to send firmware public key (%d)", + err); goto done; } @@ -520,8 +779,8 @@ static int intel_setup(struct hci_uart *hu) */ err = btintel_secure_send(hdev, 0x02, 256, fw->data + 388); if (err < 0) { - BT_ERR("%s: Failed to send firmware signature (%d)", - hdev->name, err); + bt_dev_err(hdev, "Failed to send firmware signature (%d)", + err); goto done; } @@ -533,8 +792,8 @@ static int intel_setup(struct hci_uart *hu) frag_len += sizeof(*cmd) + cmd->plen; - BT_DBG("%s: patching %td/%zu", hdev->name, - (fw_ptr - fw->data), fw->size); + bt_dev_dbg(hdev, "Patching %td/%zu", (fw_ptr - fw->data), + fw->size); /* The parameter length of the secure send command requires * a 4 byte alignment. It happens so that the firmware file @@ -552,8 +811,8 @@ static int intel_setup(struct hci_uart *hu) */ err = btintel_secure_send(hdev, 0x01, frag_len, fw_ptr); if (err < 0) { - BT_ERR("%s: Failed to send firmware data (%d)", - hdev->name, err); + bt_dev_err(hdev, "Failed to send firmware data (%d)", + err); goto done; } @@ -563,7 +822,7 @@ static int intel_setup(struct hci_uart *hu) set_bit(STATE_FIRMWARE_LOADED, &intel->flags); - BT_INFO("%s: Waiting for firmware download to complete", hdev->name); + bt_dev_info(hdev, "Waiting for firmware download to complete"); /* Before switching the device into operational mode and with that * booting the loaded firmware, wait for the bootloader notification @@ -580,19 +839,19 @@ static int intel_setup(struct hci_uart *hu) TASK_INTERRUPTIBLE, msecs_to_jiffies(5000)); if (err == 1) { - BT_ERR("%s: Firmware loading interrupted", hdev->name); + bt_dev_err(hdev, "Firmware loading interrupted"); err = -EINTR; goto done; } if (err) { - BT_ERR("%s: Firmware loading timeout", hdev->name); + bt_dev_err(hdev, "Firmware loading timeout"); err = -ETIMEDOUT; goto done; } if (test_bit(STATE_FIRMWARE_FAILED, &intel->flags)) { - BT_ERR("%s: Firmware loading failed", hdev->name); + bt_dev_err(hdev, "Firmware loading failed"); err = -ENOEXEC; goto done; } @@ -601,7 +860,7 @@ static int intel_setup(struct hci_uart *hu) delta = ktime_sub(rettime, calltime); duration = (unsigned long long) ktime_to_ns(delta) >> 10; - BT_INFO("%s: Firmware loaded in %llu usecs", hdev->name, duration); + bt_dev_info(hdev, "Firmware loaded in %llu usecs", duration); done: release_firmware(fw); @@ -634,7 +893,7 @@ done: * 1 second. However if that happens, then just fail the setup * since something went wrong. */ - BT_INFO("%s: Waiting for device to boot", hdev->name); + bt_dev_info(hdev, "Waiting for device to boot"); err = intel_wait_booting(hu); if (err) @@ -646,7 +905,39 @@ done: delta = ktime_sub(rettime, calltime); duration = (unsigned long long) ktime_to_ns(delta) >> 10; - BT_INFO("%s: Device booted in %llu usecs", hdev->name, duration); + bt_dev_info(hdev, "Device booted in %llu usecs", duration); + + /* Enable LPM if matching pdev with wakeup enabled */ + mutex_lock(&intel_device_list_lock); + list_for_each(p, &intel_device_list) { + struct intel_device *dev = list_entry(p, struct intel_device, + list); + if (hu->tty->dev->parent == dev->pdev->dev.parent) { + if (device_may_wakeup(&dev->pdev->dev)) + idev = dev; + break; + } + } + mutex_unlock(&intel_device_list_lock); + + if (!idev) + goto no_lpm; + + bt_dev_info(hdev, "Enabling LPM"); + + skb = __hci_cmd_sync(hdev, 0xfc8b, sizeof(lpm_param), lpm_param, + HCI_CMD_TIMEOUT); + if (IS_ERR(skb)) { + bt_dev_err(hdev, "Failed to enable LPM"); + goto no_lpm; + } + kfree_skb(skb); + + set_bit(STATE_LPM_ENABLED, &intel->flags); + +no_lpm: + /* Ignore errors, device can work without DDC parameters */ + btintel_load_ddc_config(hdev, fwname); skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_CMD_TIMEOUT); if (IS_ERR(skb)) @@ -659,7 +950,7 @@ done: return err; } - BT_INFO("%s: Setup complete", hdev->name); + bt_dev_info(hdev, "Setup complete"); clear_bit(STATE_BOOTLOADER, &intel->flags); @@ -708,10 +999,71 @@ recv: return hci_recv_frame(hdev, skb); } +static void intel_recv_lpm_notify(struct hci_dev *hdev, int value) +{ + struct hci_uart *hu = hci_get_drvdata(hdev); + struct intel_data *intel = hu->priv; + + bt_dev_dbg(hdev, "TX idle notification (%d)", value); + + if (value) { + set_bit(STATE_TX_ACTIVE, &intel->flags); + schedule_work(&intel->busy_work); + } else { + clear_bit(STATE_TX_ACTIVE, &intel->flags); + } +} + +static int intel_recv_lpm(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_lpm_pkt *lpm = (void *)skb->data; + struct hci_uart *hu = hci_get_drvdata(hdev); + struct intel_data *intel = hu->priv; + + switch (lpm->opcode) { + case LPM_OP_TX_NOTIFY: + if (lpm->dlen < 1) { + bt_dev_err(hu->hdev, "Invalid LPM notification packet"); + break; + } + intel_recv_lpm_notify(hdev, lpm->data[0]); + break; + case LPM_OP_SUSPEND_ACK: + set_bit(STATE_SUSPENDED, &intel->flags); + if (test_and_clear_bit(STATE_LPM_TRANSACTION, &intel->flags)) { + smp_mb__after_atomic(); + wake_up_bit(&intel->flags, STATE_LPM_TRANSACTION); + } + break; + case LPM_OP_RESUME_ACK: + clear_bit(STATE_SUSPENDED, &intel->flags); + if (test_and_clear_bit(STATE_LPM_TRANSACTION, &intel->flags)) { + smp_mb__after_atomic(); + wake_up_bit(&intel->flags, STATE_LPM_TRANSACTION); + } + break; + default: + bt_dev_err(hdev, "Unknown LPM opcode (%02x)", lpm->opcode); + break; + } + + kfree_skb(skb); + + return 0; +} + +#define INTEL_RECV_LPM \ + .type = HCI_LPM_PKT, \ + .hlen = HCI_LPM_HDR_SIZE, \ + .loff = 1, \ + .lsize = 1, \ + .maxlen = HCI_LPM_MAX_SIZE + static const struct h4_recv_pkt intel_recv_pkts[] = { - { H4_RECV_ACL, .recv = hci_recv_frame }, - { H4_RECV_SCO, .recv = hci_recv_frame }, - { H4_RECV_EVENT, .recv = intel_recv_event }, + { H4_RECV_ACL, .recv = hci_recv_frame }, + { H4_RECV_SCO, .recv = hci_recv_frame }, + { H4_RECV_EVENT, .recv = intel_recv_event }, + { INTEL_RECV_LPM, .recv = intel_recv_lpm }, }; static int intel_recv(struct hci_uart *hu, const void *data, int count) @@ -726,7 +1078,7 @@ static int intel_recv(struct hci_uart *hu, const void *data, int count) ARRAY_SIZE(intel_recv_pkts)); if (IS_ERR(intel->rx_skb)) { int err = PTR_ERR(intel->rx_skb); - BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err); + bt_dev_err(hu->hdev, "Frame reassembly failed (%d)", err); intel->rx_skb = NULL; return err; } @@ -737,9 +1089,27 @@ static int intel_recv(struct hci_uart *hu, const void *data, int count) static int intel_enqueue(struct hci_uart *hu, struct sk_buff *skb) { struct intel_data *intel = hu->priv; + struct list_head *p; BT_DBG("hu %p skb %p", hu, skb); + /* Be sure our controller is resumed and potential LPM transaction + * completed before enqueuing any packet. + */ + mutex_lock(&intel_device_list_lock); + list_for_each(p, &intel_device_list) { + struct intel_device *idev = list_entry(p, struct intel_device, + list); + + if (hu->tty->dev->parent == idev->pdev->dev.parent) { + pm_runtime_get_sync(&idev->pdev->dev); + pm_runtime_mark_last_busy(&idev->pdev->dev); + pm_runtime_put_autosuspend(&idev->pdev->dev); + break; + } + } + mutex_unlock(&intel_device_list_lock); + skb_queue_tail(&intel->txq, skb); return 0; @@ -813,6 +1183,59 @@ static int intel_acpi_probe(struct intel_device *idev) } #endif +#ifdef CONFIG_PM +static int intel_suspend_device(struct device *dev) +{ + struct intel_device *idev = dev_get_drvdata(dev); + + mutex_lock(&idev->hu_lock); + if (idev->hu) + intel_lpm_suspend(idev->hu); + mutex_unlock(&idev->hu_lock); + + return 0; +} + +static int intel_resume_device(struct device *dev) +{ + struct intel_device *idev = dev_get_drvdata(dev); + + mutex_lock(&idev->hu_lock); + if (idev->hu) + intel_lpm_resume(idev->hu); + mutex_unlock(&idev->hu_lock); + + return 0; +} +#endif + +#ifdef CONFIG_PM_SLEEP +static int intel_suspend(struct device *dev) +{ + struct intel_device *idev = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(idev->irq); + + return intel_suspend_device(dev); +} + +static int intel_resume(struct device *dev) +{ + struct intel_device *idev = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(idev->irq); + + return intel_resume_device(dev); +} +#endif + +static const struct dev_pm_ops intel_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(intel_suspend, intel_resume) + SET_RUNTIME_PM_OPS(intel_suspend_device, intel_resume_device, NULL) +}; + static int intel_probe(struct platform_device *pdev) { struct intel_device *idev; @@ -821,6 +1244,8 @@ static int intel_probe(struct platform_device *pdev) if (!idev) return -ENOMEM; + mutex_init(&idev->hu_lock); + idev->pdev = pdev; if (ACPI_HANDLE(&pdev->dev)) { @@ -838,14 +1263,40 @@ static int intel_probe(struct platform_device *pdev) return PTR_ERR(idev->reset); } + idev->irq = platform_get_irq(pdev, 0); + if (idev->irq < 0) { + struct gpio_desc *host_wake; + + dev_err(&pdev->dev, "No IRQ, falling back to gpio-irq\n"); + + host_wake = devm_gpiod_get_optional(&pdev->dev, "host-wake", + GPIOD_IN); + if (IS_ERR(host_wake)) { + dev_err(&pdev->dev, "Unable to retrieve IRQ\n"); + goto no_irq; + } + + idev->irq = gpiod_to_irq(host_wake); + if (idev->irq < 0) { + dev_err(&pdev->dev, "No corresponding irq for gpio\n"); + goto no_irq; + } + } + + /* Only enable wake-up/irq when controller is powered */ + device_set_wakeup_capable(&pdev->dev, true); + device_wakeup_disable(&pdev->dev); + +no_irq: platform_set_drvdata(pdev, idev); /* Place this instance on the device list */ - spin_lock(&intel_device_list_lock); + mutex_lock(&intel_device_list_lock); list_add_tail(&idev->list, &intel_device_list); - spin_unlock(&intel_device_list_lock); + mutex_unlock(&intel_device_list_lock); - dev_info(&pdev->dev, "registered.\n"); + dev_info(&pdev->dev, "registered, gpio(%d)/irq(%d).\n", + desc_to_gpio(idev->reset), idev->irq); return 0; } @@ -854,9 +1305,11 @@ static int intel_remove(struct platform_device *pdev) { struct intel_device *idev = platform_get_drvdata(pdev); - spin_lock(&intel_device_list_lock); + device_wakeup_disable(&pdev->dev); + + mutex_lock(&intel_device_list_lock); list_del(&idev->list); - spin_unlock(&intel_device_list_lock); + mutex_unlock(&intel_device_list_lock); dev_info(&pdev->dev, "unregistered.\n"); @@ -869,6 +1322,7 @@ static struct platform_driver intel_driver = { .driver = { .name = "hci_intel", .acpi_match_table = ACPI_PTR(intel_acpi_match), + .pm = &intel_pm_ops, }, }; diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index 6b9b91267959..21f4ea4ce610 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -41,13 +41,13 @@ #define HCI_IBS_SLEEP_IND 0xFE #define HCI_IBS_WAKE_IND 0xFD #define HCI_IBS_WAKE_ACK 0xFC -#define HCI_MAX_IBS_SIZE 10 +#define HCI_MAX_IBS_SIZE 10 /* Controller states */ #define STATE_IN_BAND_SLEEP_ENABLED 1 -#define IBS_WAKE_RETRANS_TIMEOUT_MS 100 -#define IBS_TX_IDLE_TIMEOUT_MS 2000 +#define IBS_WAKE_RETRANS_TIMEOUT_MS 100 +#define IBS_TX_IDLE_TIMEOUT_MS 2000 #define BAUDRATE_SETTLE_TIMEOUT_MS 300 /* HCI_IBS transmit side sleep protocol states */ @@ -181,8 +181,8 @@ static void serial_clock_vote(unsigned long vote, struct hci_uart *hu) else __serial_clock_off(hu->tty); - BT_DBG("Vote serial clock %s(%s)", new_vote? "true" : "false", - vote? "true" : "false"); + BT_DBG("Vote serial clock %s(%s)", new_vote ? "true" : "false", + vote ? "true" : "false"); diff = jiffies_to_msecs(jiffies - qca->vote_last_jif); @@ -821,7 +821,7 @@ static struct sk_buff *qca_dequeue(struct hci_uart *hu) static uint8_t qca_get_baudrate_value(int speed) { - switch(speed) { + switch (speed) { case 9600: return QCA_BAUDRATE_9600; case 19200: diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index e8c96b8e86f4..6d04183ed955 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -129,6 +129,16 @@ config CAN_RCAR To compile this driver as a module, choose M here: the module will be called rcar_can. +config CAN_SUN4I + tristate "Allwinner A10 CAN controller" + depends on MACH_SUN4I || MACH_SUN7I || COMPILE_TEST + ---help--- + Say Y here if you want to use CAN controller found on Allwinner + A10/A20 SoCs. + + To compile this driver as a module, choose M here: the module will + be called sun4i_can. + config CAN_XILINXCAN tristate "Xilinx CAN" depends on ARCH_ZYNQ || ARM64 || MICROBLAZE || COMPILE_TEST diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index c533c62b0f5e..1f21cef1d458 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_CAN_FLEXCAN) += flexcan.o obj-$(CONFIG_PCH_CAN) += pch_can.o obj-$(CONFIG_CAN_GRCAN) += grcan.o obj-$(CONFIG_CAN_RCAR) += rcar_can.o +obj-$(CONFIG_CAN_SUN4I) += sun4i_can.o obj-$(CONFIG_CAN_XILINXCAN) += xilinx_can.o subdir-ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c index c83f0f03482b..868fe945e35a 100644 --- a/drivers/net/can/flexcan.c +++ b/drivers/net/can/flexcan.c @@ -26,12 +26,8 @@ #include <linux/can/led.h> #include <linux/clk.h> #include <linux/delay.h> -#include <linux/if_arp.h> -#include <linux/if_ether.h> #include <linux/interrupt.h> #include <linux/io.h> -#include <linux/kernel.h> -#include <linux/list.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_device.h> @@ -63,10 +59,10 @@ #define FLEXCAN_MCR_LPRIO_EN BIT(13) #define FLEXCAN_MCR_AEN BIT(12) #define FLEXCAN_MCR_MAXMB(x) ((x) & 0x7f) -#define FLEXCAN_MCR_IDAM_A (0 << 8) -#define FLEXCAN_MCR_IDAM_B (1 << 8) -#define FLEXCAN_MCR_IDAM_C (2 << 8) -#define FLEXCAN_MCR_IDAM_D (3 << 8) +#define FLEXCAN_MCR_IDAM_A (0x0 << 8) +#define FLEXCAN_MCR_IDAM_B (0x1 << 8) +#define FLEXCAN_MCR_IDAM_C (0x2 << 8) +#define FLEXCAN_MCR_IDAM_D (0x3 << 8) /* FLEXCAN control register (CANCTRL) bits */ #define FLEXCAN_CTRL_PRESDIV(x) (((x) & 0xff) << 24) @@ -161,7 +157,7 @@ #define FLEXCAN_MB_CODE_RX_INACTIVE (0x0 << 24) #define FLEXCAN_MB_CODE_RX_EMPTY (0x4 << 24) #define FLEXCAN_MB_CODE_RX_FULL (0x2 << 24) -#define FLEXCAN_MB_CODE_RX_OVERRRUN (0x6 << 24) +#define FLEXCAN_MB_CODE_RX_OVERRUN (0x6 << 24) #define FLEXCAN_MB_CODE_RX_RANSWER (0xa << 24) #define FLEXCAN_MB_CODE_TX_INACTIVE (0x8 << 24) @@ -175,12 +171,9 @@ #define FLEXCAN_MB_CNT_LENGTH(x) (((x) & 0xf) << 16) #define FLEXCAN_MB_CNT_TIMESTAMP(x) ((x) & 0xffff) -#define FLEXCAN_MB_CODE_MASK (0xf0ffffff) +#define FLEXCAN_TIMEOUT_US (50) -#define FLEXCAN_TIMEOUT_US (50) - -/* - * FLEXCAN hardware feature flags +/* FLEXCAN hardware feature flags * * Below is some version info we got: * SOC Version IP-Version Glitch- [TR]WRN_INT Memory err RTR re- @@ -194,9 +187,9 @@ * * Some SOCs do not have the RX_WARN & TX_WARN interrupt line connected. */ -#define FLEXCAN_HAS_V10_FEATURES BIT(1) /* For core version >= 10 */ -#define FLEXCAN_HAS_BROKEN_ERR_STATE BIT(2) /* [TR]WRN_INT not connected */ -#define FLEXCAN_HAS_MECR_FEATURES BIT(3) /* Memory error detection */ +#define FLEXCAN_QUIRK_BROKEN_ERR_STATE BIT(1) /* [TR]WRN_INT not connected */ +#define FLEXCAN_QUIRK_DISABLE_RXFG BIT(2) /* Disable RX FIFO Global mask */ +#define FLEXCAN_QUIRK_DISABLE_MECR BIT(3) /* Disble Memory error detection */ /* Structure of the message buffer */ struct flexcan_mb { @@ -228,7 +221,7 @@ struct flexcan_regs { u32 rxfgmask; /* 0x48 */ u32 rxfir; /* 0x4c */ u32 _reserved3[12]; /* 0x50 */ - struct flexcan_mb cantxfg[64]; /* 0x80 */ + struct flexcan_mb mb[64]; /* 0x80 */ /* FIFO-mode: * MB * 0x080...0x08f 0 RX message buffer @@ -236,7 +229,7 @@ struct flexcan_regs { * 0x0e0...0x0ff 6-7 8 entry ID table * (mx25, mx28, mx35, mx53) * 0x0e0...0x2df 6-7..37 8..128 entry ID table - * size conf'ed via ctrl2::RFFN + * size conf'ed via ctrl2::RFFN * (mx6, vf610) */ u32 _reserved4[408]; @@ -251,14 +244,14 @@ struct flexcan_regs { }; struct flexcan_devtype_data { - u32 features; /* hardware controller features */ + u32 quirks; /* quirks needed for different IP cores */ }; struct flexcan_priv { struct can_priv can; struct napi_struct napi; - void __iomem *base; + struct flexcan_regs __iomem *regs; u32 reg_esr; u32 reg_ctrl_default; @@ -270,14 +263,17 @@ struct flexcan_priv { }; static struct flexcan_devtype_data fsl_p1010_devtype_data = { - .features = FLEXCAN_HAS_BROKEN_ERR_STATE, + .quirks = FLEXCAN_QUIRK_BROKEN_ERR_STATE, }; + static struct flexcan_devtype_data fsl_imx28_devtype_data; + static struct flexcan_devtype_data fsl_imx6q_devtype_data = { - .features = FLEXCAN_HAS_V10_FEATURES, + .quirks = FLEXCAN_QUIRK_DISABLE_RXFG, }; + static struct flexcan_devtype_data fsl_vf610_devtype_data = { - .features = FLEXCAN_HAS_V10_FEATURES | FLEXCAN_HAS_MECR_FEATURES, + .quirks = FLEXCAN_QUIRK_DISABLE_RXFG | FLEXCAN_QUIRK_DISABLE_MECR, }; static const struct can_bittiming_const flexcan_bittiming_const = { @@ -292,11 +288,10 @@ static const struct can_bittiming_const flexcan_bittiming_const = { .brp_inc = 1, }; -/* - * Abstract off the read/write for arm versus ppc. This +/* Abstract off the read/write for arm versus ppc. This * assumes that PPC uses big-endian registers and everything * else uses little-endian registers, independent of CPU - * endianess. + * endianness. */ #if defined(CONFIG_PPC) static inline u32 flexcan_read(void __iomem *addr) @@ -345,7 +340,7 @@ static inline int flexcan_has_and_handle_berr(const struct flexcan_priv *priv, static int flexcan_chip_enable(struct flexcan_priv *priv) { - struct flexcan_regs __iomem *regs = priv->base; + struct flexcan_regs __iomem *regs = priv->regs; unsigned int timeout = FLEXCAN_TIMEOUT_US / 10; u32 reg; @@ -364,7 +359,7 @@ static int flexcan_chip_enable(struct flexcan_priv *priv) static int flexcan_chip_disable(struct flexcan_priv *priv) { - struct flexcan_regs __iomem *regs = priv->base; + struct flexcan_regs __iomem *regs = priv->regs; unsigned int timeout = FLEXCAN_TIMEOUT_US / 10; u32 reg; @@ -383,7 +378,7 @@ static int flexcan_chip_disable(struct flexcan_priv *priv) static int flexcan_chip_freeze(struct flexcan_priv *priv) { - struct flexcan_regs __iomem *regs = priv->base; + struct flexcan_regs __iomem *regs = priv->regs; unsigned int timeout = 1000 * 1000 * 10 / priv->can.bittiming.bitrate; u32 reg; @@ -402,7 +397,7 @@ static int flexcan_chip_freeze(struct flexcan_priv *priv) static int flexcan_chip_unfreeze(struct flexcan_priv *priv) { - struct flexcan_regs __iomem *regs = priv->base; + struct flexcan_regs __iomem *regs = priv->regs; unsigned int timeout = FLEXCAN_TIMEOUT_US / 10; u32 reg; @@ -421,7 +416,7 @@ static int flexcan_chip_unfreeze(struct flexcan_priv *priv) static int flexcan_chip_softreset(struct flexcan_priv *priv) { - struct flexcan_regs __iomem *regs = priv->base; + struct flexcan_regs __iomem *regs = priv->regs; unsigned int timeout = FLEXCAN_TIMEOUT_US / 10; flexcan_write(FLEXCAN_MCR_SOFTRST, ®s->mcr); @@ -434,12 +429,11 @@ static int flexcan_chip_softreset(struct flexcan_priv *priv) return 0; } - static int __flexcan_get_berr_counter(const struct net_device *dev, struct can_berr_counter *bec) { const struct flexcan_priv *priv = netdev_priv(dev); - struct flexcan_regs __iomem *regs = priv->base; + struct flexcan_regs __iomem *regs = priv->regs; u32 reg = flexcan_read(®s->ecr); bec->txerr = (reg >> 0) & 0xff; @@ -474,9 +468,10 @@ static int flexcan_get_berr_counter(const struct net_device *dev, static int flexcan_start_xmit(struct sk_buff *skb, struct net_device *dev) { const struct flexcan_priv *priv = netdev_priv(dev); - struct flexcan_regs __iomem *regs = priv->base; + struct flexcan_regs __iomem *regs = priv->regs; struct can_frame *cf = (struct can_frame *)skb->data; u32 can_id; + u32 data; u32 ctrl = FLEXCAN_MB_CODE_TX_DATA | (cf->can_dlc << 16); if (can_dropped_invalid_skb(dev, skb)) @@ -495,26 +490,26 @@ static int flexcan_start_xmit(struct sk_buff *skb, struct net_device *dev) ctrl |= FLEXCAN_MB_CNT_RTR; if (cf->can_dlc > 0) { - u32 data = be32_to_cpup((__be32 *)&cf->data[0]); - flexcan_write(data, ®s->cantxfg[FLEXCAN_TX_BUF_ID].data[0]); + data = be32_to_cpup((__be32 *)&cf->data[0]); + flexcan_write(data, ®s->mb[FLEXCAN_TX_BUF_ID].data[0]); } if (cf->can_dlc > 3) { - u32 data = be32_to_cpup((__be32 *)&cf->data[4]); - flexcan_write(data, ®s->cantxfg[FLEXCAN_TX_BUF_ID].data[1]); + data = be32_to_cpup((__be32 *)&cf->data[4]); + flexcan_write(data, ®s->mb[FLEXCAN_TX_BUF_ID].data[1]); } can_put_echo_skb(skb, dev, 0); - flexcan_write(can_id, ®s->cantxfg[FLEXCAN_TX_BUF_ID].can_id); - flexcan_write(ctrl, ®s->cantxfg[FLEXCAN_TX_BUF_ID].can_ctrl); + flexcan_write(can_id, ®s->mb[FLEXCAN_TX_BUF_ID].can_id); + flexcan_write(ctrl, ®s->mb[FLEXCAN_TX_BUF_ID].can_ctrl); /* Errata ERR005829 step8: * Write twice INACTIVE(0x8) code to first MB. */ flexcan_write(FLEXCAN_MB_CODE_TX_INACTIVE, - ®s->cantxfg[FLEXCAN_TX_BUF_RESERVED].can_ctrl); + ®s->mb[FLEXCAN_TX_BUF_RESERVED].can_ctrl); flexcan_write(FLEXCAN_MB_CODE_TX_INACTIVE, - ®s->cantxfg[FLEXCAN_TX_BUF_RESERVED].can_ctrl); + ®s->mb[FLEXCAN_TX_BUF_RESERVED].can_ctrl); return NETDEV_TX_OK; } @@ -597,14 +592,14 @@ static int flexcan_poll_state(struct net_device *dev, u32 reg_esr) flt = reg_esr & FLEXCAN_ESR_FLT_CONF_MASK; if (likely(flt == FLEXCAN_ESR_FLT_CONF_ACTIVE)) { tx_state = unlikely(reg_esr & FLEXCAN_ESR_TX_WRN) ? - CAN_STATE_ERROR_WARNING : CAN_STATE_ERROR_ACTIVE; + CAN_STATE_ERROR_WARNING : CAN_STATE_ERROR_ACTIVE; rx_state = unlikely(reg_esr & FLEXCAN_ESR_RX_WRN) ? - CAN_STATE_ERROR_WARNING : CAN_STATE_ERROR_ACTIVE; + CAN_STATE_ERROR_WARNING : CAN_STATE_ERROR_ACTIVE; new_state = max(tx_state, rx_state); } else { __flexcan_get_berr_counter(dev, &bec); new_state = flt == FLEXCAN_ESR_FLT_CONF_PASSIVE ? - CAN_STATE_ERROR_PASSIVE : CAN_STATE_BUS_OFF; + CAN_STATE_ERROR_PASSIVE : CAN_STATE_BUS_OFF; rx_state = bec.rxerr >= bec.txerr ? new_state : 0; tx_state = bec.rxerr <= bec.txerr ? new_state : 0; } @@ -633,8 +628,8 @@ static void flexcan_read_fifo(const struct net_device *dev, struct can_frame *cf) { const struct flexcan_priv *priv = netdev_priv(dev); - struct flexcan_regs __iomem *regs = priv->base; - struct flexcan_mb __iomem *mb = ®s->cantxfg[0]; + struct flexcan_regs __iomem *regs = priv->regs; + struct flexcan_mb __iomem *mb = ®s->mb[0]; u32 reg_ctrl, reg_id; reg_ctrl = flexcan_read(&mb->can_ctrl); @@ -683,12 +678,11 @@ static int flexcan_poll(struct napi_struct *napi, int quota) { struct net_device *dev = napi->dev; const struct flexcan_priv *priv = netdev_priv(dev); - struct flexcan_regs __iomem *regs = priv->base; + struct flexcan_regs __iomem *regs = priv->regs; u32 reg_iflag1, reg_esr; int work_done = 0; - /* - * The error bits are cleared on read, + /* The error bits are cleared on read, * use saved value from irq handler. */ reg_esr = flexcan_read(®s->esr) | priv->reg_esr; @@ -723,17 +717,17 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id) struct net_device *dev = dev_id; struct net_device_stats *stats = &dev->stats; struct flexcan_priv *priv = netdev_priv(dev); - struct flexcan_regs __iomem *regs = priv->base; + struct flexcan_regs __iomem *regs = priv->regs; u32 reg_iflag1, reg_esr; reg_iflag1 = flexcan_read(®s->iflag1); reg_esr = flexcan_read(®s->esr); + /* ACK all bus error and state change IRQ sources */ if (reg_esr & FLEXCAN_ESR_ALL_INT) flexcan_write(reg_esr & FLEXCAN_ESR_ALL_INT, ®s->esr); - /* - * schedule NAPI in case of: + /* schedule NAPI in case of: * - rx IRQ * - state change IRQ * - bus error IRQ and bus error reporting is activated @@ -741,15 +735,14 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id) if ((reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE) || (reg_esr & FLEXCAN_ESR_ERR_STATE) || flexcan_has_and_handle_berr(priv, reg_esr)) { - /* - * The error bits are cleared on read, + /* The error bits are cleared on read, * save them for later use. */ priv->reg_esr = reg_esr & FLEXCAN_ESR_ERR_BUS; flexcan_write(FLEXCAN_IFLAG_DEFAULT & - ~FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, ®s->imask1); + ~FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, ®s->imask1); flexcan_write(priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_ALL, - ®s->ctrl); + ®s->ctrl); napi_schedule(&priv->napi); } @@ -765,9 +758,10 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id) stats->tx_bytes += can_get_echo_skb(dev, 0); stats->tx_packets++; can_led_event(dev, CAN_LED_EVENT_TX); - /* after sending a RTR frame mailbox is in RX mode */ + + /* after sending a RTR frame MB is in RX mode */ flexcan_write(FLEXCAN_MB_CODE_TX_INACTIVE, - ®s->cantxfg[FLEXCAN_TX_BUF_ID].can_ctrl); + ®s->mb[FLEXCAN_TX_BUF_ID].can_ctrl); flexcan_write((1 << FLEXCAN_TX_BUF_ID), ®s->iflag1); netif_wake_queue(dev); } @@ -779,7 +773,7 @@ static void flexcan_set_bittiming(struct net_device *dev) { const struct flexcan_priv *priv = netdev_priv(dev); const struct can_bittiming *bt = &priv->can.bittiming; - struct flexcan_regs __iomem *regs = priv->base; + struct flexcan_regs __iomem *regs = priv->regs; u32 reg; reg = flexcan_read(®s->ctrl); @@ -813,8 +807,7 @@ static void flexcan_set_bittiming(struct net_device *dev) flexcan_read(®s->mcr), flexcan_read(®s->ctrl)); } -/* - * flexcan_chip_start +/* flexcan_chip_start * * this functions is entered with clocks enabled * @@ -822,7 +815,7 @@ static void flexcan_set_bittiming(struct net_device *dev) static int flexcan_chip_start(struct net_device *dev) { struct flexcan_priv *priv = netdev_priv(dev); - struct flexcan_regs __iomem *regs = priv->base; + struct flexcan_regs __iomem *regs = priv->regs; u32 reg_mcr, reg_ctrl, reg_ctrl2, reg_mecr; int err, i; @@ -838,29 +831,26 @@ static int flexcan_chip_start(struct net_device *dev) flexcan_set_bittiming(dev); - /* - * MCR + /* MCR * * enable freeze * enable fifo * halt now * only supervisor access * enable warning int - * choose format C * disable local echo - * + * choose format C + * set max mailbox number */ reg_mcr = flexcan_read(®s->mcr); reg_mcr &= ~FLEXCAN_MCR_MAXMB(0xff); reg_mcr |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_FEN | FLEXCAN_MCR_HALT | - FLEXCAN_MCR_SUPV | FLEXCAN_MCR_WRN_EN | - FLEXCAN_MCR_IDAM_C | FLEXCAN_MCR_SRX_DIS | - FLEXCAN_MCR_MAXMB(FLEXCAN_TX_BUF_ID); + FLEXCAN_MCR_SUPV | FLEXCAN_MCR_WRN_EN | FLEXCAN_MCR_SRX_DIS | + FLEXCAN_MCR_IDAM_C | FLEXCAN_MCR_MAXMB(FLEXCAN_TX_BUF_ID); netdev_dbg(dev, "%s: writing mcr=0x%08x", __func__, reg_mcr); flexcan_write(reg_mcr, ®s->mcr); - /* - * CTRL + /* CTRL * * disable timer sync feature * @@ -875,12 +865,12 @@ static int flexcan_chip_start(struct net_device *dev) reg_ctrl &= ~FLEXCAN_CTRL_TSYN; reg_ctrl |= FLEXCAN_CTRL_BOFF_REC | FLEXCAN_CTRL_LBUF | FLEXCAN_CTRL_ERR_STATE; - /* - * enable the "error interrupt" (FLEXCAN_CTRL_ERR_MSK), + + /* enable the "error interrupt" (FLEXCAN_CTRL_ERR_MSK), * on most Flexcan cores, too. Otherwise we don't get * any error warning or passive interrupts. */ - if (priv->devtype_data->features & FLEXCAN_HAS_BROKEN_ERR_STATE || + if (priv->devtype_data->quirks & FLEXCAN_QUIRK_BROKEN_ERR_STATE || priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) reg_ctrl |= FLEXCAN_CTRL_ERR_MSK; else @@ -888,41 +878,41 @@ static int flexcan_chip_start(struct net_device *dev) /* save for later use */ priv->reg_ctrl_default = reg_ctrl; + /* leave interrupts disabled for now */ + reg_ctrl &= ~FLEXCAN_CTRL_ERR_ALL; netdev_dbg(dev, "%s: writing ctrl=0x%08x", __func__, reg_ctrl); flexcan_write(reg_ctrl, ®s->ctrl); /* clear and invalidate all mailboxes first */ - for (i = FLEXCAN_TX_BUF_ID; i < ARRAY_SIZE(regs->cantxfg); i++) { + for (i = FLEXCAN_TX_BUF_ID; i < ARRAY_SIZE(regs->mb); i++) { flexcan_write(FLEXCAN_MB_CODE_RX_INACTIVE, - ®s->cantxfg[i].can_ctrl); + ®s->mb[i].can_ctrl); } /* Errata ERR005829: mark first TX mailbox as INACTIVE */ flexcan_write(FLEXCAN_MB_CODE_TX_INACTIVE, - ®s->cantxfg[FLEXCAN_TX_BUF_RESERVED].can_ctrl); + ®s->mb[FLEXCAN_TX_BUF_RESERVED].can_ctrl); /* mark TX mailbox as INACTIVE */ flexcan_write(FLEXCAN_MB_CODE_TX_INACTIVE, - ®s->cantxfg[FLEXCAN_TX_BUF_ID].can_ctrl); + ®s->mb[FLEXCAN_TX_BUF_ID].can_ctrl); /* acceptance mask/acceptance code (accept everything) */ flexcan_write(0x0, ®s->rxgmask); flexcan_write(0x0, ®s->rx14mask); flexcan_write(0x0, ®s->rx15mask); - if (priv->devtype_data->features & FLEXCAN_HAS_V10_FEATURES) + if (priv->devtype_data->quirks & FLEXCAN_QUIRK_DISABLE_RXFG) flexcan_write(0x0, ®s->rxfgmask); - /* - * On Vybrid, disable memory error detection interrupts + /* On Vybrid, disable memory error detection interrupts * and freeze mode. * This also works around errata e5295 which generates * false positive memory errors and put the device in * freeze mode. */ - if (priv->devtype_data->features & FLEXCAN_HAS_MECR_FEATURES) { - /* - * Follow the protocol as described in "Detection + if (priv->devtype_data->quirks & FLEXCAN_QUIRK_DISABLE_MECR) { + /* Follow the protocol as described in "Detection * and Correction of Memory Errors" to write to * MECR register */ @@ -934,7 +924,7 @@ static int flexcan_chip_start(struct net_device *dev) reg_mecr &= ~FLEXCAN_MECR_ECRWRDIS; flexcan_write(reg_mecr, ®s->mecr); reg_mecr &= ~(FLEXCAN_MECR_NCEFAFRZ | FLEXCAN_MECR_HANCEI_MSK | - FLEXCAN_MECR_FANCEI_MSK); + FLEXCAN_MECR_FANCEI_MSK); flexcan_write(reg_mecr, ®s->mecr); } @@ -949,8 +939,11 @@ static int flexcan_chip_start(struct net_device *dev) priv->can.state = CAN_STATE_ERROR_ACTIVE; - /* enable FIFO interrupts */ + /* enable interrupts atomically */ + disable_irq(dev->irq); + flexcan_write(priv->reg_ctrl_default, ®s->ctrl); flexcan_write(FLEXCAN_IFLAG_DEFAULT, ®s->imask1); + enable_irq(dev->irq); /* print chip status */ netdev_dbg(dev, "%s: reading mcr=0x%08x ctrl=0x%08x\n", __func__, @@ -965,16 +958,14 @@ static int flexcan_chip_start(struct net_device *dev) return err; } -/* - * flexcan_chip_stop +/* flexcan_chip_stop * * this functions is entered with clocks enabled - * */ static void flexcan_chip_stop(struct net_device *dev) { struct flexcan_priv *priv = netdev_priv(dev); - struct flexcan_regs __iomem *regs = priv->base; + struct flexcan_regs __iomem *regs = priv->regs; /* freeze + disable module */ flexcan_chip_freeze(priv); @@ -987,8 +978,6 @@ static void flexcan_chip_stop(struct net_device *dev) flexcan_transceiver_disable(priv); priv->can.state = CAN_STATE_STOPPED; - - return; } static int flexcan_open(struct net_device *dev) @@ -1085,7 +1074,7 @@ static const struct net_device_ops flexcan_netdev_ops = { static int register_flexcandev(struct net_device *dev) { struct flexcan_priv *priv = netdev_priv(dev); - struct flexcan_regs __iomem *regs = priv->base; + struct flexcan_regs __iomem *regs = priv->regs; u32 reg, err; err = clk_prepare_enable(priv->clk_ipg); @@ -1114,8 +1103,7 @@ static int register_flexcandev(struct net_device *dev) FLEXCAN_MCR_FEN | FLEXCAN_MCR_SUPV; flexcan_write(reg, ®s->mcr); - /* - * Currently we only support newer versions of this core + /* Currently we only support newer versions of this core * featuring a RX FIFO. Older cores found on some Coldfire * derivates are not yet supported. */ @@ -1168,7 +1156,7 @@ static int flexcan_probe(struct platform_device *pdev) struct regulator *reg_xceiver; struct resource *mem; struct clk *clk_ipg = NULL, *clk_per = NULL; - void __iomem *base; + struct flexcan_regs __iomem *regs; int err, irq; u32 clock_freq = 0; @@ -1180,7 +1168,7 @@ static int flexcan_probe(struct platform_device *pdev) if (pdev->dev.of_node) of_property_read_u32(pdev->dev.of_node, - "clock-frequency", &clock_freq); + "clock-frequency", &clock_freq); if (!clock_freq) { clk_ipg = devm_clk_get(&pdev->dev, "ipg"); @@ -1202,9 +1190,9 @@ static int flexcan_probe(struct platform_device *pdev) if (irq <= 0) return -ENODEV; - base = devm_ioremap_resource(&pdev->dev, mem); - if (IS_ERR(base)) - return PTR_ERR(base); + regs = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(regs)) + return PTR_ERR(regs); of_id = of_match_device(flexcan_of_match, &pdev->dev); if (of_id) { @@ -1232,12 +1220,11 @@ static int flexcan_probe(struct platform_device *pdev) priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_LISTENONLY | CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_BERR_REPORTING; - priv->base = base; + priv->regs = regs; priv->clk_ipg = clk_ipg; priv->clk_per = clk_per; priv->pdata = dev_get_platdata(&pdev->dev); priv->devtype_data = devtype_data; - priv->reg_xceiver = reg_xceiver; netif_napi_add(dev, &priv->napi, flexcan_poll, FLEXCAN_NAPI_WEIGHT); @@ -1254,7 +1241,7 @@ static int flexcan_probe(struct platform_device *pdev) devm_can_led_init(dev); dev_info(&pdev->dev, "device registered (reg_base=%p, irq=%d)\n", - priv->base, dev->irq); + priv->regs, dev->irq); return 0; diff --git a/drivers/net/can/sun4i_can.c b/drivers/net/can/sun4i_can.c new file mode 100644 index 000000000000..10d8497635e8 --- /dev/null +++ b/drivers/net/can/sun4i_can.c @@ -0,0 +1,857 @@ +/* + * sun4i_can.c - CAN bus controller driver for Allwinner SUN4I&SUN7I based SoCs + * + * Copyright (C) 2013 Peter Chen + * Copyright (C) 2015 Gerhard Bertelsmann + * All rights reserved. + * + * Parts of this software are based on (derived from) the SJA1000 code by: + * Copyright (C) 2014 Oliver Hartkopp <oliver.hartkopp@volkswagen.de> + * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com> + * Copyright (C) 2002-2007 Volkswagen Group Electronic Research + * Copyright (C) 2003 Matthias Brukner, Trajet Gmbh, Rebenring 33, + * 38106 Braunschweig, GERMANY + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Volkswagen nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * Alternatively, provided that this notice is retained in full, this + * software may be distributed under the terms of the GNU General + * Public License ("GPL") version 2, in which case the provisions of the + * GPL apply INSTEAD OF those given above. + * + * The provided data structures and external interfaces from this code + * are not restricted to be used by modules with a GPL compatible license. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + */ + +#include <linux/netdevice.h> +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> +#include <linux/can/led.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> + +#define DRV_NAME "sun4i_can" + +/* Registers address (physical base address 0x01C2BC00) */ +#define SUN4I_REG_MSEL_ADDR 0x0000 /* CAN Mode Select */ +#define SUN4I_REG_CMD_ADDR 0x0004 /* CAN Command */ +#define SUN4I_REG_STA_ADDR 0x0008 /* CAN Status */ +#define SUN4I_REG_INT_ADDR 0x000c /* CAN Interrupt Flag */ +#define SUN4I_REG_INTEN_ADDR 0x0010 /* CAN Interrupt Enable */ +#define SUN4I_REG_BTIME_ADDR 0x0014 /* CAN Bus Timing 0 */ +#define SUN4I_REG_TEWL_ADDR 0x0018 /* CAN Tx Error Warning Limit */ +#define SUN4I_REG_ERRC_ADDR 0x001c /* CAN Error Counter */ +#define SUN4I_REG_RMCNT_ADDR 0x0020 /* CAN Receive Message Counter */ +#define SUN4I_REG_RBUFSA_ADDR 0x0024 /* CAN Receive Buffer Start Address */ +#define SUN4I_REG_BUF0_ADDR 0x0040 /* CAN Tx/Rx Buffer 0 */ +#define SUN4I_REG_BUF1_ADDR 0x0044 /* CAN Tx/Rx Buffer 1 */ +#define SUN4I_REG_BUF2_ADDR 0x0048 /* CAN Tx/Rx Buffer 2 */ +#define SUN4I_REG_BUF3_ADDR 0x004c /* CAN Tx/Rx Buffer 3 */ +#define SUN4I_REG_BUF4_ADDR 0x0050 /* CAN Tx/Rx Buffer 4 */ +#define SUN4I_REG_BUF5_ADDR 0x0054 /* CAN Tx/Rx Buffer 5 */ +#define SUN4I_REG_BUF6_ADDR 0x0058 /* CAN Tx/Rx Buffer 6 */ +#define SUN4I_REG_BUF7_ADDR 0x005c /* CAN Tx/Rx Buffer 7 */ +#define SUN4I_REG_BUF8_ADDR 0x0060 /* CAN Tx/Rx Buffer 8 */ +#define SUN4I_REG_BUF9_ADDR 0x0064 /* CAN Tx/Rx Buffer 9 */ +#define SUN4I_REG_BUF10_ADDR 0x0068 /* CAN Tx/Rx Buffer 10 */ +#define SUN4I_REG_BUF11_ADDR 0x006c /* CAN Tx/Rx Buffer 11 */ +#define SUN4I_REG_BUF12_ADDR 0x0070 /* CAN Tx/Rx Buffer 12 */ +#define SUN4I_REG_ACPC_ADDR 0x0040 /* CAN Acceptance Code 0 */ +#define SUN4I_REG_ACPM_ADDR 0x0044 /* CAN Acceptance Mask 0 */ +#define SUN4I_REG_RBUF_RBACK_START_ADDR 0x0180 /* CAN transmit buffer start */ +#define SUN4I_REG_RBUF_RBACK_END_ADDR 0x01b0 /* CAN transmit buffer end */ + +/* Controller Register Description */ + +/* mode select register (r/w) + * offset:0x0000 default:0x0000_0001 + */ +#define SUN4I_MSEL_SLEEP_MODE (0x01 << 4) /* write in reset mode */ +#define SUN4I_MSEL_WAKE_UP (0x00 << 4) +#define SUN4I_MSEL_SINGLE_FILTER (0x01 << 3) /* write in reset mode */ +#define SUN4I_MSEL_DUAL_FILTERS (0x00 << 3) +#define SUN4I_MSEL_LOOPBACK_MODE BIT(2) +#define SUN4I_MSEL_LISTEN_ONLY_MODE BIT(1) +#define SUN4I_MSEL_RESET_MODE BIT(0) + +/* command register (w) + * offset:0x0004 default:0x0000_0000 + */ +#define SUN4I_CMD_BUS_OFF_REQ BIT(5) +#define SUN4I_CMD_SELF_RCV_REQ BIT(4) +#define SUN4I_CMD_CLEAR_OR_FLAG BIT(3) +#define SUN4I_CMD_RELEASE_RBUF BIT(2) +#define SUN4I_CMD_ABORT_REQ BIT(1) +#define SUN4I_CMD_TRANS_REQ BIT(0) + +/* status register (r) + * offset:0x0008 default:0x0000_003c + */ +#define SUN4I_STA_BIT_ERR (0x00 << 22) +#define SUN4I_STA_FORM_ERR (0x01 << 22) +#define SUN4I_STA_STUFF_ERR (0x02 << 22) +#define SUN4I_STA_OTHER_ERR (0x03 << 22) +#define SUN4I_STA_MASK_ERR (0x03 << 22) +#define SUN4I_STA_ERR_DIR BIT(21) +#define SUN4I_STA_ERR_SEG_CODE (0x1f << 16) +#define SUN4I_STA_START (0x03 << 16) +#define SUN4I_STA_ID28_21 (0x02 << 16) +#define SUN4I_STA_ID20_18 (0x06 << 16) +#define SUN4I_STA_SRTR (0x04 << 16) +#define SUN4I_STA_IDE (0x05 << 16) +#define SUN4I_STA_ID17_13 (0x07 << 16) +#define SUN4I_STA_ID12_5 (0x0f << 16) +#define SUN4I_STA_ID4_0 (0x0e << 16) +#define SUN4I_STA_RTR (0x0c << 16) +#define SUN4I_STA_RB1 (0x0d << 16) +#define SUN4I_STA_RB0 (0x09 << 16) +#define SUN4I_STA_DLEN (0x0b << 16) +#define SUN4I_STA_DATA_FIELD (0x0a << 16) +#define SUN4I_STA_CRC_SEQUENCE (0x08 << 16) +#define SUN4I_STA_CRC_DELIMITER (0x18 << 16) +#define SUN4I_STA_ACK (0x19 << 16) +#define SUN4I_STA_ACK_DELIMITER (0x1b << 16) +#define SUN4I_STA_END (0x1a << 16) +#define SUN4I_STA_INTERMISSION (0x12 << 16) +#define SUN4I_STA_ACTIVE_ERROR (0x11 << 16) +#define SUN4I_STA_PASSIVE_ERROR (0x16 << 16) +#define SUN4I_STA_TOLERATE_DOMINANT_BITS (0x13 << 16) +#define SUN4I_STA_ERROR_DELIMITER (0x17 << 16) +#define SUN4I_STA_OVERLOAD (0x1c << 16) +#define SUN4I_STA_BUS_OFF BIT(7) +#define SUN4I_STA_ERR_STA BIT(6) +#define SUN4I_STA_TRANS_BUSY BIT(5) +#define SUN4I_STA_RCV_BUSY BIT(4) +#define SUN4I_STA_TRANS_OVER BIT(3) +#define SUN4I_STA_TBUF_RDY BIT(2) +#define SUN4I_STA_DATA_ORUN BIT(1) +#define SUN4I_STA_RBUF_RDY BIT(0) + +/* interrupt register (r) + * offset:0x000c default:0x0000_0000 + */ +#define SUN4I_INT_BUS_ERR BIT(7) +#define SUN4I_INT_ARB_LOST BIT(6) +#define SUN4I_INT_ERR_PASSIVE BIT(5) +#define SUN4I_INT_WAKEUP BIT(4) +#define SUN4I_INT_DATA_OR BIT(3) +#define SUN4I_INT_ERR_WRN BIT(2) +#define SUN4I_INT_TBUF_VLD BIT(1) +#define SUN4I_INT_RBUF_VLD BIT(0) + +/* interrupt enable register (r/w) + * offset:0x0010 default:0x0000_0000 + */ +#define SUN4I_INTEN_BERR BIT(7) +#define SUN4I_INTEN_ARB_LOST BIT(6) +#define SUN4I_INTEN_ERR_PASSIVE BIT(5) +#define SUN4I_INTEN_WAKEUP BIT(4) +#define SUN4I_INTEN_OR BIT(3) +#define SUN4I_INTEN_ERR_WRN BIT(2) +#define SUN4I_INTEN_TX BIT(1) +#define SUN4I_INTEN_RX BIT(0) + +/* error code */ +#define SUN4I_ERR_INRCV (0x1 << 5) +#define SUN4I_ERR_INTRANS (0x0 << 5) + +/* filter mode */ +#define SUN4I_FILTER_CLOSE 0 +#define SUN4I_SINGLE_FLTER_MODE 1 +#define SUN4I_DUAL_FILTER_MODE 2 + +/* message buffer flags */ +#define SUN4I_MSG_EFF_FLAG BIT(7) +#define SUN4I_MSG_RTR_FLAG BIT(6) + +/* max. number of interrupts handled in ISR */ +#define SUN4I_CAN_MAX_IRQ 20 +#define SUN4I_MODE_MAX_RETRIES 100 + +struct sun4ican_priv { + struct can_priv can; + void __iomem *base; + struct clk *clk; + spinlock_t cmdreg_lock; /* lock for concurrent cmd register writes */ +}; + +static const struct can_bittiming_const sun4ican_bittiming_const = { + .name = DRV_NAME, + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 64, + .brp_inc = 1, +}; + +static void sun4i_can_write_cmdreg(struct sun4ican_priv *priv, u8 val) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->cmdreg_lock, flags); + writel(val, priv->base + SUN4I_REG_CMD_ADDR); + spin_unlock_irqrestore(&priv->cmdreg_lock, flags); +} + +static int set_normal_mode(struct net_device *dev) +{ + struct sun4ican_priv *priv = netdev_priv(dev); + int retry = SUN4I_MODE_MAX_RETRIES; + u32 mod_reg_val = 0; + + do { + mod_reg_val = readl(priv->base + SUN4I_REG_MSEL_ADDR); + mod_reg_val &= ~SUN4I_MSEL_RESET_MODE; + writel(mod_reg_val, priv->base + SUN4I_REG_MSEL_ADDR); + } while (retry-- && (mod_reg_val & SUN4I_MSEL_RESET_MODE)); + + if (readl(priv->base + SUN4I_REG_MSEL_ADDR) & SUN4I_MSEL_RESET_MODE) { + netdev_err(dev, + "setting controller into normal mode failed!\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int set_reset_mode(struct net_device *dev) +{ + struct sun4ican_priv *priv = netdev_priv(dev); + int retry = SUN4I_MODE_MAX_RETRIES; + u32 mod_reg_val = 0; + + do { + mod_reg_val = readl(priv->base + SUN4I_REG_MSEL_ADDR); + mod_reg_val |= SUN4I_MSEL_RESET_MODE; + writel(mod_reg_val, priv->base + SUN4I_REG_MSEL_ADDR); + } while (retry-- && !(mod_reg_val & SUN4I_MSEL_RESET_MODE)); + + if (!(readl(priv->base + SUN4I_REG_MSEL_ADDR) & + SUN4I_MSEL_RESET_MODE)) { + netdev_err(dev, "setting controller into reset mode failed!\n"); + return -ETIMEDOUT; + } + + return 0; +} + +/* bittiming is called in reset_mode only */ +static int sun4ican_set_bittiming(struct net_device *dev) +{ + struct sun4ican_priv *priv = netdev_priv(dev); + struct can_bittiming *bt = &priv->can.bittiming; + u32 cfg; + + cfg = ((bt->brp - 1) & 0x3FF) | + (((bt->sjw - 1) & 0x3) << 14) | + (((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) << 16) | + (((bt->phase_seg2 - 1) & 0x7) << 20); + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + cfg |= 0x800000; + + netdev_dbg(dev, "setting BITTIMING=0x%08x\n", cfg); + writel(cfg, priv->base + SUN4I_REG_BTIME_ADDR); + + return 0; +} + +static int sun4ican_get_berr_counter(const struct net_device *dev, + struct can_berr_counter *bec) +{ + struct sun4ican_priv *priv = netdev_priv(dev); + u32 errors; + int err; + + err = clk_prepare_enable(priv->clk); + if (err) { + netdev_err(dev, "could not enable clock\n"); + return err; + } + + errors = readl(priv->base + SUN4I_REG_ERRC_ADDR); + + bec->txerr = errors & 0xFF; + bec->rxerr = (errors >> 16) & 0xFF; + + clk_disable_unprepare(priv->clk); + + return 0; +} + +static int sun4i_can_start(struct net_device *dev) +{ + struct sun4ican_priv *priv = netdev_priv(dev); + int err; + u32 mod_reg_val; + + /* we need to enter the reset mode */ + err = set_reset_mode(dev); + if (err) { + netdev_err(dev, "could not enter reset mode\n"); + return err; + } + + /* set filters - we accept all */ + writel(0x00000000, priv->base + SUN4I_REG_ACPC_ADDR); + writel(0xFFFFFFFF, priv->base + SUN4I_REG_ACPM_ADDR); + + /* clear error counters and error code capture */ + writel(0, priv->base + SUN4I_REG_ERRC_ADDR); + + /* enable interrupts */ + if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) + writel(0xFF, priv->base + SUN4I_REG_INTEN_ADDR); + else + writel(0xFF & ~SUN4I_INTEN_BERR, + priv->base + SUN4I_REG_INTEN_ADDR); + + /* enter the selected mode */ + mod_reg_val = readl(priv->base + SUN4I_REG_MSEL_ADDR); + if (priv->can.ctrlmode & CAN_CTRLMODE_PRESUME_ACK) + mod_reg_val |= SUN4I_MSEL_LOOPBACK_MODE; + else if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + mod_reg_val |= SUN4I_MSEL_LISTEN_ONLY_MODE; + writel(mod_reg_val, priv->base + SUN4I_REG_MSEL_ADDR); + + err = sun4ican_set_bittiming(dev); + if (err) + return err; + + /* we are ready to enter the normal mode */ + err = set_normal_mode(dev); + if (err) { + netdev_err(dev, "could not enter normal mode\n"); + return err; + } + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + return 0; +} + +static int sun4i_can_stop(struct net_device *dev) +{ + struct sun4ican_priv *priv = netdev_priv(dev); + int err; + + priv->can.state = CAN_STATE_STOPPED; + /* we need to enter reset mode */ + err = set_reset_mode(dev); + if (err) { + netdev_err(dev, "could not enter reset mode\n"); + return err; + } + + /* disable all interrupts */ + writel(0, priv->base + SUN4I_REG_INTEN_ADDR); + + return 0; +} + +static int sun4ican_set_mode(struct net_device *dev, enum can_mode mode) +{ + int err; + + switch (mode) { + case CAN_MODE_START: + err = sun4i_can_start(dev); + if (err) { + netdev_err(dev, "starting CAN controller failed!\n"); + return err; + } + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + break; + + default: + return -EOPNOTSUPP; + } + return 0; +} + +/* transmit a CAN message + * message layout in the sk_buff should be like this: + * xx xx xx xx ff ll 00 11 22 33 44 55 66 77 + * [ can_id ] [flags] [len] [can data (up to 8 bytes] + */ +static int sun4ican_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct sun4ican_priv *priv = netdev_priv(dev); + struct can_frame *cf = (struct can_frame *)skb->data; + u8 dlc; + u32 dreg, msg_flag_n; + canid_t id; + int i; + + if (can_dropped_invalid_skb(dev, skb)) + return NETDEV_TX_OK; + + netif_stop_queue(dev); + + id = cf->can_id; + dlc = cf->can_dlc; + msg_flag_n = dlc; + + if (id & CAN_RTR_FLAG) + msg_flag_n |= SUN4I_MSG_RTR_FLAG; + + if (id & CAN_EFF_FLAG) { + msg_flag_n |= SUN4I_MSG_EFF_FLAG; + dreg = SUN4I_REG_BUF5_ADDR; + writel((id >> 21) & 0xFF, priv->base + SUN4I_REG_BUF1_ADDR); + writel((id >> 13) & 0xFF, priv->base + SUN4I_REG_BUF2_ADDR); + writel((id >> 5) & 0xFF, priv->base + SUN4I_REG_BUF3_ADDR); + writel((id << 3) & 0xF8, priv->base + SUN4I_REG_BUF4_ADDR); + } else { + dreg = SUN4I_REG_BUF3_ADDR; + writel((id >> 3) & 0xFF, priv->base + SUN4I_REG_BUF1_ADDR); + writel((id << 5) & 0xE0, priv->base + SUN4I_REG_BUF2_ADDR); + } + + for (i = 0; i < dlc; i++) + writel(cf->data[i], priv->base + (dreg + i * 4)); + + writel(msg_flag_n, priv->base + SUN4I_REG_BUF0_ADDR); + + can_put_echo_skb(skb, dev, 0); + + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) + sun4i_can_write_cmdreg(priv, SUN4I_CMD_SELF_RCV_REQ); + else + sun4i_can_write_cmdreg(priv, SUN4I_CMD_TRANS_REQ); + + return NETDEV_TX_OK; +} + +static void sun4i_can_rx(struct net_device *dev) +{ + struct sun4ican_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u8 fi; + u32 dreg; + canid_t id; + int i; + + /* create zero'ed CAN frame buffer */ + skb = alloc_can_skb(dev, &cf); + if (!skb) + return; + + fi = readl(priv->base + SUN4I_REG_BUF0_ADDR); + cf->can_dlc = get_can_dlc(fi & 0x0F); + if (fi & SUN4I_MSG_EFF_FLAG) { + dreg = SUN4I_REG_BUF5_ADDR; + id = (readl(priv->base + SUN4I_REG_BUF1_ADDR) << 21) | + (readl(priv->base + SUN4I_REG_BUF2_ADDR) << 13) | + (readl(priv->base + SUN4I_REG_BUF3_ADDR) << 5) | + ((readl(priv->base + SUN4I_REG_BUF4_ADDR) >> 3) & 0x1f); + id |= CAN_EFF_FLAG; + } else { + dreg = SUN4I_REG_BUF3_ADDR; + id = (readl(priv->base + SUN4I_REG_BUF1_ADDR) << 3) | + ((readl(priv->base + SUN4I_REG_BUF2_ADDR) >> 5) & 0x7); + } + + /* remote frame ? */ + if (fi & SUN4I_MSG_RTR_FLAG) + id |= CAN_RTR_FLAG; + else + for (i = 0; i < cf->can_dlc; i++) + cf->data[i] = readl(priv->base + dreg + i * 4); + + cf->can_id = id; + + sun4i_can_write_cmdreg(priv, SUN4I_CMD_RELEASE_RBUF); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + + can_led_event(dev, CAN_LED_EVENT_RX); +} + +static int sun4i_can_err(struct net_device *dev, u8 isrc, u8 status) +{ + struct sun4ican_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + struct can_frame *cf; + struct sk_buff *skb; + enum can_state state = priv->can.state; + enum can_state rx_state, tx_state; + unsigned int rxerr, txerr, errc; + u32 ecc, alc; + + /* we don't skip if alloc fails because we want the stats anyhow */ + skb = alloc_can_err_skb(dev, &cf); + + errc = readl(priv->base + SUN4I_REG_ERRC_ADDR); + rxerr = (errc >> 16) & 0xFF; + txerr = errc & 0xFF; + + if (skb) { + cf->data[6] = txerr; + cf->data[7] = rxerr; + } + + if (isrc & SUN4I_INT_DATA_OR) { + /* data overrun interrupt */ + netdev_dbg(dev, "data overrun interrupt\n"); + if (likely(skb)) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + } + stats->rx_over_errors++; + stats->rx_errors++; + /* clear bit */ + sun4i_can_write_cmdreg(priv, SUN4I_CMD_CLEAR_OR_FLAG); + } + if (isrc & SUN4I_INT_ERR_WRN) { + /* error warning interrupt */ + netdev_dbg(dev, "error warning interrupt\n"); + + if (status & SUN4I_STA_BUS_OFF) + state = CAN_STATE_BUS_OFF; + else if (status & SUN4I_STA_ERR_STA) + state = CAN_STATE_ERROR_WARNING; + else + state = CAN_STATE_ERROR_ACTIVE; + } + if (isrc & SUN4I_INT_BUS_ERR) { + /* bus error interrupt */ + netdev_dbg(dev, "bus error interrupt\n"); + priv->can.can_stats.bus_error++; + stats->rx_errors++; + + if (likely(skb)) { + ecc = readl(priv->base + SUN4I_REG_STA_ADDR); + + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + + switch (ecc & SUN4I_STA_MASK_ERR) { + case SUN4I_STA_BIT_ERR: + cf->data[2] |= CAN_ERR_PROT_BIT; + break; + case SUN4I_STA_FORM_ERR: + cf->data[2] |= CAN_ERR_PROT_FORM; + break; + case SUN4I_STA_STUFF_ERR: + cf->data[2] |= CAN_ERR_PROT_STUFF; + break; + default: + cf->data[2] |= CAN_ERR_PROT_UNSPEC; + cf->data[3] = (ecc & SUN4I_STA_ERR_SEG_CODE) + >> 16; + break; + } + /* error occurred during transmission? */ + if ((ecc & SUN4I_STA_ERR_DIR) == 0) + cf->data[2] |= CAN_ERR_PROT_TX; + } + } + if (isrc & SUN4I_INT_ERR_PASSIVE) { + /* error passive interrupt */ + netdev_dbg(dev, "error passive interrupt\n"); + if (state == CAN_STATE_ERROR_PASSIVE) + state = CAN_STATE_ERROR_WARNING; + else + state = CAN_STATE_ERROR_PASSIVE; + } + if (isrc & SUN4I_INT_ARB_LOST) { + /* arbitration lost interrupt */ + netdev_dbg(dev, "arbitration lost interrupt\n"); + alc = readl(priv->base + SUN4I_REG_STA_ADDR); + priv->can.can_stats.arbitration_lost++; + stats->tx_errors++; + if (likely(skb)) { + cf->can_id |= CAN_ERR_LOSTARB; + cf->data[0] = (alc & 0x1f) >> 8; + } + } + + if (state != priv->can.state) { + tx_state = txerr >= rxerr ? state : 0; + rx_state = txerr <= rxerr ? state : 0; + + if (likely(skb)) + can_change_state(dev, cf, tx_state, rx_state); + else + priv->can.state = state; + if (state == CAN_STATE_BUS_OFF) + can_bus_off(dev); + } + + if (likely(skb)) { + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + } else { + return -ENOMEM; + } + + return 0; +} + +static irqreturn_t sun4i_can_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct sun4ican_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + u8 isrc, status; + int n = 0; + + while ((isrc = readl(priv->base + SUN4I_REG_INT_ADDR)) && + (n < SUN4I_CAN_MAX_IRQ)) { + n++; + status = readl(priv->base + SUN4I_REG_STA_ADDR); + + if (isrc & SUN4I_INT_WAKEUP) + netdev_warn(dev, "wakeup interrupt\n"); + + if (isrc & SUN4I_INT_TBUF_VLD) { + /* transmission complete interrupt */ + stats->tx_bytes += + readl(priv->base + + SUN4I_REG_RBUF_RBACK_START_ADDR) & 0xf; + stats->tx_packets++; + can_get_echo_skb(dev, 0); + netif_wake_queue(dev); + can_led_event(dev, CAN_LED_EVENT_TX); + } + if (isrc & SUN4I_INT_RBUF_VLD) { + /* receive interrupt */ + while (status & SUN4I_STA_RBUF_RDY) { + /* RX buffer is not empty */ + sun4i_can_rx(dev); + status = readl(priv->base + SUN4I_REG_STA_ADDR); + } + } + if (isrc & + (SUN4I_INT_DATA_OR | SUN4I_INT_ERR_WRN | SUN4I_INT_BUS_ERR | + SUN4I_INT_ERR_PASSIVE | SUN4I_INT_ARB_LOST)) { + /* error interrupt */ + if (sun4i_can_err(dev, isrc, status)) + netdev_err(dev, "can't allocate buffer - clearing pending interrupts\n"); + } + /* clear interrupts */ + writel(isrc, priv->base + SUN4I_REG_INT_ADDR); + readl(priv->base + SUN4I_REG_INT_ADDR); + } + if (n >= SUN4I_CAN_MAX_IRQ) + netdev_dbg(dev, "%d messages handled in ISR", n); + + return (n) ? IRQ_HANDLED : IRQ_NONE; +} + +static int sun4ican_open(struct net_device *dev) +{ + struct sun4ican_priv *priv = netdev_priv(dev); + int err; + + /* common open */ + err = open_candev(dev); + if (err) + return err; + + /* register interrupt handler */ + err = request_irq(dev->irq, sun4i_can_interrupt, 0, dev->name, dev); + if (err) { + netdev_err(dev, "request_irq err: %d\n", err); + goto exit_irq; + } + + /* turn on clocking for CAN peripheral block */ + err = clk_prepare_enable(priv->clk); + if (err) { + netdev_err(dev, "could not enable CAN peripheral clock\n"); + goto exit_clock; + } + + err = sun4i_can_start(dev); + if (err) { + netdev_err(dev, "could not start CAN peripheral\n"); + goto exit_can_start; + } + + can_led_event(dev, CAN_LED_EVENT_OPEN); + netif_start_queue(dev); + + return 0; + +exit_can_start: + clk_disable_unprepare(priv->clk); +exit_clock: + free_irq(dev->irq, dev); +exit_irq: + close_candev(dev); + return err; +} + +static int sun4ican_close(struct net_device *dev) +{ + struct sun4ican_priv *priv = netdev_priv(dev); + + netif_stop_queue(dev); + sun4i_can_stop(dev); + clk_disable_unprepare(priv->clk); + + free_irq(dev->irq, dev); + close_candev(dev); + can_led_event(dev, CAN_LED_EVENT_STOP); + + return 0; +} + +static const struct net_device_ops sun4ican_netdev_ops = { + .ndo_open = sun4ican_open, + .ndo_stop = sun4ican_close, + .ndo_start_xmit = sun4ican_start_xmit, +}; + +static const struct of_device_id sun4ican_of_match[] = { + {.compatible = "allwinner,sun4i-a10-can"}, + {}, +}; + +MODULE_DEVICE_TABLE(of, sun4ican_of_match); + +static int sun4ican_remove(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + + unregister_netdev(dev); + free_candev(dev); + + return 0; +} + +static int sun4ican_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct resource *mem; + struct clk *clk; + void __iomem *addr; + int err, irq; + struct net_device *dev; + struct sun4ican_priv *priv; + + clk = of_clk_get(np, 0); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "unable to request clock\n"); + err = -ENODEV; + goto exit; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "could not get a valid irq\n"); + err = -ENODEV; + goto exit; + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + addr = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(addr)) { + err = -EBUSY; + goto exit; + } + + dev = alloc_candev(sizeof(struct sun4ican_priv), 1); + if (!dev) { + dev_err(&pdev->dev, + "could not allocate memory for CAN device\n"); + err = -ENOMEM; + goto exit; + } + + dev->netdev_ops = &sun4ican_netdev_ops; + dev->irq = irq; + dev->flags |= IFF_ECHO; + + priv = netdev_priv(dev); + priv->can.clock.freq = clk_get_rate(clk); + priv->can.bittiming_const = &sun4ican_bittiming_const; + priv->can.do_set_mode = sun4ican_set_mode; + priv->can.do_get_berr_counter = sun4ican_get_berr_counter; + priv->can.ctrlmode_supported = CAN_CTRLMODE_BERR_REPORTING | + CAN_CTRLMODE_LISTENONLY | + CAN_CTRLMODE_LOOPBACK | + CAN_CTRLMODE_PRESUME_ACK | + CAN_CTRLMODE_3_SAMPLES; + priv->base = addr; + priv->clk = clk; + spin_lock_init(&priv->cmdreg_lock); + + platform_set_drvdata(pdev, dev); + SET_NETDEV_DEV(dev, &pdev->dev); + + err = register_candev(dev); + if (err) { + dev_err(&pdev->dev, "registering %s failed (err=%d)\n", + DRV_NAME, err); + goto exit_free; + } + devm_can_led_init(dev); + + dev_info(&pdev->dev, "device registered (base=%p, irq=%d)\n", + priv->base, dev->irq); + + return 0; + +exit_free: + free_candev(dev); +exit: + return err; +} + +static struct platform_driver sun4i_can_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = sun4ican_of_match, + }, + .probe = sun4ican_probe, + .remove = sun4ican_remove, +}; + +module_platform_driver(sun4i_can_driver); + +MODULE_AUTHOR("Peter Chen <xingkongcp@gmail.com>"); +MODULE_AUTHOR("Gerhard Bertelsmann <info@gerhard-bertelsmann.de>"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION(DRV_NAME "CAN driver for Allwinner SoCs (A10/A20)"); diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 1e72722abbf1..efcb1119076f 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -608,8 +608,7 @@ static int bcmgenet_set_coalesce(struct net_device *dev, * transmitted, or when the ring is emtpy. */ if (ec->tx_coalesce_usecs || ec->tx_coalesce_usecs_high || - ec->tx_coalesce_usecs_irq || ec->tx_coalesce_usecs_high || - ec->tx_coalesce_usecs_low) + ec->tx_coalesce_usecs_irq || ec->tx_coalesce_usecs_low) return -EOPNOTSUPP; /* Program all TX queues with the same values, as there is no diff --git a/drivers/net/ethernet/hisilicon/Kconfig b/drivers/net/ethernet/hisilicon/Kconfig index dead17b5d769..165b5a8aa2ea 100644 --- a/drivers/net/ethernet/hisilicon/Kconfig +++ b/drivers/net/ethernet/hisilicon/Kconfig @@ -5,7 +5,7 @@ config NET_VENDOR_HISILICON bool "Hisilicon devices" default y - depends on ARM + depends on ARM || ARM64 ---help--- If you have a network (Ethernet) card belonging to this class, say Y. @@ -27,8 +27,40 @@ config HIP04_ETH select PHYLIB select MARVELL_PHY select MFD_SYSCON + select HNS_MDIO ---help--- If you wish to compile a kernel for a hardware with hisilicon p04 SoC and want to use the internal ethernet then you should answer Y to this. +config HNS_MDIO + tristate "Hisilicon HNS MDIO device Support" + select MDIO + ---help--- + This selects the HNS MDIO support. It is needed by HNS_DSAF to access + the PHY + +config HNS + tristate "Hisilicon Network Subsystem Support (Framework)" + ---help--- + This selects the framework support for Hisilicon Network Subsystem. It + is needed by any driver which provides HNS acceleration engine or make + use of the engine + +config HNS_DSAF + tristate "Hisilicon HNS DSAF device Support" + select HNS + select HNS_MDIO + ---help--- + This selects the DSAF (Distributed System Area Frabric) network + acceleration engine support. The engine is used in Hisilicon hip05, + Hi1610 and further ICT SoC + +config HNS_ENET + tristate "Hisilicon HNS Ethernet Device Support" + select PHYLIB + select HNS + ---help--- + This selects the general ethernet driver for HNS. This module make + use of any HNS AE driver, such as HNS_DSAF + endif # NET_VENDOR_HISILICON diff --git a/drivers/net/ethernet/hisilicon/Makefile b/drivers/net/ethernet/hisilicon/Makefile index 6c14540a4dc5..390b71fb3000 100644 --- a/drivers/net/ethernet/hisilicon/Makefile +++ b/drivers/net/ethernet/hisilicon/Makefile @@ -3,4 +3,6 @@ # obj-$(CONFIG_HIX5HD2_GMAC) += hix5hd2_gmac.o -obj-$(CONFIG_HIP04_ETH) += hip04_mdio.o hip04_eth.o +obj-$(CONFIG_HIP04_ETH) += hip04_eth.o +obj-$(CONFIG_HNS_MDIO) += hns_mdio.o +obj-$(CONFIG_HNS) += hns/ diff --git a/drivers/net/ethernet/hisilicon/hip04_mdio.c b/drivers/net/ethernet/hisilicon/hip04_mdio.c deleted file mode 100644 index fca0a5be1f0f..000000000000 --- a/drivers/net/ethernet/hisilicon/hip04_mdio.c +++ /dev/null @@ -1,185 +0,0 @@ -/* Copyright (c) 2014 Linaro Ltd. - * Copyright (c) 2014 Hisilicon Limited. - * - * 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/module.h> -#include <linux/platform_device.h> -#include <linux/io.h> -#include <linux/of_mdio.h> -#include <linux/delay.h> - -#define MDIO_CMD_REG 0x0 -#define MDIO_ADDR_REG 0x4 -#define MDIO_WDATA_REG 0x8 -#define MDIO_RDATA_REG 0xc -#define MDIO_STA_REG 0x10 - -#define MDIO_START BIT(14) -#define MDIO_R_VALID BIT(1) -#define MDIO_READ (BIT(12) | BIT(11) | MDIO_START) -#define MDIO_WRITE (BIT(12) | BIT(10) | MDIO_START) - -struct hip04_mdio_priv { - void __iomem *base; -}; - -#define WAIT_TIMEOUT 10 -static int hip04_mdio_wait_ready(struct mii_bus *bus) -{ - struct hip04_mdio_priv *priv = bus->priv; - int i; - - for (i = 0; readl_relaxed(priv->base + MDIO_CMD_REG) & MDIO_START; i++) { - if (i == WAIT_TIMEOUT) - return -ETIMEDOUT; - msleep(20); - } - - return 0; -} - -static int hip04_mdio_read(struct mii_bus *bus, int mii_id, int regnum) -{ - struct hip04_mdio_priv *priv = bus->priv; - u32 val; - int ret; - - ret = hip04_mdio_wait_ready(bus); - if (ret < 0) - goto out; - - val = regnum | (mii_id << 5) | MDIO_READ; - writel_relaxed(val, priv->base + MDIO_CMD_REG); - - ret = hip04_mdio_wait_ready(bus); - if (ret < 0) - goto out; - - val = readl_relaxed(priv->base + MDIO_STA_REG); - if (val & MDIO_R_VALID) { - dev_err(bus->parent, "SMI bus read not valid\n"); - ret = -ENODEV; - goto out; - } - - val = readl_relaxed(priv->base + MDIO_RDATA_REG); - ret = val & 0xFFFF; -out: - return ret; -} - -static int hip04_mdio_write(struct mii_bus *bus, int mii_id, - int regnum, u16 value) -{ - struct hip04_mdio_priv *priv = bus->priv; - u32 val; - int ret; - - ret = hip04_mdio_wait_ready(bus); - if (ret < 0) - goto out; - - writel_relaxed(value, priv->base + MDIO_WDATA_REG); - val = regnum | (mii_id << 5) | MDIO_WRITE; - writel_relaxed(val, priv->base + MDIO_CMD_REG); -out: - return ret; -} - -static int hip04_mdio_reset(struct mii_bus *bus) -{ - int temp, i; - - for (i = 0; i < PHY_MAX_ADDR; i++) { - hip04_mdio_write(bus, i, 22, 0); - temp = hip04_mdio_read(bus, i, MII_BMCR); - if (temp < 0) - continue; - - temp |= BMCR_RESET; - if (hip04_mdio_write(bus, i, MII_BMCR, temp) < 0) - continue; - } - - mdelay(500); - return 0; -} - -static int hip04_mdio_probe(struct platform_device *pdev) -{ - struct resource *r; - struct mii_bus *bus; - struct hip04_mdio_priv *priv; - int ret; - - bus = mdiobus_alloc_size(sizeof(struct hip04_mdio_priv)); - if (!bus) { - dev_err(&pdev->dev, "Cannot allocate MDIO bus\n"); - return -ENOMEM; - } - - bus->name = "hip04_mdio_bus"; - bus->read = hip04_mdio_read; - bus->write = hip04_mdio_write; - bus->reset = hip04_mdio_reset; - snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev)); - bus->parent = &pdev->dev; - priv = bus->priv; - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->base = devm_ioremap_resource(&pdev->dev, r); - if (IS_ERR(priv->base)) { - ret = PTR_ERR(priv->base); - goto out_mdio; - } - - ret = of_mdiobus_register(bus, pdev->dev.of_node); - if (ret < 0) { - dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret); - goto out_mdio; - } - - platform_set_drvdata(pdev, bus); - - return 0; - -out_mdio: - mdiobus_free(bus); - return ret; -} - -static int hip04_mdio_remove(struct platform_device *pdev) -{ - struct mii_bus *bus = platform_get_drvdata(pdev); - - mdiobus_unregister(bus); - mdiobus_free(bus); - - return 0; -} - -static const struct of_device_id hip04_mdio_match[] = { - { .compatible = "hisilicon,hip04-mdio" }, - { } -}; -MODULE_DEVICE_TABLE(of, hip04_mdio_match); - -static struct platform_driver hip04_mdio_driver = { - .probe = hip04_mdio_probe, - .remove = hip04_mdio_remove, - .driver = { - .name = "hip04-mdio", - .of_match_table = hip04_mdio_match, - }, -}; - -module_platform_driver(hip04_mdio_driver); - -MODULE_DESCRIPTION("HISILICON P04 MDIO interface driver"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:hip04-mdio"); diff --git a/drivers/net/ethernet/hisilicon/hns/Makefile b/drivers/net/ethernet/hisilicon/hns/Makefile new file mode 100644 index 000000000000..6010c83e38d8 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/Makefile @@ -0,0 +1,12 @@ +# +# Makefile for the HISILICON network device drivers. +# + +obj-$(CONFIG_HNS) += hnae.o + +obj-$(CONFIG_HNS_DSAF) += hns_dsaf.o +hns_dsaf-objs = hns_ae_adapt.o hns_dsaf_gmac.o hns_dsaf_mac.o hns_dsaf_misc.o \ + hns_dsaf_main.o hns_dsaf_ppe.o hns_dsaf_rcb.o hns_dsaf_xgmac.o + +obj-$(CONFIG_HNS_ENET) += hns_enet_drv.o +hns_enet_drv-objs = hns_enet.o hns_ethtool.o diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.c b/drivers/net/ethernet/hisilicon/hns/hnae.c new file mode 100644 index 000000000000..0a0a9e80dcd7 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hnae.c @@ -0,0 +1,507 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * 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/dma-mapping.h> +#include <linux/interrupt.h> +#include <linux/skbuff.h> +#include <linux/slab.h> + +#include "hnae.h" + +#define cls_to_ae_dev(dev) container_of(dev, struct hnae_ae_dev, cls_dev) + +static struct class *hnae_class; + +static void +hnae_list_add(spinlock_t *lock, struct list_head *node, struct list_head *head) +{ + unsigned long flags; + + spin_lock_irqsave(lock, flags); + list_add_tail_rcu(node, head); + spin_unlock_irqrestore(lock, flags); +} + +static void hnae_list_del(spinlock_t *lock, struct list_head *node) +{ + unsigned long flags; + + spin_lock_irqsave(lock, flags); + list_del_rcu(node); + spin_unlock_irqrestore(lock, flags); +} + +static int hnae_alloc_buffer(struct hnae_ring *ring, struct hnae_desc_cb *cb) +{ + unsigned int order = hnae_page_order(ring); + struct page *p = dev_alloc_pages(order); + + if (!p) + return -ENOMEM; + + cb->priv = p; + cb->page_offset = 0; + cb->reuse_flag = 0; + cb->buf = page_address(p); + cb->length = hnae_page_size(ring); + cb->type = DESC_TYPE_PAGE; + + return 0; +} + +static void hnae_free_buffer(struct hnae_ring *ring, struct hnae_desc_cb *cb) +{ + if (cb->type == DESC_TYPE_SKB) + dev_kfree_skb_any((struct sk_buff *)cb->priv); + else if (unlikely(is_rx_ring(ring))) + put_page((struct page *)cb->priv); + memset(cb, 0, sizeof(*cb)); +} + +static int hnae_map_buffer(struct hnae_ring *ring, struct hnae_desc_cb *cb) +{ + cb->dma = dma_map_page(ring_to_dev(ring), cb->priv, 0, + cb->length, ring_to_dma_dir(ring)); + + if (dma_mapping_error(ring_to_dev(ring), cb->dma)) + return -EIO; + + return 0; +} + +static void hnae_unmap_buffer(struct hnae_ring *ring, struct hnae_desc_cb *cb) +{ + if (cb->type == DESC_TYPE_SKB) + dma_unmap_single(ring_to_dev(ring), cb->dma, cb->length, + ring_to_dma_dir(ring)); + else + dma_unmap_page(ring_to_dev(ring), cb->dma, cb->length, + ring_to_dma_dir(ring)); +} + +static struct hnae_buf_ops hnae_bops = { + .alloc_buffer = hnae_alloc_buffer, + .free_buffer = hnae_free_buffer, + .map_buffer = hnae_map_buffer, + .unmap_buffer = hnae_unmap_buffer, +}; + +static int __ae_match(struct device *dev, const void *data) +{ + struct hnae_ae_dev *hdev = cls_to_ae_dev(dev); + const char *ae_id = data; + + if (!strncmp(ae_id, hdev->name, AE_NAME_SIZE)) + return 1; + + return 0; +} + +static struct hnae_ae_dev *find_ae(const char *ae_id) +{ + struct device *dev; + + WARN_ON(!ae_id); + + dev = class_find_device(hnae_class, NULL, ae_id, __ae_match); + + return dev ? cls_to_ae_dev(dev) : NULL; +} + +static void hnae_free_buffers(struct hnae_ring *ring) +{ + int i; + + for (i = 0; i < ring->desc_num; i++) + hnae_free_buffer_detach(ring, i); +} + +/* Allocate memory for raw pkg, and map with dma */ +static int hnae_alloc_buffers(struct hnae_ring *ring) +{ + int i, j, ret; + + for (i = 0; i < ring->desc_num; i++) { + ret = hnae_alloc_buffer_attach(ring, i); + if (ret) + goto out_buffer_fail; + } + + return 0; + +out_buffer_fail: + for (j = i - 1; j >= 0; j--) + hnae_free_buffer_detach(ring, j); + return ret; +} + +/* free desc along with its attached buffer */ +static void hnae_free_desc(struct hnae_ring *ring) +{ + hnae_free_buffers(ring); + dma_unmap_single(ring_to_dev(ring), ring->desc_dma_addr, + ring->desc_num * sizeof(ring->desc[0]), + ring_to_dma_dir(ring)); + ring->desc_dma_addr = 0; + kfree(ring->desc); + ring->desc = NULL; +} + +/* alloc desc, without buffer attached */ +static int hnae_alloc_desc(struct hnae_ring *ring) +{ + int size = ring->desc_num * sizeof(ring->desc[0]); + + ring->desc = kzalloc(size, GFP_KERNEL); + if (!ring->desc) + return -ENOMEM; + + ring->desc_dma_addr = dma_map_single(ring_to_dev(ring), + ring->desc, size, ring_to_dma_dir(ring)); + if (dma_mapping_error(ring_to_dev(ring), ring->desc_dma_addr)) { + ring->desc_dma_addr = 0; + kfree(ring->desc); + ring->desc = NULL; + return -ENOMEM; + } + + return 0; +} + +/* fini ring, also free the buffer for the ring */ +static void hnae_fini_ring(struct hnae_ring *ring) +{ + hnae_free_desc(ring); + kfree(ring->desc_cb); + ring->desc_cb = NULL; + ring->next_to_clean = 0; + ring->next_to_use = 0; +} + +/* init ring, and with buffer for rx ring */ +static int +hnae_init_ring(struct hnae_queue *q, struct hnae_ring *ring, int flags) +{ + int ret; + + if (ring->desc_num <= 0 || ring->buf_size <= 0) + return -EINVAL; + + ring->q = q; + ring->flags = flags; + assert(!ring->desc && !ring->desc_cb && !ring->desc_dma_addr); + + /* not matter for tx or rx ring, the ntc and ntc start from 0 */ + assert(ring->next_to_use == 0); + assert(ring->next_to_clean == 0); + + ring->desc_cb = kcalloc(ring->desc_num, sizeof(ring->desc_cb[0]), + GFP_KERNEL); + if (!ring->desc_cb) { + ret = -ENOMEM; + goto out; + } + + ret = hnae_alloc_desc(ring); + if (ret) + goto out_with_desc_cb; + + if (is_rx_ring(ring)) { + ret = hnae_alloc_buffers(ring); + if (ret) + goto out_with_desc; + } + + return 0; + +out_with_desc: + hnae_free_desc(ring); +out_with_desc_cb: + kfree(ring->desc_cb); + ring->desc_cb = NULL; +out: + return ret; +} + +static int hnae_init_queue(struct hnae_handle *h, struct hnae_queue *q, + struct hnae_ae_dev *dev) +{ + int ret; + + q->dev = dev; + q->handle = h; + + ret = hnae_init_ring(q, &q->tx_ring, q->tx_ring.flags | RINGF_DIR); + if (ret) + goto out; + + ret = hnae_init_ring(q, &q->rx_ring, q->rx_ring.flags & ~RINGF_DIR); + if (ret) + goto out_with_tx_ring; + + if (dev->ops->init_queue) + dev->ops->init_queue(q); + + return 0; + +out_with_tx_ring: + hnae_fini_ring(&q->tx_ring); +out: + return ret; +} + +static void hnae_fini_queue(struct hnae_queue *q) +{ + if (q->dev->ops->fini_queue) + q->dev->ops->fini_queue(q); + + hnae_fini_ring(&q->tx_ring); + hnae_fini_ring(&q->rx_ring); +} + +/** + * ae_chain - define ae chain head + */ +static RAW_NOTIFIER_HEAD(ae_chain); + +int hnae_register_notifier(struct notifier_block *nb) +{ + return raw_notifier_chain_register(&ae_chain, nb); +} +EXPORT_SYMBOL(hnae_register_notifier); + +void hnae_unregister_notifier(struct notifier_block *nb) +{ + if (raw_notifier_chain_unregister(&ae_chain, nb)) + dev_err(NULL, "notifier chain unregister fail\n"); +} +EXPORT_SYMBOL(hnae_unregister_notifier); + +int hnae_reinit_handle(struct hnae_handle *handle) +{ + int i, j; + int ret; + + for (i = 0; i < handle->q_num; i++) /* free ring*/ + hnae_fini_queue(handle->qs[i]); + + if (handle->dev->ops->reset) + handle->dev->ops->reset(handle); + + for (i = 0; i < handle->q_num; i++) {/* reinit ring*/ + ret = hnae_init_queue(handle, handle->qs[i], handle->dev); + if (ret) + goto out_when_init_queue; + } + return 0; +out_when_init_queue: + for (j = i - 1; j >= 0; j--) + hnae_fini_queue(handle->qs[j]); + return ret; +} +EXPORT_SYMBOL(hnae_reinit_handle); + +/* hnae_get_handle - get a handle from the AE + * @owner_dev: the dev use this handle + * @ae_id: the id of the ae to be used + * @ae_opts: the options set for the handle + * @bops: the callbacks for buffer management + * + * return handle ptr or ERR_PTR + */ +struct hnae_handle *hnae_get_handle(struct device *owner_dev, + const char *ae_id, u32 port_id, + struct hnae_buf_ops *bops) +{ + struct hnae_ae_dev *dev; + struct hnae_handle *handle; + int i, j; + int ret; + + dev = find_ae(ae_id); + if (!dev) + return ERR_PTR(-ENODEV); + + handle = dev->ops->get_handle(dev, port_id); + if (IS_ERR(handle)) + return handle; + + handle->dev = dev; + handle->owner_dev = owner_dev; + handle->bops = bops ? bops : &hnae_bops; + handle->eport_id = port_id; + + for (i = 0; i < handle->q_num; i++) { + ret = hnae_init_queue(handle, handle->qs[i], dev); + if (ret) + goto out_when_init_queue; + } + + __module_get(dev->owner); + + hnae_list_add(&dev->lock, &handle->node, &dev->handle_list); + + return handle; + +out_when_init_queue: + for (j = i - 1; j >= 0; j--) + hnae_fini_queue(handle->qs[j]); + + return ERR_PTR(-ENOMEM); +} +EXPORT_SYMBOL(hnae_get_handle); + +void hnae_put_handle(struct hnae_handle *h) +{ + struct hnae_ae_dev *dev = h->dev; + int i; + + for (i = 0; i < h->q_num; i++) + hnae_fini_queue(h->qs[i]); + + if (h->dev->ops->reset) + h->dev->ops->reset(h); + + hnae_list_del(&dev->lock, &h->node); + + if (dev->ops->put_handle) + dev->ops->put_handle(h); + + module_put(dev->owner); +} +EXPORT_SYMBOL(hnae_put_handle); + +static void hnae_release(struct device *dev) +{ +} + +/** + * hnae_ae_register - register a AE engine to hnae framework + * @hdev: the hnae ae engine device + * @owner: the module who provides this dev + * NOTE: the duplicated name will not be checked + */ +int hnae_ae_register(struct hnae_ae_dev *hdev, struct module *owner) +{ + static atomic_t id = ATOMIC_INIT(-1); + int ret; + + if (!hdev->dev) + return -ENODEV; + + if (!hdev->ops || !hdev->ops->get_handle || + !hdev->ops->toggle_ring_irq || + !hdev->ops->toggle_queue_status || + !hdev->ops->get_status || !hdev->ops->adjust_link) + return -EINVAL; + + hdev->owner = owner; + hdev->id = (int)atomic_inc_return(&id); + hdev->cls_dev.parent = hdev->dev; + hdev->cls_dev.class = hnae_class; + hdev->cls_dev.release = hnae_release; + (void)dev_set_name(&hdev->cls_dev, "hnae%d", hdev->id); + ret = device_register(&hdev->cls_dev); + if (ret) + return ret; + + __module_get(THIS_MODULE); + + INIT_LIST_HEAD(&hdev->handle_list); + spin_lock_init(&hdev->lock); + + ret = raw_notifier_call_chain(&ae_chain, HNAE_AE_REGISTER, NULL); + if (ret) + dev_dbg(hdev->dev, + "has not notifier for AE: %s\n", hdev->name); + + return 0; +} +EXPORT_SYMBOL(hnae_ae_register); + +/** + * hnae_ae_unregister - unregisters a HNAE AE engine + * @cdev: the device to unregister + */ +void hnae_ae_unregister(struct hnae_ae_dev *hdev) +{ + device_unregister(&hdev->cls_dev); + module_put(THIS_MODULE); +} +EXPORT_SYMBOL(hnae_ae_unregister); + +static ssize_t handles_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t s = 0; + struct hnae_ae_dev *hdev = cls_to_ae_dev(dev); + struct hnae_handle *h; + int i = 0, j; + + list_for_each_entry_rcu(h, &hdev->handle_list, node) { + s += sprintf(buf + s, "handle %d (eport_id=%u from %s):\n", + i++, h->eport_id, h->dev->name); + for (j = 0; j < h->q_num; j++) { + s += sprintf(buf + s, "\tqueue[%d] on 0x%llx\n", + j, (u64)h->qs[i]->io_base); +#define HANDEL_TX_MSG "\t\ttx_ring on 0x%llx:%u,%u,%u,%u,%u,%llu,%llu\n" + s += sprintf(buf + s, + HANDEL_TX_MSG, + (u64)h->qs[i]->tx_ring.io_base, + h->qs[i]->tx_ring.buf_size, + h->qs[i]->tx_ring.desc_num, + h->qs[i]->tx_ring.max_desc_num_per_pkt, + h->qs[i]->tx_ring.max_raw_data_sz_per_desc, + h->qs[i]->tx_ring.max_pkt_size, + h->qs[i]->tx_ring.stats.sw_err_cnt, + h->qs[i]->tx_ring.stats.io_err_cnt); + s += sprintf(buf + s, + "\t\trx_ring on 0x%llx:%u,%u,%llu,%llu,%llu\n", + (u64)h->qs[i]->rx_ring.io_base, + h->qs[i]->rx_ring.buf_size, + h->qs[i]->rx_ring.desc_num, + h->qs[i]->rx_ring.stats.sw_err_cnt, + h->qs[i]->rx_ring.stats.io_err_cnt, + h->qs[i]->rx_ring.stats.seg_pkt_cnt); + } + } + + return s; +} + +static DEVICE_ATTR_RO(handles); +static struct attribute *hnae_class_attrs[] = { + &dev_attr_handles.attr, + NULL, +}; +ATTRIBUTE_GROUPS(hnae_class); + +static int __init hnae_init(void) +{ + hnae_class = class_create(THIS_MODULE, "hnae"); + if (IS_ERR(hnae_class)) + return PTR_ERR(hnae_class); + + hnae_class->dev_groups = hnae_class_groups; + return 0; +} + +static void __exit hnae_exit(void) +{ + class_destroy(hnae_class); +} + +subsys_initcall(hnae_init); +module_exit(hnae_exit); + +MODULE_AUTHOR("Hisilicon, Inc."); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Hisilicon Network Acceleration Engine Framework"); + +/* vi: set tw=78 noet: */ diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.h b/drivers/net/ethernet/hisilicon/hns/hnae.h new file mode 100644 index 000000000000..5edd8cdc2485 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hnae.h @@ -0,0 +1,583 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * 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 __HNAE_H +#define __HNAE_H + +/* Names used in this framework: + * ae handle (handle): + * a set of queues provided by AE + * ring buffer queue (rbq): + * the channel between upper layer and the AE, can do tx and rx + * ring: + * a tx or rx channel within a rbq + * ring description (desc): + * an element in the ring with packet information + * buffer: + * a memory region referred by desc with the full packet payload + * + * "num" means a static number set as a parameter, "count" mean a dynamic + * number set while running + * "cb" means control block + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/notifier.h> +#include <linux/types.h> + +#define HNAE_DRIVER_VERSION "1.3.0" +#define HNAE_DRIVER_NAME "hns" +#define HNAE_COPYRIGHT "Copyright(c) 2015 Huawei Corporation." +#define HNAE_DRIVER_STRING "Hisilicon Network Subsystem Driver" +#define HNAE_DEFAULT_DEVICE_DESCR "Hisilicon Network Subsystem" + +#ifdef DEBUG + +#ifndef assert +#define assert(expr) \ +do { \ + if (!(expr)) { \ + pr_err("Assertion failed! %s, %s, %s, line %d\n", \ + #expr, __FILE__, __func__, __LINE__); \ + } \ +} while (0) +#endif + +#else + +#ifndef assert +#define assert(expr) +#endif + +#endif + +#define AE_VERSION_1 ('6' << 16 | '6' << 8 | '0') +#define AE_VERSION_2 ('1' << 24 | '6' << 16 | '1' << 8 | '0') +#define AE_NAME_SIZE 16 + +/* some said the RX and TX RCB format should not be the same in the future. But + * it is the same now... + */ +#define RCB_REG_BASEADDR_L 0x00 /* P660 support only 32bit accessing */ +#define RCB_REG_BASEADDR_H 0x04 +#define RCB_REG_BD_NUM 0x08 +#define RCB_REG_BD_LEN 0x0C +#define RCB_REG_PKTLINE 0x10 +#define RCB_REG_TAIL 0x18 +#define RCB_REG_HEAD 0x1C +#define RCB_REG_FBDNUM 0x20 +#define RCB_REG_OFFSET 0x24 /* pkt num to be handled */ +#define RCB_REG_PKTNUM_RECORD 0x2C /* total pkt received */ + +#define HNS_RX_HEAD_SIZE 256 + +#define HNAE_AE_REGISTER 0x1 + +#define RCB_RING_NAME_LEN 16 + +enum hnae_led_state { + HNAE_LED_INACTIVE, + HNAE_LED_ACTIVE, + HNAE_LED_ON, + HNAE_LED_OFF +}; + +#define HNS_RX_FLAG_VLAN_PRESENT 0x1 +#define HNS_RX_FLAG_L3ID_IPV4 0x0 +#define HNS_RX_FLAG_L3ID_IPV6 0x1 +#define HNS_RX_FLAG_L4ID_UDP 0x0 +#define HNS_RX_FLAG_L4ID_TCP 0x1 + +#define HNS_TXD_ASID_S 0 +#define HNS_TXD_ASID_M (0xff << HNS_TXD_ASID_S) +#define HNS_TXD_BUFNUM_S 8 +#define HNS_TXD_BUFNUM_M (0x3 << HNS_TXD_BUFNUM_S) +#define HNS_TXD_PORTID_S 10 +#define HNS_TXD_PORTID_M (0x7 << HNS_TXD_PORTID_S) + +#define HNS_TXD_RA_B 8 +#define HNS_TXD_RI_B 9 +#define HNS_TXD_L4CS_B 10 +#define HNS_TXD_L3CS_B 11 +#define HNS_TXD_FE_B 12 +#define HNS_TXD_VLD_B 13 +#define HNS_TXD_IPOFFSET_S 14 +#define HNS_TXD_IPOFFSET_M (0xff << HNS_TXD_IPOFFSET_S) + +#define HNS_RXD_IPOFFSET_S 0 +#define HNS_RXD_IPOFFSET_M (0xff << HNS_TXD_IPOFFSET_S) +#define HNS_RXD_BUFNUM_S 8 +#define HNS_RXD_BUFNUM_M (0x3 << HNS_RXD_BUFNUM_S) +#define HNS_RXD_PORTID_S 10 +#define HNS_RXD_PORTID_M (0x7 << HNS_RXD_PORTID_S) +#define HNS_RXD_DMAC_S 13 +#define HNS_RXD_DMAC_M (0x3 << HNS_RXD_DMAC_S) +#define HNS_RXD_VLAN_S 15 +#define HNS_RXD_VLAN_M (0x3 << HNS_RXD_VLAN_S) +#define HNS_RXD_L3ID_S 17 +#define HNS_RXD_L3ID_M (0xf << HNS_RXD_L3ID_S) +#define HNS_RXD_L4ID_S 21 +#define HNS_RXD_L4ID_M (0xf << HNS_RXD_L4ID_S) +#define HNS_RXD_FE_B 25 +#define HNS_RXD_FRAG_B 26 +#define HNS_RXD_VLD_B 27 +#define HNS_RXD_L2E_B 28 +#define HNS_RXD_L3E_B 29 +#define HNS_RXD_L4E_B 30 +#define HNS_RXD_DROP_B 31 + +#define HNS_RXD_VLANID_S 8 +#define HNS_RXD_VLANID_M (0xfff << HNS_RXD_VLANID_S) +#define HNS_RXD_CFI_B 20 +#define HNS_RXD_PRI_S 21 +#define HNS_RXD_PRI_M (0x7 << HNS_RXD_PRI_S) +#define HNS_RXD_ASID_S 24 +#define HNS_RXD_ASID_M (0xff << HNS_RXD_ASID_S) + +/* hardware spec ring buffer format */ +struct __packed hnae_desc { + __le64 addr; + union { + struct { + __le16 asid_bufnum_pid; + __le16 send_size; + __le32 flag_ipoffset; + __le32 reserved_3[4]; + } tx; + + struct { + __le32 ipoff_bnum_pid_flag; + __le16 pkt_len; + __le16 size; + __le32 vlan_pri_asid; + __le32 reserved_2[3]; + } rx; + }; +}; + +struct hnae_desc_cb { + dma_addr_t dma; /* dma address of this desc */ + void *buf; /* cpu addr for a desc */ + + /* priv data for the desc, e.g. skb when use with ip stack*/ + void *priv; + u16 page_offset; + u16 reuse_flag; + + u16 length; /* length of the buffer */ + + /* desc type, used by the ring user to mark the type of the priv data */ + u16 type; +}; + +#define setflags(flags, bits) ((flags) |= (bits)) +#define unsetflags(flags, bits) ((flags) &= ~(bits)) + +/* hnae_ring->flags fields */ +#define RINGF_DIR 0x1 /* TX or RX ring, set if TX */ +#define is_tx_ring(ring) ((ring)->flags & RINGF_DIR) +#define is_rx_ring(ring) (!is_tx_ring(ring)) +#define ring_to_dma_dir(ring) (is_tx_ring(ring) ? \ + DMA_TO_DEVICE : DMA_FROM_DEVICE) + +struct ring_stats { + u64 io_err_cnt; + u64 sw_err_cnt; + u64 seg_pkt_cnt; + union { + struct { + u64 tx_pkts; + u64 tx_bytes; + u64 tx_err_cnt; + u64 restart_queue; + u64 tx_busy; + }; + struct { + u64 rx_pkts; + u64 rx_bytes; + u64 rx_err_cnt; + u64 reuse_pg_cnt; + u64 err_pkt_len; + u64 non_vld_descs; + u64 err_bd_num; + u64 l2_err; + u64 l3l4_csum_err; + }; + }; +}; + +struct hnae_queue; + +struct hnae_ring { + u8 __iomem *io_base; /* base io address for the ring */ + struct hnae_desc *desc; /* dma map address space */ + struct hnae_desc_cb *desc_cb; + struct hnae_queue *q; + int irq; + char ring_name[RCB_RING_NAME_LEN]; + + /* statistic */ + struct ring_stats stats; + + dma_addr_t desc_dma_addr; + u32 buf_size; /* size for hnae_desc->addr, preset by AE */ + u16 desc_num; /* total number of desc */ + u16 max_desc_num_per_pkt; + u16 max_raw_data_sz_per_desc; + u16 max_pkt_size; + int next_to_use; /* idx of next spare desc */ + + /* idx of lastest sent desc, the ring is empty when equal to + * next_to_use + */ + int next_to_clean; + + int flags; /* ring attribute */ + int irq_init_flag; +}; + +#define ring_ptr_move_fw(ring, p) \ + ((ring)->p = ((ring)->p + 1) % (ring)->desc_num) +#define ring_ptr_move_bw(ring, p) \ + ((ring)->p = ((ring)->p - 1 + (ring)->desc_num) % (ring)->desc_num) + +enum hns_desc_type { + DESC_TYPE_SKB, + DESC_TYPE_PAGE, +}; + +#define assert_is_ring_idx(ring, idx) \ + assert((idx) >= 0 && (idx) < (ring)->desc_num) + +/* the distance between [begin, end) in a ring buffer + * note: there is a unuse slot between the begin and the end + */ +static inline int ring_dist(struct hnae_ring *ring, int begin, int end) +{ + assert_is_ring_idx(ring, begin); + assert_is_ring_idx(ring, end); + + return (end - begin + ring->desc_num) % ring->desc_num; +} + +static inline int ring_space(struct hnae_ring *ring) +{ + return ring->desc_num - + ring_dist(ring, ring->next_to_clean, ring->next_to_use) - 1; +} + +static inline int is_ring_empty(struct hnae_ring *ring) +{ + assert_is_ring_idx(ring, ring->next_to_use); + assert_is_ring_idx(ring, ring->next_to_clean); + + return ring->next_to_use == ring->next_to_clean; +} + +#define hnae_buf_size(_ring) ((_ring)->buf_size) +#define hnae_page_order(_ring) (get_order(hnae_buf_size(_ring))) +#define hnae_page_size(_ring) (PAGE_SIZE << hnae_page_order(_ring)) + +struct hnae_handle; + +/* allocate and dma map space for hnae desc */ +struct hnae_buf_ops { + int (*alloc_buffer)(struct hnae_ring *ring, struct hnae_desc_cb *cb); + void (*free_buffer)(struct hnae_ring *ring, struct hnae_desc_cb *cb); + int (*map_buffer)(struct hnae_ring *ring, struct hnae_desc_cb *cb); + void (*unmap_buffer)(struct hnae_ring *ring, struct hnae_desc_cb *cb); +}; + +struct hnae_queue { + void __iomem *io_base; + phys_addr_t phy_base; + struct hnae_ae_dev *dev; /* the device who use this queue */ + struct hnae_ring rx_ring, tx_ring; + struct hnae_handle *handle; +}; + +/*hnae loop mode*/ +enum hnae_loop { + MAC_INTERNALLOOP_MAC = 0, + MAC_INTERNALLOOP_SERDES, + MAC_INTERNALLOOP_PHY, + MAC_LOOP_NONE, +}; + +/*hnae port type*/ +enum hnae_port_type { + HNAE_PORT_SERVICE = 0, + HNAE_PORT_DEBUG +}; + +/* This struct defines the operation on the handle. + * + * get_handle(): (mandatory) + * Get a handle from AE according to its name and options. + * the AE driver should manage the space used by handle and its queues while + * the HNAE framework will allocate desc and desc_cb for all rings in the + * queues. + * put_handle(): + * Release the handle. + * start(): + * Enable the hardware, include all queues + * stop(): + * Disable the hardware + * set_opts(): (mandatory) + * Set options to the AE + * get_opts(): (mandatory) + * Get options from the AE + * get_status(): + * Get the carrier state of the back channel of the handle, 1 for ok, 0 for + * non-ok + * toggle_ring_irq(): (mandatory) + * Set the ring irq to be enabled(0) or disable(1) + * toggle_queue_status(): (mandatory) + * Set the queue to be enabled(1) or disable(0), this will not change the + * ring irq state + * adjust_link() + * adjust link status + * set_loopback() + * set loopback + * get_ring_bdnum_limit() + * get ring bd number limit + * get_pauseparam() + * get tx and rx of pause frame use + * set_autoneg() + * set auto autonegotiation of pause frame use + * get_autoneg() + * get auto autonegotiation of pause frame use + * set_pauseparam() + * set tx and rx of pause frame use + * get_coalesce_usecs() + * get usecs to delay a TX interrupt after a packet is sent + * get_rx_max_coalesced_frames() + * get Maximum number of packets to be sent before a TX interrupt. + * set_coalesce_usecs() + * set usecs to delay a TX interrupt after a packet is sent + * set_coalesce_frames() + * set Maximum number of packets to be sent before a TX interrupt. + * get_ringnum() + * get RX/TX ring number + * get_max_ringnum() + * get RX/TX ring maximum number + * get_mac_addr() + * get mac address + * set_mac_addr() + * set mac address + * set_mc_addr() + * set multicast mode + * set_mtu() + * set mtu + * update_stats() + * update Old network device statistics + * get_ethtool_stats() + * get ethtool network device statistics + * get_strings() + * get a set of strings that describe the requested objects + * get_sset_count() + * get number of strings that @get_strings will write + * update_led_status() + * update the led status + * set_led_id() + * set led id + * get_regs() + * get regs dump + * get_regs_len() + * get the len of the regs dump + */ +struct hnae_ae_ops { + struct hnae_handle *(*get_handle)(struct hnae_ae_dev *dev, + u32 port_id); + void (*put_handle)(struct hnae_handle *handle); + void (*init_queue)(struct hnae_queue *q); + void (*fini_queue)(struct hnae_queue *q); + int (*start)(struct hnae_handle *handle); + void (*stop)(struct hnae_handle *handle); + void (*reset)(struct hnae_handle *handle); + int (*set_opts)(struct hnae_handle *handle, int type, void *opts); + int (*get_opts)(struct hnae_handle *handle, int type, void **opts); + int (*get_status)(struct hnae_handle *handle); + int (*get_info)(struct hnae_handle *handle, + u8 *auto_neg, u16 *speed, u8 *duplex); + void (*toggle_ring_irq)(struct hnae_ring *ring, u32 val); + void (*toggle_queue_status)(struct hnae_queue *queue, u32 val); + void (*adjust_link)(struct hnae_handle *handle, int speed, int duplex); + int (*set_loopback)(struct hnae_handle *handle, + enum hnae_loop loop_mode, int en); + void (*get_ring_bdnum_limit)(struct hnae_queue *queue, + u32 *uplimit); + void (*get_pauseparam)(struct hnae_handle *handle, + u32 *auto_neg, u32 *rx_en, u32 *tx_en); + int (*set_autoneg)(struct hnae_handle *handle, u8 enable); + int (*get_autoneg)(struct hnae_handle *handle); + int (*set_pauseparam)(struct hnae_handle *handle, + u32 auto_neg, u32 rx_en, u32 tx_en); + void (*get_coalesce_usecs)(struct hnae_handle *handle, + u32 *tx_usecs, u32 *rx_usecs); + void (*get_rx_max_coalesced_frames)(struct hnae_handle *handle, + u32 *tx_frames, u32 *rx_frames); + void (*set_coalesce_usecs)(struct hnae_handle *handle, u32 timeout); + int (*set_coalesce_frames)(struct hnae_handle *handle, + u32 coalesce_frames); + int (*get_mac_addr)(struct hnae_handle *handle, void **p); + int (*set_mac_addr)(struct hnae_handle *handle, void *p); + int (*set_mc_addr)(struct hnae_handle *handle, void *addr); + int (*set_mtu)(struct hnae_handle *handle, int new_mtu); + void (*update_stats)(struct hnae_handle *handle, + struct net_device_stats *net_stats); + void (*get_stats)(struct hnae_handle *handle, u64 *data); + void (*get_strings)(struct hnae_handle *handle, + u32 stringset, u8 *data); + int (*get_sset_count)(struct hnae_handle *handle, int stringset); + void (*update_led_status)(struct hnae_handle *handle); + int (*set_led_id)(struct hnae_handle *handle, + enum hnae_led_state status); + void (*get_regs)(struct hnae_handle *handle, void *data); + int (*get_regs_len)(struct hnae_handle *handle); +}; + +struct hnae_ae_dev { + struct device cls_dev; /* the class dev */ + struct device *dev; /* the presented dev */ + struct hnae_ae_ops *ops; + struct list_head node; + struct module *owner; /* the module who provides this dev */ + int id; + char name[AE_NAME_SIZE]; + struct list_head handle_list; + spinlock_t lock; /* lock to protect the handle_list */ +}; + +struct hnae_handle { + struct device *owner_dev; /* the device which make use of this handle */ + struct hnae_ae_dev *dev; /* the device who provides this handle */ + struct device_node *phy_node; + phy_interface_t phy_if; + u32 if_support; + int q_num; + int vf_id; + u32 eport_id; + enum hnae_port_type port_type; + struct list_head node; /* list to hnae_ae_dev->handle_list */ + struct hnae_buf_ops *bops; /* operation for the buffer */ + struct hnae_queue **qs; /* array base of all queues */ +}; + +#define ring_to_dev(ring) ((ring)->q->dev->dev) + +struct hnae_handle *hnae_get_handle(struct device *owner_dev, const char *ae_id, + u32 port_id, struct hnae_buf_ops *bops); +void hnae_put_handle(struct hnae_handle *handle); +int hnae_ae_register(struct hnae_ae_dev *dev, struct module *owner); +void hnae_ae_unregister(struct hnae_ae_dev *dev); + +int hnae_register_notifier(struct notifier_block *nb); +void hnae_unregister_notifier(struct notifier_block *nb); +int hnae_reinit_handle(struct hnae_handle *handle); + +#define hnae_queue_xmit(q, buf_num) writel_relaxed(buf_num, \ + (q)->tx_ring.io_base + RCB_REG_TAIL) + +#ifndef assert +#define assert(cond) +#endif + +static inline int hnae_reserve_buffer_map(struct hnae_ring *ring, + struct hnae_desc_cb *cb) +{ + struct hnae_buf_ops *bops = ring->q->handle->bops; + int ret; + + ret = bops->alloc_buffer(ring, cb); + if (ret) + goto out; + + ret = bops->map_buffer(ring, cb); + if (ret) + goto out_with_buf; + + return 0; + +out_with_buf: + bops->free_buffer(ring, cb); +out: + return ret; +} + +static inline int hnae_alloc_buffer_attach(struct hnae_ring *ring, int i) +{ + int ret = hnae_reserve_buffer_map(ring, &ring->desc_cb[i]); + + if (ret) + return ret; + + ring->desc[i].addr = (__le64)ring->desc_cb[i].dma; + + return 0; +} + +static inline void hnae_buffer_detach(struct hnae_ring *ring, int i) +{ + ring->q->handle->bops->unmap_buffer(ring, &ring->desc_cb[i]); + ring->desc[i].addr = 0; +} + +static inline void hnae_free_buffer_detach(struct hnae_ring *ring, int i) +{ + struct hnae_buf_ops *bops = ring->q->handle->bops; + struct hnae_desc_cb *cb = &ring->desc_cb[i]; + + if (!ring->desc_cb[i].dma) + return; + + hnae_buffer_detach(ring, i); + bops->free_buffer(ring, cb); +} + +/* detach a in-used buffer and replace with a reserved one */ +static inline void hnae_replace_buffer(struct hnae_ring *ring, int i, + struct hnae_desc_cb *res_cb) +{ + struct hnae_buf_ops *bops = ring->q->handle->bops; + struct hnae_desc_cb tmp_cb = ring->desc_cb[i]; + + bops->unmap_buffer(ring, &ring->desc_cb[i]); + ring->desc_cb[i] = *res_cb; + *res_cb = tmp_cb; + ring->desc[i].addr = (__le64)ring->desc_cb[i].dma; + ring->desc[i].rx.ipoff_bnum_pid_flag = 0; +} + +static inline void hnae_reuse_buffer(struct hnae_ring *ring, int i) +{ + ring->desc_cb[i].reuse_flag = 0; + ring->desc[i].addr = (__le64)(ring->desc_cb[i].dma + + ring->desc_cb[i].page_offset); + ring->desc[i].rx.ipoff_bnum_pid_flag = 0; +} + +#define hnae_set_field(origin, mask, shift, val) \ + do { \ + (origin) &= (~(mask)); \ + (origin) |= ((val) << (shift)) & (mask); \ + } while (0) + +#define hnae_set_bit(origin, shift, val) \ + hnae_set_field((origin), (0x1 << (shift)), (shift), (val)) + +#define hnae_get_field(origin, mask, shift) (((origin) & (mask)) >> (shift)) + +#define hnae_get_bit(origin, shift) \ + hnae_get_field((origin), (0x1 << (shift)), (shift)) + +#endif diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c new file mode 100644 index 000000000000..a2c72f84e397 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c @@ -0,0 +1,777 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * 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/etherdevice.h> +#include <linux/netdevice.h> +#include <linux/spinlock.h> + +#include "hnae.h" +#include "hns_dsaf_mac.h" +#include "hns_dsaf_main.h" +#include "hns_dsaf_ppe.h" +#include "hns_dsaf_rcb.h" + +#define AE_NAME_PORT_ID_IDX 6 +#define ETH_STATIC_REG 1 +#define ETH_DUMP_REG 5 +#define ETH_GSTRING_LEN 32 + +static struct hns_mac_cb *hns_get_mac_cb(struct hnae_handle *handle) +{ + struct hnae_vf_cb *vf_cb = hns_ae_get_vf_cb(handle); + + return vf_cb->mac_cb; +} + +/** + * hns_ae_map_eport_to_dport - translate enet port id to dsaf port id + * @port_id: enet port id + *: debug port 0-1, service port 2 -7 (dsaf mode only 2) + * return: dsaf port id + *: service ports 0 - 5, debug port 6-7 + **/ +static int hns_ae_map_eport_to_dport(u32 port_id) +{ + int port_index; + + if (port_id < DSAF_DEBUG_NW_NUM) + port_index = port_id + DSAF_SERVICE_PORT_NUM_PER_DSAF; + else + port_index = port_id - DSAF_DEBUG_NW_NUM; + + return port_index; +} + +static struct dsaf_device *hns_ae_get_dsaf_dev(struct hnae_ae_dev *dev) +{ + return container_of(dev, struct dsaf_device, ae_dev); +} + +static struct hns_ppe_cb *hns_get_ppe_cb(struct hnae_handle *handle) +{ + int ppe_index; + int ppe_common_index; + struct ppe_common_cb *ppe_comm; + struct hnae_vf_cb *vf_cb = hns_ae_get_vf_cb(handle); + + if (vf_cb->port_index < DSAF_SERVICE_PORT_NUM_PER_DSAF) { + ppe_index = vf_cb->port_index; + ppe_common_index = 0; + } else { + ppe_index = 0; + ppe_common_index = + vf_cb->port_index - DSAF_SERVICE_PORT_NUM_PER_DSAF + 1; + } + ppe_comm = vf_cb->dsaf_dev->ppe_common[ppe_common_index]; + return &ppe_comm->ppe_cb[ppe_index]; +} + +static int hns_ae_get_q_num_per_vf( + struct dsaf_device *dsaf_dev, int port) +{ + int common_idx = hns_dsaf_get_comm_idx_by_port(port); + + return dsaf_dev->rcb_common[common_idx]->max_q_per_vf; +} + +static int hns_ae_get_vf_num_per_port( + struct dsaf_device *dsaf_dev, int port) +{ + int common_idx = hns_dsaf_get_comm_idx_by_port(port); + + return dsaf_dev->rcb_common[common_idx]->max_vfn; +} + +static struct ring_pair_cb *hns_ae_get_base_ring_pair( + struct dsaf_device *dsaf_dev, int port) +{ + int common_idx = hns_dsaf_get_comm_idx_by_port(port); + struct rcb_common_cb *rcb_comm = dsaf_dev->rcb_common[common_idx]; + int q_num = rcb_comm->max_q_per_vf; + int vf_num = rcb_comm->max_vfn; + + if (common_idx == HNS_DSAF_COMM_SERVICE_NW_IDX) + return &rcb_comm->ring_pair_cb[port * q_num * vf_num]; + else + return &rcb_comm->ring_pair_cb[0]; +} + +static struct ring_pair_cb *hns_ae_get_ring_pair(struct hnae_queue *q) +{ + return container_of(q, struct ring_pair_cb, q); +} + +struct hnae_handle *hns_ae_get_handle(struct hnae_ae_dev *dev, + u32 port_id) +{ + int port_idx; + int vfnum_per_port; + int qnum_per_vf; + int i; + struct dsaf_device *dsaf_dev; + struct hnae_handle *ae_handle; + struct ring_pair_cb *ring_pair_cb; + struct hnae_vf_cb *vf_cb; + + dsaf_dev = hns_ae_get_dsaf_dev(dev); + port_idx = hns_ae_map_eport_to_dport(port_id); + + ring_pair_cb = hns_ae_get_base_ring_pair(dsaf_dev, port_idx); + vfnum_per_port = hns_ae_get_vf_num_per_port(dsaf_dev, port_idx); + qnum_per_vf = hns_ae_get_q_num_per_vf(dsaf_dev, port_idx); + + vf_cb = kzalloc(sizeof(*vf_cb) + + qnum_per_vf * sizeof(struct hnae_queue *), GFP_KERNEL); + if (unlikely(!vf_cb)) { + dev_err(dsaf_dev->dev, "malloc vf_cb fail!\n"); + ae_handle = ERR_PTR(-ENOMEM); + goto handle_err; + } + ae_handle = &vf_cb->ae_handle; + /* ae_handle Init */ + ae_handle->owner_dev = dsaf_dev->dev; + ae_handle->dev = dev; + ae_handle->q_num = qnum_per_vf; + + /* find ring pair, and set vf id*/ + for (ae_handle->vf_id = 0; + ae_handle->vf_id < vfnum_per_port; ae_handle->vf_id++) { + if (!ring_pair_cb->used_by_vf) + break; + ring_pair_cb += qnum_per_vf; + } + if (ae_handle->vf_id >= vfnum_per_port) { + dev_err(dsaf_dev->dev, "malloc queue fail!\n"); + ae_handle = ERR_PTR(-EINVAL); + goto vf_id_err; + } + + ae_handle->qs = (struct hnae_queue **)(&ae_handle->qs + 1); + for (i = 0; i < qnum_per_vf; i++) { + ae_handle->qs[i] = &ring_pair_cb->q; + ae_handle->qs[i]->rx_ring.q = ae_handle->qs[i]; + ae_handle->qs[i]->tx_ring.q = ae_handle->qs[i]; + + ring_pair_cb->used_by_vf = 1; + if (port_idx < DSAF_SERVICE_PORT_NUM_PER_DSAF) + ring_pair_cb->port_id_in_dsa = port_idx; + else + ring_pair_cb->port_id_in_dsa = 0; + + ring_pair_cb++; + } + + vf_cb->dsaf_dev = dsaf_dev; + vf_cb->port_index = port_idx; + vf_cb->mac_cb = &dsaf_dev->mac_cb[port_idx]; + + ae_handle->phy_if = vf_cb->mac_cb->phy_if; + ae_handle->phy_node = vf_cb->mac_cb->phy_node; + ae_handle->if_support = vf_cb->mac_cb->if_support; + ae_handle->port_type = vf_cb->mac_cb->mac_type; + + return ae_handle; +vf_id_err: + kfree(vf_cb); +handle_err: + return ae_handle; +} + +static void hns_ae_put_handle(struct hnae_handle *handle) +{ + struct hnae_vf_cb *vf_cb = hns_ae_get_vf_cb(handle); + int i; + + vf_cb->mac_cb = NULL; + + kfree(vf_cb); + + for (i = 0; i < handle->q_num; i++) + hns_ae_get_ring_pair(handle->qs[i])->used_by_vf = 0; +} + +static void hns_ae_ring_enable_all(struct hnae_handle *handle, int val) +{ + int q_num = handle->q_num; + int i; + + for (i = 0; i < q_num; i++) + hns_rcb_ring_enable_hw(handle->qs[i], val); +} + +static void hns_ae_init_queue(struct hnae_queue *q) +{ + struct ring_pair_cb *ring = + container_of(q, struct ring_pair_cb, q); + + hns_rcb_init_hw(ring); +} + +static void hns_ae_fini_queue(struct hnae_queue *q) +{ + struct hnae_vf_cb *vf_cb = hns_ae_get_vf_cb(q->handle); + + if (vf_cb->mac_cb->mac_type == HNAE_PORT_SERVICE) + hns_rcb_reset_ring_hw(q); +} + +static int hns_ae_set_mac_address(struct hnae_handle *handle, void *p) +{ + int ret; + struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle); + + if (!p || !is_valid_ether_addr((const u8 *)p)) { + dev_err(handle->owner_dev, "is not valid ether addr !\n"); + return -EADDRNOTAVAIL; + } + + ret = hns_mac_change_vf_addr(mac_cb, handle->vf_id, p); + if (ret != 0) { + dev_err(handle->owner_dev, + "set_mac_address fail, ret=%d!\n", ret); + return ret; + } + + return 0; +} + +static int hns_ae_set_multicast_one(struct hnae_handle *handle, void *addr) +{ + int ret; + char *mac_addr = (char *)addr; + struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle); + + assert(mac_cb); + + if (mac_cb->mac_type != HNAE_PORT_SERVICE) + return 0; + + ret = hns_mac_set_multi(mac_cb, mac_cb->mac_id, mac_addr, ENABLE); + if (ret) { + dev_err(handle->owner_dev, + "mac add mul_mac:%pM port%d fail, ret = %#x!\n", + mac_addr, mac_cb->mac_id, ret); + return ret; + } + + ret = hns_mac_set_multi(mac_cb, DSAF_BASE_INNER_PORT_NUM, + mac_addr, ENABLE); + if (ret) + dev_err(handle->owner_dev, + "mac add mul_mac:%pM port%d fail, ret = %#x!\n", + mac_addr, DSAF_BASE_INNER_PORT_NUM, ret); + + return ret; +} + +static int hns_ae_set_mtu(struct hnae_handle *handle, int new_mtu) +{ + struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle); + + return hns_mac_set_mtu(mac_cb, new_mtu); +} + +static int hns_ae_start(struct hnae_handle *handle) +{ + int ret; + struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle); + + ret = hns_mac_vm_config_bc_en(mac_cb, 0, ENABLE); + if (ret) + return ret; + + hns_ae_ring_enable_all(handle, 1); + msleep(100); + + hns_mac_start(mac_cb); + + return 0; +} + +void hns_ae_stop(struct hnae_handle *handle) +{ + struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle); + + /* just clean tx fbd, neednot rx fbd*/ + hns_rcb_wait_fbd_clean(handle->qs, handle->q_num, RCB_INT_FLAG_TX); + + msleep(20); + + hns_mac_stop(mac_cb); + + usleep_range(10000, 20000); + + hns_ae_ring_enable_all(handle, 0); + + (void)hns_mac_vm_config_bc_en(mac_cb, 0, DISABLE); +} + +static void hns_ae_reset(struct hnae_handle *handle) +{ + struct hnae_vf_cb *vf_cb = hns_ae_get_vf_cb(handle); + + if (vf_cb->mac_cb->mac_type == HNAE_PORT_DEBUG) { + u8 ppe_common_index = + vf_cb->port_index - DSAF_SERVICE_PORT_NUM_PER_DSAF + 1; + + hns_mac_reset(vf_cb->mac_cb); + hns_ppe_reset_common(vf_cb->dsaf_dev, ppe_common_index); + } +} + +void hns_ae_toggle_ring_irq(struct hnae_ring *ring, u32 mask) +{ + u32 flag; + + if (is_tx_ring(ring)) + flag = RCB_INT_FLAG_TX; + else + flag = RCB_INT_FLAG_RX; + + hns_rcb_int_clr_hw(ring->q, flag); + hns_rcb_int_ctrl_hw(ring->q, flag, mask); +} + +static void hns_ae_toggle_queue_status(struct hnae_queue *queue, u32 val) +{ + hns_rcb_start(queue, val); +} + +static int hns_ae_get_link_status(struct hnae_handle *handle) +{ + u32 link_status; + struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle); + + hns_mac_get_link_status(mac_cb, &link_status); + + return !!link_status; +} + +static int hns_ae_get_mac_info(struct hnae_handle *handle, + u8 *auto_neg, u16 *speed, u8 *duplex) +{ + struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle); + + return hns_mac_get_port_info(mac_cb, auto_neg, speed, duplex); +} + +static void hns_ae_adjust_link(struct hnae_handle *handle, int speed, + int duplex) +{ + struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle); + + hns_mac_adjust_link(mac_cb, speed, duplex); +} + +static void hns_ae_get_ring_bdnum_limit(struct hnae_queue *queue, + u32 *uplimit) +{ + *uplimit = HNS_RCB_RING_MAX_PENDING_BD; +} + +static void hns_ae_get_pauseparam(struct hnae_handle *handle, + u32 *auto_neg, u32 *rx_en, u32 *tx_en) +{ + assert(handle); + + hns_mac_get_autoneg(hns_get_mac_cb(handle), auto_neg); + + hns_mac_get_pauseparam(hns_get_mac_cb(handle), rx_en, tx_en); +} + +static int hns_ae_set_autoneg(struct hnae_handle *handle, u8 enable) +{ + assert(handle); + + return hns_mac_set_autoneg(hns_get_mac_cb(handle), enable); +} + +static int hns_ae_get_autoneg(struct hnae_handle *handle) +{ + u32 auto_neg; + + assert(handle); + + hns_mac_get_autoneg(hns_get_mac_cb(handle), &auto_neg); + + return auto_neg; +} + +static int hns_ae_set_pauseparam(struct hnae_handle *handle, + u32 autoneg, u32 rx_en, u32 tx_en) +{ + struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle); + int ret; + + ret = hns_mac_set_autoneg(mac_cb, autoneg); + if (ret) + return ret; + + return hns_mac_set_pauseparam(mac_cb, rx_en, tx_en); +} + +static void hns_ae_get_coalesce_usecs(struct hnae_handle *handle, + u32 *tx_usecs, u32 *rx_usecs) +{ + int port; + + port = hns_ae_map_eport_to_dport(handle->eport_id); + + *tx_usecs = hns_rcb_get_coalesce_usecs( + hns_ae_get_dsaf_dev(handle->dev), + hns_dsaf_get_comm_idx_by_port(port)); + *rx_usecs = hns_rcb_get_coalesce_usecs( + hns_ae_get_dsaf_dev(handle->dev), + hns_dsaf_get_comm_idx_by_port(port)); +} + +static void hns_ae_get_rx_max_coalesced_frames(struct hnae_handle *handle, + u32 *tx_frames, u32 *rx_frames) +{ + int port; + + assert(handle); + + port = hns_ae_map_eport_to_dport(handle->eport_id); + + *tx_frames = hns_rcb_get_coalesced_frames( + hns_ae_get_dsaf_dev(handle->dev), port); + *rx_frames = hns_rcb_get_coalesced_frames( + hns_ae_get_dsaf_dev(handle->dev), port); +} + +static void hns_ae_set_coalesce_usecs(struct hnae_handle *handle, + u32 timeout) +{ + int port; + + assert(handle); + + port = hns_ae_map_eport_to_dport(handle->eport_id); + + hns_rcb_set_coalesce_usecs(hns_ae_get_dsaf_dev(handle->dev), + port, timeout); +} + +static int hns_ae_set_coalesce_frames(struct hnae_handle *handle, + u32 coalesce_frames) +{ + int port; + int ret; + + assert(handle); + + port = hns_ae_map_eport_to_dport(handle->eport_id); + + ret = hns_rcb_set_coalesced_frames(hns_ae_get_dsaf_dev(handle->dev), + port, coalesce_frames); + return ret; +} + +void hns_ae_update_stats(struct hnae_handle *handle, + struct net_device_stats *net_stats) +{ + int port; + int idx; + struct dsaf_device *dsaf_dev; + struct hns_mac_cb *mac_cb; + struct hns_ppe_cb *ppe_cb; + struct hnae_queue *queue; + struct hnae_vf_cb *vf_cb = hns_ae_get_vf_cb(handle); + u64 tx_bytes = 0, rx_bytes = 0, tx_packets = 0, rx_packets = 0; + u64 rx_errors = 0, tx_errors = 0, tx_dropped = 0; + u64 rx_missed_errors = 0; + + dsaf_dev = hns_ae_get_dsaf_dev(handle->dev); + if (!dsaf_dev) + return; + port = vf_cb->port_index; + ppe_cb = hns_get_ppe_cb(handle); + mac_cb = hns_get_mac_cb(handle); + + for (idx = 0; idx < handle->q_num; idx++) { + queue = handle->qs[idx]; + hns_rcb_update_stats(queue); + + tx_bytes += queue->tx_ring.stats.tx_bytes; + tx_packets += queue->tx_ring.stats.tx_pkts; + rx_bytes += queue->rx_ring.stats.rx_bytes; + rx_packets += queue->rx_ring.stats.rx_pkts; + + rx_errors += queue->rx_ring.stats.err_pkt_len + + queue->rx_ring.stats.l2_err + + queue->rx_ring.stats.l3l4_csum_err; + } + + hns_ppe_update_stats(ppe_cb); + rx_missed_errors = ppe_cb->hw_stats.rx_drop_no_buf; + tx_errors += ppe_cb->hw_stats.tx_err_checksum + + ppe_cb->hw_stats.tx_err_fifo_empty; + + if (mac_cb->mac_type == HNAE_PORT_SERVICE) { + hns_dsaf_update_stats(dsaf_dev, port); + /* for port upline direction, i.e., rx. */ + rx_missed_errors += dsaf_dev->hw_stats[port].bp_drop; + rx_missed_errors += dsaf_dev->hw_stats[port].pad_drop; + rx_missed_errors += dsaf_dev->hw_stats[port].crc_false; + + /* for port downline direction, i.e., tx. */ + port = port + DSAF_PPE_INODE_BASE; + hns_dsaf_update_stats(dsaf_dev, port); + tx_dropped += dsaf_dev->hw_stats[port].bp_drop; + tx_dropped += dsaf_dev->hw_stats[port].pad_drop; + tx_dropped += dsaf_dev->hw_stats[port].crc_false; + tx_dropped += dsaf_dev->hw_stats[port].rslt_drop; + tx_dropped += dsaf_dev->hw_stats[port].vlan_drop; + tx_dropped += dsaf_dev->hw_stats[port].stp_drop; + } + + hns_mac_update_stats(mac_cb); + rx_errors += mac_cb->hw_stats.rx_fifo_overrun_err; + + tx_errors += mac_cb->hw_stats.tx_bad_pkts + + mac_cb->hw_stats.tx_fragment_err + + mac_cb->hw_stats.tx_jabber_err + + mac_cb->hw_stats.tx_underrun_err + + mac_cb->hw_stats.tx_crc_err; + + net_stats->tx_bytes = tx_bytes; + net_stats->tx_packets = tx_packets; + net_stats->rx_bytes = rx_bytes; + net_stats->rx_dropped = 0; + net_stats->rx_packets = rx_packets; + net_stats->rx_errors = rx_errors; + net_stats->tx_errors = tx_errors; + net_stats->tx_dropped = tx_dropped; + net_stats->rx_missed_errors = rx_missed_errors; + net_stats->rx_crc_errors = mac_cb->hw_stats.rx_fcs_err; + net_stats->rx_frame_errors = mac_cb->hw_stats.rx_align_err; + net_stats->rx_fifo_errors = mac_cb->hw_stats.rx_fifo_overrun_err; + net_stats->rx_length_errors = mac_cb->hw_stats.rx_len_err; + net_stats->multicast = mac_cb->hw_stats.rx_mc_pkts; +} + +void hns_ae_get_stats(struct hnae_handle *handle, u64 *data) +{ + int idx; + struct hns_mac_cb *mac_cb; + struct hns_ppe_cb *ppe_cb; + u64 *p = data; + struct hnae_vf_cb *vf_cb; + + if (!handle || !data) { + pr_err("hns_ae_get_stats NULL handle or data pointer!\n"); + return; + } + + vf_cb = hns_ae_get_vf_cb(handle); + mac_cb = hns_get_mac_cb(handle); + ppe_cb = hns_get_ppe_cb(handle); + + for (idx = 0; idx < handle->q_num; idx++) { + hns_rcb_get_stats(handle->qs[idx], p); + p += hns_rcb_get_ring_sset_count((int)ETH_SS_STATS); + } + + hns_ppe_get_stats(ppe_cb, p); + p += hns_ppe_get_sset_count((int)ETH_SS_STATS); + + hns_mac_get_stats(mac_cb, p); + p += hns_mac_get_sset_count(mac_cb, (int)ETH_SS_STATS); + + if (mac_cb->mac_type == HNAE_PORT_SERVICE) + hns_dsaf_get_stats(vf_cb->dsaf_dev, p, vf_cb->port_index); +} + +void hns_ae_get_strings(struct hnae_handle *handle, + u32 stringset, u8 *data) +{ + int port; + int idx; + struct hns_mac_cb *mac_cb; + struct hns_ppe_cb *ppe_cb; + u8 *p = data; + struct hnae_vf_cb *vf_cb; + + assert(handle); + + vf_cb = hns_ae_get_vf_cb(handle); + port = vf_cb->port_index; + mac_cb = hns_get_mac_cb(handle); + ppe_cb = hns_get_ppe_cb(handle); + + for (idx = 0; idx < handle->q_num; idx++) { + hns_rcb_get_strings(stringset, p, idx); + p += ETH_GSTRING_LEN * hns_rcb_get_ring_sset_count(stringset); + } + + hns_ppe_get_strings(ppe_cb, stringset, p); + p += ETH_GSTRING_LEN * hns_ppe_get_sset_count(stringset); + + hns_mac_get_strings(mac_cb, stringset, p); + p += ETH_GSTRING_LEN * hns_mac_get_sset_count(mac_cb, stringset); + + if (mac_cb->mac_type == HNAE_PORT_SERVICE) + hns_dsaf_get_strings(stringset, p, port); +} + +int hns_ae_get_sset_count(struct hnae_handle *handle, int stringset) +{ + u32 sset_count = 0; + struct hns_mac_cb *mac_cb; + + assert(handle); + + mac_cb = hns_get_mac_cb(handle); + + sset_count += hns_rcb_get_ring_sset_count(stringset) * handle->q_num; + sset_count += hns_ppe_get_sset_count(stringset); + sset_count += hns_mac_get_sset_count(mac_cb, stringset); + + if (mac_cb->mac_type == HNAE_PORT_SERVICE) + sset_count += hns_dsaf_get_sset_count(stringset); + + return sset_count; +} + +static int hns_ae_config_loopback(struct hnae_handle *handle, + enum hnae_loop loop, int en) +{ + int ret; + struct hnae_vf_cb *vf_cb = hns_ae_get_vf_cb(handle); + + switch (loop) { + case MAC_INTERNALLOOP_SERDES: + ret = hns_mac_config_sds_loopback(vf_cb->mac_cb, en); + break; + case MAC_INTERNALLOOP_MAC: + ret = hns_mac_config_mac_loopback(vf_cb->mac_cb, loop, en); + break; + default: + ret = -EINVAL; + } + return ret; +} + +void hns_ae_update_led_status(struct hnae_handle *handle) +{ + struct hns_mac_cb *mac_cb; + + assert(handle); + mac_cb = hns_get_mac_cb(handle); + if (!mac_cb->cpld_vaddr) + return; + hns_set_led_opt(mac_cb); +} + +int hns_ae_cpld_set_led_id(struct hnae_handle *handle, + enum hnae_led_state status) +{ + struct hns_mac_cb *mac_cb; + + assert(handle); + + mac_cb = hns_get_mac_cb(handle); + + return hns_cpld_led_set_id(mac_cb, status); +} + +void hns_ae_get_regs(struct hnae_handle *handle, void *data) +{ + u32 *p = data; + u32 rcb_com_idx; + int i; + struct hnae_vf_cb *vf_cb = hns_ae_get_vf_cb(handle); + struct hns_ppe_cb *ppe_cb = hns_get_ppe_cb(handle); + + hns_ppe_get_regs(ppe_cb, p); + p += hns_ppe_get_regs_count(); + + rcb_com_idx = hns_dsaf_get_comm_idx_by_port(vf_cb->port_index); + hns_rcb_get_common_regs(vf_cb->dsaf_dev->rcb_common[rcb_com_idx], p); + p += hns_rcb_get_common_regs_count(); + + for (i = 0; i < handle->q_num; i++) { + hns_rcb_get_ring_regs(handle->qs[i], p); + p += hns_rcb_get_ring_regs_count(); + } + + hns_mac_get_regs(vf_cb->mac_cb, p); + p += hns_mac_get_regs_count(vf_cb->mac_cb); + + if (vf_cb->mac_cb->mac_type == HNAE_PORT_SERVICE) + hns_dsaf_get_regs(vf_cb->dsaf_dev, vf_cb->port_index, p); +} + +int hns_ae_get_regs_len(struct hnae_handle *handle) +{ + u32 total_num; + struct hnae_vf_cb *vf_cb = hns_ae_get_vf_cb(handle); + + total_num = hns_ppe_get_regs_count(); + total_num += hns_rcb_get_common_regs_count(); + total_num += hns_rcb_get_ring_regs_count() * handle->q_num; + total_num += hns_mac_get_regs_count(vf_cb->mac_cb); + + if (vf_cb->mac_cb->mac_type == HNAE_PORT_SERVICE) + total_num += hns_dsaf_get_regs_count(); + + return total_num; +} + +static struct hnae_ae_ops hns_dsaf_ops = { + .get_handle = hns_ae_get_handle, + .put_handle = hns_ae_put_handle, + .init_queue = hns_ae_init_queue, + .fini_queue = hns_ae_fini_queue, + .start = hns_ae_start, + .stop = hns_ae_stop, + .reset = hns_ae_reset, + .toggle_ring_irq = hns_ae_toggle_ring_irq, + .toggle_queue_status = hns_ae_toggle_queue_status, + .get_status = hns_ae_get_link_status, + .get_info = hns_ae_get_mac_info, + .adjust_link = hns_ae_adjust_link, + .set_loopback = hns_ae_config_loopback, + .get_ring_bdnum_limit = hns_ae_get_ring_bdnum_limit, + .get_pauseparam = hns_ae_get_pauseparam, + .set_autoneg = hns_ae_set_autoneg, + .get_autoneg = hns_ae_get_autoneg, + .set_pauseparam = hns_ae_set_pauseparam, + .get_coalesce_usecs = hns_ae_get_coalesce_usecs, + .get_rx_max_coalesced_frames = hns_ae_get_rx_max_coalesced_frames, + .set_coalesce_usecs = hns_ae_set_coalesce_usecs, + .set_coalesce_frames = hns_ae_set_coalesce_frames, + .set_mac_addr = hns_ae_set_mac_address, + .set_mc_addr = hns_ae_set_multicast_one, + .set_mtu = hns_ae_set_mtu, + .update_stats = hns_ae_update_stats, + .get_stats = hns_ae_get_stats, + .get_strings = hns_ae_get_strings, + .get_sset_count = hns_ae_get_sset_count, + .update_led_status = hns_ae_update_led_status, + .set_led_id = hns_ae_cpld_set_led_id, + .get_regs = hns_ae_get_regs, + .get_regs_len = hns_ae_get_regs_len +}; + +int hns_dsaf_ae_init(struct dsaf_device *dsaf_dev) +{ + struct hnae_ae_dev *ae_dev = &dsaf_dev->ae_dev; + + ae_dev->ops = &hns_dsaf_ops; + ae_dev->dev = dsaf_dev->dev; + + return hnae_ae_register(ae_dev, THIS_MODULE); +} + +void hns_dsaf_ae_uninit(struct dsaf_device *dsaf_dev) +{ + hnae_ae_unregister(&dsaf_dev->ae_dev); +} diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c new file mode 100644 index 000000000000..b8517b00e706 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c @@ -0,0 +1,704 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * 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/delay.h> +#include <linux/of_mdio.h> +#include "hns_dsaf_main.h" +#include "hns_dsaf_mac.h" +#include "hns_dsaf_gmac.h" + +static const struct mac_stats_string g_gmac_stats_string[] = { + {"gmac_rx_octets_total_ok", MAC_STATS_FIELD_OFF(rx_good_bytes)}, + {"gmac_rx_octets_bad", MAC_STATS_FIELD_OFF(rx_bad_bytes)}, + {"gmac_rx_uc_pkts", MAC_STATS_FIELD_OFF(rx_uc_pkts)}, + {"gamc_rx_mc_pkts", MAC_STATS_FIELD_OFF(rx_mc_pkts)}, + {"gmac_rx_bc_pkts", MAC_STATS_FIELD_OFF(rx_bc_pkts)}, + {"gmac_rx_pkts_64octets", MAC_STATS_FIELD_OFF(rx_64bytes)}, + {"gmac_rx_pkts_65to127", MAC_STATS_FIELD_OFF(rx_65to127)}, + {"gmac_rx_pkts_128to255", MAC_STATS_FIELD_OFF(rx_128to255)}, + {"gmac_rx_pkts_256to511", MAC_STATS_FIELD_OFF(rx_256to511)}, + {"gmac_rx_pkts_512to1023", MAC_STATS_FIELD_OFF(rx_512to1023)}, + {"gmac_rx_pkts_1024to1518", MAC_STATS_FIELD_OFF(rx_1024to1518)}, + {"gmac_rx_pkts_1519tomax", MAC_STATS_FIELD_OFF(rx_1519tomax)}, + {"gmac_rx_fcs_errors", MAC_STATS_FIELD_OFF(rx_fcs_err)}, + {"gmac_rx_tagged", MAC_STATS_FIELD_OFF(rx_vlan_pkts)}, + {"gmac_rx_data_err", MAC_STATS_FIELD_OFF(rx_data_err)}, + {"gmac_rx_align_errors", MAC_STATS_FIELD_OFF(rx_align_err)}, + {"gmac_rx_long_errors", MAC_STATS_FIELD_OFF(rx_oversize)}, + {"gmac_rx_jabber_errors", MAC_STATS_FIELD_OFF(rx_jabber_err)}, + {"gmac_rx_pause_maccontrol", MAC_STATS_FIELD_OFF(rx_pfc_tc0)}, + {"gmac_rx_unknown_maccontrol", MAC_STATS_FIELD_OFF(rx_unknown_ctrl)}, + {"gmac_rx_very_long_err", MAC_STATS_FIELD_OFF(rx_long_err)}, + {"gmac_rx_runt_err", MAC_STATS_FIELD_OFF(rx_minto64)}, + {"gmac_rx_short_err", MAC_STATS_FIELD_OFF(rx_under_min)}, + {"gmac_rx_filt_pkt", MAC_STATS_FIELD_OFF(rx_filter_bytes)}, + {"gmac_rx_octets_total_filt", MAC_STATS_FIELD_OFF(rx_filter_pkts)}, + {"gmac_rx_overrun_cnt", MAC_STATS_FIELD_OFF(rx_fifo_overrun_err)}, + {"gmac_rx_length_err", MAC_STATS_FIELD_OFF(rx_len_err)}, + {"gmac_rx_fail_comma", MAC_STATS_FIELD_OFF(rx_comma_err)}, + + {"gmac_tx_octets_ok", MAC_STATS_FIELD_OFF(tx_good_bytes)}, + {"gmac_tx_octets_bad", MAC_STATS_FIELD_OFF(tx_bad_bytes)}, + {"gmac_tx_uc_pkts", MAC_STATS_FIELD_OFF(tx_uc_pkts)}, + {"gmac_tx_mc_pkts", MAC_STATS_FIELD_OFF(tx_mc_pkts)}, + {"gmac_tx_bc_pkts", MAC_STATS_FIELD_OFF(tx_bc_pkts)}, + {"gmac_tx_pkts_64octets", MAC_STATS_FIELD_OFF(tx_64bytes)}, + {"gmac_tx_pkts_65to127", MAC_STATS_FIELD_OFF(tx_65to127)}, + {"gmac_tx_pkts_128to255", MAC_STATS_FIELD_OFF(tx_128to255)}, + {"gmac_tx_pkts_256to511", MAC_STATS_FIELD_OFF(tx_256to511)}, + {"gmac_tx_pkts_512to1023", MAC_STATS_FIELD_OFF(tx_512to1023)}, + {"gmac_tx_pkts_1024to1518", MAC_STATS_FIELD_OFF(tx_1024to1518)}, + {"gmac_tx_pkts_1519tomax", MAC_STATS_FIELD_OFF(tx_1519tomax)}, + {"gmac_tx_excessive_length_drop", MAC_STATS_FIELD_OFF(tx_jabber_err)}, + {"gmac_tx_underrun", MAC_STATS_FIELD_OFF(tx_underrun_err)}, + {"gmac_tx_tagged", MAC_STATS_FIELD_OFF(tx_vlan)}, + {"gmac_tx_crc_error", MAC_STATS_FIELD_OFF(tx_crc_err)}, + {"gmac_tx_pause_frames", MAC_STATS_FIELD_OFF(tx_pfc_tc0)} +}; + +static void hns_gmac_enable(void *mac_drv, enum mac_commom_mode mode) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + /*enable GE rX/tX */ + if ((mode == MAC_COMM_MODE_TX) || (mode == MAC_COMM_MODE_RX_AND_TX)) + dsaf_set_dev_bit(drv, GMAC_PORT_EN_REG, GMAC_PORT_TX_EN_B, 1); + + if ((mode == MAC_COMM_MODE_RX) || (mode == MAC_COMM_MODE_RX_AND_TX)) + dsaf_set_dev_bit(drv, GMAC_PORT_EN_REG, GMAC_PORT_RX_EN_B, 1); +} + +static void hns_gmac_disable(void *mac_drv, enum mac_commom_mode mode) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + /*disable GE rX/tX */ + if ((mode == MAC_COMM_MODE_TX) || (mode == MAC_COMM_MODE_RX_AND_TX)) + dsaf_set_dev_bit(drv, GMAC_PORT_EN_REG, GMAC_PORT_TX_EN_B, 0); + + if ((mode == MAC_COMM_MODE_RX) || (mode == MAC_COMM_MODE_RX_AND_TX)) + dsaf_set_dev_bit(drv, GMAC_PORT_EN_REG, GMAC_PORT_RX_EN_B, 0); +} + +/** +*hns_gmac_get_en - get port enable +*@mac_drv:mac device +*@rx:rx enable +*@tx:tx enable +*/ +static void hns_gmac_get_en(void *mac_drv, u32 *rx, u32 *tx) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + u32 porten; + + porten = dsaf_read_dev(drv, GMAC_PORT_EN_REG); + *tx = dsaf_get_bit(porten, GMAC_PORT_TX_EN_B); + *rx = dsaf_get_bit(porten, GMAC_PORT_RX_EN_B); +} + +static void hns_gmac_free(void *mac_drv) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + struct dsaf_device *dsaf_dev + = (struct dsaf_device *)dev_get_drvdata(drv->dev); + + u32 mac_id = drv->mac_id; + + hns_dsaf_ge_srst_by_port(dsaf_dev, mac_id, 0); +} + +static void hns_gmac_set_tx_auto_pause_frames(void *mac_drv, u16 newval) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + dsaf_set_dev_field(drv, GMAC_FC_TX_TIMER_REG, GMAC_FC_TX_TIMER_M, + GMAC_FC_TX_TIMER_S, newval); +} + +static void hns_gmac_get_tx_auto_pause_frames(void *mac_drv, u16 *newval) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + *newval = dsaf_get_dev_field(drv, GMAC_FC_TX_TIMER_REG, + GMAC_FC_TX_TIMER_M, GMAC_FC_TX_TIMER_S); +} + +static void hns_gmac_set_rx_auto_pause_frames(void *mac_drv, u32 newval) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + dsaf_set_dev_bit(drv, GMAC_PAUSE_EN_REG, + GMAC_PAUSE_EN_RX_FDFC_B, !!newval); +} + +static void hns_gmac_config_max_frame_length(void *mac_drv, u16 newval) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + dsaf_set_dev_field(drv, GMAC_MAX_FRM_SIZE_REG, GMAC_MAX_FRM_SIZE_M, + GMAC_MAX_FRM_SIZE_S, newval); + + dsaf_set_dev_field(drv, GAMC_RX_MAX_FRAME, GMAC_MAX_FRM_SIZE_M, + GMAC_MAX_FRM_SIZE_S, newval); +} + +static void hns_gmac_config_an_mode(void *mac_drv, u8 newval) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + dsaf_set_dev_bit(drv, GMAC_TRANSMIT_CONTROL_REG, + GMAC_TX_AN_EN_B, !!newval); +} + +static void hns_gmac_tx_loop_pkt_dis(void *mac_drv) +{ + u32 tx_loop_pkt_pri; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + tx_loop_pkt_pri = dsaf_read_dev(drv, GMAC_TX_LOOP_PKT_PRI_REG); + dsaf_set_bit(tx_loop_pkt_pri, GMAC_TX_LOOP_PKT_EN_B, 1); + dsaf_set_bit(tx_loop_pkt_pri, GMAC_TX_LOOP_PKT_HIG_PRI_B, 0); + dsaf_write_dev(drv, GMAC_TX_LOOP_PKT_PRI_REG, tx_loop_pkt_pri); +} + +static void hns_gmac_set_duplex_type(void *mac_drv, u8 newval) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + dsaf_set_dev_bit(drv, GMAC_DUPLEX_TYPE_REG, + GMAC_DUPLEX_TYPE_B, !!newval); +} + +static void hns_gmac_get_duplex_type(void *mac_drv, + enum hns_gmac_duplex_mdoe *duplex_mode) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + *duplex_mode = (enum hns_gmac_duplex_mdoe)dsaf_get_dev_bit( + drv, GMAC_DUPLEX_TYPE_REG, GMAC_DUPLEX_TYPE_B); +} + +static void hns_gmac_get_port_mode(void *mac_drv, enum hns_port_mode *port_mode) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + *port_mode = (enum hns_port_mode)dsaf_get_dev_field( + drv, GMAC_PORT_MODE_REG, GMAC_PORT_MODE_M, GMAC_PORT_MODE_S); +} + +static void hns_gmac_port_mode_get(void *mac_drv, + struct hns_gmac_port_mode_cfg *port_mode) +{ + u32 tx_ctrl; + u32 recv_ctrl; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + port_mode->port_mode = (enum hns_port_mode)dsaf_get_dev_field( + drv, GMAC_PORT_MODE_REG, GMAC_PORT_MODE_M, GMAC_PORT_MODE_S); + + tx_ctrl = dsaf_read_dev(drv, GMAC_TRANSMIT_CONTROL_REG); + recv_ctrl = dsaf_read_dev(drv, GMAC_RECV_CONTROL_REG); + + port_mode->max_frm_size = + dsaf_get_dev_field(drv, GMAC_MAX_FRM_SIZE_REG, + GMAC_MAX_FRM_SIZE_M, GMAC_MAX_FRM_SIZE_S); + port_mode->short_runts_thr = + dsaf_get_dev_field(drv, GMAC_SHORT_RUNTS_THR_REG, + GMAC_SHORT_RUNTS_THR_M, + GMAC_SHORT_RUNTS_THR_S); + + port_mode->pad_enable = dsaf_get_bit(tx_ctrl, GMAC_TX_PAD_EN_B); + port_mode->crc_add = dsaf_get_bit(tx_ctrl, GMAC_TX_CRC_ADD_B); + port_mode->an_enable = dsaf_get_bit(tx_ctrl, GMAC_TX_AN_EN_B); + + port_mode->runt_pkt_en = + dsaf_get_bit(recv_ctrl, GMAC_RECV_CTRL_RUNT_PKT_EN_B); + port_mode->strip_pad_en = + dsaf_get_bit(recv_ctrl, GMAC_RECV_CTRL_STRIP_PAD_EN_B); +} + +static void hns_gmac_pause_frm_cfg(void *mac_drv, u32 rx_pause_en, + u32 tx_pause_en) +{ + u32 pause_en; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + pause_en = dsaf_read_dev(drv, GMAC_PAUSE_EN_REG); + dsaf_set_bit(pause_en, GMAC_PAUSE_EN_RX_FDFC_B, !!rx_pause_en); + dsaf_set_bit(pause_en, GMAC_PAUSE_EN_TX_FDFC_B, !!tx_pause_en); + dsaf_write_dev(drv, GMAC_PAUSE_EN_REG, pause_en); +} + +static void hns_gmac_get_pausefrm_cfg(void *mac_drv, u32 *rx_pause_en, + u32 *tx_pause_en) +{ + u32 pause_en; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + pause_en = dsaf_read_dev(drv, GMAC_PAUSE_EN_REG); + + *rx_pause_en = dsaf_get_bit(pause_en, GMAC_PAUSE_EN_RX_FDFC_B); + *tx_pause_en = dsaf_get_bit(pause_en, GMAC_PAUSE_EN_TX_FDFC_B); +} + +static int hns_gmac_adjust_link(void *mac_drv, enum mac_speed speed, + u32 full_duplex) +{ + u32 tx_ctrl; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + dsaf_set_dev_bit(drv, GMAC_DUPLEX_TYPE_REG, + GMAC_DUPLEX_TYPE_B, !!full_duplex); + + switch (speed) { + case MAC_SPEED_10: + dsaf_set_dev_field( + drv, GMAC_PORT_MODE_REG, + GMAC_PORT_MODE_M, GMAC_PORT_MODE_S, 0x6); + break; + case MAC_SPEED_100: + dsaf_set_dev_field( + drv, GMAC_PORT_MODE_REG, + GMAC_PORT_MODE_M, GMAC_PORT_MODE_S, 0x7); + break; + case MAC_SPEED_1000: + dsaf_set_dev_field( + drv, GMAC_PORT_MODE_REG, + GMAC_PORT_MODE_M, GMAC_PORT_MODE_S, 0x8); + break; + default: + dev_err(drv->dev, + "hns_gmac_adjust_link fail, speed%d mac%d\n", + speed, drv->mac_id); + return -EINVAL; + } + + tx_ctrl = dsaf_read_dev(drv, GMAC_TRANSMIT_CONTROL_REG); + dsaf_set_bit(tx_ctrl, GMAC_TX_PAD_EN_B, 1); + dsaf_set_bit(tx_ctrl, GMAC_TX_CRC_ADD_B, 1); + dsaf_write_dev(drv, GMAC_TRANSMIT_CONTROL_REG, tx_ctrl); + + dsaf_set_dev_bit(drv, GMAC_MODE_CHANGE_EN_REG, + GMAC_MODE_CHANGE_EB_B, 1); + + return 0; +} + +static void hns_gmac_init(void *mac_drv) +{ + u32 port; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + struct dsaf_device *dsaf_dev + = (struct dsaf_device *)dev_get_drvdata(drv->dev); + + port = drv->mac_id; + + hns_dsaf_ge_srst_by_port(dsaf_dev, port, 0); + mdelay(10); + hns_dsaf_ge_srst_by_port(dsaf_dev, port, 1); + mdelay(10); + hns_gmac_disable(mac_drv, MAC_COMM_MODE_RX_AND_TX); + hns_gmac_tx_loop_pkt_dis(mac_drv); +} + +void hns_gmac_update_stats(void *mac_drv) +{ + struct mac_hw_stats *hw_stats = NULL; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + hw_stats = &drv->mac_cb->hw_stats; + + /* RX */ + hw_stats->rx_good_bytes + += dsaf_read_dev(drv, GMAC_RX_OCTETS_TOTAL_OK_REG); + hw_stats->rx_bad_bytes + += dsaf_read_dev(drv, GMAC_RX_OCTETS_BAD_REG); + hw_stats->rx_uc_pkts += dsaf_read_dev(drv, GMAC_RX_UC_PKTS_REG); + hw_stats->rx_mc_pkts += dsaf_read_dev(drv, GMAC_RX_MC_PKTS_REG); + hw_stats->rx_bc_pkts += dsaf_read_dev(drv, GMAC_RX_BC_PKTS_REG); + hw_stats->rx_64bytes + += dsaf_read_dev(drv, GMAC_RX_PKTS_64OCTETS_REG); + hw_stats->rx_65to127 + += dsaf_read_dev(drv, GMAC_RX_PKTS_65TO127OCTETS_REG); + hw_stats->rx_128to255 + += dsaf_read_dev(drv, GMAC_RX_PKTS_128TO255OCTETS_REG); + hw_stats->rx_256to511 + += dsaf_read_dev(drv, GMAC_RX_PKTS_255TO511OCTETS_REG); + hw_stats->rx_512to1023 + += dsaf_read_dev(drv, GMAC_RX_PKTS_512TO1023OCTETS_REG); + hw_stats->rx_1024to1518 + += dsaf_read_dev(drv, GMAC_RX_PKTS_1024TO1518OCTETS_REG); + hw_stats->rx_1519tomax + += dsaf_read_dev(drv, GMAC_RX_PKTS_1519TOMAXOCTETS_REG); + hw_stats->rx_fcs_err += dsaf_read_dev(drv, GMAC_RX_FCS_ERRORS_REG); + hw_stats->rx_vlan_pkts += dsaf_read_dev(drv, GMAC_RX_TAGGED_REG); + hw_stats->rx_data_err += dsaf_read_dev(drv, GMAC_RX_DATA_ERR_REG); + hw_stats->rx_align_err + += dsaf_read_dev(drv, GMAC_RX_ALIGN_ERRORS_REG); + hw_stats->rx_oversize + += dsaf_read_dev(drv, GMAC_RX_LONG_ERRORS_REG); + hw_stats->rx_jabber_err + += dsaf_read_dev(drv, GMAC_RX_JABBER_ERRORS_REG); + hw_stats->rx_pfc_tc0 + += dsaf_read_dev(drv, GMAC_RX_PAUSE_MACCTRL_FRAM_REG); + hw_stats->rx_unknown_ctrl + += dsaf_read_dev(drv, GMAC_RX_UNKNOWN_MACCTRL_FRAM_REG); + hw_stats->rx_long_err + += dsaf_read_dev(drv, GMAC_RX_VERY_LONG_ERR_CNT_REG); + hw_stats->rx_minto64 + += dsaf_read_dev(drv, GMAC_RX_RUNT_ERR_CNT_REG); + hw_stats->rx_under_min + += dsaf_read_dev(drv, GMAC_RX_SHORT_ERR_CNT_REG); + hw_stats->rx_filter_pkts + += dsaf_read_dev(drv, GMAC_RX_FILT_PKT_CNT_REG); + hw_stats->rx_filter_bytes + += dsaf_read_dev(drv, GMAC_RX_OCTETS_TOTAL_FILT_REG); + hw_stats->rx_fifo_overrun_err + += dsaf_read_dev(drv, GMAC_RX_OVERRUN_CNT_REG); + hw_stats->rx_len_err + += dsaf_read_dev(drv, GMAC_RX_LENGTHFIELD_ERR_CNT_REG); + hw_stats->rx_comma_err + += dsaf_read_dev(drv, GMAC_RX_FAIL_COMMA_CNT_REG); + + /* TX */ + hw_stats->tx_good_bytes + += dsaf_read_dev(drv, GMAC_OCTETS_TRANSMITTED_OK_REG); + hw_stats->tx_bad_bytes + += dsaf_read_dev(drv, GMAC_OCTETS_TRANSMITTED_BAD_REG); + hw_stats->tx_uc_pkts += dsaf_read_dev(drv, GMAC_TX_UC_PKTS_REG); + hw_stats->tx_mc_pkts += dsaf_read_dev(drv, GMAC_TX_MC_PKTS_REG); + hw_stats->tx_bc_pkts += dsaf_read_dev(drv, GMAC_TX_BC_PKTS_REG); + hw_stats->tx_64bytes + += dsaf_read_dev(drv, GMAC_TX_PKTS_64OCTETS_REG); + hw_stats->tx_65to127 + += dsaf_read_dev(drv, GMAC_TX_PKTS_65TO127OCTETS_REG); + hw_stats->tx_128to255 + += dsaf_read_dev(drv, GMAC_TX_PKTS_128TO255OCTETS_REG); + hw_stats->tx_256to511 + += dsaf_read_dev(drv, GMAC_TX_PKTS_255TO511OCTETS_REG); + hw_stats->tx_512to1023 + += dsaf_read_dev(drv, GMAC_TX_PKTS_512TO1023OCTETS_REG); + hw_stats->tx_1024to1518 + += dsaf_read_dev(drv, GMAC_TX_PKTS_1024TO1518OCTETS_REG); + hw_stats->tx_1519tomax + += dsaf_read_dev(drv, GMAC_TX_PKTS_1519TOMAXOCTETS_REG); + hw_stats->tx_jabber_err + += dsaf_read_dev(drv, GMAC_TX_EXCESSIVE_LENGTH_DROP_REG); + hw_stats->tx_underrun_err + += dsaf_read_dev(drv, GMAC_TX_UNDERRUN_REG); + hw_stats->tx_vlan += dsaf_read_dev(drv, GMAC_TX_TAGGED_REG); + hw_stats->tx_crc_err += dsaf_read_dev(drv, GMAC_TX_CRC_ERROR_REG); + hw_stats->tx_pfc_tc0 + += dsaf_read_dev(drv, GMAC_TX_PAUSE_FRAMES_REG); +} + +static void hns_gmac_set_mac_addr(void *mac_drv, char *mac_addr) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + if (drv->mac_id >= DSAF_SERVICE_NW_NUM) { + u32 high_val = mac_addr[1] | (mac_addr[0] << 8); + + u32 low_val = mac_addr[5] | (mac_addr[4] << 8) + | (mac_addr[3] << 16) | (mac_addr[2] << 24); + dsaf_write_dev(drv, GMAC_STATION_ADDR_LOW_2_REG, low_val); + dsaf_write_dev(drv, GMAC_STATION_ADDR_HIGH_2_REG, high_val); + } +} + +static int hns_gmac_config_loopback(void *mac_drv, enum hnae_loop loop_mode, + u8 enable) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + switch (loop_mode) { + case MAC_INTERNALLOOP_MAC: + dsaf_set_dev_bit(drv, GMAC_LOOP_REG, GMAC_LP_REG_CF2MI_LP_EN_B, + !!enable); + break; + default: + dev_err(drv->dev, "loop_mode error\n"); + return -EINVAL; + } + + return 0; +} + +static void hns_gmac_config_pad_and_crc(void *mac_drv, u8 newval) +{ + u32 tx_ctrl; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + tx_ctrl = dsaf_read_dev(drv, GMAC_TRANSMIT_CONTROL_REG); + dsaf_set_bit(tx_ctrl, GMAC_TX_PAD_EN_B, !!newval); + dsaf_set_bit(tx_ctrl, GMAC_TX_CRC_ADD_B, !!newval); + dsaf_write_dev(drv, GMAC_TRANSMIT_CONTROL_REG, tx_ctrl); +} + +static void hns_gmac_get_id(void *mac_drv, u8 *mac_id) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + *mac_id = drv->mac_id; +} + +static void hns_gmac_get_info(void *mac_drv, struct mac_info *mac_info) +{ + enum hns_gmac_duplex_mdoe duplex; + enum hns_port_mode speed; + u32 rx_pause; + u32 tx_pause; + u32 rx; + u32 tx; + u16 fc_tx_timer; + struct hns_gmac_port_mode_cfg port_mode = { GMAC_10M_MII, 0 }; + + hns_gmac_port_mode_get(mac_drv, &port_mode); + mac_info->pad_and_crc_en = port_mode.crc_add && port_mode.pad_enable; + mac_info->auto_neg = port_mode.an_enable; + + hns_gmac_get_tx_auto_pause_frames(mac_drv, &fc_tx_timer); + mac_info->tx_pause_time = fc_tx_timer; + + hns_gmac_get_en(mac_drv, &rx, &tx); + mac_info->port_en = rx && tx; + + hns_gmac_get_duplex_type(mac_drv, &duplex); + mac_info->duplex = duplex; + + hns_gmac_get_port_mode(mac_drv, &speed); + switch (speed) { + case GMAC_10M_SGMII: + mac_info->speed = MAC_SPEED_10; + break; + case GMAC_100M_SGMII: + mac_info->speed = MAC_SPEED_100; + break; + case GMAC_1000M_SGMII: + mac_info->speed = MAC_SPEED_1000; + break; + default: + mac_info->speed = 0; + break; + } + + hns_gmac_get_pausefrm_cfg(mac_drv, &rx_pause, &tx_pause); + mac_info->rx_pause_en = rx_pause; + mac_info->tx_pause_en = tx_pause; +} + +static void hns_gmac_autoneg_stat(void *mac_drv, u32 *enable) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + *enable = dsaf_get_dev_bit(drv, GMAC_TRANSMIT_CONTROL_REG, + GMAC_TX_AN_EN_B); +} + +static void hns_gmac_get_link_status(void *mac_drv, u32 *link_stat) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + *link_stat = dsaf_get_dev_bit(drv, GMAC_AN_NEG_STATE_REG, + GMAC_AN_NEG_STAT_RX_SYNC_OK_B); +} + +static void hns_gmac_get_regs(void *mac_drv, void *data) +{ + u32 *regs = data; + int i; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + /* base config registers */ + regs[0] = dsaf_read_dev(drv, GMAC_DUPLEX_TYPE_REG); + regs[1] = dsaf_read_dev(drv, GMAC_FD_FC_TYPE_REG); + regs[2] = dsaf_read_dev(drv, GMAC_FC_TX_TIMER_REG); + regs[3] = dsaf_read_dev(drv, GMAC_FD_FC_ADDR_LOW_REG); + regs[4] = dsaf_read_dev(drv, GMAC_FD_FC_ADDR_HIGH_REG); + regs[5] = dsaf_read_dev(drv, GMAC_IPG_TX_TIMER_REG); + regs[6] = dsaf_read_dev(drv, GMAC_PAUSE_THR_REG); + regs[7] = dsaf_read_dev(drv, GMAC_MAX_FRM_SIZE_REG); + regs[8] = dsaf_read_dev(drv, GMAC_PORT_MODE_REG); + regs[9] = dsaf_read_dev(drv, GMAC_PORT_EN_REG); + regs[10] = dsaf_read_dev(drv, GMAC_PAUSE_EN_REG); + regs[11] = dsaf_read_dev(drv, GMAC_SHORT_RUNTS_THR_REG); + regs[12] = dsaf_read_dev(drv, GMAC_AN_NEG_STATE_REG); + regs[13] = dsaf_read_dev(drv, GMAC_TX_LOCAL_PAGE_REG); + regs[14] = dsaf_read_dev(drv, GMAC_TRANSMIT_CONTROL_REG); + regs[15] = dsaf_read_dev(drv, GMAC_REC_FILT_CONTROL_REG); + regs[16] = dsaf_read_dev(drv, GMAC_PTP_CONFIG_REG); + + /* rx static registers */ + regs[17] = dsaf_read_dev(drv, GMAC_RX_OCTETS_TOTAL_OK_REG); + regs[18] = dsaf_read_dev(drv, GMAC_RX_OCTETS_BAD_REG); + regs[19] = dsaf_read_dev(drv, GMAC_RX_UC_PKTS_REG); + regs[20] = dsaf_read_dev(drv, GMAC_RX_MC_PKTS_REG); + regs[21] = dsaf_read_dev(drv, GMAC_RX_BC_PKTS_REG); + regs[22] = dsaf_read_dev(drv, GMAC_RX_PKTS_64OCTETS_REG); + regs[23] = dsaf_read_dev(drv, GMAC_RX_PKTS_65TO127OCTETS_REG); + regs[24] = dsaf_read_dev(drv, GMAC_RX_PKTS_128TO255OCTETS_REG); + regs[25] = dsaf_read_dev(drv, GMAC_RX_PKTS_255TO511OCTETS_REG); + regs[26] = dsaf_read_dev(drv, GMAC_RX_PKTS_512TO1023OCTETS_REG); + regs[27] = dsaf_read_dev(drv, GMAC_RX_PKTS_1024TO1518OCTETS_REG); + regs[28] = dsaf_read_dev(drv, GMAC_RX_PKTS_1519TOMAXOCTETS_REG); + regs[29] = dsaf_read_dev(drv, GMAC_RX_FCS_ERRORS_REG); + regs[30] = dsaf_read_dev(drv, GMAC_RX_TAGGED_REG); + regs[31] = dsaf_read_dev(drv, GMAC_RX_DATA_ERR_REG); + regs[32] = dsaf_read_dev(drv, GMAC_RX_ALIGN_ERRORS_REG); + regs[33] = dsaf_read_dev(drv, GMAC_RX_LONG_ERRORS_REG); + regs[34] = dsaf_read_dev(drv, GMAC_RX_JABBER_ERRORS_REG); + regs[35] = dsaf_read_dev(drv, GMAC_RX_PAUSE_MACCTRL_FRAM_REG); + regs[36] = dsaf_read_dev(drv, GMAC_RX_UNKNOWN_MACCTRL_FRAM_REG); + regs[37] = dsaf_read_dev(drv, GMAC_RX_VERY_LONG_ERR_CNT_REG); + regs[38] = dsaf_read_dev(drv, GMAC_RX_RUNT_ERR_CNT_REG); + regs[39] = dsaf_read_dev(drv, GMAC_RX_SHORT_ERR_CNT_REG); + regs[40] = dsaf_read_dev(drv, GMAC_RX_FILT_PKT_CNT_REG); + regs[41] = dsaf_read_dev(drv, GMAC_RX_OCTETS_TOTAL_FILT_REG); + + /* tx static registers */ + regs[42] = dsaf_read_dev(drv, GMAC_OCTETS_TRANSMITTED_OK_REG); + regs[43] = dsaf_read_dev(drv, GMAC_OCTETS_TRANSMITTED_BAD_REG); + regs[44] = dsaf_read_dev(drv, GMAC_TX_UC_PKTS_REG); + regs[45] = dsaf_read_dev(drv, GMAC_TX_MC_PKTS_REG); + regs[46] = dsaf_read_dev(drv, GMAC_TX_BC_PKTS_REG); + regs[47] = dsaf_read_dev(drv, GMAC_TX_PKTS_64OCTETS_REG); + regs[48] = dsaf_read_dev(drv, GMAC_TX_PKTS_65TO127OCTETS_REG); + regs[49] = dsaf_read_dev(drv, GMAC_TX_PKTS_128TO255OCTETS_REG); + regs[50] = dsaf_read_dev(drv, GMAC_TX_PKTS_255TO511OCTETS_REG); + regs[51] = dsaf_read_dev(drv, GMAC_TX_PKTS_512TO1023OCTETS_REG); + regs[52] = dsaf_read_dev(drv, GMAC_TX_PKTS_1024TO1518OCTETS_REG); + regs[53] = dsaf_read_dev(drv, GMAC_TX_PKTS_1519TOMAXOCTETS_REG); + regs[54] = dsaf_read_dev(drv, GMAC_TX_EXCESSIVE_LENGTH_DROP_REG); + regs[55] = dsaf_read_dev(drv, GMAC_TX_UNDERRUN_REG); + regs[56] = dsaf_read_dev(drv, GMAC_TX_TAGGED_REG); + regs[57] = dsaf_read_dev(drv, GMAC_TX_CRC_ERROR_REG); + regs[58] = dsaf_read_dev(drv, GMAC_TX_PAUSE_FRAMES_REG); + + regs[59] = dsaf_read_dev(drv, GAMC_RX_MAX_FRAME); + regs[60] = dsaf_read_dev(drv, GMAC_LINE_LOOP_BACK_REG); + regs[61] = dsaf_read_dev(drv, GMAC_CF_CRC_STRIP_REG); + regs[62] = dsaf_read_dev(drv, GMAC_MODE_CHANGE_EN_REG); + regs[63] = dsaf_read_dev(drv, GMAC_SIXTEEN_BIT_CNTR_REG); + regs[64] = dsaf_read_dev(drv, GMAC_LD_LINK_COUNTER_REG); + regs[65] = dsaf_read_dev(drv, GMAC_LOOP_REG); + regs[66] = dsaf_read_dev(drv, GMAC_RECV_CONTROL_REG); + regs[67] = dsaf_read_dev(drv, GMAC_VLAN_CODE_REG); + regs[68] = dsaf_read_dev(drv, GMAC_RX_OVERRUN_CNT_REG); + regs[69] = dsaf_read_dev(drv, GMAC_RX_LENGTHFIELD_ERR_CNT_REG); + regs[70] = dsaf_read_dev(drv, GMAC_RX_FAIL_COMMA_CNT_REG); + + regs[71] = dsaf_read_dev(drv, GMAC_STATION_ADDR_LOW_0_REG); + regs[72] = dsaf_read_dev(drv, GMAC_STATION_ADDR_HIGH_0_REG); + regs[73] = dsaf_read_dev(drv, GMAC_STATION_ADDR_LOW_1_REG); + regs[74] = dsaf_read_dev(drv, GMAC_STATION_ADDR_HIGH_1_REG); + regs[75] = dsaf_read_dev(drv, GMAC_STATION_ADDR_LOW_2_REG); + regs[76] = dsaf_read_dev(drv, GMAC_STATION_ADDR_HIGH_2_REG); + regs[77] = dsaf_read_dev(drv, GMAC_STATION_ADDR_LOW_3_REG); + regs[78] = dsaf_read_dev(drv, GMAC_STATION_ADDR_HIGH_3_REG); + regs[79] = dsaf_read_dev(drv, GMAC_STATION_ADDR_LOW_4_REG); + regs[80] = dsaf_read_dev(drv, GMAC_STATION_ADDR_HIGH_4_REG); + regs[81] = dsaf_read_dev(drv, GMAC_STATION_ADDR_LOW_5_REG); + regs[82] = dsaf_read_dev(drv, GMAC_STATION_ADDR_HIGH_5_REG); + regs[83] = dsaf_read_dev(drv, GMAC_STATION_ADDR_LOW_MSK_0_REG); + regs[84] = dsaf_read_dev(drv, GMAC_STATION_ADDR_HIGH_MSK_0_REG); + regs[85] = dsaf_read_dev(drv, GMAC_STATION_ADDR_LOW_MSK_1_REG); + regs[86] = dsaf_read_dev(drv, GMAC_STATION_ADDR_HIGH_MSK_1_REG); + regs[87] = dsaf_read_dev(drv, GMAC_MAC_SKIP_LEN_REG); + regs[88] = dsaf_read_dev(drv, GMAC_TX_LOOP_PKT_PRI_REG); + + /* mark end of mac regs */ + for (i = 89; i < 96; i++) + regs[i] = 0xaaaaaaaa; +} + +static void hns_gmac_get_stats(void *mac_drv, u64 *data) +{ + u32 i; + u64 *buf = data; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + struct mac_hw_stats *hw_stats = NULL; + + hw_stats = &drv->mac_cb->hw_stats; + + for (i = 0; i < ARRAY_SIZE(g_gmac_stats_string); i++) { + buf[i] = DSAF_STATS_READ(hw_stats, + g_gmac_stats_string[i].offset); + } +} + +static void hns_gmac_get_strings(u32 stringset, u8 *data) +{ + char *buff = (char *)data; + u32 i; + + if (stringset != ETH_SS_STATS) + return; + + for (i = 0; i < ARRAY_SIZE(g_gmac_stats_string); i++) { + snprintf(buff, ETH_GSTRING_LEN, g_gmac_stats_string[i].desc); + buff = buff + ETH_GSTRING_LEN; + } +} + +static int hns_gmac_get_sset_count(int stringset) +{ + if (stringset == ETH_SS_STATS) + return ARRAY_SIZE(g_gmac_stats_string); + + return 0; +} + +static int hns_gmac_get_regs_count(void) +{ + return ETH_GMAC_DUMP_NUM; +} + +void *hns_gmac_config(struct hns_mac_cb *mac_cb, struct mac_params *mac_param) +{ + struct mac_driver *mac_drv; + + mac_drv = devm_kzalloc(mac_cb->dev, sizeof(*mac_drv), GFP_KERNEL); + if (!mac_drv) + return NULL; + + mac_drv->mac_init = hns_gmac_init; + mac_drv->mac_enable = hns_gmac_enable; + mac_drv->mac_disable = hns_gmac_disable; + mac_drv->mac_free = hns_gmac_free; + mac_drv->adjust_link = hns_gmac_adjust_link; + mac_drv->set_tx_auto_pause_frames = hns_gmac_set_tx_auto_pause_frames; + mac_drv->config_max_frame_length = hns_gmac_config_max_frame_length; + mac_drv->mac_pausefrm_cfg = hns_gmac_pause_frm_cfg; + + mac_drv->mac_id = mac_param->mac_id; + mac_drv->mac_mode = mac_param->mac_mode; + mac_drv->io_base = mac_param->vaddr; + mac_drv->dev = mac_param->dev; + mac_drv->mac_cb = mac_cb; + + mac_drv->set_mac_addr = hns_gmac_set_mac_addr; + mac_drv->set_an_mode = hns_gmac_config_an_mode; + mac_drv->config_loopback = hns_gmac_config_loopback; + mac_drv->config_pad_and_crc = hns_gmac_config_pad_and_crc; + mac_drv->config_half_duplex = hns_gmac_set_duplex_type; + mac_drv->set_rx_ignore_pause_frames = hns_gmac_set_rx_auto_pause_frames; + mac_drv->mac_get_id = hns_gmac_get_id; + mac_drv->get_info = hns_gmac_get_info; + mac_drv->autoneg_stat = hns_gmac_autoneg_stat; + mac_drv->get_pause_enable = hns_gmac_get_pausefrm_cfg; + mac_drv->get_link_status = hns_gmac_get_link_status; + mac_drv->get_regs = hns_gmac_get_regs; + mac_drv->get_regs_count = hns_gmac_get_regs_count; + mac_drv->get_ethtool_stats = hns_gmac_get_stats; + mac_drv->get_sset_count = hns_gmac_get_sset_count; + mac_drv->get_strings = hns_gmac_get_strings; + mac_drv->update_stats = hns_gmac_update_stats; + + return (void *)mac_drv; +} diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.h new file mode 100644 index 000000000000..44fe3010dc6d --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * 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 _HNS_GMAC_H +#define _HNS_GMAC_H + +#include "hns_dsaf_mac.h" + +enum hns_port_mode { + GMAC_10M_MII = 0, + GMAC_100M_MII, + GMAC_1000M_GMII, + GMAC_10M_RGMII, + GMAC_100M_RGMII, + GMAC_1000M_RGMII, + GMAC_10M_SGMII, + GMAC_100M_SGMII, + GMAC_1000M_SGMII, + GMAC_10000M_SGMII /* 10GE */ +}; + +enum hns_gmac_duplex_mdoe { + GMAC_HALF_DUPLEX_MODE = 0, + GMAC_FULL_DUPLEX_MODE +}; + +struct hns_gmac_port_mode_cfg { + enum hns_port_mode port_mode; + u32 max_frm_size; + u32 short_runts_thr; + u32 pad_enable; + u32 crc_add; + u32 an_enable; /*auto-nego enable */ + u32 runt_pkt_en; + u32 strip_pad_en; +}; + +#define ETH_GMAC_DUMP_NUM 96 +#endif /* __HNS_GMAC_H__ */ diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c new file mode 100644 index 000000000000..a8bd27b7f439 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c @@ -0,0 +1,900 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * 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/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/netdevice.h> +#include <linux/phy_fixed.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_address.h> + +#include "hns_dsaf_misc.h" +#include "hns_dsaf_main.h" +#include "hns_dsaf_rcb.h" + +#define MAC_EN_FLAG_V 0xada0328 + +static const u16 mac_phy_to_speed[] = { + [PHY_INTERFACE_MODE_MII] = MAC_SPEED_100, + [PHY_INTERFACE_MODE_GMII] = MAC_SPEED_1000, + [PHY_INTERFACE_MODE_SGMII] = MAC_SPEED_1000, + [PHY_INTERFACE_MODE_TBI] = MAC_SPEED_1000, + [PHY_INTERFACE_MODE_RMII] = MAC_SPEED_100, + [PHY_INTERFACE_MODE_RGMII] = MAC_SPEED_1000, + [PHY_INTERFACE_MODE_RGMII_ID] = MAC_SPEED_1000, + [PHY_INTERFACE_MODE_RGMII_RXID] = MAC_SPEED_1000, + [PHY_INTERFACE_MODE_RGMII_TXID] = MAC_SPEED_1000, + [PHY_INTERFACE_MODE_RTBI] = MAC_SPEED_1000, + [PHY_INTERFACE_MODE_XGMII] = MAC_SPEED_10000 +}; + +static const enum mac_mode g_mac_mode_100[] = { + [PHY_INTERFACE_MODE_MII] = MAC_MODE_MII_100, + [PHY_INTERFACE_MODE_RMII] = MAC_MODE_RMII_100 +}; + +static const enum mac_mode g_mac_mode_1000[] = { + [PHY_INTERFACE_MODE_GMII] = MAC_MODE_GMII_1000, + [PHY_INTERFACE_MODE_SGMII] = MAC_MODE_SGMII_1000, + [PHY_INTERFACE_MODE_TBI] = MAC_MODE_TBI_1000, + [PHY_INTERFACE_MODE_RGMII] = MAC_MODE_RGMII_1000, + [PHY_INTERFACE_MODE_RGMII_ID] = MAC_MODE_RGMII_1000, + [PHY_INTERFACE_MODE_RGMII_RXID] = MAC_MODE_RGMII_1000, + [PHY_INTERFACE_MODE_RGMII_TXID] = MAC_MODE_RGMII_1000, + [PHY_INTERFACE_MODE_RTBI] = MAC_MODE_RTBI_1000 +}; + +static enum mac_mode hns_mac_dev_to_enet_if(const struct hns_mac_cb *mac_cb) +{ + switch (mac_cb->max_speed) { + case MAC_SPEED_100: + return g_mac_mode_100[mac_cb->phy_if]; + case MAC_SPEED_1000: + return g_mac_mode_1000[mac_cb->phy_if]; + case MAC_SPEED_10000: + return MAC_MODE_XGMII_10000; + default: + return MAC_MODE_MII_100; + } +} + +static enum mac_mode hns_get_enet_interface(const struct hns_mac_cb *mac_cb) +{ + switch (mac_cb->max_speed) { + case MAC_SPEED_100: + return g_mac_mode_100[mac_cb->phy_if]; + case MAC_SPEED_1000: + return g_mac_mode_1000[mac_cb->phy_if]; + case MAC_SPEED_10000: + return MAC_MODE_XGMII_10000; + default: + return MAC_MODE_MII_100; + } +} + +int hns_mac_get_sfp_prsnt(struct hns_mac_cb *mac_cb, int *sfp_prsnt) +{ + if (!mac_cb->cpld_vaddr) + return -ENODEV; + + *sfp_prsnt = !dsaf_read_b((u64)mac_cb->cpld_vaddr + + MAC_SFP_PORT_OFFSET); + + return 0; +} + +void hns_mac_get_link_status(struct hns_mac_cb *mac_cb, u32 *link_status) +{ + struct mac_driver *mac_ctrl_drv; + int ret, sfp_prsnt; + + mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + if (mac_ctrl_drv->get_link_status) + mac_ctrl_drv->get_link_status(mac_ctrl_drv, link_status); + else + *link_status = 0; + + ret = hns_mac_get_sfp_prsnt(mac_cb, &sfp_prsnt); + if (!ret) + *link_status = *link_status && sfp_prsnt; + + mac_cb->link = *link_status; +} + +int hns_mac_get_port_info(struct hns_mac_cb *mac_cb, + u8 *auto_neg, u16 *speed, u8 *duplex) +{ + struct mac_driver *mac_ctrl_drv; + struct mac_info info; + + mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + if (!mac_ctrl_drv->get_info) + return -ENODEV; + + mac_ctrl_drv->get_info(mac_ctrl_drv, &info); + if (auto_neg) + *auto_neg = info.auto_neg; + if (speed) + *speed = info.speed; + if (duplex) + *duplex = info.duplex; + + return 0; +} + +void hns_mac_adjust_link(struct hns_mac_cb *mac_cb, int speed, int duplex) +{ + int ret; + struct mac_driver *mac_ctrl_drv; + + mac_ctrl_drv = (struct mac_driver *)(mac_cb->priv.mac); + + mac_cb->speed = speed; + mac_cb->half_duplex = !duplex; + mac_ctrl_drv->mac_mode = hns_mac_dev_to_enet_if(mac_cb); + + if (mac_ctrl_drv->adjust_link) { + ret = mac_ctrl_drv->adjust_link(mac_ctrl_drv, + (enum mac_speed)speed, duplex); + if (ret) { + dev_err(mac_cb->dev, + "adjust_link failed,%s mac%d ret = %#x!\n", + mac_cb->dsaf_dev->ae_dev.name, + mac_cb->mac_id, ret); + return; + } + } +} + +/** + *hns_mac_get_inner_port_num - get mac table inner port number + *@mac_cb: mac device + *@vmid: vm id + *@port_num:port number + * + */ +static int hns_mac_get_inner_port_num(struct hns_mac_cb *mac_cb, + u8 vmid, u8 *port_num) +{ + u8 tmp_port; + u32 comm_idx; + + if (mac_cb->dsaf_dev->dsaf_mode <= DSAF_MODE_ENABLE) { + if (mac_cb->mac_id != DSAF_MAX_PORT_NUM_PER_CHIP) { + dev_err(mac_cb->dev, + "input invalid,%s mac%d vmid%d !\n", + mac_cb->dsaf_dev->ae_dev.name, + mac_cb->mac_id, vmid); + return -EINVAL; + } + } else if (mac_cb->dsaf_dev->dsaf_mode < DSAF_MODE_MAX) { + if (mac_cb->mac_id <= DSAF_MAX_PORT_NUM_PER_CHIP) { + dev_err(mac_cb->dev, + "input invalid,%s mac%d vmid%d!\n", + mac_cb->dsaf_dev->ae_dev.name, + mac_cb->mac_id, vmid); + return -EINVAL; + } + } else { + dev_err(mac_cb->dev, "dsaf mode invalid,%s mac%d!\n", + mac_cb->dsaf_dev->ae_dev.name, mac_cb->mac_id); + return -EINVAL; + } + + comm_idx = hns_dsaf_get_comm_idx_by_port(mac_cb->mac_id); + + if (vmid >= mac_cb->dsaf_dev->rcb_common[comm_idx]->max_vfn) { + dev_err(mac_cb->dev, "input invalid,%s mac%d vmid%d !\n", + mac_cb->dsaf_dev->ae_dev.name, mac_cb->mac_id, vmid); + return -EINVAL; + } + + switch (mac_cb->dsaf_dev->dsaf_mode) { + case DSAF_MODE_ENABLE_FIX: + tmp_port = 0; + break; + case DSAF_MODE_DISABLE_FIX: + tmp_port = 0; + break; + case DSAF_MODE_ENABLE_0VM: + case DSAF_MODE_ENABLE_8VM: + case DSAF_MODE_ENABLE_16VM: + case DSAF_MODE_ENABLE_32VM: + case DSAF_MODE_ENABLE_128VM: + case DSAF_MODE_DISABLE_2PORT_8VM: + case DSAF_MODE_DISABLE_2PORT_16VM: + case DSAF_MODE_DISABLE_2PORT_64VM: + case DSAF_MODE_DISABLE_6PORT_0VM: + case DSAF_MODE_DISABLE_6PORT_2VM: + case DSAF_MODE_DISABLE_6PORT_4VM: + case DSAF_MODE_DISABLE_6PORT_16VM: + tmp_port = vmid; + break; + default: + dev_err(mac_cb->dev, "dsaf mode invalid,%s mac%d!\n", + mac_cb->dsaf_dev->ae_dev.name, mac_cb->mac_id); + return -EINVAL; + } + tmp_port += DSAF_BASE_INNER_PORT_NUM; + + *port_num = tmp_port; + + return 0; +} + +/** + *hns_mac_get_inner_port_num - change vf mac address + *@mac_cb: mac device + *@vmid: vmid + *@addr:mac address + */ +int hns_mac_change_vf_addr(struct hns_mac_cb *mac_cb, + u32 vmid, char *addr) +{ + int ret; + struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev; + struct dsaf_drv_mac_single_dest_entry mac_entry; + struct mac_entry_idx *old_entry; + + old_entry = &mac_cb->addr_entry_idx[vmid]; + if (dsaf_dev) { + memcpy(mac_entry.addr, addr, sizeof(mac_entry.addr)); + mac_entry.in_vlan_id = old_entry->vlan_id; + mac_entry.in_port_num = mac_cb->mac_id; + ret = hns_mac_get_inner_port_num(mac_cb, (u8)vmid, + &mac_entry.port_num); + if (ret) + return ret; + + if ((old_entry->valid != 0) && + (memcmp(old_entry->addr, + addr, sizeof(mac_entry.addr)) != 0)) { + ret = hns_dsaf_del_mac_entry(dsaf_dev, + old_entry->vlan_id, + mac_cb->mac_id, + old_entry->addr); + if (ret) + return ret; + } + + ret = hns_dsaf_set_mac_uc_entry(dsaf_dev, &mac_entry); + if (ret) + return ret; + } + + if ((mac_ctrl_drv->set_mac_addr) && (vmid == 0)) + mac_ctrl_drv->set_mac_addr(mac_cb->priv.mac, addr); + + memcpy(old_entry->addr, addr, sizeof(old_entry->addr)); + old_entry->valid = 1; + return 0; +} + +int hns_mac_set_multi(struct hns_mac_cb *mac_cb, + u32 port_num, char *addr, u8 en) +{ + int ret; + struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev; + struct dsaf_drv_mac_single_dest_entry mac_entry; + + if (dsaf_dev && addr) { + memcpy(mac_entry.addr, addr, sizeof(mac_entry.addr)); + mac_entry.in_vlan_id = 0;/*vlan_id;*/ + mac_entry.in_port_num = mac_cb->mac_id; + mac_entry.port_num = port_num; + + if (en == DISABLE) + ret = hns_dsaf_del_mac_mc_port(dsaf_dev, &mac_entry); + else + ret = hns_dsaf_add_mac_mc_port(dsaf_dev, &mac_entry); + if (ret) { + dev_err(dsaf_dev->dev, + "set mac mc port failed,%s mac%d ret = %#x!\n", + mac_cb->dsaf_dev->ae_dev.name, + mac_cb->mac_id, ret); + return ret; + } + } + + return 0; +} + +/** + *hns_mac_del_mac - delete mac address into dsaf table,can't delete the same + * address twice + *@net_dev: net device + *@vfn : vf lan + *@mac : mac address + *return status + */ +int hns_mac_del_mac(struct hns_mac_cb *mac_cb, u32 vfn, char *mac) +{ + struct mac_entry_idx *old_mac; + struct dsaf_device *dsaf_dev; + u32 ret; + + dsaf_dev = mac_cb->dsaf_dev; + + if (vfn < DSAF_MAX_VM_NUM) { + old_mac = &mac_cb->addr_entry_idx[vfn]; + } else { + dev_err(mac_cb->dev, + "vf queue is too large,%s mac%d queue = %#x!\n", + mac_cb->dsaf_dev->ae_dev.name, mac_cb->mac_id, vfn); + return -EINVAL; + } + + if (dsaf_dev) { + ret = hns_dsaf_del_mac_entry(dsaf_dev, old_mac->vlan_id, + mac_cb->mac_id, old_mac->addr); + if (ret) + return ret; + + if (memcmp(old_mac->addr, mac, sizeof(old_mac->addr)) == 0) + old_mac->valid = 0; + } + + return 0; +} + +static void hns_mac_param_get(struct mac_params *param, + struct hns_mac_cb *mac_cb) +{ + param->vaddr = (void *)mac_cb->vaddr; + param->mac_mode = hns_get_enet_interface(mac_cb); + memcpy(param->addr, mac_cb->addr_entry_idx[0].addr, + MAC_NUM_OCTETS_PER_ADDR); + param->mac_id = mac_cb->mac_id; + param->dev = mac_cb->dev; +} + +/** + *hns_mac_queue_config_bc_en - set broadcast rx&tx enable + *@mac_cb: mac device + *@queue: queue number + *@en:enable + *retuen 0 - success , negative --fail + */ +static int hns_mac_port_config_bc_en(struct hns_mac_cb *mac_cb, + u32 port_num, u16 vlan_id, u8 en) +{ + int ret; + struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev; + u8 addr[MAC_NUM_OCTETS_PER_ADDR] + = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + struct dsaf_drv_mac_single_dest_entry mac_entry; + + /* directy return ok in debug network mode */ + if (mac_cb->mac_type == HNAE_PORT_DEBUG) + return 0; + + if (dsaf_dev) { + memcpy(mac_entry.addr, addr, sizeof(mac_entry.addr)); + mac_entry.in_vlan_id = vlan_id; + mac_entry.in_port_num = mac_cb->mac_id; + mac_entry.port_num = port_num; + + if (en == DISABLE) + ret = hns_dsaf_del_mac_mc_port(dsaf_dev, &mac_entry); + else + ret = hns_dsaf_add_mac_mc_port(dsaf_dev, &mac_entry); + return ret; + } + + return 0; +} + +/** + *hns_mac_vm_config_bc_en - set broadcast rx&tx enable + *@mac_cb: mac device + *@vmid: vm id + *@en:enable + *retuen 0 - success , negative --fail + */ +int hns_mac_vm_config_bc_en(struct hns_mac_cb *mac_cb, u32 vmid, u8 en) +{ + int ret; + struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev; + u8 port_num; + u8 addr[MAC_NUM_OCTETS_PER_ADDR] + = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + struct mac_entry_idx *uc_mac_entry; + struct dsaf_drv_mac_single_dest_entry mac_entry; + + if (mac_cb->mac_type == HNAE_PORT_DEBUG) + return 0; + + uc_mac_entry = &mac_cb->addr_entry_idx[vmid]; + + if (dsaf_dev) { + memcpy(mac_entry.addr, addr, sizeof(mac_entry.addr)); + mac_entry.in_vlan_id = uc_mac_entry->vlan_id; + mac_entry.in_port_num = mac_cb->mac_id; + ret = hns_mac_get_inner_port_num(mac_cb, vmid, &port_num); + if (ret) + return ret; + mac_entry.port_num = port_num; + + if (en == DISABLE) + ret = hns_dsaf_del_mac_mc_port(dsaf_dev, &mac_entry); + else + ret = hns_dsaf_add_mac_mc_port(dsaf_dev, &mac_entry); + return ret; + } + + return 0; +} + +void hns_mac_reset(struct hns_mac_cb *mac_cb) +{ + struct mac_driver *drv; + + drv = hns_mac_get_drv(mac_cb); + + drv->mac_init(drv); + + if (drv->config_max_frame_length) + drv->config_max_frame_length(drv, mac_cb->max_frm); + + if (drv->set_tx_auto_pause_frames) + drv->set_tx_auto_pause_frames(drv, mac_cb->tx_pause_frm_time); + + if (drv->set_an_mode) + drv->set_an_mode(drv, 1); + + if (drv->mac_pausefrm_cfg) { + if (mac_cb->mac_type == HNAE_PORT_DEBUG) + drv->mac_pausefrm_cfg(drv, 0, 0); + else /* mac rx must disable, dsaf pfc close instead of it*/ + drv->mac_pausefrm_cfg(drv, 0, 1); + } +} + +int hns_mac_set_mtu(struct hns_mac_cb *mac_cb, u32 new_mtu) +{ + struct mac_driver *drv = hns_mac_get_drv(mac_cb); + u32 buf_size = mac_cb->dsaf_dev->buf_size; + u32 new_frm = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; + + if ((new_mtu < MAC_MIN_MTU) || (new_frm > MAC_MAX_MTU) || + (new_frm > HNS_RCB_RING_MAX_BD_PER_PKT * buf_size)) + return -EINVAL; + + if (!drv->config_max_frame_length) + return -ECHILD; + + /* adjust max frame to be at least the size of a standard frame */ + if (new_frm < (ETH_FRAME_LEN + ETH_FCS_LEN + VLAN_HLEN)) + new_frm = (ETH_FRAME_LEN + ETH_FCS_LEN + VLAN_HLEN); + + drv->config_max_frame_length(drv, new_frm); + + mac_cb->max_frm = new_frm; + + return 0; +} + +void hns_mac_start(struct hns_mac_cb *mac_cb) +{ + struct mac_driver *mac_drv = hns_mac_get_drv(mac_cb); + + /* for virt */ + if (mac_drv->mac_en_flg == MAC_EN_FLAG_V) { + /*plus 1 when the virtual mac has been enabled */ + mac_drv->virt_dev_num += 1; + return; + } + + if (mac_drv->mac_enable) { + mac_drv->mac_enable(mac_cb->priv.mac, MAC_COMM_MODE_RX_AND_TX); + mac_drv->mac_en_flg = MAC_EN_FLAG_V; + } +} + +void hns_mac_stop(struct hns_mac_cb *mac_cb) +{ + struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + /*modified for virtualization */ + if (mac_ctrl_drv->virt_dev_num > 0) { + mac_ctrl_drv->virt_dev_num -= 1; + if (mac_ctrl_drv->virt_dev_num > 0) + return; + } + + if (mac_ctrl_drv->mac_disable) + mac_ctrl_drv->mac_disable(mac_cb->priv.mac, + MAC_COMM_MODE_RX_AND_TX); + + mac_ctrl_drv->mac_en_flg = 0; + mac_cb->link = 0; + cpld_led_reset(mac_cb); +} + +/** + * hns_mac_get_autoneg - get auto autonegotiation + * @mac_cb: mac control block + * @enable: enable or not + * retuen 0 - success , negative --fail + */ +void hns_mac_get_autoneg(struct hns_mac_cb *mac_cb, u32 *auto_neg) +{ + struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + if (mac_ctrl_drv->autoneg_stat) + mac_ctrl_drv->autoneg_stat(mac_ctrl_drv, auto_neg); + else + *auto_neg = 0; +} + +/** + * hns_mac_get_pauseparam - set rx & tx pause parameter + * @mac_cb: mac control block + * @rx_en: rx enable status + * @tx_en: tx enable status + * retuen 0 - success , negative --fail + */ +void hns_mac_get_pauseparam(struct hns_mac_cb *mac_cb, u32 *rx_en, u32 *tx_en) +{ + struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + if (mac_ctrl_drv->get_pause_enable) { + mac_ctrl_drv->get_pause_enable(mac_ctrl_drv, rx_en, tx_en); + } else { + *rx_en = 0; + *tx_en = 0; + } + + /* Due to the chip defect, the service mac's rx pause CAN'T be enabled. + * We set the rx pause frm always be true (1), because DSAF deals with + * the rx pause frm instead of service mac. After all, we still support + * rx pause frm. + */ + if (mac_cb->mac_type == HNAE_PORT_SERVICE) + *rx_en = 1; +} + +/** + * hns_mac_set_autoneg - set auto autonegotiation + * @mac_cb: mac control block + * @enable: enable or not + * retuen 0 - success , negative --fail + */ +int hns_mac_set_autoneg(struct hns_mac_cb *mac_cb, u8 enable) +{ + struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + if (mac_cb->phy_if == PHY_INTERFACE_MODE_XGMII && enable) { + dev_err(mac_cb->dev, "enable autoneg is not allowed!"); + return -ENOTSUPP; + } + + if (mac_ctrl_drv->set_an_mode) + mac_ctrl_drv->set_an_mode(mac_ctrl_drv, enable); + + return 0; +} + +/** + * hns_mac_set_autoneg - set rx & tx pause parameter + * @mac_cb: mac control block + * @rx_en: rx enable or not + * @tx_en: tx enable or not + * return 0 - success , negative --fail + */ +int hns_mac_set_pauseparam(struct hns_mac_cb *mac_cb, u32 rx_en, u32 tx_en) +{ + struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + if (mac_cb->mac_type == HNAE_PORT_SERVICE) { + if (!rx_en) { + dev_err(mac_cb->dev, "disable rx_pause is not allowed!"); + return -EINVAL; + } + } else if (mac_cb->mac_type == HNAE_PORT_DEBUG) { + if (tx_en || rx_en) { + dev_err(mac_cb->dev, "enable tx_pause or enable rx_pause are not allowed!"); + return -EINVAL; + } + } else { + dev_err(mac_cb->dev, "Unsupport this operation!"); + return -EINVAL; + } + + if (mac_ctrl_drv->mac_pausefrm_cfg) + mac_ctrl_drv->mac_pausefrm_cfg(mac_ctrl_drv, rx_en, tx_en); + + return 0; +} + +/** + * hns_mac_init_ex - mac init + * @mac_cb: mac control block + * retuen 0 - success , negative --fail + */ +static int hns_mac_init_ex(struct hns_mac_cb *mac_cb) +{ + int ret; + struct mac_params param; + struct mac_driver *drv; + + hns_dsaf_fix_mac_mode(mac_cb); + + memset(¶m, 0, sizeof(struct mac_params)); + hns_mac_param_get(¶m, mac_cb); + + if (MAC_SPEED_FROM_MODE(param.mac_mode) < MAC_SPEED_10000) + drv = (struct mac_driver *)hns_gmac_config(mac_cb, ¶m); + else + drv = (struct mac_driver *)hns_xgmac_config(mac_cb, ¶m); + + if (!drv) + return -ENOMEM; + + mac_cb->priv.mac = (void *)drv; + hns_mac_reset(mac_cb); + + hns_mac_adjust_link(mac_cb, mac_cb->speed, !mac_cb->half_duplex); + + ret = hns_mac_port_config_bc_en(mac_cb, mac_cb->mac_id, 0, ENABLE); + if (ret) + goto free_mac_drv; + + return 0; + +free_mac_drv: + drv->mac_free(mac_cb->priv.mac); + mac_cb->priv.mac = NULL; + + return ret; +} + +/** + *mac_free_dev - get mac information from device node + *@mac_cb: mac device + *@np:device node + *@mac_mode_idx:mac mode index + */ +static void hns_mac_get_info(struct hns_mac_cb *mac_cb, + struct device_node *np, u32 mac_mode_idx) +{ + mac_cb->link = false; + mac_cb->half_duplex = false; + mac_cb->speed = mac_phy_to_speed[mac_cb->phy_if]; + mac_cb->max_speed = mac_cb->speed; + + if (mac_cb->phy_if == PHY_INTERFACE_MODE_SGMII) { + mac_cb->if_support = MAC_GMAC_SUPPORTED; + mac_cb->if_support |= SUPPORTED_1000baseT_Full; + } else if (mac_cb->phy_if == PHY_INTERFACE_MODE_XGMII) { + mac_cb->if_support = SUPPORTED_10000baseR_FEC; + mac_cb->if_support |= SUPPORTED_10000baseKR_Full; + } + + mac_cb->max_frm = MAC_DEFAULT_MTU; + mac_cb->tx_pause_frm_time = MAC_DEFAULT_PAUSE_TIME; + + /* Get the rest of the PHY information */ + mac_cb->phy_node = of_parse_phandle(np, "phy-handle", mac_cb->mac_id); + if (mac_cb->phy_node) + dev_dbg(mac_cb->dev, "mac%d phy_node: %s\n", + mac_cb->mac_id, mac_cb->phy_node->name); +} + +/** + * hns_mac_get_mode - get mac mode + * @phy_if: phy interface + * retuen 0 - gmac, 1 - xgmac , negative --fail + */ +static int hns_mac_get_mode(phy_interface_t phy_if) +{ + switch (phy_if) { + case PHY_INTERFACE_MODE_SGMII: + return MAC_GMAC_IDX; + case PHY_INTERFACE_MODE_XGMII: + return MAC_XGMAC_IDX; + default: + return -EINVAL; + } +} + +u8 __iomem *hns_mac_get_vaddr(struct dsaf_device *dsaf_dev, + struct hns_mac_cb *mac_cb, u32 mac_mode_idx) +{ + u8 __iomem *base = dsaf_dev->io_base; + int mac_id = mac_cb->mac_id; + + if (mac_cb->mac_type == HNAE_PORT_SERVICE) + return base + 0x40000 + mac_id * 0x4000 - + mac_mode_idx * 0x20000; + else + return mac_cb->serdes_vaddr + 0x1000 + + (mac_id - DSAF_SERVICE_PORT_NUM_PER_DSAF) * 0x100000; +} + +/** + * hns_mac_get_cfg - get mac cfg from dtb or acpi table + * @dsaf_dev: dsa fabric device struct pointer + * @mac_idx: mac index + * retuen 0 - success , negative --fail + */ +int hns_mac_get_cfg(struct dsaf_device *dsaf_dev, int mac_idx) +{ + int ret; + u32 mac_mode_idx; + struct hns_mac_cb *mac_cb = &dsaf_dev->mac_cb[mac_idx]; + + mac_cb->dsaf_dev = dsaf_dev; + mac_cb->dev = dsaf_dev->dev; + mac_cb->mac_id = mac_idx; + + mac_cb->sys_ctl_vaddr = dsaf_dev->sc_base; + mac_cb->serdes_vaddr = dsaf_dev->sds_base; + + if (dsaf_dev->cpld_base && + mac_idx < DSAF_SERVICE_PORT_NUM_PER_DSAF) + mac_cb->cpld_vaddr = dsaf_dev->cpld_base + + mac_cb->mac_id * CPLD_ADDR_PORT_OFFSET; + mac_cb->sfp_prsnt = 0; + mac_cb->txpkt_for_led = 0; + mac_cb->rxpkt_for_led = 0; + + if (mac_idx < DSAF_SERVICE_PORT_NUM_PER_DSAF) + mac_cb->mac_type = HNAE_PORT_SERVICE; + else + mac_cb->mac_type = HNAE_PORT_DEBUG; + + mac_cb->phy_if = hns_mac_get_phy_if(mac_cb); + + ret = hns_mac_get_mode(mac_cb->phy_if); + if (ret < 0) { + dev_err(dsaf_dev->dev, + "hns_mac_get_mode failed,mac%d ret = %#x!\n", + mac_cb->mac_id, ret); + return ret; + } + mac_mode_idx = (u32)ret; + + hns_mac_get_info(mac_cb, mac_cb->dev->of_node, mac_mode_idx); + + mac_cb->vaddr = hns_mac_get_vaddr(dsaf_dev, mac_cb, mac_mode_idx); + + return 0; +} + +/** + * hns_mac_init - init mac + * @dsaf_dev: dsa fabric device struct pointer + * retuen 0 - success , negative --fail + */ +int hns_mac_init(struct dsaf_device *dsaf_dev) +{ + int i; + int ret; + size_t size; + struct hns_mac_cb *mac_cb; + + size = sizeof(struct hns_mac_cb) * DSAF_MAX_PORT_NUM_PER_CHIP; + dsaf_dev->mac_cb = devm_kzalloc(dsaf_dev->dev, size, GFP_KERNEL); + if (!dsaf_dev->mac_cb) + return -ENOMEM; + + for (i = 0; i < DSAF_MAX_PORT_NUM_PER_CHIP; i++) { + ret = hns_mac_get_cfg(dsaf_dev, i); + if (ret) + goto free_mac_cb; + + mac_cb = &dsaf_dev->mac_cb[i]; + ret = hns_mac_init_ex(mac_cb); + if (ret) + goto free_mac_cb; + } + + return 0; + +free_mac_cb: + dsaf_dev->mac_cb = NULL; + + return ret; +} + +void hns_mac_uninit(struct dsaf_device *dsaf_dev) +{ + cpld_led_reset(dsaf_dev->mac_cb); + dsaf_dev->mac_cb = NULL; +} + +int hns_mac_config_mac_loopback(struct hns_mac_cb *mac_cb, + enum hnae_loop loop, int en) +{ + int ret; + struct mac_driver *drv = hns_mac_get_drv(mac_cb); + + if (drv->config_loopback) + ret = drv->config_loopback(drv, loop, en); + else + ret = -ENOTSUPP; + + return ret; +} + +void hns_mac_update_stats(struct hns_mac_cb *mac_cb) +{ + struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + mac_ctrl_drv->update_stats(mac_ctrl_drv); +} + +void hns_mac_get_stats(struct hns_mac_cb *mac_cb, u64 *data) +{ + struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + mac_ctrl_drv->get_ethtool_stats(mac_ctrl_drv, data); +} + +void hns_mac_get_strings(struct hns_mac_cb *mac_cb, + int stringset, u8 *data) +{ + struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + mac_ctrl_drv->get_strings(stringset, data); +} + +int hns_mac_get_sset_count(struct hns_mac_cb *mac_cb, int stringset) +{ + struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + return mac_ctrl_drv->get_sset_count(stringset); +} + +int hns_mac_get_regs_count(struct hns_mac_cb *mac_cb) +{ + struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + return mac_ctrl_drv->get_regs_count(); +} + +void hns_mac_get_regs(struct hns_mac_cb *mac_cb, void *data) +{ + struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + mac_ctrl_drv->get_regs(mac_ctrl_drv, data); +} + +void hns_set_led_opt(struct hns_mac_cb *mac_cb) +{ + int nic_data = 0; + int txpkts, rxpkts; + + txpkts = mac_cb->txpkt_for_led - mac_cb->hw_stats.tx_good_pkts; + rxpkts = mac_cb->rxpkt_for_led - mac_cb->hw_stats.rx_good_pkts; + if (txpkts || rxpkts) + nic_data = 1; + else + nic_data = 0; + mac_cb->txpkt_for_led = mac_cb->hw_stats.tx_good_pkts; + mac_cb->rxpkt_for_led = mac_cb->hw_stats.rx_good_pkts; + hns_cpld_set_led(mac_cb, (int)mac_cb->link, + mac_cb->speed, nic_data); +} + +int hns_cpld_led_set_id(struct hns_mac_cb *mac_cb, + enum hnae_led_state status) +{ + if (!mac_cb || !mac_cb->cpld_vaddr) + return 0; + + return cpld_set_led_id(mac_cb, status); +} diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h new file mode 100644 index 000000000000..7da95a7581f9 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h @@ -0,0 +1,456 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * 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 _HNS_DSAF_MAC_H +#define _HNS_DSAF_MAC_H + +#include <linux/phy.h> +#include <linux/kernel.h> +#include <linux/if_vlan.h> +#include "hns_dsaf_main.h" + +struct dsaf_device; + +#define MAC_GMAC_SUPPORTED \ + (SUPPORTED_10baseT_Half \ + | SUPPORTED_10baseT_Full \ + | SUPPORTED_100baseT_Half \ + | SUPPORTED_100baseT_Full \ + | SUPPORTED_Autoneg) + +#define MAC_DEFAULT_MTU (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN + ETH_DATA_LEN) +#define MAC_MAX_MTU 9600 +#define MAC_MIN_MTU 68 + +#define MAC_DEFAULT_PAUSE_TIME 0xff + +#define MAC_GMAC_IDX 0 +#define MAC_XGMAC_IDX 1 + +#define ETH_STATIC_REG 1 +#define ETH_DUMP_REG 5 +/* check mac addr broadcast */ +#define MAC_IS_BROADCAST(p) ((*(p) == 0xff) && (*((p) + 1) == 0xff) && \ + (*((p) + 2) == 0xff) && (*((p) + 3) == 0xff) && \ + (*((p) + 4) == 0xff) && (*((p) + 5) == 0xff)) + +/* check mac addr is 01-00-5e-xx-xx-xx*/ +#define MAC_IS_L3_MULTICAST(p) ((*((p) + 0) == 0x01) && \ + (*((p) + 1) == 0x00) && \ + (*((p) + 2) == 0x5e)) + +/*check the mac addr is 0 in all bit*/ +#define MAC_IS_ALL_ZEROS(p) ((*(p) == 0) && (*((p) + 1) == 0) && \ + (*((p) + 2) == 0) && (*((p) + 3) == 0) && \ + (*((p) + 4) == 0) && (*((p) + 5) == 0)) + +/*check mac addr multicast*/ +#define MAC_IS_MULTICAST(p) ((*((u8 *)((p) + 0)) & 0x01) ? (1) : (0)) + +/**< Number of octets (8-bit bytes) in an ethernet address */ +#define MAC_NUM_OCTETS_PER_ADDR 6 + +struct mac_priv { + void *mac; +}; + +/* net speed */ +enum mac_speed { + MAC_SPEED_10 = 10, /**< 10 Mbps */ + MAC_SPEED_100 = 100, /**< 100 Mbps */ + MAC_SPEED_1000 = 1000, /**< 1000 Mbps = 1 Gbps */ + MAC_SPEED_10000 = 10000 /**< 10000 Mbps = 10 Gbps */ +}; + +/*mac interface keyword */ +enum mac_intf { + MAC_IF_NONE = 0x00000000, /**< interface not invalid */ + MAC_IF_MII = 0x00010000, /**< MII interface */ + MAC_IF_RMII = 0x00020000, /**< RMII interface */ + MAC_IF_SMII = 0x00030000, /**< SMII interface */ + MAC_IF_GMII = 0x00040000, /**< GMII interface */ + MAC_IF_RGMII = 0x00050000, /**< RGMII interface */ + MAC_IF_TBI = 0x00060000, /**< TBI interface */ + MAC_IF_RTBI = 0x00070000, /**< RTBI interface */ + MAC_IF_SGMII = 0x00080000, /**< SGMII interface */ + MAC_IF_XGMII = 0x00090000, /**< XGMII interface */ + MAC_IF_QSGMII = 0x000a0000 /**< QSGMII interface */ +}; + +/*mac mode */ +enum mac_mode { + /**< Invalid Ethernet mode */ + MAC_MODE_INVALID = 0, + /**< 10 Mbps MII */ + MAC_MODE_MII_10 = (MAC_IF_MII | MAC_SPEED_10), + /**< 100 Mbps MII */ + MAC_MODE_MII_100 = (MAC_IF_MII | MAC_SPEED_100), + /**< 10 Mbps RMII */ + MAC_MODE_RMII_10 = (MAC_IF_RMII | MAC_SPEED_10), + /**< 100 Mbps RMII */ + MAC_MODE_RMII_100 = (MAC_IF_RMII | MAC_SPEED_100), + /**< 10 Mbps SMII */ + MAC_MODE_SMII_10 = (MAC_IF_SMII | MAC_SPEED_10), + /**< 100 Mbps SMII */ + MAC_MODE_SMII_100 = (MAC_IF_SMII | MAC_SPEED_100), + /**< 1000 Mbps GMII */ + MAC_MODE_GMII_1000 = (MAC_IF_GMII | MAC_SPEED_1000), + /**< 10 Mbps RGMII */ + MAC_MODE_RGMII_10 = (MAC_IF_RGMII | MAC_SPEED_10), + /**< 100 Mbps RGMII */ + MAC_MODE_RGMII_100 = (MAC_IF_RGMII | MAC_SPEED_100), + /**< 1000 Mbps RGMII */ + MAC_MODE_RGMII_1000 = (MAC_IF_RGMII | MAC_SPEED_1000), + /**< 1000 Mbps TBI */ + MAC_MODE_TBI_1000 = (MAC_IF_TBI | MAC_SPEED_1000), + /**< 1000 Mbps RTBI */ + MAC_MODE_RTBI_1000 = (MAC_IF_RTBI | MAC_SPEED_1000), + /**< 10 Mbps SGMII */ + MAC_MODE_SGMII_10 = (MAC_IF_SGMII | MAC_SPEED_10), + /**< 100 Mbps SGMII */ + MAC_MODE_SGMII_100 = (MAC_IF_SGMII | MAC_SPEED_100), + /**< 1000 Mbps SGMII */ + MAC_MODE_SGMII_1000 = (MAC_IF_SGMII | MAC_SPEED_1000), + /**< 10000 Mbps XGMII */ + MAC_MODE_XGMII_10000 = (MAC_IF_XGMII | MAC_SPEED_10000), + /**< 1000 Mbps QSGMII */ + MAC_MODE_QSGMII_1000 = (MAC_IF_QSGMII | MAC_SPEED_1000) +}; + +/*mac communicate mode*/ +enum mac_commom_mode { + MAC_COMM_MODE_NONE = 0, /**< No transmit/receive communication */ + MAC_COMM_MODE_RX = 1, /**< Only receive communication */ + MAC_COMM_MODE_TX = 2, /**< Only transmit communication */ + MAC_COMM_MODE_RX_AND_TX = 3 /**< Both tx and rx communication */ +}; + +/*mac statistics */ +struct mac_statistics { + u64 stat_pkts64; /* r-10G tr-DT 64 byte frame counter */ + u64 stat_pkts65to127; /* r-10G 65 to 127 byte frame counter */ + u64 stat_pkts128to255; /* r-10G 128 to 255 byte frame counter */ + u64 stat_pkts256to511; /*r-10G 256 to 511 byte frame counter */ + u64 stat_pkts512to1023;/* r-10G 512 to 1023 byte frame counter */ + u64 stat_pkts1024to1518; /* r-10G 1024 to 1518 byte frame counter */ + u64 stat_pkts1519to1522; /* r-10G 1519 to 1522 byte good frame count*/ + /* Total number of packets that were less than 64 octets */ + /* long with a wrong CRC.*/ + u64 stat_fragments; + /* Total number of packets longer than valid maximum length octets */ + u64 stat_jabbers; + /* number of dropped packets due to internal errors of */ + /* the MAC Client. */ + u64 stat_drop_events; + /* Incremented when frames of correct length but with */ + /* CRC error are received.*/ + u64 stat_crc_align_errors; + /* Total number of packets that were less than 64 octets */ + /* long with a good CRC.*/ + u64 stat_undersize_pkts; + u64 stat_oversize_pkts; /**< T,B.D*/ + + u64 stat_rx_pause; /**< Pause MAC Control received */ + u64 stat_tx_pause; /**< Pause MAC Control sent */ + + u64 in_octets; /**< Total number of byte received. */ + u64 in_pkts; /* Total number of packets received.*/ + u64 in_mcast_pkts; /* Total number of multicast frame received */ + u64 in_bcast_pkts; /* Total number of broadcast frame received */ + /* Frames received, but discarded due to */ + /* problems within the MAC RX. */ + u64 in_discards; + u64 in_errors; /* Number of frames received with error: */ + /* - FIFO Overflow Error */ + /* - CRC Error */ + /* - Frame Too Long Error */ + /* - Alignment Error */ + u64 out_octets; /*Total number of byte sent. */ + u64 out_pkts; /**< Total number of packets sent .*/ + u64 out_mcast_pkts; /* Total number of multicast frame sent */ + u64 out_bcast_pkts; /* Total number of multicast frame sent */ + /* Frames received, but discarded due to problems within */ + /* the MAC TX N/A!.*/ + u64 out_discards; + u64 out_errors; /*Number of frames transmitted with error: */ + /* - FIFO Overflow Error */ + /* - FIFO Underflow Error */ + /* - Other */ +}; + +/*mac para struct ,mac get param from nic or dsaf when initialize*/ +struct mac_params { + char addr[MAC_NUM_OCTETS_PER_ADDR]; + void *vaddr; /*virtual address*/ + struct device *dev; + u8 mac_id; + /**< Ethernet operation mode (MAC-PHY interface and speed) */ + enum mac_mode mac_mode; +}; + +struct mac_info { + u16 speed;/* The forced speed (lower bits) in */ + /* *mbps. Please use */ + /* * ethtool_cmd_speed()/_set() to */ + /* * access it */ + u8 duplex; /* Duplex, half or full */ + u8 auto_neg; /* Enable or disable autonegotiation */ + enum hnae_loop loop_mode; + u8 tx_pause_en; + u8 tx_pause_time; + u8 rx_pause_en; + u8 pad_and_crc_en; + u8 promiscuous_en; + u8 port_en; /*port enable*/ +}; + +struct mac_entry_idx { + u8 addr[MAC_NUM_OCTETS_PER_ADDR]; + u16 vlan_id:12; + u16 valid:1; + u16 qos:3; +}; + +struct mac_hw_stats { + u64 rx_good_pkts; /* only for xgmac */ + u64 rx_good_bytes; + u64 rx_total_pkts; /* only for xgmac */ + u64 rx_total_bytes; /* only for xgmac */ + u64 rx_bad_bytes; /* only for gmac */ + u64 rx_uc_pkts; + u64 rx_mc_pkts; + u64 rx_bc_pkts; + u64 rx_fragment_err; /* only for xgmac */ + u64 rx_undersize; /* only for xgmac */ + u64 rx_under_min; + u64 rx_minto64; /* only for gmac */ + u64 rx_64bytes; + u64 rx_65to127; + u64 rx_128to255; + u64 rx_256to511; + u64 rx_512to1023; + u64 rx_1024to1518; + u64 rx_1519tomax; + u64 rx_1519tomax_good; /* only for xgmac */ + u64 rx_oversize; + u64 rx_jabber_err; + u64 rx_fcs_err; + u64 rx_vlan_pkts; /* only for gmac */ + u64 rx_data_err; /* only for gmac */ + u64 rx_align_err; /* only for gmac */ + u64 rx_long_err; /* only for gmac */ + u64 rx_pfc_tc0; + u64 rx_pfc_tc1; /* only for xgmac */ + u64 rx_pfc_tc2; /* only for xgmac */ + u64 rx_pfc_tc3; /* only for xgmac */ + u64 rx_pfc_tc4; /* only for xgmac */ + u64 rx_pfc_tc5; /* only for xgmac */ + u64 rx_pfc_tc6; /* only for xgmac */ + u64 rx_pfc_tc7; /* only for xgmac */ + u64 rx_unknown_ctrl; + u64 rx_filter_pkts; /* only for gmac */ + u64 rx_filter_bytes; /* only for gmac */ + u64 rx_fifo_overrun_err;/* only for gmac */ + u64 rx_len_err; /* only for gmac */ + u64 rx_comma_err; /* only for gmac */ + u64 rx_symbol_err; /* only for xgmac */ + u64 tx_good_to_sw; /* only for xgmac */ + u64 tx_bad_to_sw; /* only for xgmac */ + u64 rx_1731_pkts; /* only for xgmac */ + + u64 tx_good_bytes; + u64 tx_good_pkts; /* only for xgmac */ + u64 tx_total_bytes; /* only for xgmac */ + u64 tx_total_pkts; /* only for xgmac */ + u64 tx_bad_bytes; /* only for gmac */ + u64 tx_bad_pkts; /* only for xgmac */ + u64 tx_uc_pkts; + u64 tx_mc_pkts; + u64 tx_bc_pkts; + u64 tx_undersize; /* only for xgmac */ + u64 tx_fragment_err; /* only for xgmac */ + u64 tx_under_min_pkts; /* only for gmac */ + u64 tx_64bytes; + u64 tx_65to127; + u64 tx_128to255; + u64 tx_256to511; + u64 tx_512to1023; + u64 tx_1024to1518; + u64 tx_1519tomax; + u64 tx_1519tomax_good; /* only for xgmac */ + u64 tx_oversize; /* only for xgmac */ + u64 tx_jabber_err; + u64 tx_underrun_err; /* only for gmac */ + u64 tx_vlan; /* only for gmac */ + u64 tx_crc_err; /* only for gmac */ + u64 tx_pfc_tc0; + u64 tx_pfc_tc1; /* only for xgmac */ + u64 tx_pfc_tc2; /* only for xgmac */ + u64 tx_pfc_tc3; /* only for xgmac */ + u64 tx_pfc_tc4; /* only for xgmac */ + u64 tx_pfc_tc5; /* only for xgmac */ + u64 tx_pfc_tc6; /* only for xgmac */ + u64 tx_pfc_tc7; /* only for xgmac */ + u64 tx_ctrl; /* only for xgmac */ + u64 tx_1731_pkts; /* only for xgmac */ + u64 tx_1588_pkts; /* only for xgmac */ + u64 rx_good_from_sw; /* only for xgmac */ + u64 rx_bad_from_sw; /* only for xgmac */ +}; + +struct hns_mac_cb { + struct device *dev; + struct dsaf_device *dsaf_dev; + struct mac_priv priv; + u8 __iomem *vaddr; + u8 __iomem *cpld_vaddr; + u8 __iomem *sys_ctl_vaddr; + u8 __iomem *serdes_vaddr; + struct mac_entry_idx addr_entry_idx[DSAF_MAX_VM_NUM]; + u8 sfp_prsnt; + u8 cpld_led_value; + u8 mac_id; + + u8 link; + u8 half_duplex; + u16 speed; + u16 max_speed; + u16 max_frm; + u16 tx_pause_frm_time; + u32 if_support; + u64 txpkt_for_led; + u64 rxpkt_for_led; + enum hnae_port_type mac_type; + phy_interface_t phy_if; + enum hnae_loop loop_mode; + + struct device_node *phy_node; + + struct mac_hw_stats hw_stats; +}; + +struct mac_driver { + /*init Mac when init nic or dsaf*/ + void (*mac_init)(void *mac_drv); + /*remove mac when remove nic or dsaf*/ + void (*mac_free)(void *mac_drv); + /*enable mac when enable nic or dsaf*/ + void (*mac_enable)(void *mac_drv, enum mac_commom_mode mode); + /*disable mac when disable nic or dsaf*/ + void (*mac_disable)(void *mac_drv, enum mac_commom_mode mode); + /* config mac address*/ + void (*set_mac_addr)(void *mac_drv, char *mac_addr); + /*adjust mac mode of port,include speed and duplex*/ + int (*adjust_link)(void *mac_drv, enum mac_speed speed, + u32 full_duplex); + /* config autoegotaite mode of port*/ + void (*set_an_mode)(void *mac_drv, u8 enable); + /* config loopbank mode */ + int (*config_loopback)(void *mac_drv, enum hnae_loop loop_mode, + u8 enable); + /* config mtu*/ + void (*config_max_frame_length)(void *mac_drv, u16 newval); + /*config PAD and CRC enable */ + void (*config_pad_and_crc)(void *mac_drv, u8 newval); + /* config duplex mode*/ + void (*config_half_duplex)(void *mac_drv, u8 newval); + /*config tx pause time,if pause_time is zero,disable tx pause enable*/ + void (*set_tx_auto_pause_frames)(void *mac_drv, u16 pause_time); + /*config rx pause enable*/ + void (*set_rx_ignore_pause_frames)(void *mac_drv, u32 enable); + /* config rx mode for promiscuous*/ + int (*set_promiscuous)(void *mac_drv, u8 enable); + /* get mac id */ + void (*mac_get_id)(void *mac_drv, u8 *mac_id); + void (*mac_pausefrm_cfg)(void *mac_drv, u32 rx_en, u32 tx_en); + + void (*autoneg_stat)(void *mac_drv, u32 *enable); + int (*set_pause_enable)(void *mac_drv, u32 rx_en, u32 tx_en); + void (*get_pause_enable)(void *mac_drv, u32 *rx_en, u32 *tx_en); + void (*get_link_status)(void *mac_drv, u32 *link_stat); + /* get the imporant regs*/ + void (*get_regs)(void *mac_drv, void *data); + int (*get_regs_count)(void); + /* get strings name for ethtool statistic */ + void (*get_strings)(u32 stringset, u8 *data); + /* get the number of strings*/ + int (*get_sset_count)(int stringset); + + /* get the statistic by ethtools*/ + void (*get_ethtool_stats)(void *mac_drv, u64 *data); + + /* get mac information */ + void (*get_info)(void *mac_drv, struct mac_info *mac_info); + + void (*update_stats)(void *mac_drv); + + enum mac_mode mac_mode; + u8 mac_id; + struct hns_mac_cb *mac_cb; + void __iomem *io_base; + unsigned int mac_en_flg;/*you'd better don't enable mac twice*/ + unsigned int virt_dev_num; + struct device *dev; +}; + +struct mac_stats_string { + char desc[64]; + unsigned long offset; +}; + +#define MAC_MAKE_MODE(interface, speed) (enum mac_mode)((interface) | (speed)) +#define MAC_INTERFACE_FROM_MODE(mode) (enum mac_intf)((mode) & 0xFFFF0000) +#define MAC_SPEED_FROM_MODE(mode) (enum mac_speed)((mode) & 0x0000FFFF) +#define MAC_STATS_FIELD_OFF(field) (offsetof(struct mac_hw_stats, field)) + +static inline struct mac_driver *hns_mac_get_drv( + const struct hns_mac_cb *mac_cb) +{ + return (struct mac_driver *)(mac_cb->priv.mac); +} + +void *hns_gmac_config(struct hns_mac_cb *mac_cb, + struct mac_params *mac_param); +void *hns_xgmac_config(struct hns_mac_cb *mac_cb, + struct mac_params *mac_param); + +int hns_mac_init(struct dsaf_device *dsaf_dev); +void mac_adjust_link(struct net_device *net_dev); +void hns_mac_get_link_status(struct hns_mac_cb *mac_cb, u32 *link_status); +int hns_mac_change_vf_addr(struct hns_mac_cb *mac_cb, u32 vmid, char *addr); +int hns_mac_set_multi(struct hns_mac_cb *mac_cb, + u32 port_num, char *addr, u8 en); +int hns_mac_vm_config_bc_en(struct hns_mac_cb *mac_cb, u32 vm, u8 en); +void hns_mac_start(struct hns_mac_cb *mac_cb); +void hns_mac_stop(struct hns_mac_cb *mac_cb); +int hns_mac_del_mac(struct hns_mac_cb *mac_cb, u32 vfn, char *mac); +void hns_mac_uninit(struct dsaf_device *dsaf_dev); +void hns_mac_adjust_link(struct hns_mac_cb *mac_cb, int speed, int duplex); +void hns_mac_reset(struct hns_mac_cb *mac_cb); +void hns_mac_get_autoneg(struct hns_mac_cb *mac_cb, u32 *auto_neg); +void hns_mac_get_pauseparam(struct hns_mac_cb *mac_cb, u32 *rx_en, u32 *tx_en); +int hns_mac_set_autoneg(struct hns_mac_cb *mac_cb, u8 enable); +int hns_mac_set_pauseparam(struct hns_mac_cb *mac_cb, u32 rx_en, u32 tx_en); +int hns_mac_set_mtu(struct hns_mac_cb *mac_cb, u32 new_mtu); +int hns_mac_get_port_info(struct hns_mac_cb *mac_cb, + u8 *auto_neg, u16 *speed, u8 *duplex); +phy_interface_t hns_mac_get_phy_if(struct hns_mac_cb *mac_cb); +int hns_mac_config_sds_loopback(struct hns_mac_cb *mac_cb, u8 en); +int hns_mac_config_mac_loopback(struct hns_mac_cb *mac_cb, + enum hnae_loop loop, int en); +void hns_mac_update_stats(struct hns_mac_cb *mac_cb); +void hns_mac_get_stats(struct hns_mac_cb *mac_cb, u64 *data); +void hns_mac_get_strings(struct hns_mac_cb *mac_cb, int stringset, u8 *data); +int hns_mac_get_sset_count(struct hns_mac_cb *mac_cb, int stringset); +void hns_mac_get_regs(struct hns_mac_cb *mac_cb, void *data); +int hns_mac_get_regs_count(struct hns_mac_cb *mac_cb); +void hns_set_led_opt(struct hns_mac_cb *mac_cb); +int hns_cpld_led_set_id(struct hns_mac_cb *mac_cb, + enum hnae_led_state status); +#endif /* _HNS_DSAF_MAC_H */ diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c new file mode 100644 index 000000000000..26ae6c64d74c --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c @@ -0,0 +1,2445 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * 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/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/netdevice.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/device.h> +#include "hns_dsaf_main.h" +#include "hns_dsaf_rcb.h" +#include "hns_dsaf_ppe.h" +#include "hns_dsaf_mac.h" + +const char *g_dsaf_mode_match[DSAF_MODE_MAX] = { + [DSAF_MODE_DISABLE_2PORT_64VM] = "2port-64vf", + [DSAF_MODE_DISABLE_6PORT_0VM] = "6port-16rss", + [DSAF_MODE_DISABLE_6PORT_16VM] = "6port-16vf", +}; + +int hns_dsaf_get_cfg(struct dsaf_device *dsaf_dev) +{ + int ret, i; + u32 desc_num; + u32 buf_size; + const char *name, *mode_str; + struct device_node *np = dsaf_dev->dev->of_node; + + if (of_device_is_compatible(np, "hisilicon,hns-dsaf-v2")) + dsaf_dev->dsaf_ver = AE_VERSION_2; + else + dsaf_dev->dsaf_ver = AE_VERSION_1; + + ret = of_property_read_string(np, "dsa_name", &name); + if (ret) { + dev_err(dsaf_dev->dev, "get dsaf name fail, ret=%d!\n", ret); + return ret; + } + strncpy(dsaf_dev->ae_dev.name, name, AE_NAME_SIZE); + dsaf_dev->ae_dev.name[AE_NAME_SIZE - 1] = '\0'; + + ret = of_property_read_string(np, "mode", &mode_str); + if (ret) { + dev_err(dsaf_dev->dev, "get dsaf mode fail, ret=%d!\n", ret); + return ret; + } + for (i = 0; i < DSAF_MODE_MAX; i++) { + if (g_dsaf_mode_match[i] && + !strcmp(mode_str, g_dsaf_mode_match[i])) + break; + } + if (i >= DSAF_MODE_MAX || + i == DSAF_MODE_INVALID || i == DSAF_MODE_ENABLE) { + dev_err(dsaf_dev->dev, + "%s prs mode str fail!\n", dsaf_dev->ae_dev.name); + return -EINVAL; + } + dsaf_dev->dsaf_mode = (enum dsaf_mode)i; + + if (dsaf_dev->dsaf_mode > DSAF_MODE_ENABLE) + dsaf_dev->dsaf_en = HRD_DSAF_NO_DSAF_MODE; + else + dsaf_dev->dsaf_en = HRD_DSAF_MODE; + + if ((i == DSAF_MODE_ENABLE_16VM) || + (i == DSAF_MODE_DISABLE_2PORT_8VM) || + (i == DSAF_MODE_DISABLE_6PORT_2VM)) + dsaf_dev->dsaf_tc_mode = HRD_DSAF_8TC_MODE; + else + dsaf_dev->dsaf_tc_mode = HRD_DSAF_4TC_MODE; + + dsaf_dev->sc_base = of_iomap(np, 0); + if (!dsaf_dev->sc_base) { + dev_err(dsaf_dev->dev, + "%s of_iomap 0 fail!\n", dsaf_dev->ae_dev.name); + ret = -ENOMEM; + goto unmap_base_addr; + } + + dsaf_dev->sds_base = of_iomap(np, 1); + if (!dsaf_dev->sds_base) { + dev_err(dsaf_dev->dev, + "%s of_iomap 1 fail!\n", dsaf_dev->ae_dev.name); + ret = -ENOMEM; + goto unmap_base_addr; + } + + dsaf_dev->ppe_base = of_iomap(np, 2); + if (!dsaf_dev->ppe_base) { + dev_err(dsaf_dev->dev, + "%s of_iomap 2 fail!\n", dsaf_dev->ae_dev.name); + ret = -ENOMEM; + goto unmap_base_addr; + } + + dsaf_dev->io_base = of_iomap(np, 3); + if (!dsaf_dev->io_base) { + dev_err(dsaf_dev->dev, + "%s of_iomap 3 fail!\n", dsaf_dev->ae_dev.name); + ret = -ENOMEM; + goto unmap_base_addr; + } + + dsaf_dev->cpld_base = of_iomap(np, 4); + if (!dsaf_dev->cpld_base) + dev_dbg(dsaf_dev->dev, "NO CPLD ADDR"); + + ret = of_property_read_u32(np, "desc-num", &desc_num); + if (ret < 0 || desc_num < HNS_DSAF_MIN_DESC_CNT || + desc_num > HNS_DSAF_MAX_DESC_CNT) { + dev_err(dsaf_dev->dev, "get desc-num(%d) fail, ret=%d!\n", + desc_num, ret); + goto unmap_base_addr; + } + dsaf_dev->desc_num = desc_num; + + ret = of_property_read_u32(np, "buf-size", &buf_size); + if (ret < 0) { + dev_err(dsaf_dev->dev, + "get buf-size fail, ret=%d!\r\n", ret); + goto unmap_base_addr; + } + dsaf_dev->buf_size = buf_size; + + dsaf_dev->buf_size_type = hns_rcb_buf_size2type(buf_size); + if (dsaf_dev->buf_size_type < 0) { + dev_err(dsaf_dev->dev, + "buf_size(%d) is wrong!\n", buf_size); + goto unmap_base_addr; + } + + if (!dma_set_mask_and_coherent(dsaf_dev->dev, DMA_BIT_MASK(64ULL))) + dev_dbg(dsaf_dev->dev, "set mask to 64bit\n"); + else + dev_err(dsaf_dev->dev, "set mask to 64bit fail!\n"); + + return 0; + +unmap_base_addr: + if (dsaf_dev->io_base) + iounmap(dsaf_dev->io_base); + if (dsaf_dev->ppe_base) + iounmap(dsaf_dev->ppe_base); + if (dsaf_dev->sds_base) + iounmap(dsaf_dev->sds_base); + if (dsaf_dev->sc_base) + iounmap(dsaf_dev->sc_base); + if (dsaf_dev->cpld_base) + iounmap(dsaf_dev->cpld_base); + return ret; +} + +static void hns_dsaf_free_cfg(struct dsaf_device *dsaf_dev) +{ + if (dsaf_dev->io_base) + iounmap(dsaf_dev->io_base); + + if (dsaf_dev->ppe_base) + iounmap(dsaf_dev->ppe_base); + + if (dsaf_dev->sds_base) + iounmap(dsaf_dev->sds_base); + + if (dsaf_dev->sc_base) + iounmap(dsaf_dev->sc_base); + + if (dsaf_dev->cpld_base) + iounmap(dsaf_dev->cpld_base); +} + +/** + * hns_dsaf_sbm_link_sram_init_en - config dsaf_sbm_init_en + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_sbm_link_sram_init_en(struct dsaf_device *dsaf_dev) +{ + dsaf_set_dev_bit(dsaf_dev, DSAF_CFG_0_REG, DSAF_CFG_SBM_INIT_S, 1); +} + +/** + * hns_dsaf_reg_cnt_clr_ce - config hns_dsaf_reg_cnt_clr_ce + * @dsaf_id: dsa fabric id + * @hns_dsaf_reg_cnt_clr_ce: config value + */ +static void +hns_dsaf_reg_cnt_clr_ce(struct dsaf_device *dsaf_dev, u32 reg_cnt_clr_ce) +{ + dsaf_set_dev_bit(dsaf_dev, DSAF_DSA_REG_CNT_CLR_CE_REG, + DSAF_CNT_CLR_CE_S, reg_cnt_clr_ce); +} + +/** + * hns_ppe_qid_cfg - config ppe qid + * @dsaf_id: dsa fabric id + * @pppe_qid_cfg: value array + */ +static void +hns_dsaf_ppe_qid_cfg(struct dsaf_device *dsaf_dev, u32 qid_cfg) +{ + u32 i; + + for (i = 0; i < DSAF_COMM_CHN; i++) { + dsaf_set_dev_field(dsaf_dev, + DSAF_PPE_QID_CFG_0_REG + 0x0004 * i, + DSAF_PPE_QID_CFG_M, DSAF_PPE_QID_CFG_S, + qid_cfg); + } +} + +/** + * hns_dsaf_sw_port_type_cfg - cfg sw type + * @dsaf_id: dsa fabric id + * @psw_port_type: array + */ +static void hns_dsaf_sw_port_type_cfg(struct dsaf_device *dsaf_dev, + enum dsaf_sw_port_type port_type) +{ + u32 i; + + for (i = 0; i < DSAF_SW_PORT_NUM; i++) { + dsaf_set_dev_field(dsaf_dev, + DSAF_SW_PORT_TYPE_0_REG + 0x0004 * i, + DSAF_SW_PORT_TYPE_M, DSAF_SW_PORT_TYPE_S, + port_type); + } +} + +/** + * hns_dsaf_stp_port_type_cfg - cfg stp type + * @dsaf_id: dsa fabric id + * @pstp_port_type: array + */ +static void hns_dsaf_stp_port_type_cfg(struct dsaf_device *dsaf_dev, + enum dsaf_stp_port_type port_type) +{ + u32 i; + + for (i = 0; i < DSAF_COMM_CHN; i++) { + dsaf_set_dev_field(dsaf_dev, + DSAF_STP_PORT_TYPE_0_REG + 0x0004 * i, + DSAF_STP_PORT_TYPE_M, DSAF_STP_PORT_TYPE_S, + port_type); + } +} + +/** + * hns_dsaf_sbm_cfg - config sbm + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_sbm_cfg(struct dsaf_device *dsaf_dev) +{ + u32 o_sbm_cfg; + u32 i; + + for (i = 0; i < DSAF_SBM_NUM; i++) { + o_sbm_cfg = dsaf_read_dev(dsaf_dev, + DSAF_SBM_CFG_REG_0_REG + 0x80 * i); + dsaf_set_bit(o_sbm_cfg, DSAF_SBM_CFG_EN_S, 1); + dsaf_set_bit(o_sbm_cfg, DSAF_SBM_CFG_SHCUT_EN_S, 0); + dsaf_write_dev(dsaf_dev, + DSAF_SBM_CFG_REG_0_REG + 0x80 * i, o_sbm_cfg); + } +} + +/** + * hns_dsaf_sbm_cfg_mib_en - config sbm + * @dsaf_id: dsa fabric id + */ +static int hns_dsaf_sbm_cfg_mib_en(struct dsaf_device *dsaf_dev) +{ + u32 sbm_cfg_mib_en; + u32 i; + u32 reg; + u32 read_cnt; + + for (i = 0; i < DSAF_SBM_NUM; i++) { + reg = DSAF_SBM_CFG_REG_0_REG + 0x80 * i; + dsaf_set_dev_bit(dsaf_dev, reg, DSAF_SBM_CFG_MIB_EN_S, 1); + } + + /* waitint for all sbm enable finished */ + for (i = 0; i < DSAF_SBM_NUM; i++) { + read_cnt = 0; + reg = DSAF_SBM_CFG_REG_0_REG + 0x80 * i; + do { + udelay(1); + sbm_cfg_mib_en = dsaf_get_dev_bit( + dsaf_dev, reg, DSAF_SBM_CFG_MIB_EN_S); + read_cnt++; + } while (sbm_cfg_mib_en == 0 && + read_cnt < DSAF_CFG_READ_CNT); + + if (sbm_cfg_mib_en == 0) { + dev_err(dsaf_dev->dev, + "sbm_cfg_mib_en fail,%s,sbm_num=%d\n", + dsaf_dev->ae_dev.name, i); + return -ENODEV; + } + } + + return 0; +} + +/** + * hns_dsaf_sbm_bp_wl_cfg - config sbm + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_sbm_bp_wl_cfg(struct dsaf_device *dsaf_dev) +{ + u32 o_sbm_bp_cfg0; + u32 o_sbm_bp_cfg1; + u32 o_sbm_bp_cfg2; + u32 o_sbm_bp_cfg3; + u32 reg; + u32 i; + + /* XGE */ + for (i = 0; i < DSAF_XGE_NUM; i++) { + reg = DSAF_SBM_BP_CFG_0_XGE_REG_0_REG + 0x80 * i; + o_sbm_bp_cfg0 = dsaf_read_dev(dsaf_dev, reg); + dsaf_set_field(o_sbm_bp_cfg0, DSAF_SBM_CFG0_COM_MAX_BUF_NUM_M, + DSAF_SBM_CFG0_COM_MAX_BUF_NUM_S, 512); + dsaf_set_field(o_sbm_bp_cfg0, DSAF_SBM_CFG0_VC0_MAX_BUF_NUM_M, + DSAF_SBM_CFG0_VC0_MAX_BUF_NUM_S, 0); + dsaf_set_field(o_sbm_bp_cfg0, DSAF_SBM_CFG0_VC1_MAX_BUF_NUM_M, + DSAF_SBM_CFG0_VC1_MAX_BUF_NUM_S, 0); + dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg0); + + reg = DSAF_SBM_BP_CFG_1_REG_0_REG + 0x80 * i; + o_sbm_bp_cfg1 = dsaf_read_dev(dsaf_dev, reg); + dsaf_set_field(o_sbm_bp_cfg1, DSAF_SBM_CFG1_TC4_MAX_BUF_NUM_M, + DSAF_SBM_CFG1_TC4_MAX_BUF_NUM_S, 0); + dsaf_set_field(o_sbm_bp_cfg1, DSAF_SBM_CFG1_TC0_MAX_BUF_NUM_M, + DSAF_SBM_CFG1_TC0_MAX_BUF_NUM_S, 0); + dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg1); + + reg = DSAF_SBM_BP_CFG_2_XGE_REG_0_REG + 0x80 * i; + o_sbm_bp_cfg2 = dsaf_read_dev(dsaf_dev, reg); + dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_SET_BUF_NUM_M, + DSAF_SBM_CFG2_SET_BUF_NUM_S, 104); + dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_RESET_BUF_NUM_M, + DSAF_SBM_CFG2_RESET_BUF_NUM_S, 128); + dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg2); + + reg = DSAF_SBM_BP_CFG_3_REG_0_REG + 0x80 * i; + o_sbm_bp_cfg3 = dsaf_read_dev(dsaf_dev, reg); + dsaf_set_field(o_sbm_bp_cfg3, + DSAF_SBM_CFG3_SET_BUF_NUM_NO_PFC_M, + DSAF_SBM_CFG3_SET_BUF_NUM_NO_PFC_S, 110); + dsaf_set_field(o_sbm_bp_cfg3, + DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_M, + DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_S, 160); + dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg3); + + /* for no enable pfc mode */ + reg = DSAF_SBM_BP_CFG_4_REG_0_REG + 0x80 * i; + o_sbm_bp_cfg3 = dsaf_read_dev(dsaf_dev, reg); + dsaf_set_field(o_sbm_bp_cfg3, + DSAF_SBM_CFG3_SET_BUF_NUM_NO_PFC_M, + DSAF_SBM_CFG3_SET_BUF_NUM_NO_PFC_S, 128); + dsaf_set_field(o_sbm_bp_cfg3, + DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_M, + DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_S, 192); + dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg3); + } + + /* PPE */ + for (i = 0; i < DSAF_COMM_CHN; i++) { + reg = DSAF_SBM_BP_CFG_2_PPE_REG_0_REG + 0x80 * i; + o_sbm_bp_cfg2 = dsaf_read_dev(dsaf_dev, reg); + dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_SET_BUF_NUM_M, + DSAF_SBM_CFG2_SET_BUF_NUM_S, 10); + dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_RESET_BUF_NUM_M, + DSAF_SBM_CFG2_RESET_BUF_NUM_S, 12); + dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg2); + } + + /* RoCEE */ + for (i = 0; i < DSAF_COMM_CHN; i++) { + reg = DSAF_SBM_BP_CFG_2_ROCEE_REG_0_REG + 0x80 * i; + o_sbm_bp_cfg2 = dsaf_read_dev(dsaf_dev, reg); + dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_SET_BUF_NUM_M, + DSAF_SBM_CFG2_SET_BUF_NUM_S, 2); + dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_RESET_BUF_NUM_M, + DSAF_SBM_CFG2_RESET_BUF_NUM_S, 4); + dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg2); + } +} + +/** + * hns_dsaf_voq_bp_all_thrd_cfg - voq + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_voq_bp_all_thrd_cfg(struct dsaf_device *dsaf_dev) +{ + u32 voq_bp_all_thrd; + u32 i; + + for (i = 0; i < DSAF_VOQ_NUM; i++) { + voq_bp_all_thrd = dsaf_read_dev( + dsaf_dev, DSAF_VOQ_BP_ALL_THRD_0_REG + 0x40 * i); + if (i < DSAF_XGE_NUM) { + dsaf_set_field(voq_bp_all_thrd, + DSAF_VOQ_BP_ALL_DOWNTHRD_M, + DSAF_VOQ_BP_ALL_DOWNTHRD_S, 930); + dsaf_set_field(voq_bp_all_thrd, + DSAF_VOQ_BP_ALL_UPTHRD_M, + DSAF_VOQ_BP_ALL_UPTHRD_S, 950); + } else { + dsaf_set_field(voq_bp_all_thrd, + DSAF_VOQ_BP_ALL_DOWNTHRD_M, + DSAF_VOQ_BP_ALL_DOWNTHRD_S, 220); + dsaf_set_field(voq_bp_all_thrd, + DSAF_VOQ_BP_ALL_UPTHRD_M, + DSAF_VOQ_BP_ALL_UPTHRD_S, 230); + } + dsaf_write_dev( + dsaf_dev, DSAF_VOQ_BP_ALL_THRD_0_REG + 0x40 * i, + voq_bp_all_thrd); + } +} + +/** + * hns_dsaf_tbl_tcam_data_cfg - tbl + * @dsaf_id: dsa fabric id + * @ptbl_tcam_data: addr + */ +static void hns_dsaf_tbl_tcam_data_cfg( + struct dsaf_device *dsaf_dev, + struct dsaf_tbl_tcam_data *ptbl_tcam_data) +{ + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_LOW_0_REG, + ptbl_tcam_data->tbl_tcam_data_low); + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_HIGH_0_REG, + ptbl_tcam_data->tbl_tcam_data_high); +} + +/** + * dsaf_tbl_tcam_mcast_cfg - tbl + * @dsaf_id: dsa fabric id + * @ptbl_tcam_mcast: addr + */ +static void hns_dsaf_tbl_tcam_mcast_cfg( + struct dsaf_device *dsaf_dev, + struct dsaf_tbl_tcam_mcast_cfg *mcast) +{ + u32 mcast_cfg4; + + mcast_cfg4 = dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_4_0_REG); + dsaf_set_bit(mcast_cfg4, DSAF_TBL_MCAST_CFG4_ITEM_VLD_S, + mcast->tbl_mcast_item_vld); + dsaf_set_bit(mcast_cfg4, DSAF_TBL_MCAST_CFG4_OLD_EN_S, + mcast->tbl_mcast_old_en); + dsaf_set_field(mcast_cfg4, DSAF_TBL_MCAST_CFG4_VM128_112_M, + DSAF_TBL_MCAST_CFG4_VM128_112_S, + mcast->tbl_mcast_port_msk[4]); + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_4_0_REG, mcast_cfg4); + + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_3_0_REG, + mcast->tbl_mcast_port_msk[3]); + + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_2_0_REG, + mcast->tbl_mcast_port_msk[2]); + + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_1_0_REG, + mcast->tbl_mcast_port_msk[1]); + + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_0_0_REG, + mcast->tbl_mcast_port_msk[0]); +} + +/** + * hns_dsaf_tbl_tcam_ucast_cfg - tbl + * @dsaf_id: dsa fabric id + * @ptbl_tcam_ucast: addr + */ +static void hns_dsaf_tbl_tcam_ucast_cfg( + struct dsaf_device *dsaf_dev, + struct dsaf_tbl_tcam_ucast_cfg *tbl_tcam_ucast) +{ + u32 ucast_cfg1; + + ucast_cfg1 = dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_UCAST_CFG_0_REG); + dsaf_set_bit(ucast_cfg1, DSAF_TBL_UCAST_CFG1_MAC_DISCARD_S, + tbl_tcam_ucast->tbl_ucast_mac_discard); + dsaf_set_bit(ucast_cfg1, DSAF_TBL_UCAST_CFG1_ITEM_VLD_S, + tbl_tcam_ucast->tbl_ucast_item_vld); + dsaf_set_bit(ucast_cfg1, DSAF_TBL_UCAST_CFG1_OLD_EN_S, + tbl_tcam_ucast->tbl_ucast_old_en); + dsaf_set_bit(ucast_cfg1, DSAF_TBL_UCAST_CFG1_DVC_S, + tbl_tcam_ucast->tbl_ucast_dvc); + dsaf_set_field(ucast_cfg1, DSAF_TBL_UCAST_CFG1_OUT_PORT_M, + DSAF_TBL_UCAST_CFG1_OUT_PORT_S, + tbl_tcam_ucast->tbl_ucast_out_port); + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_UCAST_CFG_0_REG, ucast_cfg1); +} + +/** + * hns_dsaf_tbl_line_cfg - tbl + * @dsaf_id: dsa fabric id + * @ptbl_lin: addr + */ +static void hns_dsaf_tbl_line_cfg(struct dsaf_device *dsaf_dev, + struct dsaf_tbl_line_cfg *tbl_lin) +{ + u32 tbl_line; + + tbl_line = dsaf_read_dev(dsaf_dev, DSAF_TBL_LIN_CFG_0_REG); + dsaf_set_bit(tbl_line, DSAF_TBL_LINE_CFG_MAC_DISCARD_S, + tbl_lin->tbl_line_mac_discard); + dsaf_set_bit(tbl_line, DSAF_TBL_LINE_CFG_DVC_S, + tbl_lin->tbl_line_dvc); + dsaf_set_field(tbl_line, DSAF_TBL_LINE_CFG_OUT_PORT_M, + DSAF_TBL_LINE_CFG_OUT_PORT_S, + tbl_lin->tbl_line_out_port); + dsaf_write_dev(dsaf_dev, DSAF_TBL_LIN_CFG_0_REG, tbl_line); +} + +/** + * hns_dsaf_tbl_tcam_mcast_pul - tbl + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_tbl_tcam_mcast_pul(struct dsaf_device *dsaf_dev) +{ + u32 o_tbl_pul; + + o_tbl_pul = dsaf_read_dev(dsaf_dev, DSAF_TBL_PUL_0_REG); + dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_MCAST_VLD_S, 1); + dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, o_tbl_pul); + dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_MCAST_VLD_S, 0); + dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, o_tbl_pul); +} + +/** + * hns_dsaf_tbl_line_pul - tbl + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_tbl_line_pul(struct dsaf_device *dsaf_dev) +{ + u32 tbl_pul; + + tbl_pul = dsaf_read_dev(dsaf_dev, DSAF_TBL_PUL_0_REG); + dsaf_set_bit(tbl_pul, DSAF_TBL_PUL_LINE_VLD_S, 1); + dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, tbl_pul); + dsaf_set_bit(tbl_pul, DSAF_TBL_PUL_LINE_VLD_S, 0); + dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, tbl_pul); +} + +/** + * hns_dsaf_tbl_tcam_data_mcast_pul - tbl + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_tbl_tcam_data_mcast_pul( + struct dsaf_device *dsaf_dev) +{ + u32 o_tbl_pul; + + o_tbl_pul = dsaf_read_dev(dsaf_dev, DSAF_TBL_PUL_0_REG); + dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_TCAM_DATA_VLD_S, 1); + dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_MCAST_VLD_S, 1); + dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, o_tbl_pul); + dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_TCAM_DATA_VLD_S, 0); + dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_MCAST_VLD_S, 0); + dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, o_tbl_pul); +} + +/** + * hns_dsaf_tbl_tcam_data_ucast_pul - tbl + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_tbl_tcam_data_ucast_pul( + struct dsaf_device *dsaf_dev) +{ + u32 o_tbl_pul; + + o_tbl_pul = dsaf_read_dev(dsaf_dev, DSAF_TBL_PUL_0_REG); + dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_TCAM_DATA_VLD_S, 1); + dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_UCAST_VLD_S, 1); + dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, o_tbl_pul); + dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_TCAM_DATA_VLD_S, 0); + dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_UCAST_VLD_S, 0); + dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, o_tbl_pul); +} + +/** + * hns_dsaf_tbl_stat_en - tbl + * @dsaf_id: dsa fabric id + * @ptbl_stat_en: addr + */ +static void hns_dsaf_tbl_stat_en(struct dsaf_device *dsaf_dev) +{ + u32 o_tbl_ctrl; + + o_tbl_ctrl = dsaf_read_dev(dsaf_dev, DSAF_TBL_DFX_CTRL_0_REG); + dsaf_set_bit(o_tbl_ctrl, DSAF_TBL_DFX_LINE_LKUP_NUM_EN_S, 1); + dsaf_set_bit(o_tbl_ctrl, DSAF_TBL_DFX_UC_LKUP_NUM_EN_S, 1); + dsaf_set_bit(o_tbl_ctrl, DSAF_TBL_DFX_MC_LKUP_NUM_EN_S, 1); + dsaf_set_bit(o_tbl_ctrl, DSAF_TBL_DFX_BC_LKUP_NUM_EN_S, 1); + dsaf_write_dev(dsaf_dev, DSAF_TBL_DFX_CTRL_0_REG, o_tbl_ctrl); +} + +/** + * hns_dsaf_rocee_bp_en - rocee back press enable + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_rocee_bp_en(struct dsaf_device *dsaf_dev) +{ + dsaf_set_dev_bit(dsaf_dev, DSAF_XGE_CTRL_SIG_CFG_0_REG, + DSAF_FC_XGE_TX_PAUSE_S, 1); +} + +/* set msk for dsaf exception irq*/ +static void hns_dsaf_int_xge_msk_set(struct dsaf_device *dsaf_dev, + u32 chnn_num, u32 mask_set) +{ + dsaf_write_dev(dsaf_dev, + DSAF_XGE_INT_MSK_0_REG + 0x4 * chnn_num, mask_set); +} + +static void hns_dsaf_int_ppe_msk_set(struct dsaf_device *dsaf_dev, + u32 chnn_num, u32 msk_set) +{ + dsaf_write_dev(dsaf_dev, + DSAF_PPE_INT_MSK_0_REG + 0x4 * chnn_num, msk_set); +} + +static void hns_dsaf_int_rocee_msk_set(struct dsaf_device *dsaf_dev, + u32 chnn, u32 msk_set) +{ + dsaf_write_dev(dsaf_dev, + DSAF_ROCEE_INT_MSK_0_REG + 0x4 * chnn, msk_set); +} + +static void +hns_dsaf_int_tbl_msk_set(struct dsaf_device *dsaf_dev, u32 msk_set) +{ + dsaf_write_dev(dsaf_dev, DSAF_TBL_INT_MSK_0_REG, msk_set); +} + +/* clr dsaf exception irq*/ +static void hns_dsaf_int_xge_src_clr(struct dsaf_device *dsaf_dev, + u32 chnn_num, u32 int_src) +{ + dsaf_write_dev(dsaf_dev, + DSAF_XGE_INT_SRC_0_REG + 0x4 * chnn_num, int_src); +} + +static void hns_dsaf_int_ppe_src_clr(struct dsaf_device *dsaf_dev, + u32 chnn, u32 int_src) +{ + dsaf_write_dev(dsaf_dev, + DSAF_PPE_INT_SRC_0_REG + 0x4 * chnn, int_src); +} + +static void hns_dsaf_int_rocee_src_clr(struct dsaf_device *dsaf_dev, + u32 chnn, u32 int_src) +{ + dsaf_write_dev(dsaf_dev, + DSAF_ROCEE_INT_SRC_0_REG + 0x4 * chnn, int_src); +} + +static void hns_dsaf_int_tbl_src_clr(struct dsaf_device *dsaf_dev, + u32 int_src) +{ + dsaf_write_dev(dsaf_dev, DSAF_TBL_INT_SRC_0_REG, int_src); +} + +/** + * hns_dsaf_single_line_tbl_cfg - INT + * @dsaf_id: dsa fabric id + * @address: + * @ptbl_line: + */ +static void hns_dsaf_single_line_tbl_cfg( + struct dsaf_device *dsaf_dev, + u32 address, struct dsaf_tbl_line_cfg *ptbl_line) +{ + /*Write Addr*/ + hns_dsaf_tbl_line_addr_cfg(dsaf_dev, address); + + /*Write Line*/ + hns_dsaf_tbl_line_cfg(dsaf_dev, ptbl_line); + + /*Write Plus*/ + hns_dsaf_tbl_line_pul(dsaf_dev); +} + +/** + * hns_dsaf_tcam_uc_cfg - INT + * @dsaf_id: dsa fabric id + * @address, + * @ptbl_tcam_data, + */ +static void hns_dsaf_tcam_uc_cfg( + struct dsaf_device *dsaf_dev, u32 address, + struct dsaf_tbl_tcam_data *ptbl_tcam_data, + struct dsaf_tbl_tcam_ucast_cfg *ptbl_tcam_ucast) +{ + /*Write Addr*/ + hns_dsaf_tbl_tcam_addr_cfg(dsaf_dev, address); + /*Write Tcam Data*/ + hns_dsaf_tbl_tcam_data_cfg(dsaf_dev, ptbl_tcam_data); + /*Write Tcam Ucast*/ + hns_dsaf_tbl_tcam_ucast_cfg(dsaf_dev, ptbl_tcam_ucast); + /*Write Plus*/ + hns_dsaf_tbl_tcam_data_ucast_pul(dsaf_dev); +} + +/** + * hns_dsaf_tcam_mc_cfg - INT + * @dsaf_id: dsa fabric id + * @address, + * @ptbl_tcam_data, + * @ptbl_tcam_mcast, + */ +static void hns_dsaf_tcam_mc_cfg( + struct dsaf_device *dsaf_dev, u32 address, + struct dsaf_tbl_tcam_data *ptbl_tcam_data, + struct dsaf_tbl_tcam_mcast_cfg *ptbl_tcam_mcast) +{ + /*Write Addr*/ + hns_dsaf_tbl_tcam_addr_cfg(dsaf_dev, address); + /*Write Tcam Data*/ + hns_dsaf_tbl_tcam_data_cfg(dsaf_dev, ptbl_tcam_data); + /*Write Tcam Mcast*/ + hns_dsaf_tbl_tcam_mcast_cfg(dsaf_dev, ptbl_tcam_mcast); + /*Write Plus*/ + hns_dsaf_tbl_tcam_data_mcast_pul(dsaf_dev); +} + +/** + * hns_dsaf_tcam_mc_invld - INT + * @dsaf_id: dsa fabric id + * @address + */ +static void hns_dsaf_tcam_mc_invld(struct dsaf_device *dsaf_dev, u32 address) +{ + /*Write Addr*/ + hns_dsaf_tbl_tcam_addr_cfg(dsaf_dev, address); + + /*write tcam mcast*/ + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_0_0_REG, 0); + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_1_0_REG, 0); + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_2_0_REG, 0); + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_3_0_REG, 0); + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_4_0_REG, 0); + + /*Write Plus*/ + hns_dsaf_tbl_tcam_mcast_pul(dsaf_dev); +} + +/** + * hns_dsaf_tcam_uc_get - INT + * @dsaf_id: dsa fabric id + * @address + * @ptbl_tcam_data + * @ptbl_tcam_ucast + */ +static void hns_dsaf_tcam_uc_get( + struct dsaf_device *dsaf_dev, u32 address, + struct dsaf_tbl_tcam_data *ptbl_tcam_data, + struct dsaf_tbl_tcam_ucast_cfg *ptbl_tcam_ucast) +{ + u32 tcam_read_data0; + u32 tcam_read_data4; + + /*Write Addr*/ + hns_dsaf_tbl_tcam_addr_cfg(dsaf_dev, address); + + /*read tcam item puls*/ + hns_dsaf_tbl_tcam_load_pul(dsaf_dev); + + /*read tcam data*/ + ptbl_tcam_data->tbl_tcam_data_high + = dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_RDATA_LOW_0_REG); + ptbl_tcam_data->tbl_tcam_data_low + = dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_RDATA_HIGH_0_REG); + + /*read tcam mcast*/ + tcam_read_data0 = dsaf_read_dev(dsaf_dev, + DSAF_TBL_TCAM_RAM_RDATA0_0_REG); + tcam_read_data4 = dsaf_read_dev(dsaf_dev, + DSAF_TBL_TCAM_RAM_RDATA4_0_REG); + + ptbl_tcam_ucast->tbl_ucast_item_vld + = dsaf_get_bit(tcam_read_data4, + DSAF_TBL_MCAST_CFG4_ITEM_VLD_S); + ptbl_tcam_ucast->tbl_ucast_old_en + = dsaf_get_bit(tcam_read_data4, DSAF_TBL_MCAST_CFG4_OLD_EN_S); + ptbl_tcam_ucast->tbl_ucast_mac_discard + = dsaf_get_bit(tcam_read_data0, + DSAF_TBL_UCAST_CFG1_MAC_DISCARD_S); + ptbl_tcam_ucast->tbl_ucast_out_port + = dsaf_get_field(tcam_read_data0, + DSAF_TBL_UCAST_CFG1_OUT_PORT_M, + DSAF_TBL_UCAST_CFG1_OUT_PORT_S); + ptbl_tcam_ucast->tbl_ucast_dvc + = dsaf_get_bit(tcam_read_data0, DSAF_TBL_UCAST_CFG1_DVC_S); +} + +/** + * hns_dsaf_tcam_mc_get - INT + * @dsaf_id: dsa fabric id + * @address + * @ptbl_tcam_data + * @ptbl_tcam_ucast + */ +static void hns_dsaf_tcam_mc_get( + struct dsaf_device *dsaf_dev, u32 address, + struct dsaf_tbl_tcam_data *ptbl_tcam_data, + struct dsaf_tbl_tcam_mcast_cfg *ptbl_tcam_mcast) +{ + u32 data_tmp; + + /*Write Addr*/ + hns_dsaf_tbl_tcam_addr_cfg(dsaf_dev, address); + + /*read tcam item puls*/ + hns_dsaf_tbl_tcam_load_pul(dsaf_dev); + + /*read tcam data*/ + ptbl_tcam_data->tbl_tcam_data_high = + dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_RDATA_LOW_0_REG); + ptbl_tcam_data->tbl_tcam_data_low = + dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_RDATA_HIGH_0_REG); + + /*read tcam mcast*/ + ptbl_tcam_mcast->tbl_mcast_port_msk[0] = + dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_RAM_RDATA0_0_REG); + ptbl_tcam_mcast->tbl_mcast_port_msk[1] = + dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_RAM_RDATA1_0_REG); + ptbl_tcam_mcast->tbl_mcast_port_msk[2] = + dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_RAM_RDATA2_0_REG); + ptbl_tcam_mcast->tbl_mcast_port_msk[3] = + dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_RAM_RDATA3_0_REG); + + data_tmp = dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_RAM_RDATA4_0_REG); + ptbl_tcam_mcast->tbl_mcast_item_vld = + dsaf_get_bit(data_tmp, DSAF_TBL_MCAST_CFG4_ITEM_VLD_S); + ptbl_tcam_mcast->tbl_mcast_old_en = + dsaf_get_bit(data_tmp, DSAF_TBL_MCAST_CFG4_OLD_EN_S); + ptbl_tcam_mcast->tbl_mcast_port_msk[4] = + dsaf_get_field(data_tmp, DSAF_TBL_MCAST_CFG4_VM128_112_M, + DSAF_TBL_MCAST_CFG4_VM128_112_S); +} + +/** + * hns_dsaf_tbl_line_init - INT + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_tbl_line_init(struct dsaf_device *dsaf_dev) +{ + u32 i; + /* defaultly set all lineal mac table entry resulting discard */ + struct dsaf_tbl_line_cfg tbl_line[] = {{1, 0, 0} }; + + for (i = 0; i < DSAF_LINE_SUM; i++) + hns_dsaf_single_line_tbl_cfg(dsaf_dev, i, tbl_line); +} + +/** + * hns_dsaf_tbl_tcam_init - INT + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_tbl_tcam_init(struct dsaf_device *dsaf_dev) +{ + u32 i; + struct dsaf_tbl_tcam_data tcam_data[] = {{0, 0} }; + struct dsaf_tbl_tcam_ucast_cfg tcam_ucast[] = {{0, 0, 0, 0, 0} }; + + /*tcam tbl*/ + for (i = 0; i < DSAF_TCAM_SUM; i++) + hns_dsaf_tcam_uc_cfg(dsaf_dev, i, tcam_data, tcam_ucast); +} + +/** + * hns_dsaf_pfc_en_cfg - dsaf pfc pause cfg + * @mac_cb: mac contrl block + */ +static void hns_dsaf_pfc_en_cfg(struct dsaf_device *dsaf_dev, + int mac_id, int en) +{ + if (!en) + dsaf_write_dev(dsaf_dev, DSAF_PFC_EN_0_REG + mac_id * 4, 0); + else + dsaf_write_dev(dsaf_dev, DSAF_PFC_EN_0_REG + mac_id * 4, 0xff); +} + +/** + * hns_dsaf_tbl_tcam_init - INT + * @dsaf_id: dsa fabric id + * @dsaf_mode + */ +static void hns_dsaf_comm_init(struct dsaf_device *dsaf_dev) +{ + u32 i; + u32 o_dsaf_cfg; + + o_dsaf_cfg = dsaf_read_dev(dsaf_dev, DSAF_CFG_0_REG); + dsaf_set_bit(o_dsaf_cfg, DSAF_CFG_EN_S, dsaf_dev->dsaf_en); + dsaf_set_bit(o_dsaf_cfg, DSAF_CFG_TC_MODE_S, dsaf_dev->dsaf_tc_mode); + dsaf_set_bit(o_dsaf_cfg, DSAF_CFG_CRC_EN_S, 0); + dsaf_set_bit(o_dsaf_cfg, DSAF_CFG_MIX_MODE_S, 0); + dsaf_set_bit(o_dsaf_cfg, DSAF_CFG_LOCA_ADDR_EN_S, 0); + dsaf_write_dev(dsaf_dev, DSAF_CFG_0_REG, o_dsaf_cfg); + + hns_dsaf_reg_cnt_clr_ce(dsaf_dev, 1); + hns_dsaf_stp_port_type_cfg(dsaf_dev, DSAF_STP_PORT_TYPE_FORWARD); + + /* set 22 queue per tx ppe engine, only used in switch mode */ + hns_dsaf_ppe_qid_cfg(dsaf_dev, DSAF_DEFAUTL_QUEUE_NUM_PER_PPE); + + /* in non switch mode, set all port to access mode */ + hns_dsaf_sw_port_type_cfg(dsaf_dev, DSAF_SW_PORT_TYPE_NON_VLAN); + + /*set dsaf pfc to 0 for parseing rx pause*/ + for (i = 0; i < DSAF_COMM_CHN; i++) + hns_dsaf_pfc_en_cfg(dsaf_dev, i, 0); + + /*msk and clr exception irqs */ + for (i = 0; i < DSAF_COMM_CHN; i++) { + hns_dsaf_int_xge_src_clr(dsaf_dev, i, 0xfffffffful); + hns_dsaf_int_ppe_src_clr(dsaf_dev, i, 0xfffffffful); + hns_dsaf_int_rocee_src_clr(dsaf_dev, i, 0xfffffffful); + + hns_dsaf_int_xge_msk_set(dsaf_dev, i, 0xfffffffful); + hns_dsaf_int_ppe_msk_set(dsaf_dev, i, 0xfffffffful); + hns_dsaf_int_rocee_msk_set(dsaf_dev, i, 0xfffffffful); + } + hns_dsaf_int_tbl_src_clr(dsaf_dev, 0xfffffffful); + hns_dsaf_int_tbl_msk_set(dsaf_dev, 0xfffffffful); +} + +/** + * hns_dsaf_inode_init - INT + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_inode_init(struct dsaf_device *dsaf_dev) +{ + u32 reg; + u32 tc_cfg; + u32 i; + + if (dsaf_dev->dsaf_tc_mode == HRD_DSAF_4TC_MODE) + tc_cfg = HNS_DSAF_I4TC_CFG; + else + tc_cfg = HNS_DSAF_I8TC_CFG; + + for (i = 0; i < DSAF_INODE_NUM; i++) { + reg = DSAF_INODE_IN_PORT_NUM_0_REG + 0x80 * i; + dsaf_set_dev_field(dsaf_dev, reg, DSAF_INODE_IN_PORT_NUM_M, + DSAF_INODE_IN_PORT_NUM_S, i % DSAF_XGE_NUM); + + reg = DSAF_INODE_PRI_TC_CFG_0_REG + 0x80 * i; + dsaf_write_dev(dsaf_dev, reg, tc_cfg); + } +} + +/** + * hns_dsaf_sbm_init - INT + * @dsaf_id: dsa fabric id + */ +static int hns_dsaf_sbm_init(struct dsaf_device *dsaf_dev) +{ + u32 flag; + u32 cnt = 0; + int ret; + + hns_dsaf_sbm_bp_wl_cfg(dsaf_dev); + + /* enable sbm chanel, disable sbm chanel shcut function*/ + hns_dsaf_sbm_cfg(dsaf_dev); + + /* enable sbm mib */ + ret = hns_dsaf_sbm_cfg_mib_en(dsaf_dev); + if (ret) { + dev_err(dsaf_dev->dev, + "hns_dsaf_sbm_cfg_mib_en fail,%s, ret=%d\n", + dsaf_dev->ae_dev.name, ret); + return ret; + } + + /* enable sbm initial link sram */ + hns_dsaf_sbm_link_sram_init_en(dsaf_dev); + + do { + usleep_range(200, 210);/*udelay(200);*/ + flag = dsaf_read_dev(dsaf_dev, DSAF_SRAM_INIT_OVER_0_REG); + cnt++; + } while (flag != DSAF_SRAM_INIT_FINISH_FLAG && cnt < DSAF_CFG_READ_CNT); + + if (flag != DSAF_SRAM_INIT_FINISH_FLAG) { + dev_err(dsaf_dev->dev, + "hns_dsaf_sbm_init fail %s, flag=%d, cnt=%d\n", + dsaf_dev->ae_dev.name, flag, cnt); + return -ENODEV; + } + + hns_dsaf_rocee_bp_en(dsaf_dev); + + return 0; +} + +/** + * hns_dsaf_tbl_init - INT + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_tbl_init(struct dsaf_device *dsaf_dev) +{ + hns_dsaf_tbl_stat_en(dsaf_dev); + + hns_dsaf_tbl_tcam_init(dsaf_dev); + hns_dsaf_tbl_line_init(dsaf_dev); +} + +/** + * hns_dsaf_voq_init - INT + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_voq_init(struct dsaf_device *dsaf_dev) +{ + hns_dsaf_voq_bp_all_thrd_cfg(dsaf_dev); +} + +/** + * hns_dsaf_init_hw - init dsa fabric hardware + * @dsaf_dev: dsa fabric device struct pointer + */ +static int hns_dsaf_init_hw(struct dsaf_device *dsaf_dev) +{ + int ret; + + dev_dbg(dsaf_dev->dev, + "hns_dsaf_init_hw begin %s !\n", dsaf_dev->ae_dev.name); + + hns_dsaf_rst(dsaf_dev, 0); + mdelay(10); + hns_dsaf_rst(dsaf_dev, 1); + + hns_dsaf_comm_init(dsaf_dev); + + /*init XBAR_INODE*/ + hns_dsaf_inode_init(dsaf_dev); + + /*init SBM*/ + ret = hns_dsaf_sbm_init(dsaf_dev); + if (ret) + return ret; + + /*init TBL*/ + hns_dsaf_tbl_init(dsaf_dev); + + /*init VOQ*/ + hns_dsaf_voq_init(dsaf_dev); + + return 0; +} + +/** + * hns_dsaf_remove_hw - uninit dsa fabric hardware + * @dsaf_dev: dsa fabric device struct pointer + */ +static void hns_dsaf_remove_hw(struct dsaf_device *dsaf_dev) +{ + /*reset*/ + hns_dsaf_rst(dsaf_dev, 0); +} + +/** + * hns_dsaf_init - init dsa fabric + * @dsaf_dev: dsa fabric device struct pointer + * retuen 0 - success , negative --fail + */ +static int hns_dsaf_init(struct dsaf_device *dsaf_dev) +{ + struct dsaf_drv_priv *priv = + (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev); + u32 i; + int ret; + + ret = hns_dsaf_init_hw(dsaf_dev); + if (ret) + return ret; + + /* malloc mem for tcam mac key(vlan+mac) */ + priv->soft_mac_tbl = vzalloc(sizeof(*priv->soft_mac_tbl) + * DSAF_TCAM_SUM); + if (!priv->soft_mac_tbl) { + ret = -ENOMEM; + goto remove_hw; + } + + /*all entry invall */ + for (i = 0; i < DSAF_TCAM_SUM; i++) + (priv->soft_mac_tbl + i)->index = DSAF_INVALID_ENTRY_IDX; + + return 0; + +remove_hw: + hns_dsaf_remove_hw(dsaf_dev); + return ret; +} + +/** + * hns_dsaf_free - free dsa fabric + * @dsaf_dev: dsa fabric device struct pointer + */ +static void hns_dsaf_free(struct dsaf_device *dsaf_dev) +{ + struct dsaf_drv_priv *priv = + (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev); + + hns_dsaf_remove_hw(dsaf_dev); + + /* free all mac mem */ + vfree(priv->soft_mac_tbl); + priv->soft_mac_tbl = NULL; +} + +/** + * hns_dsaf_find_soft_mac_entry - find dsa fabric soft entry + * @dsaf_dev: dsa fabric device struct pointer + * @mac_key: mac entry struct pointer + */ +static u16 hns_dsaf_find_soft_mac_entry( + struct dsaf_device *dsaf_dev, + struct dsaf_drv_tbl_tcam_key *mac_key) +{ + struct dsaf_drv_priv *priv = + (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev); + struct dsaf_drv_soft_mac_tbl *soft_mac_entry; + u32 i; + + soft_mac_entry = priv->soft_mac_tbl; + for (i = 0; i < DSAF_TCAM_SUM; i++) { + /* invall tab entry */ + if ((soft_mac_entry->index != DSAF_INVALID_ENTRY_IDX) && + (soft_mac_entry->tcam_key.high.val == mac_key->high.val) && + (soft_mac_entry->tcam_key.low.val == mac_key->low.val)) + /* return find result --soft index */ + return soft_mac_entry->index; + + soft_mac_entry++; + } + return DSAF_INVALID_ENTRY_IDX; +} + +/** + * hns_dsaf_find_empty_mac_entry - search dsa fabric soft empty-entry + * @dsaf_dev: dsa fabric device struct pointer + */ +static u16 hns_dsaf_find_empty_mac_entry(struct dsaf_device *dsaf_dev) +{ + struct dsaf_drv_priv *priv = + (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev); + struct dsaf_drv_soft_mac_tbl *soft_mac_entry; + u32 i; + + soft_mac_entry = priv->soft_mac_tbl; + for (i = 0; i < DSAF_TCAM_SUM; i++) { + /* inv all entry */ + if (soft_mac_entry->index == DSAF_INVALID_ENTRY_IDX) + /* return find result --soft index */ + return i; + + soft_mac_entry++; + } + return DSAF_INVALID_ENTRY_IDX; +} + +/** + * hns_dsaf_set_mac_key - set mac key + * @dsaf_dev: dsa fabric device struct pointer + * @mac_key: tcam key pointer + * @vlan_id: vlan id + * @in_port_num: input port num + * @addr: mac addr + */ +static void hns_dsaf_set_mac_key( + struct dsaf_device *dsaf_dev, + struct dsaf_drv_tbl_tcam_key *mac_key, u16 vlan_id, u8 in_port_num, + u8 *addr) +{ + u8 port; + + if (dsaf_dev->dsaf_mode <= DSAF_MODE_ENABLE) + /*DSAF mode : in port id fixed 0*/ + port = 0; + else + /*non-dsaf mode*/ + port = in_port_num; + + mac_key->high.bits.mac_0 = addr[0]; + mac_key->high.bits.mac_1 = addr[1]; + mac_key->high.bits.mac_2 = addr[2]; + mac_key->high.bits.mac_3 = addr[3]; + mac_key->low.bits.mac_4 = addr[4]; + mac_key->low.bits.mac_5 = addr[5]; + mac_key->low.bits.vlan = vlan_id; + mac_key->low.bits.port = port; +} + +/** + * hns_dsaf_set_mac_uc_entry - set mac uc-entry + * @dsaf_dev: dsa fabric device struct pointer + * @mac_entry: uc-mac entry + */ +int hns_dsaf_set_mac_uc_entry( + struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_single_dest_entry *mac_entry) +{ + u16 entry_index = DSAF_INVALID_ENTRY_IDX; + struct dsaf_drv_tbl_tcam_key mac_key; + struct dsaf_tbl_tcam_ucast_cfg mac_data; + struct dsaf_drv_priv *priv = + (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev); + struct dsaf_drv_soft_mac_tbl *soft_mac_entry = priv->soft_mac_tbl; + + /* mac addr check */ + if (MAC_IS_ALL_ZEROS(mac_entry->addr) || + MAC_IS_BROADCAST(mac_entry->addr) || + MAC_IS_MULTICAST(mac_entry->addr)) { + dev_err(dsaf_dev->dev, + "set_uc %s Mac %02x:%02x:%02x:%02x:%02x:%02x err!\n", + dsaf_dev->ae_dev.name, mac_entry->addr[0], + mac_entry->addr[1], mac_entry->addr[2], + mac_entry->addr[3], mac_entry->addr[4], + mac_entry->addr[5]); + return -EINVAL; + } + + /* config key */ + hns_dsaf_set_mac_key(dsaf_dev, &mac_key, mac_entry->in_vlan_id, + mac_entry->in_port_num, mac_entry->addr); + + /* entry ie exist? */ + entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key); + if (entry_index == DSAF_INVALID_ENTRY_IDX) { + /*if has not inv entry,find a empty entry */ + entry_index = hns_dsaf_find_empty_mac_entry(dsaf_dev); + if (entry_index == DSAF_INVALID_ENTRY_IDX) { + /* has not empty,return error */ + dev_err(dsaf_dev->dev, + "set_uc_entry failed, %s Mac key(%#x:%#x)\n", + dsaf_dev->ae_dev.name, + mac_key.high.val, mac_key.low.val); + return -EINVAL; + } + } + + dev_dbg(dsaf_dev->dev, + "set_uc_entry, %s Mac key(%#x:%#x) entry_index%d\n", + dsaf_dev->ae_dev.name, mac_key.high.val, + mac_key.low.val, entry_index); + + /* config hardware entry */ + mac_data.tbl_ucast_item_vld = 1; + mac_data.tbl_ucast_mac_discard = 0; + mac_data.tbl_ucast_old_en = 0; + /* default config dvc to 0 */ + mac_data.tbl_ucast_dvc = 0; + mac_data.tbl_ucast_out_port = mac_entry->port_num; + hns_dsaf_tcam_uc_cfg( + dsaf_dev, entry_index, + (struct dsaf_tbl_tcam_data *)(&mac_key), &mac_data); + + /* config software entry */ + soft_mac_entry += entry_index; + soft_mac_entry->index = entry_index; + soft_mac_entry->tcam_key.high.val = mac_key.high.val; + soft_mac_entry->tcam_key.low.val = mac_key.low.val; + + return 0; +} + +/** + * hns_dsaf_set_mac_mc_entry - set mac mc-entry + * @dsaf_dev: dsa fabric device struct pointer + * @mac_entry: mc-mac entry + */ +int hns_dsaf_set_mac_mc_entry( + struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_multi_dest_entry *mac_entry) +{ + u16 entry_index = DSAF_INVALID_ENTRY_IDX; + struct dsaf_drv_tbl_tcam_key mac_key; + struct dsaf_tbl_tcam_mcast_cfg mac_data; + struct dsaf_drv_priv *priv = + (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev); + struct dsaf_drv_soft_mac_tbl *soft_mac_entry = priv->soft_mac_tbl; + struct dsaf_drv_tbl_tcam_key tmp_mac_key; + + /* mac addr check */ + if (MAC_IS_ALL_ZEROS(mac_entry->addr)) { + dev_err(dsaf_dev->dev, + "set uc %s Mac %02x:%02x:%02x:%02x:%02x:%02x err!\n", + dsaf_dev->ae_dev.name, mac_entry->addr[0], + mac_entry->addr[1], mac_entry->addr[2], + mac_entry->addr[3], + mac_entry->addr[4], mac_entry->addr[5]); + return -EINVAL; + } + + /*config key */ + hns_dsaf_set_mac_key(dsaf_dev, &mac_key, + mac_entry->in_vlan_id, + mac_entry->in_port_num, mac_entry->addr); + + /* entry ie exist? */ + entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key); + if (entry_index == DSAF_INVALID_ENTRY_IDX) { + /*if hasnot, find enpty entry*/ + entry_index = hns_dsaf_find_empty_mac_entry(dsaf_dev); + if (entry_index == DSAF_INVALID_ENTRY_IDX) { + /*if hasnot empty, error*/ + dev_err(dsaf_dev->dev, + "set_uc_entry failed, %s Mac key(%#x:%#x)\n", + dsaf_dev->ae_dev.name, + mac_key.high.val, mac_key.low.val); + return -EINVAL; + } + + /* config hardware entry */ + memset(mac_data.tbl_mcast_port_msk, + 0, sizeof(mac_data.tbl_mcast_port_msk)); + } else { + /* config hardware entry */ + hns_dsaf_tcam_mc_get( + dsaf_dev, entry_index, + (struct dsaf_tbl_tcam_data *)(&tmp_mac_key), &mac_data); + } + mac_data.tbl_mcast_old_en = 0; + mac_data.tbl_mcast_item_vld = 1; + dsaf_set_field(mac_data.tbl_mcast_port_msk[0], + 0x3F, 0, mac_entry->port_mask[0]); + + dev_dbg(dsaf_dev->dev, + "set_uc_entry, %s key(%#x:%#x) entry_index%d\n", + dsaf_dev->ae_dev.name, mac_key.high.val, + mac_key.low.val, entry_index); + + hns_dsaf_tcam_mc_cfg( + dsaf_dev, entry_index, + (struct dsaf_tbl_tcam_data *)(&mac_key), &mac_data); + + /* config software entry */ + soft_mac_entry += entry_index; + soft_mac_entry->index = entry_index; + soft_mac_entry->tcam_key.high.val = mac_key.high.val; + soft_mac_entry->tcam_key.low.val = mac_key.low.val; + + return 0; +} + +/** + * hns_dsaf_add_mac_mc_port - add mac mc-port + * @dsaf_dev: dsa fabric device struct pointer + * @mac_entry: mc-mac entry + */ +int hns_dsaf_add_mac_mc_port(struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_single_dest_entry *mac_entry) +{ + u16 entry_index = DSAF_INVALID_ENTRY_IDX; + struct dsaf_drv_tbl_tcam_key mac_key; + struct dsaf_tbl_tcam_mcast_cfg mac_data; + struct dsaf_drv_priv *priv = + (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev); + struct dsaf_drv_soft_mac_tbl *soft_mac_entry = priv->soft_mac_tbl; + struct dsaf_drv_tbl_tcam_key tmp_mac_key; + int mskid; + + /*chechk mac addr */ + if (MAC_IS_ALL_ZEROS(mac_entry->addr)) { + dev_err(dsaf_dev->dev, + "set_entry failed,addr %02x:%02x:%02x:%02x:%02x:%02x!\n", + mac_entry->addr[0], mac_entry->addr[1], + mac_entry->addr[2], mac_entry->addr[3], + mac_entry->addr[4], mac_entry->addr[5]); + return -EINVAL; + } + + /*config key */ + hns_dsaf_set_mac_key( + dsaf_dev, &mac_key, mac_entry->in_vlan_id, + mac_entry->in_port_num, mac_entry->addr); + + memset(&mac_data, 0, sizeof(struct dsaf_tbl_tcam_mcast_cfg)); + + /*check exist? */ + entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key); + if (entry_index == DSAF_INVALID_ENTRY_IDX) { + /*if hasnot , find a empty*/ + entry_index = hns_dsaf_find_empty_mac_entry(dsaf_dev); + if (entry_index == DSAF_INVALID_ENTRY_IDX) { + /*if hasnot empty, error*/ + dev_err(dsaf_dev->dev, + "set_uc_entry failed, %s Mac key(%#x:%#x)\n", + dsaf_dev->ae_dev.name, mac_key.high.val, + mac_key.low.val); + return -EINVAL; + } + } else { + /*if exist, add in */ + hns_dsaf_tcam_mc_get( + dsaf_dev, entry_index, + (struct dsaf_tbl_tcam_data *)(&tmp_mac_key), &mac_data); + } + /* config hardware entry */ + if (mac_entry->port_num < DSAF_SERVICE_NW_NUM) { + mskid = mac_entry->port_num; + } else if (mac_entry->port_num >= DSAF_BASE_INNER_PORT_NUM) { + mskid = mac_entry->port_num - + DSAF_BASE_INNER_PORT_NUM + DSAF_SERVICE_NW_NUM; + } else { + dev_err(dsaf_dev->dev, + "%s,pnum(%d)error,key(%#x:%#x)\n", + dsaf_dev->ae_dev.name, mac_entry->port_num, + mac_key.high.val, mac_key.low.val); + return -EINVAL; + } + dsaf_set_bit(mac_data.tbl_mcast_port_msk[mskid / 32], mskid % 32, 1); + mac_data.tbl_mcast_old_en = 0; + mac_data.tbl_mcast_item_vld = 1; + + dev_dbg(dsaf_dev->dev, + "set_uc_entry, %s Mac key(%#x:%#x) entry_index%d\n", + dsaf_dev->ae_dev.name, mac_key.high.val, + mac_key.low.val, entry_index); + + hns_dsaf_tcam_mc_cfg( + dsaf_dev, entry_index, + (struct dsaf_tbl_tcam_data *)(&mac_key), &mac_data); + + /*config software entry */ + soft_mac_entry += entry_index; + soft_mac_entry->index = entry_index; + soft_mac_entry->tcam_key.high.val = mac_key.high.val; + soft_mac_entry->tcam_key.low.val = mac_key.low.val; + + return 0; +} + +/** + * hns_dsaf_del_mac_entry - del mac mc-port + * @dsaf_dev: dsa fabric device struct pointer + * @vlan_id: vlian id + * @in_port_num: input port num + * @addr : mac addr + */ +int hns_dsaf_del_mac_entry(struct dsaf_device *dsaf_dev, u16 vlan_id, + u8 in_port_num, u8 *addr) +{ + u16 entry_index = DSAF_INVALID_ENTRY_IDX; + struct dsaf_drv_tbl_tcam_key mac_key; + struct dsaf_drv_priv *priv = + (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev); + struct dsaf_drv_soft_mac_tbl *soft_mac_entry = priv->soft_mac_tbl; + + /*check mac addr */ + if (MAC_IS_ALL_ZEROS(addr) || MAC_IS_BROADCAST(addr)) { + dev_err(dsaf_dev->dev, + "del_entry failed,addr %02x:%02x:%02x:%02x:%02x:%02x!\n", + addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); + return -EINVAL; + } + + /*config key */ + hns_dsaf_set_mac_key(dsaf_dev, &mac_key, vlan_id, in_port_num, addr); + + /*exist ?*/ + entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key); + if (entry_index == DSAF_INVALID_ENTRY_IDX) { + /*not exist, error */ + dev_err(dsaf_dev->dev, + "del_mac_entry failed, %s Mac key(%#x:%#x)\n", + dsaf_dev->ae_dev.name, + mac_key.high.val, mac_key.low.val); + return -EINVAL; + } + dev_dbg(dsaf_dev->dev, + "del_mac_entry, %s Mac key(%#x:%#x) entry_index%d\n", + dsaf_dev->ae_dev.name, mac_key.high.val, + mac_key.low.val, entry_index); + + /*do del opt*/ + hns_dsaf_tcam_mc_invld(dsaf_dev, entry_index); + + /*del soft emtry */ + soft_mac_entry += entry_index; + soft_mac_entry->index = DSAF_INVALID_ENTRY_IDX; + + return 0; +} + +/** + * hns_dsaf_del_mac_mc_port - del mac mc- port + * @dsaf_dev: dsa fabric device struct pointer + * @mac_entry: mac entry + */ +int hns_dsaf_del_mac_mc_port(struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_single_dest_entry *mac_entry) +{ + u16 entry_index = DSAF_INVALID_ENTRY_IDX; + struct dsaf_drv_tbl_tcam_key mac_key; + struct dsaf_drv_priv *priv = + (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev); + struct dsaf_drv_soft_mac_tbl *soft_mac_entry = priv->soft_mac_tbl; + u16 vlan_id; + u8 in_port_num; + struct dsaf_tbl_tcam_mcast_cfg mac_data; + struct dsaf_drv_tbl_tcam_key tmp_mac_key; + int mskid; + const u8 empty_msk[sizeof(mac_data.tbl_mcast_port_msk)] = {0}; + + if (!(void *)mac_entry) { + dev_err(dsaf_dev->dev, + "hns_dsaf_del_mac_mc_port mac_entry is NULL\n"); + return -EINVAL; + } + + /*get key info*/ + vlan_id = mac_entry->in_vlan_id; + in_port_num = mac_entry->in_port_num; + + /*check mac addr */ + if (MAC_IS_ALL_ZEROS(mac_entry->addr)) { + dev_err(dsaf_dev->dev, + "del_port failed, addr %02x:%02x:%02x:%02x:%02x:%02x!\n", + mac_entry->addr[0], mac_entry->addr[1], + mac_entry->addr[2], mac_entry->addr[3], + mac_entry->addr[4], mac_entry->addr[5]); + return -EINVAL; + } + + /*config key */ + hns_dsaf_set_mac_key(dsaf_dev, &mac_key, vlan_id, in_port_num, + mac_entry->addr); + + /*check is exist? */ + entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key); + if (entry_index == DSAF_INVALID_ENTRY_IDX) { + /*find none */ + dev_err(dsaf_dev->dev, + "find_soft_mac_entry failed, %s Mac key(%#x:%#x)\n", + dsaf_dev->ae_dev.name, + mac_key.high.val, mac_key.low.val); + return -EINVAL; + } + + dev_dbg(dsaf_dev->dev, + "del_mac_mc_port, %s key(%#x:%#x) index%d\n", + dsaf_dev->ae_dev.name, mac_key.high.val, + mac_key.low.val, entry_index); + + /*read entry*/ + hns_dsaf_tcam_mc_get( + dsaf_dev, entry_index, + (struct dsaf_tbl_tcam_data *)(&tmp_mac_key), &mac_data); + + /*del the port*/ + if (mac_entry->port_num < DSAF_SERVICE_NW_NUM) { + mskid = mac_entry->port_num; + } else if (mac_entry->port_num >= DSAF_BASE_INNER_PORT_NUM) { + mskid = mac_entry->port_num - + DSAF_BASE_INNER_PORT_NUM + DSAF_SERVICE_NW_NUM; + } else { + dev_err(dsaf_dev->dev, + "%s,pnum(%d)error,key(%#x:%#x)\n", + dsaf_dev->ae_dev.name, mac_entry->port_num, + mac_key.high.val, mac_key.low.val); + return -EINVAL; + } + dsaf_set_bit(mac_data.tbl_mcast_port_msk[mskid / 32], mskid % 32, 0); + + /*check non port, do del entry */ + if (!memcmp(mac_data.tbl_mcast_port_msk, empty_msk, + sizeof(mac_data.tbl_mcast_port_msk))) { + hns_dsaf_tcam_mc_invld(dsaf_dev, entry_index); + + /* del soft entry */ + soft_mac_entry += entry_index; + soft_mac_entry->index = DSAF_INVALID_ENTRY_IDX; + } else { /* not zer, just del port, updata*/ + hns_dsaf_tcam_mc_cfg( + dsaf_dev, entry_index, + (struct dsaf_tbl_tcam_data *)(&mac_key), &mac_data); + } + + return 0; +} + +/** + * hns_dsaf_get_mac_uc_entry - get mac uc entry + * @dsaf_dev: dsa fabric device struct pointer + * @mac_entry: mac entry + */ +int hns_dsaf_get_mac_uc_entry(struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_single_dest_entry *mac_entry) +{ + u16 entry_index = DSAF_INVALID_ENTRY_IDX; + struct dsaf_drv_tbl_tcam_key mac_key; + + struct dsaf_tbl_tcam_ucast_cfg mac_data; + + /* check macaddr */ + if (MAC_IS_ALL_ZEROS(mac_entry->addr) || + MAC_IS_BROADCAST(mac_entry->addr)) { + dev_err(dsaf_dev->dev, + "get_entry failed,addr %02x:%02x:%02x:%02x:%02x:%02x\n", + mac_entry->addr[0], mac_entry->addr[1], + mac_entry->addr[2], mac_entry->addr[3], + mac_entry->addr[4], mac_entry->addr[5]); + return -EINVAL; + } + + /*config key */ + hns_dsaf_set_mac_key(dsaf_dev, &mac_key, mac_entry->in_vlan_id, + mac_entry->in_port_num, mac_entry->addr); + + /*check exist? */ + entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key); + if (entry_index == DSAF_INVALID_ENTRY_IDX) { + /*find none, error */ + dev_err(dsaf_dev->dev, + "get_uc_entry failed, %s Mac key(%#x:%#x)\n", + dsaf_dev->ae_dev.name, + mac_key.high.val, mac_key.low.val); + return -EINVAL; + } + dev_dbg(dsaf_dev->dev, + "get_uc_entry, %s Mac key(%#x:%#x) entry_index%d\n", + dsaf_dev->ae_dev.name, mac_key.high.val, + mac_key.low.val, entry_index); + + /*read entry*/ + hns_dsaf_tcam_uc_get(dsaf_dev, entry_index, + (struct dsaf_tbl_tcam_data *)&mac_key, &mac_data); + mac_entry->port_num = mac_data.tbl_ucast_out_port; + + return 0; +} + +/** + * hns_dsaf_get_mac_mc_entry - get mac mc entry + * @dsaf_dev: dsa fabric device struct pointer + * @mac_entry: mac entry + */ +int hns_dsaf_get_mac_mc_entry(struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_multi_dest_entry *mac_entry) +{ + u16 entry_index = DSAF_INVALID_ENTRY_IDX; + struct dsaf_drv_tbl_tcam_key mac_key; + + struct dsaf_tbl_tcam_mcast_cfg mac_data; + + /*check mac addr */ + if (MAC_IS_ALL_ZEROS(mac_entry->addr) || + MAC_IS_BROADCAST(mac_entry->addr)) { + dev_err(dsaf_dev->dev, + "get_entry failed,addr %02x:%02x:%02x:%02x:%02x:%02x\n", + mac_entry->addr[0], mac_entry->addr[1], + mac_entry->addr[2], mac_entry->addr[3], + mac_entry->addr[4], mac_entry->addr[5]); + return -EINVAL; + } + + /*config key */ + hns_dsaf_set_mac_key(dsaf_dev, &mac_key, mac_entry->in_vlan_id, + mac_entry->in_port_num, mac_entry->addr); + + /*check exist? */ + entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key); + if (entry_index == DSAF_INVALID_ENTRY_IDX) { + /* find none, error */ + dev_err(dsaf_dev->dev, + "get_mac_uc_entry failed, %s Mac key(%#x:%#x)\n", + dsaf_dev->ae_dev.name, mac_key.high.val, + mac_key.low.val); + return -EINVAL; + } + dev_dbg(dsaf_dev->dev, + "get_mac_uc_entry, %s Mac key(%#x:%#x) entry_index%d\n", + dsaf_dev->ae_dev.name, mac_key.high.val, + mac_key.low.val, entry_index); + + /*read entry */ + hns_dsaf_tcam_mc_get(dsaf_dev, entry_index, + (struct dsaf_tbl_tcam_data *)&mac_key, &mac_data); + + mac_entry->port_mask[0] = mac_data.tbl_mcast_port_msk[0] & 0x3F; + return 0; +} + +/** + * hns_dsaf_get_mac_entry_by_index - get mac entry by tab index + * @dsaf_dev: dsa fabric device struct pointer + * @entry_index: tab entry index + * @mac_entry: mac entry + */ +int hns_dsaf_get_mac_entry_by_index( + struct dsaf_device *dsaf_dev, + u16 entry_index, struct dsaf_drv_mac_multi_dest_entry *mac_entry) +{ + struct dsaf_drv_tbl_tcam_key mac_key; + + struct dsaf_tbl_tcam_mcast_cfg mac_data; + struct dsaf_tbl_tcam_ucast_cfg mac_uc_data; + char mac_addr[MAC_NUM_OCTETS_PER_ADDR] = {0}; + + if (entry_index >= DSAF_TCAM_SUM) { + /* find none, del error */ + dev_err(dsaf_dev->dev, "get_uc_entry failed, %s\n", + dsaf_dev->ae_dev.name); + return -EINVAL; + } + + /* mc entry, do read opt */ + hns_dsaf_tcam_mc_get(dsaf_dev, entry_index, + (struct dsaf_tbl_tcam_data *)&mac_key, &mac_data); + + mac_entry->port_mask[0] = mac_data.tbl_mcast_port_msk[0] & 0x3F; + + /***get mac addr*/ + mac_addr[0] = mac_key.high.bits.mac_0; + mac_addr[1] = mac_key.high.bits.mac_1; + mac_addr[2] = mac_key.high.bits.mac_2; + mac_addr[3] = mac_key.high.bits.mac_3; + mac_addr[4] = mac_key.low.bits.mac_4; + mac_addr[5] = mac_key.low.bits.mac_5; + /**is mc or uc*/ + if (MAC_IS_MULTICAST((u8 *)mac_addr) || + MAC_IS_L3_MULTICAST((u8 *)mac_addr)) { + /**mc donot do*/ + } else { + /*is not mc, just uc... */ + hns_dsaf_tcam_uc_get(dsaf_dev, entry_index, + (struct dsaf_tbl_tcam_data *)&mac_key, + &mac_uc_data); + mac_entry->port_mask[0] = (1 << mac_uc_data.tbl_ucast_out_port); + } + + return 0; +} + +static struct dsaf_device *hns_dsaf_alloc_dev(struct device *dev, + size_t sizeof_priv) +{ + struct dsaf_device *dsaf_dev; + + dsaf_dev = devm_kzalloc(dev, + sizeof(*dsaf_dev) + sizeof_priv, GFP_KERNEL); + if (unlikely(!dsaf_dev)) { + dsaf_dev = ERR_PTR(-ENOMEM); + } else { + dsaf_dev->dev = dev; + dev_set_drvdata(dev, dsaf_dev); + } + + return dsaf_dev; +} + +/** + * hns_dsaf_free_dev - free dev mem + * @dev: struct device pointer + */ +static void hns_dsaf_free_dev(struct dsaf_device *dsaf_dev) +{ + (void)dev_set_drvdata(dsaf_dev->dev, NULL); +} + +/** + * dsaf_pfc_unit_cnt - set pfc unit count + * @dsaf_id: dsa fabric id + * @pport_rate: value array + * @pdsaf_pfc_unit_cnt: value array + */ +static void hns_dsaf_pfc_unit_cnt(struct dsaf_device *dsaf_dev, int mac_id, + enum dsaf_port_rate_mode rate) +{ + u32 unit_cnt; + + switch (rate) { + case DSAF_PORT_RATE_10000: + unit_cnt = HNS_DSAF_PFC_UNIT_CNT_FOR_XGE; + break; + case DSAF_PORT_RATE_1000: + unit_cnt = HNS_DSAF_PFC_UNIT_CNT_FOR_GE_1000; + break; + case DSAF_PORT_RATE_2500: + unit_cnt = HNS_DSAF_PFC_UNIT_CNT_FOR_GE_1000; + break; + default: + unit_cnt = HNS_DSAF_PFC_UNIT_CNT_FOR_XGE; + } + + dsaf_set_dev_field(dsaf_dev, + (DSAF_PFC_UNIT_CNT_0_REG + 0x4 * (u64)mac_id), + DSAF_PFC_UNINT_CNT_M, DSAF_PFC_UNINT_CNT_S, + unit_cnt); +} + +/** + * dsaf_port_work_rate_cfg - fifo + * @dsaf_id: dsa fabric id + * @xge_ge_work_mode + */ +void hns_dsaf_port_work_rate_cfg(struct dsaf_device *dsaf_dev, int mac_id, + enum dsaf_port_rate_mode rate_mode) +{ + u32 port_work_mode; + + port_work_mode = dsaf_read_dev( + dsaf_dev, DSAF_XGE_GE_WORK_MODE_0_REG + 0x4 * (u64)mac_id); + + if (rate_mode == DSAF_PORT_RATE_10000) + dsaf_set_bit(port_work_mode, DSAF_XGE_GE_WORK_MODE_S, 1); + else + dsaf_set_bit(port_work_mode, DSAF_XGE_GE_WORK_MODE_S, 0); + + dsaf_write_dev(dsaf_dev, + DSAF_XGE_GE_WORK_MODE_0_REG + 0x4 * (u64)mac_id, + port_work_mode); + + hns_dsaf_pfc_unit_cnt(dsaf_dev, mac_id, rate_mode); +} + +/** + * hns_dsaf_fix_mac_mode - dsaf modify mac mode + * @mac_cb: mac contrl block + */ +void hns_dsaf_fix_mac_mode(struct hns_mac_cb *mac_cb) +{ + enum dsaf_port_rate_mode mode; + struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev; + int mac_id = mac_cb->mac_id; + + if (mac_cb->mac_type != HNAE_PORT_SERVICE) + return; + if (mac_cb->phy_if == PHY_INTERFACE_MODE_XGMII) + mode = DSAF_PORT_RATE_10000; + else + mode = DSAF_PORT_RATE_1000; + + hns_dsaf_port_work_rate_cfg(dsaf_dev, mac_id, mode); +} + +void hns_dsaf_update_stats(struct dsaf_device *dsaf_dev, u32 node_num) +{ + struct dsaf_hw_stats *hw_stats + = &dsaf_dev->hw_stats[node_num]; + + hw_stats->pad_drop += dsaf_read_dev(dsaf_dev, + DSAF_INODE_PAD_DISCARD_NUM_0_REG + 0x80 * (u64)node_num); + hw_stats->man_pkts += dsaf_read_dev(dsaf_dev, + DSAF_INODE_FINAL_IN_MAN_NUM_0_REG + 0x80 * (u64)node_num); + hw_stats->rx_pkts += dsaf_read_dev(dsaf_dev, + DSAF_INODE_FINAL_IN_PKT_NUM_0_REG + 0x80 * (u64)node_num); + hw_stats->rx_pkt_id += dsaf_read_dev(dsaf_dev, + DSAF_INODE_SBM_PID_NUM_0_REG + 0x80 * (u64)node_num); + hw_stats->rx_pause_frame += dsaf_read_dev(dsaf_dev, + DSAF_INODE_FINAL_IN_PAUSE_NUM_0_REG + 0x80 * (u64)node_num); + hw_stats->release_buf_num += dsaf_read_dev(dsaf_dev, + DSAF_INODE_SBM_RELS_NUM_0_REG + 0x80 * (u64)node_num); + hw_stats->sbm_drop += dsaf_read_dev(dsaf_dev, + DSAF_INODE_SBM_DROP_NUM_0_REG + 0x80 * (u64)node_num); + hw_stats->crc_false += dsaf_read_dev(dsaf_dev, + DSAF_INODE_CRC_FALSE_NUM_0_REG + 0x80 * (u64)node_num); + hw_stats->bp_drop += dsaf_read_dev(dsaf_dev, + DSAF_INODE_BP_DISCARD_NUM_0_REG + 0x80 * (u64)node_num); + hw_stats->rslt_drop += dsaf_read_dev(dsaf_dev, + DSAF_INODE_RSLT_DISCARD_NUM_0_REG + 0x80 * (u64)node_num); + hw_stats->local_addr_false += dsaf_read_dev(dsaf_dev, + DSAF_INODE_LOCAL_ADDR_FALSE_NUM_0_REG + 0x80 * (u64)node_num); + + hw_stats->vlan_drop += dsaf_read_dev(dsaf_dev, + DSAF_INODE_SW_VLAN_TAG_DISC_0_REG + 0x80 * (u64)node_num); + hw_stats->stp_drop += dsaf_read_dev(dsaf_dev, + DSAF_INODE_IN_DATA_STP_DISC_0_REG + 0x80 * (u64)node_num); + + hw_stats->tx_pkts += dsaf_read_dev(dsaf_dev, + DSAF_XOD_RCVPKT_CNT_0_REG + 0x90 * (u64)node_num); +} + +/** + *hns_dsaf_get_regs - dump dsaf regs + *@dsaf_dev: dsaf device + *@data:data for value of regs + */ +void hns_dsaf_get_regs(struct dsaf_device *ddev, u32 port, void *data) +{ + u32 i = 0; + u32 j; + u32 *p = data; + + /* dsaf common registers */ + p[0] = dsaf_read_dev(ddev, DSAF_SRAM_INIT_OVER_0_REG); + p[1] = dsaf_read_dev(ddev, DSAF_CFG_0_REG); + p[2] = dsaf_read_dev(ddev, DSAF_ECC_ERR_INVERT_0_REG); + p[3] = dsaf_read_dev(ddev, DSAF_ABNORMAL_TIMEOUT_0_REG); + p[4] = dsaf_read_dev(ddev, DSAF_FSM_TIMEOUT_0_REG); + p[5] = dsaf_read_dev(ddev, DSAF_DSA_REG_CNT_CLR_CE_REG); + p[6] = dsaf_read_dev(ddev, DSAF_DSA_SBM_INF_FIFO_THRD_REG); + p[7] = dsaf_read_dev(ddev, DSAF_DSA_SRAM_1BIT_ECC_SEL_REG); + p[8] = dsaf_read_dev(ddev, DSAF_DSA_SRAM_1BIT_ECC_CNT_REG); + + p[9] = dsaf_read_dev(ddev, DSAF_PFC_EN_0_REG + port * 4); + p[10] = dsaf_read_dev(ddev, DSAF_PFC_UNIT_CNT_0_REG + port * 4); + p[11] = dsaf_read_dev(ddev, DSAF_XGE_INT_MSK_0_REG + port * 4); + p[12] = dsaf_read_dev(ddev, DSAF_XGE_INT_SRC_0_REG + port * 4); + p[13] = dsaf_read_dev(ddev, DSAF_XGE_INT_STS_0_REG + port * 4); + p[14] = dsaf_read_dev(ddev, DSAF_XGE_INT_MSK_0_REG + port * 4); + p[15] = dsaf_read_dev(ddev, DSAF_PPE_INT_MSK_0_REG + port * 4); + p[16] = dsaf_read_dev(ddev, DSAF_ROCEE_INT_MSK_0_REG + port * 4); + p[17] = dsaf_read_dev(ddev, DSAF_XGE_INT_SRC_0_REG + port * 4); + p[18] = dsaf_read_dev(ddev, DSAF_PPE_INT_SRC_0_REG + port * 4); + p[19] = dsaf_read_dev(ddev, DSAF_ROCEE_INT_SRC_0_REG + port * 4); + p[20] = dsaf_read_dev(ddev, DSAF_XGE_INT_STS_0_REG + port * 4); + p[21] = dsaf_read_dev(ddev, DSAF_PPE_INT_STS_0_REG + port * 4); + p[22] = dsaf_read_dev(ddev, DSAF_ROCEE_INT_STS_0_REG + port * 4); + p[23] = dsaf_read_dev(ddev, DSAF_PPE_QID_CFG_0_REG + port * 4); + + for (i = 0; i < DSAF_SW_PORT_NUM; i++) + p[24 + i] = dsaf_read_dev(ddev, + DSAF_SW_PORT_TYPE_0_REG + i * 4); + + p[32] = dsaf_read_dev(ddev, DSAF_MIX_DEF_QID_0_REG + port * 4); + + for (i = 0; i < DSAF_SW_PORT_NUM; i++) + p[33 + i] = dsaf_read_dev(ddev, + DSAF_PORT_DEF_VLAN_0_REG + i * 4); + + for (i = 0; i < DSAF_TOTAL_QUEUE_NUM; i++) + p[41 + i] = dsaf_read_dev(ddev, + DSAF_VM_DEF_VLAN_0_REG + i * 4); + + /* dsaf inode registers */ + p[170] = dsaf_read_dev(ddev, DSAF_INODE_CUT_THROUGH_CFG_0_REG); + + p[171] = dsaf_read_dev(ddev, + DSAF_INODE_ECC_ERR_ADDR_0_REG + port * 0x80); + + for (i = 0; i < DSAF_INODE_NUM / DSAF_COMM_CHN; i++) { + j = i * DSAF_COMM_CHN + port; + p[172 + i] = dsaf_read_dev(ddev, + DSAF_INODE_IN_PORT_NUM_0_REG + j * 0x80); + p[175 + i] = dsaf_read_dev(ddev, + DSAF_INODE_PRI_TC_CFG_0_REG + j * 0x80); + p[178 + i] = dsaf_read_dev(ddev, + DSAF_INODE_BP_STATUS_0_REG + j * 0x80); + p[181 + i] = dsaf_read_dev(ddev, + DSAF_INODE_PAD_DISCARD_NUM_0_REG + j * 0x80); + p[184 + i] = dsaf_read_dev(ddev, + DSAF_INODE_FINAL_IN_MAN_NUM_0_REG + j * 0x80); + p[187 + i] = dsaf_read_dev(ddev, + DSAF_INODE_FINAL_IN_PKT_NUM_0_REG + j * 0x80); + p[190 + i] = dsaf_read_dev(ddev, + DSAF_INODE_SBM_PID_NUM_0_REG + j * 0x80); + p[193 + i] = dsaf_read_dev(ddev, + DSAF_INODE_FINAL_IN_PAUSE_NUM_0_REG + j * 0x80); + p[196 + i] = dsaf_read_dev(ddev, + DSAF_INODE_SBM_RELS_NUM_0_REG + j * 0x80); + p[199 + i] = dsaf_read_dev(ddev, + DSAF_INODE_SBM_DROP_NUM_0_REG + j * 0x80); + p[202 + i] = dsaf_read_dev(ddev, + DSAF_INODE_CRC_FALSE_NUM_0_REG + j * 0x80); + p[205 + i] = dsaf_read_dev(ddev, + DSAF_INODE_BP_DISCARD_NUM_0_REG + j * 0x80); + p[208 + i] = dsaf_read_dev(ddev, + DSAF_INODE_RSLT_DISCARD_NUM_0_REG + j * 0x80); + p[211 + i] = dsaf_read_dev(ddev, + DSAF_INODE_LOCAL_ADDR_FALSE_NUM_0_REG + j * 0x80); + p[214 + i] = dsaf_read_dev(ddev, + DSAF_INODE_VOQ_OVER_NUM_0_REG + j * 0x80); + p[217 + i] = dsaf_read_dev(ddev, + DSAF_INODE_BD_SAVE_STATUS_0_REG + j * 4); + p[220 + i] = dsaf_read_dev(ddev, + DSAF_INODE_BD_ORDER_STATUS_0_REG + j * 4); + p[223 + i] = dsaf_read_dev(ddev, + DSAF_INODE_SW_VLAN_TAG_DISC_0_REG + j * 4); + p[224 + i] = dsaf_read_dev(ddev, + DSAF_INODE_IN_DATA_STP_DISC_0_REG + j * 4); + } + + p[227] = dsaf_read_dev(ddev, DSAF_INODE_GE_FC_EN_0_REG + port * 4); + + for (i = 0; i < DSAF_INODE_NUM / DSAF_COMM_CHN; i++) { + j = i * DSAF_COMM_CHN + port; + p[228 + i] = dsaf_read_dev(ddev, + DSAF_INODE_VC0_IN_PKT_NUM_0_REG + j * 4); + } + + p[231] = dsaf_read_dev(ddev, + DSAF_INODE_VC1_IN_PKT_NUM_0_REG + port * 4); + + /* dsaf inode registers */ + for (i = 0; i < DSAF_SBM_NUM / DSAF_COMM_CHN; i++) { + j = i * DSAF_COMM_CHN + port; + p[232 + i] = dsaf_read_dev(ddev, + DSAF_SBM_CFG_REG_0_REG + j * 0x80); + p[235 + i] = dsaf_read_dev(ddev, + DSAF_SBM_BP_CFG_0_XGE_REG_0_REG + j * 0x80); + p[238 + i] = dsaf_read_dev(ddev, + DSAF_SBM_BP_CFG_1_REG_0_REG + j * 0x80); + p[241 + i] = dsaf_read_dev(ddev, + DSAF_SBM_BP_CFG_2_XGE_REG_0_REG + j * 0x80); + p[244 + i] = dsaf_read_dev(ddev, + DSAF_SBM_FREE_CNT_0_0_REG + j * 0x80); + p[245 + i] = dsaf_read_dev(ddev, + DSAF_SBM_FREE_CNT_1_0_REG + j * 0x80); + p[248 + i] = dsaf_read_dev(ddev, + DSAF_SBM_BP_CNT_0_0_REG + j * 0x80); + p[251 + i] = dsaf_read_dev(ddev, + DSAF_SBM_BP_CNT_1_0_REG + j * 0x80); + p[254 + i] = dsaf_read_dev(ddev, + DSAF_SBM_BP_CNT_2_0_REG + j * 0x80); + p[257 + i] = dsaf_read_dev(ddev, + DSAF_SBM_BP_CNT_3_0_REG + j * 0x80); + p[260 + i] = dsaf_read_dev(ddev, + DSAF_SBM_INER_ST_0_REG + j * 0x80); + p[263 + i] = dsaf_read_dev(ddev, + DSAF_SBM_MIB_REQ_FAILED_TC_0_REG + j * 0x80); + p[266 + i] = dsaf_read_dev(ddev, + DSAF_SBM_LNK_INPORT_CNT_0_REG + j * 0x80); + p[269 + i] = dsaf_read_dev(ddev, + DSAF_SBM_LNK_DROP_CNT_0_REG + j * 0x80); + p[272 + i] = dsaf_read_dev(ddev, + DSAF_SBM_INF_OUTPORT_CNT_0_REG + j * 0x80); + p[275 + i] = dsaf_read_dev(ddev, + DSAF_SBM_LNK_INPORT_TC0_CNT_0_REG + j * 0x80); + p[278 + i] = dsaf_read_dev(ddev, + DSAF_SBM_LNK_INPORT_TC1_CNT_0_REG + j * 0x80); + p[281 + i] = dsaf_read_dev(ddev, + DSAF_SBM_LNK_INPORT_TC2_CNT_0_REG + j * 0x80); + p[284 + i] = dsaf_read_dev(ddev, + DSAF_SBM_LNK_INPORT_TC3_CNT_0_REG + j * 0x80); + p[287 + i] = dsaf_read_dev(ddev, + DSAF_SBM_LNK_INPORT_TC4_CNT_0_REG + j * 0x80); + p[290 + i] = dsaf_read_dev(ddev, + DSAF_SBM_LNK_INPORT_TC5_CNT_0_REG + j * 0x80); + p[293 + i] = dsaf_read_dev(ddev, + DSAF_SBM_LNK_INPORT_TC6_CNT_0_REG + j * 0x80); + p[296 + i] = dsaf_read_dev(ddev, + DSAF_SBM_LNK_INPORT_TC7_CNT_0_REG + j * 0x80); + p[299 + i] = dsaf_read_dev(ddev, + DSAF_SBM_LNK_REQ_CNT_0_REG + j * 0x80); + p[302 + i] = dsaf_read_dev(ddev, + DSAF_SBM_LNK_RELS_CNT_0_REG + j * 0x80); + p[305 + i] = dsaf_read_dev(ddev, + DSAF_SBM_BP_CFG_3_REG_0_REG + j * 0x80); + p[308 + i] = dsaf_read_dev(ddev, + DSAF_SBM_BP_CFG_4_REG_0_REG + j * 0x80); + } + + /* dsaf onode registers */ + for (i = 0; i < DSAF_XOD_NUM; i++) { + p[311 + i] = dsaf_read_dev(ddev, + DSAF_XOD_ETS_TSA_TC0_TC3_CFG_0_REG + j * 0x90); + p[319 + i] = dsaf_read_dev(ddev, + DSAF_XOD_ETS_TSA_TC4_TC7_CFG_0_REG + j * 0x90); + p[327 + i] = dsaf_read_dev(ddev, + DSAF_XOD_ETS_BW_TC0_TC3_CFG_0_REG + j * 0x90); + p[335 + i] = dsaf_read_dev(ddev, + DSAF_XOD_ETS_BW_TC4_TC7_CFG_0_REG + j * 0x90); + p[343 + i] = dsaf_read_dev(ddev, + DSAF_XOD_ETS_BW_OFFSET_CFG_0_REG + j * 0x90); + p[351 + i] = dsaf_read_dev(ddev, + DSAF_XOD_ETS_TOKEN_CFG_0_REG + j * 0x90); + } + + p[359] = dsaf_read_dev(ddev, DSAF_XOD_PFS_CFG_0_0_REG + port * 0x90); + p[360] = dsaf_read_dev(ddev, DSAF_XOD_PFS_CFG_1_0_REG + port * 0x90); + p[361] = dsaf_read_dev(ddev, DSAF_XOD_PFS_CFG_2_0_REG + port * 0x90); + + for (i = 0; i < DSAF_XOD_BIG_NUM / DSAF_COMM_CHN; i++) { + j = i * DSAF_COMM_CHN + port; + p[362 + i] = dsaf_read_dev(ddev, + DSAF_XOD_GNT_L_0_REG + j * 0x90); + p[365 + i] = dsaf_read_dev(ddev, + DSAF_XOD_GNT_H_0_REG + j * 0x90); + p[368 + i] = dsaf_read_dev(ddev, + DSAF_XOD_CONNECT_STATE_0_REG + j * 0x90); + p[371 + i] = dsaf_read_dev(ddev, + DSAF_XOD_RCVPKT_CNT_0_REG + j * 0x90); + p[374 + i] = dsaf_read_dev(ddev, + DSAF_XOD_RCVTC0_CNT_0_REG + j * 0x90); + p[377 + i] = dsaf_read_dev(ddev, + DSAF_XOD_RCVTC1_CNT_0_REG + j * 0x90); + p[380 + i] = dsaf_read_dev(ddev, + DSAF_XOD_RCVTC2_CNT_0_REG + j * 0x90); + p[383 + i] = dsaf_read_dev(ddev, + DSAF_XOD_RCVTC3_CNT_0_REG + j * 0x90); + p[386 + i] = dsaf_read_dev(ddev, + DSAF_XOD_RCVVC0_CNT_0_REG + j * 0x90); + p[389 + i] = dsaf_read_dev(ddev, + DSAF_XOD_RCVVC1_CNT_0_REG + j * 0x90); + } + + p[392] = dsaf_read_dev(ddev, + DSAF_XOD_XGE_RCVIN0_CNT_0_REG + port * 0x90); + p[393] = dsaf_read_dev(ddev, + DSAF_XOD_XGE_RCVIN1_CNT_0_REG + port * 0x90); + p[394] = dsaf_read_dev(ddev, + DSAF_XOD_XGE_RCVIN2_CNT_0_REG + port * 0x90); + p[395] = dsaf_read_dev(ddev, + DSAF_XOD_XGE_RCVIN3_CNT_0_REG + port * 0x90); + p[396] = dsaf_read_dev(ddev, + DSAF_XOD_XGE_RCVIN4_CNT_0_REG + port * 0x90); + p[397] = dsaf_read_dev(ddev, + DSAF_XOD_XGE_RCVIN5_CNT_0_REG + port * 0x90); + p[398] = dsaf_read_dev(ddev, + DSAF_XOD_XGE_RCVIN6_CNT_0_REG + port * 0x90); + p[399] = dsaf_read_dev(ddev, + DSAF_XOD_XGE_RCVIN7_CNT_0_REG + port * 0x90); + p[400] = dsaf_read_dev(ddev, + DSAF_XOD_PPE_RCVIN0_CNT_0_REG + port * 0x90); + p[401] = dsaf_read_dev(ddev, + DSAF_XOD_PPE_RCVIN1_CNT_0_REG + port * 0x90); + p[402] = dsaf_read_dev(ddev, + DSAF_XOD_ROCEE_RCVIN0_CNT_0_REG + port * 0x90); + p[403] = dsaf_read_dev(ddev, + DSAF_XOD_ROCEE_RCVIN1_CNT_0_REG + port * 0x90); + p[404] = dsaf_read_dev(ddev, + DSAF_XOD_FIFO_STATUS_0_REG + port * 0x90); + + /* dsaf voq registers */ + for (i = 0; i < DSAF_VOQ_NUM / DSAF_COMM_CHN; i++) { + j = (i * DSAF_COMM_CHN + port) * 0x90; + p[405 + i] = dsaf_read_dev(ddev, + DSAF_VOQ_ECC_INVERT_EN_0_REG + j); + p[408 + i] = dsaf_read_dev(ddev, + DSAF_VOQ_SRAM_PKT_NUM_0_REG + j); + p[411 + i] = dsaf_read_dev(ddev, DSAF_VOQ_IN_PKT_NUM_0_REG + j); + p[414 + i] = dsaf_read_dev(ddev, + DSAF_VOQ_OUT_PKT_NUM_0_REG + j); + p[417 + i] = dsaf_read_dev(ddev, + DSAF_VOQ_ECC_ERR_ADDR_0_REG + j); + p[420 + i] = dsaf_read_dev(ddev, DSAF_VOQ_BP_STATUS_0_REG + j); + p[423 + i] = dsaf_read_dev(ddev, DSAF_VOQ_SPUP_IDLE_0_REG + j); + p[426 + i] = dsaf_read_dev(ddev, + DSAF_VOQ_XGE_XOD_REQ_0_0_REG + j); + p[429 + i] = dsaf_read_dev(ddev, + DSAF_VOQ_XGE_XOD_REQ_1_0_REG + j); + p[432 + i] = dsaf_read_dev(ddev, + DSAF_VOQ_PPE_XOD_REQ_0_REG + j); + p[435 + i] = dsaf_read_dev(ddev, + DSAF_VOQ_ROCEE_XOD_REQ_0_REG + j); + p[438 + i] = dsaf_read_dev(ddev, + DSAF_VOQ_BP_ALL_THRD_0_REG + j); + } + + /* dsaf tbl registers */ + p[441] = dsaf_read_dev(ddev, DSAF_TBL_CTRL_0_REG); + p[442] = dsaf_read_dev(ddev, DSAF_TBL_INT_MSK_0_REG); + p[443] = dsaf_read_dev(ddev, DSAF_TBL_INT_SRC_0_REG); + p[444] = dsaf_read_dev(ddev, DSAF_TBL_INT_STS_0_REG); + p[445] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_ADDR_0_REG); + p[446] = dsaf_read_dev(ddev, DSAF_TBL_LINE_ADDR_0_REG); + p[447] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_HIGH_0_REG); + p[448] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_LOW_0_REG); + p[449] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_MCAST_CFG_4_0_REG); + p[450] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_MCAST_CFG_3_0_REG); + p[451] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_MCAST_CFG_2_0_REG); + p[452] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_MCAST_CFG_1_0_REG); + p[453] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_MCAST_CFG_0_0_REG); + p[454] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_UCAST_CFG_0_REG); + p[455] = dsaf_read_dev(ddev, DSAF_TBL_LIN_CFG_0_REG); + p[456] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RDATA_HIGH_0_REG); + p[457] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RDATA_LOW_0_REG); + p[458] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RAM_RDATA4_0_REG); + p[459] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RAM_RDATA3_0_REG); + p[460] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RAM_RDATA2_0_REG); + p[461] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RAM_RDATA1_0_REG); + p[462] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RAM_RDATA0_0_REG); + p[463] = dsaf_read_dev(ddev, DSAF_TBL_LIN_RDATA_0_REG); + + for (i = 0; i < DSAF_SW_PORT_NUM; i++) { + j = i * 0x8; + p[464 + 2 * i] = dsaf_read_dev(ddev, + DSAF_TBL_DA0_MIS_INFO1_0_REG + j); + p[465 + 2 * i] = dsaf_read_dev(ddev, + DSAF_TBL_DA0_MIS_INFO0_0_REG + j); + } + + p[480] = dsaf_read_dev(ddev, DSAF_TBL_SA_MIS_INFO2_0_REG); + p[481] = dsaf_read_dev(ddev, DSAF_TBL_SA_MIS_INFO1_0_REG); + p[482] = dsaf_read_dev(ddev, DSAF_TBL_SA_MIS_INFO0_0_REG); + p[483] = dsaf_read_dev(ddev, DSAF_TBL_PUL_0_REG); + p[484] = dsaf_read_dev(ddev, DSAF_TBL_OLD_RSLT_0_REG); + p[485] = dsaf_read_dev(ddev, DSAF_TBL_OLD_SCAN_VAL_0_REG); + p[486] = dsaf_read_dev(ddev, DSAF_TBL_DFX_CTRL_0_REG); + p[487] = dsaf_read_dev(ddev, DSAF_TBL_DFX_STAT_0_REG); + p[488] = dsaf_read_dev(ddev, DSAF_TBL_DFX_STAT_2_0_REG); + p[489] = dsaf_read_dev(ddev, DSAF_TBL_LKUP_NUM_I_0_REG); + p[490] = dsaf_read_dev(ddev, DSAF_TBL_LKUP_NUM_O_0_REG); + p[491] = dsaf_read_dev(ddev, DSAF_TBL_UCAST_BCAST_MIS_INFO_0_0_REG); + + /* dsaf other registers */ + p[492] = dsaf_read_dev(ddev, DSAF_INODE_FIFO_WL_0_REG + port * 0x4); + p[493] = dsaf_read_dev(ddev, DSAF_ONODE_FIFO_WL_0_REG + port * 0x4); + p[494] = dsaf_read_dev(ddev, DSAF_XGE_GE_WORK_MODE_0_REG + port * 0x4); + p[495] = dsaf_read_dev(ddev, + DSAF_XGE_APP_RX_LINK_UP_0_REG + port * 0x4); + p[496] = dsaf_read_dev(ddev, DSAF_NETPORT_CTRL_SIG_0_REG + port * 0x4); + p[497] = dsaf_read_dev(ddev, DSAF_XGE_CTRL_SIG_CFG_0_REG + port * 0x4); + + /* mark end of dsaf regs */ + for (i = 498; i < 504; i++) + p[i] = 0xdddddddd; +} + +static char *hns_dsaf_get_node_stats_strings(char *data, int node) +{ + char *buff = data; + + snprintf(buff, ETH_GSTRING_LEN, "innod%d_pad_drop_pkts", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "innod%d_manage_pkts", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "innod%d_rx_pkts", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "innod%d_rx_pkt_id", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "innod%d_rx_pause_frame", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "innod%d_release_buf_num", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "innod%d_sbm_drop_pkts", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "innod%d_crc_false_pkts", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "innod%d_bp_drop_pkts", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "innod%d_lookup_rslt_drop_pkts", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "innod%d_local_rslt_fail_pkts", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "innod%d_vlan_drop_pkts", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "innod%d_stp_drop_pkts", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "onnod%d_tx_pkts", node); + buff = buff + ETH_GSTRING_LEN; + + return buff; +} + +static u64 *hns_dsaf_get_node_stats(struct dsaf_device *ddev, u64 *data, + int node_num) +{ + u64 *p = data; + struct dsaf_hw_stats *hw_stats = &ddev->hw_stats[node_num]; + + p[0] = hw_stats->pad_drop; + p[1] = hw_stats->man_pkts; + p[2] = hw_stats->rx_pkts; + p[3] = hw_stats->rx_pkt_id; + p[4] = hw_stats->rx_pause_frame; + p[5] = hw_stats->release_buf_num; + p[6] = hw_stats->sbm_drop; + p[7] = hw_stats->crc_false; + p[8] = hw_stats->bp_drop; + p[9] = hw_stats->rslt_drop; + p[10] = hw_stats->local_addr_false; + p[11] = hw_stats->vlan_drop; + p[12] = hw_stats->stp_drop; + p[13] = hw_stats->tx_pkts; + + return &p[14]; +} + +/** + *hns_dsaf_get_stats - get dsaf statistic + *@ddev: dsaf device + *@data:statistic value + *@port: port num + */ +void hns_dsaf_get_stats(struct dsaf_device *ddev, u64 *data, int port) +{ + u64 *p = data; + int node_num = port; + + /* for ge/xge node info */ + p = hns_dsaf_get_node_stats(ddev, p, node_num); + + /* for ppe node info */ + node_num = port + DSAF_PPE_INODE_BASE; + (void)hns_dsaf_get_node_stats(ddev, p, node_num); +} + +/** + *hns_dsaf_get_sset_count - get dsaf string set count + *@stringset: type of values in data + *return dsaf string name count + */ +int hns_dsaf_get_sset_count(int stringset) +{ + if (stringset == ETH_SS_STATS) + return DSAF_STATIC_NUM; + + return 0; +} + +/** + *hns_dsaf_get_strings - get dsaf string set + *@stringset:srting set index + *@data:strings name value + *@port:port index + */ +void hns_dsaf_get_strings(int stringset, u8 *data, int port) +{ + char *buff = (char *)data; + int node = port; + + if (stringset != ETH_SS_STATS) + return; + + /* for ge/xge node info */ + buff = hns_dsaf_get_node_stats_strings(buff, node); + + /* for ppe node info */ + node = port + DSAF_PPE_INODE_BASE; + (void)hns_dsaf_get_node_stats_strings(buff, node); +} + +/** + *hns_dsaf_get_sset_count - get dsaf regs count + *return dsaf regs count + */ +int hns_dsaf_get_regs_count(void) +{ + return DSAF_DUMP_REGS_NUM; +} + +/** + * dsaf_probe - probo dsaf dev + * @pdev: dasf platform device + * retuen 0 - success , negative --fail + */ +static int hns_dsaf_probe(struct platform_device *pdev) +{ + struct dsaf_device *dsaf_dev; + int ret; + + dsaf_dev = hns_dsaf_alloc_dev(&pdev->dev, sizeof(struct dsaf_drv_priv)); + if (IS_ERR(dsaf_dev)) { + ret = PTR_ERR(dsaf_dev); + dev_err(&pdev->dev, + "dsaf_probe dsaf_alloc_dev failed, ret = %#x!\n", ret); + return ret; + } + + ret = hns_dsaf_get_cfg(dsaf_dev); + if (ret) + goto free_dev; + + ret = hns_dsaf_init(dsaf_dev); + if (ret) + goto free_cfg; + + ret = hns_mac_init(dsaf_dev); + if (ret) + goto uninit_dsaf; + + ret = hns_ppe_init(dsaf_dev); + if (ret) + goto uninit_mac; + + ret = hns_dsaf_ae_init(dsaf_dev); + if (ret) + goto uninit_ppe; + + return 0; + +uninit_ppe: + hns_ppe_uninit(dsaf_dev); + +uninit_mac: + hns_mac_uninit(dsaf_dev); + +uninit_dsaf: + hns_dsaf_free(dsaf_dev); + +free_cfg: + hns_dsaf_free_cfg(dsaf_dev); + +free_dev: + hns_dsaf_free_dev(dsaf_dev); + + return ret; +} + +/** + * dsaf_remove - remove dsaf dev + * @pdev: dasf platform device + */ +static int hns_dsaf_remove(struct platform_device *pdev) +{ + struct dsaf_device *dsaf_dev = dev_get_drvdata(&pdev->dev); + + hns_dsaf_ae_uninit(dsaf_dev); + + hns_ppe_uninit(dsaf_dev); + + hns_mac_uninit(dsaf_dev); + + hns_dsaf_free(dsaf_dev); + + hns_dsaf_free_cfg(dsaf_dev); + + hns_dsaf_free_dev(dsaf_dev); + + return 0; +} + +static const struct of_device_id g_dsaf_match[] = { + {.compatible = "hisilicon,hns-dsaf-v1"}, + {.compatible = "hisilicon,hns-dsaf-v2"}, + {} +}; + +static struct platform_driver g_dsaf_driver = { + .probe = hns_dsaf_probe, + .remove = hns_dsaf_remove, + .driver = { + .name = DSAF_DRV_NAME, + .of_match_table = g_dsaf_match, + }, +}; + +module_platform_driver(g_dsaf_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Huawei Tech. Co., Ltd."); +MODULE_DESCRIPTION("HNS DSAF driver"); +MODULE_VERSION(DSAF_MOD_VERSION); diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h new file mode 100644 index 000000000000..e0417c09ba79 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h @@ -0,0 +1,427 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * 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 __HNS_DSAF_MAIN_H +#define __HNS_DSAF_MAIN_H +#include "hnae.h" + +#include "hns_dsaf_reg.h" +#include "hns_dsaf_mac.h" + +struct hns_mac_cb; + +#define DSAF_DRV_NAME "hns_dsaf" +#define DSAF_MOD_VERSION "v1.0" + +#define ENABLE (0x1) +#define DISABLE (0x0) + +#define HNS_DSAF_DEBUG_NW_REG_OFFSET (0x100000) + +#define DSAF_BASE_INNER_PORT_NUM (127) /* mac tbl qid*/ + +#define DSAF_MAX_CHIP_NUM (2) /*max 2 chips */ + +#define DSAF_DEFAUTL_QUEUE_NUM_PER_PPE (22) + +#define HNS_DSAF_MAX_DESC_CNT (1024) +#define HNS_DSAF_MIN_DESC_CNT (16) + +#define DSAF_INVALID_ENTRY_IDX (0xffff) + +#define DSAF_CFG_READ_CNT (30) +#define DSAF_SRAM_INIT_FINISH_FLAG (0xff) + +#define MAC_NUM_OCTETS_PER_ADDR 6 + +#define DSAF_DUMP_REGS_NUM 504 +#define DSAF_STATIC_NUM 28 + +#define DSAF_STATS_READ(p, offset) (*((u64 *)((u64)(p) + (offset)))) + +enum hal_dsaf_mode { + HRD_DSAF_NO_DSAF_MODE = 0x0, + HRD_DSAF_MODE = 0x1, +}; + +enum hal_dsaf_tc_mode { + HRD_DSAF_4TC_MODE = 0X0, + HRD_DSAF_8TC_MODE = 0X1, +}; + +struct dsaf_vm_def_vlan { + u32 vm_def_vlan_id; + u32 vm_def_vlan_cfi; + u32 vm_def_vlan_pri; +}; + +struct dsaf_tbl_tcam_data { + u32 tbl_tcam_data_high; + u32 tbl_tcam_data_low; +}; + +#define DSAF_PORT_MSK_NUM \ + ((DSAF_TOTAL_QUEUE_NUM + DSAF_SERVICE_NW_NUM - 1) / 32 + 1) +struct dsaf_tbl_tcam_mcast_cfg { + u8 tbl_mcast_old_en; + u8 tbl_mcast_item_vld; + u32 tbl_mcast_port_msk[DSAF_PORT_MSK_NUM]; +}; + +struct dsaf_tbl_tcam_ucast_cfg { + u32 tbl_ucast_old_en; + u32 tbl_ucast_item_vld; + u32 tbl_ucast_mac_discard; + u32 tbl_ucast_dvc; + u32 tbl_ucast_out_port; +}; + +struct dsaf_tbl_line_cfg { + u32 tbl_line_mac_discard; + u32 tbl_line_dvc; + u32 tbl_line_out_port; +}; + +enum dsaf_port_rate_mode { + DSAF_PORT_RATE_1000 = 0, + DSAF_PORT_RATE_2500, + DSAF_PORT_RATE_10000 +}; + +enum dsaf_stp_port_type { + DSAF_STP_PORT_TYPE_DISCARD = 0, + DSAF_STP_PORT_TYPE_BLOCK = 1, + DSAF_STP_PORT_TYPE_LISTEN = 2, + DSAF_STP_PORT_TYPE_LEARN = 3, + DSAF_STP_PORT_TYPE_FORWARD = 4 +}; + +enum dsaf_sw_port_type { + DSAF_SW_PORT_TYPE_NON_VLAN = 0, + DSAF_SW_PORT_TYPE_ACCESS = 1, + DSAF_SW_PORT_TYPE_TRUNK = 2, +}; + +#define DSAF_SUB_BASE_SIZE (0x10000) + +/* dsaf mode define */ +enum dsaf_mode { + DSAF_MODE_INVALID = 0, /**< Invalid dsaf mode */ + DSAF_MODE_ENABLE_FIX, /**< en DSAF-mode, fixed to queue*/ + DSAF_MODE_ENABLE_0VM, /**< en DSAF-mode, support 0 VM */ + DSAF_MODE_ENABLE_8VM, /**< en DSAF-mode, support 8 VM */ + DSAF_MODE_ENABLE_16VM, /**< en DSAF-mode, support 16 VM */ + DSAF_MODE_ENABLE_32VM, /**< en DSAF-mode, support 32 VM */ + DSAF_MODE_ENABLE_128VM, /**< en DSAF-mode, support 128 VM */ + DSAF_MODE_ENABLE, /**< before is enable DSAF mode*/ + DSAF_MODE_DISABLE_FIX, /**< non-dasf, fixed to queue*/ + DSAF_MODE_DISABLE_2PORT_8VM, /**< non-dasf, 2port 8VM */ + DSAF_MODE_DISABLE_2PORT_16VM, /**< non-dasf, 2port 16VM */ + DSAF_MODE_DISABLE_2PORT_64VM, /**< non-dasf, 2port 64VM */ + DSAF_MODE_DISABLE_6PORT_0VM, /**< non-dasf, 6port 0VM */ + DSAF_MODE_DISABLE_6PORT_2VM, /**< non-dasf, 6port 2VM */ + DSAF_MODE_DISABLE_6PORT_4VM, /**< non-dasf, 6port 4VM */ + DSAF_MODE_DISABLE_6PORT_16VM, /**< non-dasf, 6port 16VM */ + DSAF_MODE_MAX /**< the last one, use as the num */ +}; + +#define DSAF_DEST_PORT_NUM 256 /* DSAF max port num */ +#define DSAF_WORD_BIT_CNT 32 /* the num bit of word */ + +/*mac entry, mc or uc entry*/ +struct dsaf_drv_mac_single_dest_entry { + /* mac addr, match the entry*/ + u8 addr[MAC_NUM_OCTETS_PER_ADDR]; + u16 in_vlan_id; /* value of VlanId */ + + /* the vld input port num, dsaf-mode fix 0, */ + /* non-dasf is the entry whitch port vld*/ + u8 in_port_num; + + u8 port_num; /*output port num*/ + u8 rsv[6]; +}; + +/*only mc entry*/ +struct dsaf_drv_mac_multi_dest_entry { + /* mac addr, match the entry*/ + u8 addr[MAC_NUM_OCTETS_PER_ADDR]; + u16 in_vlan_id; + /* this mac addr output port,*/ + /* bit0-bit5 means Port0-Port5(1bit is vld)**/ + u32 port_mask[DSAF_DEST_PORT_NUM / DSAF_WORD_BIT_CNT]; + + /* the vld input port num, dsaf-mode fix 0,*/ + /* non-dasf is the entry whitch port vld*/ + u8 in_port_num; + u8 rsv[7]; +}; + +struct dsaf_hw_stats { + u64 pad_drop; + u64 man_pkts; + u64 rx_pkts; + u64 rx_pkt_id; + u64 rx_pause_frame; + u64 release_buf_num; + u64 sbm_drop; + u64 crc_false; + u64 bp_drop; + u64 rslt_drop; + u64 local_addr_false; + u64 vlan_drop; + u64 stp_drop; + u64 tx_pkts; +}; + +struct hnae_vf_cb { + u8 port_index; + struct hns_mac_cb *mac_cb; + struct dsaf_device *dsaf_dev; + struct hnae_handle ae_handle; /* must be the last number */ +}; + +struct dsaf_int_xge_src { + u32 xid_xge_ecc_err_int_src; + u32 xid_xge_fsm_timout_int_src; + u32 sbm_xge_lnk_fsm_timout_int_src; + u32 sbm_xge_lnk_ecc_2bit_int_src; + u32 sbm_xge_mib_req_failed_int_src; + u32 sbm_xge_mib_req_fsm_timout_int_src; + u32 sbm_xge_mib_rels_fsm_timout_int_src; + u32 sbm_xge_sram_ecc_2bit_int_src; + u32 sbm_xge_mib_buf_sum_err_int_src; + u32 sbm_xge_mib_req_extra_int_src; + u32 sbm_xge_mib_rels_extra_int_src; + u32 voq_xge_start_to_over_0_int_src; + u32 voq_xge_start_to_over_1_int_src; + u32 voq_xge_ecc_err_int_src; +}; + +struct dsaf_int_ppe_src { + u32 xid_ppe_fsm_timout_int_src; + u32 sbm_ppe_lnk_fsm_timout_int_src; + u32 sbm_ppe_lnk_ecc_2bit_int_src; + u32 sbm_ppe_mib_req_failed_int_src; + u32 sbm_ppe_mib_req_fsm_timout_int_src; + u32 sbm_ppe_mib_rels_fsm_timout_int_src; + u32 sbm_ppe_sram_ecc_2bit_int_src; + u32 sbm_ppe_mib_buf_sum_err_int_src; + u32 sbm_ppe_mib_req_extra_int_src; + u32 sbm_ppe_mib_rels_extra_int_src; + u32 voq_ppe_start_to_over_0_int_src; + u32 voq_ppe_ecc_err_int_src; + u32 xod_ppe_fifo_rd_empty_int_src; + u32 xod_ppe_fifo_wr_full_int_src; +}; + +struct dsaf_int_rocee_src { + u32 xid_rocee_fsm_timout_int_src; + u32 sbm_rocee_lnk_fsm_timout_int_src; + u32 sbm_rocee_lnk_ecc_2bit_int_src; + u32 sbm_rocee_mib_req_failed_int_src; + u32 sbm_rocee_mib_req_fsm_timout_int_src; + u32 sbm_rocee_mib_rels_fsm_timout_int_src; + u32 sbm_rocee_sram_ecc_2bit_int_src; + u32 sbm_rocee_mib_buf_sum_err_int_src; + u32 sbm_rocee_mib_req_extra_int_src; + u32 sbm_rocee_mib_rels_extra_int_src; + u32 voq_rocee_start_to_over_0_int_src; + u32 voq_rocee_ecc_err_int_src; +}; + +struct dsaf_int_tbl_src { + u32 tbl_da0_mis_src; + u32 tbl_da1_mis_src; + u32 tbl_da2_mis_src; + u32 tbl_da3_mis_src; + u32 tbl_da4_mis_src; + u32 tbl_da5_mis_src; + u32 tbl_da6_mis_src; + u32 tbl_da7_mis_src; + u32 tbl_sa_mis_src; + u32 tbl_old_sech_end_src; + u32 lram_ecc_err1_src; + u32 lram_ecc_err2_src; + u32 tram_ecc_err1_src; + u32 tram_ecc_err2_src; + u32 tbl_ucast_bcast_xge0_src; + u32 tbl_ucast_bcast_xge1_src; + u32 tbl_ucast_bcast_xge2_src; + u32 tbl_ucast_bcast_xge3_src; + u32 tbl_ucast_bcast_xge4_src; + u32 tbl_ucast_bcast_xge5_src; + u32 tbl_ucast_bcast_ppe_src; + u32 tbl_ucast_bcast_rocee_src; +}; + +struct dsaf_int_stat { + struct dsaf_int_xge_src dsaf_int_xge_stat[DSAF_COMM_CHN]; + struct dsaf_int_ppe_src dsaf_int_ppe_stat[DSAF_COMM_CHN]; + struct dsaf_int_rocee_src dsaf_int_rocee_stat[DSAF_COMM_CHN]; + struct dsaf_int_tbl_src dsaf_int_tbl_stat[1]; + +}; + +/* Dsaf device struct define ,and mac -> dsaf */ +struct dsaf_device { + struct device *dev; + struct hnae_ae_dev ae_dev; + + void *priv; + + int virq[DSAF_IRQ_NUM]; + + u8 __iomem *sc_base; + u8 __iomem *sds_base; + u8 __iomem *ppe_base; + u8 __iomem *io_base; + u8 __iomem *cpld_base; + + u32 desc_num; /* desc num per queue*/ + u32 buf_size; /* ring buffer size */ + int buf_size_type; /* ring buffer size-type */ + enum dsaf_mode dsaf_mode; /* dsaf mode */ + enum hal_dsaf_mode dsaf_en; + enum hal_dsaf_tc_mode dsaf_tc_mode; + u32 dsaf_ver; + + struct ppe_common_cb *ppe_common[DSAF_COMM_DEV_NUM]; + struct rcb_common_cb *rcb_common[DSAF_COMM_DEV_NUM]; + struct hns_mac_cb *mac_cb; + + struct dsaf_hw_stats hw_stats[DSAF_NODE_NUM]; + struct dsaf_int_stat int_stat; +}; + +static inline void *hns_dsaf_dev_priv(const struct dsaf_device *dsaf_dev) +{ + return (void *)((u64)dsaf_dev + sizeof(*dsaf_dev)); +} + +struct dsaf_drv_tbl_tcam_key { + union { + struct { + u8 mac_3; + u8 mac_2; + u8 mac_1; + u8 mac_0; + } bits; + + u32 val; + } high; + union { + struct { + u32 port:4; /* port id, */ + /* dsaf-mode fixed 0, non-dsaf-mode port id*/ + u32 vlan:12; /* vlan id */ + u32 mac_5:8; + u32 mac_4:8; + } bits; + + u32 val; + } low; +}; + +struct dsaf_drv_soft_mac_tbl { + struct dsaf_drv_tbl_tcam_key tcam_key; + u16 index; /*the entry's index in tcam tab*/ +}; + +struct dsaf_drv_priv { + /* soft tab Mac key, for hardware tab*/ + struct dsaf_drv_soft_mac_tbl *soft_mac_tbl; +}; + +static inline void hns_dsaf_tbl_tcam_addr_cfg(struct dsaf_device *dsaf_dev, + u32 tab_tcam_addr) +{ + dsaf_set_dev_field(dsaf_dev, DSAF_TBL_TCAM_ADDR_0_REG, + DSAF_TBL_TCAM_ADDR_M, DSAF_TBL_TCAM_ADDR_S, + tab_tcam_addr); +} + +static inline void hns_dsaf_tbl_tcam_load_pul(struct dsaf_device *dsaf_dev) +{ + u32 o_tbl_pul; + + o_tbl_pul = dsaf_read_dev(dsaf_dev, DSAF_TBL_PUL_0_REG); + dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_TCAM_LOAD_S, 1); + dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, o_tbl_pul); + dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_TCAM_LOAD_S, 0); + dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, o_tbl_pul); +} + +static inline void hns_dsaf_tbl_line_addr_cfg(struct dsaf_device *dsaf_dev, + u32 tab_line_addr) +{ + dsaf_set_dev_field(dsaf_dev, DSAF_TBL_LINE_ADDR_0_REG, + DSAF_TBL_LINE_ADDR_M, DSAF_TBL_LINE_ADDR_S, + tab_line_addr); +} + +static inline int hns_dsaf_get_comm_idx_by_port(int port) +{ + if ((port < DSAF_COMM_CHN) || (port == DSAF_MAX_PORT_NUM_PER_CHIP)) + return 0; + else + return (port - DSAF_COMM_CHN + 1); +} + +static inline struct hnae_vf_cb *hns_ae_get_vf_cb( + struct hnae_handle *handle) +{ + return container_of(handle, struct hnae_vf_cb, ae_handle); +} + +int hns_dsaf_set_mac_uc_entry(struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_single_dest_entry *mac_entry); +int hns_dsaf_set_mac_mc_entry(struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_multi_dest_entry *mac_entry); +int hns_dsaf_add_mac_mc_port(struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_single_dest_entry *mac_entry); +int hns_dsaf_del_mac_entry(struct dsaf_device *dsaf_dev, u16 vlan_id, + u8 in_port_num, u8 *addr); +int hns_dsaf_del_mac_mc_port(struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_single_dest_entry *mac_entry); +int hns_dsaf_get_mac_uc_entry(struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_single_dest_entry *mac_entry); +int hns_dsaf_get_mac_mc_entry(struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_multi_dest_entry *mac_entry); +int hns_dsaf_get_mac_entry_by_index( + struct dsaf_device *dsaf_dev, + u16 entry_index, + struct dsaf_drv_mac_multi_dest_entry *mac_entry); + +void hns_dsaf_rst(struct dsaf_device *dsaf_dev, u32 val); + +void hns_ppe_srst_by_port(struct dsaf_device *dsaf_dev, u32 port, u32 val); + +void hns_ppe_com_srst(struct ppe_common_cb *ppe_common, u32 val); + +void hns_dsaf_fix_mac_mode(struct hns_mac_cb *mac_cb); + +int hns_dsaf_ae_init(struct dsaf_device *dsaf_dev); +void hns_dsaf_ae_uninit(struct dsaf_device *dsaf_dev); + +void hns_dsaf_xge_srst_by_port(struct dsaf_device *dsaf_dev, u32 port, u32 val); +void hns_dsaf_ge_srst_by_port(struct dsaf_device *dsaf_dev, u32 port, u32 val); +void hns_dsaf_xge_core_srst_by_port(struct dsaf_device *dsaf_dev, + u32 port, u32 val); + +void hns_dsaf_update_stats(struct dsaf_device *dsaf_dev, u32 inode_num); + +int hns_dsaf_get_sset_count(int stringset); +void hns_dsaf_get_stats(struct dsaf_device *ddev, u64 *data, int port); +void hns_dsaf_get_strings(int stringset, u8 *data, int port); + +void hns_dsaf_get_regs(struct dsaf_device *ddev, u32 port, void *data); +int hns_dsaf_get_regs_count(void); + +#endif /* __HNS_DSAF_MAIN_H__ */ diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c new file mode 100644 index 000000000000..d611388aecef --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c @@ -0,0 +1,317 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * 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 "hns_dsaf_misc.h" +#include "hns_dsaf_mac.h" +#include "hns_dsaf_reg.h" +#include "hns_dsaf_ppe.h" + +void hns_cpld_set_led(struct hns_mac_cb *mac_cb, int link_status, + u16 speed, int data) +{ + int speed_reg = 0; + u8 value; + + if (!mac_cb) { + pr_err("sfp_led_opt mac_dev is null!\n"); + return; + } + if (!mac_cb->cpld_vaddr) { + dev_err(mac_cb->dev, "mac_id=%d, cpld_vaddr is null !\n", + mac_cb->mac_id); + return; + } + + if (speed == MAC_SPEED_10000) + speed_reg = 1; + + value = mac_cb->cpld_led_value; + + if (link_status) { + dsaf_set_bit(value, DSAF_LED_LINK_B, link_status); + dsaf_set_field(value, DSAF_LED_SPEED_M, + DSAF_LED_SPEED_S, speed_reg); + dsaf_set_bit(value, DSAF_LED_DATA_B, data); + + if (value != mac_cb->cpld_led_value) { + dsaf_write_b(mac_cb->cpld_vaddr, value); + mac_cb->cpld_led_value = value; + } + } else { + dsaf_write_b(mac_cb->cpld_vaddr, CPLD_LED_DEFAULT_VALUE); + mac_cb->cpld_led_value = CPLD_LED_DEFAULT_VALUE; + } +} + +void cpld_led_reset(struct hns_mac_cb *mac_cb) +{ + if (!mac_cb || !mac_cb->cpld_vaddr) + return; + + dsaf_write_b(mac_cb->cpld_vaddr, CPLD_LED_DEFAULT_VALUE); + mac_cb->cpld_led_value = CPLD_LED_DEFAULT_VALUE; +} + +int cpld_set_led_id(struct hns_mac_cb *mac_cb, + enum hnae_led_state status) +{ + switch (status) { + case HNAE_LED_ACTIVE: + mac_cb->cpld_led_value = dsaf_read_b(mac_cb->cpld_vaddr); + return 2; + case HNAE_LED_ON: + dsaf_set_bit(mac_cb->cpld_led_value, DSAF_LED_ANCHOR_B, + CPLD_LED_ON_VALUE); + dsaf_write_b(mac_cb->cpld_vaddr, mac_cb->cpld_led_value); + break; + case HNAE_LED_OFF: + dsaf_set_bit(mac_cb->cpld_led_value, DSAF_LED_ANCHOR_B, + CPLD_LED_DEFAULT_VALUE); + dsaf_write_b(mac_cb->cpld_vaddr, mac_cb->cpld_led_value); + break; + case HNAE_LED_INACTIVE: + dsaf_set_bit(mac_cb->cpld_led_value, DSAF_LED_ANCHOR_B, + CPLD_LED_DEFAULT_VALUE); + dsaf_write_b(mac_cb->cpld_vaddr, mac_cb->cpld_led_value); + break; + default: + break; + } + + return 0; +} + +#define RESET_REQ_OR_DREQ 1 + +void hns_dsaf_rst(struct dsaf_device *dsaf_dev, u32 val) +{ + u32 xbar_reg_addr; + u32 nt_reg_addr; + + if (!val) { + xbar_reg_addr = DSAF_SUB_SC_XBAR_RESET_REQ_REG; + nt_reg_addr = DSAF_SUB_SC_NT_RESET_REQ_REG; + } else { + xbar_reg_addr = DSAF_SUB_SC_XBAR_RESET_DREQ_REG; + nt_reg_addr = DSAF_SUB_SC_NT_RESET_DREQ_REG; + } + + dsaf_write_reg(dsaf_dev->sc_base, xbar_reg_addr, + RESET_REQ_OR_DREQ); + dsaf_write_reg(dsaf_dev->sc_base, nt_reg_addr, + RESET_REQ_OR_DREQ); +} + +void hns_dsaf_xge_srst_by_port(struct dsaf_device *dsaf_dev, u32 port, u32 val) +{ + u32 reg_val = 0; + u32 reg_addr; + + if (port >= DSAF_XGE_NUM) + return; + + reg_val |= RESET_REQ_OR_DREQ; + reg_val |= 0x2082082 << port; + + if (val == 0) + reg_addr = DSAF_SUB_SC_XGE_RESET_REQ_REG; + else + reg_addr = DSAF_SUB_SC_XGE_RESET_DREQ_REG; + + dsaf_write_reg(dsaf_dev->sc_base, reg_addr, reg_val); +} + +void hns_dsaf_xge_core_srst_by_port(struct dsaf_device *dsaf_dev, + u32 port, u32 val) +{ + u32 reg_val = 0; + u32 reg_addr; + + if (port >= DSAF_XGE_NUM) + return; + + reg_val |= XGMAC_TRX_CORE_SRST_M << port; + + if (val == 0) + reg_addr = DSAF_SUB_SC_XGE_RESET_REQ_REG; + else + reg_addr = DSAF_SUB_SC_XGE_RESET_DREQ_REG; + + dsaf_write_reg(dsaf_dev->sc_base, reg_addr, reg_val); +} + +void hns_dsaf_ge_srst_by_port(struct dsaf_device *dsaf_dev, u32 port, u32 val) +{ + u32 reg_val_1; + u32 reg_val_2; + + if (port >= DSAF_GE_NUM) + return; + + if (port < DSAF_SERVICE_NW_NUM) { + reg_val_1 = 0x1 << port; + reg_val_2 = 0x1041041 << port; + + if (val == 0) { + dsaf_write_reg(dsaf_dev->sc_base, + DSAF_SUB_SC_GE_RESET_REQ1_REG, + reg_val_1); + + dsaf_write_reg(dsaf_dev->sc_base, + DSAF_SUB_SC_GE_RESET_REQ0_REG, + reg_val_2); + } else { + dsaf_write_reg(dsaf_dev->sc_base, + DSAF_SUB_SC_GE_RESET_DREQ0_REG, + reg_val_2); + + dsaf_write_reg(dsaf_dev->sc_base, + DSAF_SUB_SC_GE_RESET_DREQ1_REG, + reg_val_1); + } + } else { + reg_val_1 = 0x15540 << (port - 6); + reg_val_2 = 0x100 << (port - 6); + + if (val == 0) { + dsaf_write_reg(dsaf_dev->sc_base, + DSAF_SUB_SC_GE_RESET_REQ1_REG, + reg_val_1); + + dsaf_write_reg(dsaf_dev->sc_base, + DSAF_SUB_SC_PPE_RESET_REQ_REG, + reg_val_2); + } else { + dsaf_write_reg(dsaf_dev->sc_base, + DSAF_SUB_SC_GE_RESET_DREQ1_REG, + reg_val_1); + + dsaf_write_reg(dsaf_dev->sc_base, + DSAF_SUB_SC_PPE_RESET_DREQ_REG, + reg_val_2); + } + } +} + +void hns_ppe_srst_by_port(struct dsaf_device *dsaf_dev, u32 port, u32 val) +{ + u32 reg_val = 0; + u32 reg_addr; + + reg_val |= RESET_REQ_OR_DREQ << port; + + if (val == 0) + reg_addr = DSAF_SUB_SC_PPE_RESET_REQ_REG; + else + reg_addr = DSAF_SUB_SC_PPE_RESET_DREQ_REG; + + dsaf_write_reg(dsaf_dev->sc_base, reg_addr, reg_val); +} + +void hns_ppe_com_srst(struct ppe_common_cb *ppe_common, u32 val) +{ + int comm_index = ppe_common->comm_index; + struct dsaf_device *dsaf_dev = ppe_common->dsaf_dev; + u32 reg_val; + u32 reg_addr; + + if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) { + reg_val = RESET_REQ_OR_DREQ; + if (val == 0) + reg_addr = DSAF_SUB_SC_RCB_PPE_COM_RESET_REQ_REG; + else + reg_addr = DSAF_SUB_SC_RCB_PPE_COM_RESET_DREQ_REG; + + } else { + reg_val = 0x100 << (comm_index - 1); + + if (val == 0) + reg_addr = DSAF_SUB_SC_PPE_RESET_REQ_REG; + else + reg_addr = DSAF_SUB_SC_PPE_RESET_DREQ_REG; + } + + dsaf_write_reg(dsaf_dev->sc_base, reg_addr, reg_val); +} + +/** + * hns_mac_get_sds_mode - get phy ifterface form serdes mode + * @mac_cb: mac control block + * retuen phy interface + */ +phy_interface_t hns_mac_get_phy_if(struct hns_mac_cb *mac_cb) +{ + u32 hilink3_mode; + u32 hilink4_mode; + void __iomem *sys_ctl_vaddr = mac_cb->sys_ctl_vaddr; + int dev_id = mac_cb->mac_id; + phy_interface_t phy_if = PHY_INTERFACE_MODE_NA; + + hilink3_mode = dsaf_read_reg(sys_ctl_vaddr, HNS_MAC_HILINK3_REG); + hilink4_mode = dsaf_read_reg(sys_ctl_vaddr, HNS_MAC_HILINK4_REG); + if (dev_id >= 0 && dev_id <= 3) { + if (hilink4_mode == 0) + phy_if = PHY_INTERFACE_MODE_SGMII; + else + phy_if = PHY_INTERFACE_MODE_XGMII; + } else if (dev_id >= 4 && dev_id <= 5) { + if (hilink3_mode == 0) + phy_if = PHY_INTERFACE_MODE_SGMII; + else + phy_if = PHY_INTERFACE_MODE_XGMII; + } else { + phy_if = PHY_INTERFACE_MODE_SGMII; + } + + dev_dbg(mac_cb->dev, + "hilink3_mode=%d, hilink4_mode=%d dev_id=%d, phy_if=%d\n", + hilink3_mode, hilink4_mode, dev_id, phy_if); + return phy_if; +} + +/** + * hns_mac_config_sds_loopback - set loop back for serdes + * @mac_cb: mac control block + * retuen 0 == success + */ +int hns_mac_config_sds_loopback(struct hns_mac_cb *mac_cb, u8 en) +{ + /* port 0-3 hilink4 base is serdes_vaddr + 0x00280000 + * port 4-7 hilink3 base is serdes_vaddr + 0x00200000 + */ + u8 *base_addr = (u8 *)mac_cb->serdes_vaddr + + (mac_cb->mac_id <= 3 ? 0x00280000 : 0x00200000); + const u8 lane_id[] = { + 0, /* mac 0 -> lane 0 */ + 1, /* mac 1 -> lane 1 */ + 2, /* mac 2 -> lane 2 */ + 3, /* mac 3 -> lane 3 */ + 2, /* mac 4 -> lane 2 */ + 3, /* mac 5 -> lane 3 */ + 0, /* mac 6 -> lane 0 */ + 1 /* mac 7 -> lane 1 */ + }; +#define RX_CSR(lane, reg) ((0x4080 + (reg) * 0x0002 + (lane) * 0x0200) * 2) + u64 reg_offset = RX_CSR(lane_id[mac_cb->mac_id], 0); + + int sfp_prsnt; + int ret = hns_mac_get_sfp_prsnt(mac_cb, &sfp_prsnt); + + if (!mac_cb->phy_node) { + if (ret) + pr_info("please confirm sfp is present or not\n"); + else + if (!sfp_prsnt) + pr_info("no sfp in this eth\n"); + } + + dsaf_set_reg_field(base_addr, reg_offset, 1ull << 10, 10, !!en); + + return 0; +} diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.h new file mode 100644 index 000000000000..419f07aa9734 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * 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 _HNS_DSAF_MISC_H +#define _HNS_DSAF_MISC_H + +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> + +#include "hns_dsaf_mac.h" + +#define CPLD_ADDR_PORT_OFFSET 0x4 + +#define HS_LED_ON 0xE +#define HS_LED_OFF 0xF + +#define CPLD_LED_ON_VALUE 1 +#define CPLD_LED_DEFAULT_VALUE 0 + +#define MAC_SFP_PORT_OFFSET 0x2 + +#define DSAF_LED_SPEED_S 0 +#define DSAF_LED_SPEED_M (0x3 << DSAF_LED_SPEED_S) + +#define DSAF_LED_LINK_B 2 +#define DSAF_LED_DATA_B 4 +#define DSAF_LED_ANCHOR_B 5 + +void hns_cpld_set_led(struct hns_mac_cb *mac_cb, int link_status, + u16 speed, int data); +void cpld_led_reset(struct hns_mac_cb *mac_cb); +int cpld_set_led_id(struct hns_mac_cb *mac_cb, + enum hnae_led_state status); +int hns_mac_get_sfp_prsnt(struct hns_mac_cb *mac_cb, int *sfp_prsnt); + +#endif diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c new file mode 100644 index 000000000000..67f33f185a44 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c @@ -0,0 +1,583 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * 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/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> + +#include "hns_dsaf_ppe.h" + +static void __iomem *hns_ppe_common_get_ioaddr( + struct ppe_common_cb *ppe_common) +{ + void __iomem *base_addr; + + int idx = ppe_common->comm_index; + + if (idx == HNS_DSAF_COMM_SERVICE_NW_IDX) + base_addr = ppe_common->dsaf_dev->ppe_base + + PPE_COMMON_REG_OFFSET; + else + base_addr = ppe_common->dsaf_dev->sds_base + + (idx - 1) * HNS_DSAF_DEBUG_NW_REG_OFFSET + + PPE_COMMON_REG_OFFSET; + + return base_addr; +} + +/** + * hns_ppe_common_get_cfg - get ppe common config + * @dsaf_dev: dasf device + * comm_index: common index + * retuen 0 - success , negative --fail + */ +int hns_ppe_common_get_cfg(struct dsaf_device *dsaf_dev, int comm_index) +{ + struct ppe_common_cb *ppe_common; + int ppe_num; + + if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) + ppe_num = HNS_PPE_SERVICE_NW_ENGINE_NUM; + else + ppe_num = HNS_PPE_DEBUG_NW_ENGINE_NUM; + + ppe_common = devm_kzalloc(dsaf_dev->dev, sizeof(*ppe_common) + + ppe_num * sizeof(struct hns_ppe_cb), GFP_KERNEL); + if (!ppe_common) + return -ENOMEM; + + ppe_common->ppe_num = ppe_num; + ppe_common->dsaf_dev = dsaf_dev; + ppe_common->comm_index = comm_index; + if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) + ppe_common->ppe_mode = PPE_COMMON_MODE_SERVICE; + else + ppe_common->ppe_mode = PPE_COMMON_MODE_DEBUG; + ppe_common->dev = dsaf_dev->dev; + + ppe_common->io_base = hns_ppe_common_get_ioaddr(ppe_common); + + dsaf_dev->ppe_common[comm_index] = ppe_common; + + return 0; +} + +void hns_ppe_common_free_cfg(struct dsaf_device *dsaf_dev, u32 comm_index) +{ + dsaf_dev->ppe_common[comm_index] = NULL; +} + +static void __iomem *hns_ppe_get_iobase(struct ppe_common_cb *ppe_common, + int ppe_idx) +{ + void __iomem *base_addr; + int common_idx = ppe_common->comm_index; + + if (ppe_common->ppe_mode == PPE_COMMON_MODE_SERVICE) { + base_addr = ppe_common->dsaf_dev->ppe_base + + ppe_idx * PPE_REG_OFFSET; + + } else { + base_addr = ppe_common->dsaf_dev->sds_base + + (common_idx - 1) * HNS_DSAF_DEBUG_NW_REG_OFFSET; + } + + return base_addr; +} + +static int hns_ppe_get_port(struct ppe_common_cb *ppe_common, int idx) +{ + int port; + + if (ppe_common->ppe_mode == PPE_COMMON_MODE_SERVICE) + port = idx; + else + port = HNS_PPE_SERVICE_NW_ENGINE_NUM + + ppe_common->comm_index - 1; + + return port; +} + +static void hns_ppe_get_cfg(struct ppe_common_cb *ppe_common) +{ + u32 i; + struct hns_ppe_cb *ppe_cb; + u32 ppe_num = ppe_common->ppe_num; + + for (i = 0; i < ppe_num; i++) { + ppe_cb = &ppe_common->ppe_cb[i]; + ppe_cb->dev = ppe_common->dev; + ppe_cb->next = NULL; + ppe_cb->ppe_common_cb = ppe_common; + ppe_cb->index = i; + ppe_cb->port = hns_ppe_get_port(ppe_common, i); + ppe_cb->io_base = hns_ppe_get_iobase(ppe_common, i); + ppe_cb->virq = 0; + } +} + +static void hns_ppe_cnt_clr_ce(struct hns_ppe_cb *ppe_cb) +{ + dsaf_set_dev_bit(ppe_cb, PPE_TNL_0_5_CNT_CLR_CE_REG, + PPE_CNT_CLR_CE_B, 1); +} + +/** + * hns_ppe_checksum_hw - set ppe checksum caculate + * @ppe_device: ppe device + * @value: value + */ +static void hns_ppe_checksum_hw(struct hns_ppe_cb *ppe_cb, u32 value) +{ + dsaf_set_dev_field(ppe_cb, PPE_CFG_PRO_CHECK_EN_REG, + 0xfffffff, 0, value); +} + +static void hns_ppe_set_qid_mode(struct ppe_common_cb *ppe_common, + enum ppe_qid_mode qid_mdoe) +{ + dsaf_set_dev_field(ppe_common, PPE_COM_CFG_QID_MODE_REG, + PPE_CFG_QID_MODE_CF_QID_MODE_M, + PPE_CFG_QID_MODE_CF_QID_MODE_S, qid_mdoe); +} + +/** + * hns_ppe_set_qid - set ppe qid + * @ppe_common: ppe common device + * @qid: queue id + */ +static void hns_ppe_set_qid(struct ppe_common_cb *ppe_common, u32 qid) +{ + u32 qid_mod = dsaf_read_dev(ppe_common, PPE_COM_CFG_QID_MODE_REG); + + if (!dsaf_get_field(qid_mod, PPE_CFG_QID_MODE_DEF_QID_M, + PPE_CFG_QID_MODE_DEF_QID_S)) { + dsaf_set_field(qid_mod, PPE_CFG_QID_MODE_DEF_QID_M, + PPE_CFG_QID_MODE_DEF_QID_S, qid); + dsaf_write_dev(ppe_common, PPE_COM_CFG_QID_MODE_REG, qid_mod); + } +} + +/** + * hns_ppe_set_port_mode - set port mode + * @ppe_device: ppe device + * @mode: port mode + */ +static void hns_ppe_set_port_mode(struct hns_ppe_cb *ppe_cb, + enum ppe_port_mode mode) +{ + dsaf_write_dev(ppe_cb, PPE_CFG_XGE_MODE_REG, mode); +} + +/** + * hns_ppe_common_init_hw - init ppe common device + * @ppe_common: ppe common device + * + * Return 0 on success, negative on failure + */ +static int hns_ppe_common_init_hw(struct ppe_common_cb *ppe_common) +{ + enum ppe_qid_mode qid_mode; + enum dsaf_mode dsaf_mode = ppe_common->dsaf_dev->dsaf_mode; + + hns_ppe_com_srst(ppe_common, 0); + mdelay(100); + hns_ppe_com_srst(ppe_common, 1); + mdelay(100); + + if (ppe_common->ppe_mode == PPE_COMMON_MODE_SERVICE) { + switch (dsaf_mode) { + case DSAF_MODE_ENABLE_FIX: + case DSAF_MODE_DISABLE_FIX: + qid_mode = PPE_QID_MODE0; + hns_ppe_set_qid(ppe_common, 0); + break; + case DSAF_MODE_ENABLE_0VM: + case DSAF_MODE_DISABLE_2PORT_64VM: + qid_mode = PPE_QID_MODE3; + break; + case DSAF_MODE_ENABLE_8VM: + case DSAF_MODE_DISABLE_2PORT_16VM: + qid_mode = PPE_QID_MODE4; + break; + case DSAF_MODE_ENABLE_16VM: + case DSAF_MODE_DISABLE_6PORT_0VM: + qid_mode = PPE_QID_MODE5; + break; + case DSAF_MODE_ENABLE_32VM: + case DSAF_MODE_DISABLE_6PORT_16VM: + qid_mode = PPE_QID_MODE2; + break; + case DSAF_MODE_ENABLE_128VM: + case DSAF_MODE_DISABLE_6PORT_4VM: + qid_mode = PPE_QID_MODE1; + break; + case DSAF_MODE_DISABLE_2PORT_8VM: + qid_mode = PPE_QID_MODE7; + break; + case DSAF_MODE_DISABLE_6PORT_2VM: + qid_mode = PPE_QID_MODE6; + break; + default: + dev_err(ppe_common->dev, + "get ppe queue mode failed! dsaf_mode=%d\n", + dsaf_mode); + return -EINVAL; + } + hns_ppe_set_qid_mode(ppe_common, qid_mode); + } + + dsaf_set_dev_bit(ppe_common, PPE_COM_COMMON_CNT_CLR_CE_REG, + PPE_COMMON_CNT_CLR_CE_B, 1); + + return 0; +} + +/*clr ppe exception irq*/ +static void hns_ppe_exc_irq_en(struct hns_ppe_cb *ppe_cb, int en) +{ + u32 clr_vlue = 0xfffffffful; + u32 msk_vlue = en ? 0xfffffffful : 0; /*1 is en, 0 is dis*/ + u32 vld_msk = 0; + + /*only care bit 0,1,7*/ + dsaf_set_bit(vld_msk, 0, 1); + dsaf_set_bit(vld_msk, 1, 1); + dsaf_set_bit(vld_msk, 7, 1); + + /*clr sts**/ + dsaf_write_dev(ppe_cb, PPE_RINT_REG, clr_vlue); + + /*for some reserved bits, so set 0**/ + dsaf_write_dev(ppe_cb, PPE_INTEN_REG, msk_vlue & vld_msk); +} + +/** + * ppe_init_hw - init ppe + * @ppe_device: ppe device + */ +static void hns_ppe_init_hw(struct hns_ppe_cb *ppe_cb) +{ + struct ppe_common_cb *ppe_common_cb = ppe_cb->ppe_common_cb; + u32 port = ppe_cb->port; + struct dsaf_device *dsaf_dev = ppe_common_cb->dsaf_dev; + + hns_ppe_srst_by_port(dsaf_dev, port, 0); + mdelay(10); + hns_ppe_srst_by_port(dsaf_dev, port, 1); + + /* clr and msk except irq*/ + hns_ppe_exc_irq_en(ppe_cb, 0); + + if (ppe_common_cb->ppe_mode == PPE_COMMON_MODE_DEBUG) + hns_ppe_set_port_mode(ppe_cb, PPE_MODE_GE); + else + hns_ppe_set_port_mode(ppe_cb, PPE_MODE_XGE); + hns_ppe_checksum_hw(ppe_cb, 0xffffffff); + hns_ppe_cnt_clr_ce(ppe_cb); +} + +/** + * ppe_uninit_hw - uninit ppe + * @ppe_device: ppe device + */ +static void hns_ppe_uninit_hw(struct hns_ppe_cb *ppe_cb) +{ + u32 port; + + if (ppe_cb->ppe_common_cb) { + port = ppe_cb->index; + hns_ppe_srst_by_port(ppe_cb->ppe_common_cb->dsaf_dev, port, 0); + } +} + +void hns_ppe_uninit_ex(struct ppe_common_cb *ppe_common) +{ + u32 i; + + for (i = 0; i < ppe_common->ppe_num; i++) { + hns_ppe_uninit_hw(&ppe_common->ppe_cb[i]); + memset(&ppe_common->ppe_cb[i], 0, sizeof(struct hns_ppe_cb)); + } +} + +void hns_ppe_uninit(struct dsaf_device *dsaf_dev) +{ + u32 i; + + for (i = 0; i < HNS_PPE_COM_NUM; i++) { + if (dsaf_dev->ppe_common[i]) + hns_ppe_uninit_ex(dsaf_dev->ppe_common[i]); + hns_rcb_common_free_cfg(dsaf_dev, i); + hns_ppe_common_free_cfg(dsaf_dev, i); + } +} + +/** + * hns_ppe_reset - reinit ppe/rcb hw + * @dsaf_dev: dasf device + * retuen void + */ +void hns_ppe_reset_common(struct dsaf_device *dsaf_dev, u8 ppe_common_index) +{ + u32 i; + int ret; + struct ppe_common_cb *ppe_common; + + ppe_common = dsaf_dev->ppe_common[ppe_common_index]; + ret = hns_ppe_common_init_hw(ppe_common); + if (ret) + return; + + ret = hns_rcb_common_init_hw(dsaf_dev->rcb_common[ppe_common_index]); + if (ret) + return; + + for (i = 0; i < ppe_common->ppe_num; i++) + hns_ppe_init_hw(&ppe_common->ppe_cb[i]); + + hns_rcb_common_init_commit_hw(dsaf_dev->rcb_common[ppe_common_index]); +} + +void hns_ppe_update_stats(struct hns_ppe_cb *ppe_cb) +{ + struct hns_ppe_hw_stats *hw_stats = &ppe_cb->hw_stats; + + hw_stats->rx_pkts_from_sw + += dsaf_read_dev(ppe_cb, PPE_HIS_RX_SW_PKT_CNT_REG); + hw_stats->rx_pkts + += dsaf_read_dev(ppe_cb, PPE_HIS_RX_WR_BD_OK_PKT_CNT_REG); + hw_stats->rx_drop_no_bd + += dsaf_read_dev(ppe_cb, PPE_HIS_RX_PKT_NO_BUF_CNT_REG); + hw_stats->rx_alloc_buf_fail + += dsaf_read_dev(ppe_cb, PPE_HIS_RX_APP_BUF_FAIL_CNT_REG); + hw_stats->rx_alloc_buf_wait + += dsaf_read_dev(ppe_cb, PPE_HIS_RX_APP_BUF_WAIT_CNT_REG); + hw_stats->rx_drop_no_buf + += dsaf_read_dev(ppe_cb, PPE_HIS_RX_PKT_DROP_FUL_CNT_REG); + hw_stats->rx_err_fifo_full + += dsaf_read_dev(ppe_cb, PPE_HIS_RX_PKT_DROP_PRT_CNT_REG); + + hw_stats->tx_bd_form_rcb + += dsaf_read_dev(ppe_cb, PPE_HIS_TX_BD_CNT_REG); + hw_stats->tx_pkts_from_rcb + += dsaf_read_dev(ppe_cb, PPE_HIS_TX_PKT_CNT_REG); + hw_stats->tx_pkts + += dsaf_read_dev(ppe_cb, PPE_HIS_TX_PKT_OK_CNT_REG); + hw_stats->tx_err_fifo_empty + += dsaf_read_dev(ppe_cb, PPE_HIS_TX_PKT_EPT_CNT_REG); + hw_stats->tx_err_checksum + += dsaf_read_dev(ppe_cb, PPE_HIS_TX_PKT_CS_FAIL_CNT_REG); +} + +int hns_ppe_get_sset_count(int stringset) +{ + if (stringset == ETH_SS_STATS) + return ETH_PPE_STATIC_NUM; + return 0; +} + +int hns_ppe_get_regs_count(void) +{ + return ETH_PPE_DUMP_NUM; +} + +/** + * ppe_get_strings - get ppe srting + * @ppe_device: ppe device + * @stringset: string set type + * @data: output string + */ +void hns_ppe_get_strings(struct hns_ppe_cb *ppe_cb, int stringset, u8 *data) +{ + char *buff = (char *)data; + int index = ppe_cb->index; + + snprintf(buff, ETH_GSTRING_LEN, "ppe%d_rx_sw_pkt", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "ppe%d_rx_pkt_ok", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "ppe%d_rx_drop_pkt_no_bd", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "ppe%d_rx_alloc_buf_fail", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "ppe%d_rx_alloc_buf_wait", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "ppe%d_rx_pkt_drop_no_buf", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "ppe%d_rx_pkt_err_fifo_full", index); + buff = buff + ETH_GSTRING_LEN; + + snprintf(buff, ETH_GSTRING_LEN, "ppe%d_tx_bd", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "ppe%d_tx_pkt", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "ppe%d_tx_pkt_ok", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "ppe%d_tx_pkt_err_fifo_empty", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "ppe%d_tx_pkt_err_csum_fail", index); +} + +void hns_ppe_get_stats(struct hns_ppe_cb *ppe_cb, u64 *data) +{ + u64 *regs_buff = data; + struct hns_ppe_hw_stats *hw_stats = &ppe_cb->hw_stats; + + regs_buff[0] = hw_stats->rx_pkts_from_sw; + regs_buff[1] = hw_stats->rx_pkts; + regs_buff[2] = hw_stats->rx_drop_no_bd; + regs_buff[3] = hw_stats->rx_alloc_buf_fail; + regs_buff[4] = hw_stats->rx_alloc_buf_wait; + regs_buff[5] = hw_stats->rx_drop_no_buf; + regs_buff[6] = hw_stats->rx_err_fifo_full; + + regs_buff[7] = hw_stats->tx_bd_form_rcb; + regs_buff[8] = hw_stats->tx_pkts_from_rcb; + regs_buff[9] = hw_stats->tx_pkts; + regs_buff[10] = hw_stats->tx_err_fifo_empty; + regs_buff[11] = hw_stats->tx_err_checksum; +} + +/** + * hns_ppe_init - init ppe device + * @dsaf_dev: dasf device + * retuen 0 - success , negative --fail + */ +int hns_ppe_init(struct dsaf_device *dsaf_dev) +{ + int i, k; + int ret; + + for (i = 0; i < HNS_PPE_COM_NUM; i++) { + ret = hns_ppe_common_get_cfg(dsaf_dev, i); + if (ret) + goto get_ppe_cfg_fail; + + ret = hns_rcb_common_get_cfg(dsaf_dev, i); + if (ret) + goto get_rcb_cfg_fail; + + hns_ppe_get_cfg(dsaf_dev->ppe_common[i]); + + hns_rcb_get_cfg(dsaf_dev->rcb_common[i]); + } + + for (i = 0; i < HNS_PPE_COM_NUM; i++) + hns_ppe_reset_common(dsaf_dev, i); + + return 0; + +get_rcb_cfg_fail: + hns_ppe_common_free_cfg(dsaf_dev, i); +get_ppe_cfg_fail: + for (k = i - 1; k >= 0; k--) { + hns_rcb_common_free_cfg(dsaf_dev, k); + hns_ppe_common_free_cfg(dsaf_dev, k); + } + return ret; +} + +void hns_ppe_get_regs(struct hns_ppe_cb *ppe_cb, void *data) +{ + struct ppe_common_cb *ppe_common = ppe_cb->ppe_common_cb; + u32 *regs = data; + u32 i; + u32 offset; + + /* ppe common registers */ + regs[0] = dsaf_read_dev(ppe_common, PPE_COM_CFG_QID_MODE_REG); + regs[1] = dsaf_read_dev(ppe_common, PPE_COM_INTEN_REG); + regs[2] = dsaf_read_dev(ppe_common, PPE_COM_RINT_REG); + regs[3] = dsaf_read_dev(ppe_common, PPE_COM_INTSTS_REG); + regs[4] = dsaf_read_dev(ppe_common, PPE_COM_COMMON_CNT_CLR_CE_REG); + + for (i = 0; i < DSAF_TOTAL_QUEUE_NUM; i++) { + offset = PPE_COM_HIS_RX_PKT_QID_DROP_CNT_REG + 0x4 * i; + regs[5 + i] = dsaf_read_dev(ppe_common, offset); + offset = PPE_COM_HIS_RX_PKT_QID_OK_CNT_REG + 0x4 * i; + regs[5 + i + DSAF_TOTAL_QUEUE_NUM] + = dsaf_read_dev(ppe_common, offset); + offset = PPE_COM_HIS_TX_PKT_QID_ERR_CNT_REG + 0x4 * i; + regs[5 + i + DSAF_TOTAL_QUEUE_NUM * 2] + = dsaf_read_dev(ppe_common, offset); + offset = PPE_COM_HIS_TX_PKT_QID_OK_CNT_REG + 0x4 * i; + regs[5 + i + DSAF_TOTAL_QUEUE_NUM * 3] + = dsaf_read_dev(ppe_common, offset); + } + + /* mark end of ppe regs */ + for (i = 521; i < 524; i++) + regs[i] = 0xeeeeeeee; + + /* ppe channel registers */ + regs[525] = dsaf_read_dev(ppe_cb, PPE_CFG_TX_FIFO_THRSLD_REG); + regs[526] = dsaf_read_dev(ppe_cb, PPE_CFG_RX_FIFO_THRSLD_REG); + regs[527] = dsaf_read_dev(ppe_cb, PPE_CFG_RX_FIFO_PAUSE_THRSLD_REG); + regs[528] = dsaf_read_dev(ppe_cb, PPE_CFG_RX_FIFO_SW_BP_THRSLD_REG); + regs[529] = dsaf_read_dev(ppe_cb, PPE_CFG_PAUSE_IDLE_CNT_REG); + regs[530] = dsaf_read_dev(ppe_cb, PPE_CFG_BUS_CTRL_REG); + regs[531] = dsaf_read_dev(ppe_cb, PPE_CFG_TNL_TO_BE_RST_REG); + regs[532] = dsaf_read_dev(ppe_cb, PPE_CURR_TNL_CAN_RST_REG); + + regs[533] = dsaf_read_dev(ppe_cb, PPE_CFG_XGE_MODE_REG); + regs[534] = dsaf_read_dev(ppe_cb, PPE_CFG_MAX_FRAME_LEN_REG); + regs[535] = dsaf_read_dev(ppe_cb, PPE_CFG_RX_PKT_MODE_REG); + regs[536] = dsaf_read_dev(ppe_cb, PPE_CFG_RX_VLAN_TAG_REG); + regs[537] = dsaf_read_dev(ppe_cb, PPE_CFG_TAG_GEN_REG); + regs[538] = dsaf_read_dev(ppe_cb, PPE_CFG_PARSE_TAG_REG); + regs[539] = dsaf_read_dev(ppe_cb, PPE_CFG_PRO_CHECK_EN_REG); + + regs[540] = dsaf_read_dev(ppe_cb, PPE_INTEN_REG); + regs[541] = dsaf_read_dev(ppe_cb, PPE_RINT_REG); + regs[542] = dsaf_read_dev(ppe_cb, PPE_INTSTS_REG); + regs[543] = dsaf_read_dev(ppe_cb, PPE_CFG_RX_PKT_INT_REG); + + regs[544] = dsaf_read_dev(ppe_cb, PPE_CFG_HEAT_DECT_TIME0_REG); + regs[545] = dsaf_read_dev(ppe_cb, PPE_CFG_HEAT_DECT_TIME1_REG); + + /* ppe static */ + regs[546] = dsaf_read_dev(ppe_cb, PPE_HIS_RX_SW_PKT_CNT_REG); + regs[547] = dsaf_read_dev(ppe_cb, PPE_HIS_RX_WR_BD_OK_PKT_CNT_REG); + regs[548] = dsaf_read_dev(ppe_cb, PPE_HIS_RX_PKT_NO_BUF_CNT_REG); + regs[549] = dsaf_read_dev(ppe_cb, PPE_HIS_TX_BD_CNT_REG); + regs[550] = dsaf_read_dev(ppe_cb, PPE_HIS_TX_PKT_CNT_REG); + regs[551] = dsaf_read_dev(ppe_cb, PPE_HIS_TX_PKT_OK_CNT_REG); + regs[552] = dsaf_read_dev(ppe_cb, PPE_HIS_TX_PKT_EPT_CNT_REG); + regs[553] = dsaf_read_dev(ppe_cb, PPE_HIS_TX_PKT_CS_FAIL_CNT_REG); + regs[554] = dsaf_read_dev(ppe_cb, PPE_HIS_RX_APP_BUF_FAIL_CNT_REG); + regs[555] = dsaf_read_dev(ppe_cb, PPE_HIS_RX_APP_BUF_WAIT_CNT_REG); + regs[556] = dsaf_read_dev(ppe_cb, PPE_HIS_RX_PKT_DROP_FUL_CNT_REG); + regs[557] = dsaf_read_dev(ppe_cb, PPE_HIS_RX_PKT_DROP_PRT_CNT_REG); + + regs[558] = dsaf_read_dev(ppe_cb, PPE_TNL_0_5_CNT_CLR_CE_REG); + regs[559] = dsaf_read_dev(ppe_cb, PPE_CFG_AXI_DBG_REG); + regs[560] = dsaf_read_dev(ppe_cb, PPE_HIS_PRO_ERR_REG); + regs[561] = dsaf_read_dev(ppe_cb, PPE_HIS_TNL_FIFO_ERR_REG); + regs[562] = dsaf_read_dev(ppe_cb, PPE_CURR_CFF_DATA_NUM_REG); + regs[563] = dsaf_read_dev(ppe_cb, PPE_CURR_RX_ST_REG); + regs[564] = dsaf_read_dev(ppe_cb, PPE_CURR_TX_ST_REG); + regs[565] = dsaf_read_dev(ppe_cb, PPE_CURR_RX_FIFO0_REG); + regs[566] = dsaf_read_dev(ppe_cb, PPE_CURR_RX_FIFO1_REG); + regs[567] = dsaf_read_dev(ppe_cb, PPE_CURR_TX_FIFO0_REG); + regs[568] = dsaf_read_dev(ppe_cb, PPE_CURR_TX_FIFO1_REG); + regs[569] = dsaf_read_dev(ppe_cb, PPE_ECO0_REG); + regs[570] = dsaf_read_dev(ppe_cb, PPE_ECO1_REG); + regs[571] = dsaf_read_dev(ppe_cb, PPE_ECO2_REG); + + /* mark end of ppe regs */ + for (i = 572; i < 576; i++) + regs[i] = 0xeeeeeeee; +} diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h new file mode 100644 index 000000000000..4894f9a0d39f --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * 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 _HNS_DSAF_PPE_H +#define _HNS_DSAF_PPE_H + +#include <linux/platform_device.h> + +#include "hns_dsaf_main.h" +#include "hns_dsaf_mac.h" +#include "hns_dsaf_rcb.h" + +#define HNS_PPE_SERVICE_NW_ENGINE_NUM DSAF_COMM_CHN +#define HNS_PPE_DEBUG_NW_ENGINE_NUM 1 +#define HNS_PPE_COM_NUM DSAF_COMM_DEV_NUM + +#define PPE_COMMON_REG_OFFSET 0x70000 +#define PPE_REG_OFFSET 0x10000 + +#define ETH_PPE_DUMP_NUM 576 +#define ETH_PPE_STATIC_NUM 12 +enum ppe_qid_mode { + PPE_QID_MODE0 = 0, /* fixed queue id mode */ + PPE_QID_MODE1, /* switch:128VM non switch:6Port/4VM/4TC */ + PPE_QID_MODE2, /* switch:32VM/4TC non switch:6Port/16VM */ + PPE_QID_MODE3, /* switch:4TC/8TAG non switch:2Port/64VM */ + PPE_QID_MODE4, /* switch:8VM/16TAG non switch:2Port/16VM/4TC */ + PPE_QID_MODE5, /* non switch:6Port/16TAG */ + PPE_QID_MODE6, /* non switch:6Port/2VM/8TC */ + PPE_QID_MODE7, /* non switch:2Port/8VM/8TC */ +}; + +enum ppe_port_mode { + PPE_MODE_GE = 0, + PPE_MODE_XGE, +}; + +enum ppe_common_mode { + PPE_COMMON_MODE_DEBUG = 0, + PPE_COMMON_MODE_SERVICE, + PPE_COMMON_MODE_MAX +}; + +struct hns_ppe_hw_stats { + u64 rx_pkts_from_sw; + u64 rx_pkts; + u64 rx_drop_no_bd; + u64 rx_alloc_buf_fail; + u64 rx_alloc_buf_wait; + u64 rx_drop_no_buf; + u64 rx_err_fifo_full; + u64 tx_bd_form_rcb; + u64 tx_pkts_from_rcb; + u64 tx_pkts; + u64 tx_err_fifo_empty; + u64 tx_err_checksum; +}; + +struct hns_ppe_cb { + struct device *dev; + struct hns_ppe_cb *next; /* pointer to next ppe device */ + struct ppe_common_cb *ppe_common_cb; /* belong to */ + struct hns_ppe_hw_stats hw_stats; + + u8 index; /* index in a ppe common device */ + u8 port; /* port id in dsaf */ + void __iomem *io_base; + int virq; +}; + +struct ppe_common_cb { + struct device *dev; + struct dsaf_device *dsaf_dev; + void __iomem *io_base; + + enum ppe_common_mode ppe_mode; + + u8 comm_index; /*ppe_common index*/ + + u32 ppe_num; + struct hns_ppe_cb ppe_cb[0]; + +}; + +int hns_ppe_init(struct dsaf_device *dsaf_dev); + +void hns_ppe_uninit(struct dsaf_device *dsaf_dev); + +void hns_ppe_reset_common(struct dsaf_device *dsaf_dev, u8 ppe_common_index); + +void hns_ppe_update_stats(struct hns_ppe_cb *ppe_cb); + +int hns_ppe_get_sset_count(int stringset); +int hns_ppe_get_regs_count(void); +void hns_ppe_get_regs(struct hns_ppe_cb *ppe_cb, void *data); + +void hns_ppe_get_strings(struct hns_ppe_cb *ppe_cb, int stringset, u8 *data); +void hns_ppe_get_stats(struct hns_ppe_cb *ppe_cb, u64 *data); +#endif /* _HNS_DSAF_PPE_H */ diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c new file mode 100644 index 000000000000..50f3427709d9 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c @@ -0,0 +1,1023 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * 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/cdev.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <asm/cacheflush.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/of_irq.h> +#include <linux/spinlock.h> + +#include "hns_dsaf_main.h" +#include "hns_dsaf_ppe.h" +#include "hns_dsaf_rcb.h" + +#define RCB_COMMON_REG_OFFSET 0x80000 +#define TX_RING 0 +#define RX_RING 1 + +#define RCB_RESET_WAIT_TIMES 30 +#define RCB_RESET_TRY_TIMES 10 + +/** + *hns_rcb_wait_fbd_clean - clean fbd + *@qs: ring struct pointer array + *@qnum: num of array + *@flag: tx or rx flag + */ +void hns_rcb_wait_fbd_clean(struct hnae_queue **qs, int q_num, u32 flag) +{ + int i, wait_cnt; + u32 fbd_num; + + for (wait_cnt = i = 0; i < q_num; wait_cnt++) { + usleep_range(200, 300); + fbd_num = 0; + if (flag & RCB_INT_FLAG_TX) + fbd_num += dsaf_read_dev(qs[i], + RCB_RING_TX_RING_FBDNUM_REG); + if (flag & RCB_INT_FLAG_RX) + fbd_num += dsaf_read_dev(qs[i], + RCB_RING_RX_RING_FBDNUM_REG); + if (!fbd_num) + i++; + if (wait_cnt >= 10000) + break; + } + + if (i < q_num) + dev_err(qs[i]->handle->owner_dev, + "queue(%d) wait fbd(%d) clean fail!!\n", i, fbd_num); +} + +/** + *hns_rcb_reset_ring_hw - ring reset + *@q: ring struct pointer + */ +void hns_rcb_reset_ring_hw(struct hnae_queue *q) +{ + u32 wait_cnt; + u32 try_cnt = 0; + u32 could_ret; + + u32 tx_fbd_num; + + while (try_cnt++ < RCB_RESET_TRY_TIMES) { + usleep_range(100, 200); + tx_fbd_num = dsaf_read_dev(q, RCB_RING_TX_RING_FBDNUM_REG); + if (tx_fbd_num) + continue; + + dsaf_write_dev(q, RCB_RING_PREFETCH_EN_REG, 0); + + dsaf_write_dev(q, RCB_RING_T0_BE_RST, 1); + + msleep(20); + could_ret = dsaf_read_dev(q, RCB_RING_COULD_BE_RST); + + wait_cnt = 0; + while (!could_ret && (wait_cnt < RCB_RESET_WAIT_TIMES)) { + dsaf_write_dev(q, RCB_RING_T0_BE_RST, 0); + + dsaf_write_dev(q, RCB_RING_T0_BE_RST, 1); + + msleep(20); + could_ret = dsaf_read_dev(q, RCB_RING_COULD_BE_RST); + + wait_cnt++; + } + + dsaf_write_dev(q, RCB_RING_T0_BE_RST, 0); + + if (could_ret) + break; + } + + if (try_cnt >= RCB_RESET_TRY_TIMES) + dev_err(q->dev->dev, "port%d reset ring fail\n", + hns_ae_get_vf_cb(q->handle)->port_index); +} + +/** + *hns_rcb_int_ctrl_hw - rcb irq enable control + *@q: hnae queue struct pointer + *@flag:ring flag tx or rx + *@mask:mask + */ +void hns_rcb_int_ctrl_hw(struct hnae_queue *q, u32 flag, u32 mask) +{ + u32 int_mask_en = !!mask; + + if (flag & RCB_INT_FLAG_TX) { + dsaf_write_dev(q, RCB_RING_INTMSK_TXWL_REG, int_mask_en); + dsaf_write_dev(q, RCB_RING_INTMSK_TX_OVERTIME_REG, + int_mask_en); + } + + if (flag & RCB_INT_FLAG_RX) { + dsaf_write_dev(q, RCB_RING_INTMSK_RXWL_REG, int_mask_en); + dsaf_write_dev(q, RCB_RING_INTMSK_RX_OVERTIME_REG, + int_mask_en); + } +} + +void hns_rcb_int_clr_hw(struct hnae_queue *q, u32 flag) +{ + u32 clr = 1; + + if (flag & RCB_INT_FLAG_TX) { + dsaf_write_dev(q, RCB_RING_INTSTS_TX_RING_REG, clr); + dsaf_write_dev(q, RCB_RING_INTSTS_TX_OVERTIME_REG, clr); + } + + if (flag & RCB_INT_FLAG_RX) { + dsaf_write_dev(q, RCB_RING_INTSTS_RX_RING_REG, clr); + dsaf_write_dev(q, RCB_RING_INTSTS_RX_OVERTIME_REG, clr); + } +} + +/** + *hns_rcb_ring_enable_hw - enable ring + *@ring: rcb ring + */ +void hns_rcb_ring_enable_hw(struct hnae_queue *q, u32 val) +{ + dsaf_write_dev(q, RCB_RING_PREFETCH_EN_REG, !!val); +} + +void hns_rcb_start(struct hnae_queue *q, u32 val) +{ + hns_rcb_ring_enable_hw(q, val); +} + +/** + *hns_rcb_common_init_commit_hw - make rcb common init completed + *@rcb_common: rcb common device + */ +void hns_rcb_common_init_commit_hw(struct rcb_common_cb *rcb_common) +{ + wmb(); /* Sync point before breakpoint */ + dsaf_write_dev(rcb_common, RCB_COM_CFG_SYS_FSH_REG, 1); + wmb(); /* Sync point after breakpoint */ +} + +/** + *hns_rcb_ring_init - init rcb ring + *@ring_pair: ring pair control block + *@ring_type: ring type, RX_RING or TX_RING + */ +static void hns_rcb_ring_init(struct ring_pair_cb *ring_pair, int ring_type) +{ + struct hnae_queue *q = &ring_pair->q; + struct rcb_common_cb *rcb_common = ring_pair->rcb_common; + u32 bd_size_type = rcb_common->dsaf_dev->buf_size_type; + struct hnae_ring *ring = + (ring_type == RX_RING) ? &q->rx_ring : &q->tx_ring; + dma_addr_t dma = ring->desc_dma_addr; + + if (ring_type == RX_RING) { + dsaf_write_dev(q, RCB_RING_RX_RING_BASEADDR_L_REG, + (u32)dma); + dsaf_write_dev(q, RCB_RING_RX_RING_BASEADDR_H_REG, + (u32)(dma >> 32)); + + dsaf_write_dev(q, RCB_RING_RX_RING_BD_LEN_REG, + bd_size_type); + dsaf_write_dev(q, RCB_RING_RX_RING_BD_NUM_REG, + ring_pair->port_id_in_dsa); + dsaf_write_dev(q, RCB_RING_RX_RING_PKTLINE_REG, + ring_pair->port_id_in_dsa); + } else { + dsaf_write_dev(q, RCB_RING_TX_RING_BASEADDR_L_REG, + (u32)dma); + dsaf_write_dev(q, RCB_RING_TX_RING_BASEADDR_H_REG, + (u32)(dma >> 32)); + + dsaf_write_dev(q, RCB_RING_TX_RING_BD_LEN_REG, + bd_size_type); + dsaf_write_dev(q, RCB_RING_TX_RING_BD_NUM_REG, + ring_pair->port_id_in_dsa); + dsaf_write_dev(q, RCB_RING_TX_RING_PKTLINE_REG, + ring_pair->port_id_in_dsa); + } +} + +/** + *hns_rcb_init_hw - init rcb hardware + *@ring: rcb ring + */ +void hns_rcb_init_hw(struct ring_pair_cb *ring) +{ + hns_rcb_ring_init(ring, RX_RING); + hns_rcb_ring_init(ring, TX_RING); +} + +/** + *hns_rcb_set_port_desc_cnt - set rcb port description num + *@rcb_common: rcb_common device + *@port_idx:port index + *@desc_cnt:BD num + */ +static void hns_rcb_set_port_desc_cnt(struct rcb_common_cb *rcb_common, + u32 port_idx, u32 desc_cnt) +{ + if (port_idx >= HNS_RCB_SERVICE_NW_ENGINE_NUM) + port_idx = 0; + + dsaf_write_dev(rcb_common, RCB_CFG_BD_NUM_REG + port_idx * 4, + desc_cnt); +} + +/** + *hns_rcb_set_port_coalesced_frames - set rcb port coalesced frames + *@rcb_common: rcb_common device + *@port_idx:port index + *@coalesced_frames:BD num for coalesced frames + */ +static int hns_rcb_set_port_coalesced_frames(struct rcb_common_cb *rcb_common, + u32 port_idx, + u32 coalesced_frames) +{ + if (port_idx >= HNS_RCB_SERVICE_NW_ENGINE_NUM) + port_idx = 0; + if (coalesced_frames >= rcb_common->desc_num || + coalesced_frames > HNS_RCB_MAX_COALESCED_FRAMES) + return -EINVAL; + + dsaf_write_dev(rcb_common, RCB_CFG_PKTLINE_REG + port_idx * 4, + coalesced_frames); + return 0; +} + +/** + *hns_rcb_get_port_coalesced_frames - set rcb port coalesced frames + *@rcb_common: rcb_common device + *@port_idx:port index + * return coaleseced frames value + */ +static u32 hns_rcb_get_port_coalesced_frames(struct rcb_common_cb *rcb_common, + u32 port_idx) +{ + if (port_idx >= HNS_RCB_SERVICE_NW_ENGINE_NUM) + port_idx = 0; + + return dsaf_read_dev(rcb_common, + RCB_CFG_PKTLINE_REG + port_idx * 4); +} + +/** + *hns_rcb_set_timeout - set rcb port coalesced time_out + *@rcb_common: rcb_common device + *@time_out:time for coalesced time_out + */ +static void hns_rcb_set_timeout(struct rcb_common_cb *rcb_common, + u32 timeout) +{ + dsaf_write_dev(rcb_common, RCB_CFG_OVERTIME_REG, timeout); +} + +static int hns_rcb_common_get_port_num(struct rcb_common_cb *rcb_common) +{ + if (rcb_common->comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) + return HNS_RCB_SERVICE_NW_ENGINE_NUM; + else + return HNS_RCB_DEBUG_NW_ENGINE_NUM; +} + +/*clr rcb comm exception irq**/ +static void hns_rcb_comm_exc_irq_en( + struct rcb_common_cb *rcb_common, int en) +{ + u32 clr_vlue = 0xfffffffful; + u32 msk_vlue = en ? 0 : 0xfffffffful; + + /* clr int*/ + dsaf_write_dev(rcb_common, RCB_COM_INTSTS_ECC_ERR_REG, clr_vlue); + + dsaf_write_dev(rcb_common, RCB_COM_SF_CFG_RING_STS, clr_vlue); + + dsaf_write_dev(rcb_common, RCB_COM_SF_CFG_BD_RINT_STS, clr_vlue); + + dsaf_write_dev(rcb_common, RCB_COM_RINT_TX_PKT_REG, clr_vlue); + dsaf_write_dev(rcb_common, RCB_COM_AXI_ERR_STS, clr_vlue); + + /*en msk*/ + dsaf_write_dev(rcb_common, RCB_COM_INTMASK_ECC_ERR_REG, msk_vlue); + + dsaf_write_dev(rcb_common, RCB_COM_SF_CFG_INTMASK_RING, msk_vlue); + + /*for tx bd neednot cacheline, so msk sf_txring_fbd_intmask (bit 1)**/ + dsaf_write_dev(rcb_common, RCB_COM_SF_CFG_INTMASK_BD, msk_vlue | 2); + + dsaf_write_dev(rcb_common, RCB_COM_INTMSK_TX_PKT_REG, msk_vlue); + dsaf_write_dev(rcb_common, RCB_COM_AXI_WR_ERR_INTMASK, msk_vlue); +} + +/** + *hns_rcb_common_init_hw - init rcb common hardware + *@rcb_common: rcb_common device + *retuen 0 - success , negative --fail + */ +int hns_rcb_common_init_hw(struct rcb_common_cb *rcb_common) +{ + u32 reg_val; + int i; + int port_num = hns_rcb_common_get_port_num(rcb_common); + + hns_rcb_comm_exc_irq_en(rcb_common, 0); + + reg_val = dsaf_read_dev(rcb_common, RCB_COM_CFG_INIT_FLAG_REG); + if (0x1 != (reg_val & 0x1)) { + dev_err(rcb_common->dsaf_dev->dev, + "RCB_COM_CFG_INIT_FLAG_REG reg = 0x%x\n", reg_val); + return -EBUSY; + } + + for (i = 0; i < port_num; i++) { + hns_rcb_set_port_desc_cnt(rcb_common, i, rcb_common->desc_num); + (void)hns_rcb_set_port_coalesced_frames( + rcb_common, i, rcb_common->coalesced_frames); + } + hns_rcb_set_timeout(rcb_common, rcb_common->timeout); + + dsaf_write_dev(rcb_common, RCB_COM_CFG_ENDIAN_REG, + HNS_RCB_COMMON_ENDIAN); + + return 0; +} + +int hns_rcb_buf_size2type(u32 buf_size) +{ + int bd_size_type; + + switch (buf_size) { + case 512: + bd_size_type = HNS_BD_SIZE_512_TYPE; + break; + case 1024: + bd_size_type = HNS_BD_SIZE_1024_TYPE; + break; + case 2048: + bd_size_type = HNS_BD_SIZE_2048_TYPE; + break; + case 4096: + bd_size_type = HNS_BD_SIZE_4096_TYPE; + break; + default: + bd_size_type = -EINVAL; + } + + return bd_size_type; +} + +static void hns_rcb_ring_get_cfg(struct hnae_queue *q, int ring_type) +{ + struct hnae_ring *ring; + struct rcb_common_cb *rcb_common; + struct ring_pair_cb *ring_pair_cb; + u32 buf_size; + u16 desc_num; + int irq_idx; + + ring_pair_cb = container_of(q, struct ring_pair_cb, q); + if (ring_type == RX_RING) { + ring = &q->rx_ring; + ring->io_base = ring_pair_cb->q.io_base; + irq_idx = HNS_RCB_IRQ_IDX_RX; + } else { + ring = &q->tx_ring; + ring->io_base = (u8 __iomem *)ring_pair_cb->q.io_base + + HNS_RCB_TX_REG_OFFSET; + irq_idx = HNS_RCB_IRQ_IDX_TX; + } + + rcb_common = ring_pair_cb->rcb_common; + buf_size = rcb_common->dsaf_dev->buf_size; + desc_num = rcb_common->dsaf_dev->desc_num; + + ring->desc = NULL; + ring->desc_cb = NULL; + + ring->irq = ring_pair_cb->virq[irq_idx]; + ring->desc_dma_addr = 0; + + ring->buf_size = buf_size; + ring->desc_num = desc_num; + ring->max_desc_num_per_pkt = HNS_RCB_RING_MAX_BD_PER_PKT; + ring->max_raw_data_sz_per_desc = HNS_RCB_MAX_PKT_SIZE; + ring->max_pkt_size = HNS_RCB_MAX_PKT_SIZE; + ring->next_to_use = 0; + ring->next_to_clean = 0; +} + +static void hns_rcb_ring_pair_get_cfg(struct ring_pair_cb *ring_pair_cb) +{ + ring_pair_cb->q.handle = NULL; + + hns_rcb_ring_get_cfg(&ring_pair_cb->q, RX_RING); + hns_rcb_ring_get_cfg(&ring_pair_cb->q, TX_RING); +} + +static int hns_rcb_get_port(struct rcb_common_cb *rcb_common, int ring_idx) +{ + int comm_index = rcb_common->comm_index; + int port; + int q_num; + + if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) { + q_num = (int)rcb_common->max_q_per_vf * rcb_common->max_vfn; + port = ring_idx / q_num; + } else { + port = HNS_RCB_SERVICE_NW_ENGINE_NUM + comm_index - 1; + } + + return port; +} + +static int hns_rcb_get_base_irq_idx(struct rcb_common_cb *rcb_common) +{ + int comm_index = rcb_common->comm_index; + + if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) + return HNS_SERVICE_RING_IRQ_IDX; + else + return HNS_DEBUG_RING_IRQ_IDX + (comm_index - 1) * 2; +} + +#define RCB_COMM_BASE_TO_RING_BASE(base, ringid)\ + ((base) + 0x10000 + HNS_RCB_REG_OFFSET * (ringid)) +/** + *hns_rcb_get_cfg - get rcb config + *@rcb_common: rcb common device + */ +void hns_rcb_get_cfg(struct rcb_common_cb *rcb_common) +{ + struct ring_pair_cb *ring_pair_cb; + u32 i; + u32 ring_num = rcb_common->ring_num; + int base_irq_idx = hns_rcb_get_base_irq_idx(rcb_common); + struct device_node *np = rcb_common->dsaf_dev->dev->of_node; + + for (i = 0; i < ring_num; i++) { + ring_pair_cb = &rcb_common->ring_pair_cb[i]; + ring_pair_cb->rcb_common = rcb_common; + ring_pair_cb->dev = rcb_common->dsaf_dev->dev; + ring_pair_cb->index = i; + ring_pair_cb->q.io_base = + RCB_COMM_BASE_TO_RING_BASE(rcb_common->io_base, i); + ring_pair_cb->port_id_in_dsa = hns_rcb_get_port(rcb_common, i); + ring_pair_cb->virq[HNS_RCB_IRQ_IDX_TX] + = irq_of_parse_and_map(np, base_irq_idx + i * 2); + ring_pair_cb->virq[HNS_RCB_IRQ_IDX_RX] + = irq_of_parse_and_map(np, base_irq_idx + i * 2 + 1); + ring_pair_cb->q.phy_base = + RCB_COMM_BASE_TO_RING_BASE(rcb_common->phy_base, i); + hns_rcb_ring_pair_get_cfg(ring_pair_cb); + } +} + +/** + *hns_rcb_get_coalesced_frames - get rcb port coalesced frames + *@rcb_common: rcb_common device + *@comm_index:port index + *return coalesced_frames + */ +u32 hns_rcb_get_coalesced_frames(struct dsaf_device *dsaf_dev, int port) +{ + int comm_index = hns_dsaf_get_comm_idx_by_port(port); + struct rcb_common_cb *rcb_comm = dsaf_dev->rcb_common[comm_index]; + + return hns_rcb_get_port_coalesced_frames(rcb_comm, port); +} + +/** + *hns_rcb_get_coalesce_usecs - get rcb port coalesced time_out + *@rcb_common: rcb_common device + *@comm_index:port index + *return time_out + */ +u32 hns_rcb_get_coalesce_usecs(struct dsaf_device *dsaf_dev, int comm_index) +{ + struct rcb_common_cb *rcb_comm = dsaf_dev->rcb_common[comm_index]; + + return rcb_comm->timeout; +} + +/** + *hns_rcb_set_coalesce_usecs - set rcb port coalesced time_out + *@rcb_common: rcb_common device + *@comm_index: comm :index + *@etx_usecs:tx time for coalesced time_out + *@rx_usecs:rx time for coalesced time_out + */ +void hns_rcb_set_coalesce_usecs(struct dsaf_device *dsaf_dev, + int port, u32 timeout) +{ + int comm_index = hns_dsaf_get_comm_idx_by_port(port); + struct rcb_common_cb *rcb_comm = dsaf_dev->rcb_common[comm_index]; + + if (rcb_comm->timeout == timeout) + return; + + if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) { + dev_err(dsaf_dev->dev, + "error: not support coalesce_usecs setting!\n"); + return; + } + rcb_comm->timeout = timeout; + hns_rcb_set_timeout(rcb_comm, rcb_comm->timeout); +} + +/** + *hns_rcb_set_coalesced_frames - set rcb coalesced frames + *@rcb_common: rcb_common device + *@tx_frames:tx BD num for coalesced frames + *@rx_frames:rx BD num for coalesced frames + *Return 0 on success, negative on failure + */ +int hns_rcb_set_coalesced_frames(struct dsaf_device *dsaf_dev, + int port, u32 coalesced_frames) +{ + int comm_index = hns_dsaf_get_comm_idx_by_port(port); + struct rcb_common_cb *rcb_comm = dsaf_dev->rcb_common[comm_index]; + u32 coalesced_reg_val; + int ret; + + coalesced_reg_val = hns_rcb_get_port_coalesced_frames(rcb_comm, port); + + if (coalesced_reg_val == coalesced_frames) + return 0; + + if (coalesced_frames >= HNS_RCB_MIN_COALESCED_FRAMES) { + ret = hns_rcb_set_port_coalesced_frames(rcb_comm, port, + coalesced_frames); + return ret; + } else { + return -EINVAL; + } +} + +/** + *hns_rcb_get_queue_mode - get max VM number and max ring number per VM + * accordding to dsaf mode + *@dsaf_mode: dsaf mode + *@max_vfn : max vfn number + *@max_q_per_vf:max ring number per vm + */ +static void hns_rcb_get_queue_mode(enum dsaf_mode dsaf_mode, int comm_index, + u16 *max_vfn, u16 *max_q_per_vf) +{ + if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) { + switch (dsaf_mode) { + case DSAF_MODE_DISABLE_6PORT_0VM: + *max_vfn = 1; + *max_q_per_vf = 16; + break; + case DSAF_MODE_DISABLE_FIX: + *max_vfn = 1; + *max_q_per_vf = 1; + break; + case DSAF_MODE_DISABLE_2PORT_64VM: + *max_vfn = 64; + *max_q_per_vf = 1; + break; + case DSAF_MODE_DISABLE_6PORT_16VM: + *max_vfn = 16; + *max_q_per_vf = 1; + break; + default: + *max_vfn = 1; + *max_q_per_vf = 16; + break; + } + } else { + *max_vfn = 1; + *max_q_per_vf = 1; + } +} + +int hns_rcb_get_ring_num(struct dsaf_device *dsaf_dev, int comm_index) +{ + if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) { + switch (dsaf_dev->dsaf_mode) { + case DSAF_MODE_ENABLE_FIX: + return 1; + + case DSAF_MODE_DISABLE_FIX: + return 6; + + case DSAF_MODE_ENABLE_0VM: + return 32; + + case DSAF_MODE_DISABLE_6PORT_0VM: + case DSAF_MODE_ENABLE_16VM: + case DSAF_MODE_DISABLE_6PORT_2VM: + case DSAF_MODE_DISABLE_6PORT_16VM: + case DSAF_MODE_DISABLE_6PORT_4VM: + case DSAF_MODE_ENABLE_8VM: + return 96; + + case DSAF_MODE_DISABLE_2PORT_16VM: + case DSAF_MODE_DISABLE_2PORT_8VM: + case DSAF_MODE_ENABLE_32VM: + case DSAF_MODE_DISABLE_2PORT_64VM: + case DSAF_MODE_ENABLE_128VM: + return 128; + + default: + dev_warn(dsaf_dev->dev, + "get ring num fail,use default!dsaf_mode=%d\n", + dsaf_dev->dsaf_mode); + return 128; + } + } else { + return 1; + } +} + +void __iomem *hns_rcb_common_get_vaddr(struct dsaf_device *dsaf_dev, + int comm_index) +{ + void __iomem *base_addr; + + if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) + base_addr = dsaf_dev->ppe_base + RCB_COMMON_REG_OFFSET; + else + base_addr = dsaf_dev->sds_base + + (comm_index - 1) * HNS_DSAF_DEBUG_NW_REG_OFFSET + + RCB_COMMON_REG_OFFSET; + + return base_addr; +} + +static phys_addr_t hns_rcb_common_get_paddr(struct dsaf_device *dsaf_dev, + int comm_index) +{ + struct device_node *np = dsaf_dev->dev->of_node; + phys_addr_t phy_addr; + const __be32 *tmp_addr; + u64 addr_offset = 0; + u64 size = 0; + int index = 0; + + if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) { + index = 2; + addr_offset = RCB_COMMON_REG_OFFSET; + } else { + index = 1; + addr_offset = (comm_index - 1) * HNS_DSAF_DEBUG_NW_REG_OFFSET + + RCB_COMMON_REG_OFFSET; + } + tmp_addr = of_get_address(np, index, &size, NULL); + phy_addr = of_translate_address(np, tmp_addr); + return phy_addr + addr_offset; +} + +int hns_rcb_common_get_cfg(struct dsaf_device *dsaf_dev, + int comm_index) +{ + struct rcb_common_cb *rcb_common; + enum dsaf_mode dsaf_mode = dsaf_dev->dsaf_mode; + u16 max_vfn; + u16 max_q_per_vf; + int ring_num = hns_rcb_get_ring_num(dsaf_dev, comm_index); + + rcb_common = + devm_kzalloc(dsaf_dev->dev, sizeof(*rcb_common) + + ring_num * sizeof(struct ring_pair_cb), GFP_KERNEL); + if (!rcb_common) { + dev_err(dsaf_dev->dev, "rcb common devm_kzalloc fail!\n"); + return -ENOMEM; + } + rcb_common->comm_index = comm_index; + rcb_common->ring_num = ring_num; + rcb_common->dsaf_dev = dsaf_dev; + + rcb_common->desc_num = dsaf_dev->desc_num; + rcb_common->coalesced_frames = HNS_RCB_DEF_COALESCED_FRAMES; + rcb_common->timeout = HNS_RCB_MAX_TIME_OUT; + + hns_rcb_get_queue_mode(dsaf_mode, comm_index, &max_vfn, &max_q_per_vf); + rcb_common->max_vfn = max_vfn; + rcb_common->max_q_per_vf = max_q_per_vf; + + rcb_common->io_base = hns_rcb_common_get_vaddr(dsaf_dev, comm_index); + rcb_common->phy_base = hns_rcb_common_get_paddr(dsaf_dev, comm_index); + + dsaf_dev->rcb_common[comm_index] = rcb_common; + return 0; +} + +void hns_rcb_common_free_cfg(struct dsaf_device *dsaf_dev, + u32 comm_index) +{ + dsaf_dev->rcb_common[comm_index] = NULL; +} + +void hns_rcb_update_stats(struct hnae_queue *queue) +{ + struct ring_pair_cb *ring = + container_of(queue, struct ring_pair_cb, q); + struct dsaf_device *dsaf_dev = ring->rcb_common->dsaf_dev; + struct ppe_common_cb *ppe_common + = dsaf_dev->ppe_common[ring->rcb_common->comm_index]; + struct hns_ring_hw_stats *hw_stats = &ring->hw_stats; + + hw_stats->rx_pkts += dsaf_read_dev(queue, + RCB_RING_RX_RING_PKTNUM_RECORD_REG); + dsaf_write_dev(queue, RCB_RING_RX_RING_PKTNUM_RECORD_REG, 0x1); + + hw_stats->ppe_rx_ok_pkts += dsaf_read_dev(ppe_common, + PPE_COM_HIS_RX_PKT_QID_OK_CNT_REG + 4 * ring->index); + hw_stats->ppe_rx_drop_pkts += dsaf_read_dev(ppe_common, + PPE_COM_HIS_RX_PKT_QID_DROP_CNT_REG + 4 * ring->index); + + hw_stats->tx_pkts += dsaf_read_dev(queue, + RCB_RING_TX_RING_PKTNUM_RECORD_REG); + dsaf_write_dev(queue, RCB_RING_TX_RING_PKTNUM_RECORD_REG, 0x1); + + hw_stats->ppe_tx_ok_pkts += dsaf_read_dev(ppe_common, + PPE_COM_HIS_TX_PKT_QID_OK_CNT_REG + 4 * ring->index); + hw_stats->ppe_tx_drop_pkts += dsaf_read_dev(ppe_common, + PPE_COM_HIS_TX_PKT_QID_ERR_CNT_REG + 4 * ring->index); +} + +/** + *hns_rcb_get_stats - get rcb statistic + *@ring: rcb ring + *@data:statistic value + */ +void hns_rcb_get_stats(struct hnae_queue *queue, u64 *data) +{ + u64 *regs_buff = data; + struct ring_pair_cb *ring = + container_of(queue, struct ring_pair_cb, q); + struct hns_ring_hw_stats *hw_stats = &ring->hw_stats; + + regs_buff[0] = hw_stats->tx_pkts; + regs_buff[1] = hw_stats->ppe_tx_ok_pkts; + regs_buff[2] = hw_stats->ppe_tx_drop_pkts; + regs_buff[3] = + dsaf_read_dev(queue, RCB_RING_TX_RING_FBDNUM_REG); + + regs_buff[4] = queue->tx_ring.stats.tx_pkts; + regs_buff[5] = queue->tx_ring.stats.tx_bytes; + regs_buff[6] = queue->tx_ring.stats.tx_err_cnt; + regs_buff[7] = queue->tx_ring.stats.io_err_cnt; + regs_buff[8] = queue->tx_ring.stats.sw_err_cnt; + regs_buff[9] = queue->tx_ring.stats.seg_pkt_cnt; + regs_buff[10] = queue->tx_ring.stats.restart_queue; + regs_buff[11] = queue->tx_ring.stats.tx_busy; + + regs_buff[12] = hw_stats->rx_pkts; + regs_buff[13] = hw_stats->ppe_rx_ok_pkts; + regs_buff[14] = hw_stats->ppe_rx_drop_pkts; + regs_buff[15] = + dsaf_read_dev(queue, RCB_RING_RX_RING_FBDNUM_REG); + + regs_buff[16] = queue->rx_ring.stats.rx_pkts; + regs_buff[17] = queue->rx_ring.stats.rx_bytes; + regs_buff[18] = queue->rx_ring.stats.rx_err_cnt; + regs_buff[19] = queue->rx_ring.stats.io_err_cnt; + regs_buff[20] = queue->rx_ring.stats.sw_err_cnt; + regs_buff[21] = queue->rx_ring.stats.seg_pkt_cnt; + regs_buff[22] = queue->rx_ring.stats.reuse_pg_cnt; + regs_buff[23] = queue->rx_ring.stats.err_pkt_len; + regs_buff[24] = queue->rx_ring.stats.non_vld_descs; + regs_buff[25] = queue->rx_ring.stats.err_bd_num; + regs_buff[26] = queue->rx_ring.stats.l2_err; + regs_buff[27] = queue->rx_ring.stats.l3l4_csum_err; +} + +/** + *hns_rcb_get_ring_sset_count - rcb string set count + *@stringset:ethtool cmd + *return rcb ring string set count + */ +int hns_rcb_get_ring_sset_count(int stringset) +{ + if (stringset == ETH_SS_STATS) + return HNS_RING_STATIC_REG_NUM; + + return 0; +} + +/** + *hns_rcb_get_common_regs_count - rcb common regs count + *return regs count + */ +int hns_rcb_get_common_regs_count(void) +{ + return HNS_RCB_COMMON_DUMP_REG_NUM; +} + +/** + *rcb_get_sset_count - rcb ring regs count + *return regs count + */ +int hns_rcb_get_ring_regs_count(void) +{ + return HNS_RCB_RING_DUMP_REG_NUM; +} + +/** + *hns_rcb_get_strings - get rcb string set + *@stringset:string set index + *@data:strings name value + *@index:queue index + */ +void hns_rcb_get_strings(int stringset, u8 *data, int index) +{ + char *buff = (char *)data; + + if (stringset != ETH_SS_STATS) + return; + + snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_rcb_pkt_num", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_ppe_tx_pkt_num", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_ppe_drop_pkt_num", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_fbd_num", index); + buff = buff + ETH_GSTRING_LEN; + + snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_pkt_num", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_bytes", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_err_cnt", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_io_err", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_sw_err", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_seg_pkt", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_restart_queue", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_tx_busy", index); + buff = buff + ETH_GSTRING_LEN; + + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_rcb_pkt_num", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_ppe_pkt_num", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_ppe_drop_pkt_num", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_fbd_num", index); + buff = buff + ETH_GSTRING_LEN; + + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_pkt_num", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_bytes", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_err_cnt", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_io_err", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_sw_err", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_seg_pkt", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_reuse_pg", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_len_err", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_non_vld_desc_err", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_bd_num_err", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_l2_err", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_l3l4csum_err", index); +} + +void hns_rcb_get_common_regs(struct rcb_common_cb *rcb_com, void *data) +{ + u32 *regs = data; + u32 i = 0; + + /*rcb common registers */ + regs[0] = dsaf_read_dev(rcb_com, RCB_COM_CFG_ENDIAN_REG); + regs[1] = dsaf_read_dev(rcb_com, RCB_COM_CFG_SYS_FSH_REG); + regs[2] = dsaf_read_dev(rcb_com, RCB_COM_CFG_INIT_FLAG_REG); + + regs[3] = dsaf_read_dev(rcb_com, RCB_COM_CFG_PKT_REG); + regs[4] = dsaf_read_dev(rcb_com, RCB_COM_CFG_RINVLD_REG); + regs[5] = dsaf_read_dev(rcb_com, RCB_COM_CFG_FNA_REG); + regs[6] = dsaf_read_dev(rcb_com, RCB_COM_CFG_FA_REG); + regs[7] = dsaf_read_dev(rcb_com, RCB_COM_CFG_PKT_TC_BP_REG); + regs[8] = dsaf_read_dev(rcb_com, RCB_COM_CFG_PPE_TNL_CLKEN_REG); + + regs[9] = dsaf_read_dev(rcb_com, RCB_COM_INTMSK_TX_PKT_REG); + regs[10] = dsaf_read_dev(rcb_com, RCB_COM_RINT_TX_PKT_REG); + regs[11] = dsaf_read_dev(rcb_com, RCB_COM_INTMASK_ECC_ERR_REG); + regs[12] = dsaf_read_dev(rcb_com, RCB_COM_INTSTS_ECC_ERR_REG); + regs[13] = dsaf_read_dev(rcb_com, RCB_COM_EBD_SRAM_ERR_REG); + regs[14] = dsaf_read_dev(rcb_com, RCB_COM_RXRING_ERR_REG); + regs[15] = dsaf_read_dev(rcb_com, RCB_COM_TXRING_ERR_REG); + regs[16] = dsaf_read_dev(rcb_com, RCB_COM_TX_FBD_ERR_REG); + regs[17] = dsaf_read_dev(rcb_com, RCB_SRAM_ECC_CHK_EN_REG); + regs[18] = dsaf_read_dev(rcb_com, RCB_SRAM_ECC_CHK0_REG); + regs[19] = dsaf_read_dev(rcb_com, RCB_SRAM_ECC_CHK1_REG); + regs[20] = dsaf_read_dev(rcb_com, RCB_SRAM_ECC_CHK2_REG); + regs[21] = dsaf_read_dev(rcb_com, RCB_SRAM_ECC_CHK3_REG); + regs[22] = dsaf_read_dev(rcb_com, RCB_SRAM_ECC_CHK4_REG); + regs[23] = dsaf_read_dev(rcb_com, RCB_SRAM_ECC_CHK5_REG); + regs[24] = dsaf_read_dev(rcb_com, RCB_ECC_ERR_ADDR0_REG); + regs[25] = dsaf_read_dev(rcb_com, RCB_ECC_ERR_ADDR3_REG); + regs[26] = dsaf_read_dev(rcb_com, RCB_ECC_ERR_ADDR4_REG); + regs[27] = dsaf_read_dev(rcb_com, RCB_ECC_ERR_ADDR5_REG); + + regs[28] = dsaf_read_dev(rcb_com, RCB_COM_SF_CFG_INTMASK_RING); + regs[29] = dsaf_read_dev(rcb_com, RCB_COM_SF_CFG_RING_STS); + regs[30] = dsaf_read_dev(rcb_com, RCB_COM_SF_CFG_RING); + regs[31] = dsaf_read_dev(rcb_com, RCB_COM_SF_CFG_INTMASK_BD); + regs[32] = dsaf_read_dev(rcb_com, RCB_COM_SF_CFG_BD_RINT_STS); + regs[33] = dsaf_read_dev(rcb_com, RCB_COM_RCB_RD_BD_BUSY); + regs[34] = dsaf_read_dev(rcb_com, RCB_COM_RCB_FBD_CRT_EN); + regs[35] = dsaf_read_dev(rcb_com, RCB_COM_AXI_WR_ERR_INTMASK); + regs[36] = dsaf_read_dev(rcb_com, RCB_COM_AXI_ERR_STS); + regs[37] = dsaf_read_dev(rcb_com, RCB_COM_CHK_TX_FBD_NUM_REG); + + /* rcb common entry registers */ + for (i = 0; i < 16; i++) { /* total 16 model registers */ + regs[38 + i] + = dsaf_read_dev(rcb_com, RCB_CFG_BD_NUM_REG + 4 * i); + regs[54 + i] + = dsaf_read_dev(rcb_com, RCB_CFG_PKTLINE_REG + 4 * i); + } + + regs[70] = dsaf_read_dev(rcb_com, RCB_CFG_OVERTIME_REG); + regs[71] = dsaf_read_dev(rcb_com, RCB_CFG_PKTLINE_INT_NUM_REG); + regs[72] = dsaf_read_dev(rcb_com, RCB_CFG_OVERTIME_INT_NUM_REG); + + /* mark end of rcb common regs */ + for (i = 73; i < 80; i++) + regs[i] = 0xcccccccc; +} + +void hns_rcb_get_ring_regs(struct hnae_queue *queue, void *data) +{ + u32 *regs = data; + struct ring_pair_cb *ring_pair + = container_of(queue, struct ring_pair_cb, q); + u32 i = 0; + + /*rcb ring registers */ + regs[0] = dsaf_read_dev(queue, RCB_RING_RX_RING_BASEADDR_L_REG); + regs[1] = dsaf_read_dev(queue, RCB_RING_RX_RING_BASEADDR_H_REG); + regs[2] = dsaf_read_dev(queue, RCB_RING_RX_RING_BD_NUM_REG); + regs[3] = dsaf_read_dev(queue, RCB_RING_RX_RING_BD_LEN_REG); + regs[4] = dsaf_read_dev(queue, RCB_RING_RX_RING_PKTLINE_REG); + regs[5] = dsaf_read_dev(queue, RCB_RING_RX_RING_TAIL_REG); + regs[6] = dsaf_read_dev(queue, RCB_RING_RX_RING_HEAD_REG); + regs[7] = dsaf_read_dev(queue, RCB_RING_RX_RING_FBDNUM_REG); + regs[8] = dsaf_read_dev(queue, RCB_RING_RX_RING_PKTNUM_RECORD_REG); + + regs[9] = dsaf_read_dev(queue, RCB_RING_TX_RING_BASEADDR_L_REG); + regs[10] = dsaf_read_dev(queue, RCB_RING_TX_RING_BASEADDR_H_REG); + regs[11] = dsaf_read_dev(queue, RCB_RING_TX_RING_BD_NUM_REG); + regs[12] = dsaf_read_dev(queue, RCB_RING_TX_RING_BD_LEN_REG); + regs[13] = dsaf_read_dev(queue, RCB_RING_TX_RING_PKTLINE_REG); + regs[15] = dsaf_read_dev(queue, RCB_RING_TX_RING_TAIL_REG); + regs[16] = dsaf_read_dev(queue, RCB_RING_TX_RING_HEAD_REG); + regs[17] = dsaf_read_dev(queue, RCB_RING_TX_RING_FBDNUM_REG); + regs[18] = dsaf_read_dev(queue, RCB_RING_TX_RING_OFFSET_REG); + regs[19] = dsaf_read_dev(queue, RCB_RING_TX_RING_PKTNUM_RECORD_REG); + + regs[20] = dsaf_read_dev(queue, RCB_RING_PREFETCH_EN_REG); + regs[21] = dsaf_read_dev(queue, RCB_RING_CFG_VF_NUM_REG); + regs[22] = dsaf_read_dev(queue, RCB_RING_ASID_REG); + regs[23] = dsaf_read_dev(queue, RCB_RING_RX_VM_REG); + regs[24] = dsaf_read_dev(queue, RCB_RING_T0_BE_RST); + regs[25] = dsaf_read_dev(queue, RCB_RING_COULD_BE_RST); + regs[26] = dsaf_read_dev(queue, RCB_RING_WRR_WEIGHT_REG); + + regs[27] = dsaf_read_dev(queue, RCB_RING_INTMSK_RXWL_REG); + regs[28] = dsaf_read_dev(queue, RCB_RING_INTSTS_RX_RING_REG); + regs[29] = dsaf_read_dev(queue, RCB_RING_INTMSK_TXWL_REG); + regs[30] = dsaf_read_dev(queue, RCB_RING_INTSTS_TX_RING_REG); + regs[31] = dsaf_read_dev(queue, RCB_RING_INTMSK_RX_OVERTIME_REG); + regs[32] = dsaf_read_dev(queue, RCB_RING_INTSTS_RX_OVERTIME_REG); + regs[33] = dsaf_read_dev(queue, RCB_RING_INTMSK_TX_OVERTIME_REG); + regs[34] = dsaf_read_dev(queue, RCB_RING_INTSTS_TX_OVERTIME_REG); + + /* mark end of ring regs */ + for (i = 35; i < 40; i++) + regs[i] = 0xcccccc00 + ring_pair->index; +} diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h new file mode 100644 index 000000000000..c7db6130a3cf --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * 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 _HNS_DSAF_RCB_H +#define _HNS_DSAF_RCB_H + +#include <linux/netdevice.h> +#include <linux/platform_device.h> + +#include "hnae.h" +#include "hns_dsaf_main.h" + +struct rcb_common_cb; + +#define HNS_RCB_IRQ_NUM_PER_QUEUE 2 +#define HNS_RCB_IRQ_IDX_TX 0 +#define HNS_RCB_IRQ_IDX_RX 1 +#define HNS_RCB_TX_REG_OFFSET 0x40 + +#define HNS_RCB_SERVICE_NW_ENGINE_NUM DSAF_COMM_CHN +#define HNS_RCB_DEBUG_NW_ENGINE_NUM 1 +#define HNS_RCB_RING_MAX_BD_PER_PKT 3 +#define HNS_RCB_MAX_PKT_SIZE MAC_MAX_MTU + +#define HNS_RCB_RING_MAX_PENDING_BD 1024 +#define HNS_RCB_RING_MIN_PENDING_BD 16 + +#define HNS_RCB_REG_OFFSET 0x10000 + +#define HNS_RCB_MAX_COALESCED_FRAMES 1023 +#define HNS_RCB_MIN_COALESCED_FRAMES 1 +#define HNS_RCB_DEF_COALESCED_FRAMES 50 +#define HNS_RCB_MAX_TIME_OUT 0x500 + +#define HNS_RCB_COMMON_ENDIAN 1 + +#define HNS_BD_SIZE_512_TYPE 0 +#define HNS_BD_SIZE_1024_TYPE 1 +#define HNS_BD_SIZE_2048_TYPE 2 +#define HNS_BD_SIZE_4096_TYPE 3 + +#define HNS_RCB_COMMON_DUMP_REG_NUM 80 +#define HNS_RCB_RING_DUMP_REG_NUM 40 +#define HNS_RING_STATIC_REG_NUM 28 + +#define HNS_DUMP_REG_NUM 500 +#define HNS_STATIC_REG_NUM 12 + +enum rcb_int_flag { + RCB_INT_FLAG_TX = 0x1, + RCB_INT_FLAG_RX = (0x1 << 1), + RCB_INT_FLAG_MAX = (0x1 << 2), /*must be the last element */ +}; + +struct hns_ring_hw_stats { + u64 tx_pkts; + u64 ppe_tx_ok_pkts; + u64 ppe_tx_drop_pkts; + u64 rx_pkts; + u64 ppe_rx_ok_pkts; + u64 ppe_rx_drop_pkts; +}; + +struct ring_pair_cb { + struct rcb_common_cb *rcb_common; /* ring belongs to */ + struct device *dev; /*device for DMA mapping */ + struct hnae_queue q; + + u16 index; /* global index in a rcb common device */ + u16 buf_size; + + int virq[HNS_RCB_IRQ_NUM_PER_QUEUE]; + + u8 port_id_in_dsa; + u8 used_by_vf; + + struct hns_ring_hw_stats hw_stats; +}; + +struct rcb_common_cb { + u8 __iomem *io_base; + phys_addr_t phy_base; + struct dsaf_device *dsaf_dev; + u16 max_vfn; + u16 max_q_per_vf; + + u8 comm_index; + u32 ring_num; + u32 coalesced_frames; /* frames threshold of rx interrupt */ + u32 timeout; /* time threshold of rx interrupt */ + u32 desc_num; /* desc num per queue*/ + + struct ring_pair_cb ring_pair_cb[0]; +}; + +int hns_rcb_buf_size2type(u32 buf_size); + +int hns_rcb_common_get_cfg(struct dsaf_device *dsaf_dev, int comm_index); +void hns_rcb_common_free_cfg(struct dsaf_device *dsaf_dev, u32 comm_index); +int hns_rcb_common_init_hw(struct rcb_common_cb *rcb_common); +void hns_rcb_start(struct hnae_queue *q, u32 val); +void hns_rcb_get_cfg(struct rcb_common_cb *rcb_common); +void hns_rcb_common_init_commit_hw(struct rcb_common_cb *rcb_common); + +void hns_rcb_ring_enable_hw(struct hnae_queue *q, u32 val); +void hns_rcb_int_clr_hw(struct hnae_queue *q, u32 flag); +void hns_rcb_int_ctrl_hw(struct hnae_queue *q, u32 flag, u32 enable); +void hns_rcb_init_hw(struct ring_pair_cb *ring); +void hns_rcb_reset_ring_hw(struct hnae_queue *q); +void hns_rcb_wait_fbd_clean(struct hnae_queue **qs, int q_num, u32 flag); + +u32 hns_rcb_get_coalesced_frames(struct dsaf_device *dsaf_dev, int comm_index); +u32 hns_rcb_get_coalesce_usecs(struct dsaf_device *dsaf_dev, int comm_index); +void hns_rcb_set_coalesce_usecs(struct dsaf_device *dsaf_dev, + int comm_index, u32 timeout); +int hns_rcb_set_coalesced_frames(struct dsaf_device *dsaf_dev, + int comm_index, u32 coalesce_frames); +void hns_rcb_update_stats(struct hnae_queue *queue); + +void hns_rcb_get_stats(struct hnae_queue *queue, u64 *data); + +void hns_rcb_get_common_regs(struct rcb_common_cb *rcb_common, void *data); + +int hns_rcb_get_ring_sset_count(int stringset); +int hns_rcb_get_common_regs_count(void); +int hns_rcb_get_ring_regs_count(void); + +void hns_rcb_get_ring_regs(struct hnae_queue *queue, void *data); + +void hns_rcb_get_strings(int stringset, u8 *data, int index); +#endif /* _HNS_DSAF_RCB_H */ diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h new file mode 100644 index 000000000000..6fc58ba01f87 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h @@ -0,0 +1,972 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * 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 _DSAF_REG_H_ +#define _DSAF_REG_H_ + +#define HNS_GE_FIFO_ERR_INTNUM 8 +#define HNS_XGE_ERR_INTNUM 6 +#define HNS_RCB_COMM_ERR_INTNUM 12 +#define HNS_PPE_TNL_ERR_INTNUM 8 +#define HNS_DSAF_EVENT_INTNUM 21 +#define HNS_DEBUG_RING_INTNUM 4 +#define HNS_SERVICE_RING_INTNUM 256 + +#define HNS_DEBUG_RING_IRQ_IDX (HNS_GE_FIFO_ERR_INTNUM + HNS_XGE_ERR_INTNUM +\ + HNS_RCB_COMM_ERR_INTNUM + HNS_PPE_TNL_ERR_INTNUM +\ + HNS_DSAF_EVENT_INTNUM) +#define HNS_SERVICE_RING_IRQ_IDX (HNS_DEBUG_RING_IRQ_IDX +\ + HNS_DEBUG_RING_INTNUM) + +#define DSAF_IRQ_NUM 18 + +#define DSAF_MAX_PORT_NUM_PER_CHIP 8 +#define DSAF_SERVICE_PORT_NUM_PER_DSAF 6 +#define DSAF_MAX_VM_NUM 128 + +#define DSAF_COMM_DEV_NUM 3 +#define DSAF_PPE_INODE_BASE 6 +#define HNS_DSAF_COMM_SERVICE_NW_IDX 0 +#define DSAF_DEBUG_NW_NUM 2 +#define DSAF_SERVICE_NW_NUM 6 +#define DSAF_COMM_CHN DSAF_SERVICE_NW_NUM +#define DSAF_GE_NUM ((DSAF_SERVICE_NW_NUM) + (DSAF_DEBUG_NW_NUM)) +#define DSAF_PORT_NUM ((DSAF_SERVICE_NW_NUM) + (DSAF_DEBUG_NW_NUM)) +#define DSAF_XGE_NUM DSAF_SERVICE_NW_NUM +#define DSAF_NODE_NUM 18 +#define DSAF_XOD_BIG_NUM DSAF_NODE_NUM +#define DSAF_SBM_NUM DSAF_NODE_NUM +#define DSAF_VOQ_NUM DSAF_NODE_NUM +#define DSAF_INODE_NUM DSAF_NODE_NUM +#define DSAF_XOD_NUM 8 +#define DSAF_TBL_NUM 8 +#define DSAF_SW_PORT_NUM 8 +#define DSAF_TOTAL_QUEUE_NUM 129 + +#define DSAF_TCAM_SUM 512 +#define DSAF_LINE_SUM (2048 * 14) + +#define DSAF_SUB_SC_NT_SRAM_CLK_SEL_REG 0x100 +#define DSAF_SUB_SC_HILINK3_CRG_CTRL0_REG 0x180 +#define DSAF_SUB_SC_HILINK3_CRG_CTRL1_REG 0x184 +#define DSAF_SUB_SC_HILINK3_CRG_CTRL2_REG 0x188 +#define DSAF_SUB_SC_HILINK3_CRG_CTRL3_REG 0x18C +#define DSAF_SUB_SC_HILINK4_CRG_CTRL0_REG 0x190 +#define DSAF_SUB_SC_HILINK4_CRG_CTRL1_REG 0x194 +#define DSAF_SUB_SC_DSAF_CLK_EN_REG 0x300 +#define DSAF_SUB_SC_DSAF_CLK_DIS_REG 0x304 +#define DSAF_SUB_SC_NT_CLK_EN_REG 0x308 +#define DSAF_SUB_SC_NT_CLK_DIS_REG 0x30C +#define DSAF_SUB_SC_XGE_CLK_EN_REG 0x310 +#define DSAF_SUB_SC_XGE_CLK_DIS_REG 0x314 +#define DSAF_SUB_SC_GE_CLK_EN_REG 0x318 +#define DSAF_SUB_SC_GE_CLK_DIS_REG 0x31C +#define DSAF_SUB_SC_PPE_CLK_EN_REG 0x320 +#define DSAF_SUB_SC_PPE_CLK_DIS_REG 0x324 +#define DSAF_SUB_SC_RCB_PPE_COM_CLK_EN_REG 0x350 +#define DSAF_SUB_SC_RCB_PPE_COM_CLK_DIS_REG 0x354 +#define DSAF_SUB_SC_XBAR_RESET_REQ_REG 0xA00 +#define DSAF_SUB_SC_XBAR_RESET_DREQ_REG 0xA04 +#define DSAF_SUB_SC_NT_RESET_REQ_REG 0xA08 +#define DSAF_SUB_SC_NT_RESET_DREQ_REG 0xA0C +#define DSAF_SUB_SC_XGE_RESET_REQ_REG 0xA10 +#define DSAF_SUB_SC_XGE_RESET_DREQ_REG 0xA14 +#define DSAF_SUB_SC_GE_RESET_REQ0_REG 0xA18 +#define DSAF_SUB_SC_GE_RESET_DREQ0_REG 0xA1C +#define DSAF_SUB_SC_GE_RESET_REQ1_REG 0xA20 +#define DSAF_SUB_SC_GE_RESET_DREQ1_REG 0xA24 +#define DSAF_SUB_SC_PPE_RESET_REQ_REG 0xA48 +#define DSAF_SUB_SC_PPE_RESET_DREQ_REG 0xA4C +#define DSAF_SUB_SC_RCB_PPE_COM_RESET_REQ_REG 0xA88 +#define DSAF_SUB_SC_RCB_PPE_COM_RESET_DREQ_REG 0xA8C +#define DSAF_SUB_SC_LIGHT_MODULE_DETECT_EN_REG 0x2060 +#define DSAF_SUB_SC_TCAM_MBIST_EN_REG 0x2300 +#define DSAF_SUB_SC_DSAF_CLK_ST_REG 0x5300 +#define DSAF_SUB_SC_NT_CLK_ST_REG 0x5304 +#define DSAF_SUB_SC_XGE_CLK_ST_REG 0x5308 +#define DSAF_SUB_SC_GE_CLK_ST_REG 0x530C +#define DSAF_SUB_SC_PPE_CLK_ST_REG 0x5310 +#define DSAF_SUB_SC_ROCEE_CLK_ST_REG 0x5314 +#define DSAF_SUB_SC_CPU_CLK_ST_REG 0x5318 +#define DSAF_SUB_SC_RCB_PPE_COM_CLK_ST_REG 0x5328 +#define DSAF_SUB_SC_XBAR_RESET_ST_REG 0x5A00 +#define DSAF_SUB_SC_NT_RESET_ST_REG 0x5A04 +#define DSAF_SUB_SC_XGE_RESET_ST_REG 0x5A08 +#define DSAF_SUB_SC_GE_RESET_ST0_REG 0x5A0C +#define DSAF_SUB_SC_GE_RESET_ST1_REG 0x5A10 +#define DSAF_SUB_SC_PPE_RESET_ST_REG 0x5A24 +#define DSAF_SUB_SC_RCB_PPE_COM_RESET_ST_REG 0x5A44 + +/*serdes offset**/ +#define HNS_MAC_HILINK3_REG DSAF_SUB_SC_HILINK3_CRG_CTRL0_REG +#define HNS_MAC_HILINK4_REG DSAF_SUB_SC_HILINK4_CRG_CTRL0_REG +#define HNS_MAC_LANE0_CTLEDFE_REG 0x000BFFCCULL +#define HNS_MAC_LANE1_CTLEDFE_REG 0x000BFFBCULL +#define HNS_MAC_LANE2_CTLEDFE_REG 0x000BFFACULL +#define HNS_MAC_LANE3_CTLEDFE_REG 0x000BFF9CULL +#define HNS_MAC_LANE0_STATE_REG 0x000BFFD4ULL +#define HNS_MAC_LANE1_STATE_REG 0x000BFFC4ULL +#define HNS_MAC_LANE2_STATE_REG 0x000BFFB4ULL +#define HNS_MAC_LANE3_STATE_REG 0x000BFFA4ULL + +#define HILINK_RESET_TIMOUT 10000 + +#define DSAF_SRAM_INIT_OVER_0_REG 0x0 +#define DSAF_CFG_0_REG 0x4 +#define DSAF_ECC_ERR_INVERT_0_REG 0x8 +#define DSAF_ABNORMAL_TIMEOUT_0_REG 0x1C +#define DSAF_FSM_TIMEOUT_0_REG 0x20 +#define DSAF_DSA_REG_CNT_CLR_CE_REG 0x2C +#define DSAF_DSA_SBM_INF_FIFO_THRD_REG 0x30 +#define DSAF_DSA_SRAM_1BIT_ECC_SEL_REG 0x34 +#define DSAF_DSA_SRAM_1BIT_ECC_CNT_REG 0x38 +#define DSAF_PFC_EN_0_REG 0x50 +#define DSAF_PFC_UNIT_CNT_0_REG 0x70 +#define DSAF_XGE_INT_MSK_0_REG 0x100 +#define DSAF_PPE_INT_MSK_0_REG 0x120 +#define DSAF_ROCEE_INT_MSK_0_REG 0x140 +#define DSAF_XGE_INT_SRC_0_REG 0x160 +#define DSAF_PPE_INT_SRC_0_REG 0x180 +#define DSAF_ROCEE_INT_SRC_0_REG 0x1A0 +#define DSAF_XGE_INT_STS_0_REG 0x1C0 +#define DSAF_PPE_INT_STS_0_REG 0x1E0 +#define DSAF_ROCEE_INT_STS_0_REG 0x200 +#define DSAF_PPE_QID_CFG_0_REG 0x300 +#define DSAF_SW_PORT_TYPE_0_REG 0x320 +#define DSAF_STP_PORT_TYPE_0_REG 0x340 +#define DSAF_MIX_DEF_QID_0_REG 0x360 +#define DSAF_PORT_DEF_VLAN_0_REG 0x380 +#define DSAF_VM_DEF_VLAN_0_REG 0x400 + +#define DSAF_INODE_CUT_THROUGH_CFG_0_REG 0x1000 +#define DSAF_INODE_ECC_INVERT_EN_0_REG 0x1008 +#define DSAF_INODE_ECC_ERR_ADDR_0_REG 0x100C +#define DSAF_INODE_IN_PORT_NUM_0_REG 0x1018 +#define DSAF_INODE_PRI_TC_CFG_0_REG 0x101C +#define DSAF_INODE_BP_STATUS_0_REG 0x1020 +#define DSAF_INODE_PAD_DISCARD_NUM_0_REG 0x1028 +#define DSAF_INODE_FINAL_IN_MAN_NUM_0_REG 0x102C +#define DSAF_INODE_FINAL_IN_PKT_NUM_0_REG 0x1030 +#define DSAF_INODE_SBM_PID_NUM_0_REG 0x1038 +#define DSAF_INODE_FINAL_IN_PAUSE_NUM_0_REG 0x103C +#define DSAF_INODE_SBM_RELS_NUM_0_REG 0x104C +#define DSAF_INODE_SBM_DROP_NUM_0_REG 0x1050 +#define DSAF_INODE_CRC_FALSE_NUM_0_REG 0x1054 +#define DSAF_INODE_BP_DISCARD_NUM_0_REG 0x1058 +#define DSAF_INODE_RSLT_DISCARD_NUM_0_REG 0x105C +#define DSAF_INODE_LOCAL_ADDR_FALSE_NUM_0_REG 0x1060 +#define DSAF_INODE_VOQ_OVER_NUM_0_REG 0x1068 +#define DSAF_INODE_BD_SAVE_STATUS_0_REG 0x1900 +#define DSAF_INODE_BD_ORDER_STATUS_0_REG 0x1950 +#define DSAF_INODE_SW_VLAN_TAG_DISC_0_REG 0x1A00 +#define DSAF_INODE_IN_DATA_STP_DISC_0_REG 0x1A50 +#define DSAF_INODE_GE_FC_EN_0_REG 0x1B00 +#define DSAF_INODE_VC0_IN_PKT_NUM_0_REG 0x1B50 +#define DSAF_INODE_VC1_IN_PKT_NUM_0_REG 0x1C00 + +#define DSAF_SBM_CFG_REG_0_REG 0x2000 +#define DSAF_SBM_BP_CFG_0_XGE_REG_0_REG 0x2004 +#define DSAF_SBM_BP_CFG_0_PPE_REG_0_REG 0x2304 +#define DSAF_SBM_BP_CFG_0_ROCEE_REG_0_REG 0x2604 +#define DSAF_SBM_BP_CFG_1_REG_0_REG 0x2008 +#define DSAF_SBM_BP_CFG_2_XGE_REG_0_REG 0x200C +#define DSAF_SBM_BP_CFG_2_PPE_REG_0_REG 0x230C +#define DSAF_SBM_BP_CFG_2_ROCEE_REG_0_REG 0x260C +#define DSAF_SBM_FREE_CNT_0_0_REG 0x2010 +#define DSAF_SBM_FREE_CNT_1_0_REG 0x2014 +#define DSAF_SBM_BP_CNT_0_0_REG 0x2018 +#define DSAF_SBM_BP_CNT_1_0_REG 0x201C +#define DSAF_SBM_BP_CNT_2_0_REG 0x2020 +#define DSAF_SBM_BP_CNT_3_0_REG 0x2024 +#define DSAF_SBM_INER_ST_0_REG 0x2028 +#define DSAF_SBM_MIB_REQ_FAILED_TC_0_REG 0x202C +#define DSAF_SBM_LNK_INPORT_CNT_0_REG 0x2030 +#define DSAF_SBM_LNK_DROP_CNT_0_REG 0x2034 +#define DSAF_SBM_INF_OUTPORT_CNT_0_REG 0x2038 +#define DSAF_SBM_LNK_INPORT_TC0_CNT_0_REG 0x203C +#define DSAF_SBM_LNK_INPORT_TC1_CNT_0_REG 0x2040 +#define DSAF_SBM_LNK_INPORT_TC2_CNT_0_REG 0x2044 +#define DSAF_SBM_LNK_INPORT_TC3_CNT_0_REG 0x2048 +#define DSAF_SBM_LNK_INPORT_TC4_CNT_0_REG 0x204C +#define DSAF_SBM_LNK_INPORT_TC5_CNT_0_REG 0x2050 +#define DSAF_SBM_LNK_INPORT_TC6_CNT_0_REG 0x2054 +#define DSAF_SBM_LNK_INPORT_TC7_CNT_0_REG 0x2058 +#define DSAF_SBM_LNK_REQ_CNT_0_REG 0x205C +#define DSAF_SBM_LNK_RELS_CNT_0_REG 0x2060 +#define DSAF_SBM_BP_CFG_3_REG_0_REG 0x2068 +#define DSAF_SBM_BP_CFG_4_REG_0_REG 0x206C + +#define DSAF_XOD_ETS_TSA_TC0_TC3_CFG_0_REG 0x3000 +#define DSAF_XOD_ETS_TSA_TC4_TC7_CFG_0_REG 0x3004 +#define DSAF_XOD_ETS_BW_TC0_TC3_CFG_0_REG 0x3008 +#define DSAF_XOD_ETS_BW_TC4_TC7_CFG_0_REG 0x300C +#define DSAF_XOD_ETS_BW_OFFSET_CFG_0_REG 0x3010 +#define DSAF_XOD_ETS_TOKEN_CFG_0_REG 0x3014 +#define DSAF_XOD_PFS_CFG_0_0_REG 0x3018 +#define DSAF_XOD_PFS_CFG_1_0_REG 0x301C +#define DSAF_XOD_PFS_CFG_2_0_REG 0x3020 +#define DSAF_XOD_GNT_L_0_REG 0x3024 +#define DSAF_XOD_GNT_H_0_REG 0x3028 +#define DSAF_XOD_CONNECT_STATE_0_REG 0x302C +#define DSAF_XOD_RCVPKT_CNT_0_REG 0x3030 +#define DSAF_XOD_RCVTC0_CNT_0_REG 0x3034 +#define DSAF_XOD_RCVTC1_CNT_0_REG 0x3038 +#define DSAF_XOD_RCVTC2_CNT_0_REG 0x303C +#define DSAF_XOD_RCVTC3_CNT_0_REG 0x3040 +#define DSAF_XOD_RCVVC0_CNT_0_REG 0x3044 +#define DSAF_XOD_RCVVC1_CNT_0_REG 0x3048 +#define DSAF_XOD_XGE_RCVIN0_CNT_0_REG 0x304C +#define DSAF_XOD_XGE_RCVIN1_CNT_0_REG 0x3050 +#define DSAF_XOD_XGE_RCVIN2_CNT_0_REG 0x3054 +#define DSAF_XOD_XGE_RCVIN3_CNT_0_REG 0x3058 +#define DSAF_XOD_XGE_RCVIN4_CNT_0_REG 0x305C +#define DSAF_XOD_XGE_RCVIN5_CNT_0_REG 0x3060 +#define DSAF_XOD_XGE_RCVIN6_CNT_0_REG 0x3064 +#define DSAF_XOD_XGE_RCVIN7_CNT_0_REG 0x3068 +#define DSAF_XOD_PPE_RCVIN0_CNT_0_REG 0x306C +#define DSAF_XOD_PPE_RCVIN1_CNT_0_REG 0x3070 +#define DSAF_XOD_ROCEE_RCVIN0_CNT_0_REG 0x3074 +#define DSAF_XOD_ROCEE_RCVIN1_CNT_0_REG 0x3078 +#define DSAF_XOD_FIFO_STATUS_0_REG 0x307C + +#define DSAF_VOQ_ECC_INVERT_EN_0_REG 0x4004 +#define DSAF_VOQ_SRAM_PKT_NUM_0_REG 0x4008 +#define DSAF_VOQ_IN_PKT_NUM_0_REG 0x400C +#define DSAF_VOQ_OUT_PKT_NUM_0_REG 0x4010 +#define DSAF_VOQ_ECC_ERR_ADDR_0_REG 0x4014 +#define DSAF_VOQ_BP_STATUS_0_REG 0x4018 +#define DSAF_VOQ_SPUP_IDLE_0_REG 0x401C +#define DSAF_VOQ_XGE_XOD_REQ_0_0_REG 0x4024 +#define DSAF_VOQ_XGE_XOD_REQ_1_0_REG 0x4028 +#define DSAF_VOQ_PPE_XOD_REQ_0_REG 0x402C +#define DSAF_VOQ_ROCEE_XOD_REQ_0_REG 0x4030 +#define DSAF_VOQ_BP_ALL_THRD_0_REG 0x4034 + +#define DSAF_TBL_CTRL_0_REG 0x5000 +#define DSAF_TBL_INT_MSK_0_REG 0x5004 +#define DSAF_TBL_INT_SRC_0_REG 0x5008 +#define DSAF_TBL_INT_STS_0_REG 0x5100 +#define DSAF_TBL_TCAM_ADDR_0_REG 0x500C +#define DSAF_TBL_LINE_ADDR_0_REG 0x5010 +#define DSAF_TBL_TCAM_HIGH_0_REG 0x5014 +#define DSAF_TBL_TCAM_LOW_0_REG 0x5018 +#define DSAF_TBL_TCAM_MCAST_CFG_4_0_REG 0x501C +#define DSAF_TBL_TCAM_MCAST_CFG_3_0_REG 0x5020 +#define DSAF_TBL_TCAM_MCAST_CFG_2_0_REG 0x5024 +#define DSAF_TBL_TCAM_MCAST_CFG_1_0_REG 0x5028 +#define DSAF_TBL_TCAM_MCAST_CFG_0_0_REG 0x502C +#define DSAF_TBL_TCAM_UCAST_CFG_0_REG 0x5030 +#define DSAF_TBL_LIN_CFG_0_REG 0x5034 +#define DSAF_TBL_TCAM_RDATA_HIGH_0_REG 0x5038 +#define DSAF_TBL_TCAM_RDATA_LOW_0_REG 0x503C +#define DSAF_TBL_TCAM_RAM_RDATA4_0_REG 0x5040 +#define DSAF_TBL_TCAM_RAM_RDATA3_0_REG 0x5044 +#define DSAF_TBL_TCAM_RAM_RDATA2_0_REG 0x5048 +#define DSAF_TBL_TCAM_RAM_RDATA1_0_REG 0x504C +#define DSAF_TBL_TCAM_RAM_RDATA0_0_REG 0x5050 +#define DSAF_TBL_LIN_RDATA_0_REG 0x5054 +#define DSAF_TBL_DA0_MIS_INFO1_0_REG 0x5058 +#define DSAF_TBL_DA0_MIS_INFO0_0_REG 0x505C +#define DSAF_TBL_SA_MIS_INFO2_0_REG 0x5104 +#define DSAF_TBL_SA_MIS_INFO1_0_REG 0x5098 +#define DSAF_TBL_SA_MIS_INFO0_0_REG 0x509C +#define DSAF_TBL_PUL_0_REG 0x50A0 +#define DSAF_TBL_OLD_RSLT_0_REG 0x50A4 +#define DSAF_TBL_OLD_SCAN_VAL_0_REG 0x50A8 +#define DSAF_TBL_DFX_CTRL_0_REG 0x50AC +#define DSAF_TBL_DFX_STAT_0_REG 0x50B0 +#define DSAF_TBL_DFX_STAT_2_0_REG 0x5108 +#define DSAF_TBL_LKUP_NUM_I_0_REG 0x50C0 +#define DSAF_TBL_LKUP_NUM_O_0_REG 0x50E0 +#define DSAF_TBL_UCAST_BCAST_MIS_INFO_0_0_REG 0x510C + +#define DSAF_INODE_FIFO_WL_0_REG 0x6000 +#define DSAF_ONODE_FIFO_WL_0_REG 0x6020 +#define DSAF_XGE_GE_WORK_MODE_0_REG 0x6040 +#define DSAF_XGE_APP_RX_LINK_UP_0_REG 0x6080 +#define DSAF_NETPORT_CTRL_SIG_0_REG 0x60A0 +#define DSAF_XGE_CTRL_SIG_CFG_0_REG 0x60C0 + +#define PPE_COM_CFG_QID_MODE_REG 0x0 +#define PPE_COM_INTEN_REG 0x110 +#define PPE_COM_RINT_REG 0x114 +#define PPE_COM_INTSTS_REG 0x118 +#define PPE_COM_COMMON_CNT_CLR_CE_REG 0x1120 +#define PPE_COM_HIS_RX_PKT_QID_DROP_CNT_REG 0x300 +#define PPE_COM_HIS_RX_PKT_QID_OK_CNT_REG 0x600 +#define PPE_COM_HIS_TX_PKT_QID_ERR_CNT_REG 0x900 +#define PPE_COM_HIS_TX_PKT_QID_OK_CNT_REG 0xC00 +#define PPE_COM_COMMON_CNT_CLR_CE_REG 0x1120 + +#define PPE_CFG_TX_FIFO_THRSLD_REG 0x0 +#define PPE_CFG_RX_FIFO_THRSLD_REG 0x4 +#define PPE_CFG_RX_FIFO_PAUSE_THRSLD_REG 0x8 +#define PPE_CFG_RX_FIFO_SW_BP_THRSLD_REG 0xC +#define PPE_CFG_PAUSE_IDLE_CNT_REG 0x10 +#define PPE_CFG_BUS_CTRL_REG 0x40 +#define PPE_CFG_TNL_TO_BE_RST_REG 0x48 +#define PPE_CURR_TNL_CAN_RST_REG 0x4C +#define PPE_CFG_XGE_MODE_REG 0x80 +#define PPE_CFG_MAX_FRAME_LEN_REG 0x84 +#define PPE_CFG_RX_PKT_MODE_REG 0x88 +#define PPE_CFG_RX_VLAN_TAG_REG 0x8C +#define PPE_CFG_TAG_GEN_REG 0x90 +#define PPE_CFG_PARSE_TAG_REG 0x94 +#define PPE_CFG_PRO_CHECK_EN_REG 0x98 +#define PPE_INTEN_REG 0x100 +#define PPE_RINT_REG 0x104 +#define PPE_INTSTS_REG 0x108 +#define PPE_CFG_RX_PKT_INT_REG 0x140 +#define PPE_CFG_HEAT_DECT_TIME0_REG 0x144 +#define PPE_CFG_HEAT_DECT_TIME1_REG 0x148 +#define PPE_HIS_RX_SW_PKT_CNT_REG 0x200 +#define PPE_HIS_RX_WR_BD_OK_PKT_CNT_REG 0x204 +#define PPE_HIS_RX_PKT_NO_BUF_CNT_REG 0x208 +#define PPE_HIS_TX_BD_CNT_REG 0x20C +#define PPE_HIS_TX_PKT_CNT_REG 0x210 +#define PPE_HIS_TX_PKT_OK_CNT_REG 0x214 +#define PPE_HIS_TX_PKT_EPT_CNT_REG 0x218 +#define PPE_HIS_TX_PKT_CS_FAIL_CNT_REG 0x21C +#define PPE_HIS_RX_APP_BUF_FAIL_CNT_REG 0x220 +#define PPE_HIS_RX_APP_BUF_WAIT_CNT_REG 0x224 +#define PPE_HIS_RX_PKT_DROP_FUL_CNT_REG 0x228 +#define PPE_HIS_RX_PKT_DROP_PRT_CNT_REG 0x22C +#define PPE_TNL_0_5_CNT_CLR_CE_REG 0x300 +#define PPE_CFG_AXI_DBG_REG 0x304 +#define PPE_HIS_PRO_ERR_REG 0x308 +#define PPE_HIS_TNL_FIFO_ERR_REG 0x30C +#define PPE_CURR_CFF_DATA_NUM_REG 0x310 +#define PPE_CURR_RX_ST_REG 0x314 +#define PPE_CURR_TX_ST_REG 0x318 +#define PPE_CURR_RX_FIFO0_REG 0x31C +#define PPE_CURR_RX_FIFO1_REG 0x320 +#define PPE_CURR_TX_FIFO0_REG 0x324 +#define PPE_CURR_TX_FIFO1_REG 0x328 +#define PPE_ECO0_REG 0x32C +#define PPE_ECO1_REG 0x330 +#define PPE_ECO2_REG 0x334 + +#define RCB_COM_CFG_ENDIAN_REG 0x0 +#define RCB_COM_CFG_SYS_FSH_REG 0xC +#define RCB_COM_CFG_INIT_FLAG_REG 0x10 +#define RCB_COM_CFG_PKT_REG 0x30 +#define RCB_COM_CFG_RINVLD_REG 0x34 +#define RCB_COM_CFG_FNA_REG 0x38 +#define RCB_COM_CFG_FA_REG 0x3C +#define RCB_COM_CFG_PKT_TC_BP_REG 0x40 +#define RCB_COM_CFG_PPE_TNL_CLKEN_REG 0x44 + +#define RCB_COM_INTMSK_TX_PKT_REG 0x3A0 +#define RCB_COM_RINT_TX_PKT_REG 0x3A8 +#define RCB_COM_INTMASK_ECC_ERR_REG 0x400 +#define RCB_COM_INTSTS_ECC_ERR_REG 0x408 +#define RCB_COM_EBD_SRAM_ERR_REG 0x410 +#define RCB_COM_RXRING_ERR_REG 0x41C +#define RCB_COM_TXRING_ERR_REG 0x420 +#define RCB_COM_TX_FBD_ERR_REG 0x424 +#define RCB_SRAM_ECC_CHK_EN_REG 0x428 +#define RCB_SRAM_ECC_CHK0_REG 0x42C +#define RCB_SRAM_ECC_CHK1_REG 0x430 +#define RCB_SRAM_ECC_CHK2_REG 0x434 +#define RCB_SRAM_ECC_CHK3_REG 0x438 +#define RCB_SRAM_ECC_CHK4_REG 0x43c +#define RCB_SRAM_ECC_CHK5_REG 0x440 +#define RCB_ECC_ERR_ADDR0_REG 0x450 +#define RCB_ECC_ERR_ADDR3_REG 0x45C +#define RCB_ECC_ERR_ADDR4_REG 0x460 +#define RCB_ECC_ERR_ADDR5_REG 0x464 + +#define RCB_COM_SF_CFG_INTMASK_RING 0x480 +#define RCB_COM_SF_CFG_RING_STS 0x484 +#define RCB_COM_SF_CFG_RING 0x488 +#define RCB_COM_SF_CFG_INTMASK_BD 0x48C +#define RCB_COM_SF_CFG_BD_RINT_STS 0x470 +#define RCB_COM_RCB_RD_BD_BUSY 0x490 +#define RCB_COM_RCB_FBD_CRT_EN 0x494 +#define RCB_COM_AXI_WR_ERR_INTMASK 0x498 +#define RCB_COM_AXI_ERR_STS 0x49C +#define RCB_COM_CHK_TX_FBD_NUM_REG 0x4a0 + +#define RCB_CFG_BD_NUM_REG 0x9000 +#define RCB_CFG_PKTLINE_REG 0x9050 + +#define RCB_CFG_OVERTIME_REG 0x9300 +#define RCB_CFG_PKTLINE_INT_NUM_REG 0x9304 +#define RCB_CFG_OVERTIME_INT_NUM_REG 0x9308 + +#define RCB_RING_RX_RING_BASEADDR_L_REG 0x00000 +#define RCB_RING_RX_RING_BASEADDR_H_REG 0x00004 +#define RCB_RING_RX_RING_BD_NUM_REG 0x00008 +#define RCB_RING_RX_RING_BD_LEN_REG 0x0000C +#define RCB_RING_RX_RING_PKTLINE_REG 0x00010 +#define RCB_RING_RX_RING_TAIL_REG 0x00018 +#define RCB_RING_RX_RING_HEAD_REG 0x0001C +#define RCB_RING_RX_RING_FBDNUM_REG 0x00020 +#define RCB_RING_RX_RING_PKTNUM_RECORD_REG 0x0002C + +#define RCB_RING_TX_RING_BASEADDR_L_REG 0x00040 +#define RCB_RING_TX_RING_BASEADDR_H_REG 0x00044 +#define RCB_RING_TX_RING_BD_NUM_REG 0x00048 +#define RCB_RING_TX_RING_BD_LEN_REG 0x0004C +#define RCB_RING_TX_RING_PKTLINE_REG 0x00050 +#define RCB_RING_TX_RING_TAIL_REG 0x00058 +#define RCB_RING_TX_RING_HEAD_REG 0x0005C +#define RCB_RING_TX_RING_FBDNUM_REG 0x00060 +#define RCB_RING_TX_RING_OFFSET_REG 0x00064 +#define RCB_RING_TX_RING_PKTNUM_RECORD_REG 0x0006C + +#define RCB_RING_PREFETCH_EN_REG 0x0007C +#define RCB_RING_CFG_VF_NUM_REG 0x00080 +#define RCB_RING_ASID_REG 0x0008C +#define RCB_RING_RX_VM_REG 0x00090 +#define RCB_RING_T0_BE_RST 0x00094 +#define RCB_RING_COULD_BE_RST 0x00098 +#define RCB_RING_WRR_WEIGHT_REG 0x0009c + +#define RCB_RING_INTMSK_RXWL_REG 0x000A0 +#define RCB_RING_INTSTS_RX_RING_REG 0x000A4 +#define RCB_RING_INTMSK_TXWL_REG 0x000AC +#define RCB_RING_INTSTS_TX_RING_REG 0x000B0 +#define RCB_RING_INTMSK_RX_OVERTIME_REG 0x000B8 +#define RCB_RING_INTSTS_RX_OVERTIME_REG 0x000BC +#define RCB_RING_INTMSK_TX_OVERTIME_REG 0x000C4 +#define RCB_RING_INTSTS_TX_OVERTIME_REG 0x000C8 + +#define GMAC_DUPLEX_TYPE_REG 0x0008UL +#define GMAC_FD_FC_TYPE_REG 0x000CUL +#define GMAC_FC_TX_TIMER_REG 0x001CUL +#define GMAC_FD_FC_ADDR_LOW_REG 0x0020UL +#define GMAC_FD_FC_ADDR_HIGH_REG 0x0024UL +#define GMAC_IPG_TX_TIMER_REG 0x0030UL +#define GMAC_PAUSE_THR_REG 0x0038UL +#define GMAC_MAX_FRM_SIZE_REG 0x003CUL +#define GMAC_PORT_MODE_REG 0x0040UL +#define GMAC_PORT_EN_REG 0x0044UL +#define GMAC_PAUSE_EN_REG 0x0048UL +#define GMAC_SHORT_RUNTS_THR_REG 0x0050UL +#define GMAC_AN_NEG_STATE_REG 0x0058UL +#define GMAC_TX_LOCAL_PAGE_REG 0x005CUL +#define GMAC_TRANSMIT_CONTROL_REG 0x0060UL +#define GMAC_REC_FILT_CONTROL_REG 0x0064UL +#define GMAC_PTP_CONFIG_REG 0x0074UL + +#define GMAC_RX_OCTETS_TOTAL_OK_REG 0x0080UL +#define GMAC_RX_OCTETS_BAD_REG 0x0084UL +#define GMAC_RX_UC_PKTS_REG 0x0088UL +#define GMAC_RX_MC_PKTS_REG 0x008CUL +#define GMAC_RX_BC_PKTS_REG 0x0090UL +#define GMAC_RX_PKTS_64OCTETS_REG 0x0094UL +#define GMAC_RX_PKTS_65TO127OCTETS_REG 0x0098UL +#define GMAC_RX_PKTS_128TO255OCTETS_REG 0x009CUL +#define GMAC_RX_PKTS_255TO511OCTETS_REG 0x00A0UL +#define GMAC_RX_PKTS_512TO1023OCTETS_REG 0x00A4UL +#define GMAC_RX_PKTS_1024TO1518OCTETS_REG 0x00A8UL +#define GMAC_RX_PKTS_1519TOMAXOCTETS_REG 0x00ACUL +#define GMAC_RX_FCS_ERRORS_REG 0x00B0UL +#define GMAC_RX_TAGGED_REG 0x00B4UL +#define GMAC_RX_DATA_ERR_REG 0x00B8UL +#define GMAC_RX_ALIGN_ERRORS_REG 0x00BCUL +#define GMAC_RX_LONG_ERRORS_REG 0x00C0UL +#define GMAC_RX_JABBER_ERRORS_REG 0x00C4UL +#define GMAC_RX_PAUSE_MACCTRL_FRAM_REG 0x00C8UL +#define GMAC_RX_UNKNOWN_MACCTRL_FRAM_REG 0x00CCUL +#define GMAC_RX_VERY_LONG_ERR_CNT_REG 0x00D0UL +#define GMAC_RX_RUNT_ERR_CNT_REG 0x00D4UL +#define GMAC_RX_SHORT_ERR_CNT_REG 0x00D8UL +#define GMAC_RX_FILT_PKT_CNT_REG 0x00E8UL +#define GMAC_RX_OCTETS_TOTAL_FILT_REG 0x00ECUL +#define GMAC_OCTETS_TRANSMITTED_OK_REG 0x0100UL +#define GMAC_OCTETS_TRANSMITTED_BAD_REG 0x0104UL +#define GMAC_TX_UC_PKTS_REG 0x0108UL +#define GMAC_TX_MC_PKTS_REG 0x010CUL +#define GMAC_TX_BC_PKTS_REG 0x0110UL +#define GMAC_TX_PKTS_64OCTETS_REG 0x0114UL +#define GMAC_TX_PKTS_65TO127OCTETS_REG 0x0118UL +#define GMAC_TX_PKTS_128TO255OCTETS_REG 0x011CUL +#define GMAC_TX_PKTS_255TO511OCTETS_REG 0x0120UL +#define GMAC_TX_PKTS_512TO1023OCTETS_REG 0x0124UL +#define GMAC_TX_PKTS_1024TO1518OCTETS_REG 0x0128UL +#define GMAC_TX_PKTS_1519TOMAXOCTETS_REG 0x012CUL +#define GMAC_TX_EXCESSIVE_LENGTH_DROP_REG 0x014CUL +#define GMAC_TX_UNDERRUN_REG 0x0150UL +#define GMAC_TX_TAGGED_REG 0x0154UL +#define GMAC_TX_CRC_ERROR_REG 0x0158UL +#define GMAC_TX_PAUSE_FRAMES_REG 0x015CUL +#define GAMC_RX_MAX_FRAME 0x0170UL +#define GMAC_LINE_LOOP_BACK_REG 0x01A8UL +#define GMAC_CF_CRC_STRIP_REG 0x01B0UL +#define GMAC_MODE_CHANGE_EN_REG 0x01B4UL +#define GMAC_SIXTEEN_BIT_CNTR_REG 0x01CCUL +#define GMAC_LD_LINK_COUNTER_REG 0x01D0UL +#define GMAC_LOOP_REG 0x01DCUL +#define GMAC_RECV_CONTROL_REG 0x01E0UL +#define GMAC_VLAN_CODE_REG 0x01E8UL +#define GMAC_RX_OVERRUN_CNT_REG 0x01ECUL +#define GMAC_RX_LENGTHFIELD_ERR_CNT_REG 0x01F4UL +#define GMAC_RX_FAIL_COMMA_CNT_REG 0x01F8UL +#define GMAC_STATION_ADDR_LOW_0_REG 0x0200UL +#define GMAC_STATION_ADDR_HIGH_0_REG 0x0204UL +#define GMAC_STATION_ADDR_LOW_1_REG 0x0208UL +#define GMAC_STATION_ADDR_HIGH_1_REG 0x020CUL +#define GMAC_STATION_ADDR_LOW_2_REG 0x0210UL +#define GMAC_STATION_ADDR_HIGH_2_REG 0x0214UL +#define GMAC_STATION_ADDR_LOW_3_REG 0x0218UL +#define GMAC_STATION_ADDR_HIGH_3_REG 0x021CUL +#define GMAC_STATION_ADDR_LOW_4_REG 0x0220UL +#define GMAC_STATION_ADDR_HIGH_4_REG 0x0224UL +#define GMAC_STATION_ADDR_LOW_5_REG 0x0228UL +#define GMAC_STATION_ADDR_HIGH_5_REG 0x022CUL +#define GMAC_STATION_ADDR_LOW_MSK_0_REG 0x0230UL +#define GMAC_STATION_ADDR_HIGH_MSK_0_REG 0x0234UL +#define GMAC_STATION_ADDR_LOW_MSK_1_REG 0x0238UL +#define GMAC_STATION_ADDR_HIGH_MSK_1_REG 0x023CUL +#define GMAC_MAC_SKIP_LEN_REG 0x0240UL +#define GMAC_TX_LOOP_PKT_PRI_REG 0x0378UL + +#define XGMAC_INT_STATUS_REG 0x0 +#define XGMAC_INT_ENABLE_REG 0x4 +#define XGMAC_INT_SET_REG 0x8 +#define XGMAC_IERR_U_INFO_REG 0xC +#define XGMAC_OVF_INFO_REG 0x10 +#define XGMAC_OVF_CNT_REG 0x14 +#define XGMAC_PORT_MODE_REG 0x40 +#define XGMAC_CLK_ENABLE_REG 0x44 +#define XGMAC_RESET_REG 0x48 +#define XGMAC_LINK_CONTROL_REG 0x50 +#define XGMAC_LINK_STATUS_REG 0x54 +#define XGMAC_SPARE_REG 0xC0 +#define XGMAC_SPARE_CNT_REG 0xC4 + +#define XGMAC_MAC_ENABLE_REG 0x100 +#define XGMAC_MAC_CONTROL_REG 0x104 +#define XGMAC_MAC_IPG_REG 0x120 +#define XGMAC_MAC_MSG_CRC_EN_REG 0x124 +#define XGMAC_MAC_MSG_IMG_REG 0x128 +#define XGMAC_MAC_MSG_FC_CFG_REG 0x12C +#define XGMAC_MAC_MSG_TC_CFG_REG 0x130 +#define XGMAC_MAC_PAD_SIZE_REG 0x134 +#define XGMAC_MAC_MIN_PKT_SIZE_REG 0x138 +#define XGMAC_MAC_MAX_PKT_SIZE_REG 0x13C +#define XGMAC_MAC_PAUSE_CTRL_REG 0x160 +#define XGMAC_MAC_PAUSE_TIME_REG 0x164 +#define XGMAC_MAC_PAUSE_GAP_REG 0x168 +#define XGMAC_MAC_PAUSE_LOCAL_MAC_H_REG 0x16C +#define XGMAC_MAC_PAUSE_LOCAL_MAC_L_REG 0x170 +#define XGMAC_MAC_PAUSE_PEER_MAC_H_REG 0x174 +#define XGMAC_MAC_PAUSE_PEER_MAC_L_REG 0x178 +#define XGMAC_MAC_PFC_PRI_EN_REG 0x17C +#define XGMAC_MAC_1588_CTRL_REG 0x180 +#define XGMAC_MAC_1588_TX_PORT_DLY_REG 0x184 +#define XGMAC_MAC_1588_RX_PORT_DLY_REG 0x188 +#define XGMAC_MAC_1588_ASYM_DLY_REG 0x18C +#define XGMAC_MAC_1588_ADJUST_CFG_REG 0x190 +#define XGMAC_MAC_Y1731_ETH_TYPE_REG 0x194 +#define XGMAC_MAC_MIB_CONTROL_REG 0x198 +#define XGMAC_MAC_WAN_RATE_ADJUST_REG 0x19C +#define XGMAC_MAC_TX_ERR_MARK_REG 0x1A0 +#define XGMAC_MAC_TX_LF_RF_CONTROL_REG 0x1A4 +#define XGMAC_MAC_RX_LF_RF_STATUS_REG 0x1A8 +#define XGMAC_MAC_TX_RUNT_PKT_CNT_REG 0x1C0 +#define XGMAC_MAC_RX_RUNT_PKT_CNT_REG 0x1C4 +#define XGMAC_MAC_RX_PREAM_ERR_PKT_CNT_REG 0x1C8 +#define XGMAC_MAC_TX_LF_RF_TERM_PKT_CNT_REG 0x1CC +#define XGMAC_MAC_TX_SN_MISMATCH_PKT_CNT_REG 0x1D0 +#define XGMAC_MAC_RX_ERR_MSG_CNT_REG 0x1D4 +#define XGMAC_MAC_RX_ERR_EFD_CNT_REG 0x1D8 +#define XGMAC_MAC_ERR_INFO_REG 0x1DC +#define XGMAC_MAC_DBG_INFO_REG 0x1E0 + +#define XGMAC_PCS_BASER_SYNC_THD_REG 0x330 +#define XGMAC_PCS_STATUS1_REG 0x404 +#define XGMAC_PCS_BASER_STATUS1_REG 0x410 +#define XGMAC_PCS_BASER_STATUS2_REG 0x414 +#define XGMAC_PCS_BASER_SEEDA_0_REG 0x420 +#define XGMAC_PCS_BASER_SEEDA_1_REG 0x424 +#define XGMAC_PCS_BASER_SEEDB_0_REG 0x428 +#define XGMAC_PCS_BASER_SEEDB_1_REG 0x42C +#define XGMAC_PCS_BASER_TEST_CONTROL_REG 0x430 +#define XGMAC_PCS_BASER_TEST_ERR_CNT_REG 0x434 +#define XGMAC_PCS_DBG_INFO_REG 0x4C0 +#define XGMAC_PCS_DBG_INFO1_REG 0x4C4 +#define XGMAC_PCS_DBG_INFO2_REG 0x4C8 +#define XGMAC_PCS_DBG_INFO3_REG 0x4CC + +#define XGMAC_PMA_ENABLE_REG 0x700 +#define XGMAC_PMA_CONTROL_REG 0x704 +#define XGMAC_PMA_SIGNAL_STATUS_REG 0x708 +#define XGMAC_PMA_DBG_INFO_REG 0x70C +#define XGMAC_PMA_FEC_ABILITY_REG 0x740 +#define XGMAC_PMA_FEC_CONTROL_REG 0x744 +#define XGMAC_PMA_FEC_CORR_BLOCK_CNT__REG 0x750 +#define XGMAC_PMA_FEC_UNCORR_BLOCK_CNT__REG 0x760 + +#define XGMAC_TX_PKTS_FRAGMENT 0x0000 +#define XGMAC_TX_PKTS_UNDERSIZE 0x0008 +#define XGMAC_TX_PKTS_UNDERMIN 0x0010 +#define XGMAC_TX_PKTS_64OCTETS 0x0018 +#define XGMAC_TX_PKTS_65TO127OCTETS 0x0020 +#define XGMAC_TX_PKTS_128TO255OCTETS 0x0028 +#define XGMAC_TX_PKTS_256TO511OCTETS 0x0030 +#define XGMAC_TX_PKTS_512TO1023OCTETS 0x0038 +#define XGMAC_TX_PKTS_1024TO1518OCTETS 0x0040 +#define XGMAC_TX_PKTS_1519TOMAXOCTETS 0x0048 +#define XGMAC_TX_PKTS_1519TOMAXOCTETSOK 0x0050 +#define XGMAC_TX_PKTS_OVERSIZE 0x0058 +#define XGMAC_TX_PKTS_JABBER 0x0060 +#define XGMAC_TX_GOODPKTS 0x0068 +#define XGMAC_TX_GOODOCTETS 0x0070 +#define XGMAC_TX_TOTAL_PKTS 0x0078 +#define XGMAC_TX_TOTALOCTETS 0x0080 +#define XGMAC_TX_UNICASTPKTS 0x0088 +#define XGMAC_TX_MULTICASTPKTS 0x0090 +#define XGMAC_TX_BROADCASTPKTS 0x0098 +#define XGMAC_TX_PRI0PAUSEPKTS 0x00a0 +#define XGMAC_TX_PRI1PAUSEPKTS 0x00a8 +#define XGMAC_TX_PRI2PAUSEPKTS 0x00b0 +#define XGMAC_TX_PRI3PAUSEPKTS 0x00b8 +#define XGMAC_TX_PRI4PAUSEPKTS 0x00c0 +#define XGMAC_TX_PRI5PAUSEPKTS 0x00c8 +#define XGMAC_TX_PRI6PAUSEPKTS 0x00d0 +#define XGMAC_TX_PRI7PAUSEPKTS 0x00d8 +#define XGMAC_TX_MACCTRLPKTS 0x00e0 +#define XGMAC_TX_1731PKTS 0x00e8 +#define XGMAC_TX_1588PKTS 0x00f0 +#define XGMAC_RX_FROMAPPGOODPKTS 0x00f8 +#define XGMAC_RX_FROMAPPBADPKTS 0x0100 +#define XGMAC_TX_ERRALLPKTS 0x0108 + +#define XGMAC_RX_PKTS_FRAGMENT 0x0110 +#define XGMAC_RX_PKTSUNDERSIZE 0x0118 +#define XGMAC_RX_PKTS_UNDERMIN 0x0120 +#define XGMAC_RX_PKTS_64OCTETS 0x0128 +#define XGMAC_RX_PKTS_65TO127OCTETS 0x0130 +#define XGMAC_RX_PKTS_128TO255OCTETS 0x0138 +#define XGMAC_RX_PKTS_256TO511OCTETS 0x0140 +#define XGMAC_RX_PKTS_512TO1023OCTETS 0x0148 +#define XGMAC_RX_PKTS_1024TO1518OCTETS 0x0150 +#define XGMAC_RX_PKTS_1519TOMAXOCTETS 0x0158 +#define XGMAC_RX_PKTS_1519TOMAXOCTETSOK 0x0160 +#define XGMAC_RX_PKTS_OVERSIZE 0x0168 +#define XGMAC_RX_PKTS_JABBER 0x0170 +#define XGMAC_RX_GOODPKTS 0x0178 +#define XGMAC_RX_GOODOCTETS 0x0180 +#define XGMAC_RX_TOTAL_PKTS 0x0188 +#define XGMAC_RX_TOTALOCTETS 0x0190 +#define XGMAC_RX_UNICASTPKTS 0x0198 +#define XGMAC_RX_MULTICASTPKTS 0x01a0 +#define XGMAC_RX_BROADCASTPKTS 0x01a8 +#define XGMAC_RX_PRI0PAUSEPKTS 0x01b0 +#define XGMAC_RX_PRI1PAUSEPKTS 0x01b8 +#define XGMAC_RX_PRI2PAUSEPKTS 0x01c0 +#define XGMAC_RX_PRI3PAUSEPKTS 0x01c8 +#define XGMAC_RX_PRI4PAUSEPKTS 0x01d0 +#define XGMAC_RX_PRI5PAUSEPKTS 0x01d8 +#define XGMAC_RX_PRI6PAUSEPKTS 0x01e0 +#define XGMAC_RX_PRI7PAUSEPKTS 0x01e8 +#define XGMAC_RX_MACCTRLPKTS 0x01f0 +#define XGMAC_TX_SENDAPPGOODPKTS 0x01f8 +#define XGMAC_TX_SENDAPPBADPKTS 0x0200 +#define XGMAC_RX_1731PKTS 0x0208 +#define XGMAC_RX_SYMBOLERRPKTS 0x0210 +#define XGMAC_RX_FCSERRPKTS 0x0218 + +#define XGMAC_TRX_CORE_SRST_M 0x2080 + +#define DSAF_CFG_EN_S 0 +#define DSAF_CFG_TC_MODE_S 1 +#define DSAF_CFG_CRC_EN_S 2 +#define DSAF_CFG_SBM_INIT_S 3 +#define DSAF_CFG_MIX_MODE_S 4 +#define DSAF_CFG_STP_MODE_S 5 +#define DSAF_CFG_LOCA_ADDR_EN_S 6 + +#define DSAF_CNT_CLR_CE_S 0 +#define DSAF_SNAP_EN_S 1 + +#define HNS_DSAF_PFC_UNIT_CNT_FOR_XGE 41 +#define HNS_DSAF_PFC_UNIT_CNT_FOR_GE_1000 410 +#define HNS_DSAF_PFC_UNIT_CNT_FOR_GE_2500 103 + +#define DSAF_PFC_UNINT_CNT_M ((1ULL << 9) - 1) +#define DSAF_PFC_UNINT_CNT_S 0 + +#define DSAF_PPE_QID_CFG_M 0xFF +#define DSAF_PPE_QID_CFG_S 0 + +#define DSAF_SW_PORT_TYPE_M 3 +#define DSAF_SW_PORT_TYPE_S 0 + +#define DSAF_STP_PORT_TYPE_M 7 +#define DSAF_STP_PORT_TYPE_S 0 + +#define DSAF_INODE_IN_PORT_NUM_M 7 +#define DSAF_INODE_IN_PORT_NUM_S 0 + +#define HNS_DSAF_I4TC_CFG 0x18688688 +#define HNS_DSAF_I8TC_CFG 0x18FAC688 + +#define DSAF_SBM_CFG_SHCUT_EN_S 0 +#define DSAF_SBM_CFG_EN_S 1 +#define DSAF_SBM_CFG_MIB_EN_S 2 +#define DSAF_SBM_CFG_ECC_INVERT_EN_S 3 + +#define DSAF_SBM_CFG0_VC1_MAX_BUF_NUM_S 0 +#define DSAF_SBM_CFG0_VC1_MAX_BUF_NUM_M (((1ULL << 10) - 1) << 0) +#define DSAF_SBM_CFG0_VC0_MAX_BUF_NUM_S 10 +#define DSAF_SBM_CFG0_VC0_MAX_BUF_NUM_M (((1ULL << 10) - 1) << 10) +#define DSAF_SBM_CFG0_COM_MAX_BUF_NUM_S 20 +#define DSAF_SBM_CFG0_COM_MAX_BUF_NUM_M (((1ULL << 11) - 1) << 20) + +#define DSAF_SBM_CFG1_TC4_MAX_BUF_NUM_S 0 +#define DSAF_SBM_CFG1_TC4_MAX_BUF_NUM_M (((1ULL << 10) - 1) << 0) +#define DSAF_SBM_CFG1_TC0_MAX_BUF_NUM_S 10 +#define DSAF_SBM_CFG1_TC0_MAX_BUF_NUM_M (((1ULL << 10) - 1) << 10) + +#define DSAF_SBM_CFG2_SET_BUF_NUM_S 0 +#define DSAF_SBM_CFG2_SET_BUF_NUM_M (((1ULL << 10) - 1) << 0) +#define DSAF_SBM_CFG2_RESET_BUF_NUM_S 10 +#define DSAF_SBM_CFG2_RESET_BUF_NUM_M (((1ULL << 10) - 1) << 10) + +#define DSAF_SBM_CFG3_SET_BUF_NUM_NO_PFC_S 0 +#define DSAF_SBM_CFG3_SET_BUF_NUM_NO_PFC_M (((1ULL << 10) - 1) << 0) +#define DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_S 10 +#define DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_M (((1ULL << 10) - 1) << 10) + +#define DSAF_TBL_TCAM_ADDR_S 0 +#define DSAF_TBL_TCAM_ADDR_M ((1ULL << 9) - 1) + +#define DSAF_TBL_LINE_ADDR_S 0 +#define DSAF_TBL_LINE_ADDR_M ((1ULL << 15) - 1) + +#define DSAF_TBL_MCAST_CFG4_VM128_112_S 0 +#define DSAF_TBL_MCAST_CFG4_VM128_112_M (((1ULL << 7) - 1) << 0) +#define DSAF_TBL_MCAST_CFG4_ITEM_VLD_S 7 +#define DSAF_TBL_MCAST_CFG4_OLD_EN_S 8 + +#define DSAF_TBL_MCAST_CFG0_XGE5_0_S 0 +#define DSAF_TBL_MCAST_CFG0_XGE5_0_M (((1ULL << 6) - 1) << 0) +#define DSAF_TBL_MCAST_CFG0_VM25_0_S 6 +#define DSAF_TBL_MCAST_CFG0_VM25_0_M (((1ULL << 26) - 1) << 6) + +#define DSAF_TBL_UCAST_CFG1_OUT_PORT_S 0 +#define DSAF_TBL_UCAST_CFG1_OUT_PORT_M (((1ULL << 8) - 1) << 0) +#define DSAF_TBL_UCAST_CFG1_DVC_S 8 +#define DSAF_TBL_UCAST_CFG1_MAC_DISCARD_S 9 +#define DSAF_TBL_UCAST_CFG1_ITEM_VLD_S 10 +#define DSAF_TBL_UCAST_CFG1_OLD_EN_S 11 + +#define DSAF_TBL_LINE_CFG_OUT_PORT_S 0 +#define DSAF_TBL_LINE_CFG_OUT_PORT_M (((1ULL << 8) - 1) << 0) +#define DSAF_TBL_LINE_CFG_DVC_S 8 +#define DSAF_TBL_LINE_CFG_MAC_DISCARD_S 9 + +#define DSAF_TBL_PUL_OLD_RSLT_RE_S 0 +#define DSAF_TBL_PUL_MCAST_VLD_S 1 +#define DSAF_TBL_PUL_TCAM_DATA_VLD_S 2 +#define DSAF_TBL_PUL_UCAST_VLD_S 3 +#define DSAF_TBL_PUL_LINE_VLD_S 4 +#define DSAF_TBL_PUL_TCAM_LOAD_S 5 +#define DSAF_TBL_PUL_LINE_LOAD_S 6 + +#define DSAF_TBL_DFX_LINE_LKUP_NUM_EN_S 0 +#define DSAF_TBL_DFX_UC_LKUP_NUM_EN_S 1 +#define DSAF_TBL_DFX_MC_LKUP_NUM_EN_S 2 +#define DSAF_TBL_DFX_BC_LKUP_NUM_EN_S 3 +#define DSAF_TBL_DFX_RAM_ERR_INJECT_EN_S 4 + +#define DSAF_VOQ_BP_ALL_DOWNTHRD_S 0 +#define DSAF_VOQ_BP_ALL_DOWNTHRD_M (((1ULL << 10) - 1) << 0) +#define DSAF_VOQ_BP_ALL_UPTHRD_S 10 +#define DSAF_VOQ_BP_ALL_UPTHRD_M (((1ULL << 10) - 1) << 10) + +#define DSAF_XGE_GE_WORK_MODE_S 0 +#define DSAF_XGE_GE_LOOPBACK_S 1 + +#define DSAF_FC_XGE_TX_PAUSE_S 0 +#define DSAF_REGS_XGE_CNT_CAR_S 1 + +#define PPE_CFG_QID_MODE_DEF_QID_S 0 +#define PPE_CFG_QID_MODE_DEF_QID_M (0xff << PPE_CFG_QID_MODE_DEF_QID_S) + +#define PPE_CFG_QID_MODE_CF_QID_MODE_S 8 +#define PPE_CFG_QID_MODE_CF_QID_MODE_M (0x7 << PPE_CFG_QID_MODE_CF_QID_MODE_S) + +#define PPE_CNT_CLR_CE_B 0 +#define PPE_CNT_CLR_SNAP_EN_B 1 + +#define PPE_COMMON_CNT_CLR_CE_B 0 +#define PPE_COMMON_CNT_CLR_SNAP_EN_B 1 + +#define GMAC_DUPLEX_TYPE_B 0 + +#define GMAC_FC_TX_TIMER_S 0 +#define GMAC_FC_TX_TIMER_M 0xffff + +#define GMAC_MAX_FRM_SIZE_S 0 +#define GMAC_MAX_FRM_SIZE_M 0xffff + +#define GMAC_PORT_MODE_S 0 +#define GMAC_PORT_MODE_M 0xf + +#define GMAC_RGMII_1000M_DELAY_B 4 +#define GMAC_MII_TX_EDGE_SEL_B 5 +#define GMAC_FIFO_ERR_AUTO_RST_B 6 +#define GMAC_DBG_CLK_LOS_MSK_B 7 + +#define GMAC_PORT_RX_EN_B 1 +#define GMAC_PORT_TX_EN_B 2 + +#define GMAC_PAUSE_EN_RX_FDFC_B 0 +#define GMAC_PAUSE_EN_TX_FDFC_B 1 +#define GMAC_PAUSE_EN_TX_HDFC_B 2 + +#define GMAC_SHORT_RUNTS_THR_S 0 +#define GMAC_SHORT_RUNTS_THR_M 0x1f + +#define GMAC_AN_NEG_STAT_FD_B 5 +#define GMAC_AN_NEG_STAT_HD_B 6 +#define GMAC_AN_NEG_STAT_RF1_DUPLIEX_B 12 +#define GMAC_AN_NEG_STAT_RF2_B 13 + +#define GMAC_AN_NEG_STAT_NP_LNK_OK_B 15 +#define GMAC_AN_NEG_STAT_RX_SYNC_OK_B 20 +#define GMAC_AN_NEG_STAT_AN_DONE_B 21 + +#define GMAC_AN_NEG_STAT_PS_S 7 +#define GMAC_AN_NEG_STAT_PS_M (0x3 << GMAC_AN_NEG_STAT_PS_S) + +#define GMAC_AN_NEG_STAT_SPEED_S 10 +#define GMAC_AN_NEG_STAT_SPEED_M (0x3 << GMAC_AN_NEG_STAT_SPEED_S) + +#define GMAC_TX_AN_EN_B 5 +#define GMAC_TX_CRC_ADD_B 6 +#define GMAC_TX_PAD_EN_B 7 + +#define GMAC_LINE_LOOPBACK_B 0 + +#define GMAC_LP_REG_CF_EXT_DRV_LP_B 1 +#define GMAC_LP_REG_CF2MI_LP_EN_B 2 + +#define GMAC_MODE_CHANGE_EB_B 0 + +#define GMAC_RECV_CTRL_STRIP_PAD_EN_B 3 +#define GMAC_RECV_CTRL_RUNT_PKT_EN_B 4 + +#define GMAC_TX_LOOP_PKT_HIG_PRI_B 0 +#define GMAC_TX_LOOP_PKT_EN_B 1 + +#define XGMAC_PORT_MODE_TX_S 0x0 +#define XGMAC_PORT_MODE_TX_M (0x3 << XGMAC_PORT_MODE_TX_S) +#define XGMAC_PORT_MODE_TX_40G_B 0x3 +#define XGMAC_PORT_MODE_RX_S 0x4 +#define XGMAC_PORT_MODE_RX_M (0x3 << XGMAC_PORT_MODE_RX_S) +#define XGMAC_PORT_MODE_RX_40G_B 0x7 + +#define XGMAC_ENABLE_TX_B 0 +#define XGMAC_ENABLE_RX_B 1 + +#define XGMAC_CTL_TX_FCS_B 0 +#define XGMAC_CTL_TX_PAD_B 1 +#define XGMAC_CTL_TX_PREAMBLE_TRANS_B 3 +#define XGMAC_CTL_TX_UNDER_MIN_ERR_B 4 +#define XGMAC_CTL_TX_TRUNCATE_B 5 +#define XGMAC_CTL_TX_1588_B 8 +#define XGMAC_CTL_TX_1731_B 9 +#define XGMAC_CTL_TX_PFC_B 10 +#define XGMAC_CTL_RX_FCS_B 16 +#define XGMAC_CTL_RX_FCS_STRIP_B 17 +#define XGMAC_CTL_RX_PREAMBLE_TRANS_B 19 +#define XGMAC_CTL_RX_UNDER_MIN_ERR_B 20 +#define XGMAC_CTL_RX_TRUNCATE_B 21 +#define XGMAC_CTL_RX_1588_B 24 +#define XGMAC_CTL_RX_1731_B 25 +#define XGMAC_CTL_RX_PFC_B 26 + +#define XGMAC_PMA_FEC_CTL_TX_B 0 +#define XGMAC_PMA_FEC_CTL_RX_B 1 +#define XGMAC_PMA_FEC_CTL_ERR_EN 2 +#define XGMAC_PMA_FEC_CTL_ERR_SH 3 + +#define XGMAC_PAUSE_CTL_TX_B 0 +#define XGMAC_PAUSE_CTL_RX_B 1 +#define XGMAC_PAUSE_CTL_RSP_MODE_B 2 +#define XGMAC_PAUSE_CTL_TX_XOFF_B 3 + +static inline void dsaf_write_reg(void *base, u32 reg, u32 value) +{ + u8 __iomem *reg_addr = ACCESS_ONCE(base); + + writel(value, reg_addr + reg); +} + +#define dsaf_write_dev(a, reg, value) \ + dsaf_write_reg((a)->io_base, (reg), (value)) + +static inline u32 dsaf_read_reg(u8 *base, u32 reg) +{ + u8 __iomem *reg_addr = ACCESS_ONCE(base); + + return readl(reg_addr + reg); +} + +#define dsaf_read_dev(a, reg) \ + dsaf_read_reg((a)->io_base, (reg)) + +#define dsaf_set_field(origin, mask, shift, val) \ + do { \ + (origin) &= (~(mask)); \ + (origin) |= (((val) << (shift)) & (mask)); \ + } while (0) + +#define dsaf_set_bit(origin, shift, val) \ + dsaf_set_field((origin), (1ull << (shift)), (shift), (val)) + +static inline void dsaf_set_reg_field(void *base, u32 reg, u32 mask, u32 shift, + u32 val) +{ + u32 origin = dsaf_read_reg(base, reg); + + dsaf_set_field(origin, mask, shift, val); + dsaf_write_reg(base, reg, origin); +} + +#define dsaf_set_dev_field(dev, reg, mask, shift, val) \ + dsaf_set_reg_field((dev)->io_base, (reg), (mask), (shift), (val)) + +#define dsaf_set_dev_bit(dev, reg, bit, val) \ + dsaf_set_reg_field((dev)->io_base, (reg), (1ull << (bit)), (bit), (val)) + +#define dsaf_get_field(origin, mask, shift) (((origin) & (mask)) >> (shift)) + +#define dsaf_get_bit(origin, shift) \ + dsaf_get_field((origin), (1ull << (shift)), (shift)) + +static inline u32 dsaf_get_reg_field(void *base, u32 reg, u32 mask, u32 shift) +{ + u32 origin; + + origin = dsaf_read_reg(base, reg); + return dsaf_get_field(origin, mask, shift); +} + +#define dsaf_get_dev_field(dev, reg, mask, shift) \ + dsaf_get_reg_field((dev)->io_base, (reg), (mask), (shift)) + +#define dsaf_get_dev_bit(dev, reg, bit) \ + dsaf_get_reg_field((dev)->io_base, (reg), (1ull << (bit)), (bit)) + +#define dsaf_write_b(addr, data)\ + writeb((data), (__iomem unsigned char *)(addr)) +#define dsaf_read_b(addr)\ + readb((__iomem unsigned char *)(addr)) + +#define hns_mac_reg_read64(drv, offset) \ + readq((__iomem void *)(((u64)(drv)->io_base + 0xc00 + (offset)))) + +#endif /* _DSAF_REG_H */ diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c new file mode 100644 index 000000000000..fe7fa1d6852b --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c @@ -0,0 +1,836 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * 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/of_mdio.h> +#include "hns_dsaf_main.h" +#include "hns_dsaf_mac.h" +#include "hns_dsaf_xgmac.h" +#include "hns_dsaf_reg.h" + +static const struct mac_stats_string g_xgmac_stats_string[] = { + {"xgmac_tx_bad_pkts_minto64", MAC_STATS_FIELD_OFF(tx_fragment_err)}, + {"xgmac_tx_good_pkts_minto64", MAC_STATS_FIELD_OFF(tx_undersize)}, + {"xgmac_tx_total_pkts_minto64", MAC_STATS_FIELD_OFF(tx_under_min_pkts)}, + {"xgmac_tx_pkts_64", MAC_STATS_FIELD_OFF(tx_64bytes)}, + {"xgmac_tx_pkts_65to127", MAC_STATS_FIELD_OFF(tx_65to127)}, + {"xgmac_tx_pkts_128to255", MAC_STATS_FIELD_OFF(tx_128to255)}, + {"xgmac_tx_pkts_256to511", MAC_STATS_FIELD_OFF(tx_256to511)}, + {"xgmac_tx_pkts_512to1023", MAC_STATS_FIELD_OFF(tx_512to1023)}, + {"xgmac_tx_pkts_1024to1518", MAC_STATS_FIELD_OFF(tx_1024to1518)}, + {"xgmac_tx_pkts_1519tomax", MAC_STATS_FIELD_OFF(tx_1519tomax)}, + {"xgmac_tx_good_pkts_1519tomax", + MAC_STATS_FIELD_OFF(tx_1519tomax_good)}, + {"xgmac_tx_good_pkts_untralmax", MAC_STATS_FIELD_OFF(tx_oversize)}, + {"xgmac_tx_bad_pkts_untralmax", MAC_STATS_FIELD_OFF(tx_jabber_err)}, + {"xgmac_tx_good_pkts_all", MAC_STATS_FIELD_OFF(tx_good_pkts)}, + {"xgmac_tx_good_byte_all", MAC_STATS_FIELD_OFF(tx_good_bytes)}, + {"xgmac_tx_total_pkt", MAC_STATS_FIELD_OFF(tx_total_pkts)}, + {"xgmac_tx_total_byt", MAC_STATS_FIELD_OFF(tx_total_bytes)}, + {"xgmac_tx_uc_pkt", MAC_STATS_FIELD_OFF(tx_uc_pkts)}, + {"xgmac_tx_mc_pkt", MAC_STATS_FIELD_OFF(tx_mc_pkts)}, + {"xgmac_tx_bc_pkt", MAC_STATS_FIELD_OFF(tx_bc_pkts)}, + {"xgmac_tx_pause_frame_num", MAC_STATS_FIELD_OFF(tx_pfc_tc0)}, + {"xgmac_tx_pfc_per_1pause_framer", MAC_STATS_FIELD_OFF(tx_pfc_tc1)}, + {"xgmac_tx_pfc_per_2pause_framer", MAC_STATS_FIELD_OFF(tx_pfc_tc2)}, + {"xgmac_tx_pfc_per_3pause_framer", MAC_STATS_FIELD_OFF(tx_pfc_tc3)}, + {"xgmac_tx_pfc_per_4pause_framer", MAC_STATS_FIELD_OFF(tx_pfc_tc4)}, + {"xgmac_tx_pfc_per_5pause_framer", MAC_STATS_FIELD_OFF(tx_pfc_tc5)}, + {"xgmac_tx_pfc_per_6pause_framer", MAC_STATS_FIELD_OFF(tx_pfc_tc6)}, + {"xgmac_tx_pfc_per_7pause_framer", MAC_STATS_FIELD_OFF(tx_pfc_tc7)}, + {"xgmac_tx_mac_ctrol_frame", MAC_STATS_FIELD_OFF(tx_ctrl)}, + {"xgmac_tx_1731_pkts", MAC_STATS_FIELD_OFF(tx_1731_pkts)}, + {"xgmac_tx_1588_pkts", MAC_STATS_FIELD_OFF(tx_1588_pkts)}, + {"xgmac_rx_good_pkt_from_dsaf", MAC_STATS_FIELD_OFF(rx_good_from_sw)}, + {"xgmac_rx_bad_pkt_from_dsaf", MAC_STATS_FIELD_OFF(rx_bad_from_sw)}, + {"xgmac_tx_bad_pkt_64tomax", MAC_STATS_FIELD_OFF(tx_bad_pkts)}, + + {"xgmac_rx_not_well_pkt", MAC_STATS_FIELD_OFF(rx_fragment_err)}, + {"xgmac_rx_good_well_pkt", MAC_STATS_FIELD_OFF(rx_undersize)}, + {"xgmac_rx_total_pkt", MAC_STATS_FIELD_OFF(rx_under_min)}, + {"xgmac_rx_pkt_64", MAC_STATS_FIELD_OFF(rx_64bytes)}, + {"xgmac_rx_pkt_65to127", MAC_STATS_FIELD_OFF(rx_65to127)}, + {"xgmac_rx_pkt_128to255", MAC_STATS_FIELD_OFF(rx_128to255)}, + {"xgmac_rx_pkt_256to511", MAC_STATS_FIELD_OFF(rx_256to511)}, + {"xgmac_rx_pkt_512to1023", MAC_STATS_FIELD_OFF(rx_512to1023)}, + {"xgmac_rx_pkt_1024to1518", MAC_STATS_FIELD_OFF(rx_1024to1518)}, + {"xgmac_rx_pkt_1519tomax", MAC_STATS_FIELD_OFF(rx_1519tomax)}, + {"xgmac_rx_good_pkt_1519tomax", MAC_STATS_FIELD_OFF(rx_1519tomax_good)}, + {"xgmac_rx_good_pkt_untramax", MAC_STATS_FIELD_OFF(rx_oversize)}, + {"xgmac_rx_bad_pkt_untramax", MAC_STATS_FIELD_OFF(rx_jabber_err)}, + {"xgmac_rx_good_pkt", MAC_STATS_FIELD_OFF(rx_good_pkts)}, + {"xgmac_rx_good_byt", MAC_STATS_FIELD_OFF(rx_good_bytes)}, + {"xgmac_rx_pkt", MAC_STATS_FIELD_OFF(rx_total_pkts)}, + {"xgmac_rx_byt", MAC_STATS_FIELD_OFF(rx_total_bytes)}, + {"xgmac_rx_uc_pkt", MAC_STATS_FIELD_OFF(rx_uc_pkts)}, + {"xgmac_rx_mc_pkt", MAC_STATS_FIELD_OFF(rx_mc_pkts)}, + {"xgmac_rx_bc_pkt", MAC_STATS_FIELD_OFF(rx_bc_pkts)}, + {"xgmac_rx_pause_frame_num", MAC_STATS_FIELD_OFF(rx_pfc_tc0)}, + {"xgmac_rx_pfc_per_1pause_frame", MAC_STATS_FIELD_OFF(rx_pfc_tc1)}, + {"xgmac_rx_pfc_per_2pause_frame", MAC_STATS_FIELD_OFF(rx_pfc_tc2)}, + {"xgmac_rx_pfc_per_3pause_frame", MAC_STATS_FIELD_OFF(rx_pfc_tc3)}, + {"xgmac_rx_pfc_per_4pause_frame", MAC_STATS_FIELD_OFF(rx_pfc_tc4)}, + {"xgmac_rx_pfc_per_5pause_frame", MAC_STATS_FIELD_OFF(rx_pfc_tc5)}, + {"xgmac_rx_pfc_per_6pause_frame", MAC_STATS_FIELD_OFF(rx_pfc_tc6)}, + {"xgmac_rx_pfc_per_7pause_frame", MAC_STATS_FIELD_OFF(rx_pfc_tc7)}, + {"xgmac_rx_mac_control", MAC_STATS_FIELD_OFF(rx_unknown_ctrl)}, + {"xgmac_tx_good_pkt_todsaf", MAC_STATS_FIELD_OFF(tx_good_to_sw)}, + {"xgmac_tx_bad_pkt_todsaf", MAC_STATS_FIELD_OFF(tx_bad_to_sw)}, + {"xgmac_rx_1731_pkt", MAC_STATS_FIELD_OFF(rx_1731_pkts)}, + {"xgmac_rx_symbol_err_pkt", MAC_STATS_FIELD_OFF(rx_symbol_err)}, + {"xgmac_rx_fcs_pkt", MAC_STATS_FIELD_OFF(rx_fcs_err)} +}; + +/** + *hns_xgmac_tx_enable - xgmac port tx enable + *@drv: mac driver + *@value: value of enable + */ +static void hns_xgmac_tx_enable(struct mac_driver *drv, u32 value) +{ + dsaf_set_dev_bit(drv, XGMAC_MAC_ENABLE_REG, XGMAC_ENABLE_TX_B, !!value); +} + +/** + *hns_xgmac_rx_enable - xgmac port rx enable + *@drv: mac driver + *@value: value of enable + */ +static void hns_xgmac_rx_enable(struct mac_driver *drv, u32 value) +{ + dsaf_set_dev_bit(drv, XGMAC_MAC_ENABLE_REG, XGMAC_ENABLE_RX_B, !!value); +} + +/** + *hns_xgmac_enable - enable xgmac port + *@drv: mac driver + *@mode: mode of mac port + */ +static void hns_xgmac_enable(void *mac_drv, enum mac_commom_mode mode) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + struct dsaf_device *dsaf_dev + = (struct dsaf_device *)dev_get_drvdata(drv->dev); + u32 port = drv->mac_id; + + hns_dsaf_xge_core_srst_by_port(dsaf_dev, port, 1); + mdelay(10); + + /*enable XGE rX/tX */ + if (mode == MAC_COMM_MODE_TX) { + hns_xgmac_tx_enable(drv, 1); + } else if (mode == MAC_COMM_MODE_RX) { + hns_xgmac_rx_enable(drv, 1); + } else if (mode == MAC_COMM_MODE_RX_AND_TX) { + hns_xgmac_tx_enable(drv, 1); + hns_xgmac_rx_enable(drv, 1); + } else { + dev_err(drv->dev, "error mac mode:%d\n", mode); + } +} + +/** + *hns_xgmac_disable - disable xgmac port + *@mac_drv: mac driver + *@mode: mode of mac port + */ +static void hns_xgmac_disable(void *mac_drv, enum mac_commom_mode mode) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + struct dsaf_device *dsaf_dev + = (struct dsaf_device *)dev_get_drvdata(drv->dev); + u32 port = drv->mac_id; + + if (mode == MAC_COMM_MODE_TX) { + hns_xgmac_tx_enable(drv, 0); + } else if (mode == MAC_COMM_MODE_RX) { + hns_xgmac_rx_enable(drv, 0); + } else if (mode == MAC_COMM_MODE_RX_AND_TX) { + hns_xgmac_tx_enable(drv, 0); + hns_xgmac_rx_enable(drv, 0); + } + + mdelay(10); + hns_dsaf_xge_core_srst_by_port(dsaf_dev, port, 0); +} + +/** + *hns_xgmac_pma_fec_enable - xgmac PMA FEC enable + *@drv: mac driver + *@tx_value: tx value + *@rx_value: rx value + *return status + */ +static void hns_xgmac_pma_fec_enable(struct mac_driver *drv, u32 tx_value, + u32 rx_value) +{ + u32 origin = dsaf_read_dev(drv, XGMAC_PMA_FEC_CONTROL_REG); + + dsaf_set_bit(origin, XGMAC_PMA_FEC_CTL_TX_B, !!tx_value); + dsaf_set_bit(origin, XGMAC_PMA_FEC_CTL_RX_B, !!rx_value); + dsaf_write_dev(drv, XGMAC_PMA_FEC_CONTROL_REG, origin); +} + +/* clr exc irq for xge*/ +static void hns_xgmac_exc_irq_en(struct mac_driver *drv, u32 en) +{ + u32 clr_vlue = 0xfffffffful; + u32 msk_vlue = en ? 0xfffffffful : 0; /*1 is en, 0 is dis*/ + + dsaf_write_dev(drv, XGMAC_INT_STATUS_REG, clr_vlue); + dsaf_write_dev(drv, XGMAC_INT_ENABLE_REG, msk_vlue); +} + +/** + *hns_xgmac_init - initialize XGE + *@mac_drv: mac driver + */ +static void hns_xgmac_init(void *mac_drv) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + struct dsaf_device *dsaf_dev + = (struct dsaf_device *)dev_get_drvdata(drv->dev); + u32 port = drv->mac_id; + + hns_dsaf_xge_srst_by_port(dsaf_dev, port, 0); + mdelay(100); + hns_dsaf_xge_srst_by_port(dsaf_dev, port, 1); + + mdelay(100); + hns_xgmac_exc_irq_en(drv, 0); + + hns_xgmac_pma_fec_enable(drv, 0x0, 0x0); + + hns_xgmac_disable(mac_drv, MAC_COMM_MODE_RX_AND_TX); +} + +/** + *hns_xgmac_config_pad_and_crc - set xgmac pad and crc enable the same time + *@mac_drv: mac driver + *@newval:enable of pad and crc + */ +static void hns_xgmac_config_pad_and_crc(void *mac_drv, u8 newval) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + u32 origin = dsaf_read_dev(drv, XGMAC_MAC_CONTROL_REG); + + dsaf_set_bit(origin, XGMAC_CTL_TX_PAD_B, !!newval); + dsaf_set_bit(origin, XGMAC_CTL_TX_FCS_B, !!newval); + dsaf_set_bit(origin, XGMAC_CTL_RX_FCS_B, !!newval); + dsaf_write_dev(drv, XGMAC_MAC_CONTROL_REG, origin); +} + +/** + *hns_xgmac_pausefrm_cfg - set pause param about xgmac + *@mac_drv: mac driver + *@newval:enable of pad and crc + */ +static void hns_xgmac_pausefrm_cfg(void *mac_drv, u32 rx_en, u32 tx_en) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + u32 origin = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_CTRL_REG); + + dsaf_set_bit(origin, XGMAC_PAUSE_CTL_TX_B, !!tx_en); + dsaf_set_bit(origin, XGMAC_PAUSE_CTL_RX_B, !!rx_en); + dsaf_write_dev(drv, XGMAC_MAC_PAUSE_CTRL_REG, origin); +} + +static void hns_xgmac_set_pausefrm_mac_addr(void *mac_drv, char *mac_addr) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + u32 high_val = mac_addr[1] | (mac_addr[0] << 8); + u32 low_val = mac_addr[5] | (mac_addr[4] << 8) + | (mac_addr[3] << 16) | (mac_addr[2] << 24); + dsaf_write_dev(drv, XGMAC_MAC_PAUSE_LOCAL_MAC_L_REG, low_val); + dsaf_write_dev(drv, XGMAC_MAC_PAUSE_LOCAL_MAC_H_REG, high_val); +} + +/** + *hns_xgmac_set_rx_ignore_pause_frames - set rx pause param about xgmac + *@mac_drv: mac driver + *@enable:enable rx pause param + */ +static void hns_xgmac_set_rx_ignore_pause_frames(void *mac_drv, u32 enable) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + dsaf_set_dev_bit(drv, XGMAC_MAC_PAUSE_CTRL_REG, + XGMAC_PAUSE_CTL_RX_B, !!enable); +} + +/** + *hns_xgmac_set_tx_auto_pause_frames - set tx pause param about xgmac + *@mac_drv: mac driver + *@enable:enable tx pause param + */ +static void hns_xgmac_set_tx_auto_pause_frames(void *mac_drv, u16 enable) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + dsaf_set_dev_bit(drv, XGMAC_MAC_PAUSE_CTRL_REG, + XGMAC_PAUSE_CTL_TX_B, !!enable); + + /*if enable is not zero ,set tx pause time */ + if (enable) + dsaf_write_dev(drv, XGMAC_MAC_PAUSE_TIME_REG, enable); +} + +/** + *hns_xgmac_get_id - get xgmac port id + *@mac_drv: mac driver + *@newval:xgmac max frame length + */ +static void hns_xgmac_get_id(void *mac_drv, u8 *mac_id) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + *mac_id = drv->mac_id; +} + +/** + *hns_xgmac_config_max_frame_length - set xgmac max frame length + *@mac_drv: mac driver + *@newval:xgmac max frame length + */ +static void hns_xgmac_config_max_frame_length(void *mac_drv, u16 newval) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + dsaf_write_dev(drv, XGMAC_MAC_MAX_PKT_SIZE_REG, newval); +} + +void hns_xgmac_update_stats(void *mac_drv) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + struct mac_hw_stats *hw_stats = &drv->mac_cb->hw_stats; + + /* TX */ + hw_stats->tx_fragment_err + = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_FRAGMENT); + hw_stats->tx_undersize + = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_UNDERSIZE); + hw_stats->tx_under_min_pkts + = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_UNDERMIN); + hw_stats->tx_64bytes = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_64OCTETS); + hw_stats->tx_65to127 + = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_65TO127OCTETS); + hw_stats->tx_128to255 + = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_128TO255OCTETS); + hw_stats->tx_256to511 + = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_256TO511OCTETS); + hw_stats->tx_512to1023 + = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_512TO1023OCTETS); + hw_stats->tx_1024to1518 + = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_1024TO1518OCTETS); + hw_stats->tx_1519tomax + = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_1519TOMAXOCTETS); + hw_stats->tx_1519tomax_good + = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_1519TOMAXOCTETSOK); + hw_stats->tx_oversize = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_OVERSIZE); + hw_stats->tx_jabber_err = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_JABBER); + hw_stats->tx_good_pkts = hns_mac_reg_read64(drv, XGMAC_TX_GOODPKTS); + hw_stats->tx_good_bytes = hns_mac_reg_read64(drv, XGMAC_TX_GOODOCTETS); + hw_stats->tx_total_pkts = hns_mac_reg_read64(drv, XGMAC_TX_TOTAL_PKTS); + hw_stats->tx_total_bytes + = hns_mac_reg_read64(drv, XGMAC_TX_TOTALOCTETS); + hw_stats->tx_uc_pkts = hns_mac_reg_read64(drv, XGMAC_TX_UNICASTPKTS); + hw_stats->tx_mc_pkts = hns_mac_reg_read64(drv, XGMAC_TX_MULTICASTPKTS); + hw_stats->tx_bc_pkts = hns_mac_reg_read64(drv, XGMAC_TX_BROADCASTPKTS); + hw_stats->tx_pfc_tc0 = hns_mac_reg_read64(drv, XGMAC_TX_PRI0PAUSEPKTS); + hw_stats->tx_pfc_tc1 = hns_mac_reg_read64(drv, XGMAC_TX_PRI1PAUSEPKTS); + hw_stats->tx_pfc_tc2 = hns_mac_reg_read64(drv, XGMAC_TX_PRI2PAUSEPKTS); + hw_stats->tx_pfc_tc3 = hns_mac_reg_read64(drv, XGMAC_TX_PRI3PAUSEPKTS); + hw_stats->tx_pfc_tc4 = hns_mac_reg_read64(drv, XGMAC_TX_PRI4PAUSEPKTS); + hw_stats->tx_pfc_tc5 = hns_mac_reg_read64(drv, XGMAC_TX_PRI5PAUSEPKTS); + hw_stats->tx_pfc_tc6 = hns_mac_reg_read64(drv, XGMAC_TX_PRI6PAUSEPKTS); + hw_stats->tx_pfc_tc7 = hns_mac_reg_read64(drv, XGMAC_TX_PRI7PAUSEPKTS); + hw_stats->tx_ctrl = hns_mac_reg_read64(drv, XGMAC_TX_MACCTRLPKTS); + hw_stats->tx_1731_pkts = hns_mac_reg_read64(drv, XGMAC_TX_1731PKTS); + hw_stats->tx_1588_pkts = hns_mac_reg_read64(drv, XGMAC_TX_1588PKTS); + hw_stats->rx_good_from_sw + = hns_mac_reg_read64(drv, XGMAC_RX_FROMAPPGOODPKTS); + hw_stats->rx_bad_from_sw + = hns_mac_reg_read64(drv, XGMAC_RX_FROMAPPBADPKTS); + hw_stats->tx_bad_pkts = hns_mac_reg_read64(drv, XGMAC_TX_ERRALLPKTS); + + /* RX */ + hw_stats->rx_fragment_err + = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_FRAGMENT); + hw_stats->rx_undersize + = hns_mac_reg_read64(drv, XGMAC_RX_PKTSUNDERSIZE); + hw_stats->rx_under_min + = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_UNDERMIN); + hw_stats->rx_64bytes = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_64OCTETS); + hw_stats->rx_65to127 + = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_65TO127OCTETS); + hw_stats->rx_128to255 + = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_128TO255OCTETS); + hw_stats->rx_256to511 + = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_256TO511OCTETS); + hw_stats->rx_512to1023 + = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_512TO1023OCTETS); + hw_stats->rx_1024to1518 + = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_1024TO1518OCTETS); + hw_stats->rx_1519tomax + = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_1519TOMAXOCTETS); + hw_stats->rx_1519tomax_good + = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_1519TOMAXOCTETSOK); + hw_stats->rx_oversize = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_OVERSIZE); + hw_stats->rx_jabber_err = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_JABBER); + hw_stats->rx_good_pkts = hns_mac_reg_read64(drv, XGMAC_RX_GOODPKTS); + hw_stats->rx_good_bytes = hns_mac_reg_read64(drv, XGMAC_RX_GOODOCTETS); + hw_stats->rx_total_pkts = hns_mac_reg_read64(drv, XGMAC_RX_TOTAL_PKTS); + hw_stats->rx_total_bytes + = hns_mac_reg_read64(drv, XGMAC_RX_TOTALOCTETS); + hw_stats->rx_uc_pkts = hns_mac_reg_read64(drv, XGMAC_RX_UNICASTPKTS); + hw_stats->rx_mc_pkts = hns_mac_reg_read64(drv, XGMAC_RX_MULTICASTPKTS); + hw_stats->rx_bc_pkts = hns_mac_reg_read64(drv, XGMAC_RX_BROADCASTPKTS); + hw_stats->rx_pfc_tc0 = hns_mac_reg_read64(drv, XGMAC_RX_PRI0PAUSEPKTS); + hw_stats->rx_pfc_tc1 = hns_mac_reg_read64(drv, XGMAC_RX_PRI1PAUSEPKTS); + hw_stats->rx_pfc_tc2 = hns_mac_reg_read64(drv, XGMAC_RX_PRI2PAUSEPKTS); + hw_stats->rx_pfc_tc3 = hns_mac_reg_read64(drv, XGMAC_RX_PRI3PAUSEPKTS); + hw_stats->rx_pfc_tc4 = hns_mac_reg_read64(drv, XGMAC_RX_PRI4PAUSEPKTS); + hw_stats->rx_pfc_tc5 = hns_mac_reg_read64(drv, XGMAC_RX_PRI5PAUSEPKTS); + hw_stats->rx_pfc_tc6 = hns_mac_reg_read64(drv, XGMAC_RX_PRI6PAUSEPKTS); + hw_stats->rx_pfc_tc7 = hns_mac_reg_read64(drv, XGMAC_RX_PRI7PAUSEPKTS); + + hw_stats->rx_unknown_ctrl + = hns_mac_reg_read64(drv, XGMAC_RX_MACCTRLPKTS); + hw_stats->tx_good_to_sw + = hns_mac_reg_read64(drv, XGMAC_TX_SENDAPPGOODPKTS); + hw_stats->tx_bad_to_sw + = hns_mac_reg_read64(drv, XGMAC_TX_SENDAPPBADPKTS); + hw_stats->rx_1731_pkts = hns_mac_reg_read64(drv, XGMAC_RX_1731PKTS); + hw_stats->rx_symbol_err + = hns_mac_reg_read64(drv, XGMAC_RX_SYMBOLERRPKTS); + hw_stats->rx_fcs_err = hns_mac_reg_read64(drv, XGMAC_RX_FCSERRPKTS); +} + +/** + *hns_xgmac_free - free xgmac driver + *@mac_drv: mac driver + */ +static void hns_xgmac_free(void *mac_drv) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + struct dsaf_device *dsaf_dev + = (struct dsaf_device *)dev_get_drvdata(drv->dev); + + u32 mac_id = drv->mac_id; + + hns_dsaf_xge_srst_by_port(dsaf_dev, mac_id, 0); +} + +/** + *hns_xgmac_get_info - get xgmac information + *@mac_drv: mac driver + *@mac_info:mac information + */ +static void hns_xgmac_get_info(void *mac_drv, struct mac_info *mac_info) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + u32 pause_time, pause_ctrl, port_mode, ctrl_val; + + ctrl_val = dsaf_read_dev(drv, XGMAC_MAC_CONTROL_REG); + mac_info->pad_and_crc_en = dsaf_get_bit(ctrl_val, XGMAC_CTL_TX_PAD_B); + mac_info->auto_neg = 0; + + pause_time = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_TIME_REG); + mac_info->tx_pause_time = pause_time; + + port_mode = dsaf_read_dev(drv, XGMAC_PORT_MODE_REG); + mac_info->port_en = dsaf_get_field(port_mode, XGMAC_PORT_MODE_TX_M, + XGMAC_PORT_MODE_TX_S) && + dsaf_get_field(port_mode, XGMAC_PORT_MODE_RX_M, + XGMAC_PORT_MODE_RX_S); + mac_info->duplex = 1; + mac_info->speed = MAC_SPEED_10000; + + pause_ctrl = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_CTRL_REG); + mac_info->rx_pause_en = dsaf_get_bit(pause_ctrl, XGMAC_PAUSE_CTL_RX_B); + mac_info->tx_pause_en = dsaf_get_bit(pause_ctrl, XGMAC_PAUSE_CTL_TX_B); +} + +/** + *hns_xgmac_get_pausefrm_cfg - get xgmac pause param + *@mac_drv: mac driver + *@rx_en:xgmac rx pause enable + *@tx_en:xgmac tx pause enable + */ +static void hns_xgmac_get_pausefrm_cfg(void *mac_drv, u32 *rx_en, u32 *tx_en) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + u32 pause_ctrl; + + pause_ctrl = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_CTRL_REG); + *rx_en = dsaf_get_bit(pause_ctrl, XGMAC_PAUSE_CTL_RX_B); + *tx_en = dsaf_get_bit(pause_ctrl, XGMAC_PAUSE_CTL_TX_B); +} + +/** + *hns_xgmac_get_link_status - get xgmac link status + *@mac_drv: mac driver + *@link_stat: xgmac link stat + */ +static void hns_xgmac_get_link_status(void *mac_drv, u32 *link_stat) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + *link_stat = dsaf_read_dev(drv, XGMAC_LINK_STATUS_REG); +} + +/** + *hns_xgmac_get_regs - dump xgmac regs + *@mac_drv: mac driver + *@cmd:ethtool cmd + *@data:data for value of regs + */ +static void hns_xgmac_get_regs(void *mac_drv, void *data) +{ + u32 i = 0; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + u32 *regs = data; + u64 qtmp; + + /* base config registers */ + regs[0] = dsaf_read_dev(drv, XGMAC_INT_STATUS_REG); + regs[1] = dsaf_read_dev(drv, XGMAC_INT_ENABLE_REG); + regs[2] = dsaf_read_dev(drv, XGMAC_INT_SET_REG); + regs[3] = dsaf_read_dev(drv, XGMAC_IERR_U_INFO_REG); + regs[4] = dsaf_read_dev(drv, XGMAC_OVF_INFO_REG); + regs[5] = dsaf_read_dev(drv, XGMAC_OVF_CNT_REG); + regs[6] = dsaf_read_dev(drv, XGMAC_PORT_MODE_REG); + regs[7] = dsaf_read_dev(drv, XGMAC_CLK_ENABLE_REG); + regs[8] = dsaf_read_dev(drv, XGMAC_RESET_REG); + regs[9] = dsaf_read_dev(drv, XGMAC_LINK_CONTROL_REG); + regs[10] = dsaf_read_dev(drv, XGMAC_LINK_STATUS_REG); + + regs[11] = dsaf_read_dev(drv, XGMAC_SPARE_REG); + regs[12] = dsaf_read_dev(drv, XGMAC_SPARE_CNT_REG); + regs[13] = dsaf_read_dev(drv, XGMAC_MAC_ENABLE_REG); + regs[14] = dsaf_read_dev(drv, XGMAC_MAC_CONTROL_REG); + regs[15] = dsaf_read_dev(drv, XGMAC_MAC_IPG_REG); + regs[16] = dsaf_read_dev(drv, XGMAC_MAC_MSG_CRC_EN_REG); + regs[17] = dsaf_read_dev(drv, XGMAC_MAC_MSG_IMG_REG); + regs[18] = dsaf_read_dev(drv, XGMAC_MAC_MSG_FC_CFG_REG); + regs[19] = dsaf_read_dev(drv, XGMAC_MAC_MSG_TC_CFG_REG); + regs[20] = dsaf_read_dev(drv, XGMAC_MAC_PAD_SIZE_REG); + regs[21] = dsaf_read_dev(drv, XGMAC_MAC_MIN_PKT_SIZE_REG); + regs[22] = dsaf_read_dev(drv, XGMAC_MAC_MAX_PKT_SIZE_REG); + regs[23] = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_CTRL_REG); + regs[24] = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_TIME_REG); + regs[25] = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_GAP_REG); + regs[26] = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_LOCAL_MAC_H_REG); + regs[27] = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_LOCAL_MAC_L_REG); + regs[28] = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_PEER_MAC_H_REG); + regs[29] = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_PEER_MAC_L_REG); + regs[30] = dsaf_read_dev(drv, XGMAC_MAC_PFC_PRI_EN_REG); + regs[31] = dsaf_read_dev(drv, XGMAC_MAC_1588_CTRL_REG); + regs[32] = dsaf_read_dev(drv, XGMAC_MAC_1588_TX_PORT_DLY_REG); + regs[33] = dsaf_read_dev(drv, XGMAC_MAC_1588_RX_PORT_DLY_REG); + regs[34] = dsaf_read_dev(drv, XGMAC_MAC_1588_ASYM_DLY_REG); + regs[35] = dsaf_read_dev(drv, XGMAC_MAC_1588_ADJUST_CFG_REG); + + regs[36] = dsaf_read_dev(drv, XGMAC_MAC_Y1731_ETH_TYPE_REG); + regs[37] = dsaf_read_dev(drv, XGMAC_MAC_MIB_CONTROL_REG); + regs[38] = dsaf_read_dev(drv, XGMAC_MAC_WAN_RATE_ADJUST_REG); + regs[39] = dsaf_read_dev(drv, XGMAC_MAC_TX_ERR_MARK_REG); + regs[40] = dsaf_read_dev(drv, XGMAC_MAC_TX_LF_RF_CONTROL_REG); + regs[41] = dsaf_read_dev(drv, XGMAC_MAC_RX_LF_RF_STATUS_REG); + regs[42] = dsaf_read_dev(drv, XGMAC_MAC_TX_RUNT_PKT_CNT_REG); + regs[43] = dsaf_read_dev(drv, XGMAC_MAC_RX_RUNT_PKT_CNT_REG); + regs[44] = dsaf_read_dev(drv, XGMAC_MAC_RX_PREAM_ERR_PKT_CNT_REG); + regs[45] = dsaf_read_dev(drv, XGMAC_MAC_TX_LF_RF_TERM_PKT_CNT_REG); + regs[46] = dsaf_read_dev(drv, XGMAC_MAC_TX_SN_MISMATCH_PKT_CNT_REG); + regs[47] = dsaf_read_dev(drv, XGMAC_MAC_RX_ERR_MSG_CNT_REG); + regs[48] = dsaf_read_dev(drv, XGMAC_MAC_RX_ERR_EFD_CNT_REG); + regs[49] = dsaf_read_dev(drv, XGMAC_MAC_ERR_INFO_REG); + regs[50] = dsaf_read_dev(drv, XGMAC_MAC_DBG_INFO_REG); + + regs[51] = dsaf_read_dev(drv, XGMAC_PCS_BASER_SYNC_THD_REG); + regs[52] = dsaf_read_dev(drv, XGMAC_PCS_STATUS1_REG); + regs[53] = dsaf_read_dev(drv, XGMAC_PCS_BASER_STATUS1_REG); + regs[54] = dsaf_read_dev(drv, XGMAC_PCS_BASER_STATUS2_REG); + regs[55] = dsaf_read_dev(drv, XGMAC_PCS_BASER_SEEDA_0_REG); + regs[56] = dsaf_read_dev(drv, XGMAC_PCS_BASER_SEEDA_1_REG); + regs[57] = dsaf_read_dev(drv, XGMAC_PCS_BASER_SEEDB_0_REG); + regs[58] = dsaf_read_dev(drv, XGMAC_PCS_BASER_SEEDB_1_REG); + regs[59] = dsaf_read_dev(drv, XGMAC_PCS_BASER_TEST_CONTROL_REG); + regs[60] = dsaf_read_dev(drv, XGMAC_PCS_BASER_TEST_ERR_CNT_REG); + regs[61] = dsaf_read_dev(drv, XGMAC_PCS_DBG_INFO_REG); + regs[62] = dsaf_read_dev(drv, XGMAC_PCS_DBG_INFO1_REG); + regs[63] = dsaf_read_dev(drv, XGMAC_PCS_DBG_INFO2_REG); + regs[64] = dsaf_read_dev(drv, XGMAC_PCS_DBG_INFO3_REG); + + regs[65] = dsaf_read_dev(drv, XGMAC_PMA_ENABLE_REG); + regs[66] = dsaf_read_dev(drv, XGMAC_PMA_CONTROL_REG); + regs[67] = dsaf_read_dev(drv, XGMAC_PMA_SIGNAL_STATUS_REG); + regs[68] = dsaf_read_dev(drv, XGMAC_PMA_DBG_INFO_REG); + regs[69] = dsaf_read_dev(drv, XGMAC_PMA_FEC_ABILITY_REG); + regs[70] = dsaf_read_dev(drv, XGMAC_PMA_FEC_CONTROL_REG); + regs[71] = dsaf_read_dev(drv, XGMAC_PMA_FEC_CORR_BLOCK_CNT__REG); + regs[72] = dsaf_read_dev(drv, XGMAC_PMA_FEC_UNCORR_BLOCK_CNT__REG); + + /* status registers */ +#define hns_xgmac_cpy_q(p, q) \ + do {\ + *(p) = (u32)(q);\ + *((p) + 1) = (u32)((q) >> 32);\ + } while (0) + + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_FRAGMENT); + hns_xgmac_cpy_q(®s[73], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_UNDERSIZE); + hns_xgmac_cpy_q(®s[75], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_UNDERMIN); + hns_xgmac_cpy_q(®s[77], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_64OCTETS); + hns_xgmac_cpy_q(®s[79], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_65TO127OCTETS); + hns_xgmac_cpy_q(®s[81], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_128TO255OCTETS); + hns_xgmac_cpy_q(®s[83], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_256TO511OCTETS); + hns_xgmac_cpy_q(®s[85], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_512TO1023OCTETS); + hns_xgmac_cpy_q(®s[87], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_1024TO1518OCTETS); + hns_xgmac_cpy_q(®s[89], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_1519TOMAXOCTETS); + hns_xgmac_cpy_q(®s[91], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_1519TOMAXOCTETSOK); + hns_xgmac_cpy_q(®s[93], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_OVERSIZE); + hns_xgmac_cpy_q(®s[95], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_JABBER); + hns_xgmac_cpy_q(®s[97], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_GOODPKTS); + hns_xgmac_cpy_q(®s[99], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_GOODOCTETS); + hns_xgmac_cpy_q(®s[101], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_TOTAL_PKTS); + hns_xgmac_cpy_q(®s[103], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_TOTALOCTETS); + hns_xgmac_cpy_q(®s[105], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_UNICASTPKTS); + hns_xgmac_cpy_q(®s[107], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_MULTICASTPKTS); + hns_xgmac_cpy_q(®s[109], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_BROADCASTPKTS); + hns_xgmac_cpy_q(®s[111], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PRI0PAUSEPKTS); + hns_xgmac_cpy_q(®s[113], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PRI1PAUSEPKTS); + hns_xgmac_cpy_q(®s[115], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PRI2PAUSEPKTS); + hns_xgmac_cpy_q(®s[117], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PRI3PAUSEPKTS); + hns_xgmac_cpy_q(®s[119], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PRI4PAUSEPKTS); + hns_xgmac_cpy_q(®s[121], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PRI5PAUSEPKTS); + hns_xgmac_cpy_q(®s[123], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PRI6PAUSEPKTS); + hns_xgmac_cpy_q(®s[125], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PRI7PAUSEPKTS); + hns_xgmac_cpy_q(®s[127], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_MACCTRLPKTS); + hns_xgmac_cpy_q(®s[129], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_1731PKTS); + hns_xgmac_cpy_q(®s[131], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_1588PKTS); + hns_xgmac_cpy_q(®s[133], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_FROMAPPGOODPKTS); + hns_xgmac_cpy_q(®s[135], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_FROMAPPBADPKTS); + hns_xgmac_cpy_q(®s[137], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_ERRALLPKTS); + hns_xgmac_cpy_q(®s[139], qtmp); + + /* RX */ + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_FRAGMENT); + hns_xgmac_cpy_q(®s[141], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTSUNDERSIZE); + hns_xgmac_cpy_q(®s[143], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_UNDERMIN); + hns_xgmac_cpy_q(®s[145], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_64OCTETS); + hns_xgmac_cpy_q(®s[147], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_65TO127OCTETS); + hns_xgmac_cpy_q(®s[149], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_128TO255OCTETS); + hns_xgmac_cpy_q(®s[151], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_256TO511OCTETS); + hns_xgmac_cpy_q(®s[153], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_512TO1023OCTETS); + hns_xgmac_cpy_q(®s[155], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_1024TO1518OCTETS); + hns_xgmac_cpy_q(®s[157], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_1519TOMAXOCTETS); + hns_xgmac_cpy_q(®s[159], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_1519TOMAXOCTETSOK); + hns_xgmac_cpy_q(®s[161], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_OVERSIZE); + hns_xgmac_cpy_q(®s[163], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_JABBER); + hns_xgmac_cpy_q(®s[165], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_GOODPKTS); + hns_xgmac_cpy_q(®s[167], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_GOODOCTETS); + hns_xgmac_cpy_q(®s[169], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_TOTAL_PKTS); + hns_xgmac_cpy_q(®s[171], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_TOTALOCTETS); + hns_xgmac_cpy_q(®s[173], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_UNICASTPKTS); + hns_xgmac_cpy_q(®s[175], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_MULTICASTPKTS); + hns_xgmac_cpy_q(®s[177], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_BROADCASTPKTS); + hns_xgmac_cpy_q(®s[179], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PRI0PAUSEPKTS); + hns_xgmac_cpy_q(®s[181], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PRI1PAUSEPKTS); + hns_xgmac_cpy_q(®s[183], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PRI2PAUSEPKTS); + hns_xgmac_cpy_q(®s[185], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PRI3PAUSEPKTS); + hns_xgmac_cpy_q(®s[187], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PRI4PAUSEPKTS); + hns_xgmac_cpy_q(®s[189], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PRI5PAUSEPKTS); + hns_xgmac_cpy_q(®s[191], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PRI6PAUSEPKTS); + hns_xgmac_cpy_q(®s[193], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PRI7PAUSEPKTS); + hns_xgmac_cpy_q(®s[195], qtmp); + + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_MACCTRLPKTS); + hns_xgmac_cpy_q(®s[197], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_SENDAPPGOODPKTS); + hns_xgmac_cpy_q(®s[199], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_SENDAPPBADPKTS); + hns_xgmac_cpy_q(®s[201], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_1731PKTS); + hns_xgmac_cpy_q(®s[203], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_SYMBOLERRPKTS); + hns_xgmac_cpy_q(®s[205], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_FCSERRPKTS); + hns_xgmac_cpy_q(®s[207], qtmp); + + /* mark end of mac regs */ + for (i = 208; i < 214; i++) + regs[i] = 0xaaaaaaaa; +} + +/** + *hns_xgmac_get_stats - get xgmac statistic + *@mac_drv: mac driver + *@data:data for value of stats regs + */ +static void hns_xgmac_get_stats(void *mac_drv, u64 *data) +{ + u32 i; + u64 *buf = data; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + struct mac_hw_stats *hw_stats = NULL; + + hw_stats = &drv->mac_cb->hw_stats; + + for (i = 0; i < ARRAY_SIZE(g_xgmac_stats_string); i++) { + buf[i] = DSAF_STATS_READ(hw_stats, + g_xgmac_stats_string[i].offset); + } +} + +/** + *hns_xgmac_get_strings - get xgmac strings name + *@stringset: type of values in data + *@data:data for value of string name + */ +static void hns_xgmac_get_strings(u32 stringset, u8 *data) +{ + char *buff = (char *)data; + u32 i; + + if (stringset != ETH_SS_STATS) + return; + + for (i = 0; i < ARRAY_SIZE(g_xgmac_stats_string); i++) { + snprintf(buff, ETH_GSTRING_LEN, g_xgmac_stats_string[i].desc); + buff = buff + ETH_GSTRING_LEN; + } +} + +/** + *hns_xgmac_get_sset_count - get xgmac string set count + *@stringset: type of values in data + *return xgmac string set count + */ +static int hns_xgmac_get_sset_count(int stringset) +{ + if (stringset == ETH_SS_STATS) + return ARRAY_SIZE(g_xgmac_stats_string); + + return 0; +} + +/** + *hns_xgmac_get_regs_count - get xgmac regs count + *return xgmac regs count + */ +static int hns_xgmac_get_regs_count(void) +{ + return ETH_XGMAC_DUMP_NUM; +} + +void *hns_xgmac_config(struct hns_mac_cb *mac_cb, struct mac_params *mac_param) +{ + struct mac_driver *mac_drv; + + mac_drv = devm_kzalloc(mac_cb->dev, sizeof(*mac_drv), GFP_KERNEL); + if (!mac_drv) + return NULL; + + mac_drv->mac_init = hns_xgmac_init; + mac_drv->mac_enable = hns_xgmac_enable; + mac_drv->mac_disable = hns_xgmac_disable; + + mac_drv->mac_id = mac_param->mac_id; + mac_drv->mac_mode = mac_param->mac_mode; + mac_drv->io_base = mac_param->vaddr; + mac_drv->dev = mac_param->dev; + mac_drv->mac_cb = mac_cb; + + mac_drv->set_mac_addr = hns_xgmac_set_pausefrm_mac_addr; + mac_drv->set_an_mode = NULL; + mac_drv->config_loopback = NULL; + mac_drv->config_pad_and_crc = hns_xgmac_config_pad_and_crc; + mac_drv->config_half_duplex = NULL; + mac_drv->set_rx_ignore_pause_frames = + hns_xgmac_set_rx_ignore_pause_frames; + mac_drv->mac_get_id = hns_xgmac_get_id; + mac_drv->mac_free = hns_xgmac_free; + mac_drv->adjust_link = NULL; + mac_drv->set_tx_auto_pause_frames = hns_xgmac_set_tx_auto_pause_frames; + mac_drv->config_max_frame_length = hns_xgmac_config_max_frame_length; + mac_drv->mac_pausefrm_cfg = hns_xgmac_pausefrm_cfg; + mac_drv->autoneg_stat = NULL; + mac_drv->get_info = hns_xgmac_get_info; + mac_drv->get_pause_enable = hns_xgmac_get_pausefrm_cfg; + mac_drv->get_link_status = hns_xgmac_get_link_status; + mac_drv->get_regs = hns_xgmac_get_regs; + mac_drv->get_ethtool_stats = hns_xgmac_get_stats; + mac_drv->get_sset_count = hns_xgmac_get_sset_count; + mac_drv->get_regs_count = hns_xgmac_get_regs_count; + mac_drv->get_strings = hns_xgmac_get_strings; + mac_drv->update_stats = hns_xgmac_update_stats; + + return (void *)mac_drv; +} diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.h new file mode 100644 index 000000000000..139f7297c7b4 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.h @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * 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 _HNS_XGMAC_H +#define _HNS_XGMAC_H + +#define ETH_XGMAC_DUMP_NUM (214) + +#endif diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c new file mode 100644 index 000000000000..0713ced120b6 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c @@ -0,0 +1,1646 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * 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/clk.h> +#include <linux/cpumask.h> +#include <linux/etherdevice.h> +#include <linux/if_vlan.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/ip.h> +#include <linux/ipv6.h> +#include <linux/module.h> +#include <linux/phy.h> +#include <linux/platform_device.h> +#include <linux/skbuff.h> + +#include "hnae.h" +#include "hns_enet.h" + +#define NIC_MAX_Q_PER_VF 16 +#define HNS_NIC_TX_TIMEOUT (5 * HZ) + +#define SERVICE_TIMER_HZ (1 * HZ) + +#define NIC_TX_CLEAN_MAX_NUM 256 +#define NIC_RX_CLEAN_MAX_NUM 64 + +#define RCB_ERR_PRINT_CYCLE 1000 + +#define RCB_IRQ_NOT_INITED 0 +#define RCB_IRQ_INITED 1 + +static void fill_desc(struct hnae_ring *ring, void *priv, + int size, dma_addr_t dma, int frag_end, + int buf_num, enum hns_desc_type type) +{ + struct hnae_desc *desc = &ring->desc[ring->next_to_use]; + struct hnae_desc_cb *desc_cb = &ring->desc_cb[ring->next_to_use]; + struct sk_buff *skb; + __be16 protocol; + u32 ip_offset; + u32 asid_bufnum_pid = 0; + u32 flag_ipoffset = 0; + + desc_cb->priv = priv; + desc_cb->length = size; + desc_cb->dma = dma; + desc_cb->type = type; + + desc->addr = cpu_to_le64(dma); + desc->tx.send_size = cpu_to_le16((u16)size); + + /*config bd buffer end */ + flag_ipoffset |= 1 << HNS_TXD_VLD_B; + + asid_bufnum_pid |= buf_num << HNS_TXD_BUFNUM_S; + + if (type == DESC_TYPE_SKB) { + skb = (struct sk_buff *)priv; + + if (skb->ip_summed == CHECKSUM_PARTIAL) { + protocol = skb->protocol; + ip_offset = ETH_HLEN; + + /*if it is a SW VLAN check the next protocol*/ + if (protocol == htons(ETH_P_8021Q)) { + ip_offset += VLAN_HLEN; + protocol = vlan_get_protocol(skb); + skb->protocol = protocol; + } + + if (skb->protocol == htons(ETH_P_IP)) { + flag_ipoffset |= 1 << HNS_TXD_L3CS_B; + /* check for tcp/udp header */ + flag_ipoffset |= 1 << HNS_TXD_L4CS_B; + + } else if (skb->protocol == htons(ETH_P_IPV6)) { + /* ipv6 has not l3 cs, check for L4 header */ + flag_ipoffset |= 1 << HNS_TXD_L4CS_B; + } + + flag_ipoffset |= ip_offset << HNS_TXD_IPOFFSET_S; + } + } + + flag_ipoffset |= frag_end << HNS_TXD_FE_B; + + desc->tx.asid_bufnum_pid = cpu_to_le16(asid_bufnum_pid); + desc->tx.flag_ipoffset = cpu_to_le32(flag_ipoffset); + + ring_ptr_move_fw(ring, next_to_use); +} + +static void unfill_desc(struct hnae_ring *ring) +{ + ring_ptr_move_bw(ring, next_to_use); +} + +int hns_nic_net_xmit_hw(struct net_device *ndev, + struct sk_buff *skb, + struct hns_nic_ring_data *ring_data) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct device *dev = priv->dev; + struct hnae_ring *ring = ring_data->ring; + struct netdev_queue *dev_queue; + struct skb_frag_struct *frag; + int buf_num; + dma_addr_t dma; + int size, next_to_use; + int i, j; + struct sk_buff *new_skb; + + assert(ring->max_desc_num_per_pkt <= ring->desc_num); + + /* no. of segments (plus a header) */ + buf_num = skb_shinfo(skb)->nr_frags + 1; + + if (unlikely(buf_num > ring->max_desc_num_per_pkt)) { + if (ring_space(ring) < 1) { + ring->stats.tx_busy++; + goto out_net_tx_busy; + } + + new_skb = skb_copy(skb, GFP_ATOMIC); + if (!new_skb) { + ring->stats.sw_err_cnt++; + netdev_err(ndev, "no memory to xmit!\n"); + goto out_err_tx_ok; + } + + dev_kfree_skb_any(skb); + skb = new_skb; + buf_num = 1; + assert(skb_shinfo(skb)->nr_frags == 1); + } else if (buf_num > ring_space(ring)) { + ring->stats.tx_busy++; + goto out_net_tx_busy; + } + next_to_use = ring->next_to_use; + + /* fill the first part */ + size = skb_headlen(skb); + dma = dma_map_single(dev, skb->data, size, DMA_TO_DEVICE); + if (dma_mapping_error(dev, dma)) { + netdev_err(ndev, "TX head DMA map failed\n"); + ring->stats.sw_err_cnt++; + goto out_err_tx_ok; + } + fill_desc(ring, skb, size, dma, buf_num == 1 ? 1 : 0, buf_num, + DESC_TYPE_SKB); + + /* fill the fragments */ + for (i = 1; i < buf_num; i++) { + frag = &skb_shinfo(skb)->frags[i - 1]; + size = skb_frag_size(frag); + dma = skb_frag_dma_map(dev, frag, 0, size, DMA_TO_DEVICE); + if (dma_mapping_error(dev, dma)) { + netdev_err(ndev, "TX frag(%d) DMA map failed\n", i); + ring->stats.sw_err_cnt++; + goto out_map_frag_fail; + } + fill_desc(ring, skb_frag_page(frag), size, dma, + buf_num - 1 == i ? 1 : 0, buf_num, DESC_TYPE_PAGE); + } + + /*complete translate all packets*/ + dev_queue = netdev_get_tx_queue(ndev, skb->queue_mapping); + netdev_tx_sent_queue(dev_queue, skb->len); + + wmb(); /* commit all data before submit */ + assert(skb->queue_mapping < priv->ae_handle->q_num); + hnae_queue_xmit(priv->ae_handle->qs[skb->queue_mapping], buf_num); + ring->stats.tx_pkts++; + ring->stats.tx_bytes += skb->len; + + return NETDEV_TX_OK; + +out_map_frag_fail: + + for (j = i - 1; j > 0; j--) { + unfill_desc(ring); + next_to_use = ring->next_to_use; + dma_unmap_page(dev, ring->desc_cb[next_to_use].dma, + ring->desc_cb[next_to_use].length, + DMA_TO_DEVICE); + } + + unfill_desc(ring); + next_to_use = ring->next_to_use; + dma_unmap_single(dev, ring->desc_cb[next_to_use].dma, + ring->desc_cb[next_to_use].length, DMA_TO_DEVICE); + +out_err_tx_ok: + + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + +out_net_tx_busy: + + netif_stop_subqueue(ndev, skb->queue_mapping); + + /* Herbert's original patch had: + * smp_mb__after_netif_stop_queue(); + * but since that doesn't exist yet, just open code it. + */ + smp_mb(); + return NETDEV_TX_BUSY; +} + +/** + * hns_nic_get_headlen - determine size of header for RSC/LRO/GRO/FCOE + * @data: pointer to the start of the headers + * @max: total length of section to find headers in + * + * This function is meant to determine the length of headers that will + * be recognized by hardware for LRO, GRO, and RSC offloads. The main + * motivation of doing this is to only perform one pull for IPv4 TCP + * packets so that we can do basic things like calculating the gso_size + * based on the average data per packet. + **/ +static unsigned int hns_nic_get_headlen(unsigned char *data, u32 flag, + unsigned int max_size) +{ + unsigned char *network; + u8 hlen; + + /* this should never happen, but better safe than sorry */ + if (max_size < ETH_HLEN) + return max_size; + + /* initialize network frame pointer */ + network = data; + + /* set first protocol and move network header forward */ + network += ETH_HLEN; + + /* handle any vlan tag if present */ + if (hnae_get_field(flag, HNS_RXD_VLAN_M, HNS_RXD_VLAN_S) + == HNS_RX_FLAG_VLAN_PRESENT) { + if ((typeof(max_size))(network - data) > (max_size - VLAN_HLEN)) + return max_size; + + network += VLAN_HLEN; + } + + /* handle L3 protocols */ + if (hnae_get_field(flag, HNS_RXD_L3ID_M, HNS_RXD_L3ID_S) + == HNS_RX_FLAG_L3ID_IPV4) { + if ((typeof(max_size))(network - data) > + (max_size - sizeof(struct iphdr))) + return max_size; + + /* access ihl as a u8 to avoid unaligned access on ia64 */ + hlen = (network[0] & 0x0F) << 2; + + /* verify hlen meets minimum size requirements */ + if (hlen < sizeof(struct iphdr)) + return network - data; + + /* record next protocol if header is present */ + } else if (hnae_get_field(flag, HNS_RXD_L3ID_M, HNS_RXD_L3ID_S) + == HNS_RX_FLAG_L3ID_IPV6) { + if ((typeof(max_size))(network - data) > + (max_size - sizeof(struct ipv6hdr))) + return max_size; + + /* record next protocol */ + hlen = sizeof(struct ipv6hdr); + } else { + return network - data; + } + + /* relocate pointer to start of L4 header */ + network += hlen; + + /* finally sort out TCP/UDP */ + if (hnae_get_field(flag, HNS_RXD_L4ID_M, HNS_RXD_L4ID_S) + == HNS_RX_FLAG_L4ID_TCP) { + if ((typeof(max_size))(network - data) > + (max_size - sizeof(struct tcphdr))) + return max_size; + + /* access doff as a u8 to avoid unaligned access on ia64 */ + hlen = (network[12] & 0xF0) >> 2; + + /* verify hlen meets minimum size requirements */ + if (hlen < sizeof(struct tcphdr)) + return network - data; + + network += hlen; + } else if (hnae_get_field(flag, HNS_RXD_L4ID_M, HNS_RXD_L4ID_S) + == HNS_RX_FLAG_L4ID_UDP) { + if ((typeof(max_size))(network - data) > + (max_size - sizeof(struct udphdr))) + return max_size; + + network += sizeof(struct udphdr); + } + + /* If everything has gone correctly network should be the + * data section of the packet and will be the end of the header. + * If not then it probably represents the end of the last recognized + * header. + */ + if ((typeof(max_size))(network - data) < max_size) + return network - data; + else + return max_size; +} + +static void +hns_nic_reuse_page(struct hnae_desc_cb *desc_cb, int tsize, int last_offset) +{ + /* avoid re-using remote pages,flag default unreuse */ + if (likely(page_to_nid(desc_cb->priv) == numa_node_id())) { + /* move offset up to the next cache line */ + desc_cb->page_offset += tsize; + + if (desc_cb->page_offset <= last_offset) { + desc_cb->reuse_flag = 1; + /* bump ref count on page before it is given*/ + get_page(desc_cb->priv); + } + } +} + +static int hns_nic_poll_rx_skb(struct hns_nic_ring_data *ring_data, + struct sk_buff **out_skb, int *out_bnum) +{ + struct hnae_ring *ring = ring_data->ring; + struct net_device *ndev = ring_data->napi.dev; + struct sk_buff *skb; + struct hnae_desc *desc; + struct hnae_desc_cb *desc_cb; + unsigned char *va; + int bnum, length, size, i, truesize, last_offset; + int pull_len; + u32 bnum_flag; + + last_offset = hnae_page_size(ring) - hnae_buf_size(ring); + desc = &ring->desc[ring->next_to_clean]; + desc_cb = &ring->desc_cb[ring->next_to_clean]; + length = le16_to_cpu(desc->rx.pkt_len); + bnum_flag = le32_to_cpu(desc->rx.ipoff_bnum_pid_flag); + bnum = hnae_get_field(bnum_flag, HNS_RXD_BUFNUM_M, HNS_RXD_BUFNUM_S); + *out_bnum = bnum; + va = (unsigned char *)desc_cb->buf + desc_cb->page_offset; + + skb = *out_skb = napi_alloc_skb(&ring_data->napi, HNS_RX_HEAD_SIZE); + if (unlikely(!skb)) { + netdev_err(ndev, "alloc rx skb fail\n"); + ring->stats.sw_err_cnt++; + return -ENOMEM; + } + + if (length <= HNS_RX_HEAD_SIZE) { + memcpy(__skb_put(skb, length), va, ALIGN(length, sizeof(long))); + + /* we can reuse buffer as-is, just make sure it is local */ + if (likely(page_to_nid(desc_cb->priv) == numa_node_id())) + desc_cb->reuse_flag = 1; + else /* this page cannot be reused so discard it */ + put_page(desc_cb->priv); + + ring_ptr_move_fw(ring, next_to_clean); + + if (unlikely(bnum != 1)) { /* check err*/ + *out_bnum = 1; + goto out_bnum_err; + } + } else { + ring->stats.seg_pkt_cnt++; + + pull_len = hns_nic_get_headlen(va, bnum_flag, HNS_RX_HEAD_SIZE); + memcpy(__skb_put(skb, pull_len), va, + ALIGN(pull_len, sizeof(long))); + + size = le16_to_cpu(desc->rx.size); + truesize = ALIGN(size, L1_CACHE_BYTES); + skb_add_rx_frag(skb, 0, desc_cb->priv, + desc_cb->page_offset + pull_len, + size - pull_len, truesize - pull_len); + + hns_nic_reuse_page(desc_cb, truesize, last_offset); + ring_ptr_move_fw(ring, next_to_clean); + + if (unlikely(bnum >= (int)MAX_SKB_FRAGS)) { /* check err*/ + *out_bnum = 1; + goto out_bnum_err; + } + for (i = 1; i < bnum; i++) { + desc = &ring->desc[ring->next_to_clean]; + desc_cb = &ring->desc_cb[ring->next_to_clean]; + size = le16_to_cpu(desc->rx.size); + truesize = ALIGN(size, L1_CACHE_BYTES); + skb_add_rx_frag(skb, i, desc_cb->priv, + desc_cb->page_offset, + size, truesize); + + hns_nic_reuse_page(desc_cb, truesize, last_offset); + ring_ptr_move_fw(ring, next_to_clean); + } + } + + /* check except process, free skb and jump the desc */ + if (unlikely((!bnum) || (bnum > ring->max_desc_num_per_pkt))) { +out_bnum_err: + *out_bnum = *out_bnum ? *out_bnum : 1; /* ntc moved,cannot 0*/ + netdev_err(ndev, "invalid bnum(%d,%d,%d,%d),%016llx,%016llx\n", + bnum, ring->max_desc_num_per_pkt, + length, (int)MAX_SKB_FRAGS, + ((u64 *)desc)[0], ((u64 *)desc)[1]); + ring->stats.err_bd_num++; + dev_kfree_skb_any(skb); + return -EDOM; + } + + bnum_flag = le32_to_cpu(desc->rx.ipoff_bnum_pid_flag); + + if (unlikely(!hnae_get_bit(bnum_flag, HNS_RXD_VLD_B))) { + netdev_err(ndev, "no valid bd,%016llx,%016llx\n", + ((u64 *)desc)[0], ((u64 *)desc)[1]); + ring->stats.non_vld_descs++; + dev_kfree_skb_any(skb); + return -EINVAL; + } + + if (unlikely((!desc->rx.pkt_len) || + hnae_get_bit(bnum_flag, HNS_RXD_DROP_B))) { + if (!(ring->stats.err_pkt_len % RCB_ERR_PRINT_CYCLE)) + netdev_dbg(ndev, + "pkt_len(%u),drop(%u),%#llx,%#llx\n", + le16_to_cpu(desc->rx.pkt_len), + hnae_get_bit(bnum_flag, HNS_RXD_DROP_B), + ((u64 *)desc)[0], ((u64 *)desc)[1]); + ring->stats.err_pkt_len++; + dev_kfree_skb_any(skb); + return -EFAULT; + } + + if (unlikely(hnae_get_bit(bnum_flag, HNS_RXD_L2E_B))) { + if (!(ring->stats.l2_err % RCB_ERR_PRINT_CYCLE)) + netdev_dbg(ndev, "L2 check err,%#llx,%#llx\n", + ((u64 *)desc)[0], ((u64 *)desc)[1]); + ring->stats.l2_err++; + dev_kfree_skb_any(skb); + return -EFAULT; + } + + ring->stats.rx_pkts++; + ring->stats.rx_bytes += skb->len; + + if (unlikely(hnae_get_bit(bnum_flag, HNS_RXD_L3E_B) || + hnae_get_bit(bnum_flag, HNS_RXD_L4E_B))) { + if (!(ring->stats.l3l4_csum_err % RCB_ERR_PRINT_CYCLE)) + netdev_dbg(ndev, + "check err(%#x),%#llx,%#llx\n", + hnae_get_bit(bnum_flag, HNS_RXD_L3E_B) | + hnae_get_bit(bnum_flag, HNS_RXD_L4E_B), + ((u64 *)desc)[0], ((u64 *)desc)[1]); + ring->stats.l3l4_csum_err++; + return 0; + } + + skb->ip_summed = CHECKSUM_UNNECESSARY; + + return 0; +} + +static void +hns_nic_alloc_rx_buffers(struct hns_nic_ring_data *ring_data, int cleand_count) +{ + int i, ret; + struct hnae_desc_cb res_cbs; + struct hnae_desc_cb *desc_cb; + struct hnae_ring *ring = ring_data->ring; + struct net_device *ndev = ring_data->napi.dev; + + for (i = 0; i < cleand_count; i++) { + desc_cb = &ring->desc_cb[ring->next_to_use]; + if (desc_cb->reuse_flag) { + ring->stats.reuse_pg_cnt++; + hnae_reuse_buffer(ring, ring->next_to_use); + } else { + ret = hnae_reserve_buffer_map(ring, &res_cbs); + if (ret) { + ring->stats.sw_err_cnt++; + netdev_err(ndev, "hnae reserve buffer map failed.\n"); + break; + } + hnae_replace_buffer(ring, ring->next_to_use, &res_cbs); + } + + ring_ptr_move_fw(ring, next_to_use); + } + + wmb(); /* make all data has been write before submit */ + writel_relaxed(i, ring->io_base + RCB_REG_HEAD); +} + +/* return error number for error or number of desc left to take + */ +static void hns_nic_rx_up_pro(struct hns_nic_ring_data *ring_data, + struct sk_buff *skb) +{ + struct net_device *ndev = ring_data->napi.dev; + + skb->protocol = eth_type_trans(skb, ndev); + (void)napi_gro_receive(&ring_data->napi, skb); + ndev->last_rx = jiffies; +} + +static int hns_nic_rx_poll_one(struct hns_nic_ring_data *ring_data, + int budget, void *v) +{ + struct hnae_ring *ring = ring_data->ring; + struct sk_buff *skb; + int num, bnum, ex_num; +#define RCB_NOF_ALLOC_RX_BUFF_ONCE 16 + int recv_pkts, recv_bds, clean_count, err; + + num = readl_relaxed(ring->io_base + RCB_REG_FBDNUM); + rmb(); /* make sure num taken effect before the other data is touched */ + + recv_pkts = 0, recv_bds = 0, clean_count = 0; +recv: + while (recv_pkts < budget && recv_bds < num) { + /* reuse or realloc buffers*/ + if (clean_count >= RCB_NOF_ALLOC_RX_BUFF_ONCE) { + hns_nic_alloc_rx_buffers(ring_data, clean_count); + clean_count = 0; + } + + /* poll one pkg*/ + err = hns_nic_poll_rx_skb(ring_data, &skb, &bnum); + if (unlikely(!skb)) /* this fault cannot be repaired */ + break; + + recv_bds += bnum; + clean_count += bnum; + if (unlikely(err)) { /* do jump the err */ + recv_pkts++; + continue; + } + + /* do update ip stack process*/ + ((void (*)(struct hns_nic_ring_data *, struct sk_buff *))v)( + ring_data, skb); + recv_pkts++; + } + + /* make all data has been write before submit */ + if (clean_count > 0) { + hns_nic_alloc_rx_buffers(ring_data, clean_count); + clean_count = 0; + } + + if (recv_pkts < budget) { + ex_num = readl_relaxed(ring->io_base + RCB_REG_FBDNUM); + rmb(); /*complete read rx ring bd number*/ + if (ex_num > 0) { + num += ex_num; + goto recv; + } + } + + return recv_pkts; +} + +static void hns_nic_rx_fini_pro(struct hns_nic_ring_data *ring_data) +{ + struct hnae_ring *ring = ring_data->ring; + int num = 0; + + /* for hardware bug fixed */ + num = readl_relaxed(ring->io_base + RCB_REG_FBDNUM); + + if (num > 0) { + ring_data->ring->q->handle->dev->ops->toggle_ring_irq( + ring_data->ring, 1); + + napi_schedule(&ring_data->napi); + } +} + +static inline void hns_nic_reclaim_one_desc(struct hnae_ring *ring, + int *bytes, int *pkts) +{ + struct hnae_desc_cb *desc_cb = &ring->desc_cb[ring->next_to_clean]; + + (*pkts) += (desc_cb->type == DESC_TYPE_SKB); + (*bytes) += desc_cb->length; + /* desc_cb will be cleaned, after hnae_free_buffer_detach*/ + hnae_free_buffer_detach(ring, ring->next_to_clean); + + ring_ptr_move_fw(ring, next_to_clean); +} + +static int is_valid_clean_head(struct hnae_ring *ring, int h) +{ + int u = ring->next_to_use; + int c = ring->next_to_clean; + + if (unlikely(h > ring->desc_num)) + return 0; + + assert(u > 0 && u < ring->desc_num); + assert(c > 0 && c < ring->desc_num); + assert(u != c && h != c); /* must be checked before call this func */ + + return u > c ? (h > c && h <= u) : (h > c || h <= u); +} + +/* netif_tx_lock will turn down the performance, set only when necessary */ +#ifdef CONFIG_NET_POLL_CONTROLLER +#define NETIF_TX_LOCK(ndev) netif_tx_lock(ndev) +#define NETIF_TX_UNLOCK(ndev) netif_tx_unlock(ndev) +#else +#define NETIF_TX_LOCK(ndev) +#define NETIF_TX_UNLOCK(ndev) +#endif +/* reclaim all desc in one budget + * return error or number of desc left + */ +static int hns_nic_tx_poll_one(struct hns_nic_ring_data *ring_data, + int budget, void *v) +{ + struct hnae_ring *ring = ring_data->ring; + struct net_device *ndev = ring_data->napi.dev; + struct netdev_queue *dev_queue; + struct hns_nic_priv *priv = netdev_priv(ndev); + int head; + int bytes, pkts; + + NETIF_TX_LOCK(ndev); + + head = readl_relaxed(ring->io_base + RCB_REG_HEAD); + rmb(); /* make sure head is ready before touch any data */ + + if (is_ring_empty(ring) || head == ring->next_to_clean) { + NETIF_TX_UNLOCK(ndev); + return 0; /* no data to poll */ + } + + if (!is_valid_clean_head(ring, head)) { + netdev_err(ndev, "wrong head (%d, %d-%d)\n", head, + ring->next_to_use, ring->next_to_clean); + ring->stats.io_err_cnt++; + NETIF_TX_UNLOCK(ndev); + return -EIO; + } + + bytes = 0; + pkts = 0; + while (head != ring->next_to_clean) + hns_nic_reclaim_one_desc(ring, &bytes, &pkts); + + NETIF_TX_UNLOCK(ndev); + + dev_queue = netdev_get_tx_queue(ndev, ring_data->queue_index); + netdev_tx_completed_queue(dev_queue, pkts, bytes); + + if (unlikely(pkts && netif_carrier_ok(ndev) && + (ring_space(ring) >= ring->max_desc_num_per_pkt * 2))) { + /* Make sure that anybody stopping the queue after this + * sees the new next_to_clean. + */ + smp_mb(); + if (netif_tx_queue_stopped(dev_queue) && + !test_bit(NIC_STATE_DOWN, &priv->state)) { + netif_tx_wake_queue(dev_queue); + ring->stats.restart_queue++; + } + } + return 0; +} + +static void hns_nic_tx_fini_pro(struct hns_nic_ring_data *ring_data) +{ + struct hnae_ring *ring = ring_data->ring; + int head = ring->next_to_clean; + + /* for hardware bug fixed */ + head = readl_relaxed(ring->io_base + RCB_REG_HEAD); + + if (head != ring->next_to_clean) { + ring_data->ring->q->handle->dev->ops->toggle_ring_irq( + ring_data->ring, 1); + + napi_schedule(&ring_data->napi); + } +} + +static void hns_nic_tx_clr_all_bufs(struct hns_nic_ring_data *ring_data) +{ + struct hnae_ring *ring = ring_data->ring; + struct net_device *ndev = ring_data->napi.dev; + struct netdev_queue *dev_queue; + int head; + int bytes, pkts; + + NETIF_TX_LOCK(ndev); + + head = ring->next_to_use; /* ntu :soft setted ring position*/ + bytes = 0; + pkts = 0; + while (head != ring->next_to_clean) + hns_nic_reclaim_one_desc(ring, &bytes, &pkts); + + NETIF_TX_UNLOCK(ndev); + + dev_queue = netdev_get_tx_queue(ndev, ring_data->queue_index); + netdev_tx_reset_queue(dev_queue); +} + +static int hns_nic_common_poll(struct napi_struct *napi, int budget) +{ + struct hns_nic_ring_data *ring_data = + container_of(napi, struct hns_nic_ring_data, napi); + int clean_complete = ring_data->poll_one( + ring_data, budget, ring_data->ex_process); + + if (clean_complete >= 0 && clean_complete < budget) { + napi_complete(napi); + ring_data->ring->q->handle->dev->ops->toggle_ring_irq( + ring_data->ring, 0); + + ring_data->fini_process(ring_data); + } + + return clean_complete; +} + +static irqreturn_t hns_irq_handle(int irq, void *dev) +{ + struct hns_nic_ring_data *ring_data = (struct hns_nic_ring_data *)dev; + + ring_data->ring->q->handle->dev->ops->toggle_ring_irq( + ring_data->ring, 1); + napi_schedule(&ring_data->napi); + + return IRQ_HANDLED; +} + +/** + *hns_nic_adjust_link - adjust net work mode by the phy stat or new param + *@ndev: net device + */ +static void hns_nic_adjust_link(struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h = priv->ae_handle; + + h->dev->ops->adjust_link(h, ndev->phydev->speed, ndev->phydev->duplex); +} + +/** + *hns_nic_init_phy - init phy + *@ndev: net device + *@h: ae handle + * Return 0 on success, negative on failure + */ +int hns_nic_init_phy(struct net_device *ndev, struct hnae_handle *h) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct phy_device *phy_dev = NULL; + + if (!h->phy_node) + return 0; + + if (h->phy_if != PHY_INTERFACE_MODE_XGMII) + phy_dev = of_phy_connect(ndev, h->phy_node, + hns_nic_adjust_link, 0, h->phy_if); + else + phy_dev = of_phy_attach(ndev, h->phy_node, 0, h->phy_if); + + if (unlikely(!phy_dev) || IS_ERR(phy_dev)) + return !phy_dev ? -ENODEV : PTR_ERR(phy_dev); + + phy_dev->supported &= h->if_support; + phy_dev->advertising = phy_dev->supported; + + if (h->phy_if == PHY_INTERFACE_MODE_XGMII) + phy_dev->autoneg = false; + + priv->phy = phy_dev; + + return 0; +} + +static int hns_nic_ring_open(struct net_device *netdev, int idx) +{ + struct hns_nic_priv *priv = netdev_priv(netdev); + struct hnae_handle *h = priv->ae_handle; + + napi_enable(&priv->ring_data[idx].napi); + + enable_irq(priv->ring_data[idx].ring->irq); + h->dev->ops->toggle_ring_irq(priv->ring_data[idx].ring, 0); + + return 0; +} + +static int hns_nic_net_set_mac_address(struct net_device *ndev, void *p) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h = priv->ae_handle; + struct sockaddr *mac_addr = p; + int ret; + + if (!mac_addr || !is_valid_ether_addr((const u8 *)mac_addr->sa_data)) + return -EADDRNOTAVAIL; + + ret = h->dev->ops->set_mac_addr(h, mac_addr->sa_data); + if (ret) { + netdev_err(ndev, "set_mac_address fail, ret=%d!\n", ret); + return ret; + } + + memcpy(ndev->dev_addr, mac_addr->sa_data, ndev->addr_len); + + return 0; +} + +void hns_nic_update_stats(struct net_device *netdev) +{ + struct hns_nic_priv *priv = netdev_priv(netdev); + struct hnae_handle *h = priv->ae_handle; + + h->dev->ops->update_stats(h, &netdev->stats); +} + +/* set mac addr if it is configed. or leave it to the AE driver */ +static void hns_init_mac_addr(struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct device_node *node = priv->dev->of_node; + const void *mac_addr_temp; + + mac_addr_temp = of_get_mac_address(node); + if (mac_addr_temp && is_valid_ether_addr(mac_addr_temp)) { + memcpy(ndev->dev_addr, mac_addr_temp, ndev->addr_len); + } else { + eth_hw_addr_random(ndev); + dev_warn(priv->dev, "No valid mac, use random mac %pM", + ndev->dev_addr); + } +} + +static void hns_nic_ring_close(struct net_device *netdev, int idx) +{ + struct hns_nic_priv *priv = netdev_priv(netdev); + struct hnae_handle *h = priv->ae_handle; + + h->dev->ops->toggle_ring_irq(priv->ring_data[idx].ring, 1); + disable_irq(priv->ring_data[idx].ring->irq); + + napi_disable(&priv->ring_data[idx].napi); +} + +static int hns_nic_init_irq(struct hns_nic_priv *priv) +{ + struct hnae_handle *h = priv->ae_handle; + struct hns_nic_ring_data *rd; + int i; + int ret; + int cpu; + cpumask_t mask; + + for (i = 0; i < h->q_num * 2; i++) { + rd = &priv->ring_data[i]; + + if (rd->ring->irq_init_flag == RCB_IRQ_INITED) + break; + + snprintf(rd->ring->ring_name, RCB_RING_NAME_LEN, + "%s-%s%d", priv->netdev->name, + (i < h->q_num ? "tx" : "rx"), rd->queue_index); + + rd->ring->ring_name[RCB_RING_NAME_LEN - 1] = '\0'; + + ret = request_irq(rd->ring->irq, + hns_irq_handle, 0, rd->ring->ring_name, rd); + if (ret) { + netdev_err(priv->netdev, "request irq(%d) fail\n", + rd->ring->irq); + return ret; + } + disable_irq(rd->ring->irq); + rd->ring->irq_init_flag = RCB_IRQ_INITED; + + /*set cpu affinity*/ + if (cpu_online(rd->queue_index)) { + cpumask_clear(&mask); + cpu = rd->queue_index; + cpumask_set_cpu(cpu, &mask); + irq_set_affinity_hint(rd->ring->irq, &mask); + } + } + + return 0; +} + +static int hns_nic_net_up(struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h = priv->ae_handle; + int i, j, k; + int ret; + + ret = hns_nic_init_irq(priv); + if (ret != 0) { + netdev_err(ndev, "hns init irq failed! ret=%d\n", ret); + return ret; + } + + for (i = 0; i < h->q_num * 2; i++) { + ret = hns_nic_ring_open(ndev, i); + if (ret) + goto out_has_some_queues; + } + + for (k = 0; k < h->q_num; k++) + h->dev->ops->toggle_queue_status(h->qs[k], 1); + + ret = h->dev->ops->set_mac_addr(h, ndev->dev_addr); + if (ret) + goto out_set_mac_addr_err; + + ret = h->dev->ops->start ? h->dev->ops->start(h) : 0; + if (ret) + goto out_start_err; + + if (priv->phy) + phy_start(priv->phy); + + clear_bit(NIC_STATE_DOWN, &priv->state); + (void)mod_timer(&priv->service_timer, jiffies + SERVICE_TIMER_HZ); + + return 0; + +out_start_err: + netif_stop_queue(ndev); +out_set_mac_addr_err: + for (k = 0; k < h->q_num; k++) + h->dev->ops->toggle_queue_status(h->qs[k], 0); +out_has_some_queues: + for (j = i - 1; j >= 0; j--) + hns_nic_ring_close(ndev, j); + + set_bit(NIC_STATE_DOWN, &priv->state); + + return ret; +} + +static void hns_nic_net_down(struct net_device *ndev) +{ + int i; + struct hnae_ae_ops *ops; + struct hns_nic_priv *priv = netdev_priv(ndev); + + if (test_and_set_bit(NIC_STATE_DOWN, &priv->state)) + return; + + (void)del_timer_sync(&priv->service_timer); + netif_tx_stop_all_queues(ndev); + netif_carrier_off(ndev); + netif_tx_disable(ndev); + priv->link = 0; + + if (priv->phy) + phy_stop(priv->phy); + + ops = priv->ae_handle->dev->ops; + + if (ops->stop) + ops->stop(priv->ae_handle); + + netif_tx_stop_all_queues(ndev); + + for (i = priv->ae_handle->q_num - 1; i >= 0; i--) { + hns_nic_ring_close(ndev, i); + hns_nic_ring_close(ndev, i + priv->ae_handle->q_num); + + /* clean tx buffers*/ + hns_nic_tx_clr_all_bufs(priv->ring_data + i); + } +} + +void hns_nic_net_reset(struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *handle = priv->ae_handle; + + while (test_and_set_bit(NIC_STATE_RESETTING, &priv->state)) + usleep_range(1000, 2000); + + (void)hnae_reinit_handle(handle); + + clear_bit(NIC_STATE_RESETTING, &priv->state); +} + +void hns_nic_net_reinit(struct net_device *netdev) +{ + struct hns_nic_priv *priv = netdev_priv(netdev); + + priv->netdev->trans_start = jiffies; + while (test_and_set_bit(NIC_STATE_REINITING, &priv->state)) + usleep_range(1000, 2000); + + hns_nic_net_down(netdev); + hns_nic_net_reset(netdev); + (void)hns_nic_net_up(netdev); + clear_bit(NIC_STATE_REINITING, &priv->state); +} + +static int hns_nic_net_open(struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h = priv->ae_handle; + int ret; + + if (test_bit(NIC_STATE_TESTING, &priv->state)) + return -EBUSY; + + priv->link = 0; + netif_carrier_off(ndev); + + ret = netif_set_real_num_tx_queues(ndev, h->q_num); + if (ret < 0) { + netdev_err(ndev, "netif_set_real_num_tx_queues fail, ret=%d!\n", + ret); + return ret; + } + + ret = netif_set_real_num_rx_queues(ndev, h->q_num); + if (ret < 0) { + netdev_err(ndev, + "netif_set_real_num_rx_queues fail, ret=%d!\n", ret); + return ret; + } + + ret = hns_nic_net_up(ndev); + if (ret) { + netdev_err(ndev, + "hns net up fail, ret=%d!\n", ret); + return ret; + } + + return 0; +} + +static int hns_nic_net_stop(struct net_device *ndev) +{ + hns_nic_net_down(ndev); + + return 0; +} + +static void hns_tx_timeout_reset(struct hns_nic_priv *priv); +static void hns_nic_net_timeout(struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + + hns_tx_timeout_reset(priv); +} + +static int hns_nic_do_ioctl(struct net_device *netdev, struct ifreq *ifr, + int cmd) +{ + struct hns_nic_priv *priv = netdev_priv(netdev); + struct phy_device *phy_dev = priv->phy; + + if (!netif_running(netdev)) + return -EINVAL; + + if (!phy_dev) + return -ENOTSUPP; + + return phy_mii_ioctl(phy_dev, ifr, cmd); +} + +/* use only for netconsole to poll with the device without interrupt */ +#ifdef CONFIG_NET_POLL_CONTROLLER +void hns_nic_poll_controller(struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + unsigned long flags; + int i; + + local_irq_save(flags); + for (i = 0; i < priv->ae_handle->q_num * 2; i++) + napi_schedule(&priv->ring_data[i].napi); + local_irq_restore(flags); +} +#endif + +static netdev_tx_t hns_nic_net_xmit(struct sk_buff *skb, + struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + int ret; + + assert(skb->queue_mapping < ndev->ae_handle->q_num); + ret = hns_nic_net_xmit_hw(ndev, skb, + &tx_ring_data(priv, skb->queue_mapping)); + if (ret == NETDEV_TX_OK) { + ndev->trans_start = jiffies; + ndev->stats.tx_bytes += skb->len; + ndev->stats.tx_packets++; + } + return (netdev_tx_t)ret; +} + +static int hns_nic_change_mtu(struct net_device *ndev, int new_mtu) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h = priv->ae_handle; + int ret; + + /* MTU < 68 is an error and causes problems on some kernels */ + if (new_mtu < 68) + return -EINVAL; + + if (!h->dev->ops->set_mtu) + return -ENOTSUPP; + + if (netif_running(ndev)) { + (void)hns_nic_net_stop(ndev); + msleep(100); + + ret = h->dev->ops->set_mtu(h, new_mtu); + if (ret) + netdev_err(ndev, "set mtu fail, return value %d\n", + ret); + + if (hns_nic_net_open(ndev)) + netdev_err(ndev, "hns net open fail\n"); + } else { + ret = h->dev->ops->set_mtu(h, new_mtu); + } + + if (!ret) + ndev->mtu = new_mtu; + + return ret; +} + +/** + * nic_set_multicast_list - set mutl mac address + * @netdev: net device + * @p: mac address + * + * return void + */ +void hns_set_multicast_list(struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h = priv->ae_handle; + struct netdev_hw_addr *ha = NULL; + + if (!h) { + netdev_err(ndev, "hnae handle is null\n"); + return; + } + + if (h->dev->ops->set_mc_addr) { + netdev_for_each_mc_addr(ha, ndev) + if (h->dev->ops->set_mc_addr(h, ha->addr)) + netdev_err(ndev, "set multicast fail\n"); + } +} + +struct rtnl_link_stats64 *hns_nic_get_stats64(struct net_device *ndev, + struct rtnl_link_stats64 *stats) +{ + int idx = 0; + u64 tx_bytes = 0; + u64 rx_bytes = 0; + u64 tx_pkts = 0; + u64 rx_pkts = 0; + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h = priv->ae_handle; + + for (idx = 0; idx < h->q_num; idx++) { + tx_bytes += h->qs[idx]->tx_ring.stats.tx_bytes; + tx_pkts += h->qs[idx]->tx_ring.stats.tx_pkts; + rx_bytes += h->qs[idx]->rx_ring.stats.rx_bytes; + rx_pkts += h->qs[idx]->rx_ring.stats.rx_pkts; + } + + stats->tx_bytes = tx_bytes; + stats->tx_packets = tx_pkts; + stats->rx_bytes = rx_bytes; + stats->rx_packets = rx_pkts; + + stats->rx_errors = ndev->stats.rx_errors; + stats->multicast = ndev->stats.multicast; + stats->rx_length_errors = ndev->stats.rx_length_errors; + stats->rx_crc_errors = ndev->stats.rx_crc_errors; + stats->rx_missed_errors = ndev->stats.rx_missed_errors; + + stats->tx_errors = ndev->stats.tx_errors; + stats->rx_dropped = ndev->stats.rx_dropped; + stats->tx_dropped = ndev->stats.tx_dropped; + stats->collisions = ndev->stats.collisions; + stats->rx_over_errors = ndev->stats.rx_over_errors; + stats->rx_frame_errors = ndev->stats.rx_frame_errors; + stats->rx_fifo_errors = ndev->stats.rx_fifo_errors; + stats->tx_aborted_errors = ndev->stats.tx_aborted_errors; + stats->tx_carrier_errors = ndev->stats.tx_carrier_errors; + stats->tx_fifo_errors = ndev->stats.tx_fifo_errors; + stats->tx_heartbeat_errors = ndev->stats.tx_heartbeat_errors; + stats->tx_window_errors = ndev->stats.tx_window_errors; + stats->rx_compressed = ndev->stats.rx_compressed; + stats->tx_compressed = ndev->stats.tx_compressed; + + return stats; +} + +static const struct net_device_ops hns_nic_netdev_ops = { + .ndo_open = hns_nic_net_open, + .ndo_stop = hns_nic_net_stop, + .ndo_start_xmit = hns_nic_net_xmit, + .ndo_tx_timeout = hns_nic_net_timeout, + .ndo_set_mac_address = hns_nic_net_set_mac_address, + .ndo_change_mtu = hns_nic_change_mtu, + .ndo_do_ioctl = hns_nic_do_ioctl, + .ndo_get_stats64 = hns_nic_get_stats64, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = hns_nic_poll_controller, +#endif + .ndo_set_rx_mode = hns_set_multicast_list, +}; + +static void hns_nic_update_link_status(struct net_device *netdev) +{ + struct hns_nic_priv *priv = netdev_priv(netdev); + + struct hnae_handle *h = priv->ae_handle; + int state = 1; + + if (priv->phy) { + if (!genphy_update_link(priv->phy)) + state = priv->phy->link; + else + state = 0; + } + state = state && h->dev->ops->get_status(h); + + if (state != priv->link) { + if (state) { + netif_carrier_on(netdev); + netif_tx_wake_all_queues(netdev); + netdev_info(netdev, "link up\n"); + } else { + netif_carrier_off(netdev); + netdev_info(netdev, "link down\n"); + } + priv->link = state; + } +} + +/* for dumping key regs*/ +static void hns_nic_dump(struct hns_nic_priv *priv) +{ + struct hnae_handle *h = priv->ae_handle; + struct hnae_ae_ops *ops = h->dev->ops; + u32 *data, reg_num, i; + + if (ops->get_regs_len && ops->get_regs) { + reg_num = ops->get_regs_len(priv->ae_handle); + reg_num = (reg_num + 3ul) & ~3ul; + data = kcalloc(reg_num, sizeof(u32), GFP_KERNEL); + if (data) { + ops->get_regs(priv->ae_handle, data); + for (i = 0; i < reg_num; i += 4) + pr_info("0x%08x: 0x%08x 0x%08x 0x%08x 0x%08x\n", + i, data[i], data[i + 1], + data[i + 2], data[i + 3]); + kfree(data); + } + } + + for (i = 0; i < h->q_num; i++) { + pr_info("tx_queue%d_next_to_clean:%d\n", + i, h->qs[i]->tx_ring.next_to_clean); + pr_info("tx_queue%d_next_to_use:%d\n", + i, h->qs[i]->tx_ring.next_to_use); + pr_info("rx_queue%d_next_to_clean:%d\n", + i, h->qs[i]->rx_ring.next_to_clean); + pr_info("rx_queue%d_next_to_use:%d\n", + i, h->qs[i]->rx_ring.next_to_use); + } +} + +/* for resetting suntask*/ +static void hns_nic_reset_subtask(struct hns_nic_priv *priv) +{ + enum hnae_port_type type = priv->ae_handle->port_type; + + if (!test_bit(NIC_STATE2_RESET_REQUESTED, &priv->state)) + return; + clear_bit(NIC_STATE2_RESET_REQUESTED, &priv->state); + + /* If we're already down, removing or resetting, just bail */ + if (test_bit(NIC_STATE_DOWN, &priv->state) || + test_bit(NIC_STATE_REMOVING, &priv->state) || + test_bit(NIC_STATE_RESETTING, &priv->state)) + return; + + hns_nic_dump(priv); + netdev_err(priv->netdev, "Reset %s port\n", + (type == HNAE_PORT_DEBUG ? "debug" : "business")); + + rtnl_lock(); + if (type == HNAE_PORT_DEBUG) { + hns_nic_net_reinit(priv->netdev); + } else { + hns_nic_net_down(priv->netdev); + hns_nic_net_reset(priv->netdev); + } + rtnl_unlock(); +} + +/* for doing service complete*/ +static void hns_nic_service_event_complete(struct hns_nic_priv *priv) +{ + assert(!test_bit(NIC_STATE_SERVICE_SCHED, &priv->state)); + + smp_mb__before_atomic(); + clear_bit(NIC_STATE_SERVICE_SCHED, &priv->state); +} + +static void hns_nic_service_task(struct work_struct *work) +{ + struct hns_nic_priv *priv + = container_of(work, struct hns_nic_priv, service_task); + struct hnae_handle *h = priv->ae_handle; + + hns_nic_update_link_status(priv->netdev); + h->dev->ops->update_led_status(h); + hns_nic_update_stats(priv->netdev); + + hns_nic_reset_subtask(priv); + hns_nic_service_event_complete(priv); +} + +static void hns_nic_task_schedule(struct hns_nic_priv *priv) +{ + if (!test_bit(NIC_STATE_DOWN, &priv->state) && + !test_bit(NIC_STATE_REMOVING, &priv->state) && + !test_and_set_bit(NIC_STATE_SERVICE_SCHED, &priv->state)) + (void)schedule_work(&priv->service_task); +} + +static void hns_nic_service_timer(unsigned long data) +{ + struct hns_nic_priv *priv = (struct hns_nic_priv *)data; + + (void)mod_timer(&priv->service_timer, jiffies + SERVICE_TIMER_HZ); + + hns_nic_task_schedule(priv); +} + +/** + * hns_tx_timeout_reset - initiate reset due to Tx timeout + * @priv: driver private struct + **/ +static void hns_tx_timeout_reset(struct hns_nic_priv *priv) +{ + /* Do the reset outside of interrupt context */ + if (!test_bit(NIC_STATE_DOWN, &priv->state)) { + set_bit(NIC_STATE2_RESET_REQUESTED, &priv->state); + netdev_warn(priv->netdev, + "initiating reset due to tx timeout(%llu,0x%lx)\n", + priv->tx_timeout_count, priv->state); + priv->tx_timeout_count++; + hns_nic_task_schedule(priv); + } +} + +static int hns_nic_init_ring_data(struct hns_nic_priv *priv) +{ + struct hnae_handle *h = priv->ae_handle; + struct hns_nic_ring_data *rd; + int i; + + if (h->q_num > NIC_MAX_Q_PER_VF) { + netdev_err(priv->netdev, "too much queue (%d)\n", h->q_num); + return -EINVAL; + } + + priv->ring_data = kzalloc(h->q_num * sizeof(*priv->ring_data) * 2, + GFP_KERNEL); + if (!priv->ring_data) + return -ENOMEM; + + for (i = 0; i < h->q_num; i++) { + rd = &priv->ring_data[i]; + rd->queue_index = i; + rd->ring = &h->qs[i]->tx_ring; + rd->poll_one = hns_nic_tx_poll_one; + rd->fini_process = hns_nic_tx_fini_pro; + + netif_napi_add(priv->netdev, &rd->napi, + hns_nic_common_poll, NIC_TX_CLEAN_MAX_NUM); + rd->ring->irq_init_flag = RCB_IRQ_NOT_INITED; + } + for (i = h->q_num; i < h->q_num * 2; i++) { + rd = &priv->ring_data[i]; + rd->queue_index = i - h->q_num; + rd->ring = &h->qs[i - h->q_num]->rx_ring; + rd->poll_one = hns_nic_rx_poll_one; + rd->ex_process = hns_nic_rx_up_pro; + rd->fini_process = hns_nic_rx_fini_pro; + + netif_napi_add(priv->netdev, &rd->napi, + hns_nic_common_poll, NIC_RX_CLEAN_MAX_NUM); + rd->ring->irq_init_flag = RCB_IRQ_NOT_INITED; + } + + return 0; +} + +static void hns_nic_uninit_ring_data(struct hns_nic_priv *priv) +{ + struct hnae_handle *h = priv->ae_handle; + int i; + + for (i = 0; i < h->q_num * 2; i++) { + netif_napi_del(&priv->ring_data[i].napi); + if (priv->ring_data[i].ring->irq_init_flag == RCB_IRQ_INITED) { + irq_set_affinity_hint(priv->ring_data[i].ring->irq, + NULL); + free_irq(priv->ring_data[i].ring->irq, + &priv->ring_data[i]); + } + + priv->ring_data[i].ring->irq_init_flag = RCB_IRQ_NOT_INITED; + } + kfree(priv->ring_data); +} + +static int hns_nic_try_get_ae(struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h; + int ret; + + h = hnae_get_handle(&priv->netdev->dev, + priv->ae_name, priv->port_id, NULL); + if (IS_ERR_OR_NULL(h)) { + ret = PTR_ERR(h); + dev_dbg(priv->dev, "has not handle, register notifier!\n"); + goto out; + } + priv->ae_handle = h; + + ret = hns_nic_init_phy(ndev, h); + if (ret) { + dev_err(priv->dev, "probe phy device fail!\n"); + goto out_init_phy; + } + + ret = hns_nic_init_ring_data(priv); + if (ret) { + ret = -ENOMEM; + goto out_init_ring_data; + } + + ret = register_netdev(ndev); + if (ret) { + dev_err(priv->dev, "probe register netdev fail!\n"); + goto out_reg_ndev_fail; + } + return 0; + +out_reg_ndev_fail: + hns_nic_uninit_ring_data(priv); + priv->ring_data = NULL; +out_init_phy: +out_init_ring_data: + hnae_put_handle(priv->ae_handle); + priv->ae_handle = NULL; +out: + return ret; +} + +static int hns_nic_notifier_action(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct hns_nic_priv *priv = + container_of(nb, struct hns_nic_priv, notifier_block); + + assert(action == HNAE_AE_REGISTER); + + if (!hns_nic_try_get_ae(priv->netdev)) { + hnae_unregister_notifier(&priv->notifier_block); + priv->notifier_block.notifier_call = NULL; + } + return 0; +} + +static int hns_nic_dev_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct net_device *ndev; + struct hns_nic_priv *priv; + struct device_node *node = dev->of_node; + int ret; + + ndev = alloc_etherdev_mq(sizeof(struct hns_nic_priv), NIC_MAX_Q_PER_VF); + if (!ndev) + return -ENOMEM; + + platform_set_drvdata(pdev, ndev); + + priv = netdev_priv(ndev); + priv->dev = dev; + priv->netdev = ndev; + + if (of_device_is_compatible(node, "hisilicon,hns-nic-v2")) + priv->enet_ver = AE_VERSION_2; + else + priv->enet_ver = AE_VERSION_1; + + ret = of_property_read_string(node, "ae-name", &priv->ae_name); + if (ret) + goto out_read_string_fail; + + ret = of_property_read_u32(node, "port-id", &priv->port_id); + if (ret) + goto out_read_string_fail; + + hns_init_mac_addr(ndev); + + ndev->watchdog_timeo = HNS_NIC_TX_TIMEOUT; + ndev->priv_flags |= IFF_UNICAST_FLT; + ndev->netdev_ops = &hns_nic_netdev_ops; + hns_ethtool_set_ops(ndev); + ndev->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | + NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_GSO | + NETIF_F_GRO; + ndev->vlan_features |= + NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM; + ndev->vlan_features |= NETIF_F_SG | NETIF_F_GSO | NETIF_F_GRO; + + SET_NETDEV_DEV(ndev, dev); + + if (!dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64))) + dev_dbg(dev, "set mask to 64bit\n"); + else + dev_err(dev, "set mask to 32bit fail!\n"); + + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(ndev); + + setup_timer(&priv->service_timer, hns_nic_service_timer, + (unsigned long)priv); + INIT_WORK(&priv->service_task, hns_nic_service_task); + + set_bit(NIC_STATE_SERVICE_INITED, &priv->state); + clear_bit(NIC_STATE_SERVICE_SCHED, &priv->state); + set_bit(NIC_STATE_DOWN, &priv->state); + + if (hns_nic_try_get_ae(priv->netdev)) { + priv->notifier_block.notifier_call = hns_nic_notifier_action; + ret = hnae_register_notifier(&priv->notifier_block); + if (ret) { + dev_err(dev, "register notifier fail!\n"); + goto out_notify_fail; + } + dev_dbg(dev, "has not handle, register notifier!\n"); + } + + return 0; + +out_notify_fail: + (void)cancel_work_sync(&priv->service_task); +out_read_string_fail: + free_netdev(ndev); + return ret; +} + +static int hns_nic_dev_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + struct hns_nic_priv *priv = netdev_priv(ndev); + + if (ndev->reg_state != NETREG_UNINITIALIZED) + unregister_netdev(ndev); + + if (priv->ring_data) + hns_nic_uninit_ring_data(priv); + priv->ring_data = NULL; + + if (priv->phy) + phy_disconnect(priv->phy); + priv->phy = NULL; + + if (!IS_ERR_OR_NULL(priv->ae_handle)) + hnae_put_handle(priv->ae_handle); + priv->ae_handle = NULL; + if (priv->notifier_block.notifier_call) + hnae_unregister_notifier(&priv->notifier_block); + priv->notifier_block.notifier_call = NULL; + + set_bit(NIC_STATE_REMOVING, &priv->state); + (void)cancel_work_sync(&priv->service_task); + + free_netdev(ndev); + return 0; +} + +static const struct of_device_id hns_enet_of_match[] = { + {.compatible = "hisilicon,hns-nic-v1",}, + {.compatible = "hisilicon,hns-nic-v2",}, + {}, +}; + +MODULE_DEVICE_TABLE(of, hns_enet_of_match); + +static struct platform_driver hns_nic_dev_driver = { + .driver = { + .name = "hns-nic", + .owner = THIS_MODULE, + .of_match_table = hns_enet_of_match, + }, + .probe = hns_nic_dev_probe, + .remove = hns_nic_dev_remove, +}; + +module_platform_driver(hns_nic_dev_driver); + +MODULE_DESCRIPTION("HISILICON HNS Ethernet driver"); +MODULE_AUTHOR("Hisilicon, Inc."); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:hns-nic"); diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.h b/drivers/net/ethernet/hisilicon/hns/hns_enet.h new file mode 100644 index 000000000000..dae0ed19ac6d --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * 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 __HNS_ENET_H +#define __HNS_ENET_H + +#include <linux/netdevice.h> +#include <linux/of_net.h> +#include <linux/of_mdio.h> +#include <linux/timer.h> +#include <linux/workqueue.h> + +#include "hnae.h" + +enum hns_nic_state { + NIC_STATE_TESTING = 0, + NIC_STATE_RESETTING, + NIC_STATE_REINITING, + NIC_STATE_DOWN, + NIC_STATE_DISABLED, + NIC_STATE_REMOVING, + NIC_STATE_SERVICE_INITED, + NIC_STATE_SERVICE_SCHED, + NIC_STATE2_RESET_REQUESTED, + NIC_STATE_MAX +}; + +struct hns_nic_ring_data { + struct hnae_ring *ring; + struct napi_struct napi; + int queue_index; + int (*poll_one)(struct hns_nic_ring_data *, int, void *); + void (*ex_process)(struct hns_nic_ring_data *, struct sk_buff *); + void (*fini_process)(struct hns_nic_ring_data *); +}; + +struct hns_nic_priv { + const char *ae_name; + u32 enet_ver; + u32 port_id; + int phy_mode; + int phy_led_val; + struct phy_device *phy; + struct net_device *netdev; + struct device *dev; + struct hnae_handle *ae_handle; + + /* the cb for nic to manage the ring buffer, the first half of the + * array is for tx_ring and vice versa for the second half + */ + struct hns_nic_ring_data *ring_data; + + /* The most recently read link state */ + int link; + u64 tx_timeout_count; + + unsigned long state; + + struct timer_list service_timer; + + struct work_struct service_task; + + struct notifier_block notifier_block; +}; + +#define tx_ring_data(priv, idx) ((priv)->ring_data[idx]) +#define rx_ring_data(priv, idx) \ + ((priv)->ring_data[(priv)->ae_handle->q_num + (idx)]) + +void hns_ethtool_set_ops(struct net_device *ndev); +void hns_nic_net_reset(struct net_device *ndev); +void hns_nic_net_reinit(struct net_device *netdev); +int hns_nic_init_phy(struct net_device *ndev, struct hnae_handle *h); +int hns_nic_net_xmit_hw(struct net_device *ndev, + struct sk_buff *skb, + struct hns_nic_ring_data *ring_data); + +#endif /**__HNS_ENET_H */ diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c new file mode 100644 index 000000000000..2550208cb22e --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c @@ -0,0 +1,1230 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * 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/etherdevice.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include "hns_enet.h" + +#define HNS_PHY_PAGE_MDIX 0 +#define HNS_PHY_PAGE_LED 3 +#define HNS_PHY_PAGE_COPPER 0 + +#define HNS_PHY_PAGE_REG 22 /* Page Selection Reg. */ +#define HNS_PHY_CSC_REG 16 /* Copper Specific Control Register */ +#define HNS_PHY_CSS_REG 17 /* Copper Specific Status Register */ +#define HNS_LED_FC_REG 16 /* LED Function Control Reg. */ +#define HNS_LED_PC_REG 17 /* LED Polarity Control Reg. */ + +#define HNS_LED_FORCE_ON 9 +#define HNS_LED_FORCE_OFF 8 + +#define HNS_CHIP_VERSION 660 +#define HNS_NET_STATS_CNT 26 + +#define PHY_MDIX_CTRL_S (5) +#define PHY_MDIX_CTRL_M (3 << PHY_MDIX_CTRL_S) + +#define PHY_MDIX_STATUS_B (6) +#define PHY_SPEED_DUP_RESOLVE_B (11) + +/** + *hns_nic_get_link - get current link status + *@net_dev: net_device + *retuen 0 - success , negative --fail + */ +static u32 hns_nic_get_link(struct net_device *net_dev) +{ + struct hns_nic_priv *priv = netdev_priv(net_dev); + u32 link_stat = priv->link; + struct hnae_handle *h; + + assert(priv && priv->ae_handle); + h = priv->ae_handle; + + if (priv->phy) { + if (!genphy_update_link(priv->phy)) + link_stat = priv->phy->link; + else + link_stat = 0; + } + + if (h->dev && h->dev->ops && h->dev->ops->get_status) + link_stat = link_stat && h->dev->ops->get_status(h); + else + link_stat = 0; + + return link_stat; +} + +static void hns_get_mdix_mode(struct net_device *net_dev, + struct ethtool_cmd *cmd) +{ + int mdix_ctrl, mdix, retval, is_resolved; + struct hns_nic_priv *priv = netdev_priv(net_dev); + struct phy_device *phy_dev = priv->phy; + + if (!phy_dev || !phy_dev->bus) { + cmd->eth_tp_mdix_ctrl = ETH_TP_MDI_INVALID; + cmd->eth_tp_mdix = ETH_TP_MDI_INVALID; + return; + } + + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, HNS_PHY_PAGE_REG, + HNS_PHY_PAGE_MDIX); + + retval = mdiobus_read(phy_dev->bus, phy_dev->addr, HNS_PHY_CSC_REG); + mdix_ctrl = hnae_get_field(retval, PHY_MDIX_CTRL_M, PHY_MDIX_CTRL_S); + + retval = mdiobus_read(phy_dev->bus, phy_dev->addr, HNS_PHY_CSS_REG); + mdix = hnae_get_bit(retval, PHY_MDIX_STATUS_B); + is_resolved = hnae_get_bit(retval, PHY_SPEED_DUP_RESOLVE_B); + + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, HNS_PHY_PAGE_REG, + HNS_PHY_PAGE_COPPER); + + switch (mdix_ctrl) { + case 0x0: + cmd->eth_tp_mdix_ctrl = ETH_TP_MDI; + break; + case 0x1: + cmd->eth_tp_mdix_ctrl = ETH_TP_MDI_X; + break; + case 0x3: + cmd->eth_tp_mdix_ctrl = ETH_TP_MDI_AUTO; + break; + default: + cmd->eth_tp_mdix_ctrl = ETH_TP_MDI_INVALID; + break; + } + + if (!is_resolved) + cmd->eth_tp_mdix = ETH_TP_MDI_INVALID; + else if (mdix) + cmd->eth_tp_mdix = ETH_TP_MDI_X; + else + cmd->eth_tp_mdix = ETH_TP_MDI; +} + +/** + *hns_nic_get_settings - implement ethtool get settings + *@net_dev: net_device + *@cmd: ethtool_cmd + *retuen 0 - success , negative --fail + */ +static int hns_nic_get_settings(struct net_device *net_dev, + struct ethtool_cmd *cmd) +{ + struct hns_nic_priv *priv = netdev_priv(net_dev); + struct hnae_handle *h; + u32 link_stat; + int ret; + u8 duplex; + u16 speed; + + if (!priv || !priv->ae_handle) + return -ESRCH; + + h = priv->ae_handle; + if (!h->dev || !h->dev->ops || !h->dev->ops->get_info) + return -ESRCH; + + ret = h->dev->ops->get_info(h, NULL, &speed, &duplex); + if (ret < 0) { + netdev_err(net_dev, "%s get_info error!\n", __func__); + return -EINVAL; + } + + /* When there is no phy, autoneg is off. */ + cmd->autoneg = false; + ethtool_cmd_speed_set(cmd, speed); + cmd->duplex = duplex; + + if (priv->phy) + (void)phy_ethtool_gset(priv->phy, cmd); + + link_stat = hns_nic_get_link(net_dev); + if (!link_stat) { + ethtool_cmd_speed_set(cmd, (u32)SPEED_UNKNOWN); + cmd->duplex = DUPLEX_UNKNOWN; + } + + if (cmd->autoneg) + cmd->advertising |= ADVERTISED_Autoneg; + + cmd->supported |= h->if_support; + if (h->phy_if == PHY_INTERFACE_MODE_SGMII) { + cmd->supported |= SUPPORTED_TP; + cmd->advertising |= ADVERTISED_1000baseT_Full; + } else if (h->phy_if == PHY_INTERFACE_MODE_XGMII) { + cmd->supported |= SUPPORTED_FIBRE; + cmd->advertising |= ADVERTISED_10000baseKR_Full; + } + + if (h->port_type == HNAE_PORT_SERVICE) { + cmd->port = PORT_FIBRE; + cmd->supported |= SUPPORTED_Pause; + } else { + cmd->port = PORT_TP; + } + + cmd->transceiver = XCVR_EXTERNAL; + cmd->mdio_support = (ETH_MDIO_SUPPORTS_C45 | ETH_MDIO_SUPPORTS_C22); + hns_get_mdix_mode(net_dev, cmd); + + return 0; +} + +/** + *hns_nic_set_settings - implement ethtool set settings + *@net_dev: net_device + *@cmd: ethtool_cmd + *retuen 0 - success , negative --fail + */ +static int hns_nic_set_settings(struct net_device *net_dev, + struct ethtool_cmd *cmd) +{ + struct hns_nic_priv *priv = netdev_priv(net_dev); + struct hnae_handle *h; + int link_stat; + u32 speed; + u8 duplex, autoneg; + + if (!netif_running(net_dev)) + return -ESRCH; + + if (!priv || !priv->ae_handle || !priv->ae_handle->dev || + !priv->ae_handle->dev->ops) + return -ENODEV; + + h = priv->ae_handle; + link_stat = hns_nic_get_link(net_dev); + duplex = cmd->duplex; + speed = ethtool_cmd_speed(cmd); + autoneg = cmd->autoneg; + + if (!link_stat) { + if (duplex != (u8)DUPLEX_UNKNOWN || speed != (u32)SPEED_UNKNOWN) + return -EINVAL; + + if (h->phy_if == PHY_INTERFACE_MODE_SGMII && h->phy_node) { + priv->phy->autoneg = autoneg; + return phy_start_aneg(priv->phy); + } + } + + if (h->phy_if == PHY_INTERFACE_MODE_XGMII) { + if (autoneg != AUTONEG_DISABLE) + return -EINVAL; + + if (speed != SPEED_10000 || duplex != DUPLEX_FULL) + return -EINVAL; + } else if (h->phy_if == PHY_INTERFACE_MODE_SGMII) { + if (!h->phy_node && autoneg != AUTONEG_DISABLE) + return -EINVAL; + + if (speed == SPEED_1000 && duplex == DUPLEX_HALF) + return -EINVAL; + + if (speed != SPEED_10 && speed != SPEED_100 && + speed != SPEED_1000) + return -EINVAL; + } else { + netdev_err(net_dev, "Not supported!"); + return -ENOTSUPP; + } + + if (priv->phy) { + return phy_ethtool_sset(priv->phy, cmd); + } else if (h->dev->ops->adjust_link && link_stat) { + h->dev->ops->adjust_link(h, speed, duplex); + return 0; + } + netdev_err(net_dev, "Not supported!"); + return -ENOTSUPP; +} + +static const char hns_nic_test_strs[][ETH_GSTRING_LEN] = { + "Mac Loopback test", + "Serdes Loopback test", + "Phy Loopback test" +}; + +static int hns_nic_config_phy_loopback(struct phy_device *phy_dev, u8 en) +{ +#define COPPER_CONTROL_REG 0 +#define PHY_LOOP_BACK BIT(14) + u16 val = 0; + + if (phy_dev->is_c45) /* c45 branch adding for XGE PHY */ + return -ENOTSUPP; + + if (en) { + /* speed : 1000M */ + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + HNS_PHY_PAGE_REG, 2); + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + 21, 0x1046); + /* Force Master */ + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + 9, 0x1F00); + /* Soft-reset */ + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + 0, 0x9140); + /* If autoneg disabled,two soft-reset operations */ + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + 0, 0x9140); + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + 22, 0xFA); + + /* Default is 0x0400 */ + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + 1, 0x418); + + /* Force 1000M Link, Default is 0x0200 */ + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + 7, 0x20C); + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + 22, 0); + + /* Enable MAC loop-back */ + val = (u16)mdiobus_read(phy_dev->bus, phy_dev->addr, + COPPER_CONTROL_REG); + val |= PHY_LOOP_BACK; + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + COPPER_CONTROL_REG, val); + } else { + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + 22, 0xFA); + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + 1, 0x400); + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + 7, 0x200); + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + 22, 0); + + val = (u16)mdiobus_read(phy_dev->bus, phy_dev->addr, + COPPER_CONTROL_REG); + val &= ~PHY_LOOP_BACK; + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + COPPER_CONTROL_REG, val); + } + return 0; +} + +static int __lb_setup(struct net_device *ndev, + enum hnae_loop loop) +{ + int ret = 0; + struct hns_nic_priv *priv = netdev_priv(ndev); + struct phy_device *phy_dev = priv->phy; + struct hnae_handle *h = priv->ae_handle; + + switch (loop) { + case MAC_INTERNALLOOP_PHY: + if ((phy_dev) && (!phy_dev->is_c45)) + ret = hns_nic_config_phy_loopback(phy_dev, 0x1); + break; + case MAC_INTERNALLOOP_MAC: + if ((h->dev->ops->set_loopback) && + (priv->ae_handle->phy_if != PHY_INTERFACE_MODE_XGMII)) + ret = h->dev->ops->set_loopback(h, loop, 0x1); + break; + case MAC_INTERNALLOOP_SERDES: + if (h->dev->ops->set_loopback) + ret = h->dev->ops->set_loopback(h, loop, 0x1); + break; + case MAC_LOOP_NONE: + if ((phy_dev) && (!phy_dev->is_c45)) + ret |= hns_nic_config_phy_loopback(phy_dev, 0x0); + + if (h->dev->ops->set_loopback) { + if (priv->ae_handle->phy_if != PHY_INTERFACE_MODE_XGMII) + ret |= h->dev->ops->set_loopback(h, + MAC_INTERNALLOOP_MAC, 0x0); + + ret |= h->dev->ops->set_loopback(h, + MAC_INTERNALLOOP_SERDES, 0x0); + } + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int __lb_up(struct net_device *ndev, + enum hnae_loop loop_mode) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h = priv->ae_handle; + int speed, duplex; + int ret; + + hns_nic_net_reset(ndev); + + if (priv->phy) { + phy_disconnect(priv->phy); + msleep(100); + + ret = hns_nic_init_phy(ndev, h); + if (ret) + return ret; + } + + ret = __lb_setup(ndev, loop_mode); + if (ret) + return ret; + + msleep(100); + + ret = h->dev->ops->start ? h->dev->ops->start(h) : 0; + if (ret) + return ret; + + if (priv->phy) + phy_start(priv->phy); + + /* link adjust duplex*/ + if (priv->ae_handle->phy_if != PHY_INTERFACE_MODE_XGMII) + speed = 1000; + else + speed = 10000; + duplex = 1; + + h->dev->ops->adjust_link(h, speed, duplex); + + return 0; +} + +static void __lb_other_process(struct hns_nic_ring_data *ring_data, + struct sk_buff *skb) +{ + struct net_device *ndev; + struct hnae_ring *ring; + struct netdev_queue *dev_queue; + struct sk_buff *new_skb; + unsigned int frame_size; + int check_ok; + u32 i; + char buff[33]; /* 32B data and the last character '\0' */ + + if (!ring_data) { /* Just for doing create frame*/ + frame_size = skb->len; + memset(skb->data, 0xFF, frame_size); + frame_size &= ~1ul; + memset(&skb->data[frame_size / 2], 0xAA, frame_size / 2 - 1); + memset(&skb->data[frame_size / 2 + 10], 0xBE, + frame_size / 2 - 11); + memset(&skb->data[frame_size / 2 + 12], 0xAF, + frame_size / 2 - 13); + return; + } + + ring = ring_data->ring; + ndev = ring_data->napi.dev; + if (is_tx_ring(ring)) { /* for tx queue reset*/ + dev_queue = netdev_get_tx_queue(ndev, ring_data->queue_index); + netdev_tx_reset_queue(dev_queue); + return; + } + + frame_size = skb->len; + frame_size &= ~1ul; + /* for mutl buffer*/ + new_skb = skb_copy(skb, GFP_ATOMIC); + dev_kfree_skb_any(skb); + skb = new_skb; + + check_ok = 0; + if (*(skb->data + 10) == 0xFF) { /* for rx check frame*/ + if ((*(skb->data + frame_size / 2 + 10) == 0xBE) && + (*(skb->data + frame_size / 2 + 12) == 0xAF)) + check_ok = 1; + } + + if (check_ok) { + ndev->stats.rx_packets++; + ndev->stats.rx_bytes += skb->len; + } else { + ndev->stats.rx_frame_errors++; + for (i = 0; i < skb->len; i++) { + snprintf(buff + i % 16 * 2, 3, /* tailing \0*/ + "%02x", *(skb->data + i)); + if ((i % 16 == 15) || (i == skb->len - 1)) + pr_info("%s\n", buff); + } + } + dev_kfree_skb_any(skb); +} + +static int __lb_clean_rings(struct hns_nic_priv *priv, + int ringid0, int ringid1, int budget) +{ + int i, ret; + struct hns_nic_ring_data *ring_data; + struct net_device *ndev = priv->netdev; + unsigned long rx_packets = ndev->stats.rx_packets; + unsigned long rx_bytes = ndev->stats.rx_bytes; + unsigned long rx_frame_errors = ndev->stats.rx_frame_errors; + + for (i = ringid0; i <= ringid1; i++) { + ring_data = &priv->ring_data[i]; + (void)ring_data->poll_one(ring_data, + budget, __lb_other_process); + } + ret = (int)(ndev->stats.rx_packets - rx_packets); + ndev->stats.rx_packets = rx_packets; + ndev->stats.rx_bytes = rx_bytes; + ndev->stats.rx_frame_errors = rx_frame_errors; + return ret; +} + +/** + * nic_run_loopback_test - run loopback test + * @nic_dev: net device + * @loopback_type: loopback type + */ +static int __lb_run_test(struct net_device *ndev, + enum hnae_loop loop_mode) +{ +#define NIC_LB_TEST_PKT_NUM_PER_CYCLE 1 +#define NIC_LB_TEST_RING_ID 0 +#define NIC_LB_TEST_FRAME_SIZE 128 +/* nic loopback test err */ +#define NIC_LB_TEST_NO_MEM_ERR 1 +#define NIC_LB_TEST_TX_CNT_ERR 2 +#define NIC_LB_TEST_RX_CNT_ERR 3 +#define NIC_LB_TEST_RX_PKG_ERR 4 + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h = priv->ae_handle; + int i, j, lc, good_cnt, ret_val = 0; + unsigned int size; + netdev_tx_t tx_ret_val; + struct sk_buff *skb; + + size = NIC_LB_TEST_FRAME_SIZE; + /* allocate test skb */ + skb = alloc_skb(size, GFP_KERNEL); + if (!skb) + return NIC_LB_TEST_NO_MEM_ERR; + + /* place data into test skb */ + (void)skb_put(skb, size); + __lb_other_process(NULL, skb); + skb->queue_mapping = NIC_LB_TEST_RING_ID; + + lc = 1; + for (j = 0; j < lc; j++) { + /* reset count of good packets */ + good_cnt = 0; + /* place 64 packets on the transmit queue*/ + for (i = 0; i < NIC_LB_TEST_PKT_NUM_PER_CYCLE; i++) { + (void)skb_get(skb); + + tx_ret_val = (netdev_tx_t)hns_nic_net_xmit_hw( + ndev, skb, + &tx_ring_data(priv, skb->queue_mapping)); + if (tx_ret_val == NETDEV_TX_OK) + good_cnt++; + else + break; + } + if (good_cnt != NIC_LB_TEST_PKT_NUM_PER_CYCLE) { + ret_val = NIC_LB_TEST_TX_CNT_ERR; + dev_err(priv->dev, "%s sent fail, cnt=0x%x, budget=0x%x\n", + hns_nic_test_strs[loop_mode], good_cnt, + NIC_LB_TEST_PKT_NUM_PER_CYCLE); + break; + } + + /* allow 100 milliseconds for packets to go from Tx to Rx */ + msleep(100); + + good_cnt = __lb_clean_rings(priv, + h->q_num, h->q_num * 2 - 1, + NIC_LB_TEST_PKT_NUM_PER_CYCLE); + if (good_cnt != NIC_LB_TEST_PKT_NUM_PER_CYCLE) { + ret_val = NIC_LB_TEST_RX_CNT_ERR; + dev_err(priv->dev, "%s recv fail, cnt=0x%x, budget=0x%x\n", + hns_nic_test_strs[loop_mode], good_cnt, + NIC_LB_TEST_PKT_NUM_PER_CYCLE); + break; + } + (void)__lb_clean_rings(priv, + NIC_LB_TEST_RING_ID, NIC_LB_TEST_RING_ID, + NIC_LB_TEST_PKT_NUM_PER_CYCLE); + } + + /* free the original skb */ + kfree_skb(skb); + + return ret_val; +} + +static int __lb_down(struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h = priv->ae_handle; + int ret; + + ret = __lb_setup(ndev, MAC_LOOP_NONE); + if (ret) + netdev_err(ndev, "%s: __lb_setup return error(%d)!\n", + __func__, + ret); + + if (priv->phy) + phy_stop(priv->phy); + + if (h->dev->ops->stop) + h->dev->ops->stop(h); + + usleep_range(10000, 20000); + (void)__lb_clean_rings(priv, 0, h->q_num - 1, 256); + + hns_nic_net_reset(ndev); + + return 0; +} + +/** + * hns_nic_self_test - self test + * @dev: net device + * @eth_test: test cmd + * @data: test result + */ +static void hns_nic_self_test(struct net_device *ndev, + struct ethtool_test *eth_test, u64 *data) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + bool if_running = netif_running(ndev); +#define SELF_TEST_TPYE_NUM 3 + int st_param[SELF_TEST_TPYE_NUM][2]; + int i; + int test_index = 0; + + st_param[0][0] = MAC_INTERNALLOOP_MAC; /* XGE not supported lb */ + st_param[0][1] = (priv->ae_handle->phy_if != PHY_INTERFACE_MODE_XGMII); + st_param[1][0] = MAC_INTERNALLOOP_SERDES; + st_param[1][1] = 1; /*serdes must exist*/ + st_param[2][0] = MAC_INTERNALLOOP_PHY; /* only supporte phy node*/ + st_param[2][1] = ((!!(priv->ae_handle->phy_node)) && + (priv->ae_handle->phy_if != PHY_INTERFACE_MODE_XGMII)); + + if (eth_test->flags == ETH_TEST_FL_OFFLINE) { + set_bit(NIC_STATE_TESTING, &priv->state); + + if (if_running) + (void)dev_close(ndev); + + for (i = 0; i < SELF_TEST_TPYE_NUM; i++) { + if (!st_param[i][1]) + continue; /* NEXT testing */ + + data[test_index] = __lb_up(ndev, + (enum hnae_loop)st_param[i][0]); + if (!data[test_index]) { + data[test_index] = __lb_run_test( + ndev, (enum hnae_loop)st_param[i][0]); + (void)__lb_down(ndev); + } + + if (data[test_index]) + eth_test->flags |= ETH_TEST_FL_FAILED; + + test_index++; + } + + hns_nic_net_reset(priv->netdev); + + clear_bit(NIC_STATE_TESTING, &priv->state); + + if (if_running) + (void)dev_open(ndev); + } + /* Online tests aren't run; pass by default */ + + (void)msleep_interruptible(4 * 1000); +} + +/** + * hns_nic_get_drvinfo - get net driver info + * @dev: net device + * @drvinfo: driver info + */ +static void hns_nic_get_drvinfo(struct net_device *net_dev, + struct ethtool_drvinfo *drvinfo) +{ + struct hns_nic_priv *priv = netdev_priv(net_dev); + + assert(priv); + + strncpy(drvinfo->version, HNAE_DRIVER_VERSION, + sizeof(drvinfo->version)); + drvinfo->version[sizeof(drvinfo->version) - 1] = '\0'; + + strncpy(drvinfo->driver, HNAE_DRIVER_NAME, sizeof(drvinfo->driver)); + drvinfo->driver[sizeof(drvinfo->driver) - 1] = '\0'; + + strncpy(drvinfo->bus_info, priv->dev->bus->name, + sizeof(drvinfo->bus_info)); + drvinfo->bus_info[ETHTOOL_BUSINFO_LEN - 1] = '\0'; + + strncpy(drvinfo->fw_version, "N/A", ETHTOOL_FWVERS_LEN); + drvinfo->eedump_len = 0; +} + +/** + * hns_get_ringparam - get ring parameter + * @dev: net device + * @param: ethtool parameter + */ +void hns_get_ringparam(struct net_device *net_dev, + struct ethtool_ringparam *param) +{ + struct hns_nic_priv *priv = netdev_priv(net_dev); + struct hnae_ae_ops *ops; + struct hnae_queue *queue; + u32 uplimit = 0; + + queue = priv->ae_handle->qs[0]; + ops = priv->ae_handle->dev->ops; + + if (ops->get_ring_bdnum_limit) + ops->get_ring_bdnum_limit(queue, &uplimit); + + param->rx_max_pending = uplimit; + param->tx_max_pending = uplimit; + param->rx_pending = queue->rx_ring.desc_num; + param->tx_pending = queue->tx_ring.desc_num; +} + +/** + * hns_get_pauseparam - get pause parameter + * @dev: net device + * @param: pause parameter + */ +static void hns_get_pauseparam(struct net_device *net_dev, + struct ethtool_pauseparam *param) +{ + struct hns_nic_priv *priv = netdev_priv(net_dev); + struct hnae_ae_ops *ops; + + ops = priv->ae_handle->dev->ops; + + if (ops->get_pauseparam) + ops->get_pauseparam(priv->ae_handle, ¶m->autoneg, + ¶m->rx_pause, ¶m->tx_pause); +} + +/** + * hns_set_pauseparam - set pause parameter + * @dev: net device + * @param: pause parameter + * + * Return 0 on success, negative on failure + */ +static int hns_set_pauseparam(struct net_device *net_dev, + struct ethtool_pauseparam *param) +{ + struct hns_nic_priv *priv = netdev_priv(net_dev); + struct hnae_handle *h; + struct hnae_ae_ops *ops; + + assert(priv || priv->ae_handle); + + h = priv->ae_handle; + ops = h->dev->ops; + + if (!ops->set_pauseparam) + return -ESRCH; + + return ops->set_pauseparam(priv->ae_handle, param->autoneg, + param->rx_pause, param->tx_pause); +} + +/** + * hns_get_coalesce - get coalesce info. + * @dev: net device + * @ec: coalesce info. + * + * Return 0 on success, negative on failure. + */ +static int hns_get_coalesce(struct net_device *net_dev, + struct ethtool_coalesce *ec) +{ + struct hns_nic_priv *priv = netdev_priv(net_dev); + struct hnae_ae_ops *ops; + + ops = priv->ae_handle->dev->ops; + + ec->use_adaptive_rx_coalesce = 1; + ec->use_adaptive_tx_coalesce = 1; + + if ((!ops->get_coalesce_usecs) || + (!ops->get_rx_max_coalesced_frames)) + return -ESRCH; + + ops->get_coalesce_usecs(priv->ae_handle, + &ec->tx_coalesce_usecs, + &ec->rx_coalesce_usecs); + + ops->get_rx_max_coalesced_frames( + priv->ae_handle, + &ec->tx_max_coalesced_frames, + &ec->rx_max_coalesced_frames); + + return 0; +} + +/** + * hns_set_coalesce - set coalesce info. + * @dev: net device + * @ec: coalesce info. + * + * Return 0 on success, negative on failure. + */ +static int hns_set_coalesce(struct net_device *net_dev, + struct ethtool_coalesce *ec) +{ + struct hns_nic_priv *priv = netdev_priv(net_dev); + struct hnae_ae_ops *ops; + int ret; + + assert(priv || priv->ae_handle); + + ops = priv->ae_handle->dev->ops; + + if (ec->tx_coalesce_usecs != ec->rx_coalesce_usecs) + return -EINVAL; + + if (ec->rx_max_coalesced_frames != ec->tx_max_coalesced_frames) + return -EINVAL; + + if ((!ops->set_coalesce_usecs) || + (!ops->set_coalesce_frames)) + return -ESRCH; + + ops->set_coalesce_usecs(priv->ae_handle, + ec->rx_coalesce_usecs); + + ret = ops->set_coalesce_frames( + priv->ae_handle, + ec->rx_max_coalesced_frames); + + return ret; +} + +/** + * hns_get_channels - get channel info. + * @dev: net device + * @ch: channel info. + */ +void hns_get_channels(struct net_device *net_dev, struct ethtool_channels *ch) +{ + struct hns_nic_priv *priv = netdev_priv(net_dev); + + ch->max_rx = priv->ae_handle->q_num; + ch->max_tx = priv->ae_handle->q_num; + + ch->rx_count = priv->ae_handle->q_num; + ch->tx_count = priv->ae_handle->q_num; +} + +/** + * get_ethtool_stats - get detail statistics. + * @dev: net device + * @stats: statistics info. + * @data: statistics data. + */ +void hns_get_ethtool_stats(struct net_device *netdev, + struct ethtool_stats *stats, u64 *data) +{ + u64 *p = data; + struct hns_nic_priv *priv = netdev_priv(netdev); + struct hnae_handle *h = priv->ae_handle; + const struct rtnl_link_stats64 *net_stats; + struct rtnl_link_stats64 temp; + + if (!h->dev->ops->get_stats || !h->dev->ops->update_stats) { + netdev_err(netdev, "get_stats or update_stats is null!\n"); + return; + } + + h->dev->ops->update_stats(h, &netdev->stats); + + net_stats = dev_get_stats(netdev, &temp); + + /* get netdev statistics */ + p[0] = net_stats->rx_packets; + p[1] = net_stats->tx_packets; + p[2] = net_stats->rx_bytes; + p[3] = net_stats->tx_bytes; + p[4] = net_stats->rx_errors; + p[5] = net_stats->tx_errors; + p[6] = net_stats->rx_dropped; + p[7] = net_stats->tx_dropped; + p[8] = net_stats->multicast; + p[9] = net_stats->collisions; + p[10] = net_stats->rx_over_errors; + p[11] = net_stats->rx_crc_errors; + p[12] = net_stats->rx_frame_errors; + p[13] = net_stats->rx_fifo_errors; + p[14] = net_stats->rx_missed_errors; + p[15] = net_stats->tx_aborted_errors; + p[16] = net_stats->tx_carrier_errors; + p[17] = net_stats->tx_fifo_errors; + p[18] = net_stats->tx_heartbeat_errors; + p[19] = net_stats->rx_length_errors; + p[20] = net_stats->tx_window_errors; + p[21] = net_stats->rx_compressed; + p[22] = net_stats->tx_compressed; + + p[23] = netdev->rx_dropped.counter; + p[24] = netdev->tx_dropped.counter; + + p[25] = priv->tx_timeout_count; + + /* get driver statistics */ + h->dev->ops->get_stats(h, &p[26]); +} + +/** + * get_strings: Return a set of strings that describe the requested objects + * @dev: net device + * @stats: string set ID. + * @data: objects data. + */ +void hns_get_strings(struct net_device *netdev, u32 stringset, u8 *data) +{ + struct hns_nic_priv *priv = netdev_priv(netdev); + struct hnae_handle *h = priv->ae_handle; + char *buff = (char *)data; + + if (!h->dev->ops->get_strings) { + netdev_err(netdev, "h->dev->ops->get_strings is null!\n"); + return; + } + + if (stringset == ETH_SS_TEST) { + if (priv->ae_handle->phy_if != PHY_INTERFACE_MODE_XGMII) { + memcpy(buff, hns_nic_test_strs[MAC_INTERNALLOOP_MAC], + ETH_GSTRING_LEN); + buff += ETH_GSTRING_LEN; + } + memcpy(buff, hns_nic_test_strs[MAC_INTERNALLOOP_SERDES], + ETH_GSTRING_LEN); + buff += ETH_GSTRING_LEN; + if ((priv->phy) && (!priv->phy->is_c45)) + memcpy(buff, hns_nic_test_strs[MAC_INTERNALLOOP_PHY], + ETH_GSTRING_LEN); + + } else { + snprintf(buff, ETH_GSTRING_LEN, "rx_packets"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_packets"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_bytes"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_bytes"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_dropped"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_dropped"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "multicast"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "collisions"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_over_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_crc_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_frame_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_fifo_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_missed_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_aborted_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_carrier_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_fifo_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_heartbeat_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_length_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_window_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_compressed"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_compressed"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "netdev_rx_dropped"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "netdev_tx_dropped"); + buff = buff + ETH_GSTRING_LEN; + + snprintf(buff, ETH_GSTRING_LEN, "netdev_tx_timeout"); + buff = buff + ETH_GSTRING_LEN; + + h->dev->ops->get_strings(h, stringset, (u8 *)buff); + } +} + +/** + * nic_get_sset_count - get string set count witch returned by nic_get_strings. + * @dev: net device + * @stringset: string set index, 0: self test string; 1: statistics string. + * + * Return string set count. + */ +int hns_get_sset_count(struct net_device *netdev, int stringset) +{ + struct hns_nic_priv *priv = netdev_priv(netdev); + struct hnae_handle *h = priv->ae_handle; + struct hnae_ae_ops *ops = h->dev->ops; + + if (!ops->get_sset_count) { + netdev_err(netdev, "get_sset_count is null!\n"); + return -EOPNOTSUPP; + } + if (stringset == ETH_SS_TEST) { + u32 cnt = (sizeof(hns_nic_test_strs) / ETH_GSTRING_LEN); + + if (priv->ae_handle->phy_if == PHY_INTERFACE_MODE_XGMII) + cnt--; + + if ((!priv->phy) || (priv->phy->is_c45)) + cnt--; + + return cnt; + } else { + return (HNS_NET_STATS_CNT + ops->get_sset_count(h, stringset)); + } +} + +/** + * hns_phy_led_set - set phy LED status. + * @dev: net device + * @value: LED state. + * + * Return 0 on success, negative on failure. + */ +int hns_phy_led_set(struct net_device *netdev, int value) +{ + int retval; + struct hns_nic_priv *priv = netdev_priv(netdev); + struct phy_device *phy_dev = priv->phy; + + if (!phy_dev->bus) { + netdev_err(netdev, "phy_dev->bus is null!\n"); + return -EINVAL; + } + retval = mdiobus_write(phy_dev->bus, phy_dev->addr, + HNS_PHY_PAGE_REG, HNS_PHY_PAGE_LED); + retval = mdiobus_write(phy_dev->bus, phy_dev->addr, HNS_LED_FC_REG, + value); + retval = mdiobus_write(phy_dev->bus, phy_dev->addr, + HNS_PHY_PAGE_REG, HNS_PHY_PAGE_COPPER); + if (retval) { + netdev_err(netdev, "mdiobus_write fail !\n"); + return retval; + } + return 0; +} + +/** + * nic_set_phys_id - set phy identify LED. + * @dev: net device + * @state: LED state. + * + * Return 0 on success, negative on failure. + */ +int hns_set_phys_id(struct net_device *netdev, enum ethtool_phys_id_state state) +{ + struct hns_nic_priv *priv = netdev_priv(netdev); + struct hnae_handle *h = priv->ae_handle; + struct phy_device *phy_dev = priv->phy; + int ret; + + if (phy_dev) + switch (state) { + case ETHTOOL_ID_ACTIVE: + ret = mdiobus_write(phy_dev->bus, phy_dev->addr, + HNS_PHY_PAGE_REG, + HNS_PHY_PAGE_LED); + if (ret) + return ret; + + priv->phy_led_val = (u16)mdiobus_read(phy_dev->bus, + phy_dev->addr, + HNS_LED_FC_REG); + + ret = mdiobus_write(phy_dev->bus, phy_dev->addr, + HNS_PHY_PAGE_REG, + HNS_PHY_PAGE_COPPER); + if (ret) + return ret; + return 2; + case ETHTOOL_ID_ON: + ret = hns_phy_led_set(netdev, HNS_LED_FORCE_ON); + if (ret) + return ret; + break; + case ETHTOOL_ID_OFF: + ret = hns_phy_led_set(netdev, HNS_LED_FORCE_OFF); + if (ret) + return ret; + break; + case ETHTOOL_ID_INACTIVE: + ret = mdiobus_write(phy_dev->bus, phy_dev->addr, + HNS_PHY_PAGE_REG, + HNS_PHY_PAGE_LED); + if (ret) + return ret; + + ret = mdiobus_write(phy_dev->bus, phy_dev->addr, + HNS_LED_FC_REG, priv->phy_led_val); + if (ret) + return ret; + + ret = mdiobus_write(phy_dev->bus, phy_dev->addr, + HNS_PHY_PAGE_REG, + HNS_PHY_PAGE_COPPER); + if (ret) + return ret; + break; + default: + return -EINVAL; + } + else + switch (state) { + case ETHTOOL_ID_ACTIVE: + return h->dev->ops->set_led_id(h, HNAE_LED_ACTIVE); + case ETHTOOL_ID_ON: + return h->dev->ops->set_led_id(h, HNAE_LED_ON); + case ETHTOOL_ID_OFF: + return h->dev->ops->set_led_id(h, HNAE_LED_OFF); + case ETHTOOL_ID_INACTIVE: + return h->dev->ops->set_led_id(h, HNAE_LED_INACTIVE); + default: + return -EINVAL; + } + + return 0; +} + +/** + * hns_get_regs - get net device register + * @dev: net device + * @cmd: ethtool cmd + * @date: register data + */ +void hns_get_regs(struct net_device *net_dev, struct ethtool_regs *cmd, + void *data) +{ + struct hns_nic_priv *priv = netdev_priv(net_dev); + struct hnae_ae_ops *ops; + + assert(priv || priv->ae_handle); + + ops = priv->ae_handle->dev->ops; + + cmd->version = HNS_CHIP_VERSION; + if (!ops->get_regs) { + netdev_err(net_dev, "ops->get_regs is null!\n"); + return; + } + ops->get_regs(priv->ae_handle, data); +} + +/** + * nic_get_regs_len - get total register len. + * @dev: net device + * + * Return total register len. + */ +static int hns_get_regs_len(struct net_device *net_dev) +{ + u32 reg_num; + struct hns_nic_priv *priv = netdev_priv(net_dev); + struct hnae_ae_ops *ops; + + assert(priv || priv->ae_handle); + + ops = priv->ae_handle->dev->ops; + if (!ops->get_regs_len) { + netdev_err(net_dev, "ops->get_regs_len is null!\n"); + return -EOPNOTSUPP; + } + + reg_num = ops->get_regs_len(priv->ae_handle); + if (reg_num > 0) + return reg_num * sizeof(u32); + else + return reg_num; /* error code */ +} + +/** + * hns_nic_nway_reset - nway reset + * @dev: net device + * + * Return 0 on success, negative on failure + */ +static int hns_nic_nway_reset(struct net_device *netdev) +{ + int ret = 0; + struct hns_nic_priv *priv = netdev_priv(netdev); + struct phy_device *phy = priv->phy; + + if (netif_running(netdev)) { + if (phy) + ret = genphy_restart_aneg(phy); + } + + return ret; +} + +static struct ethtool_ops hns_ethtool_ops = { + .get_drvinfo = hns_nic_get_drvinfo, + .get_link = hns_nic_get_link, + .get_settings = hns_nic_get_settings, + .set_settings = hns_nic_set_settings, + .get_ringparam = hns_get_ringparam, + .get_pauseparam = hns_get_pauseparam, + .set_pauseparam = hns_set_pauseparam, + .get_coalesce = hns_get_coalesce, + .set_coalesce = hns_set_coalesce, + .get_channels = hns_get_channels, + .self_test = hns_nic_self_test, + .get_strings = hns_get_strings, + .get_sset_count = hns_get_sset_count, + .get_ethtool_stats = hns_get_ethtool_stats, + .set_phys_id = hns_set_phys_id, + .get_regs_len = hns_get_regs_len, + .get_regs = hns_get_regs, + .nway_reset = hns_nic_nway_reset, +}; + +void hns_ethtool_set_ops(struct net_device *ndev) +{ + ndev->ethtool_ops = &hns_ethtool_ops; +} diff --git a/drivers/net/ethernet/hisilicon/hns_mdio.c b/drivers/net/ethernet/hisilicon/hns_mdio.c new file mode 100644 index 000000000000..e4ec52ae61ff --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns_mdio.c @@ -0,0 +1,520 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * 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/errno.h> +#include <linux/etherdevice.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/netdevice.h> +#include <linux/of_address.h> +#include <linux/of.h> +#include <linux/of_mdio.h> +#include <linux/of_platform.h> +#include <linux/phy.h> +#include <linux/platform_device.h> +#include <linux/spinlock_types.h> + +#define MDIO_DRV_NAME "Hi-HNS_MDIO" +#define MDIO_BUS_NAME "Hisilicon MII Bus" +#define MDIO_DRV_VERSION "1.3.0" +#define MDIO_COPYRIGHT "Copyright(c) 2015 Huawei Corporation." +#define MDIO_DRV_STRING MDIO_BUS_NAME +#define MDIO_DEFAULT_DEVICE_DESCR MDIO_BUS_NAME + +#define MDIO_CTL_DEV_ADDR(x) (x & 0x1f) +#define MDIO_CTL_PORT_ADDR(x) ((x & 0x1f) << 5) + +#define MDIO_TIMEOUT 1000000 + +struct hns_mdio_device { + void *vbase; /* mdio reg base address */ + void *sys_vbase; +}; + +/* mdio reg */ +#define MDIO_COMMAND_REG 0x0 +#define MDIO_ADDR_REG 0x4 +#define MDIO_WDATA_REG 0x8 +#define MDIO_RDATA_REG 0xc +#define MDIO_STA_REG 0x10 + +/* cfg phy bit map */ +#define MDIO_CMD_DEVAD_M 0x1f +#define MDIO_CMD_DEVAD_S 0 +#define MDIO_CMD_PRTAD_M 0x1f +#define MDIO_CMD_PRTAD_S 5 +#define MDIO_CMD_OP_M 0x3 +#define MDIO_CMD_OP_S 10 +#define MDIO_CMD_ST_M 0x3 +#define MDIO_CMD_ST_S 12 +#define MDIO_CMD_START_B 14 + +#define MDIO_ADDR_DATA_M 0xffff +#define MDIO_ADDR_DATA_S 0 + +#define MDIO_WDATA_DATA_M 0xffff +#define MDIO_WDATA_DATA_S 0 + +#define MDIO_RDATA_DATA_M 0xffff +#define MDIO_RDATA_DATA_S 0 + +#define MDIO_STATE_STA_B 0 + +enum mdio_st_clause { + MDIO_ST_CLAUSE_45 = 0, + MDIO_ST_CLAUSE_22 +}; + +enum mdio_c22_op_seq { + MDIO_C22_WRITE = 1, + MDIO_C22_READ = 2 +}; + +enum mdio_c45_op_seq { + MDIO_C45_WRITE_ADDR = 0, + MDIO_C45_WRITE_DATA, + MDIO_C45_READ_INCREMENT, + MDIO_C45_READ +}; + +/* peri subctrl reg */ +#define MDIO_SC_CLK_EN 0x338 +#define MDIO_SC_CLK_DIS 0x33C +#define MDIO_SC_RESET_REQ 0xA38 +#define MDIO_SC_RESET_DREQ 0xA3C +#define MDIO_SC_CTRL 0x2010 +#define MDIO_SC_CLK_ST 0x531C +#define MDIO_SC_RESET_ST 0x5A1C + +static void mdio_write_reg(void *base, u32 reg, u32 value) +{ + u8 __iomem *reg_addr = (u8 __iomem *)base; + + writel_relaxed(value, reg_addr + reg); +} + +#define MDIO_WRITE_REG(a, reg, value) \ + mdio_write_reg((a)->vbase, (reg), (value)) + +static u32 mdio_read_reg(void *base, u32 reg) +{ + u8 __iomem *reg_addr = (u8 __iomem *)base; + + return readl_relaxed(reg_addr + reg); +} + +#define mdio_set_field(origin, mask, shift, val) \ + do { \ + (origin) &= (~((mask) << (shift))); \ + (origin) |= (((val) & (mask)) << (shift)); \ + } while (0) + +#define mdio_get_field(origin, mask, shift) (((origin) >> (shift)) & (mask)) + +static void mdio_set_reg_field(void *base, u32 reg, u32 mask, u32 shift, + u32 val) +{ + u32 origin = mdio_read_reg(base, reg); + + mdio_set_field(origin, mask, shift, val); + mdio_write_reg(base, reg, origin); +} + +#define MDIO_SET_REG_FIELD(dev, reg, mask, shift, val) \ + mdio_set_reg_field((dev)->vbase, (reg), (mask), (shift), (val)) + +static u32 mdio_get_reg_field(void *base, u32 reg, u32 mask, u32 shift) +{ + u32 origin; + + origin = mdio_read_reg(base, reg); + return mdio_get_field(origin, mask, shift); +} + +#define MDIO_GET_REG_FIELD(dev, reg, mask, shift) \ + mdio_get_reg_field((dev)->vbase, (reg), (mask), (shift)) + +#define MDIO_GET_REG_BIT(dev, reg, bit) \ + mdio_get_reg_field((dev)->vbase, (reg), 0x1ull, (bit)) + +#define MDIO_CHECK_SET_ST 1 +#define MDIO_CHECK_CLR_ST 0 + +static int mdio_sc_cfg_reg_write(struct hns_mdio_device *mdio_dev, + u32 cfg_reg, u32 set_val, + u32 st_reg, u32 st_msk, u8 check_st) +{ + u32 time_cnt; + u32 reg_value; + + mdio_write_reg((void *)mdio_dev->sys_vbase, cfg_reg, set_val); + + for (time_cnt = MDIO_TIMEOUT; time_cnt; time_cnt--) { + reg_value = mdio_read_reg((void *)mdio_dev->sys_vbase, st_reg); + reg_value &= st_msk; + if ((!!check_st) == (!!reg_value)) + break; + } + + if ((!!check_st) != (!!reg_value)) + return -EBUSY; + + return 0; +} + +static int hns_mdio_wait_ready(struct mii_bus *bus) +{ + struct hns_mdio_device *mdio_dev = bus->priv; + int i; + u32 cmd_reg_value = 1; + + /* waitting for MDIO_COMMAND_REG 's mdio_start==0 */ + /* after that can do read or write*/ + for (i = 0; cmd_reg_value; i++) { + cmd_reg_value = MDIO_GET_REG_BIT(mdio_dev, + MDIO_COMMAND_REG, + MDIO_CMD_START_B); + if (i == MDIO_TIMEOUT) + return -ETIMEDOUT; + } + + return 0; +} + +static void hns_mdio_cmd_write(struct hns_mdio_device *mdio_dev, + u8 is_c45, u8 op, u8 phy_id, u16 cmd) +{ + u32 cmd_reg_value; + u8 st = is_c45 ? MDIO_ST_CLAUSE_45 : MDIO_ST_CLAUSE_22; + + cmd_reg_value = st << MDIO_CMD_ST_S; + cmd_reg_value |= op << MDIO_CMD_OP_S; + cmd_reg_value |= + (phy_id & MDIO_CMD_PRTAD_M) << MDIO_CMD_PRTAD_S; + cmd_reg_value |= (cmd & MDIO_CMD_DEVAD_M) << MDIO_CMD_DEVAD_S; + cmd_reg_value |= 1 << MDIO_CMD_START_B; + + MDIO_WRITE_REG(mdio_dev, MDIO_COMMAND_REG, cmd_reg_value); +} + +/** + * hns_mdio_write - access phy register + * @bus: mdio bus + * @phy_id: phy id + * @regnum: register num + * @value: register value + * + * Return 0 on success, negative on failure + */ +static int hns_mdio_write(struct mii_bus *bus, + int phy_id, int regnum, u16 data) +{ + int ret; + struct hns_mdio_device *mdio_dev = (struct hns_mdio_device *)bus->priv; + u8 devad = ((regnum >> 16) & 0x1f); + u8 is_c45 = !!(regnum & MII_ADDR_C45); + u16 reg = (u16)(regnum & 0xffff); + u8 op; + u16 cmd_reg_cfg; + + dev_dbg(&bus->dev, "mdio write %s,base is %p\n", + bus->id, mdio_dev->vbase); + dev_dbg(&bus->dev, "phy id=%d, is_c45=%d, devad=%d, reg=%#x, write data=%d\n", + phy_id, is_c45, devad, reg, data); + + /* wait for ready */ + ret = hns_mdio_wait_ready(bus); + if (ret) { + dev_err(&bus->dev, "MDIO bus is busy\n"); + return ret; + } + + if (!is_c45) { + cmd_reg_cfg = reg; + op = MDIO_C22_WRITE; + } else { + /* config the cmd-reg to write addr*/ + MDIO_SET_REG_FIELD(mdio_dev, MDIO_ADDR_REG, MDIO_ADDR_DATA_M, + MDIO_ADDR_DATA_S, reg); + + hns_mdio_cmd_write(mdio_dev, is_c45, + MDIO_C45_WRITE_ADDR, phy_id, devad); + + /* check for read or write opt is finished */ + ret = hns_mdio_wait_ready(bus); + if (ret) { + dev_err(&bus->dev, "MDIO bus is busy\n"); + return ret; + } + + /* config the data needed writing */ + cmd_reg_cfg = devad; + op = MDIO_C45_WRITE_ADDR; + } + + MDIO_SET_REG_FIELD(mdio_dev, MDIO_WDATA_REG, MDIO_WDATA_DATA_M, + MDIO_WDATA_DATA_S, data); + + hns_mdio_cmd_write(mdio_dev, is_c45, op, phy_id, cmd_reg_cfg); + + return 0; +} + +/** + * hns_mdio_read - access phy register + * @bus: mdio bus + * @phy_id: phy id + * @regnum: register num + * @value: register value + * + * Return phy register value + */ +static int hns_mdio_read(struct mii_bus *bus, int phy_id, int regnum) +{ + int ret; + u16 reg_val = 0; + u8 devad = ((regnum >> 16) & 0x1f); + u8 is_c45 = !!(regnum & MII_ADDR_C45); + u16 reg = (u16)(regnum & 0xffff); + struct hns_mdio_device *mdio_dev = (struct hns_mdio_device *)bus->priv; + + dev_dbg(&bus->dev, "mdio read %s,base is %p\n", + bus->id, mdio_dev->vbase); + dev_dbg(&bus->dev, "phy id=%d, is_c45=%d, devad=%d, reg=%#x!\n", + phy_id, is_c45, devad, reg); + + /* Step 1: wait for ready */ + ret = hns_mdio_wait_ready(bus); + if (ret) { + dev_err(&bus->dev, "MDIO bus is busy\n"); + return ret; + } + + if (!is_c45) { + hns_mdio_cmd_write(mdio_dev, is_c45, + MDIO_C22_READ, phy_id, reg); + } else { + MDIO_SET_REG_FIELD(mdio_dev, MDIO_ADDR_REG, MDIO_ADDR_DATA_M, + MDIO_ADDR_DATA_S, reg); + + /* Step 2; config the cmd-reg to write addr*/ + hns_mdio_cmd_write(mdio_dev, is_c45, + MDIO_C45_WRITE_ADDR, phy_id, devad); + + /* Step 3: check for read or write opt is finished */ + ret = hns_mdio_wait_ready(bus); + if (ret) { + dev_err(&bus->dev, "MDIO bus is busy\n"); + return ret; + } + + hns_mdio_cmd_write(mdio_dev, is_c45, + MDIO_C45_WRITE_ADDR, phy_id, devad); + } + + /* Step 5: waitting for MDIO_COMMAND_REG 's mdio_start==0,*/ + /* check for read or write opt is finished */ + ret = hns_mdio_wait_ready(bus); + if (ret) { + dev_err(&bus->dev, "MDIO bus is busy\n"); + return ret; + } + + reg_val = MDIO_GET_REG_BIT(mdio_dev, MDIO_STA_REG, MDIO_STATE_STA_B); + if (reg_val) { + dev_err(&bus->dev, " ERROR! MDIO Read failed!\n"); + return -EBUSY; + } + + /* Step 6; get out data*/ + reg_val = (u16)MDIO_GET_REG_FIELD(mdio_dev, MDIO_RDATA_REG, + MDIO_RDATA_DATA_M, MDIO_RDATA_DATA_S); + + return reg_val; +} + +/** + * hns_mdio_reset - reset mdio bus + * @bus: mdio bus + * + * Return 0 on success, negative on failure + */ +static int hns_mdio_reset(struct mii_bus *bus) +{ + struct hns_mdio_device *mdio_dev = (struct hns_mdio_device *)bus->priv; + int ret; + + if (!mdio_dev->sys_vbase) { + dev_err(&bus->dev, "mdio sys ctl reg has not maped\n"); + return -ENODEV; + } + + /*1. reset req, and read reset st check*/ + ret = mdio_sc_cfg_reg_write(mdio_dev, MDIO_SC_RESET_REQ, 0x1, + MDIO_SC_RESET_ST, 0x1, + MDIO_CHECK_SET_ST); + if (ret) { + dev_err(&bus->dev, "MDIO reset fail\n"); + return ret; + } + + /*2. dis clk, and read clk st check*/ + ret = mdio_sc_cfg_reg_write(mdio_dev, MDIO_SC_CLK_DIS, + 0x1, MDIO_SC_CLK_ST, 0x1, + MDIO_CHECK_CLR_ST); + if (ret) { + dev_err(&bus->dev, "MDIO dis clk fail\n"); + return ret; + } + + /*3. reset dreq, and read reset st check*/ + ret = mdio_sc_cfg_reg_write(mdio_dev, MDIO_SC_RESET_DREQ, 0x1, + MDIO_SC_RESET_ST, 0x1, + MDIO_CHECK_CLR_ST); + if (ret) { + dev_err(&bus->dev, "MDIO dis clk fail\n"); + return ret; + } + + /*4. en clk, and read clk st check*/ + ret = mdio_sc_cfg_reg_write(mdio_dev, MDIO_SC_CLK_EN, + 0x1, MDIO_SC_CLK_ST, 0x1, + MDIO_CHECK_SET_ST); + if (ret) + dev_err(&bus->dev, "MDIO en clk fail\n"); + + return ret; +} + +/** + * hns_mdio_bus_name - get mdio bus name + * @name: mdio bus name + * @np: mdio device node pointer + */ +static void hns_mdio_bus_name(char *name, struct device_node *np) +{ + const u32 *addr; + u64 taddr = OF_BAD_ADDR; + + addr = of_get_address(np, 0, NULL, NULL); + if (addr) + taddr = of_translate_address(np, addr); + + snprintf(name, MII_BUS_ID_SIZE, "%s@%llx", np->name, + (unsigned long long)taddr); +} + +/** + * hns_mdio_probe - probe mdio device + * @pdev: mdio platform device + * + * Return 0 on success, negative on failure + */ +static int hns_mdio_probe(struct platform_device *pdev) +{ + struct device_node *np; + struct hns_mdio_device *mdio_dev; + struct mii_bus *new_bus; + struct resource *res; + int ret; + + if (!pdev) { + dev_err(NULL, "pdev is NULL!\r\n"); + return -ENODEV; + } + np = pdev->dev.of_node; + mdio_dev = devm_kzalloc(&pdev->dev, sizeof(*mdio_dev), GFP_KERNEL); + if (!mdio_dev) + return -ENOMEM; + + new_bus = devm_mdiobus_alloc(&pdev->dev); + if (!new_bus) { + dev_err(&pdev->dev, "mdiobus_alloc fail!\n"); + return -ENOMEM; + } + + new_bus->name = MDIO_BUS_NAME; + new_bus->read = hns_mdio_read; + new_bus->write = hns_mdio_write; + new_bus->reset = hns_mdio_reset; + new_bus->priv = mdio_dev; + hns_mdio_bus_name(new_bus->id, np); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mdio_dev->vbase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mdio_dev->vbase)) { + ret = PTR_ERR(mdio_dev->vbase); + return ret; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + mdio_dev->sys_vbase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mdio_dev->sys_vbase)) { + ret = PTR_ERR(mdio_dev->sys_vbase); + return ret; + } + + new_bus->irq = devm_kcalloc(&pdev->dev, PHY_MAX_ADDR, + sizeof(int), GFP_KERNEL); + if (!new_bus->irq) + return -ENOMEM; + + new_bus->parent = &pdev->dev; + platform_set_drvdata(pdev, new_bus); + + ret = of_mdiobus_register(new_bus, np); + if (ret) { + dev_err(&pdev->dev, "Cannot register as MDIO bus!\n"); + platform_set_drvdata(pdev, NULL); + return ret; + } + + return 0; +} + +/** + * hns_mdio_remove - remove mdio device + * @pdev: mdio platform device + * + * Return 0 on success, negative on failure + */ +static int hns_mdio_remove(struct platform_device *pdev) +{ + struct mii_bus *bus; + + bus = platform_get_drvdata(pdev); + + mdiobus_unregister(bus); + platform_set_drvdata(pdev, NULL); + return 0; +} + +static const struct of_device_id hns_mdio_match[] = { + {.compatible = "hisilicon,mdio"}, + {.compatible = "hisilicon,hns-mdio"}, + {} +}; + +static struct platform_driver hns_mdio_driver = { + .probe = hns_mdio_probe, + .remove = hns_mdio_remove, + .driver = { + .name = MDIO_DRV_NAME, + .of_match_table = hns_mdio_match, + }, +}; + +module_platform_driver(hns_mdio_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Huawei Tech. Co., Ltd."); +MODULE_DESCRIPTION("Hisilicon HNS MDIO driver"); +MODULE_ALIAS("platform:" MDIO_DRV_NAME); diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq.c b/drivers/net/ethernet/intel/i40e/i40e_adminq.c index 3e0d20037675..199275dbe624 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq.c +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq.c @@ -657,6 +657,9 @@ i40e_status i40e_shutdown_adminq(struct i40e_hw *hw) /* destroy the locks */ + if (hw->nvm_buff.va) + i40e_free_virt_mem(hw, &hw->nvm_buff); + return ret_code; } @@ -889,6 +892,10 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw, "AQTX: desc and buffer writeback:\n"); i40e_debug_aq(hw, I40E_DEBUG_AQ_COMMAND, (void *)desc, buff, buff_size); + /* save writeback aq if requested */ + if (details->wb_desc) + *details->wb_desc = *desc_on_ring; + /* update the error if time out occurred */ if ((!cmd_completed) && (!details->async && !details->postpone)) { @@ -1014,6 +1021,19 @@ clean_arq_element_out: i40e_release_nvm(hw); hw->aq.nvm_release_on_done = false; } + + switch (hw->nvmupd_state) { + case I40E_NVMUPD_STATE_INIT_WAIT: + hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; + break; + + case I40E_NVMUPD_STATE_WRITE_WAIT: + hw->nvmupd_state = I40E_NVMUPD_STATE_WRITING; + break; + + default: + break; + } } return ret_code; diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq.h b/drivers/net/ethernet/intel/i40e/i40e_adminq.h index 28e519a50de4..b67b34c35533 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq.h +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq.h @@ -69,6 +69,7 @@ struct i40e_asq_cmd_details { u16 flags_dis; bool async; bool postpone; + struct i40e_aq_desc *wb_desc; }; #define I40E_ADMINQ_DETAILS(R, i) \ diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 851c1a159be8..530d8b6739f9 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -39,7 +39,7 @@ static const char i40e_driver_string[] = #define DRV_VERSION_MAJOR 1 #define DRV_VERSION_MINOR 3 -#define DRV_VERSION_BUILD 9 +#define DRV_VERSION_BUILD 21 #define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \ __stringify(DRV_VERSION_MINOR) "." \ __stringify(DRV_VERSION_BUILD) DRV_KERN diff --git a/drivers/net/ethernet/intel/i40e/i40e_nvm.c b/drivers/net/ethernet/intel/i40e/i40e_nvm.c index 9b83abc0e774..d0288ad4e9b2 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_nvm.c +++ b/drivers/net/ethernet/intel/i40e/i40e_nvm.c @@ -418,6 +418,10 @@ static i40e_status i40e_write_nvm_aq(struct i40e_hw *hw, u8 module_pointer, bool last_command) { i40e_status ret_code = I40E_ERR_NVM; + struct i40e_asq_cmd_details cmd_details; + + memset(&cmd_details, 0, sizeof(cmd_details)); + cmd_details.wb_desc = &hw->nvm_wb_desc; /* Here we are checking the SR limit only for the flat memory model. * We cannot do it for the module-based model, as we did not acquire @@ -443,7 +447,7 @@ static i40e_status i40e_write_nvm_aq(struct i40e_hw *hw, u8 module_pointer, ret_code = i40e_aq_update_nvm(hw, module_pointer, 2 * offset, /*bytes*/ 2 * words, /*bytes*/ - data, last_command, NULL); + data, last_command, &cmd_details); return ret_code; } @@ -592,25 +596,31 @@ i40e_validate_nvm_checksum_exit: static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - u8 *bytes, int *errno); + u8 *bytes, int *perrno); static i40e_status i40e_nvmupd_state_reading(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - u8 *bytes, int *errno); + u8 *bytes, int *perrno); static i40e_status i40e_nvmupd_state_writing(struct i40e_hw *hw, struct i40e_nvm_access *cmd, u8 *bytes, int *errno); static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - int *errno); + int *perrno); static i40e_status i40e_nvmupd_nvm_erase(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - int *errno); + int *perrno); static i40e_status i40e_nvmupd_nvm_write(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - u8 *bytes, int *errno); + u8 *bytes, int *perrno); static i40e_status i40e_nvmupd_nvm_read(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - u8 *bytes, int *errno); + u8 *bytes, int *perrno); +static i40e_status i40e_nvmupd_exec_aq(struct i40e_hw *hw, + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno); +static i40e_status i40e_nvmupd_get_aq_result(struct i40e_hw *hw, + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno); static inline u8 i40e_nvmupd_get_module(u32 val) { return (u8)(val & I40E_NVM_MOD_PNT_MASK); @@ -634,6 +644,9 @@ static char *i40e_nvm_update_state_str[] = { "I40E_NVMUPD_CSUM_CON", "I40E_NVMUPD_CSUM_SA", "I40E_NVMUPD_CSUM_LCB", + "I40E_NVMUPD_STATUS", + "I40E_NVMUPD_EXEC_AQ", + "I40E_NVMUPD_GET_AQ_RESULT", }; /** @@ -641,30 +654,60 @@ static char *i40e_nvm_update_state_str[] = { * @hw: pointer to hardware structure * @cmd: pointer to nvm update command * @bytes: pointer to the data buffer - * @errno: pointer to return error code + * @perrno: pointer to return error code * * Dispatches command depending on what update state is current **/ i40e_status i40e_nvmupd_command(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - u8 *bytes, int *errno) + u8 *bytes, int *perrno) { i40e_status status; + enum i40e_nvmupd_cmd upd_cmd; /* assume success */ - *errno = 0; + *perrno = 0; + + /* early check for status command and debug msgs */ + upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno); + + i40e_debug(hw, I40E_DEBUG_NVM, "%s state %d nvm_release_on_hold %d\n", + i40e_nvm_update_state_str[upd_cmd], + hw->nvmupd_state, + hw->aq.nvm_release_on_done); + + if (upd_cmd == I40E_NVMUPD_INVALID) { + *perrno = -EFAULT; + i40e_debug(hw, I40E_DEBUG_NVM, + "i40e_nvmupd_validate_command returns %d errno %d\n", + upd_cmd, *perrno); + } + + /* a status request returns immediately rather than + * going into the state machine + */ + if (upd_cmd == I40E_NVMUPD_STATUS) { + bytes[0] = hw->nvmupd_state; + return 0; + } switch (hw->nvmupd_state) { case I40E_NVMUPD_STATE_INIT: - status = i40e_nvmupd_state_init(hw, cmd, bytes, errno); + status = i40e_nvmupd_state_init(hw, cmd, bytes, perrno); break; case I40E_NVMUPD_STATE_READING: - status = i40e_nvmupd_state_reading(hw, cmd, bytes, errno); + status = i40e_nvmupd_state_reading(hw, cmd, bytes, perrno); break; case I40E_NVMUPD_STATE_WRITING: - status = i40e_nvmupd_state_writing(hw, cmd, bytes, errno); + status = i40e_nvmupd_state_writing(hw, cmd, bytes, perrno); + break; + + case I40E_NVMUPD_STATE_INIT_WAIT: + case I40E_NVMUPD_STATE_WRITE_WAIT: + status = I40E_ERR_NOT_READY; + *perrno = -EBUSY; break; default: @@ -672,7 +715,7 @@ i40e_status i40e_nvmupd_command(struct i40e_hw *hw, i40e_debug(hw, I40E_DEBUG_NVM, "NVMUPD: no such state %d\n", hw->nvmupd_state); status = I40E_NOT_SUPPORTED; - *errno = -ESRCH; + *perrno = -ESRCH; break; } return status; @@ -683,28 +726,28 @@ i40e_status i40e_nvmupd_command(struct i40e_hw *hw, * @hw: pointer to hardware structure * @cmd: pointer to nvm update command buffer * @bytes: pointer to the data buffer - * @errno: pointer to return error code + * @perrno: pointer to return error code * * Process legitimate commands of the Init state and conditionally set next * state. Reject all other commands. **/ static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - u8 *bytes, int *errno) + u8 *bytes, int *perrno) { i40e_status status = 0; enum i40e_nvmupd_cmd upd_cmd; - upd_cmd = i40e_nvmupd_validate_command(hw, cmd, errno); + upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno); switch (upd_cmd) { case I40E_NVMUPD_READ_SA: status = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); if (status) { - *errno = i40e_aq_rc_to_posix(status, + *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); } else { - status = i40e_nvmupd_nvm_read(hw, cmd, bytes, errno); + status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno); i40e_release_nvm(hw); } break; @@ -712,10 +755,10 @@ static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw, case I40E_NVMUPD_READ_SNT: status = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); if (status) { - *errno = i40e_aq_rc_to_posix(status, + *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); } else { - status = i40e_nvmupd_nvm_read(hw, cmd, bytes, errno); + status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno); if (status) i40e_release_nvm(hw); else @@ -726,70 +769,83 @@ static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw, case I40E_NVMUPD_WRITE_ERA: status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE); if (status) { - *errno = i40e_aq_rc_to_posix(status, + *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); } else { - status = i40e_nvmupd_nvm_erase(hw, cmd, errno); - if (status) + status = i40e_nvmupd_nvm_erase(hw, cmd, perrno); + if (status) { i40e_release_nvm(hw); - else + } else { hw->aq.nvm_release_on_done = true; + hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; + } } break; case I40E_NVMUPD_WRITE_SA: status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE); if (status) { - *errno = i40e_aq_rc_to_posix(status, + *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); } else { - status = i40e_nvmupd_nvm_write(hw, cmd, bytes, errno); - if (status) + status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno); + if (status) { i40e_release_nvm(hw); - else + } else { hw->aq.nvm_release_on_done = true; + hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; + } } break; case I40E_NVMUPD_WRITE_SNT: status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE); if (status) { - *errno = i40e_aq_rc_to_posix(status, + *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); } else { - status = i40e_nvmupd_nvm_write(hw, cmd, bytes, errno); + status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno); if (status) i40e_release_nvm(hw); else - hw->nvmupd_state = I40E_NVMUPD_STATE_WRITING; + hw->nvmupd_state = I40E_NVMUPD_STATE_WRITE_WAIT; } break; case I40E_NVMUPD_CSUM_SA: status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE); if (status) { - *errno = i40e_aq_rc_to_posix(status, + *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); } else { status = i40e_update_nvm_checksum(hw); if (status) { - *errno = hw->aq.asq_last_status ? + *perrno = hw->aq.asq_last_status ? i40e_aq_rc_to_posix(status, hw->aq.asq_last_status) : -EIO; i40e_release_nvm(hw); } else { hw->aq.nvm_release_on_done = true; + hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; } } break; + case I40E_NVMUPD_EXEC_AQ: + status = i40e_nvmupd_exec_aq(hw, cmd, bytes, perrno); + break; + + case I40E_NVMUPD_GET_AQ_RESULT: + status = i40e_nvmupd_get_aq_result(hw, cmd, bytes, perrno); + break; + default: i40e_debug(hw, I40E_DEBUG_NVM, "NVMUPD: bad cmd %s in init state\n", i40e_nvm_update_state_str[upd_cmd]); status = I40E_ERR_NVM; - *errno = -ESRCH; + *perrno = -ESRCH; break; } return status; @@ -800,28 +856,28 @@ static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw, * @hw: pointer to hardware structure * @cmd: pointer to nvm update command buffer * @bytes: pointer to the data buffer - * @errno: pointer to return error code + * @perrno: pointer to return error code * * NVM ownership is already held. Process legitimate commands and set any * change in state; reject all other commands. **/ static i40e_status i40e_nvmupd_state_reading(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - u8 *bytes, int *errno) + u8 *bytes, int *perrno) { - i40e_status status; + i40e_status status = 0; enum i40e_nvmupd_cmd upd_cmd; - upd_cmd = i40e_nvmupd_validate_command(hw, cmd, errno); + upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno); switch (upd_cmd) { case I40E_NVMUPD_READ_SA: case I40E_NVMUPD_READ_CON: - status = i40e_nvmupd_nvm_read(hw, cmd, bytes, errno); + status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno); break; case I40E_NVMUPD_READ_LCB: - status = i40e_nvmupd_nvm_read(hw, cmd, bytes, errno); + status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno); i40e_release_nvm(hw); hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; break; @@ -831,7 +887,7 @@ static i40e_status i40e_nvmupd_state_reading(struct i40e_hw *hw, "NVMUPD: bad cmd %s in reading state.\n", i40e_nvm_update_state_str[upd_cmd]); status = I40E_NOT_SUPPORTED; - *errno = -ESRCH; + *perrno = -ESRCH; break; } return status; @@ -842,55 +898,68 @@ static i40e_status i40e_nvmupd_state_reading(struct i40e_hw *hw, * @hw: pointer to hardware structure * @cmd: pointer to nvm update command buffer * @bytes: pointer to the data buffer - * @errno: pointer to return error code + * @perrno: pointer to return error code * * NVM ownership is already held. Process legitimate commands and set any * change in state; reject all other commands **/ static i40e_status i40e_nvmupd_state_writing(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - u8 *bytes, int *errno) + u8 *bytes, int *perrno) { - i40e_status status; + i40e_status status = 0; enum i40e_nvmupd_cmd upd_cmd; bool retry_attempt = false; - upd_cmd = i40e_nvmupd_validate_command(hw, cmd, errno); + upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno); retry: switch (upd_cmd) { case I40E_NVMUPD_WRITE_CON: - status = i40e_nvmupd_nvm_write(hw, cmd, bytes, errno); + status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno); + if (!status) + hw->nvmupd_state = I40E_NVMUPD_STATE_WRITE_WAIT; break; case I40E_NVMUPD_WRITE_LCB: - status = i40e_nvmupd_nvm_write(hw, cmd, bytes, errno); - if (!status) + status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno); + if (status) { + *perrno = hw->aq.asq_last_status ? + i40e_aq_rc_to_posix(status, + hw->aq.asq_last_status) : + -EIO; + hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; + } else { hw->aq.nvm_release_on_done = true; - hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; + hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; + } break; case I40E_NVMUPD_CSUM_CON: status = i40e_update_nvm_checksum(hw); if (status) { - *errno = hw->aq.asq_last_status ? + *perrno = hw->aq.asq_last_status ? i40e_aq_rc_to_posix(status, hw->aq.asq_last_status) : -EIO; hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; + } else { + hw->nvmupd_state = I40E_NVMUPD_STATE_WRITE_WAIT; } break; case I40E_NVMUPD_CSUM_LCB: status = i40e_update_nvm_checksum(hw); - if (status) - *errno = hw->aq.asq_last_status ? + if (status) { + *perrno = hw->aq.asq_last_status ? i40e_aq_rc_to_posix(status, hw->aq.asq_last_status) : -EIO; - else + hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; + } else { hw->aq.nvm_release_on_done = true; - hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; + hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; + } break; default: @@ -898,7 +967,7 @@ retry: "NVMUPD: bad cmd %s in writing state.\n", i40e_nvm_update_state_str[upd_cmd]); status = I40E_NOT_SUPPORTED; - *errno = -ESRCH; + *perrno = -ESRCH; break; } @@ -941,21 +1010,22 @@ retry: * i40e_nvmupd_validate_command - Validate given command * @hw: pointer to hardware structure * @cmd: pointer to nvm update command buffer - * @errno: pointer to return error code + * @perrno: pointer to return error code * * Return one of the valid command types or I40E_NVMUPD_INVALID **/ static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - int *errno) + int *perrno) { enum i40e_nvmupd_cmd upd_cmd; - u8 transaction; + u8 module, transaction; /* anything that doesn't match a recognized case is an error */ upd_cmd = I40E_NVMUPD_INVALID; transaction = i40e_nvmupd_get_transaction(cmd->config); + module = i40e_nvmupd_get_module(cmd->config); /* limits on data size */ if ((cmd->data_size < 1) || @@ -963,7 +1033,7 @@ static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw, i40e_debug(hw, I40E_DEBUG_NVM, "i40e_nvmupd_validate_command data_size %d\n", cmd->data_size); - *errno = -EFAULT; + *perrno = -EFAULT; return I40E_NVMUPD_INVALID; } @@ -982,6 +1052,12 @@ static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw, case I40E_NVM_SA: upd_cmd = I40E_NVMUPD_READ_SA; break; + case I40E_NVM_EXEC: + if (module == 0xf) + upd_cmd = I40E_NVMUPD_STATUS; + else if (module == 0) + upd_cmd = I40E_NVMUPD_GET_AQ_RESULT; + break; } break; @@ -1011,21 +1087,155 @@ static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw, case (I40E_NVM_CSUM|I40E_NVM_LCB): upd_cmd = I40E_NVMUPD_CSUM_LCB; break; + case I40E_NVM_EXEC: + if (module == 0) + upd_cmd = I40E_NVMUPD_EXEC_AQ; + break; } break; } - i40e_debug(hw, I40E_DEBUG_NVM, "%s state %d nvm_release_on_hold %d\n", - i40e_nvm_update_state_str[upd_cmd], - hw->nvmupd_state, - hw->aq.nvm_release_on_done); - if (upd_cmd == I40E_NVMUPD_INVALID) { - *errno = -EFAULT; + return upd_cmd; +} + +/** + * i40e_nvmupd_exec_aq - Run an AQ command + * @hw: pointer to hardware structure + * @cmd: pointer to nvm update command buffer + * @bytes: pointer to the data buffer + * @perrno: pointer to return error code + * + * cmd structure contains identifiers and data buffer + **/ +static i40e_status i40e_nvmupd_exec_aq(struct i40e_hw *hw, + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno) +{ + struct i40e_asq_cmd_details cmd_details; + i40e_status status; + struct i40e_aq_desc *aq_desc; + u32 buff_size = 0; + u8 *buff = NULL; + u32 aq_desc_len; + u32 aq_data_len; + + i40e_debug(hw, I40E_DEBUG_NVM, "NVMUPD: %s\n", __func__); + memset(&cmd_details, 0, sizeof(cmd_details)); + cmd_details.wb_desc = &hw->nvm_wb_desc; + + aq_desc_len = sizeof(struct i40e_aq_desc); + memset(&hw->nvm_wb_desc, 0, aq_desc_len); + + /* get the aq descriptor */ + if (cmd->data_size < aq_desc_len) { i40e_debug(hw, I40E_DEBUG_NVM, - "i40e_nvmupd_validate_command returns %d errno %d\n", - upd_cmd, *errno); + "NVMUPD: not enough aq desc bytes for exec, size %d < %d\n", + cmd->data_size, aq_desc_len); + *perrno = -EINVAL; + return I40E_ERR_PARAM; } - return upd_cmd; + aq_desc = (struct i40e_aq_desc *)bytes; + + /* if data buffer needed, make sure it's ready */ + aq_data_len = cmd->data_size - aq_desc_len; + buff_size = max_t(u32, aq_data_len, le16_to_cpu(aq_desc->datalen)); + if (buff_size) { + if (!hw->nvm_buff.va) { + status = i40e_allocate_virt_mem(hw, &hw->nvm_buff, + hw->aq.asq_buf_size); + if (status) + i40e_debug(hw, I40E_DEBUG_NVM, + "NVMUPD: i40e_allocate_virt_mem for exec buff failed, %d\n", + status); + } + + if (hw->nvm_buff.va) { + buff = hw->nvm_buff.va; + memcpy(buff, &bytes[aq_desc_len], aq_data_len); + } + } + + /* and away we go! */ + status = i40e_asq_send_command(hw, aq_desc, buff, + buff_size, &cmd_details); + if (status) { + i40e_debug(hw, I40E_DEBUG_NVM, + "i40e_nvmupd_exec_aq err %s aq_err %s\n", + i40e_stat_str(hw, status), + i40e_aq_str(hw, hw->aq.asq_last_status)); + *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); + } + + return status; +} + +/** + * i40e_nvmupd_get_aq_result - Get the results from the previous exec_aq + * @hw: pointer to hardware structure + * @cmd: pointer to nvm update command buffer + * @bytes: pointer to the data buffer + * @perrno: pointer to return error code + * + * cmd structure contains identifiers and data buffer + **/ +static i40e_status i40e_nvmupd_get_aq_result(struct i40e_hw *hw, + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno) +{ + u32 aq_total_len; + u32 aq_desc_len; + int remainder; + u8 *buff; + + i40e_debug(hw, I40E_DEBUG_NVM, "NVMUPD: %s\n", __func__); + + aq_desc_len = sizeof(struct i40e_aq_desc); + aq_total_len = aq_desc_len + le16_to_cpu(hw->nvm_wb_desc.datalen); + + /* check offset range */ + if (cmd->offset > aq_total_len) { + i40e_debug(hw, I40E_DEBUG_NVM, "%s: offset too big %d > %d\n", + __func__, cmd->offset, aq_total_len); + *perrno = -EINVAL; + return I40E_ERR_PARAM; + } + + /* check copylength range */ + if (cmd->data_size > (aq_total_len - cmd->offset)) { + int new_len = aq_total_len - cmd->offset; + + i40e_debug(hw, I40E_DEBUG_NVM, "%s: copy length %d too big, trimming to %d\n", + __func__, cmd->data_size, new_len); + cmd->data_size = new_len; + } + + remainder = cmd->data_size; + if (cmd->offset < aq_desc_len) { + u32 len = aq_desc_len - cmd->offset; + + len = min(len, cmd->data_size); + i40e_debug(hw, I40E_DEBUG_NVM, "%s: aq_desc bytes %d to %d\n", + __func__, cmd->offset, cmd->offset + len); + + buff = ((u8 *)&hw->nvm_wb_desc) + cmd->offset; + memcpy(bytes, buff, len); + + bytes += len; + remainder -= len; + buff = hw->nvm_buff.va; + } else { + buff = hw->nvm_buff.va + (cmd->offset - aq_desc_len); + } + + if (remainder > 0) { + int start_byte = buff - (u8 *)hw->nvm_buff.va; + + i40e_debug(hw, I40E_DEBUG_NVM, "%s: databuf bytes %d to %d\n", + __func__, start_byte, start_byte + remainder); + memcpy(bytes, buff, remainder); + } + + return 0; } /** @@ -1033,14 +1243,15 @@ static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw, * @hw: pointer to hardware structure * @cmd: pointer to nvm update command buffer * @bytes: pointer to the data buffer - * @errno: pointer to return error code + * @perrno: pointer to return error code * * cmd structure contains identifiers and data buffer **/ static i40e_status i40e_nvmupd_nvm_read(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - u8 *bytes, int *errno) + u8 *bytes, int *perrno) { + struct i40e_asq_cmd_details cmd_details; i40e_status status; u8 module, transaction; bool last; @@ -1049,8 +1260,11 @@ static i40e_status i40e_nvmupd_nvm_read(struct i40e_hw *hw, module = i40e_nvmupd_get_module(cmd->config); last = (transaction == I40E_NVM_LCB) || (transaction == I40E_NVM_SA); + memset(&cmd_details, 0, sizeof(cmd_details)); + cmd_details.wb_desc = &hw->nvm_wb_desc; + status = i40e_aq_read_nvm(hw, module, cmd->offset, (u16)cmd->data_size, - bytes, last, NULL); + bytes, last, &cmd_details); if (status) { i40e_debug(hw, I40E_DEBUG_NVM, "i40e_nvmupd_nvm_read mod 0x%x off 0x%x len 0x%x\n", @@ -1058,7 +1272,7 @@ static i40e_status i40e_nvmupd_nvm_read(struct i40e_hw *hw, i40e_debug(hw, I40E_DEBUG_NVM, "i40e_nvmupd_nvm_read status %d aq %d\n", status, hw->aq.asq_last_status); - *errno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); + *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); } return status; @@ -1068,23 +1282,28 @@ static i40e_status i40e_nvmupd_nvm_read(struct i40e_hw *hw, * i40e_nvmupd_nvm_erase - Erase an NVM module * @hw: pointer to hardware structure * @cmd: pointer to nvm update command buffer - * @errno: pointer to return error code + * @perrno: pointer to return error code * * module, offset, data_size and data are in cmd structure **/ static i40e_status i40e_nvmupd_nvm_erase(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - int *errno) + int *perrno) { i40e_status status = 0; + struct i40e_asq_cmd_details cmd_details; u8 module, transaction; bool last; transaction = i40e_nvmupd_get_transaction(cmd->config); module = i40e_nvmupd_get_module(cmd->config); last = (transaction & I40E_NVM_LCB); + + memset(&cmd_details, 0, sizeof(cmd_details)); + cmd_details.wb_desc = &hw->nvm_wb_desc; + status = i40e_aq_erase_nvm(hw, module, cmd->offset, (u16)cmd->data_size, - last, NULL); + last, &cmd_details); if (status) { i40e_debug(hw, I40E_DEBUG_NVM, "i40e_nvmupd_nvm_erase mod 0x%x off 0x%x len 0x%x\n", @@ -1092,7 +1311,7 @@ static i40e_status i40e_nvmupd_nvm_erase(struct i40e_hw *hw, i40e_debug(hw, I40E_DEBUG_NVM, "i40e_nvmupd_nvm_erase status %d aq %d\n", status, hw->aq.asq_last_status); - *errno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); + *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); } return status; @@ -1103,15 +1322,16 @@ static i40e_status i40e_nvmupd_nvm_erase(struct i40e_hw *hw, * @hw: pointer to hardware structure * @cmd: pointer to nvm update command buffer * @bytes: pointer to the data buffer - * @errno: pointer to return error code + * @perrno: pointer to return error code * * module, offset, data_size and data are in cmd structure **/ static i40e_status i40e_nvmupd_nvm_write(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - u8 *bytes, int *errno) + u8 *bytes, int *perrno) { i40e_status status = 0; + struct i40e_asq_cmd_details cmd_details; u8 module, transaction; bool last; @@ -1119,8 +1339,12 @@ static i40e_status i40e_nvmupd_nvm_write(struct i40e_hw *hw, module = i40e_nvmupd_get_module(cmd->config); last = (transaction & I40E_NVM_LCB); + memset(&cmd_details, 0, sizeof(cmd_details)); + cmd_details.wb_desc = &hw->nvm_wb_desc; + status = i40e_aq_update_nvm(hw, module, cmd->offset, - (u16)cmd->data_size, bytes, last, NULL); + (u16)cmd->data_size, bytes, last, + &cmd_details); if (status) { i40e_debug(hw, I40E_DEBUG_NVM, "i40e_nvmupd_nvm_write mod 0x%x off 0x%x len 0x%x\n", @@ -1128,7 +1352,7 @@ static i40e_status i40e_nvmupd_nvm_write(struct i40e_hw *hw, i40e_debug(hw, I40E_DEBUG_NVM, "i40e_nvmupd_nvm_write status %d aq %d\n", status, hw->aq.asq_last_status); - *errno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); + *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); } return status; diff --git a/drivers/net/ethernet/intel/i40e/i40e_type.h b/drivers/net/ethernet/intel/i40e/i40e_type.h index 4842239ee777..af4829065af6 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_type.h +++ b/drivers/net/ethernet/intel/i40e/i40e_type.h @@ -305,12 +305,17 @@ enum i40e_nvmupd_cmd { I40E_NVMUPD_CSUM_CON, I40E_NVMUPD_CSUM_SA, I40E_NVMUPD_CSUM_LCB, + I40E_NVMUPD_STATUS, + I40E_NVMUPD_EXEC_AQ, + I40E_NVMUPD_GET_AQ_RESULT, }; enum i40e_nvmupd_state { I40E_NVMUPD_STATE_INIT, I40E_NVMUPD_STATE_READING, - I40E_NVMUPD_STATE_WRITING + I40E_NVMUPD_STATE_WRITING, + I40E_NVMUPD_STATE_INIT_WAIT, + I40E_NVMUPD_STATE_WRITE_WAIT, }; /* nvm_access definition and its masks/shifts need to be accessible to @@ -329,6 +334,7 @@ enum i40e_nvmupd_state { #define I40E_NVM_SA (I40E_NVM_SNT | I40E_NVM_LCB) #define I40E_NVM_ERA 0x4 #define I40E_NVM_CSUM 0x8 +#define I40E_NVM_EXEC 0xf #define I40E_NVM_ADAPT_SHIFT 16 #define I40E_NVM_ADAPT_MASK (0xffff << I40E_NVM_ADAPT_SHIFT) @@ -492,6 +498,8 @@ struct i40e_hw { /* state of nvm update process */ enum i40e_nvmupd_state nvmupd_state; + struct i40e_aq_desc nvm_wb_desc; + struct i40e_virt_mem nvm_buff; /* HMC info */ struct i40e_hmc_info hmc; /* HMC info struct */ diff --git a/drivers/net/ethernet/intel/i40evf/i40e_adminq.c b/drivers/net/ethernet/intel/i40evf/i40e_adminq.c index f08450b90774..0940db731382 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_adminq.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_adminq.c @@ -596,6 +596,9 @@ i40e_status i40evf_shutdown_adminq(struct i40e_hw *hw) /* destroy the locks */ + if (hw->nvm_buff.va) + i40e_free_virt_mem(hw, &hw->nvm_buff); + return ret_code; } @@ -830,6 +833,10 @@ i40e_status i40evf_asq_send_command(struct i40e_hw *hw, i40evf_debug_aq(hw, I40E_DEBUG_AQ_COMMAND, (void *)desc, buff, buff_size); + /* save writeback aq if requested */ + if (details->wb_desc) + *details->wb_desc = *desc_on_ring; + /* update the error if time out occurred */ if ((!cmd_completed) && (!details->async && !details->postpone)) { diff --git a/drivers/net/ethernet/intel/i40evf/i40e_adminq.h b/drivers/net/ethernet/intel/i40evf/i40e_adminq.h index ef43d68f67b3..547b79b81ffe 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_adminq.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_adminq.h @@ -69,6 +69,7 @@ struct i40e_asq_cmd_details { u16 flags_dis; bool async; bool postpone; + struct i40e_aq_desc *wb_desc; }; #define I40E_ADMINQ_DETAILS(R, i) \ diff --git a/drivers/net/ethernet/intel/i40evf/i40e_type.h b/drivers/net/ethernet/intel/i40evf/i40e_type.h index 24a2693869a1..ed7166693e5f 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_type.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_type.h @@ -304,12 +304,17 @@ enum i40e_nvmupd_cmd { I40E_NVMUPD_CSUM_CON, I40E_NVMUPD_CSUM_SA, I40E_NVMUPD_CSUM_LCB, + I40E_NVMUPD_STATUS, + I40E_NVMUPD_EXEC_AQ, + I40E_NVMUPD_GET_AQ_RESULT, }; enum i40e_nvmupd_state { I40E_NVMUPD_STATE_INIT, I40E_NVMUPD_STATE_READING, - I40E_NVMUPD_STATE_WRITING + I40E_NVMUPD_STATE_WRITING, + I40E_NVMUPD_STATE_INIT_WAIT, + I40E_NVMUPD_STATE_WRITE_WAIT, }; /* nvm_access definition and its masks/shifts need to be accessible to @@ -328,6 +333,7 @@ enum i40e_nvmupd_state { #define I40E_NVM_SA (I40E_NVM_SNT | I40E_NVM_LCB) #define I40E_NVM_ERA 0x4 #define I40E_NVM_CSUM 0x8 +#define I40E_NVM_EXEC 0xf #define I40E_NVM_ADAPT_SHIFT 16 #define I40E_NVM_ADAPT_MASK (0xffff << I40E_NVM_ADAPT_SHIFT) @@ -486,6 +492,8 @@ struct i40e_hw { /* state of nvm update process */ enum i40e_nvmupd_state nvmupd_state; + struct i40e_aq_desc nvm_wb_desc; + struct i40e_virt_mem nvm_buff; /* HMC info */ struct i40e_hmc_info hmc; /* HMC info struct */ diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index e85849b9ff98..5fc820412fca 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -34,7 +34,7 @@ char i40evf_driver_name[] = "i40evf"; static const char i40evf_driver_string[] = "Intel(R) XL710/X710 Virtual Function Network Driver"; -#define DRV_VERSION "1.3.5" +#define DRV_VERSION "1.3.13" const char i40evf_driver_version[] = DRV_VERSION; static const char i40evf_copyright[] = "Copyright (c) 2013 - 2015 Intel Corporation."; diff --git a/drivers/net/ethernet/ti/cpsw-common.c b/drivers/net/ethernet/ti/cpsw-common.c index f59509486113..c08be62bceba 100644 --- a/drivers/net/ethernet/ti/cpsw-common.c +++ b/drivers/net/ethernet/ti/cpsw-common.c @@ -19,11 +19,38 @@ #include "cpsw.h" -#define AM33XX_CTRL_MAC_LO_REG(offset, id) ((offset) + 0x8 * (id)) -#define AM33XX_CTRL_MAC_HI_REG(offset, id) ((offset) + 0x8 * (id) + 0x4) +#define CTRL_MAC_LO_REG(offset, id) ((offset) + 0x8 * (id)) +#define CTRL_MAC_HI_REG(offset, id) ((offset) + 0x8 * (id) + 0x4) -int cpsw_am33xx_cm_get_macid(struct device *dev, u16 offset, int slave, - u8 *mac_addr) +static int davinci_emac_3517_get_macid(struct device *dev, u16 offset, + int slave, u8 *mac_addr) +{ + u32 macid_lsb; + u32 macid_msb; + struct regmap *syscon; + + syscon = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon"); + if (IS_ERR(syscon)) { + if (PTR_ERR(syscon) == -ENODEV) + return 0; + return PTR_ERR(syscon); + } + + regmap_read(syscon, CTRL_MAC_LO_REG(offset, slave), &macid_lsb); + regmap_read(syscon, CTRL_MAC_HI_REG(offset, slave), &macid_msb); + + mac_addr[0] = (macid_msb >> 16) & 0xff; + mac_addr[1] = (macid_msb >> 8) & 0xff; + mac_addr[2] = macid_msb & 0xff; + mac_addr[3] = (macid_lsb >> 16) & 0xff; + mac_addr[4] = (macid_lsb >> 8) & 0xff; + mac_addr[5] = macid_lsb & 0xff; + + return 0; +} + +static int cpsw_am33xx_cm_get_macid(struct device *dev, u16 offset, int slave, + u8 *mac_addr) { u32 macid_lo; u32 macid_hi; @@ -36,10 +63,8 @@ int cpsw_am33xx_cm_get_macid(struct device *dev, u16 offset, int slave, return PTR_ERR(syscon); } - regmap_read(syscon, AM33XX_CTRL_MAC_LO_REG(offset, slave), - &macid_lo); - regmap_read(syscon, AM33XX_CTRL_MAC_HI_REG(offset, slave), - &macid_hi); + regmap_read(syscon, CTRL_MAC_LO_REG(offset, slave), &macid_lo); + regmap_read(syscon, CTRL_MAC_HI_REG(offset, slave), &macid_hi); mac_addr[5] = (macid_lo >> 8) & 0xff; mac_addr[4] = macid_lo & 0xff; @@ -50,6 +75,27 @@ int cpsw_am33xx_cm_get_macid(struct device *dev, u16 offset, int slave, return 0; } -EXPORT_SYMBOL_GPL(cpsw_am33xx_cm_get_macid); + +int ti_cm_get_macid(struct device *dev, int slave, u8 *mac_addr) +{ + if (of_machine_is_compatible("ti,am33xx")) + return cpsw_am33xx_cm_get_macid(dev, 0x630, slave, mac_addr); + + if (of_device_is_compatible(dev->of_node, "ti,am3517-emac")) + return davinci_emac_3517_get_macid(dev, 0x110, slave, mac_addr); + + if (of_device_is_compatible(dev->of_node, "ti,dm816-emac")) + return cpsw_am33xx_cm_get_macid(dev, 0x30, slave, mac_addr); + + if (of_machine_is_compatible("ti,am4372")) + return cpsw_am33xx_cm_get_macid(dev, 0x630, slave, mac_addr); + + if (of_machine_is_compatible("ti,dra7")) + return davinci_emac_3517_get_macid(dev, 0x514, slave, mac_addr); + + dev_err(dev, "incompatible machine/device type for reading mac address\n"); + return -ENOENT; +} +EXPORT_SYMBOL_GPL(ti_cm_get_macid); MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index c670317f3cb0..75584cc36339 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -2058,13 +2058,10 @@ no_phy_slave: if (mac_addr) { memcpy(slave_data->mac_addr, mac_addr, ETH_ALEN); } else { - if (of_machine_is_compatible("ti,am33xx")) { - ret = cpsw_am33xx_cm_get_macid(&pdev->dev, - 0x630, i, - slave_data->mac_addr); - if (ret) - return ret; - } + ret = ti_cm_get_macid(&pdev->dev, i, + slave_data->mac_addr); + if (ret) + return ret; } if (data->dual_emac) { if (of_property_read_u32(slave_node, "dual_emac_res_vlan", diff --git a/drivers/net/ethernet/ti/cpsw.h b/drivers/net/ethernet/ti/cpsw.h index ca90efafd156..442a7038e660 100644 --- a/drivers/net/ethernet/ti/cpsw.h +++ b/drivers/net/ethernet/ti/cpsw.h @@ -41,7 +41,6 @@ struct cpsw_platform_data { }; void cpsw_phy_sel(struct device *dev, phy_interface_t phy_mode, int slave); -int cpsw_am33xx_cm_get_macid(struct device *dev, u16 offset, int slave, - u8 *mac_addr); +int ti_cm_get_macid(struct device *dev, int slave, u8 *mac_addr); #endif /* __CPSW_H__ */ diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c index a21c77bc1b27..39a54b27db66 100644 --- a/drivers/net/ethernet/ti/davinci_emac.c +++ b/drivers/net/ethernet/ti/davinci_emac.c @@ -1882,51 +1882,13 @@ davinci_emac_of_get_pdata(struct platform_device *pdev, struct emac_priv *priv) return pdata; } -static int davinci_emac_3517_get_macid(struct device *dev, u16 offset, - int slave, u8 *mac_addr) -{ - u32 macid_lsb; - u32 macid_msb; - struct regmap *syscon; - - syscon = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon"); - if (IS_ERR(syscon)) { - if (PTR_ERR(syscon) == -ENODEV) - return 0; - return PTR_ERR(syscon); - } - - regmap_read(syscon, offset, &macid_lsb); - regmap_read(syscon, offset + 4, &macid_msb); - - mac_addr[0] = (macid_msb >> 16) & 0xff; - mac_addr[1] = (macid_msb >> 8) & 0xff; - mac_addr[2] = macid_msb & 0xff; - mac_addr[3] = (macid_lsb >> 16) & 0xff; - mac_addr[4] = (macid_lsb >> 8) & 0xff; - mac_addr[5] = macid_lsb & 0xff; - - return 0; -} - static int davinci_emac_try_get_mac(struct platform_device *pdev, int instance, u8 *mac_addr) { - int error = -EINVAL; - if (!pdev->dev.of_node) - return error; - - if (of_device_is_compatible(pdev->dev.of_node, "ti,am3517-emac")) - error = davinci_emac_3517_get_macid(&pdev->dev, 0x110, - 0, mac_addr); - else if (of_device_is_compatible(pdev->dev.of_node, - "ti,dm816-emac")) - error = cpsw_am33xx_cm_get_macid(&pdev->dev, 0x30, - instance, - mac_addr); - - return error; + return -EINVAL; + + return ti_cm_get_macid(&pdev->dev, instance, mac_addr); } /** diff --git a/drivers/net/ieee802154/Kconfig b/drivers/net/ieee802154/Kconfig index 1dd5ab8e5054..5a614b2d0767 100644 --- a/drivers/net/ieee802154/Kconfig +++ b/drivers/net/ieee802154/Kconfig @@ -32,6 +32,13 @@ config IEEE802154_AT86RF230 This driver can also be built as a module. To do so, say M here. the module will be called 'at86rf230'. +config IEEE802154_AT86RF230_DEBUGFS + depends on IEEE802154_AT86RF230 + bool "AT86RF230 debugfs interface" + depends on DEBUG_FS + ---help--- + This option compiles debugfs code for the at86rf230 driver. + config IEEE802154_MRF24J40 tristate "Microchip MRF24J40 transceiver driver" depends on IEEE802154_DRIVERS && MAC802154 diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index 6422caac8d40..9756e6451038 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -31,6 +31,7 @@ #include <linux/skbuff.h> #include <linux/of_gpio.h> #include <linux/ieee802154.h> +#include <linux/debugfs.h> #include <net/mac802154.h> #include <net/cfg802154.h> @@ -83,6 +84,15 @@ struct at86rf230_state_change { bool irq_enable; }; +struct at86rf230_trac { + u64 success; + u64 success_data_pending; + u64 success_wait_for_ack; + u64 channel_access_failure; + u64 no_ack; + u64 invalid; +}; + struct at86rf230_local { struct spi_device *spi; @@ -103,6 +113,8 @@ struct at86rf230_local { u8 tx_retry; struct sk_buff *tx_skb; struct at86rf230_state_change tx; + + struct at86rf230_trac trac; }; #define AT86RF2XX_NUMREGS 0x3F @@ -377,14 +389,6 @@ at86rf230_async_read_reg(struct at86rf230_local *lp, const u8 reg, } } -static inline u8 at86rf230_state_to_force(u8 state) -{ - if (state == STATE_TX_ON) - return STATE_FORCE_TX_ON; - else - return STATE_FORCE_TRX_OFF; -} - static void at86rf230_async_state_assert(void *context) { @@ -426,7 +430,7 @@ at86rf230_async_state_assert(void *context) u8 state = ctx->to_state; if (lp->tx_retry >= AT86RF2XX_MAX_TX_RETRIES) - state = at86rf230_state_to_force(state); + state = STATE_FORCE_TRX_OFF; lp->tx_retry++; at86rf230_async_state_change(lp, ctx, state, @@ -667,28 +671,34 @@ at86rf230_tx_trac_check(void *context) { struct at86rf230_state_change *ctx = context; struct at86rf230_local *lp = ctx->lp; - const u8 *buf = ctx->buf; - const u8 trac = (buf[1] & 0xe0) >> 5; - /* If trac status is different than zero we need to do a state change - * to STATE_FORCE_TRX_OFF then STATE_RX_AACK_ON to recover the - * transceiver. - */ - if (trac) - at86rf230_async_state_change(lp, ctx, STATE_FORCE_TRX_OFF, - at86rf230_tx_on, true); - else - at86rf230_tx_on(context); -} + if (IS_ENABLED(CONFIG_IEEE802154_AT86RF230_DEBUGFS)) { + u8 trac = TRAC_MASK(ctx->buf[1]); -static void -at86rf230_tx_trac_status(void *context) -{ - struct at86rf230_state_change *ctx = context; - struct at86rf230_local *lp = ctx->lp; + switch (trac) { + case TRAC_SUCCESS: + lp->trac.success++; + break; + case TRAC_SUCCESS_DATA_PENDING: + lp->trac.success_data_pending++; + break; + case TRAC_CHANNEL_ACCESS_FAILURE: + lp->trac.channel_access_failure++; + break; + case TRAC_NO_ACK: + lp->trac.no_ack++; + break; + case TRAC_INVALID: + lp->trac.invalid++; + break; + default: + WARN_ONCE(1, "received tx trac status %d\n", trac); + break; + } + } - at86rf230_async_read_reg(lp, RG_TRX_STATE, ctx, - at86rf230_tx_trac_check, true); + at86rf230_async_state_change(lp, &lp->irq, STATE_TX_ON, + at86rf230_tx_on, true); } static void @@ -723,13 +733,32 @@ at86rf230_rx_read_frame_complete(void *context) } static void -at86rf230_rx_read_frame(void *context) +at86rf230_rx_trac_check(void *context) { struct at86rf230_state_change *ctx = context; struct at86rf230_local *lp = ctx->lp; u8 *buf = ctx->buf; int rc; + if (IS_ENABLED(CONFIG_IEEE802154_AT86RF230_DEBUGFS)) { + u8 trac = TRAC_MASK(buf[1]); + + switch (trac) { + case TRAC_SUCCESS: + lp->trac.success++; + break; + case TRAC_SUCCESS_WAIT_FOR_ACK: + lp->trac.success_wait_for_ack++; + break; + case TRAC_INVALID: + lp->trac.invalid++; + break; + default: + WARN_ONCE(1, "received rx trac status %d\n", trac); + break; + } + } + buf[0] = CMD_FB; ctx->trx.len = AT86RF2XX_MAX_BUF; ctx->msg.complete = at86rf230_rx_read_frame_complete; @@ -742,26 +771,12 @@ at86rf230_rx_read_frame(void *context) } static void -at86rf230_rx_trac_check(void *context) -{ - /* Possible check on trac status here. This could be useful to make - * some stats why receive is failed. Not used at the moment, but it's - * maybe timing relevant. Datasheet doesn't say anything about this. - * The programming guide say do it so. - */ - - at86rf230_rx_read_frame(context); -} - -static void at86rf230_irq_trx_end(struct at86rf230_local *lp) { if (lp->is_tx) { lp->is_tx = 0; - at86rf230_async_state_change(lp, &lp->irq, - STATE_FORCE_TX_ON, - at86rf230_tx_trac_status, - true); + at86rf230_async_read_reg(lp, RG_TRX_STATE, &lp->irq, + at86rf230_tx_trac_check, true); } else { at86rf230_async_read_reg(lp, RG_TRX_STATE, &lp->irq, at86rf230_rx_trac_check, true); @@ -920,6 +935,10 @@ at86rf230_start(struct ieee802154_hw *hw) { struct at86rf230_local *lp = hw->priv; + /* reset trac stats on start */ + if (IS_ENABLED(CONFIG_IEEE802154_AT86RF230_DEBUGFS)) + memset(&lp->trac, 0, sizeof(struct at86rf230_trac)); + at86rf230_awake(lp); enable_irq(lp->spi->irq); @@ -1357,7 +1376,7 @@ static int at86rf230_hw_init(struct at86rf230_local *lp, u8 xtal_trim) if (irq_type == IRQ_TYPE_EDGE_RISING || irq_type == IRQ_TYPE_EDGE_FALLING) dev_warn(&lp->spi->dev, - "Using edge triggered irq's are not recommended!\n"); + "Using edge triggered irq's are not recommended, because it can cause races and result in a non-functional driver!\n"); if (irq_type == IRQ_TYPE_EDGE_FALLING || irq_type == IRQ_TYPE_LEVEL_LOW) irq_pol = IRQ_ACTIVE_LOW; @@ -1620,6 +1639,66 @@ at86rf230_setup_spi_messages(struct at86rf230_local *lp) lp->tx.timer.function = at86rf230_async_state_timer; } +#ifdef CONFIG_IEEE802154_AT86RF230_DEBUGFS +static struct dentry *at86rf230_debugfs_root; + +static int at86rf230_stats_show(struct seq_file *file, void *offset) +{ + struct at86rf230_local *lp = file->private; + + seq_printf(file, "SUCCESS:\t\t%8llu\n", lp->trac.success); + seq_printf(file, "SUCCESS_DATA_PENDING:\t%8llu\n", + lp->trac.success_data_pending); + seq_printf(file, "SUCCESS_WAIT_FOR_ACK:\t%8llu\n", + lp->trac.success_wait_for_ack); + seq_printf(file, "CHANNEL_ACCESS_FAILURE:\t%8llu\n", + lp->trac.channel_access_failure); + seq_printf(file, "NO_ACK:\t\t\t%8llu\n", lp->trac.no_ack); + seq_printf(file, "INVALID:\t\t%8llu\n", lp->trac.invalid); + return 0; +} + +static int at86rf230_stats_open(struct inode *inode, struct file *file) +{ + return single_open(file, at86rf230_stats_show, inode->i_private); +} + +static const struct file_operations at86rf230_stats_fops = { + .open = at86rf230_stats_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int at86rf230_debugfs_init(struct at86rf230_local *lp) +{ + char debugfs_dir_name[DNAME_INLINE_LEN + 1] = "at86rf230-"; + struct dentry *stats; + + strncat(debugfs_dir_name, dev_name(&lp->spi->dev), DNAME_INLINE_LEN); + + at86rf230_debugfs_root = debugfs_create_dir(debugfs_dir_name, NULL); + if (!at86rf230_debugfs_root) + return -ENOMEM; + + stats = debugfs_create_file("trac_stats", S_IRUGO, + at86rf230_debugfs_root, lp, + &at86rf230_stats_fops); + if (!stats) + return -ENOMEM; + + return 0; +} + +static void at86rf230_debugfs_remove(void) +{ + debugfs_remove_recursive(at86rf230_debugfs_root); +} +#else +static int at86rf230_debugfs_init(struct at86rf230_local *lp) { return 0; } +static void at86rf230_debugfs_remove(void) { } +#endif + static int at86rf230_probe(struct spi_device *spi) { struct ieee802154_hw *hw; @@ -1715,12 +1794,18 @@ static int at86rf230_probe(struct spi_device *spi) /* going into sleep by default */ at86rf230_sleep(lp); - rc = ieee802154_register_hw(lp->hw); + rc = at86rf230_debugfs_init(lp); if (rc) goto free_dev; + rc = ieee802154_register_hw(lp->hw); + if (rc) + goto free_debugfs; + return rc; +free_debugfs: + at86rf230_debugfs_remove(); free_dev: ieee802154_free_hw(lp->hw); @@ -1735,6 +1820,7 @@ static int at86rf230_remove(struct spi_device *spi) at86rf230_write_subreg(lp, SR_IRQ_MASK, 0); ieee802154_unregister_hw(lp->hw); ieee802154_free_hw(lp->hw); + at86rf230_debugfs_remove(); dev_dbg(&spi->dev, "unregistered at86rf230\n"); return 0; diff --git a/drivers/net/ieee802154/at86rf230.h b/drivers/net/ieee802154/at86rf230.h index 1e6d1cc677f6..fd9c1f467f63 100644 --- a/drivers/net/ieee802154/at86rf230.h +++ b/drivers/net/ieee802154/at86rf230.h @@ -216,5 +216,13 @@ #define STATE_TRANSITION_IN_PROGRESS 0x1F #define TRX_STATE_MASK (0x1F) +#define TRAC_MASK(x) ((x & 0xe0) >> 5) + +#define TRAC_SUCCESS 0 +#define TRAC_SUCCESS_DATA_PENDING 1 +#define TRAC_SUCCESS_WAIT_FOR_ACK 2 +#define TRAC_CHANNEL_ACCESS_FAILURE 3 +#define TRAC_NO_ACK 5 +#define TRAC_INVALID 7 #endif /* !_AT86RF230_H */ diff --git a/drivers/net/ieee802154/atusb.c b/drivers/net/ieee802154/atusb.c index 80dfc725b8dc..199a94a9c8bc 100644 --- a/drivers/net/ieee802154/atusb.c +++ b/drivers/net/ieee802154/atusb.c @@ -559,6 +559,7 @@ static int atusb_get_and_show_chip(struct atusb *atusb) { struct usb_device *usb_dev = atusb->usb_dev; uint8_t man_id_0, man_id_1, part_num, version_num; + const char *chip; man_id_0 = atusb_read_reg(atusb, RG_MAN_ID_0); man_id_1 = atusb_read_reg(atusb, RG_MAN_ID_1); @@ -574,14 +575,22 @@ static int atusb_get_and_show_chip(struct atusb *atusb) man_id_1, man_id_0); goto fail; } - if (part_num != 3 && part_num != 2) { + + switch (part_num) { + case 2: + chip = "AT86RF230"; + break; + case 3: + chip = "AT86RF231"; + break; + default: dev_err(&usb_dev->dev, "unexpected transceiver, part 0x%02x version 0x%02x\n", part_num, version_num); goto fail; } - dev_info(&usb_dev->dev, "ATUSB: AT86RF231 version %d\n", version_num); + dev_info(&usb_dev->dev, "ATUSB: %s version %d\n", chip, version_num); return 0; diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index f821a97d7827..9bf63c27a9b7 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -1819,19 +1819,22 @@ again: goto destroy_ring; } - if (num_queues == 1) { - err = write_queue_xenstore_keys(&info->queues[0], &xbt, 0); /* flat */ - if (err) - goto abort_transaction_no_dev_fatal; - } else { + if (xenbus_exists(XBT_NIL, + info->xbdev->otherend, "multi-queue-max-queues")) { /* Write the number of queues */ - err = xenbus_printf(xbt, dev->nodename, "multi-queue-num-queues", - "%u", num_queues); + err = xenbus_printf(xbt, dev->nodename, + "multi-queue-num-queues", "%u", num_queues); if (err) { message = "writing multi-queue-num-queues"; goto abort_transaction_no_dev_fatal; } + } + if (num_queues == 1) { + err = write_queue_xenstore_keys(&info->queues[0], &xbt, 0); /* flat */ + if (err) + goto abort_transaction_no_dev_fatal; + } else { /* Write the keys for each queue */ for (i = 0; i < num_queues; ++i) { queue = &info->queues[i]; diff --git a/drivers/s390/char/monreader.c b/drivers/s390/char/monreader.c index b7d60306b0bc..fc94bfdceb95 100644 --- a/drivers/s390/char/monreader.c +++ b/drivers/s390/char/monreader.c @@ -229,7 +229,7 @@ static struct mon_msg *mon_next_message(struct mon_private *monpriv) /****************************************************************************** * IUCV handler * *****************************************************************************/ -static void mon_iucv_path_complete(struct iucv_path *path, u8 ipuser[16]) +static void mon_iucv_path_complete(struct iucv_path *path, u8 *ipuser) { struct mon_private *monpriv = path->private; @@ -237,7 +237,7 @@ static void mon_iucv_path_complete(struct iucv_path *path, u8 ipuser[16]) wake_up(&mon_conn_wait_queue); } -static void mon_iucv_path_severed(struct iucv_path *path, u8 ipuser[16]) +static void mon_iucv_path_severed(struct iucv_path *path, u8 *ipuser) { struct mon_private *monpriv = path->private; diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c index 9bb48d70957c..799c1524c779 100644 --- a/drivers/s390/char/vmlogrdr.c +++ b/drivers/s390/char/vmlogrdr.c @@ -99,8 +99,8 @@ static const struct file_operations vmlogrdr_fops = { }; -static void vmlogrdr_iucv_path_complete(struct iucv_path *, u8 ipuser[16]); -static void vmlogrdr_iucv_path_severed(struct iucv_path *, u8 ipuser[16]); +static void vmlogrdr_iucv_path_complete(struct iucv_path *, u8 *ipuser); +static void vmlogrdr_iucv_path_severed(struct iucv_path *, u8 *ipuser); static void vmlogrdr_iucv_message_pending(struct iucv_path *, struct iucv_message *); @@ -160,7 +160,7 @@ static struct cdev *vmlogrdr_cdev = NULL; static int recording_class_AB; -static void vmlogrdr_iucv_path_complete(struct iucv_path *path, u8 ipuser[16]) +static void vmlogrdr_iucv_path_complete(struct iucv_path *path, u8 *ipuser) { struct vmlogrdr_priv_t * logptr = path->private; @@ -171,7 +171,7 @@ static void vmlogrdr_iucv_path_complete(struct iucv_path *path, u8 ipuser[16]) } -static void vmlogrdr_iucv_path_severed(struct iucv_path *path, u8 ipuser[16]) +static void vmlogrdr_iucv_path_severed(struct iucv_path *path, u8 *ipuser) { struct vmlogrdr_priv_t * logptr = path->private; u8 reason = (u8) ipuser[8]; diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index 33f7040d711d..0ba3a2f81750 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -149,12 +149,11 @@ static struct device_driver netiucv_driver = { .pm = &netiucv_pm_ops, }; -static int netiucv_callback_connreq(struct iucv_path *, - u8 ipvmid[8], u8 ipuser[16]); -static void netiucv_callback_connack(struct iucv_path *, u8 ipuser[16]); -static void netiucv_callback_connrej(struct iucv_path *, u8 ipuser[16]); -static void netiucv_callback_connsusp(struct iucv_path *, u8 ipuser[16]); -static void netiucv_callback_connres(struct iucv_path *, u8 ipuser[16]); +static int netiucv_callback_connreq(struct iucv_path *, u8 *, u8 *); +static void netiucv_callback_connack(struct iucv_path *, u8 *); +static void netiucv_callback_connrej(struct iucv_path *, u8 *); +static void netiucv_callback_connsusp(struct iucv_path *, u8 *); +static void netiucv_callback_connres(struct iucv_path *, u8 *); static void netiucv_callback_rx(struct iucv_path *, struct iucv_message *); static void netiucv_callback_txdone(struct iucv_path *, struct iucv_message *); @@ -556,8 +555,8 @@ static void netiucv_callback_connack(struct iucv_path *path, u8 ipuser[16]) fsm_event(conn->fsm, CONN_EVENT_CONN_ACK, conn); } -static int netiucv_callback_connreq(struct iucv_path *path, - u8 ipvmid[8], u8 ipuser[16]) +static int netiucv_callback_connreq(struct iucv_path *path, u8 *ipvmid, + u8 *ipuser) { struct iucv_connection *conn = path->private; struct iucv_event ev; @@ -587,21 +586,21 @@ static int netiucv_callback_connreq(struct iucv_path *path, return rc; } -static void netiucv_callback_connrej(struct iucv_path *path, u8 ipuser[16]) +static void netiucv_callback_connrej(struct iucv_path *path, u8 *ipuser) { struct iucv_connection *conn = path->private; fsm_event(conn->fsm, CONN_EVENT_CONN_REJ, conn); } -static void netiucv_callback_connsusp(struct iucv_path *path, u8 ipuser[16]) +static void netiucv_callback_connsusp(struct iucv_path *path, u8 *ipuser) { struct iucv_connection *conn = path->private; fsm_event(conn->fsm, CONN_EVENT_CONN_SUS, conn); } -static void netiucv_callback_connres(struct iucv_path *path, u8 ipuser[16]) +static void netiucv_callback_connres(struct iucv_path *path, u8 *ipuser) { struct iucv_connection *conn = path->private; diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index ba974a2e409f..6719447d13f0 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -663,9 +663,7 @@ struct qeth_card_info { char mcl_level[QETH_MCL_LENGTH + 1]; int guestlan; int mac_bits; - int portname_required; int portno; - char portname[9]; enum qeth_card_types type; enum qeth_link_types link_type; int is_multicast_different; @@ -969,6 +967,15 @@ int qeth_hw_trap(struct qeth_card *, enum qeth_diags_trap_action); int qeth_query_ipassists(struct qeth_card *, enum qeth_prot_versions prot); void qeth_trace_features(struct qeth_card *); void qeth_close_dev(struct qeth_card *); +int qeth_send_simple_setassparms(struct qeth_card *, enum qeth_ipa_funcs, + __u16, long); +int qeth_send_setassparms(struct qeth_card *, struct qeth_cmd_buffer *, __u16, + long, + int (*reply_cb)(struct qeth_card *, + struct qeth_reply *, unsigned long), + void *); +int qeth_start_ipa_tx_checksum(struct qeth_card *); +int qeth_set_rx_csum(struct qeth_card *, int); /* exports for OSN */ int qeth_osn_assist(struct net_device *, void *, int); diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 5e20fba37bff..31ac53fa5cee 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -1982,14 +1982,6 @@ static void qeth_idx_read_cb(struct qeth_channel *channel, goto out; } -/** - * * temporary fix for microcode bug - * * to revert it,replace OR by AND - * */ - if ((!QETH_IDX_NO_PORTNAME_REQUIRED(iob->data)) || - (card->info.type == QETH_CARD_TYPE_OSD)) - card->info.portname_required = 1; - memcpy(&temp, QETH_IDX_ACT_FUNC_LEVEL(iob->data), 2); if (temp != qeth_peer_func_level(card->info.func_level)) { QETH_DBF_MESSAGE(2, "%s IDX_ACTIVATE on read channel: function " @@ -2360,8 +2352,6 @@ static int qeth_ulp_enable(struct qeth_card *card) &card->token.cm_connection_r, QETH_MPC_TOKEN_LENGTH); memcpy(QETH_ULP_ENABLE_FILTER_TOKEN(iob->data), &card->token.ulp_filter_w, QETH_MPC_TOKEN_LENGTH); - memcpy(QETH_ULP_ENABLE_PORTNAME_AND_LL(iob->data), - card->info.portname, 9); rc = qeth_send_control_data(card, ULP_ENABLE_SIZE, iob, qeth_ulp_enable_cb, NULL); return rc; @@ -2680,48 +2670,6 @@ out_qdio: return rc; } -static void qeth_print_status_with_portname(struct qeth_card *card) -{ - char dbf_text[15]; - int i; - - sprintf(dbf_text, "%s", card->info.portname + 1); - for (i = 0; i < 8; i++) - dbf_text[i] = - (char) _ebcasc[(__u8) dbf_text[i]]; - dbf_text[8] = 0; - dev_info(&card->gdev->dev, "Device is a%s card%s%s%s\n" - "with link type %s (portname: %s)\n", - qeth_get_cardname(card), - (card->info.mcl_level[0]) ? " (level: " : "", - (card->info.mcl_level[0]) ? card->info.mcl_level : "", - (card->info.mcl_level[0]) ? ")" : "", - qeth_get_cardname_short(card), - dbf_text); - -} - -static void qeth_print_status_no_portname(struct qeth_card *card) -{ - if (card->info.portname[0]) - dev_info(&card->gdev->dev, "Device is a%s " - "card%s%s%s\nwith link type %s " - "(no portname needed by interface).\n", - qeth_get_cardname(card), - (card->info.mcl_level[0]) ? " (level: " : "", - (card->info.mcl_level[0]) ? card->info.mcl_level : "", - (card->info.mcl_level[0]) ? ")" : "", - qeth_get_cardname_short(card)); - else - dev_info(&card->gdev->dev, "Device is a%s " - "card%s%s%s\nwith link type %s.\n", - qeth_get_cardname(card), - (card->info.mcl_level[0]) ? " (level: " : "", - (card->info.mcl_level[0]) ? card->info.mcl_level : "", - (card->info.mcl_level[0]) ? ")" : "", - qeth_get_cardname_short(card)); -} - void qeth_print_status_message(struct qeth_card *card) { switch (card->info.type) { @@ -2758,10 +2706,13 @@ void qeth_print_status_message(struct qeth_card *card) default: memset(&card->info.mcl_level[0], 0, QETH_MCL_LENGTH + 1); } - if (card->info.portname_required) - qeth_print_status_with_portname(card); - else - qeth_print_status_no_portname(card); + dev_info(&card->gdev->dev, + "Device is a%s card%s%s%s\nwith link type %s.\n", + qeth_get_cardname(card), + (card->info.mcl_level[0]) ? " (level: " : "", + (card->info.mcl_level[0]) ? card->info.mcl_level : "", + (card->info.mcl_level[0]) ? ")" : "", + qeth_get_cardname_short(card)); } EXPORT_SYMBOL_GPL(qeth_print_status_message); @@ -5027,13 +4978,11 @@ static void qeth_core_free_card(struct qeth_card *card) void qeth_trace_features(struct qeth_card *card) { QETH_CARD_TEXT(card, 2, "features"); - QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa4.supported_funcs); - QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa4.enabled_funcs); - QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa6.supported_funcs); - QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa6.enabled_funcs); - QETH_CARD_TEXT_(card, 2, "%x", card->options.adp.supported_funcs); - QETH_CARD_TEXT_(card, 2, "%x", card->options.adp.enabled_funcs); - QETH_CARD_TEXT_(card, 2, "%x", card->info.diagass_support); + QETH_CARD_HEX(card, 2, &card->options.ipa4, sizeof(card->options.ipa4)); + QETH_CARD_HEX(card, 2, &card->options.ipa6, sizeof(card->options.ipa6)); + QETH_CARD_HEX(card, 2, &card->options.adp, sizeof(card->options.adp)); + QETH_CARD_HEX(card, 2, &card->info.diagass_support, + sizeof(card->info.diagass_support)); } EXPORT_SYMBOL_GPL(qeth_trace_features); @@ -5132,6 +5081,7 @@ retriable: } card->options.ipa4.supported_funcs = 0; + card->options.ipa6.supported_funcs = 0; card->options.adp.supported_funcs = 0; card->options.sbp.supported_funcs = 0; card->info.diagass_support = 0; @@ -5317,6 +5267,102 @@ no_mem: } EXPORT_SYMBOL_GPL(qeth_core_get_next_skb); +static int qeth_setassparms_cb(struct qeth_card *card, + struct qeth_reply *reply, unsigned long data) +{ + struct qeth_ipa_cmd *cmd; + + QETH_CARD_TEXT(card, 4, "defadpcb"); + + cmd = (struct qeth_ipa_cmd *) data; + if (cmd->hdr.return_code == 0) { + cmd->hdr.return_code = cmd->data.setassparms.hdr.return_code; + if (cmd->hdr.prot_version == QETH_PROT_IPV4) + card->options.ipa4.enabled_funcs = cmd->hdr.ipa_enabled; + if (cmd->hdr.prot_version == QETH_PROT_IPV6) + card->options.ipa6.enabled_funcs = cmd->hdr.ipa_enabled; + } + if (cmd->data.setassparms.hdr.assist_no == IPA_INBOUND_CHECKSUM && + cmd->data.setassparms.hdr.command_code == IPA_CMD_ASS_START) { + card->info.csum_mask = cmd->data.setassparms.data.flags_32bit; + QETH_CARD_TEXT_(card, 3, "csum:%d", card->info.csum_mask); + } + if (cmd->data.setassparms.hdr.assist_no == IPA_OUTBOUND_CHECKSUM && + cmd->data.setassparms.hdr.command_code == IPA_CMD_ASS_START) { + card->info.tx_csum_mask = + cmd->data.setassparms.data.flags_32bit; + QETH_CARD_TEXT_(card, 3, "tcsu:%d", card->info.tx_csum_mask); + } + + return 0; +} + +static struct qeth_cmd_buffer *qeth_get_setassparms_cmd(struct qeth_card *card, + enum qeth_ipa_funcs ipa_func, + __u16 cmd_code, __u16 len, + enum qeth_prot_versions prot) +{ + struct qeth_cmd_buffer *iob; + struct qeth_ipa_cmd *cmd; + + QETH_CARD_TEXT(card, 4, "getasscm"); + iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETASSPARMS, prot); + + if (iob) { + cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); + cmd->data.setassparms.hdr.assist_no = ipa_func; + cmd->data.setassparms.hdr.length = 8 + len; + cmd->data.setassparms.hdr.command_code = cmd_code; + cmd->data.setassparms.hdr.return_code = 0; + cmd->data.setassparms.hdr.seq_no = 0; + } + + return iob; +} + +int qeth_send_setassparms(struct qeth_card *card, + struct qeth_cmd_buffer *iob, __u16 len, long data, + int (*reply_cb)(struct qeth_card *, + struct qeth_reply *, unsigned long), + void *reply_param) +{ + int rc; + struct qeth_ipa_cmd *cmd; + + QETH_CARD_TEXT(card, 4, "sendassp"); + + cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); + if (len <= sizeof(__u32)) + cmd->data.setassparms.data.flags_32bit = (__u32) data; + else /* (len > sizeof(__u32)) */ + memcpy(&cmd->data.setassparms.data, (void *) data, len); + + rc = qeth_send_ipa_cmd(card, iob, reply_cb, reply_param); + return rc; +} +EXPORT_SYMBOL_GPL(qeth_send_setassparms); + +int qeth_send_simple_setassparms(struct qeth_card *card, + enum qeth_ipa_funcs ipa_func, + __u16 cmd_code, long data) +{ + int rc; + int length = 0; + struct qeth_cmd_buffer *iob; + + QETH_CARD_TEXT(card, 4, "simassp4"); + if (data) + length = sizeof(__u32); + iob = qeth_get_setassparms_cmd(card, ipa_func, cmd_code, + length, QETH_PROT_IPV4); + if (!iob) + return -ENOMEM; + rc = qeth_send_setassparms(card, iob, length, data, + qeth_setassparms_cb, NULL); + return rc; +} +EXPORT_SYMBOL_GPL(qeth_send_simple_setassparms); + static void qeth_unregister_dbf_views(void) { int x; @@ -6003,6 +6049,75 @@ int qeth_core_ethtool_get_settings(struct net_device *netdev, } EXPORT_SYMBOL_GPL(qeth_core_ethtool_get_settings); +static int qeth_send_checksum_command(struct qeth_card *card) +{ + int rc; + + rc = qeth_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM, + IPA_CMD_ASS_START, 0); + if (rc) { + dev_warn(&card->gdev->dev, "Starting HW checksumming for %s " + "failed, using SW checksumming\n", + QETH_CARD_IFNAME(card)); + return rc; + } + rc = qeth_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM, + IPA_CMD_ASS_ENABLE, + card->info.csum_mask); + if (rc) { + dev_warn(&card->gdev->dev, "Enabling HW checksumming for %s " + "failed, using SW checksumming\n", + QETH_CARD_IFNAME(card)); + return rc; + } + return 0; +} + +int qeth_set_rx_csum(struct qeth_card *card, int on) +{ + int rc; + + if (on) { + rc = qeth_send_checksum_command(card); + if (rc) + return -EIO; + dev_info(&card->gdev->dev, + "HW Checksumming (inbound) enabled\n"); + } else { + rc = qeth_send_simple_setassparms(card, + IPA_INBOUND_CHECKSUM, IPA_CMD_ASS_STOP, 0); + if (rc) + return -EIO; + } + return 0; +} +EXPORT_SYMBOL_GPL(qeth_set_rx_csum); + +int qeth_start_ipa_tx_checksum(struct qeth_card *card) +{ + int rc = 0; + + if (!qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM)) + return rc; + rc = qeth_send_simple_setassparms(card, IPA_OUTBOUND_CHECKSUM, + IPA_CMD_ASS_START, 0); + if (rc) + goto err_out; + rc = qeth_send_simple_setassparms(card, IPA_OUTBOUND_CHECKSUM, + IPA_CMD_ASS_ENABLE, + card->info.tx_csum_mask); + if (rc) + goto err_out; + + dev_info(&card->gdev->dev, "HW TX Checksumming enabled\n"); + return rc; +err_out: + dev_warn(&card->gdev->dev, "Enabling HW TX checksumming for %s " + "failed, using SW TX checksumming\n", QETH_CARD_IFNAME(card)); + return rc; +} +EXPORT_SYMBOL_GPL(qeth_start_ipa_tx_checksum); + static int __init qeth_core_init(void) { int rc; diff --git a/drivers/s390/net/qeth_core_sys.c b/drivers/s390/net/qeth_core_sys.c index 423bec56cffa..e6e5b9671bf2 100644 --- a/drivers/s390/net/qeth_core_sys.c +++ b/drivers/s390/net/qeth_core_sys.c @@ -153,52 +153,17 @@ static DEVICE_ATTR(portno, 0644, qeth_dev_portno_show, qeth_dev_portno_store); static ssize_t qeth_dev_portname_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct qeth_card *card = dev_get_drvdata(dev); - char portname[9] = {0, }; - - if (!card) - return -EINVAL; - - if (card->info.portname_required) { - memcpy(portname, card->info.portname + 1, 8); - EBCASC(portname, 8); - return sprintf(buf, "%s\n", portname); - } else - return sprintf(buf, "no portname required\n"); + return sprintf(buf, "no portname required\n"); } static ssize_t qeth_dev_portname_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct qeth_card *card = dev_get_drvdata(dev); - char *tmp; - int i, rc = 0; - - if (!card) - return -EINVAL; - - mutex_lock(&card->conf_mutex); - if ((card->state != CARD_STATE_DOWN) && - (card->state != CARD_STATE_RECOVER)) { - rc = -EPERM; - goto out; - } - tmp = strsep((char **) &buf, "\n"); - if ((strlen(tmp) > 8) || (strlen(tmp) == 0)) { - rc = -EINVAL; - goto out; - } - - card->info.portname[0] = strlen(tmp); - /* for beauty reasons */ - for (i = 1; i < 9; i++) - card->info.portname[i] = ' '; - strcpy(card->info.portname + 1, tmp); - ASCEBC(card->info.portname + 1, 8); -out: - mutex_unlock(&card->conf_mutex); - return rc ? rc : count; + dev_warn_once(&card->gdev->dev, + "portname is deprecated and is ignored\n"); + return count; } static DEVICE_ATTR(portname, 0644, qeth_dev_portname_show, diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index a8556692f632..dc905b37aa12 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -252,6 +252,23 @@ static inline int qeth_l2_get_cast_type(struct qeth_card *card, return RTN_UNSPEC; } +static inline void qeth_l2_hdr_csum(struct qeth_card *card, + struct qeth_hdr *hdr, struct sk_buff *skb) +{ + struct iphdr *iph = ip_hdr(skb); + + /* tcph->check contains already the pseudo hdr checksum + * so just set the header flags + */ + if (iph->protocol == IPPROTO_UDP) + hdr->hdr.l2.flags[1] |= QETH_HDR_EXT_UDP; + hdr->hdr.l2.flags[1] |= QETH_HDR_EXT_CSUM_TRANSP_REQ | + QETH_HDR_EXT_CSUM_HDR_REQ; + iph->check = 0; + if (card->options.performance_stats) + card->perf_stats.tx_csum++; +} + static void qeth_l2_fill_header(struct qeth_card *card, struct qeth_hdr *hdr, struct sk_buff *skb, int cast_type) { @@ -390,6 +407,38 @@ static int qeth_l2_vlan_rx_kill_vid(struct net_device *dev, return rc; } +static netdev_features_t qeth_l2_fix_features(struct net_device *dev, + netdev_features_t features) +{ + struct qeth_card *card = dev->ml_priv; + + QETH_DBF_TEXT(SETUP, 2, "fixfeat"); + if (!qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM)) + features &= ~NETIF_F_IP_CSUM; + if (!qeth_is_supported(card, IPA_INBOUND_CHECKSUM)) + features &= ~NETIF_F_RXCSUM; + QETH_DBF_HEX(SETUP, 2, &features, sizeof(features)); + return features; +} + +static int qeth_l2_set_features(struct net_device *dev, + netdev_features_t features) +{ + struct qeth_card *card = dev->ml_priv; + netdev_features_t changed = dev->features ^ features; + + QETH_DBF_TEXT(SETUP, 2, "setfeat"); + QETH_DBF_HEX(SETUP, 2, &features, sizeof(features)); + + if (card->state == CARD_STATE_DOWN || + card->state == CARD_STATE_RECOVER) + return 0; + + if (!(changed & NETIF_F_RXCSUM)) + return 0; + return qeth_set_rx_csum(card, features & NETIF_F_RXCSUM ? 1 : 0); +} + static void qeth_l2_stop_card(struct qeth_card *card, int recovery_mode) { QETH_DBF_TEXT(SETUP , 2, "stopcard"); @@ -450,7 +499,15 @@ static int qeth_l2_process_inbound_buffer(struct qeth_card *card, case QETH_HEADER_TYPE_LAYER2: skb->pkt_type = PACKET_HOST; skb->protocol = eth_type_trans(skb, skb->dev); - skb->ip_summed = CHECKSUM_NONE; + if ((card->dev->features & NETIF_F_RXCSUM) + && ((hdr->hdr.l2.flags[1] & + (QETH_HDR_EXT_CSUM_HDR_REQ | + QETH_HDR_EXT_CSUM_TRANSP_REQ)) == + (QETH_HDR_EXT_CSUM_HDR_REQ | + QETH_HDR_EXT_CSUM_TRANSP_REQ))) + skb->ip_summed = CHECKSUM_UNNECESSARY; + else + skb->ip_summed = CHECKSUM_NONE; if (skb->protocol == htons(ETH_P_802_2)) *((__u32 *)skb->cb) = ++card->seqno.pkt_seqno; len = skb->len; @@ -803,6 +860,8 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) sizeof(struct qeth_hdr)); skb_set_mac_header(new_skb, sizeof(struct qeth_hdr)); qeth_l2_fill_header(card, hdr, new_skb, cast_type); + if (new_skb->ip_summed == CHECKSUM_PARTIAL) + qeth_l2_hdr_csum(card, hdr, new_skb); } } @@ -968,6 +1027,8 @@ static const struct net_device_ops qeth_l2_netdev_ops = { .ndo_vlan_rx_add_vid = qeth_l2_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = qeth_l2_vlan_rx_kill_vid, .ndo_tx_timeout = qeth_tx_timeout, + .ndo_fix_features = qeth_l2_fix_features, + .ndo_set_features = qeth_l2_set_features }; static int qeth_l2_setup_netdev(struct qeth_card *card) @@ -997,6 +1058,11 @@ static int qeth_l2_setup_netdev(struct qeth_card *card) (card->info.type != QETH_CARD_TYPE_OSN) ? &qeth_l2_ethtool_ops : &qeth_l2_osn_ops; card->dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER; + if (card->info.type == QETH_CARD_TYPE_OSD && !card->info.guestlan) { + card->dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM; + /* Turn on RX offloading per default */ + card->dev->features |= NETIF_F_RXCSUM; + } card->info.broadcast_capable = 1; qeth_l2_request_initial_mac(card); SET_NETDEV_DEV(card->dev, &card->gdev->dev); @@ -1004,6 +1070,17 @@ static int qeth_l2_setup_netdev(struct qeth_card *card) return register_netdev(card->dev); } +static int qeth_l2_start_ipassists(struct qeth_card *card) +{ + /* configure isolation level */ + if (qeth_set_access_ctrl_online(card, 0)) + return -ENODEV; + if (qeth_is_supported(card, IPA_INBOUND_CHECKSUM)) + qeth_set_rx_csum(card, 1); + qeth_start_ipa_tx_checksum(card); + return 0; +} + static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode) { struct qeth_card *card = dev_get_drvdata(&gdev->dev); @@ -1069,12 +1146,8 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode) contin: if ((card->info.type == QETH_CARD_TYPE_OSD) || (card->info.type == QETH_CARD_TYPE_OSX)) { - /* configure isolation level */ - rc = qeth_set_access_ctrl_online(card, 0); - if (rc) { - rc = -ENODEV; + if (qeth_l2_start_ipassists(card)) goto out_remove; - } } if (card->info.type != QETH_CARD_TYPE_OSN && @@ -1452,8 +1525,8 @@ static void qeth_bridge_emit_host_event(struct qeth_card *card, env[i] = str[i]; i++; } if (code & IPA_ADDR_CHANGE_CODE_MACADDR) { - snprintf(str[i], sizeof(str[i]), "MAC=%pM6", - &addr_lnid->mac); + snprintf(str[i], sizeof(str[i]), "MAC=%pM", + addr_lnid->mac); env[i] = str[i]; i++; } snprintf(str[i], sizeof(str[i]), "NTOK_BUSID=%x.%x.%04x", diff --git a/drivers/s390/net/qeth_l2_sys.c b/drivers/s390/net/qeth_l2_sys.c index 52673cd1db99..692db49e3d2a 100644 --- a/drivers/s390/net/qeth_l2_sys.c +++ b/drivers/s390/net/qeth_l2_sys.c @@ -109,7 +109,7 @@ static ssize_t qeth_bridge_port_state_show(struct device *dev, return qeth_bridge_port_role_state_show(dev, attr, buf, 1); } -static DEVICE_ATTR(bridge_state, 0644, qeth_bridge_port_state_show, +static DEVICE_ATTR(bridge_state, 0444, qeth_bridge_port_state_show, NULL); static ssize_t qeth_bridgeport_hostnotification_show(struct device *dev, diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index a1aaa36e9ebb..543960e96b42 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -1065,27 +1065,6 @@ static struct qeth_cmd_buffer *qeth_l3_get_setassparms_cmd( return iob; } -static int qeth_l3_send_setassparms(struct qeth_card *card, - struct qeth_cmd_buffer *iob, __u16 len, long data, - int (*reply_cb)(struct qeth_card *, struct qeth_reply *, - unsigned long), - void *reply_param) -{ - int rc; - struct qeth_ipa_cmd *cmd; - - QETH_CARD_TEXT(card, 4, "sendassp"); - - cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); - if (len <= sizeof(__u32)) - cmd->data.setassparms.data.flags_32bit = (__u32) data; - else /* (len > sizeof(__u32)) */ - memcpy(&cmd->data.setassparms.data, (void *) data, len); - - rc = qeth_send_ipa_cmd(card, iob, reply_cb, reply_param); - return rc; -} - #ifdef CONFIG_QETH_IPV6 static int qeth_l3_send_simple_setassparms_ipv6(struct qeth_card *card, enum qeth_ipa_funcs ipa_func, __u16 cmd_code) @@ -1098,31 +1077,12 @@ static int qeth_l3_send_simple_setassparms_ipv6(struct qeth_card *card, 0, QETH_PROT_IPV6); if (!iob) return -ENOMEM; - rc = qeth_l3_send_setassparms(card, iob, 0, 0, + rc = qeth_send_setassparms(card, iob, 0, 0, qeth_l3_default_setassparms_cb, NULL); return rc; } #endif -static int qeth_l3_send_simple_setassparms(struct qeth_card *card, - enum qeth_ipa_funcs ipa_func, __u16 cmd_code, long data) -{ - int rc; - int length = 0; - struct qeth_cmd_buffer *iob; - - QETH_CARD_TEXT(card, 4, "simassp4"); - if (data) - length = sizeof(__u32); - iob = qeth_l3_get_setassparms_cmd(card, ipa_func, cmd_code, - length, QETH_PROT_IPV4); - if (!iob) - return -ENOMEM; - rc = qeth_l3_send_setassparms(card, iob, length, data, - qeth_l3_default_setassparms_cb, NULL); - return rc; -} - static int qeth_l3_start_ipa_arp_processing(struct qeth_card *card) { int rc; @@ -1135,8 +1095,8 @@ static int qeth_l3_start_ipa_arp_processing(struct qeth_card *card) QETH_CARD_IFNAME(card)); return 0; } - rc = qeth_l3_send_simple_setassparms(card, IPA_ARP_PROCESSING, - IPA_CMD_ASS_START, 0); + rc = qeth_send_simple_setassparms(card, IPA_ARP_PROCESSING, + IPA_CMD_ASS_START, 0); if (rc) { dev_warn(&card->gdev->dev, "Starting ARP processing support for %s failed\n", @@ -1158,7 +1118,7 @@ static int qeth_l3_start_ipa_ip_fragmentation(struct qeth_card *card) return -EOPNOTSUPP; } - rc = qeth_l3_send_simple_setassparms(card, IPA_IP_FRAGMENTATION, + rc = qeth_send_simple_setassparms(card, IPA_IP_FRAGMENTATION, IPA_CMD_ASS_START, 0); if (rc) { dev_warn(&card->gdev->dev, @@ -1183,7 +1143,7 @@ static int qeth_l3_start_ipa_source_mac(struct qeth_card *card) return -EOPNOTSUPP; } - rc = qeth_l3_send_simple_setassparms(card, IPA_SOURCE_MAC, + rc = qeth_send_simple_setassparms(card, IPA_SOURCE_MAC, IPA_CMD_ASS_START, 0); if (rc) dev_warn(&card->gdev->dev, @@ -1204,7 +1164,7 @@ static int qeth_l3_start_ipa_vlan(struct qeth_card *card) return -EOPNOTSUPP; } - rc = qeth_l3_send_simple_setassparms(card, IPA_VLAN_PRIO, + rc = qeth_send_simple_setassparms(card, IPA_VLAN_PRIO, IPA_CMD_ASS_START, 0); if (rc) { dev_warn(&card->gdev->dev, @@ -1229,7 +1189,7 @@ static int qeth_l3_start_ipa_multicast(struct qeth_card *card) return -EOPNOTSUPP; } - rc = qeth_l3_send_simple_setassparms(card, IPA_MULTICASTING, + rc = qeth_send_simple_setassparms(card, IPA_MULTICASTING, IPA_CMD_ASS_START, 0); if (rc) { dev_warn(&card->gdev->dev, @@ -1259,7 +1219,7 @@ static int qeth_l3_softsetup_ipv6(struct qeth_card *card) QETH_CARD_IFNAME(card)); return rc; } - rc = qeth_l3_send_simple_setassparms(card, IPA_IPV6, + rc = qeth_send_simple_setassparms(card, IPA_IPV6, IPA_CMD_ASS_START, 3); if (rc) { dev_err(&card->gdev->dev, @@ -1319,7 +1279,7 @@ static int qeth_l3_start_ipa_broadcast(struct qeth_card *card) rc = -EOPNOTSUPP; goto out; } - rc = qeth_l3_send_simple_setassparms(card, IPA_FILTERING, + rc = qeth_send_simple_setassparms(card, IPA_FILTERING, IPA_CMD_ASS_START, 0); if (rc) { dev_warn(&card->gdev->dev, "Enabling broadcast filtering for " @@ -1327,7 +1287,7 @@ static int qeth_l3_start_ipa_broadcast(struct qeth_card *card) goto out; } - rc = qeth_l3_send_simple_setassparms(card, IPA_FILTERING, + rc = qeth_send_simple_setassparms(card, IPA_FILTERING, IPA_CMD_ASS_CONFIGURE, 1); if (rc) { dev_warn(&card->gdev->dev, @@ -1337,7 +1297,7 @@ static int qeth_l3_start_ipa_broadcast(struct qeth_card *card) } card->info.broadcast_capable = QETH_BROADCAST_WITH_ECHO; dev_info(&card->gdev->dev, "Broadcast enabled\n"); - rc = qeth_l3_send_simple_setassparms(card, IPA_FILTERING, + rc = qeth_send_simple_setassparms(card, IPA_FILTERING, IPA_CMD_ASS_ENABLE, 1); if (rc) { dev_warn(&card->gdev->dev, "Setting up broadcast echo " @@ -1353,84 +1313,18 @@ out: return rc; } -static int qeth_l3_send_checksum_command(struct qeth_card *card) -{ - int rc; - - rc = qeth_l3_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM, - IPA_CMD_ASS_START, 0); - if (rc) { - dev_warn(&card->gdev->dev, "Starting HW checksumming for %s " - "failed, using SW checksumming\n", - QETH_CARD_IFNAME(card)); - return rc; - } - rc = qeth_l3_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM, - IPA_CMD_ASS_ENABLE, - card->info.csum_mask); - if (rc) { - dev_warn(&card->gdev->dev, "Enabling HW checksumming for %s " - "failed, using SW checksumming\n", - QETH_CARD_IFNAME(card)); - return rc; - } - return 0; -} - -static int qeth_l3_set_rx_csum(struct qeth_card *card, int on) -{ - int rc = 0; - - if (on) { - rc = qeth_l3_send_checksum_command(card); - if (rc) - return -EIO; - dev_info(&card->gdev->dev, - "HW Checksumming (inbound) enabled\n"); - } else { - rc = qeth_l3_send_simple_setassparms(card, - IPA_INBOUND_CHECKSUM, IPA_CMD_ASS_STOP, 0); - if (rc) - return -EIO; - } - - return 0; -} - -static int qeth_l3_start_ipa_checksum(struct qeth_card *card) +static void qeth_l3_start_ipa_checksum(struct qeth_card *card) { QETH_CARD_TEXT(card, 3, "strtcsum"); - - if (card->dev->features & NETIF_F_RXCSUM) { - rtnl_lock(); - /* force set_features call */ - card->dev->features &= ~NETIF_F_RXCSUM; - netdev_update_features(card->dev); - rtnl_unlock(); - } - return 0; + if (qeth_is_supported(card, IPA_INBOUND_CHECKSUM) + && (card->dev->features & NETIF_F_RXCSUM)) + qeth_set_rx_csum(card, 1); } -static int qeth_l3_start_ipa_tx_checksum(struct qeth_card *card) +static void qeth_l3_start_ipa_tx_checksum(struct qeth_card *card) { - int rc = 0; - - if (!qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM)) - return rc; - rc = qeth_l3_send_simple_setassparms(card, IPA_OUTBOUND_CHECKSUM, - IPA_CMD_ASS_START, 0); - if (rc) - goto err_out; - rc = qeth_l3_send_simple_setassparms(card, IPA_OUTBOUND_CHECKSUM, - IPA_CMD_ASS_ENABLE, card->info.tx_csum_mask); - if (rc) - goto err_out; - dev_info(&card->gdev->dev, "HW TX Checksumming enabled\n"); - return rc; -err_out: - dev_warn(&card->gdev->dev, "Enabling HW TX checksumming for %s " - "failed, using SW TX checksumming\n", QETH_CARD_IFNAME(card)); - return rc; + QETH_CARD_TEXT(card, 3, "strttxcs"); + qeth_start_ipa_tx_checksum(card); } static int qeth_l3_start_ipa_tso(struct qeth_card *card) @@ -1445,8 +1339,8 @@ static int qeth_l3_start_ipa_tso(struct qeth_card *card) QETH_CARD_IFNAME(card)); rc = -EOPNOTSUPP; } else { - rc = qeth_l3_send_simple_setassparms(card, IPA_OUTBOUND_TSO, - IPA_CMD_ASS_START, 0); + rc = qeth_send_simple_setassparms(card, IPA_OUTBOUND_TSO, + IPA_CMD_ASS_START, 0); if (rc) dev_warn(&card->gdev->dev, "Starting outbound TCP " "segmentation offload for %s failed\n", @@ -1950,7 +1844,6 @@ static inline int qeth_l3_rebuild_skb(struct qeth_card *card, skb->ip_summed = CHECKSUM_NONE; } else skb->ip_summed = CHECKSUM_NONE; - return is_vlan; } @@ -2287,7 +2180,7 @@ static int qeth_l3_arp_set_no_entries(struct qeth_card *card, int no_entries) if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) { return -EOPNOTSUPP; } - rc = qeth_l3_send_simple_setassparms(card, IPA_ARP_PROCESSING, + rc = qeth_send_simple_setassparms(card, IPA_ARP_PROCESSING, IPA_CMD_ASS_ARP_SET_NO_ENTRIES, no_entries); if (rc) { @@ -2552,7 +2445,7 @@ static int qeth_l3_arp_add_entry(struct qeth_card *card, QETH_PROT_IPV4); if (!iob) return -ENOMEM; - rc = qeth_l3_send_setassparms(card, iob, + rc = qeth_send_setassparms(card, iob, sizeof(struct qeth_arp_cache_entry), (unsigned long) entry, qeth_l3_default_setassparms_cb, NULL); @@ -2593,7 +2486,7 @@ static int qeth_l3_arp_remove_entry(struct qeth_card *card, QETH_PROT_IPV4); if (!iob) return -ENOMEM; - rc = qeth_l3_send_setassparms(card, iob, + rc = qeth_send_setassparms(card, iob, 12, (unsigned long)buf, qeth_l3_default_setassparms_cb, NULL); if (rc) { @@ -2624,7 +2517,7 @@ static int qeth_l3_arp_flush_cache(struct qeth_card *card) if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) { return -EOPNOTSUPP; } - rc = qeth_l3_send_simple_setassparms(card, IPA_ARP_PROCESSING, + rc = qeth_send_simple_setassparms(card, IPA_ARP_PROCESSING, IPA_CMD_ASS_ARP_FLUSH_CACHE, 0); if (rc) { tmp = rc; @@ -3187,7 +3080,6 @@ static netdev_features_t qeth_l3_fix_features(struct net_device *dev, features &= ~NETIF_F_TSO; if (!qeth_is_supported(card, IPA_INBOUND_CHECKSUM)) features &= ~NETIF_F_RXCSUM; - return features; } @@ -3204,7 +3096,7 @@ static int qeth_l3_set_features(struct net_device *dev, card->state == CARD_STATE_RECOVER) return 0; - return qeth_l3_set_rx_csum(card, features & NETIF_F_RXCSUM ? 1 : 0); + return qeth_set_rx_csum(card, features & NETIF_F_RXCSUM ? 1 : 0); } static const struct ethtool_ops qeth_l3_ethtool_ops = { diff --git a/drivers/s390/net/smsgiucv.c b/drivers/s390/net/smsgiucv.c index d8f990b6b332..a851d34c642b 100644 --- a/drivers/s390/net/smsgiucv.c +++ b/drivers/s390/net/smsgiucv.c @@ -49,7 +49,7 @@ static DEFINE_SPINLOCK(smsg_list_lock); static LIST_HEAD(smsg_list); static int iucv_path_connected; -static int smsg_path_pending(struct iucv_path *, u8 ipvmid[8], u8 ipuser[16]); +static int smsg_path_pending(struct iucv_path *, u8 *, u8 *); static void smsg_message_pending(struct iucv_path *, struct iucv_message *); static struct iucv_handler smsg_handler = { @@ -57,8 +57,7 @@ static struct iucv_handler smsg_handler = { .message_pending = smsg_message_pending, }; -static int smsg_path_pending(struct iucv_path *path, u8 ipvmid[8], - u8 ipuser[16]) +static int smsg_path_pending(struct iucv_path *path, u8 *ipvmid, u8 *ipuser) { if (strncmp(ipvmid, "*MSG ", 8) != 0) return -EINVAL; diff --git a/drivers/tty/hvc/hvc_iucv.c b/drivers/tty/hvc/hvc_iucv.c index bb809cf36617..8b70a1627356 100644 --- a/drivers/tty/hvc/hvc_iucv.c +++ b/drivers/tty/hvc/hvc_iucv.c @@ -88,8 +88,8 @@ struct iucv_tty_buffer { }; /* IUCV callback handler */ -static int hvc_iucv_path_pending(struct iucv_path *, u8[8], u8[16]); -static void hvc_iucv_path_severed(struct iucv_path *, u8[16]); +static int hvc_iucv_path_pending(struct iucv_path *, u8 *, u8 *); +static void hvc_iucv_path_severed(struct iucv_path *, u8 *); static void hvc_iucv_msg_pending(struct iucv_path *, struct iucv_message *); static void hvc_iucv_msg_complete(struct iucv_path *, struct iucv_message *); @@ -782,8 +782,8 @@ static int hvc_iucv_filter_connreq(u8 ipvmid[8]) * * Locking: struct hvc_iucv_private->lock */ -static int hvc_iucv_path_pending(struct iucv_path *path, - u8 ipvmid[8], u8 ipuser[16]) +static int hvc_iucv_path_pending(struct iucv_path *path, u8 *ipvmid, + u8 *ipuser) { struct hvc_iucv_private *priv, *tmp; u8 wildcard[9] = "lnxhvc "; @@ -881,7 +881,7 @@ out_path_handled: * * Locking: struct hvc_iucv_private->lock */ -static void hvc_iucv_path_severed(struct iucv_path *path, u8 ipuser[16]) +static void hvc_iucv_path_severed(struct iucv_path *path, u8 *ipuser) { struct hvc_iucv_private *priv = path->private; diff --git a/include/linux/can/dev.h b/include/linux/can/dev.h index c3a9c8fc60fa..56dcadd83716 100644 --- a/include/linux/can/dev.h +++ b/include/linux/can/dev.h @@ -14,9 +14,10 @@ #define _CAN_DEV_H #include <linux/can.h> -#include <linux/can/netlink.h> #include <linux/can/error.h> #include <linux/can/led.h> +#include <linux/can/netlink.h> +#include <linux/netdevice.h> /* * CAN mode diff --git a/include/linux/can/led.h b/include/linux/can/led.h index 146de4506d21..2746f7c2f87d 100644 --- a/include/linux/can/led.h +++ b/include/linux/can/led.h @@ -11,6 +11,7 @@ #include <linux/if.h> #include <linux/leds.h> +#include <linux/netdevice.h> enum can_led_event { CAN_LED_EVENT_OPEN, diff --git a/include/linux/ieee802154.h b/include/linux/ieee802154.h index 1dc1f4ed4001..db01492814d3 100644 --- a/include/linux/ieee802154.h +++ b/include/linux/ieee802154.h @@ -205,6 +205,31 @@ enum { IEEE802154_SCAN_IN_PROGRESS = 0xfc, }; +/* frame control handling */ +#define IEEE802154_FCTL_FTYPE 0x0003 +#define IEEE802154_FCTL_INTRA_PAN 0x0040 + +#define IEEE802154_FTYPE_DATA 0x0001 + +/* + * ieee802154_is_data - check if type is IEEE802154_FTYPE_DATA + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee802154_is_data(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE802154_FCTL_FTYPE)) == + cpu_to_le16(IEEE802154_FTYPE_DATA); +} + +/** + * ieee802154_is_intra_pan - check if intra pan id communication + * @fc: frame control bytes in little-endian byteorder + */ +static inline bool ieee802154_is_intra_pan(__le16 fc) +{ + return fc & cpu_to_le16(IEEE802154_FCTL_INTRA_PAN); +} + /** * ieee802154_is_valid_psdu_len - check if psdu len is valid * available lengths: diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 937b97893d5f..fcb573be75d9 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -112,11 +112,11 @@ struct tcp_request_sock_ops; struct tcp_request_sock { struct inet_request_sock req; const struct tcp_request_sock_ops *af_specific; + struct skb_mstamp snt_synack; /* first SYNACK sent time */ bool tfo_listener; u32 txhash; u32 rcv_isn; u32 snt_isn; - u32 snt_synack; /* synack sent time */ u32 last_oow_ack_time; /* last SYNACK */ u32 rcv_nxt; /* the ack # by SYNACK. For * FastOpen it's the seq# diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h index a2f59ec98d24..eeae5eb58754 100644 --- a/include/net/6lowpan.h +++ b/include/net/6lowpan.h @@ -126,13 +126,19 @@ (((a)[6]) == 0xFF) && \ (((a)[7]) == 0xFF)) -#define LOWPAN_DISPATCH_IPV6 0x41 /* 01000001 = 65 */ -#define LOWPAN_DISPATCH_HC1 0x42 /* 01000010 = 66 */ -#define LOWPAN_DISPATCH_IPHC 0x60 /* 011xxxxx = ... */ -#define LOWPAN_DISPATCH_FRAG1 0xc0 /* 11000xxx */ -#define LOWPAN_DISPATCH_FRAGN 0xe0 /* 11100xxx */ +#define LOWPAN_DISPATCH_IPV6 0x41 /* 01000001 = 65 */ +#define LOWPAN_DISPATCH_IPHC 0x60 /* 011xxxxx = ... */ +#define LOWPAN_DISPATCH_IPHC_MASK 0xe0 -#define LOWPAN_DISPATCH_MASK 0xf8 /* 11111000 */ +static inline bool lowpan_is_ipv6(u8 dispatch) +{ + return dispatch == LOWPAN_DISPATCH_IPV6; +} + +static inline bool lowpan_is_iphc(u8 dispatch) +{ + return (dispatch & LOWPAN_DISPATCH_IPHC_MASK) == LOWPAN_DISPATCH_IPHC; +} #define LOWPAN_FRAG_TIMEOUT (HZ * 60) /* time-out 60 sec */ @@ -218,6 +224,19 @@ struct lowpan_priv *lowpan_priv(const struct net_device *dev) return netdev_priv(dev); } +struct lowpan_802154_cb { + u16 d_tag; + unsigned int d_size; + u8 d_offset; +}; + +static inline +struct lowpan_802154_cb *lowpan_802154_cb(const struct sk_buff *skb) +{ + BUILD_BUG_ON(sizeof(struct lowpan_802154_cb) > sizeof(skb->cb)); + return (struct lowpan_802154_cb *)skb->cb; +} + #ifdef DEBUG /* print data in line */ static inline void raw_dump_inline(const char *caller, char *msg, @@ -280,119 +299,6 @@ static inline void lowpan_push_hc_data(u8 **hc_ptr, const void *data, *hc_ptr += len; } -static inline u8 lowpan_addr_mode_size(const u8 addr_mode) -{ - static const u8 addr_sizes[] = { - [LOWPAN_IPHC_ADDR_00] = 16, - [LOWPAN_IPHC_ADDR_01] = 8, - [LOWPAN_IPHC_ADDR_02] = 2, - [LOWPAN_IPHC_ADDR_03] = 0, - }; - return addr_sizes[addr_mode]; -} - -static inline u8 lowpan_next_hdr_size(const u8 h_enc, u16 *uncomp_header) -{ - u8 ret = 1; - - if ((h_enc & LOWPAN_NHC_UDP_MASK) == LOWPAN_NHC_UDP_ID) { - *uncomp_header += sizeof(struct udphdr); - - switch (h_enc & LOWPAN_NHC_UDP_CS_P_11) { - case LOWPAN_NHC_UDP_CS_P_00: - ret += 4; - break; - case LOWPAN_NHC_UDP_CS_P_01: - case LOWPAN_NHC_UDP_CS_P_10: - ret += 3; - break; - case LOWPAN_NHC_UDP_CS_P_11: - ret++; - break; - default: - break; - } - - if (!(h_enc & LOWPAN_NHC_UDP_CS_C)) - ret += 2; - } - - return ret; -} - -/** - * lowpan_uncompress_size - returns skb->len size with uncompressed header - * @skb: sk_buff with 6lowpan header inside - * @datagram_offset: optional to get the datagram_offset value - * - * Returns the skb->len with uncompressed header - */ -static inline u16 -lowpan_uncompress_size(const struct sk_buff *skb, u16 *dgram_offset) -{ - u16 ret = 2, uncomp_header = sizeof(struct ipv6hdr); - u8 iphc0, iphc1, h_enc; - - iphc0 = skb_network_header(skb)[0]; - iphc1 = skb_network_header(skb)[1]; - - switch ((iphc0 & LOWPAN_IPHC_TF) >> 3) { - case 0: - ret += 4; - break; - case 1: - ret += 3; - break; - case 2: - ret++; - break; - default: - break; - } - - if (!(iphc0 & LOWPAN_IPHC_NH_C)) - ret++; - - if (!(iphc0 & 0x03)) - ret++; - - ret += lowpan_addr_mode_size((iphc1 & LOWPAN_IPHC_SAM) >> - LOWPAN_IPHC_SAM_BIT); - - if (iphc1 & LOWPAN_IPHC_M) { - switch ((iphc1 & LOWPAN_IPHC_DAM_11) >> - LOWPAN_IPHC_DAM_BIT) { - case LOWPAN_IPHC_DAM_00: - ret += 16; - break; - case LOWPAN_IPHC_DAM_01: - ret += 6; - break; - case LOWPAN_IPHC_DAM_10: - ret += 4; - break; - case LOWPAN_IPHC_DAM_11: - ret++; - break; - default: - break; - } - } else { - ret += lowpan_addr_mode_size((iphc1 & LOWPAN_IPHC_DAM_11) >> - LOWPAN_IPHC_DAM_BIT); - } - - if (iphc0 & LOWPAN_IPHC_NH_C) { - h_enc = skb_network_header(skb)[ret]; - ret += lowpan_next_hdr_size(h_enc, &uncomp_header); - } - - if (dgram_offset) - *dgram_offset = uncomp_header; - - return skb->len + uncomp_header - ret; -} - void lowpan_netdev_setup(struct net_device *dev, enum lowpan_lltypes lltype); int diff --git a/include/net/af_ieee802154.h b/include/net/af_ieee802154.h index 7d38e2ffd256..a5563d27a3eb 100644 --- a/include/net/af_ieee802154.h +++ b/include/net/af_ieee802154.h @@ -1,5 +1,5 @@ /* - * IEEE 802.15.4 inteface for userspace + * IEEE 802.15.4 interface for userspace * * Copyright 2007, 2008 Siemens AG * diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 38d8a34d3589..f5ade8573393 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -123,11 +123,22 @@ __printf(1, 2) void bt_info(const char *fmt, ...); __printf(1, 2) void bt_err(const char *fmt, ...); +__printf(1, 2) +void bt_err_ratelimited(const char *fmt, ...); #define BT_INFO(fmt, ...) bt_info(fmt "\n", ##__VA_ARGS__) #define BT_ERR(fmt, ...) bt_err(fmt "\n", ##__VA_ARGS__) #define BT_DBG(fmt, ...) pr_debug(fmt "\n", ##__VA_ARGS__) +#define BT_ERR_RATELIMITED(fmt, ...) bt_err_ratelimited(fmt "\n", ##__VA_ARGS__) + +#define bt_dev_info(hdev, fmt, ...) \ + BT_INFO("%s: " fmt, (hdev)->name, ##__VA_ARGS__) +#define bt_dev_err(hdev, fmt, ...) \ + BT_ERR("%s: " fmt, (hdev)->name, ##__VA_ARGS__) +#define bt_dev_dbg(hdev, fmt, ...) \ + BT_DBG("%s: " fmt, (hdev)->name, ##__VA_ARGS__) + /* Connection and socket states */ enum { BT_CONNECTED = 1, /* Equal to TCP_ESTABLISHED to make net code happy */ diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 9e1a59e01fa2..256e6734c1fb 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -987,6 +987,7 @@ int hci_resume_dev(struct hci_dev *hdev); int hci_reset_dev(struct hci_dev *hdev); int hci_dev_open(__u16 dev); int hci_dev_close(__u16 dev); +int hci_dev_do_close(struct hci_dev *hdev); int hci_dev_reset(__u16 dev); int hci_dev_reset_stat(__u16 dev); int hci_dev_cmd(unsigned int cmd, void __user *arg); diff --git a/include/net/iucv/iucv.h b/include/net/iucv/iucv.h index 0894ced31957..b867b0cf79e8 100644 --- a/include/net/iucv/iucv.h +++ b/include/net/iucv/iucv.h @@ -141,14 +141,14 @@ struct iucv_handler { * called is the order of the registration of the iucv handlers * to the base code. */ - int (*path_pending)(struct iucv_path *, u8 ipvmid[8], u8 ipuser[16]); + int (*path_pending)(struct iucv_path *, u8 *ipvmid, u8 *ipuser); /* * The path_complete function is called after an iucv interrupt * type 0x02 has been received for a path that has been established * for this handler with iucv_path_connect and got accepted by the * peer with iucv_path_accept. */ - void (*path_complete)(struct iucv_path *, u8 ipuser[16]); + void (*path_complete)(struct iucv_path *, u8 *ipuser); /* * The path_severed function is called after an iucv interrupt * type 0x03 has been received. The communication peer shutdown @@ -156,20 +156,20 @@ struct iucv_handler { * remaining messages can be received until a iucv_path_sever * shuts down the other end of the path as well. */ - void (*path_severed)(struct iucv_path *, u8 ipuser[16]); + void (*path_severed)(struct iucv_path *, u8 *ipuser); /* * The path_quiesced function is called after an icuv interrupt * type 0x04 has been received. The communication peer has quiesced * the path. Delivery of messages is stopped until iucv_path_resume * has been called. */ - void (*path_quiesced)(struct iucv_path *, u8 ipuser[16]); + void (*path_quiesced)(struct iucv_path *, u8 *ipuser); /* * The path_resumed function is called after an icuv interrupt * type 0x05 has been received. The communication peer has resumed * the path. */ - void (*path_resumed)(struct iucv_path *, u8 ipuser[16]); + void (*path_resumed)(struct iucv_path *, u8 *ipuser); /* * The message_pending function is called after an icuv interrupt * type 0x06 or type 0x07 has been received. A new message is @@ -256,7 +256,7 @@ static inline void iucv_path_free(struct iucv_path *path) * Returns the result of the CP IUCV call. */ int iucv_path_accept(struct iucv_path *path, struct iucv_handler *handler, - u8 userdata[16], void *private); + u8 *userdata, void *private); /** * iucv_path_connect @@ -274,7 +274,7 @@ int iucv_path_accept(struct iucv_path *path, struct iucv_handler *handler, * Returns the result of the CP IUCV call. */ int iucv_path_connect(struct iucv_path *path, struct iucv_handler *handler, - u8 userid[8], u8 system[8], u8 userdata[16], + u8 *userid, u8 *system, u8 *userdata, void *private); /** @@ -287,7 +287,7 @@ int iucv_path_connect(struct iucv_path *path, struct iucv_handler *handler, * * Returns the result from the CP IUCV call. */ -int iucv_path_quiesce(struct iucv_path *path, u8 userdata[16]); +int iucv_path_quiesce(struct iucv_path *path, u8 *userdata); /** * iucv_path_resume: @@ -299,7 +299,7 @@ int iucv_path_quiesce(struct iucv_path *path, u8 userdata[16]); * * Returns the result from the CP IUCV call. */ -int iucv_path_resume(struct iucv_path *path, u8 userdata[16]); +int iucv_path_resume(struct iucv_path *path, u8 *userdata); /** * iucv_path_sever @@ -310,7 +310,7 @@ int iucv_path_resume(struct iucv_path *path, u8 userdata[16]); * * Returns the result from the CP IUCV call. */ -int iucv_path_sever(struct iucv_path *path, u8 userdata[16]); +int iucv_path_sever(struct iucv_path *path, u8 *userdata); /** * iucv_message_purge diff --git a/include/net/mac802154.h b/include/net/mac802154.h index b7f99615224b..32bd7c0467d4 100644 --- a/include/net/mac802154.h +++ b/include/net/mac802154.h @@ -250,6 +250,21 @@ struct ieee802154_ops { }; /** + * ieee802154_get_fc_from_skb - get the frame control field from an skb + * @skb: skb where the frame control field will be get from + */ +static inline __le16 ieee802154_get_fc_from_skb(const struct sk_buff *skb) +{ + /* return some invalid fc on failure */ + if (unlikely(skb->mac_len < 2)) { + WARN_ON(1); + return cpu_to_le16(0); + } + + return (__force __le16)__get_unaligned_memmove16(skb_mac_header(skb)); +} + +/** * ieee802154_be64_to_le64 - copies and convert be64 to le64 * @le64_dst: le64 destination pointer * @be64_src: be64 source pointer diff --git a/include/net/tcp.h b/include/net/tcp.h index 0cab28cd43a9..5cf9672c13e2 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -565,6 +565,7 @@ bool tcp_schedule_loss_probe(struct sock *sk); /* tcp_input.c */ void tcp_resume_early_retransmit(struct sock *sk); void tcp_rearm_rto(struct sock *sk); +void tcp_synack_rtt_meas(struct sock *sk, struct request_sock *req); void tcp_reset(struct sock *sk); /* tcp_timer.c */ diff --git a/net/6lowpan/iphc.c b/net/6lowpan/iphc.c index 1e0071fdcf72..78c8a495b571 100644 --- a/net/6lowpan/iphc.c +++ b/net/6lowpan/iphc.c @@ -366,7 +366,18 @@ lowpan_header_decompress(struct sk_buff *skb, struct net_device *dev, return err; } - hdr.payload_len = htons(skb->len); + switch (lowpan_priv(dev)->lltype) { + case LOWPAN_LLTYPE_IEEE802154: + if (lowpan_802154_cb(skb)->d_size) + hdr.payload_len = htons(lowpan_802154_cb(skb)->d_size - + sizeof(struct ipv6hdr)); + else + hdr.payload_len = htons(skb->len); + break; + default: + hdr.payload_len = htons(skb->len); + break; + } pr_debug("skb headroom size = %d, data length = %d\n", skb_headroom(skb), skb->len); diff --git a/net/6lowpan/nhc_udp.c b/net/6lowpan/nhc_udp.c index c6bcaeb428ae..72d0b57eb6e5 100644 --- a/net/6lowpan/nhc_udp.c +++ b/net/6lowpan/nhc_udp.c @@ -71,7 +71,18 @@ static int udp_uncompress(struct sk_buff *skb, size_t needed) * here, we obtain the hint from the remaining size of the * frame */ - uh.len = htons(skb->len + sizeof(struct udphdr)); + switch (lowpan_priv(skb->dev)->lltype) { + case LOWPAN_LLTYPE_IEEE802154: + if (lowpan_802154_cb(skb)->d_size) + uh.len = htons(lowpan_802154_cb(skb)->d_size - + sizeof(struct ipv6hdr)); + else + uh.len = htons(skb->len + sizeof(struct udphdr)); + break; + default: + uh.len = htons(skb->len + sizeof(struct udphdr)); + break; + } pr_debug("uncompressed UDP length: src = %d", ntohs(uh.len)); /* replace the compressed UDP head by the uncompressed UDP diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index adcbc74c2432..a7cdd99ec3f1 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -693,7 +693,8 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt) hci_setup_event_mask(req); - if (hdev->commands[6] & 0x20) { + if (hdev->commands[6] & 0x20 && + !test_bit(HCI_QUIRK_BROKEN_STORED_LINK_KEY, &hdev->quirks)) { struct hci_cp_read_stored_link_key cp; bacpy(&cp.bdaddr, BDADDR_ANY); @@ -1548,7 +1549,7 @@ static void hci_pend_le_actions_clear(struct hci_dev *hdev) BT_DBG("All LE pending actions cleared"); } -static int hci_dev_do_close(struct hci_dev *hdev) +int hci_dev_do_close(struct hci_dev *hdev) { BT_DBG("%s %p", hdev->name, hdev); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 186041866315..8acec932123a 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4719,6 +4719,27 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, struct hci_conn *conn; bool match; u32 flags; + u8 *ptr, real_len; + + /* Find the end of the data in case the report contains padded zero + * bytes at the end causing an invalid length value. + * + * When data is NULL, len is 0 so there is no need for extra ptr + * check as 'ptr < data + 0' is already false in such case. + */ + for (ptr = data; ptr < data + len && *ptr; ptr += *ptr + 1) { + if (ptr + 1 + *ptr > data + len) + break; + } + + real_len = ptr - data; + + /* Adjust for actual length */ + if (len != real_len) { + BT_ERR_RATELIMITED("%s advertising data length corrected", + hdev->name); + len = real_len; + } /* If the direct address is present, then this report is from * a LE Direct Advertising Report event. In that case it is diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index f2d30d1156c9..150556345263 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -503,7 +503,16 @@ static int hci_sock_release(struct socket *sock) if (hdev) { if (hci_pi(sk)->channel == HCI_CHANNEL_USER) { - hci_dev_close(hdev->id); + /* When releasing an user channel exclusive access, + * call hci_dev_do_close directly instead of calling + * hci_dev_close to ensure the exclusive access will + * be released and the controller brought back down. + * + * The checking of HCI_AUTO_OFF is not needed in this + * case since it will have been cleared already when + * opening the user channel. + */ + hci_dev_do_close(hdev); hci_dev_clear_flag(hdev, HCI_USER_CHANNEL); mgmt_index_added(hdev); } diff --git a/net/bluetooth/lib.c b/net/bluetooth/lib.c index b36bc0415854..8b4cdce3f62e 100644 --- a/net/bluetooth/lib.c +++ b/net/bluetooth/lib.c @@ -166,3 +166,19 @@ void bt_err(const char *format, ...) va_end(args); } EXPORT_SYMBOL(bt_err); + +void bt_err_ratelimited(const char *format, ...) +{ + struct va_format vaf; + va_list args; + + va_start(args, format); + + vaf.fmt = format; + vaf.va = &args; + + pr_err_ratelimited("%pV", &vaf); + + va_end(args); +} +EXPORT_SYMBOL(bt_err_ratelimited); diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index ad82324f710f..4d56e593faad 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -495,7 +495,7 @@ static int smp_ah(struct crypto_blkcipher *tfm, const u8 irk[16], } /* The output of the random address function ah is: - * ah(h, r) = e(k, r') mod 2^24 + * ah(k, r) = e(k, r') mod 2^24 * The output of the security function e is then truncated to 24 bits * by taking the least significant 24 bits of the output of e as the * result of ah. diff --git a/net/ieee802154/6lowpan/6lowpan_i.h b/net/ieee802154/6lowpan/6lowpan_i.h index ea339fa94c27..b4e17a7c0df0 100644 --- a/net/ieee802154/6lowpan/6lowpan_i.h +++ b/net/ieee802154/6lowpan/6lowpan_i.h @@ -7,6 +7,15 @@ #include <net/inet_frag.h> #include <net/6lowpan.h> +typedef unsigned __bitwise__ lowpan_rx_result; +#define RX_CONTINUE ((__force lowpan_rx_result) 0u) +#define RX_DROP_UNUSABLE ((__force lowpan_rx_result) 1u) +#define RX_DROP ((__force lowpan_rx_result) 2u) +#define RX_QUEUED ((__force lowpan_rx_result) 3u) + +#define LOWPAN_DISPATCH_FRAG1 0xc0 +#define LOWPAN_DISPATCH_FRAGN 0xe0 + struct lowpan_create_arg { u16 tag; u16 d_size; @@ -40,7 +49,7 @@ static inline u32 ieee802154_addr_hash(const struct ieee802154_addr *a) /* private device info */ struct lowpan_dev_info { - struct net_device *real_dev; /* real WPAN device ptr */ + struct net_device *wdev; /* wpan device ptr */ u16 fragment_tag; }; @@ -62,4 +71,7 @@ int lowpan_header_create(struct sk_buff *skb, struct net_device *dev, const void *_saddr, unsigned int len); netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev); +int lowpan_iphc_decompress(struct sk_buff *skb); +lowpan_rx_result lowpan_rx_h_ipv6(struct sk_buff *skb); + #endif /* __IEEE802154_6LOWPAN_I_H__ */ diff --git a/net/ieee802154/6lowpan/core.c b/net/ieee802154/6lowpan/core.c index 953b1c49f5d1..9f0cfa598e3a 100644 --- a/net/ieee802154/6lowpan/core.c +++ b/net/ieee802154/6lowpan/core.c @@ -61,7 +61,7 @@ static struct header_ops lowpan_header_ops = { static struct lock_class_key lowpan_tx_busylock; static struct lock_class_key lowpan_netdev_xmit_lock_key; -static void lowpan_set_lockdep_class_one(struct net_device *dev, +static void lowpan_set_lockdep_class_one(struct net_device *ldev, struct netdev_queue *txq, void *_unused) { @@ -69,35 +69,52 @@ static void lowpan_set_lockdep_class_one(struct net_device *dev, &lowpan_netdev_xmit_lock_key); } -static int lowpan_dev_init(struct net_device *dev) +static int lowpan_dev_init(struct net_device *ldev) { - netdev_for_each_tx_queue(dev, lowpan_set_lockdep_class_one, NULL); - dev->qdisc_tx_busylock = &lowpan_tx_busylock; + netdev_for_each_tx_queue(ldev, lowpan_set_lockdep_class_one, NULL); + ldev->qdisc_tx_busylock = &lowpan_tx_busylock; + return 0; +} + +static int lowpan_open(struct net_device *dev) +{ + if (!open_count) + lowpan_rx_init(); + open_count++; + return 0; +} + +static int lowpan_stop(struct net_device *dev) +{ + open_count--; + if (!open_count) + lowpan_rx_exit(); return 0; } static const struct net_device_ops lowpan_netdev_ops = { .ndo_init = lowpan_dev_init, .ndo_start_xmit = lowpan_xmit, + .ndo_open = lowpan_open, + .ndo_stop = lowpan_stop, }; -static void lowpan_setup(struct net_device *dev) +static void lowpan_setup(struct net_device *ldev) { - dev->addr_len = IEEE802154_ADDR_LEN; - memset(dev->broadcast, 0xff, IEEE802154_ADDR_LEN); - dev->type = ARPHRD_6LOWPAN; + ldev->addr_len = IEEE802154_ADDR_LEN; + memset(ldev->broadcast, 0xff, IEEE802154_ADDR_LEN); + ldev->type = ARPHRD_6LOWPAN; /* Frame Control + Sequence Number + Address fields + Security Header */ - dev->hard_header_len = 2 + 1 + 20 + 14; - dev->needed_tailroom = 2; /* FCS */ - dev->mtu = IPV6_MIN_MTU; - dev->priv_flags |= IFF_NO_QUEUE; - dev->flags = IFF_BROADCAST | IFF_MULTICAST; - dev->watchdog_timeo = 0; - - dev->netdev_ops = &lowpan_netdev_ops; - dev->header_ops = &lowpan_header_ops; - dev->destructor = free_netdev; - dev->features |= NETIF_F_NETNS_LOCAL; + ldev->hard_header_len = 2 + 1 + 20 + 14; + ldev->needed_tailroom = 2; /* FCS */ + ldev->mtu = IPV6_MIN_MTU; + ldev->priv_flags |= IFF_NO_QUEUE; + ldev->flags = IFF_BROADCAST | IFF_MULTICAST; + + ldev->netdev_ops = &lowpan_netdev_ops; + ldev->header_ops = &lowpan_header_ops; + ldev->destructor = free_netdev; + ldev->features |= NETIF_F_NETNS_LOCAL; } static int lowpan_validate(struct nlattr *tb[], struct nlattr *data[]) @@ -109,10 +126,10 @@ static int lowpan_validate(struct nlattr *tb[], struct nlattr *data[]) return 0; } -static int lowpan_newlink(struct net *src_net, struct net_device *dev, +static int lowpan_newlink(struct net *src_net, struct net_device *ldev, struct nlattr *tb[], struct nlattr *data[]) { - struct net_device *real_dev; + struct net_device *wdev; int ret; ASSERT_RTNL(); @@ -120,58 +137,47 @@ static int lowpan_newlink(struct net *src_net, struct net_device *dev, pr_debug("adding new link\n"); if (!tb[IFLA_LINK] || - !net_eq(dev_net(dev), &init_net)) + !net_eq(dev_net(ldev), &init_net)) return -EINVAL; - /* find and hold real wpan device */ - real_dev = dev_get_by_index(dev_net(dev), nla_get_u32(tb[IFLA_LINK])); - if (!real_dev) + /* find and hold wpan device */ + wdev = dev_get_by_index(dev_net(ldev), nla_get_u32(tb[IFLA_LINK])); + if (!wdev) return -ENODEV; - if (real_dev->type != ARPHRD_IEEE802154) { - dev_put(real_dev); + if (wdev->type != ARPHRD_IEEE802154) { + dev_put(wdev); return -EINVAL; } - if (real_dev->ieee802154_ptr->lowpan_dev) { - dev_put(real_dev); + if (wdev->ieee802154_ptr->lowpan_dev) { + dev_put(wdev); return -EBUSY; } - lowpan_dev_info(dev)->real_dev = real_dev; + lowpan_dev_info(ldev)->wdev = wdev; /* Set the lowpan hardware address to the wpan hardware address. */ - memcpy(dev->dev_addr, real_dev->dev_addr, IEEE802154_ADDR_LEN); + memcpy(ldev->dev_addr, wdev->dev_addr, IEEE802154_ADDR_LEN); - lowpan_netdev_setup(dev, LOWPAN_LLTYPE_IEEE802154); + lowpan_netdev_setup(ldev, LOWPAN_LLTYPE_IEEE802154); - ret = register_netdevice(dev); + ret = register_netdevice(ldev); if (ret < 0) { - dev_put(real_dev); + dev_put(wdev); return ret; } - real_dev->ieee802154_ptr->lowpan_dev = dev; - if (!open_count) - lowpan_rx_init(); - - open_count++; - + wdev->ieee802154_ptr->lowpan_dev = ldev; return 0; } -static void lowpan_dellink(struct net_device *dev, struct list_head *head) +static void lowpan_dellink(struct net_device *ldev, struct list_head *head) { - struct lowpan_dev_info *lowpan_dev = lowpan_dev_info(dev); - struct net_device *real_dev = lowpan_dev->real_dev; + struct net_device *wdev = lowpan_dev_info(ldev)->wdev; ASSERT_RTNL(); - open_count--; - - if (!open_count) - lowpan_rx_exit(); - - real_dev->ieee802154_ptr->lowpan_dev = NULL; - unregister_netdevice(dev); - dev_put(real_dev); + wdev->ieee802154_ptr->lowpan_dev = NULL; + unregister_netdevice(ldev); + dev_put(wdev); } static struct rtnl_link_ops lowpan_link_ops __read_mostly = { @@ -196,9 +202,9 @@ static inline void lowpan_netlink_fini(void) static int lowpan_device_event(struct notifier_block *unused, unsigned long event, void *ptr) { - struct net_device *dev = netdev_notifier_info_to_dev(ptr); + struct net_device *wdev = netdev_notifier_info_to_dev(ptr); - if (dev->type != ARPHRD_IEEE802154) + if (wdev->type != ARPHRD_IEEE802154) goto out; switch (event) { @@ -207,8 +213,8 @@ static int lowpan_device_event(struct notifier_block *unused, * also delete possible lowpan interfaces which belongs * to the wpan interface. */ - if (dev->ieee802154_ptr && dev->ieee802154_ptr->lowpan_dev) - lowpan_dellink(dev->ieee802154_ptr->lowpan_dev, NULL); + if (wdev->ieee802154_ptr->lowpan_dev) + lowpan_dellink(wdev->ieee802154_ptr->lowpan_dev, NULL); break; default: break; diff --git a/net/ieee802154/6lowpan/reassembly.c b/net/ieee802154/6lowpan/reassembly.c index 214d44aef35b..12e8cf4bda9f 100644 --- a/net/ieee802154/6lowpan/reassembly.c +++ b/net/ieee802154/6lowpan/reassembly.c @@ -32,21 +32,10 @@ static const char lowpan_frags_cache_name[] = "lowpan-frags"; -struct lowpan_frag_info { - u16 d_tag; - u16 d_size; - u8 d_offset; -}; - -static struct lowpan_frag_info *lowpan_cb(struct sk_buff *skb) -{ - return (struct lowpan_frag_info *)skb->cb; -} - static struct inet_frags lowpan_frags; static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, - struct sk_buff *prev, struct net_device *dev); + struct sk_buff *prev, struct net_device *ldev); static unsigned int lowpan_hash_frag(u16 tag, u16 d_size, const struct ieee802154_addr *saddr, @@ -111,7 +100,7 @@ out: } static inline struct lowpan_frag_queue * -fq_find(struct net *net, const struct lowpan_frag_info *frag_info, +fq_find(struct net *net, const struct lowpan_802154_cb *cb, const struct ieee802154_addr *src, const struct ieee802154_addr *dst) { @@ -121,12 +110,12 @@ fq_find(struct net *net, const struct lowpan_frag_info *frag_info, struct netns_ieee802154_lowpan *ieee802154_lowpan = net_ieee802154_lowpan(net); - arg.tag = frag_info->d_tag; - arg.d_size = frag_info->d_size; + arg.tag = cb->d_tag; + arg.d_size = cb->d_size; arg.src = src; arg.dst = dst; - hash = lowpan_hash_frag(frag_info->d_tag, frag_info->d_size, src, dst); + hash = lowpan_hash_frag(cb->d_tag, cb->d_size, src, dst); q = inet_frag_find(&ieee802154_lowpan->frags, &lowpan_frags, &arg, hash); @@ -138,17 +127,17 @@ fq_find(struct net *net, const struct lowpan_frag_info *frag_info, } static int lowpan_frag_queue(struct lowpan_frag_queue *fq, - struct sk_buff *skb, const u8 frag_type) + struct sk_buff *skb, u8 frag_type) { struct sk_buff *prev, *next; - struct net_device *dev; + struct net_device *ldev; int end, offset; if (fq->q.flags & INET_FRAG_COMPLETE) goto err; - offset = lowpan_cb(skb)->d_offset << 3; - end = lowpan_cb(skb)->d_size; + offset = lowpan_802154_cb(skb)->d_offset << 3; + end = lowpan_802154_cb(skb)->d_size; /* Is this the final fragment? */ if (offset + skb->len == end) { @@ -174,13 +163,16 @@ static int lowpan_frag_queue(struct lowpan_frag_queue *fq, * this fragment, right? */ prev = fq->q.fragments_tail; - if (!prev || lowpan_cb(prev)->d_offset < lowpan_cb(skb)->d_offset) { + if (!prev || + lowpan_802154_cb(prev)->d_offset < + lowpan_802154_cb(skb)->d_offset) { next = NULL; goto found; } prev = NULL; for (next = fq->q.fragments; next != NULL; next = next->next) { - if (lowpan_cb(next)->d_offset >= lowpan_cb(skb)->d_offset) + if (lowpan_802154_cb(next)->d_offset >= + lowpan_802154_cb(skb)->d_offset) break; /* bingo! */ prev = next; } @@ -195,18 +187,15 @@ found: else fq->q.fragments = skb; - dev = skb->dev; - if (dev) + ldev = skb->dev; + if (ldev) skb->dev = NULL; fq->q.stamp = skb->tstamp; - if (frag_type == LOWPAN_DISPATCH_FRAG1) { - /* Calculate uncomp. 6lowpan header to estimate full size */ - fq->q.meat += lowpan_uncompress_size(skb, NULL); + if (frag_type == LOWPAN_DISPATCH_FRAG1) fq->q.flags |= INET_FRAG_FIRST_IN; - } else { - fq->q.meat += skb->len; - } + + fq->q.meat += skb->len; add_frag_mem_limit(fq->q.net, skb->truesize); if (fq->q.flags == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) && @@ -215,7 +204,7 @@ found: unsigned long orefdst = skb->_skb_refdst; skb->_skb_refdst = 0UL; - res = lowpan_frag_reasm(fq, prev, dev); + res = lowpan_frag_reasm(fq, prev, ldev); skb->_skb_refdst = orefdst; return res; } @@ -235,7 +224,7 @@ err: * the last and the first frames arrived and all the bits are here. */ static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, struct sk_buff *prev, - struct net_device *dev) + struct net_device *ldev) { struct sk_buff *fp, *head = fq->q.fragments; int sum_truesize; @@ -313,7 +302,7 @@ static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, struct sk_buff *prev, sub_frag_mem_limit(fq->q.net, sum_truesize); head->next = NULL; - head->dev = dev; + head->dev = ldev; head->tstamp = fq->q.stamp; fq->q.fragments = NULL; @@ -325,24 +314,87 @@ out_oom: return -1; } -static int lowpan_get_frag_info(struct sk_buff *skb, const u8 frag_type, - struct lowpan_frag_info *frag_info) +static int lowpan_frag_rx_handlers_result(struct sk_buff *skb, + lowpan_rx_result res) +{ + switch (res) { + case RX_QUEUED: + return NET_RX_SUCCESS; + case RX_CONTINUE: + /* nobody cared about this packet */ + net_warn_ratelimited("%s: received unknown dispatch\n", + __func__); + + /* fall-through */ + default: + /* all others failure */ + return NET_RX_DROP; + } +} + +static lowpan_rx_result lowpan_frag_rx_h_iphc(struct sk_buff *skb) +{ + int ret; + + if (!lowpan_is_iphc(*skb_network_header(skb))) + return RX_CONTINUE; + + ret = lowpan_iphc_decompress(skb); + if (ret < 0) + return RX_DROP; + + return RX_QUEUED; +} + +static int lowpan_invoke_frag_rx_handlers(struct sk_buff *skb) +{ + lowpan_rx_result res; + +#define CALL_RXH(rxh) \ + do { \ + res = rxh(skb); \ + if (res != RX_CONTINUE) \ + goto rxh_next; \ + } while (0) + + /* likely at first */ + CALL_RXH(lowpan_frag_rx_h_iphc); + CALL_RXH(lowpan_rx_h_ipv6); + +rxh_next: + return lowpan_frag_rx_handlers_result(skb, res); +#undef CALL_RXH +} + +#define LOWPAN_FRAG_DGRAM_SIZE_HIGH_MASK 0x07 +#define LOWPAN_FRAG_DGRAM_SIZE_HIGH_SHIFT 8 + +static int lowpan_get_cb(struct sk_buff *skb, u8 frag_type, + struct lowpan_802154_cb *cb) { bool fail; - u8 pattern = 0, low = 0; + u8 high = 0, low = 0; __be16 d_tag = 0; - fail = lowpan_fetch_skb(skb, &pattern, 1); + fail = lowpan_fetch_skb(skb, &high, 1); fail |= lowpan_fetch_skb(skb, &low, 1); - frag_info->d_size = (pattern & 7) << 8 | low; + /* remove the dispatch value and use first three bits as high value + * for the datagram size + */ + cb->d_size = (high & LOWPAN_FRAG_DGRAM_SIZE_HIGH_MASK) << + LOWPAN_FRAG_DGRAM_SIZE_HIGH_SHIFT | low; fail |= lowpan_fetch_skb(skb, &d_tag, 2); - frag_info->d_tag = ntohs(d_tag); + cb->d_tag = ntohs(d_tag); if (frag_type == LOWPAN_DISPATCH_FRAGN) { - fail |= lowpan_fetch_skb(skb, &frag_info->d_offset, 1); + fail |= lowpan_fetch_skb(skb, &cb->d_offset, 1); } else { skb_reset_network_header(skb); - frag_info->d_offset = 0; + cb->d_offset = 0; + /* check if datagram_size has ipv6hdr on FRAG1 */ + fail |= cb->d_size < sizeof(struct ipv6hdr); + /* check if we can dereference the dispatch value */ + fail |= !skb->len; } if (unlikely(fail)) @@ -351,27 +403,33 @@ static int lowpan_get_frag_info(struct sk_buff *skb, const u8 frag_type, return 0; } -int lowpan_frag_rcv(struct sk_buff *skb, const u8 frag_type) +int lowpan_frag_rcv(struct sk_buff *skb, u8 frag_type) { struct lowpan_frag_queue *fq; struct net *net = dev_net(skb->dev); - struct lowpan_frag_info *frag_info = lowpan_cb(skb); - struct ieee802154_addr source, dest; + struct lowpan_802154_cb *cb = lowpan_802154_cb(skb); + struct ieee802154_hdr hdr; int err; - source = mac_cb(skb)->source; - dest = mac_cb(skb)->dest; + if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0) + goto err; - err = lowpan_get_frag_info(skb, frag_type, frag_info); + err = lowpan_get_cb(skb, frag_type, cb); if (err < 0) goto err; - if (frag_info->d_size > IPV6_MIN_MTU) { + if (frag_type == LOWPAN_DISPATCH_FRAG1) { + err = lowpan_invoke_frag_rx_handlers(skb); + if (err == NET_RX_DROP) + goto err; + } + + if (cb->d_size > IPV6_MIN_MTU) { net_warn_ratelimited("lowpan_frag_rcv: datagram size exceeds MTU\n"); goto err; } - fq = fq_find(net, frag_info, &source, &dest); + fq = fq_find(net, cb, &hdr.source, &hdr.dest); if (fq != NULL) { int ret; @@ -387,7 +445,6 @@ err: kfree_skb(skb); return -1; } -EXPORT_SYMBOL(lowpan_frag_rcv); #ifdef CONFIG_SYSCTL static int zero; diff --git a/net/ieee802154/6lowpan/rx.c b/net/ieee802154/6lowpan/rx.c index 12e10201d263..b1fd47d2802b 100644 --- a/net/ieee802154/6lowpan/rx.c +++ b/net/ieee802154/6lowpan/rx.c @@ -11,40 +11,99 @@ #include <linux/if_arp.h> #include <net/6lowpan.h> +#include <net/mac802154.h> #include <net/ieee802154_netdev.h> #include "6lowpan_i.h" -static int lowpan_give_skb_to_device(struct sk_buff *skb, - struct net_device *dev) +#define LOWPAN_DISPATCH_FIRST 0xc0 +#define LOWPAN_DISPATCH_FRAG_MASK 0xf8 + +#define LOWPAN_DISPATCH_NALP 0x00 +#define LOWPAN_DISPATCH_ESC 0x40 +#define LOWPAN_DISPATCH_HC1 0x42 +#define LOWPAN_DISPATCH_DFF 0x43 +#define LOWPAN_DISPATCH_BC0 0x50 +#define LOWPAN_DISPATCH_MESH 0x80 + +static int lowpan_give_skb_to_device(struct sk_buff *skb) { - skb->dev = dev->ieee802154_ptr->lowpan_dev; skb->protocol = htons(ETH_P_IPV6); - skb->pkt_type = PACKET_HOST; return netif_rx(skb); } -static int -iphc_decompress(struct sk_buff *skb, const struct ieee802154_hdr *hdr) +static int lowpan_rx_handlers_result(struct sk_buff *skb, lowpan_rx_result res) +{ + switch (res) { + case RX_CONTINUE: + /* nobody cared about this packet */ + net_warn_ratelimited("%s: received unknown dispatch\n", + __func__); + + /* fall-through */ + case RX_DROP_UNUSABLE: + kfree_skb(skb); + + /* fall-through */ + case RX_DROP: + return NET_RX_DROP; + case RX_QUEUED: + return lowpan_give_skb_to_device(skb); + default: + break; + } + + return NET_RX_DROP; +} + +static inline bool lowpan_is_frag1(u8 dispatch) +{ + return (dispatch & LOWPAN_DISPATCH_FRAG_MASK) == LOWPAN_DISPATCH_FRAG1; +} + +static inline bool lowpan_is_fragn(u8 dispatch) +{ + return (dispatch & LOWPAN_DISPATCH_FRAG_MASK) == LOWPAN_DISPATCH_FRAGN; +} + +static lowpan_rx_result lowpan_rx_h_frag(struct sk_buff *skb) +{ + int ret; + + if (!(lowpan_is_frag1(*skb_network_header(skb)) || + lowpan_is_fragn(*skb_network_header(skb)))) + return RX_CONTINUE; + + ret = lowpan_frag_rcv(skb, *skb_network_header(skb) & + LOWPAN_DISPATCH_FRAG_MASK); + if (ret == 1) + return RX_QUEUED; + + /* Packet is freed by lowpan_frag_rcv on error or put into the frag + * bucket. + */ + return RX_DROP; +} + +int lowpan_iphc_decompress(struct sk_buff *skb) { - u8 iphc0, iphc1; struct ieee802154_addr_sa sa, da; + struct ieee802154_hdr hdr; + u8 iphc0, iphc1; void *sap, *dap; - raw_dump_table(__func__, "raw skb data dump", skb->data, skb->len); - /* at least two bytes will be used for the encoding */ - if (skb->len < 2) + if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0) return -EINVAL; - if (lowpan_fetch_skb_u8(skb, &iphc0)) - return -EINVAL; + raw_dump_table(__func__, "raw skb data dump", skb->data, skb->len); - if (lowpan_fetch_skb_u8(skb, &iphc1)) + if (lowpan_fetch_skb_u8(skb, &iphc0) || + lowpan_fetch_skb_u8(skb, &iphc1)) return -EINVAL; - ieee802154_addr_to_sa(&sa, &hdr->source); - ieee802154_addr_to_sa(&da, &hdr->dest); + ieee802154_addr_to_sa(&sa, &hdr.source); + ieee802154_addr_to_sa(&da, &hdr.dest); if (sa.addr_type == IEEE802154_ADDR_SHORT) sap = &sa.short_addr; @@ -61,77 +120,216 @@ iphc_decompress(struct sk_buff *skb, const struct ieee802154_hdr *hdr) IEEE802154_ADDR_LEN, iphc0, iphc1); } -static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, struct net_device *orig_dev) +static lowpan_rx_result lowpan_rx_h_iphc(struct sk_buff *skb) { - struct ieee802154_hdr hdr; int ret; - if (dev->type != ARPHRD_IEEE802154 || - !dev->ieee802154_ptr->lowpan_dev) - goto drop; + if (!lowpan_is_iphc(*skb_network_header(skb))) + return RX_CONTINUE; - skb = skb_share_check(skb, GFP_ATOMIC); - if (!skb) - goto drop; + /* Setting datagram_offset to zero indicates non frag handling + * while doing lowpan_header_decompress. + */ + lowpan_802154_cb(skb)->d_size = 0; - if (!netif_running(dev)) - goto drop_skb; + ret = lowpan_iphc_decompress(skb); + if (ret < 0) + return RX_DROP_UNUSABLE; - if (skb->pkt_type == PACKET_OTHERHOST) - goto drop_skb; + return RX_QUEUED; +} - if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0) - goto drop_skb; - - /* check that it's our buffer */ - if (skb->data[0] == LOWPAN_DISPATCH_IPV6) { - /* Pull off the 1-byte of 6lowpan header. */ - skb_pull(skb, 1); - return lowpan_give_skb_to_device(skb, dev); - } else { - switch (skb->data[0] & 0xe0) { - case LOWPAN_DISPATCH_IPHC: /* ipv6 datagram */ - ret = iphc_decompress(skb, &hdr); - if (ret < 0) - goto drop_skb; - - return lowpan_give_skb_to_device(skb, dev); - case LOWPAN_DISPATCH_FRAG1: /* first fragment header */ - ret = lowpan_frag_rcv(skb, LOWPAN_DISPATCH_FRAG1); - if (ret == 1) { - ret = iphc_decompress(skb, &hdr); - if (ret < 0) - goto drop_skb; - - return lowpan_give_skb_to_device(skb, dev); - } else if (ret == -1) { - return NET_RX_DROP; - } else { - return NET_RX_SUCCESS; - } - case LOWPAN_DISPATCH_FRAGN: /* next fragments headers */ - ret = lowpan_frag_rcv(skb, LOWPAN_DISPATCH_FRAGN); - if (ret == 1) { - ret = iphc_decompress(skb, &hdr); - if (ret < 0) - goto drop_skb; - - return lowpan_give_skb_to_device(skb, dev); - } else if (ret == -1) { - return NET_RX_DROP; - } else { - return NET_RX_SUCCESS; - } - default: - break; - } +lowpan_rx_result lowpan_rx_h_ipv6(struct sk_buff *skb) +{ + if (!lowpan_is_ipv6(*skb_network_header(skb))) + return RX_CONTINUE; + + /* Pull off the 1-byte of 6lowpan header. */ + skb_pull(skb, 1); + return RX_QUEUED; +} + +static inline bool lowpan_is_esc(u8 dispatch) +{ + return dispatch == LOWPAN_DISPATCH_ESC; +} + +static lowpan_rx_result lowpan_rx_h_esc(struct sk_buff *skb) +{ + if (!lowpan_is_esc(*skb_network_header(skb))) + return RX_CONTINUE; + + net_warn_ratelimited("%s: %s\n", skb->dev->name, + "6LoWPAN ESC not supported\n"); + + return RX_DROP_UNUSABLE; +} + +static inline bool lowpan_is_hc1(u8 dispatch) +{ + return dispatch == LOWPAN_DISPATCH_HC1; +} + +static lowpan_rx_result lowpan_rx_h_hc1(struct sk_buff *skb) +{ + if (!lowpan_is_hc1(*skb_network_header(skb))) + return RX_CONTINUE; + + net_warn_ratelimited("%s: %s\n", skb->dev->name, + "6LoWPAN HC1 not supported\n"); + + return RX_DROP_UNUSABLE; +} + +static inline bool lowpan_is_dff(u8 dispatch) +{ + return dispatch == LOWPAN_DISPATCH_DFF; +} + +static lowpan_rx_result lowpan_rx_h_dff(struct sk_buff *skb) +{ + if (!lowpan_is_dff(*skb_network_header(skb))) + return RX_CONTINUE; + + net_warn_ratelimited("%s: %s\n", skb->dev->name, + "6LoWPAN DFF not supported\n"); + + return RX_DROP_UNUSABLE; +} + +static inline bool lowpan_is_bc0(u8 dispatch) +{ + return dispatch == LOWPAN_DISPATCH_BC0; +} + +static lowpan_rx_result lowpan_rx_h_bc0(struct sk_buff *skb) +{ + if (!lowpan_is_bc0(*skb_network_header(skb))) + return RX_CONTINUE; + + net_warn_ratelimited("%s: %s\n", skb->dev->name, + "6LoWPAN BC0 not supported\n"); + + return RX_DROP_UNUSABLE; +} + +static inline bool lowpan_is_mesh(u8 dispatch) +{ + return (dispatch & LOWPAN_DISPATCH_FIRST) == LOWPAN_DISPATCH_MESH; +} + +static lowpan_rx_result lowpan_rx_h_mesh(struct sk_buff *skb) +{ + if (!lowpan_is_mesh(*skb_network_header(skb))) + return RX_CONTINUE; + + net_warn_ratelimited("%s: %s\n", skb->dev->name, + "6LoWPAN MESH not supported\n"); + + return RX_DROP_UNUSABLE; +} + +static int lowpan_invoke_rx_handlers(struct sk_buff *skb) +{ + lowpan_rx_result res; + +#define CALL_RXH(rxh) \ + do { \ + res = rxh(skb); \ + if (res != RX_CONTINUE) \ + goto rxh_next; \ + } while (0) + + /* likely at first */ + CALL_RXH(lowpan_rx_h_iphc); + CALL_RXH(lowpan_rx_h_frag); + CALL_RXH(lowpan_rx_h_ipv6); + CALL_RXH(lowpan_rx_h_esc); + CALL_RXH(lowpan_rx_h_hc1); + CALL_RXH(lowpan_rx_h_dff); + CALL_RXH(lowpan_rx_h_bc0); + CALL_RXH(lowpan_rx_h_mesh); + +rxh_next: + return lowpan_rx_handlers_result(skb, res); +#undef CALL_RXH +} + +static inline bool lowpan_is_nalp(u8 dispatch) +{ + return (dispatch & LOWPAN_DISPATCH_FIRST) == LOWPAN_DISPATCH_NALP; +} + +/* Lookup for reserved dispatch values at: + * https://www.iana.org/assignments/_6lowpan-parameters/_6lowpan-parameters.xhtml#_6lowpan-parameters-1 + * + * Last Updated: 2015-01-22 + */ +static inline bool lowpan_is_reserved(u8 dispatch) +{ + return ((dispatch >= 0x44 && dispatch <= 0x4F) || + (dispatch >= 0x51 && dispatch <= 0x5F) || + (dispatch >= 0xc8 && dispatch <= 0xdf) || + (dispatch >= 0xe8 && dispatch <= 0xff)); +} + +/* lowpan_rx_h_check checks on generic 6LoWPAN requirements + * in MAC and 6LoWPAN header. + * + * Don't manipulate the skb here, it could be shared buffer. + */ +static inline bool lowpan_rx_h_check(struct sk_buff *skb) +{ + __le16 fc = ieee802154_get_fc_from_skb(skb); + + /* check on ieee802154 conform 6LoWPAN header */ + if (!ieee802154_is_data(fc) || + !ieee802154_is_intra_pan(fc)) + return false; + + /* check if we can dereference the dispatch */ + if (unlikely(!skb->len)) + return false; + + if (lowpan_is_nalp(*skb_network_header(skb)) || + lowpan_is_reserved(*skb_network_header(skb))) + return false; + + return true; +} + +static int lowpan_rcv(struct sk_buff *skb, struct net_device *wdev, + struct packet_type *pt, struct net_device *orig_wdev) +{ + struct net_device *ldev; + + if (wdev->type != ARPHRD_IEEE802154 || + skb->pkt_type == PACKET_OTHERHOST || + !lowpan_rx_h_check(skb)) + return NET_RX_DROP; + + ldev = wdev->ieee802154_ptr->lowpan_dev; + if (!ldev || !netif_running(ldev)) + return NET_RX_DROP; + + /* Replacing skb->dev and followed rx handlers will manipulate skb. */ + skb = skb_share_check(skb, GFP_ATOMIC); + if (!skb) + return NET_RX_DROP; + skb->dev = ldev; + + /* When receive frag1 it's likely that we manipulate the buffer. + * When recevie iphc we manipulate the data buffer. So we need + * to unshare the buffer. + */ + if (lowpan_is_frag1(*skb_network_header(skb)) || + lowpan_is_iphc(*skb_network_header(skb))) { + skb = skb_unshare(skb, GFP_ATOMIC); + if (!skb) + return NET_RX_DROP; } -drop_skb: - kfree_skb(skb); -drop: - return NET_RX_DROP; + return lowpan_invoke_rx_handlers(skb); } static struct packet_type lowpan_packet_type = { diff --git a/net/ieee802154/6lowpan/tx.c b/net/ieee802154/6lowpan/tx.c index f6263fc12340..54939d031ea5 100644 --- a/net/ieee802154/6lowpan/tx.c +++ b/net/ieee802154/6lowpan/tx.c @@ -36,7 +36,7 @@ lowpan_addr_info *lowpan_skb_priv(const struct sk_buff *skb) sizeof(struct lowpan_addr_info)); } -int lowpan_header_create(struct sk_buff *skb, struct net_device *dev, +int lowpan_header_create(struct sk_buff *skb, struct net_device *ldev, unsigned short type, const void *_daddr, const void *_saddr, unsigned int len) { @@ -51,7 +51,7 @@ int lowpan_header_create(struct sk_buff *skb, struct net_device *dev, return 0; if (!saddr) - saddr = dev->dev_addr; + saddr = ldev->dev_addr; raw_dump_inline(__func__, "saddr", (unsigned char *)saddr, 8); raw_dump_inline(__func__, "daddr", (unsigned char *)daddr, 8); @@ -73,22 +73,21 @@ static struct sk_buff* lowpan_alloc_frag(struct sk_buff *skb, int size, const struct ieee802154_hdr *master_hdr) { - struct net_device *real_dev = lowpan_dev_info(skb->dev)->real_dev; + struct net_device *wdev = lowpan_dev_info(skb->dev)->wdev; struct sk_buff *frag; int rc; - frag = alloc_skb(real_dev->hard_header_len + - real_dev->needed_tailroom + size, + frag = alloc_skb(wdev->hard_header_len + wdev->needed_tailroom + size, GFP_ATOMIC); if (likely(frag)) { - frag->dev = real_dev; + frag->dev = wdev; frag->priority = skb->priority; - skb_reserve(frag, real_dev->hard_header_len); + skb_reserve(frag, wdev->hard_header_len); skb_reset_network_header(frag); *mac_cb(frag) = *mac_cb(skb); - rc = dev_hard_header(frag, real_dev, 0, &master_hdr->dest, + rc = dev_hard_header(frag, wdev, 0, &master_hdr->dest, &master_hdr->source, size); if (rc < 0) { kfree_skb(frag); @@ -123,19 +122,17 @@ lowpan_xmit_fragment(struct sk_buff *skb, const struct ieee802154_hdr *wpan_hdr, } static int -lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *dev, - const struct ieee802154_hdr *wpan_hdr) +lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *ldev, + const struct ieee802154_hdr *wpan_hdr, u16 dgram_size, + u16 dgram_offset) { - u16 dgram_size, dgram_offset; __be16 frag_tag; u8 frag_hdr[5]; int frag_cap, frag_len, payload_cap, rc; int skb_unprocessed, skb_offset; - dgram_size = lowpan_uncompress_size(skb, &dgram_offset) - - skb->mac_len; - frag_tag = htons(lowpan_dev_info(dev)->fragment_tag); - lowpan_dev_info(dev)->fragment_tag++; + frag_tag = htons(lowpan_dev_info(ldev)->fragment_tag); + lowpan_dev_info(ldev)->fragment_tag++; frag_hdr[0] = LOWPAN_DISPATCH_FRAG1 | ((dgram_size >> 8) & 0x07); frag_hdr[1] = dgram_size & 0xff; @@ -188,9 +185,10 @@ err: return rc; } -static int lowpan_header(struct sk_buff *skb, struct net_device *dev) +static int lowpan_header(struct sk_buff *skb, struct net_device *ldev, + u16 *dgram_size, u16 *dgram_offset) { - struct wpan_dev *wpan_dev = lowpan_dev_info(dev)->real_dev->ieee802154_ptr; + struct wpan_dev *wpan_dev = lowpan_dev_info(ldev)->wdev->ieee802154_ptr; struct ieee802154_addr sa, da; struct ieee802154_mac_cb *cb = mac_cb_init(skb); struct lowpan_addr_info info; @@ -202,7 +200,10 @@ static int lowpan_header(struct sk_buff *skb, struct net_device *dev) daddr = &info.daddr.u.extended_addr; saddr = &info.saddr.u.extended_addr; - lowpan_header_compress(skb, dev, ETH_P_IPV6, daddr, saddr, skb->len); + *dgram_size = skb->len; + lowpan_header_compress(skb, ldev, ETH_P_IPV6, daddr, saddr, skb->len); + /* dgram_offset = (saved bytes after compression) + lowpan header len */ + *dgram_offset = (*dgram_size - skb->len) + skb_network_header_len(skb); cb->type = IEEE802154_FC_TYPE_DATA; @@ -227,14 +228,15 @@ static int lowpan_header(struct sk_buff *skb, struct net_device *dev) cb->ackreq = wpan_dev->ackreq; } - return dev_hard_header(skb, lowpan_dev_info(dev)->real_dev, - ETH_P_IPV6, (void *)&da, (void *)&sa, 0); + return dev_hard_header(skb, lowpan_dev_info(ldev)->wdev, ETH_P_IPV6, + (void *)&da, (void *)&sa, 0); } -netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev) +netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *ldev) { struct ieee802154_hdr wpan_hdr; int max_single, ret; + u16 dgram_size, dgram_offset; pr_debug("package xmit\n"); @@ -245,7 +247,7 @@ netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev) if (!skb) return NET_XMIT_DROP; - ret = lowpan_header(skb, dev); + ret = lowpan_header(skb, ldev, &dgram_size, &dgram_offset); if (ret < 0) { kfree_skb(skb); return NET_XMIT_DROP; @@ -259,13 +261,14 @@ netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev) max_single = ieee802154_max_payload(&wpan_hdr); if (skb_tail_pointer(skb) - skb_network_header(skb) <= max_single) { - skb->dev = lowpan_dev_info(dev)->real_dev; + skb->dev = lowpan_dev_info(ldev)->wdev; return dev_queue_xmit(skb); } else { netdev_tx_t rc; pr_debug("frame is too big, fragmentation is needed\n"); - rc = lowpan_xmit_fragmented(skb, dev, &wpan_hdr); + rc = lowpan_xmit_fragmented(skb, ldev, &wpan_hdr, dgram_size, + dgram_offset); return rc < 0 ? NET_XMIT_DROP : rc; } diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index d70b1f603692..6595affded20 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -345,7 +345,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) ireq->wscale_ok = tcp_opt.wscale_ok; ireq->tstamp_ok = tcp_opt.saw_tstamp; req->ts_recent = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsval : 0; - treq->snt_synack = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsecr : 0; + treq->snt_synack.v64 = 0; treq->tfo_listener = false; ireq->ir_iif = sk->sk_bound_dev_if; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index a62e9c76d485..497adf58a6b8 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -2953,21 +2953,21 @@ static inline bool tcp_ack_update_rtt(struct sock *sk, const int flag, } /* Compute time elapsed between (last) SYNACK and the ACK completing 3WHS. */ -static void tcp_synack_rtt_meas(struct sock *sk, const u32 synack_stamp) +void tcp_synack_rtt_meas(struct sock *sk, struct request_sock *req) { - struct tcp_sock *tp = tcp_sk(sk); - long seq_rtt_us = -1L; + long rtt_us = -1L; - if (synack_stamp && !tp->total_retrans) - seq_rtt_us = jiffies_to_usecs(tcp_time_stamp - synack_stamp); + if (req && !req->num_retrans && tcp_rsk(req)->snt_synack.v64) { + struct skb_mstamp now; - /* If the ACK acks both the SYNACK and the (Fast Open'd) data packets - * sent in SYN_RECV, SYNACK RTT is the smooth RTT computed in tcp_ack() - */ - if (!tp->srtt_us) - tcp_ack_update_rtt(sk, FLAG_SYN_ACKED, seq_rtt_us, -1L); + skb_mstamp_get(&now); + rtt_us = skb_mstamp_us_delta(&now, &tcp_rsk(req)->snt_synack); + } + + tcp_ack_update_rtt(sk, FLAG_SYN_ACKED, rtt_us, -1L); } + static void tcp_cong_avoid(struct sock *sk, u32 ack, u32 acked) { const struct inet_connection_sock *icsk = inet_csk(sk); @@ -5706,7 +5706,6 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, struct request_sock *req; int queued = 0; bool acceptable; - u32 synack_stamp; tp->rx_opt.saw_tstamp = 0; @@ -5785,15 +5784,16 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, if (!acceptable) return 1; + if (!tp->srtt_us) + tcp_synack_rtt_meas(sk, req); + /* Once we leave TCP_SYN_RECV, we no longer need req * so release it. */ if (req) { - synack_stamp = tcp_rsk(req)->snt_synack; tp->total_retrans = req->num_retrans; reqsk_fastopen_remove(sk, req, false); } else { - synack_stamp = tp->lsndtime; /* Make sure socket is routed, for correct metrics. */ icsk->icsk_af_ops->rebuild_header(sk); tcp_init_congestion_control(sk); @@ -5816,7 +5816,6 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, tp->snd_una = TCP_SKB_CB(skb)->ack_seq; tp->snd_wnd = ntohs(th->window) << tp->rx_opt.snd_wscale; tcp_init_wl(tp, TCP_SKB_CB(skb)->seq); - tcp_synack_rtt_meas(sk, synack_stamp); if (tp->rx_opt.tstamp_ok) tp->advmss -= TCPOLEN_TSTAMP_ALIGNED; @@ -6027,7 +6026,7 @@ static void tcp_openreq_init(struct request_sock *req, req->cookie_ts = 0; tcp_rsk(req)->rcv_isn = TCP_SKB_CB(skb)->seq; tcp_rsk(req)->rcv_nxt = TCP_SKB_CB(skb)->seq + 1; - tcp_rsk(req)->snt_synack = tcp_time_stamp; + skb_mstamp_get(&tcp_rsk(req)->snt_synack); tcp_rsk(req)->last_oow_ack_time = 0; req->mss = rx_opt->mss_clamp; req->ts_recent = rx_opt->saw_tstamp ? rx_opt->rcv_tsval : 0; diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 6d8795b066ac..10933d01b982 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -470,7 +470,7 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, newtp->snd_ssthresh = TCP_INFINITE_SSTHRESH; tcp_enable_early_retrans(newtp); newtp->tlp_high_seq = 0; - newtp->lsndtime = treq->snt_synack; + newtp->lsndtime = treq->snt_synack.stamp_jiffies; newtp->last_oow_ack_time = 0; newtp->total_retrans = req->num_retrans; @@ -760,6 +760,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, if (!child) goto listen_overflow; + tcp_synack_rtt_meas(child, req); inet_csk_reqsk_queue_drop(sk, req); inet_csk_reqsk_queue_add(sk, req, child); /* Warning: caller must not call reqsk_put(req); diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index d0ad3554c333..4cd0b50d4e46 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2165,7 +2165,7 @@ bool tcp_schedule_loss_probe(struct sock *sk) /* Don't do any loss probe on a Fast Open connection before 3WHS * finishes. */ - if (sk->sk_state == TCP_SYN_RECV) + if (tp->fastopen_rsk) return false; /* TLP is only scheduled when next timer event is RTO. */ @@ -2175,7 +2175,7 @@ bool tcp_schedule_loss_probe(struct sock *sk) /* Schedule a loss probe in 2*RTT for SACK capable connections * in Open state, that are either limited by cwnd or application. */ - if (sysctl_tcp_early_retrans < 3 || !tp->srtt_us || !tp->packets_out || + if (sysctl_tcp_early_retrans < 3 || !tp->packets_out || !tcp_is_sack(tp) || inet_csk(sk)->icsk_ca_state != TCP_CA_Open) return false; @@ -2184,9 +2184,10 @@ bool tcp_schedule_loss_probe(struct sock *sk) return false; /* Probe timeout is at least 1.5*rtt + TCP_DELACK_MAX to account - * for delayed ack when there's one outstanding packet. + * for delayed ack when there's one outstanding packet. If no RTT + * sample is available then probe after TCP_TIMEOUT_INIT. */ - timeout = rtt << 1; + timeout = rtt << 1 ? : TCP_TIMEOUT_INIT; if (tp->packets_out == 1) timeout = max_t(u32, timeout, (rtt + (rtt >> 1) + TCP_DELACK_MAX)); diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index 0909f4e0d53c..2461b3ff9551 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -210,7 +210,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) ireq->wscale_ok = tcp_opt.wscale_ok; ireq->tstamp_ok = tcp_opt.saw_tstamp; req->ts_recent = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsval : 0; - treq->snt_synack = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsecr : 0; + treq->snt_synack.v64 = 0; treq->rcv_isn = ntohl(th->seq) - 1; treq->snt_isn = cookie; diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index 918151c11348..fcb2752419c6 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -95,11 +95,10 @@ static void afiucv_hs_callback_txnotify(struct sk_buff *, enum iucv_tx_notify); /* Call Back functions */ static void iucv_callback_rx(struct iucv_path *, struct iucv_message *); static void iucv_callback_txdone(struct iucv_path *, struct iucv_message *); -static void iucv_callback_connack(struct iucv_path *, u8 ipuser[16]); -static int iucv_callback_connreq(struct iucv_path *, u8 ipvmid[8], - u8 ipuser[16]); -static void iucv_callback_connrej(struct iucv_path *, u8 ipuser[16]); -static void iucv_callback_shutdown(struct iucv_path *, u8 ipuser[16]); +static void iucv_callback_connack(struct iucv_path *, u8 *); +static int iucv_callback_connreq(struct iucv_path *, u8 *, u8 *); +static void iucv_callback_connrej(struct iucv_path *, u8 *); +static void iucv_callback_shutdown(struct iucv_path *, u8 *); static struct iucv_sock_list iucv_sk_list = { .lock = __RW_LOCK_UNLOCKED(iucv_sk_list.lock), diff --git a/net/iucv/iucv.c b/net/iucv/iucv.c index 2a6a1fdd62c0..7eaa000c9258 100644 --- a/net/iucv/iucv.c +++ b/net/iucv/iucv.c @@ -713,7 +713,7 @@ static struct notifier_block __refdata iucv_cpu_notifier = { * * Sever an iucv path to free up the pathid. Used internally. */ -static int iucv_sever_pathid(u16 pathid, u8 userdata[16]) +static int iucv_sever_pathid(u16 pathid, u8 *userdata) { union iucv_param *parm; @@ -876,7 +876,7 @@ static struct notifier_block iucv_reboot_notifier = { * Returns the result of the CP IUCV call. */ int iucv_path_accept(struct iucv_path *path, struct iucv_handler *handler, - u8 userdata[16], void *private) + u8 *userdata, void *private) { union iucv_param *parm; int rc; @@ -923,7 +923,7 @@ EXPORT_SYMBOL(iucv_path_accept); * Returns the result of the CP IUCV call. */ int iucv_path_connect(struct iucv_path *path, struct iucv_handler *handler, - u8 userid[8], u8 system[8], u8 userdata[16], + u8 *userid, u8 *system, u8 *userdata, void *private) { union iucv_param *parm; @@ -985,7 +985,7 @@ EXPORT_SYMBOL(iucv_path_connect); * * Returns the result from the CP IUCV call. */ -int iucv_path_quiesce(struct iucv_path *path, u8 userdata[16]) +int iucv_path_quiesce(struct iucv_path *path, u8 *userdata) { union iucv_param *parm; int rc; @@ -1017,7 +1017,7 @@ EXPORT_SYMBOL(iucv_path_quiesce); * * Returns the result from the CP IUCV call. */ -int iucv_path_resume(struct iucv_path *path, u8 userdata[16]) +int iucv_path_resume(struct iucv_path *path, u8 *userdata) { union iucv_param *parm; int rc; @@ -1047,7 +1047,7 @@ out: * * Returns the result from the CP IUCV call. */ -int iucv_path_sever(struct iucv_path *path, u8 userdata[16]) +int iucv_path_sever(struct iucv_path *path, u8 *userdata) { int rc; diff --git a/net/rxrpc/ar-connection.c b/net/rxrpc/ar-connection.c index 6631f4f1e39b..692b3e67fb54 100644 --- a/net/rxrpc/ar-connection.c +++ b/net/rxrpc/ar-connection.c @@ -808,7 +808,7 @@ void rxrpc_put_connection(struct rxrpc_connection *conn) ASSERTCMP(atomic_read(&conn->usage), >, 0); - conn->put_time = get_seconds(); + conn->put_time = ktime_get_seconds(); if (atomic_dec_and_test(&conn->usage)) { _debug("zombie"); rxrpc_queue_delayed_work(&rxrpc_connection_reap, 0); @@ -852,7 +852,7 @@ static void rxrpc_connection_reaper(struct work_struct *work) _enter(""); - now = get_seconds(); + now = ktime_get_seconds(); earliest = ULONG_MAX; write_lock_bh(&rxrpc_connection_lock); diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index aef1bd294e17..2934a73a5981 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -208,7 +208,7 @@ struct rxrpc_transport { struct rb_root server_conns; /* server connections on this transport */ struct list_head link; /* link in master session list */ struct sk_buff_head error_queue; /* error packets awaiting processing */ - time_t put_time; /* time at which to reap */ + unsigned long put_time; /* time at which to reap */ spinlock_t client_lock; /* client connection allocation lock */ rwlock_t conn_lock; /* lock for active/dead connections */ atomic_t usage; @@ -256,7 +256,7 @@ struct rxrpc_connection { struct rxrpc_crypt csum_iv; /* packet checksum base */ unsigned long events; #define RXRPC_CONN_CHALLENGE 0 /* send challenge packet */ - time_t put_time; /* time at which to reap */ + unsigned long put_time; /* time at which to reap */ rwlock_t lock; /* access lock */ spinlock_t state_lock; /* state-change lock */ atomic_t usage; diff --git a/net/rxrpc/ar-transport.c b/net/rxrpc/ar-transport.c index 1976dec84f29..9946467f16b4 100644 --- a/net/rxrpc/ar-transport.c +++ b/net/rxrpc/ar-transport.c @@ -189,7 +189,7 @@ void rxrpc_put_transport(struct rxrpc_transport *trans) ASSERTCMP(atomic_read(&trans->usage), >, 0); - trans->put_time = get_seconds(); + trans->put_time = ktime_get_seconds(); if (unlikely(atomic_dec_and_test(&trans->usage))) { _debug("zombie"); /* let the reaper determine the timeout to avoid a race with @@ -226,7 +226,7 @@ static void rxrpc_transport_reaper(struct work_struct *work) _enter(""); - now = get_seconds(); + now = ktime_get_seconds(); earliest = ULONG_MAX; /* extract all the transports that have been dead too long */ |