Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: dsa: rzn1-a5psw: add vlan support

Add support for vlan operation (add, del, filtering) on the RZN1
driver. The a5psw switch supports up to 32 VLAN IDs with filtering,
tagged/untagged VLANs and PVID for each ports.

Signed-off-by: Clément Léger <clement.leger@bootlin.com>
Signed-off-by: Alexis Lothoré <alexis.lothore@bootlin.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Clément Léger and committed by
David S. Miller
7b3f77c4 0d37f839

+171 -3
+166
drivers/net/dsa/rzn1_a5psw.c
··· 639 639 return ret; 640 640 } 641 641 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 + 642 782 static u64 a5psw_read_stat(struct a5psw *a5psw, u32 offset, int port) 643 783 { 644 784 u32 reg_lo, reg_hi; ··· 896 756 ctrl_stats->MACControlFramesReceived = stat; 897 757 } 898 758 759 + static void a5psw_vlan_setup(struct a5psw *a5psw, int port) 760 + { 761 + u32 reg; 762 + 763 + /* Enable TAG always mode for the port, this is actually controlled 764 + * by VLAN_IN_MODE_ENA field which will be used for PVID insertion 765 + */ 766 + reg = A5PSW_VLAN_IN_MODE_TAG_ALWAYS; 767 + reg <<= A5PSW_VLAN_IN_MODE_PORT_SHIFT(port); 768 + a5psw_reg_rmw(a5psw, A5PSW_VLAN_IN_MODE, A5PSW_VLAN_IN_MODE_PORT(port), 769 + reg); 770 + 771 + /* Set transparent mode for output frame manipulation, this will depend 772 + * on the VLAN_RES configuration mode 773 + */ 774 + reg = A5PSW_VLAN_OUT_MODE_TRANSPARENT; 775 + reg <<= A5PSW_VLAN_OUT_MODE_PORT_SHIFT(port); 776 + a5psw_reg_rmw(a5psw, A5PSW_VLAN_OUT_MODE, 777 + A5PSW_VLAN_OUT_MODE_PORT(port), reg); 778 + } 779 + 899 780 static int a5psw_setup(struct dsa_switch *ds) 900 781 { 901 782 struct a5psw *a5psw = ds->priv; ··· 991 830 /* Enable standalone mode for user ports */ 992 831 if (dsa_port_is_user(dp)) 993 832 a5psw_port_set_standalone(a5psw, port, true); 833 + 834 + a5psw_vlan_setup(a5psw, port); 994 835 } 995 836 996 837 return 0; ··· 1022 859 .port_bridge_flags = a5psw_port_bridge_flags, 1023 860 .port_stp_state_set = a5psw_port_stp_state_set, 1024 861 .port_fast_age = a5psw_port_fast_age, 862 + .port_vlan_filtering = a5psw_port_vlan_filtering, 863 + .port_vlan_add = a5psw_port_vlan_add, 864 + .port_vlan_del = a5psw_port_vlan_del, 1025 865 .port_fdb_add = a5psw_port_fdb_add, 1026 866 .port_fdb_del = a5psw_port_fdb_del, 1027 867 .port_fdb_dump = a5psw_port_fdb_dump,
+5 -3
drivers/net/dsa/rzn1_a5psw.h
··· 51 51 #define A5PSW_VLAN_IN_MODE_TAG_ALWAYS 0x2 52 52 53 53 #define A5PSW_VLAN_OUT_MODE 0x2C 54 - #define A5PSW_VLAN_OUT_MODE_PORT(port) (GENMASK(1, 0) << ((port) * 2)) 54 + #define A5PSW_VLAN_OUT_MODE_PORT_SHIFT(port) ((port) * 2) 55 + #define A5PSW_VLAN_OUT_MODE_PORT(port) (GENMASK(1, 0) << \ 56 + A5PSW_VLAN_OUT_MODE_PORT_SHIFT(port)) 55 57 #define A5PSW_VLAN_OUT_MODE_DIS 0x0 56 58 #define A5PSW_VLAN_OUT_MODE_STRIP 0x1 57 59 #define A5PSW_VLAN_OUT_MODE_TAG_THROUGH 0x2 ··· 62 60 #define A5PSW_VLAN_IN_MODE_ENA 0x30 63 61 #define A5PSW_VLAN_TAG_ID 0x34 64 62 65 - #define A5PSW_SYSTEM_TAGINFO(port) (0x200 + A5PSW_PORT_OFFSET(port)) 63 + #define A5PSW_SYSTEM_TAGINFO(port) (0x200 + 4 * (port)) 66 64 67 65 #define A5PSW_AUTH_PORT(port) (0x240 + 4 * (port)) 68 66 #define A5PSW_AUTH_PORT_AUTHORIZED BIT(0) ··· 71 69 #define A5PSW_VLAN_RES_WR_PORTMASK BIT(30) 72 70 #define A5PSW_VLAN_RES_WR_TAGMASK BIT(29) 73 71 #define A5PSW_VLAN_RES_RD_TAGMASK BIT(28) 74 - #define A5PSW_VLAN_RES_ID GENMASK(16, 5) 72 + #define A5PSW_VLAN_RES_VLANID GENMASK(16, 5) 75 73 #define A5PSW_VLAN_RES_PORTMASK GENMASK(4, 0) 76 74 77 75 #define A5PSW_RXMATCH_CONFIG(port) (0x3e80 + 4 * (port))