diff mbox series

[v3,2/3] net: phy: ca_phy: Add driver for CAxxxx SoCs

Message ID 1600909154-1713-2-git-send-email-alex.nemirovsky@cortina-access.com
State Changes Requested
Delegated to: Tom Rini
Headers show
Series [v3,1/3] net: cortina_ni: Add eth support for Cortina Access CAxxxx SoCs | expand

Commit Message

Alex Nemirovsky Sept. 24, 2020, 12:59 a.m. UTC
From: Abbie Chang <abbie.chang@cortina-access.com>

Add phy driver support for MACs embedded inside Cortina Access SoCs

Signed-off-by: Abbie Chang <abbie.chang@cortina-access.com>
Signed-off-by: Alex Nemirovsky <alex.nemirovsky@cortina-access.com>

CC: Joe Hershberger <joe.hershberger@ni.com>
CC: Tom Rini <trini@konsulko.com>
CC: Aaron Tseng <aaron.tseng@cortina-access.com>

Moved out PHY specific code out of Cortina NI Ethernet driver
and into a Cortina Access PHY interface driver

---

(no changes since v1)

 MAINTAINERS              |   2 +
 drivers/net/phy/Kconfig  |   9 ++++
 drivers/net/phy/Makefile |   1 +
 drivers/net/phy/ca_phy.c | 133 +++++++++++++++++++++++++++++++++++++++++++++++
 drivers/net/phy/phy.c    |   2 +
 include/phy.h            |   1 +
 6 files changed, 148 insertions(+)
 create mode 100644 drivers/net/phy/ca_phy.c

Comments

Tom Rini Sept. 29, 2020, 5:57 p.m. UTC | #1
On Wed, Sep 23, 2020 at 05:59:13PM -0700, Alex Nemirovsky wrote:
> From: Abbie Chang <abbie.chang@cortina-access.com>
> 
> Add phy driver support for MACs embedded inside Cortina Access SoCs
> 
> Signed-off-by: Abbie Chang <abbie.chang@cortina-access.com>
> Signed-off-by: Alex Nemirovsky <alex.nemirovsky@cortina-access.com>
> 
> CC: Joe Hershberger <joe.hershberger@ni.com>
> CC: Tom Rini <trini@konsulko.com>
> CC: Aaron Tseng <aaron.tseng@cortina-access.com>
> 
> Moved out PHY specific code out of Cortina NI Ethernet driver
> and into a Cortina Access PHY interface driver
[snip]
> +__weak void __internal_phy_init(struct phy_device *phydev, int reset_phy)
> +{
> +	u8 phy_addr;
> +	u16     data;
> +
> +	/* should initialize 4 GPHYs at once */
> +	for (phy_addr = 4; phy_addr > 0; phy_addr--) {
> +		phydev->addr = phy_addr;
> +		phy_write(phydev, MDIO_DEVAD_NONE, 31, 0x0BC6);
> +		phy_write(phydev, MDIO_DEVAD_NONE, 16, 0x0053);
> +		phy_write(phydev, MDIO_DEVAD_NONE, 18, 0x4003);
> +		phy_write(phydev, MDIO_DEVAD_NONE, 22, 0x7e01);
> +		phy_write(phydev, MDIO_DEVAD_NONE, 31, 0x0A42);
> +		phy_write(phydev, MDIO_DEVAD_NONE, 31, 0x0A40);
> +		phy_write(phydev, MDIO_DEVAD_NONE,  0, 0x1140);
> +	}
> +
> +	/* workaround to fix GPHY fail */
> +	for (phy_addr = 1; phy_addr < 5; phy_addr++) {
> +		/* Clear clock fail interrupt */
> +		phydev->addr = phy_addr;
> +		phy_write(phydev, MDIO_DEVAD_NONE, 31, 0xB90);
> +		data = phy_read(phydev, MDIO_DEVAD_NONE, 19);
> +		if (data == 0x10) {
> +			phy_write(phydev, MDIO_DEVAD_NONE, 31, 0xB90);
> +			data = phy_read(phydev, MDIO_DEVAD_NONE, 19);
> +			printf("%s: read again.\n", __func__);
> +		}
> +
> +		printf("%s: phy_addr=%d, read register 19, value=0x%x\n",
> +		       __func__, phy_addr, data);
> +	}
> +}
> +
> +__weak void __external_phy_init(struct phy_device *phydev, int reset_phy)
> +{
> +	u16 val;
> +
> +	/* Disable response PHYAD=0 function of RTL8211 series PHY */
> +	/* REG31 write 0x0007, set to extension page */
> +	phy_write(phydev, MDIO_DEVAD_NONE, 31, 0x0007);
> +
> +	/* REG30 write 0x002C, set to extension page 44 */
> +	phy_write(phydev, MDIO_DEVAD_NONE, 30, 0x002C);
> +
> +	/*
> +	 * REG27 write bit[2] = 0 disable response PHYAD = 0 function.
> +	 * we should read REG27 and clear bit[2], and write back
> +	 */
> +	val = phy_read(phydev, MDIO_DEVAD_NONE, 27);
> +	val &= ~(1 << 2);
> +	phy_write(phydev, MDIO_DEVAD_NONE, 27, val);
> +
> +	/* REG31 write 0X0000, back to page0 */
> +	phy_write(phydev, MDIO_DEVAD_NONE, 31, 0x0000);
> +}
> +
> +static int rtl8211_external_config(struct phy_device *phydev)
> +{
> +	__external_phy_init(phydev, 0);
> +	printf("%s: initialize RTL8211 external done.\n", __func__);
> +	return 0;
> +}
> +
> +static int rtl8211_internal_config(struct phy_device *phydev)
> +{
> +	struct phy_device phydev_init;
> +
> +	memcpy(&phydev_init, phydev, sizeof(struct phy_device));
> +	/* should initialize 4 GPHYs at once */
> +	__internal_phy_init(&phydev_init, 0);
> +	printf("%s: initialize RTL8211 internal done.\n", __func__);
> +	return 0;
> +}

Why do we have two weak functions (which have very generic names and
could get overwritten by accident) and not just inlining them to the
functions?
Alex Nemirovsky Sept. 29, 2020, 6:17 p.m. UTC | #2
Tom,
Yes the intent is to allow overwrite the functionality elsewhere. I agree that the name is too generic and should be more unique to avoid name space conflicts. 

Abbie,
Please Review and update per Tom’s request.  
> On Sep 29, 2020, at 10:57 AM, Tom Rini <trini@konsulko.com> wrote:
> 
> On Wed, Sep 23, 2020 at 05:59:13PM -0700, Alex Nemirovsky wrote:
>> From: Abbie Chang <abbie.chang@cortina-access.com>
>> 
>> Add phy driver support for MACs embedded inside Cortina Access SoCs
>> 
>> Signed-off-by: Abbie Chang <abbie.chang@cortina-access.com>
>> Signed-off-by: Alex Nemirovsky <alex.nemirovsky@cortina-access.com>
>> 
>> CC: Joe Hershberger <joe.hershberger@ni.com>
>> CC: Tom Rini <trini@konsulko.com>
>> CC: Aaron Tseng <aaron.tseng@cortina-access.com>
>> 
>> Moved out PHY specific code out of Cortina NI Ethernet driver
>> and into a Cortina Access PHY interface driver
> [snip]
>> +__weak void __internal_phy_init(struct phy_device *phydev, int reset_phy)
>> +{
>> +    u8 phy_addr;
>> +    u16     data;
>> +
>> +    /* should initialize 4 GPHYs at once */
>> +    for (phy_addr = 4; phy_addr > 0; phy_addr--) {
>> +        phydev->addr = phy_addr;
>> +        phy_write(phydev, MDIO_DEVAD_NONE, 31, 0x0BC6);
>> +        phy_write(phydev, MDIO_DEVAD_NONE, 16, 0x0053);
>> +        phy_write(phydev, MDIO_DEVAD_NONE, 18, 0x4003);
>> +        phy_write(phydev, MDIO_DEVAD_NONE, 22, 0x7e01);
>> +        phy_write(phydev, MDIO_DEVAD_NONE, 31, 0x0A42);
>> +        phy_write(phydev, MDIO_DEVAD_NONE, 31, 0x0A40);
>> +        phy_write(phydev, MDIO_DEVAD_NONE,  0, 0x1140);
>> +    }
>> +
>> +    /* workaround to fix GPHY fail */
>> +    for (phy_addr = 1; phy_addr < 5; phy_addr++) {
>> +        /* Clear clock fail interrupt */
>> +        phydev->addr = phy_addr;
>> +        phy_write(phydev, MDIO_DEVAD_NONE, 31, 0xB90);
>> +        data = phy_read(phydev, MDIO_DEVAD_NONE, 19);
>> +        if (data == 0x10) {
>> +            phy_write(phydev, MDIO_DEVAD_NONE, 31, 0xB90);
>> +            data = phy_read(phydev, MDIO_DEVAD_NONE, 19);
>> +            printf("%s: read again.\n", __func__);
>> +        }
>> +
>> +        printf("%s: phy_addr=%d, read register 19, value=0x%x\n",
>> +               __func__, phy_addr, data);
>> +    }
>> +}
>> +
>> +__weak void __external_phy_init(struct phy_device *phydev, int reset_phy)
>> +{
>> +    u16 val;
>> +
>> +    /* Disable response PHYAD=0 function of RTL8211 series PHY */
>> +    /* REG31 write 0x0007, set to extension page */
>> +    phy_write(phydev, MDIO_DEVAD_NONE, 31, 0x0007);
>> +
>> +    /* REG30 write 0x002C, set to extension page 44 */
>> +    phy_write(phydev, MDIO_DEVAD_NONE, 30, 0x002C);
>> +
>> +    /*
>> +     * REG27 write bit[2] = 0 disable response PHYAD = 0 function.
>> +     * we should read REG27 and clear bit[2], and write back
>> +     */
>> +    val = phy_read(phydev, MDIO_DEVAD_NONE, 27);
>> +    val &= ~(1 << 2);
>> +    phy_write(phydev, MDIO_DEVAD_NONE, 27, val);
>> +
>> +    /* REG31 write 0X0000, back to page0 */
>> +    phy_write(phydev, MDIO_DEVAD_NONE, 31, 0x0000);
>> +}
>> +
>> +static int rtl8211_external_config(struct phy_device *phydev)
>> +{
>> +    __external_phy_init(phydev, 0);
>> +    printf("%s: initialize RTL8211 external done.\n", __func__);
>> +    return 0;
>> +}
>> +
>> +static int rtl8211_internal_config(struct phy_device *phydev)
>> +{
>> +    struct phy_device phydev_init;
>> +
>> +    memcpy(&phydev_init, phydev, sizeof(struct phy_device));
>> +    /* should initialize 4 GPHYs at once */
>> +    __internal_phy_init(&phydev_init, 0);
>> +    printf("%s: initialize RTL8211 internal done.\n", __func__);
>> +    return 0;
>> +}
> 
> Why do we have two weak functions (which have very generic names and
> could get overwritten by accident) and not just inlining them to the
> functions?
> 
> -- 
> Tom
Tom Rini Sept. 29, 2020, 6:21 p.m. UTC | #3
On Tue, Sep 29, 2020 at 06:17:26PM +0000, Alex Nemirovsky wrote:

> Tom,
> Yes the intent is to allow overwrite the functionality elsewhere. I agree that the name is too generic and should be more unique to avoid name space conflicts. 

Any sort of board specific tweaking should be done via device tree
properties, I strongly suspect.

> 
> Abbie,
> Please Review and update per Tom’s request.  
> > On Sep 29, 2020, at 10:57 AM, Tom Rini <trini@konsulko.com> wrote:
> > 
> > On Wed, Sep 23, 2020 at 05:59:13PM -0700, Alex Nemirovsky wrote:
> >> From: Abbie Chang <abbie.chang@cortina-access.com>
> >> 
> >> Add phy driver support for MACs embedded inside Cortina Access SoCs
> >> 
> >> Signed-off-by: Abbie Chang <abbie.chang@cortina-access.com>
> >> Signed-off-by: Alex Nemirovsky <alex.nemirovsky@cortina-access.com>
> >> 
> >> CC: Joe Hershberger <joe.hershberger@ni.com>
> >> CC: Tom Rini <trini@konsulko.com>
> >> CC: Aaron Tseng <aaron.tseng@cortina-access.com>
> >> 
> >> Moved out PHY specific code out of Cortina NI Ethernet driver
> >> and into a Cortina Access PHY interface driver
> > [snip]
> >> +__weak void __internal_phy_init(struct phy_device *phydev, int reset_phy)
> >> +{
> >> +    u8 phy_addr;
> >> +    u16     data;
> >> +
> >> +    /* should initialize 4 GPHYs at once */
> >> +    for (phy_addr = 4; phy_addr > 0; phy_addr--) {
> >> +        phydev->addr = phy_addr;
> >> +        phy_write(phydev, MDIO_DEVAD_NONE, 31, 0x0BC6);
> >> +        phy_write(phydev, MDIO_DEVAD_NONE, 16, 0x0053);
> >> +        phy_write(phydev, MDIO_DEVAD_NONE, 18, 0x4003);
> >> +        phy_write(phydev, MDIO_DEVAD_NONE, 22, 0x7e01);
> >> +        phy_write(phydev, MDIO_DEVAD_NONE, 31, 0x0A42);
> >> +        phy_write(phydev, MDIO_DEVAD_NONE, 31, 0x0A40);
> >> +        phy_write(phydev, MDIO_DEVAD_NONE,  0, 0x1140);
> >> +    }
> >> +
> >> +    /* workaround to fix GPHY fail */
> >> +    for (phy_addr = 1; phy_addr < 5; phy_addr++) {
> >> +        /* Clear clock fail interrupt */
> >> +        phydev->addr = phy_addr;
> >> +        phy_write(phydev, MDIO_DEVAD_NONE, 31, 0xB90);
> >> +        data = phy_read(phydev, MDIO_DEVAD_NONE, 19);
> >> +        if (data == 0x10) {
> >> +            phy_write(phydev, MDIO_DEVAD_NONE, 31, 0xB90);
> >> +            data = phy_read(phydev, MDIO_DEVAD_NONE, 19);
> >> +            printf("%s: read again.\n", __func__);
> >> +        }
> >> +
> >> +        printf("%s: phy_addr=%d, read register 19, value=0x%x\n",
> >> +               __func__, phy_addr, data);
> >> +    }
> >> +}
> >> +
> >> +__weak void __external_phy_init(struct phy_device *phydev, int reset_phy)
> >> +{
> >> +    u16 val;
> >> +
> >> +    /* Disable response PHYAD=0 function of RTL8211 series PHY */
> >> +    /* REG31 write 0x0007, set to extension page */
> >> +    phy_write(phydev, MDIO_DEVAD_NONE, 31, 0x0007);
> >> +
> >> +    /* REG30 write 0x002C, set to extension page 44 */
> >> +    phy_write(phydev, MDIO_DEVAD_NONE, 30, 0x002C);
> >> +
> >> +    /*
> >> +     * REG27 write bit[2] = 0 disable response PHYAD = 0 function.
> >> +     * we should read REG27 and clear bit[2], and write back
> >> +     */
> >> +    val = phy_read(phydev, MDIO_DEVAD_NONE, 27);
> >> +    val &= ~(1 << 2);
> >> +    phy_write(phydev, MDIO_DEVAD_NONE, 27, val);
> >> +
> >> +    /* REG31 write 0X0000, back to page0 */
> >> +    phy_write(phydev, MDIO_DEVAD_NONE, 31, 0x0000);
> >> +}
> >> +
> >> +static int rtl8211_external_config(struct phy_device *phydev)
> >> +{
> >> +    __external_phy_init(phydev, 0);
> >> +    printf("%s: initialize RTL8211 external done.\n", __func__);
> >> +    return 0;
> >> +}
> >> +
> >> +static int rtl8211_internal_config(struct phy_device *phydev)
> >> +{
> >> +    struct phy_device phydev_init;
> >> +
> >> +    memcpy(&phydev_init, phydev, sizeof(struct phy_device));
> >> +    /* should initialize 4 GPHYs at once */
> >> +    __internal_phy_init(&phydev_init, 0);
> >> +    printf("%s: initialize RTL8211 internal done.\n", __func__);
> >> +    return 0;
> >> +}
> > 
> > Why do we have two weak functions (which have very generic names and
> > could get overwritten by accident) and not just inlining them to the
> > functions?
> > 
> > -- 
> > Tom
Alex Nemirovsky Sept. 29, 2020, 6:29 p.m. UTC | #4
Tom,

> On Sep 29, 2020, at 11:21 AM, Tom Rini <trini@konsulko.com> wrote:
> 
> On Tue, Sep 29, 2020 at 06:17:26PM +0000, Alex Nemirovsky wrote:
> 
>> Tom,
>> Yes the intent is to allow overwrite the functionality elsewhere. I agree that the name is too generic and should be more unique to avoid name space conflicts. 
> 
> Any sort of board specific tweaking should be done via device tree
> properties, I strongly suspect.

You maybe right.  That might be a design possibility.  I personally don’t know enough about this to comment either way.  I’ll pass this on to the team for consideration and review of the overall approach.  
> 
>> 
>> Abbie,
>> Please Review and update per Tom’s request.  
>>>> On Sep 29, 2020, at 10:57 AM, Tom Rini <trini@konsulko.com> wrote:
>>> 
>>> On Wed, Sep 23, 2020 at 05:59:13PM -0700, Alex Nemirovsky wrote:
>>>> From: Abbie Chang <abbie.chang@cortina-access.com>
>>>> 
>>>> Add phy driver support for MACs embedded inside Cortina Access SoCs
>>>> 
>>>> Signed-off-by: Abbie Chang <abbie.chang@cortina-access.com>
>>>> Signed-off-by: Alex Nemirovsky <alex.nemirovsky@cortina-access.com>
>>>> 
>>>> CC: Joe Hershberger <joe.hershberger@ni.com>
>>>> CC: Tom Rini <trini@konsulko.com>
>>>> CC: Aaron Tseng <aaron.tseng@cortina-access.com>
>>>> 
>>>> Moved out PHY specific code out of Cortina NI Ethernet driver
>>>> and into a Cortina Access PHY interface driver
>>> [snip]
>>>> +__weak void __internal_phy_init(struct phy_device *phydev, int reset_phy)
>>>> +{
>>>> +    u8 phy_addr;
>>>> +    u16     data;
>>>> +
>>>> +    /* should initialize 4 GPHYs at once */
>>>> +    for (phy_addr = 4; phy_addr > 0; phy_addr--) {
>>>> +        phydev->addr = phy_addr;
>>>> +        phy_write(phydev, MDIO_DEVAD_NONE, 31, 0x0BC6);
>>>> +        phy_write(phydev, MDIO_DEVAD_NONE, 16, 0x0053);
>>>> +        phy_write(phydev, MDIO_DEVAD_NONE, 18, 0x4003);
>>>> +        phy_write(phydev, MDIO_DEVAD_NONE, 22, 0x7e01);
>>>> +        phy_write(phydev, MDIO_DEVAD_NONE, 31, 0x0A42);
>>>> +        phy_write(phydev, MDIO_DEVAD_NONE, 31, 0x0A40);
>>>> +        phy_write(phydev, MDIO_DEVAD_NONE,  0, 0x1140);
>>>> +    }
>>>> +
>>>> +    /* workaround to fix GPHY fail */
>>>> +    for (phy_addr = 1; phy_addr < 5; phy_addr++) {
>>>> +        /* Clear clock fail interrupt */
>>>> +        phydev->addr = phy_addr;
>>>> +        phy_write(phydev, MDIO_DEVAD_NONE, 31, 0xB90);
>>>> +        data = phy_read(phydev, MDIO_DEVAD_NONE, 19);
>>>> +        if (data == 0x10) {
>>>> +            phy_write(phydev, MDIO_DEVAD_NONE, 31, 0xB90);
>>>> +            data = phy_read(phydev, MDIO_DEVAD_NONE, 19);
>>>> +            printf("%s: read again.\n", __func__);
>>>> +        }
>>>> +
>>>> +        printf("%s: phy_addr=%d, read register 19, value=0x%x\n",
>>>> +               __func__, phy_addr, data);
>>>> +    }
>>>> +}
>>>> +
>>>> +__weak void __external_phy_init(struct phy_device *phydev, int reset_phy)
>>>> +{
>>>> +    u16 val;
>>>> +
>>>> +    /* Disable response PHYAD=0 function of RTL8211 series PHY */
>>>> +    /* REG31 write 0x0007, set to extension page */
>>>> +    phy_write(phydev, MDIO_DEVAD_NONE, 31, 0x0007);
>>>> +
>>>> +    /* REG30 write 0x002C, set to extension page 44 */
>>>> +    phy_write(phydev, MDIO_DEVAD_NONE, 30, 0x002C);
>>>> +
>>>> +    /*
>>>> +     * REG27 write bit[2] = 0 disable response PHYAD = 0 function.
>>>> +     * we should read REG27 and clear bit[2], and write back
>>>> +     */
>>>> +    val = phy_read(phydev, MDIO_DEVAD_NONE, 27);
>>>> +    val &= ~(1 << 2);
>>>> +    phy_write(phydev, MDIO_DEVAD_NONE, 27, val);
>>>> +
>>>> +    /* REG31 write 0X0000, back to page0 */
>>>> +    phy_write(phydev, MDIO_DEVAD_NONE, 31, 0x0000);
>>>> +}
>>>> +
>>>> +static int rtl8211_external_config(struct phy_device *phydev)
>>>> +{
>>>> +    __external_phy_init(phydev, 0);
>>>> +    printf("%s: initialize RTL8211 external done.\n", __func__);
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static int rtl8211_internal_config(struct phy_device *phydev)
>>>> +{
>>>> +    struct phy_device phydev_init;
>>>> +
>>>> +    memcpy(&phydev_init, phydev, sizeof(struct phy_device));
>>>> +    /* should initialize 4 GPHYs at once */
>>>> +    __internal_phy_init(&phydev_init, 0);
>>>> +    printf("%s: initialize RTL8211 internal done.\n", __func__);
>>>> +    return 0;
>>>> +}
>>> 
>>> Why do we have two weak functions (which have very generic names and
>>> could get overwritten by accident) and not just inlining them to the
>>> functions?
>>> 
>>> -- 
>>> Tom
> 
> -- 
> Tom
diff mbox series

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index dd4156c..a9ab791 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -199,6 +199,7 @@  F:	drivers/i2c/i2c-cortina.c
 F:	drivers/i2c/i2c-cortina.h
 F:	drivers/net/cortina_ni.c
 F:	drivers/net/cortina_ni.h
+F:	drivers/net/phy/ca_phy.c
 
 ARM/CZ.NIC TURRIS MOX SUPPORT
 M:	Marek Behun <marek.behun@nic.cz>
@@ -785,6 +786,7 @@  F:	drivers/i2c/i2c-cortina.c
 F:	drivers/i2c/i2c-cortina.h
 F:	drivers/net/cortina_ni.c
 F:	drivers/net/cortina_ni.h
+F:	drivers/net/phy/ca_phy.c
 
 MIPS MSCC
 M:	Gregory CLEMENT <gregory.clement@bootlin.com>
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 4e1a93b..7aa60be 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -122,6 +122,15 @@  config SYS_CORTINA_FW_IN_SPIFLASH
 
 endchoice
 
+config PHY_CORTINA_ACCESS
+	bool "Cortina Access Ethernet PHYs support"
+	default y
+	depends on CORTINA_NI_ENET
+	help
+		Cortina Access Ethernet PHYs support
+
+		Cortina Access Ethernet PHYs init process
+
 config PHY_DAVICOM
 	bool "Davicom Ethernet PHYs support"
 
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 6e72233..e967f82 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -14,6 +14,7 @@  obj-$(CONFIG_PHY_AQUANTIA) += aquantia.o
 obj-$(CONFIG_PHY_ATHEROS) += atheros.o
 obj-$(CONFIG_PHY_BROADCOM) += broadcom.o
 obj-$(CONFIG_PHY_CORTINA) += cortina.o
+obj-$(CONFIG_PHY_CORTINA_ACCESS) += ca_phy.o
 obj-$(CONFIG_PHY_DAVICOM) += davicom.o
 obj-$(CONFIG_PHY_ET1011C) += et1011c.o
 obj-$(CONFIG_PHY_LXT) += lxt.o
diff --git a/drivers/net/phy/ca_phy.c b/drivers/net/phy/ca_phy.c
new file mode 100644
index 0000000..d3a49f9
--- /dev/null
+++ b/drivers/net/phy/ca_phy.c
@@ -0,0 +1,133 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Cortina CS4315/CS4340 10G PHY drivers
+ *
+ * Copyright 2014 Freescale Semiconductor, Inc.
+ * Copyright 2018 NXP
+ *
+ */
+
+#include <config.h>
+#include <common.h>
+#include <log.h>
+#include <malloc.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/err.h>
+#include <phy.h>
+
+#define PHY_ID_RTL8211_EXT      0x001cc910
+#define PHY_ID_RTL8211_INT	0x001cc980
+#define PHY_ID_MASK             0xFFFFFFF0
+
+__weak void __internal_phy_init(struct phy_device *phydev, int reset_phy)
+{
+	u8 phy_addr;
+	u16     data;
+
+	/* should initialize 4 GPHYs at once */
+	for (phy_addr = 4; phy_addr > 0; phy_addr--) {
+		phydev->addr = phy_addr;
+		phy_write(phydev, MDIO_DEVAD_NONE, 31, 0x0BC6);
+		phy_write(phydev, MDIO_DEVAD_NONE, 16, 0x0053);
+		phy_write(phydev, MDIO_DEVAD_NONE, 18, 0x4003);
+		phy_write(phydev, MDIO_DEVAD_NONE, 22, 0x7e01);
+		phy_write(phydev, MDIO_DEVAD_NONE, 31, 0x0A42);
+		phy_write(phydev, MDIO_DEVAD_NONE, 31, 0x0A40);
+		phy_write(phydev, MDIO_DEVAD_NONE,  0, 0x1140);
+	}
+
+	/* workaround to fix GPHY fail */
+	for (phy_addr = 1; phy_addr < 5; phy_addr++) {
+		/* Clear clock fail interrupt */
+		phydev->addr = phy_addr;
+		phy_write(phydev, MDIO_DEVAD_NONE, 31, 0xB90);
+		data = phy_read(phydev, MDIO_DEVAD_NONE, 19);
+		if (data == 0x10) {
+			phy_write(phydev, MDIO_DEVAD_NONE, 31, 0xB90);
+			data = phy_read(phydev, MDIO_DEVAD_NONE, 19);
+			printf("%s: read again.\n", __func__);
+		}
+
+		printf("%s: phy_addr=%d, read register 19, value=0x%x\n",
+		       __func__, phy_addr, data);
+	}
+}
+
+__weak void __external_phy_init(struct phy_device *phydev, int reset_phy)
+{
+	u16 val;
+
+	/* Disable response PHYAD=0 function of RTL8211 series PHY */
+	/* REG31 write 0x0007, set to extension page */
+	phy_write(phydev, MDIO_DEVAD_NONE, 31, 0x0007);
+
+	/* REG30 write 0x002C, set to extension page 44 */
+	phy_write(phydev, MDIO_DEVAD_NONE, 30, 0x002C);
+
+	/*
+	 * REG27 write bit[2] = 0 disable response PHYAD = 0 function.
+	 * we should read REG27 and clear bit[2], and write back
+	 */
+	val = phy_read(phydev, MDIO_DEVAD_NONE, 27);
+	val &= ~(1 << 2);
+	phy_write(phydev, MDIO_DEVAD_NONE, 27, val);
+
+	/* REG31 write 0X0000, back to page0 */
+	phy_write(phydev, MDIO_DEVAD_NONE, 31, 0x0000);
+}
+
+static int rtl8211_external_config(struct phy_device *phydev)
+{
+	__external_phy_init(phydev, 0);
+	printf("%s: initialize RTL8211 external done.\n", __func__);
+	return 0;
+}
+
+static int rtl8211_internal_config(struct phy_device *phydev)
+{
+	struct phy_device phydev_init;
+
+	memcpy(&phydev_init, phydev, sizeof(struct phy_device));
+	/* should initialize 4 GPHYs at once */
+	__internal_phy_init(&phydev_init, 0);
+	printf("%s: initialize RTL8211 internal done.\n", __func__);
+	return 0;
+}
+
+static int rtl8211_probe(struct phy_device *phydev)
+{
+	/* disable reset behavior */
+	phydev->flags = PHY_FLAG_BROKEN_RESET;
+	return 0;
+}
+
+/* Support for RTL8211 External PHY */
+struct phy_driver rtl8211_external_driver = {
+	.name = "Cortina RTL8211 External",
+	.uid = PHY_ID_RTL8211_EXT,
+	.mask = PHY_ID_MASK,
+	.features = PHY_GBIT_FEATURES,
+	.config = &rtl8211_external_config,
+	.probe = &rtl8211_probe,
+	.startup = &genphy_startup,
+};
+
+/* Support for RTL8211 Internal PHY */
+struct phy_driver rtl8211_internal_driver = {
+	.name = "Cortina RTL8211 Inrernal",
+	.uid = PHY_ID_RTL8211_INT,
+	.mask = PHY_ID_MASK,
+	.features = PHY_GBIT_FEATURES,
+	.config = &rtl8211_internal_config,
+	.probe = &rtl8211_probe,
+	.startup = &genphy_startup,
+};
+
+int phy_cortina_access_init(void)
+{
+	phy_register(&rtl8211_external_driver);
+	phy_register(&rtl8211_internal_driver);
+	return 0;
+}
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 6778989..f0b4aa4 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -500,6 +500,8 @@  int phy_init(void)
 #ifdef CONFIG_PHY_CORTINA
 	phy_cortina_init();
 #endif
+	if (IS_ENABLED(CONFIG_PHY_CORTINA_ACCESS))
+		phy_cortina_access_init();
 #ifdef CONFIG_PHY_DAVICOM
 	phy_davicom_init();
 #endif
diff --git a/include/phy.h b/include/phy.h
index 1dbbf65..c2ecd70 100644
--- a/include/phy.h
+++ b/include/phy.h
@@ -493,6 +493,7 @@  int phy_aquantia_init(void);
 int phy_atheros_init(void);
 int phy_broadcom_init(void);
 int phy_cortina_init(void);
+int phy_cortina_access_init(void);
 int phy_davicom_init(void);
 int phy_et1011c_init(void);
 int phy_lxt_init(void);