Merge branch 'genet_5.10' into devel-1.6

This commit is contained in:
Bjarne von Horn 2024-02-07 14:48:05 +01:00
commit 26ffb1d265
29 changed files with 24208 additions and 7 deletions

View File

@ -149,6 +149,17 @@
#include "../globals.h"
#include "ecdev.h"
#ifdef CONFIG_SUSE_KERNEL
#include <linux/suse_version.h>
#else
# ifndef SUSE_VERSION
# define SUSE_VERSION 0
# endif
# ifndef SUSE_PATCHLEVEL
# define SUSE_PATCHLEVEL 0
# endif
#endif
#define RTL8139_DRIVER_NAME DRV_NAME \
" EtherCAT-capable Fast Ethernet driver " \
DRV_VERSION ", master " EC_MASTER_VERSION
@ -2383,8 +2394,11 @@ static int rtl8139_set_mac_address(struct net_device *dev, void *p)
if (!is_valid_ether_addr(addr->sa_data))
return -EADDRNOTAVAIL;
#if SUSE_VERSION == 15 && SUSE_PATCHLEVEL >= 5
eth_hw_addr_set(dev, addr->sa_data);
#else
memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
#endif
spin_lock_irq(&tp->lock);
RTL_W8_F(Cfg9346, Cfg9346_Unlock);

View File

@ -2408,7 +2408,11 @@ static int e100_set_mac_address(struct net_device *netdev, void *p)
if (!is_valid_ether_addr(addr->sa_data))
return -EADDRNOTAVAIL;
#if SUSE_VERSION == 15 && SUSE_PATCHLEVEL >= 5
eth_hw_addr_set(netdev, addr->sa_data);
#else
memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
#endif
e100_exec_cb(nic, NULL, e100_setup_iaaddr);
return 0;
@ -3092,7 +3096,11 @@ static int e100_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
e100_phy_init(nic);
#if SUSE_VERSION == 15 && SUSE_PATCHLEVEL >= 5
eth_hw_addr_set(netdev, (const u8*)nic->eeprom);
#else
memcpy(netdev->dev_addr, nic->eeprom, ETH_ALEN);
#endif
if (!is_valid_ether_addr(netdev->dev_addr)) {
if (!eeprom_bad_csum_allow) {
netif_err(nic, probe, nic->netdev, "Invalid MAC address from EEPROM, aborting\n");

View File

@ -8,6 +8,17 @@
#include <linux/bitops.h>
#include <linux/if_vlan.h>
#ifdef CONFIG_SUSE_KERNEL
#include <linux/suse_version.h>
#else
# ifndef SUSE_VERSION
# define SUSE_VERSION 0
# endif
# ifndef SUSE_PATCHLEVEL
# define SUSE_PATCHLEVEL 0
# endif
#endif
char e1000_driver_name[] = "ec_e1000";
static char e1000_driver_string[] = "Intel(R) PRO/1000 Network Driver";
static const char e1000_copyright[] = "Copyright (c) 1999-2006 Intel Corporation.";
@ -1136,7 +1147,11 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
e_err(probe, "EEPROM Read Error\n");
}
/* don't block initialization here due to bad MAC address */
#if SUSE_VERSION == 15 && SUSE_PATCHLEVEL >= 5
eth_hw_addr_set(netdev, hw->mac_addr);
#else
memcpy(netdev->dev_addr, hw->mac_addr, netdev->addr_len);
#endif
if (!is_valid_ether_addr(netdev->dev_addr))
e_err(probe, "Invalid MAC Address\n");
@ -2276,7 +2291,11 @@ static int e1000_set_mac(struct net_device *netdev, void *p)
if (hw->mac_type == e1000_82542_rev2_0)
e1000_enter_82542_rst(adapter);
#if SUSE_VERSION == 15 && SUSE_PATCHLEVEL >= 5
eth_hw_addr_set(netdev, addr->sa_data);
#else
memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
#endif
memcpy(hw->mac_addr, addr->sa_data, netdev->addr_len);
e1000_rar_set(hw, hw->mac_addr, 0);

View File

@ -29,6 +29,17 @@
#include "e1000-5.14-ethercat.h"
#ifdef CONFIG_SUSE_KERNEL
#include <linux/suse_version.h>
#else
# ifndef SUSE_VERSION
# define SUSE_VERSION 0
# endif
# ifndef SUSE_PATCHLEVEL
# define SUSE_PATCHLEVEL 0
# endif
#endif
char e1000e_driver_name[] = "ec_e1000e";
static inline int check_arbiter_wa_flag(const struct e1000_adapter *adapter)
@ -4290,7 +4301,7 @@ void e1000e_reset(struct e1000_adapter *adapter)
/**
* e1000e_trigger_lsc - trigger an LSC interrupt
* @adapter:
* @adapter:
*
* Fire a link status change interrupt to start the watchdog.
**/
@ -4887,7 +4898,11 @@ static int e1000_set_mac(struct net_device *netdev, void *p)
if (!is_valid_ether_addr(addr->sa_data))
return -EADDRNOTAVAIL;
#if SUSE_VERSION == 15 && SUSE_PATCHLEVEL >= 5
eth_hw_addr_set(netdev, addr->sa_data);
#else
memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
#endif
memcpy(adapter->hw.mac.addr, addr->sa_data, netdev->addr_len);
hw->mac.ops.rar_set(&adapter->hw, adapter->hw.mac.addr, 0);
@ -7705,7 +7720,11 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
dev_err(&pdev->dev,
"NVM Read Error while reading MAC address\n");
#if SUSE_VERSION == 15 && SUSE_PATCHLEVEL >= 5
eth_hw_addr_set(netdev, adapter->hw.mac.addr);
#else
memcpy(netdev->dev_addr, adapter->hw.mac.addr, netdev->addr_len);
#endif
if (!is_valid_ether_addr(netdev->dev_addr)) {
dev_err(&pdev->dev, "Invalid MAC Address: %pM\n",

View File

@ -41,6 +41,17 @@
#define EC_GEN_RX_BUF_SIZE 1600
#ifdef CONFIG_SUSE_KERNEL
#include <linux/suse_version.h>
#else
# ifndef SUSE_VERSION
# define SUSE_VERSION 0
# endif
# ifndef SUSE_PATCHLEVEL
# define SUSE_PATCHLEVEL 0
# endif
#endif
/****************************************************************************/
int __init ec_gen_init_module(void);
@ -239,7 +250,7 @@ int ec_gen_device_offer(
int ret = 0;
dev->used_netdev = desc->netdev;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) || (SUSE_VERSION == 15 && SUSE_PATCHLEVEL >= 5)
eth_hw_addr_set(dev->netdev, desc->dev_addr);
#else
memcpy(dev->netdev->dev_addr, desc->dev_addr, ETH_ALEN);

View File

@ -22,13 +22,33 @@
include $(top_srcdir)/Makefile.kbuild
EXTRA_DIST = \
bcmgenet-5.10-ethercat.c \
bcmgenet-5.10-ethercat.h \
bcmgenet-5.10-orig.c \
bcmgenet-5.10-orig.h \
bcmgenet-5.14-ethercat.c \
bcmgenet-5.14-ethercat.h \
bcmgenet-5.14-orig.c \
bcmgenet-5.14-orig.h \
bcmgenet-6.1-ethercat.c \
bcmgenet-6.1-ethercat.h \
bcmgenet-6.1-orig.c \
bcmgenet-6.1-orig.h \
bcmgenet_wol-5.10-ethercat.c \
bcmgenet_wol-5.10-orig.c \
bcmgenet_wol-5.14-ethercat.c \
bcmgenet_wol-5.14-orig.c \
bcmgenet_wol-6.1-ethercat.c \
bcmgenet_wol-6.1-orig.c \
bcmmii-5.14-ethercat.c \
bcmmii-5.14-orig.c \
bcmmii-5.10-ethercat.c \
bcmmii-5.10-orig.c \
bcmmii-6.1-ethercat.c \
bcmmii-6.1-orig.c
bcmmii-6.1-orig.c \
unimac-5.14-ethercat.h \
unimac-5.14-orig.h \
unimac-6.1-ethercat.h \
unimac-6.1-orig.h
#------------------------------------------------------------------------------

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,764 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2014-2020 Broadcom
*/
#ifndef __BCMGENET_H__
#define __BCMGENET_H__
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/spinlock.h>
#include <linux/clk.h>
#include <linux/mii.h>
#include <linux/if_vlan.h>
#include <linux/phy.h>
#include <linux/dim.h>
#include <linux/ethtool.h>
/* EtherCAT header file */
#include "../ecdev.h"
/* total number of Buffer Descriptors, same for Rx/Tx */
#define TOTAL_DESC 256
/* which ring is descriptor based */
#define DESC_INDEX 16
/* Body(1500) + EH_SIZE(14) + VLANTAG(4) + BRCMTAG(6) + FCS(4) = 1528.
* 1536 is multiple of 256 bytes
*/
#define ENET_BRCM_TAG_LEN 6
#define ENET_PAD 8
#define ENET_MAX_MTU_SIZE (ETH_DATA_LEN + ETH_HLEN + VLAN_HLEN + \
ENET_BRCM_TAG_LEN + ETH_FCS_LEN + ENET_PAD)
#define DMA_MAX_BURST_LENGTH 0x10
/* misc. configuration */
#define MAX_NUM_OF_FS_RULES 16
#define CLEAR_ALL_HFB 0xFF
#define DMA_FC_THRESH_HI (TOTAL_DESC >> 4)
#define DMA_FC_THRESH_LO 5
/* 64B receive/transmit status block */
struct status_64 {
u32 length_status; /* length and peripheral status */
u32 ext_status; /* Extended status*/
u32 rx_csum; /* partial rx checksum */
u32 unused1[9]; /* unused */
u32 tx_csum_info; /* Tx checksum info. */
u32 unused2[3]; /* unused */
};
/* Rx status bits */
#define STATUS_RX_EXT_MASK 0x1FFFFF
#define STATUS_RX_CSUM_MASK 0xFFFF
#define STATUS_RX_CSUM_OK 0x10000
#define STATUS_RX_CSUM_FR 0x20000
#define STATUS_RX_PROTO_TCP 0
#define STATUS_RX_PROTO_UDP 1
#define STATUS_RX_PROTO_ICMP 2
#define STATUS_RX_PROTO_OTHER 3
#define STATUS_RX_PROTO_MASK 3
#define STATUS_RX_PROTO_SHIFT 18
#define STATUS_FILTER_INDEX_MASK 0xFFFF
/* Tx status bits */
#define STATUS_TX_CSUM_START_MASK 0X7FFF
#define STATUS_TX_CSUM_START_SHIFT 16
#define STATUS_TX_CSUM_PROTO_UDP 0x8000
#define STATUS_TX_CSUM_OFFSET_MASK 0x7FFF
#define STATUS_TX_CSUM_LV 0x80000000
/* DMA Descriptor */
#define DMA_DESC_LENGTH_STATUS 0x00 /* in bytes of data in buffer */
#define DMA_DESC_ADDRESS_LO 0x04 /* lower bits of PA */
#define DMA_DESC_ADDRESS_HI 0x08 /* upper 32 bits of PA, GENETv4+ */
/* Rx/Tx common counter group */
struct bcmgenet_pkt_counters {
u32 cnt_64; /* RO Received/Transmited 64 bytes packet */
u32 cnt_127; /* RO Rx/Tx 127 bytes packet */
u32 cnt_255; /* RO Rx/Tx 65-255 bytes packet */
u32 cnt_511; /* RO Rx/Tx 256-511 bytes packet */
u32 cnt_1023; /* RO Rx/Tx 512-1023 bytes packet */
u32 cnt_1518; /* RO Rx/Tx 1024-1518 bytes packet */
u32 cnt_mgv; /* RO Rx/Tx 1519-1522 good VLAN packet */
u32 cnt_2047; /* RO Rx/Tx 1522-2047 bytes packet*/
u32 cnt_4095; /* RO Rx/Tx 2048-4095 bytes packet*/
u32 cnt_9216; /* RO Rx/Tx 4096-9216 bytes packet*/
};
/* RSV, Receive Status Vector */
struct bcmgenet_rx_counters {
struct bcmgenet_pkt_counters pkt_cnt;
u32 pkt; /* RO (0x428) Received pkt count*/
u32 bytes; /* RO Received byte count */
u32 mca; /* RO # of Received multicast pkt */
u32 bca; /* RO # of Receive broadcast pkt */
u32 fcs; /* RO # of Received FCS error */
u32 cf; /* RO # of Received control frame pkt*/
u32 pf; /* RO # of Received pause frame pkt */
u32 uo; /* RO # of unknown op code pkt */
u32 aln; /* RO # of alignment error count */
u32 flr; /* RO # of frame length out of range count */
u32 cde; /* RO # of code error pkt */
u32 fcr; /* RO # of carrier sense error pkt */
u32 ovr; /* RO # of oversize pkt*/
u32 jbr; /* RO # of jabber count */
u32 mtue; /* RO # of MTU error pkt*/
u32 pok; /* RO # of Received good pkt */
u32 uc; /* RO # of unicast pkt */
u32 ppp; /* RO # of PPP pkt */
u32 rcrc; /* RO (0x470),# of CRC match pkt */
};
/* TSV, Transmit Status Vector */
struct bcmgenet_tx_counters {
struct bcmgenet_pkt_counters pkt_cnt;
u32 pkts; /* RO (0x4a8) Transmited pkt */
u32 mca; /* RO # of xmited multicast pkt */
u32 bca; /* RO # of xmited broadcast pkt */
u32 pf; /* RO # of xmited pause frame count */
u32 cf; /* RO # of xmited control frame count */
u32 fcs; /* RO # of xmited FCS error count */
u32 ovr; /* RO # of xmited oversize pkt */
u32 drf; /* RO # of xmited deferral pkt */
u32 edf; /* RO # of xmited Excessive deferral pkt*/
u32 scl; /* RO # of xmited single collision pkt */
u32 mcl; /* RO # of xmited multiple collision pkt*/
u32 lcl; /* RO # of xmited late collision pkt */
u32 ecl; /* RO # of xmited excessive collision pkt*/
u32 frg; /* RO # of xmited fragments pkt*/
u32 ncl; /* RO # of xmited total collision count */
u32 jbr; /* RO # of xmited jabber count*/
u32 bytes; /* RO # of xmited byte count */
u32 pok; /* RO # of xmited good pkt */
u32 uc; /* RO (0x0x4f0)# of xmited unitcast pkt */
};
struct bcmgenet_mib_counters {
struct bcmgenet_rx_counters rx;
struct bcmgenet_tx_counters tx;
u32 rx_runt_cnt;
u32 rx_runt_fcs;
u32 rx_runt_fcs_align;
u32 rx_runt_bytes;
u32 rbuf_ovflow_cnt;
u32 rbuf_err_cnt;
u32 mdf_err_cnt;
u32 alloc_rx_buff_failed;
u32 rx_dma_failed;
u32 tx_dma_failed;
u32 tx_realloc_tsb;
u32 tx_realloc_tsb_failed;
};
#define UMAC_HD_BKP_CTRL 0x004
#define HD_FC_EN (1 << 0)
#define HD_FC_BKOFF_OK (1 << 1)
#define IPG_CONFIG_RX_SHIFT 2
#define IPG_CONFIG_RX_MASK 0x1F
#define UMAC_CMD 0x008
#define CMD_TX_EN (1 << 0)
#define CMD_RX_EN (1 << 1)
#define UMAC_SPEED_10 0
#define UMAC_SPEED_100 1
#define UMAC_SPEED_1000 2
#define UMAC_SPEED_2500 3
#define CMD_SPEED_SHIFT 2
#define CMD_SPEED_MASK 3
#define CMD_PROMISC (1 << 4)
#define CMD_PAD_EN (1 << 5)
#define CMD_CRC_FWD (1 << 6)
#define CMD_PAUSE_FWD (1 << 7)
#define CMD_RX_PAUSE_IGNORE (1 << 8)
#define CMD_TX_ADDR_INS (1 << 9)
#define CMD_HD_EN (1 << 10)
#define CMD_SW_RESET (1 << 13)
#define CMD_LCL_LOOP_EN (1 << 15)
#define CMD_AUTO_CONFIG (1 << 22)
#define CMD_CNTL_FRM_EN (1 << 23)
#define CMD_NO_LEN_CHK (1 << 24)
#define CMD_RMT_LOOP_EN (1 << 25)
#define CMD_PRBL_EN (1 << 27)
#define CMD_TX_PAUSE_IGNORE (1 << 28)
#define CMD_TX_RX_EN (1 << 29)
#define CMD_RUNT_FILTER_DIS (1 << 30)
#define UMAC_MAC0 0x00C
#define UMAC_MAC1 0x010
#define UMAC_MAX_FRAME_LEN 0x014
#define UMAC_MODE 0x44
#define MODE_LINK_STATUS (1 << 5)
#define UMAC_EEE_CTRL 0x064
#define EN_LPI_RX_PAUSE (1 << 0)
#define EN_LPI_TX_PFC (1 << 1)
#define EN_LPI_TX_PAUSE (1 << 2)
#define EEE_EN (1 << 3)
#define RX_FIFO_CHECK (1 << 4)
#define EEE_TX_CLK_DIS (1 << 5)
#define DIS_EEE_10M (1 << 6)
#define LP_IDLE_PREDICTION_MODE (1 << 7)
#define UMAC_EEE_LPI_TIMER 0x068
#define UMAC_EEE_WAKE_TIMER 0x06C
#define UMAC_EEE_REF_COUNT 0x070
#define EEE_REFERENCE_COUNT_MASK 0xffff
#define UMAC_TX_FLUSH 0x334
#define UMAC_MIB_START 0x400
#define UMAC_MDIO_CMD 0x614
#define MDIO_START_BUSY (1 << 29)
#define MDIO_READ_FAIL (1 << 28)
#define MDIO_RD (2 << 26)
#define MDIO_WR (1 << 26)
#define MDIO_PMD_SHIFT 21
#define MDIO_PMD_MASK 0x1F
#define MDIO_REG_SHIFT 16
#define MDIO_REG_MASK 0x1F
#define UMAC_RBUF_OVFL_CNT_V1 0x61C
#define RBUF_OVFL_CNT_V2 0x80
#define RBUF_OVFL_CNT_V3PLUS 0x94
#define UMAC_MPD_CTRL 0x620
#define MPD_EN (1 << 0)
#define MPD_PW_EN (1 << 27)
#define MPD_MSEQ_LEN_SHIFT 16
#define MPD_MSEQ_LEN_MASK 0xFF
#define UMAC_MPD_PW_MS 0x624
#define UMAC_MPD_PW_LS 0x628
#define UMAC_RBUF_ERR_CNT_V1 0x634
#define RBUF_ERR_CNT_V2 0x84
#define RBUF_ERR_CNT_V3PLUS 0x98
#define UMAC_MDF_ERR_CNT 0x638
#define UMAC_MDF_CTRL 0x650
#define UMAC_MDF_ADDR 0x654
#define UMAC_MIB_CTRL 0x580
#define MIB_RESET_RX (1 << 0)
#define MIB_RESET_RUNT (1 << 1)
#define MIB_RESET_TX (1 << 2)
#define RBUF_CTRL 0x00
#define RBUF_64B_EN (1 << 0)
#define RBUF_ALIGN_2B (1 << 1)
#define RBUF_BAD_DIS (1 << 2)
#define RBUF_STATUS 0x0C
#define RBUF_STATUS_WOL (1 << 0)
#define RBUF_STATUS_MPD_INTR_ACTIVE (1 << 1)
#define RBUF_STATUS_ACPI_INTR_ACTIVE (1 << 2)
#define RBUF_CHK_CTRL 0x14
#define RBUF_RXCHK_EN (1 << 0)
#define RBUF_SKIP_FCS (1 << 4)
#define RBUF_L3_PARSE_DIS (1 << 5)
#define RBUF_ENERGY_CTRL 0x9c
#define RBUF_EEE_EN (1 << 0)
#define RBUF_PM_EN (1 << 1)
#define RBUF_TBUF_SIZE_CTRL 0xb4
#define RBUF_HFB_CTRL_V1 0x38
#define RBUF_HFB_FILTER_EN_SHIFT 16
#define RBUF_HFB_FILTER_EN_MASK 0xffff0000
#define RBUF_HFB_EN (1 << 0)
#define RBUF_HFB_256B (1 << 1)
#define RBUF_ACPI_EN (1 << 2)
#define RBUF_HFB_LEN_V1 0x3C
#define RBUF_FLTR_LEN_MASK 0xFF
#define RBUF_FLTR_LEN_SHIFT 8
#define TBUF_CTRL 0x00
#define TBUF_64B_EN (1 << 0)
#define TBUF_BP_MC 0x0C
#define TBUF_ENERGY_CTRL 0x14
#define TBUF_EEE_EN (1 << 0)
#define TBUF_PM_EN (1 << 1)
#define TBUF_CTRL_V1 0x80
#define TBUF_BP_MC_V1 0xA0
#define HFB_CTRL 0x00
#define HFB_FLT_ENABLE_V3PLUS 0x04
#define HFB_FLT_LEN_V2 0x04
#define HFB_FLT_LEN_V3PLUS 0x1C
/* uniMac intrl2 registers */
#define INTRL2_CPU_STAT 0x00
#define INTRL2_CPU_SET 0x04
#define INTRL2_CPU_CLEAR 0x08
#define INTRL2_CPU_MASK_STATUS 0x0C
#define INTRL2_CPU_MASK_SET 0x10
#define INTRL2_CPU_MASK_CLEAR 0x14
/* INTRL2 instance 0 definitions */
#define UMAC_IRQ_SCB (1 << 0)
#define UMAC_IRQ_EPHY (1 << 1)
#define UMAC_IRQ_PHY_DET_R (1 << 2)
#define UMAC_IRQ_PHY_DET_F (1 << 3)
#define UMAC_IRQ_LINK_UP (1 << 4)
#define UMAC_IRQ_LINK_DOWN (1 << 5)
#define UMAC_IRQ_LINK_EVENT (UMAC_IRQ_LINK_UP | UMAC_IRQ_LINK_DOWN)
#define UMAC_IRQ_UMAC (1 << 6)
#define UMAC_IRQ_UMAC_TSV (1 << 7)
#define UMAC_IRQ_TBUF_UNDERRUN (1 << 8)
#define UMAC_IRQ_RBUF_OVERFLOW (1 << 9)
#define UMAC_IRQ_HFB_SM (1 << 10)
#define UMAC_IRQ_HFB_MM (1 << 11)
#define UMAC_IRQ_MPD_R (1 << 12)
#define UMAC_IRQ_WAKE_EVENT (UMAC_IRQ_HFB_SM | UMAC_IRQ_HFB_MM | \
UMAC_IRQ_MPD_R)
#define UMAC_IRQ_RXDMA_MBDONE (1 << 13)
#define UMAC_IRQ_RXDMA_PDONE (1 << 14)
#define UMAC_IRQ_RXDMA_BDONE (1 << 15)
#define UMAC_IRQ_RXDMA_DONE UMAC_IRQ_RXDMA_MBDONE
#define UMAC_IRQ_TXDMA_MBDONE (1 << 16)
#define UMAC_IRQ_TXDMA_PDONE (1 << 17)
#define UMAC_IRQ_TXDMA_BDONE (1 << 18)
#define UMAC_IRQ_TXDMA_DONE UMAC_IRQ_TXDMA_MBDONE
/* Only valid for GENETv3+ */
#define UMAC_IRQ_MDIO_DONE (1 << 23)
#define UMAC_IRQ_MDIO_ERROR (1 << 24)
/* INTRL2 instance 1 definitions */
#define UMAC_IRQ1_TX_INTR_MASK 0xFFFF
#define UMAC_IRQ1_RX_INTR_MASK 0xFFFF
#define UMAC_IRQ1_RX_INTR_SHIFT 16
/* Register block offsets */
#define GENET_SYS_OFF 0x0000
#define GENET_GR_BRIDGE_OFF 0x0040
#define GENET_EXT_OFF 0x0080
#define GENET_INTRL2_0_OFF 0x0200
#define GENET_INTRL2_1_OFF 0x0240
#define GENET_RBUF_OFF 0x0300
#define GENET_UMAC_OFF 0x0800
/* SYS block offsets and register definitions */
#define SYS_REV_CTRL 0x00
#define SYS_PORT_CTRL 0x04
#define PORT_MODE_INT_EPHY 0
#define PORT_MODE_INT_GPHY 1
#define PORT_MODE_EXT_EPHY 2
#define PORT_MODE_EXT_GPHY 3
#define PORT_MODE_EXT_RVMII_25 (4 | BIT(4))
#define PORT_MODE_EXT_RVMII_50 4
#define LED_ACT_SOURCE_MAC (1 << 9)
#define SYS_RBUF_FLUSH_CTRL 0x08
#define SYS_TBUF_FLUSH_CTRL 0x0C
#define RBUF_FLUSH_CTRL_V1 0x04
/* Ext block register offsets and definitions */
#define EXT_EXT_PWR_MGMT 0x00
#define EXT_PWR_DOWN_BIAS (1 << 0)
#define EXT_PWR_DOWN_DLL (1 << 1)
#define EXT_PWR_DOWN_PHY (1 << 2)
#define EXT_PWR_DN_EN_LD (1 << 3)
#define EXT_ENERGY_DET (1 << 4)
#define EXT_IDDQ_FROM_PHY (1 << 5)
#define EXT_IDDQ_GLBL_PWR (1 << 7)
#define EXT_PHY_RESET (1 << 8)
#define EXT_ENERGY_DET_MASK (1 << 12)
#define EXT_PWR_DOWN_PHY_TX (1 << 16)
#define EXT_PWR_DOWN_PHY_RX (1 << 17)
#define EXT_PWR_DOWN_PHY_SD (1 << 18)
#define EXT_PWR_DOWN_PHY_RD (1 << 19)
#define EXT_PWR_DOWN_PHY_EN (1 << 20)
#define EXT_RGMII_OOB_CTRL 0x0C
#define RGMII_MODE_EN_V123 (1 << 0)
#define RGMII_LINK (1 << 4)
#define OOB_DISABLE (1 << 5)
#define RGMII_MODE_EN (1 << 6)
#define ID_MODE_DIS (1 << 16)
#define EXT_GPHY_CTRL 0x1C
#define EXT_CFG_IDDQ_BIAS (1 << 0)
#define EXT_CFG_PWR_DOWN (1 << 1)
#define EXT_CK25_DIS (1 << 4)
#define EXT_GPHY_RESET (1 << 5)
/* DMA rings size */
#define DMA_RING_SIZE (0x40)
#define DMA_RINGS_SIZE (DMA_RING_SIZE * (DESC_INDEX + 1))
/* DMA registers common definitions */
#define DMA_RW_POINTER_MASK 0x1FF
#define DMA_P_INDEX_DISCARD_CNT_MASK 0xFFFF
#define DMA_P_INDEX_DISCARD_CNT_SHIFT 16
#define DMA_BUFFER_DONE_CNT_MASK 0xFFFF
#define DMA_BUFFER_DONE_CNT_SHIFT 16
#define DMA_P_INDEX_MASK 0xFFFF
#define DMA_C_INDEX_MASK 0xFFFF
/* DMA ring size register */
#define DMA_RING_SIZE_MASK 0xFFFF
#define DMA_RING_SIZE_SHIFT 16
#define DMA_RING_BUFFER_SIZE_MASK 0xFFFF
/* DMA interrupt threshold register */
#define DMA_INTR_THRESHOLD_MASK 0x01FF
/* DMA XON/XOFF register */
#define DMA_XON_THREHOLD_MASK 0xFFFF
#define DMA_XOFF_THRESHOLD_MASK 0xFFFF
#define DMA_XOFF_THRESHOLD_SHIFT 16
/* DMA flow period register */
#define DMA_FLOW_PERIOD_MASK 0xFFFF
#define DMA_MAX_PKT_SIZE_MASK 0xFFFF
#define DMA_MAX_PKT_SIZE_SHIFT 16
/* DMA control register */
#define DMA_EN (1 << 0)
#define DMA_RING_BUF_EN_SHIFT 0x01
#define DMA_RING_BUF_EN_MASK 0xFFFF
#define DMA_TSB_SWAP_EN (1 << 20)
/* DMA status register */
#define DMA_DISABLED (1 << 0)
#define DMA_DESC_RAM_INIT_BUSY (1 << 1)
/* DMA SCB burst size register */
#define DMA_SCB_BURST_SIZE_MASK 0x1F
/* DMA activity vector register */
#define DMA_ACTIVITY_VECTOR_MASK 0x1FFFF
/* DMA backpressure mask register */
#define DMA_BACKPRESSURE_MASK 0x1FFFF
#define DMA_PFC_ENABLE (1 << 31)
/* DMA backpressure status register */
#define DMA_BACKPRESSURE_STATUS_MASK 0x1FFFF
/* DMA override register */
#define DMA_LITTLE_ENDIAN_MODE (1 << 0)
#define DMA_REGISTER_MODE (1 << 1)
/* DMA timeout register */
#define DMA_TIMEOUT_MASK 0xFFFF
#define DMA_TIMEOUT_VAL 5000 /* micro seconds */
/* TDMA rate limiting control register */
#define DMA_RATE_LIMIT_EN_MASK 0xFFFF
/* TDMA arbitration control register */
#define DMA_ARBITER_MODE_MASK 0x03
#define DMA_RING_BUF_PRIORITY_MASK 0x1F
#define DMA_RING_BUF_PRIORITY_SHIFT 5
#define DMA_PRIO_REG_INDEX(q) ((q) / 6)
#define DMA_PRIO_REG_SHIFT(q) (((q) % 6) * DMA_RING_BUF_PRIORITY_SHIFT)
#define DMA_RATE_ADJ_MASK 0xFF
/* Tx/Rx Dma Descriptor common bits*/
#define DMA_BUFLENGTH_MASK 0x0fff
#define DMA_BUFLENGTH_SHIFT 16
#define DMA_OWN 0x8000
#define DMA_EOP 0x4000
#define DMA_SOP 0x2000
#define DMA_WRAP 0x1000
/* Tx specific Dma descriptor bits */
#define DMA_TX_UNDERRUN 0x0200
#define DMA_TX_APPEND_CRC 0x0040
#define DMA_TX_OW_CRC 0x0020
#define DMA_TX_DO_CSUM 0x0010
#define DMA_TX_QTAG_SHIFT 7
/* Rx Specific Dma descriptor bits */
#define DMA_RX_CHK_V3PLUS 0x8000
#define DMA_RX_CHK_V12 0x1000
#define DMA_RX_BRDCAST 0x0040
#define DMA_RX_MULT 0x0020
#define DMA_RX_LG 0x0010
#define DMA_RX_NO 0x0008
#define DMA_RX_RXER 0x0004
#define DMA_RX_CRC_ERROR 0x0002
#define DMA_RX_OV 0x0001
#define DMA_RX_FI_MASK 0x001F
#define DMA_RX_FI_SHIFT 0x0007
#define DMA_DESC_ALLOC_MASK 0x00FF
#define DMA_ARBITER_RR 0x00
#define DMA_ARBITER_WRR 0x01
#define DMA_ARBITER_SP 0x02
struct enet_cb {
struct sk_buff *skb;
void __iomem *bd_addr;
DEFINE_DMA_UNMAP_ADDR(dma_addr);
DEFINE_DMA_UNMAP_LEN(dma_len);
};
/* power management mode */
enum bcmgenet_power_mode {
GENET_POWER_CABLE_SENSE = 0,
GENET_POWER_PASSIVE,
GENET_POWER_WOL_MAGIC,
};
struct bcmgenet_priv;
/* We support both runtime GENET detection and compile-time
* to optimize code-paths for a given hardware
*/
enum bcmgenet_version {
GENET_V1 = 1,
GENET_V2,
GENET_V3,
GENET_V4,
GENET_V5
};
#define GENET_IS_V1(p) ((p)->version == GENET_V1)
#define GENET_IS_V2(p) ((p)->version == GENET_V2)
#define GENET_IS_V3(p) ((p)->version == GENET_V3)
#define GENET_IS_V4(p) ((p)->version == GENET_V4)
#define GENET_IS_V5(p) ((p)->version == GENET_V5)
/* Hardware flags */
#define GENET_HAS_40BITS (1 << 0)
#define GENET_HAS_EXT (1 << 1)
#define GENET_HAS_MDIO_INTR (1 << 2)
#define GENET_HAS_MOCA_LINK_DET (1 << 3)
/* BCMGENET hardware parameters, keep this structure nicely aligned
* since it is going to be used in hot paths
*/
struct bcmgenet_hw_params {
u8 tx_queues;
u8 tx_bds_per_q;
u8 rx_queues;
u8 rx_bds_per_q;
u8 bp_in_en_shift;
u32 bp_in_mask;
u8 hfb_filter_cnt;
u8 hfb_filter_size;
u8 qtag_mask;
u16 tbuf_offset;
u32 hfb_offset;
u32 hfb_reg_offset;
u32 rdma_offset;
u32 tdma_offset;
u32 words_per_bd;
u32 flags;
};
struct bcmgenet_skb_cb {
struct enet_cb *first_cb; /* First control block of SKB */
struct enet_cb *last_cb; /* Last control block of SKB */
unsigned int bytes_sent; /* bytes on the wire (no TSB) */
};
#define GENET_CB(skb) ((struct bcmgenet_skb_cb *)((skb)->cb))
struct bcmgenet_tx_ring {
spinlock_t lock; /* ring lock */
struct napi_struct napi; /* NAPI per tx queue */
unsigned long packets;
unsigned long bytes;
unsigned int index; /* ring index */
unsigned int queue; /* queue index */
struct enet_cb *cbs; /* tx ring buffer control block*/
unsigned int size; /* size of each tx ring */
unsigned int clean_ptr; /* Tx ring clean pointer */
unsigned int c_index; /* last consumer index of each ring*/
unsigned int free_bds; /* # of free bds for each ring */
unsigned int write_ptr; /* Tx ring write pointer SW copy */
unsigned int prod_index; /* Tx ring producer index SW copy */
unsigned int cb_ptr; /* Tx ring initial CB ptr */
unsigned int end_ptr; /* Tx ring end CB ptr */
void (*int_enable)(struct bcmgenet_tx_ring *);
void (*int_disable)(struct bcmgenet_tx_ring *);
struct bcmgenet_priv *priv;
};
struct bcmgenet_net_dim {
u16 use_dim;
u16 event_ctr;
unsigned long packets;
unsigned long bytes;
struct dim dim;
};
struct bcmgenet_rx_ring {
struct napi_struct napi; /* Rx NAPI struct */
unsigned long bytes;
unsigned long packets;
unsigned long errors;
unsigned long dropped;
unsigned int index; /* Rx ring index */
struct enet_cb *cbs; /* Rx ring buffer control block */
unsigned int size; /* Rx ring size */
unsigned int c_index; /* Rx last consumer index */
unsigned int read_ptr; /* Rx ring read pointer */
unsigned int cb_ptr; /* Rx ring initial CB ptr */
unsigned int end_ptr; /* Rx ring end CB ptr */
unsigned int old_discards;
struct bcmgenet_net_dim dim;
u32 rx_max_coalesced_frames;
u32 rx_coalesce_usecs;
void (*int_enable)(struct bcmgenet_rx_ring *);
void (*int_disable)(struct bcmgenet_rx_ring *);
struct bcmgenet_priv *priv;
};
enum bcmgenet_rxnfc_state {
BCMGENET_RXNFC_STATE_UNUSED = 0,
BCMGENET_RXNFC_STATE_DISABLED,
BCMGENET_RXNFC_STATE_ENABLED
};
struct bcmgenet_rxnfc_rule {
struct list_head list;
struct ethtool_rx_flow_spec fs;
enum bcmgenet_rxnfc_state state;
};
/* device context */
struct bcmgenet_priv {
void __iomem *base;
enum bcmgenet_version version;
struct net_device *dev;
/* transmit variables */
void __iomem *tx_bds;
struct enet_cb *tx_cbs;
unsigned int num_tx_bds;
struct bcmgenet_tx_ring tx_rings[DESC_INDEX + 1];
/* receive variables */
void __iomem *rx_bds;
struct enet_cb *rx_cbs;
unsigned int num_rx_bds;
unsigned int rx_buf_len;
struct bcmgenet_rxnfc_rule rxnfc_rules[MAX_NUM_OF_FS_RULES];
struct list_head rxnfc_list;
struct bcmgenet_rx_ring rx_rings[DESC_INDEX + 1];
/* other misc variables */
struct bcmgenet_hw_params *hw_params;
/* MDIO bus variables */
wait_queue_head_t wq;
bool internal_phy;
struct device_node *phy_dn;
struct device_node *mdio_dn;
struct mii_bus *mii_bus;
u16 gphy_rev;
struct clk *clk_eee;
bool clk_eee_enabled;
/* PHY device variables */
int old_link;
int old_speed;
int old_duplex;
int old_pause;
phy_interface_t phy_interface;
int phy_addr;
int ext_phy;
/* Interrupt variables */
struct work_struct bcmgenet_irq_work;
int irq0;
int irq1;
int wol_irq;
bool wol_irq_disabled;
/* shared status */
spinlock_t lock;
unsigned int irq0_stat;
/* HW descriptors/checksum variables */
bool crc_fwd_en;
u32 dma_max_burst_length;
u32 msg_enable;
struct clk *clk;
struct platform_device *pdev;
struct platform_device *mii_pdev;
/* WOL */
struct clk *clk_wol;
u32 wolopts;
u8 sopass[SOPASS_MAX];
bool wol_active;
struct bcmgenet_mib_counters mib;
struct ethtool_eee eee;
/* EtherCAT device variables */
ec_device_t *ecdev;
};
#define GENET_IO_MACRO(name, offset) \
static inline u32 bcmgenet_##name##_readl(struct bcmgenet_priv *priv, \
u32 off) \
{ \
/* MIPS chips strapped for BE will automagically configure the \
* peripheral registers for CPU-native byte order. \
*/ \
if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) \
return __raw_readl(priv->base + offset + off); \
else \
return readl_relaxed(priv->base + offset + off); \
} \
static inline void bcmgenet_##name##_writel(struct bcmgenet_priv *priv, \
u32 val, u32 off) \
{ \
if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) \
__raw_writel(val, priv->base + offset + off); \
else \
writel_relaxed(val, priv->base + offset + off); \
}
GENET_IO_MACRO(ext, GENET_EXT_OFF);
GENET_IO_MACRO(umac, GENET_UMAC_OFF);
GENET_IO_MACRO(sys, GENET_SYS_OFF);
/* interrupt l2 registers accessors */
GENET_IO_MACRO(intrl2_0, GENET_INTRL2_0_OFF);
GENET_IO_MACRO(intrl2_1, GENET_INTRL2_1_OFF);
/* HFB register accessors */
GENET_IO_MACRO(hfb, priv->hw_params->hfb_offset);
/* GENET v2+ HFB control and filter len helpers */
GENET_IO_MACRO(hfb_reg, priv->hw_params->hfb_reg_offset);
/* RBUF register accessors */
GENET_IO_MACRO(rbuf, GENET_RBUF_OFF);
/* MDIO routines */
int bcmgenet_mii_init(struct net_device *dev);
int bcmgenet_mii_config(struct net_device *dev, bool init);
int bcmgenet_mii_probe(struct net_device *dev);
void bcmgenet_mii_exit(struct net_device *dev);
void bcmgenet_phy_power_set(struct net_device *dev, bool enable);
void bcmgenet_mii_setup(struct net_device *dev);
/* Wake-on-LAN routines */
void bcmgenet_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol);
int bcmgenet_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol);
int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv,
enum bcmgenet_power_mode mode);
void bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv,
enum bcmgenet_power_mode mode);
#endif /* __BCMGENET_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,759 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2014-2020 Broadcom
*/
#ifndef __BCMGENET_H__
#define __BCMGENET_H__
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/spinlock.h>
#include <linux/clk.h>
#include <linux/mii.h>
#include <linux/if_vlan.h>
#include <linux/phy.h>
#include <linux/dim.h>
#include <linux/ethtool.h>
/* total number of Buffer Descriptors, same for Rx/Tx */
#define TOTAL_DESC 256
/* which ring is descriptor based */
#define DESC_INDEX 16
/* Body(1500) + EH_SIZE(14) + VLANTAG(4) + BRCMTAG(6) + FCS(4) = 1528.
* 1536 is multiple of 256 bytes
*/
#define ENET_BRCM_TAG_LEN 6
#define ENET_PAD 8
#define ENET_MAX_MTU_SIZE (ETH_DATA_LEN + ETH_HLEN + VLAN_HLEN + \
ENET_BRCM_TAG_LEN + ETH_FCS_LEN + ENET_PAD)
#define DMA_MAX_BURST_LENGTH 0x10
/* misc. configuration */
#define MAX_NUM_OF_FS_RULES 16
#define CLEAR_ALL_HFB 0xFF
#define DMA_FC_THRESH_HI (TOTAL_DESC >> 4)
#define DMA_FC_THRESH_LO 5
/* 64B receive/transmit status block */
struct status_64 {
u32 length_status; /* length and peripheral status */
u32 ext_status; /* Extended status*/
u32 rx_csum; /* partial rx checksum */
u32 unused1[9]; /* unused */
u32 tx_csum_info; /* Tx checksum info. */
u32 unused2[3]; /* unused */
};
/* Rx status bits */
#define STATUS_RX_EXT_MASK 0x1FFFFF
#define STATUS_RX_CSUM_MASK 0xFFFF
#define STATUS_RX_CSUM_OK 0x10000
#define STATUS_RX_CSUM_FR 0x20000
#define STATUS_RX_PROTO_TCP 0
#define STATUS_RX_PROTO_UDP 1
#define STATUS_RX_PROTO_ICMP 2
#define STATUS_RX_PROTO_OTHER 3
#define STATUS_RX_PROTO_MASK 3
#define STATUS_RX_PROTO_SHIFT 18
#define STATUS_FILTER_INDEX_MASK 0xFFFF
/* Tx status bits */
#define STATUS_TX_CSUM_START_MASK 0X7FFF
#define STATUS_TX_CSUM_START_SHIFT 16
#define STATUS_TX_CSUM_PROTO_UDP 0x8000
#define STATUS_TX_CSUM_OFFSET_MASK 0x7FFF
#define STATUS_TX_CSUM_LV 0x80000000
/* DMA Descriptor */
#define DMA_DESC_LENGTH_STATUS 0x00 /* in bytes of data in buffer */
#define DMA_DESC_ADDRESS_LO 0x04 /* lower bits of PA */
#define DMA_DESC_ADDRESS_HI 0x08 /* upper 32 bits of PA, GENETv4+ */
/* Rx/Tx common counter group */
struct bcmgenet_pkt_counters {
u32 cnt_64; /* RO Received/Transmited 64 bytes packet */
u32 cnt_127; /* RO Rx/Tx 127 bytes packet */
u32 cnt_255; /* RO Rx/Tx 65-255 bytes packet */
u32 cnt_511; /* RO Rx/Tx 256-511 bytes packet */
u32 cnt_1023; /* RO Rx/Tx 512-1023 bytes packet */
u32 cnt_1518; /* RO Rx/Tx 1024-1518 bytes packet */
u32 cnt_mgv; /* RO Rx/Tx 1519-1522 good VLAN packet */
u32 cnt_2047; /* RO Rx/Tx 1522-2047 bytes packet*/
u32 cnt_4095; /* RO Rx/Tx 2048-4095 bytes packet*/
u32 cnt_9216; /* RO Rx/Tx 4096-9216 bytes packet*/
};
/* RSV, Receive Status Vector */
struct bcmgenet_rx_counters {
struct bcmgenet_pkt_counters pkt_cnt;
u32 pkt; /* RO (0x428) Received pkt count*/
u32 bytes; /* RO Received byte count */
u32 mca; /* RO # of Received multicast pkt */
u32 bca; /* RO # of Receive broadcast pkt */
u32 fcs; /* RO # of Received FCS error */
u32 cf; /* RO # of Received control frame pkt*/
u32 pf; /* RO # of Received pause frame pkt */
u32 uo; /* RO # of unknown op code pkt */
u32 aln; /* RO # of alignment error count */
u32 flr; /* RO # of frame length out of range count */
u32 cde; /* RO # of code error pkt */
u32 fcr; /* RO # of carrier sense error pkt */
u32 ovr; /* RO # of oversize pkt*/
u32 jbr; /* RO # of jabber count */
u32 mtue; /* RO # of MTU error pkt*/
u32 pok; /* RO # of Received good pkt */
u32 uc; /* RO # of unicast pkt */
u32 ppp; /* RO # of PPP pkt */
u32 rcrc; /* RO (0x470),# of CRC match pkt */
};
/* TSV, Transmit Status Vector */
struct bcmgenet_tx_counters {
struct bcmgenet_pkt_counters pkt_cnt;
u32 pkts; /* RO (0x4a8) Transmited pkt */
u32 mca; /* RO # of xmited multicast pkt */
u32 bca; /* RO # of xmited broadcast pkt */
u32 pf; /* RO # of xmited pause frame count */
u32 cf; /* RO # of xmited control frame count */
u32 fcs; /* RO # of xmited FCS error count */
u32 ovr; /* RO # of xmited oversize pkt */
u32 drf; /* RO # of xmited deferral pkt */
u32 edf; /* RO # of xmited Excessive deferral pkt*/
u32 scl; /* RO # of xmited single collision pkt */
u32 mcl; /* RO # of xmited multiple collision pkt*/
u32 lcl; /* RO # of xmited late collision pkt */
u32 ecl; /* RO # of xmited excessive collision pkt*/
u32 frg; /* RO # of xmited fragments pkt*/
u32 ncl; /* RO # of xmited total collision count */
u32 jbr; /* RO # of xmited jabber count*/
u32 bytes; /* RO # of xmited byte count */
u32 pok; /* RO # of xmited good pkt */
u32 uc; /* RO (0x0x4f0)# of xmited unitcast pkt */
};
struct bcmgenet_mib_counters {
struct bcmgenet_rx_counters rx;
struct bcmgenet_tx_counters tx;
u32 rx_runt_cnt;
u32 rx_runt_fcs;
u32 rx_runt_fcs_align;
u32 rx_runt_bytes;
u32 rbuf_ovflow_cnt;
u32 rbuf_err_cnt;
u32 mdf_err_cnt;
u32 alloc_rx_buff_failed;
u32 rx_dma_failed;
u32 tx_dma_failed;
u32 tx_realloc_tsb;
u32 tx_realloc_tsb_failed;
};
#define UMAC_HD_BKP_CTRL 0x004
#define HD_FC_EN (1 << 0)
#define HD_FC_BKOFF_OK (1 << 1)
#define IPG_CONFIG_RX_SHIFT 2
#define IPG_CONFIG_RX_MASK 0x1F
#define UMAC_CMD 0x008
#define CMD_TX_EN (1 << 0)
#define CMD_RX_EN (1 << 1)
#define UMAC_SPEED_10 0
#define UMAC_SPEED_100 1
#define UMAC_SPEED_1000 2
#define UMAC_SPEED_2500 3
#define CMD_SPEED_SHIFT 2
#define CMD_SPEED_MASK 3
#define CMD_PROMISC (1 << 4)
#define CMD_PAD_EN (1 << 5)
#define CMD_CRC_FWD (1 << 6)
#define CMD_PAUSE_FWD (1 << 7)
#define CMD_RX_PAUSE_IGNORE (1 << 8)
#define CMD_TX_ADDR_INS (1 << 9)
#define CMD_HD_EN (1 << 10)
#define CMD_SW_RESET (1 << 13)
#define CMD_LCL_LOOP_EN (1 << 15)
#define CMD_AUTO_CONFIG (1 << 22)
#define CMD_CNTL_FRM_EN (1 << 23)
#define CMD_NO_LEN_CHK (1 << 24)
#define CMD_RMT_LOOP_EN (1 << 25)
#define CMD_PRBL_EN (1 << 27)
#define CMD_TX_PAUSE_IGNORE (1 << 28)
#define CMD_TX_RX_EN (1 << 29)
#define CMD_RUNT_FILTER_DIS (1 << 30)
#define UMAC_MAC0 0x00C
#define UMAC_MAC1 0x010
#define UMAC_MAX_FRAME_LEN 0x014
#define UMAC_MODE 0x44
#define MODE_LINK_STATUS (1 << 5)
#define UMAC_EEE_CTRL 0x064
#define EN_LPI_RX_PAUSE (1 << 0)
#define EN_LPI_TX_PFC (1 << 1)
#define EN_LPI_TX_PAUSE (1 << 2)
#define EEE_EN (1 << 3)
#define RX_FIFO_CHECK (1 << 4)
#define EEE_TX_CLK_DIS (1 << 5)
#define DIS_EEE_10M (1 << 6)
#define LP_IDLE_PREDICTION_MODE (1 << 7)
#define UMAC_EEE_LPI_TIMER 0x068
#define UMAC_EEE_WAKE_TIMER 0x06C
#define UMAC_EEE_REF_COUNT 0x070
#define EEE_REFERENCE_COUNT_MASK 0xffff
#define UMAC_TX_FLUSH 0x334
#define UMAC_MIB_START 0x400
#define UMAC_MDIO_CMD 0x614
#define MDIO_START_BUSY (1 << 29)
#define MDIO_READ_FAIL (1 << 28)
#define MDIO_RD (2 << 26)
#define MDIO_WR (1 << 26)
#define MDIO_PMD_SHIFT 21
#define MDIO_PMD_MASK 0x1F
#define MDIO_REG_SHIFT 16
#define MDIO_REG_MASK 0x1F
#define UMAC_RBUF_OVFL_CNT_V1 0x61C
#define RBUF_OVFL_CNT_V2 0x80
#define RBUF_OVFL_CNT_V3PLUS 0x94
#define UMAC_MPD_CTRL 0x620
#define MPD_EN (1 << 0)
#define MPD_PW_EN (1 << 27)
#define MPD_MSEQ_LEN_SHIFT 16
#define MPD_MSEQ_LEN_MASK 0xFF
#define UMAC_MPD_PW_MS 0x624
#define UMAC_MPD_PW_LS 0x628
#define UMAC_RBUF_ERR_CNT_V1 0x634
#define RBUF_ERR_CNT_V2 0x84
#define RBUF_ERR_CNT_V3PLUS 0x98
#define UMAC_MDF_ERR_CNT 0x638
#define UMAC_MDF_CTRL 0x650
#define UMAC_MDF_ADDR 0x654
#define UMAC_MIB_CTRL 0x580
#define MIB_RESET_RX (1 << 0)
#define MIB_RESET_RUNT (1 << 1)
#define MIB_RESET_TX (1 << 2)
#define RBUF_CTRL 0x00
#define RBUF_64B_EN (1 << 0)
#define RBUF_ALIGN_2B (1 << 1)
#define RBUF_BAD_DIS (1 << 2)
#define RBUF_STATUS 0x0C
#define RBUF_STATUS_WOL (1 << 0)
#define RBUF_STATUS_MPD_INTR_ACTIVE (1 << 1)
#define RBUF_STATUS_ACPI_INTR_ACTIVE (1 << 2)
#define RBUF_CHK_CTRL 0x14
#define RBUF_RXCHK_EN (1 << 0)
#define RBUF_SKIP_FCS (1 << 4)
#define RBUF_L3_PARSE_DIS (1 << 5)
#define RBUF_ENERGY_CTRL 0x9c
#define RBUF_EEE_EN (1 << 0)
#define RBUF_PM_EN (1 << 1)
#define RBUF_TBUF_SIZE_CTRL 0xb4
#define RBUF_HFB_CTRL_V1 0x38
#define RBUF_HFB_FILTER_EN_SHIFT 16
#define RBUF_HFB_FILTER_EN_MASK 0xffff0000
#define RBUF_HFB_EN (1 << 0)
#define RBUF_HFB_256B (1 << 1)
#define RBUF_ACPI_EN (1 << 2)
#define RBUF_HFB_LEN_V1 0x3C
#define RBUF_FLTR_LEN_MASK 0xFF
#define RBUF_FLTR_LEN_SHIFT 8
#define TBUF_CTRL 0x00
#define TBUF_64B_EN (1 << 0)
#define TBUF_BP_MC 0x0C
#define TBUF_ENERGY_CTRL 0x14
#define TBUF_EEE_EN (1 << 0)
#define TBUF_PM_EN (1 << 1)
#define TBUF_CTRL_V1 0x80
#define TBUF_BP_MC_V1 0xA0
#define HFB_CTRL 0x00
#define HFB_FLT_ENABLE_V3PLUS 0x04
#define HFB_FLT_LEN_V2 0x04
#define HFB_FLT_LEN_V3PLUS 0x1C
/* uniMac intrl2 registers */
#define INTRL2_CPU_STAT 0x00
#define INTRL2_CPU_SET 0x04
#define INTRL2_CPU_CLEAR 0x08
#define INTRL2_CPU_MASK_STATUS 0x0C
#define INTRL2_CPU_MASK_SET 0x10
#define INTRL2_CPU_MASK_CLEAR 0x14
/* INTRL2 instance 0 definitions */
#define UMAC_IRQ_SCB (1 << 0)
#define UMAC_IRQ_EPHY (1 << 1)
#define UMAC_IRQ_PHY_DET_R (1 << 2)
#define UMAC_IRQ_PHY_DET_F (1 << 3)
#define UMAC_IRQ_LINK_UP (1 << 4)
#define UMAC_IRQ_LINK_DOWN (1 << 5)
#define UMAC_IRQ_LINK_EVENT (UMAC_IRQ_LINK_UP | UMAC_IRQ_LINK_DOWN)
#define UMAC_IRQ_UMAC (1 << 6)
#define UMAC_IRQ_UMAC_TSV (1 << 7)
#define UMAC_IRQ_TBUF_UNDERRUN (1 << 8)
#define UMAC_IRQ_RBUF_OVERFLOW (1 << 9)
#define UMAC_IRQ_HFB_SM (1 << 10)
#define UMAC_IRQ_HFB_MM (1 << 11)
#define UMAC_IRQ_MPD_R (1 << 12)
#define UMAC_IRQ_WAKE_EVENT (UMAC_IRQ_HFB_SM | UMAC_IRQ_HFB_MM | \
UMAC_IRQ_MPD_R)
#define UMAC_IRQ_RXDMA_MBDONE (1 << 13)
#define UMAC_IRQ_RXDMA_PDONE (1 << 14)
#define UMAC_IRQ_RXDMA_BDONE (1 << 15)
#define UMAC_IRQ_RXDMA_DONE UMAC_IRQ_RXDMA_MBDONE
#define UMAC_IRQ_TXDMA_MBDONE (1 << 16)
#define UMAC_IRQ_TXDMA_PDONE (1 << 17)
#define UMAC_IRQ_TXDMA_BDONE (1 << 18)
#define UMAC_IRQ_TXDMA_DONE UMAC_IRQ_TXDMA_MBDONE
/* Only valid for GENETv3+ */
#define UMAC_IRQ_MDIO_DONE (1 << 23)
#define UMAC_IRQ_MDIO_ERROR (1 << 24)
/* INTRL2 instance 1 definitions */
#define UMAC_IRQ1_TX_INTR_MASK 0xFFFF
#define UMAC_IRQ1_RX_INTR_MASK 0xFFFF
#define UMAC_IRQ1_RX_INTR_SHIFT 16
/* Register block offsets */
#define GENET_SYS_OFF 0x0000
#define GENET_GR_BRIDGE_OFF 0x0040
#define GENET_EXT_OFF 0x0080
#define GENET_INTRL2_0_OFF 0x0200
#define GENET_INTRL2_1_OFF 0x0240
#define GENET_RBUF_OFF 0x0300
#define GENET_UMAC_OFF 0x0800
/* SYS block offsets and register definitions */
#define SYS_REV_CTRL 0x00
#define SYS_PORT_CTRL 0x04
#define PORT_MODE_INT_EPHY 0
#define PORT_MODE_INT_GPHY 1
#define PORT_MODE_EXT_EPHY 2
#define PORT_MODE_EXT_GPHY 3
#define PORT_MODE_EXT_RVMII_25 (4 | BIT(4))
#define PORT_MODE_EXT_RVMII_50 4
#define LED_ACT_SOURCE_MAC (1 << 9)
#define SYS_RBUF_FLUSH_CTRL 0x08
#define SYS_TBUF_FLUSH_CTRL 0x0C
#define RBUF_FLUSH_CTRL_V1 0x04
/* Ext block register offsets and definitions */
#define EXT_EXT_PWR_MGMT 0x00
#define EXT_PWR_DOWN_BIAS (1 << 0)
#define EXT_PWR_DOWN_DLL (1 << 1)
#define EXT_PWR_DOWN_PHY (1 << 2)
#define EXT_PWR_DN_EN_LD (1 << 3)
#define EXT_ENERGY_DET (1 << 4)
#define EXT_IDDQ_FROM_PHY (1 << 5)
#define EXT_IDDQ_GLBL_PWR (1 << 7)
#define EXT_PHY_RESET (1 << 8)
#define EXT_ENERGY_DET_MASK (1 << 12)
#define EXT_PWR_DOWN_PHY_TX (1 << 16)
#define EXT_PWR_DOWN_PHY_RX (1 << 17)
#define EXT_PWR_DOWN_PHY_SD (1 << 18)
#define EXT_PWR_DOWN_PHY_RD (1 << 19)
#define EXT_PWR_DOWN_PHY_EN (1 << 20)
#define EXT_RGMII_OOB_CTRL 0x0C
#define RGMII_MODE_EN_V123 (1 << 0)
#define RGMII_LINK (1 << 4)
#define OOB_DISABLE (1 << 5)
#define RGMII_MODE_EN (1 << 6)
#define ID_MODE_DIS (1 << 16)
#define EXT_GPHY_CTRL 0x1C
#define EXT_CFG_IDDQ_BIAS (1 << 0)
#define EXT_CFG_PWR_DOWN (1 << 1)
#define EXT_CK25_DIS (1 << 4)
#define EXT_GPHY_RESET (1 << 5)
/* DMA rings size */
#define DMA_RING_SIZE (0x40)
#define DMA_RINGS_SIZE (DMA_RING_SIZE * (DESC_INDEX + 1))
/* DMA registers common definitions */
#define DMA_RW_POINTER_MASK 0x1FF
#define DMA_P_INDEX_DISCARD_CNT_MASK 0xFFFF
#define DMA_P_INDEX_DISCARD_CNT_SHIFT 16
#define DMA_BUFFER_DONE_CNT_MASK 0xFFFF
#define DMA_BUFFER_DONE_CNT_SHIFT 16
#define DMA_P_INDEX_MASK 0xFFFF
#define DMA_C_INDEX_MASK 0xFFFF
/* DMA ring size register */
#define DMA_RING_SIZE_MASK 0xFFFF
#define DMA_RING_SIZE_SHIFT 16
#define DMA_RING_BUFFER_SIZE_MASK 0xFFFF
/* DMA interrupt threshold register */
#define DMA_INTR_THRESHOLD_MASK 0x01FF
/* DMA XON/XOFF register */
#define DMA_XON_THREHOLD_MASK 0xFFFF
#define DMA_XOFF_THRESHOLD_MASK 0xFFFF
#define DMA_XOFF_THRESHOLD_SHIFT 16
/* DMA flow period register */
#define DMA_FLOW_PERIOD_MASK 0xFFFF
#define DMA_MAX_PKT_SIZE_MASK 0xFFFF
#define DMA_MAX_PKT_SIZE_SHIFT 16
/* DMA control register */
#define DMA_EN (1 << 0)
#define DMA_RING_BUF_EN_SHIFT 0x01
#define DMA_RING_BUF_EN_MASK 0xFFFF
#define DMA_TSB_SWAP_EN (1 << 20)
/* DMA status register */
#define DMA_DISABLED (1 << 0)
#define DMA_DESC_RAM_INIT_BUSY (1 << 1)
/* DMA SCB burst size register */
#define DMA_SCB_BURST_SIZE_MASK 0x1F
/* DMA activity vector register */
#define DMA_ACTIVITY_VECTOR_MASK 0x1FFFF
/* DMA backpressure mask register */
#define DMA_BACKPRESSURE_MASK 0x1FFFF
#define DMA_PFC_ENABLE (1 << 31)
/* DMA backpressure status register */
#define DMA_BACKPRESSURE_STATUS_MASK 0x1FFFF
/* DMA override register */
#define DMA_LITTLE_ENDIAN_MODE (1 << 0)
#define DMA_REGISTER_MODE (1 << 1)
/* DMA timeout register */
#define DMA_TIMEOUT_MASK 0xFFFF
#define DMA_TIMEOUT_VAL 5000 /* micro seconds */
/* TDMA rate limiting control register */
#define DMA_RATE_LIMIT_EN_MASK 0xFFFF
/* TDMA arbitration control register */
#define DMA_ARBITER_MODE_MASK 0x03
#define DMA_RING_BUF_PRIORITY_MASK 0x1F
#define DMA_RING_BUF_PRIORITY_SHIFT 5
#define DMA_PRIO_REG_INDEX(q) ((q) / 6)
#define DMA_PRIO_REG_SHIFT(q) (((q) % 6) * DMA_RING_BUF_PRIORITY_SHIFT)
#define DMA_RATE_ADJ_MASK 0xFF
/* Tx/Rx Dma Descriptor common bits*/
#define DMA_BUFLENGTH_MASK 0x0fff
#define DMA_BUFLENGTH_SHIFT 16
#define DMA_OWN 0x8000
#define DMA_EOP 0x4000
#define DMA_SOP 0x2000
#define DMA_WRAP 0x1000
/* Tx specific Dma descriptor bits */
#define DMA_TX_UNDERRUN 0x0200
#define DMA_TX_APPEND_CRC 0x0040
#define DMA_TX_OW_CRC 0x0020
#define DMA_TX_DO_CSUM 0x0010
#define DMA_TX_QTAG_SHIFT 7
/* Rx Specific Dma descriptor bits */
#define DMA_RX_CHK_V3PLUS 0x8000
#define DMA_RX_CHK_V12 0x1000
#define DMA_RX_BRDCAST 0x0040
#define DMA_RX_MULT 0x0020
#define DMA_RX_LG 0x0010
#define DMA_RX_NO 0x0008
#define DMA_RX_RXER 0x0004
#define DMA_RX_CRC_ERROR 0x0002
#define DMA_RX_OV 0x0001
#define DMA_RX_FI_MASK 0x001F
#define DMA_RX_FI_SHIFT 0x0007
#define DMA_DESC_ALLOC_MASK 0x00FF
#define DMA_ARBITER_RR 0x00
#define DMA_ARBITER_WRR 0x01
#define DMA_ARBITER_SP 0x02
struct enet_cb {
struct sk_buff *skb;
void __iomem *bd_addr;
DEFINE_DMA_UNMAP_ADDR(dma_addr);
DEFINE_DMA_UNMAP_LEN(dma_len);
};
/* power management mode */
enum bcmgenet_power_mode {
GENET_POWER_CABLE_SENSE = 0,
GENET_POWER_PASSIVE,
GENET_POWER_WOL_MAGIC,
};
struct bcmgenet_priv;
/* We support both runtime GENET detection and compile-time
* to optimize code-paths for a given hardware
*/
enum bcmgenet_version {
GENET_V1 = 1,
GENET_V2,
GENET_V3,
GENET_V4,
GENET_V5
};
#define GENET_IS_V1(p) ((p)->version == GENET_V1)
#define GENET_IS_V2(p) ((p)->version == GENET_V2)
#define GENET_IS_V3(p) ((p)->version == GENET_V3)
#define GENET_IS_V4(p) ((p)->version == GENET_V4)
#define GENET_IS_V5(p) ((p)->version == GENET_V5)
/* Hardware flags */
#define GENET_HAS_40BITS (1 << 0)
#define GENET_HAS_EXT (1 << 1)
#define GENET_HAS_MDIO_INTR (1 << 2)
#define GENET_HAS_MOCA_LINK_DET (1 << 3)
/* BCMGENET hardware parameters, keep this structure nicely aligned
* since it is going to be used in hot paths
*/
struct bcmgenet_hw_params {
u8 tx_queues;
u8 tx_bds_per_q;
u8 rx_queues;
u8 rx_bds_per_q;
u8 bp_in_en_shift;
u32 bp_in_mask;
u8 hfb_filter_cnt;
u8 hfb_filter_size;
u8 qtag_mask;
u16 tbuf_offset;
u32 hfb_offset;
u32 hfb_reg_offset;
u32 rdma_offset;
u32 tdma_offset;
u32 words_per_bd;
u32 flags;
};
struct bcmgenet_skb_cb {
struct enet_cb *first_cb; /* First control block of SKB */
struct enet_cb *last_cb; /* Last control block of SKB */
unsigned int bytes_sent; /* bytes on the wire (no TSB) */
};
#define GENET_CB(skb) ((struct bcmgenet_skb_cb *)((skb)->cb))
struct bcmgenet_tx_ring {
spinlock_t lock; /* ring lock */
struct napi_struct napi; /* NAPI per tx queue */
unsigned long packets;
unsigned long bytes;
unsigned int index; /* ring index */
unsigned int queue; /* queue index */
struct enet_cb *cbs; /* tx ring buffer control block*/
unsigned int size; /* size of each tx ring */
unsigned int clean_ptr; /* Tx ring clean pointer */
unsigned int c_index; /* last consumer index of each ring*/
unsigned int free_bds; /* # of free bds for each ring */
unsigned int write_ptr; /* Tx ring write pointer SW copy */
unsigned int prod_index; /* Tx ring producer index SW copy */
unsigned int cb_ptr; /* Tx ring initial CB ptr */
unsigned int end_ptr; /* Tx ring end CB ptr */
void (*int_enable)(struct bcmgenet_tx_ring *);
void (*int_disable)(struct bcmgenet_tx_ring *);
struct bcmgenet_priv *priv;
};
struct bcmgenet_net_dim {
u16 use_dim;
u16 event_ctr;
unsigned long packets;
unsigned long bytes;
struct dim dim;
};
struct bcmgenet_rx_ring {
struct napi_struct napi; /* Rx NAPI struct */
unsigned long bytes;
unsigned long packets;
unsigned long errors;
unsigned long dropped;
unsigned int index; /* Rx ring index */
struct enet_cb *cbs; /* Rx ring buffer control block */
unsigned int size; /* Rx ring size */
unsigned int c_index; /* Rx last consumer index */
unsigned int read_ptr; /* Rx ring read pointer */
unsigned int cb_ptr; /* Rx ring initial CB ptr */
unsigned int end_ptr; /* Rx ring end CB ptr */
unsigned int old_discards;
struct bcmgenet_net_dim dim;
u32 rx_max_coalesced_frames;
u32 rx_coalesce_usecs;
void (*int_enable)(struct bcmgenet_rx_ring *);
void (*int_disable)(struct bcmgenet_rx_ring *);
struct bcmgenet_priv *priv;
};
enum bcmgenet_rxnfc_state {
BCMGENET_RXNFC_STATE_UNUSED = 0,
BCMGENET_RXNFC_STATE_DISABLED,
BCMGENET_RXNFC_STATE_ENABLED
};
struct bcmgenet_rxnfc_rule {
struct list_head list;
struct ethtool_rx_flow_spec fs;
enum bcmgenet_rxnfc_state state;
};
/* device context */
struct bcmgenet_priv {
void __iomem *base;
enum bcmgenet_version version;
struct net_device *dev;
/* transmit variables */
void __iomem *tx_bds;
struct enet_cb *tx_cbs;
unsigned int num_tx_bds;
struct bcmgenet_tx_ring tx_rings[DESC_INDEX + 1];
/* receive variables */
void __iomem *rx_bds;
struct enet_cb *rx_cbs;
unsigned int num_rx_bds;
unsigned int rx_buf_len;
struct bcmgenet_rxnfc_rule rxnfc_rules[MAX_NUM_OF_FS_RULES];
struct list_head rxnfc_list;
struct bcmgenet_rx_ring rx_rings[DESC_INDEX + 1];
/* other misc variables */
struct bcmgenet_hw_params *hw_params;
/* MDIO bus variables */
wait_queue_head_t wq;
bool internal_phy;
struct device_node *phy_dn;
struct device_node *mdio_dn;
struct mii_bus *mii_bus;
u16 gphy_rev;
struct clk *clk_eee;
bool clk_eee_enabled;
/* PHY device variables */
int old_link;
int old_speed;
int old_duplex;
int old_pause;
phy_interface_t phy_interface;
int phy_addr;
int ext_phy;
/* Interrupt variables */
struct work_struct bcmgenet_irq_work;
int irq0;
int irq1;
int wol_irq;
bool wol_irq_disabled;
/* shared status */
spinlock_t lock;
unsigned int irq0_stat;
/* HW descriptors/checksum variables */
bool crc_fwd_en;
u32 dma_max_burst_length;
u32 msg_enable;
struct clk *clk;
struct platform_device *pdev;
struct platform_device *mii_pdev;
/* WOL */
struct clk *clk_wol;
u32 wolopts;
u8 sopass[SOPASS_MAX];
bool wol_active;
struct bcmgenet_mib_counters mib;
struct ethtool_eee eee;
};
#define GENET_IO_MACRO(name, offset) \
static inline u32 bcmgenet_##name##_readl(struct bcmgenet_priv *priv, \
u32 off) \
{ \
/* MIPS chips strapped for BE will automagically configure the \
* peripheral registers for CPU-native byte order. \
*/ \
if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) \
return __raw_readl(priv->base + offset + off); \
else \
return readl_relaxed(priv->base + offset + off); \
} \
static inline void bcmgenet_##name##_writel(struct bcmgenet_priv *priv, \
u32 val, u32 off) \
{ \
if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) \
__raw_writel(val, priv->base + offset + off); \
else \
writel_relaxed(val, priv->base + offset + off); \
}
GENET_IO_MACRO(ext, GENET_EXT_OFF);
GENET_IO_MACRO(umac, GENET_UMAC_OFF);
GENET_IO_MACRO(sys, GENET_SYS_OFF);
/* interrupt l2 registers accessors */
GENET_IO_MACRO(intrl2_0, GENET_INTRL2_0_OFF);
GENET_IO_MACRO(intrl2_1, GENET_INTRL2_1_OFF);
/* HFB register accessors */
GENET_IO_MACRO(hfb, priv->hw_params->hfb_offset);
/* GENET v2+ HFB control and filter len helpers */
GENET_IO_MACRO(hfb_reg, priv->hw_params->hfb_reg_offset);
/* RBUF register accessors */
GENET_IO_MACRO(rbuf, GENET_RBUF_OFF);
/* MDIO routines */
int bcmgenet_mii_init(struct net_device *dev);
int bcmgenet_mii_config(struct net_device *dev, bool init);
int bcmgenet_mii_probe(struct net_device *dev);
void bcmgenet_mii_exit(struct net_device *dev);
void bcmgenet_phy_power_set(struct net_device *dev, bool enable);
void bcmgenet_mii_setup(struct net_device *dev);
/* Wake-on-LAN routines */
void bcmgenet_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol);
int bcmgenet_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol);
int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv,
enum bcmgenet_power_mode mode);
void bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv,
enum bcmgenet_power_mode mode);
#endif /* __BCMGENET_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,709 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2014-2020 Broadcom
*/
#ifndef __BCMGENET_H__
#define __BCMGENET_H__
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/spinlock.h>
#include <linux/clk.h>
#include <linux/mii.h>
#include <linux/if_vlan.h>
#include <linux/phy.h>
#include <linux/dim.h>
#include <linux/ethtool.h>
#include "unimac-5.14-ethercat.h"
/* EtherCAT header file */
#include "../ecdev.h"
/* total number of Buffer Descriptors, same for Rx/Tx */
#define TOTAL_DESC 256
/* which ring is descriptor based */
#define DESC_INDEX 16
/* Body(1500) + EH_SIZE(14) + VLANTAG(4) + BRCMTAG(6) + FCS(4) = 1528.
* 1536 is multiple of 256 bytes
*/
#define ENET_BRCM_TAG_LEN 6
#define ENET_PAD 8
#define ENET_MAX_MTU_SIZE (ETH_DATA_LEN + ETH_HLEN + VLAN_HLEN + \
ENET_BRCM_TAG_LEN + ETH_FCS_LEN + ENET_PAD)
#define DMA_MAX_BURST_LENGTH 0x10
/* misc. configuration */
#define MAX_NUM_OF_FS_RULES 16
#define CLEAR_ALL_HFB 0xFF
#define DMA_FC_THRESH_HI (TOTAL_DESC >> 4)
#define DMA_FC_THRESH_LO 5
/* 64B receive/transmit status block */
struct status_64 {
u32 length_status; /* length and peripheral status */
u32 ext_status; /* Extended status*/
u32 rx_csum; /* partial rx checksum */
u32 unused1[9]; /* unused */
u32 tx_csum_info; /* Tx checksum info. */
u32 unused2[3]; /* unused */
};
/* Rx status bits */
#define STATUS_RX_EXT_MASK 0x1FFFFF
#define STATUS_RX_CSUM_MASK 0xFFFF
#define STATUS_RX_CSUM_OK 0x10000
#define STATUS_RX_CSUM_FR 0x20000
#define STATUS_RX_PROTO_TCP 0
#define STATUS_RX_PROTO_UDP 1
#define STATUS_RX_PROTO_ICMP 2
#define STATUS_RX_PROTO_OTHER 3
#define STATUS_RX_PROTO_MASK 3
#define STATUS_RX_PROTO_SHIFT 18
#define STATUS_FILTER_INDEX_MASK 0xFFFF
/* Tx status bits */
#define STATUS_TX_CSUM_START_MASK 0X7FFF
#define STATUS_TX_CSUM_START_SHIFT 16
#define STATUS_TX_CSUM_PROTO_UDP 0x8000
#define STATUS_TX_CSUM_OFFSET_MASK 0x7FFF
#define STATUS_TX_CSUM_LV 0x80000000
/* DMA Descriptor */
#define DMA_DESC_LENGTH_STATUS 0x00 /* in bytes of data in buffer */
#define DMA_DESC_ADDRESS_LO 0x04 /* lower bits of PA */
#define DMA_DESC_ADDRESS_HI 0x08 /* upper 32 bits of PA, GENETv4+ */
/* Rx/Tx common counter group */
struct bcmgenet_pkt_counters {
u32 cnt_64; /* RO Received/Transmited 64 bytes packet */
u32 cnt_127; /* RO Rx/Tx 127 bytes packet */
u32 cnt_255; /* RO Rx/Tx 65-255 bytes packet */
u32 cnt_511; /* RO Rx/Tx 256-511 bytes packet */
u32 cnt_1023; /* RO Rx/Tx 512-1023 bytes packet */
u32 cnt_1518; /* RO Rx/Tx 1024-1518 bytes packet */
u32 cnt_mgv; /* RO Rx/Tx 1519-1522 good VLAN packet */
u32 cnt_2047; /* RO Rx/Tx 1522-2047 bytes packet*/
u32 cnt_4095; /* RO Rx/Tx 2048-4095 bytes packet*/
u32 cnt_9216; /* RO Rx/Tx 4096-9216 bytes packet*/
};
/* RSV, Receive Status Vector */
struct bcmgenet_rx_counters {
struct bcmgenet_pkt_counters pkt_cnt;
u32 pkt; /* RO (0x428) Received pkt count*/
u32 bytes; /* RO Received byte count */
u32 mca; /* RO # of Received multicast pkt */
u32 bca; /* RO # of Receive broadcast pkt */
u32 fcs; /* RO # of Received FCS error */
u32 cf; /* RO # of Received control frame pkt*/
u32 pf; /* RO # of Received pause frame pkt */
u32 uo; /* RO # of unknown op code pkt */
u32 aln; /* RO # of alignment error count */
u32 flr; /* RO # of frame length out of range count */
u32 cde; /* RO # of code error pkt */
u32 fcr; /* RO # of carrier sense error pkt */
u32 ovr; /* RO # of oversize pkt*/
u32 jbr; /* RO # of jabber count */
u32 mtue; /* RO # of MTU error pkt*/
u32 pok; /* RO # of Received good pkt */
u32 uc; /* RO # of unicast pkt */
u32 ppp; /* RO # of PPP pkt */
u32 rcrc; /* RO (0x470),# of CRC match pkt */
};
/* TSV, Transmit Status Vector */
struct bcmgenet_tx_counters {
struct bcmgenet_pkt_counters pkt_cnt;
u32 pkts; /* RO (0x4a8) Transmited pkt */
u32 mca; /* RO # of xmited multicast pkt */
u32 bca; /* RO # of xmited broadcast pkt */
u32 pf; /* RO # of xmited pause frame count */
u32 cf; /* RO # of xmited control frame count */
u32 fcs; /* RO # of xmited FCS error count */
u32 ovr; /* RO # of xmited oversize pkt */
u32 drf; /* RO # of xmited deferral pkt */
u32 edf; /* RO # of xmited Excessive deferral pkt*/
u32 scl; /* RO # of xmited single collision pkt */
u32 mcl; /* RO # of xmited multiple collision pkt*/
u32 lcl; /* RO # of xmited late collision pkt */
u32 ecl; /* RO # of xmited excessive collision pkt*/
u32 frg; /* RO # of xmited fragments pkt*/
u32 ncl; /* RO # of xmited total collision count */
u32 jbr; /* RO # of xmited jabber count*/
u32 bytes; /* RO # of xmited byte count */
u32 pok; /* RO # of xmited good pkt */
u32 uc; /* RO (0x0x4f0)# of xmited unitcast pkt */
};
struct bcmgenet_mib_counters {
struct bcmgenet_rx_counters rx;
struct bcmgenet_tx_counters tx;
u32 rx_runt_cnt;
u32 rx_runt_fcs;
u32 rx_runt_fcs_align;
u32 rx_runt_bytes;
u32 rbuf_ovflow_cnt;
u32 rbuf_err_cnt;
u32 mdf_err_cnt;
u32 alloc_rx_buff_failed;
u32 rx_dma_failed;
u32 tx_dma_failed;
u32 tx_realloc_tsb;
u32 tx_realloc_tsb_failed;
};
#define UMAC_MIB_START 0x400
#define UMAC_MDIO_CMD 0x614
#define MDIO_START_BUSY (1 << 29)
#define MDIO_READ_FAIL (1 << 28)
#define MDIO_RD (2 << 26)
#define MDIO_WR (1 << 26)
#define MDIO_PMD_SHIFT 21
#define MDIO_PMD_MASK 0x1F
#define MDIO_REG_SHIFT 16
#define MDIO_REG_MASK 0x1F
#define UMAC_RBUF_OVFL_CNT_V1 0x61C
#define RBUF_OVFL_CNT_V2 0x80
#define RBUF_OVFL_CNT_V3PLUS 0x94
#define UMAC_MPD_CTRL 0x620
#define MPD_EN (1 << 0)
#define MPD_PW_EN (1 << 27)
#define MPD_MSEQ_LEN_SHIFT 16
#define MPD_MSEQ_LEN_MASK 0xFF
#define UMAC_MPD_PW_MS 0x624
#define UMAC_MPD_PW_LS 0x628
#define UMAC_RBUF_ERR_CNT_V1 0x634
#define RBUF_ERR_CNT_V2 0x84
#define RBUF_ERR_CNT_V3PLUS 0x98
#define UMAC_MDF_ERR_CNT 0x638
#define UMAC_MDF_CTRL 0x650
#define UMAC_MDF_ADDR 0x654
#define UMAC_MIB_CTRL 0x580
#define MIB_RESET_RX (1 << 0)
#define MIB_RESET_RUNT (1 << 1)
#define MIB_RESET_TX (1 << 2)
#define RBUF_CTRL 0x00
#define RBUF_64B_EN (1 << 0)
#define RBUF_ALIGN_2B (1 << 1)
#define RBUF_BAD_DIS (1 << 2)
#define RBUF_STATUS 0x0C
#define RBUF_STATUS_WOL (1 << 0)
#define RBUF_STATUS_MPD_INTR_ACTIVE (1 << 1)
#define RBUF_STATUS_ACPI_INTR_ACTIVE (1 << 2)
#define RBUF_CHK_CTRL 0x14
#define RBUF_RXCHK_EN (1 << 0)
#define RBUF_SKIP_FCS (1 << 4)
#define RBUF_L3_PARSE_DIS (1 << 5)
#define RBUF_ENERGY_CTRL 0x9c
#define RBUF_EEE_EN (1 << 0)
#define RBUF_PM_EN (1 << 1)
#define RBUF_TBUF_SIZE_CTRL 0xb4
#define RBUF_HFB_CTRL_V1 0x38
#define RBUF_HFB_FILTER_EN_SHIFT 16
#define RBUF_HFB_FILTER_EN_MASK 0xffff0000
#define RBUF_HFB_EN (1 << 0)
#define RBUF_HFB_256B (1 << 1)
#define RBUF_ACPI_EN (1 << 2)
#define RBUF_HFB_LEN_V1 0x3C
#define RBUF_FLTR_LEN_MASK 0xFF
#define RBUF_FLTR_LEN_SHIFT 8
#define TBUF_CTRL 0x00
#define TBUF_64B_EN (1 << 0)
#define TBUF_BP_MC 0x0C
#define TBUF_ENERGY_CTRL 0x14
#define TBUF_EEE_EN (1 << 0)
#define TBUF_PM_EN (1 << 1)
#define TBUF_CTRL_V1 0x80
#define TBUF_BP_MC_V1 0xA0
#define HFB_CTRL 0x00
#define HFB_FLT_ENABLE_V3PLUS 0x04
#define HFB_FLT_LEN_V2 0x04
#define HFB_FLT_LEN_V3PLUS 0x1C
/* uniMac intrl2 registers */
#define INTRL2_CPU_STAT 0x00
#define INTRL2_CPU_SET 0x04
#define INTRL2_CPU_CLEAR 0x08
#define INTRL2_CPU_MASK_STATUS 0x0C
#define INTRL2_CPU_MASK_SET 0x10
#define INTRL2_CPU_MASK_CLEAR 0x14
/* INTRL2 instance 0 definitions */
#define UMAC_IRQ_SCB (1 << 0)
#define UMAC_IRQ_EPHY (1 << 1)
#define UMAC_IRQ_PHY_DET_R (1 << 2)
#define UMAC_IRQ_PHY_DET_F (1 << 3)
#define UMAC_IRQ_LINK_UP (1 << 4)
#define UMAC_IRQ_LINK_DOWN (1 << 5)
#define UMAC_IRQ_LINK_EVENT (UMAC_IRQ_LINK_UP | UMAC_IRQ_LINK_DOWN)
#define UMAC_IRQ_UMAC (1 << 6)
#define UMAC_IRQ_UMAC_TSV (1 << 7)
#define UMAC_IRQ_TBUF_UNDERRUN (1 << 8)
#define UMAC_IRQ_RBUF_OVERFLOW (1 << 9)
#define UMAC_IRQ_HFB_SM (1 << 10)
#define UMAC_IRQ_HFB_MM (1 << 11)
#define UMAC_IRQ_MPD_R (1 << 12)
#define UMAC_IRQ_WAKE_EVENT (UMAC_IRQ_HFB_SM | UMAC_IRQ_HFB_MM | \
UMAC_IRQ_MPD_R)
#define UMAC_IRQ_RXDMA_MBDONE (1 << 13)
#define UMAC_IRQ_RXDMA_PDONE (1 << 14)
#define UMAC_IRQ_RXDMA_BDONE (1 << 15)
#define UMAC_IRQ_RXDMA_DONE UMAC_IRQ_RXDMA_MBDONE
#define UMAC_IRQ_TXDMA_MBDONE (1 << 16)
#define UMAC_IRQ_TXDMA_PDONE (1 << 17)
#define UMAC_IRQ_TXDMA_BDONE (1 << 18)
#define UMAC_IRQ_TXDMA_DONE UMAC_IRQ_TXDMA_MBDONE
/* Only valid for GENETv3+ */
#define UMAC_IRQ_MDIO_DONE (1 << 23)
#define UMAC_IRQ_MDIO_ERROR (1 << 24)
/* INTRL2 instance 1 definitions */
#define UMAC_IRQ1_TX_INTR_MASK 0xFFFF
#define UMAC_IRQ1_RX_INTR_MASK 0xFFFF
#define UMAC_IRQ1_RX_INTR_SHIFT 16
/* Register block offsets */
#define GENET_SYS_OFF 0x0000
#define GENET_GR_BRIDGE_OFF 0x0040
#define GENET_EXT_OFF 0x0080
#define GENET_INTRL2_0_OFF 0x0200
#define GENET_INTRL2_1_OFF 0x0240
#define GENET_RBUF_OFF 0x0300
#define GENET_UMAC_OFF 0x0800
/* SYS block offsets and register definitions */
#define SYS_REV_CTRL 0x00
#define SYS_PORT_CTRL 0x04
#define PORT_MODE_INT_EPHY 0
#define PORT_MODE_INT_GPHY 1
#define PORT_MODE_EXT_EPHY 2
#define PORT_MODE_EXT_GPHY 3
#define PORT_MODE_EXT_RVMII_25 (4 | BIT(4))
#define PORT_MODE_EXT_RVMII_50 4
#define LED_ACT_SOURCE_MAC (1 << 9)
#define SYS_RBUF_FLUSH_CTRL 0x08
#define SYS_TBUF_FLUSH_CTRL 0x0C
#define RBUF_FLUSH_CTRL_V1 0x04
/* Ext block register offsets and definitions */
#define EXT_EXT_PWR_MGMT 0x00
#define EXT_PWR_DOWN_BIAS (1 << 0)
#define EXT_PWR_DOWN_DLL (1 << 1)
#define EXT_PWR_DOWN_PHY (1 << 2)
#define EXT_PWR_DN_EN_LD (1 << 3)
#define EXT_ENERGY_DET (1 << 4)
#define EXT_IDDQ_FROM_PHY (1 << 5)
#define EXT_IDDQ_GLBL_PWR (1 << 7)
#define EXT_PHY_RESET (1 << 8)
#define EXT_ENERGY_DET_MASK (1 << 12)
#define EXT_PWR_DOWN_PHY_TX (1 << 16)
#define EXT_PWR_DOWN_PHY_RX (1 << 17)
#define EXT_PWR_DOWN_PHY_SD (1 << 18)
#define EXT_PWR_DOWN_PHY_RD (1 << 19)
#define EXT_PWR_DOWN_PHY_EN (1 << 20)
#define EXT_RGMII_OOB_CTRL 0x0C
#define RGMII_MODE_EN_V123 (1 << 0)
#define RGMII_LINK (1 << 4)
#define OOB_DISABLE (1 << 5)
#define RGMII_MODE_EN (1 << 6)
#define ID_MODE_DIS (1 << 16)
#define EXT_GPHY_CTRL 0x1C
#define EXT_CFG_IDDQ_BIAS (1 << 0)
#define EXT_CFG_PWR_DOWN (1 << 1)
#define EXT_CK25_DIS (1 << 4)
#define EXT_GPHY_RESET (1 << 5)
/* DMA rings size */
#define DMA_RING_SIZE (0x40)
#define DMA_RINGS_SIZE (DMA_RING_SIZE * (DESC_INDEX + 1))
/* DMA registers common definitions */
#define DMA_RW_POINTER_MASK 0x1FF
#define DMA_P_INDEX_DISCARD_CNT_MASK 0xFFFF
#define DMA_P_INDEX_DISCARD_CNT_SHIFT 16
#define DMA_BUFFER_DONE_CNT_MASK 0xFFFF
#define DMA_BUFFER_DONE_CNT_SHIFT 16
#define DMA_P_INDEX_MASK 0xFFFF
#define DMA_C_INDEX_MASK 0xFFFF
/* DMA ring size register */
#define DMA_RING_SIZE_MASK 0xFFFF
#define DMA_RING_SIZE_SHIFT 16
#define DMA_RING_BUFFER_SIZE_MASK 0xFFFF
/* DMA interrupt threshold register */
#define DMA_INTR_THRESHOLD_MASK 0x01FF
/* DMA XON/XOFF register */
#define DMA_XON_THREHOLD_MASK 0xFFFF
#define DMA_XOFF_THRESHOLD_MASK 0xFFFF
#define DMA_XOFF_THRESHOLD_SHIFT 16
/* DMA flow period register */
#define DMA_FLOW_PERIOD_MASK 0xFFFF
#define DMA_MAX_PKT_SIZE_MASK 0xFFFF
#define DMA_MAX_PKT_SIZE_SHIFT 16
/* DMA control register */
#define DMA_EN (1 << 0)
#define DMA_RING_BUF_EN_SHIFT 0x01
#define DMA_RING_BUF_EN_MASK 0xFFFF
#define DMA_TSB_SWAP_EN (1 << 20)
/* DMA status register */
#define DMA_DISABLED (1 << 0)
#define DMA_DESC_RAM_INIT_BUSY (1 << 1)
/* DMA SCB burst size register */
#define DMA_SCB_BURST_SIZE_MASK 0x1F
/* DMA activity vector register */
#define DMA_ACTIVITY_VECTOR_MASK 0x1FFFF
/* DMA backpressure mask register */
#define DMA_BACKPRESSURE_MASK 0x1FFFF
#define DMA_PFC_ENABLE (1 << 31)
/* DMA backpressure status register */
#define DMA_BACKPRESSURE_STATUS_MASK 0x1FFFF
/* DMA override register */
#define DMA_LITTLE_ENDIAN_MODE (1 << 0)
#define DMA_REGISTER_MODE (1 << 1)
/* DMA timeout register */
#define DMA_TIMEOUT_MASK 0xFFFF
#define DMA_TIMEOUT_VAL 5000 /* micro seconds */
/* TDMA rate limiting control register */
#define DMA_RATE_LIMIT_EN_MASK 0xFFFF
/* TDMA arbitration control register */
#define DMA_ARBITER_MODE_MASK 0x03
#define DMA_RING_BUF_PRIORITY_MASK 0x1F
#define DMA_RING_BUF_PRIORITY_SHIFT 5
#define DMA_PRIO_REG_INDEX(q) ((q) / 6)
#define DMA_PRIO_REG_SHIFT(q) (((q) % 6) * DMA_RING_BUF_PRIORITY_SHIFT)
#define DMA_RATE_ADJ_MASK 0xFF
/* Tx/Rx Dma Descriptor common bits*/
#define DMA_BUFLENGTH_MASK 0x0fff
#define DMA_BUFLENGTH_SHIFT 16
#define DMA_OWN 0x8000
#define DMA_EOP 0x4000
#define DMA_SOP 0x2000
#define DMA_WRAP 0x1000
/* Tx specific Dma descriptor bits */
#define DMA_TX_UNDERRUN 0x0200
#define DMA_TX_APPEND_CRC 0x0040
#define DMA_TX_OW_CRC 0x0020
#define DMA_TX_DO_CSUM 0x0010
#define DMA_TX_QTAG_SHIFT 7
/* Rx Specific Dma descriptor bits */
#define DMA_RX_CHK_V3PLUS 0x8000
#define DMA_RX_CHK_V12 0x1000
#define DMA_RX_BRDCAST 0x0040
#define DMA_RX_MULT 0x0020
#define DMA_RX_LG 0x0010
#define DMA_RX_NO 0x0008
#define DMA_RX_RXER 0x0004
#define DMA_RX_CRC_ERROR 0x0002
#define DMA_RX_OV 0x0001
#define DMA_RX_FI_MASK 0x001F
#define DMA_RX_FI_SHIFT 0x0007
#define DMA_DESC_ALLOC_MASK 0x00FF
#define DMA_ARBITER_RR 0x00
#define DMA_ARBITER_WRR 0x01
#define DMA_ARBITER_SP 0x02
struct enet_cb {
struct sk_buff *skb;
void __iomem *bd_addr;
DEFINE_DMA_UNMAP_ADDR(dma_addr);
DEFINE_DMA_UNMAP_LEN(dma_len);
};
/* power management mode */
enum bcmgenet_power_mode {
GENET_POWER_CABLE_SENSE = 0,
GENET_POWER_PASSIVE,
GENET_POWER_WOL_MAGIC,
};
struct bcmgenet_priv;
/* We support both runtime GENET detection and compile-time
* to optimize code-paths for a given hardware
*/
enum bcmgenet_version {
GENET_V1 = 1,
GENET_V2,
GENET_V3,
GENET_V4,
GENET_V5
};
#define GENET_IS_V1(p) ((p)->version == GENET_V1)
#define GENET_IS_V2(p) ((p)->version == GENET_V2)
#define GENET_IS_V3(p) ((p)->version == GENET_V3)
#define GENET_IS_V4(p) ((p)->version == GENET_V4)
#define GENET_IS_V5(p) ((p)->version == GENET_V5)
/* Hardware flags */
#define GENET_HAS_40BITS (1 << 0)
#define GENET_HAS_EXT (1 << 1)
#define GENET_HAS_MDIO_INTR (1 << 2)
#define GENET_HAS_MOCA_LINK_DET (1 << 3)
/* BCMGENET hardware parameters, keep this structure nicely aligned
* since it is going to be used in hot paths
*/
struct bcmgenet_hw_params {
u8 tx_queues;
u8 tx_bds_per_q;
u8 rx_queues;
u8 rx_bds_per_q;
u8 bp_in_en_shift;
u32 bp_in_mask;
u8 hfb_filter_cnt;
u8 hfb_filter_size;
u8 qtag_mask;
u16 tbuf_offset;
u32 hfb_offset;
u32 hfb_reg_offset;
u32 rdma_offset;
u32 tdma_offset;
u32 words_per_bd;
u32 flags;
};
struct bcmgenet_skb_cb {
struct enet_cb *first_cb; /* First control block of SKB */
struct enet_cb *last_cb; /* Last control block of SKB */
unsigned int bytes_sent; /* bytes on the wire (no TSB) */
};
#define GENET_CB(skb) ((struct bcmgenet_skb_cb *)((skb)->cb))
struct bcmgenet_tx_ring {
spinlock_t lock; /* ring lock */
struct napi_struct napi; /* NAPI per tx queue */
unsigned long packets;
unsigned long bytes;
unsigned int index; /* ring index */
unsigned int queue; /* queue index */
struct enet_cb *cbs; /* tx ring buffer control block*/
unsigned int size; /* size of each tx ring */
unsigned int clean_ptr; /* Tx ring clean pointer */
unsigned int c_index; /* last consumer index of each ring*/
unsigned int free_bds; /* # of free bds for each ring */
unsigned int write_ptr; /* Tx ring write pointer SW copy */
unsigned int prod_index; /* Tx ring producer index SW copy */
unsigned int cb_ptr; /* Tx ring initial CB ptr */
unsigned int end_ptr; /* Tx ring end CB ptr */
void (*int_enable)(struct bcmgenet_tx_ring *);
void (*int_disable)(struct bcmgenet_tx_ring *);
struct bcmgenet_priv *priv;
};
struct bcmgenet_net_dim {
u16 use_dim;
u16 event_ctr;
unsigned long packets;
unsigned long bytes;
struct dim dim;
};
struct bcmgenet_rx_ring {
struct napi_struct napi; /* Rx NAPI struct */
unsigned long bytes;
unsigned long packets;
unsigned long errors;
unsigned long dropped;
unsigned int index; /* Rx ring index */
struct enet_cb *cbs; /* Rx ring buffer control block */
unsigned int size; /* Rx ring size */
unsigned int c_index; /* Rx last consumer index */
unsigned int read_ptr; /* Rx ring read pointer */
unsigned int cb_ptr; /* Rx ring initial CB ptr */
unsigned int end_ptr; /* Rx ring end CB ptr */
unsigned int old_discards;
struct bcmgenet_net_dim dim;
u32 rx_max_coalesced_frames;
u32 rx_coalesce_usecs;
void (*int_enable)(struct bcmgenet_rx_ring *);
void (*int_disable)(struct bcmgenet_rx_ring *);
struct bcmgenet_priv *priv;
};
enum bcmgenet_rxnfc_state {
BCMGENET_RXNFC_STATE_UNUSED = 0,
BCMGENET_RXNFC_STATE_DISABLED,
BCMGENET_RXNFC_STATE_ENABLED
};
struct bcmgenet_rxnfc_rule {
struct list_head list;
struct ethtool_rx_flow_spec fs;
enum bcmgenet_rxnfc_state state;
};
/* device context */
struct bcmgenet_priv {
void __iomem *base;
enum bcmgenet_version version;
struct net_device *dev;
/* transmit variables */
void __iomem *tx_bds;
struct enet_cb *tx_cbs;
unsigned int num_tx_bds;
struct bcmgenet_tx_ring tx_rings[DESC_INDEX + 1];
/* receive variables */
void __iomem *rx_bds;
struct enet_cb *rx_cbs;
unsigned int num_rx_bds;
unsigned int rx_buf_len;
struct bcmgenet_rxnfc_rule rxnfc_rules[MAX_NUM_OF_FS_RULES];
struct list_head rxnfc_list;
struct bcmgenet_rx_ring rx_rings[DESC_INDEX + 1];
/* other misc variables */
struct bcmgenet_hw_params *hw_params;
/* MDIO bus variables */
wait_queue_head_t wq;
bool internal_phy;
struct device_node *phy_dn;
struct device_node *mdio_dn;
struct mii_bus *mii_bus;
u16 gphy_rev;
struct clk *clk_eee;
bool clk_eee_enabled;
/* PHY device variables */
int old_link;
int old_speed;
int old_duplex;
int old_pause;
phy_interface_t phy_interface;
int phy_addr;
int ext_phy;
/* Interrupt variables */
struct work_struct bcmgenet_irq_work;
int irq0;
int irq1;
int wol_irq;
bool wol_irq_disabled;
/* shared status */
spinlock_t lock;
unsigned int irq0_stat;
/* HW descriptors/checksum variables */
bool crc_fwd_en;
u32 dma_max_burst_length;
u32 msg_enable;
struct clk *clk;
struct platform_device *pdev;
struct platform_device *mii_pdev;
/* WOL */
struct clk *clk_wol;
u32 wolopts;
u8 sopass[SOPASS_MAX];
bool wol_active;
struct bcmgenet_mib_counters mib;
struct ethtool_eee eee;
/* EtherCAT device variables */
ec_device_t *ecdev;
};
#define GENET_IO_MACRO(name, offset) \
static inline u32 bcmgenet_##name##_readl(struct bcmgenet_priv *priv, \
u32 off) \
{ \
/* MIPS chips strapped for BE will automagically configure the \
* peripheral registers for CPU-native byte order. \
*/ \
if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) \
return __raw_readl(priv->base + offset + off); \
else \
return readl_relaxed(priv->base + offset + off); \
} \
static inline void bcmgenet_##name##_writel(struct bcmgenet_priv *priv, \
u32 val, u32 off) \
{ \
if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) \
__raw_writel(val, priv->base + offset + off); \
else \
writel_relaxed(val, priv->base + offset + off); \
}
GENET_IO_MACRO(ext, GENET_EXT_OFF);
GENET_IO_MACRO(umac, GENET_UMAC_OFF);
GENET_IO_MACRO(sys, GENET_SYS_OFF);
/* interrupt l2 registers accessors */
GENET_IO_MACRO(intrl2_0, GENET_INTRL2_0_OFF);
GENET_IO_MACRO(intrl2_1, GENET_INTRL2_1_OFF);
/* HFB register accessors */
GENET_IO_MACRO(hfb, priv->hw_params->hfb_offset);
/* GENET v2+ HFB control and filter len helpers */
GENET_IO_MACRO(hfb_reg, priv->hw_params->hfb_reg_offset);
/* RBUF register accessors */
GENET_IO_MACRO(rbuf, GENET_RBUF_OFF);
/* MDIO routines */
int bcmgenet_mii_init(struct net_device *dev);
int bcmgenet_mii_config(struct net_device *dev, bool init);
int bcmgenet_mii_probe(struct net_device *dev);
void bcmgenet_mii_exit(struct net_device *dev);
void bcmgenet_phy_power_set(struct net_device *dev, bool enable);
void bcmgenet_mii_setup(struct net_device *dev);
/* Wake-on-LAN routines */
void bcmgenet_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol);
int bcmgenet_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol);
int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv,
enum bcmgenet_power_mode mode);
void bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv,
enum bcmgenet_power_mode mode);
#endif /* __BCMGENET_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,704 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2014-2020 Broadcom
*/
#ifndef __BCMGENET_H__
#define __BCMGENET_H__
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/spinlock.h>
#include <linux/clk.h>
#include <linux/mii.h>
#include <linux/if_vlan.h>
#include <linux/phy.h>
#include <linux/dim.h>
#include <linux/ethtool.h>
#include "../unimac.h"
/* total number of Buffer Descriptors, same for Rx/Tx */
#define TOTAL_DESC 256
/* which ring is descriptor based */
#define DESC_INDEX 16
/* Body(1500) + EH_SIZE(14) + VLANTAG(4) + BRCMTAG(6) + FCS(4) = 1528.
* 1536 is multiple of 256 bytes
*/
#define ENET_BRCM_TAG_LEN 6
#define ENET_PAD 8
#define ENET_MAX_MTU_SIZE (ETH_DATA_LEN + ETH_HLEN + VLAN_HLEN + \
ENET_BRCM_TAG_LEN + ETH_FCS_LEN + ENET_PAD)
#define DMA_MAX_BURST_LENGTH 0x10
/* misc. configuration */
#define MAX_NUM_OF_FS_RULES 16
#define CLEAR_ALL_HFB 0xFF
#define DMA_FC_THRESH_HI (TOTAL_DESC >> 4)
#define DMA_FC_THRESH_LO 5
/* 64B receive/transmit status block */
struct status_64 {
u32 length_status; /* length and peripheral status */
u32 ext_status; /* Extended status*/
u32 rx_csum; /* partial rx checksum */
u32 unused1[9]; /* unused */
u32 tx_csum_info; /* Tx checksum info. */
u32 unused2[3]; /* unused */
};
/* Rx status bits */
#define STATUS_RX_EXT_MASK 0x1FFFFF
#define STATUS_RX_CSUM_MASK 0xFFFF
#define STATUS_RX_CSUM_OK 0x10000
#define STATUS_RX_CSUM_FR 0x20000
#define STATUS_RX_PROTO_TCP 0
#define STATUS_RX_PROTO_UDP 1
#define STATUS_RX_PROTO_ICMP 2
#define STATUS_RX_PROTO_OTHER 3
#define STATUS_RX_PROTO_MASK 3
#define STATUS_RX_PROTO_SHIFT 18
#define STATUS_FILTER_INDEX_MASK 0xFFFF
/* Tx status bits */
#define STATUS_TX_CSUM_START_MASK 0X7FFF
#define STATUS_TX_CSUM_START_SHIFT 16
#define STATUS_TX_CSUM_PROTO_UDP 0x8000
#define STATUS_TX_CSUM_OFFSET_MASK 0x7FFF
#define STATUS_TX_CSUM_LV 0x80000000
/* DMA Descriptor */
#define DMA_DESC_LENGTH_STATUS 0x00 /* in bytes of data in buffer */
#define DMA_DESC_ADDRESS_LO 0x04 /* lower bits of PA */
#define DMA_DESC_ADDRESS_HI 0x08 /* upper 32 bits of PA, GENETv4+ */
/* Rx/Tx common counter group */
struct bcmgenet_pkt_counters {
u32 cnt_64; /* RO Received/Transmited 64 bytes packet */
u32 cnt_127; /* RO Rx/Tx 127 bytes packet */
u32 cnt_255; /* RO Rx/Tx 65-255 bytes packet */
u32 cnt_511; /* RO Rx/Tx 256-511 bytes packet */
u32 cnt_1023; /* RO Rx/Tx 512-1023 bytes packet */
u32 cnt_1518; /* RO Rx/Tx 1024-1518 bytes packet */
u32 cnt_mgv; /* RO Rx/Tx 1519-1522 good VLAN packet */
u32 cnt_2047; /* RO Rx/Tx 1522-2047 bytes packet*/
u32 cnt_4095; /* RO Rx/Tx 2048-4095 bytes packet*/
u32 cnt_9216; /* RO Rx/Tx 4096-9216 bytes packet*/
};
/* RSV, Receive Status Vector */
struct bcmgenet_rx_counters {
struct bcmgenet_pkt_counters pkt_cnt;
u32 pkt; /* RO (0x428) Received pkt count*/
u32 bytes; /* RO Received byte count */
u32 mca; /* RO # of Received multicast pkt */
u32 bca; /* RO # of Receive broadcast pkt */
u32 fcs; /* RO # of Received FCS error */
u32 cf; /* RO # of Received control frame pkt*/
u32 pf; /* RO # of Received pause frame pkt */
u32 uo; /* RO # of unknown op code pkt */
u32 aln; /* RO # of alignment error count */
u32 flr; /* RO # of frame length out of range count */
u32 cde; /* RO # of code error pkt */
u32 fcr; /* RO # of carrier sense error pkt */
u32 ovr; /* RO # of oversize pkt*/
u32 jbr; /* RO # of jabber count */
u32 mtue; /* RO # of MTU error pkt*/
u32 pok; /* RO # of Received good pkt */
u32 uc; /* RO # of unicast pkt */
u32 ppp; /* RO # of PPP pkt */
u32 rcrc; /* RO (0x470),# of CRC match pkt */
};
/* TSV, Transmit Status Vector */
struct bcmgenet_tx_counters {
struct bcmgenet_pkt_counters pkt_cnt;
u32 pkts; /* RO (0x4a8) Transmited pkt */
u32 mca; /* RO # of xmited multicast pkt */
u32 bca; /* RO # of xmited broadcast pkt */
u32 pf; /* RO # of xmited pause frame count */
u32 cf; /* RO # of xmited control frame count */
u32 fcs; /* RO # of xmited FCS error count */
u32 ovr; /* RO # of xmited oversize pkt */
u32 drf; /* RO # of xmited deferral pkt */
u32 edf; /* RO # of xmited Excessive deferral pkt*/
u32 scl; /* RO # of xmited single collision pkt */
u32 mcl; /* RO # of xmited multiple collision pkt*/
u32 lcl; /* RO # of xmited late collision pkt */
u32 ecl; /* RO # of xmited excessive collision pkt*/
u32 frg; /* RO # of xmited fragments pkt*/
u32 ncl; /* RO # of xmited total collision count */
u32 jbr; /* RO # of xmited jabber count*/
u32 bytes; /* RO # of xmited byte count */
u32 pok; /* RO # of xmited good pkt */
u32 uc; /* RO (0x0x4f0)# of xmited unitcast pkt */
};
struct bcmgenet_mib_counters {
struct bcmgenet_rx_counters rx;
struct bcmgenet_tx_counters tx;
u32 rx_runt_cnt;
u32 rx_runt_fcs;
u32 rx_runt_fcs_align;
u32 rx_runt_bytes;
u32 rbuf_ovflow_cnt;
u32 rbuf_err_cnt;
u32 mdf_err_cnt;
u32 alloc_rx_buff_failed;
u32 rx_dma_failed;
u32 tx_dma_failed;
u32 tx_realloc_tsb;
u32 tx_realloc_tsb_failed;
};
#define UMAC_MIB_START 0x400
#define UMAC_MDIO_CMD 0x614
#define MDIO_START_BUSY (1 << 29)
#define MDIO_READ_FAIL (1 << 28)
#define MDIO_RD (2 << 26)
#define MDIO_WR (1 << 26)
#define MDIO_PMD_SHIFT 21
#define MDIO_PMD_MASK 0x1F
#define MDIO_REG_SHIFT 16
#define MDIO_REG_MASK 0x1F
#define UMAC_RBUF_OVFL_CNT_V1 0x61C
#define RBUF_OVFL_CNT_V2 0x80
#define RBUF_OVFL_CNT_V3PLUS 0x94
#define UMAC_MPD_CTRL 0x620
#define MPD_EN (1 << 0)
#define MPD_PW_EN (1 << 27)
#define MPD_MSEQ_LEN_SHIFT 16
#define MPD_MSEQ_LEN_MASK 0xFF
#define UMAC_MPD_PW_MS 0x624
#define UMAC_MPD_PW_LS 0x628
#define UMAC_RBUF_ERR_CNT_V1 0x634
#define RBUF_ERR_CNT_V2 0x84
#define RBUF_ERR_CNT_V3PLUS 0x98
#define UMAC_MDF_ERR_CNT 0x638
#define UMAC_MDF_CTRL 0x650
#define UMAC_MDF_ADDR 0x654
#define UMAC_MIB_CTRL 0x580
#define MIB_RESET_RX (1 << 0)
#define MIB_RESET_RUNT (1 << 1)
#define MIB_RESET_TX (1 << 2)
#define RBUF_CTRL 0x00
#define RBUF_64B_EN (1 << 0)
#define RBUF_ALIGN_2B (1 << 1)
#define RBUF_BAD_DIS (1 << 2)
#define RBUF_STATUS 0x0C
#define RBUF_STATUS_WOL (1 << 0)
#define RBUF_STATUS_MPD_INTR_ACTIVE (1 << 1)
#define RBUF_STATUS_ACPI_INTR_ACTIVE (1 << 2)
#define RBUF_CHK_CTRL 0x14
#define RBUF_RXCHK_EN (1 << 0)
#define RBUF_SKIP_FCS (1 << 4)
#define RBUF_L3_PARSE_DIS (1 << 5)
#define RBUF_ENERGY_CTRL 0x9c
#define RBUF_EEE_EN (1 << 0)
#define RBUF_PM_EN (1 << 1)
#define RBUF_TBUF_SIZE_CTRL 0xb4
#define RBUF_HFB_CTRL_V1 0x38
#define RBUF_HFB_FILTER_EN_SHIFT 16
#define RBUF_HFB_FILTER_EN_MASK 0xffff0000
#define RBUF_HFB_EN (1 << 0)
#define RBUF_HFB_256B (1 << 1)
#define RBUF_ACPI_EN (1 << 2)
#define RBUF_HFB_LEN_V1 0x3C
#define RBUF_FLTR_LEN_MASK 0xFF
#define RBUF_FLTR_LEN_SHIFT 8
#define TBUF_CTRL 0x00
#define TBUF_64B_EN (1 << 0)
#define TBUF_BP_MC 0x0C
#define TBUF_ENERGY_CTRL 0x14
#define TBUF_EEE_EN (1 << 0)
#define TBUF_PM_EN (1 << 1)
#define TBUF_CTRL_V1 0x80
#define TBUF_BP_MC_V1 0xA0
#define HFB_CTRL 0x00
#define HFB_FLT_ENABLE_V3PLUS 0x04
#define HFB_FLT_LEN_V2 0x04
#define HFB_FLT_LEN_V3PLUS 0x1C
/* uniMac intrl2 registers */
#define INTRL2_CPU_STAT 0x00
#define INTRL2_CPU_SET 0x04
#define INTRL2_CPU_CLEAR 0x08
#define INTRL2_CPU_MASK_STATUS 0x0C
#define INTRL2_CPU_MASK_SET 0x10
#define INTRL2_CPU_MASK_CLEAR 0x14
/* INTRL2 instance 0 definitions */
#define UMAC_IRQ_SCB (1 << 0)
#define UMAC_IRQ_EPHY (1 << 1)
#define UMAC_IRQ_PHY_DET_R (1 << 2)
#define UMAC_IRQ_PHY_DET_F (1 << 3)
#define UMAC_IRQ_LINK_UP (1 << 4)
#define UMAC_IRQ_LINK_DOWN (1 << 5)
#define UMAC_IRQ_LINK_EVENT (UMAC_IRQ_LINK_UP | UMAC_IRQ_LINK_DOWN)
#define UMAC_IRQ_UMAC (1 << 6)
#define UMAC_IRQ_UMAC_TSV (1 << 7)
#define UMAC_IRQ_TBUF_UNDERRUN (1 << 8)
#define UMAC_IRQ_RBUF_OVERFLOW (1 << 9)
#define UMAC_IRQ_HFB_SM (1 << 10)
#define UMAC_IRQ_HFB_MM (1 << 11)
#define UMAC_IRQ_MPD_R (1 << 12)
#define UMAC_IRQ_WAKE_EVENT (UMAC_IRQ_HFB_SM | UMAC_IRQ_HFB_MM | \
UMAC_IRQ_MPD_R)
#define UMAC_IRQ_RXDMA_MBDONE (1 << 13)
#define UMAC_IRQ_RXDMA_PDONE (1 << 14)
#define UMAC_IRQ_RXDMA_BDONE (1 << 15)
#define UMAC_IRQ_RXDMA_DONE UMAC_IRQ_RXDMA_MBDONE
#define UMAC_IRQ_TXDMA_MBDONE (1 << 16)
#define UMAC_IRQ_TXDMA_PDONE (1 << 17)
#define UMAC_IRQ_TXDMA_BDONE (1 << 18)
#define UMAC_IRQ_TXDMA_DONE UMAC_IRQ_TXDMA_MBDONE
/* Only valid for GENETv3+ */
#define UMAC_IRQ_MDIO_DONE (1 << 23)
#define UMAC_IRQ_MDIO_ERROR (1 << 24)
/* INTRL2 instance 1 definitions */
#define UMAC_IRQ1_TX_INTR_MASK 0xFFFF
#define UMAC_IRQ1_RX_INTR_MASK 0xFFFF
#define UMAC_IRQ1_RX_INTR_SHIFT 16
/* Register block offsets */
#define GENET_SYS_OFF 0x0000
#define GENET_GR_BRIDGE_OFF 0x0040
#define GENET_EXT_OFF 0x0080
#define GENET_INTRL2_0_OFF 0x0200
#define GENET_INTRL2_1_OFF 0x0240
#define GENET_RBUF_OFF 0x0300
#define GENET_UMAC_OFF 0x0800
/* SYS block offsets and register definitions */
#define SYS_REV_CTRL 0x00
#define SYS_PORT_CTRL 0x04
#define PORT_MODE_INT_EPHY 0
#define PORT_MODE_INT_GPHY 1
#define PORT_MODE_EXT_EPHY 2
#define PORT_MODE_EXT_GPHY 3
#define PORT_MODE_EXT_RVMII_25 (4 | BIT(4))
#define PORT_MODE_EXT_RVMII_50 4
#define LED_ACT_SOURCE_MAC (1 << 9)
#define SYS_RBUF_FLUSH_CTRL 0x08
#define SYS_TBUF_FLUSH_CTRL 0x0C
#define RBUF_FLUSH_CTRL_V1 0x04
/* Ext block register offsets and definitions */
#define EXT_EXT_PWR_MGMT 0x00
#define EXT_PWR_DOWN_BIAS (1 << 0)
#define EXT_PWR_DOWN_DLL (1 << 1)
#define EXT_PWR_DOWN_PHY (1 << 2)
#define EXT_PWR_DN_EN_LD (1 << 3)
#define EXT_ENERGY_DET (1 << 4)
#define EXT_IDDQ_FROM_PHY (1 << 5)
#define EXT_IDDQ_GLBL_PWR (1 << 7)
#define EXT_PHY_RESET (1 << 8)
#define EXT_ENERGY_DET_MASK (1 << 12)
#define EXT_PWR_DOWN_PHY_TX (1 << 16)
#define EXT_PWR_DOWN_PHY_RX (1 << 17)
#define EXT_PWR_DOWN_PHY_SD (1 << 18)
#define EXT_PWR_DOWN_PHY_RD (1 << 19)
#define EXT_PWR_DOWN_PHY_EN (1 << 20)
#define EXT_RGMII_OOB_CTRL 0x0C
#define RGMII_MODE_EN_V123 (1 << 0)
#define RGMII_LINK (1 << 4)
#define OOB_DISABLE (1 << 5)
#define RGMII_MODE_EN (1 << 6)
#define ID_MODE_DIS (1 << 16)
#define EXT_GPHY_CTRL 0x1C
#define EXT_CFG_IDDQ_BIAS (1 << 0)
#define EXT_CFG_PWR_DOWN (1 << 1)
#define EXT_CK25_DIS (1 << 4)
#define EXT_GPHY_RESET (1 << 5)
/* DMA rings size */
#define DMA_RING_SIZE (0x40)
#define DMA_RINGS_SIZE (DMA_RING_SIZE * (DESC_INDEX + 1))
/* DMA registers common definitions */
#define DMA_RW_POINTER_MASK 0x1FF
#define DMA_P_INDEX_DISCARD_CNT_MASK 0xFFFF
#define DMA_P_INDEX_DISCARD_CNT_SHIFT 16
#define DMA_BUFFER_DONE_CNT_MASK 0xFFFF
#define DMA_BUFFER_DONE_CNT_SHIFT 16
#define DMA_P_INDEX_MASK 0xFFFF
#define DMA_C_INDEX_MASK 0xFFFF
/* DMA ring size register */
#define DMA_RING_SIZE_MASK 0xFFFF
#define DMA_RING_SIZE_SHIFT 16
#define DMA_RING_BUFFER_SIZE_MASK 0xFFFF
/* DMA interrupt threshold register */
#define DMA_INTR_THRESHOLD_MASK 0x01FF
/* DMA XON/XOFF register */
#define DMA_XON_THREHOLD_MASK 0xFFFF
#define DMA_XOFF_THRESHOLD_MASK 0xFFFF
#define DMA_XOFF_THRESHOLD_SHIFT 16
/* DMA flow period register */
#define DMA_FLOW_PERIOD_MASK 0xFFFF
#define DMA_MAX_PKT_SIZE_MASK 0xFFFF
#define DMA_MAX_PKT_SIZE_SHIFT 16
/* DMA control register */
#define DMA_EN (1 << 0)
#define DMA_RING_BUF_EN_SHIFT 0x01
#define DMA_RING_BUF_EN_MASK 0xFFFF
#define DMA_TSB_SWAP_EN (1 << 20)
/* DMA status register */
#define DMA_DISABLED (1 << 0)
#define DMA_DESC_RAM_INIT_BUSY (1 << 1)
/* DMA SCB burst size register */
#define DMA_SCB_BURST_SIZE_MASK 0x1F
/* DMA activity vector register */
#define DMA_ACTIVITY_VECTOR_MASK 0x1FFFF
/* DMA backpressure mask register */
#define DMA_BACKPRESSURE_MASK 0x1FFFF
#define DMA_PFC_ENABLE (1 << 31)
/* DMA backpressure status register */
#define DMA_BACKPRESSURE_STATUS_MASK 0x1FFFF
/* DMA override register */
#define DMA_LITTLE_ENDIAN_MODE (1 << 0)
#define DMA_REGISTER_MODE (1 << 1)
/* DMA timeout register */
#define DMA_TIMEOUT_MASK 0xFFFF
#define DMA_TIMEOUT_VAL 5000 /* micro seconds */
/* TDMA rate limiting control register */
#define DMA_RATE_LIMIT_EN_MASK 0xFFFF
/* TDMA arbitration control register */
#define DMA_ARBITER_MODE_MASK 0x03
#define DMA_RING_BUF_PRIORITY_MASK 0x1F
#define DMA_RING_BUF_PRIORITY_SHIFT 5
#define DMA_PRIO_REG_INDEX(q) ((q) / 6)
#define DMA_PRIO_REG_SHIFT(q) (((q) % 6) * DMA_RING_BUF_PRIORITY_SHIFT)
#define DMA_RATE_ADJ_MASK 0xFF
/* Tx/Rx Dma Descriptor common bits*/
#define DMA_BUFLENGTH_MASK 0x0fff
#define DMA_BUFLENGTH_SHIFT 16
#define DMA_OWN 0x8000
#define DMA_EOP 0x4000
#define DMA_SOP 0x2000
#define DMA_WRAP 0x1000
/* Tx specific Dma descriptor bits */
#define DMA_TX_UNDERRUN 0x0200
#define DMA_TX_APPEND_CRC 0x0040
#define DMA_TX_OW_CRC 0x0020
#define DMA_TX_DO_CSUM 0x0010
#define DMA_TX_QTAG_SHIFT 7
/* Rx Specific Dma descriptor bits */
#define DMA_RX_CHK_V3PLUS 0x8000
#define DMA_RX_CHK_V12 0x1000
#define DMA_RX_BRDCAST 0x0040
#define DMA_RX_MULT 0x0020
#define DMA_RX_LG 0x0010
#define DMA_RX_NO 0x0008
#define DMA_RX_RXER 0x0004
#define DMA_RX_CRC_ERROR 0x0002
#define DMA_RX_OV 0x0001
#define DMA_RX_FI_MASK 0x001F
#define DMA_RX_FI_SHIFT 0x0007
#define DMA_DESC_ALLOC_MASK 0x00FF
#define DMA_ARBITER_RR 0x00
#define DMA_ARBITER_WRR 0x01
#define DMA_ARBITER_SP 0x02
struct enet_cb {
struct sk_buff *skb;
void __iomem *bd_addr;
DEFINE_DMA_UNMAP_ADDR(dma_addr);
DEFINE_DMA_UNMAP_LEN(dma_len);
};
/* power management mode */
enum bcmgenet_power_mode {
GENET_POWER_CABLE_SENSE = 0,
GENET_POWER_PASSIVE,
GENET_POWER_WOL_MAGIC,
};
struct bcmgenet_priv;
/* We support both runtime GENET detection and compile-time
* to optimize code-paths for a given hardware
*/
enum bcmgenet_version {
GENET_V1 = 1,
GENET_V2,
GENET_V3,
GENET_V4,
GENET_V5
};
#define GENET_IS_V1(p) ((p)->version == GENET_V1)
#define GENET_IS_V2(p) ((p)->version == GENET_V2)
#define GENET_IS_V3(p) ((p)->version == GENET_V3)
#define GENET_IS_V4(p) ((p)->version == GENET_V4)
#define GENET_IS_V5(p) ((p)->version == GENET_V5)
/* Hardware flags */
#define GENET_HAS_40BITS (1 << 0)
#define GENET_HAS_EXT (1 << 1)
#define GENET_HAS_MDIO_INTR (1 << 2)
#define GENET_HAS_MOCA_LINK_DET (1 << 3)
/* BCMGENET hardware parameters, keep this structure nicely aligned
* since it is going to be used in hot paths
*/
struct bcmgenet_hw_params {
u8 tx_queues;
u8 tx_bds_per_q;
u8 rx_queues;
u8 rx_bds_per_q;
u8 bp_in_en_shift;
u32 bp_in_mask;
u8 hfb_filter_cnt;
u8 hfb_filter_size;
u8 qtag_mask;
u16 tbuf_offset;
u32 hfb_offset;
u32 hfb_reg_offset;
u32 rdma_offset;
u32 tdma_offset;
u32 words_per_bd;
u32 flags;
};
struct bcmgenet_skb_cb {
struct enet_cb *first_cb; /* First control block of SKB */
struct enet_cb *last_cb; /* Last control block of SKB */
unsigned int bytes_sent; /* bytes on the wire (no TSB) */
};
#define GENET_CB(skb) ((struct bcmgenet_skb_cb *)((skb)->cb))
struct bcmgenet_tx_ring {
spinlock_t lock; /* ring lock */
struct napi_struct napi; /* NAPI per tx queue */
unsigned long packets;
unsigned long bytes;
unsigned int index; /* ring index */
unsigned int queue; /* queue index */
struct enet_cb *cbs; /* tx ring buffer control block*/
unsigned int size; /* size of each tx ring */
unsigned int clean_ptr; /* Tx ring clean pointer */
unsigned int c_index; /* last consumer index of each ring*/
unsigned int free_bds; /* # of free bds for each ring */
unsigned int write_ptr; /* Tx ring write pointer SW copy */
unsigned int prod_index; /* Tx ring producer index SW copy */
unsigned int cb_ptr; /* Tx ring initial CB ptr */
unsigned int end_ptr; /* Tx ring end CB ptr */
void (*int_enable)(struct bcmgenet_tx_ring *);
void (*int_disable)(struct bcmgenet_tx_ring *);
struct bcmgenet_priv *priv;
};
struct bcmgenet_net_dim {
u16 use_dim;
u16 event_ctr;
unsigned long packets;
unsigned long bytes;
struct dim dim;
};
struct bcmgenet_rx_ring {
struct napi_struct napi; /* Rx NAPI struct */
unsigned long bytes;
unsigned long packets;
unsigned long errors;
unsigned long dropped;
unsigned int index; /* Rx ring index */
struct enet_cb *cbs; /* Rx ring buffer control block */
unsigned int size; /* Rx ring size */
unsigned int c_index; /* Rx last consumer index */
unsigned int read_ptr; /* Rx ring read pointer */
unsigned int cb_ptr; /* Rx ring initial CB ptr */
unsigned int end_ptr; /* Rx ring end CB ptr */
unsigned int old_discards;
struct bcmgenet_net_dim dim;
u32 rx_max_coalesced_frames;
u32 rx_coalesce_usecs;
void (*int_enable)(struct bcmgenet_rx_ring *);
void (*int_disable)(struct bcmgenet_rx_ring *);
struct bcmgenet_priv *priv;
};
enum bcmgenet_rxnfc_state {
BCMGENET_RXNFC_STATE_UNUSED = 0,
BCMGENET_RXNFC_STATE_DISABLED,
BCMGENET_RXNFC_STATE_ENABLED
};
struct bcmgenet_rxnfc_rule {
struct list_head list;
struct ethtool_rx_flow_spec fs;
enum bcmgenet_rxnfc_state state;
};
/* device context */
struct bcmgenet_priv {
void __iomem *base;
enum bcmgenet_version version;
struct net_device *dev;
/* transmit variables */
void __iomem *tx_bds;
struct enet_cb *tx_cbs;
unsigned int num_tx_bds;
struct bcmgenet_tx_ring tx_rings[DESC_INDEX + 1];
/* receive variables */
void __iomem *rx_bds;
struct enet_cb *rx_cbs;
unsigned int num_rx_bds;
unsigned int rx_buf_len;
struct bcmgenet_rxnfc_rule rxnfc_rules[MAX_NUM_OF_FS_RULES];
struct list_head rxnfc_list;
struct bcmgenet_rx_ring rx_rings[DESC_INDEX + 1];
/* other misc variables */
struct bcmgenet_hw_params *hw_params;
/* MDIO bus variables */
wait_queue_head_t wq;
bool internal_phy;
struct device_node *phy_dn;
struct device_node *mdio_dn;
struct mii_bus *mii_bus;
u16 gphy_rev;
struct clk *clk_eee;
bool clk_eee_enabled;
/* PHY device variables */
int old_link;
int old_speed;
int old_duplex;
int old_pause;
phy_interface_t phy_interface;
int phy_addr;
int ext_phy;
/* Interrupt variables */
struct work_struct bcmgenet_irq_work;
int irq0;
int irq1;
int wol_irq;
bool wol_irq_disabled;
/* shared status */
spinlock_t lock;
unsigned int irq0_stat;
/* HW descriptors/checksum variables */
bool crc_fwd_en;
u32 dma_max_burst_length;
u32 msg_enable;
struct clk *clk;
struct platform_device *pdev;
struct platform_device *mii_pdev;
/* WOL */
struct clk *clk_wol;
u32 wolopts;
u8 sopass[SOPASS_MAX];
bool wol_active;
struct bcmgenet_mib_counters mib;
struct ethtool_eee eee;
};
#define GENET_IO_MACRO(name, offset) \
static inline u32 bcmgenet_##name##_readl(struct bcmgenet_priv *priv, \
u32 off) \
{ \
/* MIPS chips strapped for BE will automagically configure the \
* peripheral registers for CPU-native byte order. \
*/ \
if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) \
return __raw_readl(priv->base + offset + off); \
else \
return readl_relaxed(priv->base + offset + off); \
} \
static inline void bcmgenet_##name##_writel(struct bcmgenet_priv *priv, \
u32 val, u32 off) \
{ \
if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) \
__raw_writel(val, priv->base + offset + off); \
else \
writel_relaxed(val, priv->base + offset + off); \
}
GENET_IO_MACRO(ext, GENET_EXT_OFF);
GENET_IO_MACRO(umac, GENET_UMAC_OFF);
GENET_IO_MACRO(sys, GENET_SYS_OFF);
/* interrupt l2 registers accessors */
GENET_IO_MACRO(intrl2_0, GENET_INTRL2_0_OFF);
GENET_IO_MACRO(intrl2_1, GENET_INTRL2_1_OFF);
/* HFB register accessors */
GENET_IO_MACRO(hfb, priv->hw_params->hfb_offset);
/* GENET v2+ HFB control and filter len helpers */
GENET_IO_MACRO(hfb_reg, priv->hw_params->hfb_reg_offset);
/* RBUF register accessors */
GENET_IO_MACRO(rbuf, GENET_RBUF_OFF);
/* MDIO routines */
int bcmgenet_mii_init(struct net_device *dev);
int bcmgenet_mii_config(struct net_device *dev, bool init);
int bcmgenet_mii_probe(struct net_device *dev);
void bcmgenet_mii_exit(struct net_device *dev);
void bcmgenet_phy_power_set(struct net_device *dev, bool enable);
void bcmgenet_mii_setup(struct net_device *dev);
/* Wake-on-LAN routines */
void bcmgenet_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol);
int bcmgenet_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol);
int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv,
enum bcmgenet_power_mode mode);
void bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv,
enum bcmgenet_power_mode mode);
#endif /* __BCMGENET_H__ */

View File

@ -1992,6 +1992,7 @@ static struct sk_buff *bcmgenet_add_tsb(struct net_device *dev,
/* If 64 byte status block enabled, must make sure skb has
* enough headroom for us to insert 64B status block.
*/
BUG_ON(priv->ecdev);
new_skb = skb_realloc_headroom(skb, sizeof(*status));
if (!new_skb) {
dev_kfree_skb_any(skb);

View File

@ -0,0 +1,245 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Broadcom GENET (Gigabit Ethernet) Wake-on-LAN support
*
* Copyright (c) 2014-2020 Broadcom
*/
#define pr_fmt(fmt) "bcmgenet_wol: " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/clk.h>
#include <linux/version.h>
#include <linux/platform_device.h>
#include <net/arp.h>
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/phy.h>
#include "bcmgenet-5.10-ethercat.h"
/* ethtool function - get WOL (Wake on LAN) settings, Only Magic Packet
* Detection is supported through ethtool
*/
void bcmgenet_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
struct device *kdev = &priv->pdev->dev;
if (!device_can_wakeup(kdev)) {
wol->supported = 0;
wol->wolopts = 0;
return;
}
wol->supported = WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER;
wol->wolopts = priv->wolopts;
memset(wol->sopass, 0, sizeof(wol->sopass));
if (wol->wolopts & WAKE_MAGICSECURE)
memcpy(wol->sopass, priv->sopass, sizeof(priv->sopass));
}
/* ethtool function - set WOL (Wake on LAN) settings.
* Only for magic packet detection mode.
*/
int bcmgenet_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
struct device *kdev = &priv->pdev->dev;
if (!device_can_wakeup(kdev))
return -ENOTSUPP;
if (wol->wolopts & ~(WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER))
return -EINVAL;
if (wol->wolopts & WAKE_MAGICSECURE)
memcpy(priv->sopass, wol->sopass, sizeof(priv->sopass));
/* Flag the device and relevant IRQ as wakeup capable */
if (wol->wolopts) {
device_set_wakeup_enable(kdev, 1);
/* Avoid unbalanced enable_irq_wake calls */
if (priv->wol_irq_disabled)
enable_irq_wake(priv->wol_irq);
priv->wol_irq_disabled = false;
} else {
device_set_wakeup_enable(kdev, 0);
/* Avoid unbalanced disable_irq_wake calls */
if (!priv->wol_irq_disabled)
disable_irq_wake(priv->wol_irq);
priv->wol_irq_disabled = true;
}
priv->wolopts = wol->wolopts;
return 0;
}
static int bcmgenet_poll_wol_status(struct bcmgenet_priv *priv)
{
struct net_device *dev = priv->dev;
int retries = 0;
while (!(bcmgenet_rbuf_readl(priv, RBUF_STATUS)
& RBUF_STATUS_WOL)) {
retries++;
if (retries > 5) {
netdev_crit(dev, "polling wol mode timeout\n");
return -ETIMEDOUT;
}
mdelay(1);
}
return retries;
}
static void bcmgenet_set_mpd_password(struct bcmgenet_priv *priv)
{
bcmgenet_umac_writel(priv, get_unaligned_be16(&priv->sopass[0]),
UMAC_MPD_PW_MS);
bcmgenet_umac_writel(priv, get_unaligned_be32(&priv->sopass[2]),
UMAC_MPD_PW_LS);
}
int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv,
enum bcmgenet_power_mode mode)
{
struct net_device *dev = priv->dev;
struct bcmgenet_rxnfc_rule *rule;
u32 reg, hfb_ctrl_reg, hfb_enable = 0;
int retries = 0;
if (mode != GENET_POWER_WOL_MAGIC) {
netif_err(priv, wol, dev, "unsupported mode: %d\n", mode);
return -EINVAL;
}
/* Can't suspend with WoL if MAC is still in reset */
reg = bcmgenet_umac_readl(priv, UMAC_CMD);
if (reg & CMD_SW_RESET)
reg &= ~CMD_SW_RESET;
/* disable RX */
reg &= ~CMD_RX_EN;
bcmgenet_umac_writel(priv, reg, UMAC_CMD);
mdelay(10);
if (priv->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) {
reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
reg |= MPD_EN;
if (priv->wolopts & WAKE_MAGICSECURE) {
bcmgenet_set_mpd_password(priv);
reg |= MPD_PW_EN;
}
bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
}
hfb_ctrl_reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL);
if (priv->wolopts & WAKE_FILTER) {
list_for_each_entry(rule, &priv->rxnfc_list, list)
if (rule->fs.ring_cookie == RX_CLS_FLOW_WAKE)
hfb_enable |= (1 << rule->fs.location);
reg = (hfb_ctrl_reg & ~RBUF_HFB_EN) | RBUF_ACPI_EN;
bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL);
}
/* Do not leave UniMAC in MPD mode only */
retries = bcmgenet_poll_wol_status(priv);
if (retries < 0) {
reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
reg &= ~(MPD_EN | MPD_PW_EN);
bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
bcmgenet_hfb_reg_writel(priv, hfb_ctrl_reg, HFB_CTRL);
return retries;
}
netif_dbg(priv, wol, dev, "MPD WOL-ready status set after %d msec\n",
retries);
clk_prepare_enable(priv->clk_wol);
priv->wol_active = 1;
if (hfb_enable) {
bcmgenet_hfb_reg_writel(priv, hfb_enable,
HFB_FLT_ENABLE_V3PLUS + 4);
hfb_ctrl_reg = RBUF_HFB_EN | RBUF_ACPI_EN;
bcmgenet_hfb_reg_writel(priv, hfb_ctrl_reg, HFB_CTRL);
}
/* Enable CRC forward */
reg = bcmgenet_umac_readl(priv, UMAC_CMD);
priv->crc_fwd_en = 1;
reg |= CMD_CRC_FWD;
/* Receiver must be enabled for WOL MP detection */
reg |= CMD_RX_EN;
bcmgenet_umac_writel(priv, reg, UMAC_CMD);
reg = UMAC_IRQ_MPD_R;
if (hfb_enable)
reg |= UMAC_IRQ_HFB_SM | UMAC_IRQ_HFB_MM;
bcmgenet_intrl2_0_writel(priv, reg, INTRL2_CPU_MASK_CLEAR);
return 0;
}
void bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv,
enum bcmgenet_power_mode mode)
{
u32 reg;
if (mode != GENET_POWER_WOL_MAGIC) {
netif_err(priv, wol, priv->dev, "invalid mode: %d\n", mode);
return;
}
if (!priv->wol_active)
return; /* failed to suspend so skip the rest */
priv->wol_active = 0;
clk_disable_unprepare(priv->clk_wol);
priv->crc_fwd_en = 0;
/* Disable Magic Packet Detection */
if (priv->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) {
reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
if (!(reg & MPD_EN))
return; /* already reset so skip the rest */
reg &= ~(MPD_EN | MPD_PW_EN);
bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
}
/* Disable WAKE_FILTER Detection */
if (priv->wolopts & WAKE_FILTER) {
reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL);
if (!(reg & RBUF_ACPI_EN))
return; /* already reset so skip the rest */
reg &= ~(RBUF_HFB_EN | RBUF_ACPI_EN);
bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL);
}
/* Disable CRC Forward */
reg = bcmgenet_umac_readl(priv, UMAC_CMD);
reg &= ~CMD_CRC_FWD;
bcmgenet_umac_writel(priv, reg, UMAC_CMD);
}

View File

@ -0,0 +1,245 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Broadcom GENET (Gigabit Ethernet) Wake-on-LAN support
*
* Copyright (c) 2014-2020 Broadcom
*/
#define pr_fmt(fmt) "bcmgenet_wol: " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/clk.h>
#include <linux/version.h>
#include <linux/platform_device.h>
#include <net/arp.h>
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/phy.h>
#include "bcmgenet.h"
/* ethtool function - get WOL (Wake on LAN) settings, Only Magic Packet
* Detection is supported through ethtool
*/
void bcmgenet_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
struct device *kdev = &priv->pdev->dev;
if (!device_can_wakeup(kdev)) {
wol->supported = 0;
wol->wolopts = 0;
return;
}
wol->supported = WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER;
wol->wolopts = priv->wolopts;
memset(wol->sopass, 0, sizeof(wol->sopass));
if (wol->wolopts & WAKE_MAGICSECURE)
memcpy(wol->sopass, priv->sopass, sizeof(priv->sopass));
}
/* ethtool function - set WOL (Wake on LAN) settings.
* Only for magic packet detection mode.
*/
int bcmgenet_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
struct device *kdev = &priv->pdev->dev;
if (!device_can_wakeup(kdev))
return -ENOTSUPP;
if (wol->wolopts & ~(WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER))
return -EINVAL;
if (wol->wolopts & WAKE_MAGICSECURE)
memcpy(priv->sopass, wol->sopass, sizeof(priv->sopass));
/* Flag the device and relevant IRQ as wakeup capable */
if (wol->wolopts) {
device_set_wakeup_enable(kdev, 1);
/* Avoid unbalanced enable_irq_wake calls */
if (priv->wol_irq_disabled)
enable_irq_wake(priv->wol_irq);
priv->wol_irq_disabled = false;
} else {
device_set_wakeup_enable(kdev, 0);
/* Avoid unbalanced disable_irq_wake calls */
if (!priv->wol_irq_disabled)
disable_irq_wake(priv->wol_irq);
priv->wol_irq_disabled = true;
}
priv->wolopts = wol->wolopts;
return 0;
}
static int bcmgenet_poll_wol_status(struct bcmgenet_priv *priv)
{
struct net_device *dev = priv->dev;
int retries = 0;
while (!(bcmgenet_rbuf_readl(priv, RBUF_STATUS)
& RBUF_STATUS_WOL)) {
retries++;
if (retries > 5) {
netdev_crit(dev, "polling wol mode timeout\n");
return -ETIMEDOUT;
}
mdelay(1);
}
return retries;
}
static void bcmgenet_set_mpd_password(struct bcmgenet_priv *priv)
{
bcmgenet_umac_writel(priv, get_unaligned_be16(&priv->sopass[0]),
UMAC_MPD_PW_MS);
bcmgenet_umac_writel(priv, get_unaligned_be32(&priv->sopass[2]),
UMAC_MPD_PW_LS);
}
int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv,
enum bcmgenet_power_mode mode)
{
struct net_device *dev = priv->dev;
struct bcmgenet_rxnfc_rule *rule;
u32 reg, hfb_ctrl_reg, hfb_enable = 0;
int retries = 0;
if (mode != GENET_POWER_WOL_MAGIC) {
netif_err(priv, wol, dev, "unsupported mode: %d\n", mode);
return -EINVAL;
}
/* Can't suspend with WoL if MAC is still in reset */
reg = bcmgenet_umac_readl(priv, UMAC_CMD);
if (reg & CMD_SW_RESET)
reg &= ~CMD_SW_RESET;
/* disable RX */
reg &= ~CMD_RX_EN;
bcmgenet_umac_writel(priv, reg, UMAC_CMD);
mdelay(10);
if (priv->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) {
reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
reg |= MPD_EN;
if (priv->wolopts & WAKE_MAGICSECURE) {
bcmgenet_set_mpd_password(priv);
reg |= MPD_PW_EN;
}
bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
}
hfb_ctrl_reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL);
if (priv->wolopts & WAKE_FILTER) {
list_for_each_entry(rule, &priv->rxnfc_list, list)
if (rule->fs.ring_cookie == RX_CLS_FLOW_WAKE)
hfb_enable |= (1 << rule->fs.location);
reg = (hfb_ctrl_reg & ~RBUF_HFB_EN) | RBUF_ACPI_EN;
bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL);
}
/* Do not leave UniMAC in MPD mode only */
retries = bcmgenet_poll_wol_status(priv);
if (retries < 0) {
reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
reg &= ~(MPD_EN | MPD_PW_EN);
bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
bcmgenet_hfb_reg_writel(priv, hfb_ctrl_reg, HFB_CTRL);
return retries;
}
netif_dbg(priv, wol, dev, "MPD WOL-ready status set after %d msec\n",
retries);
clk_prepare_enable(priv->clk_wol);
priv->wol_active = 1;
if (hfb_enable) {
bcmgenet_hfb_reg_writel(priv, hfb_enable,
HFB_FLT_ENABLE_V3PLUS + 4);
hfb_ctrl_reg = RBUF_HFB_EN | RBUF_ACPI_EN;
bcmgenet_hfb_reg_writel(priv, hfb_ctrl_reg, HFB_CTRL);
}
/* Enable CRC forward */
reg = bcmgenet_umac_readl(priv, UMAC_CMD);
priv->crc_fwd_en = 1;
reg |= CMD_CRC_FWD;
/* Receiver must be enabled for WOL MP detection */
reg |= CMD_RX_EN;
bcmgenet_umac_writel(priv, reg, UMAC_CMD);
reg = UMAC_IRQ_MPD_R;
if (hfb_enable)
reg |= UMAC_IRQ_HFB_SM | UMAC_IRQ_HFB_MM;
bcmgenet_intrl2_0_writel(priv, reg, INTRL2_CPU_MASK_CLEAR);
return 0;
}
void bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv,
enum bcmgenet_power_mode mode)
{
u32 reg;
if (mode != GENET_POWER_WOL_MAGIC) {
netif_err(priv, wol, priv->dev, "invalid mode: %d\n", mode);
return;
}
if (!priv->wol_active)
return; /* failed to suspend so skip the rest */
priv->wol_active = 0;
clk_disable_unprepare(priv->clk_wol);
priv->crc_fwd_en = 0;
/* Disable Magic Packet Detection */
if (priv->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) {
reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
if (!(reg & MPD_EN))
return; /* already reset so skip the rest */
reg &= ~(MPD_EN | MPD_PW_EN);
bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
}
/* Disable WAKE_FILTER Detection */
if (priv->wolopts & WAKE_FILTER) {
reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL);
if (!(reg & RBUF_ACPI_EN))
return; /* already reset so skip the rest */
reg &= ~(RBUF_HFB_EN | RBUF_ACPI_EN);
bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL);
}
/* Disable CRC Forward */
reg = bcmgenet_umac_readl(priv, UMAC_CMD);
reg &= ~CMD_CRC_FWD;
bcmgenet_umac_writel(priv, reg, UMAC_CMD);
}

View File

@ -0,0 +1,237 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Broadcom GENET (Gigabit Ethernet) Wake-on-LAN support
*
* Copyright (c) 2014-2020 Broadcom
*/
#define pr_fmt(fmt) "bcmgenet_wol: " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <net/arp.h>
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/phy.h>
#include "bcmgenet-5.14-ethercat.h"
/* ethtool function - get WOL (Wake on LAN) settings, Only Magic Packet
* Detection is supported through ethtool
*/
void bcmgenet_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
wol->supported = WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER;
wol->wolopts = priv->wolopts;
memset(wol->sopass, 0, sizeof(wol->sopass));
if (wol->wolopts & WAKE_MAGICSECURE)
memcpy(wol->sopass, priv->sopass, sizeof(priv->sopass));
}
/* ethtool function - set WOL (Wake on LAN) settings.
* Only for magic packet detection mode.
*/
int bcmgenet_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
struct device *kdev = &priv->pdev->dev;
if (!device_can_wakeup(kdev))
return -ENOTSUPP;
if (wol->wolopts & ~(WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER))
return -EINVAL;
if (wol->wolopts & WAKE_MAGICSECURE)
memcpy(priv->sopass, wol->sopass, sizeof(priv->sopass));
/* Flag the device and relevant IRQ as wakeup capable */
if (wol->wolopts) {
device_set_wakeup_enable(kdev, 1);
/* Avoid unbalanced enable_irq_wake calls */
if (priv->wol_irq_disabled)
enable_irq_wake(priv->wol_irq);
priv->wol_irq_disabled = false;
} else {
device_set_wakeup_enable(kdev, 0);
/* Avoid unbalanced disable_irq_wake calls */
if (!priv->wol_irq_disabled)
disable_irq_wake(priv->wol_irq);
priv->wol_irq_disabled = true;
}
priv->wolopts = wol->wolopts;
return 0;
}
static int bcmgenet_poll_wol_status(struct bcmgenet_priv *priv)
{
struct net_device *dev = priv->dev;
int retries = 0;
while (!(bcmgenet_rbuf_readl(priv, RBUF_STATUS)
& RBUF_STATUS_WOL)) {
retries++;
if (retries > 5) {
netdev_crit(dev, "polling wol mode timeout\n");
return -ETIMEDOUT;
}
mdelay(1);
}
return retries;
}
static void bcmgenet_set_mpd_password(struct bcmgenet_priv *priv)
{
bcmgenet_umac_writel(priv, get_unaligned_be16(&priv->sopass[0]),
UMAC_MPD_PW_MS);
bcmgenet_umac_writel(priv, get_unaligned_be32(&priv->sopass[2]),
UMAC_MPD_PW_LS);
}
int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv,
enum bcmgenet_power_mode mode)
{
struct net_device *dev = priv->dev;
struct bcmgenet_rxnfc_rule *rule;
u32 reg, hfb_ctrl_reg, hfb_enable = 0;
int retries = 0;
if (mode != GENET_POWER_WOL_MAGIC) {
netif_err(priv, wol, dev, "unsupported mode: %d\n", mode);
return -EINVAL;
}
/* Can't suspend with WoL if MAC is still in reset */
reg = bcmgenet_umac_readl(priv, UMAC_CMD);
if (reg & CMD_SW_RESET)
reg &= ~CMD_SW_RESET;
/* disable RX */
reg &= ~CMD_RX_EN;
bcmgenet_umac_writel(priv, reg, UMAC_CMD);
mdelay(10);
if (priv->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) {
reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
reg |= MPD_EN;
if (priv->wolopts & WAKE_MAGICSECURE) {
bcmgenet_set_mpd_password(priv);
reg |= MPD_PW_EN;
}
bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
}
hfb_ctrl_reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL);
if (priv->wolopts & WAKE_FILTER) {
list_for_each_entry(rule, &priv->rxnfc_list, list)
if (rule->fs.ring_cookie == RX_CLS_FLOW_WAKE)
hfb_enable |= (1 << rule->fs.location);
reg = (hfb_ctrl_reg & ~RBUF_HFB_EN) | RBUF_ACPI_EN;
bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL);
}
/* Do not leave UniMAC in MPD mode only */
retries = bcmgenet_poll_wol_status(priv);
if (retries < 0) {
reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
reg &= ~(MPD_EN | MPD_PW_EN);
bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
bcmgenet_hfb_reg_writel(priv, hfb_ctrl_reg, HFB_CTRL);
return retries;
}
netif_dbg(priv, wol, dev, "MPD WOL-ready status set after %d msec\n",
retries);
clk_prepare_enable(priv->clk_wol);
priv->wol_active = 1;
if (hfb_enable) {
bcmgenet_hfb_reg_writel(priv, hfb_enable,
HFB_FLT_ENABLE_V3PLUS + 4);
hfb_ctrl_reg = RBUF_HFB_EN | RBUF_ACPI_EN;
bcmgenet_hfb_reg_writel(priv, hfb_ctrl_reg, HFB_CTRL);
}
/* Enable CRC forward */
reg = bcmgenet_umac_readl(priv, UMAC_CMD);
priv->crc_fwd_en = 1;
reg |= CMD_CRC_FWD;
/* Receiver must be enabled for WOL MP detection */
reg |= CMD_RX_EN;
bcmgenet_umac_writel(priv, reg, UMAC_CMD);
reg = UMAC_IRQ_MPD_R;
if (hfb_enable)
reg |= UMAC_IRQ_HFB_SM | UMAC_IRQ_HFB_MM;
bcmgenet_intrl2_0_writel(priv, reg, INTRL2_CPU_MASK_CLEAR);
return 0;
}
void bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv,
enum bcmgenet_power_mode mode)
{
u32 reg;
if (mode != GENET_POWER_WOL_MAGIC) {
netif_err(priv, wol, priv->dev, "invalid mode: %d\n", mode);
return;
}
if (!priv->wol_active)
return; /* failed to suspend so skip the rest */
priv->wol_active = 0;
clk_disable_unprepare(priv->clk_wol);
priv->crc_fwd_en = 0;
/* Disable Magic Packet Detection */
if (priv->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) {
reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
if (!(reg & MPD_EN))
return; /* already reset so skip the rest */
reg &= ~(MPD_EN | MPD_PW_EN);
bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
}
/* Disable WAKE_FILTER Detection */
if (priv->wolopts & WAKE_FILTER) {
reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL);
if (!(reg & RBUF_ACPI_EN))
return; /* already reset so skip the rest */
reg &= ~(RBUF_HFB_EN | RBUF_ACPI_EN);
bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL);
}
/* Disable CRC Forward */
reg = bcmgenet_umac_readl(priv, UMAC_CMD);
reg &= ~CMD_CRC_FWD;
bcmgenet_umac_writel(priv, reg, UMAC_CMD);
}

View File

@ -0,0 +1,237 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Broadcom GENET (Gigabit Ethernet) Wake-on-LAN support
*
* Copyright (c) 2014-2020 Broadcom
*/
#define pr_fmt(fmt) "bcmgenet_wol: " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <net/arp.h>
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/phy.h>
#include "bcmgenet.h"
/* ethtool function - get WOL (Wake on LAN) settings, Only Magic Packet
* Detection is supported through ethtool
*/
void bcmgenet_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
wol->supported = WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER;
wol->wolopts = priv->wolopts;
memset(wol->sopass, 0, sizeof(wol->sopass));
if (wol->wolopts & WAKE_MAGICSECURE)
memcpy(wol->sopass, priv->sopass, sizeof(priv->sopass));
}
/* ethtool function - set WOL (Wake on LAN) settings.
* Only for magic packet detection mode.
*/
int bcmgenet_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
struct device *kdev = &priv->pdev->dev;
if (!device_can_wakeup(kdev))
return -ENOTSUPP;
if (wol->wolopts & ~(WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER))
return -EINVAL;
if (wol->wolopts & WAKE_MAGICSECURE)
memcpy(priv->sopass, wol->sopass, sizeof(priv->sopass));
/* Flag the device and relevant IRQ as wakeup capable */
if (wol->wolopts) {
device_set_wakeup_enable(kdev, 1);
/* Avoid unbalanced enable_irq_wake calls */
if (priv->wol_irq_disabled)
enable_irq_wake(priv->wol_irq);
priv->wol_irq_disabled = false;
} else {
device_set_wakeup_enable(kdev, 0);
/* Avoid unbalanced disable_irq_wake calls */
if (!priv->wol_irq_disabled)
disable_irq_wake(priv->wol_irq);
priv->wol_irq_disabled = true;
}
priv->wolopts = wol->wolopts;
return 0;
}
static int bcmgenet_poll_wol_status(struct bcmgenet_priv *priv)
{
struct net_device *dev = priv->dev;
int retries = 0;
while (!(bcmgenet_rbuf_readl(priv, RBUF_STATUS)
& RBUF_STATUS_WOL)) {
retries++;
if (retries > 5) {
netdev_crit(dev, "polling wol mode timeout\n");
return -ETIMEDOUT;
}
mdelay(1);
}
return retries;
}
static void bcmgenet_set_mpd_password(struct bcmgenet_priv *priv)
{
bcmgenet_umac_writel(priv, get_unaligned_be16(&priv->sopass[0]),
UMAC_MPD_PW_MS);
bcmgenet_umac_writel(priv, get_unaligned_be32(&priv->sopass[2]),
UMAC_MPD_PW_LS);
}
int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv,
enum bcmgenet_power_mode mode)
{
struct net_device *dev = priv->dev;
struct bcmgenet_rxnfc_rule *rule;
u32 reg, hfb_ctrl_reg, hfb_enable = 0;
int retries = 0;
if (mode != GENET_POWER_WOL_MAGIC) {
netif_err(priv, wol, dev, "unsupported mode: %d\n", mode);
return -EINVAL;
}
/* Can't suspend with WoL if MAC is still in reset */
reg = bcmgenet_umac_readl(priv, UMAC_CMD);
if (reg & CMD_SW_RESET)
reg &= ~CMD_SW_RESET;
/* disable RX */
reg &= ~CMD_RX_EN;
bcmgenet_umac_writel(priv, reg, UMAC_CMD);
mdelay(10);
if (priv->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) {
reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
reg |= MPD_EN;
if (priv->wolopts & WAKE_MAGICSECURE) {
bcmgenet_set_mpd_password(priv);
reg |= MPD_PW_EN;
}
bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
}
hfb_ctrl_reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL);
if (priv->wolopts & WAKE_FILTER) {
list_for_each_entry(rule, &priv->rxnfc_list, list)
if (rule->fs.ring_cookie == RX_CLS_FLOW_WAKE)
hfb_enable |= (1 << rule->fs.location);
reg = (hfb_ctrl_reg & ~RBUF_HFB_EN) | RBUF_ACPI_EN;
bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL);
}
/* Do not leave UniMAC in MPD mode only */
retries = bcmgenet_poll_wol_status(priv);
if (retries < 0) {
reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
reg &= ~(MPD_EN | MPD_PW_EN);
bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
bcmgenet_hfb_reg_writel(priv, hfb_ctrl_reg, HFB_CTRL);
return retries;
}
netif_dbg(priv, wol, dev, "MPD WOL-ready status set after %d msec\n",
retries);
clk_prepare_enable(priv->clk_wol);
priv->wol_active = 1;
if (hfb_enable) {
bcmgenet_hfb_reg_writel(priv, hfb_enable,
HFB_FLT_ENABLE_V3PLUS + 4);
hfb_ctrl_reg = RBUF_HFB_EN | RBUF_ACPI_EN;
bcmgenet_hfb_reg_writel(priv, hfb_ctrl_reg, HFB_CTRL);
}
/* Enable CRC forward */
reg = bcmgenet_umac_readl(priv, UMAC_CMD);
priv->crc_fwd_en = 1;
reg |= CMD_CRC_FWD;
/* Receiver must be enabled for WOL MP detection */
reg |= CMD_RX_EN;
bcmgenet_umac_writel(priv, reg, UMAC_CMD);
reg = UMAC_IRQ_MPD_R;
if (hfb_enable)
reg |= UMAC_IRQ_HFB_SM | UMAC_IRQ_HFB_MM;
bcmgenet_intrl2_0_writel(priv, reg, INTRL2_CPU_MASK_CLEAR);
return 0;
}
void bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv,
enum bcmgenet_power_mode mode)
{
u32 reg;
if (mode != GENET_POWER_WOL_MAGIC) {
netif_err(priv, wol, priv->dev, "invalid mode: %d\n", mode);
return;
}
if (!priv->wol_active)
return; /* failed to suspend so skip the rest */
priv->wol_active = 0;
clk_disable_unprepare(priv->clk_wol);
priv->crc_fwd_en = 0;
/* Disable Magic Packet Detection */
if (priv->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) {
reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
if (!(reg & MPD_EN))
return; /* already reset so skip the rest */
reg &= ~(MPD_EN | MPD_PW_EN);
bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
}
/* Disable WAKE_FILTER Detection */
if (priv->wolopts & WAKE_FILTER) {
reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL);
if (!(reg & RBUF_ACPI_EN))
return; /* already reset so skip the rest */
reg &= ~(RBUF_HFB_EN | RBUF_ACPI_EN);
bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL);
}
/* Disable CRC Forward */
reg = bcmgenet_umac_readl(priv, UMAC_CMD);
reg &= ~CMD_CRC_FWD;
bcmgenet_umac_writel(priv, reg, UMAC_CMD);
}

View File

@ -0,0 +1,637 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Broadcom GENET MDIO routines
*
* Copyright (c) 2014-2017 Broadcom
*/
#include <linux/acpi.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/wait.h>
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <linux/bitops.h>
#include <linux/netdevice.h>
#include <linux/platform_device.h>
#include <linux/phy.h>
#include <linux/phy_fixed.h>
#include <linux/brcmphy.h>
#include <linux/of.h>
#include <linux/of_net.h>
#include <linux/of_mdio.h>
#include <linux/platform_data/bcmgenet.h>
#include <linux/platform_data/mdio-bcm-unimac.h>
#include "bcmgenet-5.10-ethercat.h"
/* setup netdev link state when PHY link status change and
* update UMAC and RGMII block when link up
*/
void bcmgenet_mii_setup(struct net_device *dev)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
struct phy_device *phydev = dev->phydev;
u32 reg, cmd_bits = 0;
bool status_changed = false;
if (priv->old_link != phydev->link) {
status_changed = true;
priv->old_link = phydev->link;
}
if (phydev->link) {
/* check speed/duplex/pause changes */
if (priv->old_speed != phydev->speed) {
status_changed = true;
priv->old_speed = phydev->speed;
}
if (priv->old_duplex != phydev->duplex) {
status_changed = true;
priv->old_duplex = phydev->duplex;
}
if (priv->old_pause != phydev->pause) {
status_changed = true;
priv->old_pause = phydev->pause;
}
/* done if nothing has changed */
if (!status_changed)
return;
/* speed */
if (phydev->speed == SPEED_1000)
cmd_bits = UMAC_SPEED_1000;
else if (phydev->speed == SPEED_100)
cmd_bits = UMAC_SPEED_100;
else
cmd_bits = UMAC_SPEED_10;
cmd_bits <<= CMD_SPEED_SHIFT;
/* duplex */
if (phydev->duplex != DUPLEX_FULL)
cmd_bits |= CMD_HD_EN;
/* pause capability */
if (!phydev->pause)
cmd_bits |= CMD_RX_PAUSE_IGNORE | CMD_TX_PAUSE_IGNORE;
/*
* Program UMAC and RGMII block based on established
* link speed, duplex, and pause. The speed set in
* umac->cmd tell RGMII block which clock to use for
* transmit -- 25MHz(100Mbps) or 125MHz(1Gbps).
* Receive clock is provided by the PHY.
*/
reg = bcmgenet_ext_readl(priv, EXT_RGMII_OOB_CTRL);
reg &= ~OOB_DISABLE;
reg |= RGMII_LINK;
bcmgenet_ext_writel(priv, reg, EXT_RGMII_OOB_CTRL);
reg = bcmgenet_umac_readl(priv, UMAC_CMD);
reg &= ~((CMD_SPEED_MASK << CMD_SPEED_SHIFT) |
CMD_HD_EN |
CMD_RX_PAUSE_IGNORE | CMD_TX_PAUSE_IGNORE);
reg |= cmd_bits;
if (reg & CMD_SW_RESET) {
reg &= ~CMD_SW_RESET;
bcmgenet_umac_writel(priv, reg, UMAC_CMD);
udelay(2);
reg |= CMD_TX_EN | CMD_RX_EN;
}
bcmgenet_umac_writel(priv, reg, UMAC_CMD);
} else {
/* done if nothing has changed */
if (!status_changed)
return;
/* needed for MoCA fixed PHY to reflect correct link status */
netif_carrier_off(dev);
}
if (priv->ecdev)
ecdev_set_link(priv->ecdev, phydev->link);
phy_print_status(phydev);
}
static int bcmgenet_fixed_phy_link_update(struct net_device *dev,
struct fixed_phy_status *status)
{
struct bcmgenet_priv *priv;
u32 reg;
if (dev && dev->phydev && status) {
priv = netdev_priv(dev);
reg = bcmgenet_umac_readl(priv, UMAC_MODE);
status->link = !!(reg & MODE_LINK_STATUS);
}
return 0;
}
void bcmgenet_phy_power_set(struct net_device *dev, bool enable)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
u32 reg = 0;
/* EXT_GPHY_CTRL is only valid for GENETv4 and onward */
if (GENET_IS_V4(priv)) {
reg = bcmgenet_ext_readl(priv, EXT_GPHY_CTRL);
if (enable) {
reg &= ~EXT_CK25_DIS;
bcmgenet_ext_writel(priv, reg, EXT_GPHY_CTRL);
mdelay(1);
reg &= ~(EXT_CFG_IDDQ_BIAS | EXT_CFG_PWR_DOWN);
reg |= EXT_GPHY_RESET;
bcmgenet_ext_writel(priv, reg, EXT_GPHY_CTRL);
mdelay(1);
reg &= ~EXT_GPHY_RESET;
} else {
reg |= EXT_CFG_IDDQ_BIAS | EXT_CFG_PWR_DOWN |
EXT_GPHY_RESET;
bcmgenet_ext_writel(priv, reg, EXT_GPHY_CTRL);
mdelay(1);
reg |= EXT_CK25_DIS;
}
bcmgenet_ext_writel(priv, reg, EXT_GPHY_CTRL);
udelay(60);
} else {
mdelay(1);
}
}
static void bcmgenet_moca_phy_setup(struct bcmgenet_priv *priv)
{
u32 reg;
if (!GENET_IS_V5(priv)) {
/* Speed settings are set in bcmgenet_mii_setup() */
reg = bcmgenet_sys_readl(priv, SYS_PORT_CTRL);
reg |= LED_ACT_SOURCE_MAC;
bcmgenet_sys_writel(priv, reg, SYS_PORT_CTRL);
}
if (priv->hw_params->flags & GENET_HAS_MOCA_LINK_DET)
fixed_phy_set_link_update(priv->dev->phydev,
bcmgenet_fixed_phy_link_update);
}
int bcmgenet_mii_config(struct net_device *dev, bool init)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
struct phy_device *phydev = dev->phydev;
struct device *kdev = &priv->pdev->dev;
const char *phy_name = NULL;
u32 id_mode_dis = 0;
u32 port_ctrl;
u32 reg;
switch (priv->phy_interface) {
case PHY_INTERFACE_MODE_INTERNAL:
phy_name = "internal PHY";
fallthrough;
case PHY_INTERFACE_MODE_MOCA:
/* Irrespective of the actually configured PHY speed (100 or
* 1000) GENETv4 only has an internal GPHY so we will just end
* up masking the Gigabit features from what we support, not
* switching to the EPHY
*/
if (GENET_IS_V4(priv))
port_ctrl = PORT_MODE_INT_GPHY;
else
port_ctrl = PORT_MODE_INT_EPHY;
if (!phy_name) {
phy_name = "MoCA";
bcmgenet_moca_phy_setup(priv);
}
break;
case PHY_INTERFACE_MODE_MII:
phy_name = "external MII";
phy_set_max_speed(phydev, SPEED_100);
port_ctrl = PORT_MODE_EXT_EPHY;
break;
case PHY_INTERFACE_MODE_REVMII:
phy_name = "external RvMII";
/* of_mdiobus_register took care of reading the 'max-speed'
* PHY property for us, effectively limiting the PHY supported
* capabilities, use that knowledge to also configure the
* Reverse MII interface correctly.
*/
if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
dev->phydev->supported))
port_ctrl = PORT_MODE_EXT_RVMII_50;
else
port_ctrl = PORT_MODE_EXT_RVMII_25;
break;
case PHY_INTERFACE_MODE_RGMII:
/* RGMII_NO_ID: TXC transitions at the same time as TXD
* (requires PCB or receiver-side delay)
*
* ID is implicitly disabled for 100Mbps (RG)MII operation.
*/
phy_name = "external RGMII (no delay)";
id_mode_dis = BIT(16);
port_ctrl = PORT_MODE_EXT_GPHY;
break;
case PHY_INTERFACE_MODE_RGMII_TXID:
/* RGMII_TXID: Add 2ns delay on TXC (90 degree shift) */
phy_name = "external RGMII (TX delay)";
port_ctrl = PORT_MODE_EXT_GPHY;
break;
case PHY_INTERFACE_MODE_RGMII_RXID:
phy_name = "external RGMII (RX delay)";
port_ctrl = PORT_MODE_EXT_GPHY;
break;
default:
dev_err(kdev, "unknown phy mode: %d\n", priv->phy_interface);
return -EINVAL;
}
bcmgenet_sys_writel(priv, port_ctrl, SYS_PORT_CTRL);
priv->ext_phy = !priv->internal_phy &&
(priv->phy_interface != PHY_INTERFACE_MODE_MOCA);
/* This is an external PHY (xMII), so we need to enable the RGMII
* block for the interface to work
*/
if (priv->ext_phy) {
reg = bcmgenet_ext_readl(priv, EXT_RGMII_OOB_CTRL);
reg &= ~ID_MODE_DIS;
reg |= id_mode_dis;
if (GENET_IS_V1(priv) || GENET_IS_V2(priv) || GENET_IS_V3(priv))
reg |= RGMII_MODE_EN_V123;
else
reg |= RGMII_MODE_EN;
bcmgenet_ext_writel(priv, reg, EXT_RGMII_OOB_CTRL);
}
if (init)
dev_info(kdev, "configuring instance for %s\n", phy_name);
return 0;
}
int bcmgenet_mii_probe(struct net_device *dev)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
struct device *kdev = &priv->pdev->dev;
struct device_node *dn = kdev->of_node;
struct phy_device *phydev;
u32 phy_flags = 0;
int ret;
/* Communicate the integrated PHY revision */
if (priv->internal_phy)
phy_flags = priv->gphy_rev;
/* Initialize link state variables that bcmgenet_mii_setup() uses */
priv->old_link = -1;
priv->old_speed = -1;
priv->old_duplex = -1;
priv->old_pause = -1;
if (dn) {
phydev = of_phy_connect(dev, priv->phy_dn, bcmgenet_mii_setup,
phy_flags, priv->phy_interface);
if (!phydev) {
pr_err("could not attach to PHY\n");
return -ENODEV;
}
} else {
if (has_acpi_companion(kdev)) {
char mdio_bus_id[MII_BUS_ID_SIZE];
struct mii_bus *unimacbus;
snprintf(mdio_bus_id, MII_BUS_ID_SIZE, "%s-%d",
UNIMAC_MDIO_DRV_NAME, priv->pdev->id);
unimacbus = mdio_find_bus(mdio_bus_id);
if (!unimacbus) {
pr_err("Unable to find mii\n");
return -ENODEV;
}
phydev = phy_find_first(unimacbus);
put_device(&unimacbus->dev);
if (!phydev) {
pr_err("Unable to find PHY\n");
return -ENODEV;
}
} else {
phydev = dev->phydev;
}
phydev->dev_flags = phy_flags;
ret = phy_connect_direct(dev, phydev, bcmgenet_mii_setup,
priv->phy_interface);
if (ret) {
pr_err("could not attach to PHY\n");
return -ENODEV;
}
}
/* Configure port multiplexer based on what the probed PHY device since
* reading the 'max-speed' property determines the maximum supported
* PHY speed which is needed for bcmgenet_mii_config() to configure
* things appropriately.
*/
ret = bcmgenet_mii_config(dev, true);
if (ret) {
phy_disconnect(dev->phydev);
return ret;
}
linkmode_copy(phydev->advertising, phydev->supported);
/* The internal PHY has its link interrupts routed to the
* Ethernet MAC ISRs. On GENETv5 there is a hardware issue
* that prevents the signaling of link UP interrupts when
* the link operates at 10Mbps, so fallback to polling for
* those versions of GENET.
*/
if (priv->internal_phy && !GENET_IS_V5(priv))
dev->phydev->irq = PHY_IGNORE_INTERRUPT;
return 0;
}
static struct device_node *bcmgenet_mii_of_find_mdio(struct bcmgenet_priv *priv)
{
struct device_node *dn = priv->pdev->dev.of_node;
struct device *kdev = &priv->pdev->dev;
char *compat;
compat = kasprintf(GFP_KERNEL, "brcm,genet-mdio-v%d", priv->version);
if (!compat)
return NULL;
priv->mdio_dn = of_get_compatible_child(dn, compat);
kfree(compat);
if (!priv->mdio_dn) {
dev_err(kdev, "unable to find MDIO bus node\n");
return NULL;
}
return priv->mdio_dn;
}
static void bcmgenet_mii_pdata_init(struct bcmgenet_priv *priv,
struct unimac_mdio_pdata *ppd)
{
struct device *kdev = &priv->pdev->dev;
struct bcmgenet_platform_data *pd = kdev->platform_data;
if (pd->phy_interface != PHY_INTERFACE_MODE_MOCA && pd->mdio_enabled) {
/*
* Internal or external PHY with MDIO access
*/
if (pd->phy_address >= 0 && pd->phy_address < PHY_MAX_ADDR)
ppd->phy_mask = 1 << pd->phy_address;
else
ppd->phy_mask = 0;
}
}
static int bcmgenet_mii_wait(void *wait_func_data)
{
struct bcmgenet_priv *priv = wait_func_data;
wait_event_timeout(priv->wq,
!(bcmgenet_umac_readl(priv, UMAC_MDIO_CMD)
& MDIO_START_BUSY),
HZ / 100);
return 0;
}
static int bcmgenet_mii_register(struct bcmgenet_priv *priv)
{
struct platform_device *pdev = priv->pdev;
struct bcmgenet_platform_data *pdata = pdev->dev.platform_data;
struct device_node *dn = pdev->dev.of_node;
struct unimac_mdio_pdata ppd;
struct platform_device *ppdev;
struct resource *pres, res;
int id, ret;
pres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!pres) {
dev_err(&pdev->dev, "Invalid resource\n");
return -EINVAL;
}
memset(&res, 0, sizeof(res));
memset(&ppd, 0, sizeof(ppd));
ppd.wait_func = bcmgenet_mii_wait;
ppd.wait_func_data = priv;
ppd.bus_name = "bcmgenet MII bus";
/* Unimac MDIO bus controller starts at UniMAC offset + MDIO_CMD
* and is 2 * 32-bits word long, 8 bytes total.
*/
res.start = pres->start + GENET_UMAC_OFF + UMAC_MDIO_CMD;
res.end = res.start + 8;
res.flags = IORESOURCE_MEM;
if (dn)
id = of_alias_get_id(dn, "eth");
else
id = pdev->id;
ppdev = platform_device_alloc(UNIMAC_MDIO_DRV_NAME, id);
if (!ppdev)
return -ENOMEM;
/* Retain this platform_device pointer for later cleanup */
priv->mii_pdev = ppdev;
ppdev->dev.parent = &pdev->dev;
if (dn)
ppdev->dev.of_node = bcmgenet_mii_of_find_mdio(priv);
else if (pdata)
bcmgenet_mii_pdata_init(priv, &ppd);
else
ppd.phy_mask = ~0;
ret = platform_device_add_resources(ppdev, &res, 1);
if (ret)
goto out;
ret = platform_device_add_data(ppdev, &ppd, sizeof(ppd));
if (ret)
goto out;
ret = platform_device_add(ppdev);
if (ret)
goto out;
return 0;
out:
platform_device_put(ppdev);
return ret;
}
static int bcmgenet_phy_interface_init(struct bcmgenet_priv *priv)
{
struct device *kdev = &priv->pdev->dev;
int phy_mode = device_get_phy_mode(kdev);
if (phy_mode < 0) {
dev_err(kdev, "invalid PHY mode property\n");
return phy_mode;
}
priv->phy_interface = phy_mode;
/* We need to specifically look up whether this PHY interface is
* internal or not *before* we even try to probe the PHY driver
* over MDIO as we may have shut down the internal PHY for power
* saving purposes.
*/
if (priv->phy_interface == PHY_INTERFACE_MODE_INTERNAL)
priv->internal_phy = true;
return 0;
}
static int bcmgenet_mii_of_init(struct bcmgenet_priv *priv)
{
struct device_node *dn = priv->pdev->dev.of_node;
struct phy_device *phydev;
int ret;
/* Fetch the PHY phandle */
priv->phy_dn = of_parse_phandle(dn, "phy-handle", 0);
/* In the case of a fixed PHY, the DT node associated
* to the PHY is the Ethernet MAC DT node.
*/
if (!priv->phy_dn && of_phy_is_fixed_link(dn)) {
ret = of_phy_register_fixed_link(dn);
if (ret)
return ret;
priv->phy_dn = of_node_get(dn);
}
/* Get the link mode */
ret = bcmgenet_phy_interface_init(priv);
if (ret)
return ret;
/* Make sure we initialize MoCA PHYs with a link down */
if (priv->phy_interface == PHY_INTERFACE_MODE_MOCA) {
phydev = of_phy_find_device(dn);
if (phydev) {
phydev->link = 0;
put_device(&phydev->mdio.dev);
}
}
return 0;
}
static int bcmgenet_mii_pd_init(struct bcmgenet_priv *priv)
{
struct device *kdev = &priv->pdev->dev;
struct bcmgenet_platform_data *pd = kdev->platform_data;
char phy_name[MII_BUS_ID_SIZE + 3];
char mdio_bus_id[MII_BUS_ID_SIZE];
struct phy_device *phydev;
snprintf(mdio_bus_id, MII_BUS_ID_SIZE, "%s-%d",
UNIMAC_MDIO_DRV_NAME, priv->pdev->id);
if (pd->phy_interface != PHY_INTERFACE_MODE_MOCA && pd->mdio_enabled) {
snprintf(phy_name, MII_BUS_ID_SIZE, PHY_ID_FMT,
mdio_bus_id, pd->phy_address);
/*
* Internal or external PHY with MDIO access
*/
phydev = phy_attach(priv->dev, phy_name, pd->phy_interface);
if (!phydev) {
dev_err(kdev, "failed to register PHY device\n");
return -ENODEV;
}
} else {
/*
* MoCA port or no MDIO access.
* Use fixed PHY to represent the link layer.
*/
struct fixed_phy_status fphy_status = {
.link = 1,
.speed = pd->phy_speed,
.duplex = pd->phy_duplex,
.pause = 0,
.asym_pause = 0,
};
phydev = fixed_phy_register(PHY_POLL, &fphy_status, NULL);
if (!phydev || IS_ERR(phydev)) {
dev_err(kdev, "failed to register fixed PHY device\n");
return -ENODEV;
}
/* Make sure we initialize MoCA PHYs with a link down */
phydev->link = 0;
}
priv->phy_interface = pd->phy_interface;
return 0;
}
static int bcmgenet_mii_bus_init(struct bcmgenet_priv *priv)
{
struct device *kdev = &priv->pdev->dev;
struct device_node *dn = kdev->of_node;
if (dn)
return bcmgenet_mii_of_init(priv);
else if (has_acpi_companion(kdev))
return bcmgenet_phy_interface_init(priv);
else
return bcmgenet_mii_pd_init(priv);
}
int bcmgenet_mii_init(struct net_device *dev)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
int ret;
ret = bcmgenet_mii_register(priv);
if (ret)
return ret;
ret = bcmgenet_mii_bus_init(priv);
if (ret)
goto out;
return 0;
out:
bcmgenet_mii_exit(dev);
return ret;
}
void bcmgenet_mii_exit(struct net_device *dev)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
struct device_node *dn = priv->pdev->dev.of_node;
if (of_phy_is_fixed_link(dn))
of_phy_deregister_fixed_link(dn);
of_node_put(priv->phy_dn);
platform_device_unregister(priv->mii_pdev);
}

View File

@ -0,0 +1,635 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Broadcom GENET MDIO routines
*
* Copyright (c) 2014-2017 Broadcom
*/
#include <linux/acpi.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/wait.h>
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <linux/bitops.h>
#include <linux/netdevice.h>
#include <linux/platform_device.h>
#include <linux/phy.h>
#include <linux/phy_fixed.h>
#include <linux/brcmphy.h>
#include <linux/of.h>
#include <linux/of_net.h>
#include <linux/of_mdio.h>
#include <linux/platform_data/bcmgenet.h>
#include <linux/platform_data/mdio-bcm-unimac.h>
#include "bcmgenet.h"
/* setup netdev link state when PHY link status change and
* update UMAC and RGMII block when link up
*/
void bcmgenet_mii_setup(struct net_device *dev)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
struct phy_device *phydev = dev->phydev;
u32 reg, cmd_bits = 0;
bool status_changed = false;
if (priv->old_link != phydev->link) {
status_changed = true;
priv->old_link = phydev->link;
}
if (phydev->link) {
/* check speed/duplex/pause changes */
if (priv->old_speed != phydev->speed) {
status_changed = true;
priv->old_speed = phydev->speed;
}
if (priv->old_duplex != phydev->duplex) {
status_changed = true;
priv->old_duplex = phydev->duplex;
}
if (priv->old_pause != phydev->pause) {
status_changed = true;
priv->old_pause = phydev->pause;
}
/* done if nothing has changed */
if (!status_changed)
return;
/* speed */
if (phydev->speed == SPEED_1000)
cmd_bits = UMAC_SPEED_1000;
else if (phydev->speed == SPEED_100)
cmd_bits = UMAC_SPEED_100;
else
cmd_bits = UMAC_SPEED_10;
cmd_bits <<= CMD_SPEED_SHIFT;
/* duplex */
if (phydev->duplex != DUPLEX_FULL)
cmd_bits |= CMD_HD_EN;
/* pause capability */
if (!phydev->pause)
cmd_bits |= CMD_RX_PAUSE_IGNORE | CMD_TX_PAUSE_IGNORE;
/*
* Program UMAC and RGMII block based on established
* link speed, duplex, and pause. The speed set in
* umac->cmd tell RGMII block which clock to use for
* transmit -- 25MHz(100Mbps) or 125MHz(1Gbps).
* Receive clock is provided by the PHY.
*/
reg = bcmgenet_ext_readl(priv, EXT_RGMII_OOB_CTRL);
reg &= ~OOB_DISABLE;
reg |= RGMII_LINK;
bcmgenet_ext_writel(priv, reg, EXT_RGMII_OOB_CTRL);
reg = bcmgenet_umac_readl(priv, UMAC_CMD);
reg &= ~((CMD_SPEED_MASK << CMD_SPEED_SHIFT) |
CMD_HD_EN |
CMD_RX_PAUSE_IGNORE | CMD_TX_PAUSE_IGNORE);
reg |= cmd_bits;
if (reg & CMD_SW_RESET) {
reg &= ~CMD_SW_RESET;
bcmgenet_umac_writel(priv, reg, UMAC_CMD);
udelay(2);
reg |= CMD_TX_EN | CMD_RX_EN;
}
bcmgenet_umac_writel(priv, reg, UMAC_CMD);
} else {
/* done if nothing has changed */
if (!status_changed)
return;
/* needed for MoCA fixed PHY to reflect correct link status */
netif_carrier_off(dev);
}
phy_print_status(phydev);
}
static int bcmgenet_fixed_phy_link_update(struct net_device *dev,
struct fixed_phy_status *status)
{
struct bcmgenet_priv *priv;
u32 reg;
if (dev && dev->phydev && status) {
priv = netdev_priv(dev);
reg = bcmgenet_umac_readl(priv, UMAC_MODE);
status->link = !!(reg & MODE_LINK_STATUS);
}
return 0;
}
void bcmgenet_phy_power_set(struct net_device *dev, bool enable)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
u32 reg = 0;
/* EXT_GPHY_CTRL is only valid for GENETv4 and onward */
if (GENET_IS_V4(priv)) {
reg = bcmgenet_ext_readl(priv, EXT_GPHY_CTRL);
if (enable) {
reg &= ~EXT_CK25_DIS;
bcmgenet_ext_writel(priv, reg, EXT_GPHY_CTRL);
mdelay(1);
reg &= ~(EXT_CFG_IDDQ_BIAS | EXT_CFG_PWR_DOWN);
reg |= EXT_GPHY_RESET;
bcmgenet_ext_writel(priv, reg, EXT_GPHY_CTRL);
mdelay(1);
reg &= ~EXT_GPHY_RESET;
} else {
reg |= EXT_CFG_IDDQ_BIAS | EXT_CFG_PWR_DOWN |
EXT_GPHY_RESET;
bcmgenet_ext_writel(priv, reg, EXT_GPHY_CTRL);
mdelay(1);
reg |= EXT_CK25_DIS;
}
bcmgenet_ext_writel(priv, reg, EXT_GPHY_CTRL);
udelay(60);
} else {
mdelay(1);
}
}
static void bcmgenet_moca_phy_setup(struct bcmgenet_priv *priv)
{
u32 reg;
if (!GENET_IS_V5(priv)) {
/* Speed settings are set in bcmgenet_mii_setup() */
reg = bcmgenet_sys_readl(priv, SYS_PORT_CTRL);
reg |= LED_ACT_SOURCE_MAC;
bcmgenet_sys_writel(priv, reg, SYS_PORT_CTRL);
}
if (priv->hw_params->flags & GENET_HAS_MOCA_LINK_DET)
fixed_phy_set_link_update(priv->dev->phydev,
bcmgenet_fixed_phy_link_update);
}
int bcmgenet_mii_config(struct net_device *dev, bool init)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
struct phy_device *phydev = dev->phydev;
struct device *kdev = &priv->pdev->dev;
const char *phy_name = NULL;
u32 id_mode_dis = 0;
u32 port_ctrl;
u32 reg;
switch (priv->phy_interface) {
case PHY_INTERFACE_MODE_INTERNAL:
phy_name = "internal PHY";
fallthrough;
case PHY_INTERFACE_MODE_MOCA:
/* Irrespective of the actually configured PHY speed (100 or
* 1000) GENETv4 only has an internal GPHY so we will just end
* up masking the Gigabit features from what we support, not
* switching to the EPHY
*/
if (GENET_IS_V4(priv))
port_ctrl = PORT_MODE_INT_GPHY;
else
port_ctrl = PORT_MODE_INT_EPHY;
if (!phy_name) {
phy_name = "MoCA";
bcmgenet_moca_phy_setup(priv);
}
break;
case PHY_INTERFACE_MODE_MII:
phy_name = "external MII";
phy_set_max_speed(phydev, SPEED_100);
port_ctrl = PORT_MODE_EXT_EPHY;
break;
case PHY_INTERFACE_MODE_REVMII:
phy_name = "external RvMII";
/* of_mdiobus_register took care of reading the 'max-speed'
* PHY property for us, effectively limiting the PHY supported
* capabilities, use that knowledge to also configure the
* Reverse MII interface correctly.
*/
if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
dev->phydev->supported))
port_ctrl = PORT_MODE_EXT_RVMII_50;
else
port_ctrl = PORT_MODE_EXT_RVMII_25;
break;
case PHY_INTERFACE_MODE_RGMII:
/* RGMII_NO_ID: TXC transitions at the same time as TXD
* (requires PCB or receiver-side delay)
*
* ID is implicitly disabled for 100Mbps (RG)MII operation.
*/
phy_name = "external RGMII (no delay)";
id_mode_dis = BIT(16);
port_ctrl = PORT_MODE_EXT_GPHY;
break;
case PHY_INTERFACE_MODE_RGMII_TXID:
/* RGMII_TXID: Add 2ns delay on TXC (90 degree shift) */
phy_name = "external RGMII (TX delay)";
port_ctrl = PORT_MODE_EXT_GPHY;
break;
case PHY_INTERFACE_MODE_RGMII_RXID:
phy_name = "external RGMII (RX delay)";
port_ctrl = PORT_MODE_EXT_GPHY;
break;
default:
dev_err(kdev, "unknown phy mode: %d\n", priv->phy_interface);
return -EINVAL;
}
bcmgenet_sys_writel(priv, port_ctrl, SYS_PORT_CTRL);
priv->ext_phy = !priv->internal_phy &&
(priv->phy_interface != PHY_INTERFACE_MODE_MOCA);
/* This is an external PHY (xMII), so we need to enable the RGMII
* block for the interface to work
*/
if (priv->ext_phy) {
reg = bcmgenet_ext_readl(priv, EXT_RGMII_OOB_CTRL);
reg &= ~ID_MODE_DIS;
reg |= id_mode_dis;
if (GENET_IS_V1(priv) || GENET_IS_V2(priv) || GENET_IS_V3(priv))
reg |= RGMII_MODE_EN_V123;
else
reg |= RGMII_MODE_EN;
bcmgenet_ext_writel(priv, reg, EXT_RGMII_OOB_CTRL);
}
if (init)
dev_info(kdev, "configuring instance for %s\n", phy_name);
return 0;
}
int bcmgenet_mii_probe(struct net_device *dev)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
struct device *kdev = &priv->pdev->dev;
struct device_node *dn = kdev->of_node;
struct phy_device *phydev;
u32 phy_flags = 0;
int ret;
/* Communicate the integrated PHY revision */
if (priv->internal_phy)
phy_flags = priv->gphy_rev;
/* Initialize link state variables that bcmgenet_mii_setup() uses */
priv->old_link = -1;
priv->old_speed = -1;
priv->old_duplex = -1;
priv->old_pause = -1;
if (dn) {
phydev = of_phy_connect(dev, priv->phy_dn, bcmgenet_mii_setup,
phy_flags, priv->phy_interface);
if (!phydev) {
pr_err("could not attach to PHY\n");
return -ENODEV;
}
} else {
if (has_acpi_companion(kdev)) {
char mdio_bus_id[MII_BUS_ID_SIZE];
struct mii_bus *unimacbus;
snprintf(mdio_bus_id, MII_BUS_ID_SIZE, "%s-%d",
UNIMAC_MDIO_DRV_NAME, priv->pdev->id);
unimacbus = mdio_find_bus(mdio_bus_id);
if (!unimacbus) {
pr_err("Unable to find mii\n");
return -ENODEV;
}
phydev = phy_find_first(unimacbus);
put_device(&unimacbus->dev);
if (!phydev) {
pr_err("Unable to find PHY\n");
return -ENODEV;
}
} else {
phydev = dev->phydev;
}
phydev->dev_flags = phy_flags;
ret = phy_connect_direct(dev, phydev, bcmgenet_mii_setup,
priv->phy_interface);
if (ret) {
pr_err("could not attach to PHY\n");
return -ENODEV;
}
}
/* Configure port multiplexer based on what the probed PHY device since
* reading the 'max-speed' property determines the maximum supported
* PHY speed which is needed for bcmgenet_mii_config() to configure
* things appropriately.
*/
ret = bcmgenet_mii_config(dev, true);
if (ret) {
phy_disconnect(dev->phydev);
return ret;
}
linkmode_copy(phydev->advertising, phydev->supported);
/* The internal PHY has its link interrupts routed to the
* Ethernet MAC ISRs. On GENETv5 there is a hardware issue
* that prevents the signaling of link UP interrupts when
* the link operates at 10Mbps, so fallback to polling for
* those versions of GENET.
*/
if (priv->internal_phy && !GENET_IS_V5(priv))
dev->phydev->irq = PHY_IGNORE_INTERRUPT;
return 0;
}
static struct device_node *bcmgenet_mii_of_find_mdio(struct bcmgenet_priv *priv)
{
struct device_node *dn = priv->pdev->dev.of_node;
struct device *kdev = &priv->pdev->dev;
char *compat;
compat = kasprintf(GFP_KERNEL, "brcm,genet-mdio-v%d", priv->version);
if (!compat)
return NULL;
priv->mdio_dn = of_get_compatible_child(dn, compat);
kfree(compat);
if (!priv->mdio_dn) {
dev_err(kdev, "unable to find MDIO bus node\n");
return NULL;
}
return priv->mdio_dn;
}
static void bcmgenet_mii_pdata_init(struct bcmgenet_priv *priv,
struct unimac_mdio_pdata *ppd)
{
struct device *kdev = &priv->pdev->dev;
struct bcmgenet_platform_data *pd = kdev->platform_data;
if (pd->phy_interface != PHY_INTERFACE_MODE_MOCA && pd->mdio_enabled) {
/*
* Internal or external PHY with MDIO access
*/
if (pd->phy_address >= 0 && pd->phy_address < PHY_MAX_ADDR)
ppd->phy_mask = 1 << pd->phy_address;
else
ppd->phy_mask = 0;
}
}
static int bcmgenet_mii_wait(void *wait_func_data)
{
struct bcmgenet_priv *priv = wait_func_data;
wait_event_timeout(priv->wq,
!(bcmgenet_umac_readl(priv, UMAC_MDIO_CMD)
& MDIO_START_BUSY),
HZ / 100);
return 0;
}
static int bcmgenet_mii_register(struct bcmgenet_priv *priv)
{
struct platform_device *pdev = priv->pdev;
struct bcmgenet_platform_data *pdata = pdev->dev.platform_data;
struct device_node *dn = pdev->dev.of_node;
struct unimac_mdio_pdata ppd;
struct platform_device *ppdev;
struct resource *pres, res;
int id, ret;
pres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!pres) {
dev_err(&pdev->dev, "Invalid resource\n");
return -EINVAL;
}
memset(&res, 0, sizeof(res));
memset(&ppd, 0, sizeof(ppd));
ppd.wait_func = bcmgenet_mii_wait;
ppd.wait_func_data = priv;
ppd.bus_name = "bcmgenet MII bus";
/* Unimac MDIO bus controller starts at UniMAC offset + MDIO_CMD
* and is 2 * 32-bits word long, 8 bytes total.
*/
res.start = pres->start + GENET_UMAC_OFF + UMAC_MDIO_CMD;
res.end = res.start + 8;
res.flags = IORESOURCE_MEM;
if (dn)
id = of_alias_get_id(dn, "eth");
else
id = pdev->id;
ppdev = platform_device_alloc(UNIMAC_MDIO_DRV_NAME, id);
if (!ppdev)
return -ENOMEM;
/* Retain this platform_device pointer for later cleanup */
priv->mii_pdev = ppdev;
ppdev->dev.parent = &pdev->dev;
if (dn)
ppdev->dev.of_node = bcmgenet_mii_of_find_mdio(priv);
else if (pdata)
bcmgenet_mii_pdata_init(priv, &ppd);
else
ppd.phy_mask = ~0;
ret = platform_device_add_resources(ppdev, &res, 1);
if (ret)
goto out;
ret = platform_device_add_data(ppdev, &ppd, sizeof(ppd));
if (ret)
goto out;
ret = platform_device_add(ppdev);
if (ret)
goto out;
return 0;
out:
platform_device_put(ppdev);
return ret;
}
static int bcmgenet_phy_interface_init(struct bcmgenet_priv *priv)
{
struct device *kdev = &priv->pdev->dev;
int phy_mode = device_get_phy_mode(kdev);
if (phy_mode < 0) {
dev_err(kdev, "invalid PHY mode property\n");
return phy_mode;
}
priv->phy_interface = phy_mode;
/* We need to specifically look up whether this PHY interface is
* internal or not *before* we even try to probe the PHY driver
* over MDIO as we may have shut down the internal PHY for power
* saving purposes.
*/
if (priv->phy_interface == PHY_INTERFACE_MODE_INTERNAL)
priv->internal_phy = true;
return 0;
}
static int bcmgenet_mii_of_init(struct bcmgenet_priv *priv)
{
struct device_node *dn = priv->pdev->dev.of_node;
struct phy_device *phydev;
int ret;
/* Fetch the PHY phandle */
priv->phy_dn = of_parse_phandle(dn, "phy-handle", 0);
/* In the case of a fixed PHY, the DT node associated
* to the PHY is the Ethernet MAC DT node.
*/
if (!priv->phy_dn && of_phy_is_fixed_link(dn)) {
ret = of_phy_register_fixed_link(dn);
if (ret)
return ret;
priv->phy_dn = of_node_get(dn);
}
/* Get the link mode */
ret = bcmgenet_phy_interface_init(priv);
if (ret)
return ret;
/* Make sure we initialize MoCA PHYs with a link down */
if (priv->phy_interface == PHY_INTERFACE_MODE_MOCA) {
phydev = of_phy_find_device(dn);
if (phydev) {
phydev->link = 0;
put_device(&phydev->mdio.dev);
}
}
return 0;
}
static int bcmgenet_mii_pd_init(struct bcmgenet_priv *priv)
{
struct device *kdev = &priv->pdev->dev;
struct bcmgenet_platform_data *pd = kdev->platform_data;
char phy_name[MII_BUS_ID_SIZE + 3];
char mdio_bus_id[MII_BUS_ID_SIZE];
struct phy_device *phydev;
snprintf(mdio_bus_id, MII_BUS_ID_SIZE, "%s-%d",
UNIMAC_MDIO_DRV_NAME, priv->pdev->id);
if (pd->phy_interface != PHY_INTERFACE_MODE_MOCA && pd->mdio_enabled) {
snprintf(phy_name, MII_BUS_ID_SIZE, PHY_ID_FMT,
mdio_bus_id, pd->phy_address);
/*
* Internal or external PHY with MDIO access
*/
phydev = phy_attach(priv->dev, phy_name, pd->phy_interface);
if (!phydev) {
dev_err(kdev, "failed to register PHY device\n");
return -ENODEV;
}
} else {
/*
* MoCA port or no MDIO access.
* Use fixed PHY to represent the link layer.
*/
struct fixed_phy_status fphy_status = {
.link = 1,
.speed = pd->phy_speed,
.duplex = pd->phy_duplex,
.pause = 0,
.asym_pause = 0,
};
phydev = fixed_phy_register(PHY_POLL, &fphy_status, NULL);
if (!phydev || IS_ERR(phydev)) {
dev_err(kdev, "failed to register fixed PHY device\n");
return -ENODEV;
}
/* Make sure we initialize MoCA PHYs with a link down */
phydev->link = 0;
}
priv->phy_interface = pd->phy_interface;
return 0;
}
static int bcmgenet_mii_bus_init(struct bcmgenet_priv *priv)
{
struct device *kdev = &priv->pdev->dev;
struct device_node *dn = kdev->of_node;
if (dn)
return bcmgenet_mii_of_init(priv);
else if (has_acpi_companion(kdev))
return bcmgenet_phy_interface_init(priv);
else
return bcmgenet_mii_pd_init(priv);
}
int bcmgenet_mii_init(struct net_device *dev)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
int ret;
ret = bcmgenet_mii_register(priv);
if (ret)
return ret;
ret = bcmgenet_mii_bus_init(priv);
if (ret)
goto out;
return 0;
out:
bcmgenet_mii_exit(dev);
return ret;
}
void bcmgenet_mii_exit(struct net_device *dev)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
struct device_node *dn = priv->pdev->dev.of_node;
if (of_phy_is_fixed_link(dn))
of_phy_deregister_fixed_link(dn);
of_node_put(priv->phy_dn);
platform_device_unregister(priv->mii_pdev);
}

View File

@ -0,0 +1,637 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Broadcom GENET MDIO routines
*
* Copyright (c) 2014-2017 Broadcom
*/
#include <linux/acpi.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/wait.h>
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <linux/bitops.h>
#include <linux/netdevice.h>
#include <linux/platform_device.h>
#include <linux/phy.h>
#include <linux/phy_fixed.h>
#include <linux/brcmphy.h>
#include <linux/of.h>
#include <linux/of_net.h>
#include <linux/of_mdio.h>
#include <linux/platform_data/bcmgenet.h>
#include <linux/platform_data/mdio-bcm-unimac.h>
#include "bcmgenet-5.14-ethercat.h"
/* setup netdev link state when PHY link status change and
* update UMAC and RGMII block when link up
*/
void bcmgenet_mii_setup(struct net_device *dev)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
struct phy_device *phydev = dev->phydev;
u32 reg, cmd_bits = 0;
bool status_changed = false;
if (priv->old_link != phydev->link) {
status_changed = true;
priv->old_link = phydev->link;
}
if (phydev->link) {
/* check speed/duplex/pause changes */
if (priv->old_speed != phydev->speed) {
status_changed = true;
priv->old_speed = phydev->speed;
}
if (priv->old_duplex != phydev->duplex) {
status_changed = true;
priv->old_duplex = phydev->duplex;
}
if (priv->old_pause != phydev->pause) {
status_changed = true;
priv->old_pause = phydev->pause;
}
/* done if nothing has changed */
if (!status_changed)
return;
/* speed */
if (phydev->speed == SPEED_1000)
cmd_bits = CMD_SPEED_1000;
else if (phydev->speed == SPEED_100)
cmd_bits = CMD_SPEED_100;
else
cmd_bits = CMD_SPEED_10;
cmd_bits <<= CMD_SPEED_SHIFT;
/* duplex */
if (phydev->duplex != DUPLEX_FULL)
cmd_bits |= CMD_HD_EN;
/* pause capability */
if (!phydev->pause)
cmd_bits |= CMD_RX_PAUSE_IGNORE | CMD_TX_PAUSE_IGNORE;
/*
* Program UMAC and RGMII block based on established
* link speed, duplex, and pause. The speed set in
* umac->cmd tell RGMII block which clock to use for
* transmit -- 25MHz(100Mbps) or 125MHz(1Gbps).
* Receive clock is provided by the PHY.
*/
reg = bcmgenet_ext_readl(priv, EXT_RGMII_OOB_CTRL);
reg &= ~OOB_DISABLE;
reg |= RGMII_LINK;
bcmgenet_ext_writel(priv, reg, EXT_RGMII_OOB_CTRL);
reg = bcmgenet_umac_readl(priv, UMAC_CMD);
reg &= ~((CMD_SPEED_MASK << CMD_SPEED_SHIFT) |
CMD_HD_EN |
CMD_RX_PAUSE_IGNORE | CMD_TX_PAUSE_IGNORE);
reg |= cmd_bits;
if (reg & CMD_SW_RESET) {
reg &= ~CMD_SW_RESET;
bcmgenet_umac_writel(priv, reg, UMAC_CMD);
udelay(2);
reg |= CMD_TX_EN | CMD_RX_EN;
}
bcmgenet_umac_writel(priv, reg, UMAC_CMD);
} else {
/* done if nothing has changed */
if (!status_changed)
return;
/* needed for MoCA fixed PHY to reflect correct link status */
netif_carrier_off(dev);
}
if (priv->ecdev)
ecdev_set_link(priv->ecdev, phydev->link);
phy_print_status(phydev);
}
static int bcmgenet_fixed_phy_link_update(struct net_device *dev,
struct fixed_phy_status *status)
{
struct bcmgenet_priv *priv;
u32 reg;
if (dev && dev->phydev && status) {
priv = netdev_priv(dev);
reg = bcmgenet_umac_readl(priv, UMAC_MODE);
status->link = !!(reg & MODE_LINK_STATUS);
}
return 0;
}
void bcmgenet_phy_power_set(struct net_device *dev, bool enable)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
u32 reg = 0;
/* EXT_GPHY_CTRL is only valid for GENETv4 and onward */
if (GENET_IS_V4(priv)) {
reg = bcmgenet_ext_readl(priv, EXT_GPHY_CTRL);
if (enable) {
reg &= ~EXT_CK25_DIS;
bcmgenet_ext_writel(priv, reg, EXT_GPHY_CTRL);
mdelay(1);
reg &= ~(EXT_CFG_IDDQ_BIAS | EXT_CFG_PWR_DOWN);
reg |= EXT_GPHY_RESET;
bcmgenet_ext_writel(priv, reg, EXT_GPHY_CTRL);
mdelay(1);
reg &= ~EXT_GPHY_RESET;
} else {
reg |= EXT_CFG_IDDQ_BIAS | EXT_CFG_PWR_DOWN |
EXT_GPHY_RESET;
bcmgenet_ext_writel(priv, reg, EXT_GPHY_CTRL);
mdelay(1);
reg |= EXT_CK25_DIS;
}
bcmgenet_ext_writel(priv, reg, EXT_GPHY_CTRL);
udelay(60);
} else {
mdelay(1);
}
}
static void bcmgenet_moca_phy_setup(struct bcmgenet_priv *priv)
{
u32 reg;
if (!GENET_IS_V5(priv)) {
/* Speed settings are set in bcmgenet_mii_setup() */
reg = bcmgenet_sys_readl(priv, SYS_PORT_CTRL);
reg |= LED_ACT_SOURCE_MAC;
bcmgenet_sys_writel(priv, reg, SYS_PORT_CTRL);
}
if (priv->hw_params->flags & GENET_HAS_MOCA_LINK_DET)
fixed_phy_set_link_update(priv->dev->phydev,
bcmgenet_fixed_phy_link_update);
}
int bcmgenet_mii_config(struct net_device *dev, bool init)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
struct phy_device *phydev = dev->phydev;
struct device *kdev = &priv->pdev->dev;
const char *phy_name = NULL;
u32 id_mode_dis = 0;
u32 port_ctrl;
u32 reg;
switch (priv->phy_interface) {
case PHY_INTERFACE_MODE_INTERNAL:
phy_name = "internal PHY";
fallthrough;
case PHY_INTERFACE_MODE_MOCA:
/* Irrespective of the actually configured PHY speed (100 or
* 1000) GENETv4 only has an internal GPHY so we will just end
* up masking the Gigabit features from what we support, not
* switching to the EPHY
*/
if (GENET_IS_V4(priv))
port_ctrl = PORT_MODE_INT_GPHY;
else
port_ctrl = PORT_MODE_INT_EPHY;
if (!phy_name) {
phy_name = "MoCA";
bcmgenet_moca_phy_setup(priv);
}
break;
case PHY_INTERFACE_MODE_MII:
phy_name = "external MII";
phy_set_max_speed(phydev, SPEED_100);
port_ctrl = PORT_MODE_EXT_EPHY;
break;
case PHY_INTERFACE_MODE_REVMII:
phy_name = "external RvMII";
/* of_mdiobus_register took care of reading the 'max-speed'
* PHY property for us, effectively limiting the PHY supported
* capabilities, use that knowledge to also configure the
* Reverse MII interface correctly.
*/
if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
dev->phydev->supported))
port_ctrl = PORT_MODE_EXT_RVMII_50;
else
port_ctrl = PORT_MODE_EXT_RVMII_25;
break;
case PHY_INTERFACE_MODE_RGMII:
/* RGMII_NO_ID: TXC transitions at the same time as TXD
* (requires PCB or receiver-side delay)
*
* ID is implicitly disabled for 100Mbps (RG)MII operation.
*/
phy_name = "external RGMII (no delay)";
id_mode_dis = BIT(16);
port_ctrl = PORT_MODE_EXT_GPHY;
break;
case PHY_INTERFACE_MODE_RGMII_TXID:
/* RGMII_TXID: Add 2ns delay on TXC (90 degree shift) */
phy_name = "external RGMII (TX delay)";
port_ctrl = PORT_MODE_EXT_GPHY;
break;
case PHY_INTERFACE_MODE_RGMII_RXID:
phy_name = "external RGMII (RX delay)";
port_ctrl = PORT_MODE_EXT_GPHY;
break;
default:
dev_err(kdev, "unknown phy mode: %d\n", priv->phy_interface);
return -EINVAL;
}
bcmgenet_sys_writel(priv, port_ctrl, SYS_PORT_CTRL);
priv->ext_phy = !priv->internal_phy &&
(priv->phy_interface != PHY_INTERFACE_MODE_MOCA);
/* This is an external PHY (xMII), so we need to enable the RGMII
* block for the interface to work
*/
if (priv->ext_phy) {
reg = bcmgenet_ext_readl(priv, EXT_RGMII_OOB_CTRL);
reg &= ~ID_MODE_DIS;
reg |= id_mode_dis;
if (GENET_IS_V1(priv) || GENET_IS_V2(priv) || GENET_IS_V3(priv))
reg |= RGMII_MODE_EN_V123;
else
reg |= RGMII_MODE_EN;
bcmgenet_ext_writel(priv, reg, EXT_RGMII_OOB_CTRL);
}
if (init)
dev_info(kdev, "configuring instance for %s\n", phy_name);
return 0;
}
int bcmgenet_mii_probe(struct net_device *dev)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
struct device *kdev = &priv->pdev->dev;
struct device_node *dn = kdev->of_node;
struct phy_device *phydev;
u32 phy_flags = 0;
int ret;
/* Communicate the integrated PHY revision */
if (priv->internal_phy)
phy_flags = priv->gphy_rev;
/* Initialize link state variables that bcmgenet_mii_setup() uses */
priv->old_link = -1;
priv->old_speed = -1;
priv->old_duplex = -1;
priv->old_pause = -1;
if (dn) {
phydev = of_phy_connect(dev, priv->phy_dn, bcmgenet_mii_setup,
phy_flags, priv->phy_interface);
if (!phydev) {
pr_err("could not attach to PHY\n");
return -ENODEV;
}
} else {
if (has_acpi_companion(kdev)) {
char mdio_bus_id[MII_BUS_ID_SIZE];
struct mii_bus *unimacbus;
snprintf(mdio_bus_id, MII_BUS_ID_SIZE, "%s-%d",
UNIMAC_MDIO_DRV_NAME, priv->pdev->id);
unimacbus = mdio_find_bus(mdio_bus_id);
if (!unimacbus) {
pr_err("Unable to find mii\n");
return -ENODEV;
}
phydev = phy_find_first(unimacbus);
put_device(&unimacbus->dev);
if (!phydev) {
pr_err("Unable to find PHY\n");
return -ENODEV;
}
} else {
phydev = dev->phydev;
}
phydev->dev_flags = phy_flags;
ret = phy_connect_direct(dev, phydev, bcmgenet_mii_setup,
priv->phy_interface);
if (ret) {
pr_err("could not attach to PHY\n");
return -ENODEV;
}
}
/* Configure port multiplexer based on what the probed PHY device since
* reading the 'max-speed' property determines the maximum supported
* PHY speed which is needed for bcmgenet_mii_config() to configure
* things appropriately.
*/
ret = bcmgenet_mii_config(dev, true);
if (ret) {
phy_disconnect(dev->phydev);
return ret;
}
linkmode_copy(phydev->advertising, phydev->supported);
/* The internal PHY has its link interrupts routed to the
* Ethernet MAC ISRs. On GENETv5 there is a hardware issue
* that prevents the signaling of link UP interrupts when
* the link operates at 10Mbps, so fallback to polling for
* those versions of GENET.
*/
if (priv->internal_phy && !GENET_IS_V5(priv))
dev->phydev->irq = PHY_MAC_INTERRUPT;
return 0;
}
static struct device_node *bcmgenet_mii_of_find_mdio(struct bcmgenet_priv *priv)
{
struct device_node *dn = priv->pdev->dev.of_node;
struct device *kdev = &priv->pdev->dev;
char *compat;
compat = kasprintf(GFP_KERNEL, "brcm,genet-mdio-v%d", priv->version);
if (!compat)
return NULL;
priv->mdio_dn = of_get_compatible_child(dn, compat);
kfree(compat);
if (!priv->mdio_dn) {
dev_err(kdev, "unable to find MDIO bus node\n");
return NULL;
}
return priv->mdio_dn;
}
static void bcmgenet_mii_pdata_init(struct bcmgenet_priv *priv,
struct unimac_mdio_pdata *ppd)
{
struct device *kdev = &priv->pdev->dev;
struct bcmgenet_platform_data *pd = kdev->platform_data;
if (pd->phy_interface != PHY_INTERFACE_MODE_MOCA && pd->mdio_enabled) {
/*
* Internal or external PHY with MDIO access
*/
if (pd->phy_address >= 0 && pd->phy_address < PHY_MAX_ADDR)
ppd->phy_mask = 1 << pd->phy_address;
else
ppd->phy_mask = 0;
}
}
static int bcmgenet_mii_wait(void *wait_func_data)
{
struct bcmgenet_priv *priv = wait_func_data;
wait_event_timeout(priv->wq,
!(bcmgenet_umac_readl(priv, UMAC_MDIO_CMD)
& MDIO_START_BUSY),
HZ / 100);
return 0;
}
static int bcmgenet_mii_register(struct bcmgenet_priv *priv)
{
struct platform_device *pdev = priv->pdev;
struct bcmgenet_platform_data *pdata = pdev->dev.platform_data;
struct device_node *dn = pdev->dev.of_node;
struct unimac_mdio_pdata ppd;
struct platform_device *ppdev;
struct resource *pres, res;
int id, ret;
pres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!pres) {
dev_err(&pdev->dev, "Invalid resource\n");
return -EINVAL;
}
memset(&res, 0, sizeof(res));
memset(&ppd, 0, sizeof(ppd));
ppd.wait_func = bcmgenet_mii_wait;
ppd.wait_func_data = priv;
ppd.bus_name = "bcmgenet MII bus";
/* Unimac MDIO bus controller starts at UniMAC offset + MDIO_CMD
* and is 2 * 32-bits word long, 8 bytes total.
*/
res.start = pres->start + GENET_UMAC_OFF + UMAC_MDIO_CMD;
res.end = res.start + 8;
res.flags = IORESOURCE_MEM;
if (dn)
id = of_alias_get_id(dn, "eth");
else
id = pdev->id;
ppdev = platform_device_alloc(UNIMAC_MDIO_DRV_NAME, id);
if (!ppdev)
return -ENOMEM;
/* Retain this platform_device pointer for later cleanup */
priv->mii_pdev = ppdev;
ppdev->dev.parent = &pdev->dev;
if (dn)
ppdev->dev.of_node = bcmgenet_mii_of_find_mdio(priv);
else if (pdata)
bcmgenet_mii_pdata_init(priv, &ppd);
else
ppd.phy_mask = ~0;
ret = platform_device_add_resources(ppdev, &res, 1);
if (ret)
goto out;
ret = platform_device_add_data(ppdev, &ppd, sizeof(ppd));
if (ret)
goto out;
ret = platform_device_add(ppdev);
if (ret)
goto out;
return 0;
out:
platform_device_put(ppdev);
return ret;
}
static int bcmgenet_phy_interface_init(struct bcmgenet_priv *priv)
{
struct device *kdev = &priv->pdev->dev;
int phy_mode = device_get_phy_mode(kdev);
if (phy_mode < 0) {
dev_err(kdev, "invalid PHY mode property\n");
return phy_mode;
}
priv->phy_interface = phy_mode;
/* We need to specifically look up whether this PHY interface is
* internal or not *before* we even try to probe the PHY driver
* over MDIO as we may have shut down the internal PHY for power
* saving purposes.
*/
if (priv->phy_interface == PHY_INTERFACE_MODE_INTERNAL)
priv->internal_phy = true;
return 0;
}
static int bcmgenet_mii_of_init(struct bcmgenet_priv *priv)
{
struct device_node *dn = priv->pdev->dev.of_node;
struct phy_device *phydev;
int ret;
/* Fetch the PHY phandle */
priv->phy_dn = of_parse_phandle(dn, "phy-handle", 0);
/* In the case of a fixed PHY, the DT node associated
* to the PHY is the Ethernet MAC DT node.
*/
if (!priv->phy_dn && of_phy_is_fixed_link(dn)) {
ret = of_phy_register_fixed_link(dn);
if (ret)
return ret;
priv->phy_dn = of_node_get(dn);
}
/* Get the link mode */
ret = bcmgenet_phy_interface_init(priv);
if (ret)
return ret;
/* Make sure we initialize MoCA PHYs with a link down */
if (priv->phy_interface == PHY_INTERFACE_MODE_MOCA) {
phydev = of_phy_find_device(dn);
if (phydev) {
phydev->link = 0;
put_device(&phydev->mdio.dev);
}
}
return 0;
}
static int bcmgenet_mii_pd_init(struct bcmgenet_priv *priv)
{
struct device *kdev = &priv->pdev->dev;
struct bcmgenet_platform_data *pd = kdev->platform_data;
char phy_name[MII_BUS_ID_SIZE + 3];
char mdio_bus_id[MII_BUS_ID_SIZE];
struct phy_device *phydev;
snprintf(mdio_bus_id, MII_BUS_ID_SIZE, "%s-%d",
UNIMAC_MDIO_DRV_NAME, priv->pdev->id);
if (pd->phy_interface != PHY_INTERFACE_MODE_MOCA && pd->mdio_enabled) {
snprintf(phy_name, MII_BUS_ID_SIZE, PHY_ID_FMT,
mdio_bus_id, pd->phy_address);
/*
* Internal or external PHY with MDIO access
*/
phydev = phy_attach(priv->dev, phy_name, pd->phy_interface);
if (!phydev) {
dev_err(kdev, "failed to register PHY device\n");
return -ENODEV;
}
} else {
/*
* MoCA port or no MDIO access.
* Use fixed PHY to represent the link layer.
*/
struct fixed_phy_status fphy_status = {
.link = 1,
.speed = pd->phy_speed,
.duplex = pd->phy_duplex,
.pause = 0,
.asym_pause = 0,
};
phydev = fixed_phy_register(PHY_POLL, &fphy_status, NULL);
if (!phydev || IS_ERR(phydev)) {
dev_err(kdev, "failed to register fixed PHY device\n");
return -ENODEV;
}
/* Make sure we initialize MoCA PHYs with a link down */
phydev->link = 0;
}
priv->phy_interface = pd->phy_interface;
return 0;
}
static int bcmgenet_mii_bus_init(struct bcmgenet_priv *priv)
{
struct device *kdev = &priv->pdev->dev;
struct device_node *dn = kdev->of_node;
if (dn)
return bcmgenet_mii_of_init(priv);
else if (has_acpi_companion(kdev))
return bcmgenet_phy_interface_init(priv);
else
return bcmgenet_mii_pd_init(priv);
}
int bcmgenet_mii_init(struct net_device *dev)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
int ret;
ret = bcmgenet_mii_register(priv);
if (ret)
return ret;
ret = bcmgenet_mii_bus_init(priv);
if (ret)
goto out;
return 0;
out:
bcmgenet_mii_exit(dev);
return ret;
}
void bcmgenet_mii_exit(struct net_device *dev)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
struct device_node *dn = priv->pdev->dev.of_node;
if (of_phy_is_fixed_link(dn))
of_phy_deregister_fixed_link(dn);
of_node_put(priv->phy_dn);
platform_device_unregister(priv->mii_pdev);
}

View File

@ -0,0 +1,635 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Broadcom GENET MDIO routines
*
* Copyright (c) 2014-2017 Broadcom
*/
#include <linux/acpi.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/wait.h>
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <linux/bitops.h>
#include <linux/netdevice.h>
#include <linux/platform_device.h>
#include <linux/phy.h>
#include <linux/phy_fixed.h>
#include <linux/brcmphy.h>
#include <linux/of.h>
#include <linux/of_net.h>
#include <linux/of_mdio.h>
#include <linux/platform_data/bcmgenet.h>
#include <linux/platform_data/mdio-bcm-unimac.h>
#include "bcmgenet.h"
/* setup netdev link state when PHY link status change and
* update UMAC and RGMII block when link up
*/
void bcmgenet_mii_setup(struct net_device *dev)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
struct phy_device *phydev = dev->phydev;
u32 reg, cmd_bits = 0;
bool status_changed = false;
if (priv->old_link != phydev->link) {
status_changed = true;
priv->old_link = phydev->link;
}
if (phydev->link) {
/* check speed/duplex/pause changes */
if (priv->old_speed != phydev->speed) {
status_changed = true;
priv->old_speed = phydev->speed;
}
if (priv->old_duplex != phydev->duplex) {
status_changed = true;
priv->old_duplex = phydev->duplex;
}
if (priv->old_pause != phydev->pause) {
status_changed = true;
priv->old_pause = phydev->pause;
}
/* done if nothing has changed */
if (!status_changed)
return;
/* speed */
if (phydev->speed == SPEED_1000)
cmd_bits = CMD_SPEED_1000;
else if (phydev->speed == SPEED_100)
cmd_bits = CMD_SPEED_100;
else
cmd_bits = CMD_SPEED_10;
cmd_bits <<= CMD_SPEED_SHIFT;
/* duplex */
if (phydev->duplex != DUPLEX_FULL)
cmd_bits |= CMD_HD_EN;
/* pause capability */
if (!phydev->pause)
cmd_bits |= CMD_RX_PAUSE_IGNORE | CMD_TX_PAUSE_IGNORE;
/*
* Program UMAC and RGMII block based on established
* link speed, duplex, and pause. The speed set in
* umac->cmd tell RGMII block which clock to use for
* transmit -- 25MHz(100Mbps) or 125MHz(1Gbps).
* Receive clock is provided by the PHY.
*/
reg = bcmgenet_ext_readl(priv, EXT_RGMII_OOB_CTRL);
reg &= ~OOB_DISABLE;
reg |= RGMII_LINK;
bcmgenet_ext_writel(priv, reg, EXT_RGMII_OOB_CTRL);
reg = bcmgenet_umac_readl(priv, UMAC_CMD);
reg &= ~((CMD_SPEED_MASK << CMD_SPEED_SHIFT) |
CMD_HD_EN |
CMD_RX_PAUSE_IGNORE | CMD_TX_PAUSE_IGNORE);
reg |= cmd_bits;
if (reg & CMD_SW_RESET) {
reg &= ~CMD_SW_RESET;
bcmgenet_umac_writel(priv, reg, UMAC_CMD);
udelay(2);
reg |= CMD_TX_EN | CMD_RX_EN;
}
bcmgenet_umac_writel(priv, reg, UMAC_CMD);
} else {
/* done if nothing has changed */
if (!status_changed)
return;
/* needed for MoCA fixed PHY to reflect correct link status */
netif_carrier_off(dev);
}
phy_print_status(phydev);
}
static int bcmgenet_fixed_phy_link_update(struct net_device *dev,
struct fixed_phy_status *status)
{
struct bcmgenet_priv *priv;
u32 reg;
if (dev && dev->phydev && status) {
priv = netdev_priv(dev);
reg = bcmgenet_umac_readl(priv, UMAC_MODE);
status->link = !!(reg & MODE_LINK_STATUS);
}
return 0;
}
void bcmgenet_phy_power_set(struct net_device *dev, bool enable)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
u32 reg = 0;
/* EXT_GPHY_CTRL is only valid for GENETv4 and onward */
if (GENET_IS_V4(priv)) {
reg = bcmgenet_ext_readl(priv, EXT_GPHY_CTRL);
if (enable) {
reg &= ~EXT_CK25_DIS;
bcmgenet_ext_writel(priv, reg, EXT_GPHY_CTRL);
mdelay(1);
reg &= ~(EXT_CFG_IDDQ_BIAS | EXT_CFG_PWR_DOWN);
reg |= EXT_GPHY_RESET;
bcmgenet_ext_writel(priv, reg, EXT_GPHY_CTRL);
mdelay(1);
reg &= ~EXT_GPHY_RESET;
} else {
reg |= EXT_CFG_IDDQ_BIAS | EXT_CFG_PWR_DOWN |
EXT_GPHY_RESET;
bcmgenet_ext_writel(priv, reg, EXT_GPHY_CTRL);
mdelay(1);
reg |= EXT_CK25_DIS;
}
bcmgenet_ext_writel(priv, reg, EXT_GPHY_CTRL);
udelay(60);
} else {
mdelay(1);
}
}
static void bcmgenet_moca_phy_setup(struct bcmgenet_priv *priv)
{
u32 reg;
if (!GENET_IS_V5(priv)) {
/* Speed settings are set in bcmgenet_mii_setup() */
reg = bcmgenet_sys_readl(priv, SYS_PORT_CTRL);
reg |= LED_ACT_SOURCE_MAC;
bcmgenet_sys_writel(priv, reg, SYS_PORT_CTRL);
}
if (priv->hw_params->flags & GENET_HAS_MOCA_LINK_DET)
fixed_phy_set_link_update(priv->dev->phydev,
bcmgenet_fixed_phy_link_update);
}
int bcmgenet_mii_config(struct net_device *dev, bool init)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
struct phy_device *phydev = dev->phydev;
struct device *kdev = &priv->pdev->dev;
const char *phy_name = NULL;
u32 id_mode_dis = 0;
u32 port_ctrl;
u32 reg;
switch (priv->phy_interface) {
case PHY_INTERFACE_MODE_INTERNAL:
phy_name = "internal PHY";
fallthrough;
case PHY_INTERFACE_MODE_MOCA:
/* Irrespective of the actually configured PHY speed (100 or
* 1000) GENETv4 only has an internal GPHY so we will just end
* up masking the Gigabit features from what we support, not
* switching to the EPHY
*/
if (GENET_IS_V4(priv))
port_ctrl = PORT_MODE_INT_GPHY;
else
port_ctrl = PORT_MODE_INT_EPHY;
if (!phy_name) {
phy_name = "MoCA";
bcmgenet_moca_phy_setup(priv);
}
break;
case PHY_INTERFACE_MODE_MII:
phy_name = "external MII";
phy_set_max_speed(phydev, SPEED_100);
port_ctrl = PORT_MODE_EXT_EPHY;
break;
case PHY_INTERFACE_MODE_REVMII:
phy_name = "external RvMII";
/* of_mdiobus_register took care of reading the 'max-speed'
* PHY property for us, effectively limiting the PHY supported
* capabilities, use that knowledge to also configure the
* Reverse MII interface correctly.
*/
if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
dev->phydev->supported))
port_ctrl = PORT_MODE_EXT_RVMII_50;
else
port_ctrl = PORT_MODE_EXT_RVMII_25;
break;
case PHY_INTERFACE_MODE_RGMII:
/* RGMII_NO_ID: TXC transitions at the same time as TXD
* (requires PCB or receiver-side delay)
*
* ID is implicitly disabled for 100Mbps (RG)MII operation.
*/
phy_name = "external RGMII (no delay)";
id_mode_dis = BIT(16);
port_ctrl = PORT_MODE_EXT_GPHY;
break;
case PHY_INTERFACE_MODE_RGMII_TXID:
/* RGMII_TXID: Add 2ns delay on TXC (90 degree shift) */
phy_name = "external RGMII (TX delay)";
port_ctrl = PORT_MODE_EXT_GPHY;
break;
case PHY_INTERFACE_MODE_RGMII_RXID:
phy_name = "external RGMII (RX delay)";
port_ctrl = PORT_MODE_EXT_GPHY;
break;
default:
dev_err(kdev, "unknown phy mode: %d\n", priv->phy_interface);
return -EINVAL;
}
bcmgenet_sys_writel(priv, port_ctrl, SYS_PORT_CTRL);
priv->ext_phy = !priv->internal_phy &&
(priv->phy_interface != PHY_INTERFACE_MODE_MOCA);
/* This is an external PHY (xMII), so we need to enable the RGMII
* block for the interface to work
*/
if (priv->ext_phy) {
reg = bcmgenet_ext_readl(priv, EXT_RGMII_OOB_CTRL);
reg &= ~ID_MODE_DIS;
reg |= id_mode_dis;
if (GENET_IS_V1(priv) || GENET_IS_V2(priv) || GENET_IS_V3(priv))
reg |= RGMII_MODE_EN_V123;
else
reg |= RGMII_MODE_EN;
bcmgenet_ext_writel(priv, reg, EXT_RGMII_OOB_CTRL);
}
if (init)
dev_info(kdev, "configuring instance for %s\n", phy_name);
return 0;
}
int bcmgenet_mii_probe(struct net_device *dev)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
struct device *kdev = &priv->pdev->dev;
struct device_node *dn = kdev->of_node;
struct phy_device *phydev;
u32 phy_flags = 0;
int ret;
/* Communicate the integrated PHY revision */
if (priv->internal_phy)
phy_flags = priv->gphy_rev;
/* Initialize link state variables that bcmgenet_mii_setup() uses */
priv->old_link = -1;
priv->old_speed = -1;
priv->old_duplex = -1;
priv->old_pause = -1;
if (dn) {
phydev = of_phy_connect(dev, priv->phy_dn, bcmgenet_mii_setup,
phy_flags, priv->phy_interface);
if (!phydev) {
pr_err("could not attach to PHY\n");
return -ENODEV;
}
} else {
if (has_acpi_companion(kdev)) {
char mdio_bus_id[MII_BUS_ID_SIZE];
struct mii_bus *unimacbus;
snprintf(mdio_bus_id, MII_BUS_ID_SIZE, "%s-%d",
UNIMAC_MDIO_DRV_NAME, priv->pdev->id);
unimacbus = mdio_find_bus(mdio_bus_id);
if (!unimacbus) {
pr_err("Unable to find mii\n");
return -ENODEV;
}
phydev = phy_find_first(unimacbus);
put_device(&unimacbus->dev);
if (!phydev) {
pr_err("Unable to find PHY\n");
return -ENODEV;
}
} else {
phydev = dev->phydev;
}
phydev->dev_flags = phy_flags;
ret = phy_connect_direct(dev, phydev, bcmgenet_mii_setup,
priv->phy_interface);
if (ret) {
pr_err("could not attach to PHY\n");
return -ENODEV;
}
}
/* Configure port multiplexer based on what the probed PHY device since
* reading the 'max-speed' property determines the maximum supported
* PHY speed which is needed for bcmgenet_mii_config() to configure
* things appropriately.
*/
ret = bcmgenet_mii_config(dev, true);
if (ret) {
phy_disconnect(dev->phydev);
return ret;
}
linkmode_copy(phydev->advertising, phydev->supported);
/* The internal PHY has its link interrupts routed to the
* Ethernet MAC ISRs. On GENETv5 there is a hardware issue
* that prevents the signaling of link UP interrupts when
* the link operates at 10Mbps, so fallback to polling for
* those versions of GENET.
*/
if (priv->internal_phy && !GENET_IS_V5(priv))
dev->phydev->irq = PHY_MAC_INTERRUPT;
return 0;
}
static struct device_node *bcmgenet_mii_of_find_mdio(struct bcmgenet_priv *priv)
{
struct device_node *dn = priv->pdev->dev.of_node;
struct device *kdev = &priv->pdev->dev;
char *compat;
compat = kasprintf(GFP_KERNEL, "brcm,genet-mdio-v%d", priv->version);
if (!compat)
return NULL;
priv->mdio_dn = of_get_compatible_child(dn, compat);
kfree(compat);
if (!priv->mdio_dn) {
dev_err(kdev, "unable to find MDIO bus node\n");
return NULL;
}
return priv->mdio_dn;
}
static void bcmgenet_mii_pdata_init(struct bcmgenet_priv *priv,
struct unimac_mdio_pdata *ppd)
{
struct device *kdev = &priv->pdev->dev;
struct bcmgenet_platform_data *pd = kdev->platform_data;
if (pd->phy_interface != PHY_INTERFACE_MODE_MOCA && pd->mdio_enabled) {
/*
* Internal or external PHY with MDIO access
*/
if (pd->phy_address >= 0 && pd->phy_address < PHY_MAX_ADDR)
ppd->phy_mask = 1 << pd->phy_address;
else
ppd->phy_mask = 0;
}
}
static int bcmgenet_mii_wait(void *wait_func_data)
{
struct bcmgenet_priv *priv = wait_func_data;
wait_event_timeout(priv->wq,
!(bcmgenet_umac_readl(priv, UMAC_MDIO_CMD)
& MDIO_START_BUSY),
HZ / 100);
return 0;
}
static int bcmgenet_mii_register(struct bcmgenet_priv *priv)
{
struct platform_device *pdev = priv->pdev;
struct bcmgenet_platform_data *pdata = pdev->dev.platform_data;
struct device_node *dn = pdev->dev.of_node;
struct unimac_mdio_pdata ppd;
struct platform_device *ppdev;
struct resource *pres, res;
int id, ret;
pres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!pres) {
dev_err(&pdev->dev, "Invalid resource\n");
return -EINVAL;
}
memset(&res, 0, sizeof(res));
memset(&ppd, 0, sizeof(ppd));
ppd.wait_func = bcmgenet_mii_wait;
ppd.wait_func_data = priv;
ppd.bus_name = "bcmgenet MII bus";
/* Unimac MDIO bus controller starts at UniMAC offset + MDIO_CMD
* and is 2 * 32-bits word long, 8 bytes total.
*/
res.start = pres->start + GENET_UMAC_OFF + UMAC_MDIO_CMD;
res.end = res.start + 8;
res.flags = IORESOURCE_MEM;
if (dn)
id = of_alias_get_id(dn, "eth");
else
id = pdev->id;
ppdev = platform_device_alloc(UNIMAC_MDIO_DRV_NAME, id);
if (!ppdev)
return -ENOMEM;
/* Retain this platform_device pointer for later cleanup */
priv->mii_pdev = ppdev;
ppdev->dev.parent = &pdev->dev;
if (dn)
ppdev->dev.of_node = bcmgenet_mii_of_find_mdio(priv);
else if (pdata)
bcmgenet_mii_pdata_init(priv, &ppd);
else
ppd.phy_mask = ~0;
ret = platform_device_add_resources(ppdev, &res, 1);
if (ret)
goto out;
ret = platform_device_add_data(ppdev, &ppd, sizeof(ppd));
if (ret)
goto out;
ret = platform_device_add(ppdev);
if (ret)
goto out;
return 0;
out:
platform_device_put(ppdev);
return ret;
}
static int bcmgenet_phy_interface_init(struct bcmgenet_priv *priv)
{
struct device *kdev = &priv->pdev->dev;
int phy_mode = device_get_phy_mode(kdev);
if (phy_mode < 0) {
dev_err(kdev, "invalid PHY mode property\n");
return phy_mode;
}
priv->phy_interface = phy_mode;
/* We need to specifically look up whether this PHY interface is
* internal or not *before* we even try to probe the PHY driver
* over MDIO as we may have shut down the internal PHY for power
* saving purposes.
*/
if (priv->phy_interface == PHY_INTERFACE_MODE_INTERNAL)
priv->internal_phy = true;
return 0;
}
static int bcmgenet_mii_of_init(struct bcmgenet_priv *priv)
{
struct device_node *dn = priv->pdev->dev.of_node;
struct phy_device *phydev;
int ret;
/* Fetch the PHY phandle */
priv->phy_dn = of_parse_phandle(dn, "phy-handle", 0);
/* In the case of a fixed PHY, the DT node associated
* to the PHY is the Ethernet MAC DT node.
*/
if (!priv->phy_dn && of_phy_is_fixed_link(dn)) {
ret = of_phy_register_fixed_link(dn);
if (ret)
return ret;
priv->phy_dn = of_node_get(dn);
}
/* Get the link mode */
ret = bcmgenet_phy_interface_init(priv);
if (ret)
return ret;
/* Make sure we initialize MoCA PHYs with a link down */
if (priv->phy_interface == PHY_INTERFACE_MODE_MOCA) {
phydev = of_phy_find_device(dn);
if (phydev) {
phydev->link = 0;
put_device(&phydev->mdio.dev);
}
}
return 0;
}
static int bcmgenet_mii_pd_init(struct bcmgenet_priv *priv)
{
struct device *kdev = &priv->pdev->dev;
struct bcmgenet_platform_data *pd = kdev->platform_data;
char phy_name[MII_BUS_ID_SIZE + 3];
char mdio_bus_id[MII_BUS_ID_SIZE];
struct phy_device *phydev;
snprintf(mdio_bus_id, MII_BUS_ID_SIZE, "%s-%d",
UNIMAC_MDIO_DRV_NAME, priv->pdev->id);
if (pd->phy_interface != PHY_INTERFACE_MODE_MOCA && pd->mdio_enabled) {
snprintf(phy_name, MII_BUS_ID_SIZE, PHY_ID_FMT,
mdio_bus_id, pd->phy_address);
/*
* Internal or external PHY with MDIO access
*/
phydev = phy_attach(priv->dev, phy_name, pd->phy_interface);
if (!phydev) {
dev_err(kdev, "failed to register PHY device\n");
return -ENODEV;
}
} else {
/*
* MoCA port or no MDIO access.
* Use fixed PHY to represent the link layer.
*/
struct fixed_phy_status fphy_status = {
.link = 1,
.speed = pd->phy_speed,
.duplex = pd->phy_duplex,
.pause = 0,
.asym_pause = 0,
};
phydev = fixed_phy_register(PHY_POLL, &fphy_status, NULL);
if (!phydev || IS_ERR(phydev)) {
dev_err(kdev, "failed to register fixed PHY device\n");
return -ENODEV;
}
/* Make sure we initialize MoCA PHYs with a link down */
phydev->link = 0;
}
priv->phy_interface = pd->phy_interface;
return 0;
}
static int bcmgenet_mii_bus_init(struct bcmgenet_priv *priv)
{
struct device *kdev = &priv->pdev->dev;
struct device_node *dn = kdev->of_node;
if (dn)
return bcmgenet_mii_of_init(priv);
else if (has_acpi_companion(kdev))
return bcmgenet_phy_interface_init(priv);
else
return bcmgenet_mii_pd_init(priv);
}
int bcmgenet_mii_init(struct net_device *dev)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
int ret;
ret = bcmgenet_mii_register(priv);
if (ret)
return ret;
ret = bcmgenet_mii_bus_init(priv);
if (ret)
goto out;
return 0;
out:
bcmgenet_mii_exit(dev);
return ret;
}
void bcmgenet_mii_exit(struct net_device *dev)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
struct device_node *dn = priv->pdev->dev.of_node;
if (of_phy_is_fixed_link(dn))
of_phy_deregister_fixed_link(dn);
of_node_put(priv->phy_dn);
platform_device_unregister(priv->mii_pdev);
}

View File

@ -0,0 +1,68 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __UNIMAC_H
#define __UNIMAC_H
#define UMAC_HD_BKP_CTRL 0x004
#define HD_FC_EN (1 << 0)
#define HD_FC_BKOFF_OK (1 << 1)
#define IPG_CONFIG_RX_SHIFT 2
#define IPG_CONFIG_RX_MASK 0x1F
#define UMAC_CMD 0x008
#define CMD_TX_EN (1 << 0)
#define CMD_RX_EN (1 << 1)
#define CMD_SPEED_10 0
#define CMD_SPEED_100 1
#define CMD_SPEED_1000 2
#define CMD_SPEED_2500 3
#define CMD_SPEED_SHIFT 2
#define CMD_SPEED_MASK 3
#define CMD_PROMISC (1 << 4)
#define CMD_PAD_EN (1 << 5)
#define CMD_CRC_FWD (1 << 6)
#define CMD_PAUSE_FWD (1 << 7)
#define CMD_RX_PAUSE_IGNORE (1 << 8)
#define CMD_TX_ADDR_INS (1 << 9)
#define CMD_HD_EN (1 << 10)
#define CMD_SW_RESET_OLD (1 << 11)
#define CMD_SW_RESET (1 << 13)
#define CMD_LCL_LOOP_EN (1 << 15)
#define CMD_AUTO_CONFIG (1 << 22)
#define CMD_CNTL_FRM_EN (1 << 23)
#define CMD_NO_LEN_CHK (1 << 24)
#define CMD_RMT_LOOP_EN (1 << 25)
#define CMD_RX_ERR_DISC (1 << 26)
#define CMD_PRBL_EN (1 << 27)
#define CMD_TX_PAUSE_IGNORE (1 << 28)
#define CMD_TX_RX_EN (1 << 29)
#define CMD_RUNT_FILTER_DIS (1 << 30)
#define UMAC_MAC0 0x00c
#define UMAC_MAC1 0x010
#define UMAC_MAX_FRAME_LEN 0x014
#define UMAC_PAUSE_QUANTA 0x018
#define UMAC_MODE 0x044
#define MODE_LINK_STATUS (1 << 5)
#define UMAC_FRM_TAG0 0x048 /* outer tag */
#define UMAC_FRM_TAG1 0x04c /* inner tag */
#define UMAC_TX_IPG_LEN 0x05c
#define UMAC_EEE_CTRL 0x064
#define EN_LPI_RX_PAUSE (1 << 0)
#define EN_LPI_TX_PFC (1 << 1)
#define EN_LPI_TX_PAUSE (1 << 2)
#define EEE_EN (1 << 3)
#define RX_FIFO_CHECK (1 << 4)
#define EEE_TX_CLK_DIS (1 << 5)
#define DIS_EEE_10M (1 << 6)
#define LP_IDLE_PREDICTION_MODE (1 << 7)
#define UMAC_EEE_LPI_TIMER 0x068
#define UMAC_EEE_WAKE_TIMER 0x06C
#define UMAC_EEE_REF_COUNT 0x070
#define EEE_REFERENCE_COUNT_MASK 0xffff
#define UMAC_RX_IPG_INV 0x078
#define UMAC_MACSEC_PROG_TX_CRC 0x310
#define UMAC_MACSEC_CTRL 0x314
#define UMAC_PAUSE_CTRL 0x330
#define UMAC_TX_FLUSH 0x334
#define UMAC_RX_FIFO_STATUS 0x338
#define UMAC_TX_FIFO_STATUS 0x33c
#endif

View File

@ -0,0 +1,68 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __UNIMAC_H
#define __UNIMAC_H
#define UMAC_HD_BKP_CTRL 0x004
#define HD_FC_EN (1 << 0)
#define HD_FC_BKOFF_OK (1 << 1)
#define IPG_CONFIG_RX_SHIFT 2
#define IPG_CONFIG_RX_MASK 0x1F
#define UMAC_CMD 0x008
#define CMD_TX_EN (1 << 0)
#define CMD_RX_EN (1 << 1)
#define CMD_SPEED_10 0
#define CMD_SPEED_100 1
#define CMD_SPEED_1000 2
#define CMD_SPEED_2500 3
#define CMD_SPEED_SHIFT 2
#define CMD_SPEED_MASK 3
#define CMD_PROMISC (1 << 4)
#define CMD_PAD_EN (1 << 5)
#define CMD_CRC_FWD (1 << 6)
#define CMD_PAUSE_FWD (1 << 7)
#define CMD_RX_PAUSE_IGNORE (1 << 8)
#define CMD_TX_ADDR_INS (1 << 9)
#define CMD_HD_EN (1 << 10)
#define CMD_SW_RESET_OLD (1 << 11)
#define CMD_SW_RESET (1 << 13)
#define CMD_LCL_LOOP_EN (1 << 15)
#define CMD_AUTO_CONFIG (1 << 22)
#define CMD_CNTL_FRM_EN (1 << 23)
#define CMD_NO_LEN_CHK (1 << 24)
#define CMD_RMT_LOOP_EN (1 << 25)
#define CMD_RX_ERR_DISC (1 << 26)
#define CMD_PRBL_EN (1 << 27)
#define CMD_TX_PAUSE_IGNORE (1 << 28)
#define CMD_TX_RX_EN (1 << 29)
#define CMD_RUNT_FILTER_DIS (1 << 30)
#define UMAC_MAC0 0x00c
#define UMAC_MAC1 0x010
#define UMAC_MAX_FRAME_LEN 0x014
#define UMAC_PAUSE_QUANTA 0x018
#define UMAC_MODE 0x044
#define MODE_LINK_STATUS (1 << 5)
#define UMAC_FRM_TAG0 0x048 /* outer tag */
#define UMAC_FRM_TAG1 0x04c /* inner tag */
#define UMAC_TX_IPG_LEN 0x05c
#define UMAC_EEE_CTRL 0x064
#define EN_LPI_RX_PAUSE (1 << 0)
#define EN_LPI_TX_PFC (1 << 1)
#define EN_LPI_TX_PAUSE (1 << 2)
#define EEE_EN (1 << 3)
#define RX_FIFO_CHECK (1 << 4)
#define EEE_TX_CLK_DIS (1 << 5)
#define DIS_EEE_10M (1 << 6)
#define LP_IDLE_PREDICTION_MODE (1 << 7)
#define UMAC_EEE_LPI_TIMER 0x068
#define UMAC_EEE_WAKE_TIMER 0x06C
#define UMAC_EEE_REF_COUNT 0x070
#define EEE_REFERENCE_COUNT_MASK 0xffff
#define UMAC_RX_IPG_INV 0x078
#define UMAC_MACSEC_PROG_TX_CRC 0x310
#define UMAC_MACSEC_CTRL 0x314
#define UMAC_PAUSE_CTRL 0x330
#define UMAC_TX_FLUSH 0x334
#define UMAC_RX_FIFO_STATUS 0x338
#define UMAC_TX_FIFO_STATUS 0x33c
#endif

38
devices/genet/update.sh Executable file
View File

@ -0,0 +1,38 @@
#!/bin/bash
if [ $# -ne 3 ]; then
echo "Need 3 arguments: 1) kernel source dir, 2) previous version, 3) version to add"
exit 1
fi
KERNELDIR=$1
PREVER=$2
KERNELVER=$3
GENETDIR=drivers/net/ethernet/broadcom/genet
FILES="bcmgenet.c bcmgenet.h bcmgenet_wol.c bcmmii.c"
for f in $FILES; do
echo $f
o=${f/\./-$KERNELVER-orig.}
e=${f/\./-$KERNELVER-ethercat.}
cp -v $KERNELDIR/$GENETDIR/$f $o
chmod 644 $o
cp -v $o $e
op=${f/\./-$PREVER-orig.}
ep=${f/\./-$PREVER-ethercat.}
diff -up $op $ep | patch -p1 --no-backup-if-mismatch $e
sed -i s/$PREVER-ethercat.h/$KERNELVER-ethercat.h/ $e
git add $o $e
echo -e "\t$e \\" >> Makefile.am
echo -e "\t$o \\" >> Makefile.am
done
cp $KERNELDIR/$GENETDIR/../unimac.h unimac-$KERNELVER-orig.h
cp $KERNELDIR/$GENETDIR/../unimac.h unimac-$KERNELVER-ethercat.h
git add unimac-$KERNELVER-orig.h unimac-$KERNELVER-ethercat.h
echo -e "\tunimac-$KERNELVER-ethercat.h \\" >> Makefile.am
echo -e "\tunimac-$KERNELVER-orig.h \\" >> Makefile.am
echo "Don't forget to update Makefile.am!"

View File

@ -3438,7 +3438,11 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
dev_err(&pdev->dev, "NVM Read Error\n");
}
#if SUSE_VERSION == 15 && SUSE_PATCHLEVEL >= 5
eth_hw_addr_set(netdev, hw->mac.addr);
#else
memcpy(netdev->dev_addr, hw->mac.addr, netdev->addr_len);
#endif
if (!is_valid_ether_addr(netdev->dev_addr)) {
dev_err(&pdev->dev, "Invalid MAC Address\n");
@ -5108,7 +5112,11 @@ static int igb_set_mac(struct net_device *netdev, void *p)
if (!is_valid_ether_addr(addr->sa_data))
return -EADDRNOTAVAIL;
#if SUSE_VERSION == 15 && SUSE_PATCHLEVEL >= 5
eth_hw_addr_set(netdev, addr->sa_data);
#else
memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
#endif
memcpy(hw->mac.addr, addr->sa_data, netdev->addr_len);
/* set the correct pool for the new PF MAC address in entry 0 */

View File

@ -5320,7 +5320,7 @@ static int rtl_get_ether_clk(struct rtl8169_private *tp)
static void rtl_init_mac_address(struct rtl8169_private *tp)
{
struct net_device *dev = tp->dev;
u8 *mac_addr = dev->dev_addr;
u8 mac_addr[ETH_ALEN];
int rc;
rc = eth_platform_get_mac_address(tp_to_dev(tp), mac_addr);
@ -5338,6 +5338,12 @@ static void rtl_init_mac_address(struct rtl8169_private *tp)
eth_hw_addr_random(dev);
dev_warn(tp_to_dev(tp), "can't read MAC address, setting random one\n");
done:
#if SUSE_VERSION == 15 && SUSE_PATCHLEVEL >= 5
eth_hw_addr_set(dev, mac_addr);
#else
memcpy(dev->dev_addr, mac_addr, sizeof(mac_addr));
#endif
rtl_rar_set(tp, mac_addr);
}

View File

@ -38,6 +38,16 @@
#include "mailbox.h"
#include "ethernet.h"
#ifdef CONFIG_SUSE_KERNEL
#include <linux/suse_version.h>
#else
# ifndef SUSE_VERSION
# define SUSE_VERSION 0
# endif
# ifndef SUSE_PATCHLEVEL
# define SUSE_PATCHLEVEL 0
# endif
#endif
/****************************************************************************/
/** Defines the debug level of EoE processing.
@ -156,7 +166,7 @@ int ec_eoe_init(
// initialize net_device
eoe->dev->netdev_ops = &ec_eoedev_ops;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) || (SUSE_VERSION == 15 && SUSE_PATCHLEVEL >= 5)
eth_hw_addr_set(eoe->dev, mac_addr);
#else
memcpy(eoe->dev->dev_addr, mac_addr, sizeof(mac_addr));
@ -184,7 +194,7 @@ int ec_eoe_init(
// make the last address octet unique
mac_addr[ETH_ALEN - 1] = (uint8_t) eoe->dev->ifindex;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) || (SUSE_VERSION == 15 && SUSE_PATCHLEVEL >= 5)
eth_hw_addr_set(eoe->dev, mac_addr);
#else
memcpy(eoe->dev->dev_addr, mac_addr, sizeof(mac_addr));