diff options
author | Igal Liberman <igal.liberman@freescale.com> | 2015-12-21 02:21:27 +0200 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-12-27 20:51:40 -0500 |
commit | 57ba4c9b56d898a955f95cb4091aeab28e902cac (patch) | |
tree | 96546473d3a2a889a58f68dcefa87bded37757d3 /drivers/net/ethernet | |
parent | 414fd46e77626f4e1f89ab4be6b876ef91e89dfa (diff) | |
download | op-kernel-dev-57ba4c9b56d898a955f95cb4091aeab28e902cac.zip op-kernel-dev-57ba4c9b56d898a955f95cb4091aeab28e902cac.tar.gz |
fsl/fman: Add FMan MAC support
Add the Data Path Acceleration Architecture Frame Manger MAC support.
This patch adds The FMan MAC configuration, initialization and
runtime control routines.
This patch contains support for these types of MACs:
- dTSEC: Three speed Ethernet controller (10/100/1000 Mbps)
- tGEC: 10G Ethernet controller (10 Gbps)
- mEMAC: Multi-rate Ethernet MAC (10/100/1000/10000 Mbps)
Different FMan revisions have different type and number of MACs.
Signed-off-by: Igal Liberman <igal.liberman@freescale.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet')
-rw-r--r-- | drivers/net/ethernet/freescale/fman/Makefile | 3 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/fman/fman_dtsec.c | 1453 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/fman/fman_dtsec.h | 59 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/fman/fman_mac.h | 278 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/fman/fman_memac.c | 1170 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/fman/fman_memac.h | 60 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/fman/fman_tgec.c | 786 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/fman/fman_tgec.h | 55 |
8 files changed, 3863 insertions, 1 deletions
diff --git a/drivers/net/ethernet/freescale/fman/Makefile b/drivers/net/ethernet/freescale/fman/Makefile index fb5a7f0..43360d70 100644 --- a/drivers/net/ethernet/freescale/fman/Makefile +++ b/drivers/net/ethernet/freescale/fman/Makefile @@ -1,5 +1,6 @@ subdir-ccflags-y += -I$(srctree)/drivers/net/ethernet/freescale/fman -obj-y += fsl_fman.o +obj-y += fsl_fman.o fsl_fman_mac.o fsl_fman-objs := fman_muram.o fman.o +fsl_fman_mac-objs := fman_dtsec.o fman_memac.o fman_tgec.o diff --git a/drivers/net/ethernet/freescale/fman/fman_dtsec.c b/drivers/net/ethernet/freescale/fman/fman_dtsec.c new file mode 100644 index 0000000..d78e2ba --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.c @@ -0,0 +1,1453 @@ +/* + * Copyright 2008-2015 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "fman_dtsec.h" +#include "fman.h" + +#include <linux/slab.h> +#include <linux/bitrev.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/phy.h> +#include <linux/crc32.h> +#include <linux/of_mdio.h> +#include <linux/mii.h> + +/* TBI register addresses */ +#define MII_TBICON 0x11 + +/* TBICON register bit fields */ +#define TBICON_SOFT_RESET 0x8000 /* Soft reset */ +#define TBICON_DISABLE_RX_DIS 0x2000 /* Disable receive disparity */ +#define TBICON_DISABLE_TX_DIS 0x1000 /* Disable transmit disparity */ +#define TBICON_AN_SENSE 0x0100 /* Auto-negotiation sense enable */ +#define TBICON_CLK_SELECT 0x0020 /* Clock select */ +#define TBICON_MI_MODE 0x0010 /* GMII mode (TBI if not set) */ + +#define TBIANA_SGMII 0x4001 +#define TBIANA_1000X 0x01a0 + +/* Interrupt Mask Register (IMASK) */ +#define DTSEC_IMASK_BREN 0x80000000 +#define DTSEC_IMASK_RXCEN 0x40000000 +#define DTSEC_IMASK_MSROEN 0x04000000 +#define DTSEC_IMASK_GTSCEN 0x02000000 +#define DTSEC_IMASK_BTEN 0x01000000 +#define DTSEC_IMASK_TXCEN 0x00800000 +#define DTSEC_IMASK_TXEEN 0x00400000 +#define DTSEC_IMASK_LCEN 0x00040000 +#define DTSEC_IMASK_CRLEN 0x00020000 +#define DTSEC_IMASK_XFUNEN 0x00010000 +#define DTSEC_IMASK_ABRTEN 0x00008000 +#define DTSEC_IMASK_IFERREN 0x00004000 +#define DTSEC_IMASK_MAGEN 0x00000800 +#define DTSEC_IMASK_MMRDEN 0x00000400 +#define DTSEC_IMASK_MMWREN 0x00000200 +#define DTSEC_IMASK_GRSCEN 0x00000100 +#define DTSEC_IMASK_TDPEEN 0x00000002 +#define DTSEC_IMASK_RDPEEN 0x00000001 + +#define DTSEC_EVENTS_MASK \ + ((u32)(DTSEC_IMASK_BREN | \ + DTSEC_IMASK_RXCEN | \ + DTSEC_IMASK_BTEN | \ + DTSEC_IMASK_TXCEN | \ + DTSEC_IMASK_TXEEN | \ + DTSEC_IMASK_ABRTEN | \ + DTSEC_IMASK_LCEN | \ + DTSEC_IMASK_CRLEN | \ + DTSEC_IMASK_XFUNEN | \ + DTSEC_IMASK_IFERREN | \ + DTSEC_IMASK_MAGEN | \ + DTSEC_IMASK_TDPEEN | \ + DTSEC_IMASK_RDPEEN)) + +/* dtsec timestamp event bits */ +#define TMR_PEMASK_TSREEN 0x00010000 +#define TMR_PEVENT_TSRE 0x00010000 + +/* Group address bit indication */ +#define MAC_GROUP_ADDRESS 0x0000010000000000ULL + +/* Defaults */ +#define DEFAULT_HALFDUP_RETRANSMIT 0xf +#define DEFAULT_HALFDUP_COLL_WINDOW 0x37 +#define DEFAULT_TX_PAUSE_TIME 0xf000 +#define DEFAULT_RX_PREPEND 0 +#define DEFAULT_PREAMBLE_LEN 7 +#define DEFAULT_TX_PAUSE_TIME_EXTD 0 +#define DEFAULT_NON_BACK_TO_BACK_IPG1 0x40 +#define DEFAULT_NON_BACK_TO_BACK_IPG2 0x60 +#define DEFAULT_MIN_IFG_ENFORCEMENT 0x50 +#define DEFAULT_BACK_TO_BACK_IPG 0x60 +#define DEFAULT_MAXIMUM_FRAME 0x600 + +/* register related defines (bits, field offsets..) */ +#define DTSEC_ID2_INT_REDUCED_OFF 0x00010000 + +#define DTSEC_ECNTRL_GMIIM 0x00000040 +#define DTSEC_ECNTRL_TBIM 0x00000020 +#define DTSEC_ECNTRL_SGMIIM 0x00000002 +#define DTSEC_ECNTRL_RPM 0x00000010 +#define DTSEC_ECNTRL_R100M 0x00000008 +#define DTSEC_ECNTRL_QSGMIIM 0x00000001 + +#define DTSEC_TCTRL_GTS 0x00000020 + +#define RCTRL_PAL_MASK 0x001f0000 +#define RCTRL_PAL_SHIFT 16 +#define RCTRL_GHTX 0x00000400 +#define RCTRL_GRS 0x00000020 +#define RCTRL_MPROM 0x00000008 +#define RCTRL_RSF 0x00000004 +#define RCTRL_UPROM 0x00000001 + +#define MACCFG1_SOFT_RESET 0x80000000 +#define MACCFG1_RX_FLOW 0x00000020 +#define MACCFG1_TX_FLOW 0x00000010 +#define MACCFG1_TX_EN 0x00000001 +#define MACCFG1_RX_EN 0x00000004 + +#define MACCFG2_NIBBLE_MODE 0x00000100 +#define MACCFG2_BYTE_MODE 0x00000200 +#define MACCFG2_PAD_CRC_EN 0x00000004 +#define MACCFG2_FULL_DUPLEX 0x00000001 +#define MACCFG2_PREAMBLE_LENGTH_MASK 0x0000f000 +#define MACCFG2_PREAMBLE_LENGTH_SHIFT 12 + +#define IPGIFG_NON_BACK_TO_BACK_IPG_1_SHIFT 24 +#define IPGIFG_NON_BACK_TO_BACK_IPG_2_SHIFT 16 +#define IPGIFG_MIN_IFG_ENFORCEMENT_SHIFT 8 + +#define IPGIFG_NON_BACK_TO_BACK_IPG_1 0x7F000000 +#define IPGIFG_NON_BACK_TO_BACK_IPG_2 0x007F0000 +#define IPGIFG_MIN_IFG_ENFORCEMENT 0x0000FF00 +#define IPGIFG_BACK_TO_BACK_IPG 0x0000007F + +#define HAFDUP_EXCESS_DEFER 0x00010000 +#define HAFDUP_COLLISION_WINDOW 0x000003ff +#define HAFDUP_RETRANSMISSION_MAX_SHIFT 12 +#define HAFDUP_RETRANSMISSION_MAX 0x0000f000 + +#define NUM_OF_HASH_REGS 8 /* Number of hash table registers */ + +#define PTV_PTE_MASK 0xffff0000 +#define PTV_PT_MASK 0x0000ffff +#define PTV_PTE_SHIFT 16 + +#define MAX_PACKET_ALIGNMENT 31 +#define MAX_INTER_PACKET_GAP 0x7f +#define MAX_RETRANSMISSION 0x0f +#define MAX_COLLISION_WINDOW 0x03ff + +/* Hash table size (32 bits*8 regs) */ +#define DTSEC_HASH_TABLE_SIZE 256 +/* Extended Hash table size (32 bits*16 regs) */ +#define EXTENDED_HASH_TABLE_SIZE 512 + +/* dTSEC Memory Map registers */ +struct dtsec_regs { + /* dTSEC General Control and Status Registers */ + u32 tsec_id; /* 0x000 ETSEC_ID register */ + u32 tsec_id2; /* 0x004 ETSEC_ID2 register */ + u32 ievent; /* 0x008 Interrupt event register */ + u32 imask; /* 0x00C Interrupt mask register */ + u32 reserved0010[1]; + u32 ecntrl; /* 0x014 E control register */ + u32 ptv; /* 0x018 Pause time value register */ + u32 tbipa; /* 0x01C TBI PHY address register */ + u32 tmr_ctrl; /* 0x020 Time-stamp Control register */ + u32 tmr_pevent; /* 0x024 Time-stamp event register */ + u32 tmr_pemask; /* 0x028 Timer event mask register */ + u32 reserved002c[5]; + u32 tctrl; /* 0x040 Transmit control register */ + u32 reserved0044[3]; + u32 rctrl; /* 0x050 Receive control register */ + u32 reserved0054[11]; + u32 igaddr[8]; /* 0x080-0x09C Individual/group address */ + u32 gaddr[8]; /* 0x0A0-0x0BC Group address registers 0-7 */ + u32 reserved00c0[16]; + u32 maccfg1; /* 0x100 MAC configuration #1 */ + u32 maccfg2; /* 0x104 MAC configuration #2 */ + u32 ipgifg; /* 0x108 IPG/IFG */ + u32 hafdup; /* 0x10C Half-duplex */ + u32 maxfrm; /* 0x110 Maximum frame */ + u32 reserved0114[10]; + u32 ifstat; /* 0x13C Interface status */ + u32 macstnaddr1; /* 0x140 Station Address,part 1 */ + u32 macstnaddr2; /* 0x144 Station Address,part 2 */ + struct { + u32 exact_match1; /* octets 1-4 */ + u32 exact_match2; /* octets 5-6 */ + } macaddr[15]; /* 0x148-0x1BC mac exact match addresses 1-15 */ + u32 reserved01c0[16]; + u32 tr64; /* 0x200 Tx and Rx 64 byte frame counter */ + u32 tr127; /* 0x204 Tx and Rx 65 to 127 byte frame counter */ + u32 tr255; /* 0x208 Tx and Rx 128 to 255 byte frame counter */ + u32 tr511; /* 0x20C Tx and Rx 256 to 511 byte frame counter */ + u32 tr1k; /* 0x210 Tx and Rx 512 to 1023 byte frame counter */ + u32 trmax; /* 0x214 Tx and Rx 1024 to 1518 byte frame counter */ + u32 trmgv; + /* 0x218 Tx and Rx 1519 to 1522 byte good VLAN frame count */ + u32 rbyt; /* 0x21C receive byte counter */ + u32 rpkt; /* 0x220 receive packet counter */ + u32 rfcs; /* 0x224 receive FCS error counter */ + u32 rmca; /* 0x228 RMCA Rx multicast packet counter */ + u32 rbca; /* 0x22C Rx broadcast packet counter */ + u32 rxcf; /* 0x230 Rx control frame packet counter */ + u32 rxpf; /* 0x234 Rx pause frame packet counter */ + u32 rxuo; /* 0x238 Rx unknown OP code counter */ + u32 raln; /* 0x23C Rx alignment error counter */ + u32 rflr; /* 0x240 Rx frame length error counter */ + u32 rcde; /* 0x244 Rx code error counter */ + u32 rcse; /* 0x248 Rx carrier sense error counter */ + u32 rund; /* 0x24C Rx undersize packet counter */ + u32 rovr; /* 0x250 Rx oversize packet counter */ + u32 rfrg; /* 0x254 Rx fragments counter */ + u32 rjbr; /* 0x258 Rx jabber counter */ + u32 rdrp; /* 0x25C Rx drop */ + u32 tbyt; /* 0x260 Tx byte counter */ + u32 tpkt; /* 0x264 Tx packet counter */ + u32 tmca; /* 0x268 Tx multicast packet counter */ + u32 tbca; /* 0x26C Tx broadcast packet counter */ + u32 txpf; /* 0x270 Tx pause control frame counter */ + u32 tdfr; /* 0x274 Tx deferral packet counter */ + u32 tedf; /* 0x278 Tx excessive deferral packet counter */ + u32 tscl; /* 0x27C Tx single collision packet counter */ + u32 tmcl; /* 0x280 Tx multiple collision packet counter */ + u32 tlcl; /* 0x284 Tx late collision packet counter */ + u32 txcl; /* 0x288 Tx excessive collision packet counter */ + u32 tncl; /* 0x28C Tx total collision counter */ + u32 reserved0290[1]; + u32 tdrp; /* 0x294 Tx drop frame counter */ + u32 tjbr; /* 0x298 Tx jabber frame counter */ + u32 tfcs; /* 0x29C Tx FCS error counter */ + u32 txcf; /* 0x2A0 Tx control frame counter */ + u32 tovr; /* 0x2A4 Tx oversize frame counter */ + u32 tund; /* 0x2A8 Tx undersize frame counter */ + u32 tfrg; /* 0x2AC Tx fragments frame counter */ + u32 car1; /* 0x2B0 carry register one register* */ + u32 car2; /* 0x2B4 carry register two register* */ + u32 cam1; /* 0x2B8 carry register one mask register */ + u32 cam2; /* 0x2BC carry register two mask register */ + u32 reserved02c0[848]; +}; + +/* struct dtsec_cfg - dTSEC configuration + * Transmit half-duplex flow control, under software control for 10/100-Mbps + * half-duplex media. If set, back pressure is applied to media by raising + * carrier. + * halfdup_retransmit: + * Number of retransmission attempts following a collision. + * If this is exceeded dTSEC aborts transmission due to excessive collisions. + * The standard specifies the attempt limit to be 15. + * halfdup_coll_window: + * The number of bytes of the frame during which collisions may occur. + * The default value of 55 corresponds to the frame byte at the end of the + * standard 512-bit slot time window. If collisions are detected after this + * byte, the late collision event is asserted and transmission of current + * frame is aborted. + * tx_pad_crc: + * Pad and append CRC. If set, the MAC pads all ransmitted short frames and + * appends a CRC to every frame regardless of padding requirement. + * tx_pause_time: + * Transmit pause time value. This pause value is used as part of the pause + * frame to be sent when a transmit pause frame is initiated. + * If set to 0 this disables transmission of pause frames. + * preamble_len: + * Length, in bytes, of the preamble field preceding each Ethernet + * start-of-frame delimiter byte. The default value of 0x7 should be used in + * order to guarantee reliable operation with IEEE 802.3 compliant hardware. + * rx_prepend: + * Packet alignment padding length. The specified number of bytes (1-31) + * of zero padding are inserted before the start of each received frame. + * For Ethernet, where optional preamble extraction is enabled, the padding + * appears before the preamble, otherwise the padding precedes the + * layer 2 header. + * + * This structure contains basic dTSEC configuration and must be passed to + * init() function. A default set of configuration values can be + * obtained by calling set_dflts(). + */ +struct dtsec_cfg { + u16 halfdup_retransmit; + u16 halfdup_coll_window; + bool tx_pad_crc; + u16 tx_pause_time; + bool ptp_tsu_en; + bool ptp_exception_en; + u32 preamble_len; + u32 rx_prepend; + u16 tx_pause_time_extd; + u16 maximum_frame; + u32 non_back_to_back_ipg1; + u32 non_back_to_back_ipg2; + u32 min_ifg_enforcement; + u32 back_to_back_ipg; +}; + +struct fman_mac { + /* pointer to dTSEC memory mapped registers */ + struct dtsec_regs __iomem *regs; + /* MAC address of device */ + u64 addr; + /* Ethernet physical interface */ + phy_interface_t phy_if; + u16 max_speed; + void *dev_id; /* device cookie used by the exception cbs */ + fman_mac_exception_cb *exception_cb; + fman_mac_exception_cb *event_cb; + /* Number of individual addresses in registers for this station */ + u8 num_of_ind_addr_in_regs; + /* pointer to driver's global address hash table */ + struct eth_hash_t *multicast_addr_hash; + /* pointer to driver's individual address hash table */ + struct eth_hash_t *unicast_addr_hash; + u8 mac_id; + u32 exceptions; + bool ptp_tsu_enabled; + bool en_tsu_err_exeption; + struct dtsec_cfg *dtsec_drv_param; + void *fm; + struct fman_rev_info fm_rev_info; + bool basex_if; + struct phy_device *tbiphy; +}; + +static void set_dflts(struct dtsec_cfg *cfg) +{ + cfg->halfdup_retransmit = DEFAULT_HALFDUP_RETRANSMIT; + cfg->halfdup_coll_window = DEFAULT_HALFDUP_COLL_WINDOW; + cfg->tx_pad_crc = true; + cfg->tx_pause_time = DEFAULT_TX_PAUSE_TIME; + /* PHY address 0 is reserved (DPAA RM) */ + cfg->rx_prepend = DEFAULT_RX_PREPEND; + cfg->ptp_tsu_en = true; + cfg->ptp_exception_en = true; + cfg->preamble_len = DEFAULT_PREAMBLE_LEN; + cfg->tx_pause_time_extd = DEFAULT_TX_PAUSE_TIME_EXTD; + cfg->non_back_to_back_ipg1 = DEFAULT_NON_BACK_TO_BACK_IPG1; + cfg->non_back_to_back_ipg2 = DEFAULT_NON_BACK_TO_BACK_IPG2; + cfg->min_ifg_enforcement = DEFAULT_MIN_IFG_ENFORCEMENT; + cfg->back_to_back_ipg = DEFAULT_BACK_TO_BACK_IPG; + cfg->maximum_frame = DEFAULT_MAXIMUM_FRAME; +} + +static int init(struct dtsec_regs __iomem *regs, struct dtsec_cfg *cfg, + phy_interface_t iface, u16 iface_speed, u8 *macaddr, + u32 exception_mask, u8 tbi_addr) +{ + bool is_rgmii, is_sgmii, is_qsgmii; + int i; + u32 tmp; + + /* Soft reset */ + iowrite32be(MACCFG1_SOFT_RESET, ®s->maccfg1); + iowrite32be(0, ®s->maccfg1); + + /* dtsec_id2 */ + tmp = ioread32be(®s->tsec_id2); + + /* check RGMII support */ + if (iface == PHY_INTERFACE_MODE_RGMII || + iface == PHY_INTERFACE_MODE_RMII) + if (tmp & DTSEC_ID2_INT_REDUCED_OFF) + return -EINVAL; + + if (iface == PHY_INTERFACE_MODE_SGMII || + iface == PHY_INTERFACE_MODE_MII) + if (tmp & DTSEC_ID2_INT_REDUCED_OFF) + return -EINVAL; + + is_rgmii = iface == PHY_INTERFACE_MODE_RGMII; + is_sgmii = iface == PHY_INTERFACE_MODE_SGMII; + is_qsgmii = iface == PHY_INTERFACE_MODE_QSGMII; + + tmp = 0; + if (is_rgmii || iface == PHY_INTERFACE_MODE_GMII) + tmp |= DTSEC_ECNTRL_GMIIM; + if (is_sgmii) + tmp |= (DTSEC_ECNTRL_SGMIIM | DTSEC_ECNTRL_TBIM); + if (is_qsgmii) + tmp |= (DTSEC_ECNTRL_SGMIIM | DTSEC_ECNTRL_TBIM | + DTSEC_ECNTRL_QSGMIIM); + if (is_rgmii) + tmp |= DTSEC_ECNTRL_RPM; + if (iface_speed == SPEED_100) + tmp |= DTSEC_ECNTRL_R100M; + + iowrite32be(tmp, ®s->ecntrl); + + tmp = 0; + + if (cfg->tx_pause_time) + tmp |= cfg->tx_pause_time; + if (cfg->tx_pause_time_extd) + tmp |= cfg->tx_pause_time_extd << PTV_PTE_SHIFT; + iowrite32be(tmp, ®s->ptv); + + tmp = 0; + tmp |= (cfg->rx_prepend << RCTRL_PAL_SHIFT) & RCTRL_PAL_MASK; + /* Accept short frames */ + tmp |= RCTRL_RSF; + + iowrite32be(tmp, ®s->rctrl); + + /* Assign a Phy Address to the TBI (TBIPA). + * Done also in cases where TBI is not selected to avoid conflict with + * the external PHY's Physical address + */ + iowrite32be(tbi_addr, ®s->tbipa); + + iowrite32be(0, ®s->tmr_ctrl); + + if (cfg->ptp_tsu_en) { + tmp = 0; + tmp |= TMR_PEVENT_TSRE; + iowrite32be(tmp, ®s->tmr_pevent); + + if (cfg->ptp_exception_en) { + tmp = 0; + tmp |= TMR_PEMASK_TSREEN; + iowrite32be(tmp, ®s->tmr_pemask); + } + } + + tmp = 0; + tmp |= MACCFG1_RX_FLOW; + tmp |= MACCFG1_TX_FLOW; + iowrite32be(tmp, ®s->maccfg1); + + tmp = 0; + + if (iface_speed < SPEED_1000) + tmp |= MACCFG2_NIBBLE_MODE; + else if (iface_speed == SPEED_1000) + tmp |= MACCFG2_BYTE_MODE; + + tmp |= (cfg->preamble_len << MACCFG2_PREAMBLE_LENGTH_SHIFT) & + MACCFG2_PREAMBLE_LENGTH_MASK; + if (cfg->tx_pad_crc) + tmp |= MACCFG2_PAD_CRC_EN; + /* Full Duplex */ + tmp |= MACCFG2_FULL_DUPLEX; + iowrite32be(tmp, ®s->maccfg2); + + tmp = (((cfg->non_back_to_back_ipg1 << + IPGIFG_NON_BACK_TO_BACK_IPG_1_SHIFT) + & IPGIFG_NON_BACK_TO_BACK_IPG_1) + | ((cfg->non_back_to_back_ipg2 << + IPGIFG_NON_BACK_TO_BACK_IPG_2_SHIFT) + & IPGIFG_NON_BACK_TO_BACK_IPG_2) + | ((cfg->min_ifg_enforcement << IPGIFG_MIN_IFG_ENFORCEMENT_SHIFT) + & IPGIFG_MIN_IFG_ENFORCEMENT) + | (cfg->back_to_back_ipg & IPGIFG_BACK_TO_BACK_IPG)); + iowrite32be(tmp, ®s->ipgifg); + + tmp = 0; + tmp |= HAFDUP_EXCESS_DEFER; + tmp |= ((cfg->halfdup_retransmit << HAFDUP_RETRANSMISSION_MAX_SHIFT) + & HAFDUP_RETRANSMISSION_MAX); + tmp |= (cfg->halfdup_coll_window & HAFDUP_COLLISION_WINDOW); + + iowrite32be(tmp, ®s->hafdup); + + /* Initialize Maximum frame length */ + iowrite32be(cfg->maximum_frame, ®s->maxfrm); + + iowrite32be(0xffffffff, ®s->cam1); + iowrite32be(0xffffffff, ®s->cam2); + + iowrite32be(exception_mask, ®s->imask); + + iowrite32be(0xffffffff, ®s->ievent); + + tmp = (u32)((macaddr[5] << 24) | + (macaddr[4] << 16) | (macaddr[3] << 8) | macaddr[2]); + iowrite32be(tmp, ®s->macstnaddr1); + + tmp = (u32)((macaddr[1] << 24) | (macaddr[0] << 16)); + iowrite32be(tmp, ®s->macstnaddr2); + + /* HASH */ + for (i = 0; i < NUM_OF_HASH_REGS; i++) { + /* Initialize IADDRx */ + iowrite32be(0, ®s->igaddr[i]); + /* Initialize GADDRx */ + iowrite32be(0, ®s->gaddr[i]); + } + + return 0; +} + +static void set_mac_address(struct dtsec_regs __iomem *regs, u8 *adr) +{ + u32 tmp; + + tmp = (u32)((adr[5] << 24) | + (adr[4] << 16) | (adr[3] << 8) | adr[2]); + iowrite32be(tmp, ®s->macstnaddr1); + + tmp = (u32)((adr[1] << 24) | (adr[0] << 16)); + iowrite32be(tmp, ®s->macstnaddr2); +} + +static void set_bucket(struct dtsec_regs __iomem *regs, int bucket, + bool enable) +{ + int reg_idx = (bucket >> 5) & 0xf; + int bit_idx = bucket & 0x1f; + u32 bit_mask = 0x80000000 >> bit_idx; + u32 __iomem *reg; + + if (reg_idx > 7) + reg = ®s->gaddr[reg_idx - 8]; + else + reg = ®s->igaddr[reg_idx]; + + if (enable) + iowrite32be(ioread32be(reg) | bit_mask, reg); + else + iowrite32be(ioread32be(reg) & (~bit_mask), reg); +} + +static int check_init_parameters(struct fman_mac *dtsec) +{ + if (dtsec->max_speed >= SPEED_10000) { + pr_err("1G MAC driver supports 1G or lower speeds\n"); + return -EINVAL; + } + if (dtsec->addr == 0) { + pr_err("Ethernet MAC Must have a valid MAC Address\n"); + return -EINVAL; + } + if ((dtsec->dtsec_drv_param)->rx_prepend > + MAX_PACKET_ALIGNMENT) { + pr_err("packetAlignmentPadding can't be > than %d\n", + MAX_PACKET_ALIGNMENT); + return -EINVAL; + } + if (((dtsec->dtsec_drv_param)->non_back_to_back_ipg1 > + MAX_INTER_PACKET_GAP) || + ((dtsec->dtsec_drv_param)->non_back_to_back_ipg2 > + MAX_INTER_PACKET_GAP) || + ((dtsec->dtsec_drv_param)->back_to_back_ipg > + MAX_INTER_PACKET_GAP)) { + pr_err("Inter packet gap can't be greater than %d\n", + MAX_INTER_PACKET_GAP); + return -EINVAL; + } + if ((dtsec->dtsec_drv_param)->halfdup_retransmit > + MAX_RETRANSMISSION) { + pr_err("maxRetransmission can't be greater than %d\n", + MAX_RETRANSMISSION); + return -EINVAL; + } + if ((dtsec->dtsec_drv_param)->halfdup_coll_window > + MAX_COLLISION_WINDOW) { + pr_err("collisionWindow can't be greater than %d\n", + MAX_COLLISION_WINDOW); + return -EINVAL; + /* If Auto negotiation process is disabled, need to set up the PHY + * using the MII Management Interface + */ + } + if (!dtsec->exception_cb) { + pr_err("uninitialized exception_cb\n"); + return -EINVAL; + } + if (!dtsec->event_cb) { + pr_err("uninitialized event_cb\n"); + return -EINVAL; + } + + return 0; +} + +static int get_exception_flag(enum fman_mac_exceptions exception) +{ + u32 bit_mask; + + switch (exception) { + case FM_MAC_EX_1G_BAB_RX: + bit_mask = DTSEC_IMASK_BREN; + break; + case FM_MAC_EX_1G_RX_CTL: + bit_mask = DTSEC_IMASK_RXCEN; + break; + case FM_MAC_EX_1G_GRATEFUL_TX_STP_COMPLET: + bit_mask = DTSEC_IMASK_GTSCEN; + break; + case FM_MAC_EX_1G_BAB_TX: + bit_mask = DTSEC_IMASK_BTEN; + break; + case FM_MAC_EX_1G_TX_CTL: + bit_mask = DTSEC_IMASK_TXCEN; + break; + case FM_MAC_EX_1G_TX_ERR: + bit_mask = DTSEC_IMASK_TXEEN; + break; + case FM_MAC_EX_1G_LATE_COL: + bit_mask = DTSEC_IMASK_LCEN; + break; + case FM_MAC_EX_1G_COL_RET_LMT: + bit_mask = DTSEC_IMASK_CRLEN; + break; + case FM_MAC_EX_1G_TX_FIFO_UNDRN: + bit_mask = DTSEC_IMASK_XFUNEN; + break; + case FM_MAC_EX_1G_MAG_PCKT: + bit_mask = DTSEC_IMASK_MAGEN; + break; + case FM_MAC_EX_1G_MII_MNG_RD_COMPLET: + bit_mask = DTSEC_IMASK_MMRDEN; + break; + case FM_MAC_EX_1G_MII_MNG_WR_COMPLET: + bit_mask = DTSEC_IMASK_MMWREN; + break; + case FM_MAC_EX_1G_GRATEFUL_RX_STP_COMPLET: + bit_mask = DTSEC_IMASK_GRSCEN; + break; + case FM_MAC_EX_1G_DATA_ERR: + bit_mask = DTSEC_IMASK_TDPEEN; + break; + case FM_MAC_EX_1G_RX_MIB_CNT_OVFL: + bit_mask = DTSEC_IMASK_MSROEN; + break; + default: + bit_mask = 0; + break; + } + + return bit_mask; +} + +static bool is_init_done(struct dtsec_cfg *dtsec_drv_params) +{ + /* Checks if dTSEC driver parameters were initialized */ + if (!dtsec_drv_params) + return true; + + return false; +} + +static u16 dtsec_get_max_frame_length(struct fman_mac *dtsec) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + + if (is_init_done(dtsec->dtsec_drv_param)) + return 0; + + return (u16)ioread32be(®s->maxfrm); +} + +static void dtsec_isr(void *handle) +{ + struct fman_mac *dtsec = (struct fman_mac *)handle; + struct dtsec_regs __iomem *regs = dtsec->regs; + u32 event; + + /* do not handle MDIO events */ + event = ioread32be(®s->ievent) & + (u32)(~(DTSEC_IMASK_MMRDEN | DTSEC_IMASK_MMWREN)); + + event &= ioread32be(®s->imask); + + iowrite32be(event, ®s->ievent); + + if (event & DTSEC_IMASK_BREN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_BAB_RX); + if (event & DTSEC_IMASK_RXCEN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_RX_CTL); + if (event & DTSEC_IMASK_GTSCEN) + dtsec->exception_cb(dtsec->dev_id, + FM_MAC_EX_1G_GRATEFUL_TX_STP_COMPLET); + if (event & DTSEC_IMASK_BTEN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_BAB_TX); + if (event & DTSEC_IMASK_TXCEN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_TX_CTL); + if (event & DTSEC_IMASK_TXEEN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_TX_ERR); + if (event & DTSEC_IMASK_LCEN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_LATE_COL); + if (event & DTSEC_IMASK_CRLEN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_COL_RET_LMT); + if (event & DTSEC_IMASK_XFUNEN) { + /* FM_TX_LOCKUP_ERRATA_DTSEC6 Errata workaround */ + if (dtsec->fm_rev_info.major == 2) { + u32 tpkt1, tmp_reg1, tpkt2, tmp_reg2, i; + /* a. Write 0x00E0_0C00 to DTSEC_ID + * This is a read only register + * b. Read and save the value of TPKT + */ + tpkt1 = ioread32be(®s->tpkt); + + /* c. Read the register at dTSEC address offset 0x32C */ + tmp_reg1 = ioread32be(®s->reserved02c0[27]); + + /* d. Compare bits [9:15] to bits [25:31] of the + * register at address offset 0x32C. + */ + if ((tmp_reg1 & 0x007F0000) != + (tmp_reg1 & 0x0000007F)) { + /* If they are not equal, save the value of + * this register and wait for at least + * MAXFRM*16 ns + */ + usleep_range((u32)(min + (dtsec_get_max_frame_length(dtsec) * + 16 / 1000, 1)), (u32) + (min(dtsec_get_max_frame_length + (dtsec) * 16 / 1000, 1) + 1)); + } + + /* e. Read and save TPKT again and read the register + * at dTSEC address offset 0x32C again + */ + tpkt2 = ioread32be(®s->tpkt); + tmp_reg2 = ioread32be(®s->reserved02c0[27]); + + /* f. Compare the value of TPKT saved in step b to + * value read in step e. Also compare bits [9:15] of + * the register at offset 0x32C saved in step d to the + * value of bits [9:15] saved in step e. If the two + * registers values are unchanged, then the transmit + * portion of the dTSEC controller is locked up and + * the user should proceed to the recover sequence. + */ + if ((tpkt1 == tpkt2) && ((tmp_reg1 & 0x007F0000) == + (tmp_reg2 & 0x007F0000))) { + /* recover sequence */ + + /* a.Write a 1 to RCTRL[GRS] */ + + iowrite32be(ioread32be(®s->rctrl) | + RCTRL_GRS, ®s->rctrl); + + /* b.Wait until IEVENT[GRSC]=1, or at least + * 100 us has elapsed. + */ + for (i = 0; i < 100; i++) { + if (ioread32be(®s->ievent) & + DTSEC_IMASK_GRSCEN) + break; + udelay(1); + } + if (ioread32be(®s->ievent) & + DTSEC_IMASK_GRSCEN) + iowrite32be(DTSEC_IMASK_GRSCEN, + ®s->ievent); + else + pr_debug("Rx lockup due to Tx lockup\n"); + + /* c.Write a 1 to bit n of FM_RSTC + * (offset 0x0CC of FPM) + */ + fman_reset_mac(dtsec->fm, dtsec->mac_id); + + /* d.Wait 4 Tx clocks (32 ns) */ + udelay(1); + + /* e.Write a 0 to bit n of FM_RSTC. */ + /* cleared by FMAN + */ + } + } + + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_TX_FIFO_UNDRN); + } + if (event & DTSEC_IMASK_MAGEN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_MAG_PCKT); + if (event & DTSEC_IMASK_GRSCEN) + dtsec->exception_cb(dtsec->dev_id, + FM_MAC_EX_1G_GRATEFUL_RX_STP_COMPLET); + if (event & DTSEC_IMASK_TDPEEN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_DATA_ERR); + if (event & DTSEC_IMASK_RDPEEN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_1G_RX_DATA_ERR); + + /* masked interrupts */ + WARN_ON(event & DTSEC_IMASK_ABRTEN); + WARN_ON(event & DTSEC_IMASK_IFERREN); +} + +static void dtsec_1588_isr(void *handle) +{ + struct fman_mac *dtsec = (struct fman_mac *)handle; + struct dtsec_regs __iomem *regs = dtsec->regs; + u32 event; + + if (dtsec->ptp_tsu_enabled) { + event = ioread32be(®s->tmr_pevent); + event &= ioread32be(®s->tmr_pemask); + + if (event) { + iowrite32be(event, ®s->tmr_pevent); + WARN_ON(event & TMR_PEVENT_TSRE); + dtsec->exception_cb(dtsec->dev_id, + FM_MAC_EX_1G_1588_TS_RX_ERR); + } + } +} + +static void free_init_resources(struct fman_mac *dtsec) +{ + fman_unregister_intr(dtsec->fm, FMAN_MOD_MAC, dtsec->mac_id, + FMAN_INTR_TYPE_ERR); + fman_unregister_intr(dtsec->fm, FMAN_MOD_MAC, dtsec->mac_id, + FMAN_INTR_TYPE_NORMAL); + + /* release the driver's group hash table */ + free_hash_table(dtsec->multicast_addr_hash); + dtsec->multicast_addr_hash = NULL; + + /* release the driver's individual hash table */ + free_hash_table(dtsec->unicast_addr_hash); + dtsec->unicast_addr_hash = NULL; +} + +int dtsec_cfg_max_frame_len(struct fman_mac *dtsec, u16 new_val) +{ + if (is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + dtsec->dtsec_drv_param->maximum_frame = new_val; + + return 0; +} + +int dtsec_cfg_pad_and_crc(struct fman_mac *dtsec, bool new_val) +{ + if (is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + dtsec->dtsec_drv_param->tx_pad_crc = new_val; + + return 0; +} + +int dtsec_enable(struct fman_mac *dtsec, enum comm_mode mode) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + u32 tmp; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + /* Enable */ + tmp = ioread32be(®s->maccfg1); + if (mode & COMM_MODE_RX) + tmp |= MACCFG1_RX_EN; + if (mode & COMM_MODE_TX) + tmp |= MACCFG1_TX_EN; + + iowrite32be(tmp, ®s->maccfg1); + + /* Graceful start - clear the graceful receive stop bit */ + if (mode & COMM_MODE_TX) + iowrite32be(ioread32be(®s->tctrl) & ~DTSEC_TCTRL_GTS, + ®s->tctrl); + if (mode & COMM_MODE_RX) + iowrite32be(ioread32be(®s->rctrl) & ~RCTRL_GRS, + ®s->rctrl); + + return 0; +} + +int dtsec_disable(struct fman_mac *dtsec, enum comm_mode mode) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + u32 tmp; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + /* Gracefull stop - Assert the graceful transmit stop bit */ + if (mode & COMM_MODE_RX) { + tmp = ioread32be(®s->rctrl) | RCTRL_GRS; + iowrite32be(tmp, ®s->rctrl); + + if (dtsec->fm_rev_info.major == 2) + usleep_range(100, 200); + else + udelay(10); + } + + if (mode & COMM_MODE_TX) { + if (dtsec->fm_rev_info.major == 2) + pr_debug("GTS not supported due to DTSEC_A004 errata.\n"); + else + pr_debug("GTS not supported due to DTSEC_A0014 errata.\n"); + } + + tmp = ioread32be(®s->maccfg1); + if (mode & COMM_MODE_RX) + tmp &= ~MACCFG1_RX_EN; + if (mode & COMM_MODE_TX) + tmp &= ~MACCFG1_TX_EN; + + iowrite32be(tmp, ®s->maccfg1); + + return 0; +} + +int dtsec_set_tx_pause_frames(struct fman_mac *dtsec, + u8 __maybe_unused priority, + u16 pause_time, u16 __maybe_unused thresh_time) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + u32 ptv = 0; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + /* FM_BAD_TX_TS_IN_B_2_B_ERRATA_DTSEC_A003 Errata workaround */ + if (dtsec->fm_rev_info.major == 2) + if (pause_time < 0 && pause_time <= 320) { + pr_warn("pause-time: %d illegal.Should be > 320\n", + pause_time); + return -EINVAL; + } + + if (pause_time) { + ptv = ioread32be(®s->ptv); + ptv &= PTV_PTE_MASK; + ptv |= pause_time & PTV_PT_MASK; + iowrite32be(ptv, ®s->ptv); + + /* trigger the transmission of a flow-control pause frame */ + iowrite32be(ioread32be(®s->maccfg1) | MACCFG1_TX_FLOW, + ®s->maccfg1); + } else + iowrite32be(ioread32be(®s->maccfg1) & ~MACCFG1_TX_FLOW, + ®s->maccfg1); + + return 0; +} + +int dtsec_accept_rx_pause_frames(struct fman_mac *dtsec, bool en) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + u32 tmp; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + tmp = ioread32be(®s->maccfg1); + if (en) + tmp |= MACCFG1_RX_FLOW; + else + tmp &= ~MACCFG1_RX_FLOW; + iowrite32be(tmp, ®s->maccfg1); + + return 0; +} + +int dtsec_modify_mac_address(struct fman_mac *dtsec, enet_addr_t *enet_addr) +{ + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + /* Initialize MAC Station Address registers (1 & 2) + * Station address have to be swapped (big endian to little endian + */ + dtsec->addr = ENET_ADDR_TO_UINT64(*enet_addr); + set_mac_address(dtsec->regs, (u8 *)(*enet_addr)); + + return 0; +} + +int dtsec_add_hash_mac_address(struct fman_mac *dtsec, enet_addr_t *eth_addr) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + struct eth_hash_entry *hash_entry; + u64 addr; + s32 bucket; + u32 crc = 0xFFFFFFFF; + bool mcast, ghtx; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + addr = ENET_ADDR_TO_UINT64(*eth_addr); + + ghtx = (bool)((ioread32be(®s->rctrl) & RCTRL_GHTX) ? true : false); + mcast = (bool)((addr & MAC_GROUP_ADDRESS) ? true : false); + + /* Cannot handle unicast mac addr when GHTX is on */ + if (ghtx && !mcast) { + pr_err("Could not compute hash bucket\n"); + return -EINVAL; + } + crc = crc32_le(crc, (u8 *)eth_addr, ETH_ALEN); + crc = bitrev32(crc); + + /* considering the 9 highest order bits in crc H[8:0]: + *if ghtx = 0 H[8:6] (highest order 3 bits) identify the hash register + *and H[5:1] (next 5 bits) identify the hash bit + *if ghts = 1 H[8:5] (highest order 4 bits) identify the hash register + *and H[4:0] (next 5 bits) identify the hash bit. + * + *In bucket index output the low 5 bits identify the hash register + *bit, while the higher 4 bits identify the hash register + */ + + if (ghtx) { + bucket = (s32)((crc >> 23) & 0x1ff); + } else { + bucket = (s32)((crc >> 24) & 0xff); + /* if !ghtx and mcast the bit must be set in gaddr instead of + *igaddr. + */ + if (mcast) + bucket += 0x100; + } + + set_bucket(dtsec->regs, bucket, true); + + /* Create element to be added to the driver hash table */ + hash_entry = kmalloc(sizeof(*hash_entry), GFP_KERNEL); + if (!hash_entry) + return -ENOMEM; + hash_entry->addr = addr; + INIT_LIST_HEAD(&hash_entry->node); + + if (addr & MAC_GROUP_ADDRESS) + /* Group Address */ + list_add_tail(&hash_entry->node, + &dtsec->multicast_addr_hash->lsts[bucket]); + else + list_add_tail(&hash_entry->node, + &dtsec->unicast_addr_hash->lsts[bucket]); + + return 0; +} + +int dtsec_del_hash_mac_address(struct fman_mac *dtsec, enet_addr_t *eth_addr) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + struct list_head *pos; + struct eth_hash_entry *hash_entry = NULL; + u64 addr; + s32 bucket; + u32 crc = 0xFFFFFFFF; + bool mcast, ghtx; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + addr = ENET_ADDR_TO_UINT64(*eth_addr); + + ghtx = (bool)((ioread32be(®s->rctrl) & RCTRL_GHTX) ? true : false); + mcast = (bool)((addr & MAC_GROUP_ADDRESS) ? true : false); + + /* Cannot handle unicast mac addr when GHTX is on */ + if (ghtx && !mcast) { + pr_err("Could not compute hash bucket\n"); + return -EINVAL; + } + crc = crc32_le(crc, (u8 *)eth_addr, ETH_ALEN); + crc = bitrev32(crc); + + if (ghtx) { + bucket = (s32)((crc >> 23) & 0x1ff); + } else { + bucket = (s32)((crc >> 24) & 0xff); + /* if !ghtx and mcast the bit must be set + * in gaddr instead of igaddr. + */ + if (mcast) + bucket += 0x100; + } + + if (addr & MAC_GROUP_ADDRESS) { + /* Group Address */ + list_for_each(pos, + &dtsec->multicast_addr_hash->lsts[bucket]) { + hash_entry = ETH_HASH_ENTRY_OBJ(pos); + if (hash_entry->addr == addr) { + list_del_init(&hash_entry->node); + kfree(hash_entry); + break; + } + } + if (list_empty(&dtsec->multicast_addr_hash->lsts[bucket])) + set_bucket(dtsec->regs, bucket, false); + } else { + /* Individual Address */ + list_for_each(pos, + &dtsec->unicast_addr_hash->lsts[bucket]) { + hash_entry = ETH_HASH_ENTRY_OBJ(pos); + if (hash_entry->addr == addr) { + list_del_init(&hash_entry->node); + kfree(hash_entry); + break; + } + } + if (list_empty(&dtsec->unicast_addr_hash->lsts[bucket])) + set_bucket(dtsec->regs, bucket, false); + } + + /* address does not exist */ + WARN_ON(!hash_entry); + + return 0; +} + +int dtsec_set_promiscuous(struct fman_mac *dtsec, bool new_val) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + u32 tmp; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + /* Set unicast promiscuous */ + tmp = ioread32be(®s->rctrl); + if (new_val) + tmp |= RCTRL_UPROM; + else + tmp &= ~RCTRL_UPROM; + + iowrite32be(tmp, ®s->rctrl); + + /* Set multicast promiscuous */ + tmp = ioread32be(®s->rctrl); + if (new_val) + tmp |= RCTRL_MPROM; + else + tmp &= ~RCTRL_MPROM; + + iowrite32be(tmp, ®s->rctrl); + + return 0; +} + +int dtsec_adjust_link(struct fman_mac *dtsec, u16 speed) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + u32 tmp; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + tmp = ioread32be(®s->maccfg2); + + /* Full Duplex */ + tmp |= MACCFG2_FULL_DUPLEX; + + tmp &= ~(MACCFG2_NIBBLE_MODE | MACCFG2_BYTE_MODE); + if (speed < SPEED_1000) + tmp |= MACCFG2_NIBBLE_MODE; + else if (speed == SPEED_1000) + tmp |= MACCFG2_BYTE_MODE; + iowrite32be(tmp, ®s->maccfg2); + + tmp = ioread32be(®s->ecntrl); + if (speed == SPEED_100) + tmp |= DTSEC_ECNTRL_R100M; + else + tmp &= ~DTSEC_ECNTRL_R100M; + iowrite32be(tmp, ®s->ecntrl); + + return 0; +} + +int dtsec_restart_autoneg(struct fman_mac *dtsec) +{ + u16 tmp_reg16; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + tmp_reg16 = phy_read(dtsec->tbiphy, MII_BMCR); + + tmp_reg16 &= ~(BMCR_SPEED100 | BMCR_SPEED1000); + tmp_reg16 |= (BMCR_ANENABLE | BMCR_ANRESTART | + BMCR_FULLDPLX | BMCR_SPEED1000); + + phy_write(dtsec->tbiphy, MII_BMCR, tmp_reg16); + + return 0; +} + +int dtsec_get_version(struct fman_mac *dtsec, u32 *mac_version) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + *mac_version = ioread32be(®s->tsec_id); + + return 0; +} + +int dtsec_set_exception(struct fman_mac *dtsec, + enum fman_mac_exceptions exception, bool enable) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + u32 bit_mask = 0; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + if (exception != FM_MAC_EX_1G_1588_TS_RX_ERR) { + bit_mask = get_exception_flag(exception); + if (bit_mask) { + if (enable) + dtsec->exceptions |= bit_mask; + else + dtsec->exceptions &= ~bit_mask; + } else { + pr_err("Undefined exception\n"); + return -EINVAL; + } + if (enable) + iowrite32be(ioread32be(®s->imask) | bit_mask, + ®s->imask); + else + iowrite32be(ioread32be(®s->imask) & ~bit_mask, + ®s->imask); + } else { + if (!dtsec->ptp_tsu_enabled) { + pr_err("Exception valid for 1588 only\n"); + return -EINVAL; + } + switch (exception) { + case FM_MAC_EX_1G_1588_TS_RX_ERR: + if (enable) { + dtsec->en_tsu_err_exeption = true; + iowrite32be(ioread32be(®s->tmr_pemask) | + TMR_PEMASK_TSREEN, + ®s->tmr_pemask); + } else { + dtsec->en_tsu_err_exeption = false; + iowrite32be(ioread32be(®s->tmr_pemask) & + ~TMR_PEMASK_TSREEN, + ®s->tmr_pemask); + } + break; + default: + pr_err("Undefined exception\n"); + return -EINVAL; + } + } + + return 0; +} + +int dtsec_init(struct fman_mac *dtsec) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + struct dtsec_cfg *dtsec_drv_param; + int err; + u16 max_frm_ln; + enet_addr_t eth_addr; + + if (is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + if (DEFAULT_RESET_ON_INIT && + (fman_reset_mac(dtsec->fm, dtsec->mac_id) != 0)) { + pr_err("Can't reset MAC!\n"); + return -EINVAL; + } + + err = check_init_parameters(dtsec); + if (err) + return err; + + dtsec_drv_param = dtsec->dtsec_drv_param; + + MAKE_ENET_ADDR_FROM_UINT64(dtsec->addr, eth_addr); + + err = init(dtsec->regs, dtsec_drv_param, dtsec->phy_if, + dtsec->max_speed, (u8 *)eth_addr, dtsec->exceptions, + dtsec->tbiphy->addr); + if (err) { + free_init_resources(dtsec); + pr_err("DTSEC version doesn't support this i/f mode\n"); + return err; + } + + if (dtsec->phy_if == PHY_INTERFACE_MODE_SGMII) { + u16 tmp_reg16; + + /* Configure the TBI PHY Control Register */ + tmp_reg16 = TBICON_CLK_SELECT | TBICON_SOFT_RESET; + phy_write(dtsec->tbiphy, MII_TBICON, tmp_reg16); + + tmp_reg16 = TBICON_CLK_SELECT; + phy_write(dtsec->tbiphy, MII_TBICON, tmp_reg16); + + tmp_reg16 = (BMCR_RESET | BMCR_ANENABLE | + BMCR_FULLDPLX | BMCR_SPEED1000); + phy_write(dtsec->tbiphy, MII_BMCR, tmp_reg16); + + if (dtsec->basex_if) + tmp_reg16 = TBIANA_1000X; + else + tmp_reg16 = TBIANA_SGMII; + phy_write(dtsec->tbiphy, MII_ADVERTISE, tmp_reg16); + + tmp_reg16 = (BMCR_ANENABLE | BMCR_ANRESTART | + BMCR_FULLDPLX | BMCR_SPEED1000); + + phy_write(dtsec->tbiphy, MII_BMCR, tmp_reg16); + } + + /* Max Frame Length */ + max_frm_ln = (u16)ioread32be(®s->maxfrm); + err = fman_set_mac_max_frame(dtsec->fm, dtsec->mac_id, max_frm_ln); + if (err) { + pr_err("Setting max frame length failed\n"); + free_init_resources(dtsec); + return -EINVAL; + } + + dtsec->multicast_addr_hash = + alloc_hash_table(EXTENDED_HASH_TABLE_SIZE); + if (!dtsec->multicast_addr_hash) { + free_init_resources(dtsec); + pr_err("MC hash table is failed\n"); + return -ENOMEM; + } + + dtsec->unicast_addr_hash = alloc_hash_table(DTSEC_HASH_TABLE_SIZE); + if (!dtsec->unicast_addr_hash) { + free_init_resources(dtsec); + pr_err("UC hash table is failed\n"); + return -ENOMEM; + } + + /* register err intr handler for dtsec to FPM (err) */ + fman_register_intr(dtsec->fm, FMAN_MOD_MAC, dtsec->mac_id, + FMAN_INTR_TYPE_ERR, dtsec_isr, dtsec); + /* register 1588 intr handler for TMR to FPM (normal) */ + fman_register_intr(dtsec->fm, FMAN_MOD_MAC, dtsec->mac_id, + FMAN_INTR_TYPE_NORMAL, dtsec_1588_isr, dtsec); + + kfree(dtsec_drv_param); + dtsec->dtsec_drv_param = NULL; + + return 0; +} + +int dtsec_free(struct fman_mac *dtsec) +{ + free_init_resources(dtsec); + + kfree(dtsec->dtsec_drv_param); + dtsec->dtsec_drv_param = NULL; + kfree(dtsec); + + return 0; +} + +struct fman_mac *dtsec_config(struct fman_mac_params *params) +{ + struct fman_mac *dtsec; + struct dtsec_cfg *dtsec_drv_param; + void __iomem *base_addr; + + base_addr = params->base_addr; + + /* allocate memory for the UCC GETH data structure. */ + dtsec = kzalloc(sizeof(*dtsec), GFP_KERNEL); + if (!dtsec) + return NULL; + + /* allocate memory for the d_tsec driver parameters data structure. */ + dtsec_drv_param = kzalloc(sizeof(*dtsec_drv_param), GFP_KERNEL); + if (!dtsec_drv_param) + goto err_dtsec; + + /* Plant parameter structure pointer */ + dtsec->dtsec_drv_param = dtsec_drv_param; + + set_dflts(dtsec_drv_param); + + dtsec->regs = base_addr; + dtsec->addr = ENET_ADDR_TO_UINT64(params->addr); + dtsec->max_speed = params->max_speed; + dtsec->phy_if = params->phy_if; + dtsec->mac_id = params->mac_id; + dtsec->exceptions = (DTSEC_IMASK_BREN | + DTSEC_IMASK_RXCEN | + DTSEC_IMASK_BTEN | + DTSEC_IMASK_TXCEN | + DTSEC_IMASK_TXEEN | + DTSEC_IMASK_ABRTEN | + DTSEC_IMASK_LCEN | + DTSEC_IMASK_CRLEN | + DTSEC_IMASK_XFUNEN | + DTSEC_IMASK_IFERREN | + DTSEC_IMASK_MAGEN | + DTSEC_IMASK_TDPEEN | + DTSEC_IMASK_RDPEEN); + dtsec->exception_cb = params->exception_cb; + dtsec->event_cb = params->event_cb; + dtsec->dev_id = params->dev_id; + dtsec->ptp_tsu_enabled = dtsec->dtsec_drv_param->ptp_tsu_en; + dtsec->en_tsu_err_exeption = dtsec->dtsec_drv_param->ptp_exception_en; + + dtsec->fm = params->fm; + dtsec->basex_if = params->basex_if; + + if (!params->internal_phy_node) { + pr_err("TBI PHY node is not available\n"); + goto err_dtsec_drv_param; + } + + dtsec->tbiphy = of_phy_find_device(params->internal_phy_node); + if (!dtsec->tbiphy) { + pr_err("of_phy_find_device (TBI PHY) failed\n"); + put_device(&dtsec->tbiphy->dev); + goto err_dtsec_drv_param; + } + + put_device(&dtsec->tbiphy->dev); + + /* Save FMan revision */ + fman_get_revision(dtsec->fm, &dtsec->fm_rev_info); + + return dtsec; + +err_dtsec_drv_param: + kfree(dtsec_drv_param); +err_dtsec: + kfree(dtsec); + return NULL; +} diff --git a/drivers/net/ethernet/freescale/fman/fman_dtsec.h b/drivers/net/ethernet/freescale/fman/fman_dtsec.h new file mode 100644 index 0000000..c4467c0 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.h @@ -0,0 +1,59 @@ +/* + * Copyright 2008-2015 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __DTSEC_H +#define __DTSEC_H + +#include "fman_mac.h" + +struct fman_mac *dtsec_config(struct fman_mac_params *params); +int dtsec_set_promiscuous(struct fman_mac *dtsec, bool new_val); +int dtsec_modify_mac_address(struct fman_mac *dtsec, enet_addr_t *enet_addr); +int dtsec_adjust_link(struct fman_mac *dtsec, + u16 speed); +int dtsec_restart_autoneg(struct fman_mac *dtsec); +int dtsec_cfg_max_frame_len(struct fman_mac *dtsec, u16 new_val); +int dtsec_cfg_pad_and_crc(struct fman_mac *dtsec, bool new_val); +int dtsec_enable(struct fman_mac *dtsec, enum comm_mode mode); +int dtsec_disable(struct fman_mac *dtsec, enum comm_mode mode); +int dtsec_init(struct fman_mac *dtsec); +int dtsec_free(struct fman_mac *dtsec); +int dtsec_accept_rx_pause_frames(struct fman_mac *dtsec, bool en); +int dtsec_set_tx_pause_frames(struct fman_mac *dtsec, u8 priority, + u16 pause_time, u16 thresh_time); +int dtsec_set_exception(struct fman_mac *dtsec, + enum fman_mac_exceptions exception, bool enable); +int dtsec_add_hash_mac_address(struct fman_mac *dtsec, enet_addr_t *eth_addr); +int dtsec_del_hash_mac_address(struct fman_mac *dtsec, enet_addr_t *eth_addr); +int dtsec_get_version(struct fman_mac *dtsec, u32 *mac_version); + +#endif /* __DTSEC_H */ diff --git a/drivers/net/ethernet/freescale/fman/fman_mac.h b/drivers/net/ethernet/freescale/fman/fman_mac.h new file mode 100644 index 0000000..8ddeedb --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_mac.h @@ -0,0 +1,278 @@ +/* + * Copyright 2008-2015 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* FM MAC ... */ +#ifndef __FM_MAC_H +#define __FM_MAC_H + +#include "fman.h" + +#include <linux/slab.h> +#include <linux/phy.h> +#include <linux/if_ether.h> + +struct fman_mac; + +/* Ethernet Address */ +typedef u8 enet_addr_t[ETH_ALEN]; + +#define ENET_ADDR_TO_UINT64(_enet_addr) \ + (u64)(((u64)(_enet_addr)[0] << 40) | \ + ((u64)(_enet_addr)[1] << 32) | \ + ((u64)(_enet_addr)[2] << 24) | \ + ((u64)(_enet_addr)[3] << 16) | \ + ((u64)(_enet_addr)[4] << 8) | \ + ((u64)(_enet_addr)[5])) + +#define MAKE_ENET_ADDR_FROM_UINT64(_addr64, _enet_addr) \ + do { \ + int i; \ + for (i = 0; i < ETH_ALEN; i++) \ + (_enet_addr)[i] = \ + (u8)((_addr64) >> ((5 - i) * 8)); \ + } while (0) + +/* defaults */ +#define DEFAULT_RESET_ON_INIT false + +/* PFC defines */ +#define FSL_FM_PAUSE_TIME_ENABLE 0xf000 +#define FSL_FM_PAUSE_TIME_DISABLE 0 +#define FSL_FM_PAUSE_THRESH_DEFAULT 0 + +#define FM_MAC_NO_PFC 0xff + +/* HASH defines */ +#define ETH_HASH_ENTRY_OBJ(ptr) \ + hlist_entry_safe(ptr, struct eth_hash_entry, node) + +/* Enumeration (bit flags) of communication modes (Transmit, + * receive or both). + */ +enum comm_mode { + COMM_MODE_NONE = 0, /* No transmit/receive communication */ + COMM_MODE_RX = 1, /* Only receive communication */ + COMM_MODE_TX = 2, /* Only transmit communication */ + COMM_MODE_RX_AND_TX = 3 /* Both transmit and receive communication */ +}; + +/* FM MAC Exceptions */ +enum fman_mac_exceptions { + FM_MAC_EX_10G_MDIO_SCAN_EVENT = 0 + /* 10GEC MDIO scan event interrupt */ + , FM_MAC_EX_10G_MDIO_CMD_CMPL + /* 10GEC MDIO command completion interrupt */ + , FM_MAC_EX_10G_REM_FAULT + /* 10GEC, mEMAC Remote fault interrupt */ + , FM_MAC_EX_10G_LOC_FAULT + /* 10GEC, mEMAC Local fault interrupt */ + , FM_MAC_EX_10G_TX_ECC_ER + /* 10GEC, mEMAC Transmit frame ECC error interrupt */ + , FM_MAC_EX_10G_TX_FIFO_UNFL + /* 10GEC, mEMAC Transmit FIFO underflow interrupt */ + , FM_MAC_EX_10G_TX_FIFO_OVFL + /* 10GEC, mEMAC Transmit FIFO overflow interrupt */ + , FM_MAC_EX_10G_TX_ER + /* 10GEC Transmit frame error interrupt */ + , FM_MAC_EX_10G_RX_FIFO_OVFL + /* 10GEC, mEMAC Receive FIFO overflow interrupt */ + , FM_MAC_EX_10G_RX_ECC_ER + /* 10GEC, mEMAC Receive frame ECC error interrupt */ + , FM_MAC_EX_10G_RX_JAB_FRM + /* 10GEC Receive jabber frame interrupt */ + , FM_MAC_EX_10G_RX_OVRSZ_FRM + /* 10GEC Receive oversized frame interrupt */ + , FM_MAC_EX_10G_RX_RUNT_FRM + /* 10GEC Receive runt frame interrupt */ + , FM_MAC_EX_10G_RX_FRAG_FRM + /* 10GEC Receive fragment frame interrupt */ + , FM_MAC_EX_10G_RX_LEN_ER + /* 10GEC Receive payload length error interrupt */ + , FM_MAC_EX_10G_RX_CRC_ER + /* 10GEC Receive CRC error interrupt */ + , FM_MAC_EX_10G_RX_ALIGN_ER + /* 10GEC Receive alignment error interrupt */ + , FM_MAC_EX_1G_BAB_RX + /* dTSEC Babbling receive error */ + , FM_MAC_EX_1G_RX_CTL + /* dTSEC Receive control (pause frame) interrupt */ + , FM_MAC_EX_1G_GRATEFUL_TX_STP_COMPLET + /* dTSEC Graceful transmit stop complete */ + , FM_MAC_EX_1G_BAB_TX + /* dTSEC Babbling transmit error */ + , FM_MAC_EX_1G_TX_CTL + /* dTSEC Transmit control (pause frame) interrupt */ + , FM_MAC_EX_1G_TX_ERR + /* dTSEC Transmit error */ + , FM_MAC_EX_1G_LATE_COL + /* dTSEC Late collision */ + , FM_MAC_EX_1G_COL_RET_LMT + /* dTSEC Collision retry limit */ + , FM_MAC_EX_1G_TX_FIFO_UNDRN + /* dTSEC Transmit FIFO underrun */ + , FM_MAC_EX_1G_MAG_PCKT + /* dTSEC Magic Packet detection */ + , FM_MAC_EX_1G_MII_MNG_RD_COMPLET + /* dTSEC MII management read completion */ + , FM_MAC_EX_1G_MII_MNG_WR_COMPLET + /* dTSEC MII management write completion */ + , FM_MAC_EX_1G_GRATEFUL_RX_STP_COMPLET + /* dTSEC Graceful receive stop complete */ + , FM_MAC_EX_1G_DATA_ERR + /* dTSEC Internal data error on transmit */ + , FM_MAC_1G_RX_DATA_ERR + /* dTSEC Internal data error on receive */ + , FM_MAC_EX_1G_1588_TS_RX_ERR + /* dTSEC Time-Stamp Receive Error */ + , FM_MAC_EX_1G_RX_MIB_CNT_OVFL + /* dTSEC MIB counter overflow */ + , FM_MAC_EX_TS_FIFO_ECC_ERR + /* mEMAC Time-stamp FIFO ECC error interrupt; + * not supported on T4240/B4860 rev1 chips + */ + , FM_MAC_EX_MAGIC_PACKET_INDICATION = FM_MAC_EX_1G_MAG_PCKT + /* mEMAC Magic Packet Indication Interrupt */ +}; + +struct eth_hash_entry { + u64 addr; /* Ethernet Address */ + struct list_head node; +}; + +typedef void (fman_mac_exception_cb)(void *dev_id, + enum fman_mac_exceptions exceptions); + +/* FMan MAC config input */ +struct fman_mac_params { + /* Base of memory mapped FM MAC registers */ + void __iomem *base_addr; + /* MAC address of device; First octet is sent first */ + enet_addr_t addr; + /* MAC ID; numbering of dTSEC and 1G-mEMAC: + * 0 - FM_MAX_NUM_OF_1G_MACS; + * numbering of 10G-MAC (TGEC) and 10G-mEMAC: + * 0 - FM_MAX_NUM_OF_10G_MACS + */ + u8 mac_id; + /* PHY interface */ + phy_interface_t phy_if; + /* Note that the speed should indicate the maximum rate that + * this MAC should support rather than the actual speed; + */ + u16 max_speed; + /* A handle to the FM object this port related to */ + void *fm; + /* MDIO exceptions interrupt source - not valid for all + * MACs; MUST be set to 'NO_IRQ' for MACs that don't have + * mdio-irq, or for polling + */ + void *dev_id; /* device cookie used by the exception cbs */ + fman_mac_exception_cb *event_cb; /* MDIO Events Callback Routine */ + fman_mac_exception_cb *exception_cb;/* Exception Callback Routine */ + /* SGMII/QSGII interface with 1000BaseX auto-negotiation between MAC + * and phy or backplane; Note: 1000BaseX auto-negotiation relates only + * to interface between MAC and phy/backplane, SGMII phy can still + * synchronize with far-end phy at 10Mbps, 100Mbps or 1000Mbps + */ + bool basex_if; + /* Pointer to TBI/PCS PHY node, used for TBI/PCS PHY access */ + struct device_node *internal_phy_node; +}; + +struct eth_hash_t { + u16 size; + struct list_head *lsts; +}; + +static inline struct eth_hash_entry +*dequeue_addr_from_hash_entry(struct list_head *addr_lst) +{ + struct eth_hash_entry *hash_entry = NULL; + + if (!list_empty(addr_lst)) { + hash_entry = ETH_HASH_ENTRY_OBJ(addr_lst->next); + list_del_init(&hash_entry->node); + } + return hash_entry; +} + +static inline void free_hash_table(struct eth_hash_t *hash) +{ + struct eth_hash_entry *hash_entry; + int i = 0; + + if (hash) { + if (hash->lsts) { + for (i = 0; i < hash->size; i++) { + hash_entry = + dequeue_addr_from_hash_entry(&hash->lsts[i]); + while (hash_entry) { + kfree(hash_entry); + hash_entry = + dequeue_addr_from_hash_entry(&hash-> + lsts[i]); + } + } + + kfree(hash->lsts); + } + + kfree(hash); + } +} + +static inline struct eth_hash_t *alloc_hash_table(u16 size) +{ + u32 i; + struct eth_hash_t *hash; + + /* Allocate address hash table */ + hash = kmalloc_array(size, sizeof(struct eth_hash_t *), GFP_KERNEL); + if (!hash) + return NULL; + + hash->size = size; + + hash->lsts = kmalloc_array(hash->size, sizeof(struct list_head), + GFP_KERNEL); + if (!hash->lsts) { + kfree(hash); + return NULL; + } + + for (i = 0; i < hash->size; i++) + INIT_LIST_HEAD(&hash->lsts[i]); + + return hash; +} + +#endif /* __FM_MAC_H */ diff --git a/drivers/net/ethernet/freescale/fman/fman_memac.c b/drivers/net/ethernet/freescale/fman/fman_memac.c new file mode 100644 index 0000000..58bb720 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_memac.c @@ -0,0 +1,1170 @@ +/* + * Copyright 2008-2015 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "fman_memac.h" +#include "fman.h" + +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/phy.h> +#include <linux/of_mdio.h> + +/* PCS registers */ +#define MDIO_SGMII_CR 0x00 +#define MDIO_SGMII_DEV_ABIL_SGMII 0x04 +#define MDIO_SGMII_LINK_TMR_L 0x12 +#define MDIO_SGMII_LINK_TMR_H 0x13 +#define MDIO_SGMII_IF_MODE 0x14 + +/* SGMII Control defines */ +#define SGMII_CR_AN_EN 0x1000 +#define SGMII_CR_RESTART_AN 0x0200 +#define SGMII_CR_FD 0x0100 +#define SGMII_CR_SPEED_SEL1_1G 0x0040 +#define SGMII_CR_DEF_VAL (SGMII_CR_AN_EN | SGMII_CR_FD | \ + SGMII_CR_SPEED_SEL1_1G) + +/* SGMII Device Ability for SGMII defines */ +#define MDIO_SGMII_DEV_ABIL_SGMII_MODE 0x4001 +#define MDIO_SGMII_DEV_ABIL_BASEX_MODE 0x01A0 + +/* Link timer define */ +#define LINK_TMR_L 0xa120 +#define LINK_TMR_H 0x0007 +#define LINK_TMR_L_BASEX 0xaf08 +#define LINK_TMR_H_BASEX 0x002f + +/* SGMII IF Mode defines */ +#define IF_MODE_USE_SGMII_AN 0x0002 +#define IF_MODE_SGMII_EN 0x0001 +#define IF_MODE_SGMII_SPEED_100M 0x0004 +#define IF_MODE_SGMII_SPEED_1G 0x0008 +#define IF_MODE_SGMII_DUPLEX_HALF 0x0010 + +/* Num of additional exact match MAC adr regs */ +#define MEMAC_NUM_OF_PADDRS 7 + +/* Control and Configuration Register (COMMAND_CONFIG) */ +#define CMD_CFG_REG_LOWP_RXETY 0x01000000 /* 07 Rx low power indication */ +#define CMD_CFG_TX_LOWP_ENA 0x00800000 /* 08 Tx Low Power Idle Enable */ +#define CMD_CFG_PFC_MODE 0x00080000 /* 12 Enable PFC */ +#define CMD_CFG_NO_LEN_CHK 0x00020000 /* 14 Payload length check disable */ +#define CMD_CFG_SW_RESET 0x00001000 /* 19 S/W Reset, self clearing bit */ +#define CMD_CFG_TX_PAD_EN 0x00000800 /* 20 Enable Tx padding of frames */ +#define CMD_CFG_PAUSE_IGNORE 0x00000100 /* 23 Ignore Pause frame quanta */ +#define CMD_CFG_CRC_FWD 0x00000040 /* 25 Terminate/frwd CRC of frames */ +#define CMD_CFG_PAD_EN 0x00000020 /* 26 Frame padding removal */ +#define CMD_CFG_PROMIS_EN 0x00000010 /* 27 Promiscuous operation enable */ +#define CMD_CFG_RX_EN 0x00000002 /* 30 MAC receive path enable */ +#define CMD_CFG_TX_EN 0x00000001 /* 31 MAC transmit path enable */ + +/* Transmit FIFO Sections Register (TX_FIFO_SECTIONS) */ +#define TX_FIFO_SECTIONS_TX_EMPTY_MASK 0xFFFF0000 +#define TX_FIFO_SECTIONS_TX_AVAIL_MASK 0x0000FFFF +#define TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_10G 0x00400000 +#define TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_1G 0x00100000 +#define TX_FIFO_SECTIONS_TX_AVAIL_10G 0x00000019 +#define TX_FIFO_SECTIONS_TX_AVAIL_1G 0x00000020 +#define TX_FIFO_SECTIONS_TX_AVAIL_SLOW_10G 0x00000060 + +#define GET_TX_EMPTY_DEFAULT_VALUE(_val) \ +do { \ + _val &= ~TX_FIFO_SECTIONS_TX_EMPTY_MASK; \ + ((_val == TX_FIFO_SECTIONS_TX_AVAIL_10G) ? \ + (_val |= TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_10G) :\ + (_val |= TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_1G));\ +} while (0) + +/* Interface Mode Register (IF_MODE) */ + +#define IF_MODE_MASK 0x00000003 /* 30-31 Mask on i/f mode bits */ +#define IF_MODE_XGMII 0x00000000 /* 30-31 XGMII (10G) interface */ +#define IF_MODE_GMII 0x00000002 /* 30-31 GMII (1G) interface */ +#define IF_MODE_RGMII 0x00000004 +#define IF_MODE_RGMII_AUTO 0x00008000 +#define IF_MODE_RGMII_1000 0x00004000 /* 10 - 1000Mbps RGMII */ +#define IF_MODE_RGMII_100 0x00000000 /* 00 - 100Mbps RGMII */ +#define IF_MODE_RGMII_10 0x00002000 /* 01 - 10Mbps RGMII */ +#define IF_MODE_RGMII_SP_MASK 0x00006000 /* Setsp mask bits */ +#define IF_MODE_RGMII_FD 0x00001000 /* Full duplex RGMII */ +#define IF_MODE_HD 0x00000040 /* Half duplex operation */ + +/* Hash table Control Register (HASHTABLE_CTRL) */ +#define HASH_CTRL_MCAST_EN 0x00000100 +/* 26-31 Hash table address code */ +#define HASH_CTRL_ADDR_MASK 0x0000003F +/* MAC mcast indication */ +#define GROUP_ADDRESS 0x0000010000000000LL +#define HASH_TABLE_SIZE 64 /* Hash tbl size */ + +/* Interrupt Mask Register (IMASK) */ +#define MEMAC_IMASK_MGI 0x40000000 /* 1 Magic pkt detect indication */ +#define MEMAC_IMASK_TSECC_ER 0x20000000 /* 2 Timestamp FIFO ECC error evnt */ +#define MEMAC_IMASK_TECC_ER 0x02000000 /* 6 Transmit frame ECC error evnt */ +#define MEMAC_IMASK_RECC_ER 0x01000000 /* 7 Receive frame ECC error evnt */ + +#define MEMAC_ALL_ERRS_IMASK \ + ((u32)(MEMAC_IMASK_TSECC_ER | \ + MEMAC_IMASK_TECC_ER | \ + MEMAC_IMASK_RECC_ER | \ + MEMAC_IMASK_MGI)) + +#define MEMAC_IEVNT_PCS 0x80000000 /* PCS (XG). Link sync (G) */ +#define MEMAC_IEVNT_AN 0x40000000 /* Auto-negotiation */ +#define MEMAC_IEVNT_LT 0x20000000 /* Link Training/New page */ +#define MEMAC_IEVNT_MGI 0x00004000 /* Magic pkt detection */ +#define MEMAC_IEVNT_TS_ECC_ER 0x00002000 /* Timestamp FIFO ECC error*/ +#define MEMAC_IEVNT_RX_FIFO_OVFL 0x00001000 /* Rx FIFO overflow */ +#define MEMAC_IEVNT_TX_FIFO_UNFL 0x00000800 /* Tx FIFO underflow */ +#define MEMAC_IEVNT_TX_FIFO_OVFL 0x00000400 /* Tx FIFO overflow */ +#define MEMAC_IEVNT_TX_ECC_ER 0x00000200 /* Tx frame ECC error */ +#define MEMAC_IEVNT_RX_ECC_ER 0x00000100 /* Rx frame ECC error */ +#define MEMAC_IEVNT_LI_FAULT 0x00000080 /* Link Interruption flt */ +#define MEMAC_IEVNT_RX_EMPTY 0x00000040 /* Rx FIFO empty */ +#define MEMAC_IEVNT_TX_EMPTY 0x00000020 /* Tx FIFO empty */ +#define MEMAC_IEVNT_RX_LOWP 0x00000010 /* Low Power Idle */ +#define MEMAC_IEVNT_PHY_LOS 0x00000004 /* Phy loss of signal */ +#define MEMAC_IEVNT_REM_FAULT 0x00000002 /* Remote fault (XGMII) */ +#define MEMAC_IEVNT_LOC_FAULT 0x00000001 /* Local fault (XGMII) */ + +#define DEFAULT_PAUSE_QUANTA 0xf000 +#define DEFAULT_FRAME_LENGTH 0x600 +#define DEFAULT_TX_IPG_LENGTH 12 + +#define CLXY_PAUSE_QUANTA_CLX_PQNT 0x0000FFFF +#define CLXY_PAUSE_QUANTA_CLY_PQNT 0xFFFF0000 +#define CLXY_PAUSE_THRESH_CLX_QTH 0x0000FFFF +#define CLXY_PAUSE_THRESH_CLY_QTH 0xFFFF0000 + +struct mac_addr { + /* Lower 32 bits of 48-bit MAC address */ + u32 mac_addr_l; + /* Upper 16 bits of 48-bit MAC address */ + u32 mac_addr_u; +}; + +/* memory map */ +struct memac_regs { + u32 res0000[2]; /* General Control and Status */ + u32 command_config; /* 0x008 Ctrl and cfg */ + struct mac_addr mac_addr0; /* 0x00C-0x010 MAC_ADDR_0...1 */ + u32 maxfrm; /* 0x014 Max frame length */ + u32 res0018[1]; + u32 rx_fifo_sections; /* Receive FIFO configuration reg */ + u32 tx_fifo_sections; /* Transmit FIFO configuration reg */ + u32 res0024[2]; + u32 hashtable_ctrl; /* 0x02C Hash table control */ + u32 res0030[4]; + u32 ievent; /* 0x040 Interrupt event */ + u32 tx_ipg_length; /* 0x044 Transmitter inter-packet-gap */ + u32 res0048; + u32 imask; /* 0x04C Interrupt mask */ + u32 res0050; + u32 pause_quanta[4]; /* 0x054 Pause quanta */ + u32 pause_thresh[4]; /* 0x064 Pause quanta threshold */ + u32 rx_pause_status; /* 0x074 Receive pause status */ + u32 res0078[2]; + struct mac_addr mac_addr[MEMAC_NUM_OF_PADDRS];/* 0x80-0x0B4 mac padr */ + u32 lpwake_timer; /* 0x0B8 Low Power Wakeup Timer */ + u32 sleep_timer; /* 0x0BC Transmit EEE Low Power Timer */ + u32 res00c0[8]; + u32 statn_config; /* 0x0E0 Statistics configuration */ + u32 res00e4[7]; + /* Rx Statistics Counter */ + u32 reoct_l; + u32 reoct_u; + u32 roct_l; + u32 roct_u; + u32 raln_l; + u32 raln_u; + u32 rxpf_l; + u32 rxpf_u; + u32 rfrm_l; + u32 rfrm_u; + u32 rfcs_l; + u32 rfcs_u; + u32 rvlan_l; + u32 rvlan_u; + u32 rerr_l; + u32 rerr_u; + u32 ruca_l; + u32 ruca_u; + u32 rmca_l; + u32 rmca_u; + u32 rbca_l; + u32 rbca_u; + u32 rdrp_l; + u32 rdrp_u; + u32 rpkt_l; + u32 rpkt_u; + u32 rund_l; + u32 rund_u; + u32 r64_l; + u32 r64_u; + u32 r127_l; + u32 r127_u; + u32 r255_l; + u32 r255_u; + u32 r511_l; + u32 r511_u; + u32 r1023_l; + u32 r1023_u; + u32 r1518_l; + u32 r1518_u; + u32 r1519x_l; + u32 r1519x_u; + u32 rovr_l; + u32 rovr_u; + u32 rjbr_l; + u32 rjbr_u; + u32 rfrg_l; + u32 rfrg_u; + u32 rcnp_l; + u32 rcnp_u; + u32 rdrntp_l; + u32 rdrntp_u; + u32 res01d0[12]; + /* Tx Statistics Counter */ + u32 teoct_l; + u32 teoct_u; + u32 toct_l; + u32 toct_u; + u32 res0210[2]; + u32 txpf_l; + u32 txpf_u; + u32 tfrm_l; + u32 tfrm_u; + u32 tfcs_l; + u32 tfcs_u; + u32 tvlan_l; + u32 tvlan_u; + u32 terr_l; + u32 terr_u; + u32 tuca_l; + u32 tuca_u; + u32 tmca_l; + u32 tmca_u; + u32 tbca_l; + u32 tbca_u; + u32 res0258[2]; + u32 tpkt_l; + u32 tpkt_u; + u32 tund_l; + u32 tund_u; + u32 t64_l; + u32 t64_u; + u32 t127_l; + u32 t127_u; + u32 t255_l; + u32 t255_u; + u32 t511_l; + u32 t511_u; + u32 t1023_l; + u32 t1023_u; + u32 t1518_l; + u32 t1518_u; + u32 t1519x_l; + u32 t1519x_u; + u32 res02a8[6]; + u32 tcnp_l; + u32 tcnp_u; + u32 res02c8[14]; + /* Line Interface Control */ + u32 if_mode; /* 0x300 Interface Mode Control */ + u32 if_status; /* 0x304 Interface Status */ + u32 res0308[14]; + /* HiGig/2 */ + u32 hg_config; /* 0x340 Control and cfg */ + u32 res0344[3]; + u32 hg_pause_quanta; /* 0x350 Pause quanta */ + u32 res0354[3]; + u32 hg_pause_thresh; /* 0x360 Pause quanta threshold */ + u32 res0364[3]; + u32 hgrx_pause_status; /* 0x370 Receive pause status */ + u32 hg_fifos_status; /* 0x374 fifos status */ + u32 rhm; /* 0x378 rx messages counter */ + u32 thm; /* 0x37C tx messages counter */ +}; + +struct memac_cfg { + bool reset_on_init; + bool pause_ignore; + bool promiscuous_mode_enable; + struct fixed_phy_status *fixed_link; + u16 max_frame_length; + u16 pause_quanta; + u32 tx_ipg_length; +}; + +struct fman_mac { + /* Pointer to MAC memory mapped registers */ + struct memac_regs __iomem *regs; + /* MAC address of device */ + u64 addr; + /* Ethernet physical interface */ + phy_interface_t phy_if; + u16 max_speed; + void *dev_id; /* device cookie used by the exception cbs */ + fman_mac_exception_cb *exception_cb; + fman_mac_exception_cb *event_cb; + /* Pointer to driver's global address hash table */ + struct eth_hash_t *multicast_addr_hash; + /* Pointer to driver's individual address hash table */ + struct eth_hash_t *unicast_addr_hash; + u8 mac_id; + u32 exceptions; + struct memac_cfg *memac_drv_param; + void *fm; + struct fman_rev_info fm_rev_info; + bool basex_if; + struct phy_device *pcsphy; +}; + +static void add_addr_in_paddr(struct memac_regs __iomem *regs, u8 *adr, + u8 paddr_num) +{ + u32 tmp0, tmp1; + + tmp0 = (u32)(adr[0] | adr[1] << 8 | adr[2] << 16 | adr[3] << 24); + tmp1 = (u32)(adr[4] | adr[5] << 8); + + if (paddr_num == 0) { + iowrite32be(tmp0, ®s->mac_addr0.mac_addr_l); + iowrite32be(tmp1, ®s->mac_addr0.mac_addr_u); + } else { + iowrite32be(tmp0, ®s->mac_addr[paddr_num - 1].mac_addr_l); + iowrite32be(tmp1, ®s->mac_addr[paddr_num - 1].mac_addr_u); + } +} + +static int reset(struct memac_regs __iomem *regs) +{ + u32 tmp; + int count; + + tmp = ioread32be(®s->command_config); + + tmp |= CMD_CFG_SW_RESET; + + iowrite32be(tmp, ®s->command_config); + + count = 100; + do { + udelay(1); + } while ((ioread32be(®s->command_config) & CMD_CFG_SW_RESET) && + --count); + + if (count == 0) + return -EBUSY; + + return 0; +} + +static void set_exception(struct memac_regs __iomem *regs, u32 val, + bool enable) +{ + u32 tmp; + + tmp = ioread32be(®s->imask); + if (enable) + tmp |= val; + else + tmp &= ~val; + + iowrite32be(tmp, ®s->imask); +} + +static int init(struct memac_regs __iomem *regs, struct memac_cfg *cfg, + phy_interface_t phy_if, u16 speed, bool slow_10g_if, + u32 exceptions) +{ + u32 tmp; + + /* Config */ + tmp = 0; + if (cfg->promiscuous_mode_enable) + tmp |= CMD_CFG_PROMIS_EN; + if (cfg->pause_ignore) + tmp |= CMD_CFG_PAUSE_IGNORE; + + /* Payload length check disable */ + tmp |= CMD_CFG_NO_LEN_CHK; + /* Enable padding of frames in transmit direction */ + tmp |= CMD_CFG_TX_PAD_EN; + + tmp |= CMD_CFG_CRC_FWD; + + iowrite32be(tmp, ®s->command_config); + + /* Max Frame Length */ + iowrite32be((u32)cfg->max_frame_length, ®s->maxfrm); + + /* Pause Time */ + iowrite32be((u32)cfg->pause_quanta, ®s->pause_quanta[0]); + iowrite32be((u32)0, ®s->pause_thresh[0]); + + /* IF_MODE */ + tmp = 0; + switch (phy_if) { + case PHY_INTERFACE_MODE_XGMII: + tmp |= IF_MODE_XGMII; + break; + default: + tmp |= IF_MODE_GMII; + if (phy_if == PHY_INTERFACE_MODE_RGMII) + tmp |= IF_MODE_RGMII | IF_MODE_RGMII_AUTO; + } + iowrite32be(tmp, ®s->if_mode); + + /* TX_FIFO_SECTIONS */ + tmp = 0; + if (phy_if == PHY_INTERFACE_MODE_XGMII) { + if (slow_10g_if) { + tmp |= (TX_FIFO_SECTIONS_TX_AVAIL_SLOW_10G | + TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_10G); + } else { + tmp |= (TX_FIFO_SECTIONS_TX_AVAIL_10G | + TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_10G); + } + } else { + tmp |= (TX_FIFO_SECTIONS_TX_AVAIL_1G | + TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_1G); + } + iowrite32be(tmp, ®s->tx_fifo_sections); + + /* clear all pending events and set-up interrupts */ + iowrite32be(0xffffffff, ®s->ievent); + set_exception(regs, exceptions, true); + + return 0; +} + +static void set_dflts(struct memac_cfg *cfg) +{ + cfg->reset_on_init = false; + cfg->promiscuous_mode_enable = false; + cfg->pause_ignore = false; + cfg->tx_ipg_length = DEFAULT_TX_IPG_LENGTH; + cfg->max_frame_length = DEFAULT_FRAME_LENGTH; + cfg->pause_quanta = DEFAULT_PAUSE_QUANTA; +} + +static u32 get_mac_addr_hash_code(u64 eth_addr) +{ + u64 mask1, mask2; + u32 xor_val = 0; + u8 i, j; + + for (i = 0; i < 6; i++) { + mask1 = eth_addr & (u64)0x01; + eth_addr >>= 1; + + for (j = 0; j < 7; j++) { + mask2 = eth_addr & (u64)0x01; + mask1 ^= mask2; + eth_addr >>= 1; + } + + xor_val |= (mask1 << (5 - i)); + } + + return xor_val; +} + +static void setup_sgmii_internal_phy(struct fman_mac *memac, + struct fixed_phy_status *fixed_link) +{ + u16 tmp_reg16; + + /* SGMII mode */ + tmp_reg16 = IF_MODE_SGMII_EN; + if (!fixed_link) + /* AN enable */ + tmp_reg16 |= IF_MODE_USE_SGMII_AN; + else { + switch (fixed_link->speed) { + case 10: + /* For 10M: IF_MODE[SPEED_10M] = 0 */ + break; + case 100: + tmp_reg16 |= IF_MODE_SGMII_SPEED_100M; + break; + case 1000: /* fallthrough */ + default: + tmp_reg16 |= IF_MODE_SGMII_SPEED_1G; + break; + } + if (!fixed_link->duplex) + tmp_reg16 |= IF_MODE_SGMII_DUPLEX_HALF; + } + phy_write(memac->pcsphy, MDIO_SGMII_IF_MODE, tmp_reg16); + + /* Device ability according to SGMII specification */ + tmp_reg16 = MDIO_SGMII_DEV_ABIL_SGMII_MODE; + phy_write(memac->pcsphy, MDIO_SGMII_DEV_ABIL_SGMII, tmp_reg16); + + /* Adjust link timer for SGMII - + * According to Cisco SGMII specification the timer should be 1.6 ms. + * The link_timer register is configured in units of the clock. + * - When running as 1G SGMII, Serdes clock is 125 MHz, so + * unit = 1 / (125*10^6 Hz) = 8 ns. + * 1.6 ms in units of 8 ns = 1.6ms / 8ns = 2*10^5 = 0x30d40 + * - When running as 2.5G SGMII, Serdes clock is 312.5 MHz, so + * unit = 1 / (312.5*10^6 Hz) = 3.2 ns. + * 1.6 ms in units of 3.2 ns = 1.6ms / 3.2ns = 5*10^5 = 0x7a120. + * Since link_timer value of 1G SGMII will be too short for 2.5 SGMII, + * we always set up here a value of 2.5 SGMII. + */ + phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_H, LINK_TMR_H); + phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_L, LINK_TMR_L); + + if (!fixed_link) + /* Restart AN */ + tmp_reg16 = SGMII_CR_DEF_VAL | SGMII_CR_RESTART_AN; + else + /* AN disabled */ + tmp_reg16 = SGMII_CR_DEF_VAL & ~SGMII_CR_AN_EN; + phy_write(memac->pcsphy, 0x0, tmp_reg16); +} + +static void setup_sgmii_internal_phy_base_x(struct fman_mac *memac) +{ + u16 tmp_reg16; + + /* AN Device capability */ + tmp_reg16 = MDIO_SGMII_DEV_ABIL_BASEX_MODE; + phy_write(memac->pcsphy, MDIO_SGMII_DEV_ABIL_SGMII, tmp_reg16); + + /* Adjust link timer for SGMII - + * For Serdes 1000BaseX auto-negotiation the timer should be 10 ms. + * The link_timer register is configured in units of the clock. + * - When running as 1G SGMII, Serdes clock is 125 MHz, so + * unit = 1 / (125*10^6 Hz) = 8 ns. + * 10 ms in units of 8 ns = 10ms / 8ns = 1250000 = 0x1312d0 + * - When running as 2.5G SGMII, Serdes clock is 312.5 MHz, so + * unit = 1 / (312.5*10^6 Hz) = 3.2 ns. + * 10 ms in units of 3.2 ns = 10ms / 3.2ns = 3125000 = 0x2faf08. + * Since link_timer value of 1G SGMII will be too short for 2.5 SGMII, + * we always set up here a value of 2.5 SGMII. + */ + phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_H, LINK_TMR_H_BASEX); + phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_L, LINK_TMR_L_BASEX); + + /* Restart AN */ + tmp_reg16 = SGMII_CR_DEF_VAL | SGMII_CR_RESTART_AN; + phy_write(memac->pcsphy, 0x0, tmp_reg16); +} + +static int check_init_parameters(struct fman_mac *memac) +{ + if (memac->addr == 0) { + pr_err("Ethernet MAC must have a valid MAC address\n"); + return -EINVAL; + } + if (!memac->exception_cb) { + pr_err("Uninitialized exception handler\n"); + return -EINVAL; + } + if (!memac->event_cb) { + pr_warn("Uninitialize event handler\n"); + return -EINVAL; + } + + return 0; +} + +static int get_exception_flag(enum fman_mac_exceptions exception) +{ + u32 bit_mask; + + switch (exception) { + case FM_MAC_EX_10G_TX_ECC_ER: + bit_mask = MEMAC_IMASK_TECC_ER; + break; + case FM_MAC_EX_10G_RX_ECC_ER: + bit_mask = MEMAC_IMASK_RECC_ER; + break; + case FM_MAC_EX_TS_FIFO_ECC_ERR: + bit_mask = MEMAC_IMASK_TSECC_ER; + break; + case FM_MAC_EX_MAGIC_PACKET_INDICATION: + bit_mask = MEMAC_IMASK_MGI; + break; + default: + bit_mask = 0; + break; + } + + return bit_mask; +} + +static void memac_err_exception(void *handle) +{ + struct fman_mac *memac = (struct fman_mac *)handle; + struct memac_regs __iomem *regs = memac->regs; + u32 event, imask; + + event = ioread32be(®s->ievent); + imask = ioread32be(®s->imask); + + /* Imask include both error and notification/event bits. + * Leaving only error bits enabled by imask. + * The imask error bits are shifted by 16 bits offset from + * their corresponding location in the ievent - hence the >> 16 + */ + event &= ((imask & MEMAC_ALL_ERRS_IMASK) >> 16); + + iowrite32be(event, ®s->ievent); + + if (event & MEMAC_IEVNT_TS_ECC_ER) + memac->exception_cb(memac->dev_id, FM_MAC_EX_TS_FIFO_ECC_ERR); + if (event & MEMAC_IEVNT_TX_ECC_ER) + memac->exception_cb(memac->dev_id, FM_MAC_EX_10G_TX_ECC_ER); + if (event & MEMAC_IEVNT_RX_ECC_ER) + memac->exception_cb(memac->dev_id, FM_MAC_EX_10G_RX_ECC_ER); +} + +static void memac_exception(void *handle) +{ + struct fman_mac *memac = (struct fman_mac *)handle; + struct memac_regs __iomem *regs = memac->regs; + u32 event, imask; + + event = ioread32be(®s->ievent); + imask = ioread32be(®s->imask); + + /* Imask include both error and notification/event bits. + * Leaving only error bits enabled by imask. + * The imask error bits are shifted by 16 bits offset from + * their corresponding location in the ievent - hence the >> 16 + */ + event &= ((imask & MEMAC_ALL_ERRS_IMASK) >> 16); + + iowrite32be(event, ®s->ievent); + + if (event & MEMAC_IEVNT_MGI) + memac->exception_cb(memac->dev_id, + FM_MAC_EX_MAGIC_PACKET_INDICATION); +} + +static void free_init_resources(struct fman_mac *memac) +{ + fman_unregister_intr(memac->fm, FMAN_MOD_MAC, memac->mac_id, + FMAN_INTR_TYPE_ERR); + + fman_unregister_intr(memac->fm, FMAN_MOD_MAC, memac->mac_id, + FMAN_INTR_TYPE_NORMAL); + + /* release the driver's group hash table */ + free_hash_table(memac->multicast_addr_hash); + memac->multicast_addr_hash = NULL; + + /* release the driver's individual hash table */ + free_hash_table(memac->unicast_addr_hash); + memac->unicast_addr_hash = NULL; +} + +static bool is_init_done(struct memac_cfg *memac_drv_params) +{ + /* Checks if mEMAC driver parameters were initialized */ + if (!memac_drv_params) + return true; + + return false; +} + +int memac_enable(struct fman_mac *memac, enum comm_mode mode) +{ + struct memac_regs __iomem *regs = memac->regs; + u32 tmp; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + tmp = ioread32be(®s->command_config); + if (mode & COMM_MODE_RX) + tmp |= CMD_CFG_RX_EN; + if (mode & COMM_MODE_TX) + tmp |= CMD_CFG_TX_EN; + + iowrite32be(tmp, ®s->command_config); + + return 0; +} + +int memac_disable(struct fman_mac *memac, enum comm_mode mode) +{ + struct memac_regs __iomem *regs = memac->regs; + u32 tmp; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + tmp = ioread32be(®s->command_config); + if (mode & COMM_MODE_RX) + tmp &= ~CMD_CFG_RX_EN; + if (mode & COMM_MODE_TX) + tmp &= ~CMD_CFG_TX_EN; + + iowrite32be(tmp, ®s->command_config); + + return 0; +} + +int memac_set_promiscuous(struct fman_mac *memac, bool new_val) +{ + struct memac_regs __iomem *regs = memac->regs; + u32 tmp; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + tmp = ioread32be(®s->command_config); + if (new_val) + tmp |= CMD_CFG_PROMIS_EN; + else + tmp &= ~CMD_CFG_PROMIS_EN; + + iowrite32be(tmp, ®s->command_config); + + return 0; +} + +int memac_adjust_link(struct fman_mac *memac, u16 speed) +{ + struct memac_regs __iomem *regs = memac->regs; + u32 tmp; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + tmp = ioread32be(®s->if_mode); + + /* Set full duplex */ + tmp &= ~IF_MODE_HD; + + if (memac->phy_if == PHY_INTERFACE_MODE_RGMII) { + /* Configure RGMII in manual mode */ + tmp &= ~IF_MODE_RGMII_AUTO; + tmp &= ~IF_MODE_RGMII_SP_MASK; + /* Full duplex */ + tmp |= IF_MODE_RGMII_FD; + + switch (speed) { + case SPEED_1000: + tmp |= IF_MODE_RGMII_1000; + break; + case SPEED_100: + tmp |= IF_MODE_RGMII_100; + break; + case SPEED_10: + tmp |= IF_MODE_RGMII_10; + break; + default: + break; + } + } + + iowrite32be(tmp, ®s->if_mode); + + return 0; +} + +int memac_cfg_max_frame_len(struct fman_mac *memac, u16 new_val) +{ + if (is_init_done(memac->memac_drv_param)) + return -EINVAL; + + memac->memac_drv_param->max_frame_length = new_val; + + return 0; +} + +int memac_cfg_reset_on_init(struct fman_mac *memac, bool enable) +{ + if (is_init_done(memac->memac_drv_param)) + return -EINVAL; + + memac->memac_drv_param->reset_on_init = enable; + + return 0; +} + +int memac_cfg_fixed_link(struct fman_mac *memac, + struct fixed_phy_status *fixed_link) +{ + if (is_init_done(memac->memac_drv_param)) + return -EINVAL; + + memac->memac_drv_param->fixed_link = fixed_link; + + return 0; +} + +int memac_set_tx_pause_frames(struct fman_mac *memac, u8 priority, + u16 pause_time, u16 thresh_time) +{ + struct memac_regs __iomem *regs = memac->regs; + u32 tmp; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + tmp = ioread32be(®s->tx_fifo_sections); + + GET_TX_EMPTY_DEFAULT_VALUE(tmp); + iowrite32be(tmp, ®s->tx_fifo_sections); + + tmp = ioread32be(®s->command_config); + tmp &= ~CMD_CFG_PFC_MODE; + priority = 0; + + iowrite32be(tmp, ®s->command_config); + + tmp = ioread32be(®s->pause_quanta[priority / 2]); + if (priority % 2) + tmp &= CLXY_PAUSE_QUANTA_CLX_PQNT; + else + tmp &= CLXY_PAUSE_QUANTA_CLY_PQNT; + tmp |= ((u32)pause_time << (16 * (priority % 2))); + iowrite32be(tmp, ®s->pause_quanta[priority / 2]); + + tmp = ioread32be(®s->pause_thresh[priority / 2]); + if (priority % 2) + tmp &= CLXY_PAUSE_THRESH_CLX_QTH; + else + tmp &= CLXY_PAUSE_THRESH_CLY_QTH; + tmp |= ((u32)thresh_time << (16 * (priority % 2))); + iowrite32be(tmp, ®s->pause_thresh[priority / 2]); + + return 0; +} + +int memac_accept_rx_pause_frames(struct fman_mac *memac, bool en) +{ + struct memac_regs __iomem *regs = memac->regs; + u32 tmp; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + tmp = ioread32be(®s->command_config); + if (en) + tmp &= ~CMD_CFG_PAUSE_IGNORE; + else + tmp |= CMD_CFG_PAUSE_IGNORE; + + iowrite32be(tmp, ®s->command_config); + + return 0; +} + +int memac_modify_mac_address(struct fman_mac *memac, enet_addr_t *enet_addr) +{ + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + add_addr_in_paddr(memac->regs, (u8 *)(*enet_addr), 0); + + return 0; +} + +int memac_add_hash_mac_address(struct fman_mac *memac, enet_addr_t *eth_addr) +{ + struct memac_regs __iomem *regs = memac->regs; + struct eth_hash_entry *hash_entry; + u32 hash; + u64 addr; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + addr = ENET_ADDR_TO_UINT64(*eth_addr); + + if (!(addr & GROUP_ADDRESS)) { + /* Unicast addresses not supported in hash */ + pr_err("Unicast Address\n"); + return -EINVAL; + } + hash = get_mac_addr_hash_code(addr) & HASH_CTRL_ADDR_MASK; + + /* Create element to be added to the driver hash table */ + hash_entry = kmalloc(sizeof(*hash_entry), GFP_KERNEL); + if (!hash_entry) + return -ENOMEM; + hash_entry->addr = addr; + INIT_LIST_HEAD(&hash_entry->node); + + list_add_tail(&hash_entry->node, + &memac->multicast_addr_hash->lsts[hash]); + iowrite32be(hash | HASH_CTRL_MCAST_EN, ®s->hashtable_ctrl); + + return 0; +} + +int memac_del_hash_mac_address(struct fman_mac *memac, enet_addr_t *eth_addr) +{ + struct memac_regs __iomem *regs = memac->regs; + struct eth_hash_entry *hash_entry = NULL; + struct list_head *pos; + u32 hash; + u64 addr; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + addr = ENET_ADDR_TO_UINT64(*eth_addr); + + hash = get_mac_addr_hash_code(addr) & HASH_CTRL_ADDR_MASK; + + list_for_each(pos, &memac->multicast_addr_hash->lsts[hash]) { + hash_entry = ETH_HASH_ENTRY_OBJ(pos); + if (hash_entry->addr == addr) { + list_del_init(&hash_entry->node); + kfree(hash_entry); + break; + } + } + if (list_empty(&memac->multicast_addr_hash->lsts[hash])) + iowrite32be(hash & ~HASH_CTRL_MCAST_EN, ®s->hashtable_ctrl); + + return 0; +} + +int memac_set_exception(struct fman_mac *memac, + enum fman_mac_exceptions exception, bool enable) +{ + u32 bit_mask = 0; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + bit_mask = get_exception_flag(exception); + if (bit_mask) { + if (enable) + memac->exceptions |= bit_mask; + else + memac->exceptions &= ~bit_mask; + } else { + pr_err("Undefined exception\n"); + return -EINVAL; + } + set_exception(memac->regs, bit_mask, enable); + + return 0; +} + +int memac_init(struct fman_mac *memac) +{ + struct memac_cfg *memac_drv_param; + u8 i; + enet_addr_t eth_addr; + bool slow_10g_if = false; + struct fixed_phy_status *fixed_link; + int err; + u32 reg32 = 0; + + if (is_init_done(memac->memac_drv_param)) + return -EINVAL; + + err = check_init_parameters(memac); + if (err) + return err; + + memac_drv_param = memac->memac_drv_param; + + if (memac->fm_rev_info.major == 6 && memac->fm_rev_info.minor == 4) + slow_10g_if = true; + + /* First, reset the MAC if desired. */ + if (memac_drv_param->reset_on_init) { + err = reset(memac->regs); + if (err) { + pr_err("mEMAC reset failed\n"); + return err; + } + } + + /* MAC Address */ + MAKE_ENET_ADDR_FROM_UINT64(memac->addr, eth_addr); + add_addr_in_paddr(memac->regs, (u8 *)eth_addr, 0); + + fixed_link = memac_drv_param->fixed_link; + + init(memac->regs, memac->memac_drv_param, memac->phy_if, + memac->max_speed, slow_10g_if, memac->exceptions); + + /* FM_RX_FIFO_CORRUPT_ERRATA_10GMAC_A006320 errata workaround + * Exists only in FMan 6.0 and 6.3. + */ + if ((memac->fm_rev_info.major == 6) && + ((memac->fm_rev_info.minor == 0) || + (memac->fm_rev_info.minor == 3))) { + /* MAC strips CRC from received frames - this workaround + * should decrease the likelihood of bug appearance + */ + reg32 = ioread32be(&memac->regs->command_config); + reg32 &= ~CMD_CFG_CRC_FWD; + iowrite32be(reg32, &memac->regs->command_config); + } + + if (memac->phy_if == PHY_INTERFACE_MODE_SGMII) { + /* Configure internal SGMII PHY */ + if (memac->basex_if) + setup_sgmii_internal_phy_base_x(memac); + else + setup_sgmii_internal_phy(memac, fixed_link); + } else if (memac->phy_if == PHY_INTERFACE_MODE_QSGMII) { + /* Configure 4 internal SGMII PHYs */ + for (i = 0; i < 4; i++) { + u8 qsmgii_phy_addr, phy_addr; + /* QSGMII PHY address occupies 3 upper bits of 5-bit + * phy_address; the lower 2 bits are used to extend + * register address space and access each one of 4 + * ports inside QSGMII. + */ + phy_addr = memac->pcsphy->addr; + qsmgii_phy_addr = (u8)((phy_addr << 2) | i); + memac->pcsphy->addr = qsmgii_phy_addr; + if (memac->basex_if) + setup_sgmii_internal_phy_base_x(memac); + else + setup_sgmii_internal_phy(memac, fixed_link); + + memac->pcsphy->addr = phy_addr; + } + } + + /* Max Frame Length */ + err = fman_set_mac_max_frame(memac->fm, memac->mac_id, + memac_drv_param->max_frame_length); + if (err) { + pr_err("settings Mac max frame length is FAILED\n"); + return err; + } + + memac->multicast_addr_hash = alloc_hash_table(HASH_TABLE_SIZE); + if (!memac->multicast_addr_hash) { + free_init_resources(memac); + pr_err("allocation hash table is FAILED\n"); + return -ENOMEM; + } + + memac->unicast_addr_hash = alloc_hash_table(HASH_TABLE_SIZE); + if (!memac->unicast_addr_hash) { + free_init_resources(memac); + pr_err("allocation hash table is FAILED\n"); + return -ENOMEM; + } + + fman_register_intr(memac->fm, FMAN_MOD_MAC, memac->mac_id, + FMAN_INTR_TYPE_ERR, memac_err_exception, memac); + + fman_register_intr(memac->fm, FMAN_MOD_MAC, memac->mac_id, + FMAN_INTR_TYPE_NORMAL, memac_exception, memac); + + kfree(memac_drv_param); + memac->memac_drv_param = NULL; + + return 0; +} + +int memac_free(struct fman_mac *memac) +{ + free_init_resources(memac); + + kfree(memac->memac_drv_param); + kfree(memac); + + return 0; +} + +struct fman_mac *memac_config(struct fman_mac_params *params) +{ + struct fman_mac *memac; + struct memac_cfg *memac_drv_param; + void __iomem *base_addr; + + base_addr = params->base_addr; + /* allocate memory for the m_emac data structure */ + memac = kzalloc(sizeof(*memac), GFP_KERNEL); + if (!memac) + return NULL; + + /* allocate memory for the m_emac driver parameters data structure */ + memac_drv_param = kzalloc(sizeof(*memac_drv_param), GFP_KERNEL); + if (!memac_drv_param) { + memac_free(memac); + return NULL; + } + + /* Plant parameter structure pointer */ + memac->memac_drv_param = memac_drv_param; + + set_dflts(memac_drv_param); + + memac->addr = ENET_ADDR_TO_UINT64(params->addr); + + memac->regs = base_addr; + memac->max_speed = params->max_speed; + memac->phy_if = params->phy_if; + memac->mac_id = params->mac_id; + memac->exceptions = (MEMAC_IMASK_TSECC_ER | MEMAC_IMASK_TECC_ER | + MEMAC_IMASK_RECC_ER | MEMAC_IMASK_MGI); + memac->exception_cb = params->exception_cb; + memac->event_cb = params->event_cb; + memac->dev_id = params->dev_id; + memac->fm = params->fm; + memac->basex_if = params->basex_if; + + /* Save FMan revision */ + fman_get_revision(memac->fm, &memac->fm_rev_info); + + if (memac->phy_if == PHY_INTERFACE_MODE_SGMII) { + if (!params->internal_phy_node) { + pr_err("PCS PHY node is not available\n"); + memac_free(memac); + return NULL; + } + + memac->pcsphy = of_phy_find_device(params->internal_phy_node); + if (!memac->pcsphy) { + pr_err("of_phy_find_device (PCS PHY) failed\n"); + memac_free(memac); + return NULL; + } + } + + return memac; +} diff --git a/drivers/net/ethernet/freescale/fman/fman_memac.h b/drivers/net/ethernet/freescale/fman/fman_memac.h new file mode 100644 index 0000000..173d8e0 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_memac.h @@ -0,0 +1,60 @@ +/* + * Copyright 2008-2015 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __MEMAC_H +#define __MEMAC_H + +#include "fman_mac.h" + +#include <linux/netdevice.h> + +struct fman_mac *memac_config(struct fman_mac_params *params); +int memac_set_promiscuous(struct fman_mac *memac, bool new_val); +int memac_modify_mac_address(struct fman_mac *memac, enet_addr_t *enet_addr); +int memac_adjust_link(struct fman_mac *memac, u16 speed); +int memac_cfg_max_frame_len(struct fman_mac *memac, u16 new_val); +int memac_cfg_reset_on_init(struct fman_mac *memac, bool enable); +int memac_cfg_fixed_link(struct fman_mac *memac, + struct fixed_phy_status *fixed_link); +int memac_enable(struct fman_mac *memac, enum comm_mode mode); +int memac_disable(struct fman_mac *memac, enum comm_mode mode); +int memac_init(struct fman_mac *memac); +int memac_free(struct fman_mac *memac); +int memac_accept_rx_pause_frames(struct fman_mac *memac, bool en); +int memac_set_tx_pause_frames(struct fman_mac *memac, u8 priority, + u16 pause_time, u16 thresh_time); +int memac_set_exception(struct fman_mac *memac, + enum fman_mac_exceptions exception, bool enable); +int memac_add_hash_mac_address(struct fman_mac *memac, enet_addr_t *eth_addr); +int memac_del_hash_mac_address(struct fman_mac *memac, enet_addr_t *eth_addr); + +#endif /* __MEMAC_H */ diff --git a/drivers/net/ethernet/freescale/fman/fman_tgec.c b/drivers/net/ethernet/freescale/fman/fman_tgec.c new file mode 100644 index 0000000..efabb04 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_tgec.c @@ -0,0 +1,786 @@ +/* + * Copyright 2008-2015 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "fman_tgec.h" +#include "fman.h" + +#include <linux/slab.h> +#include <linux/bitrev.h> +#include <linux/io.h> +#include <linux/crc32.h> + +/* Transmit Inter-Packet Gap Length Register (TX_IPG_LENGTH) */ +#define TGEC_TX_IPG_LENGTH_MASK 0x000003ff + +/* Command and Configuration Register (COMMAND_CONFIG) */ +#define CMD_CFG_NO_LEN_CHK 0x00020000 +#define CMD_CFG_PAUSE_IGNORE 0x00000100 +#define CMF_CFG_CRC_FWD 0x00000040 +#define CMD_CFG_PROMIS_EN 0x00000010 +#define CMD_CFG_RX_EN 0x00000002 +#define CMD_CFG_TX_EN 0x00000001 + +/* Interrupt Mask Register (IMASK) */ +#define TGEC_IMASK_MDIO_SCAN_EVENT 0x00010000 +#define TGEC_IMASK_MDIO_CMD_CMPL 0x00008000 +#define TGEC_IMASK_REM_FAULT 0x00004000 +#define TGEC_IMASK_LOC_FAULT 0x00002000 +#define TGEC_IMASK_TX_ECC_ER 0x00001000 +#define TGEC_IMASK_TX_FIFO_UNFL 0x00000800 +#define TGEC_IMASK_TX_FIFO_OVFL 0x00000400 +#define TGEC_IMASK_TX_ER 0x00000200 +#define TGEC_IMASK_RX_FIFO_OVFL 0x00000100 +#define TGEC_IMASK_RX_ECC_ER 0x00000080 +#define TGEC_IMASK_RX_JAB_FRM 0x00000040 +#define TGEC_IMASK_RX_OVRSZ_FRM 0x00000020 +#define TGEC_IMASK_RX_RUNT_FRM 0x00000010 +#define TGEC_IMASK_RX_FRAG_FRM 0x00000008 +#define TGEC_IMASK_RX_LEN_ER 0x00000004 +#define TGEC_IMASK_RX_CRC_ER 0x00000002 +#define TGEC_IMASK_RX_ALIGN_ER 0x00000001 + +/* Hashtable Control Register (HASHTABLE_CTRL) */ +#define TGEC_HASH_MCAST_SHIFT 23 +#define TGEC_HASH_MCAST_EN 0x00000200 +#define TGEC_HASH_ADR_MSK 0x000001ff + +#define DEFAULT_TX_IPG_LENGTH 12 +#define DEFAULT_MAX_FRAME_LENGTH 0x600 +#define DEFAULT_PAUSE_QUANT 0xf000 + +/* number of pattern match registers (entries) */ +#define TGEC_NUM_OF_PADDRS 1 + +/* Group address bit indication */ +#define GROUP_ADDRESS 0x0000010000000000LL + +/* Hash table size (= 32 bits*8 regs) */ +#define TGEC_HASH_TABLE_SIZE 512 + +/* tGEC memory map */ +struct tgec_regs { + u32 tgec_id; /* 0x000 Controller ID */ + u32 reserved001[1]; /* 0x004 */ + u32 command_config; /* 0x008 Control and configuration */ + u32 mac_addr_0; /* 0x00c Lower 32 bits of the MAC adr */ + u32 mac_addr_1; /* 0x010 Upper 16 bits of the MAC adr */ + u32 maxfrm; /* 0x014 Maximum frame length */ + u32 pause_quant; /* 0x018 Pause quanta */ + u32 rx_fifo_sections; /* 0x01c */ + u32 tx_fifo_sections; /* 0x020 */ + u32 rx_fifo_almost_f_e; /* 0x024 */ + u32 tx_fifo_almost_f_e; /* 0x028 */ + u32 hashtable_ctrl; /* 0x02c Hash table control */ + u32 mdio_cfg_status; /* 0x030 */ + u32 mdio_command; /* 0x034 */ + u32 mdio_data; /* 0x038 */ + u32 mdio_regaddr; /* 0x03c */ + u32 status; /* 0x040 */ + u32 tx_ipg_len; /* 0x044 Transmitter inter-packet-gap */ + u32 mac_addr_2; /* 0x048 Lower 32 bits of 2nd MAC adr */ + u32 mac_addr_3; /* 0x04c Upper 16 bits of 2nd MAC adr */ + u32 rx_fifo_ptr_rd; /* 0x050 */ + u32 rx_fifo_ptr_wr; /* 0x054 */ + u32 tx_fifo_ptr_rd; /* 0x058 */ + u32 tx_fifo_ptr_wr; /* 0x05c */ + u32 imask; /* 0x060 Interrupt mask */ + u32 ievent; /* 0x064 Interrupt event */ + u32 udp_port; /* 0x068 Defines a UDP Port number */ + u32 type_1588v2; /* 0x06c Type field for 1588v2 */ + u32 reserved070[4]; /* 0x070 */ + /* 10Ge Statistics Counter */ + u32 tfrm_u; /* 80 aFramesTransmittedOK */ + u32 tfrm_l; /* 84 aFramesTransmittedOK */ + u32 rfrm_u; /* 88 aFramesReceivedOK */ + u32 rfrm_l; /* 8c aFramesReceivedOK */ + u32 rfcs_u; /* 90 aFrameCheckSequenceErrors */ + u32 rfcs_l; /* 94 aFrameCheckSequenceErrors */ + u32 raln_u; /* 98 aAlignmentErrors */ + u32 raln_l; /* 9c aAlignmentErrors */ + u32 txpf_u; /* A0 aPAUSEMACCtrlFramesTransmitted */ + u32 txpf_l; /* A4 aPAUSEMACCtrlFramesTransmitted */ + u32 rxpf_u; /* A8 aPAUSEMACCtrlFramesReceived */ + u32 rxpf_l; /* Ac aPAUSEMACCtrlFramesReceived */ + u32 rlong_u; /* B0 aFrameTooLongErrors */ + u32 rlong_l; /* B4 aFrameTooLongErrors */ + u32 rflr_u; /* B8 aInRangeLengthErrors */ + u32 rflr_l; /* Bc aInRangeLengthErrors */ + u32 tvlan_u; /* C0 VLANTransmittedOK */ + u32 tvlan_l; /* C4 VLANTransmittedOK */ + u32 rvlan_u; /* C8 VLANReceivedOK */ + u32 rvlan_l; /* Cc VLANReceivedOK */ + u32 toct_u; /* D0 if_out_octets */ + u32 toct_l; /* D4 if_out_octets */ + u32 roct_u; /* D8 if_in_octets */ + u32 roct_l; /* Dc if_in_octets */ + u32 ruca_u; /* E0 if_in_ucast_pkts */ + u32 ruca_l; /* E4 if_in_ucast_pkts */ + u32 rmca_u; /* E8 ifInMulticastPkts */ + u32 rmca_l; /* Ec ifInMulticastPkts */ + u32 rbca_u; /* F0 ifInBroadcastPkts */ + u32 rbca_l; /* F4 ifInBroadcastPkts */ + u32 terr_u; /* F8 if_out_errors */ + u32 terr_l; /* Fc if_out_errors */ + u32 reserved100[2]; /* 100-108 */ + u32 tuca_u; /* 108 if_out_ucast_pkts */ + u32 tuca_l; /* 10c if_out_ucast_pkts */ + u32 tmca_u; /* 110 ifOutMulticastPkts */ + u32 tmca_l; /* 114 ifOutMulticastPkts */ + u32 tbca_u; /* 118 ifOutBroadcastPkts */ + u32 tbca_l; /* 11c ifOutBroadcastPkts */ + u32 rdrp_u; /* 120 etherStatsDropEvents */ + u32 rdrp_l; /* 124 etherStatsDropEvents */ + u32 reoct_u; /* 128 etherStatsOctets */ + u32 reoct_l; /* 12c etherStatsOctets */ + u32 rpkt_u; /* 130 etherStatsPkts */ + u32 rpkt_l; /* 134 etherStatsPkts */ + u32 trund_u; /* 138 etherStatsUndersizePkts */ + u32 trund_l; /* 13c etherStatsUndersizePkts */ + u32 r64_u; /* 140 etherStatsPkts64Octets */ + u32 r64_l; /* 144 etherStatsPkts64Octets */ + u32 r127_u; /* 148 etherStatsPkts65to127Octets */ + u32 r127_l; /* 14c etherStatsPkts65to127Octets */ + u32 r255_u; /* 150 etherStatsPkts128to255Octets */ + u32 r255_l; /* 154 etherStatsPkts128to255Octets */ + u32 r511_u; /* 158 etherStatsPkts256to511Octets */ + u32 r511_l; /* 15c etherStatsPkts256to511Octets */ + u32 r1023_u; /* 160 etherStatsPkts512to1023Octets */ + u32 r1023_l; /* 164 etherStatsPkts512to1023Octets */ + u32 r1518_u; /* 168 etherStatsPkts1024to1518Octets */ + u32 r1518_l; /* 16c etherStatsPkts1024to1518Octets */ + u32 r1519x_u; /* 170 etherStatsPkts1519toX */ + u32 r1519x_l; /* 174 etherStatsPkts1519toX */ + u32 trovr_u; /* 178 etherStatsOversizePkts */ + u32 trovr_l; /* 17c etherStatsOversizePkts */ + u32 trjbr_u; /* 180 etherStatsJabbers */ + u32 trjbr_l; /* 184 etherStatsJabbers */ + u32 trfrg_u; /* 188 etherStatsFragments */ + u32 trfrg_l; /* 18C etherStatsFragments */ + u32 rerr_u; /* 190 if_in_errors */ + u32 rerr_l; /* 194 if_in_errors */ +}; + +struct tgec_cfg { + bool pause_ignore; + bool promiscuous_mode_enable; + u16 max_frame_length; + u16 pause_quant; + u32 tx_ipg_length; +}; + +struct fman_mac { + /* Pointer to the memory mapped registers. */ + struct tgec_regs __iomem *regs; + /* MAC address of device; */ + u64 addr; + u16 max_speed; + void *dev_id; /* device cookie used by the exception cbs */ + fman_mac_exception_cb *exception_cb; + fman_mac_exception_cb *event_cb; + /* pointer to driver's global address hash table */ + struct eth_hash_t *multicast_addr_hash; + /* pointer to driver's individual address hash table */ + struct eth_hash_t *unicast_addr_hash; + u8 mac_id; + u32 exceptions; + struct tgec_cfg *cfg; + void *fm; + struct fman_rev_info fm_rev_info; +}; + +static void set_mac_address(struct tgec_regs __iomem *regs, u8 *adr) +{ + u32 tmp0, tmp1; + + tmp0 = (u32)(adr[0] | adr[1] << 8 | adr[2] << 16 | adr[3] << 24); + tmp1 = (u32)(adr[4] | adr[5] << 8); + iowrite32be(tmp0, ®s->mac_addr_0); + iowrite32be(tmp1, ®s->mac_addr_1); +} + +static void set_dflts(struct tgec_cfg *cfg) +{ + cfg->promiscuous_mode_enable = false; + cfg->pause_ignore = false; + cfg->tx_ipg_length = DEFAULT_TX_IPG_LENGTH; + cfg->max_frame_length = DEFAULT_MAX_FRAME_LENGTH; + cfg->pause_quant = DEFAULT_PAUSE_QUANT; +} + +static int init(struct tgec_regs __iomem *regs, struct tgec_cfg *cfg, + u32 exception_mask) +{ + u32 tmp; + + /* Config */ + tmp = CMF_CFG_CRC_FWD; + if (cfg->promiscuous_mode_enable) + tmp |= CMD_CFG_PROMIS_EN; + if (cfg->pause_ignore) + tmp |= CMD_CFG_PAUSE_IGNORE; + /* Payload length check disable */ + tmp |= CMD_CFG_NO_LEN_CHK; + iowrite32be(tmp, ®s->command_config); + + /* Max Frame Length */ + iowrite32be((u32)cfg->max_frame_length, ®s->maxfrm); + /* Pause Time */ + iowrite32be(cfg->pause_quant, ®s->pause_quant); + + /* clear all pending events and set-up interrupts */ + iowrite32be(0xffffffff, ®s->ievent); + iowrite32be(ioread32be(®s->imask) | exception_mask, ®s->imask); + + return 0; +} + +static int check_init_parameters(struct fman_mac *tgec) +{ + if (tgec->max_speed < SPEED_10000) { + pr_err("10G MAC driver only support 10G speed\n"); + return -EINVAL; + } + if (tgec->addr == 0) { + pr_err("Ethernet 10G MAC Must have valid MAC Address\n"); + return -EINVAL; + } + if (!tgec->exception_cb) { + pr_err("uninitialized exception_cb\n"); + return -EINVAL; + } + if (!tgec->event_cb) { + pr_err("uninitialized event_cb\n"); + return -EINVAL; + } + + return 0; +} + +static int get_exception_flag(enum fman_mac_exceptions exception) +{ + u32 bit_mask; + + switch (exception) { + case FM_MAC_EX_10G_MDIO_SCAN_EVENT: + bit_mask = TGEC_IMASK_MDIO_SCAN_EVENT; + break; + case FM_MAC_EX_10G_MDIO_CMD_CMPL: + bit_mask = TGEC_IMASK_MDIO_CMD_CMPL; + break; + case FM_MAC_EX_10G_REM_FAULT: + bit_mask = TGEC_IMASK_REM_FAULT; + break; + case FM_MAC_EX_10G_LOC_FAULT: + bit_mask = TGEC_IMASK_LOC_FAULT; + break; + case FM_MAC_EX_10G_TX_ECC_ER: + bit_mask = TGEC_IMASK_TX_ECC_ER; + break; + case FM_MAC_EX_10G_TX_FIFO_UNFL: + bit_mask = TGEC_IMASK_TX_FIFO_UNFL; + break; + case FM_MAC_EX_10G_TX_FIFO_OVFL: + bit_mask = TGEC_IMASK_TX_FIFO_OVFL; + break; + case FM_MAC_EX_10G_TX_ER: + bit_mask = TGEC_IMASK_TX_ER; + break; + case FM_MAC_EX_10G_RX_FIFO_OVFL: + bit_mask = TGEC_IMASK_RX_FIFO_OVFL; + break; + case FM_MAC_EX_10G_RX_ECC_ER: + bit_mask = TGEC_IMASK_RX_ECC_ER; + break; + case FM_MAC_EX_10G_RX_JAB_FRM: + bit_mask = TGEC_IMASK_RX_JAB_FRM; + break; + case FM_MAC_EX_10G_RX_OVRSZ_FRM: + bit_mask = TGEC_IMASK_RX_OVRSZ_FRM; + break; + case FM_MAC_EX_10G_RX_RUNT_FRM: + bit_mask = TGEC_IMASK_RX_RUNT_FRM; + break; + case FM_MAC_EX_10G_RX_FRAG_FRM: + bit_mask = TGEC_IMASK_RX_FRAG_FRM; + break; + case FM_MAC_EX_10G_RX_LEN_ER: + bit_mask = TGEC_IMASK_RX_LEN_ER; + break; + case FM_MAC_EX_10G_RX_CRC_ER: + bit_mask = TGEC_IMASK_RX_CRC_ER; + break; + case FM_MAC_EX_10G_RX_ALIGN_ER: + bit_mask = TGEC_IMASK_RX_ALIGN_ER; + break; + default: + bit_mask = 0; + break; + } + + return bit_mask; +} + +static void tgec_err_exception(void *handle) +{ + struct fman_mac *tgec = (struct fman_mac *)handle; + struct tgec_regs __iomem *regs = tgec->regs; + u32 event; + + /* do not handle MDIO events */ + event = ioread32be(®s->ievent) & + ~(TGEC_IMASK_MDIO_SCAN_EVENT | + TGEC_IMASK_MDIO_CMD_CMPL); + + event &= ioread32be(®s->imask); + + iowrite32be(event, ®s->ievent); + + if (event & TGEC_IMASK_REM_FAULT) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_REM_FAULT); + if (event & TGEC_IMASK_LOC_FAULT) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_LOC_FAULT); + if (event & TGEC_IMASK_TX_ECC_ER) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_TX_ECC_ER); + if (event & TGEC_IMASK_TX_FIFO_UNFL) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_TX_FIFO_UNFL); + if (event & TGEC_IMASK_TX_FIFO_OVFL) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_TX_FIFO_OVFL); + if (event & TGEC_IMASK_TX_ER) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_TX_ER); + if (event & TGEC_IMASK_RX_FIFO_OVFL) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_FIFO_OVFL); + if (event & TGEC_IMASK_RX_ECC_ER) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_ECC_ER); + if (event & TGEC_IMASK_RX_JAB_FRM) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_JAB_FRM); + if (event & TGEC_IMASK_RX_OVRSZ_FRM) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_OVRSZ_FRM); + if (event & TGEC_IMASK_RX_RUNT_FRM) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_RUNT_FRM); + if (event & TGEC_IMASK_RX_FRAG_FRM) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_FRAG_FRM); + if (event & TGEC_IMASK_RX_LEN_ER) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_LEN_ER); + if (event & TGEC_IMASK_RX_CRC_ER) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_CRC_ER); + if (event & TGEC_IMASK_RX_ALIGN_ER) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_ALIGN_ER); +} + +static void free_init_resources(struct fman_mac *tgec) +{ + fman_unregister_intr(tgec->fm, FMAN_MOD_MAC, tgec->mac_id, + FMAN_INTR_TYPE_ERR); + + /* release the driver's group hash table */ + free_hash_table(tgec->multicast_addr_hash); + tgec->multicast_addr_hash = NULL; + + /* release the driver's individual hash table */ + free_hash_table(tgec->unicast_addr_hash); + tgec->unicast_addr_hash = NULL; +} + +static bool is_init_done(struct tgec_cfg *cfg) +{ + /* Checks if tGEC driver parameters were initialized */ + if (!cfg) + return true; + + return false; +} + +int tgec_enable(struct fman_mac *tgec, enum comm_mode mode) +{ + struct tgec_regs __iomem *regs = tgec->regs; + u32 tmp; + + if (!is_init_done(tgec->cfg)) + return -EINVAL; + + tmp = ioread32be(®s->command_config); + if (mode & COMM_MODE_RX) + tmp |= CMD_CFG_RX_EN; + if (mode & COMM_MODE_TX) + tmp |= CMD_CFG_TX_EN; + iowrite32be(tmp, ®s->command_config); + + return 0; +} + +int tgec_disable(struct fman_mac *tgec, enum comm_mode mode) +{ + struct tgec_regs __iomem *regs = tgec->regs; + u32 tmp; + + if (!is_init_done(tgec->cfg)) + return -EINVAL; + + tmp = ioread32be(®s->command_config); + if (mode & COMM_MODE_RX) + tmp &= ~CMD_CFG_RX_EN; + if (mode & COMM_MODE_TX) + tmp &= ~CMD_CFG_TX_EN; + iowrite32be(tmp, ®s->command_config); + + return 0; +} + +int tgec_set_promiscuous(struct fman_mac *tgec, bool new_val) +{ + struct tgec_regs __iomem *regs = tgec->regs; + u32 tmp; + + if (!is_init_done(tgec->cfg)) + return -EINVAL; + + tmp = ioread32be(®s->command_config); + if (new_val) + tmp |= CMD_CFG_PROMIS_EN; + else + tmp &= ~CMD_CFG_PROMIS_EN; + iowrite32be(tmp, ®s->command_config); + + return 0; +} + +int tgec_cfg_max_frame_len(struct fman_mac *tgec, u16 new_val) +{ + if (is_init_done(tgec->cfg)) + return -EINVAL; + + tgec->cfg->max_frame_length = new_val; + + return 0; +} + +int tgec_set_tx_pause_frames(struct fman_mac *tgec, u8 __maybe_unused priority, + u16 pause_time, u16 __maybe_unused thresh_time) +{ + struct tgec_regs __iomem *regs = tgec->regs; + + if (!is_init_done(tgec->cfg)) + return -EINVAL; + + iowrite32be((u32)pause_time, ®s->pause_quant); + + return 0; +} + +int tgec_accept_rx_pause_frames(struct fman_mac *tgec, bool en) +{ + struct tgec_regs __iomem *regs = tgec->regs; + u32 tmp; + + if (!is_init_done(tgec->cfg)) + return -EINVAL; + + tmp = ioread32be(®s->command_config); + if (!en) + tmp |= CMD_CFG_PAUSE_IGNORE; + else + tmp &= ~CMD_CFG_PAUSE_IGNORE; + iowrite32be(tmp, ®s->command_config); + + return 0; +} + +int tgec_modify_mac_address(struct fman_mac *tgec, enet_addr_t *p_enet_addr) +{ + if (!is_init_done(tgec->cfg)) + return -EINVAL; + + tgec->addr = ENET_ADDR_TO_UINT64(*p_enet_addr); + set_mac_address(tgec->regs, (u8 *)(*p_enet_addr)); + + return 0; +} + +int tgec_add_hash_mac_address(struct fman_mac *tgec, enet_addr_t *eth_addr) +{ + struct tgec_regs __iomem *regs = tgec->regs; + struct eth_hash_entry *hash_entry; + u32 crc = 0xFFFFFFFF, hash; + u64 addr; + + if (!is_init_done(tgec->cfg)) + return -EINVAL; + + addr = ENET_ADDR_TO_UINT64(*eth_addr); + + if (!(addr & GROUP_ADDRESS)) { + /* Unicast addresses not supported in hash */ + pr_err("Unicast Address\n"); + return -EINVAL; + } + /* CRC calculation */ + crc = crc32_le(crc, (u8 *)eth_addr, ETH_ALEN); + crc = bitrev32(crc); + /* Take 9 MSB bits */ + hash = (crc >> TGEC_HASH_MCAST_SHIFT) & TGEC_HASH_ADR_MSK; + + /* Create element to be added to the driver hash table */ + hash_entry = kmalloc(sizeof(*hash_entry), GFP_KERNEL); + if (!hash_entry) + return -ENOMEM; + hash_entry->addr = addr; + INIT_LIST_HEAD(&hash_entry->node); + + list_add_tail(&hash_entry->node, + &tgec->multicast_addr_hash->lsts[hash]); + iowrite32be((hash | TGEC_HASH_MCAST_EN), ®s->hashtable_ctrl); + + return 0; +} + +int tgec_del_hash_mac_address(struct fman_mac *tgec, enet_addr_t *eth_addr) +{ + struct tgec_regs __iomem *regs = tgec->regs; + struct eth_hash_entry *hash_entry = NULL; + struct list_head *pos; + u32 crc = 0xFFFFFFFF, hash; + u64 addr; + + if (!is_init_done(tgec->cfg)) + return -EINVAL; + + addr = ((*(u64 *)eth_addr) >> 16); + + /* CRC calculation */ + crc = crc32_le(crc, (u8 *)eth_addr, ETH_ALEN); + crc = bitrev32(crc); + /* Take 9 MSB bits */ + hash = (crc >> TGEC_HASH_MCAST_SHIFT) & TGEC_HASH_ADR_MSK; + + list_for_each(pos, &tgec->multicast_addr_hash->lsts[hash]) { + hash_entry = ETH_HASH_ENTRY_OBJ(pos); + if (hash_entry->addr == addr) { + list_del_init(&hash_entry->node); + kfree(hash_entry); + break; + } + } + if (list_empty(&tgec->multicast_addr_hash->lsts[hash])) + iowrite32be((hash & ~TGEC_HASH_MCAST_EN), + ®s->hashtable_ctrl); + + return 0; +} + +int tgec_get_version(struct fman_mac *tgec, u32 *mac_version) +{ + struct tgec_regs __iomem *regs = tgec->regs; + + if (!is_init_done(tgec->cfg)) + return -EINVAL; + + *mac_version = ioread32be(®s->tgec_id); + + return 0; +} + +int tgec_set_exception(struct fman_mac *tgec, + enum fman_mac_exceptions exception, bool enable) +{ + struct tgec_regs __iomem *regs = tgec->regs; + u32 bit_mask = 0; + + if (!is_init_done(tgec->cfg)) + return -EINVAL; + + bit_mask = get_exception_flag(exception); + if (bit_mask) { + if (enable) + tgec->exceptions |= bit_mask; + else + tgec->exceptions &= ~bit_mask; + } else { + pr_err("Undefined exception\n"); + return -EINVAL; + } + if (enable) + iowrite32be(ioread32be(®s->imask) | bit_mask, ®s->imask); + else + iowrite32be(ioread32be(®s->imask) & ~bit_mask, ®s->imask); + + return 0; +} + +int tgec_init(struct fman_mac *tgec) +{ + struct tgec_cfg *cfg; + enet_addr_t eth_addr; + int err; + + if (is_init_done(tgec->cfg)) + return -EINVAL; + + if (DEFAULT_RESET_ON_INIT && + (fman_reset_mac(tgec->fm, tgec->mac_id) != 0)) { + pr_err("Can't reset MAC!\n"); + return -EINVAL; + } + + err = check_init_parameters(tgec); + if (err) + return err; + + cfg = tgec->cfg; + + MAKE_ENET_ADDR_FROM_UINT64(tgec->addr, eth_addr); + set_mac_address(tgec->regs, (u8 *)eth_addr); + + /* interrupts */ + /* FM_10G_REM_N_LCL_FLT_EX_10GMAC_ERRATA_SW005 Errata workaround */ + if (tgec->fm_rev_info.major <= 2) + tgec->exceptions &= ~(TGEC_IMASK_REM_FAULT | + TGEC_IMASK_LOC_FAULT); + + err = init(tgec->regs, cfg, tgec->exceptions); + if (err) { + free_init_resources(tgec); + pr_err("TGEC version doesn't support this i/f mode\n"); + return err; + } + + /* Max Frame Length */ + err = fman_set_mac_max_frame(tgec->fm, tgec->mac_id, + cfg->max_frame_length); + if (err) { + pr_err("Setting max frame length FAILED\n"); + free_init_resources(tgec); + return -EINVAL; + } + + /* FM_TX_FIFO_CORRUPTION_ERRATA_10GMAC_A007 Errata workaround */ + if (tgec->fm_rev_info.major == 2) { + struct tgec_regs __iomem *regs = tgec->regs; + u32 tmp; + + /* restore the default tx ipg Length */ + tmp = (ioread32be(®s->tx_ipg_len) & + ~TGEC_TX_IPG_LENGTH_MASK) | 12; + + iowrite32be(tmp, ®s->tx_ipg_len); + } + + tgec->multicast_addr_hash = alloc_hash_table(TGEC_HASH_TABLE_SIZE); + if (!tgec->multicast_addr_hash) { + free_init_resources(tgec); + pr_err("allocation hash table is FAILED\n"); + return -ENOMEM; + } + + tgec->unicast_addr_hash = alloc_hash_table(TGEC_HASH_TABLE_SIZE); + if (!tgec->unicast_addr_hash) { + free_init_resources(tgec); + pr_err("allocation hash table is FAILED\n"); + return -ENOMEM; + } + + fman_register_intr(tgec->fm, FMAN_MOD_MAC, tgec->mac_id, + FMAN_INTR_TYPE_ERR, tgec_err_exception, tgec); + + kfree(cfg); + tgec->cfg = NULL; + + return 0; +} + +int tgec_free(struct fman_mac *tgec) +{ + free_init_resources(tgec); + + if (tgec->cfg) + tgec->cfg = NULL; + + kfree(tgec->cfg); + kfree(tgec); + + return 0; +} + +struct fman_mac *tgec_config(struct fman_mac_params *params) +{ + struct fman_mac *tgec; + struct tgec_cfg *cfg; + void __iomem *base_addr; + + base_addr = params->base_addr; + /* allocate memory for the UCC GETH data structure. */ + tgec = kzalloc(sizeof(*tgec), GFP_KERNEL); + if (!tgec) + return NULL; + + /* allocate memory for the 10G MAC driver parameters data structure. */ + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + tgec_free(tgec); + return NULL; + } + + /* Plant parameter structure pointer */ + tgec->cfg = cfg; + + set_dflts(cfg); + + tgec->regs = base_addr; + tgec->addr = ENET_ADDR_TO_UINT64(params->addr); + tgec->max_speed = params->max_speed; + tgec->mac_id = params->mac_id; + tgec->exceptions = (TGEC_IMASK_MDIO_SCAN_EVENT | + TGEC_IMASK_REM_FAULT | + TGEC_IMASK_LOC_FAULT | + TGEC_IMASK_TX_ECC_ER | + TGEC_IMASK_TX_FIFO_UNFL | + TGEC_IMASK_TX_FIFO_OVFL | + TGEC_IMASK_TX_ER | + TGEC_IMASK_RX_FIFO_OVFL | + TGEC_IMASK_RX_ECC_ER | + TGEC_IMASK_RX_JAB_FRM | + TGEC_IMASK_RX_OVRSZ_FRM | + TGEC_IMASK_RX_RUNT_FRM | + TGEC_IMASK_RX_FRAG_FRM | + TGEC_IMASK_RX_CRC_ER | + TGEC_IMASK_RX_ALIGN_ER); + tgec->exception_cb = params->exception_cb; + tgec->event_cb = params->event_cb; + tgec->dev_id = params->dev_id; + tgec->fm = params->fm; + + /* Save FMan revision */ + fman_get_revision(tgec->fm, &tgec->fm_rev_info); + + return tgec; +} diff --git a/drivers/net/ethernet/freescale/fman/fman_tgec.h b/drivers/net/ethernet/freescale/fman/fman_tgec.h new file mode 100644 index 0000000..514bba9 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_tgec.h @@ -0,0 +1,55 @@ +/* + * Copyright 2008-2015 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __TGEC_H +#define __TGEC_H + +#include "fman_mac.h" + +struct fman_mac *tgec_config(struct fman_mac_params *params); +int tgec_set_promiscuous(struct fman_mac *tgec, bool new_val); +int tgec_modify_mac_address(struct fman_mac *tgec, enet_addr_t *enet_addr); +int tgec_cfg_max_frame_len(struct fman_mac *tgec, u16 new_val); +int tgec_enable(struct fman_mac *tgec, enum comm_mode mode); +int tgec_disable(struct fman_mac *tgec, enum comm_mode mode); +int tgec_init(struct fman_mac *tgec); +int tgec_free(struct fman_mac *tgec); +int tgec_accept_rx_pause_frames(struct fman_mac *tgec, bool en); +int tgec_set_tx_pause_frames(struct fman_mac *tgec, u8 priority, + u16 pause_time, u16 thresh_time); +int tgec_set_exception(struct fman_mac *tgec, + enum fman_mac_exceptions exception, bool enable); +int tgec_add_hash_mac_address(struct fman_mac *tgec, enet_addr_t *eth_addr); +int tgec_del_hash_mac_address(struct fman_mac *tgec, enet_addr_t *eth_addr); +int tgec_get_version(struct fman_mac *tgec, u32 *mac_version); + +#endif /* __TGEC_H */ |