linux/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
Iyappan Subramanian e3978673f5 drivers: net: xgene: Fix MSS programming
Current driver programs static value of MSS in hardware register for TSO
offload engine to segment the TCP payload regardless the MSS value
provided by network stack.

This patch fixes this by programming hardware registers with the
stack provided MSS value.

Since the hardware has the limitation of having only 4 MSS registers,
this patch uses reference count of mss values being used.

Signed-off-by: Iyappan Subramanian <isubramanian@apm.com>
Signed-off-by: Toan Le <toanle@apm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-09-23 08:38:38 -04:00

1865 lines
45 KiB
C

/* Applied Micro X-Gene SoC Ethernet Driver
*
* Copyright (c) 2014, Applied Micro Circuits Corporation
* Authors: Iyappan Subramanian <isubramanian@apm.com>
* Ravi Patel <rapatel@apm.com>
* Keyur Chudgar <kchudgar@apm.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/gpio.h>
#include "xgene_enet_main.h"
#include "xgene_enet_hw.h"
#include "xgene_enet_sgmac.h"
#include "xgene_enet_xgmac.h"
#define RES_ENET_CSR 0
#define RES_RING_CSR 1
#define RES_RING_CMD 2
static const struct of_device_id xgene_enet_of_match[];
static const struct acpi_device_id xgene_enet_acpi_match[];
static void xgene_enet_init_bufpool(struct xgene_enet_desc_ring *buf_pool)
{
struct xgene_enet_raw_desc16 *raw_desc;
int i;
for (i = 0; i < buf_pool->slots; i++) {
raw_desc = &buf_pool->raw_desc16[i];
/* Hardware expects descriptor in little endian format */
raw_desc->m0 = cpu_to_le64(i |
SET_VAL(FPQNUM, buf_pool->dst_ring_num) |
SET_VAL(STASH, 3));
}
}
static int xgene_enet_refill_bufpool(struct xgene_enet_desc_ring *buf_pool,
u32 nbuf)
{
struct sk_buff *skb;
struct xgene_enet_raw_desc16 *raw_desc;
struct xgene_enet_pdata *pdata;
struct net_device *ndev;
struct device *dev;
dma_addr_t dma_addr;
u32 tail = buf_pool->tail;
u32 slots = buf_pool->slots - 1;
u16 bufdatalen, len;
int i;
ndev = buf_pool->ndev;
dev = ndev_to_dev(buf_pool->ndev);
pdata = netdev_priv(ndev);
bufdatalen = BUF_LEN_CODE_2K | (SKB_BUFFER_SIZE & GENMASK(11, 0));
len = XGENE_ENET_MAX_MTU;
for (i = 0; i < nbuf; i++) {
raw_desc = &buf_pool->raw_desc16[tail];
skb = netdev_alloc_skb_ip_align(ndev, len);
if (unlikely(!skb))
return -ENOMEM;
dma_addr = dma_map_single(dev, skb->data, len, DMA_FROM_DEVICE);
if (dma_mapping_error(dev, dma_addr)) {
netdev_err(ndev, "DMA mapping error\n");
dev_kfree_skb_any(skb);
return -EINVAL;
}
buf_pool->rx_skb[tail] = skb;
raw_desc->m1 = cpu_to_le64(SET_VAL(DATAADDR, dma_addr) |
SET_VAL(BUFDATALEN, bufdatalen) |
SET_BIT(COHERENT));
tail = (tail + 1) & slots;
}
pdata->ring_ops->wr_cmd(buf_pool, nbuf);
buf_pool->tail = tail;
return 0;
}
static u8 xgene_enet_hdr_len(const void *data)
{
const struct ethhdr *eth = data;
return (eth->h_proto == htons(ETH_P_8021Q)) ? VLAN_ETH_HLEN : ETH_HLEN;
}
static void xgene_enet_delete_bufpool(struct xgene_enet_desc_ring *buf_pool)
{
struct device *dev = ndev_to_dev(buf_pool->ndev);
struct xgene_enet_raw_desc16 *raw_desc;
dma_addr_t dma_addr;
int i;
/* Free up the buffers held by hardware */
for (i = 0; i < buf_pool->slots; i++) {
if (buf_pool->rx_skb[i]) {
dev_kfree_skb_any(buf_pool->rx_skb[i]);
raw_desc = &buf_pool->raw_desc16[i];
dma_addr = GET_VAL(DATAADDR, le64_to_cpu(raw_desc->m1));
dma_unmap_single(dev, dma_addr, XGENE_ENET_MAX_MTU,
DMA_FROM_DEVICE);
}
}
}
static irqreturn_t xgene_enet_rx_irq(const int irq, void *data)
{
struct xgene_enet_desc_ring *rx_ring = data;
if (napi_schedule_prep(&rx_ring->napi)) {
disable_irq_nosync(irq);
__napi_schedule(&rx_ring->napi);
}
return IRQ_HANDLED;
}
static int xgene_enet_tx_completion(struct xgene_enet_desc_ring *cp_ring,
struct xgene_enet_raw_desc *raw_desc)
{
struct xgene_enet_pdata *pdata = netdev_priv(cp_ring->ndev);
struct sk_buff *skb;
struct device *dev;
skb_frag_t *frag;
dma_addr_t *frag_dma_addr;
u16 skb_index;
u8 status;
int i, ret = 0;
u8 mss_index;
skb_index = GET_VAL(USERINFO, le64_to_cpu(raw_desc->m0));
skb = cp_ring->cp_skb[skb_index];
frag_dma_addr = &cp_ring->frag_dma_addr[skb_index * MAX_SKB_FRAGS];
dev = ndev_to_dev(cp_ring->ndev);
dma_unmap_single(dev, GET_VAL(DATAADDR, le64_to_cpu(raw_desc->m1)),
skb_headlen(skb),
DMA_TO_DEVICE);
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
frag = &skb_shinfo(skb)->frags[i];
dma_unmap_page(dev, frag_dma_addr[i], skb_frag_size(frag),
DMA_TO_DEVICE);
}
if (GET_BIT(ET, le64_to_cpu(raw_desc->m3))) {
mss_index = GET_VAL(MSS, le64_to_cpu(raw_desc->m3));
spin_lock(&pdata->mss_lock);
pdata->mss_refcnt[mss_index]--;
spin_unlock(&pdata->mss_lock);
}
/* Checking for error */
status = GET_VAL(LERR, le64_to_cpu(raw_desc->m0));
if (unlikely(status > 2)) {
xgene_enet_parse_error(cp_ring, netdev_priv(cp_ring->ndev),
status);
ret = -EIO;
}
if (likely(skb)) {
dev_kfree_skb_any(skb);
} else {
netdev_err(cp_ring->ndev, "completion skb is NULL\n");
ret = -EIO;
}
return ret;
}
static int xgene_enet_setup_mss(struct net_device *ndev, u32 mss)
{
struct xgene_enet_pdata *pdata = netdev_priv(ndev);
bool mss_index_found = false;
int mss_index;
int i;
spin_lock(&pdata->mss_lock);
/* Reuse the slot if MSS matches */
for (i = 0; !mss_index_found && i < NUM_MSS_REG; i++) {
if (pdata->mss[i] == mss) {
pdata->mss_refcnt[i]++;
mss_index = i;
mss_index_found = true;
}
}
/* Overwrite the slot with ref_count = 0 */
for (i = 0; !mss_index_found && i < NUM_MSS_REG; i++) {
if (!pdata->mss_refcnt[i]) {
pdata->mss_refcnt[i]++;
pdata->mac_ops->set_mss(pdata, mss, i);
pdata->mss[i] = mss;
mss_index = i;
mss_index_found = true;
}
}
spin_unlock(&pdata->mss_lock);
/* No slots with ref_count = 0 available, return busy */
if (!mss_index_found)
return -EBUSY;
return mss_index;
}
static int xgene_enet_work_msg(struct sk_buff *skb, u64 *hopinfo)
{
struct net_device *ndev = skb->dev;
struct iphdr *iph;
u8 l3hlen = 0, l4hlen = 0;
u8 ethhdr, proto = 0, csum_enable = 0;
u32 hdr_len, mss = 0;
u32 i, len, nr_frags;
int mss_index;
ethhdr = xgene_enet_hdr_len(skb->data);
if (unlikely(skb->protocol != htons(ETH_P_IP)) &&
unlikely(skb->protocol != htons(ETH_P_8021Q)))
goto out;
if (unlikely(!(skb->dev->features & NETIF_F_IP_CSUM)))
goto out;
iph = ip_hdr(skb);
if (unlikely(ip_is_fragment(iph)))
goto out;
if (likely(iph->protocol == IPPROTO_TCP)) {
l4hlen = tcp_hdrlen(skb) >> 2;
csum_enable = 1;
proto = TSO_IPPROTO_TCP;
if (ndev->features & NETIF_F_TSO) {
hdr_len = ethhdr + ip_hdrlen(skb) + tcp_hdrlen(skb);
mss = skb_shinfo(skb)->gso_size;
if (skb_is_nonlinear(skb)) {
len = skb_headlen(skb);
nr_frags = skb_shinfo(skb)->nr_frags;
for (i = 0; i < 2 && i < nr_frags; i++)
len += skb_shinfo(skb)->frags[i].size;
/* HW requires header must reside in 3 buffer */
if (unlikely(hdr_len > len)) {
if (skb_linearize(skb))
return 0;
}
}
if (!mss || ((skb->len - hdr_len) <= mss))
goto out;
mss_index = xgene_enet_setup_mss(ndev, mss);
if (unlikely(mss_index < 0))
return -EBUSY;
*hopinfo |= SET_BIT(ET) | SET_VAL(MSS, mss_index);
}
} else if (iph->protocol == IPPROTO_UDP) {
l4hlen = UDP_HDR_SIZE;
csum_enable = 1;
}
out:
l3hlen = ip_hdrlen(skb) >> 2;
*hopinfo |= SET_VAL(TCPHDR, l4hlen) |
SET_VAL(IPHDR, l3hlen) |
SET_VAL(ETHHDR, ethhdr) |
SET_VAL(EC, csum_enable) |
SET_VAL(IS, proto) |
SET_BIT(IC) |
SET_BIT(TYPE_ETH_WORK_MESSAGE);
return 0;
}
static u16 xgene_enet_encode_len(u16 len)
{
return (len == BUFLEN_16K) ? 0 : len;
}
static void xgene_set_addr_len(__le64 *desc, u32 idx, dma_addr_t addr, u32 len)
{
desc[idx ^ 1] = cpu_to_le64(SET_VAL(DATAADDR, addr) |
SET_VAL(BUFDATALEN, len));
}
static __le64 *xgene_enet_get_exp_bufs(struct xgene_enet_desc_ring *ring)
{
__le64 *exp_bufs;
exp_bufs = &ring->exp_bufs[ring->exp_buf_tail * MAX_EXP_BUFFS];
memset(exp_bufs, 0, sizeof(__le64) * MAX_EXP_BUFFS);
ring->exp_buf_tail = (ring->exp_buf_tail + 1) & ((ring->slots / 2) - 1);
return exp_bufs;
}
static dma_addr_t *xgene_get_frag_dma_array(struct xgene_enet_desc_ring *ring)
{
return &ring->cp_ring->frag_dma_addr[ring->tail * MAX_SKB_FRAGS];
}
static int xgene_enet_setup_tx_desc(struct xgene_enet_desc_ring *tx_ring,
struct sk_buff *skb)
{
struct device *dev = ndev_to_dev(tx_ring->ndev);
struct xgene_enet_pdata *pdata = netdev_priv(tx_ring->ndev);
struct xgene_enet_raw_desc *raw_desc;
__le64 *exp_desc = NULL, *exp_bufs = NULL;
dma_addr_t dma_addr, pbuf_addr, *frag_dma_addr;
skb_frag_t *frag;
u16 tail = tx_ring->tail;
u64 hopinfo = 0;
u32 len, hw_len;
u8 ll = 0, nv = 0, idx = 0;
bool split = false;
u32 size, offset, ell_bytes = 0;
u32 i, fidx, nr_frags, count = 1;
int ret;
raw_desc = &tx_ring->raw_desc[tail];
tail = (tail + 1) & (tx_ring->slots - 1);
memset(raw_desc, 0, sizeof(struct xgene_enet_raw_desc));
ret = xgene_enet_work_msg(skb, &hopinfo);
if (ret)
return ret;
raw_desc->m3 = cpu_to_le64(SET_VAL(HENQNUM, tx_ring->dst_ring_num) |
hopinfo);
len = skb_headlen(skb);
hw_len = xgene_enet_encode_len(len);
dma_addr = dma_map_single(dev, skb->data, len, DMA_TO_DEVICE);
if (dma_mapping_error(dev, dma_addr)) {
netdev_err(tx_ring->ndev, "DMA mapping error\n");
return -EINVAL;
}
/* Hardware expects descriptor in little endian format */
raw_desc->m1 = cpu_to_le64(SET_VAL(DATAADDR, dma_addr) |
SET_VAL(BUFDATALEN, hw_len) |
SET_BIT(COHERENT));
if (!skb_is_nonlinear(skb))
goto out;
/* scatter gather */
nv = 1;
exp_desc = (void *)&tx_ring->raw_desc[tail];
tail = (tail + 1) & (tx_ring->slots - 1);
memset(exp_desc, 0, sizeof(struct xgene_enet_raw_desc));
nr_frags = skb_shinfo(skb)->nr_frags;
for (i = nr_frags; i < 4 ; i++)
exp_desc[i ^ 1] = cpu_to_le64(LAST_BUFFER);
frag_dma_addr = xgene_get_frag_dma_array(tx_ring);
for (i = 0, fidx = 0; split || (fidx < nr_frags); i++) {
if (!split) {
frag = &skb_shinfo(skb)->frags[fidx];
size = skb_frag_size(frag);
offset = 0;
pbuf_addr = skb_frag_dma_map(dev, frag, 0, size,
DMA_TO_DEVICE);
if (dma_mapping_error(dev, pbuf_addr))
return -EINVAL;
frag_dma_addr[fidx] = pbuf_addr;
fidx++;
if (size > BUFLEN_16K)
split = true;
}
if (size > BUFLEN_16K) {
len = BUFLEN_16K;
size -= BUFLEN_16K;
} else {
len = size;
split = false;
}
dma_addr = pbuf_addr + offset;
hw_len = xgene_enet_encode_len(len);
switch (i) {
case 0:
case 1:
case 2:
xgene_set_addr_len(exp_desc, i, dma_addr, hw_len);
break;
case 3:
if (split || (fidx != nr_frags)) {
exp_bufs = xgene_enet_get_exp_bufs(tx_ring);
xgene_set_addr_len(exp_bufs, idx, dma_addr,
hw_len);
idx++;
ell_bytes += len;
} else {
xgene_set_addr_len(exp_desc, i, dma_addr,
hw_len);
}
break;
default:
xgene_set_addr_len(exp_bufs, idx, dma_addr, hw_len);
idx++;
ell_bytes += len;
break;
}
if (split)
offset += BUFLEN_16K;
}
count++;
if (idx) {
ll = 1;
dma_addr = dma_map_single(dev, exp_bufs,
sizeof(u64) * MAX_EXP_BUFFS,
DMA_TO_DEVICE);
if (dma_mapping_error(dev, dma_addr)) {
dev_kfree_skb_any(skb);
return -EINVAL;
}
i = ell_bytes >> LL_BYTES_LSB_LEN;
exp_desc[2] = cpu_to_le64(SET_VAL(DATAADDR, dma_addr) |
SET_VAL(LL_BYTES_MSB, i) |
SET_VAL(LL_LEN, idx));
raw_desc->m2 = cpu_to_le64(SET_VAL(LL_BYTES_LSB, ell_bytes));
}
out:
raw_desc->m0 = cpu_to_le64(SET_VAL(LL, ll) | SET_VAL(NV, nv) |
SET_VAL(USERINFO, tx_ring->tail));
tx_ring->cp_ring->cp_skb[tx_ring->tail] = skb;
pdata->tx_level[tx_ring->cp_ring->index] += count;
tx_ring->tail = tail;
return count;
}
static netdev_tx_t xgene_enet_start_xmit(struct sk_buff *skb,
struct net_device *ndev)
{
struct xgene_enet_pdata *pdata = netdev_priv(ndev);
struct xgene_enet_desc_ring *tx_ring;
int index = skb->queue_mapping;
u32 tx_level = pdata->tx_level[index];
int count;
tx_ring = pdata->tx_ring[index];
if (tx_level < pdata->txc_level[index])
tx_level += ((typeof(pdata->tx_level[index]))~0U);
if ((tx_level - pdata->txc_level[index]) > pdata->tx_qcnt_hi) {
netif_stop_subqueue(ndev, index);
return NETDEV_TX_BUSY;
}
if (skb_padto(skb, XGENE_MIN_ENET_FRAME_SIZE))
return NETDEV_TX_OK;
count = xgene_enet_setup_tx_desc(tx_ring, skb);
if (count == -EBUSY)
return NETDEV_TX_BUSY;
if (count <= 0) {
dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
skb_tx_timestamp(skb);
tx_ring->tx_packets++;
tx_ring->tx_bytes += skb->len;
pdata->ring_ops->wr_cmd(tx_ring, count);
return NETDEV_TX_OK;
}
static void xgene_enet_skip_csum(struct sk_buff *skb)
{
struct iphdr *iph = ip_hdr(skb);
if (!ip_is_fragment(iph) ||
(iph->protocol != IPPROTO_TCP && iph->protocol != IPPROTO_UDP)) {
skb->ip_summed = CHECKSUM_UNNECESSARY;
}
}
static int xgene_enet_rx_frame(struct xgene_enet_desc_ring *rx_ring,
struct xgene_enet_raw_desc *raw_desc)
{
struct net_device *ndev;
struct device *dev;
struct xgene_enet_desc_ring *buf_pool;
u32 datalen, skb_index;
struct sk_buff *skb;
u8 status;
int ret = 0;
ndev = rx_ring->ndev;
dev = ndev_to_dev(rx_ring->ndev);
buf_pool = rx_ring->buf_pool;
dma_unmap_single(dev, GET_VAL(DATAADDR, le64_to_cpu(raw_desc->m1)),
XGENE_ENET_MAX_MTU, DMA_FROM_DEVICE);
skb_index = GET_VAL(USERINFO, le64_to_cpu(raw_desc->m0));
skb = buf_pool->rx_skb[skb_index];
buf_pool->rx_skb[skb_index] = NULL;
/* checking for error */
status = (GET_VAL(ELERR, le64_to_cpu(raw_desc->m0)) << LERR_LEN) ||
GET_VAL(LERR, le64_to_cpu(raw_desc->m0));
if (unlikely(status > 2)) {
dev_kfree_skb_any(skb);
xgene_enet_parse_error(rx_ring, netdev_priv(rx_ring->ndev),
status);
ret = -EIO;
goto out;
}
/* strip off CRC as HW isn't doing this */
datalen = GET_VAL(BUFDATALEN, le64_to_cpu(raw_desc->m1));
datalen = (datalen & DATALEN_MASK) - 4;
prefetch(skb->data - NET_IP_ALIGN);
skb_put(skb, datalen);
skb_checksum_none_assert(skb);
skb->protocol = eth_type_trans(skb, ndev);
if (likely((ndev->features & NETIF_F_IP_CSUM) &&
skb->protocol == htons(ETH_P_IP))) {
xgene_enet_skip_csum(skb);
}
rx_ring->rx_packets++;
rx_ring->rx_bytes += datalen;
napi_gro_receive(&rx_ring->napi, skb);
out:
if (--rx_ring->nbufpool == 0) {
ret = xgene_enet_refill_bufpool(buf_pool, NUM_BUFPOOL);
rx_ring->nbufpool = NUM_BUFPOOL;
}
return ret;
}
static bool is_rx_desc(struct xgene_enet_raw_desc *raw_desc)
{
return GET_VAL(FPQNUM, le64_to_cpu(raw_desc->m0)) ? true : false;
}
static int xgene_enet_process_ring(struct xgene_enet_desc_ring *ring,
int budget)
{
struct net_device *ndev = ring->ndev;
struct xgene_enet_pdata *pdata = netdev_priv(ndev);
struct xgene_enet_raw_desc *raw_desc, *exp_desc;
u16 head = ring->head;
u16 slots = ring->slots - 1;
int ret, desc_count, count = 0, processed = 0;
bool is_completion;
do {
raw_desc = &ring->raw_desc[head];
desc_count = 0;
is_completion = false;
exp_desc = NULL;
if (unlikely(xgene_enet_is_desc_slot_empty(raw_desc)))
break;
/* read fpqnum field after dataaddr field */
dma_rmb();
if (GET_BIT(NV, le64_to_cpu(raw_desc->m0))) {
head = (head + 1) & slots;
exp_desc = &ring->raw_desc[head];
if (unlikely(xgene_enet_is_desc_slot_empty(exp_desc))) {
head = (head - 1) & slots;
break;
}
dma_rmb();
count++;
desc_count++;
}
if (is_rx_desc(raw_desc)) {
ret = xgene_enet_rx_frame(ring, raw_desc);
} else {
ret = xgene_enet_tx_completion(ring, raw_desc);
is_completion = true;
}
xgene_enet_mark_desc_slot_empty(raw_desc);
if (exp_desc)
xgene_enet_mark_desc_slot_empty(exp_desc);
head = (head + 1) & slots;
count++;
desc_count++;
processed++;
if (is_completion)
pdata->txc_level[ring->index] += desc_count;
if (ret)
break;
} while (--budget);
if (likely(count)) {
pdata->ring_ops->wr_cmd(ring, -count);
ring->head = head;
if (__netif_subqueue_stopped(ndev, ring->index))
netif_start_subqueue(ndev, ring->index);
}
return processed;
}
static int xgene_enet_napi(struct napi_struct *napi, const int budget)
{
struct xgene_enet_desc_ring *ring;
int processed;
ring = container_of(napi, struct xgene_enet_desc_ring, napi);
processed = xgene_enet_process_ring(ring, budget);
if (processed != budget) {
napi_complete(napi);
enable_irq(ring->irq);
}
return processed;
}
static void xgene_enet_timeout(struct net_device *ndev)
{
struct xgene_enet_pdata *pdata = netdev_priv(ndev);
struct netdev_queue *txq;
int i;
pdata->mac_ops->reset(pdata);
for (i = 0; i < pdata->txq_cnt; i++) {
txq = netdev_get_tx_queue(ndev, i);
txq->trans_start = jiffies;
netif_tx_start_queue(txq);
}
}
static void xgene_enet_set_irq_name(struct net_device *ndev)
{
struct xgene_enet_pdata *pdata = netdev_priv(ndev);
struct xgene_enet_desc_ring *ring;
int i;
for (i = 0; i < pdata->rxq_cnt; i++) {
ring = pdata->rx_ring[i];
if (!pdata->cq_cnt) {
snprintf(ring->irq_name, IRQ_ID_SIZE, "%s-rx-txc",
ndev->name);
} else {
snprintf(ring->irq_name, IRQ_ID_SIZE, "%s-rx-%d",
ndev->name, i);
}
}
for (i = 0; i < pdata->cq_cnt; i++) {
ring = pdata->tx_ring[i]->cp_ring;
snprintf(ring->irq_name, IRQ_ID_SIZE, "%s-txc-%d",
ndev->name, i);
}
}
static int xgene_enet_register_irq(struct net_device *ndev)
{
struct xgene_enet_pdata *pdata = netdev_priv(ndev);
struct device *dev = ndev_to_dev(ndev);
struct xgene_enet_desc_ring *ring;
int ret = 0, i;
xgene_enet_set_irq_name(ndev);
for (i = 0; i < pdata->rxq_cnt; i++) {
ring = pdata->rx_ring[i];
irq_set_status_flags(ring->irq, IRQ_DISABLE_UNLAZY);
ret = devm_request_irq(dev, ring->irq, xgene_enet_rx_irq,
0, ring->irq_name, ring);
if (ret) {
netdev_err(ndev, "Failed to request irq %s\n",
ring->irq_name);
}
}
for (i = 0; i < pdata->cq_cnt; i++) {
ring = pdata->tx_ring[i]->cp_ring;
irq_set_status_flags(ring->irq, IRQ_DISABLE_UNLAZY);
ret = devm_request_irq(dev, ring->irq, xgene_enet_rx_irq,
0, ring->irq_name, ring);
if (ret) {
netdev_err(ndev, "Failed to request irq %s\n",
ring->irq_name);
}
}
return ret;
}
static void xgene_enet_free_irq(struct net_device *ndev)
{
struct xgene_enet_pdata *pdata;
struct xgene_enet_desc_ring *ring;
struct device *dev;
int i;
pdata = netdev_priv(ndev);
dev = ndev_to_dev(ndev);
for (i = 0; i < pdata->rxq_cnt; i++) {
ring = pdata->rx_ring[i];
irq_clear_status_flags(ring->irq, IRQ_DISABLE_UNLAZY);
devm_free_irq(dev, ring->irq, ring);
}
for (i = 0; i < pdata->cq_cnt; i++) {
ring = pdata->tx_ring[i]->cp_ring;
irq_clear_status_flags(ring->irq, IRQ_DISABLE_UNLAZY);
devm_free_irq(dev, ring->irq, ring);
}
}
static void xgene_enet_napi_enable(struct xgene_enet_pdata *pdata)
{
struct napi_struct *napi;
int i;
for (i = 0; i < pdata->rxq_cnt; i++) {
napi = &pdata->rx_ring[i]->napi;
napi_enable(napi);
}
for (i = 0; i < pdata->cq_cnt; i++) {
napi = &pdata->tx_ring[i]->cp_ring->napi;
napi_enable(napi);
}
}
static void xgene_enet_napi_disable(struct xgene_enet_pdata *pdata)
{
struct napi_struct *napi;
int i;
for (i = 0; i < pdata->rxq_cnt; i++) {
napi = &pdata->rx_ring[i]->napi;
napi_disable(napi);
}
for (i = 0; i < pdata->cq_cnt; i++) {
napi = &pdata->tx_ring[i]->cp_ring->napi;
napi_disable(napi);
}
}
static int xgene_enet_open(struct net_device *ndev)
{
struct xgene_enet_pdata *pdata = netdev_priv(ndev);
const struct xgene_mac_ops *mac_ops = pdata->mac_ops;
int ret;
ret = netif_set_real_num_tx_queues(ndev, pdata->txq_cnt);
if (ret)
return ret;
ret = netif_set_real_num_rx_queues(ndev, pdata->rxq_cnt);
if (ret)
return ret;
xgene_enet_napi_enable(pdata);
ret = xgene_enet_register_irq(ndev);
if (ret)
return ret;
if (ndev->phydev) {
phy_start(ndev->phydev);
} else {
schedule_delayed_work(&pdata->link_work, PHY_POLL_LINK_OFF);
netif_carrier_off(ndev);
}
mac_ops->tx_enable(pdata);
mac_ops->rx_enable(pdata);
netif_tx_start_all_queues(ndev);
return ret;
}
static int xgene_enet_close(struct net_device *ndev)
{
struct xgene_enet_pdata *pdata = netdev_priv(ndev);
const struct xgene_mac_ops *mac_ops = pdata->mac_ops;
int i;
netif_tx_stop_all_queues(ndev);
mac_ops->tx_disable(pdata);
mac_ops->rx_disable(pdata);
if (ndev->phydev)
phy_stop(ndev->phydev);
else
cancel_delayed_work_sync(&pdata->link_work);
xgene_enet_free_irq(ndev);
xgene_enet_napi_disable(pdata);
for (i = 0; i < pdata->rxq_cnt; i++)
xgene_enet_process_ring(pdata->rx_ring[i], -1);
return 0;
}
static void xgene_enet_delete_ring(struct xgene_enet_desc_ring *ring)
{
struct xgene_enet_pdata *pdata;
struct device *dev;
pdata = netdev_priv(ring->ndev);
dev = ndev_to_dev(ring->ndev);
pdata->ring_ops->clear(ring);
dmam_free_coherent(dev, ring->size, ring->desc_addr, ring->dma);
}
static void xgene_enet_delete_desc_rings(struct xgene_enet_pdata *pdata)
{
struct xgene_enet_desc_ring *buf_pool;
struct xgene_enet_desc_ring *ring;
int i;
for (i = 0; i < pdata->txq_cnt; i++) {
ring = pdata->tx_ring[i];
if (ring) {
xgene_enet_delete_ring(ring);
pdata->port_ops->clear(pdata, ring);
if (pdata->cq_cnt)
xgene_enet_delete_ring(ring->cp_ring);
pdata->tx_ring[i] = NULL;
}
}
for (i = 0; i < pdata->rxq_cnt; i++) {
ring = pdata->rx_ring[i];
if (ring) {
buf_pool = ring->buf_pool;
xgene_enet_delete_bufpool(buf_pool);
xgene_enet_delete_ring(buf_pool);
pdata->port_ops->clear(pdata, buf_pool);
xgene_enet_delete_ring(ring);
pdata->rx_ring[i] = NULL;
}
}
}
static int xgene_enet_get_ring_size(struct device *dev,
enum xgene_enet_ring_cfgsize cfgsize)
{
int size = -EINVAL;
switch (cfgsize) {
case RING_CFGSIZE_512B:
size = 0x200;
break;
case RING_CFGSIZE_2KB:
size = 0x800;
break;
case RING_CFGSIZE_16KB:
size = 0x4000;
break;
case RING_CFGSIZE_64KB:
size = 0x10000;
break;
case RING_CFGSIZE_512KB:
size = 0x80000;
break;
default:
dev_err(dev, "Unsupported cfg ring size %d\n", cfgsize);
break;
}
return size;
}
static void xgene_enet_free_desc_ring(struct xgene_enet_desc_ring *ring)
{
struct xgene_enet_pdata *pdata;
struct device *dev;
if (!ring)
return;
dev = ndev_to_dev(ring->ndev);
pdata = netdev_priv(ring->ndev);
if (ring->desc_addr) {
pdata->ring_ops->clear(ring);
dmam_free_coherent(dev, ring->size, ring->desc_addr, ring->dma);
}
devm_kfree(dev, ring);
}
static void xgene_enet_free_desc_rings(struct xgene_enet_pdata *pdata)
{
struct device *dev = &pdata->pdev->dev;
struct xgene_enet_desc_ring *ring;
int i;
for (i = 0; i < pdata->txq_cnt; i++) {
ring = pdata->tx_ring[i];
if (ring) {
if (ring->cp_ring && ring->cp_ring->cp_skb)
devm_kfree(dev, ring->cp_ring->cp_skb);
if (ring->cp_ring && pdata->cq_cnt)
xgene_enet_free_desc_ring(ring->cp_ring);
xgene_enet_free_desc_ring(ring);
}
}
for (i = 0; i < pdata->rxq_cnt; i++) {
ring = pdata->rx_ring[i];
if (ring) {
if (ring->buf_pool) {
if (ring->buf_pool->rx_skb)
devm_kfree(dev, ring->buf_pool->rx_skb);
xgene_enet_free_desc_ring(ring->buf_pool);
}
xgene_enet_free_desc_ring(ring);
}
}
}
static bool is_irq_mbox_required(struct xgene_enet_pdata *pdata,
struct xgene_enet_desc_ring *ring)
{
if ((pdata->enet_id == XGENE_ENET2) &&
(xgene_enet_ring_owner(ring->id) == RING_OWNER_CPU)) {
return true;
}
return false;
}
static void __iomem *xgene_enet_ring_cmd_base(struct xgene_enet_pdata *pdata,
struct xgene_enet_desc_ring *ring)
{
u8 num_ring_id_shift = pdata->ring_ops->num_ring_id_shift;
return pdata->ring_cmd_addr + (ring->num << num_ring_id_shift);
}
static struct xgene_enet_desc_ring *xgene_enet_create_desc_ring(
struct net_device *ndev, u32 ring_num,
enum xgene_enet_ring_cfgsize cfgsize, u32 ring_id)
{
struct xgene_enet_pdata *pdata = netdev_priv(ndev);
struct device *dev = ndev_to_dev(ndev);
struct xgene_enet_desc_ring *ring;
void *irq_mbox_addr;
int size;
size = xgene_enet_get_ring_size(dev, cfgsize);
if (size < 0)
return NULL;
ring = devm_kzalloc(dev, sizeof(struct xgene_enet_desc_ring),
GFP_KERNEL);
if (!ring)
return NULL;
ring->ndev = ndev;
ring->num = ring_num;
ring->cfgsize = cfgsize;
ring->id = ring_id;
ring->desc_addr = dmam_alloc_coherent(dev, size, &ring->dma,
GFP_KERNEL | __GFP_ZERO);
if (!ring->desc_addr) {
devm_kfree(dev, ring);
return NULL;
}
ring->size = size;
if (is_irq_mbox_required(pdata, ring)) {
irq_mbox_addr = dmam_alloc_coherent(dev, INTR_MBOX_SIZE,
&ring->irq_mbox_dma,
GFP_KERNEL | __GFP_ZERO);
if (!irq_mbox_addr) {
dmam_free_coherent(dev, size, ring->desc_addr,
ring->dma);
devm_kfree(dev, ring);
return NULL;
}
ring->irq_mbox_addr = irq_mbox_addr;
}
ring->cmd_base = xgene_enet_ring_cmd_base(pdata, ring);
ring->cmd = ring->cmd_base + INC_DEC_CMD_ADDR;
ring = pdata->ring_ops->setup(ring);
netdev_dbg(ndev, "ring info: num=%d size=%d id=%d slots=%d\n",
ring->num, ring->size, ring->id, ring->slots);
return ring;
}
static u16 xgene_enet_get_ring_id(enum xgene_ring_owner owner, u8 bufnum)
{
return (owner << 6) | (bufnum & GENMASK(5, 0));
}
static enum xgene_ring_owner xgene_derive_ring_owner(struct xgene_enet_pdata *p)
{
enum xgene_ring_owner owner;
if (p->enet_id == XGENE_ENET1) {
switch (p->phy_mode) {
case PHY_INTERFACE_MODE_SGMII:
owner = RING_OWNER_ETH0;
break;
default:
owner = (!p->port_id) ? RING_OWNER_ETH0 :
RING_OWNER_ETH1;
break;
}
} else {
owner = (!p->port_id) ? RING_OWNER_ETH0 : RING_OWNER_ETH1;
}
return owner;
}
static u8 xgene_start_cpu_bufnum(struct xgene_enet_pdata *pdata)
{
struct device *dev = &pdata->pdev->dev;
u32 cpu_bufnum;
int ret;
ret = device_property_read_u32(dev, "channel", &cpu_bufnum);
return (!ret) ? cpu_bufnum : pdata->cpu_bufnum;
}
static int xgene_enet_create_desc_rings(struct net_device *ndev)
{
struct xgene_enet_pdata *pdata = netdev_priv(ndev);
struct device *dev = ndev_to_dev(ndev);
struct xgene_enet_desc_ring *rx_ring, *tx_ring, *cp_ring;
struct xgene_enet_desc_ring *buf_pool = NULL;
enum xgene_ring_owner owner;
dma_addr_t dma_exp_bufs;
u8 cpu_bufnum;
u8 eth_bufnum = pdata->eth_bufnum;
u8 bp_bufnum = pdata->bp_bufnum;
u16 ring_num = pdata->ring_num;
__le64 *exp_bufs;
u16 ring_id;
int i, ret, size;
cpu_bufnum = xgene_start_cpu_bufnum(pdata);
for (i = 0; i < pdata->rxq_cnt; i++) {
/* allocate rx descriptor ring */
owner = xgene_derive_ring_owner(pdata);
ring_id = xgene_enet_get_ring_id(RING_OWNER_CPU, cpu_bufnum++);
rx_ring = xgene_enet_create_desc_ring(ndev, ring_num++,
RING_CFGSIZE_16KB,
ring_id);
if (!rx_ring) {
ret = -ENOMEM;
goto err;
}
/* allocate buffer pool for receiving packets */
owner = xgene_derive_ring_owner(pdata);
ring_id = xgene_enet_get_ring_id(owner, bp_bufnum++);
buf_pool = xgene_enet_create_desc_ring(ndev, ring_num++,
RING_CFGSIZE_2KB,
ring_id);
if (!buf_pool) {
ret = -ENOMEM;
goto err;
}
rx_ring->nbufpool = NUM_BUFPOOL;
rx_ring->buf_pool = buf_pool;
rx_ring->irq = pdata->irqs[i];
buf_pool->rx_skb = devm_kcalloc(dev, buf_pool->slots,
sizeof(struct sk_buff *),
GFP_KERNEL);
if (!buf_pool->rx_skb) {
ret = -ENOMEM;
goto err;
}
buf_pool->dst_ring_num = xgene_enet_dst_ring_num(buf_pool);
rx_ring->buf_pool = buf_pool;
pdata->rx_ring[i] = rx_ring;
}
for (i = 0; i < pdata->txq_cnt; i++) {
/* allocate tx descriptor ring */
owner = xgene_derive_ring_owner(pdata);
ring_id = xgene_enet_get_ring_id(owner, eth_bufnum++);
tx_ring = xgene_enet_create_desc_ring(ndev, ring_num++,
RING_CFGSIZE_16KB,
ring_id);
if (!tx_ring) {
ret = -ENOMEM;
goto err;
}
size = (tx_ring->slots / 2) * sizeof(__le64) * MAX_EXP_BUFFS;
exp_bufs = dmam_alloc_coherent(dev, size, &dma_exp_bufs,
GFP_KERNEL | __GFP_ZERO);
if (!exp_bufs) {
ret = -ENOMEM;
goto err;
}
tx_ring->exp_bufs = exp_bufs;
pdata->tx_ring[i] = tx_ring;
if (!pdata->cq_cnt) {
cp_ring = pdata->rx_ring[i];
} else {
/* allocate tx completion descriptor ring */
ring_id = xgene_enet_get_ring_id(RING_OWNER_CPU,
cpu_bufnum++);
cp_ring = xgene_enet_create_desc_ring(ndev, ring_num++,
RING_CFGSIZE_16KB,
ring_id);
if (!cp_ring) {
ret = -ENOMEM;
goto err;
}
cp_ring->irq = pdata->irqs[pdata->rxq_cnt + i];
cp_ring->index = i;
}
cp_ring->cp_skb = devm_kcalloc(dev, tx_ring->slots,
sizeof(struct sk_buff *),
GFP_KERNEL);
if (!cp_ring->cp_skb) {
ret = -ENOMEM;
goto err;
}
size = sizeof(dma_addr_t) * MAX_SKB_FRAGS;
cp_ring->frag_dma_addr = devm_kcalloc(dev, tx_ring->slots,
size, GFP_KERNEL);
if (!cp_ring->frag_dma_addr) {
devm_kfree(dev, cp_ring->cp_skb);
ret = -ENOMEM;
goto err;
}
tx_ring->cp_ring = cp_ring;
tx_ring->dst_ring_num = xgene_enet_dst_ring_num(cp_ring);
}
pdata->ring_ops->coalesce(pdata->tx_ring[0]);
pdata->tx_qcnt_hi = pdata->tx_ring[0]->slots - 128;
return 0;
err:
xgene_enet_free_desc_rings(pdata);
return ret;
}
static struct rtnl_link_stats64 *xgene_enet_get_stats64(
struct net_device *ndev,
struct rtnl_link_stats64 *storage)
{
struct xgene_enet_pdata *pdata = netdev_priv(ndev);
struct rtnl_link_stats64 *stats = &pdata->stats;
struct xgene_enet_desc_ring *ring;
int i;
memset(stats, 0, sizeof(struct rtnl_link_stats64));
for (i = 0; i < pdata->txq_cnt; i++) {
ring = pdata->tx_ring[i];
if (ring) {
stats->tx_packets += ring->tx_packets;
stats->tx_bytes += ring->tx_bytes;
}
}
for (i = 0; i < pdata->rxq_cnt; i++) {
ring = pdata->rx_ring[i];
if (ring) {
stats->rx_packets += ring->rx_packets;
stats->rx_bytes += ring->rx_bytes;
stats->rx_errors += ring->rx_length_errors +
ring->rx_crc_errors +
ring->rx_frame_errors +
ring->rx_fifo_errors;
stats->rx_dropped += ring->rx_dropped;
}
}
memcpy(storage, stats, sizeof(struct rtnl_link_stats64));
return storage;
}
static int xgene_enet_set_mac_address(struct net_device *ndev, void *addr)
{
struct xgene_enet_pdata *pdata = netdev_priv(ndev);
int ret;
ret = eth_mac_addr(ndev, addr);
if (ret)
return ret;
pdata->mac_ops->set_mac_addr(pdata);
return ret;
}
static const struct net_device_ops xgene_ndev_ops = {
.ndo_open = xgene_enet_open,
.ndo_stop = xgene_enet_close,
.ndo_start_xmit = xgene_enet_start_xmit,
.ndo_tx_timeout = xgene_enet_timeout,
.ndo_get_stats64 = xgene_enet_get_stats64,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = xgene_enet_set_mac_address,
};
#ifdef CONFIG_ACPI
static void xgene_get_port_id_acpi(struct device *dev,
struct xgene_enet_pdata *pdata)
{
acpi_status status;
u64 temp;
status = acpi_evaluate_integer(ACPI_HANDLE(dev), "_SUN", NULL, &temp);
if (ACPI_FAILURE(status)) {
pdata->port_id = 0;
} else {
pdata->port_id = temp;
}
return;
}
#endif
static void xgene_get_port_id_dt(struct device *dev, struct xgene_enet_pdata *pdata)
{
u32 id = 0;
of_property_read_u32(dev->of_node, "port-id", &id);
pdata->port_id = id & BIT(0);
return;
}
static int xgene_get_tx_delay(struct xgene_enet_pdata *pdata)
{
struct device *dev = &pdata->pdev->dev;
int delay, ret;
ret = of_property_read_u32(dev->of_node, "tx-delay", &delay);
if (ret) {
pdata->tx_delay = 4;
return 0;
}
if (delay < 0 || delay > 7) {
dev_err(dev, "Invalid tx-delay specified\n");
return -EINVAL;
}
pdata->tx_delay = delay;
return 0;
}
static int xgene_get_rx_delay(struct xgene_enet_pdata *pdata)
{
struct device *dev = &pdata->pdev->dev;
int delay, ret;
ret = of_property_read_u32(dev->of_node, "rx-delay", &delay);
if (ret) {
pdata->rx_delay = 2;
return 0;
}
if (delay < 0 || delay > 7) {
dev_err(dev, "Invalid rx-delay specified\n");
return -EINVAL;
}
pdata->rx_delay = delay;
return 0;
}
static int xgene_enet_get_irqs(struct xgene_enet_pdata *pdata)
{
struct platform_device *pdev = pdata->pdev;
struct device *dev = &pdev->dev;
int i, ret, max_irqs;
if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII)
max_irqs = 1;
else if (pdata->phy_mode == PHY_INTERFACE_MODE_SGMII)
max_irqs = 2;
else
max_irqs = XGENE_MAX_ENET_IRQ;
for (i = 0; i < max_irqs; i++) {
ret = platform_get_irq(pdev, i);
if (ret <= 0) {
if (pdata->phy_mode == PHY_INTERFACE_MODE_XGMII) {
max_irqs = i;
pdata->rxq_cnt = max_irqs / 2;
pdata->txq_cnt = max_irqs / 2;
pdata->cq_cnt = max_irqs / 2;
break;
}
dev_err(dev, "Unable to get ENET IRQ\n");
ret = ret ? : -ENXIO;
return ret;
}
pdata->irqs[i] = ret;
}
return 0;
}
static int xgene_enet_check_phy_handle(struct xgene_enet_pdata *pdata)
{
int ret;
if (pdata->phy_mode == PHY_INTERFACE_MODE_XGMII)
return 0;
if (!IS_ENABLED(CONFIG_MDIO_XGENE))
return 0;
ret = xgene_enet_phy_connect(pdata->ndev);
if (!ret)
pdata->mdio_driver = true;
return 0;
}
static void xgene_enet_gpiod_get(struct xgene_enet_pdata *pdata)
{
struct device *dev = &pdata->pdev->dev;
if (pdata->phy_mode != PHY_INTERFACE_MODE_XGMII)
return;
pdata->sfp_rdy = gpiod_get(dev, "rxlos", GPIOD_IN);
if (IS_ERR(pdata->sfp_rdy))
pdata->sfp_rdy = gpiod_get(dev, "sfp", GPIOD_IN);
}
static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata)
{
struct platform_device *pdev;
struct net_device *ndev;
struct device *dev;
struct resource *res;
void __iomem *base_addr;
u32 offset;
int ret = 0;
pdev = pdata->pdev;
dev = &pdev->dev;
ndev = pdata->ndev;
res = platform_get_resource(pdev, IORESOURCE_MEM, RES_ENET_CSR);
if (!res) {
dev_err(dev, "Resource enet_csr not defined\n");
return -ENODEV;
}
pdata->base_addr = devm_ioremap(dev, res->start, resource_size(res));
if (!pdata->base_addr) {
dev_err(dev, "Unable to retrieve ENET Port CSR region\n");
return -ENOMEM;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, RES_RING_CSR);
if (!res) {
dev_err(dev, "Resource ring_csr not defined\n");
return -ENODEV;
}
pdata->ring_csr_addr = devm_ioremap(dev, res->start,
resource_size(res));
if (!pdata->ring_csr_addr) {
dev_err(dev, "Unable to retrieve ENET Ring CSR region\n");
return -ENOMEM;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, RES_RING_CMD);
if (!res) {
dev_err(dev, "Resource ring_cmd not defined\n");
return -ENODEV;
}
pdata->ring_cmd_addr = devm_ioremap(dev, res->start,
resource_size(res));
if (!pdata->ring_cmd_addr) {
dev_err(dev, "Unable to retrieve ENET Ring command region\n");
return -ENOMEM;
}
if (dev->of_node)
xgene_get_port_id_dt(dev, pdata);
#ifdef CONFIG_ACPI
else
xgene_get_port_id_acpi(dev, pdata);
#endif
if (!device_get_mac_address(dev, ndev->dev_addr, ETH_ALEN))
eth_hw_addr_random(ndev);
memcpy(ndev->perm_addr, ndev->dev_addr, ndev->addr_len);
pdata->phy_mode = device_get_phy_mode(dev);
if (pdata->phy_mode < 0) {
dev_err(dev, "Unable to get phy-connection-type\n");
return pdata->phy_mode;
}
if (pdata->phy_mode != PHY_INTERFACE_MODE_RGMII &&
pdata->phy_mode != PHY_INTERFACE_MODE_SGMII &&
pdata->phy_mode != PHY_INTERFACE_MODE_XGMII) {
dev_err(dev, "Incorrect phy-connection-type specified\n");
return -ENODEV;
}
ret = xgene_get_tx_delay(pdata);
if (ret)
return ret;
ret = xgene_get_rx_delay(pdata);
if (ret)
return ret;
ret = xgene_enet_get_irqs(pdata);
if (ret)
return ret;
ret = xgene_enet_check_phy_handle(pdata);
if (ret)
return ret;
xgene_enet_gpiod_get(pdata);
pdata->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(pdata->clk)) {
/* Firmware may have set up the clock already. */
dev_info(dev, "clocks have been setup already\n");
}
if (pdata->phy_mode != PHY_INTERFACE_MODE_XGMII)
base_addr = pdata->base_addr - (pdata->port_id * MAC_OFFSET);
else
base_addr = pdata->base_addr;
pdata->eth_csr_addr = base_addr + BLOCK_ETH_CSR_OFFSET;
pdata->cle.base = base_addr + BLOCK_ETH_CLE_CSR_OFFSET;
pdata->eth_ring_if_addr = base_addr + BLOCK_ETH_RING_IF_OFFSET;
pdata->eth_diag_csr_addr = base_addr + BLOCK_ETH_DIAG_CSR_OFFSET;
if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII ||
pdata->phy_mode == PHY_INTERFACE_MODE_SGMII) {
pdata->mcx_mac_addr = pdata->base_addr + BLOCK_ETH_MAC_OFFSET;
offset = (pdata->enet_id == XGENE_ENET1) ?
BLOCK_ETH_MAC_CSR_OFFSET :
X2_BLOCK_ETH_MAC_CSR_OFFSET;
pdata->mcx_mac_csr_addr = base_addr + offset;
} else {
pdata->mcx_mac_addr = base_addr + BLOCK_AXG_MAC_OFFSET;
pdata->mcx_mac_csr_addr = base_addr + BLOCK_AXG_MAC_CSR_OFFSET;
pdata->pcs_addr = base_addr + BLOCK_PCS_OFFSET;
}
pdata->rx_buff_cnt = NUM_PKT_BUF;
return 0;
}
static int xgene_enet_init_hw(struct xgene_enet_pdata *pdata)
{
struct xgene_enet_cle *enet_cle = &pdata->cle;
struct net_device *ndev = pdata->ndev;
struct xgene_enet_desc_ring *buf_pool;
u16 dst_ring_num;
int i, ret;
ret = pdata->port_ops->reset(pdata);
if (ret)
return ret;
ret = xgene_enet_create_desc_rings(ndev);
if (ret) {
netdev_err(ndev, "Error in ring configuration\n");
return ret;
}
/* setup buffer pool */
for (i = 0; i < pdata->rxq_cnt; i++) {
buf_pool = pdata->rx_ring[i]->buf_pool;
xgene_enet_init_bufpool(buf_pool);
ret = xgene_enet_refill_bufpool(buf_pool, pdata->rx_buff_cnt);
if (ret)
goto err;
}
dst_ring_num = xgene_enet_dst_ring_num(pdata->rx_ring[0]);
buf_pool = pdata->rx_ring[0]->buf_pool;
if (pdata->phy_mode == PHY_INTERFACE_MODE_XGMII) {
/* Initialize and Enable PreClassifier Tree */
enet_cle->max_nodes = 512;
enet_cle->max_dbptrs = 1024;
enet_cle->parsers = 3;
enet_cle->active_parser = PARSER_ALL;
enet_cle->ptree.start_node = 0;
enet_cle->ptree.start_dbptr = 0;
enet_cle->jump_bytes = 8;
ret = pdata->cle_ops->cle_init(pdata);
if (ret) {
netdev_err(ndev, "Preclass Tree init error\n");
goto err;
}
} else {
pdata->port_ops->cle_bypass(pdata, dst_ring_num, buf_pool->id);
}
pdata->phy_speed = SPEED_UNKNOWN;
pdata->mac_ops->init(pdata);
return ret;
err:
xgene_enet_delete_desc_rings(pdata);
return ret;
}
static void xgene_enet_setup_ops(struct xgene_enet_pdata *pdata)
{
switch (pdata->phy_mode) {
case PHY_INTERFACE_MODE_RGMII:
pdata->mac_ops = &xgene_gmac_ops;
pdata->port_ops = &xgene_gport_ops;
pdata->rm = RM3;
pdata->rxq_cnt = 1;
pdata->txq_cnt = 1;
pdata->cq_cnt = 0;
break;
case PHY_INTERFACE_MODE_SGMII:
pdata->mac_ops = &xgene_sgmac_ops;
pdata->port_ops = &xgene_sgport_ops;
pdata->rm = RM1;
pdata->rxq_cnt = 1;
pdata->txq_cnt = 1;
pdata->cq_cnt = 1;
break;
default:
pdata->mac_ops = &xgene_xgmac_ops;
pdata->port_ops = &xgene_xgport_ops;
pdata->cle_ops = &xgene_cle3in_ops;
pdata->rm = RM0;
if (!pdata->rxq_cnt) {
pdata->rxq_cnt = XGENE_NUM_RX_RING;
pdata->txq_cnt = XGENE_NUM_TX_RING;
pdata->cq_cnt = XGENE_NUM_TXC_RING;
}
break;
}
if (pdata->enet_id == XGENE_ENET1) {
switch (pdata->port_id) {
case 0:
if (pdata->phy_mode == PHY_INTERFACE_MODE_XGMII) {
pdata->cpu_bufnum = X2_START_CPU_BUFNUM_0;
pdata->eth_bufnum = X2_START_ETH_BUFNUM_0;
pdata->bp_bufnum = X2_START_BP_BUFNUM_0;
pdata->ring_num = START_RING_NUM_0;
} else {
pdata->cpu_bufnum = START_CPU_BUFNUM_0;
pdata->eth_bufnum = START_ETH_BUFNUM_0;
pdata->bp_bufnum = START_BP_BUFNUM_0;
pdata->ring_num = START_RING_NUM_0;
}
break;
case 1:
if (pdata->phy_mode == PHY_INTERFACE_MODE_XGMII) {
pdata->cpu_bufnum = XG_START_CPU_BUFNUM_1;
pdata->eth_bufnum = XG_START_ETH_BUFNUM_1;
pdata->bp_bufnum = XG_START_BP_BUFNUM_1;
pdata->ring_num = XG_START_RING_NUM_1;
} else {
pdata->cpu_bufnum = START_CPU_BUFNUM_1;
pdata->eth_bufnum = START_ETH_BUFNUM_1;
pdata->bp_bufnum = START_BP_BUFNUM_1;
pdata->ring_num = START_RING_NUM_1;
}
break;
default:
break;
}
pdata->ring_ops = &xgene_ring1_ops;
} else {
switch (pdata->port_id) {
case 0:
pdata->cpu_bufnum = X2_START_CPU_BUFNUM_0;
pdata->eth_bufnum = X2_START_ETH_BUFNUM_0;
pdata->bp_bufnum = X2_START_BP_BUFNUM_0;
pdata->ring_num = X2_START_RING_NUM_0;
break;
case 1:
pdata->cpu_bufnum = X2_START_CPU_BUFNUM_1;
pdata->eth_bufnum = X2_START_ETH_BUFNUM_1;
pdata->bp_bufnum = X2_START_BP_BUFNUM_1;
pdata->ring_num = X2_START_RING_NUM_1;
break;
default:
break;
}
pdata->rm = RM0;
pdata->ring_ops = &xgene_ring2_ops;
}
}
static void xgene_enet_napi_add(struct xgene_enet_pdata *pdata)
{
struct napi_struct *napi;
int i;
for (i = 0; i < pdata->rxq_cnt; i++) {
napi = &pdata->rx_ring[i]->napi;
netif_napi_add(pdata->ndev, napi, xgene_enet_napi,
NAPI_POLL_WEIGHT);
}
for (i = 0; i < pdata->cq_cnt; i++) {
napi = &pdata->tx_ring[i]->cp_ring->napi;
netif_napi_add(pdata->ndev, napi, xgene_enet_napi,
NAPI_POLL_WEIGHT);
}
}
static int xgene_enet_probe(struct platform_device *pdev)
{
struct net_device *ndev;
struct xgene_enet_pdata *pdata;
struct device *dev = &pdev->dev;
void (*link_state)(struct work_struct *);
const struct of_device_id *of_id;
int ret;
ndev = alloc_etherdev_mqs(sizeof(struct xgene_enet_pdata),
XGENE_NUM_RX_RING, XGENE_NUM_TX_RING);
if (!ndev)
return -ENOMEM;
pdata = netdev_priv(ndev);
pdata->pdev = pdev;
pdata->ndev = ndev;
SET_NETDEV_DEV(ndev, dev);
platform_set_drvdata(pdev, pdata);
ndev->netdev_ops = &xgene_ndev_ops;
xgene_enet_set_ethtool_ops(ndev);
ndev->features |= NETIF_F_IP_CSUM |
NETIF_F_GSO |
NETIF_F_GRO |
NETIF_F_SG;
of_id = of_match_device(xgene_enet_of_match, &pdev->dev);
if (of_id) {
pdata->enet_id = (enum xgene_enet_id)of_id->data;
}
#ifdef CONFIG_ACPI
else {
const struct acpi_device_id *acpi_id;
acpi_id = acpi_match_device(xgene_enet_acpi_match, &pdev->dev);
if (acpi_id)
pdata->enet_id = (enum xgene_enet_id) acpi_id->driver_data;
}
#endif
if (!pdata->enet_id) {
ret = -ENODEV;
goto err;
}
ret = xgene_enet_get_resources(pdata);
if (ret)
goto err;
xgene_enet_setup_ops(pdata);
if (pdata->phy_mode == PHY_INTERFACE_MODE_XGMII) {
ndev->features |= NETIF_F_TSO;
spin_lock_init(&pdata->mss_lock);
}
ndev->hw_features = ndev->features;
ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(64));
if (ret) {
netdev_err(ndev, "No usable DMA configuration\n");
goto err;
}
ret = xgene_enet_init_hw(pdata);
if (ret)
goto err;
link_state = pdata->mac_ops->link_state;
if (pdata->phy_mode == PHY_INTERFACE_MODE_XGMII) {
INIT_DELAYED_WORK(&pdata->link_work, link_state);
} else if (!pdata->mdio_driver) {
if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII)
ret = xgene_enet_mdio_config(pdata);
else
INIT_DELAYED_WORK(&pdata->link_work, link_state);
if (ret)
goto err1;
}
xgene_enet_napi_add(pdata);
ret = register_netdev(ndev);
if (ret) {
netdev_err(ndev, "Failed to register netdev\n");
goto err2;
}
return 0;
err2:
/*
* If necessary, free_netdev() will call netif_napi_del() and undo
* the effects of xgene_enet_napi_add()'s calls to netif_napi_add().
*/
if (pdata->mdio_driver)
xgene_enet_phy_disconnect(pdata);
else if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII)
xgene_enet_mdio_remove(pdata);
err1:
xgene_enet_delete_desc_rings(pdata);
err:
free_netdev(ndev);
return ret;
}
static int xgene_enet_remove(struct platform_device *pdev)
{
struct xgene_enet_pdata *pdata;
struct net_device *ndev;
pdata = platform_get_drvdata(pdev);
ndev = pdata->ndev;
rtnl_lock();
if (netif_running(ndev))
dev_close(ndev);
rtnl_unlock();
if (pdata->mdio_driver)
xgene_enet_phy_disconnect(pdata);
else if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII)
xgene_enet_mdio_remove(pdata);
unregister_netdev(ndev);
pdata->port_ops->shutdown(pdata);
xgene_enet_delete_desc_rings(pdata);
free_netdev(ndev);
return 0;
}
static void xgene_enet_shutdown(struct platform_device *pdev)
{
struct xgene_enet_pdata *pdata;
pdata = platform_get_drvdata(pdev);
if (!pdata)
return;
if (!pdata->ndev)
return;
xgene_enet_remove(pdev);
}
#ifdef CONFIG_ACPI
static const struct acpi_device_id xgene_enet_acpi_match[] = {
{ "APMC0D05", XGENE_ENET1},
{ "APMC0D30", XGENE_ENET1},
{ "APMC0D31", XGENE_ENET1},
{ "APMC0D3F", XGENE_ENET1},
{ "APMC0D26", XGENE_ENET2},
{ "APMC0D25", XGENE_ENET2},
{ }
};
MODULE_DEVICE_TABLE(acpi, xgene_enet_acpi_match);
#endif
#ifdef CONFIG_OF
static const struct of_device_id xgene_enet_of_match[] = {
{.compatible = "apm,xgene-enet", .data = (void *)XGENE_ENET1},
{.compatible = "apm,xgene1-sgenet", .data = (void *)XGENE_ENET1},
{.compatible = "apm,xgene1-xgenet", .data = (void *)XGENE_ENET1},
{.compatible = "apm,xgene2-sgenet", .data = (void *)XGENE_ENET2},
{.compatible = "apm,xgene2-xgenet", .data = (void *)XGENE_ENET2},
{},
};
MODULE_DEVICE_TABLE(of, xgene_enet_of_match);
#endif
static struct platform_driver xgene_enet_driver = {
.driver = {
.name = "xgene-enet",
.of_match_table = of_match_ptr(xgene_enet_of_match),
.acpi_match_table = ACPI_PTR(xgene_enet_acpi_match),
},
.probe = xgene_enet_probe,
.remove = xgene_enet_remove,
.shutdown = xgene_enet_shutdown,
};
module_platform_driver(xgene_enet_driver);
MODULE_DESCRIPTION("APM X-Gene SoC Ethernet driver");
MODULE_VERSION(XGENE_DRV_VERSION);
MODULE_AUTHOR("Iyappan Subramanian <isubramanian@apm.com>");
MODULE_AUTHOR("Keyur Chudgar <kchudgar@apm.com>");
MODULE_LICENSE("GPL");