linux/drivers/net/ethernet/renesas/ravb_main.c

2245 lines
56 KiB
C
Raw Normal View History

/* Renesas Ethernet AVB device driver
*
* Copyright (C) 2014-2015 Renesas Electronics Corporation
* Copyright (C) 2015 Renesas Solutions Corp.
* Copyright (C) 2015-2016 Cogent Embedded, Inc. <source@cogentembedded.com>
*
* Based on the SuperH Ethernet driver
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*/
#include <linux/cache.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/if_vlan.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/net_tstamp.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/of_mdio.h>
#include <linux/of_net.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/sys_soc.h>
#include <asm/div64.h>
#include "ravb.h"
#define RAVB_DEF_MSG_ENABLE \
(NETIF_MSG_LINK | \
NETIF_MSG_TIMER | \
NETIF_MSG_RX_ERR | \
NETIF_MSG_TX_ERR)
ravb: Add dma queue interrupt support This patch supports the following interrupts. - One interrupt for multiple (timestamp, error, gPTP) - One interrupt for emac - Four interrupts for dma queue (best effort rx/tx, network control rx/tx) This patch improve efficiency of the interrupt handler by adding the interrupt handler corresponding to each interrupt source described above. Additionally, it reduces the number of times of the access to EthernetAVB IF. Also this patch prevent this driver depends on the whim of a boot loader. [ykaneko0929@gmail.com: define bit names of registers] [ykaneko0929@gmail.com: add comment for gen3 only registers] [ykaneko0929@gmail.com: fix coding style] [ykaneko0929@gmail.com: update changelog] [ykaneko0929@gmail.com: gen3: fix initialization of interrupts] [ykaneko0929@gmail.com: gen3: fix clearing interrupts] [ykaneko0929@gmail.com: gen3: add helper function for request_irq()] [ykaneko0929@gmail.com: gen3: remove IRQF_SHARED flag for request_irq()] [ykaneko0929@gmail.com: revert ravb_close() and ravb_ptp_stop()] [ykaneko0929@gmail.com: avoid calling free_irq() to non-hooked interrupts] [ykaneko0929@gmail.com: make NC/BE interrupt handler a function] [ykaneko0929@gmail.com: make timestamp interrupt handler a function] [ykaneko0929@gmail.com: timestamp interrupt is handled in multiple interrupt handler instead of dma queue interrupt handler] Signed-off-by: Kazuya Mizuguchi <kazuya.mizuguchi.ks@renesas.com> Signed-off-by: Yoshihiro Kaneko <ykaneko0929@gmail.com> Acked-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2016-04-03 22:54:38 +08:00
static const char *ravb_rx_irqs[NUM_RX_QUEUE] = {
"ch0", /* RAVB_BE */
"ch1", /* RAVB_NC */
};
static const char *ravb_tx_irqs[NUM_TX_QUEUE] = {
"ch18", /* RAVB_BE */
"ch19", /* RAVB_NC */
};
void ravb_modify(struct net_device *ndev, enum ravb_reg reg, u32 clear,
u32 set)
{
ravb_write(ndev, (ravb_read(ndev, reg) & ~clear) | set, reg);
}
int ravb_wait(struct net_device *ndev, enum ravb_reg reg, u32 mask, u32 value)
{
int i;
for (i = 0; i < 10000; i++) {
if ((ravb_read(ndev, reg) & mask) == value)
return 0;
udelay(10);
}
return -ETIMEDOUT;
}
static int ravb_config(struct net_device *ndev)
{
int error;
/* Set config mode */
ravb_modify(ndev, CCC, CCC_OPC, CCC_OPC_CONFIG);
/* Check if the operating mode is changed to the config mode */
error = ravb_wait(ndev, CSR, CSR_OPS, CSR_OPS_CONFIG);
if (error)
netdev_err(ndev, "failed to switch device to config mode\n");
return error;
}
static void ravb_set_duplex(struct net_device *ndev)
{
struct ravb_private *priv = netdev_priv(ndev);
ravb_modify(ndev, ECMR, ECMR_DM, priv->duplex ? ECMR_DM : 0);
}
static void ravb_set_rate(struct net_device *ndev)
{
struct ravb_private *priv = netdev_priv(ndev);
switch (priv->speed) {
case 100: /* 100BASE */
ravb_write(ndev, GECMR_SPEED_100, GECMR);
break;
case 1000: /* 1000BASE */
ravb_write(ndev, GECMR_SPEED_1000, GECMR);
break;
}
}
static void ravb_set_buffer_align(struct sk_buff *skb)
{
u32 reserve = (unsigned long)skb->data & (RAVB_ALIGN - 1);
if (reserve)
skb_reserve(skb, RAVB_ALIGN - reserve);
}
/* Get MAC address from the MAC address registers
*
* Ethernet AVB device doesn't have ROM for MAC address.
* This function gets the MAC address that was used by a bootloader.
*/
static void ravb_read_mac_address(struct net_device *ndev, const u8 *mac)
{
if (mac) {
ether_addr_copy(ndev->dev_addr, mac);
} else {
u32 mahr = ravb_read(ndev, MAHR);
u32 malr = ravb_read(ndev, MALR);
ndev->dev_addr[0] = (mahr >> 24) & 0xFF;
ndev->dev_addr[1] = (mahr >> 16) & 0xFF;
ndev->dev_addr[2] = (mahr >> 8) & 0xFF;
ndev->dev_addr[3] = (mahr >> 0) & 0xFF;
ndev->dev_addr[4] = (malr >> 8) & 0xFF;
ndev->dev_addr[5] = (malr >> 0) & 0xFF;
}
}
static void ravb_mdio_ctrl(struct mdiobb_ctrl *ctrl, u32 mask, int set)
{
struct ravb_private *priv = container_of(ctrl, struct ravb_private,
mdiobb);
ravb_modify(priv->ndev, PIR, mask, set ? mask : 0);
}
/* MDC pin control */
static void ravb_set_mdc(struct mdiobb_ctrl *ctrl, int level)
{
ravb_mdio_ctrl(ctrl, PIR_MDC, level);
}
/* Data I/O pin control */
static void ravb_set_mdio_dir(struct mdiobb_ctrl *ctrl, int output)
{
ravb_mdio_ctrl(ctrl, PIR_MMD, output);
}
/* Set data bit */
static void ravb_set_mdio_data(struct mdiobb_ctrl *ctrl, int value)
{
ravb_mdio_ctrl(ctrl, PIR_MDO, value);
}
/* Get data bit */
static int ravb_get_mdio_data(struct mdiobb_ctrl *ctrl)
{
struct ravb_private *priv = container_of(ctrl, struct ravb_private,
mdiobb);
return (ravb_read(priv->ndev, PIR) & PIR_MDI) != 0;
}
/* MDIO bus control struct */
static struct mdiobb_ops bb_ops = {
.owner = THIS_MODULE,
.set_mdc = ravb_set_mdc,
.set_mdio_dir = ravb_set_mdio_dir,
.set_mdio_data = ravb_set_mdio_data,
.get_mdio_data = ravb_get_mdio_data,
};
/* Free TX skb function for AVB-IP */
static int ravb_tx_free(struct net_device *ndev, int q, bool free_txed_only)
{
struct ravb_private *priv = netdev_priv(ndev);
struct net_device_stats *stats = &priv->stats[q];
struct ravb_tx_desc *desc;
int free_num = 0;
int entry;
u32 size;
for (; priv->cur_tx[q] - priv->dirty_tx[q] > 0; priv->dirty_tx[q]++) {
bool txed;
entry = priv->dirty_tx[q] % (priv->num_tx_ring[q] *
NUM_TX_DESC);
desc = &priv->tx_ring[q][entry];
txed = desc->die_dt == DT_FEMPTY;
if (free_txed_only && !txed)
break;
/* Descriptor type must be checked before all other reads */
dma_rmb();
size = le16_to_cpu(desc->ds_tagl) & TX_DS;
/* Free the original skb. */
if (priv->tx_skb[q][entry / NUM_TX_DESC]) {
dma_unmap_single(ndev->dev.parent, le32_to_cpu(desc->dptr),
size, DMA_TO_DEVICE);
/* Last packet descriptor? */
if (entry % NUM_TX_DESC == NUM_TX_DESC - 1) {
entry /= NUM_TX_DESC;
dev_kfree_skb_any(priv->tx_skb[q][entry]);
priv->tx_skb[q][entry] = NULL;
if (txed)
stats->tx_packets++;
}
free_num++;
}
if (txed)
stats->tx_bytes += size;
desc->die_dt = DT_EEMPTY;
}
return free_num;
}
/* Free skb's and DMA buffers for Ethernet AVB */
static void ravb_ring_free(struct net_device *ndev, int q)
{
struct ravb_private *priv = netdev_priv(ndev);
int ring_size;
int i;
/* Free RX skb ringbuffer */
if (priv->rx_skb[q]) {
for (i = 0; i < priv->num_rx_ring[q]; i++)
dev_kfree_skb(priv->rx_skb[q][i]);
}
kfree(priv->rx_skb[q]);
priv->rx_skb[q] = NULL;
/* Free aligned TX buffers */
kfree(priv->tx_align[q]);
priv->tx_align[q] = NULL;
if (priv->rx_ring[q]) {
for (i = 0; i < priv->num_rx_ring[q]; i++) {
struct ravb_ex_rx_desc *desc = &priv->rx_ring[q][i];
if (!dma_mapping_error(ndev->dev.parent,
le32_to_cpu(desc->dptr)))
dma_unmap_single(ndev->dev.parent,
le32_to_cpu(desc->dptr),
PKT_BUF_SZ,
DMA_FROM_DEVICE);
}
ring_size = sizeof(struct ravb_ex_rx_desc) *
(priv->num_rx_ring[q] + 1);
dma_free_coherent(ndev->dev.parent, ring_size, priv->rx_ring[q],
priv->rx_desc_dma[q]);
priv->rx_ring[q] = NULL;
}
if (priv->tx_ring[q]) {
ravb_tx_free(ndev, q, false);
ring_size = sizeof(struct ravb_tx_desc) *
(priv->num_tx_ring[q] * NUM_TX_DESC + 1);
dma_free_coherent(ndev->dev.parent, ring_size, priv->tx_ring[q],
priv->tx_desc_dma[q]);
priv->tx_ring[q] = NULL;
}
/* Free TX skb ringbuffer.
* SKBs are freed by ravb_tx_free() call above.
*/
kfree(priv->tx_skb[q]);
priv->tx_skb[q] = NULL;
}
/* Format skb and descriptor buffer for Ethernet AVB */
static void ravb_ring_format(struct net_device *ndev, int q)
{
struct ravb_private *priv = netdev_priv(ndev);
struct ravb_ex_rx_desc *rx_desc;
struct ravb_tx_desc *tx_desc;
struct ravb_desc *desc;
int rx_ring_size = sizeof(*rx_desc) * priv->num_rx_ring[q];
int tx_ring_size = sizeof(*tx_desc) * priv->num_tx_ring[q] *
NUM_TX_DESC;
dma_addr_t dma_addr;
int i;
priv->cur_rx[q] = 0;
priv->cur_tx[q] = 0;
priv->dirty_rx[q] = 0;
priv->dirty_tx[q] = 0;
memset(priv->rx_ring[q], 0, rx_ring_size);
/* Build RX ring buffer */
for (i = 0; i < priv->num_rx_ring[q]; i++) {
/* RX descriptor */
rx_desc = &priv->rx_ring[q][i];
rx_desc->ds_cc = cpu_to_le16(PKT_BUF_SZ);
dma_addr = dma_map_single(ndev->dev.parent, priv->rx_skb[q][i]->data,
PKT_BUF_SZ,
DMA_FROM_DEVICE);
/* We just set the data size to 0 for a failed mapping which
* should prevent DMA from happening...
*/
if (dma_mapping_error(ndev->dev.parent, dma_addr))
rx_desc->ds_cc = cpu_to_le16(0);
rx_desc->dptr = cpu_to_le32(dma_addr);
rx_desc->die_dt = DT_FEMPTY;
}
rx_desc = &priv->rx_ring[q][i];
rx_desc->dptr = cpu_to_le32((u32)priv->rx_desc_dma[q]);
rx_desc->die_dt = DT_LINKFIX; /* type */
memset(priv->tx_ring[q], 0, tx_ring_size);
/* Build TX ring buffer */
for (i = 0, tx_desc = priv->tx_ring[q]; i < priv->num_tx_ring[q];
i++, tx_desc++) {
tx_desc->die_dt = DT_EEMPTY;
tx_desc++;
tx_desc->die_dt = DT_EEMPTY;
}
tx_desc->dptr = cpu_to_le32((u32)priv->tx_desc_dma[q]);
tx_desc->die_dt = DT_LINKFIX; /* type */
/* RX descriptor base address for best effort */
desc = &priv->desc_bat[RX_QUEUE_OFFSET + q];
desc->die_dt = DT_LINKFIX; /* type */
desc->dptr = cpu_to_le32((u32)priv->rx_desc_dma[q]);
/* TX descriptor base address for best effort */
desc = &priv->desc_bat[q];
desc->die_dt = DT_LINKFIX; /* type */
desc->dptr = cpu_to_le32((u32)priv->tx_desc_dma[q]);
}
/* Init skb and descriptor buffer for Ethernet AVB */
static int ravb_ring_init(struct net_device *ndev, int q)
{
struct ravb_private *priv = netdev_priv(ndev);
struct sk_buff *skb;
int ring_size;
int i;
/* Allocate RX and TX skb rings */
priv->rx_skb[q] = kcalloc(priv->num_rx_ring[q],
sizeof(*priv->rx_skb[q]), GFP_KERNEL);
priv->tx_skb[q] = kcalloc(priv->num_tx_ring[q],
sizeof(*priv->tx_skb[q]), GFP_KERNEL);
if (!priv->rx_skb[q] || !priv->tx_skb[q])
goto error;
for (i = 0; i < priv->num_rx_ring[q]; i++) {
skb = netdev_alloc_skb(ndev, PKT_BUF_SZ + RAVB_ALIGN - 1);
if (!skb)
goto error;
ravb_set_buffer_align(skb);
priv->rx_skb[q][i] = skb;
}
/* Allocate rings for the aligned buffers */
priv->tx_align[q] = kmalloc(DPTR_ALIGN * priv->num_tx_ring[q] +
DPTR_ALIGN - 1, GFP_KERNEL);
if (!priv->tx_align[q])
goto error;
/* Allocate all RX descriptors. */
ring_size = sizeof(struct ravb_ex_rx_desc) * (priv->num_rx_ring[q] + 1);
priv->rx_ring[q] = dma_alloc_coherent(ndev->dev.parent, ring_size,
&priv->rx_desc_dma[q],
GFP_KERNEL);
if (!priv->rx_ring[q])
goto error;
priv->dirty_rx[q] = 0;
/* Allocate all TX descriptors. */
ring_size = sizeof(struct ravb_tx_desc) *
(priv->num_tx_ring[q] * NUM_TX_DESC + 1);
priv->tx_ring[q] = dma_alloc_coherent(ndev->dev.parent, ring_size,
&priv->tx_desc_dma[q],
GFP_KERNEL);
if (!priv->tx_ring[q])
goto error;
return 0;
error:
ravb_ring_free(ndev, q);
return -ENOMEM;
}
/* E-MAC init function */
static void ravb_emac_init(struct net_device *ndev)
{
struct ravb_private *priv = netdev_priv(ndev);
/* Receive frame limit set register */
ravb_write(ndev, ndev->mtu + ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN, RFLR);
/* PAUSE prohibition */
ravb_write(ndev, ECMR_ZPF | (priv->duplex ? ECMR_DM : 0) |
ECMR_TE | ECMR_RE, ECMR);
ravb_set_rate(ndev);
/* Set MAC address */
ravb_write(ndev,
(ndev->dev_addr[0] << 24) | (ndev->dev_addr[1] << 16) |
(ndev->dev_addr[2] << 8) | (ndev->dev_addr[3]), MAHR);
ravb_write(ndev,
(ndev->dev_addr[4] << 8) | (ndev->dev_addr[5]), MALR);
/* E-MAC status register clear */
ravb_write(ndev, ECSR_ICD | ECSR_MPD, ECSR);
/* E-MAC interrupt enable register */
ravb_write(ndev, ECSIPR_ICDIP | ECSIPR_MPDIP | ECSIPR_LCHNGIP, ECSIPR);
}
/* Device init function for Ethernet AVB */
static int ravb_dmac_init(struct net_device *ndev)
{
ravb: Add dma queue interrupt support This patch supports the following interrupts. - One interrupt for multiple (timestamp, error, gPTP) - One interrupt for emac - Four interrupts for dma queue (best effort rx/tx, network control rx/tx) This patch improve efficiency of the interrupt handler by adding the interrupt handler corresponding to each interrupt source described above. Additionally, it reduces the number of times of the access to EthernetAVB IF. Also this patch prevent this driver depends on the whim of a boot loader. [ykaneko0929@gmail.com: define bit names of registers] [ykaneko0929@gmail.com: add comment for gen3 only registers] [ykaneko0929@gmail.com: fix coding style] [ykaneko0929@gmail.com: update changelog] [ykaneko0929@gmail.com: gen3: fix initialization of interrupts] [ykaneko0929@gmail.com: gen3: fix clearing interrupts] [ykaneko0929@gmail.com: gen3: add helper function for request_irq()] [ykaneko0929@gmail.com: gen3: remove IRQF_SHARED flag for request_irq()] [ykaneko0929@gmail.com: revert ravb_close() and ravb_ptp_stop()] [ykaneko0929@gmail.com: avoid calling free_irq() to non-hooked interrupts] [ykaneko0929@gmail.com: make NC/BE interrupt handler a function] [ykaneko0929@gmail.com: make timestamp interrupt handler a function] [ykaneko0929@gmail.com: timestamp interrupt is handled in multiple interrupt handler instead of dma queue interrupt handler] Signed-off-by: Kazuya Mizuguchi <kazuya.mizuguchi.ks@renesas.com> Signed-off-by: Yoshihiro Kaneko <ykaneko0929@gmail.com> Acked-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2016-04-03 22:54:38 +08:00
struct ravb_private *priv = netdev_priv(ndev);
int error;
/* Set CONFIG mode */
error = ravb_config(ndev);
if (error)
return error;
error = ravb_ring_init(ndev, RAVB_BE);
if (error)
return error;
error = ravb_ring_init(ndev, RAVB_NC);
if (error) {
ravb_ring_free(ndev, RAVB_BE);
return error;
}
/* Descriptor format */
ravb_ring_format(ndev, RAVB_BE);
ravb_ring_format(ndev, RAVB_NC);
#if defined(__LITTLE_ENDIAN)
ravb_modify(ndev, CCC, CCC_BOC, 0);
#else
ravb_modify(ndev, CCC, CCC_BOC, CCC_BOC);
#endif
/* Set AVB RX */
ravb_write(ndev,
RCR_EFFS | RCR_ENCF | RCR_ETS0 | RCR_ESF | 0x18000000, RCR);
/* Set FIFO size */
ravb_write(ndev, TGC_TQP_AVBMODE1 | 0x00222200, TGC);
/* Timestamp enable */
ravb_write(ndev, TCCR_TFEN, TCCR);
/* Interrupt init: */
ravb: Add dma queue interrupt support This patch supports the following interrupts. - One interrupt for multiple (timestamp, error, gPTP) - One interrupt for emac - Four interrupts for dma queue (best effort rx/tx, network control rx/tx) This patch improve efficiency of the interrupt handler by adding the interrupt handler corresponding to each interrupt source described above. Additionally, it reduces the number of times of the access to EthernetAVB IF. Also this patch prevent this driver depends on the whim of a boot loader. [ykaneko0929@gmail.com: define bit names of registers] [ykaneko0929@gmail.com: add comment for gen3 only registers] [ykaneko0929@gmail.com: fix coding style] [ykaneko0929@gmail.com: update changelog] [ykaneko0929@gmail.com: gen3: fix initialization of interrupts] [ykaneko0929@gmail.com: gen3: fix clearing interrupts] [ykaneko0929@gmail.com: gen3: add helper function for request_irq()] [ykaneko0929@gmail.com: gen3: remove IRQF_SHARED flag for request_irq()] [ykaneko0929@gmail.com: revert ravb_close() and ravb_ptp_stop()] [ykaneko0929@gmail.com: avoid calling free_irq() to non-hooked interrupts] [ykaneko0929@gmail.com: make NC/BE interrupt handler a function] [ykaneko0929@gmail.com: make timestamp interrupt handler a function] [ykaneko0929@gmail.com: timestamp interrupt is handled in multiple interrupt handler instead of dma queue interrupt handler] Signed-off-by: Kazuya Mizuguchi <kazuya.mizuguchi.ks@renesas.com> Signed-off-by: Yoshihiro Kaneko <ykaneko0929@gmail.com> Acked-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2016-04-03 22:54:38 +08:00
if (priv->chip_id == RCAR_GEN3) {
/* Clear DIL.DPLx */
ravb_write(ndev, 0, DIL);
/* Set queue specific interrupt */
ravb_write(ndev, CIE_CRIE | CIE_CTIE | CIE_CL0M, CIE);
}
/* Frame receive */
ravb_write(ndev, RIC0_FRE0 | RIC0_FRE1, RIC0);
/* Disable FIFO full warning */
ravb_write(ndev, 0, RIC1);
/* Receive FIFO full error, descriptor empty */
ravb_write(ndev, RIC2_QFE0 | RIC2_QFE1 | RIC2_RFFE, RIC2);
/* Frame transmitted, timestamp FIFO updated */
ravb_write(ndev, TIC_FTE0 | TIC_FTE1 | TIC_TFUE, TIC);
/* Setting the control will start the AVB-DMAC process. */
ravb_modify(ndev, CCC, CCC_OPC, CCC_OPC_OPERATION);
return 0;
}
static void ravb_get_tx_tstamp(struct net_device *ndev)
{
struct ravb_private *priv = netdev_priv(ndev);
struct ravb_tstamp_skb *ts_skb, *ts_skb2;
struct skb_shared_hwtstamps shhwtstamps;
struct sk_buff *skb;
struct timespec64 ts;
u16 tag, tfa_tag;
int count;
u32 tfa2;
count = (ravb_read(ndev, TSR) & TSR_TFFL) >> 8;
while (count--) {
tfa2 = ravb_read(ndev, TFA2);
tfa_tag = (tfa2 & TFA2_TST) >> 16;
ts.tv_nsec = (u64)ravb_read(ndev, TFA0);
ts.tv_sec = ((u64)(tfa2 & TFA2_TSV) << 32) |
ravb_read(ndev, TFA1);
memset(&shhwtstamps, 0, sizeof(shhwtstamps));
shhwtstamps.hwtstamp = timespec64_to_ktime(ts);
list_for_each_entry_safe(ts_skb, ts_skb2, &priv->ts_skb_list,
list) {
skb = ts_skb->skb;
tag = ts_skb->tag;
list_del(&ts_skb->list);
kfree(ts_skb);
if (tag == tfa_tag) {
skb_tstamp_tx(skb, &shhwtstamps);
break;
}
}
ravb_modify(ndev, TCCR, TCCR_TFR, TCCR_TFR);
}
}
/* Packet receive function for Ethernet AVB */
static bool ravb_rx(struct net_device *ndev, int *quota, int q)
{
struct ravb_private *priv = netdev_priv(ndev);
int entry = priv->cur_rx[q] % priv->num_rx_ring[q];
int boguscnt = (priv->dirty_rx[q] + priv->num_rx_ring[q]) -
priv->cur_rx[q];
struct net_device_stats *stats = &priv->stats[q];
struct ravb_ex_rx_desc *desc;
struct sk_buff *skb;
dma_addr_t dma_addr;
struct timespec64 ts;
u8 desc_status;
u16 pkt_len;
int limit;
boguscnt = min(boguscnt, *quota);
limit = boguscnt;
desc = &priv->rx_ring[q][entry];
while (desc->die_dt != DT_FEMPTY) {
/* Descriptor type must be checked before all other reads */
dma_rmb();
desc_status = desc->msc;
pkt_len = le16_to_cpu(desc->ds_cc) & RX_DS;
if (--boguscnt < 0)
break;
/* We use 0-byte descriptors to mark the DMA mapping errors */
if (!pkt_len)
continue;
if (desc_status & MSC_MC)
stats->multicast++;
if (desc_status & (MSC_CRC | MSC_RFE | MSC_RTSF | MSC_RTLF |
MSC_CEEF)) {
stats->rx_errors++;
if (desc_status & MSC_CRC)
stats->rx_crc_errors++;
if (desc_status & MSC_RFE)
stats->rx_frame_errors++;
if (desc_status & (MSC_RTLF | MSC_RTSF))
stats->rx_length_errors++;
if (desc_status & MSC_CEEF)
stats->rx_missed_errors++;
} else {
u32 get_ts = priv->tstamp_rx_ctrl & RAVB_RXTSTAMP_TYPE;
skb = priv->rx_skb[q][entry];
priv->rx_skb[q][entry] = NULL;
dma_unmap_single(ndev->dev.parent, le32_to_cpu(desc->dptr),
PKT_BUF_SZ,
DMA_FROM_DEVICE);
get_ts &= (q == RAVB_NC) ?
RAVB_RXTSTAMP_TYPE_V2_L2_EVENT :
~RAVB_RXTSTAMP_TYPE_V2_L2_EVENT;
if (get_ts) {
struct skb_shared_hwtstamps *shhwtstamps;
shhwtstamps = skb_hwtstamps(skb);
memset(shhwtstamps, 0, sizeof(*shhwtstamps));
ts.tv_sec = ((u64) le16_to_cpu(desc->ts_sh) <<
32) | le32_to_cpu(desc->ts_sl);
ts.tv_nsec = le32_to_cpu(desc->ts_n);
shhwtstamps->hwtstamp = timespec64_to_ktime(ts);
}
skb_put(skb, pkt_len);
skb->protocol = eth_type_trans(skb, ndev);
napi_gro_receive(&priv->napi[q], skb);
stats->rx_packets++;
stats->rx_bytes += pkt_len;
}
entry = (++priv->cur_rx[q]) % priv->num_rx_ring[q];
desc = &priv->rx_ring[q][entry];
}
/* Refill the RX ring buffers. */
for (; priv->cur_rx[q] - priv->dirty_rx[q] > 0; priv->dirty_rx[q]++) {
entry = priv->dirty_rx[q] % priv->num_rx_ring[q];
desc = &priv->rx_ring[q][entry];
desc->ds_cc = cpu_to_le16(PKT_BUF_SZ);
if (!priv->rx_skb[q][entry]) {
skb = netdev_alloc_skb(ndev,
PKT_BUF_SZ + RAVB_ALIGN - 1);
if (!skb)
break; /* Better luck next round. */
ravb_set_buffer_align(skb);
dma_addr = dma_map_single(ndev->dev.parent, skb->data,
le16_to_cpu(desc->ds_cc),
DMA_FROM_DEVICE);
skb_checksum_none_assert(skb);
/* We just set the data size to 0 for a failed mapping
* which should prevent DMA from happening...
*/
if (dma_mapping_error(ndev->dev.parent, dma_addr))
desc->ds_cc = cpu_to_le16(0);
desc->dptr = cpu_to_le32(dma_addr);
priv->rx_skb[q][entry] = skb;
}
/* Descriptor type must be set after all the above writes */
dma_wmb();
desc->die_dt = DT_FEMPTY;
}
*quota -= limit - (++boguscnt);
return boguscnt <= 0;
}
static void ravb_rcv_snd_disable(struct net_device *ndev)
{
/* Disable TX and RX */
ravb_modify(ndev, ECMR, ECMR_RE | ECMR_TE, 0);
}
static void ravb_rcv_snd_enable(struct net_device *ndev)
{
/* Enable TX and RX */
ravb_modify(ndev, ECMR, ECMR_RE | ECMR_TE, ECMR_RE | ECMR_TE);
}
/* function for waiting dma process finished */
static int ravb_stop_dma(struct net_device *ndev)
{
int error;
/* Wait for stopping the hardware TX process */
error = ravb_wait(ndev, TCCR,
TCCR_TSRQ0 | TCCR_TSRQ1 | TCCR_TSRQ2 | TCCR_TSRQ3, 0);
if (error)
return error;
error = ravb_wait(ndev, CSR, CSR_TPO0 | CSR_TPO1 | CSR_TPO2 | CSR_TPO3,
0);
if (error)
return error;
/* Stop the E-MAC's RX/TX processes. */
ravb_rcv_snd_disable(ndev);
/* Wait for stopping the RX DMA process */
error = ravb_wait(ndev, CSR, CSR_RPO, 0);
if (error)
return error;
/* Stop AVB-DMAC process */
return ravb_config(ndev);
}
/* E-MAC interrupt handler */
ravb: Add dma queue interrupt support This patch supports the following interrupts. - One interrupt for multiple (timestamp, error, gPTP) - One interrupt for emac - Four interrupts for dma queue (best effort rx/tx, network control rx/tx) This patch improve efficiency of the interrupt handler by adding the interrupt handler corresponding to each interrupt source described above. Additionally, it reduces the number of times of the access to EthernetAVB IF. Also this patch prevent this driver depends on the whim of a boot loader. [ykaneko0929@gmail.com: define bit names of registers] [ykaneko0929@gmail.com: add comment for gen3 only registers] [ykaneko0929@gmail.com: fix coding style] [ykaneko0929@gmail.com: update changelog] [ykaneko0929@gmail.com: gen3: fix initialization of interrupts] [ykaneko0929@gmail.com: gen3: fix clearing interrupts] [ykaneko0929@gmail.com: gen3: add helper function for request_irq()] [ykaneko0929@gmail.com: gen3: remove IRQF_SHARED flag for request_irq()] [ykaneko0929@gmail.com: revert ravb_close() and ravb_ptp_stop()] [ykaneko0929@gmail.com: avoid calling free_irq() to non-hooked interrupts] [ykaneko0929@gmail.com: make NC/BE interrupt handler a function] [ykaneko0929@gmail.com: make timestamp interrupt handler a function] [ykaneko0929@gmail.com: timestamp interrupt is handled in multiple interrupt handler instead of dma queue interrupt handler] Signed-off-by: Kazuya Mizuguchi <kazuya.mizuguchi.ks@renesas.com> Signed-off-by: Yoshihiro Kaneko <ykaneko0929@gmail.com> Acked-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2016-04-03 22:54:38 +08:00
static void ravb_emac_interrupt_unlocked(struct net_device *ndev)
{
struct ravb_private *priv = netdev_priv(ndev);
u32 ecsr, psr;
ecsr = ravb_read(ndev, ECSR);
ravb_write(ndev, ecsr, ECSR); /* clear interrupt */
if (ecsr & ECSR_ICD)
ndev->stats.tx_carrier_errors++;
if (ecsr & ECSR_LCHNG) {
/* Link changed */
if (priv->no_avb_link)
return;
psr = ravb_read(ndev, PSR);
if (priv->avb_link_active_low)
psr ^= PSR_LMON;
if (!(psr & PSR_LMON)) {
/* DIsable RX and TX */
ravb_rcv_snd_disable(ndev);
} else {
/* Enable RX and TX */
ravb_rcv_snd_enable(ndev);
}
}
}
ravb: Add dma queue interrupt support This patch supports the following interrupts. - One interrupt for multiple (timestamp, error, gPTP) - One interrupt for emac - Four interrupts for dma queue (best effort rx/tx, network control rx/tx) This patch improve efficiency of the interrupt handler by adding the interrupt handler corresponding to each interrupt source described above. Additionally, it reduces the number of times of the access to EthernetAVB IF. Also this patch prevent this driver depends on the whim of a boot loader. [ykaneko0929@gmail.com: define bit names of registers] [ykaneko0929@gmail.com: add comment for gen3 only registers] [ykaneko0929@gmail.com: fix coding style] [ykaneko0929@gmail.com: update changelog] [ykaneko0929@gmail.com: gen3: fix initialization of interrupts] [ykaneko0929@gmail.com: gen3: fix clearing interrupts] [ykaneko0929@gmail.com: gen3: add helper function for request_irq()] [ykaneko0929@gmail.com: gen3: remove IRQF_SHARED flag for request_irq()] [ykaneko0929@gmail.com: revert ravb_close() and ravb_ptp_stop()] [ykaneko0929@gmail.com: avoid calling free_irq() to non-hooked interrupts] [ykaneko0929@gmail.com: make NC/BE interrupt handler a function] [ykaneko0929@gmail.com: make timestamp interrupt handler a function] [ykaneko0929@gmail.com: timestamp interrupt is handled in multiple interrupt handler instead of dma queue interrupt handler] Signed-off-by: Kazuya Mizuguchi <kazuya.mizuguchi.ks@renesas.com> Signed-off-by: Yoshihiro Kaneko <ykaneko0929@gmail.com> Acked-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2016-04-03 22:54:38 +08:00
static irqreturn_t ravb_emac_interrupt(int irq, void *dev_id)
{
struct net_device *ndev = dev_id;
struct ravb_private *priv = netdev_priv(ndev);
spin_lock(&priv->lock);
ravb_emac_interrupt_unlocked(ndev);
mmiowb();
spin_unlock(&priv->lock);
return IRQ_HANDLED;
}
/* Error interrupt handler */
static void ravb_error_interrupt(struct net_device *ndev)
{
struct ravb_private *priv = netdev_priv(ndev);
u32 eis, ris2;
eis = ravb_read(ndev, EIS);
ravb_write(ndev, ~EIS_QFS, EIS);
if (eis & EIS_QFS) {
ris2 = ravb_read(ndev, RIS2);
ravb_write(ndev, ~(RIS2_QFF0 | RIS2_RFFF), RIS2);
/* Receive Descriptor Empty int */
if (ris2 & RIS2_QFF0)
priv->stats[RAVB_BE].rx_over_errors++;
/* Receive Descriptor Empty int */
if (ris2 & RIS2_QFF1)
priv->stats[RAVB_NC].rx_over_errors++;
/* Receive FIFO Overflow int */
if (ris2 & RIS2_RFFF)
priv->rx_fifo_errors++;
}
}
ravb: Add dma queue interrupt support This patch supports the following interrupts. - One interrupt for multiple (timestamp, error, gPTP) - One interrupt for emac - Four interrupts for dma queue (best effort rx/tx, network control rx/tx) This patch improve efficiency of the interrupt handler by adding the interrupt handler corresponding to each interrupt source described above. Additionally, it reduces the number of times of the access to EthernetAVB IF. Also this patch prevent this driver depends on the whim of a boot loader. [ykaneko0929@gmail.com: define bit names of registers] [ykaneko0929@gmail.com: add comment for gen3 only registers] [ykaneko0929@gmail.com: fix coding style] [ykaneko0929@gmail.com: update changelog] [ykaneko0929@gmail.com: gen3: fix initialization of interrupts] [ykaneko0929@gmail.com: gen3: fix clearing interrupts] [ykaneko0929@gmail.com: gen3: add helper function for request_irq()] [ykaneko0929@gmail.com: gen3: remove IRQF_SHARED flag for request_irq()] [ykaneko0929@gmail.com: revert ravb_close() and ravb_ptp_stop()] [ykaneko0929@gmail.com: avoid calling free_irq() to non-hooked interrupts] [ykaneko0929@gmail.com: make NC/BE interrupt handler a function] [ykaneko0929@gmail.com: make timestamp interrupt handler a function] [ykaneko0929@gmail.com: timestamp interrupt is handled in multiple interrupt handler instead of dma queue interrupt handler] Signed-off-by: Kazuya Mizuguchi <kazuya.mizuguchi.ks@renesas.com> Signed-off-by: Yoshihiro Kaneko <ykaneko0929@gmail.com> Acked-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2016-04-03 22:54:38 +08:00
static bool ravb_queue_interrupt(struct net_device *ndev, int q)
{
struct ravb_private *priv = netdev_priv(ndev);
u32 ris0 = ravb_read(ndev, RIS0);
u32 ric0 = ravb_read(ndev, RIC0);
u32 tis = ravb_read(ndev, TIS);
u32 tic = ravb_read(ndev, TIC);
if (((ris0 & ric0) & BIT(q)) || ((tis & tic) & BIT(q))) {
if (napi_schedule_prep(&priv->napi[q])) {
/* Mask RX and TX interrupts */
if (priv->chip_id == RCAR_GEN2) {
ravb_write(ndev, ric0 & ~BIT(q), RIC0);
ravb_write(ndev, tic & ~BIT(q), TIC);
} else {
ravb_write(ndev, BIT(q), RID0);
ravb_write(ndev, BIT(q), TID);
}
__napi_schedule(&priv->napi[q]);
} else {
netdev_warn(ndev,
"ignoring interrupt, rx status 0x%08x, rx mask 0x%08x,\n",
ris0, ric0);
netdev_warn(ndev,
" tx status 0x%08x, tx mask 0x%08x.\n",
tis, tic);
}
return true;
}
return false;
}
static bool ravb_timestamp_interrupt(struct net_device *ndev)
{
u32 tis = ravb_read(ndev, TIS);
if (tis & TIS_TFUF) {
ravb_write(ndev, ~TIS_TFUF, TIS);
ravb_get_tx_tstamp(ndev);
return true;
}
return false;
}
static irqreturn_t ravb_interrupt(int irq, void *dev_id)
{
struct net_device *ndev = dev_id;
struct ravb_private *priv = netdev_priv(ndev);
irqreturn_t result = IRQ_NONE;
u32 iss;
spin_lock(&priv->lock);
/* Get interrupt status */
iss = ravb_read(ndev, ISS);
/* Received and transmitted interrupts */
if (iss & (ISS_FRS | ISS_FTS | ISS_TFUS)) {
int q;
/* Timestamp updated */
ravb: Add dma queue interrupt support This patch supports the following interrupts. - One interrupt for multiple (timestamp, error, gPTP) - One interrupt for emac - Four interrupts for dma queue (best effort rx/tx, network control rx/tx) This patch improve efficiency of the interrupt handler by adding the interrupt handler corresponding to each interrupt source described above. Additionally, it reduces the number of times of the access to EthernetAVB IF. Also this patch prevent this driver depends on the whim of a boot loader. [ykaneko0929@gmail.com: define bit names of registers] [ykaneko0929@gmail.com: add comment for gen3 only registers] [ykaneko0929@gmail.com: fix coding style] [ykaneko0929@gmail.com: update changelog] [ykaneko0929@gmail.com: gen3: fix initialization of interrupts] [ykaneko0929@gmail.com: gen3: fix clearing interrupts] [ykaneko0929@gmail.com: gen3: add helper function for request_irq()] [ykaneko0929@gmail.com: gen3: remove IRQF_SHARED flag for request_irq()] [ykaneko0929@gmail.com: revert ravb_close() and ravb_ptp_stop()] [ykaneko0929@gmail.com: avoid calling free_irq() to non-hooked interrupts] [ykaneko0929@gmail.com: make NC/BE interrupt handler a function] [ykaneko0929@gmail.com: make timestamp interrupt handler a function] [ykaneko0929@gmail.com: timestamp interrupt is handled in multiple interrupt handler instead of dma queue interrupt handler] Signed-off-by: Kazuya Mizuguchi <kazuya.mizuguchi.ks@renesas.com> Signed-off-by: Yoshihiro Kaneko <ykaneko0929@gmail.com> Acked-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2016-04-03 22:54:38 +08:00
if (ravb_timestamp_interrupt(ndev))
result = IRQ_HANDLED;
/* Network control and best effort queue RX/TX */
for (q = RAVB_NC; q >= RAVB_BE; q--) {
ravb: Add dma queue interrupt support This patch supports the following interrupts. - One interrupt for multiple (timestamp, error, gPTP) - One interrupt for emac - Four interrupts for dma queue (best effort rx/tx, network control rx/tx) This patch improve efficiency of the interrupt handler by adding the interrupt handler corresponding to each interrupt source described above. Additionally, it reduces the number of times of the access to EthernetAVB IF. Also this patch prevent this driver depends on the whim of a boot loader. [ykaneko0929@gmail.com: define bit names of registers] [ykaneko0929@gmail.com: add comment for gen3 only registers] [ykaneko0929@gmail.com: fix coding style] [ykaneko0929@gmail.com: update changelog] [ykaneko0929@gmail.com: gen3: fix initialization of interrupts] [ykaneko0929@gmail.com: gen3: fix clearing interrupts] [ykaneko0929@gmail.com: gen3: add helper function for request_irq()] [ykaneko0929@gmail.com: gen3: remove IRQF_SHARED flag for request_irq()] [ykaneko0929@gmail.com: revert ravb_close() and ravb_ptp_stop()] [ykaneko0929@gmail.com: avoid calling free_irq() to non-hooked interrupts] [ykaneko0929@gmail.com: make NC/BE interrupt handler a function] [ykaneko0929@gmail.com: make timestamp interrupt handler a function] [ykaneko0929@gmail.com: timestamp interrupt is handled in multiple interrupt handler instead of dma queue interrupt handler] Signed-off-by: Kazuya Mizuguchi <kazuya.mizuguchi.ks@renesas.com> Signed-off-by: Yoshihiro Kaneko <ykaneko0929@gmail.com> Acked-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2016-04-03 22:54:38 +08:00
if (ravb_queue_interrupt(ndev, q))
result = IRQ_HANDLED;
}
}
/* E-MAC status summary */
if (iss & ISS_MS) {
ravb: Add dma queue interrupt support This patch supports the following interrupts. - One interrupt for multiple (timestamp, error, gPTP) - One interrupt for emac - Four interrupts for dma queue (best effort rx/tx, network control rx/tx) This patch improve efficiency of the interrupt handler by adding the interrupt handler corresponding to each interrupt source described above. Additionally, it reduces the number of times of the access to EthernetAVB IF. Also this patch prevent this driver depends on the whim of a boot loader. [ykaneko0929@gmail.com: define bit names of registers] [ykaneko0929@gmail.com: add comment for gen3 only registers] [ykaneko0929@gmail.com: fix coding style] [ykaneko0929@gmail.com: update changelog] [ykaneko0929@gmail.com: gen3: fix initialization of interrupts] [ykaneko0929@gmail.com: gen3: fix clearing interrupts] [ykaneko0929@gmail.com: gen3: add helper function for request_irq()] [ykaneko0929@gmail.com: gen3: remove IRQF_SHARED flag for request_irq()] [ykaneko0929@gmail.com: revert ravb_close() and ravb_ptp_stop()] [ykaneko0929@gmail.com: avoid calling free_irq() to non-hooked interrupts] [ykaneko0929@gmail.com: make NC/BE interrupt handler a function] [ykaneko0929@gmail.com: make timestamp interrupt handler a function] [ykaneko0929@gmail.com: timestamp interrupt is handled in multiple interrupt handler instead of dma queue interrupt handler] Signed-off-by: Kazuya Mizuguchi <kazuya.mizuguchi.ks@renesas.com> Signed-off-by: Yoshihiro Kaneko <ykaneko0929@gmail.com> Acked-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2016-04-03 22:54:38 +08:00
ravb_emac_interrupt_unlocked(ndev);
result = IRQ_HANDLED;
}
/* Error status summary */
if (iss & ISS_ES) {
ravb_error_interrupt(ndev);
result = IRQ_HANDLED;
}
ravb: Add dma queue interrupt support This patch supports the following interrupts. - One interrupt for multiple (timestamp, error, gPTP) - One interrupt for emac - Four interrupts for dma queue (best effort rx/tx, network control rx/tx) This patch improve efficiency of the interrupt handler by adding the interrupt handler corresponding to each interrupt source described above. Additionally, it reduces the number of times of the access to EthernetAVB IF. Also this patch prevent this driver depends on the whim of a boot loader. [ykaneko0929@gmail.com: define bit names of registers] [ykaneko0929@gmail.com: add comment for gen3 only registers] [ykaneko0929@gmail.com: fix coding style] [ykaneko0929@gmail.com: update changelog] [ykaneko0929@gmail.com: gen3: fix initialization of interrupts] [ykaneko0929@gmail.com: gen3: fix clearing interrupts] [ykaneko0929@gmail.com: gen3: add helper function for request_irq()] [ykaneko0929@gmail.com: gen3: remove IRQF_SHARED flag for request_irq()] [ykaneko0929@gmail.com: revert ravb_close() and ravb_ptp_stop()] [ykaneko0929@gmail.com: avoid calling free_irq() to non-hooked interrupts] [ykaneko0929@gmail.com: make NC/BE interrupt handler a function] [ykaneko0929@gmail.com: make timestamp interrupt handler a function] [ykaneko0929@gmail.com: timestamp interrupt is handled in multiple interrupt handler instead of dma queue interrupt handler] Signed-off-by: Kazuya Mizuguchi <kazuya.mizuguchi.ks@renesas.com> Signed-off-by: Yoshihiro Kaneko <ykaneko0929@gmail.com> Acked-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2016-04-03 22:54:38 +08:00
/* gPTP interrupt status summary */
if (iss & ISS_CGIS) {
ravb_ptp_interrupt(ndev);
result = IRQ_HANDLED;
}
mmiowb();
spin_unlock(&priv->lock);
return result;
}
ravb: Add dma queue interrupt support This patch supports the following interrupts. - One interrupt for multiple (timestamp, error, gPTP) - One interrupt for emac - Four interrupts for dma queue (best effort rx/tx, network control rx/tx) This patch improve efficiency of the interrupt handler by adding the interrupt handler corresponding to each interrupt source described above. Additionally, it reduces the number of times of the access to EthernetAVB IF. Also this patch prevent this driver depends on the whim of a boot loader. [ykaneko0929@gmail.com: define bit names of registers] [ykaneko0929@gmail.com: add comment for gen3 only registers] [ykaneko0929@gmail.com: fix coding style] [ykaneko0929@gmail.com: update changelog] [ykaneko0929@gmail.com: gen3: fix initialization of interrupts] [ykaneko0929@gmail.com: gen3: fix clearing interrupts] [ykaneko0929@gmail.com: gen3: add helper function for request_irq()] [ykaneko0929@gmail.com: gen3: remove IRQF_SHARED flag for request_irq()] [ykaneko0929@gmail.com: revert ravb_close() and ravb_ptp_stop()] [ykaneko0929@gmail.com: avoid calling free_irq() to non-hooked interrupts] [ykaneko0929@gmail.com: make NC/BE interrupt handler a function] [ykaneko0929@gmail.com: make timestamp interrupt handler a function] [ykaneko0929@gmail.com: timestamp interrupt is handled in multiple interrupt handler instead of dma queue interrupt handler] Signed-off-by: Kazuya Mizuguchi <kazuya.mizuguchi.ks@renesas.com> Signed-off-by: Yoshihiro Kaneko <ykaneko0929@gmail.com> Acked-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2016-04-03 22:54:38 +08:00
/* Timestamp/Error/gPTP interrupt handler */
static irqreturn_t ravb_multi_interrupt(int irq, void *dev_id)
{
struct net_device *ndev = dev_id;
struct ravb_private *priv = netdev_priv(ndev);
irqreturn_t result = IRQ_NONE;
u32 iss;
spin_lock(&priv->lock);
/* Get interrupt status */
iss = ravb_read(ndev, ISS);
/* Timestamp updated */
if ((iss & ISS_TFUS) && ravb_timestamp_interrupt(ndev))
result = IRQ_HANDLED;
/* Error status summary */
if (iss & ISS_ES) {
ravb_error_interrupt(ndev);
result = IRQ_HANDLED;
}
/* gPTP interrupt status summary */
if (iss & ISS_CGIS) {
ravb_ptp_interrupt(ndev);
ravb: Add dma queue interrupt support This patch supports the following interrupts. - One interrupt for multiple (timestamp, error, gPTP) - One interrupt for emac - Four interrupts for dma queue (best effort rx/tx, network control rx/tx) This patch improve efficiency of the interrupt handler by adding the interrupt handler corresponding to each interrupt source described above. Additionally, it reduces the number of times of the access to EthernetAVB IF. Also this patch prevent this driver depends on the whim of a boot loader. [ykaneko0929@gmail.com: define bit names of registers] [ykaneko0929@gmail.com: add comment for gen3 only registers] [ykaneko0929@gmail.com: fix coding style] [ykaneko0929@gmail.com: update changelog] [ykaneko0929@gmail.com: gen3: fix initialization of interrupts] [ykaneko0929@gmail.com: gen3: fix clearing interrupts] [ykaneko0929@gmail.com: gen3: add helper function for request_irq()] [ykaneko0929@gmail.com: gen3: remove IRQF_SHARED flag for request_irq()] [ykaneko0929@gmail.com: revert ravb_close() and ravb_ptp_stop()] [ykaneko0929@gmail.com: avoid calling free_irq() to non-hooked interrupts] [ykaneko0929@gmail.com: make NC/BE interrupt handler a function] [ykaneko0929@gmail.com: make timestamp interrupt handler a function] [ykaneko0929@gmail.com: timestamp interrupt is handled in multiple interrupt handler instead of dma queue interrupt handler] Signed-off-by: Kazuya Mizuguchi <kazuya.mizuguchi.ks@renesas.com> Signed-off-by: Yoshihiro Kaneko <ykaneko0929@gmail.com> Acked-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2016-04-03 22:54:38 +08:00
result = IRQ_HANDLED;
}
ravb: Add dma queue interrupt support This patch supports the following interrupts. - One interrupt for multiple (timestamp, error, gPTP) - One interrupt for emac - Four interrupts for dma queue (best effort rx/tx, network control rx/tx) This patch improve efficiency of the interrupt handler by adding the interrupt handler corresponding to each interrupt source described above. Additionally, it reduces the number of times of the access to EthernetAVB IF. Also this patch prevent this driver depends on the whim of a boot loader. [ykaneko0929@gmail.com: define bit names of registers] [ykaneko0929@gmail.com: add comment for gen3 only registers] [ykaneko0929@gmail.com: fix coding style] [ykaneko0929@gmail.com: update changelog] [ykaneko0929@gmail.com: gen3: fix initialization of interrupts] [ykaneko0929@gmail.com: gen3: fix clearing interrupts] [ykaneko0929@gmail.com: gen3: add helper function for request_irq()] [ykaneko0929@gmail.com: gen3: remove IRQF_SHARED flag for request_irq()] [ykaneko0929@gmail.com: revert ravb_close() and ravb_ptp_stop()] [ykaneko0929@gmail.com: avoid calling free_irq() to non-hooked interrupts] [ykaneko0929@gmail.com: make NC/BE interrupt handler a function] [ykaneko0929@gmail.com: make timestamp interrupt handler a function] [ykaneko0929@gmail.com: timestamp interrupt is handled in multiple interrupt handler instead of dma queue interrupt handler] Signed-off-by: Kazuya Mizuguchi <kazuya.mizuguchi.ks@renesas.com> Signed-off-by: Yoshihiro Kaneko <ykaneko0929@gmail.com> Acked-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2016-04-03 22:54:38 +08:00
mmiowb();
spin_unlock(&priv->lock);
return result;
}
static irqreturn_t ravb_dma_interrupt(int irq, void *dev_id, int q)
{
struct net_device *ndev = dev_id;
struct ravb_private *priv = netdev_priv(ndev);
irqreturn_t result = IRQ_NONE;
spin_lock(&priv->lock);
/* Network control/Best effort queue RX/TX */
if (ravb_queue_interrupt(ndev, q))
result = IRQ_HANDLED;
mmiowb();
spin_unlock(&priv->lock);
return result;
}
static irqreturn_t ravb_be_interrupt(int irq, void *dev_id)
{
return ravb_dma_interrupt(irq, dev_id, RAVB_BE);
}
static irqreturn_t ravb_nc_interrupt(int irq, void *dev_id)
{
return ravb_dma_interrupt(irq, dev_id, RAVB_NC);
}
static int ravb_poll(struct napi_struct *napi, int budget)
{
struct net_device *ndev = napi->dev;
struct ravb_private *priv = netdev_priv(ndev);
unsigned long flags;
int q = napi - priv->napi;
int mask = BIT(q);
int quota = budget;
u32 ris0, tis;
for (;;) {
tis = ravb_read(ndev, TIS);
ris0 = ravb_read(ndev, RIS0);
if (!((ris0 & mask) || (tis & mask)))
break;
/* Processing RX Descriptor Ring */
if (ris0 & mask) {
/* Clear RX interrupt */
ravb_write(ndev, ~mask, RIS0);
if (ravb_rx(ndev, &quota, q))
goto out;
}
/* Processing TX Descriptor Ring */
if (tis & mask) {
spin_lock_irqsave(&priv->lock, flags);
/* Clear TX interrupt */
ravb_write(ndev, ~mask, TIS);
ravb_tx_free(ndev, q, true);
netif_wake_subqueue(ndev, q);
mmiowb();
spin_unlock_irqrestore(&priv->lock, flags);
}
}
napi_complete(napi);
/* Re-enable RX/TX interrupts */
spin_lock_irqsave(&priv->lock, flags);
ravb: Add dma queue interrupt support This patch supports the following interrupts. - One interrupt for multiple (timestamp, error, gPTP) - One interrupt for emac - Four interrupts for dma queue (best effort rx/tx, network control rx/tx) This patch improve efficiency of the interrupt handler by adding the interrupt handler corresponding to each interrupt source described above. Additionally, it reduces the number of times of the access to EthernetAVB IF. Also this patch prevent this driver depends on the whim of a boot loader. [ykaneko0929@gmail.com: define bit names of registers] [ykaneko0929@gmail.com: add comment for gen3 only registers] [ykaneko0929@gmail.com: fix coding style] [ykaneko0929@gmail.com: update changelog] [ykaneko0929@gmail.com: gen3: fix initialization of interrupts] [ykaneko0929@gmail.com: gen3: fix clearing interrupts] [ykaneko0929@gmail.com: gen3: add helper function for request_irq()] [ykaneko0929@gmail.com: gen3: remove IRQF_SHARED flag for request_irq()] [ykaneko0929@gmail.com: revert ravb_close() and ravb_ptp_stop()] [ykaneko0929@gmail.com: avoid calling free_irq() to non-hooked interrupts] [ykaneko0929@gmail.com: make NC/BE interrupt handler a function] [ykaneko0929@gmail.com: make timestamp interrupt handler a function] [ykaneko0929@gmail.com: timestamp interrupt is handled in multiple interrupt handler instead of dma queue interrupt handler] Signed-off-by: Kazuya Mizuguchi <kazuya.mizuguchi.ks@renesas.com> Signed-off-by: Yoshihiro Kaneko <ykaneko0929@gmail.com> Acked-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2016-04-03 22:54:38 +08:00
if (priv->chip_id == RCAR_GEN2) {
ravb_modify(ndev, RIC0, mask, mask);
ravb_modify(ndev, TIC, mask, mask);
} else {
ravb_write(ndev, mask, RIE0);
ravb_write(ndev, mask, TIE);
}
mmiowb();
spin_unlock_irqrestore(&priv->lock, flags);
/* Receive error message handling */
priv->rx_over_errors = priv->stats[RAVB_BE].rx_over_errors;
priv->rx_over_errors += priv->stats[RAVB_NC].rx_over_errors;
if (priv->rx_over_errors != ndev->stats.rx_over_errors)
ndev->stats.rx_over_errors = priv->rx_over_errors;
if (priv->rx_fifo_errors != ndev->stats.rx_fifo_errors)
ndev->stats.rx_fifo_errors = priv->rx_fifo_errors;
out:
return budget - quota;
}
/* PHY state control function */
static void ravb_adjust_link(struct net_device *ndev)
{
struct ravb_private *priv = netdev_priv(ndev);
struct phy_device *phydev = ndev->phydev;
bool new_state = false;
if (phydev->link) {
if (phydev->duplex != priv->duplex) {
new_state = true;
priv->duplex = phydev->duplex;
ravb_set_duplex(ndev);
}
if (phydev->speed != priv->speed) {
new_state = true;
priv->speed = phydev->speed;
ravb_set_rate(ndev);
}
if (!priv->link) {
ravb_modify(ndev, ECMR, ECMR_TXF, 0);
new_state = true;
priv->link = phydev->link;
if (priv->no_avb_link)
ravb_rcv_snd_enable(ndev);
}
} else if (priv->link) {
new_state = true;
priv->link = 0;
priv->speed = 0;
priv->duplex = -1;
if (priv->no_avb_link)
ravb_rcv_snd_disable(ndev);
}
if (new_state && netif_msg_link(priv))
phy_print_status(phydev);
}
static const struct soc_device_attribute r8a7795es10[] = {
{ .soc_id = "r8a7795", .revision = "ES1.0", },
{ /* sentinel */ }
};
/* PHY init function */
static int ravb_phy_init(struct net_device *ndev)
{
struct device_node *np = ndev->dev.parent->of_node;
struct ravb_private *priv = netdev_priv(ndev);
struct phy_device *phydev;
struct device_node *pn;
int err;
priv->link = 0;
priv->speed = 0;
priv->duplex = -1;
/* Try connecting to PHY */
pn = of_parse_phandle(np, "phy-handle", 0);
if (!pn) {
/* In the case of a fixed PHY, the DT node associated
* to the PHY is the Ethernet MAC DT node.
*/
if (of_phy_is_fixed_link(np)) {
err = of_phy_register_fixed_link(np);
if (err)
return err;
}
pn = of_node_get(np);
}
phydev = of_phy_connect(ndev, pn, ravb_adjust_link, 0,
priv->phy_interface);
of_node_put(pn);
if (!phydev) {
netdev_err(ndev, "failed to connect PHY\n");
err = -ENOENT;
goto err_deregister_fixed_link;
}
/* This driver only support 10/100Mbit speeds on R-Car H3 ES1.0
* at this time.
*/
if (soc_device_match(r8a7795es10)) {
err = phy_set_max_speed(phydev, SPEED_100);
if (err) {
netdev_err(ndev, "failed to limit PHY to 100Mbit/s\n");
goto err_phy_disconnect;
}
netdev_info(ndev, "limited PHY to 100Mbit/s\n");
}
/* 10BASE is not supported */
phydev->supported &= ~PHY_10BT_FEATURES;
phy_attached_info(phydev);
return 0;
err_phy_disconnect:
phy_disconnect(phydev);
err_deregister_fixed_link:
if (of_phy_is_fixed_link(np))
of_phy_deregister_fixed_link(np);
return err;
}
/* PHY control start function */
static int ravb_phy_start(struct net_device *ndev)
{
int error;
error = ravb_phy_init(ndev);
if (error)
return error;
phy_start(ndev->phydev);
return 0;
}
static int ravb_get_link_ksettings(struct net_device *ndev,
struct ethtool_link_ksettings *cmd)
{
struct ravb_private *priv = netdev_priv(ndev);
int error = -ENODEV;
unsigned long flags;
if (ndev->phydev) {
spin_lock_irqsave(&priv->lock, flags);
error = phy_ethtool_ksettings_get(ndev->phydev, cmd);
spin_unlock_irqrestore(&priv->lock, flags);
}
return error;
}
static int ravb_set_link_ksettings(struct net_device *ndev,
const struct ethtool_link_ksettings *cmd)
{
struct ravb_private *priv = netdev_priv(ndev);
unsigned long flags;
int error;
if (!ndev->phydev)
return -ENODEV;
spin_lock_irqsave(&priv->lock, flags);
/* Disable TX and RX */
ravb_rcv_snd_disable(ndev);
error = phy_ethtool_ksettings_set(ndev->phydev, cmd);
if (error)
goto error_exit;
if (cmd->base.duplex == DUPLEX_FULL)
priv->duplex = 1;
else
priv->duplex = 0;
ravb_set_duplex(ndev);
error_exit:
mdelay(1);
/* Enable TX and RX */
ravb_rcv_snd_enable(ndev);
mmiowb();
spin_unlock_irqrestore(&priv->lock, flags);
return error;
}
static int ravb_nway_reset(struct net_device *ndev)
{
struct ravb_private *priv = netdev_priv(ndev);
int error = -ENODEV;
unsigned long flags;
if (ndev->phydev) {
spin_lock_irqsave(&priv->lock, flags);
error = phy_start_aneg(ndev->phydev);
spin_unlock_irqrestore(&priv->lock, flags);
}
return error;
}
static u32 ravb_get_msglevel(struct net_device *ndev)
{
struct ravb_private *priv = netdev_priv(ndev);
return priv->msg_enable;
}
static void ravb_set_msglevel(struct net_device *ndev, u32 value)
{
struct ravb_private *priv = netdev_priv(ndev);
priv->msg_enable = value;
}
static const char ravb_gstrings_stats[][ETH_GSTRING_LEN] = {
"rx_queue_0_current",
"tx_queue_0_current",
"rx_queue_0_dirty",
"tx_queue_0_dirty",
"rx_queue_0_packets",
"tx_queue_0_packets",
"rx_queue_0_bytes",
"tx_queue_0_bytes",
"rx_queue_0_mcast_packets",
"rx_queue_0_errors",
"rx_queue_0_crc_errors",
"rx_queue_0_frame_errors",
"rx_queue_0_length_errors",
"rx_queue_0_missed_errors",
"rx_queue_0_over_errors",
"rx_queue_1_current",
"tx_queue_1_current",
"rx_queue_1_dirty",
"tx_queue_1_dirty",
"rx_queue_1_packets",
"tx_queue_1_packets",
"rx_queue_1_bytes",
"tx_queue_1_bytes",
"rx_queue_1_mcast_packets",
"rx_queue_1_errors",
"rx_queue_1_crc_errors",
"rx_queue_1_frame_errors",
"rx_queue_1_length_errors",
"rx_queue_1_missed_errors",
"rx_queue_1_over_errors",
};
#define RAVB_STATS_LEN ARRAY_SIZE(ravb_gstrings_stats)
static int ravb_get_sset_count(struct net_device *netdev, int sset)
{
switch (sset) {
case ETH_SS_STATS:
return RAVB_STATS_LEN;
default:
return -EOPNOTSUPP;
}
}
static void ravb_get_ethtool_stats(struct net_device *ndev,
struct ethtool_stats *stats, u64 *data)
{
struct ravb_private *priv = netdev_priv(ndev);
int i = 0;
int q;
/* Device-specific stats */
for (q = RAVB_BE; q < NUM_RX_QUEUE; q++) {
struct net_device_stats *stats = &priv->stats[q];
data[i++] = priv->cur_rx[q];
data[i++] = priv->cur_tx[q];
data[i++] = priv->dirty_rx[q];
data[i++] = priv->dirty_tx[q];
data[i++] = stats->rx_packets;
data[i++] = stats->tx_packets;
data[i++] = stats->rx_bytes;
data[i++] = stats->tx_bytes;
data[i++] = stats->multicast;
data[i++] = stats->rx_errors;
data[i++] = stats->rx_crc_errors;
data[i++] = stats->rx_frame_errors;
data[i++] = stats->rx_length_errors;
data[i++] = stats->rx_missed_errors;
data[i++] = stats->rx_over_errors;
}
}
static void ravb_get_strings(struct net_device *ndev, u32 stringset, u8 *data)
{
switch (stringset) {
case ETH_SS_STATS:
memcpy(data, *ravb_gstrings_stats, sizeof(ravb_gstrings_stats));
break;
}
}
static void ravb_get_ringparam(struct net_device *ndev,
struct ethtool_ringparam *ring)
{
struct ravb_private *priv = netdev_priv(ndev);
ring->rx_max_pending = BE_RX_RING_MAX;
ring->tx_max_pending = BE_TX_RING_MAX;
ring->rx_pending = priv->num_rx_ring[RAVB_BE];
ring->tx_pending = priv->num_tx_ring[RAVB_BE];
}
static int ravb_set_ringparam(struct net_device *ndev,
struct ethtool_ringparam *ring)
{
struct ravb_private *priv = netdev_priv(ndev);
int error;
if (ring->tx_pending > BE_TX_RING_MAX ||
ring->rx_pending > BE_RX_RING_MAX ||
ring->tx_pending < BE_TX_RING_MIN ||
ring->rx_pending < BE_RX_RING_MIN)
return -EINVAL;
if (ring->rx_mini_pending || ring->rx_jumbo_pending)
return -EINVAL;
if (netif_running(ndev)) {
netif_device_detach(ndev);
/* Stop PTP Clock driver */
if (priv->chip_id == RCAR_GEN2)
ravb_ptp_stop(ndev);
/* Wait for DMA stopping */
error = ravb_stop_dma(ndev);
if (error) {
netdev_err(ndev,
"cannot set ringparam! Any AVB processes are still running?\n");
return error;
}
synchronize_irq(ndev->irq);
/* Free all the skb's in the RX queue and the DMA buffers. */
ravb_ring_free(ndev, RAVB_BE);
ravb_ring_free(ndev, RAVB_NC);
}
/* Set new parameters */
priv->num_rx_ring[RAVB_BE] = ring->rx_pending;
priv->num_tx_ring[RAVB_BE] = ring->tx_pending;
if (netif_running(ndev)) {
error = ravb_dmac_init(ndev);
if (error) {
netdev_err(ndev,
"%s: ravb_dmac_init() failed, error %d\n",
__func__, error);
return error;
}
ravb_emac_init(ndev);
/* Initialise PTP Clock driver */
if (priv->chip_id == RCAR_GEN2)
ravb_ptp_init(ndev, priv->pdev);
netif_device_attach(ndev);
}
return 0;
}
static int ravb_get_ts_info(struct net_device *ndev,
struct ethtool_ts_info *info)
{
struct ravb_private *priv = netdev_priv(ndev);
info->so_timestamping =
SOF_TIMESTAMPING_TX_SOFTWARE |
SOF_TIMESTAMPING_RX_SOFTWARE |
SOF_TIMESTAMPING_SOFTWARE |
SOF_TIMESTAMPING_TX_HARDWARE |
SOF_TIMESTAMPING_RX_HARDWARE |
SOF_TIMESTAMPING_RAW_HARDWARE;
info->tx_types = (1 << HWTSTAMP_TX_OFF) | (1 << HWTSTAMP_TX_ON);
info->rx_filters =
(1 << HWTSTAMP_FILTER_NONE) |
(1 << HWTSTAMP_FILTER_PTP_V2_L2_EVENT) |
(1 << HWTSTAMP_FILTER_ALL);
info->phc_index = ptp_clock_index(priv->ptp.clock);
return 0;
}
static const struct ethtool_ops ravb_ethtool_ops = {
.nway_reset = ravb_nway_reset,
.get_msglevel = ravb_get_msglevel,
.set_msglevel = ravb_set_msglevel,
.get_link = ethtool_op_get_link,
.get_strings = ravb_get_strings,
.get_ethtool_stats = ravb_get_ethtool_stats,
.get_sset_count = ravb_get_sset_count,
.get_ringparam = ravb_get_ringparam,
.set_ringparam = ravb_set_ringparam,
.get_ts_info = ravb_get_ts_info,
.get_link_ksettings = ravb_get_link_ksettings,
.set_link_ksettings = ravb_set_link_ksettings,
};
ravb: Add dma queue interrupt support This patch supports the following interrupts. - One interrupt for multiple (timestamp, error, gPTP) - One interrupt for emac - Four interrupts for dma queue (best effort rx/tx, network control rx/tx) This patch improve efficiency of the interrupt handler by adding the interrupt handler corresponding to each interrupt source described above. Additionally, it reduces the number of times of the access to EthernetAVB IF. Also this patch prevent this driver depends on the whim of a boot loader. [ykaneko0929@gmail.com: define bit names of registers] [ykaneko0929@gmail.com: add comment for gen3 only registers] [ykaneko0929@gmail.com: fix coding style] [ykaneko0929@gmail.com: update changelog] [ykaneko0929@gmail.com: gen3: fix initialization of interrupts] [ykaneko0929@gmail.com: gen3: fix clearing interrupts] [ykaneko0929@gmail.com: gen3: add helper function for request_irq()] [ykaneko0929@gmail.com: gen3: remove IRQF_SHARED flag for request_irq()] [ykaneko0929@gmail.com: revert ravb_close() and ravb_ptp_stop()] [ykaneko0929@gmail.com: avoid calling free_irq() to non-hooked interrupts] [ykaneko0929@gmail.com: make NC/BE interrupt handler a function] [ykaneko0929@gmail.com: make timestamp interrupt handler a function] [ykaneko0929@gmail.com: timestamp interrupt is handled in multiple interrupt handler instead of dma queue interrupt handler] Signed-off-by: Kazuya Mizuguchi <kazuya.mizuguchi.ks@renesas.com> Signed-off-by: Yoshihiro Kaneko <ykaneko0929@gmail.com> Acked-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2016-04-03 22:54:38 +08:00
static inline int ravb_hook_irq(unsigned int irq, irq_handler_t handler,
struct net_device *ndev, struct device *dev,
const char *ch)
{
char *name;
int error;
name = devm_kasprintf(dev, GFP_KERNEL, "%s:%s", ndev->name, ch);
if (!name)
return -ENOMEM;
error = request_irq(irq, handler, 0, name, ndev);
if (error)
netdev_err(ndev, "cannot request IRQ %s\n", name);
return error;
}
/* Network device open function for Ethernet AVB */
static int ravb_open(struct net_device *ndev)
{
struct ravb_private *priv = netdev_priv(ndev);
ravb: Add dma queue interrupt support This patch supports the following interrupts. - One interrupt for multiple (timestamp, error, gPTP) - One interrupt for emac - Four interrupts for dma queue (best effort rx/tx, network control rx/tx) This patch improve efficiency of the interrupt handler by adding the interrupt handler corresponding to each interrupt source described above. Additionally, it reduces the number of times of the access to EthernetAVB IF. Also this patch prevent this driver depends on the whim of a boot loader. [ykaneko0929@gmail.com: define bit names of registers] [ykaneko0929@gmail.com: add comment for gen3 only registers] [ykaneko0929@gmail.com: fix coding style] [ykaneko0929@gmail.com: update changelog] [ykaneko0929@gmail.com: gen3: fix initialization of interrupts] [ykaneko0929@gmail.com: gen3: fix clearing interrupts] [ykaneko0929@gmail.com: gen3: add helper function for request_irq()] [ykaneko0929@gmail.com: gen3: remove IRQF_SHARED flag for request_irq()] [ykaneko0929@gmail.com: revert ravb_close() and ravb_ptp_stop()] [ykaneko0929@gmail.com: avoid calling free_irq() to non-hooked interrupts] [ykaneko0929@gmail.com: make NC/BE interrupt handler a function] [ykaneko0929@gmail.com: make timestamp interrupt handler a function] [ykaneko0929@gmail.com: timestamp interrupt is handled in multiple interrupt handler instead of dma queue interrupt handler] Signed-off-by: Kazuya Mizuguchi <kazuya.mizuguchi.ks@renesas.com> Signed-off-by: Yoshihiro Kaneko <ykaneko0929@gmail.com> Acked-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2016-04-03 22:54:38 +08:00
struct platform_device *pdev = priv->pdev;
struct device *dev = &pdev->dev;
int error;
napi_enable(&priv->napi[RAVB_BE]);
napi_enable(&priv->napi[RAVB_NC]);
ravb: Add dma queue interrupt support This patch supports the following interrupts. - One interrupt for multiple (timestamp, error, gPTP) - One interrupt for emac - Four interrupts for dma queue (best effort rx/tx, network control rx/tx) This patch improve efficiency of the interrupt handler by adding the interrupt handler corresponding to each interrupt source described above. Additionally, it reduces the number of times of the access to EthernetAVB IF. Also this patch prevent this driver depends on the whim of a boot loader. [ykaneko0929@gmail.com: define bit names of registers] [ykaneko0929@gmail.com: add comment for gen3 only registers] [ykaneko0929@gmail.com: fix coding style] [ykaneko0929@gmail.com: update changelog] [ykaneko0929@gmail.com: gen3: fix initialization of interrupts] [ykaneko0929@gmail.com: gen3: fix clearing interrupts] [ykaneko0929@gmail.com: gen3: add helper function for request_irq()] [ykaneko0929@gmail.com: gen3: remove IRQF_SHARED flag for request_irq()] [ykaneko0929@gmail.com: revert ravb_close() and ravb_ptp_stop()] [ykaneko0929@gmail.com: avoid calling free_irq() to non-hooked interrupts] [ykaneko0929@gmail.com: make NC/BE interrupt handler a function] [ykaneko0929@gmail.com: make timestamp interrupt handler a function] [ykaneko0929@gmail.com: timestamp interrupt is handled in multiple interrupt handler instead of dma queue interrupt handler] Signed-off-by: Kazuya Mizuguchi <kazuya.mizuguchi.ks@renesas.com> Signed-off-by: Yoshihiro Kaneko <ykaneko0929@gmail.com> Acked-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2016-04-03 22:54:38 +08:00
if (priv->chip_id == RCAR_GEN2) {
error = request_irq(ndev->irq, ravb_interrupt, IRQF_SHARED,
ndev->name, ndev);
if (error) {
netdev_err(ndev, "cannot request IRQ\n");
ravb: Add dma queue interrupt support This patch supports the following interrupts. - One interrupt for multiple (timestamp, error, gPTP) - One interrupt for emac - Four interrupts for dma queue (best effort rx/tx, network control rx/tx) This patch improve efficiency of the interrupt handler by adding the interrupt handler corresponding to each interrupt source described above. Additionally, it reduces the number of times of the access to EthernetAVB IF. Also this patch prevent this driver depends on the whim of a boot loader. [ykaneko0929@gmail.com: define bit names of registers] [ykaneko0929@gmail.com: add comment for gen3 only registers] [ykaneko0929@gmail.com: fix coding style] [ykaneko0929@gmail.com: update changelog] [ykaneko0929@gmail.com: gen3: fix initialization of interrupts] [ykaneko0929@gmail.com: gen3: fix clearing interrupts] [ykaneko0929@gmail.com: gen3: add helper function for request_irq()] [ykaneko0929@gmail.com: gen3: remove IRQF_SHARED flag for request_irq()] [ykaneko0929@gmail.com: revert ravb_close() and ravb_ptp_stop()] [ykaneko0929@gmail.com: avoid calling free_irq() to non-hooked interrupts] [ykaneko0929@gmail.com: make NC/BE interrupt handler a function] [ykaneko0929@gmail.com: make timestamp interrupt handler a function] [ykaneko0929@gmail.com: timestamp interrupt is handled in multiple interrupt handler instead of dma queue interrupt handler] Signed-off-by: Kazuya Mizuguchi <kazuya.mizuguchi.ks@renesas.com> Signed-off-by: Yoshihiro Kaneko <ykaneko0929@gmail.com> Acked-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2016-04-03 22:54:38 +08:00
goto out_napi_off;
}
ravb: Add dma queue interrupt support This patch supports the following interrupts. - One interrupt for multiple (timestamp, error, gPTP) - One interrupt for emac - Four interrupts for dma queue (best effort rx/tx, network control rx/tx) This patch improve efficiency of the interrupt handler by adding the interrupt handler corresponding to each interrupt source described above. Additionally, it reduces the number of times of the access to EthernetAVB IF. Also this patch prevent this driver depends on the whim of a boot loader. [ykaneko0929@gmail.com: define bit names of registers] [ykaneko0929@gmail.com: add comment for gen3 only registers] [ykaneko0929@gmail.com: fix coding style] [ykaneko0929@gmail.com: update changelog] [ykaneko0929@gmail.com: gen3: fix initialization of interrupts] [ykaneko0929@gmail.com: gen3: fix clearing interrupts] [ykaneko0929@gmail.com: gen3: add helper function for request_irq()] [ykaneko0929@gmail.com: gen3: remove IRQF_SHARED flag for request_irq()] [ykaneko0929@gmail.com: revert ravb_close() and ravb_ptp_stop()] [ykaneko0929@gmail.com: avoid calling free_irq() to non-hooked interrupts] [ykaneko0929@gmail.com: make NC/BE interrupt handler a function] [ykaneko0929@gmail.com: make timestamp interrupt handler a function] [ykaneko0929@gmail.com: timestamp interrupt is handled in multiple interrupt handler instead of dma queue interrupt handler] Signed-off-by: Kazuya Mizuguchi <kazuya.mizuguchi.ks@renesas.com> Signed-off-by: Yoshihiro Kaneko <ykaneko0929@gmail.com> Acked-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2016-04-03 22:54:38 +08:00
} else {
error = ravb_hook_irq(ndev->irq, ravb_multi_interrupt, ndev,
dev, "ch22:multi");
if (error)
goto out_napi_off;
error = ravb_hook_irq(priv->emac_irq, ravb_emac_interrupt, ndev,
dev, "ch24:emac");
if (error)
goto out_free_irq;
error = ravb_hook_irq(priv->rx_irqs[RAVB_BE], ravb_be_interrupt,
ndev, dev, "ch0:rx_be");
if (error)
goto out_free_irq_emac;
error = ravb_hook_irq(priv->tx_irqs[RAVB_BE], ravb_be_interrupt,
ndev, dev, "ch18:tx_be");
if (error)
goto out_free_irq_be_rx;
error = ravb_hook_irq(priv->rx_irqs[RAVB_NC], ravb_nc_interrupt,
ndev, dev, "ch1:rx_nc");
if (error)
goto out_free_irq_be_tx;
error = ravb_hook_irq(priv->tx_irqs[RAVB_NC], ravb_nc_interrupt,
ndev, dev, "ch19:tx_nc");
if (error)
goto out_free_irq_nc_rx;
}
/* Device init */
error = ravb_dmac_init(ndev);
if (error)
ravb: Add dma queue interrupt support This patch supports the following interrupts. - One interrupt for multiple (timestamp, error, gPTP) - One interrupt for emac - Four interrupts for dma queue (best effort rx/tx, network control rx/tx) This patch improve efficiency of the interrupt handler by adding the interrupt handler corresponding to each interrupt source described above. Additionally, it reduces the number of times of the access to EthernetAVB IF. Also this patch prevent this driver depends on the whim of a boot loader. [ykaneko0929@gmail.com: define bit names of registers] [ykaneko0929@gmail.com: add comment for gen3 only registers] [ykaneko0929@gmail.com: fix coding style] [ykaneko0929@gmail.com: update changelog] [ykaneko0929@gmail.com: gen3: fix initialization of interrupts] [ykaneko0929@gmail.com: gen3: fix clearing interrupts] [ykaneko0929@gmail.com: gen3: add helper function for request_irq()] [ykaneko0929@gmail.com: gen3: remove IRQF_SHARED flag for request_irq()] [ykaneko0929@gmail.com: revert ravb_close() and ravb_ptp_stop()] [ykaneko0929@gmail.com: avoid calling free_irq() to non-hooked interrupts] [ykaneko0929@gmail.com: make NC/BE interrupt handler a function] [ykaneko0929@gmail.com: make timestamp interrupt handler a function] [ykaneko0929@gmail.com: timestamp interrupt is handled in multiple interrupt handler instead of dma queue interrupt handler] Signed-off-by: Kazuya Mizuguchi <kazuya.mizuguchi.ks@renesas.com> Signed-off-by: Yoshihiro Kaneko <ykaneko0929@gmail.com> Acked-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2016-04-03 22:54:38 +08:00
goto out_free_irq_nc_tx;
ravb_emac_init(ndev);
/* Initialise PTP Clock driver */
if (priv->chip_id == RCAR_GEN2)
ravb_ptp_init(ndev, priv->pdev);
netif_tx_start_all_queues(ndev);
/* PHY control start */
error = ravb_phy_start(ndev);
if (error)
goto out_ptp_stop;
return 0;
out_ptp_stop:
/* Stop PTP Clock driver */
if (priv->chip_id == RCAR_GEN2)
ravb_ptp_stop(ndev);
ravb: Add dma queue interrupt support This patch supports the following interrupts. - One interrupt for multiple (timestamp, error, gPTP) - One interrupt for emac - Four interrupts for dma queue (best effort rx/tx, network control rx/tx) This patch improve efficiency of the interrupt handler by adding the interrupt handler corresponding to each interrupt source described above. Additionally, it reduces the number of times of the access to EthernetAVB IF. Also this patch prevent this driver depends on the whim of a boot loader. [ykaneko0929@gmail.com: define bit names of registers] [ykaneko0929@gmail.com: add comment for gen3 only registers] [ykaneko0929@gmail.com: fix coding style] [ykaneko0929@gmail.com: update changelog] [ykaneko0929@gmail.com: gen3: fix initialization of interrupts] [ykaneko0929@gmail.com: gen3: fix clearing interrupts] [ykaneko0929@gmail.com: gen3: add helper function for request_irq()] [ykaneko0929@gmail.com: gen3: remove IRQF_SHARED flag for request_irq()] [ykaneko0929@gmail.com: revert ravb_close() and ravb_ptp_stop()] [ykaneko0929@gmail.com: avoid calling free_irq() to non-hooked interrupts] [ykaneko0929@gmail.com: make NC/BE interrupt handler a function] [ykaneko0929@gmail.com: make timestamp interrupt handler a function] [ykaneko0929@gmail.com: timestamp interrupt is handled in multiple interrupt handler instead of dma queue interrupt handler] Signed-off-by: Kazuya Mizuguchi <kazuya.mizuguchi.ks@renesas.com> Signed-off-by: Yoshihiro Kaneko <ykaneko0929@gmail.com> Acked-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2016-04-03 22:54:38 +08:00
out_free_irq_nc_tx:
if (priv->chip_id == RCAR_GEN2)
goto out_free_irq;
free_irq(priv->tx_irqs[RAVB_NC], ndev);
out_free_irq_nc_rx:
free_irq(priv->rx_irqs[RAVB_NC], ndev);
out_free_irq_be_tx:
free_irq(priv->tx_irqs[RAVB_BE], ndev);
out_free_irq_be_rx:
free_irq(priv->rx_irqs[RAVB_BE], ndev);
out_free_irq_emac:
free_irq(priv->emac_irq, ndev);
out_free_irq:
free_irq(ndev->irq, ndev);
out_napi_off:
napi_disable(&priv->napi[RAVB_NC]);
napi_disable(&priv->napi[RAVB_BE]);
return error;
}
/* Timeout function for Ethernet AVB */
static void ravb_tx_timeout(struct net_device *ndev)
{
struct ravb_private *priv = netdev_priv(ndev);
netif_err(priv, tx_err, ndev,
"transmit timed out, status %08x, resetting...\n",
ravb_read(ndev, ISS));
/* tx_errors count up */
ndev->stats.tx_errors++;
schedule_work(&priv->work);
}
static void ravb_tx_timeout_work(struct work_struct *work)
{
struct ravb_private *priv = container_of(work, struct ravb_private,
work);
struct net_device *ndev = priv->ndev;
netif_tx_stop_all_queues(ndev);
/* Stop PTP Clock driver */
if (priv->chip_id == RCAR_GEN2)
ravb_ptp_stop(ndev);
/* Wait for DMA stopping */
ravb_stop_dma(ndev);
ravb_ring_free(ndev, RAVB_BE);
ravb_ring_free(ndev, RAVB_NC);
/* Device init */
ravb_dmac_init(ndev);
ravb_emac_init(ndev);
/* Initialise PTP Clock driver */
if (priv->chip_id == RCAR_GEN2)
ravb_ptp_init(ndev, priv->pdev);
netif_tx_start_all_queues(ndev);
}
/* Packet transmit function for Ethernet AVB */
static netdev_tx_t ravb_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
struct ravb_private *priv = netdev_priv(ndev);
u16 q = skb_get_queue_mapping(skb);
struct ravb_tstamp_skb *ts_skb;
struct ravb_tx_desc *desc;
unsigned long flags;
u32 dma_addr;
void *buffer;
u32 entry;
u32 len;
spin_lock_irqsave(&priv->lock, flags);
if (priv->cur_tx[q] - priv->dirty_tx[q] > (priv->num_tx_ring[q] - 1) *
NUM_TX_DESC) {
netif_err(priv, tx_queued, ndev,
"still transmitting with the full ring!\n");
netif_stop_subqueue(ndev, q);
spin_unlock_irqrestore(&priv->lock, flags);
return NETDEV_TX_BUSY;
}
entry = priv->cur_tx[q] % (priv->num_tx_ring[q] * NUM_TX_DESC);
priv->tx_skb[q][entry / NUM_TX_DESC] = skb;
if (skb_put_padto(skb, ETH_ZLEN))
goto drop;
buffer = PTR_ALIGN(priv->tx_align[q], DPTR_ALIGN) +
entry / NUM_TX_DESC * DPTR_ALIGN;
len = PTR_ALIGN(skb->data, DPTR_ALIGN) - skb->data;
ravb: do not use zero-length alignment DMA descriptor Due to alignment requirements of the hardware transmissions are split into two DMA descriptors, a small padding descriptor of 0 - 3 bytes in length followed by a descriptor for rest of the packet. In the case of IP packets the first descriptor will never be zero due to the way that the stack aligns buffers for IP packets. However, for non-IP packets it may be zero. In that case it has been reported that timeouts occur, presumably because transmission stops at the first zero-length DMA descriptor and thus the packet is not transmitted. However, in my environment a BUG is triggered as follows: [ 20.381417] ------------[ cut here ]------------ [ 20.386054] kernel BUG at lib/swiotlb.c:495! [ 20.390324] Internal error: Oops - BUG: 0 [#1] PREEMPT SMP [ 20.395805] Modules linked in: [ 20.398862] CPU: 0 PID: 2089 Comm: mz Not tainted 4.10.0-rc3-00001-gf13ad2db193f #162 [ 20.406689] Hardware name: Renesas Salvator-X board based on r8a7796 (DT) [ 20.413474] task: ffff80063b1f1900 task.stack: ffff80063a71c000 [ 20.419404] PC is at swiotlb_tbl_map_single+0x178/0x2ec [ 20.424625] LR is at map_single+0x4c/0x98 [ 20.428629] pc : [<ffff00000839c4c0>] lr : [<ffff00000839c680>] pstate: 800001c5 [ 20.436019] sp : ffff80063a71f9b0 [ 20.439327] x29: ffff80063a71f9b0 x28: ffff80063a20d500 [ 20.444636] x27: ffff000008ed5000 x26: 0000000000000000 [ 20.449944] x25: 000000067abe2adc x24: 0000000000000000 [ 20.455252] x23: 0000000000200000 x22: 0000000000000001 [ 20.460559] x21: 0000000000175ffe x20: ffff80063b2a0010 [ 20.465866] x19: 0000000000000000 x18: 0000ffffcae6fb20 [ 20.471173] x17: 0000ffffa09ba018 x16: ffff0000087c8b70 [ 20.476480] x15: 0000ffffa084f588 x14: 0000ffffa09cfa14 [ 20.481787] x13: 0000ffffcae87ff0 x12: 000000000063abe2 [ 20.487098] x11: ffff000008096360 x10: ffff80063abe2adc [ 20.492407] x9 : 0000000000000000 x8 : 0000000000000000 [ 20.497718] x7 : 0000000000000000 x6 : ffff000008ed50d0 [ 20.503028] x5 : 0000000000000000 x4 : 0000000000000001 [ 20.508338] x3 : 0000000000000000 x2 : 000000067abe2adc [ 20.513648] x1 : 00000000bafff000 x0 : 0000000000000000 [ 20.518958] [ 20.520446] Process mz (pid: 2089, stack limit = 0xffff80063a71c000) [ 20.526798] Stack: (0xffff80063a71f9b0 to 0xffff80063a720000) [ 20.532543] f9a0: ffff80063a71fa30 ffff00000839c680 [ 20.540374] f9c0: ffff80063b2a0010 ffff80063b2a0010 0000000000000001 0000000000000000 [ 20.548204] f9e0: 000000000000006e ffff80063b23c000 ffff80063b23c000 0000000000000000 [ 20.556034] fa00: ffff80063b23c000 ffff80063a20d500 000000013b1f1900 0000000000000000 [ 20.563864] fa20: ffff80063ffd18e0 ffff80063b2a0010 ffff80063a71fa60 ffff00000839cd10 [ 20.571694] fa40: ffff80063b2a0010 0000000000000000 ffff80063ffd18e0 000000067abe2adc [ 20.579524] fa60: ffff80063a71fa90 ffff000008096380 ffff80063b2a0010 0000000000000000 [ 20.587353] fa80: 0000000000000000 0000000000000001 ffff80063a71fac0 ffff00000864f770 [ 20.595184] faa0: ffff80063b23caf0 0000000000000000 0000000000000000 0000000000000140 [ 20.603014] fac0: ffff80063a71fb60 ffff0000087e6498 ffff80063a20d500 ffff80063b23c000 [ 20.610843] fae0: 0000000000000000 ffff000008daeaf0 0000000000000000 ffff000008daeb00 [ 20.618673] fb00: ffff80063a71fc0c ffff000008da7000 ffff80063b23c090 ffff80063a44f000 [ 20.626503] fb20: 0000000000000000 ffff000008daeb00 ffff80063a71fc0c ffff000008da7000 [ 20.634333] fb40: ffff80063b23c090 0000000000000000 ffff800600000037 ffff0000087e63d8 [ 20.642163] fb60: ffff80063a71fbc0 ffff000008807510 ffff80063a692400 ffff80063a20d500 [ 20.649993] fb80: ffff80063a44f000 ffff80063b23c000 ffff80063a69249c 0000000000000000 [ 20.657823] fba0: 0000000000000000 ffff80063a087800 ffff80063b23c000 ffff80063a20d500 [ 20.665653] fbc0: ffff80063a71fc10 ffff0000087e67dc ffff80063a20d500 ffff80063a692400 [ 20.673483] fbe0: ffff80063b23c000 0000000000000000 ffff80063a44f000 ffff80063a69249c [ 20.681312] fc00: ffff80063a5f1a10 000000103a087800 ffff80063a71fc70 ffff0000087e6b24 [ 20.689142] fc20: ffff80063a5f1a80 ffff80063a71fde8 000000000000000f 00000000000005ea [ 20.696972] fc40: ffff80063a5f1a10 0000000000000000 000000000000000f ffff00000887fbd0 [ 20.704802] fc60: fffffff43a5f1a80 0000000000000000 ffff80063a71fc80 ffff000008880240 [ 20.712632] fc80: ffff80063a71fd90 ffff0000087c7a34 ffff80063afc7180 0000000000000000 [ 20.720462] fca0: 0000ffffcae6fe18 0000000000000014 0000000060000000 0000000000000015 [ 20.728292] fcc0: 0000000000000123 00000000000000ce ffff0000088d2000 ffff80063b1f1900 [ 20.736122] fce0: 0000000000008933 ffff000008e7cb80 ffff80063a71fd80 ffff0000087c50a4 [ 20.743951] fd00: 0000000000008933 ffff000008e7cb80 ffff000008e7cb80 000000100000000e [ 20.751781] fd20: ffff80063a71fe4c 0000ffff00000300 0000000000000123 0000000000000000 [ 20.759611] fd40: 0000000000000000 ffff80063b1f0000 000000000000000e 0000000000000300 [ 20.767441] fd60: 0000000000000000 0000000000000000 0000000000000000 0000000000000000 [ 20.775271] fd80: 0000000000000000 0000000000000000 ffff80063a71fda0 ffff0000087c8c20 [ 20.783100] fda0: 0000000000000000 ffff000008082f30 0000000000000000 0000800637260000 [ 20.790930] fdc0: ffffffffffffffff 0000ffffa0903078 0000000000000000 000000001ea87232 [ 20.798760] fde0: 000000000000000f ffff80063a71fe40 ffff800600000014 ffff000000000001 [ 20.806590] fe00: 0000000000000000 0000000000000000 ffff80063a71fde8 0000000000000000 [ 20.814420] fe20: 0000000000000000 0000000000000000 0000000000000000 0000000000000001 [ 20.822249] fe40: 0000000203000011 0000000000000000 0000000000000000 ffff80063a68aa00 [ 20.830079] fe60: ffff80063a68aa00 0000000000000003 0000000000008933 ffff0000081f1b9c [ 20.837909] fe80: 0000000000000000 ffff000008082f30 0000000000000000 0000800637260000 [ 20.845739] fea0: ffffffffffffffff 0000ffffa07ca81c 0000000060000000 0000000000000015 [ 20.853569] fec0: 0000000000000003 000000001ea87232 000000000000000f 0000000000000000 [ 20.861399] fee0: 0000ffffcae6fe18 0000000000000014 0000000000000300 0000000000000000 [ 20.869228] ff00: 00000000000000ce 0000000000000000 00000000ffffffff 0000000000000000 [ 20.877059] ff20: 0000000000000002 0000ffffcae87ff0 0000ffffa09cfa14 0000ffffa084f588 [ 20.884888] ff40: 0000000000000000 0000ffffa09ba018 0000ffffcae6fb20 000000001ea87010 [ 20.892718] ff60: 0000ffffa09b9000 0000ffffcae6fe30 0000ffffcae6fe18 000000000000000f [ 20.900548] ff80: 0000000000000003 000000001ea87232 0000000000000000 0000000000000000 [ 20.908378] ffa0: 0000000000000000 0000ffffcae6fdc0 0000ffffa09a7824 0000ffffcae6fdc0 [ 20.916208] ffc0: 0000ffffa0903078 0000000060000000 0000000000000003 00000000000000ce [ 20.924038] ffe0: 0000000000000000 0000000000000000 ffffffffffffffff ffffffffffffffff [ 20.931867] Call trace: [ 20.934312] Exception stack(0xffff80063a71f7e0 to 0xffff80063a71f910) [ 20.940750] f7e0: 0000000000000000 0001000000000000 ffff80063a71f9b0 ffff00000839c4c0 [ 20.948580] f800: ffff80063a71f840 ffff00000888a6e4 ffff80063a24c418 ffff80063a24c448 [ 20.956410] f820: 0000000000000000 ffff00000811cd54 ffff80063a71f860 ffff80063a24c458 [ 20.964240] f840: ffff80063a71f870 ffff00000888b258 ffff80063a24c418 0000000000000001 [ 20.972070] f860: ffff80063a71f910 ffff80063a7b7028 ffff80063a71f890 ffff0000088825e4 [ 20.979899] f880: 0000000000000000 00000000bafff000 000000067abe2adc 0000000000000000 [ 20.987729] f8a0: 0000000000000001 0000000000000000 ffff000008ed50d0 0000000000000000 [ 20.995560] f8c0: 0000000000000000 0000000000000000 ffff80063abe2adc ffff000008096360 [ 21.003390] f8e0: 000000000063abe2 0000ffffcae87ff0 0000ffffa09cfa14 0000ffffa084f588 [ 21.011219] f900: ffff0000087c8b70 0000ffffa09ba018 [ 21.016097] [<ffff00000839c4c0>] swiotlb_tbl_map_single+0x178/0x2ec [ 21.022362] [<ffff00000839c680>] map_single+0x4c/0x98 [ 21.027411] [<ffff00000839cd10>] swiotlb_map_page+0xa4/0x138 [ 21.033072] [<ffff000008096380>] __swiotlb_map_page+0x20/0x7c [ 21.038821] [<ffff00000864f770>] ravb_start_xmit+0x174/0x668 [ 21.044484] [<ffff0000087e6498>] dev_hard_start_xmit+0x8c/0x120 [ 21.050407] [<ffff000008807510>] sch_direct_xmit+0x108/0x1a0 [ 21.056064] [<ffff0000087e67dc>] __dev_queue_xmit+0x194/0x4cc [ 21.061807] [<ffff0000087e6b24>] dev_queue_xmit+0x10/0x18 [ 21.067214] [<ffff000008880240>] packet_sendmsg+0xf40/0x1220 [ 21.072873] [<ffff0000087c7a34>] sock_sendmsg+0x18/0x2c [ 21.078097] [<ffff0000087c8c20>] SyS_sendto+0xb0/0xf0 [ 21.083150] [<ffff000008082f30>] el0_svc_naked+0x24/0x28 [ 21.088462] Code: d34bfef7 2a1803f3 1a9f86d6 35fff878 (d4210000) [ 21.094611] ---[ end trace 5bc544ad491f3814 ]--- [ 21.099234] Kernel panic - not syncing: Fatal exception in interrupt [ 21.105587] Kernel Offset: disabled [ 21.109073] Memory Limit: none [ 21.112126] ---[ end Kernel panic - not syncing: Fatal exception in interrupt Fixes: 2f45d1902acf ("ravb: minimize TX data copying") Signed-off-by: Masaru Nagai <masaru.nagai.vx@renesas.com Signed-off-by: Simon Horman <horms+renesas@verge.net.au> Acked-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2017-01-16 18:45:21 +08:00
/* Zero length DMA descriptors are problematic as they seem to
* terminate DMA transfers. Avoid them by simply using a length of
* DPTR_ALIGN (4) when skb data is aligned to DPTR_ALIGN.
*
* As skb is guaranteed to have at least ETH_ZLEN (60) bytes of
* data by the call to skb_put_padto() above this is safe with
* respect to both the length of the first DMA descriptor (len)
* overflowing the available data and the length of the second DMA
* descriptor (skb->len - len) being negative.
*/
if (len == 0)
len = DPTR_ALIGN;
memcpy(buffer, skb->data, len);
dma_addr = dma_map_single(ndev->dev.parent, buffer, len, DMA_TO_DEVICE);
if (dma_mapping_error(ndev->dev.parent, dma_addr))
goto drop;
desc = &priv->tx_ring[q][entry];
desc->ds_tagl = cpu_to_le16(len);
desc->dptr = cpu_to_le32(dma_addr);
buffer = skb->data + len;
len = skb->len - len;
dma_addr = dma_map_single(ndev->dev.parent, buffer, len, DMA_TO_DEVICE);
if (dma_mapping_error(ndev->dev.parent, dma_addr))
goto unmap;
desc++;
desc->ds_tagl = cpu_to_le16(len);
desc->dptr = cpu_to_le32(dma_addr);
/* TX timestamp required */
if (q == RAVB_NC) {
ts_skb = kmalloc(sizeof(*ts_skb), GFP_ATOMIC);
if (!ts_skb) {
desc--;
dma_unmap_single(ndev->dev.parent, dma_addr, len,
DMA_TO_DEVICE);
goto unmap;
}
ts_skb->skb = skb;
ts_skb->tag = priv->ts_skb_tag++;
priv->ts_skb_tag &= 0x3ff;
list_add_tail(&ts_skb->list, &priv->ts_skb_list);
/* TAG and timestamp required flag */
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
desc->tagh_tsr = (ts_skb->tag >> 4) | TX_TSR;
desc->ds_tagl |= le16_to_cpu(ts_skb->tag << 12);
}
skb_tx_timestamp(skb);
/* Descriptor type must be set after all the above writes */
dma_wmb();
desc->die_dt = DT_FEND;
desc--;
desc->die_dt = DT_FSTART;
ravb_modify(ndev, TCCR, TCCR_TSRQ0 << q, TCCR_TSRQ0 << q);
priv->cur_tx[q] += NUM_TX_DESC;
if (priv->cur_tx[q] - priv->dirty_tx[q] >
(priv->num_tx_ring[q] - 1) * NUM_TX_DESC &&
!ravb_tx_free(ndev, q, true))
netif_stop_subqueue(ndev, q);
exit:
mmiowb();
spin_unlock_irqrestore(&priv->lock, flags);
return NETDEV_TX_OK;
unmap:
dma_unmap_single(ndev->dev.parent, le32_to_cpu(desc->dptr),
le16_to_cpu(desc->ds_tagl), DMA_TO_DEVICE);
drop:
dev_kfree_skb_any(skb);
priv->tx_skb[q][entry / NUM_TX_DESC] = NULL;
goto exit;
}
static u16 ravb_select_queue(struct net_device *ndev, struct sk_buff *skb,
void *accel_priv, select_queue_fallback_t fallback)
{
/* If skb needs TX timestamp, it is handled in network control queue */
return (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) ? RAVB_NC :
RAVB_BE;
}
static struct net_device_stats *ravb_get_stats(struct net_device *ndev)
{
struct ravb_private *priv = netdev_priv(ndev);
struct net_device_stats *nstats, *stats0, *stats1;
nstats = &ndev->stats;
stats0 = &priv->stats[RAVB_BE];
stats1 = &priv->stats[RAVB_NC];
nstats->tx_dropped += ravb_read(ndev, TROCR);
ravb_write(ndev, 0, TROCR); /* (write clear) */
nstats->collisions += ravb_read(ndev, CDCR);
ravb_write(ndev, 0, CDCR); /* (write clear) */
nstats->tx_carrier_errors += ravb_read(ndev, LCCR);
ravb_write(ndev, 0, LCCR); /* (write clear) */
nstats->tx_carrier_errors += ravb_read(ndev, CERCR);
ravb_write(ndev, 0, CERCR); /* (write clear) */
nstats->tx_carrier_errors += ravb_read(ndev, CEECR);
ravb_write(ndev, 0, CEECR); /* (write clear) */
nstats->rx_packets = stats0->rx_packets + stats1->rx_packets;
nstats->tx_packets = stats0->tx_packets + stats1->tx_packets;
nstats->rx_bytes = stats0->rx_bytes + stats1->rx_bytes;
nstats->tx_bytes = stats0->tx_bytes + stats1->tx_bytes;
nstats->multicast = stats0->multicast + stats1->multicast;
nstats->rx_errors = stats0->rx_errors + stats1->rx_errors;
nstats->rx_crc_errors = stats0->rx_crc_errors + stats1->rx_crc_errors;
nstats->rx_frame_errors =
stats0->rx_frame_errors + stats1->rx_frame_errors;
nstats->rx_length_errors =
stats0->rx_length_errors + stats1->rx_length_errors;
nstats->rx_missed_errors =
stats0->rx_missed_errors + stats1->rx_missed_errors;
nstats->rx_over_errors =
stats0->rx_over_errors + stats1->rx_over_errors;
return nstats;
}
/* Update promiscuous bit */
static void ravb_set_rx_mode(struct net_device *ndev)
{
struct ravb_private *priv = netdev_priv(ndev);
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
ravb_modify(ndev, ECMR, ECMR_PRM,
ndev->flags & IFF_PROMISC ? ECMR_PRM : 0);
mmiowb();
spin_unlock_irqrestore(&priv->lock, flags);
}
/* Device close function for Ethernet AVB */
static int ravb_close(struct net_device *ndev)
{
struct device_node *np = ndev->dev.parent->of_node;
struct ravb_private *priv = netdev_priv(ndev);
struct ravb_tstamp_skb *ts_skb, *ts_skb2;
netif_tx_stop_all_queues(ndev);
/* Disable interrupts by clearing the interrupt masks. */
ravb_write(ndev, 0, RIC0);
ravb_write(ndev, 0, RIC2);
ravb_write(ndev, 0, TIC);
/* Stop PTP Clock driver */
if (priv->chip_id == RCAR_GEN2)
ravb_ptp_stop(ndev);
/* Set the config mode to stop the AVB-DMAC's processes */
if (ravb_stop_dma(ndev) < 0)
netdev_err(ndev,
"device will be stopped after h/w processes are done.\n");
/* Clear the timestamp list */
list_for_each_entry_safe(ts_skb, ts_skb2, &priv->ts_skb_list, list) {
list_del(&ts_skb->list);
kfree(ts_skb);
}
/* PHY disconnect */
if (ndev->phydev) {
phy_stop(ndev->phydev);
phy_disconnect(ndev->phydev);
if (of_phy_is_fixed_link(np))
of_phy_deregister_fixed_link(np);
}
if (priv->chip_id != RCAR_GEN2) {
free_irq(priv->tx_irqs[RAVB_NC], ndev);
free_irq(priv->rx_irqs[RAVB_NC], ndev);
free_irq(priv->tx_irqs[RAVB_BE], ndev);
free_irq(priv->rx_irqs[RAVB_BE], ndev);
free_irq(priv->emac_irq, ndev);
}
free_irq(ndev->irq, ndev);
napi_disable(&priv->napi[RAVB_NC]);
napi_disable(&priv->napi[RAVB_BE]);
/* Free all the skb's in the RX queue and the DMA buffers. */
ravb_ring_free(ndev, RAVB_BE);
ravb_ring_free(ndev, RAVB_NC);
return 0;
}
static int ravb_hwtstamp_get(struct net_device *ndev, struct ifreq *req)
{
struct ravb_private *priv = netdev_priv(ndev);
struct hwtstamp_config config;
config.flags = 0;
config.tx_type = priv->tstamp_tx_ctrl ? HWTSTAMP_TX_ON :
HWTSTAMP_TX_OFF;
if (priv->tstamp_rx_ctrl & RAVB_RXTSTAMP_TYPE_V2_L2_EVENT)
config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT;
else if (priv->tstamp_rx_ctrl & RAVB_RXTSTAMP_TYPE_ALL)
config.rx_filter = HWTSTAMP_FILTER_ALL;
else
config.rx_filter = HWTSTAMP_FILTER_NONE;
return copy_to_user(req->ifr_data, &config, sizeof(config)) ?
-EFAULT : 0;
}
/* Control hardware time stamping */
static int ravb_hwtstamp_set(struct net_device *ndev, struct ifreq *req)
{
struct ravb_private *priv = netdev_priv(ndev);
struct hwtstamp_config config;
u32 tstamp_rx_ctrl = RAVB_RXTSTAMP_ENABLED;
u32 tstamp_tx_ctrl;
if (copy_from_user(&config, req->ifr_data, sizeof(config)))
return -EFAULT;
/* Reserved for future extensions */
if (config.flags)
return -EINVAL;
switch (config.tx_type) {
case HWTSTAMP_TX_OFF:
tstamp_tx_ctrl = 0;
break;
case HWTSTAMP_TX_ON:
tstamp_tx_ctrl = RAVB_TXTSTAMP_ENABLED;
break;
default:
return -ERANGE;
}
switch (config.rx_filter) {
case HWTSTAMP_FILTER_NONE:
tstamp_rx_ctrl = 0;
break;
case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
tstamp_rx_ctrl |= RAVB_RXTSTAMP_TYPE_V2_L2_EVENT;
break;
default:
config.rx_filter = HWTSTAMP_FILTER_ALL;
tstamp_rx_ctrl |= RAVB_RXTSTAMP_TYPE_ALL;
}
priv->tstamp_tx_ctrl = tstamp_tx_ctrl;
priv->tstamp_rx_ctrl = tstamp_rx_ctrl;
return copy_to_user(req->ifr_data, &config, sizeof(config)) ?
-EFAULT : 0;
}
/* ioctl to device function */
static int ravb_do_ioctl(struct net_device *ndev, struct ifreq *req, int cmd)
{
struct phy_device *phydev = ndev->phydev;
if (!netif_running(ndev))
return -EINVAL;
if (!phydev)
return -ENODEV;
switch (cmd) {
case SIOCGHWTSTAMP:
return ravb_hwtstamp_get(ndev, req);
case SIOCSHWTSTAMP:
return ravb_hwtstamp_set(ndev, req);
}
return phy_mii_ioctl(phydev, req, cmd);
}
static const struct net_device_ops ravb_netdev_ops = {
.ndo_open = ravb_open,
.ndo_stop = ravb_close,
.ndo_start_xmit = ravb_start_xmit,
.ndo_select_queue = ravb_select_queue,
.ndo_get_stats = ravb_get_stats,
.ndo_set_rx_mode = ravb_set_rx_mode,
.ndo_tx_timeout = ravb_tx_timeout,
.ndo_do_ioctl = ravb_do_ioctl,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
};
/* MDIO bus init function */
static int ravb_mdio_init(struct ravb_private *priv)
{
struct platform_device *pdev = priv->pdev;
struct device *dev = &pdev->dev;
int error;
/* Bitbang init */
priv->mdiobb.ops = &bb_ops;
/* MII controller setting */
priv->mii_bus = alloc_mdio_bitbang(&priv->mdiobb);
if (!priv->mii_bus)
return -ENOMEM;
/* Hook up MII support for ethtool */
priv->mii_bus->name = "ravb_mii";
priv->mii_bus->parent = dev;
snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
pdev->name, pdev->id);
/* Register MDIO bus */
error = of_mdiobus_register(priv->mii_bus, dev->of_node);
if (error)
goto out_free_bus;
return 0;
out_free_bus:
free_mdio_bitbang(priv->mii_bus);
return error;
}
/* MDIO bus release function */
static int ravb_mdio_release(struct ravb_private *priv)
{
/* Unregister mdio bus */
mdiobus_unregister(priv->mii_bus);
/* Free bitbang info */
free_mdio_bitbang(priv->mii_bus);
return 0;
}
static const struct of_device_id ravb_match_table[] = {
{ .compatible = "renesas,etheravb-r8a7790", .data = (void *)RCAR_GEN2 },
{ .compatible = "renesas,etheravb-r8a7794", .data = (void *)RCAR_GEN2 },
{ .compatible = "renesas,etheravb-rcar-gen2", .data = (void *)RCAR_GEN2 },
{ .compatible = "renesas,etheravb-r8a7795", .data = (void *)RCAR_GEN3 },
{ .compatible = "renesas,etheravb-rcar-gen3", .data = (void *)RCAR_GEN3 },
{ }
};
MODULE_DEVICE_TABLE(of, ravb_match_table);
static int ravb_set_gti(struct net_device *ndev)
{
struct device *dev = ndev->dev.parent;
struct device_node *np = dev->of_node;
unsigned long rate;
struct clk *clk;
uint64_t inc;
clk = of_clk_get(np, 0);
if (IS_ERR(clk)) {
dev_err(dev, "could not get clock\n");
return PTR_ERR(clk);
}
rate = clk_get_rate(clk);
clk_put(clk);
if (!rate)
return -EINVAL;
inc = 1000000000ULL << 20;
do_div(inc, rate);
if (inc < GTI_TIV_MIN || inc > GTI_TIV_MAX) {
dev_err(dev, "gti.tiv increment 0x%llx is outside the range 0x%x - 0x%x\n",
inc, GTI_TIV_MIN, GTI_TIV_MAX);
return -EINVAL;
}
ravb_write(ndev, inc, GTI);
return 0;
}
static void ravb_set_config_mode(struct net_device *ndev)
{
struct ravb_private *priv = netdev_priv(ndev);
if (priv->chip_id == RCAR_GEN2) {
ravb_modify(ndev, CCC, CCC_OPC, CCC_OPC_CONFIG);
/* Set CSEL value */
ravb_modify(ndev, CCC, CCC_CSEL, CCC_CSEL_HPB);
} else {
ravb_modify(ndev, CCC, CCC_OPC, CCC_OPC_CONFIG |
CCC_GAC | CCC_CSEL_HPB);
}
}
/* Set tx and rx clock internal delay modes */
static void ravb_set_delay_mode(struct net_device *ndev)
{
struct ravb_private *priv = netdev_priv(ndev);
int set = 0;
if (priv->phy_interface == PHY_INTERFACE_MODE_RGMII_ID ||
priv->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID)
set |= APSR_DM_RDM;
if (priv->phy_interface == PHY_INTERFACE_MODE_RGMII_ID ||
priv->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID)
set |= APSR_DM_TDM;
ravb_modify(ndev, APSR, APSR_DM, set);
}
static int ravb_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct ravb_private *priv;
enum ravb_chip_id chip_id;
struct net_device *ndev;
int error, irq, q;
struct resource *res;
ravb: Add dma queue interrupt support This patch supports the following interrupts. - One interrupt for multiple (timestamp, error, gPTP) - One interrupt for emac - Four interrupts for dma queue (best effort rx/tx, network control rx/tx) This patch improve efficiency of the interrupt handler by adding the interrupt handler corresponding to each interrupt source described above. Additionally, it reduces the number of times of the access to EthernetAVB IF. Also this patch prevent this driver depends on the whim of a boot loader. [ykaneko0929@gmail.com: define bit names of registers] [ykaneko0929@gmail.com: add comment for gen3 only registers] [ykaneko0929@gmail.com: fix coding style] [ykaneko0929@gmail.com: update changelog] [ykaneko0929@gmail.com: gen3: fix initialization of interrupts] [ykaneko0929@gmail.com: gen3: fix clearing interrupts] [ykaneko0929@gmail.com: gen3: add helper function for request_irq()] [ykaneko0929@gmail.com: gen3: remove IRQF_SHARED flag for request_irq()] [ykaneko0929@gmail.com: revert ravb_close() and ravb_ptp_stop()] [ykaneko0929@gmail.com: avoid calling free_irq() to non-hooked interrupts] [ykaneko0929@gmail.com: make NC/BE interrupt handler a function] [ykaneko0929@gmail.com: make timestamp interrupt handler a function] [ykaneko0929@gmail.com: timestamp interrupt is handled in multiple interrupt handler instead of dma queue interrupt handler] Signed-off-by: Kazuya Mizuguchi <kazuya.mizuguchi.ks@renesas.com> Signed-off-by: Yoshihiro Kaneko <ykaneko0929@gmail.com> Acked-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2016-04-03 22:54:38 +08:00
int i;
if (!np) {
dev_err(&pdev->dev,
"this driver is required to be instantiated from device tree\n");
return -EINVAL;
}
/* Get base address */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "invalid resource\n");
return -EINVAL;
}
ndev = alloc_etherdev_mqs(sizeof(struct ravb_private),
NUM_TX_QUEUE, NUM_RX_QUEUE);
if (!ndev)
return -ENOMEM;
pm_runtime_enable(&pdev->dev);
pm_runtime_get_sync(&pdev->dev);
/* The Ether-specific entries in the device structure. */
ndev->base_addr = res->start;
chip_id = (enum ravb_chip_id)of_device_get_match_data(&pdev->dev);
if (chip_id == RCAR_GEN3)
irq = platform_get_irq_byname(pdev, "ch22");
else
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
error = irq;
goto out_release;
}
ndev->irq = irq;
SET_NETDEV_DEV(ndev, &pdev->dev);
priv = netdev_priv(ndev);
priv->ndev = ndev;
priv->pdev = pdev;
priv->num_tx_ring[RAVB_BE] = BE_TX_RING_SIZE;
priv->num_rx_ring[RAVB_BE] = BE_RX_RING_SIZE;
priv->num_tx_ring[RAVB_NC] = NC_TX_RING_SIZE;
priv->num_rx_ring[RAVB_NC] = NC_RX_RING_SIZE;
priv->addr = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(priv->addr)) {
error = PTR_ERR(priv->addr);
goto out_release;
}
spin_lock_init(&priv->lock);
INIT_WORK(&priv->work, ravb_tx_timeout_work);
priv->phy_interface = of_get_phy_mode(np);
priv->no_avb_link = of_property_read_bool(np, "renesas,no-ether-link");
priv->avb_link_active_low =
of_property_read_bool(np, "renesas,ether-link-active-low");
if (chip_id == RCAR_GEN3) {
irq = platform_get_irq_byname(pdev, "ch24");
if (irq < 0) {
error = irq;
goto out_release;
}
priv->emac_irq = irq;
ravb: Add dma queue interrupt support This patch supports the following interrupts. - One interrupt for multiple (timestamp, error, gPTP) - One interrupt for emac - Four interrupts for dma queue (best effort rx/tx, network control rx/tx) This patch improve efficiency of the interrupt handler by adding the interrupt handler corresponding to each interrupt source described above. Additionally, it reduces the number of times of the access to EthernetAVB IF. Also this patch prevent this driver depends on the whim of a boot loader. [ykaneko0929@gmail.com: define bit names of registers] [ykaneko0929@gmail.com: add comment for gen3 only registers] [ykaneko0929@gmail.com: fix coding style] [ykaneko0929@gmail.com: update changelog] [ykaneko0929@gmail.com: gen3: fix initialization of interrupts] [ykaneko0929@gmail.com: gen3: fix clearing interrupts] [ykaneko0929@gmail.com: gen3: add helper function for request_irq()] [ykaneko0929@gmail.com: gen3: remove IRQF_SHARED flag for request_irq()] [ykaneko0929@gmail.com: revert ravb_close() and ravb_ptp_stop()] [ykaneko0929@gmail.com: avoid calling free_irq() to non-hooked interrupts] [ykaneko0929@gmail.com: make NC/BE interrupt handler a function] [ykaneko0929@gmail.com: make timestamp interrupt handler a function] [ykaneko0929@gmail.com: timestamp interrupt is handled in multiple interrupt handler instead of dma queue interrupt handler] Signed-off-by: Kazuya Mizuguchi <kazuya.mizuguchi.ks@renesas.com> Signed-off-by: Yoshihiro Kaneko <ykaneko0929@gmail.com> Acked-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2016-04-03 22:54:38 +08:00
for (i = 0; i < NUM_RX_QUEUE; i++) {
irq = platform_get_irq_byname(pdev, ravb_rx_irqs[i]);
if (irq < 0) {
error = irq;
goto out_release;
}
priv->rx_irqs[i] = irq;
}
for (i = 0; i < NUM_TX_QUEUE; i++) {
irq = platform_get_irq_byname(pdev, ravb_tx_irqs[i]);
if (irq < 0) {
error = irq;
goto out_release;
}
priv->tx_irqs[i] = irq;
}
}
priv->chip_id = chip_id;
/* Set function */
ndev->netdev_ops = &ravb_netdev_ops;
ndev->ethtool_ops = &ravb_ethtool_ops;
/* Set AVB config mode */
ravb_set_config_mode(ndev);
/* Set GTI value */
error = ravb_set_gti(ndev);
if (error)
goto out_release;
/* Request GTI loading */
ravb_modify(ndev, GCCR, GCCR_LTI, GCCR_LTI);
if (priv->chip_id != RCAR_GEN2)
ravb_set_delay_mode(ndev);
/* Allocate descriptor base address table */
priv->desc_bat_size = sizeof(struct ravb_desc) * DBAT_ENTRY_NUM;
priv->desc_bat = dma_alloc_coherent(ndev->dev.parent, priv->desc_bat_size,
&priv->desc_bat_dma, GFP_KERNEL);
if (!priv->desc_bat) {
dev_err(&pdev->dev,
"Cannot allocate desc base address table (size %d bytes)\n",
priv->desc_bat_size);
error = -ENOMEM;
goto out_release;
}
for (q = RAVB_BE; q < DBAT_ENTRY_NUM; q++)
priv->desc_bat[q].die_dt = DT_EOS;
ravb_write(ndev, priv->desc_bat_dma, DBAT);
/* Initialise HW timestamp list */
INIT_LIST_HEAD(&priv->ts_skb_list);
/* Initialise PTP Clock driver */
if (chip_id != RCAR_GEN2)
ravb_ptp_init(ndev, pdev);
/* Debug message level */
priv->msg_enable = RAVB_DEF_MSG_ENABLE;
/* Read and set MAC address */
ravb_read_mac_address(ndev, of_get_mac_address(np));
if (!is_valid_ether_addr(ndev->dev_addr)) {
dev_warn(&pdev->dev,
"no valid MAC address supplied, using a random one\n");
eth_hw_addr_random(ndev);
}
/* MDIO bus init */
error = ravb_mdio_init(priv);
if (error) {
dev_err(&pdev->dev, "failed to initialize MDIO\n");
goto out_dma_free;
}
netif_napi_add(ndev, &priv->napi[RAVB_BE], ravb_poll, 64);
netif_napi_add(ndev, &priv->napi[RAVB_NC], ravb_poll, 64);
/* Network device register */
error = register_netdev(ndev);
if (error)
goto out_napi_del;
/* Print device information */
netdev_info(ndev, "Base address at %#x, %pM, IRQ %d.\n",
(u32)ndev->base_addr, ndev->dev_addr, ndev->irq);
platform_set_drvdata(pdev, ndev);
return 0;
out_napi_del:
netif_napi_del(&priv->napi[RAVB_NC]);
netif_napi_del(&priv->napi[RAVB_BE]);
ravb_mdio_release(priv);
out_dma_free:
dma_free_coherent(ndev->dev.parent, priv->desc_bat_size, priv->desc_bat,
priv->desc_bat_dma);
/* Stop PTP Clock driver */
if (chip_id != RCAR_GEN2)
ravb_ptp_stop(ndev);
out_release:
if (ndev)
free_netdev(ndev);
pm_runtime_put(&pdev->dev);
pm_runtime_disable(&pdev->dev);
return error;
}
static int ravb_remove(struct platform_device *pdev)
{
struct net_device *ndev = platform_get_drvdata(pdev);
struct ravb_private *priv = netdev_priv(ndev);
/* Stop PTP Clock driver */
if (priv->chip_id != RCAR_GEN2)
ravb_ptp_stop(ndev);
dma_free_coherent(ndev->dev.parent, priv->desc_bat_size, priv->desc_bat,
priv->desc_bat_dma);
/* Set reset mode */
ravb_write(ndev, CCC_OPC_RESET, CCC);
pm_runtime_put_sync(&pdev->dev);
unregister_netdev(ndev);
netif_napi_del(&priv->napi[RAVB_NC]);
netif_napi_del(&priv->napi[RAVB_BE]);
ravb_mdio_release(priv);
pm_runtime_disable(&pdev->dev);
free_netdev(ndev);
platform_set_drvdata(pdev, NULL);
return 0;
}
static int __maybe_unused ravb_suspend(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
int ret = 0;
if (netif_running(ndev)) {
netif_device_detach(ndev);
ret = ravb_close(ndev);
}
return ret;
}
static int __maybe_unused ravb_resume(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
struct ravb_private *priv = netdev_priv(ndev);
int ret = 0;
/* All register have been reset to default values.
* Restore all registers which where setup at probe time and
* reopen device if it was running before system suspended.
*/
/* Set AVB config mode */
ravb_set_config_mode(ndev);
/* Set GTI value */
ret = ravb_set_gti(ndev);
if (ret)
return ret;
/* Request GTI loading */
ravb_modify(ndev, GCCR, GCCR_LTI, GCCR_LTI);
if (priv->chip_id != RCAR_GEN2)
ravb_set_delay_mode(ndev);
/* Restore descriptor base address table */
ravb_write(ndev, priv->desc_bat_dma, DBAT);
if (netif_running(ndev)) {
ret = ravb_open(ndev);
if (ret < 0)
return ret;
netif_device_attach(ndev);
}
return ret;
}
static int __maybe_unused ravb_runtime_nop(struct device *dev)
{
/* Runtime PM callback shared between ->runtime_suspend()
* and ->runtime_resume(). Simply returns success.
*
* This driver re-initializes all registers after
* pm_runtime_get_sync() anyway so there is no need
* to save and restore registers here.
*/
return 0;
}
static const struct dev_pm_ops ravb_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(ravb_suspend, ravb_resume)
SET_RUNTIME_PM_OPS(ravb_runtime_nop, ravb_runtime_nop, NULL)
};
static struct platform_driver ravb_driver = {
.probe = ravb_probe,
.remove = ravb_remove,
.driver = {
.name = "ravb",
.pm = &ravb_dev_pm_ops,
.of_match_table = ravb_match_table,
},
};
module_platform_driver(ravb_driver);
MODULE_AUTHOR("Mitsuhiro Kimura, Masaru Nagai");
MODULE_DESCRIPTION("Renesas Ethernet AVB driver");
MODULE_LICENSE("GPL v2");