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 ;
}
2017-11-02 00:48:45 +01:00
static void phy_led_trigger_no_link ( struct phy_device * phy )
{
if ( phy - > last_triggered ) {
led_trigger_event ( & phy - > last_triggered - > trigger , LED_OFF ) ;
2017-11-02 00:49:18 +01:00
led_trigger_event ( & phy - > led_link_trigger - > trigger , LED_OFF ) ;
2017-11-02 00:48:45 +01:00
phy - > last_triggered = NULL ;
}
}
2016-10-17 10:49:55 -05:00
void phy_led_trigger_change_speed ( struct phy_device * phy )
{
struct phy_led_trigger * plt ;
if ( ! phy - > link )
2017-11-02 00:48:45 +01:00
return phy_led_trigger_no_link ( phy ) ;
2016-10-17 10:49:55 -05:00
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 ) ;
2017-11-02 00:48:45 +01:00
return phy_led_trigger_no_link ( phy ) ;
2016-10-17 10:49:55 -05:00
}
if ( plt ! = phy - > last_triggered ) {
2017-11-02 00:49:18 +01:00
if ( ! phy - > last_triggered )
led_trigger_event ( & phy - > led_link_trigger - > trigger ,
LED_FULL ) ;
2016-10-17 10:49:55 -05:00
led_trigger_event ( & phy - > last_triggered - > trigger , LED_OFF ) ;
led_trigger_event ( & plt - > trigger , LED_FULL ) ;
phy - > last_triggered = plt ;
}
}
EXPORT_SYMBOL_GPL ( phy_led_trigger_change_speed ) ;
2017-11-02 00:49:18 +01:00
static void phy_led_trigger_format_name ( struct phy_device * phy , char * buf ,
size_t size , char * suffix )
{
snprintf ( buf , size , PHY_ID_FMT " :%s " ,
phy - > mdio . bus - > id , phy - > mdio . addr , suffix ) ;
}
2016-10-17 10:49:55 -05:00
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 ) ) ;
2017-11-02 00:49:18 +01:00
phy_led_trigger_format_name ( phy , plt - > name , sizeof ( plt - > name ) ,
name_suffix ) ;
2016-10-17 10:49:55 -05:00
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 ;
2017-11-02 00:49:18 +01:00
phy - > led_link_trigger = devm_kzalloc ( & phy - > mdio . dev ,
sizeof ( * phy - > led_link_trigger ) ,
GFP_KERNEL ) ;
if ( ! phy - > led_link_trigger ) {
err = - ENOMEM ;
goto out_clear ;
}
phy_led_trigger_format_name ( phy , phy - > led_link_trigger - > name ,
sizeof ( phy - > led_link_trigger - > name ) ,
" link " ) ;
phy - > led_link_trigger - > trigger . name = phy - > led_link_trigger - > name ;
err = led_trigger_register ( & phy - > led_link_trigger - > trigger ) ;
if ( err )
goto out_free_link ;
2016-10-17 10:49:55 -05:00
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 ;
2017-11-02 00:49:18 +01:00
goto out_unreg_link ;
2017-01-25 11:39:48 +01:00
}
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-11-02 00:49:18 +01:00
out_unreg_link :
phy_led_trigger_unregister ( phy - > led_link_trigger ) ;
out_free_link :
devm_kfree ( & phy - > mdio . dev , phy - > led_link_trigger ) ;
phy - > led_link_trigger = NULL ;
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 ] ) ;
2017-11-02 00:49:18 +01:00
if ( phy - > led_link_trigger )
phy_led_trigger_unregister ( phy - > led_link_trigger ) ;
2016-10-17 10:49:55 -05:00
}
EXPORT_SYMBOL_GPL ( phy_led_triggers_unregister ) ;