Message ID | AM5PR0701MB26574EBBE4E7CACC9173B345E4670@AM5PR0701MB2657.eurprd07.prod.outlook.com |
---|---|
State | Changes Requested, archived |
Delegated to: | David Miller |
Headers | show |
Series | Add a driver for Renesas uPD60620 and uPD60620A PHYs | expand |
On Fri, Sep 22, 2017 at 05:08:45PM +0000, Bernd Edlinger wrote: > Signed-off-by: Bernd Edlinger <bernd.edlinger@hotmail.de> > --- > drivers/net/phy/Kconfig | 5 + > drivers/net/phy/Makefile | 1 + > drivers/net/phy/uPD60620.c | 226 > +++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 232 insertions(+) > create mode 100644 drivers/net/phy/uPD60620.c > > diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig > index a9d16a3..25089f0 100644 > --- a/drivers/net/phy/Kconfig > +++ b/drivers/net/phy/Kconfig > @@ -287,6 +287,11 @@ config DP83867_PHY > ---help--- > Currently supports the DP83867 PHY. > > +config RENESAS_PHY > + tristate "Driver for Renesas PHYs" > + ---help--- > + Supports the uPD60620 and uPD60620A PHYs. > + Hi Bernd Please call this "Reneseas PHYs" and place in it alphabetical order. > config FIXED_PHY > tristate "MDIO Bus/PHY emulation with fixed speed/link PHYs" > depends on PHYLIB > diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile > index 416df92..1404ad3 100644 > --- a/drivers/net/phy/Makefile > +++ b/drivers/net/phy/Makefile > @@ -72,6 +72,7 @@ obj-$(CONFIG_MICROSEMI_PHY) += mscc.o > obj-$(CONFIG_NATIONAL_PHY) += national.o > obj-$(CONFIG_QSEMI_PHY) += qsemi.o > obj-$(CONFIG_REALTEK_PHY) += realtek.o > +obj-$(CONFIG_RENESAS_PHY) += uPD60620.o > obj-$(CONFIG_ROCKCHIP_PHY) += rockchip.o > obj-$(CONFIG_SMSC_PHY) += smsc.o > obj-$(CONFIG_STE10XP) += ste10Xp.o > diff --git a/drivers/net/phy/uPD60620.c b/drivers/net/phy/uPD60620.c > new file mode 100644 > index 0000000..b3d900c > --- /dev/null > +++ b/drivers/net/phy/uPD60620.c > @@ -0,0 +1,226 @@ > +/* > + * Driver for the Renesas PHY uPD60620. > + * > + * Copyright (C) 2015 Softing Industrial Automation GmbH > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + * > + */ > + > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/phy.h> > + > +#define UPD60620_PHY_ID 0xb8242824 > + > +/* Extended Registers and values */ > +/* PHY Special Control/Status */ > +#define PHY_PHYSCR 0x1F /* PHY.31 */ > +#define PHY_PHYSCR_10MB 0x0004 /* PHY speed = 10mb */ > +#define PHY_PHYSCR_100MB 0x0008 /* PHY speed = 100mb */ > +#define PHY_PHYSCR_DUPLEX 0x0010 /* PHY Duplex */ > +#define PHY_PHYSCR_RSVD5 0x0020 /* Reserved Bit 5 */ > +#define PHY_PHYSCR_MIIMOD 0x0040 /* Enable 4B5B MII mode */ Are any of these comments actually useful. It seems like the defines are pretty obvious. > +#define PHY_PHYSCR_RSVD7 0x0080 /* Reserved Bit 7 */ > +#define PHY_PHYSCR_RSVD8 0x0100 /* Reserved Bit 8 */ > +#define PHY_PHYSCR_RSVD9 0x0200 /* Reserved Bit 9 */ > +#define PHY_PHYSCR_RSVD10 0x0400 /* Reserved Bit 10 */ > +#define PHY_PHYSCR_RSVD11 0x0800 /* Reserved Bit 11 */ > +#define PHY_PHYSCR_ANDONE 0x1000 /* Auto negotiation done */ > +#define PHY_PHYSCR_RSVD13 0x2000 /* Reserved Bit 13 */ > +#define PHY_PHYSCR_RSVD14 0x4000 /* Reserved Bit 14 */ > +#define PHY_PHYSCR_RSVD15 0x8000 /* Reserved Bit 15 */ It looks like the only register you use is SCR and SPM. Maybe delete all the rest? Or do you plan to add more features making use of these registers? > +/* Init PHY */ > + > +static int upd60620_config_init(struct phy_device *phydev) > +{ > + /* Enable support for passive HUBs (could be a strap option) */ > + /* PHYMODE: All speeds, HD in parallel detect */ > + return phy_write(phydev, PHY_SPM, 0x0180 | phydev->mdio.addr); > +} > + > +/* Get PHY status from common registers */ > + > +static int upd60620_read_status(struct phy_device *phydev) > +{ > + int phy_state; > + > + /* Read negotiated state */ > + phy_state = phy_read(phydev, MII_BMSR); > + if (phy_state < 0) > + return phy_state; > + > + phydev->link = 0; > + phydev->lp_advertising = 0; > + phydev->pause = 0; > + phydev->asym_pause = 0; > + > + if (phy_state & BMSR_ANEGCOMPLETE) { It is worth comparing this against genphy_read_status() which is the reference implementation. You would normally check if auto negotiation is enabled, not if it has completed. If it is enabled you read the current negotiated state, even if it is not completed. > + phy_state = phy_read(phydev, PHY_PHYSCR); > + if (phy_state < 0) > + return phy_state; > + > + if (phy_state & (PHY_PHYSCR_10MB | PHY_PHYSCR_100MB)) { > + phydev->link = 1; > + phydev->speed = SPEED_10; > + phydev->duplex = DUPLEX_HALF; > + > + if (phy_state & PHY_PHYSCR_100MB) > + phydev->speed = SPEED_100; > + if (phy_state & PHY_PHYSCR_DUPLEX) > + phydev->duplex = DUPLEX_FULL; > + > + phy_state = phy_read(phydev, MII_LPA); > + if (phy_state < 0) > + return phy_state; > + > + phydev->lp_advertising > + = mii_lpa_to_ethtool_lpa_t(phy_state); > + > + if (phydev->duplex == DUPLEX_FULL) { > + if (phy_state & LPA_PAUSE_CAP) > + phydev->pause = 1; > + if (phy_state & LPA_PAUSE_ASYM) > + phydev->asym_pause = 1; > + } > + } > + } else if (phy_state & BMSR_LSTATUS) { The else clause is then for a fixed configuration. Since all you are looking at is BMCR, you can probably just cut/paste from genphy_read_status(). > + phy_state = phy_read(phydev, MII_BMCR); > + if (phy_state < 0) > + return phy_state; > + > + if (!(phy_state & BMCR_ANENABLE)) { > + phydev->link = 1; > + phydev->speed = SPEED_10; > + phydev->duplex = DUPLEX_HALF; > + > + if (phy_state & BMCR_SPEED100) > + phydev->speed = SPEED_100; > + if (phy_state & BMCR_FULLDPLX) > + phydev->duplex = DUPLEX_FULL; > + } > + } > + return 0; > +} Andrew
On 09/22/17 19:59, Andrew Lunn wrote: > On Fri, Sep 22, 2017 at 05:08:45PM +0000, Bernd Edlinger wrote: >> >> +config RENESAS_PHY >> + tristate "Driver for Renesas PHYs" >> + ---help--- >> + Supports the uPD60620 and uPD60620A PHYs. >> + > > Hi Bernd > > Please call this "Reneseas PHYs" and place in it alphabetical order. > Done. >> + >> +/* Extended Registers and values */ >> +/* PHY Special Control/Status */ >> +#define PHY_PHYSCR 0x1F /* PHY.31 */ >> +#define PHY_PHYSCR_10MB 0x0004 /* PHY speed = 10mb */ >> +#define PHY_PHYSCR_100MB 0x0008 /* PHY speed = 100mb */ >> +#define PHY_PHYSCR_DUPLEX 0x0010 /* PHY Duplex */ >> +#define PHY_PHYSCR_RSVD5 0x0020 /* Reserved Bit 5 */ >> +#define PHY_PHYSCR_MIIMOD 0x0040 /* Enable 4B5B MII mode */ > > Are any of these comments actually useful. It seems like the defines > are pretty obvious. > >> +#define PHY_PHYSCR_RSVD7 0x0080 /* Reserved Bit 7 */ >> +#define PHY_PHYSCR_RSVD8 0x0100 /* Reserved Bit 8 */ >> +#define PHY_PHYSCR_RSVD9 0x0200 /* Reserved Bit 9 */ >> +#define PHY_PHYSCR_RSVD10 0x0400 /* Reserved Bit 10 */ >> +#define PHY_PHYSCR_RSVD11 0x0800 /* Reserved Bit 11 */ >> +#define PHY_PHYSCR_ANDONE 0x1000 /* Auto negotiation done */ >> +#define PHY_PHYSCR_RSVD13 0x2000 /* Reserved Bit 13 */ >> +#define PHY_PHYSCR_RSVD14 0x4000 /* Reserved Bit 14 */ >> +#define PHY_PHYSCR_RSVD15 0x8000 /* Reserved Bit 15 */ > > It looks like the only register you use is SCR and SPM. Maybe delete > all the rest? Or do you plan to add more features making use of these > registers? > No, I removed all unused defines for now. >> + phydev->link = 0; >> + phydev->lp_advertising = 0; >> + phydev->pause = 0; >> + phydev->asym_pause = 0; >> + >> + if (phy_state & BMSR_ANEGCOMPLETE) { > > It is worth comparing this against genphy_read_status() which is the > reference implementation. You would normally check if auto negotiation > is enabled, not if it has completed. If it is enabled you read the > current negotiated state, even if it is not completed. > Do you suggest that there are cases where auto negotiation does not reach completion, and still provides a usable link status? I have tried to connect to link partners with fixed configuration but even then the auto negotiation always competes normally. >> + phy_state = phy_read(phydev, PHY_PHYSCR); >> + if (phy_state < 0) >> + return phy_state; >> + >> + if (phy_state & (PHY_PHYSCR_10MB | PHY_PHYSCR_100MB)) { >> + phydev->link = 1; >> + phydev->speed = SPEED_10; >> + phydev->duplex = DUPLEX_HALF; >> + >> + if (phy_state & PHY_PHYSCR_100MB) >> + phydev->speed = SPEED_100; >> + if (phy_state & PHY_PHYSCR_DUPLEX) >> + phydev->duplex = DUPLEX_FULL; >> + >> + phy_state = phy_read(phydev, MII_LPA); >> + if (phy_state < 0) >> + return phy_state; >> + >> + phydev->lp_advertising >> + = mii_lpa_to_ethtool_lpa_t(phy_state); >> + >> + if (phydev->duplex == DUPLEX_FULL) { >> + if (phy_state & LPA_PAUSE_CAP) >> + phydev->pause = 1; >> + if (phy_state & LPA_PAUSE_ASYM) >> + phydev->asym_pause = 1; >> + } >> + } >> + } else if (phy_state & BMSR_LSTATUS) { > > The else clause is then for a fixed configuration. Since all you are > looking at is BMCR, you can probably just cut/paste from > genphy_read_status(). > I think I can fold the fixed speed case in the auto negotiation case: The PHYSCR has always the correct values for fixed settings. I was initially unsure if I should look at it while autonegotiation is not complete, but as you pointed out, that is the generally accepted practice. Thanks Bernd. From 2e101aed8466b314251972d1eaccfb43cf177078 Mon Sep 17 00:00:00 2001 From: Bernd Edlinger <bernd.edlinger@hotmail.de> Date: Thu, 21 Sep 2017 15:46:16 +0200 Subject: [PATCH 2/5] Add a driver for Renesas uPD60620 and uPD60620A PHYs. Signed-off-by: Bernd Edlinger <bernd.edlinger@hotmail.de> --- drivers/net/phy/Kconfig | 5 +++ drivers/net/phy/Makefile | 1 + drivers/net/phy/uPD60620.c | 109 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 drivers/net/phy/uPD60620.c diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index a9d16a3..f67943b 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -366,6 +366,11 @@ config REALTEK_PHY ---help--- Supports the Realtek 821x PHY. +config RENESAS_PHY + tristate "Driver for Renesas PHYs" + ---help--- + Supports the Renesas PHYs uPD60620 and uPD60620A. + config ROCKCHIP_PHY tristate "Driver for Rockchip Ethernet PHYs" ---help--- diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 416df92..1404ad3 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -72,6 +72,7 @@ obj-$(CONFIG_MICROSEMI_PHY) += mscc.o obj-$(CONFIG_NATIONAL_PHY) += national.o obj-$(CONFIG_QSEMI_PHY) += qsemi.o obj-$(CONFIG_REALTEK_PHY) += realtek.o +obj-$(CONFIG_RENESAS_PHY) += uPD60620.o obj-$(CONFIG_ROCKCHIP_PHY) += rockchip.o obj-$(CONFIG_SMSC_PHY) += smsc.o obj-$(CONFIG_STE10XP) += ste10Xp.o diff --git a/drivers/net/phy/uPD60620.c b/drivers/net/phy/uPD60620.c new file mode 100644 index 0000000..96b3347 --- /dev/null +++ b/drivers/net/phy/uPD60620.c @@ -0,0 +1,109 @@ +/* + * Driver for the Renesas PHY uPD60620. + * + * Copyright (C) 2015 Softing Industrial Automation GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/phy.h> + +#define UPD60620_PHY_ID 0xb8242824 + +/* Extended Registers and values */ +/* PHY Special Control/Status */ +#define PHY_PHYSCR 0x1F /* PHY.31 */ +#define PHY_PHYSCR_10MB 0x0004 /* PHY speed = 10mb */ +#define PHY_PHYSCR_100MB 0x0008 /* PHY speed = 100mb */ +#define PHY_PHYSCR_DUPLEX 0x0010 /* PHY Duplex */ + +/* PHY Special Modes */ +#define PHY_SPM 0x12 /* PHY.18 */ + +/* Init PHY */ + +static int upd60620_config_init(struct phy_device *phydev) +{ + /* Enable support for passive HUBs (could be a strap option) */ + /* PHYMODE: All speeds, HD in parallel detect */ + return phy_write(phydev, PHY_SPM, 0x0180 | phydev->mdio.addr); +} + +/* Get PHY status from common registers */ + +static int upd60620_read_status(struct phy_device *phydev) +{ + int phy_state; + + /* Read negotiated state */ + phy_state = phy_read(phydev, MII_BMSR); + if (phy_state < 0) + return phy_state; + + phydev->link = 0; + phydev->lp_advertising = 0; + phydev->pause = 0; + phydev->asym_pause = 0; + + if (phy_state & (BMSR_ANEGCOMPLETE | BMSR_LSTATUS)) { + phy_state = phy_read(phydev, PHY_PHYSCR); + if (phy_state < 0) + return phy_state; + + if (phy_state & (PHY_PHYSCR_10MB | PHY_PHYSCR_100MB)) { + phydev->link = 1; + phydev->speed = SPEED_10; + phydev->duplex = DUPLEX_HALF; + + if (phy_state & PHY_PHYSCR_100MB) + phydev->speed = SPEED_100; + if (phy_state & PHY_PHYSCR_DUPLEX) + phydev->duplex = DUPLEX_FULL; + + phy_state = phy_read(phydev, MII_LPA); + if (phy_state < 0) + return phy_state; + + phydev->lp_advertising + = mii_lpa_to_ethtool_lpa_t(phy_state); + + if (phydev->duplex == DUPLEX_FULL) { + if (phy_state & LPA_PAUSE_CAP) + phydev->pause = 1; + if (phy_state & LPA_PAUSE_ASYM) + phydev->asym_pause = 1; + } + } + } + return 0; +} + +MODULE_DESCRIPTION("Renesas uPD60620 PHY driver"); +MODULE_AUTHOR("Bernd Edlinger <bernd.edlinger@hotmail.de>"); +MODULE_LICENSE("GPL"); + +static struct phy_driver upd60620_driver[1] = { { + .phy_id = UPD60620_PHY_ID, + .phy_id_mask = 0xfffffffe, + .name = "Renesas uPD60620", + .features = PHY_BASIC_FEATURES, + .flags = 0, + .config_init = upd60620_config_init, + .config_aneg = genphy_config_aneg, + .read_status = upd60620_read_status, +} }; + +module_phy_driver(upd60620_driver); + +static struct mdio_device_id __maybe_unused upd60620_tbl[] = { + { UPD60620_PHY_ID, 0xfffffffe }, + { } +}; + +MODULE_DEVICE_TABLE(mdio, upd60620_tbl);
Hi Bernd > >> + if (phy_state & BMSR_ANEGCOMPLETE) { > > > > It is worth comparing this against genphy_read_status() which is the > > reference implementation. You would normally check if auto negotiation > > is enabled, not if it has completed. If it is enabled you read the > > current negotiated state, even if it is not completed. > > > > Do you suggest that there are cases where auto negotiation does not > reach completion, and still provides a usable link status? My experience is that it often return 10/half, since everything should support that. And depending on what the MAC is doing, packets can sometime get across the link. > I have tried to connect to link partners with fixed configuration > but even then the auto negotiation always competes normally. Which is a bit odd. There are a few different possibilities here. The peer PHY driver is broken. Rather than doing fixed, it actually set the possible negotiation options to just the one setting you tried to fix it to. And hence the uPD60620 device negotiated fine. Or the uPD60620 is broken is said it negotiated, but in fact it failed. What was the result? 10/Half, or the fixed values you set the peer to? > > >From 2e101aed8466b314251972d1eaccfb43cf177078 Mon Sep 17 00:00:00 2001 > From: Bernd Edlinger <bernd.edlinger@hotmail.de> > Date: Thu, 21 Sep 2017 15:46:16 +0200 > Subject: [PATCH 2/5] Add a driver for Renesas uPD60620 and uPD60620A PHYs. > > Signed-off-by: Bernd Edlinger <bernd.edlinger@hotmail.de> Please send this is a new patch. If we were to take this is is, all the comments above would end up in the commit message. > --- Under the --- you can however add comments which don't go into the commit log. Good practice is to list the things you changed since the previous version. Thanks Andrew
Hi Andrew, sorry for delayed reply. Looks like I did not receive a copy of your e-mail. >> Do you suggest that there are cases where auto negotiation does not >> reach completion, and still provides a usable link status? > > My experience is that it often return 10/half, since everything should > support that. And depending on what the MAC is doing, packets can > sometime get across the link. >> >> I have tried to connect to link partners with fixed configuration >> but even then the auto negotiation always competes normally. > > Which is a bit odd. > > There are a few different possibilities here. The peer PHY driver is > broken. Rather than doing fixed, it actually set the possible > negotiation options to just the one setting you tried to fix it > to. And hence the uPD60620 device negotiated fine. Or the uPD60620 is > broken is said it negotiated, but in fact it failed. > > What was the result? 10/Half, or the fixed values you set the peer to? This is a dual-channel PHY, so I did just connect both ports and played with the mii-tool -F / -A in different combinations on each port and observed what happens when the cable is plugged in. What happens is that the port with autonegotiation enabled detects the correct speed and always half duplex, so the ASIC _pretends_ that autonegotiatiation completes, when in fact only parallel detection succeeded. Of course the other phy may be in full-duplex mode, but that can not be detected by parallel detection. The duplex mode would be full duplex by default, but my initialization overrides a possible strap option and changes that to half duplex: + /* Enable support for passive HUBs (could be a strap option) */ + /* PHYMODE: All speeds, HD in parallel detect */ + return phy_write(phydev, PHY_SPM, 0x0180 | phydev->mdio.addr); >> >> Signed-off-by: Bernd Edlinger <bernd.edlinger@hotmail.de> > > Please send this is a new patch. If we were to take this is is, all > the comments above would end up in the commit message. > > --- > > Under the --- you can however add comments which don't go into the > commit log. Good practice is to list the things you changed since the > previous version. Thanks, I did not know that. I will re-send the patch in a new thread. Bernd.
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index a9d16a3..25089f0 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -287,6 +287,11 @@ config DP83867_PHY ---help--- Currently supports the DP83867 PHY. +config RENESAS_PHY + tristate "Driver for Renesas PHYs" + ---help--- + Supports the uPD60620 and uPD60620A PHYs. + config FIXED_PHY tristate "MDIO Bus/PHY emulation with fixed speed/link PHYs" depends on PHYLIB diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 416df92..1404ad3 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -72,6 +72,7 @@ obj-$(CONFIG_MICROSEMI_PHY) += mscc.o obj-$(CONFIG_NATIONAL_PHY) += national.o obj-$(CONFIG_QSEMI_PHY) += qsemi.o obj-$(CONFIG_REALTEK_PHY) += realtek.o +obj-$(CONFIG_RENESAS_PHY) += uPD60620.o obj-$(CONFIG_ROCKCHIP_PHY) += rockchip.o obj-$(CONFIG_SMSC_PHY) += smsc.o obj-$(CONFIG_STE10XP) += ste10Xp.o diff --git a/drivers/net/phy/uPD60620.c b/drivers/net/phy/uPD60620.c new file mode 100644 index 0000000..b3d900c --- /dev/null +++ b/drivers/net/phy/uPD60620.c @@ -0,0 +1,226 @@ +/* + * Driver for the Renesas PHY uPD60620. + * + * Copyright (C) 2015 Softing Industrial Automation GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/phy.h> + +#define UPD60620_PHY_ID 0xb8242824 + +/* Extended Registers and values */ +/* PHY Special Control/Status */ +#define PHY_PHYSCR 0x1F /* PHY.31 */ +#define PHY_PHYSCR_10MB 0x0004 /* PHY speed = 10mb */ +#define PHY_PHYSCR_100MB 0x0008 /* PHY speed = 100mb */ +#define PHY_PHYSCR_DUPLEX 0x0010 /* PHY Duplex */ +#define PHY_PHYSCR_RSVD5 0x0020 /* Reserved Bit 5 */ +#define PHY_PHYSCR_MIIMOD 0x0040 /* Enable 4B5B MII mode */ +#define PHY_PHYSCR_RSVD7 0x0080 /* Reserved Bit 7 */ +#define PHY_PHYSCR_RSVD8 0x0100 /* Reserved Bit 8 */ +#define PHY_PHYSCR_RSVD9 0x0200 /* Reserved Bit 9 */ +#define PHY_PHYSCR_RSVD10 0x0400 /* Reserved Bit 10 */ +#define PHY_PHYSCR_RSVD11 0x0800 /* Reserved Bit 11 */ +#define PHY_PHYSCR_ANDONE 0x1000 /* Auto negotiation done */ +#define PHY_PHYSCR_RSVD13 0x2000 /* Reserved Bit 13 */ +#define PHY_PHYSCR_RSVD14 0x4000 /* Reserved Bit 14 */ +#define PHY_PHYSCR_RSVD15 0x8000 /* Reserved Bit 15 */ + +/* PHY Global Config Mapping */ +#define PHY_GLOBAL_CONFIG 0x07 +/* PHY GPIO Config Register 1 */ +#define PHY_GPIO_CONFIG1 0x01 /* PHY 7.1 */ +#define PHY_GPIO4_INT0 0x000d /* GPIO4 configuration */ +#define PHY_GPIO5_INT1 0x00d0 /* GPIO5 configuration */ + +/* PHY Interrupt Control Register */ +#define PHY_ICR 0x1e /* PHY.30 */ +#define PHY_ICR_RSVD0 0x0001 /* Reserved bit 0 */ +#define PHY_ICR_ANCPRRN 0x0002 /* Auto negotiation paged received */ +#define PHY_ICR_PDFEN 0x0004 /* Parallel detection fault */ +#define PHY_ICR_ANCLPAEN 0x0008 /* Auto negotiation last page ack */ +#define PHY_ICR_LNKINTEN 0x0010 /* Link down */ +#define PHY_ICR_REMFD 0x0020 /* Remote fault detected */ +#define PHY_ICR_ANCINTEN 0x0040 /* Auto negotiation complete */ +#define PHY_ICR_EOEN 0x0080 /* Energy on generated */ +#define PHY_ICR_RSVD8 0x0100 /* Reserved bit 8 */ +#define PHY_ICR_FEQTRGEN 0x0200 /* FEQ Trigger */ +#define PHY_ICR_BERTRGEN 0x0400 /* BER Counter Trigger */ +#define PHY_ICR_MLINTEN 0x0800 /* Maxlvl */ +#define PHY_ICR_CLPINTEN 0x1000 /* Clipping */ +#define PHY_ICR_RSVD13 0x2000 /* Reserved bit 13 */ +#define PHY_ICR_RSVD14 0x4000 /* Reserved bit 14 */ +#define PHY_ICR_RSVD15 0x8000 /* Reserved bit 15 */ + +/* PHY Interrupt Status Register */ +#define PHY_ISR 0x1d /* PHY.29 */ +#define PHY_ISR_DUPINT 0x0000 /* Placeholder for Duplex/Speed intr */ +#define PHY_ISR_RSVD0 0x0001 /* Reserved bit 0 */ +#define PHY_ISR_ANCPR 0x0002 /* Auto negotiation paged received */ +#define PHY_ISR_PDF 0x0004 /* Parallel detection fault */ +#define PHY_ISR_ANCLPA 0x0008 /* Auto negotiation last page ack */ +#define PHY_ISR_LNKINT 0x0010 /* Link down */ +#define PHY_ISR_REMFD 0x0020 /* Remote fault detected */ +#define PHY_ISR_ANCINT 0x0040 /* Auto negotiation complete */ +#define PHY_ISR_EO 0x0080 /* Energy on generated */ +#define PHY_ISR_RSVD8 0x0100 /* Reserved bit 8 */ +#define PHY_ISR_FEQTRG 0x0200 /* FEQ Trigger */ +#define PHY_ISR_BERTRG 0x0400 /* BER Counter Trigger */ +#define PHY_ISR_MLINT 0x0800 /* Maxlvl */ +#define PHY_ISR_CLPINT 0x1000 /* Clipping */ +#define PHY_ISR_RSVD13 0x2000 /* Reserved bit 13 */ +#define PHY_ISR_RSVD14 0x4000 /* Reserved bit 14 */ +#define PHY_ISR_RSVD15 0x8000 /* Reserved bit 15 */ + +/* PHY Diagnosis Control/Status Register*/ +#define PHY_DCS 0x19 /* PHY.25 */ +#define PHY_DCS_PWDIAG1 0x0001 /* Pulse Width Diagnosis -- 8ns */ +#define PHY_DCS_DIAGSEL 0x0020 /* Diagnosis select Tx or Rx */ +#define PHY_DCS_DIAGPOL 0x0040 /* Diagnosis stop polarity */ +#define PHY_DCS_DIAGDONE 0x0080 /* Diagnosis done */ +#define PHY_DCS_ADCTRIG05 0x0700 /* ADC Trigger level 0.5V for cable len */ +#define PHY_DCS_ADCMAX 0x3F00 /* ADC Max value */ +#define PHY_DCS_DIAGINIT 0x4000 /* Init TDR test */ + +/* PHY Diagnosis Counter Register */ +#define PHY_DCR 0x1a /* PHY.26 */ +#define PHY_DCR_DIGNCNT 0x00ff /* Diagnosis Count */ +#define PHY_DCR_CNTWIN 0xff00 /* Diagnosis Count Window */ + +/* PHY Mode Control/Status Register */ +#define PHY_MCS 0x11 /* PHY.17 */ +#define PHY_MCS_RSVD0 0x0001 /* Reserved bit 0 */ +#define PHY_MCS_ENERGYON 0x0002 /* Energy on the Line status */ +#define PHY_MCS_FGLS 0x0004 /* Force Good Link Status */ +#define PHY_MCS_RSVD3 0x0008 /* Reserved bit 3 */ +#define PHY_MCS_DCDPATGEN 0x0010 /* DCD measuring pattern generation */ +#define PHY_MCS_RSVD5 0x0020 /* Reserved bit 5 */ +#define PHY_MCS_MDIMODE 0x0040 /* Force MDIX or MDI */ +#define PHY_MCS_AUTOMDIX 0x0080 /* Auto MDIX enable */ +#define PHY_MCS_FASTEST 0x0100 /* Auto negotiation test mode */ +#define PHY_MCS_FARLOOP 0x0200 /* Remote loopback enable */ +#define PHY_MCS_RSVD10 0x0400 /* Reserved bit 10 */ +#define PHY_MCS_LOWSQEN 0x0800 /* Squelch Threshold */ +#define PHY_MCS_RSVD12 0x1000 /* Reserved bit 12 */ +#define PHY_MCS_EPWRDOWN 0x2000 /* Power down mode enable */ +#define PHY_MCS_FASTRIP 0x4000 /* 10Base-T Fast mode */ +#define PHY_MCS_RSVD15 0x8000 /* Reserved bit 15 */ + +/* PHY Special Modes */ +#define PHY_SPM 0x12 /* PHY.18 */ +#define PHY_SPM_FX_MODE 0x400 /* Enable 100BASE-FX mode */ +#define PHY_SPM_PHYMODE 0x1E0 /* PHY mode of operation */ +#define PHY_SPM_PHYADD 0x01F /* PHY address of device */ + +/* PHY BER Counter Register */ +#define PHY_BER 0x17 /* PHY.23 */ +#define PHY_BER_COUNT 0x007f /* BER Count Bits 6-0 */ +#define PHY_BER_WINDOW 0x0780 /* BER Window Bits 10-7 */ +#define PHY_BER_CNT_TRIG 0x3800 /* BER count trigger bits 13-11 */ +#define PHY_BER_CNT_LNK_EN 0x4000 /* BER count link enable */ +#define PHY_BER_LNK_OK 0x8000 /* BER link OK */ + +/* Init PHY */ + +static int upd60620_config_init(struct phy_device *phydev) +{ + /* Enable support for passive HUBs (could be a strap option) */ + /* PHYMODE: All speeds, HD in parallel detect */ + return phy_write(phydev, PHY_SPM, 0x0180 | phydev->mdio.addr); +} + +/* Get PHY status from common registers */ + +static int upd60620_read_status(struct phy_device *phydev) +{ + int phy_state; + + /* Read negotiated state */ + phy_state = phy_read(phydev, MII_BMSR); + if (phy_state < 0) + return phy_state; + + phydev->link = 0; + phydev->lp_advertising = 0; + phydev->pause = 0; + phydev->asym_pause = 0; + + if (phy_state & BMSR_ANEGCOMPLETE) { + phy_state = phy_read(phydev, PHY_PHYSCR); + if (phy_state < 0) + return phy_state; + + if (phy_state & (PHY_PHYSCR_10MB | PHY_PHYSCR_100MB)) { + phydev->link = 1; + phydev->speed = SPEED_10; + phydev->duplex = DUPLEX_HALF; + + if (phy_state & PHY_PHYSCR_100MB) + phydev->speed = SPEED_100; + if (phy_state & PHY_PHYSCR_DUPLEX) + phydev->duplex = DUPLEX_FULL; + + phy_state = phy_read(phydev, MII_LPA); + if (phy_state < 0) + return phy_state; + + phydev->lp_advertising + = mii_lpa_to_ethtool_lpa_t(phy_state); + + if (phydev->duplex == DUPLEX_FULL) { + if (phy_state & LPA_PAUSE_CAP) + phydev->pause = 1; + if (phy_state & LPA_PAUSE_ASYM) + phydev->asym_pause = 1; + } + } + } else if (phy_state & BMSR_LSTATUS) { + phy_state = phy_read(phydev, MII_BMCR); + if (phy_state < 0) + return phy_state; + + if (!(phy_state & BMCR_ANENABLE)) { + phydev->link = 1; + phydev->speed = SPEED_10; + phydev->duplex = DUPLEX_HALF; + + if (phy_state & BMCR_SPEED100) + phydev->speed = SPEED_100; + if (phy_state & BMCR_FULLDPLX) + phydev->duplex = DUPLEX_FULL; + } + } + return 0; +} + +MODULE_DESCRIPTION("Renesas uPD60620 PHY driver"); +MODULE_AUTHOR("Bernd Edlinger <bernd.edlinger@hotmail.de>"); +MODULE_LICENSE("GPL"); + +static struct phy_driver upd60620_driver[1] = { { + .phy_id = UPD60620_PHY_ID, + .phy_id_mask = 0xfffffffe, + .name = "Renesas uPD60620", + .features = PHY_BASIC_FEATURES, + .flags = 0, + .config_init = upd60620_config_init, + .config_aneg = genphy_config_aneg, + .read_status = upd60620_read_status, +} }; + +module_phy_driver(upd60620_driver); + +static struct mdio_device_id __maybe_unused upd60620_tbl[] = { + { UPD60620_PHY_ID, 0xfffffffe }, + { } +};
Signed-off-by: Bernd Edlinger <bernd.edlinger@hotmail.de> --- drivers/net/phy/Kconfig | 5 + drivers/net/phy/Makefile | 1 + drivers/net/phy/uPD60620.c | 226 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 232 insertions(+) create mode 100644 drivers/net/phy/uPD60620.c + +MODULE_DEVICE_TABLE(mdio, upd60620_tbl); -- 2.7.4