diff mbox series

Add a driver for Renesas uPD60620 and uPD60620A PHYs

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

Commit Message

Bernd Edlinger Sept. 22, 2017, 5:08 p.m. UTC
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

Comments

Andrew Lunn Sept. 22, 2017, 5:59 p.m. UTC | #1
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
Bernd Edlinger Sept. 28, 2017, 6:12 p.m. UTC | #2
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);
Andrew Lunn Sept. 28, 2017, 10:59 p.m. UTC | #3
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
Bernd Edlinger Oct. 8, 2017, 1:31 p.m. UTC | #4
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 mbox series

Patch

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 },
+	{ }
+};