2014-06-03 22:04:03 +02:00
/*
* Thunderbolt Cactus Ridge driver - capabilities lookup
*
* Copyright ( c ) 2014 Andreas Noever < andreas . noever @ gmail . com >
*/
# include <linux/slab.h>
# include <linux/errno.h>
# include "tb.h"
2017-06-06 15:24:58 +03:00
# define CAP_OFFSET_MAX 0xff
# define VSE_CAP_OFFSET_MAX 0xffff
2014-06-03 22:04:03 +02:00
struct tb_cap_any {
union {
struct tb_cap_basic basic ;
struct tb_cap_extended_short extended_short ;
struct tb_cap_extended_long extended_long ;
} ;
} __packed ;
2017-06-06 15:24:58 +03:00
/**
* tb_port_find_cap ( ) - Find port capability
* @ port : Port to find the capability for
* @ cap : Capability to look
*
* Returns offset to start of capability or % - ENOENT if no such
* capability was found . Negative errno is returned if there was an
* error .
*/
int tb_port_find_cap ( struct tb_port * port , enum tb_port_cap cap )
2014-06-03 22:04:03 +02:00
{
2017-06-06 15:24:58 +03:00
u32 offset ;
2014-06-03 22:04:03 +02:00
2017-06-06 15:24:58 +03:00
/*
* DP out adapters claim to implement TMU capability but in
* reality they do not so we hard code the adapter specific
* capability offset here .
*/
if ( port - > config . type = = TB_TYPE_DP_HDMI_OUT )
offset = 0x39 ;
2014-06-03 22:04:03 +02:00
else
2017-06-06 15:24:58 +03:00
offset = 0x1 ;
do {
struct tb_cap_any header ;
int ret ;
ret = tb_port_read ( port , & header , TB_CFG_PORT , offset , 1 ) ;
if ( ret )
return ret ;
if ( header . basic . cap = = cap )
return offset ;
offset = header . basic . next ;
} while ( offset ) ;
return - ENOENT ;
2014-06-03 22:04:03 +02:00
}
2017-06-06 15:24:58 +03:00
static int tb_switch_find_cap ( struct tb_switch * sw , enum tb_switch_cap cap )
2014-06-03 22:04:03 +02:00
{
2017-06-06 15:24:58 +03:00
int offset = sw - > config . first_cap_offset ;
while ( offset > 0 & & offset < CAP_OFFSET_MAX ) {
struct tb_cap_any header ;
int ret ;
ret = tb_sw_read ( sw , & header , TB_CFG_SWITCH , offset , 1 ) ;
if ( ret )
return ret ;
if ( header . basic . cap = = cap )
return offset ;
offset = header . basic . next ;
2014-06-03 22:04:03 +02:00
}
2017-06-06 15:24:58 +03:00
return - ENOENT ;
2014-06-03 22:04:03 +02:00
}
/**
2017-06-06 15:24:58 +03:00
* tb_switch_find_vse_cap ( ) - Find switch vendor specific capability
* @ sw : Switch to find the capability for
* @ vsec : Vendor specific capability to look
2014-06-03 22:04:03 +02:00
*
2017-06-06 15:24:58 +03:00
* Functions enumerates vendor specific capabilities ( VSEC ) of a switch
* and returns offset when capability matching @ vsec is found . If no
* such capability is found returns % - ENOENT . In case of error returns
* negative errno .
2014-06-03 22:04:03 +02:00
*/
2017-06-06 15:24:58 +03:00
int tb_switch_find_vse_cap ( struct tb_switch * sw , enum tb_switch_vse_cap vsec )
2014-06-03 22:04:03 +02:00
{
struct tb_cap_any header ;
2017-06-06 15:24:58 +03:00
int offset ;
offset = tb_switch_find_cap ( sw , TB_SWITCH_CAP_VSE ) ;
if ( offset < 0 )
return offset ;
while ( offset > 0 & & offset < VSE_CAP_OFFSET_MAX ) {
int ret ;
ret = tb_sw_read ( sw , & header , TB_CFG_SWITCH , offset , 2 ) ;
if ( ret )
return ret ;
/*
* Extended vendor specific capabilities come in two
* flavors : short and long . The latter is used when
* offset is over 0xff .
*/
if ( offset > = CAP_OFFSET_MAX ) {
if ( header . extended_long . vsec_id = = vsec )
2014-06-03 22:04:03 +02:00
return offset ;
2017-06-06 15:24:58 +03:00
offset = header . extended_long . next ;
} else {
if ( header . extended_short . vsec_id = = vsec )
return offset ;
if ( ! header . extended_short . length )
return - ENOENT ;
offset = header . extended_short . next ;
2014-06-03 22:04:03 +02:00
}
}
2017-06-06 15:24:58 +03:00
return - ENOENT ;
2014-06-03 22:04:03 +02:00
}