diff options
author | Jeff Kirsher <jeffrey.t.kirsher@intel.com> | 2011-06-15 09:51:27 -0700 |
---|---|---|
committer | Jeff Kirsher <jeffrey.t.kirsher@intel.com> | 2011-08-12 03:41:18 -0700 |
commit | c23c5c1663b36c1428df32d1645232662459e02f (patch) | |
tree | 99a1497e3599cffd5945936f3552a4e0cb8b73dc /drivers/net/octeon/octeon_mgmt.c | |
parent | 554f4ffd3b2cd88e42007d069bc519563d0b459f (diff) |
octeon: Move the Cavium driver
Move the Cavium driver to drivers/net/ethernet/octeon/ and
make the necessary Kconfig and Makefile changes.
CC: David Daney <david.daney@cavium.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Acked-by: David Daney <david.daney@cavium.com>
Diffstat (limited to 'drivers/net/octeon/octeon_mgmt.c')
-rw-r--r-- | drivers/net/octeon/octeon_mgmt.c | 1166 |
1 files changed, 0 insertions, 1166 deletions
diff --git a/drivers/net/octeon/octeon_mgmt.c b/drivers/net/octeon/octeon_mgmt.c deleted file mode 100644 index 429e08c84e9b..000000000000 --- a/drivers/net/octeon/octeon_mgmt.c +++ /dev/null @@ -1,1166 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (C) 2009 Cavium Networks - */ - -#include <linux/capability.h> -#include <linux/dma-mapping.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/platform_device.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/if_vlan.h> -#include <linux/slab.h> -#include <linux/phy.h> -#include <linux/spinlock.h> - -#include <asm/octeon/octeon.h> -#include <asm/octeon/cvmx-mixx-defs.h> -#include <asm/octeon/cvmx-agl-defs.h> - -#define DRV_NAME "octeon_mgmt" -#define DRV_VERSION "2.0" -#define DRV_DESCRIPTION \ - "Cavium Networks Octeon MII (management) port Network Driver" - -#define OCTEON_MGMT_NAPI_WEIGHT 16 - -/* - * Ring sizes that are powers of two allow for more efficient modulo - * opertions. - */ -#define OCTEON_MGMT_RX_RING_SIZE 512 -#define OCTEON_MGMT_TX_RING_SIZE 128 - -/* Allow 8 bytes for vlan and FCS. */ -#define OCTEON_MGMT_RX_HEADROOM (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN) - -union mgmt_port_ring_entry { - u64 d64; - struct { - u64 reserved_62_63:2; - /* Length of the buffer/packet in bytes */ - u64 len:14; - /* For TX, signals that the packet should be timestamped */ - u64 tstamp:1; - /* The RX error code */ - u64 code:7; -#define RING_ENTRY_CODE_DONE 0xf -#define RING_ENTRY_CODE_MORE 0x10 - /* Physical address of the buffer */ - u64 addr:40; - } s; -}; - -struct octeon_mgmt { - struct net_device *netdev; - int port; - int irq; - u64 *tx_ring; - dma_addr_t tx_ring_handle; - unsigned int tx_next; - unsigned int tx_next_clean; - unsigned int tx_current_fill; - /* The tx_list lock also protects the ring related variables */ - struct sk_buff_head tx_list; - - /* RX variables only touched in napi_poll. No locking necessary. */ - u64 *rx_ring; - dma_addr_t rx_ring_handle; - unsigned int rx_next; - unsigned int rx_next_fill; - unsigned int rx_current_fill; - struct sk_buff_head rx_list; - - spinlock_t lock; - unsigned int last_duplex; - unsigned int last_link; - struct device *dev; - struct napi_struct napi; - struct tasklet_struct tx_clean_tasklet; - struct phy_device *phydev; -}; - -static void octeon_mgmt_set_rx_irq(struct octeon_mgmt *p, int enable) -{ - int port = p->port; - union cvmx_mixx_intena mix_intena; - unsigned long flags; - - spin_lock_irqsave(&p->lock, flags); - mix_intena.u64 = cvmx_read_csr(CVMX_MIXX_INTENA(port)); - mix_intena.s.ithena = enable ? 1 : 0; - cvmx_write_csr(CVMX_MIXX_INTENA(port), mix_intena.u64); - spin_unlock_irqrestore(&p->lock, flags); -} - -static void octeon_mgmt_set_tx_irq(struct octeon_mgmt *p, int enable) -{ - int port = p->port; - union cvmx_mixx_intena mix_intena; - unsigned long flags; - - spin_lock_irqsave(&p->lock, flags); - mix_intena.u64 = cvmx_read_csr(CVMX_MIXX_INTENA(port)); - mix_intena.s.othena = enable ? 1 : 0; - cvmx_write_csr(CVMX_MIXX_INTENA(port), mix_intena.u64); - spin_unlock_irqrestore(&p->lock, flags); -} - -static inline void octeon_mgmt_enable_rx_irq(struct octeon_mgmt *p) -{ - octeon_mgmt_set_rx_irq(p, 1); -} - -static inline void octeon_mgmt_disable_rx_irq(struct octeon_mgmt *p) -{ - octeon_mgmt_set_rx_irq(p, 0); -} - -static inline void octeon_mgmt_enable_tx_irq(struct octeon_mgmt *p) -{ - octeon_mgmt_set_tx_irq(p, 1); -} - -static inline void octeon_mgmt_disable_tx_irq(struct octeon_mgmt *p) -{ - octeon_mgmt_set_tx_irq(p, 0); -} - -static unsigned int ring_max_fill(unsigned int ring_size) -{ - return ring_size - 8; -} - -static unsigned int ring_size_to_bytes(unsigned int ring_size) -{ - return ring_size * sizeof(union mgmt_port_ring_entry); -} - -static void octeon_mgmt_rx_fill_ring(struct net_device *netdev) -{ - struct octeon_mgmt *p = netdev_priv(netdev); - int port = p->port; - - while (p->rx_current_fill < ring_max_fill(OCTEON_MGMT_RX_RING_SIZE)) { - unsigned int size; - union mgmt_port_ring_entry re; - struct sk_buff *skb; - - /* CN56XX pass 1 needs 8 bytes of padding. */ - size = netdev->mtu + OCTEON_MGMT_RX_HEADROOM + 8 + NET_IP_ALIGN; - - skb = netdev_alloc_skb(netdev, size); - if (!skb) - break; - skb_reserve(skb, NET_IP_ALIGN); - __skb_queue_tail(&p->rx_list, skb); - - re.d64 = 0; - re.s.len = size; - re.s.addr = dma_map_single(p->dev, skb->data, - size, - DMA_FROM_DEVICE); - - /* Put it in the ring. */ - p->rx_ring[p->rx_next_fill] = re.d64; - dma_sync_single_for_device(p->dev, p->rx_ring_handle, - ring_size_to_bytes(OCTEON_MGMT_RX_RING_SIZE), - DMA_BIDIRECTIONAL); - p->rx_next_fill = - (p->rx_next_fill + 1) % OCTEON_MGMT_RX_RING_SIZE; - p->rx_current_fill++; - /* Ring the bell. */ - cvmx_write_csr(CVMX_MIXX_IRING2(port), 1); - } -} - -static void octeon_mgmt_clean_tx_buffers(struct octeon_mgmt *p) -{ - int port = p->port; - union cvmx_mixx_orcnt mix_orcnt; - union mgmt_port_ring_entry re; - struct sk_buff *skb; - int cleaned = 0; - unsigned long flags; - - mix_orcnt.u64 = cvmx_read_csr(CVMX_MIXX_ORCNT(port)); - while (mix_orcnt.s.orcnt) { - spin_lock_irqsave(&p->tx_list.lock, flags); - - mix_orcnt.u64 = cvmx_read_csr(CVMX_MIXX_ORCNT(port)); - - if (mix_orcnt.s.orcnt == 0) { - spin_unlock_irqrestore(&p->tx_list.lock, flags); - break; - } - - dma_sync_single_for_cpu(p->dev, p->tx_ring_handle, - ring_size_to_bytes(OCTEON_MGMT_TX_RING_SIZE), - DMA_BIDIRECTIONAL); - - re.d64 = p->tx_ring[p->tx_next_clean]; - p->tx_next_clean = - (p->tx_next_clean + 1) % OCTEON_MGMT_TX_RING_SIZE; - skb = __skb_dequeue(&p->tx_list); - - mix_orcnt.u64 = 0; - mix_orcnt.s.orcnt = 1; - - /* Acknowledge to hardware that we have the buffer. */ - cvmx_write_csr(CVMX_MIXX_ORCNT(port), mix_orcnt.u64); - p->tx_current_fill--; - - spin_unlock_irqrestore(&p->tx_list.lock, flags); - - dma_unmap_single(p->dev, re.s.addr, re.s.len, - DMA_TO_DEVICE); - dev_kfree_skb_any(skb); - cleaned++; - - mix_orcnt.u64 = cvmx_read_csr(CVMX_MIXX_ORCNT(port)); - } - - if (cleaned && netif_queue_stopped(p->netdev)) - netif_wake_queue(p->netdev); -} - -static void octeon_mgmt_clean_tx_tasklet(unsigned long arg) -{ - struct octeon_mgmt *p = (struct octeon_mgmt *)arg; - octeon_mgmt_clean_tx_buffers(p); - octeon_mgmt_enable_tx_irq(p); -} - -static void octeon_mgmt_update_rx_stats(struct net_device *netdev) -{ - struct octeon_mgmt *p = netdev_priv(netdev); - int port = p->port; - unsigned long flags; - u64 drop, bad; - - /* These reads also clear the count registers. */ - drop = cvmx_read_csr(CVMX_AGL_GMX_RXX_STATS_PKTS_DRP(port)); - bad = cvmx_read_csr(CVMX_AGL_GMX_RXX_STATS_PKTS_BAD(port)); - - if (drop || bad) { - /* Do an atomic update. */ - spin_lock_irqsave(&p->lock, flags); - netdev->stats.rx_errors += bad; - netdev->stats.rx_dropped += drop; - spin_unlock_irqrestore(&p->lock, flags); - } -} - -static void octeon_mgmt_update_tx_stats(struct net_device *netdev) -{ - struct octeon_mgmt *p = netdev_priv(netdev); - int port = p->port; - unsigned long flags; - - union cvmx_agl_gmx_txx_stat0 s0; - union cvmx_agl_gmx_txx_stat1 s1; - - /* These reads also clear the count registers. */ - s0.u64 = cvmx_read_csr(CVMX_AGL_GMX_TXX_STAT0(port)); - s1.u64 = cvmx_read_csr(CVMX_AGL_GMX_TXX_STAT1(port)); - - if (s0.s.xsdef || s0.s.xscol || s1.s.scol || s1.s.mcol) { - /* Do an atomic update. */ - spin_lock_irqsave(&p->lock, flags); - netdev->stats.tx_errors += s0.s.xsdef + s0.s.xscol; - netdev->stats.collisions += s1.s.scol + s1.s.mcol; - spin_unlock_irqrestore(&p->lock, flags); - } -} - -/* - * Dequeue a receive skb and its corresponding ring entry. The ring - * entry is returned, *pskb is updated to point to the skb. - */ -static u64 octeon_mgmt_dequeue_rx_buffer(struct octeon_mgmt *p, - struct sk_buff **pskb) -{ - union mgmt_port_ring_entry re; - - dma_sync_single_for_cpu(p->dev, p->rx_ring_handle, - ring_size_to_bytes(OCTEON_MGMT_RX_RING_SIZE), - DMA_BIDIRECTIONAL); - - re.d64 = p->rx_ring[p->rx_next]; - p->rx_next = (p->rx_next + 1) % OCTEON_MGMT_RX_RING_SIZE; - p->rx_current_fill--; - *pskb = __skb_dequeue(&p->rx_list); - - dma_unmap_single(p->dev, re.s.addr, - ETH_FRAME_LEN + OCTEON_MGMT_RX_HEADROOM, - DMA_FROM_DEVICE); - - return re.d64; -} - - -static int octeon_mgmt_receive_one(struct octeon_mgmt *p) -{ - int port = p->port; - struct net_device *netdev = p->netdev; - union cvmx_mixx_ircnt mix_ircnt; - union mgmt_port_ring_entry re; - struct sk_buff *skb; - struct sk_buff *skb2; - struct sk_buff *skb_new; - union mgmt_port_ring_entry re2; - int rc = 1; - - - re.d64 = octeon_mgmt_dequeue_rx_buffer(p, &skb); - if (likely(re.s.code == RING_ENTRY_CODE_DONE)) { - /* A good packet, send it up. */ - skb_put(skb, re.s.len); -good: - skb->protocol = eth_type_trans(skb, netdev); - netdev->stats.rx_packets++; - netdev->stats.rx_bytes += skb->len; - netif_receive_skb(skb); - rc = 0; - } else if (re.s.code == RING_ENTRY_CODE_MORE) { - /* - * Packet split across skbs. This can happen if we - * increase the MTU. Buffers that are already in the - * rx ring can then end up being too small. As the rx - * ring is refilled, buffers sized for the new MTU - * will be used and we should go back to the normal - * non-split case. - */ - skb_put(skb, re.s.len); - do { - re2.d64 = octeon_mgmt_dequeue_rx_buffer(p, &skb2); - if (re2.s.code != RING_ENTRY_CODE_MORE - && re2.s.code != RING_ENTRY_CODE_DONE) - goto split_error; - skb_put(skb2, re2.s.len); - skb_new = skb_copy_expand(skb, 0, skb2->len, - GFP_ATOMIC); - if (!skb_new) - goto split_error; - if (skb_copy_bits(skb2, 0, skb_tail_pointer(skb_new), - skb2->len)) - goto split_error; - skb_put(skb_new, skb2->len); - dev_kfree_skb_any(skb); - dev_kfree_skb_any(skb2); - skb = skb_new; - } while (re2.s.code == RING_ENTRY_CODE_MORE); - goto good; - } else { - /* Some other error, discard it. */ - dev_kfree_skb_any(skb); - /* - * Error statistics are accumulated in - * octeon_mgmt_update_rx_stats. - */ - } - goto done; -split_error: - /* Discard the whole mess. */ - dev_kfree_skb_any(skb); - dev_kfree_skb_any(skb2); - while (re2.s.code == RING_ENTRY_CODE_MORE) { - re2.d64 = octeon_mgmt_dequeue_rx_buffer(p, &skb2); - dev_kfree_skb_any(skb2); - } - netdev->stats.rx_errors++; - -done: - /* Tell the hardware we processed a packet. */ - mix_ircnt.u64 = 0; - mix_ircnt.s.ircnt = 1; - cvmx_write_csr(CVMX_MIXX_IRCNT(port), mix_ircnt.u64); - return rc; -} - -static int octeon_mgmt_receive_packets(struct octeon_mgmt *p, int budget) -{ - int port = p->port; - unsigned int work_done = 0; - union cvmx_mixx_ircnt mix_ircnt; - int rc; - - mix_ircnt.u64 = cvmx_read_csr(CVMX_MIXX_IRCNT(port)); - while (work_done < budget && mix_ircnt.s.ircnt) { - - rc = octeon_mgmt_receive_one(p); - if (!rc) - work_done++; - - /* Check for more packets. */ - mix_ircnt.u64 = cvmx_read_csr(CVMX_MIXX_IRCNT(port)); - } - - octeon_mgmt_rx_fill_ring(p->netdev); - - return work_done; -} - -static int octeon_mgmt_napi_poll(struct napi_struct *napi, int budget) -{ - struct octeon_mgmt *p = container_of(napi, struct octeon_mgmt, napi); - struct net_device *netdev = p->netdev; - unsigned int work_done = 0; - - work_done = octeon_mgmt_receive_packets(p, budget); - - if (work_done < budget) { - /* We stopped because no more packets were available. */ - napi_complete(napi); - octeon_mgmt_enable_rx_irq(p); - } - octeon_mgmt_update_rx_stats(netdev); - - return work_done; -} - -/* Reset the hardware to clean state. */ -static void octeon_mgmt_reset_hw(struct octeon_mgmt *p) -{ - union cvmx_mixx_ctl mix_ctl; - union cvmx_mixx_bist mix_bist; - union cvmx_agl_gmx_bist agl_gmx_bist; - - mix_ctl.u64 = 0; - cvmx_write_csr(CVMX_MIXX_CTL(p->port), mix_ctl.u64); - do { - mix_ctl.u64 = cvmx_read_csr(CVMX_MIXX_CTL(p->port)); - } while (mix_ctl.s.busy); - mix_ctl.s.reset = 1; - cvmx_write_csr(CVMX_MIXX_CTL(p->port), mix_ctl.u64); - cvmx_read_csr(CVMX_MIXX_CTL(p->port)); - cvmx_wait(64); - - mix_bist.u64 = cvmx_read_csr(CVMX_MIXX_BIST(p->port)); - if (mix_bist.u64) - dev_warn(p->dev, "MIX failed BIST (0x%016llx)\n", - (unsigned long long)mix_bist.u64); - - agl_gmx_bist.u64 = cvmx_read_csr(CVMX_AGL_GMX_BIST); - if (agl_gmx_bist.u64) - dev_warn(p->dev, "AGL failed BIST (0x%016llx)\n", - (unsigned long long)agl_gmx_bist.u64); -} - -struct octeon_mgmt_cam_state { - u64 cam[6]; - u64 cam_mask; - int cam_index; -}; - -static void octeon_mgmt_cam_state_add(struct octeon_mgmt_cam_state *cs, - unsigned char *addr) -{ - int i; - - for (i = 0; i < 6; i++) - cs->cam[i] |= (u64)addr[i] << (8 * (cs->cam_index)); - cs->cam_mask |= (1ULL << cs->cam_index); - cs->cam_index++; -} - -static void octeon_mgmt_set_rx_filtering(struct net_device *netdev) -{ - struct octeon_mgmt *p = netdev_priv(netdev); - int port = p->port; - union cvmx_agl_gmx_rxx_adr_ctl adr_ctl; - union cvmx_agl_gmx_prtx_cfg agl_gmx_prtx; - unsigned long flags; - unsigned int prev_packet_enable; - unsigned int cam_mode = 1; /* 1 - Accept on CAM match */ - unsigned int multicast_mode = 1; /* 1 - Reject all multicast. */ - struct octeon_mgmt_cam_state cam_state; - struct netdev_hw_addr *ha; - int available_cam_entries; - - memset(&cam_state, 0, sizeof(cam_state)); - - if ((netdev->flags & IFF_PROMISC) || netdev->uc.count > 7) { - cam_mode = 0; - available_cam_entries = 8; - } else { - /* - * One CAM entry for the primary address, leaves seven - * for the secondary addresses. - */ - available_cam_entries = 7 - netdev->uc.count; - } - - if (netdev->flags & IFF_MULTICAST) { - if (cam_mode == 0 || (netdev->flags & IFF_ALLMULTI) || - netdev_mc_count(netdev) > available_cam_entries) - multicast_mode = 2; /* 2 - Accept all multicast. */ - else - multicast_mode = 0; /* 0 - Use CAM. */ - } - - if (cam_mode == 1) { - /* Add primary address. */ - octeon_mgmt_cam_state_add(&cam_state, netdev->dev_addr); - netdev_for_each_uc_addr(ha, netdev) - octeon_mgmt_cam_state_add(&cam_state, ha->addr); - } - if (multicast_mode == 0) { - netdev_for_each_mc_addr(ha, netdev) - octeon_mgmt_cam_state_add(&cam_state, ha->addr); - } - - spin_lock_irqsave(&p->lock, flags); - - /* Disable packet I/O. */ - agl_gmx_prtx.u64 = cvmx_read_csr(CVMX_AGL_GMX_PRTX_CFG(port)); - prev_packet_enable = agl_gmx_prtx.s.en; - agl_gmx_prtx.s.en = 0; - cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port), agl_gmx_prtx.u64); - - adr_ctl.u64 = 0; - adr_ctl.s.cam_mode = cam_mode; - adr_ctl.s.mcst = multicast_mode; - adr_ctl.s.bcst = 1; /* Allow broadcast */ - - cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CTL(port), adr_ctl.u64); - - cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM0(port), cam_state.cam[0]); - cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM1(port), cam_state.cam[1]); - cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM2(port), cam_state.cam[2]); - cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM3(port), cam_state.cam[3]); - cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM4(port), cam_state.cam[4]); - cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM5(port), cam_state.cam[5]); - cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM_EN(port), cam_state.cam_mask); - - /* Restore packet I/O. */ - agl_gmx_prtx.s.en = prev_packet_enable; - cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port), agl_gmx_prtx.u64); - - spin_unlock_irqrestore(&p->lock, flags); -} - -static int octeon_mgmt_set_mac_address(struct net_device *netdev, void *addr) -{ - struct sockaddr *sa = addr; - - if (!is_valid_ether_addr(sa->sa_data)) - return -EADDRNOTAVAIL; - - memcpy(netdev->dev_addr, sa->sa_data, ETH_ALEN); - - octeon_mgmt_set_rx_filtering(netdev); - - return 0; -} - -static int octeon_mgmt_change_mtu(struct net_device *netdev, int new_mtu) -{ - struct octeon_mgmt *p = netdev_priv(netdev); - int port = p->port; - int size_without_fcs = new_mtu + OCTEON_MGMT_RX_HEADROOM; - - /* - * Limit the MTU to make sure the ethernet packets are between - * 64 bytes and 16383 bytes. - */ - if (size_without_fcs < 64 || size_without_fcs > 16383) { - dev_warn(p->dev, "MTU must be between %d and %d.\n", - 64 - OCTEON_MGMT_RX_HEADROOM, - 16383 - OCTEON_MGMT_RX_HEADROOM); - return -EINVAL; - } - - netdev->mtu = new_mtu; - - cvmx_write_csr(CVMX_AGL_GMX_RXX_FRM_MAX(port), size_without_fcs); - cvmx_write_csr(CVMX_AGL_GMX_RXX_JABBER(port), - (size_without_fcs + 7) & 0xfff8); - - return 0; -} - -static irqreturn_t octeon_mgmt_interrupt(int cpl, void *dev_id) -{ - struct net_device *netdev = dev_id; - struct octeon_mgmt *p = netdev_priv(netdev); - int port = p->port; - union cvmx_mixx_isr mixx_isr; - - mixx_isr.u64 = cvmx_read_csr(CVMX_MIXX_ISR(port)); - - /* Clear any pending interrupts */ - cvmx_write_csr(CVMX_MIXX_ISR(port), mixx_isr.u64); - cvmx_read_csr(CVMX_MIXX_ISR(port)); - - if (mixx_isr.s.irthresh) { - octeon_mgmt_disable_rx_irq(p); - napi_schedule(&p->napi); - } - if (mixx_isr.s.orthresh) { - octeon_mgmt_disable_tx_irq(p); - tasklet_schedule(&p->tx_clean_tasklet); - } - - return IRQ_HANDLED; -} - -static int octeon_mgmt_ioctl(struct net_device *netdev, - struct ifreq *rq, int cmd) -{ - struct octeon_mgmt *p = netdev_priv(netdev); - - if (!netif_running(netdev)) - return -EINVAL; - - if (!p->phydev) - return -EINVAL; - - return phy_mii_ioctl(p->phydev, rq, cmd); -} - -static void octeon_mgmt_adjust_link(struct net_device *netdev) -{ - struct octeon_mgmt *p = netdev_priv(netdev); - int port = p->port; - union cvmx_agl_gmx_prtx_cfg prtx_cfg; - unsigned long flags; - int link_changed = 0; - - spin_lock_irqsave(&p->lock, flags); - if (p->phydev->link) { - if (!p->last_link) - link_changed = 1; - if (p->last_duplex != p->phydev->duplex) { - p->last_duplex = p->phydev->duplex; - prtx_cfg.u64 = - cvmx_read_csr(CVMX_AGL_GMX_PRTX_CFG(port)); - prtx_cfg.s.duplex = p->phydev->duplex; - cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port), - prtx_cfg.u64); - } - } else { - if (p->last_link) - link_changed = -1; - } - p->last_link = p->phydev->link; - spin_unlock_irqrestore(&p->lock, flags); - - if (link_changed != 0) { - if (link_changed > 0) { - netif_carrier_on(netdev); - pr_info("%s: Link is up - %d/%s\n", netdev->name, - p->phydev->speed, - DUPLEX_FULL == p->phydev->duplex ? - "Full" : "Half"); - } else { - netif_carrier_off(netdev); - pr_info("%s: Link is down\n", netdev->name); - } - } -} - -static int octeon_mgmt_init_phy(struct net_device *netdev) -{ - struct octeon_mgmt *p = netdev_priv(netdev); - char phy_id[20]; - - if (octeon_is_simulation()) { - /* No PHYs in the simulator. */ - netif_carrier_on(netdev); - return 0; - } - - snprintf(phy_id, sizeof(phy_id), PHY_ID_FMT, "0", p->port); - - p->phydev = phy_connect(netdev, phy_id, octeon_mgmt_adjust_link, 0, - PHY_INTERFACE_MODE_MII); - - if (IS_ERR(p->phydev)) { - p->phydev = NULL; - return -1; - } - - phy_start_aneg(p->phydev); - - return 0; -} - -static int octeon_mgmt_open(struct net_device *netdev) -{ - struct octeon_mgmt *p = netdev_priv(netdev); - int port = p->port; - union cvmx_mixx_ctl mix_ctl; - union cvmx_agl_gmx_inf_mode agl_gmx_inf_mode; - union cvmx_mixx_oring1 oring1; - union cvmx_mixx_iring1 iring1; - union cvmx_agl_gmx_prtx_cfg prtx_cfg; - union cvmx_agl_gmx_rxx_frm_ctl rxx_frm_ctl; - union cvmx_mixx_irhwm mix_irhwm; - union cvmx_mixx_orhwm mix_orhwm; - union cvmx_mixx_intena mix_intena; - struct sockaddr sa; - - /* Allocate ring buffers. */ - p->tx_ring = kzalloc(ring_size_to_bytes(OCTEON_MGMT_TX_RING_SIZE), - GFP_KERNEL); - if (!p->tx_ring) - return -ENOMEM; - p->tx_ring_handle = - dma_map_single(p->dev, p->tx_ring, - ring_size_to_bytes(OCTEON_MGMT_TX_RING_SIZE), - DMA_BIDIRECTIONAL); - p->tx_next = 0; - p->tx_next_clean = 0; - p->tx_current_fill = 0; - - - p->rx_ring = kzalloc(ring_size_to_bytes(OCTEON_MGMT_RX_RING_SIZE), - GFP_KERNEL); - if (!p->rx_ring) - goto err_nomem; - p->rx_ring_handle = - dma_map_single(p->dev, p->rx_ring, - ring_size_to_bytes(OCTEON_MGMT_RX_RING_SIZE), - DMA_BIDIRECTIONAL); - - p->rx_next = 0; - p->rx_next_fill = 0; - p->rx_current_fill = 0; - - octeon_mgmt_reset_hw(p); - - mix_ctl.u64 = cvmx_read_csr(CVMX_MIXX_CTL(port)); - - /* Bring it out of reset if needed. */ - if (mix_ctl.s.reset) { - mix_ctl.s.reset = 0; - cvmx_write_csr(CVMX_MIXX_CTL(port), mix_ctl.u64); - do { - mix_ctl.u64 = cvmx_read_csr(CVMX_MIXX_CTL(port)); - } while (mix_ctl.s.reset); - } - - agl_gmx_inf_mode.u64 = 0; - agl_gmx_inf_mode.s.en = 1; - cvmx_write_csr(CVMX_AGL_GMX_INF_MODE, agl_gmx_inf_mode.u64); - - oring1.u64 = 0; - oring1.s.obase = p->tx_ring_handle >> 3; - oring1.s.osize = OCTEON_MGMT_TX_RING_SIZE; - cvmx_write_csr(CVMX_MIXX_ORING1(port), oring1.u64); - - iring1.u64 = 0; - iring1.s.ibase = p->rx_ring_handle >> 3; - iring1.s.isize = OCTEON_MGMT_RX_RING_SIZE; - cvmx_write_csr(CVMX_MIXX_IRING1(port), iring1.u64); - - /* Disable packet I/O. */ - prtx_cfg.u64 = cvmx_read_csr(CVMX_AGL_GMX_PRTX_CFG(port)); - prtx_cfg.s.en = 0; - cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port), prtx_cfg.u64); - - memcpy(sa.sa_data, netdev->dev_addr, ETH_ALEN); - octeon_mgmt_set_mac_address(netdev, &sa); - - octeon_mgmt_change_mtu(netdev, netdev->mtu); - - /* - * Enable the port HW. Packets are not allowed until - * cvmx_mgmt_port_enable() is called. - */ - mix_ctl.u64 = 0; - mix_ctl.s.crc_strip = 1; /* Strip the ending CRC */ - mix_ctl.s.en = 1; /* Enable the port */ - mix_ctl.s.nbtarb = 0; /* Arbitration mode */ - /* MII CB-request FIFO programmable high watermark */ - mix_ctl.s.mrq_hwm = 1; - cvmx_write_csr(CVMX_MIXX_CTL(port), mix_ctl.u64); - - if (OCTEON_IS_MODEL(OCTEON_CN56XX_PASS1_X) - || OCTEON_IS_MODEL(OCTEON_CN52XX_PASS1_X)) { - /* - * Force compensation values, as they are not - * determined properly by HW - */ - union cvmx_agl_gmx_drv_ctl drv_ctl; - - drv_ctl.u64 = cvmx_read_csr(CVMX_AGL_GMX_DRV_CTL); - if (port) { - drv_ctl.s.byp_en1 = 1; - drv_ctl.s.nctl1 = 6; - drv_ctl.s.pctl1 = 6; - } else { - drv_ctl.s.byp_en = 1; - drv_ctl.s.nctl = 6; - drv_ctl.s.pctl = 6; - } - cvmx_write_csr(CVMX_AGL_GMX_DRV_CTL, drv_ctl.u64); - } - - octeon_mgmt_rx_fill_ring(netdev); - - /* Clear statistics. */ - /* Clear on read. */ - cvmx_write_csr(CVMX_AGL_GMX_RXX_STATS_CTL(port), 1); - cvmx_write_csr(CVMX_AGL_GMX_RXX_STATS_PKTS_DRP(port), 0); - cvmx_write_csr(CVMX_AGL_GMX_RXX_STATS_PKTS_BAD(port), 0); - - cvmx_write_csr(CVMX_AGL_GMX_TXX_STATS_CTL(port), 1); - cvmx_write_csr(CVMX_AGL_GMX_TXX_STAT0(port), 0); - cvmx_write_csr(CVMX_AGL_GMX_TXX_STAT1(port), 0); - - /* Clear any pending interrupts */ - cvmx_write_csr(CVMX_MIXX_ISR(port), cvmx_read_csr(CVMX_MIXX_ISR(port))); - - if (request_irq(p->irq, octeon_mgmt_interrupt, 0, netdev->name, - netdev)) { - dev_err(p->dev, "request_irq(%d) failed.\n", p->irq); - goto err_noirq; - } - - /* Interrupt every single RX packet */ - mix_irhwm.u64 = 0; - mix_irhwm.s.irhwm = 0; - cvmx_write_csr(CVMX_MIXX_IRHWM(port), mix_irhwm.u64); - - /* Interrupt when we have 1 or more packets to clean. */ - mix_orhwm.u64 = 0; - mix_orhwm.s.orhwm = 1; - cvmx_write_csr(CVMX_MIXX_ORHWM(port), mix_orhwm.u64); - - /* Enable receive and transmit interrupts */ - mix_intena.u64 = 0; - mix_intena.s.ithena = 1; - mix_intena.s.othena = 1; - cvmx_write_csr(CVMX_MIXX_INTENA(port), mix_intena.u64); - - - /* Enable packet I/O. */ - - rxx_frm_ctl.u64 = 0; - rxx_frm_ctl.s.pre_align = 1; - /* - * When set, disables the length check for non-min sized pkts - * with padding in the client data. - */ - rxx_frm_ctl.s.pad_len = 1; - /* When set, disables the length check for VLAN pkts */ - rxx_frm_ctl.s.vlan_len = 1; - /* When set, PREAMBLE checking is less strict */ - rxx_frm_ctl.s.pre_free = 1; - /* Control Pause Frames can match station SMAC */ - rxx_frm_ctl.s.ctl_smac = 0; - /* Control Pause Frames can match globally assign Multicast address */ - rxx_frm_ctl.s.ctl_mcst = 1; - /* Forward pause information to TX block */ - rxx_frm_ctl.s.ctl_bck = 1; - /* Drop Control Pause Frames */ - rxx_frm_ctl.s.ctl_drp = 1; - /* Strip off the preamble */ - rxx_frm_ctl.s.pre_strp = 1; - /* - * This port is configured to send PREAMBLE+SFD to begin every - * frame. GMX checks that the PREAMBLE is sent correctly. - */ - rxx_frm_ctl.s.pre_chk = 1; - cvmx_write_csr(CVMX_AGL_GMX_RXX_FRM_CTL(port), rxx_frm_ctl.u64); - - /* Enable the AGL block */ - agl_gmx_inf_mode.u64 = 0; - agl_gmx_inf_mode.s.en = 1; - cvmx_write_csr(CVMX_AGL_GMX_INF_MODE, agl_gmx_inf_mode.u64); - - /* Configure the port duplex and enables */ - prtx_cfg.u64 = cvmx_read_csr(CVMX_AGL_GMX_PRTX_CFG(port)); - prtx_cfg.s.tx_en = 1; - prtx_cfg.s.rx_en = 1; - prtx_cfg.s.en = 1; - p->last_duplex = 1; - prtx_cfg.s.duplex = p->last_duplex; - cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port), prtx_cfg.u64); - - p->last_link = 0; - netif_carrier_off(netdev); - - if (octeon_mgmt_init_phy(netdev)) { - dev_err(p->dev, "Cannot initialize PHY.\n"); - goto err_noirq; - } - - netif_wake_queue(netdev); - napi_enable(&p->napi); - - return 0; -err_noirq: - octeon_mgmt_reset_hw(p); - dma_unmap_single(p->dev, p->rx_ring_handle, - ring_size_to_bytes(OCTEON_MGMT_RX_RING_SIZE), - DMA_BIDIRECTIONAL); - kfree(p->rx_ring); -err_nomem: - dma_unmap_single(p->dev, p->tx_ring_handle, - ring_size_to_bytes(OCTEON_MGMT_TX_RING_SIZE), - DMA_BIDIRECTIONAL); - kfree(p->tx_ring); - return -ENOMEM; -} - -static int octeon_mgmt_stop(struct net_device *netdev) -{ - struct octeon_mgmt *p = netdev_priv(netdev); - - napi_disable(&p->napi); - netif_stop_queue(netdev); - - if (p->phydev) - phy_disconnect(p->phydev); - - netif_carrier_off(netdev); - - octeon_mgmt_reset_hw(p); - - free_irq(p->irq, netdev); - - /* dma_unmap is a nop on Octeon, so just free everything. */ - skb_queue_purge(&p->tx_list); - skb_queue_purge(&p->rx_list); - - dma_unmap_single(p->dev, p->rx_ring_handle, - ring_size_to_bytes(OCTEON_MGMT_RX_RING_SIZE), - DMA_BIDIRECTIONAL); - kfree(p->rx_ring); - - dma_unmap_single(p->dev, p->tx_ring_handle, - ring_size_to_bytes(OCTEON_MGMT_TX_RING_SIZE), - DMA_BIDIRECTIONAL); - kfree(p->tx_ring); - - return 0; -} - -static int octeon_mgmt_xmit(struct sk_buff *skb, struct net_device *netdev) -{ - struct octeon_mgmt *p = netdev_priv(netdev); - int port = p->port; - union mgmt_port_ring_entry re; - unsigned long flags; - int rv = NETDEV_TX_BUSY; - - re.d64 = 0; - re.s.len = skb->len; - re.s.addr = dma_map_single(p->dev, skb->data, - skb->len, - DMA_TO_DEVICE); - - spin_lock_irqsave(&p->tx_list.lock, flags); - - if (unlikely(p->tx_current_fill >= ring_max_fill(OCTEON_MGMT_TX_RING_SIZE) - 1)) { - spin_unlock_irqrestore(&p->tx_list.lock, flags); - netif_stop_queue(netdev); - spin_lock_irqsave(&p->tx_list.lock, flags); - } - - if (unlikely(p->tx_current_fill >= - ring_max_fill(OCTEON_MGMT_TX_RING_SIZE))) { - spin_unlock_irqrestore(&p->tx_list.lock, flags); - dma_unmap_single(p->dev, re.s.addr, re.s.len, - DMA_TO_DEVICE); - goto out; - } - - __skb_queue_tail(&p->tx_list, skb); - - /* Put it in the ring. */ - p->tx_ring[p->tx_next] = re.d64; - p->tx_next = (p->tx_next + 1) % OCTEON_MGMT_TX_RING_SIZE; - p->tx_current_fill++; - - spin_unlock_irqrestore(&p->tx_list.lock, flags); - - dma_sync_single_for_device(p->dev, p->tx_ring_handle, - ring_size_to_bytes(OCTEON_MGMT_TX_RING_SIZE), - DMA_BIDIRECTIONAL); - - netdev->stats.tx_packets++; - netdev->stats.tx_bytes += skb->len; - - /* Ring the bell. */ - cvmx_write_csr(CVMX_MIXX_ORING2(port), 1); - - rv = NETDEV_TX_OK; -out: - octeon_mgmt_update_tx_stats(netdev); - return rv; -} - -#ifdef CONFIG_NET_POLL_CONTROLLER -static void octeon_mgmt_poll_controller(struct net_device *netdev) -{ - struct octeon_mgmt *p = netdev_priv(netdev); - - octeon_mgmt_receive_packets(p, 16); - octeon_mgmt_update_rx_stats(netdev); -} -#endif - -static void octeon_mgmt_get_drvinfo(struct net_device *netdev, - struct ethtool_drvinfo *info) -{ - strncpy(info->driver, DRV_NAME, sizeof(info->driver)); - strncpy(info->version, DRV_VERSION, sizeof(info->version)); - strncpy(info->fw_version, "N/A", sizeof(info->fw_version)); - strncpy(info->bus_info, "N/A", sizeof(info->bus_info)); - info->n_stats = 0; - info->testinfo_len = 0; - info->regdump_len = 0; - info->eedump_len = 0; -} - -static int octeon_mgmt_get_settings(struct net_device *netdev, - struct ethtool_cmd *cmd) -{ - struct octeon_mgmt *p = netdev_priv(netdev); - - if (p->phydev) - return phy_ethtool_gset(p->phydev, cmd); - - return -EINVAL; -} - -static int octeon_mgmt_set_settings(struct net_device *netdev, - struct ethtool_cmd *cmd) -{ - struct octeon_mgmt *p = netdev_priv(netdev); - - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - if (p->phydev) - return phy_ethtool_sset(p->phydev, cmd); - - return -EINVAL; -} - -static const struct ethtool_ops octeon_mgmt_ethtool_ops = { - .get_drvinfo = octeon_mgmt_get_drvinfo, - .get_link = ethtool_op_get_link, - .get_settings = octeon_mgmt_get_settings, - .set_settings = octeon_mgmt_set_settings -}; - -static const struct net_device_ops octeon_mgmt_ops = { - .ndo_open = octeon_mgmt_open, - .ndo_stop = octeon_mgmt_stop, - .ndo_start_xmit = octeon_mgmt_xmit, - .ndo_set_rx_mode = octeon_mgmt_set_rx_filtering, - .ndo_set_multicast_list = octeon_mgmt_set_rx_filtering, - .ndo_set_mac_address = octeon_mgmt_set_mac_address, - .ndo_do_ioctl = octeon_mgmt_ioctl, - .ndo_change_mtu = octeon_mgmt_change_mtu, -#ifdef CONFIG_NET_POLL_CONTROLLER - .ndo_poll_controller = octeon_mgmt_poll_controller, -#endif -}; - -static int __devinit octeon_mgmt_probe(struct platform_device *pdev) -{ - struct resource *res_irq; - struct net_device *netdev; - struct octeon_mgmt *p; - int i; - - netdev = alloc_etherdev(sizeof(struct octeon_mgmt)); - if (netdev == NULL) - return -ENOMEM; - - dev_set_drvdata(&pdev->dev, netdev); - p = netdev_priv(netdev); - netif_napi_add(netdev, &p->napi, octeon_mgmt_napi_poll, - OCTEON_MGMT_NAPI_WEIGHT); - - p->netdev = netdev; - p->dev = &pdev->dev; - - p->port = pdev->id; - snprintf(netdev->name, IFNAMSIZ, "mgmt%d", p->port); - - res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res_irq) - goto err; - - p->irq = res_irq->start; - spin_lock_init(&p->lock); - - skb_queue_head_init(&p->tx_list); - skb_queue_head_init(&p->rx_list); - tasklet_init(&p->tx_clean_tasklet, - octeon_mgmt_clean_tx_tasklet, (unsigned long)p); - - netdev->netdev_ops = &octeon_mgmt_ops; - netdev->ethtool_ops = &octeon_mgmt_ethtool_ops; - - /* The mgmt ports get the first N MACs. */ - for (i = 0; i < 6; i++) - netdev->dev_addr[i] = octeon_bootinfo->mac_addr_base[i]; - netdev->dev_addr[5] += p->port; - - if (p->port >= octeon_bootinfo->mac_addr_count) - dev_err(&pdev->dev, - "Error %s: Using MAC outside of the assigned range: %pM\n", - netdev->name, netdev->dev_addr); - - if (register_netdev(netdev)) - goto err; - - dev_info(&pdev->dev, "Version " DRV_VERSION "\n"); - return 0; -err: - free_netdev(netdev); - return -ENOENT; -} - -static int __devexit octeon_mgmt_remove(struct platform_device *pdev) -{ - struct net_device *netdev = dev_get_drvdata(&pdev->dev); - - unregister_netdev(netdev); - free_netdev(netdev); - return 0; -} - -static struct platform_driver octeon_mgmt_driver = { - .driver = { - .name = "octeon_mgmt", - .owner = THIS_MODULE, - }, - .probe = octeon_mgmt_probe, - .remove = __devexit_p(octeon_mgmt_remove), -}; - -extern void octeon_mdiobus_force_mod_depencency(void); - -static int __init octeon_mgmt_mod_init(void) -{ - /* Force our mdiobus driver module to be loaded first. */ - octeon_mdiobus_force_mod_depencency(); - return platform_driver_register(&octeon_mgmt_driver); -} - -static void __exit octeon_mgmt_mod_exit(void) -{ - platform_driver_unregister(&octeon_mgmt_driver); -} - -module_init(octeon_mgmt_mod_init); -module_exit(octeon_mgmt_mod_exit); - -MODULE_DESCRIPTION(DRV_DESCRIPTION); -MODULE_AUTHOR("David Daney"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(DRV_VERSION); |