2014-10-29 10:44:56 -07:00
/*
* net / dsa / mv88e6352 . c - Marvell 88e6352 switch chip support
*
* Copyright ( c ) 2014 Guenter Roeck
*
* Derived from mv88e6123_61_65 . c
* Copyright ( c ) 2008 - 2009 Marvell Semiconductor
*
* 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 <linux/delay.h>
# include <linux/jiffies.h>
# include <linux/list.h>
# include <linux/module.h>
# include <linux/netdevice.h>
# include <linux/platform_device.h>
# include <linux/phy.h>
# include <net/dsa.h>
# include "mv88e6xxx.h"
static char * mv88e6352_probe ( struct device * host_dev , int sw_addr )
{
struct mii_bus * bus = dsa_host_dev_to_mii_bus ( host_dev ) ;
int ret ;
if ( bus = = NULL )
return NULL ;
2015-04-02 04:06:39 +02:00
ret = __mv88e6xxx_reg_read ( bus , sw_addr , REG_PORT ( 0 ) , PORT_SWITCH_ID ) ;
2014-10-29 10:44:56 -07:00
if ( ret > = 0 ) {
2015-05-06 01:09:50 +02:00
if ( ( ret & 0xfff0 ) = = PORT_SWITCH_ID_6172 )
return " Marvell 88E6172 " ;
2015-04-02 04:06:39 +02:00
if ( ( ret & 0xfff0 ) = = PORT_SWITCH_ID_6176 )
2014-10-29 10:44:57 -07:00
return " Marvell 88E6176 " ;
2015-07-07 20:38:15 -07:00
if ( ret = = PORT_SWITCH_ID_6320_A1 )
return " Marvell 88E6320 (A1) " ;
if ( ret = = PORT_SWITCH_ID_6320_A2 )
return " Marvell 88e6320 (A2) " ;
if ( ( ret & 0xfff0 ) = = PORT_SWITCH_ID_6320 )
return " Marvell 88E6320 " ;
if ( ret = = PORT_SWITCH_ID_6321_A1 )
return " Marvell 88E6321 (A1) " ;
if ( ret = = PORT_SWITCH_ID_6321_A2 )
return " Marvell 88e6321 (A2) " ;
if ( ( ret & 0xfff0 ) = = PORT_SWITCH_ID_6321 )
return " Marvell 88E6321 " ;
2015-04-02 04:06:39 +02:00
if ( ret = = PORT_SWITCH_ID_6352_A0 )
2014-10-29 10:44:56 -07:00
return " Marvell 88E6352 (A0) " ;
2015-04-02 04:06:39 +02:00
if ( ret = = PORT_SWITCH_ID_6352_A1 )
2014-10-29 10:44:56 -07:00
return " Marvell 88E6352 (A1) " ;
2015-04-02 04:06:39 +02:00
if ( ( ret & 0xfff0 ) = = PORT_SWITCH_ID_6352 )
2014-10-29 10:44:56 -07:00
return " Marvell 88E6352 " ;
}
return NULL ;
}
static int mv88e6352_setup_global ( struct dsa_switch * ds )
{
2015-05-06 01:09:49 +02:00
u32 upstream_port = dsa_upstream_port ( ds ) ;
2014-10-29 10:44:56 -07:00
int ret ;
2015-05-06 01:09:49 +02:00
u32 reg ;
2015-05-06 01:09:47 +02:00
ret = mv88e6xxx_setup_global ( ds ) ;
if ( ret )
return ret ;
2014-10-29 10:44:56 -07:00
/* Discard packets with excessive collisions,
* mask all interrupt sources , enable PPU ( bit 14 , undocumented ) .
*/
2015-05-06 01:09:49 +02:00
REG_WRITE ( REG_GLOBAL , GLOBAL_CONTROL ,
GLOBAL_CONTROL_PPU_ENABLE | GLOBAL_CONTROL_DISCARD_EXCESS ) ;
2014-10-29 10:44:56 -07:00
/* Configure the upstream port, and configure the upstream
* port as the port to which ingress and egress monitor frames
* are to be sent .
*/
2015-05-06 01:09:49 +02:00
reg = upstream_port < < GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT |
upstream_port < < GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT |
upstream_port < < GLOBAL_MONITOR_CONTROL_ARP_SHIFT ;
REG_WRITE ( REG_GLOBAL , GLOBAL_MONITOR_CONTROL , reg ) ;
2014-10-29 10:44:56 -07:00
/* Disable remote management for now, and set the switch's
* DSA device number .
*/
REG_WRITE ( REG_GLOBAL , 0x1c , ds - > index & 0x1f ) ;
return 0 ;
}
static int mv88e6352_setup ( struct dsa_switch * ds )
{
struct mv88e6xxx_priv_state * ps = ds_to_priv ( ds ) ;
int ret ;
2015-03-26 18:36:28 -07:00
ret = mv88e6xxx_setup_common ( ds ) ;
if ( ret < 0 )
return ret ;
2015-04-02 04:06:33 +02:00
ps - > num_ports = 7 ;
2014-10-29 10:45:03 -07:00
mutex_init ( & ps - > eeprom_mutex ) ;
2014-10-29 10:44:56 -07:00
2015-04-02 04:06:34 +02:00
ret = mv88e6xxx_switch_reset ( ds , true ) ;
2014-10-29 10:44:56 -07:00
if ( ret < 0 )
return ret ;
ret = mv88e6352_setup_global ( ds ) ;
if ( ret < 0 )
return ret ;
2015-05-06 01:09:48 +02:00
return mv88e6xxx_setup_ports ( ds ) ;
2014-10-29 10:44:56 -07:00
}
2014-10-29 10:45:03 -07:00
static int mv88e6352_read_eeprom_word ( struct dsa_switch * ds , int addr )
{
struct mv88e6xxx_priv_state * ps = ds_to_priv ( ds ) ;
int ret ;
mutex_lock ( & ps - > eeprom_mutex ) ;
2015-08-08 17:04:50 +02:00
ret = mv88e6xxx_reg_write ( ds , REG_GLOBAL2 , GLOBAL2_EEPROM_OP ,
GLOBAL2_EEPROM_OP_READ |
( addr & GLOBAL2_EEPROM_OP_ADDR_MASK ) ) ;
2014-10-29 10:45:03 -07:00
if ( ret < 0 )
goto error ;
2015-02-14 19:17:50 +01:00
ret = mv88e6xxx_eeprom_busy_wait ( ds ) ;
2014-10-29 10:45:03 -07:00
if ( ret < 0 )
goto error ;
2015-08-08 17:04:50 +02:00
ret = mv88e6xxx_reg_read ( ds , REG_GLOBAL2 , GLOBAL2_EEPROM_DATA ) ;
2014-10-29 10:45:03 -07:00
error :
mutex_unlock ( & ps - > eeprom_mutex ) ;
return ret ;
}
static int mv88e6352_get_eeprom ( struct dsa_switch * ds ,
struct ethtool_eeprom * eeprom , u8 * data )
{
int offset ;
int len ;
int ret ;
offset = eeprom - > offset ;
len = eeprom - > len ;
eeprom - > len = 0 ;
eeprom - > magic = 0xc3ec4951 ;
2015-02-14 19:17:50 +01:00
ret = mv88e6xxx_eeprom_load_wait ( ds ) ;
2014-10-29 10:45:03 -07:00
if ( ret < 0 )
return ret ;
if ( offset & 1 ) {
int word ;
word = mv88e6352_read_eeprom_word ( ds , offset > > 1 ) ;
if ( word < 0 )
return word ;
* data + + = ( word > > 8 ) & 0xff ;
offset + + ;
len - - ;
eeprom - > len + + ;
}
while ( len > = 2 ) {
int word ;
word = mv88e6352_read_eeprom_word ( ds , offset > > 1 ) ;
if ( word < 0 )
return word ;
* data + + = word & 0xff ;
* data + + = ( word > > 8 ) & 0xff ;
offset + = 2 ;
len - = 2 ;
eeprom - > len + = 2 ;
}
if ( len ) {
int word ;
word = mv88e6352_read_eeprom_word ( ds , offset > > 1 ) ;
if ( word < 0 )
return word ;
* data + + = word & 0xff ;
offset + + ;
len - - ;
eeprom - > len + + ;
}
return 0 ;
}
static int mv88e6352_eeprom_is_readonly ( struct dsa_switch * ds )
{
int ret ;
2015-08-08 17:04:50 +02:00
ret = mv88e6xxx_reg_read ( ds , REG_GLOBAL2 , GLOBAL2_EEPROM_OP ) ;
2014-10-29 10:45:03 -07:00
if ( ret < 0 )
return ret ;
2015-08-08 17:04:50 +02:00
if ( ! ( ret & GLOBAL2_EEPROM_OP_WRITE_EN ) )
2014-10-29 10:45:03 -07:00
return - EROFS ;
return 0 ;
}
static int mv88e6352_write_eeprom_word ( struct dsa_switch * ds , int addr ,
u16 data )
{
struct mv88e6xxx_priv_state * ps = ds_to_priv ( ds ) ;
int ret ;
mutex_lock ( & ps - > eeprom_mutex ) ;
2015-08-08 17:04:50 +02:00
ret = mv88e6xxx_reg_write ( ds , REG_GLOBAL2 , GLOBAL2_EEPROM_DATA , data ) ;
2014-10-29 10:45:03 -07:00
if ( ret < 0 )
goto error ;
2015-08-08 17:04:50 +02:00
ret = mv88e6xxx_reg_write ( ds , REG_GLOBAL2 , GLOBAL2_EEPROM_OP ,
GLOBAL2_EEPROM_OP_WRITE |
( addr & GLOBAL2_EEPROM_OP_ADDR_MASK ) ) ;
2014-10-29 10:45:03 -07:00
if ( ret < 0 )
goto error ;
2015-02-14 19:17:50 +01:00
ret = mv88e6xxx_eeprom_busy_wait ( ds ) ;
2014-10-29 10:45:03 -07:00
error :
mutex_unlock ( & ps - > eeprom_mutex ) ;
return ret ;
}
static int mv88e6352_set_eeprom ( struct dsa_switch * ds ,
struct ethtool_eeprom * eeprom , u8 * data )
{
int offset ;
int ret ;
int len ;
if ( eeprom - > magic ! = 0xc3ec4951 )
return - EINVAL ;
ret = mv88e6352_eeprom_is_readonly ( ds ) ;
if ( ret )
return ret ;
offset = eeprom - > offset ;
len = eeprom - > len ;
eeprom - > len = 0 ;
2015-02-14 19:17:50 +01:00
ret = mv88e6xxx_eeprom_load_wait ( ds ) ;
2014-10-29 10:45:03 -07:00
if ( ret < 0 )
return ret ;
if ( offset & 1 ) {
int word ;
word = mv88e6352_read_eeprom_word ( ds , offset > > 1 ) ;
if ( word < 0 )
return word ;
word = ( * data + + < < 8 ) | ( word & 0xff ) ;
ret = mv88e6352_write_eeprom_word ( ds , offset > > 1 , word ) ;
if ( ret < 0 )
return ret ;
offset + + ;
len - - ;
eeprom - > len + + ;
}
while ( len > = 2 ) {
int word ;
word = * data + + ;
word | = * data + + < < 8 ;
ret = mv88e6352_write_eeprom_word ( ds , offset > > 1 , word ) ;
if ( ret < 0 )
return ret ;
offset + = 2 ;
len - = 2 ;
eeprom - > len + = 2 ;
}
if ( len ) {
int word ;
word = mv88e6352_read_eeprom_word ( ds , offset > > 1 ) ;
if ( word < 0 )
return word ;
word = ( word & 0xff00 ) | * data + + ;
ret = mv88e6352_write_eeprom_word ( ds , offset > > 1 , word ) ;
if ( ret < 0 )
return ret ;
offset + + ;
len - - ;
eeprom - > len + + ;
}
return 0 ;
}
2014-10-29 10:44:56 -07:00
struct dsa_switch_driver mv88e6352_switch_driver = {
. tag_protocol = DSA_TAG_PROTO_EDSA ,
. priv_size = sizeof ( struct mv88e6xxx_priv_state ) ,
. probe = mv88e6352_probe ,
. setup = mv88e6352_setup ,
. set_addr = mv88e6xxx_set_addr_indirect ,
2015-04-02 04:06:36 +02:00
. phy_read = mv88e6xxx_phy_read_indirect ,
. phy_write = mv88e6xxx_phy_write_indirect ,
2015-04-02 04:06:38 +02:00
. get_strings = mv88e6xxx_get_strings ,
. get_ethtool_stats = mv88e6xxx_get_ethtool_stats ,
. get_sset_count = mv88e6xxx_get_sset_count ,
2015-08-31 15:56:47 +02:00
. adjust_link = mv88e6xxx_adjust_link ,
2015-03-06 22:23:52 -08:00
. set_eee = mv88e6xxx_set_eee ,
. get_eee = mv88e6xxx_get_eee ,
2014-10-29 10:44:59 -07:00
# ifdef CONFIG_NET_DSA_HWMON
2015-07-25 09:42:28 -07:00
. get_temp = mv88e6xxx_get_temp ,
. get_temp_limit = mv88e6xxx_get_temp_limit ,
. set_temp_limit = mv88e6xxx_set_temp_limit ,
. get_temp_alarm = mv88e6xxx_get_temp_alarm ,
2014-10-29 10:44:59 -07:00
# endif
2014-10-29 10:45:03 -07:00
. get_eeprom = mv88e6352_get_eeprom ,
. set_eeprom = mv88e6352_set_eeprom ,
2014-10-29 10:45:06 -07:00
. get_regs_len = mv88e6xxx_get_regs_len ,
. get_regs = mv88e6xxx_get_regs ,
2015-03-26 18:36:36 -07:00
. port_join_bridge = mv88e6xxx_join_bridge ,
. port_leave_bridge = mv88e6xxx_leave_bridge ,
. port_stp_update = mv88e6xxx_port_stp_update ,
2015-08-13 12:52:19 -04:00
. port_pvid_get = mv88e6xxx_port_pvid_get ,
2015-08-13 12:52:22 -04:00
. port_pvid_set = mv88e6xxx_port_pvid_set ,
. port_vlan_add = mv88e6xxx_port_vlan_add ,
2015-08-13 12:52:21 -04:00
. port_vlan_del = mv88e6xxx_port_vlan_del ,
2015-08-13 12:52:19 -04:00
. vlan_getnext = mv88e6xxx_vlan_getnext ,
2015-08-10 09:09:49 -04:00
. port_fdb_add = mv88e6xxx_port_fdb_add ,
. port_fdb_del = mv88e6xxx_port_fdb_del ,
. port_fdb_getnext = mv88e6xxx_port_fdb_getnext ,
2014-10-29 10:44:56 -07:00
} ;
2015-05-06 01:09:50 +02:00
MODULE_ALIAS ( " platform:mv88e6172 " ) ;
2015-07-07 20:38:15 -07:00
MODULE_ALIAS ( " platform:mv88e6176 " ) ;
MODULE_ALIAS ( " platform:mv88e6320 " ) ;
MODULE_ALIAS ( " platform:mv88e6321 " ) ;
MODULE_ALIAS ( " platform:mv88e6352 " ) ;