diff options
author | Yangbo Lu <yangbo.lu@nxp.com> | 2020-04-10 10:47:05 +0800 |
---|---|---|
committer | Petr Štetiar <ynezz@true.cz> | 2020-05-07 12:53:06 +0200 |
commit | cddd4591404fb4c53dc0b3c0b15b942cdbed4356 (patch) | |
tree | 392c1179de46b0f804e3789edca19069b64e6b44 /target/linux/layerscape/patches-5.4/701-net-0009-dpa-SDK-DPAA-1.x-Ethernet-driver.patch | |
parent | d1d2c0b5579ea4f69a42246c9318539d61ba1999 (diff) | |
download | upstream-cddd4591404fb4c53dc0b3c0b15b942cdbed4356.tar.gz upstream-cddd4591404fb4c53dc0b3c0b15b942cdbed4356.tar.bz2 upstream-cddd4591404fb4c53dc0b3c0b15b942cdbed4356.zip |
layerscape: add patches-5.4
Add patches for linux-5.4. The patches are from NXP LSDK-20.04 release
which was tagged LSDK-20.04-V5.4.
https://source.codeaurora.org/external/qoriq/qoriq-components/linux/
For boards LS1021A-IOT, and Traverse-LS1043 which are not involved in
LSDK, port the dts patches from 4.14.
The patches are sorted into the following categories:
301-arch-xxxx
302-dts-xxxx
303-core-xxxx
701-net-xxxx
801-audio-xxxx
802-can-xxxx
803-clock-xxxx
804-crypto-xxxx
805-display-xxxx
806-dma-xxxx
807-gpio-xxxx
808-i2c-xxxx
809-jailhouse-xxxx
810-keys-xxxx
811-kvm-xxxx
812-pcie-xxxx
813-pm-xxxx
814-qe-xxxx
815-sata-xxxx
816-sdhc-xxxx
817-spi-xxxx
818-thermal-xxxx
819-uart-xxxx
820-usb-xxxx
821-vfio-xxxx
Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
Diffstat (limited to 'target/linux/layerscape/patches-5.4/701-net-0009-dpa-SDK-DPAA-1.x-Ethernet-driver.patch')
-rw-r--r-- | target/linux/layerscape/patches-5.4/701-net-0009-dpa-SDK-DPAA-1.x-Ethernet-driver.patch | 12969 |
1 files changed, 12969 insertions, 0 deletions
diff --git a/target/linux/layerscape/patches-5.4/701-net-0009-dpa-SDK-DPAA-1.x-Ethernet-driver.patch b/target/linux/layerscape/patches-5.4/701-net-0009-dpa-SDK-DPAA-1.x-Ethernet-driver.patch new file mode 100644 index 0000000000..29b52d2238 --- /dev/null +++ b/target/linux/layerscape/patches-5.4/701-net-0009-dpa-SDK-DPAA-1.x-Ethernet-driver.patch @@ -0,0 +1,12969 @@ +From f7f94b1e7e9c6044a23bab1c5e773f6259f2d3e0 Mon Sep 17 00:00:00 2001 +From: Madalin Bucur <madalin.bucur@nxp.com> +Date: Wed, 10 May 2017 16:39:42 +0300 +Subject: [PATCH] dpa: SDK DPAA 1.x Ethernet driver + +Signed-off-by: Madalin Bucur <madalin.bucur@nxp.com> +--- + drivers/net/ethernet/freescale/sdk_dpaa/Kconfig | 173 ++ + drivers/net/ethernet/freescale/sdk_dpaa/Makefile | 46 + + .../net/ethernet/freescale/sdk_dpaa/dpaa_1588.c | 580 ++++++ + .../net/ethernet/freescale/sdk_dpaa/dpaa_1588.h | 138 ++ + .../net/ethernet/freescale/sdk_dpaa/dpaa_debugfs.c | 180 ++ + .../net/ethernet/freescale/sdk_dpaa/dpaa_debugfs.h | 43 + + drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth.c | 1210 ++++++++++++ + drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth.h | 697 +++++++ + .../ethernet/freescale/sdk_dpaa/dpaa_eth_base.c | 263 +++ + .../ethernet/freescale/sdk_dpaa/dpaa_eth_base.h | 50 + + .../ethernet/freescale/sdk_dpaa/dpaa_eth_ceetm.c | 1991 ++++++++++++++++++++ + .../ethernet/freescale/sdk_dpaa/dpaa_eth_ceetm.h | 236 +++ + .../ethernet/freescale/sdk_dpaa/dpaa_eth_common.c | 1812 ++++++++++++++++++ + .../ethernet/freescale/sdk_dpaa/dpaa_eth_common.h | 226 +++ + .../ethernet/freescale/sdk_dpaa/dpaa_eth_proxy.c | 381 ++++ + .../net/ethernet/freescale/sdk_dpaa/dpaa_eth_sg.c | 1113 +++++++++++ + .../ethernet/freescale/sdk_dpaa/dpaa_eth_sysfs.c | 278 +++ + .../ethernet/freescale/sdk_dpaa/dpaa_eth_trace.h | 144 ++ + .../net/ethernet/freescale/sdk_dpaa/dpaa_ethtool.c | 544 ++++++ + drivers/net/ethernet/freescale/sdk_dpaa/dpaa_ptp.c | 290 +++ + drivers/net/ethernet/freescale/sdk_dpaa/mac-api.c | 909 +++++++++ + drivers/net/ethernet/freescale/sdk_dpaa/mac.c | 489 +++++ + drivers/net/ethernet/freescale/sdk_dpaa/mac.h | 135 ++ + .../net/ethernet/freescale/sdk_dpaa/offline_port.c | 848 +++++++++ + .../net/ethernet/freescale/sdk_dpaa/offline_port.h | 59 + + 25 files changed, 12835 insertions(+) + create mode 100644 drivers/net/ethernet/freescale/sdk_dpaa/Kconfig + create mode 100644 drivers/net/ethernet/freescale/sdk_dpaa/Makefile + create mode 100644 drivers/net/ethernet/freescale/sdk_dpaa/dpaa_1588.c + create mode 100644 drivers/net/ethernet/freescale/sdk_dpaa/dpaa_1588.h + create mode 100644 drivers/net/ethernet/freescale/sdk_dpaa/dpaa_debugfs.c + create mode 100644 drivers/net/ethernet/freescale/sdk_dpaa/dpaa_debugfs.h + create mode 100644 drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth.c + create mode 100644 drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth.h + create mode 100644 drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_base.c + create mode 100644 drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_base.h + create mode 100644 drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_ceetm.c + create mode 100644 drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_ceetm.h + create mode 100644 drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_common.c + create mode 100644 drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_common.h + create mode 100644 drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_proxy.c + create mode 100644 drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_sg.c + create mode 100644 drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_sysfs.c + create mode 100644 drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_trace.h + create mode 100644 drivers/net/ethernet/freescale/sdk_dpaa/dpaa_ethtool.c + create mode 100644 drivers/net/ethernet/freescale/sdk_dpaa/dpaa_ptp.c + create mode 100644 drivers/net/ethernet/freescale/sdk_dpaa/mac-api.c + create mode 100644 drivers/net/ethernet/freescale/sdk_dpaa/mac.c + create mode 100644 drivers/net/ethernet/freescale/sdk_dpaa/mac.h + create mode 100644 drivers/net/ethernet/freescale/sdk_dpaa/offline_port.c + create mode 100644 drivers/net/ethernet/freescale/sdk_dpaa/offline_port.h + +--- /dev/null ++++ b/drivers/net/ethernet/freescale/sdk_dpaa/Kconfig +@@ -0,0 +1,173 @@ ++menuconfig FSL_SDK_DPAA_ETH ++ tristate "DPAA Ethernet" ++ depends on (FSL_SOC || ARM64 || ARM) && FSL_SDK_BMAN && FSL_SDK_QMAN && FSL_SDK_FMAN && !FSL_DPAA_ETH ++ select PHYLIB ++ help ++ Data Path Acceleration Architecture Ethernet driver, ++ supporting the Freescale QorIQ chips. ++ Depends on Freescale Buffer Manager and Queue Manager ++ driver and Frame Manager Driver. ++ ++if FSL_SDK_DPAA_ETH ++ ++config FSL_DPAA_HOOKS ++ bool "DPAA Ethernet driver hooks" ++ ++config FSL_DPAA_CEETM ++ bool "DPAA CEETM QoS" ++ depends on NET_SCHED ++ default n ++ help ++ Enable QoS offloading support through the CEETM hardware block. ++ ++config FSL_DPAA_OFFLINE_PORTS ++ bool "Offline Ports support" ++ depends on FSL_SDK_DPAA_ETH ++ default y ++ help ++ The Offline Parsing / Host Command ports (short: OH ports, of Offline ports) provide ++ most of the functionality of the regular, online ports, except they receive their ++ frames from a core or an accelerator on the SoC, via QMan frame queues, ++ rather than directly from the network. ++ Offline ports are configured via PCD (Parse-Classify-Distribute) schemes, just like ++ any online FMan port. They deliver the processed frames to frame queues, according ++ to the applied PCD configurations. ++ ++ Choosing this feature will not impact the functionality and/or performance of the system, ++ so it is safe to have it. ++ ++config FSL_DPAA_ADVANCED_DRIVERS ++ bool "Advanced DPAA Ethernet drivers" ++ depends on FSL_SDK_DPAA_ETH ++ default y ++ help ++ Besides the standard DPAA Ethernet driver the DPAA Proxy initialization driver ++ is needed to support advanced scenarios. Select this to also build the advanced ++ drivers. ++ ++config FSL_DPAA_ETH_JUMBO_FRAME ++ bool "Optimize for jumbo frames" ++ default n ++ help ++ Optimize the DPAA Ethernet driver throughput for large frames ++ termination traffic (e.g. 4K and above). ++ NOTE: This option can only be used if FSL_FM_MAX_FRAME_SIZE ++ is set to 9600 bytes. ++ Using this option in combination with small frames increases ++ significantly the driver's memory footprint and may even deplete ++ the system memory. Also, the skb truesize is altered and messages ++ from the stack that warn against this are bypassed. ++ This option is not available on LS1043. ++ ++config FSL_DPAA_TS ++ bool "Linux compliant timestamping" ++ depends on FSL_SDK_DPAA_ETH ++ default n ++ help ++ Enable Linux API compliant timestamping support. ++ ++config FSL_DPAA_1588 ++ bool "IEEE 1588-compliant timestamping" ++ depends on FSL_SDK_DPAA_ETH ++ select FSL_DPAA_TS ++ default n ++ help ++ Enable IEEE1588 support code. ++ ++config FSL_DPAA_ETH_USE_NDO_SELECT_QUEUE ++ bool "Use driver's Tx queue selection mechanism" ++ default y ++ depends on FSL_SDK_DPAA_ETH ++ help ++ The DPAA-Ethernet driver defines a ndo_select_queue() callback for optimal selection ++ of the egress FQ. That will override the XPS support for this netdevice. ++ If for whatever reason you want to be in control of the egress FQ-to-CPU selection and mapping, ++ or simply don't want to use the driver's ndo_select_queue() callback, then unselect this ++ and use the standard XPS support instead. ++ ++config FSL_DPAA_ETH_MAX_BUF_COUNT ++ int "Maximum nuber of buffers in private bpool" ++ depends on FSL_SDK_DPAA_ETH ++ range 64 2048 ++ default "128" ++ help ++ The maximum number of buffers to be by default allocated in the DPAA-Ethernet private port's ++ buffer pool. One needn't normally modify this, as it has probably been tuned for performance ++ already. This cannot be lower than DPAA_ETH_REFILL_THRESHOLD. ++ ++config FSL_DPAA_ETH_REFILL_THRESHOLD ++ int "Private bpool refill threshold" ++ depends on FSL_SDK_DPAA_ETH ++ range 32 FSL_DPAA_ETH_MAX_BUF_COUNT ++ default "80" ++ help ++ The DPAA-Ethernet driver will start replenishing buffer pools whose count ++ falls below this threshold. This must be related to DPAA_ETH_MAX_BUF_COUNT. One needn't normally ++ modify this value unless one has very specific performance reasons. ++ ++config FSL_DPAA_CS_THRESHOLD_1G ++ hex "Egress congestion threshold on 1G ports" ++ depends on FSL_SDK_DPAA_ETH ++ range 0x1000 0x10000000 ++ default "0x06000000" ++ help ++ The size in bytes of the egress Congestion State notification threshold on 1G ports. ++ The 1G dTSECs can quite easily be flooded by cores doing Tx in a tight loop ++ (e.g. by sending UDP datagrams at "while(1) speed"), ++ and the larger the frame size, the more acute the problem. ++ So we have to find a balance between these factors: ++ - avoiding the device staying congested for a prolonged time (risking ++ the netdev watchdog to fire - see also the tx_timeout module param); ++ - affecting performance of protocols such as TCP, which otherwise ++ behave well under the congestion notification mechanism; ++ - preventing the Tx cores from tightly-looping (as if the congestion ++ threshold was too low to be effective); ++ - running out of memory if the CS threshold is set too high. ++ ++config FSL_DPAA_CS_THRESHOLD_10G ++ hex "Egress congestion threshold on 10G ports" ++ depends on FSL_SDK_DPAA_ETH ++ range 0x1000 0x20000000 ++ default "0x10000000" ++ help ++ The size in bytes of the egress Congestion State notification threshold on 10G ports. ++ ++config FSL_DPAA_INGRESS_CS_THRESHOLD ++ hex "Ingress congestion threshold on FMan ports" ++ depends on FSL_SDK_DPAA_ETH ++ default "0x10000000" ++ help ++ The size in bytes of the ingress tail-drop threshold on FMan ports. ++ Traffic piling up above this value will be rejected by QMan and discarded by FMan. ++ ++config FSL_DPAA_ETH_DEBUGFS ++ bool "DPAA Ethernet debugfs interface" ++ depends on DEBUG_FS && FSL_SDK_DPAA_ETH ++ default y ++ help ++ This option compiles debugfs code for the DPAA Ethernet driver. ++ ++config FSL_DPAA_ETH_DEBUG ++ bool "DPAA Ethernet Debug Support" ++ depends on FSL_SDK_DPAA_ETH ++ default n ++ help ++ This option compiles debug code for the DPAA Ethernet driver. ++ ++config FSL_DPAA_DBG_LOOP ++ bool "DPAA Ethernet Debug loopback" ++ depends on FSL_DPAA_ETH_DEBUGFS && FSL_DPAA_ETH_USE_NDO_SELECT_QUEUE ++ default n ++ help ++ This option allows to divert all received traffic on a certain interface A towards a ++ selected interface B. This option is used to benchmark the HW + Ethernet driver in ++ isolation from the Linux networking stack. The loops are controlled by debugfs entries, ++ one for each interface. By default all loops are disabled (target value is -1). I.e. to ++ change the loop setting for interface 4 and divert all received traffic to interface 5 ++ write Tx interface number in the receive interface debugfs file: ++ # cat /sys/kernel/debug/powerpc/fsl_dpa/eth4_loop ++ 4->-1 ++ # echo 5 > /sys/kernel/debug/powerpc/fsl_dpa/eth4_loop ++ # cat /sys/kernel/debug/powerpc/fsl_dpa/eth4_loop ++ 4->5 ++endif # FSL_SDK_DPAA_ETH +--- /dev/null ++++ b/drivers/net/ethernet/freescale/sdk_dpaa/Makefile +@@ -0,0 +1,46 @@ ++# ++# Makefile for the Freescale Ethernet controllers ++# ++ccflags-y += -DVERSION=\"\" ++# ++# Include netcomm SW specific definitions ++include $(srctree)/drivers/net/ethernet/freescale/sdk_fman/ncsw_config.mk ++ ++ccflags-y += -I$(NET_DPA) ++ ++obj-$(CONFIG_FSL_SDK_DPAA_ETH) += fsl_mac.o fsl_dpa.o ++obj-$(CONFIG_PTP_1588_CLOCK_DPAA) += dpaa_ptp.o ++ ++fsl_dpa-objs += dpaa_ethtool.o dpaa_eth_sysfs.o dpaa_eth.o dpaa_eth_sg.o dpaa_eth_common.o ++ifeq ($(CONFIG_FSL_DPAA_DBG_LOOP),y) ++fsl_dpa-objs += dpaa_debugfs.o ++endif ++ifeq ($(CONFIG_FSL_DPAA_1588),y) ++fsl_dpa-objs += dpaa_1588.o ++endif ++ifeq ($(CONFIG_FSL_DPAA_CEETM),y) ++ccflags-y += -Idrivers/net/ethernet/freescale/sdk_fman/src/wrapper ++fsl_dpa-objs += dpaa_eth_ceetm.o ++endif ++ ++fsl_mac-objs += mac.o mac-api.o ++ ++# Advanced drivers ++ifeq ($(CONFIG_FSL_DPAA_ADVANCED_DRIVERS),y) ++obj-$(CONFIG_FSL_SDK_DPAA_ETH) += fsl_advanced.o ++obj-$(CONFIG_FSL_SDK_DPAA_ETH) += fsl_proxy.o ++ ++fsl_advanced-objs += dpaa_eth_base.o ++# suport for multiple drivers per kernel module comes in kernel 3.14 ++# so we are forced to generate several modules for the advanced drivers ++fsl_proxy-objs += dpaa_eth_proxy.o ++ ++ifeq ($(CONFIG_FSL_DPAA_OFFLINE_PORTS),y) ++obj-$(CONFIG_FSL_SDK_DPAA_ETH) += fsl_oh.o ++ ++fsl_oh-objs += offline_port.o ++endif ++endif ++ ++# Needed by the tracing framework ++CFLAGS_dpaa_eth.o := -I$(src) +--- /dev/null ++++ b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_1588.c +@@ -0,0 +1,580 @@ ++/* Copyright (C) 2011 Freescale Semiconductor, Inc. ++ * Copyright (C) 2009 IXXAT Automation, GmbH ++ * ++ * DPAA Ethernet Driver -- IEEE 1588 interface functionality ++ * ++ * 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. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ */ ++#include <linux/io.h> ++#include <linux/device.h> ++#include <linux/fs.h> ++#include <linux/vmalloc.h> ++#include <linux/spinlock.h> ++#include <linux/ip.h> ++#include <linux/ipv6.h> ++#include <linux/udp.h> ++#include <asm/div64.h> ++#include "dpaa_eth.h" ++#include "dpaa_eth_common.h" ++#include "dpaa_1588.h" ++#include "mac.h" ++ ++static int dpa_ptp_init_circ(struct dpa_ptp_circ_buf *ptp_buf, u32 size) ++{ ++ struct circ_buf *circ_buf = &ptp_buf->circ_buf; ++ ++ circ_buf->buf = vmalloc(sizeof(struct dpa_ptp_data) * size); ++ if (!circ_buf->buf) ++ return 1; ++ ++ circ_buf->head = 0; ++ circ_buf->tail = 0; ++ ptp_buf->size = size; ++ spin_lock_init(&ptp_buf->ptp_lock); ++ ++ return 0; ++} ++ ++static void dpa_ptp_reset_circ(struct dpa_ptp_circ_buf *ptp_buf, u32 size) ++{ ++ struct circ_buf *circ_buf = &ptp_buf->circ_buf; ++ ++ circ_buf->head = 0; ++ circ_buf->tail = 0; ++ ptp_buf->size = size; ++} ++ ++static int dpa_ptp_insert(struct dpa_ptp_circ_buf *ptp_buf, ++ struct dpa_ptp_data *data) ++{ ++ struct circ_buf *circ_buf = &ptp_buf->circ_buf; ++ int size = ptp_buf->size; ++ struct dpa_ptp_data *tmp; ++ unsigned long flags; ++ int head, tail; ++ ++ spin_lock_irqsave(&ptp_buf->ptp_lock, flags); ++ ++ head = circ_buf->head; ++ tail = circ_buf->tail; ++ ++ if (CIRC_SPACE(head, tail, size) <= 0) ++ circ_buf->tail = (tail + 1) & (size - 1); ++ ++ tmp = (struct dpa_ptp_data *)(circ_buf->buf) + head; ++ memcpy(tmp, data, sizeof(struct dpa_ptp_data)); ++ ++ circ_buf->head = (head + 1) & (size - 1); ++ ++ spin_unlock_irqrestore(&ptp_buf->ptp_lock, flags); ++ ++ return 0; ++} ++ ++static int dpa_ptp_is_ident_match(struct dpa_ptp_ident *dst, ++ struct dpa_ptp_ident *src) ++{ ++ int ret; ++ ++ if ((dst->version != src->version) || (dst->msg_type != src->msg_type)) ++ return 0; ++ ++ if ((dst->netw_prot == src->netw_prot) ++ || src->netw_prot == DPA_PTP_PROT_DONTCARE) { ++ if (dst->seq_id != src->seq_id) ++ return 0; ++ ++ ret = memcmp(dst->snd_port_id, src->snd_port_id, ++ DPA_PTP_SOURCE_PORT_LENGTH); ++ if (ret) ++ return 0; ++ else ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static int dpa_ptp_find_and_remove(struct dpa_ptp_circ_buf *ptp_buf, ++ struct dpa_ptp_ident *ident, ++ struct dpa_ptp_time *ts) ++{ ++ struct circ_buf *circ_buf = &ptp_buf->circ_buf; ++ int size = ptp_buf->size; ++ int head, tail, idx; ++ unsigned long flags; ++ struct dpa_ptp_data *tmp, *tmp2; ++ struct dpa_ptp_ident *tmp_ident; ++ ++ spin_lock_irqsave(&ptp_buf->ptp_lock, flags); ++ ++ head = circ_buf->head; ++ tail = idx = circ_buf->tail; ++ ++ if (CIRC_CNT(head, tail, size) == 0) { ++ spin_unlock_irqrestore(&ptp_buf->ptp_lock, flags); ++ return 1; ++ } ++ ++ while (idx != head) { ++ tmp = (struct dpa_ptp_data *)(circ_buf->buf) + idx; ++ tmp_ident = &tmp->ident; ++ if (dpa_ptp_is_ident_match(tmp_ident, ident)) ++ break; ++ idx = (idx + 1) & (size - 1); ++ } ++ ++ if (idx == head) { ++ spin_unlock_irqrestore(&ptp_buf->ptp_lock, flags); ++ return 1; ++ } ++ ++ ts->sec = tmp->ts.sec; ++ ts->nsec = tmp->ts.nsec; ++ ++ if (idx != tail) { ++ if (CIRC_CNT(idx, tail, size) > TS_ACCUMULATION_THRESHOLD) { ++ tail = circ_buf->tail = ++ (idx - TS_ACCUMULATION_THRESHOLD) & (size - 1); ++ } ++ ++ while (CIRC_CNT(idx, tail, size) > 0) { ++ tmp = (struct dpa_ptp_data *)(circ_buf->buf) + idx; ++ idx = (idx - 1) & (size - 1); ++ tmp2 = (struct dpa_ptp_data *)(circ_buf->buf) + idx; ++ *tmp = *tmp2; ++ } ++ } ++ circ_buf->tail = (tail + 1) & (size - 1); ++ ++ spin_unlock_irqrestore(&ptp_buf->ptp_lock, flags); ++ ++ return 0; ++} ++ ++/* Parse the PTP packets ++ * ++ * The PTP header can be found in an IPv4 packet, IPv6 patcket or in ++ * an IEEE802.3 ethernet frame. This function returns the position of ++ * the PTP packet or NULL if no PTP found ++ */ ++static u8 *dpa_ptp_parse_packet(struct sk_buff *skb, u16 *eth_type) ++{ ++ u8 *pos = skb->data + ETH_ALEN + ETH_ALEN; ++ u8 *ptp_loc = NULL; ++ u8 msg_type; ++ u32 access_len = ETH_ALEN + ETH_ALEN + DPA_ETYPE_LEN; ++ struct iphdr *iph; ++ struct udphdr *udph; ++ struct ipv6hdr *ipv6h; ++ ++ /* when we can receive S/G frames we need to check the data we want to ++ * access is in the linear skb buffer ++ */ ++ if (!pskb_may_pull(skb, access_len)) ++ return NULL; ++ ++ *eth_type = *((u16 *)pos); ++ ++ /* Check if inner tag is here */ ++ if (*eth_type == ETH_P_8021Q) { ++ access_len += DPA_VLAN_TAG_LEN; ++ ++ if (!pskb_may_pull(skb, access_len)) ++ return NULL; ++ ++ pos += DPA_VLAN_TAG_LEN; ++ *eth_type = *((u16 *)pos); ++ } ++ ++ pos += DPA_ETYPE_LEN; ++ ++ switch (*eth_type) { ++ /* Transport of PTP over Ethernet */ ++ case ETH_P_1588: ++ ptp_loc = pos; ++ ++ if (!pskb_may_pull(skb, access_len + PTP_OFFS_MSG_TYPE + 1)) ++ return NULL; ++ ++ msg_type = *((u8 *)(ptp_loc + PTP_OFFS_MSG_TYPE)) & 0xf; ++ if ((msg_type == PTP_MSGTYPE_SYNC) ++ || (msg_type == PTP_MSGTYPE_DELREQ) ++ || (msg_type == PTP_MSGTYPE_PDELREQ) ++ || (msg_type == PTP_MSGTYPE_PDELRESP)) ++ return ptp_loc; ++ break; ++ /* Transport of PTP over IPv4 */ ++ case ETH_P_IP: ++ iph = (struct iphdr *)pos; ++ access_len += sizeof(struct iphdr); ++ ++ if (!pskb_may_pull(skb, access_len)) ++ return NULL; ++ ++ if (ntohs(iph->protocol) != IPPROTO_UDP) ++ return NULL; ++ ++ access_len += iph->ihl * 4 - sizeof(struct iphdr) + ++ sizeof(struct udphdr); ++ ++ if (!pskb_may_pull(skb, access_len)) ++ return NULL; ++ ++ pos += iph->ihl * 4; ++ udph = (struct udphdr *)pos; ++ if (ntohs(udph->dest) != 319) ++ return NULL; ++ ptp_loc = pos + sizeof(struct udphdr); ++ break; ++ /* Transport of PTP over IPv6 */ ++ case ETH_P_IPV6: ++ ipv6h = (struct ipv6hdr *)pos; ++ ++ access_len += sizeof(struct ipv6hdr) + sizeof(struct udphdr); ++ ++ if (ntohs(ipv6h->nexthdr) != IPPROTO_UDP) ++ return NULL; ++ ++ pos += sizeof(struct ipv6hdr); ++ udph = (struct udphdr *)pos; ++ if (ntohs(udph->dest) != 319) ++ return NULL; ++ ptp_loc = pos + sizeof(struct udphdr); ++ break; ++ default: ++ break; ++ } ++ ++ return ptp_loc; ++} ++ ++static int dpa_ptp_store_stamp(const struct dpa_priv_s *priv, ++ struct sk_buff *skb, void *data, enum port_type rx_tx, ++ struct dpa_ptp_data *ptp_data) ++{ ++ u64 nsec; ++ u32 mod; ++ u8 *ptp_loc; ++ u16 eth_type; ++ ++ ptp_loc = dpa_ptp_parse_packet(skb, ð_type); ++ if (!ptp_loc) ++ return -EINVAL; ++ ++ switch (eth_type) { ++ case ETH_P_IP: ++ ptp_data->ident.netw_prot = DPA_PTP_PROT_IPV4; ++ break; ++ case ETH_P_IPV6: ++ ptp_data->ident.netw_prot = DPA_PTP_PROT_IPV6; ++ break; ++ case ETH_P_1588: ++ ptp_data->ident.netw_prot = DPA_PTP_PROT_802_3; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ if (!pskb_may_pull(skb, ptp_loc - skb->data + PTP_OFFS_SEQ_ID + 2)) ++ return -EINVAL; ++ ++ ptp_data->ident.version = *(ptp_loc + PTP_OFFS_VER_PTP) & 0xf; ++ ptp_data->ident.msg_type = *(ptp_loc + PTP_OFFS_MSG_TYPE) & 0xf; ++ ptp_data->ident.seq_id = *((u16 *)(ptp_loc + PTP_OFFS_SEQ_ID)); ++ memcpy(ptp_data->ident.snd_port_id, ptp_loc + PTP_OFFS_SRCPRTID, ++ DPA_PTP_SOURCE_PORT_LENGTH); ++ ++ nsec = dpa_get_timestamp_ns(priv, rx_tx, data); ++ mod = do_div(nsec, NANOSEC_PER_SECOND); ++ ptp_data->ts.sec = nsec; ++ ptp_data->ts.nsec = mod; ++ ++ return 0; ++} ++ ++void dpa_ptp_store_txstamp(const struct dpa_priv_s *priv, ++ struct sk_buff *skb, void *data) ++{ ++ struct dpa_ptp_tsu *tsu = priv->tsu; ++ struct dpa_ptp_data ptp_tx_data; ++ ++ if (dpa_ptp_store_stamp(priv, skb, data, TX, &ptp_tx_data)) ++ return; ++ ++ dpa_ptp_insert(&tsu->tx_timestamps, &ptp_tx_data); ++} ++ ++void dpa_ptp_store_rxstamp(const struct dpa_priv_s *priv, ++ struct sk_buff *skb, void *data) ++{ ++ struct dpa_ptp_tsu *tsu = priv->tsu; ++ struct dpa_ptp_data ptp_rx_data; ++ ++ if (dpa_ptp_store_stamp(priv, skb, data, RX, &ptp_rx_data)) ++ return; ++ ++ dpa_ptp_insert(&tsu->rx_timestamps, &ptp_rx_data); ++} ++ ++static uint8_t dpa_get_tx_timestamp(struct dpa_ptp_tsu *ptp_tsu, ++ struct dpa_ptp_ident *ident, ++ struct dpa_ptp_time *ts) ++{ ++ struct dpa_ptp_tsu *tsu = ptp_tsu; ++ struct dpa_ptp_time tmp; ++ int flag; ++ ++ flag = dpa_ptp_find_and_remove(&tsu->tx_timestamps, ident, &tmp); ++ if (!flag) { ++ ts->sec = tmp.sec; ++ ts->nsec = tmp.nsec; ++ return 0; ++ } ++ ++ return -1; ++} ++ ++static uint8_t dpa_get_rx_timestamp(struct dpa_ptp_tsu *ptp_tsu, ++ struct dpa_ptp_ident *ident, ++ struct dpa_ptp_time *ts) ++{ ++ struct dpa_ptp_tsu *tsu = ptp_tsu; ++ struct dpa_ptp_time tmp; ++ int flag; ++ ++ flag = dpa_ptp_find_and_remove(&tsu->rx_timestamps, ident, &tmp); ++ if (!flag) { ++ ts->sec = tmp.sec; ++ ts->nsec = tmp.nsec; ++ return 0; ++ } ++ ++ return -1; ++} ++ ++static void dpa_set_fiper_alarm(struct dpa_ptp_tsu *tsu, ++ struct dpa_ptp_time *cnt_time) ++{ ++ struct mac_device *mac_dev = tsu->dpa_priv->mac_dev; ++ u64 tmp, fiper; ++ ++ if (mac_dev->fm_rtc_disable) ++ mac_dev->fm_rtc_disable(get_fm_handle(tsu->dpa_priv->net_dev)); ++ ++ /* TMR_FIPER1 will pulse every second after ALARM1 expired */ ++ tmp = (u64)cnt_time->sec * NANOSEC_PER_SECOND + (u64)cnt_time->nsec; ++ fiper = NANOSEC_PER_SECOND - DPA_PTP_NOMINAL_FREQ_PERIOD_NS; ++ if (mac_dev->fm_rtc_set_alarm) ++ mac_dev->fm_rtc_set_alarm(get_fm_handle(tsu->dpa_priv->net_dev), ++ 0, tmp); ++ if (mac_dev->fm_rtc_set_fiper) ++ mac_dev->fm_rtc_set_fiper(get_fm_handle(tsu->dpa_priv->net_dev), ++ 0, fiper); ++ ++ if (mac_dev->fm_rtc_enable) ++ mac_dev->fm_rtc_enable(get_fm_handle(tsu->dpa_priv->net_dev)); ++} ++ ++static void dpa_get_curr_cnt(struct dpa_ptp_tsu *tsu, ++ struct dpa_ptp_time *curr_time) ++{ ++ struct mac_device *mac_dev = tsu->dpa_priv->mac_dev; ++ u64 tmp; ++ u32 mod; ++ ++ if (mac_dev->fm_rtc_get_cnt) ++ mac_dev->fm_rtc_get_cnt(get_fm_handle(tsu->dpa_priv->net_dev), ++ &tmp); ++ ++ mod = do_div(tmp, NANOSEC_PER_SECOND); ++ curr_time->sec = (u32)tmp; ++ curr_time->nsec = mod; ++} ++ ++static void dpa_set_1588cnt(struct dpa_ptp_tsu *tsu, ++ struct dpa_ptp_time *cnt_time) ++{ ++ struct mac_device *mac_dev = tsu->dpa_priv->mac_dev; ++ u64 tmp; ++ ++ tmp = (u64)cnt_time->sec * NANOSEC_PER_SECOND + (u64)cnt_time->nsec; ++ ++ if (mac_dev->fm_rtc_set_cnt) ++ mac_dev->fm_rtc_set_cnt(get_fm_handle(tsu->dpa_priv->net_dev), ++ tmp); ++ ++ /* Restart fiper two seconds later */ ++ cnt_time->sec += 2; ++ cnt_time->nsec = 0; ++ dpa_set_fiper_alarm(tsu, cnt_time); ++} ++ ++static void dpa_get_drift(struct dpa_ptp_tsu *tsu, u32 *addend) ++{ ++ struct mac_device *mac_dev = tsu->dpa_priv->mac_dev; ++ u32 drift; ++ ++ if (mac_dev->fm_rtc_get_drift) ++ mac_dev->fm_rtc_get_drift(get_fm_handle(tsu->dpa_priv->net_dev), ++ &drift); ++ ++ *addend = drift; ++} ++ ++static void dpa_set_drift(struct dpa_ptp_tsu *tsu, u32 addend) ++{ ++ struct mac_device *mac_dev = tsu->dpa_priv->mac_dev; ++ ++ if (mac_dev->fm_rtc_set_drift) ++ mac_dev->fm_rtc_set_drift(get_fm_handle(tsu->dpa_priv->net_dev), ++ addend); ++} ++ ++static void dpa_flush_timestamp(struct dpa_ptp_tsu *tsu) ++{ ++ dpa_ptp_reset_circ(&tsu->rx_timestamps, DEFAULT_PTP_RX_BUF_SZ); ++ dpa_ptp_reset_circ(&tsu->tx_timestamps, DEFAULT_PTP_TX_BUF_SZ); ++} ++ ++int dpa_ioctl_1588(struct net_device *dev, struct ifreq *ifr, int cmd) ++{ ++ struct dpa_priv_s *priv = netdev_priv(dev); ++ struct dpa_ptp_tsu *tsu = priv->tsu; ++ struct mac_device *mac_dev = priv->mac_dev; ++ struct dpa_ptp_data ptp_data; ++ struct dpa_ptp_data *ptp_data_user; ++ struct dpa_ptp_time act_time; ++ u32 addend; ++ int retval = 0; ++ ++ if (!tsu || !tsu->valid) ++ return -ENODEV; ++ ++ switch (cmd) { ++ case PTP_ENBL_TXTS_IOCTL: ++ tsu->hwts_tx_en_ioctl = 1; ++ if (mac_dev->fm_rtc_enable) ++ mac_dev->fm_rtc_enable(get_fm_handle(dev)); ++ if (mac_dev->ptp_enable) ++ mac_dev->ptp_enable(mac_dev->get_mac_handle(mac_dev)); ++ break; ++ case PTP_DSBL_TXTS_IOCTL: ++ tsu->hwts_tx_en_ioctl = 0; ++ if (mac_dev->fm_rtc_disable) ++ mac_dev->fm_rtc_disable(get_fm_handle(dev)); ++ if (mac_dev->ptp_disable) ++ mac_dev->ptp_disable(mac_dev->get_mac_handle(mac_dev)); ++ break; ++ case PTP_ENBL_RXTS_IOCTL: ++ tsu->hwts_rx_en_ioctl = 1; ++ break; ++ case PTP_DSBL_RXTS_IOCTL: ++ tsu->hwts_rx_en_ioctl = 0; ++ break; ++ case PTP_GET_RX_TIMESTAMP: ++ ptp_data_user = (struct dpa_ptp_data *)ifr->ifr_data; ++ if (copy_from_user(&ptp_data.ident, ++ &ptp_data_user->ident, sizeof(ptp_data.ident))) ++ return -EINVAL; ++ ++ if (dpa_get_rx_timestamp(tsu, &ptp_data.ident, &ptp_data.ts)) ++ return -EAGAIN; ++ ++ if (copy_to_user((void __user *)&ptp_data_user->ts, ++ &ptp_data.ts, sizeof(ptp_data.ts))) ++ return -EFAULT; ++ break; ++ case PTP_GET_TX_TIMESTAMP: ++ ptp_data_user = (struct dpa_ptp_data *)ifr->ifr_data; ++ if (copy_from_user(&ptp_data.ident, ++ &ptp_data_user->ident, sizeof(ptp_data.ident))) ++ return -EINVAL; ++ ++ if (dpa_get_tx_timestamp(tsu, &ptp_data.ident, &ptp_data.ts)) ++ return -EAGAIN; ++ ++ if (copy_to_user((void __user *)&ptp_data_user->ts, ++ &ptp_data.ts, sizeof(ptp_data.ts))) ++ return -EFAULT; ++ break; ++ case PTP_GET_TIME: ++ dpa_get_curr_cnt(tsu, &act_time); ++ if (copy_to_user(ifr->ifr_data, &act_time, sizeof(act_time))) ++ return -EFAULT; ++ break; ++ case PTP_SET_TIME: ++ if (copy_from_user(&act_time, ifr->ifr_data, sizeof(act_time))) ++ return -EINVAL; ++ dpa_set_1588cnt(tsu, &act_time); ++ break; ++ case PTP_GET_ADJ: ++ dpa_get_drift(tsu, &addend); ++ if (copy_to_user(ifr->ifr_data, &addend, sizeof(addend))) ++ return -EFAULT; ++ break; ++ case PTP_SET_ADJ: ++ if (copy_from_user(&addend, ifr->ifr_data, sizeof(addend))) ++ return -EINVAL; ++ dpa_set_drift(tsu, addend); ++ break; ++ case PTP_SET_FIPER_ALARM: ++ if (copy_from_user(&act_time, ifr->ifr_data, sizeof(act_time))) ++ return -EINVAL; ++ dpa_set_fiper_alarm(tsu, &act_time); ++ break; ++ case PTP_CLEANUP_TS: ++ dpa_flush_timestamp(tsu); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return retval; ++} ++ ++int dpa_ptp_init(struct dpa_priv_s *priv) ++{ ++ struct dpa_ptp_tsu *tsu; ++ ++ /* Allocate memory for PTP structure */ ++ tsu = kzalloc(sizeof(struct dpa_ptp_tsu), GFP_KERNEL); ++ if (!tsu) ++ return -ENOMEM; ++ ++ tsu->valid = TRUE; ++ tsu->dpa_priv = priv; ++ ++ dpa_ptp_init_circ(&tsu->rx_timestamps, DEFAULT_PTP_RX_BUF_SZ); ++ dpa_ptp_init_circ(&tsu->tx_timestamps, DEFAULT_PTP_TX_BUF_SZ); ++ ++ priv->tsu = tsu; ++ ++ return 0; ++} ++EXPORT_SYMBOL(dpa_ptp_init); ++ ++void dpa_ptp_cleanup(struct dpa_priv_s *priv) ++{ ++ struct dpa_ptp_tsu *tsu = priv->tsu; ++ ++ tsu->valid = FALSE; ++ vfree(tsu->rx_timestamps.circ_buf.buf); ++ vfree(tsu->tx_timestamps.circ_buf.buf); ++ ++ kfree(tsu); ++} ++EXPORT_SYMBOL(dpa_ptp_cleanup); +--- /dev/null ++++ b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_1588.h +@@ -0,0 +1,138 @@ ++/* Copyright (C) 2011 Freescale Semiconductor, Inc. ++ * ++ * 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. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ */ ++#ifndef __DPAA_1588_H__ ++#define __DPAA_1588_H__ ++ ++#include <linux/netdevice.h> ++#include <linux/etherdevice.h> ++#include <linux/circ_buf.h> ++#include <linux/fsl_qman.h> ++ ++#define DEFAULT_PTP_RX_BUF_SZ 256 ++#define DEFAULT_PTP_TX_BUF_SZ 256 ++ ++/* 1588 private ioctl calls */ ++#define PTP_ENBL_TXTS_IOCTL SIOCDEVPRIVATE ++#define PTP_DSBL_TXTS_IOCTL (SIOCDEVPRIVATE + 1) ++#define PTP_ENBL_RXTS_IOCTL (SIOCDEVPRIVATE + 2) ++#define PTP_DSBL_RXTS_IOCTL (SIOCDEVPRIVATE + 3) ++#define PTP_GET_TX_TIMESTAMP (SIOCDEVPRIVATE + 4) ++#define PTP_GET_RX_TIMESTAMP (SIOCDEVPRIVATE + 5) ++#define PTP_SET_TIME (SIOCDEVPRIVATE + 6) ++#define PTP_GET_TIME (SIOCDEVPRIVATE + 7) ++#define PTP_SET_FIPER_ALARM (SIOCDEVPRIVATE + 8) ++#define PTP_SET_ADJ (SIOCDEVPRIVATE + 9) ++#define PTP_GET_ADJ (SIOCDEVPRIVATE + 10) ++#define PTP_CLEANUP_TS (SIOCDEVPRIVATE + 11) ++ ++/* PTP V2 message type */ ++enum { ++ PTP_MSGTYPE_SYNC = 0x0, ++ PTP_MSGTYPE_DELREQ = 0x1, ++ PTP_MSGTYPE_PDELREQ = 0x2, ++ PTP_MSGTYPE_PDELRESP = 0x3, ++ PTP_MSGTYPE_FLWUP = 0x8, ++ PTP_MSGTYPE_DELRESP = 0x9, ++ PTP_MSGTYPE_PDELRES_FLWUP = 0xA, ++ PTP_MSGTYPE_ANNOUNCE = 0xB, ++ PTP_MSGTYPE_SGNLNG = 0xC, ++ PTP_MSGTYPE_MNGMNT = 0xD, ++}; ++ ++/* Byte offset of data in the PTP V2 headers */ ++#define PTP_OFFS_MSG_TYPE 0 ++#define PTP_OFFS_VER_PTP 1 ++#define PTP_OFFS_MSG_LEN 2 ++#define PTP_OFFS_DOM_NMB 4 ++#define PTP_OFFS_FLAGS 6 ++#define PTP_OFFS_CORFIELD 8 ++#define PTP_OFFS_SRCPRTID 20 ++#define PTP_OFFS_SEQ_ID 30 ++#define PTP_OFFS_CTRL 32 ++#define PTP_OFFS_LOGMEAN 33 ++ ++#define PTP_IP_OFFS 14 ++#define PTP_UDP_OFFS 34 ++#define PTP_HEADER_OFFS 42 ++#define PTP_MSG_TYPE_OFFS (PTP_HEADER_OFFS + PTP_OFFS_MSG_TYPE) ++#define PTP_SPORT_ID_OFFS (PTP_HEADER_OFFS + PTP_OFFS_SRCPRTID) ++#define PTP_SEQ_ID_OFFS (PTP_HEADER_OFFS + PTP_OFFS_SEQ_ID) ++#define PTP_CTRL_OFFS (PTP_HEADER_OFFS + PTP_OFFS_CTRL) ++ ++/* 1588-2008 network protocol enumeration values */ ++#define DPA_PTP_PROT_IPV4 1 ++#define DPA_PTP_PROT_IPV6 2 ++#define DPA_PTP_PROT_802_3 3 ++#define DPA_PTP_PROT_DONTCARE 0xFFFF ++ ++#define DPA_PTP_SOURCE_PORT_LENGTH 10 ++#define DPA_PTP_HEADER_SZE 34 ++#define DPA_ETYPE_LEN 2 ++#define DPA_VLAN_TAG_LEN 4 ++#define NANOSEC_PER_SECOND 1000000000 ++ ++/* The threshold between the current found one and the oldest one */ ++#define TS_ACCUMULATION_THRESHOLD 50 ++ ++/* Struct needed to identify a timestamp */ ++struct dpa_ptp_ident { ++ u8 version; ++ u8 msg_type; ++ u16 netw_prot; ++ u16 seq_id; ++ u8 snd_port_id[DPA_PTP_SOURCE_PORT_LENGTH]; ++}; ++ ++/* Timestamp format in 1588-2008 */ ++struct dpa_ptp_time { ++ u64 sec; /* just 48 bit used */ ++ u32 nsec; ++}; ++ ++/* needed for timestamp data over ioctl */ ++struct dpa_ptp_data { ++ struct dpa_ptp_ident ident; ++ struct dpa_ptp_time ts; ++}; ++ ++struct dpa_ptp_circ_buf { ++ struct circ_buf circ_buf; ++ u32 size; ++ spinlock_t ptp_lock; ++}; ++ ++/* PTP TSU control structure */ ++struct dpa_ptp_tsu { ++ struct dpa_priv_s *dpa_priv; ++ bool valid; ++ struct dpa_ptp_circ_buf rx_timestamps; ++ struct dpa_ptp_circ_buf tx_timestamps; ++ ++ /* HW timestamping over ioctl enabled flag */ ++ int hwts_tx_en_ioctl; ++ int hwts_rx_en_ioctl; ++}; ++ ++extern int dpa_ptp_init(struct dpa_priv_s *priv); ++extern void dpa_ptp_cleanup(struct dpa_priv_s *priv); ++extern void dpa_ptp_store_txstamp(const struct dpa_priv_s *priv, ++ struct sk_buff *skb, void *data); ++extern void dpa_ptp_store_rxstamp(const struct dpa_priv_s *priv, ++ struct sk_buff *skb, void *data); ++extern int dpa_ioctl_1588(struct net_device *dev, struct ifreq *ifr, int cmd); ++#endif +--- /dev/null ++++ b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_debugfs.c +@@ -0,0 +1,180 @@ ++/* Copyright 2008-2013 Freescale Semiconductor Inc. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * * Neither the name of Freescale Semiconductor nor the ++ * names of its contributors may be used to endorse or promote products ++ * derived from this software without specific prior written permission. ++ * ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") as published by the Free Software ++ * Foundation, either version 2 of that License or (at your option) any ++ * later version. ++ * ++ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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/module.h> ++#include <linux/fsl_qman.h> /* struct qm_mcr_querycgr */ ++#include <linux/debugfs.h> ++#include "dpaa_debugfs.h" ++#include "dpaa_eth.h" /* struct dpa_priv_s, dpa_percpu_priv_s, dpa_bp */ ++ ++#define DPA_DEBUGFS_DESCRIPTION "FSL DPAA Ethernet debugfs entries" ++#define DPA_ETH_DEBUGFS_ROOT "fsl_dpa" ++ ++static struct dentry *dpa_debugfs_root; ++ ++static int __cold dpa_debugfs_loop_open(struct inode *inode, struct file *file); ++static ssize_t dpa_loop_write(struct file *f, ++ const char __user *buf, size_t count, loff_t *off); ++ ++static const struct file_operations dpa_debugfs_lp_fops = { ++ .open = dpa_debugfs_loop_open, ++ .write = dpa_loop_write, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static int dpa_debugfs_loop_show(struct seq_file *file, void *offset) ++{ ++ struct dpa_priv_s *priv; ++ ++ BUG_ON(offset == NULL); ++ ++ priv = netdev_priv((struct net_device *)file->private); ++ seq_printf(file, "%d->%d\n", priv->loop_id, priv->loop_to); ++ ++ return 0; ++} ++ ++static int user_input_convert(const char __user *user_buf, size_t count, ++ long *val) ++{ ++ char buf[12]; ++ ++ if (count > sizeof(buf) - 1) ++ return -EINVAL; ++ if (copy_from_user(buf, user_buf, count)) ++ return -EFAULT; ++ buf[count] = '\0'; ++ if (kstrtol(buf, 0, val)) ++ return -EINVAL; ++ return 0; ++} ++ ++static ssize_t dpa_loop_write(struct file *f, ++ const char __user *buf, size_t count, loff_t *off) ++{ ++ struct dpa_priv_s *priv; ++ struct net_device *netdev; ++ struct seq_file *sf; ++ int ret; ++ long val; ++ ++ ret = user_input_convert(buf, count, &val); ++ if (ret) ++ return ret; ++ ++ sf = (struct seq_file *)f->private_data; ++ netdev = (struct net_device *)sf->private; ++ priv = netdev_priv(netdev); ++ ++ priv->loop_to = ((val < 0) || (val > 20)) ? -1 : val; ++ ++ return count; ++} ++ ++static int __cold dpa_debugfs_loop_open(struct inode *inode, struct file *file) ++{ ++ int _errno; ++ const struct net_device *net_dev; ++ ++ _errno = single_open(file, dpa_debugfs_loop_show, inode->i_private); ++ if (unlikely(_errno < 0)) { ++ net_dev = (struct net_device *)inode->i_private; ++ ++ if (netif_msg_drv((struct dpa_priv_s *)netdev_priv(net_dev))) ++ netdev_err(net_dev, "single_open() = %d\n", ++ _errno); ++ } ++ ++ return _errno; ++} ++ ++ ++int dpa_netdev_debugfs_create(struct net_device *net_dev) ++{ ++ struct dpa_priv_s *priv = netdev_priv(net_dev); ++ static int cnt; ++ char loop_file_name[100]; ++ ++ if (unlikely(dpa_debugfs_root == NULL)) { ++ pr_err(KBUILD_MODNAME ": %s:%hu:%s(): \t%s\n", ++ KBUILD_BASENAME".c", __LINE__, __func__, ++ "root debugfs missing, possible module ordering issue"); ++ return -ENOMEM; ++ } ++ ++ sprintf(loop_file_name, "eth%d_loop", ++cnt); ++ priv->debugfs_loop_file = debugfs_create_file(loop_file_name, ++ S_IRUGO, ++ dpa_debugfs_root, ++ net_dev, ++ &dpa_debugfs_lp_fops); ++ if (unlikely(priv->debugfs_loop_file == NULL)) { ++ netdev_err(net_dev, "debugfs_create_file(%s/%s)", ++ dpa_debugfs_root->d_iname, ++ loop_file_name); ++ ++ return -ENOMEM; ++ } ++ return 0; ++} ++ ++void dpa_netdev_debugfs_remove(struct net_device *net_dev) ++{ ++ struct dpa_priv_s *priv = netdev_priv(net_dev); ++ ++ debugfs_remove(priv->debugfs_loop_file); ++} ++ ++int __init dpa_debugfs_module_init(void) ++{ ++ int _errno = 0; ++ ++ pr_info(KBUILD_MODNAME ": " DPA_DEBUGFS_DESCRIPTION "\n"); ++ ++ dpa_debugfs_root = debugfs_create_dir(DPA_ETH_DEBUGFS_ROOT, NULL); ++ ++ if (unlikely(dpa_debugfs_root == NULL)) { ++ _errno = -ENOMEM; ++ pr_err(KBUILD_MODNAME ": %s:%hu:%s():\n", ++ KBUILD_BASENAME".c", __LINE__, __func__); ++ pr_err("\tdebugfs_create_dir(%s/"KBUILD_MODNAME") = %d\n", ++ DPA_ETH_DEBUGFS_ROOT, _errno); ++ } ++ ++ return _errno; ++} ++ ++void __exit dpa_debugfs_module_exit(void) ++{ ++ debugfs_remove(dpa_debugfs_root); ++} +--- /dev/null ++++ b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_debugfs.h +@@ -0,0 +1,43 @@ ++/* Copyright 2008-2013 Freescale Semiconductor Inc. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * * Neither the name of Freescale Semiconductor nor the ++ * names of its contributors may be used to endorse or promote products ++ * derived from this software without specific prior written permission. ++ * ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") as published by the Free Software ++ * Foundation, either version 2 of that License or (at your option) any ++ * later version. ++ * ++ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. ++ */ ++ ++#ifndef DPAA_DEBUGFS_H_ ++#define DPAA_DEBUGFS_H_ ++ ++#include <linux/netdevice.h> ++#include <linux/dcache.h> /* struct dentry needed in dpaa_eth.h */ ++ ++int dpa_netdev_debugfs_create(struct net_device *net_dev); ++void dpa_netdev_debugfs_remove(struct net_device *net_dev); ++int __init dpa_debugfs_module_init(void); ++void __exit dpa_debugfs_module_exit(void); ++ ++#endif /* DPAA_DEBUGFS_H_ */ +--- /dev/null ++++ b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth.c +@@ -0,0 +1,1210 @@ ++/* Copyright 2008-2013 Freescale Semiconductor Inc. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * * Neither the name of Freescale Semiconductor nor the ++ * names of its contributors may be used to endorse or promote products ++ * derived from this software without specific prior written permission. ++ * ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") as published by the Free Software ++ * Foundation, either version 2 of that License or (at your option) any ++ * later version. ++ * ++ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. ++ */ ++ ++#ifdef CONFIG_FSL_DPAA_ETH_DEBUG ++#define pr_fmt(fmt) \ ++ KBUILD_MODNAME ": %s:%hu:%s() " fmt, \ ++ KBUILD_BASENAME".c", __LINE__, __func__ ++#else ++#define pr_fmt(fmt) \ ++ KBUILD_MODNAME ": " fmt ++#endif ++ ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/of_mdio.h> ++#include <linux/of_net.h> ++#include <linux/kthread.h> ++#include <linux/io.h> ++#include <linux/if_arp.h> /* arp_hdr_len() */ ++#include <linux/if_vlan.h> /* VLAN_HLEN */ ++#include <linux/icmp.h> /* struct icmphdr */ ++#include <linux/ip.h> /* struct iphdr */ ++#include <linux/ipv6.h> /* struct ipv6hdr */ ++#include <linux/udp.h> /* struct udphdr */ ++#include <linux/tcp.h> /* struct tcphdr */ ++#include <linux/net.h> /* net_ratelimit() */ ++#include <linux/if_ether.h> /* ETH_P_IP and ETH_P_IPV6 */ ++#include <linux/highmem.h> ++#include <linux/percpu.h> ++#include <linux/dma-mapping.h> ++#include <linux/fsl_bman.h> ++#ifdef CONFIG_SOC_BUS ++#include <linux/sys_soc.h> /* soc_device_match */ ++#endif ++ ++#include "fsl_fman.h" ++#include "fm_ext.h" ++#include "fm_port_ext.h" ++ ++#include "mac.h" ++#include "dpaa_eth.h" ++#include "dpaa_eth_common.h" ++#ifdef CONFIG_FSL_DPAA_DBG_LOOP ++#include "dpaa_debugfs.h" ++#endif /* CONFIG_FSL_DPAA_DBG_LOOP */ ++ ++/* CREATE_TRACE_POINTS only needs to be defined once. Other dpa files ++ * using trace events only need to #include <trace/events/sched.h> ++ */ ++#define CREATE_TRACE_POINTS ++#include "dpaa_eth_trace.h" ++ ++#define DPA_NAPI_WEIGHT 64 ++ ++/* Valid checksum indication */ ++#define DPA_CSUM_VALID 0xFFFF ++ ++#define DPA_DESCRIPTION "FSL DPAA Ethernet driver" ++ ++MODULE_LICENSE("Dual BSD/GPL"); ++ ++MODULE_AUTHOR("Andy Fleming <afleming@freescale.com>"); ++ ++MODULE_DESCRIPTION(DPA_DESCRIPTION); ++ ++static uint8_t debug = -1; ++module_param(debug, byte, S_IRUGO); ++MODULE_PARM_DESC(debug, "Module/Driver verbosity level"); ++ ++/* This has to work in tandem with the DPA_CS_THRESHOLD_xxx values. */ ++static uint16_t tx_timeout = 1000; ++module_param(tx_timeout, ushort, S_IRUGO); ++MODULE_PARM_DESC(tx_timeout, "The Tx timeout in ms"); ++ ++static const char rtx[][3] = { ++ [RX] = "RX", ++ [TX] = "TX" ++}; ++ ++#ifndef CONFIG_PPC ++bool dpaa_errata_a010022; ++EXPORT_SYMBOL(dpaa_errata_a010022); ++#endif ++ ++/* BM */ ++ ++#define DPAA_ETH_MAX_PAD (L1_CACHE_BYTES * 8) ++ ++static uint8_t dpa_priv_common_bpid; ++ ++#ifdef CONFIG_FSL_DPAA_DBG_LOOP ++struct net_device *dpa_loop_netdevs[20]; ++#endif ++ ++#ifdef CONFIG_PM ++ ++static int dpaa_suspend(struct device *dev) ++{ ++ struct net_device *net_dev; ++ struct dpa_priv_s *priv; ++ struct mac_device *mac_dev; ++ int err = 0; ++ ++ net_dev = dev_get_drvdata(dev); ++ ++ if (net_dev->flags & IFF_UP) { ++ priv = netdev_priv(net_dev); ++ mac_dev = priv->mac_dev; ++ ++ if (priv->wol & DPAA_WOL_MAGIC) { ++ err = priv->mac_dev->set_wol(mac_dev->port_dev[RX], ++ priv->mac_dev->get_mac_handle(mac_dev), true); ++ if (err) { ++ netdev_err(net_dev, "set_wol() = %d\n", err); ++ goto set_wol_failed; ++ } ++ } ++ ++ err = fm_port_suspend(mac_dev->port_dev[RX]); ++ if (err) { ++ netdev_err(net_dev, "fm_port_suspend(RX) = %d\n", err); ++ goto rx_port_suspend_failed; ++ } ++ ++ err = fm_port_suspend(mac_dev->port_dev[TX]); ++ if (err) { ++ netdev_err(net_dev, "fm_port_suspend(TX) = %d\n", err); ++ goto tx_port_suspend_failed; ++ } ++ } ++ ++ return 0; ++ ++tx_port_suspend_failed: ++ fm_port_resume(mac_dev->port_dev[RX]); ++rx_port_suspend_failed: ++ if (priv->wol & DPAA_WOL_MAGIC) { ++ priv->mac_dev->set_wol(mac_dev->port_dev[RX], ++ priv->mac_dev->get_mac_handle(mac_dev), false); ++ } ++set_wol_failed: ++ return err; ++} ++ ++static int dpaa_resume(struct device *dev) ++{ ++ struct net_device *net_dev; ++ struct dpa_priv_s *priv; ++ struct mac_device *mac_dev; ++ int err = 0; ++ ++ net_dev = dev_get_drvdata(dev); ++ ++ if (net_dev->flags & IFF_UP) { ++ priv = netdev_priv(net_dev); ++ mac_dev = priv->mac_dev; ++ ++ err = fm_mac_resume(mac_dev->get_mac_handle(mac_dev)); ++ if (err) { ++ netdev_err(net_dev, "fm_mac_resume = %d\n", err); ++ goto resume_failed; ++ } ++ ++ err = fm_port_resume(mac_dev->port_dev[TX]); ++ if (err) { ++ netdev_err(net_dev, "fm_port_resume(TX) = %d\n", err); ++ goto resume_failed; ++ } ++ ++ err = fm_port_resume(mac_dev->port_dev[RX]); ++ if (err) { ++ netdev_err(net_dev, "fm_port_resume(RX) = %d\n", err); ++ goto resume_failed; ++ } ++ ++ if (priv->wol & DPAA_WOL_MAGIC) { ++ err = priv->mac_dev->set_wol(mac_dev->port_dev[RX], ++ priv->mac_dev->get_mac_handle(mac_dev), false); ++ if (err) { ++ netdev_err(net_dev, "set_wol() = %d\n", err); ++ goto resume_failed; ++ } ++ } ++ } ++ ++ return 0; ++ ++resume_failed: ++ return err; ++} ++ ++static const struct dev_pm_ops dpaa_pm_ops = { ++ .suspend = dpaa_suspend, ++ .resume = dpaa_resume, ++}; ++ ++#define DPAA_PM_OPS (&dpaa_pm_ops) ++ ++#else /* CONFIG_PM */ ++ ++#define DPAA_PM_OPS NULL ++ ++#endif /* CONFIG_PM */ ++ ++/* Checks whether the checksum field in Parse Results array is valid ++ * (equals 0xFFFF) and increments the .cse counter otherwise ++ */ ++static inline void ++dpa_csum_validation(const struct dpa_priv_s *priv, ++ struct dpa_percpu_priv_s *percpu_priv, ++ const struct qm_fd *fd) ++{ ++ dma_addr_t addr = qm_fd_addr(fd); ++ struct dpa_bp *dpa_bp = priv->dpa_bp; ++ void *frm = phys_to_virt(addr); ++ fm_prs_result_t *parse_result; ++ ++ if (unlikely(!frm)) ++ return; ++ ++ dma_sync_single_for_cpu(dpa_bp->dev, addr, DPA_RX_PRIV_DATA_SIZE + ++ DPA_PARSE_RESULTS_SIZE, DMA_BIDIRECTIONAL); ++ ++ parse_result = (fm_prs_result_t *)(frm + DPA_RX_PRIV_DATA_SIZE); ++ ++ if (parse_result->cksum != DPA_CSUM_VALID) ++ percpu_priv->rx_errors.cse++; ++} ++ ++static void _dpa_rx_error(struct net_device *net_dev, ++ const struct dpa_priv_s *priv, ++ struct dpa_percpu_priv_s *percpu_priv, ++ const struct qm_fd *fd, ++ u32 fqid) ++{ ++ /* limit common, possibly innocuous Rx FIFO Overflow errors' ++ * interference with zero-loss convergence benchmark results. ++ */ ++ if (likely(fd->status & FM_FD_STAT_ERR_PHYSICAL)) ++ pr_warn_once("fsl-dpa: non-zero error counters in fman statistics (sysfs)\n"); ++ else ++ if (netif_msg_hw(priv) && net_ratelimit()) ++ netdev_dbg(net_dev, "Err FD status = 0x%08x\n", ++ fd->status & FM_FD_STAT_RX_ERRORS); ++#ifdef CONFIG_FSL_DPAA_HOOKS ++ if (dpaa_eth_hooks.rx_error && ++ dpaa_eth_hooks.rx_error(net_dev, fd, fqid) == DPAA_ETH_STOLEN) ++ /* it's up to the hook to perform resource cleanup */ ++ return; ++#endif ++ percpu_priv->stats.rx_errors++; ++ ++ if (fd->status & FM_PORT_FRM_ERR_DMA) ++ percpu_priv->rx_errors.dme++; ++ if (fd->status & FM_PORT_FRM_ERR_PHYSICAL) ++ percpu_priv->rx_errors.fpe++; ++ if (fd->status & FM_PORT_FRM_ERR_SIZE) ++ percpu_priv->rx_errors.fse++; ++ if (fd->status & FM_PORT_FRM_ERR_PRS_HDR_ERR) ++ percpu_priv->rx_errors.phe++; ++ if (fd->status & FM_FD_STAT_L4CV) ++ dpa_csum_validation(priv, percpu_priv, fd); ++ ++ dpa_fd_release(net_dev, fd); ++} ++ ++static void _dpa_tx_error(struct net_device *net_dev, ++ const struct dpa_priv_s *priv, ++ struct dpa_percpu_priv_s *percpu_priv, ++ const struct qm_fd *fd, ++ u32 fqid) ++{ ++ struct sk_buff *skb; ++ ++ if (netif_msg_hw(priv) && net_ratelimit()) ++ netdev_warn(net_dev, "FD status = 0x%08x\n", ++ fd->status & FM_FD_STAT_TX_ERRORS); ++#ifdef CONFIG_FSL_DPAA_HOOKS ++ if (dpaa_eth_hooks.tx_error && ++ dpaa_eth_hooks.tx_error(net_dev, fd, fqid) == DPAA_ETH_STOLEN) ++ /* now the hook must ensure proper cleanup */ ++ return; ++#endif ++ percpu_priv->stats.tx_errors++; ++ ++ /* If we intended the buffers from this frame to go into the bpools ++ * when the FMan transmit was done, we need to put it in manually. ++ */ ++ if (fd->bpid != 0xff) { ++ dpa_fd_release(net_dev, fd); ++ return; ++ } ++ ++ skb = _dpa_cleanup_tx_fd(priv, fd); ++ dev_kfree_skb(skb); ++} ++ ++/* Helper function to factor out frame validation logic on all Rx paths. Its ++ * purpose is to extract from the Parse Results structure information about ++ * the integrity of the frame, its checksum, the length of the parsed headers ++ * and whether the frame is suitable for GRO. ++ * ++ * Assumes no parser errors, since any error frame is dropped before this ++ * function is called. ++ * ++ * @skb will have its ip_summed field overwritten; ++ * @use_gro will only be written with 0, if the frame is definitely not ++ * GRO-able; otherwise, it will be left unchanged; ++ * @hdr_size will be written with a safe value, at least the size of the ++ * headers' length. ++ */ ++void __hot _dpa_process_parse_results(const fm_prs_result_t *parse_results, ++ const struct qm_fd *fd, ++ struct sk_buff *skb, int *use_gro) ++{ ++ if (fd->status & FM_FD_STAT_L4CV) { ++ /* The parser has run and performed L4 checksum validation. ++ * We know there were no parser errors (and implicitly no ++ * L4 csum error), otherwise we wouldn't be here. ++ */ ++ skb->ip_summed = CHECKSUM_UNNECESSARY; ++ ++ /* Don't go through GRO for certain types of traffic that ++ * we know are not GRO-able, such as dgram-based protocols. ++ * In the worst-case scenarios, such as small-pkt terminating ++ * UDP, the extra GRO processing would be overkill. ++ * ++ * The only protocol the Parser supports that is also GRO-able ++ * is currently TCP. ++ */ ++ if (!fm_l4_frame_is_tcp(parse_results)) ++ *use_gro = 0; ++ ++ return; ++ } ++ ++ /* We're here because either the parser didn't run or the L4 checksum ++ * was not verified. This may include the case of a UDP frame with ++ * checksum zero or an L4 proto other than TCP/UDP ++ */ ++ skb->ip_summed = CHECKSUM_NONE; ++ ++ /* Bypass GRO for unknown traffic or if no PCDs are applied */ ++ *use_gro = 0; ++} ++ ++int dpaa_eth_poll(struct napi_struct *napi, int budget) ++{ ++ struct dpa_napi_portal *np = ++ container_of(napi, struct dpa_napi_portal, napi); ++ ++ int cleaned = qman_p_poll_dqrr(np->p, budget); ++ ++ if (cleaned < budget) { ++ int tmp; ++ napi_complete(napi); ++ tmp = qman_p_irqsource_add(np->p, QM_PIRQ_DQRI); ++ DPA_BUG_ON(tmp); ++ } ++ ++ return cleaned; ++} ++EXPORT_SYMBOL(dpaa_eth_poll); ++ ++static void __hot _dpa_tx_conf(struct net_device *net_dev, ++ const struct dpa_priv_s *priv, ++ struct dpa_percpu_priv_s *percpu_priv, ++ const struct qm_fd *fd, ++ u32 fqid) ++{ ++ struct sk_buff *skb; ++ ++ /* do we need the timestamp for the error frames? */ ++ ++ if (unlikely(fd->status & FM_FD_STAT_TX_ERRORS) != 0) { ++ if (netif_msg_hw(priv) && net_ratelimit()) ++ netdev_warn(net_dev, "FD status = 0x%08x\n", ++ fd->status & FM_FD_STAT_TX_ERRORS); ++ ++ percpu_priv->stats.tx_errors++; ++ } ++ ++ /* hopefully we need not get the timestamp before the hook */ ++#ifdef CONFIG_FSL_DPAA_HOOKS ++ if (dpaa_eth_hooks.tx_confirm && dpaa_eth_hooks.tx_confirm(net_dev, ++ fd, fqid) == DPAA_ETH_STOLEN) ++ /* it's the hook that must now perform cleanup */ ++ return; ++#endif ++ /* This might not perfectly reflect the reality, if the core dequeuing ++ * the Tx confirmation is different from the one that did the enqueue, ++ * but at least it'll show up in the total count. ++ */ ++ percpu_priv->tx_confirm++; ++ ++ skb = _dpa_cleanup_tx_fd(priv, fd); ++ ++ dev_kfree_skb(skb); ++} ++ ++enum qman_cb_dqrr_result ++priv_rx_error_dqrr(struct qman_portal *portal, ++ struct qman_fq *fq, ++ const struct qm_dqrr_entry *dq) ++{ ++ struct net_device *net_dev; ++ struct dpa_priv_s *priv; ++ struct dpa_percpu_priv_s *percpu_priv; ++ int *count_ptr; ++ ++ net_dev = ((struct dpa_fq *)fq)->net_dev; ++ priv = netdev_priv(net_dev); ++ ++ percpu_priv = raw_cpu_ptr(priv->percpu_priv); ++ count_ptr = raw_cpu_ptr(priv->dpa_bp->percpu_count); ++ ++ if (dpaa_eth_napi_schedule(percpu_priv, portal)) ++ return qman_cb_dqrr_stop; ++ ++ if (unlikely(dpaa_eth_refill_bpools(priv->dpa_bp, count_ptr))) ++ /* Unable to refill the buffer pool due to insufficient ++ * system memory. Just release the frame back into the pool, ++ * otherwise we'll soon end up with an empty buffer pool. ++ */ ++ dpa_fd_release(net_dev, &dq->fd); ++ else ++ _dpa_rx_error(net_dev, priv, percpu_priv, &dq->fd, fq->fqid); ++ ++ return qman_cb_dqrr_consume; ++} ++ ++ ++enum qman_cb_dqrr_result __hot ++priv_rx_default_dqrr(struct qman_portal *portal, ++ struct qman_fq *fq, ++ const struct qm_dqrr_entry *dq) ++{ ++ struct net_device *net_dev; ++ struct dpa_priv_s *priv; ++ struct dpa_percpu_priv_s *percpu_priv; ++ int *count_ptr; ++ struct dpa_bp *dpa_bp; ++ ++ net_dev = ((struct dpa_fq *)fq)->net_dev; ++ priv = netdev_priv(net_dev); ++ dpa_bp = priv->dpa_bp; ++ ++ /* Trace the Rx fd */ ++ trace_dpa_rx_fd(net_dev, fq, &dq->fd); ++ ++ /* IRQ handler, non-migratable; safe to use raw_cpu_ptr here */ ++ percpu_priv = raw_cpu_ptr(priv->percpu_priv); ++ count_ptr = raw_cpu_ptr(dpa_bp->percpu_count); ++ ++ if (unlikely(dpaa_eth_napi_schedule(percpu_priv, portal))) ++ return qman_cb_dqrr_stop; ++ ++ /* Vale of plenty: make sure we didn't run out of buffers */ ++ ++ if (unlikely(dpaa_eth_refill_bpools(dpa_bp, count_ptr))) ++ /* Unable to refill the buffer pool due to insufficient ++ * system memory. Just release the frame back into the pool, ++ * otherwise we'll soon end up with an empty buffer pool. ++ */ ++ dpa_fd_release(net_dev, &dq->fd); ++ else ++ _dpa_rx(net_dev, portal, priv, percpu_priv, &dq->fd, fq->fqid, ++ count_ptr); ++ ++ return qman_cb_dqrr_consume; ++} ++ ++enum qman_cb_dqrr_result ++priv_tx_conf_error_dqrr(struct qman_portal *portal, ++ struct qman_fq *fq, ++ const struct qm_dqrr_entry *dq) ++{ ++ struct net_device *net_dev; ++ struct dpa_priv_s *priv; ++ struct dpa_percpu_priv_s *percpu_priv; ++ ++ net_dev = ((struct dpa_fq *)fq)->net_dev; ++ priv = netdev_priv(net_dev); ++ ++ percpu_priv = raw_cpu_ptr(priv->percpu_priv); ++ ++ if (dpaa_eth_napi_schedule(percpu_priv, portal)) ++ return qman_cb_dqrr_stop; ++ ++ _dpa_tx_error(net_dev, priv, percpu_priv, &dq->fd, fq->fqid); ++ ++ return qman_cb_dqrr_consume; ++} ++ ++enum qman_cb_dqrr_result __hot ++priv_tx_conf_default_dqrr(struct qman_portal *portal, ++ struct qman_fq *fq, ++ const struct qm_dqrr_entry *dq) ++{ ++ struct net_device *net_dev; ++ struct dpa_priv_s *priv; ++ struct dpa_percpu_priv_s *percpu_priv; ++ ++ net_dev = ((struct dpa_fq *)fq)->net_dev; ++ priv = netdev_priv(net_dev); ++ ++ /* Trace the fd */ ++ trace_dpa_tx_conf_fd(net_dev, fq, &dq->fd); ++ ++ /* Non-migratable context, safe to use raw_cpu_ptr */ ++ percpu_priv = raw_cpu_ptr(priv->percpu_priv); ++ ++ if (dpaa_eth_napi_schedule(percpu_priv, portal)) ++ return qman_cb_dqrr_stop; ++ ++ _dpa_tx_conf(net_dev, priv, percpu_priv, &dq->fd, fq->fqid); ++ ++ return qman_cb_dqrr_consume; ++} ++ ++void priv_ern(struct qman_portal *portal, ++ struct qman_fq *fq, ++ const struct qm_mr_entry *msg) ++{ ++ struct net_device *net_dev; ++ const struct dpa_priv_s *priv; ++ struct sk_buff *skb; ++ struct dpa_percpu_priv_s *percpu_priv; ++ struct qm_fd fd = msg->ern.fd; ++ ++ net_dev = ((struct dpa_fq *)fq)->net_dev; ++ priv = netdev_priv(net_dev); ++ /* Non-migratable context, safe to use raw_cpu_ptr */ ++ percpu_priv = raw_cpu_ptr(priv->percpu_priv); ++ ++ percpu_priv->stats.tx_dropped++; ++ percpu_priv->stats.tx_fifo_errors++; ++ count_ern(percpu_priv, msg); ++ ++ /* If we intended this buffer to go into the pool ++ * when the FM was done, we need to put it in ++ * manually. ++ */ ++ if (msg->ern.fd.bpid != 0xff) { ++ dpa_fd_release(net_dev, &fd); ++ return; ++ } ++ ++ skb = _dpa_cleanup_tx_fd(priv, &fd); ++ dev_kfree_skb_any(skb); ++} ++ ++const struct dpa_fq_cbs_t private_fq_cbs = { ++ .rx_defq = { .cb = { .dqrr = priv_rx_default_dqrr } }, ++ .tx_defq = { .cb = { .dqrr = priv_tx_conf_default_dqrr } }, ++ .rx_errq = { .cb = { .dqrr = priv_rx_error_dqrr } }, ++ .tx_errq = { .cb = { .dqrr = priv_tx_conf_error_dqrr } }, ++ .egress_ern = { .cb = { .ern = priv_ern } } ++}; ++EXPORT_SYMBOL(private_fq_cbs); ++ ++static void dpaa_eth_napi_enable(struct dpa_priv_s *priv) ++{ ++ struct dpa_percpu_priv_s *percpu_priv; ++ int i, j; ++ ++ for_each_possible_cpu(i) { ++ percpu_priv = per_cpu_ptr(priv->percpu_priv, i); ++ ++ for (j = 0; j < qman_portal_max; j++) ++ napi_enable(&percpu_priv->np[j].napi); ++ } ++} ++ ++static void dpaa_eth_napi_disable(struct dpa_priv_s *priv) ++{ ++ struct dpa_percpu_priv_s *percpu_priv; ++ int i, j; ++ ++ for_each_possible_cpu(i) { ++ percpu_priv = per_cpu_ptr(priv->percpu_priv, i); ++ ++ for (j = 0; j < qman_portal_max; j++) ++ napi_disable(&percpu_priv->np[j].napi); ++ } ++} ++ ++static int __cold dpa_eth_priv_start(struct net_device *net_dev) ++{ ++ int err; ++ struct dpa_priv_s *priv; ++ ++ priv = netdev_priv(net_dev); ++ ++ dpaa_eth_napi_enable(priv); ++ ++ err = dpa_start(net_dev); ++ if (err < 0) ++ dpaa_eth_napi_disable(priv); ++ ++ return err; ++} ++ ++ ++ ++static int __cold dpa_eth_priv_stop(struct net_device *net_dev) ++{ ++ int _errno; ++ struct dpa_priv_s *priv; ++ ++ _errno = dpa_stop(net_dev); ++ /* Allow NAPI to consume any frame still in the Rx/TxConfirm ++ * ingress queues. This is to avoid a race between the current ++ * context and ksoftirqd which could leave NAPI disabled while ++ * in fact there's still Rx traffic to be processed. ++ */ ++ usleep_range(5000, 10000); ++ ++ priv = netdev_priv(net_dev); ++ dpaa_eth_napi_disable(priv); ++ ++ return _errno; ++} ++ ++#ifdef CONFIG_NET_POLL_CONTROLLER ++static void dpaa_eth_poll_controller(struct net_device *net_dev) ++{ ++ struct dpa_priv_s *priv = netdev_priv(net_dev); ++ struct dpa_percpu_priv_s *percpu_priv = ++ raw_cpu_ptr(priv->percpu_priv); ++ struct qman_portal *p; ++ const struct qman_portal_config *pc; ++ struct dpa_napi_portal *np; ++ ++ p = (struct qman_portal *)qman_get_affine_portal(smp_processor_id()); ++ pc = qman_p_get_portal_config(p); ++ np = &percpu_priv->np[pc->index]; ++ ++ qman_p_irqsource_remove(np->p, QM_PIRQ_DQRI); ++ qman_p_poll_dqrr(np->p, np->napi.weight); ++ qman_p_irqsource_add(np->p, QM_PIRQ_DQRI); ++} ++#endif ++ ++static const struct net_device_ops dpa_private_ops = { ++ .ndo_open = dpa_eth_priv_start, ++ .ndo_start_xmit = dpa_tx, ++ .ndo_stop = dpa_eth_priv_stop, ++ .ndo_tx_timeout = dpa_timeout, ++ .ndo_get_stats64 = dpa_get_stats64, ++ .ndo_set_mac_address = dpa_set_mac_address, ++ .ndo_validate_addr = eth_validate_addr, ++#ifdef CONFIG_FSL_DPAA_ETH_USE_NDO_SELECT_QUEUE ++ .ndo_select_queue = dpa_select_queue, ++#endif ++ .ndo_change_mtu = dpa_change_mtu, ++ .ndo_set_rx_mode = dpa_set_rx_mode, ++ .ndo_init = dpa_ndo_init, ++ .ndo_set_features = dpa_set_features, ++ .ndo_fix_features = dpa_fix_features, ++ .ndo_do_ioctl = dpa_ioctl, ++#ifdef CONFIG_NET_POLL_CONTROLLER ++ .ndo_poll_controller = dpaa_eth_poll_controller, ++#endif ++}; ++ ++static int dpa_private_napi_add(struct net_device *net_dev) ++{ ++ struct dpa_priv_s *priv = netdev_priv(net_dev); ++ struct dpa_percpu_priv_s *percpu_priv; ++ int i, cpu; ++ ++ for_each_possible_cpu(cpu) { ++ percpu_priv = per_cpu_ptr(priv->percpu_priv, cpu); ++ ++ percpu_priv->np = devm_kzalloc(net_dev->dev.parent, ++ qman_portal_max * sizeof(struct dpa_napi_portal), ++ GFP_KERNEL); ++ ++ if (unlikely(percpu_priv->np == NULL)) { ++ dev_err(net_dev->dev.parent, "devm_kzalloc() failed\n"); ++ return -ENOMEM; ++ } ++ ++ for (i = 0; i < qman_portal_max; i++) ++ netif_napi_add(net_dev, &percpu_priv->np[i].napi, ++ dpaa_eth_poll, DPA_NAPI_WEIGHT); ++ } ++ ++ return 0; ++} ++ ++void dpa_private_napi_del(struct net_device *net_dev) ++{ ++ struct dpa_priv_s *priv = netdev_priv(net_dev); ++ struct dpa_percpu_priv_s *percpu_priv; ++ int i, cpu; ++ ++ for_each_possible_cpu(cpu) { ++ percpu_priv = per_cpu_ptr(priv->percpu_priv, cpu); ++ ++ if (percpu_priv->np) { ++ for (i = 0; i < qman_portal_max; i++) ++ netif_napi_del(&percpu_priv->np[i].napi); ++ ++ devm_kfree(net_dev->dev.parent, percpu_priv->np); ++ } ++ } ++} ++EXPORT_SYMBOL(dpa_private_napi_del); ++ ++static int dpa_private_netdev_init(struct net_device *net_dev) ++{ ++ int i; ++ struct dpa_priv_s *priv = netdev_priv(net_dev); ++ struct dpa_percpu_priv_s *percpu_priv; ++ const uint8_t *mac_addr; ++ ++ /* Although we access another CPU's private data here ++ * we do it at initialization so it is safe ++ */ ++ for_each_possible_cpu(i) { ++ percpu_priv = per_cpu_ptr(priv->percpu_priv, i); ++ percpu_priv->net_dev = net_dev; ++ } ++ ++ net_dev->netdev_ops = &dpa_private_ops; ++ mac_addr = priv->mac_dev->addr; ++ ++ net_dev->mem_start = priv->mac_dev->res->start; ++ net_dev->mem_end = priv->mac_dev->res->end; ++ ++ net_dev->hw_features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | ++ NETIF_F_LLTX); ++ ++ /* Advertise S/G and HIGHDMA support for private interfaces */ ++ net_dev->hw_features |= NETIF_F_SG | NETIF_F_HIGHDMA; ++ /* Recent kernels enable GSO automatically, if ++ * we declare NETIF_F_SG. For conformity, we'll ++ * still declare GSO explicitly. ++ */ ++ net_dev->features |= NETIF_F_GSO; ++ ++ /* Advertise GRO support */ ++ net_dev->features |= NETIF_F_GRO; ++ ++ return dpa_netdev_init(net_dev, mac_addr, tx_timeout); ++} ++ ++static struct dpa_bp * __cold ++dpa_priv_bp_probe(struct device *dev) ++{ ++ struct dpa_bp *dpa_bp; ++ ++ dpa_bp = devm_kzalloc(dev, sizeof(*dpa_bp), GFP_KERNEL); ++ if (unlikely(dpa_bp == NULL)) { ++ dev_err(dev, "devm_kzalloc() failed\n"); ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ dpa_bp->percpu_count = devm_alloc_percpu(dev, *dpa_bp->percpu_count); ++ dpa_bp->target_count = CONFIG_FSL_DPAA_ETH_MAX_BUF_COUNT; ++ ++ dpa_bp->seed_cb = dpa_bp_priv_seed; ++ dpa_bp->free_buf_cb = _dpa_bp_free_pf; ++ ++ return dpa_bp; ++} ++ ++/* Place all ingress FQs (Rx Default, Rx Error, PCD FQs) in a dedicated CGR. ++ * We won't be sending congestion notifications to FMan; for now, we just use ++ * this CGR to generate enqueue rejections to FMan in order to drop the frames ++ * before they reach our ingress queues and eat up memory. ++ */ ++static int dpaa_eth_priv_ingress_cgr_init(struct dpa_priv_s *priv) ++{ ++ struct qm_mcc_initcgr initcgr; ++ u32 cs_th; ++ int err; ++ ++ err = qman_alloc_cgrid(&priv->ingress_cgr.cgrid); ++ if (err < 0) { ++ pr_err("Error %d allocating CGR ID\n", err); ++ goto out_error; ++ } ++ ++ /* Enable CS TD, but disable Congestion State Change Notifications. */ ++ initcgr.we_mask = QM_CGR_WE_CS_THRES; ++ initcgr.cgr.cscn_en = QM_CGR_EN; ++ cs_th = CONFIG_FSL_DPAA_INGRESS_CS_THRESHOLD; ++ qm_cgr_cs_thres_set64(&initcgr.cgr.cs_thres, cs_th, 1); ++ ++ initcgr.we_mask |= QM_CGR_WE_CSTD_EN; ++ initcgr.cgr.cstd_en = QM_CGR_EN; ++ ++ /* This is actually a hack, because this CGR will be associated with ++ * our affine SWP. However, we'll place our ingress FQs in it. ++ */ ++ err = qman_create_cgr(&priv->ingress_cgr, QMAN_CGR_FLAG_USE_INIT, ++ &initcgr); ++ if (err < 0) { ++ pr_err("Error %d creating ingress CGR with ID %d\n", err, ++ priv->ingress_cgr.cgrid); ++ qman_release_cgrid(priv->ingress_cgr.cgrid); ++ goto out_error; ++ } ++ pr_debug("Created ingress CGR %d for netdev with hwaddr %pM\n", ++ priv->ingress_cgr.cgrid, priv->mac_dev->addr); ++ ++ /* struct qman_cgr allows special cgrid values (i.e. outside the 0..255 ++ * range), but we have no common initialization path between the ++ * different variants of the DPAA Eth driver, so we do it here rather ++ * than modifying every other variant than "private Eth". ++ */ ++ priv->use_ingress_cgr = true; ++ ++out_error: ++ return err; ++} ++ ++static int dpa_priv_bp_create(struct net_device *net_dev, struct dpa_bp *dpa_bp, ++ size_t count) ++{ ++ struct dpa_priv_s *priv = netdev_priv(net_dev); ++ int i; ++ ++ if (netif_msg_probe(priv)) ++ dev_dbg(net_dev->dev.parent, ++ "Using private BM buffer pools\n"); ++ ++ priv->bp_count = count; ++ ++ for (i = 0; i < count; i++) { ++ int err; ++ err = dpa_bp_alloc(&dpa_bp[i]); ++ if (err < 0) { ++ dpa_bp_free(priv); ++ priv->dpa_bp = NULL; ++ return err; ++ } ++ ++ priv->dpa_bp = &dpa_bp[i]; ++ } ++ ++ dpa_priv_common_bpid = priv->dpa_bp->bpid; ++ return 0; ++} ++ ++static const struct of_device_id dpa_match[]; ++ ++#ifdef CONFIG_FSL_DPAA_DBG_LOOP ++static int dpa_new_loop_id(void) ++{ ++ static int if_id; ++ ++ return if_id++; ++} ++#endif ++ ++static int ++dpaa_eth_priv_probe(struct platform_device *_of_dev) ++{ ++ int err = 0, i, channel; ++ struct device *dev; ++ struct device_node *dpa_node; ++ struct dpa_bp *dpa_bp; ++ size_t count = 1; ++ struct net_device *net_dev = NULL; ++ struct dpa_priv_s *priv = NULL; ++ struct dpa_percpu_priv_s *percpu_priv; ++ struct fm_port_fqs port_fqs; ++ struct dpa_buffer_layout_s *buf_layout = NULL; ++ struct mac_device *mac_dev; ++ ++ dev = &_of_dev->dev; ++ ++ dpa_node = dev->of_node; ++ ++ if (!of_device_is_available(dpa_node)) ++ return -ENODEV; ++ ++ /* Get the buffer pools assigned to this interface; ++ * run only once the default pool probing code ++ */ ++ dpa_bp = (dpa_bpid2pool(dpa_priv_common_bpid)) ? : ++ dpa_priv_bp_probe(dev); ++ if (IS_ERR(dpa_bp)) ++ return PTR_ERR(dpa_bp); ++ ++ /* Allocate this early, so we can store relevant information in ++ * the private area (needed by 1588 code in dpa_mac_probe) ++ */ ++ net_dev = alloc_etherdev_mq(sizeof(*priv), DPAA_ETH_TX_QUEUES); ++ if (!net_dev) { ++ dev_err(dev, "alloc_etherdev_mq() failed\n"); ++ goto alloc_etherdev_mq_failed; ++ } ++ ++ /* Do this here, so we can be verbose early */ ++ SET_NETDEV_DEV(net_dev, dev); ++ dev_set_drvdata(dev, net_dev); ++ ++ priv = netdev_priv(net_dev); ++ priv->net_dev = net_dev; ++ strcpy(priv->if_type, "private"); ++ ++ priv->msg_enable = netif_msg_init(debug, -1); ++ ++#ifdef CONFIG_FSL_DPAA_DBG_LOOP ++ priv->loop_id = dpa_new_loop_id(); ++ priv->loop_to = -1; /* disabled by default */ ++ dpa_loop_netdevs[priv->loop_id] = net_dev; ++#endif ++ ++ mac_dev = dpa_mac_probe(_of_dev); ++ if (IS_ERR(mac_dev) || !mac_dev) { ++ err = PTR_ERR(mac_dev); ++ goto mac_probe_failed; ++ } ++ ++ /* We have physical ports, so we need to establish ++ * the buffer layout. ++ */ ++ buf_layout = devm_kzalloc(dev, 2 * sizeof(*buf_layout), ++ GFP_KERNEL); ++ if (!buf_layout) { ++ dev_err(dev, "devm_kzalloc() failed\n"); ++ goto alloc_failed; ++ } ++ dpa_set_buffers_layout(mac_dev, buf_layout); ++ ++ /* For private ports, need to compute the size of the default ++ * buffer pool, based on FMan port buffer layout;also update ++ * the maximum buffer size for private ports if necessary ++ */ ++ dpa_bp->size = dpa_bp_size(&buf_layout[RX]); ++ ++#ifdef CONFIG_FSL_DPAA_ETH_JUMBO_FRAME ++ /* We only want to use jumbo frame optimization if we actually have ++ * L2 MAX FRM set for jumbo frames as well. ++ */ ++#ifndef CONFIG_PPC ++ if (likely(!dpaa_errata_a010022)) ++#endif ++ if(fm_get_max_frm() < 9600) ++ dev_warn(dev, ++ "Invalid configuration: if jumbo frames support is on, FSL_FM_MAX_FRAME_SIZE should be set to 9600\n"); ++#endif ++ ++ INIT_LIST_HEAD(&priv->dpa_fq_list); ++ ++ memset(&port_fqs, 0, sizeof(port_fqs)); ++ ++ err = dpa_fq_probe_mac(dev, &priv->dpa_fq_list, &port_fqs, true, RX); ++ if (!err) ++ err = dpa_fq_probe_mac(dev, &priv->dpa_fq_list, ++ &port_fqs, true, TX); ++ ++ if (err < 0) ++ goto fq_probe_failed; ++ ++ /* bp init */ ++ ++ err = dpa_priv_bp_create(net_dev, dpa_bp, count); ++ ++ if (err < 0) ++ goto bp_create_failed; ++ ++ priv->mac_dev = mac_dev; ++ ++ channel = dpa_get_channel(); ++ ++ if (channel < 0) { ++ err = channel; ++ goto get_channel_failed; ++ } ++ ++ priv->channel = (uint16_t)channel; ++ dpaa_eth_add_channel(priv->channel); ++ ++ dpa_fq_setup(priv, &private_fq_cbs, priv->mac_dev->port_dev[TX]); ++ ++ /* Create a congestion group for this netdev, with ++ * dynamically-allocated CGR ID. ++ * Must be executed after probing the MAC, but before ++ * assigning the egress FQs to the CGRs. ++ */ ++ err = dpaa_eth_cgr_init(priv); ++ if (err < 0) { ++ dev_err(dev, "Error initializing CGR\n"); ++ goto tx_cgr_init_failed; ++ } ++ err = dpaa_eth_priv_ingress_cgr_init(priv); ++ if (err < 0) { ++ dev_err(dev, "Error initializing ingress CGR\n"); ++ goto rx_cgr_init_failed; ++ } ++ ++ /* Add the FQs to the interface, and make them active */ ++ err = dpa_fqs_init(dev, &priv->dpa_fq_list, false); ++ if (err < 0) ++ goto fq_alloc_failed; ++ ++ priv->buf_layout = buf_layout; ++ priv->tx_headroom = dpa_get_headroom(&priv->buf_layout[TX]); ++ priv->rx_headroom = dpa_get_headroom(&priv->buf_layout[RX]); ++ ++ /* All real interfaces need their ports initialized */ ++ dpaa_eth_init_ports(mac_dev, dpa_bp, count, &port_fqs, ++ buf_layout, dev); ++ ++#ifdef CONFIG_FMAN_PFC ++ for (i = 0; i < CONFIG_FMAN_PFC_COS_COUNT; i++) { ++ err = fm_port_set_pfc_priorities_mapping_to_qman_wq( ++ mac_dev->port_dev[TX], i, i); ++ if (unlikely(err != 0)) { ++ dev_err(dev, "Error maping PFC %u to WQ %u\n", i, i); ++ goto pfc_mapping_failed; ++ } ++ } ++#endif ++ ++ priv->percpu_priv = devm_alloc_percpu(dev, *priv->percpu_priv); ++ ++ if (priv->percpu_priv == NULL) { ++ dev_err(dev, "devm_alloc_percpu() failed\n"); ++ err = -ENOMEM; ++ goto alloc_percpu_failed; ++ } ++ for_each_possible_cpu(i) { ++ percpu_priv = per_cpu_ptr(priv->percpu_priv, i); ++ memset(percpu_priv, 0, sizeof(*percpu_priv)); ++ } ++ ++ /* Initialize NAPI */ ++ err = dpa_private_napi_add(net_dev); ++ ++ if (err < 0) ++ goto napi_add_failed; ++ ++ err = dpa_private_netdev_init(net_dev); ++ ++ if (err < 0) ++ goto netdev_init_failed; ++ ++ dpaa_eth_sysfs_init(&net_dev->dev); ++ ++#ifdef CONFIG_PM ++ device_set_wakeup_capable(dev, true); ++#endif ++ ++ pr_info("fsl_dpa: Probed interface %s\n", net_dev->name); ++ ++ return 0; ++ ++netdev_init_failed: ++napi_add_failed: ++ dpa_private_napi_del(net_dev); ++alloc_percpu_failed: ++#ifdef CONFIG_FMAN_PFC ++pfc_mapping_failed: ++#endif ++ dpa_fq_free(dev, &priv->dpa_fq_list); ++fq_alloc_failed: ++ qman_delete_cgr_safe(&priv->ingress_cgr); ++ qman_release_cgrid(priv->ingress_cgr.cgrid); ++rx_cgr_init_failed: ++ qman_delete_cgr_safe(&priv->cgr_data.cgr); ++ qman_release_cgrid(priv->cgr_data.cgr.cgrid); ++tx_cgr_init_failed: ++get_channel_failed: ++ dpa_bp_free(priv); ++bp_create_failed: ++fq_probe_failed: ++alloc_failed: ++mac_probe_failed: ++ dev_set_drvdata(dev, NULL); ++ free_netdev(net_dev); ++alloc_etherdev_mq_failed: ++ if (atomic_read(&dpa_bp->refs) == 0) ++ devm_kfree(dev, dpa_bp); ++ ++ return err; ++} ++ ++static const struct of_device_id dpa_match[] = { ++ { ++ .compatible = "fsl,dpa-ethernet" ++ }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, dpa_match); ++ ++static struct platform_driver dpa_driver = { ++ .driver = { ++ .name = KBUILD_MODNAME, ++ .of_match_table = dpa_match, ++ .owner = THIS_MODULE, ++ .pm = DPAA_PM_OPS, ++ }, ++ .probe = dpaa_eth_priv_probe, ++ .remove = dpa_remove ++}; ++ ++#ifndef CONFIG_PPC ++static bool __init __cold soc_has_errata_a010022(void) ++{ ++#ifdef CONFIG_SOC_BUS ++ const struct soc_device_attribute soc_msi_matches[] = { ++ { .family = "QorIQ LS1043A", ++ .data = NULL }, ++ { }, ++ }; ++ ++ if (soc_device_match(soc_msi_matches)) ++ return true; ++ ++ return false; ++#else ++ return true; /* cannot identify SoC */ ++#endif ++} ++#endif ++ ++static int __init __cold dpa_load(void) ++{ ++ int _errno; ++ ++ pr_info(DPA_DESCRIPTION "\n"); ++ ++#ifdef CONFIG_FSL_DPAA_DBG_LOOP ++ dpa_debugfs_module_init(); ++#endif /* CONFIG_FSL_DPAA_DBG_LOOP */ ++ ++ /* initialise dpaa_eth mirror values */ ++ dpa_rx_extra_headroom = fm_get_rx_extra_headroom(); ++ dpa_max_frm = fm_get_max_frm(); ++ dpa_num_cpus = num_possible_cpus(); ++ ++#ifndef CONFIG_PPC ++ /* Detect if the current SoC requires the 4K alignment workaround */ ++ dpaa_errata_a010022 = soc_has_errata_a010022(); ++#endif ++ ++#ifdef CONFIG_FSL_DPAA_DBG_LOOP ++ memset(dpa_loop_netdevs, 0, sizeof(dpa_loop_netdevs)); ++#endif ++ ++ _errno = platform_driver_register(&dpa_driver); ++ if (unlikely(_errno < 0)) { ++ pr_err(KBUILD_MODNAME ++ ": %s:%hu:%s(): platform_driver_register() = %d\n", ++ KBUILD_BASENAME".c", __LINE__, __func__, _errno); ++ } ++ ++ pr_debug(KBUILD_MODNAME ": %s:%s() ->\n", ++ KBUILD_BASENAME".c", __func__); ++ ++ return _errno; ++} ++module_init(dpa_load); ++ ++static void __exit __cold dpa_unload(void) ++{ ++ pr_debug(KBUILD_MODNAME ": -> %s:%s()\n", ++ KBUILD_BASENAME".c", __func__); ++ ++ platform_driver_unregister(&dpa_driver); ++ ++#ifdef CONFIG_FSL_DPAA_DBG_LOOP ++ dpa_debugfs_module_exit(); ++#endif /* CONFIG_FSL_DPAA_DBG_LOOP */ ++ ++ /* Only one channel is used and needs to be relased after all ++ * interfaces are removed ++ */ ++ dpa_release_channel(); ++ ++ pr_debug(KBUILD_MODNAME ": %s:%s() ->\n", ++ KBUILD_BASENAME".c", __func__); ++} ++module_exit(dpa_unload); +--- /dev/null ++++ b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth.h +@@ -0,0 +1,697 @@ ++/* Copyright 2008-2012 Freescale Semiconductor Inc. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * * Neither the name of Freescale Semiconductor nor the ++ * names of its contributors may be used to endorse or promote products ++ * derived from this software without specific prior written permission. ++ * ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") as published by the Free Software ++ * Foundation, either version 2 of that License or (at your option) any ++ * later version. ++ * ++ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. ++ */ ++ ++#ifndef __DPA_H ++#define __DPA_H ++ ++#include <linux/netdevice.h> ++#include <linux/fsl_qman.h> /* struct qman_fq */ ++ ++#include "fm_ext.h" ++#include "dpaa_eth_trace.h" ++ ++extern int dpa_rx_extra_headroom; ++extern int dpa_max_frm; ++extern int dpa_num_cpus; ++ ++#define dpa_get_rx_extra_headroom() dpa_rx_extra_headroom ++#define dpa_get_max_frm() dpa_max_frm ++ ++#define dpa_get_max_mtu() \ ++ (dpa_get_max_frm() - (VLAN_ETH_HLEN + ETH_FCS_LEN)) ++ ++#define __hot ++ ++/* Simple enum of FQ types - used for array indexing */ ++enum port_type {RX, TX}; ++ ++/* TODO: This structure should be renamed & moved to the FMD wrapper */ ++struct dpa_buffer_layout_s { ++ uint16_t priv_data_size; ++ bool parse_results; ++ bool time_stamp; ++ bool hash_results; ++ uint8_t manip_extra_space; ++ uint16_t data_align; ++}; ++ ++#ifdef CONFIG_FSL_DPAA_ETH_DEBUG ++#define DPA_BUG_ON(cond) BUG_ON(cond) ++#else ++#define DPA_BUG_ON(cond) ++#endif ++ ++#define DPA_TX_PRIV_DATA_SIZE 16 ++#define DPA_PARSE_RESULTS_SIZE sizeof(fm_prs_result_t) ++#define DPA_TIME_STAMP_SIZE 8 ++#define DPA_HASH_RESULTS_SIZE 8 ++#define DPA_RX_PRIV_DATA_SIZE (DPA_TX_PRIV_DATA_SIZE + \ ++ dpa_get_rx_extra_headroom()) ++ ++#define FM_FD_STAT_RX_ERRORS \ ++ (FM_PORT_FRM_ERR_DMA | FM_PORT_FRM_ERR_PHYSICAL | \ ++ FM_PORT_FRM_ERR_SIZE | FM_PORT_FRM_ERR_CLS_DISCARD | \ ++ FM_PORT_FRM_ERR_EXTRACTION | FM_PORT_FRM_ERR_NO_SCHEME | \ ++ FM_PORT_FRM_ERR_ILL_PLCR | FM_PORT_FRM_ERR_PRS_TIMEOUT | \ ++ FM_PORT_FRM_ERR_PRS_ILL_INSTRUCT | FM_PORT_FRM_ERR_PRS_HDR_ERR) ++ ++#define FM_FD_STAT_TX_ERRORS \ ++ (FM_PORT_FRM_ERR_UNSUPPORTED_FORMAT | \ ++ FM_PORT_FRM_ERR_LENGTH | FM_PORT_FRM_ERR_DMA) ++ ++#ifndef CONFIG_FSL_DPAA_ETH_JUMBO_FRAME ++/* The raw buffer size must be cacheline aligned. ++ * Normally we use 2K buffers. ++ */ ++#define DPA_BP_RAW_SIZE 2048 ++#else ++/* For jumbo frame optimizations, use buffers large enough to accommodate ++ * 9.6K frames, FD maximum offset, skb sh_info overhead and some extra ++ * space to account for further alignments. ++ */ ++#define DPA_MAX_FRM_SIZE 9600 ++#ifdef CONFIG_PPC ++#define DPA_BP_RAW_SIZE \ ++ ((DPA_MAX_FRM_SIZE + DPA_MAX_FD_OFFSET + \ ++ sizeof(struct skb_shared_info) + 128) & ~(SMP_CACHE_BYTES - 1)) ++#else /* CONFIG_PPC */ ++#define DPA_BP_RAW_SIZE ((unlikely(dpaa_errata_a010022)) ? 2048 : \ ++ ((DPA_MAX_FRM_SIZE + DPA_MAX_FD_OFFSET + \ ++ sizeof(struct skb_shared_info) + 128) & ~(SMP_CACHE_BYTES - 1))) ++#endif /* CONFIG_PPC */ ++#endif /* CONFIG_FSL_DPAA_ETH_JUMBO_FRAME */ ++ ++/* This is what FMan is ever allowed to use. ++ * FMan-DMA requires 16-byte alignment for Rx buffers, but SKB_DATA_ALIGN is ++ * even stronger (SMP_CACHE_BYTES-aligned), so we just get away with that, ++ * via SKB_WITH_OVERHEAD(). We can't rely on netdev_alloc_frag() giving us ++ * half-page-aligned buffers (can we?), so we reserve some more space ++ * for start-of-buffer alignment. ++ */ ++#define dpa_bp_size(buffer_layout) (SKB_WITH_OVERHEAD(DPA_BP_RAW_SIZE) - \ ++ SMP_CACHE_BYTES) ++/* We must ensure that skb_shinfo is always cacheline-aligned. */ ++#define DPA_SKB_SIZE(size) ((size) & ~(SMP_CACHE_BYTES - 1)) ++ ++/* Maximum size of a buffer for which recycling is allowed. ++ * We need an upper limit such that forwarded skbs that get reallocated on Tx ++ * aren't allowed to grow unboundedly. On the other hand, we need to make sure ++ * that skbs allocated by us will not fail to be recycled due to their size. ++ * ++ * For a requested size, the kernel allocator provides the next power of two ++ * sized block, which the stack will use as is, regardless of the actual size ++ * it required; since we must accommodate at most 9.6K buffers (L2 maximum ++ * supported frame size), set the recycling upper limit to 16K. ++ */ ++#define DPA_RECYCLE_MAX_SIZE 16384 ++ ++#if defined(CONFIG_FSL_SDK_FMAN_TEST) ++/*TODO: temporary for fman pcd testing */ ++#define FMAN_PCD_TESTS_MAX_NUM_RANGES 20 ++#endif ++ ++#define DPAA_ETH_FQ_DELTA 0x10000 ++ ++#define DPAA_ETH_PCD_FQ_BASE(device_addr) \ ++ (((device_addr) & 0x1fffff) >> 6) ++ ++#define DPAA_ETH_PCD_FQ_HI_PRIO_BASE(device_addr) \ ++ (DPAA_ETH_FQ_DELTA + DPAA_ETH_PCD_FQ_BASE(device_addr)) ++ ++/* Largest value that the FQD's OAL field can hold. ++ * This is DPAA-1.x specific. ++ * TODO: This rather belongs in fsl_qman.h ++ */ ++#define FSL_QMAN_MAX_OAL 127 ++ ++/* Maximum offset value for a contig or sg FD (represented on 9 bits) */ ++#define DPA_MAX_FD_OFFSET ((1 << 9) - 1) ++ ++/* Default alignment for start of data in an Rx FD */ ++#define DPA_FD_DATA_ALIGNMENT 16 ++ ++/* Values for the L3R field of the FM Parse Results ++ */ ++/* L3 Type field: First IP Present IPv4 */ ++#define FM_L3_PARSE_RESULT_IPV4 0x8000 ++/* L3 Type field: First IP Present IPv6 */ ++#define FM_L3_PARSE_RESULT_IPV6 0x4000 ++ ++/* Values for the L4R field of the FM Parse Results ++ * See $8.8.4.7.20 - L4 HXS - L4 Results from DPAA-Rev2 Reference Manual. ++ */ ++/* L4 Type field: UDP */ ++#define FM_L4_PARSE_RESULT_UDP 0x40 ++/* L4 Type field: TCP */ ++#define FM_L4_PARSE_RESULT_TCP 0x20 ++/* FD status field indicating whether the FM Parser has attempted to validate ++ * the L4 csum of the frame. ++ * Note that having this bit set doesn't necessarily imply that the checksum ++ * is valid. One would have to check the parse results to find that out. ++ */ ++#define FM_FD_STAT_L4CV 0x00000004 ++ ++ ++#define FM_FD_STAT_ERR_PHYSICAL FM_PORT_FRM_ERR_PHYSICAL ++ ++/* Check if the parsed frame was found to be a TCP segment. ++ * ++ * @parse_result_ptr must be of type (fm_prs_result_t *). ++ */ ++#define fm_l4_frame_is_tcp(parse_result_ptr) \ ++ ((parse_result_ptr)->l4r & FM_L4_PARSE_RESULT_TCP) ++ ++/* number of Tx queues to FMan */ ++#ifdef CONFIG_FMAN_PFC ++#define DPAA_ETH_TX_QUEUES (NR_CPUS * CONFIG_FMAN_PFC_COS_COUNT) ++#else ++#define DPAA_ETH_TX_QUEUES NR_CPUS ++#endif ++ ++#define DPAA_ETH_RX_QUEUES 128 ++ ++/* Convenience macros for storing/retrieving the skb back-pointers. They must ++ * accommodate both recycling and confirmation paths - i.e. cases when the buf ++ * was allocated by ourselves, respectively by the stack. In the former case, ++ * we could store the skb at negative offset; in the latter case, we can't, ++ * so we'll use 0 as offset. ++ * ++ * NB: @off is an offset from a (struct sk_buff **) pointer! ++ */ ++#define DPA_WRITE_SKB_PTR(skb, skbh, addr, off) \ ++{ \ ++ skbh = (struct sk_buff **)addr; \ ++ *(skbh + (off)) = skb; \ ++} ++#define DPA_READ_SKB_PTR(skb, skbh, addr, off) \ ++{ \ ++ skbh = (struct sk_buff **)addr; \ ++ skb = *(skbh + (off)); \ ++} ++ ++#ifdef CONFIG_PM ++/* Magic Packet wakeup */ ++#define DPAA_WOL_MAGIC 0x00000001 ++#endif ++ ++#if defined(CONFIG_FSL_SDK_FMAN_TEST) ++struct pcd_range { ++ uint32_t base; ++ uint32_t count; ++}; ++#endif ++ ++/* More detailed FQ types - used for fine-grained WQ assignments */ ++enum dpa_fq_type { ++ FQ_TYPE_RX_DEFAULT = 1, /* Rx Default FQs */ ++ FQ_TYPE_RX_ERROR, /* Rx Error FQs */ ++ FQ_TYPE_RX_PCD, /* User-defined PCDs */ ++ FQ_TYPE_TX, /* "Real" Tx FQs */ ++ FQ_TYPE_TX_CONFIRM, /* Tx default Conf FQ (actually an Rx FQ) */ ++ FQ_TYPE_TX_CONF_MQ, /* Tx conf FQs (one for each Tx FQ) */ ++ FQ_TYPE_TX_ERROR, /* Tx Error FQs (these are actually Rx FQs) */ ++ FQ_TYPE_RX_PCD_HI_PRIO, /* User-defined high-priority PCDs */ ++}; ++ ++struct dpa_fq { ++ struct qman_fq fq_base; ++ struct list_head list; ++ struct net_device *net_dev; ++ bool init; ++ uint32_t fqid; ++ uint32_t flags; ++ uint16_t channel; ++ uint8_t wq; ++ enum dpa_fq_type fq_type; ++}; ++ ++struct dpa_fq_cbs_t { ++ struct qman_fq rx_defq; ++ struct qman_fq tx_defq; ++ struct qman_fq rx_errq; ++ struct qman_fq tx_errq; ++ struct qman_fq egress_ern; ++}; ++ ++struct fqid_cell { ++ uint32_t start; ++ uint32_t count; ++}; ++ ++struct dpa_bp { ++ struct bman_pool *pool; ++ uint8_t bpid; ++ struct device *dev; ++ union { ++ /* The buffer pools used for the private ports are initialized ++ * with target_count buffers for each CPU; at runtime the ++ * number of buffers per CPU is constantly brought back to this ++ * level ++ */ ++ int target_count; ++ /* The configured value for the number of buffers in the pool, ++ * used for shared port buffer pools ++ */ ++ int config_count; ++ }; ++ size_t size; ++ bool seed_pool; ++ /* physical address of the contiguous memory used by the pool to store ++ * the buffers ++ */ ++ dma_addr_t paddr; ++ /* virtual address of the contiguous memory used by the pool to store ++ * the buffers ++ */ ++ void __iomem *vaddr; ++ /* current number of buffers in the bpool alloted to this CPU */ ++ int __percpu *percpu_count; ++ atomic_t refs; ++ /* some bpools need to be seeded before use by this cb */ ++ int (*seed_cb)(struct dpa_bp *); ++ /* some bpools need to be emptied before freeing; this cb is used ++ * for freeing of individual buffers taken from the pool ++ */ ++ void (*free_buf_cb)(void *addr); ++}; ++ ++struct dpa_rx_errors { ++ u64 dme; /* DMA Error */ ++ u64 fpe; /* Frame Physical Error */ ++ u64 fse; /* Frame Size Error */ ++ u64 phe; /* Header Error */ ++ u64 cse; /* Checksum Validation Error */ ++}; ++ ++/* Counters for QMan ERN frames - one counter per rejection code */ ++struct dpa_ern_cnt { ++ u64 cg_tdrop; /* Congestion group taildrop */ ++ u64 wred; /* WRED congestion */ ++ u64 err_cond; /* Error condition */ ++ u64 early_window; /* Order restoration, frame too early */ ++ u64 late_window; /* Order restoration, frame too late */ ++ u64 fq_tdrop; /* FQ taildrop */ ++ u64 fq_retired; /* FQ is retired */ ++ u64 orp_zero; /* ORP disabled */ ++}; ++ ++struct dpa_napi_portal { ++ struct napi_struct napi; ++ struct qman_portal *p; ++}; ++ ++struct dpa_percpu_priv_s { ++ struct net_device *net_dev; ++ struct dpa_napi_portal *np; ++ u64 in_interrupt; ++ u64 tx_returned; ++ u64 tx_confirm; ++ /* fragmented (non-linear) skbuffs received from the stack */ ++ u64 tx_frag_skbuffs; ++ /* number of S/G frames received */ ++ u64 rx_sg; ++ ++ struct rtnl_link_stats64 stats; ++ struct dpa_rx_errors rx_errors; ++ struct dpa_ern_cnt ern_cnt; ++}; ++ ++struct dpa_priv_s { ++ struct dpa_percpu_priv_s __percpu *percpu_priv; ++ struct dpa_bp *dpa_bp; ++ /* Store here the needed Tx headroom for convenience and speed ++ * (even though it can be computed based on the fields of buf_layout) ++ */ ++ uint16_t tx_headroom; ++ struct net_device *net_dev; ++ struct mac_device *mac_dev; ++ struct qman_fq *egress_fqs[DPAA_ETH_TX_QUEUES]; ++ struct qman_fq *conf_fqs[DPAA_ETH_TX_QUEUES]; ++ ++ size_t bp_count; ++ ++ uint16_t channel; /* "fsl,qman-channel-id" */ ++ struct list_head dpa_fq_list; ++ ++#ifdef CONFIG_FSL_DPAA_DBG_LOOP ++ struct dentry *debugfs_loop_file; ++#endif ++ ++ uint32_t msg_enable; /* net_device message level */ ++#ifdef CONFIG_FSL_DPAA_1588 ++ struct dpa_ptp_tsu *tsu; ++#endif ++ ++#if defined(CONFIG_FSL_SDK_FMAN_TEST) ++/* TODO: this is temporary until pcd support is implemented in dpaa */ ++ int priv_pcd_num_ranges; ++ struct pcd_range priv_pcd_ranges[FMAN_PCD_TESTS_MAX_NUM_RANGES]; ++#endif ++ ++ struct { ++ /** ++ * All egress queues to a given net device belong to one ++ * (and the same) congestion group. ++ */ ++ struct qman_cgr cgr; ++ /* If congested, when it began. Used for performance stats. */ ++ u32 congestion_start_jiffies; ++ /* Number of jiffies the Tx port was congested. */ ++ u32 congested_jiffies; ++ /** ++ * Counter for the number of times the CGR ++ * entered congestion state ++ */ ++ u32 cgr_congested_count; ++ } cgr_data; ++ /* Use a per-port CGR for ingress traffic. */ ++ bool use_ingress_cgr; ++ struct qman_cgr ingress_cgr; ++ ++#ifdef CONFIG_FSL_DPAA_TS ++ bool ts_tx_en; /* Tx timestamping enabled */ ++ bool ts_rx_en; /* Rx timestamping enabled */ ++#endif /* CONFIG_FSL_DPAA_TS */ ++ ++ struct dpa_buffer_layout_s *buf_layout; ++ uint16_t rx_headroom; ++ char if_type[30]; ++ ++ void *peer; ++#ifdef CONFIG_PM ++ u32 wol; ++#endif ++#ifdef CONFIG_FSL_DPAA_DBG_LOOP ++ int loop_id; ++ int loop_to; ++#endif ++#ifdef CONFIG_FSL_DPAA_CEETM ++ bool ceetm_en; /* CEETM QoS enabled */ ++#endif ++}; ++ ++struct fm_port_fqs { ++ struct dpa_fq *tx_defq; ++ struct dpa_fq *tx_errq; ++ struct dpa_fq *rx_defq; ++ struct dpa_fq *rx_errq; ++}; ++ ++ ++#ifdef CONFIG_FSL_DPAA_DBG_LOOP ++extern struct net_device *dpa_loop_netdevs[20]; ++#endif ++ ++/* functions with different implementation for SG and non-SG: */ ++int dpa_bp_priv_seed(struct dpa_bp *dpa_bp); ++int dpaa_eth_refill_bpools(struct dpa_bp *dpa_bp, int *count_ptr); ++void __hot _dpa_rx(struct net_device *net_dev, ++ struct qman_portal *portal, ++ const struct dpa_priv_s *priv, ++ struct dpa_percpu_priv_s *percpu_priv, ++ const struct qm_fd *fd, ++ u32 fqid, ++ int *count_ptr); ++int __hot dpa_tx(struct sk_buff *skb, struct net_device *net_dev); ++int __hot dpa_tx_extended(struct sk_buff *skb, struct net_device *net_dev, ++ struct qman_fq *egress_fq, struct qman_fq *conf_fq); ++struct sk_buff *_dpa_cleanup_tx_fd(const struct dpa_priv_s *priv, ++ const struct qm_fd *fd); ++void __hot _dpa_process_parse_results(const fm_prs_result_t *parse_results, ++ const struct qm_fd *fd, ++ struct sk_buff *skb, ++ int *use_gro); ++#ifndef CONFIG_FSL_DPAA_TS ++bool dpa_skb_is_recyclable(struct sk_buff *skb); ++bool dpa_buf_is_recyclable(struct sk_buff *skb, ++ uint32_t min_size, ++ uint16_t min_offset, ++ unsigned char **new_buf_start); ++#endif ++int __hot skb_to_contig_fd(struct dpa_priv_s *priv, ++ struct sk_buff *skb, struct qm_fd *fd, ++ int *count_ptr, int *offset); ++int __hot skb_to_sg_fd(struct dpa_priv_s *priv, ++ struct sk_buff *skb, struct qm_fd *fd); ++int __cold __attribute__((nonnull)) ++ _dpa_fq_free(struct device *dev, struct qman_fq *fq); ++ ++/* Turn on HW checksum computation for this outgoing frame. ++ * If the current protocol is not something we support in this regard ++ * (or if the stack has already computed the SW checksum), we do nothing. ++ * ++ * Returns 0 if all goes well (or HW csum doesn't apply), and a negative value ++ * otherwise. ++ * ++ * Note that this function may modify the fd->cmd field and the skb data buffer ++ * (the Parse Results area). ++ */ ++int dpa_enable_tx_csum(struct dpa_priv_s *priv, ++ struct sk_buff *skb, struct qm_fd *fd, char *parse_results); ++ ++static inline int dpaa_eth_napi_schedule(struct dpa_percpu_priv_s *percpu_priv, ++ struct qman_portal *portal) ++{ ++ /* In case of threaded ISR for RT enable kernel, ++ * in_irq() does not return appropriate value, so use ++ * in_serving_softirq to distinguish softirq or irq context. ++ */ ++ if (unlikely(in_irq() || !in_serving_softirq())) { ++ /* Disable QMan IRQ and invoke NAPI */ ++ int ret = qman_p_irqsource_remove(portal, QM_PIRQ_DQRI); ++ if (likely(!ret)) { ++ const struct qman_portal_config *pc = ++ qman_p_get_portal_config(portal); ++ struct dpa_napi_portal *np = ++ &percpu_priv->np[pc->index]; ++ ++ np->p = portal; ++ napi_schedule(&np->napi); ++ percpu_priv->in_interrupt++; ++ return 1; ++ } ++ } ++ return 0; ++} ++ ++static inline ssize_t __const __must_check __attribute__((nonnull)) ++dpa_fd_length(const struct qm_fd *fd) ++{ ++ return fd->length20; ++} ++ ++static inline ssize_t __const __must_check __attribute__((nonnull)) ++dpa_fd_offset(const struct qm_fd *fd) ++{ ++ return fd->offset; ++} ++ ++/* Verifies if the skb length is below the interface MTU */ ++static inline int dpa_check_rx_mtu(struct sk_buff *skb, int mtu) ++{ ++ if (unlikely(skb->len > mtu)) ++ if ((skb->protocol != htons(ETH_P_8021Q)) ++ || (skb->len > mtu + 4)) ++ return -1; ++ ++ return 0; ++} ++ ++static inline uint16_t dpa_get_headroom(struct dpa_buffer_layout_s *bl) ++{ ++ uint16_t headroom; ++ /* The frame headroom must accommodate: ++ * - the driver private data area ++ * - parse results, hash results, timestamp if selected ++ * - manip extra space ++ * If either hash results or time stamp are selected, both will ++ * be copied to/from the frame headroom, as TS is located between PR and ++ * HR in the IC and IC copy size has a granularity of 16bytes ++ * (see description of FMBM_RICP and FMBM_TICP registers in DPAARM) ++ * ++ * Also make sure the headroom is a multiple of data_align bytes ++ */ ++ headroom = (uint16_t)(bl->priv_data_size + ++ (bl->parse_results ? DPA_PARSE_RESULTS_SIZE : 0) + ++ (bl->hash_results || bl->time_stamp ? ++ DPA_TIME_STAMP_SIZE + DPA_HASH_RESULTS_SIZE : 0) + ++ bl->manip_extra_space); ++ ++ return bl->data_align ? ALIGN(headroom, bl->data_align) : headroom; ++} ++ ++int fm_mac_dump_regs(struct mac_device *h_dev, char *buf, int n); ++int fm_mac_dump_rx_stats(struct mac_device *h_dev, char *buf, int n); ++int fm_mac_dump_tx_stats(struct mac_device *h_dev, char *buf, int n); ++ ++void dpaa_eth_sysfs_remove(struct device *dev); ++void dpaa_eth_sysfs_init(struct device *dev); ++int dpaa_eth_poll(struct napi_struct *napi, int budget); ++ ++void dpa_private_napi_del(struct net_device *net_dev); ++ ++/* Equivalent to a memset(0), but works faster */ ++static inline void clear_fd(struct qm_fd *fd) ++{ ++ fd->opaque_addr = 0; ++ fd->opaque = 0; ++ fd->cmd = 0; ++} ++ ++static inline int _dpa_tx_fq_to_id(const struct dpa_priv_s *priv, ++ struct qman_fq *tx_fq) ++{ ++ int i; ++ ++ for (i = 0; i < DPAA_ETH_TX_QUEUES; i++) ++ if (priv->egress_fqs[i] == tx_fq) ++ return i; ++ ++ return -EINVAL; ++} ++ ++static inline int __hot dpa_xmit(struct dpa_priv_s *priv, ++ struct rtnl_link_stats64 *percpu_stats, ++ struct qm_fd *fd, struct qman_fq *egress_fq, ++ struct qman_fq *conf_fq) ++{ ++ int err, i; ++ ++ if (fd->bpid == 0xff) ++ fd->cmd |= qman_fq_fqid(conf_fq); ++ ++ /* Trace this Tx fd */ ++ trace_dpa_tx_fd(priv->net_dev, egress_fq, fd); ++ ++ for (i = 0; i < 100000; i++) { ++ err = qman_enqueue(egress_fq, fd, 0); ++ if (err != -EBUSY) ++ break; ++ } ++ ++ if (unlikely(err < 0)) { ++ /* TODO differentiate b/w -EBUSY (EQCR full) and other codes? */ ++ percpu_stats->tx_errors++; ++ percpu_stats->tx_fifo_errors++; ++ return err; ++ } ++ ++ percpu_stats->tx_packets++; ++ percpu_stats->tx_bytes += dpa_fd_length(fd); ++ ++ return 0; ++} ++ ++/* Use multiple WQs for FQ assignment: ++ * - Tx Confirmation queues go to WQ1. ++ * - Rx Default, Tx and PCD queues go to WQ3 (no differentiation between ++ * Rx and Tx traffic, or between Rx Default and Rx PCD frames). ++ * - Rx Error and Tx Error queues go to WQ2 (giving them a better chance ++ * to be scheduled, in case there are many more FQs in WQ3). ++ * This ensures that Tx-confirmed buffers are timely released. In particular, ++ * it avoids congestion on the Tx Confirm FQs, which can pile up PFDRs if they ++ * are greatly outnumbered by other FQs in the system (usually PCDs), while ++ * dequeue scheduling is round-robin. ++ */ ++static inline void _dpa_assign_wq(struct dpa_fq *fq) ++{ ++ switch (fq->fq_type) { ++ case FQ_TYPE_TX_CONFIRM: ++ case FQ_TYPE_TX_CONF_MQ: ++ fq->wq = 1; ++ break; ++ case FQ_TYPE_RX_DEFAULT: ++ case FQ_TYPE_TX: ++ fq->wq = 3; ++ break; ++ case FQ_TYPE_RX_ERROR: ++ case FQ_TYPE_TX_ERROR: ++ case FQ_TYPE_RX_PCD_HI_PRIO: ++ fq->wq = 2; ++ break; ++ case FQ_TYPE_RX_PCD: ++ fq->wq = 5; ++ break; ++ default: ++ WARN(1, "Invalid FQ type %d for FQID %d!\n", ++ fq->fq_type, fq->fqid); ++ } ++} ++ ++#ifdef CONFIG_FSL_DPAA_ETH_USE_NDO_SELECT_QUEUE ++/* Use in lieu of skb_get_queue_mapping() */ ++#ifdef CONFIG_FMAN_PFC ++#define dpa_get_queue_mapping(skb) \ ++ (((skb)->priority < CONFIG_FMAN_PFC_COS_COUNT) ? \ ++ ((skb)->priority * dpa_num_cpus + smp_processor_id()) : \ ++ ((CONFIG_FMAN_PFC_COS_COUNT - 1) * \ ++ dpa_num_cpus + smp_processor_id())); ++ ++#else ++#define dpa_get_queue_mapping(skb) \ ++ raw_smp_processor_id() ++#endif ++#else ++/* Use the queue selected by XPS */ ++#define dpa_get_queue_mapping(skb) \ ++ skb_get_queue_mapping(skb) ++#endif ++ ++#ifdef CONFIG_PTP_1588_CLOCK_DPAA ++struct ptp_priv_s { ++ struct device_node *node; ++ struct platform_device *of_dev; ++ struct mac_device *mac_dev; ++}; ++extern struct ptp_priv_s ptp_priv; ++#endif ++ ++static inline void _dpa_bp_free_pf(void *addr) ++{ ++ put_page(virt_to_head_page(addr)); ++} ++ ++/* TODO: LS1043A SoC has a HW issue regarding FMan DMA transactions; The issue ++ * manifests itself at high traffic rates when frames exceed 4K memory ++ * boundaries; For the moment, we use a SW workaround to avoid frames larger ++ * than 4K or that exceed 4K alignments. ++ */ ++ ++#ifndef CONFIG_PPC ++extern bool dpaa_errata_a010022; /* SoC affected by A010022 errata */ ++ ++#define HAS_DMA_ISSUE(start, size) \ ++ (((u64)(start) + (size)) > (((u64)(start) + 0x1000) & ~0xFFF)) ++#define BOUNDARY_4K(start, size) (((u64)(start) + (u64)(size)) & ~0xFFF) ++ ++#endif /* !CONFIG_PPC */ ++ ++#endif /* __DPA_H */ +--- /dev/null ++++ b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_base.c +@@ -0,0 +1,263 @@ ++/* Copyright 2008-2013 Freescale Semiconductor, Inc. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * * Neither the name of Freescale Semiconductor nor the ++ * names of its contributors may be used to endorse or promote products ++ * derived from this software without specific prior written permission. ++ * ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") as published by the Free Software ++ * Foundation, either version 2 of that License or (at your option) any ++ * later version. ++ * ++ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. ++ */ ++ ++#ifdef CONFIG_FSL_DPAA_ETH_DEBUG ++#define pr_fmt(fmt) \ ++ KBUILD_MODNAME ": %s:%hu:%s() " fmt, \ ++ KBUILD_BASENAME".c", __LINE__, __func__ ++#else ++#define pr_fmt(fmt) \ ++ KBUILD_MODNAME ": " fmt ++#endif ++ ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/io.h> ++#include <linux/of_platform.h> ++#include <linux/of_net.h> ++#include <linux/etherdevice.h> ++#include <linux/kthread.h> ++#include <linux/percpu.h> ++#include <linux/highmem.h> ++#include <linux/sort.h> ++#include <linux/fsl_qman.h> ++#include "dpaa_eth.h" ++#include "dpaa_eth_common.h" ++#include "dpaa_eth_base.h" ++ ++#define DPA_DESCRIPTION "FSL DPAA Advanced drivers:" ++ ++MODULE_LICENSE("Dual BSD/GPL"); ++ ++uint8_t advanced_debug = -1; ++module_param(advanced_debug, byte, S_IRUGO); ++MODULE_PARM_DESC(advanced_debug, "Module/Driver verbosity level"); ++EXPORT_SYMBOL(advanced_debug); ++ ++static int dpa_bp_cmp(const void *dpa_bp0, const void *dpa_bp1) ++{ ++ return ((struct dpa_bp *)dpa_bp0)->size - ++ ((struct dpa_bp *)dpa_bp1)->size; ++} ++ ++struct dpa_bp * __cold __must_check /* __attribute__((nonnull)) */ ++dpa_bp_probe(struct platform_device *_of_dev, size_t *count) ++{ ++ int i, lenp, na, ns, err; ++ struct device *dev; ++ struct device_node *dev_node; ++ const __be32 *bpool_cfg; ++ struct dpa_bp *dpa_bp; ++ u32 bpid; ++ ++ dev = &_of_dev->dev; ++ ++ *count = of_count_phandle_with_args(dev->of_node, ++ "fsl,bman-buffer-pools", NULL); ++ if (*count < 1) { ++ dev_err(dev, "missing fsl,bman-buffer-pools device tree entry\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ dpa_bp = devm_kzalloc(dev, *count * sizeof(*dpa_bp), GFP_KERNEL); ++ if (dpa_bp == NULL) { ++ dev_err(dev, "devm_kzalloc() failed\n"); ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ dev_node = of_find_node_by_path("/"); ++ if (unlikely(dev_node == NULL)) { ++ dev_err(dev, "of_find_node_by_path(/) failed\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ na = of_n_addr_cells(dev_node); ++ ns = of_n_size_cells(dev_node); ++ ++ for (i = 0; i < *count; i++) { ++ of_node_put(dev_node); ++ ++ dev_node = of_parse_phandle(dev->of_node, ++ "fsl,bman-buffer-pools", i); ++ if (dev_node == NULL) { ++ dev_err(dev, "of_find_node_by_phandle() failed\n"); ++ return ERR_PTR(-EFAULT); ++ } ++ ++ if (unlikely(!of_device_is_compatible(dev_node, "fsl,bpool"))) { ++ dev_err(dev, ++ "!of_device_is_compatible(%s, fsl,bpool)\n", ++ dev_node->full_name); ++ dpa_bp = ERR_PTR(-EINVAL); ++ goto _return_of_node_put; ++ } ++ ++ err = of_property_read_u32(dev_node, "fsl,bpid", &bpid); ++ if (err) { ++ dev_err(dev, "Cannot find buffer pool ID in the device tree\n"); ++ dpa_bp = ERR_PTR(-EINVAL); ++ goto _return_of_node_put; ++ } ++ dpa_bp[i].bpid = (uint8_t)bpid; ++ ++ bpool_cfg = of_get_property(dev_node, "fsl,bpool-ethernet-cfg", ++ &lenp); ++ if (bpool_cfg && (lenp == (2 * ns + na) * sizeof(*bpool_cfg))) { ++ const uint32_t *seed_pool; ++ ++ dpa_bp[i].config_count = ++ (int)of_read_number(bpool_cfg, ns); ++ dpa_bp[i].size = ++ (size_t)of_read_number(bpool_cfg + ns, ns); ++ dpa_bp[i].paddr = ++ of_read_number(bpool_cfg + 2 * ns, na); ++ ++ seed_pool = of_get_property(dev_node, ++ "fsl,bpool-ethernet-seeds", &lenp); ++ dpa_bp[i].seed_pool = !!seed_pool; ++ ++ } else { ++ dev_err(dev, ++ "Missing/invalid fsl,bpool-ethernet-cfg device tree entry for node %s\n", ++ dev_node->full_name); ++ dpa_bp = ERR_PTR(-EINVAL); ++ goto _return_of_node_put; ++ } ++ } ++ ++ sort(dpa_bp, *count, sizeof(*dpa_bp), dpa_bp_cmp, NULL); ++ ++ return dpa_bp; ++ ++_return_of_node_put: ++ if (dev_node) ++ of_node_put(dev_node); ++ ++ return dpa_bp; ++} ++EXPORT_SYMBOL(dpa_bp_probe); ++ ++int dpa_bp_shared_port_seed(struct dpa_bp *bp) ++{ ++ void __iomem **ptr; ++ ++ /* In MAC-less and Shared-MAC scenarios the physical ++ * address of the buffer pool in device tree is set ++ * to 0 to specify that another entity (USDPAA) will ++ * allocate and seed the buffers ++ */ ++ if (!bp->paddr) ++ return 0; ++ ++ /* allocate memory region for buffers */ ++ devm_request_mem_region(bp->dev, bp->paddr, ++ bp->size * bp->config_count, KBUILD_MODNAME); ++ /* managed ioremap unmapping */ ++ ptr = devres_alloc(devm_ioremap_release, sizeof(*ptr), GFP_KERNEL); ++ if (!ptr) ++ return -EIO; ++#ifndef CONFIG_PPC ++ bp->vaddr = ioremap_cache_ns(bp->paddr, bp->size * bp->config_count); ++#else ++ bp->vaddr = ioremap_prot(bp->paddr, bp->size * bp->config_count, 0); ++#endif ++ if (bp->vaddr == NULL) { ++ pr_err("Could not map memory for pool %d\n", bp->bpid); ++ devres_free(ptr); ++ return -EIO; ++ } ++ *ptr = bp->vaddr; ++ devres_add(bp->dev, ptr); ++ ++ /* seed pool with buffers from that memory region */ ++ if (bp->seed_pool) { ++ int count = bp->target_count; ++ dma_addr_t addr = bp->paddr; ++ ++ while (count) { ++ struct bm_buffer bufs[8]; ++ uint8_t num_bufs = 0; ++ ++ do { ++ BUG_ON(addr > 0xffffffffffffull); ++ bufs[num_bufs].bpid = bp->bpid; ++ bm_buffer_set64(&bufs[num_bufs++], addr); ++ addr += bp->size; ++ ++ } while (--count && (num_bufs < 8)); ++ ++ while (bman_release(bp->pool, bufs, num_bufs, 0)) ++ cpu_relax(); ++ } ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(dpa_bp_shared_port_seed); ++ ++int dpa_bp_create(struct net_device *net_dev, struct dpa_bp *dpa_bp, ++ size_t count) ++{ ++ struct dpa_priv_s *priv = netdev_priv(net_dev); ++ int i; ++ ++ priv->dpa_bp = dpa_bp; ++ priv->bp_count = count; ++ ++ for (i = 0; i < count; i++) { ++ int err; ++ err = dpa_bp_alloc(&dpa_bp[i]); ++ if (err < 0) { ++ dpa_bp_free(priv); ++ priv->dpa_bp = NULL; ++ return err; ++ } ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(dpa_bp_create); ++ ++static int __init __cold dpa_advanced_load(void) ++{ ++ pr_info(DPA_DESCRIPTION "\n"); ++ ++ return 0; ++} ++module_init(dpa_advanced_load); ++ ++static void __exit __cold dpa_advanced_unload(void) ++{ ++ pr_debug(KBUILD_MODNAME ": -> %s:%s()\n", ++ KBUILD_BASENAME".c", __func__); ++ ++} ++module_exit(dpa_advanced_unload); +--- /dev/null ++++ b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_base.h +@@ -0,0 +1,50 @@ ++/* Copyright 2008-2013 Freescale Semiconductor, Inc. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * * Neither the name of Freescale Semiconductor nor the ++ * names of its contributors may be used to endorse or promote products ++ * derived from this software without specific prior written permission. ++ * ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") as published by the Free Software ++ * Foundation, either version 2 of that License or (at your option) any ++ * later version. ++ * ++ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. ++ */ ++ ++#ifndef __DPAA_ETH_BASE_H ++#define __DPAA_ETH_BASE_H ++ ++#include <linux/etherdevice.h> /* struct net_device */ ++#include <linux/fsl_bman.h> /* struct bm_buffer */ ++#include <linux/of_platform.h> /* struct platform_device */ ++#include <linux/net_tstamp.h> /* struct hwtstamp_config */ ++ ++extern uint8_t advanced_debug; ++extern const struct dpa_fq_cbs_t shared_fq_cbs; ++extern int __hot dpa_shared_tx(struct sk_buff *skb, struct net_device *net_dev); ++ ++struct dpa_bp * __cold __must_check /* __attribute__((nonnull)) */ ++dpa_bp_probe(struct platform_device *_of_dev, size_t *count); ++int dpa_bp_create(struct net_device *net_dev, struct dpa_bp *dpa_bp, ++ size_t count); ++int dpa_bp_shared_port_seed(struct dpa_bp *bp); ++ ++#endif /* __DPAA_ETH_BASE_H */ +--- /dev/null ++++ b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_ceetm.c +@@ -0,0 +1,1991 @@ ++/* Copyright 2008-2016 Freescale Semiconductor Inc. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * * Neither the name of Freescale Semiconductor nor the ++ * names of its contributors may be used to endorse or promote products ++ * derived from this software without specific prior written permission. ++ * ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") as published by the Free Software ++ * Foundation, either version 2 of that License or (at your option) any ++ * later version. ++ * ++ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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/init.h> ++#include "dpaa_eth_ceetm.h" ++ ++#define DPA_CEETM_DESCRIPTION "FSL DPAA CEETM qdisc" ++ ++const struct nla_policy ceetm_policy[TCA_CEETM_MAX + 1] = { ++ [TCA_CEETM_COPT] = { .len = sizeof(struct tc_ceetm_copt) }, ++ [TCA_CEETM_QOPS] = { .len = sizeof(struct tc_ceetm_qopt) }, ++}; ++ ++struct Qdisc_ops ceetm_qdisc_ops; ++ ++/* Obtain the DCP and the SP ids from the FMan port */ ++static void get_dcp_and_sp(struct net_device *dev, enum qm_dc_portal *dcp_id, ++ unsigned int *sp_id) ++{ ++ uint32_t channel; ++ t_LnxWrpFmPortDev *port_dev; ++ struct dpa_priv_s *dpa_priv = netdev_priv(dev); ++ struct mac_device *mac_dev = dpa_priv->mac_dev; ++ ++ port_dev = (t_LnxWrpFmPortDev *)mac_dev->port_dev[TX]; ++ channel = port_dev->txCh; ++ ++ *sp_id = channel & CHANNEL_SP_MASK; ++ pr_debug(KBUILD_BASENAME " : FM sub-portal ID %d\n", *sp_id); ++ ++ if (channel < DCP0_MAX_CHANNEL) { ++ *dcp_id = qm_dc_portal_fman0; ++ pr_debug(KBUILD_BASENAME " : DCP ID 0\n"); ++ } else { ++ *dcp_id = qm_dc_portal_fman1; ++ pr_debug(KBUILD_BASENAME " : DCP ID 1\n"); ++ } ++} ++ ++/* Enqueue Rejection Notification callback */ ++static void ceetm_ern(struct qman_portal *portal, struct qman_fq *fq, ++ const struct qm_mr_entry *msg) ++{ ++ struct net_device *net_dev; ++ struct ceetm_class *cls; ++ struct ceetm_class_stats *cstats = NULL; ++ const struct dpa_priv_s *dpa_priv; ++ struct dpa_percpu_priv_s *dpa_percpu_priv; ++ struct sk_buff *skb; ++ struct qm_fd fd = msg->ern.fd; ++ ++ net_dev = ((struct ceetm_fq *)fq)->net_dev; ++ dpa_priv = netdev_priv(net_dev); ++ dpa_percpu_priv = raw_cpu_ptr(dpa_priv->percpu_priv); ++ ++ /* Increment DPA counters */ ++ dpa_percpu_priv->stats.tx_dropped++; ++ dpa_percpu_priv->stats.tx_fifo_errors++; ++ ++ /* Increment CEETM counters */ ++ cls = ((struct ceetm_fq *)fq)->ceetm_cls; ++ switch (cls->type) { ++ case CEETM_PRIO: ++ cstats = this_cpu_ptr(cls->prio.cstats); ++ break; ++ case CEETM_WBFS: ++ cstats = this_cpu_ptr(cls->wbfs.cstats); ++ break; ++ } ++ ++ if (cstats) ++ cstats->ern_drop_count++; ++ ++ if (fd.bpid != 0xff) { ++ dpa_fd_release(net_dev, &fd); ++ return; ++ } ++ ++ skb = _dpa_cleanup_tx_fd(dpa_priv, &fd); ++ dev_kfree_skb_any(skb); ++} ++ ++/* Congestion State Change Notification callback */ ++static void ceetm_cscn(struct qm_ceetm_ccg *ccg, void *cb_ctx, int congested) ++{ ++ struct ceetm_fq *ceetm_fq = (struct ceetm_fq *)cb_ctx; ++ struct dpa_priv_s *dpa_priv = netdev_priv(ceetm_fq->net_dev); ++ struct ceetm_class *cls = ceetm_fq->ceetm_cls; ++ struct ceetm_class_stats *cstats = NULL; ++ ++ switch (cls->type) { ++ case CEETM_PRIO: ++ cstats = this_cpu_ptr(cls->prio.cstats); ++ break; ++ case CEETM_WBFS: ++ cstats = this_cpu_ptr(cls->wbfs.cstats); ++ break; ++ } ++ ++ if (congested) { ++ dpa_priv->cgr_data.congestion_start_jiffies = jiffies; ++ netif_tx_stop_all_queues(dpa_priv->net_dev); ++ dpa_priv->cgr_data.cgr_congested_count++; ++ if (cstats) ++ cstats->congested_count++; ++ } else { ++ dpa_priv->cgr_data.congested_jiffies += ++ (jiffies - dpa_priv->cgr_data.congestion_start_jiffies); ++ netif_tx_wake_all_queues(dpa_priv->net_dev); ++ } ++} ++ ++/* Allocate a ceetm fq */ ++static int ceetm_alloc_fq(struct ceetm_fq **fq, struct net_device *dev, ++ struct ceetm_class *cls) ++{ ++ *fq = kzalloc(sizeof(**fq), GFP_KERNEL); ++ if (!*fq) ++ return -ENOMEM; ++ ++ (*fq)->net_dev = dev; ++ (*fq)->ceetm_cls = cls; ++ return 0; ++} ++ ++/* Configure a ceetm Class Congestion Group */ ++static int ceetm_config_ccg(struct qm_ceetm_ccg **ccg, ++ struct qm_ceetm_channel *channel, unsigned int id, ++ struct ceetm_fq *fq, struct dpa_priv_s *dpa_priv) ++{ ++ int err; ++ u32 cs_th; ++ u16 ccg_mask; ++ struct qm_ceetm_ccg_params ccg_params; ++ ++ err = qman_ceetm_ccg_claim(ccg, channel, id, ceetm_cscn, fq); ++ if (err) ++ return err; ++ ++ /* Configure the count mode (frames/bytes), enable congestion state ++ * notifications, configure the congestion entry and exit thresholds, ++ * enable tail-drop, configure the tail-drop mode, and set the ++ * overhead accounting limit ++ */ ++ ccg_mask = QM_CCGR_WE_MODE | ++ QM_CCGR_WE_CSCN_EN | ++ QM_CCGR_WE_CS_THRES_IN | QM_CCGR_WE_CS_THRES_OUT | ++ QM_CCGR_WE_TD_EN | QM_CCGR_WE_TD_MODE | ++ QM_CCGR_WE_OAL; ++ ++ ccg_params.mode = 0; /* count bytes */ ++ ccg_params.cscn_en = 1; /* generate notifications */ ++ ccg_params.td_en = 1; /* enable tail-drop */ ++ ccg_params.td_mode = 0; /* tail-drop on congestion state */ ++ ccg_params.oal = (signed char)(min(sizeof(struct sk_buff) + ++ dpa_priv->tx_headroom, (size_t)FSL_QMAN_MAX_OAL)); ++ ++ /* Set the congestion state thresholds according to the link speed */ ++ if (dpa_priv->mac_dev->if_support & SUPPORTED_10000baseT_Full) ++ cs_th = CONFIG_FSL_DPAA_CS_THRESHOLD_10G; ++ else ++ cs_th = CONFIG_FSL_DPAA_CS_THRESHOLD_1G; ++ ++ qm_cgr_cs_thres_set64(&ccg_params.cs_thres_in, cs_th, 1); ++ qm_cgr_cs_thres_set64(&ccg_params.cs_thres_out, ++ cs_th * CEETM_CCGR_RATIO, 1); ++ ++ err = qman_ceetm_ccg_set(*ccg, ccg_mask, &ccg_params); ++ if (err) ++ return err; ++ ++ return 0; ++} ++ ++/* Configure a ceetm Logical Frame Queue */ ++static int ceetm_config_lfq(struct qm_ceetm_cq *cq, struct ceetm_fq *fq, ++ struct qm_ceetm_lfq **lfq) ++{ ++ int err; ++ u64 context_a; ++ u32 context_b; ++ ++ err = qman_ceetm_lfq_claim(lfq, cq); ++ if (err) ++ return err; ++ ++ /* Get the former contexts in order to preserve context B */ ++ err = qman_ceetm_lfq_get_context(*lfq, &context_a, &context_b); ++ if (err) ++ return err; ++ ++ context_a = CEETM_CONTEXT_A; ++ err = qman_ceetm_lfq_set_context(*lfq, context_a, context_b); ++ if (err) ++ return err; ++ ++ (*lfq)->ern = ceetm_ern; ++ ++ err = qman_ceetm_create_fq(*lfq, &fq->fq); ++ if (err) ++ return err; ++ ++ return 0; ++} ++ ++/* Configure a prio ceetm class */ ++static int ceetm_config_prio_cls(struct ceetm_class *cls, ++ struct net_device *dev, ++ struct qm_ceetm_channel *channel, ++ unsigned int id) ++{ ++ int err; ++ struct dpa_priv_s *dpa_priv = netdev_priv(dev); ++ ++ err = ceetm_alloc_fq(&cls->prio.fq, dev, cls); ++ if (err) ++ return err; ++ ++ /* Claim and configure the CCG */ ++ err = ceetm_config_ccg(&cls->prio.ccg, channel, id, cls->prio.fq, ++ dpa_priv); ++ if (err) ++ return err; ++ ++ /* Claim and configure the CQ */ ++ err = qman_ceetm_cq_claim(&cls->prio.cq, channel, id, cls->prio.ccg); ++ if (err) ++ return err; ++ ++ if (cls->shaped) { ++ err = qman_ceetm_channel_set_cq_cr_eligibility(channel, id, 1); ++ if (err) ++ return err; ++ ++ err = qman_ceetm_channel_set_cq_er_eligibility(channel, id, 1); ++ if (err) ++ return err; ++ } ++ ++ /* Claim and configure a LFQ */ ++ err = ceetm_config_lfq(cls->prio.cq, cls->prio.fq, &cls->prio.lfq); ++ if (err) ++ return err; ++ ++ return 0; ++} ++ ++/* Configure a wbfs ceetm class */ ++static int ceetm_config_wbfs_cls(struct ceetm_class *cls, ++ struct net_device *dev, ++ struct qm_ceetm_channel *channel, ++ unsigned int id, int type) ++{ ++ int err; ++ struct dpa_priv_s *dpa_priv = netdev_priv(dev); ++ ++ err = ceetm_alloc_fq(&cls->wbfs.fq, dev, cls); ++ if (err) ++ return err; ++ ++ /* Claim and configure the CCG */ ++ err = ceetm_config_ccg(&cls->wbfs.ccg, channel, id, cls->wbfs.fq, ++ dpa_priv); ++ if (err) ++ return err; ++ ++ /* Claim and configure the CQ */ ++ if (type == WBFS_GRP_B) ++ err = qman_ceetm_cq_claim_B(&cls->wbfs.cq, channel, id, ++ cls->wbfs.ccg); ++ else ++ err = qman_ceetm_cq_claim_A(&cls->wbfs.cq, channel, id, ++ cls->wbfs.ccg); ++ if (err) ++ return err; ++ ++ /* Configure the CQ weight: real number multiplied by 100 to get rid ++ * of the fraction ++ */ ++ err = qman_ceetm_set_queue_weight_in_ratio(cls->wbfs.cq, ++ cls->wbfs.weight * 100); ++ if (err) ++ return err; ++ ++ /* Claim and configure a LFQ */ ++ err = ceetm_config_lfq(cls->wbfs.cq, cls->wbfs.fq, &cls->wbfs.lfq); ++ if (err) ++ return err; ++ ++ return 0; ++} ++ ++/* Find class in qdisc hash table using given handle */ ++static inline struct ceetm_class *ceetm_find(u32 handle, struct Qdisc *sch) ++{ ++ struct ceetm_qdisc *priv = qdisc_priv(sch); ++ struct Qdisc_class_common *clc; ++ ++ pr_debug(KBUILD_BASENAME " : %s : find class %X in qdisc %X\n", ++ __func__, handle, sch->handle); ++ ++ clc = qdisc_class_find(&priv->clhash, handle); ++ return clc ? container_of(clc, struct ceetm_class, common) : NULL; ++} ++ ++/* Insert a class in the qdisc's class hash */ ++static void ceetm_link_class(struct Qdisc *sch, ++ struct Qdisc_class_hash *clhash, ++ struct Qdisc_class_common *common) ++{ ++ sch_tree_lock(sch); ++ qdisc_class_hash_insert(clhash, common); ++ sch_tree_unlock(sch); ++ qdisc_class_hash_grow(sch, clhash); ++} ++ ++/* Destroy a ceetm class */ ++static void ceetm_cls_destroy(struct Qdisc *sch, struct ceetm_class *cl) ++{ ++ if (!cl) ++ return; ++ ++ pr_debug(KBUILD_BASENAME " : %s : destroy class %X from under %X\n", ++ __func__, cl->common.classid, sch->handle); ++ ++ switch (cl->type) { ++ case CEETM_ROOT: ++ if (cl->root.child) { ++ qdisc_destroy(cl->root.child); ++ cl->root.child = NULL; ++ } ++ ++ if (cl->root.ch && qman_ceetm_channel_release(cl->root.ch)) ++ pr_err(KBUILD_BASENAME ++ " : %s : error releasing the channel %d\n", ++ __func__, cl->root.ch->idx); ++ ++ break; ++ ++ case CEETM_PRIO: ++ if (cl->prio.child) { ++ qdisc_destroy(cl->prio.child); ++ cl->prio.child = NULL; ++ } ++ ++ if (cl->prio.lfq && qman_ceetm_lfq_release(cl->prio.lfq)) ++ pr_err(KBUILD_BASENAME ++ " : %s : error releasing the LFQ %d\n", ++ __func__, cl->prio.lfq->idx); ++ ++ if (cl->prio.cq && qman_ceetm_cq_release(cl->prio.cq)) ++ pr_err(KBUILD_BASENAME ++ " : %s : error releasing the CQ %d\n", ++ __func__, cl->prio.cq->idx); ++ ++ if (cl->prio.ccg && qman_ceetm_ccg_release(cl->prio.ccg)) ++ pr_err(KBUILD_BASENAME ++ " : %s : error releasing the CCG %d\n", ++ __func__, cl->prio.ccg->idx); ++ ++ kfree(cl->prio.fq); ++ ++ if (cl->prio.cstats) ++ free_percpu(cl->prio.cstats); ++ ++ break; ++ ++ case CEETM_WBFS: ++ if (cl->wbfs.lfq && qman_ceetm_lfq_release(cl->wbfs.lfq)) ++ pr_err(KBUILD_BASENAME ++ " : %s : error releasing the LFQ %d\n", ++ __func__, cl->wbfs.lfq->idx); ++ ++ if (cl->wbfs.cq && qman_ceetm_cq_release(cl->wbfs.cq)) ++ pr_err(KBUILD_BASENAME ++ " : %s : error releasing the CQ %d\n", ++ __func__, cl->wbfs.cq->idx); ++ ++ if (cl->wbfs.ccg && qman_ceetm_ccg_release(cl->wbfs.ccg)) ++ pr_err(KBUILD_BASENAME ++ " : %s : error releasing the CCG %d\n", ++ __func__, cl->wbfs.ccg->idx); ++ ++ kfree(cl->wbfs.fq); ++ ++ if (cl->wbfs.cstats) ++ free_percpu(cl->wbfs.cstats); ++ } ++ ++ tcf_destroy_chain(&cl->filter_list); ++ kfree(cl); ++} ++ ++/* Destroy a ceetm qdisc */ ++static void ceetm_destroy(struct Qdisc *sch) ++{ ++ unsigned int ntx, i; ++ struct hlist_node *next; ++ struct ceetm_class *cl; ++ struct ceetm_qdisc *priv = qdisc_priv(sch); ++ struct net_device *dev = qdisc_dev(sch); ++ ++ pr_debug(KBUILD_BASENAME " : %s : destroy qdisc %X\n", ++ __func__, sch->handle); ++ ++ /* All filters need to be removed before destroying the classes */ ++ tcf_destroy_chain(&priv->filter_list); ++ ++ for (i = 0; i < priv->clhash.hashsize; i++) { ++ hlist_for_each_entry(cl, &priv->clhash.hash[i], common.hnode) ++ tcf_destroy_chain(&cl->filter_list); ++ } ++ ++ for (i = 0; i < priv->clhash.hashsize; i++) { ++ hlist_for_each_entry_safe(cl, next, &priv->clhash.hash[i], ++ common.hnode) ++ ceetm_cls_destroy(sch, cl); ++ } ++ ++ qdisc_class_hash_destroy(&priv->clhash); ++ ++ switch (priv->type) { ++ case CEETM_ROOT: ++ dpa_disable_ceetm(dev); ++ ++ if (priv->root.lni && qman_ceetm_lni_release(priv->root.lni)) ++ pr_err(KBUILD_BASENAME ++ " : %s : error releasing the LNI %d\n", ++ __func__, priv->root.lni->idx); ++ ++ if (priv->root.sp && qman_ceetm_sp_release(priv->root.sp)) ++ pr_err(KBUILD_BASENAME ++ " : %s : error releasing the SP %d\n", ++ __func__, priv->root.sp->idx); ++ ++ if (priv->root.qstats) ++ free_percpu(priv->root.qstats); ++ ++ if (!priv->root.qdiscs) ++ break; ++ ++ /* Remove the pfifo qdiscs */ ++ for (ntx = 0; ntx < dev->num_tx_queues; ntx++) ++ if (priv->root.qdiscs[ntx]) ++ qdisc_destroy(priv->root.qdiscs[ntx]); ++ ++ kfree(priv->root.qdiscs); ++ break; ++ ++ case CEETM_PRIO: ++ if (priv->prio.parent) ++ priv->prio.parent->root.child = NULL; ++ break; ++ ++ case CEETM_WBFS: ++ if (priv->wbfs.parent) ++ priv->wbfs.parent->prio.child = NULL; ++ break; ++ } ++} ++ ++static int ceetm_dump(struct Qdisc *sch, struct sk_buff *skb) ++{ ++ struct Qdisc *qdisc; ++ unsigned int ntx, i; ++ struct nlattr *nest; ++ struct tc_ceetm_qopt qopt; ++ struct ceetm_qdisc_stats *qstats; ++ struct net_device *dev = qdisc_dev(sch); ++ struct ceetm_qdisc *priv = qdisc_priv(sch); ++ ++ pr_debug(KBUILD_BASENAME " : %s : qdisc %X\n", __func__, sch->handle); ++ ++ sch_tree_lock(sch); ++ memset(&qopt, 0, sizeof(qopt)); ++ qopt.type = priv->type; ++ qopt.shaped = priv->shaped; ++ ++ switch (priv->type) { ++ case CEETM_ROOT: ++ /* Gather statistics from the underlying pfifo qdiscs */ ++ sch->q.qlen = 0; ++ memset(&sch->bstats, 0, sizeof(sch->bstats)); ++ memset(&sch->qstats, 0, sizeof(sch->qstats)); ++ ++ for (ntx = 0; ntx < dev->num_tx_queues; ntx++) { ++ qdisc = netdev_get_tx_queue(dev, ntx)->qdisc_sleeping; ++ sch->q.qlen += qdisc->q.qlen; ++ sch->bstats.bytes += qdisc->bstats.bytes; ++ sch->bstats.packets += qdisc->bstats.packets; ++ sch->qstats.qlen += qdisc->qstats.qlen; ++ sch->qstats.backlog += qdisc->qstats.backlog; ++ sch->qstats.drops += qdisc->qstats.drops; ++ sch->qstats.requeues += qdisc->qstats.requeues; ++ sch->qstats.overlimits += qdisc->qstats.overlimits; ++ } ++ ++ for_each_online_cpu(i) { ++ qstats = per_cpu_ptr(priv->root.qstats, i); ++ sch->qstats.drops += qstats->drops; ++ } ++ ++ qopt.rate = priv->root.rate; ++ qopt.ceil = priv->root.ceil; ++ qopt.overhead = priv->root.overhead; ++ break; ++ ++ case CEETM_PRIO: ++ qopt.qcount = priv->prio.qcount; ++ break; ++ ++ case CEETM_WBFS: ++ qopt.qcount = priv->wbfs.qcount; ++ qopt.cr = priv->wbfs.cr; ++ qopt.er = priv->wbfs.er; ++ break; ++ ++ default: ++ pr_err(KBUILD_BASENAME " : %s : invalid qdisc\n", __func__); ++ sch_tree_unlock(sch); ++ return -EINVAL; ++ } ++ ++ nest = nla_nest_start(skb, TCA_OPTIONS); ++ if (!nest) ++ goto nla_put_failure; ++ if (nla_put(skb, TCA_CEETM_QOPS, sizeof(qopt), &qopt)) ++ goto nla_put_failure; ++ nla_nest_end(skb, nest); ++ ++ sch_tree_unlock(sch); ++ return skb->len; ++ ++nla_put_failure: ++ sch_tree_unlock(sch); ++ nla_nest_cancel(skb, nest); ++ return -EMSGSIZE; ++} ++ ++/* Configure a root ceetm qdisc */ ++static int ceetm_init_root(struct Qdisc *sch, struct ceetm_qdisc *priv, ++ struct tc_ceetm_qopt *qopt) ++{ ++ struct netdev_queue *dev_queue; ++ struct Qdisc *qdisc; ++ enum qm_dc_portal dcp_id; ++ unsigned int i, sp_id, parent_id; ++ int err; ++ u64 bps; ++ struct qm_ceetm_sp *sp; ++ struct qm_ceetm_lni *lni; ++ struct net_device *dev = qdisc_dev(sch); ++ struct dpa_priv_s *dpa_priv = netdev_priv(dev); ++ struct mac_device *mac_dev = dpa_priv->mac_dev; ++ ++ pr_debug(KBUILD_BASENAME " : %s : qdisc %X\n", __func__, sch->handle); ++ ++ /* Validate inputs */ ++ if (sch->parent != TC_H_ROOT) { ++ pr_err("CEETM: a root ceetm qdisc can not be attached to a class\n"); ++ tcf_destroy_chain(&priv->filter_list); ++ qdisc_class_hash_destroy(&priv->clhash); ++ return -EINVAL; ++ } ++ ++ if (!mac_dev) { ++ pr_err("CEETM: the interface is lacking a mac\n"); ++ err = -EINVAL; ++ goto err_init_root; ++ } ++ ++ /* pre-allocate underlying pfifo qdiscs */ ++ priv->root.qdiscs = kcalloc(dev->num_tx_queues, ++ sizeof(priv->root.qdiscs[0]), ++ GFP_KERNEL); ++ if (!priv->root.qdiscs) { ++ err = -ENOMEM; ++ goto err_init_root; ++ } ++ ++ for (i = 0; i < dev->num_tx_queues; i++) { ++ dev_queue = netdev_get_tx_queue(dev, i); ++ parent_id = TC_H_MAKE(TC_H_MAJ(sch->handle), ++ TC_H_MIN(i + PFIFO_MIN_OFFSET)); ++ ++ qdisc = qdisc_create_dflt(dev_queue, &pfifo_qdisc_ops, ++ parent_id); ++ if (!qdisc) { ++ err = -ENOMEM; ++ goto err_init_root; ++ } ++ ++ priv->root.qdiscs[i] = qdisc; ++ qdisc->flags |= TCQ_F_ONETXQUEUE; ++ } ++ ++ sch->flags |= TCQ_F_MQROOT; ++ ++ priv->root.qstats = alloc_percpu(struct ceetm_qdisc_stats); ++ if (!priv->root.qstats) { ++ pr_err(KBUILD_BASENAME " : %s : alloc_percpu() failed\n", ++ __func__); ++ err = -ENOMEM; ++ goto err_init_root; ++ } ++ ++ priv->shaped = qopt->shaped; ++ priv->root.rate = qopt->rate; ++ priv->root.ceil = qopt->ceil; ++ priv->root.overhead = qopt->overhead; ++ ++ /* Claim the SP */ ++ get_dcp_and_sp(dev, &dcp_id, &sp_id); ++ err = qman_ceetm_sp_claim(&sp, dcp_id, sp_id); ++ if (err) { ++ pr_err(KBUILD_BASENAME " : %s : failed to claim the SP\n", ++ __func__); ++ goto err_init_root; ++ } ++ ++ priv->root.sp = sp; ++ ++ /* Claim the LNI - will use the same id as the SP id since SPs 0-7 ++ * are connected to the TX FMan ports ++ */ ++ err = qman_ceetm_lni_claim(&lni, dcp_id, sp_id); ++ if (err) { ++ pr_err(KBUILD_BASENAME " : %s : failed to claim the LNI\n", ++ __func__); ++ goto err_init_root; ++ } ++ ++ priv->root.lni = lni; ++ ++ err = qman_ceetm_sp_set_lni(sp, lni); ++ if (err) { ++ pr_err(KBUILD_BASENAME " : %s : failed to link the SP and LNI\n", ++ __func__); ++ goto err_init_root; ++ } ++ ++ lni->sp = sp; ++ ++ /* Configure the LNI shaper */ ++ if (priv->shaped) { ++ err = qman_ceetm_lni_enable_shaper(lni, 1, priv->root.overhead); ++ if (err) { ++ pr_err(KBUILD_BASENAME " : %s : failed to configure the LNI shaper\n", ++ __func__); ++ goto err_init_root; ++ } ++ ++ bps = priv->root.rate << 3; /* Bps -> bps */ ++ err = qman_ceetm_lni_set_commit_rate_bps(lni, bps, dev->mtu); ++ if (err) { ++ pr_err(KBUILD_BASENAME " : %s : failed to configure the LNI shaper\n", ++ __func__); ++ goto err_init_root; ++ } ++ ++ bps = priv->root.ceil << 3; /* Bps -> bps */ ++ err = qman_ceetm_lni_set_excess_rate_bps(lni, bps, dev->mtu); ++ if (err) { ++ pr_err(KBUILD_BASENAME " : %s : failed to configure the LNI shaper\n", ++ __func__); ++ goto err_init_root; ++ } ++ } ++ ++ /* TODO default configuration */ ++ ++ dpa_enable_ceetm(dev); ++ return 0; ++ ++err_init_root: ++ ceetm_destroy(sch); ++ return err; ++} ++ ++/* Configure a prio ceetm qdisc */ ++static int ceetm_init_prio(struct Qdisc *sch, struct ceetm_qdisc *priv, ++ struct tc_ceetm_qopt *qopt) ++{ ++ int err; ++ unsigned int i; ++ struct ceetm_class *parent_cl, *child_cl; ++ struct Qdisc *parent_qdisc; ++ struct net_device *dev = qdisc_dev(sch); ++ ++ pr_debug(KBUILD_BASENAME " : %s : qdisc %X\n", __func__, sch->handle); ++ ++ if (sch->parent == TC_H_ROOT) { ++ pr_err("CEETM: a prio ceetm qdisc can not be root\n"); ++ err = -EINVAL; ++ goto err_init_prio; ++ } ++ ++ parent_qdisc = qdisc_lookup(dev, TC_H_MAJ(sch->parent)); ++ if (strcmp(parent_qdisc->ops->id, ceetm_qdisc_ops.id)) { ++ pr_err("CEETM: a ceetm qdisc can not be attached to other qdisc/class types\n"); ++ err = -EINVAL; ++ goto err_init_prio; ++ } ++ ++ /* Obtain the parent root ceetm_class */ ++ parent_cl = ceetm_find(sch->parent, parent_qdisc); ++ ++ if (!parent_cl || parent_cl->type != CEETM_ROOT) { ++ pr_err("CEETM: a prio ceetm qdiscs can be added only under a root ceetm class\n"); ++ err = -EINVAL; ++ goto err_init_prio; ++ } ++ ++ priv->prio.parent = parent_cl; ++ parent_cl->root.child = sch; ++ ++ priv->shaped = parent_cl->shaped; ++ priv->prio.qcount = qopt->qcount; ++ ++ /* Create and configure qcount child classes */ ++ for (i = 0; i < priv->prio.qcount; i++) { ++ child_cl = kzalloc(sizeof(*child_cl), GFP_KERNEL); ++ if (!child_cl) { ++ pr_err(KBUILD_BASENAME " : %s : kzalloc() failed\n", ++ __func__); ++ err = -ENOMEM; ++ goto err_init_prio; ++ } ++ ++ child_cl->prio.cstats = alloc_percpu(struct ceetm_class_stats); ++ if (!child_cl->prio.cstats) { ++ pr_err(KBUILD_BASENAME " : %s : alloc_percpu() failed\n", ++ __func__); ++ err = -ENOMEM; ++ goto err_init_prio_cls; ++ } ++ ++ child_cl->common.classid = TC_H_MAKE(sch->handle, (i + 1)); ++ child_cl->refcnt = 1; ++ child_cl->parent = sch; ++ child_cl->type = CEETM_PRIO; ++ child_cl->shaped = priv->shaped; ++ child_cl->prio.child = NULL; ++ ++ /* All shaped CQs have CR and ER enabled by default */ ++ child_cl->prio.cr = child_cl->shaped; ++ child_cl->prio.er = child_cl->shaped; ++ child_cl->prio.fq = NULL; ++ child_cl->prio.cq = NULL; ++ ++ /* Configure the corresponding hardware CQ */ ++ err = ceetm_config_prio_cls(child_cl, dev, ++ parent_cl->root.ch, i); ++ if (err) { ++ pr_err(KBUILD_BASENAME " : %s : failed to configure the ceetm prio class %X\n", ++ __func__, child_cl->common.classid); ++ goto err_init_prio_cls; ++ } ++ ++ /* Add class handle in Qdisc */ ++ ceetm_link_class(sch, &priv->clhash, &child_cl->common); ++ pr_debug(KBUILD_BASENAME " : %s : added ceetm prio class %X associated with CQ %d and CCG %d\n", ++ __func__, child_cl->common.classid, ++ child_cl->prio.cq->idx, child_cl->prio.ccg->idx); ++ } ++ ++ return 0; ++ ++err_init_prio_cls: ++ ceetm_cls_destroy(sch, child_cl); ++err_init_prio: ++ ceetm_destroy(sch); ++ return err; ++} ++ ++/* Configure a wbfs ceetm qdisc */ ++static int ceetm_init_wbfs(struct Qdisc *sch, struct ceetm_qdisc *priv, ++ struct tc_ceetm_qopt *qopt) ++{ ++ int err, group_b, small_group; ++ unsigned int i, id, prio_a, prio_b; ++ struct ceetm_class *parent_cl, *child_cl, *root_cl; ++ struct Qdisc *parent_qdisc; ++ struct ceetm_qdisc *parent_priv; ++ struct qm_ceetm_channel *channel; ++ struct net_device *dev = qdisc_dev(sch); ++ ++ pr_debug(KBUILD_BASENAME " : %s : qdisc %X\n", __func__, sch->handle); ++ ++ /* Validate inputs */ ++ if (sch->parent == TC_H_ROOT) { ++ pr_err("CEETM: a wbfs ceetm qdiscs can not be root\n"); ++ err = -EINVAL; ++ goto err_init_wbfs; ++ } ++ ++ /* Obtain the parent prio ceetm qdisc */ ++ parent_qdisc = qdisc_lookup(dev, TC_H_MAJ(sch->parent)); ++ if (strcmp(parent_qdisc->ops->id, ceetm_qdisc_ops.id)) { ++ pr_err("CEETM: a ceetm qdisc can not be attached to other qdisc/class types\n"); ++ err = -EINVAL; ++ goto err_init_wbfs; ++ } ++ ++ /* Obtain the parent prio ceetm class */ ++ parent_cl = ceetm_find(sch->parent, parent_qdisc); ++ parent_priv = qdisc_priv(parent_qdisc); ++ ++ if (!parent_cl || parent_cl->type != CEETM_PRIO) { ++ pr_err("CEETM: a wbfs ceetm qdiscs can be added only under a prio ceetm class\n"); ++ err = -EINVAL; ++ goto err_init_wbfs; ++ } ++ ++ if (!qopt->qcount || !qopt->qweight[0]) { ++ pr_err("CEETM: qcount and qweight are mandatory for a wbfs ceetm qdisc\n"); ++ err = -EINVAL; ++ goto err_init_wbfs; ++ } ++ ++ priv->shaped = parent_cl->shaped; ++ ++ if (!priv->shaped && (qopt->cr || qopt->er)) { ++ pr_err("CEETM: CR/ER can be enabled only for shaped wbfs ceetm qdiscs\n"); ++ err = -EINVAL; ++ goto err_init_wbfs; ++ } ++ ++ if (priv->shaped && !(qopt->cr || qopt->er)) { ++ pr_err("CEETM: either CR or ER must be enabled for shaped wbfs ceetm qdiscs\n"); ++ err = -EINVAL; ++ goto err_init_wbfs; ++ } ++ ++ /* Obtain the parent root ceetm class */ ++ root_cl = parent_priv->prio.parent; ++ if ((root_cl->root.wbfs_grp_a && root_cl->root.wbfs_grp_b) || ++ root_cl->root.wbfs_grp_large) { ++ pr_err("CEETM: no more wbfs classes are available\n"); ++ err = -EINVAL; ++ goto err_init_wbfs; ++ } ++ ++ if ((root_cl->root.wbfs_grp_a || root_cl->root.wbfs_grp_b) && ++ qopt->qcount == CEETM_MAX_WBFS_QCOUNT) { ++ pr_err("CEETM: only %d wbfs classes are available\n", ++ CEETM_MIN_WBFS_QCOUNT); ++ err = -EINVAL; ++ goto err_init_wbfs; ++ } ++ ++ priv->wbfs.parent = parent_cl; ++ parent_cl->prio.child = sch; ++ ++ priv->wbfs.qcount = qopt->qcount; ++ priv->wbfs.cr = qopt->cr; ++ priv->wbfs.er = qopt->er; ++ ++ channel = root_cl->root.ch; ++ ++ /* Configure the hardware wbfs channel groups */ ++ if (priv->wbfs.qcount == CEETM_MAX_WBFS_QCOUNT) { ++ /* Configure the large group A */ ++ priv->wbfs.group_type = WBFS_GRP_LARGE; ++ small_group = false; ++ group_b = false; ++ prio_a = TC_H_MIN(parent_cl->common.classid) - 1; ++ prio_b = prio_a; ++ ++ } else if (root_cl->root.wbfs_grp_a) { ++ /* Configure the group B */ ++ priv->wbfs.group_type = WBFS_GRP_B; ++ ++ err = qman_ceetm_channel_get_group(channel, &small_group, ++ &prio_a, &prio_b); ++ if (err) { ++ pr_err(KBUILD_BASENAME " : %s : failed to get group details\n", ++ __func__); ++ goto err_init_wbfs; ++ } ++ ++ small_group = true; ++ group_b = true; ++ prio_b = TC_H_MIN(parent_cl->common.classid) - 1; ++ /* If group A isn't configured, configure it as group B */ ++ prio_a = prio_a ? : prio_b; ++ ++ } else { ++ /* Configure the small group A */ ++ priv->wbfs.group_type = WBFS_GRP_A; ++ ++ err = qman_ceetm_channel_get_group(channel, &small_group, ++ &prio_a, &prio_b); ++ if (err) { ++ pr_err(KBUILD_BASENAME " : %s : failed to get group details\n", ++ __func__); ++ goto err_init_wbfs; ++ } ++ ++ small_group = true; ++ group_b = false; ++ prio_a = TC_H_MIN(parent_cl->common.classid) - 1; ++ /* If group B isn't configured, configure it as group A */ ++ prio_b = prio_b ? : prio_a; ++ } ++ ++ err = qman_ceetm_channel_set_group(channel, small_group, prio_a, ++ prio_b); ++ if (err) ++ goto err_init_wbfs; ++ ++ if (priv->shaped) { ++ err = qman_ceetm_channel_set_group_cr_eligibility(channel, ++ group_b, ++ priv->wbfs.cr); ++ if (err) { ++ pr_err(KBUILD_BASENAME " : %s : failed to set group CR eligibility\n", ++ __func__); ++ goto err_init_wbfs; ++ } ++ ++ err = qman_ceetm_channel_set_group_er_eligibility(channel, ++ group_b, ++ priv->wbfs.er); ++ if (err) { ++ pr_err(KBUILD_BASENAME " : %s : failed to set group ER eligibility\n", ++ __func__); ++ goto err_init_wbfs; ++ } ++ } ++ ++ /* Create qcount child classes */ ++ for (i = 0; i < priv->wbfs.qcount; i++) { ++ child_cl = kzalloc(sizeof(*child_cl), GFP_KERNEL); ++ if (!child_cl) { ++ pr_err(KBUILD_BASENAME " : %s : kzalloc() failed\n", ++ __func__); ++ err = -ENOMEM; ++ goto err_init_wbfs; ++ } ++ ++ child_cl->wbfs.cstats = alloc_percpu(struct ceetm_class_stats); ++ if (!child_cl->wbfs.cstats) { ++ pr_err(KBUILD_BASENAME " : %s : alloc_percpu() failed\n", ++ __func__); ++ err = -ENOMEM; ++ goto err_init_wbfs_cls; ++ } ++ ++ child_cl->common.classid = TC_H_MAKE(sch->handle, (i + 1)); ++ child_cl->refcnt = 1; ++ child_cl->parent = sch; ++ child_cl->type = CEETM_WBFS; ++ child_cl->shaped = priv->shaped; ++ child_cl->wbfs.fq = NULL; ++ child_cl->wbfs.cq = NULL; ++ child_cl->wbfs.weight = qopt->qweight[i]; ++ ++ if (priv->wbfs.group_type == WBFS_GRP_B) ++ id = WBFS_GRP_B_OFFSET + i; ++ else ++ id = WBFS_GRP_A_OFFSET + i; ++ ++ err = ceetm_config_wbfs_cls(child_cl, dev, channel, id, ++ priv->wbfs.group_type); ++ if (err) { ++ pr_err(KBUILD_BASENAME " : %s : failed to configure the ceetm wbfs class %X\n", ++ __func__, child_cl->common.classid); ++ goto err_init_wbfs_cls; ++ } ++ ++ /* Add class handle in Qdisc */ ++ ceetm_link_class(sch, &priv->clhash, &child_cl->common); ++ pr_debug(KBUILD_BASENAME " : %s : added ceetm wbfs class %X associated with CQ %d and CCG %d\n", ++ __func__, child_cl->common.classid, ++ child_cl->wbfs.cq->idx, child_cl->wbfs.ccg->idx); ++ } ++ ++ /* Signal the root class that a group has been configured */ ++ switch (priv->wbfs.group_type) { ++ case WBFS_GRP_LARGE: ++ root_cl->root.wbfs_grp_large = true; ++ break; ++ case WBFS_GRP_A: ++ root_cl->root.wbfs_grp_a = true; ++ break; ++ case WBFS_GRP_B: ++ root_cl->root.wbfs_grp_b = true; ++ break; ++ } ++ ++ return 0; ++ ++err_init_wbfs_cls: ++ ceetm_cls_destroy(sch, child_cl); ++err_init_wbfs: ++ ceetm_destroy(sch); ++ return err; ++} ++ ++/* Configure a generic ceetm qdisc */ ++static int ceetm_init(struct Qdisc *sch, struct nlattr *opt) ++{ ++ struct tc_ceetm_qopt *qopt; ++ struct nlattr *tb[TCA_CEETM_QOPS + 1]; ++ int ret; ++ struct ceetm_qdisc *priv = qdisc_priv(sch); ++ struct net_device *dev = qdisc_dev(sch); ++ ++ pr_debug(KBUILD_BASENAME " : %s : qdisc %X\n", __func__, sch->handle); ++ ++ if (!netif_is_multiqueue(dev)) ++ return -EOPNOTSUPP; ++ ++ if (!opt) { ++ pr_err(KBUILD_BASENAME " : %s : tc error\n", __func__); ++ return -EINVAL; ++ } ++ ++ ret = nla_parse_nested(tb, TCA_CEETM_QOPS, opt, ceetm_policy); ++ if (ret < 0) { ++ pr_err(KBUILD_BASENAME " : %s : tc error\n", __func__); ++ return ret; ++ } ++ ++ if (!tb[TCA_CEETM_QOPS]) { ++ pr_err(KBUILD_BASENAME " : %s : tc error\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (TC_H_MIN(sch->handle)) { ++ pr_err("CEETM: a qdisc should not have a minor\n"); ++ return -EINVAL; ++ } ++ ++ qopt = nla_data(tb[TCA_CEETM_QOPS]); ++ ++ /* Initialize the class hash list. Each qdisc has its own class hash */ ++ ret = qdisc_class_hash_init(&priv->clhash); ++ if (ret < 0) { ++ pr_err(KBUILD_BASENAME " : %s : qdisc_class_hash_init failed\n", ++ __func__); ++ return ret; ++ } ++ ++ priv->type = qopt->type; ++ ++ switch (priv->type) { ++ case CEETM_ROOT: ++ ret = ceetm_init_root(sch, priv, qopt); ++ break; ++ case CEETM_PRIO: ++ ret = ceetm_init_prio(sch, priv, qopt); ++ break; ++ case CEETM_WBFS: ++ ret = ceetm_init_wbfs(sch, priv, qopt); ++ break; ++ default: ++ pr_err(KBUILD_BASENAME " : %s : invalid qdisc\n", __func__); ++ ceetm_destroy(sch); ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++/* Edit a root ceetm qdisc */ ++static int ceetm_change_root(struct Qdisc *sch, struct ceetm_qdisc *priv, ++ struct net_device *dev, ++ struct tc_ceetm_qopt *qopt) ++{ ++ int err = 0; ++ u64 bps; ++ ++ if (priv->shaped != (bool)qopt->shaped) { ++ pr_err("CEETM: qdisc %X is %s\n", sch->handle, ++ priv->shaped ? "shaped" : "unshaped"); ++ return -EINVAL; ++ } ++ ++ /* Nothing to modify for unshaped qdiscs */ ++ if (!priv->shaped) ++ return 0; ++ ++ /* Configure the LNI shaper */ ++ if (priv->root.overhead != qopt->overhead) { ++ err = qman_ceetm_lni_enable_shaper(priv->root.lni, 1, ++ qopt->overhead); ++ if (err) ++ goto change_err; ++ priv->root.overhead = qopt->overhead; ++ } ++ ++ if (priv->root.rate != qopt->rate) { ++ bps = qopt->rate << 3; /* Bps -> bps */ ++ err = qman_ceetm_lni_set_commit_rate_bps(priv->root.lni, bps, ++ dev->mtu); ++ if (err) ++ goto change_err; ++ priv->root.rate = qopt->rate; ++ } ++ ++ if (priv->root.ceil != qopt->ceil) { ++ bps = qopt->ceil << 3; /* Bps -> bps */ ++ err = qman_ceetm_lni_set_excess_rate_bps(priv->root.lni, bps, ++ dev->mtu); ++ if (err) ++ goto change_err; ++ priv->root.ceil = qopt->ceil; ++ } ++ ++ return 0; ++ ++change_err: ++ pr_err(KBUILD_BASENAME " : %s : failed to configure the root ceetm qdisc %X\n", ++ __func__, sch->handle); ++ return err; ++} ++ ++/* Edit a wbfs ceetm qdisc */ ++static int ceetm_change_wbfs(struct Qdisc *sch, struct ceetm_qdisc *priv, ++ struct tc_ceetm_qopt *qopt) ++{ ++ int err; ++ bool group_b; ++ struct qm_ceetm_channel *channel; ++ struct ceetm_class *prio_class, *root_class; ++ struct ceetm_qdisc *prio_qdisc; ++ ++ if (qopt->qcount) { ++ pr_err("CEETM: the qcount can not be modified\n"); ++ return -EINVAL; ++ } ++ ++ if (qopt->qweight[0]) { ++ pr_err("CEETM: the qweight can be modified through the wbfs classes\n"); ++ return -EINVAL; ++ } ++ ++ if (!priv->shaped && (qopt->cr || qopt->er)) { ++ pr_err("CEETM: CR/ER can be enabled only for shaped wbfs ceetm qdiscs\n"); ++ return -EINVAL; ++ } ++ ++ if (priv->shaped && !(qopt->cr || qopt->er)) { ++ pr_err("CEETM: either CR or ER must be enabled for shaped wbfs ceetm qdiscs\n"); ++ return -EINVAL; ++ } ++ ++ /* Nothing to modify for unshaped qdiscs */ ++ if (!priv->shaped) ++ return 0; ++ ++ prio_class = priv->wbfs.parent; ++ prio_qdisc = qdisc_priv(prio_class->parent); ++ root_class = prio_qdisc->prio.parent; ++ channel = root_class->root.ch; ++ group_b = priv->wbfs.group_type == WBFS_GRP_B; ++ ++ if (qopt->cr != priv->wbfs.cr) { ++ err = qman_ceetm_channel_set_group_cr_eligibility(channel, ++ group_b, ++ qopt->cr); ++ if (err) ++ goto change_err; ++ priv->wbfs.cr = qopt->cr; ++ } ++ ++ if (qopt->er != priv->wbfs.er) { ++ err = qman_ceetm_channel_set_group_er_eligibility(channel, ++ group_b, ++ qopt->er); ++ if (err) ++ goto change_err; ++ priv->wbfs.er = qopt->er; ++ } ++ ++ return 0; ++ ++change_err: ++ pr_err(KBUILD_BASENAME " : %s : failed to configure the wbfs ceetm qdisc %X\n", ++ __func__, sch->handle); ++ return err; ++} ++ ++/* Edit a ceetm qdisc */ ++static int ceetm_change(struct Qdisc *sch, struct nlattr *opt) ++{ ++ struct tc_ceetm_qopt *qopt; ++ struct nlattr *tb[TCA_CEETM_QOPS + 1]; ++ int ret; ++ struct ceetm_qdisc *priv = qdisc_priv(sch); ++ struct net_device *dev = qdisc_dev(sch); ++ ++ pr_debug(KBUILD_BASENAME " : %s : qdisc %X\n", __func__, sch->handle); ++ ++ ret = nla_parse_nested(tb, TCA_CEETM_QOPS, opt, ceetm_policy); ++ if (ret < 0) { ++ pr_err(KBUILD_BASENAME " : %s : tc error\n", __func__); ++ return ret; ++ } ++ ++ if (!tb[TCA_CEETM_QOPS]) { ++ pr_err(KBUILD_BASENAME " : %s : tc error\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (TC_H_MIN(sch->handle)) { ++ pr_err("CEETM: a qdisc should not have a minor\n"); ++ return -EINVAL; ++ } ++ ++ qopt = nla_data(tb[TCA_CEETM_QOPS]); ++ ++ if (priv->type != qopt->type) { ++ pr_err("CEETM: qdisc %X is not of the provided type\n", ++ sch->handle); ++ return -EINVAL; ++ } ++ ++ switch (priv->type) { ++ case CEETM_ROOT: ++ ret = ceetm_change_root(sch, priv, dev, qopt); ++ break; ++ case CEETM_PRIO: ++ pr_err("CEETM: prio qdiscs can not be modified\n"); ++ ret = -EINVAL; ++ break; ++ case CEETM_WBFS: ++ ret = ceetm_change_wbfs(sch, priv, qopt); ++ break; ++ default: ++ pr_err(KBUILD_BASENAME " : %s : invalid qdisc\n", __func__); ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++/* Attach the underlying pfifo qdiscs */ ++static void ceetm_attach(struct Qdisc *sch) ++{ ++ struct net_device *dev = qdisc_dev(sch); ++ struct ceetm_qdisc *priv = qdisc_priv(sch); ++ struct Qdisc *qdisc, *old_qdisc; ++ unsigned int i; ++ ++ pr_debug(KBUILD_BASENAME " : %s : qdisc %X\n", __func__, sch->handle); ++ ++ for (i = 0; i < dev->num_tx_queues; i++) { ++ qdisc = priv->root.qdiscs[i]; ++ old_qdisc = dev_graft_qdisc(qdisc->dev_queue, qdisc); ++ if (old_qdisc) ++ qdisc_destroy(old_qdisc); ++ } ++} ++ ++static unsigned long ceetm_cls_get(struct Qdisc *sch, u32 classid) ++{ ++ struct ceetm_class *cl; ++ ++ pr_debug(KBUILD_BASENAME " : %s : classid %X from qdisc %X\n", ++ __func__, classid, sch->handle); ++ cl = ceetm_find(classid, sch); ++ ++ if (cl) ++ cl->refcnt++; /* Will decrement in put() */ ++ return (unsigned long)cl; ++} ++ ++static void ceetm_cls_put(struct Qdisc *sch, unsigned long arg) ++{ ++ struct ceetm_class *cl = (struct ceetm_class *)arg; ++ ++ pr_debug(KBUILD_BASENAME " : %s : classid %X from qdisc %X\n", ++ __func__, cl->common.classid, sch->handle); ++ cl->refcnt--; ++ ++ if (cl->refcnt == 0) ++ ceetm_cls_destroy(sch, cl); ++} ++ ++static int ceetm_cls_change_root(struct ceetm_class *cl, ++ struct tc_ceetm_copt *copt, ++ struct net_device *dev) ++{ ++ int err; ++ u64 bps; ++ ++ if ((bool)copt->shaped != cl->shaped) { ++ pr_err("CEETM: class %X is %s\n", cl->common.classid, ++ cl->shaped ? "shaped" : "unshaped"); ++ return -EINVAL; ++ } ++ ++ if (cl->shaped && cl->root.rate != copt->rate) { ++ bps = copt->rate << 3; /* Bps -> bps */ ++ err = qman_ceetm_channel_set_commit_rate_bps(cl->root.ch, bps, ++ dev->mtu); ++ if (err) ++ goto change_cls_err; ++ cl->root.rate = copt->rate; ++ } ++ ++ if (cl->shaped && cl->root.ceil != copt->ceil) { ++ bps = copt->ceil << 3; /* Bps -> bps */ ++ err = qman_ceetm_channel_set_excess_rate_bps(cl->root.ch, bps, ++ dev->mtu); ++ if (err) ++ goto change_cls_err; ++ cl->root.ceil = copt->ceil; ++ } ++ ++ if (!cl->shaped && cl->root.tbl != copt->tbl) { ++ err = qman_ceetm_channel_set_weight(cl->root.ch, copt->tbl); ++ if (err) ++ goto change_cls_err; ++ cl->root.tbl = copt->tbl; ++ } ++ ++ return 0; ++ ++change_cls_err: ++ pr_err(KBUILD_BASENAME " : %s : failed to configure the ceetm root class %X\n", ++ __func__, cl->common.classid); ++ return err; ++} ++ ++static int ceetm_cls_change_prio(struct ceetm_class *cl, ++ struct tc_ceetm_copt *copt) ++{ ++ int err; ++ ++ if (!cl->shaped && (copt->cr || copt->er)) { ++ pr_err("CEETM: only shaped classes can have CR and ER enabled\n"); ++ return -EINVAL; ++ } ++ ++ if (cl->prio.cr != (bool)copt->cr) { ++ err = qman_ceetm_channel_set_cq_cr_eligibility( ++ cl->prio.cq->parent, ++ cl->prio.cq->idx, ++ copt->cr); ++ if (err) ++ goto change_cls_err; ++ cl->prio.cr = copt->cr; ++ } ++ ++ if (cl->prio.er != (bool)copt->er) { ++ err = qman_ceetm_channel_set_cq_er_eligibility( ++ cl->prio.cq->parent, ++ cl->prio.cq->idx, ++ copt->er); ++ if (err) ++ goto change_cls_err; ++ cl->prio.er = copt->er; ++ } ++ ++ return 0; ++ ++change_cls_err: ++ pr_err(KBUILD_BASENAME " : %s : failed to configure the ceetm prio class %X\n", ++ __func__, cl->common.classid); ++ return err; ++} ++ ++static int ceetm_cls_change_wbfs(struct ceetm_class *cl, ++ struct tc_ceetm_copt *copt) ++{ ++ int err; ++ ++ if (copt->weight != cl->wbfs.weight) { ++ /* Configure the CQ weight: real number multiplied by 100 to ++ * get rid of the fraction ++ */ ++ err = qman_ceetm_set_queue_weight_in_ratio(cl->wbfs.cq, ++ copt->weight * 100); ++ ++ if (err) { ++ pr_err(KBUILD_BASENAME " : %s : failed to configure the ceetm wbfs class %X\n", ++ __func__, cl->common.classid); ++ return err; ++ } ++ ++ cl->wbfs.weight = copt->weight; ++ } ++ ++ return 0; ++} ++ ++/* Add a ceetm root class or configure a ceetm root/prio/wbfs class */ ++static int ceetm_cls_change(struct Qdisc *sch, u32 classid, u32 parentid, ++ struct nlattr **tca, unsigned long *arg) ++{ ++ int err; ++ u64 bps; ++ struct ceetm_qdisc *priv; ++ struct ceetm_class *cl = (struct ceetm_class *)*arg; ++ struct nlattr *opt = tca[TCA_OPTIONS]; ++ struct nlattr *tb[__TCA_CEETM_MAX]; ++ struct tc_ceetm_copt *copt; ++ struct qm_ceetm_channel *channel; ++ struct net_device *dev = qdisc_dev(sch); ++ ++ pr_debug(KBUILD_BASENAME " : %s : classid %X under qdisc %X\n", ++ __func__, classid, sch->handle); ++ ++ if (strcmp(sch->ops->id, ceetm_qdisc_ops.id)) { ++ pr_err("CEETM: a ceetm class can not be attached to other qdisc/class types\n"); ++ return -EINVAL; ++ } ++ ++ priv = qdisc_priv(sch); ++ ++ if (!opt) { ++ pr_err(KBUILD_BASENAME " : %s : tc error\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (!cl && sch->handle != parentid) { ++ pr_err("CEETM: classes can be attached to the root ceetm qdisc only\n"); ++ return -EINVAL; ++ } ++ ++ if (!cl && priv->type != CEETM_ROOT) { ++ pr_err("CEETM: only root ceetm classes can be attached to the root ceetm qdisc\n"); ++ return -EINVAL; ++ } ++ ++ err = nla_parse_nested(tb, TCA_CEETM_COPT, opt, ceetm_policy); ++ if (err < 0) { ++ pr_err(KBUILD_BASENAME " : %s : tc error\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (!tb[TCA_CEETM_COPT]) { ++ pr_err(KBUILD_BASENAME " : %s : tc error\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (TC_H_MIN(classid) >= PFIFO_MIN_OFFSET) { ++ pr_err("CEETM: only minors 0x01 to 0x20 can be used for ceetm root classes\n"); ++ return -EINVAL; ++ } ++ ++ copt = nla_data(tb[TCA_CEETM_COPT]); ++ ++ /* Configure an existing ceetm class */ ++ if (cl) { ++ if (copt->type != cl->type) { ++ pr_err("CEETM: class %X is not of the provided type\n", ++ cl->common.classid); ++ return -EINVAL; ++ } ++ ++ switch (copt->type) { ++ case CEETM_ROOT: ++ return ceetm_cls_change_root(cl, copt, dev); ++ ++ case CEETM_PRIO: ++ return ceetm_cls_change_prio(cl, copt); ++ ++ case CEETM_WBFS: ++ return ceetm_cls_change_wbfs(cl, copt); ++ ++ default: ++ pr_err(KBUILD_BASENAME " : %s : invalid class\n", ++ __func__); ++ return -EINVAL; ++ } ++ } ++ ++ /* Add a new root ceetm class */ ++ if (copt->type != CEETM_ROOT) { ++ pr_err("CEETM: only root ceetm classes can be attached to the root ceetm qdisc\n"); ++ return -EINVAL; ++ } ++ ++ if (copt->shaped && !priv->shaped) { ++ pr_err("CEETM: can not add a shaped ceetm root class under an unshaped ceetm root qdisc\n"); ++ return -EINVAL; ++ } ++ ++ cl = kzalloc(sizeof(*cl), GFP_KERNEL); ++ if (!cl) ++ return -ENOMEM; ++ ++ cl->type = copt->type; ++ cl->shaped = copt->shaped; ++ cl->root.rate = copt->rate; ++ cl->root.ceil = copt->ceil; ++ cl->root.tbl = copt->tbl; ++ ++ cl->common.classid = classid; ++ cl->refcnt = 1; ++ cl->parent = sch; ++ cl->root.child = NULL; ++ cl->root.wbfs_grp_a = false; ++ cl->root.wbfs_grp_b = false; ++ cl->root.wbfs_grp_large = false; ++ ++ /* Claim a CEETM channel */ ++ err = qman_ceetm_channel_claim(&channel, priv->root.lni); ++ if (err) { ++ pr_err(KBUILD_BASENAME " : %s : failed to claim a channel\n", ++ __func__); ++ goto claim_err; ++ } ++ ++ cl->root.ch = channel; ++ ++ if (cl->shaped) { ++ /* Configure the channel shaper */ ++ err = qman_ceetm_channel_enable_shaper(channel, 1); ++ if (err) ++ goto channel_err; ++ ++ bps = cl->root.rate << 3; /* Bps -> bps */ ++ err = qman_ceetm_channel_set_commit_rate_bps(channel, bps, ++ dev->mtu); ++ if (err) ++ goto channel_err; ++ ++ bps = cl->root.ceil << 3; /* Bps -> bps */ ++ err = qman_ceetm_channel_set_excess_rate_bps(channel, bps, ++ dev->mtu); ++ if (err) ++ goto channel_err; ++ ++ } else { ++ /* Configure the uFQ algorithm */ ++ err = qman_ceetm_channel_set_weight(channel, cl->root.tbl); ++ if (err) ++ goto channel_err; ++ } ++ ++ /* Add class handle in Qdisc */ ++ ceetm_link_class(sch, &priv->clhash, &cl->common); ++ ++ pr_debug(KBUILD_BASENAME " : %s : configured class %X associated with channel %d\n", ++ __func__, classid, channel->idx); ++ *arg = (unsigned long)cl; ++ return 0; ++ ++channel_err: ++ pr_err(KBUILD_BASENAME " : %s : failed to configure the channel %d\n", ++ __func__, channel->idx); ++ if (qman_ceetm_channel_release(channel)) ++ pr_err(KBUILD_BASENAME " : %s : failed to release the channel %d\n", ++ __func__, channel->idx); ++claim_err: ++ kfree(cl); ++ return err; ++} ++ ++static void ceetm_cls_walk(struct Qdisc *sch, struct qdisc_walker *arg) ++{ ++ struct ceetm_qdisc *priv = qdisc_priv(sch); ++ struct ceetm_class *cl; ++ unsigned int i; ++ ++ pr_debug(KBUILD_BASENAME " : %s : qdisc %X\n", __func__, sch->handle); ++ ++ if (arg->stop) ++ return; ++ ++ for (i = 0; i < priv->clhash.hashsize; i++) { ++ hlist_for_each_entry(cl, &priv->clhash.hash[i], common.hnode) { ++ if (arg->count < arg->skip) { ++ arg->count++; ++ continue; ++ } ++ if (arg->fn(sch, (unsigned long)cl, arg) < 0) { ++ arg->stop = 1; ++ return; ++ } ++ arg->count++; ++ } ++ } ++} ++ ++static int ceetm_cls_dump(struct Qdisc *sch, unsigned long arg, ++ struct sk_buff *skb, struct tcmsg *tcm) ++{ ++ struct ceetm_class *cl = (struct ceetm_class *)arg; ++ struct nlattr *nest; ++ struct tc_ceetm_copt copt; ++ ++ pr_debug(KBUILD_BASENAME " : %s : class %X under qdisc %X\n", ++ __func__, cl->common.classid, sch->handle); ++ ++ sch_tree_lock(sch); ++ ++ tcm->tcm_parent = ((struct Qdisc *)cl->parent)->handle; ++ tcm->tcm_handle = cl->common.classid; ++ ++ memset(&copt, 0, sizeof(copt)); ++ ++ copt.shaped = cl->shaped; ++ copt.type = cl->type; ++ ++ switch (cl->type) { ++ case CEETM_ROOT: ++ if (cl->root.child) ++ tcm->tcm_info = cl->root.child->handle; ++ ++ copt.rate = cl->root.rate; ++ copt.ceil = cl->root.ceil; ++ copt.tbl = cl->root.tbl; ++ break; ++ ++ case CEETM_PRIO: ++ if (cl->prio.child) ++ tcm->tcm_info = cl->prio.child->handle; ++ ++ copt.cr = cl->prio.cr; ++ copt.er = cl->prio.er; ++ break; ++ ++ case CEETM_WBFS: ++ copt.weight = cl->wbfs.weight; ++ break; ++ } ++ ++ nest = nla_nest_start(skb, TCA_OPTIONS); ++ if (!nest) ++ goto nla_put_failure; ++ if (nla_put(skb, TCA_CEETM_COPT, sizeof(copt), &copt)) ++ goto nla_put_failure; ++ nla_nest_end(skb, nest); ++ sch_tree_unlock(sch); ++ return skb->len; ++ ++nla_put_failure: ++ sch_tree_unlock(sch); ++ nla_nest_cancel(skb, nest); ++ return -EMSGSIZE; ++} ++ ++static int ceetm_cls_delete(struct Qdisc *sch, unsigned long arg) ++{ ++ struct ceetm_qdisc *priv = qdisc_priv(sch); ++ struct ceetm_class *cl = (struct ceetm_class *)arg; ++ ++ pr_debug(KBUILD_BASENAME " : %s : class %X under qdisc %X\n", ++ __func__, cl->common.classid, sch->handle); ++ ++ sch_tree_lock(sch); ++ qdisc_class_hash_remove(&priv->clhash, &cl->common); ++ cl->refcnt--; ++ ++ /* The refcnt should be at least 1 since we have incremented it in ++ * get(). Will decrement again in put() where we will call destroy() ++ * to actually free the memory if it reaches 0. ++ */ ++ WARN_ON(cl->refcnt == 0); ++ ++ sch_tree_unlock(sch); ++ return 0; ++} ++ ++/* Get the class' child qdisc, if any */ ++static struct Qdisc *ceetm_cls_leaf(struct Qdisc *sch, unsigned long arg) ++{ ++ struct ceetm_class *cl = (struct ceetm_class *)arg; ++ ++ pr_debug(KBUILD_BASENAME " : %s : class %X under qdisc %X\n", ++ __func__, cl->common.classid, sch->handle); ++ ++ switch (cl->type) { ++ case CEETM_ROOT: ++ return cl->root.child; ++ ++ case CEETM_PRIO: ++ return cl->prio.child; ++ } ++ ++ return NULL; ++} ++ ++static int ceetm_cls_graft(struct Qdisc *sch, unsigned long arg, ++ struct Qdisc *new, struct Qdisc **old) ++{ ++ if (new && strcmp(new->ops->id, ceetm_qdisc_ops.id)) { ++ pr_err("CEETM: only ceetm qdiscs can be attached to ceetm classes\n"); ++ return -EOPNOTSUPP; ++ } ++ ++ return 0; ++} ++ ++static int ceetm_cls_dump_stats(struct Qdisc *sch, unsigned long arg, ++ struct gnet_dump *d) ++{ ++ unsigned int i; ++ struct ceetm_class *cl = (struct ceetm_class *)arg; ++ struct gnet_stats_basic_packed tmp_bstats; ++ struct ceetm_class_stats *cstats = NULL; ++ struct qm_ceetm_cq *cq = NULL; ++ struct tc_ceetm_xstats xstats; ++ ++ memset(&xstats, 0, sizeof(xstats)); ++ memset(&tmp_bstats, 0, sizeof(tmp_bstats)); ++ ++ switch (cl->type) { ++ case CEETM_ROOT: ++ return 0; ++ case CEETM_PRIO: ++ cq = cl->prio.cq; ++ break; ++ case CEETM_WBFS: ++ cq = cl->wbfs.cq; ++ break; ++ } ++ ++ for_each_online_cpu(i) { ++ switch (cl->type) { ++ case CEETM_PRIO: ++ cstats = per_cpu_ptr(cl->prio.cstats, i); ++ break; ++ case CEETM_WBFS: ++ cstats = per_cpu_ptr(cl->wbfs.cstats, i); ++ break; ++ } ++ ++ if (cstats) { ++ xstats.ern_drop_count += cstats->ern_drop_count; ++ xstats.congested_count += cstats->congested_count; ++ tmp_bstats.bytes += cstats->bstats.bytes; ++ tmp_bstats.packets += cstats->bstats.packets; ++ } ++ } ++ ++ if (gnet_stats_copy_basic(d, NULL, &tmp_bstats) < 0) ++ return -1; ++ ++ if (cq && qman_ceetm_cq_get_dequeue_statistics(cq, 0, ++ &xstats.frame_count, ++ &xstats.byte_count)) ++ return -1; ++ ++ return gnet_stats_copy_app(d, &xstats, sizeof(xstats)); ++} ++ ++static struct tcf_proto **ceetm_tcf_chain(struct Qdisc *sch, unsigned long arg) ++{ ++ struct ceetm_qdisc *priv = qdisc_priv(sch); ++ struct ceetm_class *cl = (struct ceetm_class *)arg; ++ struct tcf_proto **fl = cl ? &cl->filter_list : &priv->filter_list; ++ ++ pr_debug(KBUILD_BASENAME " : %s : class %X under qdisc %X\n", __func__, ++ cl ? cl->common.classid : 0, sch->handle); ++ return fl; ++} ++ ++static unsigned long ceetm_tcf_bind(struct Qdisc *sch, unsigned long parent, ++ u32 classid) ++{ ++ struct ceetm_class *cl = ceetm_find(classid, sch); ++ ++ pr_debug(KBUILD_BASENAME " : %s : class %X under qdisc %X\n", __func__, ++ cl ? cl->common.classid : 0, sch->handle); ++ return (unsigned long)cl; ++} ++ ++static void ceetm_tcf_unbind(struct Qdisc *sch, unsigned long arg) ++{ ++ struct ceetm_class *cl = (struct ceetm_class *)arg; ++ ++ pr_debug(KBUILD_BASENAME " : %s : class %X under qdisc %X\n", __func__, ++ cl ? cl->common.classid : 0, sch->handle); ++} ++ ++const struct Qdisc_class_ops ceetm_cls_ops = { ++ .graft = ceetm_cls_graft, ++ .leaf = ceetm_cls_leaf, ++ .get = ceetm_cls_get, ++ .put = ceetm_cls_put, ++ .change = ceetm_cls_change, ++ .delete = ceetm_cls_delete, ++ .walk = ceetm_cls_walk, ++ .tcf_chain = ceetm_tcf_chain, ++ .bind_tcf = ceetm_tcf_bind, ++ .unbind_tcf = ceetm_tcf_unbind, ++ .dump = ceetm_cls_dump, ++ .dump_stats = ceetm_cls_dump_stats, ++}; ++ ++struct Qdisc_ops ceetm_qdisc_ops __read_mostly = { ++ .id = "ceetm", ++ .priv_size = sizeof(struct ceetm_qdisc), ++ .cl_ops = &ceetm_cls_ops, ++ .init = ceetm_init, ++ .destroy = ceetm_destroy, ++ .change = ceetm_change, ++ .dump = ceetm_dump, ++ .attach = ceetm_attach, ++ .owner = THIS_MODULE, ++}; ++ ++/* Run the filters and classifiers attached to the qdisc on the provided skb */ ++static struct ceetm_class *ceetm_classify(struct sk_buff *skb, ++ struct Qdisc *sch, int *qerr, ++ bool *act_drop) ++{ ++ struct ceetm_qdisc *priv = qdisc_priv(sch); ++ struct ceetm_class *cl = NULL, *wbfs_cl; ++ struct tcf_result res; ++ struct tcf_proto *tcf; ++ int result; ++ ++ *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; ++ tcf = priv->filter_list; ++ while (tcf && (result = tc_classify(skb, tcf, &res)) >= 0) { ++#ifdef CONFIG_NET_CLS_ACT ++ switch (result) { ++ case TC_ACT_QUEUED: ++ case TC_ACT_STOLEN: ++ *qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN; ++ case TC_ACT_SHOT: ++ /* No valid class found due to action */ ++ *act_drop = true; ++ return NULL; ++ } ++#endif ++ cl = (void *)res.class; ++ if (!cl) { ++ if (res.classid == sch->handle) { ++ /* The filter leads to the qdisc */ ++ /* TODO default qdisc */ ++ return NULL; ++ } ++ ++ cl = ceetm_find(res.classid, sch); ++ if (!cl) ++ /* The filter leads to an invalid class */ ++ break; ++ } ++ ++ /* The class might have its own filters attached */ ++ tcf = cl->filter_list; ++ } ++ ++ if (!cl) { ++ /* No valid class found */ ++ /* TODO default qdisc */ ++ return NULL; ++ } ++ ++ switch (cl->type) { ++ case CEETM_ROOT: ++ if (cl->root.child) { ++ /* Run the prio qdisc classifiers */ ++ return ceetm_classify(skb, cl->root.child, qerr, ++ act_drop); ++ } else { ++ /* The root class does not have a child prio qdisc */ ++ /* TODO default qdisc */ ++ return NULL; ++ } ++ case CEETM_PRIO: ++ if (cl->prio.child) { ++ /* If filters lead to a wbfs class, return it. ++ * Otherwise, return the prio class ++ */ ++ wbfs_cl = ceetm_classify(skb, cl->prio.child, qerr, ++ act_drop); ++ /* A NULL result might indicate either an erroneous ++ * filter, or no filters at all. We will assume the ++ * latter ++ */ ++ return wbfs_cl ? : cl; ++ } ++ } ++ ++ /* For wbfs and childless prio classes, return the class directly */ ++ return cl; ++} ++ ++int __hot ceetm_tx(struct sk_buff *skb, struct net_device *net_dev) ++{ ++ int ret; ++ bool act_drop = false; ++ struct Qdisc *sch = net_dev->qdisc; ++ struct ceetm_class *cl; ++ struct dpa_priv_s *priv_dpa; ++ struct qman_fq *egress_fq, *conf_fq; ++ struct ceetm_qdisc *priv = qdisc_priv(sch); ++ struct ceetm_qdisc_stats *qstats = this_cpu_ptr(priv->root.qstats); ++ struct ceetm_class_stats *cstats; ++ const int queue_mapping = dpa_get_queue_mapping(skb); ++ spinlock_t *root_lock = qdisc_lock(sch); ++ ++ spin_lock(root_lock); ++ cl = ceetm_classify(skb, sch, &ret, &act_drop); ++ spin_unlock(root_lock); ++ ++#ifdef CONFIG_NET_CLS_ACT ++ if (act_drop) { ++ if (ret & __NET_XMIT_BYPASS) ++ qstats->drops++; ++ goto drop; ++ } ++#endif ++ /* TODO default class */ ++ if (unlikely(!cl)) { ++ qstats->drops++; ++ goto drop; ++ } ++ ++ priv_dpa = netdev_priv(net_dev); ++ conf_fq = priv_dpa->conf_fqs[queue_mapping]; ++ ++ /* Choose the proper tx fq and update the basic stats (bytes and ++ * packets sent by the class) ++ */ ++ switch (cl->type) { ++ case CEETM_PRIO: ++ egress_fq = &cl->prio.fq->fq; ++ cstats = this_cpu_ptr(cl->prio.cstats); ++ break; ++ case CEETM_WBFS: ++ egress_fq = &cl->wbfs.fq->fq; ++ cstats = this_cpu_ptr(cl->wbfs.cstats); ++ break; ++ default: ++ qstats->drops++; ++ goto drop; ++ } ++ ++ bstats_update(&cstats->bstats, skb); ++ return dpa_tx_extended(skb, net_dev, egress_fq, conf_fq); ++ ++drop: ++ dev_kfree_skb_any(skb); ++ return NET_XMIT_SUCCESS; ++} ++ ++static int __init ceetm_register(void) ++{ ++ int _errno = 0; ++ ++ pr_info(KBUILD_MODNAME ": " DPA_CEETM_DESCRIPTION "\n"); ++ ++ _errno = register_qdisc(&ceetm_qdisc_ops); ++ if (unlikely(_errno)) ++ pr_err(KBUILD_MODNAME ++ ": %s:%hu:%s(): register_qdisc() = %d\n", ++ KBUILD_BASENAME ".c", __LINE__, __func__, _errno); ++ ++ return _errno; ++} ++ ++static void __exit ceetm_unregister(void) ++{ ++ pr_debug(KBUILD_MODNAME ": %s:%s() ->\n", ++ KBUILD_BASENAME ".c", __func__); ++ ++ unregister_qdisc(&ceetm_qdisc_ops); ++} ++ ++module_init(ceetm_register); ++module_exit(ceetm_unregister); +--- /dev/null ++++ b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_ceetm.h +@@ -0,0 +1,236 @@ ++/* Copyright 2008-2016 Freescale Semiconductor Inc. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * * Neither the name of Freescale Semiconductor nor the ++ * names of its contributors may be used to endorse or promote products ++ * derived from this software without specific prior written permission. ++ * ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") as published by the Free Software ++ * Foundation, either version 2 of that License or (at your option) any ++ * later version. ++ * ++ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. ++ */ ++ ++#ifndef __DPAA_ETH_CEETM_H ++#define __DPAA_ETH_CEETM_H ++ ++#include <net/pkt_sched.h> ++#include <net/netlink.h> ++#include <lnxwrp_fm.h> ++ ++#include "mac.h" ++#include "dpaa_eth_common.h" ++ ++/* Mask to determine the sub-portal id from a channel number */ ++#define CHANNEL_SP_MASK 0x1f ++/* The number of the last channel that services DCP0, connected to FMan 0. ++ * Value validated for B4 and T series platforms. ++ */ ++#define DCP0_MAX_CHANNEL 0x80f ++/* A2V=1 - field A2 is valid ++ * A0V=1 - field A0 is valid - enables frame confirmation ++ * OVOM=1 - override operation mode bits with values from A2 ++ * EBD=1 - external buffers are deallocated at the end of the FMan flow ++ * NL=0 - the BMI releases all the internal buffers ++ */ ++#define CEETM_CONTEXT_A 0x1a00000080000000 ++/* The ratio between the superior and inferior congestion state thresholds. The ++ * lower threshold is set to 7/8 of the superior one (as the default for WQ ++ * scheduling). ++ */ ++#define CEETM_CCGR_RATIO 0.875 ++/* For functional purposes, there are num_tx_queues pfifo qdiscs through which ++ * frames reach the driver. Their handles start from 1:21. Handles 1:1 to 1:20 ++ * are reserved for the maximum 32 CEETM channels (majors and minors are in ++ * hex). ++ */ ++#define PFIFO_MIN_OFFSET 0x21 ++ ++/* A maximum of 8 CQs can be linked to a CQ channel or to a WBFS scheduler. */ ++#define CEETM_MAX_PRIO_QCOUNT 8 ++#define CEETM_MAX_WBFS_QCOUNT 8 ++#define CEETM_MIN_WBFS_QCOUNT 4 ++ ++/* The id offsets of the CQs belonging to WBFS groups (ids 8-11/15 for group A ++ * and/or 12-15 for group B). ++ */ ++#define WBFS_GRP_A_OFFSET 8 ++#define WBFS_GRP_B_OFFSET 12 ++ ++#define WBFS_GRP_A 1 ++#define WBFS_GRP_B 2 ++#define WBFS_GRP_LARGE 3 ++ ++enum { ++ TCA_CEETM_UNSPEC, ++ TCA_CEETM_COPT, ++ TCA_CEETM_QOPS, ++ __TCA_CEETM_MAX, ++}; ++ ++/* CEETM configuration types */ ++enum { ++ CEETM_ROOT = 1, ++ CEETM_PRIO, ++ CEETM_WBFS ++}; ++ ++#define TCA_CEETM_MAX (__TCA_CEETM_MAX - 1) ++extern const struct nla_policy ceetm_policy[TCA_CEETM_MAX + 1]; ++ ++struct ceetm_class; ++struct ceetm_qdisc_stats; ++struct ceetm_class_stats; ++ ++struct ceetm_fq { ++ struct qman_fq fq; ++ struct net_device *net_dev; ++ struct ceetm_class *ceetm_cls; ++}; ++ ++struct root_q { ++ struct Qdisc **qdiscs; ++ __u16 overhead; ++ __u32 rate; ++ __u32 ceil; ++ struct qm_ceetm_sp *sp; ++ struct qm_ceetm_lni *lni; ++ struct ceetm_qdisc_stats __percpu *qstats; ++}; ++ ++struct prio_q { ++ __u16 qcount; ++ struct ceetm_class *parent; ++}; ++ ++struct wbfs_q { ++ __u16 qcount; ++ int group_type; ++ struct ceetm_class *parent; ++ __u16 cr; ++ __u16 er; ++}; ++ ++struct ceetm_qdisc { ++ int type; /* LNI/CHNL/WBFS */ ++ bool shaped; ++ union { ++ struct root_q root; ++ struct prio_q prio; ++ struct wbfs_q wbfs; ++ }; ++ struct Qdisc_class_hash clhash; ++ struct tcf_proto *filter_list; /* qdisc attached filters */ ++}; ++ ++/* CEETM Qdisc configuration parameters */ ++struct tc_ceetm_qopt { ++ __u32 type; ++ __u16 shaped; ++ __u16 qcount; ++ __u16 overhead; ++ __u32 rate; ++ __u32 ceil; ++ __u16 cr; ++ __u16 er; ++ __u8 qweight[CEETM_MAX_WBFS_QCOUNT]; ++}; ++ ++struct root_c { ++ unsigned int rate; ++ unsigned int ceil; ++ unsigned int tbl; ++ bool wbfs_grp_a; ++ bool wbfs_grp_b; ++ bool wbfs_grp_large; ++ struct Qdisc *child; ++ struct qm_ceetm_channel *ch; ++}; ++ ++struct prio_c { ++ bool cr; ++ bool er; ++ struct ceetm_fq *fq; /* Hardware FQ instance Handle */ ++ struct qm_ceetm_lfq *lfq; ++ struct qm_ceetm_cq *cq; /* Hardware Class Queue instance Handle */ ++ struct qm_ceetm_ccg *ccg; ++ /* only one wbfs can be linked to one priority CQ */ ++ struct Qdisc *child; ++ struct ceetm_class_stats __percpu *cstats; ++}; ++ ++struct wbfs_c { ++ __u8 weight; /* The weight of the class between 1 and 248 */ ++ struct ceetm_fq *fq; /* Hardware FQ instance Handle */ ++ struct qm_ceetm_lfq *lfq; ++ struct qm_ceetm_cq *cq; /* Hardware Class Queue instance Handle */ ++ struct qm_ceetm_ccg *ccg; ++ struct ceetm_class_stats __percpu *cstats; ++}; ++ ++struct ceetm_class { ++ struct Qdisc_class_common common; ++ int refcnt; /* usage count of this class */ ++ struct tcf_proto *filter_list; /* class attached filters */ ++ struct Qdisc *parent; ++ bool shaped; ++ int type; /* ROOT/PRIO/WBFS */ ++ union { ++ struct root_c root; ++ struct prio_c prio; ++ struct wbfs_c wbfs; ++ }; ++}; ++ ++/* CEETM Class configuration parameters */ ++struct tc_ceetm_copt { ++ __u32 type; ++ __u16 shaped; ++ __u32 rate; ++ __u32 ceil; ++ __u16 tbl; ++ __u16 cr; ++ __u16 er; ++ __u8 weight; ++}; ++ ++/* CEETM stats */ ++struct ceetm_qdisc_stats { ++ __u32 drops; ++}; ++ ++struct ceetm_class_stats { ++ /* Software counters */ ++ struct gnet_stats_basic_packed bstats; ++ __u32 ern_drop_count; ++ __u32 congested_count; ++}; ++ ++struct tc_ceetm_xstats { ++ __u32 ern_drop_count; ++ __u32 congested_count; ++ /* Hardware counters */ ++ __u64 frame_count; ++ __u64 byte_count; ++}; ++ ++int __hot ceetm_tx(struct sk_buff *skb, struct net_device *net_dev); ++#endif +--- /dev/null ++++ b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_common.c +@@ -0,0 +1,1812 @@ ++/* Copyright 2008-2013 Freescale Semiconductor, Inc. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * * Neither the name of Freescale Semiconductor nor the ++ * names of its contributors may be used to endorse or promote products ++ * derived from this software without specific prior written permission. ++ * ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") as published by the Free Software ++ * Foundation, either version 2 of that License or (at your option) any ++ * later version. ++ * ++ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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/init.h> ++#include <linux/module.h> ++#include <linux/of_platform.h> ++#include <linux/of_net.h> ++#include <linux/etherdevice.h> ++#include <linux/kthread.h> ++#include <linux/percpu.h> ++#include <linux/highmem.h> ++#include <linux/sort.h> ++#include <linux/fsl_qman.h> ++#include <linux/ip.h> ++#include <linux/ipv6.h> ++#include <linux/if_vlan.h> /* vlan_eth_hdr */ ++#include "dpaa_eth.h" ++#include "dpaa_eth_common.h" ++#ifdef CONFIG_FSL_DPAA_1588 ++#include "dpaa_1588.h" ++#endif ++#ifdef CONFIG_FSL_DPAA_DBG_LOOP ++#include "dpaa_debugfs.h" ++#endif /* CONFIG_FSL_DPAA_DBG_LOOP */ ++#include "mac.h" ++ ++/* Size in bytes of the FQ taildrop threshold */ ++#define DPA_FQ_TD 0x200000 ++ ++#ifdef CONFIG_PTP_1588_CLOCK_DPAA ++struct ptp_priv_s ptp_priv; ++#endif ++ ++static struct dpa_bp *dpa_bp_array[64]; ++ ++int dpa_max_frm; ++EXPORT_SYMBOL(dpa_max_frm); ++ ++int dpa_rx_extra_headroom; ++EXPORT_SYMBOL(dpa_rx_extra_headroom); ++ ++int dpa_num_cpus = NR_CPUS; ++ ++static const struct fqid_cell tx_confirm_fqids[] = { ++ {0, DPAA_ETH_TX_QUEUES} ++}; ++ ++static struct fqid_cell default_fqids[][3] = { ++ [RX] = { {0, 1}, {0, 1}, {0, DPAA_ETH_RX_QUEUES} }, ++ [TX] = { {0, 1}, {0, 1}, {0, DPAA_ETH_TX_QUEUES} } ++}; ++ ++static const char fsl_qman_frame_queues[][25] = { ++ [RX] = "fsl,qman-frame-queues-rx", ++ [TX] = "fsl,qman-frame-queues-tx" ++}; ++#ifdef CONFIG_FSL_DPAA_HOOKS ++/* A set of callbacks for hooking into the fastpath at different points. */ ++struct dpaa_eth_hooks_s dpaa_eth_hooks; ++EXPORT_SYMBOL(dpaa_eth_hooks); ++/* This function should only be called on the probe paths, since it makes no ++ * effort to guarantee consistency of the destination hooks structure. ++ */ ++void fsl_dpaa_eth_set_hooks(struct dpaa_eth_hooks_s *hooks) ++{ ++ if (hooks) ++ dpaa_eth_hooks = *hooks; ++ else ++ pr_err("NULL pointer to hooks!\n"); ++} ++EXPORT_SYMBOL(fsl_dpaa_eth_set_hooks); ++#endif ++ ++int dpa_netdev_init(struct net_device *net_dev, ++ const uint8_t *mac_addr, ++ uint16_t tx_timeout) ++{ ++ int err; ++ struct dpa_priv_s *priv = netdev_priv(net_dev); ++ struct device *dev = net_dev->dev.parent; ++ ++ net_dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; ++ ++ net_dev->features |= net_dev->hw_features; ++ net_dev->vlan_features = net_dev->features; ++ ++ memcpy(net_dev->perm_addr, mac_addr, net_dev->addr_len); ++ memcpy(net_dev->dev_addr, mac_addr, net_dev->addr_len); ++ ++ net_dev->ethtool_ops = &dpa_ethtool_ops; ++ ++ net_dev->needed_headroom = priv->tx_headroom; ++ net_dev->watchdog_timeo = msecs_to_jiffies(tx_timeout); ++ ++ err = register_netdev(net_dev); ++ if (err < 0) { ++ dev_err(dev, "register_netdev() = %d\n", err); ++ return err; ++ } ++ ++#ifdef CONFIG_FSL_DPAA_DBG_LOOP ++ /* create debugfs entry for this net_device */ ++ err = dpa_netdev_debugfs_create(net_dev); ++ if (err) { ++ unregister_netdev(net_dev); ++ return err; ++ } ++#endif /* CONFIG_FSL_DPAA_DBG_LOOP */ ++ ++ return 0; ++} ++EXPORT_SYMBOL(dpa_netdev_init); ++ ++int __cold dpa_start(struct net_device *net_dev) ++{ ++ int err, i; ++ struct dpa_priv_s *priv; ++ struct mac_device *mac_dev; ++ ++ priv = netdev_priv(net_dev); ++ mac_dev = priv->mac_dev; ++ ++ err = mac_dev->init_phy(net_dev, priv->mac_dev); ++ if (err < 0) { ++ if (netif_msg_ifup(priv)) ++ netdev_err(net_dev, "init_phy() = %d\n", err); ++ return err; ++ } ++ ++ for_each_port_device(i, mac_dev->port_dev) { ++ err = fm_port_enable(mac_dev->port_dev[i]); ++ if (err) ++ goto mac_start_failed; ++ } ++ ++ err = priv->mac_dev->start(mac_dev); ++ if (err < 0) { ++ if (netif_msg_ifup(priv)) ++ netdev_err(net_dev, "mac_dev->start() = %d\n", err); ++ goto mac_start_failed; ++ } ++ ++ netif_tx_start_all_queues(net_dev); ++ ++ return 0; ++ ++mac_start_failed: ++ for_each_port_device(i, mac_dev->port_dev) ++ fm_port_disable(mac_dev->port_dev[i]); ++ ++ return err; ++} ++EXPORT_SYMBOL(dpa_start); ++ ++int __cold dpa_stop(struct net_device *net_dev) ++{ ++ int _errno, i, err; ++ struct dpa_priv_s *priv; ++ struct mac_device *mac_dev; ++ ++ priv = netdev_priv(net_dev); ++ mac_dev = priv->mac_dev; ++ ++ netif_tx_stop_all_queues(net_dev); ++ /* Allow the Fman (Tx) port to process in-flight frames before we ++ * try switching it off. ++ */ ++ usleep_range(5000, 10000); ++ ++ _errno = mac_dev->stop(mac_dev); ++ if (unlikely(_errno < 0)) ++ if (netif_msg_ifdown(priv)) ++ netdev_err(net_dev, "mac_dev->stop() = %d\n", ++ _errno); ++ ++ for_each_port_device(i, mac_dev->port_dev) { ++ err = fm_port_disable(mac_dev->port_dev[i]); ++ _errno = err ? err : _errno; ++ } ++ ++ if (mac_dev->phy_dev) ++ phy_disconnect(mac_dev->phy_dev); ++ mac_dev->phy_dev = NULL; ++ ++ return _errno; ++} ++EXPORT_SYMBOL(dpa_stop); ++ ++void __cold dpa_timeout(struct net_device *net_dev) ++{ ++ const struct dpa_priv_s *priv; ++ struct dpa_percpu_priv_s *percpu_priv; ++ ++ priv = netdev_priv(net_dev); ++ percpu_priv = raw_cpu_ptr(priv->percpu_priv); ++ ++ if (netif_msg_timer(priv)) ++ netdev_crit(net_dev, "Transmit timeout!\n"); ++ ++ percpu_priv->stats.tx_errors++; ++} ++EXPORT_SYMBOL(dpa_timeout); ++ ++/* net_device */ ++ ++/** ++ * @param net_dev the device for which statistics are calculated ++ * @param stats the function fills this structure with the device's statistics ++ * @return the address of the structure containing the statistics ++ * ++ * Calculates the statistics for the given device by adding the statistics ++ * collected by each CPU. ++ */ ++void __cold ++dpa_get_stats64(struct net_device *net_dev, ++ struct rtnl_link_stats64 *stats) ++{ ++ struct dpa_priv_s *priv = netdev_priv(net_dev); ++ u64 *cpustats; ++ u64 *netstats = (u64 *)stats; ++ int i, j; ++ struct dpa_percpu_priv_s *percpu_priv; ++ int numstats = sizeof(struct rtnl_link_stats64) / sizeof(u64); ++ ++ for_each_possible_cpu(i) { ++ percpu_priv = per_cpu_ptr(priv->percpu_priv, i); ++ ++ cpustats = (u64 *)&percpu_priv->stats; ++ ++ for (j = 0; j < numstats; j++) ++ netstats[j] += cpustats[j]; ++ } ++} ++EXPORT_SYMBOL(dpa_get_stats64); ++ ++int dpa_change_mtu(struct net_device *net_dev, int new_mtu) ++{ ++ const int max_mtu = dpa_get_max_mtu(); ++ ++ /* Make sure we don't exceed the Ethernet controller's MAXFRM */ ++ if (new_mtu < 68 || new_mtu > max_mtu) { ++ netdev_err(net_dev, "Invalid L3 mtu %d (must be between %d and %d).\n", ++ new_mtu, 68, max_mtu); ++ return -EINVAL; ++ } ++ net_dev->mtu = new_mtu; ++ ++ return 0; ++} ++EXPORT_SYMBOL(dpa_change_mtu); ++ ++/* .ndo_init callback */ ++int dpa_ndo_init(struct net_device *net_dev) ++{ ++ /* If fsl_fm_max_frm is set to a higher value than the all-common 1500, ++ * we choose conservatively and let the user explicitly set a higher ++ * MTU via ifconfig. Otherwise, the user may end up with different MTUs ++ * in the same LAN. ++ * If on the other hand fsl_fm_max_frm has been chosen below 1500, ++ * start with the maximum allowed. ++ */ ++ int init_mtu = min(dpa_get_max_mtu(), ETH_DATA_LEN); ++ ++ pr_debug("Setting initial MTU on net device: %d\n", init_mtu); ++ net_dev->mtu = init_mtu; ++ ++ return 0; ++} ++EXPORT_SYMBOL(dpa_ndo_init); ++ ++int dpa_set_features(struct net_device *dev, netdev_features_t features) ++{ ++ /* Not much to do here for now */ ++ dev->features = features; ++ return 0; ++} ++EXPORT_SYMBOL(dpa_set_features); ++ ++netdev_features_t dpa_fix_features(struct net_device *dev, ++ netdev_features_t features) ++{ ++ netdev_features_t unsupported_features = 0; ++ ++ /* In theory we should never be requested to enable features that ++ * we didn't set in netdev->features and netdev->hw_features at probe ++ * time, but double check just to be on the safe side. ++ * We don't support enabling Rx csum through ethtool yet ++ */ ++ unsupported_features |= NETIF_F_RXCSUM; ++ ++ features &= ~unsupported_features; ++ ++ return features; ++} ++EXPORT_SYMBOL(dpa_fix_features); ++ ++#ifdef CONFIG_FSL_DPAA_TS ++u64 dpa_get_timestamp_ns(const struct dpa_priv_s *priv, enum port_type rx_tx, ++ const void *data) ++{ ++ u64 *ts, ns; ++ ++ ts = fm_port_get_buffer_time_stamp(priv->mac_dev->port_dev[rx_tx], ++ data); ++ ++ if (!ts || *ts == 0) ++ return 0; ++ ++ be64_to_cpus(ts); ++ ++ /* multiple DPA_PTP_NOMINAL_FREQ_PERIOD_NS for case of non power of 2 */ ++ ns = *ts << DPA_PTP_NOMINAL_FREQ_PERIOD_SHIFT; ++ ++ return ns; ++} ++ ++int dpa_get_ts(const struct dpa_priv_s *priv, enum port_type rx_tx, ++ struct skb_shared_hwtstamps *shhwtstamps, const void *data) ++{ ++ u64 ns; ++ ++ ns = dpa_get_timestamp_ns(priv, rx_tx, data); ++ ++ if (ns == 0) ++ return -EINVAL; ++ ++ memset(shhwtstamps, 0, sizeof(*shhwtstamps)); ++ shhwtstamps->hwtstamp = ns_to_ktime(ns); ++ ++ return 0; ++} ++ ++static void dpa_ts_tx_enable(struct net_device *dev) ++{ ++ struct dpa_priv_s *priv = netdev_priv(dev); ++ struct mac_device *mac_dev = priv->mac_dev; ++ ++ if (mac_dev->fm_rtc_enable) ++ mac_dev->fm_rtc_enable(get_fm_handle(dev)); ++ if (mac_dev->ptp_enable) ++ mac_dev->ptp_enable(mac_dev->get_mac_handle(mac_dev)); ++ ++ priv->ts_tx_en = true; ++} ++ ++static void dpa_ts_tx_disable(struct net_device *dev) ++{ ++ struct dpa_priv_s *priv = netdev_priv(dev); ++ ++#if 0 ++/* the RTC might be needed by the Rx Ts, cannot disable here ++ * no separate ptp_disable API for Rx/Tx, cannot disable here ++ */ ++ struct mac_device *mac_dev = priv->mac_dev; ++ ++ if (mac_dev->fm_rtc_disable) ++ mac_dev->fm_rtc_disable(get_fm_handle(dev)); ++ ++ if (mac_dev->ptp_disable) ++ mac_dev->ptp_disable(mac_dev->get_mac_handle(mac_dev)); ++#endif ++ ++ priv->ts_tx_en = false; ++} ++ ++static void dpa_ts_rx_enable(struct net_device *dev) ++{ ++ struct dpa_priv_s *priv = netdev_priv(dev); ++ struct mac_device *mac_dev = priv->mac_dev; ++ ++ if (mac_dev->fm_rtc_enable) ++ mac_dev->fm_rtc_enable(get_fm_handle(dev)); ++ if (mac_dev->ptp_enable) ++ mac_dev->ptp_enable(mac_dev->get_mac_handle(mac_dev)); ++ ++ priv->ts_rx_en = true; ++} ++ ++static void dpa_ts_rx_disable(struct net_device *dev) ++{ ++ struct dpa_priv_s *priv = netdev_priv(dev); ++ ++#if 0 ++/* the RTC might be needed by the Tx Ts, cannot disable here ++ * no separate ptp_disable API for Rx/Tx, cannot disable here ++ */ ++ struct mac_device *mac_dev = priv->mac_dev; ++ ++ if (mac_dev->fm_rtc_disable) ++ mac_dev->fm_rtc_disable(get_fm_handle(dev)); ++ ++ if (mac_dev->ptp_disable) ++ mac_dev->ptp_disable(mac_dev->get_mac_handle(mac_dev)); ++#endif ++ ++ priv->ts_rx_en = false; ++} ++ ++static int dpa_ts_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) ++{ ++ struct hwtstamp_config config; ++ ++ if (copy_from_user(&config, rq->ifr_data, sizeof(config))) ++ return -EFAULT; ++ ++ switch (config.tx_type) { ++ case HWTSTAMP_TX_OFF: ++ dpa_ts_tx_disable(dev); ++ break; ++ case HWTSTAMP_TX_ON: ++ dpa_ts_tx_enable(dev); ++ break; ++ default: ++ return -ERANGE; ++ } ++ ++ if (config.rx_filter == HWTSTAMP_FILTER_NONE) ++ dpa_ts_rx_disable(dev); ++ else { ++ dpa_ts_rx_enable(dev); ++ /* TS is set for all frame types, not only those requested */ ++ config.rx_filter = HWTSTAMP_FILTER_ALL; ++ } ++ ++ return copy_to_user(rq->ifr_data, &config, sizeof(config)) ? ++ -EFAULT : 0; ++} ++#endif /* CONFIG_FSL_DPAA_TS */ ++ ++int dpa_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) ++{ ++#ifdef CONFIG_FSL_DPAA_1588 ++ struct dpa_priv_s *priv = netdev_priv(dev); ++#endif ++ int ret = 0; ++ ++ /* at least one timestamping feature must be enabled */ ++#ifdef CONFIG_FSL_DPAA_TS ++ if (!netif_running(dev)) ++#endif ++ return -EINVAL; ++ ++#ifdef CONFIG_FSL_DPAA_TS ++ if (cmd == SIOCSHWTSTAMP) ++ return dpa_ts_ioctl(dev, rq, cmd); ++#endif /* CONFIG_FSL_DPAA_TS */ ++ ++#ifdef CONFIG_FSL_DPAA_1588 ++ if ((cmd >= PTP_ENBL_TXTS_IOCTL) && (cmd <= PTP_CLEANUP_TS)) { ++ if (priv->tsu && priv->tsu->valid) ++ ret = dpa_ioctl_1588(dev, rq, cmd); ++ else ++ ret = -ENODEV; ++ } ++#endif ++ ++ return ret; ++} ++EXPORT_SYMBOL(dpa_ioctl); ++ ++int __cold dpa_remove(struct platform_device *of_dev) ++{ ++ int err; ++ struct device *dev; ++ struct net_device *net_dev; ++ struct dpa_priv_s *priv; ++ ++ dev = &of_dev->dev; ++ net_dev = dev_get_drvdata(dev); ++ ++ priv = netdev_priv(net_dev); ++ ++ dpaa_eth_sysfs_remove(dev); ++ ++ dev_set_drvdata(dev, NULL); ++ unregister_netdev(net_dev); ++ ++ err = dpa_fq_free(dev, &priv->dpa_fq_list); ++ ++ qman_delete_cgr_safe(&priv->ingress_cgr); ++ qman_release_cgrid(priv->ingress_cgr.cgrid); ++ qman_delete_cgr_safe(&priv->cgr_data.cgr); ++ qman_release_cgrid(priv->cgr_data.cgr.cgrid); ++ ++ dpa_private_napi_del(net_dev); ++ ++ dpa_bp_free(priv); ++ ++ if (priv->buf_layout) ++ devm_kfree(dev, priv->buf_layout); ++ ++#ifdef CONFIG_FSL_DPAA_DBG_LOOP ++ /* remove debugfs entry for this net_device */ ++ dpa_netdev_debugfs_remove(net_dev); ++#endif /* CONFIG_FSL_DPAA_DBG_LOOP */ ++ ++#ifdef CONFIG_FSL_DPAA_1588 ++ if (priv->tsu && priv->tsu->valid) ++ dpa_ptp_cleanup(priv); ++#endif ++ ++ free_netdev(net_dev); ++ ++ return err; ++} ++EXPORT_SYMBOL(dpa_remove); ++ ++struct mac_device * __cold __must_check ++__attribute__((nonnull)) ++dpa_mac_probe(struct platform_device *_of_dev) ++{ ++ struct device *dpa_dev, *dev; ++ struct device_node *mac_node; ++ struct platform_device *of_dev; ++ struct mac_device *mac_dev; ++#ifdef CONFIG_FSL_DPAA_1588 ++ int lenp; ++ const phandle *phandle_prop; ++ struct net_device *net_dev = NULL; ++ struct dpa_priv_s *priv = NULL; ++ struct device_node *timer_node; ++#endif ++ dpa_dev = &_of_dev->dev; ++ ++ mac_node = of_parse_phandle(_of_dev->dev.of_node, "fsl,fman-mac", 0); ++ if (unlikely(mac_node == NULL)) { ++ dev_err(dpa_dev, "Cannot find MAC device device tree node\n"); ++ return ERR_PTR(-EFAULT); ++ } ++ ++ of_dev = of_find_device_by_node(mac_node); ++ if (unlikely(of_dev == NULL)) { ++ dev_err(dpa_dev, "of_find_device_by_node(%s) failed\n", ++ mac_node->full_name); ++ of_node_put(mac_node); ++ return ERR_PTR(-EINVAL); ++ } ++ of_node_put(mac_node); ++ ++ dev = &of_dev->dev; ++ ++ mac_dev = dev_get_drvdata(dev); ++ if (unlikely(mac_dev == NULL)) { ++ dev_err(dpa_dev, "dev_get_drvdata(%s) failed\n", ++ dev_name(dev)); ++ return ERR_PTR(-EINVAL); ++ } ++ ++#ifdef CONFIG_FSL_DPAA_1588 ++ phandle_prop = of_get_property(mac_node, "ptimer-handle", &lenp); ++ if (phandle_prop && ((mac_dev->phy_if != PHY_INTERFACE_MODE_SGMII) || ++ ((mac_dev->phy_if == PHY_INTERFACE_MODE_SGMII) && ++ (mac_dev->speed == SPEED_1000)))) { ++ timer_node = of_find_node_by_phandle(*phandle_prop); ++ if (timer_node) ++ net_dev = dev_get_drvdata(dpa_dev); ++ if (timer_node && net_dev) { ++ priv = netdev_priv(net_dev); ++ if (!dpa_ptp_init(priv)) ++ dev_info(dev, "%s: ptp 1588 is initialized.\n", ++ mac_node->full_name); ++ } ++ } ++#endif ++ ++#ifdef CONFIG_PTP_1588_CLOCK_DPAA ++ if ((mac_dev->phy_if != PHY_INTERFACE_MODE_SGMII) || ++ ((mac_dev->phy_if == PHY_INTERFACE_MODE_SGMII) && ++ (mac_dev->speed == SPEED_1000))) { ++ ptp_priv.node = of_parse_phandle(mac_node, "ptimer-handle", 0); ++ if (ptp_priv.node) { ++ ptp_priv.of_dev = of_find_device_by_node(ptp_priv.node); ++ if (unlikely(ptp_priv.of_dev == NULL)) { ++ dev_err(dpa_dev, ++ "Cannot find device represented by timer_node\n"); ++ of_node_put(ptp_priv.node); ++ return ERR_PTR(-EINVAL); ++ } ++ ptp_priv.mac_dev = mac_dev; ++ } ++ } ++#endif ++ return mac_dev; ++} ++EXPORT_SYMBOL(dpa_mac_probe); ++ ++int dpa_set_mac_address(struct net_device *net_dev, void *addr) ++{ ++ const struct dpa_priv_s *priv; ++ int _errno; ++ struct mac_device *mac_dev; ++ ++ priv = netdev_priv(net_dev); ++ ++ _errno = eth_mac_addr(net_dev, addr); ++ if (_errno < 0) { ++ if (netif_msg_drv(priv)) ++ netdev_err(net_dev, ++ "eth_mac_addr() = %d\n", ++ _errno); ++ return _errno; ++ } ++ ++ mac_dev = priv->mac_dev; ++ ++ _errno = mac_dev->change_addr(mac_dev->get_mac_handle(mac_dev), ++ net_dev->dev_addr); ++ if (_errno < 0) { ++ if (netif_msg_drv(priv)) ++ netdev_err(net_dev, ++ "mac_dev->change_addr() = %d\n", ++ _errno); ++ return _errno; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(dpa_set_mac_address); ++ ++void dpa_set_rx_mode(struct net_device *net_dev) ++{ ++ int _errno; ++ const struct dpa_priv_s *priv; ++ ++ priv = netdev_priv(net_dev); ++ ++ if (!!(net_dev->flags & IFF_PROMISC) != priv->mac_dev->promisc) { ++ priv->mac_dev->promisc = !priv->mac_dev->promisc; ++ _errno = priv->mac_dev->set_promisc( ++ priv->mac_dev->get_mac_handle(priv->mac_dev), ++ priv->mac_dev->promisc); ++ if (unlikely(_errno < 0) && netif_msg_drv(priv)) ++ netdev_err(net_dev, ++ "mac_dev->set_promisc() = %d\n", ++ _errno); ++ } ++ ++ _errno = priv->mac_dev->set_multi(net_dev, priv->mac_dev); ++ if (unlikely(_errno < 0) && netif_msg_drv(priv)) ++ netdev_err(net_dev, "mac_dev->set_multi() = %d\n", _errno); ++} ++EXPORT_SYMBOL(dpa_set_rx_mode); ++ ++void dpa_set_buffers_layout(struct mac_device *mac_dev, ++ struct dpa_buffer_layout_s *layout) ++{ ++ struct fm_port_params params; ++ ++ /* Rx */ ++ layout[RX].priv_data_size = (uint16_t)DPA_RX_PRIV_DATA_SIZE; ++ layout[RX].parse_results = true; ++ layout[RX].hash_results = true; ++#ifdef CONFIG_FSL_DPAA_TS ++ layout[RX].time_stamp = true; ++#endif ++ fm_port_get_buff_layout_ext_params(mac_dev->port_dev[RX], ¶ms); ++ layout[RX].manip_extra_space = params.manip_extra_space; ++ /* a value of zero for data alignment means "don't care", so align to ++ * a non-zero value to prevent FMD from using its own default ++ */ ++ layout[RX].data_align = params.data_align ? : DPA_FD_DATA_ALIGNMENT; ++ ++ /* Tx */ ++ layout[TX].priv_data_size = DPA_TX_PRIV_DATA_SIZE; ++ layout[TX].parse_results = true; ++ layout[TX].hash_results = true; ++#ifdef CONFIG_FSL_DPAA_TS ++ layout[TX].time_stamp = true; ++#endif ++ fm_port_get_buff_layout_ext_params(mac_dev->port_dev[TX], ¶ms); ++ layout[TX].manip_extra_space = params.manip_extra_space; ++ layout[TX].data_align = params.data_align ? : DPA_FD_DATA_ALIGNMENT; ++} ++EXPORT_SYMBOL(dpa_set_buffers_layout); ++ ++int __attribute__((nonnull)) ++dpa_bp_alloc(struct dpa_bp *dpa_bp) ++{ ++ int err; ++ struct bman_pool_params bp_params; ++ struct platform_device *pdev; ++ ++ if (dpa_bp->size == 0 || dpa_bp->config_count == 0) { ++ pr_err("Buffer pool is not properly initialized! Missing size or initial number of buffers"); ++ return -EINVAL; ++ } ++ ++ memset(&bp_params, 0, sizeof(struct bman_pool_params)); ++#ifdef CONFIG_FMAN_PFC ++ bp_params.flags = BMAN_POOL_FLAG_THRESH; ++ bp_params.thresholds[0] = bp_params.thresholds[2] = ++ CONFIG_FSL_DPAA_ETH_REFILL_THRESHOLD; ++ bp_params.thresholds[1] = bp_params.thresholds[3] = ++ CONFIG_FSL_DPAA_ETH_MAX_BUF_COUNT; ++#endif ++ ++ /* If the pool is already specified, we only create one per bpid */ ++ if (dpa_bpid2pool_use(dpa_bp->bpid)) ++ return 0; ++ ++ if (dpa_bp->bpid == 0) ++ bp_params.flags |= BMAN_POOL_FLAG_DYNAMIC_BPID; ++ else ++ bp_params.bpid = dpa_bp->bpid; ++ ++ dpa_bp->pool = bman_new_pool(&bp_params); ++ if (unlikely(dpa_bp->pool == NULL)) { ++ pr_err("bman_new_pool() failed\n"); ++ return -ENODEV; ++ } ++ ++ dpa_bp->bpid = (uint8_t)bman_get_params(dpa_bp->pool)->bpid; ++ ++ pdev = platform_device_register_simple("dpaa_eth_bpool", ++ dpa_bp->bpid, NULL, 0); ++ if (IS_ERR(pdev)) { ++ pr_err("platform_device_register_simple() failed\n"); ++ err = PTR_ERR(pdev); ++ goto pdev_register_failed; ++ } ++ { ++ struct dma_map_ops *ops = get_dma_ops(&pdev->dev); ++ ops->dma_supported = NULL; ++ } ++ err = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(40)); ++ if (err) { ++ pr_err("dma_coerce_mask_and_coherent() failed\n"); ++ goto pdev_mask_failed; ++ } ++#ifdef CONFIG_FMAN_ARM ++ /* force coherency */ ++ pdev->dev.archdata.dma_coherent = true; ++ arch_setup_dma_ops(&pdev->dev, 0, 0, NULL, true); ++#endif ++ ++ dpa_bp->dev = &pdev->dev; ++ ++ if (dpa_bp->seed_cb) { ++ err = dpa_bp->seed_cb(dpa_bp); ++ if (err) ++ goto pool_seed_failed; ++ } ++ ++ dpa_bpid2pool_map(dpa_bp->bpid, dpa_bp); ++ ++ return 0; ++ ++pool_seed_failed: ++pdev_mask_failed: ++ platform_device_unregister(pdev); ++pdev_register_failed: ++ bman_free_pool(dpa_bp->pool); ++ ++ return err; ++} ++EXPORT_SYMBOL(dpa_bp_alloc); ++ ++void dpa_bp_drain(struct dpa_bp *bp) ++{ ++ int ret, num = 8; ++ ++ do { ++ struct bm_buffer bmb[8]; ++ int i; ++ ++ ret = bman_acquire(bp->pool, bmb, num, 0); ++ if (ret < 0) { ++ if (num == 8) { ++ /* we have less than 8 buffers left; ++ * drain them one by one ++ */ ++ num = 1; ++ ret = 1; ++ continue; ++ } else { ++ /* Pool is fully drained */ ++ break; ++ } ++ } ++ ++ for (i = 0; i < num; i++) { ++ dma_addr_t addr = bm_buf_addr(&bmb[i]); ++ ++ dma_unmap_single(bp->dev, addr, bp->size, ++ DMA_BIDIRECTIONAL); ++ ++ bp->free_buf_cb(phys_to_virt(addr)); ++ } ++ } while (ret > 0); ++} ++EXPORT_SYMBOL(dpa_bp_drain); ++ ++static void __cold __attribute__((nonnull)) ++_dpa_bp_free(struct dpa_bp *dpa_bp) ++{ ++ struct dpa_bp *bp = dpa_bpid2pool(dpa_bp->bpid); ++ ++ /* the mapping between bpid and dpa_bp is done very late in the ++ * allocation procedure; if something failed before the mapping, the bp ++ * was not configured, therefore we don't need the below instructions ++ */ ++ if (!bp) ++ return; ++ ++ if (!atomic_dec_and_test(&bp->refs)) ++ return; ++ ++ if (bp->free_buf_cb) ++ dpa_bp_drain(bp); ++ ++ dpa_bp_array[bp->bpid] = NULL; ++ bman_free_pool(bp->pool); ++ ++ if (bp->dev) ++ platform_device_unregister(to_platform_device(bp->dev)); ++} ++ ++void __cold __attribute__((nonnull)) ++dpa_bp_free(struct dpa_priv_s *priv) ++{ ++ int i; ++ ++ if (priv->dpa_bp) ++ for (i = 0; i < priv->bp_count; i++) ++ _dpa_bp_free(&priv->dpa_bp[i]); ++} ++EXPORT_SYMBOL(dpa_bp_free); ++ ++struct dpa_bp *dpa_bpid2pool(int bpid) ++{ ++ return dpa_bp_array[bpid]; ++} ++EXPORT_SYMBOL(dpa_bpid2pool); ++ ++void dpa_bpid2pool_map(int bpid, struct dpa_bp *dpa_bp) ++{ ++ dpa_bp_array[bpid] = dpa_bp; ++ atomic_set(&dpa_bp->refs, 1); ++} ++ ++bool dpa_bpid2pool_use(int bpid) ++{ ++ if (dpa_bpid2pool(bpid)) { ++ atomic_inc(&dpa_bp_array[bpid]->refs); ++ return true; ++ } ++ ++ return false; ++} ++ ++#ifdef CONFIG_FSL_DPAA_ETH_USE_NDO_SELECT_QUEUE ++u16 dpa_select_queue(struct net_device *net_dev, struct sk_buff *skb, ++ struct net_device *sb_dev, ++ select_queue_fallback_t fallback) ++{ ++ return dpa_get_queue_mapping(skb); ++} ++EXPORT_SYMBOL(dpa_select_queue); ++#endif ++ ++struct dpa_fq *dpa_fq_alloc(struct device *dev, ++ u32 fq_start, ++ u32 fq_count, ++ struct list_head *list, ++ enum dpa_fq_type fq_type) ++{ ++ int i; ++ struct dpa_fq *dpa_fq; ++ ++ dpa_fq = devm_kzalloc(dev, sizeof(*dpa_fq) * fq_count, GFP_KERNEL); ++ if (dpa_fq == NULL) ++ return NULL; ++ ++ for (i = 0; i < fq_count; i++) { ++ dpa_fq[i].fq_type = fq_type; ++ if (fq_type == FQ_TYPE_RX_PCD_HI_PRIO) ++ dpa_fq[i].fqid = fq_start ? ++ DPAA_ETH_FQ_DELTA + fq_start + i : 0; ++ else ++ dpa_fq[i].fqid = fq_start ? fq_start + i : 0; ++ ++ list_add_tail(&dpa_fq[i].list, list); ++ } ++ ++#ifdef CONFIG_FMAN_PFC ++ if (fq_type == FQ_TYPE_TX) ++ for (i = 0; i < fq_count; i++) ++ dpa_fq[i].wq = i / dpa_num_cpus; ++ else ++#endif ++ for (i = 0; i < fq_count; i++) ++ _dpa_assign_wq(dpa_fq + i); ++ ++ return dpa_fq; ++} ++EXPORT_SYMBOL(dpa_fq_alloc); ++ ++/* Probing of FQs for MACful ports */ ++int dpa_fq_probe_mac(struct device *dev, struct list_head *list, ++ struct fm_port_fqs *port_fqs, ++ bool alloc_tx_conf_fqs, ++ enum port_type ptype) ++{ ++ struct fqid_cell *fqids = NULL; ++ const void *fqids_off = NULL; ++ struct dpa_fq *dpa_fq = NULL; ++ struct device_node *np = dev->of_node; ++ int num_ranges; ++ int i, lenp; ++ ++ if (ptype == TX && alloc_tx_conf_fqs) { ++ if (!dpa_fq_alloc(dev, tx_confirm_fqids->start, ++ tx_confirm_fqids->count, list, ++ FQ_TYPE_TX_CONF_MQ)) ++ goto fq_alloc_failed; ++ } ++ ++ fqids_off = of_get_property(np, fsl_qman_frame_queues[ptype], &lenp); ++ if (fqids_off == NULL) { ++ /* No dts definition, so use the defaults. */ ++ fqids = default_fqids[ptype]; ++ num_ranges = 3; ++ } else { ++ num_ranges = lenp / sizeof(*fqids); ++ ++ fqids = devm_kzalloc(dev, sizeof(*fqids) * num_ranges, ++ GFP_KERNEL); ++ if (fqids == NULL) ++ goto fqids_alloc_failed; ++ ++ /* convert to CPU endianess */ ++ for (i = 0; i < num_ranges; i++) { ++ fqids[i].start = be32_to_cpup(fqids_off + ++ i * sizeof(*fqids)); ++ fqids[i].count = be32_to_cpup(fqids_off + ++ i * sizeof(*fqids) + sizeof(__be32)); ++ } ++ } ++ ++ for (i = 0; i < num_ranges; i++) { ++ switch (i) { ++ case 0: ++ /* The first queue is the error queue */ ++ if (fqids[i].count != 1) ++ goto invalid_error_queue; ++ ++ dpa_fq = dpa_fq_alloc(dev, fqids[i].start, ++ fqids[i].count, list, ++ ptype == RX ? ++ FQ_TYPE_RX_ERROR : ++ FQ_TYPE_TX_ERROR); ++ if (dpa_fq == NULL) ++ goto fq_alloc_failed; ++ ++ if (ptype == RX) ++ port_fqs->rx_errq = &dpa_fq[0]; ++ else ++ port_fqs->tx_errq = &dpa_fq[0]; ++ break; ++ case 1: ++ /* the second queue is the default queue */ ++ if (fqids[i].count != 1) ++ goto invalid_default_queue; ++ ++ dpa_fq = dpa_fq_alloc(dev, fqids[i].start, ++ fqids[i].count, list, ++ ptype == RX ? ++ FQ_TYPE_RX_DEFAULT : ++ FQ_TYPE_TX_CONFIRM); ++ if (dpa_fq == NULL) ++ goto fq_alloc_failed; ++ ++ if (ptype == RX) ++ port_fqs->rx_defq = &dpa_fq[0]; ++ else ++ port_fqs->tx_defq = &dpa_fq[0]; ++ break; ++ default: ++ /* all subsequent queues are either RX* PCD or Tx */ ++ if (ptype == RX) { ++ if (!dpa_fq_alloc(dev, fqids[i].start, ++ fqids[i].count, list, ++ FQ_TYPE_RX_PCD) || ++ !dpa_fq_alloc(dev, fqids[i].start, ++ fqids[i].count, list, ++ FQ_TYPE_RX_PCD_HI_PRIO)) ++ goto fq_alloc_failed; ++ } else { ++ if (!dpa_fq_alloc(dev, fqids[i].start, ++ fqids[i].count, list, ++ FQ_TYPE_TX)) ++ goto fq_alloc_failed; ++ } ++ break; ++ } ++ } ++ ++ return 0; ++ ++fq_alloc_failed: ++fqids_alloc_failed: ++ dev_err(dev, "Cannot allocate memory for frame queues\n"); ++ return -ENOMEM; ++ ++invalid_default_queue: ++invalid_error_queue: ++ dev_err(dev, "Too many default or error queues\n"); ++ return -EINVAL; ++} ++EXPORT_SYMBOL(dpa_fq_probe_mac); ++ ++static u32 rx_pool_channel; ++static DEFINE_SPINLOCK(rx_pool_channel_init); ++ ++int dpa_get_channel(void) ++{ ++ spin_lock(&rx_pool_channel_init); ++ if (!rx_pool_channel) { ++ u32 pool; ++ int ret = qman_alloc_pool(&pool); ++ if (!ret) ++ rx_pool_channel = pool; ++ } ++ spin_unlock(&rx_pool_channel_init); ++ if (!rx_pool_channel) ++ return -ENOMEM; ++ return rx_pool_channel; ++} ++EXPORT_SYMBOL(dpa_get_channel); ++ ++void dpa_release_channel(void) ++{ ++ qman_release_pool(rx_pool_channel); ++} ++EXPORT_SYMBOL(dpa_release_channel); ++ ++void dpaa_eth_add_channel(u16 channel) ++{ ++ const cpumask_t *cpus = qman_affine_cpus(); ++ u32 pool = QM_SDQCR_CHANNELS_POOL_CONV(channel); ++ int cpu; ++ struct qman_portal *portal; ++ ++ for_each_cpu(cpu, cpus) { ++ portal = (struct qman_portal *)qman_get_affine_portal(cpu); ++ qman_p_static_dequeue_add(portal, pool); ++ } ++} ++EXPORT_SYMBOL(dpaa_eth_add_channel); ++ ++/** ++ * Congestion group state change notification callback. ++ * Stops the device's egress queues while they are congested and ++ * wakes them upon exiting congested state. ++ * Also updates some CGR-related stats. ++ */ ++static void dpaa_eth_cgscn(struct qman_portal *qm, struct qman_cgr *cgr, ++ ++ int congested) ++{ ++ struct dpa_priv_s *priv = (struct dpa_priv_s *)container_of(cgr, ++ struct dpa_priv_s, cgr_data.cgr); ++ ++ if (congested) { ++ priv->cgr_data.congestion_start_jiffies = jiffies; ++ netif_tx_stop_all_queues(priv->net_dev); ++ priv->cgr_data.cgr_congested_count++; ++ } else { ++ priv->cgr_data.congested_jiffies += ++ (jiffies - priv->cgr_data.congestion_start_jiffies); ++ netif_tx_wake_all_queues(priv->net_dev); ++ } ++} ++ ++int dpaa_eth_cgr_init(struct dpa_priv_s *priv) ++{ ++ struct qm_mcc_initcgr initcgr; ++ u32 cs_th; ++ int err; ++ ++ err = qman_alloc_cgrid(&priv->cgr_data.cgr.cgrid); ++ if (err < 0) { ++ pr_err("Error %d allocating CGR ID\n", err); ++ goto out_error; ++ } ++ priv->cgr_data.cgr.cb = dpaa_eth_cgscn; ++ ++ /* Enable Congestion State Change Notifications and CS taildrop */ ++ initcgr.we_mask = QM_CGR_WE_CSCN_EN | QM_CGR_WE_CS_THRES; ++ initcgr.cgr.cscn_en = QM_CGR_EN; ++ ++ /* Set different thresholds based on the MAC speed. ++ * TODO: this may turn suboptimal if the MAC is reconfigured at a speed ++ * lower than its max, e.g. if a dTSEC later negotiates a 100Mbps link. ++ * In such cases, we ought to reconfigure the threshold, too. ++ */ ++ if (priv->mac_dev->if_support & SUPPORTED_10000baseT_Full) ++ cs_th = CONFIG_FSL_DPAA_CS_THRESHOLD_10G; ++ else ++ cs_th = CONFIG_FSL_DPAA_CS_THRESHOLD_1G; ++ qm_cgr_cs_thres_set64(&initcgr.cgr.cs_thres, cs_th, 1); ++ ++ initcgr.we_mask |= QM_CGR_WE_CSTD_EN; ++ initcgr.cgr.cstd_en = QM_CGR_EN; ++ ++ err = qman_create_cgr(&priv->cgr_data.cgr, QMAN_CGR_FLAG_USE_INIT, ++ &initcgr); ++ if (err < 0) { ++ pr_err("Error %d creating CGR with ID %d\n", err, ++ priv->cgr_data.cgr.cgrid); ++ qman_release_cgrid(priv->cgr_data.cgr.cgrid); ++ goto out_error; ++ } ++ pr_debug("Created CGR %d for netdev with hwaddr %pM on QMan channel %d\n", ++ priv->cgr_data.cgr.cgrid, priv->mac_dev->addr, ++ priv->cgr_data.cgr.chan); ++ ++out_error: ++ return err; ++} ++EXPORT_SYMBOL(dpaa_eth_cgr_init); ++ ++static inline void dpa_setup_ingress(const struct dpa_priv_s *priv, ++ struct dpa_fq *fq, ++ const struct qman_fq *template) ++{ ++ fq->fq_base = *template; ++ fq->net_dev = priv->net_dev; ++ ++ fq->flags = QMAN_FQ_FLAG_NO_ENQUEUE; ++ fq->channel = priv->channel; ++} ++ ++static inline void dpa_setup_egress(const struct dpa_priv_s *priv, ++ struct dpa_fq *fq, ++ struct fm_port *port, ++ const struct qman_fq *template) ++{ ++ fq->fq_base = *template; ++ fq->net_dev = priv->net_dev; ++ ++ if (port) { ++ fq->flags = QMAN_FQ_FLAG_TO_DCPORTAL; ++ fq->channel = (uint16_t)fm_get_tx_port_channel(port); ++ } else { ++ fq->flags = QMAN_FQ_FLAG_NO_MODIFY; ++ } ++} ++ ++void dpa_fq_setup(struct dpa_priv_s *priv, const struct dpa_fq_cbs_t *fq_cbs, ++ struct fm_port *tx_port) ++{ ++ struct dpa_fq *fq; ++ uint16_t portals[NR_CPUS]; ++ int cpu, portal_cnt = 0, num_portals = 0; ++ uint32_t pcd_fqid, pcd_fqid_hi_prio; ++ const cpumask_t *affine_cpus = qman_affine_cpus(); ++ int egress_cnt = 0, conf_cnt = 0; ++ ++ /* Prepare for PCD FQs init */ ++ for_each_cpu(cpu, affine_cpus) ++ portals[num_portals++] = qman_affine_channel(cpu); ++ if (num_portals == 0) ++ dev_err(priv->net_dev->dev.parent, ++ "No Qman software (affine) channels found"); ++ ++ pcd_fqid = (priv->mac_dev) ? ++ DPAA_ETH_PCD_FQ_BASE(priv->mac_dev->res->start) : 0; ++ pcd_fqid_hi_prio = (priv->mac_dev) ? ++ DPAA_ETH_PCD_FQ_HI_PRIO_BASE(priv->mac_dev->res->start) : 0; ++ ++ /* Initialize each FQ in the list */ ++ list_for_each_entry(fq, &priv->dpa_fq_list, list) { ++ switch (fq->fq_type) { ++ case FQ_TYPE_RX_DEFAULT: ++ BUG_ON(!priv->mac_dev); ++ dpa_setup_ingress(priv, fq, &fq_cbs->rx_defq); ++ break; ++ case FQ_TYPE_RX_ERROR: ++ BUG_ON(!priv->mac_dev); ++ dpa_setup_ingress(priv, fq, &fq_cbs->rx_errq); ++ break; ++ case FQ_TYPE_RX_PCD: ++ /* For MACless we can't have dynamic Rx queues */ ++ BUG_ON(!priv->mac_dev && !fq->fqid); ++ dpa_setup_ingress(priv, fq, &fq_cbs->rx_defq); ++ if (!fq->fqid) ++ fq->fqid = pcd_fqid++; ++ fq->channel = portals[portal_cnt]; ++ portal_cnt = (portal_cnt + 1) % num_portals; ++ break; ++ case FQ_TYPE_RX_PCD_HI_PRIO: ++ /* For MACless we can't have dynamic Hi Pri Rx queues */ ++ BUG_ON(!priv->mac_dev && !fq->fqid); ++ dpa_setup_ingress(priv, fq, &fq_cbs->rx_defq); ++ if (!fq->fqid) ++ fq->fqid = pcd_fqid_hi_prio++; ++ fq->channel = portals[portal_cnt]; ++ portal_cnt = (portal_cnt + 1) % num_portals; ++ break; ++ case FQ_TYPE_TX: ++ dpa_setup_egress(priv, fq, tx_port, ++ &fq_cbs->egress_ern); ++ /* If we have more Tx queues than the number of cores, ++ * just ignore the extra ones. ++ */ ++ if (egress_cnt < DPAA_ETH_TX_QUEUES) ++ priv->egress_fqs[egress_cnt++] = &fq->fq_base; ++ break; ++ case FQ_TYPE_TX_CONFIRM: ++ BUG_ON(!priv->mac_dev); ++ dpa_setup_ingress(priv, fq, &fq_cbs->tx_defq); ++ break; ++ case FQ_TYPE_TX_CONF_MQ: ++ BUG_ON(!priv->mac_dev); ++ dpa_setup_ingress(priv, fq, &fq_cbs->tx_defq); ++ priv->conf_fqs[conf_cnt++] = &fq->fq_base; ++ break; ++ case FQ_TYPE_TX_ERROR: ++ BUG_ON(!priv->mac_dev); ++ dpa_setup_ingress(priv, fq, &fq_cbs->tx_errq); ++ break; ++ default: ++ dev_warn(priv->net_dev->dev.parent, ++ "Unknown FQ type detected!\n"); ++ break; ++ } ++ } ++ ++ /* The number of Tx queues may be smaller than the number of cores, if ++ * the Tx queue range is specified in the device tree instead of being ++ * dynamically allocated. ++ * Make sure all CPUs receive a corresponding Tx queue. ++ */ ++ while (egress_cnt < DPAA_ETH_TX_QUEUES) { ++ list_for_each_entry(fq, &priv->dpa_fq_list, list) { ++ if (fq->fq_type != FQ_TYPE_TX) ++ continue; ++ priv->egress_fqs[egress_cnt++] = &fq->fq_base; ++ if (egress_cnt == DPAA_ETH_TX_QUEUES) ++ break; ++ } ++ } ++} ++EXPORT_SYMBOL(dpa_fq_setup); ++ ++int dpa_fq_init(struct dpa_fq *dpa_fq, bool td_enable) ++{ ++ int _errno; ++ const struct dpa_priv_s *priv; ++ struct device *dev; ++ struct qman_fq *fq; ++ struct qm_mcc_initfq initfq; ++ struct qman_fq *confq; ++ int queue_id; ++ ++ priv = netdev_priv(dpa_fq->net_dev); ++ dev = dpa_fq->net_dev->dev.parent; ++ ++ if (dpa_fq->fqid == 0) ++ dpa_fq->flags |= QMAN_FQ_FLAG_DYNAMIC_FQID; ++ ++ dpa_fq->init = !(dpa_fq->flags & QMAN_FQ_FLAG_NO_MODIFY); ++ ++ _errno = qman_create_fq(dpa_fq->fqid, dpa_fq->flags, &dpa_fq->fq_base); ++ if (_errno) { ++ dev_err(dev, "qman_create_fq() failed\n"); ++ return _errno; ++ } ++ fq = &dpa_fq->fq_base; ++ ++ if (dpa_fq->init) { ++ memset(&initfq, 0, sizeof(initfq)); ++ ++ initfq.we_mask = QM_INITFQ_WE_FQCTRL; ++ /* FIXME: why would we want to keep an empty FQ in cache? */ ++ initfq.fqd.fq_ctrl = QM_FQCTRL_PREFERINCACHE; ++ ++ /* Try to reduce the number of portal interrupts for ++ * Tx Confirmation FQs. ++ */ ++ if (dpa_fq->fq_type == FQ_TYPE_TX_CONFIRM) ++ initfq.fqd.fq_ctrl |= QM_FQCTRL_HOLDACTIVE; ++ ++ /* FQ placement */ ++ initfq.we_mask |= QM_INITFQ_WE_DESTWQ; ++ ++ initfq.fqd.dest.channel = dpa_fq->channel; ++ initfq.fqd.dest.wq = dpa_fq->wq; ++ ++ /* Put all egress queues in a congestion group of their own. ++ * Sensu stricto, the Tx confirmation queues are Rx FQs, ++ * rather than Tx - but they nonetheless account for the ++ * memory footprint on behalf of egress traffic. We therefore ++ * place them in the netdev's CGR, along with the Tx FQs. ++ */ ++ if (dpa_fq->fq_type == FQ_TYPE_TX || ++ dpa_fq->fq_type == FQ_TYPE_TX_CONFIRM || ++ dpa_fq->fq_type == FQ_TYPE_TX_CONF_MQ) { ++ initfq.we_mask |= QM_INITFQ_WE_CGID; ++ initfq.fqd.fq_ctrl |= QM_FQCTRL_CGE; ++ initfq.fqd.cgid = (uint8_t)priv->cgr_data.cgr.cgrid; ++ /* Set a fixed overhead accounting, in an attempt to ++ * reduce the impact of fixed-size skb shells and the ++ * driver's needed headroom on system memory. This is ++ * especially the case when the egress traffic is ++ * composed of small datagrams. ++ * Unfortunately, QMan's OAL value is capped to an ++ * insufficient value, but even that is better than ++ * no overhead accounting at all. ++ */ ++ initfq.we_mask |= QM_INITFQ_WE_OAC; ++ initfq.fqd.oac_init.oac = QM_OAC_CG; ++ initfq.fqd.oac_init.oal = ++ (signed char)(min(sizeof(struct sk_buff) + ++ priv->tx_headroom, (size_t)FSL_QMAN_MAX_OAL)); ++ } ++ ++ if (td_enable) { ++ initfq.we_mask |= QM_INITFQ_WE_TDTHRESH; ++ qm_fqd_taildrop_set(&initfq.fqd.td, ++ DPA_FQ_TD, 1); ++ initfq.fqd.fq_ctrl = QM_FQCTRL_TDE; ++ } ++ ++ /* Configure the Tx confirmation queue, now that we know ++ * which Tx queue it pairs with. ++ */ ++ if (dpa_fq->fq_type == FQ_TYPE_TX) { ++ queue_id = _dpa_tx_fq_to_id(priv, &dpa_fq->fq_base); ++ if (queue_id >= 0) { ++ confq = priv->conf_fqs[queue_id]; ++ if (confq) { ++ initfq.we_mask |= QM_INITFQ_WE_CONTEXTA; ++ /* ContextA: OVOM=1 (use contextA2 bits instead of ICAD) ++ * A2V=1 (contextA A2 field is valid) ++ * A0V=1 (contextA A0 field is valid) ++ * B0V=1 (contextB field is valid) ++ * ContextA A2: EBD=1 (deallocate buffers inside FMan) ++ * ContextB B0(ASPID): 0 (absolute Virtual Storage ID) ++ */ ++ initfq.fqd.context_a.hi = 0x1e000000; ++ initfq.fqd.context_a.lo = 0x80000000; ++ } ++ } ++ } ++ ++ /* Put all *private* ingress queues in our "ingress CGR". */ ++ if (priv->use_ingress_cgr && ++ (dpa_fq->fq_type == FQ_TYPE_RX_DEFAULT || ++ dpa_fq->fq_type == FQ_TYPE_RX_ERROR || ++ dpa_fq->fq_type == FQ_TYPE_RX_PCD || ++ dpa_fq->fq_type == FQ_TYPE_RX_PCD_HI_PRIO)) { ++ initfq.we_mask |= QM_INITFQ_WE_CGID; ++ initfq.fqd.fq_ctrl |= QM_FQCTRL_CGE; ++ initfq.fqd.cgid = (uint8_t)priv->ingress_cgr.cgrid; ++ /* Set a fixed overhead accounting, just like for the ++ * egress CGR. ++ */ ++ initfq.we_mask |= QM_INITFQ_WE_OAC; ++ initfq.fqd.oac_init.oac = QM_OAC_CG; ++ initfq.fqd.oac_init.oal = ++ (signed char)(min(sizeof(struct sk_buff) + ++ priv->tx_headroom, (size_t)FSL_QMAN_MAX_OAL)); ++ } ++ ++ /* Initialization common to all ingress queues */ ++ if (dpa_fq->flags & QMAN_FQ_FLAG_NO_ENQUEUE) { ++ initfq.we_mask |= QM_INITFQ_WE_CONTEXTA; ++ initfq.fqd.fq_ctrl |= ++ QM_FQCTRL_CTXASTASHING | QM_FQCTRL_AVOIDBLOCK; ++ initfq.fqd.context_a.stashing.exclusive = ++ QM_STASHING_EXCL_DATA | QM_STASHING_EXCL_CTX | ++ QM_STASHING_EXCL_ANNOTATION; ++ initfq.fqd.context_a.stashing.data_cl = 2; ++ initfq.fqd.context_a.stashing.annotation_cl = 1; ++ initfq.fqd.context_a.stashing.context_cl = ++ DIV_ROUND_UP(sizeof(struct qman_fq), 64); ++ } ++ ++ _errno = qman_init_fq(fq, QMAN_INITFQ_FLAG_SCHED, &initfq); ++ if (_errno < 0) { ++ if (DPA_RX_PCD_HI_PRIO_FQ_INIT_FAIL(dpa_fq, _errno)) { ++ dpa_fq->init = 0; ++ } else { ++ dev_err(dev, "qman_init_fq(%u) = %d\n", ++ qman_fq_fqid(fq), _errno); ++ qman_destroy_fq(fq, 0); ++ } ++ return _errno; ++ } ++ } ++ ++ dpa_fq->fqid = qman_fq_fqid(fq); ++ ++ return 0; ++} ++EXPORT_SYMBOL(dpa_fq_init); ++ ++int __cold __attribute__((nonnull)) ++_dpa_fq_free(struct device *dev, struct qman_fq *fq) ++{ ++ int _errno, __errno; ++ struct dpa_fq *dpa_fq; ++ const struct dpa_priv_s *priv; ++ ++ _errno = 0; ++ ++ dpa_fq = container_of(fq, struct dpa_fq, fq_base); ++ priv = netdev_priv(dpa_fq->net_dev); ++ ++ if (dpa_fq->init) { ++ _errno = qman_retire_fq(fq, NULL); ++ if (unlikely(_errno < 0) && netif_msg_drv(priv)) ++ dev_err(dev, "qman_retire_fq(%u) = %d\n", ++ qman_fq_fqid(fq), _errno); ++ ++ __errno = qman_oos_fq(fq); ++ if (unlikely(__errno < 0) && netif_msg_drv(priv)) { ++ dev_err(dev, "qman_oos_fq(%u) = %d\n", ++ qman_fq_fqid(fq), __errno); ++ if (_errno >= 0) ++ _errno = __errno; ++ } ++ } ++ ++ qman_destroy_fq(fq, 0); ++ list_del(&dpa_fq->list); ++ ++ return _errno; ++} ++EXPORT_SYMBOL(_dpa_fq_free); ++ ++int __cold __attribute__((nonnull)) ++dpa_fq_free(struct device *dev, struct list_head *list) ++{ ++ int _errno, __errno; ++ struct dpa_fq *dpa_fq, *tmp; ++ ++ _errno = 0; ++ list_for_each_entry_safe(dpa_fq, tmp, list, list) { ++ __errno = _dpa_fq_free(dev, (struct qman_fq *)dpa_fq); ++ if (unlikely(__errno < 0) && _errno >= 0) ++ _errno = __errno; ++ } ++ ++ return _errno; ++} ++EXPORT_SYMBOL(dpa_fq_free); ++ ++int dpa_fqs_init(struct device *dev, struct list_head *list, bool td_enable) ++{ ++ int _errno, __errno; ++ struct dpa_fq *dpa_fq, *tmp; ++ static bool print_msg __read_mostly; ++ ++ _errno = 0; ++ print_msg = true; ++ list_for_each_entry_safe(dpa_fq, tmp, list, list) { ++ __errno = dpa_fq_init(dpa_fq, td_enable); ++ if (unlikely(__errno < 0) && _errno >= 0) { ++ if (DPA_RX_PCD_HI_PRIO_FQ_INIT_FAIL(dpa_fq, __errno)) { ++ if (print_msg) { ++ dev_warn(dev, ++ "Skip RX PCD High Priority FQs initialization\n"); ++ print_msg = false; ++ } ++ if (_dpa_fq_free(dev, (struct qman_fq *)dpa_fq)) ++ dev_warn(dev, ++ "Error freeing frame queues\n"); ++ } else { ++ _errno = __errno; ++ break; ++ } ++ } ++ } ++ ++ return _errno; ++} ++EXPORT_SYMBOL(dpa_fqs_init); ++static void ++dpaa_eth_init_tx_port(struct fm_port *port, struct dpa_fq *errq, ++ struct dpa_fq *defq, struct dpa_buffer_layout_s *buf_layout) ++{ ++ struct fm_port_params tx_port_param; ++ bool frag_enabled = false; ++ ++ memset(&tx_port_param, 0, sizeof(tx_port_param)); ++ dpaa_eth_init_port(tx, port, tx_port_param, errq->fqid, defq->fqid, ++ buf_layout, frag_enabled); ++} ++ ++static void ++dpaa_eth_init_rx_port(struct fm_port *port, struct dpa_bp *bp, size_t count, ++ struct dpa_fq *errq, struct dpa_fq *defq, ++ struct dpa_buffer_layout_s *buf_layout) ++{ ++ struct fm_port_params rx_port_param; ++ int i; ++ bool frag_enabled = false; ++ ++ memset(&rx_port_param, 0, sizeof(rx_port_param)); ++ count = min(ARRAY_SIZE(rx_port_param.pool_param), count); ++ rx_port_param.num_pools = (uint8_t)count; ++ for (i = 0; i < count; i++) { ++ if (i >= rx_port_param.num_pools) ++ break; ++ rx_port_param.pool_param[i].id = bp[i].bpid; ++ rx_port_param.pool_param[i].size = (uint16_t)bp[i].size; ++ } ++ ++ dpaa_eth_init_port(rx, port, rx_port_param, errq->fqid, defq->fqid, ++ buf_layout, frag_enabled); ++} ++ ++#if defined(CONFIG_FSL_SDK_FMAN_TEST) ++/* Defined as weak, to be implemented by fman pcd tester. */ ++int dpa_alloc_pcd_fqids(struct device *, uint32_t, uint8_t, uint32_t *) ++__attribute__((weak)); ++ ++int dpa_free_pcd_fqids(struct device *, uint32_t) __attribute__((weak)); ++#else ++int dpa_alloc_pcd_fqids(struct device *, uint32_t, uint8_t, uint32_t *); ++ ++int dpa_free_pcd_fqids(struct device *, uint32_t); ++ ++#endif /* CONFIG_FSL_SDK_FMAN_TEST */ ++ ++ ++int dpa_alloc_pcd_fqids(struct device *dev, uint32_t num, ++ uint8_t alignment, uint32_t *base_fqid) ++{ ++ dev_crit(dev, "callback not implemented!\n"); ++ ++ return 0; ++} ++ ++int dpa_free_pcd_fqids(struct device *dev, uint32_t base_fqid) ++{ ++ ++ dev_crit(dev, "callback not implemented!\n"); ++ ++ return 0; ++} ++ ++void dpaa_eth_init_ports(struct mac_device *mac_dev, ++ struct dpa_bp *bp, size_t count, ++ struct fm_port_fqs *port_fqs, ++ struct dpa_buffer_layout_s *buf_layout, ++ struct device *dev) ++{ ++ struct fm_port_pcd_param rx_port_pcd_param; ++ struct fm_port *rxport = mac_dev->port_dev[RX]; ++ struct fm_port *txport = mac_dev->port_dev[TX]; ++ ++ dpaa_eth_init_tx_port(txport, port_fqs->tx_errq, ++ port_fqs->tx_defq, &buf_layout[TX]); ++ dpaa_eth_init_rx_port(rxport, bp, count, port_fqs->rx_errq, ++ port_fqs->rx_defq, &buf_layout[RX]); ++ ++ rx_port_pcd_param.cba = dpa_alloc_pcd_fqids; ++ rx_port_pcd_param.cbf = dpa_free_pcd_fqids; ++ rx_port_pcd_param.dev = dev; ++ fm_port_pcd_bind(rxport, &rx_port_pcd_param); ++} ++EXPORT_SYMBOL(dpaa_eth_init_ports); ++ ++void dpa_release_sgt(struct qm_sg_entry *sgt) ++{ ++ struct dpa_bp *dpa_bp; ++ struct bm_buffer bmb[DPA_BUFF_RELEASE_MAX]; ++ uint8_t i = 0, j; ++ ++ memset(bmb, 0, DPA_BUFF_RELEASE_MAX * sizeof(struct bm_buffer)); ++ ++ do { ++ dpa_bp = dpa_bpid2pool(qm_sg_entry_get_bpid(&sgt[i])); ++ DPA_BUG_ON(!dpa_bp); ++ ++ j = 0; ++ do { ++ DPA_BUG_ON(qm_sg_entry_get_ext(&sgt[i])); ++ bm_buffer_set64(&bmb[j], qm_sg_addr(&sgt[i])); ++ ++ j++; i++; ++ } while (j < ARRAY_SIZE(bmb) && ++ !qm_sg_entry_get_final(&sgt[i-1]) && ++ qm_sg_entry_get_bpid(&sgt[i-1]) == ++ qm_sg_entry_get_bpid(&sgt[i])); ++ ++ while (bman_release(dpa_bp->pool, bmb, j, 0)) ++ cpu_relax(); ++ } while (!qm_sg_entry_get_final(&sgt[i-1])); ++} ++EXPORT_SYMBOL(dpa_release_sgt); ++ ++void __attribute__((nonnull)) ++dpa_fd_release(const struct net_device *net_dev, const struct qm_fd *fd) ++{ ++ struct qm_sg_entry *sgt; ++ struct dpa_bp *dpa_bp; ++ struct bm_buffer bmb; ++ dma_addr_t addr; ++ void *vaddr; ++ ++ bmb.opaque = 0; ++ bm_buffer_set64(&bmb, qm_fd_addr(fd)); ++ ++ dpa_bp = dpa_bpid2pool(fd->bpid); ++ DPA_BUG_ON(!dpa_bp); ++ ++ if (fd->format == qm_fd_sg) { ++ vaddr = phys_to_virt(qm_fd_addr(fd)); ++ sgt = vaddr + dpa_fd_offset(fd); ++ ++ dma_unmap_single(dpa_bp->dev, qm_fd_addr(fd), dpa_bp->size, ++ DMA_BIDIRECTIONAL); ++ ++ dpa_release_sgt(sgt); ++ addr = dma_map_single(dpa_bp->dev, vaddr, dpa_bp->size, ++ DMA_BIDIRECTIONAL); ++ if (unlikely(dma_mapping_error(dpa_bp->dev, addr))) { ++ dev_err(dpa_bp->dev, "DMA mapping failed"); ++ return; ++ } ++ bm_buffer_set64(&bmb, addr); ++ } ++ ++ while (bman_release(dpa_bp->pool, &bmb, 1, 0)) ++ cpu_relax(); ++} ++EXPORT_SYMBOL(dpa_fd_release); ++ ++void count_ern(struct dpa_percpu_priv_s *percpu_priv, ++ const struct qm_mr_entry *msg) ++{ ++ switch (msg->ern.rc & QM_MR_RC_MASK) { ++ case QM_MR_RC_CGR_TAILDROP: ++ percpu_priv->ern_cnt.cg_tdrop++; ++ break; ++ case QM_MR_RC_WRED: ++ percpu_priv->ern_cnt.wred++; ++ break; ++ case QM_MR_RC_ERROR: ++ percpu_priv->ern_cnt.err_cond++; ++ break; ++ case QM_MR_RC_ORPWINDOW_EARLY: ++ percpu_priv->ern_cnt.early_window++; ++ break; ++ case QM_MR_RC_ORPWINDOW_LATE: ++ percpu_priv->ern_cnt.late_window++; ++ break; ++ case QM_MR_RC_FQ_TAILDROP: ++ percpu_priv->ern_cnt.fq_tdrop++; ++ break; ++ case QM_MR_RC_ORPWINDOW_RETIRED: ++ percpu_priv->ern_cnt.fq_retired++; ++ break; ++ case QM_MR_RC_ORP_ZERO: ++ percpu_priv->ern_cnt.orp_zero++; ++ break; ++ } ++} ++EXPORT_SYMBOL(count_ern); ++ ++/** ++ * Turn on HW checksum computation for this outgoing frame. ++ * If the current protocol is not something we support in this regard ++ * (or if the stack has already computed the SW checksum), we do nothing. ++ * ++ * Returns 0 if all goes well (or HW csum doesn't apply), and a negative value ++ * otherwise. ++ * ++ * Note that this function may modify the fd->cmd field and the skb data buffer ++ * (the Parse Results area). ++ */ ++int dpa_enable_tx_csum(struct dpa_priv_s *priv, ++ struct sk_buff *skb, struct qm_fd *fd, char *parse_results) ++{ ++ fm_prs_result_t *parse_result; ++ struct iphdr *iph; ++ struct ipv6hdr *ipv6h = NULL; ++ u8 l4_proto; ++ u16 ethertype = ntohs(skb->protocol); ++ int retval = 0; ++ ++ if (skb->ip_summed != CHECKSUM_PARTIAL) ++ return 0; ++ ++ /* Note: L3 csum seems to be already computed in sw, but we can't choose ++ * L4 alone from the FM configuration anyway. ++ */ ++ ++ /* Fill in some fields of the Parse Results array, so the FMan ++ * can find them as if they came from the FMan Parser. ++ */ ++ parse_result = (fm_prs_result_t *)parse_results; ++ ++ /* If we're dealing with VLAN, get the real Ethernet type */ ++ if (ethertype == ETH_P_8021Q) { ++ /* We can't always assume the MAC header is set correctly ++ * by the stack, so reset to beginning of skb->data ++ */ ++ skb_reset_mac_header(skb); ++ ethertype = ntohs(vlan_eth_hdr(skb)->h_vlan_encapsulated_proto); ++ } ++ ++ /* Fill in the relevant L3 parse result fields ++ * and read the L4 protocol type ++ */ ++ switch (ethertype) { ++ case ETH_P_IP: ++ parse_result->l3r = cpu_to_be16(FM_L3_PARSE_RESULT_IPV4); ++ iph = ip_hdr(skb); ++ DPA_BUG_ON(iph == NULL); ++ l4_proto = iph->protocol; ++ break; ++ case ETH_P_IPV6: ++ parse_result->l3r = cpu_to_be16(FM_L3_PARSE_RESULT_IPV6); ++ ipv6h = ipv6_hdr(skb); ++ DPA_BUG_ON(ipv6h == NULL); ++ l4_proto = ipv6h->nexthdr; ++ break; ++ default: ++ /* We shouldn't even be here */ ++ if (netif_msg_tx_err(priv) && net_ratelimit()) ++ netdev_alert(priv->net_dev, ++ "Can't compute HW csum for L3 proto 0x%x\n", ++ ntohs(skb->protocol)); ++ retval = -EIO; ++ goto return_error; ++ } ++ ++ /* Fill in the relevant L4 parse result fields */ ++ switch (l4_proto) { ++ case IPPROTO_UDP: ++ parse_result->l4r = FM_L4_PARSE_RESULT_UDP; ++ break; ++ case IPPROTO_TCP: ++ parse_result->l4r = FM_L4_PARSE_RESULT_TCP; ++ break; ++ default: ++ /* This can as well be a BUG() */ ++ if (netif_msg_tx_err(priv) && net_ratelimit()) ++ netdev_alert(priv->net_dev, ++ "Can't compute HW csum for L4 proto 0x%x\n", ++ l4_proto); ++ retval = -EIO; ++ goto return_error; ++ } ++ ++ /* At index 0 is IPOffset_1 as defined in the Parse Results */ ++ parse_result->ip_off[0] = (uint8_t)skb_network_offset(skb); ++ parse_result->l4_off = (uint8_t)skb_transport_offset(skb); ++ ++ /* Enable L3 (and L4, if TCP or UDP) HW checksum. */ ++ fd->cmd |= FM_FD_CMD_RPD | FM_FD_CMD_DTC; ++ ++ /* On P1023 and similar platforms fd->cmd interpretation could ++ * be disabled by setting CONTEXT_A bit ICMD; currently this bit ++ * is not set so we do not need to check; in the future, if/when ++ * using context_a we need to check this bit ++ */ ++ ++return_error: ++ return retval; ++} ++EXPORT_SYMBOL(dpa_enable_tx_csum); ++ ++#ifdef CONFIG_FSL_DPAA_CEETM ++void dpa_enable_ceetm(struct net_device *dev) ++{ ++ struct dpa_priv_s *priv = netdev_priv(dev); ++ priv->ceetm_en = true; ++} ++EXPORT_SYMBOL(dpa_enable_ceetm); ++ ++void dpa_disable_ceetm(struct net_device *dev) ++{ ++ struct dpa_priv_s *priv = netdev_priv(dev); ++ priv->ceetm_en = false; ++} ++EXPORT_SYMBOL(dpa_disable_ceetm); ++#endif +--- /dev/null ++++ b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_common.h +@@ -0,0 +1,226 @@ ++/* Copyright 2008-2013 Freescale Semiconductor, Inc. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * * Neither the name of Freescale Semiconductor nor the ++ * names of its contributors may be used to endorse or promote products ++ * derived from this software without specific prior written permission. ++ * ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") as published by the Free Software ++ * Foundation, either version 2 of that License or (at your option) any ++ * later version. ++ * ++ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. ++ */ ++ ++#ifndef __DPAA_ETH_COMMON_H ++#define __DPAA_ETH_COMMON_H ++ ++#include <linux/etherdevice.h> /* struct net_device */ ++#include <linux/fsl_bman.h> /* struct bm_buffer */ ++#include <linux/of_platform.h> /* struct platform_device */ ++#include <linux/net_tstamp.h> /* struct hwtstamp_config */ ++ ++#include "dpaa_eth.h" ++#include "lnxwrp_fsl_fman.h" ++ ++#define dpaa_eth_init_port(type, port, param, errq_id, defq_id, buf_layout,\ ++ frag_enabled) \ ++{ \ ++ param.errq = errq_id; \ ++ param.defq = defq_id; \ ++ param.priv_data_size = buf_layout->priv_data_size; \ ++ param.parse_results = buf_layout->parse_results; \ ++ param.hash_results = buf_layout->hash_results; \ ++ param.frag_enable = frag_enabled; \ ++ param.time_stamp = buf_layout->time_stamp; \ ++ param.manip_extra_space = buf_layout->manip_extra_space; \ ++ param.data_align = buf_layout->data_align; \ ++ fm_set_##type##_port_params(port, ¶m); \ ++} ++ ++#define DPA_SGT_MAX_ENTRIES 16 /* maximum number of entries in SG Table */ ++ ++#define DPA_SGT_ENTRIES_THRESHOLD DPA_SGT_MAX_ENTRIES ++ ++#define DPA_BUFF_RELEASE_MAX 8 /* maximum number of buffers released at once */ ++ ++#define DPA_RX_PCD_HI_PRIO_FQ_INIT_FAIL(dpa_fq, _errno) \ ++ (((dpa_fq)->fq_type == FQ_TYPE_RX_PCD_HI_PRIO) && \ ++ (_errno == -EIO)) ++/* return codes for the dpaa-eth hooks */ ++enum dpaa_eth_hook_result { ++ /* fd/skb was retained by the hook. ++ * ++ * On the Rx path, this means the Ethernet driver will _not_ ++ * deliver the skb to the stack. Instead, the hook implementation ++ * is expected to properly dispose of the skb. ++ * ++ * On the Tx path, the Ethernet driver's dpa_tx() function will ++ * immediately return NETDEV_TX_OK. The hook implementation is expected ++ * to free the skb. *DO*NOT* release it to BMan, or enqueue it to FMan, ++ * unless you know exactly what you're doing! ++ * ++ * On the confirmation/error paths, the Ethernet driver will _not_ ++ * perform any fd cleanup, nor update the interface statistics. ++ */ ++ DPAA_ETH_STOLEN, ++ /* fd/skb was returned to the Ethernet driver for regular processing. ++ * The hook is not allowed to, for instance, reallocate the skb (as if ++ * by linearizing, copying, cloning or reallocating the headroom). ++ */ ++ DPAA_ETH_CONTINUE ++}; ++ ++typedef enum dpaa_eth_hook_result (*dpaa_eth_ingress_hook_t)( ++ struct sk_buff *skb, struct net_device *net_dev, u32 fqid); ++typedef enum dpaa_eth_hook_result (*dpaa_eth_egress_hook_t)( ++ struct sk_buff *skb, struct net_device *net_dev); ++typedef enum dpaa_eth_hook_result (*dpaa_eth_confirm_hook_t)( ++ struct net_device *net_dev, const struct qm_fd *fd, u32 fqid); ++ ++/* used in napi related functions */ ++extern u16 qman_portal_max; ++ ++/* from dpa_ethtool.c */ ++extern const struct ethtool_ops dpa_ethtool_ops; ++ ++#ifdef CONFIG_FSL_DPAA_HOOKS ++/* Various hooks used for unit-testing and/or fastpath optimizations. ++ * Currently only one set of such hooks is supported. ++ */ ++struct dpaa_eth_hooks_s { ++ /* Invoked on the Tx private path, immediately after receiving the skb ++ * from the stack. ++ */ ++ dpaa_eth_egress_hook_t tx; ++ ++ /* Invoked on the Rx private path, right before passing the skb ++ * up the stack. At that point, the packet's protocol id has already ++ * been set. The skb's data pointer is now at the L3 header, and ++ * skb->mac_header points to the L2 header. skb->len has been adjusted ++ * to be the length of L3+payload (i.e., the length of the ++ * original frame minus the L2 header len). ++ * For more details on what the skb looks like, see eth_type_trans(). ++ */ ++ dpaa_eth_ingress_hook_t rx_default; ++ ++ /* Driver hook for the Rx error private path. */ ++ dpaa_eth_confirm_hook_t rx_error; ++ /* Driver hook for the Tx confirmation private path. */ ++ dpaa_eth_confirm_hook_t tx_confirm; ++ /* Driver hook for the Tx error private path. */ ++ dpaa_eth_confirm_hook_t tx_error; ++}; ++ ++void fsl_dpaa_eth_set_hooks(struct dpaa_eth_hooks_s *hooks); ++ ++extern struct dpaa_eth_hooks_s dpaa_eth_hooks; ++#endif ++ ++int dpa_netdev_init(struct net_device *net_dev, ++ const uint8_t *mac_addr, ++ uint16_t tx_timeout); ++int __cold dpa_start(struct net_device *net_dev); ++int __cold dpa_stop(struct net_device *net_dev); ++void __cold dpa_timeout(struct net_device *net_dev); ++void __cold ++dpa_get_stats64(struct net_device *net_dev, ++ struct rtnl_link_stats64 *stats); ++int dpa_change_mtu(struct net_device *net_dev, int new_mtu); ++int dpa_ndo_init(struct net_device *net_dev); ++int dpa_set_features(struct net_device *dev, netdev_features_t features); ++netdev_features_t dpa_fix_features(struct net_device *dev, ++ netdev_features_t features); ++#ifdef CONFIG_FSL_DPAA_TS ++u64 dpa_get_timestamp_ns(const struct dpa_priv_s *priv, ++ enum port_type rx_tx, const void *data); ++/* Updates the skb shared hw timestamp from the hardware timestamp */ ++int dpa_get_ts(const struct dpa_priv_s *priv, enum port_type rx_tx, ++ struct skb_shared_hwtstamps *shhwtstamps, const void *data); ++#endif /* CONFIG_FSL_DPAA_TS */ ++int dpa_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); ++int __cold dpa_remove(struct platform_device *of_dev); ++struct mac_device * __cold __must_check ++__attribute__((nonnull)) dpa_mac_probe(struct platform_device *_of_dev); ++int dpa_set_mac_address(struct net_device *net_dev, void *addr); ++void dpa_set_rx_mode(struct net_device *net_dev); ++void dpa_set_buffers_layout(struct mac_device *mac_dev, ++ struct dpa_buffer_layout_s *layout); ++int __attribute__((nonnull)) ++dpa_bp_alloc(struct dpa_bp *dpa_bp); ++void __cold __attribute__((nonnull)) ++dpa_bp_free(struct dpa_priv_s *priv); ++struct dpa_bp *dpa_bpid2pool(int bpid); ++void dpa_bpid2pool_map(int bpid, struct dpa_bp *dpa_bp); ++bool dpa_bpid2pool_use(int bpid); ++void dpa_bp_drain(struct dpa_bp *bp); ++#ifdef CONFIG_FSL_DPAA_ETH_USE_NDO_SELECT_QUEUE ++u16 dpa_select_queue(struct net_device *net_dev, struct sk_buff *skb, ++ struct net_device *sb_dev, ++ select_queue_fallback_t fallback); ++#endif ++struct dpa_fq *dpa_fq_alloc(struct device *dev, ++ u32 fq_start, ++ u32 fq_count, ++ struct list_head *list, ++ enum dpa_fq_type fq_type); ++int dpa_fq_probe_mac(struct device *dev, struct list_head *list, ++ struct fm_port_fqs *port_fqs, ++ bool tx_conf_fqs_per_core, ++ enum port_type ptype); ++int dpa_get_channel(void); ++void dpa_release_channel(void); ++void dpaa_eth_add_channel(u16 channel); ++int dpaa_eth_cgr_init(struct dpa_priv_s *priv); ++void dpa_fq_setup(struct dpa_priv_s *priv, const struct dpa_fq_cbs_t *fq_cbs, ++ struct fm_port *tx_port); ++int dpa_fq_init(struct dpa_fq *dpa_fq, bool td_enable); ++int dpa_fqs_init(struct device *dev, struct list_head *list, bool td_enable); ++int __cold __attribute__((nonnull)) ++dpa_fq_free(struct device *dev, struct list_head *list); ++void dpaa_eth_init_ports(struct mac_device *mac_dev, ++ struct dpa_bp *bp, size_t count, ++ struct fm_port_fqs *port_fqs, ++ struct dpa_buffer_layout_s *buf_layout, ++ struct device *dev); ++void dpa_release_sgt(struct qm_sg_entry *sgt); ++void __attribute__((nonnull)) ++dpa_fd_release(const struct net_device *net_dev, const struct qm_fd *fd); ++void count_ern(struct dpa_percpu_priv_s *percpu_priv, ++ const struct qm_mr_entry *msg); ++int dpa_enable_tx_csum(struct dpa_priv_s *priv, ++ struct sk_buff *skb, struct qm_fd *fd, char *parse_results); ++#ifdef CONFIG_FSL_DPAA_CEETM ++void dpa_enable_ceetm(struct net_device *dev); ++void dpa_disable_ceetm(struct net_device *dev); ++#endif ++struct proxy_device { ++ struct mac_device *mac_dev; ++}; ++ ++/* mac device control functions exposed by proxy interface*/ ++int dpa_proxy_start(struct net_device *net_dev); ++int dpa_proxy_stop(struct proxy_device *proxy_dev, struct net_device *net_dev); ++int dpa_proxy_set_mac_address(struct proxy_device *proxy_dev, ++ struct net_device *net_dev); ++int dpa_proxy_set_rx_mode(struct proxy_device *proxy_dev, ++ struct net_device *net_dev); ++ ++#endif /* __DPAA_ETH_COMMON_H */ +--- /dev/null ++++ b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_proxy.c +@@ -0,0 +1,381 @@ ++/* Copyright 2008-2013 Freescale Semiconductor Inc. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * * Neither the name of Freescale Semiconductor nor the ++ * names of its contributors may be used to endorse or promote products ++ * derived from this software without specific prior written permission. ++ * ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") as published by the Free Software ++ * Foundation, either version 2 of that License or (at your option) any ++ * later version. ++ * ++ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. ++ */ ++ ++#ifdef CONFIG_FSL_DPAA_ETH_DEBUG ++#define pr_fmt(fmt) \ ++ KBUILD_MODNAME ": %s:%hu:%s() " fmt, \ ++ KBUILD_BASENAME".c", __LINE__, __func__ ++#else ++#define pr_fmt(fmt) \ ++ KBUILD_MODNAME ": " fmt ++#endif ++ ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/of_platform.h> ++#include "dpaa_eth.h" ++#include "dpaa_eth_common.h" ++#include "dpaa_eth_base.h" ++#include "lnxwrp_fsl_fman.h" /* fm_get_rx_extra_headroom(), fm_get_max_frm() */ ++#include "mac.h" ++ ++#define DPA_DESCRIPTION "FSL DPAA Proxy initialization driver" ++ ++MODULE_LICENSE("Dual BSD/GPL"); ++ ++MODULE_DESCRIPTION(DPA_DESCRIPTION); ++ ++static int __cold dpa_eth_proxy_remove(struct platform_device *of_dev); ++#ifdef CONFIG_PM ++ ++static int proxy_suspend(struct device *dev) ++{ ++ struct proxy_device *proxy_dev = dev_get_drvdata(dev); ++ struct mac_device *mac_dev = proxy_dev->mac_dev; ++ int err = 0; ++ ++ err = fm_port_suspend(mac_dev->port_dev[RX]); ++ if (err) ++ goto port_suspend_failed; ++ ++ err = fm_port_suspend(mac_dev->port_dev[TX]); ++ if (err) ++ err = fm_port_resume(mac_dev->port_dev[RX]); ++ ++port_suspend_failed: ++ return err; ++} ++ ++static int proxy_resume(struct device *dev) ++{ ++ struct proxy_device *proxy_dev = dev_get_drvdata(dev); ++ struct mac_device *mac_dev = proxy_dev->mac_dev; ++ int err = 0; ++ ++ err = fm_port_resume(mac_dev->port_dev[TX]); ++ if (err) ++ goto port_resume_failed; ++ ++ err = fm_port_resume(mac_dev->port_dev[RX]); ++ if (err) ++ err = fm_port_suspend(mac_dev->port_dev[TX]); ++ ++port_resume_failed: ++ return err; ++} ++ ++static const struct dev_pm_ops proxy_pm_ops = { ++ .suspend = proxy_suspend, ++ .resume = proxy_resume, ++}; ++ ++#define PROXY_PM_OPS (&proxy_pm_ops) ++ ++#else /* CONFIG_PM */ ++ ++#define PROXY_PM_OPS NULL ++ ++#endif /* CONFIG_PM */ ++ ++static int dpaa_eth_proxy_probe(struct platform_device *_of_dev) ++{ ++ int err = 0, i; ++ struct device *dev; ++ struct device_node *dpa_node; ++ struct dpa_bp *dpa_bp; ++ struct list_head proxy_fq_list; ++ size_t count; ++ struct fm_port_fqs port_fqs; ++ struct dpa_buffer_layout_s *buf_layout = NULL; ++ struct mac_device *mac_dev; ++ struct proxy_device *proxy_dev; ++ ++ dev = &_of_dev->dev; ++ ++ dpa_node = dev->of_node; ++ ++ if (!of_device_is_available(dpa_node)) ++ return -ENODEV; ++ ++ /* Get the buffer pools assigned to this interface */ ++ dpa_bp = dpa_bp_probe(_of_dev, &count); ++ if (IS_ERR(dpa_bp)) ++ return PTR_ERR(dpa_bp); ++ ++ mac_dev = dpa_mac_probe(_of_dev); ++ if (IS_ERR(mac_dev)) ++ return PTR_ERR(mac_dev); ++ ++ proxy_dev = devm_kzalloc(dev, sizeof(*proxy_dev), GFP_KERNEL); ++ if (!proxy_dev) { ++ dev_err(dev, "devm_kzalloc() failed\n"); ++ return -ENOMEM; ++ } ++ ++ proxy_dev->mac_dev = mac_dev; ++ dev_set_drvdata(dev, proxy_dev); ++ ++ /* We have physical ports, so we need to establish ++ * the buffer layout. ++ */ ++ buf_layout = devm_kzalloc(dev, 2 * sizeof(*buf_layout), ++ GFP_KERNEL); ++ if (!buf_layout) { ++ dev_err(dev, "devm_kzalloc() failed\n"); ++ return -ENOMEM; ++ } ++ dpa_set_buffers_layout(mac_dev, buf_layout); ++ ++ INIT_LIST_HEAD(&proxy_fq_list); ++ ++ memset(&port_fqs, 0, sizeof(port_fqs)); ++ ++ err = dpa_fq_probe_mac(dev, &proxy_fq_list, &port_fqs, true, RX); ++ if (!err) ++ err = dpa_fq_probe_mac(dev, &proxy_fq_list, &port_fqs, true, ++ TX); ++ if (err < 0) { ++ devm_kfree(dev, buf_layout); ++ return err; ++ } ++ ++ /* Proxy initializer - Just configures the MAC on behalf of ++ * another partition. ++ */ ++ dpaa_eth_init_ports(mac_dev, dpa_bp, count, &port_fqs, ++ buf_layout, dev); ++ ++ /* Proxy interfaces need to be started, and the allocated ++ * memory freed ++ */ ++ devm_kfree(dev, buf_layout); ++ devm_kfree(dev, dpa_bp); ++ ++ /* Free FQ structures */ ++ devm_kfree(dev, port_fqs.rx_defq); ++ devm_kfree(dev, port_fqs.rx_errq); ++ devm_kfree(dev, port_fqs.tx_defq); ++ devm_kfree(dev, port_fqs.tx_errq); ++ ++ for_each_port_device(i, mac_dev->port_dev) { ++ err = fm_port_enable(mac_dev->port_dev[i]); ++ if (err) ++ goto port_enable_fail; ++ } ++ ++ dev_info(dev, "probed MAC device with MAC address: %02hx:%02hx:%02hx:%02hx:%02hx:%02hx\n", ++ mac_dev->addr[0], mac_dev->addr[1], mac_dev->addr[2], ++ mac_dev->addr[3], mac_dev->addr[4], mac_dev->addr[5]); ++ ++ return 0; /* Proxy interface initialization ended */ ++ ++port_enable_fail: ++ for_each_port_device(i, mac_dev->port_dev) ++ fm_port_disable(mac_dev->port_dev[i]); ++ dpa_eth_proxy_remove(_of_dev); ++ ++ return err; ++} ++ ++int dpa_proxy_set_mac_address(struct proxy_device *proxy_dev, ++ struct net_device *net_dev) ++{ ++ struct mac_device *mac_dev; ++ int _errno; ++ ++ mac_dev = proxy_dev->mac_dev; ++ ++ _errno = mac_dev->change_addr(mac_dev->get_mac_handle(mac_dev), ++ net_dev->dev_addr); ++ if (_errno < 0) ++ return _errno; ++ ++ return 0; ++} ++EXPORT_SYMBOL(dpa_proxy_set_mac_address); ++ ++int dpa_proxy_set_rx_mode(struct proxy_device *proxy_dev, ++ struct net_device *net_dev) ++{ ++ struct mac_device *mac_dev = proxy_dev->mac_dev; ++ int _errno; ++ ++ if (!!(net_dev->flags & IFF_PROMISC) != mac_dev->promisc) { ++ mac_dev->promisc = !mac_dev->promisc; ++ _errno = mac_dev->set_promisc(mac_dev->get_mac_handle(mac_dev), ++ mac_dev->promisc); ++ if (unlikely(_errno < 0)) ++ netdev_err(net_dev, "mac_dev->set_promisc() = %d\n", ++ _errno); ++ } ++ ++ _errno = mac_dev->set_multi(net_dev, mac_dev); ++ if (unlikely(_errno < 0)) ++ return _errno; ++ ++ return 0; ++} ++EXPORT_SYMBOL(dpa_proxy_set_rx_mode); ++ ++int dpa_proxy_start(struct net_device *net_dev) ++{ ++ struct mac_device *mac_dev; ++ const struct dpa_priv_s *priv; ++ struct proxy_device *proxy_dev; ++ int _errno; ++ int i; ++ ++ priv = netdev_priv(net_dev); ++ proxy_dev = (struct proxy_device *)priv->peer; ++ mac_dev = proxy_dev->mac_dev; ++ ++ _errno = mac_dev->init_phy(net_dev, mac_dev); ++ if (_errno < 0) { ++ if (netif_msg_drv(priv)) ++ netdev_err(net_dev, "init_phy() = %d\n", ++ _errno); ++ return _errno; ++ } ++ ++ for_each_port_device(i, mac_dev->port_dev) { ++ _errno = fm_port_enable(mac_dev->port_dev[i]); ++ if (_errno) ++ goto port_enable_fail; ++ } ++ ++ _errno = mac_dev->start(mac_dev); ++ if (_errno < 0) { ++ if (netif_msg_drv(priv)) ++ netdev_err(net_dev, "mac_dev->start() = %d\n", ++ _errno); ++ goto port_enable_fail; ++ } ++ ++ return _errno; ++ ++port_enable_fail: ++ for_each_port_device(i, mac_dev->port_dev) ++ fm_port_disable(mac_dev->port_dev[i]); ++ ++ return _errno; ++} ++EXPORT_SYMBOL(dpa_proxy_start); ++ ++int dpa_proxy_stop(struct proxy_device *proxy_dev, struct net_device *net_dev) ++{ ++ struct mac_device *mac_dev = proxy_dev->mac_dev; ++ const struct dpa_priv_s *priv = netdev_priv(net_dev); ++ int _errno, i, err; ++ ++ _errno = mac_dev->stop(mac_dev); ++ if (_errno < 0) { ++ if (netif_msg_drv(priv)) ++ netdev_err(net_dev, "mac_dev->stop() = %d\n", ++ _errno); ++ return _errno; ++ } ++ ++ for_each_port_device(i, mac_dev->port_dev) { ++ err = fm_port_disable(mac_dev->port_dev[i]); ++ _errno = err ? err : _errno; ++ } ++ ++ if (mac_dev->phy_dev) ++ phy_disconnect(mac_dev->phy_dev); ++ mac_dev->phy_dev = NULL; ++ ++ return _errno; ++} ++EXPORT_SYMBOL(dpa_proxy_stop); ++ ++static int __cold dpa_eth_proxy_remove(struct platform_device *of_dev) ++{ ++ struct device *dev = &of_dev->dev; ++ struct proxy_device *proxy_dev = dev_get_drvdata(dev); ++ ++ kfree(proxy_dev); ++ ++ dev_set_drvdata(dev, NULL); ++ ++ return 0; ++} ++ ++static const struct of_device_id dpa_proxy_match[] = { ++ { ++ .compatible = "fsl,dpa-ethernet-init" ++ }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, dpa_proxy_match); ++ ++static struct platform_driver dpa_proxy_driver = { ++ .driver = { ++ .name = KBUILD_MODNAME "-proxy", ++ .of_match_table = dpa_proxy_match, ++ .owner = THIS_MODULE, ++ .pm = PROXY_PM_OPS, ++ }, ++ .probe = dpaa_eth_proxy_probe, ++ .remove = dpa_eth_proxy_remove ++}; ++ ++static int __init __cold dpa_proxy_load(void) ++{ ++ int _errno; ++ ++ pr_info(DPA_DESCRIPTION "\n"); ++ ++ /* Initialize dpaa_eth mirror values */ ++ dpa_rx_extra_headroom = fm_get_rx_extra_headroom(); ++ dpa_max_frm = fm_get_max_frm(); ++ ++ _errno = platform_driver_register(&dpa_proxy_driver); ++ if (unlikely(_errno < 0)) { ++ pr_err(KBUILD_MODNAME ++ ": %s:%hu:%s(): platform_driver_register() = %d\n", ++ KBUILD_BASENAME".c", __LINE__, __func__, _errno); ++ } ++ ++ pr_debug(KBUILD_MODNAME ": %s:%s() ->\n", ++ KBUILD_BASENAME".c", __func__); ++ ++ return _errno; ++} ++module_init(dpa_proxy_load); ++ ++static void __exit __cold dpa_proxy_unload(void) ++{ ++ platform_driver_unregister(&dpa_proxy_driver); ++ ++ pr_debug(KBUILD_MODNAME ": %s:%s() ->\n", ++ KBUILD_BASENAME".c", __func__); ++} ++module_exit(dpa_proxy_unload); +--- /dev/null ++++ b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_sg.c +@@ -0,0 +1,1113 @@ ++/* Copyright 2012 Freescale Semiconductor Inc. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * * Neither the name of Freescale Semiconductor nor the ++ * names of its contributors may be used to endorse or promote products ++ * derived from this software without specific prior written permission. ++ * ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") as published by the Free Software ++ * Foundation, either version 2 of that License or (at your option) any ++ * later version. ++ * ++ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. ++ */ ++ ++#ifdef CONFIG_FSL_DPAA_ETH_DEBUG ++#define pr_fmt(fmt) \ ++ KBUILD_MODNAME ": %s:%hu:%s() " fmt, \ ++ KBUILD_BASENAME".c", __LINE__, __func__ ++#else ++#define pr_fmt(fmt) \ ++ KBUILD_MODNAME ": " fmt ++#endif ++ ++#include <linux/init.h> ++#include <linux/skbuff.h> ++#include <linux/highmem.h> ++#include <linux/fsl_bman.h> ++ ++#include "dpaa_eth.h" ++#include "dpaa_eth_common.h" ++#ifdef CONFIG_FSL_DPAA_1588 ++#include "dpaa_1588.h" ++#endif ++#ifdef CONFIG_FSL_DPAA_CEETM ++#include "dpaa_eth_ceetm.h" ++#endif ++ ++/* DMA map and add a page frag back into the bpool. ++ * @vaddr fragment must have been allocated with netdev_alloc_frag(), ++ * specifically for fitting into @dpa_bp. ++ */ ++static void dpa_bp_recycle_frag(struct dpa_bp *dpa_bp, unsigned long vaddr, ++ int *count_ptr) ++{ ++ struct bm_buffer bmb; ++ dma_addr_t addr; ++ ++ bmb.opaque = 0; ++ ++ addr = dma_map_single(dpa_bp->dev, (void *)vaddr, dpa_bp->size, ++ DMA_BIDIRECTIONAL); ++ if (unlikely(dma_mapping_error(dpa_bp->dev, addr))) { ++ dev_err(dpa_bp->dev, "DMA mapping failed"); ++ return; ++ } ++ ++ bm_buffer_set64(&bmb, addr); ++ ++ while (bman_release(dpa_bp->pool, &bmb, 1, 0)) ++ cpu_relax(); ++ ++ (*count_ptr)++; ++} ++ ++static int _dpa_bp_add_8_bufs(const struct dpa_bp *dpa_bp) ++{ ++ struct bm_buffer bmb[8]; ++ void *new_buf; ++ dma_addr_t addr; ++ uint8_t i; ++ struct device *dev = dpa_bp->dev; ++ struct sk_buff *skb, **skbh; ++ ++ memset(bmb, 0, sizeof(struct bm_buffer) * 8); ++ ++ for (i = 0; i < 8; i++) { ++ /* We'll prepend the skb back-pointer; can't use the DPA ++ * priv space, because FMan will overwrite it (from offset 0) ++ * if it ends up being the second, third, etc. fragment ++ * in a S/G frame. ++ * ++ * We only need enough space to store a pointer, but allocate ++ * an entire cacheline for performance reasons. ++ */ ++#ifndef CONFIG_PPC ++ if (unlikely(dpaa_errata_a010022)) ++ new_buf = page_address(alloc_page(GFP_ATOMIC)); ++ else ++#endif ++ new_buf = netdev_alloc_frag(SMP_CACHE_BYTES + DPA_BP_RAW_SIZE); ++ ++ if (unlikely(!new_buf)) ++ goto netdev_alloc_failed; ++ new_buf = PTR_ALIGN(new_buf + SMP_CACHE_BYTES, SMP_CACHE_BYTES); ++ ++ skb = build_skb(new_buf, DPA_SKB_SIZE(dpa_bp->size) + ++ SKB_DATA_ALIGN(sizeof(struct skb_shared_info))); ++ if (unlikely(!skb)) { ++ put_page(virt_to_head_page(new_buf)); ++ goto build_skb_failed; ++ } ++ DPA_WRITE_SKB_PTR(skb, skbh, new_buf, -1); ++ ++ addr = dma_map_single(dev, new_buf, ++ dpa_bp->size, DMA_BIDIRECTIONAL); ++ if (unlikely(dma_mapping_error(dev, addr))) ++ goto dma_map_failed; ++ ++ bm_buffer_set64(&bmb[i], addr); ++ } ++ ++release_bufs: ++ /* Release the buffers. In case bman is busy, keep trying ++ * until successful. bman_release() is guaranteed to succeed ++ * in a reasonable amount of time ++ */ ++ while (unlikely(bman_release(dpa_bp->pool, bmb, i, 0))) ++ cpu_relax(); ++ return i; ++ ++dma_map_failed: ++ kfree_skb(skb); ++ ++build_skb_failed: ++netdev_alloc_failed: ++ net_err_ratelimited("dpa_bp_add_8_bufs() failed\n"); ++ WARN_ONCE(1, "Memory allocation failure on Rx\n"); ++ ++ bm_buffer_set64(&bmb[i], 0); ++ /* Avoid releasing a completely null buffer; bman_release() requires ++ * at least one buffer. ++ */ ++ if (likely(i)) ++ goto release_bufs; ++ ++ return 0; ++} ++ ++/* Cold path wrapper over _dpa_bp_add_8_bufs(). */ ++static void dpa_bp_add_8_bufs(const struct dpa_bp *dpa_bp, int cpu) ++{ ++ int *count_ptr = per_cpu_ptr(dpa_bp->percpu_count, cpu); ++ *count_ptr += _dpa_bp_add_8_bufs(dpa_bp); ++} ++ ++int dpa_bp_priv_seed(struct dpa_bp *dpa_bp) ++{ ++ int i; ++ ++ /* Give each CPU an allotment of "config_count" buffers */ ++ for_each_possible_cpu(i) { ++ int j; ++ ++ /* Although we access another CPU's counters here ++ * we do it at boot time so it is safe ++ */ ++ for (j = 0; j < dpa_bp->config_count; j += 8) ++ dpa_bp_add_8_bufs(dpa_bp, i); ++ } ++ return 0; ++} ++EXPORT_SYMBOL(dpa_bp_priv_seed); ++ ++/* Add buffers/(pages) for Rx processing whenever bpool count falls below ++ * REFILL_THRESHOLD. ++ */ ++int dpaa_eth_refill_bpools(struct dpa_bp *dpa_bp, int *countptr) ++{ ++ int count = *countptr; ++ int new_bufs; ++ ++ if (unlikely(count < CONFIG_FSL_DPAA_ETH_REFILL_THRESHOLD)) { ++ do { ++ new_bufs = _dpa_bp_add_8_bufs(dpa_bp); ++ if (unlikely(!new_bufs)) { ++ /* Avoid looping forever if we've temporarily ++ * run out of memory. We'll try again at the ++ * next NAPI cycle. ++ */ ++ break; ++ } ++ count += new_bufs; ++ } while (count < CONFIG_FSL_DPAA_ETH_MAX_BUF_COUNT); ++ ++ *countptr = count; ++ if (unlikely(count < CONFIG_FSL_DPAA_ETH_MAX_BUF_COUNT)) ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(dpaa_eth_refill_bpools); ++ ++/* Cleanup function for outgoing frame descriptors that were built on Tx path, ++ * either contiguous frames or scatter/gather ones. ++ * Skb freeing is not handled here. ++ * ++ * This function may be called on error paths in the Tx function, so guard ++ * against cases when not all fd relevant fields were filled in. ++ * ++ * Return the skb backpointer, since for S/G frames the buffer containing it ++ * gets freed here. ++ */ ++struct sk_buff *_dpa_cleanup_tx_fd(const struct dpa_priv_s *priv, ++ const struct qm_fd *fd) ++{ ++ const struct qm_sg_entry *sgt; ++ int i; ++ struct dpa_bp *dpa_bp = priv->dpa_bp; ++ dma_addr_t addr = qm_fd_addr(fd); ++ dma_addr_t sg_addr; ++ struct sk_buff **skbh; ++ struct sk_buff *skb = NULL; ++ const enum dma_data_direction dma_dir = DMA_TO_DEVICE; ++ int nr_frags; ++ int sg_len; ++ ++ /* retrieve skb back pointer */ ++ DPA_READ_SKB_PTR(skb, skbh, phys_to_virt(addr), 0); ++ ++ if (unlikely(fd->format == qm_fd_sg)) { ++ nr_frags = skb_shinfo(skb)->nr_frags; ++ dma_unmap_single(dpa_bp->dev, addr, dpa_fd_offset(fd) + ++ sizeof(struct qm_sg_entry) * (1 + nr_frags), ++ dma_dir); ++ ++ /* The sgt buffer has been allocated with netdev_alloc_frag(), ++ * it's from lowmem. ++ */ ++ sgt = phys_to_virt(addr + dpa_fd_offset(fd)); ++#ifdef CONFIG_FSL_DPAA_1588 ++ if (priv->tsu && priv->tsu->valid && ++ priv->tsu->hwts_tx_en_ioctl) ++ dpa_ptp_store_txstamp(priv, skb, (void *)skbh); ++#endif ++#ifdef CONFIG_FSL_DPAA_TS ++ if (unlikely(priv->ts_tx_en && ++ skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) { ++ struct skb_shared_hwtstamps shhwtstamps; ++ ++ dpa_get_ts(priv, TX, &shhwtstamps, (void *)skbh); ++ skb_tstamp_tx(skb, &shhwtstamps); ++ } ++#endif /* CONFIG_FSL_DPAA_TS */ ++ ++ /* sgt[0] is from lowmem, was dma_map_single()-ed */ ++ sg_addr = qm_sg_addr(&sgt[0]); ++ sg_len = qm_sg_entry_get_len(&sgt[0]); ++ dma_unmap_single(dpa_bp->dev, sg_addr, sg_len, dma_dir); ++ ++ /* remaining pages were mapped with dma_map_page() */ ++ for (i = 1; i <= nr_frags; i++) { ++ DPA_BUG_ON(qm_sg_entry_get_ext(&sgt[i])); ++ sg_addr = qm_sg_addr(&sgt[i]); ++ sg_len = qm_sg_entry_get_len(&sgt[i]); ++ dma_unmap_page(dpa_bp->dev, sg_addr, sg_len, dma_dir); ++ } ++ ++ /* Free the page frag that we allocated on Tx */ ++ put_page(virt_to_head_page(sgt)); ++ } else { ++ dma_unmap_single(dpa_bp->dev, addr, ++ skb_tail_pointer(skb) - (u8 *)skbh, dma_dir); ++#ifdef CONFIG_FSL_DPAA_TS ++ /* get the timestamp for non-SG frames */ ++#ifdef CONFIG_FSL_DPAA_1588 ++ if (priv->tsu && priv->tsu->valid && ++ priv->tsu->hwts_tx_en_ioctl) ++ dpa_ptp_store_txstamp(priv, skb, (void *)skbh); ++#endif ++ if (unlikely(priv->ts_tx_en && ++ skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) { ++ struct skb_shared_hwtstamps shhwtstamps; ++ ++ dpa_get_ts(priv, TX, &shhwtstamps, (void *)skbh); ++ skb_tstamp_tx(skb, &shhwtstamps); ++ } ++#endif ++ } ++ ++ return skb; ++} ++EXPORT_SYMBOL(_dpa_cleanup_tx_fd); ++ ++#ifndef CONFIG_FSL_DPAA_TS ++bool dpa_skb_is_recyclable(struct sk_buff *skb) ++{ ++ /* No recycling possible if skb buffer is kmalloc'ed */ ++ if (skb->head_frag == 0) ++ return false; ++ ++ /* or if it's an userspace buffer */ ++ if (skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY) ++ return false; ++ ++ /* or if it's cloned or shared */ ++ if (skb_shared(skb) || skb_cloned(skb) || ++ skb->fclone != SKB_FCLONE_UNAVAILABLE) ++ return false; ++ ++ return true; ++} ++EXPORT_SYMBOL(dpa_skb_is_recyclable); ++ ++bool dpa_buf_is_recyclable(struct sk_buff *skb, ++ uint32_t min_size, ++ uint16_t min_offset, ++ unsigned char **new_buf_start) ++{ ++ unsigned char *new; ++ ++ /* In order to recycle a buffer, the following conditions must be met: ++ * - buffer size no less than the buffer pool size ++ * - buffer size no higher than an upper limit (to avoid moving too much ++ * system memory to the buffer pools) ++ * - buffer address aligned to cacheline bytes ++ * - offset of data from start of buffer no lower than a minimum value ++ * - offset of data from start of buffer no higher than a maximum value ++ */ ++ new = min(skb_end_pointer(skb) - min_size, skb->data - min_offset); ++ ++ /* left align to the nearest cacheline */ ++ new = (unsigned char *)((unsigned long)new & ~(SMP_CACHE_BYTES - 1)); ++ ++ if (likely(new >= skb->head && ++ new >= (skb->data - DPA_MAX_FD_OFFSET) && ++ skb_end_pointer(skb) - new <= DPA_RECYCLE_MAX_SIZE)) { ++ *new_buf_start = new; ++ return true; ++ } ++ ++ return false; ++} ++EXPORT_SYMBOL(dpa_buf_is_recyclable); ++#endif ++ ++/* Build a linear skb around the received buffer. ++ * We are guaranteed there is enough room at the end of the data buffer to ++ * accommodate the shared info area of the skb. ++ */ ++static struct sk_buff *__hot contig_fd_to_skb(const struct dpa_priv_s *priv, ++ const struct qm_fd *fd, int *use_gro) ++{ ++ dma_addr_t addr = qm_fd_addr(fd); ++ ssize_t fd_off = dpa_fd_offset(fd); ++ void *vaddr; ++ const fm_prs_result_t *parse_results; ++ struct sk_buff *skb = NULL, **skbh; ++ ++ vaddr = phys_to_virt(addr); ++ DPA_BUG_ON(!IS_ALIGNED((unsigned long)vaddr, SMP_CACHE_BYTES)); ++ ++ /* Retrieve the skb and adjust data and tail pointers, to make sure ++ * forwarded skbs will have enough space on Tx if extra headers ++ * are added. ++ */ ++ DPA_READ_SKB_PTR(skb, skbh, vaddr, -1); ++ ++#ifdef CONFIG_FSL_DPAA_ETH_JUMBO_FRAME ++ /* When using jumbo Rx buffers, we risk having frames dropped due to ++ * the socket backlog reaching its maximum allowed size. ++ * Use the frame length for the skb truesize instead of the buffer ++ * size, as this is the size of the data that actually gets copied to ++ * userspace. ++ * The stack may increase the payload. In this case, it will want to ++ * warn us that the frame length is larger than the truesize. We ++ * bypass the warning. ++ */ ++#ifndef CONFIG_PPC ++ /* We do not support Jumbo frames on LS1043 and thus we edit ++ * the skb truesize only when the 4k errata is not present. ++ */ ++ if (likely(!dpaa_errata_a010022)) ++#endif ++ skb->truesize = SKB_TRUESIZE(dpa_fd_length(fd)); ++#endif ++ ++ DPA_BUG_ON(fd_off != priv->rx_headroom); ++ skb_reserve(skb, fd_off); ++ skb_put(skb, dpa_fd_length(fd)); ++ ++ /* Peek at the parse results for csum validation */ ++ parse_results = (const fm_prs_result_t *)(vaddr + ++ DPA_RX_PRIV_DATA_SIZE); ++ _dpa_process_parse_results(parse_results, fd, skb, use_gro); ++ ++#ifdef CONFIG_FSL_DPAA_1588 ++ if (priv->tsu && priv->tsu->valid && priv->tsu->hwts_rx_en_ioctl) ++ dpa_ptp_store_rxstamp(priv, skb, vaddr); ++#endif ++#ifdef CONFIG_FSL_DPAA_TS ++ if (priv->ts_rx_en) ++ dpa_get_ts(priv, RX, skb_hwtstamps(skb), vaddr); ++#endif /* CONFIG_FSL_DPAA_TS */ ++ ++ return skb; ++} ++ ++ ++/* Build an skb with the data of the first S/G entry in the linear portion and ++ * the rest of the frame as skb fragments. ++ * ++ * The page fragment holding the S/G Table is recycled here. ++ */ ++static struct sk_buff *__hot sg_fd_to_skb(const struct dpa_priv_s *priv, ++ const struct qm_fd *fd, int *use_gro, ++ int *count_ptr) ++{ ++ const struct qm_sg_entry *sgt; ++ dma_addr_t addr = qm_fd_addr(fd); ++ ssize_t fd_off = dpa_fd_offset(fd); ++ dma_addr_t sg_addr; ++ void *vaddr, *sg_vaddr; ++ struct dpa_bp *dpa_bp; ++ struct page *page, *head_page; ++ int frag_offset, frag_len; ++ int page_offset; ++ int i; ++ const fm_prs_result_t *parse_results; ++ struct sk_buff *skb = NULL, *skb_tmp, **skbh; ++ ++ vaddr = phys_to_virt(addr); ++ DPA_BUG_ON(!IS_ALIGNED((unsigned long)vaddr, SMP_CACHE_BYTES)); ++ ++ dpa_bp = priv->dpa_bp; ++ /* Iterate through the SGT entries and add data buffers to the skb */ ++ sgt = vaddr + fd_off; ++ for (i = 0; i < DPA_SGT_MAX_ENTRIES; i++) { ++ /* Extension bit is not supported */ ++ DPA_BUG_ON(qm_sg_entry_get_ext(&sgt[i])); ++ ++ /* We use a single global Rx pool */ ++ DPA_BUG_ON(dpa_bp != ++ dpa_bpid2pool(qm_sg_entry_get_bpid(&sgt[i]))); ++ ++ sg_addr = qm_sg_addr(&sgt[i]); ++ sg_vaddr = phys_to_virt(sg_addr); ++ DPA_BUG_ON(!IS_ALIGNED((unsigned long)sg_vaddr, ++ SMP_CACHE_BYTES)); ++ ++ dma_unmap_single(dpa_bp->dev, sg_addr, dpa_bp->size, ++ DMA_BIDIRECTIONAL); ++ if (i == 0) { ++ DPA_READ_SKB_PTR(skb, skbh, sg_vaddr, -1); ++ DPA_BUG_ON(skb->head != sg_vaddr); ++#ifdef CONFIG_FSL_DPAA_1588 ++ if (priv->tsu && priv->tsu->valid && ++ priv->tsu->hwts_rx_en_ioctl) ++ dpa_ptp_store_rxstamp(priv, skb, vaddr); ++#endif ++#ifdef CONFIG_FSL_DPAA_TS ++ if (priv->ts_rx_en) ++ dpa_get_ts(priv, RX, skb_hwtstamps(skb), vaddr); ++#endif /* CONFIG_FSL_DPAA_TS */ ++ ++ /* In the case of a SG frame, FMan stores the Internal ++ * Context in the buffer containing the sgt. ++ * Inspect the parse results before anything else. ++ */ ++ parse_results = (const fm_prs_result_t *)(vaddr + ++ DPA_RX_PRIV_DATA_SIZE); ++ _dpa_process_parse_results(parse_results, fd, skb, ++ use_gro); ++ ++ /* Make sure forwarded skbs will have enough space ++ * on Tx, if extra headers are added. ++ */ ++ DPA_BUG_ON(fd_off != priv->rx_headroom); ++ skb_reserve(skb, fd_off); ++ skb_put(skb, qm_sg_entry_get_len(&sgt[i])); ++ } else { ++ /* Not the first S/G entry; all data from buffer will ++ * be added in an skb fragment; fragment index is offset ++ * by one since first S/G entry was incorporated in the ++ * linear part of the skb. ++ * ++ * Caution: 'page' may be a tail page. ++ */ ++ DPA_READ_SKB_PTR(skb_tmp, skbh, sg_vaddr, -1); ++ page = virt_to_page(sg_vaddr); ++ head_page = virt_to_head_page(sg_vaddr); ++ ++ /* Free (only) the skbuff shell because its data buffer ++ * is already a frag in the main skb. ++ */ ++ get_page(head_page); ++ dev_kfree_skb(skb_tmp); ++ ++ /* Compute offset in (possibly tail) page */ ++ page_offset = ((unsigned long)sg_vaddr & ++ (PAGE_SIZE - 1)) + ++ (page_address(page) - page_address(head_page)); ++ /* page_offset only refers to the beginning of sgt[i]; ++ * but the buffer itself may have an internal offset. ++ */ ++ frag_offset = qm_sg_entry_get_offset(&sgt[i]) + ++ page_offset; ++ frag_len = qm_sg_entry_get_len(&sgt[i]); ++ /* skb_add_rx_frag() does no checking on the page; if ++ * we pass it a tail page, we'll end up with ++ * bad page accounting and eventually with segafults. ++ */ ++ skb_add_rx_frag(skb, i - 1, head_page, frag_offset, ++ frag_len, dpa_bp->size); ++ } ++ /* Update the pool count for the current {cpu x bpool} */ ++ (*count_ptr)--; ++ ++ if (qm_sg_entry_get_final(&sgt[i])) ++ break; ++ } ++ WARN_ONCE(i == DPA_SGT_MAX_ENTRIES, "No final bit on SGT\n"); ++ ++ /* recycle the SGT fragment */ ++ DPA_BUG_ON(dpa_bp != dpa_bpid2pool(fd->bpid)); ++ dpa_bp_recycle_frag(dpa_bp, (unsigned long)vaddr, count_ptr); ++ return skb; ++} ++ ++#ifdef CONFIG_FSL_DPAA_DBG_LOOP ++static inline int dpa_skb_loop(const struct dpa_priv_s *priv, ++ struct sk_buff *skb) ++{ ++ if (unlikely(priv->loop_to < 0)) ++ return 0; /* loop disabled by default */ ++ ++ skb_push(skb, ETH_HLEN); /* compensate for eth_type_trans */ ++ dpa_tx(skb, dpa_loop_netdevs[priv->loop_to]); ++ ++ return 1; /* Frame Tx on the selected interface */ ++} ++#endif ++ ++void __hot _dpa_rx(struct net_device *net_dev, ++ struct qman_portal *portal, ++ const struct dpa_priv_s *priv, ++ struct dpa_percpu_priv_s *percpu_priv, ++ const struct qm_fd *fd, ++ u32 fqid, ++ int *count_ptr) ++{ ++ struct dpa_bp *dpa_bp; ++ struct sk_buff *skb; ++ dma_addr_t addr = qm_fd_addr(fd); ++ u32 fd_status = fd->status; ++ unsigned int skb_len; ++ struct rtnl_link_stats64 *percpu_stats = &percpu_priv->stats; ++ int use_gro = net_dev->features & NETIF_F_GRO; ++ ++ if (unlikely(fd_status & FM_FD_STAT_RX_ERRORS) != 0) { ++ if (netif_msg_hw(priv) && net_ratelimit()) ++ netdev_warn(net_dev, "FD status = 0x%08x\n", ++ fd_status & FM_FD_STAT_RX_ERRORS); ++ ++ percpu_stats->rx_errors++; ++ goto _release_frame; ++ } ++ ++ dpa_bp = priv->dpa_bp; ++ DPA_BUG_ON(dpa_bp != dpa_bpid2pool(fd->bpid)); ++ ++ /* prefetch the first 64 bytes of the frame or the SGT start */ ++ dma_unmap_single(dpa_bp->dev, addr, dpa_bp->size, DMA_BIDIRECTIONAL); ++ prefetch(phys_to_virt(addr) + dpa_fd_offset(fd)); ++ ++ /* The only FD types that we may receive are contig and S/G */ ++ DPA_BUG_ON((fd->format != qm_fd_contig) && (fd->format != qm_fd_sg)); ++ ++ if (likely(fd->format == qm_fd_contig)) { ++#ifdef CONFIG_FSL_DPAA_HOOKS ++ /* Execute the Rx processing hook, if it exists. */ ++ if (dpaa_eth_hooks.rx_default && ++ dpaa_eth_hooks.rx_default((void *)fd, net_dev, ++ fqid) == DPAA_ETH_STOLEN) { ++ /* won't count the rx bytes in */ ++ return; ++ } ++#endif ++ skb = contig_fd_to_skb(priv, fd, &use_gro); ++ } else { ++ skb = sg_fd_to_skb(priv, fd, &use_gro, count_ptr); ++ percpu_priv->rx_sg++; ++ } ++ ++ /* Account for either the contig buffer or the SGT buffer (depending on ++ * which case we were in) having been removed from the pool. ++ */ ++ (*count_ptr)--; ++ skb->protocol = eth_type_trans(skb, net_dev); ++ ++ /* IP Reassembled frames are allowed to be larger than MTU */ ++ if (unlikely(dpa_check_rx_mtu(skb, net_dev->mtu) && ++ !(fd_status & FM_FD_IPR))) { ++ percpu_stats->rx_dropped++; ++ goto drop_bad_frame; ++ } ++ ++ skb_len = skb->len; ++ ++#ifdef CONFIG_FSL_DPAA_DBG_LOOP ++ if (dpa_skb_loop(priv, skb)) { ++ percpu_stats->rx_packets++; ++ percpu_stats->rx_bytes += skb_len; ++ return; ++ } ++#endif ++ ++ if (use_gro) { ++ gro_result_t gro_result; ++ const struct qman_portal_config *pc = ++ qman_p_get_portal_config(portal); ++ struct dpa_napi_portal *np = &percpu_priv->np[pc->index]; ++ ++ np->p = portal; ++ gro_result = napi_gro_receive(&np->napi, skb); ++ /* If frame is dropped by the stack, rx_dropped counter is ++ * incremented automatically, so no need for us to update it ++ */ ++ if (unlikely(gro_result == GRO_DROP)) ++ goto packet_dropped; ++ } else if (unlikely(netif_receive_skb(skb) == NET_RX_DROP)) ++ goto packet_dropped; ++ ++ percpu_stats->rx_packets++; ++ percpu_stats->rx_bytes += skb_len; ++ ++packet_dropped: ++ return; ++ ++drop_bad_frame: ++ dev_kfree_skb(skb); ++ return; ++ ++_release_frame: ++ dpa_fd_release(net_dev, fd); ++} ++ ++int __hot skb_to_contig_fd(struct dpa_priv_s *priv, ++ struct sk_buff *skb, struct qm_fd *fd, ++ int *count_ptr, int *offset) ++{ ++ struct sk_buff **skbh; ++ dma_addr_t addr; ++ struct dpa_bp *dpa_bp = priv->dpa_bp; ++ struct net_device *net_dev = priv->net_dev; ++ int err; ++ enum dma_data_direction dma_dir; ++ unsigned char *buffer_start; ++ int dma_map_size; ++ ++#ifndef CONFIG_FSL_DPAA_TS ++ /* Check recycling conditions; only if timestamp support is not ++ * enabled, otherwise we need the fd back on tx confirmation ++ */ ++ ++ /* We can recycle the buffer if: ++ * - the pool is not full ++ * - the buffer meets the skb recycling conditions ++ * - the buffer meets our own (size, offset, align) conditions ++ */ ++ if (likely((*count_ptr < dpa_bp->target_count) && ++ dpa_skb_is_recyclable(skb) && ++ dpa_buf_is_recyclable(skb, dpa_bp->size, ++ priv->tx_headroom, &buffer_start))) { ++ /* Buffer is recyclable; use the new start address ++ * and set fd parameters and DMA mapping direction ++ */ ++ fd->bpid = dpa_bp->bpid; ++ DPA_BUG_ON(skb->data - buffer_start > DPA_MAX_FD_OFFSET); ++ fd->offset = (uint16_t)(skb->data - buffer_start); ++ dma_dir = DMA_BIDIRECTIONAL; ++ dma_map_size = dpa_bp->size; ++ ++ DPA_WRITE_SKB_PTR(skb, skbh, buffer_start, -1); ++ *offset = skb_headroom(skb) - fd->offset; ++ } else ++#endif ++ { ++ /* Not recyclable. ++ * We are guaranteed to have at least tx_headroom bytes ++ * available, so just use that for offset. ++ */ ++ fd->bpid = 0xff; ++ buffer_start = skb->data - priv->tx_headroom; ++ fd->offset = priv->tx_headroom; ++ dma_dir = DMA_TO_DEVICE; ++ dma_map_size = skb_tail_pointer(skb) - buffer_start; ++ ++ /* The buffer will be Tx-confirmed, but the TxConf cb must ++ * necessarily look at our Tx private data to retrieve the ++ * skbuff. (In short: can't use DPA_WRITE_SKB_PTR() here.) ++ */ ++ DPA_WRITE_SKB_PTR(skb, skbh, buffer_start, 0); ++ } ++ ++ /* Enable L3/L4 hardware checksum computation. ++ * ++ * We must do this before dma_map_single(DMA_TO_DEVICE), because we may ++ * need to write into the skb. ++ */ ++ err = dpa_enable_tx_csum(priv, skb, fd, ++ ((char *)skbh) + DPA_TX_PRIV_DATA_SIZE); ++ if (unlikely(err < 0)) { ++ if (netif_msg_tx_err(priv) && net_ratelimit()) ++ netdev_err(net_dev, "HW csum error: %d\n", err); ++ return err; ++ } ++ ++ /* Fill in the rest of the FD fields */ ++ fd->format = qm_fd_contig; ++ fd->length20 = skb->len; ++ fd->cmd |= FM_FD_CMD_FCO; ++ ++ /* Map the entire buffer size that may be seen by FMan, but no more */ ++ addr = dma_map_single(dpa_bp->dev, skbh, dma_map_size, dma_dir); ++ if (unlikely(dma_mapping_error(dpa_bp->dev, addr))) { ++ if (netif_msg_tx_err(priv) && net_ratelimit()) ++ netdev_err(net_dev, "dma_map_single() failed\n"); ++ return -EINVAL; ++ } ++ qm_fd_addr_set64(fd, addr); ++ ++ return 0; ++} ++EXPORT_SYMBOL(skb_to_contig_fd); ++ ++#ifndef CONFIG_PPC ++struct sk_buff *split_skb_at_4k_boundaries(struct sk_buff *skb) ++{ ++ unsigned int length, nr_frags, moved_len = 0; ++ u64 page_start; ++ struct page *page; ++ skb_frag_t *frag; ++ int i = 0, j = 0; ++ ++ /* make sure skb is not shared */ ++ skb = skb_share_check(skb, GFP_ATOMIC); ++ if (!skb) ++ return NULL; ++ ++ nr_frags = skb_shinfo(skb)->nr_frags; ++ page_start = (u64)skb->data; ++ ++ /* split the linear part at the first 4k boundary and create one (big) ++ * fragment with the rest ++ */ ++ if (HAS_DMA_ISSUE(skb->data, skb_headlen(skb))) { ++ /* we'll add one more frag, make sure there's room */ ++ if (nr_frags + 1 > DPA_SGT_MAX_ENTRIES) ++ return NULL; ++ ++ /* next page boundary */ ++ page_start = (page_start + 0x1000) & ~0xFFF; ++ page = virt_to_page(page_start); ++ ++ /* move the rest of fragments to make room for a new one at j */ ++ for (i = nr_frags - 1; i >= j; i--) ++ skb_shinfo(skb)->frags[i + 1] = skb_shinfo(skb)->frags[i]; ++ ++ /* move length bytes to a paged fragment at j */ ++ length = min((u64)0x1000, ++ (u64)skb->data + skb_headlen(skb) - page_start); ++ skb->data_len += length; ++ moved_len += length; ++ skb_fill_page_desc(skb, j++, page, 0, length); ++ get_page(page); ++ skb_shinfo(skb)->nr_frags = ++nr_frags; ++ } ++ /* adjust the tail pointer */ ++ skb->tail -= moved_len; ++ j = 0; ++ ++ /* split any paged fragment that crosses a 4K boundary */ ++ while (j < nr_frags) { ++ frag = &skb_shinfo(skb)->frags[j]; ++ ++ /* if there is a 4K boundary between the fragment's offset and end */ ++ if (HAS_DMA_ISSUE(frag->page_offset, frag->size)) { ++ /* we'll add one more frag, make sure there's room */ ++ if (nr_frags + 1 > DPA_SGT_MAX_ENTRIES) ++ return NULL; ++ ++ /* new page boundary */ ++ page_start = (u64)page_address(skb_frag_page(frag)) + ++ frag->page_offset + 0x1000; ++ page_start = (u64)page_start & ~0xFFF; ++ page = virt_to_page(page_start); ++ ++ /* move the rest of fragments to make room for a new one at j+1 */ ++ for (i = nr_frags - 1; i > j; i--) ++ skb_shinfo(skb)->frags[i + 1] = ++ skb_shinfo(skb)->frags[i]; ++ ++ /* move length bytes to a new paged fragment at j+1 */ ++ length = (u64)page_address(skb_frag_page(frag)) + ++ frag->page_offset + frag->size - page_start; ++ frag->size -= length; ++ skb_fill_page_desc(skb, j + 1, page, 0, length); ++ get_page(page); ++ skb_shinfo(skb)->nr_frags = ++nr_frags; ++ } ++ ++ /* move to next frag */ ++ j++; ++ } ++ ++ return skb; ++} ++#endif ++ ++int __hot skb_to_sg_fd(struct dpa_priv_s *priv, ++ struct sk_buff *skb, struct qm_fd *fd) ++{ ++ struct dpa_bp *dpa_bp = priv->dpa_bp; ++ dma_addr_t addr; ++ dma_addr_t sg_addr; ++ struct sk_buff **skbh; ++ struct net_device *net_dev = priv->net_dev; ++ int sg_len, sgt_size; ++ int err; ++ ++ struct qm_sg_entry *sgt; ++ void *sgt_buf; ++ skb_frag_t *frag; ++ int i = 0, j = 0; ++ int nr_frags; ++ const enum dma_data_direction dma_dir = DMA_TO_DEVICE; ++ ++ nr_frags = skb_shinfo(skb)->nr_frags; ++ fd->format = qm_fd_sg; ++ ++ sgt_size = sizeof(struct qm_sg_entry) * (1 + nr_frags); ++ ++ /* Get a page frag to store the SGTable, or a full page if the errata ++ * is in place and we need to avoid crossing a 4k boundary. ++ */ ++#ifndef CONFIG_PPC ++ if (unlikely(dpaa_errata_a010022)) ++ sgt_buf = page_address(alloc_page(GFP_ATOMIC)); ++ else ++#endif ++ sgt_buf = netdev_alloc_frag(priv->tx_headroom + sgt_size); ++ if (unlikely(!sgt_buf)) { ++ dev_err(dpa_bp->dev, "netdev_alloc_frag() failed\n"); ++ return -ENOMEM; ++ } ++ ++ /* it seems that the memory allocator does not zero the allocated mem */ ++ memset(sgt_buf, 0, priv->tx_headroom + sgt_size); ++ ++ /* Enable L3/L4 hardware checksum computation. ++ * ++ * We must do this before dma_map_single(DMA_TO_DEVICE), because we may ++ * need to write into the skb. ++ */ ++ err = dpa_enable_tx_csum(priv, skb, fd, ++ sgt_buf + DPA_TX_PRIV_DATA_SIZE); ++ if (unlikely(err < 0)) { ++ if (netif_msg_tx_err(priv) && net_ratelimit()) ++ netdev_err(net_dev, "HW csum error: %d\n", err); ++ goto csum_failed; ++ } ++ ++ /* Assign the data from skb->data to the first SG list entry */ ++ sgt = (struct qm_sg_entry *)(sgt_buf + priv->tx_headroom); ++ sg_len = skb_headlen(skb); ++ qm_sg_entry_set_bpid(&sgt[0], 0xff); ++ qm_sg_entry_set_offset(&sgt[0], 0); ++ qm_sg_entry_set_len(&sgt[0], sg_len); ++ qm_sg_entry_set_ext(&sgt[0], 0); ++ qm_sg_entry_set_final(&sgt[0], 0); ++ ++ addr = dma_map_single(dpa_bp->dev, skb->data, sg_len, dma_dir); ++ if (unlikely(dma_mapping_error(dpa_bp->dev, addr))) { ++ dev_err(dpa_bp->dev, "DMA mapping failed"); ++ err = -EINVAL; ++ goto sg0_map_failed; ++ } ++ ++ qm_sg_entry_set64(&sgt[0], addr); ++ ++ /* populate the rest of SGT entries */ ++ for (i = 1; i <= nr_frags; i++) { ++ frag = &skb_shinfo(skb)->frags[i - 1]; ++ qm_sg_entry_set_bpid(&sgt[i], 0xff); ++ qm_sg_entry_set_offset(&sgt[i], 0); ++ qm_sg_entry_set_len(&sgt[i], frag->size); ++ qm_sg_entry_set_ext(&sgt[i], 0); ++ ++ if (i == nr_frags) ++ qm_sg_entry_set_final(&sgt[i], 1); ++ else ++ qm_sg_entry_set_final(&sgt[i], 0); ++ ++ DPA_BUG_ON(!skb_frag_page(frag)); ++ addr = skb_frag_dma_map(dpa_bp->dev, frag, 0, frag->size, ++ dma_dir); ++ if (unlikely(dma_mapping_error(dpa_bp->dev, addr))) { ++ dev_err(dpa_bp->dev, "DMA mapping failed"); ++ err = -EINVAL; ++ goto sg_map_failed; ++ } ++ ++ /* keep the offset in the address */ ++ qm_sg_entry_set64(&sgt[i], addr); ++ } ++ ++ fd->length20 = skb->len; ++ fd->offset = priv->tx_headroom; ++ ++ /* DMA map the SGT page */ ++ DPA_WRITE_SKB_PTR(skb, skbh, sgt_buf, 0); ++ addr = dma_map_single(dpa_bp->dev, sgt_buf, ++ priv->tx_headroom + sgt_size, ++ dma_dir); ++ ++ if (unlikely(dma_mapping_error(dpa_bp->dev, addr))) { ++ dev_err(dpa_bp->dev, "DMA mapping failed"); ++ err = -EINVAL; ++ goto sgt_map_failed; ++ } ++ ++ qm_fd_addr_set64(fd, addr); ++ fd->bpid = 0xff; ++ fd->cmd |= FM_FD_CMD_FCO; ++ ++ return 0; ++ ++sgt_map_failed: ++sg_map_failed: ++ for (j = 0; j < i; j++) { ++ sg_addr = qm_sg_addr(&sgt[j]); ++ dma_unmap_page(dpa_bp->dev, sg_addr, ++ qm_sg_entry_get_len(&sgt[j]), dma_dir); ++ } ++sg0_map_failed: ++csum_failed: ++ put_page(virt_to_head_page(sgt_buf)); ++ ++ return err; ++} ++EXPORT_SYMBOL(skb_to_sg_fd); ++ ++int __hot dpa_tx(struct sk_buff *skb, struct net_device *net_dev) ++{ ++ struct dpa_priv_s *priv; ++ const int queue_mapping = dpa_get_queue_mapping(skb); ++ struct qman_fq *egress_fq, *conf_fq; ++ ++#ifdef CONFIG_FSL_DPAA_HOOKS ++ /* If there is a Tx hook, run it. */ ++ if (dpaa_eth_hooks.tx && ++ dpaa_eth_hooks.tx(skb, net_dev) == DPAA_ETH_STOLEN) ++ /* won't update any Tx stats */ ++ return NETDEV_TX_OK; ++#endif ++ ++ priv = netdev_priv(net_dev); ++ ++#ifdef CONFIG_FSL_DPAA_CEETM ++ if (priv->ceetm_en) ++ return ceetm_tx(skb, net_dev); ++#endif ++ ++ egress_fq = priv->egress_fqs[queue_mapping]; ++ conf_fq = priv->conf_fqs[queue_mapping]; ++ ++ return dpa_tx_extended(skb, net_dev, egress_fq, conf_fq); ++} ++ ++int __hot dpa_tx_extended(struct sk_buff *skb, struct net_device *net_dev, ++ struct qman_fq *egress_fq, struct qman_fq *conf_fq) ++{ ++ struct dpa_priv_s *priv; ++ struct qm_fd fd; ++ struct dpa_percpu_priv_s *percpu_priv; ++ struct rtnl_link_stats64 *percpu_stats; ++ int err = 0; ++ const bool nonlinear = skb_is_nonlinear(skb); ++ int *countptr, offset = 0; ++ ++ priv = netdev_priv(net_dev); ++ /* Non-migratable context, safe to use raw_cpu_ptr */ ++ percpu_priv = raw_cpu_ptr(priv->percpu_priv); ++ percpu_stats = &percpu_priv->stats; ++ countptr = raw_cpu_ptr(priv->dpa_bp->percpu_count); ++ ++ clear_fd(&fd); ++ ++#ifdef CONFIG_FSL_DPAA_1588 ++ if (priv->tsu && priv->tsu->valid && priv->tsu->hwts_tx_en_ioctl) ++ fd.cmd |= FM_FD_CMD_UPD; ++#endif ++#ifdef CONFIG_FSL_DPAA_TS ++ if (unlikely(priv->ts_tx_en && ++ skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) ++ fd.cmd |= FM_FD_CMD_UPD; ++ skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; ++#endif /* CONFIG_FSL_DPAA_TS */ ++ ++#ifndef CONFIG_PPC ++ if (unlikely(dpaa_errata_a010022)) { ++ skb = split_skb_at_4k_boundaries(skb); ++ if (!skb) ++ goto skb_to_fd_failed; ++ } ++#endif ++ ++ /* MAX_SKB_FRAGS is larger than our DPA_SGT_MAX_ENTRIES; make sure ++ * we don't feed FMan with more fragments than it supports. ++ * Btw, we're using the first sgt entry to store the linear part of ++ * the skb, so we're one extra frag short. ++ */ ++ if (nonlinear && ++ likely(skb_shinfo(skb)->nr_frags < DPA_SGT_MAX_ENTRIES)) { ++ /* Just create a S/G fd based on the skb */ ++ err = skb_to_sg_fd(priv, skb, &fd); ++ percpu_priv->tx_frag_skbuffs++; ++ } else { ++ /* Make sure we have enough headroom to accommodate private ++ * data, parse results, etc. Normally this shouldn't happen if ++ * we're here via the standard kernel stack. ++ */ ++ if (unlikely(skb_headroom(skb) < priv->tx_headroom)) { ++ struct sk_buff *skb_new; ++ ++ skb_new = skb_realloc_headroom(skb, priv->tx_headroom); ++ if (unlikely(!skb_new)) { ++ dev_kfree_skb(skb); ++ percpu_stats->tx_errors++; ++ return NETDEV_TX_OK; ++ } ++ dev_kfree_skb(skb); ++ skb = skb_new; ++ } ++ ++ /* We're going to store the skb backpointer at the beginning ++ * of the data buffer, so we need a privately owned skb ++ */ ++ ++ /* Code borrowed from skb_unshare(). */ ++ if (skb_cloned(skb)) { ++ struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC); ++ kfree_skb(skb); ++ skb = nskb; ++ /* skb_copy() has now linearized the skbuff. */ ++ } else if (unlikely(nonlinear)) { ++ /* We are here because the egress skb contains ++ * more fragments than we support. In this case, ++ * we have no choice but to linearize it ourselves. ++ */ ++ err = __skb_linearize(skb); ++ } ++ if (unlikely(!skb || err < 0)) ++ /* Common out-of-memory error path */ ++ goto enomem; ++ ++ err = skb_to_contig_fd(priv, skb, &fd, countptr, &offset); ++ } ++ if (unlikely(err < 0)) ++ goto skb_to_fd_failed; ++ ++ if (fd.bpid != 0xff) { ++ skb_recycle(skb); ++ /* skb_recycle() reserves NET_SKB_PAD as skb headroom, ++ * but we need the skb to look as if returned by build_skb(). ++ * We need to manually adjust the tailptr as well. ++ */ ++ skb->data = skb->head + offset; ++ skb_reset_tail_pointer(skb); ++ ++ (*countptr)++; ++ percpu_priv->tx_returned++; ++ } ++ ++ if (unlikely(dpa_xmit(priv, percpu_stats, &fd, egress_fq, conf_fq) < 0)) ++ goto xmit_failed; ++ ++ return NETDEV_TX_OK; ++ ++xmit_failed: ++ if (fd.bpid != 0xff) { ++ (*countptr)--; ++ percpu_priv->tx_returned--; ++ dpa_fd_release(net_dev, &fd); ++ percpu_stats->tx_errors++; ++ return NETDEV_TX_OK; ++ } ++ _dpa_cleanup_tx_fd(priv, &fd); ++skb_to_fd_failed: ++enomem: ++ percpu_stats->tx_errors++; ++ dev_kfree_skb(skb); ++ return NETDEV_TX_OK; ++} ++EXPORT_SYMBOL(dpa_tx_extended); +--- /dev/null ++++ b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_sysfs.c +@@ -0,0 +1,278 @@ ++/* Copyright 2008-2012 Freescale Semiconductor Inc. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * * Neither the name of Freescale Semiconductor nor the ++ * names of its contributors may be used to endorse or promote products ++ * derived from this software without specific prior written permission. ++ * ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") as published by the Free Software ++ * Foundation, either version 2 of that License or (at your option) any ++ * later version. ++ * ++ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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/init.h> ++#include <linux/module.h> ++#include <linux/kthread.h> ++#include <linux/io.h> ++#include <linux/of_net.h> ++#include "dpaa_eth.h" ++#include "mac.h" /* struct mac_device */ ++#ifdef CONFIG_FSL_DPAA_1588 ++#include "dpaa_1588.h" ++#endif ++ ++static ssize_t dpaa_eth_show_addr(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct dpa_priv_s *priv = netdev_priv(to_net_dev(dev)); ++ struct mac_device *mac_dev = priv->mac_dev; ++ ++ if (mac_dev) ++ return sprintf(buf, "%llx", ++ (unsigned long long)mac_dev->res->start); ++ else ++ return sprintf(buf, "none"); ++} ++ ++static ssize_t dpaa_eth_show_type(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct dpa_priv_s *priv = netdev_priv(to_net_dev(dev)); ++ ssize_t res = 0; ++ ++ if (priv) ++ res = sprintf(buf, "%s", priv->if_type); ++ ++ return res; ++} ++ ++static ssize_t dpaa_eth_show_fqids(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct dpa_priv_s *priv = netdev_priv(to_net_dev(dev)); ++ ssize_t bytes = 0; ++ int i = 0; ++ char *str; ++ struct dpa_fq *fq; ++ struct dpa_fq *tmp; ++ struct dpa_fq *prev = NULL; ++ u32 first_fqid = 0; ++ u32 last_fqid = 0; ++ char *prevstr = NULL; ++ ++ list_for_each_entry_safe(fq, tmp, &priv->dpa_fq_list, list) { ++ switch (fq->fq_type) { ++ case FQ_TYPE_RX_DEFAULT: ++ str = "Rx default"; ++ break; ++ case FQ_TYPE_RX_ERROR: ++ str = "Rx error"; ++ break; ++ case FQ_TYPE_RX_PCD: ++ str = "Rx PCD"; ++ break; ++ case FQ_TYPE_TX_CONFIRM: ++ str = "Tx default confirmation"; ++ break; ++ case FQ_TYPE_TX_CONF_MQ: ++ str = "Tx confirmation (mq)"; ++ break; ++ case FQ_TYPE_TX_ERROR: ++ str = "Tx error"; ++ break; ++ case FQ_TYPE_TX: ++ str = "Tx"; ++ break; ++ case FQ_TYPE_RX_PCD_HI_PRIO: ++ str ="Rx PCD High Priority"; ++ break; ++ default: ++ str = "Unknown"; ++ } ++ ++ if (prev && (abs(fq->fqid - prev->fqid) != 1 || ++ str != prevstr)) { ++ if (last_fqid == first_fqid) ++ bytes += sprintf(buf + bytes, ++ "%s: %d\n", prevstr, prev->fqid); ++ else ++ bytes += sprintf(buf + bytes, ++ "%s: %d - %d\n", prevstr, ++ first_fqid, last_fqid); ++ } ++ ++ if (prev && abs(fq->fqid - prev->fqid) == 1 && str == prevstr) ++ last_fqid = fq->fqid; ++ else ++ first_fqid = last_fqid = fq->fqid; ++ ++ prev = fq; ++ prevstr = str; ++ i++; ++ } ++ ++ if (prev) { ++ if (last_fqid == first_fqid) ++ bytes += sprintf(buf + bytes, "%s: %d\n", prevstr, ++ prev->fqid); ++ else ++ bytes += sprintf(buf + bytes, "%s: %d - %d\n", prevstr, ++ first_fqid, last_fqid); ++ } ++ ++ return bytes; ++} ++ ++static ssize_t dpaa_eth_show_bpids(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ ssize_t bytes = 0; ++ struct dpa_priv_s *priv = netdev_priv(to_net_dev(dev)); ++ struct dpa_bp *dpa_bp = priv->dpa_bp; ++ int i = 0; ++ ++ for (i = 0; i < priv->bp_count; i++) ++ bytes += snprintf(buf + bytes, PAGE_SIZE, "%u\n", ++ dpa_bp[i].bpid); ++ ++ return bytes; ++} ++ ++static ssize_t dpaa_eth_show_mac_regs(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct dpa_priv_s *priv = netdev_priv(to_net_dev(dev)); ++ struct mac_device *mac_dev = priv->mac_dev; ++ int n = 0; ++ ++ if (mac_dev) ++ n = fm_mac_dump_regs(mac_dev, buf, n); ++ else ++ return sprintf(buf, "no mac registers\n"); ++ ++ return n; ++} ++ ++static ssize_t dpaa_eth_show_mac_rx_stats(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct dpa_priv_s *priv = netdev_priv(to_net_dev(dev)); ++ struct mac_device *mac_dev = priv->mac_dev; ++ int n = 0; ++ ++ if (mac_dev) ++ n = fm_mac_dump_rx_stats(mac_dev, buf, n); ++ else ++ return sprintf(buf, "no mac rx stats\n"); ++ ++ return n; ++} ++ ++static ssize_t dpaa_eth_show_mac_tx_stats(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct dpa_priv_s *priv = netdev_priv(to_net_dev(dev)); ++ struct mac_device *mac_dev = priv->mac_dev; ++ int n = 0; ++ ++ if (mac_dev) ++ n = fm_mac_dump_tx_stats(mac_dev, buf, n); ++ else ++ return sprintf(buf, "no mac tx stats\n"); ++ ++ return n; ++} ++ ++#ifdef CONFIG_FSL_DPAA_1588 ++static ssize_t dpaa_eth_show_ptp_1588(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct dpa_priv_s *priv = netdev_priv(to_net_dev(dev)); ++ ++ if (priv->tsu && priv->tsu->valid) ++ return sprintf(buf, "1\n"); ++ else ++ return sprintf(buf, "0\n"); ++} ++ ++static ssize_t dpaa_eth_set_ptp_1588(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct dpa_priv_s *priv = netdev_priv(to_net_dev(dev)); ++ unsigned int num; ++ unsigned long flags; ++ ++ if (kstrtouint(buf, 0, &num) < 0) ++ return -EINVAL; ++ ++ local_irq_save(flags); ++ ++ if (num) { ++ if (priv->tsu) ++ priv->tsu->valid = TRUE; ++ } else { ++ if (priv->tsu) ++ priv->tsu->valid = FALSE; ++ } ++ ++ local_irq_restore(flags); ++ ++ return count; ++} ++#endif ++ ++static struct device_attribute dpaa_eth_attrs[] = { ++ __ATTR(device_addr, S_IRUGO, dpaa_eth_show_addr, NULL), ++ __ATTR(device_type, S_IRUGO, dpaa_eth_show_type, NULL), ++ __ATTR(fqids, S_IRUGO, dpaa_eth_show_fqids, NULL), ++ __ATTR(bpids, S_IRUGO, dpaa_eth_show_bpids, NULL), ++ __ATTR(mac_regs, S_IRUGO, dpaa_eth_show_mac_regs, NULL), ++ __ATTR(mac_rx_stats, S_IRUGO, dpaa_eth_show_mac_rx_stats, NULL), ++ __ATTR(mac_tx_stats, S_IRUGO, dpaa_eth_show_mac_tx_stats, NULL), ++#ifdef CONFIG_FSL_DPAA_1588 ++ __ATTR(ptp_1588, S_IRUGO | S_IWUSR, dpaa_eth_show_ptp_1588, ++ dpaa_eth_set_ptp_1588), ++#endif ++}; ++ ++void dpaa_eth_sysfs_init(struct device *dev) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(dpaa_eth_attrs); i++) ++ if (device_create_file(dev, &dpaa_eth_attrs[i])) { ++ dev_err(dev, "Error creating sysfs file\n"); ++ while (i > 0) ++ device_remove_file(dev, &dpaa_eth_attrs[--i]); ++ return; ++ } ++} ++EXPORT_SYMBOL(dpaa_eth_sysfs_init); ++ ++void dpaa_eth_sysfs_remove(struct device *dev) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(dpaa_eth_attrs); i++) ++ device_remove_file(dev, &dpaa_eth_attrs[i]); ++} +--- /dev/null ++++ b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_trace.h +@@ -0,0 +1,144 @@ ++/* Copyright 2013 Freescale Semiconductor Inc. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * * Neither the name of Freescale Semiconductor nor the ++ * names of its contributors may be used to endorse or promote products ++ * derived from this software without specific prior written permission. ++ * ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") as published by the Free Software ++ * Foundation, either version 2 of that License or (at your option) any ++ * later version. ++ * ++ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. ++ */ ++ ++#undef TRACE_SYSTEM ++#define TRACE_SYSTEM dpaa_eth ++ ++#if !defined(_DPAA_ETH_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) ++#define _DPAA_ETH_TRACE_H ++ ++#include <linux/skbuff.h> ++#include <linux/netdevice.h> ++#include "dpaa_eth.h" ++#include <linux/tracepoint.h> ++ ++#define fd_format_name(format) { qm_fd_##format, #format } ++#define fd_format_list \ ++ fd_format_name(contig), \ ++ fd_format_name(sg) ++#define TR_FMT "[%s] fqid=%d, fd: addr=0x%llx, format=%s, off=%u, len=%u," \ ++ " status=0x%08x" ++ ++/* This is used to declare a class of events. ++ * individual events of this type will be defined below. ++ */ ++ ++/* Store details about a frame descriptor and the FQ on which it was ++ * transmitted/received. ++ */ ++DECLARE_EVENT_CLASS(dpaa_eth_fd, ++ /* Trace function prototype */ ++ TP_PROTO(struct net_device *netdev, ++ struct qman_fq *fq, ++ const struct qm_fd *fd), ++ ++ /* Repeat argument list here */ ++ TP_ARGS(netdev, fq, fd), ++ ++ /* A structure containing the relevant information we want to record. ++ * Declare name and type for each normal element, name, type and size ++ * for arrays. Use __string for variable length strings. ++ */ ++ TP_STRUCT__entry( ++ __field(u32, fqid) ++ __field(u64, fd_addr) ++ __field(u8, fd_format) ++ __field(u16, fd_offset) ++ __field(u32, fd_length) ++ __field(u32, fd_status) ++ __string(name, netdev->name) ++ ), ++ ++ /* The function that assigns values to the above declared fields */ ++ TP_fast_assign( ++ __entry->fqid = fq->fqid; ++ __entry->fd_addr = qm_fd_addr_get64(fd); ++ __entry->fd_format = fd->format; ++ __entry->fd_offset = dpa_fd_offset(fd); ++ __entry->fd_length = dpa_fd_length(fd); ++ __entry->fd_status = fd->status; ++ __assign_str(name, netdev->name); ++ ), ++ ++ /* This is what gets printed when the trace event is triggered */ ++ /* TODO: print the status using __print_flags() */ ++ TP_printk(TR_FMT, ++ __get_str(name), __entry->fqid, __entry->fd_addr, ++ __print_symbolic(__entry->fd_format, fd_format_list), ++ __entry->fd_offset, __entry->fd_length, __entry->fd_status) ++); ++ ++/* Now declare events of the above type. Format is: ++ * DEFINE_EVENT(class, name, proto, args), with proto and args same as for class ++ */ ++ ++/* Tx (egress) fd */ ++DEFINE_EVENT(dpaa_eth_fd, dpa_tx_fd, ++ ++ TP_PROTO(struct net_device *netdev, ++ struct qman_fq *fq, ++ const struct qm_fd *fd), ++ ++ TP_ARGS(netdev, fq, fd) ++); ++ ++/* Rx fd */ ++DEFINE_EVENT(dpaa_eth_fd, dpa_rx_fd, ++ ++ TP_PROTO(struct net_device *netdev, ++ struct qman_fq *fq, ++ const struct qm_fd *fd), ++ ++ TP_ARGS(netdev, fq, fd) ++); ++ ++/* Tx confirmation fd */ ++DEFINE_EVENT(dpaa_eth_fd, dpa_tx_conf_fd, ++ ++ TP_PROTO(struct net_device *netdev, ++ struct qman_fq *fq, ++ const struct qm_fd *fd), ++ ++ TP_ARGS(netdev, fq, fd) ++); ++ ++/* If only one event of a certain type needs to be declared, use TRACE_EVENT(). ++ * The syntax is the same as for DECLARE_EVENT_CLASS(). ++ */ ++ ++#endif /* _DPAA_ETH_TRACE_H */ ++ ++/* This must be outside ifdef _DPAA_ETH_TRACE_H */ ++#undef TRACE_INCLUDE_PATH ++#define TRACE_INCLUDE_PATH . ++#undef TRACE_INCLUDE_FILE ++#define TRACE_INCLUDE_FILE dpaa_eth_trace ++#include <trace/define_trace.h> +--- /dev/null ++++ b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_ethtool.c +@@ -0,0 +1,544 @@ ++/* Copyright 2008-2012 Freescale Semiconductor, Inc. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * * Neither the name of Freescale Semiconductor nor the ++ * names of its contributors may be used to endorse or promote products ++ * derived from this software without specific prior written permission. ++ * ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") as published by the Free Software ++ * Foundation, either version 2 of that License or (at your option) any ++ * later version. ++ * ++ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. ++ */ ++ ++#ifdef CONFIG_FSL_DPAA_ETH_DEBUG ++#define pr_fmt(fmt) \ ++ KBUILD_MODNAME ": %s:%hu:%s() " fmt, \ ++ KBUILD_BASENAME".c", __LINE__, __func__ ++#else ++#define pr_fmt(fmt) \ ++ KBUILD_MODNAME ": " fmt ++#endif ++ ++#include <linux/string.h> ++ ++#include "dpaa_eth.h" ++#include "mac.h" /* struct mac_device */ ++#include "dpaa_eth_common.h" ++ ++static const char dpa_stats_percpu[][ETH_GSTRING_LEN] = { ++ "interrupts", ++ "rx packets", ++ "tx packets", ++ "tx recycled", ++ "tx confirm", ++ "tx S/G", ++ "rx S/G", ++ "tx error", ++ "rx error", ++ "bp count" ++}; ++ ++static char dpa_stats_global[][ETH_GSTRING_LEN] = { ++ /* dpa rx errors */ ++ "rx dma error", ++ "rx frame physical error", ++ "rx frame size error", ++ "rx header error", ++ "rx csum error", ++ ++ /* demultiplexing errors */ ++ "qman cg_tdrop", ++ "qman wred", ++ "qman error cond", ++ "qman early window", ++ "qman late window", ++ "qman fq tdrop", ++ "qman fq retired", ++ "qman orp disabled", ++ ++ /* congestion related stats */ ++ "congestion time (ms)", ++ "entered congestion", ++ "congested (0/1)" ++}; ++ ++#define DPA_STATS_PERCPU_LEN ARRAY_SIZE(dpa_stats_percpu) ++#define DPA_STATS_GLOBAL_LEN ARRAY_SIZE(dpa_stats_global) ++ ++static int __cold dpa_get_settings(struct net_device *net_dev, ++ struct ethtool_cmd *et_cmd) ++{ ++ int _errno; ++ struct dpa_priv_s *priv; ++ ++ priv = netdev_priv(net_dev); ++ ++ if (priv->mac_dev == NULL) { ++ netdev_info(net_dev, "This is a MAC-less interface\n"); ++ return -ENODEV; ++ } ++ if (unlikely(priv->mac_dev->phy_dev == NULL)) { ++ netdev_dbg(net_dev, "phy device not initialized\n"); ++ return 0; ++ } ++ ++ _errno = phy_ethtool_gset(priv->mac_dev->phy_dev, et_cmd); ++ if (unlikely(_errno < 0)) ++ netdev_err(net_dev, "phy_ethtool_gset() = %d\n", _errno); ++ ++ return _errno; ++} ++ ++static int __cold dpa_set_settings(struct net_device *net_dev, ++ struct ethtool_cmd *et_cmd) ++{ ++ int _errno; ++ struct dpa_priv_s *priv; ++ ++ priv = netdev_priv(net_dev); ++ ++ if (priv->mac_dev == NULL) { ++ netdev_info(net_dev, "This is a MAC-less interface\n"); ++ return -ENODEV; ++ } ++ if (unlikely(priv->mac_dev->phy_dev == NULL)) { ++ netdev_err(net_dev, "phy device not initialized\n"); ++ return -ENODEV; ++ } ++ ++ _errno = phy_ethtool_sset(priv->mac_dev->phy_dev, et_cmd); ++ if (unlikely(_errno < 0)) ++ netdev_err(net_dev, "phy_ethtool_sset() = %d\n", _errno); ++ ++ return _errno; ++} ++ ++static void __cold dpa_get_drvinfo(struct net_device *net_dev, ++ struct ethtool_drvinfo *drvinfo) ++{ ++ int _errno; ++ ++ strncpy(drvinfo->driver, KBUILD_MODNAME, ++ sizeof(drvinfo->driver) - 1)[sizeof(drvinfo->driver)-1] = 0; ++ _errno = snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), ++ "%X", 0); ++ ++ if (unlikely(_errno >= sizeof(drvinfo->fw_version))) { ++ /* Truncated output */ ++ netdev_notice(net_dev, "snprintf() = %d\n", _errno); ++ } else if (unlikely(_errno < 0)) { ++ netdev_warn(net_dev, "snprintf() = %d\n", _errno); ++ memset(drvinfo->fw_version, 0, sizeof(drvinfo->fw_version)); ++ } ++ strncpy(drvinfo->bus_info, dev_name(net_dev->dev.parent->parent), ++ sizeof(drvinfo->bus_info)-1)[sizeof(drvinfo->bus_info)-1] = 0; ++} ++ ++static uint32_t __cold dpa_get_msglevel(struct net_device *net_dev) ++{ ++ return ((struct dpa_priv_s *)netdev_priv(net_dev))->msg_enable; ++} ++ ++static void __cold dpa_set_msglevel(struct net_device *net_dev, ++ uint32_t msg_enable) ++{ ++ ((struct dpa_priv_s *)netdev_priv(net_dev))->msg_enable = msg_enable; ++} ++ ++static int __cold dpa_nway_reset(struct net_device *net_dev) ++{ ++ int _errno; ++ struct dpa_priv_s *priv; ++ ++ priv = netdev_priv(net_dev); ++ ++ if (priv->mac_dev == NULL) { ++ netdev_info(net_dev, "This is a MAC-less interface\n"); ++ return -ENODEV; ++ } ++ if (unlikely(priv->mac_dev->phy_dev == NULL)) { ++ netdev_err(net_dev, "phy device not initialized\n"); ++ return -ENODEV; ++ } ++ ++ _errno = 0; ++ if (priv->mac_dev->phy_dev->autoneg) { ++ _errno = phy_start_aneg(priv->mac_dev->phy_dev); ++ if (unlikely(_errno < 0)) ++ netdev_err(net_dev, "phy_start_aneg() = %d\n", ++ _errno); ++ } ++ ++ return _errno; ++} ++ ++static void __cold dpa_get_pauseparam(struct net_device *net_dev, ++ struct ethtool_pauseparam *epause) ++{ ++ struct dpa_priv_s *priv; ++ struct mac_device *mac_dev; ++ struct phy_device *phy_dev; ++ ++ priv = netdev_priv(net_dev); ++ mac_dev = priv->mac_dev; ++ ++ if (mac_dev == NULL) { ++ netdev_info(net_dev, "This is a MAC-less interface\n"); ++ return; ++ } ++ ++ phy_dev = mac_dev->phy_dev; ++ if (unlikely(phy_dev == NULL)) { ++ netdev_err(net_dev, "phy device not initialized\n"); ++ return; ++ } ++ ++ epause->autoneg = mac_dev->autoneg_pause; ++ epause->rx_pause = mac_dev->rx_pause_active; ++ epause->tx_pause = mac_dev->tx_pause_active; ++} ++ ++static int __cold dpa_set_pauseparam(struct net_device *net_dev, ++ struct ethtool_pauseparam *epause) ++{ ++ struct dpa_priv_s *priv; ++ struct mac_device *mac_dev; ++ struct phy_device *phy_dev; ++ int _errno; ++ u32 newadv, oldadv; ++ bool rx_pause, tx_pause; ++ ++ priv = netdev_priv(net_dev); ++ mac_dev = priv->mac_dev; ++ ++ if (mac_dev == NULL) { ++ netdev_info(net_dev, "This is a MAC-less interface\n"); ++ return -ENODEV; ++ } ++ ++ phy_dev = mac_dev->phy_dev; ++ if (unlikely(phy_dev == NULL)) { ++ netdev_err(net_dev, "phy device not initialized\n"); ++ return -ENODEV; ++ } ++ ++ if (!(phy_dev->supported & SUPPORTED_Pause) || ++ (!(phy_dev->supported & SUPPORTED_Asym_Pause) && ++ (epause->rx_pause != epause->tx_pause))) ++ return -EINVAL; ++ ++ /* The MAC should know how to handle PAUSE frame autonegotiation before ++ * adjust_link is triggered by a forced renegotiation of sym/asym PAUSE ++ * settings. ++ */ ++ mac_dev->autoneg_pause = !!epause->autoneg; ++ mac_dev->rx_pause_req = !!epause->rx_pause; ++ mac_dev->tx_pause_req = !!epause->tx_pause; ++ ++ /* Determine the sym/asym advertised PAUSE capabilities from the desired ++ * rx/tx pause settings. ++ */ ++ newadv = 0; ++ if (epause->rx_pause) ++ newadv = ADVERTISED_Pause | ADVERTISED_Asym_Pause; ++ if (epause->tx_pause) ++ newadv |= ADVERTISED_Asym_Pause; ++ ++ oldadv = phy_dev->advertising & ++ (ADVERTISED_Pause | ADVERTISED_Asym_Pause); ++ ++ /* If there are differences between the old and the new advertised ++ * values, restart PHY autonegotiation and advertise the new values. ++ */ ++ if (oldadv != newadv) { ++ phy_dev->advertising &= ~(ADVERTISED_Pause ++ | ADVERTISED_Asym_Pause); ++ phy_dev->advertising |= newadv; ++ if (phy_dev->autoneg) { ++ _errno = phy_start_aneg(phy_dev); ++ if (unlikely(_errno < 0)) ++ netdev_err(net_dev, "phy_start_aneg() = %d\n", ++ _errno); ++ } ++ } ++ ++ get_pause_cfg(mac_dev, &rx_pause, &tx_pause); ++ _errno = set_mac_active_pause(mac_dev, rx_pause, tx_pause); ++ if (unlikely(_errno < 0)) ++ netdev_err(net_dev, "set_mac_active_pause() = %d\n", _errno); ++ ++ return _errno; ++} ++ ++#ifdef CONFIG_PM ++static void dpa_get_wol(struct net_device *net_dev, struct ethtool_wolinfo *wol) ++{ ++ struct dpa_priv_s *priv = netdev_priv(net_dev); ++ ++ wol->supported = 0; ++ wol->wolopts = 0; ++ ++ if (!priv->wol || !device_can_wakeup(net_dev->dev.parent)) ++ return; ++ ++ if (priv->wol & DPAA_WOL_MAGIC) { ++ wol->supported = WAKE_MAGIC; ++ wol->wolopts = WAKE_MAGIC; ++ } ++} ++ ++static int dpa_set_wol(struct net_device *net_dev, struct ethtool_wolinfo *wol) ++{ ++ struct dpa_priv_s *priv = netdev_priv(net_dev); ++ ++ if (priv->mac_dev == NULL) { ++ netdev_info(net_dev, "This is a MAC-less interface\n"); ++ return -ENODEV; ++ } ++ ++ if (unlikely(priv->mac_dev->phy_dev == NULL)) { ++ netdev_dbg(net_dev, "phy device not initialized\n"); ++ return -ENODEV; ++ } ++ ++ if (!device_can_wakeup(net_dev->dev.parent) || ++ (wol->wolopts & ~WAKE_MAGIC)) ++ return -EOPNOTSUPP; ++ ++ priv->wol = 0; ++ ++ if (wol->wolopts & WAKE_MAGIC) { ++ priv->wol = DPAA_WOL_MAGIC; ++ device_set_wakeup_enable(net_dev->dev.parent, 1); ++ } else { ++ device_set_wakeup_enable(net_dev->dev.parent, 0); ++ } ++ ++ return 0; ++} ++#endif ++ ++static int dpa_get_eee(struct net_device *net_dev, struct ethtool_eee *et_eee) ++{ ++ struct dpa_priv_s *priv; ++ ++ priv = netdev_priv(net_dev); ++ if (priv->mac_dev == NULL) { ++ netdev_info(net_dev, "This is a MAC-less interface\n"); ++ return -ENODEV; ++ } ++ ++ if (unlikely(priv->mac_dev->phy_dev == NULL)) { ++ netdev_err(net_dev, "phy device not initialized\n"); ++ return -ENODEV; ++ } ++ ++ return phy_ethtool_get_eee(priv->mac_dev->phy_dev, et_eee); ++} ++ ++static int dpa_set_eee(struct net_device *net_dev, struct ethtool_eee *et_eee) ++{ ++ struct dpa_priv_s *priv; ++ ++ priv = netdev_priv(net_dev); ++ if (priv->mac_dev == NULL) { ++ netdev_info(net_dev, "This is a MAC-less interface\n"); ++ return -ENODEV; ++ } ++ ++ if (unlikely(priv->mac_dev->phy_dev == NULL)) { ++ netdev_err(net_dev, "phy device not initialized\n"); ++ return -ENODEV; ++ } ++ ++ return phy_ethtool_set_eee(priv->mac_dev->phy_dev, et_eee); ++} ++ ++static int dpa_get_sset_count(struct net_device *net_dev, int type) ++{ ++ unsigned int total_stats, num_stats; ++ ++ num_stats = num_online_cpus() + 1; ++ total_stats = num_stats * DPA_STATS_PERCPU_LEN + DPA_STATS_GLOBAL_LEN; ++ ++ switch (type) { ++ case ETH_SS_STATS: ++ return total_stats; ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ ++static void copy_stats(struct dpa_percpu_priv_s *percpu_priv, int num_cpus, ++ int crr_cpu, u64 bp_count, u64 *data) ++{ ++ int num_stat_values = num_cpus + 1; ++ int crr_stat = 0; ++ ++ /* update current CPU's stats and also add them to the total values */ ++ data[crr_stat * num_stat_values + crr_cpu] = percpu_priv->in_interrupt; ++ data[crr_stat++ * num_stat_values + num_cpus] += percpu_priv->in_interrupt; ++ ++ data[crr_stat * num_stat_values + crr_cpu] = percpu_priv->stats.rx_packets; ++ data[crr_stat++ * num_stat_values + num_cpus] += percpu_priv->stats.rx_packets; ++ ++ data[crr_stat * num_stat_values + crr_cpu] = percpu_priv->stats.tx_packets; ++ data[crr_stat++ * num_stat_values + num_cpus] += percpu_priv->stats.tx_packets; ++ ++ data[crr_stat * num_stat_values + crr_cpu] = percpu_priv->tx_returned; ++ data[crr_stat++ * num_stat_values + num_cpus] += percpu_priv->tx_returned; ++ ++ data[crr_stat * num_stat_values + crr_cpu] = percpu_priv->tx_confirm; ++ data[crr_stat++ * num_stat_values + num_cpus] += percpu_priv->tx_confirm; ++ ++ data[crr_stat * num_stat_values + crr_cpu] = percpu_priv->tx_frag_skbuffs; ++ data[crr_stat++ * num_stat_values + num_cpus] += percpu_priv->tx_frag_skbuffs; ++ ++ data[crr_stat * num_stat_values + crr_cpu] = percpu_priv->rx_sg; ++ data[crr_stat++ * num_stat_values + num_cpus] += percpu_priv->rx_sg; ++ ++ data[crr_stat * num_stat_values + crr_cpu] = percpu_priv->stats.tx_errors; ++ data[crr_stat++ * num_stat_values + num_cpus] += percpu_priv->stats.tx_errors; ++ ++ data[crr_stat * num_stat_values + crr_cpu] = percpu_priv->stats.rx_errors; ++ data[crr_stat++ * num_stat_values + num_cpus] += percpu_priv->stats.rx_errors; ++ ++ data[crr_stat * num_stat_values + crr_cpu] = bp_count; ++ data[crr_stat++ * num_stat_values + num_cpus] += bp_count; ++} ++ ++static void dpa_get_ethtool_stats(struct net_device *net_dev, ++ struct ethtool_stats *stats, u64 *data) ++{ ++ u64 bp_count, cg_time, cg_num, cg_status; ++ struct dpa_percpu_priv_s *percpu_priv; ++ struct qm_mcr_querycgr query_cgr; ++ struct dpa_rx_errors rx_errors; ++ struct dpa_ern_cnt ern_cnt; ++ struct dpa_priv_s *priv; ++ unsigned int num_cpus, offset; ++ struct dpa_bp *dpa_bp; ++ int total_stats, i; ++ ++ total_stats = dpa_get_sset_count(net_dev, ETH_SS_STATS); ++ priv = netdev_priv(net_dev); ++ dpa_bp = priv->dpa_bp; ++ num_cpus = num_online_cpus(); ++ bp_count = 0; ++ ++ memset(&rx_errors, 0, sizeof(struct dpa_rx_errors)); ++ memset(&ern_cnt, 0, sizeof(struct dpa_ern_cnt)); ++ memset(data, 0, total_stats * sizeof(u64)); ++ ++ for_each_online_cpu(i) { ++ percpu_priv = per_cpu_ptr(priv->percpu_priv, i); ++ ++ if (dpa_bp->percpu_count) ++ bp_count = *(per_cpu_ptr(dpa_bp->percpu_count, i)); ++ ++ rx_errors.dme += percpu_priv->rx_errors.dme; ++ rx_errors.fpe += percpu_priv->rx_errors.fpe; ++ rx_errors.fse += percpu_priv->rx_errors.fse; ++ rx_errors.phe += percpu_priv->rx_errors.phe; ++ rx_errors.cse += percpu_priv->rx_errors.cse; ++ ++ ern_cnt.cg_tdrop += percpu_priv->ern_cnt.cg_tdrop; ++ ern_cnt.wred += percpu_priv->ern_cnt.wred; ++ ern_cnt.err_cond += percpu_priv->ern_cnt.err_cond; ++ ern_cnt.early_window += percpu_priv->ern_cnt.early_window; ++ ern_cnt.late_window += percpu_priv->ern_cnt.late_window; ++ ern_cnt.fq_tdrop += percpu_priv->ern_cnt.fq_tdrop; ++ ern_cnt.fq_retired += percpu_priv->ern_cnt.fq_retired; ++ ern_cnt.orp_zero += percpu_priv->ern_cnt.orp_zero; ++ ++ copy_stats(percpu_priv, num_cpus, i, bp_count, data); ++ } ++ ++ offset = (num_cpus + 1) * DPA_STATS_PERCPU_LEN; ++ memcpy(data + offset, &rx_errors, sizeof(struct dpa_rx_errors)); ++ ++ offset += sizeof(struct dpa_rx_errors) / sizeof(u64); ++ memcpy(data + offset, &ern_cnt, sizeof(struct dpa_ern_cnt)); ++ ++ /* gather congestion related counters */ ++ cg_num = 0; ++ cg_status = 0; ++ cg_time = jiffies_to_msecs(priv->cgr_data.congested_jiffies); ++ if (qman_query_cgr(&priv->cgr_data.cgr, &query_cgr) == 0) { ++ cg_num = priv->cgr_data.cgr_congested_count; ++ cg_status = query_cgr.cgr.cs; ++ ++ /* reset congestion stats (like QMan API does */ ++ priv->cgr_data.congested_jiffies = 0; ++ priv->cgr_data.cgr_congested_count = 0; ++ } ++ ++ offset += sizeof(struct dpa_ern_cnt) / sizeof(u64); ++ data[offset++] = cg_time; ++ data[offset++] = cg_num; ++ data[offset++] = cg_status; ++} ++ ++static void dpa_get_strings(struct net_device *net_dev, u32 stringset, u8 *data) ++{ ++ unsigned int i, j, num_cpus, size; ++ char stat_string_cpu[ETH_GSTRING_LEN]; ++ u8 *strings; ++ ++ strings = data; ++ num_cpus = num_online_cpus(); ++ size = DPA_STATS_GLOBAL_LEN * ETH_GSTRING_LEN; ++ ++ for (i = 0; i < DPA_STATS_PERCPU_LEN; i++) { ++ for (j = 0; j < num_cpus; j++) { ++ snprintf(stat_string_cpu, ETH_GSTRING_LEN, "%s [CPU %d]", dpa_stats_percpu[i], j); ++ memcpy(strings, stat_string_cpu, ETH_GSTRING_LEN); ++ strings += ETH_GSTRING_LEN; ++ } ++ snprintf(stat_string_cpu, ETH_GSTRING_LEN, "%s [TOTAL]", dpa_stats_percpu[i]); ++ memcpy(strings, stat_string_cpu, ETH_GSTRING_LEN); ++ strings += ETH_GSTRING_LEN; ++ } ++ memcpy(strings, dpa_stats_global, size); ++} ++ ++const struct ethtool_ops dpa_ethtool_ops = { ++ .get_settings = dpa_get_settings, ++ .set_settings = dpa_set_settings, ++ .get_drvinfo = dpa_get_drvinfo, ++ .get_msglevel = dpa_get_msglevel, ++ .set_msglevel = dpa_set_msglevel, ++ .nway_reset = dpa_nway_reset, ++ .get_pauseparam = dpa_get_pauseparam, ++ .set_pauseparam = dpa_set_pauseparam, ++ .self_test = NULL, /* TODO invoke the cold-boot unit-test? */ ++ .get_link = ethtool_op_get_link, ++ .get_eee = dpa_get_eee, ++ .set_eee = dpa_set_eee, ++ .get_sset_count = dpa_get_sset_count, ++ .get_ethtool_stats = dpa_get_ethtool_stats, ++ .get_strings = dpa_get_strings, ++#ifdef CONFIG_PM ++ .get_wol = dpa_get_wol, ++ .set_wol = dpa_set_wol, ++#endif ++}; +--- /dev/null ++++ b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_ptp.c +@@ -0,0 +1,290 @@ ++/* ++ * DPAA Ethernet Driver -- PTP 1588 clock using the dTSEC ++ * ++ * Author: Yangbo Lu <yangbo.lu@freescale.com> ++ * ++ * Copyright 2014 Freescale Semiconductor, Inc. ++ * ++ * 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/device.h> ++#include <linux/hrtimer.h> ++#include <linux/init.h> ++#include <linux/interrupt.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/of_platform.h> ++#include <linux/timex.h> ++#include <linux/io.h> ++ ++#include <linux/ptp_clock_kernel.h> ++ ++#include "dpaa_eth.h" ++#include "mac.h" ++ ++struct ptp_clock *clock; ++ ++static struct mac_device *mac_dev; ++static u32 freqCompensation; ++ ++/* Bit definitions for the TMR_CTRL register */ ++#define ALM1P (1<<31) /* Alarm1 output polarity */ ++#define ALM2P (1<<30) /* Alarm2 output polarity */ ++#define FS (1<<28) /* FIPER start indication */ ++#define PP1L (1<<27) /* Fiper1 pulse loopback mode enabled. */ ++#define PP2L (1<<26) /* Fiper2 pulse loopback mode enabled. */ ++#define TCLK_PERIOD_SHIFT (16) /* 1588 timer reference clock period. */ ++#define TCLK_PERIOD_MASK (0x3ff) ++#define RTPE (1<<15) /* Record Tx Timestamp to PAL Enable. */ ++#define FRD (1<<14) /* FIPER Realignment Disable */ ++#define ESFDP (1<<11) /* External Tx/Rx SFD Polarity. */ ++#define ESFDE (1<<10) /* External Tx/Rx SFD Enable. */ ++#define ETEP2 (1<<9) /* External trigger 2 edge polarity */ ++#define ETEP1 (1<<8) /* External trigger 1 edge polarity */ ++#define COPH (1<<7) /* Generated clock output phase. */ ++#define CIPH (1<<6) /* External oscillator input clock phase */ ++#define TMSR (1<<5) /* Timer soft reset. */ ++#define BYP (1<<3) /* Bypass drift compensated clock */ ++#define TE (1<<2) /* 1588 timer enable. */ ++#define CKSEL_SHIFT (0) /* 1588 Timer reference clock source */ ++#define CKSEL_MASK (0x3) ++ ++/* Bit definitions for the TMR_TEVENT register */ ++#define ETS2 (1<<25) /* External trigger 2 timestamp sampled */ ++#define ETS1 (1<<24) /* External trigger 1 timestamp sampled */ ++#define ALM2 (1<<17) /* Current time = alarm time register 2 */ ++#define ALM1 (1<<16) /* Current time = alarm time register 1 */ ++#define PP1 (1<<7) /* periodic pulse generated on FIPER1 */ ++#define PP2 (1<<6) /* periodic pulse generated on FIPER2 */ ++#define PP3 (1<<5) /* periodic pulse generated on FIPER3 */ ++ ++/* Bit definitions for the TMR_TEMASK register */ ++#define ETS2EN (1<<25) /* External trigger 2 timestamp enable */ ++#define ETS1EN (1<<24) /* External trigger 1 timestamp enable */ ++#define ALM2EN (1<<17) /* Timer ALM2 event enable */ ++#define ALM1EN (1<<16) /* Timer ALM1 event enable */ ++#define PP1EN (1<<7) /* Periodic pulse event 1 enable */ ++#define PP2EN (1<<6) /* Periodic pulse event 2 enable */ ++ ++/* Bit definitions for the TMR_PEVENT register */ ++#define TXP2 (1<<9) /* PTP transmitted timestamp im TXTS2 */ ++#define TXP1 (1<<8) /* PTP transmitted timestamp in TXTS1 */ ++#define RXP (1<<0) /* PTP frame has been received */ ++ ++/* Bit definitions for the TMR_PEMASK register */ ++#define TXP2EN (1<<9) /* Transmit PTP packet event 2 enable */ ++#define TXP1EN (1<<8) /* Transmit PTP packet event 1 enable */ ++#define RXPEN (1<<0) /* Receive PTP packet event enable */ ++ ++/* Bit definitions for the TMR_STAT register */ ++#define STAT_VEC_SHIFT (0) /* Timer general purpose status vector */ ++#define STAT_VEC_MASK (0x3f) ++ ++/* Bit definitions for the TMR_PRSC register */ ++#define PRSC_OCK_SHIFT (0) /* Output clock division/prescale factor. */ ++#define PRSC_OCK_MASK (0xffff) ++ ++ ++#define N_EXT_TS 2 ++ ++static void set_alarm(void) ++{ ++ u64 ns; ++ ++ if (mac_dev->fm_rtc_get_cnt) ++ mac_dev->fm_rtc_get_cnt(mac_dev->fm_dev, &ns); ++ ns += 1500000000ULL; ++ ns = div_u64(ns, 1000000000UL) * 1000000000ULL; ++ ns -= DPA_PTP_NOMINAL_FREQ_PERIOD_NS; ++ if (mac_dev->fm_rtc_set_alarm) ++ mac_dev->fm_rtc_set_alarm(mac_dev->fm_dev, 0, ns); ++} ++ ++static void set_fipers(void) ++{ ++ u64 fiper; ++ ++ if (mac_dev->fm_rtc_disable) ++ mac_dev->fm_rtc_disable(mac_dev->fm_dev); ++ ++ set_alarm(); ++ fiper = 1000000000ULL - DPA_PTP_NOMINAL_FREQ_PERIOD_NS; ++ if (mac_dev->fm_rtc_set_fiper) ++ mac_dev->fm_rtc_set_fiper(mac_dev->fm_dev, 0, fiper); ++ ++ if (mac_dev->fm_rtc_enable) ++ mac_dev->fm_rtc_enable(mac_dev->fm_dev); ++} ++ ++/* PTP clock operations */ ++ ++static int ptp_dpa_adjfreq(struct ptp_clock_info *ptp, s32 ppb) ++{ ++ u64 adj; ++ u32 diff, tmr_add; ++ int neg_adj = 0; ++ ++ if (ppb < 0) { ++ neg_adj = 1; ++ ppb = -ppb; ++ } ++ ++ tmr_add = freqCompensation; ++ adj = tmr_add; ++ adj *= ppb; ++ diff = div_u64(adj, 1000000000ULL); ++ ++ tmr_add = neg_adj ? tmr_add - diff : tmr_add + diff; ++ ++ if (mac_dev->fm_rtc_set_drift) ++ mac_dev->fm_rtc_set_drift(mac_dev->fm_dev, tmr_add); ++ ++ return 0; ++} ++ ++static int ptp_dpa_adjtime(struct ptp_clock_info *ptp, s64 delta) ++{ ++ s64 now; ++ ++ if (mac_dev->fm_rtc_get_cnt) ++ mac_dev->fm_rtc_get_cnt(mac_dev->fm_dev, &now); ++ ++ now += delta; ++ ++ if (mac_dev->fm_rtc_set_cnt) ++ mac_dev->fm_rtc_set_cnt(mac_dev->fm_dev, now); ++ set_fipers(); ++ ++ return 0; ++} ++ ++static int ptp_dpa_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) ++{ ++ u64 ns; ++ u32 remainder; ++ ++ if (mac_dev->fm_rtc_get_cnt) ++ mac_dev->fm_rtc_get_cnt(mac_dev->fm_dev, &ns); ++ ++ ts->tv_sec = div_u64_rem(ns, 1000000000, &remainder); ++ ts->tv_nsec = remainder; ++ return 0; ++} ++ ++static int ptp_dpa_settime(struct ptp_clock_info *ptp, ++ const struct timespec64 *ts) ++{ ++ u64 ns; ++ ++ ns = ts->tv_sec * 1000000000ULL; ++ ns += ts->tv_nsec; ++ ++ if (mac_dev->fm_rtc_set_cnt) ++ mac_dev->fm_rtc_set_cnt(mac_dev->fm_dev, ns); ++ set_fipers(); ++ return 0; ++} ++ ++static int ptp_dpa_enable(struct ptp_clock_info *ptp, ++ struct ptp_clock_request *rq, int on) ++{ ++ u32 bit; ++ ++ switch (rq->type) { ++ case PTP_CLK_REQ_EXTTS: ++ switch (rq->extts.index) { ++ case 0: ++ bit = ETS1EN; ++ break; ++ case 1: ++ bit = ETS2EN; ++ break; ++ default: ++ return -EINVAL; ++ } ++ if (on) { ++ if (mac_dev->fm_rtc_enable_interrupt) ++ mac_dev->fm_rtc_enable_interrupt( ++ mac_dev->fm_dev, bit); ++ } else { ++ if (mac_dev->fm_rtc_disable_interrupt) ++ mac_dev->fm_rtc_disable_interrupt( ++ mac_dev->fm_dev, bit); ++ } ++ return 0; ++ ++ case PTP_CLK_REQ_PPS: ++ if (on) { ++ if (mac_dev->fm_rtc_enable_interrupt) ++ mac_dev->fm_rtc_enable_interrupt( ++ mac_dev->fm_dev, PP1EN); ++ } else { ++ if (mac_dev->fm_rtc_disable_interrupt) ++ mac_dev->fm_rtc_disable_interrupt( ++ mac_dev->fm_dev, PP1EN); ++ } ++ return 0; ++ ++ default: ++ break; ++ } ++ ++ return -EOPNOTSUPP; ++} ++ ++static struct ptp_clock_info ptp_dpa_caps = { ++ .owner = THIS_MODULE, ++ .name = "dpaa clock", ++ .max_adj = 512000, ++ .n_alarm = 0, ++ .n_ext_ts = N_EXT_TS, ++ .n_per_out = 0, ++ .pps = 1, ++ .adjfreq = ptp_dpa_adjfreq, ++ .adjtime = ptp_dpa_adjtime, ++ .gettime64 = ptp_dpa_gettime, ++ .settime64 = ptp_dpa_settime, ++ .enable = ptp_dpa_enable, ++}; ++ ++static int __init __cold dpa_ptp_load(void) ++{ ++ struct device *ptp_dev; ++ struct timespec64 now; ++ int dpa_phc_index; ++ int err; ++ ++ if (!(ptp_priv.of_dev && ptp_priv.mac_dev)) ++ return -ENODEV; ++ ++ ptp_dev = &ptp_priv.of_dev->dev; ++ mac_dev = ptp_priv.mac_dev; ++ ++ if (mac_dev->fm_rtc_get_drift) ++ mac_dev->fm_rtc_get_drift(mac_dev->fm_dev, &freqCompensation); ++ ++ getnstimeofday64(&now); ++ ptp_dpa_settime(&ptp_dpa_caps, &now); ++ ++ clock = ptp_clock_register(&ptp_dpa_caps, ptp_dev); ++ if (IS_ERR(clock)) { ++ err = PTR_ERR(clock); ++ return err; ++ } ++ dpa_phc_index = ptp_clock_index(clock); ++ return 0; ++} ++module_init(dpa_ptp_load); ++ ++static void __exit __cold dpa_ptp_unload(void) ++{ ++ if (mac_dev->fm_rtc_disable_interrupt) ++ mac_dev->fm_rtc_disable_interrupt(mac_dev->fm_dev, 0xffffffff); ++ ptp_clock_unregister(clock); ++} ++module_exit(dpa_ptp_unload); +--- /dev/null ++++ b/drivers/net/ethernet/freescale/sdk_dpaa/mac-api.c +@@ -0,0 +1,909 @@ ++/* Copyright 2008-2012 Freescale Semiconductor, Inc. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * * Neither the name of Freescale Semiconductor nor the ++ * names of its contributors may be used to endorse or promote products ++ * derived from this software without specific prior written permission. ++ * ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") as published by the Free Software ++ * Foundation, either version 2 of that License or (at your option) any ++ * later version. ++ * ++ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. ++ */ ++ ++#ifdef CONFIG_FSL_DPAA_ETH_DEBUG ++#define pr_fmt(fmt) \ ++ KBUILD_MODNAME ": %s:%hu:%s() " fmt, \ ++ KBUILD_BASENAME".c", __LINE__, __func__ ++#else ++#define pr_fmt(fmt) \ ++ KBUILD_MODNAME ": " fmt ++#endif ++ ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/io.h> ++#include <linux/of_platform.h> ++#include <linux/of_mdio.h> ++#include <linux/phy.h> ++#include <linux/netdevice.h> ++ ++#include "dpaa_eth.h" ++#include "mac.h" ++#include "lnxwrp_fsl_fman.h" ++ ++#include "error_ext.h" /* GET_ERROR_TYPE, E_OK */ ++ ++#include "fsl_fman_dtsec.h" ++#include "fsl_fman_tgec.h" ++#include "fsl_fman_memac.h" ++#include "../sdk_fman/src/wrapper/lnxwrp_sysfs_fm.h" ++ ++#define MAC_DESCRIPTION "FSL FMan MAC API based driver" ++ ++MODULE_LICENSE("Dual BSD/GPL"); ++ ++MODULE_AUTHOR("Emil Medve <Emilian.Medve@Freescale.com>"); ++ ++MODULE_DESCRIPTION(MAC_DESCRIPTION); ++ ++struct mac_priv_s { ++ struct fm_mac_dev *fm_mac; ++}; ++ ++const char *mac_driver_description __initconst = MAC_DESCRIPTION; ++const size_t mac_sizeof_priv[] = { ++ [DTSEC] = sizeof(struct mac_priv_s), ++ [XGMAC] = sizeof(struct mac_priv_s), ++ [MEMAC] = sizeof(struct mac_priv_s) ++}; ++ ++static const enet_mode_t _100[] = { ++ [PHY_INTERFACE_MODE_MII] = e_ENET_MODE_MII_100, ++ [PHY_INTERFACE_MODE_RMII] = e_ENET_MODE_RMII_100 ++}; ++ ++static const enet_mode_t _1000[] = { ++ [PHY_INTERFACE_MODE_GMII] = e_ENET_MODE_GMII_1000, ++ [PHY_INTERFACE_MODE_SGMII] = e_ENET_MODE_SGMII_1000, ++ [PHY_INTERFACE_MODE_QSGMII] = e_ENET_MODE_QSGMII_1000, ++ [PHY_INTERFACE_MODE_TBI] = e_ENET_MODE_TBI_1000, ++ [PHY_INTERFACE_MODE_RGMII] = e_ENET_MODE_RGMII_1000, ++ [PHY_INTERFACE_MODE_RGMII_ID] = e_ENET_MODE_RGMII_1000, ++ [PHY_INTERFACE_MODE_RGMII_RXID] = e_ENET_MODE_RGMII_1000, ++ [PHY_INTERFACE_MODE_RGMII_TXID] = e_ENET_MODE_RGMII_1000, ++ [PHY_INTERFACE_MODE_RTBI] = e_ENET_MODE_RTBI_1000 ++}; ++ ++static enet_mode_t __cold __attribute__((nonnull)) ++macdev2enetinterface(const struct mac_device *mac_dev) ++{ ++ switch (mac_dev->max_speed) { ++ case SPEED_100: ++ return _100[mac_dev->phy_if]; ++ case SPEED_1000: ++ return _1000[mac_dev->phy_if]; ++ case SPEED_2500: ++ return e_ENET_MODE_SGMII_2500; ++ case SPEED_10000: ++ return e_ENET_MODE_XGMII_10000; ++ default: ++ return e_ENET_MODE_MII_100; ++ } ++} ++ ++static void mac_exception(handle_t _mac_dev, e_FmMacExceptions exception) ++{ ++ struct mac_device *mac_dev; ++ ++ mac_dev = (struct mac_device *)_mac_dev; ++ ++ if (e_FM_MAC_EX_10G_RX_FIFO_OVFL == exception) { ++ /* don't flag RX FIFO after the first */ ++ fm_mac_set_exception(mac_dev->get_mac_handle(mac_dev), ++ e_FM_MAC_EX_10G_RX_FIFO_OVFL, false); ++ dev_err(mac_dev->dev, "10G MAC got RX FIFO Error = %x\n", ++ exception); ++ } ++ ++ dev_dbg(mac_dev->dev, "%s:%s() -> %d\n", KBUILD_BASENAME".c", __func__, ++ exception); ++} ++ ++static int __cold init(struct mac_device *mac_dev) ++{ ++ int _errno; ++ struct mac_priv_s *priv; ++ t_FmMacParams param; ++ uint32_t version; ++ ++ priv = macdev_priv(mac_dev); ++ ++ param.baseAddr = (typeof(param.baseAddr))(uintptr_t)devm_ioremap( ++ mac_dev->dev, mac_dev->res->start, 0x2000); ++ param.enetMode = macdev2enetinterface(mac_dev); ++ memcpy(¶m.addr, mac_dev->addr, min(sizeof(param.addr), ++ sizeof(mac_dev->addr))); ++ param.macId = mac_dev->cell_index; ++ param.h_Fm = (handle_t)mac_dev->fm; ++ param.mdioIrq = NO_IRQ; ++ param.f_Exception = mac_exception; ++ param.f_Event = mac_exception; ++ param.h_App = mac_dev; ++ ++ priv->fm_mac = fm_mac_config(¶m); ++ if (unlikely(priv->fm_mac == NULL)) { ++ _errno = -EINVAL; ++ goto _return; ++ } ++ ++ fm_mac_set_handle(mac_dev->fm_dev, priv->fm_mac, ++ (macdev2enetinterface(mac_dev) != e_ENET_MODE_XGMII_10000) ? ++ param.macId : param.macId + FM_MAX_NUM_OF_1G_MACS); ++ ++ _errno = fm_mac_config_max_frame_length(priv->fm_mac, ++ fm_get_max_frm()); ++ if (unlikely(_errno < 0)) ++ goto _return_fm_mac_free; ++ ++ if (macdev2enetinterface(mac_dev) != e_ENET_MODE_XGMII_10000) { ++ /* 10G always works with pad and CRC */ ++ _errno = fm_mac_config_pad_and_crc(priv->fm_mac, true); ++ if (unlikely(_errno < 0)) ++ goto _return_fm_mac_free; ++ ++ _errno = fm_mac_config_half_duplex(priv->fm_mac, ++ mac_dev->half_duplex); ++ if (unlikely(_errno < 0)) ++ goto _return_fm_mac_free; ++ } else { ++ _errno = fm_mac_config_reset_on_init(priv->fm_mac, true); ++ if (unlikely(_errno < 0)) ++ goto _return_fm_mac_free; ++ } ++ ++ _errno = fm_mac_init(priv->fm_mac); ++ if (unlikely(_errno < 0)) ++ goto _return_fm_mac_free; ++ ++#ifndef CONFIG_FMAN_MIB_CNT_OVF_IRQ_EN ++ /* For 1G MAC, disable by default the MIB counters overflow interrupt */ ++ if (macdev2enetinterface(mac_dev) != e_ENET_MODE_XGMII_10000) { ++ _errno = fm_mac_set_exception(mac_dev->get_mac_handle(mac_dev), ++ e_FM_MAC_EX_1G_RX_MIB_CNT_OVFL, FALSE); ++ if (unlikely(_errno < 0)) ++ goto _return_fm_mac_free; ++ } ++#endif /* !CONFIG_FMAN_MIB_CNT_OVF_IRQ_EN */ ++ ++ /* For 10G MAC, disable Tx ECC exception */ ++ if (macdev2enetinterface(mac_dev) == e_ENET_MODE_XGMII_10000) { ++ _errno = fm_mac_set_exception(mac_dev->get_mac_handle(mac_dev), ++ e_FM_MAC_EX_10G_1TX_ECC_ER, FALSE); ++ if (unlikely(_errno < 0)) ++ goto _return_fm_mac_free; ++ } ++ ++ _errno = fm_mac_get_version(priv->fm_mac, &version); ++ if (unlikely(_errno < 0)) ++ goto _return_fm_mac_free; ++ ++ dev_info(mac_dev->dev, "FMan %s version: 0x%08x\n", ++ ((macdev2enetinterface(mac_dev) != e_ENET_MODE_XGMII_10000) ? ++ "dTSEC" : "XGEC"), version); ++ ++ goto _return; ++ ++ ++_return_fm_mac_free: ++ fm_mac_free(mac_dev->get_mac_handle(mac_dev)); ++ ++_return: ++ return _errno; ++} ++ ++static int __cold memac_init(struct mac_device *mac_dev) ++{ ++ int _errno; ++ struct mac_priv_s *priv; ++ t_FmMacParams param; ++ ++ priv = macdev_priv(mac_dev); ++ ++ param.baseAddr = (typeof(param.baseAddr))(uintptr_t)devm_ioremap( ++ mac_dev->dev, mac_dev->res->start, 0x2000); ++ param.enetMode = macdev2enetinterface(mac_dev); ++ memcpy(¶m.addr, mac_dev->addr, sizeof(mac_dev->addr)); ++ param.macId = mac_dev->cell_index; ++ param.h_Fm = (handle_t)mac_dev->fm; ++ param.mdioIrq = NO_IRQ; ++ param.f_Exception = mac_exception; ++ param.f_Event = mac_exception; ++ param.h_App = mac_dev; ++ ++ priv->fm_mac = fm_mac_config(¶m); ++ if (unlikely(priv->fm_mac == NULL)) { ++ _errno = -EINVAL; ++ goto _return; ++ } ++ ++ fm_mac_set_handle(mac_dev->fm_dev, priv->fm_mac, ++ (macdev2enetinterface(mac_dev) != e_ENET_MODE_XGMII_10000) ? ++ param.macId : param.macId + FM_MAX_NUM_OF_1G_MACS); ++ ++ _errno = fm_mac_config_max_frame_length(priv->fm_mac, fm_get_max_frm()); ++ if (unlikely(_errno < 0)) ++ goto _return_fm_mac_free; ++ ++ _errno = fm_mac_config_reset_on_init(priv->fm_mac, true); ++ if (unlikely(_errno < 0)) ++ goto _return_fm_mac_free; ++ ++ _errno = fm_mac_init(priv->fm_mac); ++ if (unlikely(_errno < 0)) ++ goto _return_fm_mac_free; ++ ++ dev_info(mac_dev->dev, "FMan MEMAC\n"); ++ ++ goto _return; ++ ++_return_fm_mac_free: ++ fm_mac_free(priv->fm_mac); ++ ++_return: ++ return _errno; ++} ++ ++static int __cold start(struct mac_device *mac_dev) ++{ ++ int _errno; ++ struct phy_device *phy_dev = mac_dev->phy_dev; ++ ++ _errno = fm_mac_enable(mac_dev->get_mac_handle(mac_dev)); ++ ++ if (!_errno && phy_dev) ++ phy_start(phy_dev); ++ ++ return _errno; ++} ++ ++static int __cold stop(struct mac_device *mac_dev) ++{ ++ if (mac_dev->phy_dev) ++ phy_stop(mac_dev->phy_dev); ++ ++ return fm_mac_disable(mac_dev->get_mac_handle(mac_dev)); ++} ++ ++static int __cold set_multi(struct net_device *net_dev, ++ struct mac_device *mac_dev) ++{ ++ struct mac_priv_s *mac_priv; ++ struct mac_address *old_addr, *tmp; ++ struct netdev_hw_addr *ha; ++ int _errno; ++ ++ mac_priv = macdev_priv(mac_dev); ++ ++ /* Clear previous address list */ ++ list_for_each_entry_safe(old_addr, tmp, &mac_dev->mc_addr_list, list) { ++ _errno = fm_mac_remove_hash_mac_addr(mac_priv->fm_mac, ++ (t_EnetAddr *)old_addr->addr); ++ if (_errno < 0) ++ return _errno; ++ ++ list_del(&old_addr->list); ++ kfree(old_addr); ++ } ++ ++ /* Add all the addresses from the new list */ ++ netdev_for_each_mc_addr(ha, net_dev) { ++ _errno = fm_mac_add_hash_mac_addr(mac_priv->fm_mac, ++ (t_EnetAddr *)ha->addr); ++ if (_errno < 0) ++ return _errno; ++ ++ tmp = kmalloc(sizeof(struct mac_address), GFP_ATOMIC); ++ if (!tmp) { ++ dev_err(mac_dev->dev, "Out of memory\n"); ++ return -ENOMEM; ++ } ++ memcpy(tmp->addr, ha->addr, ETH_ALEN); ++ list_add(&tmp->list, &mac_dev->mc_addr_list); ++ } ++ return 0; ++} ++ ++/* Avoid redundant calls to FMD, if the MAC driver already contains the desired ++ * active PAUSE settings. Otherwise, the new active settings should be reflected ++ * in FMan. ++ */ ++int set_mac_active_pause(struct mac_device *mac_dev, bool rx, bool tx) ++{ ++ struct fm_mac_dev *fm_mac_dev = mac_dev->get_mac_handle(mac_dev); ++ int _errno = 0; ++ ++ if (unlikely(rx != mac_dev->rx_pause_active)) { ++ _errno = fm_mac_set_rx_pause_frames(fm_mac_dev, rx); ++ if (likely(_errno == 0)) ++ mac_dev->rx_pause_active = rx; ++ } ++ ++ if (unlikely(tx != mac_dev->tx_pause_active)) { ++ _errno = fm_mac_set_tx_pause_frames(fm_mac_dev, tx); ++ if (likely(_errno == 0)) ++ mac_dev->tx_pause_active = tx; ++ } ++ ++ return _errno; ++} ++EXPORT_SYMBOL(set_mac_active_pause); ++ ++/* Determine the MAC RX/TX PAUSE frames settings based on PHY ++ * autonegotiation or values set by eththool. ++ */ ++void get_pause_cfg(struct mac_device *mac_dev, bool *rx_pause, bool *tx_pause) ++{ ++ struct phy_device *phy_dev = mac_dev->phy_dev; ++ u16 lcl_adv, rmt_adv; ++ u8 flowctrl; ++ ++ *rx_pause = *tx_pause = false; ++ ++ if (!phy_dev->duplex) ++ return; ++ ++ /* If PAUSE autonegotiation is disabled, the TX/RX PAUSE settings ++ * are those set by ethtool. ++ */ ++ if (!mac_dev->autoneg_pause) { ++ *rx_pause = mac_dev->rx_pause_req; ++ *tx_pause = mac_dev->tx_pause_req; ++ return; ++ } ++ ++ /* Else if PAUSE autonegotiation is enabled, the TX/RX PAUSE ++ * settings depend on the result of the link negotiation. ++ */ ++ ++ /* get local capabilities */ ++ lcl_adv = 0; ++ if (phy_dev->advertising & ADVERTISED_Pause) ++ lcl_adv |= ADVERTISE_PAUSE_CAP; ++ if (phy_dev->advertising & ADVERTISED_Asym_Pause) ++ lcl_adv |= ADVERTISE_PAUSE_ASYM; ++ ++ /* get link partner capabilities */ ++ rmt_adv = 0; ++ if (phy_dev->pause) ++ rmt_adv |= LPA_PAUSE_CAP; ++ if (phy_dev->asym_pause) ++ rmt_adv |= LPA_PAUSE_ASYM; ++ ++ /* Calculate TX/RX settings based on local and peer advertised ++ * symmetric/asymmetric PAUSE capabilities. ++ */ ++ flowctrl = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv); ++ if (flowctrl & FLOW_CTRL_RX) ++ *rx_pause = true; ++ if (flowctrl & FLOW_CTRL_TX) ++ *tx_pause = true; ++} ++EXPORT_SYMBOL(get_pause_cfg); ++ ++static void adjust_link_void(struct net_device *net_dev) ++{ ++} ++ ++static void adjust_link(struct net_device *net_dev) ++{ ++ struct dpa_priv_s *priv = netdev_priv(net_dev); ++ struct mac_device *mac_dev = priv->mac_dev; ++ struct phy_device *phy_dev = mac_dev->phy_dev; ++ struct fm_mac_dev *fm_mac_dev; ++ bool rx_pause, tx_pause; ++ int _errno; ++ ++ fm_mac_dev = mac_dev->get_mac_handle(mac_dev); ++ fm_mac_adjust_link(fm_mac_dev, phy_dev->link, phy_dev->speed, ++ phy_dev->duplex); ++ ++ get_pause_cfg(mac_dev, &rx_pause, &tx_pause); ++ _errno = set_mac_active_pause(mac_dev, rx_pause, tx_pause); ++ if (unlikely(_errno < 0)) ++ netdev_err(net_dev, "set_mac_active_pause() = %d\n", _errno); ++} ++ ++/* Initializes driver's PHY state, and attaches to the PHY. ++ * Returns 0 on success. ++ */ ++static int dtsec_init_phy(struct net_device *net_dev, ++ struct mac_device *mac_dev) ++{ ++ struct phy_device *phy_dev; ++ ++ if (of_phy_is_fixed_link(mac_dev->phy_node)) ++ phy_dev = of_phy_attach(net_dev, mac_dev->phy_node, ++ 0, mac_dev->phy_if); ++ else ++ phy_dev = of_phy_connect(net_dev, mac_dev->phy_node, ++ &adjust_link, 0, mac_dev->phy_if); ++ if (unlikely(phy_dev == NULL) || IS_ERR(phy_dev)) { ++ netdev_err(net_dev, "Could not connect to PHY %s\n", ++ mac_dev->phy_node ? ++ mac_dev->phy_node->full_name : ++ mac_dev->fixed_bus_id); ++ return phy_dev == NULL ? -ENODEV : PTR_ERR(phy_dev); ++ } ++ ++ /* Remove any features not supported by the controller */ ++ phy_dev->supported &= mac_dev->if_support; ++ /* Enable the symmetric and asymmetric PAUSE frame advertisements, ++ * as most of the PHY drivers do not enable them by default. ++ */ ++ phy_dev->supported |= (SUPPORTED_Pause | SUPPORTED_Asym_Pause); ++ phy_dev->advertising = phy_dev->supported; ++ ++ mac_dev->phy_dev = phy_dev; ++ ++ return 0; ++} ++ ++static int xgmac_init_phy(struct net_device *net_dev, ++ struct mac_device *mac_dev) ++{ ++ struct phy_device *phy_dev; ++ ++ if (of_phy_is_fixed_link(mac_dev->phy_node)) ++ phy_dev = of_phy_attach(net_dev, mac_dev->phy_node, ++ 0, mac_dev->phy_if); ++ else ++ phy_dev = of_phy_connect(net_dev, mac_dev->phy_node, ++ &adjust_link_void, 0, mac_dev->phy_if); ++ if (unlikely(phy_dev == NULL) || IS_ERR(phy_dev)) { ++ netdev_err(net_dev, "Could not attach to PHY %s\n", ++ mac_dev->phy_node ? ++ mac_dev->phy_node->full_name : ++ mac_dev->fixed_bus_id); ++ return phy_dev == NULL ? -ENODEV : PTR_ERR(phy_dev); ++ } ++ ++ phy_dev->supported &= mac_dev->if_support; ++ /* Enable the symmetric and asymmetric PAUSE frame advertisements, ++ * as most of the PHY drivers do not enable them by default. ++ */ ++ phy_dev->supported |= (SUPPORTED_Pause | SUPPORTED_Asym_Pause); ++ phy_dev->advertising = phy_dev->supported; ++ ++ mac_dev->phy_dev = phy_dev; ++ ++ return 0; ++} ++ ++static int memac_init_phy(struct net_device *net_dev, ++ struct mac_device *mac_dev) ++{ ++ struct phy_device *phy_dev; ++ ++ if (of_phy_is_fixed_link(mac_dev->phy_node)) { ++ phy_dev = of_phy_attach(net_dev, mac_dev->phy_node, ++ 0, mac_dev->phy_if); ++ } else if ((macdev2enetinterface(mac_dev) == e_ENET_MODE_XGMII_10000) || ++ (macdev2enetinterface(mac_dev) == e_ENET_MODE_SGMII_2500)) { ++ phy_dev = of_phy_connect(net_dev, mac_dev->phy_node, ++ &adjust_link_void, 0, ++ mac_dev->phy_if); ++ } else { ++ phy_dev = of_phy_connect(net_dev, mac_dev->phy_node, ++ &adjust_link, 0, mac_dev->phy_if); ++ } ++ ++ if (unlikely(phy_dev == NULL) || IS_ERR(phy_dev)) { ++ netdev_err(net_dev, "Could not connect to PHY %s\n", ++ mac_dev->phy_node ? ++ mac_dev->phy_node->full_name : ++ mac_dev->fixed_bus_id); ++ return phy_dev == NULL ? -ENODEV : PTR_ERR(phy_dev); ++ } ++ ++ /* Remove any features not supported by the controller */ ++ phy_dev->supported &= mac_dev->if_support; ++ /* Enable the symmetric and asymmetric PAUSE frame advertisements, ++ * as most of the PHY drivers do not enable them by default. ++ */ ++ phy_dev->supported |= (SUPPORTED_Pause | SUPPORTED_Asym_Pause); ++ phy_dev->advertising = phy_dev->supported; ++ ++ mac_dev->phy_dev = phy_dev; ++ ++ return 0; ++} ++ ++static int __cold uninit(struct fm_mac_dev *fm_mac_dev) ++{ ++ int _errno, __errno; ++ ++ _errno = fm_mac_disable(fm_mac_dev); ++ __errno = fm_mac_free(fm_mac_dev); ++ ++ if (unlikely(__errno < 0)) ++ _errno = __errno; ++ ++ return _errno; ++} ++ ++static struct fm_mac_dev *get_mac_handle(struct mac_device *mac_dev) ++{ ++ const struct mac_priv_s *priv; ++ priv = macdev_priv(mac_dev); ++ return priv->fm_mac; ++} ++ ++static int dtsec_dump_regs(struct mac_device *h_mac, char *buf, int nn) ++{ ++ struct dtsec_regs *p_mm = (struct dtsec_regs *) h_mac->vaddr; ++ int i = 0, n = nn; ++ ++ FM_DMP_SUBTITLE(buf, n, "\n"); ++ ++ FM_DMP_TITLE(buf, n, p_mm, "FM MAC - DTSEC-%d", h_mac->cell_index); ++ ++ FM_DMP_V32(buf, n, p_mm, tsec_id); ++ FM_DMP_V32(buf, n, p_mm, tsec_id2); ++ FM_DMP_V32(buf, n, p_mm, ievent); ++ FM_DMP_V32(buf, n, p_mm, imask); ++ FM_DMP_V32(buf, n, p_mm, ecntrl); ++ FM_DMP_V32(buf, n, p_mm, ptv); ++ FM_DMP_V32(buf, n, p_mm, tmr_ctrl); ++ FM_DMP_V32(buf, n, p_mm, tmr_pevent); ++ FM_DMP_V32(buf, n, p_mm, tmr_pemask); ++ FM_DMP_V32(buf, n, p_mm, tctrl); ++ FM_DMP_V32(buf, n, p_mm, rctrl); ++ FM_DMP_V32(buf, n, p_mm, maccfg1); ++ FM_DMP_V32(buf, n, p_mm, maccfg2); ++ FM_DMP_V32(buf, n, p_mm, ipgifg); ++ FM_DMP_V32(buf, n, p_mm, hafdup); ++ FM_DMP_V32(buf, n, p_mm, maxfrm); ++ ++ FM_DMP_V32(buf, n, p_mm, macstnaddr1); ++ FM_DMP_V32(buf, n, p_mm, macstnaddr2); ++ ++ for (i = 0; i < 7; ++i) { ++ FM_DMP_V32(buf, n, p_mm, macaddr[i].exact_match1); ++ FM_DMP_V32(buf, n, p_mm, macaddr[i].exact_match2); ++ } ++ ++ FM_DMP_V32(buf, n, p_mm, car1); ++ FM_DMP_V32(buf, n, p_mm, car2); ++ ++ return n; ++} ++ ++static int xgmac_dump_regs(struct mac_device *h_mac, char *buf, int nn) ++{ ++ struct tgec_regs *p_mm = (struct tgec_regs *) h_mac->vaddr; ++ int n = nn; ++ ++ FM_DMP_SUBTITLE(buf, n, "\n"); ++ FM_DMP_TITLE(buf, n, p_mm, "FM MAC - TGEC -%d", h_mac->cell_index); ++ ++ FM_DMP_V32(buf, n, p_mm, tgec_id); ++ FM_DMP_V32(buf, n, p_mm, command_config); ++ FM_DMP_V32(buf, n, p_mm, mac_addr_0); ++ FM_DMP_V32(buf, n, p_mm, mac_addr_1); ++ FM_DMP_V32(buf, n, p_mm, maxfrm); ++ FM_DMP_V32(buf, n, p_mm, pause_quant); ++ FM_DMP_V32(buf, n, p_mm, rx_fifo_sections); ++ FM_DMP_V32(buf, n, p_mm, tx_fifo_sections); ++ FM_DMP_V32(buf, n, p_mm, rx_fifo_almost_f_e); ++ FM_DMP_V32(buf, n, p_mm, tx_fifo_almost_f_e); ++ FM_DMP_V32(buf, n, p_mm, hashtable_ctrl); ++ FM_DMP_V32(buf, n, p_mm, mdio_cfg_status); ++ FM_DMP_V32(buf, n, p_mm, mdio_command); ++ FM_DMP_V32(buf, n, p_mm, mdio_data); ++ FM_DMP_V32(buf, n, p_mm, mdio_regaddr); ++ FM_DMP_V32(buf, n, p_mm, status); ++ FM_DMP_V32(buf, n, p_mm, tx_ipg_len); ++ FM_DMP_V32(buf, n, p_mm, mac_addr_2); ++ FM_DMP_V32(buf, n, p_mm, mac_addr_3); ++ FM_DMP_V32(buf, n, p_mm, rx_fifo_ptr_rd); ++ FM_DMP_V32(buf, n, p_mm, rx_fifo_ptr_wr); ++ FM_DMP_V32(buf, n, p_mm, tx_fifo_ptr_rd); ++ FM_DMP_V32(buf, n, p_mm, tx_fifo_ptr_wr); ++ FM_DMP_V32(buf, n, p_mm, imask); ++ FM_DMP_V32(buf, n, p_mm, ievent); ++ ++ return n; ++} ++ ++static int memac_dump_regs(struct mac_device *h_mac, char *buf, int nn) ++{ ++ struct memac_regs *p_mm = (struct memac_regs *) h_mac->vaddr; ++ int i = 0, n = nn; ++ ++ FM_DMP_SUBTITLE(buf, n, "\n"); ++ FM_DMP_TITLE(buf, n, p_mm, "FM MAC - MEMAC -%d", h_mac->cell_index); ++ ++ FM_DMP_V32(buf, n, p_mm, command_config); ++ FM_DMP_V32(buf, n, p_mm, mac_addr0.mac_addr_l); ++ FM_DMP_V32(buf, n, p_mm, mac_addr0.mac_addr_u); ++ FM_DMP_V32(buf, n, p_mm, maxfrm); ++ FM_DMP_V32(buf, n, p_mm, hashtable_ctrl); ++ FM_DMP_V32(buf, n, p_mm, ievent); ++ FM_DMP_V32(buf, n, p_mm, tx_ipg_length); ++ FM_DMP_V32(buf, n, p_mm, imask); ++ ++ for (i = 0; i < 4; ++i) ++ FM_DMP_V32(buf, n, p_mm, pause_quanta[i]); ++ ++ for (i = 0; i < 4; ++i) ++ FM_DMP_V32(buf, n, p_mm, pause_thresh[i]); ++ ++ FM_DMP_V32(buf, n, p_mm, rx_pause_status); ++ ++ for (i = 0; i < MEMAC_NUM_OF_PADDRS; ++i) { ++ FM_DMP_V32(buf, n, p_mm, mac_addr[i].mac_addr_l); ++ FM_DMP_V32(buf, n, p_mm, mac_addr[i].mac_addr_u); ++ } ++ ++ FM_DMP_V32(buf, n, p_mm, lpwake_timer); ++ FM_DMP_V32(buf, n, p_mm, sleep_timer); ++ FM_DMP_V32(buf, n, p_mm, statn_config); ++ FM_DMP_V32(buf, n, p_mm, if_mode); ++ FM_DMP_V32(buf, n, p_mm, if_status); ++ FM_DMP_V32(buf, n, p_mm, hg_config); ++ FM_DMP_V32(buf, n, p_mm, hg_pause_quanta); ++ FM_DMP_V32(buf, n, p_mm, hg_pause_thresh); ++ FM_DMP_V32(buf, n, p_mm, hgrx_pause_status); ++ FM_DMP_V32(buf, n, p_mm, hg_fifos_status); ++ FM_DMP_V32(buf, n, p_mm, rhm); ++ FM_DMP_V32(buf, n, p_mm, thm); ++ ++ return n; ++} ++ ++static int memac_dump_regs_rx(struct mac_device *h_mac, char *buf, int nn) ++{ ++ struct memac_regs *p_mm = (struct memac_regs *) h_mac->vaddr; ++ int n = nn; ++ ++ FM_DMP_SUBTITLE(buf, n, "\n"); ++ FM_DMP_TITLE(buf, n, p_mm, "FM MAC - MEMAC -%d Rx stats", h_mac->cell_index); ++ ++ /* Rx Statistics Counter */ ++ FM_DMP_V32(buf, n, p_mm, reoct_l); ++ FM_DMP_V32(buf, n, p_mm, reoct_u); ++ FM_DMP_V32(buf, n, p_mm, roct_l); ++ FM_DMP_V32(buf, n, p_mm, roct_u); ++ FM_DMP_V32(buf, n, p_mm, raln_l); ++ FM_DMP_V32(buf, n, p_mm, raln_u); ++ FM_DMP_V32(buf, n, p_mm, rxpf_l); ++ FM_DMP_V32(buf, n, p_mm, rxpf_u); ++ FM_DMP_V32(buf, n, p_mm, rfrm_l); ++ FM_DMP_V32(buf, n, p_mm, rfrm_u); ++ FM_DMP_V32(buf, n, p_mm, rfcs_l); ++ FM_DMP_V32(buf, n, p_mm, rfcs_u); ++ FM_DMP_V32(buf, n, p_mm, rvlan_l); ++ FM_DMP_V32(buf, n, p_mm, rvlan_u); ++ FM_DMP_V32(buf, n, p_mm, rerr_l); ++ FM_DMP_V32(buf, n, p_mm, rerr_u); ++ FM_DMP_V32(buf, n, p_mm, ruca_l); ++ FM_DMP_V32(buf, n, p_mm, ruca_u); ++ FM_DMP_V32(buf, n, p_mm, rmca_l); ++ FM_DMP_V32(buf, n, p_mm, rmca_u); ++ FM_DMP_V32(buf, n, p_mm, rbca_l); ++ FM_DMP_V32(buf, n, p_mm, rbca_u); ++ FM_DMP_V32(buf, n, p_mm, rdrp_l); ++ FM_DMP_V32(buf, n, p_mm, rdrp_u); ++ FM_DMP_V32(buf, n, p_mm, rpkt_l); ++ FM_DMP_V32(buf, n, p_mm, rpkt_u); ++ FM_DMP_V32(buf, n, p_mm, rund_l); ++ FM_DMP_V32(buf, n, p_mm, rund_u); ++ FM_DMP_V32(buf, n, p_mm, r64_l); ++ FM_DMP_V32(buf, n, p_mm, r64_u); ++ FM_DMP_V32(buf, n, p_mm, r127_l); ++ FM_DMP_V32(buf, n, p_mm, r127_u); ++ FM_DMP_V32(buf, n, p_mm, r255_l); ++ FM_DMP_V32(buf, n, p_mm, r255_u); ++ FM_DMP_V32(buf, n, p_mm, r511_l); ++ FM_DMP_V32(buf, n, p_mm, r511_u); ++ FM_DMP_V32(buf, n, p_mm, r1023_l); ++ FM_DMP_V32(buf, n, p_mm, r1023_u); ++ FM_DMP_V32(buf, n, p_mm, r1518_l); ++ FM_DMP_V32(buf, n, p_mm, r1518_u); ++ FM_DMP_V32(buf, n, p_mm, r1519x_l); ++ FM_DMP_V32(buf, n, p_mm, r1519x_u); ++ FM_DMP_V32(buf, n, p_mm, rovr_l); ++ FM_DMP_V32(buf, n, p_mm, rovr_u); ++ FM_DMP_V32(buf, n, p_mm, rjbr_l); ++ FM_DMP_V32(buf, n, p_mm, rjbr_u); ++ FM_DMP_V32(buf, n, p_mm, rfrg_l); ++ FM_DMP_V32(buf, n, p_mm, rfrg_u); ++ FM_DMP_V32(buf, n, p_mm, rcnp_l); ++ FM_DMP_V32(buf, n, p_mm, rcnp_u); ++ FM_DMP_V32(buf, n, p_mm, rdrntp_l); ++ FM_DMP_V32(buf, n, p_mm, rdrntp_u); ++ ++ return n; ++} ++ ++static int memac_dump_regs_tx(struct mac_device *h_mac, char *buf, int nn) ++{ ++ struct memac_regs *p_mm = (struct memac_regs *) h_mac->vaddr; ++ int n = nn; ++ ++ FM_DMP_SUBTITLE(buf, n, "\n"); ++ FM_DMP_TITLE(buf, n, p_mm, "FM MAC - MEMAC -%d Tx stats", h_mac->cell_index); ++ ++ ++ /* Tx Statistics Counter */ ++ FM_DMP_V32(buf, n, p_mm, teoct_l); ++ FM_DMP_V32(buf, n, p_mm, teoct_u); ++ FM_DMP_V32(buf, n, p_mm, toct_l); ++ FM_DMP_V32(buf, n, p_mm, toct_u); ++ FM_DMP_V32(buf, n, p_mm, txpf_l); ++ FM_DMP_V32(buf, n, p_mm, txpf_u); ++ FM_DMP_V32(buf, n, p_mm, tfrm_l); ++ FM_DMP_V32(buf, n, p_mm, tfrm_u); ++ FM_DMP_V32(buf, n, p_mm, tfcs_l); ++ FM_DMP_V32(buf, n, p_mm, tfcs_u); ++ FM_DMP_V32(buf, n, p_mm, tvlan_l); ++ FM_DMP_V32(buf, n, p_mm, tvlan_u); ++ FM_DMP_V32(buf, n, p_mm, terr_l); ++ FM_DMP_V32(buf, n, p_mm, terr_u); ++ FM_DMP_V32(buf, n, p_mm, tuca_l); ++ FM_DMP_V32(buf, n, p_mm, tuca_u); ++ FM_DMP_V32(buf, n, p_mm, tmca_l); ++ FM_DMP_V32(buf, n, p_mm, tmca_u); ++ FM_DMP_V32(buf, n, p_mm, tbca_l); ++ FM_DMP_V32(buf, n, p_mm, tbca_u); ++ FM_DMP_V32(buf, n, p_mm, tpkt_l); ++ FM_DMP_V32(buf, n, p_mm, tpkt_u); ++ FM_DMP_V32(buf, n, p_mm, tund_l); ++ FM_DMP_V32(buf, n, p_mm, tund_u); ++ FM_DMP_V32(buf, n, p_mm, t64_l); ++ FM_DMP_V32(buf, n, p_mm, t64_u); ++ FM_DMP_V32(buf, n, p_mm, t127_l); ++ FM_DMP_V32(buf, n, p_mm, t127_u); ++ FM_DMP_V32(buf, n, p_mm, t255_l); ++ FM_DMP_V32(buf, n, p_mm, t255_u); ++ FM_DMP_V32(buf, n, p_mm, t511_l); ++ FM_DMP_V32(buf, n, p_mm, t511_u); ++ FM_DMP_V32(buf, n, p_mm, t1023_l); ++ FM_DMP_V32(buf, n, p_mm, t1023_u); ++ FM_DMP_V32(buf, n, p_mm, t1518_l); ++ FM_DMP_V32(buf, n, p_mm, t1518_u); ++ FM_DMP_V32(buf, n, p_mm, t1519x_l); ++ FM_DMP_V32(buf, n, p_mm, t1519x_u); ++ FM_DMP_V32(buf, n, p_mm, tcnp_l); ++ FM_DMP_V32(buf, n, p_mm, tcnp_u); ++ ++ return n; ++} ++ ++int fm_mac_dump_regs(struct mac_device *h_mac, char *buf, int nn) ++{ ++ int n = nn; ++ ++ n = h_mac->dump_mac_regs(h_mac, buf, n); ++ ++ return n; ++} ++EXPORT_SYMBOL(fm_mac_dump_regs); ++ ++int fm_mac_dump_rx_stats(struct mac_device *h_mac, char *buf, int nn) ++{ ++ int n = nn; ++ ++ if(h_mac->dump_mac_rx_stats) ++ n = h_mac->dump_mac_rx_stats(h_mac, buf, n); ++ ++ return n; ++} ++EXPORT_SYMBOL(fm_mac_dump_rx_stats); ++ ++int fm_mac_dump_tx_stats(struct mac_device *h_mac, char *buf, int nn) ++{ ++ int n = nn; ++ ++ if(h_mac->dump_mac_tx_stats) ++ n = h_mac->dump_mac_tx_stats(h_mac, buf, n); ++ ++ return n; ++} ++EXPORT_SYMBOL(fm_mac_dump_tx_stats); ++ ++static void __cold setup_dtsec(struct mac_device *mac_dev) ++{ ++ mac_dev->init_phy = dtsec_init_phy; ++ mac_dev->init = init; ++ mac_dev->start = start; ++ mac_dev->stop = stop; ++ mac_dev->set_promisc = fm_mac_set_promiscuous; ++ mac_dev->change_addr = fm_mac_modify_mac_addr; ++ mac_dev->set_multi = set_multi; ++ mac_dev->uninit = uninit; ++ mac_dev->ptp_enable = fm_mac_enable_1588_time_stamp; ++ mac_dev->ptp_disable = fm_mac_disable_1588_time_stamp; ++ mac_dev->get_mac_handle = get_mac_handle; ++ mac_dev->set_tx_pause = fm_mac_set_tx_pause_frames; ++ mac_dev->set_rx_pause = fm_mac_set_rx_pause_frames; ++ mac_dev->fm_rtc_enable = fm_rtc_enable; ++ mac_dev->fm_rtc_disable = fm_rtc_disable; ++ mac_dev->fm_rtc_get_cnt = fm_rtc_get_cnt; ++ mac_dev->fm_rtc_set_cnt = fm_rtc_set_cnt; ++ mac_dev->fm_rtc_get_drift = fm_rtc_get_drift; ++ mac_dev->fm_rtc_set_drift = fm_rtc_set_drift; ++ mac_dev->fm_rtc_set_alarm = fm_rtc_set_alarm; ++ mac_dev->fm_rtc_set_fiper = fm_rtc_set_fiper; ++ mac_dev->set_wol = fm_mac_set_wol; ++ mac_dev->dump_mac_regs = dtsec_dump_regs; ++} ++ ++static void __cold setup_xgmac(struct mac_device *mac_dev) ++{ ++ mac_dev->init_phy = xgmac_init_phy; ++ mac_dev->init = init; ++ mac_dev->start = start; ++ mac_dev->stop = stop; ++ mac_dev->set_promisc = fm_mac_set_promiscuous; ++ mac_dev->change_addr = fm_mac_modify_mac_addr; ++ mac_dev->set_multi = set_multi; ++ mac_dev->uninit = uninit; ++ mac_dev->get_mac_handle = get_mac_handle; ++ mac_dev->set_tx_pause = fm_mac_set_tx_pause_frames; ++ mac_dev->set_rx_pause = fm_mac_set_rx_pause_frames; ++ mac_dev->set_wol = fm_mac_set_wol; ++ mac_dev->dump_mac_regs = xgmac_dump_regs; ++} ++ ++static void __cold setup_memac(struct mac_device *mac_dev) ++{ ++ mac_dev->init_phy = memac_init_phy; ++ mac_dev->init = memac_init; ++ mac_dev->start = start; ++ mac_dev->stop = stop; ++ mac_dev->set_promisc = fm_mac_set_promiscuous; ++ mac_dev->change_addr = fm_mac_modify_mac_addr; ++ mac_dev->set_multi = set_multi; ++ mac_dev->uninit = uninit; ++ mac_dev->get_mac_handle = get_mac_handle; ++ mac_dev->set_tx_pause = fm_mac_set_tx_pause_frames; ++ mac_dev->set_rx_pause = fm_mac_set_rx_pause_frames; ++ mac_dev->fm_rtc_enable = fm_rtc_enable; ++ mac_dev->fm_rtc_disable = fm_rtc_disable; ++ mac_dev->fm_rtc_get_cnt = fm_rtc_get_cnt; ++ mac_dev->fm_rtc_set_cnt = fm_rtc_set_cnt; ++ mac_dev->fm_rtc_get_drift = fm_rtc_get_drift; ++ mac_dev->fm_rtc_set_drift = fm_rtc_set_drift; ++ mac_dev->fm_rtc_set_alarm = fm_rtc_set_alarm; ++ mac_dev->fm_rtc_set_fiper = fm_rtc_set_fiper; ++ mac_dev->set_wol = fm_mac_set_wol; ++ mac_dev->dump_mac_regs = memac_dump_regs; ++ mac_dev->dump_mac_rx_stats = memac_dump_regs_rx; ++ mac_dev->dump_mac_tx_stats = memac_dump_regs_tx; ++} ++ ++void (*const mac_setup[])(struct mac_device *mac_dev) = { ++ [DTSEC] = setup_dtsec, ++ [XGMAC] = setup_xgmac, ++ [MEMAC] = setup_memac ++}; +--- /dev/null ++++ b/drivers/net/ethernet/freescale/sdk_dpaa/mac.c +@@ -0,0 +1,489 @@ ++/* Copyright 2008-2012 Freescale Semiconductor, Inc. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * * Neither the name of Freescale Semiconductor nor the ++ * names of its contributors may be used to endorse or promote products ++ * derived from this software without specific prior written permission. ++ * ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") as published by the Free Software ++ * Foundation, either version 2 of that License or (at your option) any ++ * later version. ++ * ++ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. ++ */ ++ ++#ifdef CONFIG_FSL_DPAA_ETH_DEBUG ++#define pr_fmt(fmt) \ ++ KBUILD_MODNAME ": %s:%hu:%s() " fmt, \ ++ KBUILD_BASENAME".c", __LINE__, __func__ ++#else ++#define pr_fmt(fmt) \ ++ KBUILD_MODNAME ": " fmt ++#endif ++ ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/of_address.h> ++#include <linux/of_platform.h> ++#include <linux/of_net.h> ++#include <linux/of_mdio.h> ++#include <linux/phy_fixed.h> ++#include <linux/device.h> ++#include <linux/phy.h> ++#include <linux/io.h> ++ ++#include "lnxwrp_fm_ext.h" ++ ++#include "mac.h" ++ ++#define DTSEC_SUPPORTED \ ++ (SUPPORTED_10baseT_Half \ ++ | SUPPORTED_10baseT_Full \ ++ | SUPPORTED_100baseT_Half \ ++ | SUPPORTED_100baseT_Full \ ++ | SUPPORTED_Autoneg \ ++ | SUPPORTED_Pause \ ++ | SUPPORTED_Asym_Pause \ ++ | SUPPORTED_MII) ++ ++static const char phy_str[][11] = { ++ [PHY_INTERFACE_MODE_MII] = "mii", ++ [PHY_INTERFACE_MODE_GMII] = "gmii", ++ [PHY_INTERFACE_MODE_SGMII] = "sgmii", ++ [PHY_INTERFACE_MODE_QSGMII] = "qsgmii", ++ [PHY_INTERFACE_MODE_TBI] = "tbi", ++ [PHY_INTERFACE_MODE_RMII] = "rmii", ++ [PHY_INTERFACE_MODE_RGMII] = "rgmii", ++ [PHY_INTERFACE_MODE_RGMII_ID] = "rgmii-id", ++ [PHY_INTERFACE_MODE_RGMII_RXID] = "rgmii-rxid", ++ [PHY_INTERFACE_MODE_RGMII_TXID] = "rgmii-txid", ++ [PHY_INTERFACE_MODE_RTBI] = "rtbi", ++ [PHY_INTERFACE_MODE_XGMII] = "xgmii", ++ [PHY_INTERFACE_MODE_SGMII_2500] = "sgmii-2500", ++}; ++ ++static phy_interface_t __pure __attribute__((nonnull)) str2phy(const char *str) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(phy_str); i++) ++ if (strcmp(str, phy_str[i]) == 0) ++ return (phy_interface_t)i; ++ ++ return PHY_INTERFACE_MODE_MII; ++} ++ ++static const uint16_t phy2speed[] = { ++ [PHY_INTERFACE_MODE_MII] = SPEED_100, ++ [PHY_INTERFACE_MODE_GMII] = SPEED_1000, ++ [PHY_INTERFACE_MODE_SGMII] = SPEED_1000, ++ [PHY_INTERFACE_MODE_QSGMII] = SPEED_1000, ++ [PHY_INTERFACE_MODE_TBI] = SPEED_1000, ++ [PHY_INTERFACE_MODE_RMII] = SPEED_100, ++ [PHY_INTERFACE_MODE_RGMII] = SPEED_1000, ++ [PHY_INTERFACE_MODE_RGMII_ID] = SPEED_1000, ++ [PHY_INTERFACE_MODE_RGMII_RXID] = SPEED_1000, ++ [PHY_INTERFACE_MODE_RGMII_TXID] = SPEED_1000, ++ [PHY_INTERFACE_MODE_RTBI] = SPEED_1000, ++ [PHY_INTERFACE_MODE_XGMII] = SPEED_10000, ++ [PHY_INTERFACE_MODE_SGMII_2500] = SPEED_2500, ++}; ++ ++static struct mac_device * __cold ++alloc_macdev(struct device *dev, size_t sizeof_priv, ++ void (*setup)(struct mac_device *mac_dev)) ++{ ++ struct mac_device *mac_dev; ++ ++ mac_dev = devm_kzalloc(dev, sizeof(*mac_dev) + sizeof_priv, GFP_KERNEL); ++ if (unlikely(mac_dev == NULL)) ++ mac_dev = ERR_PTR(-ENOMEM); ++ else { ++ mac_dev->dev = dev; ++ dev_set_drvdata(dev, mac_dev); ++ setup(mac_dev); ++ } ++ ++ return mac_dev; ++} ++ ++static int __cold free_macdev(struct mac_device *mac_dev) ++{ ++ dev_set_drvdata(mac_dev->dev, NULL); ++ ++ return mac_dev->uninit(mac_dev->get_mac_handle(mac_dev)); ++} ++ ++static const struct of_device_id mac_match[] = { ++ [DTSEC] = { ++ .compatible = "fsl,fman-1g-mac" ++ }, ++ [XGMAC] = { ++ .compatible = "fsl,fman-10g-mac" ++ }, ++ [MEMAC] = { ++ .compatible = "fsl,fman-memac" ++ }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, mac_match); ++ ++static int __cold mac_probe(struct platform_device *_of_dev) ++{ ++ int _errno, i; ++ struct device *dev; ++ struct device_node *mac_node, *dev_node; ++ struct mac_device *mac_dev; ++ struct platform_device *of_dev; ++ struct resource res; ++ const uint8_t *mac_addr; ++ const char *char_prop; ++ int nph; ++ u32 cell_index; ++ const struct of_device_id *match; ++ ++ dev = &_of_dev->dev; ++ mac_node = dev->of_node; ++ ++ match = of_match_device(mac_match, dev); ++ if (!match) ++ return -EINVAL; ++ ++ for (i = 0; i < ARRAY_SIZE(mac_match) - 1 && match != mac_match + i; ++ i++) ++ ; ++ BUG_ON(i >= ARRAY_SIZE(mac_match) - 1); ++ ++ mac_dev = alloc_macdev(dev, mac_sizeof_priv[i], mac_setup[i]); ++ if (IS_ERR(mac_dev)) { ++ _errno = PTR_ERR(mac_dev); ++ dev_err(dev, "alloc_macdev() = %d\n", _errno); ++ goto _return; ++ } ++ ++ INIT_LIST_HEAD(&mac_dev->mc_addr_list); ++ ++ /* Get the FM node */ ++ dev_node = of_get_parent(mac_node); ++ if (unlikely(dev_node == NULL)) { ++ dev_err(dev, "of_get_parent(%s) failed\n", ++ mac_node->full_name); ++ _errno = -EINVAL; ++ goto _return_dev_set_drvdata; ++ } ++ ++ of_dev = of_find_device_by_node(dev_node); ++ if (unlikely(of_dev == NULL)) { ++ dev_err(dev, "of_find_device_by_node(%s) failed\n", ++ dev_node->full_name); ++ _errno = -EINVAL; ++ goto _return_of_node_put; ++ } ++ ++ mac_dev->fm_dev = fm_bind(&of_dev->dev); ++ if (unlikely(mac_dev->fm_dev == NULL)) { ++ dev_err(dev, "fm_bind(%s) failed\n", dev_node->full_name); ++ _errno = -ENODEV; ++ goto _return_of_node_put; ++ } ++ ++ mac_dev->fm = (void *)fm_get_handle(mac_dev->fm_dev); ++ of_node_put(dev_node); ++ ++ /* Get the address of the memory mapped registers */ ++ _errno = of_address_to_resource(mac_node, 0, &res); ++ if (unlikely(_errno < 0)) { ++ dev_err(dev, "of_address_to_resource(%s) = %d\n", ++ mac_node->full_name, _errno); ++ goto _return_dev_set_drvdata; ++ } ++ ++ mac_dev->res = __devm_request_region( ++ dev, ++ fm_get_mem_region(mac_dev->fm_dev), ++ res.start, res.end + 1 - res.start, "mac"); ++ if (unlikely(mac_dev->res == NULL)) { ++ dev_err(dev, "__devm_request_mem_region(mac) failed\n"); ++ _errno = -EBUSY; ++ goto _return_dev_set_drvdata; ++ } ++ ++ mac_dev->vaddr = devm_ioremap(dev, mac_dev->res->start, ++ mac_dev->res->end + 1 ++ - mac_dev->res->start); ++ if (unlikely(mac_dev->vaddr == NULL)) { ++ dev_err(dev, "devm_ioremap() failed\n"); ++ _errno = -EIO; ++ goto _return_dev_set_drvdata; ++ } ++ ++#define TBIPA_OFFSET 0x1c ++#define TBIPA_DEFAULT_ADDR 5 /* override if used as external PHY addr. */ ++ mac_dev->tbi_node = of_parse_phandle(mac_node, "tbi-handle", 0); ++ if (mac_dev->tbi_node) { ++ u32 tbiaddr = TBIPA_DEFAULT_ADDR; ++ const __be32 *tbi_reg; ++ void __iomem *addr; ++ ++ tbi_reg = of_get_property(mac_dev->tbi_node, "reg", NULL); ++ if (tbi_reg) ++ tbiaddr = be32_to_cpup(tbi_reg); ++ addr = mac_dev->vaddr + TBIPA_OFFSET; ++ /* TODO: out_be32 does not exist on ARM */ ++ out_be32(addr, tbiaddr); ++ } ++ ++ if (!of_device_is_available(mac_node)) { ++ devm_iounmap(dev, mac_dev->vaddr); ++ __devm_release_region(dev, fm_get_mem_region(mac_dev->fm_dev), ++ res.start, res.end + 1 - res.start); ++ fm_unbind(mac_dev->fm_dev); ++ devm_kfree(dev, mac_dev); ++ dev_set_drvdata(dev, NULL); ++ return -ENODEV; ++ } ++ ++ /* Get the cell-index */ ++ _errno = of_property_read_u32(mac_node, "cell-index", &cell_index); ++ if (unlikely(_errno)) { ++ dev_err(dev, "Cannot read cell-index of mac node %s from device tree\n", ++ mac_node->full_name); ++ goto _return_dev_set_drvdata; ++ } ++ mac_dev->cell_index = (uint8_t)cell_index; ++ if (mac_dev->cell_index >= 8) ++ mac_dev->cell_index -= 8; ++ ++ /* Get the MAC address */ ++ mac_addr = of_get_mac_address(mac_node); ++ if (unlikely(mac_addr == NULL)) { ++ dev_err(dev, "of_get_mac_address(%s) failed\n", ++ mac_node->full_name); ++ _errno = -EINVAL; ++ goto _return_dev_set_drvdata; ++ } ++ memcpy(mac_dev->addr, mac_addr, sizeof(mac_dev->addr)); ++ ++ /* Verify the number of port handles */ ++ nph = of_count_phandle_with_args(mac_node, "fsl,fman-ports", NULL); ++ if (unlikely(nph < 0)) { ++ dev_err(dev, "Cannot read port handles of mac node %s from device tree\n", ++ mac_node->full_name); ++ _errno = nph; ++ goto _return_dev_set_drvdata; ++ } ++ ++ if (nph != ARRAY_SIZE(mac_dev->port_dev)) { ++ dev_err(dev, "Not supported number of port handles of mac node %s from device tree\n", ++ mac_node->full_name); ++ _errno = -EINVAL; ++ goto _return_dev_set_drvdata; ++ } ++ ++ for_each_port_device(i, mac_dev->port_dev) { ++ dev_node = of_parse_phandle(mac_node, "fsl,fman-ports", i); ++ if (unlikely(dev_node == NULL)) { ++ dev_err(dev, "Cannot find port node referenced by mac node %s from device tree\n", ++ mac_node->full_name); ++ _errno = -EINVAL; ++ goto _return_of_node_put; ++ } ++ ++ of_dev = of_find_device_by_node(dev_node); ++ if (unlikely(of_dev == NULL)) { ++ dev_err(dev, "of_find_device_by_node(%s) failed\n", ++ dev_node->full_name); ++ _errno = -EINVAL; ++ goto _return_of_node_put; ++ } ++ ++ mac_dev->port_dev[i] = fm_port_bind(&of_dev->dev); ++ if (unlikely(mac_dev->port_dev[i] == NULL)) { ++ dev_err(dev, "dev_get_drvdata(%s) failed\n", ++ dev_node->full_name); ++ _errno = -EINVAL; ++ goto _return_of_node_put; ++ } ++ of_node_put(dev_node); ++ } ++ ++ /* Get the PHY connection type */ ++ _errno = of_property_read_string(mac_node, "phy-connection-type", ++ &char_prop); ++ if (unlikely(_errno)) { ++ dev_warn(dev, ++ "Cannot read PHY connection type of mac node %s from device tree. Defaulting to MII\n", ++ mac_node->full_name); ++ mac_dev->phy_if = PHY_INTERFACE_MODE_MII; ++ } else ++ mac_dev->phy_if = str2phy(char_prop); ++ ++ mac_dev->link = false; ++ mac_dev->half_duplex = false; ++ mac_dev->speed = phy2speed[mac_dev->phy_if]; ++ mac_dev->max_speed = mac_dev->speed; ++ mac_dev->if_support = DTSEC_SUPPORTED; ++ /* We don't support half-duplex in SGMII mode */ ++ if (strstr(char_prop, "sgmii") || strstr(char_prop, "qsgmii") || ++ strstr(char_prop, "sgmii-2500")) ++ mac_dev->if_support &= ~(SUPPORTED_10baseT_Half | ++ SUPPORTED_100baseT_Half); ++ ++ /* Gigabit support (no half-duplex) */ ++ if (mac_dev->max_speed == SPEED_1000 || ++ mac_dev->max_speed == SPEED_2500) ++ mac_dev->if_support |= SUPPORTED_1000baseT_Full; ++ ++ /* The 10G interface only supports one mode */ ++ if (strstr(char_prop, "xgmii")) ++ mac_dev->if_support = SUPPORTED_10000baseT_Full; ++ ++ /* Get the rest of the PHY information */ ++ mac_dev->phy_node = of_parse_phandle(mac_node, "phy-handle", 0); ++ if (!mac_dev->phy_node) { ++ struct phy_device *phy; ++ ++ if (!of_phy_is_fixed_link(mac_node)) { ++ dev_err(dev, "Wrong PHY information of mac node %s\n", ++ mac_node->full_name); ++ goto _return_dev_set_drvdata; ++ } ++ ++ _errno = of_phy_register_fixed_link(mac_node); ++ if (_errno) ++ goto _return_dev_set_drvdata; ++ ++ mac_dev->fixed_link = devm_kzalloc(mac_dev->dev, ++ sizeof(*mac_dev->fixed_link), ++ GFP_KERNEL); ++ if (!mac_dev->fixed_link) ++ goto _return_dev_set_drvdata; ++ ++ mac_dev->phy_node = of_node_get(mac_node); ++ phy = of_phy_find_device(mac_dev->phy_node); ++ if (!phy) ++ goto _return_dev_set_drvdata; ++ ++ mac_dev->fixed_link->link = phy->link; ++ mac_dev->fixed_link->speed = phy->speed; ++ mac_dev->fixed_link->duplex = phy->duplex; ++ mac_dev->fixed_link->pause = phy->pause; ++ mac_dev->fixed_link->asym_pause = phy->asym_pause; ++ } ++ ++ _errno = mac_dev->init(mac_dev); ++ if (unlikely(_errno < 0)) { ++ dev_err(dev, "mac_dev->init() = %d\n", _errno); ++ goto _return_dev_set_drvdata; ++ } ++ ++ /* pause frame autonegotiation enabled*/ ++ mac_dev->autoneg_pause = true; ++ ++ /* by intializing the values to false, force FMD to enable PAUSE frames ++ * on RX and TX ++ */ ++ mac_dev->rx_pause_req = mac_dev->tx_pause_req = true; ++ mac_dev->rx_pause_active = mac_dev->tx_pause_active = false; ++ _errno = set_mac_active_pause(mac_dev, true, true); ++ if (unlikely(_errno < 0)) ++ dev_err(dev, "set_mac_active_pause() = %d\n", _errno); ++ ++ dev_info(dev, ++ "FMan MAC address: %02hx:%02hx:%02hx:%02hx:%02hx:%02hx\n", ++ mac_dev->addr[0], mac_dev->addr[1], mac_dev->addr[2], ++ mac_dev->addr[3], mac_dev->addr[4], mac_dev->addr[5]); ++ ++ goto _return; ++ ++_return_of_node_put: ++ of_node_put(dev_node); ++_return_dev_set_drvdata: ++ dev_set_drvdata(dev, NULL); ++_return: ++ return _errno; ++} ++ ++static int __cold mac_remove(struct platform_device *of_dev) ++{ ++ int i, _errno; ++ struct device *dev; ++ struct mac_device *mac_dev; ++ ++ dev = &of_dev->dev; ++ mac_dev = (struct mac_device *)dev_get_drvdata(dev); ++ ++ for_each_port_device(i, mac_dev->port_dev) ++ fm_port_unbind(mac_dev->port_dev[i]); ++ ++ fm_unbind(mac_dev->fm_dev); ++ ++ _errno = free_macdev(mac_dev); ++ ++ return _errno; ++} ++ ++static struct platform_driver mac_driver = { ++ .driver = { ++ .name = KBUILD_MODNAME, ++ .of_match_table = mac_match, ++ .owner = THIS_MODULE, ++ }, ++ .probe = mac_probe, ++ .remove = mac_remove ++}; ++ ++static int __init __cold mac_load(void) ++{ ++ int _errno; ++ ++ pr_debug(KBUILD_MODNAME ": -> %s:%s()\n", ++ KBUILD_BASENAME".c", __func__); ++ ++ pr_info(KBUILD_MODNAME ": %s\n", mac_driver_description); ++ ++ _errno = platform_driver_register(&mac_driver); ++ if (unlikely(_errno < 0)) { ++ pr_err(KBUILD_MODNAME ": %s:%hu:%s(): platform_driver_register() = %d\n", ++ KBUILD_BASENAME".c", __LINE__, __func__, _errno); ++ goto _return; ++ } ++ ++ goto _return; ++ ++_return: ++ pr_debug(KBUILD_MODNAME ": %s:%s() ->\n", ++ KBUILD_BASENAME".c", __func__); ++ ++ return _errno; ++} ++module_init(mac_load); ++ ++static void __exit __cold mac_unload(void) ++{ ++ pr_debug(KBUILD_MODNAME ": -> %s:%s()\n", ++ KBUILD_BASENAME".c", __func__); ++ ++ platform_driver_unregister(&mac_driver); ++ ++ pr_debug(KBUILD_MODNAME ": %s:%s() ->\n", ++ KBUILD_BASENAME".c", __func__); ++} ++module_exit(mac_unload); +--- /dev/null ++++ b/drivers/net/ethernet/freescale/sdk_dpaa/mac.h +@@ -0,0 +1,135 @@ ++/* Copyright 2008-2011 Freescale Semiconductor, Inc. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * * Neither the name of Freescale Semiconductor nor the ++ * names of its contributors may be used to endorse or promote products ++ * derived from this software without specific prior written permission. ++ * ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") as published by the Free Software ++ * Foundation, either version 2 of that License or (at your option) any ++ * later version. ++ * ++ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. ++ */ ++ ++#ifndef __MAC_H ++#define __MAC_H ++ ++#include <linux/device.h> /* struct device, BUS_ID_SIZE */ ++#include <linux/if_ether.h> /* ETH_ALEN */ ++#include <linux/phy.h> /* phy_interface_t, struct phy_device */ ++#include <linux/list.h> ++ ++#include "lnxwrp_fsl_fman.h" /* struct port_device */ ++ ++enum {DTSEC, XGMAC, MEMAC}; ++ ++struct mac_device { ++ struct device *dev; ++ void *priv; ++ uint8_t cell_index; ++ struct resource *res; ++ void __iomem *vaddr; ++ uint8_t addr[ETH_ALEN]; ++ bool promisc; ++ ++ struct fm *fm_dev; ++ struct fm_port *port_dev[2]; ++ ++ phy_interface_t phy_if; ++ u32 if_support; ++ bool link; ++ bool half_duplex; ++ uint16_t speed; ++ uint16_t max_speed; ++ struct device_node *phy_node; ++ char fixed_bus_id[MII_BUS_ID_SIZE + 3]; ++ struct device_node *tbi_node; ++ struct phy_device *phy_dev; ++ void *fm; ++ /* List of multicast addresses */ ++ struct list_head mc_addr_list; ++ struct fixed_phy_status *fixed_link; ++ ++ bool autoneg_pause; ++ bool rx_pause_req; ++ bool tx_pause_req; ++ bool rx_pause_active; ++ bool tx_pause_active; ++ ++ struct fm_mac_dev *(*get_mac_handle)(struct mac_device *mac_dev); ++ int (*init_phy)(struct net_device *net_dev, struct mac_device *mac_dev); ++ int (*init)(struct mac_device *mac_dev); ++ int (*start)(struct mac_device *mac_dev); ++ int (*stop)(struct mac_device *mac_dev); ++ int (*set_promisc)(struct fm_mac_dev *fm_mac_dev, bool enable); ++ int (*change_addr)(struct fm_mac_dev *fm_mac_dev, uint8_t *addr); ++ int (*set_multi)(struct net_device *net_dev, ++ struct mac_device *mac_dev); ++ int (*uninit)(struct fm_mac_dev *fm_mac_dev); ++ int (*ptp_enable)(struct fm_mac_dev *fm_mac_dev); ++ int (*ptp_disable)(struct fm_mac_dev *fm_mac_dev); ++ int (*set_rx_pause)(struct fm_mac_dev *fm_mac_dev, bool en); ++ int (*set_tx_pause)(struct fm_mac_dev *fm_mac_dev, bool en); ++ int (*fm_rtc_enable)(struct fm *fm_dev); ++ int (*fm_rtc_disable)(struct fm *fm_dev); ++ int (*fm_rtc_get_cnt)(struct fm *fm_dev, uint64_t *ts); ++ int (*fm_rtc_set_cnt)(struct fm *fm_dev, uint64_t ts); ++ int (*fm_rtc_get_drift)(struct fm *fm_dev, uint32_t *drift); ++ int (*fm_rtc_set_drift)(struct fm *fm_dev, uint32_t drift); ++ int (*fm_rtc_set_alarm)(struct fm *fm_dev, uint32_t id, uint64_t time); ++ int (*fm_rtc_set_fiper)(struct fm *fm_dev, uint32_t id, ++ uint64_t fiper); ++#ifdef CONFIG_PTP_1588_CLOCK_DPAA ++ int (*fm_rtc_enable_interrupt)(struct fm *fm_dev, uint32_t events); ++ int (*fm_rtc_disable_interrupt)(struct fm *fm_dev, uint32_t events); ++#endif ++ int (*set_wol)(struct fm_port *port, struct fm_mac_dev *fm_mac_dev, ++ bool en); ++ int (*dump_mac_regs)(struct mac_device *h_mac, char *buf, int nn); ++ int (*dump_mac_rx_stats)(struct mac_device *h_mac, char *buf, int nn); ++ int (*dump_mac_tx_stats)(struct mac_device *h_mac, char *buf, int nn); ++}; ++ ++struct mac_address { ++ uint8_t addr[ETH_ALEN]; ++ struct list_head list; ++}; ++ ++#define get_fm_handle(net_dev) \ ++ (((struct dpa_priv_s *)netdev_priv(net_dev))->mac_dev->fm_dev) ++ ++#define for_each_port_device(i, port_dev) \ ++ for (i = 0; i < ARRAY_SIZE(port_dev); i++) ++ ++static inline __attribute((nonnull)) void *macdev_priv( ++ const struct mac_device *mac_dev) ++{ ++ return (void *)mac_dev + sizeof(*mac_dev); ++} ++ ++extern const char *mac_driver_description; ++extern const size_t mac_sizeof_priv[]; ++extern void (*const mac_setup[])(struct mac_device *mac_dev); ++ ++int set_mac_active_pause(struct mac_device *mac_dev, bool rx, bool tx); ++void get_pause_cfg(struct mac_device *mac_dev, bool *rx_pause, bool *tx_pause); ++ ++#endif /* __MAC_H */ +--- /dev/null ++++ b/drivers/net/ethernet/freescale/sdk_dpaa/offline_port.c +@@ -0,0 +1,848 @@ ++/* Copyright 2011-2012 Freescale Semiconductor Inc. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * * Neither the name of Freescale Semiconductor nor the ++ * names of its contributors may be used to endorse or promote products ++ * derived from this software without specific prior written permission. ++ * ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") as published by the Free Software ++ * Foundation, either version 2 of that License or (at your option) any ++ * later version. ++ * ++ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. ++ */ ++ ++/* Offline Parsing / Host Command port driver for FSL QorIQ FMan. ++ * Validates device-tree configuration and sets up the offline ports. ++ */ ++ ++#ifdef CONFIG_FSL_DPAA_ETH_DEBUG ++#define pr_fmt(fmt) \ ++ KBUILD_MODNAME ": %s:%hu:%s() " fmt, \ ++ KBUILD_BASENAME".c", __LINE__, __func__ ++#else ++#define pr_fmt(fmt) \ ++ KBUILD_MODNAME ": " fmt ++#endif ++ ++ ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/of_platform.h> ++#include <linux/fsl_qman.h> ++ ++#include "offline_port.h" ++#include "dpaa_eth.h" ++#include "dpaa_eth_common.h" ++ ++#define OH_MOD_DESCRIPTION "FSL FMan Offline Parsing port driver" ++/* Manip extra space and data alignment for fragmentation */ ++#define FRAG_MANIP_SPACE 128 ++#define FRAG_DATA_ALIGN 64 ++ ++ ++MODULE_LICENSE("Dual BSD/GPL"); ++MODULE_AUTHOR("Bogdan Hamciuc <bogdan.hamciuc@freescale.com>"); ++MODULE_DESCRIPTION(OH_MOD_DESCRIPTION); ++ ++ ++static const struct of_device_id oh_port_match_table[] = { ++ { ++ .compatible = "fsl,dpa-oh" ++ }, ++ { ++ .compatible = "fsl,dpa-oh-shared" ++ }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, oh_port_match_table); ++ ++#ifdef CONFIG_PM ++ ++static int oh_suspend(struct device *dev) ++{ ++ struct dpa_oh_config_s *oh_config; ++ ++ oh_config = dev_get_drvdata(dev); ++ return fm_port_suspend(oh_config->oh_port); ++} ++ ++static int oh_resume(struct device *dev) ++{ ++ struct dpa_oh_config_s *oh_config; ++ ++ oh_config = dev_get_drvdata(dev); ++ return fm_port_resume(oh_config->oh_port); ++} ++ ++static const struct dev_pm_ops oh_pm_ops = { ++ .suspend = oh_suspend, ++ .resume = oh_resume, ++}; ++ ++#define OH_PM_OPS (&oh_pm_ops) ++ ++#else /* CONFIG_PM */ ++ ++#define OH_PM_OPS NULL ++ ++#endif /* CONFIG_PM */ ++ ++/* Creates Frame Queues */ ++static uint32_t oh_fq_create(struct qman_fq *fq, ++ uint32_t fq_id, uint16_t channel, ++ uint16_t wq_id) ++{ ++ struct qm_mcc_initfq fq_opts; ++ uint32_t create_flags, init_flags; ++ uint32_t ret = 0; ++ ++ if (fq == NULL) ++ return 1; ++ ++ /* Set flags for FQ create */ ++ create_flags = QMAN_FQ_FLAG_LOCKED | QMAN_FQ_FLAG_TO_DCPORTAL; ++ ++ /* Create frame queue */ ++ ret = qman_create_fq(fq_id, create_flags, fq); ++ if (ret != 0) ++ return 1; ++ ++ /* Set flags for FQ init */ ++ init_flags = QMAN_INITFQ_FLAG_SCHED; ++ ++ /* Set FQ init options. Specify destination WQ ID and channel */ ++ fq_opts.we_mask = QM_INITFQ_WE_DESTWQ; ++ fq_opts.fqd.dest.wq = wq_id; ++ fq_opts.fqd.dest.channel = channel; ++ ++ /* Initialize frame queue */ ++ ret = qman_init_fq(fq, init_flags, &fq_opts); ++ if (ret != 0) { ++ qman_destroy_fq(fq, 0); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static void dump_fq(struct device *dev, int fqid, uint16_t channel) ++{ ++ if (channel) { ++ /* display fqs with a valid (!= 0) destination channel */ ++ dev_info(dev, "FQ ID:%d Channel ID:%d\n", fqid, channel); ++ } ++} ++ ++static void dump_fq_duple(struct device *dev, struct qman_fq *fqs, ++ int fqs_count, uint16_t channel_id) ++{ ++ int i; ++ for (i = 0; i < fqs_count; i++) ++ dump_fq(dev, (fqs + i)->fqid, channel_id); ++} ++ ++static void dump_oh_config(struct device *dev, struct dpa_oh_config_s *conf) ++{ ++ struct list_head *fq_list; ++ struct fq_duple *fqd; ++ int i; ++ ++ dev_info(dev, "Default egress frame queue: %d\n", conf->default_fqid); ++ dev_info(dev, "Default error frame queue: %d\n", conf->error_fqid); ++ ++ /* TX queues (old initialization) */ ++ dev_info(dev, "Initialized queues:"); ++ for (i = 0; i < conf->egress_cnt; i++) ++ dump_fq_duple(dev, conf->egress_fqs, conf->egress_cnt, ++ conf->channel); ++ ++ /* initialized ingress queues */ ++ list_for_each(fq_list, &conf->fqs_ingress_list) { ++ fqd = list_entry(fq_list, struct fq_duple, fq_list); ++ dump_fq_duple(dev, fqd->fqs, fqd->fqs_count, fqd->channel_id); ++ } ++ ++ /* initialized egress queues */ ++ list_for_each(fq_list, &conf->fqs_egress_list) { ++ fqd = list_entry(fq_list, struct fq_duple, fq_list); ++ dump_fq_duple(dev, fqd->fqs, fqd->fqs_count, fqd->channel_id); ++ } ++} ++ ++/* Destroys Frame Queues */ ++static void oh_fq_destroy(struct qman_fq *fq) ++{ ++ int _errno = 0; ++ ++ _errno = qman_retire_fq(fq, NULL); ++ if (unlikely(_errno < 0)) ++ pr_err(KBUILD_MODNAME": %s:%hu:%s(): qman_retire_fq(%u)=%d\n", ++ KBUILD_BASENAME".c", __LINE__, __func__, ++ qman_fq_fqid(fq), _errno); ++ ++ _errno = qman_oos_fq(fq); ++ if (unlikely(_errno < 0)) { ++ pr_err(KBUILD_MODNAME": %s:%hu:%s(): qman_oos_fq(%u)=%d\n", ++ KBUILD_BASENAME".c", __LINE__, __func__, ++ qman_fq_fqid(fq), _errno); ++ } ++ ++ qman_destroy_fq(fq, 0); ++} ++ ++/* Allocation code for the OH port's PCD frame queues */ ++static int __cold oh_alloc_pcd_fqids(struct device *dev, ++ uint32_t num, ++ uint8_t alignment, ++ uint32_t *base_fqid) ++{ ++ dev_crit(dev, "callback not implemented!\n"); ++ BUG(); ++ ++ return 0; ++} ++ ++static int __cold oh_free_pcd_fqids(struct device *dev, uint32_t base_fqid) ++{ ++ dev_crit(dev, "callback not implemented!\n"); ++ BUG(); ++ ++ return 0; ++} ++ ++static void oh_set_buffer_layout(struct fm_port *port, ++ struct dpa_buffer_layout_s *layout) ++{ ++ struct fm_port_params params; ++ ++ layout->priv_data_size = DPA_TX_PRIV_DATA_SIZE; ++ layout->parse_results = true; ++ layout->hash_results = true; ++ layout->time_stamp = false; ++ ++ fm_port_get_buff_layout_ext_params(port, ¶ms); ++ layout->manip_extra_space = params.manip_extra_space; ++ layout->data_align = params.data_align; ++} ++ ++static int ++oh_port_probe(struct platform_device *_of_dev) ++{ ++ struct device *dpa_oh_dev; ++ struct device_node *dpa_oh_node; ++ int lenp, _errno = 0, fq_idx, duple_idx; ++ int n_size, i, j, ret, duples_count; ++ struct platform_device *oh_of_dev; ++ struct device_node *oh_node, *bpool_node = NULL, *root_node; ++ struct device *oh_dev; ++ struct dpa_oh_config_s *oh_config = NULL; ++ const __be32 *oh_all_queues; ++ const __be32 *channel_ids; ++ const __be32 *oh_tx_queues; ++ uint32_t queues_count; ++ uint32_t crt_fqid_base; ++ uint32_t crt_fq_count; ++ bool frag_enabled = false; ++ struct fm_port_params oh_port_tx_params; ++ struct fm_port_pcd_param oh_port_pcd_params; ++ struct dpa_buffer_layout_s buf_layout; ++ ++ /* True if the current partition owns the OH port. */ ++ bool init_oh_port; ++ ++ const struct of_device_id *match; ++ int crt_ext_pools_count; ++ u32 ext_pool_size; ++ u32 port_id; ++ u32 channel_id; ++ ++ int channel_ids_count; ++ int channel_idx; ++ struct fq_duple *fqd; ++ struct list_head *fq_list, *fq_list_tmp; ++ ++ const __be32 *bpool_cfg; ++ uint32_t bpid; ++ ++ memset(&oh_port_tx_params, 0, sizeof(oh_port_tx_params)); ++ dpa_oh_dev = &_of_dev->dev; ++ dpa_oh_node = dpa_oh_dev->of_node; ++ BUG_ON(dpa_oh_node == NULL); ++ ++ match = of_match_device(oh_port_match_table, dpa_oh_dev); ++ if (!match) ++ return -EINVAL; ++ ++ dev_dbg(dpa_oh_dev, "Probing OH port...\n"); ++ ++ /* Find the referenced OH node */ ++ oh_node = of_parse_phandle(dpa_oh_node, "fsl,fman-oh-port", 0); ++ if (oh_node == NULL) { ++ dev_err(dpa_oh_dev, ++ "Can't find OH node referenced from node %s\n", ++ dpa_oh_node->full_name); ++ return -EINVAL; ++ } ++ dev_info(dpa_oh_dev, "Found OH node handle compatible with %s\n", ++ match->compatible); ++ ++ _errno = of_property_read_u32(oh_node, "cell-index", &port_id); ++ if (_errno) { ++ dev_err(dpa_oh_dev, "No port id found in node %s\n", ++ dpa_oh_node->full_name); ++ goto return_kfree; ++ } ++ ++ _errno = of_property_read_u32(oh_node, "fsl,qman-channel-id", ++ &channel_id); ++ if (_errno) { ++ dev_err(dpa_oh_dev, "No channel id found in node %s\n", ++ dpa_oh_node->full_name); ++ goto return_kfree; ++ } ++ ++ oh_of_dev = of_find_device_by_node(oh_node); ++ BUG_ON(oh_of_dev == NULL); ++ oh_dev = &oh_of_dev->dev; ++ ++ /* The OH port must be initialized exactly once. ++ * The following scenarios are of interest: ++ * - the node is Linux-private (will always initialize it); ++ * - the node is shared between two Linux partitions ++ * (only one of them will initialize it); ++ * - the node is shared between a Linux and a LWE partition ++ * (Linux will initialize it) - "fsl,dpa-oh-shared" ++ */ ++ ++ /* Check if the current partition owns the OH port ++ * and ought to initialize it. It may be the case that we leave this ++ * to another (also Linux) partition. ++ */ ++ init_oh_port = strcmp(match->compatible, "fsl,dpa-oh-shared"); ++ ++ /* If we aren't the "owner" of the OH node, we're done here. */ ++ if (!init_oh_port) { ++ dev_dbg(dpa_oh_dev, ++ "Not owning the shared OH port %s, will not initialize it.\n", ++ oh_node->full_name); ++ of_node_put(oh_node); ++ return 0; ++ } ++ ++ /* Allocate OH dev private data */ ++ oh_config = devm_kzalloc(dpa_oh_dev, sizeof(*oh_config), GFP_KERNEL); ++ if (oh_config == NULL) { ++ dev_err(dpa_oh_dev, ++ "Can't allocate private data for OH node %s referenced from node %s!\n", ++ oh_node->full_name, dpa_oh_node->full_name); ++ _errno = -ENOMEM; ++ goto return_kfree; ++ } ++ ++ INIT_LIST_HEAD(&oh_config->fqs_ingress_list); ++ INIT_LIST_HEAD(&oh_config->fqs_egress_list); ++ ++ /* FQs that enter OH port */ ++ lenp = 0; ++ oh_all_queues = of_get_property(dpa_oh_node, ++ "fsl,qman-frame-queues-ingress", &lenp); ++ if (lenp % (2 * sizeof(*oh_all_queues))) { ++ dev_warn(dpa_oh_dev, ++ "Wrong ingress queues format for OH node %s referenced from node %s!\n", ++ oh_node->full_name, dpa_oh_node->full_name); ++ /* just ignore the last unpaired value */ ++ } ++ ++ duples_count = lenp / (2 * sizeof(*oh_all_queues)); ++ dev_err(dpa_oh_dev, "Allocating %d ingress frame queues duples\n", ++ duples_count); ++ for (duple_idx = 0; duple_idx < duples_count; duple_idx++) { ++ crt_fqid_base = be32_to_cpu(oh_all_queues[2 * duple_idx]); ++ crt_fq_count = be32_to_cpu(oh_all_queues[2 * duple_idx + 1]); ++ ++ fqd = devm_kzalloc(dpa_oh_dev, ++ sizeof(struct fq_duple), GFP_KERNEL); ++ if (!fqd) { ++ dev_err(dpa_oh_dev, "Can't allocate structures for ingress frame queues for OH node %s referenced from node %s!\n", ++ oh_node->full_name, ++ dpa_oh_node->full_name); ++ _errno = -ENOMEM; ++ goto return_kfree; ++ } ++ ++ fqd->fqs = devm_kzalloc(dpa_oh_dev, ++ crt_fq_count * sizeof(struct qman_fq), ++ GFP_KERNEL); ++ if (!fqd->fqs) { ++ dev_err(dpa_oh_dev, "Can't allocate structures for ingress frame queues for OH node %s referenced from node %s!\n", ++ oh_node->full_name, ++ dpa_oh_node->full_name); ++ _errno = -ENOMEM; ++ goto return_kfree; ++ } ++ ++ for (j = 0; j < crt_fq_count; j++) ++ (fqd->fqs + j)->fqid = crt_fqid_base + j; ++ fqd->fqs_count = crt_fq_count; ++ fqd->channel_id = (uint16_t)channel_id; ++ list_add(&fqd->fq_list, &oh_config->fqs_ingress_list); ++ } ++ ++ /* create the ingress queues */ ++ list_for_each(fq_list, &oh_config->fqs_ingress_list) { ++ fqd = list_entry(fq_list, struct fq_duple, fq_list); ++ ++ for (j = 0; j < fqd->fqs_count; j++) { ++ ret = oh_fq_create(fqd->fqs + j, ++ (fqd->fqs + j)->fqid, ++ fqd->channel_id, 3); ++ if (ret != 0) { ++ dev_err(dpa_oh_dev, "Unable to create ingress frame queue %d for OH node %s referenced from node %s!\n", ++ (fqd->fqs + j)->fqid, ++ oh_node->full_name, ++ dpa_oh_node->full_name); ++ _errno = -EINVAL; ++ goto return_kfree; ++ } ++ } ++ } ++ ++ /* FQs that exit OH port */ ++ lenp = 0; ++ oh_all_queues = of_get_property(dpa_oh_node, ++ "fsl,qman-frame-queues-egress", &lenp); ++ if (lenp % (2 * sizeof(*oh_all_queues))) { ++ dev_warn(dpa_oh_dev, ++ "Wrong egress queues format for OH node %s referenced from node %s!\n", ++ oh_node->full_name, dpa_oh_node->full_name); ++ /* just ignore the last unpaired value */ ++ } ++ ++ duples_count = lenp / (2 * sizeof(*oh_all_queues)); ++ dev_dbg(dpa_oh_dev, "Allocating %d egress frame queues duples\n", ++ duples_count); ++ for (duple_idx = 0; duple_idx < duples_count; duple_idx++) { ++ crt_fqid_base = be32_to_cpu(oh_all_queues[2 * duple_idx]); ++ crt_fq_count = be32_to_cpu(oh_all_queues[2 * duple_idx + 1]); ++ ++ fqd = devm_kzalloc(dpa_oh_dev, ++ sizeof(struct fq_duple), GFP_KERNEL); ++ if (!fqd) { ++ dev_err(dpa_oh_dev, "Can't allocate structures for egress frame queues for OH node %s referenced from node %s!\n", ++ oh_node->full_name, ++ dpa_oh_node->full_name); ++ _errno = -ENOMEM; ++ goto return_kfree; ++ } ++ ++ fqd->fqs = devm_kzalloc(dpa_oh_dev, ++ crt_fq_count * sizeof(struct qman_fq), ++ GFP_KERNEL); ++ if (!fqd->fqs) { ++ dev_err(dpa_oh_dev, ++ "Can't allocate structures for egress frame queues for OH node %s referenced from node %s!\n", ++ oh_node->full_name, ++ dpa_oh_node->full_name); ++ _errno = -ENOMEM; ++ goto return_kfree; ++ } ++ ++ for (j = 0; j < crt_fq_count; j++) ++ (fqd->fqs + j)->fqid = crt_fqid_base + j; ++ fqd->fqs_count = crt_fq_count; ++ /* channel ID is specified in another attribute */ ++ fqd->channel_id = 0; ++ list_add_tail(&fqd->fq_list, &oh_config->fqs_egress_list); ++ ++ /* allocate the queue */ ++ ++ } ++ ++ /* channel_ids for FQs that exit OH port */ ++ lenp = 0; ++ channel_ids = of_get_property(dpa_oh_node, ++ "fsl,qman-channel-ids-egress", &lenp); ++ ++ channel_ids_count = lenp / (sizeof(*channel_ids)); ++ if (channel_ids_count != duples_count) { ++ dev_warn(dpa_oh_dev, ++ "Not all egress queues have a channel id for OH node %s referenced from node %s!\n", ++ oh_node->full_name, dpa_oh_node->full_name); ++ /* just ignore the queues that do not have a Channel ID */ ++ } ++ ++ channel_idx = 0; ++ list_for_each(fq_list, &oh_config->fqs_egress_list) { ++ if (channel_idx + 1 > channel_ids_count) ++ break; ++ fqd = list_entry(fq_list, struct fq_duple, fq_list); ++ fqd->channel_id = ++ (uint16_t)be32_to_cpu(channel_ids[channel_idx++]); ++ } ++ ++ /* create egress queues */ ++ list_for_each(fq_list, &oh_config->fqs_egress_list) { ++ fqd = list_entry(fq_list, struct fq_duple, fq_list); ++ ++ if (fqd->channel_id == 0) { ++ /* missing channel id in dts */ ++ continue; ++ } ++ ++ for (j = 0; j < fqd->fqs_count; j++) { ++ ret = oh_fq_create(fqd->fqs + j, ++ (fqd->fqs + j)->fqid, ++ fqd->channel_id, 3); ++ if (ret != 0) { ++ dev_err(dpa_oh_dev, "Unable to create egress frame queue %d for OH node %s referenced from node %s!\n", ++ (fqd->fqs + j)->fqid, ++ oh_node->full_name, ++ dpa_oh_node->full_name); ++ _errno = -EINVAL; ++ goto return_kfree; ++ } ++ } ++ } ++ ++ /* Read FQ ids/nums for the DPA OH node */ ++ oh_all_queues = of_get_property(dpa_oh_node, ++ "fsl,qman-frame-queues-oh", &lenp); ++ if (oh_all_queues == NULL) { ++ dev_err(dpa_oh_dev, ++ "No frame queues have been defined for OH node %s referenced from node %s\n", ++ oh_node->full_name, dpa_oh_node->full_name); ++ _errno = -EINVAL; ++ goto return_kfree; ++ } ++ ++ /* Check that the OH error and default FQs are there */ ++ BUG_ON(lenp % (2 * sizeof(*oh_all_queues))); ++ queues_count = lenp / (2 * sizeof(*oh_all_queues)); ++ if (queues_count != 2) { ++ dev_err(dpa_oh_dev, ++ "Error and Default queues must be defined for OH node %s referenced from node %s\n", ++ oh_node->full_name, dpa_oh_node->full_name); ++ _errno = -EINVAL; ++ goto return_kfree; ++ } ++ ++ /* Read the FQIDs defined for this OH port */ ++ dev_dbg(dpa_oh_dev, "Reading %d queues...\n", queues_count); ++ fq_idx = 0; ++ ++ /* Error FQID - must be present */ ++ crt_fqid_base = be32_to_cpu(oh_all_queues[fq_idx++]); ++ crt_fq_count = be32_to_cpu(oh_all_queues[fq_idx++]); ++ if (crt_fq_count != 1) { ++ dev_err(dpa_oh_dev, ++ "Only 1 Error FQ allowed in OH node %s referenced from node %s (read: %d FQIDs).\n", ++ oh_node->full_name, dpa_oh_node->full_name, ++ crt_fq_count); ++ _errno = -EINVAL; ++ goto return_kfree; ++ } ++ oh_config->error_fqid = crt_fqid_base; ++ dev_dbg(dpa_oh_dev, "Read Error FQID 0x%x for OH port %s.\n", ++ oh_config->error_fqid, oh_node->full_name); ++ ++ /* Default FQID - must be present */ ++ crt_fqid_base = be32_to_cpu(oh_all_queues[fq_idx++]); ++ crt_fq_count = be32_to_cpu(oh_all_queues[fq_idx++]); ++ if (crt_fq_count != 1) { ++ dev_err(dpa_oh_dev, ++ "Only 1 Default FQ allowed in OH node %s referenced from %s (read: %d FQIDs).\n", ++ oh_node->full_name, dpa_oh_node->full_name, ++ crt_fq_count); ++ _errno = -EINVAL; ++ goto return_kfree; ++ } ++ oh_config->default_fqid = crt_fqid_base; ++ dev_dbg(dpa_oh_dev, "Read Default FQID 0x%x for OH port %s.\n", ++ oh_config->default_fqid, oh_node->full_name); ++ ++ /* TX FQID - presence is optional */ ++ oh_tx_queues = of_get_property(dpa_oh_node, "fsl,qman-frame-queues-tx", ++ &lenp); ++ if (oh_tx_queues == NULL) { ++ dev_dbg(dpa_oh_dev, ++ "No tx queues have been defined for OH node %s referenced from node %s\n", ++ oh_node->full_name, dpa_oh_node->full_name); ++ goto config_port; ++ } ++ ++ /* Check that queues-tx has only a base and a count defined */ ++ BUG_ON(lenp % (2 * sizeof(*oh_tx_queues))); ++ queues_count = lenp / (2 * sizeof(*oh_tx_queues)); ++ if (queues_count != 1) { ++ dev_err(dpa_oh_dev, ++ "TX queues must be defined in only one <base count> tuple for OH node %s referenced from node %s\n", ++ oh_node->full_name, dpa_oh_node->full_name); ++ _errno = -EINVAL; ++ goto return_kfree; ++ } ++ ++ fq_idx = 0; ++ crt_fqid_base = be32_to_cpu(oh_tx_queues[fq_idx++]); ++ crt_fq_count = be32_to_cpu(oh_tx_queues[fq_idx++]); ++ oh_config->egress_cnt = crt_fq_count; ++ ++ /* Allocate TX queues */ ++ dev_dbg(dpa_oh_dev, "Allocating %d queues for TX...\n", crt_fq_count); ++ oh_config->egress_fqs = devm_kzalloc(dpa_oh_dev, ++ crt_fq_count * sizeof(struct qman_fq), GFP_KERNEL); ++ if (oh_config->egress_fqs == NULL) { ++ dev_err(dpa_oh_dev, ++ "Can't allocate private data for TX queues for OH node %s referenced from node %s!\n", ++ oh_node->full_name, dpa_oh_node->full_name); ++ _errno = -ENOMEM; ++ goto return_kfree; ++ } ++ ++ /* Create TX queues */ ++ for (i = 0; i < crt_fq_count; i++) { ++ ret = oh_fq_create(oh_config->egress_fqs + i, ++ crt_fqid_base + i, (uint16_t)channel_id, 3); ++ if (ret != 0) { ++ dev_err(dpa_oh_dev, ++ "Unable to create TX frame queue %d for OH node %s referenced from node %s!\n", ++ crt_fqid_base + i, oh_node->full_name, ++ dpa_oh_node->full_name); ++ _errno = -EINVAL; ++ goto return_kfree; ++ } ++ } ++ ++config_port: ++ /* Get a handle to the fm_port so we can set ++ * its configuration params ++ */ ++ oh_config->oh_port = fm_port_bind(oh_dev); ++ if (oh_config->oh_port == NULL) { ++ dev_err(dpa_oh_dev, "NULL drvdata from fm port dev %s!\n", ++ oh_node->full_name); ++ _errno = -EINVAL; ++ goto return_kfree; ++ } ++ ++ oh_set_buffer_layout(oh_config->oh_port, &buf_layout); ++ ++ /* read the pool handlers */ ++ crt_ext_pools_count = of_count_phandle_with_args(dpa_oh_node, ++ "fsl,bman-buffer-pools", NULL); ++ if (crt_ext_pools_count <= 0) { ++ dev_info(dpa_oh_dev, ++ "OH port %s has no buffer pool. Fragmentation will not be enabled\n", ++ oh_node->full_name); ++ goto init_port; ++ } ++ ++ /* used for reading ext_pool_size*/ ++ root_node = of_find_node_by_path("/"); ++ if (root_node == NULL) { ++ dev_err(dpa_oh_dev, "of_find_node_by_path(/) failed\n"); ++ _errno = -EINVAL; ++ goto return_kfree; ++ } ++ ++ n_size = of_n_size_cells(root_node); ++ of_node_put(root_node); ++ ++ dev_dbg(dpa_oh_dev, "OH port number of pools = %d\n", ++ crt_ext_pools_count); ++ ++ oh_port_tx_params.num_pools = (uint8_t)crt_ext_pools_count; ++ ++ for (i = 0; i < crt_ext_pools_count; i++) { ++ bpool_node = of_parse_phandle(dpa_oh_node, ++ "fsl,bman-buffer-pools", i); ++ if (bpool_node == NULL) { ++ dev_err(dpa_oh_dev, "Invalid Buffer pool node\n"); ++ _errno = -EINVAL; ++ goto return_kfree; ++ } ++ ++ _errno = of_property_read_u32(bpool_node, "fsl,bpid", &bpid); ++ if (_errno) { ++ dev_err(dpa_oh_dev, "Invalid Buffer Pool ID\n"); ++ _errno = -EINVAL; ++ goto return_kfree; ++ } ++ ++ oh_port_tx_params.pool_param[i].id = (uint8_t)bpid; ++ dev_dbg(dpa_oh_dev, "OH port bpool id = %u\n", bpid); ++ ++ bpool_cfg = of_get_property(bpool_node, ++ "fsl,bpool-ethernet-cfg", &lenp); ++ if (bpool_cfg == NULL) { ++ dev_err(dpa_oh_dev, "Invalid Buffer pool config params\n"); ++ _errno = -EINVAL; ++ goto return_kfree; ++ } ++ ++ ext_pool_size = of_read_number(bpool_cfg + n_size, n_size); ++ oh_port_tx_params.pool_param[i].size = (uint16_t)ext_pool_size; ++ dev_dbg(dpa_oh_dev, "OH port bpool size = %u\n", ++ ext_pool_size); ++ of_node_put(bpool_node); ++ ++ } ++ ++ if (buf_layout.data_align != FRAG_DATA_ALIGN || ++ buf_layout.manip_extra_space != FRAG_MANIP_SPACE) ++ goto init_port; ++ ++ frag_enabled = true; ++ dev_info(dpa_oh_dev, "IP Fragmentation enabled for OH port %d", ++ port_id); ++ ++init_port: ++ of_node_put(oh_node); ++ /* Set Tx params */ ++ dpaa_eth_init_port(tx, oh_config->oh_port, oh_port_tx_params, ++ oh_config->error_fqid, oh_config->default_fqid, (&buf_layout), ++ frag_enabled); ++ /* Set PCD params */ ++ oh_port_pcd_params.cba = oh_alloc_pcd_fqids; ++ oh_port_pcd_params.cbf = oh_free_pcd_fqids; ++ oh_port_pcd_params.dev = dpa_oh_dev; ++ fm_port_pcd_bind(oh_config->oh_port, &oh_port_pcd_params); ++ ++ dev_set_drvdata(dpa_oh_dev, oh_config); ++ ++ /* Enable the OH port */ ++ _errno = fm_port_enable(oh_config->oh_port); ++ if (_errno) ++ goto return_kfree; ++ ++ dev_info(dpa_oh_dev, "OH port %s enabled.\n", oh_node->full_name); ++ ++ /* print of all referenced & created queues */ ++ dump_oh_config(dpa_oh_dev, oh_config); ++ ++ return 0; ++ ++return_kfree: ++ if (bpool_node) ++ of_node_put(bpool_node); ++ if (oh_node) ++ of_node_put(oh_node); ++ if (oh_config && oh_config->egress_fqs) ++ devm_kfree(dpa_oh_dev, oh_config->egress_fqs); ++ ++ list_for_each_safe(fq_list, fq_list_tmp, &oh_config->fqs_ingress_list) { ++ fqd = list_entry(fq_list, struct fq_duple, fq_list); ++ list_del(fq_list); ++ devm_kfree(dpa_oh_dev, fqd->fqs); ++ devm_kfree(dpa_oh_dev, fqd); ++ } ++ ++ list_for_each_safe(fq_list, fq_list_tmp, &oh_config->fqs_egress_list) { ++ fqd = list_entry(fq_list, struct fq_duple, fq_list); ++ list_del(fq_list); ++ devm_kfree(dpa_oh_dev, fqd->fqs); ++ devm_kfree(dpa_oh_dev, fqd); ++ } ++ ++ devm_kfree(dpa_oh_dev, oh_config); ++ return _errno; ++} ++ ++static int __cold oh_port_remove(struct platform_device *_of_dev) ++{ ++ int _errno = 0, i; ++ struct dpa_oh_config_s *oh_config; ++ ++ pr_info("Removing OH port...\n"); ++ ++ oh_config = dev_get_drvdata(&_of_dev->dev); ++ if (oh_config == NULL) { ++ pr_err(KBUILD_MODNAME ++ ": %s:%hu:%s(): No OH config in device private data!\n", ++ KBUILD_BASENAME".c", __LINE__, __func__); ++ _errno = -ENODEV; ++ goto return_error; ++ } ++ ++ if (oh_config->egress_fqs) ++ for (i = 0; i < oh_config->egress_cnt; i++) ++ oh_fq_destroy(oh_config->egress_fqs + i); ++ ++ if (oh_config->oh_port == NULL) { ++ pr_err(KBUILD_MODNAME ++ ": %s:%hu:%s(): No fm port in device private data!\n", ++ KBUILD_BASENAME".c", __LINE__, __func__); ++ _errno = -EINVAL; ++ goto free_egress_fqs; ++ } ++ ++ _errno = fm_port_disable(oh_config->oh_port); ++ ++free_egress_fqs: ++ if (oh_config->egress_fqs) ++ devm_kfree(&_of_dev->dev, oh_config->egress_fqs); ++ devm_kfree(&_of_dev->dev, oh_config); ++ dev_set_drvdata(&_of_dev->dev, NULL); ++ ++return_error: ++ return _errno; ++} ++ ++static struct platform_driver oh_port_driver = { ++ .driver = { ++ .name = KBUILD_MODNAME, ++ .of_match_table = oh_port_match_table, ++ .owner = THIS_MODULE, ++ .pm = OH_PM_OPS, ++ }, ++ .probe = oh_port_probe, ++ .remove = oh_port_remove ++}; ++ ++static int __init __cold oh_port_load(void) ++{ ++ int _errno; ++ ++ pr_info(OH_MOD_DESCRIPTION "\n"); ++ ++ _errno = platform_driver_register(&oh_port_driver); ++ if (_errno < 0) { ++ pr_err(KBUILD_MODNAME ++ ": %s:%hu:%s(): platform_driver_register() = %d\n", ++ KBUILD_BASENAME".c", __LINE__, __func__, _errno); ++ } ++ ++ pr_debug(KBUILD_MODNAME ": %s:%s() ->\n", ++ KBUILD_BASENAME".c", __func__); ++ return _errno; ++} ++module_init(oh_port_load); ++ ++static void __exit __cold oh_port_unload(void) ++{ ++ pr_debug(KBUILD_MODNAME ": -> %s:%s()\n", ++ KBUILD_BASENAME".c", __func__); ++ ++ platform_driver_unregister(&oh_port_driver); ++ ++ pr_debug(KBUILD_MODNAME ": %s:%s() ->\n", ++ KBUILD_BASENAME".c", __func__); ++} ++module_exit(oh_port_unload); +--- /dev/null ++++ b/drivers/net/ethernet/freescale/sdk_dpaa/offline_port.h +@@ -0,0 +1,59 @@ ++/* Copyright 2011 Freescale Semiconductor Inc. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * * Neither the name of Freescale Semiconductor nor the ++ * names of its contributors may be used to endorse or promote products ++ * derived from this software without specific prior written permission. ++ * ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") as published by the Free Software ++ * Foundation, either version 2 of that License or (at your option) any ++ * later version. ++ * ++ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``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 Freescale Semiconductor 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. ++ */ ++ ++#ifndef __OFFLINE_PORT_H ++#define __OFFLINE_PORT_H ++ ++struct fm_port; ++struct qman_fq; ++ ++/* fqs are defined in duples (base_fq, fq_count) */ ++struct fq_duple { ++ struct qman_fq *fqs; ++ int fqs_count; ++ uint16_t channel_id; ++ struct list_head fq_list; ++}; ++ ++/* OH port configuration */ ++struct dpa_oh_config_s { ++ uint32_t error_fqid; ++ uint32_t default_fqid; ++ struct fm_port *oh_port; ++ uint32_t egress_cnt; ++ struct qman_fq *egress_fqs; ++ uint16_t channel; ++ ++ struct list_head fqs_ingress_list; ++ struct list_head fqs_egress_list; ++}; ++ ++#endif /* __OFFLINE_PORT_H */ |