Merge branch 'genet_5.10' into 'stable-1.5'
Genet 5.10 and 5.14 See merge request etherlab.org/ethercat!115
This commit is contained in:
commit
97c7b6ed2c
|
|
@ -155,6 +155,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
|
||||
|
|
@ -2389,8 +2400,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);
|
||||
|
|
|
|||
|
|
@ -2415,7 +2415,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;
|
||||
|
|
@ -3099,7 +3103,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");
|
||||
|
|
|
|||
|
|
@ -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.";
|
||||
|
|
@ -1137,7 +1148,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");
|
||||
|
|
@ -2273,7 +2288,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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -49,6 +49,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);
|
||||
|
|
@ -255,7 +266,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);
|
||||
|
|
|
|||
|
|
@ -28,13 +28,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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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__ */
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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!"
|
||||
|
|
@ -3434,7 +3434,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");
|
||||
|
|
@ -5102,7 +5106,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 */
|
||||
|
|
|
|||
|
|
@ -5317,7 +5317,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);
|
||||
|
|
@ -5335,6 +5335,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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -44,6 +44,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.
|
||||
|
|
@ -172,7 +182,7 @@ int ec_eoe_init(
|
|||
eoe->dev->get_stats = ec_eoedev_stats;
|
||||
#endif
|
||||
|
||||
#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));
|
||||
|
|
@ -200,7 +210,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));
|
||||
|
|
|
|||
Loading…
Reference in New Issue