Skip to content

Commit 5f4d487

Browse files
mwallekuba-moo
authored andcommitted
net: phy: mxl-gpy: add MDINT workaround
At least the GPY215B and GPY215C has a bug where it is still driving the interrupt line (MDINT) even after the interrupt status register is read and its bits are cleared. This will cause an interrupt storm. Although the MDINT is multiplexed with a GPIO pin and theoretically we could switch the pinmux to GPIO input mode, this isn't possible because the access to this register will stall exactly as long as the interrupt line is asserted. We exploit this very fact and just read a random internal register in our interrupt handler. This way, it will be delayed until the external interrupt line is released and an interrupt storm is avoided. The internal register access via the mailbox was deduced by looking at the downstream PHY API because the datasheet doesn't mention any of this. Fixes: 7d901a1 ("net: phy: add Maxlinear GPY115/21x/24x driver") Signed-off-by: Michael Walle <[email protected]> Reviewed-by: Andrew Lunn <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent 65e349f commit 5f4d487

File tree

1 file changed

+85
-0
lines changed

1 file changed

+85
-0
lines changed

drivers/net/phy/mxl-gpy.c

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <linux/module.h>
1010
#include <linux/bitfield.h>
1111
#include <linux/hwmon.h>
12+
#include <linux/mutex.h>
1213
#include <linux/phy.h>
1314
#include <linux/polynomial.h>
1415
#include <linux/netdevice.h>
@@ -70,14 +71,28 @@
7071
#define VPSPEC1_TEMP_STA 0x0E
7172
#define VPSPEC1_TEMP_STA_DATA GENMASK(9, 0)
7273

74+
/* Mailbox */
75+
#define VSPEC1_MBOX_DATA 0x5
76+
#define VSPEC1_MBOX_ADDRLO 0x6
77+
#define VSPEC1_MBOX_CMD 0x7
78+
#define VSPEC1_MBOX_CMD_ADDRHI GENMASK(7, 0)
79+
#define VSPEC1_MBOX_CMD_RD (0 << 8)
80+
#define VSPEC1_MBOX_CMD_READY BIT(15)
81+
7382
/* WoL */
7483
#define VPSPEC2_WOL_CTL 0x0E06
7584
#define VPSPEC2_WOL_AD01 0x0E08
7685
#define VPSPEC2_WOL_AD23 0x0E09
7786
#define VPSPEC2_WOL_AD45 0x0E0A
7887
#define WOL_EN BIT(0)
7988

89+
/* Internal registers, access via mbox */
90+
#define REG_GPIO0_OUT 0xd3ce00
91+
8092
struct gpy_priv {
93+
/* serialize mailbox acesses */
94+
struct mutex mbox_lock;
95+
8196
u8 fw_major;
8297
u8 fw_minor;
8398
};
@@ -187,6 +202,45 @@ static int gpy_hwmon_register(struct phy_device *phydev)
187202
}
188203
#endif
189204

205+
static int gpy_mbox_read(struct phy_device *phydev, u32 addr)
206+
{
207+
struct gpy_priv *priv = phydev->priv;
208+
int val, ret;
209+
u16 cmd;
210+
211+
mutex_lock(&priv->mbox_lock);
212+
213+
ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_MBOX_ADDRLO,
214+
addr);
215+
if (ret)
216+
goto out;
217+
218+
cmd = VSPEC1_MBOX_CMD_RD;
219+
cmd |= FIELD_PREP(VSPEC1_MBOX_CMD_ADDRHI, addr >> 16);
220+
221+
ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_MBOX_CMD, cmd);
222+
if (ret)
223+
goto out;
224+
225+
/* The mbox read is used in the interrupt workaround. It was observed
226+
* that a read might take up to 2.5ms. This is also the time for which
227+
* the interrupt line is stuck low. To be on the safe side, poll the
228+
* ready bit for 10ms.
229+
*/
230+
ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1,
231+
VSPEC1_MBOX_CMD, val,
232+
(val & VSPEC1_MBOX_CMD_READY),
233+
500, 10000, false);
234+
if (ret)
235+
goto out;
236+
237+
ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_MBOX_DATA);
238+
239+
out:
240+
mutex_unlock(&priv->mbox_lock);
241+
return ret;
242+
}
243+
190244
static int gpy_config_init(struct phy_device *phydev)
191245
{
192246
int ret;
@@ -201,6 +255,13 @@ static int gpy_config_init(struct phy_device *phydev)
201255
return ret < 0 ? ret : 0;
202256
}
203257

258+
static bool gpy_has_broken_mdint(struct phy_device *phydev)
259+
{
260+
/* At least these PHYs are known to have broken interrupt handling */
261+
return phydev->drv->phy_id == PHY_ID_GPY215B ||
262+
phydev->drv->phy_id == PHY_ID_GPY215C;
263+
}
264+
204265
static int gpy_probe(struct phy_device *phydev)
205266
{
206267
struct device *dev = &phydev->mdio.dev;
@@ -218,6 +279,7 @@ static int gpy_probe(struct phy_device *phydev)
218279
if (!priv)
219280
return -ENOMEM;
220281
phydev->priv = priv;
282+
mutex_init(&priv->mbox_lock);
221283

222284
fw_version = phy_read(phydev, PHY_FWV);
223285
if (fw_version < 0)
@@ -492,6 +554,29 @@ static irqreturn_t gpy_handle_interrupt(struct phy_device *phydev)
492554
if (!(reg & PHY_IMASK_MASK))
493555
return IRQ_NONE;
494556

557+
/* The PHY might leave the interrupt line asserted even after PHY_ISTAT
558+
* is read. To avoid interrupt storms, delay the interrupt handling as
559+
* long as the PHY drives the interrupt line. An internal bus read will
560+
* stall as long as the interrupt line is asserted, thus just read a
561+
* random register here.
562+
* Because we cannot access the internal bus at all while the interrupt
563+
* is driven by the PHY, there is no way to make the interrupt line
564+
* unstuck (e.g. by changing the pinmux to GPIO input) during that time
565+
* frame. Therefore, polling is the best we can do and won't do any more
566+
* harm.
567+
* It was observed that this bug happens on link state and link speed
568+
* changes on a GPY215B and GYP215C independent of the firmware version
569+
* (which doesn't mean that this list is exhaustive).
570+
*/
571+
if (gpy_has_broken_mdint(phydev) &&
572+
(reg & (PHY_IMASK_LSTC | PHY_IMASK_LSPC))) {
573+
reg = gpy_mbox_read(phydev, REG_GPIO0_OUT);
574+
if (reg < 0) {
575+
phy_error(phydev);
576+
return IRQ_NONE;
577+
}
578+
}
579+
495580
phy_trigger_machine(phydev);
496581

497582
return IRQ_HANDLED;

0 commit comments

Comments
 (0)