2017-05-26 01:03:20 +02:00
/*
* Marvell 88e6 xxx Ethernet switch PHY and PPU support
*
* Copyright ( c ) 2008 Marvell Semiconductor
*
* Copyright ( c ) 2017 Andrew Lunn < andrew @ lunn . ch >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*/
# include <linux/mdio.h>
# include <linux/module.h>
2017-06-02 17:06:15 -04:00
# include "chip.h"
2017-05-26 01:03:20 +02:00
# include "phy.h"
int mv88e6165_phy_read ( struct mv88e6xxx_chip * chip , struct mii_bus * bus ,
int addr , int reg , u16 * val )
{
return mv88e6xxx_read ( chip , addr , reg , val ) ;
}
int mv88e6165_phy_write ( struct mv88e6xxx_chip * chip , struct mii_bus * bus ,
int addr , int reg , u16 val )
{
return mv88e6xxx_write ( chip , addr , reg , val ) ;
}
int mv88e6xxx_phy_read ( struct mv88e6xxx_chip * chip , int phy , int reg , u16 * val )
{
int addr = phy ; /* PHY devices addresses start at 0x0 */
struct mii_bus * bus ;
bus = mv88e6xxx_default_mdio_bus ( chip ) ;
if ( ! bus )
return - EOPNOTSUPP ;
if ( ! chip - > info - > ops - > phy_read )
return - EOPNOTSUPP ;
return chip - > info - > ops - > phy_read ( chip , bus , addr , reg , val ) ;
}
int mv88e6xxx_phy_write ( struct mv88e6xxx_chip * chip , int phy , int reg , u16 val )
{
int addr = phy ; /* PHY devices addresses start at 0x0 */
struct mii_bus * bus ;
bus = mv88e6xxx_default_mdio_bus ( chip ) ;
if ( ! bus )
return - EOPNOTSUPP ;
if ( ! chip - > info - > ops - > phy_write )
return - EOPNOTSUPP ;
return chip - > info - > ops - > phy_write ( chip , bus , addr , reg , val ) ;
}
static int mv88e6xxx_phy_page_get ( struct mv88e6xxx_chip * chip , int phy , u8 page )
{
2017-06-08 18:34:14 -04:00
return mv88e6xxx_phy_write ( chip , phy , MV88E6XXX_PHY_PAGE , page ) ;
2017-05-26 01:03:20 +02:00
}
static void mv88e6xxx_phy_page_put ( struct mv88e6xxx_chip * chip , int phy )
{
int err ;
/* Restore PHY page Copper 0x0 for access via the registered
* MDIO bus
*/
2017-06-08 18:34:14 -04:00
err = mv88e6xxx_phy_write ( chip , phy , MV88E6XXX_PHY_PAGE ,
MV88E6XXX_PHY_PAGE_COPPER ) ;
2017-05-26 01:03:20 +02:00
if ( unlikely ( err ) ) {
dev_err ( chip - > dev ,
" failed to restore PHY %d page Copper (%d) \n " ,
phy , err ) ;
}
}
int mv88e6xxx_phy_page_read ( struct mv88e6xxx_chip * chip , int phy ,
u8 page , int reg , u16 * val )
{
int err ;
/* There is no paging for registers 22 */
2017-06-08 18:34:14 -04:00
if ( reg = = MV88E6XXX_PHY_PAGE )
2017-05-26 01:03:20 +02:00
return - EINVAL ;
err = mv88e6xxx_phy_page_get ( chip , phy , page ) ;
if ( ! err ) {
err = mv88e6xxx_phy_read ( chip , phy , reg , val ) ;
mv88e6xxx_phy_page_put ( chip , phy ) ;
}
return err ;
}
int mv88e6xxx_phy_page_write ( struct mv88e6xxx_chip * chip , int phy ,
u8 page , int reg , u16 val )
{
int err ;
/* There is no paging for registers 22 */
2017-06-08 18:34:14 -04:00
if ( reg = = MV88E6XXX_PHY_PAGE )
2017-05-26 01:03:20 +02:00
return - EINVAL ;
err = mv88e6xxx_phy_page_get ( chip , phy , page ) ;
if ( ! err ) {
2017-06-08 18:34:14 -04:00
err = mv88e6xxx_phy_write ( chip , phy , MV88E6XXX_PHY_PAGE , page ) ;
2017-05-26 01:03:20 +02:00
mv88e6xxx_phy_page_put ( chip , phy ) ;
}
return err ;
}
2017-05-26 18:03:07 -04:00
static int mv88e6xxx_phy_ppu_disable ( struct mv88e6xxx_chip * chip )
2017-05-26 01:03:20 +02:00
{
if ( ! chip - > info - > ops - > ppu_disable )
return 0 ;
return chip - > info - > ops - > ppu_disable ( chip ) ;
}
2017-05-26 18:03:07 -04:00
static int mv88e6xxx_phy_ppu_enable ( struct mv88e6xxx_chip * chip )
2017-05-26 01:03:20 +02:00
{
if ( ! chip - > info - > ops - > ppu_enable )
return 0 ;
return chip - > info - > ops - > ppu_enable ( chip ) ;
}
2017-05-26 18:03:07 -04:00
static void mv88e6xxx_phy_ppu_reenable_work ( struct work_struct * ugly )
2017-05-26 01:03:20 +02:00
{
struct mv88e6xxx_chip * chip ;
chip = container_of ( ugly , struct mv88e6xxx_chip , ppu_work ) ;
mutex_lock ( & chip - > reg_lock ) ;
if ( mutex_trylock ( & chip - > ppu_mutex ) ) {
2017-05-26 18:03:07 -04:00
if ( mv88e6xxx_phy_ppu_enable ( chip ) = = 0 )
2017-05-26 01:03:20 +02:00
chip - > ppu_disabled = 0 ;
mutex_unlock ( & chip - > ppu_mutex ) ;
}
mutex_unlock ( & chip - > reg_lock ) ;
}
2017-05-26 18:03:07 -04:00
static void mv88e6xxx_phy_ppu_reenable_timer ( unsigned long _ps )
2017-05-26 01:03:20 +02:00
{
struct mv88e6xxx_chip * chip = ( void * ) _ps ;
schedule_work ( & chip - > ppu_work ) ;
}
2017-05-26 18:03:07 -04:00
static int mv88e6xxx_phy_ppu_access_get ( struct mv88e6xxx_chip * chip )
2017-05-26 01:03:20 +02:00
{
int ret ;
mutex_lock ( & chip - > ppu_mutex ) ;
/* If the PHY polling unit is enabled, disable it so that
* we can access the PHY registers . If it was already
* disabled , cancel the timer that is going to re - enable
* it .
*/
if ( ! chip - > ppu_disabled ) {
2017-05-26 18:03:07 -04:00
ret = mv88e6xxx_phy_ppu_disable ( chip ) ;
2017-05-26 01:03:20 +02:00
if ( ret < 0 ) {
mutex_unlock ( & chip - > ppu_mutex ) ;
return ret ;
}
chip - > ppu_disabled = 1 ;
} else {
del_timer ( & chip - > ppu_timer ) ;
ret = 0 ;
}
return ret ;
}
2017-05-26 18:03:07 -04:00
static void mv88e6xxx_phy_ppu_access_put ( struct mv88e6xxx_chip * chip )
2017-05-26 01:03:20 +02:00
{
/* Schedule a timer to re-enable the PHY polling unit. */
mod_timer ( & chip - > ppu_timer , jiffies + msecs_to_jiffies ( 10 ) ) ;
mutex_unlock ( & chip - > ppu_mutex ) ;
}
2017-05-26 18:03:07 -04:00
static void mv88e6xxx_phy_ppu_state_init ( struct mv88e6xxx_chip * chip )
2017-05-26 01:03:20 +02:00
{
mutex_init ( & chip - > ppu_mutex ) ;
2017-05-26 18:03:07 -04:00
INIT_WORK ( & chip - > ppu_work , mv88e6xxx_phy_ppu_reenable_work ) ;
setup_timer ( & chip - > ppu_timer , mv88e6xxx_phy_ppu_reenable_timer ,
2017-05-26 01:03:20 +02:00
( unsigned long ) chip ) ;
}
2017-05-26 18:03:07 -04:00
static void mv88e6xxx_phy_ppu_state_destroy ( struct mv88e6xxx_chip * chip )
2017-05-26 01:03:20 +02:00
{
del_timer_sync ( & chip - > ppu_timer ) ;
}
2017-05-26 18:03:06 -04:00
int mv88e6185_phy_ppu_read ( struct mv88e6xxx_chip * chip , struct mii_bus * bus ,
2017-05-26 01:03:20 +02:00
int addr , int reg , u16 * val )
{
int err ;
2017-05-26 18:03:07 -04:00
err = mv88e6xxx_phy_ppu_access_get ( chip ) ;
2017-05-26 01:03:20 +02:00
if ( ! err ) {
err = mv88e6xxx_read ( chip , addr , reg , val ) ;
2017-05-26 18:03:07 -04:00
mv88e6xxx_phy_ppu_access_put ( chip ) ;
2017-05-26 01:03:20 +02:00
}
return err ;
}
2017-05-26 18:03:06 -04:00
int mv88e6185_phy_ppu_write ( struct mv88e6xxx_chip * chip , struct mii_bus * bus ,
2017-05-26 01:03:20 +02:00
int addr , int reg , u16 val )
{
int err ;
2017-05-26 18:03:07 -04:00
err = mv88e6xxx_phy_ppu_access_get ( chip ) ;
2017-05-26 01:03:20 +02:00
if ( ! err ) {
err = mv88e6xxx_write ( chip , addr , reg , val ) ;
2017-05-26 18:03:07 -04:00
mv88e6xxx_phy_ppu_access_put ( chip ) ;
2017-05-26 01:03:20 +02:00
}
return err ;
}
void mv88e6xxx_phy_init ( struct mv88e6xxx_chip * chip )
{
if ( chip - > info - > ops - > ppu_enable & & chip - > info - > ops - > ppu_disable )
2017-05-26 18:03:07 -04:00
mv88e6xxx_phy_ppu_state_init ( chip ) ;
2017-05-26 01:03:20 +02:00
}
void mv88e6xxx_phy_destroy ( struct mv88e6xxx_chip * chip )
{
if ( chip - > info - > ops - > ppu_enable & & chip - > info - > ops - > ppu_disable )
2017-05-26 18:03:07 -04:00
mv88e6xxx_phy_ppu_state_destroy ( chip ) ;
2017-05-26 01:03:20 +02:00
}
2017-05-26 18:03:05 -04:00
int mv88e6xxx_phy_setup ( struct mv88e6xxx_chip * chip )
{
2017-05-26 18:03:07 -04:00
return mv88e6xxx_phy_ppu_enable ( chip ) ;
2017-05-26 18:03:05 -04:00
}