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 |
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
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