@@ -118,6 +118,13 @@ do { \
#define TX_PRIORITY_MAPPING 0x33221100
#define CPDMA_TX_PRIORITY_MAP 0x76543210
+enum {
+ CPSW_IRQ_TYPE_RX_THRESH,
+ CPSW_IRQ_TYPE_RX,
+ CPSW_IRQ_TYPE_TX,
+ CPSW_IRQ_TYPE_MISC,
+};
+
#define cpsw_enable_irq(priv) \
do { \
u32 i; \
@@ -312,9 +319,12 @@ struct cpsw_priv {
struct cpdma_ctlr *dma;
struct cpdma_chan *txch, *rxch;
struct cpsw_ale *ale;
+ bool rx_irqs_disabled;
+ bool tx_irqs_disabled;
+ bool misc_irqs_disabled;
/* snapshot of IRQ numbers */
- u32 irqs_table[4];
- u32 num_irqs;
+ u32 irqs_table[4];
+ u32 num_irqs;
struct cpts cpts;
};
@@ -350,21 +360,93 @@ static void cpsw_ndo_set_rx_mode(struct net_device *ndev)
}
}
-static void cpsw_intr_enable(struct cpsw_priv *priv)
+static void cpsw_intr_rx_enable(struct cpsw_priv *priv)
+{
+ __raw_writel(0x01, &priv->wr_regs->rx_en);
+ cpdma_chan_int_ctrl(priv->rxch, true);
+ return;
+}
+
+static void cpsw_intr_rx_disable(struct cpsw_priv *priv)
+{
+ __raw_writel(0x00, &priv->wr_regs->rx_en);
+ cpdma_chan_int_ctrl(priv->rxch, false);
+ return;
+}
+
+static void cpsw_intr_tx_enable(struct cpsw_priv *priv)
+{
+ __raw_writel(0x01, &priv->wr_regs->tx_en);
+ cpdma_chan_int_ctrl(priv->txch, true);
+ return;
+}
+
+static void cpsw_intr_tx_disable(struct cpsw_priv *priv)
{
- __raw_writel(0xFF, &priv->wr_regs->tx_en);
- __raw_writel(0xFF, &priv->wr_regs->rx_en);
+ __raw_writel(0x00, &priv->wr_regs->tx_en);
+ cpdma_chan_int_ctrl(priv->txch, false);
+ return;
+}
+static void cpsw_intr_misc_enable(struct cpsw_priv *priv)
+{
+ __raw_writel(0x04, &priv->wr_regs->misc_en);
cpdma_ctlr_int_ctrl(priv->dma, true);
return;
}
+static void cpsw_intr_misc_disable(struct cpsw_priv *priv)
+{
+ __raw_writel(0x00, &priv->wr_regs->misc_en);
+ cpdma_ctlr_int_ctrl(priv->dma, false);
+}
+
+static void cpsw_intr_enable(struct cpsw_priv *priv)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ if (priv->rx_irqs_disabled) {
+ enable_irq(priv->irqs_table[CPSW_IRQ_TYPE_RX]);
+ priv->rx_irqs_disabled = false;
+ }
+ if (priv->tx_irqs_disabled) {
+ enable_irq(priv->irqs_table[CPSW_IRQ_TYPE_TX]);
+ priv->tx_irqs_disabled = false;
+ }
+ if (priv->misc_irqs_disabled) {
+ enable_irq(priv->irqs_table[CPSW_IRQ_TYPE_MISC]);
+ priv->misc_irqs_disabled = false;
+ }
+ cpsw_intr_rx_enable(priv);
+ cpsw_intr_tx_enable(priv);
+ cpsw_intr_misc_enable(priv);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+ }
+
static void cpsw_intr_disable(struct cpsw_priv *priv)
{
- __raw_writel(0, &priv->wr_regs->tx_en);
- __raw_writel(0, &priv->wr_regs->rx_en);
+ unsigned long flags;
+ spin_lock_irqsave(&priv->lock, flags);
+ if (!priv->rx_irqs_disabled) {
+ disable_irq_nosync(priv->irqs_table[CPSW_IRQ_TYPE_RX]);
+ priv->rx_irqs_disabled = true;
+ }
+ if (!priv->tx_irqs_disabled) {
+ disable_irq_nosync(priv->irqs_table[CPSW_IRQ_TYPE_TX]);
+ priv->tx_irqs_disabled = true;
+ }
+ if (!priv->misc_irqs_disabled) {
+ disable_irq_nosync(priv->irqs_table[CPSW_IRQ_TYPE_MISC]);
+ priv->misc_irqs_disabled = true;
+ }
- cpdma_ctlr_int_ctrl(priv->dma, false);
+ cpsw_intr_rx_disable(priv);
+ cpsw_intr_tx_disable(priv);
+ cpsw_intr_misc_disable(priv);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
return;
}
@@ -422,15 +504,57 @@ void cpsw_rx_handler(void *token, int len, int status)
WARN_ON(ret < 0);
}
-static irqreturn_t cpsw_interrupt(int irq, void *dev_id)
+static irqreturn_t cpsw_rx_interrupt(int irq, void *dev_id)
{
struct cpsw_priv *priv = dev_id;
+ unsigned long flags;
- if (likely(netif_running(priv->ndev))) {
- cpsw_intr_disable(priv);
- cpsw_disable_irq(priv);
+ spin_lock_irqsave(&priv->lock, flags);
+ disable_irq_nosync(irq);
+ priv->rx_irqs_disabled = true;
+ cpsw_intr_rx_disable(priv);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ if (netif_running(priv->ndev))
napi_schedule(&priv->napi);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cpsw_tx_interrupt(int irq, void *dev_id)
+{
+ struct cpsw_priv *priv = dev_id;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ disable_irq_nosync(irq);
+ priv->tx_irqs_disabled = true;
+ cpsw_intr_tx_disable(priv);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ if (netif_running(priv->ndev))
+ napi_schedule(&priv->napi);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cpsw_misc_interrupt(int irq, void *dev_id)
+{
+ struct cpsw_priv *priv = dev_id;
+ unsigned long flags;
+
+ if (!netif_running(priv->ndev)) {
+ spin_lock_irqsave(&priv->lock, flags);
+ disable_irq_nosync(irq);
+ priv->misc_irqs_disabled = true;
+ cpsw_intr_misc_disable(priv);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return IRQ_HANDLED;
}
+
+ printk(KERN_ERR "Host error: %x\n", cpdma_get_host_state(priv->dma));
+ cpdma_ctlr_misc_eoi(priv->dma);
+
return IRQ_HANDLED;
}
@@ -445,20 +569,39 @@ static inline int cpsw_get_slave_port(struct cpsw_priv *priv, u32 slave_num)
static int cpsw_poll(struct napi_struct *napi, int budget)
{
struct cpsw_priv *priv = napi_to_priv(napi);
- int num_tx, num_rx;
+ int num_rx = 0;
+ int txcnt = 0;
+ int tx;
+ unsigned long flags;
- num_tx = cpdma_chan_process(priv->txch, 128);
- num_rx = cpdma_chan_process(priv->rxch, budget);
-
- if (num_rx || num_tx)
- cpsw_dbg(priv, intr, "poll %d rx, %d tx pkts\n",
- num_rx, num_tx);
+ /* cleanup the complete TX queue */
+ do {
+ tx = cpdma_chan_process(priv->txch, 128);
+ if (tx > 0)
+ txcnt += tx;
+ } while (tx == 128);
+ if (txcnt) {
+ spin_lock_irqsave(&priv->lock, flags);
+ if (priv->tx_irqs_disabled == true) {
+ cpsw_intr_tx_enable(priv);
+ cpdma_ctlr_tx_eoi(priv->dma);
+ enable_irq(priv->irqs_table[CPSW_IRQ_TYPE_TX]);
+ priv->tx_irqs_disabled = false;
+ }
+ spin_unlock_irqrestore(&priv->lock, flags);
+ }
+ num_rx = cpdma_chan_process(priv->rxch, budget);
if (num_rx < budget) {
- napi_complete(napi);
- cpsw_intr_enable(priv);
- cpdma_ctlr_eoi(priv->dma);
- cpsw_enable_irq(priv);
+ spin_lock_irqsave(&priv->lock, flags);
+ if (priv->rx_irqs_disabled == true) {
+ napi_complete(napi);
+ cpsw_intr_rx_enable(priv);
+ cpdma_ctlr_rx_eoi(priv->dma);
+ enable_irq(priv->irqs_table[CPSW_IRQ_TYPE_RX]);
+ priv->rx_irqs_disabled = false;
+ }
+ spin_unlock_irqrestore(&priv->lock, flags);
}
return num_rx;
@@ -679,7 +822,8 @@ static int cpsw_ndo_open(struct net_device *ndev)
cpdma_ctlr_start(priv->dma);
cpsw_intr_enable(priv);
napi_enable(&priv->napi);
- cpdma_ctlr_eoi(priv->dma);
+ cpdma_ctlr_rx_eoi(priv->dma);
+ cpdma_ctlr_tx_eoi(priv->dma);
return 0;
}
@@ -702,7 +846,6 @@ static int cpsw_ndo_stop(struct net_device *ndev)
napi_disable(&priv->napi);
netif_carrier_off(priv->ndev);
cpsw_intr_disable(priv);
- cpdma_ctlr_int_ctrl(priv->dma, false);
cpdma_ctlr_stop(priv->dma);
cpsw_ale_stop(priv->ale);
for_each_slave(priv, cpsw_slave_stop, priv);
@@ -896,12 +1039,11 @@ static void cpsw_ndo_tx_timeout(struct net_device *ndev)
cpsw_err(priv, tx_err, "transmit timeout, restarting dma\n");
priv->stats.tx_errors++;
cpsw_intr_disable(priv);
- cpdma_ctlr_int_ctrl(priv->dma, false);
cpdma_chan_stop(priv->txch);
cpdma_chan_start(priv->txch);
- cpdma_ctlr_int_ctrl(priv->dma, true);
cpsw_intr_enable(priv);
- cpdma_ctlr_eoi(priv->dma);
+ cpdma_ctlr_rx_eoi(priv->dma);
+ cpdma_ctlr_tx_eoi(priv->dma);
}
static struct net_device_stats *cpsw_ndo_get_stats(struct net_device *ndev)
@@ -916,11 +1058,11 @@ static void cpsw_ndo_poll_controller(struct net_device *ndev)
struct cpsw_priv *priv = netdev_priv(ndev);
cpsw_intr_disable(priv);
- cpdma_ctlr_int_ctrl(priv->dma, false);
- cpsw_interrupt(ndev->irq, priv);
- cpdma_ctlr_int_ctrl(priv->dma, true);
+ cpsw_rx_interrupt(priv->irqs_table[CPSW_IRQ_TYPE_RX], priv);
+ cpsw_tx_interrupt(priv->irqs_table[CPSW_IRQ_TYPE_TX], priv);
cpsw_intr_enable(priv);
- cpdma_ctlr_eoi(priv->dma);
+ cpdma_ctlr_rx_eoi(priv->dma);
+ cpdma_ctlr_tx_eoi(priv->dma);
}
#endif
@@ -1333,17 +1475,37 @@ static int cpsw_probe(struct platform_device *pdev)
goto clean_ale_ret;
}
+ priv->num_irqs = 0;
while ((res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, k))) {
- for (i = res->start; i <= res->end; i++) {
- if (request_irq(i, cpsw_interrupt, IRQF_DISABLED,
+ i = res->start;
+ switch (k) {
+ case CPSW_IRQ_TYPE_RX_THRESH:
+ i = 0;
+ break;
+ case CPSW_IRQ_TYPE_RX:
+ if (request_irq(i, cpsw_rx_interrupt, 0,
dev_name(&pdev->dev), priv)) {
dev_err(priv->dev, "error attaching irq\n");
- goto clean_ale_ret;
+ goto clean_irq_ret;
}
- priv->irqs_table[k] = i;
- priv->num_irqs = k;
+ break;
+ case CPSW_IRQ_TYPE_TX:
+ if (request_irq(i, cpsw_tx_interrupt, 0,
+ dev_name(&pdev->dev), priv)) {
+ dev_err(priv->dev, "error attaching irq\n");
+ goto clean_irq_ret;
+ }
+ break;
+ case CPSW_IRQ_TYPE_MISC:
+ if (request_irq(i, cpsw_misc_interrupt, 0,
+ dev_name(&pdev->dev), priv)) {
+ dev_err(priv->dev, "error attaching irq\n");
+ goto clean_irq_ret;
+ }
+ break;
}
- k++;
+ priv->irqs_table[k++] = i;
+ priv->num_irqs = k;
}
ndev->flags |= IFF_ALLMULTI; /* see cpsw_ndo_change_rx_flags() */
@@ -1371,7 +1533,10 @@ static int cpsw_probe(struct platform_device *pdev)
return 0;
clean_irq_ret:
- free_irq(ndev->irq, priv);
+ for (i = 0; i < priv->num_irqs; i++) {
+ if (priv->irqs_table[i] > 0)
+ free_irq(priv->irqs_table[i], priv);
+ }
clean_ale_ret:
cpsw_ale_destroy(priv->ale);
clean_dma_ret:
@@ -1402,12 +1567,16 @@ static int cpsw_remove(struct platform_device *pdev)
{
struct net_device *ndev = platform_get_drvdata(pdev);
struct cpsw_priv *priv = netdev_priv(ndev);
+ int i;
pr_info("removing device");
platform_set_drvdata(pdev, NULL);
cpts_unregister(&priv->cpts);
- free_irq(ndev->irq, priv);
+ for (i = 0; i < priv->num_irqs; i++) {
+ if (priv->irqs_table[i] > 0)
+ free_irq(priv->irqs_table[i], priv);
+ }
cpsw_ale_destroy(priv->ale);
cpdma_chan_destroy(priv->txch);
cpdma_chan_destroy(priv->rxch);
@@ -479,7 +479,7 @@ EXPORT_SYMBOL_GPL(cpdma_ctlr_destroy);
int cpdma_ctlr_int_ctrl(struct cpdma_ctlr *ctlr, bool enable)
{
unsigned long flags;
- int i, reg;
+ int reg;
spin_lock_irqsave(&ctlr->lock, flags);
if (ctlr->state != CPDMA_STATE_ACTIVE) {
@@ -489,20 +489,35 @@ int cpdma_ctlr_int_ctrl(struct cpdma_ctlr *ctlr, bool enable)
reg = enable ? CPDMA_DMAINTMASKSET : CPDMA_DMAINTMASKCLEAR;
dma_reg_write(ctlr, reg, CPDMA_DMAINT_HOSTERR);
-
- for (i = 0; i < ARRAY_SIZE(ctlr->channels); i++) {
- if (ctlr->channels[i])
- cpdma_chan_int_ctrl(ctlr->channels[i], enable);
- }
-
spin_unlock_irqrestore(&ctlr->lock, flags);
return 0;
}
+EXPORT_SYMBOL_GPL(cpdma_ctlr_int_ctrl);
+
+u32 cpdma_get_host_state(struct cpdma_ctlr *ctlr)
+{
+ return dma_reg_read(ctlr, CPDMA_DMASTATUS);
+}
+EXPORT_SYMBOL_GPL(cpdma_get_host_state);
+
+void cpdma_ctlr_rx_eoi(struct cpdma_ctlr *ctlr)
+{
+ dma_reg_write(ctlr, CPDMA_MACEOIVECTOR, 1);
+}
+EXPORT_SYMBOL_GPL(cpdma_ctlr_rx_eoi);
+
+void cpdma_ctlr_tx_eoi(struct cpdma_ctlr *ctlr)
+{
+ dma_reg_write(ctlr, CPDMA_MACEOIVECTOR, 2);
+}
+EXPORT_SYMBOL_GPL(cpdma_ctlr_tx_eoi);
-void cpdma_ctlr_eoi(struct cpdma_ctlr *ctlr)
+void cpdma_ctlr_misc_eoi(struct cpdma_ctlr *ctlr)
{
dma_reg_write(ctlr, CPDMA_MACEOIVECTOR, 0);
+ dma_reg_write(ctlr, CPDMA_MACEOIVECTOR, 3);
}
+EXPORT_SYMBOL_GPL(cpdma_ctlr_misc_eoi);
struct cpdma_chan *cpdma_chan_create(struct cpdma_ctlr *ctlr, int chan_num,
cpdma_handler_fn handler)
@@ -86,8 +86,11 @@ int cpdma_chan_submit(struct cpdma_chan *chan, void *token, void *data,
int cpdma_chan_process(struct cpdma_chan *chan, int quota);
int cpdma_ctlr_int_ctrl(struct cpdma_ctlr *ctlr, bool enable);
-void cpdma_ctlr_eoi(struct cpdma_ctlr *ctlr);
+void cpdma_ctlr_rx_eoi(struct cpdma_ctlr *ctlr);
+void cpdma_ctlr_tx_eoi(struct cpdma_ctlr *ctlr);
+void cpdma_ctlr_misc_eoi(struct cpdma_ctlr *ctlr);
int cpdma_chan_int_ctrl(struct cpdma_chan *chan, bool enable);
+u32 cpdma_get_host_state(struct cpdma_ctlr *ctlr);
enum cpdma_control {
CPDMA_CMD_IDLE, /* write-only */