diff options
Diffstat (limited to 'drivers/net/ethernet/microchip')
24 files changed, 1630 insertions, 946 deletions
diff --git a/drivers/net/ethernet/microchip/Kconfig b/drivers/net/ethernet/microchip/Kconfig index 43ba71e82260..ee046468652c 100644 --- a/drivers/net/ethernet/microchip/Kconfig +++ b/drivers/net/ethernet/microchip/Kconfig @@ -46,18 +46,21 @@ config LAN743X tristate "LAN743x support" depends on PCI depends on PTP_1588_CLOCK_OPTIONAL - select PHYLIB select FIXED_PHY select CRC16 select CRC32 + select PHYLINK help - Support for the Microchip LAN743x PCI Express Gigabit Ethernet chip + Support for the Microchip LAN743x and PCI11x1x families of PCI + Express Ethernet devices To compile this driver as a module, choose M here. The module will be called lan743x. +source "drivers/net/ethernet/microchip/lan865x/Kconfig" source "drivers/net/ethernet/microchip/lan966x/Kconfig" source "drivers/net/ethernet/microchip/sparx5/Kconfig" source "drivers/net/ethernet/microchip/vcap/Kconfig" +source "drivers/net/ethernet/microchip/fdma/Kconfig" endif # NET_VENDOR_MICROCHIP diff --git a/drivers/net/ethernet/microchip/Makefile b/drivers/net/ethernet/microchip/Makefile index bbd349264e6f..3c65baed9fd8 100644 --- a/drivers/net/ethernet/microchip/Makefile +++ b/drivers/net/ethernet/microchip/Makefile @@ -9,6 +9,8 @@ obj-$(CONFIG_LAN743X) += lan743x.o lan743x-objs := lan743x_main.o lan743x_ethtool.o lan743x_ptp.o +obj-$(CONFIG_LAN865X) += lan865x/ obj-$(CONFIG_LAN966X_SWITCH) += lan966x/ obj-$(CONFIG_SPARX5_SWITCH) += sparx5/ obj-$(CONFIG_VCAP) += vcap/ +obj-$(CONFIG_FDMA) += fdma/ diff --git a/drivers/net/ethernet/microchip/fdma/Kconfig b/drivers/net/ethernet/microchip/fdma/Kconfig new file mode 100644 index 000000000000..59159ad6701a --- /dev/null +++ b/drivers/net/ethernet/microchip/fdma/Kconfig @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Microchip FDMA API configuration +# + +if NET_VENDOR_MICROCHIP + +config FDMA + bool "FDMA API" + help + Provides the basic FDMA functionality for multiple Microchip + switchcores. + + Say Y here if you want to build the FDMA API that provides a common + set of functions and data structures for interacting with the Frame + DMA engine in multiple microchip switchcores. + +endif # NET_VENDOR_MICROCHIP diff --git a/drivers/net/ethernet/microchip/fdma/Makefile b/drivers/net/ethernet/microchip/fdma/Makefile new file mode 100644 index 000000000000..cc9a736be357 --- /dev/null +++ b/drivers/net/ethernet/microchip/fdma/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for Microchip FDMA +# + +obj-$(CONFIG_FDMA) += fdma.o +fdma-y += fdma_api.o diff --git a/drivers/net/ethernet/microchip/fdma/fdma_api.c b/drivers/net/ethernet/microchip/fdma/fdma_api.c new file mode 100644 index 000000000000..e78c3590da9e --- /dev/null +++ b/drivers/net/ethernet/microchip/fdma/fdma_api.c @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include "fdma_api.h" + +#include <linux/bits.h> +#include <linux/etherdevice.h> +#include <linux/types.h> + +/* Add a DB to a DCB, providing a callback for getting the DB dataptr. */ +static int __fdma_db_add(struct fdma *fdma, int dcb_idx, int db_idx, u64 status, + int (*cb)(struct fdma *fdma, int dcb_idx, + int db_idx, u64 *dataptr)) +{ + struct fdma_db *db = fdma_db_get(fdma, dcb_idx, db_idx); + + db->status = status; + + return cb(fdma, dcb_idx, db_idx, &db->dataptr); +} + +/* Add a DB to a DCB, using the callback set in the fdma_ops struct. */ +int fdma_db_add(struct fdma *fdma, int dcb_idx, int db_idx, u64 status) +{ + return __fdma_db_add(fdma, + dcb_idx, + db_idx, + status, + fdma->ops.dataptr_cb); +} + +/* Add a DCB with callbacks for getting the DB dataptr and the DCB nextptr. */ +int __fdma_dcb_add(struct fdma *fdma, int dcb_idx, u64 info, u64 status, + int (*dcb_cb)(struct fdma *fdma, int dcb_idx, u64 *nextptr), + int (*db_cb)(struct fdma *fdma, int dcb_idx, int db_idx, + u64 *dataptr)) +{ + struct fdma_dcb *dcb = fdma_dcb_get(fdma, dcb_idx); + int i, err; + + for (i = 0; i < fdma->n_dbs; i++) { + err = __fdma_db_add(fdma, dcb_idx, i, status, db_cb); + if (unlikely(err)) + return err; + } + + err = dcb_cb(fdma, dcb_idx, &fdma->last_dcb->nextptr); + if (unlikely(err)) + return err; + + fdma->last_dcb = dcb; + + dcb->nextptr = FDMA_DCB_INVALID_DATA; + dcb->info = info; + + return 0; +} +EXPORT_SYMBOL_GPL(__fdma_dcb_add); + +/* Add a DCB, using the preset callbacks in the fdma_ops struct. */ +int fdma_dcb_add(struct fdma *fdma, int dcb_idx, u64 info, u64 status) +{ + return __fdma_dcb_add(fdma, + dcb_idx, + info, status, + fdma->ops.nextptr_cb, + fdma->ops.dataptr_cb); +} +EXPORT_SYMBOL_GPL(fdma_dcb_add); + +/* Initialize the DCB's and DB's. */ +int fdma_dcbs_init(struct fdma *fdma, u64 info, u64 status) +{ + int i, err; + + fdma->last_dcb = fdma->dcbs; + fdma->db_index = 0; + fdma->dcb_index = 0; + + for (i = 0; i < fdma->n_dcbs; i++) { + err = fdma_dcb_add(fdma, i, info, status); + if (err) + return err; + } + + return 0; +} +EXPORT_SYMBOL_GPL(fdma_dcbs_init); + +/* Allocate coherent DMA memory for FDMA. */ +int fdma_alloc_coherent(struct device *dev, struct fdma *fdma) +{ + fdma->dcbs = dma_alloc_coherent(dev, + fdma->size, + &fdma->dma, + GFP_KERNEL); + if (!fdma->dcbs) + return -ENOMEM; + + return 0; +} +EXPORT_SYMBOL_GPL(fdma_alloc_coherent); + +/* Allocate physical memory for FDMA. */ +int fdma_alloc_phys(struct fdma *fdma) +{ + fdma->dcbs = kzalloc(fdma->size, GFP_KERNEL); + if (!fdma->dcbs) + return -ENOMEM; + + fdma->dma = virt_to_phys(fdma->dcbs); + + return 0; +} +EXPORT_SYMBOL_GPL(fdma_alloc_phys); + +/* Free coherent DMA memory. */ +void fdma_free_coherent(struct device *dev, struct fdma *fdma) +{ + dma_free_coherent(dev, fdma->size, fdma->dcbs, fdma->dma); +} +EXPORT_SYMBOL_GPL(fdma_free_coherent); + +/* Free virtual memory. */ +void fdma_free_phys(struct fdma *fdma) +{ + kfree(fdma->dcbs); +} +EXPORT_SYMBOL_GPL(fdma_free_phys); + +/* Get the size of the FDMA memory */ +u32 fdma_get_size(struct fdma *fdma) +{ + return ALIGN(sizeof(struct fdma_dcb) * fdma->n_dcbs, PAGE_SIZE); +} +EXPORT_SYMBOL_GPL(fdma_get_size); + +/* Get the size of the FDMA memory. This function is only applicable if the + * dataptr addresses and DCB's are in contiguous memory. + */ +u32 fdma_get_size_contiguous(struct fdma *fdma) +{ + return ALIGN(fdma->n_dcbs * sizeof(struct fdma_dcb) + + fdma->n_dcbs * fdma->n_dbs * fdma->db_size, + PAGE_SIZE); +} +EXPORT_SYMBOL_GPL(fdma_get_size_contiguous); diff --git a/drivers/net/ethernet/microchip/fdma/fdma_api.h b/drivers/net/ethernet/microchip/fdma/fdma_api.h new file mode 100644 index 000000000000..d91affe8bd98 --- /dev/null +++ b/drivers/net/ethernet/microchip/fdma/fdma_api.h @@ -0,0 +1,243 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef _FDMA_API_H_ +#define _FDMA_API_H_ + +#include <linux/bits.h> +#include <linux/etherdevice.h> +#include <linux/types.h> + +/* This provides a common set of functions and data structures for interacting + * with the Frame DMA engine on multiple Microchip switchcores. + * + * Frame DMA DCB format: + * + * +---------------------------+ + * | Next Ptr | + * +---------------------------+ + * | Reserved | Info | + * +---------------------------+ + * | Data0 Ptr | + * +---------------------------+ + * | Reserved | Status0 | + * +---------------------------+ + * | Data1 Ptr | + * +---------------------------+ + * | Reserved | Status1 | + * +---------------------------+ + * | Data2 Ptr | + * +---------------------------+ + * | Reserved | Status2 | + * |-------------|-------------| + * | | + * | | + * | | + * | | + * | | + * |---------------------------| + * | Data14 Ptr | + * +-------------|-------------+ + * | Reserved | Status14 | + * +-------------|-------------+ + * + * The data pointers points to the actual frame data to be received or sent. The + * addresses of the data pointers can, as of writing, be either a: DMA address, + * physical address or mapped address. + * + */ + +#define FDMA_DCB_INFO_DATAL(x) ((x) & GENMASK(15, 0)) +#define FDMA_DCB_INFO_TOKEN BIT(17) +#define FDMA_DCB_INFO_INTR BIT(18) +#define FDMA_DCB_INFO_SW(x) (((x) << 24) & GENMASK(31, 24)) + +#define FDMA_DCB_STATUS_BLOCKL(x) ((x) & GENMASK(15, 0)) +#define FDMA_DCB_STATUS_SOF BIT(16) +#define FDMA_DCB_STATUS_EOF BIT(17) +#define FDMA_DCB_STATUS_INTR BIT(18) +#define FDMA_DCB_STATUS_DONE BIT(19) +#define FDMA_DCB_STATUS_BLOCKO(x) (((x) << 20) & GENMASK(31, 20)) +#define FDMA_DCB_INVALID_DATA 0x1 + +#define FDMA_DB_MAX 15 /* Max number of DB's on Sparx5 */ + +struct fdma; + +struct fdma_db { + u64 dataptr; + u64 status; +}; + +struct fdma_dcb { + u64 nextptr; + u64 info; + struct fdma_db db[FDMA_DB_MAX]; +}; + +struct fdma_ops { + /* User-provided callback to set the dataptr */ + int (*dataptr_cb)(struct fdma *fdma, int dcb_idx, int db_idx, u64 *ptr); + /* User-provided callback to set the nextptr */ + int (*nextptr_cb)(struct fdma *fdma, int dcb_idx, u64 *ptr); +}; + +struct fdma { + void *priv; + + /* Virtual addresses */ + struct fdma_dcb *dcbs; + struct fdma_dcb *last_dcb; + + /* DMA address */ + dma_addr_t dma; + + /* Size of DCB + DB memory */ + int size; + + /* Indexes used to access the next-to-be-used DCB or DB */ + int db_index; + int dcb_index; + + /* Number of DCB's and DB's */ + u32 n_dcbs; + u32 n_dbs; + + /* Size of DB's */ + u32 db_size; + + /* Channel id this FDMA object operates on */ + u32 channel_id; + + struct fdma_ops ops; +}; + +/* Advance the DCB index and wrap if required. */ +static inline void fdma_dcb_advance(struct fdma *fdma) +{ + fdma->dcb_index++; + if (fdma->dcb_index >= fdma->n_dcbs) + fdma->dcb_index = 0; +} + +/* Advance the DB index. */ +static inline void fdma_db_advance(struct fdma *fdma) +{ + fdma->db_index++; +} + +/* Reset the db index to zero. */ +static inline void fdma_db_reset(struct fdma *fdma) +{ + fdma->db_index = 0; +} + +/* Check if a DCB can be reused in case of multiple DB's per DCB. */ +static inline bool fdma_dcb_is_reusable(struct fdma *fdma) +{ + return fdma->db_index != fdma->n_dbs; +} + +/* Check if the FDMA has marked this DB as done. */ +static inline bool fdma_db_is_done(struct fdma_db *db) +{ + return db->status & FDMA_DCB_STATUS_DONE; +} + +/* Get the length of a DB. */ +static inline int fdma_db_len_get(struct fdma_db *db) +{ + return FDMA_DCB_STATUS_BLOCKL(db->status); +} + +/* Set the length of a DB. */ +static inline void fdma_dcb_len_set(struct fdma_dcb *dcb, u32 len) +{ + dcb->info = FDMA_DCB_INFO_DATAL(len); +} + +/* Get a DB by index. */ +static inline struct fdma_db *fdma_db_get(struct fdma *fdma, int dcb_idx, + int db_idx) +{ + return &fdma->dcbs[dcb_idx].db[db_idx]; +} + +/* Get the next DB. */ +static inline struct fdma_db *fdma_db_next_get(struct fdma *fdma) +{ + return fdma_db_get(fdma, fdma->dcb_index, fdma->db_index); +} + +/* Get a DCB by index. */ +static inline struct fdma_dcb *fdma_dcb_get(struct fdma *fdma, int dcb_idx) +{ + return &fdma->dcbs[dcb_idx]; +} + +/* Get the next DCB. */ +static inline struct fdma_dcb *fdma_dcb_next_get(struct fdma *fdma) +{ + return fdma_dcb_get(fdma, fdma->dcb_index); +} + +/* Check if the FDMA has frames ready for extraction. */ +static inline bool fdma_has_frames(struct fdma *fdma) +{ + return fdma_db_is_done(fdma_db_next_get(fdma)); +} + +/* Get a nextptr by index */ +static inline int fdma_nextptr_cb(struct fdma *fdma, int dcb_idx, u64 *nextptr) +{ + *nextptr = fdma->dma + (sizeof(struct fdma_dcb) * dcb_idx); + return 0; +} + +/* Get the DMA address of a dataptr, by index. This function is only applicable + * if the dataptr addresses and DCB's are in contiguous memory and the driver + * supports XDP. + */ +static inline u64 fdma_dataptr_get_contiguous(struct fdma *fdma, int dcb_idx, + int db_idx) +{ + return fdma->dma + (sizeof(struct fdma_dcb) * fdma->n_dcbs) + + (dcb_idx * fdma->n_dbs + db_idx) * fdma->db_size + + XDP_PACKET_HEADROOM; +} + +/* Get the virtual address of a dataptr, by index. This function is only + * applicable if the dataptr addresses and DCB's are in contiguous memory and + * the driver supports XDP. + */ +static inline void *fdma_dataptr_virt_get_contiguous(struct fdma *fdma, + int dcb_idx, int db_idx) +{ + return (u8 *)fdma->dcbs + (sizeof(struct fdma_dcb) * fdma->n_dcbs) + + (dcb_idx * fdma->n_dbs + db_idx) * fdma->db_size + + XDP_PACKET_HEADROOM; +} + +/* Check if this DCB is the last used DCB. */ +static inline bool fdma_is_last(struct fdma *fdma, struct fdma_dcb *dcb) +{ + return dcb == fdma->last_dcb; +} + +int fdma_dcbs_init(struct fdma *fdma, u64 info, u64 status); +int fdma_db_add(struct fdma *fdma, int dcb_idx, int db_idx, u64 status); +int fdma_dcb_add(struct fdma *fdma, int dcb_idx, u64 info, u64 status); +int __fdma_dcb_add(struct fdma *fdma, int dcb_idx, u64 info, u64 status, + int (*dcb_cb)(struct fdma *fdma, int dcb_idx, u64 *nextptr), + int (*db_cb)(struct fdma *fdma, int dcb_idx, int db_idx, + u64 *dataptr)); + +int fdma_alloc_coherent(struct device *dev, struct fdma *fdma); +int fdma_alloc_phys(struct fdma *fdma); + +void fdma_free_coherent(struct device *dev, struct fdma *fdma); +void fdma_free_phys(struct fdma *fdma); + +u32 fdma_get_size(struct fdma *fdma); +u32 fdma_get_size_contiguous(struct fdma *fdma); + +#endif diff --git a/drivers/net/ethernet/microchip/lan743x_ethtool.c b/drivers/net/ethernet/microchip/lan743x_ethtool.c index 3a63ec091413..1a1cbd034eda 100644 --- a/drivers/net/ethernet/microchip/lan743x_ethtool.c +++ b/drivers/net/ethernet/microchip/lan743x_ethtool.c @@ -1034,16 +1034,12 @@ static int lan743x_ethtool_get_ts_info(struct net_device *netdev, struct lan743x_adapter *adapter = netdev_priv(netdev); ts_info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | - SOF_TIMESTAMPING_RX_SOFTWARE | - SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_TX_HARDWARE | SOF_TIMESTAMPING_RX_HARDWARE | SOF_TIMESTAMPING_RAW_HARDWARE; if (adapter->ptp.ptp_clock) ts_info->phc_index = ptp_clock_index(adapter->ptp.ptp_clock); - else - ts_info->phc_index = -1; ts_info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON) | @@ -1058,61 +1054,55 @@ static int lan743x_ethtool_get_eee(struct net_device *netdev, struct ethtool_keee *eee) { struct lan743x_adapter *adapter = netdev_priv(netdev); - struct phy_device *phydev = netdev->phydev; - u32 buf; - int ret; - - if (!phydev) - return -EIO; - if (!phydev->drv) { - netif_err(adapter, drv, adapter->netdev, - "Missing PHY Driver\n"); - return -EIO; - } - ret = phy_ethtool_get_eee(phydev, eee); - if (ret < 0) - return ret; + eee->tx_lpi_timer = lan743x_csr_read(adapter, + MAC_EEE_TX_LPI_REQ_DLY_CNT); - buf = lan743x_csr_read(adapter, MAC_CR); - if (buf & MAC_CR_EEE_EN_) { - /* EEE_TX_LPI_REQ_DLY & tx_lpi_timer are same uSec unit */ - buf = lan743x_csr_read(adapter, MAC_EEE_TX_LPI_REQ_DLY_CNT); - eee->tx_lpi_timer = buf; - } else { - eee->tx_lpi_timer = 0; - } - - return 0; + return phylink_ethtool_get_eee(adapter->phylink, eee); } static int lan743x_ethtool_set_eee(struct net_device *netdev, struct ethtool_keee *eee) { - struct lan743x_adapter *adapter; - struct phy_device *phydev; - u32 buf = 0; + struct lan743x_adapter *adapter = netdev_priv(netdev); + u32 tx_lpi_timer; - if (!netdev) - return -EINVAL; - adapter = netdev_priv(netdev); - if (!adapter) - return -EINVAL; - phydev = netdev->phydev; - if (!phydev) - return -EIO; - if (!phydev->drv) { - netif_err(adapter, drv, adapter->netdev, - "Missing PHY Driver\n"); - return -EIO; - } + tx_lpi_timer = lan743x_csr_read(adapter, MAC_EEE_TX_LPI_REQ_DLY_CNT); + if (tx_lpi_timer != eee->tx_lpi_timer) { + u32 mac_cr = lan743x_csr_read(adapter, MAC_CR); + + /* Software should only change this field when Energy Efficient + * Ethernet Enable (EEEEN) is cleared. + * This function will trigger an autonegotiation restart and + * eee will be reenabled during link up if eee was negotiated. + */ + lan743x_mac_eee_enable(adapter, false); + lan743x_csr_write(adapter, MAC_EEE_TX_LPI_REQ_DLY_CNT, + eee->tx_lpi_timer); - if (eee->eee_enabled) { - buf = (u32)eee->tx_lpi_timer; - lan743x_csr_write(adapter, MAC_EEE_TX_LPI_REQ_DLY_CNT, buf); + if (mac_cr & MAC_CR_EEE_EN_) + lan743x_mac_eee_enable(adapter, true); } - return phy_ethtool_set_eee(phydev, eee); + return phylink_ethtool_set_eee(adapter->phylink, eee); +} + +static int +lan743x_ethtool_set_link_ksettings(struct net_device *netdev, + const struct ethtool_link_ksettings *cmd) +{ + struct lan743x_adapter *adapter = netdev_priv(netdev); + + return phylink_ethtool_ksettings_set(adapter->phylink, cmd); +} + +static int +lan743x_ethtool_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd) +{ + struct lan743x_adapter *adapter = netdev_priv(netdev); + + return phylink_ethtool_ksettings_get(adapter->phylink, cmd); } #ifdef CONFIG_PM @@ -1124,8 +1114,7 @@ static void lan743x_ethtool_get_wol(struct net_device *netdev, wol->supported = 0; wol->wolopts = 0; - if (netdev->phydev) - phy_ethtool_get_wol(netdev->phydev, wol); + phylink_ethtool_get_wol(adapter->phylink, wol); if (wol->supported != adapter->phy_wol_supported) netif_warn(adapter, drv, adapter->netdev, @@ -1166,7 +1155,7 @@ static int lan743x_ethtool_set_wol(struct net_device *netdev, !(adapter->phy_wol_supported & WAKE_MAGICSECURE)) phy_wol.wolopts &= ~WAKE_MAGIC; - ret = phy_ethtool_set_wol(netdev->phydev, &phy_wol); + ret = phylink_ethtool_set_wol(adapter->phylink, wol); if (ret && (ret != -EOPNOTSUPP)) return ret; @@ -1355,44 +1344,16 @@ static void lan743x_get_pauseparam(struct net_device *dev, struct ethtool_pauseparam *pause) { struct lan743x_adapter *adapter = netdev_priv(dev); - struct lan743x_phy *phy = &adapter->phy; - if (phy->fc_request_control & FLOW_CTRL_TX) - pause->tx_pause = 1; - if (phy->fc_request_control & FLOW_CTRL_RX) - pause->rx_pause = 1; - pause->autoneg = phy->fc_autoneg; + phylink_ethtool_get_pauseparam(adapter->phylink, pause); } static int lan743x_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam *pause) { struct lan743x_adapter *adapter = netdev_priv(dev); - struct phy_device *phydev = dev->phydev; - struct lan743x_phy *phy = &adapter->phy; - - if (!phydev) - return -ENODEV; - - if (!phy_validate_pause(phydev, pause)) - return -EINVAL; - - phy->fc_request_control = 0; - if (pause->rx_pause) - phy->fc_request_control |= FLOW_CTRL_RX; - if (pause->tx_pause) - phy->fc_request_control |= FLOW_CTRL_TX; - - phy->fc_autoneg = pause->autoneg; - - if (pause->autoneg == AUTONEG_DISABLE) - lan743x_mac_flow_ctrl_set_enables(adapter, pause->tx_pause, - pause->rx_pause); - else - phy_set_asym_pause(phydev, pause->rx_pause, pause->tx_pause); - - return 0; + return phylink_ethtool_set_pauseparam(adapter->phylink, pause); } const struct ethtool_ops lan743x_ethtool_ops = { @@ -1417,8 +1378,8 @@ const struct ethtool_ops lan743x_ethtool_ops = { .get_ts_info = lan743x_ethtool_get_ts_info, .get_eee = lan743x_ethtool_get_eee, .set_eee = lan743x_ethtool_set_eee, - .get_link_ksettings = phy_ethtool_get_link_ksettings, - .set_link_ksettings = phy_ethtool_set_link_ksettings, + .get_link_ksettings = lan743x_ethtool_get_link_ksettings, + .set_link_ksettings = lan743x_ethtool_set_link_ksettings, .get_regs_len = lan743x_get_regs_len, .get_regs = lan743x_get_regs, .get_pauseparam = lan743x_get_pauseparam, diff --git a/drivers/net/ethernet/microchip/lan743x_main.c b/drivers/net/ethernet/microchip/lan743x_main.c index e418539565b1..4dc5adcda6a3 100644 --- a/drivers/net/ethernet/microchip/lan743x_main.c +++ b/drivers/net/ethernet/microchip/lan743x_main.c @@ -15,6 +15,7 @@ #include <linux/rtnetlink.h> #include <linux/iopoll.h> #include <linux/crc16.h> +#include <linux/phylink.h> #include "lan743x_main.h" #include "lan743x_ethtool.h" @@ -992,6 +993,42 @@ static int lan743x_sgmii_write(struct lan743x_adapter *adapter, return ret; } +static int lan743x_get_lsd(int speed, int duplex, u8 mss) +{ + int lsd; + + switch (speed) { + case SPEED_2500: + if (mss == MASTER_SLAVE_STATE_SLAVE) + lsd = LINK_2500_SLAVE; + else + lsd = LINK_2500_MASTER; + break; + case SPEED_1000: + if (mss == MASTER_SLAVE_STATE_SLAVE) + lsd = LINK_1000_SLAVE; + else + lsd = LINK_1000_MASTER; + break; + case SPEED_100: + if (duplex == DUPLEX_FULL) + lsd = LINK_100FD; + else + lsd = LINK_100HD; + break; + case SPEED_10: + if (duplex == DUPLEX_FULL) + lsd = LINK_10FD; + else + lsd = LINK_10HD; + break; + default: + lsd = -EINVAL; + } + + return lsd; +} + static int lan743x_sgmii_mpll_set(struct lan743x_adapter *adapter, u16 baud) { @@ -1041,26 +1078,7 @@ static int lan743x_sgmii_2_5G_mode_set(struct lan743x_adapter *adapter, VR_MII_BAUD_RATE_1P25GBPS); } -static int lan743x_is_sgmii_2_5G_mode(struct lan743x_adapter *adapter, - bool *status) -{ - int ret; - - ret = lan743x_sgmii_read(adapter, MDIO_MMD_VEND2, - VR_MII_GEN2_4_MPLL_CTRL1); - if (ret < 0) - return ret; - - if (ret == VR_MII_MPLL_MULTIPLIER_125 || - ret == VR_MII_MPLL_MULTIPLIER_50) - *status = true; - else - *status = false; - - return 0; -} - -static int lan743x_sgmii_aneg_update(struct lan743x_adapter *adapter) +static int lan743x_serdes_clock_and_aneg_update(struct lan743x_adapter *adapter) { enum lan743x_sgmii_lsd lsd = adapter->sgmii_lsd; int mii_ctrl; @@ -1147,68 +1165,11 @@ static int lan743x_pcs_seq_state(struct lan743x_adapter *adapter, u8 state) return 0; } -static int lan743x_sgmii_config(struct lan743x_adapter *adapter) +static int lan743x_pcs_power_reset(struct lan743x_adapter *adapter) { - struct net_device *netdev = adapter->netdev; - struct phy_device *phydev = netdev->phydev; - enum lan743x_sgmii_lsd lsd = POWER_DOWN; int mii_ctl; - bool status; int ret; - switch (phydev->speed) { - case SPEED_2500: - if (phydev->master_slave_state == MASTER_SLAVE_STATE_MASTER) - lsd = LINK_2500_MASTER; - else - lsd = LINK_2500_SLAVE; - break; - case SPEED_1000: - if (phydev->master_slave_state == MASTER_SLAVE_STATE_MASTER) - lsd = LINK_1000_MASTER; - else - lsd = LINK_1000_SLAVE; - break; - case SPEED_100: - if (phydev->duplex) - lsd = LINK_100FD; - else - lsd = LINK_100HD; - break; - case SPEED_10: - if (phydev->duplex) - lsd = LINK_10FD; - else - lsd = LINK_10HD; - break; - default: - netif_err(adapter, drv, adapter->netdev, - "Invalid speed %d\n", phydev->speed); - return -EINVAL; - } - - adapter->sgmii_lsd = lsd; - ret = lan743x_sgmii_aneg_update(adapter); - if (ret < 0) { - netif_err(adapter, drv, adapter->netdev, - "error %d SGMII cfg failed\n", ret); - return ret; - } - - ret = lan743x_is_sgmii_2_5G_mode(adapter, &status); - if (ret < 0) { - netif_err(adapter, drv, adapter->netdev, - "error %d SGMII get mode failed\n", ret); - return ret; - } - - if (status) - netif_dbg(adapter, drv, adapter->netdev, - "SGMII 2.5G mode enable\n"); - else - netif_dbg(adapter, drv, adapter->netdev, - "SGMII 1G mode enable\n"); - /* SGMII/1000/2500BASE-X PCS power down */ mii_ctl = lan743x_sgmii_read(adapter, MDIO_MMD_VEND2, MII_BMCR); if (mii_ctl < 0) @@ -1229,11 +1190,7 @@ static int lan743x_sgmii_config(struct lan743x_adapter *adapter) if (ret < 0) return ret; - ret = lan743x_pcs_seq_state(adapter, PCS_POWER_STATE_UP); - if (ret < 0) - return ret; - - return 0; + return lan743x_pcs_seq_state(adapter, PCS_POWER_STATE_UP); } static void lan743x_mac_set_address(struct lan743x_adapter *adapter, @@ -1389,103 +1346,11 @@ static int lan743x_phy_reset(struct lan743x_adapter *adapter) 50000, 1000000); } -static void lan743x_phy_update_flowcontrol(struct lan743x_adapter *adapter, - u16 local_adv, u16 remote_adv) -{ - struct lan743x_phy *phy = &adapter->phy; - u8 cap; - - if (phy->fc_autoneg) - cap = mii_resolve_flowctrl_fdx(local_adv, remote_adv); - else - cap = phy->fc_request_control; - - lan743x_mac_flow_ctrl_set_enables(adapter, - cap & FLOW_CTRL_TX, - cap & FLOW_CTRL_RX); -} - static int lan743x_phy_init(struct lan743x_adapter *adapter) { return lan743x_phy_reset(adapter); } -static void lan743x_phy_link_status_change(struct net_device *netdev) -{ - struct lan743x_adapter *adapter = netdev_priv(netdev); - struct phy_device *phydev = netdev->phydev; - u32 data; - - phy_print_status(phydev); - if (phydev->state == PHY_RUNNING) { - int remote_advertisement = 0; - int local_advertisement = 0; - - data = lan743x_csr_read(adapter, MAC_CR); - - /* set duplex mode */ - if (phydev->duplex) - data |= MAC_CR_DPX_; - else - data &= ~MAC_CR_DPX_; - - /* set bus speed */ - switch (phydev->speed) { - case SPEED_10: - data &= ~MAC_CR_CFG_H_; - data &= ~MAC_CR_CFG_L_; - break; - case SPEED_100: - data &= ~MAC_CR_CFG_H_; - data |= MAC_CR_CFG_L_; - break; - case SPEED_1000: - data |= MAC_CR_CFG_H_; - data &= ~MAC_CR_CFG_L_; - break; - case SPEED_2500: - data |= MAC_CR_CFG_H_; - data |= MAC_CR_CFG_L_; - break; - } - lan743x_csr_write(adapter, MAC_CR, data); - - local_advertisement = - linkmode_adv_to_mii_adv_t(phydev->advertising); - remote_advertisement = - linkmode_adv_to_mii_adv_t(phydev->lp_advertising); - - lan743x_phy_update_flowcontrol(adapter, local_advertisement, - remote_advertisement); - lan743x_ptp_update_latency(adapter, phydev->speed); - if (phydev->interface == PHY_INTERFACE_MODE_SGMII || - phydev->interface == PHY_INTERFACE_MODE_1000BASEX || - phydev->interface == PHY_INTERFACE_MODE_2500BASEX) - lan743x_sgmii_config(adapter); - - data = lan743x_csr_read(adapter, MAC_CR); - if (phydev->enable_tx_lpi) - data |= MAC_CR_EEE_EN_; - else - data &= ~MAC_CR_EEE_EN_; - lan743x_csr_write(adapter, MAC_CR, data); - } -} - -static void lan743x_phy_close(struct lan743x_adapter *adapter) -{ - struct net_device *netdev = adapter->netdev; - struct phy_device *phydev = netdev->phydev; - - phy_stop(netdev->phydev); - phy_disconnect(netdev->phydev); - - /* using phydev here as phy_disconnect NULLs netdev->phydev */ - if (phy_is_pseudo_fixed_link(phydev)) - fixed_phy_unregister(phydev); - -} - static void lan743x_phy_interface_select(struct lan743x_adapter *adapter) { u32 id_rev; @@ -1502,65 +1367,9 @@ static void lan743x_phy_interface_select(struct lan743x_adapter *adapter) adapter->phy_interface = PHY_INTERFACE_MODE_MII; else adapter->phy_interface = PHY_INTERFACE_MODE_RGMII; -} - -static int lan743x_phy_open(struct lan743x_adapter *adapter) -{ - struct net_device *netdev = adapter->netdev; - struct lan743x_phy *phy = &adapter->phy; - struct fixed_phy_status fphy_status = { - .link = 1, - .speed = SPEED_1000, - .duplex = DUPLEX_FULL, - }; - struct phy_device *phydev; - int ret = -EIO; - - /* try devicetree phy, or fixed link */ - phydev = of_phy_get_and_connect(netdev, adapter->pdev->dev.of_node, - lan743x_phy_link_status_change); - - if (!phydev) { - /* try internal phy */ - phydev = phy_find_first(adapter->mdiobus); - if (!phydev) { - if ((adapter->csr.id_rev & ID_REV_ID_MASK_) == - ID_REV_ID_LAN7431_) { - phydev = fixed_phy_register(PHY_POLL, - &fphy_status, NULL); - if (IS_ERR(phydev)) { - netdev_err(netdev, "No PHY/fixed_PHY found\n"); - return PTR_ERR(phydev); - } - } else { - goto return_error; - } - } - - lan743x_phy_interface_select(adapter); - - ret = phy_connect_direct(netdev, phydev, - lan743x_phy_link_status_change, - adapter->phy_interface); - if (ret) - goto return_error; - } - - /* MAC doesn't support 1000T Half */ - phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_1000baseT_Half_BIT); - - /* support both flow controls */ - phy_support_asym_pause(phydev); - phy->fc_request_control = (FLOW_CTRL_RX | FLOW_CTRL_TX); - phy->fc_autoneg = phydev->autoneg; - - phy_start(phydev); - phy_start_aneg(phydev); - phy_attached_info(phydev); - return 0; -return_error: - return ret; + netif_dbg(adapter, drv, adapter->netdev, + "selected phy interface: 0x%X\n", adapter->phy_interface); } static void lan743x_rfe_open(struct lan743x_adapter *adapter) @@ -3061,6 +2870,336 @@ return_error: return ret; } +static int lan743x_phylink_sgmii_config(struct lan743x_adapter *adapter) +{ + u32 sgmii_ctl; + int ret; + + ret = lan743x_get_lsd(SPEED_1000, DUPLEX_FULL, + MASTER_SLAVE_STATE_MASTER); + if (ret < 0) { + netif_err(adapter, drv, adapter->netdev, + "error %d link-speed-duplex(LSD) invalid\n", ret); + return ret; + } + + adapter->sgmii_lsd = ret; + netif_dbg(adapter, drv, adapter->netdev, + "Link Speed Duplex (lsd) : 0x%X\n", adapter->sgmii_lsd); + + /* LINK_STATUS_SOURCE from the External PHY via SGMII */ + sgmii_ctl = lan743x_csr_read(adapter, SGMII_CTL); + sgmii_ctl &= ~SGMII_CTL_LINK_STATUS_SOURCE_; + lan743x_csr_write(adapter, SGMII_CTL, sgmii_ctl); + + ret = lan743x_serdes_clock_and_aneg_update(adapter); + if (ret < 0) { + netif_err(adapter, drv, adapter->netdev, + "error %d sgmii aneg update failed\n", ret); + return ret; + } + + return lan743x_pcs_power_reset(adapter); +} + +static int lan743x_phylink_1000basex_config(struct lan743x_adapter *adapter) +{ + u32 sgmii_ctl; + int ret; + + ret = lan743x_get_lsd(SPEED_1000, DUPLEX_FULL, + MASTER_SLAVE_STATE_MASTER); + if (ret < 0) { + netif_err(adapter, drv, adapter->netdev, + "error %d link-speed-duplex(LSD) invalid\n", ret); + return ret; + } + + adapter->sgmii_lsd = ret; + netif_dbg(adapter, drv, adapter->netdev, + "Link Speed Duplex (lsd) : 0x%X\n", adapter->sgmii_lsd); + + /* LINK_STATUS_SOURCE from 1000BASE-X PCS link status */ + sgmii_ctl = lan743x_csr_read(adapter, SGMII_CTL); + sgmii_ctl |= SGMII_CTL_LINK_STATUS_SOURCE_; + lan743x_csr_write(adapter, SGMII_CTL, sgmii_ctl); + + ret = lan743x_serdes_clock_and_aneg_update(adapter); + if (ret < 0) { + netif_err(adapter, drv, adapter->netdev, + "error %d 1000basex aneg update failed\n", ret); + return ret; + } + + return lan743x_pcs_power_reset(adapter); +} + +static int lan743x_phylink_2500basex_config(struct lan743x_adapter *adapter) +{ + u32 sgmii_ctl; + int ret; + + ret = lan743x_get_lsd(SPEED_2500, DUPLEX_FULL, + MASTER_SLAVE_STATE_MASTER); + if (ret < 0) { + netif_err(adapter, drv, adapter->netdev, + "error %d link-speed-duplex(LSD) invalid\n", ret); + return ret; + } + + adapter->sgmii_lsd = ret; + netif_dbg(adapter, drv, adapter->netdev, + "Link Speed Duplex (lsd) : 0x%X\n", adapter->sgmii_lsd); + + /* LINK_STATUS_SOURCE from 2500BASE-X PCS link status */ + sgmii_ctl = lan743x_csr_read(adapter, SGMII_CTL); + sgmii_ctl |= SGMII_CTL_LINK_STATUS_SOURCE_; + lan743x_csr_write(adapter, SGMII_CTL, sgmii_ctl); + + ret = lan743x_serdes_clock_and_aneg_update(adapter); + if (ret < 0) { + netif_err(adapter, drv, adapter->netdev, + "error %d 2500basex aneg update failed\n", ret); + return ret; + } + + return lan743x_pcs_power_reset(adapter); +} + +void lan743x_mac_eee_enable(struct lan743x_adapter *adapter, bool enable) +{ + u32 mac_cr; + + mac_cr = lan743x_csr_read(adapter, MAC_CR); + if (enable) + mac_cr |= MAC_CR_EEE_EN_; + else + mac_cr &= ~MAC_CR_EEE_EN_; + lan743x_csr_write(adapter, MAC_CR, mac_cr); +} + +static void lan743x_phylink_mac_config(struct phylink_config *config, + unsigned int link_an_mode, + const struct phylink_link_state *state) +{ + struct net_device *netdev = to_net_dev(config->dev); + struct lan743x_adapter *adapter = netdev_priv(netdev); + int ret; + + switch (state->interface) { + case PHY_INTERFACE_MODE_2500BASEX: + ret = lan743x_phylink_2500basex_config(adapter); + if (ret < 0) + netif_err(adapter, drv, adapter->netdev, + "2500BASEX config failed. Error %d\n", ret); + else + netif_dbg(adapter, drv, adapter->netdev, + "2500BASEX mode selected and configured\n"); + break; + case PHY_INTERFACE_MODE_1000BASEX: + ret = lan743x_phylink_1000basex_config(adapter); + if (ret < 0) + netif_err(adapter, drv, adapter->netdev, + "1000BASEX config failed. Error %d\n", ret); + else + netif_dbg(adapter, drv, adapter->netdev, + "1000BASEX mode selected and configured\n"); + break; + case PHY_INTERFACE_MODE_SGMII: + ret = lan743x_phylink_sgmii_config(adapter); + if (ret < 0) + netif_err(adapter, drv, adapter->netdev, + "SGMII config failed. Error %d\n", ret); + else + netif_dbg(adapter, drv, adapter->netdev, + "SGMII mode selected and configured\n"); + break; + default: + netif_dbg(adapter, drv, adapter->netdev, + "RGMII/GMII/MII(0x%X) mode enable\n", + state->interface); + break; + } +} + +static void lan743x_phylink_mac_link_down(struct phylink_config *config, + unsigned int link_an_mode, + phy_interface_t interface) +{ + struct net_device *netdev = to_net_dev(config->dev); + struct lan743x_adapter *adapter = netdev_priv(netdev); + + netif_tx_stop_all_queues(to_net_dev(config->dev)); + lan743x_mac_eee_enable(adapter, false); +} + +static void lan743x_phylink_mac_link_up(struct phylink_config *config, + struct phy_device *phydev, + unsigned int link_an_mode, + phy_interface_t interface, + int speed, int duplex, + bool tx_pause, bool rx_pause) +{ + struct net_device *netdev = to_net_dev(config->dev); + struct lan743x_adapter *adapter = netdev_priv(netdev); + int mac_cr; + u8 cap; + + mac_cr = lan743x_csr_read(adapter, MAC_CR); + /* Pre-initialize register bits. + * Resulting value corresponds to SPEED_10 + */ + mac_cr &= ~(MAC_CR_CFG_H_ | MAC_CR_CFG_L_); + if (speed == SPEED_2500) + mac_cr |= MAC_CR_CFG_H_ | MAC_CR_CFG_L_; + else if (speed == SPEED_1000) + mac_cr |= MAC_CR_CFG_H_; + else if (speed == SPEED_100) + mac_cr |= MAC_CR_CFG_L_; + + lan743x_csr_write(adapter, MAC_CR, mac_cr); + + lan743x_ptp_update_latency(adapter, speed); + + /* Flow Control operation */ + cap = 0; + if (tx_pause) + cap |= FLOW_CTRL_TX; + if (rx_pause) + cap |= FLOW_CTRL_RX; + + lan743x_mac_flow_ctrl_set_enables(adapter, + cap & FLOW_CTRL_TX, + cap & FLOW_CTRL_RX); + + if (phydev) + lan743x_mac_eee_enable(adapter, phydev->enable_tx_lpi); + + netif_tx_wake_all_queues(netdev); +} + +static const struct phylink_mac_ops lan743x_phylink_mac_ops = { + .mac_config = lan743x_phylink_mac_config, + .mac_link_down = lan743x_phylink_mac_link_down, + .mac_link_up = lan743x_phylink_mac_link_up, +}; + +static int lan743x_phylink_create(struct lan743x_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + struct phylink *pl; + + adapter->phylink_config.dev = &netdev->dev; + adapter->phylink_config.type = PHYLINK_NETDEV; + adapter->phylink_config.mac_managed_pm = false; + + adapter->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | + MAC_SYM_PAUSE | MAC_10 | MAC_100 | MAC_1000FD; + + lan743x_phy_interface_select(adapter); + + switch (adapter->phy_interface) { + case PHY_INTERFACE_MODE_SGMII: + __set_bit(PHY_INTERFACE_MODE_SGMII, + adapter->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, + adapter->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, + adapter->phylink_config.supported_interfaces); + adapter->phylink_config.mac_capabilities |= MAC_2500FD; + break; + case PHY_INTERFACE_MODE_GMII: + __set_bit(PHY_INTERFACE_MODE_GMII, + adapter->phylink_config.supported_interfaces); + break; + case PHY_INTERFACE_MODE_MII: + __set_bit(PHY_INTERFACE_MODE_MII, + adapter->phylink_config.supported_interfaces); + break; + default: + phy_interface_set_rgmii(adapter->phylink_config.supported_interfaces); + } + + pl = phylink_create(&adapter->phylink_config, NULL, + adapter->phy_interface, &lan743x_phylink_mac_ops); + + if (IS_ERR(pl)) { + netdev_err(netdev, "Could not create phylink (%pe)\n", pl); + return PTR_ERR(pl); + } + + adapter->phylink = pl; + netdev_dbg(netdev, "lan743x phylink created"); + + return 0; +} + +static bool lan743x_phy_handle_exists(struct device_node *dn) +{ + dn = of_parse_phandle(dn, "phy-handle", 0); + of_node_put(dn); + return dn != NULL; +} + +static int lan743x_phylink_connect(struct lan743x_adapter *adapter) +{ + struct device_node *dn = adapter->pdev->dev.of_node; + struct net_device *dev = adapter->netdev; + struct phy_device *phydev; + int ret; + + if (dn) + ret = phylink_of_phy_connect(adapter->phylink, dn, 0); + + if (!dn || (ret && !lan743x_phy_handle_exists(dn))) { + phydev = phy_find_first(adapter->mdiobus); + if (phydev) { + /* attach the mac to the phy */ + ret = phylink_connect_phy(adapter->phylink, phydev); + } else if (((adapter->csr.id_rev & ID_REV_ID_MASK_) == + ID_REV_ID_LAN7431_) || adapter->is_pci11x1x) { + struct phylink_link_state state; + unsigned long caps; + + caps = adapter->phylink_config.mac_capabilities; + if (caps & MAC_2500FD) { + state.speed = SPEED_2500; + state.duplex = DUPLEX_FULL; + } else if (caps & MAC_1000FD) { + state.speed = SPEED_1000; + state.duplex = DUPLEX_FULL; + } else { + state.speed = SPEED_UNKNOWN; + state.duplex = DUPLEX_UNKNOWN; + } + + ret = phylink_set_fixed_link(adapter->phylink, &state); + if (ret) { + netdev_err(dev, "Could not set fixed link\n"); + return ret; + } + } else { + netdev_err(dev, "no PHY found\n"); + return -ENXIO; + } + } + + if (ret) { + netdev_err(dev, "Could not attach PHY (%d)\n", ret); + return ret; + } + + phylink_start(adapter->phylink); + + return 0; +} + +static void lan743x_phylink_disconnect(struct lan743x_adapter *adapter) +{ + phylink_stop(adapter->phylink); + phylink_disconnect_phy(adapter->phylink); +} + static int lan743x_netdev_close(struct net_device *netdev) { struct lan743x_adapter *adapter = netdev_priv(netdev); @@ -3074,7 +3213,7 @@ static int lan743x_netdev_close(struct net_device *netdev) lan743x_ptp_close(adapter); - lan743x_phy_close(adapter); + lan743x_phylink_disconnect(adapter); lan743x_mac_close(adapter); @@ -3097,13 +3236,13 @@ static int lan743x_netdev_open(struct net_device *netdev) if (ret) goto close_intr; - ret = lan743x_phy_open(adapter); + ret = lan743x_phylink_connect(adapter); if (ret) goto close_mac; ret = lan743x_ptp_open(adapter); if (ret) - goto close_phy; + goto close_mac; lan743x_rfe_open(adapter); @@ -3119,6 +3258,9 @@ static int lan743x_netdev_open(struct net_device *netdev) goto close_tx; } + if (netdev->phydev) + phy_support_eee(netdev->phydev); + #ifdef CONFIG_PM if (adapter->netdev->phydev) { struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL }; @@ -3143,9 +3285,8 @@ close_rx: lan743x_rx_close(&adapter->rx[index]); } lan743x_ptp_close(adapter); - -close_phy: - lan743x_phy_close(adapter); + if (adapter->phylink) + lan743x_phylink_disconnect(adapter); close_mac: lan743x_mac_close(adapter); @@ -3174,11 +3315,14 @@ static netdev_tx_t lan743x_netdev_xmit_frame(struct sk_buff *skb, static int lan743x_netdev_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) { + struct lan743x_adapter *adapter = netdev_priv(netdev); + if (!netif_running(netdev)) return -EINVAL; if (cmd == SIOCSHWTSTAMP) return lan743x_ptp_ioctl(netdev, ifr, cmd); - return phy_mii_ioctl(netdev->phydev, ifr, cmd); + + return phylink_mii_ioctl(adapter->phylink, ifr, cmd); } static void lan743x_netdev_set_multicast(struct net_device *netdev) @@ -3283,10 +3427,17 @@ static void lan743x_mdiobus_cleanup(struct lan743x_adapter *adapter) mdiobus_unregister(adapter->mdiobus); } +static void lan743x_destroy_phylink(struct lan743x_adapter *adapter) +{ + phylink_destroy(adapter->phylink); + adapter->phylink = NULL; +} + static void lan743x_full_cleanup(struct lan743x_adapter *adapter) { unregister_netdev(adapter->netdev); + lan743x_destroy_phylink(adapter); lan743x_mdiobus_cleanup(adapter); lan743x_hardware_cleanup(adapter); lan743x_pci_cleanup(adapter); @@ -3500,14 +3651,21 @@ static int lan743x_pcidev_probe(struct pci_dev *pdev, NETIF_F_HW_CSUM | NETIF_F_RXCSUM; adapter->netdev->hw_features = adapter->netdev->features; - /* carrier off reporting is important to ethtool even BEFORE open */ - netif_carrier_off(netdev); + ret = lan743x_phylink_create(adapter); + if (ret < 0) { + netif_err(adapter, probe, netdev, + "failed to setup phylink (%d)\n", ret); + goto cleanup_mdiobus; + } ret = register_netdev(adapter->netdev); if (ret < 0) - goto cleanup_mdiobus; + goto cleanup_phylink; return 0; +cleanup_phylink: + lan743x_destroy_phylink(adapter); + cleanup_mdiobus: lan743x_mdiobus_cleanup(adapter); @@ -3763,6 +3921,7 @@ static int lan743x_pm_resume(struct device *dev) MAC_WK_SRC_WK_FR_SAVED_; lan743x_csr_write(adapter, MAC_WK_SRC, data); + rtnl_lock(); /* open netdev when netdev is at running state while resume. * For instance, it is true when system wakesup after pm-suspend * However, it is false when system wakes up after suspend GUI menu @@ -3771,6 +3930,7 @@ static int lan743x_pm_resume(struct device *dev) lan743x_netdev_open(netdev); netif_device_attach(netdev); + rtnl_unlock(); return 0; } diff --git a/drivers/net/ethernet/microchip/lan743x_main.h b/drivers/net/ethernet/microchip/lan743x_main.h index 3b2585a384e2..8ef897c114d3 100644 --- a/drivers/net/ethernet/microchip/lan743x_main.h +++ b/drivers/net/ethernet/microchip/lan743x_main.h @@ -5,6 +5,7 @@ #define _LAN743X_H #include <linux/phy.h> +#include <linux/phylink.h> #include "lan743x_ptp.h" #define DRIVER_AUTHOR "Bryan Whitehead <Bryan.Whitehead@microchip.com>" @@ -1083,6 +1084,8 @@ struct lan743x_adapter { u32 flags; u32 hw_cfg; phy_interface_t phy_interface; + struct phylink *phylink; + struct phylink_config phylink_config; }; #define LAN743X_COMPONENT_FLAG_RX(channel) BIT(20 + (channel)) @@ -1203,5 +1206,6 @@ void lan743x_hs_syslock_release(struct lan743x_adapter *adapter); void lan743x_mac_flow_ctrl_set_enables(struct lan743x_adapter *adapter, bool tx_enable, bool rx_enable); int lan743x_sgmii_read(struct lan743x_adapter *adapter, u8 mmd, u16 addr); +void lan743x_mac_eee_enable(struct lan743x_adapter *adapter, bool enable); #endif /* _LAN743X_H */ diff --git a/drivers/net/ethernet/microchip/lan865x/Kconfig b/drivers/net/ethernet/microchip/lan865x/Kconfig new file mode 100644 index 000000000000..7f2a4e7e1915 --- /dev/null +++ b/drivers/net/ethernet/microchip/lan865x/Kconfig @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Microchip LAN865x Driver Support +# + +if NET_VENDOR_MICROCHIP + +config LAN865X + tristate "LAN865x support" + depends on SPI + select OA_TC6 + help + Support for the Microchip LAN8650/1 Rev.B0/B1 MACPHY Ethernet chip. It + uses OPEN Alliance 10BASE-T1x Serial Interface specification. + + To compile this driver as a module, choose M here. The module will be + called lan865x. + +endif # NET_VENDOR_MICROCHIP diff --git a/drivers/net/ethernet/microchip/lan865x/Makefile b/drivers/net/ethernet/microchip/lan865x/Makefile new file mode 100644 index 000000000000..9f5dd89c1eb8 --- /dev/null +++ b/drivers/net/ethernet/microchip/lan865x/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for the Microchip LAN865x Driver +# + +obj-$(CONFIG_LAN865X) += lan865x.o diff --git a/drivers/net/ethernet/microchip/lan865x/lan865x.c b/drivers/net/ethernet/microchip/lan865x/lan865x.c new file mode 100644 index 000000000000..dd436bdff0f8 --- /dev/null +++ b/drivers/net/ethernet/microchip/lan865x/lan865x.c @@ -0,0 +1,429 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Microchip's LAN865x 10BASE-T1S MAC-PHY driver + * + * Author: Parthiban Veerasooran <parthiban.veerasooran@microchip.com> + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/phy.h> +#include <linux/oa_tc6.h> + +#define DRV_NAME "lan8650" + +/* MAC Network Control Register */ +#define LAN865X_REG_MAC_NET_CTL 0x00010000 +#define MAC_NET_CTL_TXEN BIT(3) /* Transmit Enable */ +#define MAC_NET_CTL_RXEN BIT(2) /* Receive Enable */ + +/* MAC Network Configuration Reg */ +#define LAN865X_REG_MAC_NET_CFG 0x00010001 +#define MAC_NET_CFG_PROMISCUOUS_MODE BIT(4) +#define MAC_NET_CFG_MULTICAST_MODE BIT(6) +#define MAC_NET_CFG_UNICAST_MODE BIT(7) + +/* MAC Hash Register Bottom */ +#define LAN865X_REG_MAC_L_HASH 0x00010020 +/* MAC Hash Register Top */ +#define LAN865X_REG_MAC_H_HASH 0x00010021 +/* MAC Specific Addr 1 Bottom Reg */ +#define LAN865X_REG_MAC_L_SADDR1 0x00010022 +/* MAC Specific Addr 1 Top Reg */ +#define LAN865X_REG_MAC_H_SADDR1 0x00010023 + +struct lan865x_priv { + struct work_struct multicast_work; + struct net_device *netdev; + struct spi_device *spi; + struct oa_tc6 *tc6; +}; + +static int lan865x_set_hw_macaddr_low_bytes(struct oa_tc6 *tc6, const u8 *mac) +{ + u32 regval; + + regval = (mac[3] << 24) | (mac[2] << 16) | (mac[1] << 8) | mac[0]; + + return oa_tc6_write_register(tc6, LAN865X_REG_MAC_L_SADDR1, regval); +} + +static int lan865x_set_hw_macaddr(struct lan865x_priv *priv, const u8 *mac) +{ + int restore_ret; + u32 regval; + int ret; + + /* Configure MAC address low bytes */ + ret = lan865x_set_hw_macaddr_low_bytes(priv->tc6, mac); + if (ret) + return ret; + + /* Prepare and configure MAC address high bytes */ + regval = (mac[5] << 8) | mac[4]; + ret = oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_H_SADDR1, + regval); + if (!ret) + return 0; + + /* Restore the old MAC address low bytes from netdev if the new MAC + * address high bytes setting failed. + */ + restore_ret = lan865x_set_hw_macaddr_low_bytes(priv->tc6, + priv->netdev->dev_addr); + if (restore_ret) + return restore_ret; + + return ret; +} + +static const struct ethtool_ops lan865x_ethtool_ops = { + .get_link_ksettings = phy_ethtool_get_link_ksettings, + .set_link_ksettings = phy_ethtool_set_link_ksettings, +}; + +static int lan865x_set_mac_address(struct net_device *netdev, void *addr) +{ + struct lan865x_priv *priv = netdev_priv(netdev); + struct sockaddr *address = addr; + int ret; + + ret = eth_prepare_mac_addr_change(netdev, addr); + if (ret < 0) + return ret; + + if (ether_addr_equal(address->sa_data, netdev->dev_addr)) + return 0; + + ret = lan865x_set_hw_macaddr(priv, address->sa_data); + if (ret) + return ret; + + eth_commit_mac_addr_change(netdev, addr); + + return 0; +} + +static u32 get_address_bit(u8 addr[ETH_ALEN], u32 bit) +{ + return ((addr[bit / 8]) >> (bit % 8)) & 1; +} + +static u32 lan865x_hash(u8 addr[ETH_ALEN]) +{ + u32 hash_index = 0; + + for (int i = 0; i < 6; i++) { + u32 hash = 0; + + for (int j = 0; j < 8; j++) + hash ^= get_address_bit(addr, (j * 6) + i); + + hash_index |= (hash << i); + } + + return hash_index; +} + +static int lan865x_set_specific_multicast_addr(struct lan865x_priv *priv) +{ + struct netdev_hw_addr *ha; + u32 hash_lo = 0; + u32 hash_hi = 0; + int ret; + + netdev_for_each_mc_addr(ha, priv->netdev) { + u32 bit_num = lan865x_hash(ha->addr); + + if (bit_num >= BIT(5)) + hash_hi |= (1 << (bit_num - BIT(5))); + else + hash_lo |= (1 << bit_num); + } + + /* Enabling specific multicast addresses */ + ret = oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_H_HASH, hash_hi); + if (ret) { + netdev_err(priv->netdev, "Failed to write reg_hashh: %d\n", + ret); + return ret; + } + + ret = oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_L_HASH, hash_lo); + if (ret) + netdev_err(priv->netdev, "Failed to write reg_hashl: %d\n", + ret); + + return ret; +} + +static int lan865x_set_all_multicast_addr(struct lan865x_priv *priv) +{ + int ret; + + /* Enabling all multicast addresses */ + ret = oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_H_HASH, + 0xffffffff); + if (ret) { + netdev_err(priv->netdev, "Failed to write reg_hashh: %d\n", + ret); + return ret; + } + + ret = oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_L_HASH, + 0xffffffff); + if (ret) + netdev_err(priv->netdev, "Failed to write reg_hashl: %d\n", + ret); + + return ret; +} + +static int lan865x_clear_all_multicast_addr(struct lan865x_priv *priv) +{ + int ret; + + ret = oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_H_HASH, 0); + if (ret) { + netdev_err(priv->netdev, "Failed to write reg_hashh: %d\n", + ret); + return ret; + } + + ret = oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_L_HASH, 0); + if (ret) + netdev_err(priv->netdev, "Failed to write reg_hashl: %d\n", + ret); + + return ret; +} + +static void lan865x_multicast_work_handler(struct work_struct *work) +{ + struct lan865x_priv *priv = container_of(work, struct lan865x_priv, + multicast_work); + u32 regval = 0; + int ret; + + if (priv->netdev->flags & IFF_PROMISC) { + /* Enabling promiscuous mode */ + regval |= MAC_NET_CFG_PROMISCUOUS_MODE; + regval &= (~MAC_NET_CFG_MULTICAST_MODE); + regval &= (~MAC_NET_CFG_UNICAST_MODE); + } else if (priv->netdev->flags & IFF_ALLMULTI) { + /* Enabling all multicast mode */ + if (lan865x_set_all_multicast_addr(priv)) + return; + + regval &= (~MAC_NET_CFG_PROMISCUOUS_MODE); + regval |= MAC_NET_CFG_MULTICAST_MODE; + regval &= (~MAC_NET_CFG_UNICAST_MODE); + } else if (!netdev_mc_empty(priv->netdev)) { + /* Enabling specific multicast mode */ + if (lan865x_set_specific_multicast_addr(priv)) + return; + + regval &= (~MAC_NET_CFG_PROMISCUOUS_MODE); + regval |= MAC_NET_CFG_MULTICAST_MODE; + regval &= (~MAC_NET_CFG_UNICAST_MODE); + } else { + /* Enabling local mac address only */ + if (lan865x_clear_all_multicast_addr(priv)) + return; + } + ret = oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_NET_CFG, regval); + if (ret) + netdev_err(priv->netdev, "Failed to enable promiscuous/multicast/normal mode: %d\n", + ret); +} + +static void lan865x_set_multicast_list(struct net_device *netdev) +{ + struct lan865x_priv *priv = netdev_priv(netdev); + + schedule_work(&priv->multicast_work); +} + +static netdev_tx_t lan865x_send_packet(struct sk_buff *skb, + struct net_device *netdev) +{ + struct lan865x_priv *priv = netdev_priv(netdev); + + return oa_tc6_start_xmit(priv->tc6, skb); +} + +static int lan865x_hw_disable(struct lan865x_priv *priv) +{ + u32 regval; + + if (oa_tc6_read_register(priv->tc6, LAN865X_REG_MAC_NET_CTL, ®val)) + return -ENODEV; + + regval &= ~(MAC_NET_CTL_TXEN | MAC_NET_CTL_RXEN); + + if (oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_NET_CTL, regval)) + return -ENODEV; + + return 0; +} + +static int lan865x_net_close(struct net_device *netdev) +{ + struct lan865x_priv *priv = netdev_priv(netdev); + int ret; + + netif_stop_queue(netdev); + phy_stop(netdev->phydev); + ret = lan865x_hw_disable(priv); + if (ret) { + netdev_err(netdev, "Failed to disable the hardware: %d\n", ret); + return ret; + } + + return 0; +} + +static int lan865x_hw_enable(struct lan865x_priv *priv) +{ + u32 regval; + + if (oa_tc6_read_register(priv->tc6, LAN865X_REG_MAC_NET_CTL, ®val)) + return -ENODEV; + + regval |= MAC_NET_CTL_TXEN | MAC_NET_CTL_RXEN; + + if (oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_NET_CTL, regval)) + return -ENODEV; + + return 0; +} + +static int lan865x_net_open(struct net_device *netdev) +{ + struct lan865x_priv *priv = netdev_priv(netdev); + int ret; + + ret = lan865x_hw_enable(priv); + if (ret) { + netdev_err(netdev, "Failed to enable hardware: %d\n", ret); + return ret; + } + + phy_start(netdev->phydev); + + return 0; +} + +static const struct net_device_ops lan865x_netdev_ops = { + .ndo_open = lan865x_net_open, + .ndo_stop = lan865x_net_close, + .ndo_start_xmit = lan865x_send_packet, + .ndo_set_rx_mode = lan865x_set_multicast_list, + .ndo_set_mac_address = lan865x_set_mac_address, +}; + +static int lan865x_probe(struct spi_device *spi) +{ + struct net_device *netdev; + struct lan865x_priv *priv; + int ret; + + netdev = alloc_etherdev(sizeof(struct lan865x_priv)); + if (!netdev) + return -ENOMEM; + + priv = netdev_priv(netdev); + priv->netdev = netdev; + priv->spi = spi; + spi_set_drvdata(spi, priv); + INIT_WORK(&priv->multicast_work, lan865x_multicast_work_handler); + + priv->tc6 = oa_tc6_init(spi, netdev); + if (!priv->tc6) { + ret = -ENODEV; + goto free_netdev; + } + + /* As per the point s3 in the below errata, SPI receive Ethernet frame + * transfer may halt when starting the next frame in the same data block + * (chunk) as the end of a previous frame. The RFA field should be + * configured to 01b or 10b for proper operation. In these modes, only + * one receive Ethernet frame will be placed in a single data block. + * When the RFA field is written to 01b, received frames will be forced + * to only start in the first word of the data block payload (SWO=0). As + * recommended, enable zero align receive frame feature for proper + * operation. + * + * https://ww1.microchip.com/downloads/aemDocuments/documents/AIS/ProductDocuments/Errata/LAN8650-1-Errata-80001075.pdf + */ + ret = oa_tc6_zero_align_receive_frame_enable(priv->tc6); + if (ret) { + dev_err(&spi->dev, "Failed to set ZARFE: %d\n", ret); + goto oa_tc6_exit; + } + + /* Get the MAC address from the SPI device tree node */ + if (device_get_ethdev_address(&spi->dev, netdev)) + eth_hw_addr_random(netdev); + + ret = lan865x_set_hw_macaddr(priv, netdev->dev_addr); + if (ret) { + dev_err(&spi->dev, "Failed to configure MAC: %d\n", ret); + goto oa_tc6_exit; + } + + netdev->if_port = IF_PORT_10BASET; + netdev->irq = spi->irq; + netdev->netdev_ops = &lan865x_netdev_ops; + netdev->ethtool_ops = &lan865x_ethtool_ops; + + ret = register_netdev(netdev); + if (ret) { + dev_err(&spi->dev, "Register netdev failed (ret = %d)", ret); + goto oa_tc6_exit; + } + + return 0; + +oa_tc6_exit: + oa_tc6_exit(priv->tc6); +free_netdev: + free_netdev(priv->netdev); + return ret; +} + +static void lan865x_remove(struct spi_device *spi) +{ + struct lan865x_priv *priv = spi_get_drvdata(spi); + + cancel_work_sync(&priv->multicast_work); + unregister_netdev(priv->netdev); + oa_tc6_exit(priv->tc6); + free_netdev(priv->netdev); +} + +static const struct spi_device_id spidev_spi_ids[] = { + { .name = "lan8650" }, + {}, +}; + +static const struct of_device_id lan865x_dt_ids[] = { + { .compatible = "microchip,lan8650" }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, lan865x_dt_ids); + +static struct spi_driver lan865x_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = lan865x_dt_ids, + }, + .probe = lan865x_probe, + .remove = lan865x_remove, + .id_table = spidev_spi_ids, +}; +module_spi_driver(lan865x_driver); + +MODULE_DESCRIPTION(DRV_NAME " 10Base-T1S MACPHY Ethernet Driver"); +MODULE_AUTHOR("Parthiban Veerasooran <parthiban.veerasooran@microchip.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/microchip/lan966x/Kconfig b/drivers/net/ethernet/microchip/lan966x/Kconfig index f9ebffc04eb8..f663b6e12466 100644 --- a/drivers/net/ethernet/microchip/lan966x/Kconfig +++ b/drivers/net/ethernet/microchip/lan966x/Kconfig @@ -8,6 +8,7 @@ config LAN966X_SWITCH select PHYLINK select PAGE_POOL select VCAP + select FDMA help This driver supports the Lan966x network switch device. diff --git a/drivers/net/ethernet/microchip/lan966x/Makefile b/drivers/net/ethernet/microchip/lan966x/Makefile index 3b6ac331691d..4cdbe263502c 100644 --- a/drivers/net/ethernet/microchip/lan966x/Makefile +++ b/drivers/net/ethernet/microchip/lan966x/Makefile @@ -20,3 +20,4 @@ lan966x-switch-$(CONFIG_DEBUG_FS) += lan966x_vcap_debugfs.o # Provide include files ccflags-y += -I$(srctree)/drivers/net/ethernet/microchip/vcap +ccflags-y += -I$(srctree)/drivers/net/ethernet/microchip/fdma diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_ethtool.c b/drivers/net/ethernet/microchip/lan966x/lan966x_ethtool.c index aec7066d83b3..2474dfd330f4 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_ethtool.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_ethtool.c @@ -549,16 +549,13 @@ static int lan966x_get_ts_info(struct net_device *dev, phc = &lan966x->phc[LAN966X_PHC_PORT]; - info->phc_index = phc->clock ? ptp_clock_index(phc->clock) : -1; - if (info->phc_index == -1) { - info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE | - SOF_TIMESTAMPING_RX_SOFTWARE | - SOF_TIMESTAMPING_SOFTWARE; + if (phc->clock) { + info->phc_index = ptp_clock_index(phc->clock); + } else { + info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE; return 0; } info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE | - SOF_TIMESTAMPING_RX_SOFTWARE | - SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_TX_HARDWARE | SOF_TIMESTAMPING_RX_HARDWARE | SOF_TIMESTAMPING_RAW_HARDWARE; diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c index 3960534ac2ad..502670718104 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c @@ -6,31 +6,55 @@ #include "lan966x_main.h" -static int lan966x_fdma_channel_active(struct lan966x *lan966x) -{ - return lan_rd(lan966x, FDMA_CH_ACTIVE); -} - -static struct page *lan966x_fdma_rx_alloc_page(struct lan966x_rx *rx, - struct lan966x_db *db) +static int lan966x_fdma_rx_dataptr_cb(struct fdma *fdma, int dcb, int db, + u64 *dataptr) { + struct lan966x *lan966x = (struct lan966x *)fdma->priv; + struct lan966x_rx *rx = &lan966x->rx; struct page *page; page = page_pool_dev_alloc_pages(rx->page_pool); if (unlikely(!page)) - return NULL; + return -ENOMEM; + + rx->page[dcb][db] = page; + *dataptr = page_pool_get_dma_addr(page) + XDP_PACKET_HEADROOM; + + return 0; +} - db->dataptr = page_pool_get_dma_addr(page) + XDP_PACKET_HEADROOM; +static int lan966x_fdma_tx_dataptr_cb(struct fdma *fdma, int dcb, int db, + u64 *dataptr) +{ + struct lan966x *lan966x = (struct lan966x *)fdma->priv; + + *dataptr = lan966x->tx.dcbs_buf[dcb].dma_addr; - return page; + return 0; +} + +static int lan966x_fdma_xdp_tx_dataptr_cb(struct fdma *fdma, int dcb, int db, + u64 *dataptr) +{ + struct lan966x *lan966x = (struct lan966x *)fdma->priv; + + *dataptr = lan966x->tx.dcbs_buf[dcb].dma_addr + XDP_PACKET_HEADROOM; + + return 0; +} + +static int lan966x_fdma_channel_active(struct lan966x *lan966x) +{ + return lan_rd(lan966x, FDMA_CH_ACTIVE); } static void lan966x_fdma_rx_free_pages(struct lan966x_rx *rx) { + struct fdma *fdma = &rx->fdma; int i, j; - for (i = 0; i < FDMA_DCB_MAX; ++i) { - for (j = 0; j < FDMA_RX_DCB_MAX_DBS; ++j) + for (i = 0; i < fdma->n_dcbs; ++i) { + for (j = 0; j < fdma->n_dbs; ++j) page_pool_put_full_page(rx->page_pool, rx->page[i][j], false); } @@ -38,41 +62,23 @@ static void lan966x_fdma_rx_free_pages(struct lan966x_rx *rx) static void lan966x_fdma_rx_free_page(struct lan966x_rx *rx) { + struct fdma *fdma = &rx->fdma; struct page *page; - page = rx->page[rx->dcb_index][rx->db_index]; + page = rx->page[fdma->dcb_index][fdma->db_index]; if (unlikely(!page)) return; page_pool_recycle_direct(rx->page_pool, page); } -static void lan966x_fdma_rx_add_dcb(struct lan966x_rx *rx, - struct lan966x_rx_dcb *dcb, - u64 nextptr) -{ - struct lan966x_db *db; - int i; - - for (i = 0; i < FDMA_RX_DCB_MAX_DBS; ++i) { - db = &dcb->db[i]; - db->status = FDMA_DCB_STATUS_INTR; - } - - dcb->nextptr = FDMA_DCB_INVALID_DATA; - dcb->info = FDMA_DCB_INFO_DATAL(PAGE_SIZE << rx->page_order); - - rx->last_entry->nextptr = nextptr; - rx->last_entry = dcb; -} - static int lan966x_fdma_rx_alloc_page_pool(struct lan966x_rx *rx) { struct lan966x *lan966x = rx->lan966x; struct page_pool_params pp_params = { .order = rx->page_order, .flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV, - .pool_size = FDMA_DCB_MAX, + .pool_size = rx->fdma.n_dcbs, .nid = NUMA_NO_NODE, .dev = lan966x->dev, .dma_dir = DMA_FROM_DEVICE, @@ -104,84 +110,41 @@ static int lan966x_fdma_rx_alloc_page_pool(struct lan966x_rx *rx) static int lan966x_fdma_rx_alloc(struct lan966x_rx *rx) { struct lan966x *lan966x = rx->lan966x; - struct lan966x_rx_dcb *dcb; - struct lan966x_db *db; - struct page *page; - int i, j; - int size; + struct fdma *fdma = &rx->fdma; + int err; if (lan966x_fdma_rx_alloc_page_pool(rx)) return PTR_ERR(rx->page_pool); - /* calculate how many pages are needed to allocate the dcbs */ - size = sizeof(struct lan966x_rx_dcb) * FDMA_DCB_MAX; - size = ALIGN(size, PAGE_SIZE); - - rx->dcbs = dma_alloc_coherent(lan966x->dev, size, &rx->dma, GFP_KERNEL); - if (!rx->dcbs) - return -ENOMEM; - - rx->last_entry = rx->dcbs; - rx->db_index = 0; - rx->dcb_index = 0; - - /* Now for each dcb allocate the dbs */ - for (i = 0; i < FDMA_DCB_MAX; ++i) { - dcb = &rx->dcbs[i]; - dcb->info = 0; - - /* For each db allocate a page and map it to the DB dataptr. */ - for (j = 0; j < FDMA_RX_DCB_MAX_DBS; ++j) { - db = &dcb->db[j]; - page = lan966x_fdma_rx_alloc_page(rx, db); - if (!page) - return -ENOMEM; - - db->status = 0; - rx->page[i][j] = page; - } + err = fdma_alloc_coherent(lan966x->dev, fdma); + if (err) + return err; - lan966x_fdma_rx_add_dcb(rx, dcb, rx->dma + sizeof(*dcb) * i); - } + fdma_dcbs_init(fdma, FDMA_DCB_INFO_DATAL(fdma->db_size), + FDMA_DCB_STATUS_INTR); return 0; } -static void lan966x_fdma_rx_advance_dcb(struct lan966x_rx *rx) -{ - rx->dcb_index++; - rx->dcb_index &= FDMA_DCB_MAX - 1; -} - -static void lan966x_fdma_rx_free(struct lan966x_rx *rx) -{ - struct lan966x *lan966x = rx->lan966x; - u32 size; - - /* Now it is possible to do the cleanup of dcb */ - size = sizeof(struct lan966x_tx_dcb) * FDMA_DCB_MAX; - size = ALIGN(size, PAGE_SIZE); - dma_free_coherent(lan966x->dev, size, rx->dcbs, rx->dma); -} - static void lan966x_fdma_rx_start(struct lan966x_rx *rx) { struct lan966x *lan966x = rx->lan966x; + struct fdma *fdma = &rx->fdma; u32 mask; /* When activating a channel, first is required to write the first DCB * address and then to activate it */ - lan_wr(lower_32_bits((u64)rx->dma), lan966x, - FDMA_DCB_LLP(rx->channel_id)); - lan_wr(upper_32_bits((u64)rx->dma), lan966x, - FDMA_DCB_LLP1(rx->channel_id)); + lan_wr(lower_32_bits((u64)fdma->dma), lan966x, + FDMA_DCB_LLP(fdma->channel_id)); + lan_wr(upper_32_bits((u64)fdma->dma), lan966x, + FDMA_DCB_LLP1(fdma->channel_id)); - lan_wr(FDMA_CH_CFG_CH_DCB_DB_CNT_SET(FDMA_RX_DCB_MAX_DBS) | + lan_wr(FDMA_CH_CFG_CH_DCB_DB_CNT_SET(fdma->n_dbs) | FDMA_CH_CFG_CH_INTR_DB_EOF_ONLY_SET(1) | FDMA_CH_CFG_CH_INJ_PORT_SET(0) | FDMA_CH_CFG_CH_MEM_SET(1), - lan966x, FDMA_CH_CFG(rx->channel_id)); + lan966x, FDMA_CH_CFG(fdma->channel_id)); /* Start fdma */ lan_rmw(FDMA_PORT_CTRL_XTR_STOP_SET(0), @@ -191,13 +154,13 @@ static void lan966x_fdma_rx_start(struct lan966x_rx *rx) /* Enable interrupts */ mask = lan_rd(lan966x, FDMA_INTR_DB_ENA); mask = FDMA_INTR_DB_ENA_INTR_DB_ENA_GET(mask); - mask |= BIT(rx->channel_id); + mask |= BIT(fdma->channel_id); lan_rmw(FDMA_INTR_DB_ENA_INTR_DB_ENA_SET(mask), FDMA_INTR_DB_ENA_INTR_DB_ENA, lan966x, FDMA_INTR_DB_ENA); /* Activate the channel */ - lan_rmw(FDMA_CH_ACTIVATE_CH_ACTIVATE_SET(BIT(rx->channel_id)), + lan_rmw(FDMA_CH_ACTIVATE_CH_ACTIVATE_SET(BIT(fdma->channel_id)), FDMA_CH_ACTIVATE_CH_ACTIVATE, lan966x, FDMA_CH_ACTIVATE); } @@ -205,18 +168,19 @@ static void lan966x_fdma_rx_start(struct lan966x_rx *rx) static void lan966x_fdma_rx_disable(struct lan966x_rx *rx) { struct lan966x *lan966x = rx->lan966x; + struct fdma *fdma = &rx->fdma; u32 val; /* Disable the channel */ - lan_rmw(FDMA_CH_DISABLE_CH_DISABLE_SET(BIT(rx->channel_id)), + lan_rmw(FDMA_CH_DISABLE_CH_DISABLE_SET(BIT(fdma->channel_id)), FDMA_CH_DISABLE_CH_DISABLE, lan966x, FDMA_CH_DISABLE); readx_poll_timeout_atomic(lan966x_fdma_channel_active, lan966x, - val, !(val & BIT(rx->channel_id)), + val, !(val & BIT(fdma->channel_id)), READL_SLEEP_US, READL_TIMEOUT_US); - lan_rmw(FDMA_CH_DB_DISCARD_DB_DISCARD_SET(BIT(rx->channel_id)), + lan_rmw(FDMA_CH_DB_DISCARD_DB_DISCARD_SET(BIT(fdma->channel_id)), FDMA_CH_DB_DISCARD_DB_DISCARD, lan966x, FDMA_CH_DB_DISCARD); } @@ -225,50 +189,27 @@ static void lan966x_fdma_rx_reload(struct lan966x_rx *rx) { struct lan966x *lan966x = rx->lan966x; - lan_rmw(FDMA_CH_RELOAD_CH_RELOAD_SET(BIT(rx->channel_id)), + lan_rmw(FDMA_CH_RELOAD_CH_RELOAD_SET(BIT(rx->fdma.channel_id)), FDMA_CH_RELOAD_CH_RELOAD, lan966x, FDMA_CH_RELOAD); } -static void lan966x_fdma_tx_add_dcb(struct lan966x_tx *tx, - struct lan966x_tx_dcb *dcb) -{ - dcb->nextptr = FDMA_DCB_INVALID_DATA; - dcb->info = 0; -} - static int lan966x_fdma_tx_alloc(struct lan966x_tx *tx) { struct lan966x *lan966x = tx->lan966x; - struct lan966x_tx_dcb *dcb; - struct lan966x_db *db; - int size; - int i, j; + struct fdma *fdma = &tx->fdma; + int err; - tx->dcbs_buf = kcalloc(FDMA_DCB_MAX, sizeof(struct lan966x_tx_dcb_buf), + tx->dcbs_buf = kcalloc(fdma->n_dcbs, sizeof(struct lan966x_tx_dcb_buf), GFP_KERNEL); if (!tx->dcbs_buf) return -ENOMEM; - /* calculate how many pages are needed to allocate the dcbs */ - size = sizeof(struct lan966x_tx_dcb) * FDMA_DCB_MAX; - size = ALIGN(size, PAGE_SIZE); - tx->dcbs = dma_alloc_coherent(lan966x->dev, size, &tx->dma, GFP_KERNEL); - if (!tx->dcbs) + err = fdma_alloc_coherent(lan966x->dev, fdma); + if (err) goto out; - /* Now for each dcb allocate the db */ - for (i = 0; i < FDMA_DCB_MAX; ++i) { - dcb = &tx->dcbs[i]; - - for (j = 0; j < FDMA_TX_DCB_MAX_DBS; ++j) { - db = &dcb->db[j]; - db->dataptr = 0; - db->status = 0; - } - - lan966x_fdma_tx_add_dcb(tx, dcb); - } + fdma_dcbs_init(fdma, 0, 0); return 0; @@ -280,33 +221,30 @@ out: static void lan966x_fdma_tx_free(struct lan966x_tx *tx) { struct lan966x *lan966x = tx->lan966x; - int size; kfree(tx->dcbs_buf); - - size = sizeof(struct lan966x_tx_dcb) * FDMA_DCB_MAX; - size = ALIGN(size, PAGE_SIZE); - dma_free_coherent(lan966x->dev, size, tx->dcbs, tx->dma); + fdma_free_coherent(lan966x->dev, &tx->fdma); } static void lan966x_fdma_tx_activate(struct lan966x_tx *tx) { struct lan966x *lan966x = tx->lan966x; + struct fdma *fdma = &tx->fdma; u32 mask; /* When activating a channel, first is required to write the first DCB * address and then to activate it */ - lan_wr(lower_32_bits((u64)tx->dma), lan966x, - FDMA_DCB_LLP(tx->channel_id)); - lan_wr(upper_32_bits((u64)tx->dma), lan966x, - FDMA_DCB_LLP1(tx->channel_id)); + lan_wr(lower_32_bits((u64)fdma->dma), lan966x, + FDMA_DCB_LLP(fdma->channel_id)); + lan_wr(upper_32_bits((u64)fdma->dma), lan966x, + FDMA_DCB_LLP1(fdma->channel_id)); - lan_wr(FDMA_CH_CFG_CH_DCB_DB_CNT_SET(FDMA_TX_DCB_MAX_DBS) | + lan_wr(FDMA_CH_CFG_CH_DCB_DB_CNT_SET(fdma->n_dbs) | FDMA_CH_CFG_CH_INTR_DB_EOF_ONLY_SET(1) | FDMA_CH_CFG_CH_INJ_PORT_SET(0) | FDMA_CH_CFG_CH_MEM_SET(1), - lan966x, FDMA_CH_CFG(tx->channel_id)); + lan966x, FDMA_CH_CFG(fdma->channel_id)); /* Start fdma */ lan_rmw(FDMA_PORT_CTRL_INJ_STOP_SET(0), @@ -316,13 +254,13 @@ static void lan966x_fdma_tx_activate(struct lan966x_tx *tx) /* Enable interrupts */ mask = lan_rd(lan966x, FDMA_INTR_DB_ENA); mask = FDMA_INTR_DB_ENA_INTR_DB_ENA_GET(mask); - mask |= BIT(tx->channel_id); + mask |= BIT(fdma->channel_id); lan_rmw(FDMA_INTR_DB_ENA_INTR_DB_ENA_SET(mask), FDMA_INTR_DB_ENA_INTR_DB_ENA, lan966x, FDMA_INTR_DB_ENA); /* Activate the channel */ - lan_rmw(FDMA_CH_ACTIVATE_CH_ACTIVATE_SET(BIT(tx->channel_id)), + lan_rmw(FDMA_CH_ACTIVATE_CH_ACTIVATE_SET(BIT(fdma->channel_id)), FDMA_CH_ACTIVATE_CH_ACTIVATE, lan966x, FDMA_CH_ACTIVATE); } @@ -330,23 +268,23 @@ static void lan966x_fdma_tx_activate(struct lan966x_tx *tx) static void lan966x_fdma_tx_disable(struct lan966x_tx *tx) { struct lan966x *lan966x = tx->lan966x; + struct fdma *fdma = &tx->fdma; u32 val; /* Disable the channel */ - lan_rmw(FDMA_CH_DISABLE_CH_DISABLE_SET(BIT(tx->channel_id)), + lan_rmw(FDMA_CH_DISABLE_CH_DISABLE_SET(BIT(fdma->channel_id)), FDMA_CH_DISABLE_CH_DISABLE, lan966x, FDMA_CH_DISABLE); readx_poll_timeout_atomic(lan966x_fdma_channel_active, lan966x, - val, !(val & BIT(tx->channel_id)), + val, !(val & BIT(fdma->channel_id)), READL_SLEEP_US, READL_TIMEOUT_US); - lan_rmw(FDMA_CH_DB_DISCARD_DB_DISCARD_SET(BIT(tx->channel_id)), + lan_rmw(FDMA_CH_DB_DISCARD_DB_DISCARD_SET(BIT(fdma->channel_id)), FDMA_CH_DB_DISCARD_DB_DISCARD, lan966x, FDMA_CH_DB_DISCARD); tx->activated = false; - tx->last_in_use = -1; } static void lan966x_fdma_tx_reload(struct lan966x_tx *tx) @@ -354,7 +292,7 @@ static void lan966x_fdma_tx_reload(struct lan966x_tx *tx) struct lan966x *lan966x = tx->lan966x; /* Write the registers to reload the channel */ - lan_rmw(FDMA_CH_RELOAD_CH_RELOAD_SET(BIT(tx->channel_id)), + lan_rmw(FDMA_CH_RELOAD_CH_RELOAD_SET(BIT(tx->fdma.channel_id)), FDMA_CH_RELOAD_CH_RELOAD, lan966x, FDMA_CH_RELOAD); } @@ -393,23 +331,24 @@ static void lan966x_fdma_tx_clear_buf(struct lan966x *lan966x, int weight) struct lan966x_tx *tx = &lan966x->tx; struct lan966x_rx *rx = &lan966x->rx; struct lan966x_tx_dcb_buf *dcb_buf; + struct fdma *fdma = &tx->fdma; struct xdp_frame_bulk bq; - struct lan966x_db *db; unsigned long flags; bool clear = false; + struct fdma_db *db; int i; xdp_frame_bulk_init(&bq); spin_lock_irqsave(&lan966x->tx_lock, flags); - for (i = 0; i < FDMA_DCB_MAX; ++i) { + for (i = 0; i < fdma->n_dcbs; ++i) { dcb_buf = &tx->dcbs_buf[i]; if (!dcb_buf->used) continue; - db = &tx->dcbs[i].db[0]; - if (!(db->status & FDMA_DCB_STATUS_DONE)) + db = fdma_db_get(fdma, i, 0); + if (!fdma_db_is_done(db)) continue; dcb_buf->dev->stats.tx_packets++; @@ -449,27 +388,16 @@ static void lan966x_fdma_tx_clear_buf(struct lan966x *lan966x, int weight) spin_unlock_irqrestore(&lan966x->tx_lock, flags); } -static bool lan966x_fdma_rx_more_frames(struct lan966x_rx *rx) -{ - struct lan966x_db *db; - - /* Check if there is any data */ - db = &rx->dcbs[rx->dcb_index].db[rx->db_index]; - if (unlikely(!(db->status & FDMA_DCB_STATUS_DONE))) - return false; - - return true; -} - static int lan966x_fdma_rx_check_frame(struct lan966x_rx *rx, u64 *src_port) { struct lan966x *lan966x = rx->lan966x; + struct fdma *fdma = &rx->fdma; struct lan966x_port *port; - struct lan966x_db *db; + struct fdma_db *db; struct page *page; - db = &rx->dcbs[rx->dcb_index].db[rx->db_index]; - page = rx->page[rx->dcb_index][rx->db_index]; + db = fdma_db_next_get(fdma); + page = rx->page[fdma->dcb_index][fdma->db_index]; if (unlikely(!page)) return FDMA_ERROR; @@ -494,16 +422,17 @@ static struct sk_buff *lan966x_fdma_rx_get_frame(struct lan966x_rx *rx, u64 src_port) { struct lan966x *lan966x = rx->lan966x; - struct lan966x_db *db; + struct fdma *fdma = &rx->fdma; struct sk_buff *skb; + struct fdma_db *db; struct page *page; u64 timestamp; /* Get the received frame and unmap it */ - db = &rx->dcbs[rx->dcb_index].db[rx->db_index]; - page = rx->page[rx->dcb_index][rx->db_index]; + db = fdma_db_next_get(fdma); + page = rx->page[fdma->dcb_index][fdma->db_index]; - skb = build_skb(page_address(page), PAGE_SIZE << rx->page_order); + skb = build_skb(page_address(page), fdma->db_size); if (unlikely(!skb)) goto free_page; @@ -546,21 +475,19 @@ static int lan966x_fdma_napi_poll(struct napi_struct *napi, int weight) { struct lan966x *lan966x = container_of(napi, struct lan966x, napi); struct lan966x_rx *rx = &lan966x->rx; - int dcb_reload = rx->dcb_index; - struct lan966x_rx_dcb *old_dcb; - struct lan966x_db *db; + int old_dcb, dcb_reload, counter = 0; + struct fdma *fdma = &rx->fdma; bool redirect = false; struct sk_buff *skb; - struct page *page; - int counter = 0; u64 src_port; - u64 nextptr; + + dcb_reload = fdma->dcb_index; lan966x_fdma_tx_clear_buf(lan966x, weight); /* Get all received skb */ while (counter < weight) { - if (!lan966x_fdma_rx_more_frames(rx)) + if (!fdma_has_frames(fdma)) break; counter++; @@ -570,22 +497,22 @@ static int lan966x_fdma_napi_poll(struct napi_struct *napi, int weight) break; case FDMA_ERROR: lan966x_fdma_rx_free_page(rx); - lan966x_fdma_rx_advance_dcb(rx); + fdma_dcb_advance(fdma); goto allocate_new; case FDMA_REDIRECT: redirect = true; fallthrough; case FDMA_TX: - lan966x_fdma_rx_advance_dcb(rx); + fdma_dcb_advance(fdma); continue; case FDMA_DROP: lan966x_fdma_rx_free_page(rx); - lan966x_fdma_rx_advance_dcb(rx); + fdma_dcb_advance(fdma); continue; } skb = lan966x_fdma_rx_get_frame(rx, src_port); - lan966x_fdma_rx_advance_dcb(rx); + fdma_dcb_advance(fdma); if (!skb) goto allocate_new; @@ -594,20 +521,14 @@ static int lan966x_fdma_napi_poll(struct napi_struct *napi, int weight) allocate_new: /* Allocate new pages and map them */ - while (dcb_reload != rx->dcb_index) { - db = &rx->dcbs[dcb_reload].db[rx->db_index]; - page = lan966x_fdma_rx_alloc_page(rx, db); - if (unlikely(!page)) - break; - rx->page[dcb_reload][rx->db_index] = page; - - old_dcb = &rx->dcbs[dcb_reload]; + while (dcb_reload != fdma->dcb_index) { + old_dcb = dcb_reload; dcb_reload++; - dcb_reload &= FDMA_DCB_MAX - 1; + dcb_reload &= fdma->n_dcbs - 1; + + fdma_dcb_add(fdma, old_dcb, FDMA_DCB_INFO_DATAL(fdma->db_size), + FDMA_DCB_STATUS_INTR); - nextptr = rx->dma + ((unsigned long)old_dcb - - (unsigned long)rx->dcbs); - lan966x_fdma_rx_add_dcb(rx, old_dcb, nextptr); lan966x_fdma_rx_reload(rx); } @@ -650,56 +571,30 @@ irqreturn_t lan966x_fdma_irq_handler(int irq, void *args) static int lan966x_fdma_get_next_dcb(struct lan966x_tx *tx) { struct lan966x_tx_dcb_buf *dcb_buf; + struct fdma *fdma = &tx->fdma; int i; - for (i = 0; i < FDMA_DCB_MAX; ++i) { + for (i = 0; i < fdma->n_dcbs; ++i) { dcb_buf = &tx->dcbs_buf[i]; - if (!dcb_buf->used && i != tx->last_in_use) + if (!dcb_buf->used && + !fdma_is_last(&tx->fdma, &tx->fdma.dcbs[i])) return i; } return -1; } -static void lan966x_fdma_tx_setup_dcb(struct lan966x_tx *tx, - int next_to_use, int len, - dma_addr_t dma_addr) -{ - struct lan966x_tx_dcb *next_dcb; - struct lan966x_db *next_db; - - next_dcb = &tx->dcbs[next_to_use]; - next_dcb->nextptr = FDMA_DCB_INVALID_DATA; - - next_db = &next_dcb->db[0]; - next_db->dataptr = dma_addr; - next_db->status = FDMA_DCB_STATUS_SOF | - FDMA_DCB_STATUS_EOF | - FDMA_DCB_STATUS_INTR | - FDMA_DCB_STATUS_BLOCKO(0) | - FDMA_DCB_STATUS_BLOCKL(len); -} - -static void lan966x_fdma_tx_start(struct lan966x_tx *tx, int next_to_use) +static void lan966x_fdma_tx_start(struct lan966x_tx *tx) { struct lan966x *lan966x = tx->lan966x; - struct lan966x_tx_dcb *dcb; if (likely(lan966x->tx.activated)) { - /* Connect current dcb to the next db */ - dcb = &tx->dcbs[tx->last_in_use]; - dcb->nextptr = tx->dma + (next_to_use * - sizeof(struct lan966x_tx_dcb)); - lan966x_fdma_tx_reload(tx); } else { /* Because it is first time, then just activate */ lan966x->tx.activated = true; lan966x_fdma_tx_activate(tx); } - - /* Move to next dcb because this last in use */ - tx->last_in_use = next_to_use; } int lan966x_fdma_xmit_xdpf(struct lan966x_port *port, void *ptr, u32 len) @@ -752,11 +647,6 @@ int lan966x_fdma_xmit_xdpf(struct lan966x_port *port, void *ptr, u32 len) next_dcb_buf->data.xdpf = xdpf; next_dcb_buf->len = xdpf->len + IFH_LEN_BYTES; - - /* Setup next dcb */ - lan966x_fdma_tx_setup_dcb(tx, next_to_use, - xdpf->len + IFH_LEN_BYTES, - dma_addr); } else { page = ptr; @@ -773,11 +663,6 @@ int lan966x_fdma_xmit_xdpf(struct lan966x_port *port, void *ptr, u32 len) next_dcb_buf->data.page = page; next_dcb_buf->len = len + IFH_LEN_BYTES; - - /* Setup next dcb */ - lan966x_fdma_tx_setup_dcb(tx, next_to_use, - len + IFH_LEN_BYTES, - dma_addr + XDP_PACKET_HEADROOM); } /* Fill up the buffer */ @@ -788,8 +673,19 @@ int lan966x_fdma_xmit_xdpf(struct lan966x_port *port, void *ptr, u32 len) next_dcb_buf->ptp = false; next_dcb_buf->dev = port->dev; + __fdma_dcb_add(&tx->fdma, + next_to_use, + 0, + FDMA_DCB_STATUS_INTR | + FDMA_DCB_STATUS_SOF | + FDMA_DCB_STATUS_EOF | + FDMA_DCB_STATUS_BLOCKO(0) | + FDMA_DCB_STATUS_BLOCKL(next_dcb_buf->len), + &fdma_nextptr_cb, + &lan966x_fdma_xdp_tx_dataptr_cb); + /* Start the transmission */ - lan966x_fdma_tx_start(tx, next_to_use); + lan966x_fdma_tx_start(tx); out: spin_unlock(&lan966x->tx_lock); @@ -847,9 +743,6 @@ int lan966x_fdma_xmit(struct sk_buff *skb, __be32 *ifh, struct net_device *dev) goto release; } - /* Setup next dcb */ - lan966x_fdma_tx_setup_dcb(tx, next_to_use, skb->len, dma_addr); - /* Fill up the buffer */ next_dcb_buf = &tx->dcbs_buf[next_to_use]; next_dcb_buf->use_skb = true; @@ -861,12 +754,21 @@ int lan966x_fdma_xmit(struct sk_buff *skb, __be32 *ifh, struct net_device *dev) next_dcb_buf->ptp = false; next_dcb_buf->dev = dev; + fdma_dcb_add(&tx->fdma, + next_to_use, + 0, + FDMA_DCB_STATUS_INTR | + FDMA_DCB_STATUS_SOF | + FDMA_DCB_STATUS_EOF | + FDMA_DCB_STATUS_BLOCKO(0) | + FDMA_DCB_STATUS_BLOCKL(skb->len)); + if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && LAN966X_SKB_CB(skb)->rew_op == IFH_REW_OP_TWO_STEP_PTP) next_dcb_buf->ptp = true; /* Start the transmission */ - lan966x_fdma_tx_start(tx, next_to_use); + lan966x_fdma_tx_start(tx); return NETDEV_TX_OK; @@ -908,14 +810,11 @@ static int lan966x_qsys_sw_status(struct lan966x *lan966x) static int lan966x_fdma_reload(struct lan966x *lan966x, int new_mtu) { struct page_pool *page_pool; - dma_addr_t rx_dma; - void *rx_dcbs; - u32 size; + struct fdma fdma_rx_old; int err; /* Store these for later to free them */ - rx_dma = lan966x->rx.dma; - rx_dcbs = lan966x->rx.dcbs; + memcpy(&fdma_rx_old, &lan966x->rx.fdma, sizeof(struct fdma)); page_pool = lan966x->rx.page_pool; napi_synchronize(&lan966x->napi); @@ -931,9 +830,7 @@ static int lan966x_fdma_reload(struct lan966x *lan966x, int new_mtu) goto restore; lan966x_fdma_rx_start(&lan966x->rx); - size = sizeof(struct lan966x_rx_dcb) * FDMA_DCB_MAX; - size = ALIGN(size, PAGE_SIZE); - dma_free_coherent(lan966x->dev, size, rx_dcbs, rx_dma); + fdma_free_coherent(lan966x->dev, &fdma_rx_old); page_pool_destroy(page_pool); @@ -943,8 +840,7 @@ static int lan966x_fdma_reload(struct lan966x *lan966x, int new_mtu) return err; restore: lan966x->rx.page_pool = page_pool; - lan966x->rx.dma = rx_dma; - lan966x->rx.dcbs = rx_dcbs; + memcpy(&lan966x->rx.fdma, &fdma_rx_old, sizeof(struct fdma)); lan966x_fdma_rx_start(&lan966x->rx); return err; @@ -1034,11 +930,24 @@ int lan966x_fdma_init(struct lan966x *lan966x) return 0; lan966x->rx.lan966x = lan966x; - lan966x->rx.channel_id = FDMA_XTR_CHANNEL; + lan966x->rx.fdma.channel_id = FDMA_XTR_CHANNEL; + lan966x->rx.fdma.n_dcbs = FDMA_DCB_MAX; + lan966x->rx.fdma.n_dbs = FDMA_RX_DCB_MAX_DBS; + lan966x->rx.fdma.priv = lan966x; + lan966x->rx.fdma.size = fdma_get_size(&lan966x->rx.fdma); + lan966x->rx.fdma.db_size = PAGE_SIZE << lan966x->rx.page_order; + lan966x->rx.fdma.ops.nextptr_cb = &fdma_nextptr_cb; + lan966x->rx.fdma.ops.dataptr_cb = &lan966x_fdma_rx_dataptr_cb; lan966x->rx.max_mtu = lan966x_fdma_get_max_frame(lan966x); lan966x->tx.lan966x = lan966x; - lan966x->tx.channel_id = FDMA_INJ_CHANNEL; - lan966x->tx.last_in_use = -1; + lan966x->tx.fdma.channel_id = FDMA_INJ_CHANNEL; + lan966x->tx.fdma.n_dcbs = FDMA_DCB_MAX; + lan966x->tx.fdma.n_dbs = FDMA_TX_DCB_MAX_DBS; + lan966x->tx.fdma.priv = lan966x; + lan966x->tx.fdma.size = fdma_get_size(&lan966x->tx.fdma); + lan966x->tx.fdma.db_size = PAGE_SIZE << lan966x->rx.page_order; + lan966x->tx.fdma.ops.nextptr_cb = &fdma_nextptr_cb; + lan966x->tx.fdma.ops.dataptr_cb = &lan966x_fdma_tx_dataptr_cb; err = lan966x_fdma_rx_alloc(&lan966x->rx); if (err) @@ -1046,7 +955,7 @@ int lan966x_fdma_init(struct lan966x *lan966x) err = lan966x_fdma_tx_alloc(&lan966x->tx); if (err) { - lan966x_fdma_rx_free(&lan966x->rx); + fdma_free_coherent(lan966x->dev, &lan966x->rx.fdma); return err; } @@ -1067,7 +976,7 @@ void lan966x_fdma_deinit(struct lan966x *lan966x) napi_disable(&lan966x->napi); lan966x_fdma_rx_free_pages(&lan966x->rx); - lan966x_fdma_rx_free(&lan966x->rx); + fdma_free_coherent(lan966x->dev, &lan966x->rx.fdma); page_pool_destroy(lan966x->rx.page_pool); lan966x_fdma_tx_free(&lan966x->tx); } diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c index ec672af12e25..534d4716d5f7 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c @@ -816,7 +816,7 @@ static int lan966x_probe_port(struct lan966x *lan966x, u32 p, NETIF_F_HW_VLAN_STAG_TX | NETIF_F_HW_TC; dev->hw_features |= NETIF_F_HW_TC; - dev->priv_flags |= IFF_SEE_ALL_HWTSTAMP_REQUESTS; + dev->see_all_hwtstamp_requests = true; dev->needed_headroom = IFH_LEN_BYTES; eth_hw_addr_gen(dev, lan966x->base_mac, p + 1); diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h index f8bebbcf77b2..25cb2f61986f 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h @@ -16,6 +16,7 @@ #include <net/switchdev.h> #include <net/xdp.h> +#include <fdma_api.h> #include <vcap_api.h> #include <vcap_api_client.h> @@ -76,15 +77,6 @@ #define FDMA_RX_DCB_MAX_DBS 1 #define FDMA_TX_DCB_MAX_DBS 1 -#define FDMA_DCB_INFO_DATAL(x) ((x) & GENMASK(15, 0)) - -#define FDMA_DCB_STATUS_BLOCKL(x) ((x) & GENMASK(15, 0)) -#define FDMA_DCB_STATUS_SOF BIT(16) -#define FDMA_DCB_STATUS_EOF BIT(17) -#define FDMA_DCB_STATUS_INTR BIT(18) -#define FDMA_DCB_STATUS_DONE BIT(19) -#define FDMA_DCB_STATUS_BLOCKO(x) (((x) << 20) & GENMASK(31, 20)) -#define FDMA_DCB_INVALID_DATA 0x1 #define FDMA_XTR_CHANNEL 6 #define FDMA_INJ_CHANNEL 0 @@ -199,49 +191,14 @@ enum vcap_is1_port_sel_rt { struct lan966x_port; -struct lan966x_db { - u64 dataptr; - u64 status; -}; - -struct lan966x_rx_dcb { - u64 nextptr; - u64 info; - struct lan966x_db db[FDMA_RX_DCB_MAX_DBS]; -}; - -struct lan966x_tx_dcb { - u64 nextptr; - u64 info; - struct lan966x_db db[FDMA_TX_DCB_MAX_DBS]; -}; - struct lan966x_rx { struct lan966x *lan966x; - /* Pointer to the array of hardware dcbs. */ - struct lan966x_rx_dcb *dcbs; - - /* Pointer to the last address in the dcbs. */ - struct lan966x_rx_dcb *last_entry; + struct fdma fdma; /* For each DB, there is a page */ struct page *page[FDMA_DCB_MAX][FDMA_RX_DCB_MAX_DBS]; - /* Represents the db_index, it can have a value between 0 and - * FDMA_RX_DCB_MAX_DBS, once it reaches the value of FDMA_RX_DCB_MAX_DBS - * it means that the DCB can be reused. - */ - int db_index; - - /* Represents the index in the dcbs. It has a value between 0 and - * FDMA_DCB_MAX - */ - int dcb_index; - - /* Represents the dma address to the dcbs array */ - dma_addr_t dma; - /* Represents the page order that is used to allocate the pages for the * RX buffers. This value is calculated based on max MTU of the devices. */ @@ -252,8 +209,6 @@ struct lan966x_rx { */ u32 max_mtu; - u8 channel_id; - struct page_pool *page_pool; }; @@ -275,18 +230,11 @@ struct lan966x_tx_dcb_buf { struct lan966x_tx { struct lan966x *lan966x; - /* Pointer to the dcb list */ - struct lan966x_tx_dcb *dcbs; - u16 last_in_use; - - /* Represents the DMA address to the first entry of the dcb entries. */ - dma_addr_t dma; + struct fdma fdma; /* Array of dcbs that are given to the HW */ struct lan966x_tx_dcb_buf *dcbs_buf; - u8 channel_id; - bool activated; }; diff --git a/drivers/net/ethernet/microchip/sparx5/Kconfig b/drivers/net/ethernet/microchip/sparx5/Kconfig index f58c506bda22..3f04992eace6 100644 --- a/drivers/net/ethernet/microchip/sparx5/Kconfig +++ b/drivers/net/ethernet/microchip/sparx5/Kconfig @@ -10,6 +10,7 @@ config SPARX5_SWITCH select PHY_SPARX5_SERDES select RESET_CONTROLLER select VCAP + select FDMA help This driver supports the Sparx5 network switch device. diff --git a/drivers/net/ethernet/microchip/sparx5/Makefile b/drivers/net/ethernet/microchip/sparx5/Makefile index b68fe9c9a656..288de95add18 100644 --- a/drivers/net/ethernet/microchip/sparx5/Makefile +++ b/drivers/net/ethernet/microchip/sparx5/Makefile @@ -18,3 +18,4 @@ sparx5-switch-$(CONFIG_DEBUG_FS) += sparx5_vcap_debugfs.o # Provide include files ccflags-y += -I$(srctree)/drivers/net/ethernet/microchip/vcap +ccflags-y += -I$(srctree)/drivers/net/ethernet/microchip/fdma diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_ethtool.c b/drivers/net/ethernet/microchip/sparx5/sparx5_ethtool.c index 4f800c1a435d..d898a7238b48 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_ethtool.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_ethtool.c @@ -1194,16 +1194,13 @@ static int sparx5_get_ts_info(struct net_device *dev, phc = &sparx5->phc[SPARX5_PHC_PORT]; - info->phc_index = phc->clock ? ptp_clock_index(phc->clock) : -1; - if (info->phc_index == -1) { - info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE | - SOF_TIMESTAMPING_RX_SOFTWARE | - SOF_TIMESTAMPING_SOFTWARE; + if (phc->clock) { + info->phc_index = ptp_clock_index(phc->clock); + } else { + info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE; return 0; } info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE | - SOF_TIMESTAMPING_RX_SOFTWARE | - SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_TX_HARDWARE | SOF_TIMESTAMPING_RX_HARDWARE | SOF_TIMESTAMPING_RAW_HARDWARE; diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c b/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c index 1915998f6079..61df874b7623 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c @@ -21,107 +21,51 @@ #define FDMA_XTR_CHANNEL 6 #define FDMA_INJ_CHANNEL 0 -#define FDMA_DCB_INFO_DATAL(x) ((x) & GENMASK(15, 0)) -#define FDMA_DCB_INFO_TOKEN BIT(17) -#define FDMA_DCB_INFO_INTR BIT(18) -#define FDMA_DCB_INFO_SW(x) (((x) << 24) & GENMASK(31, 24)) - -#define FDMA_DCB_STATUS_BLOCKL(x) ((x) & GENMASK(15, 0)) -#define FDMA_DCB_STATUS_SOF BIT(16) -#define FDMA_DCB_STATUS_EOF BIT(17) -#define FDMA_DCB_STATUS_INTR BIT(18) -#define FDMA_DCB_STATUS_DONE BIT(19) -#define FDMA_DCB_STATUS_BLOCKO(x) (((x) << 20) & GENMASK(31, 20)) -#define FDMA_DCB_INVALID_DATA 0x1 - #define FDMA_XTR_BUFFER_SIZE 2048 #define FDMA_WEIGHT 4 -/* Frame DMA DCB format - * - * +---------------------------+ - * | Next Ptr | - * +---------------------------+ - * | Reserved | Info | - * +---------------------------+ - * | Data0 Ptr | - * +---------------------------+ - * | Reserved | Status0 | - * +---------------------------+ - * | Data1 Ptr | - * +---------------------------+ - * | Reserved | Status1 | - * +---------------------------+ - * | Data2 Ptr | - * +---------------------------+ - * | Reserved | Status2 | - * |-------------|-------------| - * | | - * | | - * | | - * | | - * | | - * |---------------------------| - * | Data14 Ptr | - * +-------------|-------------+ - * | Reserved | Status14 | - * +-------------|-------------+ - */ - -/* For each hardware DB there is an entry in this list and when the HW DB - * entry is used, this SW DB entry is moved to the back of the list - */ -struct sparx5_db { - struct list_head list; - void *cpu_addr; -}; - -static void sparx5_fdma_rx_add_dcb(struct sparx5_rx *rx, - struct sparx5_rx_dcb_hw *dcb, - u64 nextptr) +static int sparx5_fdma_tx_dataptr_cb(struct fdma *fdma, int dcb, int db, + u64 *dataptr) { - int idx = 0; - - /* Reset the status of the DB */ - for (idx = 0; idx < FDMA_RX_DCB_MAX_DBS; ++idx) { - struct sparx5_db_hw *db = &dcb->db[idx]; + *dataptr = fdma->dma + (sizeof(struct fdma_dcb) * fdma->n_dcbs) + + ((dcb * fdma->n_dbs + db) * fdma->db_size); - db->status = FDMA_DCB_STATUS_INTR; - } - dcb->nextptr = FDMA_DCB_INVALID_DATA; - dcb->info = FDMA_DCB_INFO_DATAL(FDMA_XTR_BUFFER_SIZE); - rx->last_entry->nextptr = nextptr; - rx->last_entry = dcb; + return 0; } -static void sparx5_fdma_tx_add_dcb(struct sparx5_tx *tx, - struct sparx5_tx_dcb_hw *dcb, - u64 nextptr) +static int sparx5_fdma_rx_dataptr_cb(struct fdma *fdma, int dcb, int db, + u64 *dataptr) { - int idx = 0; + struct sparx5 *sparx5 = fdma->priv; + struct sparx5_rx *rx = &sparx5->rx; + struct sk_buff *skb; - /* Reset the status of the DB */ - for (idx = 0; idx < FDMA_TX_DCB_MAX_DBS; ++idx) { - struct sparx5_db_hw *db = &dcb->db[idx]; + skb = __netdev_alloc_skb(rx->ndev, fdma->db_size, GFP_ATOMIC); + if (unlikely(!skb)) + return -ENOMEM; - db->status = FDMA_DCB_STATUS_DONE; - } - dcb->nextptr = FDMA_DCB_INVALID_DATA; - dcb->info = FDMA_DCB_INFO_DATAL(FDMA_XTR_BUFFER_SIZE); + *dataptr = virt_to_phys(skb->data); + + rx->skb[dcb][db] = skb; + + return 0; } static void sparx5_fdma_rx_activate(struct sparx5 *sparx5, struct sparx5_rx *rx) { + struct fdma *fdma = &rx->fdma; + /* Write the buffer address in the LLP and LLP1 regs */ - spx5_wr(((u64)rx->dma) & GENMASK(31, 0), sparx5, - FDMA_DCB_LLP(rx->channel_id)); - spx5_wr(((u64)rx->dma) >> 32, sparx5, FDMA_DCB_LLP1(rx->channel_id)); + spx5_wr(((u64)fdma->dma) & GENMASK(31, 0), sparx5, + FDMA_DCB_LLP(fdma->channel_id)); + spx5_wr(((u64)fdma->dma) >> 32, sparx5, + FDMA_DCB_LLP1(fdma->channel_id)); /* Set the number of RX DBs to be used, and DB end-of-frame interrupt */ - spx5_wr(FDMA_CH_CFG_CH_DCB_DB_CNT_SET(FDMA_RX_DCB_MAX_DBS) | + spx5_wr(FDMA_CH_CFG_CH_DCB_DB_CNT_SET(fdma->n_dbs) | FDMA_CH_CFG_CH_INTR_DB_EOF_ONLY_SET(1) | FDMA_CH_CFG_CH_INJ_PORT_SET(XTR_QUEUE), - sparx5, FDMA_CH_CFG(rx->channel_id)); + sparx5, FDMA_CH_CFG(fdma->channel_id)); /* Set the RX Watermark to max */ spx5_rmw(FDMA_XTR_CFG_XTR_FIFO_WM_SET(31), FDMA_XTR_CFG_XTR_FIFO_WM, @@ -133,22 +77,24 @@ static void sparx5_fdma_rx_activate(struct sparx5 *sparx5, struct sparx5_rx *rx) sparx5, FDMA_PORT_CTRL(0)); /* Enable RX channel DB interrupt */ - spx5_rmw(BIT(rx->channel_id), - BIT(rx->channel_id) & FDMA_INTR_DB_ENA_INTR_DB_ENA, + spx5_rmw(BIT(fdma->channel_id), + BIT(fdma->channel_id) & FDMA_INTR_DB_ENA_INTR_DB_ENA, sparx5, FDMA_INTR_DB_ENA); /* Activate the RX channel */ - spx5_wr(BIT(rx->channel_id), sparx5, FDMA_CH_ACTIVATE); + spx5_wr(BIT(fdma->channel_id), sparx5, FDMA_CH_ACTIVATE); } static void sparx5_fdma_rx_deactivate(struct sparx5 *sparx5, struct sparx5_rx *rx) { + struct fdma *fdma = &rx->fdma; + /* Deactivate the RX channel */ - spx5_rmw(0, BIT(rx->channel_id) & FDMA_CH_ACTIVATE_CH_ACTIVATE, + spx5_rmw(0, BIT(fdma->channel_id) & FDMA_CH_ACTIVATE_CH_ACTIVATE, sparx5, FDMA_CH_ACTIVATE); /* Disable RX channel DB interrupt */ - spx5_rmw(0, BIT(rx->channel_id) & FDMA_INTR_DB_ENA_INTR_DB_ENA, + spx5_rmw(0, BIT(fdma->channel_id) & FDMA_INTR_DB_ENA_INTR_DB_ENA, sparx5, FDMA_INTR_DB_ENA); /* Stop RX fdma */ @@ -158,75 +104,55 @@ static void sparx5_fdma_rx_deactivate(struct sparx5 *sparx5, struct sparx5_rx *r static void sparx5_fdma_tx_activate(struct sparx5 *sparx5, struct sparx5_tx *tx) { + struct fdma *fdma = &tx->fdma; + /* Write the buffer address in the LLP and LLP1 regs */ - spx5_wr(((u64)tx->dma) & GENMASK(31, 0), sparx5, - FDMA_DCB_LLP(tx->channel_id)); - spx5_wr(((u64)tx->dma) >> 32, sparx5, FDMA_DCB_LLP1(tx->channel_id)); + spx5_wr(((u64)fdma->dma) & GENMASK(31, 0), sparx5, + FDMA_DCB_LLP(fdma->channel_id)); + spx5_wr(((u64)fdma->dma) >> 32, sparx5, + FDMA_DCB_LLP1(fdma->channel_id)); /* Set the number of TX DBs to be used, and DB end-of-frame interrupt */ - spx5_wr(FDMA_CH_CFG_CH_DCB_DB_CNT_SET(FDMA_TX_DCB_MAX_DBS) | + spx5_wr(FDMA_CH_CFG_CH_DCB_DB_CNT_SET(fdma->n_dbs) | FDMA_CH_CFG_CH_INTR_DB_EOF_ONLY_SET(1) | FDMA_CH_CFG_CH_INJ_PORT_SET(INJ_QUEUE), - sparx5, FDMA_CH_CFG(tx->channel_id)); + sparx5, FDMA_CH_CFG(fdma->channel_id)); /* Start TX fdma */ spx5_rmw(FDMA_PORT_CTRL_INJ_STOP_SET(0), FDMA_PORT_CTRL_INJ_STOP, sparx5, FDMA_PORT_CTRL(0)); /* Activate the channel */ - spx5_wr(BIT(tx->channel_id), sparx5, FDMA_CH_ACTIVATE); + spx5_wr(BIT(fdma->channel_id), sparx5, FDMA_CH_ACTIVATE); } static void sparx5_fdma_tx_deactivate(struct sparx5 *sparx5, struct sparx5_tx *tx) { /* Disable the channel */ - spx5_rmw(0, BIT(tx->channel_id) & FDMA_CH_ACTIVATE_CH_ACTIVATE, + spx5_rmw(0, BIT(tx->fdma.channel_id) & FDMA_CH_ACTIVATE_CH_ACTIVATE, sparx5, FDMA_CH_ACTIVATE); } -static void sparx5_fdma_rx_reload(struct sparx5 *sparx5, struct sparx5_rx *rx) +static void sparx5_fdma_reload(struct sparx5 *sparx5, struct fdma *fdma) { /* Reload the RX channel */ - spx5_wr(BIT(rx->channel_id), sparx5, FDMA_CH_RELOAD); -} - -static void sparx5_fdma_tx_reload(struct sparx5 *sparx5, struct sparx5_tx *tx) -{ - /* Reload the TX channel */ - spx5_wr(BIT(tx->channel_id), sparx5, FDMA_CH_RELOAD); -} - -static struct sk_buff *sparx5_fdma_rx_alloc_skb(struct sparx5_rx *rx) -{ - return __netdev_alloc_skb(rx->ndev, FDMA_XTR_BUFFER_SIZE, - GFP_ATOMIC); + spx5_wr(BIT(fdma->channel_id), sparx5, FDMA_CH_RELOAD); } static bool sparx5_fdma_rx_get_frame(struct sparx5 *sparx5, struct sparx5_rx *rx) { - struct sparx5_db_hw *db_hw; - unsigned int packet_size; + struct fdma *fdma = &rx->fdma; struct sparx5_port *port; - struct sk_buff *new_skb; + struct fdma_db *db_hw; struct frame_info fi; struct sk_buff *skb; - dma_addr_t dma_addr; /* Check if the DCB is done */ - db_hw = &rx->dcb_entries[rx->dcb_index].db[rx->db_index]; - if (unlikely(!(db_hw->status & FDMA_DCB_STATUS_DONE))) - return false; - skb = rx->skb[rx->dcb_index][rx->db_index]; - /* Replace the DB entry with a new SKB */ - new_skb = sparx5_fdma_rx_alloc_skb(rx); - if (unlikely(!new_skb)) + db_hw = fdma_db_next_get(fdma); + if (unlikely(!fdma_db_is_done(db_hw))) return false; - /* Map the new skb data and set the new skb */ - dma_addr = virt_to_phys(new_skb->data); - rx->skb[rx->dcb_index][rx->db_index] = new_skb; - db_hw->dataptr = dma_addr; - packet_size = FDMA_DCB_STATUS_BLOCKL(db_hw->status); - skb_put(skb, packet_size); + skb = rx->skb[fdma->dcb_index][fdma->db_index]; + skb_put(skb, fdma_db_len_get(db_hw)); /* Now do the normal processing of the skb */ sparx5_ifh_parse((u32 *)skb->data, &fi); /* Map to port netdev */ @@ -259,84 +185,62 @@ static int sparx5_fdma_napi_callback(struct napi_struct *napi, int weight) { struct sparx5_rx *rx = container_of(napi, struct sparx5_rx, napi); struct sparx5 *sparx5 = container_of(rx, struct sparx5, rx); + struct fdma *fdma = &rx->fdma; int counter = 0; while (counter < weight && sparx5_fdma_rx_get_frame(sparx5, rx)) { - struct sparx5_rx_dcb_hw *old_dcb; - - rx->db_index++; + fdma_db_advance(fdma); counter++; /* Check if the DCB can be reused */ - if (rx->db_index != FDMA_RX_DCB_MAX_DBS) + if (fdma_dcb_is_reusable(fdma)) continue; - /* As the DCB can be reused, just advance the dcb_index - * pointer and set the nextptr in the DCB - */ - rx->db_index = 0; - old_dcb = &rx->dcb_entries[rx->dcb_index]; - rx->dcb_index++; - rx->dcb_index &= FDMA_DCB_MAX - 1; - sparx5_fdma_rx_add_dcb(rx, old_dcb, - rx->dma + - ((unsigned long)old_dcb - - (unsigned long)rx->dcb_entries)); + fdma_dcb_add(fdma, fdma->dcb_index, + FDMA_DCB_INFO_DATAL(fdma->db_size), + FDMA_DCB_STATUS_INTR); + fdma_db_reset(fdma); + fdma_dcb_advance(fdma); } if (counter < weight) { napi_complete_done(&rx->napi, counter); - spx5_rmw(BIT(rx->channel_id), - BIT(rx->channel_id) & FDMA_INTR_DB_ENA_INTR_DB_ENA, + spx5_rmw(BIT(fdma->channel_id), + BIT(fdma->channel_id) & FDMA_INTR_DB_ENA_INTR_DB_ENA, sparx5, FDMA_INTR_DB_ENA); } if (counter) - sparx5_fdma_rx_reload(sparx5, rx); + sparx5_fdma_reload(sparx5, fdma); return counter; } -static struct sparx5_tx_dcb_hw *sparx5_fdma_next_dcb(struct sparx5_tx *tx, - struct sparx5_tx_dcb_hw *dcb) -{ - struct sparx5_tx_dcb_hw *next_dcb; - - next_dcb = dcb; - next_dcb++; - /* Handle wrap-around */ - if ((unsigned long)next_dcb >= - ((unsigned long)tx->first_entry + FDMA_DCB_MAX * sizeof(*dcb))) - next_dcb = tx->first_entry; - return next_dcb; -} - int sparx5_fdma_xmit(struct sparx5 *sparx5, u32 *ifh, struct sk_buff *skb) { - struct sparx5_tx_dcb_hw *next_dcb_hw; struct sparx5_tx *tx = &sparx5->tx; + struct fdma *fdma = &tx->fdma; static bool first_time = true; - struct sparx5_db_hw *db_hw; - struct sparx5_db *db; + void *virt_addr; - next_dcb_hw = sparx5_fdma_next_dcb(tx, tx->curr_entry); - db_hw = &next_dcb_hw->db[0]; - if (!(db_hw->status & FDMA_DCB_STATUS_DONE)) + fdma_dcb_advance(fdma); + if (!fdma_db_is_done(fdma_db_get(fdma, fdma->dcb_index, 0))) return -EINVAL; - db = list_first_entry(&tx->db_list, struct sparx5_db, list); - list_move_tail(&db->list, &tx->db_list); - next_dcb_hw->nextptr = FDMA_DCB_INVALID_DATA; - tx->curr_entry->nextptr = tx->dma + - ((unsigned long)next_dcb_hw - - (unsigned long)tx->first_entry); - tx->curr_entry = next_dcb_hw; - memset(db->cpu_addr, 0, FDMA_XTR_BUFFER_SIZE); - memcpy(db->cpu_addr, ifh, IFH_LEN * 4); - memcpy(db->cpu_addr + IFH_LEN * 4, skb->data, skb->len); - db_hw->status = FDMA_DCB_STATUS_SOF | - FDMA_DCB_STATUS_EOF | - FDMA_DCB_STATUS_BLOCKO(0) | - FDMA_DCB_STATUS_BLOCKL(skb->len + IFH_LEN * 4 + 4); + + /* Get the virtual address of the dataptr for the next DB */ + virt_addr = ((u8 *)fdma->dcbs + + (sizeof(struct fdma_dcb) * fdma->n_dcbs) + + ((fdma->dcb_index * fdma->n_dbs) * fdma->db_size)); + + memcpy(virt_addr, ifh, IFH_LEN * 4); + memcpy(virt_addr + IFH_LEN * 4, skb->data, skb->len); + + fdma_dcb_add(fdma, fdma->dcb_index, 0, + FDMA_DCB_STATUS_SOF | + FDMA_DCB_STATUS_EOF | + FDMA_DCB_STATUS_BLOCKO(0) | + FDMA_DCB_STATUS_BLOCKL(skb->len + IFH_LEN * 4 + 4)); + if (first_time) { sparx5_fdma_tx_activate(sparx5, tx); first_time = false; } else { - sparx5_fdma_tx_reload(sparx5, tx); + sparx5_fdma_reload(sparx5, fdma); } return NETDEV_TX_OK; } @@ -344,43 +248,16 @@ int sparx5_fdma_xmit(struct sparx5 *sparx5, u32 *ifh, struct sk_buff *skb) static int sparx5_fdma_rx_alloc(struct sparx5 *sparx5) { struct sparx5_rx *rx = &sparx5->rx; - struct sparx5_rx_dcb_hw *dcb; - int idx, jdx; - int size; - - size = sizeof(struct sparx5_rx_dcb_hw) * FDMA_DCB_MAX; - size = ALIGN(size, PAGE_SIZE); - rx->dcb_entries = devm_kzalloc(sparx5->dev, size, GFP_KERNEL); - if (!rx->dcb_entries) - return -ENOMEM; - rx->dma = virt_to_phys(rx->dcb_entries); - rx->last_entry = rx->dcb_entries; - rx->db_index = 0; - rx->dcb_index = 0; - /* Now for each dcb allocate the db */ - for (idx = 0; idx < FDMA_DCB_MAX; ++idx) { - dcb = &rx->dcb_entries[idx]; - dcb->info = 0; - /* For each db allocate an skb and map skb data pointer to the DB - * dataptr. In this way when the frame is received the skb->data - * will contain the frame, so no memcpy is needed - */ - for (jdx = 0; jdx < FDMA_RX_DCB_MAX_DBS; ++jdx) { - struct sparx5_db_hw *db_hw = &dcb->db[jdx]; - dma_addr_t dma_addr; - struct sk_buff *skb; - - skb = sparx5_fdma_rx_alloc_skb(rx); - if (!skb) - return -ENOMEM; - - dma_addr = virt_to_phys(skb->data); - db_hw->dataptr = dma_addr; - db_hw->status = 0; - rx->skb[idx][jdx] = skb; - } - sparx5_fdma_rx_add_dcb(rx, dcb, rx->dma + sizeof(*dcb) * idx); - } + struct fdma *fdma = &rx->fdma; + int err; + + err = fdma_alloc_phys(fdma); + if (err) + return err; + + fdma_dcbs_init(fdma, FDMA_DCB_INFO_DATAL(fdma->db_size), + FDMA_DCB_STATUS_INTR); + netif_napi_add_weight(rx->ndev, &rx->napi, sparx5_fdma_napi_callback, FDMA_WEIGHT); napi_enable(&rx->napi); @@ -391,57 +268,33 @@ static int sparx5_fdma_rx_alloc(struct sparx5 *sparx5) static int sparx5_fdma_tx_alloc(struct sparx5 *sparx5) { struct sparx5_tx *tx = &sparx5->tx; - struct sparx5_tx_dcb_hw *dcb; - int idx, jdx; - int size; - - size = sizeof(struct sparx5_tx_dcb_hw) * FDMA_DCB_MAX; - size = ALIGN(size, PAGE_SIZE); - tx->curr_entry = devm_kzalloc(sparx5->dev, size, GFP_KERNEL); - if (!tx->curr_entry) - return -ENOMEM; - tx->dma = virt_to_phys(tx->curr_entry); - tx->first_entry = tx->curr_entry; - INIT_LIST_HEAD(&tx->db_list); - /* Now for each dcb allocate the db */ - for (idx = 0; idx < FDMA_DCB_MAX; ++idx) { - dcb = &tx->curr_entry[idx]; - dcb->info = 0; - /* TX databuffers must be 16byte aligned */ - for (jdx = 0; jdx < FDMA_TX_DCB_MAX_DBS; ++jdx) { - struct sparx5_db_hw *db_hw = &dcb->db[jdx]; - struct sparx5_db *db; - dma_addr_t phys; - void *cpu_addr; - - cpu_addr = devm_kzalloc(sparx5->dev, - FDMA_XTR_BUFFER_SIZE, - GFP_KERNEL); - if (!cpu_addr) - return -ENOMEM; - phys = virt_to_phys(cpu_addr); - db_hw->dataptr = phys; - db_hw->status = 0; - db = devm_kzalloc(sparx5->dev, sizeof(*db), GFP_KERNEL); - if (!db) - return -ENOMEM; - db->cpu_addr = cpu_addr; - list_add_tail(&db->list, &tx->db_list); - } - sparx5_fdma_tx_add_dcb(tx, dcb, tx->dma + sizeof(*dcb) * idx); - /* Let the curr_entry to point to the last allocated entry */ - if (idx == FDMA_DCB_MAX - 1) - tx->curr_entry = dcb; - } + struct fdma *fdma = &tx->fdma; + int err; + + err = fdma_alloc_phys(fdma); + if (err) + return err; + + fdma_dcbs_init(fdma, FDMA_DCB_INFO_DATAL(fdma->db_size), + FDMA_DCB_STATUS_DONE); + return 0; } static void sparx5_fdma_rx_init(struct sparx5 *sparx5, struct sparx5_rx *rx, int channel) { + struct fdma *fdma = &rx->fdma; int idx; - rx->channel_id = channel; + fdma->channel_id = channel; + fdma->n_dcbs = FDMA_DCB_MAX; + fdma->n_dbs = FDMA_RX_DCB_MAX_DBS; + fdma->priv = sparx5; + fdma->db_size = ALIGN(FDMA_XTR_BUFFER_SIZE, PAGE_SIZE); + fdma->size = fdma_get_size(&sparx5->rx.fdma); + fdma->ops.dataptr_cb = &sparx5_fdma_rx_dataptr_cb; + fdma->ops.nextptr_cb = &fdma_nextptr_cb; /* Fetch a netdev for SKB and NAPI use, any will do */ for (idx = 0; idx < SPX5_PORTS; ++idx) { struct sparx5_port *port = sparx5->ports[idx]; @@ -456,7 +309,16 @@ static void sparx5_fdma_rx_init(struct sparx5 *sparx5, static void sparx5_fdma_tx_init(struct sparx5 *sparx5, struct sparx5_tx *tx, int channel) { - tx->channel_id = channel; + struct fdma *fdma = &tx->fdma; + + fdma->channel_id = channel; + fdma->n_dcbs = FDMA_DCB_MAX; + fdma->n_dbs = FDMA_TX_DCB_MAX_DBS; + fdma->priv = sparx5; + fdma->db_size = ALIGN(FDMA_XTR_BUFFER_SIZE, PAGE_SIZE); + fdma->size = fdma_get_size_contiguous(&sparx5->tx.fdma); + fdma->ops.dataptr_cb = &sparx5_fdma_tx_dataptr_cb; + fdma->ops.nextptr_cb = &fdma_nextptr_cb; } irqreturn_t sparx5_fdma_handler(int irq, void *args) @@ -594,5 +456,7 @@ int sparx5_fdma_stop(struct sparx5 *sparx5) read_poll_timeout(sparx5_fdma_port_ctrl, val, FDMA_PORT_CTRL_XTR_BUF_IS_EMPTY_GET(val) == 0, 500, 10000, 0, sparx5); + fdma_free_phys(&sparx5->rx.fdma); + fdma_free_phys(&sparx5->tx.fdma); return 0; } diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h index 1982ae03b4fe..3309060b1e4c 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h @@ -20,6 +20,8 @@ #include <linux/debugfs.h> #include <net/flow_offload.h> +#include <fdma_api.h> + #include "sparx5_main_regs.h" /* Target chip type */ @@ -100,23 +102,6 @@ enum sparx5_vlan_port_type { struct sparx5; -struct sparx5_db_hw { - u64 dataptr; - u64 status; -}; - -struct sparx5_rx_dcb_hw { - u64 nextptr; - u64 info; - struct sparx5_db_hw db[FDMA_RX_DCB_MAX_DBS]; -}; - -struct sparx5_tx_dcb_hw { - u64 nextptr; - u64 info; - struct sparx5_db_hw db[FDMA_TX_DCB_MAX_DBS]; -}; - /* Frame DMA receive state: * For each DB, there is a SKB, and the skb data pointer is mapped in * the DB. Once a frame is received the skb is given to the upper layers @@ -124,14 +109,10 @@ struct sparx5_tx_dcb_hw { * When the db_index reached FDMA_RX_DCB_MAX_DBS the DB is reused. */ struct sparx5_rx { - struct sparx5_rx_dcb_hw *dcb_entries; - struct sparx5_rx_dcb_hw *last_entry; + struct fdma fdma; struct sk_buff *skb[FDMA_DCB_MAX][FDMA_RX_DCB_MAX_DBS]; - int db_index; - int dcb_index; dma_addr_t dma; struct napi_struct napi; - u32 channel_id; struct net_device *ndev; u64 packets; }; @@ -140,11 +121,7 @@ struct sparx5_rx { * DCBs are chained using the DCBs nextptr field. */ struct sparx5_tx { - struct sparx5_tx_dcb_hw *curr_entry; - struct sparx5_tx_dcb_hw *first_entry; - struct list_head db_list; - dma_addr_t dma; - u32 channel_id; + struct fdma fdma; u64 packets; u64 dropped; }; diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c index 51d9423b08a6..f2a5a36fdacd 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c @@ -1442,18 +1442,8 @@ static void vcap_api_encode_rule_test(struct kunit *test) vcap_enable_lookups(&test_vctrl, &test_netdev, 0, 0, rule->cookie, false); - vcap_free_rule(rule); - - /* Check that the rule has been freed: tricky to access since this - * memory should not be accessible anymore - */ - KUNIT_EXPECT_PTR_NE(test, NULL, rule); - ret = list_empty(&rule->keyfields); - KUNIT_EXPECT_EQ(test, true, ret); - ret = list_empty(&rule->actionfields); - KUNIT_EXPECT_EQ(test, true, ret); - - vcap_del_rule(&test_vctrl, &test_netdev, id); + ret = vcap_del_rule(&test_vctrl, &test_netdev, id); + KUNIT_EXPECT_EQ(test, 0, ret); } static void vcap_api_set_rule_counter_test(struct kunit *test) |