2016-10-17 10:49:55 -05:00
/* Copyright (C) 2016 National Instruments Corp.
*
* 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
# include <linux/leds.h>
# include <linux/phy.h>
2017-01-25 11:39:49 +01:00
# include <linux/phy_led_triggers.h>
2016-10-17 10:49:55 -05:00
# include <linux/netdevice.h>
static struct phy_led_trigger * phy_speed_to_led_trigger ( struct phy_device * phy ,
unsigned int speed )
{
unsigned int i ;
for ( i = 0 ; i < phy - > phy_num_led_triggers ; i + + ) {
if ( phy - > phy_led_triggers [ i ] . speed = = speed )
return & phy - > phy_led_triggers [ i ] ;
}
return NULL ;
}
void phy_led_trigger_change_speed ( struct phy_device * phy )
{
struct phy_led_trigger * plt ;
if ( ! phy - > link )
goto out_change_speed ;
if ( phy - > speed = = 0 )
return ;
plt = phy_speed_to_led_trigger ( phy , phy - > speed ) ;
if ( ! plt ) {
netdev_alert ( phy - > attached_dev ,
" No phy led trigger registered for speed(%d) \n " ,
phy - > speed ) ;
goto out_change_speed ;
}
if ( plt ! = phy - > last_triggered ) {
led_trigger_event ( & phy - > last_triggered - > trigger , LED_OFF ) ;
led_trigger_event ( & plt - > trigger , LED_FULL ) ;
phy - > last_triggered = plt ;
}
return ;
out_change_speed :
if ( phy - > last_triggered ) {
led_trigger_event ( & phy - > last_triggered - > trigger ,
LED_OFF ) ;
phy - > last_triggered = NULL ;
}
}
EXPORT_SYMBOL_GPL ( phy_led_trigger_change_speed ) ;
static int phy_led_trigger_register ( struct phy_device * phy ,
struct phy_led_trigger * plt ,
unsigned int speed )
{
char name_suffix [ PHY_LED_TRIGGER_SPEED_SUFFIX_SIZE ] ;
plt - > speed = speed ;
if ( speed < SPEED_1000 )
snprintf ( name_suffix , sizeof ( name_suffix ) , " %dMbps " , speed ) ;
else if ( speed = = SPEED_2500 )
snprintf ( name_suffix , sizeof ( name_suffix ) , " 2.5Gbps " ) ;
else
snprintf ( name_suffix , sizeof ( name_suffix ) , " %dGbps " ,
DIV_ROUND_CLOSEST ( speed , 1000 ) ) ;
snprintf ( plt - > name , sizeof ( plt - > name ) , PHY_ID_FMT " :%s " ,
phy - > mdio . bus - > id , phy - > mdio . addr , name_suffix ) ;
plt - > trigger . name = plt - > name ;
return led_trigger_register ( & plt - > trigger ) ;
}
static void phy_led_trigger_unregister ( struct phy_led_trigger * plt )
{
led_trigger_unregister ( & plt - > trigger ) ;
}
int phy_led_triggers_register ( struct phy_device * phy )
{
int i , err ;
unsigned int speeds [ 50 ] ;
phy - > phy_num_led_triggers = phy_supported_speeds ( phy , speeds ,
ARRAY_SIZE ( speeds ) ) ;
if ( ! phy - > phy_num_led_triggers )
return 0 ;
phy - > phy_led_triggers = devm_kzalloc ( & phy - > mdio . dev ,
sizeof ( struct phy_led_trigger ) *
phy - > phy_num_led_triggers ,
GFP_KERNEL ) ;
2017-01-25 11:39:48 +01:00
if ( ! phy - > phy_led_triggers ) {
err = - ENOMEM ;
goto out_clear ;
}
2016-10-17 10:49:55 -05:00
for ( i = 0 ; i < phy - > phy_num_led_triggers ; i + + ) {
err = phy_led_trigger_register ( phy , & phy - > phy_led_triggers [ i ] ,
speeds [ i ] ) ;
if ( err )
goto out_unreg ;
}
phy - > last_triggered = NULL ;
phy_led_trigger_change_speed ( phy ) ;
return 0 ;
out_unreg :
while ( i - - )
phy_led_trigger_unregister ( & phy - > phy_led_triggers [ i ] ) ;
devm_kfree ( & phy - > mdio . dev , phy - > phy_led_triggers ) ;
2017-01-25 11:39:48 +01:00
out_clear :
phy - > phy_num_led_triggers = 0 ;
2016-10-17 10:49:55 -05:00
return err ;
}
EXPORT_SYMBOL_GPL ( phy_led_triggers_register ) ;
void phy_led_triggers_unregister ( struct phy_device * phy )
{
int i ;
for ( i = 0 ; i < phy - > phy_num_led_triggers ; i + + )
phy_led_trigger_unregister ( & phy - > phy_led_triggers [ i ] ) ;
}
EXPORT_SYMBOL_GPL ( phy_led_triggers_unregister ) ;