#include #include #include "gsc3280mac1000.h" static void gsc3280mac1000_core_init(void __iomem *ioaddr) { u32 value = readl(ioaddr + GSC3280MAC_CONTROL); value |= GSC3280MAC_CORE_INIT; writel(value, ioaddr + GSC3280MAC_CONTROL); /* Freeze MMC counters */ writel(0x8, ioaddr + GSC3280MAC_MMC_CTRL); /* Mask GSC3280MAC interrupts */ writel(0x207, ioaddr + GSC3280MAC_INT_MASK); #ifdef GSC3280MAC_VLAN_TAG_USED /* Tag detection without filtering */ writel(0x0, ioaddr + GSC3280MAC_VLAN_TAG); #endif } static int gsc3280mac1000_rx_coe_supported(void __iomem *ioaddr) { u32 value = readl(ioaddr + GSC3280MAC_CONTROL); value |= GSC3280MAC_CONTROL_IPC; writel(value, ioaddr + GSC3280MAC_CONTROL); value = readl(ioaddr + GSC3280MAC_CONTROL); return !!(value & GSC3280MAC_CONTROL_IPC); } static void gsc3280mac1000_dump_regs(void __iomem *ioaddr) { int i; pr_info("\tGSC3280MAC1000 regs (base addr = 0x%p)\n", ioaddr); for (i = 0; i < 55; i++) { int offset = i * 4; pr_info("\tReg No. %d (offset 0x%x): 0x%08x\n", i, offset, readl(ioaddr + offset)); } } static void gsc3280mac1000_set_umac_addr(void __iomem *ioaddr, unsigned char *addr, unsigned int reg_n) { gsc3280mac_set_mac_addr(ioaddr, addr, GSC3280MAC_ADDR_HIGH(reg_n), GSC3280MAC_ADDR_LOW(reg_n)); } static void gsc3280mac1000_get_umac_addr(void __iomem *ioaddr, unsigned char *addr, unsigned int reg_n) { gsc3280mac_get_mac_addr(ioaddr, addr, GSC3280MAC_ADDR_HIGH(reg_n), GSC3280MAC_ADDR_LOW(reg_n)); } static void gsc3280mac1000_set_filter(struct net_device *dev) { void __iomem *ioaddr = (void __iomem *) dev->base_addr; unsigned int value = 0; CHIP_DBG(KERN_INFO "%s: # mcasts %d, # unicast %d\n", __func__, netdev_mc_count(dev), netdev_uc_count(dev)); if (dev->flags & IFF_PROMISC) value = GSC3280MAC_FRAME_FILTER_PR; else if ((netdev_mc_count(dev) > HASH_TABLE_SIZE) || (dev->flags & IFF_ALLMULTI)) { value = GSC3280MAC_FRAME_FILTER_PM; /* pass all multi */ writel(0xffffffff, ioaddr + GSC3280MAC_HASH_HIGH); writel(0xffffffff, ioaddr + GSC3280MAC_HASH_LOW); } else if (!netdev_mc_empty(dev)) { u32 mc_filter[2]; struct netdev_hw_addr *ha; /* Hash filter for multicast */ value = GSC3280MAC_FRAME_FILTER_HMC; memset(mc_filter, 0, sizeof(mc_filter)); netdev_for_each_mc_addr(ha, dev) { /* The upper 6 bits of the calculated CRC are used to index the contens of the hash table */ int bit_nr = bitrev32(~crc32_le(~0, ha->addr, 6)) >> 26; /* The most significant bit determines the register to * use (H/L) while the other 5 bits determine the bit * within the register. */ mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); } writel(mc_filter[0], ioaddr + GSC3280MAC_HASH_LOW); writel(mc_filter[1], ioaddr + GSC3280MAC_HASH_HIGH); } /* Handle multiple unicast addresses (perfect filtering)*/ if (netdev_uc_count(dev) > GSC3280MAC_MAX_UNICAST_ADDRESSES) /* Switch to promiscuous mode is more than 16 addrs are required */ value |= GSC3280MAC_FRAME_FILTER_PR; else { int reg = 1; struct netdev_hw_addr *ha; netdev_for_each_uc_addr(ha, dev) { gsc3280mac1000_set_umac_addr(ioaddr, ha->addr, reg); reg++; } } #ifdef FRAME_FILTER_DEBUG /* Enable Receive all mode (to debug filtering_fail errors) */ value |= GSC3280MAC_FRAME_FILTER_RA; #endif writel(value, ioaddr + GSC3280MAC_FRAME_FILTER); CHIP_DBG(KERN_INFO "\tFrame Filter reg: 0x%08x\n\tHash regs: " "HI 0x%08x, LO 0x%08x\n", readl(ioaddr + GSC3280MAC_FRAME_FILTER), readl(ioaddr + GSC3280MAC_HASH_HIGH), readl(ioaddr + GSC3280MAC_HASH_LOW)); } static void gsc3280mac1000_flow_ctrl(void __iomem *ioaddr, unsigned int duplex, unsigned int fc, unsigned int pause_time) { unsigned int flow = 0; CHIP_DBG(KERN_DEBUG "GSC3280MAC Flow-Control:\n"); if (fc & FLOW_RX) { CHIP_DBG(KERN_DEBUG "\tReceive Flow-Control ON\n"); flow |= GSC3280MAC_FLOW_CTRL_RFE; } if (fc & FLOW_TX) { CHIP_DBG(KERN_DEBUG "\tTransmit Flow-Control ON\n"); flow |= GSC3280MAC_FLOW_CTRL_TFE; } if (duplex) { CHIP_DBG(KERN_DEBUG "\tduplex mode: PAUSE %d\n", pause_time); flow |= (pause_time << GSC3280MAC_FLOW_CTRL_PT_SHIFT); } writel(flow, ioaddr + GSC3280MAC_FLOW_CTRL); } static void gsc3280mac1000_pmt(void __iomem *ioaddr, unsigned long mode) { unsigned int pmt = 0; if (mode & WAKE_MAGIC) { CHIP_DBG(KERN_DEBUG "GSC3280MAC: WOL Magic frame\n"); pmt |= power_down | magic_pkt_en; } if (mode & WAKE_UCAST) { CHIP_DBG(KERN_DEBUG "GSC3280MAC: WOL on global unicast\n"); pmt |= global_unicast; } writel(pmt, ioaddr + GSC3280MAC_PMT); } static void gsc3280mac1000_irq_status(void __iomem *ioaddr) { u32 intr_status = readl(ioaddr + GSC3280MAC_INT_STATUS); /* Not used events (e.g. MMC interrupts) are not handled. */ if ((intr_status & mmc_tx_irq)) CHIP_DBG(KERN_DEBUG "GSC3280MAC: MMC tx interrupt: 0x%08x\n", readl(ioaddr + GSC3280MAC_MMC_TX_INTR)); if (unlikely(intr_status & mmc_rx_irq)) CHIP_DBG(KERN_DEBUG "GSC3280MAC: MMC rx interrupt: 0x%08x\n", readl(ioaddr + GSC3280MAC_MMC_RX_INTR)); if (unlikely(intr_status & mmc_rx_csum_offload_irq)) CHIP_DBG(KERN_DEBUG "GSC3280MAC: MMC rx csum offload: 0x%08x\n", readl(ioaddr + GSC3280MAC_MMC_RX_CSUM_OFFLOAD)); if (unlikely(intr_status & pmt_irq)) { CHIP_DBG(KERN_DEBUG "GSC3280MAC: received Magic frame\n"); /* clear the PMT bits 5 and 6 by reading the PMT * status register. */ readl(ioaddr + GSC3280MAC_PMT); } } static const struct gsc3280mac_ops gsc3280mac1000_ops = { .core_init = gsc3280mac1000_core_init, .rx_coe = gsc3280mac1000_rx_coe_supported, .dump_regs = gsc3280mac1000_dump_regs, .host_irq_status = gsc3280mac1000_irq_status, .set_filter = gsc3280mac1000_set_filter, .flow_ctrl = gsc3280mac1000_flow_ctrl, .pmt = gsc3280mac1000_pmt, .set_umac_addr = gsc3280mac1000_set_umac_addr, .get_umac_addr = gsc3280mac1000_get_umac_addr, }; struct mac_device_info *gsc3280mac1000_setup(void __iomem *ioaddr) { struct mac_device_info *mac; u32 uid = readl(ioaddr + GSC3280MAC_VERSION); pr_info("\tGSC3280MAC1000 - user ID: 0x%x, GSC3280 ID: 0x%x\n", ((uid & 0x0000ff00) >> 8), (uid & 0x000000ff)); mac = kzalloc(sizeof(const struct mac_device_info), GFP_KERNEL); if (!mac) return NULL; mac->mac = &gsc3280mac1000_ops; mac->dma = &gsc3280mac1000_dma_ops; mac->link.port = GSC3280MAC_CONTROL_PS; mac->link.duplex = GSC3280MAC_CONTROL_DM; mac->link.speed = GSC3280MAC_CONTROL_FES; mac->mii.addr = GSC3280MAC_MII_ADDR; mac->mii.data = GSC3280MAC_MII_DATA; return mac; }