📄 pm3393.c
字号:
/***************************************************************************** * * * File: pm3393.c * * $Revision: 1.16 $ * * $Date: 2005/05/14 00:59:32 $ * * Description: * * PMC/SIERRA (pm3393) MAC-PHY functionality. * * part of the Chelsio 10Gb Ethernet Driver. * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License, version 2, as * * published by the Free Software Foundation. * * * * You should have received a copy of the GNU General Public License along * * with this program; if not, write to the Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * * * http://www.chelsio.com * * * * Copyright (c) 2003 - 2005 Chelsio Communications, Inc. * * All rights reserved. * * * * Maintainers: maintainers@chelsio.com * * * * Authors: Dimitrios Michailidis <dm@chelsio.com> * * Tina Yang <tainay@chelsio.com> * * Felix Marti <felix@chelsio.com> * * Scott Bardone <sbardone@chelsio.com> * * Kurt Ottaway <kottaway@chelsio.com> * * Frank DiMambro <frank@chelsio.com> * * * * History: * * * ****************************************************************************/#include "common.h"#include "regs.h"#include "gmac.h"#include "elmer0.h"#include "suni1x10gexp_regs.h"/* 802.3ae 10Gb/s MDIO Manageable Device(MMD) */enum { MMD_RESERVED, MMD_PMAPMD, MMD_WIS, MMD_PCS, MMD_PHY_XGXS, /* XGMII Extender Sublayer */ MMD_DTE_XGXS,};enum { PHY_XGXS_CTRL_1, PHY_XGXS_STATUS_1};#define OFFSET(REG_ADDR) (REG_ADDR << 2)/* Max frame size PM3393 can handle. Includes Ethernet header and CRC. */#define MAX_FRAME_SIZE 9600#define IPG 12#define TXXG_CONF1_VAL ((IPG << SUNI1x10GEXP_BITOFF_TXXG_IPGT) | \ SUNI1x10GEXP_BITMSK_TXXG_32BIT_ALIGN | SUNI1x10GEXP_BITMSK_TXXG_CRCEN | \ SUNI1x10GEXP_BITMSK_TXXG_PADEN)#define RXXG_CONF1_VAL (SUNI1x10GEXP_BITMSK_RXXG_PUREP | 0x14 | \ SUNI1x10GEXP_BITMSK_RXXG_FLCHK | SUNI1x10GEXP_BITMSK_RXXG_CRC_STRIP)/* Update statistics every 15 minutes */#define STATS_TICK_SECS (15 * 60)enum { /* RMON registers */ RxOctetsReceivedOK = SUNI1x10GEXP_REG_MSTAT_COUNTER_1_LOW, RxUnicastFramesReceivedOK = SUNI1x10GEXP_REG_MSTAT_COUNTER_4_LOW, RxMulticastFramesReceivedOK = SUNI1x10GEXP_REG_MSTAT_COUNTER_5_LOW, RxBroadcastFramesReceivedOK = SUNI1x10GEXP_REG_MSTAT_COUNTER_6_LOW, RxPAUSEMACCtrlFramesReceived = SUNI1x10GEXP_REG_MSTAT_COUNTER_8_LOW, RxFrameCheckSequenceErrors = SUNI1x10GEXP_REG_MSTAT_COUNTER_10_LOW, RxFramesLostDueToInternalMACErrors = SUNI1x10GEXP_REG_MSTAT_COUNTER_11_LOW, RxSymbolErrors = SUNI1x10GEXP_REG_MSTAT_COUNTER_12_LOW, RxInRangeLengthErrors = SUNI1x10GEXP_REG_MSTAT_COUNTER_13_LOW, RxFramesTooLongErrors = SUNI1x10GEXP_REG_MSTAT_COUNTER_15_LOW, RxJabbers = SUNI1x10GEXP_REG_MSTAT_COUNTER_16_LOW, RxFragments = SUNI1x10GEXP_REG_MSTAT_COUNTER_17_LOW, RxUndersizedFrames = SUNI1x10GEXP_REG_MSTAT_COUNTER_18_LOW, TxOctetsTransmittedOK = SUNI1x10GEXP_REG_MSTAT_COUNTER_33_LOW, TxFramesLostDueToInternalMACTransmissionError = SUNI1x10GEXP_REG_MSTAT_COUNTER_35_LOW, TxTransmitSystemError = SUNI1x10GEXP_REG_MSTAT_COUNTER_36_LOW, TxUnicastFramesTransmittedOK = SUNI1x10GEXP_REG_MSTAT_COUNTER_38_LOW, TxMulticastFramesTransmittedOK = SUNI1x10GEXP_REG_MSTAT_COUNTER_40_LOW, TxBroadcastFramesTransmittedOK = SUNI1x10GEXP_REG_MSTAT_COUNTER_42_LOW, TxPAUSEMACCtrlFramesTransmitted = SUNI1x10GEXP_REG_MSTAT_COUNTER_43_LOW};struct _cmac_instance { u8 enabled; u8 fc; u8 mac_addr[6];};static int pmread(struct cmac *cmac, u32 reg, u32 * data32){ t1_tpi_read(cmac->adapter, OFFSET(reg), data32); return 0;}static int pmwrite(struct cmac *cmac, u32 reg, u32 data32){ t1_tpi_write(cmac->adapter, OFFSET(reg), data32); return 0;}/* Port reset. */static int pm3393_reset(struct cmac *cmac){ return 0;}/* * Enable interrupts for the PM3393 1. Enable PM3393 BLOCK interrupts. 2. Enable PM3393 Master Interrupt bit(INTE) 3. Enable ELMER's PM3393 bit. 4. Enable Terminator external interrupt.*/static int pm3393_interrupt_enable(struct cmac *cmac){ u32 pl_intr; /* PM3393 - Enabling all hardware block interrupts. */ pmwrite(cmac, SUNI1x10GEXP_REG_SERDES_3125_INTERRUPT_ENABLE, 0xffff); pmwrite(cmac, SUNI1x10GEXP_REG_XRF_INTERRUPT_ENABLE, 0xffff); pmwrite(cmac, SUNI1x10GEXP_REG_XRF_DIAG_INTERRUPT_ENABLE, 0xffff); pmwrite(cmac, SUNI1x10GEXP_REG_RXOAM_INTERRUPT_ENABLE, 0xffff); /* Don't interrupt on statistics overflow, we are polling */ pmwrite(cmac, SUNI1x10GEXP_REG_MSTAT_INTERRUPT_MASK_0, 0); pmwrite(cmac, SUNI1x10GEXP_REG_MSTAT_INTERRUPT_MASK_1, 0); pmwrite(cmac, SUNI1x10GEXP_REG_MSTAT_INTERRUPT_MASK_2, 0); pmwrite(cmac, SUNI1x10GEXP_REG_MSTAT_INTERRUPT_MASK_3, 0); pmwrite(cmac, SUNI1x10GEXP_REG_IFLX_FIFO_OVERFLOW_ENABLE, 0xffff); pmwrite(cmac, SUNI1x10GEXP_REG_PL4ODP_INTERRUPT_MASK, 0xffff); pmwrite(cmac, SUNI1x10GEXP_REG_XTEF_INTERRUPT_ENABLE, 0xffff); pmwrite(cmac, SUNI1x10GEXP_REG_TXOAM_INTERRUPT_ENABLE, 0xffff); pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_CONFIG_3, 0xffff); pmwrite(cmac, SUNI1x10GEXP_REG_PL4IO_LOCK_DETECT_MASK, 0xffff); pmwrite(cmac, SUNI1x10GEXP_REG_TXXG_CONFIG_3, 0xffff); pmwrite(cmac, SUNI1x10GEXP_REG_PL4IDU_INTERRUPT_MASK, 0xffff); pmwrite(cmac, SUNI1x10GEXP_REG_EFLX_FIFO_OVERFLOW_ERROR_ENABLE, 0xffff); /* PM3393 - Global interrupt enable */ /* TBD XXX Disable for now until we figure out why error interrupts keep asserting. */ pmwrite(cmac, SUNI1x10GEXP_REG_GLOBAL_INTERRUPT_ENABLE, 0 /*SUNI1x10GEXP_BITMSK_TOP_INTE */ ); /* TERMINATOR - PL_INTERUPTS_EXT */ pl_intr = readl(cmac->adapter->regs + A_PL_ENABLE); pl_intr |= F_PL_INTR_EXT; writel(pl_intr, cmac->adapter->regs + A_PL_ENABLE); return 0;}static int pm3393_interrupt_disable(struct cmac *cmac){ u32 elmer; /* PM3393 - Enabling HW interrupt blocks. */ pmwrite(cmac, SUNI1x10GEXP_REG_SERDES_3125_INTERRUPT_ENABLE, 0); pmwrite(cmac, SUNI1x10GEXP_REG_XRF_INTERRUPT_ENABLE, 0); pmwrite(cmac, SUNI1x10GEXP_REG_XRF_DIAG_INTERRUPT_ENABLE, 0); pmwrite(cmac, SUNI1x10GEXP_REG_RXOAM_INTERRUPT_ENABLE, 0); pmwrite(cmac, SUNI1x10GEXP_REG_MSTAT_INTERRUPT_MASK_0, 0); pmwrite(cmac, SUNI1x10GEXP_REG_MSTAT_INTERRUPT_MASK_1, 0); pmwrite(cmac, SUNI1x10GEXP_REG_MSTAT_INTERRUPT_MASK_2, 0); pmwrite(cmac, SUNI1x10GEXP_REG_MSTAT_INTERRUPT_MASK_3, 0); pmwrite(cmac, SUNI1x10GEXP_REG_IFLX_FIFO_OVERFLOW_ENABLE, 0); pmwrite(cmac, SUNI1x10GEXP_REG_PL4ODP_INTERRUPT_MASK, 0); pmwrite(cmac, SUNI1x10GEXP_REG_XTEF_INTERRUPT_ENABLE, 0); pmwrite(cmac, SUNI1x10GEXP_REG_TXOAM_INTERRUPT_ENABLE, 0); pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_CONFIG_3, 0); pmwrite(cmac, SUNI1x10GEXP_REG_PL4IO_LOCK_DETECT_MASK, 0); pmwrite(cmac, SUNI1x10GEXP_REG_TXXG_CONFIG_3, 0); pmwrite(cmac, SUNI1x10GEXP_REG_PL4IDU_INTERRUPT_MASK, 0); pmwrite(cmac, SUNI1x10GEXP_REG_EFLX_FIFO_OVERFLOW_ERROR_ENABLE, 0); /* PM3393 - Global interrupt enable */ pmwrite(cmac, SUNI1x10GEXP_REG_GLOBAL_INTERRUPT_ENABLE, 0); /* ELMER - External chip interrupts. */ t1_tpi_read(cmac->adapter, A_ELMER0_INT_ENABLE, &elmer); elmer &= ~ELMER0_GP_BIT1; t1_tpi_write(cmac->adapter, A_ELMER0_INT_ENABLE, elmer); /* TERMINATOR - PL_INTERUPTS_EXT */ /* DO NOT DISABLE TERMINATOR's EXTERNAL INTERRUPTS. ANOTHER CHIP * COULD WANT THEM ENABLED. We disable PM3393 at the ELMER level. */ return 0;}static int pm3393_interrupt_clear(struct cmac *cmac){ u32 elmer; u32 pl_intr; u32 val32; /* PM3393 - Clearing HW interrupt blocks. Note, this assumes * bit WCIMODE=0 for a clear-on-read. */ pmread(cmac, SUNI1x10GEXP_REG_SERDES_3125_INTERRUPT_STATUS, &val32); pmread(cmac, SUNI1x10GEXP_REG_XRF_INTERRUPT_STATUS, &val32); pmread(cmac, SUNI1x10GEXP_REG_XRF_DIAG_INTERRUPT_STATUS, &val32); pmread(cmac, SUNI1x10GEXP_REG_RXOAM_INTERRUPT_STATUS, &val32); pmread(cmac, SUNI1x10GEXP_REG_PL4ODP_INTERRUPT, &val32); pmread(cmac, SUNI1x10GEXP_REG_XTEF_INTERRUPT_STATUS, &val32); pmread(cmac, SUNI1x10GEXP_REG_IFLX_FIFO_OVERFLOW_INTERRUPT, &val32); pmread(cmac, SUNI1x10GEXP_REG_TXOAM_INTERRUPT_STATUS, &val32); pmread(cmac, SUNI1x10GEXP_REG_RXXG_INTERRUPT, &val32); pmread(cmac, SUNI1x10GEXP_REG_TXXG_INTERRUPT, &val32); pmread(cmac, SUNI1x10GEXP_REG_PL4IDU_INTERRUPT, &val32); pmread(cmac, SUNI1x10GEXP_REG_EFLX_FIFO_OVERFLOW_ERROR_INDICATION, &val32); pmread(cmac, SUNI1x10GEXP_REG_PL4IO_LOCK_DETECT_STATUS, &val32); pmread(cmac, SUNI1x10GEXP_REG_PL4IO_LOCK_DETECT_CHANGE, &val32); /* PM3393 - Global interrupt status */ pmread(cmac, SUNI1x10GEXP_REG_MASTER_INTERRUPT_STATUS, &val32); /* ELMER - External chip interrupts. */ t1_tpi_read(cmac->adapter, A_ELMER0_INT_CAUSE, &elmer); elmer |= ELMER0_GP_BIT1; t1_tpi_write(cmac->adapter, A_ELMER0_INT_CAUSE, elmer); /* TERMINATOR - PL_INTERUPTS_EXT */ pl_intr = readl(cmac->adapter->regs + A_PL_CAUSE); pl_intr |= F_PL_INTR_EXT; writel(pl_intr, cmac->adapter->regs + A_PL_CAUSE); return 0;}/* Interrupt handler */static int pm3393_interrupt_handler(struct cmac *cmac){ u32 master_intr_status;/* 1. Read master interrupt register. 2. Read BLOCK's interrupt status registers. 3. Handle BLOCK interrupts.*/ /* Read the master interrupt status register. */ pmread(cmac, SUNI1x10GEXP_REG_MASTER_INTERRUPT_STATUS, &master_intr_status); /* TBD XXX Lets just clear everything for now */ pm3393_interrupt_clear(cmac); return 0;}static int pm3393_enable(struct cmac *cmac, int which){ if (which & MAC_DIRECTION_RX) pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_CONFIG_1, (RXXG_CONF1_VAL | SUNI1x10GEXP_BITMSK_RXXG_RXEN)); if (which & MAC_DIRECTION_TX) { u32 val = TXXG_CONF1_VAL | SUNI1x10GEXP_BITMSK_TXXG_TXEN0; if (cmac->instance->fc & PAUSE_RX) val |= SUNI1x10GEXP_BITMSK_TXXG_FCRX; if (cmac->instance->fc & PAUSE_TX) val |= SUNI1x10GEXP_BITMSK_TXXG_FCTX; pmwrite(cmac, SUNI1x10GEXP_REG_TXXG_CONFIG_1, val); } cmac->instance->enabled |= which; return 0;}static int pm3393_enable_port(struct cmac *cmac, int which){ /* Clear port statistics */ pmwrite(cmac, SUNI1x10GEXP_REG_MSTAT_CONTROL, SUNI1x10GEXP_BITMSK_MSTAT_CLEAR); udelay(2); memset(&cmac->stats, 0, sizeof(struct cmac_statistics)); pm3393_enable(cmac, which); /* * XXX This should be done by the PHY and preferrably not at all. * The PHY doesn't give us link status indication on its own so have * the link management code query it instead. */ { extern void link_changed(adapter_t *adapter, int port_id); link_changed(cmac->adapter, 0); } return 0;}static int pm3393_disable(struct cmac *cmac, int which){ if (which & MAC_DIRECTION_RX) pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_CONFIG_1, RXXG_CONF1_VAL); if (which & MAC_DIRECTION_TX) pmwrite(cmac, SUNI1x10GEXP_REG_TXXG_CONFIG_1, TXXG_CONF1_VAL); /* * The disable is graceful. Give the PM3393 time. Can't wait very * long here, we may be holding locks. */ udelay(20); cmac->instance->enabled &= ~which; return 0;}static int pm3393_loopback_enable(struct cmac *cmac){ return 0;}static int pm3393_loopback_disable(struct cmac *cmac){ return 0;}static int pm3393_set_mtu(struct cmac *cmac, int mtu){ int enabled = cmac->instance->enabled; /* MAX_FRAME_SIZE includes header + FCS, mtu doesn't */ mtu += 14 + 4; if (mtu > MAX_FRAME_SIZE) return -EINVAL; /* Disable Rx/Tx MAC before configuring it. */ if (enabled) pm3393_disable(cmac, MAC_DIRECTION_RX | MAC_DIRECTION_TX); pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_MAX_FRAME_LENGTH, mtu); pmwrite(cmac, SUNI1x10GEXP_REG_TXXG_MAX_FRAME_SIZE, mtu); if (enabled) pm3393_enable(cmac, enabled); return 0;}static u32 calc_crc(u8 *b, int len){ int i; u32 crc = (u32)~0; /* calculate crc one bit at a time */ while (len--) { crc ^= *b++; for (i = 0; i < 8; i++) { if (crc & 0x1) crc = (crc >> 1) ^ 0xedb88320; else crc = (crc >> 1); } } /* reverse bits */ crc = ((crc >> 4) & 0x0f0f0f0f) | ((crc << 4) & 0xf0f0f0f0); crc = ((crc >> 2) & 0x33333333) | ((crc << 2) & 0xcccccccc); crc = ((crc >> 1) & 0x55555555) | ((crc << 1) & 0xaaaaaaaa); /* swap bytes */ crc = (crc >> 16) | (crc << 16); crc = (crc >> 8 & 0x00ff00ff) | (crc << 8 & 0xff00ff00); return crc;}static int pm3393_set_rx_mode(struct cmac *cmac, struct t1_rx_mode *rm){ int enabled = cmac->instance->enabled & MAC_DIRECTION_RX; u32 rx_mode; /* Disable MAC RX before reconfiguring it */ if (enabled) pm3393_disable(cmac, MAC_DIRECTION_RX); pmread(cmac, SUNI1x10GEXP_REG_RXXG_ADDRESS_FILTER_CONTROL_2, &rx_mode); rx_mode &= ~(SUNI1x10GEXP_BITMSK_RXXG_PMODE | SUNI1x10GEXP_BITMSK_RXXG_MHASH_EN); pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_ADDRESS_FILTER_CONTROL_2, (u16)rx_mode); if (t1_rx_mode_promisc(rm)) { /* Promiscuous mode. */ rx_mode |= SUNI1x10GEXP_BITMSK_RXXG_PMODE; } if (t1_rx_mode_allmulti(rm)) { /* Accept all multicast. */ pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_MULTICAST_HASH_LOW, 0xffff);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -