Skip to content

Commit 78ffc4a

Browse files
Russell Kingdavem330
authored andcommitted
net: phy: add paged phy register accessors
Add a set of paged phy register accessors which are inherently safe in their design against other accesses interfering with the paged access. Signed-off-by: Russell King <[email protected]> Reviewed-by: Andrew Lunn <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 788f993 commit 78ffc4a

File tree

2 files changed

+168
-0
lines changed

2 files changed

+168
-0
lines changed

drivers/net/phy/phy-core.c

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,3 +348,160 @@ int __phy_modify(struct phy_device *phydev, u32 regnum, u16 mask, u16 set)
348348
return ret;
349349
}
350350
EXPORT_SYMBOL_GPL(__phy_modify);
351+
352+
static int __phy_read_page(struct phy_device *phydev)
353+
{
354+
return phydev->drv->read_page(phydev);
355+
}
356+
357+
static int __phy_write_page(struct phy_device *phydev, int page)
358+
{
359+
return phydev->drv->write_page(phydev, page);
360+
}
361+
362+
/**
363+
* phy_save_page() - take the bus lock and save the current page
364+
* @phydev: a pointer to a &struct phy_device
365+
*
366+
* Take the MDIO bus lock, and return the current page number. On error,
367+
* returns a negative errno. phy_restore_page() must always be called
368+
* after this, irrespective of success or failure of this call.
369+
*/
370+
int phy_save_page(struct phy_device *phydev)
371+
{
372+
mutex_lock(&phydev->mdio.bus->mdio_lock);
373+
return __phy_read_page(phydev);
374+
}
375+
EXPORT_SYMBOL_GPL(phy_save_page);
376+
377+
/**
378+
* phy_select_page() - take the bus lock, save the current page, and set a page
379+
* @phydev: a pointer to a &struct phy_device
380+
* @page: desired page
381+
*
382+
* Take the MDIO bus lock to protect against concurrent access, save the
383+
* current PHY page, and set the current page. On error, returns a
384+
* negative errno, otherwise returns the previous page number.
385+
* phy_restore_page() must always be called after this, irrespective
386+
* of success or failure of this call.
387+
*/
388+
int phy_select_page(struct phy_device *phydev, int page)
389+
{
390+
int ret, oldpage;
391+
392+
oldpage = ret = phy_save_page(phydev);
393+
if (ret < 0)
394+
return ret;
395+
396+
if (oldpage != page) {
397+
ret = __phy_write_page(phydev, page);
398+
if (ret < 0)
399+
return ret;
400+
}
401+
402+
return oldpage;
403+
}
404+
EXPORT_SYMBOL_GPL(phy_select_page);
405+
406+
/**
407+
* phy_restore_page() - restore the page register and release the bus lock
408+
* @phydev: a pointer to a &struct phy_device
409+
* @oldpage: the old page, return value from phy_save_page() or phy_select_page()
410+
* @ret: operation's return code
411+
*
412+
* Release the MDIO bus lock, restoring @oldpage if it is a valid page.
413+
* This function propagates the earliest error code from the group of
414+
* operations.
415+
*
416+
* Returns:
417+
* @oldpage if it was a negative value, otherwise
418+
* @ret if it was a negative errno value, otherwise
419+
* phy_write_page()'s negative value if it were in error, otherwise
420+
* @ret.
421+
*/
422+
int phy_restore_page(struct phy_device *phydev, int oldpage, int ret)
423+
{
424+
int r;
425+
426+
if (oldpage >= 0) {
427+
r = __phy_write_page(phydev, oldpage);
428+
429+
/* Propagate the operation return code if the page write
430+
* was successful.
431+
*/
432+
if (ret >= 0 && r < 0)
433+
ret = r;
434+
} else {
435+
/* Propagate the phy page selection error code */
436+
ret = oldpage;
437+
}
438+
439+
mutex_unlock(&phydev->mdio.bus->mdio_lock);
440+
441+
return ret;
442+
}
443+
EXPORT_SYMBOL_GPL(phy_restore_page);
444+
445+
/**
446+
* phy_read_paged() - Convenience function for reading a paged register
447+
* @phydev: a pointer to a &struct phy_device
448+
* @page: the page for the phy
449+
* @regnum: register number
450+
*
451+
* Same rules as for phy_read().
452+
*/
453+
int phy_read_paged(struct phy_device *phydev, int page, u32 regnum)
454+
{
455+
int ret = 0, oldpage;
456+
457+
oldpage = phy_select_page(phydev, page);
458+
if (oldpage >= 0)
459+
ret = __phy_read(phydev, regnum);
460+
461+
return phy_restore_page(phydev, oldpage, ret);
462+
}
463+
EXPORT_SYMBOL(phy_read_paged);
464+
465+
/**
466+
* phy_write_paged() - Convenience function for writing a paged register
467+
* @phydev: a pointer to a &struct phy_device
468+
* @page: the page for the phy
469+
* @regnum: register number
470+
* @val: value to write
471+
*
472+
* Same rules as for phy_write().
473+
*/
474+
int phy_write_paged(struct phy_device *phydev, int page, u32 regnum, u16 val)
475+
{
476+
int ret = 0, oldpage;
477+
478+
oldpage = phy_select_page(phydev, page);
479+
if (oldpage >= 0)
480+
ret = __phy_write(phydev, regnum, val);
481+
482+
return phy_restore_page(phydev, oldpage, ret);
483+
}
484+
EXPORT_SYMBOL(phy_write_paged);
485+
486+
/**
487+
* phy_modify_paged() - Convenience function for modifying a paged register
488+
* @phydev: a pointer to a &struct phy_device
489+
* @page: the page for the phy
490+
* @regnum: register number
491+
* @mask: bit mask of bits to clear
492+
* @set: bit mask of bits to set
493+
*
494+
* Same rules as for phy_read() and phy_write().
495+
*/
496+
int phy_modify_paged(struct phy_device *phydev, int page, u32 regnum,
497+
u16 mask, u16 set)
498+
{
499+
int ret = 0, oldpage;
500+
501+
oldpage = phy_select_page(phydev, page);
502+
if (oldpage >= 0)
503+
ret = __phy_modify(phydev, regnum, mask, set);
504+
505+
return phy_restore_page(phydev, oldpage, ret);
506+
}
507+
EXPORT_SYMBOL(phy_modify_paged);

include/linux/phy.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,9 @@ struct phy_driver {
634634
int (*write_mmd)(struct phy_device *dev, int devnum, u16 regnum,
635635
u16 val);
636636

637+
int (*read_page)(struct phy_device *dev);
638+
int (*write_page)(struct phy_device *dev, int page);
639+
637640
/* Get the size and type of the eeprom contained within a plug-in
638641
* module */
639642
int (*module_info)(struct phy_device *dev,
@@ -838,6 +841,14 @@ static inline bool phy_is_pseudo_fixed_link(struct phy_device *phydev)
838841
*/
839842
int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val);
840843

844+
int phy_save_page(struct phy_device *phydev);
845+
int phy_select_page(struct phy_device *phydev, int page);
846+
int phy_restore_page(struct phy_device *phydev, int oldpage, int ret);
847+
int phy_read_paged(struct phy_device *phydev, int page, u32 regnum);
848+
int phy_write_paged(struct phy_device *phydev, int page, u32 regnum, u16 val);
849+
int phy_modify_paged(struct phy_device *phydev, int page, u32 regnum,
850+
u16 mask, u16 set);
851+
841852
struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id,
842853
bool is_c45,
843854
struct phy_c45_device_ids *c45_ids);

0 commit comments

Comments
 (0)