diff --git a/arch/arm64/configs/phytium_defconfig b/arch/arm64/configs/phytium_defconfig index 407d10fda023be55b7f84d911a6ee706ec259fc8..e40cc0a83d460413eff9600edf0dc5b138c42a9b 100644 --- a/arch/arm64/configs/phytium_defconfig +++ b/arch/arm64/configs/phytium_defconfig @@ -277,6 +277,9 @@ CONFIG_SKY2=y CONFIG_MLX4_EN=m CONFIG_MLX5_CORE=m CONFIG_MLX5_CORE_EN=y +CONFIG_PHYTMAC=y +CONFIG_PHYTMAC_PLATFORM=y +CONFIG_PHYTMAC_PCI=y CONFIG_QCOM_EMAC=m CONFIG_RMNET=m CONFIG_SMC91X=y diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig index fad9a2c77fa7ccc226975b5d1b1d53bc4d6b38e9..53bcc51a058adcc8d26771909243da3c2fcecb41 100644 --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -157,6 +157,7 @@ config ETHOC source "drivers/net/ethernet/packetengines/Kconfig" source "drivers/net/ethernet/pasemi/Kconfig" source "drivers/net/ethernet/pensando/Kconfig" +source "drivers/net/ethernet/phytium/Kconfig" source "drivers/net/ethernet/qlogic/Kconfig" source "drivers/net/ethernet/qualcomm/Kconfig" source "drivers/net/ethernet/rdc/Kconfig" diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile index f8f38dcb5f8a05b59952a4746ba60f70e7f1411a..ef375bf330128cf9d52c3d1d77f9041925b0e232 100644 --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile @@ -69,6 +69,7 @@ obj-$(CONFIG_NET_VENDOR_OKI) += oki-semi/ obj-$(CONFIG_ETHOC) += ethoc.o obj-$(CONFIG_NET_VENDOR_PACKET_ENGINES) += packetengines/ obj-$(CONFIG_NET_VENDOR_PASEMI) += pasemi/ +obj-$(CONFIG_NET_VENDOR_PHYTIUM) += phytium/ obj-$(CONFIG_NET_VENDOR_QLOGIC) += qlogic/ obj-$(CONFIG_NET_VENDOR_QUALCOMM) += qualcomm/ obj-$(CONFIG_NET_VENDOR_REALTEK) += realtek/ diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index 80d1ca87d814183bdc4987017688ed793dc6faf0..5b86336fe17073200635b790f50a91e18396a64c 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -9,7 +9,6 @@ #include #include -#include #include #include #include @@ -18,12 +17,6 @@ #define MACB_EXT_DESC #endif -enum irq_type { - IRQ_TYPE_INTX = 1, - IRQ_TYPE_MSI = 2, - IRQ_TYPE_MAX = 3, -}; - #define MACB_GREGS_NBR 16 #define MACB_GREGS_VERSION 2 #define MACB_MAX_QUEUES 8 @@ -180,7 +173,6 @@ enum irq_type { #define GEM_DCFG12 0x02AC /* Design Config 12 */ #define GEM_USX_CONTROL 0x0A80 /* High speed PCS control register */ #define GEM_USX_STATUS 0x0A88 /* High speed PCS status register */ -#define GEM_TAIL_ENABLE 0x0E7C /* Phytium: Enable tail */ #define GEM_TXBDCTRL 0x04cc /* TX Buffer Descriptor control register */ #define GEM_RXBDCTRL 0x04d0 /* RX Buffer Descriptor control register */ @@ -215,7 +207,6 @@ enum irq_type { #define GEM_IER(hw_q) (0x0600 + ((hw_q) << 2)) #define GEM_IDR(hw_q) (0x0620 + ((hw_q) << 2)) #define GEM_IMR(hw_q) (0x0640 + ((hw_q) << 2)) -#define GEM_TAIL(hw_q) (0x0e80 + ((hw_q) << 2)) /* Phytium: tail register */ #define GEM_SRC_SEL_LN 0x1C04 #define GEM_DIV_SEL0_LN 0x1C08 #define GEM_DIV_SEL1_LN 0x1C0C @@ -245,8 +236,6 @@ enum irq_type { #define GEM_PHY_INT_CLEAR 0x1C8C #define GEM_PHY_INT_STATE 0x1C90 -#define GEM_INTX_IRQ_MASK 0x1C7C /* Phytium: irq mask */ - /* Bitfields in NCR */ #define MACB_LB_OFFSET 0 /* reserved */ #define MACB_LB_SIZE 1 @@ -749,7 +738,6 @@ enum irq_type { #define MACB_CAPS_BD_RD_PREFETCH 0x00000080 #define MACB_CAPS_NEEDS_RSTONUBR 0x00000100 #define MACB_CAPS_SEL_CLK 0x00000200 -#define MACB_CAPS_TAILPTR 0x00001000 /* Phytium: tail register */ #define MACB_CAPS_CLK_HW_CHG 0x04000000 #define MACB_CAPS_MACB_IS_EMAC 0x08000000 #define MACB_CAPS_FIFO_MODE 0x10000000 @@ -1228,7 +1216,6 @@ struct macb_queue { unsigned int RBQS; unsigned int RBQP; unsigned int RBQPH; - unsigned int TAILADDR; unsigned int tx_head, tx_tail; struct macb_dma_desc *tx_ring; @@ -1407,24 +1394,6 @@ static inline bool gem_has_ptp(struct macb *bp) return !!(bp->caps & MACB_CAPS_GEM_HAS_PTP); } -enum phytium_type { - PHYTIUM_DEV_1P0 = 1, - PHYTIUM_DEV_2P0, - PHYTIUM_DEV_3P0, -}; - -struct phytium_platform_pdata { - int phytium_dev_type; - struct clk *txclk; - struct clk *rxclk; - struct clk *tsu_clk; - u32 caps; - int irq_type; - int irq[4]; - phy_interface_t phy_interface; - const struct property_entry *properties; -}; - /** * struct macb_platform_data - platform data for MACB Ethernet used for PCI registration * @pclk: platform clock @@ -1433,7 +1402,6 @@ struct phytium_platform_pdata { struct macb_platform_data { struct clk *pclk; struct clk *hclk; - struct phytium_platform_pdata phytium_macb_pdata; }; #endif /* _MACB_H */ diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index a82071da9c8f098af69d2617f9d6f4b0556e7293..0d8ad1facefd7d904243e37e5ab6cf4365659c6b 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -935,19 +935,6 @@ static void phytium_gem2p0_sel_clk(struct macb *bp, int speed) gem_readl(bp, HS_MAC_CONFIG))); } -static void phytium_gem3p0_sel_clk(struct macb *bp, int speed) -{ - if (speed == SPEED_100 || speed == SPEED_10) - gem_writel(bp, HS_MAC_CONFIG, GEM_BFINS(HS_MAC_SPEED, HS_SPEED_100M, - gem_readl(bp, HS_MAC_CONFIG))); - else if (speed == SPEED_1000) - gem_writel(bp, HS_MAC_CONFIG, GEM_BFINS(HS_MAC_SPEED, HS_SPEED_1000M, - gem_readl(bp, HS_MAC_CONFIG))); - else if (speed == SPEED_2500) - gem_writel(bp, HS_MAC_CONFIG, GEM_BFINS(HS_MAC_SPEED, HS_SPEED_2500M, - gem_readl(bp, HS_MAC_CONFIG))); -} - static void macb_mac_link_up(struct phylink_config *config, struct phy_device *phy, unsigned int mode, phy_interface_t interface, @@ -1086,14 +1073,8 @@ static int macb_phylink_connect(struct macb *bp) struct device_node *dn = bp->pdev->dev.of_node; struct net_device *dev = bp->dev; struct phy_device *phydev; - struct macb_platform_data *pdata = dev_get_platdata(&bp->pdev->dev); int ret = 0; - if (pdata && pdata->phytium_macb_pdata.properties) { - phylink_start(bp->phylink); - return 0; - } - if (dn) ret = phylink_of_phy_connect(bp->phylink, dn, 0); @@ -1151,11 +1132,10 @@ static int macb_mii_probe(struct net_device *dev) bp->phylink_config.dev = &dev->dev; bp->phylink_config.type = PHYLINK_NETDEV; - if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII || - bp->phy_interface == PHY_INTERFACE_MODE_2500BASEX) { + if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII) { bp->phylink_config.poll_fixed_state = true; bp->phylink_config.get_fixed_state = macb_get_pcs_fixed_state; - } else if (bp->phy_interface == PHY_INTERFACE_MODE_10GBASER || + } else if (bp->phy_interface == PHY_INTERFACE_MODE_2500BASEX || bp->phy_interface == PHY_INTERFACE_MODE_USXGMII) { bp->phylink_config.poll_fixed_state = true; bp->phylink_config.get_fixed_state = macb_get_usx_pcs_fixed_state; @@ -2532,8 +2512,6 @@ static netdev_tx_t macb_start_xmit(struct sk_buff *skb, struct net_device *dev) wmb(); skb_tx_timestamp(skb); - if (bp->caps & MACB_CAPS_TAILPTR) - queue_writel(queue, TAILADDR, BIT(31) | macb_tx_ring_wrap(bp, queue->tx_head)); macb_writel(bp, NCR, macb_readl(bp, NCR) | MACB_BIT(TSTART)); if (CIRC_SPACE(queue->tx_head, queue->tx_tail, bp->tx_ring_size) < 1) @@ -2731,9 +2709,6 @@ static void gem_init_rings(struct macb *bp) queue->tx_head = 0; queue->tx_tail = 0; - if (bp->caps & MACB_CAPS_TAILPTR) - queue_writel(queue, TAILADDR, BIT(31) | queue->tx_head); - queue->rx_tail = 0; queue->rx_prepared_head = 0; @@ -2971,8 +2946,6 @@ static void macb_init_hw(struct macb *bp) bp->rx_frm_len_mask = MACB_RX_JFRMLEN_MASK; gem_writel(bp, AXI_PIPE, 0x1010); - if (bp->caps & MACB_CAPS_TAILPTR) - gem_writel(bp, TAIL_ENABLE, 0x80000001); if (bp->phy_interface == PHY_INTERFACE_MODE_USXGMII || bp->phy_interface == PHY_INTERFACE_MODE_5GBASER || @@ -4055,15 +4028,10 @@ static void macb_configure_caps(struct macb *bp, const struct macb_config *dt_conf) { u32 dcfg; - struct macb_platform_data *pdata; if (dt_conf) bp->caps = dt_conf->caps; - pdata = dev_get_platdata(&bp->pdev->dev); - if (pdata && pdata->phytium_macb_pdata.phytium_dev_type == PHYTIUM_DEV_3P0) - bp->caps |= pdata->phytium_macb_pdata.caps; - if (hw_is_gem(bp->regs, bp->native_io)) { bp->caps |= MACB_CAPS_MACB_IS_GEM; @@ -4116,172 +4084,24 @@ static void macb_probe_queues(void __iomem *mem, *num_queues = hweight32(*queue_mask); } -#ifdef CONFIG_ACPI -static int phytium_clk_acpi_init(struct platform_device *pdev, struct clk **pclk, - struct clk **hclk, struct clk **tx_clk, - struct clk **rx_clk, struct clk **tsu_clk) +static int macb_clk_init(struct platform_device *pdev, struct clk **pclk, + struct clk **hclk, struct clk **tx_clk, + struct clk **rx_clk, struct clk **tsu_clk) { struct macb_platform_data *pdata; int err; - pdata = dev_get_platdata(&pdev->dev); - if (pdata) { - *pclk = pdata->pclk; - *hclk = pdata->hclk; - *tx_clk = pdata->phytium_macb_pdata.txclk; - *rx_clk = pdata->phytium_macb_pdata.rxclk; - *tsu_clk = pdata->phytium_macb_pdata.tsu_clk; - } else { + /* clk get not support ACPI */ + if (has_acpi_companion(&pdev->dev)) { + dev_info(&pdev->dev, "ACPI skip get macb clk\n"); *pclk = NULL; *hclk = NULL; *tx_clk = NULL; *rx_clk = NULL; *tsu_clk = NULL; + return 0; } - err = clk_prepare_enable(*pclk); - if (err) { - dev_err(&pdev->dev, "failed to enable pclk (%d)\n", err); - return err; - } - - err = clk_prepare_enable(*hclk); - if (err) { - dev_err(&pdev->dev, "failed to enable hclk (%d)\n", err); - goto err_disable_pclk; - } - - err = clk_prepare_enable(*tx_clk); - if (err) { - dev_err(&pdev->dev, "failed to enable tx_clk (%d)\n", err); - goto err_disable_hclk; - } - - err = clk_prepare_enable(*rx_clk); - if (err) { - dev_err(&pdev->dev, "failed to enable rx_clk (%d)\n", err); - goto err_disable_txclk; - } - - err = clk_prepare_enable(*tsu_clk); - if (err) { - dev_err(&pdev->dev, "failed to enable tsu_clk (%d)\n", err); - goto err_disable_rxclk; - } - - return 0; - -err_disable_rxclk: - clk_disable_unprepare(*rx_clk); - -err_disable_txclk: - clk_disable_unprepare(*tx_clk); - -err_disable_hclk: - clk_disable_unprepare(*hclk); - -err_disable_pclk: - clk_disable_unprepare(*pclk); - - return err; -} -#endif - -static int phytium_clk_init(struct platform_device *pdev, struct clk **pclk, - struct clk **hclk, struct clk **tx_clk, - struct clk **rx_clk, struct clk **tsu_clk) -{ - struct macb_platform_data *pdata; - int err; - - pdata = dev_get_platdata(&pdev->dev); - if (pdata) { - *pclk = pdata->pclk; - *hclk = pdata->hclk; - *tx_clk = pdata->phytium_macb_pdata.txclk; - *rx_clk = pdata->phytium_macb_pdata.rxclk; - *tsu_clk = pdata->phytium_macb_pdata.tsu_clk; - } else { - *pclk = devm_clk_get(&pdev->dev, "pclk"); - *hclk = devm_clk_get(&pdev->dev, "hclk"); - *tx_clk = devm_clk_get_optional(&pdev->dev, "tx_clk"); - *rx_clk = devm_clk_get_optional(&pdev->dev, "rx_clk"); - *tsu_clk = devm_clk_get_optional(&pdev->dev, "tsu_clk"); - } - - if (IS_ERR_OR_NULL(*pclk)) - return dev_err_probe(&pdev->dev, - IS_ERR(*pclk) ? PTR_ERR(*pclk) : -ENODEV, - "failed to get pclk\n"); - - if (IS_ERR_OR_NULL(*hclk)) - return dev_err_probe(&pdev->dev, - IS_ERR(*hclk) ? PTR_ERR(*hclk) : -ENODEV, - "failed to get hclk\n"); - - if (IS_ERR(*tx_clk)) - return PTR_ERR(*tx_clk); - - if (IS_ERR(*rx_clk)) - return PTR_ERR(*rx_clk); - - if (IS_ERR(*tsu_clk)) - return PTR_ERR(*tsu_clk); - - err = clk_prepare_enable(*pclk); - if (err) { - dev_err(&pdev->dev, "failed to enable pclk (%d)\n", err); - return err; - } - - err = clk_prepare_enable(*hclk); - if (err) { - dev_err(&pdev->dev, "failed to enable hclk (%d)\n", err); - goto err_disable_pclk; - } - - err = clk_prepare_enable(*tx_clk); - if (err) { - dev_err(&pdev->dev, "failed to enable tx_clk (%d)\n", err); - goto err_disable_hclk; - } - - err = clk_prepare_enable(*rx_clk); - if (err) { - dev_err(&pdev->dev, "failed to enable rx_clk (%d)\n", err); - goto err_disable_txclk; - } - - err = clk_prepare_enable(*tsu_clk); - if (err) { - dev_err(&pdev->dev, "failed to enable tsu_clk (%d)\n", err); - goto err_disable_rxclk; - } - - return 0; - -err_disable_rxclk: - clk_disable_unprepare(*rx_clk); - -err_disable_txclk: - clk_disable_unprepare(*tx_clk); - -err_disable_hclk: - clk_disable_unprepare(*hclk); - -err_disable_pclk: - clk_disable_unprepare(*pclk); - - return err; -} - -static int macb_clk_init(struct platform_device *pdev, struct clk **pclk, - struct clk **hclk, struct clk **tx_clk, - struct clk **rx_clk, struct clk **tsu_clk) -{ - struct macb_platform_data *pdata; - int err; - pdata = dev_get_platdata(&pdev->dev); if (pdata) { *pclk = pdata->pclk; @@ -4368,225 +4188,6 @@ static int macb_clk_init(struct platform_device *pdev, struct clk **pclk, return err; } -static irqreturn_t phytium_macb_interrupt_intx(int irq, void *dev_id) -{ - struct macb *bp = dev_id; - u32 irq_mask; - int i; - - irq_mask = gem_readl(bp, INTX_IRQ_MASK); - if (unlikely(!irq_mask)) - return IRQ_NONE; - - gem_writel(bp, INTX_IRQ_MASK, irq_mask); - - for (i = 0; i < bp->num_queues; i++) { - if ((irq_mask & bp->queue_mask) & BIT(i)) - macb_interrupt(irq, &bp->queues[i]); - } - return IRQ_HANDLED; -} - -static int phytium_queue_request_irq(struct platform_device *pdev, unsigned int q) -{ - int err; - struct macb_queue *queue; - struct macb_platform_data *pdata = dev_get_platdata(&pdev->dev); - struct net_device *dev = platform_get_drvdata(pdev); - struct macb *bp = netdev_priv(dev); - - queue = &bp->queues[q]; - if (q == 0 && pdata->phytium_macb_pdata.irq_type == IRQ_TYPE_INTX) { - dev->irq = pdata->phytium_macb_pdata.irq[q]; - err = devm_request_irq(&pdev->dev, dev->irq, phytium_macb_interrupt_intx, - IRQF_SHARED, dev->name, bp); - if (err) { - dev_err(&pdev->dev, - "Unable to request dev %d (error %d)\n", - dev->irq, err); - } - } - - if (pdata->phytium_macb_pdata.irq_type == IRQ_TYPE_MSI) { - queue->irq = pdata->phytium_macb_pdata.irq[q]; - err = devm_request_irq(&pdev->dev, queue->irq, macb_interrupt, - 0, dev->name, queue); - if (err) { - dev_err(&pdev->dev, - "Unable to request IRQ %d (error %d)\n", - queue->irq, err); - } - } - - return err; -} - -static int phytium_init(struct platform_device *pdev) -{ - struct net_device *dev = platform_get_drvdata(pdev); - unsigned int hw_q, q; - struct macb *bp = netdev_priv(dev); - struct macb_queue *queue; - int err; - u32 val, reg; - struct macb_platform_data *pdata; - - bp->tx_ring_size = DEFAULT_TX_RING_SIZE; - bp->rx_ring_size = DEFAULT_RX_RING_SIZE; - - pdata = dev_get_platdata(&pdev->dev); - - /* set the queue register mapping once for all: queue0 has a special - * register mapping but we don't want to test the queue index then - * compute the corresponding register offset at run time. - */ - for (hw_q = 0, q = 0; hw_q < MACB_MAX_QUEUES; ++hw_q) { - if (!(bp->queue_mask & (1 << hw_q))) - continue; - - queue = &bp->queues[q]; - queue->bp = bp; - netif_napi_add(dev, &queue->napi, macb_poll, NAPI_POLL_WEIGHT); - if (hw_q) { - queue->ISR = GEM_ISR(hw_q - 1); - queue->IER = GEM_IER(hw_q - 1); - queue->IDR = GEM_IDR(hw_q - 1); - queue->IMR = GEM_IMR(hw_q - 1); - queue->TBQP = GEM_TBQP(hw_q - 1); - queue->RBQP = GEM_RBQP(hw_q - 1); - queue->RBQS = GEM_RBQS(hw_q - 1); -#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - if (bp->hw_dma_cap & HW_DMA_CAP_64B) { - queue->TBQPH = GEM_TBQPH(hw_q - 1); - queue->RBQPH = GEM_RBQPH(hw_q - 1); - } -#endif - } else { - /* queue0 uses legacy registers */ - queue->ISR = MACB_ISR; - queue->IER = MACB_IER; - queue->IDR = MACB_IDR; - queue->IMR = MACB_IMR; - queue->TBQP = MACB_TBQP; - queue->RBQP = MACB_RBQP; -#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - if (bp->hw_dma_cap & HW_DMA_CAP_64B) { - queue->TBQPH = MACB_TBQPH; - queue->RBQPH = MACB_RBQPH; - } -#endif - } - - if (bp->caps & MACB_CAPS_TAILPTR) - queue->TAILADDR = GEM_TAIL(hw_q); - /* get irq: here we use the linux queue index, not the hardware - * queue index. the queue irq definitions in the device tree - * must remove the optional gaps that could exist in the - * hardware queue mask. - */ - if (pdata && pdata->phytium_macb_pdata.phytium_dev_type == PHYTIUM_DEV_3P0) { - err = phytium_queue_request_irq(pdev, q); - } else { - queue->irq = platform_get_irq(pdev, q); - err = devm_request_irq(&pdev->dev, queue->irq, macb_interrupt, - IRQF_SHARED, dev->name, queue); - if (err) { - dev_err(&pdev->dev, - "Unable to request IRQ %d (error %d)\n", - queue->irq, err); - return err; - } - } - - INIT_WORK(&queue->tx_error_task, macb_tx_error_task); - q++; - } - - dev->netdev_ops = &macb_netdev_ops; - - /* setup appropriated routines according to adapter type */ - if (macb_is_gem(bp)) { - bp->max_tx_length = GEM_MAX_TX_LEN; - bp->macbgem_ops.mog_alloc_rx_buffers = gem_alloc_rx_buffers; - bp->macbgem_ops.mog_free_rx_buffers = gem_free_rx_buffers; - bp->macbgem_ops.mog_init_rings = gem_init_rings; - bp->macbgem_ops.mog_rx = gem_rx; - dev->ethtool_ops = &gem_ethtool_ops; - } else { - bp->max_tx_length = MACB_MAX_TX_LEN; - bp->macbgem_ops.mog_alloc_rx_buffers = macb_alloc_rx_buffers; - bp->macbgem_ops.mog_free_rx_buffers = macb_free_rx_buffers; - bp->macbgem_ops.mog_init_rings = macb_init_rings; - bp->macbgem_ops.mog_rx = macb_rx; - dev->ethtool_ops = &macb_ethtool_ops; - } - - /* Set features */ - dev->hw_features = NETIF_F_SG; - - /* Check LSO capability */ - if (GEM_BFEXT(PBUF_LSO, gem_readl(bp, DCFG6))) - dev->hw_features |= MACB_NETIF_LSO; - - /* Checksum offload is only available on gem with packet buffer */ - if (macb_is_gem(bp) && !(bp->caps & MACB_CAPS_FIFO_MODE)) - dev->hw_features |= NETIF_F_HW_CSUM | NETIF_F_RXCSUM; - if (bp->caps & MACB_CAPS_SG_DISABLED) - dev->hw_features &= ~NETIF_F_SG; - dev->features = dev->hw_features; - - /* Check RX Flow Filters support. - * Max Rx flows set by availability of screeners & compare regs: - * each 4-tuple define requires 1 T2 screener reg + 3 compare regs - */ - reg = gem_readl(bp, DCFG8); - bp->max_tuples = min((GEM_BFEXT(SCR2CMP, reg) / 3), - GEM_BFEXT(T2SCR, reg)); - INIT_LIST_HEAD(&bp->rx_fs_list.list); - if (bp->max_tuples > 0) { - /* also needs one ethtype match to check IPv4 */ - if (GEM_BFEXT(SCR2ETH, reg) > 0) { - /* program this reg now */ - reg = 0; - reg = GEM_BFINS(ETHTCMP, (uint16_t)ETH_P_IP, reg); - gem_writel_n(bp, ETHT, SCRT2_ETHT, reg); - /* Filtering is supported in hw but don't enable it in kernel now */ - dev->hw_features |= NETIF_F_NTUPLE; - /* init Rx flow definitions */ - bp->rx_fs_list.count = 0; - spin_lock_init(&bp->rx_fs_lock); - } else { - bp->max_tuples = 0; - } - } - - if (!(bp->caps & MACB_CAPS_USRIO_DISABLED)) { - val = 0; - if (phy_interface_mode_is_rgmii(bp->phy_interface)) - val = GEM_BIT(RGMII); - else if (bp->phy_interface == PHY_INTERFACE_MODE_RMII && - (bp->caps & MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII)) - val = MACB_BIT(RMII); - else if (!(bp->caps & MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII)) - val = MACB_BIT(MII); - - if (bp->caps & MACB_CAPS_USRIO_HAS_CLKEN) - val |= MACB_BIT(CLKEN); - - macb_or_gem_writel(bp, USRIO, val); - } - - /* Set MII management clock divider */ - val = macb_mdc_clk_div(bp); - val |= macb_dbw(bp); - if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII || - bp->phy_interface == PHY_INTERFACE_MODE_2500BASEX) - val |= GEM_BIT(SGMIIEN) | GEM_BIT(PCSSEL); - macb_writel(bp, NCFGR, val); - - return 0; -} - static int macb_init(struct platform_device *pdev) { struct net_device *dev = platform_get_drvdata(pdev); @@ -5334,8 +4935,8 @@ static const struct macb_config phytium_gem1p0_config = { MACB_CAPS_BD_RD_PREFETCH | MACB_CAPS_SEL_CLK, .dma_burst_length = 16, - .clk_init = phytium_clk_init, - .init = phytium_init, + .clk_init = macb_clk_init, + .init = macb_init, .jumbo_max_len = 16360, .sel_clk_hw = phytium_gem1p0_sel_clk, }; @@ -5353,49 +4954,6 @@ static const struct macb_config phytium_gem2p0_config = { .sel_clk_hw = phytium_gem2p0_sel_clk, }; -static const struct macb_config phytium_gem3p0_config = { - .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | - MACB_CAPS_JUMBO | - MACB_CAPS_GEM_HAS_PTP | - MACB_CAPS_BD_RD_PREFETCH | - MACB_CAPS_SEL_CLK | - MACB_CAPS_TAILPTR, - .dma_burst_length = 16, - .clk_init = phytium_clk_init, - .init = phytium_init, - .jumbo_max_len = 10240, - .sel_clk_hw = phytium_gem3p0_sel_clk, -}; - -#ifdef CONFIG_ACPI -static const struct macb_config phytium_gem1p0_acpi_config = { - .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | - MACB_CAPS_JUMBO | - MACB_CAPS_GEM_HAS_PTP | - MACB_CAPS_BD_RD_PREFETCH | - MACB_CAPS_SEL_CLK, - .dma_burst_length = 16, - .clk_init = phytium_clk_acpi_init, - .init = phytium_init, - .jumbo_max_len = 10240, - .sel_clk_hw = phytium_gem1p0_sel_clk, -}; - -static const struct macb_config phytium_gem3p0_acpi_config = { - .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | - MACB_CAPS_JUMBO | - MACB_CAPS_GEM_HAS_PTP | - MACB_CAPS_BD_RD_PREFETCH | - MACB_CAPS_SEL_CLK | - MACB_CAPS_TAILPTR, - .dma_burst_length = 16, - .clk_init = phytium_clk_acpi_init, - .init = phytium_init, - .jumbo_max_len = 10240, - .sel_clk_hw = phytium_gem3p0_sel_clk, -}; -#endif - static const struct of_device_id macb_dt_ids[] = { { .compatible = "cdns,at32ap7000-macb" }, { .compatible = "cdns,at91sam9260-macb", .data = &at91sam9260_config }, @@ -5415,7 +4973,6 @@ static const struct of_device_id macb_dt_ids[] = { { .compatible = "sifive,fu540-c000-gem", .data = &fu540_c000_config }, { .compatible = "cdns,phytium-gem-1.0", .data = &phytium_gem1p0_config }, { .compatible = "cdns,phytium-gem-2.0", .data = &phytium_gem2p0_config }, - { .compatible = "cdns,phytium-gem-3.0", .data = &phytium_gem3p0_config }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, macb_dt_ids); @@ -5423,8 +4980,7 @@ MODULE_DEVICE_TABLE(of, macb_dt_ids); #ifdef CONFIG_ACPI static const struct acpi_device_id macb_acpi_ids[] = { - { .id = "PHYT0036", .driver_data = (kernel_ulong_t)&phytium_gem1p0_acpi_config }, - { .id = "PHYT0046", .driver_data = (kernel_ulong_t)&phytium_gem3p0_acpi_config }, + { .id = "PHYT0036", .driver_data = (kernel_ulong_t)&phytium_gem1p0_config }, { } }; @@ -5477,10 +5033,9 @@ static int macb_probe(struct platform_device *pdev) struct clk **) = macb_config->clk_init; int (*init)(struct platform_device *) = macb_config->init; struct device_node *np = pdev->dev.of_node; - struct clk *pclk = NULL, *hclk = NULL, *tx_clk = NULL, *rx_clk = NULL; + struct clk *pclk, *hclk = NULL, *tx_clk = NULL, *rx_clk = NULL; struct clk *tsu_clk = NULL; unsigned int queue_mask, num_queues; - struct macb_platform_data *pdata; bool native_io; struct net_device *dev; struct resource *regs; @@ -5489,15 +5044,6 @@ static int macb_probe(struct platform_device *pdev) struct macb *bp; int err, val; - pdata = dev_get_platdata(&pdev->dev); - if (pdata) { - if (pdata->phytium_macb_pdata.phytium_dev_type == PHYTIUM_DEV_3P0) { - macb_config = &phytium_gem3p0_config; - clk_init = macb_config->clk_init; - init = macb_config->init; - } - } - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); mem = devm_ioremap_resource(&pdev->dev, regs); if (IS_ERR(mem)) @@ -5630,14 +5176,10 @@ static int macb_probe(struct platform_device *pdev) } err = macb_get_phy_mode(pdev); - if (err < 0) { - if (pdata && pdata->phytium_macb_pdata.phytium_dev_type == PHYTIUM_DEV_3P0) - bp->phy_interface = pdata->phytium_macb_pdata.phy_interface; - else - bp->phy_interface = PHY_INTERFACE_MODE_MII; - } else { + if (err < 0) + bp->phy_interface = PHY_INTERFACE_MODE_MII; + else bp->phy_interface = err; - } bp->link = 0; bp->duplex = DUPLEX_UNKNOWN; @@ -5713,7 +5255,6 @@ static int macb_remove(struct platform_device *pdev) { struct net_device *dev; struct macb *bp; - struct macb_platform_data *pdata = dev_get_platdata(&pdev->dev); dev = platform_get_drvdata(pdev); @@ -5729,14 +5270,11 @@ static int macb_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); pm_runtime_dont_use_autosuspend(&pdev->dev); if (!pm_runtime_suspended(&pdev->dev)) { - if (!pdata) { - clk_disable_unprepare(bp->tx_clk); - clk_disable_unprepare(bp->hclk); - clk_disable_unprepare(bp->pclk); - clk_disable_unprepare(bp->rx_clk); - clk_disable_unprepare(bp->tsu_clk); - } - + clk_disable_unprepare(bp->tx_clk); + clk_disable_unprepare(bp->hclk); + clk_disable_unprepare(bp->pclk); + clk_disable_unprepare(bp->rx_clk); + clk_disable_unprepare(bp->tsu_clk); pm_runtime_set_suspended(&pdev->dev); } phylink_destroy(bp->phylink); diff --git a/drivers/net/ethernet/cadence/macb_pci.c b/drivers/net/ethernet/cadence/macb_pci.c index 63d8e3e01d946d577ea48cb2047898a800ec0c02..d9b05bacc9da029a698066efb268aa014169d060 100644 --- a/drivers/net/ethernet/cadence/macb_pci.c +++ b/drivers/net/ethernet/cadence/macb_pci.c @@ -21,166 +21,17 @@ #define CDNS_VENDOR_ID 0x17cd #define CDNS_DEVICE_ID 0xe007 -#define PCI_DEVICE_ID_GMAC_3P0 0xdc3b -#define PCI_SUBDEVICE_ID_SGMII 0x1000 -#define PCI_SUBDEVICE_ID_1000BASEX 0x1001 -#define PCI_SUBDEVICE_ID_USXGMII 0x1004 -#define PCI_SUBDEVICE_ID_10GBASER 0x1005 #define GEM_PCLK_RATE 50000000 #define GEM_HCLK_RATE 50000000 -#define GEM_TXCLK_RATE 25000000 -#define GEM_RXCLK_RATE 25000000 -#define GEM_TSUCLK_RATE 300000000 - -static const u32 fixedlink[][5] = { - {0, 1, 1000, 1, 0}, - {0, 1, 2500, 1, 0}, - {0, 1, 5000, 1, 0}, - {0, 1, 10000, 1, 0}, -}; - -static const struct property_entry fl_properties[][2] = { - {PROPERTY_ENTRY_U32_ARRAY("fixed-link", fixedlink[0]), {}}, - {PROPERTY_ENTRY_U32_ARRAY("fixed-link", fixedlink[1]), {}}, - {PROPERTY_ENTRY_U32_ARRAY("fixed-link", fixedlink[2]), {}}, - {PROPERTY_ENTRY_U32_ARRAY("fixed-link", fixedlink[3]), {}}, -}; - -static const struct phytium_platform_pdata phytium_sgmii_pdata = { - .phytium_dev_type = PHYTIUM_DEV_3P0, - .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | - MACB_CAPS_JUMBO | - MACB_CAPS_GEM_HAS_PTP | - MACB_CAPS_BD_RD_PREFETCH | - MACB_CAPS_USRIO_DISABLED | - MACB_CAPS_TAILPTR, - .phy_interface = PHY_INTERFACE_MODE_SGMII, -}; - -static const struct phytium_platform_pdata phytium_1000basex_pdata = { - .phytium_dev_type = PHYTIUM_DEV_3P0, - .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | - MACB_CAPS_JUMBO | - MACB_CAPS_GEM_HAS_PTP | - MACB_CAPS_BD_RD_PREFETCH | - MACB_CAPS_USRIO_DISABLED | - MACB_CAPS_TAILPTR, - .phy_interface = PHY_INTERFACE_MODE_SGMII, - .properties = fl_properties[0], -}; - -static const struct phytium_platform_pdata phytium_usxgmii_pdata = { - .phytium_dev_type = PHYTIUM_DEV_3P0, - .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | - MACB_CAPS_JUMBO | - MACB_CAPS_GEM_HAS_PTP | - MACB_CAPS_BD_RD_PREFETCH | - MACB_CAPS_USRIO_DISABLED | - MACB_CAPS_TAILPTR, - .phy_interface = PHY_INTERFACE_MODE_USXGMII, - .properties = fl_properties[3], -}; - -static int phytium_macb_pci_init(struct pci_dev *pdev, struct macb_platform_data *plat_data, - struct platform_device_info *plat_info, - struct phytium_platform_pdata *phytium_data) -{ - int i; - int err; - char clkname[20]; - - err = pci_alloc_irq_vectors(pdev, 4, 4, PCI_IRQ_MSI); - if (err < 0) { - dev_err(&pdev->dev, "err=%d, fialed to allocate MSI entry", err); - plat_data->phytium_macb_pdata.irq_type = IRQ_TYPE_INTX; - plat_data->phytium_macb_pdata.irq[0] = pdev->irq; - - } else { - plat_data->phytium_macb_pdata.irq_type = IRQ_TYPE_MSI; - for (i = 0; i < 4; i++) - plat_data->phytium_macb_pdata.irq[i] = pci_irq_vector(pdev, i); - } - - plat_data->phytium_macb_pdata.phytium_dev_type = phytium_data->phytium_dev_type; - plat_data->phytium_macb_pdata.caps = phytium_data->caps; - plat_data->phytium_macb_pdata.phy_interface = phytium_data->phy_interface; - if (phytium_data && phytium_data->properties) { - plat_info->fwnode = NULL; - plat_info->properties = phytium_data->properties; - plat_data->phytium_macb_pdata.properties = phytium_data->properties; - } - - snprintf(clkname, 20, "txclk:%02x", plat_info->id); - plat_data->phytium_macb_pdata.txclk = - clk_register_fixed_rate(&pdev->dev, clkname, NULL, 0, GEM_TXCLK_RATE); - if (IS_ERR(plat_data->phytium_macb_pdata.txclk)) { - err = PTR_ERR(plat_data->phytium_macb_pdata.txclk); - goto err_txclk_register; - } - - snprintf(clkname, 20, "rxclk:%02x", plat_info->id); - plat_data->phytium_macb_pdata.rxclk = - clk_register_fixed_rate(&pdev->dev, clkname, NULL, 0, GEM_RXCLK_RATE); - if (IS_ERR(plat_data->phytium_macb_pdata.rxclk)) { - err = PTR_ERR(plat_data->phytium_macb_pdata.rxclk); - goto err_rxclk_register; - } - - snprintf(clkname, 20, "tsuclk:%02x", plat_info->id); - plat_data->phytium_macb_pdata.tsu_clk = - clk_register_fixed_rate(&pdev->dev, clkname, NULL, 0, GEM_TSUCLK_RATE); - if (IS_ERR(plat_data->phytium_macb_pdata.tsu_clk)) { - err = PTR_ERR(plat_data->phytium_macb_pdata.tsu_clk); - goto err_tsuclk_register; - } - - return 0; - -err_tsuclk_register: - clk_unregister(plat_data->phytium_macb_pdata.rxclk); - -err_rxclk_register: - clk_unregister(plat_data->phytium_macb_pdata.txclk); - -err_txclk_register: - - return err; -} - -static void phytium_macb_pci_uninit(struct pci_dev *pdev) -{ - struct platform_device *plat_dev = pci_get_drvdata(pdev); - struct macb_platform_data *plat_data = dev_get_platdata(&plat_dev->dev); - - plat_dev->dev.dma_ops = NULL; - plat_dev->dev.iommu = NULL; - plat_dev->dev.iommu_group = NULL; - plat_dev->dev.dma_range_map = NULL; - - clk_unregister(plat_data->phytium_macb_pdata.txclk); - clk_unregister(plat_data->phytium_macb_pdata.rxclk); - clk_unregister(plat_data->phytium_macb_pdata.tsu_clk); - - if (plat_data->phytium_macb_pdata.properties) { - struct fwnode_handle *fw_node = dev_fwnode(&plat_dev->dev); - - if (fw_node) - fwnode_remove_software_node(fw_node); - fw_node = NULL; - } -} static int macb_probe(struct pci_dev *pdev, const struct pci_device_id *id) { int err; struct platform_device *plat_dev; struct platform_device_info plat_info; - struct phytium_platform_pdata *phytium_data = NULL; struct macb_platform_data plat_data; struct resource res[2]; - char pclk_name[20] = "pclk"; - char hclk_name[20] = "hclk"; /* enable pci device */ err = pcim_enable_device(pdev); @@ -204,44 +55,32 @@ static int macb_probe(struct pci_dev *pdev, const struct pci_device_id *id) /* set up macb platform data */ memset(&plat_data, 0, sizeof(plat_data)); - /* set up platform device info */ - memset(&plat_info, 0, sizeof(plat_info)); - plat_info.parent = &pdev->dev; - plat_info.fwnode = pdev->dev.fwnode; - plat_info.name = PLAT_DRIVER_NAME; - plat_info.id = pdev->devfn; - plat_info.res = res; - plat_info.num_res = ARRAY_SIZE(res); - plat_info.data = &plat_data; - plat_info.size_data = sizeof(plat_data); - plat_info.dma_mask = pdev->dma_mask; - if (pdev->vendor == PCI_VENDOR_ID_PHYTIUM) - phytium_data = (struct phytium_platform_pdata *)id->driver_data; - /* initialize clocks */ - if (pdev->device == PCI_DEVICE_ID_GMAC_3P0) { - plat_info.id = (pdev->bus->number << 8) | pdev->devfn; - snprintf(pclk_name, 20, "pclk:%02x", plat_info.id); - snprintf(hclk_name, 20, "hclk:%02x", plat_info.id); - } - plat_data.pclk = clk_register_fixed_rate(&pdev->dev, pclk_name, NULL, 0, + plat_data.pclk = clk_register_fixed_rate(&pdev->dev, "pclk", NULL, 0, GEM_PCLK_RATE); if (IS_ERR(plat_data.pclk)) { err = PTR_ERR(plat_data.pclk); goto err_pclk_register; } - plat_data.hclk = clk_register_fixed_rate(&pdev->dev, hclk_name, NULL, 0, + plat_data.hclk = clk_register_fixed_rate(&pdev->dev, "hclk", NULL, 0, GEM_HCLK_RATE); if (IS_ERR(plat_data.hclk)) { err = PTR_ERR(plat_data.hclk); goto err_hclk_register; } - if (pdev->device == PCI_DEVICE_ID_GMAC_3P0) { - if (phytium_macb_pci_init(pdev, &plat_data, &plat_info, phytium_data)) - goto err_phytium_clk_register; - } + /* set up platform device info */ + memset(&plat_info, 0, sizeof(plat_info)); + plat_info.parent = &pdev->dev; + plat_info.fwnode = pdev->dev.fwnode; + plat_info.name = PLAT_DRIVER_NAME; + plat_info.id = pdev->devfn; + plat_info.res = res; + plat_info.num_res = ARRAY_SIZE(res); + plat_info.data = &plat_data; + plat_info.size_data = sizeof(plat_data); + plat_info.dma_mask = pdev->dma_mask; res[1].start = pci_irq_vector(pdev, 0); res[1].name = PCI_DRIVER_NAME; @@ -254,21 +93,11 @@ static int macb_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err_plat_dev_register; } - if (pdev->device == PCI_DEVICE_ID_GMAC_3P0) { - plat_dev->dev.dma_ops = (&pdev->dev)->dma_ops; - plat_dev->dev.iommu = (&pdev->dev)->iommu; - plat_dev->dev.iommu_group = (&pdev->dev)->iommu_group; - plat_dev->dev.dma_range_map = (&pdev->dev)->dma_range_map; - } pci_set_drvdata(pdev, plat_dev); return 0; err_plat_dev_register: - if (pdev->device == PCI_DEVICE_ID_GMAC_3P0) - clk_unregister(plat_data.phytium_macb_pdata.tsu_clk); - -err_phytium_clk_register: clk_unregister(plat_data.hclk); err_hclk_register: @@ -285,26 +114,11 @@ static void macb_remove(struct pci_dev *pdev) clk_unregister(plat_data->pclk); clk_unregister(plat_data->hclk); - - if (pdev->device == PCI_DEVICE_ID_GMAC_3P0) - phytium_macb_pci_uninit(pdev); platform_device_unregister(plat_dev); - - if (pdev->device == PCI_DEVICE_ID_GMAC_3P0) - pci_free_irq_vectors(pdev); } static const struct pci_device_id dev_id_table[] = { { PCI_DEVICE(CDNS_VENDOR_ID, CDNS_DEVICE_ID), }, - { PCI_DEVICE_SUB(PCI_VENDOR_ID_PHYTIUM, PCI_DEVICE_ID_GMAC_3P0, - PCI_VENDOR_ID_PHYTIUM, PCI_SUBDEVICE_ID_SGMII), - .driver_data = (kernel_ulong_t)&phytium_sgmii_pdata}, - { PCI_DEVICE_SUB(PCI_VENDOR_ID_PHYTIUM, PCI_DEVICE_ID_GMAC_3P0, - PCI_VENDOR_ID_PHYTIUM, PCI_SUBDEVICE_ID_1000BASEX), - .driver_data = (kernel_ulong_t)&phytium_1000basex_pdata}, - { PCI_DEVICE_SUB(PCI_VENDOR_ID_PHYTIUM, PCI_DEVICE_ID_GMAC_3P0, - PCI_VENDOR_ID_PHYTIUM, PCI_SUBDEVICE_ID_USXGMII), - .driver_data = (kernel_ulong_t)&phytium_usxgmii_pdata}, { 0, } }; diff --git a/drivers/net/ethernet/phytium/Kconfig b/drivers/net/ethernet/phytium/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..d3f16d0f79a89206296be88d0510edb1963c6b4f --- /dev/null +++ b/drivers/net/ethernet/phytium/Kconfig @@ -0,0 +1,73 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Phytium device configuration +# + +config NET_VENDOR_PHYTIUM + bool "Phytium devices" + depends on HAS_IOMEM + default y + help + If you have a network (Ethernet) card belonging to this class, say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all the + remaining Cadence network card questions. If you say Y, you will be + asked for your specific card in the following questions. + +if NET_VENDOR_PHYTIUM + +config PHYTMAC + tristate "Phytium GMAC support" + depends on HAS_DMA + #depends on PTP_1588_CLOCK_OPTIONAL + select PHYLINK + select CRC32 + help + If you have a network (Ethernet) controller of this type, say Y + or M here. + + To compile this driver as a module, choose M here: the module + will be phytmac. + +config PHYTMAC_ENABLE_PTP + bool "Enable IEEE 1588 hwstamp" + depends on PHYTMAC + depends on PTP_1588_CLOCK + default y + help + Enable IEEE 1588 PTP support for PHYTMAC. + +config PHYTMAC_PLATFORM + tristate "Phytmac Platform support" + depends on PHYTMAC + help + This is Platform driver. + + To compile this driver as a module, choose M here: the module + will be called phytmac_platform. + +config PHYTMAC_PCI + tristate "Phytmac PCI support" + depends on PHYTMAC && PCI + help + This is PCI driver. + + To compile this driver as a module, choose M here: the module + will be called phytmac_pci. + +#config PHYTMAC_1P0 +# bool "Phytmac 1p0 support" +# depends on PHYTMAC +# default y +# help +# To compile this driver as a module, choose M here: the module + +#config PHYTMAC_2P0 +# bool "Phytmac 2p0 support" +# depends on PHYTMAC +# default y +# help +# To compile this driver as a module, choose M here: the module + +endif # NET_VENDOR_PHYTIUM diff --git a/drivers/net/ethernet/phytium/Makefile b/drivers/net/ethernet/phytium/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..6e710d4d54b6641bc1679405d6909827cfd09eeb --- /dev/null +++ b/drivers/net/ethernet/phytium/Makefile @@ -0,0 +1,17 @@ + +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the Phytium network device drivers. +# +# + +obj-$(CONFIG_PHYTMAC) += phytmac.o + +phytmac-objs := phytmac_main.o phytmac_ethtool.o phytmac_v1.o phytmac_v2.o +phytmac-$(CONFIG_PHYTMAC_ENABLE_PTP) += phytmac_ptp.o + +obj-$(CONFIG_PHYTMAC_PLATFORM) += phytmac-platform.o +phytmac-platform-objs := phytmac_platform.o + +obj-$(CONFIG_PHYTMAC_PCI) += phytmac-pci.o +phytmac-pci-objs := phytmac_pci.o diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h new file mode 100644 index 0000000000000000000000000000000000000000..4138326b892bcdc11fee7c11101ff25fd76c3970 --- /dev/null +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -0,0 +1,586 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _PHYTMAC_H +#define _PHYTMAC_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#define PHYTMAC_DRV_NAME "phytium-mac" +#define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" + +#define PHYTMAC_DEFAULT_MSG_ENABLE \ + (NETIF_MSG_DRV | \ + NETIF_MSG_PROBE | \ + NETIF_MSG_LINK | \ + NETIF_MSG_INTR | \ + NETIF_MSG_HW |\ + NETIF_MSG_PKTDATA) + +#define IRQ_TYPE_INT 0 +#define IRQ_TYPE_MSI 1 +#define IRQ_TYPE_INTX 2 + +#define PHYTMAC_MAX_QUEUES 8 +#define DEFAULT_DMA_BURST_LENGTH 16 +#define DEFAULT_JUMBO_MAX_LENGTH 10240 +#define PHYTMAC_MAX_TX_LEN 16320 +#define PHYTMAC_MIN_TX_LEN 64 +#define DEFAULT_TX_RING_SIZE 512 +#define DEFAULT_RX_RING_SIZE 512 +#define MAX_TX_RING_SIZE 1024 +#define MAX_RX_RING_SIZE 4096 +#define MIN_TX_RING_SIZE 64 +#define MIN_RX_RING_SIZE 64 +#define DEFAULT_TX_DESC_MIN_FREE 64 +#define MEMORY_SIZE 4096 +#define MHU_SIZE 0x20 + +#define PHYTMAC_POWEROFF 1 +#define PHYTMAC_POWERON 2 + +#define PHYTMAC_WOL_MAGIC_PACKET 1 + +#define DEFAULT_MSG_RING_SIZE 16 + +#define PHYTMAC_CAPS_JUMBO 0x00000001 +#define PHYTMAC_CAPS_PTP 0x00000002 +#define PHYTMAC_CAPS_BD_RD_PREFETCH 0x00000004 +#define PHYTMAC_CAPS_PCS 0x00000008 +#define PHYTMAC_CAPS_LSO 0x00000010 +#define PHYTMAC_CAPS_SG_DISABLED 0x00000020 +#define PHYTMAC_CAPS_TAILPTR 0x00000040 +#define PHYTMAC_CAPS_START 0x00000080 +#define PHYTMAC_CAPS_NO_WOL 0x0000100 +#define PHYTMAC_CAPS_LPI 0x0000400 +#define PHYTMAC_CAPS_MSG 0x0000800 + +#define PHYTMAC_TX 0x1 +#define PHYTMAC_RX 0x2 + +#define PHYTMAC_GREGS_LEN 16 + +#define PHYTMAC_MTU_MIN_SIZE ETH_MIN_MTU + +#define EQUAL(a, b) ((a) == (b)) + +#define TXD_USE_COUNT(_pdata, s) DIV_ROUND_UP((s), (_pdata)->max_tx_length) + +/* Bit manipulation macros */ +#define PHYTMAC_BIT(_field) \ + (1 << PHYTMAC_##_field##_INDEX) + +#define PHYTMAC_BITS(_field, value) \ + (((value) & ((1 << PHYTMAC_##_field##_WIDTH) - 1)) \ + << PHYTMAC_##_field##_INDEX) + +#define PHYTMAC_GET_BITS(_var, _field) \ + (((_var) >> (PHYTMAC_##_field##_INDEX)) \ + & ((0x1 << (PHYTMAC_##_field##_WIDTH)) - 1)) + +#define PHYTMAC_SET_BITS(_var, _field, _val) \ + (((_var) & ~(((1 << PHYTMAC_##_field##_WIDTH) - 1) \ + << PHYTMAC_##_field##_INDEX)) \ + | (((_val) & ((1 << PHYTMAC_##_field##_WIDTH) - 1)) \ + << PHYTMAC_##_field##_INDEX)) + +#define PHYTMAC_READ(_pdata, _reg) \ + __raw_readl((_pdata)->mac_regs + (_reg)) + +#define PHYTMAC_READ_BITS(_pdata, _reg, _field) \ + PHYTMAC_GET_BITS(PHYTMAC_READ((_pdata), _reg), _field) + +#define PHYTMAC_WRITE(_pdata, _reg, _val) \ + __raw_writel((_val), (_pdata)->mac_regs + (_reg)) + +#define PHYTMAC_MSG_READ(_pdata, _reg) \ + __raw_readl((_pdata)->mac_regs + (_reg)) + +#define PHYTMAC_WRITE(_pdata, _reg, _val) \ + __raw_writel((_val), (_pdata)->mac_regs + (_reg)) + +#define LSO_UFO 1 +#define LSO_TSO 2 + +#define PHYTMAC_INT_TX_COMPLETE 0x1 +#define PHYTMAC_INT_TX_ERR 0x2 +#define PHYTMAC_INT_RX_COMPLETE 0x4 +#define PHYTMAC_INT_RX_OVERRUN 0x8 +#define PHYTMAC_INT_RX_DESC_FULL 0x10 +#define PHYTMAC_RX_INT_FLAGS (PHYTMAC_INT_RX_COMPLETE \ + | PHYTMAC_INT_RX_OVERRUN) +#define PHYTMAC_TX_INT_FLAGS (PHYTMAC_INT_TX_COMPLETE \ + | PHYTMAC_INT_TX_ERR) + +#define PHYTMAC_WAKE_MAGIC 0x00000001 +#define PHYTMAC_WAKE_ARP 0x00000002 +#define PHYTMAC_WAKE_UCAST 0x00000004 +#define PHYTMAC_WAKE_MCAST 0x00000008 + +struct packet_info { + int lso; + int desc_cnt; + int hdrlen; + int nocrc; + u32 mss; + u32 seq; +}; + +#define DEV_TYPE_PLATFORM 0 +#define DEV_TYPE_PCI 1 + +struct phytmac_statistics { + char stat_string[ETH_GSTRING_LEN]; +}; + +#define STAT_TITLE(title) { \ + .stat_string = title, \ +} + +static const struct phytmac_statistics phytmac_statistics[] = { + STAT_TITLE("tx_octets"), + STAT_TITLE("tx_packets"), + STAT_TITLE("tx_bcast_packets"), + STAT_TITLE("tx_mcase_packets"), + STAT_TITLE("tx_pause_packets"), + STAT_TITLE("tx_64_byte_packets"), + STAT_TITLE("tx_65_127_byte_packets"), + STAT_TITLE("tx_128_255_byte_packets"), + STAT_TITLE("tx_256_511_byte_packets"), + STAT_TITLE("tx_512_1023_byte_packets"), + STAT_TITLE("tx_1024_1518_byte_packets"), + STAT_TITLE("tx_more_than_1518_byte_packets"), + STAT_TITLE("tx_underrun"), + STAT_TITLE("tx_single_collisions"), + STAT_TITLE("tx_multiple_collisions"), + STAT_TITLE("tx_excessive_collisions"), + STAT_TITLE("tx_late_collisions"), + STAT_TITLE("tx_deferred"), + STAT_TITLE("tx_carrier_sense_errors"), + STAT_TITLE("rx_octets"), + STAT_TITLE("rx_packets"), + STAT_TITLE("rx_bcast_packets"), + STAT_TITLE("rx_mcast_packets"), + STAT_TITLE("rx_pause_packets"), + STAT_TITLE("rx_64_byte_packets"), + STAT_TITLE("rx_65_127_byte_packets"), + STAT_TITLE("rx_128_255_byte_packets"), + STAT_TITLE("rx_256_511_byte_packets"), + STAT_TITLE("rx_512_1023_byte_packets"), + STAT_TITLE("rx_1024_1518_byte_packets"), + STAT_TITLE("rx_more_than_1518_byte_packets"), + STAT_TITLE("rx_undersized_packets"), + STAT_TITLE("rx_oversize_packets"), + STAT_TITLE("rx_jabbers"), + STAT_TITLE("rx_fcs_errors"), + STAT_TITLE("rx_length_errors"), + STAT_TITLE("rx_symbol_errors"), + STAT_TITLE("rx_alignment_errors"), + STAT_TITLE("rx_resource_over"), + STAT_TITLE("rx_overruns"), + STAT_TITLE("rx_iphdr_csum_errors"), + STAT_TITLE("rx_tcp_csum_errors"), + STAT_TITLE("rx_udp_csum_errors"), +}; + +#define PHYTMAC_STATS_LEN ARRAY_SIZE(phytmac_statistics) + +/* per queue statistics, each should be unsigned long type */ +struct phytmac_queue_stats { + unsigned long rx_packets; + unsigned long rx_bytes; + unsigned long rx_dropped; + unsigned long tx_packets; + unsigned long tx_bytes; + unsigned long tx_dropped; +}; + +static const struct phytmac_statistics queue_statistics[] = { + STAT_TITLE("rx_packets"), + STAT_TITLE("rx_bytes"), + STAT_TITLE("rx_dropped"), + STAT_TITLE("tx_packets"), + STAT_TITLE("tx_bytes"), + STAT_TITLE("tx_dropped"), +}; + +#define QUEUE_STATS_LEN ARRAY_SIZE(queue_statistics) + +struct phytmac_config { + struct phytmac_hw_if *hw_if; + u32 caps; + u32 tsu_rate; + u16 queue_num; +}; + +struct phytmac_stats { + u64 tx_octets; + u64 tx_packets; + u64 tx_bcast_packets; + u64 tx_mcase_packets; + u64 tx_pause_packets; + u64 tx_64_byte_packets; + u64 tx_65_127_byte_packets; + u64 tx_128_255_byte_packets; + u64 tx_256_511_byte_packets; + u64 tx_512_1023_byte_packets; + u64 tx_1024_1518_byte_packets; + u64 tx_more_than_1518_byte_packets; + u64 tx_underrun; + u64 tx_single_collisions; + u64 tx_multiple_collisions; + u64 tx_excessive_collisions; + u64 tx_late_collisions; + u64 tx_deferred; + u64 tx_carrier_sense_errors; + u64 rx_octets; + u64 rx_packets; + u64 rx_bcast_packets; + u64 rx_mcast_packets; + u64 rx_pause_packets; + u64 rx_64_byte_packets; + u64 rx_65_127_byte_packets; + u64 rx_128_255_byte_packets; + u64 rx_256_511_byte_packets; + u64 rx_512_1023_byte_packets; + u64 rx_1024_1518_byte_packets; + u64 rx_more_than_1518_byte_packets; + u64 rx_undersized_packets; + u64 rx_oversize_packets; + u64 rx_jabbers; + u64 rx_fcs_errors; + u64 rx_length_errors; + u64 rx_symbol_errors; + u64 rx_alignment_errors; + u64 rx_resource_over; + u64 rx_overruns; + u64 rx_iphdr_csum_errors; + u64 rx_tcp_csum_errors; + u64 rx_udp_csum_errors; +}; + +struct ts_incr { + u32 sub_ns; + u32 ns; +}; + +enum phytmac_bd_control { + TS_DISABLED, + TS_FRAME_PTP_EVENT_ONLY, + TS_ALL_PTP_FRAMES, + TS_ALL_FRAMES, +}; + +#ifdef CONFIG_PHYTMAC_ENABLE_PTP +struct phytmac_dma_desc { + u32 desc0; + u32 desc1; + u32 desc2; + u32 desc3; + u32 desc4; + u32 desc5; +}; +#else +struct phytmac_dma_desc { + u32 desc0; + u32 desc1; + u32 desc2; + u32 desc3; +}; +#endif + +struct phytmac_tx_skb { + struct sk_buff *skb; + dma_addr_t addr; + size_t length; + bool mapped_as_page; +}; + +struct phytmac_tx_ts { + struct sk_buff *skb; + u32 ts_1; + u32 ts_2; +}; + +struct phytmac_queue { + struct phytmac *pdata; + int irq; + int index; + + /* tx queue info */ + unsigned int tx_head; + unsigned int tx_tail; + unsigned int tx_xmit_more; + dma_addr_t tx_ring_addr; + struct work_struct tx_error_task; + struct napi_struct tx_napi; + struct phytmac_dma_desc *tx_ring; + struct phytmac_tx_skb *tx_skb; + /* Lock to protect tx */ + spinlock_t tx_lock; + + /* rx queue info */ + dma_addr_t rx_ring_addr; + unsigned int rx_head; + unsigned int rx_tail; + struct phytmac_dma_desc *rx_ring; + struct sk_buff **rx_skb; + struct napi_struct rx_napi; + struct phytmac_queue_stats stats; + +#ifdef CONFIG_PHYTMAC_ENABLE_PTP + struct work_struct tx_ts_task; + unsigned int tx_ts_head; + unsigned int tx_ts_tail; + struct phytmac_tx_ts tx_timestamps[128]; +#endif +}; + +struct ethtool_rx_fs_item { + struct ethtool_rx_flow_spec fs; + struct list_head list; +}; + +struct ethtool_rx_fs_list { + struct list_head list; + unsigned int count; +}; + +struct phytmac_msg { + struct completion tx_msg_comp; + u32 tx_msg_ring_size; + u32 rx_msg_ring_size; + u32 tx_msg_head; + u32 tx_msg_tail; + u32 rx_msg_head; + u32 rx_msg_tail; + /* Lock to protect msg */ + spinlock_t msg_lock; +}; + +struct ts_ctrl { + int tx_control; + int rx_control; + int one_step; +}; + +struct phytmac { + void __iomem *mac_regs; + void __iomem *msg_regs; + void __iomem *mhu_regs; + struct pci_dev *pcidev; + struct platform_device *platdev; + struct net_device *ndev; + struct device *dev; + struct ncsi_dev *ncsidev; + struct fwnode_handle *fwnode; + struct phytmac_hw_if *hw_if; + struct phytmac_msg msg_ring; + int dev_type; + int sfp_irq; + int irq_type; + int queue_irq[PHYTMAC_MAX_QUEUES]; + u32 rx_irq_mask; + u32 tx_irq_mask; + u32 msg_enable; + u32 capacities; + u32 max_tx_length; + u32 min_tx_length; + u32 jumbo_len; + u32 wol; + u32 lpi; + u32 power_state; + struct work_struct restart_task; + /* Lock to protect mac config */ + spinlock_t lock; + /* Lock to protect msg tx */ + spinlock_t msg_lock; + u32 rx_ring_size; + u32 tx_ring_size; + u32 dma_data_width; + u32 dma_addr_width; + u32 dma_burst_length; + int rx_bd_prefetch; + int tx_bd_prefetch; + int rx_buffer_len; + u16 queues_max_num; + u16 queues_num; + struct phytmac_queue queues[PHYTMAC_MAX_QUEUES]; + struct phytmac_stats stats; + u64 ethtool_stats[PHYTMAC_STATS_LEN + + QUEUE_STATS_LEN * PHYTMAC_MAX_QUEUES]; + int use_ncsi; + int use_mii; + struct mii_bus *mii_bus; + struct phylink *phylink; + struct phylink_config phylink_config; + struct phylink_pcs phylink_pcs; + int pause; + phy_interface_t phy_interface; + int speed; + int duplex; + int autoneg; + /* 1588 */ + spinlock_t ts_clk_lock; /* clock locking */ + unsigned int ts_rate; + struct ptp_clock *ptp_clock; + struct ptp_clock_info ptp_clock_info; + struct ts_incr ts_incr; + struct hwtstamp_config ts_config; + struct ts_ctrl ts_ctrl; + /* RX queue filer rule set */ + struct ethtool_rx_fs_list rx_fs_list; + /* Lock to protect fs */ + spinlock_t rx_fs_lock; + unsigned int max_rx_fs; +}; + +struct phytmac_hw_if { + int (*init_msg_ring)(struct phytmac *pdata); + int (*init_hw)(struct phytmac *pdata); + void (*reset_hw)(struct phytmac *pdata); + int (*init_ring_hw)(struct phytmac *pdata); + int (*poweron)(struct phytmac *pdata, int on); + int (*set_wol)(struct phytmac *pdata, int wake); + int (*get_feature)(struct phytmac *pdata); + int (*set_mac_address)(struct phytmac *pdata, const u8 *addr); + int (*get_mac_address)(struct phytmac *pdata, u8 *addr); + int (*enable_promise)(struct phytmac *pdata, int enable); + int (*enable_multicast)(struct phytmac *pdata, int enable); + int (*set_hash_table)(struct phytmac *pdata, unsigned long *mc_filter); + int (*enable_rx_csum)(struct phytmac *pdata, int enable); + int (*enable_tx_csum)(struct phytmac *pdata, int enable); + int (*enable_pause)(struct phytmac *pdata, int enable); + int (*enable_autoneg)(struct phytmac *pdata, int enable); + int (*enable_network)(struct phytmac *pdata, int enable, int rx_tx); + void (*get_stats)(struct phytmac *pdata); + void (*get_regs)(struct phytmac *pdata, u32 *reg_buff); + int (*set_speed)(struct phytmac *pdata); + + void (*mac_config)(struct phytmac *pdata, u32 mode, + const struct phylink_link_state *state); + int (*mac_linkup)(struct phytmac *pdata, phy_interface_t interface, + int speed, int duplex); + int (*mac_linkdown)(struct phytmac *pdata); + int (*pcs_linkup)(struct phytmac *pdata, phy_interface_t interface, + int speed, int duplex); + int (*pcs_linkdown)(struct phytmac *pdata); + unsigned int (*get_link)(struct phytmac *pdata, phy_interface_t interface); + + /* For RX coalescing */ + int (*config_rx_coalesce)(struct phytmac *pdata); + int (*config_tx_coalesce)(struct phytmac *pdata); + + /* ethtool nfc */ + int (*add_fdir_entry)(struct phytmac *pdata, struct ethtool_rx_flow_spec *rx_flow); + int (*del_fdir_entry)(struct phytmac *pdata, struct ethtool_rx_flow_spec *rx_flow); + + /* mido ops */ + int (*enable_mdio_control)(struct phytmac *pdata, int enable); + int (*mdio_read)(struct phytmac *pdata, int mii_id, int regnum, int is_c45); + int (*mdio_write)(struct phytmac *pdata, int mii_id, + int regnum, int is_c45, u16 data); + void (*enable_irq)(struct phytmac *pdata, int queue_index, u32 mask); + void (*disable_irq)(struct phytmac *pdata, int queue_index, u32 mask); + void (*clear_irq)(struct phytmac *pdata, int queue_index, u32 mask); + void (*mask_irq)(struct phytmac *pdata, int queue_index, u32 mask); + unsigned int (*get_irq)(struct phytmac *pdata, int queue_index); + unsigned int (*get_intx_mask)(struct phytmac *pdata); + unsigned int (*tx_map)(struct phytmac_queue *pdata, u32 tx_tail, + struct packet_info *packet); + void (*init_rx_map)(struct phytmac_queue *queue, u32 index); + unsigned int (*rx_map)(struct phytmac_queue *pdata, u32 index, dma_addr_t addr); + void (*transmit)(struct phytmac_queue *queue); + void (*restart)(struct phytmac *pdata); + int (*tx_complete)(const struct phytmac_dma_desc *desc); + int (*rx_complete)(const struct phytmac_dma_desc *desc); + int (*get_rx_pkt_len)(struct phytmac *pdata, const struct phytmac_dma_desc *desc); + dma_addr_t (*get_desc_addr)(const struct phytmac_dma_desc *desc); + bool (*rx_checksum)(const struct phytmac_dma_desc *desc); + void (*set_desc_rxused)(struct phytmac_dma_desc *desc); + bool (*rx_single_buffer)(const struct phytmac_dma_desc *desc); + bool (*rx_pkt_start)(const struct phytmac_dma_desc *desc); + bool (*rx_pkt_end)(const struct phytmac_dma_desc *desc); + void (*clear_rx_desc)(struct phytmac_queue *queue, int begin, int end); + void (*clear_tx_desc)(struct phytmac_queue *queue); + /* ptp */ + void (*init_ts_hw)(struct phytmac *pdata); + void (*get_time)(struct phytmac *pdata, struct timespec64 *ts); + void (*set_time)(struct phytmac *pdata, time64_t sec, long nsec); + int (*set_ts_config)(struct phytmac *pdata, struct ts_ctrl *ctrl); + void (*clear_time)(struct phytmac *pdata); + int (*set_incr)(struct phytmac *pdata, struct ts_incr *incr); + int (*adjust_fine)(struct phytmac *pdata, long ppm, bool negative); + int (*adjust_time)(struct phytmac *pdata, s64 delta, int neg); + int (*ts_valid)(struct phytmac *pdata, struct phytmac_dma_desc *desc, + int direction); + void (*get_timestamp)(struct phytmac *pdata, u32 ts_1, u32 ts_2, + struct timespec64 *ts); + unsigned int (*get_ts_rate)(struct phytmac *pdata); +}; + +/* mhu */ +#define PHYTMAC_MHU_AP_CPP_STAT 0x00 +#define PHYTMAC_MHU_AP_CPP_SET 0x04 +#define PHYTMAC_MHU_CPP_DATA0 0x18 +#define PHYTMAC_MHU_CPP_DATA1 0x1c + +#define PHYTMAC_MHU_STAT_BUSY_INDEX 0 +#define PHYTMAC_MHU_STAT_BUSY_WIDTH 1 + +#define PHYTMAC_MHU_SET_INDEX 0 +#define PHYTMAC_MHU_SET_WIDTH 1 + +#define PHYTMAC_DATA0_FREE_INDEX 0 +#define PHYTMAC_DATA0_FREE_WIDTH 1 +#define PHYTMAC_DATA0_DOMAIN_INDEX 1 +#define PHYTMAC_DATA0_DOMAIN_WIDTH 7 +#define PHYTMAC_DATA0_MSG_INDEX 8 +#define PHYTMAC_DATA0_MSG_WIDTH 8 +#define PHYTMAC_MSG_PM 0x04 +#define PHYTMAC_DATA0_PRO_INDEX 16 +#define PHYTMAC_DATA0_PRO_WIDTH 8 +#define PHYTMAC_PRO_ID 0x11 +#define PHYTMAC_DATA0_PAYLOAD_INDEX 24 +#define PHYTMAC_DATA0_PAYLOAD_WIDTH 8 + +#define PHYTMAC_DATA1_STAT_INDEX 0 +#define PHYTMAC_DATA1_STAT_WIDTH 28 +#define PHYTMAC_STATON 8 +#define PHYTMAC_STATOFF 0 +#define PHYTMAC_DATA1_MUST0_INDEX 28 +#define PHYTMAC_DATA1_MUST0_WIDTH 2 +#define PHYTMAC_DATA1_STATTYPE_INDEX 30 +#define PHYTMAC_DATA1_STATTYPE_WIDTH 1 +#define PHYTMAC_STATTYPE 0x1 +#define PHYTMAC_DATA1_MUST1_INDEX 31 +#define PHYTMAC_DATA1_MUST1_WIDTH 1 + +#define PHYTMAC_MHU_READ(_pdata, _reg) \ + __raw_readl((_pdata)->mhu_regs + (_reg)) +#define PHYTMAC_MHU_WRITE(_pdata, _reg, _val) \ + __raw_writel((_val), (_pdata)->mhu_regs + (_reg)) +#define PHYTMAC_READ_STAT(pdata) PHYTMAC_MHU_READ(pdata, PHYTMAC_MHU_AP_CPP_STAT) +#define PHYTMAC_READ_DATA0(pdata) PHYTMAC_MHU_READ(pdata, PHYTMAC_MHU_CPP_DATA0) +#define PHYTMAC_TIMEOUT 1000000000 /* in usecs */ + +struct phytmac_tx_skb *phytmac_get_tx_skb(struct phytmac_queue *queue, + unsigned int index); +struct phytmac_dma_desc *phytmac_get_tx_desc(struct phytmac_queue *queue, + unsigned int index); +struct phytmac_dma_desc *phytmac_get_rx_desc(struct phytmac_queue *queue, + unsigned int index); +void phytmac_set_ethtool_ops(struct net_device *netdev); +int phytmac_drv_probe(struct phytmac *pdata); +int phytmac_drv_remove(struct phytmac *pdata); +int phytmac_drv_suspend(struct phytmac *pdata); +int phytmac_drv_resume(struct phytmac *pdata); +struct phytmac *phytmac_alloc_pdata(struct device *dev); +void phytmac_free_pdata(struct phytmac *pdata); +int phytmac_reset_ringsize(struct phytmac *pdata, u32 rx_size, u32 tx_size); +#endif diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c new file mode 100644 index 0000000000000000000000000000000000000000..ff67da68822afe08222832d0cec27543d321b86c --- /dev/null +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -0,0 +1,532 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include "phytmac.h" +#include "phytmac_v1.h" +#include "phytmac_v2.h" +#include "phytmac_ptp.h" + +static void phytmac_get_ethtool_stats(struct net_device *ndev, + struct ethtool_stats *stats, + u64 *data) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct phytmac_hw_if *hw_if = pdata->hw_if; + int i = PHYTMAC_STATS_LEN, j; + int q; + struct phytmac_queue *queue; + unsigned long *stat; + + hw_if->get_stats(pdata); + + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) + for (j = 0, stat = &queue->stats.rx_packets; j < QUEUE_STATS_LEN; ++j, ++stat) + pdata->ethtool_stats[i++] = *stat; + + memcpy(data, &pdata->ethtool_stats, sizeof(u64) + * (PHYTMAC_STATS_LEN + QUEUE_STATS_LEN * pdata->queues_num)); +} + +static inline int phytmac_get_sset_count(struct net_device *ndev, int sset) +{ + struct phytmac *pdata = netdev_priv(ndev); + + switch (sset) { + case ETH_SS_STATS: + return PHYTMAC_STATS_LEN + QUEUE_STATS_LEN * pdata->queues_num; + default: + return -EOPNOTSUPP; + } +} + +static void phytmac_get_ethtool_strings(struct net_device *ndev, u32 sset, u8 *p) +{ + char stat_string[ETH_GSTRING_LEN]; + struct phytmac *pdata = netdev_priv(ndev); + struct phytmac_queue *queue; + unsigned int i; + unsigned int q; + + switch (sset) { + case ETH_SS_STATS: + for (i = 0; i < PHYTMAC_STATS_LEN; i++, p += ETH_GSTRING_LEN) + memcpy(p, phytmac_statistics[i].stat_string, + ETH_GSTRING_LEN); + + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { + for (i = 0; i < QUEUE_STATS_LEN; i++, p += ETH_GSTRING_LEN) { + snprintf(stat_string, ETH_GSTRING_LEN, "q%d_%s", + q, queue_statistics[i].stat_string); + memcpy(p, stat_string, ETH_GSTRING_LEN); + } + } + break; + } +} + +static inline int phytmac_get_regs_len(struct net_device *ndev) +{ + return PHYTMAC_GREGS_LEN; +} + +static void phytmac_get_regs(struct net_device *ndev, + struct ethtool_regs *regs, + void *p) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct phytmac_hw_if *hw_if = pdata->hw_if; + u32 *regs_buff = p; + + memset(p, 0, PHYTMAC_GREGS_LEN * sizeof(u32)); + + hw_if->get_regs(pdata, regs_buff); +} + +static void phytmac_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) +{ + struct phytmac *pdata = netdev_priv(ndev); + + wol->supported = WAKE_MAGIC | WAKE_UCAST | WAKE_ARP | WAKE_MCAST; + + if (pdata->wol & PHYTMAC_WAKE_MAGIC) + wol->wolopts |= WAKE_MAGIC; + if (pdata->wol & PHYTMAC_WAKE_ARP) + wol->wolopts |= WAKE_ARP; + if (pdata->wol & PHYTMAC_WAKE_UCAST) + wol->wolopts |= WAKE_UCAST; + if (pdata->wol & PHYTMAC_WAKE_MCAST) + wol->wolopts |= WAKE_MCAST; +} + +static int phytmac_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) +{ + struct phytmac *pdata = netdev_priv(ndev); + int ret; + + ret = phylink_ethtool_set_wol(pdata->phylink, wol); + + if (!ret || ret != -EOPNOTSUPP) + return ret; + + pdata->wol = 0; + + if (wol->wolopts & WAKE_MAGIC) + pdata->wol |= PHYTMAC_WAKE_MAGIC; + if (wol->wolopts & WAKE_ARP) + pdata->wol |= PHYTMAC_WAKE_ARP; + if (wol->wolopts & WAKE_UCAST) + pdata->wol |= PHYTMAC_WAKE_UCAST; + if (wol->wolopts & WAKE_MCAST) + pdata->wol |= PHYTMAC_WAKE_MCAST; + + device_set_wakeup_enable(pdata->dev, pdata->wol ? 1 : 0); + + return 0; +} + +static void phytmac_get_ringparam(struct net_device *ndev, + struct ethtool_ringparam *ring) +{ + struct phytmac *pdata = netdev_priv(ndev); + + ring->rx_max_pending = MAX_RX_RING_SIZE; + ring->tx_max_pending = MAX_TX_RING_SIZE; + + ring->rx_pending = pdata->rx_ring_size; + ring->tx_pending = pdata->tx_ring_size; +} + +static int phytmac_set_ringparam(struct net_device *ndev, + struct ethtool_ringparam *ring) +{ + struct phytmac *pdata = netdev_priv(ndev); + u32 new_rx_size, new_tx_size; + + if (ring->rx_mini_pending || ring->rx_jumbo_pending) + return -EINVAL; + + new_rx_size = clamp_t(u32, ring->rx_pending, + MIN_RX_RING_SIZE, MAX_RX_RING_SIZE); + new_rx_size = roundup_pow_of_two(new_rx_size); + + new_tx_size = clamp_t(u32, ring->tx_pending, + MIN_TX_RING_SIZE, MAX_TX_RING_SIZE); + new_tx_size = roundup_pow_of_two(new_tx_size); + + if (EQUAL(new_tx_size, pdata->tx_ring_size) && + EQUAL(new_rx_size, pdata->rx_ring_size)) { + /* nothing to do */ + return 0; + } + + return phytmac_reset_ringsize(pdata, new_rx_size, new_tx_size); +} + +static int phytmac_get_ts_info(struct net_device *ndev, + struct ethtool_ts_info *info) +{ + struct phytmac *pdata = netdev_priv(ndev); + + if (IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) { + 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; + info->tx_types = + (1 << HWTSTAMP_TX_ONESTEP_SYNC) | + (1 << HWTSTAMP_TX_OFF) | + (1 << HWTSTAMP_TX_ON); + info->rx_filters = + (1 << HWTSTAMP_FILTER_NONE) | + (1 << HWTSTAMP_FILTER_ALL); + + info->phc_index = pdata->ptp_clock ? ptp_clock_index(pdata->ptp_clock) : -1; + + return 0; + } + + return ethtool_op_get_ts_info(ndev, info); +} + +static int phytmac_add_fdir_ethtool(struct net_device *ndev, + struct ethtool_rxnfc *cmd) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct ethtool_rx_flow_spec *fs = &cmd->fs; + struct ethtool_rx_fs_item *item, *newfs; + unsigned long flags; + int ret = -EINVAL; + bool added = false; + struct phytmac_hw_if *hw_if = pdata->hw_if; + + if (cmd->fs.location >= pdata->max_rx_fs || + cmd->fs.ring_cookie >= pdata->queues_num) { + return -EINVAL; + } + + newfs = kmalloc(sizeof(*newfs), GFP_KERNEL); + if (!newfs) + return -ENOMEM; + memcpy(&newfs->fs, fs, sizeof(newfs->fs)); + + netdev_dbg(ndev, "Adding flow filter entry,type=%u,queue=%u,loc=%u,src=%08X,dst=%08X,ps=%u,pd=%u\n", + fs->flow_type, (int)fs->ring_cookie, fs->location, + htonl(fs->h_u.tcp_ip4_spec.ip4src), + htonl(fs->h_u.tcp_ip4_spec.ip4dst), + htons(fs->h_u.tcp_ip4_spec.psrc), htons(fs->h_u.tcp_ip4_spec.pdst)); + + spin_lock_irqsave(&pdata->rx_fs_lock, flags); + + /* find correct place to add in list */ + list_for_each_entry(item, &pdata->rx_fs_list.list, list) { + if (item->fs.location > newfs->fs.location) { + list_add_tail(&newfs->list, &item->list); + added = true; + break; + } else if (item->fs.location == fs->location) { + netdev_err(ndev, "Rule not added: location %d not free!\n", + fs->location); + ret = -EBUSY; + goto err; + } + } + if (!added) + list_add_tail(&newfs->list, &pdata->rx_fs_list.list); + + hw_if->add_fdir_entry(pdata, fs); + pdata->rx_fs_list.count++; + + spin_unlock_irqrestore(&pdata->rx_fs_lock, flags); + return 0; + +err: + spin_unlock_irqrestore(&pdata->rx_fs_lock, flags); + kfree(newfs); + return ret; +} + +static int phytmac_del_fdir_ethtool(struct net_device *ndev, + struct ethtool_rxnfc *cmd) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct ethtool_rx_fs_item *item; + struct ethtool_rx_flow_spec *fs; + unsigned long flags; + struct phytmac_hw_if *hw_if = pdata->hw_if; + + spin_lock_irqsave(&pdata->rx_fs_lock, flags); + + list_for_each_entry(item, &pdata->rx_fs_list.list, list) { + if (item->fs.location == cmd->fs.location) { + /* disable screener regs for the flow entry */ + fs = &item->fs; + netdev_dbg(ndev, "Deleting flow filter entry,type=%u,queue=%u,loc=%u,src=%08X,dst=%08X,ps=%u,pd=%u\n", + fs->flow_type, (int)fs->ring_cookie, fs->location, + htonl(fs->h_u.tcp_ip4_spec.ip4src), + htonl(fs->h_u.tcp_ip4_spec.ip4dst), + htons(fs->h_u.tcp_ip4_spec.psrc), + htons(fs->h_u.tcp_ip4_spec.pdst)); + + hw_if->del_fdir_entry(pdata, fs); + + list_del(&item->list); + pdata->rx_fs_list.count--; + spin_unlock_irqrestore(&pdata->rx_fs_lock, flags); + kfree(item); + return 0; + } + } + + spin_unlock_irqrestore(&pdata->rx_fs_lock, flags); + return -EINVAL; +} + +static int phytmac_get_fdir_entry(struct net_device *ndev, + struct ethtool_rxnfc *cmd) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct ethtool_rx_fs_item *item; + + list_for_each_entry(item, &pdata->rx_fs_list.list, list) { + if (item->fs.location == cmd->fs.location) { + memcpy(&cmd->fs, &item->fs, sizeof(cmd->fs)); + return 0; + } + } + return -EINVAL; +} + +static int phytmac_get_all_fdir_entries(struct net_device *ndev, + struct ethtool_rxnfc *cmd, + u32 *rule_locs) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct ethtool_rx_fs_item *item; + u32 cnt = 0; + + list_for_each_entry(item, &pdata->rx_fs_list.list, list) { + if (cnt == cmd->rule_cnt) + return -EMSGSIZE; + rule_locs[cnt] = item->fs.location; + cnt++; + } + cmd->data = pdata->max_rx_fs; + cmd->rule_cnt = cnt; + + return 0; +} + +static int phytmac_get_rxnfc(struct net_device *ndev, + struct ethtool_rxnfc *cmd, + u32 *rule_locs) +{ + struct phytmac *pdata = netdev_priv(ndev); + int ret = 0; + + switch (cmd->cmd) { + case ETHTOOL_GRXRINGS: + cmd->data = pdata->queues_num; + break; + case ETHTOOL_GRXCLSRLCNT: + cmd->rule_cnt = pdata->rx_fs_list.count; + break; + case ETHTOOL_GRXCLSRULE: + ret = phytmac_get_fdir_entry(ndev, cmd); + break; + case ETHTOOL_GRXCLSRLALL: + ret = phytmac_get_all_fdir_entries(ndev, cmd, rule_locs); + break; + default: + netdev_err(ndev, "Command parameter %d is not supported\n", cmd->cmd); + ret = -EOPNOTSUPP; + } + + return ret; +} + +static int phytmac_set_rxnfc(struct net_device *ndev, struct ethtool_rxnfc *cmd) +{ + switch (cmd->cmd) { + case ETHTOOL_SRXCLSRLINS: + return phytmac_add_fdir_ethtool(ndev, cmd); + case ETHTOOL_SRXCLSRLDEL: + return phytmac_del_fdir_ethtool(ndev, cmd); + default: + netdev_err(ndev, "Command parameter %d is not supported\n", cmd->cmd); + return -EOPNOTSUPP; + } +} + +static int phytmac_get_link_ksettings(struct net_device *ndev, + struct ethtool_link_ksettings *kset) +{ + int ret = 0; + struct phytmac *pdata = netdev_priv(ndev); + u32 supported = 0; + u32 advertising = 0; + + if (!ndev->phydev) { + if (pdata->phy_interface == PHY_INTERFACE_MODE_USXGMII || + pdata->phy_interface == PHY_INTERFACE_MODE_10GBASER) { + supported = SUPPORTED_10000baseT_Full + | SUPPORTED_FIBRE | SUPPORTED_Pause; + advertising = ADVERTISED_10000baseT_Full + | ADVERTISED_FIBRE | ADVERTISED_Pause; + kset->base.port = PORT_FIBRE; + kset->base.transceiver = XCVR_INTERNAL; + kset->base.duplex = DUPLEX_FULL; + kset->base.speed = SPEED_10000; + } else if (pdata->phy_interface == PHY_INTERFACE_MODE_2500BASEX) { + supported = SUPPORTED_2500baseX_Full | SUPPORTED_Pause; + advertising = SUPPORTED_2500baseX_Full | ADVERTISED_Pause; + kset->base.port = PORT_FIBRE; + kset->base.transceiver = XCVR_INTERNAL; + kset->base.duplex = DUPLEX_FULL; + kset->base.speed = SPEED_2500; + } else if (pdata->phy_interface == PHY_INTERFACE_MODE_1000BASEX) { + supported = SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full + | SUPPORTED_10baseT_Full | SUPPORTED_FIBRE + | SUPPORTED_Pause; + advertising = ADVERTISED_1000baseT_Full | ADVERTISED_100baseT_Full + | ADVERTISED_10baseT_Full | ADVERTISED_FIBRE + | ADVERTISED_Pause; + kset->base.port = PORT_FIBRE; + kset->base.transceiver = XCVR_INTERNAL; + kset->base.duplex = DUPLEX_FULL; + kset->base.speed = SPEED_100; + } else if (pdata->phy_interface == PHY_INTERFACE_MODE_SGMII) { + supported = SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full + | SUPPORTED_10baseT_Full | SUPPORTED_FIBRE | SUPPORTED_Pause; + advertising = ADVERTISED_1000baseT_Full | ADVERTISED_100baseT_Full + | ADVERTISED_10baseT_Full | ADVERTISED_FIBRE | ADVERTISED_Pause; + kset->base.port = PORT_FIBRE; + kset->base.transceiver = XCVR_INTERNAL; + kset->base.duplex = DUPLEX_FULL; + kset->base.speed = SPEED_1000; + } + + ethtool_convert_legacy_u32_to_link_mode(kset->link_modes.supported, + supported); + ethtool_convert_legacy_u32_to_link_mode(kset->link_modes.advertising, + advertising); + } else { + phy_ethtool_get_link_ksettings(ndev, kset); + } + + return ret; +} + +static int phytmac_set_link_ksettings(struct net_device *ndev, + const struct ethtool_link_ksettings *kset) +{ + int ret = 0; + + if (!ndev->phydev) { + netdev_err(ndev, "fixed link interface not supported set link\n"); + ret = -EOPNOTSUPP; + } else { + phy_ethtool_set_link_ksettings(ndev, kset); + } + + return ret; +} + +static inline void phytmac_get_pauseparam(struct net_device *ndev, + struct ethtool_pauseparam *pause) +{ + struct phytmac *pdata = netdev_priv(ndev); + + pause->rx_pause = pdata->pause; + pause->tx_pause = pdata->pause; +} + +static int phytmac_set_pauseparam(struct net_device *ndev, + struct ethtool_pauseparam *pause) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct phytmac_hw_if *hw_if = pdata->hw_if; + + if (pause->rx_pause != pdata->pause) + hw_if->enable_pause(pdata, pause->rx_pause); + + pdata->pause = pause->rx_pause; + + return 0; +} + +static inline void phytmac_get_channels(struct net_device *ndev, + struct ethtool_channels *ch) +{ + struct phytmac *pdata = netdev_priv(ndev); + + ch->max_combined = pdata->queues_max_num; + ch->combined_count = pdata->queues_num; +} + +static int phytmac_set_channels(struct net_device *ndev, + struct ethtool_channels *ch) +{ + struct phytmac *pdata = netdev_priv(ndev); + + if (netif_running(ndev)) + return -EBUSY; + + if (ch->combined_count > pdata->queues_max_num) { + netdev_err(ndev, "combined channel count cannot exceed %u\n", + ch->combined_count); + + return -EINVAL; + } + + pdata->queues_num = ch->combined_count; + + return 0; +} + +static inline u32 phytmac_get_msglevel(struct net_device *ndev) +{ + struct phytmac *pdata = netdev_priv(ndev); + + return pdata->msg_enable; +} + +static inline void phytmac_set_msglevel(struct net_device *ndev, u32 level) +{ + struct phytmac *pdata = netdev_priv(ndev); + + pdata->msg_enable = level; +} + +static const struct ethtool_ops phytmac_ethtool_ops = { + .get_regs_len = phytmac_get_regs_len, + .get_regs = phytmac_get_regs, + .get_msglevel = phytmac_get_msglevel, + .set_msglevel = phytmac_set_msglevel, + .get_link = ethtool_op_get_link, + .get_ts_info = phytmac_get_ts_info, + .get_ethtool_stats = phytmac_get_ethtool_stats, + .get_strings = phytmac_get_ethtool_strings, + .get_sset_count = phytmac_get_sset_count, + .get_link_ksettings = phytmac_get_link_ksettings, + .set_link_ksettings = phytmac_set_link_ksettings, + .get_ringparam = phytmac_get_ringparam, + .set_ringparam = phytmac_set_ringparam, + .get_rxnfc = phytmac_get_rxnfc, + .set_rxnfc = phytmac_set_rxnfc, + .get_pauseparam = phytmac_get_pauseparam, + .set_pauseparam = phytmac_set_pauseparam, + .get_channels = phytmac_get_channels, + .set_channels = phytmac_set_channels, + .get_wol = phytmac_get_wol, + .set_wol = phytmac_set_wol, +}; + +void phytmac_set_ethtool_ops(struct net_device *ndev) +{ + ndev->ethtool_ops = &phytmac_ethtool_ops; +} + diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c new file mode 100644 index 0000000000000000000000000000000000000000..164546d0690268776b6a94e83d288051aaa7b035 --- /dev/null +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -0,0 +1,2138 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Phytium Ethernet Controller driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "phytmac.h" +#include "phytmac_ptp.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); + +#define RX_BUFFER_MULTIPLE 64 /* bytes */ +#define MAX_MTU 3072 + +#define RX_RING_BYTES(pdata) (sizeof(struct phytmac_dma_desc) \ + * (pdata)->rx_ring_size) + +#define TX_RING_BYTES(pdata) (sizeof(struct phytmac_dma_desc)\ + * (pdata)->tx_ring_size) + +/* Max length of transmit frame must be a multiple of 8 bytes */ +#define PHYTMAC_TX_LEN_ALIGN 8 +/* Limit maximum TX length as per Cadence TSO errata. This is to avoid a + * false amba_error in TX path from the DMA assuming there is not enough + * space in the SRAM (16KB) even when there is. + */ + +static int phytmac_change_mtu(struct net_device *ndev, int new_mtu) +{ + if (netif_running(ndev)) + return -EBUSY; + + if (new_mtu > MAX_MTU) { + netdev_info(ndev, "Can not set MTU over %d.\n", MAX_MTU); + return -EINVAL; + } + + ndev->mtu = new_mtu; + + return 0; +} + +static inline void phytmac_hw_addr_set(struct net_device *dev, const u8 *addr) +{ + memcpy(dev->dev_addr, addr, ETH_ALEN); +} + +static int phytmac_set_mac_address(struct net_device *netdev, void *addr) +{ + struct phytmac *pdata = netdev_priv(netdev); + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct sockaddr *saddr = addr; + + if (netif_msg_drv(pdata)) + netdev_info(netdev, "phytmac set mac address"); + + if (!is_valid_ether_addr(saddr->sa_data)) + return -EADDRNOTAVAIL; + + phytmac_hw_addr_set(netdev, saddr->sa_data); + + hw_if->set_mac_address(pdata, saddr->sa_data); + + return 0; +} + +static int phytmac_get_mac_address(struct phytmac *pdata) +{ + struct phytmac_hw_if *hw_if = pdata->hw_if; + u8 addr[6]; + + hw_if->get_mac_address(pdata, addr); + + if (is_valid_ether_addr(addr)) { + phytmac_hw_addr_set(pdata->ndev, addr); + return 0; + } + dev_info(pdata->dev, "invalid hw address, using random\n"); + eth_hw_addr_random(pdata->ndev); + + return 0; +} + +static int phytmac_mdio_read(struct mii_bus *bus, int mii_id, int regnum) +{ + struct phytmac *pdata = bus->priv; + struct phytmac_hw_if *hw_if = pdata->hw_if; + int data; + + data = hw_if->mdio_read(pdata, mii_id, regnum, regnum & MII_ADDR_C45); + + if (netif_msg_link(pdata)) + netdev_info(pdata->ndev, "mdio read mii_id=%d, regnum=%x, data=%x\n", + mii_id, regnum, data); + + return data; +} + +static int phytmac_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 data) +{ + struct phytmac *pdata = bus->priv; + struct phytmac_hw_if *hw_if = pdata->hw_if; + int ret; + + ret = hw_if->mdio_write(pdata, mii_id, regnum, regnum & MII_ADDR_C45, data); + if (ret) + netdev_err(pdata->ndev, "mdio %d, reg %x, data %x write failed!\n", + mii_id, regnum, data); + + if (netif_msg_link(pdata)) + netdev_info(pdata->ndev, "mdio write mii_id=%d, regnum=%x, data=%x\n", + mii_id, regnum, data); + + return 0; +} + +static inline int hash_bit_value(int bitnr, __u8 *addr) +{ + if (addr[bitnr / 8] & (1 << (bitnr % 8))) + return 1; + return 0; +} + +/* Return the hash index value for the specified address. */ +static int phytmac_get_hash_index(__u8 *addr) +{ + int i, j, bitval; + int hash_index = 0; + + for (j = 0; j < 6; j++) { + for (i = 0, bitval = 0; i < 8; i++) + bitval ^= hash_bit_value(i * 6 + j, addr); + + hash_index |= (bitval << j); + } + + return hash_index; +} + +static void phytmac_set_mac_hash_table(struct net_device *ndev) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct netdev_hw_addr *ha; + unsigned long mc_filter[2]; + unsigned int bitnr; + + mc_filter[0] = 0; + mc_filter[1] = 0; + + netdev_for_each_mc_addr(ha, ndev) { + bitnr = phytmac_get_hash_index(ha->addr); + mc_filter[bitnr >> 5] |= 1 << (bitnr & 31); + } + + hw_if->set_hash_table(pdata, mc_filter); +} + +/* Enable/Disable promiscuous and multicast modes. */ +static void phytmac_set_rx_mode(struct net_device *ndev) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct phytmac_hw_if *hw_if = pdata->hw_if; + + hw_if->enable_promise(pdata, ndev->flags & IFF_PROMISC); + + hw_if->enable_multicast(pdata, ndev->flags & IFF_ALLMULTI); + if (!netdev_mc_empty(ndev)) + phytmac_set_mac_hash_table(ndev); +} + +static struct net_device_stats *phytmac_get_stats(struct net_device *dev) +{ + struct phytmac *pdata = netdev_priv(dev); + struct net_device_stats *nstat = &pdata->ndev->stats; + struct phytmac_stats *stat = &pdata->stats; + struct phytmac_hw_if *hw_if = pdata->hw_if; + + if (pdata->power_state == PHYTMAC_POWEROFF) + return nstat; + + hw_if->get_stats(pdata); + + nstat->rx_errors = (stat->rx_fcs_errors + + stat->rx_alignment_errors + + stat->rx_overruns + + stat->rx_oversize_packets + + stat->rx_jabbers + + stat->rx_undersized_packets + + stat->rx_length_errors); + nstat->rx_dropped = stat->rx_resource_over; + nstat->tx_errors = (stat->tx_late_collisions + + stat->tx_excessive_collisions + + stat->tx_underrun + + stat->tx_carrier_sense_errors); + nstat->multicast = stat->rx_mcast_packets; + nstat->collisions = (stat->tx_single_collisions + + stat->tx_multiple_collisions + + stat->tx_excessive_collisions + + stat->tx_late_collisions); + nstat->rx_length_errors = (stat->rx_oversize_packets + + stat->rx_jabbers + + stat->rx_undersized_packets + + stat->rx_length_errors); + nstat->rx_over_errors = stat->rx_resource_over; + nstat->rx_crc_errors = stat->rx_fcs_errors; + nstat->rx_frame_errors = stat->rx_alignment_errors; + nstat->rx_fifo_errors = stat->rx_overruns; + nstat->tx_aborted_errors = stat->tx_excessive_collisions; + nstat->tx_carrier_errors = stat->tx_carrier_sense_errors; + nstat->tx_fifo_errors = stat->tx_underrun; + + return nstat; +} + +static int phytmac_calc_rx_buf_len(struct phytmac *pdata, u32 mtu) +{ + unsigned int size = mtu + ETH_HLEN + ETH_FCS_LEN; + int rx_buf_len = roundup(size, RX_BUFFER_MULTIPLE); + + netdev_dbg(pdata->ndev, "mtu [%u] rx_buffer_size [%u]\n", + mtu, rx_buf_len); + + return rx_buf_len; +} + +struct phytmac_dma_desc *phytmac_get_rx_desc(struct phytmac_queue *queue, + unsigned int index) +{ + return &queue->rx_ring[index & (queue->pdata->rx_ring_size - 1)]; +} + +struct sk_buff *phytmac_get_rx_skb(struct phytmac_queue *queue, + unsigned int index) +{ + return queue->rx_skb[index & (queue->pdata->rx_ring_size - 1)]; +} + +struct phytmac_tx_skb *phytmac_get_tx_skb(struct phytmac_queue *queue, + unsigned int index) +{ + return &queue->tx_skb[index & (queue->pdata->tx_ring_size - 1)]; +} + +struct phytmac_dma_desc *phytmac_get_tx_desc(struct phytmac_queue *queue, + unsigned int index) +{ + return &queue->tx_ring[index & (queue->pdata->tx_ring_size - 1)]; +} + +static int phytmac_free_tx_resource(struct phytmac *pdata) +{ + struct phytmac_queue *queue; + unsigned int q; + int size; + + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { + kfree(queue->tx_skb); + queue->tx_skb = NULL; + + if (queue->tx_ring) { + size = TX_RING_BYTES(pdata) + pdata->tx_bd_prefetch; + dma_free_coherent(pdata->dev, size, + queue->tx_ring, queue->tx_ring_addr); + queue->tx_ring = NULL; + } + } + + return 0; +} + +static int phytmac_free_rx_resource(struct phytmac *pdata) +{ + struct phytmac_queue *queue; + struct sk_buff *skb; + struct phytmac_dma_desc *desc; + struct phytmac_hw_if *hw_if = pdata->hw_if; + dma_addr_t addr; + unsigned int q; + int size, i; + + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { + if (queue->rx_skb) { + for (i = 0; i < pdata->rx_ring_size; i++) { + skb = phytmac_get_rx_skb(queue, i); + if (skb) { + desc = &queue->rx_ring[i]; + addr = hw_if->get_desc_addr(desc); + dma_unmap_single(pdata->dev, addr, pdata->rx_buffer_len, + DMA_FROM_DEVICE); + dev_kfree_skb_any(skb); + skb = NULL; + } + } + + kfree(queue->rx_skb); + queue->rx_skb = NULL; + } + + if (queue->rx_ring) { + size = RX_RING_BYTES(pdata) + pdata->rx_bd_prefetch; + dma_free_coherent(pdata->dev, size, + queue->rx_ring, queue->rx_ring_addr); + queue->rx_ring = NULL; + } + } + + return 0; +} + +static int phytmac_alloc_tx_resource(struct phytmac *pdata) +{ + struct phytmac_queue *queue; + unsigned int q; + int size; + + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { + size = TX_RING_BYTES(pdata) + pdata->tx_bd_prefetch; + queue->tx_ring = dma_alloc_coherent(pdata->dev, size, + &queue->tx_ring_addr, + GFP_KERNEL); + if (!queue->tx_ring) + goto err; + + if (netif_msg_drv(pdata)) + netdev_info(pdata->ndev, + "Allocated TX ring for queue %u of %d bytes at %08lx\n", + q, size, (unsigned long)queue->tx_ring_addr); + + size = pdata->tx_ring_size * sizeof(struct phytmac_tx_skb); + queue->tx_skb = kmalloc(size, GFP_KERNEL); + if (!queue->tx_skb) + goto err; + + if (netif_msg_drv(pdata)) + netdev_info(pdata->ndev, + "Allocated %d TX struct tx_skb entries at %p\n", + pdata->rx_ring_size, queue->tx_skb); + } + + return 0; +err: + phytmac_free_tx_resource(pdata); + + return -ENOMEM; +} + +static int phytmac_alloc_rx_resource(struct phytmac *pdata) +{ + struct phytmac_queue *queue; + struct phytmac_hw_if *hw_if = pdata->hw_if; + unsigned int q; + int size; + int i; + + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { + size = RX_RING_BYTES(pdata) + pdata->rx_bd_prefetch; + queue->rx_ring = dma_alloc_coherent(pdata->dev, size, + &queue->rx_ring_addr, GFP_KERNEL); + if (!queue->rx_ring) + goto err; + + if (netif_msg_drv(pdata)) + netdev_info(pdata->ndev, + "Allocated RX ring for queue %u of %d bytes at %08lx\n", + q, size, (unsigned long)queue->rx_ring_addr); + + for (i = 0; i < pdata->rx_ring_size; i++) + hw_if->init_rx_map(queue, i); + + size = pdata->rx_ring_size * sizeof(struct sk_buff *); + queue->rx_skb = kzalloc(size, GFP_KERNEL); + if (!queue->rx_skb) + goto err; + + if (netif_msg_drv(pdata)) + netdev_info(pdata->ndev, + "Allocated %d RX struct sk_buff entries at %p\n", + pdata->rx_ring_size, queue->rx_skb); + } + return 0; +err: + phytmac_free_rx_resource(pdata); + + return -ENOMEM; +} + +static int phytmac_alloc_resource(struct phytmac *pdata) +{ + struct net_device *ndev = pdata->ndev; + int ret; + + pdata->rx_buffer_len = phytmac_calc_rx_buf_len(pdata, ndev->mtu); + + if (netif_msg_drv(pdata)) + netdev_info(pdata->ndev, "alloc resource, rx_buffer_len = %d\n", + pdata->rx_buffer_len); + + ret = phytmac_alloc_tx_resource(pdata); + if (ret) + return ret; + + ret = phytmac_alloc_rx_resource(pdata); + if (ret) { + phytmac_free_tx_resource(pdata); + return ret; + } + + return 0; +} + +static void phytmac_free_resource(struct phytmac *pdata) +{ + phytmac_free_tx_resource(pdata); + phytmac_free_rx_resource(pdata); +} + +static irqreturn_t phytmac_irq(int irq, void *data) +{ + struct phytmac_queue *queue = data; + struct phytmac *pdata = queue->pdata; + struct phytmac_hw_if *hw_if = pdata->hw_if; + u32 status; + + status = hw_if->get_irq(pdata, queue->index); + + if (netif_msg_intr(pdata)) + netdev_info(pdata->ndev, "phymac irq status = %x\n", status); + + if (unlikely(!status)) + return IRQ_NONE; + + while (status) { + if (status & pdata->rx_irq_mask) { + /* Disable RX interrupts */ + hw_if->disable_irq(pdata, queue->index, pdata->rx_irq_mask); + hw_if->clear_irq(pdata, queue->index, PHYTMAC_INT_RX_COMPLETE); + + if (napi_schedule_prep(&queue->rx_napi)) + __napi_schedule(&queue->rx_napi); + } + + if (status & (PHYTMAC_INT_TX_COMPLETE)) { + /* Disable TX interrupts */ + hw_if->disable_irq(pdata, queue->index, PHYTMAC_INT_TX_COMPLETE); + hw_if->clear_irq(pdata, queue->index, PHYTMAC_INT_TX_COMPLETE); + + if (napi_schedule_prep(&queue->tx_napi)) + __napi_schedule(&queue->tx_napi); + } + + if (status & PHYTMAC_INT_TX_ERR) + hw_if->clear_irq(pdata, queue->index, PHYTMAC_INT_TX_ERR); + + if (status & PHYTMAC_INT_RX_OVERRUN) { + hw_if->clear_irq(pdata, queue->index, PHYTMAC_INT_RX_OVERRUN); + pdata->stats.rx_overruns++; + } + status = hw_if->get_irq(pdata, queue->index); + } + + return IRQ_HANDLED; +} + +static irqreturn_t phytmac_intx_irq(int irq, void *data) +{ + struct phytmac *pdata = data; + struct phytmac_hw_if *hw_if = pdata->hw_if; + u32 irq_mask; + int i; + + irq_mask = hw_if->get_intx_mask(pdata); + + if (unlikely(!irq_mask)) + return IRQ_NONE; + + for (i = 0; i < pdata->queues_num; i++) { + if (irq_mask & BIT(i)) + phytmac_irq(irq, &pdata->queues[i]); + } + + return IRQ_HANDLED; +} + +static void phytmac_dump_pkt(struct phytmac *pdata, struct sk_buff *skb, bool tx) +{ + struct net_device *ndev = pdata->ndev; + + if (tx) { + netdev_dbg(ndev, "start_xmit: queue %u len %u head %p data %p tail %p end %p\n", + skb->queue_mapping, skb->len, skb->head, skb->data, + skb_tail_pointer(skb), skb_end_pointer(skb)); + } else { + netdev_dbg(ndev, "queue %u received skb of length %u, csum: %08x\n", + skb->queue_mapping, skb->len, skb->csum); + print_hex_dump(KERN_DEBUG, " mac: ", DUMP_PREFIX_ADDRESS, 16, 1, + skb_mac_header(skb), 16, true); + } + + print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_OFFSET, 16, 1, + skb->data, skb->len, true); +} + +static struct sk_buff *phytmac_rx_single(struct phytmac_queue *queue, struct phytmac_dma_desc *desc) +{ + struct phytmac *pdata = queue->pdata; + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct sk_buff *skb; + unsigned int len; + dma_addr_t addr; + + skb = phytmac_get_rx_skb(queue, queue->rx_tail); + if (unlikely(!skb)) { + netdev_err(pdata->ndev, + "inconsistent Rx descriptor chain\n"); + pdata->ndev->stats.rx_dropped++; + queue->stats.rx_dropped++; + return NULL; + } + + queue->rx_skb[queue->rx_tail & (pdata->rx_ring_size - 1)] = NULL; + len = hw_if->get_rx_pkt_len(pdata, desc); + addr = hw_if->get_desc_addr(desc); + + skb_put(skb, len); + dma_unmap_single(pdata->dev, addr, + pdata->rx_buffer_len, DMA_FROM_DEVICE); + skb->protocol = eth_type_trans(skb, pdata->ndev); + skb_checksum_none_assert(skb); + + if (pdata->ndev->features & NETIF_F_RXCSUM && + !(pdata->ndev->flags & IFF_PROMISC) && + hw_if->rx_checksum(desc)) + skb->ip_summed = CHECKSUM_UNNECESSARY; + + if (netif_msg_pktdata(pdata)) + phytmac_dump_pkt(pdata, skb, false); + + return skb; +} + +static struct sk_buff *phytmac_rx_frame(struct phytmac_queue *queue, + unsigned int first_frag, unsigned int last_frag, int len) +{ + unsigned int offset = 0; + unsigned int frag = 0; + unsigned int entry = 0; + dma_addr_t addr = 0; + struct sk_buff *skb; + struct phytmac_dma_desc *desc; + struct phytmac *pdata = queue->pdata; + struct phytmac_hw_if *hw_if = pdata->hw_if; + unsigned int frag_len = pdata->rx_buffer_len; + + if (netif_msg_drv(pdata)) + netdev_info(pdata->ndev, "rx frame %u - %u (len %u)\n", + first_frag, last_frag, len); + + skb = netdev_alloc_skb(pdata->ndev, len); + if (!skb) { + pdata->ndev->stats.rx_dropped++; + netdev_err(pdata->ndev, "rx frame alloc skb failed\n"); + return NULL; + } + + skb_checksum_none_assert(skb); + + if (pdata->ndev->features & NETIF_F_RXCSUM && + !(pdata->ndev->flags & IFF_PROMISC) && + hw_if->rx_checksum(phytmac_get_rx_desc(queue, last_frag))) + skb->ip_summed = CHECKSUM_UNNECESSARY; + + skb_put(skb, len); + + for (frag = first_frag; ; frag++) { + if (offset + frag_len > len) { + if (unlikely(frag != last_frag)) { + dev_kfree_skb_any(skb); + return NULL; + } + frag_len = len - offset; + } + + desc = phytmac_get_rx_desc(queue, frag); + addr = hw_if->get_desc_addr(desc); + dma_sync_single_for_cpu(pdata->dev, addr, frag_len, + DMA_FROM_DEVICE); + + entry = frag & (pdata->rx_ring_size - 1); + skb_copy_to_linear_data_offset(skb, offset, queue->rx_skb[entry]->data, frag_len); + + offset += pdata->rx_buffer_len; + + dma_sync_single_for_device(pdata->dev, addr, frag_len, + DMA_FROM_DEVICE); + + if (frag == last_frag) + break; + } + + skb->protocol = eth_type_trans(skb, pdata->ndev); + if (netif_msg_pktdata(pdata)) + phytmac_dump_pkt(pdata, skb, false); + + return skb; +} + +static struct sk_buff *phytmac_rx_mbuffer(struct phytmac_queue *queue) +{ + struct phytmac *pdata = queue->pdata; + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct phytmac_dma_desc *desc; + struct sk_buff *skb = NULL; + unsigned int rx_tail = 0; + int first_frag = -1; + int len; + + for (rx_tail = queue->rx_tail; ; rx_tail++) { + desc = phytmac_get_rx_desc(queue, rx_tail); + if (hw_if->rx_pkt_start(desc)) { + if (first_frag != -1) + hw_if->clear_rx_desc(queue, first_frag, rx_tail); + first_frag = rx_tail; + continue; + } + + if (hw_if->rx_pkt_end(desc)) { + queue->rx_tail = rx_tail; + len = hw_if->get_rx_pkt_len(pdata, desc); + skb = phytmac_rx_frame(queue, first_frag, rx_tail, len); + first_frag = -1; + break; + } + } + return skb; +} + +static void phytmac_rx_clean(struct phytmac_queue *queue) +{ + struct phytmac *pdata = queue->pdata; + struct phytmac_hw_if *hw_if = pdata->hw_if; + unsigned int index, space; + dma_addr_t paddr; + struct sk_buff *skb; + + space = CIRC_SPACE(queue->rx_head, queue->rx_tail, + pdata->rx_ring_size); + + while (space > 0) { + index = queue->rx_head & (pdata->rx_ring_size - 1); + + if (!queue->rx_skb[index]) { + skb = netdev_alloc_skb(pdata->ndev, pdata->rx_buffer_len); + if (unlikely(!skb)) { + netdev_err(pdata->ndev, "rx clean alloc skb failed\n"); + break; + } + + paddr = dma_map_single(pdata->dev, skb->data, + pdata->rx_buffer_len, DMA_FROM_DEVICE); + if (dma_mapping_error(pdata->dev, paddr)) { + dev_kfree_skb(skb); + break; + } + + queue->rx_skb[index] = skb; + + hw_if->rx_map(queue, index, paddr); + } else { + hw_if->rx_map(queue, index, 0); + } + + queue->rx_head++; + if (queue->rx_head >= pdata->rx_ring_size) + queue->rx_head &= (pdata->rx_ring_size - 1); + + space--; + } + + /* make newly descriptor to hardware */ + wmb(); +} + +static int phytmac_rx(struct phytmac_queue *queue, struct napi_struct *napi, + int budget) +{ + struct phytmac *pdata = queue->pdata; + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct sk_buff *skb; + struct phytmac_dma_desc *desc; + int count = 0; + + while (count < budget) { + desc = phytmac_get_rx_desc(queue, queue->rx_tail); + if (!hw_if->rx_complete(desc)) + break; + + if (hw_if->rx_single_buffer(desc)) + skb = phytmac_rx_single(queue, desc); + else + skb = phytmac_rx_mbuffer(queue); + + if (!skb) + break; + + pdata->ndev->stats.rx_packets++; + queue->stats.rx_packets++; + pdata->ndev->stats.rx_bytes += skb->len; + queue->stats.rx_bytes += skb->len; + queue->rx_tail = (queue->rx_tail + 1) & (pdata->rx_ring_size - 1); + + count++; + + if (IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) + phytmac_ptp_rxstamp(pdata, skb, desc); + + napi_gro_receive(napi, skb); + } + + phytmac_rx_clean(queue); + + return count; +} + +static void phytmac_tx_unmap(struct phytmac *pdata, struct phytmac_tx_skb *tx_skb, int budget) +{ + if (tx_skb->addr) { + if (tx_skb->mapped_as_page) + dma_unmap_page(pdata->dev, tx_skb->addr, + tx_skb->length, DMA_TO_DEVICE); + else + dma_unmap_single(pdata->dev, tx_skb->addr, + tx_skb->length, DMA_TO_DEVICE); + tx_skb->addr = 0; + } + + if (tx_skb->skb) { + napi_consume_skb(tx_skb->skb, budget); + tx_skb->skb = NULL; + } +} + +static int phytmac_maybe_stop_tx_queue(struct phytmac_queue *queue, + unsigned int count) +{ + struct phytmac *pdata = queue->pdata; + int space = CIRC_SPACE(queue->tx_tail, queue->tx_head, + pdata->tx_ring_size); + + if (space < count) { + if (netif_msg_drv(pdata)) + netdev_info(pdata->ndev, "Tx queue %d stopped, not enough descriptors available\n", + queue->index); + + netif_stop_subqueue(pdata->ndev, queue->index); + + return NETDEV_TX_BUSY; + } + + return 0; +} + +static int phytmac_maybe_wake_tx_queue(struct phytmac_queue *queue) +{ + struct phytmac *pdata = queue->pdata; + int space = CIRC_SPACE(queue->tx_tail, queue->tx_head, + pdata->tx_ring_size); + + return (space > DEFAULT_TX_DESC_MIN_FREE) ? 1 : 0; +} + +static int phytmac_tx_clean(struct phytmac_queue *queue, int budget) +{ + struct phytmac *pdata = queue->pdata; + u16 queue_index = queue->index; + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct phytmac_tx_skb *tx_skb; + struct phytmac_dma_desc *desc; + int complate = 0; + int packet_count = 0; + unsigned int tail = queue->tx_tail; + unsigned int head; + + spin_lock(&pdata->lock); + + for (head = queue->tx_head; head != tail && packet_count < budget; ) { + desc = phytmac_get_tx_desc(queue, head); + if (!hw_if->tx_complete(desc)) + break; + + /* Process all buffers of the current transmitted frame */ + for (;; head++) { + tx_skb = phytmac_get_tx_skb(queue, head); + if (tx_skb->skb) { + complate = 1; + if (IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) { + if (unlikely(skb_shinfo(tx_skb->skb)->tx_flags & + SKBTX_HW_TSTAMP) && + !phytmac_ptp_one_step(tx_skb->skb)) + phytmac_ptp_txstamp(queue, tx_skb->skb, desc); + } + + if (netif_msg_drv(pdata)) + netdev_info(pdata->ndev, "desc %u (data %p) tx complete\n", + head, tx_skb->skb->data); + + pdata->ndev->stats.tx_packets++; + queue->stats.tx_packets++; + pdata->ndev->stats.tx_bytes += tx_skb->skb->len; + queue->stats.tx_bytes += tx_skb->skb->len; + packet_count++; + } + + /* Now we can safely release resources */ + phytmac_tx_unmap(pdata, tx_skb, budget); + + if (complate) { + complate = 0; + break; + } + } + + head++; + if (head >= pdata->tx_ring_size) + head &= (pdata->tx_ring_size - 1); + } + + queue->tx_head = head; + if (__netif_subqueue_stopped(pdata->ndev, queue_index) && + (phytmac_maybe_wake_tx_queue(queue))) + netif_wake_subqueue(pdata->ndev, queue_index); + spin_unlock(&pdata->lock); + + return packet_count; +} + +static int phytmac_rx_poll(struct napi_struct *napi, int budget) +{ + struct phytmac_queue *queue = container_of(napi, struct phytmac_queue, rx_napi); + struct phytmac *pdata = queue->pdata; + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct phytmac_dma_desc *desc; + int work_done; + + work_done = phytmac_rx(queue, napi, budget); + + if (netif_msg_drv(pdata)) + netdev_info(pdata->ndev, "RX poll: queue = %u, work_done = %d, budget = %d\n", + (unsigned int)(queue->index), work_done, budget); + if (work_done < budget && napi_complete_done(napi, work_done)) { + hw_if->enable_irq(pdata, queue->index, pdata->rx_irq_mask); + + desc = phytmac_get_rx_desc(queue, queue->rx_tail); + if (hw_if->rx_complete(desc)) { + hw_if->disable_irq(pdata, queue->index, pdata->rx_irq_mask); + hw_if->clear_irq(pdata, queue->index, PHYTMAC_INT_RX_COMPLETE); + + napi_schedule(napi); + } + } + + return work_done; +} + +static int phytmac_tx_poll(struct napi_struct *napi, int budget) +{ + struct phytmac_queue *queue = container_of(napi, struct phytmac_queue, tx_napi); + struct phytmac *pdata = queue->pdata; + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct phytmac_dma_desc *desc; + int work_done; + + work_done = phytmac_tx_clean(queue, budget); + + if (netif_msg_drv(pdata)) + netdev_info(pdata->ndev, "TX poll: queue = %u, work_done = %d, budget = %d\n", + (unsigned int)(queue->index), work_done, budget); + if (work_done < budget && napi_complete_done(napi, work_done)) { + hw_if->enable_irq(pdata, queue->index, PHYTMAC_INT_TX_COMPLETE); + if (queue->tx_head != queue->tx_tail) { + desc = phytmac_get_tx_desc(queue, queue->tx_head); + + if (hw_if->tx_complete(desc)) { + hw_if->disable_irq(pdata, queue->index, PHYTMAC_INT_TX_COMPLETE); + hw_if->clear_irq(pdata, queue->index, PHYTMAC_INT_TX_COMPLETE); + + napi_schedule(napi); + } + } + } + + return work_done; +} + +static inline int phytmac_clear_csum(struct sk_buff *skb) +{ + if (skb->ip_summed != CHECKSUM_PARTIAL) + return 0; + + /* make sure we can modify the header */ + if (unlikely(skb_cow_head(skb, 0))) + return -1; + + *(__sum16 *)(skb_checksum_start(skb) + skb->csum_offset) = 0; + return 0; +} + +static int phytmac_add_fcs(struct sk_buff **skb, struct net_device *ndev) +{ + bool cloned = skb_cloned(*skb) || skb_header_cloned(*skb) || + skb_is_nonlinear(*skb); + int padlen = ETH_ZLEN - (*skb)->len; + int headroom = skb_headroom(*skb); + int tailroom = skb_tailroom(*skb); + struct sk_buff *nskb; + u32 fcs; + int i; + + if ((ndev->features & NETIF_F_HW_CSUM) || + !((*skb)->ip_summed != CHECKSUM_PARTIAL) || + skb_shinfo(*skb)->gso_size || phytmac_ptp_one_step(*skb)) + return 0; + + if (padlen <= 0) { + if (tailroom >= ETH_FCS_LEN) + goto add_fcs; + else if (!cloned && headroom + tailroom >= ETH_FCS_LEN) + padlen = 0; + else + padlen = ETH_FCS_LEN; + } else { + padlen += ETH_FCS_LEN; + } + + if (!cloned && headroom + tailroom >= padlen) { + (*skb)->data = memmove((*skb)->head, (*skb)->data, (*skb)->len); + skb_set_tail_pointer(*skb, (*skb)->len); + } else { + nskb = skb_copy_expand(*skb, 0, padlen, GFP_ATOMIC); + if (!nskb) + return -ENOMEM; + + dev_consume_skb_any(*skb); + *skb = nskb; + } + + if (padlen > ETH_FCS_LEN) + skb_put_zero(*skb, padlen - ETH_FCS_LEN); + +add_fcs: + fcs = crc32_le(~0, (*skb)->data, (*skb)->len); + fcs = ~fcs; + + for (i = 0; i < 4; ++i) + skb_put_u8(*skb, (fcs >> (i * 8)) & 0xff); + return 0; +} + +static int phytmac_packet_info(struct phytmac *pdata, + struct phytmac_queue *queue, struct sk_buff *skb, + struct packet_info *packet) +{ + int is_lso; + unsigned int hdrlen, f; + int desc_cnt; + + memset(packet, 0, sizeof(struct packet_info)); + + is_lso = (skb_shinfo(skb)->gso_size != 0); + + if (is_lso) { + /* length of headers */ + if (ip_hdr(skb)->protocol == IPPROTO_UDP) { + /* only queue eth + ip headers separately for UDP */ + hdrlen = skb_transport_offset(skb); + packet->lso = LSO_UFO; + packet->mss = skb_shinfo(skb)->gso_size + hdrlen + ETH_FCS_LEN; + } else { + hdrlen = skb_transport_offset(skb) + tcp_hdrlen(skb); + packet->lso = LSO_TSO; + packet->mss = skb_shinfo(skb)->gso_size; + } + + if (skb_headlen(skb) < hdrlen) { + dev_err(pdata->dev, "Error - LSO headers fragmented!!!\n"); + return NETDEV_TX_BUSY; + } + } else { + hdrlen = min(skb_headlen(skb), pdata->max_tx_length); + packet->lso = 0; + packet->mss = 0; + } + + packet->hdrlen = hdrlen; + + if (is_lso && (skb_headlen(skb) > hdrlen)) + desc_cnt = TXD_USE_COUNT(pdata, (skb_headlen(skb) - hdrlen)) + 1; + else + desc_cnt = TXD_USE_COUNT(pdata, hdrlen); + + for (f = 0; f < skb_shinfo(skb)->nr_frags; f++) + desc_cnt += TXD_USE_COUNT(pdata, skb_frag_size(&skb_shinfo(skb)->frags[f])); + packet->desc_cnt = desc_cnt; + + if ((!(pdata->ndev->features & NETIF_F_HW_CSUM)) && + skb->ip_summed != CHECKSUM_PARTIAL && + !is_lso && + !phytmac_ptp_one_step(skb)) + packet->nocrc = 1; + else + packet->nocrc = 0; + + if (netif_msg_pktdata(pdata)) { + netdev_info(pdata->ndev, "packet info: desc_cnt=%d, nocrc=%d,ip_summed=%d\n", + desc_cnt, packet->nocrc, skb->ip_summed); + netdev_info(pdata->ndev, "packet info: mss=%d, lso=%d,skb_len=%d, nr_frags=%d\n", + packet->mss, packet->lso, skb->len, skb_shinfo(skb)->nr_frags); + } + + return 0; +} + +static unsigned int phytmac_tx_map(struct phytmac *pdata, + struct phytmac_queue *queue, + struct sk_buff *skb, + struct packet_info *packet) +{ + dma_addr_t mapping; + struct phytmac_hw_if *hw_if = pdata->hw_if; + unsigned int len, i, tx_tail = queue->tx_tail; + struct phytmac_tx_skb *tx_skb = NULL; + unsigned int offset, size, count = 0; + unsigned int f, nr_frags = skb_shinfo(skb)->nr_frags; + + len = skb_headlen(skb); + size = packet->hdrlen; + + offset = 0; + tx_tail = queue->tx_tail; + while (len) { + tx_skb = phytmac_get_tx_skb(queue, tx_tail); + + mapping = dma_map_single(pdata->dev, + skb->data + offset, + size, DMA_TO_DEVICE); + if (dma_mapping_error(pdata->dev, mapping)) + goto dma_error; + + /* Save info to properly release resources */ + tx_skb->skb = NULL; + tx_skb->addr = mapping; + tx_skb->length = size; + tx_skb->mapped_as_page = false; + + len -= size; + offset += size; + count++; + tx_tail++; + + size = min(len, pdata->max_tx_length); + } + + /* Then, map paged data from fragments */ + for (f = 0; f < nr_frags; f++) { + const skb_frag_t *frag = &skb_shinfo(skb)->frags[f]; + + len = skb_frag_size(frag); + offset = 0; + while (len) { + size = min(len, pdata->max_tx_length); + tx_skb = phytmac_get_tx_skb(queue, tx_tail); + mapping = skb_frag_dma_map(pdata->dev, frag, + offset, size, DMA_TO_DEVICE); + if (dma_mapping_error(pdata->dev, mapping)) + goto dma_error; + + /* Save info to properly release resources */ + tx_skb->skb = NULL; + tx_skb->addr = mapping; + tx_skb->length = size; + tx_skb->mapped_as_page = true; + + len -= size; + offset += size; + count++; + tx_tail++; + } + } + + /* Should never happen */ + if (unlikely(!tx_skb)) { + netdev_err(pdata->ndev, "BUG! empty skb!\n"); + return 0; + } + + /* This is the last buffer of the frame: save socket buffer */ + tx_skb->skb = skb; + + if (hw_if->tx_map(queue, tx_tail, packet)) { + netdev_err(pdata->ndev, "BUG!hw tx map failed!\n"); + return 0; + } + + queue->tx_tail = tx_tail & (pdata->tx_ring_size - 1); + + return count; + +dma_error: + netdev_err(pdata->ndev, "TX DMA map failed\n"); + + for (i = queue->tx_tail; i != tx_tail; i++) { + tx_skb = phytmac_get_tx_skb(queue, i); + phytmac_tx_unmap(pdata, tx_skb, 0); + } + + return 0; +} + +static inline void phytmac_init_ring(struct phytmac *pdata) +{ + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct phytmac_queue *queue; + unsigned int q = 0; + + for (queue = pdata->queues; q < pdata->queues_num; ++q) { + queue->tx_head = 0; + queue->tx_tail = 0; + hw_if->clear_tx_desc(queue); + + queue->rx_head = 0; + queue->rx_tail = 0; + phytmac_rx_clean(queue); + ++queue; + } + + hw_if->init_ring_hw(pdata); +} + +static netdev_tx_t phytmac_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct phytmac_hw_if *hw_if = pdata->hw_if; + u16 queue_index = skb->queue_mapping; + struct phytmac_queue *queue = &pdata->queues[queue_index]; + netdev_tx_t ret = NETDEV_TX_OK; + struct packet_info packet; + unsigned long flags; + + if (phytmac_clear_csum(skb)) { + dev_kfree_skb_any(skb); + return ret; + } + + if (phytmac_add_fcs(&skb, ndev)) { + dev_kfree_skb_any(skb); + return ret; + } + + if (phytmac_packet_info(pdata, queue, skb, &packet)) { + dev_kfree_skb_any(skb); + return ret; + } + + if (netif_msg_pktdata(pdata)) + phytmac_dump_pkt(pdata, skb, true); + + spin_lock_irqsave(&pdata->lock, flags); + /* Check that there are enough descriptors available */ + ret = phytmac_maybe_stop_tx_queue(queue, packet.desc_cnt); + if (ret) + goto tx_return; + + /* Map socket buffer for DMA transfer */ + if (!phytmac_tx_map(pdata, queue, skb, &packet)) { + dev_kfree_skb_any(skb); + goto tx_return; + } + + skb_tx_timestamp(skb); + /* Make newly descriptor to hardware */ + wmb(); + + hw_if->transmit(queue); + +tx_return: + spin_unlock_irqrestore(&pdata->lock, flags); + return ret; +} + +static int phytmac_fixedlink_phy_connect(struct fwnode_handle *fwnode) +{ + struct fwnode_handle *dn; + u8 pl_cfg_link_an_mode = 0; + + /* Fixed links is handled without needing a PHY */ + dn = fwnode_get_named_child_node(fwnode, "fixed-link"); + if (dn || fwnode_property_present(fwnode, "fixed-link")) + pl_cfg_link_an_mode = MLO_AN_FIXED; + fwnode_handle_put(dn); + + if (pl_cfg_link_an_mode == MLO_AN_FIXED) + return 0; + return -ENODEV; +} + +static int phytmac_phylink_connect(struct phytmac *pdata) +{ + struct net_device *ndev = pdata->ndev; + struct phy_device *phydev; + struct fwnode_handle *fwnode = dev_fwnode(pdata->dev); + struct device_node *of_node = dev_of_node(pdata->dev); + int ret = 0; + + if (of_node) + ret = phylink_of_phy_connect(pdata->phylink, of_node, 0); + + if (!of_node && fwnode) + ret = phytmac_fixedlink_phy_connect(fwnode); + + if (!fwnode || ret) { + if (pdata->mii_bus) { + phydev = phy_find_first(pdata->mii_bus); + if (!phydev) { + dev_err(pdata->dev, "no PHY found\n"); + return -ENXIO; + } + /* attach the mac to the phy */ + ret = phylink_connect_phy(pdata->phylink, phydev); + } else { + netdev_err(ndev, "Not mii register\n"); + return -ENXIO; + } + } + + if (ret) { + netdev_err(ndev, "Could not attach PHY (%d)\n", ret); + return ret; + } + + return 0; +} + +int phytmac_pcs_config(struct phylink_pcs *pcs, unsigned int mode, + phy_interface_t interface, + const unsigned long *advertising, + bool permit_pause_to_mac) +{ + return 0; +} + +void phytmac_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, + phy_interface_t interface, int speed, int duplex) +{ + struct phytmac *pdata = container_of(pcs, struct phytmac, phylink_pcs); + struct phytmac_hw_if *hw_if = pdata->hw_if; + + if (netif_msg_link(pdata)) + netdev_info(pdata->ndev, "pcs link up, interface = %s, speed = %d, duplex = %d\n", + phy_modes(interface), speed, duplex); + hw_if->pcs_linkup(pdata, interface, speed, duplex); +} + +static const struct phylink_pcs_ops phytmac_pcs_phylink_ops = { + .pcs_config = phytmac_pcs_config, + .pcs_link_up = phytmac_pcs_link_up, +}; + +static int phytmac_mac_prepare(struct phylink_config *config, unsigned int mode, + phy_interface_t interface) +{ + struct phytmac *pdata = netdev_priv(to_net_dev(config->dev)); + + if (interface == PHY_INTERFACE_MODE_USXGMII || + interface == PHY_INTERFACE_MODE_10GBASER || + interface == PHY_INTERFACE_MODE_SGMII || + interface == PHY_INTERFACE_MODE_1000BASEX || + interface == PHY_INTERFACE_MODE_2500BASEX) { + pdata->phylink_pcs.ops = &phytmac_pcs_phylink_ops; + } else { + pdata->phylink_pcs.ops = NULL; + } + + if (pdata->phylink_pcs.ops) + phylink_set_pcs(pdata->phylink, &pdata->phylink_pcs); + + return 0; +} + +static void phytmac_mac_config(struct phylink_config *config, unsigned int mode, + const struct phylink_link_state *state) +{ + struct phytmac *pdata = netdev_priv(to_net_dev(config->dev)); + struct phytmac_hw_if *hw_if = pdata->hw_if; + unsigned long flags; + + if (netif_msg_link(pdata)) { + netdev_info(pdata->ndev, "mac config interface=%s, mode=%d\n", + phy_modes(state->interface), mode); + } + + spin_lock_irqsave(&pdata->lock, flags); + hw_if->mac_config(pdata, mode, state); + spin_unlock_irqrestore(&pdata->lock, flags); +} + +static void phytmac_mac_link_down(struct phylink_config *config, unsigned int mode, + phy_interface_t interface) +{ + struct net_device *ndev = to_net_dev(config->dev); + struct phytmac *pdata = netdev_priv(ndev); + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct phytmac_queue *queue; + unsigned int q; + unsigned long flags; + + if (netif_msg_link(pdata)) { + netdev_info(ndev, "link down interface:%s, mode=%d\n", + phy_modes(interface), mode); + } + + if (pdata->use_ncsi) + ncsi_stop_dev(pdata->ncsidev); + + spin_lock_irqsave(&pdata->lock, flags); + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { + hw_if->disable_irq(pdata, queue->index, pdata->rx_irq_mask | pdata->tx_irq_mask); + hw_if->clear_irq(pdata, queue->index, pdata->rx_irq_mask | pdata->tx_irq_mask); + } + + /* Disable Rx and Tx */ + hw_if->enable_network(pdata, false, PHYTMAC_RX | PHYTMAC_TX); + spin_unlock_irqrestore(&pdata->lock, flags); + + netif_tx_stop_all_queues(ndev); +} + +static void phytmac_mac_link_up(struct phylink_config *config, + struct phy_device *phy, + unsigned int mode, phy_interface_t interface, + int speed, int duplex, + bool tx_pause, bool rx_pause) +{ + struct net_device *ndev = to_net_dev(config->dev); + struct phytmac *pdata = netdev_priv(ndev); + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct phytmac_queue *queue; + unsigned long flags; + unsigned int q; + int ret; + + if (netif_msg_link(pdata)) + netdev_info(pdata->ndev, "link up interface:%s, speed:%d, duplex:%s\n", + phy_modes(interface), speed, duplex ? "full-duplex" : "half-duplex"); + + spin_lock_irqsave(&pdata->lock, flags); + + hw_if->mac_linkup(pdata, interface, speed, duplex); + + if (rx_pause != pdata->pause) { + hw_if->enable_pause(pdata, rx_pause); + pdata->pause = rx_pause; + } + + phytmac_init_ring(pdata); + + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) + hw_if->enable_irq(pdata, queue->index, pdata->rx_irq_mask | pdata->tx_irq_mask); + + /* Enable Rx and Tx */ + hw_if->enable_network(pdata, true, PHYTMAC_RX | PHYTMAC_TX); + spin_unlock_irqrestore(&pdata->lock, flags); + + if (pdata->use_ncsi) { + /* Start the NCSI device */ + ret = ncsi_start_dev(pdata->ncsidev); + if (ret) { + netdev_err(pdata->ndev, "Ncsi start dev failed (error %d)\n", ret); + return; + } + } + + netif_tx_wake_all_queues(ndev); +} + +int phytmac_mdio_register(struct phytmac *pdata) +{ + struct phytmac_hw_if *hw_if = pdata->hw_if; + int ret; + + pdata->mii_bus = mdiobus_alloc(); + if (!pdata->mii_bus) { + ret = -ENOMEM; + goto free_mdio; + } + + pdata->mii_bus->name = "phytmac_mii_bus"; + pdata->mii_bus->read = &phytmac_mdio_read; + pdata->mii_bus->write = &phytmac_mdio_write; + + if (pdata->platdev) { + snprintf(pdata->mii_bus->id, MII_BUS_ID_SIZE, "%s-%s", + pdata->mii_bus->name, netdev_name(pdata->ndev)); + } else if (pdata->pcidev) { + snprintf(pdata->mii_bus->id, MII_BUS_ID_SIZE, "%s-%s", + pdata->mii_bus->name, pci_name(pdata->pcidev)); + } else { + ret = -ENOMEM; + goto free_mdio; + } + + pdata->mii_bus->priv = pdata; + pdata->mii_bus->parent = pdata->dev; + + hw_if->enable_mdio_control(pdata, 1); + + return mdiobus_register(pdata->mii_bus); +free_mdio: + mdiobus_free(pdata->mii_bus); + return ret; +} + +static void phytmac_pcs_get_state(struct phylink_config *config, + struct phylink_link_state *state) +{ + struct phytmac *pdata = container_of(config, struct phytmac, phylink_config); + struct phytmac_hw_if *hw_if = pdata->hw_if; + + state->link = hw_if->get_link(pdata, state->interface); +} + +static void phytmac_validate(struct phylink_config *config, + unsigned long *supported, + struct phylink_link_state *state) +{ + struct net_device *ndev = to_net_dev(config->dev); + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; + struct phytmac *pdata = netdev_priv(ndev); + + if (state->interface != PHY_INTERFACE_MODE_SGMII && + state->interface != PHY_INTERFACE_MODE_2500BASEX && + state->interface != PHY_INTERFACE_MODE_5GBASER && + state->interface != PHY_INTERFACE_MODE_10GBASER && + state->interface != PHY_INTERFACE_MODE_USXGMII && + !phy_interface_mode_is_rgmii(state->interface)) { + bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); + return; + } + + phylink_set_port_modes(mask); + phylink_set(mask, Autoneg); + phylink_set(mask, Asym_Pause); + + if (state->interface == PHY_INTERFACE_MODE_10GBASER || + state->interface == PHY_INTERFACE_MODE_USXGMII) { + pdata->speed = state->speed; + pdata->duplex = state->duplex; + if (pdata->speed == SPEED_5000) { + phylink_set(mask, 5000baseT_Full); + } else { + phylink_set(mask, 10000baseCR_Full); + phylink_set(mask, 10000baseER_Full); + phylink_set(mask, 10000baseKR_Full); + phylink_set(mask, 10000baseLR_Full); + phylink_set(mask, 10000baseLRM_Full); + phylink_set(mask, 10000baseSR_Full); + phylink_set(mask, 10000baseT_Full); + } + } + + if (state->interface == PHY_INTERFACE_MODE_2500BASEX) + phylink_set(mask, 2500baseX_Full); + + if (state->interface == PHY_INTERFACE_MODE_5GBASER) + phylink_set(mask, 5000baseT_Full); + + if (state->interface == PHY_INTERFACE_MODE_1000BASEX || + state->interface == PHY_INTERFACE_MODE_SGMII || + phy_interface_mode_is_rgmii(state->interface)) { + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseX_Full); + phylink_set(mask, 1000baseT_Half); + phylink_set(mask, 10baseT_Half); + phylink_set(mask, 10baseT_Full); + phylink_set(mask, 100baseT_Half); + phylink_set(mask, 100baseT_Full); + } + + bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS); + bitmap_and(state->advertising, state->advertising, mask, + __ETHTOOL_LINK_MODE_MASK_NBITS); +} + +static const struct phylink_mac_ops phytmac_phylink_ops = { + .validate = phytmac_validate, + .mac_prepare = phytmac_mac_prepare, + .mac_config = phytmac_mac_config, + .mac_link_down = phytmac_mac_link_down, + .mac_link_up = phytmac_mac_link_up, +}; + +static int phytmac_phylink_create(struct phytmac *pdata) +{ + struct fwnode_handle *fw_node = dev_fwnode(pdata->dev); + + pdata->phylink_config.dev = &pdata->ndev->dev; + pdata->phylink_config.type = PHYLINK_NETDEV; + + if (pdata->phy_interface == PHY_INTERFACE_MODE_SGMII || + pdata->phy_interface == PHY_INTERFACE_MODE_1000BASEX || + pdata->phy_interface == PHY_INTERFACE_MODE_2500BASEX || + pdata->phy_interface == PHY_INTERFACE_MODE_USXGMII || + pdata->phy_interface == PHY_INTERFACE_MODE_10GBASER) { + pdata->phylink_config.poll_fixed_state = true; + pdata->phylink_config.get_fixed_state = phytmac_pcs_get_state; + pdata->phylink_pcs.ops = &phytmac_pcs_phylink_ops; + } + + pdata->phylink = phylink_create(&pdata->phylink_config, fw_node, + pdata->phy_interface, &phytmac_phylink_ops); + if (IS_ERR(pdata->phylink)) { + dev_err(pdata->dev, "Could not create a phylink instance (%ld)\n", + PTR_ERR(pdata->phylink)); + return PTR_ERR(pdata->phylink); + } + + return 0; +} + +static int phytmac_open(struct net_device *ndev) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct phytmac_queue *queue; + struct phytmac_hw_if *hw_if = pdata->hw_if; + unsigned int q = 0; + int ret; + + if (netif_msg_probe(pdata)) + dev_dbg(pdata->dev, "open\n"); + + /* phytmac_powerup */ + if (pdata->power_state == PHYTMAC_POWEROFF) + hw_if->poweron(pdata, PHYTMAC_POWERON); + + if (hw_if->init_msg_ring) + hw_if->init_msg_ring(pdata); + + ret = hw_if->get_feature(pdata); + if (ret) { + netdev_err(ndev, "phytmac get features failed\n"); + return ret; + } + + hw_if->reset_hw(pdata); + + ret = phytmac_get_mac_address(pdata); + if (ret) { + netdev_err(ndev, "phytmac get mad address failed\n"); + goto reset_hw; + } + + if (pdata->use_mii && !pdata->mii_bus) { + ret = phytmac_mdio_register(pdata); + if (ret) { + netdev_err(ndev, "MDIO bus registration failed\n"); + goto err_mdio_register; + } + } + + ret = phytmac_phylink_create(pdata); + if (ret) { + netdev_err(ndev, "phytmac phylink create failed(error %d)\n", ret); + goto err_mdio_register; + } + + ret = netif_set_real_num_tx_queues(ndev, pdata->queues_num); + if (ret) { + netdev_err(ndev, "error setting real tx queue number\n"); + return ret; + } + ret = netif_set_real_num_rx_queues(ndev, pdata->queues_num); + if (ret) { + netdev_err(ndev, "error setting real tx queue number\n"); + return ret; + } + + /* RX buffers initialization */ + ret = phytmac_alloc_resource(pdata); + if (ret) { + netdev_err(ndev, "Unable to allocate DMA memory (error %d)\n", + ret); + goto err_mdio_register; + } + + for (queue = pdata->queues; q < pdata->queues_num; ++q) { + napi_enable(&queue->tx_napi); + napi_enable(&queue->rx_napi); + ++queue; + } + + phytmac_init_ring(pdata); + hw_if->init_hw(pdata); + + ret = phytmac_phylink_connect(pdata); + if (ret) { + netdev_err(ndev, "phylink connet failed,(error %d)\n", + ret); + goto err_mdio_register; + } + + phylink_start(pdata->phylink); + + netif_tx_start_all_queues(pdata->ndev); + + if (IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) { + ret = phytmac_ptp_register(pdata); + if (ret) { + netdev_err(ndev, "ptp register failed, (error %d)\n", + ret); + goto err_mdio_register; + } + + phytmac_ptp_init(pdata->ndev); + } + + return 0; + +err_mdio_register: + if (pdata->mii_bus) + mdiobus_unregister(pdata->mii_bus); +reset_hw: + hw_if->reset_hw(pdata); + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q) { + napi_disable(&queue->tx_napi); + napi_disable(&queue->rx_napi); + ++queue; + } + phytmac_free_resource(pdata); + return ret; +} + +static int phytmac_close(struct net_device *ndev) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct phytmac_queue *queue; + struct phytmac_hw_if *hw_if = pdata->hw_if; + unsigned long flags; + unsigned int q; + + if (netif_msg_probe(pdata)) + dev_dbg(pdata->dev, "close"); + + netif_tx_stop_all_queues(ndev); + + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { + napi_disable(&queue->tx_napi); + napi_disable(&queue->rx_napi); + } + + phylink_stop(pdata->phylink); + phylink_disconnect_phy(pdata->phylink); + + netif_carrier_off(ndev); + + spin_lock_irqsave(&pdata->lock, flags); + hw_if->reset_hw(pdata); + spin_unlock_irqrestore(&pdata->lock, flags); + + phytmac_free_resource(pdata); + + if (IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) + phytmac_ptp_unregister(pdata); + + /* phytmac_powerup */ + if (pdata->power_state == PHYTMAC_POWERON) + hw_if->poweron(pdata, PHYTMAC_POWEROFF); + + return 0; +} + +static int phytmac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct phytmac *pdata = netdev_priv(dev); + int ret; + + if (!netif_running(dev)) + return -EINVAL; + + switch (cmd) { + case SIOCGMIIPHY: + case SIOCGMIIREG: + case SIOCSMIIREG: + ret = phylink_mii_ioctl(pdata->phylink, rq, cmd); + break; +#ifdef CONFIG_PHYTMAC_ENABLE_PTP + case SIOCSHWTSTAMP: + ret = phytmac_ptp_set_ts_config(dev, rq, cmd); + break; + case SIOCGHWTSTAMP: + ret = phytmac_ptp_get_ts_config(dev, rq); + break; +#endif + default: + break; + } + + return ret; +} + +static inline int phytmac_set_features(struct net_device *netdev, + netdev_features_t features) +{ + struct phytmac *pdata = netdev_priv(netdev); + struct phytmac_hw_if *hw_if = pdata->hw_if; + netdev_features_t changed = features ^ netdev->features; + + /* TX checksum offload */ + if (changed & NETIF_F_HW_CSUM) { + if (features & NETIF_F_HW_CSUM) + hw_if->enable_tx_csum(pdata, 1); + else + hw_if->enable_tx_csum(pdata, 0); + } + + /* RX checksum offload */ + if (changed & NETIF_F_RXCSUM) { + if (features & NETIF_F_RXCSUM && + !(netdev->flags & IFF_PROMISC)) + hw_if->enable_rx_csum(pdata, 1); + else + hw_if->enable_rx_csum(pdata, 0); + } + return 0; +} + +static netdev_features_t phytmac_features_check(struct sk_buff *skb, + struct net_device *dev, + netdev_features_t features) +{ + unsigned int nr_frags, f; + unsigned int hdrlen; + + /* there is only one buffer or protocol is not UDP */ + if (!skb_is_nonlinear(skb) || (ip_hdr(skb)->protocol != IPPROTO_UDP)) + return features; + + /* length of header */ + hdrlen = skb_transport_offset(skb); + + if (!IS_ALIGNED(skb_headlen(skb) - hdrlen, PHYTMAC_TX_LEN_ALIGN)) + return features & ~NETIF_F_TSO; + + nr_frags = skb_shinfo(skb)->nr_frags; + /* No need to check last fragment */ + nr_frags--; + for (f = 0; f < nr_frags; f++) { + const skb_frag_t *frag = &skb_shinfo(skb)->frags[f]; + + if (!IS_ALIGNED(skb_frag_size(frag), PHYTMAC_TX_LEN_ALIGN)) + return features & ~NETIF_F_TSO; + } + return features; +} + +int phytmac_reset_ringsize(struct phytmac *pdata, u32 rx_size, u32 tx_size) +{ + int ret = 0; + int reset = 0; + + if (netif_running(pdata->ndev)) { + reset = 1; + phytmac_close(pdata->ndev); + } + + pdata->rx_ring_size = rx_size; + pdata->tx_ring_size = tx_size; + + if (reset) + phytmac_open(pdata->ndev); + + return ret; +} + +static const struct net_device_ops phytmac_netdev_ops = { + .ndo_open = phytmac_open, + .ndo_stop = phytmac_close, + .ndo_start_xmit = phytmac_start_xmit, + .ndo_set_rx_mode = phytmac_set_rx_mode, + .ndo_get_stats = phytmac_get_stats, + .ndo_do_ioctl = phytmac_ioctl, + .ndo_validate_addr = eth_validate_addr, + .ndo_change_mtu = phytmac_change_mtu, + .ndo_set_mac_address = phytmac_set_mac_address, + .ndo_set_features = phytmac_set_features, + .ndo_features_check = phytmac_features_check, + .ndo_vlan_rx_add_vid = ncsi_vlan_rx_add_vid, + .ndo_vlan_rx_kill_vid = ncsi_vlan_rx_kill_vid, +}; + +static int phytmac_init(struct phytmac *pdata) +{ + struct net_device *ndev = pdata->ndev; + unsigned int q; + struct phytmac_queue *queue; + int ret; + + if (netif_msg_probe(pdata)) + dev_dbg(pdata->dev, "phytmac init !\n"); + + spin_lock_init(&pdata->lock); + + /* set the queue register mapping once for all: queue0 has a special + * register mapping but we don't want to test the queue index then + * compute the corresponding register offset at run time. + */ + for (q = 0; q < pdata->queues_num; ++q) { + queue = &pdata->queues[q]; + queue->pdata = pdata; + queue->index = q; + spin_lock_init(&queue->tx_lock); + + netif_napi_add(ndev, &queue->tx_napi, phytmac_tx_poll, NAPI_POLL_WEIGHT); + netif_napi_add(ndev, &queue->rx_napi, phytmac_rx_poll, NAPI_POLL_WEIGHT); + + if (pdata->irq_type == IRQ_TYPE_INT || pdata->irq_type == IRQ_TYPE_MSI) { + queue->irq = pdata->queue_irq[q]; + if (pdata->irq_type == IRQ_TYPE_INT) + ret = devm_request_irq(pdata->dev, queue->irq, phytmac_irq, + IRQF_SHARED, ndev->name, queue); + else + ret = devm_request_irq(pdata->dev, queue->irq, phytmac_irq, + 0, ndev->name, queue); + + if (ret) { + dev_err(pdata->dev, + "Unable to request IRQ %d (error %d)\n", + queue->irq, ret); + return ret; + } + } + } + + if (pdata->irq_type == IRQ_TYPE_INTX) { + ret = devm_request_irq(pdata->dev, pdata->queue_irq[0], phytmac_intx_irq, + IRQF_SHARED, ndev->name, pdata); + if (ret) { + dev_err(pdata->dev, + "Unable to request INTX IRQ %d (error %d)\n", + pdata->queue_irq[0], ret); + return ret; + } + } + + ndev->netdev_ops = &phytmac_netdev_ops; + phytmac_set_ethtool_ops(ndev); + eth_hw_addr_random(pdata->ndev); + + if (ndev->hw_features & NETIF_F_NTUPLE) { + INIT_LIST_HEAD(&pdata->rx_fs_list.list); + pdata->rx_fs_list.count = 0; + spin_lock_init(&pdata->rx_fs_lock); + } + + return 0; +} + +void phytmac_default_config(struct phytmac *pdata) +{ + struct net_device *ndev = pdata->ndev; + + pdata->rx_irq_mask = PHYTMAC_RX_INT_FLAGS; + pdata->tx_irq_mask = PHYTMAC_TX_INT_FLAGS; + pdata->tx_ring_size = DEFAULT_TX_RING_SIZE; + pdata->rx_ring_size = DEFAULT_RX_RING_SIZE; + pdata->max_tx_length = PHYTMAC_MAX_TX_LEN; + pdata->min_tx_length = PHYTMAC_MIN_TX_LEN; + pdata->pause = true; + + ndev->hw_features = NETIF_F_SG; + + if (pdata->capacities & PHYTMAC_CAPS_LSO) + ndev->hw_features |= NETIF_F_TSO; + + if (pdata->use_ncsi) { + ndev->hw_features &= ~(NETIF_F_HW_CSUM | NETIF_F_RXCSUM); + ndev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER; + } else { + ndev->hw_features |= NETIF_F_HW_CSUM | NETIF_F_RXCSUM; + } + + if (pdata->capacities & PHYTMAC_CAPS_SG_DISABLED) + ndev->hw_features &= ~NETIF_F_SG; + + ndev->hw_features |= NETIF_F_NTUPLE; + + ndev->min_mtu = ETH_MIN_MTU; + if (pdata->capacities & PHYTMAC_CAPS_JUMBO) + ndev->max_mtu = pdata->jumbo_len - ETH_HLEN - ETH_FCS_LEN; + else + ndev->max_mtu = ETH_DATA_LEN; + + ndev->features = ndev->hw_features; +} + +static void phytmac_ncsi_handler(struct ncsi_dev *nd) +{ + if (unlikely(nd->state != ncsi_dev_state_functional)) + return; + + netdev_dbg(nd->dev, "NCSI interface %s\n", + nd->link_up ? "up" : "down"); +} + +int phytmac_drv_probe(struct phytmac *pdata) +{ + struct net_device *ndev = pdata->ndev; + struct device *dev = pdata->dev; + int ret = 0; + + if (netif_msg_probe(pdata)) + dev_dbg(pdata->dev, "phytmac drv probe start\n"); + + phytmac_default_config(pdata); + + if (dma_set_mask(dev, DMA_BIT_MASK(40)) || + dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40))) { + dev_err(dev, "dma_set_mask or coherent failed\n"); + return 1; + } + + ret = phytmac_init(pdata); + if (ret) + goto err_out_free_netdev; + + if (pdata->use_ncsi) { + pdata->ncsidev = ncsi_register_dev(ndev, phytmac_ncsi_handler); + if (!pdata->ncsidev) + goto err_out_free_netdev; + } + + netif_carrier_off(ndev); + ret = register_netdev(ndev); + if (ret) { + dev_err(pdata->dev, "Cannot register net device, aborting.\n"); + goto err_out_free_netdev; + } + + if (netif_msg_probe(pdata)) + dev_dbg(pdata->dev, "probe success!Phytium %s at 0x%08lx irq %d (%pM)\n", + "MAC", ndev->base_addr, ndev->irq, ndev->dev_addr); + + return 0; + +err_out_free_netdev: + free_netdev(ndev); + + return ret; +} +EXPORT_SYMBOL_GPL(phytmac_drv_probe); + +int phytmac_drv_remove(struct phytmac *pdata) +{ + struct net_device *ndev = pdata->ndev; + + if (ndev) { + if (pdata->use_ncsi && pdata->ncsidev) + ncsi_unregister_dev(pdata->ncsidev); + + unregister_netdev(ndev); + + if (pdata->use_mii && pdata->mii_bus) { + mdiobus_unregister(pdata->mii_bus); + mdiobus_free(pdata->mii_bus); + } + + if (pdata->phylink) + phylink_destroy(pdata->phylink); + } + + return 0; +} +EXPORT_SYMBOL_GPL(phytmac_drv_remove); + +int phytmac_drv_suspend(struct phytmac *pdata) +{ + int q; + unsigned long flags; + struct phytmac_queue *queue; + struct phytmac_hw_if *hw_if = pdata->hw_if; + + if (!netif_running(pdata->ndev)) + return 0; + + if (pdata->power_state == PHYTMAC_POWEROFF) + return 0; + + netif_carrier_off(pdata->ndev); + netif_device_detach(pdata->ndev); + + /* napi_disable */ + for (q = 0, queue = pdata->queues; q < pdata->queues_num; + ++q, ++queue) { + napi_disable(&queue->tx_napi); + napi_disable(&queue->rx_napi); + } + + if (pdata->wol) { + hw_if->set_wol(pdata, pdata->wol); + } else { + rtnl_lock(); + phylink_stop(pdata->phylink); + rtnl_unlock(); + spin_lock_irqsave(&pdata->lock, flags); + hw_if->reset_hw(pdata); + hw_if->poweron(pdata, PHYTMAC_POWEROFF); + spin_unlock_irqrestore(&pdata->lock, flags); + } + + return 0; +} +EXPORT_SYMBOL_GPL(phytmac_drv_suspend); + +int phytmac_drv_resume(struct phytmac *pdata) +{ + int q; + struct phytmac_queue *queue; + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct ethtool_rx_fs_item *item; + + if (!netif_running(pdata->ndev)) + return 0; + + if (pdata->power_state == PHYTMAC_POWEROFF) + hw_if->poweron(pdata, PHYTMAC_POWERON); + + if (hw_if->init_msg_ring) + hw_if->init_msg_ring(pdata); + + if (pdata->wol) { + hw_if->set_wol(pdata, 0); + rtnl_lock(); + phylink_stop(pdata->phylink); + rtnl_unlock(); + } + + for (q = 0, queue = pdata->queues; q < pdata->queues_num; + ++q, ++queue) { + napi_enable(&queue->tx_napi); + napi_enable(&queue->rx_napi); + } + + hw_if->init_hw(pdata); + phytmac_set_rx_mode(pdata->ndev); + phytmac_set_features(pdata->ndev, pdata->ndev->features); + list_for_each_entry(item, &pdata->rx_fs_list.list, list) + hw_if->add_fdir_entry(pdata, &item->fs); + + rtnl_lock(); + phylink_start(pdata->phylink); + rtnl_unlock(); + + netif_device_attach(pdata->ndev); + + return 0; +} +EXPORT_SYMBOL_GPL(phytmac_drv_resume); + +struct phytmac *phytmac_alloc_pdata(struct device *dev) +{ + struct phytmac *pdata; + struct net_device *netdev; + + netdev = alloc_etherdev_mq(sizeof(struct phytmac), + PHYTMAC_MAX_QUEUES); + if (!netdev) { + dev_err(dev, "alloc_etherdev_mq failed\n"); + return ERR_PTR(-ENOMEM); + } + SET_NETDEV_DEV(netdev, dev); + pdata = netdev_priv(netdev); + pdata->ndev = netdev; + pdata->dev = dev; + + spin_lock_init(&pdata->lock); + spin_lock_init(&pdata->msg_lock); + spin_lock_init(&pdata->ts_clk_lock); + pdata->msg_enable = netif_msg_init(debug, PHYTMAC_DEFAULT_MSG_ENABLE); + + return pdata; +} +EXPORT_SYMBOL_GPL(phytmac_alloc_pdata); + +void phytmac_free_pdata(struct phytmac *pdata) +{ + struct net_device *netdev = pdata->ndev; + + free_netdev(netdev); +} +EXPORT_SYMBOL_GPL(phytmac_free_pdata); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Phytium Ethernet driver"); +MODULE_AUTHOR("Wenting Song"); +MODULE_ALIAS("platform:phytmac"); + diff --git a/drivers/net/ethernet/phytium/phytmac_pci.c b/drivers/net/ethernet/phytium/phytmac_pci.c new file mode 100644 index 0000000000000000000000000000000000000000..5cfc63f130a274246d20648c228080d87c97d058 --- /dev/null +++ b/drivers/net/ethernet/phytium/phytmac_pci.c @@ -0,0 +1,317 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Phytium GMAC PCI wrapper. + */ + +#include +#include +#include "phytmac.h" +#include "phytmac_v1.h" +#include "phytmac_v2.h" + +#define PCI_DEVICE_ID_GMAC 0xDC3B +#define PCI_SUBDEVICE_ID_SGMII 0x1000 +#define PCI_SUBDEVICE_ID_1000BASEX 0x1001 +#define PCI_SUBDEVICE_ID_2500BASEX 0x1002 +#define PCI_SUBDEVICE_ID_5GBASER 0x1003 +#define PCI_SUBDEVICE_ID_USXGMII 0x1004 +#define PCI_SUBDEVICE_ID_10GBASER 0x1005 + +struct phytmac_data { + struct phytmac_hw_if *hw_if; + u32 caps; + u32 tsu_rate; + u16 queue_num; + int speed; + bool duplex; + bool use_mii; + bool use_ncsi; + phy_interface_t interface; + const struct property_entry *properties; +}; + +static const u32 fixedlink[][5] = { + {0, 1, 1000, 1, 0}, + {0, 1, 2500, 1, 0}, + {0, 1, 5000, 1, 0}, + {0, 1, 10000, 1, 0}, +}; + +static const struct property_entry fl_properties[][2] = { + {PROPERTY_ENTRY_U32_ARRAY("fixed-link", fixedlink[0]), {}}, + {PROPERTY_ENTRY_U32_ARRAY("fixed-link", fixedlink[1]), {}}, + {PROPERTY_ENTRY_U32_ARRAY("fixed-link", fixedlink[2]), {}}, + {PROPERTY_ENTRY_U32_ARRAY("fixed-link", fixedlink[3]), {}}, +}; + +static int phytmac_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct phytmac_data *data = (struct phytmac_data *)id->driver_data; + struct phytmac *pdata; + struct device *dev = &pdev->dev; + void __iomem * const *iomap_table; + struct fwnode_handle *fw_node = NULL; + int bar_mask; + int ret, i; + + pdata = phytmac_alloc_pdata(dev); + if (IS_ERR(pdata)) { + ret = PTR_ERR(pdata); + goto err_alloc; + } + + pdata->pcidev = pdev; + pci_set_drvdata(pdev, pdata); + + ret = pcim_enable_device(pdev); + if (ret) { + dev_err(dev, "pcim_enable_device failed\n"); + goto err_pci_enable; + } + + /* Obtain the mmio areas for the device */ + bar_mask = pci_select_bars(pdev, IORESOURCE_MEM); + ret = pcim_iomap_regions(pdev, bar_mask, PHYTMAC_DRV_NAME); + if (ret) { + dev_err(dev, "pcim_iomap_regions failed\n"); + goto err_pci_enable; + } + + iomap_table = pcim_iomap_table(pdev); + if (!iomap_table) { + dev_err(dev, "pcim_iomap_table failed\n"); + ret = -ENOMEM; + goto err_pci_enable; + } + + pdata->mac_regs = iomap_table[0]; + if (!pdata->mac_regs) { + dev_err(dev, "xgmac ioremap failed\n"); + ret = -ENOMEM; + goto err_pci_enable; + } + + pdata->msg_regs = iomap_table[1]; + if (!pdata->msg_regs) { + dev_err(dev, "xpcs ioremap failed\n"); + ret = -ENOMEM; + goto err_pci_enable; + } + + pci_set_master(pdev); + + /* para */ + pdata->dma_burst_length = DEFAULT_DMA_BURST_LENGTH; + pdata->jumbo_len = DEFAULT_DMA_BURST_LENGTH; + pdata->wol |= PHYTMAC_WAKE_MAGIC; + pdata->use_ncsi = data->use_ncsi; + pdata->use_mii = data->use_mii; + pdata->phy_interface = data->interface; + pdata->queues_num = data->queue_num; + pdata->capacities = data->caps; + pdata->hw_if = data->hw_if; + + if (!pdata->use_mii) { + fw_node = fwnode_create_software_node(data->properties, NULL); + if (IS_ERR(fw_node)) { + dev_err(dev, "Failed to create software node\n"); + goto err_pci_enable; + } + pdata->dev->fwnode = fw_node; + } + + /* irq */ + ret = pci_alloc_irq_vectors(pdev, pdata->queues_num, pdata->queues_num, PCI_IRQ_MSI); + if (ret < 0) { + pdata->irq_type = IRQ_TYPE_INTX; + pdata->queue_irq[0] = pdev->irq; + } else { + pdata->irq_type = IRQ_TYPE_MSI; + for (i = 0; i < pdata->queues_num; i++) + pdata->queue_irq[i] = pci_irq_vector(pdev, i); + } + + /* Configure the netdev resource */ + ret = phytmac_drv_probe(pdata); + if (ret) + goto err_irq_vectors; + + netdev_notice(pdata->ndev, "net device enabled\n"); + + return 0; + +err_irq_vectors: + if (fw_node) + fwnode_remove_software_node(fw_node); + pci_free_irq_vectors(pdata->pcidev); + +err_pci_enable: + phytmac_free_pdata(pdata); + +err_alloc: + dev_notice(dev, "net device not enabled\n"); + + return ret; +} + +static void phytmac_pci_remove(struct pci_dev *pdev) +{ + struct phytmac *pdata = pci_get_drvdata(pdev); + struct fwnode_handle *fw_node = dev_fwnode(pdata->dev); + int i = 0; + int bar_mask; + + if (fw_node) { + fwnode_remove_software_node(fw_node); + pdata->dev->fwnode = NULL; + } + + phytmac_drv_remove(pdata); + + for (i = 0; i < pdata->queues_num; i++) + free_irq(pci_irq_vector(pdev, i), &pdata->queues[i]); + pci_free_irq_vectors(pdev); + + phytmac_free_pdata(pdata); + bar_mask = pci_select_bars(pdev, IORESOURCE_MEM); + pcim_iounmap_regions(pdev, bar_mask); + + pci_disable_device(pdev); +} + +static int __maybe_unused phytmac_pci_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct phytmac *pdata = pci_get_drvdata(pdev); + int ret; + + ret = phytmac_drv_suspend(pdata); + + return ret; +} + +static int __maybe_unused phytmac_pci_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct phytmac *pdata = pci_get_drvdata(pdev); + int ret; + + ret = phytmac_drv_resume(pdata); + + return ret; +} + +struct phytmac_data phytmac_sgmii = { + .hw_if = &phytmac_1p0_hw, + .caps = PHYTMAC_CAPS_TAILPTR + | PHYTMAC_CAPS_START + | PHYTMAC_CAPS_JUMBO + | PHYTMAC_CAPS_LSO, + .queue_num = 4, + .use_ncsi = false, + .use_mii = true, + .interface = PHY_INTERFACE_MODE_SGMII, +}; + +struct phytmac_data phytmac_1000basex = { + .hw_if = &phytmac_1p0_hw, + .caps = PHYTMAC_CAPS_TAILPTR + | PHYTMAC_CAPS_START + | PHYTMAC_CAPS_JUMBO + | PHYTMAC_CAPS_LSO, + .queue_num = 4, + .use_ncsi = false, + .use_mii = false, + .speed = 1000, + .duplex = true, + .interface = PHY_INTERFACE_MODE_SGMII, + .properties = fl_properties[0], +}; + +struct phytmac_data phytmac_2500basex = { + .hw_if = &phytmac_1p0_hw, + .caps = PHYTMAC_CAPS_TAILPTR + | PHYTMAC_CAPS_START + | PHYTMAC_CAPS_JUMBO + | PHYTMAC_CAPS_LSO, + .queue_num = 4, + .use_ncsi = false, + .use_mii = false, + .speed = 2500, + .duplex = true, + .interface = PHY_INTERFACE_MODE_2500BASEX, + .properties = fl_properties[1], +}; + +struct phytmac_data phytmac_5000baser = { + .hw_if = &phytmac_1p0_hw, + .caps = PHYTMAC_CAPS_TAILPTR + | PHYTMAC_CAPS_START + | PHYTMAC_CAPS_JUMBO + | PHYTMAC_CAPS_LSO, + .queue_num = 4, + .use_ncsi = false, + .use_mii = false, + .speed = 5000, + .duplex = true, + .interface = PHY_INTERFACE_MODE_5GBASER, + .properties = fl_properties[2], +}; + +struct phytmac_data phytmac_usxgmii = { + .hw_if = &phytmac_1p0_hw, + .caps = PHYTMAC_CAPS_TAILPTR + | PHYTMAC_CAPS_START + | PHYTMAC_CAPS_JUMBO + | PHYTMAC_CAPS_LSO, + .queue_num = 4, + .use_ncsi = false, + .use_mii = false, + .speed = 10000, + .duplex = true, + .interface = PHY_INTERFACE_MODE_USXGMII, + .properties = fl_properties[3], +}; + +static const struct pci_device_id phytmac_pci_table[] = { + { PCI_DEVICE_SUB(PCI_VENDOR_ID_PHYTIUM, PCI_DEVICE_ID_GMAC, + PCI_VENDOR_ID_PHYTIUM, PCI_SUBDEVICE_ID_SGMII), + .driver_data = (kernel_ulong_t)&phytmac_sgmii}, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_PHYTIUM, PCI_DEVICE_ID_GMAC, + PCI_VENDOR_ID_PHYTIUM, PCI_SUBDEVICE_ID_1000BASEX), + .driver_data = (kernel_ulong_t)&phytmac_1000basex}, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_PHYTIUM, PCI_DEVICE_ID_GMAC, + PCI_VENDOR_ID_PHYTIUM, PCI_SUBDEVICE_ID_2500BASEX), + .driver_data = (kernel_ulong_t)&phytmac_2500basex}, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_PHYTIUM, PCI_DEVICE_ID_GMAC, + PCI_VENDOR_ID_PHYTIUM, PCI_SUBDEVICE_ID_5GBASER), + .driver_data = (kernel_ulong_t)&phytmac_5000baser}, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_PHYTIUM, PCI_DEVICE_ID_GMAC, + PCI_VENDOR_ID_PHYTIUM, PCI_SUBDEVICE_ID_USXGMII), + .driver_data = (kernel_ulong_t)&phytmac_usxgmii}, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_PHYTIUM, PCI_DEVICE_ID_GMAC, + PCI_VENDOR_ID_PHYTIUM, PCI_SUBDEVICE_ID_10GBASER), + .driver_data = (kernel_ulong_t)&phytmac_usxgmii}, + /* Last entry must be zero */ + { 0, } +}; +MODULE_DEVICE_TABLE(pci, phytmac_pci_table); + +static const struct dev_pm_ops phytmac_pci_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(phytmac_pci_suspend, phytmac_pci_resume) +}; + +static struct pci_driver phytmac_driver = { + .name = PHYTMAC_DRV_NAME, + .id_table = phytmac_pci_table, + .probe = phytmac_pci_probe, + .remove = phytmac_pci_remove, + .driver = { + .pm = &phytmac_pci_pm_ops, + } +}; + +module_pci_driver(phytmac_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Phytium NIC PCI wrapper"); diff --git a/drivers/net/ethernet/phytium/phytmac_platform.c b/drivers/net/ethernet/phytium/phytmac_platform.c new file mode 100644 index 0000000000000000000000000000000000000000..2b3778386ce72b28d9ca7b9762c027a8485e019e --- /dev/null +++ b/drivers/net/ethernet/phytium/phytmac_platform.c @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Phytium GMAC Platform wrapper. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include +#include +#include +#include +#include +#include "phytmac.h" +#include "phytmac_v1.h" +#include "phytmac_v2.h" + +static const struct phytmac_config phytium_1p0_config = { + .hw_if = &phytmac_1p0_hw, + .caps = PHYTMAC_CAPS_TAILPTR + | PHYTMAC_CAPS_START + | PHYTMAC_CAPS_JUMBO + | PHYTMAC_CAPS_LSO, + .queue_num = 4, + .tsu_rate = 300000000, +}; + +static const struct phytmac_config phytium_2p0_config = { + .hw_if = &phytmac_2p0_hw, + .caps = PHYTMAC_CAPS_TAILPTR + | PHYTMAC_CAPS_LPI + | PHYTMAC_CAPS_LSO + | PHYTMAC_CAPS_MSG + | PHYTMAC_CAPS_JUMBO, + .queue_num = 2, + .tsu_rate = 300000000, +}; + +#if defined(CONFIG_OF) +static const struct of_device_id phytmac_dt_ids[] = { + { .compatible = "phytium,gmac-1.0", .data = &phytium_1p0_config }, + { .compatible = "phytium,gmac-2.0", .data = &phytium_2p0_config }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, phytmac_dt_ids); +#endif /* CONFIG_OF */ + +#ifdef CONFIG_ACPI +static const struct acpi_device_id phytmac_acpi_ids[] = { + { .id = "PHYT0046", .driver_data = (kernel_ulong_t)&phytium_1p0_config }, + { } +}; + +MODULE_DEVICE_TABLE(acpi, phytmac_acpi_ids); +#else +#define phytmac_acpi_ids NULL +#endif + +static int phytmac_get_phy_mode(struct platform_device *pdev) +{ + const char *pm; + int err, i; + + err = device_property_read_string(&pdev->dev, "phy-mode", &pm); + if (err < 0) + return err; + + for (i = 0; i < PHY_INTERFACE_MODE_MAX; i++) { + if (!strcasecmp(pm, phy_modes(i))) + return i; + } + + return -ENODEV; +} + +static int phytmac_plat_probe(struct platform_device *pdev) +{ + const struct phytmac_config *phytmac_config = &phytium_1p0_config; + struct device_node *np = pdev->dev.of_node; + struct resource *regs; + struct phytmac *pdata; + int ret, i; + u32 queue_num; + + pdata = phytmac_alloc_pdata(&pdev->dev); + if (IS_ERR(pdata)) { + ret = PTR_ERR(pdata); + goto err_alloc; + } + + platform_set_drvdata(pdev, pdata); + + pdata->platdev = pdev; + + if (pdev->dev.of_node) { + const struct of_device_id *match; + + match = of_match_node(phytmac_dt_ids, np); + if (match && match->data) { + phytmac_config = match->data; + pdata->hw_if = phytmac_config->hw_if; + pdata->capacities = phytmac_config->caps; + pdata->queues_max_num = phytmac_config->queue_num; + } + } else if (has_acpi_companion(&pdev->dev)) { + const struct acpi_device_id *match; + + match = acpi_match_device(phytmac_acpi_ids, &pdev->dev); + if (match && match->driver_data) { + phytmac_config = (void *)match->driver_data; + pdata->hw_if = phytmac_config->hw_if; + pdata->capacities = phytmac_config->caps; + pdata->queues_max_num = phytmac_config->queue_num; + } + } + + i = 0; + pdata->mac_regs = devm_platform_get_and_ioremap_resource(pdev, i, ®s); + if (IS_ERR(pdata->mac_regs)) { + dev_err(&pdev->dev, "mac_regs ioremap failed\n"); + ret = PTR_ERR(pdata->mac_regs); + goto err_mem; + } + pdata->ndev->base_addr = regs->start; + + if (pdata->capacities & PHYTMAC_CAPS_MSG) { + ++i; + regs = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (regs) { + pdata->msg_regs = ioremap_wt(regs->start, MEMORY_SIZE); + if (!pdata->msg_regs) { + dev_err(&pdev->dev, "msg_regs ioremap failed, i=%d\n", i); + goto err_mem; + } + } + } + + if (device_property_read_bool(&pdev->dev, "lpi")) + pdata->capacities |= PHYTMAC_CAPS_LPI; + + if (pdata->capacities & PHYTMAC_CAPS_LPI) { + /* lpi resource */ + ++i; + regs = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (regs) { + pdata->mhu_regs = ioremap(regs->start, MHU_SIZE); + if (!pdata->mhu_regs) + dev_err(&pdev->dev, "mhu_regs ioremap failed, i=%d\n", i); + } + } + + if (device_property_read_u32(&pdev->dev, "dma-burst-length", &pdata->dma_burst_length)) + pdata->dma_burst_length = DEFAULT_DMA_BURST_LENGTH; + + if (device_property_read_u32(&pdev->dev, "jumbo-max-length", &pdata->jumbo_len)) + pdata->jumbo_len = DEFAULT_JUMBO_MAX_LENGTH; + + if (device_property_read_u32(&pdev->dev, "queue-number", &queue_num)) + pdata->queues_num = pdata->queues_max_num; + else + pdata->queues_num = queue_num; + + pdata->wol = 0; + if (device_property_read_bool(&pdev->dev, "magic-packet")) + pdata->wol |= WAKE_MAGIC; + + pdata->use_ncsi = device_property_read_bool(&pdev->dev, "use-ncsi"); + pdata->use_mii = device_property_read_bool(&pdev->dev, "use-mii"); + + pdata->power_state = PHYTMAC_POWEROFF; + + device_set_wakeup_capable(&pdev->dev, pdata->wol & PHYTMAC_WOL_MAGIC_PACKET); + + for (i = 0; i < pdata->queues_num; i++) { + pdata->irq_type = IRQ_TYPE_INT; + pdata->queue_irq[i] = platform_get_irq(pdev, i); + } + + ret = phytmac_get_phy_mode(pdev); + if (ret < 0) + pdata->phy_interface = PHY_INTERFACE_MODE_MII; + else + pdata->phy_interface = ret; + + ret = phytmac_drv_probe(pdata); + if (ret) + goto err_mem; + + if (netif_msg_probe(pdata)) { + dev_notice(&pdev->dev, "phytium net device enabled\n"); + dev_dbg(pdata->dev, "use_ncsi:%d, use_mii:%d, wol:%d, queues_num:%d\n", + pdata->use_ncsi, pdata->use_mii, pdata->wol, pdata->queues_num); + } + + return 0; + +err_mem: + phytmac_free_pdata(pdata); + +err_alloc: + dev_err(&pdev->dev, "phytium net device not enabled\n"); + + return ret; +} + +static int phytmac_plat_remove(struct platform_device *pdev) +{ + struct phytmac *pdata = platform_get_drvdata(pdev); + + phytmac_drv_remove(pdata); + phytmac_free_pdata(pdata); + + return 0; +} + +static int __maybe_unused phytmac_plat_suspend(struct device *dev) +{ + struct phytmac *pdata = dev_get_drvdata(dev); + int ret; + + ret = phytmac_drv_suspend(pdata); + + return ret; +} + +static int __maybe_unused phytmac_plat_resume(struct device *dev) +{ + struct phytmac *pdata = dev_get_drvdata(dev); + int ret; + + ret = phytmac_drv_resume(pdata); + + return ret; +} + +static const struct dev_pm_ops phytmac_plat_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(phytmac_plat_suspend, phytmac_plat_resume) +}; + +static struct platform_driver phytmac_driver = { + .probe = phytmac_plat_probe, + .remove = phytmac_plat_remove, + .driver = { + .name = PHYTMAC_DRV_NAME, + .of_match_table = of_match_ptr(phytmac_dt_ids), + .acpi_match_table = phytmac_acpi_ids, + .pm = &phytmac_plat_pm_ops, + }, +}; + +module_platform_driver(phytmac_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Phytium Ethernet driver"); +MODULE_AUTHOR("Wenting Song"); +MODULE_ALIAS("platform:phytmac"); diff --git a/drivers/net/ethernet/phytium/phytmac_ptp.c b/drivers/net/ethernet/phytium/phytmac_ptp.c new file mode 100644 index 0000000000000000000000000000000000000000..26b1b75edbde1ded359a5e6fbef51c3306dc7379 --- /dev/null +++ b/drivers/net/ethernet/phytium/phytmac_ptp.c @@ -0,0 +1,304 @@ +// SPDX-License-Identifier: GPL-2.0-only +/** + * 1588 PTP support for Phytium GMAC device. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "phytmac.h" +#include "phytmac_ptp.h" + +bool phytmac_ptp_one_step(struct sk_buff *skb) +{ + struct ptp_header *hdr; + unsigned int ptp_class; + u8 msgtype; + + /* No need to parse packet if PTP TS is not involved */ + if (likely(!(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP))) + goto not_oss; + + /* Identify and return whether PTP one step sync is being processed */ + ptp_class = ptp_classify_raw(skb); + if (ptp_class == PTP_CLASS_NONE) + goto not_oss; + + hdr = ptp_parse_header(skb, ptp_class); + if (!hdr) + goto not_oss; + + if (hdr->flag_field[0] & 0x2) + goto not_oss; + + msgtype = ptp_get_msgtype(hdr, ptp_class); + if (msgtype == PTP_MSGTYPE_SYNC) + return true; + +not_oss: + return false; +} + +int phytmac_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) +{ + struct phytmac *pdata = container_of(ptp, struct phytmac, ptp_clock_info); + struct phytmac_hw_if *hw_if = pdata->hw_if; + unsigned long flags; + + spin_lock_irqsave(&pdata->ts_clk_lock, flags); + hw_if->get_time(pdata, ts); + spin_unlock_irqrestore(&pdata->ts_clk_lock, flags); + + return 0; +} + +int phytmac_ptp_settime(struct ptp_clock_info *ptp, + const struct timespec64 *ts) +{ + struct phytmac *pdata = container_of(ptp, struct phytmac, ptp_clock_info); + struct phytmac_hw_if *hw_if = pdata->hw_if; + unsigned long flags; + + spin_lock_irqsave(&pdata->ts_clk_lock, flags); + hw_if->set_time(pdata, ts->tv_sec, ts->tv_nsec); + spin_unlock_irqrestore(&pdata->ts_clk_lock, flags); + + return 0; +} + +int phytmac_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) +{ + struct phytmac *pdata = container_of(ptp, struct phytmac, ptp_clock_info); + struct phytmac_hw_if *hw_if = pdata->hw_if; + bool negative = false; + + if (scaled_ppm < 0) { + negative = true; + scaled_ppm = -scaled_ppm; + } + + hw_if->adjust_fine(pdata, scaled_ppm, negative); + return 0; +} + +int phytmac_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) +{ + struct phytmac *pdata = container_of(ptp, struct phytmac, ptp_clock_info); + struct phytmac_hw_if *hw_if = pdata->hw_if; + int negative = 0; + + if (delta < 0) { + negative = 1; + delta = -delta; + } + + spin_lock_irq(&pdata->ts_clk_lock); + hw_if->adjust_time(pdata, delta, negative); + spin_unlock_irq(&pdata->ts_clk_lock); + + return 0; +} + +int phytmac_ptp_enable(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, int on) +{ + return -EOPNOTSUPP; +} + +void phytmac_ptp_init_timer(struct phytmac *pdata) +{ + struct phytmac_hw_if *hw_if = pdata->hw_if; + u32 rem = 0; + u64 adj; + + pdata->ts_rate = hw_if->get_ts_rate(pdata); + pdata->ts_incr.ns = div_u64_rem(NSEC_PER_SEC, pdata->ts_rate, &rem); + if (rem) { + adj = rem; + adj <<= 24; + pdata->ts_incr.sub_ns = div_u64(adj, pdata->ts_rate); + } else { + pdata->ts_incr.sub_ns = 0; + } +} + +void phytmac_ptp_rxstamp(struct phytmac *pdata, struct sk_buff *skb, + struct phytmac_dma_desc *desc) +{ + struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb); + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct timespec64 ts; + + if (hw_if->ts_valid(pdata, desc, PHYTMAC_RX)) { + hw_if->get_timestamp(pdata, desc->desc4, desc->desc5, &ts); + memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps)); + shhwtstamps->hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec); + } +} + +int phytmac_ptp_txstamp(struct phytmac_queue *queue, struct sk_buff *skb, + struct phytmac_dma_desc *desc) +{ + struct phytmac *pdata = queue->pdata; + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct timespec64 ts; + struct skb_shared_hwtstamps shhwtstamps; + + if (queue->pdata->ts_config.tx_type == TS_DISABLED) + return -EOPNOTSUPP; + + if (!hw_if->ts_valid(pdata, desc, PHYTMAC_TX)) + return -EINVAL; + + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + hw_if->get_timestamp(pdata, desc->desc4, desc->desc5, &ts); + memset(&shhwtstamps, 0, sizeof(shhwtstamps)); + shhwtstamps.hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec); + skb_tstamp_tx(skb, &shhwtstamps); + + return 0; +} + +int phytmac_ptp_register(struct phytmac *pdata) +{ + pdata->ptp_clock_info.owner = THIS_MODULE; + snprintf(pdata->ptp_clock_info.name, 16, "%s", pdata->ndev->name); + pdata->ptp_clock_info.max_adj = 64000000; /* In PPB */ + pdata->ptp_clock_info.n_alarm = 0; + pdata->ptp_clock_info.n_ext_ts = 0; + pdata->ptp_clock_info.n_per_out = 0; + pdata->ptp_clock_info.pps = 1; + pdata->ptp_clock_info.adjfine = phytmac_ptp_adjfine; + pdata->ptp_clock_info.adjtime = phytmac_ptp_adjtime; + pdata->ptp_clock_info.gettime64 = phytmac_ptp_gettime; + pdata->ptp_clock_info.settime64 = phytmac_ptp_settime; + pdata->ptp_clock_info.enable = phytmac_ptp_enable; + pdata->ptp_clock = ptp_clock_register(&pdata->ptp_clock_info, pdata->dev); + if (IS_ERR_OR_NULL(pdata->ptp_clock)) { + dev_err(pdata->dev, "ptp_clock_register failed %lu\n", + PTR_ERR(pdata->ptp_clock)); + return -EINVAL; + } + + return 0; +} + +void phytmac_ptp_unregister(struct phytmac *pdata) +{ + struct phytmac_hw_if *hw_if = pdata->hw_if; + + if (pdata->ptp_clock) + ptp_clock_unregister(pdata->ptp_clock); + pdata->ptp_clock = NULL; + + hw_if->clear_time(pdata); + + dev_info(pdata->dev, "phytmac ptp clock unregistered.\n"); +} + +void phytmac_ptp_init(struct net_device *ndev) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct phytmac_hw_if *hw_if = pdata->hw_if; + + phytmac_ptp_init_timer(pdata); + + hw_if->init_ts_hw(pdata); + + dev_info(pdata->dev, "phytmac ptp clock init success.\n"); +} + +int phytmac_ptp_get_ts_config(struct net_device *dev, struct ifreq *rq) +{ + struct hwtstamp_config *tstamp_config; + struct phytmac *pdata = netdev_priv(dev); + + if (!IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) + return -EOPNOTSUPP; + + tstamp_config = &pdata->ts_config; + + if (copy_to_user(rq->ifr_data, tstamp_config, sizeof(*tstamp_config))) + return -EFAULT; + else + return 0; +} + +int phytmac_ptp_set_ts_config(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct hwtstamp_config config; + struct phytmac *pdata = netdev_priv(dev); + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct ts_ctrl tstamp_ctrl; + int ret; + + memset(&tstamp_ctrl, 0, sizeof(struct ts_ctrl)); + + if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) + return -EFAULT; + + switch (config.tx_type) { + case HWTSTAMP_TX_OFF: + break; + case HWTSTAMP_TX_ONESTEP_SYNC: + tstamp_ctrl.one_step = 1; + tstamp_ctrl.tx_control = TS_ALL_FRAMES; + break; + case HWTSTAMP_TX_ON: + tstamp_ctrl.one_step = 0; + tstamp_ctrl.tx_control = TS_ALL_FRAMES; + break; + default: + return -ERANGE; + } + + switch (config.rx_filter) { + case HWTSTAMP_FILTER_NONE: + case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: + break; + case HWTSTAMP_FILTER_PTP_V2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: + tstamp_ctrl.rx_control = TS_ALL_PTP_FRAMES; + config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; + break; + case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: + case HWTSTAMP_FILTER_ALL: + tstamp_ctrl.rx_control = TS_ALL_FRAMES; + config.rx_filter = HWTSTAMP_FILTER_ALL; + break; + default: + config.rx_filter = HWTSTAMP_FILTER_NONE; + return -ERANGE; + } + + ret = hw_if->set_ts_config(pdata, &tstamp_ctrl); + if (ret) + return ret; + + /* save these settings for future reference */ + pdata->ts_config = config; + memcpy(&pdata->ts_config, &config, sizeof(config)); + + if (copy_to_user(ifr->ifr_data, &config, sizeof(config))) + return -EFAULT; + else + return 0; +} + diff --git a/drivers/net/ethernet/phytium/phytmac_ptp.h b/drivers/net/ethernet/phytium/phytmac_ptp.h new file mode 100644 index 0000000000000000000000000000000000000000..0ee385e5fe4d0a4e165e15b80afc50dfaff3ae3d --- /dev/null +++ b/drivers/net/ethernet/phytium/phytmac_ptp.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Phytium Ethernet Controller driver + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef _PHYTMAC_PTP_H +#define _PHYTMAC_PTP_H + +#ifdef CONFIG_PHYTMAC_ENABLE_PTP + +enum ptp_msgtype { + PTP_MSGTYPE_SYNC, + PTP_MSGTYPE_DELAY_REQ, + PTP_MSGTYPE_PDELAY_REQ, + PTP_MSGTYPE_PDELAY_RESP, +}; + +bool phytmac_ptp_one_step(struct sk_buff *skb); +int phytmac_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts); +int phytmac_ptp_settime(struct ptp_clock_info *ptp, + const struct timespec64 *ts); +int phytmac_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm); +int phytmac_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta); +int phytmac_ptp_enable(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, int on); +void phytmac_ptp_init_timer(struct phytmac *pdata); +void phytmac_ptp_rxstamp(struct phytmac *pdata, struct sk_buff *skb, + struct phytmac_dma_desc *desc); +int phytmac_ptp_txstamp(struct phytmac_queue *queue, struct sk_buff *skb, + struct phytmac_dma_desc *desc); +int phytmac_ptp_register(struct phytmac *pdata); +void phytmac_ptp_unregister(struct phytmac *pdata); +void phytmac_ptp_init(struct net_device *ndev); +int phytmac_ptp_get_ts_config(struct net_device *dev, struct ifreq *rq); +int phytmac_ptp_set_ts_config(struct net_device *dev, struct ifreq *ifr, int cmd); +#else +static inline bool phytmac_ptp_one_step(struct sk_buff *skb) +{ + return 1; +} + +static inline void phytmac_ptp_rxstamp(struct phytmac *pdata, struct sk_buff *skb, + struct phytmac_dma_desc *desc) {} +static inline int phytmac_ptp_txstamp(struct phytmac_queue *queue, struct sk_buff *skb, + struct phytmac_dma_desc *desc) +{ + return -1; +} + +static inline int phytmac_ptp_register(struct phytmac *pdata) +{ + return 0; +} + +static inline void phytmac_ptp_unregister(struct phytmac *pdata) {} +static inline void phytmac_ptp_init(struct net_device *ndev) {} + +#endif +#endif diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c new file mode 100644 index 0000000000000000000000000000000000000000..10ffcae90f06e12c1ce0a272cb4d4d5599d41b8a --- /dev/null +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -0,0 +1,1348 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "phytmac.h" +#include "phytmac_v1.h" + +static int phytmac_enable_promise(struct phytmac *pdata, int enable) +{ + u32 value = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG); + + if (enable) + value |= PHYTMAC_BIT(PROMISC); + else + value &= ~PHYTMAC_BIT(PROMISC); + + PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, value); + + return 0; +} + +static int phytmac_enable_multicast(struct phytmac *pdata, int enable) +{ + u32 config; + + if (enable) { + PHYTMAC_WRITE(pdata, PHYTMAC_HASHB, -1); + PHYTMAC_WRITE(pdata, PHYTMAC_HASHT, -1); + config = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG); + config |= PHYTMAC_BIT(MH_EN); + PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, config); + } else { + PHYTMAC_WRITE(pdata, PHYTMAC_HASHB, 0); + PHYTMAC_WRITE(pdata, PHYTMAC_HASHT, 0); + config = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG); + config &= ~PHYTMAC_BIT(MH_EN); + PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, config); + } + + return 0; +} + +static int phytmac_set_mc_hash(struct phytmac *pdata, unsigned long *mc_filter) +{ + u32 config; + + PHYTMAC_WRITE(pdata, PHYTMAC_HASHB, mc_filter[0]); + PHYTMAC_WRITE(pdata, PHYTMAC_HASHT, mc_filter[1]); + config = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG); + config |= PHYTMAC_BIT(MH_EN); + PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, config); + + return 0; +} + +static int phytmac_enable_rxcsum(struct phytmac *pdata, int enable) +{ + u32 value = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG); + + if (enable) + value |= PHYTMAC_BIT(RCO_EN); + else + value &= ~PHYTMAC_BIT(RCO_EN); + + PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, value); + + return 0; +} + +static int phytmac_enable_txcsum(struct phytmac *pdata, int enable) +{ + u32 value = PHYTMAC_READ(pdata, PHYTMAC_DCONFIG); + + if (enable) + value |= PHYTMAC_BIT(TCO_EN); + else + value &= ~PHYTMAC_BIT(TCO_EN); + + PHYTMAC_WRITE(pdata, PHYTMAC_DCONFIG, value); + + return 0; +} + +static int phytmac_enable_mdio(struct phytmac *pdata, int enable) +{ + u32 value = PHYTMAC_READ(pdata, PHYTMAC_NCTRL); + + if (enable) + value |= PHYTMAC_BIT(MPE); + else + value &= ~PHYTMAC_BIT(MPE); + + PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, value); + + return 0; +} + +static int phytmac_enable_pause(struct phytmac *pdata, int enable) +{ + u32 value = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG); + + if (enable) + value |= PHYTMAC_BIT(PAUSE_EN); + else + value &= ~PHYTMAC_BIT(PAUSE_EN); + + PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, value); + return 0; +} + +static int phytmac_enable_network(struct phytmac *pdata, int enable, int rx_tx) +{ + u32 old_ctrl = PHYTMAC_READ(pdata, PHYTMAC_NCTRL); + u32 ctrl; + + ctrl = old_ctrl; + + if (rx_tx & PHYTMAC_TX) { + if (enable) + ctrl |= PHYTMAC_BIT(TE); + else + ctrl &= ~PHYTMAC_BIT(TE); + } + + if (rx_tx & PHYTMAC_RX) { + if (enable) + ctrl |= PHYTMAC_BIT(RE); + else + ctrl &= ~PHYTMAC_BIT(RE); + } + + if (ctrl ^ old_ctrl) + PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, ctrl); + + return 0; +} + +static int phytmac_enable_autoneg(struct phytmac *pdata, int enable) +{ + u32 ctrl, old_ctrl; + + if (enable) + ctrl |= PHYTMAC_BIT(AUTONEG); + else + ctrl &= ~PHYTMAC_BIT(AUTONEG); + + old_ctrl = PHYTMAC_READ(pdata, PHYTMAC_PCSCTRL); + if (old_ctrl != ctrl) + PHYTMAC_WRITE(pdata, PHYTMAC_PCSCTRL, ctrl); + + return 0; +} + +static int phytmac_mac_linkup(struct phytmac *pdata, phy_interface_t interface, + int speed, int duplex) +{ + u32 ctrl, config; + + config = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG); + + config &= ~(PHYTMAC_BIT(SPEED) | PHYTMAC_BIT(FD)); + + if (speed == SPEED_100) + config |= PHYTMAC_BIT(SPEED); + else if (speed == SPEED_1000 || speed == SPEED_2500) + config |= PHYTMAC_BIT(GM_EN); + + if (duplex) + config |= PHYTMAC_BIT(FD); + + PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, config); + + if (speed == SPEED_2500) { + ctrl = PHYTMAC_READ(pdata, PHYTMAC_NCTRL); + ctrl |= PHYTMAC_BIT(2PT5G); + PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, ctrl); + } + + if (speed == SPEED_10000) + PHYTMAC_WRITE(pdata, PHYTMAC_HCONFIG, PHYTMAC_SPEED_10000M); + else if (speed == SPEED_5000) + PHYTMAC_WRITE(pdata, PHYTMAC_HCONFIG, PHYTMAC_SPEED_5000M); + else if (speed == SPEED_2500) + PHYTMAC_WRITE(pdata, PHYTMAC_HCONFIG, PHYTMAC_SPEED_2500M); + else if (speed == SPEED_1000) + PHYTMAC_WRITE(pdata, PHYTMAC_HCONFIG, PHYTMAC_SPEED_1000M); + else + PHYTMAC_WRITE(pdata, PHYTMAC_HCONFIG, PHYTMAC_SPEED_100M); + + return 0; +} + +static int phytmac_mac_linkdown(struct phytmac *pdata) +{ + return 0; +} + +static int phytmac_pcs_linkup(struct phytmac *pdata, phy_interface_t interface, + int speed, int duplex) +{ + u32 config; + + if (interface == PHY_INTERFACE_MODE_USXGMII || + interface == PHY_INTERFACE_MODE_10GBASER) { + config = PHYTMAC_READ(pdata, PHYTMAC_USXCTRL); + if (speed == SPEED_10000) { + config = PHYTMAC_SET_BITS(config, SERDES_RATE, PHYTMAC_SERDES_RATE_10G); + config = PHYTMAC_SET_BITS(config, USX_SPEED, PHYTMAC_SPEED_10000M); + } else if (speed == SPEED_5000) { + config = PHYTMAC_SET_BITS(config, SERDES_RATE, PHYTMAC_SERDES_RATE_5G); + config = PHYTMAC_SET_BITS(config, USX_SPEED, PHYTMAC_SPEED_5000M); + } + + /* reset */ + config &= ~(PHYTMAC_BIT(RX_EN) | PHYTMAC_BIT(TX_EN)); + config |= PHYTMAC_BIT(RX_SYNC_RESET); + + PHYTMAC_WRITE(pdata, PHYTMAC_USXCTRL, config); + + /* enable rx and tx */ + config &= ~(PHYTMAC_BIT(RX_SYNC_RESET)); + config |= PHYTMAC_BIT(RX_EN) | PHYTMAC_BIT(TX_EN); + + PHYTMAC_WRITE(pdata, PHYTMAC_USXCTRL, config); + } + + return 0; +} + +static int phytmac_pcs_linkdown(struct phytmac *pdata) +{ + return 0; +} + +static int phytmac_get_mac_addr(struct phytmac *pdata, u8 *addr) +{ + u32 bottom; + u16 top; + + bottom = PHYTMAC_READ(pdata, PHYTMAC_MAC1B); + top = PHYTMAC_READ(pdata, PHYTMAC_MAC1T); + + addr[0] = bottom & 0xff; + addr[1] = (bottom >> 8) & 0xff; + addr[2] = (bottom >> 16) & 0xff; + addr[3] = (bottom >> 24) & 0xff; + addr[4] = top & 0xff; + addr[5] = (top >> 8) & 0xff; + + return 0; +} + +static int phytmac_set_mac_addr(struct phytmac *pdata, const u8 *addr) +{ + u32 bottom; + u16 top; + + bottom = cpu_to_le32(*((u32 *)addr)); + PHYTMAC_WRITE(pdata, PHYTMAC_MAC1B, bottom); + top = cpu_to_le16(*((u16 *)(addr + 4))); + PHYTMAC_WRITE(pdata, PHYTMAC_MAC1T, top); + + return 0; +} + +static void phytmac_reset_hw(struct phytmac *pdata) +{ + struct phytmac_queue *queue; + unsigned int q; + u32 ctrl; + + ctrl = PHYTMAC_READ(pdata, PHYTMAC_NCTRL); + + ctrl &= ~(PHYTMAC_BIT(RE) | PHYTMAC_BIT(TE)); + ctrl |= PHYTMAC_BIT(CLEARSTAT); + PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, ctrl); + + /* Disable and clear all interrupts and disable queues */ + for (q = 0, queue = pdata->queues; q < pdata->queues_max_num; ++q, ++queue) { + if (q == 0) { + PHYTMAC_WRITE(pdata, PHYTMAC_ID, -1); + PHYTMAC_WRITE(pdata, PHYTMAC_IS, -1); + PHYTMAC_WRITE(pdata, PHYTMAC_TXPTR_Q0, 1); + PHYTMAC_WRITE(pdata, PHYTMAC_RXPTR_Q0, 1); + } else { + PHYTMAC_WRITE(pdata, PHYTMAC_IDR(q - 1), -1); + PHYTMAC_WRITE(pdata, PHYTMAC_ISR(q - 1), -1); + PHYTMAC_WRITE(pdata, PHYTMAC_TXPTR(q - 1), -1); + PHYTMAC_WRITE(pdata, PHYTMAC_RXPTR(q - 1), -1); + } + + PHYTMAC_WRITE(pdata, PHYTMAC_TXPTRH(q), 0); + PHYTMAC_WRITE(pdata, PHYTMAC_RXPTRH(q), 0); + + if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) + PHYTMAC_WRITE(pdata, PHYTMAC_TAILPTR(q), 0); + } +} + +static void phytmac_get_regs(struct phytmac *pdata, u32 *reg_buff) +{ + reg_buff[0] = PHYTMAC_READ(pdata, PHYTMAC_NCTRL); + reg_buff[1] = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG); + reg_buff[2] = PHYTMAC_READ(pdata, PHYTMAC_NSTATUS); + reg_buff[3] = PHYTMAC_READ(pdata, PHYTMAC_DCONFIG); + reg_buff[4] = PHYTMAC_READ(pdata, PHYTMAC_TXPTR_Q0); + reg_buff[5] = PHYTMAC_READ(pdata, PHYTMAC_RXPTR_Q0); + reg_buff[6] = PHYTMAC_READ(pdata, PHYTMAC_TXPTR(1)); + reg_buff[7] = PHYTMAC_READ(pdata, PHYTMAC_RXPTR(1)); + reg_buff[8] = PHYTMAC_READ(pdata, PHYTMAC_TXPTR(2)); + reg_buff[9] = PHYTMAC_READ(pdata, PHYTMAC_RXPTR(2)); + reg_buff[10] = PHYTMAC_READ(pdata, PHYTMAC_TXPTR(3)); + reg_buff[11] = PHYTMAC_READ(pdata, PHYTMAC_RXPTR(3)); + reg_buff[12] = PHYTMAC_READ(pdata, PHYTMAC_HCONFIG); + reg_buff[13] = PHYTMAC_READ(pdata, PHYTMAC_IM); + if (pdata->phy_interface == PHY_INTERFACE_MODE_USXGMII || + pdata->phy_interface == PHY_INTERFACE_MODE_10GBASER) { + reg_buff[14] = PHYTMAC_READ(pdata, PHYTMAC_USXCTRL); + reg_buff[15] = PHYTMAC_READ(pdata, PHYTMAC_USXSTATUS); + } else { + reg_buff[14] = PHYTMAC_READ(pdata, PHYTMAC_PCSCTRL); + reg_buff[15] = PHYTMAC_READ(pdata, PHYTMAC_PCSSTATUS); + } +} + +static int phytmac_init_hw(struct phytmac *pdata) +{ + u32 config = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG); + u32 dmaconfig; + u32 nctrlconfig; + + nctrlconfig = PHYTMAC_READ(pdata, PHYTMAC_NCTRL); + nctrlconfig |= PHYTMAC_BIT(MPE); + PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, nctrlconfig); + + phytmac_set_mac_addr(pdata, pdata->ndev->dev_addr); + + PHYTMAC_WRITE(pdata, PHYTMAC_AXICTRL, 0x1010); + + /* jumbo */ + if (pdata->capacities & PHYTMAC_CAPS_JUMBO) + config |= PHYTMAC_BIT(JUMBO_EN); + else + config |= PHYTMAC_BIT(RCV_BIG); + /* promisc */ + if (pdata->ndev->flags & IFF_PROMISC) + config |= PHYTMAC_BIT(PROMISC); + if (pdata->ndev->features & NETIF_F_RXCSUM) + config |= PHYTMAC_BIT(RCO_EN); + if (!(pdata->ndev->flags & IFF_BROADCAST)) + config |= PHYTMAC_BIT(NO_BCAST); + /* pause enable */ + config |= PHYTMAC_BIT(PAUSE_EN); + /* Rx Fcs remove */ + config |= PHYTMAC_BIT(FCS_REMOVE); + if (pdata->dma_data_width == PHYTMAC_DBW_64) + config |= PHYTMAC_BIT(DBW64); + if (pdata->dma_data_width == PHYTMAC_DBW_128) + config |= PHYTMAC_BIT(DBW128); + /* mdc div */ + config = PHYTMAC_SET_BITS(config, MCD, 6); + netdev_dbg(pdata->ndev, "phytmac configure NetConfig with 0x%08x\n", + config); + PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, config); + + /* init dma */ + dmaconfig = PHYTMAC_READ(pdata, PHYTMAC_DCONFIG); + if (pdata->dma_burst_length) + dmaconfig = PHYTMAC_SET_BITS(dmaconfig, BURST, pdata->dma_burst_length); + /* default in small endian */ + dmaconfig &= ~(PHYTMAC_BIT(ENDIA_PKT) | PHYTMAC_BIT(ENDIA_DESC)); + + if (pdata->ndev->features & NETIF_F_HW_CSUM) + dmaconfig |= PHYTMAC_BIT(TCO_EN); + else + dmaconfig &= ~PHYTMAC_BIT(TCO_EN); + + if (pdata->dma_addr_width) + dmaconfig |= PHYTMAC_BIT(ABW); + + /* fdir ethtype -- ipv4 */ + PHYTMAC_WRITE(pdata, PHYTMAC_ETHT(0), (uint16_t)ETH_P_IP); + + if (IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) + dmaconfig |= PHYTMAC_BIT(RX_EXBD_EN) | PHYTMAC_BIT(TX_EXBD_EN); + + PHYTMAC_WRITE(pdata, PHYTMAC_DCONFIG, dmaconfig); + + if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) + PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_ENABLE, 0x80000001); + return 0; +} + +static int phytmac_powerup_hw(struct phytmac *pdata, int on) +{ + u32 status, data0, data1, rdata1; + int ret; + + if (pdata->capacities & PHYTMAC_CAPS_LPI) { + ret = readx_poll_timeout(PHYTMAC_READ_STAT, pdata, status, !status, + 1, PHYTMAC_TIMEOUT); + if (ret) + netdev_err(pdata->ndev, "mnh status is busy, status=%x\n", status); + + ret = readx_poll_timeout(PHYTMAC_READ_DATA0, pdata, data0, + data0 & PHYTMAC_BIT(DATA0_FREE), + 1, PHYTMAC_TIMEOUT); + if (ret) + netdev_err(pdata->ndev, "mnh data0 is busy, data0=%x\n", data0); + + data0 = 0; + data0 = PHYTMAC_SET_BITS(data0, DATA0_MSG, PHYTMAC_MSG_PM); + data0 = PHYTMAC_SET_BITS(data0, DATA0_PRO, PHYTMAC_PRO_ID); + PHYTMAC_MHU_WRITE(pdata, PHYTMAC_MHU_CPP_DATA0, data0); + data1 = 0; + + if (on == PHYTMAC_POWERON) { + data1 = PHYTMAC_SET_BITS(data1, DATA1_STAT, PHYTMAC_STATON); + data1 = PHYTMAC_SET_BITS(data1, DATA1_STATTYPE, PHYTMAC_STATTYPE); + PHYTMAC_MHU_WRITE(pdata, PHYTMAC_MHU_CPP_DATA1, data1); + } else { + data1 = PHYTMAC_SET_BITS(data1, DATA1_STAT, PHYTMAC_STATOFF); + data1 = PHYTMAC_SET_BITS(data1, DATA1_STATTYPE, PHYTMAC_STATTYPE); + PHYTMAC_MHU_WRITE(pdata, PHYTMAC_MHU_CPP_DATA1, data1); + } + + PHYTMAC_MHU_WRITE(pdata, PHYTMAC_MHU_AP_CPP_SET, 1); + ret = readx_poll_timeout(PHYTMAC_READ_DATA0, pdata, data0, + data0 & PHYTMAC_BIT(DATA0_FREE), + 1, PHYTMAC_TIMEOUT); + if (ret) + netdev_err(pdata->ndev, "mnh data0 is busy"); + + rdata1 = PHYTMAC_MHU_READ(pdata, PHYTMAC_MHU_CPP_DATA1); + if (rdata1 == data1) + netdev_err(pdata->ndev, "gmac power %s success, data1 = %x, rdata1=%x\n", + on ? "up" : "down", data1, rdata1); + else + netdev_err(pdata->ndev, "gmac power %s failed, data1 = %x, rdata1=%x\n", + on ? "up" : "down", data1, rdata1); + } + pdata->power_state = on; + + return 0; +} + +static int phytmac_set_wake(struct phytmac *pdata, int wake) +{ + u32 value = 0; + + if (wake & PHYTMAC_WAKE_MAGIC) + value |= PHYTMAC_BIT(MAGIC); + if (wake & PHYTMAC_WAKE_ARP) + value |= PHYTMAC_BIT(ARP); + if (wake & PHYTMAC_WAKE_UCAST) + value |= PHYTMAC_BIT(UCAST); + if (wake & PHYTMAC_WAKE_MCAST) + value |= PHYTMAC_BIT(MCAST); + + PHYTMAC_WRITE(pdata, PHYTMAC_WOL, value); + + return 0; +} + +static void phytmac_mdio_idle(struct phytmac *pdata) +{ + u32 val; + + /* wait for end of transfer */ + val = PHYTMAC_READ(pdata, PHYTMAC_NSTATUS); + while (!(val & PHYTMAC_BIT(MDIO_IDLE))) { + cpu_relax(); + val = PHYTMAC_READ(pdata, PHYTMAC_NSTATUS); + } +} + +static int phytmac_mdio_data_read(struct phytmac *pdata, int mii_id, int regnum, int is_c45) +{ + u16 data; + + if (is_c45) { + PHYTMAC_WRITE(pdata, PHYTMAC_MDATA, (PHYTMAC_BITS(CLAUSE_SEL, PHYTMAC_CLAUSE_C45) + | PHYTMAC_BITS(OPS, PHYTMAC_OPS_C45_ADDR) + | PHYTMAC_BITS(PHY_ADDR, mii_id) + | PHYTMAC_BITS(REG_ADDR, (regnum >> 16) & 0x1F) + | PHYTMAC_BITS(DATA, regnum & 0xFFFF) + | PHYTMAC_BITS(MUST, 2))); + phytmac_mdio_idle(pdata); + PHYTMAC_WRITE(pdata, PHYTMAC_MDATA, (PHYTMAC_BITS(CLAUSE_SEL, PHYTMAC_CLAUSE_C45) + | PHYTMAC_BITS(OPS, PHYTMAC_OPS_C45_READ) + | PHYTMAC_BITS(PHY_ADDR, mii_id) + | PHYTMAC_BITS(REG_ADDR, (regnum >> 16) & 0x1F) + | PHYTMAC_BITS(DATA, regnum & 0xFFFF) + | PHYTMAC_BITS(MUST, 2))); + } else { + PHYTMAC_WRITE(pdata, PHYTMAC_MDATA, (PHYTMAC_BITS(CLAUSE_SEL, PHYTMAC_CLAUSE_C22) + | PHYTMAC_BITS(OPS, PHYTMAC_OPS_C22_READ) + | PHYTMAC_BITS(PHY_ADDR, mii_id) + | PHYTMAC_BITS(REG_ADDR, regnum) + | PHYTMAC_BITS(MUST, 2))); + } + + phytmac_mdio_idle(pdata); + data = PHYTMAC_READ(pdata, PHYTMAC_MDATA) & 0xffff; + phytmac_mdio_idle(pdata); + + return data; +} + +static int phytmac_mdio_data_write(struct phytmac *pdata, int mii_id, + int regnum, int is_c45, u16 data) +{ + if (is_c45) { + PHYTMAC_WRITE(pdata, PHYTMAC_MDATA, (PHYTMAC_BITS(CLAUSE_SEL, PHYTMAC_CLAUSE_C45) + | PHYTMAC_BITS(OPS, PHYTMAC_OPS_C45_ADDR) + | PHYTMAC_BITS(PHY_ADDR, mii_id) + | PHYTMAC_BITS(REG_ADDR, (regnum >> 16) & 0x1F) + | PHYTMAC_BITS(DATA, regnum & 0xFFFF) + | PHYTMAC_BITS(MUST, 2))); + phytmac_mdio_idle(pdata); + PHYTMAC_WRITE(pdata, PHYTMAC_MDATA, (PHYTMAC_BITS(CLAUSE_SEL, PHYTMAC_CLAUSE_C45) + | PHYTMAC_BITS(OPS, PHYTMAC_OPS_C45_WRITE) + | PHYTMAC_BITS(PHY_ADDR, mii_id) + | PHYTMAC_BITS(REG_ADDR, (regnum >> 16) & 0x1F) + | PHYTMAC_BITS(DATA, data) + | PHYTMAC_BITS(MUST, 2))); + } else { + PHYTMAC_WRITE(pdata, PHYTMAC_MDATA, (PHYTMAC_BITS(CLAUSE_SEL, PHYTMAC_CLAUSE_C22) + | PHYTMAC_BITS(OPS, PHYTMAC_OPS_C22_WRITE) + | PHYTMAC_BITS(PHY_ADDR, mii_id) + | PHYTMAC_BITS(REG_ADDR, regnum) + | PHYTMAC_BITS(DATA, data) + | PHYTMAC_BITS(MUST, 2))); + } + phytmac_mdio_idle(pdata); + + return 0; +} + +static int phytmac_get_feature_all(struct phytmac *pdata) +{ + unsigned int queue_mask; + unsigned int num_queues; + int val; + + /* get max queues */ + queue_mask = 0x1; + queue_mask |= PHYTMAC_READ(pdata, PHYTMAC_DEFAULT2) & 0xff; + num_queues = hweight32(queue_mask); + pdata->queues_max_num = num_queues; + + /* get dma desc prefetch number */ + val = PHYTMAC_READ_BITS(pdata, PHYTMAC_DEFAULT4, TXDESCRD); + if (val) + pdata->tx_bd_prefetch = (2 << (val - 1)) * + sizeof(struct phytmac_dma_desc); + + val = PHYTMAC_READ_BITS(pdata, PHYTMAC_DEFAULT4, RXDESCRD); + if (val) + pdata->rx_bd_prefetch = (2 << (val - 1)) * + sizeof(struct phytmac_dma_desc); + + /* dma bus width */ + pdata->dma_data_width = PHYTMAC_READ_BITS(pdata, PHYTMAC_DEFAULT1, DBW); + + /* dma addr width */ + if (PHYTMAC_READ_BITS(pdata, PHYTMAC_DEFAULT2, DAW64)) + pdata->dma_addr_width = 64; + else + pdata->dma_addr_width = 32; + + /* max rx fs */ + pdata->max_rx_fs = PHYTMAC_READ_BITS(pdata, PHYTMAC_DEFAULT3, SCR2CMP); + + if (netif_msg_hw(pdata)) + netdev_info(pdata->ndev, "get feature queue_num=%d, daw=%d, dbw=%d, rx_fs=%d, rx_bd=%d, tx_bd=%d\n", + pdata->queues_num, pdata->dma_addr_width, pdata->dma_data_width, + pdata->max_rx_fs, pdata->rx_bd_prefetch, pdata->tx_bd_prefetch); + return 0; +} + +static int phytmac_add_fdir_entry(struct phytmac *pdata, struct ethtool_rx_flow_spec *rx_flow) +{ + struct ethtool_tcpip4_spec *tp4sp_v, *tp4sp_m; + u16 index = rx_flow->location; + u32 tmp, fdir_ctrl; + bool ipsrc = false; + bool ipdst = false; + bool port = false; + + tp4sp_v = &rx_flow->h_u.tcp_ip4_spec; + tp4sp_m = &rx_flow->m_u.tcp_ip4_spec; + + if (tp4sp_m->ip4src == 0xFFFFFFFF) { + tmp = 0; + tmp = PHYTMAC_SET_BITS(tmp, OFFSET, ETHTYPE_SIP_OFFSET); + tmp = PHYTMAC_SET_BITS(tmp, OFFSET_TYPE, PHYTMAC_OFFSET_AFTER_L2HEAD); + tmp = PHYTMAC_SET_BITS(tmp, DIS_MASK, 1); + PHYTMAC_WRITE(pdata, PHYTMAC_COMP1(3 * index), tp4sp_v->ip4src); + PHYTMAC_WRITE(pdata, PHYTMAC_COMP2(3 * index), tmp); + ipsrc = true; + } + + if (tp4sp_m->ip4dst == 0xFFFFFFFF) { + /* 2nd compare reg - IP destination address */ + tmp = 0; + tmp = PHYTMAC_SET_BITS(tmp, OFFSET, ETHTYPE_DIP_OFFSET); + tmp = PHYTMAC_SET_BITS(tmp, OFFSET_TYPE, PHYTMAC_OFFSET_AFTER_L2HEAD); + tmp = PHYTMAC_SET_BITS(tmp, DIS_MASK, 1); + PHYTMAC_WRITE(pdata, PHYTMAC_COMP1(3 * index + 1), tp4sp_v->ip4dst); + PHYTMAC_WRITE(pdata, PHYTMAC_COMP2(3 * index + 1), tmp); + ipdst = true; + } + + if (tp4sp_m->psrc == 0xFFFF || tp4sp_m->pdst == 0xFFFF) { + tmp = 0; + tmp = PHYTMAC_SET_BITS(tmp, OFFSET_TYPE, PHYTMAC_OFFSET_AFTER_L3HEAD); + if (tp4sp_m->psrc == tp4sp_m->pdst) { + tmp = PHYTMAC_SET_BITS(tmp, DIS_MASK, 1); + tmp = PHYTMAC_SET_BITS(tmp, OFFSET, IPHEAD_SPORT_OFFSET); + PHYTMAC_WRITE(pdata, PHYTMAC_COMP1(3 * index + 2), + tp4sp_v->psrc | (u32)(tp4sp_v->pdst << 16)); + } else { + tmp = PHYTMAC_SET_BITS(tmp, DIS_MASK, 0); + if (tp4sp_m->psrc == 0xFFFF) { /* src port */ + tmp = PHYTMAC_SET_BITS(tmp, OFFSET, IPHEAD_SPORT_OFFSET); + PHYTMAC_WRITE(pdata, PHYTMAC_COMP1(3 * index + 2), + tp4sp_m->psrc | (u32)(tp4sp_v->psrc << 16)); + } else { /* dst port */ + tmp = PHYTMAC_SET_BITS(tmp, OFFSET, IPHEAD_DPORT_OFFSET); + PHYTMAC_WRITE(pdata, PHYTMAC_COMP1(3 * index + 2), + tp4sp_m->pdst | (u32)(tp4sp_v->pdst << 16)); + } + } + PHYTMAC_WRITE(pdata, PHYTMAC_COMP2(3 * index + 2), tmp); + port = true; + } + + fdir_ctrl = PHYTMAC_READ(pdata, PHYTMAC_FDIR(rx_flow->location)); + + if (ipsrc) { + fdir_ctrl = PHYTMAC_SET_BITS(fdir_ctrl, CA, 3 * index); + fdir_ctrl = PHYTMAC_SET_BITS(fdir_ctrl, CA_EN, 1); + } + + if (ipdst) { + fdir_ctrl = PHYTMAC_SET_BITS(fdir_ctrl, CB, 3 * index + 1); + fdir_ctrl = PHYTMAC_SET_BITS(fdir_ctrl, CB_EN, 1); + } + + if (port) { + fdir_ctrl = PHYTMAC_SET_BITS(fdir_ctrl, CC, 3 * index + 2); + fdir_ctrl = PHYTMAC_SET_BITS(fdir_ctrl, CC_EN, 1); + } + + fdir_ctrl = PHYTMAC_SET_BITS(fdir_ctrl, QUEUE_NUM, (rx_flow->ring_cookie) & 0xFF); + fdir_ctrl = PHYTMAC_SET_BITS(fdir_ctrl, ETH_TYPE, 0); + fdir_ctrl = PHYTMAC_SET_BITS(fdir_ctrl, ETH_EN, 1); + + PHYTMAC_WRITE(pdata, PHYTMAC_FDIR(rx_flow->location), fdir_ctrl); + + return 0; +} + +static int phytmac_del_fdir_entry(struct phytmac *pdata, struct ethtool_rx_flow_spec *rx_flow) +{ + int i; + int index = rx_flow->location; + + PHYTMAC_WRITE(pdata, PHYTMAC_FDIR(index), 0); + for (i = 0; i < 3; i++) { + PHYTMAC_WRITE(pdata, PHYTMAC_COMP1(3 * index + i), 0); + PHYTMAC_WRITE(pdata, PHYTMAC_COMP2(3 * index + i), 0); + } + return 0; +} + +static int phytmac_init_ring_hw(struct phytmac *pdata) +{ + struct phytmac_queue *queue; + unsigned int q = 0; + u32 buffer_size = pdata->rx_buffer_len / 64; + + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { + if (q == 0) { + PHYTMAC_WRITE(pdata, PHYTMAC_RXPTR_Q0, + lower_32_bits(queue->rx_ring_addr)); + PHYTMAC_WRITE(pdata, PHYTMAC_TXPTR_Q0, + lower_32_bits(queue->tx_ring_addr)); + PHYTMAC_WRITE(pdata, PHYTMAC_DCONFIG, + PHYTMAC_SET_BITS(PHYTMAC_READ(pdata, PHYTMAC_DCONFIG), + RX_BUF_LEN, buffer_size)); + } else { + PHYTMAC_WRITE(pdata, PHYTMAC_RXPTR(q - 1), + lower_32_bits(queue->rx_ring_addr)); + PHYTMAC_WRITE(pdata, PHYTMAC_TXPTR(q - 1), + lower_32_bits(queue->tx_ring_addr)); + PHYTMAC_WRITE(pdata, PHYTMAC_RBQS(q - 1), buffer_size); + } + + PHYTMAC_WRITE(pdata, PHYTMAC_TXPTRH(q), upper_32_bits(queue->tx_ring_addr)); + PHYTMAC_WRITE(pdata, PHYTMAC_RXPTRH(q), upper_32_bits(queue->rx_ring_addr)); + + if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) + PHYTMAC_WRITE(pdata, PHYTMAC_TAILPTR(q), queue->tx_tail); + } + + return 0; +} + +static u32 phytmac_get_irq_mask(u32 mask) +{ + u32 value = 0; + + value |= (mask & PHYTMAC_INT_TX_COMPLETE) ? PHYTMAC_BIT(TXCOMP) : 0; + value |= (mask & PHYTMAC_INT_TX_ERR) ? PHYTMAC_BIT(BUS_ERR) : 0; + value |= (mask & PHYTMAC_INT_RX_COMPLETE) ? PHYTMAC_BIT(RXCOMP) : 0; + value |= (mask & PHYTMAC_INT_RX_OVERRUN) ? PHYTMAC_BIT(RXOVERRUN) : 0; + value |= (mask & PHYTMAC_INT_RX_DESC_FULL) ? PHYTMAC_BIT(RUB) : 0; + + return value; +} + +static u32 phytmac_get_irq_status(u32 value) +{ + u32 status = 0; + + status |= (value & PHYTMAC_BIT(TXCOMP)) ? PHYTMAC_INT_TX_COMPLETE : 0; + status |= (value & PHYTMAC_BIT(BUS_ERR)) ? PHYTMAC_INT_TX_ERR : 0; + status |= (value & PHYTMAC_BIT(RXCOMP)) ? PHYTMAC_INT_RX_COMPLETE : 0; + status |= (value & PHYTMAC_BIT(RXOVERRUN)) ? PHYTMAC_INT_RX_OVERRUN : 0; + status |= (value & PHYTMAC_BIT(RUB)) ? PHYTMAC_INT_RX_DESC_FULL : 0; + + return status; +} + +static void phytmac_enable_irq(struct phytmac *pdata, + int queue_index, u32 mask) +{ + u32 value; + + value = phytmac_get_irq_mask(mask); + + if (queue_index == 0) + PHYTMAC_WRITE(pdata, PHYTMAC_IE, value); + else + PHYTMAC_WRITE(pdata, PHYTMAC_IER(queue_index - 1), value); +} + +static void phytmac_disable_irq(struct phytmac *pdata, + int queue_index, u32 mask) +{ + u32 value; + + value = phytmac_get_irq_mask(mask); + + if (queue_index == 0) + PHYTMAC_WRITE(pdata, PHYTMAC_ID, value); + else + PHYTMAC_WRITE(pdata, PHYTMAC_IDR(queue_index - 1), value); +} + +static void phytmac_clear_irq(struct phytmac *pdata, + int queue_index, u32 mask) +{ + u32 value; + + value = phytmac_get_irq_mask(mask); + + if (queue_index == 0) + PHYTMAC_WRITE(pdata, PHYTMAC_IS, value); + else + PHYTMAC_WRITE(pdata, PHYTMAC_ISR(queue_index - 1), value); +} + +static unsigned int phytmac_get_intx_mask(struct phytmac *pdata) +{ + u32 value; + + value = PHYTMAC_READ(pdata, PHYTMAC_INTX_IRQ_MASK); + PHYTMAC_WRITE(pdata, PHYTMAC_INTX_IRQ_MASK, value); + + return value; +} + +static unsigned int phytmac_get_irq(struct phytmac *pdata, int queue_index) +{ + u32 status, value; + + if (queue_index == 0) + value = PHYTMAC_READ(pdata, PHYTMAC_IS); + else + value = PHYTMAC_READ(pdata, PHYTMAC_ISR(queue_index - 1)); + + status = phytmac_get_irq_status(value); + + return status; +} + +static unsigned int phytmac_tx_map_desc(struct phytmac_queue *queue, + u32 tx_tail, struct packet_info *packet) +{ + unsigned int i, ctrl; + struct phytmac *pdata = queue->pdata; + struct phytmac_dma_desc *desc; + struct phytmac_tx_skb *tx_skb; + unsigned int eof = 1; + + i = tx_tail; + + if (!(pdata->capacities & PHYTMAC_CAPS_TAILPTR)) { + ctrl = PHYTMAC_BIT(TX_USED); + desc = phytmac_get_tx_desc(queue, i); + desc->desc1 = ctrl; + } + + do { + i--; + tx_skb = phytmac_get_tx_skb(queue, i); + desc = phytmac_get_tx_desc(queue, i); + + ctrl = (u32)tx_skb->length; + if (eof) { + ctrl |= PHYTMAC_BIT(TX_LAST); + eof = 0; + } + + if (unlikely(i == (pdata->tx_ring_size - 1))) + ctrl |= PHYTMAC_BIT(TX_WRAP); + + if (i == queue->tx_tail) { + ctrl |= PHYTMAC_BITS(TX_LSO, packet->lso); + ctrl |= PHYTMAC_BITS(TX_TCP_SEQ_SRC, packet->seq); + if (packet->nocrc) + ctrl |= PHYTMAC_BIT(TX_NOCRC); + } else { + ctrl |= PHYTMAC_BITS(MSS_MFS, packet->mss); + } + + desc->desc2 = upper_32_bits(tx_skb->addr); + desc->desc0 = lower_32_bits(tx_skb->addr); + /* Make newly descriptor visible to hardware */ + wmb(); + desc->desc1 = ctrl; + } while (i != queue->tx_tail); + + return 0; +} + +static void phytmac_init_rx_map_desc(struct phytmac_queue *queue, + u32 index) +{ + struct phytmac_dma_desc *desc; + + desc = phytmac_get_rx_desc(queue, index); + + desc->desc1 = 0; + /* Make newly descriptor to hardware */ + dma_wmb(); + desc->desc0 |= PHYTMAC_BIT(RX_USED); +} + +static unsigned int phytmac_rx_map_desc(struct phytmac_queue *queue, + u32 index, dma_addr_t addr) +{ + struct phytmac *pdata = queue->pdata; + struct phytmac_dma_desc *desc; + + desc = phytmac_get_rx_desc(queue, index); + + if (addr) { + if (unlikely(index == (pdata->rx_ring_size - 1))) + addr |= PHYTMAC_BIT(RX_WRAP); + desc->desc1 = 0; + desc->desc2 = upper_32_bits(addr); + /* Make newly descriptor to hardware */ + dma_wmb(); + desc->desc0 = lower_32_bits(addr); + } else { + desc->desc1 = 0; + /* Make newly descriptor to hardware */ + dma_wmb(); + desc->desc0 &= ~PHYTMAC_BIT(RX_USED); + } + + return 0; +} + +static void phytmac_tx_start(struct phytmac_queue *queue) +{ + struct phytmac *pdata = queue->pdata; + + if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) + PHYTMAC_WRITE(pdata, PHYTMAC_TAILPTR(queue->index), + BIT(31) | queue->tx_tail); + + if (pdata->capacities & PHYTMAC_CAPS_START) + PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, + PHYTMAC_READ(pdata, PHYTMAC_NCTRL) | PHYTMAC_BIT(TSTART)); +} + +static void phytmac_restart(struct phytmac *pdata) +{ + int q; + struct phytmac_queue *queue; + + for (q = 0; q < pdata->queues_num; q++) { + queue = &pdata->queues[q]; + if (queue->tx_head != queue->tx_tail) { + PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, + PHYTMAC_READ(pdata, PHYTMAC_NCTRL) | PHYTMAC_BIT(TSTART)); + break; + } + } +} + +static int phytmac_tx_complete(const struct phytmac_dma_desc *desc) +{ + return PHYTMAC_GET_BITS(desc->desc1, TX_USED); +} + +static int phytmac_rx_complete(const struct phytmac_dma_desc *desc) +{ + return (desc->desc0 & PHYTMAC_BIT(RX_USED)) != 0; +} + +static int phytmac_rx_pkt_len(struct phytmac *pdata, const struct phytmac_dma_desc *desc) +{ + if (pdata->capacities & PHYTMAC_CAPS_JUMBO) + return desc->desc1 & PHYTMAC_JUMBO_FRAME_MASK; + else + return desc->desc1 & PHYTMAC_FRAME_MASK; +} + +static dma_addr_t phytmac_get_desc_addr(const struct phytmac_dma_desc *desc) +{ + dma_addr_t addr = 0; + + addr = ((u64)(desc->desc2) << 32); + + addr |= (desc->desc0 & 0xfffffffc); + return addr; +} + +static bool phytmac_rx_checksum(const struct phytmac_dma_desc *desc) +{ + u32 value = desc->desc1; + u32 check = value >> PHYTMAC_RX_CSUM_INDEX & 0x3; + + return (check == PHYTMAC_RX_CSUM_IP_TCP || check == PHYTMAC_RX_CSUM_IP_UDP); +} + +static bool phytmac_rx_single_buffer(const struct phytmac_dma_desc *desc) +{ + u32 value = desc->desc1; + + return ((value & PHYTMAC_BIT(RX_SOF)) && (value & PHYTMAC_BIT(RX_EOF))); +} + +static bool phytmac_rx_sof(const struct phytmac_dma_desc *desc) +{ + u32 value = desc->desc1; + + return (value & PHYTMAC_BIT(RX_SOF)); +} + +static bool phytmac_rx_eof(const struct phytmac_dma_desc *desc) +{ + u32 value = desc->desc1; + + return (value & PHYTMAC_BIT(RX_EOF)); +} + +static void phytmac_clear_rx_desc(struct phytmac_queue *queue, int begin, int end) +{ + unsigned int frag; + unsigned int tmp = end; + struct phytmac_dma_desc *desc; + + if (begin > end) + tmp = end + queue->pdata->rx_ring_size; + + for (frag = begin; frag != end; frag++) { + desc = phytmac_get_rx_desc(queue, frag); + desc->desc0 &= ~PHYTMAC_BIT(RX_USED); + } +} + +static void phytmac_mac_interface_config(struct phytmac *pdata, unsigned int mode, + const struct phylink_link_state *state) +{ + u32 old_ctrl, old_config; + u32 ctrl, config, usxctl; + + old_ctrl = PHYTMAC_READ(pdata, PHYTMAC_NCTRL); + old_config = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG); + ctrl = old_ctrl; + config = old_config; + + if (state->speed == SPEED_10000) + PHYTMAC_WRITE(pdata, PHYTMAC_HCONFIG, PHYTMAC_SPEED_10000M); + else if (state->speed == SPEED_5000) + PHYTMAC_WRITE(pdata, PHYTMAC_HCONFIG, PHYTMAC_SPEED_5000M); + else if (state->speed == SPEED_2500) + PHYTMAC_WRITE(pdata, PHYTMAC_HCONFIG, PHYTMAC_SPEED_2500M); + else if (state->speed == SPEED_1000) + PHYTMAC_WRITE(pdata, PHYTMAC_HCONFIG, PHYTMAC_SPEED_1000M); + else if (state->speed == SPEED_100 || state->speed == SPEED_10) + PHYTMAC_WRITE(pdata, PHYTMAC_HCONFIG, PHYTMAC_SPEED_100M); + + config &= ~(PHYTMAC_BIT(SGMII_EN) | PHYTMAC_BIT(PCS_EN) + | PHYTMAC_BIT(SPEED) | PHYTMAC_BIT(FD) | PHYTMAC_BIT(GM_EN)); + ctrl &= ~(PHYTMAC_BIT(HIGHSPEED) | PHYTMAC_BIT(2PT5G)); + + if (state->interface == PHY_INTERFACE_MODE_SGMII || + state->interface == PHY_INTERFACE_MODE_2500BASEX) { + config |= PHYTMAC_BIT(SGMII_EN) | PHYTMAC_BIT(PCS_EN); + } else if (state->interface == PHY_INTERFACE_MODE_10GBASER || + state->interface == PHY_INTERFACE_MODE_USXGMII || + state->interface == PHY_INTERFACE_MODE_5GBASER) { + usxctl = PHYTMAC_READ(pdata, PHYTMAC_USXCTRL); + if (state->speed == SPEED_10000) { + usxctl = PHYTMAC_SET_BITS(usxctl, SERDES_RATE, PHYTMAC_SERDES_RATE_10G); + usxctl = PHYTMAC_SET_BITS(usxctl, USX_SPEED, PHYTMAC_SPEED_10000M); + } else if (state->speed == SPEED_5000) { + usxctl = PHYTMAC_SET_BITS(usxctl, SERDES_RATE, PHYTMAC_SERDES_RATE_5G); + usxctl = PHYTMAC_SET_BITS(usxctl, USX_SPEED, PHYTMAC_SPEED_5000M); + } + usxctl |= PHYTMAC_BIT(RX_EN) | PHYTMAC_BIT(TX_EN); + PHYTMAC_WRITE(pdata, PHYTMAC_USXCTRL, usxctl); + + config |= PHYTMAC_BIT(PCS_EN); + ctrl |= PHYTMAC_BIT(HIGHSPEED); + } + + if (state->duplex) + config |= PHYTMAC_BIT(FD); + + if (old_ctrl ^ ctrl) + PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, ctrl); + + if (old_config ^ config) + PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, config); + + /* Disable AN for SGMII fixed link configuration, enable otherwise.*/ + if (state->interface == PHY_INTERFACE_MODE_SGMII || + state->interface == PHY_INTERFACE_MODE_2500BASEX) + phytmac_enable_autoneg(pdata, mode == MLO_AN_FIXED ? 0 : 1); +} + +static unsigned int phytmac_pcs_get_link(struct phytmac *pdata, + phy_interface_t interface) +{ + if (interface == PHY_INTERFACE_MODE_SGMII || + interface == PHY_INTERFACE_MODE_2500BASEX) + return PHYTMAC_READ_BITS(pdata, PHYTMAC_NSTATUS, PCS_LINK); + else if (interface == PHY_INTERFACE_MODE_USXGMII || + interface == PHY_INTERFACE_MODE_10GBASER) + return PHYTMAC_READ_BITS(pdata, PHYTMAC_USXSTATUS, USX_PCS_LINK); + + return 0; +} + +static void phytmac_clear_tx_desc(struct phytmac_queue *queue) +{ + struct phytmac *pdata = queue->pdata; + struct phytmac_dma_desc *desc = NULL; + int i; + + for (i = 0; i < queue->pdata->tx_ring_size; i++) { + desc = phytmac_get_tx_desc(queue, i); + desc->desc2 = 0; + desc->desc0 = 0; + /* make newly desc1 to hardware */ + wmb(); + desc->desc1 = PHYTMAC_BIT(TX_USED); + } + desc->desc1 |= PHYTMAC_BIT(TX_WRAP); + + if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) + PHYTMAC_WRITE(pdata, PHYTMAC_TAILPTR(queue->index), queue->tx_tail); +} + +static void phytmac_get_hw_stats(struct phytmac *pdata) +{ + u32 stats[44]; + int i, j; + u64 val; + u64 *p = &pdata->stats.tx_octets; + + for (i = 0 ; i < 44; i++) + stats[i] = PHYTMAC_READ(pdata, PHYTMAC_OCTTX + i * 4); + + for (i = 0, j = 0; i < 44; i++) { + if (i == 0 || i == 20) { + val = (u64)stats[i + 1] << 32 | stats[i]; + *p += val; + pdata->ethtool_stats[j] = *p; + ++j; + ++p; + } else { + if (i != 1 && i != 21) { + val = stats[i]; + *p += val; + pdata->ethtool_stats[j] = *p; + ++j; + ++p; + } + } + } +} + +static void phytmac_get_time(struct phytmac *pdata, struct timespec64 *ts) +{ + u32 ns, secl, sech; + + ns = PHYTMAC_READ(pdata, PHYTMAC_TN); + secl = PHYTMAC_READ(pdata, PHYTMAC_TSL); + sech = PHYTMAC_READ(pdata, PHYTMAC_TSH); + + ts->tv_nsec = ns; + ts->tv_sec = (((u64)sech << 32) | secl) & SEC_MAX_VAL; +} + +static void phytmac_set_time(struct phytmac *pdata, time64_t sec, long nsec) +{ + u32 secl, sech; + + secl = (u32)sec; + sech = (sec >> 32) & (0xffff); + + PHYTMAC_WRITE(pdata, PHYTMAC_TN, 0); + PHYTMAC_WRITE(pdata, PHYTMAC_TSH, sech); + PHYTMAC_WRITE(pdata, PHYTMAC_TSL, secl); + PHYTMAC_WRITE(pdata, PHYTMAC_TN, nsec); +} + +static void phytmac_clear_time(struct phytmac *pdata) +{ + u32 value; + + pdata->ts_incr.sub_ns = 0; + pdata->ts_incr.ns = 0; + + value = PHYTMAC_READ(pdata, PHYTMAC_TISN); + value = PHYTMAC_SET_BITS(value, SUB_NSECL, 0); + value = PHYTMAC_SET_BITS(value, SUB_NSECH, 0); + PHYTMAC_WRITE(pdata, PHYTMAC_TISN, value); + + value = PHYTMAC_READ(pdata, PHYTMAC_TI); + value = PHYTMAC_SET_BITS(value, INCR_NS, 0); + PHYTMAC_WRITE(pdata, PHYTMAC_TI, value); + + PHYTMAC_WRITE(pdata, PHYTMAC_TA, 0); +} + +static int phytmac_set_tsmode(struct phytmac *pdata, struct ts_ctrl *ctrl) +{ + if (ctrl->rx_control == TS_ALL_PTP_FRAMES) + PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, + PHYTMAC_READ(pdata, PHYTMAC_NCTRL) | PHYTMAC_BIT(STORE_RX_TS)); + + PHYTMAC_WRITE(pdata, PHYTMAC_TXBDCTRL, PHYTMAC_BITS(TX_TSMODE, ctrl->tx_control)); + PHYTMAC_WRITE(pdata, PHYTMAC_RXBDCTRL, PHYTMAC_BITS(RX_TSMODE, ctrl->rx_control)); + + return 0; +} + +static int phytmac_set_tsincr(struct phytmac *pdata, struct ts_incr *incr) +{ + u32 value; + + value = PHYTMAC_BITS(SUB_NSECL, incr->sub_ns) | + PHYTMAC_BITS(SUB_NSECH, incr->sub_ns >> 8); + PHYTMAC_WRITE(pdata, PHYTMAC_TISN, value); + PHYTMAC_WRITE(pdata, PHYTMAC_TI, incr->ns); + + return 0; +} + +static void phytmac_ptp_init_hw(struct phytmac *pdata) +{ + struct timespec64 ts; + + ts = ns_to_timespec64(ktime_to_ns(ktime_get_real())); + phytmac_set_time(pdata, ts.tv_sec, ts.tv_nsec); + + phytmac_set_tsincr(pdata, &pdata->ts_incr); +} + +static int phytmac_adjust_fine(struct phytmac *pdata, long ppm, bool negative) +{ + struct ts_incr ts_incr; + u32 tmp; + u64 adj; + + ts_incr.ns = pdata->ts_incr.ns; + ts_incr.sub_ns = pdata->ts_incr.sub_ns; + + tmp = ((u64)ts_incr.ns << PHYTMAC_SUB_NSECL_INDEX) + ts_incr.sub_ns; + adj = ((u64)ppm * tmp + (USEC_PER_SEC >> 1)) >> PHYTMAC_SUB_NSECL_INDEX; + + adj = div_u64(adj, USEC_PER_SEC); + adj = negative ? (tmp - adj) : (tmp + adj); + + ts_incr.ns = (adj >> PHYTMAC_SUB_NSEC_WIDTH) + & ((1 << PHYTMAC_SUB_NSECL_WIDTH) - 1); + ts_incr.sub_ns = adj & ((1 << PHYTMAC_SUB_NSEC_WIDTH) - 1); + + phytmac_set_tsincr(pdata, &ts_incr); + + return 0; +} + +static int phytmac_adjust_time(struct phytmac *pdata, s64 delta, int neg) +{ + u32 adj; + + if (delta > PHYTMAC_ADJUST_SEC_MAX) { + struct timespec64 now, then; + + if (neg) + then = ns_to_timespec64(-delta); + else + then = ns_to_timespec64(delta); + phytmac_get_time(pdata, &now); + now = timespec64_add(now, then); + phytmac_set_time(pdata, now.tv_sec, now.tv_nsec); + } else { + adj = (neg << PHYTMAC_INCR_ADD_INDEX) | delta; + PHYTMAC_WRITE(pdata, PHYTMAC_TA, adj); + } + + return 0; +} + +static int phytmac_ts_valid(struct phytmac *pdata, struct phytmac_dma_desc *desc, int direction) +{ + int ts_valid = 0; + + if (direction == PHYTMAC_TX) + ts_valid = desc->desc1 & PHYTMAC_BIT(TX_TS_VALID); + else if (direction == PHYTMAC_RX) + ts_valid = desc->desc0 & PHYTMAC_BIT(RX_TS_VALID); + return ts_valid; +} + +static void phytmac_get_dma_ts(struct phytmac *pdata, u32 ts_1, u32 ts_2, struct timespec64 *ts) +{ + struct timespec64 ts2; + + ts->tv_sec = (PHYTMAC_GET_BITS(ts_2, DMA_SECH) << 2) | + PHYTMAC_GET_BITS(ts_1, DMA_SECL); + ts->tv_nsec = PHYTMAC_GET_BITS(ts_1, DMA_NSEC); + + phytmac_get_time(pdata, &ts2); + + if (((ts->tv_sec ^ ts2.tv_sec) & (PHYTMAC_DMA_SEC_TOP >> 1)) != 0) + ts->tv_sec -= PHYTMAC_DMA_SEC_TOP; + + ts->tv_sec += (ts2.tv_sec & (~PHYTMAC_DMA_SEC_MASK)); +} + +static unsigned int phytmac_get_ts_rate(struct phytmac *pdata) +{ + return 300000000; +} + +struct phytmac_hw_if phytmac_1p0_hw = { + .reset_hw = phytmac_reset_hw, + .init_hw = phytmac_init_hw, + .init_ring_hw = phytmac_init_ring_hw, + .get_feature = phytmac_get_feature_all, + .get_regs = phytmac_get_regs, + .get_stats = phytmac_get_hw_stats, + .set_mac_address = phytmac_set_mac_addr, + .get_mac_address = phytmac_get_mac_addr, + .mdio_read = phytmac_mdio_data_read, + .mdio_write = phytmac_mdio_data_write, + .poweron = phytmac_powerup_hw, + .set_wol = phytmac_set_wake, + .enable_promise = phytmac_enable_promise, + .enable_multicast = phytmac_enable_multicast, + .set_hash_table = phytmac_set_mc_hash, + .enable_rx_csum = phytmac_enable_rxcsum, + .enable_tx_csum = phytmac_enable_txcsum, + .enable_mdio_control = phytmac_enable_mdio, + .enable_autoneg = phytmac_enable_autoneg, + .enable_pause = phytmac_enable_pause, + .enable_network = phytmac_enable_network, + .add_fdir_entry = phytmac_add_fdir_entry, + .del_fdir_entry = phytmac_del_fdir_entry, + /* mac config */ + .mac_config = phytmac_mac_interface_config, + .mac_linkup = phytmac_mac_linkup, + .mac_linkdown = phytmac_mac_linkdown, + .pcs_linkup = phytmac_pcs_linkup, + .pcs_linkdown = phytmac_pcs_linkdown, + .get_link = phytmac_pcs_get_link, + /* irq */ + .enable_irq = phytmac_enable_irq, + .disable_irq = phytmac_disable_irq, + .clear_irq = phytmac_clear_irq, + .get_irq = phytmac_get_irq, + .get_intx_mask = phytmac_get_intx_mask, + /* tx and rx */ + .tx_map = phytmac_tx_map_desc, + .transmit = phytmac_tx_start, + .restart = phytmac_restart, + .tx_complete = phytmac_tx_complete, + .rx_complete = phytmac_rx_complete, + .get_rx_pkt_len = phytmac_rx_pkt_len, + .get_desc_addr = phytmac_get_desc_addr, + .init_rx_map = phytmac_init_rx_map_desc, + .rx_map = phytmac_rx_map_desc, + .rx_checksum = phytmac_rx_checksum, + .rx_single_buffer = phytmac_rx_single_buffer, + .rx_pkt_start = phytmac_rx_sof, + .rx_pkt_end = phytmac_rx_eof, + .clear_rx_desc = phytmac_clear_rx_desc, + .clear_tx_desc = phytmac_clear_tx_desc, + /* ptp */ + .init_ts_hw = phytmac_ptp_init_hw, + .set_time = phytmac_set_time, + .clear_time = phytmac_clear_time, + .get_time = phytmac_get_time, + .set_ts_config = phytmac_set_tsmode, + .set_incr = phytmac_set_tsincr, + .adjust_fine = phytmac_adjust_fine, + .adjust_time = phytmac_adjust_time, + .ts_valid = phytmac_ts_valid, + .get_timestamp = phytmac_get_dma_ts, + .get_ts_rate = phytmac_get_ts_rate, +}; +EXPORT_SYMBOL_GPL(phytmac_1p0_hw); + diff --git a/drivers/net/ethernet/phytium/phytmac_v1.h b/drivers/net/ethernet/phytium/phytmac_v1.h new file mode 100644 index 0000000000000000000000000000000000000000..6f2b521aaebdbbd6e7db675f3d0056c87c29f815 --- /dev/null +++ b/drivers/net/ethernet/phytium/phytmac_v1.h @@ -0,0 +1,453 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _PHYTMAC_V1_H +#define _PHYTMAC_V1_H + +extern struct phytmac_hw_if phytmac_1p0_hw; + +#define PHYTMAC_FRAME_MASK 0x1fff +#define PHYTMAC_JUMBO_FRAME_MASK 0x3fff + +#define PHYTMAC_SPEED_100M 0 +#define PHYTMAC_SPEED_1000M 1 +#define PHYTMAC_SPEED_2500M 2 +#define PHYTMAC_SPEED_5000M 3 +#define PHYTMAC_SPEED_10000M 4 +#define PHYTMAC_SERDES_RATE_5G 0 +#define PHYTMAC_SERDES_RATE_10G 1 + +#define PHYTMAC_NCTRL 0x0000 +#define PHYTMAC_NCONFIG 0x0004 +#define PHYTMAC_NSTATUS 0x0008 +#define PHYTMAC_DCONFIG 0x0010 +#define PHYTMAC_RXPTR_Q0 0x0018 +#define PHYTMAC_TXPTR_Q0 0x001C +#define PHYTMAC_IS 0x0024 +#define PHYTMAC_IE 0x0028 +#define PHYTMAC_ID 0x002C +#define PHYTMAC_IM 0x0030 +#define PHYTMAC_MDATA 0x0034 +#define PHYTMAC_HCONFIG 0x0050 +#define PHYTMAC_AXICTRL 0x0054 +#define PHYTMAC_INT_MODERATION 0x005C +#define PHYTMAC_HASHB 0x0080 +#define PHYTMAC_HASHT 0x0084 +#define PHYTMAC_MAC1B 0x0088 +#define PHYTMAC_MAC1T 0x008C +#define PHYTMAC_WOL 0x00B8 +#define PHYTMAC_OCTTX 0x0100 +#define PHYTMAC_TISN 0x01BC +#define PHYTMAC_TSH 0x01C0 +#define PHYTMAC_TSL 0x01D0 +#define PHYTMAC_TN 0x01D4 +#define PHYTMAC_TA 0x01D8 +#define PHYTMAC_TI 0x01DC +#define PHYTMAC_PCSCTRL 0x0200 +#define PHYTMAC_PCSSTATUS 0x0204 +#define PHYTMAC_PCSANLPBASE 0x0214 +#define PHYTMAC_DEFAULT1 0x0280 /* Default HW Config 1 */ +#define PHYTMAC_DEFAULT2 0x0294 /* Default HW Config 2 */ +#define PHYTMAC_DEFAULT3 0x029C /* Default HW Config 3 */ +#define PHYTMAC_DEFAULT4 0x02A4 /* Default HW Config 4 */ +#define PHYTMAC_DEFAULT5 0x02AC /* Default HW Config 5 */ +#define PHYTMAC_USXCTRL 0x0A80 +#define PHYTMAC_USXSTATUS 0x0A88 +#define PHYTMAC_TXBDCTRL 0x04CC +#define PHYTMAC_RXBDCTRL 0x04D0 + +/* Fdir match registers */ +#define PHYTMAC_FDIR(i) (0x0540 + ((i) << 2)) + +/* EtherType registers */ +#define PHYTMAC_ETHT(i) (0x06E0 + ((i) << 2)) + +/* Fdir compare registers */ +#define PHYTMAC_COMP1(i) (0x0700 + ((i) << 3)) +#define PHYTMAC_COMP2(i) (0x0704 + ((i) << 3)) + +#define PHYTMAC_ISR(i) (0x0400 + ((i) << 2)) +#define PHYTMAC_TXPTR(i) (0x0440 + ((i) << 2)) +#define PHYTMAC_RXPTR(i) (0x0480 + ((i) << 2)) +#define PHYTMAC_RBQS(i) (0x04A0 + ((i) << 2)) +#define PHYTMAC_TXPTRH(i) (0x04c8) +#define PHYTMAC_RXPTRH(i) (0x04d4) + +#define PHYTMAC_IER(i) (0x0600 + ((i) << 2)) +#define PHYTMAC_IDR(i) (0x0620 + ((i) << 2)) +#define PHYTMAC_IMR(i) (0x0640 + ((i) << 2)) +#define PHYTMAC_TAIL_ENABLE (0x0e7c) +#define PHYTMAC_TAILPTR(i) (0x0e80 + ((i) << 2)) + +#define PHYTMAC_PHY_INT_ENABLE 0x1C88 +#define PHYTMAC_PHY_INT_CLEAR 0x1C8C +#define PHYTMAC_PHY_INT_STATE 0x1C90 +#define PHYTMAC_INTX_IRQ_MASK 0x1C7C + +#define PHYTMAC_READ_NSTATUS(pdata) PHYTMAC_READ(pdata, PHYTMAC_NSTATUS) + +/* Ethernet Network Control Register */ +#define PHYTMAC_RE_INDEX 2 /* Receive enable */ +#define PHYTMAC_RE_WIDTH 1 +#define PHYTMAC_TE_INDEX 3 /* Transmit enable */ +#define PHYTMAC_TE_WIDTH 1 +#define PHYTMAC_MPE_INDEX 4 /* Management port enable */ +#define PHYTMAC_MPE_WIDTH 1 +#define PHYTMAC_CLEARSTAT_INDEX 5 /* Clear stats regs */ +#define PHYTMAC_CLEARSTAT_WIDTH 1 +#define PHYTMAC_TSTART_INDEX 9 /* Start transmission */ +#define PHYTMAC_TSTART_WIDTH 1 +#define PHYTMAC_THALT_INDEX 10 /* Transmit halt */ +#define PHYTMAC_THALT_WIDTH 1 +#define PHYTMAC_STORE_RX_TS_INDEX 15 +#define PHYTMAC_STORE_RX_TS_WIDTH 1 +#define PHYTMAC_OSSMODE_INDEX 24 /* Enable One Step Synchro Mode */ +#define PHYTMAC_OSSMODE_WIDTH 1 +#define PHYTMAC_2PT5G_INDEX 29 /* 2.5G operation selected */ +#define PHYTMAC_2PT5G_WIDTH 1 +#define PHYTMAC_HIGHSPEED_INDEX 31 /* High speed enable */ +#define PHYTMAC_HIGHSPEED_WIDTH 1 + +/* Ethernet Network Config Register */ +#define PHYTMAC_SPEED_INDEX 0 /* Speed */ +#define PHYTMAC_SPEED_WIDTH 1 +#define PHYTMAC_FD_INDEX 1 /* Full duplex */ +#define PHYTMAC_FD_WIDTH 1 +#define PHYTMAC_JUMBO_EN_INDEX 3 /* Transmit enable */ +#define PHYTMAC_JUMBO_EN_WIDTH 1 +#define PHYTMAC_PROMISC_INDEX 4 /* Copy all frames */ +#define PHYTMAC_PROMISC_WIDTH 1 +#define PHYTMAC_NO_BCAST_INDEX 5 /* No broadcast */ +#define PHYTMAC_NO_BCAST_WIDTH 1 +#define PHYTMAC_MH_EN_INDEX 6 /* Multicast hash enable */ +#define PHYTMAC_MH_EN_WIDTH 1 +#define PHYTMAC_RCV_BIG_INDEX 8 +#define PHYTMAC_RCV_BIG_WIDTH 1 +#define PHYTMAC_GM_EN_INDEX 10 /* Gigabit mode enable */ +#define PHYTMAC_GM_EN_WIDTH 1 +#define PHYTMAC_PCS_EN_INDEX 11 /* PCS select */ +#define PHYTMAC_PCS_EN_WIDTH 1 +#define PHYTMAC_PAUSE_EN_INDEX 13 /* Pause enable */ +#define PHYTMAC_PAUSE_EN_WIDTH 1 +#define PHYTMAC_FCS_REMOVE_INDEX 17 /* FCS remove */ +#define PHYTMAC_FCS_REMOVE_WIDTH 1 +#define PHYTMAC_MCD_INDEX 18 /* MDC clock division */ +#define PHYTMAC_MCD_WIDTH 3 +#define PHYTMAC_DBW64_INDEX 21 /* Data bus width */ +#define PHYTMAC_DBW64_WIDTH 1 +#define PHYTMAC_DBW128_INDEX 22 /* Data bus width */ +#define PHYTMAC_DBW128_WIDTH 1 +#define PHYTMAC_DBW_32 1 +#define PHYTMAC_DBW_64 2 +#define PHYTMAC_DBW_128 4 +#define PHYTMAC_RCO_EN_INDEX 24 /* Receive checksum offload enable */ +#define PHYTMAC_RCO_EN_WIDTH 1 +#define PHYTMAC_SGMII_EN_INDEX 27 /* Sgmii mode enable */ +#define PHYTMAC_SGMII_EN_WIDTH 1 + +/* Ethernet Network Status Register */ +#define PHYTMAC_PCS_LINK_INDEX 0 /* PCS link status */ +#define PHYTMAC_PCS_LINK_WIDTH 1 +#define PHYTMAC_MDIO_IDLE_INDEX 2 /* Mdio idle */ +#define PHYTMAC_MDIO_IDLE_WIDTH 1 +#define PHYTMAC_PCS_FD_INDEX 3 /* PCS auto negotiation duplex resolution */ +#define PHYTMAC_PCS_FD__WIDTH 1 + +/* Ethernet Network Dma config Register */ +#define PHYTMAC_BURST_INDEX 0 /* Amba burst length */ +#define PHYTMAC_BURST_WIDTH 5 +#define PHYTMAC_ENDIA_PKT_INDEX 6 +#define PHYTMAC_ENDIA_PKT_WIDTH 1 +#define PHYTMAC_ENDIA_DESC_INDEX 7 +#define PHYTMAC_ENDIA_DESC_WIDTH 1 +#define PHYTMAC_TCO_EN_INDEX 11 /* Tx Checksum Offload en */ +#define PHYTMAC_TCO_EN_WIDTH 1 +#define PHYTMAC_RX_BUF_LEN_INDEX 16 /* DMA receive buffer size */ +#define PHYTMAC_RX_BUF_LEN_WIDTH 8 +#define PHYTMAC_RX_EXBD_EN_INDEX 28 /* Enable RX extended BD mode */ +#define PHYTMAC_RX_EXBD_EN_WIDTH 1 +#define PHYTMAC_TX_EXBD_EN_INDEX 29 /* Enable TX extended BD mode */ +#define PHYTMAC_TX_EXBD_EN_WIDTH 1 +#define PHYTMAC_ABW_INDEX 30 /* DMA address bus width */ +#define PHYTMAC_ABW_WIDTH 1 + +/* Int stauts/Enable/Disable/Mask Register */ +#define PHYTMAC_RXCOMP_INDEX 1 /* Rx complete */ +#define PHYTMAC_RXCOMP_WIDTH 1 +#define PHYTMAC_RUB_INDEX 2 /* Rx used bit read */ +#define PHYTMAC_RUB_WIDTH 1 +#define PHYTMAC_BUS_ERR_INDEX 6 /* AMBA error */ +#define PHYTMAC_BUS_ERR_WIDTH 1 +#define PHYTMAC_TXCOMP_INDEX 7 /* Tx complete */ +#define PHYTMAC_TXCOMP_WIDTH 1 +#define PHYTMAC_RXOVERRUN_INDEX 10 /* Tx overrun */ +#define PHYTMAC_RXOVERRUN_WIDTH 1 +#define PHYTMAC_RESP_ERR_INDEX 11 /* Resp not ok */ +#define PHYTMAC_RESP_ERR_WIDTH 1 + +/* Mdio read/write Register */ +#define PHYTMAC_DATA_INDEX 0 /* Data */ +#define PHYTMAC_DATA_WIDTH 16 +#define PHYTMAC_MUST_INDEX 16 /* Must Be 10 */ +#define PHYTMAC_MUST_WIDTH 2 +#define PHYTMAC_REG_ADDR_INDEX 18 /* Register address */ +#define PHYTMAC_REG_ADDR_WIDTH 5 +#define PHYTMAC_PHY_ADDR_INDEX 23 /* Phy address */ +#define PHYTMAC_PHY_ADDR_WIDTH 5 +#define PHYTMAC_OPS_INDEX 28 +#define PHYTMAC_OPS_WIDTH 2 +#define PHYTMAC_CLAUSE_SEL_INDEX 30 +#define PHYTMAC_CLAUSE_SEL_WIDTH 1 +#define PHYTMAC_CLAUSE_C22 1 +#define PHYTMAC_CLAUSE_C45 0 +#define PHYTMAC_OPS_C45_ADDR 0 +#define PHYTMAC_OPS_C45_WRITE 1 +#define PHYTMAC_OPS_C45_READ 3 +#define PHYTMAC_OPS_C22_WRITE 1 +#define PHYTMAC_OPS_C22_READ 2 + +/* hs mac config register */ +#define PHYTMAC_HS_SPEED_INDEX 0 +#define PHYTMAC_HS_SPEED_WIDTH 3 +#define PHYTMAC_HS_SPEED_100M 0 +#define PHYTMAC_HS_SPEED_1000M 1 +#define PHYTMAC_HS_SPEED_2500M 2 +#define PHYTMAC_HS_SPEED_5000M 3 +#define PHYTMAC_HS_SPEED_10G 4 + +/* WOL register */ +#define PHYTMAC_ARP_IP_INDEX 0 +#define PHYTMAC_ARP_IP_WIDTH 16 +#define PHYTMAC_MAGIC_INDEX 16 +#define PHYTMAC_MAGIC_WIDTH 1 +#define PHYTMAC_ARP_INDEX 17 +#define PHYTMAC_ARP_WIDTH 1 +#define PHYTMAC_UCAST_INDEX 18 +#define PHYTMAC_UCAST_WIDTH 1 +#define PHYTMAC_MCAST_INDEX 19 +#define PHYTMAC_MCAST_WIDTH 1 + +/* PCSCTRL register */ +#define PHYTMAC_AUTONEG_INDEX 12 +#define PHYTMAC_AUTONEG_WIDTH 1 + +/* DEFAULT1 register */ +#define PHYTMAC_DBW_INDEX 25 +#define PHYTMAC_DBW_WIDTH 3 + +/* DEFAULT2 register */ +#define PHYTMAC_DAW64_INDEX 23 +#define PHYTMAC_DAW64_WIDTH 1 + +/* DEFAULT3 register */ +#define PHYTMAC_SCR2CMP_INDEX 0 +#define PHYTMAC_SCR2CMP_WIDTH 8 +#define PHYTMAC_SCR2ETH_INDEX 8 +#define PHYTMAC_SCR2ETH_WIDTH 8 + +/* DEFAULT4 register */ +#define PHYTMAC_TXDESCRD_INDEX 12 +#define PHYTMAC_TXDESCRD_WIDTH 4 +#define PHYTMAC_RXDESCRD_INDEX 8 +#define PHYTMAC_RXDESCRD_WIDTH 4 + +/* USXCTRL register */ +#define PHYTMAC_RX_EN_INDEX 0 +#define PHYTMAC_RX_EN_WIDTH 1 +#define PHYTMAC_TX_EN_INDEX 1 +#define PHYTMAC_TX_EN_WIDTH 1 +#define PHYTMAC_RX_SYNC_RESET_INDEX 2 +#define PHYTMAC_RX_SYNC_RESET_WIDTH 1 +#define PHYTMAC_SERDES_RATE_INDEX 12 +#define PHYTMAC_SERDES_RATE_WIDTH 2 +#define PHYTMAC_USX_SPEED_INDEX 14 +#define PHYTMAC_USX_SPEED_WIDTH 3 + +/* Bitfields in USX_STATUS. */ +#define PHYTMAC_USX_PCS_LINK_INDEX 0 +#define PHYTMAC_USX_PCS_LINK_WIDTH 1 + +/* Bitfields in PHYTMAC_TISN */ +#define PHYTMAC_SUB_NSECH_INDEX 0 +#define PHYTMAC_SUB_NSECH_WIDTH 16 +#define PHYTMAC_SUB_NSECL_INDEX 24 +#define PHYTMAC_SUB_NSECL_WIDTH 8 +#define PHYTMAC_SUB_NSEC_WIDTH 24 + +/* Bitfields in PHYTMAC_TSH */ +#define PHYTMAC_SECH_INDEX 0 +#define PHYTMAC_SECH_WIDTH 16 + +/* Bitfields in PHYTMAC_TSL */ +#define PHYTMAC_SECL_INDEX 0 +#define PHYTMAC_SECL_WIDTH 32 + +/* Bitfields in PHYTMAC_TN */ +#define PHYTMAC_NSEC_INDEX 0 +#define PHYTMAC_NSEC_WIDTH 30 + +/* Bitfields in PHYTMAC_TA */ +#define PHYTMAC_INCR_SEC_INDEX 0 +#define PHYTMAC_INCR_SEC_WIDTH 30 +#define PHYTMAC_INCR_ADD_INDEX 31 +#define PHYTMAC_INCR_ADD_WIDTH 1 +#define PHYTMAC_ADJUST_SEC_MAX ((1 << PHYTMAC_INCR_SEC_WIDTH) - 1) + +/* Bitfields in PHYTMAC_TI */ +#define PHYTMAC_INCR_NS_INDEX 0 +#define PHYTMAC_INCR_NS_WIDTH 8 + +/* PHYTMAC_TXBDCTRL register */ +#define PHYTMAC_TX_TSMODE_INDEX 4 +#define PHYTMAC_TX_TSMODE_WIDTH 2 + +#define PHYTMAC_RX_TSMODE_INDEX 4 +#define PHYTMAC_RX_TSMODE_WIDTH 2 + +/* Bitfields in PHYTMAC_FDIR */ +#define PHYTMAC_QUEUE_NUM_INDEX 0 +#define PHYTMAC_QUEUE_NUM_WIDTH 4 +#define PHYTMAC_VLAN_PRI_INDEX 4 +#define PHYTMAC_VLAN_PRI_WIDTH 3 +#define PHYTMAC_VLAN_EN_INDEX 8 +#define PHYTMAC_VLAN_EN_WIDTH 1 +#define PHYTMAC_ETH_TYPE_INDEX 9 +#define PHYTMAC_ETH_TYPE_WIDTH 3 +#define PHYTMAC_ETH_EN_INDEX 12 +#define PHYTMAC_ETH_EN_WIDTH 1 +#define PHYTMAC_CA_INDEX 13 +#define PHYTMAC_CA_WIDTH 5 +#define PHYTMAC_CA_EN_INDEX 18 +#define PHYTMAC_CA_EN_WIDTH 1 +#define PHYTMAC_CB_INDEX 19 +#define PHYTMAC_CB_WIDTH 5 +#define PHYTMAC_CB_EN_INDEX 24 +#define PHYTMAC_CB_EN_WIDTH 1 +#define PHYTMAC_CC_INDEX 25 +#define PHYTMAC_CC_WIDTH 5 +#define PHYTMAC_CC_EN_INDEX 30 +#define PHYTMAC_CC_EN_WIDTH 1 + +/* Bitfields in PHYTMAC_ETHERTYPE */ +#define PHYTMAC_ETHTYPE_VALUE_INDEX 0 +#define PHYTMAC_ETHTYPE_VALUE_WIDTH 16 + +/* Bitfields in PHYTMAC_COMP1 */ +#define PHYTMAC_SPORT_INDEX 0 +#define PHYTMAC_SPORT_WIDTH 16 +#define PHYTMAC_DPORT_INDEX 16 +#define PHYTMAC_DPORTE_WIDTH 16 + +/* Bitfields in PHYTMAC_COMP2 */ +#define PHYTMAC_OFFSET_INDEX 0 +#define PHYTMAC_OFFSET_WIDTH 7 +#define ETHTYPE_SIP_OFFSET 12 +#define ETHTYPE_DIP_OFFSET 16 +#define IPHEAD_SPORT_OFFSET 0 +#define IPHEAD_DPORT_OFFSET 2 +#define PHYTMAC_OFFSET_TYPE_INDEX 7 +#define PHYTMAC_OFFSET_TYPE_WIDTH 2 +#define PHYTMAC_OFFSET_BEGIN 0 +#define PHYTMAC_OFFSET_AFTER_L2HEAD 1 +#define PHYTMAC_OFFSET_AFTER_L3HEAD 2 +#define PHYTMAC_OFFSET_AFTER_L4HEAD 3 +#define PHYTMAC_DIS_MASK_INDEX 9 +#define PHYTMAC_DIS_MASK_WIDTH 1 +#define PHYTMAC_VLAN_ID_INDEX 10 +#define PHYTMAC_VLAN_ID_WIDTH 1 + +#define PHYTMAC_TSEC_WIDTH (PHYTMAC_SECH_WIDTH + PHYTMAC_SECL_WIDTH) +#define SEC_MAX_VAL (((u64)1 << PHYTMAC_TSEC_WIDTH) - 1) +#define NSEC_MAX_VAL ((1 << PHYTMAC_NSEC_WIDTH) - 1) + +/* rx dma desc bit */ +/* DMA descriptor bitfields */ +#define PHYTMAC_RX_USED_INDEX 0 +#define PHYTMAC_RX_USED_WIDTH 1 +#define PHYTMAC_RX_WRAP_INDEX 1 +#define PHYTMAC_RX_WRAP_WIDTH 1 +#define PHYTMAC_RX_TS_VALID_INDEX 2 +#define PHYTMAC_RX_TS_VALID_WIDTH 1 +#define PHYTMAC_RX_WADDR_INDEX 2 +#define PHYTMAC_RX_WADDR_WIDTH 30 + +#define PHYTMAC_RX_FRMLEN_INDEX 0 +#define PHYTMAC_RX_FRMLEN_WIDTH 12 +#define PHYTMAC_RX_INDEX_INDEX 12 +#define PHYTMAC_RX_INDEX_WIDTH 2 +#define PHYTMAC_RX_SOF_INDEX 14 +#define PHYTMAC_RX_SOF_WIDTH 1 +#define PHYTMAC_RX_EOF_INDEX 15 +#define PHYTMAC_RX_EOF_WIDTH 1 +#define PHYTMAC_RX_CFI_INDEX 16 +#define PHYTMAC_RX_CFI_WIDTH 1 +#define PHYTMAC_RX_VLAN_PRI_INDEX 17 +#define PHYTMAC_RX_VLAN_PRI_WIDTH 3 +#define PHYTMAC_RX_PRI_TAG_INDEX 20 +#define PHYTMAC_RX_PRI_TAG_WIDTH 1 +#define PHYTMAC_RX_VLAN_TAG_INDEX 21 +#define PHYTMAC_RX_VLAN_TAG_WIDTH 1 +#define PHYTMAC_RX_UHASH_MATCH_INDEX 29 +#define PHYTMAC_RX_UHASH_MATCH_WIDTH 1 +#define PHYTMAC_RX_MHASH_MATCH_INDEX 30 +#define PHYTMAC_RX_MHASH_MATCH_WIDTH 1 +#define PHYTMAC_RX_BROADCAST_INDEX 31 +#define PHYTMAC_RX_BROADCAST_WIDTH 1 + +#define PHYTMAC_RX_FRMLEN_MASK 0x1FFF +#define PHYTMAC_RX_JFRMLEN_MASK 0x3FFF + +/* RX checksum offload disabled: bit 24 clear in NCFGR */ +#define PHYTMAC_RX_TYPEID_MATCH_INDEX 22 +#define PHYTMAC_RX_TYPEID_MATCH_WIDTH 2 + +/* RX checksum offload enabled: bit 24 set in NCFGR */ +#define PHYTMAC_RX_CSUM_INDEX 22 +#define PHYTMAC_RX_CSUM_WIDTH 2 + +/* tx dma desc bit */ +#define PHYTMAC_TX_FRMLEN_INDEX 0 +#define PHYTMAC_TX_FRMLEN_WIDTH 14 +#define PHYTMAC_TX_LAST_INDEX 15 +#define PHYTMAC_TX_LAST_WIDTH 1 +#define PHYTMAC_TX_NOCRC_INDEX 16 +#define PHYTMAC_TX_NOCRC_WIDTH 1 +#define PHYTMAC_MSS_MFS_INDEX 16 +#define PHYTMAC_MSS_MFS_WIDTH 14 +#define PHYTMAC_TX_LSO_INDEX 17 +#define PHYTMAC_TX_LSO_WIDTH 2 +#define PHYTMAC_TX_TCP_SEQ_SRC_INDEX 19 +#define PHYTMAC_TX_TCP_SEQ_SRC_WIDTH 1 +#define PHYTMAC_TX_TS_VALID_INDEX 23 +#define PHYTMAC_TX_TS_VALID_WIDTH 1 +#define PHYTMAC_TX_BUF_EXHAUSTED_INDEX 27 +#define PHYTMAC_TX_BUF_EXHAUSTED_WIDTH 1 +#define PHYTMAC_TX_UNDERRUN_INDEX 28 +#define PHYTMAC_TX_UNDERRUN_WIDTH 1 +#define PHYTMAC_TX_ERROR_INDEX 29 +#define PHYTMAC_TX_ERROR_WIDTH 1 +#define PHYTMAC_TX_WRAP_INDEX 30 +#define PHYTMAC_TX_WRAP_WIDTH 1 +#define PHYTMAC_TX_USED_INDEX 31 +#define PHYTMAC_TX_USED_WIDTH 1 + +/* Transmit DMA buffer descriptor Word 4 */ +#define PHYTMAC_DMA_NSEC_INDEX 0 +#define PHYTMAC_DMA_NSEC_WIDTH 30 +#define PHYTMAC_DMA_SECL_INDEX 30 +#define PHYTMAC_DMA_SECL_WIDTH 2 + +/* Transmit DMA buffer descriptor Word 4 */ +#define PHYTMAC_DMA_SECH_INDEX 0 +#define PHYTMAC_DMA_SECH_WIDTH 4 +#define PHYTMAC_DMA_SEC_MASK 0x3f +#define PHYTMAC_DMA_SEC_TOP 0x40 + +/* Buffer descriptor constants */ +#define PHYTMAC_RX_CSUM_NONE 0 +#define PHYTMAC_RX_CSUM_IP_ONLY 1 +#define PHYTMAC_RX_CSUM_IP_TCP 2 +#define PHYTMAC_RX_CSUM_IP_UDP 3 + +/* limit RX checksum offload to TCP and UDP packets */ +#define PHYTMAC_RX_CSUM_CHECKED_MASK 2 + +#endif diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c new file mode 100644 index 0000000000000000000000000000000000000000..943558ef9ca12b5fb1cb4adfbac34096022d9b28 --- /dev/null +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -0,0 +1,1239 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "phytmac.h" +#include "phytmac_v2.h" + +static int phytmac_msg_send(struct phytmac *pdata, u16 cmd_id, + u16 cmd_subid, void *data, int len, int wait) +{ + int index = 0; + struct phytmac_msg_info msg; + struct phytmac_msg_info msg_rx; + int ret = 0; + + ++pdata->msg_ring.tx_msg_tail; + if (pdata->msg_ring.tx_msg_tail > pdata->msg_ring.tx_msg_ring_size) + pdata->msg_ring.tx_msg_tail = 1; + index = pdata->msg_ring.tx_msg_tail; + + wait = 1; + memset(&msg, 0, sizeof(msg)); + memset(&msg_rx, 0, sizeof(msg_rx)); + msg.module_id = PHYTMAC_MODULE_ID_GMAC; + msg.cmd_id = cmd_id; + msg.cmd_subid = cmd_subid; + msg.flags = PHYTMAC_FLAGS_MSG_NOINT; + + if (len) + memcpy(&msg.para[0], data, len); + + if (netif_msg_hw(pdata)) { + netdev_info(pdata->ndev, "tx msg: cmdid=%d, subid=%d, flags=%d, len=%d, tail=%d\n", + msg.cmd_id, msg.cmd_subid, msg.flags, len, pdata->msg_ring.tx_msg_tail); + } + + memcpy(pdata->msg_regs + PHYTMAC_MSG(index), &msg, sizeof(msg)); + PHYTMAC_WRITE(pdata, PHYTMAC_TX_MSG_TAIL, + pdata->msg_ring.tx_msg_tail | PHYTMAC_BIT(TX_MSG_INT)); + + if (wait) { + memcpy(&msg_rx, pdata->msg_regs + PHYTMAC_MSG(index), MSG_HDR_LEN); + while (!(msg_rx.flags & 0x1)) { + cpu_relax(); + memcpy(&msg_rx, pdata->msg_regs + PHYTMAC_MSG(index), MSG_HDR_LEN); + } + } + + return ret; +} + +void phytmac_reset_hw(struct phytmac *pdata) +{ + int q; + u16 cmd_id, cmd_subid; + struct phytmac_ring_info ring; + + /* Disable and clear all interrupts and disable queues */ + for (q = 0; q < pdata->queues_max_num; ++q) { + PHYTMAC_WRITE(pdata, PHYTMAC_INT_DR(q), -1); + PHYTMAC_WRITE(pdata, PHYTMAC_INT_SR(q), -1); + PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_PTR(q), 0); + } + + /* reset hw rx/tx enable */ + cmd_id = PHYTMAC_MSG_CMD_DEFAULT; + cmd_subid = PHYTMAC_MSG_CMD_DEFAULT_RESET_HW; + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + + /* reset tx ring */ + memset(&ring, 0, sizeof(ring)); + ring.queue_num = pdata->queues_max_num; + cmd_subid = PHYTMAC_MSG_CMD_DEFAULT_RESET_TX_QUEUE; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&ring), sizeof(ring), 0); + + /* reset rx ring */ + cmd_subid = PHYTMAC_MSG_CMD_DEFAULT_RESET_RX_QUEUE; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&ring), sizeof(ring), 1); +} + +static int phytmac_get_mac_addr(struct phytmac *pdata, u8 *addr) +{ + int index; + u16 cmd_id, cmd_subid; + struct phytmac_mac para; + + cmd_id = PHYTMAC_MSG_CMD_GET; + cmd_subid = PHYTMAC_MSG_CMD_GET_ADDR; + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + + index = pdata->msg_ring.tx_msg_tail; + if (index <= 0) + index += pdata->msg_ring.tx_msg_ring_size; + memcpy(¶, pdata->msg_regs + PHYTMAC_MSG(index) + MSG_HDR_LEN, 8); + + addr[0] = para.addrl & 0xff; + addr[1] = (para.addrl >> 8) & 0xff; + addr[2] = (para.addrl >> 16) & 0xff; + addr[3] = (para.addrl >> 24) & 0xff; + addr[4] = para.addrh & 0xff; + addr[5] = (para.addrh >> 8) & 0xff; + + return 0; +} + +int phytmac_set_mac_addr(struct phytmac *pdata, const u8 *addr) +{ + u16 cmd_id; + u16 cmd_subid; + struct phytmac_mac para; + + memset(¶, 0, sizeof(para)); + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_ADDR; + para.addrl = cpu_to_le32(*((u32 *)addr)); + para.addrh = cpu_to_le16(*((u16 *)(addr + 4))); + + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); + + return 0; +} + +static int phytmac_init_hw(struct phytmac *pdata) +{ + u16 cmd_id, cmd_subid; + struct phytmac_dma_info dma; + struct phytmac_eth_info eth; + + phytmac_set_mac_addr(pdata, pdata->ndev->dev_addr); + + cmd_id = PHYTMAC_MSG_CMD_SET; + if (pdata->capacities & PHYTMAC_CAPS_JUMBO) + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_JUMBO; + else + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_1536_FRAMES; + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + + if (pdata->ndev->flags & IFF_PROMISC) { + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_PROMISE; + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + } + + if (pdata->ndev->features & NETIF_F_RXCSUM) { + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_RXCSUM; + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + } + + if (!(pdata->ndev->flags & IFF_BROADCAST)) { + cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_BC; + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + } + + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_PAUSE; + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_STRIPCRC; + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + + memset(&dma, 0, sizeof(dma)); + cmd_subid = PHYTMAC_MSG_CMD_SET_DMA; + dma.dma_burst_length = pdata->dma_burst_length; + if (pdata->dma_addr_width) + dma.hw_dma_cap |= HW_DMA_CAP_64B; + if (pdata->ndev->features & NETIF_F_HW_CSUM) + dma.hw_dma_cap |= HW_DMA_CAP_CSUM; + if (IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) + dma.hw_dma_cap |= HW_DMA_CAP_PTP; + if (pdata->dma_data_width == PHYTMAC_DBW64) + dma.hw_dma_cap |= HW_DMA_CAP_DDW64; + if (pdata->dma_data_width == PHYTMAC_DBW128) + dma.hw_dma_cap |= HW_DMA_CAP_DDW128; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)&dma, sizeof(dma), 0); + + memset(ð, 0, sizeof(eth)); + cmd_subid = PHYTMAC_MSG_CMD_SET_ETH_MATCH; + eth.index = 0; + eth.etype = (uint16_t)ETH_P_IP; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)ð, sizeof(eth), 1); + + return 0; +} + +static int phytmac_enable_multicast(struct phytmac *pdata, int enable) +{ + u16 cmd_id, cmd_subid; + + cmd_id = PHYTMAC_MSG_CMD_SET; + if (enable) + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_MC; + else + cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_MC; + + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + return 0; +} + +static int phytmac_set_mc_hash(struct phytmac *pdata, unsigned long *mc_filter) +{ + u16 cmd_id, cmd_subid; + struct phytmac_mc_info para; + + memset(¶, 0, sizeof(para)); + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_HASH_MC; + para.mc_bottom = (u32)mc_filter[0]; + para.mc_top = (u32)mc_filter[1]; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); + + return 0; +} + +static int phytmac_init_ring_hw(struct phytmac *pdata) +{ + u16 cmd_id, cmd_subid; + struct phytmac_ring_info rxring; + struct phytmac_ring_info txring; + struct phytmac_rxbuf_info rxbuf; + struct phytmac_queue *queue; + u32 q; + + memset(&rxring, 0, sizeof(rxring)); + memset(&txring, 0, sizeof(txring)); + memset(&rxbuf, 0, sizeof(rxbuf)); + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_INIT_TX_RING; + txring.queue_num = pdata->queues_num; + rxring.queue_num = pdata->queues_num; + txring.hw_dma_cap |= HW_DMA_CAP_64B; + rxring.hw_dma_cap |= HW_DMA_CAP_64B; + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { + PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_PTR(q), queue->tx_head); + txring.addr[q] = queue->tx_ring_addr; + rxring.addr[q] = queue->rx_ring_addr; + } + + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&txring), sizeof(txring), 0); + + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_DMA_RX_BUFSIZE; + rxbuf.queue_num = pdata->queues_num; + rxbuf.buffer_size = pdata->rx_buffer_len / 64; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&rxbuf), sizeof(rxbuf), 0); + + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_INIT_RX_RING; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&rxring), sizeof(rxring), 0); + + return 0; +} + +int phytmac_init_msg_ring(struct phytmac *pdata) +{ + u32 size = 0; + + pdata->msg_ring.tx_msg_tail = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_TAIL) & 0xff; + size = PHYTMAC_READ_BITS(pdata, PHYTMAC_SIZE, TXRING_SIZE); + pdata->msg_ring.tx_msg_ring_size = size; + if (pdata->msg_ring.tx_msg_tail == size) + pdata->msg_ring.tx_msg_tail = 0; + + PHYTMAC_WRITE(pdata, PHYTMAC_MSG_IMR, 0xfffffffe); + if (netif_msg_hw(pdata)) + netdev_info(pdata->ndev, "mac msg ring: tx_msg_ring_size=%d, tx_msg_tail=%d\n", + size, pdata->msg_ring.tx_msg_tail); + + return 0; +} + +static int phytmac_get_feature_all(struct phytmac *pdata) +{ + u16 cmd_id, cmd_subid; + int index; + struct phytmac_feature para; + + memset(¶, 0, sizeof(para)); + cmd_id = PHYTMAC_MSG_CMD_GET; + cmd_subid = PHYTMAC_MSG_CMD_GET_CAPS; + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + + index = pdata->msg_ring.tx_msg_tail; + if (index <= 0) + index += pdata->msg_ring.tx_msg_ring_size; + + memcpy(¶, pdata->msg_regs + PHYTMAC_MSG(index) + MSG_HDR_LEN, + sizeof(struct phytmac_feature)); + + pdata->queues_max_num = para.queue_num; + if (para.dma_addr_width) + pdata->dma_addr_width = 64; + else + pdata->dma_addr_width = 32; + pdata->dma_data_width = para.dma_data_width; + pdata->max_rx_fs = para.max_rx_fs; + pdata->tx_bd_prefetch = (2 << (para.tx_bd_prefetch - 1)) * + sizeof(struct phytmac_dma_desc); + pdata->rx_bd_prefetch = (2 << (para.rx_bd_prefetch - 1)) * + sizeof(struct phytmac_dma_desc); + + if (netif_msg_hw(pdata)) { + netdev_info(pdata->ndev, "feature qnum=%d, daw=%d, dbw=%d, rxfs=%d, rxbd=%d, txbd=%d\n", + pdata->queues_num, pdata->dma_addr_width, pdata->dma_data_width, + pdata->max_rx_fs, pdata->rx_bd_prefetch, pdata->tx_bd_prefetch); + } + + return 0; +} + +void phytmac_get_regs(struct phytmac *pdata, u32 *reg_buff) +{ + u16 cmd_id, cmd_subid; + u16 reg_num; + int index; + + cmd_id = PHYTMAC_MSG_CMD_GET; + cmd_subid = PHYTMAC_MSG_CMD_GET_REG_READ; + reg_num = 16; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)®_num, 2, 1); + + index = pdata->msg_ring.tx_msg_tail; + if (index <= 0) + index += pdata->msg_ring.tx_msg_ring_size; + + memcpy(®_buff, pdata->msg_regs + PHYTMAC_MSG(index) + MSG_HDR_LEN, 64); +} + +static void phytmac_get_hw_stats(struct phytmac *pdata) +{ + u16 cmd_id, cmd_subid; + u8 count; + int i, j, index; + u32 stats[48]; + u64 val; + u64 *p = &pdata->stats.tx_octets; + + cmd_id = PHYTMAC_MSG_CMD_GET; + cmd_subid = PHYTMAC_MSG_CMD_GET_STATS; + count = 1; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)&count, sizeof(count), 0); + + cmd_id = PHYTMAC_MSG_CMD_GET; + cmd_subid = PHYTMAC_MSG_CMD_GET_STATS; + count = 2; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)&count, sizeof(count), 0); + + cmd_id = PHYTMAC_MSG_CMD_GET; + cmd_subid = PHYTMAC_MSG_CMD_GET_STATS; + count = 3; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)&count, sizeof(count), 1); + + for (i = 0; i < 3; i++) { + index = pdata->msg_ring.tx_msg_tail + i - 2; + if (index <= 0) + index += pdata->msg_ring.tx_msg_ring_size; + memcpy(&stats[i * 16], pdata->msg_regs + PHYTMAC_MSG(index) + MSG_HDR_LEN, 64); + } + + for (i = 0, j = 0; i < 44; i++) { + if (i == 0 || i == 20) { + val = (u64)stats[i + 1] << 32 | stats[i]; + *p += val; + pdata->ethtool_stats[j] = *p; + ++j; + ++p; + } else { + if (i != 1 && i != 21) { + val = stats[i]; + *p += val; + pdata->ethtool_stats[j] = *p; + ++j; + ++p; + } + } + } +} + +static void phytmac_mdio_idle(struct phytmac *pdata) +{ + u32 val; + + /* wait for end of transfer */ + val = PHYTMAC_READ(pdata, PHYTMAC_NETWORK_STATUS); + while (!(val & PHYTMAC_BIT(MIDLE))) { + cpu_relax(); + val = PHYTMAC_READ(pdata, PHYTMAC_NETWORK_STATUS); + } +} + +static int phytmac_mdio_data_read(struct phytmac *pdata, int mii_id, int regnum, int is_c45) +{ + u16 data; + + if (is_c45) { + PHYTMAC_WRITE(pdata, PHYTMAC_MDIO, (PHYTMAC_BITS(CLAUSESEL, PHYTMAC_C45) + | PHYTMAC_BITS(MDCOPS, PHYTMAC_C45_ADDR) + | PHYTMAC_BITS(PHYADDR, mii_id) + | PHYTMAC_BITS(REGADDR, (regnum >> 16) & 0x1F) + | PHYTMAC_BITS(VALUE, regnum & 0xFFFF) + | PHYTMAC_BITS(CONST, 2))); + phytmac_mdio_idle(pdata); + PHYTMAC_WRITE(pdata, PHYTMAC_MDIO, (PHYTMAC_BITS(CLAUSESEL, PHYTMAC_C45) + | PHYTMAC_BITS(MDCOPS, PHYTMAC_C45_READ) + | PHYTMAC_BITS(PHYADDR, mii_id) + | PHYTMAC_BITS(REGADDR, (regnum >> 16) & 0x1F) + | PHYTMAC_BITS(VALUE, regnum & 0xFFFF) + | PHYTMAC_BITS(CONST, 2))); + } else { + PHYTMAC_WRITE(pdata, PHYTMAC_MDIO, (PHYTMAC_BITS(CLAUSESEL, PHYTMAC_C22) + | PHYTMAC_BITS(MDCOPS, PHYTMAC_C22_READ) + | PHYTMAC_BITS(PHYADDR, mii_id) + | PHYTMAC_BITS(REGADDR, regnum) + | PHYTMAC_BITS(CONST, 2))); + } + phytmac_mdio_idle(pdata); + data = PHYTMAC_READ(pdata, PHYTMAC_MDIO) & 0xffff; + phytmac_mdio_idle(pdata); + return data; +} + +static int phytmac_mdio_data_write(struct phytmac *pdata, int mii_id, + int regnum, int is_c45, u16 data) +{ + if (is_c45) { + PHYTMAC_WRITE(pdata, PHYTMAC_MDIO, (PHYTMAC_BITS(CLAUSESEL, PHYTMAC_C45) + | PHYTMAC_BITS(MDCOPS, PHYTMAC_C45_ADDR) + | PHYTMAC_BITS(PHYADDR, mii_id) + | PHYTMAC_BITS(REGADDR, (regnum >> 16) & 0x1F) + | PHYTMAC_BITS(VALUE, regnum & 0xFFFF) + | PHYTMAC_BITS(CONST, 2))); + phytmac_mdio_idle(pdata); + PHYTMAC_WRITE(pdata, PHYTMAC_MDIO, (PHYTMAC_BITS(CLAUSESEL, PHYTMAC_C45) + | PHYTMAC_BITS(MDCOPS, PHYTMAC_C45_WRITE) + | PHYTMAC_BITS(PHYADDR, mii_id) + | PHYTMAC_BITS(REGADDR, (regnum >> 16) & 0x1F) + | PHYTMAC_BITS(VALUE, data) + | PHYTMAC_BITS(CONST, 2))); + } else { + PHYTMAC_WRITE(pdata, PHYTMAC_MDIO, (PHYTMAC_BITS(CLAUSESEL, PHYTMAC_C22) + | PHYTMAC_BITS(MDCOPS, PHYTMAC_C22_WRITE) + | PHYTMAC_BITS(PHYADDR, mii_id) + | PHYTMAC_BITS(REGADDR, regnum) + | PHYTMAC_BITS(VALUE, data) + | PHYTMAC_BITS(CONST, 2))); + } + phytmac_mdio_idle(pdata); + + return 0; +} + +static int phytmac_powerup_hw(struct phytmac *pdata, int on) +{ + u32 status, data0, data1, rdata1; + int ret; + + if (pdata->capacities & PHYTMAC_CAPS_LPI) { + ret = readx_poll_timeout(PHYTMAC_READ_STAT, pdata, status, !status, + 1, PHYTMAC_TIMEOUT); + if (ret) + netdev_err(pdata->ndev, "mnh status is busy"); + + ret = readx_poll_timeout(PHYTMAC_READ_DATA0, pdata, data0, + data0 & PHYTMAC_BIT(DATA0_FREE), + 1, PHYTMAC_TIMEOUT); + if (ret) + netdev_err(pdata->ndev, "mnh data0 is busy"); + + data0 = 0; + data0 = PHYTMAC_SET_BITS(data0, DATA0_MSG, PHYTMAC_MSG_PM); + data0 = PHYTMAC_SET_BITS(data0, DATA0_PRO, PHYTMAC_PRO_ID); + PHYTMAC_MHU_WRITE(pdata, PHYTMAC_MHU_CPP_DATA0, data0); + data1 = 0; + + if (on == PHYTMAC_POWERON) { + data1 = PHYTMAC_SET_BITS(data1, DATA1_STAT, PHYTMAC_STATON); + data1 = PHYTMAC_SET_BITS(data1, DATA1_STATTYPE, PHYTMAC_STATTYPE); + PHYTMAC_MHU_WRITE(pdata, PHYTMAC_MHU_CPP_DATA1, data1); + } else { + data1 = PHYTMAC_SET_BITS(data1, DATA1_STAT, PHYTMAC_STATOFF); + data1 = PHYTMAC_SET_BITS(data1, DATA1_STATTYPE, PHYTMAC_STATTYPE); + PHYTMAC_MHU_WRITE(pdata, PHYTMAC_MHU_CPP_DATA1, data1); + } + + PHYTMAC_MHU_WRITE(pdata, PHYTMAC_MHU_AP_CPP_SET, 1); + ret = readx_poll_timeout(PHYTMAC_READ_DATA0, pdata, data0, + data0 & PHYTMAC_BIT(DATA0_FREE), + 1, PHYTMAC_TIMEOUT); + if (ret) + netdev_err(pdata->ndev, "mnh data0 is busy\n"); + + rdata1 = PHYTMAC_MHU_READ(pdata, PHYTMAC_MHU_CPP_DATA1); + if (rdata1 == data1) + netdev_err(pdata->ndev, "gmac power %s success, data1 = %x, rdata1=%x\n", + on ? "up" : "down", data1, rdata1); + else + netdev_err(pdata->ndev, "gmac power %s failed, data1 = %x, rdata1=%x\n", + on ? "up" : "down", data1, rdata1); + } + + pdata->power_state = on; + + return 0; +} + +static int phytmac_set_wake(struct phytmac *pdata, int wake) +{ + u16 cmd_id, cmd_subid; + u8 wol = (u8)wake; + + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_WOL; + + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&wol), 1, 1); + + return 0; +} + +static int phytmac_enable_promise(struct phytmac *pdata, int enable) +{ + u16 cmd_id, cmd_subid; + u8 rxcsum = 0; + + cmd_id = PHYTMAC_MSG_CMD_SET; + if (enable) { + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_PROMISE; + } else { + cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_PROMISE; + if (pdata->ndev->features & NETIF_F_RXCSUM) + rxcsum = 1; + } + + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&rxcsum), 1, 1); + + return 0; +} + +static int phytmac_enable_rxcsum(struct phytmac *pdata, int enable) +{ + u16 cmd_id, cmd_subid; + + cmd_id = PHYTMAC_MSG_CMD_SET; + if (enable) + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_RXCSUM; + else + cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_RXCSUM; + + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + + return 0; +} + +static int phytmac_enable_txcsum(struct phytmac *pdata, int enable) +{ + u16 cmd_id, cmd_subid; + + cmd_id = PHYTMAC_MSG_CMD_SET; + if (enable) + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_TXCSUM; + else + cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_TXCSUM; + + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + + return 0; +} + +static int phytmac_enable_mdio(struct phytmac *pdata, int enable) +{ + u16 cmd_id, cmd_subid; + + cmd_id = PHYTMAC_MSG_CMD_SET; + if (enable) + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_MDIO; + else + cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_MDIO; + + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + + return 0; +} + +static int phytmac_enable_autoneg(struct phytmac *pdata, int enable) +{ + u16 cmd_id, cmd_subid; + + cmd_id = PHYTMAC_MSG_CMD_SET; + if (enable) + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_AUTONEG; + else + cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_AUTONEG; + + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + + pdata->autoneg = enable; + return 0; +} + +static int phytmac_enable_pause(struct phytmac *pdata, int enable) +{ + u16 cmd_id, cmd_subid; + + cmd_id = PHYTMAC_MSG_CMD_SET; + if (enable) + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_PAUSE; + else + cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_PAUSE; + + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + + return 0; +} + +static int phytmac_enable_network(struct phytmac *pdata, int enable, int rx_tx) +{ + u16 cmd_id, cmd_subid; + + cmd_id = PHYTMAC_MSG_CMD_SET; + if (enable) + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_NETWORK; + else + cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_NETWORK; + + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + + return 0; +} + +static int phytmac_add_fdir_entry(struct phytmac *pdata, struct ethtool_rx_flow_spec *rx_flow) +{ + struct ethtool_tcpip4_spec *tp4sp_v, *tp4sp_m; + struct phytmac_fdir_info fdir; + u16 cmd_id, cmd_subid; + + memset(&fdir, 0, sizeof(fdir)); + + tp4sp_v = &rx_flow->h_u.tcp_ip4_spec; + tp4sp_m = &rx_flow->m_u.tcp_ip4_spec; + if (tp4sp_m->ip4src == 0xFFFFFFFF) { + fdir.ipsrc_en = true; + fdir.ip4src = tp4sp_v->ip4src; + } + + if (tp4sp_m->ip4dst == 0xFFFFFFFF) { + fdir.ipdst_en = true; + fdir.ip4dst = tp4sp_v->ip4dst; + } + + if (tp4sp_m->psrc == 0xFFFF || tp4sp_m->pdst == 0xFFFF) { + fdir.port_en = true; + fdir.dstport = tp4sp_v->pdst; + fdir.srcport = tp4sp_v->psrc; + fdir.dstport_mask = tp4sp_m->pdst; + fdir.srcport_mask = tp4sp_m->psrc; + } + + fdir.location = rx_flow->location; + fdir.queue = rx_flow->ring_cookie; + + if (fdir.ipsrc_en || fdir.ipdst_en || fdir.port_en) { + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_ADD_FDIR; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&fdir), sizeof(fdir), 1); + } + + return 0; +} + +static int phytmac_del_fdir_entry(struct phytmac *pdata, struct ethtool_rx_flow_spec *rx_flow) +{ + struct phytmac_fdir_info fdir; + u16 cmd_id, cmd_subid; + + memset(&fdir, 0, sizeof(fdir)); + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_DEL_FDIR; + fdir.location = (u8)rx_flow->location; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&fdir), sizeof(fdir), 1); + + return 0; +} + +static void phytmac_tx_start(struct phytmac_queue *queue) +{ + struct phytmac *pdata = queue->pdata; + + PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_PTR(queue->index), queue->tx_tail); + queue->tx_xmit_more = 0; +} + +static u32 phytmac_get_irq_mask(u32 mask) +{ + u32 value = 0; + + value |= (mask & PHYTMAC_INT_TX_COMPLETE) ? PHYTMAC_BIT(TXCOMP) : 0; + value |= (mask & PHYTMAC_INT_TX_ERR) ? PHYTMAC_BIT(DMA_ERR) : 0; + value |= (mask & PHYTMAC_INT_RX_COMPLETE) ? PHYTMAC_BIT(RXCOMP) : 0; + value |= (mask & PHYTMAC_INT_RX_OVERRUN) ? PHYTMAC_BIT(RXOVERRUN) : 0; + value |= (mask & PHYTMAC_INT_RX_DESC_FULL) ? PHYTMAC_BIT(RUSED) : 0; + + return value; +} + +static u32 phytmac_get_irq_status(u32 value) +{ + u32 status = 0; + + status |= (value & PHYTMAC_BIT(TXCOMP)) ? PHYTMAC_INT_TX_COMPLETE : 0; + status |= (value & PHYTMAC_BIT(DMA_ERR)) ? PHYTMAC_INT_TX_ERR : 0; + status |= (value & PHYTMAC_BIT(RXCOMP)) ? PHYTMAC_INT_RX_COMPLETE : 0; + status |= (value & PHYTMAC_BIT(RXOVERRUN)) ? PHYTMAC_INT_RX_OVERRUN : 0; + status |= (value & PHYTMAC_BIT(RUSED)) ? PHYTMAC_INT_RX_DESC_FULL : 0; + + return status; +} + +static void phytmac_enable_irq(struct phytmac *pdata, + int queue_index, u32 mask) +{ + u32 value; + + value = phytmac_get_irq_mask(mask); + PHYTMAC_WRITE(pdata, PHYTMAC_INT_ER(queue_index), value); +} + +static void phytmac_disable_irq(struct phytmac *pdata, + int queue_index, u32 mask) +{ + u32 value; + + value = phytmac_get_irq_mask(mask); + PHYTMAC_WRITE(pdata, PHYTMAC_INT_DR(queue_index), value); +} + +static void phytmac_clear_irq(struct phytmac *pdata, + int queue_index, u32 mask) +{ + u32 value; + + value = phytmac_get_irq_mask(mask); + PHYTMAC_WRITE(pdata, PHYTMAC_INT_SR(queue_index), value); +} + +static unsigned int phytmac_get_irq(struct phytmac *pdata, int queue_index) +{ + u32 status; + u32 value; + + value = PHYTMAC_READ(pdata, PHYTMAC_INT_SR(queue_index)); + status = phytmac_get_irq_status(value); + + return status; +} + +static void phytmac_interface_config(struct phytmac *pdata, unsigned int mode, + const struct phylink_link_state *state) +{ + struct phytmac_interface_info para; + u16 cmd_id, cmd_subid; + + memset(¶, 0, sizeof(para)); + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_MAC_CONFIG; + para.interface = state->interface; + para.autoneg = (mode == MLO_AN_FIXED ? 0 : 1); + para.speed = state->speed; + para.duplex = state->duplex; + pdata->autoneg = para.autoneg; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); +} + +static int phytmac_interface_linkup(struct phytmac *pdata, phy_interface_t interface, + int speed, int duplex) +{ + struct phytmac_interface_info para; + u16 cmd_id, cmd_subid; + + memset(¶, 0, sizeof(para)); + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_MAC_LINK_CONFIG; + para.interface = interface; + para.duplex = duplex; + para.speed = speed; + para.autoneg = pdata->autoneg; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); + + return 0; +} + +static int phytmac_interface_linkdown(struct phytmac *pdata) +{ + return 0; +} + +static int phytmac_pcs_linkup(struct phytmac *pdata, phy_interface_t interface, + int speed, int duplex) +{ + struct phytmac_interface_info para; + u16 cmd_id, cmd_subid; + + if (interface == PHY_INTERFACE_MODE_USXGMII || + interface == PHY_INTERFACE_MODE_10GBASER) { + memset(¶, 0, sizeof(para)); + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_PCS_LINK_UP; + para.interface = interface; + para.duplex = duplex; + para.speed = speed; + para.autoneg = 0; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); + } + + return 0; +} + +static int phytmac_pcs_linkdown(struct phytmac *pdata) +{ + return 0; +} + +static unsigned int phytmac_pcs_get_link(struct phytmac *pdata, phy_interface_t interface) +{ + if (interface == PHY_INTERFACE_MODE_SGMII || + interface == PHY_INTERFACE_MODE_2500BASEX) + return PHYTMAC_READ_BITS(pdata, PHYTMAC_NETWORK_STATUS, LINK); + else if (interface == PHY_INTERFACE_MODE_USXGMII || + interface == PHY_INTERFACE_MODE_10GBASER) + return PHYTMAC_READ_BITS(pdata, PHYTMAC_USX_LINK_STATUS, USX_LINK); + + return 0; +} + +static unsigned int phytmac_tx_map_desc(struct phytmac_queue *queue, + u32 tx_tail, struct packet_info *packet) +{ + unsigned int i, ctrl; + struct phytmac *pdata = queue->pdata; + struct phytmac_dma_desc *desc; + struct phytmac_tx_skb *tx_skb; + unsigned int eof = 1; + + i = tx_tail; + + do { + i--; + tx_skb = phytmac_get_tx_skb(queue, i); + desc = phytmac_get_tx_desc(queue, i); + + ctrl = (u32)tx_skb->length; + if (eof) { + ctrl |= PHYTMAC_BIT(TXLAST); + eof = 0; + } + + if (unlikely(i == (pdata->tx_ring_size - 1))) + ctrl |= PHYTMAC_BIT(TXWRAP); + + if (i == queue->tx_tail) { + ctrl |= PHYTMAC_BITS(TXLSO, packet->lso); + ctrl |= PHYTMAC_BITS(TXTCP_SEQ_SRC, packet->seq); + if (packet->nocrc) + ctrl |= PHYTMAC_BIT(TXNOCRC); + } else { + ctrl |= PHYTMAC_BITS(MSSMFS, packet->mss); + } + + desc->desc2 = upper_32_bits(tx_skb->addr); + desc->desc0 = lower_32_bits(tx_skb->addr); + /* make newly desc1 to hardward */ + wmb(); + desc->desc1 = ctrl; + } while (i != queue->tx_tail); + + return 0; +} + +static void phytmac_init_rx_map_desc(struct phytmac_queue *queue, + u32 index) +{ + struct phytmac_dma_desc *desc; + + desc = phytmac_get_rx_desc(queue, index); + + desc->desc1 = 0; + /* Make newly descriptor to hardware */ + dma_wmb(); + desc->desc0 |= PHYTMAC_BIT(RXUSED); +} + +static unsigned int phytmac_rx_map_desc(struct phytmac_queue *queue, u32 index, dma_addr_t addr) +{ + struct phytmac *pdata = queue->pdata; + struct phytmac_dma_desc *desc; + + desc = phytmac_get_rx_desc(queue, index); + + if (addr) { + if (unlikely(index == (pdata->rx_ring_size - 1))) + addr |= PHYTMAC_BIT(RXWRAP); + desc->desc1 = 0; + desc->desc2 = upper_32_bits(addr); + /* Make newly descriptor to hardware */ + dma_wmb(); + desc->desc0 = lower_32_bits(addr); + } else { + desc->desc1 = 0; + /* make newly descriptor to hardware */ + dma_wmb(); + desc->desc0 &= ~PHYTMAC_BIT(RXUSED); + } + + return 0; +} + +static int phytmac_tx_complete(const struct phytmac_dma_desc *desc) +{ + return PHYTMAC_GET_BITS(desc->desc1, TXUSED); +} + +static int phytmac_rx_complete(const struct phytmac_dma_desc *desc) +{ + return PHYTMAC_GET_BITS(desc->desc0, RXUSED); +} + +static int phytmac_rx_pkt_len(struct phytmac *pdata, const struct phytmac_dma_desc *desc) +{ + if (pdata->capacities & PHYTMAC_CAPS_JUMBO) + return desc->desc1 & PHYTMAC_RXJFRMLEN_MASK; + else + return desc->desc1 & PHYTMAC_RXFRMLEN_MASK; +} + +static dma_addr_t phytmac_get_desc_addr(const struct phytmac_dma_desc *desc) +{ + dma_addr_t addr = 0; + + addr = ((u64)(desc->desc2) << 32); + + addr |= (desc->desc0 & 0xfffffffc); + return addr; +} + +static bool phytmac_rx_checksum(const struct phytmac_dma_desc *desc) +{ + u32 value = desc->desc1; + u32 check = value >> PHYTMAC_RXCSUM_INDEX & 0x3; + + return (check == PHYTMAC_RXCSUM_IP_TCP || check == PHYTMAC_RXCSUM_IP_UDP); +} + +static bool phytmac_rx_single_buffer(const struct phytmac_dma_desc *desc) +{ + u32 value = desc->desc1; + + return ((value & PHYTMAC_BIT(RXSOF)) && (value & PHYTMAC_BIT(RXEOF))); +} + +static bool phytmac_rx_sof(const struct phytmac_dma_desc *desc) +{ + u32 value = desc->desc1; + + return (value & PHYTMAC_BIT(RXSOF)); +} + +static bool phytmac_rx_eof(const struct phytmac_dma_desc *desc) +{ + u32 value = desc->desc1; + + return (value & PHYTMAC_BIT(RXEOF)); +} + +static void phytmac_clear_rx_desc(struct phytmac_queue *queue, int begin, int end) +{ + unsigned int frag; + unsigned int tmp = end; + struct phytmac_dma_desc *desc; + + if (begin > end) + tmp = end + queue->pdata->rx_ring_size; + + for (frag = begin; frag != end; frag++) { + desc = phytmac_get_rx_desc(queue, frag); + desc->desc0 &= ~PHYTMAC_BIT(RXUSED); + } +} + +static void phytmac_clear_tx_desc(struct phytmac_queue *queue) +{ + struct phytmac *pdata = queue->pdata; + struct phytmac_dma_desc *desc = NULL; + struct phytmac_tx_skb *tx_skb = NULL; + int i; + + for (i = 0; i < queue->pdata->tx_ring_size; i++) { + desc = phytmac_get_tx_desc(queue, i); + tx_skb = phytmac_get_tx_skb(queue, i); + desc->desc2 = upper_32_bits(tx_skb->addr); + desc->desc0 = lower_32_bits(tx_skb->addr); + /* make newly desc to hardware */ + wmb(); + desc->desc1 = PHYTMAC_BIT(TXUSED); + } + desc->desc1 |= PHYTMAC_BIT(TXWRAP); + PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_PTR(queue->index), queue->tx_tail); +} + +static void phytmac_get_time(struct phytmac *pdata, struct timespec64 *ts) +{ + u32 ns, secl, sech; + + ns = PHYTMAC_READ(pdata, PHYTMAC_TIMER_NSEC); + secl = PHYTMAC_READ(pdata, PHYTMAC_TIMER_SEC); + sech = PHYTMAC_READ(pdata, PHYTMAC_TIMER_MSB_SEC); + + ts->tv_nsec = ns; + ts->tv_sec = (((u64)sech << 32) | secl) & TIMER_SEC_MAX_VAL; +} + +void phytmac_set_time(struct phytmac *pdata, time64_t sec, long nsec) +{ + u32 secl, sech; + + secl = (u32)sec; + sech = (sec >> 32) & (0xffff); + + PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_NSEC, 0); + PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_MSB_SEC, sech); + PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_SEC, secl); + PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_NSEC, nsec); +} + +void phytmac_clear_time(struct phytmac *pdata) +{ + u32 value; + + pdata->ts_incr.sub_ns = 0; + pdata->ts_incr.ns = 0; + + value = PHYTMAC_READ(pdata, PHYTMAC_TIMER_INCR); + value = PHYTMAC_SET_BITS(value, INCR_NSEC, 0); + PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_INCR, value); + + value = PHYTMAC_READ(pdata, PHYTMAC_TIMER_INCR_SUB_NSEC); + value = PHYTMAC_SET_BITS(value, INCR_SNSEC, 0); + PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_INCR_SUB_NSEC, value); + + PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_ADJUST, 0); +} + +int phytmac_set_tsmode(struct phytmac *pdata, struct ts_ctrl *ctrl) +{ + u16 cmd_id, cmd_subid; + struct phytmac_ts_config para; + + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_TS_CONFIG; + para.tx_mode = ctrl->tx_control; + para.rx_mode = ctrl->rx_control; + para.one_step = ctrl->one_step; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); + + return 0; +} + +static int phytmac_set_tsincr(struct phytmac *pdata, struct ts_incr *incr) +{ + u32 value; + + value = PHYTMAC_BITS(INCR_SNSEC, incr->sub_ns); + PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_INCR_SUB_NSEC, value); + PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_INCR, incr->ns); + + return 0; +} + +static void phytmac_ptp_init_hw(struct phytmac *pdata) +{ + struct timespec64 ts; + + ts = ns_to_timespec64(ktime_to_ns(ktime_get_real())); + phytmac_set_time(pdata, ts.tv_sec, ts.tv_nsec); + + phytmac_set_tsincr(pdata, &pdata->ts_incr); +} + +static int phytmac_adjust_fine(struct phytmac *pdata, long ppm, bool negative) +{ + struct ts_incr ts_incr; + u32 tmp; + u64 adj; + + ts_incr.ns = pdata->ts_incr.ns; + ts_incr.sub_ns = pdata->ts_incr.sub_ns; + + /* scaling: unused(8bit) | ns(8bit) | fractions(16bit) */ + tmp = ((u64)ts_incr.ns << PHYTMAC_INCR_SNSECL_INDEX) + ts_incr.sub_ns; + adj = ((u64)ppm * tmp + (USEC_PER_SEC >> 1)) >> PHYTMAC_INCR_SNSECL_INDEX; + + adj = div_u64(adj, USEC_PER_SEC); + adj = negative ? (tmp - adj) : (tmp + adj); + + ts_incr.ns = (adj >> PHYTMAC_INCR_SNSEC_WIDTH) + & ((1 << PHYTMAC_INCR_NSEC_WIDTH) - 1); + ts_incr.sub_ns = adj & ((1 << PHYTMAC_INCR_SNSEC_WIDTH) - 1); + + phytmac_set_tsincr(pdata, &ts_incr); + + return 0; +} + +int phytmac_adjust_time(struct phytmac *pdata, s64 delta, int neg) +{ + u32 adj; + + if (delta > PHYTMAC_ASEC_MAX) { + struct timespec64 now, then; + + then = ns_to_timespec64(delta); + phytmac_get_time(pdata, &now); + now = timespec64_add(now, then); + phytmac_set_time(pdata, now.tv_sec, now.tv_nsec); + } else { + adj = (neg << PHYTMAC_AADD_INDEX) | delta; + PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_ADJUST, adj); + } + + return 0; +} + +static int phytmac_ts_valid(struct phytmac *pdata, struct phytmac_dma_desc *desc, int direction) +{ + int ts_valid = 0; + + if (direction == PHYTMAC_TX) + ts_valid = desc->desc1 & PHYTMAC_BIT(TXTSVALID); + else if (direction == PHYTMAC_RX) + ts_valid = desc->desc0 & PHYTMAC_BIT(RXTSVALID); + return ts_valid; +} + +static void phytmac_get_dma_ts(struct phytmac *pdata, u32 ts_1, u32 ts_2, struct timespec64 *ts) +{ + struct timespec64 ts2; + + ts->tv_sec = (PHYTMAC_GET_BITS(ts_2, TS_SECH) << 2) | + PHYTMAC_GET_BITS(ts_1, TS_SECL); + ts->tv_nsec = PHYTMAC_GET_BITS(ts_1, TS_NSEC); + + phytmac_get_time(pdata, &ts2); + + if (((ts->tv_sec ^ ts2.tv_sec) & (PHYTMAC_TS_SEC_TOP >> 1)) != 0) + ts->tv_sec -= PHYTMAC_TS_SEC_TOP; + + ts->tv_sec += (ts2.tv_sec & (~PHYTMAC_TS_SEC_MASK)); +} + +static unsigned int phytmac_get_ts_rate(struct phytmac *pdata) +{ + return 300000000; +} + +struct phytmac_hw_if phytmac_2p0_hw = { + .init_msg_ring = phytmac_init_msg_ring, + .reset_hw = phytmac_reset_hw, + .init_hw = phytmac_init_hw, + .init_ring_hw = phytmac_init_ring_hw, + .get_feature = phytmac_get_feature_all, + .get_regs = phytmac_get_regs, + .get_stats = phytmac_get_hw_stats, + .set_mac_address = phytmac_set_mac_addr, + .get_mac_address = phytmac_get_mac_addr, + .mdio_read = phytmac_mdio_data_read, + .mdio_write = phytmac_mdio_data_write, + .poweron = phytmac_powerup_hw, + .set_wol = phytmac_set_wake, + .enable_promise = phytmac_enable_promise, + .enable_multicast = phytmac_enable_multicast, + .set_hash_table = phytmac_set_mc_hash, + .enable_rx_csum = phytmac_enable_rxcsum, + .enable_tx_csum = phytmac_enable_txcsum, + .enable_mdio_control = phytmac_enable_mdio, + .enable_autoneg = phytmac_enable_autoneg, + .enable_pause = phytmac_enable_pause, + .enable_network = phytmac_enable_network, + .add_fdir_entry = phytmac_add_fdir_entry, + .del_fdir_entry = phytmac_del_fdir_entry, + + /* mac config */ + .mac_config = phytmac_interface_config, + .mac_linkup = phytmac_interface_linkup, + .mac_linkdown = phytmac_interface_linkdown, + .pcs_linkup = phytmac_pcs_linkup, + .pcs_linkdown = phytmac_pcs_linkdown, + .get_link = phytmac_pcs_get_link, + + /* irq */ + .enable_irq = phytmac_enable_irq, + .disable_irq = phytmac_disable_irq, + .clear_irq = phytmac_clear_irq, + .get_irq = phytmac_get_irq, + + /* tx and rx */ + .tx_map = phytmac_tx_map_desc, + .transmit = phytmac_tx_start, + .tx_complete = phytmac_tx_complete, + .rx_complete = phytmac_rx_complete, + .get_rx_pkt_len = phytmac_rx_pkt_len, + .get_desc_addr = phytmac_get_desc_addr, + .init_rx_map = phytmac_init_rx_map_desc, + .rx_map = phytmac_rx_map_desc, + .rx_checksum = phytmac_rx_checksum, + .rx_single_buffer = phytmac_rx_single_buffer, + .rx_pkt_start = phytmac_rx_sof, + .rx_pkt_end = phytmac_rx_eof, + .clear_rx_desc = phytmac_clear_rx_desc, + .clear_tx_desc = phytmac_clear_tx_desc, + + /* ptp */ + .init_ts_hw = phytmac_ptp_init_hw, + .set_time = phytmac_set_time, + .clear_time = phytmac_clear_time, + .get_time = phytmac_get_time, + .set_ts_config = phytmac_set_tsmode, + .set_incr = phytmac_set_tsincr, + .adjust_fine = phytmac_adjust_fine, + .adjust_time = phytmac_adjust_time, + .ts_valid = phytmac_ts_valid, + .get_timestamp = phytmac_get_dma_ts, + .get_ts_rate = phytmac_get_ts_rate, +}; +EXPORT_SYMBOL_GPL(phytmac_2p0_hw); diff --git a/drivers/net/ethernet/phytium/phytmac_v2.h b/drivers/net/ethernet/phytium/phytmac_v2.h new file mode 100644 index 0000000000000000000000000000000000000000..92e4806ac2c1f7f72be6f0f74d34b6624d23800c --- /dev/null +++ b/drivers/net/ethernet/phytium/phytmac_v2.h @@ -0,0 +1,362 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _PHYTMAC_V2_H +#define _PHYTMAC_V2_H + +extern struct phytmac_hw_if phytmac_2p0_hw; + +#define PHYTMAC_MSG_SRAM_SIZE 4096 +#define MSG_HDR_LEN 8 + +#define PHYTMAC_TX_MSG_HEAD 0x000 +#define PHYTMAC_TX_MSG_TAIL 0x004 +#define PHYTMAC_RX_MSG_HEAD 0x008 +#define PHYTMAC_RX_MSG_TAIL 0x00c +#define PHYTMAC_MSG_IMR 0x020 +#define PHYTMAC_MSG_ISR 0x02c + +#define PHYTMAC_SIZE 0x0048 +#define PHYTMAC_NETWORK_STATUS 0x0240 +#define PHYTMAC_PCS_AN_LP 0x0244 +#define PHYTMAC_USX_LINK_STATUS 0x0248 +#define PHYTMAC_MDIO 0x0264 +#define PHYTMAC_TIMER_INCR_SUB_NSEC 0x024c +#define PHYTMAC_TIMER_INCR 0x0250 +#define PHYTMAC_TIMER_MSB_SEC 0x0254 +#define PHYTMAC_TIMER_SEC 0x0258 +#define PHYTMAC_TIMER_NSEC 0x025c +#define PHYTMAC_TIMER_ADJUST 0x0260 +#define PHYTMAC_MSG(i) (((i) - 1) * 0x48) + +#define PHYTMAC_MODULE_ID_GMAC 0x60 +#define PHYTMAC_FLAGS_MSG_COMP 0x1 +#define PHYTMAC_FLAGS_MSG_NOINT 0x2 + +/* Bitfields in PHYTMAC_TX_MSG_TAIL */ +#define PHYTMAC_TX_MSG_INT_INDEX 16 +#define PHYTMAC_TX_MSG_INT_WIDTH 1 + +/* Bitfields in PHYTMAC_MSG_ISR */ +#define PHYTMAC_MSG_COMPLETE_INDEX 0 +#define PHYTMAC_MSG_COMPLETE_WIDTH 1 + +/* Bitfields in PHYTMAC_SIZE */ +#define PHYTMAC_MEM_SIZE_INDEX 0 +#define PHYTMAC_MEM_SIZE_WIDTH 4 +#define PHYTMAC_TXRING_SIZE_INDEX 8 +#define PHYTMAC_TXRING_SIZE_WIDTH 6 + +/* Bitfields in PHYTMAC_TIMER_INCR_SUB_NSEC */ +#define PHYTMAC_INCR_SNSECH_INDEX 0 +#define PHYTMAC_INCR_SNSECH_WIDTH 16 +#define PHYTMAC_INCR_SNSECL_INDEX 24 +#define PHYTMAC_INCR_SNSECL_WIDTH 8 +#define PHYTMAC_INCR_SNSEC_WIDTH 24 + +/* Bitfields in PHYTMAC_TIMER_INCR_SUB_NSEC */ +#define PHYTMAC_INCR_SNSEC_INDEX 0 +#define PHYTMAC_INCR_SNSEC_WIDTH 24 + +/* Bitfields in PHYTMAC_TIMER_INCR */ +#define PHYTMAC_INCR_NSEC_INDEX 0 +#define PHYTMAC_INCR_NSEC_WIDTH 8 + +/* Bitfields in PHYTMAC_TIMER_MSB_SEC */ +#define PHYTMAC_TIMER_SECH_INDEX 0 +#define PHYTMAC_TIMER_SECH_WIDTH 16 + +/* Bitfields in PHYTMAC_TIMER_SEC */ +#define PHYTMAC_TIMER_SECL_INDEX 0 +#define PHYTMAC_TIMER_SECL_WIDTH 32 + +/* Bitfields in PHYTMAC_TIMER_NSEC */ +#define PHYTMAC_TIMER_NSEC_INDEX 0 +#define PHYTMAC_TIMER_NSEC_WIDTH 30 + +/* Bitfields in PHYTMAC_TIMER_ADJUST */ +#define PHYTMAC_ASEC_INDEX 0 +#define PHYTMAC_ASEC_WIDTH 30 +#define PHYTMAC_AADD_INDEX 31 +#define PHYTMAC_AADD_WIDTH 1 +#define PHYTMAC_ASEC_MAX ((1 << PHYTMAC_ASEC_WIDTH) - 1) + +#define PHYTMAC_TIMER_SEC_WIDTH (PHYTMAC_TIMER_SECH_WIDTH + PHYTMAC_TIMER_SECL_WIDTH) +#define TIMER_SEC_MAX_VAL (((u64)1 << PHYTMAC_TIMER_SEC_WIDTH) - 1) +#define TIMER_NSEC_MAX_VAL ((1 << PHYTMAC_TIMER_NSEC_WIDTH) - 1) + +#define PHYTMAC_TAIL_PTR(i) (0x0100 + ((i) * 4)) +#define PHYTMAC_INT_ER(i) (0x0140 + ((i) * 4)) +#define PHYTMAC_INT_DR(i) (0x0180 + ((i) * 4)) +#define PHYTMAC_INT_MR(i) (0x01c0 + ((i) * 4)) +#define PHYTMAC_INT_SR(i) (0x0200 + ((i) * 4)) + +#define PHYTMAC_LINK_INDEX 0 /* PCS link status */ +#define PHYTMAC_LINK_WIDTH 1 +#define PHYTMAC_MIDLE_INDEX 2 /* Mdio idle */ +#define PHYTMAC_MIDLE_WIDTH 1 + +/* Int stauts/Enable/Disable/Mask Register */ +#define PHYTMAC_RXCOMP_INDEX 1 /* Rx complete */ +#define PHYTMAC_RXCOMP_WIDTH 1 +#define PHYTMAC_RUSED_INDEX 2 /* Rx used bit read */ +#define PHYTMAC_RUSED_WIDTH 1 +#define PHYTMAC_DMA_ERR_INDEX 6 /* AMBA error */ +#define PHYTMAC_DMA_ERR_WIDTH 1 +#define PHYTMAC_TXCOMP_INDEX 7 /* Tx complete */ +#define PHYTMAC_TXCOMP_WIDTH 1 +#define PHYTMAC_RXOVERRUN_INDEX 10 /* Rx overrun */ +#define PHYTMAC_RXOVERRUN_WIDTH 1 +#define PHYTMAC_RESP_ERR_INDEX 11 /* Resp not ok */ +#define PHYTMAC_RESP_ERR_WIDTH 1 + +/* pcs an lp */ +#define PHYTMAC_AUTO_NEG_INDEX 12 +#define PHYTMAC_AUTO_NEG_WIDTH 1 + +/* Bitfields in USX_STATUS. */ +#define PHYTMAC_USX_LINK_INDEX 0 +#define PHYTMAC_USX_LINK_WIDTH 1 + +/* Mdio read/write Register */ +#define PHYTMAC_VALUE_INDEX 0 /* value */ +#define PHYTMAC_VALUE_WIDTH 16 +#define PHYTMAC_CONST_INDEX 16 /* Must Be 10 */ +#define PHYTMAC_CONST_WIDTH 2 +#define PHYTMAC_REGADDR_INDEX 18 /* Register address */ +#define PHYTMAC_REGADDR_WIDTH 5 +#define PHYTMAC_PHYADDR_INDEX 23 /* Phy address */ +#define PHYTMAC_PHYADDR_WIDTH 5 +#define PHYTMAC_MDCOPS_INDEX 28 +#define PHYTMAC_MDCOPS_WIDTH 2 +#define PHYTMAC_CLAUSESEL_INDEX 30 +#define PHYTMAC_CLAUSESEL_WIDTH 1 +#define PHYTMAC_C22 1 +#define PHYTMAC_C45 0 +#define PHYTMAC_C45_ADDR 0 +#define PHYTMAC_C45_WRITE 1 +#define PHYTMAC_C45_READ 3 +#define PHYTMAC_C22_WRITE 1 +#define PHYTMAC_C22_READ 2 + +/* rx dma desc bit */ +/* DMA descriptor bitfields */ +#define PHYTMAC_RXUSED_INDEX 0 +#define PHYTMAC_RXUSED_WIDTH 1 +#define PHYTMAC_RXWRAP_INDEX 1 +#define PHYTMAC_RXWRAP_WIDTH 1 +#define PHYTMAC_RXTSVALID_INDEX 2 +#define PHYTMAC_RXTSVALID_WIDTH 1 +#define PHYTMAC_RXWADDR_INDEX 2 +#define PHYTMAC_RXWADDR_WIDTH 30 + +#define PHYTMAC_RXFRMLEN_INDEX 0 +#define PHYTMAC_RXFRMLEN_WIDTH 12 +#define PHYTMAC_RXINDEX_INDEX 12 +#define PHYTMAC_RXINDEX_WIDTH 2 +#define PHYTMAC_RXSOF_INDEX 14 +#define PHYTMAC_RXSOF_WIDTH 1 +#define PHYTMAC_RXEOF_INDEX 15 +#define PHYTMAC_RXEOF_WIDTH 1 + +#define PHYTMAC_RXFRMLEN_MASK 0x1FFF +#define PHYTMAC_RXJFRMLEN_MASK 0x3FFF + +#define PHYTMAC_RXTYPEID_MATCH_INDEX 22 +#define PHYTMAC_RXTYPEID_MATCH_WIDTH 2 +#define PHYTMAC_RXCSUM_INDEX 22 +#define PHYTMAC_RXCSUM_WIDTH 2 + +/* Buffer descriptor constants */ +#define PHYTMAC_RXCSUM_NONE 0 +#define PHYTMAC_RXCSUM_IP 1 +#define PHYTMAC_RXCSUM_IP_TCP 2 +#define PHYTMAC_RXCSUM_IP_UDP 3 + +#define PHYTMAC_TXFRMLEN_INDEX 0 +#define PHYTMAC_TXFRMLEN_WIDTH 14 +#define PHYTMAC_TXLAST_INDEX 15 +#define PHYTMAC_TXLAST_WIDTH 1 +#define PHYTMAC_TXNOCRC_INDEX 16 +#define PHYTMAC_TXNOCRC_WIDTH 1 +#define PHYTMAC_MSSMFS_INDEX 16 +#define PHYTMAC_MSSMFS_WIDTH 14 +#define PHYTMAC_TXLSO_INDEX 17 +#define PHYTMAC_TXLSO_WIDTH 2 +#define PHYTMAC_TXTCP_SEQ_SRC_INDEX 19 +#define PHYTMAC_TXTCP_SEQ_SRC_WIDTH 1 +#define PHYTMAC_TXTSVALID_INDEX 23 +#define PHYTMAC_TXTSVALID_WIDTH 1 +#define PHYTMAC_TXWRAP_INDEX 30 +#define PHYTMAC_TXWRAP_WIDTH 1 +#define PHYTMAC_TXUSED_INDEX 31 +#define PHYTMAC_TXUSED_WIDTH 1 + +/* dma ts */ +#define PHYTMAC_TS_NSEC_INDEX 0 +#define PHYTMAC_TS_NSEC_WIDTH 30 +#define PHYTMAC_TS_SECL_INDEX 30 +#define PHYTMAC_TS_SECL_WIDTH 2 +#define PHYTMAC_TS_SECH_INDEX 0 +#define PHYTMAC_TS_SECH_WIDTH 4 +#define PHYTMAC_TS_SEC_MASK 0x3f +#define PHYTMAC_TS_SEC_TOP 0x40 + +#define HW_DMA_CAP_64B 0x1 +#define HW_DMA_CAP_CSUM 0x2 +#define HW_DMA_CAP_PTP 0x4 +#define HW_DMA_CAP_DDW64 0x8 +#define HW_DMA_CAP_DDW128 0x10 + +#define PHYTMAC_DBW64 2 +#define PHYTMAC_DBW128 4 + +enum phytmac_msg_cmd_id { + PHYTMAC_MSG_CMD_DEFAULT = 0, + PHYTMAC_MSG_CMD_SET, + PHYTMAC_MSG_CMD_GET, + PHYTMAC_MSG_CMD_DATA, + PHYTMAC_MSG_CMD_REPORT, +}; + +enum phytmac_default_subid { + PHYTMAC_MSG_CMD_DEFAULT_RESET_HW = 0, + PHYTMAC_MSG_CMD_DEFAULT_RESET_TX_QUEUE, + PHYTMAC_MSG_CMD_DEFAULT_RESET_RX_QUEUE, +}; + +enum phytmac_set_subid { + PHYTMAC_MSG_CMD_SET_INIT_ALL = 0, + PHYTMAC_MSG_CMD_SET_INIT_RING = 1, + PHYTMAC_MSG_CMD_SET_INIT_TX_RING = 2, + PHYTMAC_MSG_CMD_SET_INIT_RX_RING = 3, + PHYTMAC_MSG_CMD_SET_MAC_CONFIG = 4, + PHYTMAC_MSG_CMD_SET_ADDR = 5, + PHYTMAC_MSG_CMD_SET_DMA_RX_BUFSIZE = 6, + PHYTMAC_MSG_CMD_SET_DMA = 7, + PHYTMAC_MSG_CMD_SET_CAPS = 8, + PHYTMAC_MSG_CMD_SET_TS_CONFIG = 9, + PHYTMAC_MSG_CMD_SET_INIT_TX_ENABLE_TRANSMIT = 10, + PHYTMAC_MSG_CMD_SET_INIT_RX_ENABLE_RECEIVE = 11, + PHYTMAC_MSG_CMD_SET_ENABLE_NETWORK = 12, + PHYTMAC_MSG_CMD_SET_DISABLE_NETWORK = 13, + PHYTMAC_MSG_CMD_SET_ENABLE_MDIO = 14, + PHYTMAC_MSG_CMD_SET_DISABLE_MDIO = 15, + PHYTMAC_MSG_CMD_SET_ENABLE_TXCSUM = 16, + PHYTMAC_MSG_CMD_SET_DISABLE_TXCSUM = 17, + PHYTMAC_MSG_CMD_SET_ENABLE_RXCSUM = 18, + PHYTMAC_MSG_CMD_SET_DISABLE_RXCSUM = 19, + PHYTMAC_MSG_CMD_SET_ENABLE_PROMISE = 20, + PHYTMAC_MSG_CMD_SET_DISABLE_PROMISE = 21, + PHYTMAC_MSG_CMD_SET_ENABLE_MC = 22, + PHYTMAC_MSG_CMD_SET_DISABLE_MC = 23, + PHYTMAC_MSG_CMD_SET_ENABLE_HASH_MC = 24, + PHYTMAC_MSG_CMD_SET_ENABLE_PAUSE = 25, + PHYTMAC_MSG_CMD_SET_DISABLE_PAUSE = 26, + PHYTMAC_MSG_CMD_SET_ENABLE_JUMBO = 27, + PHYTMAC_MSG_CMD_SET_DISABLE_JUMBO = 28, + PHYTMAC_MSG_CMD_SET_ENABLE_1536_FRAMES = 29, + PHYTMAC_MSG_CMD_SET_ENABLE_STRIPCRC = 30, + PHYTMAC_MSG_CMD_SET_DISABLE_STRIPCRC = 31, + PHYTMAC_MSG_CMD_SET_PCS_LINK_UP = 32, + PHYTMAC_MSG_CMD_SET_PCS_LINK_DOWN = 33, + PHYTMAC_MSG_CMD_SET_MAC_LINK_CONFIG = 34, + PHYTMAC_MSG_CMD_SET_REG_WRITE = 35, + PHYTMAC_MSG_CMD_SET_ENABLE_BC = 36, + PHYTMAC_MSG_CMD_SET_DISABLE_BC = 37, + PHYTMAC_MSG_CMD_SET_ETH_MATCH = 38, + PHYTMAC_MSG_CMD_SET_ADD_FDIR = 39, + PHYTMAC_MSG_CMD_SET_DEL_FDIR = 40, + PHYTMAC_MSG_CMD_SET_ENABLE_AUTONEG = 41, + PHYTMAC_MSG_CMD_SET_DISABLE_AUTONEG = 42, + PHYTMAC_MSG_CMD_SET_RX_DATA_OFFSET = 43, + PHYTMAC_MSG_CMD_SET_WOL = 44, +}; + +enum phytmac_get_subid { + PHYTMAC_MSG_CMD_GET_ADDR, + PHYTMAC_MSG_CMD_GET_QUEUENUMS, + PHYTMAC_MSG_CMD_GET_CAPS, + PHYTMAC_MSG_CMD_GET_BD_PREFETCH, + PHYTMAC_MSG_CMD_GET_STATS, + PHYTMAC_MSG_CMD_GET_REG_READ, +}; + +struct phytmac_interface_info { + u8 interface; + u8 autoneg; + u16 duplex; + u32 speed; +} __packed; + +struct phytmac_mc_info { + u32 mc_bottom; + u32 mc_top; +} __packed; + +struct phytmac_fdir_info { + u32 ip4src; + u32 ip4dst; + u16 srcport; + u16 srcport_mask; + u16 dstport; + u16 dstport_mask; + u8 location; + u8 queue; + u8 ipsrc_en; + u8 ipdst_en; + u8 port_en; +} __packed; + +struct phytmac_ts_config { + u8 tx_mode; + u8 rx_mode; + u8 one_step; +} __packed; + +struct phytmac_ring_info { + u64 addr[4]; + u8 queue_num; + u8 hw_dma_cap; +} __packed; + +struct phytmac_rxbuf_info { + u8 queue_num; + u8 buffer_size; +} __packed; + +struct phytmac_dma_info { + u16 dma_burst_length; + u8 hw_dma_cap; +} __packed; + +struct phytmac_eth_info { + u16 index; + u16 etype; +} __packed; + +struct phytmac_mac { + u32 addrl; + u16 addrh; +} __packed; + +struct phytmac_feature { + u8 irq_read_clear; + u8 dma_data_width; + u8 dma_addr_width; + u8 tx_pkt_buffer; + u8 rx_pkt_buffer; + u8 pbuf_lso; + u8 queue_num; + u8 tx_bd_prefetch; + u8 rx_bd_prefetch; + u8 max_rx_fs; +} __packed; + +struct phytmac_msg_info { + u16 module_id; + u16 cmd_id; + u16 cmd_subid; + u16 flags; + u8 para[64]; +} __packed; + +#endif