2016-09-29 12:21:53 -04:00
/*
* Marvell 88E6 xxx Switch Global ( 1 ) Registers support
*
* Copyright ( c ) 2008 Marvell Semiconductor
*
2017-03-28 15:10:36 -04:00
* Copyright ( c ) 2016 - 2017 Savoir - faire Linux Inc .
* Vivien Didelot < vivien . didelot @ savoirfairelinux . com >
2016-09-29 12:21:53 -04:00
*
* 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 .
*/
2017-06-15 12:14:04 -04:00
# include <linux/bitfield.h>
2017-06-02 17:06:15 -04:00
# include "chip.h"
2016-09-29 12:21:53 -04:00
# include "global1.h"
int mv88e6xxx_g1_read ( struct mv88e6xxx_chip * chip , int reg , u16 * val )
{
int addr = chip - > info - > global1_addr ;
return mv88e6xxx_read ( chip , addr , reg , val ) ;
}
int mv88e6xxx_g1_write ( struct mv88e6xxx_chip * chip , int reg , u16 val )
{
int addr = chip - > info - > global1_addr ;
return mv88e6xxx_write ( chip , addr , reg , val ) ;
}
int mv88e6xxx_g1_wait ( struct mv88e6xxx_chip * chip , int reg , u16 mask )
{
return mv88e6xxx_wait ( chip , chip - > info - > global1_addr , reg , mask ) ;
}
2016-11-21 23:26:58 +01:00
2016-12-05 17:30:27 -05:00
/* Offset 0x00: Switch Global Status Register */
2016-12-05 17:30:28 -05:00
static int mv88e6185_g1_wait_ppu_disabled ( struct mv88e6xxx_chip * chip )
{
u16 state ;
int i , err ;
for ( i = 0 ; i < 16 ; i + + ) {
2017-06-15 12:13:59 -04:00
err = mv88e6xxx_g1_read ( chip , MV88E6XXX_G1_STS , & state ) ;
2016-12-05 17:30:28 -05:00
if ( err )
return err ;
/* Check the value of the PPUState bits 15:14 */
2017-06-15 12:13:59 -04:00
state & = MV88E6185_G1_STS_PPU_STATE_MASK ;
if ( state ! = MV88E6185_G1_STS_PPU_STATE_POLLING )
2016-12-05 17:30:28 -05:00
return 0 ;
usleep_range ( 1000 , 2000 ) ;
}
return - ETIMEDOUT ;
}
2016-12-05 17:30:27 -05:00
static int mv88e6185_g1_wait_ppu_polling ( struct mv88e6xxx_chip * chip )
{
u16 state ;
int i , err ;
for ( i = 0 ; i < 16 ; + + i ) {
2017-06-15 12:13:59 -04:00
err = mv88e6xxx_g1_read ( chip , MV88E6XXX_G1_STS , & state ) ;
2016-12-05 17:30:27 -05:00
if ( err )
return err ;
/* Check the value of the PPUState bits 15:14 */
2017-06-15 12:13:59 -04:00
state & = MV88E6185_G1_STS_PPU_STATE_MASK ;
if ( state = = MV88E6185_G1_STS_PPU_STATE_POLLING )
2016-12-05 17:30:27 -05:00
return 0 ;
usleep_range ( 1000 , 2000 ) ;
}
return - ETIMEDOUT ;
}
static int mv88e6352_g1_wait_ppu_polling ( struct mv88e6xxx_chip * chip )
{
u16 state ;
int i , err ;
for ( i = 0 ; i < 16 ; + + i ) {
2017-06-15 12:13:59 -04:00
err = mv88e6xxx_g1_read ( chip , MV88E6XXX_G1_STS , & state ) ;
2016-12-05 17:30:27 -05:00
if ( err )
return err ;
/* Check the value of the PPUState (or InitState) bit 15 */
2017-06-15 12:13:59 -04:00
if ( state & MV88E6352_G1_STS_PPU_STATE )
2016-12-05 17:30:27 -05:00
return 0 ;
usleep_range ( 1000 , 2000 ) ;
}
return - ETIMEDOUT ;
}
static int mv88e6xxx_g1_wait_init_ready ( struct mv88e6xxx_chip * chip )
{
const unsigned long timeout = jiffies + 1 * HZ ;
u16 val ;
int err ;
/* Wait up to 1 second for the switch to be ready. The InitReady bit 11
* is set to a one when all units inside the device ( ATU , VTU , etc . )
* have finished their initialization and are ready to accept frames .
*/
while ( time_before ( jiffies , timeout ) ) {
2017-06-15 12:13:59 -04:00
err = mv88e6xxx_g1_read ( chip , MV88E6XXX_G1_STS , & val ) ;
2016-12-05 17:30:27 -05:00
if ( err )
return err ;
2017-06-15 12:13:59 -04:00
if ( val & MV88E6XXX_G1_STS_INIT_READY )
2016-12-05 17:30:27 -05:00
break ;
usleep_range ( 1000 , 2000 ) ;
}
if ( time_after ( jiffies , timeout ) )
return - ETIMEDOUT ;
return 0 ;
}
2017-06-15 12:14:00 -04:00
/* Offset 0x01: Switch MAC Address Register Bytes 0 & 1
* Offset 0x02 : Switch MAC Address Register Bytes 2 & 3
* Offset 0x03 : Switch MAC Address Register Bytes 4 & 5
*/
int mv88e6xxx_g1_set_switch_mac ( struct mv88e6xxx_chip * chip , u8 * addr )
{
u16 reg ;
int err ;
reg = ( addr [ 0 ] < < 8 ) | addr [ 1 ] ;
err = mv88e6xxx_g1_write ( chip , MV88E6XXX_G1_MAC_01 , reg ) ;
if ( err )
return err ;
reg = ( addr [ 2 ] < < 8 ) | addr [ 3 ] ;
err = mv88e6xxx_g1_write ( chip , MV88E6XXX_G1_MAC_23 , reg ) ;
if ( err )
return err ;
reg = ( addr [ 4 ] < < 8 ) | addr [ 5 ] ;
err = mv88e6xxx_g1_write ( chip , MV88E6XXX_G1_MAC_45 , reg ) ;
if ( err )
return err ;
return 0 ;
}
2016-12-05 17:30:27 -05:00
/* Offset 0x04: Switch Global Control Register */
int mv88e6185_g1_reset ( struct mv88e6xxx_chip * chip )
{
u16 val ;
int err ;
/* Set the SWReset bit 15 along with the PPUEn bit 14, to also restart
* the PPU , including re - doing PHY detection and initialization
*/
2017-06-15 12:14:03 -04:00
err = mv88e6xxx_g1_read ( chip , MV88E6XXX_G1_CTL1 , & val ) ;
2016-12-05 17:30:27 -05:00
if ( err )
return err ;
2017-06-15 12:14:03 -04:00
val | = MV88E6XXX_G1_CTL1_SW_RESET ;
val | = MV88E6XXX_G1_CTL1_PPU_ENABLE ;
2016-12-05 17:30:27 -05:00
2017-06-15 12:14:03 -04:00
err = mv88e6xxx_g1_write ( chip , MV88E6XXX_G1_CTL1 , val ) ;
2016-12-05 17:30:27 -05:00
if ( err )
return err ;
err = mv88e6xxx_g1_wait_init_ready ( chip ) ;
if ( err )
return err ;
return mv88e6185_g1_wait_ppu_polling ( chip ) ;
}
int mv88e6352_g1_reset ( struct mv88e6xxx_chip * chip )
{
u16 val ;
int err ;
/* Set the SWReset bit 15 */
2017-06-15 12:14:03 -04:00
err = mv88e6xxx_g1_read ( chip , MV88E6XXX_G1_CTL1 , & val ) ;
2016-12-05 17:30:27 -05:00
if ( err )
return err ;
2017-06-15 12:14:03 -04:00
val | = MV88E6XXX_G1_CTL1_SW_RESET ;
2016-12-05 17:30:27 -05:00
2017-06-15 12:14:03 -04:00
err = mv88e6xxx_g1_write ( chip , MV88E6XXX_G1_CTL1 , val ) ;
2016-12-05 17:30:27 -05:00
if ( err )
return err ;
err = mv88e6xxx_g1_wait_init_ready ( chip ) ;
if ( err )
return err ;
return mv88e6352_g1_wait_ppu_polling ( chip ) ;
}
2016-12-05 17:30:28 -05:00
int mv88e6185_g1_ppu_enable ( struct mv88e6xxx_chip * chip )
{
u16 val ;
int err ;
2017-06-15 12:14:03 -04:00
err = mv88e6xxx_g1_read ( chip , MV88E6XXX_G1_CTL1 , & val ) ;
2016-12-05 17:30:28 -05:00
if ( err )
return err ;
2017-06-15 12:14:03 -04:00
val | = MV88E6XXX_G1_CTL1_PPU_ENABLE ;
2016-12-05 17:30:28 -05:00
2017-06-15 12:14:03 -04:00
err = mv88e6xxx_g1_write ( chip , MV88E6XXX_G1_CTL1 , val ) ;
2016-12-05 17:30:28 -05:00
if ( err )
return err ;
return mv88e6185_g1_wait_ppu_polling ( chip ) ;
}
int mv88e6185_g1_ppu_disable ( struct mv88e6xxx_chip * chip )
{
u16 val ;
int err ;
2017-06-15 12:14:03 -04:00
err = mv88e6xxx_g1_read ( chip , MV88E6XXX_G1_CTL1 , & val ) ;
2016-12-05 17:30:28 -05:00
if ( err )
return err ;
2017-06-15 12:14:03 -04:00
val & = ~ MV88E6XXX_G1_CTL1_PPU_ENABLE ;
2016-12-05 17:30:28 -05:00
2017-06-15 12:14:03 -04:00
err = mv88e6xxx_g1_write ( chip , MV88E6XXX_G1_CTL1 , val ) ;
2016-12-05 17:30:28 -05:00
if ( err )
return err ;
return mv88e6185_g1_wait_ppu_disabled ( chip ) ;
}
2016-12-03 04:35:17 +01:00
/* Offset 0x1a: Monitor Control */
/* Offset 0x1a: Monitor & MGMT Control on some devices */
int mv88e6095_g1_set_egress_port ( struct mv88e6xxx_chip * chip , int port )
{
u16 reg ;
int err ;
2017-06-15 12:14:04 -04:00
err = mv88e6xxx_g1_read ( chip , MV88E6185_G1_MONITOR_CTL , & reg ) ;
2016-12-03 04:35:17 +01:00
if ( err )
return err ;
2017-06-15 12:14:04 -04:00
reg & = ~ ( MV88E6185_G1_MONITOR_CTL_INGRESS_DEST_MASK |
MV88E6185_G1_MONITOR_CTL_EGRESS_DEST_MASK ) ;
2016-12-03 04:35:17 +01:00
2017-06-15 12:14:04 -04:00
reg | = port < < __bf_shf ( MV88E6185_G1_MONITOR_CTL_INGRESS_DEST_MASK ) |
port < < __bf_shf ( MV88E6185_G1_MONITOR_CTL_EGRESS_DEST_MASK ) ;
2016-12-03 04:35:17 +01:00
2017-06-15 12:14:04 -04:00
return mv88e6xxx_g1_write ( chip , MV88E6185_G1_MONITOR_CTL , reg ) ;
2016-12-03 04:35:17 +01:00
}
/* Older generations also call this the ARP destination. It has been
* generalized in more modern devices such that more than ARP can
* egress it
*/
int mv88e6095_g1_set_cpu_port ( struct mv88e6xxx_chip * chip , int port )
{
u16 reg ;
int err ;
2017-06-15 12:14:04 -04:00
err = mv88e6xxx_g1_read ( chip , MV88E6185_G1_MONITOR_CTL , & reg ) ;
2016-12-03 04:35:17 +01:00
if ( err )
return err ;
2017-06-15 12:14:04 -04:00
reg & = ~ MV88E6185_G1_MONITOR_CTL_ARP_DEST_MASK ;
reg | = port < < __bf_shf ( MV88E6185_G1_MONITOR_CTL_ARP_DEST_MASK ) ;
2016-12-03 04:35:17 +01:00
2017-06-15 12:14:04 -04:00
return mv88e6xxx_g1_write ( chip , MV88E6185_G1_MONITOR_CTL , reg ) ;
2016-12-03 04:35:17 +01:00
}
static int mv88e6390_g1_monitor_write ( struct mv88e6xxx_chip * chip ,
u16 pointer , u8 data )
{
u16 reg ;
2017-06-15 12:14:04 -04:00
reg = MV88E6390_G1_MONITOR_MGMT_CTL_UPDATE | pointer | data ;
2016-12-03 04:35:17 +01:00
2017-06-15 12:14:04 -04:00
return mv88e6xxx_g1_write ( chip , MV88E6390_G1_MONITOR_MGMT_CTL , reg ) ;
2016-12-03 04:35:17 +01:00
}
int mv88e6390_g1_set_egress_port ( struct mv88e6xxx_chip * chip , int port )
{
2017-06-15 12:14:04 -04:00
u16 ptr ;
2016-12-03 04:35:17 +01:00
int err ;
2017-06-15 12:14:04 -04:00
ptr = MV88E6390_G1_MONITOR_MGMT_CTL_PTR_INGRESS_DEST ;
err = mv88e6390_g1_monitor_write ( chip , ptr , port ) ;
2016-12-03 04:35:17 +01:00
if ( err )
return err ;
2017-06-15 12:14:04 -04:00
ptr = MV88E6390_G1_MONITOR_MGMT_CTL_PTR_EGRESS_DEST ;
err = mv88e6390_g1_monitor_write ( chip , ptr , port ) ;
if ( err )
return err ;
return 0 ;
2016-12-03 04:35:17 +01:00
}
int mv88e6390_g1_set_cpu_port ( struct mv88e6xxx_chip * chip , int port )
{
2017-06-15 12:14:04 -04:00
u16 ptr = MV88E6390_G1_MONITOR_MGMT_CTL_PTR_CPU_DEST ;
return mv88e6390_g1_monitor_write ( chip , ptr , port ) ;
2016-12-03 04:35:17 +01:00
}
2016-12-03 04:45:16 +01:00
int mv88e6390_g1_mgmt_rsvd2cpu ( struct mv88e6xxx_chip * chip )
{
2017-06-15 12:14:04 -04:00
u16 ptr ;
2016-12-03 04:45:16 +01:00
int err ;
/* 01:c2:80:00:00:00:00-01:c2:80:00:00:00:07 are Management */
2017-06-15 12:14:04 -04:00
ptr = MV88E6390_G1_MONITOR_MGMT_CTL_PTR_0180C280000000XLO ;
err = mv88e6390_g1_monitor_write ( chip , ptr , 0xff ) ;
2016-12-03 04:45:16 +01:00
if ( err )
return err ;
/* 01:c2:80:00:00:00:08-01:c2:80:00:00:00:0f are Management */
2017-06-15 12:14:04 -04:00
ptr = MV88E6390_G1_MONITOR_MGMT_CTL_PTR_0180C280000000XHI ;
err = mv88e6390_g1_monitor_write ( chip , ptr , 0xff ) ;
2016-12-03 04:45:16 +01:00
if ( err )
return err ;
/* 01:c2:80:00:00:00:20-01:c2:80:00:00:00:27 are Management */
2017-06-15 12:14:04 -04:00
ptr = MV88E6390_G1_MONITOR_MGMT_CTL_PTR_0180C280000002XLO ;
err = mv88e6390_g1_monitor_write ( chip , ptr , 0xff ) ;
2016-12-03 04:45:16 +01:00
if ( err )
return err ;
/* 01:c2:80:00:00:00:28-01:c2:80:00:00:00:2f are Management */
2017-06-15 12:14:04 -04:00
ptr = MV88E6390_G1_MONITOR_MGMT_CTL_PTR_0180C280000002XHI ;
err = mv88e6390_g1_monitor_write ( chip , ptr , 0xff ) ;
if ( err )
return err ;
return 0 ;
2016-12-03 04:45:16 +01:00
}
2016-11-21 23:27:01 +01:00
/* Offset 0x1c: Global Control 2 */
int mv88e6390_g1_stats_set_histogram ( struct mv88e6xxx_chip * chip )
{
u16 val ;
int err ;
2017-06-15 12:14:03 -04:00
err = mv88e6xxx_g1_read ( chip , MV88E6XXX_G1_CTL2 , & val ) ;
2016-11-21 23:27:01 +01:00
if ( err )
return err ;
2017-06-15 12:14:03 -04:00
val | = MV88E6XXX_G1_CTL2_HIST_RX_TX ;
2016-11-21 23:27:01 +01:00
2017-06-15 12:14:03 -04:00
err = mv88e6xxx_g1_write ( chip , MV88E6XXX_G1_CTL2 , val ) ;
2016-11-21 23:27:01 +01:00
return err ;
}
/* Offset 0x1d: Statistics Operation 2 */
2016-11-21 23:27:05 +01:00
int mv88e6xxx_g1_stats_wait ( struct mv88e6xxx_chip * chip )
2016-11-21 23:26:58 +01:00
{
2017-06-15 12:14:05 -04:00
return mv88e6xxx_g1_wait ( chip , MV88E6XXX_G1_STATS_OP ,
MV88E6XXX_G1_STATS_OP_BUSY ) ;
2016-11-21 23:26:58 +01:00
}
2017-11-10 00:36:41 +01:00
int mv88e6095_g1_stats_set_histogram ( struct mv88e6xxx_chip * chip )
{
u16 val ;
int err ;
err = mv88e6xxx_g1_read ( chip , MV88E6XXX_G1_STATS_OP , & val ) ;
if ( err )
return err ;
val | = MV88E6XXX_G1_STATS_OP_HIST_RX_TX ;
err = mv88e6xxx_g1_write ( chip , MV88E6XXX_G1_STATS_OP , val ) ;
return err ;
}
2016-11-21 23:26:58 +01:00
int mv88e6xxx_g1_stats_snapshot ( struct mv88e6xxx_chip * chip , int port )
{
int err ;
/* Snapshot the hardware statistics counters for this port. */
2017-06-15 12:14:05 -04:00
err = mv88e6xxx_g1_write ( chip , MV88E6XXX_G1_STATS_OP ,
MV88E6XXX_G1_STATS_OP_BUSY |
MV88E6XXX_G1_STATS_OP_CAPTURE_PORT |
MV88E6XXX_G1_STATS_OP_HIST_RX_TX | port ) ;
2016-11-21 23:26:58 +01:00
if ( err )
return err ;
/* Wait for the snapshotting to complete. */
return mv88e6xxx_g1_stats_wait ( chip ) ;
}
int mv88e6320_g1_stats_snapshot ( struct mv88e6xxx_chip * chip , int port )
{
port = ( port + 1 ) < < 5 ;
return mv88e6xxx_g1_stats_snapshot ( chip , port ) ;
}
2016-11-21 23:27:00 +01:00
int mv88e6390_g1_stats_snapshot ( struct mv88e6xxx_chip * chip , int port )
{
int err ;
port = ( port + 1 ) < < 5 ;
/* Snapshot the hardware statistics counters for this port. */
2017-06-15 12:14:05 -04:00
err = mv88e6xxx_g1_write ( chip , MV88E6XXX_G1_STATS_OP ,
MV88E6XXX_G1_STATS_OP_BUSY |
MV88E6XXX_G1_STATS_OP_CAPTURE_PORT | port ) ;
2016-11-21 23:27:00 +01:00
if ( err )
return err ;
/* Wait for the snapshotting to complete. */
return mv88e6xxx_g1_stats_wait ( chip ) ;
}
2016-11-21 23:27:05 +01:00
void mv88e6xxx_g1_stats_read ( struct mv88e6xxx_chip * chip , int stat , u32 * val )
{
u32 value ;
u16 reg ;
int err ;
* val = 0 ;
2017-06-15 12:14:05 -04:00
err = mv88e6xxx_g1_write ( chip , MV88E6XXX_G1_STATS_OP ,
MV88E6XXX_G1_STATS_OP_BUSY |
MV88E6XXX_G1_STATS_OP_READ_CAPTURED | stat ) ;
2016-11-21 23:27:05 +01:00
if ( err )
return ;
err = mv88e6xxx_g1_stats_wait ( chip ) ;
if ( err )
return ;
2017-06-15 12:14:05 -04:00
err = mv88e6xxx_g1_read ( chip , MV88E6XXX_G1_STATS_COUNTER_32 , & reg ) ;
2016-11-21 23:27:05 +01:00
if ( err )
return ;
value = reg < < 16 ;
2017-06-15 12:14:05 -04:00
err = mv88e6xxx_g1_read ( chip , MV88E6XXX_G1_STATS_COUNTER_01 , & reg ) ;
2016-11-21 23:27:05 +01:00
if ( err )
return ;
* val = value | reg ;
}
2017-11-10 00:36:41 +01:00
int mv88e6xxx_g1_stats_clear ( struct mv88e6xxx_chip * chip )
{
int err ;
u16 val ;
err = mv88e6xxx_g1_read ( chip , MV88E6XXX_G1_STATS_OP , & val ) ;
if ( err )
return err ;
val | = MV88E6XXX_G1_STATS_OP_BUSY | MV88E6XXX_G1_STATS_OP_FLUSH_ALL ;
err = mv88e6xxx_g1_write ( chip , MV88E6XXX_G1_STATS_OP , val ) ;
if ( err )
return err ;
/* Wait for the flush to complete. */
return mv88e6xxx_g1_stats_wait ( chip ) ;
}