This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / drivers / net / chelsio / mac.c
diff --git a/drivers/net/chelsio/mac.c b/drivers/net/chelsio/mac.c
new file mode 100644 (file)
index 0000000..6af39dc
--- /dev/null
@@ -0,0 +1,368 @@
+/* $Date: 2005/10/22 00:42:59 $ $RCSfile: mac.c,v $ $Revision: 1.32 $ */
+#include "gmac.h"
+#include "regs.h"
+#include "fpga_defs.h"
+
+#define MAC_CSR_INTERFACE_GMII      0x0
+#define MAC_CSR_INTERFACE_TBI       0x1
+#define MAC_CSR_INTERFACE_MII       0x2
+#define MAC_CSR_INTERFACE_RMII      0x3
+
+/* Chelsio's MAC statistics. */
+struct mac_statistics {
+
+       /* Transmit */
+       u32 TxFramesTransmittedOK;
+       u32 TxReserved1;
+       u32 TxReserved2;
+       u32 TxOctetsTransmittedOK;
+       u32 TxFramesWithDeferredXmissions;
+       u32 TxLateCollisions;
+       u32 TxFramesAbortedDueToXSCollisions;
+       u32 TxFramesLostDueToIntMACXmitError;
+       u32 TxReserved3;
+       u32 TxMulticastFrameXmittedOK;
+       u32 TxBroadcastFramesXmittedOK;
+       u32 TxFramesWithExcessiveDeferral;
+       u32 TxPAUSEMACCtrlFramesTransmitted;
+
+       /* Receive */
+       u32 RxFramesReceivedOK;
+       u32 RxFrameCheckSequenceErrors;
+       u32 RxAlignmentErrors;
+       u32 RxOctetsReceivedOK;
+       u32 RxFramesLostDueToIntMACRcvError;
+       u32 RxMulticastFramesReceivedOK;
+       u32 RxBroadcastFramesReceivedOK;
+       u32 RxInRangeLengthErrors;
+       u32 RxTxOutOfRangeLengthField;
+       u32 RxFrameTooLongErrors;
+       u32 RxPAUSEMACCtrlFramesReceived;
+};
+
+static int static_aPorts[] = {
+       FPGA_GMAC_INTERRUPT_PORT0,
+       FPGA_GMAC_INTERRUPT_PORT1,
+       FPGA_GMAC_INTERRUPT_PORT2,
+       FPGA_GMAC_INTERRUPT_PORT3
+};
+
+struct _cmac_instance {
+       u32 index;
+};
+
+static int mac_intr_enable(struct cmac *mac)
+{
+       u32 mac_intr;
+
+       if (t1_is_asic(mac->adapter)) {
+               /* ASIC */
+
+               /* We don't use the on chip MAC for ASIC products. */
+       } else {
+               /* FPGA */
+
+               /* Set parent gmac interrupt. */
+               mac_intr = readl(mac->adapter->regs + A_PL_ENABLE);
+               mac_intr |= FPGA_PCIX_INTERRUPT_GMAC;
+               writel(mac_intr, mac->adapter->regs + A_PL_ENABLE);
+
+               mac_intr = readl(mac->adapter->regs + FPGA_GMAC_ADDR_INTERRUPT_ENABLE);
+               mac_intr |= static_aPorts[mac->instance->index];
+               writel(mac_intr,
+                      mac->adapter->regs + FPGA_GMAC_ADDR_INTERRUPT_ENABLE);
+       }
+
+       return 0;
+}
+
+static int mac_intr_disable(struct cmac *mac)
+{
+       u32 mac_intr;
+
+       if (t1_is_asic(mac->adapter)) {
+               /* ASIC */
+
+               /* We don't use the on chip MAC for ASIC products. */
+       } else {
+               /* FPGA */
+
+               /* Set parent gmac interrupt. */
+               mac_intr = readl(mac->adapter->regs + A_PL_ENABLE);
+               mac_intr &= ~FPGA_PCIX_INTERRUPT_GMAC;
+               writel(mac_intr, mac->adapter->regs + A_PL_ENABLE);
+
+               mac_intr = readl(mac->adapter->regs + FPGA_GMAC_ADDR_INTERRUPT_ENABLE);
+               mac_intr &= ~(static_aPorts[mac->instance->index]);
+               writel(mac_intr,
+                      mac->adapter->regs + FPGA_GMAC_ADDR_INTERRUPT_ENABLE);
+       }
+
+       return 0;
+}
+
+static int mac_intr_clear(struct cmac *mac)
+{
+       u32 mac_intr;
+
+       if (t1_is_asic(mac->adapter)) {
+               /* ASIC */
+
+               /* We don't use the on chip MAC for ASIC products. */
+       } else {
+               /* FPGA */
+
+               /* Set parent gmac interrupt. */
+               writel(FPGA_PCIX_INTERRUPT_GMAC,
+                      mac->adapter->regs +  A_PL_CAUSE);
+               mac_intr = readl(mac->adapter->regs + FPGA_GMAC_ADDR_INTERRUPT_CAUSE);
+               mac_intr |= (static_aPorts[mac->instance->index]);
+               writel(mac_intr,
+                      mac->adapter->regs + FPGA_GMAC_ADDR_INTERRUPT_CAUSE);
+       }
+
+       return 0;
+}
+
+static int mac_get_address(struct cmac *mac, u8 addr[6])
+{
+       u32 data32_lo, data32_hi;
+
+       data32_lo = readl(mac->adapter->regs
+                         + MAC_REG_IDLO(mac->instance->index));
+       data32_hi = readl(mac->adapter->regs
+                         + MAC_REG_IDHI(mac->instance->index));
+
+       addr[0] = (u8) ((data32_hi >> 8) & 0xFF);
+       addr[1] = (u8) ((data32_hi) & 0xFF);
+       addr[2] = (u8) ((data32_lo >> 24) & 0xFF);
+       addr[3] = (u8) ((data32_lo >> 16) & 0xFF);
+       addr[4] = (u8) ((data32_lo >> 8) & 0xFF);
+       addr[5] = (u8) ((data32_lo) & 0xFF);
+       return 0;
+}
+
+static int mac_reset(struct cmac *mac)
+{
+       u32 data32;
+       int mac_in_reset, time_out = 100;
+       int idx = mac->instance->index;
+
+       data32 = readl(mac->adapter->regs + MAC_REG_CSR(idx));
+       writel(data32 | F_MAC_RESET,
+              mac->adapter->regs + MAC_REG_CSR(idx));
+
+       do {
+               data32 = readl(mac->adapter->regs + MAC_REG_CSR(idx));
+
+               mac_in_reset = data32 & F_MAC_RESET;
+               if (mac_in_reset)
+                       udelay(1);
+       } while (mac_in_reset && --time_out);
+
+       if (mac_in_reset) {
+               CH_ERR("%s: MAC %d reset timed out\n",
+                      mac->adapter->name, idx);
+               return 2;
+       }
+
+       return 0;
+}
+
+static int mac_set_rx_mode(struct cmac *mac, struct t1_rx_mode *rm)
+{
+       u32 val;
+
+       val = readl(mac->adapter->regs
+                           + MAC_REG_CSR(mac->instance->index));
+       val &= ~(F_MAC_PROMISC | F_MAC_MC_ENABLE);
+       val |= V_MAC_PROMISC(t1_rx_mode_promisc(rm) != 0);
+       val |= V_MAC_MC_ENABLE(t1_rx_mode_allmulti(rm) != 0);
+       writel(val,
+              mac->adapter->regs + MAC_REG_CSR(mac->instance->index));
+
+       return 0;
+}
+
+static int mac_set_speed_duplex_fc(struct cmac *mac, int speed, int duplex,
+                                  int fc)
+{
+       u32 data32;
+
+       data32 = readl(mac->adapter->regs
+                              + MAC_REG_CSR(mac->instance->index));
+       data32 &= ~(F_MAC_HALF_DUPLEX | V_MAC_SPEED(M_MAC_SPEED) |
+               V_INTERFACE(M_INTERFACE) | F_MAC_TX_PAUSE_ENABLE |
+               F_MAC_RX_PAUSE_ENABLE);
+
+       switch (speed) {
+       case SPEED_10:
+       case SPEED_100:
+               data32 |= V_INTERFACE(MAC_CSR_INTERFACE_MII);
+               data32 |= V_MAC_SPEED(speed == SPEED_10 ? 0 : 1);
+               break;
+       case SPEED_1000:
+               data32 |= V_INTERFACE(MAC_CSR_INTERFACE_GMII);
+               data32 |= V_MAC_SPEED(2);
+               break;
+       }
+
+       if (duplex >= 0)
+               data32 |= V_MAC_HALF_DUPLEX(duplex == DUPLEX_HALF);
+
+       if (fc >= 0) {
+               data32 |= V_MAC_RX_PAUSE_ENABLE((fc & PAUSE_RX) != 0);
+               data32 |= V_MAC_TX_PAUSE_ENABLE((fc & PAUSE_TX) != 0);
+       }
+
+       writel(data32,
+              mac->adapter->regs + MAC_REG_CSR(mac->instance->index));
+       return 0;
+}
+
+static int mac_enable(struct cmac *mac, int which)
+{
+       u32 val;
+
+       val = readl(mac->adapter->regs
+                           + MAC_REG_CSR(mac->instance->index));
+       if (which & MAC_DIRECTION_RX)
+               val |= F_MAC_RX_ENABLE;
+       if (which & MAC_DIRECTION_TX)
+               val |= F_MAC_TX_ENABLE;
+       writel(val,
+              mac->adapter->regs + MAC_REG_CSR(mac->instance->index));
+       return 0;
+}
+
+static int mac_disable(struct cmac *mac, int which)
+{
+       u32 val;
+
+       val = readl(mac->adapter->regs
+                           + MAC_REG_CSR(mac->instance->index));
+       if (which & MAC_DIRECTION_RX)
+               val &= ~F_MAC_RX_ENABLE;
+       if (which & MAC_DIRECTION_TX)
+               val &= ~F_MAC_TX_ENABLE;
+       writel(val,
+              mac->adapter->regs + MAC_REG_CSR(mac->instance->index));
+       return 0;
+}
+
+#if 0
+static int mac_set_ifs(struct cmac *mac, u32 mode)
+{
+       t1_write_reg_4(mac->adapter,
+                      MAC_REG_IFS(mac->instance->index),
+                      mode);
+       return 0;
+}
+
+static int mac_enable_isl(struct cmac *mac)
+{
+       u32 data32 = readl(mac->adapter->regs
+                                  + MAC_REG_CSR(mac->instance->index));
+       data32 |= F_MAC_RX_ENABLE | F_MAC_TX_ENABLE;
+       t1_write_reg_4(mac->adapter,
+                      MAC_REG_CSR(mac->instance->index),
+                      data32);
+       return 0;
+}
+#endif
+
+static int mac_set_mtu(struct cmac *mac, int mtu)
+{
+       if (mtu > 9600)
+               return -EINVAL;
+       writel(mtu + ETH_HLEN + VLAN_HLEN,
+              mac->adapter->regs + MAC_REG_LARGEFRAMELENGTH(mac->instance->index));
+
+       return 0;
+}
+
+static const struct cmac_statistics *mac_update_statistics(struct cmac *mac,
+                                                          int flag)
+{
+       struct mac_statistics st;
+       u32 *p = (u32 *) & st, i;
+
+       writel(0,
+              mac->adapter->regs + MAC_REG_RMCNT(mac->instance->index));
+
+       for (i = 0; i < sizeof(st) / sizeof(u32); i++)
+               *p++ = readl(mac->adapter->regs
+                            + MAC_REG_RMDATA(mac->instance->index));
+
+       /* XXX convert stats */
+       return &mac->stats;
+}
+
+static void mac_destroy(struct cmac *mac)
+{
+       kfree(mac);
+}
+
+static struct cmac_ops chelsio_mac_ops = {
+       .destroy                 = mac_destroy,
+       .reset                   = mac_reset,
+       .interrupt_enable        = mac_intr_enable,
+       .interrupt_disable       = mac_intr_disable,
+       .interrupt_clear         = mac_intr_clear,
+       .enable                  = mac_enable,
+       .disable                 = mac_disable,
+       .set_mtu                 = mac_set_mtu,
+       .set_rx_mode             = mac_set_rx_mode,
+       .set_speed_duplex_fc     = mac_set_speed_duplex_fc,
+       .macaddress_get          = mac_get_address,
+       .statistics_update       = mac_update_statistics,
+};
+
+static struct cmac *mac_create(adapter_t *adapter, int index)
+{
+       struct cmac *mac;
+       u32 data32;
+
+       if (index >= 4)
+               return NULL;
+
+       mac = kzalloc(sizeof(*mac) + sizeof(cmac_instance), GFP_KERNEL);
+       if (!mac)
+               return NULL;
+
+       mac->ops = &chelsio_mac_ops;
+       mac->instance = (cmac_instance *) (mac + 1);
+
+       mac->instance->index = index;
+       mac->adapter = adapter;
+
+       data32 = readl(adapter->regs + MAC_REG_CSR(mac->instance->index));
+       data32 &= ~(F_MAC_RESET | F_MAC_PROMISC | F_MAC_PROMISC |
+                   F_MAC_LB_ENABLE | F_MAC_RX_ENABLE | F_MAC_TX_ENABLE);
+       data32 |= F_MAC_JUMBO_ENABLE;
+       writel(data32, adapter->regs + MAC_REG_CSR(mac->instance->index));
+
+       /* Initialize the random backoff seed. */
+       data32 = 0x55aa + (3 * index);
+       writel(data32,
+              adapter->regs + MAC_REG_GMRANDBACKOFFSEED(mac->instance->index));
+
+       /* Check to see if the mac address needs to be set manually. */
+       data32 = readl(adapter->regs + MAC_REG_IDLO(mac->instance->index));
+       if (data32 == 0 || data32 == 0xffffffff) {
+               /*
+                * Add a default MAC address if we can't read one.
+                */
+               writel(0x43FFFFFF - index,
+                      adapter->regs + MAC_REG_IDLO(mac->instance->index));
+               writel(0x0007,
+                      adapter->regs + MAC_REG_IDHI(mac->instance->index));
+       }
+
+       (void) mac_set_mtu(mac, 1500);
+       return mac;
+}
+
+struct gmac t1_chelsio_mac_ops = {
+       .create = mac_create
+};