Commit f51bdc23 authored by Kazuya Mizuguchi's avatar Kazuya Mizuguchi Committed by David S. Miller
Browse files

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: default avatarKazuya Mizuguchi <kazuya.mizuguchi.ks@renesas.com>
Signed-off-by: default avatarYoshihiro Kaneko <ykaneko0929@gmail.com>
Acked-by: default avatarSergei Shtylyov <sergei.shtylyov@cogentembedded.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 9d2355ba
......@@ -157,6 +157,7 @@ enum ravb_reg {
TIC = 0x0378,
TIS = 0x037C,
ISS = 0x0380,
CIE = 0x0384, /* R-Car Gen3 only */
GCCR = 0x0390,
GMTT = 0x0394,
GPTC = 0x0398,
......@@ -170,6 +171,15 @@ enum ravb_reg {
GCT0 = 0x03B8,
GCT1 = 0x03BC,
GCT2 = 0x03C0,
GIE = 0x03CC, /* R-Car Gen3 only */
GID = 0x03D0, /* R-Car Gen3 only */
DIL = 0x0440, /* R-Car Gen3 only */
RIE0 = 0x0460, /* R-Car Gen3 only */
RID0 = 0x0464, /* R-Car Gen3 only */
RIE2 = 0x0470, /* R-Car Gen3 only */
RID2 = 0x0474, /* R-Car Gen3 only */
TIE = 0x0478, /* R-Car Gen3 only */
TID = 0x047c, /* R-Car Gen3 only */
/* E-MAC registers */
ECMR = 0x0500,
......@@ -556,6 +566,16 @@ enum ISS_BIT {
ISS_DPS15 = 0x80000000,
};
/* CIE (R-Car Gen3 only) */
enum CIE_BIT {
CIE_CRIE = 0x00000001,
CIE_CTIE = 0x00000100,
CIE_RQFM = 0x00010000,
CIE_CL0M = 0x00020000,
CIE_RFWL = 0x00040000,
CIE_RFFL = 0x00080000,
};
/* GCCR */
enum GCCR_BIT {
GCCR_TCR = 0x00000003,
......@@ -592,6 +612,188 @@ enum GIS_BIT {
GIS_PTMF = 0x00000004,
};
/* GIE (R-Car Gen3 only) */
enum GIE_BIT {
GIE_PTCS = 0x00000001,
GIE_PTOS = 0x00000002,
GIE_PTMS0 = 0x00000004,
GIE_PTMS1 = 0x00000008,
GIE_PTMS2 = 0x00000010,
GIE_PTMS3 = 0x00000020,
GIE_PTMS4 = 0x00000040,
GIE_PTMS5 = 0x00000080,
GIE_PTMS6 = 0x00000100,
GIE_PTMS7 = 0x00000200,
GIE_ATCS0 = 0x00010000,
GIE_ATCS1 = 0x00020000,
GIE_ATCS2 = 0x00040000,
GIE_ATCS3 = 0x00080000,
GIE_ATCS4 = 0x00100000,
GIE_ATCS5 = 0x00200000,
GIE_ATCS6 = 0x00400000,
GIE_ATCS7 = 0x00800000,
GIE_ATCS8 = 0x01000000,
GIE_ATCS9 = 0x02000000,
GIE_ATCS10 = 0x04000000,
GIE_ATCS11 = 0x08000000,
GIE_ATCS12 = 0x10000000,
GIE_ATCS13 = 0x20000000,
GIE_ATCS14 = 0x40000000,
GIE_ATCS15 = 0x80000000,
};
/* GID (R-Car Gen3 only) */
enum GID_BIT {
GID_PTCD = 0x00000001,
GID_PTOD = 0x00000002,
GID_PTMD0 = 0x00000004,
GID_PTMD1 = 0x00000008,
GID_PTMD2 = 0x00000010,
GID_PTMD3 = 0x00000020,
GID_PTMD4 = 0x00000040,
GID_PTMD5 = 0x00000080,
GID_PTMD6 = 0x00000100,
GID_PTMD7 = 0x00000200,
GID_ATCD0 = 0x00010000,
GID_ATCD1 = 0x00020000,
GID_ATCD2 = 0x00040000,
GID_ATCD3 = 0x00080000,
GID_ATCD4 = 0x00100000,
GID_ATCD5 = 0x00200000,
GID_ATCD6 = 0x00400000,
GID_ATCD7 = 0x00800000,
GID_ATCD8 = 0x01000000,
GID_ATCD9 = 0x02000000,
GID_ATCD10 = 0x04000000,
GID_ATCD11 = 0x08000000,
GID_ATCD12 = 0x10000000,
GID_ATCD13 = 0x20000000,
GID_ATCD14 = 0x40000000,
GID_ATCD15 = 0x80000000,
};
/* RIE0 (R-Car Gen3 only) */
enum RIE0_BIT {
RIE0_FRS0 = 0x00000001,
RIE0_FRS1 = 0x00000002,
RIE0_FRS2 = 0x00000004,
RIE0_FRS3 = 0x00000008,
RIE0_FRS4 = 0x00000010,
RIE0_FRS5 = 0x00000020,
RIE0_FRS6 = 0x00000040,
RIE0_FRS7 = 0x00000080,
RIE0_FRS8 = 0x00000100,
RIE0_FRS9 = 0x00000200,
RIE0_FRS10 = 0x00000400,
RIE0_FRS11 = 0x00000800,
RIE0_FRS12 = 0x00001000,
RIE0_FRS13 = 0x00002000,
RIE0_FRS14 = 0x00004000,
RIE0_FRS15 = 0x00008000,
RIE0_FRS16 = 0x00010000,
RIE0_FRS17 = 0x00020000,
};
/* RID0 (R-Car Gen3 only) */
enum RID0_BIT {
RID0_FRD0 = 0x00000001,
RID0_FRD1 = 0x00000002,
RID0_FRD2 = 0x00000004,
RID0_FRD3 = 0x00000008,
RID0_FRD4 = 0x00000010,
RID0_FRD5 = 0x00000020,
RID0_FRD6 = 0x00000040,
RID0_FRD7 = 0x00000080,
RID0_FRD8 = 0x00000100,
RID0_FRD9 = 0x00000200,
RID0_FRD10 = 0x00000400,
RID0_FRD11 = 0x00000800,
RID0_FRD12 = 0x00001000,
RID0_FRD13 = 0x00002000,
RID0_FRD14 = 0x00004000,
RID0_FRD15 = 0x00008000,
RID0_FRD16 = 0x00010000,
RID0_FRD17 = 0x00020000,
};
/* RIE2 (R-Car Gen3 only) */
enum RIE2_BIT {
RIE2_QFS0 = 0x00000001,
RIE2_QFS1 = 0x00000002,
RIE2_QFS2 = 0x00000004,
RIE2_QFS3 = 0x00000008,
RIE2_QFS4 = 0x00000010,
RIE2_QFS5 = 0x00000020,
RIE2_QFS6 = 0x00000040,
RIE2_QFS7 = 0x00000080,
RIE2_QFS8 = 0x00000100,
RIE2_QFS9 = 0x00000200,
RIE2_QFS10 = 0x00000400,
RIE2_QFS11 = 0x00000800,
RIE2_QFS12 = 0x00001000,
RIE2_QFS13 = 0x00002000,
RIE2_QFS14 = 0x00004000,
RIE2_QFS15 = 0x00008000,
RIE2_QFS16 = 0x00010000,
RIE2_QFS17 = 0x00020000,
RIE2_RFFS = 0x80000000,
};
/* RID2 (R-Car Gen3 only) */
enum RID2_BIT {
RID2_QFD0 = 0x00000001,
RID2_QFD1 = 0x00000002,
RID2_QFD2 = 0x00000004,
RID2_QFD3 = 0x00000008,
RID2_QFD4 = 0x00000010,
RID2_QFD5 = 0x00000020,
RID2_QFD6 = 0x00000040,
RID2_QFD7 = 0x00000080,
RID2_QFD8 = 0x00000100,
RID2_QFD9 = 0x00000200,
RID2_QFD10 = 0x00000400,
RID2_QFD11 = 0x00000800,
RID2_QFD12 = 0x00001000,
RID2_QFD13 = 0x00002000,
RID2_QFD14 = 0x00004000,
RID2_QFD15 = 0x00008000,
RID2_QFD16 = 0x00010000,
RID2_QFD17 = 0x00020000,
RID2_RFFD = 0x80000000,
};
/* TIE (R-Car Gen3 only) */
enum TIE_BIT {
TIE_FTS0 = 0x00000001,
TIE_FTS1 = 0x00000002,
TIE_FTS2 = 0x00000004,
TIE_FTS3 = 0x00000008,
TIE_TFUS = 0x00000100,
TIE_TFWS = 0x00000200,
TIE_MFUS = 0x00000400,
TIE_MFWS = 0x00000800,
TIE_TDPS0 = 0x00010000,
TIE_TDPS1 = 0x00020000,
TIE_TDPS2 = 0x00040000,
TIE_TDPS3 = 0x00080000,
};
/* TID (R-Car Gen3 only) */
enum TID_BIT {
TID_FTD0 = 0x00000001,
TID_FTD1 = 0x00000002,
TID_FTD2 = 0x00000004,
TID_FTD3 = 0x00000008,
TID_TFUD = 0x00000100,
TID_TFWD = 0x00000200,
TID_MFUD = 0x00000400,
TID_MFWD = 0x00000800,
TID_TDPD0 = 0x00010000,
TID_TDPD1 = 0x00020000,
TID_TDPD2 = 0x00040000,
TID_TDPD3 = 0x00080000,
};
/* ECMR */
enum ECMR_BIT {
ECMR_PRM = 0x00000001,
......@@ -817,6 +1019,8 @@ struct ravb_private {
int duplex;
int emac_irq;
enum ravb_chip_id chip_id;
int rx_irqs[NUM_RX_QUEUE];
int tx_irqs[NUM_TX_QUEUE];
unsigned no_avb_link:1;
unsigned avb_link_active_low:1;
......
......@@ -42,6 +42,16 @@
NETIF_MSG_RX_ERR | \
NETIF_MSG_TX_ERR)
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)
{
......@@ -365,6 +375,7 @@ static void ravb_emac_init(struct net_device *ndev)
/* Device init function for Ethernet AVB */
static int ravb_dmac_init(struct net_device *ndev)
{
struct ravb_private *priv = netdev_priv(ndev);
int error;
/* Set CONFIG mode */
......@@ -401,6 +412,12 @@ static int ravb_dmac_init(struct net_device *ndev)
ravb_write(ndev, TCCR_TFEN, TCCR);
/* Interrupt init: */
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 */
......@@ -643,7 +660,7 @@ static int ravb_stop_dma(struct net_device *ndev)
}
/* E-MAC interrupt handler */
static void ravb_emac_interrupt(struct net_device *ndev)
static void ravb_emac_interrupt_unlocked(struct net_device *ndev)
{
struct ravb_private *priv = netdev_priv(ndev);
u32 ecsr, psr;
......@@ -669,6 +686,18 @@ static void ravb_emac_interrupt(struct net_device *ndev)
}
}
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)
{
......@@ -695,6 +724,50 @@ static void ravb_error_interrupt(struct net_device *ndev)
}
}
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;
......@@ -708,46 +781,22 @@ static irqreturn_t ravb_interrupt(int irq, void *dev_id)
/* Received and transmitted interrupts */
if (iss & (ISS_FRS | ISS_FTS | ISS_TFUS)) {
u32 ris0 = ravb_read(ndev, RIS0);
u32 ric0 = ravb_read(ndev, RIC0);
u32 tis = ravb_read(ndev, TIS);
u32 tic = ravb_read(ndev, TIC);
int q;
/* Timestamp updated */
if (tis & TIS_TFUF) {
ravb_write(ndev, ~TIS_TFUF, TIS);
ravb_get_tx_tstamp(ndev);
if (ravb_timestamp_interrupt(ndev))
result = IRQ_HANDLED;
}
/* Network control and best effort queue RX/TX */
for (q = RAVB_NC; q >= RAVB_BE; q--) {
if (((ris0 & ric0) & BIT(q)) ||
((tis & tic) & BIT(q))) {
if (napi_schedule_prep(&priv->napi[q])) {
/* Mask RX and TX interrupts */
ric0 &= ~BIT(q);
tic &= ~BIT(q);
ravb_write(ndev, ric0, RIC0);
ravb_write(ndev, tic, TIC);
__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);
}
if (ravb_queue_interrupt(ndev, q))
result = IRQ_HANDLED;
}
}
}
/* E-MAC status summary */
if (iss & ISS_MS) {
ravb_emac_interrupt(ndev);
ravb_emac_interrupt_unlocked(ndev);
result = IRQ_HANDLED;
}
......@@ -757,6 +806,7 @@ static irqreturn_t ravb_interrupt(int irq, void *dev_id)
result = IRQ_HANDLED;
}
/* gPTP interrupt status summary */
if ((iss & ISS_CGIS) && ravb_ptp_interrupt(ndev) == IRQ_HANDLED)
result = IRQ_HANDLED;
......@@ -765,6 +815,64 @@ static irqreturn_t ravb_interrupt(int irq, void *dev_id)
return result;
}
/* 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) == IRQ_HANDLED)
result = IRQ_HANDLED;
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;
......@@ -804,8 +912,13 @@ static int ravb_poll(struct napi_struct *napi, int budget)
/* Re-enable RX/TX interrupts */
spin_lock_irqsave(&priv->lock, flags);
ravb_modify(ndev, RIC0, mask, mask);
ravb_modify(ndev, TIC, mask, mask);
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);
......@@ -1208,35 +1321,72 @@ static const struct ethtool_ops ravb_ethtool_ops = {
.get_ts_info = ravb_get_ts_info,
};
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);
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]);
error = request_irq(ndev->irq, ravb_interrupt, IRQF_SHARED, ndev->name,
ndev);
if (error) {
netdev_err(ndev, "cannot request IRQ\n");
goto out_napi_off;
}
if (priv->chip_id == RCAR_GEN3) {
error = request_irq(priv->emac_irq, ravb_interrupt,
IRQF_SHARED, ndev->name, ndev);
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");
goto out_free_irq;
goto out_napi_off;
}
} 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)
goto out_free_irq2;
goto out_free_irq_nc_tx;
ravb_emac_init(ndev);
/* Initialise PTP Clock driver */
......@@ -1256,9 +1406,18 @@ out_ptp_stop:
/* Stop PTP Clock driver */
if (priv->chip_id == RCAR_GEN2)
ravb_ptp_stop(ndev);
out_free_irq2:
if (priv->chip_id == RCAR_GEN3)
free_irq(priv->emac_irq, ndev);
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:
......@@ -1713,6 +1872,7 @@ static int ravb_probe(struct platform_device *pdev)
struct net_device *ndev;
int error, irq, q;
struct resource *res;
int i;
if (!np) {
dev_err(&pdev->dev,
......@@ -1782,6 +1942,22 @@ static int ravb_probe(struct platform_device *pdev)
goto out_release;
}
priv->emac_irq = irq;
for (i = 0; i < NUM_RX_QUEUE; i++) {
irq = platform_get_irq_byname(pdev, ravb_rx_irqs[i]);
if (irq < 0) {
error = irq;