From afd05b80bd7b353046dad919bb46300e0e5aaa0e Mon Sep 17 00:00:00 2001 From: Sheetal Tigadoli Date: Fri, 10 Feb 2023 06:05:51 +0000 Subject: [PATCH] ethernet:marvell:Add pci driver for spruce/oak This patch adds latest Marvell ethernet PCI GPL driver (v3.01.0000) for spruce/oak i.e for 88Q6113/88Q5072. Jira ESDP-16549 Bug 3882239 Change-Id: I15c6fab1d21cd3fa24a3cee76bdce42b27778445 Signed-off-by: Sheetal Tigadoli Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2856987 GVS: Gerrit_Virtual_Submit Reviewed-by: Sumeet Gupta --- .../marvell/mvae_lin5_gpl_v3.01.0001/Makefile | 39 + .../marvell/mvae_lin5_gpl_v3.01.0001/README | 1 + .../marvell/mvae_lin5_gpl_v3.01.0001/Readme | 166 ++ .../marvell/mvae_lin5_gpl_v3.01.0001/ldg_t.h | 34 + .../mvae_lin5_gpl_v3.01.0001/license.txt | 17 + .../marvell/mvae_lin5_gpl_v3.01.0001/m | 9 + .../marvell/mvae_lin5_gpl_v3.01.0001/oak.c | 693 ++++++ .../marvell/mvae_lin5_gpl_v3.01.0001/oak.h | 53 + .../oak_channel_stat.c | 16 + .../oak_channel_stat.h | 75 + .../mvae_lin5_gpl_v3.01.0001/oak_chksum.c | 113 + .../mvae_lin5_gpl_v3.01.0001/oak_chksum.h | 53 + .../mvae_lin5_gpl_v3.01.0001/oak_debug.c | 37 + .../mvae_lin5_gpl_v3.01.0001/oak_debug.h | 22 + .../mvae_lin5_gpl_v3.01.0001/oak_dpm.c | 222 ++ .../mvae_lin5_gpl_v3.01.0001/oak_dpm.h | 79 + .../mvae_lin5_gpl_v3.01.0001/oak_ethtool.c | 335 +++ .../mvae_lin5_gpl_v3.01.0001/oak_ethtool.h | 78 + .../mvae_lin5_gpl_v3.01.0001/oak_gicu.h | 71 + .../mvae_lin5_gpl_v3.01.0001/oak_ioc_flow.h | 39 + .../mvae_lin5_gpl_v3.01.0001/oak_ioc_lgen.h | 39 + .../mvae_lin5_gpl_v3.01.0001/oak_ioc_reg.h | 36 + .../mvae_lin5_gpl_v3.01.0001/oak_ioc_set.h | 28 + .../mvae_lin5_gpl_v3.01.0001/oak_ioc_stat.h | 33 + .../mvae_lin5_gpl_v3.01.0001/oak_irq.c | 498 +++++ .../mvae_lin5_gpl_v3.01.0001/oak_irq.h | 66 + .../mvae_lin5_gpl_v3.01.0001/oak_module.h | 49 + .../mvae_lin5_gpl_v3.01.0001/oak_net.c | 1991 +++++++++++++++++ .../mvae_lin5_gpl_v3.01.0001/oak_net.h | 194 ++ .../mvae_lin5_gpl_v3.01.0001/oak_unimac.c | 1212 ++++++++++ .../mvae_lin5_gpl_v3.01.0001/oak_unimac.h | 713 ++++++ .../oak_unimac_desc.c | 16 + .../oak_unimac_desc.h | 62 + .../oak_unimac_stat.h | 29 + 34 files changed, 7118 insertions(+) create mode 100644 drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/Makefile create mode 100644 drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/README create mode 100644 drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/Readme create mode 100644 drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/ldg_t.h create mode 100644 drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/license.txt create mode 100755 drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/m create mode 100644 drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak.c create mode 100644 drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak.h create mode 100644 drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_channel_stat.c create mode 100644 drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_channel_stat.h create mode 100644 drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_chksum.c create mode 100644 drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_chksum.h create mode 100644 drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_debug.c create mode 100644 drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_debug.h create mode 100644 drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_dpm.c create mode 100644 drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_dpm.h create mode 100644 drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_ethtool.c create mode 100644 drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_ethtool.h create mode 100644 drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_gicu.h create mode 100644 drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_ioc_flow.h create mode 100644 drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_ioc_lgen.h create mode 100644 drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_ioc_reg.h create mode 100644 drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_ioc_set.h create mode 100644 drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_ioc_stat.h create mode 100644 drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_irq.c create mode 100644 drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_irq.h create mode 100644 drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_module.h create mode 100644 drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_net.c create mode 100644 drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_net.h create mode 100644 drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_unimac.c create mode 100644 drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_unimac.h create mode 100644 drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_unimac_desc.c create mode 100644 drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_unimac_desc.h create mode 100644 drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_unimac_stat.h diff --git a/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/Makefile b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/Makefile new file mode 100644 index 00000000..3b094c74 --- /dev/null +++ b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/Makefile @@ -0,0 +1,39 @@ +## +## +## If you received this File from Marvell, you may opt to use, redistribute and/or +## modify this File in accordance with the terms and conditions of the General +## Public License Version 2, June 1991 (the "GPL License"), a copy of which is +## available along with the File in the license.txt file or by writing to the Free +## Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or +## on the worldwide web at http://www.gnu.org/licenses/gpl.txt. +## THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +## WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +## DISCLAIMED. The GPL License provides additional details about this warranty +## disclaimer. +## +## + +# +# Makefile for the Marvell(R) YukonXG PCI Express ethernet driver +# + +obj-m := oak_pci.o + +EXTRA_CFLAGS += -DKERNEL -Werror + +oak_pci-objs := oak.o oak_net.o oak_irq.o oak_ethtool.o oak_unimac.o oak_debug.o oak_chksum.o oak_dpm.o + +ifeq ($(CONFIG_OAK_DEBUG),y) +EXTRA_CFLAGS += -DDEBUG -DSIMULATION -DUNIMAC_MQ_RR +endif + +all: + make W=1 -C /lib/modules/`uname -r`/build M=`pwd` modules + +clean: + rm -f core *.o *.a *.s *.ko .mrvl* .oak* .*.o.cmd Module.symvers Module.markers oak_pci.mod* modules.order + +clobber: clean + rm -f *.log + rm -rf .tmp_versions + diff --git a/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/README b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/README new file mode 100644 index 00000000..702c8422 --- /dev/null +++ b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/README @@ -0,0 +1 @@ +EMPTY diff --git a/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/Readme b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/Readme new file mode 100644 index 00000000..fd73d7d4 --- /dev/null +++ b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/Readme @@ -0,0 +1,166 @@ + +Linux driver for OAK revision A0/B0 (For Linux kernel 5) +-------------------------------------------------------- + +The purpose of this linux driver is to provide basic functionality for network data +exchange between the common Linux protocol implementation and up to 10Gbit Ethernet +networks. + +Additional functionality is provided by the driver to the user space, that allows +control of the particular chip components e.g.: Unimac, ESU and Gicu. + +This functional enhancement is not subject of a common Linux driver but is primarily +used to control and test the operation modes of the test/board in a way that is not +directly supported by the standard network protocoll. See also the 'cmdtool' source. + +Compilation: +------------ + +1) On your target Linux system execute: sudo su +2) Then change to any directory and extract the complete source from the source file: + tar -xvf ./drv-src.tar +3) The driver supports currently the two known Marvell PCIe device vendor IDs: + + vendor: 0x11AB device: 0x0000 (undefined) + vendor: 0x11AB device: 0xABCD + + Execute: lspci -v -d 11ab: -vv + + If the device id is not listed above you do not need to change the ID in the system + but you just can add the alternate device ID to the driver source. + + With a source code editor: + Just copy line 36 in file oak.c (PCI_DEVICE(0x11AB, 0x0000), insert the new line + after the current one and change the device ID 0x0000 to the one listed in your + PCIe configuration space. + +4) COMPILATION: + + Now the driver source has to be compiled against the kernel in use, at least kernel + release 5.4.2. + + # make -C /lib/modules/`uname -r`/build M=`pwd` modules + + The command line above locates your current kernel version (uname -r) and builds a new + kernel object (oak_pci.ko) in the current directory. Alternatively you can change the + KDIR line in the file ./m accordingly and execute: ./m + + If there is any compilation error please report to Marvell. + + Please note that the driver does not support MSI. + For the purpose of parallel processing of interrupts only MSIX is supported by the driver + and must be supported (enabled) by the linux kernel. This should be the default. + +5) LOADING ...: + Once you have successfully generated the kernel object you may load it manually to + the kernel environment from your working directory. This is done with the 'insmod' system + tool. + + When loading the kernel object you should define a debug level. This generates log messages + in the system log file that can be used to trace the driver activity. + + To do this start the background syslog monitoring in a separate command shell: + # tail -f /var/log/syslog & + Alternatively you can also use the dmesg (-c / clear) command. + + Then load the driver with debug level 3 (bit 0 and 1): + + # insmod oak_pci.ko debug=0x00 chan=10 txs=32 rxs=32 rto=100 mhdr=0 + + Debug levels comply to the system defined values e.g.: + + NETIF_MSG_DRV = 0x0001, + NETIF_MSG_PROBE = 0x0002, + NETIF_MSG_LINK = 0x0004, + NETIF_MSG_TIMER = 0x0008, + NETIF_MSG_IFDOWN = 0x0010, + NETIF_MSG_IFUP = 0x0020, + NETIF_MSG_RX_ERR = 0x0040, + NETIF_MSG_TX_ERR = 0x0080, + NETIF_MSG_TX_QUEUED = 0x0100, + NETIF_MSG_INTR = 0x0200, + NETIF_MSG_TX_DONE = 0x0400, + NETIF_MSG_RX_STATUS = 0x0800, + NETIF_MSG_PKTDATA = 0x1000, + NETIF_MSG_HW = 0x2000, + NETIF_MSG_WOL = 0x4000, + +6) PARAMETERS: + Additional load parameter as used in 5) above: + chan=10 This load parameter is used to limit the internal number of transmit/receive + descriptor pairs of the PCIe Unimac at port 11. + The default is 10 and may be changed to a value between 1...10 e.g. + for better debugging. Note that not all channels will be in use by the + Linux protocol stack. + txs=32 This parameter defines the maximal number of descriptor entries on the + transmit side, that is the number of TX packets that can be queued to + each of the configured descriptors. + rxs=32 The same as txs but applies to the receive descriptors. + rto=100 Receive timeout: the timeout before a receive interrupt is executed on any + receive descriptor ring in case there is low traffic and packet delay time + in the ring shall be reduced (to be verfied with icmp response time/ping). + The value is given in micro seconds. + mhdr=1 Instructs the ESU to create the Marvell-header status per rx packet. + This may be required for some Unimac receive mappings that require packet + flow information e.g. source port id .... + There is no need to change it to zero. + +7) Once the kernel object has been loaded successfully the ifconfig -a command should + display a new network interface. + + Up to this level of operation the driver did not yet fully access the Unimac and other + parts. It just checked the existing PCIe device and 'attached' to the kernel's networking + interface and is still waiting to be started. + +8) CONFIGURATION: + When you assign an IP address to the interface e.g. #ifconfig ethX 1.1.1.1 the driver gets + the start command for complete initialization of the hardware. This may be the most critical + situation for any problems. After this the driver should be able to send and receive packets + and process interrupt handling. It's now fully operational. + + In this state you may use the cmdtool (test tool) as shown in 11) below. + +9) STOP: + ifconfig ethX stops activities on the hardware again. The driver is now disconnected from + the network and not fully operational. + +10) UNLOAD: Finally you may unload the driver with: # rmmod oak_pci + +11) CMDTOOL (comamnd line tool, may be packaged sperately) + + In the driver source directory extract the file cli-src.tar . + This creates a subdirectory cmdtool in the current directory. + + Change to the directory: # cd cmdtool + and just execute: # make + + This generates a binary: cli + + Just execute for help: # ./cli + + Syntax error, use: ./cli [ help ] ... + commands : help mac esu gicu pcie gen exec rxmap irq showc dump set ATU VTU TCAM ESTAT ... + + This shows a list of commands that can be executed on top of the driver's interface, that has to be specified + with the argument -if ethX + + Each command can be shown in detail with # ./cli help + + The following most command groups can be used: + + mac - write or read (dump) registers of the Unimac at port 11. + esu - write or read (dump) registers of the ESU. + gicu- write or read (dump) registers of the Gicu. + + irq - displays the interrupt mapping scheme of MSIX interrupts in use for each particular tx/tx channel. + showc - display tx or rx descriptor information for a specific range of descritors but the maximal number + that have been configured with rxs/txs parameter. This displays the byte count of each frame in a + tx/rx descriptor , timestamp information, buffer DMA addresses, Vlan tags, checksum info and the + HW read/write pointers of the Unimac. This command may be used to trace round robin activities on + a particular descriptor ring of the Unimac. + + ATU VTU TCAM ESTAT + Commands for ATU VTU operations or TCAM operations or ESU port statistics. + + rxmap - setup a Unimac rx-mapping on a specific queue[0-9] on a rx descripror/ring. + dump - hexdump of binary file. diff --git a/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/ldg_t.h b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/ldg_t.h new file mode 100644 index 00000000..977e91a4 --- /dev/null +++ b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/ldg_t.h @@ -0,0 +1,34 @@ +/* + * + * If you received this File from Marvell, you may opt to use, redistribute and/or + * modify this File in accordance with the terms and conditions of the General + * Public License Version 2, June 1991 (the "GPL License"), a copy of which is + * available along with the File in the license.txt file or by writing to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + * on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + * DISCLAIMED. The GPL License provides additional details about this warranty + * disclaimer. + * + */ +#ifndef H_LDG_T +#define H_LDG_T + +typedef struct ldg_tstruct { + struct napi_struct napi; + struct oak_tstruct *device; + u64 msi_tx; + u64 msi_rx; + u64 msi_te; + u64 msi_re; + u64 msi_ge; + u64 irq_mask; + u32 irq_first; + u32 irq_count; + u32 msi_grp; + char msiname[32]; +} ldg_t; + +#endif /* #ifndef H_LDG_T */ + diff --git a/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/license.txt b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/license.txt new file mode 100644 index 00000000..d0d66cf3 --- /dev/null +++ b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/license.txt @@ -0,0 +1,17 @@ + +Copyright 2020 Marvell + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +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, or at http://www.gnu.org/licenses/gpl.txt + diff --git a/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/m b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/m new file mode 100755 index 00000000..13466ac8 --- /dev/null +++ b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/m @@ -0,0 +1,9 @@ + + +KDIR=/lib/modules/5.4.2/build + +CONFIG_OAK_DEBUG=y +export CONFIG_OAK_DEBUG + +make -C $KDIR M=`pwd` modules + diff --git a/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak.c b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak.c new file mode 100644 index 00000000..49527faf --- /dev/null +++ b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak.c @@ -0,0 +1,693 @@ +/* + * + * If you received this File from Marvell, you may opt to use, redistribute and/or + * modify this File in accordance with the terms and conditions of the General + * Public License Version 2, June 1991 (the "GPL License"), a copy of which is + * available along with the File in the license.txt file or by writing to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + * on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + * DISCLAIMED. The GPL License provides additional details about this warranty + * disclaimer. + * + */ +#include "oak.h" + +/* private function prototypes */ +static int oak_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id); +static void oak_remove(struct pci_dev *pdev); +static void oak_read_set_mac_address(struct pci_dev *pdev); + +/* Oak driver name, version, and copyright */ +const char oak_driver_name[] = OAK_DRIVER_NAME; +const char oak_driver_version[] = OAK_DRIVER_VERSION; +static const char oak_driver_string[] = OAK_DRIVER_STRING; +static const char oak_driver_copyright[] = OAK_DRIVER_COPYRIGHT; + +/* Oak PCI device ID structure */ +static struct pci_device_id oak_pci_tbl[] = { + {PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x1000)}, + {PCI_DEVICE(0x11AB, 0x0000)}, /* FPGA board */ + {PCI_DEVICE(0x11AB, 0xABCD)}, /* FPGA board */ + {PCI_DEVICE(0x11AB, 0x0f13)}, + {PCI_DEVICE(0x11AB, 0x0a72)}, /* Oak */ +}; + +#ifdef CONFIG_PM_SLEEP +/* Device Power Management (DPM) support */ +static const struct dev_pm_ops oak_dpm_ops = { + .suspend = oak_dpm_suspend, + .resume = oak_dpm_resume, +}; +#endif + +/* PCIe - interface structure */ +static struct pci_driver oak_driver = { + .name = oak_driver_name, + .id_table = oak_pci_tbl, + .probe = oak_probe, + .remove = oak_remove, +#ifdef CONFIG_PM_SLEEP + .driver.pm = &oak_dpm_ops, +#endif +}; + +/* Oak ethtool operation structure */ +static const struct ethtool_ops oak_ethtool_ops = { + .get_drvinfo = oak_ethtool_get_drvinfo, + .get_ethtool_stats = oak_ethtool_get_stats, + .get_strings = oak_ethtool_get_strings, + .get_sset_count = oak_ethtool_get_sscnt, + .get_link = ethtool_op_get_link, + .get_msglevel = oak_dbg_get_level, + .set_msglevel = oak_dbg_set_level, + .get_link_ksettings = oak_ethtool_get_link_ksettings, +}; + +/* Oak netdevice operation structure */ +static const struct net_device_ops oak_netdev_ops = { + .ndo_open = oak_net_open, + .ndo_stop = oak_net_close, + .ndo_start_xmit = oak_net_xmit_frame, + .ndo_do_ioctl = oak_net_ioctl, + .ndo_set_mac_address = oak_net_set_mac_addr, + .ndo_select_queue = oak_net_select_queue, + .ndo_change_mtu = oak_net_esu_set_mtu, + .ndo_validate_addr = eth_validate_addr, +}; + +/* global variable declaration */ +u32 debug; +u32 txs = 2048; +u32 rxs = 2048; +int chan = MAX_NUM_OF_CHANNELS; +int rto = 100; +int mhdr; +u32 port_speed = 10; +int napi_wt = NAPI_POLL_WEIGHT; + +/* software level defination */ +#define SOFTWARE_INIT 10 +#define HARDWARE_INIT 20 +#define SOFTWARE_STARTED 40 + +/* Name : oak_init_module + * Returns : int + * Parameters: + * Description : This function is the entry point for the driver registration. + */ +int oak_init_module(void) +{ + s32 retval = 0; + + pr_info("%s - (%s) version %s\n", + oak_driver_string, oak_driver_name, oak_driver_version); + pr_info("%s\n", oak_driver_copyright); + + /* Register Oak PCI driver with the linux kernel + * PCI device drivers call pci_register_driver() during their + * initialization with a pointer to a structure describing the + * driver (struct pci_driver) + */ + retval = pci_register_driver(&oak_driver); + return retval; +} + +/* Name : exit_module + * Returns : void + * Parameters: + * Description : This function is the exit point for the driver. + */ +void oak_exit_module(void) +{ + /* Unregister Oak PCI driver from linux kernel */ + pci_unregister_driver(&oak_driver); +} + +/* Name : start_software + * Returns : int + * Parameters: struct pci_dev * pdev + * Description: This function registers the oak device to napi + */ +static int oak_start_software(struct pci_dev *pdev) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + int retval = 0; + + netdev->ethtool_ops = &oak_ethtool_ops; + + oak_net_add_napi(netdev); + /* register netdev will take a completed network device structure and + * add it to the kernel interfaces + */ + retval = register_netdev(netdev); + + return retval; +} + +/* Name : release_software + * Returns : void + * Parameters: struct pci_dev * pdev + * Description : This function release the Ethernet interface. + */ +static void oak_release_software(struct pci_dev *pdev) +{ + int retval; + struct net_device *netdev = pci_get_drvdata(pdev); + oak_t *oak; + + oak = netdev_priv(netdev); + +#ifdef CONFIG_PM + /* Set the PCI device power state to D3hot */ + retval = pci_set_power_state(pdev, PCI_D3hot); + if (retval == 0) + pr_info("oak: Device power state D%d\n", pdev->current_state); + else + pr_err("oak: Failed to set the device power state err: %d\n", + retval); + + /* Remove sysfs entry */ + oak_dpm_remove_sysfs(oak); +#endif + + oakdbg(debug, PROBE, "pdev=%p ndev=%p", pdev, pci_get_drvdata(pdev)); + + free_netdev(netdev); +} + +/* Name : oak_init_software + * Returns : int + * Parameters : struct pci_dev *pdev + * Description : This function initializes the PCIe interface and network device + */ +static int oak_init_software(struct pci_dev *pdev) +{ + struct net_device *netdev = NULL; + oak_t *oak; + int retval = 0; + + /* Allocates and sets up an Ethernet device + * Fill in the fields of the device structure with Ethernet-generic + * values. Basically does everything except registering the device + */ + netdev = alloc_etherdev_mq(sizeof(oak_t), chan); + + if (netdev) { + /* Set the sysfs physical device reference for the network + * logical device if set prior to registration will cause a + * symlink during initialization. + */ + SET_NETDEV_DEV(netdev, &pdev->dev); + /* Set private driver data pointer for a pci_dev */ + pci_set_drvdata(pdev, netdev); + oak = netdev_priv(netdev); + oak->device = &pdev->dev; + oak->netdev = netdev; + oak->pdev = pdev; +#ifdef CONFIG_PM + /* Create sysfs entry for D0, D1, D2 and D3 states testing */ + oak_dpm_create_sysfs(oak); +#endif + /* Register network device operations */ + netdev->netdev_ops = &oak_netdev_ops; + /* Set hardware's checksum Offload capabilities */ + netdev->features = oak_chksum_get_config(); + /* Set the min and max MTU size */ + oak_set_mtu_config(netdev); + spin_lock_init(&oak->lock); + } else { + /* If software initialization fails then we need to release the + * device and free allocated structure with retnval as ENOMEM + */ + oak_release_software(pdev); + retval = -ENOMEM; + } + + oakdbg(debug, PROBE, "pdev=%p ndev=%p err=%d", + pdev, pci_get_drvdata(pdev), retval); + + return retval; +} + +/* Name : oak_probe + * Returns : int + * Parameters : struct pci_dev *pdev, const struct pci_device_id *dev_id + * Description : This function probe the device and call initialization + * functions + */ +static int oak_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id) +{ + int retval = 0; + int err = 0; + oak_t *adapter = NULL; + +#ifdef CONFIG_PM + /* Set PCI device power state to D0 */ + err = pci_set_power_state(pdev, PCI_D0); + if (err == 0) + pr_info("oak: Device power state D%d\n", pdev->current_state); + else + pr_err("oak: Failed to set the device power state err: %d\n", + err); +#endif + /* Initialize the oak software */ + err = oak_init_software(pdev); + + if (err == 0) { + struct net_device *netdev = pci_get_drvdata(pdev); + + adapter = netdev_priv(netdev); + /* Set level as software initialization */ + adapter->level = SOFTWARE_INIT; + + /* Initialize the Oak hardware */ + err = oak_init_hardware(pdev); + + if (err == 0) { + /* Read MAC from firmware registers and set to the + * device address register. + */ + oak_read_set_mac_address(pdev); + err = oak_start_software(pdev); + if (err == 0) + /* Set level as driver software started */ + adapter->level = SOFTWARE_STARTED; + else + retval = err; + } else { + retval = err; + } + } else { + retval = err; + } + + if (err == 0) { + retval = err; + if (adapter->sw_base) + pr_info("%s[%d] - ESU register access is supported", + oak_driver_name, pdev->devfn); + } else { + oak_remove(pdev); + } + + oakdbg(debug, PROBE, "pdev=%p ndev=%p err=%d", pdev, + pci_get_drvdata(pdev), err); + + return retval; +} + +/* Name : oak_get_msix_resources + * Returns : int + * Parameters : struct pci_dev * pdev + * Description : This function allocates the MSIX resources. + */ +static int oak_get_msix_resources(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + oak_t *adapter = netdev_priv(dev); + u32 num_irqs; + u32 num_cpus = num_online_cpus(); + u32 i; + int cnt; + int retval = 0; + + num_irqs = sizeof(adapter->gicu.msi_vec) / sizeof(struct msix_entry); + /* Return the number of device's MSI-X table entries */ + cnt = pci_msix_vec_count(pdev); + + if (cnt <= 0) { + retval = -EFAULT; + } else { + if (cnt <= num_irqs) + num_irqs = cnt; + if (num_irqs > num_cpus) + num_irqs = num_cpus; + for (i = 0; i < num_irqs; i++) { + adapter->gicu.msi_vec[i].vector = 0; + adapter->gicu.msi_vec[i].entry = i; + } +#ifdef OAK_MSIX_LEGACY + /* Configure device's MSI-X capability structure - Setup the + * MSI-X capability structure of device function with a + * maximum possible number of interrupts in the range between + * minvec and maxvec upon its software driver call to request + * for MSI-X mode enabled on its hardware device function. + * It returns a negative errno if an error occurs. + * If it succeeds, it returns the actual number of interrupts + * allocated and indicates the successful configuration of + * MSI-X capability structure with new allocated MSI-X + * interrupts. + */ + retval = pci_enable_msix_range(pdev, adapter->gicu.msi_vec, + num_irqs, num_irqs); +#else + /* Which allocates up to max_vecs interrupt vectors for a PCI + * device. + */ + retval = pci_alloc_irq_vectors(pdev, num_irqs, num_irqs, + PCI_IRQ_ALL_TYPES); + if (retval) { + pr_info("int vec count %d\n", retval); + num_irqs = retval; + } +#endif + if (retval >= 0) + retval = 0; + + adapter->gicu.num_ldg = num_irqs; + oakdbg(debug, PROBE, "pdev=%p ndev=%p num_irqs=%d/%d retval=%d", + pdev, dev, num_irqs, cnt, retval); + } + return retval; +} + +/* Name : release_hardware + * Returns : void + * Parameters: struct pci_dev * pdev + * Description : This function de initializes the hardware and + * release the resources. + */ +void oak_release_hardware(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + oak_t *adapter = netdev_priv(dev); + int err = 0; + + if (adapter->gicu.num_ldg > 0) + pci_disable_msix(pdev); + /* Release reserved PCI I/O and memory resources */ + pci_release_regions(pdev); + pci_disable_device(pdev); + + oakdbg(debug, PROBE, "pdev=%p ndev=%p err=%d", + pdev, pci_get_drvdata(pdev), err); +} + +/* Name : oak_init_map_config + * Returns : int + * Parameters : struct pci_dev *pdev + * Description : This function create a virtual mapping cookie for a PCI BAR + */ +static int oak_init_map_config(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + oak_t *adapter = netdev_priv(dev); + u32 mem_flags; + int retval = 0; + + /* This function returns the flags associated with the PCI resource. + * Resource flags are used to define some features of the individual + * resource. For PCI resources associated with PCI I/O regions, the + * information is extracted from the base address registers, but can + * come from elsewhere for resources not associated with PCI devices. + */ + mem_flags = pci_resource_flags(pdev, 2); + + if ((mem_flags & IORESOURCE_MEM) == 0) + retval = -EINVAL; + else + adapter->sw_base = pci_iomap(pdev, 2, 0); + + oakdbg(debug, PROBE, + "Device found: dom=%d bus=%d dev=%d fun=%d reg-addr=%p", + pci_domain_nr(pdev->bus), pdev->bus->number, + PCI_SLOT(pdev->devfn), pdev->devfn, adapter->um_base); + + return retval; +} + +/* Name : oak_init_read_write_config + * Returns : int + * Parameters : struct pci_dev *pdev + * Description : This function read and write into config space. + */ +static int oak_init_read_write_config(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + oak_t *adapter = netdev_priv(dev); + u32 v0, v1; + u16 devctl; + int retval = 0; + + /* Create virtual mapping the PCI BAR configuration space before doing + * read or write into configuration space + */ + retval = oak_init_map_config(pdev); + + if (retval != 0) + pr_err("PCI config space mapping failed %d\n", retval); + + /* After the driver has detected the device, it usually needs to read + * from or write to the three address spaces: memory, port, and + * configuration. In particular, accessing the configuration space is + * vital to the driver, because it is the only way it can find out + * where the device is mapped in memory and in the I/O space. + */ + pci_read_config_dword(pdev, 0x10, &v0); + pci_read_config_dword(pdev, 0x14, &v1); + v0 &= 0xfffffff0U; + v0 |= 1U; + pci_write_config_dword(pdev, 0x944, v1); + pci_write_config_dword(pdev, 0x940, v0); + + pcie_capability_read_word(pdev, PCI_EXP_DEVCTL, &devctl); + /* Calculate and store TX max burst size */ + adapter->rrs = + (u16)(1U << (((devctl & PCI_EXP_DEVCTL_READRQ) >> 12) + 6)); + + if (retval == 0) + retval = oak_get_msix_resources(pdev); + + return retval; +} + +/* Name : oak_init_pci_config + * Returns : int + * Parameters : struct pci_dev *pdev + * Description : This function initialize oak pci config space + */ +static int oak_init_pci_config(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + oak_t *adapter = netdev_priv(dev); + int err = 0; + + err = pci_request_regions(pdev, OAK_DRIVER_NAME); + + if (err == 0) { + /* Enables bus-mastering for device */ + pci_set_master(pdev); + /* Save the PCI configuration space */ + pci_save_state(pdev); + + /* create a virtual mapping cookie for a PCI BAR. Using + * this function you will get a __iomem address to your + * device BAR. You can access it using ioread*() and + * iowrite*(). These functions hide the details if this + * is a MMIO or PIO address space and will just do what + * you expect from them in the correct way. + * void __iomem * pci_iomap(struct pci_dev * dev, + * int bar, unsigned long, maxlen); + * maxlen specifies the maximum length to map. If you + * want to get access to the complete BAR without + * checking for its length first, pass 0 here. + */ + adapter->um_base = pci_iomap(pdev, 0, 0); + + if (!adapter->um_base) + err = -ENOMEM; + else + err = oak_init_read_write_config(pdev); + } + return err; +} + +/* Name : oak_init_hardware + * Returns : int + * Parameters : struct pci_dev * pdev + * Description : This function initializes oak hardware + */ +int oak_init_hardware(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + oak_t *adapter = netdev_priv(dev); + int retval = 0; + u32 mem_flags; + + /* Initialize device before it's used by a driver */ + retval = pci_enable_device(pdev); + + if (retval == 0) { + /* This function returns the flags associated with this + * resource. Resource flags are used to define some features + * of the individual resource. For PCI resources associated with + * PCI I/O regions, the information is extracted from the base + * address registers, but can come from elsewhere for resources + * not associated with PCI devices + */ + mem_flags = pci_resource_flags(pdev, 0); + + if ((mem_flags & IORESOURCE_MEM) == 0) { + retval = -EINVAL; + } else { + pci_read_config_dword(pdev, PCI_CLASS_REVISION, + &adapter->pci_class_revision); + adapter->pci_class_revision &= 0x0000000FU; + if (adapter->pci_class_revision > OAK_REVISION_B0) { + retval = -EINVAL; + } else { + retval = + dma_set_mask_and_coherent(&pdev->dev, + DMA_BIT_MASK(64)); + } + if (retval == 0) + retval = oak_init_pci_config(pdev); + } + } else { + pr_err("PCI enable device failed %d\n", retval); + } + + oakdbg(debug, PROBE, "pdev=%p ndev=%p err=%d", pdev, + pci_get_drvdata(pdev), retval); + return retval; +} + +/* Name : oak_set_mtu_config + * Returns : void + * Parameters: struct net_device *netdev + * Description: This function sets the min and max MTU size in the linux netdev. + */ +void oak_set_mtu_config(struct net_device *netdev) +{ + netdev->min_mtu = ETH_MIN_MTU; + netdev->max_mtu = OAK_MAX_JUMBO_FRAME_SIZE - (ETH_HLEN + ETH_FCS_LEN); +} + +/* Name : oak_read_set_mac_address + * Returns : void + * Parameters : struct net_device *netdev + * Description : During startup, firmware writes its own MAC address into the + * EPU_DATA2 and EPU_DATA3 registers. When loaded, this function obtains MAC + * address from EPU_DATA registers and increments the NIC specific part of the + * MAC address by 1. + * The MAC address consists of two parts: + * Octet 1-3: Organizationally Unique Identifier (OUI) + * Octet 4-6: Network Interface Controller (NIC) specific part. + * The increment shall be done for the NIC specific part only. + */ +static void oak_read_set_mac_address(struct pci_dev *pdev) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + oak_t *np = netdev_priv(netdev); + /* Octet 1-3 from EPU_DATA2 */ + u32 mac_oui; + /* Octet 4-6 from EPU_DATA3 */ + u32 mac_nic; + u32 mac_update; + u32 oui_be; + unsigned char device_mac[sizeof(uint64_t)]; + char nic_mac[ETH_ALEN]; + bool rc; + + /* Read MAC address from EPU_DATA2 and EPU_DATA3 registers */ + mac_oui = oak_unimac_io_read_32(np, OAK_EPU_DATA2); + mac_nic = oak_unimac_io_read_32(np, OAK_EPU_DATA3); + pr_debug("Device MAC address OUI (EPU_DATA2) : 0x%x\n", + cpu_to_be32(mac_oui)); + pr_debug("Device MAC address NIC (EPU_DATA3) : 0x%x\n", + cpu_to_be32(mac_nic)); + + /* Copy Oak device MAC address and print */ + memcpy((void *)&device_mac, (void *)&mac_oui, sizeof(mac_oui)); + memcpy((void *)&device_mac + 4, (void *)&mac_nic, sizeof(mac_nic)); + + rc = is_valid_ether_addr(device_mac); + if (rc != 0) { + /* Copy the OUI into network device structure */ + oui_be = (__force u32)cpu_to_be32(mac_oui); + + nic_mac[0] = (char)((oui_be & 0xff000000U) >> 24); + nic_mac[1] = (char)((oui_be & 0x00ff0000U) >> 16); + nic_mac[2] = (char)((oui_be & 0x0000ff00U) >> 8); + + /* To construct MAC address we need to takeout one bye from + * EPU_DATA2 and two bytes from EPU_DATA3 register + * As an example: MAC address is 11:22:33:44:55:66 set by + * firmware then Data read from register will give below + * little endian output + * EPU_DATA2 - 0x44332211 + * EPU_DATA3 - 0x6655 + * Hence it is required to convert to endian using cpu_to_be32 + * EPU_DATA2 - 0x11223344 + * EPU_DATA3 - 0x55660000 + * To construct NIC part as 0x445566 + */ + mac_update = (__force u32)cpu_to_be32(mac_nic) >> 16; + mac_update = ((__force u32)cpu_to_be32(mac_oui) & 0xFFU) << 16 | + mac_update; + /* Update the NIC part by one */ + mac_update = mac_update + 1; + + /* Copy updated NIC to netdev layer */ + nic_mac[3] = (char)((mac_update & 0xff0000U) >> 16); + nic_mac[4] = (char)((mac_update & 0x00ff00U) >> 8); + nic_mac[5] = (char)(mac_update & 0x0000ffU); + + /* Validate locally constructed NIC MAC address and + * Copy to mac_address member of oak_t if its valid. + */ + rc = is_valid_ether_addr(nic_mac); + if (rc != 0) { + pr_info("Device MAC address : %pM\n", device_mac); + ether_addr_copy(np->mac_address, nic_mac); + ether_addr_copy(netdev->dev_addr, nic_mac); + pr_info("MAC address of NIC : %pM\n", nic_mac); + } + } +} + +/* Name : stop_software + * Returns : void + * Parameters: struct pci_dev * pdev + * Description: This function unregisters the oak driver from netdev + */ +static void oak_stop_software(struct pci_dev *pdev) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + + oak_net_del_napi(netdev); + /* This function shuts down a device interface and removes it from + * the kernel tables. + */ + unregister_netdev(netdev); +} + +/* Name : oak_remove + * Returns : void + * Parameters : struct pci_dev * pdev + * Description : This function remove the device from kernel + */ +static void oak_remove(struct pci_dev *pdev) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + oak_t *adapter = NULL; + + if (netdev) + adapter = netdev_priv(netdev); + if (adapter) { + if (adapter->level >= SOFTWARE_STARTED) + oak_stop_software(pdev); + if (adapter->level >= HARDWARE_INIT) + oak_release_hardware(pdev); + if (adapter->level >= SOFTWARE_INIT) + oak_release_software(pdev); + } + oakdbg(debug, PROBE, "pdev=%p ndev=%p", pdev, pci_get_drvdata(pdev)); + +#ifndef OAK_MSIX_LEGACY + /* Free previously allocated IRQs for a device */ + pci_free_irq_vectors(pdev); +#endif +} diff --git a/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak.h b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak.h new file mode 100644 index 00000000..979e2590 --- /dev/null +++ b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak.h @@ -0,0 +1,53 @@ +/* + * + * If you received this File from Marvell, you may opt to use, redistribute and/or + * modify this File in accordance with the terms and conditions of the General + * Public License Version 2, June 1991 (the "GPL License"), a copy of which is + * available along with the File in the license.txt file or by writing to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + * on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + * DISCLAIMED. The GPL License provides additional details about this warranty + * disclaimer. + * + */ +#ifndef H_OAK +#define H_OAK + +/* Include for relation to classifier oak_unimac */ +#include "oak_unimac.h" +/* Include for relation to classifier oak_net */ +#include "oak_net.h" +/* Include for relation to classifier oak_ethtool */ +#include "oak_ethtool.h" +#include "oak_debug.h" +/* Include for relation to classifier oak_module */ +#include "oak_module.h" +#include "oak_chksum.h" +#include "oak_dpm.h" + +#define OAK_DRIVER_NAME "oak" + +#define OAK_DRIVER_STRING "Marvell PCIe Switch Driver" + +#define OAK_DRIVER_VERSION "3.01.0001" + +#define OAK_DRIVER_COPYRIGHT "Copyright (c) Marvell - 2018" + +#define OAK_MAX_JUMBO_FRAME_SIZE (10 * 1024) + +/* EPU data register offset definition */ +#define OAK_EPU_DATA2 0xF208 +#define OAK_EPU_DATA3 0xF20C + +u32 debug; +u32 txs; +u32 rxs; +int chan; +int rto; +int mhdr; +u32 port_speed; + +#endif /* #ifndef H_OAK */ + diff --git a/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_channel_stat.c b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_channel_stat.c new file mode 100644 index 00000000..aacd5f64 --- /dev/null +++ b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_channel_stat.c @@ -0,0 +1,16 @@ +/* + * + * If you received this File from Marvell, you may opt to use, redistribute and/or + * modify this File in accordance with the terms and conditions of the General + * Public License Version 2, June 1991 (the "GPL License"), a copy of which is + * available along with the File in the license.txt file or by writing to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + * on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + * DISCLAIMED. The GPL License provides additional details about this warranty + * disclaimer. + * + */ +#include "oak_channel_stat.h" + diff --git a/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_channel_stat.h b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_channel_stat.h new file mode 100644 index 00000000..f5092a21 --- /dev/null +++ b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_channel_stat.h @@ -0,0 +1,75 @@ +/* + * + * If you received this File from Marvell, you may opt to use, redistribute and/or + * modify this File in accordance with the terms and conditions of the General + * Public License Version 2, June 1991 (the "GPL License"), a copy of which is + * available along with the File in the license.txt file or by writing to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + * on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + * DISCLAIMED. The GPL License provides additional details about this warranty + * disclaimer. + * + */ +#ifndef H_OAK_CHANNEL_STAT +#define H_OAK_CHANNEL_STAT + +typedef struct oak_chan_infostruct { + u32 flags; + u32 r_count; + u32 r_size; + u32 r_pend; + u32 r_widx; + u32 r_ridx; + u32 r_len; +} oak_chan_info; + +typedef struct oak_driver_rx_statstruct { + u64 channel; + u64 rx_alloc_pages; + u64 rx_unmap_pages; + u64 rx_alloc_error; + u64 rx_frame_error; + u64 rx_errors; + u64 rx_interrupts; + u64 rx_goodframe; + u64 rx_byte_count; + u64 rx_vlan; + u64 rx_badframe; + u64 rx_no_sof; + u64 rx_no_eof; + u64 rx_badcrc; + u64 rx_badcsum; + u64 rx_l4p_ok; + u64 rx_ip4_ok; + u64 rx_nores; + u64 rx_64; + u64 rx_128; + u64 rx_256; + u64 rx_512; + u64 rx_1024; + u64 rx_2048; + u64 rx_fragments; +} oak_driver_rx_stat; + +typedef struct oak_driver_tx_statstruct { + u64 channel; + u64 tx_frame_count; + u64 tx_frame_compl; + u64 tx_byte_count; + u64 tx_fragm_count; + u64 tx_drop; + u64 tx_errors; + u64 tx_interrupts; + u64 tx_stall_count; + u64 tx_64; + u64 tx_128; + u64 tx_256; + u64 tx_512; + u64 tx_1024; + u64 tx_2048; +} oak_driver_tx_stat; + +#endif /* #ifndef H_OAK_CHANNEL_STAT */ + diff --git a/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_chksum.c b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_chksum.c new file mode 100644 index 00000000..58e96d17 --- /dev/null +++ b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_chksum.c @@ -0,0 +1,113 @@ +/* + * + * If you received this File from Marvell, you may opt to use, redistribute and/or + * modify this File in accordance with the terms and conditions of the General + * Public License Version 2, June 1991 (the "GPL License"), a copy of which is + * available along with the File in the license.txt file or by writing to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + * on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + * DISCLAIMED. The GPL License provides additional details about this warranty + * disclaimer. + * + */ +#include "oak_net.h" +#include "oak_debug.h" +#include "oak_chksum.h" + +/* Name : oak_chksum_get_config + * Returns : netdev_features_t + * Parameters : void + * Description : This function provides Oak Hardware's Checksum Offload + * capabilities + */ +netdev_features_t oak_chksum_get_config(void) +{ + netdev_features_t features = OAK_CHKSUM_TYPE; + + /* Oak HW Supports L3 & L3 Checksum Offload and Freagmented frame, + * so scatter gather need to be enabled. + */ + features |= NETIF_F_SG; + + return features; +} + +/* Name : oak_chksum_get_tx_config + * Returns : u32 + * Parameters : struct sk_buff *skb, u32 *cs_l3, u32 *cs_l4 + * Description : This function returns the Checksum Offload configuration + * for the transmit frame. + */ +u32 oak_chksum_get_tx_config(struct sk_buff *skb, u32 *cs_l3, + u32 *cs_l4) +{ + u32 retval = 0; + int prot; + + *cs_l3 = 0; + *cs_l4 = 0; + + if (skb->ip_summed == CHECKSUM_PARTIAL) { + prot = oak_net_skb_tx_protocol_type(skb); + if (prot == L3_L4_CHKSUM) { + *cs_l3 = 1; + *cs_l4 = 1; + } else if (prot == L3_CHKSUM) { + *cs_l3 = 1; + *cs_l4 = 0; + } else { + /* Setting cs_l3 & cs_l4 to zero is done in error case after + * this function returns. + */ + retval = 1; + } + } else { + retval = 1; + } + + return retval; +} + +/* Name : oak_chksum_get_rx_config + * Returns : u32 + * Parameters : struct oak_rx_chan_t *rxc, struct oak_rxs_t *rsr + * Description : This function returns the current receive frames + * checksum state. + */ +int oak_chksum_get_rx_config(oak_rx_chan_t *rxc, oak_rxs_t *rsr) +{ + int retval = CHECKSUM_NONE; + + if (rsr->vlan == 1) + ++rxc->stat.rx_vlan; + + if (rsr->l3_ipv4 == 1 || rsr->l3_ipv6 == 1) { + if (rsr->l4_prot == OAK_TCP_IP_FRAME || + rsr->l4_prot == OAK_TCP_UDP_FRAME) { + if (rsr->l4_chk_ok == 1) { + rxc->stat.rx_l4p_ok++; + retval = CHECKSUM_UNNECESSARY; + } + } else if (rsr->l3_ipv4 == 1 && rsr->ipv4_hdr_ok == 1) { + /* + * Linux documentation for CHECKSUM_PARTIAL + * in include/linux/skbuff.h this state may occur on a packet + * received directly from another Linux OS, + * e.g., a virtualized Linux kernel on the same host, + * or it may be set in the input path in GRO or remote checksum + * offload. + * As per the discussion with the Linux kernel netdev forum + * setting ip_summed to PARTIAL on receive is only valid + * for software/virtual devices, never real HW. + * For a frame where the checksum is not verified by the HW, + * the flag will be set as CHECKSUM_NONE such that + * linux netdev layer verifies the same. + */ + rxc->stat.rx_ip4_ok++; + } + } + + return retval; +} diff --git a/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_chksum.h b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_chksum.h new file mode 100644 index 00000000..c0b42e41 --- /dev/null +++ b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_chksum.h @@ -0,0 +1,53 @@ +/* + * + * If you received this File from Marvell, you may opt to use, redistribute and/or + * modify this File in accordance with the terms and conditions of the General + * Public License Version 2, June 1991 (the "GPL License"), a copy of which is + * available along with the File in the license.txt file or by writing to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + * on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + * DISCLAIMED. The GPL License provides additional details about this warranty + * disclaimer. + * + */ +#ifndef H_OAK_CHKSUM +#define H_OAK_CHKSUM + +/* Checksum configurations supported by the Oak HW */ +#define OAK_CHKSUM_TYPE (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM) + +#define L3_L4_CHKSUM 2 +#define L3_CHKSUM 1 +#define NO_CHKSUM 0 + +#define OAK_TCP_IP_FRAME 1 +#define OAK_TCP_UDP_FRAME 2 + +/* Name : oak_chksum_get_config + * Returns : netdev_features_t + * Parameters : void + * Description : This function provides Oak Hardware's Checksum Offload + * capabilities. + */ +netdev_features_t oak_chksum_get_config(void); + +/* Name : oak_chksum_get_tx_config + * Returns : u32 + * Parameters : struct sk_buff *skb, u32 *cs_l3, u32 *cs_l4 + * Description : This function returns the Checksum Offload configuration for + * the transmit frame. + */ +u32 oak_chksum_get_tx_config(struct sk_buff *skb, u32 *cs_l3, + u32 *cs_l4); + +/* Name : oak_chksum_get_rx_config + * Returns : u32 + * Parameters : oak_rx_chan_t*, oak_rxs_t* + * Description : This function returns the current receive frames + * checksum state. + */ +int oak_chksum_get_rx_config(oak_rx_chan_t *rxc, oak_rxs_t *rsr); + +#endif /* #ifndef H_OAK_CHKSUM */ diff --git a/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_debug.c b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_debug.c new file mode 100644 index 00000000..ea2189b9 --- /dev/null +++ b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_debug.c @@ -0,0 +1,37 @@ +/* + * + * If you received this File from Marvell, you may opt to use, redistribute and/or + * modify this File in accordance with the terms and conditions of the General + * Public License Version 2, June 1991 (the "GPL License"), a copy of which is + * available along with the File in the license.txt file or by writing to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + * on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + * DISCLAIMED. The GPL License provides additional details about this warranty + * disclaimer. + * + */ + +#include "oak_debug.h" + +/* Name : oak_dbg_set_level + * Returns : void + * Parameters : struct net_device *dev, u32 level + * Description : This function set the debug level of the Ethernet interface. + */ +void oak_dbg_set_level(struct net_device *dev, u32 level) +{ + debug = level; +} + +/* Name : oak_dbg_get_level + * Returns : u32 + * Parameters : struct net_device *dev + * Description : This function get the debug level of the Ethernet interface. + */ +u32 oak_dbg_get_level(struct net_device *dev) +{ + return debug; +} + diff --git a/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_debug.h b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_debug.h new file mode 100644 index 00000000..24aef969 --- /dev/null +++ b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_debug.h @@ -0,0 +1,22 @@ + +/* + * + * If you received this File from Marvell, you may opt to use, redistribute and/or + * modify this File in accordance with the terms and conditions of the General + * Public License Version 2, June 1991 (the "GPL License"), a copy of which is + * available along with the File in the license.txt file or by writing to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + * on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + * DISCLAIMED. The GPL License provides additional details about this warranty + * disclaimer. + * + */ + +#include "linux/etherdevice.h" +#include "linux/pci.h" +#include "oak_irq.h" + +void oak_dbg_set_level(struct net_device *dev, u32 level); +u32 oak_dbg_get_level(struct net_device *dev); diff --git a/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_dpm.c b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_dpm.c new file mode 100644 index 00000000..e1dcaa1f --- /dev/null +++ b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_dpm.c @@ -0,0 +1,222 @@ +/* + * + * If you received this File from Marvell, you may opt to use, redistribute and/or + * modify this File in accordance with the terms and conditions of the General + * Public License Version 2, June 1991 (the "GPL License"), a copy of which is + * available along with the File in the license.txt file or by writing to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + * on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + * DISCLAIMED. The GPL License provides additional details about this warranty + * disclaimer. + * + */ + +#ifdef CONFIG_PM + +#include "oak_dpm.h" +#include "oak_net.h" + +/* Name : oak_dpm_set_power_state + * Returns : void + * Parameters : struct device *dev, pci_power_t state + * Description : This function set the device power state + */ +void oak_dpm_set_power_state(struct device *dev, pci_power_t state) +{ + int retval; + pci_power_t D0 = 0; + struct net_device *ndev = dev_get_drvdata(dev); + + oak_t *np = netdev_priv(ndev); + + /* If user input state is D0 and current_state not D0 + * i.e. current state is D3hot (D1, D2, D3) then + * we call resume operation + */ + if (state == PCI_D0 && np->pdev->current_state != D0) { + retval = oak_dpm_resume(dev); + if (retval) + pr_info("oak_dpm_resume: failed.\n"); + } + /* If user input state is D1 or D2 or D3 and current_state D0 + * then we do the suspend operation + */ + else if (((state == PCI_D1) || + (state == PCI_D2) || + (state == PCI_D3hot)) && (np->pdev->current_state == D0)) { + retval = oak_dpm_suspend(dev); + if (retval != 0) + pr_info("oak_dpm_suspend: failed\n"); + } +} + +/* Name : oak_dpm_state_store + * Returns : ssize_t + * Parameters : struct device *dev, struct device_attribute *attr, + * const char *buf, size_t count + * Description : This function store the sysfs entry + */ +static ssize_t oak_dpm_state_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + pci_power_t pwr; + bool d0, d1, d2, d3; + + /* Validate user input */ + d0 = sysfs_streq(buf, "D0"); + d1 = sysfs_streq(buf, "D1"); + d2 = sysfs_streq(buf, "D2"); + d3 = sysfs_streq(buf, "D3"); + + /* D0 as input, set device as D0 + * D1, D2, and D3 as input, set the device as D3 + * For any other input, error message is triggered. + */ + if (d0) { + pwr = PCI_D0; + oak_dpm_set_power_state(dev, pwr); + } else if (d1 || d2 || d3) { + pwr = PCI_D3hot; + oak_dpm_set_power_state(dev, pwr); + } else { + pr_err("oak: Wrong input, Device power states are D0, D1, D2 or D3\n"); + } + + /* With the current sysfs implementation the kobject reference count is + * only modified directly by the function sysfs_schedule_callback(). + */ + return count; +} + +/* The sysfs kernel object device attribute file oak_dpm_state + * is wrtite only. Hence only oak_dpm_state_store function is + * called by kernel when a user does input to oak_dpm_state file + */ +static DEVICE_ATTR_WO(oak_dpm_state); + +/* Name : oak_dpm_create_sysfs + * Returns : void + * Parameters : oak_t *np + * Description : This function creates sysfs entry for setting device power + * states D0, D1, D2 and D3. + */ +void oak_dpm_create_sysfs(oak_t *np) +{ + int status; + + status = sysfs_create_file(&np->pdev->dev.kobj, + &dev_attr_oak_dpm_state.attr); + if (status) + pr_err("oak: Failed to create sysfs entry\n"); +} + +/* Name : oak_dpm_remove_sysfs + * Returns : void + * Parameters : oak_t *np + * Description : This function removes sysfs entry of device power states + */ +void oak_dpm_remove_sysfs(oak_t *np) +{ + sysfs_remove_file(&np->pdev->dev.kobj, &dev_attr_oak_dpm_state.attr); +} + +#ifdef CONFIG_PM_SLEEP + +/* Name : oak_dpm_suspend + * Returns : int + * Parameters : struct device *dev + * Description : This function is called when system goes into suspend state + * and put the device into sleep state + */ +int __maybe_unused oak_dpm_suspend(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + oak_t *np = netdev_priv(ndev); + int retval = 0; + + /* If interface is up then, do driver specific operations + * gracefully close the interface + */ + retval = netif_running(ndev); + if (retval) + oak_net_close(ndev); + + /* Inform to PCI core, wake me up from D3hot when event triggers */ + pci_enable_wake(np->pdev, PCI_D3hot, true); + + /* Release the oak hardware */ + oak_release_hardware(np->pdev); + + /* To synchronize changes hold rtnl lock */ + rtnl_lock(); + + /* Save the PCI state and prepare to go for sleep */ + pci_save_state(np->pdev); + pci_prepare_to_sleep(np->pdev); + + /* Set the device power state as D3hot */ + retval = pci_set_power_state(np->pdev, PCI_D3hot); + if (retval == 0) + pr_info("%s: dpm state=D%d\n", __func__, + np->pdev->current_state); + else + pr_err("%s: Failed to set the device power state err: %d\n", + __func__, retval); + + /* Release the rtnl lock */ + rtnl_unlock(); + + return retval; +} + +/* Name : oak_dpm_resume + * Returns : int + * Parameters : struct device *dev + * Description : This function called when system goes into resume state and put + * the device into active state + */ +int __maybe_unused oak_dpm_resume(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + oak_t *np = netdev_priv(ndev); + int retval = 0; + + /* To synchronize changes hold rtnl lock */ + rtnl_lock(); + + /* Set the device power state as D0 */ + retval = pci_set_power_state(np->pdev, PCI_D0); + if (retval == 0) + pr_info("%s: dpm state=D%d\n", __func__, + np->pdev->current_state); + + /* Restore the PCI state */ + pci_restore_state(np->pdev); + + /* The driver specific operations + * Initialize the oak hardware + * If interface is up, then call oak net open + */ + retval = oak_init_hardware(np->pdev); + if (retval) { + pr_err("%s: oak init hardware Not Successful %d\n", __func__, + retval); + } else { + retval = netif_running(ndev); + if (retval) { + retval = oak_net_open(ndev); + if (retval) + pr_err("%s: oak net open failed\n", __func__); + } + } + + /* Release the rtnl lock */ + rtnl_unlock(); + + return retval; +} +#endif +#endif diff --git a/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_dpm.h b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_dpm.h new file mode 100644 index 00000000..4962762f --- /dev/null +++ b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_dpm.h @@ -0,0 +1,79 @@ +/* + * + * If you received this File from Marvell, you may opt to use, redistribute and/or + * modify this File in accordance with the terms and conditions of the General + * Public License Version 2, June 1991 (the "GPL License"), a copy of which is + * available along with the File in the license.txt file or by writing to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + * on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + * DISCLAIMED. The GPL License provides additional details about this warranty + * disclaimer. + * + */ + +#ifdef CONFIG_PM +#ifndef H_OAK_DPM +#define H_OAK_DPM + +#include +#include "oak_unimac.h" + +/* Name : init_hardware + * Returns : int + * Parameters : struct pci_dev *pdev + * Description : Initialize oak hardware. + */ +int oak_init_hardware(struct pci_dev *pdev); + +/* Name : release_hardware + * Returns : void + * Parameters : struct pci_dev * pdev + * Description : Release oak hardware. + */ +void oak_release_hardware(struct pci_dev *pdev); + +/* Name : oak_dpm_create_sysfs + * Returns : void + * Parameters : oak_t *np + * Description : This function creates sysfs entry for setting device power + * states D0, D1, D2 and D3. + */ +void oak_dpm_create_sysfs(oak_t *np); + +/* Name : oak_dpm_remove_sysfs + * Returns : void + * Parameters : oak_t *np + * Description : This function removes sysfs entry of device power states + */ +void oak_dpm_remove_sysfs(oak_t *np); + +#ifdef CONFIG_PM_SLEEP + +/* Name : oak_dpm_suspend + * Returns : int + * Parameters : struct device *dev + * Description : This function is called when system goes into suspend state + * It puts the device into sleep state + */ +int __maybe_unused oak_dpm_suspend(struct device *dev); + +/* Name : oak_dpm_resume + * Returns : int + * Parameters : struct device *dev + * Description : This function called when system goes into resume state and put + * the device into active state + */ +int __maybe_unused oak_dpm_resume(struct device *dev); + +/* Name : oak_dpm_set_power_state + * Returns : void + * Parameters : struct device *dev, pci_power_t state + * Description : This function set the device power state + */ +void oak_dpm_set_power_state(struct device *dev, pci_power_t state); + +#endif /* End of PM_SLEEP */ +#endif /* End of H_OAK_DPM */ +#endif /* End of PM */ diff --git a/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_ethtool.c b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_ethtool.c new file mode 100644 index 00000000..99901dd9 --- /dev/null +++ b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_ethtool.c @@ -0,0 +1,335 @@ +/* + * + * If you received this File from Marvell, you may opt to use, redistribute and/or + * modify this File in accordance with the terms and conditions of the General + * Public License Version 2, June 1991 (the "GPL License"), a copy of which is + * available along with the File in the license.txt file or by writing to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + * on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + * DISCLAIMED. The GPL License provides additional details about this warranty + * disclaimer. + * + */ +#include "oak_ethtool.h" +#include "oak_net.h" + +static const char umac_strings[][ETH_GSTRING_LEN] = { + {"rx_good_frames"}, + {"rx_bad_frames"}, + {"rx_stall_fifo"}, + {"rx_stall_desc"}, + {"rx_discard_desc"}, + {"tx_pause"}, + {"tx_stall_fifo"}, +}; + +static const u8 rx_strings[][ETH_GSTRING_LEN] = { + {"Rx Channel"}, + {"rx_alloc_pages"}, + {"rx_unmap_pages"}, + {"rx_alloc_error"}, + {"rx_frame_error"}, + {"rx_errors"}, + {"rx_interrupts"}, + {"rx_good_frames"}, + {"rx_byte_count"}, + {"rx_vlan"}, + {"rx_bad_frames"}, + {"rx_no_sof"}, + {"rx_no_eof"}, + {"rx_bad_crc"}, + {"rx_bad_csum"}, + {"rx_l4p_ok"}, + {"rx_ip4_ok"}, + {"rx_bad_nores"}, + {"rx_64"}, + {"rx_128"}, + {"rx_256"}, + {"rx_512"}, + {"rx_1024"}, + {"rx_2048"}, + {"rx_fragments"}, +}; + +static const u8 tx_strings[][ETH_GSTRING_LEN] = { + {"Tx Channel"}, + {"tx_frame_count"}, + {"tx_frame_compl"}, + {"tx_byte_count"}, + {"tx_fragm_count"}, + {"tx_drop"}, + {"tx_errors"}, + {"tx_interrupts"}, + {"tx_stall_count"}, + {"tx_64"}, + {"tx_128"}, + {"tx_256"}, + {"tx_512"}, + {"tx_1024"}, + {"tx_2048"}, +}; + +/* private function prototypes */ +static void oak_ethtool_get_txc_stats(oak_t *np, u64 **data); +static void oak_ethtool_get_rxc_stats(oak_t *np, u64 **data); +static void oak_ethtool_get_stall_stats(oak_t *np); +static void oak_ethtool_get_misc_stats(oak_t *np); + +/* Name : oak_ethtool_get_rxc_stats + * Returns : void + * Parameters : oak_t *np, u64 **data + * Description : This function copy Rx channel stats + */ +static void oak_ethtool_get_rxc_stats(oak_t *np, u64 **data) +{ + u32 i; + + **data = 0; + for (i = 0; i < np->num_rx_chan; i++) { + oak_rx_chan_t *rxc = &np->rx_channel[i]; + /* Copy rx channel statistics */ + + memcpy(*data, &rxc->stat, sizeof(oak_driver_rx_stat)); + **data = i + 1; + *data += (sizeof(oak_driver_rx_stat) / sizeof(u64)); + } +} + +/* Name : oak_ethtool_get_txc_stats + * Returns : void + * Parameters : oak_t *np, u64 **data + * Description : This function copy Tx channel stats + */ +static void oak_ethtool_get_txc_stats(oak_t *np, u64 **data) +{ + u32 i; + + **data = 0; + for (i = 0; i < np->num_tx_chan; i++) { + oak_tx_chan_t *txc = &np->tx_channel[i]; + /* Copy tx channel statistics */ + memcpy(*data, &txc->stat, sizeof(oak_driver_tx_stat)); + **data = i + 1; + *data += (sizeof(oak_driver_tx_stat) / sizeof(u64)); + } +} + +/* Name : oak_ethtool_get_stall_stats + * Returns : void + * Parameters : oak_t *np + * Description : This function get the tx or rx stall counter statistics of the + * Ethernet interface. + */ +static void oak_ethtool_get_stall_stats(oak_t *np) +{ + np->unimac_stat.tx_stall_fifo = + oak_unimac_io_read_32(np, OAK_UNI_STAT_TX_STALL_FIFO); + np->unimac_stat.rx_stall_desc = + oak_unimac_io_read_32(np, OAK_UNI_STAT_RX_STALL_DESC); + np->unimac_stat.rx_stall_fifo = + oak_unimac_io_read_32(np, OAK_UNI_STAT_RX_STALL_FIFO); +} + +/* Name : oak_ethtool_get_misc_stats + * Returns : void + * Parameters : oak_t *np + * Description : This function get the tx/rx good, bad, pause, disc descriptor + * statistics of the Ethernet interface. + */ +static void oak_ethtool_get_misc_stats(oak_t *np) +{ + np->unimac_stat.tx_pause = + oak_unimac_io_read_32(np, OAK_UNI_STAT_TX_PAUSE); + np->unimac_stat.rx_good_frames = + oak_unimac_io_read_32(np, OAK_UNI_STAT_RX_GOOD_FRAMES); + np->unimac_stat.rx_bad_frames = + oak_unimac_io_read_32(np, OAK_UNI_STAT_RX_BAD_FRAMES); + np->unimac_stat.rx_discard_desc = + oak_unimac_io_read_32(np, OAK_UNI_STAT_RX_DISC_DESC); +} + +/* Name : oak_ethtool_get_stats + * Returns : void + * Parameters : struct net_device *dev, struct ethtool_stats *stats, + * u64 *data + * Description : This function reads the statistics of the Ethernet interface. + */ +void oak_ethtool_get_stats(struct net_device *dev, + struct ethtool_stats *stats, u64 *data) +{ + oak_t *np = netdev_priv(dev); + + /* Get tx/rx channels stall and misc counters statistics */ + oak_ethtool_get_stall_stats(np); + oak_ethtool_get_misc_stats(np); + + memcpy(data, &np->unimac_stat, sizeof(np->unimac_stat)); + data += sizeof(np->unimac_stat) / sizeof(u64); + /* Get rx/tx channel statistics */ + oak_ethtool_get_rxc_stats(np, &data); + oak_ethtool_get_txc_stats(np, &data); +} + +/* Name : oak_ethtool_get_sscnt + * Returns : int + * Parameters : struct net_device *dev, int stringset + * Description : This function read the String Set Count value of the + * Ethernet interface. + */ +int oak_ethtool_get_sscnt(struct net_device *dev, int stringset) +{ + int retval; + oak_t *np = netdev_priv(dev); + + /* Get the string set count statistics */ + if (stringset == ETH_SS_STATS) { + retval = sizeof(np->unimac_stat) / sizeof(u64); + retval += (np->num_rx_chan * + sizeof(oak_driver_rx_stat) / sizeof(u64)); + retval += (np->num_tx_chan * + sizeof(oak_driver_tx_stat) / sizeof(u64)); + } else { + retval = -EINVAL; + } + + return retval; +} + +/* Name : oak_ethtool_get_strings + * Returns : void + * Parameters : struct net_device *dev, u32 stringset, u8 *data + * Description : This function get the Tx and Rx channel strings value of the + * Ethernet interface. + */ +void oak_ethtool_get_strings(struct net_device *dev, u32 stringset, + u8 *data) +{ + *data = 0; + if (stringset == ETH_SS_STATS) { + u32 off = 0; + u32 i; + oak_t *np = netdev_priv(dev); + + memcpy(&data[off], umac_strings, sizeof(umac_strings)); + off += sizeof(umac_strings); + + /* Copy statistics data into rx channel structure */ + for (i = 0; i < np->num_rx_chan; i++) { + memcpy(&data[off], rx_strings, sizeof(rx_strings)); + off += sizeof(rx_strings); + } + + /* Copy statistics data into tx channel structure */ + for (i = 0; i < np->num_tx_chan; i++) { + memcpy(&data[off], tx_strings, sizeof(tx_strings)); + off += sizeof(tx_strings); + } + } +} + +/* Name : oak_ethtool_get_cur_speed + * Returns : int + * Parameters : oak_t *np, int pspeed + * Description : This function caps the current PCIe speed for the Oak/Spruce + * switch. + */ +u32 oak_ethtool_cap_cur_speed(oak_t *np, u32 pspeed) +{ + enum pcie_link_width wdth; + + wdth = oak_net_pcie_get_width_cap(np->pdev); + if (wdth == PCIE_LNK_X1) { /* Oak */ + if (pspeed > OAK_MAX_SPEED) + pspeed = OAK_MAX_SPEED; + } else if (wdth == PCIE_LNK_X2) { + if (pspeed > SPRUCE_MAX_SPEED) + pspeed = SPRUCE_MAX_SPEED; + } + + return pspeed; +} + +/* Name : ethtool_get_link_ksettings + * Returns : int + * Parameters : struct net_device *dev, struct ethtool_link_ksettings *ecmd + * Description : This function get the current port link settings of the + * Ethernet interface. + */ +int oak_ethtool_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *ecmd) +{ + oak_t *oak; + u32 supported, advertising; + + oak = netdev_priv(dev); + + supported = 0; + advertising = 0; + + memset(ecmd, 0, sizeof(*ecmd)); + if (oak->speed == OAK_SPEED_1000) { + ecmd->base.speed = SPEED_1000; + supported |= SUPPORTED_1000baseT_Full | + SUPPORTED_1000baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_10baseT_Half; + supported |= SUPPORTED_Autoneg; + advertising |= ADVERTISED_1000baseT_Full | + ADVERTISED_1000baseT_Half | + ADVERTISED_100baseT_Full | + ADVERTISED_100baseT_Half | + ADVERTISED_10baseT_Full | + ADVERTISED_10baseT_Half; + } else if (oak->speed == OAK_SPEED_2500) { + ecmd->base.speed = SPEED_2500; + supported = SUPPORTED_10000baseT_Full; + advertising = ADVERTISED_2500baseX_Full; + } else if (oak->speed == OAK_SPEED_5000) { + ecmd->base.speed = SPEED_5000; + supported = SUPPORTED_10000baseT_Full; + advertising = ADVERTISED_10000baseT_Full; + } else { + ecmd->base.speed = SPEED_10000; + supported = SUPPORTED_10000baseT_Full; + supported |= SUPPORTED_TP; + advertising = ADVERTISED_10000baseT_Full; + } + ecmd->base.port = PORT_TP; + ecmd->base.duplex = DUPLEX_FULL; + ecmd->base.autoneg = AUTONEG_ENABLE; + + /* This function was added in kernel 4.7 in commit 6d62b4d5fac62 ("net: + * ethtool: export conversion function between u32 and link mode") + */ + ethtool_convert_legacy_u32_to_link_mode(ecmd->link_modes.supported, + supported); + ethtool_convert_legacy_u32_to_link_mode(ecmd->link_modes.advertising, + advertising); + + return 0; +} + +/* Name : oak_ethtool_get_drvinfo + * Returns : void + * Parameters : struct net_device *dev, struct ethtool_drvinfo *drvinfo + * Description : This function copy driver name, version and PCI bus + * information into ethtool driver information structure. + */ +void oak_ethtool_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *drvinfo) +{ + oak_t *adapter = netdev_priv(netdev); + + /* Copy a C-string into a sized buffer + * Copy driver name, version and bus information + */ + strscpy(drvinfo->driver, oak_driver_name, sizeof(drvinfo->driver)); + strscpy(drvinfo->version, oak_driver_version, + sizeof(drvinfo->version)); + strscpy(drvinfo->bus_info, pci_name(adapter->pdev), + sizeof(drvinfo->bus_info)); +} diff --git a/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_ethtool.h b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_ethtool.h new file mode 100644 index 00000000..99703f87 --- /dev/null +++ b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_ethtool.h @@ -0,0 +1,78 @@ +/* + * + * If you received this File from Marvell, you may opt to use, redistribute and/or + * modify this File in accordance with the terms and conditions of the General + * Public License Version 2, June 1991 (the "GPL License"), a copy of which is + * available along with the File in the license.txt file or by writing to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + * on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + * DISCLAIMED. The GPL License provides additional details about this warranty + * disclaimer. + * + */ +#ifndef H_OAK_ETHTOOL +#define H_OAK_ETHTOOL + +#include +/* Include for relation to classifier oak_unimac */ +#include "oak_unimac.h" + +/* Oak & Spruce max PCIe speed in Gbps */ +#define OAK_MAX_SPEED 5 +#define SPRUCE_MAX_SPEED 10 + +#define OAK_SPEED_1000 1 +#define OAK_SPEED_2500 2 +#define OAK_SPEED_5000 5 + +/* External variables declaration */ +extern u32 debug; +extern const char oak_driver_name[]; +extern const char oak_driver_version[]; + +/* Name : get_stats + * Returns : void + * Parameters: struct net_device * dev, struct ethtool_stats * stats, + * uint64_t * data + */ +void oak_ethtool_get_stats(struct net_device *dev, + struct ethtool_stats *stats, uint64_t *data); + +/* Name : get_sscnt + * Returns : int + * Parameters: oak_t * np + */ +int oak_ethtool_get_sscnt(struct net_device *dev, int stringset); + +/* Name : get_strings + * Returns : void + * Parameters: struct net_device *dev, u32 stringset, u8 *data + */ +void oak_ethtool_get_strings(struct net_device *dev, u32 stringset, + u8 *data); + +/* Name : cap_cur_speed + * Returns : int + * Parameters: oak_t * np, int pspeed + */ +u32 oak_ethtool_cap_cur_speed(oak_t *np, u32 pspeed); + +/* Name : get_link_ksettings + * Returns : int + * Parameters: net_device * dev, ethtool_link_ksettings * ecmd + */ +int oak_ethtool_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *ecmd); + +/* Name : oak_ethtool_get_drvinfo + * Returns : void + * Parameters : struct net_device *dev, struct ethtool_drvinfo *drvinfo + * Description : This function copy driver name, version and PCI bus + * information into ethtool driver information structure. + */ +void oak_ethtool_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *drvinfo); +#endif /* #ifndef H_OAK_ETHTOOL */ + diff --git a/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_gicu.h b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_gicu.h new file mode 100644 index 00000000..be8885be --- /dev/null +++ b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_gicu.h @@ -0,0 +1,71 @@ +/* + * + * If you received this File from Marvell, you may opt to use, redistribute and/or + * modify this File in accordance with the terms and conditions of the General + * Public License Version 2, June 1991 (the "GPL License"), a copy of which is + * available along with the File in the license.txt file or by writing to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + * on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + * DISCLAIMED. The GPL License provides additional details about this warranty + * disclaimer. + * + */ +#ifndef H_OAK_GICU +#define H_OAK_GICU + +/* Include for relation to classifier ldg_t */ +#include "ldg_t.h" +typedef struct oak_gicustruct { +#define OAK_GICU_IRQ_BASE 0x00070000U +#define OAK_GICU_IRQ_REG(o) (OAK_GICU_IRQ_BASE + (o)) +#define TX_DMA_BIT 0 +#define TX_ERR_BIT 1 +#define RX_DMA_BIT 2 +#define RX_ERR_BIT 3 +#define UNIMAC_DMA_BIT 31 +#define OAK_GICU_INTR_DBG_CTRL OAK_GICU_IRQ_REG(0x000) +#define OAK_GICU_INTR_FLAG_0 OAK_GICU_IRQ_REG(0x010) +#define OAK_GICU_INTR_FLAG_1 OAK_GICU_IRQ_REG(0x014) +#define OAK_GICU_HOST_SET_MASK_0 OAK_GICU_IRQ_REG(0x020) +#define OAK_GICU_HOST_SET_MASK_1 OAK_GICU_IRQ_REG(0x024) +#define OAK_GICU_HOST_CLR_MASK_0 OAK_GICU_IRQ_REG(0x030) +#define OAK_GICU_HOST_CLR_MASK_1 OAK_GICU_IRQ_REG(0x034) +#define OAK_GICU_HOST_MASK_0 0xFFFFFFFFU +#define OAK_GICU_HOST_MASK_1 0x000000FFU +#define OAK_GICU_HOST_MASK_E 0x003FFC00U +#define OAK_GICU_HOST_UNIMAC_P11_IRQ BIT(8) +#define OAK_GICU_HOST_UNIMAC_P11_RESET BIT(9) +#define OAK_GICU_DBG_INTR_EVNT_0 OAK_GICU_IRQ_REG(0x040) +#define OAK_GICU_DBG_INTR_EVNT_1 OAK_GICU_IRQ_REG(0x044) +#define OAK_GICU_DBG_REG_0_L OAK_GICU_IRQ_REG(0x050) +#define OAK_GICU_DBG_REG_0_H OAK_GICU_IRQ_REG(0x054) +#define OAK_GICU_DBG_REG_1_L OAK_GICU_IRQ_REG(0x060) +#define OAK_GICU_DBG_REG_1_H OAK_GICU_IRQ_REG(0x064) +#define OAK_GICU_DBG_REG_2 OAK_GICU_IRQ_REG(0x070) +#define OAK_GICU_DBG_REG_3 OAK_GICU_IRQ_REG(0x078) +#define OAK_GICU_INTR_GRP_SET_MASK OAK_GICU_IRQ_REG(0x080) +#define OAK_GICU_INTR_GRP_CLR_MASK OAK_GICU_IRQ_REG(0x084) +#define OAK_GICU_INTR_GRP_MASK_ENABLE BIT(31) +#define OAK_GICU_INTR_GRP_MASK_0 OAK_GICU_IRQ_REG(0x090) +#define OAK_GICU_INTR_GRP_MASK_1 OAK_GICU_IRQ_REG(0x094) +#define OAK_GICU_EPU_INTR_MASK_0 OAK_GICU_IRQ_REG(0x0C0) +#define OAK_GICU_EPU_INTR_MASK_1 OAK_GICU_IRQ_REG(0x0C4) +#define OAK_GICU_PIN_INTR_MASK_0 OAK_GICU_IRQ_REG(0x0d0) +#define OAK_GICU_PIN_INTR_MASK_1 OAK_GICU_IRQ_REG(0x0d4) +#define OAK_MAX_INTR_GRP 64 +#define OAK_MAX_CHAN_NUM 10 +#define OAK_GICU_INTR_GRP(g) OAK_GICU_IRQ_REG(0x100 + 4 * (g)) +#define OAK_INTR_MASK_TX_DMA BIT(0) +#define OAK_INTR_MASK_TX_ERR BIT(1) +#define OAK_INTR_MASK_RX_DMA BIT(2) +#define OAK_INTR_MASK_RX_ERR BIT(3) +#define OAK_NUM_IVEC (OAK_MAX_CHAN_NUM * 4 + 1) + struct msix_entry msi_vec[OAK_NUM_IVEC]; + u32 num_ldg; + ldg_t ldg[OAK_NUM_IVEC]; +} oak_gicu; + +#endif /* #ifndef H_OAK_GICU */ + diff --git a/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_ioc_flow.h b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_ioc_flow.h new file mode 100644 index 00000000..bd8a35f7 --- /dev/null +++ b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_ioc_flow.h @@ -0,0 +1,39 @@ +/* + * + * If you received this File from Marvell, you may opt to use, redistribute and/or + * modify this File in accordance with the terms and conditions of the General + * Public License Version 2, June 1991 (the "GPL License"), a copy of which is + * available along with the File in the license.txt file or by writing to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + * on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + * DISCLAIMED. The GPL License provides additional details about this warranty + * disclaimer. + * + */ +#ifndef H_OAK_IOC_FLOW +#define H_OAK_IOC_FLOW + +typedef struct oak_ioc_flowstruct { +#define OAK_IOCTL_RXFLOW (SIOCDEVPRIVATE + 8) +#define OAK_IOCTL_RXFLOW_CLEAR 0 +#define OAK_IOCTL_RXFLOW_MGMT BIT(0) +#define OAK_IOCTL_RXFLOW_QPRI BIT(3) +#define OAK_IOCTL_RXFLOW_SPID BIT(7) +#define OAK_IOCTL_RXFLOW_FLOW BIT(12) +#define OAK_IOCTL_RXFLOW_DA BIT(18) +#define OAK_IOCTL_RXFLOW_ET BIT(19) +#define OAK_IOCTL_RXFLOW_FID BIT(20) +#define OAK_IOCTL_RXFLOW_DA_MASK BIT(21) + __u32 cmd; + __u32 idx; + u32 val_lo; + u32 val_hi; + char data[16]; + int ena; + int error; +} oak_ioc_flow; + +#endif /* #ifndef H_OAK_IOC_FLOW */ + diff --git a/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_ioc_lgen.h b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_ioc_lgen.h new file mode 100644 index 00000000..8a8b2ea8 --- /dev/null +++ b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_ioc_lgen.h @@ -0,0 +1,39 @@ +/* + * + * If you received this File from Marvell, you may opt to use, redistribute and/or + * modify this File in accordance with the terms and conditions of the General + * Public License Version 2, June 1991 (the "GPL License"), a copy of which is + * available along with the File in the license.txt file or by writing to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + * on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + * DISCLAIMED. The GPL License provides additional details about this warranty + * disclaimer. + * + */ +#ifndef H_OAK_IOC_LGEN +#define H_OAK_IOC_LGEN + +typedef struct oak_ioc_lgenstruct { +#define OAK_IOCTL_LGEN (SIOCDEVPRIVATE + 3) +#define OAK_IOCTL_LGEN_INIT BIT(0) +#define OAK_IOCTL_LGEN_TX_DATA BIT(1) +#define OAK_IOCTL_LGEN_TX_START BIT(2) +#define OAK_IOCTL_LGEN_TX_STOP BIT(3) +#define OAK_IOCTL_LGEN_RX_START BIT(4) +#define OAK_IOCTL_LGEN_RX_STOP BIT(5) +#define OAK_IOCTL_LGEN_RX_DATA BIT(6) +#define OAK_IOCTL_LGEN_RELEASE BIT(7) +#define OAK_IOCTL_LGEN_TX_RESET BIT(8) +#define OAK_IOCTL_LGEN_RX_RESET BIT(9) + __u32 cmd; + __u32 offs; + __u32 len; + __u32 channel; + __u32 count; + char data[32]; + int error; +} oak_ioc_lgen; + +#endif /* #ifndef H_OAK_IOC_LGEN */ diff --git a/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_ioc_reg.h b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_ioc_reg.h new file mode 100644 index 00000000..831c773b --- /dev/null +++ b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_ioc_reg.h @@ -0,0 +1,36 @@ +/* + * + * If you received this File from Marvell, you may opt to use, redistribute and/or + * modify this File in accordance with the terms and conditions of the General + * Public License Version 2, June 1991 (the "GPL License"), a copy of which is + * available along with the File in the license.txt file or by writing to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + * on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + * DISCLAIMED. The GPL License provides additional details about this warranty + * disclaimer. + * + */ +#ifndef H_OAK_IOC_REG +#define H_OAK_IOC_REG + +typedef struct oak_ioc_regstruct { +#define OAK_IOCTL_REG_ESU_REQ (SIOCDEVPRIVATE + 1) +#define OAK_IOCTL_REG_MAC_REQ (SIOCDEVPRIVATE + 2) +#define OAK_IOCTL_REG_AHSI_REQ (SIOCDEVPRIVATE + 3) +#define OAK_IOCTL_REG_RD 1 +#define OAK_IOCTL_REG_WR 2 +#define OAK_IOCTL_REG_WS 3 +#define OAK_IOCTL_REG_WC 4 +#define OAK_IOCTL_REG_OR 5 +#define OAK_IOCTL_REG_AND 6 + __u32 cmd; + __u32 offs; + __u32 dev_no; + __u32 data; + __u32 device; + int error; +} oak_ioc_reg; + +#endif /* #ifndef H_OAK_IOC_REG */ diff --git a/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_ioc_set.h b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_ioc_set.h new file mode 100644 index 00000000..cfb21d93 --- /dev/null +++ b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_ioc_set.h @@ -0,0 +1,28 @@ +/* + * + * If you received this File from Marvell, you may opt to use, redistribute and/or + * modify this File in accordance with the terms and conditions of the General + * Public License Version 2, June 1991 (the "GPL License"), a copy of which is + * available along with the File in the license.txt file or by writing to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + * on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + * DISCLAIMED. The GPL License provides additional details about this warranty + * disclaimer. + * + */ +#ifndef H_OAK_IOC_SET +#define H_OAK_IOC_SET + +typedef struct oak_ioc_setstruct { +#define OAK_IOCTL_SET_MAC_RATE_A (SIOCDEVPRIVATE + 5) +#define OAK_IOCTL_SET_MAC_RATE_B (SIOCDEVPRIVATE + 6) +#define OAK_IOCTL_SET_TXR_RATE (SIOCDEVPRIVATE + 7) + __u32 cmd; + __u32 idx; + __u32 data; + int error; +} oak_ioc_set; + +#endif /* #ifndef H_OAK_IOC_SET */ diff --git a/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_ioc_stat.h b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_ioc_stat.h new file mode 100644 index 00000000..c129b29c --- /dev/null +++ b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_ioc_stat.h @@ -0,0 +1,33 @@ +/* + * + * If you received this File from Marvell, you may opt to use, redistribute and/or + * modify this File in accordance with the terms and conditions of the General + * Public License Version 2, June 1991 (the "GPL License"), a copy of which is + * available along with the File in the license.txt file or by writing to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + * on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + * DISCLAIMED. The GPL License provides additional details about this warranty + * disclaimer. + * + */ +#ifndef H_OAK_IOC_STAT +#define H_OAK_IOC_STAT + +typedef struct oak_ioc_statstruct { +#define OAK_IOCTL_STAT (SIOCDEVPRIVATE + 4) +#define OAK_IOCTL_STAT_GET_TXS BIT(0) +#define OAK_IOCTL_STAT_GET_RXS BIT(1) +#define OAK_IOCTL_STAT_GET_TXC BIT(2) +#define OAK_IOCTL_STAT_GET_RXC BIT(3) +#define OAK_IOCTL_STAT_GET_RXB BIT(4) +#define OAK_IOCTL_STAT_GET_LDG BIT(5) + __u32 cmd; + __u32 idx; + __u32 offs; + char data[128]; + int error; +} oak_ioc_stat; + +#endif /* #ifndef H_OAK_IOC_STAT */ diff --git a/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_irq.c b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_irq.c new file mode 100644 index 00000000..b1a2fa65 --- /dev/null +++ b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_irq.c @@ -0,0 +1,498 @@ +/* + * + * If you received this File from Marvell, you may opt to use, redistribute and/or + * modify this File in accordance with the terms and conditions of the General + * Public License Version 2, June 1991 (the "GPL License"), a copy of which is + * available along with the File in the license.txt file or by writing to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + * on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + * DISCLAIMED. The GPL License provides additional details about this warranty + * disclaimer. + * + */ +#include "oak_irq.h" + +struct oak_tstruct; + +/* Name : oak_irq_dis_gicu + * Returns : void + * Parameters : struct oak_tstruct *np, u32 mask_0, u32 mask_1 + * Description : This function set GICU mask. + */ +static void oak_irq_dis_gicu(struct oak_tstruct *np, u32 mask_0, u32 mask_1) +{ + oak_unimac_io_write_32(np, OAK_GICU_HOST_SET_MASK_0, mask_0); + oak_unimac_io_write_32(np, OAK_GICU_HOST_SET_MASK_1, mask_1); +} + +/* Name : oak_irq_ena_gicu + * Returns : void + * Parameters : struct oak_tstruct *np, u32 mask_0, u32 mask_1 + * Description : This function clear GICU mask. + */ +static void oak_irq_ena_gicu(struct oak_tstruct *np, u32 mask_0, u32 mask_1) +{ + oak_unimac_io_write_32(np, OAK_GICU_HOST_CLR_MASK_0, mask_0); + oak_unimac_io_write_32(np, OAK_GICU_HOST_CLR_MASK_1, mask_1); +} + +/* Name : oak_irq_reset_gicu_ldg + * Returns : void + * Parameters : struct oak_tstruct *np + * Description : This function resets Generic Interrupt Controller (gicu) + * Logical devices and device groups (ldg) + */ +static void oak_irq_reset_gicu_ldg(struct oak_tstruct *np) +{ + u32 i = 0; + + /* Reset GICU logical device group structure members */ + while (i < np->gicu.num_ldg) { + np->gicu.ldg[i].device = np; + np->gicu.ldg[i].msi_grp = i; + np->gicu.ldg[i].msi_tx = 0; + np->gicu.ldg[i].msi_rx = 0; + np->gicu.ldg[i].msi_te = 0; + np->gicu.ldg[i].msi_re = 0; + np->gicu.ldg[i].msi_ge = 0; + np->gicu.ldg[i].msiname[0] = '\0'; + ++i; + } +} + +/* Name : oak_irq_set_tx_rx_dma_bit + * Returns : int + * Parameters : struct oak_tstruct *np, int grp + * Description : This function sets the tx and rx bit of a dma channel + */ +static u32 oak_irq_set_tx_rx_dma_bit(struct oak_tstruct *np, u32 grp) +{ + u32 i = 0; + u64 val; + + val = (1UL << TX_DMA_BIT); + /* Set tx DMA bit for all the tx channels */ + while (i < np->num_tx_chan) { + if (np->gicu.num_ldg > 0) + grp = (grp % np->gicu.num_ldg); + np->gicu.ldg[grp].msi_tx |= val; + val <<= 4ULL; + ++grp; + ++i; + } + + i = 0; + val = (1UL << RX_DMA_BIT); + /* Set rx DMA bit for all the rx channels */ + while (i < np->num_rx_chan) { + if (np->gicu.num_ldg > 0) + grp = (grp % np->gicu.num_ldg); + np->gicu.ldg[grp].msi_rx |= val; + val <<= 4UL; + ++grp; + ++i; + } + return grp; +} + +/* Name : oak_irq_set_tx_rx_err_bit + * Returns : void + * Parameters : struct oak_tstruct *np, int grp + * Description : This function sets the tx and rx err bit of a dma channel + */ +static void oak_irq_set_tx_rx_err_bit(struct oak_tstruct *np, u32 grp) +{ + u32 i = 0; + u64 val; + + val = (1UL << TX_ERR_BIT); + /* Set tx error bit for all the tx channels */ + while (i < np->num_tx_chan) { + if (np->gicu.num_ldg > 0) + grp = (grp % np->gicu.num_ldg); + np->gicu.ldg[grp].msi_te |= val; + val <<= 4ULL; + ++grp; + ++i; + } + + i = 0; + val = (1UL << RX_ERR_BIT); + /* Set rx error bit for all the rx channels */ + while (i < np->num_rx_chan) { + if (np->gicu.num_ldg > 0) + grp = (grp % np->gicu.num_ldg); + np->gicu.ldg[grp].msi_re |= val; + val <<= 4ULL; + ++grp; + ++i; + } +} + +/* Name : oak_irq_callback + * Returns : irqreturn_t + * Parameters : int irq, void *cookie + * Description : This function set GICU IRQ mask and schedule NAPI + */ +static irqreturn_t oak_irq_callback(int irq, void *cookie) +{ + ldg_t *ldg = (ldg_t *)cookie; + irqreturn_t rc_4 = IRQ_HANDLED; + + oak_unimac_io_write_32(ldg->device, OAK_GICU_INTR_GRP_SET_MASK, + ldg->msi_grp | + (u32)OAK_GICU_INTR_GRP_MASK_ENABLE); +#ifdef DEBUG + { + u32 mask_0; + u32 mask_1; + + mask_0 = oak_unimac_io_read_32(ldg->device, + OAK_GICU_INTR_FLAG_0); + mask_1 = oak_unimac_io_read_32(ldg->device, + OAK_GICU_INTR_FLAG_1); + oakdbg(debug, INTR, "IRQ GRP %d [flag0=0x%0x flag1=0x%0x]", + ldg->msi_grp, mask_0, mask_1); + } +#endif + /* Schedule NAPI poll */ + napi_schedule(&ldg->napi); + oakdbg(debug, INTR, "============ IRQ GRP END ============"); + return rc_4; +} + +/* Name : oak_request_irq + * Returns : int + * Parameters : struct oak_tstruct *np, ldg_t *ldg, + * const char *str, u32 idx, int cpu + * Description : This function registers the PCIe irq + */ +static int oak_request_irq(struct oak_tstruct *np, ldg_t *ldg, + const char *str, u32 idx, u32 cpu) +{ + int retval = 0; + + snprintf(np->gicu.ldg[idx].msiname, + sizeof(np->gicu.ldg[idx].msiname) - 1, + "%s-%s-%d", np->pdev->driver->name, str, idx); + +#ifdef OAK_MSIX_LEGACY + /* In older version of the kernel pci_irq_vector() is not supported + */ + retval = request_irq(np->gicu.msi_vec[idx].vector, oak_irq_callback, 0, + np->gicu.ldg[idx].msiname, &np->gicu.ldg[idx]); + if (retval == 0) + retval = irq_set_affinity_hint(np->gicu.msi_vec[idx].vector, + get_cpu_mask(cpu)); +#else + /* To get the Linux IRQ numbers passed to request_irq(), Call the + * function pci_irq_vector() because most of the hard work is done for + * the driver in the PCI layer. The driver simply has to request that + * the PCI layer set up the MSI capability for Oak device. + */ + retval = request_irq(pci_irq_vector(np->pdev, idx), oak_irq_callback, 0, + np->gicu.ldg[idx].msiname, &np->gicu.ldg[idx]); + if (retval == 0) + /* Deprecated. Use irq_update_affinity_hint() or + * irq_set_affinity_and_hint() instead irq_set_affinity_hint + * as menioned in include/linux/interrupt.h + */ + retval = irq_set_affinity_hint(pci_irq_vector(np->pdev, idx), + get_cpu_mask(cpu)); +#endif + oakdbg(debug, INTR, + "np=%p ivec[%2d]=%2d tx=0x%8llx rx=0x%8llx te=0x%8llx re=0x%8llx ge=%8llx type=%s err=%d", + np, np->gicu.ldg[idx].msi_grp, np->gicu.msi_vec[idx].vector, + ldg->msi_tx, ldg->msi_rx, ldg->msi_te, ldg->msi_re, + ldg->msi_ge, str, retval); + + return retval; +} + +/* Name : oak_request_single_ivec + * Returns : int + * Parameters : struct oak_tstruct *np, ldg_t *ldg, u64 val, u32 idx, + * int cpu + * Description : This function check and set the ldg msix value, then call the + * request irq oak driver function. + */ +static int oak_irq_request_single_ivec(struct oak_tstruct *np, ldg_t *ldg, + u64 val, u32 idx, u32 cpu) +{ + const char *str = "xx"; + int retval; + + /* Set ldg MSI structure members tx, rx, te, re, ge */ + if (val == ldg->msi_tx) + str = "tx"; + if (val == ldg->msi_rx) + str = "rx"; + if (val == ldg->msi_te) + str = "te"; + if (val == ldg->msi_re) + str = "re"; + if (val == ldg->msi_ge) + str = "ge"; + + retval = oak_request_irq(np, ldg, str, idx, cpu); + + return retval; +} + +/* Name : oak_irq_allocate_ivec + * Returns : int + * Parameters : struct oak_tstruct *np + * Description : This function allocate ivec + */ +static int oak_irq_allocate_ivec(struct oak_tstruct *np) +{ + u32 i = 0; + u32 cpu; + int err = 0; + + cpu = cpumask_first(cpu_online_mask); + /* In a loop we need to check for all the available online CPU and then + * request and map IRQ line from linux kernel. The function call + * oak_irq_request_single_ivec finally endup calling request_irq and + * irq_set_affinity_hint linux kernel functions. + */ + while ((i < np->gicu.num_ldg) && (err == 0)) { + u64 val; + ldg_t *p = &np->gicu.ldg[i]; + + val = (p->msi_tx | p->msi_rx | p->msi_te | p->msi_re + | p->msi_ge); + if (val != 0) { + err = oak_irq_request_single_ivec(np, p, val, i, cpu); + cpu = cpumask_next(cpu, cpu_online_mask); + if (cpu >= nr_cpu_ids) + cpu = cpumask_first(cpu_online_mask); + if (err != 0) { + /* Reset ldg MSI structure members */ + p->msi_tx = 0; + p->msi_rx = 0; + p->msi_te = 0; + p->msi_re = 0; + p->msi_ge = 0; + } + } + ++i; + } + return err; +} + +/* Name : oak_irq_request_ivec + * Returns : int + * Parameters : struct oak_tstruct *np + * Description : This function request irq vector + */ +int oak_irq_request_ivec(struct oak_tstruct *np) +{ + u32 grp = 0; + u32 num_chan_req; + int err = 0; + + oak_irq_dis_gicu(np, OAK_GICU_HOST_MASK_0, + OAK_GICU_HOST_MASK_1 | OAK_GICU_HOST_MASK_E); + + if (np->num_rx_chan < np->num_tx_chan) + num_chan_req = np->num_tx_chan; + else + num_chan_req = np->num_rx_chan; + + if (num_chan_req <= MAX_NUM_OF_CHANNELS) { + oak_irq_reset_gicu_ldg(np); + grp = oak_irq_set_tx_rx_dma_bit(np, grp); + oak_irq_set_tx_rx_err_bit(np, grp); + } else { + err = -ENOMEM; + } + + if (err == 0) + err = oak_irq_allocate_ivec(np); + if (err != 0) + oak_irq_release_ivec(np); + + oakdbg(debug, INTR, "np=%p num_ldg=%d num_chan_req=%d err=%d", np, + np->gicu.num_ldg, num_chan_req, err); + + return err; +} + +/* Name : oak_irq_release_ivec + * Returns : void + * Parameters : struct oak_tstruct *np + * Description : This function reset the MSI interrupt vector structure. + */ +void oak_irq_release_ivec(struct oak_tstruct *np) +{ + u32 i = 0; + + while (i < np->gicu.num_ldg) { + ldg_t *p = &np->gicu.ldg[i]; + + if (((p->msi_tx | p->msi_rx | p->msi_te | p->msi_re | + p->msi_ge) != 0)) { +#ifdef OAK_MSIX_LEGACY + /* legacy kernel will not support pci_irq_vector() */ + synchronize_irq(np->gicu.msi_vec[i].vector); + irq_set_affinity_hint(np->gicu.msi_vec[i].vector, NULL); + free_irq(np->gicu.msi_vec[i].vector, &np->gicu.ldg[i]); +#else + /* Wait for pending IRQ handlers (on other CPUs) - + * This function waits for any pending IRQ handlers for + * this interrupt to complete before returning + */ + synchronize_irq(pci_irq_vector(np->pdev, i)); + irq_set_affinity_hint(pci_irq_vector(np->pdev, i), + NULL); + /* Free an interrupt allocated with request_irq */ + free_irq(pci_irq_vector(np->pdev, i), &np->gicu.ldg[i]); +#endif + p->msi_tx = 0; + p->msi_rx = 0; + p->msi_te = 0; + p->msi_re = 0; + p->msi_ge = 0; + } + ++i; + } +} + +/* Name : oak_irq_enable_gicu_64 + * Returns : void + * Parameters : struct oak_tstruct *np, u64 mask + * Description : This function set the 64bit GICU mask. + */ +void oak_irq_enable_gicu_64(struct oak_tstruct *np, u64 mask) +{ + u32 val_0 = (u32)(mask & OAK_GICU_HOST_MASK_0); + u32 val_1 = (u32)((mask >> 32) & OAK_GICU_HOST_MASK_1); + + oakdbg(debug, INTR, "Enable IRQ mask %016llx", mask); + + oak_irq_ena_gicu(np, val_0, val_1); +} + +/* Name : oak_irq_ena_general + * Returns : void + * Parameters : struct oak_tstruct *np, u32 enable + * Description : This function set the mask which serve general errors + */ +void oak_irq_ena_general(struct oak_tstruct *np, u32 enable) +{ + if (enable != 0) + enable = (u32)(OAK_UNI_INTR_SEVERE_ERRORS); + oak_unimac_io_write_32(np, OAK_UNI_IMSK, enable); +} + +/* Name : oak_unimac_enable_tx_rx_channel_irq + * Returns : void + * Parameters : struct oak_tstruct *np + * Description : This function enables tx and rx channel ring irqs + */ +static void oak_unimac_enable_tx_rx_channel_irq(struct oak_tstruct *np) +{ + u32 i; + + oak_irq_ena_gicu(np, 0, OAK_GICU_HOST_UNIMAC_P11_IRQ); + i = 0; + + /* Enable IRQ for all the tx channels */ + while (i < np->num_tx_chan) { + oak_unimac_ena_tx_ring_irq(np, i, 1); + ++i; + } + + i = 0; + + /* Enable IRQ for all the rx channels */ + while (i < np->num_rx_chan) { + oak_unimac_ena_rx_ring_irq(np, i, 1); + ++i; + } +} + +/* Name : oak_irq_enable_groups + * Returns : int + * Parameters : struct oak_tstruct *np + * Description : This function enables the group irqs + */ +int oak_irq_enable_groups(struct oak_tstruct *np) +{ + u32 grp = 0; + u32 irq; + u64 irq_val; + int retval = 0; + + /* Map irq bit and enable the group irqs for all the logical device + * groups in a loop. Then enable the IRQ on both tx/rx channel. + */ + while (grp < np->gicu.num_ldg) { + ldg_t *p = &np->gicu.ldg[grp]; + + p->irq_mask = (p->msi_tx | p->msi_rx | p->msi_te | p->msi_re | + p->msi_ge); + p->irq_first = 0; + p->irq_count = 0; + irq_val = 1; + irq = 0; + + /* The below while loop does following + * -Map IRQ bit for each group + * -Enable the IRQ line + */ + while (irq < OAK_MAX_INTR_GRP) { + if (p->irq_mask & irq_val) { + if (p->irq_count == 0) + p->irq_first = irq; + ++p->irq_count; + + oak_unimac_io_write_32(np, + OAK_GICU_INTR_GRP(irq), + grp); + oakdbg(debug, INTR, + "Map IRQ bit %02d => group #%02d (1st=%2d of %2d)", + irq, grp, p->irq_first, p->irq_count); + } + irq_val <<= 1ULL; + ++irq; + } + oak_irq_enable_gicu_64(np, p->irq_mask); + ++grp; + } + + oak_unimac_enable_tx_rx_channel_irq(np); + + return retval; +} + +/* Name : oak_irq_disable_groups + * Returns : void + * Parameters : struct oak_tstruct *np + * Description : This function disbles the group irq. + */ +void oak_irq_disable_groups(struct oak_tstruct *np) +{ + u32 i; + + oak_irq_dis_gicu(np, OAK_GICU_HOST_MASK_0, OAK_GICU_HOST_MASK_1); + i = 0; + + /* Disable IRQ for all rx channels */ + while (i < np->num_rx_chan) { + oak_unimac_ena_rx_ring_irq(np, i, 0); + ++i; + } + + i = 0; + + /* Disable IRQ for all tx channels */ + while (i < np->num_tx_chan) { + oak_unimac_ena_tx_ring_irq(np, i, 0); + ++i; + } +} + diff --git a/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_irq.h b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_irq.h new file mode 100644 index 00000000..8e92a5c1 --- /dev/null +++ b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_irq.h @@ -0,0 +1,66 @@ +/* + * + * If you received this File from Marvell, you may opt to use, redistribute and/or + * modify this File in accordance with the terms and conditions of the General + * Public License Version 2, June 1991 (the "GPL License"), a copy of which is + * available along with the File in the license.txt file or by writing to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + * on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + * DISCLAIMED. The GPL License provides additional details about this warranty + * disclaimer. + * + */ +#ifndef H_OAK_IRQ +#define H_OAK_IRQ + +/* Include for relation to classifier oak_unimac */ +#include "oak_unimac.h" + +extern u32 debug; +struct oak_tstruct; +/* Name : oak_irq_request_ivec + * Returns : int + * Parameters : struct oak_tstruct * np + * Description : This function request irq vector + */ +int oak_irq_request_ivec(struct oak_tstruct *np); + +/* Name : oak_irq_release_ivec + * Returns : void + * Parameters : struct oak_tstruct *np + * Description : This function reset the MSI interrupt vector structure. + */ +void oak_irq_release_ivec(struct oak_tstruct *np); + +/* Name : oak_irq_enable_gicu_64 + * Returns : void + * Parameters : struct oak_tstruct *np, u64 mask + * Description : This function set the 64bit GICU mask. + */ +void oak_irq_enable_gicu_64(struct oak_tstruct *np, u64 mask); + +/* Name : oak_irq_ena_general + * Returns : void + * Parameters : struct oak_tstruct *np, u32 enable + * Description : This function set the mask which serve general errors + */ +void oak_irq_ena_general(struct oak_tstruct *np, u32 enable); + +/* Name : enable_groups + * Returns : int + * Parameters: struct oak_tstruct *np + * Description : This function enable the group irq + */ +int oak_irq_enable_groups(struct oak_tstruct *np); + +/* Name : oak_irq_disable_groups + * Returns : void + * Parameters : struct oak_tstruct *np + * Description : This function disbles the group irq. + */ +void oak_irq_disable_groups(struct oak_tstruct *np); + +#endif /* #ifndef H_OAK_IRQ */ + diff --git a/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_module.h b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_module.h new file mode 100644 index 00000000..54d91a47 --- /dev/null +++ b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_module.h @@ -0,0 +1,49 @@ +/* + * + * If you received this File from Marvell, you may opt to use, redistribute and/or + * modify this File in accordance with the terms and conditions of the General + * Public License Version 2, June 1991 (the "GPL License"), a copy of which is + * available along with the File in the license.txt file or by writing to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + * on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + * DISCLAIMED. The GPL License provides additional details about this warranty + * disclaimer. + * + */ + +int oak_init_module(void); +void oak_exit_module(void); + +module_init(oak_init_module); +module_exit(oak_exit_module); + +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "OAK debug level"); + +/* RX and TX ring sizes are given as a power of 2 e.g: + * x=[0-7] :: ring-size=2^(4+x), where x is the specified load parameter. + */ +module_param(rxs, int, 0); +MODULE_PARM_DESC(rxs, "Receive ring size"); + +module_param(txs, int, 0); +MODULE_PARM_DESC(txs, "Transmit ring size"); + +module_param(chan, int, 0); +MODULE_PARM_DESC(chan, "Number of (tx/rx) channels"); + +module_param(rto, int, 0); +MODULE_PARM_DESC(rto, "Receive descriptor timeout in usec"); + +module_param(mhdr, int, 0); +MODULE_PARM_DESC(mhdr, "Marvell header generation"); + +module_param(port_speed, int, 0); +MODULE_PARM_DESC(mhdr, "Unimac 11 Port speed"); + +module_param(napi_wt, int, 0); +MODULE_PARM_DESC(napi_wt, "NAPI Poll weight/budget"); + +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_net.c b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_net.c new file mode 100644 index 00000000..abc98aba --- /dev/null +++ b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_net.c @@ -0,0 +1,1991 @@ +/* + * + * If you received this File from Marvell, you may opt to use, redistribute and/or + * modify this File in accordance with the terms and conditions of the General + * Public License Version 2, June 1991 (the "GPL License"), a copy of which is + * available along with the File in the license.txt file or by writing to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + * on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + * DISCLAIMED. The GPL License provides additional details about this warranty + * disclaimer. + * + */ +#include "oak_net.h" +#include "oak_ethtool.h" +#include "oak_chksum.h" + +/* Try module */ +#define TRY_MODULE 40 +/* Allocate or free memory */ +#define MEM_ALLOC_DONE 41 +/* Request or release IRQ */ +#define IRQ_REQUEST_DONE 42 +/* Enable or disable group */ +#define IRQ_GRP_ENABLE_DONE 43 +/* Setup is completed */ +#define SETUP_DONE 44 + +/* private function prototypes */ +static void oak_net_esu_ena_mrvl_hdr(oak_t *np); +static struct sk_buff *oak_net_tx_prepend_mrvl_hdr(struct sk_buff *skb); +static netdev_tx_t oak_net_tx_packet(oak_t *np, struct sk_buff *skb, u16 txq); +static u32 oak_net_tx_work(ldg_t *ldg, u32 ring, int budget); +static u32 oak_net_rx_work(ldg_t *ldg, u32 ring, int budget); +static int oak_net_process_rx_pkt(oak_rx_chan_t *rxc, u32 desc_num, + struct sk_buff **target); +static u32 oak_net_process_channel(ldg_t *ldg, u32 ring, u32 reason, + int budget); +static int oak_net_poll(struct napi_struct *napi, int budget); +static int oak_net_poll_core(oak_t *np, ldg_t *ldg, int budget); +static netdev_tx_t oak_net_stop_tx_queue(oak_t *np, u32 nfrags, u16 txq); + +/* Name : esu_ena_mrvl_hdr + * Returns : void + * Parameters: oak_t * np = np + * Description : This function enables Marvell header in the Ethernet frame + */ +static void oak_net_esu_ena_mrvl_hdr(oak_t *np) +{ + u32 offs = 0x10000U | (4U << 2) | (0xBU << 7); + u32 data = 0x007f; + + /* Check if Marvell header is enabled */ + if (mhdr != 0) + data |= 0x0800U; + oakdbg(debug, PROBE, "PCI class revision: 0x%x\n", + np->pci_class_revision); + /* sw32 is a macro defined in oak_unimac.h file which will be expand + * as writel. The writel is a linux kernel function used to write into + * directly mapped IO memory. + */ + sw32(np, offs, data); + + if (mhdr != 0 && np->pci_class_revision >= 1) { + oakdbg(debug, PROBE, "No MRVL header generation in SW"); + + mhdr = 0; + } +} + +/* Name : esu_set_mtu + * Returns : int + * Parameters: struct net_device * net_dev = net_dev, int new_mtu = new_mtu + * Description: This function sets the MTU size of the Ethernet interface. + */ +int oak_net_esu_set_mtu(struct net_device *net_dev, int new_mtu) +{ + oak_t *np = netdev_priv(net_dev); + u32 offs = 0x10000U | (8U << 2) | (0xBU << 7); + u32 fs = new_mtu + (ETH_HLEN + ETH_FCS_LEN); + u32 data; + int retval = 0; + + /* sr32 is a macro defined in oak_unimac.h file which will be expand + * as readl. The readl is a linux kernel function used to read from + * directly mapped IO memory. + */ + data = sr32(np, offs); + data &= ~(3U << 12); + + /* fs - Frame Size is calculated and initialized new_mtu is provided as + * user input, the range is between 1500 to 9000 + * ETH_HLEN is 14 Total octets in header + * ETH_FCS_LEN is 4 Octets in the FCS + * The fs value is calculated and used in condition, then the data bit + * is set and written into register using sw32 function. + */ + if (fs <= 10240) { + if (fs > 1522) { + if (fs <= 2048) + data |= BIT(12); + else + data |= (2U << 12); + + } else { + } + oakdbg(debug, PROBE, "MTU %d/%d data=0x%x", new_mtu, fs, data); + net_dev->mtu = new_mtu; + sw32(np, offs, data); + } else { + retval = -EINVAL; + } + + return retval; +} + +/* Name : esu_ena_speed + * Returns : void + * Parameters: int gbit = gbit, oak_t * np = np + * Description: This function sets speed in the ESU + */ +void oak_net_esu_ena_speed(u32 gbit, oak_t *np) +{ + u32 data; + u32 offs = 0x10000U | (1U << 2) | (0xBU << 7); + + /* Capture the current PCIe speed of Oak/Spruce */ + gbit = oak_ethtool_cap_cur_speed(np, gbit); + np->speed = gbit; + pr_info("oak: device=0x%x speed=%dGbps\n", np->pdev->device, gbit); + + /* We capture PCIe speed in gbit, then by referring to Oak register + * specification Interface Configuration Matrix C_Mode, Speed, and + * AltSpdValue + * C_Mode is R/W for Ports 9 & 10 only so the interface can be + * configured from its default setting (via the device’s configuration + * pins). + * + * 0x10 - Speed [3] + * 0x20 - MII MAC. speed is Link Partner [6] + * 0x30 - Internal Speed is Internal [7] + * + * [3] - C_Modes 0x0, 0x1, 0x4 and 0x5 on Port 8 (88Q5072) default to + * a Speed of 100 but they can be forced to 10 Mbit operation + * (see Port offset 0x01). For C_Mode 0x1 and C_Mode 0x0 when in PHY + * mode, the Clock Mode's frequency will also change accordingly. + * [6] - The Link Partner’s Input clocks determine the actual speed of + * the MII. The Speed bits (see Port offset 0x00) will not reflect the + * actual speed of the port unless software updates the ForceSpd bits + * (Port offset 0x01). + * [7] - Port 0 internal port speed is fixed at 1Gbps, Port 11 + * internal port maximum speed is 5Gbps for 88Q5072 . Minimum speed + * is 1Gbps. + * + * Now, by referring to Port Status Register in Oak Specification + * + * BIT(13): Alternate Speed Value + * This bit is valid when the Link bit, below, is set to a one. + * This bit along with the Speed bits below, indicates the current + * speed of the link as follows: + * 0x0 = 10 Mbps, 100 Mbps, 1000 Mbps or 10 Gbps + * 0x1 = 200 Mbps, 2500 Mbps or 5 Gbps + * + * BIT[9:8]: Speed mode. + * These bits are valid when the Link bit, above, is set to a one. + * These bits along with the AltSpdValue bit above, indicates the + * current speed of the link + * as follows: + * 0x0 = 10 Mbps + * 0x1 = 100 or 200 Mbps + * 0x2 = 1000 or 2500 Mbps + * 0x3 = 5 Gig + * The port’s Speed & AltSpdValue bits comes from the SpdValue & + * AltSpeed bits if the speed is being forced by ForcedSpd bit being a + * one (Port offset 0x01 – assuming the port can support the selected + * speed – else the port’s highest speed is reported). Otherwise the + * port’s Speed bits come from the source defined in Table 8 on page + * 57. The alternate speed of some of the Speed values is selected if + * the port’s AltSpeed is set to a one (Port offset 0x01). + * NOTE: These bits will reflect the actual speed of Ports only when an + * external PHY is attached to the port (i.e., when the SERDES + * interface is in SGMII or when the xMII interface is connected to an + * external PHY). Otherwise these bits will reflect the C_Mode speed + * of the port (as best it can) if the speed is not being forced. + */ + if (gbit == 10) { + data = 0x201f; + } else { + if (gbit == 5) + data = 0x301f; + else + data = 0x1013; + } + sw32(np, offs, data); + msleep(20); + + if (gbit == 10) { + data = 0x203f; + } else { + if (gbit == 5) + data = 0x303f; + else + data = 0x1033; + } + sw32(np, offs, data); + msleep(20); + + oakdbg(debug, PROBE, "Unimac %d Gbit speed enabled", + gbit == 1 ? 1 : 10); +} + +/* Name : tx_prepend_mrvl_hdr + * Returns : struct sk_buff * + * Parameters: struct sk_buff * skb = skb + * Description: This function adds marvell header in the skb + */ +static struct sk_buff *oak_net_tx_prepend_mrvl_hdr(struct sk_buff *skb) +{ + struct sk_buff *nskb; + void *hdr; + + /* unlikely is a linux kernel macro they are hint to the compiler to + * emit instructions that will cause branch prediction to favour + * the "likely" side of a jump instruction. + */ + if (unlikely(skb_headroom(skb) < 2)) { + /* skb_realloc_headroom(skb, newheadroom) is required when the + * memory space between skb?gt;data and skb?gt;head is getting + * too small. This function can be used to create a new socket + * buffer with a headroom corresponding to the size + * newheadroom (and not one single byte more). + */ + nskb = skb_realloc_headroom(skb, 2); + /* The macro dev_kfree_skb(), intended for use in drivers, + * turns into a call to consume_skb() + */ + dev_kfree_skb(skb); + skb = nskb; + } else { + } + + if (skb) { + /* Add data to the start of a buffer. This function extends + * the used data area of the buffer at the buffer start. + */ + hdr = skb_push(skb, 2); + /* memset() is used to fill a block of memory with a + * particular value.i.e 2 bytes tobe filled with 0 starting + * address is hdr. + */ + memset(hdr, 0, 2); + + } else { + } + + return skb; +} + +/* Name : oak_net_rbr_write_reg + * Returns : void + * Parameters : oak_rx_chan_t *rxc, oak_t *np, u32 widx, + * u32 ring + * Description : This function writes into receive buffer ring (rbr) + * hardware registers. + */ +static void oak_net_rbr_write_reg(oak_rx_chan_t *rxc, oak_t *np, u32 widx, + u32 ring) +{ + /* write memory barrier */ + wmb(); + rxc->rbr_widx = widx; + oak_unimac_io_write_32(np, OAK_UNI_RX_RING_CPU_PTR(ring), + widx & 0x7ffU); + oak_unimac_io_write_32(np, OAK_UNI_RX_RING_INT_CAUSE(ring), + OAK_MBOX_RX_RES_LOW); +} + +/* Name : oak_net_rbr_refill + * Returns : int + * Parameters : oak_t *np, u32 ring + * Description : This function refills the receive buffer + */ +int oak_net_rbr_refill(oak_t *np, u32 ring) +{ + u32 count; + u32 widx; + int num; + u32 sum = 0; + struct page *page; + dma_addr_t dma; + dma_addr_t offs; + oak_rx_chan_t *rxc = &np->rx_channel[ring]; + int rc = 0; + u32 loop_cnt; + + num = atomic_read(&rxc->rbr_pend); + count = rxc->rbr_size - 1; + + if (num >= count) { + rc = -ENOMEM; + } else { + count = (count - num) & ~1U; + widx = rxc->rbr_widx; + num = 0; + oakdbg(debug, PKTDATA, + "rbr_size=%d rbr_pend=%d refill cnt=%d widx=%d ridx=%d", + rxc->rbr_size, num, count, rxc->rbr_widx, rxc->rbr_ridx); + + /* Receive (RX) ring buffers are shared buffers between the + * device driver and network interface card (NIC), and store + * incoming packets until the device driver can process them. + * The below loop will refill pending buffer into receive + * buffer ring so that driver can process them and give it to + * upper layer in linux kernel. + */ + while ((count > 0) && (rc == 0)) { + /* Allocate a page */ + page = oak_net_alloc_page(np, &dma, DMA_FROM_DEVICE); + if (page) { + offs = dma; + loop_cnt = 0; + while ((count > 0) && + (loop_cnt < rxc->rbr_bpage)) { + oak_rxa_t *rba = &rxc->rba[widx]; + oak_rxd_t *rbr = &rxc->rbr[widx]; + + rba->page_virt = page; + + /* Keep track of last allocated descrip- + * tor in the page, count == 1 is used + * in cases when num descriptors are + * less than rbr_bpage. + */ + if (loop_cnt == (rxc->rbr_bpage - 1) || + count == 1) + rba->page_phys = dma; + else + rba->page_phys = 0; + + rba->page_offs = loop_cnt * + rxc->rbr_bsize; + rbr->buf_ptr_lo = (offs & 0xFFFFFFFFU); +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + /* High 32 bit */ + rbr->buf_ptr_hi = ((offs >> 32) + & 0xFFFFFFFFU); +#else + rbr->buf_ptr_hi = 0; +#endif + /* move to next write position */ + widx = NEXT_IDX(widx, rxc->rbr_size); + --count; + ++num; + ++loop_cnt; + /* offset to 2nd buffer in page */ + offs += rxc->rbr_bsize; + } + ++sum; + ++rxc->stat.rx_alloc_pages; + } else { + rc = -ENOMEM; + ++rxc->stat.rx_alloc_error; + } + } + /* Add integer to atomic variable */ + atomic_add(num, &rxc->rbr_pend); + oakdbg(debug, PKTDATA, + "%d pages allocated, widx=%d/%d, rc=%d", + sum, widx, rxc->rbr_widx, rc); + + if (rc == 0 && num > 0) + oak_net_rbr_write_reg(rxc, np, widx, ring); + } + + return rc; +} + +/* + * Name : oak_net_enable_irq + * Returns : int + * Parameter : struct net_device *net_dev + * Description : This function register interrupts. + */ +static int oak_net_enable_irq(struct net_device *net_dev) +{ + oak_t *np = netdev_priv(net_dev); + u16 qnum; + int retval; + + /* Request an IRQ vector */ + retval = oak_irq_request_ivec(np); + + if (retval == 0) { + /* Set level as IRQ request successful */ + np->level = IRQ_REQUEST_DONE; + /* Enable IRQ for logical device groups (ldg) */ + retval = oak_irq_enable_groups(np); + + if (retval == 0) { + /* Set level as IRQ group enable successful */ + np->level = IRQ_GRP_ENABLE_DONE; + /* Enable the Marvell header */ + oak_net_esu_ena_mrvl_hdr(np); + /* Set MTU in interface structure */ + retval = oak_net_esu_set_mtu(np->netdev, + np->netdev->mtu); + if (retval == 0) + /* Start all the txq and rxq */ + retval = oak_net_start_all(np); + if (retval == 0) { + /* Set level as setup is successful */ + np->level = SETUP_DONE; + /* Set the port speed */ + oak_net_esu_ena_speed(port_speed, np); + /* Set carrier, Device has detected that + * carrier. + */ + netif_carrier_on(net_dev); + for (qnum = 0; qnum < np->num_tx_chan; qnum++) + /* Allow transmit, Allow upper layers + * to call the device hard_start_xmit + * routine. + */ + netif_start_subqueue(np->netdev, qnum); + } + } + } + return retval; +} + +/* Name : oak_net_open + * Returns : int + * Parameters : struct net_device *net_dev + * Description : This function initialize the interface + */ +int oak_net_open(struct net_device *net_dev) +{ + oak_t *np = netdev_priv(net_dev); + struct sockaddr addr; + int err = -ENODEV; + bool rc; + + /* Manipulate the module usage count, Before calling into module code, + * you should call try_module_get() on that module. if it fails, then + * the module is being removed and you should act as if it wasn't there + * Otherwise, you can safely enter the module, and call module_put() + * when you're finished. + */ + rc = try_module_get(THIS_MODULE); + + if (rc != 0) { + /* Reset the statistics counters */ + err = oak_unimac_reset(np); + if (err == 0 && np->level == TRY_MODULE) { + /* Allocate memory for channels */ + err = oak_unimac_alloc_channels(np, rxs, txs, + chan, rto); + if (err == 0) { + /* Set the level as memory allocation done + * to indicate allocation is successful + */ + np->level = MEM_ALLOC_DONE; + err = oak_net_enable_irq(net_dev); + } + } + } + + /* If we have valid MAC address in oak_t, + * which is read from EPU_DATA registers then + * copy to socket address structure and set it + * to the NIC. + */ + if (err == 0) { + rc = is_valid_ether_addr(np->mac_address); + if (rc != 0) { + ether_addr_copy(addr.sa_data, np->mac_address); + err = oak_net_set_mac_addr(net_dev, (void *)&addr); + if (err != 0) + pr_err("Fail to set MAC address\n"); + } + } else { + oak_net_close(net_dev); + } + + oakdbg(debug, PROBE, "ndev=%p err=%d", net_dev, err); + + return err; +} + +/* Name : oak_net_close + * Returns : int + * Parameters : struct net_device *net_dev + * Description : This function close the interface + */ +int oak_net_close(struct net_device *net_dev) +{ + oak_t *np = netdev_priv(net_dev); + + /* All the below function are having void as return values. But the + * oak_net_close function is called from kernel and expects int as + * return value + */ + netif_carrier_off(net_dev); + netif_tx_disable(net_dev); + /* When the interface goes down we need to remember the + * MAC address of an interface. Because the same MAC + * address will be used when we open the interface. + * But when we remove the module from kernel and then + * load the module, the MAC address in EPU_DATA will be + * configured. + */ + ether_addr_copy(np->mac_address, net_dev->dev_addr); + + if (np->level >= SETUP_DONE) + oak_net_stop_all(np); + if (np->level >= IRQ_GRP_ENABLE_DONE) + oak_irq_disable_groups(np); + if (np->level >= IRQ_REQUEST_DONE) + oak_irq_release_ivec(np); + if (np->level >= MEM_ALLOC_DONE) { + oak_unimac_free_channels(np); + np->level = TRY_MODULE; + module_put(THIS_MODULE); + } + + oakdbg(debug, PROBE, "ndev=%p", net_dev); + /* returns 0 to satisfy return type */ + return 0; +} + +/* Name : oak_net_ioctl + * Returns : int + * Parameters : struct net_device *net_dev, struct ifreq *ifr, int cmd + * Description : This function handles IOCTL request + */ +int oak_net_ioctl(struct net_device *net_dev, struct ifreq *ifr, int cmd) +{ + oak_t *np = netdev_priv(net_dev); + int retval = -EOPNOTSUPP; +#ifdef CMDTOOL + if (cmd == OAK_IOCTL_REG_MAC_REQ || + cmd == OAK_IOCTL_REG_ESU_REQ || + cmd == OAK_IOCTL_REG_AHSI_REQ) + retval = oak_ctl_direct_register_access(np, ifr, cmd); + + if (cmd == OAK_IOCTL_STAT) + retval = oak_ctl_channel_status_access(np, ifr, cmd); + + if (cmd == OAK_IOCTL_SET_MAC_RATE_A) + retval = oak_ctl_set_mac_rate(np, ifr, cmd); + + if (cmd == OAK_IOCTL_SET_MAC_RATE_B) + retval = oak_ctl_set_mac_rate(np, ifr, cmd); + + if (cmd == OAK_IOCTL_RXFLOW) + retval = oak_ctl_set_rx_flow(np, ifr, cmd); +#endif + oakdbg(debug, DRV, "np=%p cmd=0x%x", np, cmd); + + return retval; +} + +/* Name : add_napi + * Returns : void + * Parameters: struct net_device * netdev + * Description : This function registers to napi interface to receive interrupts + */ +void oak_net_add_napi(struct net_device *netdev) +{ + oak_t *np = netdev_priv(netdev); + ldg_t *ldg = np->gicu.ldg; + u32 num_ldg = np->gicu.num_ldg; + + while (num_ldg > 0) { + /* Initialize a napi context */ + netif_napi_add(netdev, &ldg->napi, oak_net_poll, napi_wt); + /* Enable NAPI scheduling */ + napi_enable(&ldg->napi); + ++ldg; + --num_ldg; + } + + oakdbg(debug, PROBE, "%d napi IF added", np->gicu.num_ldg); +} + +/* Name : del_napi + * Returns : void + * Parameters: struct net_device * netdev + * Description : This function disables the Oak driver in napi + */ +void oak_net_del_napi(struct net_device *netdev) +{ + oak_t *np = netdev_priv(netdev); + ldg_t *ldg = np->gicu.ldg; + u32 num_ldg = np->gicu.num_ldg; + + while (num_ldg > 0) { + /* Stop NAPI from being scheduled on this context. Waits till + * any outstanding processing completes then disable napi for + * all the rings. + */ + napi_disable(&ldg->napi); + /* Unregisters napi structure */ + netif_napi_del(&ldg->napi); + ++ldg; + --num_ldg; + } + + oakdbg(debug, PROBE, "%d napi IF deleted", np->gicu.num_ldg); +} + +/* Name : set_mac_addr + * Returns : int + * Parameters: struct net_device * dev = dev, void * p_addr = addr + * Description : This function sets the provided mac address to the AHSI RX DA + */ +int oak_net_set_mac_addr(struct net_device *dev, void *p_addr) +{ + oak_t *np = netdev_priv(dev); + struct sockaddr *addr = p_addr; + u32 channel = 0; + int rc; + + rc = is_valid_ether_addr(addr->sa_data); + + if (rc == 0) { + rc = -EINVAL; + } else { + memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); + + /* When an interface come up we need to remember the + * MAC address of an interface. Because the same MAC + * address will be used when we open the interface. + * But when we remove the module from kernel and then + * load the module, the MAC address in EPU_DATA will be + * configured. + */ + ether_addr_copy(np->mac_address, dev->dev_addr); + + rc = netif_running(dev); + + if (rc) { + while (channel < np->num_rx_chan) { + oak_unimac_set_rx_da(np, channel, + dev->dev_addr, 1); + ++channel; + } + rc = 0; + } + } + oakdbg(debug, DRV, "addr=0x%02x%02x%02x%02x%02x%02x rc=%d", + dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], + dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5], rc); + + return rc; +} + +/* Name : oak_net_alloc_page + * Returns : struct page * + * Parameters : oak_t *np, dma_addr_t *dma, enum dma_data_direction dir + * Description : This function allocates page + */ +struct page *oak_net_alloc_page(oak_t *np, dma_addr_t *dma, + enum dma_data_direction dir) +{ + struct page *page; + + /* 0: 4K */ + np->page_order = 0; + np->page_size = (PAGE_SIZE << np->page_order); + + /* Allocate a single page and return a pointer to its page structure */ + page = alloc_page(GFP_ATOMIC /* | __GFP_COLD */ | __GFP_COMP); + + if (!page) { + *dma = 0; + } else { + *dma = dma_map_page(np->device, page, 0, np->page_size, + dir); + if (dma_mapping_error(np->device, *dma) != 0) { + __free_page(page); + *dma = 0; + page = NULL; + } + } + + return page; +} + +/* Name : oak_net_select_queue + * Returns : u16 + * Parameters : struct net_device *dev, struct sk_buff *skb, + * struct net_device *sb_dev + * Description : This function pre-seed the SKB by recording the RX queue + */ +u16 oak_net_select_queue(struct net_device *dev, struct sk_buff *skb, + struct net_device *sb_dev) +{ + oak_t *np = netdev_priv(dev); + u32 txq = 0; + bool rec; + u16 retval = 0; + + /* The idea is that drivers which implement multiqueue RX + * pre-seed the SKB by recording the RX queue selected by + * the hardware. + * If such a seed is found on TX, we'll use that to select + * the outgoing TX queue. + * This helps get more consistent load balancing on router + * and firewall loads. + */ + rec = skb_rx_queue_recorded(skb); + + if (!rec) + txq = smp_processor_id(); + else + txq = skb_get_rx_queue(skb); + + if (txq >= np->num_tx_chan && np->num_tx_chan > 0) + txq %= np->num_tx_chan; + + retval = (u16)txq; + + oakdbg(debug, DRV, "queue=%d of %d", txq, dev->real_num_tx_queues); + + return retval; +} + +/* Name : xmit_frame + * Returns : int + * Parameters: struct sk_buff * skb, struct net_device * net_dev + * Description : This function handles the transmit of skb to the queue + */ +netdev_tx_t oak_net_xmit_frame(struct sk_buff *skb, struct net_device *net_dev) +{ + oak_t *np = netdev_priv(net_dev); + u16 txq; + netdev_tx_t rc; + u16 nfrags; + + txq = skb->queue_mapping; + /* skb_shinfo(skb)->nr_frags shows the number of paged fragments */ + nfrags = skb_shinfo(skb)->nr_frags + 1; + rc = oak_net_stop_tx_queue(np, nfrags, txq); + + if (rc == NETDEV_TX_OK) + rc = oak_net_tx_packet(np, skb, txq); + + oakdbg(debug, TX_DONE, "nfrags=%d txq=%d rc=%d", nfrags, txq, rc); + + return rc; +} + +/* Name : oak_net_unmap_tx_pkt + * Returns : void + * Parameters : oak_txi_t *tbi, oak_tx_chan_t *txc + * Description : This function unmaps and frees the received packet + */ +static void oak_net_unmap_tx_pkt(oak_txi_t *tbi, oak_tx_chan_t *txc) +{ + oak_t *np = txc->oak; + + if (txc->tbr_size > 0) + txc->tbr_ridx = NEXT_IDX(txc->tbr_ridx, txc->tbr_size); + + /* With dma_unmap_single you unmap the memory mapped above. + * You should do this when your transfers are over. + * You should call dma_unmap_page() when the DMA activity is + * finished, e.g., from the interrupt which told you that the + * DMA transfer is done. + */ + if (tbi->mapping != 0) { + if ((tbi->flags & TX_BUFF_INFO_ADR_MAPS) + == TX_BUFF_INFO_ADR_MAPS) + dma_unmap_single(np->device, tbi->mapping, + tbi->mapsize, DMA_TO_DEVICE); + else + if ((tbi->flags & TX_BUFF_INFO_ADR_MAPP) + == TX_BUFF_INFO_ADR_MAPP) + dma_unmap_page(np->device, tbi->mapping, + tbi->mapsize, DMA_TO_DEVICE); + tbi->mapping = 0; + tbi->mapsize = 0; + } +} + +/* Name : oak_net_process_tx_pkt + * Returns : int + * Parameters : oak_tx_chan_t *txc = txc, int desc_num = desc_num + * Description : This function process the received packet to skb layer + */ +static u32 oak_net_process_tx_pkt(oak_tx_chan_t *txc, int desc_num) +{ + oak_txi_t *tbi; + u32 work_done = 0; + + while (desc_num > 0) { + tbi = &txc->tbi[txc->tbr_ridx]; + + oak_net_unmap_tx_pkt(tbi, txc); + + /* The macro dev_kfree_skb(), intended for use in drivers, + * turns into a call to consume_skb(). You free pages with + * the free family function. + */ + if ((tbi->flags & TX_BUFF_INFO_EOP) == TX_BUFF_INFO_EOP) { + if (tbi->skb) + dev_kfree_skb(tbi->skb); + if (tbi->page) + __free_page(tbi->page); + ++txc->stat.tx_frame_compl; + } + + tbi->flags = 0; + tbi->skb = NULL; + tbi->page = NULL; + --desc_num; + atomic_dec(&txc->tbr_pend); + ++work_done; + } /* end of while */ + + oakdbg(debug, TX_DONE, "work done=%d", work_done); + return work_done; +} + +/* Name : start_all + * Returns : int + * Parameters: oak_t * np = np + * Description : This function starts all the Tx/Rx channels. + */ +int oak_net_start_all(oak_t *np) +{ + u32 i = 0; + int rc = 0; + + /* Call receive buffer refill for all rx channels */ + while (i < np->num_rx_chan && rc == 0) { + rc = oak_net_rbr_refill(np, i); + ++i; + } + + /* Start all the transmit and receive queue */ + if (rc == 0) + rc = oak_unimac_start_all_txq(np, 1); + + if (rc == 0) + rc = oak_unimac_start_all_rxq(np, 1); + + if (rc == 0) + oak_irq_ena_general(np, 1); + + oakdbg(debug, IFDOWN, " ok"); + + return rc; +} + +/* Name : stop_all + * Returns : void + * Parameters: oak_t * np = np + * Description : This function frees up all the tx & rx transfer buffers + */ +void oak_net_stop_all(oak_t *np) +{ + u32 i = 0; + + oak_unimac_start_all_rxq(np, 0); + oak_unimac_start_all_txq(np, 0); + + /* Wait for all the Rx descriptors to be free */ + usleep_range(10000, 11000); + + /* Free all the receive channel buffer */ + while (i < np->num_rx_chan) { + oak_net_rbr_free(&np->rx_channel[i]); + ++i; + } + + i = 0; + + /* Free all the transmit channel buffer */ + while (i < np->num_tx_chan) { + oak_net_tbr_free(&np->tx_channel[i]); + ++i; + } + + oak_irq_ena_general(np, 0); + oakdbg(debug, IFDOWN, " ok"); +} + +/* Name : tx_stats + * Returns : void + * Parameters: oak_tx_chan_t * txc = txc, int len = len + * Description : This function gets tx channel stats for various data lengths + */ +static void oak_net_tx_stats(oak_tx_chan_t *txc, u32 len) +{ + if (len <= 64) + ++txc->stat.tx_64; + else + if (len <= 128) + ++txc->stat.tx_128; + else + if (len <= 256) + ++txc->stat.tx_256; + else + if (len <= 512) + ++txc->stat.tx_512; + else + if (len <= 1024) + ++txc->stat.tx_1024; + else + ++txc->stat.tx_2048; +} + +/* Name : rx_stats + * Returns : void + * Parameters: oak_rx_chan_t * rxc = rxc, u32 len = len + * Description : This function gets rx channel stats for various data lengths + */ +static void oak_net_rx_stats(oak_rx_chan_t *rxc, u32 len) +{ + if (len <= 64) + ++rxc->stat.rx_64; + else + if (len <= 128) + ++rxc->stat.rx_128; + else + if (len <= 256) + ++rxc->stat.rx_256; + else + if (len <= 512) + ++rxc->stat.rx_512; + else + if (len <= 1024) + ++rxc->stat.rx_1024; + else + ++rxc->stat.rx_2048; +} + +/* Name : tbr_free + * Returns : void + * Parameters: oak_tx_chan_t * txp = txp + * Description : This function frees tx channel transfer buffer + */ +void oak_net_tbr_free(oak_tx_chan_t *txp) +{ + int cnt; + + cnt = atomic_read(&txp->tbr_pend); + oak_net_process_tx_pkt(txp, cnt); + atomic_set(&txp->tbr_pend, 0); + /* write buffer index */ + txp->tbr_widx = 0; + txp->tbr_ridx = 0; +} + +/* Name : oak_net_rbr_reset + * Returns : void + * Parameters : oak_rx_chan_t *rxp = rxp + * Description : This function reset the rxp, then move to next index + */ +static void oak_net_rbr_reset(oak_rx_chan_t *rxp) +{ + rxp->rba[rxp->rbr_ridx].page_virt = NULL; + rxp->rbr[rxp->rbr_ridx].buf_ptr_hi = 0; + rxp->rbr[rxp->rbr_ridx].buf_ptr_lo = 0; + + if (rxp->rbr_size > 0) + rxp->rbr_ridx = NEXT_IDX(rxp->rbr_ridx, rxp->rbr_size); +} + +/* Name : oak_net_rbr_unmap + * Returns : void + * Parameters : oak_rx_chan_t *rxp = rxp, struct page *page, dma_addr_t dma + * Description : This function unmap the receive buffer ring + */ +static void oak_net_rbr_unmap(oak_rx_chan_t *rxp, struct page *page, + dma_addr_t dma) +{ + oak_t *np = rxp->oak; + + dma_unmap_page(np->device, dma, np->page_size, DMA_FROM_DEVICE); + ++rxp->stat.rx_unmap_pages; + /* Reset index, mapping and page_phys */ + rxp->rba[rxp->rbr_ridx].page_phys = 0; + page->index = 0; + page->mapping = NULL; + __free_page(page); +} + +/* Name : oak_net_rbr_free + * Returns : void + * Parameters : oak_rx_chan_t *rxp = rxp + * Description : This function free the receive buffer ring + */ +void oak_net_rbr_free(oak_rx_chan_t *rxp) +{ + u32 sum = 0; + struct page *page; + dma_addr_t dma; + + while (rxp->rbr_ridx != rxp->rbr_widx) { + page = rxp->rba[rxp->rbr_ridx].page_virt; + + if (page) { + dma = rxp->rba[rxp->rbr_ridx].page_phys; + ++sum; + + if (dma != 0) + /* Unmap the memory */ + oak_net_rbr_unmap(rxp, page, dma); + } + /* Reset the buffer index */ + oak_net_rbr_reset(rxp); + } + oakdbg(debug, IFDOWN, + "totally freed ring buffer size %d kByte (ring entries: %d)", + sum, rxp->rbr_size); + atomic_set(&rxp->rbr_pend, 0); + /* write buffer index */ + rxp->rbr_widx = 0; + /* read buffer index */ + rxp->rbr_ridx = 0; + rxp->rbr_len = 0; +} + +/* Name : oak_net_tx_packet + * Returns : int + * Parameters : oak_t *np, struct sk_buff *skb, u16 txq + * Description : This function transmit the packet + */ +static netdev_tx_t oak_net_tx_packet(oak_t *np, struct sk_buff *skb, u16 txq) +{ + oak_tx_chan_t *txc = &np->tx_channel[txq]; + int num = 0; + u32 frag_idx = 0; + u32 flags = 0; + u32 len = 0; + u32 cs_g3 = 0; + u32 cs_g4 = 0; + skb_frag_t *frag; + u32 nfrags; + dma_addr_t mapping = 0; + u32 retval = 0; + + if (mhdr != 0) + skb = oak_net_tx_prepend_mrvl_hdr(skb); + if (skb) { + /* OAK HW does not need padding in the data, + * only limitation is zero length packet. + * So zero byte transfer should not be programmed + * in the descriptor. + */ + if (skb->len < OAK_ONEBYTE) { + /* pad an skbuff up to a minimal size */ + if (skb_padto(skb, OAK_ONEBYTE) == 0) + len = OAK_ONEBYTE; + } else { + /* orphan a buffer + * If a buffer currently has an owner then we call the + * owner's destructor function and make the skb + * unowned. The buffer continues to exist but is no + * longer charged to its former owner. + */ + skb_orphan(skb); + /* The skb_headlen() function returns the length of + * the data presently in the kmalloc'd part of the + * buffer. + */ + len = skb_headlen(skb); + } + /* TCP segmentation allows a device to segment a single frame + * into multiple frames with a data payload size specified in + * skb_shinfo()->nr_frags + */ + nfrags = skb_shinfo(skb)->nr_frags; + + if (len > 0) { + /* When you have a single buffer to transfer, map it + * with dma_map_single + */ + mapping = dma_map_single(np->device, skb->data, len, + DMA_TO_DEVICE); + flags = TX_BUFF_INFO_ADR_MAPS; + ++num; + } else { + if (nfrags > 0) { + frag = &skb_shinfo(skb)->frags[frag_idx]; + len = skb_frag_size(frag); + /* Single-page streaming mappings: + * set up a mapping on a buffer for which you + * have a struct page pointer with user-space + * buffers mapped with get_user_pages. + */ + mapping = dma_map_page(np->device, + skb_frag_page(frag), + skb_frag_off(frag), + len, DMA_TO_DEVICE); + flags = TX_BUFF_INFO_ADR_MAPP; + ++num; + ++frag_idx; + } + } + if (num > 0) { + retval = oak_chksum_get_tx_config(skb, &cs_g3, &cs_g4); + /* For error case set checksum to zero */ + if (retval) { + cs_g3 = 0; + cs_g4 = 0; + } + oak_net_set_txd_first(txc, len, cs_g3, cs_g4, mapping, + len, flags); + /* Continue single-page streaming mappings till the + * fragmentation index is less than number of fragment + * in a while loop. + */ + while (frag_idx < nfrags) { + if (txc->tbr_size > 0) + txc->tbr_widx = NEXT_IDX(txc->tbr_widx, + txc->tbr_size); + frag = &skb_shinfo(skb)->frags[frag_idx]; + len = skb_frag_size(frag); + mapping = dma_map_page(np->device, + skb_frag_page(frag), + skb_frag_off(frag), + len, DMA_TO_DEVICE); + oak_net_set_txd_page(txc, len, mapping, len, + TX_BUFF_INFO_ADR_MAPP); + ++num; + ++frag_idx; + } + oak_net_set_txd_last(txc, skb, NULL); + if (txc->tbr_size > 0) + txc->tbr_widx = NEXT_IDX(txc->tbr_widx, + txc->tbr_size); + atomic_add(num, &txc->tbr_pend); + /* Write memory barrier */ + wmb(); + ++txc->stat.tx_frame_count; + txc->stat.tx_byte_count += skb->len; + /* Static Counter: Increment tx stats counter and bytes + * for ifconfig + */ + np->netdev->stats.tx_packets++; + np->netdev->stats.tx_bytes += skb->len; + oak_net_tx_stats(txc, skb->len); + oak_unimac_io_write_32(np, + OAK_UNI_TX_RING_CPU_PTR(txq), + txc->tbr_widx & 0x7ffU); + } else { + ++txc->stat.tx_drop; + } + } + return NETDEV_TX_OK; +} + +/* Name : skb_tx_protocol_type + * Returns : int + * Parameters : struct sk_buff *skb + * Description : This function returns the transmit frames protocol type for + * deciding the checksum offload configuration. + */ +int oak_net_skb_tx_protocol_type(struct sk_buff *skb) +{ + u8 ip_prot = 0; + int rc = NO_CHKSUM; + __be16 prot = skb->protocol; + + if (prot == htons(ETH_P_8021Q)) + prot = vlan_eth_hdr(skb)->h_vlan_encapsulated_proto; + if (prot == htons(ETH_P_IP)) { + ip_prot = ip_hdr(skb)->protocol; + rc = L3_CHKSUM; + } else if (prot == htons(ETH_P_IPV6)) { + ip_prot = ipv6_hdr(skb)->nexthdr; + rc = L3_CHKSUM; + } + if (ip_prot == IPPROTO_TCP || ip_prot == IPPROTO_UDP) + rc = L3_L4_CHKSUM; + return rc; +} + +/* Name : oak_net_tx_update_tbr_len + * Returns : void + * Parameters : ldg_t *ldg, oak_tx_chan_t *txc, u32 ring + * Description : This function update buffer len + */ +static void oak_net_tx_update_tbr_len(ldg_t *ldg, oak_tx_chan_t *txc, + u32 ring) +{ + oak_t *np = ldg->device; + u32 reason = 0; + u32 tidx; + + if (txc->tbr_len == 0) { + ++txc->stat.tx_interrupts; + reason = oak_unimac_disable_and_get_tx_irq_reason(np, ring, + &tidx); + oakdbg(debug, TX_DONE, "MB ring=%d reason=0x%x tidx=%d", ring, + reason, tidx); + if ((reason & OAK_MBOX_TX_COMP) != 0) { + if (tidx < txc->tbr_ridx) + txc->tbr_len = txc->tbr_size - txc->tbr_ridx + + tidx; + else + txc->tbr_len = tidx - txc->tbr_ridx; + } + } +} + +/* Name : oak_net_tx_min_budget + * Returns : int + * Parameters : oak_tx_chan_t *txc, u32 len, int budget + * Description : This function find the min of budget and process packet + */ +static u32 oak_net_tx_min_budget(oak_tx_chan_t *txc, u32 len, int budget) +{ + int todo; + u32 work_done = 0; + + /* Calculate the min budget and process the tx packet */ + if (len > 0) { + todo = len; + todo = min(budget, todo); + work_done = oak_net_process_tx_pkt(txc, todo); + txc->tbr_len -= work_done; + } + + return work_done; +} + +/* Name : oak_net_tx_work + * Returns : int + * Parameters : ldg_t *ldg = ldg, u32 ring = ring, int budget = budget + * Description : This function process the transmit packet + */ +static u32 oak_net_tx_work(ldg_t *ldg, u32 ring, int budget) +{ + oak_t *np = ldg->device; + oak_tx_chan_t *txc; + u32 retval; + + txc = &np->tx_channel[ring]; + /* This function inserts a hardware memory barrier that prevents any + * memory access from being moved and executed on the other side of + * the barrier. It guarantees that any memory access initiated before + * the memory barrier will be complete before passing the barrier, + * and all subsequent memory accesses will be executed after the + * barrier. This function is the same as the mb() function on + * multi-processor systems, and it is the same as the barrier() + * function on uni-processor systems. + */ + smp_mb(); + + oak_net_tx_update_tbr_len(ldg, txc, ring); + + retval = oak_net_tx_min_budget(txc, txc->tbr_len, budget); + + if (txc->tbr_len == 0) + oak_unimac_ena_tx_ring_irq(np, ring, 1); + + return retval; +} + +/* Name : oak_net_rx_update_counters + * Returns : void + * Parameters : oak_t *np, struct sk_buff *skb, ldg_t *ldg, oak_rx_chan_t *rxc + * Description : This function update counters, record rx queue and + * calls napi gro receive. + */ +static void oak_net_rx_update_counters(oak_t *np, struct sk_buff *skb, + ldg_t *ldg, + oak_rx_chan_t *rxc) +{ + /* Static Counter: Increment rx stats counter and bytes for ifconfig */ + np->netdev->stats.rx_packets++; + np->netdev->stats.rx_bytes += skb->len; + /* Increment rx channel status byte count */ + rxc->stat.rx_byte_count += skb->len; + /* Update skb protocol */ + skb->protocol = eth_type_trans(skb, np->netdev); + /* Calling skb_record_rx_queue() to set the rx queue to the queue_index + * fixes the association between descriptor and rx queue. + */ + skb_record_rx_queue(skb, ldg->msi_grp); + /* GRO (Generic receive offload) of the Linux kernel network protocol + * stack If the driver supported by GRO is processed in this way, read + * the data packet in the callback method of NAPI, and then call the + * GRO interface napi_gro_receive or napi_gro_frags to feed the data + * packet into the protocol stack. The specific GRO work is carried out + * in these two functions, they will eventually call __napi_gro_receive + * Here is napi_gro_receive, which will eventually call napi_skb_finish + * and __napi_gro_receive. Then when will GRO feed the data into the + * protocol stack, there will be two exit points, one is in + * napi_skb_finish, he will judge the return value of __napi_gro_receive + * to determine whether it is necessary to immediately feed the data + * packet into the protocol stack or Save. + */ + napi_gro_receive(&ldg->napi, skb); +} + +/* Name : oak_net_rx_work + * Returns : int + * Parameters : ldg_t *ldg, u32 ring, int budget + * Description : This function implements the receive pkt + */ +static u32 oak_net_rx_work(ldg_t *ldg, u32 ring, int budget) +{ + oak_t *np = ldg->device; + oak_rx_chan_t *rxc = &np->rx_channel[ring]; + u32 work_done = 0; + struct sk_buff *skb; + u32 reason; + int todo; + u32 ridx; + u32 compl; + + if (rxc->rbr_len == 0) { + /* This function inserts a hardware memory barrier that + * prevents any memory access from being moved and executed + * on the other side of the barrier. It guarantees that any + * memory access initiated before the memory barrier will be + * complete before passing the barrier, and all subsequent + * memory accesses will be executed after the barrier. This + * function is the same as the mb() function on multi-processor + * systems, and it is the same as the barrier() function on + * uni-processor systems. + */ + smp_mb(); + reason = le32_to_cpup((__le32 *)&rxc->mbox->intr_cause); + ++rxc->stat.rx_interrupts; + oak_unimac_io_write_32(np, OAK_UNI_RX_RING_INT_CAUSE(ring), + OAK_MBOX_RX_COMP); + ridx = le32_to_cpup((__le32 *)&rxc->mbox->dma_ptr_rel); + reason &= (OAK_MBOX_RX_COMP | OAK_MBOX_RX_RES_LOW); + + if ((reason & OAK_MBOX_RX_COMP) != 0) { + if (ridx < rxc->rbr_ridx) + rxc->rbr_len = rxc->rbr_size - rxc->rbr_ridx + + ridx; + else + rxc->rbr_len = ridx - rxc->rbr_ridx; + } + } else { + reason = 0; + } + + todo = rxc->rbr_len; + todo = min(budget, todo); + + while ((todo > 0) && (rxc->rbr_len > 0)) { + skb = NULL; + + compl = oak_net_process_rx_pkt(rxc, rxc->rbr_len, &skb); + + if (skb) + oak_net_rx_update_counters(np, skb, ldg, rxc); + + rxc->rbr_len -= compl; + ++work_done; + --todo; + } + + if (rxc->rbr_len == 0) { + oakdbg(debug, RX_STATUS, "irq enabled"); + oak_unimac_io_write_32(np, OAK_UNI_RX_RING_INT_MASK(ring), + OAK_MBOX_RX_COMP); + } + + return work_done; +} + +/* Name : oak_net_process_alloc_skb + * Returns : int + * Parameters : oak_rx_chan_t *rxc, int *tlen + * Description : This function allocate skb + */ +static int oak_net_process_alloc_skb(oak_rx_chan_t *rxc, int *tlen) +{ + oak_t *np = rxc->oak; + int good_frame; + + *tlen = 0; + if (!rxc->skb) { + rxc->skb = netdev_alloc_skb(np->netdev, OAK_RX_SKB_ALLOC_SIZE); + /* Default checksum */ + rxc->skb->ip_summed = CHECKSUM_NONE; + good_frame = 0; + } else { + /* continue last good frame == 1 */ + good_frame = 1; + ++rxc->stat.rx_fragments; + } + + if (good_frame == 1) + *tlen = rxc->skb->len; + + return good_frame; +} + +/* Name : oak_net_crc_csum_update + * Returns : void + * Parameters : oak_rx_chan_t *rxc, oak_rxs_t *rsr, int good_frame + * Description : This function update the crc, csum counters + */ +static void oak_net_crc_csum_update(oak_rx_chan_t *rxc, oak_rxs_t *rsr, + int good_frame) +{ + if (good_frame == 1) { + if (rsr->es == 0) { + rxc->skb->ip_summed = oak_chksum_get_rx_config(rxc, + rsr); + } else { + if (rsr->ec == 0) { + ++rxc->stat.rx_badcrc; + } else { + if (rsr->ec == 1) { + ++rxc->stat.rx_badcsum; + } else { + if (rsr->ec == 3) { + ++rxc->stat.rx_nores; + } + } + } + } + } +} + +/* Name : oak_net_unmap_and_free_page + * Returns : void + * Parameters : oak_t *np, oak_rxa_t *rba, oak_rx_chan_t *rxc, + * struct page *page, int good_frame + * Description : This function unmap and free pages + */ +static void oak_net_unmap_and_free_page(oak_t *np, oak_rxa_t *rba, + oak_rx_chan_t *rxc, + struct page *page, int good_frame) +{ + if (rba->page_phys != 0) { + dma_unmap_page(np->device, rba->page_phys, + np->page_size, DMA_FROM_DEVICE); + ++rxc->stat.rx_unmap_pages; + + oakdbg(debug, RX_STATUS, + " free page=0x%p dma=0x%llx ", + rba->page_virt, rba->page_phys); + /* Reset index, mapping and page_phys */ + rba->page_phys = 0; + page->index = 0; + page->mapping = NULL; + if (good_frame == 0) + __free_page(page); + } else { + if (good_frame == 1) + get_page(page); + } + rba->page_virt = NULL; +} + +/* Name : oak_net_update_stats + * Returns : void + * Parameters : oak_rx_chan_t *rxc, oak_rxs_t *rsr, int good_frame, + * int *good_frame, int *comp_frame + * Description : This function update skb counter statistics + */ +static void oak_net_update_stats(oak_rx_chan_t *rxc, oak_rxs_t *rsr, + int *good_frame, int *comp_frame) +{ + /* From Oak AHSI data sheet, + * Bit 20: First. + * Indicates the first descriptor of a frame. + * The first byte of a frame resides in the buffer pointed to by rsr + * descriptor. + * Bit 21: Last. + * Indicates the last descriptor of a frame. The last byte of a frame + * resides in the buffer pointed to by rsr descriptor. + * The combination of and indicates which part of the + * frame rsr descriptor describes. + * + * 0x00: middle descriptor of a frame + * 0x01: last descriptor of a frame + * 0x10: first descriptor of a frame + * 0x11: single descriptor describes a complete frame. + */ + if (rsr->first_last == 3) { + if (*good_frame == 1) { + ++rxc->stat.rx_no_eof; + *good_frame = 0; + } else { + *good_frame = 1; + } + *comp_frame = 1; + } else { + if (rsr->first_last == 2) { + if (*good_frame == 1) { + ++rxc->stat.rx_no_eof; + *good_frame = 0; + *comp_frame = 1; + } else { + *good_frame = 1; + } + } else { + if (rsr->first_last == 1) { + if (*good_frame == 0) + ++rxc->stat.rx_no_sof; + *comp_frame = 1; + } else { + if (*good_frame == 0) { + ++rxc->stat.rx_no_sof; + *comp_frame = 1; + } + } + } + } +} + +/* Name : oak_net_process_rx_pkt + * Returns : int + * Parameters : oak_rx_chan_t * rxc, u32 desc_num, struct sk_buff **target + * Description : This function process the received packet. + */ +static int oak_net_process_rx_pkt(oak_rx_chan_t *rxc, u32 desc_num, + struct sk_buff **target) +{ + oak_t *np = rxc->oak; + int work_done = 0; + int comp_frame = 0; + int tlen = 0; + int good_frame; + int blen; + struct page *page; + u32 off = 0; + int retval; + + good_frame = oak_net_process_alloc_skb(rxc, &tlen); + + *target = NULL; + + if (rxc->skb) { + while ((desc_num > 0) && (comp_frame == 0)) { + /* Rx status information + * Receive status register (rsr) + * Receive buffer address (rba) + */ + oak_rxs_t *rsr = &rxc->rsr[rxc->rbr_ridx]; + oak_rxa_t *rba = &rxc->rba[rxc->rbr_ridx]; + + /* Receive buffer length + * Transmit buffer length + * Page address + */ + blen = rsr->bc; + tlen += blen; + page = rba->page_virt; + + /* If the page is valid then, the received frame is a + * good frame. We do the following + * - Update the statistics counters by checking + * combination + * - Take out the number of fragments of the frame + * - Check for Marvell header and adjust offset. + * - Initialise a paged fragment in an skb + * - Update skb length, data length and true size + * If frame is not good then unmap and free the page. + * Set good_frame as 0, To indicate page lookup failure + */ + if (page) { + oak_net_update_stats(rxc, rsr, &good_frame, + &comp_frame); + if (good_frame == 1) { + int nfrags = + skb_shinfo(rxc->skb)->nr_frags; + + if (mhdr != 0) { + if ((rsr->first_last & 2U) + == 2) { + blen -= 2; + tlen -= 2; + off = 2; + } else { + off = 0; + } + } + skb_fill_page_desc(rxc->skb, + nfrags, + page, + rba->page_offs + off, + blen); + rxc->skb->len += blen; + rxc->skb->data_len += blen; + rxc->skb->truesize += blen; + } + oak_net_unmap_and_free_page(np, rba, rxc, page, + good_frame); + } else { + /* Page lookup failure */ + good_frame = 0; + } + + /* If we received a complete good frame, then we do the + * following + * - Update the CRC checksum of the frame + * - adjust headroom using skb_reserve() + * - Pull advance tail of skb header then free skb + * - Update rx channel statistics counters for various + * length and set skb as NULL + * else its a bad frame. free the skb and set as NULL. + * Point to next receive buffer descriptor index + * Decrement the descriptor number and increment + * work done counter indicating work is completed. + * + */ + if (comp_frame == 1) { + oak_net_crc_csum_update(rxc, rsr, good_frame); + + if (good_frame == 1) { + skb_reserve(rxc->skb, NET_IP_ALIGN); + if (!__pskb_pull_tail(rxc->skb, + min(tlen, + ETH_HLEN))) { + dev_kfree_skb_any(rxc->skb); + } else { + ++rxc->stat.rx_goodframe; + *target = rxc->skb; + } + oak_net_rx_stats(rxc, rxc->skb->len); + rxc->skb = NULL; + } else { + ++rxc->stat.rx_badframe; + dev_kfree_skb(rxc->skb); + rxc->skb = NULL; + } + oakdbg(debug, RX_STATUS, + "page=0x%p good-frame=%d comp_frame-frame=%d ridx=%d tlen=%d", + page, good_frame, comp_frame, + rxc->rbr_ridx, tlen); + } + if (rxc->rbr_size > 0) + rxc->rbr_ridx = NEXT_IDX(rxc->rbr_ridx, + rxc->rbr_size); + --desc_num; + atomic_dec(&rxc->rbr_pend); + ++work_done; + } + } + retval = work_done; + oakdbg(debug, RX_STATUS, + " work_done=%d %s", + work_done, !rxc->skb ? "" : "(continued)"); + + return retval; +} + +/* Name : oak_net_check_irq_mask + * Returns : int + * Parameters : ldg_t *ldg, u32 ring, u32 reason, int budget + * Description : This function check irq reason + */ +static u32 oak_net_check_irq_mask(ldg_t *ldg, u32 ring, u32 reason, + int budget) +{ + u32 retval = 0; + + /* Enable irq for tx/rx ring logical group devices */ + oak_unimac_ena_tx_ring_irq(ldg->device, ring, 0); + oak_unimac_ena_rx_ring_irq(ldg->device, ring, 0); + + /* Check for IRQ reason and call the respective function + * i.e rx work or tx or rx error function. + */ + if ((reason & OAK_INTR_MASK_RX_DMA) != 0) + retval = oak_net_rx_work(ldg, ring, budget); + + if ((reason & OAK_INTR_MASK_RX_ERR) != 0) + oak_unimac_rx_error(ldg, ring); + + if ((reason & OAK_INTR_MASK_TX_ERR) != 0) + oak_unimac_tx_error(ldg, ring); + + return retval; +} + +/* Name : oak_net_process_channel + * Returns : int + * Parameters : ldg_t *ldg, u32 ring, u32 reason, int budget + * Description : This function process the interrupt for tx/rx channel. + */ +static u32 oak_net_process_channel(ldg_t *ldg, u32 ring, u32 reason, + int budget) +{ + u16 qidx = ring; + u32 work_done; + + work_done = oak_net_check_irq_mask(ldg, ring, reason, budget); + /* Update the remaining budget */ + budget = budget - work_done; + + /* If the transmit DMA interrupt from any of the ring 0-9 then + * - we start the transmit work + * - Test status of sub queue i.e. Check individual transmit queue of + * a device. Then allow sending packets on subqueue. + */ + if ((reason & OAK_INTR_MASK_TX_DMA) != 0) { + work_done += oak_net_tx_work(ldg, ring, budget); + if (ldg->device->level < 45 && + __netif_subqueue_stopped(ldg->device->netdev, qidx) != 0) { + oak_tx_chan_t *txc = &ldg->device->tx_channel[ring]; + /* Allow sending packets on subqueue */ + netif_wake_subqueue(ldg->device->netdev, qidx); + oakdbg(debug, TX_QUEUED, "Wake Queue:%d pend=%d", ring, + atomic_read(&txc->tbr_pend)); + } + } + + + oakdbg(debug, PROBE, "chan=%i reason=0x%x work_done=%d", ring, reason, + work_done); + + oak_unimac_ena_tx_ring_irq(ldg->device, ring, 1); + oak_unimac_ena_rx_ring_irq(ldg->device, ring, 1); + + return work_done; +} + +/* Name : poll + * Returns : int + * Parameters: struct napi_struct * napi, int budget + * Description : This function handles the napi bottom half after isr. + */ +static int oak_net_poll(struct napi_struct *napi, int budget) +{ + ldg_t *ldg = container_of(napi, ldg_t, napi); + oak_t *np = ldg->device; + int work_done; + + work_done = oak_net_poll_core(np, ldg, budget); + + /* If work_done is less than budget then remove from the poll list + * and enable the IRQ + */ + if (work_done < budget) { + napi_complete(napi); + oak_irq_enable_gicu_64(np, ldg->irq_mask); + oak_unimac_io_write_32(np, OAK_GICU_INTR_GRP_CLR_MASK, + ldg->msi_grp | + OAK_GICU_INTR_GRP_MASK_ENABLE); + } + + return work_done; +} + +/* Name : oak_net_check_irq + * Returns : u64 + * Parameters : oak_t *np, ldg_t *ldg + * Description : This function checks IRQ type + */ +static u64 oak_net_check_irq(oak_t *np, ldg_t *ldg) +{ + u64 irq_mask = 0; + u32 mask_0; + u32 mask_1; + + /* Read 64 bit mask register lower 32 bit stored in mask_0 and + * upper 32 bit stored in mask_1 + */ + mask_0 = oak_unimac_io_read_32(np, OAK_GICU_INTR_FLAG_0); + mask_1 = oak_unimac_io_read_32(np, OAK_GICU_INTR_FLAG_1); + + /* Concatenate mask_0 and mask_1. Store into irq_mask variable */ + irq_mask = mask_1; + irq_mask <<= 32; + irq_mask |= mask_0; + irq_mask &= ldg->irq_mask; + + /* Check the reason for IRQ */ + if ((mask_1 & OAK_GICU_HOST_UNIMAC_P11_IRQ) != 0) { + oak_unimac_process_status(ldg); + oakdbg(debug, INTR, "UNIMAC P11 IRQ"); + } + + if ((mask_1 & OAK_GICU_HOST_UNIMAC_P11_RESET) != 0) + oakdbg(debug, INTR, "UNIMAC P11 RST"); + if ((mask_1 & OAK_GICU_HOST_MASK_E) != 0) + oakdbg(debug, INTR, "OTHER IRQ"); + + return irq_mask; +} + +/* Name : oak_net_poll_core + * Returns : int + * Parameters : oak_t *np, ldg_t *ldg, int budget + * Description : This function implement net poll functionality + */ +static int oak_net_poll_core(oak_t *np, ldg_t *ldg, int budget) +{ + u64 irq_mask = 0; + int work_done = 0; + int todo; + u64 ring; + u32 irq_count; + u32 irq_reason; + u64 irq_next; + + /* This function called from oak_net_poll to handle the napi bottom + * half after isr. We check for the IRQ mask status here and if its set + * then we do the following + * -Process channel rings till IRQ count, mask bits become less + * than zero. + * - Maintain a work_done counter to check the status on each channel. + */ + irq_mask = oak_net_check_irq(np, ldg); + if (irq_mask != 0) { + u32 max_bits = sizeof(irq_mask) * 8; + + irq_next = (1UL << ldg->irq_first); + irq_count = ldg->irq_count; + todo = budget; + + while (irq_count > 0 && max_bits > 0) { + if ((irq_mask & irq_next) != 0) { + /* oak_ilog2_kernel_utility calls internally + * ilog2 kernel function. The ilog2 function + * returns log of base 2 of 32-bit or a 64-bit + * unsigned value. This can be used to + * initialise global variables from constant + * data. + */ + ring = oak_ilog2_kernel_utility(irq_next); + ring = ring / 4; + irq_reason = (u32)(irq_next >> (ring * 4)); + work_done += oak_net_process_channel(ldg, ring, + irq_reason, + todo); + irq_count -= 1; + } + irq_next <<= 1; + max_bits -= 1; + } + } + + return work_done; +} + +/* Name : oak_net_stop_tx_queue + * Returns : int + * Parameters: oak_t * np, u32 nfrags, u16 txq + * Desciption : This function stops sending pkt to a queue if + * sufficient descriptors are not available. + */ +static netdev_tx_t oak_net_stop_tx_queue(oak_t *np, u32 nfrags, u16 txq) +{ + oak_tx_chan_t *txc = &np->tx_channel[txq]; + u32 free_desc; + netdev_tx_t rc; + + free_desc = atomic_read(&txc->tbr_pend); + free_desc = txc->tbr_size - free_desc; + + if (free_desc <= nfrags) { + netif_stop_subqueue(np->netdev, txq); + ++txc->stat.tx_stall_count; + + oakdbg(debug, TX_QUEUED, "Stop Queue:%d pend=%d", + txq, atomic_read(&txc->tbr_pend)); + + rc = NETDEV_TX_BUSY; + } else { + rc = NETDEV_TX_OK; + } + return rc; +} + +/* Name : oak_net_set_txd_first + * Returns : void + * Parameters : oak_tx_chan_t *txc, u16 len, u32 g3, u32 g4, + * dma_addr_t map, u32 sz, int flags + * Description : This function sets the first transmit descriptor + */ +void oak_net_set_txd_first(oak_tx_chan_t *txc, u16 len, u32 g3, + u32 g4, dma_addr_t map, u32 sz, u32 flags) +{ + oak_txd_t *txd; + oak_txi_t *tbi; + + /* Initialize tx descriptor and tx buffer index */ + txd = &txc->tbr[txc->tbr_widx]; + tbi = &txc->tbi[txc->tbr_widx]; + + /* Reset transmit descriptor structure members */ + txd->bc = len; + txd->res1 = 0; + txd->last = 0; + txd->first = 1; + txd->gl3_chksum = g3; + txd->gl4_chksum = g4; + txd->res2 = 0; + txd->time_valid = 0; + txd->res3 = 0; + txd->buf_ptr_lo = (map & 0xFFFFFFFFU); + +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + txd->buf_ptr_hi = (map >> 32); +#else + txd->buf_ptr_hi = 0; +#endif + /* Reset transmit buffer index structure members */ + tbi->skb = NULL; + tbi->page = NULL; + tbi->mapping = map; + tbi->mapsize = sz; + /* First fragment my be page or single mapped */ + tbi->flags = flags; + ++txc->stat.tx_fragm_count; +} + +/* Name : set_txd_page + * Returns : void + * Parameters: oak_tx_chan_t * txc, u16 len, dma_addr_t map, u32 + * sz, int flags + * Description : This function prepares the tx descriptor + */ +void oak_net_set_txd_page(oak_tx_chan_t *txc, u16 len, dma_addr_t map, + u32 sz, u32 flags) +{ + oak_txd_t *txd; + oak_txi_t *tbi; + + /* Initialize tx descriptor and tx buffer index */ + txd = &txc->tbr[txc->tbr_widx]; + tbi = &txc->tbi[txc->tbr_widx]; + + /* Reset transmit descriptor structure members */ + txd->bc = len; + txd->res1 = 0; + txd->last = 0; + txd->first = 0; + txd->gl3_chksum = 0; + txd->gl4_chksum = 0; + txd->res2 = 0; + txd->time_valid = 0; + txd->res3 = 0; + txd->buf_ptr_lo = (map & 0xFFFFFFFFU); +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + txd->buf_ptr_hi = (map >> 32); +#else + txd->buf_ptr_hi = 0; +#endif + /* Reset transmit buffer index structure members */ + tbi->skb = NULL; + tbi->page = NULL; + tbi->mapping = map; + tbi->mapsize = sz; + tbi->flags = flags; + ++txc->stat.tx_fragm_count; +} + +/* Name : set_txd_last + * Returns : void + * Parameters: oak_tx_chan_t * txc, struct sk_buff * skb, struct page * page + * Description : This function updates last tx descriptor + */ +void oak_net_set_txd_last(oak_tx_chan_t *txc, struct sk_buff *skb, + struct page *page) +{ + txc->tbr[txc->tbr_widx].last = 1; + txc->tbi[txc->tbr_widx].skb = skb; + txc->tbi[txc->tbr_widx].page = page; + txc->tbi[txc->tbr_widx].flags |= TX_BUFF_INFO_EOP; + ++txc->stat.tx_fragm_count; +} + +/* Name : oak_pcie_get_width_cap + * Returns : pcie_link_width + * Parameters: struct pci_dev * pdev + * Description : pcie_get_width_cap() API is not available in few platforms, + * so added a function for the same. + */ +enum pcie_link_width oak_net_pcie_get_width_cap(struct pci_dev *pdev) +{ + u32 lnkcap; + enum pcie_link_width wdth; + + pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &lnkcap); + if (lnkcap) + wdth = (lnkcap & PCI_EXP_LNKCAP_MLW) >> 4; + else + wdth = PCIE_LNK_WIDTH_UNKNOWN; + + return wdth; +} diff --git a/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_net.h b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_net.h new file mode 100644 index 00000000..724ceb36 --- /dev/null +++ b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_net.h @@ -0,0 +1,194 @@ +/* + * + * If you received this File from Marvell, you may opt to use, redistribute and/or + * modify this File in accordance with the terms and conditions of the General + * Public License Version 2, June 1991 (the "GPL License"), a copy of which is + * available along with the File in the license.txt file or by writing to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + * on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + * DISCLAIMED. The GPL License provides additional details about this warranty + * disclaimer. + * + */ + +#ifndef H_OAK_NET +#define H_OAK_NET + +/* Include for relation to classifier oak_unimac */ +#include "oak_unimac.h" +#ifdef CMDTOOL +/* Include for relation to classifier oak_ctl */ +#include "oak_ctl.h" +#endif +/* Include for relation to classifier linux/ip.h */ +#include "linux/ip.h" +/* Include for relation to classifier linux/ipv6.h */ +#include "linux/ipv6.h" +/* Include for relation to classifier linux/if_vlan.h */ +#include "linux/if_vlan.h" + +#define OAK_ONEBYTE 1 + +extern u32 rxs; +extern u32 txs; +extern int chan; +extern int rto; +extern int mhdr; +extern u32 port_speed; +extern int napi_wt; + +/* Name : esu_set_mtu + * Returns : int + * Parameters: struct net_device * net_dev = net_dev, int new_mtu = new_mtu + * Description: This function set the MTU size of the Ethernet interface. + */ +int oak_net_esu_set_mtu(struct net_device *net_dev, int new_mtu); + +/* Name : oak_set_mtu_config + * Returns : void + * Parameters: struct net_device *netdev + * Description: This function sets the min and max MTU size in the linux netdev. + */ +void oak_set_mtu_config(struct net_device *netdev); + +/* Name : esu_ena_speed + * Returns : void + * Parameters: int gbit = gbit, oak_t * np = np + */ +void oak_net_esu_ena_speed(u32 gbit, oak_t *np); + +/* Name : oak_net_open + * Returns : int + * Parameters : struct net_device * net_dev + * Description : This function initialize the interface + */ +int oak_net_open(struct net_device *net_dev); + +/* Name : oak_net_close + * Returns : int + * Parameters : struct net_device *net_dev + * Description : This function close the interface + */ +int oak_net_close(struct net_device *net_dev); + +/* Name : oak_net_ioctl + * Returns : int + * Parameters : struct net_device *net_dev, struct ifreq *ifr, int cmd + * Description : This function handles IOCTL request + */ +int oak_net_ioctl(struct net_device *net_dev, struct ifreq *ifr, int cmd); + +/* Name : add_napi + * Returns : void + * Parameters: struct net_device * netdev + */ +void oak_net_add_napi(struct net_device *netdev); + +/* Name : del_napi + * Returns : void + * Parameters: struct net_device * netdev + */ +void oak_net_del_napi(struct net_device *netdev); + +/* Name : set_mac_addr + * Returns : int + * Parameters: struct net_device * dev = dev, void * p_addr = addr + */ +int oak_net_set_mac_addr(struct net_device *dev, void *p_addr); + +/* Name : alloc_page + * Returns : struct page * + * Parameters : oak_t *np, dma_addr_t * dma, nt direction + * Description : This function allocate page + */ +struct page *oak_net_alloc_page(oak_t *np, dma_addr_t *dma, + enum dma_data_direction dir); + +/* Name : oak_net_select_queue + * Returns : u16 + * Parameters : struct net_device *dev, struct sk_buff *skb, + * struct net_device *sb_dev + * Description : This function pre-seed the SKB by recording the RX queue + */ +u16 oak_net_select_queue(struct net_device *dev, struct sk_buff *skb, + struct net_device *sb_dev); + +/* Name : xmit_frame + * Returns : int + * Parameters: struct sk_buff * skb, struct net_device * net_dev + */ +netdev_tx_t oak_net_xmit_frame(struct sk_buff *skb, struct net_device *net_dev); + +/* Name : start_all + * Returns : int + * Parameters: oak_t * np = np + */ +int oak_net_start_all(oak_t *np); + +/* Name : stop_all + * Returns : void + * Parameters: oak_t * np = np + */ +void oak_net_stop_all(oak_t *np); + +/* Name : tbr_free + * Returns : void + * Parameters: oak_tx_chan_t * txp = txp + */ +void oak_net_tbr_free(oak_tx_chan_t *txp); + +/* Name : oak_net_rbr_free + * Returns : void + * Parameters : oak_rx_chan_t *rxp = rxp + * Description : This function free the receive buffer ring + */ +void oak_net_rbr_free(oak_rx_chan_t *rxp); + +/* Name : add_txd_length + * Returns : void + * Parameters: oak_tx_chan_t * txc, u16 len + */ +void oak_net_add_txd_length(oak_tx_chan_t *txc, u16 len); + +/* Name : oak_net_set_txd_first + * Returns : void + * Parameters : oak_tx_chan_t *txc, u16 len, u32 g3, u32 g4, + * dma_addr_t map, u32 sz, int flags + * Description : This function set the transmit descriptor + */ +void oak_net_set_txd_first(oak_tx_chan_t *txc, u16 len, u32 g3, + u32 g4, dma_addr_t map, u32 sz, u32 flags); + +/* Name : set_txd_page + * Returns : void + * Parameters: oak_tx_chan_t * txc, u16 len, dma_addr_t map, u32 + * sz, int flags + */ +void oak_net_set_txd_page(oak_tx_chan_t *txc, u16 len, dma_addr_t map, + u32 sz, u32 flags); + +/* Name : set_txd_last + * Returns : void + * Parameters: oak_tx_chan_t * txc, struct sk_buff * skb, struct page * page + */ +void oak_net_set_txd_last(oak_tx_chan_t *txc, struct sk_buff *skb, + struct page *page); + +/* Name : oak_net_pcie_get_width_cap + * Returns : enum pcie_link_width + * Parameters: struct pci_dev * + */ +enum pcie_link_width oak_net_pcie_get_width_cap(struct pci_dev *dev); + +/* Name : oak_net_skb_tx_protocol_type + * Returns : int + * Parameters : struct sk_buff *skb + * Description : This function returns the transmit frames protocol type for + * deciding the checksum offload configuration. + */ +int oak_net_skb_tx_protocol_type(struct sk_buff *skb); + +#endif /* #ifndef H_OAK_NET */ + diff --git a/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_unimac.c b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_unimac.c new file mode 100644 index 00000000..fb9c4e54 --- /dev/null +++ b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_unimac.c @@ -0,0 +1,1212 @@ +/* + * + * If you received this File from Marvell, you may opt to use, redistribute and/or + * modify this File in accordance with the terms and conditions of the General + * Public License Version 2, June 1991 (the "GPL License"), a copy of which is + * available along with the File in the license.txt file or by writing to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + * on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + * DISCLAIMED. The GPL License provides additional details about this warranty + * disclaimer. + * + */ +#include "oak_unimac.h" + +/* private function prototypes */ +static void oak_unimac_set_channel_dma(oak_t *np, int rto, u32 rxs, u32 txs, + int chan); +static u32 oak_unimac_ena_ring(oak_t *np, u32 addr, u32 enable); +static void oak_unimac_set_arbit(oak_t *np, u32 ring, + u32 weight, u32 reg); +static void oak_unimac_set_dma_addr(oak_t *np, dma_addr_t phys, u32 reg_lo, + u32 reg_hi); + +/* Name : set_arbit_priority_based + * Returns : void + * Parameters : oak_t *np, u32 ring, u32 prio, u32 reg + * Description : This function set Tx/Rx ring priority + */ +static void oak_unimac_set_arbit_priority(oak_t *np, u32 ring, + u32 prio, u32 reg) +{ + u32 val; + + if (ring <= 9) { + /* For the provided ring number do the following + * -Read the register content into a variable + * -Set the 11th bit OR with the read value from register + * -Write the value into register. + */ + val = oak_unimac_io_read_32(np, reg); + val |= BIT(11); + + oak_unimac_io_write_32(np, reg, val); + + /* If the reg is DMA RX Channel Configuration register then + * -Set DMA RX Channel Arbiter Low register + * -Else set DMA TX Channel Arbiter Low register + * of the B0 version board. + */ + if (reg == OAK_UNI_DMA_RX_CH_CFG) + oak_unimac_set_arbit(np, ring, prio, + OAK_UNI_DMA_RX_CH_ARBIT_B0_LO); + else + oak_unimac_set_arbit(np, ring, prio, + OAK_UNI_DMA_TX_CH_ARBIT_B0_LO); + } +} + +/* Name : oak_unimac_disable_and_get_tx_irq_reason + * Returns : u32 + * Parameters : oak_t *np, u32 ring, u32 *dma_ptr + * Description : This function check the reason for Tx IRQ + */ +u32 oak_unimac_disable_and_get_tx_irq_reason(oak_t *np, u32 ring, + u32 *dma_ptr) +{ + oak_tx_chan_t *txc = &np->tx_channel[ring]; + u32 rc = 0; + + oak_unimac_ena_rx_ring_irq(np, ring, 0); + rc = le32_to_cpup((__le32 *)&txc->mbox->intr_cause); + + /* The possible reasons for the tx IRQ are + * + * OAK_MBOX_TX_COMP - Ring Process Completion. This interrupt is set + * when: + * -The number of completed TX descriptors is equal to the + * TX_RING_MAILBOX_WR_THR, or + * -The number of completed descriptors is less than + * TX_RING_MAILBOX_WR_THR after a certain time as specified in + * the TX_RING_TIMEOUT. + * + * OAK_MBOX_TX_LATE_TS - TX Late Time Stamp. + * This bit gets set when: At the time the start of frame is picked + * from the TX Memory for transmission, the hardware timer has already + * surpassed the "TimeStamp" value in the descriptor, and the delta is + * larger than the register value defined in TX_RING_MAX_LATE_TS. + * Only valid for Ring 2 to 9. + * + * OAK_MBOX_TX_ERR_HCRED - High Credit Limit Exceeded. Only valid for + * ring 2 to 9. + */ + oak_unimac_io_write_32(np, OAK_UNI_TX_RING_INT_CAUSE(ring), + (OAK_MBOX_TX_COMP | OAK_MBOX_TX_LATE_TS | + OAK_MBOX_TX_ERR_HCRED)); + *dma_ptr = le32_to_cpup((__le32 *)&txc->mbox->dma_ptr_rel); + + return rc; +} + +/* Name : oak_unimac_alloc_memory_tx + * Returns : int + * Parameters : oak_t *np, oak_tx_chan_t *txc, max_tx_size + * Description : This function allocate memory for tx channels + */ +static int oak_unimac_alloc_memory_tx(oak_t *np, oak_tx_chan_t *txc, + int max_tx_size) +{ + int retval = 0; + + if (!txc->tbr) { + txc->tbr_size = max_tx_size; + /* dma_alloc_coherent routine allocates a region of + * bytes of consistent memory. + * Consistent memory is memory for which a write by either the + * device or the processor can immediately be read by the + * processor or device without having to worry about caching + * effects. (You may however need to make sure to flush the + * processor's write buffers before telling devices to read + * that memory.) + */ + txc->tbr = dma_alloc_coherent(np->device, txc->tbr_size * + sizeof(oak_txd_t), &txc->tbr_dma, + GFP_KERNEL); + + if ((txc->tbr_dma & OAK_DMA_15) != 0) + retval = -EFAULT; + } + + if (retval == 0 && !txc->mbox) { + txc->mbox_size = 1; + /* dma_alloc_coherent() is used for data transfer between IO + * device and system memory. You allocate a kernel buffer for + * DMA transaction (in this case dma from device to system + * memory) and setup your device for dma transfer. + * An IO device usually generates an interrupt when DMA + * transfer completes. + */ + txc->mbox = dma_alloc_coherent(np->device, sizeof(oak_mbox_t), + &txc->mbox_dma, GFP_KERNEL); + + if ((txc->mbox_dma & OAK_DMA_7) != 0) + retval = -EFAULT; + } + + if (retval == 0 && !txc->tbi) + /* Get Free Pages from linux kernel and allocate memory. + * The memory is set to zero. + */ + txc->tbi = kzalloc(txc->tbr_size * sizeof(oak_txi_t), + GFP_KERNEL); + + if (retval == 0 && (!txc->tbr || !txc->mbox || !txc->tbi)) + retval = -ENOMEM; + + return retval; +} + +/* Name : oak_unimac_alloc_memory_rx + * Returns : int + * Parameters : oak_t *np, oak_rx_chan_t *rxc, max_rx_size + * Description : This function allocate memory for rx channels + */ +static int oak_unimac_alloc_memory_rx(oak_t *np, oak_rx_chan_t *rxc, + u32 max_rx_size) +{ + int retval = 0; + + if (rxc->rbr_bpage < 1) + retval = -EFAULT; + + if (retval == 0 && !rxc->rbr) { + /* Allocates a region of rbr_size bytes of consistent memory + * for rx channel rbr + */ + rxc->rbr_size = max_rx_size; + rxc->rbr = dma_alloc_coherent(np->device, rxc->rbr_size * + sizeof(oak_rxd_t), &rxc->rbr_dma, + GFP_KERNEL); + + if ((rxc->rbr_dma & OAK_DMA_7) != 0) + retval = -EFAULT; + } + + if (retval == 0 && !rxc->rsr) { + /* Allocates a region of rsr_size bytes of consistent memory + * for rx channel rsr + */ + rxc->rsr_size = max_rx_size; + rxc->rsr = dma_alloc_coherent(np->device, rxc->rsr_size * + sizeof(oak_rxs_t), &rxc->rsr_dma, + GFP_KERNEL); + if ((rxc->rsr_dma & OAK_DMA_15) != 0) + retval = -EFAULT; + } + + if (retval == 0 && !rxc->mbox) { + /* Allocates a region of mbox_size bytes of consistent memory + * for rx channel mbox + */ + rxc->mbox_size = 1; + rxc->mbox = dma_alloc_coherent(np->device, sizeof(oak_mbox_t), + &rxc->mbox_dma, GFP_KERNEL); + + if ((rxc->mbox_dma & OAK_DMA_7) != 0) + retval = -EFAULT; + } + + if (retval == 0 && !rxc->rba) + /* Get Free Pages from linux kernel and allocate memory. + * The memory is set to zero. + */ + rxc->rba = kzalloc(rxc->rbr_size * sizeof(oak_rxa_t), + GFP_KERNEL); + if (retval == 0 && (!rxc->rbr || !rxc->rsr || !rxc->mbox || !rxc->rba)) + retval = -ENOMEM; + + return retval; +} + +/* Name : oak_unimac_alloc_channels + * Returns : int + * Parameters : oak_t *np, int rxs, int txs, int chan, int rto + * Description : This function allocate tx and rx channels + */ +int oak_unimac_alloc_channels(oak_t *np, u32 rxs, u32 txs, int chan, int rto) +{ + oak_rx_chan_t *rxc; + oak_tx_chan_t *txc; + int i; + u32 max_rx_size; + u32 max_tx_size; + int max_channel; + int retval = 0; + + if (rxs < OAK_BUFFER_SIZE_16 || rxs > OAK_BUFFER_SIZE_2048) + rxs = 0; + else + rxs = oak_ilog2_kernel_utility(rxs) - 4; + + if (txs < OAK_BUFFER_SIZE_16 || txs > OAK_BUFFER_SIZE_2048) + txs = 0; + else + txs = oak_ilog2_kernel_utility(txs) - 4; + + max_rx_size = XBR_RING_SIZE(rxs); + max_tx_size = XBR_RING_SIZE(txs); + + if (chan >= MIN_NUM_OF_CHANNELS && chan <= MAX_NUM_OF_CHANNELS) { + max_channel = chan; + } else { + retval = -EFAULT; + max_channel = 0; + } + + np->num_rx_chan = 0; + np->num_tx_chan = 0; + i = 0; + + while (retval == 0 && i < max_channel) { + rxc = &np->rx_channel[i]; + /* set backpointer */ + rxc->oak = np; + /* reset flags */ + rxc->flags = 0; + atomic_set(&rxc->rbr_pend, 0); + /* software write buffer index */ + rxc->rbr_widx = 0; + /* software read buffer index */ + rxc->rbr_ridx = 0; + rxc->skb = NULL; + rxc->rbr_bsize = OAK_RX_BUFFER_SIZE; + if (rxc->rbr_bsize > 0) + rxc->rbr_bpage = (PAGE_SIZE / rxc->rbr_bsize); + + retval = oak_unimac_alloc_memory_rx(np, rxc, max_rx_size); + + if (retval == 0) { + ++np->num_rx_chan; + ++i; + } + } + + i = 0; + while (retval == 0 && i < max_channel) { + txc = &np->tx_channel[i]; + /* set backpointer */ + txc->oak = np; + /* reset flags */ + txc->flags = 0; + /* number of pending buffers ready 2b processed by hardware */ + txc->tbr_count = 0; + atomic_set(&txc->tbr_pend, 0); + /* software write buffer index */ + txc->tbr_widx = 0; + /* software read buffer index */ + txc->tbr_ridx = 0; + + retval = oak_unimac_alloc_memory_tx(np, txc, max_tx_size); + + if (retval == 0) { + ++np->num_tx_chan; + ++i; + } + } + + if (retval == 0) + oak_unimac_set_channel_dma(np, rto, rxs, txs, chan); + else + oak_unimac_free_channels(np); + + return retval; +} + +/* Name : oak_unimac_free_tx_channels + * Returns : void + * Parameters : oak_t *np + * Description : This function free unimac tx channels + */ +static void oak_unimac_free_tx_channels(oak_t *np) +{ + u32 num_tx_chan = np->num_tx_chan; + + /* Free all the allocated tx channels */ + while (num_tx_chan > 0) { + oak_tx_chan_t *chan = &np->tx_channel[num_tx_chan - 1]; + + if (chan->tbr) { + /* Free a region of tbr consistent memory */ + dma_free_coherent(np->device, chan->tbr_size * + sizeof(oak_txd_t), chan->tbr, + chan->tbr_dma); + chan->tbr = NULL; + } + if (chan->mbox) { + /* Free a region of mbox consistent memory */ + dma_free_coherent(np->device, sizeof(oak_mbox_t), + chan->mbox, chan->mbox_dma); + chan->mbox = NULL; + } + + /* Free previously allocated memory for tbi */ + kfree(chan->tbi); + chan->tbi = NULL; + + --num_tx_chan; + } +} + +/* Name : oak_unimac_free_rx_channels + * Returns : void + * Parameters : oak_t *np + * Description : This function free unimac rx channels + */ +static void oak_unimac_free_rx_channels(oak_t *np) +{ + u32 num_rx_chan = np->num_rx_chan; + + /* Free all the allocated rx channels */ + while (num_rx_chan > 0) { + oak_rx_chan_t *chan = &np->rx_channel[num_rx_chan - 1]; + + if (chan->rbr) { + /* Free a region of rbr consistent memory */ + dma_free_coherent(np->device, chan->rbr_size * + sizeof(oak_rxd_t), chan->rbr, + chan->rbr_dma); + chan->rbr = NULL; + } + if (chan->rsr) { + /* Free a region of rsr consistent memory */ + dma_free_coherent(np->device, chan->rsr_size * + sizeof(oak_rxs_t), chan->rsr, + chan->rsr_dma); + chan->rsr = NULL; + } + if (chan->mbox) { + /* Free a region of mbox consistent memory */ + dma_free_coherent(np->device, sizeof(oak_mbox_t), + chan->mbox, chan->mbox_dma); + chan->mbox = NULL; + } + + /* Free previously allocated memory for rba */ + kfree(chan->rba); + chan->rba = NULL; + + --num_rx_chan; + } +} + +/* Name : oak_unimac_free_channels + * Returns : void + * Parameters : oak_t *np + * Description : This function free unimac channels + */ +void oak_unimac_free_channels(oak_t *np) +{ + oakdbg(debug, IFDOWN, + "np=%p num_rx_chan=%d num_tx_chan=%d", np, np->num_rx_chan, + np->num_tx_chan); + oak_unimac_free_rx_channels(np); + oak_unimac_free_tx_channels(np); +} + +/* Name : oak_unimac_reset + * Returns : int + * Parameters : oak_t *np + * Description : This function reset unimac statistics + */ +int oak_unimac_reset(oak_t *np) +{ + u32 val = BIT(31); + u32 cnt = 1000; + int rc = 0; + + /* Set the 31st bit value into AHSI Control register */ + oak_unimac_io_write_32(np, OAK_UNI_CTRL, val); + while ((cnt > 0) && (val & BIT(31))) { + val = oak_unimac_io_read_32(np, OAK_UNI_CTRL); + --cnt; + } + + if (cnt > 0) + /* Reset all statistics counters */ + oak_unimac_reset_statistics(np); + else + rc = -EFAULT; + + return rc; +} + +/* Name : oak_unimac_reset_misc_counters + * Returns : void + * Parameters : oak_t *np + * Description : This function reset good, bad, disc and tx pause counters + */ +static void oak_unimac_reset_misc_counters(oak_t *np) +{ + oak_unimac_io_write_32(np, OAK_UNI_STAT_RX_GOOD_FRAMES, 0); + oak_unimac_io_write_32(np, OAK_UNI_STAT_RX_BAD_FRAMES, 0); + oak_unimac_io_write_32(np, OAK_UNI_STAT_RX_DISC_DESC, 0); + oak_unimac_io_write_32(np, OAK_UNI_STAT_TX_PAUSE, 0); +} + +/* Name : oak_unimac_reset_stall_counters + * Returns : void + * Parameters : oak_t *np + * Description : This function reset the stall desc, fifo statistics counters + */ +static void oak_unimac_reset_stall_counters(oak_t *np) +{ + oak_unimac_io_write_32(np, OAK_UNI_STAT_RX_STALL_DESC, 0); + oak_unimac_io_write_32(np, OAK_UNI_STAT_RX_STALL_FIFO, 0); + oak_unimac_io_write_32(np, OAK_UNI_STAT_TX_STALL_FIFO, 0); +} + +/* Name : oak_unimac_reset_statistics + * Returns : void + * Parameters : oak_t *np + * Description : This function reset the statistics counter + */ +void oak_unimac_reset_statistics(oak_t *np) +{ + u32 i = 0; + + /* Copy tx/rx misc and stall statistics counters */ + oak_unimac_reset_misc_counters(np); + oak_unimac_reset_stall_counters(np); + + while (i < np->num_rx_chan) { + memset(&np->rx_channel[i], 0, sizeof(oak_rx_chan_t)); + ++i; + } + + i = 0; + + while (i < np->num_tx_chan) { + memset(&np->tx_channel[i], 0, sizeof(oak_tx_chan_t)); + ++i; + } +} + +/* Name : oak_unimac_crt_bit_mask + * Returns : u32 + * Parameters : u32 off, u32 len, u32 val, u32 bit_mask + * Description : This function calculate the crt bit mask + */ +u32 oak_unimac_crt_bit_mask(u32 off, u32 len, u32 val, + u32 bit_mask) +{ + u32 ret_7 = 0; + u32 mask = 0; + u32 sz = sizeof(val) * 8; + + if ((len + off) >= sz) + len = sz - off; + mask = ((1U << len) - 1) << off; + val = val & ~mask; + val = val | ((bit_mask << off) & mask); + ret_7 = val; + + return ret_7; +} + +/* Name : oak_unimac_io_read_32 + * Returns : u32 + * Parameters : oak_t * np, u32 addr + * Description : This function read from register. + */ +u32 oak_unimac_io_read_32(oak_t *np, u32 addr) +{ + u32 val = readl(np->um_base + addr); + return val; +} + +/* Name : oak_unimac_io_write_32 + * Returns : void + * Parameters : oak_t *np, u32 addr, u32 val + * Description : This function write value to register. + */ +void oak_unimac_io_write_32(oak_t *np, u32 addr, u32 val) +{ + writel((val), np->um_base + (addr)); +} + +/* Name : oak_unimac_set_bit_num + * Returns : void + * Parameters : oak_t *np, u32 reg, u32 bit_num, int enable + * Description : This function set a bit number + */ +void oak_unimac_set_bit_num(oak_t *np, u32 reg, u32 bit_num, + u32 enable) +{ + u32 val = oak_unimac_io_read_32(np, reg); + + if (enable != 0) + val |= (1U << bit_num); + else + val &= ~(1U << bit_num); + oak_unimac_io_write_32(np, reg, val); +} + +/* Name : oak_unimac_set_rx_none + * Returns : void + * Parameters : oak_t *np, u32 ring + * Description : This function clear the rx ring + */ +void oak_unimac_set_rx_none(oak_t *np, u32 ring) +{ + oak_unimac_io_write_32(np, OAK_UNI_RX_RING_MAP(ring), 0U); + oakdbg(debug, DRV, "clear np=%p chan=%d", np, ring); +} + +/* Name : oak_unimac_set_rx_8021Q_et + * Returns : void + * Parameters : oak_t *np, u32 ring, u16 etype, u16 pcp_vid, + * int enable + * Description : This function write ethtype and calls set bit number for + * Rx ring + */ +void oak_unimac_set_rx_8021Q_et(oak_t *np, u32 ring, u16 etype, + u16 pcp_vid, u32 enable) +{ + u32 val_1; + + if (enable != 0) { + val_1 = (u32)(etype) << 16 | pcp_vid; + oak_unimac_io_write_32(np, OAK_UNI_RX_RING_ETYPE(ring), val_1); + } + oak_unimac_set_bit_num(np, OAK_UNI_RX_RING_MAP(ring), 19U, enable); + oakdbg(debug, DRV, "np=%p chan=%d etype=0x%x vid=0x%x enable=%d", + np, ring, etype, pcp_vid, enable); +} + +/* Name : oak_unimac_set_rx_8021Q_fid + * Returns : void + * Parameters : oak_t *np, u32 ring, u32 fid, int enable + * Description : This function set Rx ring Filtering database identifier (FID) + */ +void oak_unimac_set_rx_8021Q_fid(oak_t *np, u32 ring, u32 fid, + u32 enable) +{ + u32 val = oak_unimac_io_read_32(np, OAK_UNI_RX_RING_MAP(ring)); + + val = oak_unimac_crt_bit_mask(21, 3, val, fid); + + if (enable != 0) + val |= BIT(20); + else + val &= ~(BIT(20)); + + oak_unimac_io_write_32(np, OAK_UNI_RX_RING_MAP(ring), val); + oakdbg(debug, DRV, "np=%p chan=%d fid=0x%x enable=%d", + np, ring, fid, enable); +} + +/* Name : oak_unimac_set_rx_8021Q_flow + * Returns : void + * Parameters : oak_t *np, u32 ring, u32 flow_id, int enable + * Description : This function set Rx ring flow identifier + */ +void oak_unimac_set_rx_8021Q_flow(oak_t *np, u32 ring, u32 flow_id, + u32 enable) +{ + u32 val = oak_unimac_io_read_32(np, OAK_UNI_RX_RING_MAP(ring)); + + val = oak_unimac_crt_bit_mask(14, 4, val, flow_id); + + if (enable != 0) + val |= BIT(12); + else + val &= ~(BIT(12)); + oak_unimac_io_write_32(np, OAK_UNI_RX_RING_MAP(ring), val); + oakdbg(debug, DRV, "np=%p chan=%d flow_id=%d enable=%d", + np, ring, flow_id, enable); +} + +/* Name : oak_unimac_set_rx_8021Q_qpri + * Returns : void + * Parameters : oak_t *np, u32 ring, u32 qpri, int enable + * Description : This function set Rx ring queue priority. + */ +void oak_unimac_set_rx_8021Q_qpri(oak_t *np, u32 ring, u32 qpri, + u32 enable) +{ + u32 val = oak_unimac_io_read_32(np, OAK_UNI_RX_RING_MAP(ring)); + + val = oak_unimac_crt_bit_mask(4, 3, val, qpri); + + if (enable != 0) + val |= BIT(3); + else + val &= ~(BIT(3)); + + oak_unimac_io_write_32(np, OAK_UNI_RX_RING_MAP(ring), val); + oakdbg(debug, DRV, "np=%p chan=%d qpri=%d enable=%d", + np, ring, qpri, enable); +} + +/* Name : oak_unimac_set_rx_8021Q_spid + * Returns : void + * Parameters : oak_t *np, u32 ring, u32 spid, int enable + * Description : This function set Rx ring speed identifier (SPID). + */ +void oak_unimac_set_rx_8021Q_spid(oak_t *np, u32 ring, u32 spid, + u32 enable) +{ + u32 val = oak_unimac_io_read_32(np, OAK_UNI_RX_RING_MAP(ring)); + + val = oak_unimac_crt_bit_mask(8, 4, val, spid); + + if (enable != 0) + val |= BIT(7); + else + val &= ~(BIT(7)); + + oak_unimac_io_write_32(np, OAK_UNI_RX_RING_MAP(ring), val); + oakdbg(debug, DRV, "np=%p chan=%d spid=0x%x enable=%d", + np, ring, spid, enable); +} + +/* Name : oak_unimac_set_rx_da + * Returns : void + * Parameters : oak_t *np, u32 ring, unsigned char *addr, int enable + * Description : This function set MAC address to rx ring device address + * register, then set the rx ring map bit. + */ +void oak_unimac_set_rx_da(oak_t *np, u32 ring, unsigned char *addr, + u32 enable) +{ + u32 val_1; + u32 val_4; + + if (enable != 0) { + val_4 = (addr[2] << 0) | ((u32)addr[3] << 8) | + ((u32)addr[4] << 16) | ((u32)addr[5] << 24); + oak_unimac_io_write_32(np, OAK_UNI_RX_RING_DADDR_HI(ring), + val_4); + val_1 = (addr[0] << 0) | ((u32)addr[1] << 8); + oak_unimac_io_write_32(np, OAK_UNI_RX_RING_DADDR_LO(ring), + val_1); + } + + oak_unimac_set_bit_num(np, OAK_UNI_RX_RING_MAP(ring), 18, enable); + + oakdbg(debug, DRV, + "np=%p chan=%d addr=0x%02x%02x%02x%02x%02x%02x enable=%d", + np, ring, addr[0] & 0xFFU, addr[1] & 0xFFU, addr[2] & 0xFFU, + addr[3] & 0xFFU, addr[4] & 0xFFU, addr[5] & 0xFFU, enable); +} + +/* Name : oak_unimac_set_rx_da_mask + * Returns : void + * Parameters : oak_t *np, u32 ring, unsigned char *addr, int enable + * Description : This function set mask to rx ring device address register. + */ +void oak_unimac_set_rx_da_mask(oak_t *np, u32 ring, unsigned char *addr, + int enable) +{ + u32 val_1; + u32 val_4; + + if (enable != 0) { + val_1 = (addr[2] << 0) | ((u32)addr[3] << 8) | + ((u32)addr[4] << 16) | ((u32)addr[5] << 24); + oak_unimac_io_write_32(np, OAK_UNI_RX_RING_DADDR_MASK_HI(ring), + val_1); + val_4 = (addr[0] << 0) | ((u32)addr[1] << 8); + oak_unimac_io_write_32(np, OAK_UNI_RX_RING_DADDR_MASK_LO(ring), + val_4); + } else { + oak_unimac_io_write_32(np, OAK_UNI_RX_RING_DADDR_MASK_HI(ring), + 0); + oak_unimac_io_write_32(np, OAK_UNI_RX_RING_DADDR_MASK_LO(ring), + 0); + } + oakdbg(debug, DRV, + "np=%p chan=%d addr=0x%02x%02x%02x%02x%02x%02x enable=%d", + np, ring, addr[0] & 0xFFU, addr[1] & 0xFFU, addr[2] & 0xFFU, + addr[3] & 0xFFU, addr[4] & 0xFFU, addr[5] & 0xFFU, enable); +} + +/* Name : oak_unimac_set_rx_mgmt + * Returns : void + * Parameters : oak_t *np, u32 ring, u32 val, int enable + * Description : This function call set bit number function with + * value and enable options + */ +void oak_unimac_set_rx_mgmt(oak_t *np, u32 ring, u32 val, u32 enable) +{ + oak_unimac_set_bit_num(np, OAK_UNI_RX_RING_MAP(ring), 1, val); + oak_unimac_set_bit_num(np, OAK_UNI_RX_RING_MAP(ring), 0, enable); + oakdbg(debug, DRV, "np=%p chan=%d enable=%d", np, ring, enable); +} + +/* Name : oak_unimac_process_status + * Returns : void + * Parameters : ldg_t *ldg + * Description : This function get the process status + */ +void oak_unimac_process_status(ldg_t *ldg) +{ + u32 irq_reason; + u32 uni_status; + + irq_reason = oak_unimac_io_read_32(ldg->device, OAK_UNI_INTR); + + if ((irq_reason & OAK_UNI_INTR_SEVERE_ERRORS) != 0) + oakdbg(debug, INTR, + "SEVERE unimac irq reason: 0x%x", + (u32)(irq_reason & OAK_UNI_INTR_SEVERE_ERRORS)); + + if ((irq_reason & OAK_UNI_INTR_NORMAL_ERRORS) != 0) + oakdbg(debug, INTR, + "NORMAL unimac irq reason: 0x%x", + (u32)(irq_reason & OAK_UNI_INTR_NORMAL_ERRORS)); + + uni_status = oak_unimac_io_read_32(ldg->device, OAK_UNI_STAT); + oakdbg(debug, INTR, "unimac status: 0x%x", uni_status); + oak_unimac_io_write_32(ldg->device, OAK_UNI_INTR, irq_reason); +} + +/* Name : oak_unimac_rx_error + * Returns : void + * Parameters : ldg_t *ldg, u32 ring + * Description : This function check interrupt cause reason and then if the + * reason is valid refill the receive ring else increment the rx errors + * counters. + */ +void oak_unimac_rx_error(ldg_t *ldg, u32 ring) +{ + oak_t *np = ldg->device; + oak_rx_chan_t *rxc; + u32 reason; + + rxc = &np->rx_channel[ring]; + + reason = le32_to_cpup((__le32 *)&rxc->mbox->intr_cause); + + if ((reason & OAK_MBOX_RX_RES_LOW) != 0) { + oak_net_rbr_refill(np, ring); + } else { + ++rxc->stat.rx_errors; + oakdbg(debug, RX_ERR, "reason=0x%x", reason); + } +} + +/* Name : oak_unimac_tx_error + * Returns : void + * Parameters : ldg_t *ldg, u32 ring + * Description : This function Tx error reason and then count errors + */ +void oak_unimac_tx_error(ldg_t *ldg, u32 ring) +{ + oak_t *np = ldg->device; + oak_tx_chan_t *txc; + u32 reason; + + txc = &np->tx_channel[ring]; + + /* TX Ring Maximum Late Timestamp (Only Valid for Ring 2 to 9) + */ + oak_unimac_io_write_32(np, OAK_UNI_TX_RING_INT_CAUSE(ring), + (OAK_MBOX_TX_LATE_TS | OAK_MBOX_TX_ERR_HCRED)); + reason = le32_to_cpup((__le32 *)&txc->mbox->intr_cause); + + ++txc->stat.tx_errors; + + oakdbg(debug, TX_ERR, "reason=0x%x", reason); +} + +/* Name : oak_unimac_ena_rx_ring_irq + * Returns : void + * Parameters : oak_t *np, u32 ring, u32 enable + * Description : This function enables the Rx ring irq. + */ +void oak_unimac_ena_rx_ring_irq(oak_t *np, u32 ring, u32 enable) +{ + if (enable != 0) + enable = OAK_MBOX_RX_COMP | OAK_MBOX_RX_RES_LOW; + oak_unimac_io_write_32(np, OAK_UNI_RX_RING_INT_MASK(ring), enable); +} + +/* Name : oak_unimac_ena_tx_ring_irq + * Returns : void + * Parameters : oak_t *np, u32 ring, u32 enable + * Description : This function enables the Tx ring irq. + */ +void oak_unimac_ena_tx_ring_irq(oak_t *np, u32 ring, u32 enable) +{ + if (enable != 0) { + enable = OAK_MBOX_TX_COMP; + + if (ring >= 2) + enable |= (OAK_MBOX_TX_LATE_TS | OAK_MBOX_TX_ERR_HCRED); + } + oak_unimac_io_write_32(np, OAK_UNI_TX_RING_INT_MASK(ring), enable); +} + +/* Name : oak_unimac_set_tx_ring_rate + * Returns : int + * Parameters : oak_t *np, u32 ring, u32 sr_class, + * u32 hi_credit, u32 r_kbps + * Description : This function set tx ring rate limit. + */ +int oak_unimac_set_tx_ring_rate(oak_t *np, u32 ring, u32 sr_class, + u32 hi_credit, u32 r_kbps) +{ + int rc = -EFAULT; + u32 val; + + /* tx ring rate limit is only applicable for ring 2 to 9 */ + if (ring >= 2 && ring <= 9) { + if (hi_credit <= OAK_MAX_TX_HI_CREDIT_BYTES) { + val = ((sr_class & 1U) << 31); + val |= ((hi_credit & OAK_MAX_TX_HI_CREDIT_BYTES) << 17); + val |= (r_kbps & 0x1FFFFU); + + oak_unimac_io_write_32(np, + OAK_UNI_TX_RING_RATECTRL(ring), + val); + rc = 0; + } + } + oakdbg(debug, DRV, + "np=%p ring=%d sr_class=%d hi_credit=%d kbps=%d rc=%d", + np, ring, sr_class, hi_credit, r_kbps, rc); + + return rc; +} + +/* Name : oak_unimac_clr_tx_ring_rate + * Returns : void + * Parameters : oak_t *np, u32 ring + * Description : This function clear the tx ring rate limit + */ +void oak_unimac_clr_tx_ring_rate(oak_t *np, u32 ring) +{ + if (ring >= 2 && ring <= 9) { + u32 val = oak_unimac_io_read_32(np, + OAK_UNI_TX_RING_RATECTRL(ring)); + val &= 0x7FFF0000U; + oak_unimac_io_write_32(np, OAK_UNI_TX_RING_RATECTRL(ring), val); + } +} + +/* Name : oak_unimac_set_tx_mac_rate + * Returns : int + * Parameters : oak_t *np, u32 sr_class, u32 hi_credit, + * u32 r_kbps + * Description : This function set transmision mac rate limit. + */ +int oak_unimac_set_tx_mac_rate(oak_t *np, u32 sr_class, u32 hi_credit, + u32 r_kbps) +{ + int rc = -EFAULT; + u32 val; + + /* Check for High Credit Limit for Ring + * Defines the peak credit that can be accumulated while being blocked + * from transmission by another flow. This field is in bytes. + */ + if (hi_credit <= OAK_MAX_TX_HI_CREDIT_BYTES) { + val = ((hi_credit & OAK_MAX_TX_HI_CREDIT_BYTES) << 17); + val |= (r_kbps & 0x1FFFFU); + + /* Check for Stream Reservation Class + * 0x0 = Class A + * 0x1 = Class B + */ + if (sr_class == OAK_MIN_TX_RATE_CLASS_A) + oak_unimac_io_write_32(np, OAK_UNI_TXRATE_A, val); + else + oak_unimac_io_write_32(np, OAK_UNI_TXRATE_B, val); + rc = 0; + } + return rc; +} + +/* Name : oak_unimac_set_priority + * Returns : void + * Parameters : oak_t *np, u32 ring, u32 prio, u32 reg + * Description : This function set the priority + */ +static void oak_unimac_set_priority(oak_t *np, u32 ring, u32 prio, u32 reg) +{ + u32 val; + + if (ring <= 9) { + if (np->pci_class_revision >= 1) { + if (reg == OAK_UNI_DMA_TX_CH_CFG) { + if (np->rrs >= 16) { + val = oak_unimac_io_read_32(np, reg); + /* Max burst size */ + val &= ~0x7FU; + val |= ((np->rrs / 8) - 1) & 0x7FU; + /* RR: bit-10 == 1 */ + val |= BIT(10); + + oak_unimac_io_write_32(np, reg, val); + + oakdbg(debug, DRV, + "TX max burst sz: %d, val=0x%x", + np->rrs, val); + } + oak_unimac_set_arbit(np, ring, prio, + OAK_DMA_TX_CH_SCHED_B0_LO); + } + } else { + val = oak_unimac_io_read_32(np, reg); + val |= (1U << 10); + oak_unimac_io_write_32(np, reg, val); + + if (reg == OAK_UNI_DMA_RX_CH_CFG) + oak_unimac_set_arbit(np, ring, prio, + OAK_DMA_RX_CH_SCHED_LO); + else + oak_unimac_set_arbit(np, ring, prio, + OAK_DMA_TX_CH_SCHED_LO); + } + } +} + +/* Name : oak_unimac_start_all_txq + * Returns : int + * Parameters : oak_t *np, u32 enable + * Description : This function start all transmit queues + */ +int oak_unimac_start_all_txq(oak_t *np, u32 enable) +{ + int rc = 0; + u32 i = 0; + + while (i < np->num_tx_chan) { + rc = oak_unimac_start_tx_ring(np, i, enable); + if (rc) { + rc = 0; + } else { + rc = -EFAULT; + break; + } + ++i; + } + + oakdbg(debug, IFUP, " rc: %d", rc); + + return rc; +} + +/* Name : oak_unimac_start_all_rxq + * Returns : int + * Parameters : oak_t *np, u32 enable + * Description : This function start all receive queues + */ +int oak_unimac_start_all_rxq(oak_t *np, u32 enable) +{ + int rc = 0; + u32 i = 0; + + while (i < np->num_rx_chan) { + rc = oak_unimac_start_rx_ring(np, i, enable); + if (rc) { + rc = 0; + } else { + rc = -EFAULT; + break; + } + ++i; + } + + oakdbg(debug, IFUP, " rc: %d", rc); + + return rc; +} + +/* Name : oak_unimac_set_channel_dma + * Returns : void + * Parameters : oak_t *np, int rto, int rxs, int txs, int chan + * Description : This function set DMA channel + */ +static void oak_unimac_set_channel_dma(oak_t *np, int rto, u32 rxs, u32 txs, + int chan) +{ + u32 i = 0; + u32 val; + + /* Set DMA for all the rx channel */ + while (i < np->num_rx_chan) { + oak_unimac_set_dma_addr(np, np->rx_channel[i].rbr_dma, + OAK_UNI_RX_RING_DBASE_LO(i), + OAK_UNI_RX_RING_DBASE_HI(i)); + oak_unimac_set_dma_addr(np, np->rx_channel[i].rsr_dma, + OAK_UNI_RX_RING_SBASE_LO(i), + OAK_UNI_RX_RING_SBASE_HI(i)); + oak_unimac_set_dma_addr(np, np->rx_channel[i].mbox_dma, + OAK_UNI_RX_RING_MBASE_LO(i), + OAK_UNI_RX_RING_MBASE_HI(i)); + val = (np->rx_channel[i].rbr_bsize & 0xFFF8U) << 16; + val |= rxs; + + oak_unimac_io_write_32(np, OAK_UNI_RX_RING_CFG(i), val); + oak_unimac_io_write_32(np, OAK_UNI_RX_RING_PREF_THR(i), + RX_DESC_PREFETCH_TH); + val = (np->rx_channel[i].rbr_size / 4); + val <<= 16; + + oak_unimac_io_write_32(np, OAK_UNI_RX_RING_WATERMARK(i), val); + val = (np->rx_channel[i].rbr_size / 8); + val = oak_ilog2_kernel_utility(val); + + /* Set WR_THR to 0x5 (32 descriptors), maximum allowed value. + * If the number of completed descriptors is less than this + * value after a certain time as defined in the RX_RING_TIMEOUT, + * hardware will also write to the mailbox/status and trigger + * the interrupt. + */ + if (val > 5) + val = 5; + oak_unimac_io_write_32(np, OAK_UNI_RX_RING_MBOX_THR(i), val); + oak_unimac_io_write_32(np, OAK_UNI_RX_RING_TIMEOUT(i), + OAK_RING_TOUT_USEC(rto)); + + if (np->pci_class_revision >= 1) + oak_unimac_set_arbit_priority(np, i, 0, + OAK_UNI_DMA_RX_CH_CFG); + else + oak_unimac_set_priority(np, i, 0, + OAK_UNI_DMA_RX_CH_CFG); + ++i; + } + + i = 0; + + /* Set DMA for all the tx channel */ + while (i < np->num_tx_chan) { + oak_unimac_set_dma_addr(np, np->tx_channel[i].tbr_dma, + OAK_UNI_TX_RING_DBASE_LO(i), + OAK_UNI_TX_RING_DBASE_HI(i)); + oak_unimac_set_dma_addr(np, np->tx_channel[i].mbox_dma, + OAK_UNI_TX_RING_MBASE_LO(i), + OAK_UNI_TX_RING_MBASE_HI(i)); + + oak_unimac_io_write_32(np, OAK_UNI_TX_RING_CFG(i), txs); + oak_unimac_io_write_32(np, OAK_UNI_TX_RING_PREF_THR(i), + TX_DESC_PREFETCH_TH); + oak_unimac_io_write_32(np, OAK_UNI_TX_RING_MBOX_THR(i), + TX_MBOX_WRITE_TH); + oak_unimac_clr_tx_ring_rate(np, i); + oak_unimac_io_write_32(np, OAK_UNI_TX_RING_TIMEOUT(i), + OAK_RING_TOUT_MSEC(10)); + + if (np->pci_class_revision >= 1) + oak_unimac_set_arbit_priority(np, i, 0, + OAK_UNI_DMA_TX_CH_CFG); + oak_unimac_set_priority(np, i, 0, + OAK_UNI_DMA_TX_CH_CFG); + ++i; + } + /* The below kernel routine help set real number of tx/rx queues. + * To avoid skbs mapped to queues greater than real number of tx/rx + * queues, stale skbs on the qdisc will be flushed in the below routine + */ + netif_set_real_num_tx_queues(np->netdev, np->num_tx_chan); + netif_set_real_num_rx_queues(np->netdev, np->num_rx_chan); +} + +/* Name : oak_unimac_ena_ring + * Returns : u32 + * Parameters : oak_t *np, u32 addr, u32 enable + * Description : This function enables the ring + */ +static u32 oak_unimac_ena_ring(oak_t *np, u32 addr, u32 enable) +{ + u32 result; + u32 count; + + if (enable != 0) + enable = OAK_UNI_RING_ENABLE_REQ | OAK_UNI_RING_ENABLE_DONE; + oak_unimac_io_write_32(np, addr, enable & OAK_UNI_RING_ENABLE_REQ); + count = 1000; + + do { + result = oak_unimac_io_read_32(np, addr); + --count; + + } while (((enable & OAK_UNI_RING_ENABLE_DONE) != + (result & OAK_UNI_RING_ENABLE_DONE)) && (count > 0)); + +#ifdef SIMULATION + count = 1; +#endif + + return count; +} + +/* Name : oak_unimac_set_arbit + * Returns : void + * Parameters : oak_t *np, u32 ring, u32 weight, int reg + * Description : This function set arbitrary value for ring. + */ +static void oak_unimac_set_arbit(oak_t *np, u32 ring, + u32 weight, u32 reg) +{ + u32 val; + + if (ring <= 7) { + ring = ring * 4; + } else { + ring = (ring - 8) * 4; + reg += 4; + } + val = oak_unimac_io_read_32(np, reg); + val &= ~(0xFU << ring); + val |= (weight << ring); + + oak_unimac_io_write_32(np, reg, val); +} + +/* Name : oak_unimac_start_tx_ring + * Returns : u32 + * Parameters : oak_t *np, int32_t ring, u32 enable + * Description : This function start rx ring + */ +u32 oak_unimac_start_tx_ring(oak_t *np, int32_t ring, u32 enable) +{ + if (enable != 0) { + oak_unimac_io_write_32(np, OAK_UNI_TX_RING_INT_CAUSE(ring), + (OAK_MBOX_TX_COMP | OAK_MBOX_TX_LATE_TS | + OAK_MBOX_TX_ERR_HCRED)); + oak_unimac_io_write_32(np, OAK_UNI_TX_RING_CPU_PTR(ring), 0); + } + return oak_unimac_ena_ring(np, OAK_UNI_TX_RING_EN(ring), enable); +} + +/* Name : oak_unimac_start_rx_ring + * Returns : u32 + * Parameters : oak_t *np, u32 ring, u32 enable + * Description : This function start Rx ring + */ +u32 oak_unimac_start_rx_ring(oak_t *np, u32 ring, u32 enable) +{ + if (enable != 0) + oak_unimac_io_write_32(np, OAK_UNI_RX_RING_INT_CAUSE(ring), + (OAK_MBOX_RX_COMP | + OAK_MBOX_RX_RES_LOW)); + return oak_unimac_ena_ring(np, OAK_UNI_RX_RING_EN(ring), enable); +} + +/* Name : oak_unimac_set_dma_addr + * Returns : void + * Parameters : oak_t *np, dma_addr_t phys, u32 reg_lo, u32 reg_hi + * Description : This function set the DMA address + */ +static void oak_unimac_set_dma_addr(oak_t *np, dma_addr_t phys, u32 reg_lo, + u32 reg_hi) +{ + u32 addr; + + /* Low 32 bit */ + addr = (phys & 0xFFFFFFFFU); + oak_unimac_io_write_32(np, reg_lo, addr); +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + /* High 32 bit */ + addr = ((phys >> 32) & 0xFFFFFFFFU); +#else + addr = 0; +#endif + oak_unimac_io_write_32(np, reg_hi, addr); +} diff --git a/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_unimac.h b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_unimac.h new file mode 100644 index 00000000..ec7b5c24 --- /dev/null +++ b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_unimac.h @@ -0,0 +1,713 @@ +/* + * + * If you received this File from Marvell, you may opt to use, redistribute and/or + * modify this File in accordance with the terms and conditions of the General + * Public License Version 2, June 1991 (the "GPL License"), a copy of which is + * available along with the File in the license.txt file or by writing to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + * on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + * DISCLAIMED. The GPL License provides additional details about this warranty + * disclaimer. + * + */ +#ifndef H_OAK_UNIMAC +#define H_OAK_UNIMAC + +/* Include for relation to classifier linux/etherdevice */ +#include "linux/etherdevice.h" +/* Include for relation to classifier linux/pci */ +#include "linux/pci.h" +/* Include for relation to classifier oak_gicu */ +#include "oak_gicu.h" +/* Include for relation to classifier oak_channel_stat */ +#include "oak_channel_stat.h" +/* Include for relation to classifier oak_unimac_stat */ +#include "oak_unimac_stat.h" +/* Include for relation to classifier oak_unimac_desc */ +#include "oak_unimac_desc.h" +/* Include for relation to classifier oak_ioc_reg */ +#include "oak_ioc_reg.h" +/* Include for relation to classifier oak_ioc_lgen */ +#include "oak_ioc_lgen.h" +/* Include for relation to classifier oak_ioc_stat */ +#include "oak_ioc_stat.h" +/* Include for relation to classifier oak_ioc_set */ +#include "oak_ioc_set.h" +/* Include for relation to classifier oak_ioc_flow */ +#include "oak_ioc_flow.h" +/* Include for relation to classifier oak_irq */ +#include "oak_irq.h" + +#define OAK_REVISION_B0 1 + +#define OAK_PCIE_REGOFF_UNIMAC 0x00050000U + +#define OAK_UNI_DMA_RING_BASE (OAK_PCIE_REGOFF_UNIMAC + 0x00000000) + +#define OAK_UNI_DMA_TXCH_BASE (OAK_PCIE_REGOFF_UNIMAC + 0x00010000) + +#define OAK_UNI_DMA_RXCH_BASE (OAK_PCIE_REGOFF_UNIMAC + 0x00011000) + +#define OAK_UNI_DMA_GLOB_BASE (OAK_PCIE_REGOFF_UNIMAC + 0x00012000) + +#define OAK_UNI_GLOBAL(o) (OAK_UNI_DMA_GLOB_BASE + (o)) + +#define OAK_UNI_DMA_TXCH_OFFS(o) (OAK_UNI_DMA_TXCH_BASE + (o)) + +#define OAK_UNI_DMA_RXCH_OFFS(o) (OAK_UNI_DMA_RXCH_BASE + (o)) + +#define OAK_UNI_DMA_RING_TX(r, o) (OAK_UNI_DMA_RING_BASE + \ + 0x0000 + 0x1000 * (r) + (o)) + +#define OAK_UNI_DMA_RING_RX(r, o) (OAK_UNI_DMA_RING_BASE + \ + 0x0800 + 0x1000 * (r) + (o)) + +#define OAK_UNI_CFG_0 OAK_UNI_GLOBAL(0x00) + +#define OAK_UNI_CFG_1 OAK_UNI_GLOBAL(0x04) + +#define OAK_UNI_CTRL OAK_UNI_GLOBAL(0x10) + +#define OAK_UNI_STAT OAK_UNI_GLOBAL(0x14) + +#define OAK_UNI_INTR OAK_UNI_GLOBAL(0x18) + +#define OAK_UNI_IMSK OAK_UNI_GLOBAL(0x1C) + +#define OAK_UNI_RXEN OAK_UNI_GLOBAL(0x40) + +#define OAK_UNI_TXEN OAK_UNI_GLOBAL(0x44) + +#define OAK_UNI_STAT_TX_WD_HISTORY BIT(7) + +#define OAK_UNI_STAT_TX_WD_EVENT BIT(6) + +#define OAK_UNI_STAT_RX_WD_HISTORY BIT(5) + +#define OAK_UNI_STAT_RX_WD_EVENT BIT(4) + +#define OAK_UNI_STAT_RX_FIFO_EMPTY BIT(3) + +#define OAK_UNI_STAT_RX_FIFO_FULL BIT(2) + +#define OAK_UNI_STAT_TX_FIFO_EMPTY BIT(1) + +#define OAK_UNI_STAT_TX_FIFO_FULL BIT(0) + +#define OAK_UNI_INTR_RX_STAT_MEM_UCE BIT(27) + +#define OAK_UNI_INTR_RX_DESC_MEM_UCE BIT(26) + +#define OAK_UNI_INTR_TX_DESC_MEM_UCE BIT(25) + +#define OAK_UNI_INTR_AXI_WR_MEM_UCE BIT(24) + +#define OAK_UNI_INTR_TX_DATA_MEM_UCE BIT(23) + +#define OAK_UNI_INTR_TX_FIFO_MEM_UCS BIT(22) + +#define OAK_UNI_INTR_RX_FIFO_MEM_UCS BIT(21) + +#define OAK_UNI_INTR_HCS_MEM_UCE BIT(20) + +#define OAK_UNI_INTR_RX_STAT_MEM_CE BIT(19) + +#define OAK_UNI_INTR_RX_DESC_MEM_CE BIT(18) + +#define OAK_UNI_INTR_TX_DESC_MEM_CE BIT(17) + +#define OAK_UNI_INTR_AXI_WR_MEM_CE BIT(16) + +#define OAK_UNI_INTR_TX_DATA_MEM_CE BIT(15) + +#define OAK_UNI_INTR_TX_FIFO_MEM_CE BIT(14) + +#define OAK_UNI_INTR_RX_FIFO_MEM_CE BIT(13) + +#define OAK_UNI_INTR_HCS_MEM_CHECK BIT(12) + +#define OAK_UNI_INTR_TX_MTU_ERR BIT(11) + +#define OAK_UNI_INTR_RX_BERR_WR BIT(10) + +#define OAK_UNI_INTR_RX_BERR_RD BIT(9) + +#define OAK_UNI_INTR_TX_BERR_WR BIT(8) + +#define OAK_UNI_INTR_TX_BERR_RD BIT(7) + +#define OAK_UNI_INTR_BERR_HIC_A BIT(6) + +#define OAK_UNI_INTR_BERR_HIC_B BIT(5) + +#define OAK_UNI_INTR_COUNT_WRAP BIT(4) + +#define OAK_UNI_INTR_RX_WATCHDOG BIT(3) + +#define OAK_UNI_INTR_TX_WATCHDOG BIT(2) + +#define OAK_UNI_INTR_RX_STALL_FIFO BIT(1) + +#define OAK_UNI_INTR_TX_STALL_FIFO BIT(0) + +#define OAK_UNI_INTR_SEVERE_ERRORS (\ +OAK_UNI_INTR_RX_STAT_MEM_UCE | \ +OAK_UNI_INTR_RX_DESC_MEM_UCE | \ +OAK_UNI_INTR_TX_DESC_MEM_UCE | \ +OAK_UNI_INTR_AXI_WR_MEM_UCE | \ +OAK_UNI_INTR_TX_DATA_MEM_UCE | \ +OAK_UNI_INTR_TX_FIFO_MEM_UCS | \ +OAK_UNI_INTR_RX_FIFO_MEM_UCS | \ +OAK_UNI_INTR_HCS_MEM_UCE | \ +OAK_UNI_INTR_RX_STAT_MEM_CE | \ +OAK_UNI_INTR_RX_DESC_MEM_CE | \ +OAK_UNI_INTR_TX_DESC_MEM_CE | \ +OAK_UNI_INTR_AXI_WR_MEM_CE | \ +OAK_UNI_INTR_TX_DATA_MEM_CE | \ +OAK_UNI_INTR_TX_FIFO_MEM_CE | \ +OAK_UNI_INTR_RX_FIFO_MEM_CE | \ +OAK_UNI_INTR_HCS_MEM_CHECK | \ +OAK_UNI_INTR_TX_MTU_ERR | \ +OAK_UNI_INTR_RX_BERR_WR | \ +OAK_UNI_INTR_RX_BERR_RD | \ +OAK_UNI_INTR_TX_BERR_WR | \ +OAK_UNI_INTR_TX_BERR_RD \ +) + +#define OAK_UNI_INTR_NORMAL_ERRORS (OAK_UNI_INTR_BERR_HIC_A | \ + OAK_UNI_INTR_BERR_HIC_B | \ + OAK_UNI_INTR_COUNT_WRAP | \ + OAK_UNI_INTR_RX_WATCHDOG | \ + OAK_UNI_INTR_TX_WATCHDOG | \ + OAK_UNI_INTR_RX_STALL_FIFO | \ + OAK_UNI_INTR_TX_STALL_FIFO) + +#define OAK_UNI_TXRATE_B OAK_UNI_GLOBAL(0x80) + +#define OAK_UNI_TXRATE_A OAK_UNI_GLOBAL(0x84) + +#define OAK_UNI_STAT_RX_GOOD_FRAMES OAK_UNI_GLOBAL(0x100) + +#define OAK_UNI_STAT_RX_BAD_FRAMES OAK_UNI_GLOBAL(0x104) + +#define OAK_UNI_STAT_TX_PAUSE OAK_UNI_GLOBAL(0x108) + +#define OAK_UNI_STAT_TX_STALL_FIFO OAK_UNI_GLOBAL(0x10C) + +#define OAK_UNI_STAT_RX_STALL_DESC OAK_UNI_GLOBAL(0x110) + +#define OAK_UNI_STAT_RX_STALL_FIFO OAK_UNI_GLOBAL(0x114) + +#define OAK_UNI_STAT_RX_DISC_DESC OAK_UNI_GLOBAL(0x118) + +#define OAK_UNI_PTP_HW_TIME OAK_UNI_GLOBAL(0x17C) + +#define OAK_UNI_ECC_ERR_CFG OAK_UNI_GLOBAL(0x1E4) + +#define OAK_UNI_ECC_ERR_STAT_0 OAK_UNI_GLOBAL(0x1E8) + +#define OAK_UNI_ECC_ERR_STAT_1 OAK_UNI_GLOBAL(0x1EC) + +#define OAK_UNI_ECC_ERR_CNT_0 OAK_UNI_GLOBAL(0x1F0) + +#define OAK_UNI_ECC_ERR_CNT_1 OAK_UNI_GLOBAL(0x1F4) + +#define OAK_UNI_DMA_TX_CH_CFG OAK_UNI_DMA_TXCH_OFFS(0x00) + +#define OAK_UNI_DMA_TX_CH_ARBIT_B0_LO OAK_UNI_DMA_TXCH_OFFS(0x04) + +#define OAK_UNI_DMA_TX_CH_ARBIT_B0_HI OAK_UNI_DMA_TXCH_OFFS(0x08) + +#define OAK_DMA_TX_CH_SCHED_B0_LO OAK_UNI_DMA_TXCH_OFFS(0x0C) + +#define OAK_UNI_DMA_TX_CH_SCHED_B0_HI OAK_UNI_DMA_TXCH_OFFS(0x10) + +#define OAK_DMA_TX_CH_SCHED_LO OAK_UNI_DMA_TXCH_OFFS(0x04) + +#define OAK_UNI_DMA_TX_CH_SCHED_HI OAK_UNI_DMA_TXCH_OFFS(0x08) + +#define OAK_UNI_DMA_RX_CH_CFG OAK_UNI_DMA_RXCH_OFFS(0x00) + +#define OAK_UNI_DMA_RX_CH_ARBIT_B0_LO OAK_UNI_DMA_RXCH_OFFS(0x04) + +#define OAK_UNI_DMA_RX_CH_ARBIT_B0_HI OAK_UNI_DMA_RXCH_OFFS(0x08) + +#define OAK_DMA_RX_CH_SCHED_LO OAK_UNI_DMA_RXCH_OFFS(0x04) + +#define OAK_UNI_DMA_RX_CH_SCHED_HI OAK_UNI_DMA_RXCH_OFFS(0x08) + +#define OAK_UNI_TX_RING_CFG(r) OAK_UNI_DMA_RING_TX(r, 0x00) + +#define OAK_UNI_TX_RING_PREF_THR(r) OAK_UNI_DMA_RING_TX(r, 0x04) + +#define OAK_UNI_TX_RING_MBOX_THR(r) OAK_UNI_DMA_RING_TX(r, 0x08) + +#define OAK_UNI_TX_RING_DMA_PTR(r) OAK_UNI_DMA_RING_TX(r, 0x0C) + +#define OAK_UNI_TX_RING_CPU_PTR(r) OAK_UNI_DMA_RING_TX(r, 0x10) + +#define OAK_UNI_TX_RING_EN(r) OAK_UNI_DMA_RING_TX(r, 0x14) + +#define OAK_UNI_TX_RING_INT_CAUSE(r) OAK_UNI_DMA_RING_TX(r, 0x18) + +#define OAK_UNI_TX_RING_INT_MASK(r) OAK_UNI_DMA_RING_TX(r, 0x1C) + +#define OAK_UNI_TX_RING_DBASE_LO(r) OAK_UNI_DMA_RING_TX(r, 0x20) + +#define OAK_UNI_TX_RING_DBASE_HI(r) OAK_UNI_DMA_RING_TX(r, 0x24) + +#define OAK_UNI_TX_RING_MBASE_LO(r) OAK_UNI_DMA_RING_TX(r, 0x28) + +#define OAK_UNI_TX_RING_MBASE_HI(r) OAK_UNI_DMA_RING_TX(r, 0x2C) + +#define OAK_UNI_TX_RING_TIMEOUT(r) OAK_UNI_DMA_RING_TX(r, 0x30) + +#define OAK_UNI_TX_RING_RATECTRL(r) OAK_UNI_DMA_RING_TX(r, 0x34) + +#define OAK_UNI_TX_RING_MAXDTIME(r) OAK_UNI_DMA_RING_TX(r, 0x38) + +#define OAK_UNI_RING_ENABLE_REQ BIT(0) + +#define OAK_UNI_RING_ENABLE_DONE BIT(1) + +#define OAK_UNI_RX_RING_CFG(r) OAK_UNI_DMA_RING_RX(r, 0x00) + +#define OAK_UNI_RX_RING_PREF_THR(r) OAK_UNI_DMA_RING_RX(r, 0x04) + +#define OAK_UNI_RX_RING_MBOX_THR(r) OAK_UNI_DMA_RING_RX(r, 0x08) + +#define OAK_UNI_RX_RING_DMA_PTR(r) OAK_UNI_DMA_RING_RX(r, 0x0C) + +#define OAK_UNI_RX_RING_CPU_PTR(r) OAK_UNI_DMA_RING_RX(r, 0x10) + +#define OAK_UNI_RX_RING_WATERMARK(r) OAK_UNI_DMA_RING_RX(r, 0x14) + +#define OAK_UNI_RX_RING_EN(r) OAK_UNI_DMA_RING_RX(r, 0x18) + +#define OAK_UNI_RX_RING_INT_CAUSE(r) OAK_UNI_DMA_RING_RX(r, 0x1C) + +#define OAK_UNI_RX_RING_INT_MASK(r) OAK_UNI_DMA_RING_RX(r, 0x20) + +#define OAK_UNI_RX_RING_DBASE_LO(r) OAK_UNI_DMA_RING_RX(r, 0x24) + +#define OAK_UNI_RX_RING_DBASE_HI(r) OAK_UNI_DMA_RING_RX(r, 0x28) + +#define OAK_RING_TOUT_USEC(us) (OAK_CLOCK_FREQ_MHZ * 1 * (us)) + +#define OAK_UNI_RX_RING_SBASE_LO(r) OAK_UNI_DMA_RING_RX(r, 0x2C) + +#define OAK_UNI_RX_RING_SBASE_HI(r) OAK_UNI_DMA_RING_RX(r, 0x30) + +#define OAK_UNI_RX_RING_MBASE_LO(r) OAK_UNI_DMA_RING_RX(r, 0x34) + +#define OAK_UNI_RX_RING_MBASE_HI(r) OAK_UNI_DMA_RING_RX(r, 0x38) + +#define OAK_UNI_RX_RING_TIMEOUT(r) OAK_UNI_DMA_RING_RX(r, 0x3C) + +#define OAK_UNI_RX_RING_DADDR_HI(r) OAK_UNI_DMA_RING_RX(r, 0x40) + +#define OAK_UNI_RX_RING_DADDR_LO(r) OAK_UNI_DMA_RING_RX(r, 0x44) + +#define OAK_UNI_RX_RING_ETYPE(r) OAK_UNI_DMA_RING_RX(r, 0x48) + +#define OAK_UNI_RX_RING_MAP(r) OAK_UNI_DMA_RING_RX(r, 0x4C) + +#define OAK_CLOCK_FREQ_MHZ (250U) + +#define OAK_RING_TOUT_MSEC(ms) (OAK_CLOCK_FREQ_MHZ * 1000 * (ms)) + +#define OAK_MIN_TX_RATE_CLASS_A 0 + +#define OAK_MIN_TX_RATE_CLASS_B 1 + +#define OAK_MIN_TX_RATE_IN_KBPS 64 + +#define OAK_MAX_TX_RATE_IN_KBPS 4194240U + +#define OAK_DEF_TX_HI_CREDIT_BYTES 1536U + +#define OAK_MAX_TX_HI_CREDIT_BYTES 0x3fffU + +#define OAK_UNI_RX_RING_DADDR_MASK_HI(r) OAK_UNI_DMA_RING_RX(r, 0x50) + +#define OAK_UNI_RX_RING_DADDR_MASK_LO(r) OAK_UNI_DMA_RING_RX(r, 0x54) + +/* Define DMA check numbers */ +#define OAK_DMA_15 15U +#define OAK_DMA_7 7U +#define OAK_BUFFER_SIZE_16 16 +#define OAK_BUFFER_SIZE_2048 2048 + +typedef struct oak_mbox_tstruct { + u32 dma_ptr_rel; + u32 intr_cause; +} oak_mbox_t; + +typedef struct oak_rxa_tstruct { + struct page *page_virt; + dma_addr_t page_phys; + u32 page_offs; +} oak_rxa_t; + +typedef struct oak_rx_chan_tstruct { +#define OAK_RX_BUFFER_SIZE 2048 +#define OAK_RX_BUFFER_PER_PAGE (PAGE_SIZE / OAK_RX_BUFFER_SIZE) +#define OAK_RX_SKB_ALLOC_SIZE (128 + NET_IP_ALIGN) +#define OAK_RX_LGEN_RX_MODE BIT(0) +#define OAK_RX_REFILL_REQ BIT(1) + struct oak_tstruct *oak; + u32 enabled; + u32 flags; + u32 rbr_count; + u32 rbr_size; + u32 rsr_size; + u32 mbox_size; + atomic_t rbr_pend; + u32 rbr_widx; + u32 rbr_ridx; + u32 rbr_len; + u32 rbr_bsize; /* Rx buffer size */ + u32 rbr_bpage; /* Number of descriptors per page */ + dma_addr_t rbr_dma; + dma_addr_t rsr_dma; + dma_addr_t mbox_dma; + oak_rxa_t *rba; + oak_rxd_t *rbr; + oak_rxs_t *rsr; + oak_mbox_t *mbox; + oak_driver_rx_stat stat; + struct sk_buff *skb; +} oak_rx_chan_t; + +typedef struct oak_txi_tstruct { +#define TX_BUFF_INFO_NONE 0x00000000U +#define TX_BUFF_INFO_ADR_MAPS 0x00000001U +#define TX_BUFF_INFO_ADR_MAPP 0x00000002U +#define TX_BUFF_INFO_EOP 0x00000004U + struct sk_buff *skb; + struct page *page; + dma_addr_t mapping; + u32 mapsize; + u32 flags; +} oak_txi_t; + +typedef struct oak_tx_chan_tstruct { +#define OAK_RX_LGEN_TX_MODE BIT(0) + struct oak_tstruct *oak; + u32 enabled; + u32 flags; + u32 tbr_count; + u32 tbr_compl; + u32 tbr_size; + atomic_t tbr_pend; + u32 tbr_ridx; + u32 tbr_widx; + u32 tbr_len; + dma_addr_t tbr_dma; + oak_txd_t *tbr; + oak_txi_t *tbi; + u32 mbox_size; + dma_addr_t mbox_dma; + oak_mbox_t *mbox; + oak_driver_tx_stat stat; + /* lock */ + spinlock_t lock; +} oak_tx_chan_t; + +typedef struct oak_tstruct { +#define MAX_RBR_RING_ENTRIES 3U +#define MAX_TBR_RING_ENTRIES 3U +#define XBR_RING_SIZE(s) (1U << ((s) + 4U)) +#define MAX_RBR_RING_SIZE XBR_RING_SIZE(MAX_RBR_RING_ENTRIES) +#define MAX_TBR_RING_SIZE XBR_RING_SIZE(MAX_TBR_RING_ENTRIES) +#define RX_DESC_PREFETCH_TH 5 +#define RX_MBOX_WRITE_TH 5 +#define TX_DESC_PREFETCH_TH 5 +#define TX_MBOX_WRITE_TH 5 +#define OAK_MBOX_RX_RES_LOW BIT(2) +#define OAK_MBOX_RX_COMP BIT(0) +#define OAK_MBOX_TX_ERR_ABORT BIT(4) +#define OAK_MBOX_TX_ERR_HCRED BIT(3) +#define OAK_MBOX_TX_LATE_TS BIT(2) +#define OAK_MBOX_TX_COMP BIT(0) +#define OAK_IVEC_UVEC1 BIT(8) +#define NEXT_IDX(i, sz) (((i) + 1) % (sz)) +#define MAX_NUM_OF_CHANNELS 10 +#define nw32(np, reg, val) writel((val), (np)->um_base + (reg)) +#define MIN_NUM_OF_CHANNELS 1 +#define nr32(np, reg) readl((np)->um_base + (reg)) +#define sr32(np, reg) readl((np)->um_base + (reg)) +#define sw32(np, reg, val) writel((val), (np)->um_base + (reg)) +#define oakdbg(debug_var, TYPE, f, a...) \ + do { \ + if (((debug_var) & NETIF_MSG_##TYPE) != 0) { \ + pr_info("%s:" f, __func__, ##a); \ + } \ + } while (0) + + int level; + u32 pci_class_revision; + /* lock */ + spinlock_t lock; + struct net_device *netdev; + struct device *device; + struct pci_dev *pdev; + void __iomem *um_base; + void __iomem *sw_base; + u32 page_order; + u32 page_size; + oak_gicu gicu; + u32 num_rx_chan; + oak_rx_chan_t rx_channel[MAX_NUM_OF_CHANNELS]; + u32 num_tx_chan; + oak_tx_chan_t tx_channel[MAX_NUM_OF_CHANNELS]; + oak_unimac_stat unimac_stat; + u16 rrs; + u32 speed; + char mac_address[ETH_ALEN]; +} oak_t; + +int oak_net_rbr_refill(oak_t *np, u32 ring); + +/* Name : oak_unimac_disable_and_get_tx_irq_reason + * Returns : u32 + * Parameters : oak_t *np, u32 ring, u32 *dma_ptr + * Description : This function check the reason for Tx IRQ + */ +u32 oak_unimac_disable_and_get_tx_irq_reason(oak_t *np, u32 ring, + u32 *dma_ptr); + +/* Name : oak_unimac_alloc_channels + * Returns : int + * Parameters : oak_t *np, int rxs, int txs, int chan, int rto + * Description : This function allocate tx and rx channels + */ +int oak_unimac_alloc_channels(oak_t *np, u32 rxs, u32 txs, int chan, int rto); + +/* Name : oak_unimac_free_channels + * Returns : void + * Parameters : oak_t *np + * Description : This function free the tx and rx channels + */ +void oak_unimac_free_channels(oak_t *np); + +/* Name : oak_unimac_reset + * Returns : int + * Parameters : oak_t *np + * Description : This function reset unimac statistics + */ +int oak_unimac_reset(oak_t *np); + +/* Name : oak_unimac_reset_statistics + * Returns : void + * Parameters : oak_t *np + * Description : This function reset the statistics counter + */ +void oak_unimac_reset_statistics(oak_t *np); + +/* Name : oak_unimac_crt_bit_mask + * Returns : u32 + * Parameters : u32 off, u32 len, u32 val, u32 bit_mask + * Description : This function calculate the crt bit mask + */ +u32 oak_unimac_crt_bit_mask(u32 off, u32 len, u32 val, + u32 bit_mask); + +/* Name : oak_unimac_io_read_32 + * Returns : u32 + * Parameters : oak_t * np, u32 addr + * Description : This function read from register. + */ +u32 oak_unimac_io_read_32(oak_t *np, u32 addr); + +/* Name : oak_unimac_io_write_32 + * Returns : void + * Parameters : oak_t *np, u32 addr, u32 val + * Description : This function write value to register. + */ +void oak_unimac_io_write_32(oak_t *np, u32 addr, u32 val); + +/* Name : oak_unimac_set_bit_num + * Returns : void + * Parameters : oak_t *np, u32 reg, u32 bit_num, int enable + * Description : This function set a bit number + */ +void oak_unimac_set_bit_num(oak_t *np, u32 reg, u32 bit_num, + u32 enable); + +/* Name : oak_unimac_set_rx_none + * Returns : void + * Parameters : oak_t *np, u32 ring + * Description : This function clear the rx ring + */ +void oak_unimac_set_rx_none(oak_t *np, u32 ring); + +/* Name : oak_unimac_set_rx_8021Q_et + * Returns : void + * Parameters : oak_t *np, u32 ring, u16 etype, u16 pcp_vid, + * int enable + * Description : This function write ethtype and calls set bit number for + * Rx ring + */ +void oak_unimac_set_rx_8021Q_et(oak_t *np, u32 ring, u16 etype, + u16 pcp_vid, u32 enable); + +/* Name : oak_unimac_set_rx_8021Q_fid + * Returns : void + * Parameters : oak_t *np, u32 ring, u32 fid, int enable + * Description : This function set Rx ring Filtering database identifier (FID) + */ +void oak_unimac_set_rx_8021Q_fid(oak_t *np, u32 ring, u32 fid, + u32 enable); + +/* Name : oak_unimac_set_rx_8021Q_flow + * Returns : void + * Parameters : oak_t *np, u32 ring, u32 flow_id, int enable + * Description : This function set Rx ring flow identifier + */ +void oak_unimac_set_rx_8021Q_flow(oak_t *np, u32 ring, u32 flow_id, + u32 enable); + +/* Name : oak_unimac_set_rx_8021Q_qpri + * Returns : void + * Parameters : oak_t *np, u32 ring, u32 qpri, int enable + * Description : This function set Rx ring queue priority. + */ +void oak_unimac_set_rx_8021Q_qpri(oak_t *np, u32 ring, u32 qpri, + u32 enable); + +/* Name : oak_unimac_set_rx_8021Q_spid + * Returns : void + * Parameters : oak_t *np, u32 ring, u32 spid, int enable + * Description : This function set Rx ring speed identifier (SPID). + */ +void oak_unimac_set_rx_8021Q_spid(oak_t *np, u32 ring, u32 spid, + u32 enable); + +/* Name : oak_unimac_set_rx_da + * Returns : void + * Parameters : oak_t *np, u32 ring, unsigned char *addr, int enable + * Description : This function set rx ring MAC address + */ +void oak_unimac_set_rx_da(oak_t *np, u32 ring, unsigned char *addr, + u32 enable); + +/* Name : oak_unimac_set_rx_da_mask + * Returns : void + * Parameters : oak_t *np, u32 ring, unsigned char *addr, int enable + * Description : This function set rx ring MAC address + */ +void oak_unimac_set_rx_da_mask(oak_t *np, u32 ring, unsigned char *addr, + int enable); + +/* Name : oak_unimac_set_rx_mgmt + * Returns : void + * Parameters : oak_t *np, u32 ring, u32 val, int enable + * Description : This function call set bit number function with + * value and enable options + */ +void oak_unimac_set_rx_mgmt(oak_t *np, u32 ring, u32 val, u32 enable); + +/* Name : oak_unimac_process_status + * Returns : void + * Parameters : ldg_t *ldg + * Description : This function get the process status + */ +void oak_unimac_process_status(ldg_t *ldg); + +/* Name : oak_unimac_rx_error + * Returns : void + * Parameters : ldg_t *ldg, u32 ring + * Description : This function check interrupt cause reason and then if the + * reason is valid refill the receive ring else increment the rx errors + * counters. + */ +void oak_unimac_rx_error(ldg_t *ldg, u32 ring); + +/* Name : oak_unimac_tx_error + * Returns : void + * Parameters : ldg_t *ldg, u32 ring + * Description : This function Tx error reason and then count errors + */ +void oak_unimac_tx_error(ldg_t *ldg, u32 ring); + +/* Name : oak_unimac_ena_rx_ring_irq + * Returns : void + * Parameters : oak_t *np, u32 ring, u32 enable + * Description : This function enables the Rx ring irq. + */ +void oak_unimac_ena_rx_ring_irq(oak_t *np, u32 ring, u32 enable); + +/* Name : oak_unimac_ena_tx_ring_irq + * Returns : void + * Parameters : oak_t *np, u32 ring, u32 enable + * Description : This function enables the Tx ring irq. + */ +void oak_unimac_ena_tx_ring_irq(oak_t *np, u32 ring, u32 enable); + +/* Name : oak_unimac_set_tx_ring_rate + * Returns : int + * Parameters : oak_t *np, u32 ring, u32 sr_class, + * u32 hi_credit, u32 r_kbps + * Description : This function set tx ring rate limit. + */ +int oak_unimac_set_tx_ring_rate(oak_t *np, u32 ring, u32 sr_class, + u32 hi_credit, u32 r_kbps); + +/* Name : oak_unimac_clr_tx_ring_rate + * Returns : void + * Parameters : oak_t *np, u32 ring + * Description : This function clear the tx ring rate limit + */ +void oak_unimac_clr_tx_ring_rate(oak_t *np, u32 ring); + +/* Name : oak_unimac_set_tx_mac_rate + * Returns : int + * Parameters : oak_t *np, u32 sr_class, u32 hi_credit, + * u32 r_kbps + * Description : This function set transmision mac rate limit. + */ +int oak_unimac_set_tx_mac_rate(oak_t *np, u32 sr_class, u32 hi_credit, + u32 r_kbps); + +/* Name : oak_unimac_start_all_txq + * Returns : int + * Parameters : oak_t *np, u32 enable + * Description : This function start all transmit queues + */ +int oak_unimac_start_all_txq(oak_t *np, u32 enable); + +/* Name : oak_unimac_start_all_rxq + * Returns : int + * Parameters : oak_t *np, u32 enable + * Description : This function start all receive queues + */ +int oak_unimac_start_all_rxq(oak_t *np, u32 enable); + +/* Name : oak_unimac_start_tx_ring + * Returns : u32 + * Parameters : oak_t *np, int32_t ring, u32 enable + * Description : This function start rx ring + */ +u32 oak_unimac_start_tx_ring(oak_t *np, int32_t ring, u32 enable); + +/* Name : oak_unimac_start_rx_ring + * Returns : u32 + * Parameters : oak_t *np, u32 ring, u32 enable + * Description : This function start Rx ring + */ +u32 oak_unimac_start_rx_ring(oak_t *np, u32 ring, u32 enable); + +/* Name : oak_ilog2_kernel_utility + * Returns : u32 + * Parameters : u64 val + * Description : This function calls ilog2 kernel macro, ilog2 has complexity + * 67, the function ilog2 is called by net and unimac component. + */ +static inline u64 oak_ilog2_kernel_utility(u64 val) +{ + return ilog2(val); +} +#endif /* #ifndef H_OAK_UNIMAC */ diff --git a/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_unimac_desc.c b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_unimac_desc.c new file mode 100644 index 00000000..094c942e --- /dev/null +++ b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_unimac_desc.c @@ -0,0 +1,16 @@ +/* + * + * If you received this File from Marvell, you may opt to use, redistribute and/or + * modify this File in accordance with the terms and conditions of the General + * Public License Version 2, June 1991 (the "GPL License"), a copy of which is + * available along with the File in the license.txt file or by writing to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + * on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + * DISCLAIMED. The GPL License provides additional details about this warranty + * disclaimer. + * + */ +#include "oak_unimac_desc.h" + diff --git a/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_unimac_desc.h b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_unimac_desc.h new file mode 100644 index 00000000..ede2ed39 --- /dev/null +++ b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_unimac_desc.h @@ -0,0 +1,62 @@ +/* + * + * If you received this File from Marvell, you may opt to use, redistribute and/or + * modify this File in accordance with the terms and conditions of the General + * Public License Version 2, June 1991 (the "GPL License"), a copy of which is + * available along with the File in the license.txt file or by writing to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + * on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + * DISCLAIMED. The GPL License provides additional details about this warranty + * disclaimer. + * + */ +#ifndef H_OAK_UNIMAC_DESC +#define H_OAK_UNIMAC_DESC + +typedef struct oak_rxd_tstruct { + u32 buf_ptr_lo; + u32 buf_ptr_hi; +} oak_rxd_t; + +typedef struct oak_rxs_tstruct { + u32 bc : 16; + u32 es : 1; + u32 ec : 2; + u32 res1 : 1; + u32 first_last : 2; + u32 ipv4_hdr_ok : 1; + u32 l4_chk_ok : 1; + u32 l4_prot : 2; + u32 res2 : 1; + u32 l3_ipv4 : 1; + u32 l3_ipv6 : 1; + u32 vlan : 1; + u32 l2_prot : 2; + u32 timestamp : 32; + u32 rc_chksum : 16; + u32 udp_cs_0 : 1; + u32 res3 : 15; + u32 mhdr : 16; + u32 mhok : 1; + u32 res4 : 15; +} oak_rxs_t; + +typedef struct oak_txd_tstruct { + u32 bc : 16; + u32 res1 : 4; + u32 last : 1; + u32 first : 1; + u32 gl3_chksum : 1; + u32 gl4_chksum : 1; + u32 res2 : 4; + u32 time_valid : 1; + u32 res3 : 3; + u32 timestamp : 32; + u32 buf_ptr_lo : 32; + u32 buf_ptr_hi : 32; +} oak_txd_t; + +#endif /* #ifndef H_OAK_UNIMAC_DESC */ + diff --git a/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_unimac_stat.h b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_unimac_stat.h new file mode 100644 index 00000000..13199daf --- /dev/null +++ b/drivers/net/ethernet/marvell/mvae_lin5_gpl_v3.01.0001/oak_unimac_stat.h @@ -0,0 +1,29 @@ +/* + * + * If you received this File from Marvell, you may opt to use, redistribute and/or + * modify this File in accordance with the terms and conditions of the General + * Public License Version 2, June 1991 (the "GPL License"), a copy of which is + * available along with the File in the license.txt file or by writing to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or + * on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED + * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY + * DISCLAIMED. The GPL License provides additional details about this warranty + * disclaimer. + * + */ +#ifndef H_OAK_UNIMAC_STAT +#define H_OAK_UNIMAC_STAT + +typedef struct oak_unimac_statstruct { + u64 rx_good_frames; + u64 rx_bad_frames; + u64 rx_stall_fifo; + u64 rx_stall_desc; + u64 rx_discard_desc; + u64 tx_pause; + u64 tx_stall_fifo; +} oak_unimac_stat; + +#endif /* #ifndef H_OAK_UNIMAC_STAT */ +