123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840 |
- From 5d84f16b0036b33487b94abef15ad3c224c81ee9 Mon Sep 17 00:00:00 2001
- From: Daniel Golle <[email protected]>
- Date: Thu, 3 Feb 2022 16:38:50 +0000
- Subject: [PATCH] net: mdio: support hardware-assisted indirect access
- MDIO controllers found in Switch-SoCs can offload some MDIO operations
- to the hardware:
- * MMD register access via Clause-22
- Instead of using multiple operations to access MMD registers via
- MII register MII_MMD_CTRL and MII_MMD_DATA some controllers
- allow transparent access to MMD PHY registers.
- * paged MII register access
- Some PHYs (namely RealTek and Vitesse) use vendor-defined MII
- register 0x1f for paged access. Some MDIO host controllers support
- transparent paged access when used with such PHYs.
- * add convenience accessors to fully support paged access also on
- multi-PHY packages (like the embedded PHYs in RTL83xx):
- phy_package_read_paged and phy_package_write_paged
- phy_package_port_read and phy_package_port_write
- phy_package_port_read_paged and phy_package_port_write_paged
- Signed-off-by: Daniel Golle <[email protected]>
- ---
- drivers/net/phy/mdio_bus.c | 335 ++++++++++++++++++++++++++++++++++++-
- drivers/net/phy/phy-core.c | 66 +++++++-
- include/linux/mdio.h | 59 +++++++
- include/linux/phy.h | 129 ++++++++++++++
- include/uapi/linux/mii.h | 1 +
- 5 files changed, 580 insertions(+), 10 deletions(-)
- --- a/drivers/net/phy/mdio_bus.c
- +++ b/drivers/net/phy/mdio_bus.c
- @@ -742,6 +742,32 @@ out:
- }
-
- /**
- + * __mdiobus_select_page - Unlocked version of the mdiobus_select_page function
- + * @bus: the mii_bus struct
- + * @addr: the phy address
- + * @page: register page to select
- + *
- + * Selects a MDIO bus register page. Caller must hold the mdio bus lock.
- + *
- + * NOTE: MUST NOT be called from interrupt context.
- + */
- +int __mdiobus_select_page(struct mii_bus *bus, int addr, u16 page)
- +{
- + lockdep_assert_held_once(&bus->mdio_lock);
- +
- + if (bus->selected_page[addr] == page)
- + return 0;
- +
- + bus->selected_page[addr] = page;
- + if (bus->read_paged)
- + return 0;
- +
- + return bus->write(bus, addr, MII_MAINPAGE, page);
- +
- +}
- +EXPORT_SYMBOL(__mdiobus_select_page);
- +
- +/**
- * __mdiobus_read - Unlocked version of the mdiobus_read function
- * @bus: the mii_bus struct
- * @addr: the phy address
- @@ -757,7 +783,10 @@ int __mdiobus_read(struct mii_bus *bus,
-
- lockdep_assert_held_once(&bus->mdio_lock);
-
- - retval = bus->read(bus, addr, regnum);
- + if (bus->read_paged)
- + retval = bus->read_paged(bus, addr, bus->selected_page[addr], regnum);
- + else
- + retval = bus->read(bus, addr, regnum);
-
- trace_mdio_access(bus, 1, addr, regnum, retval, retval);
- mdiobus_stats_acct(&bus->stats[addr], true, retval);
- @@ -767,6 +796,40 @@ int __mdiobus_read(struct mii_bus *bus,
- EXPORT_SYMBOL(__mdiobus_read);
-
- /**
- + * __mdiobus_read_paged - Unlocked version of the mdiobus_read_paged function
- + * @bus: the mii_bus struct
- + * @addr: the phy address
- + * @page: the register page to access
- + * @regnum: register number to read
- + *
- + * Read a MDIO bus register. Caller must hold the mdio bus lock.
- + *
- + * NOTE: MUST NOT be called from interrupt context.
- + */
- +int __mdiobus_read_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum)
- +{
- + int retval;
- + int oldpage;
- +
- + lockdep_assert_held_once(&bus->mdio_lock);
- +
- + if (bus->read_paged) {
- + retval = bus->read_paged(bus, addr, page, regnum);
- + } else {
- + oldpage = bus->selected_page[addr];
- + __mdiobus_select_page(bus, addr, page);
- + retval = bus->read(bus, addr, regnum);
- + __mdiobus_select_page(bus, addr, oldpage);
- + }
- +
- + trace_mdio_access(bus, 1, addr, regnum, retval, retval);
- + mdiobus_stats_acct(&bus->stats[addr], true, retval);
- +
- + return retval;
- +}
- +EXPORT_SYMBOL(__mdiobus_read_paged);
- +
- +/**
- * __mdiobus_write - Unlocked version of the mdiobus_write function
- * @bus: the mii_bus struct
- * @addr: the phy address
- @@ -783,7 +846,10 @@ int __mdiobus_write(struct mii_bus *bus,
-
- lockdep_assert_held_once(&bus->mdio_lock);
-
- - err = bus->write(bus, addr, regnum, val);
- + if (bus->write_paged)
- + err = bus->write_paged(bus, addr, bus->selected_page[addr], regnum, val);
- + else
- + err = bus->write(bus, addr, regnum, val);
-
- trace_mdio_access(bus, 0, addr, regnum, val, err);
- mdiobus_stats_acct(&bus->stats[addr], false, err);
- @@ -793,6 +859,39 @@ int __mdiobus_write(struct mii_bus *bus,
- EXPORT_SYMBOL(__mdiobus_write);
-
- /**
- + * __mdiobus_write_paged - Unlocked version of the mdiobus_write_paged function
- + * @bus: the mii_bus struct
- + * @addr: the phy address
- + * @page: the register page to access
- + * @regnum: register number to write
- + * @val: value to write to @regnum
- + *
- + * Write a MDIO bus register. Caller must hold the mdio bus lock.
- + *
- + * NOTE: MUST NOT be called from interrupt context.
- + */
- +int __mdiobus_write_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 val)
- +{
- + int err, oldpage;
- +
- + lockdep_assert_held_once(&bus->mdio_lock);
- +
- + if (bus->write_paged) {
- + err = bus->write_paged(bus, addr, page, regnum, val);
- + } else {
- + oldpage = bus->selected_page[addr];
- + __mdiobus_select_page(bus, addr, page);
- + err = bus->write(bus, addr, regnum, val);
- + __mdiobus_select_page(bus, addr, oldpage);
- + }
- + trace_mdio_access(bus, 0, addr, regnum, val, err);
- + mdiobus_stats_acct(&bus->stats[addr], false, err);
- + return err;
- +}
- +EXPORT_SYMBOL(__mdiobus_write_paged);
- +
- +
- +/**
- * __mdiobus_modify_changed - Unlocked version of the mdiobus_modify function
- * @bus: the mii_bus struct
- * @addr: the phy address
- @@ -825,6 +924,43 @@ int __mdiobus_modify_changed(struct mii_
- EXPORT_SYMBOL_GPL(__mdiobus_modify_changed);
-
- /**
- + * __mdiobus_modify_changed_paged - Unlocked version of the mdiobus_modify_paged function
- + * @bus: the mii_bus struct
- + * @addr: the phy address
- + * @regnum: register number to modify
- + * @mask: bit mask of bits to clear
- + * @set: bit mask of bits to set
- + *
- + * Read, modify, and if any change, write the register value back to the
- + * device. Any error returns a negative number.
- + *
- + * NOTE: MUST NOT be called from interrupt context.
- + */
- +int __mdiobus_modify_changed_paged(struct mii_bus *bus, int addr, u32 regnum, u16 page,
- + u16 mask, u16 set)
- +{
- + int new, ret, oldpage;
- +
- + oldpage = bus->selected_page[addr];
- + __mdiobus_select_page(bus, addr, page);
- +
- + ret = __mdiobus_read_paged(bus, addr, page, regnum);
- + if (ret < 0)
- + return ret;
- +
- + new = (ret & ~mask) | set;
- + if (new == ret)
- + return 0;
- +
- + ret = __mdiobus_write_paged(bus, addr, page, regnum, new);
- +
- + __mdiobus_select_page(bus, addr, oldpage);
- +
- + return ret < 0 ? ret : 1;
- +}
- +EXPORT_SYMBOL_GPL(__mdiobus_modify_changed_paged);
- +
- +/**
- * mdiobus_read_nested - Nested version of the mdiobus_read function
- * @bus: the mii_bus struct
- * @addr: the phy address
- @@ -850,6 +986,79 @@ int mdiobus_read_nested(struct mii_bus *
- EXPORT_SYMBOL(mdiobus_read_nested);
-
- /**
- + * mdiobus_select_page_nested - Nested version of the mdiobus_select_page function
- + * @bus: the mii_bus struct
- + * @addr: the phy address
- + * @page: register page to access
- + *
- + * In case of nested MDIO bus access avoid lockdep false positives by
- + * using mutex_lock_nested().
- + *
- + * NOTE: MUST NOT be called from interrupt context,
- + * because the bus read/write functions may wait for an interrupt
- + * to conclude the operation.
- + */
- +int mdiobus_select_page_nested(struct mii_bus *bus, int addr, u16 page)
- +{
- + int retval;
- +
- + mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
- + retval = __mdiobus_select_page(bus, addr, page);
- + mutex_unlock(&bus->mdio_lock);
- +
- + return retval;
- +}
- +EXPORT_SYMBOL(mdiobus_select_page_nested);
- +
- +/**
- + * mdiobus_read_paged_nested - Nested version of the mdiobus_read_paged function
- + * @bus: the mii_bus struct
- + * @addr: the phy address
- + * @page: register page to access
- + * @regnum: register number to read
- + *
- + * In case of nested MDIO bus access avoid lockdep false positives by
- + * using mutex_lock_nested().
- + *
- + * NOTE: MUST NOT be called from interrupt context,
- + * because the bus read/write functions may wait for an interrupt
- + * to conclude the operation.
- + */
- +int mdiobus_read_paged_nested(struct mii_bus *bus, int addr, u16 page, u32 regnum)
- +{
- + int retval;
- +
- + mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
- + retval = __mdiobus_read_paged(bus, addr, page, regnum);
- + mutex_unlock(&bus->mdio_lock);
- +
- + return retval;
- +}
- +EXPORT_SYMBOL(mdiobus_read_paged_nested);
- +
- +/**
- + * mdiobus_select_page - Convenience function for setting the MII register page
- + * @bus: the mii_bus struct
- + * @addr: the phy address
- + * @page: the register page to set
- + *
- + * NOTE: MUST NOT be called from interrupt context,
- + * because the bus read/write functions may wait for an interrupt
- + * to conclude the operation.
- + */
- +int mdiobus_select_page(struct mii_bus *bus, int addr, u16 page)
- +{
- + int retval;
- +
- + mutex_lock(&bus->mdio_lock);
- + retval = __mdiobus_select_page(bus, addr, page);
- + mutex_unlock(&bus->mdio_lock);
- +
- + return retval;
- +}
- +EXPORT_SYMBOL(mdiobus_select_page);
- +
- +/**
- * mdiobus_read - Convenience function for reading a given MII mgmt register
- * @bus: the mii_bus struct
- * @addr: the phy address
- @@ -872,6 +1081,29 @@ int mdiobus_read(struct mii_bus *bus, in
- EXPORT_SYMBOL(mdiobus_read);
-
- /**
- + * mdiobus_read_paged - Convenience function for reading a given paged MII mgmt register
- + * @bus: the mii_bus struct
- + * @addr: the phy address
- + * @page: register page to access
- + * @regnum: register number to read
- + *
- + * NOTE: MUST NOT be called from interrupt context,
- + * because the bus read/write functions may wait for an interrupt
- + * to conclude the operation.
- + */
- +int mdiobus_read_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum)
- +{
- + int retval;
- +
- + mutex_lock(&bus->mdio_lock);
- + retval = __mdiobus_read_paged(bus, addr, page, regnum);
- + mutex_unlock(&bus->mdio_lock);
- +
- + return retval;
- +}
- +EXPORT_SYMBOL(mdiobus_read_paged);
- +
- +/**
- * mdiobus_write_nested - Nested version of the mdiobus_write function
- * @bus: the mii_bus struct
- * @addr: the phy address
- @@ -898,6 +1130,33 @@ int mdiobus_write_nested(struct mii_bus
- EXPORT_SYMBOL(mdiobus_write_nested);
-
- /**
- + * mdiobus_write_paged_nested - Nested version of the mdiobus_write_aged function
- + * @bus: the mii_bus struct
- + * @addr: the phy address
- + * @page: the register page to access
- + * @regnum: register number to write
- + * @val: value to write to @regnum
- + *
- + * In case of nested MDIO bus access avoid lockdep false positives by
- + * using mutex_lock_nested().
- + *
- + * NOTE: MUST NOT be called from interrupt context,
- + * because the bus read/write functions may wait for an interrupt
- + * to conclude the operation.
- + */
- +int mdiobus_write_paged_nested(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 val)
- +{
- + int err;
- +
- + mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
- + err = __mdiobus_write_paged(bus, addr, page, regnum, val);
- + mutex_unlock(&bus->mdio_lock);
- +
- + return err;
- +}
- +EXPORT_SYMBOL(mdiobus_write_paged_nested);
- +
- +/**
- * mdiobus_write - Convenience function for writing a given MII mgmt register
- * @bus: the mii_bus struct
- * @addr: the phy address
- @@ -921,6 +1180,30 @@ int mdiobus_write(struct mii_bus *bus, i
- EXPORT_SYMBOL(mdiobus_write);
-
- /**
- + * mdiobus_write_paged - Convenience function for writing a given paged MII mgmt register
- + * @bus: the mii_bus struct
- + * @addr: the phy address
- + * @page: the register page to access
- + * @regnum: register number to write
- + * @val: value to write to @regnum
- + *
- + * NOTE: MUST NOT be called from interrupt context,
- + * because the bus read/write functions may wait for an interrupt
- + * to conclude the operation.
- + */
- +int mdiobus_write_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 val)
- +{
- + int err;
- +
- + mutex_lock(&bus->mdio_lock);
- + err = __mdiobus_write_paged(bus, addr, page, regnum, val);
- + mutex_unlock(&bus->mdio_lock);
- +
- + return err;
- +}
- +EXPORT_SYMBOL(mdiobus_write_paged);
- +
- +/**
- * mdiobus_modify - Convenience function for modifying a given mdio device
- * register
- * @bus: the mii_bus struct
- @@ -942,6 +1225,51 @@ int mdiobus_modify(struct mii_bus *bus,
- EXPORT_SYMBOL_GPL(mdiobus_modify);
-
- /**
- + * mdiobus_modify_paged - Convenience function for modifying a given mdio device
- + * register
- + * @bus: the mii_bus struct
- + * @addr: the phy address
- + * @page: the register page to access
- + * @regnum: register number to write
- + * @mask: bit mask of bits to clear
- + * @set: bit mask of bits to set
- + */
- +int mdiobus_modify_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 mask, u16 set)
- +{
- + int err;
- +
- + mutex_lock(&bus->mdio_lock);
- + err = __mdiobus_modify_changed_paged(bus, addr, page, regnum, mask, set);
- + mutex_unlock(&bus->mdio_lock);
- +
- + return err < 0 ? err : 0;
- +}
- +EXPORT_SYMBOL_GPL(mdiobus_modify_paged);
- +
- +/**
- + * mdiobus_modify_changed_paged - Convenience function for modifying a given paged
- + * mdio device register and returning if it changed
- + * @bus: the mii_bus struct
- + * @addr: the phy address
- + * @page: the register page to access
- + * @regnum: register number to write
- + * @mask: bit mask of bits to clear
- + * @set: bit mask of bits to set
- + */
- +int mdiobus_modify_changed_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum,
- + u16 mask, u16 set)
- +{
- + int err;
- +
- + mutex_lock(&bus->mdio_lock);
- + err = __mdiobus_modify_changed_paged(bus, addr, page, regnum, mask, set);
- + mutex_unlock(&bus->mdio_lock);
- +
- + return err;
- +}
- +EXPORT_SYMBOL_GPL(mdiobus_modify_changed_paged);
- +
- +/**
- * mdio_bus_match - determine if given MDIO driver supports the given
- * MDIO device
- * @dev: target MDIO device
- --- a/drivers/net/phy/phy-core.c
- +++ b/drivers/net/phy/phy-core.c
- @@ -557,10 +557,16 @@ int __phy_read_mmd(struct phy_device *ph
- struct mii_bus *bus = phydev->mdio.bus;
- int phy_addr = phydev->mdio.addr;
-
- - mmd_phy_indirect(bus, phy_addr, devad, regnum);
- -
- - /* Read the content of the MMD's selected register */
- - val = __mdiobus_read(bus, phy_addr, MII_MMD_DATA);
- + if (bus->access_capabilities & MDIOBUS_ACCESS_C22_MMD) {
- + val = __mdiobus_c22_mmd_read(phydev->mdio.bus,
- + phydev->mdio.addr,
- + devad, regnum);
- + } else {
- + mmd_phy_indirect(bus, phy_addr, devad, regnum);
- +
- + /* Read the content of the MMD's selected register */
- + val = __mdiobus_read(bus, phy_addr, MII_MMD_DATA);
- + }
- }
- return val;
- }
- @@ -613,12 +619,18 @@ int __phy_write_mmd(struct phy_device *p
- struct mii_bus *bus = phydev->mdio.bus;
- int phy_addr = phydev->mdio.addr;
-
- - mmd_phy_indirect(bus, phy_addr, devad, regnum);
- + if (bus->access_capabilities & MDIOBUS_ACCESS_C22_MMD) {
- + ret = __mdiobus_c22_mmd_write(phydev->mdio.bus,
- + phydev->mdio.addr,
- + devad, regnum, val);
- + } else {
- + mmd_phy_indirect(bus, phy_addr, devad, regnum);
-
- - /* Write the data into MMD's selected register */
- - __mdiobus_write(bus, phy_addr, MII_MMD_DATA, val);
- + /* Write the data into MMD's selected register */
- + __mdiobus_write(bus, phy_addr, MII_MMD_DATA, val);
-
- - ret = 0;
- + ret = 0;
- + }
- }
- return ret;
- }
- @@ -824,6 +836,13 @@ EXPORT_SYMBOL_GPL(phy_modify_mmd);
-
- static int __phy_read_page(struct phy_device *phydev)
- {
- + if (phydev->drv && phydev->drv->flags & PHY_HAS_REALTEK_PAGES) {
- + struct mii_bus *bus = phydev->mdio.bus;
- + int phy_addr = phydev->mdio.addr;
- +
- + return bus->selected_page[phy_addr];
- + }
- +
- if (WARN_ONCE(!phydev->drv->read_page, "read_page callback not available, PHY driver not loaded?\n"))
- return -EOPNOTSUPP;
-
- @@ -832,6 +851,13 @@ static int __phy_read_page(struct phy_de
-
- static int __phy_write_page(struct phy_device *phydev, int page)
- {
- + if (phydev->drv && phydev->drv->flags & PHY_HAS_REALTEK_PAGES) {
- + struct mii_bus *bus = phydev->mdio.bus;
- + int phy_addr = phydev->mdio.addr;
- +
- + return __mdiobus_select_page(bus, phy_addr, page);
- + }
- +
- if (WARN_ONCE(!phydev->drv->write_page, "write_page callback not available, PHY driver not loaded?\n"))
- return -EOPNOTSUPP;
-
- @@ -933,6 +959,18 @@ int phy_read_paged(struct phy_device *ph
- {
- int ret = 0, oldpage;
-
- + if (phydev->drv && phydev->drv->flags & PHY_HAS_REALTEK_PAGES) {
- + struct mii_bus *bus = phydev->mdio.bus;
- + int phy_addr = phydev->mdio.addr;
- +
- + if (bus->read_paged) {
- + phy_lock_mdio_bus(phydev);
- + ret = bus->read_paged(bus, phy_addr, page, regnum);
- + phy_unlock_mdio_bus(phydev);
- + return ret;
- + }
- + }
- +
- oldpage = phy_select_page(phydev, page);
- if (oldpage >= 0)
- ret = __phy_read(phydev, regnum);
- @@ -954,6 +992,18 @@ int phy_write_paged(struct phy_device *p
- {
- int ret = 0, oldpage;
-
- + if (phydev->drv && phydev->drv->flags & PHY_HAS_REALTEK_PAGES) {
- + struct mii_bus *bus = phydev->mdio.bus;
- + int phy_addr = phydev->mdio.addr;
- +
- + if (bus->write_paged) {
- + phy_lock_mdio_bus(phydev);
- + ret = bus->write_paged(bus, phy_addr, page, regnum, val);
- + phy_unlock_mdio_bus(phydev);
- + return ret;
- + }
- + }
- +
- oldpage = phy_select_page(phydev, page);
- if (oldpage >= 0)
- ret = __phy_write(phydev, regnum, val);
- --- a/include/linux/mdio.h
- +++ b/include/linux/mdio.h
- @@ -14,6 +14,7 @@
- * IEEE 802.3ae clause 45 addressing mode used by 10GIGE phy chips.
- */
- #define MII_ADDR_C45 (1<<30)
- +#define MII_ADDR_C22_MMD (1<<29)
- #define MII_DEVADDR_C45_SHIFT 16
- #define MII_DEVADDR_C45_MASK GENMASK(20, 16)
- #define MII_REGADDR_C45_MASK GENMASK(15, 0)
- @@ -340,11 +341,19 @@ static inline void mii_10gbt_stat_mod_li
- advertising, lpa & MDIO_AN_10GBT_STAT_LP10G);
- }
-
- +int __mdiobus_select_page(struct mii_bus *bus, int addr, u16 page);
- int __mdiobus_read(struct mii_bus *bus, int addr, u32 regnum);
- int __mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val);
- int __mdiobus_modify_changed(struct mii_bus *bus, int addr, u32 regnum,
- u16 mask, u16 set);
-
- +int __mdiobus_read_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum);
- +int __mdiobus_write_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 val);
- +int __mdiobus_modify_changed_paged(struct mii_bus *bus, int addr, u32 regnum, u16 page,
- + u16 mask, u16 set);
- +
- +int mdiobus_select_page(struct mii_bus *bus, int addr, u16 page);
- +int mdiobus_select_page_nested(struct mii_bus *bus, int addr, u16 page);
- int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum);
- int mdiobus_read_nested(struct mii_bus *bus, int addr, u32 regnum);
- int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val);
- @@ -352,11 +361,51 @@ int mdiobus_write_nested(struct mii_bus
- int mdiobus_modify(struct mii_bus *bus, int addr, u32 regnum, u16 mask,
- u16 set);
-
- +int mdiobus_read_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum);
- +int mdiobus_read_nested_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum);
- +int mdiobus_write_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 val);
- +int mdiobus_write_nested_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 val);
- +int mdiobus_modify_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 mask,
- + u16 set);
- +int mdiobus_modify_changed_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum,
- + u16 mask, u16 set);
- +
- +static inline int mdiodev_read_paged(struct mdio_device *mdiodev, u16 page,
- + u32 regnum)
- +{
- + return mdiobus_read_paged(mdiodev->bus, mdiodev->addr, page, regnum);
- +}
- +
- +static inline int mdiodev_write_paged(struct mdio_device *mdiodev, u16 page,
- + u32 regnum, u16 val)
- +{
- + return mdiobus_write_paged(mdiodev->bus, mdiodev->addr, page, regnum, val);
- +}
- +
- +static inline int mdiodev_modify_paged(struct mdio_device *mdiodev, u16 page,
- + u32 regnum, u16 mask, u16 set)
- +{
- + return mdiobus_modify_paged(mdiodev->bus, mdiodev->addr, page, regnum,
- + mask, set);
- +}
- +
- +static inline int mdiodev_modify_changed_paged(struct mdio_device *mdiodev, u16 page,
- + u32 regnum, u16 mask, u16 set)
- +{
- + return mdiobus_modify_changed_paged(mdiodev->bus, mdiodev->addr, page, regnum,
- + mask, set);
- +}
- +
- static inline u32 mdiobus_c45_addr(int devad, u16 regnum)
- {
- return MII_ADDR_C45 | devad << MII_DEVADDR_C45_SHIFT | regnum;
- }
-
- +static inline u32 mdiobus_c22_mmd_addr(int devad, u16 regnum)
- +{
- + return MII_ADDR_C22_MMD | devad << MII_DEVADDR_C45_SHIFT | regnum;
- +}
- +
- static inline u16 mdiobus_c45_regad(u32 regnum)
- {
- return FIELD_GET(MII_REGADDR_C45_MASK, regnum);
- @@ -380,6 +429,19 @@ static inline int __mdiobus_c45_write(st
- val);
- }
-
- +static inline int __mdiobus_c22_mmd_read(struct mii_bus *bus, int prtad,
- + int devad, u16 regnum)
- +{
- + return __mdiobus_read(bus, prtad, mdiobus_c22_mmd_addr(devad, regnum));
- +}
- +
- +static inline int __mdiobus_c22_mmd_write(struct mii_bus *bus, int prtad,
- + int devad, u16 regnum, u16 val)
- +{
- + return __mdiobus_write(bus, prtad, mdiobus_c22_mmd_addr(devad, regnum),
- + val);
- +}
- +
- static inline int mdiobus_c45_read(struct mii_bus *bus, int prtad, int devad,
- u16 regnum)
- {
- --- a/include/linux/phy.h
- +++ b/include/linux/phy.h
- @@ -81,6 +81,7 @@ extern const int phy_10gbit_features_arr
- #define PHY_IS_INTERNAL 0x00000001
- #define PHY_RST_AFTER_CLK_EN 0x00000002
- #define PHY_POLL_CABLE_TEST 0x00000004
- +#define PHY_HAS_REALTEK_PAGES 0x00000010
- #define MDIO_DEVICE_IS_PHY 0x80000000
-
- /**
- @@ -428,6 +429,22 @@ struct mii_bus {
-
- /** @shared: shared state across different PHYs */
- struct phy_package_shared *shared[PHY_MAX_ADDR];
- +
- + /** @access_capabilities: hardware-assisted access capabilties */
- + enum {
- + MDIOBUS_ACCESS_SOFTWARE_ONLY = 0,
- + MDIOBUS_ACCESS_C22_MMD = 0x1,
- + } access_capabilities;
- +
- + /** @read: Perform a read transfer on the bus, offloading page access */
- + int (*read_paged)(struct mii_bus *bus, int addr, u16 page, int regnum);
- + /** @write: Perform a write transfer on the bus, offloading page access */
- + int (*write_paged)(struct mii_bus *bus, int addr, u16 page, int regnum, u16 val);
- + /** currently selected page when page access is offloaded
- + * array should be PHY_MAX_ADDR+1size, but current design of the MDIO driver
- + * uses port addresses as phy addresses and they are up to 6 bit.
- + */
- + u16 selected_page[64];
- };
- #define to_mii_bus(d) container_of(d, struct mii_bus, dev)
-
- @@ -1825,6 +1842,66 @@ static inline int __phy_package_read(str
- return __mdiobus_read(phydev->mdio.bus, shared->addr, regnum);
- }
-
- +static inline int phy_package_read_port(struct phy_device *phydev, u16 port, u32 regnum)
- +{
- + struct phy_package_shared *shared = phydev->shared;
- +
- + if (!shared)
- + return -EIO;
- +
- + return mdiobus_read(phydev->mdio.bus, shared->addr + port, regnum);
- +}
- +
- +static inline int __phy_package_read_port(struct phy_device *phydev, u16 port, u32 regnum)
- +{
- + struct phy_package_shared *shared = phydev->shared;
- +
- + if (!shared)
- + return -EIO;
- +
- + return __mdiobus_read(phydev->mdio.bus, shared->addr + port, regnum);
- +}
- +
- +static inline int phy_package_read_paged(struct phy_device *phydev, u16 page, u32 regnum)
- +{
- + struct phy_package_shared *shared = phydev->shared;
- +
- + if (!shared)
- + return -EIO;
- +
- + return mdiobus_read_paged(phydev->mdio.bus, shared->addr, page, regnum);
- +}
- +
- +static inline int __phy_package_read_paged(struct phy_device *phydev, u16 page, u32 regnum)
- +{
- + struct phy_package_shared *shared = phydev->shared;
- +
- + if (!shared)
- + return -EIO;
- +
- + return __mdiobus_read_paged(phydev->mdio.bus, shared->addr, page, regnum);
- +}
- +
- +static inline int phy_package_port_read_paged(struct phy_device *phydev, u16 port, u16 page, u32 regnum)
- +{
- + struct phy_package_shared *shared = phydev->shared;
- +
- + if (!shared)
- + return -EIO;
- +
- + return mdiobus_read_paged(phydev->mdio.bus, shared->addr + port, page, regnum);
- +}
- +
- +static inline int __phy_package_port_read_paged(struct phy_device *phydev, u16 port, u16 page, u32 regnum)
- +{
- + struct phy_package_shared *shared = phydev->shared;
- +
- + if (!shared)
- + return -EIO;
- +
- + return __mdiobus_read_paged(phydev->mdio.bus, shared->addr + port, page, regnum);
- +}
- +
- static inline int phy_package_write(struct phy_device *phydev,
- u32 regnum, u16 val)
- {
- @@ -1847,6 +1924,72 @@ static inline int __phy_package_write(st
- return __mdiobus_write(phydev->mdio.bus, shared->addr, regnum, val);
- }
-
- +static inline int phy_package_port_write(struct phy_device *phydev,
- + u16 port, u32 regnum, u16 val)
- +{
- + struct phy_package_shared *shared = phydev->shared;
- +
- + if (!shared)
- + return -EIO;
- +
- + return mdiobus_write(phydev->mdio.bus, shared->addr + port, regnum, val);
- +}
- +
- +static inline int __phy_package_port_write(struct phy_device *phydev,
- + u16 port, u32 regnum, u16 val)
- +{
- + struct phy_package_shared *shared = phydev->shared;
- +
- + if (!shared)
- + return -EIO;
- +
- + return __mdiobus_write(phydev->mdio.bus, shared->addr + port, regnum, val);
- +}
- +
- +static inline int phy_package_port_write_paged(struct phy_device *phydev,
- + u16 port, u16 page, u32 regnum, u16 val)
- +{
- + struct phy_package_shared *shared = phydev->shared;
- +
- + if (!shared)
- + return -EIO;
- +
- + return mdiobus_write_paged(phydev->mdio.bus, shared->addr + port, page, regnum, val);
- +}
- +
- +static inline int __phy_package_port_write_paged(struct phy_device *phydev,
- + u16 port, u16 page, u32 regnum, u16 val)
- +{
- + struct phy_package_shared *shared = phydev->shared;
- +
- + if (!shared)
- + return -EIO;
- +
- + return __mdiobus_write_paged(phydev->mdio.bus, shared->addr + port, page, regnum, val);
- +}
- +
- +static inline int phy_package_write_paged(struct phy_device *phydev,
- + u16 page, u32 regnum, u16 val)
- +{
- + struct phy_package_shared *shared = phydev->shared;
- +
- + if (!shared)
- + return -EIO;
- +
- + return mdiobus_write_paged(phydev->mdio.bus, shared->addr, page, regnum, val);
- +}
- +
- +static inline int __phy_package_write_paged(struct phy_device *phydev,
- + u16 page, u32 regnum, u16 val)
- +{
- + struct phy_package_shared *shared = phydev->shared;
- +
- + if (!shared)
- + return -EIO;
- +
- + return __mdiobus_write_paged(phydev->mdio.bus, shared->addr, page, regnum, val);
- +}
- +
- static inline bool __phy_package_set_once(struct phy_device *phydev,
- unsigned int b)
- {
- --- a/include/uapi/linux/mii.h
- +++ b/include/uapi/linux/mii.h
- @@ -36,6 +36,7 @@
- #define MII_RESV2 0x1a /* Reserved... */
- #define MII_TPISTATUS 0x1b /* TPI status for 10mbps */
- #define MII_NCONFIG 0x1c /* Network interface config */
- +#define MII_MAINPAGE 0x1f /* Page register */
-
- /* Basic mode control register. */
- #define BMCR_RESV 0x003f /* Unused... */
|