diff mbox series

[v4,1/2] net: cortina_ni: Add eth support for Cortina Access CAxxxx SoCs

Message ID 1591171519-10406-1-git-send-email-alex.nemirovsky@cortina-access.com
State New
Headers show
Series [v4,1/2] net: cortina_ni: Add eth support for Cortina Access CAxxxx SoCs | expand

Commit Message

Alex Nemirovsky June 3, 2020, 8:05 a.m. UTC
From: Aaron Tseng <aaron.tseng at cortina-access.com>

Add Cortina Access Ethernet device driver for CAxxxx SoCs.
This driver supports only the DM_ETH network model.

Signed-off-by: Aaron Tseng <aaron.tseng at cortina-access.com>
Signed-off-by: Alex Nemirovsky <alex.nemirovsky at cortina-access.com>

CC: Joe Hershberger <joe.hershberger at ni.com>
CC: Abbie Chang <abbie.chang at Cortina-Access.com>
CC: Tom Rini <trini at konsulko.com>

---

Changes in v4: None
Changes in v3:
- Changed commit comment to state that only DM model is supported
- Removed blank line at end of C file

Changes in v2:
- Remove legacy mode support
- Add support for additional SoC variants
- Remove unused variables

 MAINTAINERS              |    4 +
 drivers/net/Kconfig      |    7 +
 drivers/net/Makefile     |    1 +
 drivers/net/cortina_ni.c | 1909 ++++++++++++++++++++++++++++++++++++++++++++++
 drivers/net/cortina_ni.h |  592 ++++++++++++++
 5 files changed, 2513 insertions(+)
 create mode 100644 drivers/net/cortina_ni.c
 create mode 100644 drivers/net/cortina_ni.h

Comments

Tom Rini June 3, 2020, 3:03 p.m. UTC | #1
On Wed, Jun 03, 2020 at 01:05:18AM -0700, Alex Nemirovsky wrote:
> From: Aaron Tseng <aaron.tseng at cortina-access.com>
> 
> Add Cortina Access Ethernet device driver for CAxxxx SoCs.
> This driver supports only the DM_ETH network model.
> 
> Signed-off-by: Aaron Tseng <aaron.tseng at cortina-access.com>
> Signed-off-by: Alex Nemirovsky <alex.nemirovsky at cortina-access.com>
> 
> CC: Joe Hershberger <joe.hershberger at ni.com>
> CC: Abbie Chang <abbie.chang at Cortina-Access.com>
> CC: Tom Rini <trini at konsulko.com>
> 
> ---
> 
> Changes in v4: None
> Changes in v3:
> - Changed commit comment to state that only DM model is supported
> - Removed blank line at end of C file
> 
> Changes in v2:
> - Remove legacy mode support
> - Add support for additional SoC variants
> - Remove unused variables
> 
>  MAINTAINERS              |    4 +
>  drivers/net/Kconfig      |    7 +
>  drivers/net/Makefile     |    1 +
>  drivers/net/cortina_ni.c | 1909 ++++++++++++++++++++++++++++++++++++++++++++++
>  drivers/net/cortina_ni.h |  592 ++++++++++++++
>  5 files changed, 2513 insertions(+)
>  create mode 100644 drivers/net/cortina_ni.c
>  create mode 100644 drivers/net/cortina_ni.h

So, is there a similar driver in upstream Linux?  This driver doesn't
quite fit right.  At an "easy" level, there's still the customer macro
around debug statements and not using pr_debug(), and contains a whole
lot of debug code.  There's also code for a saturn platform, is that
being upstreamed?  There's a ton of union usage which is also uncommon.
A small item is it looks like this has its own crc function, when we
have those available already.  There's also things like:
> +enum ca_status_t ca_mdio_read(CA_IN       unsigned int          addr,
> +			      CA_IN       unsigned int		offset,
> +			      CA_OUT      unsigned short	*data)
> +{

Where CA_IN / CA_OUT just don't fit.

Which brings me back to why I asked the first question, this feels a lot
like a driver for an RTOS or some other system was adapted to U-Boot,
rather than writing a new driver for U-Boot based on internal knowledge
of the part in question.  And I think that applies to a lot of the
drivers as seen in the NAND review as well.  Thanks!
Alex Nemirovsky June 3, 2020, 7:03 p.m. UTC | #2
Hi Tom,

> On Jun 3, 2020, at 8:03 AM, Tom Rini <trini at konsulko.com> wrote:
> 
> On Wed, Jun 03, 2020 at 01:05:18AM -0700, Alex Nemirovsky wrote:
>> From: Aaron Tseng <aaron.tseng at cortina-access.com>
>> 
>> Add Cortina Access Ethernet device driver for CAxxxx SoCs.
>> This driver supports only the DM_ETH network model.
>> 
>> Signed-off-by: Aaron Tseng <aaron.tseng at cortina-access.com>
>> Signed-off-by: Alex Nemirovsky <alex.nemirovsky at cortina-access.com>
>> 
>> CC: Joe Hershberger <joe.hershberger at ni.com>
>> CC: Abbie Chang <abbie.chang at Cortina-Access.com>
>> CC: Tom Rini <trini at konsulko.com>
>> 
>> ---
>> 
>> Changes in v4: None
>> Changes in v3:
>> - Changed commit comment to state that only DM model is supported
>> - Removed blank line at end of C file
>> 
>> Changes in v2:
>> - Remove legacy mode support
>> - Add support for additional SoC variants
>> - Remove unused variables
>> 
>> MAINTAINERS              |    4 +
>> drivers/net/Kconfig      |    7 +
>> drivers/net/Makefile     |    1 +
>> drivers/net/cortina_ni.c | 1909 ++++++++++++++++++++++++++++++++++++++++++++++
>> drivers/net/cortina_ni.h |  592 ++++++++++++++
>> 5 files changed, 2513 insertions(+)
>> create mode 100644 drivers/net/cortina_ni.c
>> create mode 100644 drivers/net/cortina_ni.h
> 
> So, is there a similar driver in upstream Linux?

We don?t have ANY Linux drivers upstream. 
Let me see if I can parse the comments below into action items for us.
Let me know if I got it right or missed something.

>  This driver doesn't
> quite fit right.  


> At an "easy" level, there's still the customer macro
> around debug statements and not using pr_debug(),

find and convert all debug statement to using U-Boot specific  pr_debug().

> and contains a whole
> lot of debug code.

remove development debug code which is no longer used.

>  There's also code for a saturn platform, is that
> being upstreamed?  
Yes, it will be upstreamed to u-boot.The approach is to first focus on getting
all our u-boot drivers upstream for the presidio SoC engineering board.
Most of the drivers are designed to work with other SoC platforms as those
SoC reuse HW logic for our peripherals.  

> There's a ton of union usage which is also uncommon.
 Is there an action item here?

> A small item is it looks like this has its own crc function, when we
> have those available already.

reuse existing CRC functions from u-boot instead of providing our own.

>  There's also things like:
>> +enum ca_status_t ca_mdio_read(CA_IN       unsigned int          addr,
>> +			      CA_IN       unsigned int		offset,
>> +			      CA_OUT      unsigned short	*data)
>> +{
> 
> Where CA_IN / CA_OUT just don't fit.

What specifically is the action item here?
> 
> Which brings me back to why I asked the first question, this feels a lot
> like a driver for an RTOS or some other system was adapted to U-Boot,
> rather than writing a new driver for U-Boot based on internal knowledge
> of the part in question.  And I think that applies to a lot of the
> drivers as seen in the NAND review as well.  Thanks!
If I understand you correctly, you would like for the drivers to more directly REUSE
native U-BOOT core functions already provided instead of providing
our own which essentially duplicate the same core functions. 

Did I misunderstand anything or do you have something more to add
as action items for us?
> 
> -- 
> Tom
Tom Rini June 3, 2020, 8:59 p.m. UTC | #3
On Wed, Jun 03, 2020 at 07:03:09PM +0000, Alex Nemirovsky wrote:
> Hi Tom,
> 
> > On Jun 3, 2020, at 8:03 AM, Tom Rini <trini at konsulko.com> wrote:
> > 
> > On Wed, Jun 03, 2020 at 01:05:18AM -0700, Alex Nemirovsky wrote:
> >> From: Aaron Tseng <aaron.tseng at cortina-access.com>
> >> 
> >> Add Cortina Access Ethernet device driver for CAxxxx SoCs.
> >> This driver supports only the DM_ETH network model.
> >> 
> >> Signed-off-by: Aaron Tseng <aaron.tseng at cortina-access.com>
> >> Signed-off-by: Alex Nemirovsky <alex.nemirovsky at cortina-access.com>
> >> 
> >> CC: Joe Hershberger <joe.hershberger at ni.com>
> >> CC: Abbie Chang <abbie.chang at Cortina-Access.com>
> >> CC: Tom Rini <trini at konsulko.com>
> >> 
> >> ---
> >> 
> >> Changes in v4: None
> >> Changes in v3:
> >> - Changed commit comment to state that only DM model is supported
> >> - Removed blank line at end of C file
> >> 
> >> Changes in v2:
> >> - Remove legacy mode support
> >> - Add support for additional SoC variants
> >> - Remove unused variables
> >> 
> >> MAINTAINERS              |    4 +
> >> drivers/net/Kconfig      |    7 +
> >> drivers/net/Makefile     |    1 +
> >> drivers/net/cortina_ni.c | 1909 ++++++++++++++++++++++++++++++++++++++++++++++
> >> drivers/net/cortina_ni.h |  592 ++++++++++++++
> >> 5 files changed, 2513 insertions(+)
> >> create mode 100644 drivers/net/cortina_ni.c
> >> create mode 100644 drivers/net/cortina_ni.h
> > 
> > So, is there a similar driver in upstream Linux?
> 
> We don?t have ANY Linux drivers upstream. 

Ah right.

> Let me see if I can parse the comments below into action items for us.
> Let me know if I got it right or missed something.
> 
> >  This driver doesn't
> > quite fit right.  
> 
> 
> > At an "easy" level, there's still the customer macro
> > around debug statements and not using pr_debug(),
> 
> find and convert all debug statement to using U-Boot specific  pr_debug().

pr_debug and other functions, and a handful of other print functions are
in <linux/printk.h> and will both mean you don't need to wrap debug
statements but also leverage the logging functionality correctly so that
we can also discard messages to save space but also still easily bring
in useful information when a developer has a problem they're tracing.

> > and contains a whole
> > lot of debug code.
> 
> remove development debug code which is no longer used.

Yes, and probably then do some clean-up of the functions once that's
removed, if that then makes sense.

> >  There's also code for a saturn platform, is that
> > being upstreamed?  
> Yes, it will be upstreamed to u-boot.The approach is to first focus on getting
> all our u-boot drivers upstream for the presidio SoC engineering board.
> Most of the drivers are designed to work with other SoC platforms as those
> SoC reuse HW logic for our peripherals.  

OK, so lets leave the stuff for other platforms out for now.  If there's
abstractions that are needed for them, you can note that's why there's a
hook when there is a hook.  But that's probably still better handled
when introducing / posting the platform that needs them for further
context.

> > There's a ton of union usage which is also uncommon.
>  Is there an action item here?

Well, why is it written the way it's written?  It doesn't follow the
style of other network drivers and so yes, it needs to be written in the
same manner as other drivers.  So for example:
+union NI_HV_PT_PORT_STATIC_CFG_t {
+	struct {
+		unsigned int int_cfg              :  4; /* bits 3:0 */
+		unsigned int phy_mode             :  1; /* bits 4:4 */
+		unsigned int rmii_clksrc          :  1; /* bits 5:5 */
+		unsigned int inv_clk_in           :  1; /* bits 6:6 */
+		unsigned int inv_clk_out          :  1; /* bits 7:7 */
+		unsigned int inv_rxclk_out        :  1; /* bits 8:8 */
+		unsigned int tx_use_gefifo        :  1; /* bits 9:9 */
+		unsigned int smii_tx_stat         :  1; /* bits 10:10 */
+		unsigned int crs_polarity         :  1; /* bits 11:11 */
+		unsigned int lpbk_mode            :  2; /* bits 13:12 */
+		unsigned int gmii_like_half_duplex_en :  1; /* bits 14:14 */
+		unsigned int sup_tx_to_rx_lpbk_data :  1; /* bits 15:15 */
+		unsigned int rsrvd1               :  8;
+		unsigned int mac_addr6            :  8; /* bits 31:24 */
+	} bf;
+	unsigned int     wrd;
+};

But in other drivers we just do:
struct pdma_rxd_info2 {
        u32 PLEN1 : 14;
        u32 LS1 : 1;
        u32 UN_USED : 1;
        u32 PLEN0 : 14;
        u32 LS0 : 1;
        u32 DDONE : 1;
};

So yes, all of the places where you have a union we normally do a struct.

> 
> > A small item is it looks like this has its own crc function, when we
> > have those available already.
> 
> reuse existing CRC functions from u-boot instead of providing our own.

And any other non-IP-specific functionality.

> >  There's also things like:
> >> +enum ca_status_t ca_mdio_read(CA_IN       unsigned int          addr,
> >> +			      CA_IN       unsigned int		offset,
> >> +			      CA_OUT      unsigned short	*data)
> >> +{
> > 
> > Where CA_IN / CA_OUT just don't fit.
> 
> What specifically is the action item here?

To clean up the driver so it reads like a regular U-Boot network driver.
I see later in the patch that CA_IN / CA_OUT are just empty.

> > Which brings me back to why I asked the first question, this feels a lot
> > like a driver for an RTOS or some other system was adapted to U-Boot,
> > rather than writing a new driver for U-Boot based on internal knowledge
> > of the part in question.  And I think that applies to a lot of the
> > drivers as seen in the NAND review as well.  Thanks!
> If I understand you correctly, you would like for the drivers to more directly REUSE
> native U-BOOT core functions already provided instead of providing
> our own which essentially duplicate the same core functions. 

Yes.

> Did I misunderstand anything or do you have something more to add
> as action items for us?

Please look at the existing drivers for other IP blocks.  It looks like
there's PHY stuff in the driver, which should instead be leveraging and
if needed updating something under drivers/net/phy, there's debug
commands that should just be dropped.  Make sure there's no un-used
defines, structs, etc. 

Thanks!
Alex Nemirovsky June 3, 2020, 9:10 p.m. UTC | #4
Hi Tom

> On Jun 3, 2020, at 1:59 PM, Tom Rini <trini at konsulko.com> wrote:
> 
> On Wed, Jun 03, 2020 at 07:03:09PM +0000, Alex Nemirovsky wrote:
>> Hi Tom,
>> 
>>> On Jun 3, 2020, at 8:03 AM, Tom Rini <trini at konsulko.com> wrote:
>>> 
>>> On Wed, Jun 03, 2020 at 01:05:18AM -0700, Alex Nemirovsky wrote:
>>>> From: Aaron Tseng <aaron.tseng at cortina-access.com>
>>>> 
>>>> Add Cortina Access Ethernet device driver for CAxxxx SoCs.
>>>> This driver supports only the DM_ETH network model.
>>>> 
>>>> Signed-off-by: Aaron Tseng <aaron.tseng at cortina-access.com>
>>>> Signed-off-by: Alex Nemirovsky <alex.nemirovsky at cortina-access.com>
>>>> 
>>>> CC: Joe Hershberger <joe.hershberger at ni.com>
>>>> CC: Abbie Chang <abbie.chang at Cortina-Access.com>
>>>> CC: Tom Rini <trini at konsulko.com>
>>>> 
>>>> ---
>>>> 
>>>> Changes in v4: None
>>>> Changes in v3:
>>>> - Changed commit comment to state that only DM model is supported
>>>> - Removed blank line at end of C file
>>>> 
>>>> Changes in v2:
>>>> - Remove legacy mode support
>>>> - Add support for additional SoC variants
>>>> - Remove unused variables
>>>> 
>>>> MAINTAINERS              |    4 +
>>>> drivers/net/Kconfig      |    7 +
>>>> drivers/net/Makefile     |    1 +
>>>> drivers/net/cortina_ni.c | 1909 ++++++++++++++++++++++++++++++++++++++++++++++
>>>> drivers/net/cortina_ni.h |  592 ++++++++++++++
>>>> 5 files changed, 2513 insertions(+)
>>>> create mode 100644 drivers/net/cortina_ni.c
>>>> create mode 100644 drivers/net/cortina_ni.h
>>> 
>>> So, is there a similar driver in upstream Linux?
>> 
>> We don?t have ANY Linux drivers upstream. 
> 
> Ah right.
> 
>> Let me see if I can parse the comments below into action items for us.
>> Let me know if I got it right or missed something.
>> 
>>> This driver doesn't
>>> quite fit right.  
>> 
>> 
>>> At an "easy" level, there's still the customer macro
>>> around debug statements and not using pr_debug(),
>> 
>> find and convert all debug statement to using U-Boot specific  pr_debug().
> 
> pr_debug and other functions, and a handful of other print functions are
> in <linux/printk.h> and will both mean you don't need to wrap debug
> statements but also leverage the logging functionality correctly so that
> we can also discard messages to save space but also still easily bring
> in useful information when a developer has a problem they're tracing.
> 
>>> and contains a whole
>>> lot of debug code.
>> 
>> remove development debug code which is no longer used.
> 
> Yes, and probably then do some clean-up of the functions once that's
> removed, if that then makes sense.
> 
>>> There's also code for a saturn platform, is that
>>> being upstreamed?  
>> Yes, it will be upstreamed to u-boot.The approach is to first focus on getting
>> all our u-boot drivers upstream for the presidio SoC engineering board.
>> Most of the drivers are designed to work with other SoC platforms as those
>> SoC reuse HW logic for our peripherals.  
> 
> OK, so lets leave the stuff for other platforms out for now.  If there's
> abstractions that are needed for them, you can note that's why there's a
> hook when there is a hook.  But that's probably still better handled
> when introducing / posting the platform that needs them for further
> context.
> 
>>> There's a ton of union usage which is also uncommon.
>> Is there an action item here?
> 
> Well, why is it written the way it's written?  

The SW team didn?t create these structures by hand. They are autogenerated 
my our HW design tools and adopted by the SW team.  
To be clear, I am not trying to defend the use of this auto generated structures, just explaining its origin.
Obviously it will take significant effort for us to recreate these structures in the desired form.

Therefore, it must be asked.  Is it required to be recreated in the structure form that you request and C code modified 
accordingly  to be accepted into the u-boot upstream tree going forward?


> It doesn't follow the
> style of other network drivers and so yes, it needs to be written in the
> same manner as other drivers.  So for example:
> +union NI_HV_PT_PORT_STATIC_CFG_t {
> +	struct {
> +		unsigned int int_cfg              :  4; /* bits 3:0 */
> +		unsigned int phy_mode             :  1; /* bits 4:4 */
> +		unsigned int rmii_clksrc          :  1; /* bits 5:5 */
> +		unsigned int inv_clk_in           :  1; /* bits 6:6 */
> +		unsigned int inv_clk_out          :  1; /* bits 7:7 */
> +		unsigned int inv_rxclk_out        :  1; /* bits 8:8 */
> +		unsigned int tx_use_gefifo        :  1; /* bits 9:9 */
> +		unsigned int smii_tx_stat         :  1; /* bits 10:10 */
> +		unsigned int crs_polarity         :  1; /* bits 11:11 */
> +		unsigned int lpbk_mode            :  2; /* bits 13:12 */
> +		unsigned int gmii_like_half_duplex_en :  1; /* bits 14:14 */
> +		unsigned int sup_tx_to_rx_lpbk_data :  1; /* bits 15:15 */
> +		unsigned int rsrvd1               :  8;
> +		unsigned int mac_addr6            :  8; /* bits 31:24 */
> +	} bf;
> +	unsigned int     wrd;
> +};
> 
> But in other drivers we just do:
> struct pdma_rxd_info2 {
>        u32 PLEN1 : 14;
>        u32 LS1 : 1;
>        u32 UN_USED : 1;
>        u32 PLEN0 : 14;
>        u32 LS0 : 1;
>        u32 DDONE : 1;
> };
> 
> So yes, all of the places where you have a union we normally do a struct.
> 
>> 
>>> A small item is it looks like this has its own crc function, when we
>>> have those available already.
>> 
>> reuse existing CRC functions from u-boot instead of providing our own.
> 
> And any other non-IP-specific functionality.
> 
>>> There's also things like:
>>>> +enum ca_status_t ca_mdio_read(CA_IN       unsigned int          addr,
>>>> +			      CA_IN       unsigned int		offset,
>>>> +			      CA_OUT      unsigned short	*data)
>>>> +{
>>> 
>>> Where CA_IN / CA_OUT just don't fit.
>> 
>> What specifically is the action item here?
> 
> To clean up the driver so it reads like a regular U-Boot network driver.
> I see later in the patch that CA_IN / CA_OUT are just empty.
> 
>>> Which brings me back to why I asked the first question, this feels a lot
>>> like a driver for an RTOS or some other system was adapted to U-Boot,
>>> rather than writing a new driver for U-Boot based on internal knowledge
>>> of the part in question.  And I think that applies to a lot of the
>>> drivers as seen in the NAND review as well.  Thanks!
>> If I understand you correctly, you would like for the drivers to more directly REUSE
>> native U-BOOT core functions already provided instead of providing
>> our own which essentially duplicate the same core functions. 
> 
> Yes.
> 
>> Did I misunderstand anything or do you have something more to add
>> as action items for us?
> 
> Please look at the existing drivers for other IP blocks.  It looks like
> there's PHY stuff in the driver, which should instead be leveraging and
> if needed updating something under drivers/net/phy, there's debug
> commands that should just be dropped.  Make sure there's no un-used
> defines, structs, etc. 
> 
> Thanks!
> 
> -- 
> Tom
Tom Rini June 3, 2020, 10:03 p.m. UTC | #5
On Wed, Jun 03, 2020 at 09:10:46PM +0000, Alex Nemirovsky wrote:
> Hi Tom
> 
> > On Jun 3, 2020, at 1:59 PM, Tom Rini <trini at konsulko.com> wrote:
> > 
> > On Wed, Jun 03, 2020 at 07:03:09PM +0000, Alex Nemirovsky wrote:
> >> Hi Tom,
> >> 
> >>> On Jun 3, 2020, at 8:03 AM, Tom Rini <trini at konsulko.com> wrote:
> >>> 
> >>> On Wed, Jun 03, 2020 at 01:05:18AM -0700, Alex Nemirovsky wrote:
> >>>> From: Aaron Tseng <aaron.tseng at cortina-access.com>
> >>>> 
> >>>> Add Cortina Access Ethernet device driver for CAxxxx SoCs.
> >>>> This driver supports only the DM_ETH network model.
> >>>> 
> >>>> Signed-off-by: Aaron Tseng <aaron.tseng at cortina-access.com>
> >>>> Signed-off-by: Alex Nemirovsky <alex.nemirovsky at cortina-access.com>
> >>>> 
> >>>> CC: Joe Hershberger <joe.hershberger at ni.com>
> >>>> CC: Abbie Chang <abbie.chang at Cortina-Access.com>
> >>>> CC: Tom Rini <trini at konsulko.com>
> >>>> 
> >>>> ---
> >>>> 
> >>>> Changes in v4: None
> >>>> Changes in v3:
> >>>> - Changed commit comment to state that only DM model is supported
> >>>> - Removed blank line at end of C file
> >>>> 
> >>>> Changes in v2:
> >>>> - Remove legacy mode support
> >>>> - Add support for additional SoC variants
> >>>> - Remove unused variables
> >>>> 
> >>>> MAINTAINERS              |    4 +
> >>>> drivers/net/Kconfig      |    7 +
> >>>> drivers/net/Makefile     |    1 +
> >>>> drivers/net/cortina_ni.c | 1909 ++++++++++++++++++++++++++++++++++++++++++++++
> >>>> drivers/net/cortina_ni.h |  592 ++++++++++++++
> >>>> 5 files changed, 2513 insertions(+)
> >>>> create mode 100644 drivers/net/cortina_ni.c
> >>>> create mode 100644 drivers/net/cortina_ni.h
> >>> 
> >>> So, is there a similar driver in upstream Linux?
> >> 
> >> We don?t have ANY Linux drivers upstream. 
> > 
> > Ah right.
> > 
> >> Let me see if I can parse the comments below into action items for us.
> >> Let me know if I got it right or missed something.
> >> 
> >>> This driver doesn't
> >>> quite fit right.  
> >> 
> >> 
> >>> At an "easy" level, there's still the customer macro
> >>> around debug statements and not using pr_debug(),
> >> 
> >> find and convert all debug statement to using U-Boot specific  pr_debug().
> > 
> > pr_debug and other functions, and a handful of other print functions are
> > in <linux/printk.h> and will both mean you don't need to wrap debug
> > statements but also leverage the logging functionality correctly so that
> > we can also discard messages to save space but also still easily bring
> > in useful information when a developer has a problem they're tracing.
> > 
> >>> and contains a whole
> >>> lot of debug code.
> >> 
> >> remove development debug code which is no longer used.
> > 
> > Yes, and probably then do some clean-up of the functions once that's
> > removed, if that then makes sense.
> > 
> >>> There's also code for a saturn platform, is that
> >>> being upstreamed?  
> >> Yes, it will be upstreamed to u-boot.The approach is to first focus on getting
> >> all our u-boot drivers upstream for the presidio SoC engineering board.
> >> Most of the drivers are designed to work with other SoC platforms as those
> >> SoC reuse HW logic for our peripherals.  
> > 
> > OK, so lets leave the stuff for other platforms out for now.  If there's
> > abstractions that are needed for them, you can note that's why there's a
> > hook when there is a hook.  But that's probably still better handled
> > when introducing / posting the platform that needs them for further
> > context.
> > 
> >>> There's a ton of union usage which is also uncommon.
> >> Is there an action item here?
> > 
> > Well, why is it written the way it's written?  
> 
> The SW team didn?t create these structures by hand. They are autogenerated 
> my our HW design tools and adopted by the SW team.  
> To be clear, I am not trying to defend the use of this auto generated structures, just explaining its origin.
> Obviously it will take significant effort for us to recreate these structures in the desired form.
> 
> Therefore, it must be asked.  Is it required to be recreated in the structure form that you request and C code modified 
> accordingly  to be accepted into the u-boot upstream tree going forward?

Yes, and work with the HW design tool folks to generate output that's
more acceptable to various open source projects.  You aren't the first
team to have to push for this, I can tell you from experience.

But since it's generated output, it can be transformed too via sed, etc.
Thanks!
Alex Nemirovsky June 3, 2020, 10:06 p.m. UTC | #6
Hi Tom,

Thanks for your feedback.

> On Jun 3, 2020, at 3:03 PM, Tom Rini <trini at konsulko.com> wrote:
> 
> On Wed, Jun 03, 2020 at 09:10:46PM +0000, Alex Nemirovsky wrote:
>> Hi Tom
>> 
>>> On Jun 3, 2020, at 1:59 PM, Tom Rini <trini at konsulko.com> wrote:
>>> 
>>> On Wed, Jun 03, 2020 at 07:03:09PM +0000, Alex Nemirovsky wrote:
>>>> Hi Tom,
>>>> 
>>>>> On Jun 3, 2020, at 8:03 AM, Tom Rini <trini at konsulko.com> wrote:
>>>>> 
>>>>> On Wed, Jun 03, 2020 at 01:05:18AM -0700, Alex Nemirovsky wrote:
>>>>>> From: Aaron Tseng <aaron.tseng at cortina-access.com>
>>>>>> 
>>>>>> Add Cortina Access Ethernet device driver for CAxxxx SoCs.
>>>>>> This driver supports only the DM_ETH network model.
>>>>>> 
>>>>>> Signed-off-by: Aaron Tseng <aaron.tseng at cortina-access.com>
>>>>>> Signed-off-by: Alex Nemirovsky <alex.nemirovsky at cortina-access.com>
>>>>>> 
>>>>>> CC: Joe Hershberger <joe.hershberger at ni.com>
>>>>>> CC: Abbie Chang <abbie.chang at Cortina-Access.com>
>>>>>> CC: Tom Rini <trini at konsulko.com>
>>>>>> 
>>>>>> ---
>>>>>> 
>>>>>> Changes in v4: None
>>>>>> Changes in v3:
>>>>>> - Changed commit comment to state that only DM model is supported
>>>>>> - Removed blank line at end of C file
>>>>>> 
>>>>>> Changes in v2:
>>>>>> - Remove legacy mode support
>>>>>> - Add support for additional SoC variants
>>>>>> - Remove unused variables
>>>>>> 
>>>>>> MAINTAINERS              |    4 +
>>>>>> drivers/net/Kconfig      |    7 +
>>>>>> drivers/net/Makefile     |    1 +
>>>>>> drivers/net/cortina_ni.c | 1909 ++++++++++++++++++++++++++++++++++++++++++++++
>>>>>> drivers/net/cortina_ni.h |  592 ++++++++++++++
>>>>>> 5 files changed, 2513 insertions(+)
>>>>>> create mode 100644 drivers/net/cortina_ni.c
>>>>>> create mode 100644 drivers/net/cortina_ni.h
>>>>> 
>>>>> So, is there a similar driver in upstream Linux?
>>>> 
>>>> We don?t have ANY Linux drivers upstream. 
>>> 
>>> Ah right.
>>> 
>>>> Let me see if I can parse the comments below into action items for us.
>>>> Let me know if I got it right or missed something.
>>>> 
>>>>> This driver doesn't
>>>>> quite fit right.  
>>>> 
>>>> 
>>>>> At an "easy" level, there's still the customer macro
>>>>> around debug statements and not using pr_debug(),
>>>> 
>>>> find and convert all debug statement to using U-Boot specific  pr_debug().
>>> 
>>> pr_debug and other functions, and a handful of other print functions are
>>> in <linux/printk.h> and will both mean you don't need to wrap debug
>>> statements but also leverage the logging functionality correctly so that
>>> we can also discard messages to save space but also still easily bring
>>> in useful information when a developer has a problem they're tracing.
>>> 
>>>>> and contains a whole
>>>>> lot of debug code.
>>>> 
>>>> remove development debug code which is no longer used.
>>> 
>>> Yes, and probably then do some clean-up of the functions once that's
>>> removed, if that then makes sense.
>>> 
>>>>> There's also code for a saturn platform, is that
>>>>> being upstreamed?  
>>>> Yes, it will be upstreamed to u-boot.The approach is to first focus on getting
>>>> all our u-boot drivers upstream for the presidio SoC engineering board.
>>>> Most of the drivers are designed to work with other SoC platforms as those
>>>> SoC reuse HW logic for our peripherals.  
>>> 
>>> OK, so lets leave the stuff for other platforms out for now.  If there's
>>> abstractions that are needed for them, you can note that's why there's a
>>> hook when there is a hook.  But that's probably still better handled
>>> when introducing / posting the platform that needs them for further
>>> context.
>>> 
>>>>> There's a ton of union usage which is also uncommon.
>>>> Is there an action item here?
>>> 
>>> Well, why is it written the way it's written?  
>> 
>> The SW team didn?t create these structures by hand. They are autogenerated 
>> my our HW design tools and adopted by the SW team.  
>> To be clear, I am not trying to defend the use of this auto generated structures, just explaining its origin.
>> Obviously it will take significant effort for us to recreate these structures in the desired form.
>> 
>> Therefore, it must be asked.  Is it required to be recreated in the structure form that you request and C code modified 
>> accordingly  to be accepted into the u-boot upstream tree going forward?
> 
> Yes, and work with the HW design tool folks to generate output that's
> more acceptable to various open source projects.  You aren't the first
> team to have to push for this, I can tell you from experience.
> 
> But since it's generated output, it can be transformed too via sed, etc.
> Thanks!
> 
> -- 
> Tom
Ramon Fried June 4, 2020, 4:28 p.m. UTC | #7
On Wed, Jun 3, 2020 at 11:05 AM Alex Nemirovsky
<alex.nemirovsky at cortina-access.com> wrote:
>
> From: Aaron Tseng <aaron.tseng at cortina-access.com>
>
> Add Cortina Access Ethernet device driver for CAxxxx SoCs.
> This driver supports only the DM_ETH network model.
>
> Signed-off-by: Aaron Tseng <aaron.tseng at cortina-access.com>
> Signed-off-by: Alex Nemirovsky <alex.nemirovsky at cortina-access.com>
>
> CC: Joe Hershberger <joe.hershberger at ni.com>
> CC: Abbie Chang <abbie.chang at Cortina-Access.com>
> CC: Tom Rini <trini at konsulko.com>
>
> ---
>
> Changes in v4: None
> Changes in v3:
> - Changed commit comment to state that only DM model is supported
> - Removed blank line at end of C file
>
> Changes in v2:
> - Remove legacy mode support
> - Add support for additional SoC variants
> - Remove unused variables
>
>  MAINTAINERS              |    4 +
>  drivers/net/Kconfig      |    7 +
>  drivers/net/Makefile     |    1 +
>  drivers/net/cortina_ni.c | 1909 ++++++++++++++++++++++++++++++++++++++++++++++
>  drivers/net/cortina_ni.h |  592 ++++++++++++++
>  5 files changed, 2513 insertions(+)
>  create mode 100644 drivers/net/cortina_ni.c
>  create mode 100644 drivers/net/cortina_ni.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 8add9d4..1b166d2 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -181,6 +181,8 @@ F:  drivers/gpio/cortina_gpio.c
>  F:     drivers/watchdog/cortina_wdt.c
>  F:     drivers/serial/serial_cortina.c
>  F:     drivers/mmc/ca_dw_mmc.c
> +F:     drivers/net/cortina_ni.c
> +F:     drivers/net/cortina_ni.h
>
>  ARM/CZ.NIC TURRIS MOX SUPPORT
>  M:     Marek Behun <marek.behun at nic.cz>
> @@ -732,6 +734,8 @@ F:  drivers/gpio/cortina_gpio.c
>  F:     drivers/watchdog/cortina_wdt.c
>  F:     drivers/serial/serial_cortina.c
>  F:     drivers/mmc/ca_dw_mmc.c
> +F:     drivers/net/cortina_ni.c
> +F:     drivers/net/cortina_ni.h
>
>  MIPS MSCC
>  M:     Gregory CLEMENT <gregory.clement at bootlin.com>
> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
> index f7855c9..45e0480 100644
> --- a/drivers/net/Kconfig
> +++ b/drivers/net/Kconfig
> @@ -149,6 +149,13 @@ config BCMGENET
>         help
>           This driver supports the BCMGENET Ethernet MAC.
>
> +config CORTINA_NI_ENET
> +       bool "Cortina-Access Ethernet driver"
> +       depends on DM_ETH && CORTINA_PLATFORM
> +       help
> +         The driver supports the Cortina-Access Ethernet MAC for
> +         all supported CAxxxx SoCs
> +
>  config DWC_ETH_QOS
>         bool "Synopsys DWC Ethernet QOS device support"
>         depends on DM_ETH
> diff --git a/drivers/net/Makefile b/drivers/net/Makefile
> index 383ed1c..1d6ec4f 100644
> --- a/drivers/net/Makefile
> +++ b/drivers/net/Makefile
> @@ -14,6 +14,7 @@ obj-$(CONFIG_DRIVER_AX88180) += ax88180.o
>  obj-$(CONFIG_BCM_SF2_ETH) += bcm-sf2-eth.o
>  obj-$(CONFIG_BCM_SF2_ETH_GMAC) += bcm-sf2-eth-gmac.o
>  obj-$(CONFIG_CALXEDA_XGMAC) += calxedaxgmac.o
> +obj-$(CONFIG_CORTINA_NI_ENET) += cortina_ni.o
>  obj-$(CONFIG_CS8900) += cs8900.o
>  obj-$(CONFIG_TULIP) += dc2114x.o
>  obj-$(CONFIG_ETH_DESIGNWARE) += designware.o
> diff --git a/drivers/net/cortina_ni.c b/drivers/net/cortina_ni.c
> new file mode 100644
> index 0000000..d5bbf3e
> --- /dev/null
> +++ b/drivers/net/cortina_ni.c
> @@ -0,0 +1,1909 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +
> +/*
> + * Copyright (C) 2020 Cortina Access Inc.
> + * Author: Aaron Tseng <aaron.tseng at cortina-access.com>
> + *
> + * Ethernet MAC Driver for all supported CAxxxx SoCs
> + */
> +
> +#include <common.h>
> +#include <command.h>
> +#include <malloc.h>
> +#include <net.h>
> +#include <miiphy.h>
> +#include <env.h>
> +#include <linux/delay.h>
> +
> +#include "cortina_ni.h"
> +
> +static u32 reg_value;
> +
> +/* port 0-3 are individual port connect to PHY directly */
> +/* port 4-7 are LAN ports connected to QSGMII PHY */
> +int active_port = NI_PORT_5;  /* Physical port 5 */
> +u32 ge_port_phy_addr;  /* PHY address connected to active port */
> +int auto_scan_active_port;
> +
> +#define HEADER_A_SIZE  8
> +
> +/*define CORTINA_NI_DBG if individual rx,tx,init needs to be called */
> +#if CORTINA_NI_DBG
> +static struct udevice  *dbg_dev;
> +#endif
> +static struct udevice   *curr_dev;
> +
> +#if defined(CONFIG_TARGET_SATURN_ASIC)
> +#define CA_REG_READ(off)        readl((u64)KSEG1_ATU_XLAT(off))
> +#define CA_REG_WRITE(data, off) writel(data, (u64)KSEG1_ATU_XLAT(off))
> +#else
> +#define CA_REG_READ(off)        readl((u64)off)
> +#define CA_REG_WRITE(data, off) writel(data, (u64)off)
> +#endif
> +
> +int cortina_ni_recv(struct udevice *netdev);
> +static int ca_ni_ofdata_to_platdata(struct udevice *dev);
> +
> +static u32 *RDWRPTR_ADVANCE_ONE(u32 *x, unsigned long base, unsigned long max)
Please don't use upper case function names.
> +{
> +       if (x + 1 >= (u32 *)max)
> +               return (u32 *)base;
> +       else
> +               return (x + 1);
> +}
> +
> +static void ni_setup_mac_addr(void)
> +{
> +       unsigned char mac[6];
> +
> +       union NI_HV_GLB_MAC_ADDR_CFG0_t         mac_addr_cfg0;
> +       union NI_HV_GLB_MAC_ADDR_CFG1_t         mac_addr_cfg1;
> +       union NI_HV_PT_PORT_STATIC_CFG_t        port_static_cfg;
> +       union NI_HV_XRAM_CPUXRAM_CFG_t          cpuxram_cfg;
> +       struct cortina_ni_priv *priv = dev_get_priv(curr_dev);
> +
> +       /* parsing ethaddr and set to NI registers. */
> +       if (eth_env_get_enetaddr("ethaddr", mac)) {
> +               /* The complete MAC address consists of
> +                * {MAC_ADDR0_mac_addr0[0-3], MAC_ADDR1_mac_addr1[4],
> +                * PT_PORT_STATIC_CFG_mac_addr6[5]}.
> +                */
> +               mac_addr_cfg0.bf.mac_addr0 = (mac[0] << 24) +
> +                                            (mac[1] << 16) +
> +                                            (mac[2] << 8) +
> +                                            mac[3];
> +               CA_REG_WRITE(mac_addr_cfg0.wrd, priv->ni_hv_base_addr
> +                               + NI_HV_GLB_MAC_ADDR_CFG0_OFFSET);
> +
> +               mac_addr_cfg1.wrd = 0;
> +               mac_addr_cfg1.bf.mac_addr1 = mac[4];
> +               CA_REG_WRITE(mac_addr_cfg1.wrd, (priv->ni_hv_base_addr +
> +                               NI_HV_GLB_MAC_ADDR_CFG1_OFFSET));
> +
> +               port_static_cfg.wrd = CA_REG_READ((priv->ni_hv_base_addr +
> +                                       NI_HV_PT_PORT_STATIC_CFG_OFFSET +
> +                                       APB0_NI_HV_PT_STRIDE * active_port));
> +
> +               port_static_cfg.bf.mac_addr6 = mac[5];
> +               CA_REG_WRITE(port_static_cfg.wrd, (priv->ni_hv_base_addr +
> +                                       NI_HV_PT_PORT_STATIC_CFG_OFFSET +
> +                                       APB0_NI_HV_PT_STRIDE * active_port));
> +
> +               /* received only Broadcast and Address matched packets */
> +               cpuxram_cfg.wrd = CA_REG_READ((priv->ni_hv_base_addr +
> +                               NI_HV_XRAM_CPUXRAM_CFG_OFFSET));
> +               cpuxram_cfg.bf.xram_mgmt_promisc_mode = 0;
> +               cpuxram_cfg.bf.rx_0_cpu_pkt_dis = 0;
> +               cpuxram_cfg.bf.tx_0_cpu_pkt_dis = 0;
> +               CA_REG_WRITE(cpuxram_cfg.wrd, (priv->ni_hv_base_addr +
> +                                       NI_HV_XRAM_CPUXRAM_CFG_OFFSET));
> +       } else {
> +               /* received all packets(promiscuous mode) */
> +               cpuxram_cfg.wrd = CA_REG_READ((priv->ni_hv_base_addr +
> +                                       NI_HV_XRAM_CPUXRAM_CFG_OFFSET));
> +               cpuxram_cfg.bf.xram_mgmt_promisc_mode = 3;
> +               cpuxram_cfg.bf.rx_0_cpu_pkt_dis = 0;
> +               cpuxram_cfg.bf.tx_0_cpu_pkt_dis = 0;
> +               CA_REG_WRITE(cpuxram_cfg.wrd, (priv->ni_hv_base_addr +
> +                                       NI_HV_XRAM_CPUXRAM_CFG_OFFSET));
> +       }
> +}
> +
> +static void ni_enable_tx_rx(void)
> +{
> +       union NI_HV_PT_RXMAC_CFG_t rxmac_cfg;
> +       union NI_HV_PT_TXMAC_CFG_t txmac_cfg;
> +
> +       struct cortina_ni_priv *priv = dev_get_priv(curr_dev);
> +
> +       /* Enable TX and RX functions */
> +       rxmac_cfg.wrd = CA_REG_READ((priv->ni_hv_base_addr +
> +                               NI_HV_PT_RXMAC_CFG_OFFSET +
> +                               APB0_NI_HV_PT_STRIDE * active_port));
> +       rxmac_cfg.bf.rx_en = 1;
> +       CA_REG_WRITE(rxmac_cfg.wrd, (priv->ni_hv_base_addr +
> +                               NI_HV_PT_RXMAC_CFG_OFFSET +
> +                               APB0_NI_HV_PT_STRIDE * active_port));
> +
> +       txmac_cfg.wrd = CA_REG_READ((priv->ni_hv_base_addr +
> +                               NI_HV_PT_TXMAC_CFG_OFFSET +
> +                               APB0_NI_HV_PT_STRIDE * active_port));
> +       txmac_cfg.bf.tx_en = 1;
> +       CA_REG_WRITE(txmac_cfg.wrd, (priv->ni_hv_base_addr +
> +                               NI_HV_PT_TXMAC_CFG_OFFSET +
> +                               APB0_NI_HV_PT_STRIDE * active_port));
> +}
> +
> +void cortina_ni_reset(void)
> +{
> +       int i;
> +       union NI_HV_GLB_INIT_DONE_t             init_done;
> +       union NI_HV_GLB_INTF_RST_CONFIG_t       intf_rst_config;
> +       union NI_HV_GLB_STATIC_CFG_t            static_cfg;
> +       union GLOBAL_BLOCK_RESET_t              glb_blk_reset;
> +
> +       struct cortina_ni_priv *priv = dev_get_priv(curr_dev);
> +
> +       /* NI global resets */
> +       glb_blk_reset.wrd = CA_REG_READ((priv->glb_base_addr +
> +                               GLOBAL_BLOCK_RESET_OFFSET));
> +       glb_blk_reset.bf.reset_ni = 1;
> +       CA_REG_WRITE(glb_blk_reset.wrd, (priv->glb_base_addr +
> +                               GLOBAL_BLOCK_RESET_OFFSET));
> +       /* Remove resets */
> +       glb_blk_reset.bf.reset_ni = 0;
> +       CA_REG_WRITE(glb_blk_reset.wrd, (priv->glb_base_addr +
> +                               GLOBAL_BLOCK_RESET_OFFSET));
> +
> +       /* check the ready bit of NI module */
> +       for (i = 0; i < NI_READ_POLL_COUNT; i++) {
> +               init_done.wrd = CA_REG_READ((priv->ni_hv_base_addr +
> +                                       NI_HV_GLB_INIT_DONE_OFFSET));
> +               if (init_done.bf.ni_init_done)
> +                       break;
> +       }
> +       if (i == NI_READ_POLL_COUNT) {
> +               printf("%s: NI init done not ready, init_done.wrd=0x%x!!!\n",
> +                      __func__, init_done.wrd);
> +       }
> +
> +       intf_rst_config.wrd = CA_REG_READ(priv->ni_hv_base_addr +
> +                                         NI_HV_GLB_INTF_RST_CONFIG_OFFSET);
> +       switch (active_port) {
> +#if defined(CONFIG_TARGET_PRESIDIO_ASIC) || defined(CONFIG_TARGET_VENUS)
> +       case NI_PORT_0:
> +               intf_rst_config.bf.intf_rst_p0 = 0;
> +               intf_rst_config.bf.mac_rx_rst_p0 = 0;
> +               intf_rst_config.bf.mac_tx_rst_p0 = 0;
> +               break;
> +       case NI_PORT_1:
> +               intf_rst_config.bf.intf_rst_p1 = 0;
> +               intf_rst_config.bf.mac_rx_rst_p1 = 0;
> +               intf_rst_config.bf.mac_tx_rst_p1 = 0;
> +               break;
> +       case NI_PORT_2:
> +               intf_rst_config.bf.intf_rst_p2 = 0;
> +               intf_rst_config.bf.mac_rx_rst_p2 = 0;
> +               intf_rst_config.bf.mac_tx_rst_p2 = 0;
> +               break;
> +#endif
> +       case NI_PORT_3:
> +               intf_rst_config.bf.intf_rst_p3 = 0;
> +               intf_rst_config.bf.mac_tx_rst_p3 = 0;
> +               intf_rst_config.bf.mac_rx_rst_p3 = 0;
> +               break;
> +       case NI_PORT_4:
> +               intf_rst_config.bf.intf_rst_p4 = 0;
> +               intf_rst_config.bf.mac_tx_rst_p4 = 0;
> +               intf_rst_config.bf.mac_rx_rst_p4 = 0;
> +               break;
> +       }
> +
> +       CA_REG_WRITE(intf_rst_config.wrd, (priv->ni_hv_base_addr +
> +                               NI_HV_GLB_INTF_RST_CONFIG_OFFSET));
> +
> +       /* Only one GMAC can connect to CPU */
> +       static_cfg.wrd = CA_REG_READ((priv->ni_hv_base_addr +
> +                               NI_HV_GLB_STATIC_CFG_OFFSET));
> +
> +       static_cfg.bf.port_to_cpu = active_port;
> +       static_cfg.bf.txmib_mode = 1;
> +       static_cfg.bf.rxmib_mode = 1;
> +
> +       CA_REG_WRITE(static_cfg.wrd, (priv->ni_hv_base_addr +
> +                               NI_HV_GLB_STATIC_CFG_OFFSET));
> +
> +       //printf("%s: Connect port %d to CPU\n", __func__, active_port);
> +
> +#if defined(CONFIG_TARGET_SATURN_ASIC)
> +       /* set IO driver control */
> +       io_driver_control.wrd = CA_REG_READ((priv->glb_base_addr +
> +                               GLOBAL_IO_DRIVE_CONTROL_OFFSET));
> +       io_driver_control.bf.gmac_dp = 1;
> +       io_driver_control.bf.gmac_dn = 1;
> +       io_driver_control.bf.gmac_ds = 0;
> +       io_driver_control.bf.gmac_mode = 2;
> +       CA_REG_WRITE(io_driver_control.wrd, (priv->glb_base_addr +
> +                               GLOBAL_IO_DRIVE_CONTROL_OFFSET));
> +
> +       /* initialize internal GPHY */
> +       gige_phy.wrd = 0;
> +       gige_phy.bf.gphy_phyrst_cen_b = 1;
> +       CA_REG_WRITE(gige_phy.wrd, (priv->glb_base_addr +
> +                               GLOBAL_GIGE_PHY_OFFSET));
> +       mdelay(50);
> +
> +       CA_REG_WRITE(0xa46, 0xd000b0fc);
> +       mdelay(50);
> +       CA_REG_WRITE(0x1, 0xd000b0d0);
> +       mdelay(100);
> +#endif
> +}
> +
> +#define NI_ETH_SPEED_100 0xFFFFFFFE
> +#define NI_ETH_DUPLEX_FULL 0xFFFFFFD
> +#define PHY_MODE_MFE_MAC       BIT(12)
> +
> +#define NI_RX_ENB              BIT(2)
> +#define NI_TX_ENB              BIT(3)
> +#define FLOW_CNTL_RX_DSBL      BIT(8)
> +#define FLOW_CNTL_TX_DSBL      BIT(12)
> +
> +static enum ca_status_t ca_mdio_write_rgmii(unsigned int addr,
> +                                           unsigned int offset,
> +                                           unsigned short data)
> +{
> +       union PER_MDIO_ADDR_t  mdio_addr;
> +       union PER_MDIO_CTRL_t  mdio_ctrl;
> +       /* up to 10000 cycles*/
> +       unsigned int      loop_wait = __MDIO_ACCESS_TIMEOUT;
> +
> +       struct cortina_ni_priv *priv = dev_get_priv(curr_dev);
> +
> +       mdio_addr.wrd            = 0;
> +       mdio_addr.bf.mdio_addr   = addr;
> +       mdio_addr.bf.mdio_offset = offset;
> +       mdio_addr.bf.mdio_rd_wr  = __MDIO_WR_FLAG;
> +       CA_REG_WRITE(mdio_addr.wrd,
> +                    priv->per_mdio_base_addr + PER_MDIO_ADDR_OFFSET);
> +       CA_REG_WRITE(data,
> +                    priv->per_mdio_base_addr + PER_MDIO_WRDATA_OFFSET);
> +
> +#if CORTINA_NI_DBG
> +       printf("%s: mdio_addr.wrd=0x%x\n", __func__, mdio_addr.wrd);
> +#endif
> +
> +       mdio_ctrl.wrd          = 0;
> +       mdio_ctrl.bf.mdiostart = 1;
> +       CA_REG_WRITE(mdio_ctrl.wrd,
> +                    priv->per_mdio_base_addr + PER_MDIO_CTRL_OFFSET);
> +
> +#if CORTINA_NI_DBG
> +       printf("%s: phy_addr=%d, offset=%d, data=0x%x\n",
> +              __func__, addr, offset, data);
> +#endif
> +
> +       do {
> +               mdio_ctrl.wrd = CA_REG_READ((priv->per_mdio_base_addr +
> +                                       PER_MDIO_CTRL_OFFSET));
> +               if (mdio_ctrl.bf.mdiodone) {
> +                       CA_REG_WRITE(mdio_ctrl.wrd, (priv->per_mdio_base_addr +
> +                                               PER_MDIO_CTRL_OFFSET));
> +                       return CA_E_OK;
> +               }
> +       } while (--loop_wait);
> +
> +       printf("%s: PHY rite timeout!!!\n", __func__);
typo
> +       return CA_E_TIMEOUT;
> +}
> +
> +enum ca_status_t ca_mdio_write(CA_IN       unsigned int             addr,
> +                              CA_IN       unsigned int             offset,
> +                              CA_IN       unsigned short             data)
> +{
> +       /* support range: 1~31*/
> +       if (addr < CA_MDIO_ADDR_MIN || addr > CA_MDIO_ADDR_MAX)
> +               return CA_E_PARAM;
> +#if defined(CONFIG_TARGET_PRESIDIO_ASIC) || \
> +       defined(CONFIG_TARGET_SATURN_ASIC) || defined(CONFIG_TARGET_VENUS)
> +       union NI_MDIO_OPER_T mdio_oper;
> +
> +       /* the phy addr 5 is connect to RGMII */
> +       if (addr >= 5)
> +               return ca_mdio_write_rgmii(addr, offset, data);
> +
> +       mdio_oper.wrd = 0;
> +       mdio_oper.bf.reg_off = offset;
> +       mdio_oper.bf.phy_addr = addr;
> +       mdio_oper.bf.reg_base = CA_NI_MDIO_REG_BASE;
> +       CA_REG_WRITE(data, mdio_oper.wrd);
> +
> +#if CORTINA_NI_DBG
> +       printf("%s: mdio_oper.wrd=0x%x, data=0x%x\n",
> +              __func__, mdio_oper.wrd, data);
> +#endif
> +       return CA_E_OK;
> +#else
> +       return ca_mdio_write_rgmii(addr, offset, data);
> +#endif
> +}
> +
> +static enum ca_status_t ca_mdio_read_rgmii(unsigned int addr,
> +                                          unsigned int offset,
> +                                          unsigned short *data)
> +{
> +       union PER_MDIO_ADDR_t  mdio_addr;
> +       union PER_MDIO_CTRL_t  mdio_ctrl;
> +       union PER_MDIO_RDDATA_t  read_data;
> +       unsigned int loop_wait = __MDIO_ACCESS_TIMEOUT;
> +
> +       struct cortina_ni_priv *priv = dev_get_priv(curr_dev);
> +
> +       mdio_addr.wrd            = 0;
> +       mdio_addr.bf.mdio_addr   = addr;
> +       mdio_addr.bf.mdio_offset = offset;
> +       mdio_addr.bf.mdio_rd_wr  = __MDIO_RD_FLAG;
> +       CA_REG_WRITE(mdio_addr.wrd,
> +                    priv->per_mdio_base_addr + PER_MDIO_ADDR_OFFSET);
> +
> +       mdio_ctrl.wrd          = 0;
> +       mdio_ctrl.bf.mdiostart = 1;
> +       CA_REG_WRITE(mdio_ctrl.wrd,
> +                    priv->per_mdio_base_addr + PER_MDIO_CTRL_OFFSET);
> +
> +       do {
> +               mdio_ctrl.wrd = CA_REG_READ((priv->per_mdio_base_addr +
> +                                       PER_MDIO_CTRL_OFFSET));
> +               if (mdio_ctrl.bf.mdiodone) {
> +                       CA_REG_WRITE(mdio_ctrl.wrd, (priv->per_mdio_base_addr +
> +                                               PER_MDIO_CTRL_OFFSET));
> +                       read_data.wrd = CA_REG_READ((priv->per_mdio_base_addr +
> +                                               PER_MDIO_RDDATA_OFFSET));
> +                       *data = read_data.bf.mdio_rddata;
> +                       return CA_E_OK;
> +               }
> +       } while (--loop_wait);
> +
> +       printf("%s: CA_E_TIMEOUT!!\n", __func__);
> +       return CA_E_TIMEOUT;
> +}
> +
> +enum ca_status_t ca_mdio_read(CA_IN       unsigned int          addr,
> +                             CA_IN       unsigned int          offset,
> +                             CA_OUT      unsigned short        *data)
> +{
> +       if (!data)
> +               return CA_E_PARAM;
> +
> +       /* support range: 1~31*/
> +       if (addr < CA_MDIO_ADDR_MIN || addr > CA_MDIO_ADDR_MAX)
> +               return CA_E_PARAM;
> +
> +#if defined(CONFIG_TARGET_PRESIDIO_ASIC) || \
> +       defined(CONFIG_TARGET_SATURN_ASIC) || defined(CONFIG_TARGET_VENUS)
> +       union NI_MDIO_OPER_T  mdio_oper;
> +
> +       /* the phy addr 5 is connect to RGMII */
> +       if (addr >= 5)
> +               return  ca_mdio_read_rgmii(addr, offset, data);
> +
> +       mdio_oper.wrd = 0;
> +       mdio_oper.bf.reg_off = offset;
> +       mdio_oper.bf.phy_addr = addr;
> +       mdio_oper.bf.reg_base = CA_NI_MDIO_REG_BASE;
> +       *data = CA_REG_READ(mdio_oper.wrd);
> +
> +       return CA_E_OK;
> +#else
> +       return ca_mdio_read_rgmii(addr, offset, data);
> +#endif
> +}
> +
> +int ca_miiphy_read(const char *devname,
> +                  unsigned char addr,
> +                  unsigned char reg,
> +                  unsigned short *value)
> +{
> +       return ca_mdio_read(addr, reg, value);
> +}
> +
> +int ca_miiphy_write(const char *devname,
> +                   unsigned char addr,
> +                   unsigned char reg,
> +                   unsigned short value)
> +{
> +       return ca_mdio_write(addr, reg, value);
> +}
> +
> +#if defined(CONFIG_TARGET_PRESIDIO_ASIC) || defined(CONFIG_TARGET_VENUS)
> +static void cortina_ni_fix_gphy(void)
> +{
> +       u16     data;
> +       u8      phy_addr;
> +
> +       for (phy_addr = 1; phy_addr < 5; phy_addr++) {
> +               /* Clear clock fail interrupt */
> +               ca_mdio_write(phy_addr, 31, 0xB90);
> +               ca_mdio_read(phy_addr, 19, &data);
> +               if (data == 0x10) {
> +                       ca_mdio_write(phy_addr, 31, 0xB90);
> +                       ca_mdio_read(phy_addr, 19, &data);
> +                       printf("%s: read again phy_addr=", __func__);
> +                       printf("%d, read register 19, ", phy_addr);
> +                       printf("val=0x%x\n", data);
> +               }
> +#ifdef CORTINA_NI_DBG
> +               printf("%s: phy_addr=%d, read register 19, value=0x%x\n",
> +                      __func__, phy_addr, data);
> +#endif
> +       }
> +}
> +#endif
> +
> +int cortina_ni_init(struct udevice *dev)
> +{
> +       u16  vendor_id, chip_id;
> +       u32  phy_id;
> +       u16  phy_reg_value, lpagb, lpa, phy_speed, phy_duplex, speed, duplex;
> +       char *spd, *dup;
> +       union NI_HV_XRAM_CPUXRAM_ADRCFG_RX_t    cpuxram_adrcfg_rx;
> +       union NI_HV_XRAM_CPUXRAM_ADRCFG_TX_0_t  cpuxram_adrcfg_tx;
> +       union NI_HV_XRAM_CPUXRAM_CFG_t          cpuxram_cfg;
> +       union NI_HV_PT_PORT_STATIC_CFG_t        port_static_cfg;
> +       union NI_HV_PT_PORT_GLB_CFG_t           port_glb_cfg;
> +
> +       struct cortina_ni_priv *priv = dev_get_priv(dev);
> +
> +       /* read "ethaddr" and setup to NI regsiters */
> +       ni_setup_mac_addr();
> +
> +       /* RX XRAM ADDRESS CONFIG (start and end address) */
> +       cpuxram_adrcfg_rx.wrd = 0;
> +       cpuxram_adrcfg_rx.bf.rx_top_addr = RX_TOP_ADDR;
> +       cpuxram_adrcfg_rx.bf.rx_base_addr = RX_BASE_ADDR;
> +       CA_REG_WRITE(cpuxram_adrcfg_rx.wrd, (priv->ni_hv_base_addr +
> +                               NI_HV_XRAM_CPUXRAM_ADRCFG_RX_OFFSET));
> +
> +       /* TX XRAM ADDRESS CONFIG (start and end address) */
> +       cpuxram_adrcfg_tx.wrd = 0;
> +       cpuxram_adrcfg_tx.bf.tx_top_addr = TX_TOP_ADDR;
> +       cpuxram_adrcfg_tx.bf.tx_base_addr = TX_BASE_ADDR;
> +       CA_REG_WRITE(cpuxram_adrcfg_tx.wrd, (priv->ni_hv_base_addr +
> +                               NI_HV_XRAM_CPUXRAM_ADRCFG_TX_0_OFFSET));
> +
> +       ca_mdio_read(ge_port_phy_addr, 0x02, &vendor_id);
> +       ca_mdio_read(ge_port_phy_addr, 0x03, &chip_id);
> +       phy_id = (vendor_id << 16) | chip_id;
> +
> +#if defined(CONFIG_TARGET_PRESIDIO_ASIC) || defined(CONFIG_TARGET_VENUS)
> +       /* workaround to fix GPHY fail */
> +       if ((phy_id & PHY_ID_MASK) == PHY_ID_RTL8211_G3_ASIC)
> +               cortina_ni_fix_gphy();
> +#endif
> +
> +       /* PHY GB status */
> +       ca_mdio_read(ge_port_phy_addr, 0x0a, &lpagb);
> +       /* PHY GB control */
> +       ca_mdio_read(ge_port_phy_addr, 0x09, &phy_reg_value);
> +       lpagb &= phy_reg_value << 2;
> +
> +       /* Link Partner Ability */
> +       ca_mdio_read(ge_port_phy_addr, 0x05, &lpa);
> +       /* PHY Advertisement */
> +       ca_mdio_read(ge_port_phy_addr, 0x04, &phy_reg_value);
> +       lpa &= phy_reg_value;
> +
> +       /* phy_speed 0: 10Mbps, 1: 100Mbps, 2: 1000Mbps */
> +       /* duplex 0: half duplex, 1: full duplex */
> +       phy_speed = 0;
> +       phy_duplex = 0;
> +       if (lpagb & (3 << 10)) {
> +               /* 1000Mbps */
> +               phy_speed = 2;
> +               if (lpagb & (1 << 11)) {
> +                       /* 1000Mbps full */
> +                       duplex = 1;
> +               }
> +       } else if (lpa & (3 << 7)) {
> +               /* 100Mbps */
> +               phy_speed = 1;
> +               if (lpa & (1 << 8)) {
> +                       /* 100Mbps full */
> +                       phy_duplex = 1;
> +               }
> +       } else if (lpa & (1 << 6)) {
> +               /* 10Mbps full */
> +               phy_duplex = 1;
> +       }
> +
> +       switch (phy_speed) {
> +       default:
> +       case 0:
> +               spd = "10Mbps";
> +               break;
> +       case 1:
> +               spd = "100Mbps";
> +               break;
> +       case 2:
> +               spd = "1000Mbps";
> +               break;
> +       }
> +
> +       if (duplex == 1)
> +               dup = "full duplex";
> +       else
> +               dup = "half duplex";
> +
> +       printf("PHY ID 0x%08X %s %s\n", phy_id, spd, dup);
> +
> +       switch (phy_id & PHY_ID_MASK) {
> +       case PHY_ID_RTL8214:
> +               port_static_cfg.wrd =
> +                       CA_REG_READ((priv->ni_hv_base_addr +
> +                                  NI_HV_PT_PORT_STATIC_CFG_OFFSET +
> +                                  APB0_NI_HV_PT_STRIDE * active_port));
> +               /* QSGMII_GE */
> +               port_static_cfg.bf.int_cfg = GE_MAC_INTF_QSGMII_1000;
> +               CA_REG_WRITE(port_static_cfg.wrd,
> +                            (priv->ni_hv_base_addr +
> +                             NI_HV_PT_PORT_STATIC_CFG_OFFSET +
> +                             APB0_NI_HV_PT_STRIDE * active_port));
> +               break;
> +       case PHY_ID_RTL8211:
> +               /* fallthrough */
> +#if defined(CONFIG_TARGET_PRESIDIO_ASIC) || defined(CONFIG_TARGET_VENUS)
> +       case PHY_ID_RTL8211_G3_ASIC:
> +               /* fallthrough */
> +#endif
> +#ifdef CONFIG_TARGET_SATURN_ASIC
> +       case PHY_ID_RTL8211_SATURN_ASIC:
> +               /* fallthrough */
> +#endif
> +       default:
> +               /*
> +                * Configuration for Management Ethernet
> +                * Interface:
> +                * - RGMII 1000 mode or RGMII 100 mode
> +                * - MAC mode
> +                */
> +               port_static_cfg.wrd =
> +                       CA_REG_READ((priv->ni_hv_base_addr +
> +                                   NI_HV_PT_PORT_STATIC_CFG_OFFSET +
> +                                   APB0_NI_HV_PT_STRIDE * active_port));
> +               if (phy_speed == 2 /* 1000Mbps */) {
> +                       /* port 4 connects to RGMII PHY */
> +                       if (ge_port_phy_addr == 5)
> +                               port_static_cfg.bf.int_cfg =
> +                                       GE_MAC_INTF_RGMII_1000;
> +                       else
> +                               port_static_cfg.bf.int_cfg = GE_MAC_INTF_GMII;
> +               } else {
> +#if defined(CONFIG_TARGET_PRESIDIO_ASIC) || \
> +       defined(CONFIG_TARGET_SATURN_ASIC) || defined(CONFIG_TARGET_VENUS)
> +                       /* port 4 connects to RGMII PHY */
> +                       if (ge_port_phy_addr == 5) {
> +                               port_static_cfg.bf.int_cfg =
> +                                       GE_MAC_INTF_RGMII_100;
> +                       } else {
> +                               port_static_cfg.bf.int_cfg = GE_MAC_INTF_MII;
> +                       }
> +#else
> +                       port_static_cfg.bf.int_cfg = GE_MAC_INTF_RGMII_100;
> +#endif
> +               }
> +               CA_REG_WRITE(port_static_cfg.wrd,
> +                            (priv->ni_hv_base_addr +
> +                            NI_HV_PT_PORT_STATIC_CFG_OFFSET +
> +                            APB0_NI_HV_PT_STRIDE * active_port));
> +               break;
> +       }
> +
> +       port_glb_cfg.wrd = CA_REG_READ((priv->ni_hv_base_addr +
> +                                      NI_HV_PT_PORT_GLB_CFG_OFFSET +
> +                                      APB0_NI_HV_PT_STRIDE * active_port));
> +       if (phy_speed == 0)  /* 10Mbps */
> +               speed = 1;
> +       else
> +               speed = 0;
> +       if (phy_duplex == 0)  /* half duplex */
> +               duplex = 1;
> +       else
> +               duplex = 0;
> +       port_glb_cfg.bf.speed = speed;
> +       port_glb_cfg.bf.duplex = duplex;
> +       CA_REG_WRITE(port_glb_cfg.wrd, (priv->ni_hv_base_addr +
> +                                       NI_HV_PT_PORT_GLB_CFG_OFFSET +
> +                                       APB0_NI_HV_PT_STRIDE * active_port));
> +
> +#if FOR_DEBUG
> +       /* Enable MFE ethernet interface */
> +       reg_value = CA_REG_READ(NI_TOP_NI_INTF_RST_CONFIG);
> +       reg_value = reg_value & ~(INTF_RST_GE);
> +       CA_REG_WRITE(reg_value, NI_TOP_NI_INTF_RST_CONFIG);
> +#endif
> +
> +       /* Need to toggle the tx and rx cpu_pkt_dis bit */
> +       /* after changing Address config register.      */
> +       cpuxram_cfg.wrd = CA_REG_READ((priv->ni_hv_base_addr +
> +                               NI_HV_XRAM_CPUXRAM_CFG_OFFSET));
> +       cpuxram_cfg.bf.rx_0_cpu_pkt_dis = 1;
> +       cpuxram_cfg.bf.tx_0_cpu_pkt_dis = 1;
> +       CA_REG_WRITE(cpuxram_cfg.wrd, (priv->ni_hv_base_addr +
> +                               NI_HV_XRAM_CPUXRAM_CFG_OFFSET));
> +
> +       cpuxram_cfg.wrd = CA_REG_READ((priv->ni_hv_base_addr +
> +                               NI_HV_XRAM_CPUXRAM_CFG_OFFSET));
> +       cpuxram_cfg.bf.rx_0_cpu_pkt_dis = 0;
> +       cpuxram_cfg.bf.tx_0_cpu_pkt_dis = 0;
> +       CA_REG_WRITE(cpuxram_cfg.wrd, (priv->ni_hv_base_addr +
> +                               NI_HV_XRAM_CPUXRAM_CFG_OFFSET));
> +
> +       ni_enable_tx_rx();
> +
> +       return 0;
> +}
> +
> +int cortina_ni_check_rx_packet(void)
> +{
> +       static int first_time = 1;
> +       union NI_HV_XRAM_CPUXRAM_CFG_t        cpuxram_cfg;
> +
> +       struct cortina_ni_priv *priv = dev_get_priv(curr_dev);
> +
> +       if (first_time) {
> +               /* received all kind of packets */
> +               cpuxram_cfg.wrd = CA_REG_READ((priv->ni_hv_base_addr +
> +                                       NI_HV_XRAM_CPUXRAM_CFG_OFFSET));
> +               cpuxram_cfg.bf.xram_mgmt_promisc_mode = 3;
> +               cpuxram_cfg.bf.rx_0_cpu_pkt_dis = 0;
> +               cpuxram_cfg.bf.tx_0_cpu_pkt_dis = 0;
> +               CA_REG_WRITE(cpuxram_cfg.wrd, (priv->ni_hv_base_addr +
> +                                       NI_HV_XRAM_CPUXRAM_CFG_OFFSET));
> +               first_time = 0;
> +       }
> +
> +       cortina_ni_recv(curr_dev);
> +       return 0;
> +}
> +
> +/*********************************************
> + * Packet receive routine from Management FE
> + * Expects a previously allocated buffer and
> + * fills the length
> + * Retruns 0 on success -1 on failure
> + *******************************************/
> +int cortina_ni_recv(struct udevice *netdev)
> +{
> +       struct cortina_ni_priv *priv = dev_get_priv(netdev);
> +       union NI_HEADER_X_T             header_x;
> +       u32                     pktlen = 0;
> +       u32                     sw_rx_rd_ptr;
> +       u32                     hw_rx_wr_ptr;
> +       u32                     *rx_xram_ptr;
> +       int                     loop;
> +       u32                     *data_ptr;
> +       union NI_PACKET_STATUS packet_status;
> +       union NI_HV_XRAM_CPUXRAM_CPU_STA_RX_0_t cpuxram_cpu_sta_rx;
> +       union NI_HV_XRAM_CPUXRAM_CPU_CFG_RX_0_t cpuxram_cpu_cfg_rx;
> +       int index = 0;
> +#ifdef CORTINA_NI_DBG
> +       int                     blk_num;
> +       u8                      *ptr;
> +#endif
> +
> +       /* get the hw write pointer */
> +       cpuxram_cpu_sta_rx.wrd = CA_REG_READ((priv->ni_hv_base_addr +
> +                               NI_HV_XRAM_CPUXRAM_CPU_STA_RX_0_OFFSET));
> +       hw_rx_wr_ptr = cpuxram_cpu_sta_rx.bf.pkt_wr_ptr;
> +
> +       /* get the sw read pointer */
> +       cpuxram_cpu_cfg_rx.wrd = CA_REG_READ((priv->ni_hv_base_addr +
> +                               NI_HV_XRAM_CPUXRAM_CPU_CFG_RX_0_OFFSET));
> +       sw_rx_rd_ptr = cpuxram_cpu_cfg_rx.bf.pkt_rd_ptr;
> +
> +#if CORTINA_NI_DBG
> +       printf("%s: NI_HV_XRAM_CPUXRAM_CPU_STA_RX_0 = 0x%p, ",
> +              __func__,
> +              priv->ni_hv_base_addr + NI_HV_XRAM_CPUXRAM_CPU_STA_RX_0_OFFSET);
> +       printf("NI_HV_XRAM_CPUXRAM_CPU_CFG_RX_0 = 0x%p\n",
> +              priv->ni_hv_base_addr + NI_HV_XRAM_CPUXRAM_CPU_CFG_RX_0_OFFSET);
> +       printf("%s : RX hw_wr_ptr = %d, sw_rd_ptr = %d\n",
> +              __func__, hw_rx_wr_ptr, sw_rx_rd_ptr);
> +#endif
> +
> +       while (sw_rx_rd_ptr != hw_rx_wr_ptr) {
> +               /* Point to the absolute memory address of XRAM
> +                * where read pointer is
> +                */
> +               rx_xram_ptr = (u32 *)
> +                             ((unsigned long)NI_XRAM_BASE + sw_rx_rd_ptr * 8);
> +
> +               /* Wrap around if required */
> +               if (rx_xram_ptr >= (u32 *)(unsigned long)priv->rx_xram_end_adr)
> +                       rx_xram_ptr = (u32 *)
> +                                     (unsigned long)priv->rx_xram_base_adr;
> +
> +               /* Checking header XR. Do not update the read pointer yet */
> +               //rx_xram_ptr++;
> +               /* skip unused 32-bit in Header XR */
> +               rx_xram_ptr = RDWRPTR_ADVANCE_ONE(rx_xram_ptr,
> +                                                 priv->rx_xram_base_adr,
> +                                                 priv->rx_xram_end_adr);
> +
> +               header_x = (union NI_HEADER_X_T)(*rx_xram_ptr);
> +               /* Header XR [31:0] */
> +
> +               if (*rx_xram_ptr == 0xffffffff)
> +                       printf("%s: XRAM Error !\n", __func__);
> +#if CORTINA_NI_DBG
> +               printf("%s : RX next link %x(%d)\n", __func__,
> +                      header_x.bf.next_link, header_x.bf.next_link);
> +               printf("%s : bytes_valid %x\n", __func__,
> +                      header_x.bf.bytes_valid);
> +#endif
> +
> +               if (header_x.bf.ownership == 0) {
> +                       /* point to Packet status [31:0] */
> +                       //rx_xram_ptr++;
> +                       rx_xram_ptr =
> +                               RDWRPTR_ADVANCE_ONE(rx_xram_ptr,
> +                                                   priv->rx_xram_base_adr,
> +                                                   priv->rx_xram_end_adr);
> +
> +                       packet_status = (union NI_PACKET_STATUS)(*rx_xram_ptr);
> +
> +#if CORTINA_NI_DBG
> +                       printf("%s: packet_status=0x%x\n",
> +                              __func__, packet_status.wrd);
> +#endif
> +                       if (packet_status.bf.valid == 0) {
> +#if CORTINA_NI_DBG
> +                               printf("%s: Invalid Packet !!, ", __func__);
> +                               printf("Packet status=0x%x, ",
> +                                      packet_status.wrd);
> +                               printf("header_x.bf.next_link=%d\n",
> +                                      header_x.bf.next_link);
> +#endif
> +
> +                               /* Update the software read pointer */
> +                               CA_REG_WRITE(header_x.bf.next_link,
> +                                            priv->ni_hv_base_addr +
> +                                       NI_HV_XRAM_CPUXRAM_CPU_CFG_RX_0_OFFSET);
> +                               return 0;
> +                       }
> +
> +                       if (packet_status.bf.drop ||
> +                           packet_status.bf.runt ||
> +                           packet_status.bf.oversize ||
> +                           packet_status.bf.jabber ||
> +                           packet_status.bf.crc_error ||
> +                           packet_status.bf.jumbo) {
> +#if CORTINA_NI_DBG
> +                               printf("%s: Error Packet!! Packet status=0x%x,",
> +                                      __func__, packet_status.wrd);
> +                               printf(" header_x.bf.next_link=%d\n",
> +                                      header_x.bf.next_link);
> +#endif
> +
> +                               /* Update the software read pointer */
> +                               CA_REG_WRITE(header_x.bf.next_link,
> +                                            priv->ni_hv_base_addr +
> +                                       NI_HV_XRAM_CPUXRAM_CPU_CFG_RX_0_OFFSET);
> +                               return 0;
> +                       }
> +
> +                       /* check whether packet size is larger than 1514 */
> +                       if (packet_status.bf.packet_size > 1518) {
> +#if CORTINA_NI_DBG
> +                               printf("%s: Error Packet !! Packet size=%d, ",
> +                                      __func__, packet_status.bf.packet_size);
> +                               printf("larger than 1518, Packet status=0x%x, ",
> +                                      packet_status.wrd);
> +                               printf("header_x.bf.next_link=%d\n",
> +                                      header_x.bf.next_link);
> +#endif
> +
> +                               /* Update the software read pointer */
> +                               CA_REG_WRITE(header_x.bf.next_link,
> +                                            priv->ni_hv_base_addr +
> +                                       NI_HV_XRAM_CPUXRAM_CPU_CFG_RX_0_OFFSET);
> +                               return 0;
> +                       }
> +
> +                       rx_xram_ptr =
> +                               RDWRPTR_ADVANCE_ONE(rx_xram_ptr,
> +                                                   priv->rx_xram_base_adr,
> +                                                   priv->rx_xram_end_adr);
> +
> +                       pktlen = packet_status.bf.packet_size;
> +
> +#if CORTINA_NI_DBG
> +                       printf("%s : rx packet length = %d\n",
> +                              __func__, packet_status.bf.packet_size);
> +#endif
> +
> +                       rx_xram_ptr =
> +                               RDWRPTR_ADVANCE_ONE(rx_xram_ptr,
> +                                                   priv->rx_xram_base_adr,
> +                                                   priv->rx_xram_end_adr);
> +
> +                       data_ptr = (u32 *)net_rx_packets[index];
> +
> +                       /* Read out the packet */
> +                       /* Data is in little endian form in the XRAM */
> +
> +                       /* Send the packet to upper layer */
> +
> +#if CORTINA_NI_DBG
> +                       printf("%s: packet data[]=", __func__);
> +#endif
> +
> +                       for (loop = 0; loop <= pktlen / 4; loop++) {
> +#if CORTINA_NI_DBG
> +                               ptr = (u8 *)rx_xram_ptr;
> +                               if (loop < 10)
> +                                       printf("[0x%x]-[0x%x]-[0x%x]-[0x%x]",
> +                                              ptr[0], ptr[1], ptr[2], ptr[3]);
> +#endif
> +                               *data_ptr++ = *rx_xram_ptr++;
> +                               /* Wrap around if required */
> +                               if (rx_xram_ptr >= (u32 *)
> +                                   (unsigned long)priv->rx_xram_end_adr) {
> +                                       rx_xram_ptr = (u32 *)(unsigned long)
> +                                                      (priv->rx_xram_base_adr);
> +                               }
> +                       }
> +#if CORTINA_NI_DBG
> +                               printf("\n");
> +#endif
> +                       net_process_received_packet(net_rx_packets[index],
> +                                                   pktlen);
> +                       if (++index >= PKTBUFSRX)
> +                               index = 0;
> +#if CORTINA_NI_DBG
> +                       blk_num = net_rx_packets[index][0x2c] * 255 +
> +                               net_rx_packets[index][0x2d];
> +                       printf("%s: tftp block number=%d\n", __func__, blk_num);
> +#endif
> +
> +                       /* Update the software read pointer */
> +                       CA_REG_WRITE(header_x.bf.next_link,
> +                                    (priv->ni_hv_base_addr +
> +                                    NI_HV_XRAM_CPUXRAM_CPU_CFG_RX_0_OFFSET));
> +               }
> +
> +               /* get the hw write pointer */
> +               cpuxram_cpu_sta_rx.wrd =
> +                       CA_REG_READ((priv->ni_hv_base_addr +
> +                                  NI_HV_XRAM_CPUXRAM_CPU_STA_RX_0_OFFSET));
> +               hw_rx_wr_ptr = cpuxram_cpu_sta_rx.bf.pkt_wr_ptr;
> +
> +               /* get the sw read pointer */
> +               sw_rx_rd_ptr =
> +                       CA_REG_READ((priv->ni_hv_base_addr +
> +                                  NI_HV_XRAM_CPUXRAM_CPU_CFG_RX_0_OFFSET));
> +       }
> +       return 0;
> +}
> +
> +/* LITTLE_ENDIAN */
> +static u32 calc_crc(u32 crc, u8 const *p, u32 len)
> +{
> +       int i;
> +
> +       while (len--) {
> +               crc ^= *p++;
> +               for (i = 0; i < 8; i++)
> +                       crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
> +       }
> +       return crc;
> +}
> +
> +static int cortina_ni_send(struct udevice *dev, void *packet, int length)
> +{
> +       struct cortina_ni_priv *priv = dev_get_priv(dev);
> +       u32     hw_tx_rd_ptr;
> +       u32     sw_tx_wr_ptr;
> +       unsigned int    new_pkt_len;
> +       unsigned char   valid_bytes = 0;
> +       u32     *tx_xram_ptr;
> +       u16  next_link = 0;
> +       unsigned char   *pkt_buf_ptr;
> +       unsigned int    loop;
> +       u32     crc32;
> +       union NI_HEADER_X_T     hdr_xt;
> +       int             pad = 0;
> +       static unsigned char   pkt_buf[2048];
> +       u32     *data_ptr;
> +       union NI_HV_XRAM_CPUXRAM_CPU_CFG_TX_0_t cpuxram_cpu_cfg_tx;
> +#if CORTINA_NI_DBG
> +       u8 *ptr;
> +#endif
> +
> +       if (!packet || length > 2032)
> +               return -1;
> +
> +       /* Get the hardware read pointer */
> +       hw_tx_rd_ptr = CA_REG_READ((priv->ni_hv_base_addr +
> +                                 NI_HV_XRAM_CPUXRAM_CPU_STAT_TX_0_OFFSET));
> +
> +       /* Get the software write pointer */
> +       sw_tx_wr_ptr = CA_REG_READ((priv->ni_hv_base_addr +
> +                                 NI_HV_XRAM_CPUXRAM_CPU_CFG_TX_0_OFFSET));
> +
> +#if CORTINA_NI_DBG
> +#if defined(CONFIG_TARGET_SATURN_ASIC)
> +       printf("%s: NI_HV_XRAM_CPUXRAM_CPU_STAT_TX_0=0x%p, ",
> +              __func__,
> +              KSEG1_ATU_XLAT(priv->ni_hv_base_addr +
> +                             NI_HV_XRAM_CPUXRAM_CPU_STAT_TX_0_OFFSET));
> +       printf("NI_HV_XRAM_CPUXRAM_CPU_CFG_TX_0=0x%p\n",
> +              KSEG1_ATU_XLAT(priv->ni_hv_base_addr +
> +                             NI_HV_XRAM_CPUXRAM_CPU_CFG_TX_0_OFFSET));
> +#else
> +       printf("%s: NI_HV_XRAM_CPUXRAM_CPU_STAT_TX_0=0x%p, ",
> +              __func__,
> +              priv->ni_hv_base_addr + NI_HV_XRAM_CPUXRAM_CPU_STAT_TX_0_OFFSET);
> +       printf("NI_HV_XRAM_CPUXRAM_CPU_CFG_TX_0=0x%p\n",
> +              priv->ni_hv_base_addr + NI_HV_XRAM_CPUXRAM_CPU_CFG_TX_0_OFFSET);
> +#endif
> +       printf("%s : hw_tx_rd_ptr = %d\n", __func__, hw_tx_rd_ptr);
> +       printf("%s : sw_tx_wr_ptr = %d\n", __func__, sw_tx_wr_ptr);
> +#endif
> +
> +       if (hw_tx_rd_ptr != sw_tx_wr_ptr) {
> +               printf("%s: Tx FIFO is not available!\n", __func__);
> +               return 1;
> +       }
> +
> +       /* a workaround on 2015/10/01
> +        * the packet size+CRC should be 8-byte alignment
> +        */
> +       if (((length + 4) % 8) != 0)
> +               length += (8 - ((length + 4) % 8));
> +
> +       memset(pkt_buf, 0x00, sizeof(pkt_buf));
> +
> +       /* add 8-byte header_A at the beginning of packet */
> +       //memcpy(&(pkt_buf[0]), (const void *)packet, 8);
> +       memcpy(&pkt_buf[HEADER_A_SIZE], (const void *)packet, length);
> +
> +       pad = 64 - (length + 4);        /* if packet length < 60 */
> +       pad = (pad < 0) ? 0 : pad;
> +
> +#if CORTINA_NI_DBG
> +       printf("%s: length=%d, pad=%d\n", __func__, length, pad);
> +#endif
> +
> +       new_pkt_len = length + pad;     /* new packet length */
> +
> +       pkt_buf_ptr = (unsigned char *)pkt_buf;
> +
> +       /* Calculate the CRC32 */
> +       /* skip 8-byte header_A */
> +       crc32 = ~(calc_crc(~0,
> +                       (u8 *)(pkt_buf_ptr + HEADER_A_SIZE), new_pkt_len));
> +
> +#if CORTINA_NI_DBG
> +       printf("%s: crc32 is 0x%x\n", __func__, crc32);
> +       printf("%s: ~crc32 is 0x%x\n", __func__, ~crc32);
> +       printf("%s: pkt len %d\n", __func__, new_pkt_len);
> +#endif
> +       /* should add 8-byte header_! */
> +       /* CRC will re-calculated by hardware */
> +       memcpy((pkt_buf_ptr + new_pkt_len + HEADER_A_SIZE),
> +              (u8 *)(&crc32), sizeof(crc32));
> +       new_pkt_len = new_pkt_len + 4;  /* add CRC */
> +
> +       valid_bytes = new_pkt_len % 8;
> +       valid_bytes = valid_bytes ? valid_bytes : 0;
> +
> +#if CORTINA_NI_DBG
> +       printf("%s: valid_bytes %d\n", __func__, valid_bytes);
> +#endif
> +
> +       /* should add 8-byte headerA */
> +       next_link = sw_tx_wr_ptr +
> +               (new_pkt_len + 7 + HEADER_A_SIZE) / 8; /* for headr XT */
> +       next_link = next_link + 1; /* add header */
> +       /* Wrap around if required */
> +       if (next_link > priv->tx_xram_end) {
> +               next_link = priv->tx_xram_start +
> +                       (next_link - (priv->tx_xram_end + 1));
> +       }
> +
> +#if CORTINA_NI_DBG
> +       printf("%s: TX next_link %x\n", __func__, next_link);
> +#endif
> +
> +       hdr_xt.wrd = 0;
> +       hdr_xt.bf.ownership = 1;
> +       hdr_xt.bf.bytes_valid = valid_bytes;
> +       hdr_xt.bf.next_link = next_link;
> +
> +       tx_xram_ptr = (u32 *)((unsigned long)NI_XRAM_BASE + sw_tx_wr_ptr * 8);
> +
> +       /* Wrap around if required */
> +       if (tx_xram_ptr >= (u32 *)(unsigned long)priv->tx_xram_end_adr)
> +               tx_xram_ptr = (u32 *)(unsigned long)priv->tx_xram_base_adr;
> +
> +       tx_xram_ptr = RDWRPTR_ADVANCE_ONE(tx_xram_ptr,
> +                                         priv->tx_xram_base_adr,
> +                                         priv->tx_xram_end_adr);
> +
> +       *tx_xram_ptr = hdr_xt.wrd;
> +
> +       tx_xram_ptr = RDWRPTR_ADVANCE_ONE(tx_xram_ptr,
> +                                         priv->tx_xram_base_adr,
> +                                         priv->tx_xram_end_adr);
> +
> +       /* Now to copy the data . The first byte on the line goes first */
> +       data_ptr = (u32 *)pkt_buf_ptr;
> +
> +#if CORTINA_NI_DBG
> +       printf("%s: packet data[]=", __func__);
> +#endif
> +
> +       /* copy header_A to XRAM */
> +       for (loop = 0; loop <= (new_pkt_len + HEADER_A_SIZE) / 4; loop++) {
> +#if CORTINA_NI_DBG
> +               ptr = (u8 *)data_ptr;
> +               if ((loop % 4) == 0)
> +                       printf("\n");
> +               printf("[0x%x]-[0x%x]-[0x%x]-[0x%x]-",
> +                      ptr[0], ptr[1], ptr[2], ptr[3]);
> +#endif
> +
> +               *tx_xram_ptr = *data_ptr++;
> +               tx_xram_ptr = RDWRPTR_ADVANCE_ONE(tx_xram_ptr,
> +                                                 priv->tx_xram_base_adr,
> +                                                 priv->tx_xram_end_adr);
> +       }
> +#if CORTINA_NI_DBG
> +       printf("\n");
> +#endif
> +
> +       /* Publish the software write pointer */
> +       cpuxram_cpu_cfg_tx.bf.pkt_wr_ptr = next_link;
> +       CA_REG_WRITE(cpuxram_cpu_cfg_tx.wrd,
> +                    (priv->ni_hv_base_addr +
> +                    NI_HV_XRAM_CPUXRAM_CPU_CFG_TX_0_OFFSET));
> +
> +       return 0;
> +}
> +
> +void cortina_ni_halt(struct udevice *netdev)
> +{
> +#if FOR_DEBUG
> +       /* MFE MAC configuration Disable tx and rx */
> +       reg_value = CA_REG_READ((priv->ni_hv_base_addr +
> +                              NI_TOP_NI_ETH_MAC_CONFIG0_0_MFE_OFFSET));
> +       reg_value = reg_value & ~(NI_RX_ENB);
> +       reg_value = reg_value & ~(NI_TX_ENB);
> +       CA_REG_WRITE(reg_value, (priv->ni_hv_base_addr +
> +                   NI_TOP_NI_ETH_MAC_CONFIG0_0_MFE_OFFSET));
> +
> +       /* Disable MFE ethernet interface */
> +       reg_value = CA_REG_READ(TOP_NI_INTF_RST_CONFIG);
> +       reg_value = reg_value | (INTF_RST_GE1);
> +       CA_REG_WRITE(reg_value, TOP_NI_INTF_RST_CONFIG);
> +#endif
> +}
> +
> +#define RTL8214_INIT_REG_COUNT         58
> +static u32 rtl8214_init_reg_val[RTL8214_INIT_REG_COUNT] = {
> +       0x6602, 0x84D7, 0x6601, 0x0540, 0x6600, 0x00C0,
> +       0x6602, 0xF994, 0x6601, 0x0541, 0x6600, 0x00C0,
> +       0x6602, 0x2DA3, 0x6601, 0x0542, 0x6600, 0x00C0,
> +       0x6602, 0x3960, 0x6601, 0x0543, 0x6600, 0x00C0,
> +       0x6602, 0x9728, 0x6601, 0x0544, 0x6600, 0x00C0,
> +       0x6602, 0xF83F, 0x6601, 0x0545, 0x6600, 0x00C0,
> +       0x6602, 0x9D85, 0x6601, 0x0423, 0x6600, 0x00C0,
> +       0x6602, 0xD810, 0x6601, 0x0424, 0x6600, 0x00C0,
> +       0x1D11, 0x1506,
> +       0x1D12, 0x0800,
> +       0x6602, 0xC3FA, 0x6601, 0x002E, 0x6600, 0x00C0
> +};
> +
> +#if defined(CONFIG_TARGET_VENUS)
> +static void __internal_phy_init(int reset_phy)
> +{
> +       u16 phy_addr;
> +       u16 data;
> +       u32 start_time;
> +
> +       /* GPHY clock rg_clr_clk_die_sys=0/rg_auto_clr_clk_die[10]=1 */
> +       for (phy_addr = 4; phy_addr > 0; phy_addr--) {
> +               ca_mdio_write(phy_addr, 31, 0x0C40);
> +               ca_mdio_write(phy_addr, 17, 0x0480);
> +       }
> +
> +       /* port 2 PLL port AIF correction */
> +       phy_addr = 3;
> +       ca_mdio_write(phy_addr, 31, 0x0BC6);
> +       ca_mdio_write(phy_addr, 16, 0x0053);
> +       ca_mdio_write(phy_addr, 18, 0x4003);
> +       ca_mdio_write(phy_addr, 22, 0x7E01);
> +       ca_mdio_write(phy_addr, 23, 0);
> +
> +       /* GIGA port AIF correction */
> +       for (phy_addr = 4; phy_addr > 0; phy_addr--) {
> +               ca_mdio_write(phy_addr, 31, 0x0BC0);
> +               ca_mdio_write(phy_addr, 19, 0x01C0);
> +               ca_mdio_write(phy_addr, 20, 0x3C61);
> +               ca_mdio_write(phy_addr, 21, 0x8022);
> +               ca_mdio_write(phy_addr, 23, 0x0441);
> +               ca_mdio_write(phy_addr, 31, 0x0BC1);
> +               ca_mdio_write(phy_addr, 16, 0x1328);
> +               ca_mdio_write(phy_addr, 17, 0xF0B0);
> +               ca_mdio_write(phy_addr, 18, 0x0E00);
> +       }
> +       mdelay(1);
> +
> +       /* check clk_fail = 0
> +        * read phy_fatal_err_int_flag & rg_auto_clr_clk_die[10]=0
> +        * power-on & reset GPHY
> +        */
> +       for (phy_addr = 4; phy_addr > 0; phy_addr--) {
> +               ca_mdio_write(phy_addr, 31, 0x0C40);
> +               ca_mdio_read(phy_addr,  18, &data);
> +               if (data & 1)
> +                       printf("%s: GPHY clock failed!!\n", __func__);
> +               ca_mdio_write(phy_addr, 31, 0x0B90);
> +               ca_mdio_read(phy_addr,  19, &data);
> +               ca_mdio_write(phy_addr, 31, 0x0B90);
> +               ca_mdio_read(phy_addr,  19, &data);
> +               mdelay(1);
> +               ca_mdio_write(phy_addr, 31, 0x0C40);
> +               ca_mdio_write(phy_addr, 17, 0x0080);
> +               mdelay(1);
> +               ca_mdio_write(phy_addr, 31, 0x0A42);
> +
> +               if (reset_phy) {
> +                       ca_mdio_write(phy_addr,  0, 0x9140);
> +
> +                       start_time = get_timer(0);
> +                       while (get_timer(start_time) < 3000) {
> +                               ca_mdio_read(phy_addr,   0, &data);
> +                               if (!(data & 0x8000)) {
> +                                       printf("%s: GPHY addr=%d, ",
> +                                              __func__, phy_addr);
> +                                       printf("reset completed!!\n");
> +                                       break;
> +                               }
> +                       }
> +                       if (data & 0x8000)
> +                               printf("%s: GPHY addr=%d, reset timeout!!\n",
> +                                      __func__, phy_addr);
> +               } else {
> +                       ca_mdio_write(phy_addr,  0, 0x1140);
> +               }
> +
> +               mdelay(100);
> +       }
> +}
> +#endif
> +
> +#if  defined(CONFIG_TARGET_PRESIDIO_ASIC) || defined(CONFIG_TARGET_VENUS)
> +static void ca_ni_internal_phy_init(void)
> +{
> +#if defined(CONFIG_TARGET_PRESIDIO_ASIC)
> +       u8 phy_addr;
> +
> +       /* should initialize 4 GPHYs at once */
> +       for (phy_addr = 4; phy_addr > 0; phy_addr--) {
> +               ca_mdio_write(phy_addr, 31, 0x0BC6);
> +               ca_mdio_write(phy_addr, 16, 0x0053);
> +               ca_mdio_write(phy_addr, 18, 0x4003);
> +               ca_mdio_write(phy_addr, 22, 0x7e01);
> +               ca_mdio_write(phy_addr, 31, 0x0A42);
> +               ca_mdio_write(phy_addr, 31, 0x0A40);
> +               ca_mdio_write(phy_addr,  0, 0x1140);
> +       }
> +
> +       /* workaround to fix GPHY fail */
> +       cortina_ni_fix_gphy();
> +
> +#else
> +       /* should initialize 4 GPHYs at once */
> +       __internal_phy_init(0);
> +#endif
> +}
> +#endif
> +
> +#if defined(CONFIG_TARGET_PRESIDIO_ASIC) || defined(CONFIG_TARGET_VENUS)
> +extern u8 port2led[8][2];
> +
> +static void ca77xx_ni_led(int port, int sw_on)
> +{
> +#ifdef CORTINA_LED_READY
> +       if (sw_on) {
> +               /* turn on led light */
> +               __led_set(1 << port2led[port][0], STATUS_LED_ON);
> +               __led_set(1 << port2led[port][1], STATUS_LED_ON);
> +       } else {
> +               /* turn off led light */
> +               __led_set(1 << port2led[port][0], STATUS_LED_OFF);
> +               __led_set(1 << port2led[port][1], STATUS_LED_OFF);
> +       }
> +#endif
> +}
> +
> +#define AUTO_SCAN_PHY_TIMEOUT  1000            /* 1s */
> +
> +static void ca77xx_ni_scan_active_port(void)
> +{
> +       u8      phy_addr;
> +       int     port;
> +       int     found_active_port = 0;
> +       unsigned short data;
> +
> +       for (phy_addr = 1; phy_addr < 5; phy_addr++) {
> +               port = phy_addr - 1;
> +               ca_mdio_read(phy_addr, 1, &data);
> +               if (data & 0x04) {
> +                       if (found_active_port == 0) {
> +                               /* apply new active_port when port changed */
> +                               if (phy_addr != ge_port_phy_addr) {
> +                                       ge_port_phy_addr = phy_addr;
> +                                       active_port = port;
> +                                       cortina_ni_reset();
> +                                       ca77xx_ni_led(port, 1);
> +                                       printf("active port has been changed ");
> +                                       printf("port %d\n", active_port);
> +                               } else {
> +                                       ca77xx_ni_led(port, 1);
> +                               }
> +                               found_active_port = 1;
> +                       } else {
> +                               ca77xx_ni_led(port, 1);
> +                       }
> +               } else {
> +                       ca77xx_ni_led(port, 0);
> +               }
> +       }
> +}
> +
> +void ca77xx_ni_scan_phy_link(void)
> +{
> +       static u32     start_time;
> +       unsigned short data;
> +
> +       /* if etherent not initialized do nothing */
> +       if (!curr_dev)
> +               return;
> +
> +       if (start_time == 0) {
> +               start_time = get_timer(0);
> +       } else {
> +               /* scan GPHY link status per second */
> +               if (get_timer(start_time) > AUTO_SCAN_PHY_TIMEOUT) {
> +                       if (auto_scan_active_port) {
> +                               /* search for the first link PHY act
> +                                * as active port
> +                                */
> +                               ca77xx_ni_scan_active_port();
> +                       } else {
> +                               ca_mdio_read(ge_port_phy_addr, 1, &data);
> +                               if (data & 0x04)
> +                                       ca77xx_ni_led(active_port, 1);
> +                               else
> +                                       ca77xx_ni_led(active_port, 0);
> +                       }
> +                       start_time = 0;
> +               }
> +       }
> +}
> +
> +/* auto scan the first link up port as active_port */
> +#define AUTO_SCAN_TIMEOUT      3000            /* 3 seconds */
> +static void ca77xx_ni_auto_scan_active_port(void)
> +{
> +       u8      phy_addr;
> +       u32     start_time;
> +       unsigned short data;
> +
> +       ca_ni_internal_phy_init();
> +
> +       start_time = get_timer(0);
> +       while (get_timer(start_time) < AUTO_SCAN_TIMEOUT) {
> +               for (phy_addr = 1; phy_addr < 5; phy_addr++) {
> +                       ca_mdio_read(phy_addr, 1, &data);
> +                       if (data & 0x04) {
> +                               active_port = phy_addr - 1;
> +                               printf("%s: active_port=%d\n",
> +                                      __func__, active_port);
> +
> +                               ca77xx_ni_led(active_port, 1);
> +                               return;
> +                       }
> +               }
> +       }
> +       printf("%s: auto scan active_port timeout, set active_port to 1.\n",
> +              __func__);
> +       active_port = NI_PORT_1;
> +
> +       ca77xx_ni_led(active_port, 0);
> +}
> +
> +#elif defined(CONFIG_TARGET_SATURN_ASIC)
> +
> +/* auto scan the first link up port as active_port */
> +#define AUTO_SCAN_TIMEOUT       3000            /* 3 seconds */
> +static void ca77xx_ni_auto_scan_active_port(void)
> +{
> +       u8      phy_addr;
> +       u32     start_time;
> +       unsigned short data;
> +
> +       /* do internal GHPY reset */
> +       active_port = 3;
> +       cortina_ni_reset();
> +
> +       /* should initialize internal GPHY, NI port 3 */
> +       phy_addr = 1;
> +       ca_mdio_write(phy_addr, 31, 0x0BC6);
> +       ca_mdio_write(phy_addr, 16, 0x0053);
> +       ca_mdio_write(phy_addr, 18, 0x4003);
> +       ca_mdio_write(phy_addr, 22, 0x7e01);
> +       ca_mdio_write(phy_addr, 31, 0x0A42);
> +       ca_mdio_write(phy_addr, 31, 0x0A40);
> +       ca_mdio_write(phy_addr,  0, 0x1140);
> +
> +       /* workaround to fix GPHY fail */
> +       /* Clear clock fail interrupt */
> +       ca_mdio_write(phy_addr, 31, 0xB90);
> +       ca_mdio_read(phy_addr, 19, &data);
> +       //printf("%s: phy_addr=%d, read register 19, value=0x%x\n",
> +       //__func__, phy_addr, data);
Please don't push commented out lines.
> +       if (data == 0x10) {
> +               ca_mdio_write(phy_addr, 31, 0xB90);
> +               ca_mdio_read(phy_addr, 19, &data);
> +               printf("%s: read again phy_addr=%d, ", __func__, phy_addr);
> +               printf("read register 19, value=0x%x\n", data);
> +       }
> +#ifdef CORTINA_NI_DBG
> +       printf("%s: phy_addr=%d, read register 19, value=0x%x\n",
> +              __func__, phy_addr, data);
> +#endif
> +
> +       start_time = get_timer(0);
> +       while (get_timer(start_time) < AUTO_SCAN_TIMEOUT) {
> +               phy_addr = 5;  /* NI port 4 */
> +               ca_mdio_read(phy_addr, 1, &data);
> +               if (data & 0x04) {
> +                       active_port = NI_PORT_4;
> +                       printf("%s: active_port=%d\n", __func__, active_port);
> +                       return;
> +               }
> +               phy_addr = 1;  /* NI port 3 */
> +               ca_mdio_read(phy_addr, 1, &data);
> +               if (data & 0x04) {
> +                       active_port = NI_PORT_3;
> +                       printf("%s: active_port=%d\n", __func__, active_port);
> +                       return;
> +               }
> +       }
> +       printf("%s: auto scan active_port timeout, set active_port to 3.\n",
> +              __func__);
> +       active_port = NI_PORT_3;
> +}
> +
> +#endif
> +
> +#if defined(CONFIG_TARGET_PRESIDIO_ASIC) || defined(CONFIG_TARGET_VENUS)
> +#define GPHY_CAL_LEN   6
> +struct gphy_cal {
> +       u32 reg_off;
> +       u32 value;
> +};
> +
> +static struct gphy_cal gphy_cal_vlaues[GPHY_CAL_LEN] = {
> +       {0xf43380fc, 0xbcd},
> +       {0xf43380dc, 0xeeee},
> +       {0xf43380d8, 0xeeee},
> +       {0xf43380fc, 0xbce},
> +       {0xf43380c0, 0x7777},
> +       {0xf43380c4, 0x7777}
> +};
> +
> +static void do_internal_gphy_cal(void)
> +{
> +       int i, port;
> +       u32 reg_off, value;
> +
> +       for (port = 0; port < 4; port++) {
> +               for (i = 0; i < GPHY_CAL_LEN; i++) {
> +                       reg_off = gphy_cal_vlaues[i].reg_off + (port * 0x80);
> +                       value = gphy_cal_vlaues[i].value;
> +                       CA_REG_WRITE(value, reg_off);
> +                       mdelay(50);
> +               }
> +       }
> +}
> +#endif
> +
> +int ca77xx_eth_initialize(struct udevice *dev)
> +{
> +       struct cortina_ni_priv  *priv;
> +       char            *buf;
> +#if !defined(CONFIG_TARGET_VENUS)
> +       u16             val;
> +#endif
> +       int             i;
> +       u16             vendor_id, chip_id;
> +       u32             phy_id;
> +
> +       env_set_hex("active_port", 1);
> +       env_set_hex("auto_scan_active_port", 1);
> +
> +       priv = dev_get_priv(dev);
> +       priv->rx_xram_base_adr  = NI_XRAM_BASE + (RX_BASE_ADDR * 8);
> +       priv->rx_xram_end_adr   = NI_XRAM_BASE + ((RX_TOP_ADDR + 1) * 8);
> +       priv->rx_xram_start     = RX_BASE_ADDR;
> +       priv->rx_xram_end       = RX_TOP_ADDR;
> +       priv->tx_xram_base_adr  = NI_XRAM_BASE + (TX_BASE_ADDR * 8);
> +       priv->tx_xram_end_adr   = NI_XRAM_BASE + ((TX_TOP_ADDR + 1) * 8);
> +       priv->tx_xram_start     = TX_BASE_ADDR;
> +       priv->tx_xram_end       = TX_TOP_ADDR;
> +
> +       curr_dev = dev;
> +#if CORTINA_NI_DBG
> +       printf("%s: rx_base_addr:%x\t rx_top_addr %x\n",
> +              __func__, priv->rx_xram_start, priv->rx_xram_end);
> +       printf("%s: tx_base_addr:%x\t tx_top_addr %x\n",
> +              __func__, priv->tx_xram_start, priv->tx_xram_end);
> +       printf("%s: rx physical start address = %x end address = %x\n",
> +              __func__, priv->rx_xram_base_adr, priv->rx_xram_end_adr);
> +       printf("%s: tx physical start address = %x end address = %x\n",
> +              __func__, priv->tx_xram_base_adr, priv->tx_xram_end_adr);
> +#endif
> +
> +       //ni_enable_tx_rx();
> +
> +       /* set MDIO pre-scale value */
> +       reg_value = CA_REG_READ(priv->per_mdio_base_addr + PER_MDIO_CFG_OFFSET);
> +       reg_value = reg_value | 0x00280000;
> +       CA_REG_WRITE(reg_value, priv->per_mdio_base_addr + PER_MDIO_CFG_OFFSET);
> +
> +       /* In Saturn active_port are 3 or 4,
> +        * because the register offset has been shifted forward
> +        * LAN ports (port 4-7) connect to RTL8214
> +        */
> +#if defined(CONFIG_TARGET_SATURN_ASIC)
> +       buf = env_get("auto_scan_active_port");
> +       if (buf != 0) {
> +               auto_scan_active_port = simple_strtoul(buf, NULL, 0);
> +               printf("%s: auto_scan_active_port=%d\n",
> +                      __func__, auto_scan_active_port);
> +       }
> +
> +       if (auto_scan_active_port) {
> +               ca77xx_ni_auto_scan_active_port();
> +       } else {
> +               buf = env_get("active_port");
> +               if (buf != 0) {
> +                       active_port = simple_strtoul(buf, NULL, 0);
> +                       printf("%s: active_port=%d\n", __func__, active_port);
> +                       if (active_port != NI_PORT_3 &&
> +                           active_port != NI_PORT_4) {
> +                               printf("ERROR: does not support active_port ");
> +                               printf("%d!!\n", active_port);
> +                               printf("Please change active_port to 3 or 4\n");
> +                               free(dev);
> +                               free(priv);
> +                               return 1;
> +                       }
> +               } else {
> +                       active_port = NI_PORT_4;
> +               }
> +       }
> +#else
> +       buf = env_get("auto_scan_active_port");
> +       if (buf != 0) {
> +               auto_scan_active_port = simple_strtoul(buf, NULL, 0);
> +               printf("%s: auto_scan_active_port=%d\n", __func__,
> +                      auto_scan_active_port);
> +       }
> +       if (auto_scan_active_port) {
> +               ca77xx_ni_auto_scan_active_port();
> +       } else {
> +               buf = env_get("active_port");
> +               if (buf != 0) {
> +                       active_port = simple_strtoul(buf, NULL, 0);
> +                       printf("%s: active_port=%d\n", __func__, active_port);
> +                       if (active_port < NI_PORT_0 ||
> +                           active_port > NI_PORT_4) {
> +                               printf("ERROR: does not support active_port ");
> +                               printf("%d\n", active_port);
> +                               printf("Please change active_port to 0-3.\n");
> +                               free(dev);
> +                               free(priv);
> +                               return 1;
> +                       }
> +               } else {
> +                       active_port = NI_PORT_1;
> +               }
> +       }
> +#endif
> +
> +       cortina_ni_reset();
> +
> +#if defined(CONFIG_TARGET_PRESIDIO_ASIC) || defined(CONFIG_TARGET_VENUS)
> +       /* port0: phy address 1 - GMAC0: port 0
> +        * port1: phy address 2 - GMAC1: port 1
> +        * port2: phy address 3 - GMAC2: port 2
> +        * port3: phy address 4 - GMAC3: port 3
> +        * port4: phy address 5 - RGMII: port 4
> +        */
> +       ge_port_phy_addr = active_port + 1;
> +#else
> +       /* port0: phy address 1 - GMAC0: port 0
> +        * port1: phy address 2 - GMAC1: port 1
> +        * port2: phy address 3 - GMAC2: port 2
> +        * port3: phy address 4 - GMAC3: port 3
> +        * port4: phy address 16 - QSGMII: port 0
> +        * port5: phy address 17 - QSGMII: port 1
> +        * port6: phy address 18 - QSGMII: port 2
> +        * port7: phy address 19 - QSGMII: port 3
> +        */
> +       if (active_port >= NI_PORT_0 && active_port <= NI_PORT_3)
> +               ge_port_phy_addr = active_port + 1;
> +       else
> +               ge_port_phy_addr = active_port - 2;
> +#endif
> +
> +#if defined(CONFIG_TARGET_SATURN_ASIC)
> +       /* internal GPHY addr=1 */
> +       if (active_port == NI_PORT_3)
> +               ge_port_phy_addr = 1;
> +       else
> +               ge_port_phy_addr = active_port + 1;
> +
> +       printf("%s: active_port=%d, ge_port_phy_addr=%d\n",
> +              __func__, active_port, ge_port_phy_addr);
> +#endif
> +
> +       ca_mdio_read(ge_port_phy_addr, 2, &vendor_id);
> +       ca_mdio_read(ge_port_phy_addr, 3, &chip_id);
> +       phy_id = ((u32)vendor_id << 16) | chip_id;
> +
> +       printf("%s: vendor_id=0x%x\n", __func__, vendor_id);
> +       printf("%s: chip_id=0x%x\n", __func__, chip_id);
> +       printf("%s: phy_id=0x%x\n", __func__, phy_id);
> +       printf("%s: phy_id & PHY_ID_MASK=0x%x\n",
> +              __func__, (phy_id & PHY_ID_MASK));
> +       printf("%s: PHY_ID_RTL8214=0x%x\n", __func__, PHY_ID_RTL8214);
> +
> +       if ((phy_id & PHY_ID_MASK) == PHY_ID_RTL8211) {
> +#if !defined(CONFIG_TARGET_VENUS)
> +               printf("%s: do initial patch for PHY_ID_RTL8211\n", __func__);
> +               /*
> +                * Disable response PHYAD=0 function of
> +                * RTL8211 series PHY
> +                */
> +
> +               /* REG31 write 0x0007, set to extension page */
> +               ca_mdio_write(ge_port_phy_addr, 31, 0x0007);
> +
> +               /* REG30 write 0x002C, set to extension page 44 */
> +               ca_mdio_write(ge_port_phy_addr, 30, 0x002C);
> +
> +               /*
> +                * REG27 write bit[2] =0
> +                * disable response PHYAD=0  function.
> +                * we should read REG27 and clear bit[2], and write back
> +                */
> +               ca_mdio_read(ge_port_phy_addr, 27, &val);
> +               val &= ~(1 << 2);
> +               ca_mdio_write(ge_port_phy_addr, 27, val);
> +
> +               /* REG31 write 0X0000, back to page0 */
> +               ca_mdio_write(ge_port_phy_addr, 31, 0x0000);
> +#endif
> +       }
> +
> +       /* the init sequency provided by RTK */
> +       if ((phy_id & PHY_ID_MASK) == PHY_ID_RTL8214) {
> +               printf("%s: write initial sequency for PHY_ID_RTL8214!!\n",
> +                      __func__);
> +               for (i = 0; i < RTL8214_INIT_REG_COUNT; i++) {
> +                       if (!(i & 1)) {
> +                               ca_mdio_write(ge_port_phy_addr, 29,
> +                                             rtl8214_init_reg_val[i]);
> +                       } else {
> +                               ca_mdio_write(ge_port_phy_addr, 30,
> +                                             rtl8214_init_reg_val[i]);
> +                       }
> +               }
> +       }
> +
> +#if defined(CONFIG_TARGET_PRESIDIO_ASIC) || defined(CONFIG_TARGET_VENUS)
> +       if ((phy_id & PHY_ID_MASK) == PHY_ID_RTL8211_G3_ASIC) {
> +               if (!auto_scan_active_port) {
> +                       printf("%s: write initial sequency for ", __func__);
> +                       printf("PHY_ID_RTL8211_G3_ASIC!!\n");
> +
> +                       ca_ni_internal_phy_init();
> +               }
> +#if defined(CONFIG_TARGET_VENUS)
> +               else
> +                       ca77xx_ni_auto_scan_active_port();
> +#endif
> +       }
> +
> +       ca77xx_ni_scan_phy_link();
> +#endif
> +
> +       /* parsing ethaddr and set to NI registers. */
> +       ni_setup_mac_addr();
> +
> +       /* the phy_read and phy_write
> +        * should meet the proto type of miiphy_register
> +        */
> +#ifdef MIIPHY_REGISTER
> +       miiphy_register(dev->name, ca_miiphy_read, ca_miiphy_write);
> +#endif
> +
> +#if CORTINA_NI_DBG
> +       dbg_dev = dev;
> +#endif
> +
> +#if defined(CONFIG_TARGET_PRESIDIO_ASIC) || defined(CONFIG_TARGET_VENUS)
> +       /* hardware settings for RGMII port */
> +       {
> +               union GLOBAL_GLOBAL_CONFIG_t    glb_config;
> +               union GLOBAL_IO_DRIVE_CONTROL_t io_drive_control;
> +
> +               /* Generating 25Mhz reference clock for switch */
> +               glb_config.wrd = CA_REG_READ((priv->glb_base_addr +
> +                                           GLOBAL_GLOBAL_CONFIG_OFFSET));
> +               glb_config.bf.refclk_sel = 0x01;
> +               glb_config.bf.ext_reset = 0x01;
> +               CA_REG_WRITE(glb_config.wrd, (priv->glb_base_addr +
> +                           GLOBAL_GLOBAL_CONFIG_OFFSET));
> +
> +               mdelay(20);
> +
> +               /* should do a external reset */
> +               glb_config.wrd = CA_REG_READ((priv->glb_base_addr +
> +                                           GLOBAL_GLOBAL_CONFIG_OFFSET));
> +               glb_config.bf.ext_reset = 0x0;
> +               CA_REG_WRITE(glb_config.wrd, (priv->glb_base_addr +
> +                           GLOBAL_GLOBAL_CONFIG_OFFSET));
> +
> +               io_drive_control.wrd =
> +                       CA_REG_READ((priv->glb_base_addr +
> +                                  GLOBAL_IO_DRIVE_CONTROL_OFFSET));
> +               io_drive_control.bf.gmac_mode = 2;
> +               io_drive_control.bf.gmac_dn = 1;
> +               io_drive_control.bf.gmac_dp = 1;
> +               CA_REG_WRITE(io_drive_control.wrd, (priv->glb_base_addr +
> +                           GLOBAL_IO_DRIVE_CONTROL_OFFSET));
> +       }
> +
> +       /* initialize LEDs for ethernet link and traffic lights */
> +       //ca77xx_init_led();
> +
> +#if defined(CONFIG_TARGET_PRESIDIO_ASIC)
> +       /* do internal gphy calibration */
> +       do_internal_gphy_cal();
> +#endif
> +       return 0;
> +#endif
> +}
> +
> +#if CORTINA_NI_DBG
> +DECLARE_GLOBAL_DATA_PTR;
> +int do_eth_init(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
> +{
> +       cortina_ni_init(0x00);
> +       return 0;
> +}
> +
> +U_BOOT_CMD(do_eth_init, 2, 1, do_eth_init,
> +          "do_eth_init\t- to test eth_init\n",
> +          "None\n");
> +#endif
> +
> +#if CORTINA_NI_DBG
> +int do_eth_send(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
....<snip>
> +U_BOOT_CMD(phy_reg, 4, 1, do_phy_reg,
> +          "read/write PHY register",
> +          "[PHY addr] [reg_valueaddr] ([value])\n"
> +          "PHY addr : 0-31\n");
> +
This entire debug chunk. why do we need it upstream ?
diff mbox series

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index 8add9d4..1b166d2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -181,6 +181,8 @@  F:	drivers/gpio/cortina_gpio.c
 F:	drivers/watchdog/cortina_wdt.c
 F:	drivers/serial/serial_cortina.c
 F:	drivers/mmc/ca_dw_mmc.c
+F:	drivers/net/cortina_ni.c
+F:	drivers/net/cortina_ni.h
 
 ARM/CZ.NIC TURRIS MOX SUPPORT
 M:	Marek Behun <marek.behun at nic.cz>
@@ -732,6 +734,8 @@  F:	drivers/gpio/cortina_gpio.c
 F:	drivers/watchdog/cortina_wdt.c
 F:	drivers/serial/serial_cortina.c
 F:	drivers/mmc/ca_dw_mmc.c
+F:	drivers/net/cortina_ni.c
+F:	drivers/net/cortina_ni.h
 
 MIPS MSCC
 M:	Gregory CLEMENT <gregory.clement at bootlin.com>
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index f7855c9..45e0480 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -149,6 +149,13 @@  config BCMGENET
 	help
 	  This driver supports the BCMGENET Ethernet MAC.
 
+config CORTINA_NI_ENET
+    	bool "Cortina-Access Ethernet driver"
+    	depends on DM_ETH && CORTINA_PLATFORM
+    	help
+      	  The driver supports the Cortina-Access Ethernet MAC for
+	  all supported CAxxxx SoCs
+
 config DWC_ETH_QOS
 	bool "Synopsys DWC Ethernet QOS device support"
 	depends on DM_ETH
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 383ed1c..1d6ec4f 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -14,6 +14,7 @@  obj-$(CONFIG_DRIVER_AX88180) += ax88180.o
 obj-$(CONFIG_BCM_SF2_ETH) += bcm-sf2-eth.o
 obj-$(CONFIG_BCM_SF2_ETH_GMAC) += bcm-sf2-eth-gmac.o
 obj-$(CONFIG_CALXEDA_XGMAC) += calxedaxgmac.o
+obj-$(CONFIG_CORTINA_NI_ENET) += cortina_ni.o
 obj-$(CONFIG_CS8900) += cs8900.o
 obj-$(CONFIG_TULIP) += dc2114x.o
 obj-$(CONFIG_ETH_DESIGNWARE) += designware.o
diff --git a/drivers/net/cortina_ni.c b/drivers/net/cortina_ni.c
new file mode 100644
index 0000000..d5bbf3e
--- /dev/null
+++ b/drivers/net/cortina_ni.c
@@ -0,0 +1,1909 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * Copyright (C) 2020 Cortina Access Inc.
+ * Author: Aaron Tseng <aaron.tseng at cortina-access.com>
+ *
+ * Ethernet MAC Driver for all supported CAxxxx SoCs
+ */
+
+#include <common.h>
+#include <command.h>
+#include <malloc.h>
+#include <net.h>
+#include <miiphy.h>
+#include <env.h>
+#include <linux/delay.h>
+
+#include "cortina_ni.h"
+
+static u32 reg_value;
+
+/* port 0-3 are individual port connect to PHY directly */
+/* port 4-7 are LAN ports connected to QSGMII PHY */
+int active_port = NI_PORT_5;  /* Physical port 5 */
+u32 ge_port_phy_addr;  /* PHY address connected to active port */
+int auto_scan_active_port;
+
+#define HEADER_A_SIZE	8
+
+/*define CORTINA_NI_DBG if individual rx,tx,init needs to be called */
+#if CORTINA_NI_DBG
+static struct udevice	*dbg_dev;
+#endif
+static struct udevice   *curr_dev;
+
+#if defined(CONFIG_TARGET_SATURN_ASIC)
+#define CA_REG_READ(off)        readl((u64)KSEG1_ATU_XLAT(off))
+#define CA_REG_WRITE(data, off) writel(data, (u64)KSEG1_ATU_XLAT(off))
+#else
+#define CA_REG_READ(off)        readl((u64)off)
+#define CA_REG_WRITE(data, off) writel(data, (u64)off)
+#endif
+
+int cortina_ni_recv(struct udevice *netdev);
+static int ca_ni_ofdata_to_platdata(struct udevice *dev);
+
+static u32 *RDWRPTR_ADVANCE_ONE(u32 *x, unsigned long base, unsigned long max)
+{
+	if (x + 1 >= (u32 *)max)
+		return (u32 *)base;
+	else
+		return (x + 1);
+}
+
+static void ni_setup_mac_addr(void)
+{
+	unsigned char mac[6];
+
+	union NI_HV_GLB_MAC_ADDR_CFG0_t		mac_addr_cfg0;
+	union NI_HV_GLB_MAC_ADDR_CFG1_t		mac_addr_cfg1;
+	union NI_HV_PT_PORT_STATIC_CFG_t	port_static_cfg;
+	union NI_HV_XRAM_CPUXRAM_CFG_t		cpuxram_cfg;
+	struct cortina_ni_priv *priv = dev_get_priv(curr_dev);
+
+	/* parsing ethaddr and set to NI registers. */
+	if (eth_env_get_enetaddr("ethaddr", mac)) {
+		/* The complete MAC address consists of
+		 * {MAC_ADDR0_mac_addr0[0-3], MAC_ADDR1_mac_addr1[4],
+		 * PT_PORT_STATIC_CFG_mac_addr6[5]}.
+		 */
+		mac_addr_cfg0.bf.mac_addr0 = (mac[0] << 24) +
+					     (mac[1] << 16) +
+					     (mac[2] << 8) +
+					     mac[3];
+		CA_REG_WRITE(mac_addr_cfg0.wrd, priv->ni_hv_base_addr
+				+ NI_HV_GLB_MAC_ADDR_CFG0_OFFSET);
+
+		mac_addr_cfg1.wrd = 0;
+		mac_addr_cfg1.bf.mac_addr1 = mac[4];
+		CA_REG_WRITE(mac_addr_cfg1.wrd, (priv->ni_hv_base_addr +
+				NI_HV_GLB_MAC_ADDR_CFG1_OFFSET));
+
+		port_static_cfg.wrd = CA_REG_READ((priv->ni_hv_base_addr +
+					NI_HV_PT_PORT_STATIC_CFG_OFFSET +
+					APB0_NI_HV_PT_STRIDE * active_port));
+
+		port_static_cfg.bf.mac_addr6 = mac[5];
+		CA_REG_WRITE(port_static_cfg.wrd, (priv->ni_hv_base_addr +
+					NI_HV_PT_PORT_STATIC_CFG_OFFSET +
+					APB0_NI_HV_PT_STRIDE * active_port));
+
+		/* received only Broadcast and Address matched packets */
+		cpuxram_cfg.wrd = CA_REG_READ((priv->ni_hv_base_addr +
+				NI_HV_XRAM_CPUXRAM_CFG_OFFSET));
+		cpuxram_cfg.bf.xram_mgmt_promisc_mode = 0;
+		cpuxram_cfg.bf.rx_0_cpu_pkt_dis = 0;
+		cpuxram_cfg.bf.tx_0_cpu_pkt_dis = 0;
+		CA_REG_WRITE(cpuxram_cfg.wrd, (priv->ni_hv_base_addr +
+					NI_HV_XRAM_CPUXRAM_CFG_OFFSET));
+	} else {
+		/* received all packets(promiscuous mode) */
+		cpuxram_cfg.wrd = CA_REG_READ((priv->ni_hv_base_addr +
+					NI_HV_XRAM_CPUXRAM_CFG_OFFSET));
+		cpuxram_cfg.bf.xram_mgmt_promisc_mode = 3;
+		cpuxram_cfg.bf.rx_0_cpu_pkt_dis = 0;
+		cpuxram_cfg.bf.tx_0_cpu_pkt_dis = 0;
+		CA_REG_WRITE(cpuxram_cfg.wrd, (priv->ni_hv_base_addr +
+					NI_HV_XRAM_CPUXRAM_CFG_OFFSET));
+	}
+}
+
+static void ni_enable_tx_rx(void)
+{
+	union NI_HV_PT_RXMAC_CFG_t rxmac_cfg;
+	union NI_HV_PT_TXMAC_CFG_t txmac_cfg;
+
+	struct cortina_ni_priv *priv = dev_get_priv(curr_dev);
+
+	/* Enable TX and RX functions */
+	rxmac_cfg.wrd = CA_REG_READ((priv->ni_hv_base_addr +
+				NI_HV_PT_RXMAC_CFG_OFFSET +
+				APB0_NI_HV_PT_STRIDE * active_port));
+	rxmac_cfg.bf.rx_en = 1;
+	CA_REG_WRITE(rxmac_cfg.wrd, (priv->ni_hv_base_addr +
+				NI_HV_PT_RXMAC_CFG_OFFSET +
+				APB0_NI_HV_PT_STRIDE * active_port));
+
+	txmac_cfg.wrd = CA_REG_READ((priv->ni_hv_base_addr +
+				NI_HV_PT_TXMAC_CFG_OFFSET +
+				APB0_NI_HV_PT_STRIDE * active_port));
+	txmac_cfg.bf.tx_en = 1;
+	CA_REG_WRITE(txmac_cfg.wrd, (priv->ni_hv_base_addr +
+				NI_HV_PT_TXMAC_CFG_OFFSET +
+				APB0_NI_HV_PT_STRIDE * active_port));
+}
+
+void cortina_ni_reset(void)
+{
+	int i;
+	union NI_HV_GLB_INIT_DONE_t		init_done;
+	union NI_HV_GLB_INTF_RST_CONFIG_t	intf_rst_config;
+	union NI_HV_GLB_STATIC_CFG_t		static_cfg;
+	union GLOBAL_BLOCK_RESET_t		glb_blk_reset;
+
+	struct cortina_ni_priv *priv = dev_get_priv(curr_dev);
+
+       /* NI global resets */
+	glb_blk_reset.wrd = CA_REG_READ((priv->glb_base_addr +
+				GLOBAL_BLOCK_RESET_OFFSET));
+	glb_blk_reset.bf.reset_ni = 1;
+	CA_REG_WRITE(glb_blk_reset.wrd, (priv->glb_base_addr +
+				GLOBAL_BLOCK_RESET_OFFSET));
+	/* Remove resets */
+	glb_blk_reset.bf.reset_ni = 0;
+	CA_REG_WRITE(glb_blk_reset.wrd, (priv->glb_base_addr +
+				GLOBAL_BLOCK_RESET_OFFSET));
+
+	/* check the ready bit of NI module */
+	for (i = 0; i < NI_READ_POLL_COUNT; i++) {
+		init_done.wrd = CA_REG_READ((priv->ni_hv_base_addr +
+					NI_HV_GLB_INIT_DONE_OFFSET));
+		if (init_done.bf.ni_init_done)
+			break;
+	}
+	if (i == NI_READ_POLL_COUNT) {
+		printf("%s: NI init done not ready, init_done.wrd=0x%x!!!\n",
+		       __func__, init_done.wrd);
+	}
+
+	intf_rst_config.wrd = CA_REG_READ(priv->ni_hv_base_addr +
+					  NI_HV_GLB_INTF_RST_CONFIG_OFFSET);
+	switch (active_port) {
+#if defined(CONFIG_TARGET_PRESIDIO_ASIC) || defined(CONFIG_TARGET_VENUS)
+	case NI_PORT_0:
+		intf_rst_config.bf.intf_rst_p0 = 0;
+		intf_rst_config.bf.mac_rx_rst_p0 = 0;
+		intf_rst_config.bf.mac_tx_rst_p0 = 0;
+		break;
+	case NI_PORT_1:
+		intf_rst_config.bf.intf_rst_p1 = 0;
+		intf_rst_config.bf.mac_rx_rst_p1 = 0;
+		intf_rst_config.bf.mac_tx_rst_p1 = 0;
+		break;
+	case NI_PORT_2:
+		intf_rst_config.bf.intf_rst_p2 = 0;
+		intf_rst_config.bf.mac_rx_rst_p2 = 0;
+		intf_rst_config.bf.mac_tx_rst_p2 = 0;
+		break;
+#endif
+	case NI_PORT_3:
+		intf_rst_config.bf.intf_rst_p3 = 0;
+		intf_rst_config.bf.mac_tx_rst_p3 = 0;
+		intf_rst_config.bf.mac_rx_rst_p3 = 0;
+		break;
+	case NI_PORT_4:
+		intf_rst_config.bf.intf_rst_p4 = 0;
+		intf_rst_config.bf.mac_tx_rst_p4 = 0;
+		intf_rst_config.bf.mac_rx_rst_p4 = 0;
+		break;
+	}
+
+	CA_REG_WRITE(intf_rst_config.wrd, (priv->ni_hv_base_addr +
+				NI_HV_GLB_INTF_RST_CONFIG_OFFSET));
+
+	/* Only one GMAC can connect to CPU */
+	static_cfg.wrd = CA_REG_READ((priv->ni_hv_base_addr +
+				NI_HV_GLB_STATIC_CFG_OFFSET));
+
+	static_cfg.bf.port_to_cpu = active_port;
+	static_cfg.bf.txmib_mode = 1;
+	static_cfg.bf.rxmib_mode = 1;
+
+	CA_REG_WRITE(static_cfg.wrd, (priv->ni_hv_base_addr +
+				NI_HV_GLB_STATIC_CFG_OFFSET));
+
+	//printf("%s: Connect port %d to CPU\n", __func__, active_port);
+
+#if defined(CONFIG_TARGET_SATURN_ASIC)
+	/* set IO driver control */
+	io_driver_control.wrd = CA_REG_READ((priv->glb_base_addr +
+				GLOBAL_IO_DRIVE_CONTROL_OFFSET));
+	io_driver_control.bf.gmac_dp = 1;
+	io_driver_control.bf.gmac_dn = 1;
+	io_driver_control.bf.gmac_ds = 0;
+	io_driver_control.bf.gmac_mode = 2;
+	CA_REG_WRITE(io_driver_control.wrd, (priv->glb_base_addr +
+				GLOBAL_IO_DRIVE_CONTROL_OFFSET));
+
+	/* initialize internal GPHY */
+	gige_phy.wrd = 0;
+	gige_phy.bf.gphy_phyrst_cen_b = 1;
+	CA_REG_WRITE(gige_phy.wrd, (priv->glb_base_addr +
+				GLOBAL_GIGE_PHY_OFFSET));
+	mdelay(50);
+
+	CA_REG_WRITE(0xa46, 0xd000b0fc);
+	mdelay(50);
+	CA_REG_WRITE(0x1, 0xd000b0d0);
+	mdelay(100);
+#endif
+}
+
+#define NI_ETH_SPEED_100 0xFFFFFFFE
+#define NI_ETH_DUPLEX_FULL 0xFFFFFFD
+#define PHY_MODE_MFE_MAC	BIT(12)
+
+#define NI_RX_ENB		BIT(2)
+#define NI_TX_ENB		BIT(3)
+#define FLOW_CNTL_RX_DSBL	BIT(8)
+#define FLOW_CNTL_TX_DSBL	BIT(12)
+
+static enum ca_status_t ca_mdio_write_rgmii(unsigned int addr,
+					    unsigned int offset,
+					    unsigned short data)
+{
+	union PER_MDIO_ADDR_t  mdio_addr;
+	union PER_MDIO_CTRL_t  mdio_ctrl;
+	/* up to 10000 cycles*/
+	unsigned int      loop_wait = __MDIO_ACCESS_TIMEOUT;
+
+	struct cortina_ni_priv *priv = dev_get_priv(curr_dev);
+
+	mdio_addr.wrd            = 0;
+	mdio_addr.bf.mdio_addr   = addr;
+	mdio_addr.bf.mdio_offset = offset;
+	mdio_addr.bf.mdio_rd_wr  = __MDIO_WR_FLAG;
+	CA_REG_WRITE(mdio_addr.wrd,
+		     priv->per_mdio_base_addr + PER_MDIO_ADDR_OFFSET);
+	CA_REG_WRITE(data,
+		     priv->per_mdio_base_addr + PER_MDIO_WRDATA_OFFSET);
+
+#if CORTINA_NI_DBG
+	printf("%s: mdio_addr.wrd=0x%x\n", __func__, mdio_addr.wrd);
+#endif
+
+	mdio_ctrl.wrd          = 0;
+	mdio_ctrl.bf.mdiostart = 1;
+	CA_REG_WRITE(mdio_ctrl.wrd,
+		     priv->per_mdio_base_addr + PER_MDIO_CTRL_OFFSET);
+
+#if CORTINA_NI_DBG
+	printf("%s: phy_addr=%d, offset=%d, data=0x%x\n",
+	       __func__, addr, offset, data);
+#endif
+
+	do {
+		mdio_ctrl.wrd = CA_REG_READ((priv->per_mdio_base_addr +
+					PER_MDIO_CTRL_OFFSET));
+		if (mdio_ctrl.bf.mdiodone) {
+			CA_REG_WRITE(mdio_ctrl.wrd, (priv->per_mdio_base_addr +
+						PER_MDIO_CTRL_OFFSET));
+			return CA_E_OK;
+		}
+	} while (--loop_wait);
+
+	printf("%s: PHY rite timeout!!!\n", __func__);
+	return CA_E_TIMEOUT;
+}
+
+enum ca_status_t ca_mdio_write(CA_IN       unsigned int             addr,
+			       CA_IN       unsigned int             offset,
+			       CA_IN       unsigned short             data)
+{
+	/* support range: 1~31*/
+	if (addr < CA_MDIO_ADDR_MIN || addr > CA_MDIO_ADDR_MAX)
+		return CA_E_PARAM;
+#if defined(CONFIG_TARGET_PRESIDIO_ASIC) || \
+	defined(CONFIG_TARGET_SATURN_ASIC) || defined(CONFIG_TARGET_VENUS)
+	union NI_MDIO_OPER_T mdio_oper;
+
+	/* the phy addr 5 is connect to RGMII */
+	if (addr >= 5)
+		return ca_mdio_write_rgmii(addr, offset, data);
+
+	mdio_oper.wrd = 0;
+	mdio_oper.bf.reg_off = offset;
+	mdio_oper.bf.phy_addr = addr;
+	mdio_oper.bf.reg_base = CA_NI_MDIO_REG_BASE;
+	CA_REG_WRITE(data, mdio_oper.wrd);
+
+#if CORTINA_NI_DBG
+	printf("%s: mdio_oper.wrd=0x%x, data=0x%x\n",
+	       __func__, mdio_oper.wrd, data);
+#endif
+	return CA_E_OK;
+#else
+	return ca_mdio_write_rgmii(addr, offset, data);
+#endif
+}
+
+static enum ca_status_t ca_mdio_read_rgmii(unsigned int addr,
+					   unsigned int offset,
+					   unsigned short *data)
+{
+	union PER_MDIO_ADDR_t  mdio_addr;
+	union PER_MDIO_CTRL_t  mdio_ctrl;
+	union PER_MDIO_RDDATA_t  read_data;
+	unsigned int loop_wait = __MDIO_ACCESS_TIMEOUT;
+
+	struct cortina_ni_priv *priv = dev_get_priv(curr_dev);
+
+	mdio_addr.wrd            = 0;
+	mdio_addr.bf.mdio_addr   = addr;
+	mdio_addr.bf.mdio_offset = offset;
+	mdio_addr.bf.mdio_rd_wr  = __MDIO_RD_FLAG;
+	CA_REG_WRITE(mdio_addr.wrd,
+		     priv->per_mdio_base_addr + PER_MDIO_ADDR_OFFSET);
+
+	mdio_ctrl.wrd          = 0;
+	mdio_ctrl.bf.mdiostart = 1;
+	CA_REG_WRITE(mdio_ctrl.wrd,
+		     priv->per_mdio_base_addr + PER_MDIO_CTRL_OFFSET);
+
+	do {
+		mdio_ctrl.wrd = CA_REG_READ((priv->per_mdio_base_addr +
+					PER_MDIO_CTRL_OFFSET));
+		if (mdio_ctrl.bf.mdiodone) {
+			CA_REG_WRITE(mdio_ctrl.wrd, (priv->per_mdio_base_addr +
+						PER_MDIO_CTRL_OFFSET));
+			read_data.wrd = CA_REG_READ((priv->per_mdio_base_addr +
+						PER_MDIO_RDDATA_OFFSET));
+			*data = read_data.bf.mdio_rddata;
+			return CA_E_OK;
+		}
+	} while (--loop_wait);
+
+	printf("%s: CA_E_TIMEOUT!!\n", __func__);
+	return CA_E_TIMEOUT;
+}
+
+enum ca_status_t ca_mdio_read(CA_IN       unsigned int          addr,
+			      CA_IN       unsigned int		offset,
+			      CA_OUT      unsigned short	*data)
+{
+	if (!data)
+		return CA_E_PARAM;
+
+	/* support range: 1~31*/
+	if (addr < CA_MDIO_ADDR_MIN || addr > CA_MDIO_ADDR_MAX)
+		return CA_E_PARAM;
+
+#if defined(CONFIG_TARGET_PRESIDIO_ASIC) || \
+	defined(CONFIG_TARGET_SATURN_ASIC) || defined(CONFIG_TARGET_VENUS)
+	union NI_MDIO_OPER_T  mdio_oper;
+
+	/* the phy addr 5 is connect to RGMII */
+	if (addr >= 5)
+		return  ca_mdio_read_rgmii(addr, offset, data);
+
+	mdio_oper.wrd = 0;
+	mdio_oper.bf.reg_off = offset;
+	mdio_oper.bf.phy_addr = addr;
+	mdio_oper.bf.reg_base = CA_NI_MDIO_REG_BASE;
+	*data = CA_REG_READ(mdio_oper.wrd);
+
+	return CA_E_OK;
+#else
+	return ca_mdio_read_rgmii(addr, offset, data);
+#endif
+}
+
+int ca_miiphy_read(const char *devname,
+		   unsigned char addr,
+		   unsigned char reg,
+		   unsigned short *value)
+{
+	return ca_mdio_read(addr, reg, value);
+}
+
+int ca_miiphy_write(const char *devname,
+		    unsigned char addr,
+		    unsigned char reg,
+		    unsigned short value)
+{
+	return ca_mdio_write(addr, reg, value);
+}
+
+#if defined(CONFIG_TARGET_PRESIDIO_ASIC) || defined(CONFIG_TARGET_VENUS)
+static void cortina_ni_fix_gphy(void)
+{
+	u16     data;
+	u8      phy_addr;
+
+	for (phy_addr = 1; phy_addr < 5; phy_addr++) {
+		/* Clear clock fail interrupt */
+		ca_mdio_write(phy_addr, 31, 0xB90);
+		ca_mdio_read(phy_addr, 19, &data);
+		if (data == 0x10) {
+			ca_mdio_write(phy_addr, 31, 0xB90);
+			ca_mdio_read(phy_addr, 19, &data);
+			printf("%s: read again phy_addr=", __func__);
+			printf("%d, read register 19, ", phy_addr);
+			printf("val=0x%x\n", data);
+		}
+#ifdef CORTINA_NI_DBG
+		printf("%s: phy_addr=%d, read register 19, value=0x%x\n",
+		       __func__, phy_addr, data);
+#endif
+	}
+}
+#endif
+
+int cortina_ni_init(struct udevice *dev)
+{
+	u16  vendor_id, chip_id;
+	u32  phy_id;
+	u16  phy_reg_value, lpagb, lpa, phy_speed, phy_duplex, speed, duplex;
+	char *spd, *dup;
+	union NI_HV_XRAM_CPUXRAM_ADRCFG_RX_t	cpuxram_adrcfg_rx;
+	union NI_HV_XRAM_CPUXRAM_ADRCFG_TX_0_t  cpuxram_adrcfg_tx;
+	union NI_HV_XRAM_CPUXRAM_CFG_t		cpuxram_cfg;
+	union NI_HV_PT_PORT_STATIC_CFG_t	port_static_cfg;
+	union NI_HV_PT_PORT_GLB_CFG_t		port_glb_cfg;
+
+	struct cortina_ni_priv *priv = dev_get_priv(dev);
+
+	/* read "ethaddr" and setup to NI regsiters */
+	ni_setup_mac_addr();
+
+	/* RX XRAM ADDRESS CONFIG (start and end address) */
+	cpuxram_adrcfg_rx.wrd = 0;
+	cpuxram_adrcfg_rx.bf.rx_top_addr = RX_TOP_ADDR;
+	cpuxram_adrcfg_rx.bf.rx_base_addr = RX_BASE_ADDR;
+	CA_REG_WRITE(cpuxram_adrcfg_rx.wrd, (priv->ni_hv_base_addr +
+				NI_HV_XRAM_CPUXRAM_ADRCFG_RX_OFFSET));
+
+	/* TX XRAM ADDRESS CONFIG (start and end address) */
+	cpuxram_adrcfg_tx.wrd = 0;
+	cpuxram_adrcfg_tx.bf.tx_top_addr = TX_TOP_ADDR;
+	cpuxram_adrcfg_tx.bf.tx_base_addr = TX_BASE_ADDR;
+	CA_REG_WRITE(cpuxram_adrcfg_tx.wrd, (priv->ni_hv_base_addr +
+				NI_HV_XRAM_CPUXRAM_ADRCFG_TX_0_OFFSET));
+
+	ca_mdio_read(ge_port_phy_addr, 0x02, &vendor_id);
+	ca_mdio_read(ge_port_phy_addr, 0x03, &chip_id);
+	phy_id = (vendor_id << 16) | chip_id;
+
+#if defined(CONFIG_TARGET_PRESIDIO_ASIC) || defined(CONFIG_TARGET_VENUS)
+	/* workaround to fix GPHY fail */
+	if ((phy_id & PHY_ID_MASK) == PHY_ID_RTL8211_G3_ASIC)
+		cortina_ni_fix_gphy();
+#endif
+
+	/* PHY GB status */
+	ca_mdio_read(ge_port_phy_addr, 0x0a, &lpagb);
+	/* PHY GB control */
+	ca_mdio_read(ge_port_phy_addr, 0x09, &phy_reg_value);
+	lpagb &= phy_reg_value << 2;
+
+	/* Link Partner Ability */
+	ca_mdio_read(ge_port_phy_addr, 0x05, &lpa);
+	/* PHY Advertisement */
+	ca_mdio_read(ge_port_phy_addr, 0x04, &phy_reg_value);
+	lpa &= phy_reg_value;
+
+	/* phy_speed 0: 10Mbps, 1: 100Mbps, 2: 1000Mbps */
+	/* duplex 0: half duplex, 1: full duplex */
+	phy_speed = 0;
+	phy_duplex = 0;
+	if (lpagb & (3 << 10)) {
+		/* 1000Mbps */
+		phy_speed = 2;
+		if (lpagb & (1 << 11)) {
+			/* 1000Mbps full */
+			duplex = 1;
+		}
+	} else if (lpa & (3 << 7)) {
+		/* 100Mbps */
+		phy_speed = 1;
+		if (lpa & (1 << 8)) {
+			/* 100Mbps full */
+			phy_duplex = 1;
+		}
+	} else if (lpa & (1 << 6)) {
+		/* 10Mbps full */
+		phy_duplex = 1;
+	}
+
+	switch (phy_speed) {
+	default:
+	case 0:
+		spd = "10Mbps";
+		break;
+	case 1:
+		spd = "100Mbps";
+		break;
+	case 2:
+		spd = "1000Mbps";
+		break;
+	}
+
+	if (duplex == 1)
+		dup = "full duplex";
+	else
+		dup = "half duplex";
+
+	printf("PHY ID 0x%08X %s %s\n", phy_id, spd, dup);
+
+	switch (phy_id & PHY_ID_MASK) {
+	case PHY_ID_RTL8214:
+		port_static_cfg.wrd =
+			CA_REG_READ((priv->ni_hv_base_addr +
+				   NI_HV_PT_PORT_STATIC_CFG_OFFSET +
+				   APB0_NI_HV_PT_STRIDE * active_port));
+		/* QSGMII_GE */
+		port_static_cfg.bf.int_cfg = GE_MAC_INTF_QSGMII_1000;
+		CA_REG_WRITE(port_static_cfg.wrd,
+			     (priv->ni_hv_base_addr +
+			      NI_HV_PT_PORT_STATIC_CFG_OFFSET +
+			      APB0_NI_HV_PT_STRIDE * active_port));
+		break;
+	case PHY_ID_RTL8211:
+		/* fallthrough */
+#if defined(CONFIG_TARGET_PRESIDIO_ASIC) || defined(CONFIG_TARGET_VENUS)
+	case PHY_ID_RTL8211_G3_ASIC:
+		/* fallthrough */
+#endif
+#ifdef CONFIG_TARGET_SATURN_ASIC
+	case PHY_ID_RTL8211_SATURN_ASIC:
+		/* fallthrough */
+#endif
+	default:
+		/*
+		 * Configuration for Management Ethernet
+		 * Interface:
+		 * - RGMII 1000 mode or RGMII 100 mode
+		 * - MAC mode
+		 */
+		port_static_cfg.wrd =
+			CA_REG_READ((priv->ni_hv_base_addr +
+				    NI_HV_PT_PORT_STATIC_CFG_OFFSET +
+				    APB0_NI_HV_PT_STRIDE * active_port));
+		if (phy_speed == 2 /* 1000Mbps */) {
+			/* port 4 connects to RGMII PHY */
+			if (ge_port_phy_addr == 5)
+				port_static_cfg.bf.int_cfg =
+					GE_MAC_INTF_RGMII_1000;
+			else
+				port_static_cfg.bf.int_cfg = GE_MAC_INTF_GMII;
+		} else {
+#if defined(CONFIG_TARGET_PRESIDIO_ASIC) || \
+	defined(CONFIG_TARGET_SATURN_ASIC) || defined(CONFIG_TARGET_VENUS)
+			/* port 4 connects to RGMII PHY */
+			if (ge_port_phy_addr == 5) {
+				port_static_cfg.bf.int_cfg =
+					GE_MAC_INTF_RGMII_100;
+			} else {
+				port_static_cfg.bf.int_cfg = GE_MAC_INTF_MII;
+			}
+#else
+			port_static_cfg.bf.int_cfg = GE_MAC_INTF_RGMII_100;
+#endif
+		}
+		CA_REG_WRITE(port_static_cfg.wrd,
+			     (priv->ni_hv_base_addr +
+			     NI_HV_PT_PORT_STATIC_CFG_OFFSET +
+			     APB0_NI_HV_PT_STRIDE * active_port));
+		break;
+	}
+
+	port_glb_cfg.wrd = CA_REG_READ((priv->ni_hv_base_addr +
+				       NI_HV_PT_PORT_GLB_CFG_OFFSET +
+				       APB0_NI_HV_PT_STRIDE * active_port));
+	if (phy_speed == 0)  /* 10Mbps */
+		speed = 1;
+	else
+		speed = 0;
+	if (phy_duplex == 0)  /* half duplex */
+		duplex = 1;
+	else
+		duplex = 0;
+	port_glb_cfg.bf.speed = speed;
+	port_glb_cfg.bf.duplex = duplex;
+	CA_REG_WRITE(port_glb_cfg.wrd, (priv->ni_hv_base_addr +
+					NI_HV_PT_PORT_GLB_CFG_OFFSET +
+					APB0_NI_HV_PT_STRIDE * active_port));
+
+#if FOR_DEBUG
+	/* Enable MFE ethernet interface */
+	reg_value = CA_REG_READ(NI_TOP_NI_INTF_RST_CONFIG);
+	reg_value = reg_value & ~(INTF_RST_GE);
+	CA_REG_WRITE(reg_value, NI_TOP_NI_INTF_RST_CONFIG);
+#endif
+
+	/* Need to toggle the tx and rx cpu_pkt_dis bit */
+	/* after changing Address config register.      */
+	cpuxram_cfg.wrd = CA_REG_READ((priv->ni_hv_base_addr +
+				NI_HV_XRAM_CPUXRAM_CFG_OFFSET));
+	cpuxram_cfg.bf.rx_0_cpu_pkt_dis = 1;
+	cpuxram_cfg.bf.tx_0_cpu_pkt_dis = 1;
+	CA_REG_WRITE(cpuxram_cfg.wrd, (priv->ni_hv_base_addr +
+				NI_HV_XRAM_CPUXRAM_CFG_OFFSET));
+
+	cpuxram_cfg.wrd = CA_REG_READ((priv->ni_hv_base_addr +
+				NI_HV_XRAM_CPUXRAM_CFG_OFFSET));
+	cpuxram_cfg.bf.rx_0_cpu_pkt_dis = 0;
+	cpuxram_cfg.bf.tx_0_cpu_pkt_dis = 0;
+	CA_REG_WRITE(cpuxram_cfg.wrd, (priv->ni_hv_base_addr +
+				NI_HV_XRAM_CPUXRAM_CFG_OFFSET));
+
+	ni_enable_tx_rx();
+
+	return 0;
+}
+
+int cortina_ni_check_rx_packet(void)
+{
+	static int first_time = 1;
+	union NI_HV_XRAM_CPUXRAM_CFG_t        cpuxram_cfg;
+
+	struct cortina_ni_priv *priv = dev_get_priv(curr_dev);
+
+	if (first_time) {
+		/* received all kind of packets */
+		cpuxram_cfg.wrd = CA_REG_READ((priv->ni_hv_base_addr +
+					NI_HV_XRAM_CPUXRAM_CFG_OFFSET));
+		cpuxram_cfg.bf.xram_mgmt_promisc_mode = 3;
+		cpuxram_cfg.bf.rx_0_cpu_pkt_dis = 0;
+		cpuxram_cfg.bf.tx_0_cpu_pkt_dis = 0;
+		CA_REG_WRITE(cpuxram_cfg.wrd, (priv->ni_hv_base_addr +
+					NI_HV_XRAM_CPUXRAM_CFG_OFFSET));
+		first_time = 0;
+	}
+
+	cortina_ni_recv(curr_dev);
+	return 0;
+}
+
+/*********************************************
+ * Packet receive routine from Management FE
+ * Expects a previously allocated buffer and
+ * fills the length
+ * Retruns 0 on success -1 on failure
+ *******************************************/
+int cortina_ni_recv(struct udevice *netdev)
+{
+	struct cortina_ni_priv *priv = dev_get_priv(netdev);
+	union NI_HEADER_X_T		header_x;
+	u32			pktlen = 0;
+	u32			sw_rx_rd_ptr;
+	u32			hw_rx_wr_ptr;
+	u32			*rx_xram_ptr;
+	int			loop;
+	u32			*data_ptr;
+	union NI_PACKET_STATUS packet_status;
+	union NI_HV_XRAM_CPUXRAM_CPU_STA_RX_0_t cpuxram_cpu_sta_rx;
+	union NI_HV_XRAM_CPUXRAM_CPU_CFG_RX_0_t cpuxram_cpu_cfg_rx;
+	int index = 0;
+#ifdef CORTINA_NI_DBG
+	int			blk_num;
+	u8			*ptr;
+#endif
+
+	/* get the hw write pointer */
+	cpuxram_cpu_sta_rx.wrd = CA_REG_READ((priv->ni_hv_base_addr +
+				NI_HV_XRAM_CPUXRAM_CPU_STA_RX_0_OFFSET));
+	hw_rx_wr_ptr = cpuxram_cpu_sta_rx.bf.pkt_wr_ptr;
+
+	/* get the sw read pointer */
+	cpuxram_cpu_cfg_rx.wrd = CA_REG_READ((priv->ni_hv_base_addr +
+				NI_HV_XRAM_CPUXRAM_CPU_CFG_RX_0_OFFSET));
+	sw_rx_rd_ptr = cpuxram_cpu_cfg_rx.bf.pkt_rd_ptr;
+
+#if CORTINA_NI_DBG
+	printf("%s: NI_HV_XRAM_CPUXRAM_CPU_STA_RX_0 = 0x%p, ",
+	       __func__,
+	       priv->ni_hv_base_addr + NI_HV_XRAM_CPUXRAM_CPU_STA_RX_0_OFFSET);
+	printf("NI_HV_XRAM_CPUXRAM_CPU_CFG_RX_0 = 0x%p\n",
+	       priv->ni_hv_base_addr + NI_HV_XRAM_CPUXRAM_CPU_CFG_RX_0_OFFSET);
+	printf("%s : RX hw_wr_ptr = %d, sw_rd_ptr = %d\n",
+	       __func__, hw_rx_wr_ptr, sw_rx_rd_ptr);
+#endif
+
+	while (sw_rx_rd_ptr != hw_rx_wr_ptr) {
+		/* Point to the absolute memory address of XRAM
+		 * where read pointer is
+		 */
+		rx_xram_ptr = (u32 *)
+			      ((unsigned long)NI_XRAM_BASE + sw_rx_rd_ptr * 8);
+
+		/* Wrap around if required */
+		if (rx_xram_ptr >= (u32 *)(unsigned long)priv->rx_xram_end_adr)
+			rx_xram_ptr = (u32 *)
+				      (unsigned long)priv->rx_xram_base_adr;
+
+		/* Checking header XR. Do not update the read pointer yet */
+		//rx_xram_ptr++;
+		/* skip unused 32-bit in Header XR */
+		rx_xram_ptr = RDWRPTR_ADVANCE_ONE(rx_xram_ptr,
+						  priv->rx_xram_base_adr,
+						  priv->rx_xram_end_adr);
+
+		header_x = (union NI_HEADER_X_T)(*rx_xram_ptr);
+		/* Header XR [31:0] */
+
+		if (*rx_xram_ptr == 0xffffffff)
+			printf("%s: XRAM Error !\n", __func__);
+#if CORTINA_NI_DBG
+		printf("%s : RX next link %x(%d)\n", __func__,
+		       header_x.bf.next_link, header_x.bf.next_link);
+		printf("%s : bytes_valid %x\n", __func__,
+		       header_x.bf.bytes_valid);
+#endif
+
+		if (header_x.bf.ownership == 0) {
+			/* point to Packet status [31:0] */
+			//rx_xram_ptr++;
+			rx_xram_ptr =
+				RDWRPTR_ADVANCE_ONE(rx_xram_ptr,
+						    priv->rx_xram_base_adr,
+						    priv->rx_xram_end_adr);
+
+			packet_status = (union NI_PACKET_STATUS)(*rx_xram_ptr);
+
+#if CORTINA_NI_DBG
+			printf("%s: packet_status=0x%x\n",
+			       __func__, packet_status.wrd);
+#endif
+			if (packet_status.bf.valid == 0) {
+#if CORTINA_NI_DBG
+				printf("%s: Invalid Packet !!, ", __func__);
+				printf("Packet status=0x%x, ",
+				       packet_status.wrd);
+				printf("header_x.bf.next_link=%d\n",
+				       header_x.bf.next_link);
+#endif
+
+				/* Update the software read pointer */
+				CA_REG_WRITE(header_x.bf.next_link,
+					     priv->ni_hv_base_addr +
+					NI_HV_XRAM_CPUXRAM_CPU_CFG_RX_0_OFFSET);
+				return 0;
+			}
+
+			if (packet_status.bf.drop ||
+			    packet_status.bf.runt ||
+			    packet_status.bf.oversize ||
+			    packet_status.bf.jabber ||
+			    packet_status.bf.crc_error ||
+			    packet_status.bf.jumbo) {
+#if CORTINA_NI_DBG
+				printf("%s: Error Packet!! Packet status=0x%x,",
+				       __func__, packet_status.wrd);
+				printf(" header_x.bf.next_link=%d\n",
+				       header_x.bf.next_link);
+#endif
+
+				/* Update the software read pointer */
+				CA_REG_WRITE(header_x.bf.next_link,
+					     priv->ni_hv_base_addr +
+					NI_HV_XRAM_CPUXRAM_CPU_CFG_RX_0_OFFSET);
+				return 0;
+			}
+
+			/* check whether packet size is larger than 1514 */
+			if (packet_status.bf.packet_size > 1518) {
+#if CORTINA_NI_DBG
+				printf("%s: Error Packet !! Packet size=%d, ",
+				       __func__, packet_status.bf.packet_size);
+				printf("larger than 1518, Packet status=0x%x, ",
+				       packet_status.wrd);
+				printf("header_x.bf.next_link=%d\n",
+				       header_x.bf.next_link);
+#endif
+
+				/* Update the software read pointer */
+				CA_REG_WRITE(header_x.bf.next_link,
+					     priv->ni_hv_base_addr +
+					NI_HV_XRAM_CPUXRAM_CPU_CFG_RX_0_OFFSET);
+				return 0;
+			}
+
+			rx_xram_ptr =
+				RDWRPTR_ADVANCE_ONE(rx_xram_ptr,
+						    priv->rx_xram_base_adr,
+						    priv->rx_xram_end_adr);
+
+			pktlen = packet_status.bf.packet_size;
+
+#if CORTINA_NI_DBG
+			printf("%s : rx packet length = %d\n",
+			       __func__, packet_status.bf.packet_size);
+#endif
+
+			rx_xram_ptr =
+				RDWRPTR_ADVANCE_ONE(rx_xram_ptr,
+						    priv->rx_xram_base_adr,
+						    priv->rx_xram_end_adr);
+
+			data_ptr = (u32 *)net_rx_packets[index];
+
+			/* Read out the packet */
+			/* Data is in little endian form in the XRAM */
+
+			/* Send the packet to upper layer */
+
+#if CORTINA_NI_DBG
+			printf("%s: packet data[]=", __func__);
+#endif
+
+			for (loop = 0; loop <= pktlen / 4; loop++) {
+#if CORTINA_NI_DBG
+				ptr = (u8 *)rx_xram_ptr;
+				if (loop < 10)
+					printf("[0x%x]-[0x%x]-[0x%x]-[0x%x]",
+					       ptr[0], ptr[1], ptr[2], ptr[3]);
+#endif
+				*data_ptr++ = *rx_xram_ptr++;
+				/* Wrap around if required */
+				if (rx_xram_ptr >= (u32 *)
+				    (unsigned long)priv->rx_xram_end_adr) {
+					rx_xram_ptr = (u32 *)(unsigned long)
+						       (priv->rx_xram_base_adr);
+				}
+			}
+#if CORTINA_NI_DBG
+				printf("\n");
+#endif
+			net_process_received_packet(net_rx_packets[index],
+						    pktlen);
+			if (++index >= PKTBUFSRX)
+				index = 0;
+#if CORTINA_NI_DBG
+			blk_num = net_rx_packets[index][0x2c] * 255 +
+				net_rx_packets[index][0x2d];
+			printf("%s: tftp block number=%d\n", __func__, blk_num);
+#endif
+
+			/* Update the software read pointer */
+			CA_REG_WRITE(header_x.bf.next_link,
+				     (priv->ni_hv_base_addr +
+				     NI_HV_XRAM_CPUXRAM_CPU_CFG_RX_0_OFFSET));
+		}
+
+		/* get the hw write pointer */
+		cpuxram_cpu_sta_rx.wrd =
+			CA_REG_READ((priv->ni_hv_base_addr +
+				   NI_HV_XRAM_CPUXRAM_CPU_STA_RX_0_OFFSET));
+		hw_rx_wr_ptr = cpuxram_cpu_sta_rx.bf.pkt_wr_ptr;
+
+		/* get the sw read pointer */
+		sw_rx_rd_ptr =
+			CA_REG_READ((priv->ni_hv_base_addr +
+				   NI_HV_XRAM_CPUXRAM_CPU_CFG_RX_0_OFFSET));
+	}
+	return 0;
+}
+
+/* LITTLE_ENDIAN */
+static u32 calc_crc(u32 crc, u8 const *p, u32 len)
+{
+	int i;
+
+	while (len--) {
+		crc ^= *p++;
+		for (i = 0; i < 8; i++)
+			crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
+	}
+	return crc;
+}
+
+static int cortina_ni_send(struct udevice *dev, void *packet, int length)
+{
+	struct cortina_ni_priv *priv = dev_get_priv(dev);
+	u32	hw_tx_rd_ptr;
+	u32	sw_tx_wr_ptr;
+	unsigned int	new_pkt_len;
+	unsigned char	valid_bytes = 0;
+	u32	*tx_xram_ptr;
+	u16  next_link = 0;
+	unsigned char	*pkt_buf_ptr;
+	unsigned int	loop;
+	u32	crc32;
+	union NI_HEADER_X_T	hdr_xt;
+	int		pad = 0;
+	static unsigned char   pkt_buf[2048];
+	u32	*data_ptr;
+	union NI_HV_XRAM_CPUXRAM_CPU_CFG_TX_0_t cpuxram_cpu_cfg_tx;
+#if CORTINA_NI_DBG
+	u8 *ptr;
+#endif
+
+	if (!packet || length > 2032)
+		return -1;
+
+	/* Get the hardware read pointer */
+	hw_tx_rd_ptr = CA_REG_READ((priv->ni_hv_base_addr +
+				  NI_HV_XRAM_CPUXRAM_CPU_STAT_TX_0_OFFSET));
+
+	/* Get the software write pointer */
+	sw_tx_wr_ptr = CA_REG_READ((priv->ni_hv_base_addr +
+				  NI_HV_XRAM_CPUXRAM_CPU_CFG_TX_0_OFFSET));
+
+#if CORTINA_NI_DBG
+#if defined(CONFIG_TARGET_SATURN_ASIC)
+	printf("%s: NI_HV_XRAM_CPUXRAM_CPU_STAT_TX_0=0x%p, ",
+	       __func__,
+	       KSEG1_ATU_XLAT(priv->ni_hv_base_addr +
+			      NI_HV_XRAM_CPUXRAM_CPU_STAT_TX_0_OFFSET));
+	printf("NI_HV_XRAM_CPUXRAM_CPU_CFG_TX_0=0x%p\n",
+	       KSEG1_ATU_XLAT(priv->ni_hv_base_addr +
+			      NI_HV_XRAM_CPUXRAM_CPU_CFG_TX_0_OFFSET));
+#else
+	printf("%s: NI_HV_XRAM_CPUXRAM_CPU_STAT_TX_0=0x%p, ",
+	       __func__,
+	       priv->ni_hv_base_addr + NI_HV_XRAM_CPUXRAM_CPU_STAT_TX_0_OFFSET);
+	printf("NI_HV_XRAM_CPUXRAM_CPU_CFG_TX_0=0x%p\n",
+	       priv->ni_hv_base_addr + NI_HV_XRAM_CPUXRAM_CPU_CFG_TX_0_OFFSET);
+#endif
+	printf("%s : hw_tx_rd_ptr = %d\n", __func__, hw_tx_rd_ptr);
+	printf("%s : sw_tx_wr_ptr = %d\n", __func__, sw_tx_wr_ptr);
+#endif
+
+	if (hw_tx_rd_ptr != sw_tx_wr_ptr) {
+		printf("%s: Tx FIFO is not available!\n", __func__);
+		return 1;
+	}
+
+	/* a workaround on 2015/10/01
+	 * the packet size+CRC should be 8-byte alignment
+	 */
+	if (((length + 4) % 8) != 0)
+		length += (8 - ((length + 4) % 8));
+
+	memset(pkt_buf, 0x00, sizeof(pkt_buf));
+
+	/* add 8-byte header_A at the beginning of packet */
+	//memcpy(&(pkt_buf[0]), (const void *)packet, 8);
+	memcpy(&pkt_buf[HEADER_A_SIZE], (const void *)packet, length);
+
+	pad = 64 - (length + 4);	/* if packet length < 60 */
+	pad = (pad < 0) ? 0 : pad;
+
+#if CORTINA_NI_DBG
+	printf("%s: length=%d, pad=%d\n", __func__, length, pad);
+#endif
+
+	new_pkt_len = length + pad;	/* new packet length */
+
+	pkt_buf_ptr = (unsigned char *)pkt_buf;
+
+	/* Calculate the CRC32 */
+	/* skip 8-byte header_A */
+	crc32 = ~(calc_crc(~0,
+			(u8 *)(pkt_buf_ptr + HEADER_A_SIZE), new_pkt_len));
+
+#if CORTINA_NI_DBG
+	printf("%s: crc32 is 0x%x\n", __func__, crc32);
+	printf("%s: ~crc32 is 0x%x\n", __func__, ~crc32);
+	printf("%s: pkt len %d\n", __func__, new_pkt_len);
+#endif
+	/* should add 8-byte header_! */
+	/* CRC will re-calculated by hardware */
+	memcpy((pkt_buf_ptr + new_pkt_len + HEADER_A_SIZE),
+	       (u8 *)(&crc32), sizeof(crc32));
+	new_pkt_len = new_pkt_len + 4;	/* add CRC */
+
+	valid_bytes = new_pkt_len % 8;
+	valid_bytes = valid_bytes ? valid_bytes : 0;
+
+#if CORTINA_NI_DBG
+	printf("%s: valid_bytes %d\n", __func__, valid_bytes);
+#endif
+
+	/* should add 8-byte headerA */
+	next_link = sw_tx_wr_ptr +
+		(new_pkt_len + 7 + HEADER_A_SIZE) / 8; /* for headr XT */
+	next_link = next_link + 1; /* add header */
+	/* Wrap around if required */
+	if (next_link > priv->tx_xram_end) {
+		next_link = priv->tx_xram_start +
+			(next_link - (priv->tx_xram_end + 1));
+	}
+
+#if CORTINA_NI_DBG
+	printf("%s: TX next_link %x\n", __func__, next_link);
+#endif
+
+	hdr_xt.wrd = 0;
+	hdr_xt.bf.ownership = 1;
+	hdr_xt.bf.bytes_valid = valid_bytes;
+	hdr_xt.bf.next_link = next_link;
+
+	tx_xram_ptr = (u32 *)((unsigned long)NI_XRAM_BASE + sw_tx_wr_ptr * 8);
+
+	/* Wrap around if required */
+	if (tx_xram_ptr >= (u32 *)(unsigned long)priv->tx_xram_end_adr)
+		tx_xram_ptr = (u32 *)(unsigned long)priv->tx_xram_base_adr;
+
+	tx_xram_ptr = RDWRPTR_ADVANCE_ONE(tx_xram_ptr,
+					  priv->tx_xram_base_adr,
+					  priv->tx_xram_end_adr);
+
+	*tx_xram_ptr = hdr_xt.wrd;
+
+	tx_xram_ptr = RDWRPTR_ADVANCE_ONE(tx_xram_ptr,
+					  priv->tx_xram_base_adr,
+					  priv->tx_xram_end_adr);
+
+	/* Now to copy the data . The first byte on the line goes first */
+	data_ptr = (u32 *)pkt_buf_ptr;
+
+#if CORTINA_NI_DBG
+	printf("%s: packet data[]=", __func__);
+#endif
+
+	/* copy header_A to XRAM */
+	for (loop = 0; loop <= (new_pkt_len + HEADER_A_SIZE) / 4; loop++) {
+#if CORTINA_NI_DBG
+		ptr = (u8 *)data_ptr;
+		if ((loop % 4) == 0)
+			printf("\n");
+		printf("[0x%x]-[0x%x]-[0x%x]-[0x%x]-",
+		       ptr[0], ptr[1], ptr[2], ptr[3]);
+#endif
+
+		*tx_xram_ptr = *data_ptr++;
+		tx_xram_ptr = RDWRPTR_ADVANCE_ONE(tx_xram_ptr,
+						  priv->tx_xram_base_adr,
+						  priv->tx_xram_end_adr);
+	}
+#if CORTINA_NI_DBG
+	printf("\n");
+#endif
+
+	/* Publish the software write pointer */
+	cpuxram_cpu_cfg_tx.bf.pkt_wr_ptr = next_link;
+	CA_REG_WRITE(cpuxram_cpu_cfg_tx.wrd,
+		     (priv->ni_hv_base_addr +
+		     NI_HV_XRAM_CPUXRAM_CPU_CFG_TX_0_OFFSET));
+
+	return 0;
+}
+
+void cortina_ni_halt(struct udevice *netdev)
+{
+#if FOR_DEBUG
+	/* MFE MAC configuration Disable tx and rx */
+	reg_value = CA_REG_READ((priv->ni_hv_base_addr +
+			       NI_TOP_NI_ETH_MAC_CONFIG0_0_MFE_OFFSET));
+	reg_value = reg_value & ~(NI_RX_ENB);
+	reg_value = reg_value & ~(NI_TX_ENB);
+	CA_REG_WRITE(reg_value, (priv->ni_hv_base_addr +
+		    NI_TOP_NI_ETH_MAC_CONFIG0_0_MFE_OFFSET));
+
+	/* Disable MFE ethernet interface */
+	reg_value = CA_REG_READ(TOP_NI_INTF_RST_CONFIG);
+	reg_value = reg_value | (INTF_RST_GE1);
+	CA_REG_WRITE(reg_value, TOP_NI_INTF_RST_CONFIG);
+#endif
+}
+
+#define RTL8214_INIT_REG_COUNT		58
+static u32 rtl8214_init_reg_val[RTL8214_INIT_REG_COUNT] = {
+	0x6602, 0x84D7, 0x6601, 0x0540, 0x6600, 0x00C0,
+	0x6602, 0xF994, 0x6601, 0x0541, 0x6600, 0x00C0,
+	0x6602, 0x2DA3, 0x6601, 0x0542, 0x6600, 0x00C0,
+	0x6602, 0x3960, 0x6601, 0x0543, 0x6600, 0x00C0,
+	0x6602, 0x9728, 0x6601, 0x0544, 0x6600, 0x00C0,
+	0x6602, 0xF83F, 0x6601, 0x0545, 0x6600, 0x00C0,
+	0x6602, 0x9D85, 0x6601, 0x0423, 0x6600, 0x00C0,
+	0x6602, 0xD810, 0x6601, 0x0424, 0x6600, 0x00C0,
+	0x1D11, 0x1506,
+	0x1D12, 0x0800,
+	0x6602, 0xC3FA, 0x6601, 0x002E, 0x6600, 0x00C0
+};
+
+#if defined(CONFIG_TARGET_VENUS)
+static void __internal_phy_init(int reset_phy)
+{
+	u16 phy_addr;
+	u16 data;
+	u32 start_time;
+
+	/* GPHY clock rg_clr_clk_die_sys=0/rg_auto_clr_clk_die[10]=1 */
+	for (phy_addr = 4; phy_addr > 0; phy_addr--) {
+		ca_mdio_write(phy_addr, 31, 0x0C40);
+		ca_mdio_write(phy_addr, 17, 0x0480);
+	}
+
+	/* port 2 PLL port AIF correction */
+	phy_addr = 3;
+	ca_mdio_write(phy_addr, 31, 0x0BC6);
+	ca_mdio_write(phy_addr, 16, 0x0053);
+	ca_mdio_write(phy_addr, 18, 0x4003);
+	ca_mdio_write(phy_addr, 22, 0x7E01);
+	ca_mdio_write(phy_addr, 23, 0);
+
+	/* GIGA port AIF correction */
+	for (phy_addr = 4; phy_addr > 0; phy_addr--) {
+		ca_mdio_write(phy_addr, 31, 0x0BC0);
+		ca_mdio_write(phy_addr, 19, 0x01C0);
+		ca_mdio_write(phy_addr, 20, 0x3C61);
+		ca_mdio_write(phy_addr, 21, 0x8022);
+		ca_mdio_write(phy_addr, 23, 0x0441);
+		ca_mdio_write(phy_addr, 31, 0x0BC1);
+		ca_mdio_write(phy_addr, 16, 0x1328);
+		ca_mdio_write(phy_addr, 17, 0xF0B0);
+		ca_mdio_write(phy_addr, 18, 0x0E00);
+	}
+	mdelay(1);
+
+	/* check clk_fail = 0
+	 * read phy_fatal_err_int_flag & rg_auto_clr_clk_die[10]=0
+	 * power-on & reset GPHY
+	 */
+	for (phy_addr = 4; phy_addr > 0; phy_addr--) {
+		ca_mdio_write(phy_addr, 31, 0x0C40);
+		ca_mdio_read(phy_addr,  18, &data);
+		if (data & 1)
+			printf("%s: GPHY clock failed!!\n", __func__);
+		ca_mdio_write(phy_addr, 31, 0x0B90);
+		ca_mdio_read(phy_addr,  19, &data);
+		ca_mdio_write(phy_addr, 31, 0x0B90);
+		ca_mdio_read(phy_addr,  19, &data);
+		mdelay(1);
+		ca_mdio_write(phy_addr, 31, 0x0C40);
+		ca_mdio_write(phy_addr, 17, 0x0080);
+		mdelay(1);
+		ca_mdio_write(phy_addr, 31, 0x0A42);
+
+		if (reset_phy) {
+			ca_mdio_write(phy_addr,  0, 0x9140);
+
+			start_time = get_timer(0);
+			while (get_timer(start_time) < 3000) {
+				ca_mdio_read(phy_addr,   0, &data);
+				if (!(data & 0x8000)) {
+					printf("%s: GPHY addr=%d, ",
+					       __func__, phy_addr);
+					printf("reset completed!!\n");
+					break;
+				}
+			}
+			if (data & 0x8000)
+				printf("%s: GPHY addr=%d, reset timeout!!\n",
+				       __func__, phy_addr);
+		} else {
+			ca_mdio_write(phy_addr,  0, 0x1140);
+		}
+
+		mdelay(100);
+	}
+}
+#endif
+
+#if  defined(CONFIG_TARGET_PRESIDIO_ASIC) || defined(CONFIG_TARGET_VENUS)
+static void ca_ni_internal_phy_init(void)
+{
+#if defined(CONFIG_TARGET_PRESIDIO_ASIC)
+	u8 phy_addr;
+
+	/* should initialize 4 GPHYs at once */
+	for (phy_addr = 4; phy_addr > 0; phy_addr--) {
+		ca_mdio_write(phy_addr, 31, 0x0BC6);
+		ca_mdio_write(phy_addr, 16, 0x0053);
+		ca_mdio_write(phy_addr, 18, 0x4003);
+		ca_mdio_write(phy_addr, 22, 0x7e01);
+		ca_mdio_write(phy_addr, 31, 0x0A42);
+		ca_mdio_write(phy_addr, 31, 0x0A40);
+		ca_mdio_write(phy_addr,  0, 0x1140);
+	}
+
+	/* workaround to fix GPHY fail */
+	cortina_ni_fix_gphy();
+
+#else
+	/* should initialize 4 GPHYs at once */
+	__internal_phy_init(0);
+#endif
+}
+#endif
+
+#if defined(CONFIG_TARGET_PRESIDIO_ASIC) || defined(CONFIG_TARGET_VENUS)
+extern u8 port2led[8][2];
+
+static void ca77xx_ni_led(int port, int sw_on)
+{
+#ifdef CORTINA_LED_READY
+	if (sw_on) {
+		/* turn on led light */
+		__led_set(1 << port2led[port][0], STATUS_LED_ON);
+		__led_set(1 << port2led[port][1], STATUS_LED_ON);
+	} else {
+		/* turn off led light */
+		__led_set(1 << port2led[port][0], STATUS_LED_OFF);
+		__led_set(1 << port2led[port][1], STATUS_LED_OFF);
+	}
+#endif
+}
+
+#define AUTO_SCAN_PHY_TIMEOUT	1000		/* 1s */
+
+static void ca77xx_ni_scan_active_port(void)
+{
+	u8	phy_addr;
+	int	port;
+	int	found_active_port = 0;
+	unsigned short data;
+
+	for (phy_addr = 1; phy_addr < 5; phy_addr++) {
+		port = phy_addr - 1;
+		ca_mdio_read(phy_addr, 1, &data);
+		if (data & 0x04) {
+			if (found_active_port == 0) {
+				/* apply new active_port when port changed */
+				if (phy_addr != ge_port_phy_addr) {
+					ge_port_phy_addr = phy_addr;
+					active_port = port;
+					cortina_ni_reset();
+					ca77xx_ni_led(port, 1);
+					printf("active port has been changed ");
+					printf("port %d\n", active_port);
+				} else {
+					ca77xx_ni_led(port, 1);
+				}
+				found_active_port = 1;
+			} else {
+				ca77xx_ni_led(port, 1);
+			}
+		} else {
+			ca77xx_ni_led(port, 0);
+		}
+	}
+}
+
+void ca77xx_ni_scan_phy_link(void)
+{
+	static u32     start_time;
+	unsigned short data;
+
+	/* if etherent not initialized do nothing */
+	if (!curr_dev)
+		return;
+
+	if (start_time == 0) {
+		start_time = get_timer(0);
+	} else {
+		/* scan GPHY link status per second */
+		if (get_timer(start_time) > AUTO_SCAN_PHY_TIMEOUT) {
+			if (auto_scan_active_port) {
+				/* search for the first link PHY act
+				 * as active port
+				 */
+				ca77xx_ni_scan_active_port();
+			} else {
+				ca_mdio_read(ge_port_phy_addr, 1, &data);
+				if (data & 0x04)
+					ca77xx_ni_led(active_port, 1);
+				else
+					ca77xx_ni_led(active_port, 0);
+			}
+			start_time = 0;
+		}
+	}
+}
+
+/* auto scan the first link up port as active_port */
+#define AUTO_SCAN_TIMEOUT	3000		/* 3 seconds */
+static void ca77xx_ni_auto_scan_active_port(void)
+{
+	u8	phy_addr;
+	u32	start_time;
+	unsigned short data;
+
+	ca_ni_internal_phy_init();
+
+	start_time = get_timer(0);
+	while (get_timer(start_time) < AUTO_SCAN_TIMEOUT) {
+		for (phy_addr = 1; phy_addr < 5; phy_addr++) {
+			ca_mdio_read(phy_addr, 1, &data);
+			if (data & 0x04) {
+				active_port = phy_addr - 1;
+				printf("%s: active_port=%d\n",
+				       __func__, active_port);
+
+				ca77xx_ni_led(active_port, 1);
+				return;
+			}
+		}
+	}
+	printf("%s: auto scan active_port timeout, set active_port to 1.\n",
+	       __func__);
+	active_port = NI_PORT_1;
+
+	ca77xx_ni_led(active_port, 0);
+}
+
+#elif defined(CONFIG_TARGET_SATURN_ASIC)
+
+/* auto scan the first link up port as active_port */
+#define AUTO_SCAN_TIMEOUT       3000            /* 3 seconds */
+static void ca77xx_ni_auto_scan_active_port(void)
+{
+	u8      phy_addr;
+	u32     start_time;
+	unsigned short data;
+
+	/* do internal GHPY reset */
+	active_port = 3;
+	cortina_ni_reset();
+
+	/* should initialize internal GPHY, NI port 3 */
+	phy_addr = 1;
+	ca_mdio_write(phy_addr, 31, 0x0BC6);
+	ca_mdio_write(phy_addr, 16, 0x0053);
+	ca_mdio_write(phy_addr, 18, 0x4003);
+	ca_mdio_write(phy_addr, 22, 0x7e01);
+	ca_mdio_write(phy_addr, 31, 0x0A42);
+	ca_mdio_write(phy_addr, 31, 0x0A40);
+	ca_mdio_write(phy_addr,  0, 0x1140);
+
+	/* workaround to fix GPHY fail */
+	/* Clear clock fail interrupt */
+	ca_mdio_write(phy_addr, 31, 0xB90);
+	ca_mdio_read(phy_addr, 19, &data);
+	//printf("%s: phy_addr=%d, read register 19, value=0x%x\n",
+	//__func__, phy_addr, data);
+	if (data == 0x10) {
+		ca_mdio_write(phy_addr, 31, 0xB90);
+		ca_mdio_read(phy_addr, 19, &data);
+		printf("%s: read again phy_addr=%d, ", __func__, phy_addr);
+		printf("read register 19, value=0x%x\n", data);
+	}
+#ifdef CORTINA_NI_DBG
+	printf("%s: phy_addr=%d, read register 19, value=0x%x\n",
+	       __func__, phy_addr, data);
+#endif
+
+	start_time = get_timer(0);
+	while (get_timer(start_time) < AUTO_SCAN_TIMEOUT) {
+		phy_addr = 5;  /* NI port 4 */
+		ca_mdio_read(phy_addr, 1, &data);
+		if (data & 0x04) {
+			active_port = NI_PORT_4;
+			printf("%s: active_port=%d\n", __func__, active_port);
+			return;
+		}
+		phy_addr = 1;  /* NI port 3 */
+		ca_mdio_read(phy_addr, 1, &data);
+		if (data & 0x04) {
+			active_port = NI_PORT_3;
+			printf("%s: active_port=%d\n", __func__, active_port);
+			return;
+		}
+	}
+	printf("%s: auto scan active_port timeout, set active_port to 3.\n",
+	       __func__);
+	active_port = NI_PORT_3;
+}
+
+#endif
+
+#if defined(CONFIG_TARGET_PRESIDIO_ASIC) || defined(CONFIG_TARGET_VENUS)
+#define GPHY_CAL_LEN	6
+struct gphy_cal {
+	u32 reg_off;
+	u32 value;
+};
+
+static struct gphy_cal gphy_cal_vlaues[GPHY_CAL_LEN] = {
+	{0xf43380fc, 0xbcd},
+	{0xf43380dc, 0xeeee},
+	{0xf43380d8, 0xeeee},
+	{0xf43380fc, 0xbce},
+	{0xf43380c0, 0x7777},
+	{0xf43380c4, 0x7777}
+};
+
+static void do_internal_gphy_cal(void)
+{
+	int i, port;
+	u32 reg_off, value;
+
+	for (port = 0; port < 4; port++) {
+		for (i = 0; i < GPHY_CAL_LEN; i++) {
+			reg_off = gphy_cal_vlaues[i].reg_off + (port * 0x80);
+			value = gphy_cal_vlaues[i].value;
+			CA_REG_WRITE(value, reg_off);
+			mdelay(50);
+		}
+	}
+}
+#endif
+
+int ca77xx_eth_initialize(struct udevice *dev)
+{
+	struct cortina_ni_priv	*priv;
+	char		*buf;
+#if !defined(CONFIG_TARGET_VENUS)
+	u16		val;
+#endif
+	int		i;
+	u16		vendor_id, chip_id;
+	u32		phy_id;
+
+	env_set_hex("active_port", 1);
+	env_set_hex("auto_scan_active_port", 1);
+
+	priv = dev_get_priv(dev);
+	priv->rx_xram_base_adr	= NI_XRAM_BASE + (RX_BASE_ADDR * 8);
+	priv->rx_xram_end_adr	= NI_XRAM_BASE + ((RX_TOP_ADDR + 1) * 8);
+	priv->rx_xram_start	= RX_BASE_ADDR;
+	priv->rx_xram_end	= RX_TOP_ADDR;
+	priv->tx_xram_base_adr	= NI_XRAM_BASE + (TX_BASE_ADDR * 8);
+	priv->tx_xram_end_adr	= NI_XRAM_BASE + ((TX_TOP_ADDR + 1) * 8);
+	priv->tx_xram_start	= TX_BASE_ADDR;
+	priv->tx_xram_end	= TX_TOP_ADDR;
+
+	curr_dev = dev;
+#if CORTINA_NI_DBG
+	printf("%s: rx_base_addr:%x\t rx_top_addr %x\n",
+	       __func__, priv->rx_xram_start, priv->rx_xram_end);
+	printf("%s: tx_base_addr:%x\t tx_top_addr %x\n",
+	       __func__, priv->tx_xram_start, priv->tx_xram_end);
+	printf("%s: rx physical start address = %x end address = %x\n",
+	       __func__, priv->rx_xram_base_adr, priv->rx_xram_end_adr);
+	printf("%s: tx physical start address = %x end address = %x\n",
+	       __func__, priv->tx_xram_base_adr, priv->tx_xram_end_adr);
+#endif
+
+	//ni_enable_tx_rx();
+
+	/* set MDIO pre-scale value */
+	reg_value = CA_REG_READ(priv->per_mdio_base_addr + PER_MDIO_CFG_OFFSET);
+	reg_value = reg_value | 0x00280000;
+	CA_REG_WRITE(reg_value, priv->per_mdio_base_addr + PER_MDIO_CFG_OFFSET);
+
+	/* In Saturn active_port are 3 or 4,
+	 * because the register offset has been shifted forward
+	 * LAN ports (port 4-7) connect to RTL8214
+	 */
+#if defined(CONFIG_TARGET_SATURN_ASIC)
+	buf = env_get("auto_scan_active_port");
+	if (buf != 0) {
+		auto_scan_active_port = simple_strtoul(buf, NULL, 0);
+		printf("%s: auto_scan_active_port=%d\n",
+		       __func__, auto_scan_active_port);
+	}
+
+	if (auto_scan_active_port) {
+		ca77xx_ni_auto_scan_active_port();
+	} else {
+		buf = env_get("active_port");
+		if (buf != 0) {
+			active_port = simple_strtoul(buf, NULL, 0);
+			printf("%s: active_port=%d\n", __func__, active_port);
+			if (active_port != NI_PORT_3 &&
+			    active_port != NI_PORT_4) {
+				printf("ERROR: does not support active_port ");
+				printf("%d!!\n", active_port);
+				printf("Please change active_port to 3 or 4\n");
+				free(dev);
+				free(priv);
+				return 1;
+			}
+		} else {
+			active_port = NI_PORT_4;
+		}
+	}
+#else
+	buf = env_get("auto_scan_active_port");
+	if (buf != 0) {
+		auto_scan_active_port = simple_strtoul(buf, NULL, 0);
+		printf("%s: auto_scan_active_port=%d\n", __func__,
+		       auto_scan_active_port);
+	}
+	if (auto_scan_active_port) {
+		ca77xx_ni_auto_scan_active_port();
+	} else {
+		buf = env_get("active_port");
+		if (buf != 0) {
+			active_port = simple_strtoul(buf, NULL, 0);
+			printf("%s: active_port=%d\n", __func__, active_port);
+			if (active_port < NI_PORT_0 ||
+			    active_port > NI_PORT_4) {
+				printf("ERROR: does not support active_port ");
+				printf("%d\n", active_port);
+				printf("Please change active_port to 0-3.\n");
+				free(dev);
+				free(priv);
+				return 1;
+			}
+		} else {
+			active_port = NI_PORT_1;
+		}
+	}
+#endif
+
+	cortina_ni_reset();
+
+#if defined(CONFIG_TARGET_PRESIDIO_ASIC) || defined(CONFIG_TARGET_VENUS)
+	/* port0: phy address 1 - GMAC0: port 0
+	 * port1: phy address 2 - GMAC1: port 1
+	 * port2: phy address 3 - GMAC2: port 2
+	 * port3: phy address 4 - GMAC3: port 3
+	 * port4: phy address 5 - RGMII: port 4
+	 */
+	ge_port_phy_addr = active_port + 1;
+#else
+	/* port0: phy address 1	- GMAC0: port 0
+	 * port1: phy address 2 - GMAC1: port 1
+	 * port2: phy address 3 - GMAC2: port 2
+	 * port3: phy address 4 - GMAC3: port 3
+	 * port4: phy address 16 - QSGMII: port 0
+	 * port5: phy address 17 - QSGMII: port 1
+	 * port6: phy address 18 - QSGMII: port 2
+	 * port7: phy address 19 - QSGMII: port 3
+	 */
+	if (active_port >= NI_PORT_0 && active_port <= NI_PORT_3)
+		ge_port_phy_addr = active_port + 1;
+	else
+		ge_port_phy_addr = active_port - 2;
+#endif
+
+#if defined(CONFIG_TARGET_SATURN_ASIC)
+	/* internal GPHY addr=1 */
+	if (active_port == NI_PORT_3)
+		ge_port_phy_addr = 1;
+	else
+		ge_port_phy_addr = active_port + 1;
+
+	printf("%s: active_port=%d, ge_port_phy_addr=%d\n",
+	       __func__, active_port, ge_port_phy_addr);
+#endif
+
+	ca_mdio_read(ge_port_phy_addr, 2, &vendor_id);
+	ca_mdio_read(ge_port_phy_addr, 3, &chip_id);
+	phy_id = ((u32)vendor_id << 16) | chip_id;
+
+	printf("%s: vendor_id=0x%x\n", __func__, vendor_id);
+	printf("%s: chip_id=0x%x\n", __func__, chip_id);
+	printf("%s: phy_id=0x%x\n", __func__, phy_id);
+	printf("%s: phy_id & PHY_ID_MASK=0x%x\n",
+	       __func__, (phy_id & PHY_ID_MASK));
+	printf("%s: PHY_ID_RTL8214=0x%x\n", __func__, PHY_ID_RTL8214);
+
+	if ((phy_id & PHY_ID_MASK) == PHY_ID_RTL8211) {
+#if !defined(CONFIG_TARGET_VENUS)
+		printf("%s: do initial patch for PHY_ID_RTL8211\n", __func__);
+		/*
+		 * Disable response PHYAD=0 function of
+		 * RTL8211 series PHY
+		 */
+
+		/* REG31 write 0x0007, set to extension page */
+		ca_mdio_write(ge_port_phy_addr, 31, 0x0007);
+
+		/* REG30 write 0x002C, set to extension page 44 */
+		ca_mdio_write(ge_port_phy_addr, 30, 0x002C);
+
+		/*
+		 * REG27 write bit[2] =0
+		 * disable response PHYAD=0  function.
+		 * we should read REG27 and clear bit[2], and write back
+		 */
+		ca_mdio_read(ge_port_phy_addr, 27, &val);
+		val &= ~(1 << 2);
+		ca_mdio_write(ge_port_phy_addr, 27, val);
+
+		/* REG31 write 0X0000, back to page0 */
+		ca_mdio_write(ge_port_phy_addr, 31, 0x0000);
+#endif
+	}
+
+	/* the init sequency provided by RTK */
+	if ((phy_id & PHY_ID_MASK) == PHY_ID_RTL8214) {
+		printf("%s: write initial sequency for PHY_ID_RTL8214!!\n",
+		       __func__);
+		for (i = 0; i < RTL8214_INIT_REG_COUNT; i++) {
+			if (!(i & 1)) {
+				ca_mdio_write(ge_port_phy_addr, 29,
+					      rtl8214_init_reg_val[i]);
+			} else {
+				ca_mdio_write(ge_port_phy_addr, 30,
+					      rtl8214_init_reg_val[i]);
+			}
+		}
+	}
+
+#if defined(CONFIG_TARGET_PRESIDIO_ASIC) || defined(CONFIG_TARGET_VENUS)
+	if ((phy_id & PHY_ID_MASK) == PHY_ID_RTL8211_G3_ASIC) {
+		if (!auto_scan_active_port) {
+			printf("%s: write initial sequency for ", __func__);
+			printf("PHY_ID_RTL8211_G3_ASIC!!\n");
+
+			ca_ni_internal_phy_init();
+		}
+#if defined(CONFIG_TARGET_VENUS)
+		else
+			ca77xx_ni_auto_scan_active_port();
+#endif
+	}
+
+	ca77xx_ni_scan_phy_link();
+#endif
+
+	/* parsing ethaddr and set to NI registers. */
+	ni_setup_mac_addr();
+
+	/* the phy_read and phy_write
+	 * should meet the proto type of miiphy_register
+	 */
+#ifdef MIIPHY_REGISTER
+	miiphy_register(dev->name, ca_miiphy_read, ca_miiphy_write);
+#endif
+
+#if CORTINA_NI_DBG
+	dbg_dev = dev;
+#endif
+
+#if defined(CONFIG_TARGET_PRESIDIO_ASIC) || defined(CONFIG_TARGET_VENUS)
+	/* hardware settings for RGMII port */
+	{
+		union GLOBAL_GLOBAL_CONFIG_t	glb_config;
+		union GLOBAL_IO_DRIVE_CONTROL_t io_drive_control;
+
+		/* Generating 25Mhz reference clock for switch */
+		glb_config.wrd = CA_REG_READ((priv->glb_base_addr +
+					    GLOBAL_GLOBAL_CONFIG_OFFSET));
+		glb_config.bf.refclk_sel = 0x01;
+		glb_config.bf.ext_reset = 0x01;
+		CA_REG_WRITE(glb_config.wrd, (priv->glb_base_addr +
+			    GLOBAL_GLOBAL_CONFIG_OFFSET));
+
+		mdelay(20);
+
+		/* should do a external reset */
+		glb_config.wrd = CA_REG_READ((priv->glb_base_addr +
+					    GLOBAL_GLOBAL_CONFIG_OFFSET));
+		glb_config.bf.ext_reset = 0x0;
+		CA_REG_WRITE(glb_config.wrd, (priv->glb_base_addr +
+			    GLOBAL_GLOBAL_CONFIG_OFFSET));
+
+		io_drive_control.wrd =
+			CA_REG_READ((priv->glb_base_addr +
+				   GLOBAL_IO_DRIVE_CONTROL_OFFSET));
+		io_drive_control.bf.gmac_mode = 2;
+		io_drive_control.bf.gmac_dn = 1;
+		io_drive_control.bf.gmac_dp = 1;
+		CA_REG_WRITE(io_drive_control.wrd, (priv->glb_base_addr +
+			    GLOBAL_IO_DRIVE_CONTROL_OFFSET));
+	}
+
+	/* initialize LEDs for ethernet link and traffic lights */
+	//ca77xx_init_led();
+
+#if defined(CONFIG_TARGET_PRESIDIO_ASIC)
+	/* do internal gphy calibration */
+	do_internal_gphy_cal();
+#endif
+	return 0;
+#endif
+}
+
+#if CORTINA_NI_DBG
+DECLARE_GLOBAL_DATA_PTR;
+int do_eth_init(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
+{
+	cortina_ni_init(0x00);
+	return 0;
+}
+
+U_BOOT_CMD(do_eth_init, 2, 1, do_eth_init,
+	   "do_eth_init\t- to test eth_init\n",
+	   "None\n");
+#endif
+
+#if CORTINA_NI_DBG
+int do_eth_send(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
+{
+	unsigned char	pkt[1536];
+	unsigned int	i;
+
+	for (i = 0; i < 1500; i++)
+		pkt[i] = i % 256;
+
+	for (i = 60; i < 360; i++)
+		cortina_ni_send(dbg_dev, pkt, i);
+
+	return 0;
+}
+
+U_BOOT_CMD(do_eth_send, 3, 2, do_eth_send,
+	   "do_eth_send\t- to test eth_send\n",
+	   "None\n");
+
+int do_eth_rx(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
+{
+	cortina_ni_recv(dbg_dev);
+	return 0;
+}
+
+U_BOOT_CMD(do_eth_rx, 2, 1, do_eth_rx,
+	   "do_eth_rx\t- to test eth_rx\n",
+	   "None\n");
+
+int do_read_phy(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
+{
+	unsigned int	phy_adr;
+	unsigned int	reg_off;
+	unsigned short	reg_val;
+
+	phy_adr = simple_strtoul(argv[1], NULL, 0);
+	reg_off = simple_strtoul(argv[2], NULL, 0);
+	ca_mdio_read(phy_adr, reg_off, &reg_val);
+	printf("PHY_ADR = %d offset=%d reg_val=%x\n",
+	       phy_adr, reg_off, reg_val);
+	return 0;
+}
+
+U_BOOT_CMD(ca_phy_read, 3, 1, do_read_phy,
+	   "do_read_phy\t- to read PHY register\n",
+	   "None\n");
+
+int do_write_phy(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
+{
+	unsigned int	phy_adr;
+	unsigned int	reg_off;
+	unsigned int	reg_val;
+
+	phy_adr = simple_strtoul(argv[1], NULL, 0);
+	reg_off = simple_strtoul(argv[2], NULL, 0);
+	reg_val = simple_strtoul(argv[3], NULL, 0);
+	ca_mdio_write(phy_adr, reg_off, reg_val);
+	printf("PHY_ADR = %d offset=%d reg_val=%x\n",
+	       phy_adr, reg_off, reg_val);
+	return 0;
+}
+
+U_BOOT_CMD(ca_phy_write, 4, 1, do_write_phy,
+	   "do_write_phy\t- to write PHY register\n",
+	   "None\n");
+
+#endif
+
+static int do_phy_reg(struct cmd_tbl *cmdtp,
+		      int flag,
+		      int argc,
+		      char * const argv[])
+{
+	int ret, i;
+	u16 phy_addr, reg, val;
+
+	if (argc < 2) {
+		printf("Usage:\nphy_reg_value%s\n", cmdtp->help);
+		return -1;
+	}
+
+	phy_addr = simple_strtoul(argv[1], NULL, 10);
+
+	if (phy_addr > 31) {
+		printf("Usage:\nphy_reg_value%s\n", cmdtp->help);
+		return -1;
+	}
+	if (argc == 2) {
+		/* read the first 15 registers of the PHY */
+		printf("PHY addr %d:\n", phy_addr);
+		for (i = 0; i < 15; i++) {
+			ca_mdio_read(phy_addr, i, &val);
+			printf("Reg 0x%04X = 0x%04X\n", i, val);
+		}
+		return 0;
+	}
+
+	reg = simple_strtoul(argv[2], NULL, 10);
+
+	if (argc == 3) {
+		/* read cmd */
+		ca_mdio_read(phy_addr, reg, &val);
+		printf("PHY addr %d Reg 0x%04X = 0x%04X\n", phy_addr, reg, val);
+	} else {
+		/* argc > 3*/
+		/* write cmd */
+		val = simple_strtoul(argv[3], NULL, 10);
+		ret = ca_mdio_write(phy_addr, reg, val);
+		if (!ret) {
+			printf("PHY addr %d Reg 0x%04X = 0x%04X\n",
+			       phy_addr, reg, val);
+		} else {
+			printf("Can't write PHY addr %d Reg 0x%04X as 0x%04X, ",
+			       phy_addr, reg, val);
+			printf("ret = %d\n", ret);
+		}
+	}
+	return 0;
+}
+
+U_BOOT_CMD(phy_reg, 4, 1, do_phy_reg,
+	   "read/write PHY register",
+	   "[PHY addr] [reg_valueaddr] ([value])\n"
+	   "PHY addr : 0-31\n");
+
+#ifdef CONFIG_MK_CUSTOMB
+/* code custom switch register access function here */
+#endif
+
+static int cortina_eth_start(struct udevice *dev)
+{
+	return cortina_ni_init(dev);
+}
+
+int cortina_eth_send(struct udevice *dev, void *packet, int length)
+{
+	return cortina_ni_send(dev, packet, length);
+}
+
+int cortina_eth_recv(struct udevice *dev, int flags, uchar **packetp)
+{
+	return cortina_ni_recv(dev);
+}
+
+void cortina_eth_stop(struct udevice *dev)
+{
+	cortina_ni_halt(dev);
+}
+
+static int cortina_eth_probe(struct udevice *dev)
+{
+	return ca77xx_eth_initialize(dev);
+}
+
+static int ca_ni_ofdata_to_platdata(struct udevice *dev)
+{
+	struct cortina_ni_priv *priv = dev_get_priv(dev);
+
+	priv->glb_base_addr = dev_remap_addr_index(dev, 0);
+	if (!priv->glb_base_addr)
+		return -ENOENT;
+	printf("%s: priv->glb_base_addr for index 0 is 0x%p\n",
+	       __func__, priv->glb_base_addr);
+
+	priv->per_mdio_base_addr = dev_remap_addr_index(dev, 1);
+	if (!priv->per_mdio_base_addr)
+		return -ENOENT;
+	printf("%s: priv->per_mdio_base_addr for index 1 is 0x%p\n",
+	       __func__, priv->per_mdio_base_addr);
+
+	priv->ni_hv_base_addr = dev_remap_addr_index(dev, 2);
+	if (!priv->ni_hv_base_addr)
+		return -ENOENT;
+	printf("%s: priv->ni_hv_base_addr for index 2 is 0x%p\n",
+	       __func__, priv->ni_hv_base_addr);
+
+	return 0;
+}
+
+static const struct eth_ops cortina_eth_ops = {
+	.start  = cortina_eth_start,
+	.send   = cortina_eth_send,
+	.recv   = cortina_eth_recv,
+	.stop   = cortina_eth_stop,
+};
+
+static const struct udevice_id cortina_eth_ids[] = {
+	{ .compatible = "eth_cortina" },
+	{ }
+};
+
+U_BOOT_DRIVER(eth_cortina) = {
+	.name   = "eth_cortina",
+	.id     = UCLASS_ETH,
+	.of_match = cortina_eth_ids,
+	.probe  = cortina_eth_probe,
+	.ops    = &cortina_eth_ops,
+	.priv_auto_alloc_size = sizeof(struct cortina_ni_priv),
+	.platdata_auto_alloc_size = sizeof(struct eth_pdata),
+	.ofdata_to_platdata = ca_ni_ofdata_to_platdata,
+};
diff --git a/drivers/net/cortina_ni.h b/drivers/net/cortina_ni.h
new file mode 100644
index 0000000..0fea6a1
--- /dev/null
+++ b/drivers/net/cortina_ni.h
@@ -0,0 +1,592 @@ 
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+/*
+ * Copyright (C) 2020 Cortina Access Inc.
+ * Author: Aaron Tseng <aaron.tseng at cortina-access.com>
+ *
+ * Ethernet MAC Driver for all supported CAxxxx SoCs
+ */
+
+#ifndef __CORTINA_NI_H
+#define __CORTINA_NI_H
+
+#include <asm/types.h>
+#include <asm/io.h>
+#include <config.h>
+
+//#define CORTINA_NI_DBG				1
+
+#ifdef CS_BIG_ENDIAN
+#define CRCPOLY_BE					0x04c11db7
+#else /* CS_LITTLE_ENDIAN */
+#define CRCPOLY_LE					0xedb88320
+#endif
+
+#define GE_PORT0_PHY_ADDR				CONFIG_NI_PHY_ADDR_GMAC0
+#define GE_PORT1_PHY_ADDR				CONFIG_NI_PHY_ADDR_GMAC1
+#define GE_PORT2_PHY_ADDR				CONFIG_NI_PHY_ADDR_GMAC2
+
+#define PHY_ID_RTL8201					0x001cc810
+#define PHY_ID_RTL8211					0x001cc910
+#define PHY_ID_RTL8214					0x001cc940
+#define PHY_ID_RTL8211_G3_ASIC				0x001cc980
+#define PHY_ID_RTL8211_SATURN_ASIC			0x001cc900
+#define PHY_ID_QCA8337					0x004dd035
+#define PHY_ID_MASK					0xFFFFFFF0
+
+#define GE_MAC_INTF_GMII                                0x0
+#define GE_MAC_INTF_MII                                 0x1
+#define GE_MAC_INTF_RGMII_1000                          0x2
+#define GE_MAC_INTF_RGMII_100                           0x3
+#define GE_MAC_INTF_QSGMII_1000                         0x4
+#define GE_MAC_INTF_RMII                                0x5
+
+#define NI_TOP_NI_RTH_MAC_10M				1
+#define NI_TOP_NI_RTH_MAC_100M				0
+#define NI_TOP_NI_RTH_MAC_HALF				1
+#define NI_TOP_NI_RTH_MAC_FULL				0
+
+/* Defines the base and top address in CPU XRA
+ * for packets to cpu instance 0
+ * 0x300 * 8-byte = 6K-byte
+ */
+#define RX_TOP_ADDR					0x02FF
+#define RX_BASE_ADDR					0x0000
+
+/* Defines the base and top address in CPU XRAM
+ * for packets from cpu instance 0.
+ * 0x100 * 8-byte = 2K-byte
+ */
+#define TX_TOP_ADDR					0x03FF
+#define TX_BASE_ADDR					0x0300
+
+#define RX_0_CPU_PKT_DIS				BIT(0)
+#define TX_0_CPU_PKT_DIS				BIT(9)
+
+#define PHY_POLL_TIMES					0x200
+
+#define NI_XRAM_BASE                    0xF4500000
+
+enum ca_status_t {
+	CA_E_ERROR          = -1,
+	CA_E_OK             = 0x0,
+	CA_E_RESOURCE       = 0x1,
+	CA_E_PARAM          = 0x2,
+	CA_E_NOT_FOUND      = 0x3,
+	CA_E_CONFLICT       = 0x4,
+	CA_E_TIMEOUT        = 0x5,
+	CA_E_INTERNAL       = 0x6,
+	CA_E_NOT_SUPPORT    = 0x7,
+	CA_E_CONFIG         = 0x8,
+	CA_E_UNAVAIL        = 0x9,
+	CA_E_MEMORY         = 0xa,
+	CA_E_BUSY           = 0xb,
+	CA_E_FULL           = 0xc,
+	CA_E_EMPTY          = 0xd,
+	CA_E_EXISTS         = 0xe,
+	CA_E_DEV            = 0xf,
+	CA_E_PORT           = 0x10,
+	CA_E_LLID           = 0x11,
+	CA_E_VLAN           = 0x12,
+	CA_E_INIT           = 0x13,
+	CA_E_INTF           = 0x14,
+	CA_E_NEXTHOP        = 0x15,
+	CA_E_ROUTE          = 0x16,
+	CA_E_DB_CHANGED     = 0x17,
+	CA_E_INACTIVE       = 0x18,
+	CA_E_ALREADY_SET    = 0x19,
+};
+
+#ifndef CA_IN
+#define CA_IN
+#endif
+
+#ifndef CA_OUT
+#define CA_OUT
+#endif
+
+#if !defined(__ASSEMBLER__) && !defined(__ASSEMBLY__)
+struct cortina_ni_priv {
+	unsigned int	rx_xram_base_adr;
+	unsigned int	rx_xram_end_adr;
+	unsigned short  rx_xram_start;
+	unsigned short  rx_xram_end;
+	unsigned int	tx_xram_base_adr;
+	unsigned int	tx_xram_end_adr;
+	unsigned short  tx_xram_start;
+	unsigned short  tx_xram_end;
+#ifdef CONFIG_DM_ETH
+	void __iomem    *glb_base_addr;
+	void __iomem    *per_mdio_base_addr;
+	void __iomem    *ni_hv_base_addr;
+#else
+	unsigned int    glb_base_addr;
+	unsigned int    per_mdio_base_addr;
+	unsigned int    ni_hv_base_addr;
+#endif
+};
+
+union NI_HEADER_X_T {
+	struct {
+		unsigned int next_link		: 10; /* bits  9: 0 */
+		unsigned int bytes_valid		:  4; /* bits 13:10 */
+		unsigned int reserved		: 16; /* bits 29:14 */
+		unsigned int hdr_a			:  1; /* bits 30:30 */
+		unsigned int ownership		:  1; /* bits 31:31 */
+	} bf;
+	unsigned int     wrd;
+};
+
+union NI_PACKET_STATUS {
+	struct {
+		unsigned int packet_size       : 14; /* bits 13:0 */
+		unsigned int byte_valid        :  4; /* bits 17:14 */
+		unsigned int pfc               :  1; /* bits 18:18 */
+		unsigned int valid             :  1; /* bits 19:19 */
+		unsigned int drop              :  1; /* bits 20:20 */
+		unsigned int runt              :  1; /* bits 21:21 */
+		unsigned int oversize          :  1; /* bits 22:22 */
+		unsigned int jumbo             :  1; /* bits 23:23 */
+		unsigned int link_status       :  1; /* bits 24:24 */
+		unsigned int jabber            :  1; /* bits 25:25 */
+		unsigned int crc_error         :  1; /* bits 26:26 */
+		unsigned int pause             :  1; /* bits 27:27 */
+		unsigned int oam               :  1; /* bits 28:28 */
+		unsigned int unknown_opcode    :  1; /* bits 29:29 */
+		unsigned int multicast         :  1; /* bits 30:30 */
+		unsigned int broadcast         :  1; /* bits 31:31 */
+	} bf;
+	unsigned int     wrd;
+};
+
+#if defined(CONFIG_TARGET_PRESIDIO_ASIC) || \
+	defined(CONFIG_TARGET_SATURN_ASIC) || defined(CONFIG_TARGET_VENUS)
+
+#if defined(CONFIG_TARGET_PRESIDIO_ASIC)
+#define CA_NI_MDIO_REG_BASE				0xF4338
+#else
+
+#if defined(CONFIG_TARGET_VENUS)
+#define CA_NI_MDIO_REG_BASE				0xF4339
+#else
+#define CA_NI_MDIO_REG_BASE				0xD000B
+#endif
+
+#endif
+
+union NI_MDIO_OPER_T {
+	struct {
+		unsigned int reserved       : 2; /* bits  1:0 */
+		unsigned int reg_off        : 5; /* bits  6:2 */
+		unsigned int phy_addr       : 5; /* bits 11:7 */
+		unsigned int reg_base       : 20; /* bits 31:12 */
+	} bf;
+	unsigned int     wrd;
+};
+
+#endif
+
+#define NI_PORT_0					0
+#define NI_PORT_1					1
+#define NI_PORT_2					2
+#define NI_PORT_3					3
+#define NI_PORT_4					4
+#define NI_PORT_5					5
+#define NI_PORT_6					6
+#define NI_PORT_7					7
+
+#if defined(CONFIG_TARGET_SATURN_ASIC)
+#define NI_READ_POLL_COUNT				1000000
+#else
+#define NI_READ_POLL_COUNT				1000
+#endif
+
+#define __MDIO_WR_FLAG				(0)
+#define __MDIO_RD_FLAG				(1)
+#define __MDIO_ACCESS_TIMEOUT			(1000000)
+#define __MDIO_PER_CLK				(62500)
+#define CA_MDIO_ADDR_MIN			(1)
+#define CA_MDIO_ADDR_MAX			(31)
+#define CA_MDIO_CLOCK_MIN			(1)
+#define CA_MDIO_CLOCK_MAX			(20000)
+
+#endif /* !__ASSEMBLER__ */
+
+/* Copy from registers.h */
+union NI_HV_GLB_MAC_ADDR_CFG0_t {
+	struct {
+		unsigned int mac_addr0            : 32; /* bits 31:0 */
+	} bf;
+	unsigned int     wrd;
+};
+
+union NI_HV_GLB_MAC_ADDR_CFG1_t {
+	struct {
+		unsigned int mac_addr1            :  8; /* bits 7:0 */
+		unsigned int rsrvd1               : 24;
+	} bf;
+	unsigned int     wrd;
+};
+
+union NI_HV_PT_PORT_STATIC_CFG_t {
+	struct {
+		unsigned int int_cfg              :  4; /* bits 3:0 */
+		unsigned int phy_mode             :  1; /* bits 4:4 */
+		unsigned int rmii_clksrc          :  1; /* bits 5:5 */
+		unsigned int inv_clk_in           :  1; /* bits 6:6 */
+		unsigned int inv_clk_out          :  1; /* bits 7:7 */
+		unsigned int inv_rxclk_out        :  1; /* bits 8:8 */
+		unsigned int tx_use_gefifo        :  1; /* bits 9:9 */
+		unsigned int smii_tx_stat         :  1; /* bits 10:10 */
+		unsigned int crs_polarity         :  1; /* bits 11:11 */
+		unsigned int lpbk_mode            :  2; /* bits 13:12 */
+		unsigned int gmii_like_half_duplex_en :  1; /* bits 14:14 */
+		unsigned int sup_tx_to_rx_lpbk_data :  1; /* bits 15:15 */
+		unsigned int rsrvd1               :  8;
+		unsigned int mac_addr6            :  8; /* bits 31:24 */
+	} bf;
+	unsigned int     wrd;
+};
+
+union NI_HV_XRAM_CPUXRAM_CFG_t {
+	struct {
+		unsigned int rx_0_cpu_pkt_dis     :  1; /* bits 0:0 */
+		unsigned int rsrvd1               :  8;
+		unsigned int tx_0_cpu_pkt_dis     :  1; /* bits 9:9 */
+		unsigned int rsrvd2               :  1;
+		unsigned int rx_x_drop_err_pkt    :  1; /* bits 11:11 */
+		unsigned int xram_mgmt_dis_drop_ovsz_pkt :  1; /* bits 12:12 */
+		unsigned int xram_mgmt_term_large_pkt :  1; /* bits 13:13 */
+		unsigned int xram_mgmt_promisc_mode :  2; /* bits 15:14 */
+		unsigned int xram_cntr_debug_mode :  1; /* bits 16:16 */
+		unsigned int xram_cntr_op_code    :  2; /* bits 18:17 */
+		unsigned int rsrvd3               :  2;
+		unsigned int xram_rx_mgmtfifo_srst :  1; /* bits 21:21 */
+		unsigned int xram_dma_fifo_srst   :  1; /* bits 22:22 */
+		unsigned int rsrvd4               :  9;
+	} bf;
+	unsigned int     wrd;
+};
+
+union NI_HV_PT_RXMAC_CFG_t {
+	struct {
+		unsigned int rx_en                :  1; /* bits 0:0 */
+		unsigned int rsrvd1               :  7;
+		unsigned int rx_flow_disable      :  1; /* bits 8:8 */
+		unsigned int rsrvd2               :  3;
+		unsigned int rx_flow_to_tx_en     :  1; /* bits 12:12 */
+		unsigned int rx_pfc_disable       :  1; /* bits 13:13 */
+		unsigned int rsrvd3               : 15;
+		unsigned int send_pg_data         :  1; /* bits 29:29 */
+		unsigned int rsrvd4               :  2;
+	} bf;
+	unsigned int     wrd;
+};
+
+union NI_HV_PT_TXMAC_CFG_t {
+	struct {
+		unsigned int tx_en                :  1; /* bits 0:0 */
+		unsigned int rsrvd1               :  7;
+		unsigned int mac_crc_calc_en      :  1; /* bits 8:8 */
+		unsigned int tx_ipg_sel           :  3; /* bits 11:9 */
+		unsigned int tx_flow_disable      :  1; /* bits 12:12 */
+		unsigned int tx_drain             :  1; /* bits 13:13 */
+		unsigned int tx_pfc_disable       :  1; /* bits 14:14 */
+		unsigned int tx_pau_sel           :  2; /* bits 16:15 */
+		unsigned int rsrvd2               :  9;
+		unsigned int tx_auto_xon          :  1; /* bits 26:26 */
+		unsigned int rsrvd3               :  1;
+		unsigned int pass_thru_hdr        :  1; /* bits 28:28 */
+		unsigned int rsrvd4               :  3;
+	} bf;
+	unsigned int     wrd;
+};
+
+union NI_HV_GLB_INTF_RST_CONFIG_t {
+	struct {
+		unsigned int intf_rst_p0          :  1; /* bits 0:0 */
+		unsigned int intf_rst_p1          :  1; /* bits 1:1 */
+		unsigned int intf_rst_p2          :  1; /* bits 2:2 */
+		unsigned int intf_rst_p3          :  1; /* bits 3:3 */
+		unsigned int intf_rst_p4          :  1; /* bits 4:4 */
+		unsigned int mac_rx_rst_p0        :  1; /* bits 5:5 */
+		unsigned int mac_rx_rst_p1        :  1; /* bits 6:6 */
+		unsigned int mac_rx_rst_p2        :  1; /* bits 7:7 */
+		unsigned int mac_rx_rst_p3        :  1; /* bits 8:8 */
+		unsigned int mac_rx_rst_p4        :  1; /* bits 9:9 */
+		unsigned int mac_tx_rst_p0        :  1; /* bits 10:10 */
+		unsigned int mac_tx_rst_p1        :  1; /* bits 11:11 */
+		unsigned int mac_tx_rst_p2        :  1; /* bits 12:12 */
+		unsigned int mac_tx_rst_p3        :  1; /* bits 13:13 */
+		unsigned int mac_tx_rst_p4        :  1; /* bits 14:14 */
+		unsigned int port_rst_p5          :  1; /* bits 15:15 */
+		unsigned int pcs_rst_p6           :  1; /* bits 16:16 */
+		unsigned int pcs_rst_p7           :  1; /* bits 17:17 */
+		unsigned int mac_rst_p6           :  1; /* bits 18:18 */
+		unsigned int mac_rst_p7           :  1; /* bits 19:19 */
+		unsigned int rsrvd1               : 12;
+	} bf;
+	unsigned int     wrd;
+};
+
+union NI_HV_GLB_STATIC_CFG_t {
+	struct {
+		unsigned int port_to_cpu          :  4; /* bits 3:0 */
+		unsigned int mgmt_pt_to_fe_also   :  1; /* bits 4:4 */
+		unsigned int txcrc_chk_en         :  1; /* bits 5:5 */
+		unsigned int p4_rgmii_tx_clk_phase :  2; /* bits 7:6 */
+		unsigned int p4_rgmii_tx_data_order :  1; /* bits 8:8 */
+		unsigned int rsrvd1               :  7;
+		unsigned int rxmib_mode           :  1; /* bits 16:16 */
+		unsigned int txmib_mode           :  1; /* bits 17:17 */
+		unsigned int eth_sch_rdy_pkt      :  1; /* bits 18:18 */
+		unsigned int rsrvd2               :  1;
+		unsigned int rxaui_mode           :  2; /* bits 21:20 */
+		unsigned int rxaui_sigdet         :  2; /* bits 23:22 */
+		unsigned int cnt_op_mode          :  3; /* bits 26:24 */
+		unsigned int rsrvd3               :  5;
+	} bf;
+	unsigned int     wrd;
+};
+
+union GLOBAL_BLOCK_RESET_t {
+	struct {
+		unsigned int reset_ni             :  1; /* bits 0:0 */
+		unsigned int reset_l2fe           :  1; /* bits 1:1 */
+		unsigned int reset_l2tm           :  1; /* bits 2:2 */
+		unsigned int reset_l3fe           :  1; /* bits 3:3 */
+		unsigned int reset_sdram          :  1; /* bits 4:4 */
+		unsigned int reset_tqm            :  1; /* bits 5:5 */
+		unsigned int reset_pcie0          :  1; /* bits 6:6 */
+		unsigned int reset_pcie1          :  1; /* bits 7:7 */
+		unsigned int reset_pcie2          :  1; /* bits 8:8 */
+		unsigned int reset_sata           :  1; /* bits 9:9 */
+		unsigned int reset_gic400         :  1; /* bits 10:10 */
+		unsigned int rsrvd1               :  2;
+		unsigned int reset_usb            :  1; /* bits 13:13 */
+		unsigned int reset_flash          :  1; /* bits 14:14 */
+		unsigned int reset_per            :  1; /* bits 15:15 */
+		unsigned int reset_dma            :  1; /* bits 16:16 */
+		unsigned int reset_rtc            :  1; /* bits 17:17 */
+		unsigned int reset_pe0            :  1; /* bits 18:18 */
+		unsigned int reset_pe1            :  1; /* bits 19:19 */
+		unsigned int reset_rcpu0          :  1; /* bits 20:20 */
+		unsigned int reset_rcpu1          :  1; /* bits 21:21 */
+		unsigned int reset_sadb           :  1; /* bits 22:22 */
+		unsigned int rsrvd2               :  1;
+		unsigned int reset_rcrypto        :  1; /* bits 24:24 */
+		unsigned int reset_ldma           :  1; /* bits 25:25 */
+		unsigned int reset_fbm            :  1; /* bits 26:26 */
+		unsigned int reset_eaxi           :  1; /* bits 27:27 */
+		unsigned int reset_sd             :  1; /* bits 28:28 */
+		unsigned int reset_otprom         :  1; /* bits 29:29 */
+		unsigned int rsrvd3               :  2;
+	} bf;
+	unsigned int     wrd;
+};
+
+union PER_MDIO_ADDR_t {
+	struct {
+		unsigned int mdio_addr            :  5; /* bits 4:0 */
+		unsigned int rsrvd1               :  3;
+		unsigned int mdio_offset          :  5; /* bits 12:8 */
+		unsigned int rsrvd2               :  2;
+		unsigned int mdio_rd_wr           :  1; /* bits 15:15 */
+		unsigned int mdio_st              :  1; /* bits 16:16 */
+		unsigned int rsrvd3               :  1;
+		unsigned int mdio_op              :  2; /* bits 19:18 */
+		unsigned int rsrvd4               : 12;
+	} bf;
+	unsigned int     wrd;
+};
+
+union PER_MDIO_CTRL_t {
+	struct {
+		unsigned int mdiodone             :  1; /* bits 0:0 */
+		unsigned int rsrvd1               :  6;
+		unsigned int mdiostart            :  1; /* bits 7:7 */
+		unsigned int rsrvd2               : 24;
+	} bf;
+	unsigned int     wrd;
+};
+
+union PER_MDIO_RDDATA_t {
+	struct {
+		unsigned int mdio_rddata          : 16; /* bits 15:0 */
+		unsigned int rsrvd1               : 16;
+	} bf;
+	unsigned int     wrd;
+};
+
+/*
+ * XRAM
+ */
+
+union NI_HV_XRAM_CPUXRAM_ADRCFG_RX_t {
+	struct {
+		unsigned int rx_base_addr         : 10; /* bits 9:0 */
+		unsigned int rsrvd1               :  6;
+		unsigned int rx_top_addr          : 10; /* bits 25:16 */
+		unsigned int rsrvd2               :  6;
+	} bf;
+	unsigned int     wrd;
+};
+
+union NI_HV_XRAM_CPUXRAM_ADRCFG_TX_0_t {
+	struct {
+		unsigned int tx_base_addr         : 10; /* bits 9:0 */
+		unsigned int rsrvd1               :  6;
+		unsigned int tx_top_addr          : 10; /* bits 25:16 */
+		unsigned int rsrvd2               :  6;
+	} bf;
+	unsigned int     wrd;
+};
+
+union NI_HV_XRAM_CPUXRAM_CPU_STA_RX_0_t {
+	struct {
+		unsigned int pkt_wr_ptr           : 10; /* bits 9:0 */
+		unsigned int rsrvd1               :  5;
+		unsigned int int_colsc_thresh_reached :  1; /* bits 15:15 */
+		unsigned int rsrvd2               : 16;
+	} bf;
+	unsigned int     wrd;
+};
+
+union NI_HV_XRAM_CPUXRAM_CPU_CFG_RX_0_t {
+	struct {
+		unsigned int pkt_rd_ptr           : 10; /* bits 9:0 */
+		unsigned int rsrvd1               : 22;
+	} bf;
+	unsigned int     wrd;
+};
+
+union NI_HV_XRAM_CPUXRAM_CPU_CFG_TX_0_t {
+	struct {
+		unsigned int pkt_wr_ptr           : 10; /* bits 9:0 */
+		unsigned int rsrvd1               : 22;
+	} bf;
+	unsigned int     wrd;
+};
+
+union GLOBAL_GLOBAL_CONFIG_t {
+	struct {
+		unsigned int rsrvd1               :  4;
+		unsigned int wd_reset_subsys_enable :  1; /* bits 4:4 */
+		unsigned int rsrvd2               :  1;
+		unsigned int wd_reset_all_blocks  :  1; /* bits 6:6 */
+		unsigned int wd_reset_remap       :  1; /* bits 7:7 */
+		unsigned int wd_reset_ext_reset   :  1; /* bits 8:8 */
+		unsigned int ext_reset            :  1; /* bits 9:9 */
+		unsigned int cfg_pcie_0_clken     :  1; /* bits 10:10 */
+		unsigned int cfg_sata_clken       :  1; /* bits 11:11 */
+		unsigned int cfg_pcie_1_clken     :  1; /* bits 12:12 */
+		unsigned int rsrvd3               :  1;
+		unsigned int cfg_pcie_2_clken     :  1; /* bits 14:14 */
+		unsigned int rsrvd4               :  2;
+		unsigned int ext_eth_refclk       :  1; /* bits 17:17 */
+		unsigned int refclk_sel           :  2; /* bits 19:18 */
+		unsigned int rsrvd5               :  7;
+		unsigned int l3fe_pd              :  1; /* bits 27:27 */
+		unsigned int offload0_pd          :  1; /* bits 28:28 */
+		unsigned int offload1_pd          :  1; /* bits 29:29 */
+		unsigned int crypto_pd            :  1; /* bits 30:30 */
+		unsigned int core_pd              :  1; /* bits 31:31 */
+	} bf;
+	unsigned int     wrd;
+};
+
+union GLOBAL_IO_DRIVE_CONTROL_t {
+	struct {
+		unsigned int gmac_dp              :  3; /* bits 2:0 */
+		unsigned int gmac_dn              :  3; /* bits 5:3 */
+		unsigned int gmac_mode            :  2; /* bits 7:6 */
+		unsigned int gmac_ds              :  1; /* bits 8:8 */
+		unsigned int flash_ds             :  1; /* bits 9:9 */
+		unsigned int nu_ds                :  1; /* bits 10:10 */
+		unsigned int ssp_ds               :  1; /* bits 11:11 */
+		unsigned int spi_ds               :  1; /* bits 12:12 */
+		unsigned int gpio_ds              :  1; /* bits 13:13 */
+		unsigned int misc_ds              :  1; /* bits 14:14 */
+		unsigned int eaxi_ds              :  1; /* bits 15:15 */
+		unsigned int sd_ds                :  8; /* bits 23:16 */
+		unsigned int rsrvd1               :  8;
+	} bf;
+	unsigned int     wrd;
+};
+
+union NI_HV_GLB_INIT_DONE_t {
+	struct {
+		unsigned int rsrvd1               :  1;
+		unsigned int ni_init_done         :  1; /* bits 1:1 */
+		unsigned int rsrvd2               : 30;
+	} bf;
+	unsigned int     wrd;
+};
+
+union NI_HV_PT_PORT_GLB_CFG_t {
+	struct {
+		unsigned int speed                :  1; /* bits 0:0 */
+		unsigned int duplex               :  1; /* bits 1:1 */
+		unsigned int link_status          :  1; /* bits 2:2 */
+		unsigned int link_stat_mask       :  1; /* bits 3:3 */
+		unsigned int rsrvd1               :  7;
+		unsigned int power_dwn_rx         :  1; /* bits 11:11 */
+		unsigned int power_dwn_tx         :  1; /* bits 12:12 */
+		unsigned int tx_intf_lp_time      :  1; /* bits 13:13 */
+		unsigned int rsrvd2               : 18;
+	} bf;
+	unsigned int     wrd;
+};
+
+#if defined(CONFIG_TARGET_PRESIDIO_ASIC)
+#define NI_HV_GLB_MAC_ADDR_CFG0_OFFSET                  0x010
+#define NI_HV_GLB_MAC_ADDR_CFG1_OFFSET                  0x014
+#define NI_HV_PT_BASE					0x400
+#define NI_HV_XRAM_BASE					0x820
+#define GLOBAL_BLOCK_RESET_OFFSET                       0x04
+#define GLOBAL_GLOBAL_CONFIG_OFFSET                     0x20
+#define GLOBAL_IO_DRIVE_CONTROL_OFFSET                  0x4c
+#elif defined(CONFIG_TARGET_SATURN_ASIC)
+#define NI_HV_GLB_MAC_ADDR_CFG0_OFFSET                  0x010
+#define NI_HV_GLB_MAC_ADDR_CFG1_OFFSET                  0x014
+#define NI_HV_PT_BASE					0x580
+#define NI_HV_XRAM_BASE					0xA80
+#define GLOBAL_BLOCK_RESET_OFFSET                       0x28
+#define GLOBAL_GLOBAL_CONFIG_OFFSET                     0x48
+#define GLOBAL_IO_DRIVE_CONTROL_OFFSET                  0x54
+#elif defined(CONFIG_TARGET_VENUS)
+#define NI_HV_GLB_MAC_ADDR_CFG0_OFFSET                  0x014
+#define NI_HV_GLB_MAC_ADDR_CFG1_OFFSET                  0x018
+#define NI_HV_PT_BASE					0x600
+#define NI_HV_XRAM_BASE					0xA20
+#define GLOBAL_BLOCK_RESET_OFFSET                       0x28
+#define GLOBAL_GLOBAL_CONFIG_OFFSET                     0x48
+#define GLOBAL_IO_DRIVE_CONTROL_OFFSET                  0x7c
+#endif
+
+#define NI_HV_GLB_INIT_DONE_OFFSET                      0x004
+#define NI_HV_GLB_INTF_RST_CONFIG_OFFSET                0x008
+#define NI_HV_GLB_STATIC_CFG_OFFSET                     0x00c
+
+#define NI_HV_PT_PORT_STATIC_CFG_OFFSET                 NI_HV_PT_BASE
+#define NI_HV_PT_PORT_GLB_CFG_OFFSET                    (0x4 + NI_HV_PT_BASE)
+#define NI_HV_PT_RXMAC_CFG_OFFSET                       (0x8 + NI_HV_PT_BASE)
+#define NI_HV_PT_TXMAC_CFG_OFFSET                       (0x14 + NI_HV_PT_BASE)
+
+#define NI_HV_XRAM_CPUXRAM_ADRCFG_RX_OFFSET             NI_HV_XRAM_BASE
+#define NI_HV_XRAM_CPUXRAM_ADRCFG_TX_0_OFFSET           (0x4 + NI_HV_XRAM_BASE)
+#define NI_HV_XRAM_CPUXRAM_CFG_OFFSET                   (0x8 + NI_HV_XRAM_BASE)
+#define NI_HV_XRAM_CPUXRAM_CPU_CFG_RX_0_OFFSET          (0xc + NI_HV_XRAM_BASE)
+#define NI_HV_XRAM_CPUXRAM_CPU_STA_RX_0_OFFSET          (0x10 + NI_HV_XRAM_BASE)
+#define NI_HV_XRAM_CPUXRAM_CPU_CFG_TX_0_OFFSET          (0x24 + NI_HV_XRAM_BASE)
+#define NI_HV_XRAM_CPUXRAM_CPU_STAT_TX_0_OFFSET         (0x28 + NI_HV_XRAM_BASE)
+
+#define PER_MDIO_CFG_OFFSET                             0x00
+#define PER_MDIO_ADDR_OFFSET                            0x04
+#define PER_MDIO_WRDATA_OFFSET                          0x08
+#define PER_MDIO_RDDATA_OFFSET                          0x0C
+#define PER_MDIO_CTRL_OFFSET                            0x10
+
+#define APB0_NI_HV_PT_STRIDE				160
+
+#endif /* __CORTINA_NI_H */