mbox series

[net-next,v5,0/6] net: mtip: Add support for MTIP imx287 L2 switch driver

Message ID 20250414140128.390400-1-lukma@denx.de (mailing list archive)
Headers show
Series net: mtip: Add support for MTIP imx287 L2 switch driver | expand

Message

Lukasz Majewski April 14, 2025, 2:01 p.m. UTC
This patch series adds support for More Than IP's L2 switch driver embedded
in some NXP's SoCs. This one has been tested on imx287, but is also available
in the vf610.

In the past there has been performed some attempts to upstream this driver:

1. The 4.19-cip based one [1]
2. DSA based one for 5.12 [2] - i.e. the switch itself was treat as a DSA switch
   with NO tag appended.
3. The extension for FEC driver for 5.12 [3] - the trick here was to fully reuse
   FEC when the in-HW switching is disabled. When bridge offloading is enabled,
   the driver uses already configured MAC and PHY to also configure PHY.

All three approaches were not accepted as eligible for upstreaming.

The driver from this series has floowing features:

1. It is fully separated from fec_main - i.e. can be used interchangeable
   with it. To be more specific - one can build them as modules and
   if required switch between them when e.g. bridge offloading is required.

   To be more specific:
        - Use FEC_MAIN: When one needs support for two ETH ports with separate
          uDMAs used for both and bridging can be realized in SW.

        - Use MTIPL2SW: When it is enough to support two ports with only uDMA0
          attached to switch and bridging shall be offloaded to HW. 

2. This driver uses MTIP's L2 switch internal VLAN feature to provide port
   separation at boot time. Port separation is disabled when bridging is
   required.

3. Example usage:
        Configuration:
        ip link set lan0 up; sleep 1;
        ip link set lan1 up; sleep 1;
        ip link add name br0 type bridge;
        ip link set br0 up; sleep 1;
        ip link set lan0 master br0;
        ip link set lan1 master br0;
        bridge link;
        ip addr add 192.168.2.17/24 dev br0;
        ping -c 5 192.168.2.222

        Removal:
        ip link set br0 down;
        ip link delete br0 type bridge;
        ip link set dev lan1 down
        ip link set dev lan0 down

4. Limitations:
        - Driver enables and disables switch operation with learning and ageing.
        - Missing is the advanced configuration (e.g. adding entries to FBD). This is
          on purpose, as up till now we didn't had consensus about how the driver
          shall be added to Linux.

Links:
[1] - https://github.com/lmajewski/linux-imx28-l2switch/commits/master
[2] - https://github.com/lmajewski/linux-imx28-l2switch/tree/imx28-v5.12-L2-upstream-RFC_v1
[3] - https://source.denx.de/linux/linux-imx28-l2switch/-/tree/imx28-v5.12-L2-upstream-switchdev-RFC_v1?ref_type=heads

Lukasz Majewski (6):
  dt-bindings: net: Add MTIP L2 switch description
  ARM: dts: nxp: mxs: Adjust the imx28.dtsi L2 switch description
  ARM: dts: nxp: mxs: Adjust XEA board's DTS to support L2 switch
  net: mtip: The L2 switch driver for imx287
  ARM: mxs_defconfig: Update mxs_defconfig to 6.15-rc1
  ARM: mxs_defconfig: Enable CONFIG_FEC_MTIP_L2SW to support MTIP L2
    switch

 .../bindings/net/nxp,imx28-mtip-switch.yaml   |  141 ++
 MAINTAINERS                                   |    7 +
 arch/arm/boot/dts/nxp/mxs/imx28-xea.dts       |   56 +
 arch/arm/boot/dts/nxp/mxs/imx28.dtsi          |    8 +-
 arch/arm/configs/mxs_defconfig                |   14 +-
 drivers/net/ethernet/freescale/Kconfig        |    1 +
 drivers/net/ethernet/freescale/Makefile       |    1 +
 drivers/net/ethernet/freescale/mtipsw/Kconfig |   13 +
 .../net/ethernet/freescale/mtipsw/Makefile    |    3 +
 .../net/ethernet/freescale/mtipsw/mtipl2sw.c  | 1990 +++++++++++++++++
 .../net/ethernet/freescale/mtipsw/mtipl2sw.h  |  788 +++++++
 .../ethernet/freescale/mtipsw/mtipl2sw_br.c   |  120 +
 .../ethernet/freescale/mtipsw/mtipl2sw_mgnt.c |  449 ++++
 13 files changed, 3578 insertions(+), 13 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/net/nxp,imx28-mtip-switch.yaml
 create mode 100644 drivers/net/ethernet/freescale/mtipsw/Kconfig
 create mode 100644 drivers/net/ethernet/freescale/mtipsw/Makefile
 create mode 100644 drivers/net/ethernet/freescale/mtipsw/mtipl2sw.c
 create mode 100644 drivers/net/ethernet/freescale/mtipsw/mtipl2sw.h
 create mode 100644 drivers/net/ethernet/freescale/mtipsw/mtipl2sw_br.c
 create mode 100644 drivers/net/ethernet/freescale/mtipsw/mtipl2sw_mgnt.c

Comments

Andrew Lunn April 15, 2025, 1:43 a.m. UTC | #1
On Mon, Apr 14, 2025 at 04:01:26PM +0200, Lukasz Majewski wrote:
> This patch series provides support for More Than IP L2 switch embedded
> in the imx287 SoC.
> 
> This is a two port switch (placed between uDMA[01] and MAC-NET[01]),
> which can be used for offloading the network traffic.
> 
> It can be used interchangeably with current FEC driver - to be more
> specific: one can use either of it, depending on the requirements.
> 
> The biggest difference is the usage of DMA - when FEC is used, separate
> DMAs are available for each ENET-MAC block.
> However, with switch enabled - only the DMA0 is used to send/receive data
> to/form switch (and then switch sends them to respecitive ports).
> 
> Signed-off-by: Lukasz Majewski <lukma@denx.de>

Reviewed-by: Andrew Lunn <andrew@lunn.ch>

    Andrew
Stefan Wahren April 17, 2025, 10:32 a.m. UTC | #2
Hi Lukasz,

Am 14.04.25 um 16:01 schrieb Lukasz Majewski:
> This patch series provides support for More Than IP L2 switch embedded
> in the imx287 SoC.
>
> This is a two port switch (placed between uDMA[01] and MAC-NET[01]),
> which can be used for offloading the network traffic.
>
> It can be used interchangeably with current FEC driver - to be more
> specific: one can use either of it, depending on the requirements.
>
> The biggest difference is the usage of DMA - when FEC is used, separate
> DMAs are available for each ENET-MAC block.
> However, with switch enabled - only the DMA0 is used to send/receive data
> to/form switch (and then switch sends them to respecitive ports).
>
> Signed-off-by: Lukasz Majewski <lukma@denx.de>
> ---
> Changes for v2:
>
> - Remove not needed comments
> - Restore udelay(10) for switch reset (such delay is explicitly specifed
>    in the documentation
> - Add COMPILE_TEST
> - replace pr_* with dev_*
> - Use for_each_available_child_of_node_scoped()
> - Use devm_* function for memory allocation
> - Remove printing information about the HW and SW revision of the driver
> - Use devm_regulator_get_optional()
> - Change compatible prefix from 'fsl' to more up to date 'nxp'
> - Remove .owner = THIS_MODULE
> - Use devm_platform_ioremap_resource(pdev, 0);
> - Use devm_request_irq()
> - Use devm_regulator_get_enable_optional()
> - Replace clk_prepare_enable() and devm_clk_get() with single
>    call to devm_clk_get_optional_enabled()
> - Cleanup error patch when function calls in probe fail
> - Refactor the mtip_reset_phy() to serve as mdio bus reset callback
> - Add myself as the MTIP L2 switch maintainer (squashed the separated
>    commit)
> - More descriptive help paragraphs (> 4 lines)
>
> Changes for v3:
> - Remove 'bridge_offloading' module parameter (to bridge ports just after probe)
> - Remove forward references
> - Fix reverse christmas tree formatting in functions
> - Convert eligible comments to kernel doc format
> - Remove extra MAC address validation check at esw_mac_addr_static()
> - Remove mtip_print_link_status() and replace it with phy_print_status()
> - Avoid changing phy device state in the driver (instead use functions
>    exported by the phy API)
> - Do not print extra information regarding PHY (which is printed by phylib) -
>    e.g. net lan0: lan0: MTIP eth L2 switch 1e:ce:a5:0b:4c:12
> - Remove VERSION from the driver - now we rely on the SHA1 in Linux
>    mainline tree
> - Remove zeroing of the net device private area (shall be already done
>    during allocation)
> - Refactor the code to remove mtip_ndev_setup()
> - Use -ENOMEM instead of -1 return code when allocation fails
> - Replace dev_info() with dev_dbg() to reduce number of information print
>    on normal operation
> - Return ret instead of 0 from mtip_ndev_init()
> - Remove fep->mii_timeout flag from the driver
> - Remove not used stop_gpr_* fields in mtip_devinfo struct
> - Remove platform_device_id description for mtipl2sw driver
> - Add MODULE_DEVICE_TABLE() for mtip_of_match
> - Remove MODULE_ALIAS()
>
> Changes for v4:
> - Rename imx287 with imx28 (as the former is not used in kernel anymore)
> - Reorder the place where ENET interface is initialized - without this
>    change the enet_out clock has default (25 MHz) value, which causes
>    issues during reset (RMII's 50 MHz is required for proper PHY reset).
> - Use PAUR instead of PAUR register to program MAC address
> - Replace eth_mac_addr() with eth_hw_addr_set()
> - Write to HW the randomly generated MAC address (if required)
> - Adjust the reset code
> - s/read_atable/mtip_read_atable/g and s/write_atable/mtip_write_atable/g
> - Add clk_disable() and netif_napi_del() when errors occur during
>    mtip_open() - refactor the error handling path.
> - Refactor the mtip_set_multicast_list() to write (now) correct values to
>    ENET-FEC registers.
> - Replace dev_warn() with dev_err()
> - Use GPIO_ACTIVE_LOW to indicate polarity in DTS
> - Refactor code to check if network device is the switch device
> - Remove mtip_port_dev_check()
> - Refactor mtip_ndev_port_link() avoid starting HW offloading for bridge
>    when MTIP ports are parts of two distinct bridges
> - Replace del_timer() with timer_delete_sync()
>
> Changes for v5:
> - Fix spelling in Kconfig
> - Replace tmp with reg or register name
> - Replace tmpaddr with mac_addr
> - Use mac address assignment (from registers) code similar to fec_main.c (as it
>    shall handle properly generic endianess)
> - Add description for fep: in the mtip_update_atable_static() kernel doc
> - Replace writel(bdp, &fep->cur_rx) with fep->cur_rx = bdp;
> - Fix spelling of transmit
> - Remove not needed white spaces in mtipl2sw.h
> - Remove '_t' from struct mtip_addr_table_t
> - Provide proper alignment in the mtipl2sw.h
> - Add blank line after local header in mtipl2sw_br.c
> - Use %p instead of %x (and cast) for fep in debug message
> - Disable L2 switch in-HW offloading when only one
>    of eligible ports is removed from the bridge
> - Sort includes in the patch set alphabethically
> - Introduce FEC_QUIRK_SWAP_FRAME to avoid #ifdef for imx28 proper operation
> - Move 'mtip_port_info g_info' to struct switch_enet_private
> - Replace some unsigned int with u32 (on data fields with 32 bit size)
> - Remove not relevant comments from mtip_enet_init()
> - Refactor functions definitions to be void when no other
>    value than 0 is returned.
> - Use capital letters in HEX constants
> - Use u32 instead of unsigned int when applicable
> - Add error handling code after the dma_map_single() is called
> - The MCF_FEC_MSCR register can be written unconditionally
>    for all supported platforms.
> - Use IS_ENABLED() instead of #ifdef in mtip_timeout()
> - Replace dev_info() with dev_warn_ratelimited() in mtip_switch_rx()
> - Add code to handle situation when there is no memory
> - Remove kfree(fep->mii_bus->irq)
> - Provide more verbose output of mdio_{read|write} functions
> - Handle error when clk_enable() fails in mtip_open()
> - Use dev_dbg() at mtip_set_multicast_list()
> - Simplify the mtip_is_switch_netdev_port() function to return condition check value
> - Add dev_dbg() when of_get_mac_address() fails (as it may not be provided)
> - Remove return ret; in mtip_register_notifiers()
> - Replace int to bool in mtipl2sw_mgnt.c file's function definitions
> - Replace unsigned int/long with u32 where applicable (where access to
>    32 bit registers is performed)
> - Refactor code in mtip_{read|write}_atable() to be more readable
> - Remove code added for not (yet) supported IMX's vf610 SoC
> - Remove do { } while(); loop from mtip_interrupt() function
> - Introduce MTIP_PORT_FORWARDING_INIT to indicate intial value for
>    port forwarding
> - Replace 'unsigned long' to 'u32' in mtipl2sw.h
> - Replace 'unsigned short' to 'u16' in mtipl2sw.h
> - use %#x in dev_dbg()
> - Call SET_NETDEV_DEV() macro to set network device' parent - otherwise
>    phy_attach_direct() will fail.
> ---
>   MAINTAINERS                                   |    7 +
>   drivers/net/ethernet/freescale/Kconfig        |    1 +
>   drivers/net/ethernet/freescale/Makefile       |    1 +
>   drivers/net/ethernet/freescale/mtipsw/Kconfig |   13 +
>   .../net/ethernet/freescale/mtipsw/Makefile    |    3 +
>   .../net/ethernet/freescale/mtipsw/mtipl2sw.c  | 1990 +++++++++++++++++
>   .../net/ethernet/freescale/mtipsw/mtipl2sw.h  |  788 +++++++
>   .../ethernet/freescale/mtipsw/mtipl2sw_br.c   |  120 +
>   .../ethernet/freescale/mtipsw/mtipl2sw_mgnt.c |  449 ++++
>   9 files changed, 3372 insertions(+)
>   create mode 100644 drivers/net/ethernet/freescale/mtipsw/Kconfig
>   create mode 100644 drivers/net/ethernet/freescale/mtipsw/Makefile
>   create mode 100644 drivers/net/ethernet/freescale/mtipsw/mtipl2sw.c
>   create mode 100644 drivers/net/ethernet/freescale/mtipsw/mtipl2sw.h
>   create mode 100644 drivers/net/ethernet/freescale/mtipsw/mtipl2sw_br.c
>   create mode 100644 drivers/net/ethernet/freescale/mtipsw/mtipl2sw_mgnt.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 1248443035f4..af4e42a33c99 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -9448,6 +9448,13 @@ S:	Maintained
>   F:	Documentation/devicetree/bindings/i2c/i2c-mpc.yaml
>   F:	drivers/i2c/busses/i2c-mpc.c
>   
> +FREESCALE MTIP ETHERNET SWITCH DRIVER
> +M:	Lukasz Majewski <lukma@denx.de>
> +L:	netdev@vger.kernel.org
> +S:	Maintained
> +F:	Documentation/devicetree/bindings/net/nxp,imx28-mtip-switch.yaml
> +F:	drivers/net/ethernet/freescale/mtipsw/*
> +
>   FREESCALE QORIQ DPAA ETHERNET DRIVER
>   M:	Madalin Bucur <madalin.bucur@nxp.com>
>   L:	netdev@vger.kernel.org
> diff --git a/drivers/net/ethernet/freescale/Kconfig b/drivers/net/ethernet/freescale/Kconfig
> index a2d7300925a8..056a11c3a74e 100644
> --- a/drivers/net/ethernet/freescale/Kconfig
> +++ b/drivers/net/ethernet/freescale/Kconfig
> @@ -60,6 +60,7 @@ config FEC_MPC52xx_MDIO
>   
>   source "drivers/net/ethernet/freescale/fs_enet/Kconfig"
>   source "drivers/net/ethernet/freescale/fman/Kconfig"
> +source "drivers/net/ethernet/freescale/mtipsw/Kconfig"
>   
>   config FSL_PQ_MDIO
>   	tristate "Freescale PQ MDIO"
> diff --git a/drivers/net/ethernet/freescale/Makefile b/drivers/net/ethernet/freescale/Makefile
> index de7b31842233..0e6cacb0948a 100644
> --- a/drivers/net/ethernet/freescale/Makefile
> +++ b/drivers/net/ethernet/freescale/Makefile
> @@ -25,3 +25,4 @@ obj-$(CONFIG_FSL_DPAA_ETH) += dpaa/
>   obj-$(CONFIG_FSL_DPAA2_ETH) += dpaa2/
>   
>   obj-y += enetc/
> +obj-y += mtipsw/
> diff --git a/drivers/net/ethernet/freescale/mtipsw/Kconfig b/drivers/net/ethernet/freescale/mtipsw/Kconfig
> new file mode 100644
> index 000000000000..0ae58e7b1ca6
> --- /dev/null
> +++ b/drivers/net/ethernet/freescale/mtipsw/Kconfig
> @@ -0,0 +1,13 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +config FEC_MTIP_L2SW
> +	tristate "MoreThanIP L2 switch support to FEC driver"
> +	depends on OF
> +	depends on NET_SWITCHDEV
> +	depends on BRIDGE
> +	depends on ARCH_MXS || COMPILE_TEST
I think we should align the dependencies with FEC:

depends on SOC_IMX28 || COMPILE_TEST
> +	help
> +	  This enables support for the MoreThan IP L2 switch on i.MX
> +	  SoCs (e.g. iMX287). It offloads bridging to this IP block's
> +	  hardware and allows switch management with standard Linux tools.
> +	  This switch driver can be used interchangeable with the already
> +	  available FEC driver, depending on the use case's requirements.
> diff --git a/drivers/net/ethernet/freescale/mtipsw/Makefile b/drivers/net/ethernet/freescale/mtipsw/Makefile
> new file mode 100644
> index 000000000000..4d69db2226a6
> --- /dev/null
> +++ b/drivers/net/ethernet/freescale/mtipsw/Makefile
> @@ -0,0 +1,3 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +obj-$(CONFIG_FEC_MTIP_L2SW) += mtipl2sw.o mtipl2sw_mgnt.o mtipl2sw_br.o
> diff --git a/drivers/net/ethernet/freescale/mtipsw/mtipl2sw.c b/drivers/net/ethernet/freescale/mtipsw/mtipl2sw.c
> new file mode 100644
> index 000000000000..50f5a0c1bc8c
> --- /dev/null
> +++ b/drivers/net/ethernet/freescale/mtipsw/mtipl2sw.c
> @@ -0,0 +1,1990 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + *  L2 switch Controller (Ethernet L2 switch) driver for MTIP block.
> + *
> + *  Copyright (C) 2025 DENX Software Engineering GmbH
> + *  Lukasz Majewski <lukma@denx.de>
> + *
> + *  Based on a previous work by:
> + *
> + *  Copyright 2010-2012 Freescale Semiconductor, Inc.
> + *  Alison Wang (b18965@freescale.com)
> + *  Jason Jin (Jason.jin@freescale.com)
> + *
> + *  Copyright (C) 2010-2013 Freescale Semiconductor, Inc. All Rights Reserved.
> + *  Shrek Wu (B16972@freescale.com)
> + */
> +
> +#include <linux/bitops.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/errno.h>
> +#include <linux/etherdevice.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/netdevice.h>
> +#include <linux/of_mdio.h>
> +#include <linux/of_net.h>
> +#include <linux/of_platform.h>
> +#include <linux/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/rtnetlink.h>
> +#include <linux/skbuff.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/string.h>
> +
> +#include "mtipl2sw.h"
> +
> +static void swap_buffer(void *bufaddr, int len)
> +{
> +	int i;
> +	unsigned int *buf = bufaddr;
> +
> +	for (i = 0; i < len; i += 4, buf++)
> +		swab32s(buf);
> +}
> +
> +struct mtip_devinfo {
> +	u32 quirks;
> +};
> +
> +static void mtip_enet_init(struct switch_enet_private *fep, int port)
> +{
> +	void __iomem *enet_addr = fep->enet_addr;
> +	u32 mii_speed, holdtime, reg;
> +
> +	if (port == 2)
> +		enet_addr += MCF_ESW_ENET_PORT_OFFSET;
> +
> +	reg = MCF_FEC_RCR_PROM | MCF_FEC_RCR_MII_MODE |
> +		MCF_FEC_RCR_MAX_FL(1522);
> +
> +	if (fep->phy_interface[port - 1]  == PHY_INTERFACE_MODE_RMII)
> +		reg |= MCF_FEC_RCR_RMII_MODE;
> +
> +	writel(reg, enet_addr + MCF_FEC_RCR);
> +
> +	writel(MCF_FEC_TCR_FDEN, enet_addr + MCF_FEC_TCR);
> +	writel(MCF_FEC_ECR_ETHER_EN, enet_addr + MCF_FEC_ECR);
> +
> +	mii_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 5000000);
> +	mii_speed--;
> +
> +	holdtime = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 100000000) - 1;
> +
> +	fep->phy_speed = mii_speed << 1 | holdtime << 8;
> +
> +	writel(fep->phy_speed, enet_addr + MCF_FEC_MSCR);
> +}
> +
> +static void mtip_setup_mac(struct net_device *dev)
> +{
> +	struct mtip_ndev_priv *priv = netdev_priv(dev);
> +	struct switch_enet_private *fep = priv->fep;
> +	unsigned char *iap, mac_addr[ETH_ALEN];
> +
> +	/* Use MAC address from DTS */
> +	iap = &fep->mac[priv->portnum - 1][0];
> +
> +	/* Use MAC address set by bootloader */
> +	if (!is_valid_ether_addr(iap)) {
> +		*((__be32 *)&mac_addr[0]) =
> +			cpu_to_be32(readl(fep->enet_addr + MCF_FEC_PALR));
> +		*((__be16 *)&mac_addr[4]) =
> +			cpu_to_be16(readl(fep->enet_addr +
> +					  MCF_FEC_PAUR) >> 16);
> +		iap = &mac_addr[0];
> +	}
> +
> +	/* Use random MAC address */
> +	if (!is_valid_ether_addr(iap)) {
> +		eth_hw_addr_random(dev);
> +		dev_info(&fep->pdev->dev, "Using random MAC address: %pM\n",
> +			 dev->dev_addr);
> +		iap = (unsigned char *)dev->dev_addr;
> +	}
> +
> +	/* Adjust MAC if using macaddr (and increment if needed) */
> +	eth_hw_addr_gen(dev, iap, priv->portnum - 1);
> +}
> +
> +/**
> + * crc8_calc - calculate CRC for MAC storage
> + *
> + * @pmacaddress: A 6-byte array with the MAC address. The first byte is
> + *               the first byte transmitted.
> + *
> + * Calculate Galois Field Arithmetic CRC for Polynom x^8+x^2+x+1.
> + * It omits the final shift in of 8 zeroes a "normal" CRC would do
> + * (getting the remainder).
> + *
> + *  Examples (hexadecimal values):<br>
> + *   10-11-12-13-14-15  => CRC=0xc2
> + *   10-11-cc-dd-ee-00  => CRC=0xe6
> + *
> + * Return: The 8-bit CRC in bits 7:0
> + */
> +static int crc8_calc(unsigned char *pmacaddress)
> +{
> +	int byt; /* byte index */
> +	int bit; /* bit index */
> +	int crc = 0x12;
> +	int inval;
> +
> +	for (byt = 0; byt < ETH_ALEN; byt++) {
> +		inval = (((int)pmacaddress[byt]) & 0xFF);
> +		/* shift bit 0 to bit 8 so all our bits
> +		 * travel through bit 8
> +		 * (simplifies below calc)
> +		 */
> +		inval <<= 8;
> +
> +		for (bit = 0; bit < 8; bit++) {
> +			/* next input bit comes into d7 after shift */
> +			crc |= inval & 0x100;
> +			if (crc & 0x01)
> +				/* before shift  */
> +				crc ^= 0x1C0;
> +
> +			crc >>= 1;
> +			inval >>= 1;
> +		}
> +	}
> +	/* upper bits are clean as we shifted in zeroes! */
> +	return crc;
> +}
> +
> +static void mtip_read_atable(struct switch_enet_private *fep, int index,
> +			     u32 *read_lo, u32 *read_hi)
> +{
> +	struct addr_table64b_entry *atable_base =
> +		fep->hwentry->mtip_table64b_entry;
> +
> +	*read_lo = readl(&atable_base[index].lo);
> +	*read_hi = readl(&atable_base[index].hi);
> +}
> +
> +static void mtip_write_atable(struct switch_enet_private *fep, int index,
> +			      u32 write_lo, u32 write_hi)
> +{
> +	struct addr_table64b_entry *atable_base =
> +		fep->hwentry->mtip_table64b_entry;
> +
> +	writel(write_lo, &atable_base[index].lo);
> +	writel(write_hi, &atable_base[index].hi);
> +}
> +
> +/**
> + * mtip_portinfofifo_read - Read element from receive FIFO
> + *
> + * @fep: Structure describing switch
> + *
> + * Read one element from the HW receive FIFO (Queue)
> + * if available and return it.
> + *
> + * Return: mtip_port_info or NULL if no data is available.
> + */
> +static
> +struct mtip_port_info *mtip_portinfofifo_read(struct switch_enet_private *fep)
> +{
> +	struct mtip_port_info *info = &fep->g_info;
> +	struct switch_t *fecp = fep->hwp;
> +	u32 reg;
> +
> +	reg = readl(&fecp->ESW_LSR);
> +	if (reg == 0) {
> +		dev_dbg(&fep->pdev->dev, "%s: ESW_LSR = 0x%x\n", __func__, reg);
> +		return NULL;
> +	}
> +
> +	/* read word from FIFO */
> +	info->maclo = readl(&fecp->ESW_LREC0);
> +	if (info->maclo == 0) {
> +		dev_dbg(&fep->pdev->dev, "%s: mac lo 0x%x\n", __func__,
> +			info->maclo);
> +		return NULL;
> +	}
> +
> +	/* read 2nd word from FIFO */
> +	reg = readl(&fecp->ESW_LREC1);
> +	info->machi = reg & 0xFFFF;
> +	info->hash  = (reg >> 16) & 0xFF;
> +	info->port  = (reg >> 24) & 0xF;
> +
> +	return info;
> +}
> +
> +static void mtip_atable_get_entry_port_number(struct switch_enet_private *fep,
> +					      unsigned char *mac_addr, u8 *port)
> +{
> +	int block_index, block_index_end, entry;
> +	u32 mac_addr_lo, mac_addr_hi;
> +	u32 read_lo, read_hi;
> +
> +	mac_addr_lo = (u32)((mac_addr[3] << 24) | (mac_addr[2] << 16) |
> +			    (mac_addr[1] << 8) | mac_addr[0]);
> +	mac_addr_hi = (u32)((mac_addr[5] << 8) | (mac_addr[4]));
> +
> +	block_index = GET_BLOCK_PTR(crc8_calc(mac_addr));
> +	block_index_end = block_index + ATABLE_ENTRY_PER_SLOT;
> +
> +	/* now search all the entries in the selected block */
> +	for (entry = block_index; entry < block_index_end; entry++) {
> +		mtip_read_atable(fep, entry, &read_lo, &read_hi);
> +		*port = MTIP_PORT_FORWARDING_INIT;
> +
> +		if (read_lo == mac_addr_lo &&
> +		    ((read_hi & 0x0000FFFF) ==
> +		     (mac_addr_hi & 0x0000FFFF))) {
> +			/* found the correct address */
> +			if ((read_hi & (1 << 16)) && (!(read_hi & (1 << 17))))
> +				*port = AT_EXTRACT_PORT(read_hi);
> +			break;
> +		}
> +	}
> +
> +	dev_dbg(&fep->pdev->dev, "%s: MAC: %pM PORT: 0x%x\n", __func__,
> +		mac_addr, *port);
> +}
> +
> +/* Clear complete MAC Look Up Table */
> +void mtip_clear_atable(struct switch_enet_private *fep)
> +{
> +	int index;
> +
> +	for (index = 0; index < 2048; index++)
There are a lot of defines for this magic number, please use one of them
> +		mtip_write_atable(fep, index, 0, 0);
> +}
> +
> +/**
> + * mtip_update_atable_static - Update switch static address table
> + *
> + * @mac_addr: Pointer to the array containing MAC address to
> + *            be put as static entry
> + * @port:     Port bitmask numbers to be added in static entry,
> + *            valid values are 1-7
> + * @priority: The priority for the static entry in table
> + *
> + * @fep:      Pointer to the structure describing the switch
> + *
> + * Updates MAC address lookup table with a static entry.
> + *
> + * Searches if the MAC address is already there in the block and replaces
> + * the older entry with the new one. If MAC address is not there then puts
> + * a new entry in the first empty slot available in the block.
> + *
> + * Return: 0 for a successful update else -ENOSPC when no slot available
> + */
> +static int mtip_update_atable_static(unsigned char *mac_addr, unsigned int port,
> +				     unsigned int priority,
> +				     struct switch_enet_private *fep)
> +{
> +	unsigned long block_index, entry, index_end;
> +	u32 write_lo, write_hi, read_lo, read_hi;
> +
> +	write_lo = (u32)((mac_addr[3] << 24) | (mac_addr[2] << 16) |
> +			 (mac_addr[1] << 8) | mac_addr[0]);
> +	write_hi = (u32)(0 | (port << AT_SENTRY_PORTMASK_shift) |
> +			 (priority << AT_SENTRY_PRIO_shift) |
> +			 (AT_ENTRY_TYPE_STATIC << AT_ENTRY_TYPE_shift) |
> +			 (AT_ENTRY_RECORD_VALID << AT_ENTRY_VALID_shift) |
> +			 (mac_addr[5] << 8) | (mac_addr[4]));
> +
> +	block_index = GET_BLOCK_PTR(crc8_calc(mac_addr));
> +	index_end = block_index + ATABLE_ENTRY_PER_SLOT;
> +	/* Now search all the entries in the selected block */
> +	for (entry = block_index; entry < index_end; entry++) {
> +		mtip_read_atable(fep, entry, &read_lo, &read_hi);
> +		/* MAC address matched, so update the
> +		 * existing entry
> +		 * even if its a dynamic one
> +		 */
> +		if (read_lo == write_lo &&
> +		    ((read_hi & 0x0000FFFF) ==
> +		     (write_hi & 0x0000FFFF))) {
> +			mtip_write_atable(fep, entry, write_lo, write_hi);
> +			return 0;
> +		} else if (!(read_hi & (1 << 16))) {
> +			/* Fill this empty slot (valid bit zero),
> +			 * assuming no holes in the block
> +			 */
> +			mtip_write_atable(fep, entry, write_lo, write_hi);
> +			fep->at_curr_entries++;
> +			return 0;
> +		}
> +	}
> +
> +	/* No space available for this static entry */
> +	return -ENOSPC;
> +}
> +
> +static bool mtip_update_atable_dynamic1(u32 write_lo, u32 write_hi,
> +					int block_index, unsigned int port,
> +					unsigned int curr_time,
> +					struct switch_enet_private *fep)
> +{
> +	unsigned long entry, index_end;
> +	int time, timeold, indexold;
> +	u32 read_lo, read_hi;
> +	unsigned long conf;
> +
> +	/* prepare update port and timestamp */
> +	conf = AT_ENTRY_RECORD_VALID << AT_ENTRY_VALID_shift;
> +	conf |= AT_ENTRY_TYPE_DYNAMIC << AT_ENTRY_TYPE_shift;
> +	conf |= curr_time << AT_DENTRY_TIME_shift;
> +	conf |= port << AT_DENTRY_PORT_shift;
> +	conf |= write_hi;
> +
> +	/* linear search through all slot
> +	 * entries and update if found
> +	 */
> +	index_end = block_index + ATABLE_ENTRY_PER_SLOT;
> +	/* Now search all the entries in the selected block */
> +	for (entry = block_index; entry < index_end; entry++) {
> +		mtip_read_atable(fep, entry, &read_lo, &read_hi);
> +		if (read_lo == write_lo &&
> +		    ((read_hi & 0x0000FFFF) ==
> +		     (write_hi & 0x0000FFFF))) {
> +			/* found correct address,
> +			 * update timestamp.
> +			 */
> +			mtip_write_atable(fep, entry, write_lo, conf);
> +
> +			return false;
> +		} else if (!(read_hi & (1 << 16))) {
> +			/* slot is empty, then use it
> +			 * for new entry
> +			 * Note: There are no holes,
> +			 * therefore cannot be any
> +			 * more that need to be compared.
> +			 */
> +			mtip_write_atable(fep, entry, write_lo, conf);
> +			/* statistics (we do it between writing
> +			 *  .hi an .lo due to
> +			 * hardware limitation...
> +			 */
> +			fep->at_curr_entries++;
> +			/* newly inserted */
> +
> +			return true;
> +		}
> +	}
> +
> +	/* No more entry available in block overwrite oldest */
> +	timeold = 0;
> +	indexold = 0;
> +	for (entry = block_index; entry < index_end; entry++) {
> +		mtip_read_atable(fep, entry, &read_lo, &read_hi);
> +		time = AT_EXTRACT_TIMESTAMP(read_hi);
> +		dev_dbg(&fep->pdev->dev, "%s : time %x currtime %x\n",
> +			__func__, time, curr_time);
> +		time = TIMEDELTA(curr_time, time);
> +		if (time > timeold) {
> +			/* is it older ? */
> +			timeold = time;
> +			indexold = entry;
> +		}
> +	}
> +
> +	mtip_write_atable(fep, indexold, write_lo, conf);
> +
> +	/* Statistics (do it inbetween writing to .lo and .hi */
> +	fep->at_block_overflows++;
> +	dev_err(&fep->pdev->dev, "%s update time, at_block_overflows %x\n",
> +		__func__, fep->at_block_overflows);
> +	/* newly inserted */
> +	return true;
> +}
> +
> +/* dynamicms MAC address table learn and migration */
> +static void
> +mtip_atable_dynamicms_learn_migration(struct switch_enet_private *fep,
> +				      int curr_time, unsigned char *mac,
> +				      u8 *rx_port)
> +{
> +	u8 port = MTIP_PORT_FORWARDING_INIT;
> +	struct mtip_port_info *port_info;
> +	u32 rx_mac_lo, rx_mac_hi;
> +	unsigned long flags;
> +	int index;
> +
> +	spin_lock_irqsave(&fep->learn_lock, flags);
> +
> +	if (mac && is_valid_ether_addr(mac)) {
> +		rx_mac_lo = (u32)((mac[3] << 24) | (mac[2] << 16) |
> +				  (mac[1] << 8) | mac[0]);
> +		rx_mac_hi = (u32)((mac[5] << 8) | (mac[4]));
> +	}
> +
> +	port_info = mtip_portinfofifo_read(fep);
> +	while (port_info) {
> +		/* get block index from lookup table */
> +		index = GET_BLOCK_PTR(port_info->hash);
> +		mtip_update_atable_dynamic1(port_info->maclo, port_info->machi,
> +					    index, port_info->port,
> +					    curr_time, fep);
> +
> +		if (mac && is_valid_ether_addr(mac) &&
> +		    port == MTIP_PORT_FORWARDING_INIT) {
> +			if (rx_mac_lo == port_info->maclo &&
> +			    rx_mac_hi == port_info->machi) {
> +				/* The newly learned MAC is the source of
> +				 * our filtered frame.
> +				 */
> +				port = (u8)port_info->port;
> +			}
> +		}
> +		port_info = mtip_portinfofifo_read(fep);
> +	}
> +
> +	if (rx_port)
> +		*rx_port = port;
> +
> +	spin_unlock_irqrestore(&fep->learn_lock, flags);
> +}
> +
> +static void mtip_aging_timer(struct timer_list *t)
> +{
> +	struct switch_enet_private *fep = from_timer(fep, t, timer_aging);
> +
> +	if (fep)
> +		fep->curr_time = mtip_timeincrement(fep->curr_time);
> +
> +	mod_timer(&fep->timer_aging,
> +		  jiffies + msecs_to_jiffies(LEARNING_AGING_INTERVAL));
I'm not sure that fep can actually be NULL. If it's possible please 
return in order to avoid a NULL pointer deference here, otherwise drop 
the check.

Thanks