diff options
Diffstat (limited to 'drivers/net/ethernet/microchip')
31 files changed, 1050 insertions, 135 deletions
diff --git a/drivers/net/ethernet/microchip/Kconfig b/drivers/net/ethernet/microchip/Kconfig index 73832fb2bc32..ee046468652c 100644 --- a/drivers/net/ethernet/microchip/Kconfig +++ b/drivers/net/ethernet/microchip/Kconfig @@ -59,7 +59,6 @@ config LAN743X source "drivers/net/ethernet/microchip/lan865x/Kconfig" source "drivers/net/ethernet/microchip/lan966x/Kconfig" -source "drivers/net/ethernet/microchip/lan969x/Kconfig" source "drivers/net/ethernet/microchip/sparx5/Kconfig" source "drivers/net/ethernet/microchip/vcap/Kconfig" source "drivers/net/ethernet/microchip/fdma/Kconfig" diff --git a/drivers/net/ethernet/microchip/Makefile b/drivers/net/ethernet/microchip/Makefile index 7770df82200f..3c65baed9fd8 100644 --- a/drivers/net/ethernet/microchip/Makefile +++ b/drivers/net/ethernet/microchip/Makefile @@ -11,7 +11,6 @@ lan743x-objs := lan743x_main.o lan743x_ethtool.o lan743x_ptp.o obj-$(CONFIG_LAN865X) += lan865x/ obj-$(CONFIG_LAN966X_SWITCH) += lan966x/ -obj-$(CONFIG_LAN969X_SWITCH) += lan969x/ obj-$(CONFIG_SPARX5_SWITCH) += sparx5/ obj-$(CONFIG_VCAP) += vcap/ obj-$(CONFIG_FDMA) += fdma/ diff --git a/drivers/net/ethernet/microchip/lan743x_ethtool.c b/drivers/net/ethernet/microchip/lan743x_ethtool.c index 1a1cbd034eda..1459acfb1e61 100644 --- a/drivers/net/ethernet/microchip/lan743x_ethtool.c +++ b/drivers/net/ethernet/microchip/lan743x_ethtool.c @@ -1055,9 +1055,6 @@ static int lan743x_ethtool_get_eee(struct net_device *netdev, { struct lan743x_adapter *adapter = netdev_priv(netdev); - eee->tx_lpi_timer = lan743x_csr_read(adapter, - MAC_EEE_TX_LPI_REQ_DLY_CNT); - return phylink_ethtool_get_eee(adapter->phylink, eee); } @@ -1065,24 +1062,6 @@ static int lan743x_ethtool_set_eee(struct net_device *netdev, struct ethtool_keee *eee) { struct lan743x_adapter *adapter = netdev_priv(netdev); - u32 tx_lpi_timer; - - 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 (mac_cr & MAC_CR_EEE_EN_) - lan743x_mac_eee_enable(adapter, true); - } return phylink_ethtool_set_eee(adapter->phylink, eee); } diff --git a/drivers/net/ethernet/microchip/lan743x_main.c b/drivers/net/ethernet/microchip/lan743x_main.c index 4dc5adcda6a3..23760b613d3e 100644 --- a/drivers/net/ethernet/microchip/lan743x_main.c +++ b/drivers/net/ethernet/microchip/lan743x_main.c @@ -2966,7 +2966,7 @@ static int lan743x_phylink_2500basex_config(struct lan743x_adapter *adapter) return lan743x_pcs_power_reset(adapter); } -void lan743x_mac_eee_enable(struct lan743x_adapter *adapter, bool enable) +static void lan743x_mac_eee_enable(struct lan743x_adapter *adapter, bool enable) { u32 mac_cr; @@ -3027,10 +3027,8 @@ static void lan743x_phylink_mac_link_down(struct phylink_config *config, 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); + netif_tx_stop_all_queues(netdev); } static void lan743x_phylink_mac_link_up(struct phylink_config *config, @@ -3072,16 +3070,40 @@ static void lan743x_phylink_mac_link_up(struct phylink_config *config, 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 void lan743x_mac_disable_tx_lpi(struct phylink_config *config) +{ + struct net_device *netdev = to_net_dev(config->dev); + struct lan743x_adapter *adapter = netdev_priv(netdev); + + lan743x_mac_eee_enable(adapter, false); +} + +static int lan743x_mac_enable_tx_lpi(struct phylink_config *config, u32 timer, + bool tx_clk_stop) +{ + struct net_device *netdev = to_net_dev(config->dev); + struct lan743x_adapter *adapter = netdev_priv(netdev); + + /* Software should only change this field when Energy Efficient + * Ethernet Enable (EEEEN) is cleared. We ensure that by clearing + * EEEEN during probe, and phylink itself guarantees that + * mac_disable_tx_lpi() will have been previously called. + */ + lan743x_csr_write(adapter, MAC_EEE_TX_LPI_REQ_DLY_CNT, timer); + lan743x_mac_eee_enable(adapter, true); + + return 0; +} + 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, + .mac_disable_tx_lpi = lan743x_mac_disable_tx_lpi, + .mac_enable_tx_lpi = lan743x_mac_enable_tx_lpi, }; static int lan743x_phylink_create(struct lan743x_adapter *adapter) @@ -3095,6 +3117,9 @@ static int lan743x_phylink_create(struct lan743x_adapter *adapter) adapter->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_10 | MAC_100 | MAC_1000FD; + adapter->phylink_config.lpi_capabilities = MAC_100FD | MAC_1000FD; + adapter->phylink_config.lpi_timer_default = + lan743x_csr_read(adapter, MAC_EEE_TX_LPI_REQ_DLY_CNT); lan743x_phy_interface_select(adapter); @@ -3120,6 +3145,10 @@ static int lan743x_phylink_create(struct lan743x_adapter *adapter) phy_interface_set_rgmii(adapter->phylink_config.supported_interfaces); } + memcpy(adapter->phylink_config.lpi_interfaces, + adapter->phylink_config.supported_interfaces, + sizeof(adapter->phylink_config.lpi_interfaces)); + pl = phylink_create(&adapter->phylink_config, NULL, adapter->phy_interface, &lan743x_phylink_mac_ops); @@ -3517,6 +3546,9 @@ static int lan743x_hardware_init(struct lan743x_adapter *adapter, spin_lock_init(&tx->ring_lock); } + /* Ensure EEEEN is clear */ + lan743x_mac_eee_enable(adapter, false); + return 0; } diff --git a/drivers/net/ethernet/microchip/lan743x_main.h b/drivers/net/ethernet/microchip/lan743x_main.h index 8ef897c114d3..7f73d66854be 100644 --- a/drivers/net/ethernet/microchip/lan743x_main.h +++ b/drivers/net/ethernet/microchip/lan743x_main.h @@ -1206,6 +1206,5 @@ 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/lan966x/lan966x_main.h b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h index 25cb2f61986f..1efa584e7107 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h @@ -443,7 +443,7 @@ int lan966x_stats_init(struct lan966x *lan966x); void lan966x_port_config_down(struct lan966x_port *port); void lan966x_port_config_up(struct lan966x_port *port); -void lan966x_port_status_get(struct lan966x_port *port, +void lan966x_port_status_get(struct lan966x_port *port, unsigned int neg_mode, struct phylink_link_state *state); int lan966x_port_pcs_set(struct lan966x_port *port, struct lan966x_port_config *config); diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c b/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c index 1d63903f9006..75188b99e4e7 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c @@ -88,11 +88,12 @@ static struct lan966x_port *lan966x_pcs_to_port(struct phylink_pcs *pcs) } static void lan966x_pcs_get_state(struct phylink_pcs *pcs, + unsigned int neg_mode, struct phylink_link_state *state) { struct lan966x_port *port = lan966x_pcs_to_port(pcs); - lan966x_port_status_get(port, state); + lan966x_port_status_get(port, neg_mode, state); } static int lan966x_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_port.c b/drivers/net/ethernet/microchip/lan966x/lan966x_port.c index fdfa4040d9ee..cf7de0267c32 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_port.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_port.c @@ -284,7 +284,7 @@ void lan966x_port_config_up(struct lan966x_port *port) lan966x_port_link_up(port); } -void lan966x_port_status_get(struct lan966x_port *port, +void lan966x_port_status_get(struct lan966x_port *port, unsigned int neg_mode, struct phylink_link_state *state) { struct lan966x *lan966x = port->lan966x; @@ -314,7 +314,7 @@ void lan966x_port_status_get(struct lan966x_port *port, bmsr |= BMSR_ANEGCOMPLETE; lp_adv = DEV_PCS1G_ANEG_STATUS_LP_ADV_GET(val); - phylink_mii_c22_pcs_decode_state(state, bmsr, lp_adv); + phylink_mii_c22_pcs_decode_state(state, neg_mode, bmsr, lp_adv); } else { if (!state->link) return; diff --git a/drivers/net/ethernet/microchip/lan969x/Kconfig b/drivers/net/ethernet/microchip/lan969x/Kconfig deleted file mode 100644 index c5c6122ae2ec..000000000000 --- a/drivers/net/ethernet/microchip/lan969x/Kconfig +++ /dev/null @@ -1,5 +0,0 @@ -config LAN969X_SWITCH - bool "Lan969x switch driver" - depends on SPARX5_SWITCH - help - This driver supports the lan969x family of network switch devices. diff --git a/drivers/net/ethernet/microchip/lan969x/Makefile b/drivers/net/ethernet/microchip/lan969x/Makefile deleted file mode 100644 index 316405cbbc71..000000000000 --- a/drivers/net/ethernet/microchip/lan969x/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Makefile for the Microchip lan969x network device drivers. -# - -obj-$(CONFIG_SPARX5_SWITCH) += lan969x-switch.o - -lan969x-switch-y := lan969x_regs.o lan969x.o lan969x_calendar.o \ - lan969x_vcap_ag_api.o lan969x_vcap_impl.o - -# Provide include files -ccflags-y += -I$(srctree)/drivers/net/ethernet/microchip/fdma -ccflags-y += -I$(srctree)/drivers/net/ethernet/microchip/vcap diff --git a/drivers/net/ethernet/microchip/sparx5/Kconfig b/drivers/net/ethernet/microchip/sparx5/Kconfig index 3f04992eace6..35e1c0cf345e 100644 --- a/drivers/net/ethernet/microchip/sparx5/Kconfig +++ b/drivers/net/ethernet/microchip/sparx5/Kconfig @@ -24,3 +24,10 @@ config SPARX5_DCB DSCP and PCP. If unsure, set to Y. + +config LAN969X_SWITCH + bool "Lan969x switch driver" + depends on SPARX5_SWITCH + select PAGE_POOL + help + This driver supports the lan969x family of network switch devices. diff --git a/drivers/net/ethernet/microchip/sparx5/Makefile b/drivers/net/ethernet/microchip/sparx5/Makefile index 3435ca86dd70..d447f9e84d92 100644 --- a/drivers/net/ethernet/microchip/sparx5/Makefile +++ b/drivers/net/ethernet/microchip/sparx5/Makefile @@ -16,6 +16,14 @@ sparx5-switch-y := sparx5_main.o sparx5_packet.o \ sparx5-switch-$(CONFIG_SPARX5_DCB) += sparx5_dcb.o sparx5-switch-$(CONFIG_DEBUG_FS) += sparx5_vcap_debugfs.o +sparx5-switch-$(CONFIG_LAN969X_SWITCH) += lan969x/lan969x_regs.o \ + lan969x/lan969x.o \ + lan969x/lan969x_calendar.o \ + lan969x/lan969x_vcap_ag_api.o \ + lan969x/lan969x_vcap_impl.o \ + lan969x/lan969x_rgmii.o \ + lan969x/lan969x_fdma.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/lan969x/lan969x.c b/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x.c index ac37d0f74ee3..f3a9c71bea36 100644 --- a/drivers/net/ethernet/microchip/lan969x/lan969x.c +++ b/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x.c @@ -90,9 +90,12 @@ static const struct sparx5_main_io_resource lan969x_main_iomap[] = { { TARGET_DEV2G5 + 27, 0x30d8000, 1 }, /* 0xe30d8000 */ { TARGET_DEV10G + 9, 0x30dc000, 1 }, /* 0xe30dc000 */ { TARGET_PCS10G_BR + 9, 0x30e0000, 1 }, /* 0xe30e0000 */ + { TARGET_DEVRGMII, 0x30e4000, 1 }, /* 0xe30e4000 */ + { TARGET_DEVRGMII + 1, 0x30e8000, 1 }, /* 0xe30e8000 */ { TARGET_DSM, 0x30ec000, 1 }, /* 0xe30ec000 */ { TARGET_PORT_CONF, 0x30f0000, 1 }, /* 0xe30f0000 */ { TARGET_ASM, 0x3200000, 1 }, /* 0xe3200000 */ + { TARGET_HSIO_WRAP, 0x3408000, 1 }, /* 0xe3408000 */ }; static struct sparx5_sdlb_group lan969x_sdlb_groups[LAN969X_SDLB_GRP_CNT] = { @@ -273,9 +276,9 @@ static irqreturn_t lan969x_ptp_irq_handler(int irq, void *args) if (WARN_ON(!skb_match)) continue; - spin_lock(&sparx5->ptp_ts_id_lock); + spin_lock_irqsave(&sparx5->ptp_ts_id_lock, flags); sparx5->ptp_skbs--; - spin_unlock(&sparx5->ptp_ts_id_lock); + spin_unlock_irqrestore(&sparx5->ptp_ts_id_lock, flags); /* Get the h/w timestamp */ sparx5_get_hwtimestamp(sparx5, &ts, delay); @@ -329,6 +332,7 @@ static const struct sparx5_ops lan969x_ops = { .is_port_5g = &lan969x_port_is_5g, .is_port_10g = &lan969x_port_is_10g, .is_port_25g = &lan969x_port_is_25g, + .is_port_rgmii = &lan969x_port_is_rgmii, .get_port_dev_index = &lan969x_port_dev_mapping, .get_port_dev_bit = &lan969x_get_dev_mode_bit, .get_hsch_max_group_rate = &lan969x_get_hsch_max_group_rate, @@ -336,6 +340,11 @@ static const struct sparx5_ops lan969x_ops = { .set_port_mux = &lan969x_port_mux_set, .ptp_irq_handler = &lan969x_ptp_irq_handler, .dsm_calendar_calc = &lan969x_dsm_calendar_calc, + .port_config_rgmii = &lan969x_port_config_rgmii, + .fdma_init = &lan969x_fdma_init, + .fdma_deinit = &lan969x_fdma_deinit, + .fdma_poll = &lan969x_fdma_napi_poll, + .fdma_xmit = &lan969x_fdma_xmit, }; const struct sparx5_match_data lan969x_desc = { @@ -346,8 +355,3 @@ const struct sparx5_match_data lan969x_desc = { .consts = &lan969x_consts, .ops = &lan969x_ops, }; -EXPORT_SYMBOL_GPL(lan969x_desc); - -MODULE_DESCRIPTION("Microchip lan969x switch driver"); -MODULE_AUTHOR("Daniel Machon <daniel.machon@microchip.com>"); -MODULE_LICENSE("Dual MIT/GPL"); diff --git a/drivers/net/ethernet/microchip/lan969x/lan969x.h b/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x.h index 2489d0d32dfd..529fde3d4deb 100644 --- a/drivers/net/ethernet/microchip/lan969x/lan969x.h +++ b/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x.h @@ -59,7 +59,24 @@ static inline bool lan969x_port_is_25g(int portno) return false; } +static inline bool lan969x_port_is_rgmii(int portno) +{ + return portno == 28 || portno == 29; +} + /* lan969x_calendar.c */ int lan969x_dsm_calendar_calc(struct sparx5 *sparx5, u32 taxi, struct sparx5_calendar_data *data); + +/* lan969x_rgmii.c */ +int lan969x_port_config_rgmii(struct sparx5_port *port, + struct sparx5_port_config *conf); + +/* lan969x_fdma.c */ +int lan969x_fdma_init(struct sparx5 *sparx5); +int lan969x_fdma_deinit(struct sparx5 *sparx5); +int lan969x_fdma_napi_poll(struct napi_struct *napi, int weight); +int lan969x_fdma_xmit(struct sparx5 *sparx5, u32 *ifh, struct sk_buff *skb, + struct net_device *dev); + #endif diff --git a/drivers/net/ethernet/microchip/lan969x/lan969x_calendar.c b/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_calendar.c index e857640df185..e857640df185 100644 --- a/drivers/net/ethernet/microchip/lan969x/lan969x_calendar.c +++ b/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_calendar.c diff --git a/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_fdma.c b/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_fdma.c new file mode 100644 index 000000000000..1282f5c3ee6d --- /dev/null +++ b/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_fdma.c @@ -0,0 +1,406 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Microchip lan969x Switch driver + * + * Copyright (c) 2025 Microchip Technology Inc. and its subsidiaries. + */ +#include <net/page_pool/helpers.h> + +#include "../sparx5_main.h" +#include "../sparx5_main_regs.h" +#include "../sparx5_port.h" + +#include "fdma_api.h" +#include "lan969x.h" + +#define FDMA_PRIV(fdma) ((struct sparx5 *)((fdma)->priv)) + +static int lan969x_fdma_tx_dataptr_cb(struct fdma *fdma, int dcb, int db, + u64 *dataptr) +{ + *dataptr = FDMA_PRIV(fdma)->tx.dbs[dcb].dma_addr; + + return 0; +} + +static int lan969x_fdma_rx_dataptr_cb(struct fdma *fdma, int dcb, int db, + u64 *dataptr) +{ + struct sparx5_rx *rx = &FDMA_PRIV(fdma)->rx; + struct page *page; + + page = page_pool_dev_alloc_pages(rx->page_pool); + if (unlikely(!page)) + return -ENOMEM; + + rx->page[dcb][db] = page; + + *dataptr = page_pool_get_dma_addr(page); + + return 0; +} + +static int lan969x_fdma_get_next_dcb(struct sparx5_tx *tx) +{ + struct fdma *fdma = &tx->fdma; + + for (int i = 0; i < fdma->n_dcbs; ++i) + if (!tx->dbs[i].used && !fdma_is_last(fdma, &fdma->dcbs[i])) + return i; + + return -ENOSPC; +} + +static void lan969x_fdma_tx_clear_buf(struct sparx5 *sparx5, int weight) +{ + struct fdma *fdma = &sparx5->tx.fdma; + struct sparx5_tx_buf *db; + unsigned long flags; + int i; + + spin_lock_irqsave(&sparx5->tx_lock, flags); + + for (i = 0; i < fdma->n_dcbs; ++i) { + db = &sparx5->tx.dbs[i]; + + if (!db->used) + continue; + + if (!fdma_db_is_done(fdma_db_get(fdma, i, 0))) + continue; + + db->dev->stats.tx_bytes += db->skb->len; + db->dev->stats.tx_packets++; + sparx5->tx.packets++; + + dma_unmap_single(sparx5->dev, + db->dma_addr, + db->skb->len, + DMA_TO_DEVICE); + + if (!db->ptp) + napi_consume_skb(db->skb, weight); + + db->used = false; + } + + spin_unlock_irqrestore(&sparx5->tx_lock, flags); +} + +static void lan969x_fdma_free_pages(struct sparx5_rx *rx) +{ + struct fdma *fdma = &rx->fdma; + + for (int i = 0; i < fdma->n_dcbs; ++i) { + for (int j = 0; j < fdma->n_dbs; ++j) + page_pool_put_full_page(rx->page_pool, + rx->page[i][j], false); + } +} + +static struct sk_buff *lan969x_fdma_rx_get_frame(struct sparx5 *sparx5, + struct sparx5_rx *rx) +{ + const struct sparx5_consts *consts = sparx5->data->consts; + struct fdma *fdma = &rx->fdma; + struct sparx5_port *port; + struct frame_info fi; + struct sk_buff *skb; + struct fdma_db *db; + struct page *page; + + db = &fdma->dcbs[fdma->dcb_index].db[fdma->db_index]; + page = rx->page[fdma->dcb_index][fdma->db_index]; + + sparx5_ifh_parse(sparx5, page_address(page), &fi); + port = fi.src_port < consts->n_ports ? sparx5->ports[fi.src_port] : + NULL; + if (WARN_ON(!port)) + goto free_page; + + skb = build_skb(page_address(page), fdma->db_size); + if (unlikely(!skb)) + goto free_page; + + skb_mark_for_recycle(skb); + skb_put(skb, fdma_db_len_get(db)); + skb_pull(skb, IFH_LEN * sizeof(u32)); + + skb->dev = port->ndev; + + if (likely(!(skb->dev->features & NETIF_F_RXFCS))) + skb_trim(skb, skb->len - ETH_FCS_LEN); + + sparx5_ptp_rxtstamp(sparx5, skb, fi.timestamp); + skb->protocol = eth_type_trans(skb, skb->dev); + + if (test_bit(port->portno, sparx5->bridge_mask)) + skb->offload_fwd_mark = 1; + + skb->dev->stats.rx_bytes += skb->len; + skb->dev->stats.rx_packets++; + + return skb; + +free_page: + page_pool_recycle_direct(rx->page_pool, page); + + return NULL; +} + +static int lan969x_fdma_rx_alloc(struct sparx5 *sparx5) +{ + struct sparx5_rx *rx = &sparx5->rx; + struct fdma *fdma = &rx->fdma; + int err; + + struct page_pool_params pp_params = { + .order = 0, + .flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV, + .pool_size = fdma->n_dcbs * fdma->n_dbs, + .nid = NUMA_NO_NODE, + .dev = sparx5->dev, + .dma_dir = DMA_FROM_DEVICE, + .offset = 0, + .max_len = fdma->db_size - + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)), + }; + + rx->page_pool = page_pool_create(&pp_params); + if (IS_ERR(rx->page_pool)) + return PTR_ERR(rx->page_pool); + + err = fdma_alloc_coherent(sparx5->dev, fdma); + if (err) + return err; + + fdma_dcbs_init(fdma, + FDMA_DCB_INFO_DATAL(fdma->db_size), + FDMA_DCB_STATUS_INTR); + + return 0; +} + +static int lan969x_fdma_tx_alloc(struct sparx5 *sparx5) +{ + struct sparx5_tx *tx = &sparx5->tx; + struct fdma *fdma = &tx->fdma; + int err; + + tx->dbs = kcalloc(fdma->n_dcbs, + sizeof(struct sparx5_tx_buf), + GFP_KERNEL); + if (!tx->dbs) + return -ENOMEM; + + err = fdma_alloc_coherent(sparx5->dev, fdma); + if (err) { + kfree(tx->dbs); + return err; + } + + fdma_dcbs_init(fdma, + FDMA_DCB_INFO_DATAL(fdma->db_size), + FDMA_DCB_STATUS_DONE); + + return 0; +} + +static void lan969x_fdma_rx_init(struct sparx5 *sparx5) +{ + struct fdma *fdma = &sparx5->rx.fdma; + + fdma->channel_id = FDMA_XTR_CHANNEL; + fdma->n_dcbs = FDMA_DCB_MAX; + fdma->n_dbs = 1; + fdma->priv = sparx5; + fdma->size = fdma_get_size(fdma); + fdma->db_size = PAGE_SIZE; + fdma->ops.dataptr_cb = &lan969x_fdma_rx_dataptr_cb; + fdma->ops.nextptr_cb = &fdma_nextptr_cb; + + /* Fetch a netdev for SKB and NAPI use, any will do */ + for (int idx = 0; idx < sparx5->data->consts->n_ports; ++idx) { + struct sparx5_port *port = sparx5->ports[idx]; + + if (port && port->ndev) { + sparx5->rx.ndev = port->ndev; + break; + } + } +} + +static void lan969x_fdma_tx_init(struct sparx5 *sparx5) +{ + struct fdma *fdma = &sparx5->tx.fdma; + + fdma->channel_id = FDMA_INJ_CHANNEL; + fdma->n_dcbs = FDMA_DCB_MAX; + fdma->n_dbs = 1; + fdma->priv = sparx5; + fdma->size = fdma_get_size(fdma); + fdma->db_size = PAGE_SIZE; + fdma->ops.dataptr_cb = &lan969x_fdma_tx_dataptr_cb; + fdma->ops.nextptr_cb = &fdma_nextptr_cb; +} + +int lan969x_fdma_napi_poll(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); + int old_dcb, dcb_reload, counter = 0; + struct fdma *fdma = &rx->fdma; + struct sk_buff *skb; + + dcb_reload = fdma->dcb_index; + + lan969x_fdma_tx_clear_buf(sparx5, weight); + + /* Process RX data */ + while (counter < weight) { + if (!fdma_has_frames(fdma)) + break; + + skb = lan969x_fdma_rx_get_frame(sparx5, rx); + if (!skb) + break; + + napi_gro_receive(&rx->napi, skb); + + fdma_db_advance(fdma); + counter++; + /* Check if the DCB can be reused */ + if (fdma_dcb_is_reusable(fdma)) + continue; + + fdma_db_reset(fdma); + fdma_dcb_advance(fdma); + } + + /* Allocate new pages and map them */ + while (dcb_reload != fdma->dcb_index) { + old_dcb = dcb_reload; + dcb_reload++; + /* n_dcbs must be a power of 2 */ + dcb_reload &= fdma->n_dcbs - 1; + + fdma_dcb_add(fdma, + old_dcb, + FDMA_DCB_INFO_DATAL(fdma->db_size), + FDMA_DCB_STATUS_INTR); + + sparx5_fdma_reload(sparx5, fdma); + } + + if (counter < weight && napi_complete_done(napi, counter)) + spx5_wr(0xff, sparx5, FDMA_INTR_DB_ENA); + + return counter; +} + +int lan969x_fdma_xmit(struct sparx5 *sparx5, u32 *ifh, struct sk_buff *skb, + struct net_device *dev) +{ + int next_dcb, needed_headroom, needed_tailroom, err; + struct sparx5_tx *tx = &sparx5->tx; + struct fdma *fdma = &tx->fdma; + struct sparx5_tx_buf *db_buf; + u64 status; + + next_dcb = lan969x_fdma_get_next_dcb(tx); + if (next_dcb < 0) + return -EBUSY; + + needed_headroom = max_t(int, IFH_LEN * 4 - skb_headroom(skb), 0); + needed_tailroom = max_t(int, ETH_FCS_LEN - skb_tailroom(skb), 0); + if (needed_headroom || needed_tailroom || skb_header_cloned(skb)) { + err = pskb_expand_head(skb, needed_headroom, needed_tailroom, + GFP_ATOMIC); + if (unlikely(err)) + return err; + } + + skb_push(skb, IFH_LEN * 4); + memcpy(skb->data, ifh, IFH_LEN * 4); + skb_put(skb, ETH_FCS_LEN); + + db_buf = &tx->dbs[next_dcb]; + db_buf->dma_addr = dma_map_single(sparx5->dev, + skb->data, + skb->len, + DMA_TO_DEVICE); + if (dma_mapping_error(sparx5->dev, db_buf->dma_addr)) + return -ENOMEM; + + db_buf->dev = dev; + db_buf->skb = skb; + db_buf->ptp = false; + db_buf->used = true; + + if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && + SPARX5_SKB_CB(skb)->rew_op == IFH_REW_OP_TWO_STEP_PTP) + db_buf->ptp = true; + + status = FDMA_DCB_STATUS_SOF | + FDMA_DCB_STATUS_EOF | + FDMA_DCB_STATUS_BLOCKO(0) | + FDMA_DCB_STATUS_BLOCKL(skb->len) | + FDMA_DCB_STATUS_INTR; + + fdma_dcb_advance(fdma); + fdma_dcb_add(fdma, next_dcb, 0, status); + + sparx5_fdma_reload(sparx5, fdma); + + return NETDEV_TX_OK; +} + +int lan969x_fdma_init(struct sparx5 *sparx5) +{ + struct sparx5_rx *rx = &sparx5->rx; + int err; + + lan969x_fdma_rx_init(sparx5); + lan969x_fdma_tx_init(sparx5); + sparx5_fdma_injection_mode(sparx5); + + err = dma_set_mask_and_coherent(sparx5->dev, DMA_BIT_MASK(64)); + if (err) { + dev_err(sparx5->dev, "Failed to set 64-bit FDMA mask"); + return err; + } + + err = lan969x_fdma_rx_alloc(sparx5); + if (err) { + dev_err(sparx5->dev, "Failed to allocate RX buffers: %d\n", + err); + return err; + } + + err = lan969x_fdma_tx_alloc(sparx5); + if (err) { + fdma_free_coherent(sparx5->dev, &rx->fdma); + dev_err(sparx5->dev, "Failed to allocate TX buffers: %d\n", + err); + return err; + } + + /* Reset FDMA state */ + spx5_wr(FDMA_CTRL_NRESET_SET(0), sparx5, FDMA_CTRL); + spx5_wr(FDMA_CTRL_NRESET_SET(1), sparx5, FDMA_CTRL); + + return err; +} + +int lan969x_fdma_deinit(struct sparx5 *sparx5) +{ + struct sparx5_rx *rx = &sparx5->rx; + struct sparx5_tx *tx = &sparx5->tx; + + sparx5_fdma_stop(sparx5); + fdma_free_coherent(sparx5->dev, &tx->fdma); + fdma_free_coherent(sparx5->dev, &rx->fdma); + lan969x_fdma_free_pages(rx); + page_pool_destroy(rx->page_pool); + + return 0; +} diff --git a/drivers/net/ethernet/microchip/lan969x/lan969x_regs.c b/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_regs.c index ace4ba21eec4..ace4ba21eec4 100644 --- a/drivers/net/ethernet/microchip/lan969x/lan969x_regs.c +++ b/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_regs.c diff --git a/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_rgmii.c b/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_rgmii.c new file mode 100644 index 000000000000..4e422ca50828 --- /dev/null +++ b/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_rgmii.c @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Microchip lan969x Switch driver + * + * Copyright (c) 2024 Microchip Technology Inc. and its subsidiaries. + */ + +#include "lan969x.h" + +/* Tx clock selectors */ +#define LAN969X_RGMII_TX_CLK_SEL_125MHZ 1 /* 1000Mbps */ +#define LAN969X_RGMII_TX_CLK_SEL_25MHZ 2 /* 100Mbps */ +#define LAN969X_RGMII_TX_CLK_SEL_2M5MHZ 3 /* 10Mbps */ + +/* Port speed selectors */ +#define LAN969X_RGMII_SPEED_SEL_10 0 /* Select 10Mbps speed */ +#define LAN969X_RGMII_SPEED_SEL_100 1 /* Select 100Mbps speed */ +#define LAN969X_RGMII_SPEED_SEL_1000 2 /* Select 1000Mbps speed */ + +/* Clock delay selectors */ +#define LAN969X_RGMII_CLK_DELAY_SEL_1_0_NS 2 /* Phase shift 45deg */ +#define LAN969X_RGMII_CLK_DELAY_SEL_1_7_NS 3 /* Phase shift 77deg */ +#define LAN969X_RGMII_CLK_DELAY_SEL_2_0_NS 4 /* Phase shift 90deg */ +#define LAN969X_RGMII_CLK_DELAY_SEL_2_5_NS 5 /* Phase shift 112deg */ +#define LAN969X_RGMII_CLK_DELAY_SEL_3_0_NS 6 /* Phase shift 135deg */ +#define LAN969X_RGMII_CLK_DELAY_SEL_3_3_NS 7 /* Phase shift 147deg */ + +#define LAN969X_RGMII_PORT_START_IDX 28 /* Index of the first RGMII port */ +#define LAN969X_RGMII_IFG_TX 4 /* TX Inter Frame Gap value */ +#define LAN969X_RGMII_IFG_RX1 5 /* RX1 Inter Frame Gap value */ +#define LAN969X_RGMII_IFG_RX2 1 /* RX2 Inter Frame Gap value */ + +#define RGMII_PORT_IDX(port) ((port)->portno - LAN969X_RGMII_PORT_START_IDX) + +/* Get the tx clock selector based on the port speed. */ +static int lan969x_rgmii_get_clk_sel(int speed) +{ + return (speed == SPEED_10 ? LAN969X_RGMII_TX_CLK_SEL_2M5MHZ : + speed == SPEED_100 ? LAN969X_RGMII_TX_CLK_SEL_25MHZ : + LAN969X_RGMII_TX_CLK_SEL_125MHZ); +} + +/* Get the port speed selector based on the port speed. */ +static int lan969x_rgmii_get_speed_sel(int speed) +{ + return (speed == SPEED_10 ? LAN969X_RGMII_SPEED_SEL_10 : + speed == SPEED_100 ? LAN969X_RGMII_SPEED_SEL_100 : + LAN969X_RGMII_SPEED_SEL_1000); +} + +/* Get the clock delay selector based on the clock delay in picoseconds. */ +static int lan969x_rgmii_get_clk_delay_sel(struct sparx5_port *port, + u32 delay_ps, u32 *clk_delay_sel) +{ + switch (delay_ps) { + case 0: + /* Hardware default selector. */ + *clk_delay_sel = LAN969X_RGMII_CLK_DELAY_SEL_2_5_NS; + break; + case 1000: + *clk_delay_sel = LAN969X_RGMII_CLK_DELAY_SEL_1_0_NS; + break; + case 1700: + *clk_delay_sel = LAN969X_RGMII_CLK_DELAY_SEL_1_7_NS; + break; + case 2000: + *clk_delay_sel = LAN969X_RGMII_CLK_DELAY_SEL_2_0_NS; + break; + case 2500: + *clk_delay_sel = LAN969X_RGMII_CLK_DELAY_SEL_2_5_NS; + break; + case 3000: + *clk_delay_sel = LAN969X_RGMII_CLK_DELAY_SEL_3_0_NS; + break; + case 3300: + *clk_delay_sel = LAN969X_RGMII_CLK_DELAY_SEL_3_3_NS; + break; + default: + dev_err(port->sparx5->dev, "Invalid RGMII delay: %u", delay_ps); + return -EINVAL; + } + + return 0; +} + +/* Configure the RGMII tx clock frequency. */ +static void lan969x_rgmii_tx_clk_config(struct sparx5_port *port, + struct sparx5_port_config *conf) +{ + u32 clk_sel = lan969x_rgmii_get_clk_sel(conf->speed); + u32 idx = RGMII_PORT_IDX(port); + + /* Take the RGMII clock domain out of reset and set tx clock + * frequency. + */ + spx5_rmw(HSIO_WRAP_RGMII_CFG_TX_CLK_CFG_SET(clk_sel) | + HSIO_WRAP_RGMII_CFG_RGMII_TX_RST_SET(0) | + HSIO_WRAP_RGMII_CFG_RGMII_RX_RST_SET(0), + HSIO_WRAP_RGMII_CFG_TX_CLK_CFG | + HSIO_WRAP_RGMII_CFG_RGMII_TX_RST | + HSIO_WRAP_RGMII_CFG_RGMII_RX_RST, + port->sparx5, HSIO_WRAP_RGMII_CFG(idx)); +} + +/* Configure the RGMII port device. */ +static void lan969x_rgmii_port_device_config(struct sparx5_port *port, + struct sparx5_port_config *conf) +{ + u32 dtag, dotag, etype, speed_sel, idx = RGMII_PORT_IDX(port); + + speed_sel = lan969x_rgmii_get_speed_sel(conf->speed); + + etype = (port->vlan_type == SPX5_VLAN_PORT_TYPE_S_CUSTOM ? + port->custom_etype : + port->vlan_type == SPX5_VLAN_PORT_TYPE_C ? + ETH_P_8021Q : ETH_P_8021AD); + + dtag = port->max_vlan_tags == SPX5_PORT_MAX_TAGS_TWO; + dotag = port->max_vlan_tags != SPX5_PORT_MAX_TAGS_NONE; + + /* Enable the MAC. */ + spx5_wr(DEVRGMII_MAC_ENA_CFG_RX_ENA_SET(1) | + DEVRGMII_MAC_ENA_CFG_TX_ENA_SET(1), + port->sparx5, DEVRGMII_MAC_ENA_CFG(idx)); + + /* Configure the Inter Frame Gap. */ + spx5_wr(DEVRGMII_MAC_IFG_CFG_TX_IFG_SET(LAN969X_RGMII_IFG_TX) | + DEVRGMII_MAC_IFG_CFG_RX_IFG1_SET(LAN969X_RGMII_IFG_RX1) | + DEVRGMII_MAC_IFG_CFG_RX_IFG2_SET(LAN969X_RGMII_IFG_RX2), + port->sparx5, DEVRGMII_MAC_IFG_CFG(idx)); + + /* Configure port data rate. */ + spx5_wr(DEVRGMII_DEV_RST_CTRL_SPEED_SEL_SET(speed_sel), + port->sparx5, DEVRGMII_DEV_RST_CTRL(idx)); + + /* Configure VLAN awareness. */ + spx5_wr(DEVRGMII_MAC_TAGS_CFG_TAG_ID_SET(etype) | + DEVRGMII_MAC_TAGS_CFG_PB_ENA_SET(dtag) | + DEVRGMII_MAC_TAGS_CFG_VLAN_AWR_ENA_SET(dotag) | + DEVRGMII_MAC_TAGS_CFG_VLAN_LEN_AWR_ENA_SET(dotag), + port->sparx5, + DEVRGMII_MAC_TAGS_CFG(idx)); +} + +/* Configure the RGMII delay lines in the MAC. + * + * We use the rx-internal-delay-ps" and "tx-internal-delay-ps" properties to + * configure the rx and tx delays for the MAC. If these properties are missing + * or set to zero, the MAC will not apply any delay. + * + * The PHY side delays are determined by the PHY mode + * (e.g. PHY_INTERFACE_MODE_RGMII_{ID, RXID, TXID}), and ignored by the MAC side + * entirely. + */ +static int lan969x_rgmii_delay_config(struct sparx5_port *port, + struct sparx5_port_config *conf) +{ + u32 tx_clk_sel, rx_clk_sel, tx_delay_ps = 0, rx_delay_ps = 0; + u32 idx = RGMII_PORT_IDX(port); + int err; + + of_property_read_u32(port->of_node, "rx-internal-delay-ps", + &rx_delay_ps); + + of_property_read_u32(port->of_node, "tx-internal-delay-ps", + &tx_delay_ps); + + err = lan969x_rgmii_get_clk_delay_sel(port, rx_delay_ps, &rx_clk_sel); + if (err) + return err; + + err = lan969x_rgmii_get_clk_delay_sel(port, tx_delay_ps, &tx_clk_sel); + if (err) + return err; + + /* Configure rx delay. */ + spx5_rmw(HSIO_WRAP_DLL_CFG_DLL_RST_SET(0) | + HSIO_WRAP_DLL_CFG_DLL_ENA_SET(1) | + HSIO_WRAP_DLL_CFG_DLL_CLK_ENA_SET(!!rx_delay_ps) | + HSIO_WRAP_DLL_CFG_DLL_CLK_SEL_SET(rx_clk_sel), + HSIO_WRAP_DLL_CFG_DLL_RST | + HSIO_WRAP_DLL_CFG_DLL_ENA | + HSIO_WRAP_DLL_CFG_DLL_CLK_ENA | + HSIO_WRAP_DLL_CFG_DLL_CLK_SEL, + port->sparx5, HSIO_WRAP_DLL_CFG(idx, 0)); + + /* Configure tx delay. */ + spx5_rmw(HSIO_WRAP_DLL_CFG_DLL_RST_SET(0) | + HSIO_WRAP_DLL_CFG_DLL_ENA_SET(1) | + HSIO_WRAP_DLL_CFG_DLL_CLK_ENA_SET(!!tx_delay_ps) | + HSIO_WRAP_DLL_CFG_DLL_CLK_SEL_SET(tx_clk_sel), + HSIO_WRAP_DLL_CFG_DLL_RST | + HSIO_WRAP_DLL_CFG_DLL_ENA | + HSIO_WRAP_DLL_CFG_DLL_CLK_ENA | + HSIO_WRAP_DLL_CFG_DLL_CLK_SEL, + port->sparx5, HSIO_WRAP_DLL_CFG(idx, 1)); + + return 0; +} + +/* Configure GPIO's to be used as RGMII interface. */ +static void lan969x_rgmii_gpio_config(struct sparx5_port *port) +{ + u32 idx = RGMII_PORT_IDX(port); + + /* Enable the RGMII on the GPIOs. */ + spx5_wr(HSIO_WRAP_XMII_CFG_GPIO_XMII_CFG_SET(1), port->sparx5, + HSIO_WRAP_XMII_CFG(!idx)); +} + +int lan969x_port_config_rgmii(struct sparx5_port *port, + struct sparx5_port_config *conf) +{ + int err; + + err = lan969x_rgmii_delay_config(port, conf); + if (err) + return err; + + lan969x_rgmii_tx_clk_config(port, conf); + lan969x_rgmii_gpio_config(port); + lan969x_rgmii_port_device_config(port, conf); + + return 0; +} diff --git a/drivers/net/ethernet/microchip/lan969x/lan969x_vcap_ag_api.c b/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_vcap_ag_api.c index 7acc5bcf337a..7acc5bcf337a 100644 --- a/drivers/net/ethernet/microchip/lan969x/lan969x_vcap_ag_api.c +++ b/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_vcap_ag_api.c diff --git a/drivers/net/ethernet/microchip/lan969x/lan969x_vcap_impl.c b/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_vcap_impl.c index 543a1f2bf6bd..543a1f2bf6bd 100644 --- a/drivers/net/ethernet/microchip/lan969x/lan969x_vcap_impl.c +++ b/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_vcap_impl.c diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_calendar.c b/drivers/net/ethernet/microchip/sparx5/sparx5_calendar.c index 5fe941c66c17..5c46d81de530 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_calendar.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_calendar.c @@ -98,7 +98,6 @@ u32 sparx5_cal_speed_to_value(enum sparx5_cal_bw speed) default: return 0; } } -EXPORT_SYMBOL_GPL(sparx5_cal_speed_to_value); static u32 sparx5_bandwidth_to_calendar(u32 bw) { @@ -150,7 +149,6 @@ enum sparx5_cal_bw sparx5_get_port_cal_speed(struct sparx5 *sparx5, u32 portno) return SPX5_CAL_SPEED_NONE; return sparx5_bandwidth_to_calendar(port->conf.bandwidth); } -EXPORT_SYMBOL_GPL(sparx5_get_port_cal_speed); /* Auto configure the QSYS calendar based on port configuration */ int sparx5_config_auto_calendar(struct sparx5 *sparx5) diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c b/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c index 0027144a2af2..dbe86f937b21 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c @@ -18,9 +18,6 @@ #include "sparx5_main.h" #include "sparx5_port.h" -#define FDMA_XTR_CHANNEL 6 -#define FDMA_INJ_CHANNEL 0 - #define FDMA_XTR_BUFFER_SIZE 2048 #define FDMA_WEIGHT 4 @@ -133,7 +130,7 @@ static void sparx5_fdma_tx_deactivate(struct sparx5 *sparx5, struct sparx5_tx *t sparx5, FDMA_CH_ACTIVATE); } -static void sparx5_fdma_reload(struct sparx5 *sparx5, struct fdma *fdma) +void sparx5_fdma_reload(struct sparx5 *sparx5, struct fdma *fdma) { /* Reload the RX channel */ spx5_wr(BIT(fdma->channel_id), sparx5, FDMA_CH_RELOAD); @@ -183,7 +180,7 @@ static bool sparx5_fdma_rx_get_frame(struct sparx5 *sparx5, struct sparx5_rx *rx return true; } -static int sparx5_fdma_napi_callback(struct napi_struct *napi, int weight) +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); @@ -213,11 +210,11 @@ static int sparx5_fdma_napi_callback(struct napi_struct *napi, int weight) return counter; } -int sparx5_fdma_xmit(struct sparx5 *sparx5, u32 *ifh, struct sk_buff *skb) +int sparx5_fdma_xmit(struct sparx5 *sparx5, u32 *ifh, struct sk_buff *skb, + struct net_device *dev) { struct sparx5_tx *tx = &sparx5->tx; struct fdma *fdma = &tx->fdma; - static bool first_time = true; void *virt_addr; fdma_dcb_advance(fdma); @@ -238,12 +235,8 @@ int sparx5_fdma_xmit(struct sparx5 *sparx5, u32 *ifh, struct sk_buff *skb) 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_reload(sparx5, fdma); - } + sparx5_fdma_reload(sparx5, fdma); + return NETDEV_TX_OK; } @@ -260,10 +253,6 @@ static int sparx5_fdma_rx_alloc(struct sparx5 *sparx5) 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); - sparx5_fdma_rx_activate(sparx5, rx); return 0; } @@ -348,7 +337,7 @@ irqreturn_t sparx5_fdma_handler(int irq, void *args) return IRQ_HANDLED; } -static void sparx5_fdma_injection_mode(struct sparx5 *sparx5) +void sparx5_fdma_injection_mode(struct sparx5 *sparx5) { const int byte_swap = 1; int portno; @@ -410,7 +399,7 @@ static void sparx5_fdma_injection_mode(struct sparx5 *sparx5) } } -int sparx5_fdma_start(struct sparx5 *sparx5) +int sparx5_fdma_init(struct sparx5 *sparx5) { int err; @@ -443,24 +432,55 @@ int sparx5_fdma_start(struct sparx5 *sparx5) return err; } +int sparx5_fdma_deinit(struct sparx5 *sparx5) +{ + sparx5_fdma_stop(sparx5); + fdma_free_phys(&sparx5->rx.fdma); + fdma_free_phys(&sparx5->tx.fdma); + + return 0; +} + static u32 sparx5_fdma_port_ctrl(struct sparx5 *sparx5) { return spx5_rd(sparx5, FDMA_PORT_CTRL(0)); } +int sparx5_fdma_start(struct sparx5 *sparx5) +{ + const struct sparx5_ops *ops = sparx5->data->ops; + struct sparx5_rx *rx = &sparx5->rx; + struct sparx5_tx *tx = &sparx5->tx; + + netif_napi_add_weight(rx->ndev, + &rx->napi, + ops->fdma_poll, + FDMA_WEIGHT); + + napi_enable(&rx->napi); + + sparx5_fdma_rx_activate(sparx5, rx); + sparx5_fdma_tx_activate(sparx5, tx); + + return 0; +} + int sparx5_fdma_stop(struct sparx5 *sparx5) { + struct sparx5_rx *rx = &sparx5->rx; + struct sparx5_tx *tx = &sparx5->tx; u32 val; - napi_disable(&sparx5->rx.napi); + napi_disable(&rx->napi); + /* Stop the fdma and channel interrupts */ - sparx5_fdma_rx_deactivate(sparx5, &sparx5->rx); - sparx5_fdma_tx_deactivate(sparx5, &sparx5->tx); + sparx5_fdma_rx_deactivate(sparx5, rx); + sparx5_fdma_tx_deactivate(sparx5, tx); + /* Wait for the RX channel to stop */ 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.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c index 2f1013f870fb..6a0e5b83ecd0 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c @@ -24,7 +24,7 @@ #include <linux/types.h> #include <linux/reset.h> -#include "../lan969x/lan969x.h" /* for lan969x match data */ +#include "lan969x/lan969x.h" /* for lan969x match data */ #include "sparx5_main_regs.h" #include "sparx5_main.h" @@ -313,10 +313,13 @@ static int sparx5_create_port(struct sparx5 *sparx5, struct initial_port_config *config) { struct sparx5_port *spx5_port; + const struct sparx5_ops *ops; struct net_device *ndev; struct phylink *phylink; int err; + ops = sparx5->data->ops; + ndev = sparx5_create_netdev(sparx5, config->portno); if (IS_ERR(ndev)) { dev_err(sparx5->dev, "Could not create net device: %02u\n", @@ -357,6 +360,9 @@ static int sparx5_create_port(struct sparx5 *sparx5, MAC_SYM_PAUSE | MAC_10 | MAC_100 | MAC_1000FD | MAC_2500FD | MAC_5000FD | MAC_10000FD | MAC_25000FD; + if (ops->is_port_rgmii(spx5_port->portno)) + phy_interface_set_rgmii(spx5_port->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_SGMII, spx5_port->phylink_config.supported_interfaces); __set_bit(PHY_INTERFACE_MODE_QSGMII, @@ -778,16 +784,19 @@ static int sparx5_start(struct sparx5 *sparx5) /* Start Frame DMA with fallback to register based INJ/XTR */ err = -ENXIO; - if (sparx5->fdma_irq >= 0 && is_sparx5(sparx5)) { - if (GCB_CHIP_ID_REV_ID_GET(sparx5->chip_id) > 0) - err = devm_request_threaded_irq(sparx5->dev, - sparx5->fdma_irq, - NULL, - sparx5_fdma_handler, - IRQF_ONESHOT, - "sparx5-fdma", sparx5); - if (!err) - err = sparx5_fdma_start(sparx5); + if (sparx5->fdma_irq >= 0) { + if (GCB_CHIP_ID_REV_ID_GET(sparx5->chip_id) > 0 || + !is_sparx5(sparx5)) + err = devm_request_irq(sparx5->dev, + sparx5->fdma_irq, + sparx5_fdma_handler, + 0, + "sparx5-fdma", sparx5); + if (!err) { + err = ops->fdma_init(sparx5); + if (!err) + sparx5_fdma_start(sparx5); + } if (err) sparx5->fdma_irq = -ENXIO; } else { @@ -831,6 +840,7 @@ static int mchp_sparx5_probe(struct platform_device *pdev) struct initial_port_config *configs, *config; struct device_node *np = pdev->dev.of_node; struct device_node *ports, *portnp; + const struct sparx5_ops *ops; struct reset_control *reset; struct sparx5 *sparx5; int idx = 0, err = 0; @@ -852,6 +862,7 @@ static int mchp_sparx5_probe(struct platform_device *pdev) return -EINVAL; regs = sparx5->data->regs; + ops = sparx5->data->ops; /* Do switch core reset if available */ reset = devm_reset_control_get_optional_shared(&pdev->dev, "switch"); @@ -881,7 +892,7 @@ static int mchp_sparx5_probe(struct platform_device *pdev) for_each_available_child_of_node(ports, portnp) { struct sparx5_port_config *conf; - struct phy *serdes; + struct phy *serdes = NULL; u32 portno; err = of_property_read_u32(portnp, "reg", &portno); @@ -911,13 +922,17 @@ static int mchp_sparx5_probe(struct platform_device *pdev) conf->sd_sgpio = ~0; else sparx5->sd_sgpio_remapping = true; - serdes = devm_of_phy_get(sparx5->dev, portnp, NULL); - if (IS_ERR(serdes)) { - err = dev_err_probe(sparx5->dev, PTR_ERR(serdes), - "port %u: missing serdes\n", - portno); - of_node_put(portnp); - goto cleanup_config; + /* There is no SerDes node for RGMII ports. */ + if (!ops->is_port_rgmii(portno)) { + serdes = devm_of_phy_get(sparx5->dev, portnp, NULL); + if (IS_ERR(serdes)) { + err = dev_err_probe(sparx5->dev, + PTR_ERR(serdes), + "port %u: missing serdes\n", + portno); + of_node_put(portnp); + goto cleanup_config; + } } config->portno = portno; config->node = portnp; @@ -1015,6 +1030,7 @@ cleanup_pnode: static void mchp_sparx5_remove(struct platform_device *pdev) { struct sparx5 *sparx5 = platform_get_drvdata(pdev); + const struct sparx5_ops *ops = sparx5->data->ops; debugfs_remove_recursive(sparx5->debugfs_root); if (sparx5->xtr_irq) { @@ -1026,7 +1042,7 @@ static void mchp_sparx5_remove(struct platform_device *pdev) sparx5->fdma_irq = -ENXIO; } sparx5_ptp_deinit(sparx5); - sparx5_fdma_stop(sparx5); + ops->fdma_deinit(sparx5); sparx5_cleanup_ports(sparx5); sparx5_vcap_destroy(sparx5); /* Unregister netdevs */ @@ -1073,6 +1089,7 @@ static const struct sparx5_ops sparx5_ops = { .is_port_5g = &sparx5_port_is_5g, .is_port_10g = &sparx5_port_is_10g, .is_port_25g = &sparx5_port_is_25g, + .is_port_rgmii = &sparx5_port_is_rgmii, .get_port_dev_index = &sparx5_port_dev_mapping, .get_port_dev_bit = &sparx5_port_dev_mapping, .get_hsch_max_group_rate = &sparx5_get_hsch_max_group_rate, @@ -1080,6 +1097,10 @@ static const struct sparx5_ops sparx5_ops = { .set_port_mux = &sparx5_port_mux_set, .ptp_irq_handler = &sparx5_ptp_irq_handler, .dsm_calendar_calc = &sparx5_dsm_calendar_calc, + .fdma_init = &sparx5_fdma_init, + .fdma_deinit = &sparx5_fdma_deinit, + .fdma_poll = &sparx5_fdma_napi_callback, + .fdma_xmit = &sparx5_fdma_xmit, }; static const struct sparx5_match_data sparx5_desc = { @@ -1093,7 +1114,7 @@ static const struct sparx5_match_data sparx5_desc = { static const struct of_device_id mchp_sparx5_match[] = { { .compatible = "microchip,sparx5-switch", .data = &sparx5_desc }, -#if IS_ENABLED(CONFIG_LAN969X_SWITCH) +#ifdef CONFIG_LAN969X_SWITCH { .compatible = "microchip,lan9691-switch", .data = &lan969x_desc }, #endif { } diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h index d5dd953b0a71..fe7d8bcc0cd9 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h @@ -112,6 +112,8 @@ enum sparx5_feature { #define XTR_QUEUE 0 #define INJ_QUEUE 0 +#define FDMA_XTR_CHANNEL 6 +#define FDMA_INJ_CHANNEL 0 #define FDMA_DCB_MAX 64 #define FDMA_RX_DCB_MAX_DBS 15 #define FDMA_TX_DCB_MAX_DBS 1 @@ -157,11 +159,25 @@ struct sparx5_calendar_data { */ struct sparx5_rx { struct fdma fdma; - struct sk_buff *skb[FDMA_DCB_MAX][FDMA_RX_DCB_MAX_DBS]; + struct page_pool *page_pool; + union { + struct sk_buff *skb[FDMA_DCB_MAX][FDMA_RX_DCB_MAX_DBS]; + struct page *page[FDMA_DCB_MAX][FDMA_RX_DCB_MAX_DBS]; + }; dma_addr_t dma; struct napi_struct napi; struct net_device *ndev; u64 packets; + u8 page_order; +}; + +/* Used to store information about TX buffers. */ +struct sparx5_tx_buf { + struct net_device *dev; + struct sk_buff *skb; + dma_addr_t dma_addr; + bool used; + bool ptp; }; /* Frame DMA transmit state: @@ -169,6 +185,7 @@ struct sparx5_rx { */ struct sparx5_tx { struct fdma fdma; + struct sparx5_tx_buf *dbs; u64 packets; u64 dropped; }; @@ -313,6 +330,7 @@ struct sparx5_ops { bool (*is_port_5g)(int portno); bool (*is_port_10g)(int portno); bool (*is_port_25g)(int portno); + bool (*is_port_rgmii)(int portno); u32 (*get_port_dev_index)(struct sparx5 *sparx5, int port); u32 (*get_port_dev_bit)(struct sparx5 *sparx5, int port); u32 (*get_hsch_max_group_rate)(int grp); @@ -323,6 +341,13 @@ struct sparx5_ops { irqreturn_t (*ptp_irq_handler)(int irq, void *args); int (*dsm_calendar_calc)(struct sparx5 *sparx5, u32 taxi, struct sparx5_calendar_data *data); + int (*port_config_rgmii)(struct sparx5_port *port, + struct sparx5_port_config *conf); + int (*fdma_init)(struct sparx5 *sparx5); + int (*fdma_deinit)(struct sparx5 *sparx5); + int (*fdma_poll)(struct napi_struct *napi, int weight); + int (*fdma_xmit)(struct sparx5 *sparx5, u32 *ifh, struct sk_buff *skb, + struct net_device *dev); }; struct sparx5_main_io_resource { @@ -433,10 +458,16 @@ int sparx5_manual_injection_mode(struct sparx5 *sparx5); void sparx5_port_inj_timer_setup(struct sparx5_port *port); /* sparx5_fdma.c */ +int sparx5_fdma_init(struct sparx5 *sparx5); +int sparx5_fdma_deinit(struct sparx5 *sparx5); int sparx5_fdma_start(struct sparx5 *sparx5); int sparx5_fdma_stop(struct sparx5 *sparx5); -int sparx5_fdma_xmit(struct sparx5 *sparx5, u32 *ifh, struct sk_buff *skb); +int sparx5_fdma_napi_callback(struct napi_struct *napi, int weight); +int sparx5_fdma_xmit(struct sparx5 *sparx5, u32 *ifh, struct sk_buff *skb, + struct net_device *dev); irqreturn_t sparx5_fdma_handler(int irq, void *args); +void sparx5_fdma_reload(struct sparx5 *sparx5, struct fdma *fdma); +void sparx5_fdma_injection_mode(struct sparx5 *sparx5); /* sparx5_mactable.c */ void sparx5_mact_pull_work(struct work_struct *work); diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h index 561344f19062..d9ef4ef137b8 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h @@ -37,6 +37,7 @@ enum sparx5_target { TARGET_FDMA = 117, TARGET_GCB = 118, TARGET_HSCH = 119, + TARGET_HSIO_WRAP = 120, TARGET_LRN = 122, TARGET_PCEP = 129, TARGET_PCS10G_BR = 132, @@ -54,6 +55,7 @@ enum sparx5_target { TARGET_VCAP_SUPER = 326, TARGET_VOP = 327, TARGET_XQS = 331, + TARGET_DEVRGMII = 392, NUM_TARGETS = 517 }; @@ -5367,6 +5369,69 @@ extern const struct sparx5_regs *regs; #define HSCH_TAS_STATEMACHINE_CFG_REVISIT_DLY_GET(x)\ FIELD_GET(HSCH_TAS_STATEMACHINE_CFG_REVISIT_DLY, x) +/* LAN969X ONLY */ +/* HSIOWRAP:XMII_CFG:XMII_CFG */ +#define HSIO_WRAP_XMII_CFG(g) \ + __REG(TARGET_HSIO_WRAP, 0, 1, 116, g, 2, 20, 0, 0, 1, 4) + +#define HSIO_WRAP_XMII_CFG_GPIO_XMII_CFG GENMASK(2, 1) +#define HSIO_WRAP_XMII_CFG_GPIO_XMII_CFG_SET(x)\ + FIELD_PREP(HSIO_WRAP_XMII_CFG_GPIO_XMII_CFG, x) +#define HSIO_WRAP_XMII_CFG_GPIO_XMII_CFG_GET(x)\ + FIELD_GET(HSIO_WRAP_XMII_CFG_GPIO_XMII_CFG, x) + +/* LAN969X ONLY */ +/* HSIOWRAP:XMII_CFG:RGMII_CFG */ +#define HSIO_WRAP_RGMII_CFG(g) \ + __REG(TARGET_HSIO_WRAP, 0, 1, 116, g, 2, 20, 4, 0, 1, 4) + +#define HSIO_WRAP_RGMII_CFG_TX_CLK_CFG GENMASK(4, 2) +#define HSIO_WRAP_RGMII_CFG_TX_CLK_CFG_SET(x)\ + FIELD_PREP(HSIO_WRAP_RGMII_CFG_TX_CLK_CFG, x) +#define HSIO_WRAP_RGMII_CFG_TX_CLK_CFG_GET(x)\ + FIELD_GET(HSIO_WRAP_RGMII_CFG_TX_CLK_CFG, x) + +#define HSIO_WRAP_RGMII_CFG_RGMII_TX_RST BIT(1) +#define HSIO_WRAP_RGMII_CFG_RGMII_TX_RST_SET(x)\ + FIELD_PREP(HSIO_WRAP_RGMII_CFG_RGMII_TX_RST, x) +#define HSIO_WRAP_RGMII_CFG_RGMII_TX_RST_GET(x)\ + FIELD_GET(HSIO_WRAP_RGMII_CFG_RGMII_TX_RST, x) + +#define HSIO_WRAP_RGMII_CFG_RGMII_RX_RST BIT(0) +#define HSIO_WRAP_RGMII_CFG_RGMII_RX_RST_SET(x)\ + FIELD_PREP(HSIO_WRAP_RGMII_CFG_RGMII_RX_RST, x) +#define HSIO_WRAP_RGMII_CFG_RGMII_RX_RST_GET(x)\ + FIELD_GET(HSIO_WRAP_RGMII_CFG_RGMII_RX_RST, x) + +/* LAN969X ONLY */ +/* HSIOWRAP:XMII_CFG:DLL_CFG */ +#define HSIO_WRAP_DLL_CFG(g, r) \ + __REG(TARGET_HSIO_WRAP, 0, 1, 116, g, 2, 20, 12, r, 2, 4) + +#define HSIO_WRAP_DLL_CFG_DLL_ENA BIT(19) +#define HSIO_WRAP_DLL_CFG_DLL_ENA_SET(x)\ + FIELD_PREP(HSIO_WRAP_DLL_CFG_DLL_ENA, x) +#define HSIO_WRAP_DLL_CFG_DLL_ENA_GET(x)\ + FIELD_GET(HSIO_WRAP_DLL_CFG_DLL_ENA, x) + +#define HSIO_WRAP_DLL_CFG_DLL_CLK_ENA BIT(18) +#define HSIO_WRAP_DLL_CFG_DLL_CLK_ENA_SET(x)\ + FIELD_PREP(HSIO_WRAP_DLL_CFG_DLL_CLK_ENA, x) +#define HSIO_WRAP_DLL_CFG_DLL_CLK_ENA_GET(x)\ + FIELD_GET(HSIO_WRAP_DLL_CFG_DLL_CLK_ENA, x) + +#define HSIO_WRAP_DLL_CFG_DLL_CLK_SEL GENMASK(17, 15) +#define HSIO_WRAP_DLL_CFG_DLL_CLK_SEL_SET(x)\ + FIELD_PREP(HSIO_WRAP_DLL_CFG_DLL_CLK_SEL, x) +#define HSIO_WRAP_DLL_CFG_DLL_CLK_SEL_GET(x)\ + FIELD_GET(HSIO_WRAP_DLL_CFG_DLL_CLK_SEL, x) + +#define HSIO_WRAP_DLL_CFG_DLL_RST BIT(0) +#define HSIO_WRAP_DLL_CFG_DLL_RST_SET(x)\ + FIELD_PREP(HSIO_WRAP_DLL_CFG_DLL_RST, x) +#define HSIO_WRAP_DLL_CFG_DLL_RST_GET(x)\ + FIELD_GET(HSIO_WRAP_DLL_CFG_DLL_RST, x) + /* LRN:COMMON:COMMON_ACCESS_CTRL */ #define LRN_COMMON_ACCESS_CTRL \ __REG(TARGET_LRN, 0, 1, 0, 0, 1, 72, 0, 0, 1, 4) @@ -8110,4 +8175,84 @@ extern const struct sparx5_regs *regs; #define XQS_CNT(g) \ __REG(TARGET_XQS, 0, 1, 0, g, 1024, 4, 0, 0, 1, 4) +/* LAN969X ONLY */ +/* DEV1G:DEV_CFG_STATUS:DEV_RST_CTRL */ +#define DEVRGMII_DEV_RST_CTRL(t) \ + __REG(TARGET_DEVRGMII, t, 2, 0, 0, 1, 36, 0, 0, 1, 4) + +#define DEVRGMII_DEV_RST_CTRL_SPEED_SEL GENMASK(22, 20) +#define DEVRGMII_DEV_RST_CTRL_SPEED_SEL_SET(x)\ + FIELD_PREP(DEVRGMII_DEV_RST_CTRL_SPEED_SEL, x) +#define DEVRGMII_DEV_RST_CTRL_SPEED_SEL_GET(x)\ + FIELD_GET(DEVRGMII_DEV_RST_CTRL_SPEED_SEL, x) + +/* LAN969X ONLY */ +/* DEV1G:MAC_CFG_STATUS:MAC_ENA_CFG */ +#define DEVRGMII_MAC_ENA_CFG(t) \ + __REG(TARGET_DEVRGMII, t, 2, 36, 0, 1, 36, 0, 0, 1, 4) + +#define DEVRGMII_MAC_ENA_CFG_RX_ENA BIT(4) +#define DEVRGMII_MAC_ENA_CFG_RX_ENA_SET(x)\ + FIELD_PREP(DEVRGMII_MAC_ENA_CFG_RX_ENA, x) +#define DEVRGMII_MAC_ENA_CFG_RX_ENA_GET(x)\ + FIELD_GET(DEVRGMII_MAC_ENA_CFG_RX_ENA, x) + +#define DEVRGMII_MAC_ENA_CFG_TX_ENA BIT(0) +#define DEVRGMII_MAC_ENA_CFG_TX_ENA_SET(x)\ + FIELD_PREP(DEVRGMII_MAC_ENA_CFG_TX_ENA, x) +#define DEVRGMII_MAC_ENA_CFG_TX_ENA_GET(x)\ + FIELD_GET(DEVRGMII_MAC_ENA_CFG_TX_ENA, x) + +/* LAN969X ONLY */ +/* DEV1G:MAC_CFG_STATUS:MAC_TAGS_CFG */ +#define DEVRGMII_MAC_TAGS_CFG(t) \ + __REG(TARGET_DEVRGMII, t, 2, 36, 0, 1, 36, 12, 0, 1, 4) + +#define DEVRGMII_MAC_TAGS_CFG_TAG_ID GENMASK(31, 16) +#define DEVRGMII_MAC_TAGS_CFG_TAG_ID_SET(x)\ + FIELD_PREP(DEVRGMII_MAC_TAGS_CFG_TAG_ID, x) +#define DEVRGMII_MAC_TAGS_CFG_TAG_ID_GET(x)\ + FIELD_GET(DEVRGMII_MAC_TAGS_CFG_TAG_ID, x) + +#define DEVRGMII_MAC_TAGS_CFG_VLAN_LEN_AWR_ENA BIT(3) +#define DEVRGMII_MAC_TAGS_CFG_VLAN_LEN_AWR_ENA_SET(x)\ + FIELD_PREP(DEVRGMII_MAC_TAGS_CFG_VLAN_LEN_AWR_ENA, x) +#define DEVRGMII_MAC_TAGS_CFG_VLAN_LEN_AWR_ENA_GET(x)\ + FIELD_GET(DEVRGMII_MAC_TAGS_CFG_VLAN_LEN_AWR_ENA, x) + +#define DEVRGMII_MAC_TAGS_CFG_PB_ENA GENMASK(2, 1) +#define DEVRGMII_MAC_TAGS_CFG_PB_ENA_SET(x)\ + FIELD_PREP(DEVRGMII_MAC_TAGS_CFG_PB_ENA, x) +#define DEVRGMII_MAC_TAGS_CFG_PB_ENA_GET(x)\ + FIELD_GET(DEVRGMII_MAC_TAGS_CFG_PB_ENA, x) + +#define DEVRGMII_MAC_TAGS_CFG_VLAN_AWR_ENA BIT(0) +#define DEVRGMII_MAC_TAGS_CFG_VLAN_AWR_ENA_SET(x)\ + FIELD_PREP(DEVRGMII_MAC_TAGS_CFG_VLAN_AWR_ENA, x) +#define DEVRGMII_MAC_TAGS_CFG_VLAN_AWR_ENA_GET(x)\ + FIELD_GET(DEVRGMII_MAC_TAGS_CFG_VLAN_AWR_ENA, x) + +/* LAN969X ONLY */ +/* DEV1G:MAC_CFG_STATUS:MAC_IFG_CFG */ +#define DEVRGMII_MAC_IFG_CFG(t) \ + __REG(TARGET_DEVRGMII, t, 2, 36, 0, 1, 36, 24, 0, 1, 4) + +#define DEVRGMII_MAC_IFG_CFG_TX_IFG GENMASK(12, 8) +#define DEVRGMII_MAC_IFG_CFG_TX_IFG_SET(x)\ + FIELD_PREP(DEVRGMII_MAC_IFG_CFG_TX_IFG, x) +#define DEVRGMII_MAC_IFG_CFG_TX_IFG_GET(x)\ + FIELD_GET(DEVRGMII_MAC_IFG_CFG_TX_IFG, x) + +#define DEVRGMII_MAC_IFG_CFG_RX_IFG2 GENMASK(7, 4) +#define DEVRGMII_MAC_IFG_CFG_RX_IFG2_SET(x)\ + FIELD_PREP(DEVRGMII_MAC_IFG_CFG_RX_IFG2, x) +#define DEVRGMII_MAC_IFG_CFG_RX_IFG2_GET(x)\ + FIELD_GET(DEVRGMII_MAC_IFG_CFG_RX_IFG2, x) + +#define DEVRGMII_MAC_IFG_CFG_RX_IFG1 GENMASK(3, 0) +#define DEVRGMII_MAC_IFG_CFG_RX_IFG1_SET(x)\ + FIELD_PREP(DEVRGMII_MAC_IFG_CFG_RX_IFG1, x) +#define DEVRGMII_MAC_IFG_CFG_RX_IFG1_GET(x)\ + FIELD_GET(DEVRGMII_MAC_IFG_CFG_RX_IFG1, x) + #endif /* _SPARX5_MAIN_REGS_H_ */ diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_mirror.c b/drivers/net/ethernet/microchip/sparx5/sparx5_mirror.c index 9806729e9c62..76097761fa97 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_mirror.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_mirror.c @@ -12,7 +12,6 @@ #define SPX5_MIRROR_DISABLED 0 #define SPX5_MIRROR_EGRESS 1 #define SPX5_MIRROR_INGRESS 2 -#define SPX5_MIRROR_MONITOR_PORT_DEFAULT 65 #define SPX5_QFWD_MP_OFFSET 9 /* Mirror port offset in the QFWD register */ /* Convert from bool ingress/egress to mirror direction */ @@ -200,7 +199,7 @@ void sparx5_mirror_del(struct sparx5_mall_entry *entry) sparx5_mirror_monitor_set(sparx5, mirror_idx, - SPX5_MIRROR_MONITOR_PORT_DEFAULT); + sparx5->data->consts->n_ports); } void sparx5_mirror_stats(struct sparx5_mall_entry *entry, diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c b/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c index b6f635d85820..138ac58fae51 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c @@ -232,9 +232,12 @@ netdev_tx_t sparx5_port_xmit_impl(struct sk_buff *skb, struct net_device *dev) struct net_device_stats *stats = &dev->stats; struct sparx5_port *port = netdev_priv(dev); struct sparx5 *sparx5 = port->sparx5; + const struct sparx5_ops *ops; u32 ifh[IFH_LEN]; netdev_tx_t ret; + ops = sparx5->data->ops; + memset(ifh, 0, IFH_LEN * 4); sparx5_set_port_ifh(sparx5, ifh, port->portno); @@ -254,7 +257,7 @@ netdev_tx_t sparx5_port_xmit_impl(struct sk_buff *skb, struct net_device *dev) skb_tx_timestamp(skb); spin_lock(&sparx5->tx_lock); if (sparx5->fdma_irq > 0) - ret = sparx5_fdma_xmit(sparx5, ifh, skb); + ret = ops->fdma_xmit(sparx5, ifh, skb, dev); else ret = sparx5_inject(sparx5, ifh, skb, dev); spin_unlock(&sparx5->tx_lock); @@ -264,6 +267,12 @@ netdev_tx_t sparx5_port_xmit_impl(struct sk_buff *skb, struct net_device *dev) if (ret < 0) goto drop; + if (!is_sparx5(sparx5)) + /* When lan969x and TX_OK, stats and SKB consumption is handled + * in the TX completion loop, so dont go any further. + */ + return NETDEV_TX_OK; + stats->tx_bytes += skb->len; stats->tx_packets++; sparx5->tx.packets++; diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c b/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c index f8562c1a894d..cfb4b2e17ace 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c @@ -32,7 +32,19 @@ sparx5_phylink_mac_select_pcs(struct phylink_config *config, { struct sparx5_port *port = netdev_priv(to_net_dev(config->dev)); - return &port->phylink_pcs; + /* Return the PCS for all the modes that require it. */ + switch (interface) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_QSGMII: + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_2500BASEX: + case PHY_INTERFACE_MODE_5GBASER: + case PHY_INTERFACE_MODE_10GBASER: + case PHY_INTERFACE_MODE_25GBASER: + return &port->phylink_pcs; + default: + return NULL; + } } static void sparx5_phylink_mac_config(struct phylink_config *config, @@ -77,7 +89,7 @@ static struct sparx5_port *sparx5_pcs_to_port(struct phylink_pcs *pcs) return container_of(pcs, struct sparx5_port, phylink_pcs); } -static void sparx5_pcs_get_state(struct phylink_pcs *pcs, +static void sparx5_pcs_get_state(struct phylink_pcs *pcs, unsigned int neg_mode, struct phylink_link_state *state) { struct sparx5_port *port = sparx5_pcs_to_port(pcs); diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_port.c b/drivers/net/ethernet/microchip/sparx5/sparx5_port.c index 1401761c6251..04bc8fffaf96 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_port.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_port.c @@ -257,6 +257,15 @@ static int sparx5_port_verify_speed(struct sparx5 *sparx5, conf->speed != SPEED_25000)) return sparx5_port_error(port, conf, SPX5_PERR_SPEED); break; + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_TXID: + case PHY_INTERFACE_MODE_RGMII_RXID: + if (conf->speed != SPEED_1000 && + conf->speed != SPEED_100 && + conf->speed != SPEED_10) + return sparx5_port_error(port, conf, SPX5_PERR_SPEED); + break; default: return sparx5_port_error(port, conf, SPX5_PERR_IFTYPE); } @@ -994,6 +1003,7 @@ int sparx5_port_config(struct sparx5 *sparx5, struct sparx5_port *port, struct sparx5_port_config *conf) { + bool rgmii = phy_interface_mode_is_rgmii(conf->phy_mode); bool high_speed_dev = sparx5_is_baser(conf->portmode); const struct sparx5_ops *ops = sparx5->data->ops; int err, urgency, stop_wm; @@ -1002,8 +1012,14 @@ int sparx5_port_config(struct sparx5 *sparx5, if (err) return err; + if (rgmii) { + err = ops->port_config_rgmii(port, conf); + if (err) + return err; + } + /* high speed device is already configured */ - if (!high_speed_dev) + if (!rgmii && !high_speed_dev) sparx5_port_config_low_set(sparx5, port, conf); /* Configure flow control */ @@ -1067,24 +1083,6 @@ int sparx5_port_init(struct sparx5 *sparx5, if (err) return err; - /* Configure MAC vlan awareness */ - err = sparx5_port_max_tags_set(sparx5, port); - if (err) - return err; - - /* Set Max Length */ - spx5_rmw(DEV2G5_MAC_MAXLEN_CFG_MAX_LEN_SET(ETH_MAXLEN), - DEV2G5_MAC_MAXLEN_CFG_MAX_LEN, - sparx5, - DEV2G5_MAC_MAXLEN_CFG(port->portno)); - - /* 1G/2G5: Signal Detect configuration */ - spx5_wr(DEV2G5_PCS1G_SD_CFG_SD_POL_SET(sd_pol) | - DEV2G5_PCS1G_SD_CFG_SD_SEL_SET(sd_sel) | - DEV2G5_PCS1G_SD_CFG_SD_ENA_SET(sd_ena), - sparx5, - DEV2G5_PCS1G_SD_CFG(port->portno)); - /* Set Pause WM hysteresis */ spx5_rmw(QSYS_PAUSE_CFG_PAUSE_START_SET(pause_start) | QSYS_PAUSE_CFG_PAUSE_STOP_SET(pause_stop) | @@ -1108,6 +1106,27 @@ int sparx5_port_init(struct sparx5 *sparx5, ANA_CL_FILTER_CTRL_FILTER_SMAC_MC_DIS, sparx5, ANA_CL_FILTER_CTRL(port->portno)); + if (ops->is_port_rgmii(port->portno)) + return 0; /* RGMII device - nothing more to configure */ + + /* Configure MAC vlan awareness */ + err = sparx5_port_max_tags_set(sparx5, port); + if (err) + return err; + + /* Set Max Length */ + spx5_rmw(DEV2G5_MAC_MAXLEN_CFG_MAX_LEN_SET(ETH_MAXLEN), + DEV2G5_MAC_MAXLEN_CFG_MAX_LEN, + sparx5, + DEV2G5_MAC_MAXLEN_CFG(port->portno)); + + /* 1G/2G5: Signal Detect configuration */ + spx5_wr(DEV2G5_PCS1G_SD_CFG_SD_POL_SET(sd_pol) | + DEV2G5_PCS1G_SD_CFG_SD_SEL_SET(sd_sel) | + DEV2G5_PCS1G_SD_CFG_SD_ENA_SET(sd_ena), + sparx5, + DEV2G5_PCS1G_SD_CFG(port->portno)); + if (conf->portmode == PHY_INTERFACE_MODE_QSGMII || conf->portmode == PHY_INTERFACE_MODE_SGMII) { err = sparx5_serdes_set(sparx5, port, conf); @@ -1151,7 +1170,7 @@ int sparx5_port_init(struct sparx5 *sparx5, spx5_inst_rmw(DEV10G_MAC_MAXLEN_CFG_MAX_LEN_SET(ETH_MAXLEN), DEV10G_MAC_MAXLEN_CFG_MAX_LEN, devinst, - DEV10G_MAC_ENA_CFG(0)); + DEV10G_MAC_MAXLEN_CFG(0)); /* Handle Signal Detect in 10G PCS */ spx5_inst_wr(PCS10G_BR_PCS_SD_CFG_SD_POL_SET(sd_pol) | diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_port.h b/drivers/net/ethernet/microchip/sparx5/sparx5_port.h index 9b9bcc6834bc..c8a37468a3d1 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_port.h +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_port.h @@ -40,6 +40,11 @@ static inline bool sparx5_port_is_25g(int portno) return portno >= 56 && portno <= 63; } +static inline bool sparx5_port_is_rgmii(int portno) +{ + return false; +} + static inline u32 sparx5_to_high_dev(struct sparx5 *sparx5, int port) { const struct sparx5_ops *ops = sparx5->data->ops; diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c b/drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c index 1c2903700a9c..2f168700f63c 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c @@ -303,7 +303,6 @@ void sparx5_get_hwtimestamp(struct sparx5 *sparx5, spin_unlock_irqrestore(&sparx5->ptp_clock_lock, flags); } -EXPORT_SYMBOL_GPL(sparx5_get_hwtimestamp); irqreturn_t sparx5_ptp_irq_handler(int irq, void *args) { |