@@ -639,6 +639,146 @@ static int a5psw_port_fdb_dump(struct dsa_switch *ds, int port,
639639 return ret ;
640640}
641641
642+ static int a5psw_port_vlan_filtering (struct dsa_switch * ds , int port ,
643+ bool vlan_filtering ,
644+ struct netlink_ext_ack * extack )
645+ {
646+ u32 mask = BIT (port + A5PSW_VLAN_VERI_SHIFT ) |
647+ BIT (port + A5PSW_VLAN_DISC_SHIFT );
648+ u32 val = vlan_filtering ? mask : 0 ;
649+ struct a5psw * a5psw = ds -> priv ;
650+
651+ /* Disable/enable vlan tagging */
652+ a5psw_reg_rmw (a5psw , A5PSW_VLAN_IN_MODE_ENA , BIT (port ),
653+ vlan_filtering ? BIT (port ) : 0 );
654+
655+ /* Disable/enable vlan input filtering */
656+ a5psw_reg_rmw (a5psw , A5PSW_VLAN_VERIFY , mask , val );
657+
658+ return 0 ;
659+ }
660+
661+ static int a5psw_find_vlan_entry (struct a5psw * a5psw , u16 vid )
662+ {
663+ u32 vlan_res ;
664+ int i ;
665+
666+ /* Find vlan for this port */
667+ for (i = 0 ; i < A5PSW_VLAN_COUNT ; i ++ ) {
668+ vlan_res = a5psw_reg_readl (a5psw , A5PSW_VLAN_RES (i ));
669+ if (FIELD_GET (A5PSW_VLAN_RES_VLANID , vlan_res ) == vid )
670+ return i ;
671+ }
672+
673+ return -1 ;
674+ }
675+
676+ static int a5psw_new_vlan_res_entry (struct a5psw * a5psw , u16 newvid )
677+ {
678+ u32 vlan_res ;
679+ int i ;
680+
681+ /* Find a free VLAN entry */
682+ for (i = 0 ; i < A5PSW_VLAN_COUNT ; i ++ ) {
683+ vlan_res = a5psw_reg_readl (a5psw , A5PSW_VLAN_RES (i ));
684+ if (!(FIELD_GET (A5PSW_VLAN_RES_PORTMASK , vlan_res ))) {
685+ vlan_res = FIELD_PREP (A5PSW_VLAN_RES_VLANID , newvid );
686+ a5psw_reg_writel (a5psw , A5PSW_VLAN_RES (i ), vlan_res );
687+ return i ;
688+ }
689+ }
690+
691+ return -1 ;
692+ }
693+
694+ static void a5psw_port_vlan_tagged_cfg (struct a5psw * a5psw ,
695+ unsigned int vlan_res_id , int port ,
696+ bool set )
697+ {
698+ u32 mask = A5PSW_VLAN_RES_WR_PORTMASK | A5PSW_VLAN_RES_RD_TAGMASK |
699+ BIT (port );
700+ u32 vlan_res_off = A5PSW_VLAN_RES (vlan_res_id );
701+ u32 val = A5PSW_VLAN_RES_WR_TAGMASK , reg ;
702+
703+ if (set )
704+ val |= BIT (port );
705+
706+ /* Toggle tag mask read */
707+ a5psw_reg_writel (a5psw , vlan_res_off , A5PSW_VLAN_RES_RD_TAGMASK );
708+ reg = a5psw_reg_readl (a5psw , vlan_res_off );
709+ a5psw_reg_writel (a5psw , vlan_res_off , A5PSW_VLAN_RES_RD_TAGMASK );
710+
711+ reg &= ~mask ;
712+ reg |= val ;
713+ a5psw_reg_writel (a5psw , vlan_res_off , reg );
714+ }
715+
716+ static void a5psw_port_vlan_cfg (struct a5psw * a5psw , unsigned int vlan_res_id ,
717+ int port , bool set )
718+ {
719+ u32 mask = A5PSW_VLAN_RES_WR_TAGMASK | BIT (port );
720+ u32 reg = A5PSW_VLAN_RES_WR_PORTMASK ;
721+
722+ if (set )
723+ reg |= BIT (port );
724+
725+ a5psw_reg_rmw (a5psw , A5PSW_VLAN_RES (vlan_res_id ), mask , reg );
726+ }
727+
728+ static int a5psw_port_vlan_add (struct dsa_switch * ds , int port ,
729+ const struct switchdev_obj_port_vlan * vlan ,
730+ struct netlink_ext_ack * extack )
731+ {
732+ bool tagged = !(vlan -> flags & BRIDGE_VLAN_INFO_UNTAGGED );
733+ bool pvid = vlan -> flags & BRIDGE_VLAN_INFO_PVID ;
734+ struct a5psw * a5psw = ds -> priv ;
735+ u16 vid = vlan -> vid ;
736+ int vlan_res_id ;
737+
738+ dev_dbg (a5psw -> dev , "Add VLAN %d on port %d, %s, %s\n" ,
739+ vid , port , tagged ? "tagged" : "untagged" ,
740+ pvid ? "PVID" : "no PVID" );
741+
742+ vlan_res_id = a5psw_find_vlan_entry (a5psw , vid );
743+ if (vlan_res_id < 0 ) {
744+ vlan_res_id = a5psw_new_vlan_res_entry (a5psw , vid );
745+ if (vlan_res_id < 0 )
746+ return - ENOSPC ;
747+ }
748+
749+ a5psw_port_vlan_cfg (a5psw , vlan_res_id , port , true);
750+ if (tagged )
751+ a5psw_port_vlan_tagged_cfg (a5psw , vlan_res_id , port , true);
752+
753+ /* Configure port to tag with corresponding VID, but do not enable it
754+ * yet: wait for vlan filtering to be enabled to enable vlan port
755+ * tagging
756+ */
757+ if (pvid )
758+ a5psw_reg_writel (a5psw , A5PSW_SYSTEM_TAGINFO (port ), vid );
759+
760+ return 0 ;
761+ }
762+
763+ static int a5psw_port_vlan_del (struct dsa_switch * ds , int port ,
764+ const struct switchdev_obj_port_vlan * vlan )
765+ {
766+ struct a5psw * a5psw = ds -> priv ;
767+ u16 vid = vlan -> vid ;
768+ int vlan_res_id ;
769+
770+ dev_dbg (a5psw -> dev , "Removing VLAN %d on port %d\n" , vid , port );
771+
772+ vlan_res_id = a5psw_find_vlan_entry (a5psw , vid );
773+ if (vlan_res_id < 0 )
774+ return - EINVAL ;
775+
776+ a5psw_port_vlan_cfg (a5psw , vlan_res_id , port , false);
777+ a5psw_port_vlan_tagged_cfg (a5psw , vlan_res_id , port , false);
778+
779+ return 0 ;
780+ }
781+
642782static u64 a5psw_read_stat (struct a5psw * a5psw , u32 offset , int port )
643783{
644784 u32 reg_lo , reg_hi ;
@@ -756,6 +896,27 @@ static void a5psw_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
756896 ctrl_stats -> MACControlFramesReceived = stat ;
757897}
758898
899+ static void a5psw_vlan_setup (struct a5psw * a5psw , int port )
900+ {
901+ u32 reg ;
902+
903+ /* Enable TAG always mode for the port, this is actually controlled
904+ * by VLAN_IN_MODE_ENA field which will be used for PVID insertion
905+ */
906+ reg = A5PSW_VLAN_IN_MODE_TAG_ALWAYS ;
907+ reg <<= A5PSW_VLAN_IN_MODE_PORT_SHIFT (port );
908+ a5psw_reg_rmw (a5psw , A5PSW_VLAN_IN_MODE , A5PSW_VLAN_IN_MODE_PORT (port ),
909+ reg );
910+
911+ /* Set transparent mode for output frame manipulation, this will depend
912+ * on the VLAN_RES configuration mode
913+ */
914+ reg = A5PSW_VLAN_OUT_MODE_TRANSPARENT ;
915+ reg <<= A5PSW_VLAN_OUT_MODE_PORT_SHIFT (port );
916+ a5psw_reg_rmw (a5psw , A5PSW_VLAN_OUT_MODE ,
917+ A5PSW_VLAN_OUT_MODE_PORT (port ), reg );
918+ }
919+
759920static int a5psw_setup (struct dsa_switch * ds )
760921{
761922 struct a5psw * a5psw = ds -> priv ;
@@ -830,6 +991,8 @@ static int a5psw_setup(struct dsa_switch *ds)
830991 /* Enable standalone mode for user ports */
831992 if (dsa_port_is_user (dp ))
832993 a5psw_port_set_standalone (a5psw , port , true);
994+
995+ a5psw_vlan_setup (a5psw , port );
833996 }
834997
835998 return 0 ;
@@ -859,6 +1022,9 @@ static const struct dsa_switch_ops a5psw_switch_ops = {
8591022 .port_bridge_flags = a5psw_port_bridge_flags ,
8601023 .port_stp_state_set = a5psw_port_stp_state_set ,
8611024 .port_fast_age = a5psw_port_fast_age ,
1025+ .port_vlan_filtering = a5psw_port_vlan_filtering ,
1026+ .port_vlan_add = a5psw_port_vlan_add ,
1027+ .port_vlan_del = a5psw_port_vlan_del ,
8621028 .port_fdb_add = a5psw_port_fdb_add ,
8631029 .port_fdb_del = a5psw_port_fdb_del ,
8641030 .port_fdb_dump = a5psw_port_fdb_dump ,
0 commit comments