diff mbox series

[v5,net-next,5/9] enetc: Make MDIO accessors more generic and export to include/linux/fsl

Message ID 20200106013417.12154-6-olteanv@gmail.com
State Accepted
Delegated to: David Miller
Headers show
Series Convert Felix DSA switch to PHYLINK | expand

Commit Message

Vladimir Oltean Jan. 6, 2020, 1:34 a.m. UTC
From: Claudiu Manoil <claudiu.manoil@nxp.com>

Within the LS1028A SoC, the register map for the ENETC MDIO controller
is instantiated a few times: for the central (external) MDIO controller,
for the internal bus of each standalone ENETC port, and for the internal
bus of the Felix switch.

Refactoring is needed to support multiple MDIO buses from multiple
drivers. The enetc_hw structure is made an opaque type and a smaller
enetc_mdio_priv is created.

'mdio_base' - MDIO registers base address - is being parameterized, to
be able to work with different MDIO register bases.

The ENETC MDIO bus operations are exported from the fsl-enetc-mdio
kernel object, the same that registers the central MDIO controller (the
dedicated PF). The ENETC main driver has been changed to select it, and
use its exported helpers to further register its private MDIO bus. The
DSA Felix driver will do the same.

Signed-off-by: Claudiu Manoil <claudiu.manoil@nxp.com>
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
Changes in v5:
- None.

Changes in v4:
- None.

Changes in v3:
- Removed enetc_mdio.o from fsl-enetc.o, which caused a build error in
  the previous series, because enetc_mdio_read and enetc_mdio_write were
  exported from 2 different kernel objects.
- Some associated code movement from enetc_mdio.c to enetc_pf.c

 drivers/net/ethernet/freescale/enetc/Kconfig  |   1 +
 drivers/net/ethernet/freescale/enetc/Makefile |   2 +-
 .../net/ethernet/freescale/enetc/enetc_hw.h   |   1 +
 .../net/ethernet/freescale/enetc/enetc_mdio.c | 110 +++++++-----------
 .../net/ethernet/freescale/enetc/enetc_mdio.h |  12 --
 .../ethernet/freescale/enetc/enetc_pci_mdio.c |  43 ++++---
 .../net/ethernet/freescale/enetc/enetc_pf.c   |  47 ++++++++
 .../net/ethernet/freescale/enetc/enetc_pf.h   |   4 -
 include/linux/fsl/enetc_mdio.h                |  55 +++++++++
 9 files changed, 176 insertions(+), 99 deletions(-)
 delete mode 100644 drivers/net/ethernet/freescale/enetc/enetc_mdio.h
 create mode 100644 include/linux/fsl/enetc_mdio.h

Comments

Florian Fainelli Jan. 6, 2020, 7:35 p.m. UTC | #1
On 1/5/20 5:34 PM, Vladimir Oltean wrote:
> From: Claudiu Manoil <claudiu.manoil@nxp.com>
> 
> Within the LS1028A SoC, the register map for the ENETC MDIO controller
> is instantiated a few times: for the central (external) MDIO controller,
> for the internal bus of each standalone ENETC port, and for the internal
> bus of the Felix switch.
> 
> Refactoring is needed to support multiple MDIO buses from multiple
> drivers. The enetc_hw structure is made an opaque type and a smaller
> enetc_mdio_priv is created.
> 
> 'mdio_base' - MDIO registers base address - is being parameterized, to
> be able to work with different MDIO register bases.
> 
> The ENETC MDIO bus operations are exported from the fsl-enetc-mdio
> kernel object, the same that registers the central MDIO controller (the
> dedicated PF). The ENETC main driver has been changed to select it, and
> use its exported helpers to further register its private MDIO bus. The
> DSA Felix driver will do the same.

This series has already been applied so this may be food for thought at
this point, but why was not the solution to create a standalone mii_bus
driver and have all consumers be pointed it?

It is not uncommon for MDIO controllers to be re-used and integrated
within a larger block and when that happens whoever owns the largest
address space, say the Ethernet MAC can request the large resource
region and the MDIO bus controler can work on that premise, that's what
we did with genet/bcmmii.c and mdio-bcm-unimac.c for instance (so we
only do an ioremap, not request_mem_region + ioremap).

Your commit message does not provide a justification for why this
abstraction (mii_bus) was not suitable or considered here. Do you think
that could be changed?

Thanks!
Vladimir Oltean Jan. 6, 2020, 11 p.m. UTC | #2
Hi Florian,

On Mon, 6 Jan 2020 at 21:35, Florian Fainelli <f.fainelli@gmail.com> wrote:
>
> On 1/5/20 5:34 PM, Vladimir Oltean wrote:
> > From: Claudiu Manoil <claudiu.manoil@nxp.com>
> >
> > Within the LS1028A SoC, the register map for the ENETC MDIO controller
> > is instantiated a few times: for the central (external) MDIO controller,
> > for the internal bus of each standalone ENETC port, and for the internal
> > bus of the Felix switch.
> >
> > Refactoring is needed to support multiple MDIO buses from multiple
> > drivers. The enetc_hw structure is made an opaque type and a smaller
> > enetc_mdio_priv is created.
> >
> > 'mdio_base' - MDIO registers base address - is being parameterized, to
> > be able to work with different MDIO register bases.
> >
> > The ENETC MDIO bus operations are exported from the fsl-enetc-mdio
> > kernel object, the same that registers the central MDIO controller (the
> > dedicated PF). The ENETC main driver has been changed to select it, and
> > use its exported helpers to further register its private MDIO bus. The
> > DSA Felix driver will do the same.
>
> This series has already been applied so this may be food for thought at
> this point, but why was not the solution to create a standalone mii_bus
> driver and have all consumers be pointed it?
>

I have no real opinion on this.

To be honest, the reason is that the existing "culture" of Freescale
MDIO drivers wasn't to put them in drivers/net/phy/mdio-*.c, and I
just didn't look past the fence.

But what is the benefit? What gets passed between bcmgenet and
mdio-bcm-unimac with struct bcmgenet_platform_data is equivalent with
what gets passed between vsc9959 and enetc_mdio with the manual
population of struct mii_bus and struct enetc_mdio_priv, no? I'm not
even sure there is a net reduction in code size. And I am not really
sure that I want an of_node for the MDIO bus platform device anyway.
Whereas genet seems to be instantiating a port-private MDIO bus for
the _real_ (but nonetheless embedded) PHY, the MDIO bus we have here
is for the MAC PCS, which is more akin to the custom device tree
binding "pcsphy-handle" that the DPAA1 driver is using (see
arch/arm64/boot/dts/qoriq-fman3-0-10g-0.dtsi for example). So there is
no requirement to run the PHY state machine on it, it's just locally
driven, so I don't want to add a dependency on device tree where it's
really not needed. (By the way I am further confused by the
undocumented/unused "brcm,40nm-ephy" compatible string that these
device tree bindings for genet have).

> It is not uncommon for MDIO controllers to be re-used and integrated
> within a larger block and when that happens whoever owns the largest
> address space, say the Ethernet MAC can request the large resource
> region and the MDIO bus controler can work on that premise, that's what
> we did with genet/bcmmii.c and mdio-bcm-unimac.c for instance (so we
> only do an ioremap, not request_mem_region + ioremap).
>

I don't really understand this. In arch/mips/boot/dts, for all of
bcm73xx and bcm74xx SoCs, you have a single Ethernet port DT node, and
a single MDIO bus as a child beneath it, where is this reuse that you
mention?
And because I don't really understand what you've said, my following
comment maybe makes no sense, but I think what you mean by "MDIO
controller reuse" is that there are multiple instantiations of the
register map, but ultimately every transaction ends up on the same
MDIO/MDC pair of wires and the same electrical bus.
We do have some of that with the ENETC, but not with the switch, whose
internal MDIO bus has no connection to the outside world, it just
holds the PCS front-ends for the SerDes.
I also don't understand the reference to request_mem_region, perhaps
it would help if you could show some code.

> Your commit message does not provide a justification for why this
> abstraction (mii_bus) was not suitable or considered here. Do you think
> that could be changed?
>

I'm sorry, was the mii_bus abstraction really not considered here?
Based on the stuff exported in this patch, an mii_bus is exactly what
I'm registering in 9/9, no?

> Thanks!
> --
> Florian

Regards,
-Vladimir
Florian Fainelli Jan. 7, 2020, 4:56 a.m. UTC | #3
On 1/6/2020 3:00 PM, Vladimir Oltean wrote:
> Hi Florian,
> 
> On Mon, 6 Jan 2020 at 21:35, Florian Fainelli <f.fainelli@gmail.com> wrote:
>>
>> On 1/5/20 5:34 PM, Vladimir Oltean wrote:
>>> From: Claudiu Manoil <claudiu.manoil@nxp.com>
>>>
>>> Within the LS1028A SoC, the register map for the ENETC MDIO controller
>>> is instantiated a few times: for the central (external) MDIO controller,
>>> for the internal bus of each standalone ENETC port, and for the internal
>>> bus of the Felix switch.
>>>
>>> Refactoring is needed to support multiple MDIO buses from multiple
>>> drivers. The enetc_hw structure is made an opaque type and a smaller
>>> enetc_mdio_priv is created.
>>>
>>> 'mdio_base' - MDIO registers base address - is being parameterized, to
>>> be able to work with different MDIO register bases.
>>>
>>> The ENETC MDIO bus operations are exported from the fsl-enetc-mdio
>>> kernel object, the same that registers the central MDIO controller (the
>>> dedicated PF). The ENETC main driver has been changed to select it, and
>>> use its exported helpers to further register its private MDIO bus. The
>>> DSA Felix driver will do the same.
>>
>> This series has already been applied so this may be food for thought at
>> this point, but why was not the solution to create a standalone mii_bus
>> driver and have all consumers be pointed it?
>>
> 
> I have no real opinion on this.
> 
> To be honest, the reason is that the existing "culture" of Freescale
> MDIO drivers wasn't to put them in drivers/net/phy/mdio-*.c, and I
> just didn't look past the fence.
> 
> But what is the benefit? What gets passed between bcmgenet and
> mdio-bcm-unimac with struct bcmgenet_platform_data is equivalent with
> what gets passed between vsc9959 and enetc_mdio with the manual
> population of struct mii_bus and struct enetc_mdio_priv, no? I'm not
> even sure there is a net reduction in code size. And I am not really
> sure that I want an of_node for the MDIO bus platform device anyway.
> Whereas genet seems to be instantiating a port-private MDIO bus for
> the _real_ (but nonetheless embedded) PHY, the MDIO bus we have here
> is for the MAC PCS, which is more akin to the custom device tree
> binding "pcsphy-handle" that the DPAA1 driver is using (see
> arch/arm64/boot/dts/qoriq-fman3-0-10g-0.dtsi for example). So there is
> no requirement to run the PHY state machine on it, it's just locally
> driven, so I don't want to add a dependency on device tree where it's
> really not needed. (By the way I am further confused by the
> undocumented/unused "brcm,40nm-ephy" compatible string that these
> device tree bindings for genet have).

That compatibility string should not have been defined, but the DTS were
imported from our Device Tree auto-generation tool that did produce
those, when my TODO list empty, I might send an update to remove those,
unless someone thinks it's ABI and it would break something (which I can
swear won't).

> 
>> It is not uncommon for MDIO controllers to be re-used and integrated
>> within a larger block and when that happens whoever owns the largest
>> address space, say the Ethernet MAC can request the large resource
>> region and the MDIO bus controler can work on that premise, that's what
>> we did with genet/bcmmii.c and mdio-bcm-unimac.c for instance (so we
>> only do an ioremap, not request_mem_region + ioremap).
>>
> 
> I don't really understand this. In arch/mips/boot/dts, for all of
> bcm73xx and bcm74xx SoCs, you have a single Ethernet port DT node, and
> a single MDIO bus as a child beneath it, where is this reuse that you
> mention?
> And because I don't really understand what you've said, my following
> comment maybe makes no sense, but I think what you mean by "MDIO
> controller reuse" is that there are multiple instantiations of the
> register map, but ultimately every transaction ends up on the same
> MDIO/MDC pair of wires and the same electrical bus.
> We do have some of that with the ENETC, but not with the switch, whose
> internal MDIO bus has no connection to the outside world, it just
> holds the PCS front-ends for the SerDes.
> I also don't understand the reference to request_mem_region, perhaps
> it would help if you could show some code.

What I forgot telling you about is that the same MDIO bus controller is
used internally by each GENET instance to "talk" to both external and
internal PHYs, but also by the bcm_sf2.c driver which is why it made
sense to have a standalone MDIO bus driver that could either be
instantiated on its own (as is the case with bcm_sf2) or as part of a
larger block within GENET. The request_mem_region() + ioremap() comment
is because you cannot have two resources that overlap be used with
request_mem_region(), since the MDIO bus driver is embedded into a
larger block, it simply does an ioremap. If that confused you, then you
can just discard that comment, is it not particularly relevant.

> 
>> Your commit message does not provide a justification for why this
>> abstraction (mii_bus) was not suitable or considered here. Do you think
>> that could be changed?
>>
> 
> I'm sorry, was the mii_bus abstraction really not considered here?
> Based on the stuff exported in this patch, an mii_bus is exactly what
> I'm registering in 9/9, no?

I meat in the commit message, there is no justification why this was not
considered or used, by asking you ended up providing one, that is
typically what one would expect to find to explain why something was/was
not considered. It's fine, the code is merge, I won't object or require
you to use a mii_bus abstraction.
Vladimir Oltean Jan. 7, 2020, 9:46 a.m. UTC | #4
On Tue, 7 Jan 2020 at 06:56, Florian Fainelli <f.fainelli@gmail.com> wrote:
>
>
>
> On 1/6/2020 3:00 PM, Vladimir Oltean wrote:
> > Hi Florian,
> >
> > On Mon, 6 Jan 2020 at 21:35, Florian Fainelli <f.fainelli@gmail.com> wrote:
> >>
> >> On 1/5/20 5:34 PM, Vladimir Oltean wrote:
> >>> From: Claudiu Manoil <claudiu.manoil@nxp.com>
> >>>
> >>> Within the LS1028A SoC, the register map for the ENETC MDIO controller
> >>> is instantiated a few times: for the central (external) MDIO controller,
> >>> for the internal bus of each standalone ENETC port, and for the internal
> >>> bus of the Felix switch.
> >>>
> >>> Refactoring is needed to support multiple MDIO buses from multiple
> >>> drivers. The enetc_hw structure is made an opaque type and a smaller
> >>> enetc_mdio_priv is created.
> >>>
> >>> 'mdio_base' - MDIO registers base address - is being parameterized, to
> >>> be able to work with different MDIO register bases.
> >>>
> >>> The ENETC MDIO bus operations are exported from the fsl-enetc-mdio
> >>> kernel object, the same that registers the central MDIO controller (the
> >>> dedicated PF). The ENETC main driver has been changed to select it, and
> >>> use its exported helpers to further register its private MDIO bus. The
> >>> DSA Felix driver will do the same.
> >>
> >> This series has already been applied so this may be food for thought at
> >> this point, but why was not the solution to create a standalone mii_bus
> >> driver and have all consumers be pointed it?
> >>
> >
> > I have no real opinion on this.
> >
> > To be honest, the reason is that the existing "culture" of Freescale
> > MDIO drivers wasn't to put them in drivers/net/phy/mdio-*.c, and I
> > just didn't look past the fence.
> >
> > But what is the benefit? What gets passed between bcmgenet and
> > mdio-bcm-unimac with struct bcmgenet_platform_data is equivalent with
> > what gets passed between vsc9959 and enetc_mdio with the manual
> > population of struct mii_bus and struct enetc_mdio_priv, no? I'm not
> > even sure there is a net reduction in code size. And I am not really
> > sure that I want an of_node for the MDIO bus platform device anyway.
> > Whereas genet seems to be instantiating a port-private MDIO bus for
> > the _real_ (but nonetheless embedded) PHY, the MDIO bus we have here
> > is for the MAC PCS, which is more akin to the custom device tree
> > binding "pcsphy-handle" that the DPAA1 driver is using (see
> > arch/arm64/boot/dts/qoriq-fman3-0-10g-0.dtsi for example). So there is
> > no requirement to run the PHY state machine on it, it's just locally
> > driven, so I don't want to add a dependency on device tree where it's
> > really not needed. (By the way I am further confused by the
> > undocumented/unused "brcm,40nm-ephy" compatible string that these
> > device tree bindings for genet have).
>
> That compatibility string should not have been defined, but the DTS were
> imported from our Device Tree auto-generation tool that did produce
> those, when my TODO list empty, I might send an update to remove those,
> unless someone thinks it's ABI and it would break something (which I can
> swear won't).
>
> >
> >> It is not uncommon for MDIO controllers to be re-used and integrated
> >> within a larger block and when that happens whoever owns the largest
> >> address space, say the Ethernet MAC can request the large resource
> >> region and the MDIO bus controler can work on that premise, that's what
> >> we did with genet/bcmmii.c and mdio-bcm-unimac.c for instance (so we
> >> only do an ioremap, not request_mem_region + ioremap).
> >>
> >
> > I don't really understand this. In arch/mips/boot/dts, for all of
> > bcm73xx and bcm74xx SoCs, you have a single Ethernet port DT node, and
> > a single MDIO bus as a child beneath it, where is this reuse that you
> > mention?
> > And because I don't really understand what you've said, my following
> > comment maybe makes no sense, but I think what you mean by "MDIO
> > controller reuse" is that there are multiple instantiations of the
> > register map, but ultimately every transaction ends up on the same
> > MDIO/MDC pair of wires and the same electrical bus.
> > We do have some of that with the ENETC, but not with the switch, whose
> > internal MDIO bus has no connection to the outside world, it just
> > holds the PCS front-ends for the SerDes.
> > I also don't understand the reference to request_mem_region, perhaps
> > it would help if you could show some code.
>
> What I forgot telling you about is that the same MDIO bus controller is
> used internally by each GENET instance to "talk" to both external and
> internal PHYs, but also by the bcm_sf2.c driver which is why it made
> sense to have a standalone MDIO bus driver that could either be
> instantiated on its own (as is the case with bcm_sf2) or as part of a
> larger block within GENET. The request_mem_region() + ioremap() comment
> is because you cannot have two resources that overlap be used with
> request_mem_region(), since the MDIO bus driver is embedded into a
> larger block, it simply does an ioremap. If that confused you, then you
> can just discard that comment, is it not particularly relevant.
>

So we don't typically have the external MDIO controller be part of the
memory map of an Ethernet port, it has its own region in the SoC. But
this is not an external MDIO controller, and it's a completely
separate hardware instance with its own memory region not shared with
anybody else.

And I think your need for registering the slave MDIO bus in the
Starfighter 2 switch driver, on top of the same memory region as the
GENET's MDIO bus, is just to work around the fact that the switch
pseudo-PHY MDIO address is fixed at 30 on some chips, and you need to
intercept those transactions by wrapping the mii_bus->read and
mii_bus->write methods of the master, otherwise none of this would
have been needed.
If I understand correctly, couldn't you have just overridden the
mii_bus->read and mii_bus->write ops of the master MDIO bus, without
registering another one?

> >
> >> Your commit message does not provide a justification for why this
> >> abstraction (mii_bus) was not suitable or considered here. Do you think
> >> that could be changed?
> >>
> >
> > I'm sorry, was the mii_bus abstraction really not considered here?
> > Based on the stuff exported in this patch, an mii_bus is exactly what
> > I'm registering in 9/9, no?
>
> I meat in the commit message, there is no justification why this was not
> considered or used, by asking you ended up providing one, that is
> typically what one would expect to find to explain why something was/was
> not considered. It's fine, the code is merge, I won't object or require
> you to use a mii_bus abstraction.

So I answered your question? If so, it was by mistake, because I still
don't exactly understand your point (although I would like to).
The difference is that we don't register a new platform device for the
mii_bus (we use mii_bus->dev = ocelot->dev), and that it's not in
drivers/net/phy/mdio-fsl-enetc.c, although it could be, but I don't
see a clear benefit.

> --
> Florian

Regards,
-Vladimir
Florian Fainelli Jan. 7, 2020, 7:22 p.m. UTC | #5
On 1/7/20 1:46 AM, Vladimir Oltean wrote:
> 
> So we don't typically have the external MDIO controller be part of the
> memory map of an Ethernet port, it has its own region in the SoC. But
> this is not an external MDIO controller, and it's a completely
> separate hardware instance with its own memory region not shared with
> anybody else.

OK, I do not think I managed to express myself correctly, by external
MDIO controller I meant a controller that is used to interface with
off-chip (and sometimes on-chip, too) MDIO devices. That is an on-chip
MDIO controller technically, pardon me for not expressing myself more
clearly here.

> 
> And I think your need for registering the slave MDIO bus in the
> Starfighter 2 switch driver, on top of the same memory region as the
> GENET's MDIO bus, is just to work around the fact that the switch
> pseudo-PHY MDIO address is fixed at 30 on some chips, and you need to
> intercept those transactions by wrapping the mii_bus->read and
> mii_bus->write methods of the master, otherwise none of this would
> have been needed.

You are conflating two things that are not related to the point being
discussed. The SF2 switch used on 7445 and 7278 has some wrapping around
the Roboswitch original IP. The roboswitch has an internal MDIO
controller which is managed through the register space within the
switch. We added a MDIO controller to interface with both our internal
Gigabit PHYs as well as external MDIO devices would such thing exist.
The binding show the MDIO node as part of "switch_top" but not being
part of "core":
Documentation/devicetree/bindings/net/brcm,bcm7445-switch-v4.0.txt.

The same IP used as the MDIO controller (those two 32-bit words with CFG
and CMD) are the same as those that are used on GENET, hence the desire
to re-use the same piece of driver.

> If I understand correctly, couldn't you have just overridden the
> mii_bus->read and mii_bus->write ops of the master MDIO bus, without
> registering another one?

Yes maybe, I do not think this is particularly relevant to this
discussion though.

> 
>>>
>>>> Your commit message does not provide a justification for why this
>>>> abstraction (mii_bus) was not suitable or considered here. Do you think
>>>> that could be changed?
>>>>
>>>
>>> I'm sorry, was the mii_bus abstraction really not considered here?
>>> Based on the stuff exported in this patch, an mii_bus is exactly what
>>> I'm registering in 9/9, no?
>>
>> I meat in the commit message, there is no justification why this was not
>> considered or used, by asking you ended up providing one, that is
>> typically what one would expect to find to explain why something was/was
>> not considered. It's fine, the code is merge, I won't object or require
>> you to use a mii_bus abstraction.
> 
> So I answered your question? If so, it was by mistake, because I still
> don't exactly understand your point (although I would like to).
> The difference is that we don't register a new platform device for the
> mii_bus (we use mii_bus->dev = ocelot->dev), and that it's not in
> drivers/net/phy/mdio-fsl-enetc.c, although it could be, but I don't
> see a clear benefit.

To go back to the original point, I suppose the answer is no: we did not
consider using a mii_bus abstraction and in hindsight, we do not see the
point but it was interesting you brought that up. Case closed from my
side, thank you.
diff mbox series

Patch

diff --git a/drivers/net/ethernet/freescale/enetc/Kconfig b/drivers/net/ethernet/freescale/enetc/Kconfig
index edad4ca46327..fe942de19597 100644
--- a/drivers/net/ethernet/freescale/enetc/Kconfig
+++ b/drivers/net/ethernet/freescale/enetc/Kconfig
@@ -2,6 +2,7 @@ 
 config FSL_ENETC
 	tristate "ENETC PF driver"
 	depends on PCI && PCI_MSI && (ARCH_LAYERSCAPE || COMPILE_TEST)
+	select FSL_ENETC_MDIO
 	select PHYLIB
 	help
 	  This driver supports NXP ENETC gigabit ethernet controller PCIe
diff --git a/drivers/net/ethernet/freescale/enetc/Makefile b/drivers/net/ethernet/freescale/enetc/Makefile
index d0db33e5b6b7..74f7ac253b8b 100644
--- a/drivers/net/ethernet/freescale/enetc/Makefile
+++ b/drivers/net/ethernet/freescale/enetc/Makefile
@@ -3,7 +3,7 @@ 
 common-objs := enetc.o enetc_cbdr.o enetc_ethtool.o
 
 obj-$(CONFIG_FSL_ENETC) += fsl-enetc.o
-fsl-enetc-y := enetc_pf.o enetc_mdio.o $(common-objs)
+fsl-enetc-y := enetc_pf.o $(common-objs)
 fsl-enetc-$(CONFIG_PCI_IOV) += enetc_msg.o
 fsl-enetc-$(CONFIG_FSL_ENETC_QOS) += enetc_qos.o
 
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_hw.h b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
index 8375cd886dba..62554f28ce07 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_hw.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
@@ -200,6 +200,7 @@  enum enetc_bdr_type {TX, RX};
 #define ENETC_PFPMR		0x1900
 #define ENETC_PFPMR_PMACE	BIT(1)
 #define ENETC_PFPMR_MWLM	BIT(0)
+#define ENETC_EMDIO_BASE	0x1c00
 #define ENETC_PSIUMHFR0(n, err)	(((err) ? 0x1d08 : 0x1d00) + (n) * 0x10)
 #define ENETC_PSIUMHFR1(n)	(0x1d04 + (n) * 0x10)
 #define ENETC_PSIMMHFR0(n, err)	(((err) ? 0x1d00 : 0x1d08) + (n) * 0x10)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_mdio.c b/drivers/net/ethernet/freescale/enetc/enetc_mdio.c
index 149883c8f0b8..18c68e048d43 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_mdio.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_mdio.c
@@ -1,24 +1,35 @@ 
 // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
 /* Copyright 2019 NXP */
 
+#include <linux/fsl/enetc_mdio.h>
 #include <linux/mdio.h>
 #include <linux/of_mdio.h>
 #include <linux/iopoll.h>
 #include <linux/of.h>
 
-#include "enetc_mdio.h"
+#include "enetc_pf.h"
 
-#define	ENETC_MDIO_REG_OFFSET	0x1c00
 #define	ENETC_MDIO_CFG	0x0	/* MDIO configuration and status */
 #define	ENETC_MDIO_CTL	0x4	/* MDIO control */
 #define	ENETC_MDIO_DATA	0x8	/* MDIO data */
 #define	ENETC_MDIO_ADDR	0xc	/* MDIO address */
 
-#define enetc_mdio_rd(hw, off) \
-	enetc_port_rd(hw, ENETC_##off + ENETC_MDIO_REG_OFFSET)
-#define enetc_mdio_wr(hw, off, val) \
-	enetc_port_wr(hw, ENETC_##off + ENETC_MDIO_REG_OFFSET, val)
-#define enetc_mdio_rd_reg(off)	enetc_mdio_rd(hw, off)
+static inline u32 _enetc_mdio_rd(struct enetc_mdio_priv *mdio_priv, int off)
+{
+	return enetc_port_rd(mdio_priv->hw, mdio_priv->mdio_base + off);
+}
+
+static inline void _enetc_mdio_wr(struct enetc_mdio_priv *mdio_priv, int off,
+				  u32 val)
+{
+	enetc_port_wr(mdio_priv->hw, mdio_priv->mdio_base + off, val);
+}
+
+#define enetc_mdio_rd(mdio_priv, off) \
+	_enetc_mdio_rd(mdio_priv, ENETC_##off)
+#define enetc_mdio_wr(mdio_priv, off, val) \
+	_enetc_mdio_wr(mdio_priv, ENETC_##off, val)
+#define enetc_mdio_rd_reg(off)	enetc_mdio_rd(mdio_priv, off)
 
 #define ENETC_MDC_DIV		258
 
@@ -35,7 +46,7 @@ 
 #define MDIO_DATA(x)		((x) & 0xffff)
 
 #define TIMEOUT	1000
-static int enetc_mdio_wait_complete(struct enetc_hw *hw)
+static int enetc_mdio_wait_complete(struct enetc_mdio_priv *mdio_priv)
 {
 	u32 val;
 
@@ -46,7 +57,6 @@  static int enetc_mdio_wait_complete(struct enetc_hw *hw)
 int enetc_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 value)
 {
 	struct enetc_mdio_priv *mdio_priv = bus->priv;
-	struct enetc_hw *hw = mdio_priv->hw;
 	u32 mdio_ctl, mdio_cfg;
 	u16 dev_addr;
 	int ret;
@@ -61,39 +71,39 @@  int enetc_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 value)
 		mdio_cfg &= ~MDIO_CFG_ENC45;
 	}
 
-	enetc_mdio_wr(hw, MDIO_CFG, mdio_cfg);
+	enetc_mdio_wr(mdio_priv, MDIO_CFG, mdio_cfg);
 
-	ret = enetc_mdio_wait_complete(hw);
+	ret = enetc_mdio_wait_complete(mdio_priv);
 	if (ret)
 		return ret;
 
 	/* set port and dev addr */
 	mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr);
-	enetc_mdio_wr(hw, MDIO_CTL, mdio_ctl);
+	enetc_mdio_wr(mdio_priv, MDIO_CTL, mdio_ctl);
 
 	/* set the register address */
 	if (regnum & MII_ADDR_C45) {
-		enetc_mdio_wr(hw, MDIO_ADDR, regnum & 0xffff);
+		enetc_mdio_wr(mdio_priv, MDIO_ADDR, regnum & 0xffff);
 
-		ret = enetc_mdio_wait_complete(hw);
+		ret = enetc_mdio_wait_complete(mdio_priv);
 		if (ret)
 			return ret;
 	}
 
 	/* write the value */
-	enetc_mdio_wr(hw, MDIO_DATA, MDIO_DATA(value));
+	enetc_mdio_wr(mdio_priv, MDIO_DATA, MDIO_DATA(value));
 
-	ret = enetc_mdio_wait_complete(hw);
+	ret = enetc_mdio_wait_complete(mdio_priv);
 	if (ret)
 		return ret;
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(enetc_mdio_write);
 
 int enetc_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
 {
 	struct enetc_mdio_priv *mdio_priv = bus->priv;
-	struct enetc_hw *hw = mdio_priv->hw;
 	u32 mdio_ctl, mdio_cfg;
 	u16 dev_addr, value;
 	int ret;
@@ -107,86 +117,56 @@  int enetc_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
 		mdio_cfg &= ~MDIO_CFG_ENC45;
 	}
 
-	enetc_mdio_wr(hw, MDIO_CFG, mdio_cfg);
+	enetc_mdio_wr(mdio_priv, MDIO_CFG, mdio_cfg);
 
-	ret = enetc_mdio_wait_complete(hw);
+	ret = enetc_mdio_wait_complete(mdio_priv);
 	if (ret)
 		return ret;
 
 	/* set port and device addr */
 	mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr);
-	enetc_mdio_wr(hw, MDIO_CTL, mdio_ctl);
+	enetc_mdio_wr(mdio_priv, MDIO_CTL, mdio_ctl);
 
 	/* set the register address */
 	if (regnum & MII_ADDR_C45) {
-		enetc_mdio_wr(hw, MDIO_ADDR, regnum & 0xffff);
+		enetc_mdio_wr(mdio_priv, MDIO_ADDR, regnum & 0xffff);
 
-		ret = enetc_mdio_wait_complete(hw);
+		ret = enetc_mdio_wait_complete(mdio_priv);
 		if (ret)
 			return ret;
 	}
 
 	/* initiate the read */
-	enetc_mdio_wr(hw, MDIO_CTL, mdio_ctl | MDIO_CTL_READ);
+	enetc_mdio_wr(mdio_priv, MDIO_CTL, mdio_ctl | MDIO_CTL_READ);
 
-	ret = enetc_mdio_wait_complete(hw);
+	ret = enetc_mdio_wait_complete(mdio_priv);
 	if (ret)
 		return ret;
 
 	/* return all Fs if nothing was there */
-	if (enetc_mdio_rd(hw, MDIO_CFG) & MDIO_CFG_RD_ER) {
+	if (enetc_mdio_rd(mdio_priv, MDIO_CFG) & MDIO_CFG_RD_ER) {
 		dev_dbg(&bus->dev,
 			"Error while reading PHY%d reg at %d.%hhu\n",
 			phy_id, dev_addr, regnum);
 		return 0xffff;
 	}
 
-	value = enetc_mdio_rd(hw, MDIO_DATA) & 0xffff;
+	value = enetc_mdio_rd(mdio_priv, MDIO_DATA) & 0xffff;
 
 	return value;
 }
+EXPORT_SYMBOL_GPL(enetc_mdio_read);
 
-int enetc_mdio_probe(struct enetc_pf *pf)
+struct enetc_hw *enetc_hw_alloc(struct device *dev, void __iomem *port_regs)
 {
-	struct device *dev = &pf->si->pdev->dev;
-	struct enetc_mdio_priv *mdio_priv;
-	struct device_node *np;
-	struct mii_bus *bus;
-	int err;
-
-	bus = devm_mdiobus_alloc_size(dev, sizeof(*mdio_priv));
-	if (!bus)
-		return -ENOMEM;
-
-	bus->name = "Freescale ENETC MDIO Bus";
-	bus->read = enetc_mdio_read;
-	bus->write = enetc_mdio_write;
-	bus->parent = dev;
-	mdio_priv = bus->priv;
-	mdio_priv->hw = &pf->si->hw;
-	snprintf(bus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev));
-
-	np = of_get_child_by_name(dev->of_node, "mdio");
-	if (!np) {
-		dev_err(dev, "MDIO node missing\n");
-		return -EINVAL;
-	}
-
-	err = of_mdiobus_register(bus, np);
-	if (err) {
-		of_node_put(np);
-		dev_err(dev, "cannot register MDIO bus\n");
-		return err;
-	}
+	struct enetc_hw *hw;
 
-	of_node_put(np);
-	pf->mdio = bus;
+	hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
+	if (!hw)
+		return ERR_PTR(-ENOMEM);
 
-	return 0;
-}
+	hw->port = port_regs;
 
-void enetc_mdio_remove(struct enetc_pf *pf)
-{
-	if (pf->mdio)
-		mdiobus_unregister(pf->mdio);
+	return hw;
 }
+EXPORT_SYMBOL_GPL(enetc_hw_alloc);
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_mdio.h b/drivers/net/ethernet/freescale/enetc/enetc_mdio.h
deleted file mode 100644
index 60c9a3889824..000000000000
--- a/drivers/net/ethernet/freescale/enetc/enetc_mdio.h
+++ /dev/null
@@ -1,12 +0,0 @@ 
-/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
-/* Copyright 2019 NXP */
-
-#include <linux/phy.h>
-#include "enetc_pf.h"
-
-struct enetc_mdio_priv {
-	struct enetc_hw *hw;
-};
-
-int enetc_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 value);
-int enetc_mdio_read(struct mii_bus *bus, int phy_id, int regnum);
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pci_mdio.c b/drivers/net/ethernet/freescale/enetc/enetc_pci_mdio.c
index fbd41ce01f06..87c0e969da40 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pci_mdio.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pci_mdio.c
@@ -1,7 +1,8 @@ 
 // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
 /* Copyright 2019 NXP */
+#include <linux/fsl/enetc_mdio.h>
 #include <linux/of_mdio.h>
-#include "enetc_mdio.h"
+#include "enetc_pf.h"
 
 #define ENETC_MDIO_DEV_ID	0xee01
 #define ENETC_MDIO_DEV_NAME	"FSL PCIe IE Central MDIO"
@@ -13,17 +14,29 @@  static int enetc_pci_mdio_probe(struct pci_dev *pdev,
 {
 	struct enetc_mdio_priv *mdio_priv;
 	struct device *dev = &pdev->dev;
+	void __iomem *port_regs;
 	struct enetc_hw *hw;
 	struct mii_bus *bus;
 	int err;
 
-	hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
-	if (!hw)
-		return -ENOMEM;
+	port_regs = pci_iomap(pdev, 0, 0);
+	if (!port_regs) {
+		dev_err(dev, "iomap failed\n");
+		err = -ENXIO;
+		goto err_ioremap;
+	}
+
+	hw = enetc_hw_alloc(dev, port_regs);
+	if (IS_ERR(enetc_hw_alloc)) {
+		err = PTR_ERR(hw);
+		goto err_hw_alloc;
+	}
 
 	bus = devm_mdiobus_alloc_size(dev, sizeof(*mdio_priv));
-	if (!bus)
-		return -ENOMEM;
+	if (!bus) {
+		err = -ENOMEM;
+		goto err_mdiobus_alloc;
+	}
 
 	bus->name = ENETC_MDIO_BUS_NAME;
 	bus->read = enetc_mdio_read;
@@ -31,13 +44,14 @@  static int enetc_pci_mdio_probe(struct pci_dev *pdev,
 	bus->parent = dev;
 	mdio_priv = bus->priv;
 	mdio_priv->hw = hw;
+	mdio_priv->mdio_base = ENETC_EMDIO_BASE;
 	snprintf(bus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev));
 
 	pcie_flr(pdev);
 	err = pci_enable_device_mem(pdev);
 	if (err) {
 		dev_err(dev, "device enable failed\n");
-		return err;
+		goto err_pci_enable;
 	}
 
 	err = pci_request_region(pdev, 0, KBUILD_MODNAME);
@@ -46,13 +60,6 @@  static int enetc_pci_mdio_probe(struct pci_dev *pdev,
 		goto err_pci_mem_reg;
 	}
 
-	hw->port = pci_iomap(pdev, 0, 0);
-	if (!hw->port) {
-		err = -ENXIO;
-		dev_err(dev, "iomap failed\n");
-		goto err_ioremap;
-	}
-
 	err = of_mdiobus_register(bus, dev->of_node);
 	if (err)
 		goto err_mdiobus_reg;
@@ -62,12 +69,14 @@  static int enetc_pci_mdio_probe(struct pci_dev *pdev,
 	return 0;
 
 err_mdiobus_reg:
-	iounmap(mdio_priv->hw->port);
-err_ioremap:
 	pci_release_mem_regions(pdev);
 err_pci_mem_reg:
 	pci_disable_device(pdev);
-
+err_pci_enable:
+err_mdiobus_alloc:
+	iounmap(port_regs);
+err_hw_alloc:
+err_ioremap:
 	return err;
 }
 
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
index e7482d483b28..fc0d7d99e9a1 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
@@ -2,6 +2,7 @@ 
 /* Copyright 2017-2019 NXP */
 
 #include <linux/module.h>
+#include <linux/fsl/enetc_mdio.h>
 #include <linux/of_mdio.h>
 #include <linux/of_net.h>
 #include "enetc_pf.h"
@@ -749,6 +750,52 @@  static void enetc_pf_netdev_setup(struct enetc_si *si, struct net_device *ndev,
 	enetc_get_primary_mac_addr(&si->hw, ndev->dev_addr);
 }
 
+static int enetc_mdio_probe(struct enetc_pf *pf)
+{
+	struct device *dev = &pf->si->pdev->dev;
+	struct enetc_mdio_priv *mdio_priv;
+	struct device_node *np;
+	struct mii_bus *bus;
+	int err;
+
+	bus = devm_mdiobus_alloc_size(dev, sizeof(*mdio_priv));
+	if (!bus)
+		return -ENOMEM;
+
+	bus->name = "Freescale ENETC MDIO Bus";
+	bus->read = enetc_mdio_read;
+	bus->write = enetc_mdio_write;
+	bus->parent = dev;
+	mdio_priv = bus->priv;
+	mdio_priv->hw = &pf->si->hw;
+	mdio_priv->mdio_base = ENETC_EMDIO_BASE;
+	snprintf(bus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev));
+
+	np = of_get_child_by_name(dev->of_node, "mdio");
+	if (!np) {
+		dev_err(dev, "MDIO node missing\n");
+		return -EINVAL;
+	}
+
+	err = of_mdiobus_register(bus, np);
+	if (err) {
+		of_node_put(np);
+		dev_err(dev, "cannot register MDIO bus\n");
+		return err;
+	}
+
+	of_node_put(np);
+	pf->mdio = bus;
+
+	return 0;
+}
+
+static void enetc_mdio_remove(struct enetc_pf *pf)
+{
+	if (pf->mdio)
+		mdiobus_unregister(pf->mdio);
+}
+
 static int enetc_of_get_phy(struct enetc_ndev_priv *priv)
 {
 	struct enetc_pf *pf = enetc_si_priv(priv->si);
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.h b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
index 10dd1b53bb08..59e65a6f6c3e 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
@@ -49,7 +49,3 @@  struct enetc_pf {
 int enetc_msg_psi_init(struct enetc_pf *pf);
 void enetc_msg_psi_free(struct enetc_pf *pf);
 void enetc_msg_handle_rxmsg(struct enetc_pf *pf, int mbox_id, u16 *status);
-
-/* MDIO */
-int enetc_mdio_probe(struct enetc_pf *pf);
-void enetc_mdio_remove(struct enetc_pf *pf);
diff --git a/include/linux/fsl/enetc_mdio.h b/include/linux/fsl/enetc_mdio.h
new file mode 100644
index 000000000000..4875dd38af7e
--- /dev/null
+++ b/include/linux/fsl/enetc_mdio.h
@@ -0,0 +1,55 @@ 
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
+/* Copyright 2019 NXP */
+
+#ifndef _FSL_ENETC_MDIO_H_
+#define _FSL_ENETC_MDIO_H_
+
+#include <linux/phy.h>
+
+/* PCS registers */
+#define ENETC_PCS_LINK_TIMER1			0x12
+#define ENETC_PCS_LINK_TIMER1_VAL		0x06a0
+#define ENETC_PCS_LINK_TIMER2			0x13
+#define ENETC_PCS_LINK_TIMER2_VAL		0x0003
+#define ENETC_PCS_IF_MODE			0x14
+#define ENETC_PCS_IF_MODE_SGMII_EN		BIT(0)
+#define ENETC_PCS_IF_MODE_USE_SGMII_AN		BIT(1)
+#define ENETC_PCS_IF_MODE_SGMII_SPEED(x)	(((x) << 2) & GENMASK(3, 2))
+
+/* Not a mistake, the SerDes PLL needs to be set at 3.125 GHz by Reset
+ * Configuration Word (RCW, outside Linux control) for 2.5G SGMII mode. The PCS
+ * still thinks it's at gigabit.
+ */
+enum enetc_pcs_speed {
+	ENETC_PCS_SPEED_10	= 0,
+	ENETC_PCS_SPEED_100	= 1,
+	ENETC_PCS_SPEED_1000	= 2,
+	ENETC_PCS_SPEED_2500	= 2,
+};
+
+struct enetc_hw;
+
+struct enetc_mdio_priv {
+	struct enetc_hw *hw;
+	int mdio_base;
+};
+
+#if IS_REACHABLE(CONFIG_FSL_ENETC_MDIO)
+
+int enetc_mdio_read(struct mii_bus *bus, int phy_id, int regnum);
+int enetc_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 value);
+struct enetc_hw *enetc_hw_alloc(struct device *dev, void __iomem *port_regs);
+
+#else
+
+static inline int enetc_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
+{ return -EINVAL; }
+static inline int enetc_mdio_write(struct mii_bus *bus, int phy_id, int regnum,
+				   u16 value)
+{ return -EINVAL; }
+struct enetc_hw *enetc_hw_alloc(struct device *dev, void __iomem *port_regs)
+{ return ERR_PTR(-EINVAL); }
+
+#endif
+
+#endif