2017-05-01 14:05:12 -04:00
/*
* Marvell 88E6 xxx VLAN [ Spanning Tree ] Translation Unit ( VTU [ STU ] ) support
*
* Copyright ( c ) 2008 Marvell Semiconductor
* Copyright ( c ) 2015 CMC Electronics , Inc .
* Copyright ( c ) 2017 Savoir - faire Linux , Inc .
*
* 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 "mv88e6xxx.h"
# include "global1.h"
2017-05-01 14:05:14 -04:00
/* Offset 0x02: VTU FID Register */
2017-05-01 14:05:24 -04:00
static int mv88e6xxx_g1_vtu_fid_read ( struct mv88e6xxx_chip * chip ,
struct mv88e6xxx_vtu_entry * entry )
2017-05-01 14:05:14 -04:00
{
u16 val ;
int err ;
err = mv88e6xxx_g1_read ( chip , GLOBAL_VTU_FID , & val ) ;
if ( err )
return err ;
entry - > fid = val & GLOBAL_VTU_FID_MASK ;
return 0 ;
}
2017-05-01 14:05:24 -04:00
static int mv88e6xxx_g1_vtu_fid_write ( struct mv88e6xxx_chip * chip ,
struct mv88e6xxx_vtu_entry * entry )
2017-05-01 14:05:14 -04:00
{
u16 val = entry - > fid & GLOBAL_VTU_FID_MASK ;
return mv88e6xxx_g1_write ( chip , GLOBAL_VTU_FID , val ) ;
}
2017-05-01 14:05:15 -04:00
/* Offset 0x03: VTU SID Register */
2017-05-01 14:05:24 -04:00
static int mv88e6xxx_g1_vtu_sid_read ( struct mv88e6xxx_chip * chip ,
struct mv88e6xxx_vtu_entry * entry )
2017-05-01 14:05:15 -04:00
{
u16 val ;
int err ;
err = mv88e6xxx_g1_read ( chip , GLOBAL_VTU_SID , & val ) ;
if ( err )
return err ;
entry - > sid = val & GLOBAL_VTU_SID_MASK ;
return 0 ;
}
2017-05-01 14:05:24 -04:00
static int mv88e6xxx_g1_vtu_sid_write ( struct mv88e6xxx_chip * chip ,
struct mv88e6xxx_vtu_entry * entry )
2017-05-01 14:05:15 -04:00
{
u16 val = entry - > sid & GLOBAL_VTU_SID_MASK ;
return mv88e6xxx_g1_write ( chip , GLOBAL_VTU_SID , val ) ;
}
2017-05-01 14:05:12 -04:00
/* Offset 0x05: VTU Operation Register */
2017-05-01 14:05:24 -04:00
static int mv88e6xxx_g1_vtu_op_wait ( struct mv88e6xxx_chip * chip )
2017-05-01 14:05:12 -04:00
{
return mv88e6xxx_g1_wait ( chip , GLOBAL_VTU_OP , GLOBAL_VTU_OP_BUSY ) ;
}
2017-05-01 14:05:24 -04:00
static int mv88e6xxx_g1_vtu_op ( struct mv88e6xxx_chip * chip , u16 op )
2017-05-01 14:05:12 -04:00
{
int err ;
err = mv88e6xxx_g1_write ( chip , GLOBAL_VTU_OP , op ) ;
if ( err )
return err ;
return mv88e6xxx_g1_vtu_op_wait ( chip ) ;
}
2017-05-01 14:05:13 -04:00
2017-05-01 14:05:16 -04:00
/* Offset 0x06: VTU VID Register */
2017-05-01 14:05:24 -04:00
static int mv88e6xxx_g1_vtu_vid_read ( struct mv88e6xxx_chip * chip ,
struct mv88e6xxx_vtu_entry * entry )
2017-05-01 14:05:16 -04:00
{
u16 val ;
int err ;
err = mv88e6xxx_g1_read ( chip , GLOBAL_VTU_VID , & val ) ;
if ( err )
return err ;
entry - > vid = val & 0xfff ;
2017-05-01 14:05:26 -04:00
if ( val & GLOBAL_VTU_VID_PAGE )
entry - > vid | = 0x1000 ;
2017-05-01 14:05:16 -04:00
entry - > valid = ! ! ( val & GLOBAL_VTU_VID_VALID ) ;
return 0 ;
}
2017-05-01 14:05:24 -04:00
static int mv88e6xxx_g1_vtu_vid_write ( struct mv88e6xxx_chip * chip ,
struct mv88e6xxx_vtu_entry * entry )
2017-05-01 14:05:16 -04:00
{
u16 val = entry - > vid & 0xfff ;
2017-05-01 14:05:26 -04:00
if ( entry - > vid & 0x1000 )
val | = GLOBAL_VTU_VID_PAGE ;
2017-05-01 14:05:16 -04:00
if ( entry - > valid )
val | = GLOBAL_VTU_VID_VALID ;
return mv88e6xxx_g1_write ( chip , GLOBAL_VTU_VID , val ) ;
}
2017-05-01 14:05:18 -04:00
/* Offset 0x07: VTU/STU Data Register 1
* Offset 0x08 : VTU / STU Data Register 2
* Offset 0x09 : VTU / STU Data Register 3
*/
2017-05-01 14:05:24 -04:00
static int mv88e6185_g1_vtu_data_read ( struct mv88e6xxx_chip * chip ,
struct mv88e6xxx_vtu_entry * entry )
2017-05-01 14:05:18 -04:00
{
u16 regs [ 3 ] ;
int i ;
/* Read all 3 VTU/STU Data registers */
for ( i = 0 ; i < 3 ; + + i ) {
u16 * reg = & regs [ i ] ;
int err ;
err = mv88e6xxx_g1_read ( chip , GLOBAL_VTU_DATA_0_3 + i , reg ) ;
if ( err )
return err ;
}
/* Extract MemberTag and PortState data */
for ( i = 0 ; i < mv88e6xxx_num_ports ( chip ) ; + + i ) {
unsigned int member_offset = ( i % 4 ) * 4 ;
unsigned int state_offset = member_offset + 2 ;
entry - > member [ i ] = ( regs [ i / 4 ] > > member_offset ) & 0x3 ;
entry - > state [ i ] = ( regs [ i / 4 ] > > state_offset ) & 0x3 ;
}
return 0 ;
}
2017-05-01 14:05:24 -04:00
static int mv88e6185_g1_vtu_data_write ( struct mv88e6xxx_chip * chip ,
struct mv88e6xxx_vtu_entry * entry )
2017-05-01 14:05:18 -04:00
{
u16 regs [ 3 ] = { 0 } ;
int i ;
/* Insert MemberTag and PortState data */
for ( i = 0 ; i < mv88e6xxx_num_ports ( chip ) ; + + i ) {
unsigned int member_offset = ( i % 4 ) * 4 ;
unsigned int state_offset = member_offset + 2 ;
regs [ i / 4 ] | = ( entry - > member [ i ] & 0x3 ) < < member_offset ;
regs [ i / 4 ] | = ( entry - > state [ i ] & 0x3 ) < < state_offset ;
}
/* Write all 3 VTU/STU Data registers */
for ( i = 0 ; i < 3 ; + + i ) {
u16 reg = regs [ i ] ;
int err ;
err = mv88e6xxx_g1_write ( chip , GLOBAL_VTU_DATA_0_3 + i , reg ) ;
if ( err )
return err ;
}
return 0 ;
}
2017-05-01 14:05:13 -04:00
/* VLAN Translation Unit Operations */
2017-05-01 14:05:24 -04:00
static int mv88e6xxx_g1_vtu_stu_getnext ( struct mv88e6xxx_chip * chip ,
struct mv88e6xxx_vtu_entry * entry )
2017-05-01 14:05:19 -04:00
{
int err ;
err = mv88e6xxx_g1_vtu_sid_write ( chip , entry ) ;
if ( err )
return err ;
err = mv88e6xxx_g1_vtu_op ( chip , GLOBAL_VTU_OP_STU_GET_NEXT ) ;
if ( err )
return err ;
err = mv88e6xxx_g1_vtu_sid_read ( chip , entry ) ;
if ( err )
return err ;
return mv88e6xxx_g1_vtu_vid_read ( chip , entry ) ;
}
2017-05-01 14:05:24 -04:00
static int mv88e6xxx_g1_vtu_stu_get ( struct mv88e6xxx_chip * chip ,
struct mv88e6xxx_vtu_entry * vtu )
2017-05-01 14:05:20 -04:00
{
struct mv88e6xxx_vtu_entry stu ;
int err ;
err = mv88e6xxx_g1_vtu_sid_read ( chip , vtu ) ;
if ( err )
return err ;
stu . sid = vtu - > sid - 1 ;
err = mv88e6xxx_g1_vtu_stu_getnext ( chip , & stu ) ;
if ( err )
return err ;
if ( stu . sid ! = vtu - > sid | | ! stu . valid )
return - EINVAL ;
return 0 ;
}
2017-05-01 14:05:24 -04:00
static int mv88e6xxx_g1_vtu_getnext ( struct mv88e6xxx_chip * chip ,
struct mv88e6xxx_vtu_entry * entry )
2017-05-01 14:05:17 -04:00
{
int err ;
err = mv88e6xxx_g1_vtu_op_wait ( chip ) ;
if ( err )
return err ;
/* To get the next higher active VID, the VTU GetNext operation can be
* started again without setting the VID registers since it already
* contains the last VID .
*
* To save a few hardware accesses and abstract this to the caller ,
* write the VID only once , when the entry is given as invalid .
*/
if ( ! entry - > valid ) {
err = mv88e6xxx_g1_vtu_vid_write ( chip , entry ) ;
if ( err )
return err ;
}
err = mv88e6xxx_g1_vtu_op ( chip , GLOBAL_VTU_OP_VTU_GET_NEXT ) ;
if ( err )
return err ;
return mv88e6xxx_g1_vtu_vid_read ( chip , entry ) ;
}
2017-05-01 14:05:22 -04:00
int mv88e6185_g1_vtu_getnext ( struct mv88e6xxx_chip * chip ,
struct mv88e6xxx_vtu_entry * entry )
{
u16 val ;
int err ;
err = mv88e6xxx_g1_vtu_getnext ( chip , entry ) ;
if ( err )
return err ;
if ( entry - > valid ) {
err = mv88e6185_g1_vtu_data_read ( chip , entry ) ;
if ( err )
return err ;
/* VTU DBNum[3:0] are located in VTU Operation 3:0
* VTU DBNum [ 7 : 4 ] are located in VTU Operation 11 : 8
*/
err = mv88e6xxx_g1_read ( chip , GLOBAL_VTU_OP , & val ) ;
if ( err )
return err ;
entry - > fid = val & 0x000f ;
entry - > fid | = ( val & 0x0f00 ) > > 4 ;
}
return 0 ;
}
int mv88e6352_g1_vtu_getnext ( struct mv88e6xxx_chip * chip ,
struct mv88e6xxx_vtu_entry * entry )
{
int err ;
/* Fetch VLAN MemberTag data from the VTU */
err = mv88e6xxx_g1_vtu_getnext ( chip , entry ) ;
if ( err )
return err ;
if ( entry - > valid ) {
/* Fetch (and mask) VLAN PortState data from the STU */
err = mv88e6xxx_g1_vtu_stu_get ( chip , entry ) ;
if ( err )
return err ;
err = mv88e6185_g1_vtu_data_read ( chip , entry ) ;
if ( err )
return err ;
err = mv88e6xxx_g1_vtu_fid_read ( chip , entry ) ;
if ( err )
return err ;
}
return 0 ;
}
2017-05-01 14:05:23 -04:00
int mv88e6185_g1_vtu_loadpurge ( struct mv88e6xxx_chip * chip ,
struct mv88e6xxx_vtu_entry * entry )
{
u16 op = GLOBAL_VTU_OP_VTU_LOAD_PURGE ;
int err ;
err = mv88e6xxx_g1_vtu_op_wait ( chip ) ;
if ( err )
return err ;
err = mv88e6xxx_g1_vtu_vid_write ( chip , entry ) ;
if ( err )
return err ;
if ( entry - > valid ) {
err = mv88e6185_g1_vtu_data_write ( chip , entry ) ;
if ( err )
return err ;
/* VTU DBNum[3:0] are located in VTU Operation 3:0
* VTU DBNum [ 7 : 4 ] are located in VTU Operation 11 : 8
*/
op | = entry - > fid & 0x000f ;
op | = ( entry - > fid & 0x00f0 ) < < 8 ;
}
return mv88e6xxx_g1_vtu_op ( chip , op ) ;
}
int mv88e6352_g1_vtu_loadpurge ( struct mv88e6xxx_chip * chip ,
struct mv88e6xxx_vtu_entry * entry )
{
int err ;
err = mv88e6xxx_g1_vtu_op_wait ( chip ) ;
if ( err )
return err ;
err = mv88e6xxx_g1_vtu_vid_write ( chip , entry ) ;
if ( err )
return err ;
if ( entry - > valid ) {
/* Write MemberTag and PortState data */
err = mv88e6185_g1_vtu_data_write ( chip , entry ) ;
if ( err )
return err ;
err = mv88e6xxx_g1_vtu_sid_write ( chip , entry ) ;
if ( err )
return err ;
/* Load STU entry */
err = mv88e6xxx_g1_vtu_op ( chip , GLOBAL_VTU_OP_STU_LOAD_PURGE ) ;
if ( err )
return err ;
err = mv88e6xxx_g1_vtu_fid_write ( chip , entry ) ;
if ( err )
return err ;
}
/* Load/Purge VTU entry */
return mv88e6xxx_g1_vtu_op ( chip , GLOBAL_VTU_OP_VTU_LOAD_PURGE ) ;
}
2017-05-01 14:05:13 -04:00
int mv88e6xxx_g1_vtu_flush ( struct mv88e6xxx_chip * chip )
{
int err ;
err = mv88e6xxx_g1_vtu_op_wait ( chip ) ;
if ( err )
return err ;
return mv88e6xxx_g1_vtu_op ( chip , GLOBAL_VTU_OP_FLUSH_ALL ) ;
}