|
@@ -6,7 +6,7 @@
|
|
|
*
|
|
|
* License 1: GPLv2
|
|
|
*
|
|
|
- * Copyright (c) 2014 Advanced Micro Devices, Inc.
|
|
|
+ * Copyright (c) 2014-2016 Advanced Micro Devices, Inc.
|
|
|
*
|
|
|
* This file is free software; you may copy, redistribute and/or modify
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
@@ -56,7 +56,7 @@
|
|
|
*
|
|
|
* License 2: Modified BSD
|
|
|
*
|
|
|
- * Copyright (c) 2014 Advanced Micro Devices, Inc.
|
|
|
+ * Copyright (c) 2014-2016 Advanced Micro Devices, Inc.
|
|
|
* All rights reserved.
|
|
|
*
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
@@ -626,10 +626,22 @@ static irqreturn_t xgbe_an_isr(int irq, void *data)
|
|
|
|
|
|
netif_dbg(pdata, intr, pdata->netdev, "AN interrupt received\n");
|
|
|
|
|
|
- /* Interrupt reason must be read and cleared outside of IRQ context */
|
|
|
- disable_irq_nosync(pdata->an_irq);
|
|
|
+ /* Disable AN interrupts */
|
|
|
+ XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INTMASK, 0);
|
|
|
+
|
|
|
+ /* Save the interrupt(s) that fired */
|
|
|
+ pdata->an_int = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_INT);
|
|
|
|
|
|
- queue_work(pdata->an_workqueue, &pdata->an_irq_work);
|
|
|
+ if (pdata->an_int) {
|
|
|
+ /* Clear the interrupt(s) that fired and process them */
|
|
|
+ XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INT, ~pdata->an_int);
|
|
|
+
|
|
|
+ queue_work(pdata->an_workqueue, &pdata->an_irq_work);
|
|
|
+ } else {
|
|
|
+ /* Enable AN interrupts */
|
|
|
+ XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INTMASK,
|
|
|
+ XGBE_AN_INT_MASK);
|
|
|
+ }
|
|
|
|
|
|
return IRQ_HANDLED;
|
|
|
}
|
|
@@ -673,34 +685,26 @@ static void xgbe_an_state_machine(struct work_struct *work)
|
|
|
struct xgbe_prv_data,
|
|
|
an_work);
|
|
|
enum xgbe_an cur_state = pdata->an_state;
|
|
|
- unsigned int int_reg, int_mask;
|
|
|
|
|
|
mutex_lock(&pdata->an_mutex);
|
|
|
|
|
|
- /* Read the interrupt */
|
|
|
- int_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_INT);
|
|
|
- if (!int_reg)
|
|
|
+ if (!pdata->an_int)
|
|
|
goto out;
|
|
|
|
|
|
next_int:
|
|
|
- if (int_reg & XGBE_AN_PG_RCV) {
|
|
|
+ if (pdata->an_int & XGBE_AN_PG_RCV) {
|
|
|
pdata->an_state = XGBE_AN_PAGE_RECEIVED;
|
|
|
- int_mask = XGBE_AN_PG_RCV;
|
|
|
- } else if (int_reg & XGBE_AN_INC_LINK) {
|
|
|
+ pdata->an_int &= ~XGBE_AN_PG_RCV;
|
|
|
+ } else if (pdata->an_int & XGBE_AN_INC_LINK) {
|
|
|
pdata->an_state = XGBE_AN_INCOMPAT_LINK;
|
|
|
- int_mask = XGBE_AN_INC_LINK;
|
|
|
- } else if (int_reg & XGBE_AN_INT_CMPLT) {
|
|
|
+ pdata->an_int &= ~XGBE_AN_INC_LINK;
|
|
|
+ } else if (pdata->an_int & XGBE_AN_INT_CMPLT) {
|
|
|
pdata->an_state = XGBE_AN_COMPLETE;
|
|
|
- int_mask = XGBE_AN_INT_CMPLT;
|
|
|
+ pdata->an_int &= ~XGBE_AN_INT_CMPLT;
|
|
|
} else {
|
|
|
pdata->an_state = XGBE_AN_ERROR;
|
|
|
- int_mask = 0;
|
|
|
}
|
|
|
|
|
|
- /* Clear the interrupt to be processed */
|
|
|
- int_reg &= ~int_mask;
|
|
|
- XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INT, int_reg);
|
|
|
-
|
|
|
pdata->an_result = pdata->an_state;
|
|
|
|
|
|
again:
|
|
@@ -740,14 +744,14 @@ again:
|
|
|
}
|
|
|
|
|
|
if (pdata->an_state == XGBE_AN_NO_LINK) {
|
|
|
- int_reg = 0;
|
|
|
+ pdata->an_int = 0;
|
|
|
XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INT, 0);
|
|
|
} else if (pdata->an_state == XGBE_AN_ERROR) {
|
|
|
netdev_err(pdata->netdev,
|
|
|
"error during auto-negotiation, state=%u\n",
|
|
|
cur_state);
|
|
|
|
|
|
- int_reg = 0;
|
|
|
+ pdata->an_int = 0;
|
|
|
XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INT, 0);
|
|
|
}
|
|
|
|
|
@@ -765,11 +769,12 @@ again:
|
|
|
if (cur_state != pdata->an_state)
|
|
|
goto again;
|
|
|
|
|
|
- if (int_reg)
|
|
|
+ if (pdata->an_int)
|
|
|
goto next_int;
|
|
|
|
|
|
out:
|
|
|
- enable_irq(pdata->an_irq);
|
|
|
+ /* Enable AN interrupts on the way out */
|
|
|
+ XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INTMASK, XGBE_AN_INT_MASK);
|
|
|
|
|
|
mutex_unlock(&pdata->an_mutex);
|
|
|
}
|