M7350/kernel/drivers/net/ethernet/msm/qfec.c

2793 lines
68 KiB
C
Raw Normal View History

2024-09-09 08:52:07 +00:00
/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/types.h> /* size_t */
#include <linux/interrupt.h> /* mark_bh */
#include <linux/netdevice.h> /* struct device, and other headers */
#include <linux/etherdevice.h> /* eth_type_trans */
#include <linux/skbuff.h>
#include <linux/proc_fs.h>
#include <linux/timer.h>
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <linux/net_tstamp.h>
#include <linux/phy.h>
#include <linux/inet.h>
#include "qfec.h"
#define QFEC_NAME "qfec"
#define QFEC_DRV_VER "Nov 29 2011"
#define ETH_BUF_SIZE 0x600
#define MAX_N_BD 50
#define MAC_ADDR_SIZE 6
#define RX_TX_BD_RATIO 8
#define TX_BD_NUM 256
#define RX_BD_NUM 256
#define TX_BD_TI_RATIO 4
#define MAX_MDIO_REG 32
#define H_DPLX 0
#define F_DPLX 1
/*
* logging macros
*/
#define QFEC_LOG_PR 1
#define QFEC_LOG_DBG 2
#define QFEC_LOG_DBG2 4
#define QFEC_LOG_MDIO_W 8
#define QFEC_LOG_MDIO_R 16
#define QFEC_MII_EXP_MASK (EXPANSION_LCWP | EXPANSION_ENABLENPAGE \
| EXPANSION_NPCAPABLE)
static int qfec_debug = QFEC_LOG_PR;
#ifdef QFEC_DEBUG
# define QFEC_LOG(flag, ...) \
do { \
if (flag & qfec_debug) \
pr_info(__VA_ARGS__); \
} while (0)
#else
# define QFEC_LOG(flag, ...)
#endif
#define QFEC_LOG_ERR(...) pr_err(__VA_ARGS__)
/*
* driver buffer-descriptor
* contains the 4 word HW descriptor plus an additional 4-words.
* (See the DSL bits in the BUS-Mode register).
*/
#define BD_FLAG_LAST_BD 1
struct buf_desc {
struct qfec_buf_desc *p_desc;
struct sk_buff *skb;
void *buf_virt_addr;
void *buf_phys_addr;
uint32_t last_bd_flag;
};
/*
*inline functions accessing non-struct qfec_buf_desc elements
*/
/* skb */
static inline struct sk_buff *qfec_bd_skbuf_get(struct buf_desc *p_bd)
{
return p_bd->skb;
};
static inline void qfec_bd_skbuf_set(struct buf_desc *p_bd, struct sk_buff *p)
{
p_bd->skb = p;
};
/* virtual addr */
static inline void qfec_bd_virt_set(struct buf_desc *p_bd, void *addr)
{
p_bd->buf_virt_addr = addr;
};
static inline void *qfec_bd_virt_get(struct buf_desc *p_bd)
{
return p_bd->buf_virt_addr;
};
/* physical addr */
static inline void qfec_bd_phys_set(struct buf_desc *p_bd, void *addr)
{
p_bd->buf_phys_addr = addr;
};
static inline void *qfec_bd_phys_get(struct buf_desc *p_bd)
{
return p_bd->buf_phys_addr;
};
/* last_bd_flag */
static inline uint32_t qfec_bd_last_bd(struct buf_desc *p_bd)
{
return (p_bd->last_bd_flag != 0);
};
static inline void qfec_bd_last_bd_set(struct buf_desc *p_bd)
{
p_bd->last_bd_flag = BD_FLAG_LAST_BD;
};
/*
*inline functions accessing struct qfec_buf_desc elements
*/
/* ownership bit */
static inline uint32_t qfec_bd_own(struct buf_desc *p_bd)
{
return p_bd->p_desc->status & BUF_OWN;
};
static inline void qfec_bd_own_set(struct buf_desc *p_bd)
{
p_bd->p_desc->status |= BUF_OWN ;
};
static inline void qfec_bd_own_clr(struct buf_desc *p_bd)
{
p_bd->p_desc->status &= ~(BUF_OWN);
};
static inline uint32_t qfec_bd_status_get(struct buf_desc *p_bd)
{
return p_bd->p_desc->status;
};
static inline void qfec_bd_status_set(struct buf_desc *p_bd, uint32_t status)
{
p_bd->p_desc->status = status;
};
static inline uint32_t qfec_bd_status_len(struct buf_desc *p_bd)
{
return BUF_RX_FL_GET((*p_bd->p_desc));
};
/* control register */
static inline void qfec_bd_ctl_reset(struct buf_desc *p_bd)
{
p_bd->p_desc->ctl = 0;
};
static inline uint32_t qfec_bd_ctl_get(struct buf_desc *p_bd)
{
return p_bd->p_desc->ctl;
};
static inline void qfec_bd_ctl_set(struct buf_desc *p_bd, uint32_t val)
{
p_bd->p_desc->ctl |= val;
};
static inline void qfec_bd_ctl_wr(struct buf_desc *p_bd, uint32_t val)
{
p_bd->p_desc->ctl = val;
};
/* pbuf register */
static inline void *qfec_bd_pbuf_get(struct buf_desc *p_bd)
{
return p_bd->p_desc->p_buf;
}
static inline void qfec_bd_pbuf_set(struct buf_desc *p_bd, void *p)
{
p_bd->p_desc->p_buf = p;
}
/* next register */
static inline void *qfec_bd_next_get(struct buf_desc *p_bd)
{
return p_bd->p_desc->next;
};
/*
* initialize an RX BD w/ a new buf
*/
static int qfec_rbd_init(struct net_device *dev, struct buf_desc *p_bd)
{
struct sk_buff *skb;
void *p;
void *v;
/* allocate and record ptrs for sk buff */
skb = dev_alloc_skb(ETH_BUF_SIZE);
if (!skb)
goto err;
qfec_bd_skbuf_set(p_bd, skb);
v = skb_put(skb, ETH_BUF_SIZE);
qfec_bd_virt_set(p_bd, v);
p = (void *) dma_map_single(&dev->dev,
(void *)skb->data, ETH_BUF_SIZE, DMA_FROM_DEVICE);
qfec_bd_pbuf_set(p_bd, p);
qfec_bd_phys_set(p_bd, p);
/* populate control register */
/* mark the last BD and set end-of-ring bit */
qfec_bd_ctl_wr(p_bd, ETH_BUF_SIZE |
(qfec_bd_last_bd(p_bd) ? BUF_RX_RER : 0));
qfec_bd_status_set(p_bd, BUF_OWN);
if (!(qfec_debug & QFEC_LOG_DBG2))
return 0;
/* debug messages */
QFEC_LOG(QFEC_LOG_DBG2, "%s: %p bd\n", __func__, p_bd);
QFEC_LOG(QFEC_LOG_DBG2, "%s: %p skb\n", __func__, skb);
QFEC_LOG(QFEC_LOG_DBG2,
"%s: %p p_bd, %p data, %p skb_put, %p virt, %p p_buf, %p p\n",
__func__, (void *)p_bd,
(void *)skb->data, v, /*(void *)skb_put(skb, ETH_BUF_SIZE), */
(void *)qfec_bd_virt_get(p_bd), (void *)qfec_bd_pbuf_get(p_bd),
(void *)p);
return 0;
err:
return -ENOMEM;
};
/*
* ring structure used to maintain indices of buffer-descriptor (BD) usage
*
* The RX BDs are normally all pre-allocated with buffers available to be
* DMA'd into with received frames. The head indicates the first BD/buffer
* containing a received frame, and the tail indicates the oldest BD/buffer
* that needs to be restored for use. Head and tail are both initialized
* to zero, and n_free is initialized to zero, since all BD are initialized.
*
* The TX BDs are normally available for use, only being initialized as
* TX frames are requested for transmission. The head indicates the
* first available BD, and the tail indicate the oldest BD that has
* not been acknowledged as transmitted. Head and tail are both initialized
* to zero, and n_free is initialized to len, since all are available for use.
*/
struct ring {
int head;
int tail;
int n_free;
int len;
};
/* accessory in line functions for struct ring */
static inline void qfec_ring_init(struct ring *p_ring, int size, int free)
{
p_ring->head = p_ring->tail = 0;
p_ring->len = size;
p_ring->n_free = free;
}
static inline int qfec_ring_full(struct ring *p_ring)
{
return (p_ring->n_free == 0);
};
static inline int qfec_ring_empty(struct ring *p_ring)
{
return (p_ring->n_free == p_ring->len);
}
static inline void qfec_ring_head_adv(struct ring *p_ring)
{
if (++p_ring->head == p_ring->len)
p_ring->head = 0;
p_ring->n_free--;
};
static inline void qfec_ring_tail_adv(struct ring *p_ring)
{
if (++p_ring->tail == p_ring->len)
p_ring->tail = 0;
p_ring->n_free++;
};
static inline int qfec_ring_head(struct ring *p_ring)
{
return p_ring->head;
};
static inline int qfec_ring_tail(struct ring *p_ring)
{
return p_ring->tail;
};
static inline int qfec_ring_room(struct ring *p_ring)
{
return p_ring->n_free;
};
/*
* counters track normal and abnormal driver events and activity
*/
enum cntr {
isr = 0,
fatal_bus,
early_tx,
tx_no_resource,
tx_proc_stopped,
tx_jabber_tmout,
xmit,
tx_int,
tx_isr,
tx_owned,
tx_underflow,
tx_replenish,
tx_skb_null,
tx_timeout,
tx_too_large,
gmac_isr,
/* half */
norm_int,
abnorm_int,
early_rx,
rx_buf_unavail,
rx_proc_stopped,
rx_watchdog,
netif_rx_cntr,
rx_int,
rx_isr,
rx_owned,
rx_overflow,
rx_dropped,
rx_skb_null,
queue_start,
queue_stop,
rx_paddr_nok,
ts_ioctl,
ts_tx_en,
ts_tx_rtn,
ts_rec,
cntr_last,
};
static char *cntr_name[] = {
"isr",
"fatal_bus",
"early_tx",
"tx_no_resource",
"tx_proc_stopped",
"tx_jabber_tmout",
"xmit",
"tx_int",
"tx_isr",
"tx_owned",
"tx_underflow",
"tx_replenish",
"tx_skb_null",
"tx_timeout",
"tx_too_large",
"gmac_isr",
/* half */
"norm_int",
"abnorm_int",
"early_rx",
"rx_buf_unavail",
"rx_proc_stopped",
"rx_watchdog",
"netif_rx",
"rx_int",
"rx_isr",
"rx_owned",
"rx_overflow",
"rx_dropped",
"rx_skb_null",
"queue_start",
"queue_stop",
"rx_paddr_nok",
"ts_ioctl",
"ts_tx_en",
"ts_tx_rtn",
"ts_rec",
""
};
/*
* private data
*/
static struct net_device *qfec_dev;
enum qfec_state {
timestamping = 0x04,
};
struct qfec_priv {
struct net_device *net_dev;
struct net_device_stats stats; /* req statistics */
struct device dev;
spinlock_t xmit_lock;
spinlock_t mdio_lock;
unsigned int state; /* driver state */
unsigned int bd_size; /* buf-desc alloc size */
struct qfec_buf_desc *bd_base; /* * qfec-buf-desc */
dma_addr_t tbd_dma; /* dma/phy-addr buf-desc */
dma_addr_t rbd_dma; /* dma/phy-addr buf-desc */
struct resource *mac_res;
void *mac_base; /* mac (virt) base address */
struct resource *clk_res;
void *clk_base; /* clk (virt) base address */
struct resource *fuse_res;
void *fuse_base; /* mac addr fuses */
unsigned int n_tbd; /* # of TX buf-desc */
struct ring ring_tbd; /* TX ring */
struct buf_desc *p_tbd;
unsigned int tx_ic_mod; /* (%) val for setting IC */
unsigned int n_rbd; /* # of RX buf-desc */
struct ring ring_rbd; /* RX ring */
struct buf_desc *p_rbd;
struct buf_desc *p_latest_rbd;
struct buf_desc *p_ending_rbd;
unsigned long cntr[cntr_last]; /* activity counters */
struct mii_if_info mii; /* used by mii lib */
int mdio_clk; /* phy mdio clock rate */
int phy_id; /* default PHY addr (0) */
struct timer_list phy_tmr; /* monitor PHY state */
};
/*
* cntrs display
*/
static int qfec_cntrs_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct qfec_priv *priv = netdev_priv(to_net_dev(dev));
int h = (cntr_last + 1) / 2;
int l;
int n;
int count = PAGE_SIZE;
QFEC_LOG(QFEC_LOG_DBG2, "%s:\n", __func__);
l = snprintf(&buf[0], count, "%s:\n", __func__);
for (n = 0; n < h; n++) {
l += snprintf(&buf[l], count - l,
" %12lu %-16s %12lu %s\n",
priv->cntr[n], cntr_name[n],
priv->cntr[n+h], cntr_name[n+h]);
}
return l;
}
# define CNTR_INC(priv, name) (priv->cntr[name]++)
/*
* functions that manage state
*/
static inline void qfec_queue_start(struct net_device *dev)
{
struct qfec_priv *priv = netdev_priv(dev);
if (netif_queue_stopped(dev)) {
netif_wake_queue(dev);
CNTR_INC(priv, queue_start);
}
};
static inline void qfec_queue_stop(struct net_device *dev)
{
struct qfec_priv *priv = netdev_priv(dev);
netif_stop_queue(dev);
CNTR_INC(priv, queue_stop);
};
/*
* functions to access and initialize the MAC registers
*/
static inline uint32_t qfec_reg_read(struct qfec_priv *priv, uint32_t reg)
{
return ioread32((void *) (priv->mac_base + reg));
}
static void qfec_reg_write(struct qfec_priv *priv, uint32_t reg, uint32_t val)
{
uint32_t addr = (uint32_t)priv->mac_base + reg;
QFEC_LOG(QFEC_LOG_DBG2, "%s: %08x <- %08x\n", __func__, addr, val);
iowrite32(val, (void *)addr);
}
/*
* speed/duplex/pause settings
*/
static int qfec_config_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct qfec_priv *priv = netdev_priv(to_net_dev(dev));
int cfg = qfec_reg_read(priv, MAC_CONFIG_REG);
int flow = qfec_reg_read(priv, FLOW_CONTROL_REG);
int l = 0;
int count = PAGE_SIZE;
QFEC_LOG(QFEC_LOG_DBG2, "%s:\n", __func__);
l += snprintf(&buf[l], count, "%s:", __func__);
l += snprintf(&buf[l], count - l, " [0x%08x] %4dM %s %s", cfg,
(cfg & MAC_CONFIG_REG_PS)
? ((cfg & MAC_CONFIG_REG_FES) ? 100 : 10) : 1000,
cfg & MAC_CONFIG_REG_DM ? "FD" : "HD",
cfg & MAC_CONFIG_REG_IPC ? "IPC" : "NoIPC");
flow &= FLOW_CONTROL_RFE | FLOW_CONTROL_TFE;
l += snprintf(&buf[l], count - l, " [0x%08x] %s", flow,
(flow == (FLOW_CONTROL_RFE | FLOW_CONTROL_TFE)) ? "PAUSE"
: ((flow == FLOW_CONTROL_RFE) ? "RX-PAUSE"
: ((flow == FLOW_CONTROL_TFE) ? "TX-PAUSE" : "")));
l += snprintf(&buf[l], count - l, " %s", QFEC_DRV_VER);
l += snprintf(&buf[l], count - l, "\n");
return l;
}
/*
* table and functions to initialize controller registers
*/
struct reg_entry {
unsigned int rdonly;
unsigned int addr;
char *label;
unsigned int val;
};
static struct reg_entry qfec_reg_tbl[] = {
{ 0, BUS_MODE_REG, "BUS_MODE_REG", BUS_MODE_REG_DEFAULT },
{ 0, AXI_BUS_MODE_REG, "AXI_BUS_MODE_REG", AXI_BUS_MODE_DEFAULT },
{ 0, AXI_STATUS_REG, "AXI_STATUS_REG", 0 },
{ 0, MAC_ADR_0_HIGH_REG, "MAC_ADR_0_HIGH_REG", 0x00000302 },
{ 0, MAC_ADR_0_LOW_REG, "MAC_ADR_0_LOW_REG", 0x01350702 },
{ 1, RX_DES_LST_ADR_REG, "RX_DES_LST_ADR_REG", 0 },
{ 1, TX_DES_LST_ADR_REG, "TX_DES_LST_ADR_REG", 0 },
{ 1, STATUS_REG, "STATUS_REG", 0 },
{ 1, DEBUG_REG, "DEBUG_REG", 0 },
{ 0, INTRP_EN_REG, "INTRP_EN_REG", QFEC_INTRP_SETUP},
{ 1, CUR_HOST_TX_DES_REG, "CUR_HOST_TX_DES_REG", 0 },
{ 1, CUR_HOST_RX_DES_REG, "CUR_HOST_RX_DES_REG", 0 },
{ 1, CUR_HOST_TX_BU_ADR_REG, "CUR_HOST_TX_BU_ADR_REG", 0 },
{ 1, CUR_HOST_RX_BU_ADR_REG, "CUR_HOST_RX_BU_ADR_REG", 0 },
{ 1, MAC_FR_FILTER_REG, "MAC_FR_FILTER_REG", 0 },
{ 0, MAC_CONFIG_REG, "MAC_CONFIG_REG", MAC_CONFIG_REG_SPD_1G
| MAC_CONFIG_REG_DM
| MAC_CONFIG_REG_TE
| MAC_CONFIG_REG_RE
| MAC_CONFIG_REG_IPC },
{ 1, INTRP_STATUS_REG, "INTRP_STATUS_REG", 0 },
{ 1, INTRP_MASK_REG, "INTRP_MASK_REG", 0 },
{ 0, OPER_MODE_REG, "OPER_MODE_REG", OPER_MODE_REG_DEFAULT },
{ 1, GMII_ADR_REG, "GMII_ADR_REG", 0 },
{ 1, GMII_DATA_REG, "GMII_DATA_REG", 0 },
{ 0, MMC_INTR_MASK_RX_REG, "MMC_INTR_MASK_RX_REG", 0xFFFFFFFF },
{ 0, MMC_INTR_MASK_TX_REG, "MMC_INTR_MASK_TX_REG", 0xFFFFFFFF },
{ 1, TS_HIGH_REG, "TS_HIGH_REG", 0 },
{ 1, TS_LOW_REG, "TS_LOW_REG", 0 },
{ 1, TS_HI_UPDT_REG, "TS_HI_UPDATE_REG", 0 },
{ 1, TS_LO_UPDT_REG, "TS_LO_UPDATE_REG", 0 },
{ 0, TS_SUB_SEC_INCR_REG, "TS_SUB_SEC_INCR_REG", 1 },
{ 0, TS_CTL_REG, "TS_CTL_REG", TS_CTL_TSENALL
| TS_CTL_TSCTRLSSR
| TS_CTL_TSINIT
| TS_CTL_TSENA },
};
static void qfec_reg_init(struct qfec_priv *priv)
{
struct reg_entry *p = qfec_reg_tbl;
int n = ARRAY_SIZE(qfec_reg_tbl);
QFEC_LOG(QFEC_LOG_DBG, "%s:\n", __func__);
for (; n--; p++) {
if (!p->rdonly)
qfec_reg_write(priv, p->addr, p->val);
}
}
/*
* display registers thru sysfs
*/
static int qfec_reg_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct qfec_priv *priv = netdev_priv(to_net_dev(dev));
struct reg_entry *p = qfec_reg_tbl;
int n = ARRAY_SIZE(qfec_reg_tbl);
int l = 0;
int count = PAGE_SIZE;
QFEC_LOG(QFEC_LOG_DBG2, "%s:\n", __func__);
for (; n--; p++) {
l += snprintf(&buf[l], count - l, " %8p %04x %08x %s\n",
(void *)priv->mac_base + p->addr, p->addr,
qfec_reg_read(priv, p->addr), p->label);
}
return l;
}
/*
* set the MAC-0 address
*/
static void qfec_set_adr_regs(struct qfec_priv *priv, uint8_t *addr)
{
uint32_t h = 0;
uint32_t l = 0;
h = h << 8 | addr[5];
h = h << 8 | addr[4];
l = l << 8 | addr[3];
l = l << 8 | addr[2];
l = l << 8 | addr[1];
l = l << 8 | addr[0];
qfec_reg_write(priv, MAC_ADR_0_HIGH_REG, h);
qfec_reg_write(priv, MAC_ADR_0_LOW_REG, l);
QFEC_LOG(QFEC_LOG_DBG, "%s: %08x %08x\n", __func__, h, l);
}
/*
* set up the RX filter
*/
static void qfec_set_rx_mode(struct net_device *dev)
{
struct qfec_priv *priv = netdev_priv(dev);
uint32_t filter_conf;
int index;
/* Clear address filter entries */
for (index = 1; index < MAC_ADR_MAX; ++index) {
qfec_reg_write(priv, MAC_ADR_HIGH_REG_N(index), 0);
qfec_reg_write(priv, MAC_ADR_LOW_REG_N(index), 0);
}
if (dev->flags & IFF_PROMISC) {
/* Receive all frames */
filter_conf = MAC_FR_FILTER_RA;
} else if ((dev->flags & IFF_MULTICAST) == 0) {
/* Unicast filtering only */
filter_conf = MAC_FR_FILTER_HPF;
} else if ((netdev_mc_count(dev) > MAC_ADR_MAX - 1) ||
(dev->flags & IFF_ALLMULTI)) {
/* Unicast filtering is enabled, Pass all multicast frames */
filter_conf = MAC_FR_FILTER_HPF | MAC_FR_FILTER_PM;
} else {
struct netdev_hw_addr *ha;
/* Both unicast and multicast filtering are enabled */
filter_conf = MAC_FR_FILTER_HPF;
index = 1;
netdev_for_each_mc_addr(ha, dev) {
uint32_t high, low;
high = (1 << 31) | (ha->addr[5] << 8) | (ha->addr[4]);
low = (ha->addr[3] << 24) | (ha->addr[2] << 16) |
(ha->addr[1] << 8) | (ha->addr[0]);
qfec_reg_write(priv, MAC_ADR_HIGH_REG_N(index), high);
qfec_reg_write(priv, MAC_ADR_LOW_REG_N(index), low);
index++;
}
}
qfec_reg_write(priv, MAC_FR_FILTER_REG, filter_conf);
}
/*
* reset the controller
*/
#define QFEC_RESET_TIMEOUT 10000
/* reset should always clear but did not w/o test/delay
* in RgMii mode. there is no spec'd max timeout
*/
static int qfec_hw_reset(struct qfec_priv *priv)
{
int timeout = QFEC_RESET_TIMEOUT;
QFEC_LOG(QFEC_LOG_DBG, "%s:\n", __func__);
qfec_reg_write(priv, BUS_MODE_REG, BUS_MODE_SWR);
while (qfec_reg_read(priv, BUS_MODE_REG) & BUS_MODE_SWR) {
if (timeout-- == 0) {
QFEC_LOG_ERR("%s: timeout\n", __func__);
return -ETIME;
}
/* there were problems resetting the controller
* in RGMII mode when there wasn't sufficient
* delay between register reads
*/
usleep_range(100, 200);
}
return 0;
}
/*
* initialize controller
*/
static int qfec_hw_init(struct qfec_priv *priv)
{
int res = 0;
QFEC_LOG(QFEC_LOG_DBG, "%s:\n", __func__);
res = qfec_hw_reset(priv);
if (res)
return res;
qfec_reg_init(priv);
/* config buf-desc locations */
qfec_reg_write(priv, TX_DES_LST_ADR_REG, priv->tbd_dma);
qfec_reg_write(priv, RX_DES_LST_ADR_REG, priv->rbd_dma);
/* clear interrupts */
qfec_reg_write(priv, STATUS_REG, INTRP_EN_REG_NIE | INTRP_EN_REG_RIE
| INTRP_EN_REG_TIE | INTRP_EN_REG_TUE | INTRP_EN_REG_ETE);
if (priv->mii.supports_gmii) {
/* Clear RGMII */
qfec_reg_read(priv, SG_RG_SMII_STATUS_REG);
/* Disable RGMII int */
qfec_reg_write(priv, INTRP_MASK_REG, 1);
}
return res;
}
/*
* en/disable controller
*/
static void qfec_hw_enable(struct qfec_priv *priv)
{
QFEC_LOG(QFEC_LOG_DBG, "%s:\n", __func__);
qfec_reg_write(priv, OPER_MODE_REG,
qfec_reg_read(priv, OPER_MODE_REG)
| OPER_MODE_REG_ST | OPER_MODE_REG_SR);
}
static void qfec_hw_disable(struct qfec_priv *priv)
{
QFEC_LOG(QFEC_LOG_DBG, "%s:\n", __func__);
qfec_reg_write(priv, OPER_MODE_REG,
qfec_reg_read(priv, OPER_MODE_REG)
& ~(OPER_MODE_REG_ST | OPER_MODE_REG_SR));
}
/*
* interface selection
*/
struct intf_config {
uint32_t intf_sel;
uint32_t emac_ns;
uint32_t eth_x_en_ns;
uint32_t clkmux_sel;
};
#define ETH_X_EN_NS_REVMII (ETH_X_EN_NS_DEFAULT | ETH_TX_CLK_INV)
#define CLKMUX_REVMII (EMAC_CLKMUX_SEL_0 | EMAC_CLKMUX_SEL_1)
static struct intf_config intf_config_tbl[] = {
{ EMAC_PHY_INTF_SEL_MII, EMAC_NS_DEFAULT, ETH_X_EN_NS_DEFAULT, 0 },
{ EMAC_PHY_INTF_SEL_RGMII, EMAC_NS_DEFAULT, ETH_X_EN_NS_DEFAULT, 0 },
{ EMAC_PHY_INTF_SEL_REVMII, EMAC_NS_DEFAULT, ETH_X_EN_NS_REVMII,
CLKMUX_REVMII }
};
/*
* emac clk register read and write functions
*/
static inline uint32_t qfec_clkreg_read(struct qfec_priv *priv, uint32_t reg)
{
return ioread32((void *) (priv->clk_base + reg));
}
static inline void qfec_clkreg_write(struct qfec_priv *priv,
uint32_t reg, uint32_t val)
{
uint32_t addr = (uint32_t)priv->clk_base + reg;
QFEC_LOG(QFEC_LOG_DBG2, "%s: %08x <- %08x\n", __func__, addr, val);
iowrite32(val, (void *)addr);
}
/*
* configure the PHY interface and clock routing and signal bits
*/
enum phy_intfc {
INTFC_MII = 0,
INTFC_RGMII = 1,
INTFC_REVMII = 2,
};
static int qfec_intf_sel(struct qfec_priv *priv, unsigned int intfc)
{
struct intf_config *p;
QFEC_LOG(QFEC_LOG_DBG2, "%s: %d\n", __func__, intfc);
if (intfc > INTFC_REVMII) {
QFEC_LOG_ERR("%s: range\n", __func__);
return -ENXIO;
}
p = &intf_config_tbl[intfc];
qfec_clkreg_write(priv, EMAC_PHY_INTF_SEL_REG, p->intf_sel);
qfec_clkreg_write(priv, EMAC_NS_REG, p->emac_ns);
qfec_clkreg_write(priv, ETH_X_EN_NS_REG, p->eth_x_en_ns);
qfec_clkreg_write(priv, EMAC_CLKMUX_SEL_REG, p->clkmux_sel);
return 0;
}
/*
* display registers thru proc-fs
*/
static struct qfec_clk_reg {
uint32_t offset;
char *label;
} qfec_clk_regs[] = {
{ ETH_MD_REG, "ETH_MD_REG" },
{ ETH_NS_REG, "ETH_NS_REG" },
{ ETH_X_EN_NS_REG, "ETH_X_EN_NS_REG" },
{ EMAC_PTP_MD_REG, "EMAC_PTP_MD_REG" },
{ EMAC_PTP_NS_REG, "EMAC_PTP_NS_REG" },
{ EMAC_NS_REG, "EMAC_NS_REG" },
{ EMAC_TX_FS_REG, "EMAC_TX_FS_REG" },
{ EMAC_RX_FS_REG, "EMAC_RX_FS_REG" },
{ EMAC_PHY_INTF_SEL_REG, "EMAC_PHY_INTF_SEL_REG" },
{ EMAC_PHY_ADDR_REG, "EMAC_PHY_ADDR_REG" },
{ EMAC_REVMII_PHY_ADDR_REG, "EMAC_REVMII_PHY_ADDR_REG" },
{ EMAC_CLKMUX_SEL_REG, "EMAC_CLKMUX_SEL_REG" },
};
static int qfec_clk_reg_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct qfec_priv *priv = netdev_priv(to_net_dev(dev));
struct qfec_clk_reg *p = qfec_clk_regs;
int n = ARRAY_SIZE(qfec_clk_regs);
int l = 0;
int count = PAGE_SIZE;
QFEC_LOG(QFEC_LOG_DBG2, "%s:\n", __func__);
for (; n--; p++) {
l += snprintf(&buf[l], count - l, " %8p %8x %08x %s\n",
(void *)priv->clk_base + p->offset, p->offset,
qfec_clkreg_read(priv, p->offset), p->label);
}
return l;
}
/*
* speed selection
*/
struct qfec_pll_cfg {
uint32_t spd;
uint32_t eth_md; /* M [31:16], NOT 2*D [15:0] */
uint32_t eth_ns; /* NOT(M-N) [31:16], ctl bits [11:0] */
};
static struct qfec_pll_cfg qfec_pll_cfg_tbl[] = {
/* 2.5 MHz */
{ MAC_CONFIG_REG_SPD_10, ETH_MD_M(1) | ETH_MD_2D_N(100),
ETH_NS_NM(100-1)
| ETH_NS_MCNTR_EN
| ETH_NS_MCNTR_MODE_DUAL
| ETH_NS_PRE_DIV(0)
| CLK_SRC_PLL_EMAC },
/* 25 MHz */
{ MAC_CONFIG_REG_SPD_100, ETH_MD_M(1) | ETH_MD_2D_N(10),
ETH_NS_NM(10-1)
| ETH_NS_MCNTR_EN
| ETH_NS_MCNTR_MODE_DUAL
| ETH_NS_PRE_DIV(0)
| CLK_SRC_PLL_EMAC },
/* 125 MHz */
{MAC_CONFIG_REG_SPD_1G, 0, ETH_NS_PRE_DIV(1)
| CLK_SRC_PLL_EMAC },
};
enum speed {
SPD_10 = 0,
SPD_100 = 1,
SPD_1000 = 2,
};
/*
* configure the PHY interface and clock routing and signal bits
*/
static int qfec_speed_cfg(struct net_device *dev, unsigned int spd,
unsigned int dplx)
{
struct qfec_priv *priv = netdev_priv(dev);
struct qfec_pll_cfg *p;
QFEC_LOG(QFEC_LOG_DBG2, "%s: %d spd, %d dplx\n", __func__, spd, dplx);
if (spd > SPD_1000) {
QFEC_LOG_ERR("%s: range\n", __func__);
return -ENODEV;
}
p = &qfec_pll_cfg_tbl[spd];
/* set the MAC speed bits */
qfec_reg_write(priv, MAC_CONFIG_REG,
(qfec_reg_read(priv, MAC_CONFIG_REG)
& ~(MAC_CONFIG_REG_SPD | MAC_CONFIG_REG_DM))
| p->spd | (dplx ? MAC_CONFIG_REG_DM : H_DPLX));
qfec_clkreg_write(priv, ETH_MD_REG, p->eth_md);
qfec_clkreg_write(priv, ETH_NS_REG, p->eth_ns);
return 0;
}
/*
* configure PTP divider for 25 MHz assuming EMAC PLL 250 MHz
*/
static struct qfec_pll_cfg qfec_pll_ptp = {
/* 19.2 MHz tcxo */
0, 0, ETH_NS_PRE_DIV(0)
| EMAC_PTP_NS_ROOT_EN
| EMAC_PTP_NS_CLK_EN
| CLK_SRC_TCXO
};
#define PLLTEST_PAD_CFG 0x01E0
#define PLLTEST_PLL_7 0x3700
#define CLKTEST_REG 0x01EC
#define CLKTEST_EMAC_RX 0x3fc07f7a
static int qfec_ptp_cfg(struct qfec_priv *priv)
{
struct qfec_pll_cfg *p = &qfec_pll_ptp;
QFEC_LOG(QFEC_LOG_DBG2, "%s: %08x md, %08x ns\n",
__func__, p->eth_md, p->eth_ns);
qfec_clkreg_write(priv, EMAC_PTP_MD_REG, p->eth_md);
qfec_clkreg_write(priv, EMAC_PTP_NS_REG, p->eth_ns);
/* configure HS/LS clk test ports to verify clks */
qfec_clkreg_write(priv, CLKTEST_REG, CLKTEST_EMAC_RX);
qfec_clkreg_write(priv, PLLTEST_PAD_CFG, PLLTEST_PLL_7);
return 0;
}
/*
* MDIO operations
*/
/*
* wait reasonable amount of time for MDIO operation to complete, not busy
*/
static int qfec_mdio_busy(struct net_device *dev)
{
int i;
for (i = 100; i > 0; i--) {
if (!(qfec_reg_read(
netdev_priv(dev), GMII_ADR_REG) & GMII_ADR_REG_GB)) {
return 0;
}
udelay(1);
}
return -ETIME;
}
/*
* initiate either a read or write MDIO operation
*/
static int qfec_mdio_oper(struct net_device *dev, int phy_id, int reg, int wr)
{
struct qfec_priv *priv = netdev_priv(dev);
int res = 0;
/* insure phy not busy */
res = qfec_mdio_busy(dev);
if (res) {
QFEC_LOG_ERR("%s: busy\n", __func__);
goto done;
}
/* initiate operation */
qfec_reg_write(priv, GMII_ADR_REG,
GMII_ADR_REG_ADR_SET(phy_id)
| GMII_ADR_REG_REG_SET(reg)
| GMII_ADR_REG_CSR_SET(priv->mdio_clk)
| (wr ? GMII_ADR_REG_GW : 0)
| GMII_ADR_REG_GB);
/* wait for operation to complete */
res = qfec_mdio_busy(dev);
if (res)
QFEC_LOG_ERR("%s: timeout\n", __func__);
done:
return res;
}
/*
* read MDIO register
*/
static int qfec_mdio_read(struct net_device *dev, int phy_id, int reg)
{
struct qfec_priv *priv = netdev_priv(dev);
int res = 0;
unsigned long flags;
spin_lock_irqsave(&priv->mdio_lock, flags);
res = qfec_mdio_oper(dev, phy_id, reg, 0);
if (res) {
QFEC_LOG_ERR("%s: oper\n", __func__);
goto done;
}
res = qfec_reg_read(priv, GMII_DATA_REG);
QFEC_LOG(QFEC_LOG_MDIO_R, "%s: %2d reg, 0x%04x val\n",
__func__, reg, res);
done:
spin_unlock_irqrestore(&priv->mdio_lock, flags);
return res;
}
/*
* write MDIO register
*/
static void qfec_mdio_write(struct net_device *dev, int phy_id, int reg,
int val)
{
struct qfec_priv *priv = netdev_priv(dev);
unsigned long flags;
spin_lock_irqsave(&priv->mdio_lock, flags);
QFEC_LOG(QFEC_LOG_MDIO_W, "%s: %2d reg, %04x\n",
__func__, reg, val);
qfec_reg_write(priv, GMII_DATA_REG, val);
if (qfec_mdio_oper(dev, phy_id, reg, 1))
QFEC_LOG_ERR("%s: oper\n", __func__);
spin_unlock_irqrestore(&priv->mdio_lock, flags);
}
/*
* MDIO show
*/
static int qfec_mdio_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct qfec_priv *priv = netdev_priv(to_net_dev(dev));
int n;
int l = 0;
int count = PAGE_SIZE;
QFEC_LOG(QFEC_LOG_DBG2, "%s:\n", __func__);
for (n = 0; n < MAX_MDIO_REG; n++) {
if (!(n % 8))
l += snprintf(&buf[l], count - l, "\n %02x: ", n);
l += snprintf(&buf[l], count - l, " %04x",
qfec_mdio_read(to_net_dev(dev), priv->phy_id, n));
}
l += snprintf(&buf[l], count - l, "\n");
return l;
}
/*
* get auto-negotiation results
*/
#define QFEC_100 (LPA_100HALF | LPA_100FULL | LPA_100HALF)
#define QFEC_100_FD (LPA_100FULL | LPA_100BASE4)
#define QFEC_10 (LPA_10HALF | LPA_10FULL)
#define QFEC_10_FD LPA_10FULL
static void qfec_get_an(struct net_device *dev, uint32_t *spd, uint32_t *dplx)
{
struct qfec_priv *priv = netdev_priv(dev);
uint32_t advert = qfec_mdio_read(dev, priv->phy_id, MII_ADVERTISE);
uint32_t lpa = qfec_mdio_read(dev, priv->phy_id, MII_LPA);
uint32_t mastCtrl = qfec_mdio_read(dev, priv->phy_id, MII_CTRL1000);
uint32_t mastStat = qfec_mdio_read(dev, priv->phy_id, MII_STAT1000);
uint32_t anExp = qfec_mdio_read(dev, priv->phy_id, MII_EXPANSION);
uint32_t status = advert & lpa;
uint32_t flow;
if (priv->mii.supports_gmii) {
if (((anExp & QFEC_MII_EXP_MASK) == QFEC_MII_EXP_MASK)
&& (mastCtrl & ADVERTISE_1000FULL)
&& (mastStat & LPA_1000FULL)) {
*spd = SPD_1000;
*dplx = F_DPLX;
goto pause;
}
else if (((anExp & QFEC_MII_EXP_MASK) == QFEC_MII_EXP_MASK)
&& (mastCtrl & ADVERTISE_1000HALF)
&& (mastStat & LPA_1000HALF)) {
*spd = SPD_1000;
*dplx = H_DPLX;
goto pause;
}
}
/* mii speeds */
if (status & QFEC_100) {
*spd = SPD_100;
*dplx = status & QFEC_100_FD ? F_DPLX : H_DPLX;
}
else if (status & QFEC_10) {
*spd = SPD_10;
*dplx = status & QFEC_10_FD ? F_DPLX : H_DPLX;
}
/* check pause */
pause:
flow = qfec_reg_read(priv, FLOW_CONTROL_REG);
flow &= ~(FLOW_CONTROL_TFE | FLOW_CONTROL_RFE);
if (status & ADVERTISE_PAUSE_CAP) {
flow |= FLOW_CONTROL_RFE | FLOW_CONTROL_TFE;
} else if (status & ADVERTISE_PAUSE_ASYM) {
if (lpa & ADVERTISE_PAUSE_CAP)
flow |= FLOW_CONTROL_TFE;
else if (advert & ADVERTISE_PAUSE_CAP)
flow |= FLOW_CONTROL_RFE;
}
qfec_reg_write(priv, FLOW_CONTROL_REG, flow);
}
/*
* monitor phy status, and process auto-neg results when changed
*/
static void qfec_phy_monitor(unsigned long data)
{
struct net_device *dev = (struct net_device *) data;
struct qfec_priv *priv = netdev_priv(dev);
unsigned int spd = H_DPLX;
unsigned int dplx = F_DPLX;
mod_timer(&priv->phy_tmr, jiffies + HZ);
if (mii_link_ok(&priv->mii) && !netif_carrier_ok(priv->net_dev)) {
qfec_get_an(dev, &spd, &dplx);
qfec_speed_cfg(dev, spd, dplx);
QFEC_LOG(QFEC_LOG_DBG, "%s: link up, %d spd, %d dplx\n",
__func__, spd, dplx);
netif_carrier_on(dev);
}
else if (!mii_link_ok(&priv->mii) && netif_carrier_ok(priv->net_dev)) {
QFEC_LOG(QFEC_LOG_DBG, "%s: link down\n", __func__);
netif_carrier_off(dev);
}
}
/*
* dealloc buffer descriptor memory
*/
static void qfec_mem_dealloc(struct net_device *dev)
{
struct qfec_priv *priv = netdev_priv(dev);
dma_free_coherent(&dev->dev,
priv->bd_size, priv->bd_base, priv->tbd_dma);
priv->bd_base = 0;
}
/*
* allocate shared device memory for TX/RX buf-desc (and buffers)
*/
static int qfec_mem_alloc(struct net_device *dev)
{
struct qfec_priv *priv = netdev_priv(dev);
QFEC_LOG(QFEC_LOG_DBG, "%s: %p dev\n", __func__, dev);
priv->bd_size =
(priv->n_tbd + priv->n_rbd) * sizeof(struct qfec_buf_desc);
priv->p_tbd = kcalloc(priv->n_tbd, sizeof(struct buf_desc), GFP_KERNEL);
if (!priv->p_tbd) {
QFEC_LOG_ERR("%s: kcalloc failed p_tbd\n", __func__);
return -ENOMEM;
}
priv->p_rbd = kcalloc(priv->n_rbd, sizeof(struct buf_desc), GFP_KERNEL);
if (!priv->p_rbd) {
QFEC_LOG_ERR("%s: kcalloc failed p_rbd\n", __func__);
return -ENOMEM;
}
/* alloc mem for buf-desc, if not already alloc'd */
if (!priv->bd_base) {
priv->bd_base = dma_alloc_coherent(&dev->dev,
priv->bd_size, &priv->tbd_dma,
GFP_KERNEL | __GFP_DMA);
}
if (!priv->bd_base) {
QFEC_LOG_ERR("%s: dma_alloc_coherent failed\n", __func__);
return -ENOMEM;
}
priv->rbd_dma = priv->tbd_dma
+ (priv->n_tbd * sizeof(struct qfec_buf_desc));
QFEC_LOG(QFEC_LOG_DBG,
" %s: 0x%08x size, %d n_tbd, %d n_rbd\n",
__func__, priv->bd_size, priv->n_tbd, priv->n_rbd);
return 0;
}
/*
* display buffer descriptors
*/
static int qfec_bd_fmt(char *buf, int size, struct buf_desc *p_bd)
{
return snprintf(buf, size,
"%8p: %08x %08x %8p %8p %8p %8p %8p %x",
p_bd, qfec_bd_status_get(p_bd),
qfec_bd_ctl_get(p_bd), qfec_bd_pbuf_get(p_bd),
qfec_bd_next_get(p_bd), qfec_bd_skbuf_get(p_bd),
qfec_bd_virt_get(p_bd), qfec_bd_phys_get(p_bd),
qfec_bd_last_bd(p_bd));
}
static int qfec_bd_show(char *buf, int count, struct buf_desc *p_bd, int n_bd,
struct ring *p_ring, char *label)
{
int l = 0;
int n;
QFEC_LOG(QFEC_LOG_DBG2, "%s: %s\n", __func__, label);
l += snprintf(&buf[l], count, "%s: %s\n", __func__, label);
if (!p_bd)
return l;
n_bd = n_bd > MAX_N_BD ? MAX_N_BD : n_bd;
for (n = 0; n < n_bd; n++, p_bd++) {
l += qfec_bd_fmt(&buf[l], count - l, p_bd);
l += snprintf(&buf[l], count - l, "%s%s\n",
(qfec_ring_head(p_ring) == n ? " < h" : ""),
(qfec_ring_tail(p_ring) == n ? " < t" : ""));
}
return l;
}
/*
* display TX BDs
*/
static int qfec_bd_tx_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct qfec_priv *priv = netdev_priv(to_net_dev(dev));
int count = PAGE_SIZE;
return qfec_bd_show(buf, count, priv->p_tbd, priv->n_tbd,
&priv->ring_tbd, "TX");
}
/*
* display RX BDs
*/
static int qfec_bd_rx_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct qfec_priv *priv = netdev_priv(to_net_dev(dev));
int count = PAGE_SIZE;
return qfec_bd_show(buf, count, priv->p_rbd, priv->n_rbd,
&priv->ring_rbd, "RX");
}
/*
* process timestamp values
* The pbuf and next fields of the buffer descriptors are overwritten
* with the timestamp high and low register values.
*
* The low register is incremented by the value in the subsec_increment
* register and overflows at 0x8000 0000 causing the high register to
* increment.
*
* The subsec_increment register is recommended to be set to the number
* of nanosec corresponding to each clock tic, scaled by 2^31 / 10^9
* (e.g. 40 * 2^32 / 10^9 = 85.9, or 86 for 25 MHz). However, the
* rounding error in this case will result in a 1 sec error / ~14 mins.
*
* An alternate approach is used. The subsec_increment is set to 1,
* and the concatenation of the 2 timestamp registers used to count
* clock tics. The 63-bit result is manipulated to determine the number
* of sec and ns.
*/
/*
* convert 19.2 MHz clock tics into sec/ns
*/
#define TS_LOW_REG_BITS 31
#define MILLION 1000000UL
#define BILLION 1000000000UL
#define F_CLK 19200000UL
#define F_CLK_PRE_SC 24
#define F_CLK_INV_Q 56
#define F_CLK_INV (((unsigned long long)1 << F_CLK_INV_Q) / F_CLK)
#define F_CLK_TO_NS_Q 25
#define F_CLK_TO_NS \
(((((unsigned long long)1<<F_CLK_TO_NS_Q)*BILLION)+(F_CLK-1))/F_CLK)
#define US_TO_F_CLK_Q 20
#define US_TO_F_CLK \
(((((unsigned long long)1<<US_TO_F_CLK_Q)*F_CLK)+(MILLION-1))/MILLION)
static inline void qfec_get_sec(uint64_t *cnt,
uint32_t *sec, uint32_t *ns)
{
unsigned long long t;
unsigned long long subsec;
t = *cnt >> F_CLK_PRE_SC;
t *= F_CLK_INV;
t >>= F_CLK_INV_Q - F_CLK_PRE_SC;
*sec = t;
t = *cnt - (t * F_CLK);
subsec = t;
if (subsec >= F_CLK) {
subsec -= F_CLK;
*sec += 1;
}
subsec *= F_CLK_TO_NS;
subsec >>= F_CLK_TO_NS_Q;
*ns = subsec;
}
/*
* read ethernet timestamp registers, pass up raw register values
* and values converted to sec/ns
*/
static void qfec_read_timestamp(struct buf_desc *p_bd,
struct skb_shared_hwtstamps *ts)
{
unsigned long long cnt;
unsigned int sec;
unsigned int subsec;
cnt = (unsigned long)qfec_bd_next_get(p_bd);
cnt <<= TS_LOW_REG_BITS;
cnt |= (unsigned long)qfec_bd_pbuf_get(p_bd);
/* report raw counts as concatenated 63 bits */
sec = cnt >> 32;
subsec = cnt & 0xffffffff;
ts->hwtstamp = ktime_set(sec, subsec);
/* translate counts to sec and ns */
qfec_get_sec(&cnt, &sec, &subsec);
ts->syststamp = ktime_set(sec, subsec);
}
/*
* capture the current system time in the timestamp registers
*/
static int qfec_cmd(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct qfec_priv *priv = netdev_priv(to_net_dev(dev));
struct timeval tv;
if (!strncmp(buf, "setTs", 5)) {
unsigned long long cnt;
uint32_t ts_hi;
uint32_t ts_lo;
unsigned long long subsec;
do_gettimeofday(&tv);
/* convert raw sec/usec to ns */
subsec = tv.tv_usec;
subsec *= US_TO_F_CLK;
subsec >>= US_TO_F_CLK_Q;
cnt = tv.tv_sec;
cnt *= F_CLK;
cnt += subsec;
ts_hi = cnt >> 31;
ts_lo = cnt & 0x7FFFFFFF;
qfec_reg_write(priv, TS_HI_UPDT_REG, ts_hi);
qfec_reg_write(priv, TS_LO_UPDT_REG, ts_lo);
qfec_reg_write(priv, TS_CTL_REG,
qfec_reg_read(priv, TS_CTL_REG) | TS_CTL_TSINIT);
} else
pr_err("%s: unknown cmd, %s.\n", __func__, buf);
return strnlen(buf, count);
}
/*
* display ethernet tstamp and system time
*/
static int qfec_tstamp_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct qfec_priv *priv = netdev_priv(to_net_dev(dev));
int count = PAGE_SIZE;
int l;
struct timeval tv;
unsigned long long cnt;
uint32_t sec;
uint32_t ns;
uint32_t ts_hi;
uint32_t ts_lo;
/* insure that ts_hi didn't increment during read */
do {
ts_hi = qfec_reg_read(priv, TS_HIGH_REG);
ts_lo = qfec_reg_read(priv, TS_LOW_REG);
} while (ts_hi != qfec_reg_read(priv, TS_HIGH_REG));
cnt = ts_hi;
cnt <<= TS_LOW_REG_BITS;
cnt |= ts_lo;
do_gettimeofday(&tv);
ts_hi = cnt >> 32;
ts_lo = cnt & 0xffffffff;
qfec_get_sec(&cnt, &sec, &ns);
l = snprintf(buf, count,
"%12u.%09u sec 0x%08x 0x%08x tstamp %12u.%06u time-of-day\n",
sec, ns, ts_hi, ts_lo, (int)tv.tv_sec, (int)tv.tv_usec);
return l;
}
/*
* free transmitted skbufs from buffer-descriptor no owned by HW
*/
static int qfec_tx_replenish(struct net_device *dev)
{
struct qfec_priv *priv = netdev_priv(dev);
struct ring *p_ring = &priv->ring_tbd;
struct buf_desc *p_bd = &priv->p_tbd[qfec_ring_tail(p_ring)];
struct sk_buff *skb;
unsigned long flags;
CNTR_INC(priv, tx_replenish);
spin_lock_irqsave(&priv->xmit_lock, flags);
while (!qfec_ring_empty(p_ring)) {
if (qfec_bd_own(p_bd))
break; /* done for now */
skb = qfec_bd_skbuf_get(p_bd);
if (unlikely(skb == NULL)) {
QFEC_LOG_ERR("%s: null sk_buff\n", __func__);
CNTR_INC(priv, tx_skb_null);
break;
}
qfec_reg_write(priv, STATUS_REG,
STATUS_REG_TU | STATUS_REG_TI);
/* retrieve timestamp if requested */
if (qfec_bd_status_get(p_bd) & BUF_TX_TTSS) {
CNTR_INC(priv, ts_tx_rtn);
qfec_read_timestamp(p_bd, skb_hwtstamps(skb));
skb_tstamp_tx(skb, skb_hwtstamps(skb));
}
/* update statistics before freeing skb */
priv->stats.tx_packets++;
priv->stats.tx_bytes += skb->len;
dma_unmap_single(&dev->dev, (dma_addr_t) qfec_bd_pbuf_get(p_bd),
skb->len, DMA_TO_DEVICE);
dev_kfree_skb_any(skb);
qfec_bd_skbuf_set(p_bd, NULL);
qfec_ring_tail_adv(p_ring);
p_bd = &priv->p_tbd[qfec_ring_tail(p_ring)];
}
spin_unlock_irqrestore(&priv->xmit_lock, flags);
qfec_queue_start(dev);
return 0;
}
/*
* clear ownership bits of all TX buf-desc and release the sk-bufs
*/
static void qfec_tx_timeout(struct net_device *dev)
{
struct qfec_priv *priv = netdev_priv(dev);
struct buf_desc *bd = priv->p_tbd;
int n;
QFEC_LOG(QFEC_LOG_DBG, "%s:\n", __func__);
CNTR_INC(priv, tx_timeout);
for (n = 0; n < priv->n_tbd; n++, bd++)
qfec_bd_own_clr(bd);
qfec_tx_replenish(dev);
}
/*
* rx() - process a received frame
*/
static void qfec_rx_int(struct net_device *dev)
{
struct qfec_priv *priv = netdev_priv(dev);
struct ring *p_ring = &priv->ring_rbd;
struct buf_desc *p_bd = priv->p_latest_rbd;
uint32_t desc_status;
uint32_t mis_fr_reg;
desc_status = qfec_bd_status_get(p_bd);
mis_fr_reg = qfec_reg_read(priv, MIS_FR_REG);
CNTR_INC(priv, rx_int);
/* check that valid interrupt occurred */
if (unlikely(desc_status & BUF_OWN))
return;
/* accumulate missed-frame count (reg reset when read) */
priv->stats.rx_missed_errors += mis_fr_reg
& MIS_FR_REG_MISS_CNT;
/* process all unowned frames */
while (!(desc_status & BUF_OWN) && (!qfec_ring_full(p_ring))) {
struct sk_buff *skb;
struct buf_desc *p_bd_next;
skb = qfec_bd_skbuf_get(p_bd);
if (unlikely(skb == NULL)) {
QFEC_LOG_ERR("%s: null sk_buff\n", __func__);
CNTR_INC(priv, rx_skb_null);
break;
}
/* cache coherency before skb->data is accessed */
dma_unmap_single(&dev->dev,
(dma_addr_t) qfec_bd_phys_get(p_bd),
ETH_BUF_SIZE, DMA_FROM_DEVICE);
prefetch(skb->data);
if (unlikely(desc_status & BUF_RX_ES)) {
priv->stats.rx_dropped++;
CNTR_INC(priv, rx_dropped);
dev_kfree_skb(skb);
} else {
qfec_reg_write(priv, STATUS_REG, STATUS_REG_RI);
skb->len = BUF_RX_FL_GET_FROM_STATUS(desc_status);
if (priv->state & timestamping) {
CNTR_INC(priv, ts_rec);
qfec_read_timestamp(p_bd, skb_hwtstamps(skb));
}
/* update statistics before freeing skb */
priv->stats.rx_packets++;
priv->stats.rx_bytes += skb->len;
skb->dev = dev;
skb->protocol = eth_type_trans(skb, dev);
skb->ip_summed = CHECKSUM_UNNECESSARY;
if (NET_RX_DROP == netif_rx(skb)) {
priv->stats.rx_dropped++;
CNTR_INC(priv, rx_dropped);
}
CNTR_INC(priv, netif_rx_cntr);
}
if (p_bd != priv->p_ending_rbd)
p_bd_next = p_bd + 1;
else
p_bd_next = priv->p_rbd;
desc_status = qfec_bd_status_get(p_bd_next);
qfec_bd_skbuf_set(p_bd, NULL);
qfec_ring_head_adv(p_ring);
p_bd = p_bd_next;
}
priv->p_latest_rbd = p_bd;
/* replenish bufs */
while (!qfec_ring_empty(p_ring)) {
if (qfec_rbd_init(dev, &priv->p_rbd[qfec_ring_tail(p_ring)]))
break;
qfec_ring_tail_adv(p_ring);
}
qfec_reg_write(priv, STATUS_REG, STATUS_REG_RI);
}
/*
* isr() - interrupt service routine
* determine cause of interrupt and invoke/schedule appropriate
* processing or error handling
*/
#define ISR_ERR_CHK(priv, status, interrupt, cntr) \
if (status & interrupt) \
CNTR_INC(priv, cntr)
static irqreturn_t qfec_int(int irq, void *dev_id)
{
struct net_device *dev = dev_id;
struct qfec_priv *priv = netdev_priv(dev);
uint32_t status = qfec_reg_read(priv, STATUS_REG);
uint32_t int_bits = STATUS_REG_NIS | STATUS_REG_AIS;
QFEC_LOG(QFEC_LOG_DBG2, "%s: %s\n", __func__, dev->name);
/* abnormal interrupt */
if (status & STATUS_REG_AIS) {
QFEC_LOG(QFEC_LOG_DBG, "%s: abnormal status 0x%08x\n",
__func__, status);
ISR_ERR_CHK(priv, status, STATUS_REG_RU, rx_buf_unavail);
ISR_ERR_CHK(priv, status, STATUS_REG_FBI, fatal_bus);
ISR_ERR_CHK(priv, status, STATUS_REG_RWT, rx_watchdog);
ISR_ERR_CHK(priv, status, STATUS_REG_RPS, rx_proc_stopped);
ISR_ERR_CHK(priv, status, STATUS_REG_UNF, tx_underflow);
ISR_ERR_CHK(priv, status, STATUS_REG_OVF, rx_overflow);
ISR_ERR_CHK(priv, status, STATUS_REG_TJT, tx_jabber_tmout);
ISR_ERR_CHK(priv, status, STATUS_REG_TPS, tx_proc_stopped);
int_bits |= STATUS_REG_AIS_BITS;
CNTR_INC(priv, abnorm_int);
}
if (status & STATUS_REG_NIS)
CNTR_INC(priv, norm_int);
/* receive interrupt */
if (status & STATUS_REG_RI) {
CNTR_INC(priv, rx_isr);
qfec_rx_int(dev);
}
/* transmit interrupt */
if (status & STATUS_REG_TI) {
CNTR_INC(priv, tx_isr);
qfec_tx_replenish(dev);
}
/* gmac interrupt */
if (status & (STATUS_REG_GPI | STATUS_REG_GMI | STATUS_REG_GLI)) {
status &= ~(STATUS_REG_GPI | STATUS_REG_GMI | STATUS_REG_GLI);
CNTR_INC(priv, gmac_isr);
int_bits |= STATUS_REG_GPI | STATUS_REG_GMI | STATUS_REG_GLI;
qfec_reg_read(priv, SG_RG_SMII_STATUS_REG);
}
/* clear interrupts */
qfec_reg_write(priv, STATUS_REG, int_bits);
CNTR_INC(priv, isr);
return IRQ_HANDLED;
}
/*
* open () - register system resources (IRQ, DMA, ...)
* turn on HW, perform device setup.
*/
static int qfec_open(struct net_device *dev)
{
struct qfec_priv *priv = netdev_priv(dev);
struct buf_desc *p_bd;
struct ring *p_ring;
struct qfec_buf_desc *p_desc;
int n;
int res = 0;
QFEC_LOG(QFEC_LOG_DBG, "%s: %p dev\n", __func__, dev);
if (!dev) {
res = -EINVAL;
goto err;
}
/* allocate TX/RX buffer-descriptors and buffers */
res = qfec_mem_alloc(dev);
if (res)
goto err;
/* initialize TX */
p_desc = priv->bd_base;
for (n = 0, p_bd = priv->p_tbd; n < priv->n_tbd; n++, p_bd++) {
p_bd->p_desc = p_desc++;
if (n == (priv->n_tbd - 1))
qfec_bd_last_bd_set(p_bd);
qfec_bd_own_clr(p_bd); /* clear ownership */
}
qfec_ring_init(&priv->ring_tbd, priv->n_tbd, priv->n_tbd);
priv->tx_ic_mod = priv->n_tbd / TX_BD_TI_RATIO;
if (priv->tx_ic_mod == 0)
priv->tx_ic_mod = 1;
/* initialize RX buffer descriptors and allocate sk_bufs */
p_ring = &priv->ring_rbd;
qfec_ring_init(p_ring, priv->n_rbd, 0);
qfec_bd_last_bd_set(&priv->p_rbd[priv->n_rbd - 1]);
for (n = 0, p_bd = priv->p_rbd; n < priv->n_rbd; n++, p_bd++) {
p_bd->p_desc = p_desc++;
if (qfec_rbd_init(dev, p_bd))
break;
qfec_ring_tail_adv(p_ring);
}
priv->p_latest_rbd = priv->p_rbd;
priv->p_ending_rbd = priv->p_rbd + priv->n_rbd - 1;
/* config ptp clock */
qfec_ptp_cfg(priv);
/* configure PHY - must be set before reset/hw_init */
priv->mii.supports_gmii = mii_check_gmii_support(&priv->mii);
if (priv->mii.supports_gmii) {
QFEC_LOG_ERR("%s: RGMII\n", __func__);
qfec_intf_sel(priv, INTFC_RGMII);
} else {
QFEC_LOG_ERR("%s: MII\n", __func__);
qfec_intf_sel(priv, INTFC_MII);
}
/* initialize controller after BDs allocated */
res = qfec_hw_init(priv);
if (res)
goto err1;
/* get/set (primary) MAC address */
qfec_set_adr_regs(priv, dev->dev_addr);
qfec_set_rx_mode(dev);
/* start phy monitor */
QFEC_LOG(QFEC_LOG_DBG, " %s: start timer\n", __func__);
netif_carrier_off(priv->net_dev);
setup_timer(&priv->phy_tmr, qfec_phy_monitor, (unsigned long)dev);
mod_timer(&priv->phy_tmr, jiffies + HZ);
/* driver supports AN capable PHY only */
qfec_mdio_write(dev, priv->phy_id, MII_BMCR, BMCR_RESET);
res = (BMCR_ANENABLE|BMCR_ANRESTART);
qfec_mdio_write(dev, priv->phy_id, MII_BMCR, res);
/* initialize interrupts */
QFEC_LOG(QFEC_LOG_DBG, " %s: request irq %d\n", __func__, dev->irq);
res = request_irq(dev->irq, qfec_int, 0, dev->name, dev);
if (res)
goto err1;
/* enable controller */
qfec_hw_enable(priv);
netif_start_queue(dev);
QFEC_LOG(QFEC_LOG_DBG, "%s: %08x link, %08x carrier\n", __func__,
mii_link_ok(&priv->mii), netif_carrier_ok(priv->net_dev));
QFEC_LOG(QFEC_LOG_DBG, " %s: done\n", __func__);
return 0;
err1:
qfec_mem_dealloc(dev);
err:
QFEC_LOG_ERR("%s: error - %d\n", __func__, res);
return res;
}
/*
* stop() - "reverse operations performed at open time"
*/
static int qfec_stop(struct net_device *dev)
{
struct qfec_priv *priv = netdev_priv(dev);
struct buf_desc *p_bd;
struct sk_buff *skb;
int n;
QFEC_LOG(QFEC_LOG_DBG, "%s:\n", __func__);
del_timer_sync(&priv->phy_tmr);
qfec_hw_disable(priv);
qfec_queue_stop(dev);
free_irq(dev->irq, dev);
/* free all pending sk_bufs */
for (n = priv->n_rbd, p_bd = priv->p_rbd; n > 0; n--, p_bd++) {
skb = qfec_bd_skbuf_get(p_bd);
if (skb)
dev_kfree_skb(skb);
}
for (n = priv->n_tbd, p_bd = priv->p_tbd; n > 0; n--, p_bd++) {
skb = qfec_bd_skbuf_get(p_bd);
if (skb)
dev_kfree_skb(skb);
}
qfec_mem_dealloc(dev);
QFEC_LOG(QFEC_LOG_DBG, " %s: done\n", __func__);
return 0;
}
static int qfec_set_config(struct net_device *dev, struct ifmap *map)
{
QFEC_LOG(QFEC_LOG_DBG, "%s:\n", __func__);
return 0;
}
/*
* pass data from skbuf to buf-desc
*/
static int qfec_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct qfec_priv *priv = netdev_priv(dev);
struct ring *p_ring = &priv->ring_tbd;
struct buf_desc *p_bd;
uint32_t ctrl = 0;
int ret = NETDEV_TX_OK;
unsigned long flags;
CNTR_INC(priv, xmit);
spin_lock_irqsave(&priv->xmit_lock, flags);
/* If there is no room, on the ring try to free some up */
if (qfec_ring_room(p_ring) == 0)
qfec_tx_replenish(dev);
/* stop queuing if no resources available */
if (qfec_ring_room(p_ring) == 0) {
qfec_queue_stop(dev);
CNTR_INC(priv, tx_no_resource);
ret = NETDEV_TX_BUSY;
goto done;
}
/* locate and save *sk_buff */
p_bd = &priv->p_tbd[qfec_ring_head(p_ring)];
qfec_bd_skbuf_set(p_bd, skb);
/* set DMA ptr to sk_buff data and write cache to memory */
qfec_bd_pbuf_set(p_bd, (void *)
dma_map_single(&dev->dev,
(void *)skb->data, skb->len, DMA_TO_DEVICE));
ctrl = skb->len;
if (!(qfec_ring_head(p_ring) % priv->tx_ic_mod))
ctrl |= BUF_TX_IC; /* interrupt on complete */
/* check if timestamping enabled and requested */
if (priv->state & timestamping) {
if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) {
CNTR_INC(priv, ts_tx_en);
ctrl |= BUF_TX_IC; /* interrupt on complete */
ctrl |= BUF_TX_TTSE; /* enable timestamp */
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
}
}
if (qfec_bd_last_bd(p_bd))
ctrl |= BUF_RX_RER;
/* no gather, no multi buf frames */
ctrl |= BUF_TX_FS | BUF_TX_LS; /* 1st and last segment */
qfec_bd_ctl_wr(p_bd, ctrl);
qfec_bd_status_set(p_bd, BUF_OWN);
qfec_ring_head_adv(p_ring);
qfec_reg_write(priv, TX_POLL_DEM_REG, 1); /* poll */
done:
spin_unlock_irqrestore(&priv->xmit_lock, flags);
return ret;
}
static int qfec_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
struct qfec_priv *priv = netdev_priv(dev);
struct hwtstamp_config *cfg = (struct hwtstamp_config *) ifr;
QFEC_LOG(QFEC_LOG_DBG, "%s:\n", __func__);
if (cmd == SIOCSHWTSTAMP) {
CNTR_INC(priv, ts_ioctl);
QFEC_LOG(QFEC_LOG_DBG,
"%s: SIOCSHWTSTAMP - %x flags %x tx %x rx\n",
__func__, cfg->flags, cfg->tx_type, cfg->rx_filter);
cfg->flags = 0;
cfg->tx_type = HWTSTAMP_TX_ON;
cfg->rx_filter = HWTSTAMP_FILTER_ALL;
priv->state |= timestamping;
qfec_reg_write(priv, TS_CTL_REG,
qfec_reg_read(priv, TS_CTL_REG) | TS_CTL_TSENALL);
return 0;
}
return generic_mii_ioctl(&priv->mii, if_mii(ifr), cmd, NULL);
}
static struct net_device_stats *qfec_get_stats(struct net_device *dev)
{
struct qfec_priv *priv = netdev_priv(dev);
QFEC_LOG(QFEC_LOG_DBG2, "qfec_stats:\n");
priv->stats.multicast = qfec_reg_read(priv, NUM_MULTCST_FRM_RCVD_G);
return &priv->stats;
}
/*
* accept new mac address
*/
static int qfec_set_mac_address(struct net_device *dev, void *p)
{
struct qfec_priv *priv = netdev_priv(dev);
struct sockaddr *addr = p;
QFEC_LOG(QFEC_LOG_DBG, "%s:\n", __func__);
memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
qfec_set_adr_regs(priv, dev->dev_addr);
return 0;
}
/*
* read discontinuous MAC address from corrected fuse memory region
*/
static int qfec_get_mac_address(char *buf, char *mac_base, int nBytes)
{
static int offset[] = { 0, 1, 2, 3, 4, 8 };
int n;
QFEC_LOG(QFEC_LOG_DBG, "%s:\n", __func__);
for (n = 0; n < nBytes; n++)
buf[n] = ioread8(mac_base + offset[n]);
/* check that MAC programmed */
if ((buf[0] + buf[1] + buf[2] + buf[3] + buf[4] + buf[5]) == 0) {
QFEC_LOG_ERR("%s: null MAC address\n", __func__);
return -ENODATA;
}
return 0;
}
/*
* static definition of driver functions
*/
static const struct net_device_ops qfec_netdev_ops = {
.ndo_open = qfec_open,
.ndo_stop = qfec_stop,
.ndo_start_xmit = qfec_xmit,
.ndo_do_ioctl = qfec_do_ioctl,
.ndo_tx_timeout = qfec_tx_timeout,
.ndo_set_mac_address = qfec_set_mac_address,
.ndo_set_rx_mode = qfec_set_rx_mode,
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
.ndo_get_stats = qfec_get_stats,
.ndo_set_config = qfec_set_config,
};
/*
* ethtool functions
*/
static int qfec_nway_reset(struct net_device *dev)
{
struct qfec_priv *priv = netdev_priv(dev);
return mii_nway_restart(&priv->mii);
}
/*
* speed, duplex, auto-neg settings
*/
static void qfec_ethtool_getpauseparam(struct net_device *dev,
struct ethtool_pauseparam *pp)
{
struct qfec_priv *priv = netdev_priv(dev);
u32 flow = qfec_reg_read(priv, FLOW_CONTROL_REG);
u32 advert;
QFEC_LOG(QFEC_LOG_DBG, "%s:\n", __func__);
/* report current settings */
pp->tx_pause = (flow & FLOW_CONTROL_TFE) != 0;
pp->rx_pause = (flow & FLOW_CONTROL_RFE) != 0;
/* report if pause is being advertised */
advert = qfec_mdio_read(dev, priv->phy_id, MII_ADVERTISE);
pp->autoneg =
(advert & (ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM)) != 0;
}
static int qfec_ethtool_setpauseparam(struct net_device *dev,
struct ethtool_pauseparam *pp)
{
struct qfec_priv *priv = netdev_priv(dev);
u32 advert;
QFEC_LOG(QFEC_LOG_DBG, "%s: %d aneg, %d rx, %d tx\n", __func__,
pp->autoneg, pp->rx_pause, pp->tx_pause);
advert = qfec_mdio_read(dev, priv->phy_id, MII_ADVERTISE);
advert &= ~(ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
/* If pause autonegotiation is enabled, but both rx and tx are not
* because neither was specified in the ethtool cmd,
* enable both symetrical and asymetrical pause.
* otherwise, only enable the pause mode indicated by rx/tx.
*/
if (pp->autoneg) {
if (pp->rx_pause)
advert |= ADVERTISE_PAUSE_ASYM | ADVERTISE_PAUSE_CAP;
else if (pp->tx_pause)
advert |= ADVERTISE_PAUSE_ASYM;
else
advert |= ADVERTISE_PAUSE_CAP;
}
qfec_mdio_write(dev, priv->phy_id, MII_ADVERTISE, advert);
return 0;
}
/*
* ethtool ring parameter (-g/G) support
*/
/*
* setringparamam - change the tx/rx ring lengths
*/
#define MIN_RING_SIZE 3
#define MAX_RING_SIZE 1000
static int qfec_ethtool_setringparam(struct net_device *dev,
struct ethtool_ringparam *ring)
{
struct qfec_priv *priv = netdev_priv(dev);
u32 timeout = 20;
/* notify stack the link is down */
netif_carrier_off(dev);
/* allow tx to complete & free skbufs on the tx ring */
do {
usleep_range(10000, 100000);
qfec_tx_replenish(dev);
if (timeout-- == 0) {
QFEC_LOG_ERR("%s: timeout\n", __func__);
return -ETIME;
}
} while (!qfec_ring_empty(&priv->ring_tbd));
qfec_stop(dev);
/* set tx ring size */
if (ring->tx_pending < MIN_RING_SIZE)
ring->tx_pending = MIN_RING_SIZE;
else if (ring->tx_pending > MAX_RING_SIZE)
ring->tx_pending = MAX_RING_SIZE;
priv->n_tbd = ring->tx_pending;
/* set rx ring size */
if (ring->rx_pending < MIN_RING_SIZE)
ring->rx_pending = MIN_RING_SIZE;
else if (ring->rx_pending > MAX_RING_SIZE)
ring->rx_pending = MAX_RING_SIZE;
priv->n_rbd = ring->rx_pending;
qfec_open(dev);
return 0;
}
/*
* getringparamam - returns local values
*/
static void qfec_ethtool_getringparam(struct net_device *dev,
struct ethtool_ringparam *ring)
{
struct qfec_priv *priv = netdev_priv(dev);
QFEC_LOG(QFEC_LOG_DBG, "%s:\n", __func__);
ring->rx_max_pending = MAX_RING_SIZE;
ring->rx_mini_max_pending = 0;
ring->rx_jumbo_max_pending = 0;
ring->tx_max_pending = MAX_RING_SIZE;
ring->rx_pending = priv->n_rbd;
ring->rx_mini_pending = 0;
ring->rx_jumbo_pending = 0;
ring->tx_pending = priv->n_tbd;
}
/*
* speed, duplex, auto-neg settings
*/
static int
qfec_ethtool_getsettings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct qfec_priv *priv = netdev_priv(dev);
QFEC_LOG(QFEC_LOG_DBG, "%s:\n", __func__);
cmd->maxrxpkt = priv->n_rbd;
cmd->maxtxpkt = priv->n_tbd;
return mii_ethtool_gset(&priv->mii, cmd);
}
static int
qfec_ethtool_setsettings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct qfec_priv *priv = netdev_priv(dev);
QFEC_LOG(QFEC_LOG_DBG, "%s:\n", __func__);
return mii_ethtool_sset(&priv->mii, cmd);
}
/*
* msg/debug level
*/
static u32 qfec_ethtool_getmsglevel(struct net_device *dev)
{
return qfec_debug;
}
static void qfec_ethtool_setmsglevel(struct net_device *dev, u32 level)
{
qfec_debug ^= level; /* toggle on/off */
}
/*
* register dump
*/
#define DMA_DMP_OFFSET 0x0000
#define DMA_REG_OFFSET 0x1000
#define DMA_REG_LEN 23
#define MAC_DMP_OFFSET 0x0080
#define MAC_REG_OFFSET 0x0000
#define MAC_REG_LEN 55
#define TS_DMP_OFFSET 0x0180
#define TS_REG_OFFSET 0x0700
#define TS_REG_LEN 15
#define MDIO_DMP_OFFSET 0x0200
#define MDIO_REG_LEN 16
#define REG_SIZE (MDIO_DMP_OFFSET + (MDIO_REG_LEN * sizeof(short)))
static int qfec_ethtool_getregs_len(struct net_device *dev)
{
return REG_SIZE;
}
static void
qfec_ethtool_getregs(struct net_device *dev, struct ethtool_regs *regs,
void *buf)
{
struct qfec_priv *priv = netdev_priv(dev);
u32 *data = buf;
u16 *data16;
unsigned int i;
unsigned int j;
unsigned int n;
memset(buf, 0, REG_SIZE);
j = DMA_DMP_OFFSET / sizeof(u32);
for (i = DMA_REG_OFFSET, n = DMA_REG_LEN; n--; i += sizeof(u32))
data[j++] = htonl(qfec_reg_read(priv, i));
j = MAC_DMP_OFFSET / sizeof(u32);
for (i = MAC_REG_OFFSET, n = MAC_REG_LEN; n--; i += sizeof(u32))
data[j++] = htonl(qfec_reg_read(priv, i));
j = TS_DMP_OFFSET / sizeof(u32);
for (i = TS_REG_OFFSET, n = TS_REG_LEN; n--; i += sizeof(u32))
data[j++] = htonl(qfec_reg_read(priv, i));
data16 = (u16 *)&data[MDIO_DMP_OFFSET / sizeof(u32)];
for (i = 0, n = 0; i < MDIO_REG_LEN; i++)
data16[n++] = htons(qfec_mdio_read(dev, 0, i));
regs->len = REG_SIZE;
QFEC_LOG(QFEC_LOG_DBG, "%s: %d bytes\n", __func__, regs->len);
}
/*
* statistics
* return counts of various ethernet activity.
* many of these are same as in struct net_device_stats
*
* missed-frames indicates the number of attempts made by the ethernet
* controller to write to a buffer-descriptor when the BD ownership
* bit was not set. The rxfifooverflow counter (0x1D4) is not
* available. The Missed Frame and Buffer Overflow Counter register
* (0x1020) is used, but has only 16-bits and is reset when read.
* It is read and updates the value in priv->stats.rx_missed_errors
* in qfec_rx_int().
*/
static char qfec_stats_strings[][ETH_GSTRING_LEN] = {
"TX good/bad Bytes ",
"TX Bytes ",
"TX good/bad Frames ",
"TX Bcast Frames ",
"TX Mcast Frames ",
"TX Unicast Frames ",
"TX Pause Frames ",
"TX Vlan Frames ",
"TX Frames 64 ",
"TX Frames 65-127 ",
"TX Frames 128-255 ",
"TX Frames 256-511 ",
"TX Frames 512-1023 ",
"TX Frames 1024+ ",
"TX Pause Frames ",
"TX Collisions ",
"TX Late Collisions ",
"TX Excessive Collisions ",
"RX good/bad Bytes ",
"RX Bytes ",
"RX good/bad Frames ",
"RX Bcast Frames ",
"RX Mcast Frames ",
"RX Unicast Frames ",
"RX Pause Frames ",
"RX Vlan Frames ",
"RX Frames 64 ",
"RX Frames 65-127 ",
"RX Frames 128-255 ",
"RX Frames 256-511 ",
"RX Frames 512-1023 ",
"RX Frames 1024+ ",
"RX Pause Frames ",
"RX Crc error Frames ",
"RX Length error Frames ",
"RX Alignment error Frames ",
"RX Runt Frames ",
"RX Oversize Frames ",
"RX Missed Frames ",
};
static u32 qfec_stats_regs[] = {
69, 89, 70, 71, 72, 90, 92, 93,
73, 74, 75, 76, 77, 78, 92, 84,
86, 87,
97, 98, 96, 99, 100, 113, 116, 118,
107, 108, 109, 110, 111, 112, 116, 101,
114, 102, 103, 106
};
static int qfec_stats_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct qfec_priv *priv = netdev_priv(to_net_dev(dev));
int count = PAGE_SIZE;
int l = 0;
int n;
QFEC_LOG(QFEC_LOG_DBG2, "%s:\n", __func__);
for (n = 0; n < ARRAY_SIZE(qfec_stats_regs); n++) {
l += snprintf(&buf[l], count - l, " %12u %s\n",
qfec_reg_read(priv,
qfec_stats_regs[n] * sizeof(uint32_t)),
qfec_stats_strings[n]);
}
return l;
}
static int qfec_get_sset_count(struct net_device *dev, int sset)
{
switch (sset) {
case ETH_SS_STATS:
return ARRAY_SIZE(qfec_stats_regs) + 1; /* missed frames */
default:
return -EOPNOTSUPP;
}
}
static void qfec_ethtool_getstrings(struct net_device *dev, u32 stringset,
u8 *buf)
{
QFEC_LOG(QFEC_LOG_DBG, "%s: %d bytes\n", __func__,
sizeof(qfec_stats_strings));
memcpy(buf, qfec_stats_strings, sizeof(qfec_stats_strings));
}
static void qfec_ethtool_getstats(struct net_device *dev,
struct ethtool_stats *stats, uint64_t *data)
{
struct qfec_priv *priv = netdev_priv(dev);
int j = 0;
int n;
for (n = 0; n < ARRAY_SIZE(qfec_stats_regs); n++)
data[j++] = qfec_reg_read(priv,
qfec_stats_regs[n] * sizeof(uint32_t));
data[j++] = priv->stats.rx_missed_errors;
stats->n_stats = j;
}
static void qfec_ethtool_getdrvinfo(struct net_device *dev,
struct ethtool_drvinfo *info)
{
strlcpy(info->driver, QFEC_NAME, sizeof(info->driver));
strlcpy(info->version, QFEC_DRV_VER, sizeof(info->version));
strlcpy(info->bus_info, dev_name(dev->dev.parent),
sizeof(info->bus_info));
info->eedump_len = 0;
info->regdump_len = qfec_ethtool_getregs_len(dev);
}
/*
* ethtool ops table
*/
static const struct ethtool_ops qfec_ethtool_ops = {
.nway_reset = qfec_nway_reset,
.get_settings = qfec_ethtool_getsettings,
.set_settings = qfec_ethtool_setsettings,
.get_link = ethtool_op_get_link,
.get_drvinfo = qfec_ethtool_getdrvinfo,
.get_msglevel = qfec_ethtool_getmsglevel,
.set_msglevel = qfec_ethtool_setmsglevel,
.get_regs_len = qfec_ethtool_getregs_len,
.get_regs = qfec_ethtool_getregs,
.get_ringparam = qfec_ethtool_getringparam,
.set_ringparam = qfec_ethtool_setringparam,
.get_pauseparam = qfec_ethtool_getpauseparam,
.set_pauseparam = qfec_ethtool_setpauseparam,
.get_sset_count = qfec_get_sset_count,
.get_strings = qfec_ethtool_getstrings,
.get_ethtool_stats = qfec_ethtool_getstats,
};
/*
* create sysfs entries
*/
static DEVICE_ATTR(bd_tx, 0444, qfec_bd_tx_show, NULL);
static DEVICE_ATTR(bd_rx, 0444, qfec_bd_rx_show, NULL);
static DEVICE_ATTR(cfg, 0444, qfec_config_show, NULL);
static DEVICE_ATTR(clk_reg, 0444, qfec_clk_reg_show, NULL);
static DEVICE_ATTR(cmd, 0222, NULL, qfec_cmd);
static DEVICE_ATTR(cntrs, 0444, qfec_cntrs_show, NULL);
static DEVICE_ATTR(reg, 0444, qfec_reg_show, NULL);
static DEVICE_ATTR(mdio, 0444, qfec_mdio_show, NULL);
static DEVICE_ATTR(stats, 0444, qfec_stats_show, NULL);
static DEVICE_ATTR(tstamp, 0444, qfec_tstamp_show, NULL);
static void qfec_sysfs_create(struct net_device *dev)
{
if (device_create_file(&(dev->dev), &dev_attr_bd_tx) ||
device_create_file(&(dev->dev), &dev_attr_bd_rx) ||
device_create_file(&(dev->dev), &dev_attr_cfg) ||
device_create_file(&(dev->dev), &dev_attr_clk_reg) ||
device_create_file(&(dev->dev), &dev_attr_cmd) ||
device_create_file(&(dev->dev), &dev_attr_cntrs) ||
device_create_file(&(dev->dev), &dev_attr_mdio) ||
device_create_file(&(dev->dev), &dev_attr_reg) ||
device_create_file(&(dev->dev), &dev_attr_stats) ||
device_create_file(&(dev->dev), &dev_attr_tstamp))
pr_err("qfec_sysfs_create failed to create sysfs files\n");
}
/*
* map a specified resource
*/
static int qfec_map_resource(struct platform_device *plat, int resource,
struct resource **priv_res,
void **addr)
{
struct resource *res;
QFEC_LOG(QFEC_LOG_DBG, "%s: 0x%x resource\n", __func__, resource);
/* allocate region to access controller registers */
*priv_res = res = platform_get_resource(plat, resource, 0);
if (!res) {
QFEC_LOG_ERR("%s: platform_get_resource failed\n", __func__);
return -ENODEV;
}
res = request_mem_region(res->start, res->end - res->start, QFEC_NAME);
if (!res) {
QFEC_LOG_ERR("%s: request_mem_region failed, %08x %08x\n",
__func__, res->start, res->end - res->start);
return -EBUSY;
}
*addr = ioremap(res->start, res->end - res->start);
if (!*addr)
return -ENOMEM;
QFEC_LOG(QFEC_LOG_DBG, " %s: io mapped from %p to %p\n",
__func__, (void *)res->start, *addr);
return 0;
};
/*
* free allocated io regions
*/
static void qfec_free_res(struct resource *res, void *base)
{
if (res) {
if (base)
iounmap((void __iomem *)base);
release_mem_region(res->start, res->end - res->start);
}
};
/*
* probe function that obtain configuration info and allocate net_device
*/
static int __devinit qfec_probe(struct platform_device *plat)
{
struct net_device *dev;
struct qfec_priv *priv;
int ret = 0;
/* allocate device */
dev = alloc_etherdev(sizeof(struct qfec_priv));
if (!dev) {
QFEC_LOG_ERR("%s: alloc_etherdev failed\n", __func__);
ret = -ENOMEM;
goto err;
}
QFEC_LOG(QFEC_LOG_DBG, "%s: %08x dev\n", __func__, (int)dev);
qfec_dev = dev;
SET_NETDEV_DEV(dev, &plat->dev);
dev->netdev_ops = &qfec_netdev_ops;
dev->ethtool_ops = &qfec_ethtool_ops;
dev->watchdog_timeo = 2 * HZ;
dev->irq = platform_get_irq(plat, 0);
dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
/* initialize private data */
priv = (struct qfec_priv *)netdev_priv(dev);
memset((void *)priv, 0, sizeof(priv));
priv->net_dev = dev;
platform_set_drvdata(plat, dev);
priv->n_tbd = TX_BD_NUM;
priv->n_rbd = RX_BD_NUM;
/* initialize phy structure */
priv->mii.phy_id_mask = 0x1F;
priv->mii.reg_num_mask = 0x1F;
priv->mii.dev = dev;
priv->mii.mdio_read = qfec_mdio_read;
priv->mii.mdio_write = qfec_mdio_write;
/* map register regions */
ret = qfec_map_resource(
plat, IORESOURCE_MEM, &priv->mac_res, &priv->mac_base);
if (ret) {
QFEC_LOG_ERR("%s: IORESOURCE_MEM mac failed\n", __func__);
goto err1;
}
ret = qfec_map_resource(
plat, IORESOURCE_IO, &priv->clk_res, &priv->clk_base);
if (ret) {
QFEC_LOG_ERR("%s: IORESOURCE_IO clk failed\n", __func__);
goto err2;
}
ret = qfec_map_resource(
plat, IORESOURCE_DMA, &priv->fuse_res, &priv->fuse_base);
if (ret) {
QFEC_LOG_ERR("%s: IORESOURCE_DMA fuse failed\n", __func__);
goto err3;
}
/* initialize MAC addr */
ret = qfec_get_mac_address(dev->dev_addr, priv->fuse_base,
MAC_ADDR_SIZE);
if (ret)
goto err4;
QFEC_LOG(QFEC_LOG_DBG, "%s: mac %02x:%02x:%02x:%02x:%02x:%02x\n",
__func__,
dev->dev_addr[0], dev->dev_addr[1],
dev->dev_addr[2], dev->dev_addr[3],
dev->dev_addr[4], dev->dev_addr[5]);
ret = register_netdev(dev);
if (ret) {
QFEC_LOG_ERR("%s: register_netdev failed\n", __func__);
goto err4;
}
spin_lock_init(&priv->mdio_lock);
spin_lock_init(&priv->xmit_lock);
qfec_sysfs_create(dev);
return 0;
/* error handling */
err4:
qfec_free_res(priv->fuse_res, priv->fuse_base);
err3:
qfec_free_res(priv->clk_res, priv->clk_base);
err2:
qfec_free_res(priv->mac_res, priv->mac_base);
err1:
free_netdev(dev);
err:
QFEC_LOG_ERR("%s: err\n", __func__);
return ret;
}
/*
* module remove
*/
static int __devexit qfec_remove(struct platform_device *plat)
{
struct net_device *dev = platform_get_drvdata(plat);
struct qfec_priv *priv = netdev_priv(dev);
QFEC_LOG(QFEC_LOG_DBG, "%s:\n", __func__);
platform_set_drvdata(plat, NULL);
qfec_free_res(priv->fuse_res, priv->fuse_base);
qfec_free_res(priv->clk_res, priv->clk_base);
qfec_free_res(priv->mac_res, priv->mac_base);
unregister_netdev(dev);
free_netdev(dev);
return 0;
}
/*
* module support
* the FSM9xxx is not a mobile device does not support power management
*/
static struct platform_driver qfec_driver = {
.probe = qfec_probe,
.remove = __devexit_p(qfec_remove),
.driver = {
.name = QFEC_NAME,
.owner = THIS_MODULE,
},
};
/*
* module init
*/
static int __init qfec_init_module(void)
{
int res;
QFEC_LOG(QFEC_LOG_DBG, "%s: %s\n", __func__, qfec_driver.driver.name);
res = platform_driver_register(&qfec_driver);
QFEC_LOG(QFEC_LOG_DBG, "%s: %d - platform_driver_register\n",
__func__, res);
return res;
}
/*
* module exit
*/
static void __exit qfec_exit_module(void)
{
QFEC_LOG(QFEC_LOG_DBG, "%s:\n", __func__);
platform_driver_unregister(&qfec_driver);
}
MODULE_DESCRIPTION("FSM Network Driver");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Rohit Vaswani <rvaswani@codeaurora.org>");
MODULE_VERSION("1.0");
module_init(qfec_init_module);
module_exit(qfec_exit_module);