2019-05-27 08:55:01 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2017-03-11 16:12:48 -05:00
/*
* Marvell 88E6 xxx Address Translation Unit ( ATU ) support
*
* Copyright ( c ) 2008 Marvell Semiconductor
* Copyright ( c ) 2017 Savoir - faire Linux , Inc .
*/
2019-08-09 18:47:55 -04:00
# include <linux/bitfield.h>
2018-01-14 02:32:44 +01:00
# include <linux/interrupt.h>
# include <linux/irqdomain.h>
2017-03-11 16:12:48 -05:00
2017-06-02 17:06:15 -04:00
# include "chip.h"
2017-03-11 16:12:48 -05:00
# include "global1.h"
2017-03-11 16:12:52 -05:00
/* Offset 0x01: ATU FID Register */
static int mv88e6xxx_g1_atu_fid_write ( struct mv88e6xxx_chip * chip , u16 fid )
{
2017-06-15 12:14:01 -04:00
return mv88e6xxx_g1_write ( chip , MV88E6352_G1_ATU_FID , fid & 0xfff ) ;
2017-03-11 16:12:52 -05:00
}
2017-03-11 16:12:48 -05:00
/* Offset 0x0A: ATU Control Register */
2017-03-11 16:12:51 -05:00
int mv88e6xxx_g1_atu_set_learn2all ( struct mv88e6xxx_chip * chip , bool learn2all )
{
u16 val ;
int err ;
2017-06-15 12:14:01 -04:00
err = mv88e6xxx_g1_read ( chip , MV88E6XXX_G1_ATU_CTL , & val ) ;
2017-03-11 16:12:51 -05:00
if ( err )
return err ;
if ( learn2all )
2017-06-15 12:14:01 -04:00
val | = MV88E6XXX_G1_ATU_CTL_LEARN2ALL ;
2017-03-11 16:12:51 -05:00
else
2017-06-15 12:14:01 -04:00
val & = ~ MV88E6XXX_G1_ATU_CTL_LEARN2ALL ;
2017-03-11 16:12:51 -05:00
2017-06-15 12:14:01 -04:00
return mv88e6xxx_g1_write ( chip , MV88E6XXX_G1_ATU_CTL , val ) ;
2017-03-11 16:12:51 -05:00
}
2017-03-11 16:12:48 -05:00
int mv88e6xxx_g1_atu_set_age_time ( struct mv88e6xxx_chip * chip ,
unsigned int msecs )
{
const unsigned int coeff = chip - > info - > age_time_coeff ;
const unsigned int min = 0x01 * coeff ;
const unsigned int max = 0xff * coeff ;
u8 age_time ;
u16 val ;
int err ;
if ( msecs < min | | msecs > max )
return - ERANGE ;
/* Round to nearest multiple of coeff */
age_time = ( msecs + coeff / 2 ) / coeff ;
2017-06-15 12:14:01 -04:00
err = mv88e6xxx_g1_read ( chip , MV88E6XXX_G1_ATU_CTL , & val ) ;
2017-03-11 16:12:48 -05:00
if ( err )
return err ;
/* AgeTime is 11:4 bits */
val & = ~ 0xff0 ;
val | = age_time < < 4 ;
2017-06-15 12:14:01 -04:00
err = mv88e6xxx_g1_write ( chip , MV88E6XXX_G1_ATU_CTL , val ) ;
2017-03-29 15:38:37 -04:00
if ( err )
return err ;
dev_dbg ( chip - > dev , " AgeTime set to 0x%02x (%d ms) \n " , age_time ,
age_time * coeff ) ;
return 0 ;
2017-03-11 16:12:48 -05:00
}
2017-03-11 16:12:52 -05:00
2019-10-25 01:03:52 +02:00
int mv88e6165_g1_atu_get_hash ( struct mv88e6xxx_chip * chip , u8 * hash )
{
int err ;
u16 val ;
err = mv88e6xxx_g1_read ( chip , MV88E6XXX_G1_ATU_CTL , & val ) ;
if ( err )
return err ;
* hash = val & MV88E6161_G1_ATU_CTL_HASH_MASK ;
return 0 ;
}
int mv88e6165_g1_atu_set_hash ( struct mv88e6xxx_chip * chip , u8 hash )
{
int err ;
u16 val ;
if ( hash & ~ MV88E6161_G1_ATU_CTL_HASH_MASK )
return - EINVAL ;
err = mv88e6xxx_g1_read ( chip , MV88E6XXX_G1_ATU_CTL , & val ) ;
if ( err )
return err ;
val & = ~ MV88E6161_G1_ATU_CTL_HASH_MASK ;
val | = hash ;
return mv88e6xxx_g1_write ( chip , MV88E6XXX_G1_ATU_CTL , val ) ;
}
2017-03-11 16:12:52 -05:00
/* Offset 0x0B: ATU Operation Register */
static int mv88e6xxx_g1_atu_op_wait ( struct mv88e6xxx_chip * chip )
{
2019-08-09 18:47:55 -04:00
int bit = __bf_shf ( MV88E6XXX_G1_ATU_OP_BUSY ) ;
return mv88e6xxx_g1_wait_bit ( chip , MV88E6XXX_G1_ATU_OP , bit , 0 ) ;
2017-03-11 16:12:52 -05:00
}
static int mv88e6xxx_g1_atu_op ( struct mv88e6xxx_chip * chip , u16 fid , u16 op )
{
u16 val ;
int err ;
/* FID bits are dispatched all around gradually as more are supported */
if ( mv88e6xxx_num_databases ( chip ) > 256 ) {
err = mv88e6xxx_g1_atu_fid_write ( chip , fid ) ;
if ( err )
return err ;
} else {
2019-06-04 07:34:25 +00:00
if ( mv88e6xxx_num_databases ( chip ) > 64 ) {
2017-03-11 16:12:52 -05:00
/* ATU DBNum[7:4] are located in ATU Control 15:12 */
2017-06-15 12:14:01 -04:00
err = mv88e6xxx_g1_read ( chip , MV88E6XXX_G1_ATU_CTL ,
& val ) ;
2017-03-11 16:12:52 -05:00
if ( err )
return err ;
val = ( val & 0x0fff ) | ( ( fid < < 8 ) & 0xf000 ) ;
2017-06-15 12:14:01 -04:00
err = mv88e6xxx_g1_write ( chip , MV88E6XXX_G1_ATU_CTL ,
val ) ;
2017-03-11 16:12:52 -05:00
if ( err )
return err ;
2019-06-04 07:34:25 +00:00
} else if ( mv88e6xxx_num_databases ( chip ) > 16 ) {
/* ATU DBNum[5:4] are located in ATU Operation 9:8 */
op | = ( fid & 0x30 ) < < 4 ;
2017-03-11 16:12:52 -05:00
}
/* ATU DBNum[3:0] are located in ATU Operation 3:0 */
op | = fid & 0xf ;
}
2017-06-15 12:14:01 -04:00
err = mv88e6xxx_g1_write ( chip , MV88E6XXX_G1_ATU_OP ,
MV88E6XXX_G1_ATU_OP_BUSY | op ) ;
2017-03-11 16:12:52 -05:00
if ( err )
return err ;
return mv88e6xxx_g1_atu_op_wait ( chip ) ;
}
2019-11-05 01:13:00 +01:00
int mv88e6xxx_g1_atu_get_next ( struct mv88e6xxx_chip * chip , u16 fid )
{
return mv88e6xxx_g1_atu_op ( chip , fid , MV88E6XXX_G1_ATU_OP_GET_NEXT_DB ) ;
}
2017-03-11 16:12:52 -05:00
/* Offset 0x0C: ATU Data Register */
2017-03-11 16:12:53 -05:00
static int mv88e6xxx_g1_atu_data_read ( struct mv88e6xxx_chip * chip ,
struct mv88e6xxx_atu_entry * entry )
{
u16 val ;
int err ;
2017-06-15 12:14:01 -04:00
err = mv88e6xxx_g1_read ( chip , MV88E6XXX_G1_ATU_DATA , & val ) ;
2017-03-11 16:12:53 -05:00
if ( err )
return err ;
entry - > state = val & 0xf ;
2019-09-07 16:00:47 -04:00
if ( entry - > state ) {
2017-06-15 12:14:01 -04:00
entry - > trunk = ! ! ( val & MV88E6XXX_G1_ATU_DATA_TRUNK ) ;
2017-03-11 16:12:57 -05:00
entry - > portvec = ( val > > 4 ) & mv88e6xxx_port_mask ( chip ) ;
2017-03-11 16:12:53 -05:00
}
return 0 ;
}
2017-03-11 16:12:52 -05:00
static int mv88e6xxx_g1_atu_data_write ( struct mv88e6xxx_chip * chip ,
struct mv88e6xxx_atu_entry * entry )
{
u16 data = entry - > state & 0xf ;
2019-09-07 16:00:47 -04:00
if ( entry - > state ) {
2017-03-11 16:12:52 -05:00
if ( entry - > trunk )
2017-06-15 12:14:01 -04:00
data | = MV88E6XXX_G1_ATU_DATA_TRUNK ;
2017-03-11 16:12:52 -05:00
2017-03-11 16:12:57 -05:00
data | = ( entry - > portvec & mv88e6xxx_port_mask ( chip ) ) < < 4 ;
2017-03-11 16:12:52 -05:00
}
2017-06-15 12:14:01 -04:00
return mv88e6xxx_g1_write ( chip , MV88E6XXX_G1_ATU_DATA , data ) ;
2017-03-11 16:12:52 -05:00
}
/* Offset 0x0D: ATU MAC Address Register Bytes 0 & 1
* Offset 0x0E : ATU MAC Address Register Bytes 2 & 3
* Offset 0x0F : ATU MAC Address Register Bytes 4 & 5
*/
2017-03-11 16:12:53 -05:00
static int mv88e6xxx_g1_atu_mac_read ( struct mv88e6xxx_chip * chip ,
struct mv88e6xxx_atu_entry * entry )
{
u16 val ;
int i , err ;
for ( i = 0 ; i < 3 ; i + + ) {
2017-06-15 12:14:01 -04:00
err = mv88e6xxx_g1_read ( chip , MV88E6XXX_G1_ATU_MAC01 + i , & val ) ;
2017-03-11 16:12:53 -05:00
if ( err )
return err ;
entry - > mac [ i * 2 ] = val > > 8 ;
entry - > mac [ i * 2 + 1 ] = val & 0xff ;
}
return 0 ;
}
2017-03-11 16:12:52 -05:00
static int mv88e6xxx_g1_atu_mac_write ( struct mv88e6xxx_chip * chip ,
struct mv88e6xxx_atu_entry * entry )
{
u16 val ;
int i , err ;
for ( i = 0 ; i < 3 ; i + + ) {
val = ( entry - > mac [ i * 2 ] < < 8 ) | entry - > mac [ i * 2 + 1 ] ;
2017-06-15 12:14:01 -04:00
err = mv88e6xxx_g1_write ( chip , MV88E6XXX_G1_ATU_MAC01 + i , val ) ;
2017-03-11 16:12:52 -05:00
if ( err )
return err ;
}
return 0 ;
}
/* Address Translation Unit operations */
2017-03-11 16:12:53 -05:00
int mv88e6xxx_g1_atu_getnext ( struct mv88e6xxx_chip * chip , u16 fid ,
struct mv88e6xxx_atu_entry * entry )
{
int err ;
err = mv88e6xxx_g1_atu_op_wait ( chip ) ;
if ( err )
return err ;
/* Write the MAC address to iterate from only once */
2019-09-07 16:00:47 -04:00
if ( ! entry - > state ) {
2017-03-11 16:12:53 -05:00
err = mv88e6xxx_g1_atu_mac_write ( chip , entry ) ;
if ( err )
return err ;
}
2017-06-15 12:14:01 -04:00
err = mv88e6xxx_g1_atu_op ( chip , fid , MV88E6XXX_G1_ATU_OP_GET_NEXT_DB ) ;
2017-03-11 16:12:53 -05:00
if ( err )
return err ;
err = mv88e6xxx_g1_atu_data_read ( chip , entry ) ;
if ( err )
return err ;
return mv88e6xxx_g1_atu_mac_read ( chip , entry ) ;
}
2017-03-11 16:12:52 -05:00
int mv88e6xxx_g1_atu_loadpurge ( struct mv88e6xxx_chip * chip , u16 fid ,
struct mv88e6xxx_atu_entry * entry )
{
int err ;
err = mv88e6xxx_g1_atu_op_wait ( chip ) ;
if ( err )
return err ;
err = mv88e6xxx_g1_atu_mac_write ( chip , entry ) ;
if ( err )
return err ;
err = mv88e6xxx_g1_atu_data_write ( chip , entry ) ;
if ( err )
return err ;
2017-06-15 12:14:01 -04:00
return mv88e6xxx_g1_atu_op ( chip , fid , MV88E6XXX_G1_ATU_OP_LOAD_DB ) ;
2017-03-11 16:12:52 -05:00
}
2017-03-11 16:12:54 -05:00
static int mv88e6xxx_g1_atu_flushmove ( struct mv88e6xxx_chip * chip , u16 fid ,
struct mv88e6xxx_atu_entry * entry ,
bool all )
{
u16 op ;
int err ;
err = mv88e6xxx_g1_atu_op_wait ( chip ) ;
if ( err )
return err ;
err = mv88e6xxx_g1_atu_data_write ( chip , entry ) ;
if ( err )
return err ;
/* Flush/Move all or non-static entries from all or a given database */
if ( all & & fid )
2017-06-15 12:14:01 -04:00
op = MV88E6XXX_G1_ATU_OP_FLUSH_MOVE_ALL_DB ;
2017-03-11 16:12:54 -05:00
else if ( fid )
2017-06-15 12:14:01 -04:00
op = MV88E6XXX_G1_ATU_OP_FLUSH_MOVE_NON_STATIC_DB ;
2017-03-11 16:12:54 -05:00
else if ( all )
2017-06-15 12:14:01 -04:00
op = MV88E6XXX_G1_ATU_OP_FLUSH_MOVE_ALL ;
2017-03-11 16:12:54 -05:00
else
2017-06-15 12:14:01 -04:00
op = MV88E6XXX_G1_ATU_OP_FLUSH_MOVE_NON_STATIC ;
2017-03-11 16:12:54 -05:00
return mv88e6xxx_g1_atu_op ( chip , fid , op ) ;
}
int mv88e6xxx_g1_atu_flush ( struct mv88e6xxx_chip * chip , u16 fid , bool all )
{
struct mv88e6xxx_atu_entry entry = {
. state = 0 , /* Null EntryState means Flush */
} ;
return mv88e6xxx_g1_atu_flushmove ( chip , fid , & entry , all ) ;
}
2017-03-11 16:12:55 -05:00
static int mv88e6xxx_g1_atu_move ( struct mv88e6xxx_chip * chip , u16 fid ,
int from_port , int to_port , bool all )
{
struct mv88e6xxx_atu_entry entry = { 0 } ;
unsigned long mask ;
int shift ;
if ( ! chip - > info - > atu_move_port_mask )
return - EOPNOTSUPP ;
mask = chip - > info - > atu_move_port_mask ;
shift = bitmap_weight ( & mask , 16 ) ;
entry . state = 0xf , /* Full EntryState means Move */
2017-03-11 16:12:57 -05:00
entry . portvec = from_port & mask ;
entry . portvec | = ( to_port & mask ) < < shift ;
2017-03-11 16:12:55 -05:00
return mv88e6xxx_g1_atu_flushmove ( chip , fid , & entry , all ) ;
}
int mv88e6xxx_g1_atu_remove ( struct mv88e6xxx_chip * chip , u16 fid , int port ,
bool all )
{
int from_port = port ;
int to_port = chip - > info - > atu_move_port_mask ;
return mv88e6xxx_g1_atu_move ( chip , fid , from_port , to_port , all ) ;
}
2018-01-14 02:32:44 +01:00
static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn ( int irq , void * dev_id )
{
struct mv88e6xxx_chip * chip = dev_id ;
struct mv88e6xxx_atu_entry entry ;
2019-02-06 00:02:58 +01:00
int spid ;
2018-01-14 02:32:44 +01:00
int err ;
u16 val ;
2019-06-20 13:50:42 +00:00
mv88e6xxx_reg_lock ( chip ) ;
2018-01-14 02:32:44 +01:00
err = mv88e6xxx_g1_atu_op ( chip , 0 ,
MV88E6XXX_G1_ATU_OP_GET_CLR_VIOLATION ) ;
if ( err )
goto out ;
err = mv88e6xxx_g1_read ( chip , MV88E6XXX_G1_ATU_OP , & val ) ;
if ( err )
goto out ;
err = mv88e6xxx_g1_atu_data_read ( chip , & entry ) ;
if ( err )
goto out ;
err = mv88e6xxx_g1_atu_mac_read ( chip , & entry ) ;
if ( err )
goto out ;
2019-02-06 00:02:58 +01:00
spid = entry . state ;
2018-01-14 02:32:44 +01:00
if ( val & MV88E6XXX_G1_ATU_OP_AGE_OUT_VIOLATION ) {
dev_err_ratelimited ( chip - > dev ,
" ATU age out violation for %pM \n " ,
entry . mac ) ;
}
if ( val & MV88E6XXX_G1_ATU_OP_MEMBER_VIOLATION ) {
dev_err_ratelimited ( chip - > dev ,
2019-02-06 00:02:58 +01:00
" ATU member violation for %pM portvec %x spid %d \n " ,
entry . mac , entry . portvec , spid ) ;
chip - > ports [ spid ] . atu_member_violation + + ;
2018-01-14 02:32:44 +01:00
}
2018-09-14 23:46:12 +02:00
if ( val & MV88E6XXX_G1_ATU_OP_MISS_VIOLATION ) {
2018-01-14 02:32:44 +01:00
dev_err_ratelimited ( chip - > dev ,
2019-02-06 00:02:58 +01:00
" ATU miss violation for %pM portvec %x spid %d \n " ,
entry . mac , entry . portvec , spid ) ;
chip - > ports [ spid ] . atu_miss_violation + + ;
2018-03-28 23:50:28 +02:00
}
2018-01-14 02:32:44 +01:00
2018-03-28 23:50:28 +02:00
if ( val & MV88E6XXX_G1_ATU_OP_FULL_VIOLATION ) {
2018-01-14 02:32:44 +01:00
dev_err_ratelimited ( chip - > dev ,
2019-02-06 00:02:58 +01:00
" ATU full violation for %pM portvec %x spid %d \n " ,
entry . mac , entry . portvec , spid ) ;
chip - > ports [ spid ] . atu_full_violation + + ;
2018-03-28 23:50:28 +02:00
}
2019-06-20 13:50:42 +00:00
mv88e6xxx_reg_unlock ( chip ) ;
2018-01-14 02:32:44 +01:00
return IRQ_HANDLED ;
out :
2019-06-20 13:50:42 +00:00
mv88e6xxx_reg_unlock ( chip ) ;
2018-01-14 02:32:44 +01:00
dev_err ( chip - > dev , " ATU problem: error %d while handling interrupt \n " ,
err ) ;
return IRQ_HANDLED ;
}
int mv88e6xxx_g1_atu_prob_irq_setup ( struct mv88e6xxx_chip * chip )
{
int err ;
chip - > atu_prob_irq = irq_find_mapping ( chip - > g1_irq . domain ,
MV88E6XXX_G1_STS_IRQ_ATU_PROB ) ;
if ( chip - > atu_prob_irq < 0 )
2018-01-18 17:42:49 +01:00
return chip - > atu_prob_irq ;
2018-01-14 02:32:44 +01:00
err = request_threaded_irq ( chip - > atu_prob_irq , NULL ,
mv88e6xxx_g1_atu_prob_irq_thread_fn ,
IRQF_ONESHOT , " mv88e6xxx-g1-atu-prob " ,
chip ) ;
if ( err )
irq_dispose_mapping ( chip - > atu_prob_irq ) ;
return err ;
}
void mv88e6xxx_g1_atu_prob_irq_free ( struct mv88e6xxx_chip * chip )
{
free_irq ( chip - > atu_prob_irq , chip ) ;
irq_dispose_mapping ( chip - > atu_prob_irq ) ;
}