1*3fe15c64SLorenzo Bianconi // SPDX-License-Identifier: GPL-2.0-only 2*3fe15c64SLorenzo Bianconi /* 3*3fe15c64SLorenzo Bianconi * Copyright (c) 2025 AIROHA Inc 4*3fe15c64SLorenzo Bianconi * Author: Lorenzo Bianconi <lorenzo@kernel.org> 5*3fe15c64SLorenzo Bianconi */ 6*3fe15c64SLorenzo Bianconi 7*3fe15c64SLorenzo Bianconi #include "airoha_eth.h" 8*3fe15c64SLorenzo Bianconi 9*3fe15c64SLorenzo Bianconi static void airoha_debugfs_ppe_print_tuple(struct seq_file *m, 10*3fe15c64SLorenzo Bianconi void *src_addr, void *dest_addr, 11*3fe15c64SLorenzo Bianconi u16 *src_port, u16 *dest_port, 12*3fe15c64SLorenzo Bianconi bool ipv6) 13*3fe15c64SLorenzo Bianconi { 14*3fe15c64SLorenzo Bianconi __be32 n_addr[IPV6_ADDR_WORDS]; 15*3fe15c64SLorenzo Bianconi 16*3fe15c64SLorenzo Bianconi if (ipv6) { 17*3fe15c64SLorenzo Bianconi ipv6_addr_cpu_to_be32(n_addr, src_addr); 18*3fe15c64SLorenzo Bianconi seq_printf(m, "%pI6", n_addr); 19*3fe15c64SLorenzo Bianconi } else { 20*3fe15c64SLorenzo Bianconi seq_printf(m, "%pI4h", src_addr); 21*3fe15c64SLorenzo Bianconi } 22*3fe15c64SLorenzo Bianconi if (src_port) 23*3fe15c64SLorenzo Bianconi seq_printf(m, ":%d", *src_port); 24*3fe15c64SLorenzo Bianconi 25*3fe15c64SLorenzo Bianconi seq_puts(m, "->"); 26*3fe15c64SLorenzo Bianconi 27*3fe15c64SLorenzo Bianconi if (ipv6) { 28*3fe15c64SLorenzo Bianconi ipv6_addr_cpu_to_be32(n_addr, dest_addr); 29*3fe15c64SLorenzo Bianconi seq_printf(m, "%pI6", n_addr); 30*3fe15c64SLorenzo Bianconi } else { 31*3fe15c64SLorenzo Bianconi seq_printf(m, "%pI4h", dest_addr); 32*3fe15c64SLorenzo Bianconi } 33*3fe15c64SLorenzo Bianconi if (dest_port) 34*3fe15c64SLorenzo Bianconi seq_printf(m, ":%d", *dest_port); 35*3fe15c64SLorenzo Bianconi } 36*3fe15c64SLorenzo Bianconi 37*3fe15c64SLorenzo Bianconi static int airoha_ppe_debugfs_foe_show(struct seq_file *m, void *private, 38*3fe15c64SLorenzo Bianconi bool bind) 39*3fe15c64SLorenzo Bianconi { 40*3fe15c64SLorenzo Bianconi static const char *const ppe_type_str[] = { 41*3fe15c64SLorenzo Bianconi [PPE_PKT_TYPE_IPV4_HNAPT] = "IPv4 5T", 42*3fe15c64SLorenzo Bianconi [PPE_PKT_TYPE_IPV4_ROUTE] = "IPv4 3T", 43*3fe15c64SLorenzo Bianconi [PPE_PKT_TYPE_BRIDGE] = "L2B", 44*3fe15c64SLorenzo Bianconi [PPE_PKT_TYPE_IPV4_DSLITE] = "DS-LITE", 45*3fe15c64SLorenzo Bianconi [PPE_PKT_TYPE_IPV6_ROUTE_3T] = "IPv6 3T", 46*3fe15c64SLorenzo Bianconi [PPE_PKT_TYPE_IPV6_ROUTE_5T] = "IPv6 5T", 47*3fe15c64SLorenzo Bianconi [PPE_PKT_TYPE_IPV6_6RD] = "6RD", 48*3fe15c64SLorenzo Bianconi }; 49*3fe15c64SLorenzo Bianconi static const char *const ppe_state_str[] = { 50*3fe15c64SLorenzo Bianconi [AIROHA_FOE_STATE_INVALID] = "INV", 51*3fe15c64SLorenzo Bianconi [AIROHA_FOE_STATE_UNBIND] = "UNB", 52*3fe15c64SLorenzo Bianconi [AIROHA_FOE_STATE_BIND] = "BND", 53*3fe15c64SLorenzo Bianconi [AIROHA_FOE_STATE_FIN] = "FIN", 54*3fe15c64SLorenzo Bianconi }; 55*3fe15c64SLorenzo Bianconi struct airoha_ppe *ppe = m->private; 56*3fe15c64SLorenzo Bianconi int i; 57*3fe15c64SLorenzo Bianconi 58*3fe15c64SLorenzo Bianconi for (i = 0; i < PPE_NUM_ENTRIES; i++) { 59*3fe15c64SLorenzo Bianconi const char *state_str, *type_str = "UNKNOWN"; 60*3fe15c64SLorenzo Bianconi void *src_addr = NULL, *dest_addr = NULL; 61*3fe15c64SLorenzo Bianconi u16 *src_port = NULL, *dest_port = NULL; 62*3fe15c64SLorenzo Bianconi struct airoha_foe_mac_info_common *l2; 63*3fe15c64SLorenzo Bianconi unsigned char h_source[ETH_ALEN] = {}; 64*3fe15c64SLorenzo Bianconi unsigned char h_dest[ETH_ALEN]; 65*3fe15c64SLorenzo Bianconi struct airoha_foe_entry *hwe; 66*3fe15c64SLorenzo Bianconi u32 type, state, ib2, data; 67*3fe15c64SLorenzo Bianconi bool ipv6 = false; 68*3fe15c64SLorenzo Bianconi 69*3fe15c64SLorenzo Bianconi hwe = airoha_ppe_foe_get_entry(ppe, i); 70*3fe15c64SLorenzo Bianconi if (!hwe) 71*3fe15c64SLorenzo Bianconi continue; 72*3fe15c64SLorenzo Bianconi 73*3fe15c64SLorenzo Bianconi state = FIELD_GET(AIROHA_FOE_IB1_BIND_STATE, hwe->ib1); 74*3fe15c64SLorenzo Bianconi if (!state) 75*3fe15c64SLorenzo Bianconi continue; 76*3fe15c64SLorenzo Bianconi 77*3fe15c64SLorenzo Bianconi if (bind && state != AIROHA_FOE_STATE_BIND) 78*3fe15c64SLorenzo Bianconi continue; 79*3fe15c64SLorenzo Bianconi 80*3fe15c64SLorenzo Bianconi state_str = ppe_state_str[state % ARRAY_SIZE(ppe_state_str)]; 81*3fe15c64SLorenzo Bianconi type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, hwe->ib1); 82*3fe15c64SLorenzo Bianconi if (type < ARRAY_SIZE(ppe_type_str) && ppe_type_str[type]) 83*3fe15c64SLorenzo Bianconi type_str = ppe_type_str[type]; 84*3fe15c64SLorenzo Bianconi 85*3fe15c64SLorenzo Bianconi seq_printf(m, "%05x %s %7s", i, state_str, type_str); 86*3fe15c64SLorenzo Bianconi 87*3fe15c64SLorenzo Bianconi switch (type) { 88*3fe15c64SLorenzo Bianconi case PPE_PKT_TYPE_IPV4_HNAPT: 89*3fe15c64SLorenzo Bianconi case PPE_PKT_TYPE_IPV4_DSLITE: 90*3fe15c64SLorenzo Bianconi src_port = &hwe->ipv4.orig_tuple.src_port; 91*3fe15c64SLorenzo Bianconi dest_port = &hwe->ipv4.orig_tuple.dest_port; 92*3fe15c64SLorenzo Bianconi fallthrough; 93*3fe15c64SLorenzo Bianconi case PPE_PKT_TYPE_IPV4_ROUTE: 94*3fe15c64SLorenzo Bianconi src_addr = &hwe->ipv4.orig_tuple.src_ip; 95*3fe15c64SLorenzo Bianconi dest_addr = &hwe->ipv4.orig_tuple.dest_ip; 96*3fe15c64SLorenzo Bianconi break; 97*3fe15c64SLorenzo Bianconi case PPE_PKT_TYPE_IPV6_ROUTE_5T: 98*3fe15c64SLorenzo Bianconi src_port = &hwe->ipv6.src_port; 99*3fe15c64SLorenzo Bianconi dest_port = &hwe->ipv6.dest_port; 100*3fe15c64SLorenzo Bianconi fallthrough; 101*3fe15c64SLorenzo Bianconi case PPE_PKT_TYPE_IPV6_ROUTE_3T: 102*3fe15c64SLorenzo Bianconi case PPE_PKT_TYPE_IPV6_6RD: 103*3fe15c64SLorenzo Bianconi src_addr = &hwe->ipv6.src_ip; 104*3fe15c64SLorenzo Bianconi dest_addr = &hwe->ipv6.dest_ip; 105*3fe15c64SLorenzo Bianconi ipv6 = true; 106*3fe15c64SLorenzo Bianconi break; 107*3fe15c64SLorenzo Bianconi default: 108*3fe15c64SLorenzo Bianconi break; 109*3fe15c64SLorenzo Bianconi } 110*3fe15c64SLorenzo Bianconi 111*3fe15c64SLorenzo Bianconi if (src_addr && dest_addr) { 112*3fe15c64SLorenzo Bianconi seq_puts(m, " orig="); 113*3fe15c64SLorenzo Bianconi airoha_debugfs_ppe_print_tuple(m, src_addr, dest_addr, 114*3fe15c64SLorenzo Bianconi src_port, dest_port, ipv6); 115*3fe15c64SLorenzo Bianconi } 116*3fe15c64SLorenzo Bianconi 117*3fe15c64SLorenzo Bianconi switch (type) { 118*3fe15c64SLorenzo Bianconi case PPE_PKT_TYPE_IPV4_HNAPT: 119*3fe15c64SLorenzo Bianconi case PPE_PKT_TYPE_IPV4_DSLITE: 120*3fe15c64SLorenzo Bianconi src_port = &hwe->ipv4.new_tuple.src_port; 121*3fe15c64SLorenzo Bianconi dest_port = &hwe->ipv4.new_tuple.dest_port; 122*3fe15c64SLorenzo Bianconi fallthrough; 123*3fe15c64SLorenzo Bianconi case PPE_PKT_TYPE_IPV4_ROUTE: 124*3fe15c64SLorenzo Bianconi src_addr = &hwe->ipv4.new_tuple.src_ip; 125*3fe15c64SLorenzo Bianconi dest_addr = &hwe->ipv4.new_tuple.dest_ip; 126*3fe15c64SLorenzo Bianconi seq_puts(m, " new="); 127*3fe15c64SLorenzo Bianconi airoha_debugfs_ppe_print_tuple(m, src_addr, dest_addr, 128*3fe15c64SLorenzo Bianconi src_port, dest_port, 129*3fe15c64SLorenzo Bianconi ipv6); 130*3fe15c64SLorenzo Bianconi break; 131*3fe15c64SLorenzo Bianconi default: 132*3fe15c64SLorenzo Bianconi break; 133*3fe15c64SLorenzo Bianconi } 134*3fe15c64SLorenzo Bianconi 135*3fe15c64SLorenzo Bianconi if (type >= PPE_PKT_TYPE_IPV6_ROUTE_3T) { 136*3fe15c64SLorenzo Bianconi data = hwe->ipv6.data; 137*3fe15c64SLorenzo Bianconi ib2 = hwe->ipv6.ib2; 138*3fe15c64SLorenzo Bianconi l2 = &hwe->ipv6.l2; 139*3fe15c64SLorenzo Bianconi } else { 140*3fe15c64SLorenzo Bianconi data = hwe->ipv4.data; 141*3fe15c64SLorenzo Bianconi ib2 = hwe->ipv4.ib2; 142*3fe15c64SLorenzo Bianconi l2 = &hwe->ipv4.l2.common; 143*3fe15c64SLorenzo Bianconi *((__be16 *)&h_source[4]) = 144*3fe15c64SLorenzo Bianconi cpu_to_be16(hwe->ipv4.l2.src_mac_lo); 145*3fe15c64SLorenzo Bianconi } 146*3fe15c64SLorenzo Bianconi 147*3fe15c64SLorenzo Bianconi *((__be32 *)h_dest) = cpu_to_be32(l2->dest_mac_hi); 148*3fe15c64SLorenzo Bianconi *((__be16 *)&h_dest[4]) = cpu_to_be16(l2->dest_mac_lo); 149*3fe15c64SLorenzo Bianconi *((__be32 *)h_source) = cpu_to_be32(l2->src_mac_hi); 150*3fe15c64SLorenzo Bianconi 151*3fe15c64SLorenzo Bianconi seq_printf(m, " eth=%pM->%pM etype=%04x data=%08x" 152*3fe15c64SLorenzo Bianconi " vlan=%d,%d ib1=%08x ib2=%08x\n", 153*3fe15c64SLorenzo Bianconi h_source, h_dest, l2->etype, data, 154*3fe15c64SLorenzo Bianconi l2->vlan1, l2->vlan2, hwe->ib1, ib2); 155*3fe15c64SLorenzo Bianconi } 156*3fe15c64SLorenzo Bianconi 157*3fe15c64SLorenzo Bianconi return 0; 158*3fe15c64SLorenzo Bianconi } 159*3fe15c64SLorenzo Bianconi 160*3fe15c64SLorenzo Bianconi static int airoha_ppe_debugfs_foe_all_show(struct seq_file *m, void *private) 161*3fe15c64SLorenzo Bianconi { 162*3fe15c64SLorenzo Bianconi return airoha_ppe_debugfs_foe_show(m, private, false); 163*3fe15c64SLorenzo Bianconi } 164*3fe15c64SLorenzo Bianconi DEFINE_SHOW_ATTRIBUTE(airoha_ppe_debugfs_foe_all); 165*3fe15c64SLorenzo Bianconi 166*3fe15c64SLorenzo Bianconi static int airoha_ppe_debugfs_foe_bind_show(struct seq_file *m, void *private) 167*3fe15c64SLorenzo Bianconi { 168*3fe15c64SLorenzo Bianconi return airoha_ppe_debugfs_foe_show(m, private, true); 169*3fe15c64SLorenzo Bianconi } 170*3fe15c64SLorenzo Bianconi DEFINE_SHOW_ATTRIBUTE(airoha_ppe_debugfs_foe_bind); 171*3fe15c64SLorenzo Bianconi 172*3fe15c64SLorenzo Bianconi int airoha_ppe_debugfs_init(struct airoha_ppe *ppe) 173*3fe15c64SLorenzo Bianconi { 174*3fe15c64SLorenzo Bianconi ppe->debugfs_dir = debugfs_create_dir("ppe", NULL); 175*3fe15c64SLorenzo Bianconi debugfs_create_file("entries", 0444, ppe->debugfs_dir, ppe, 176*3fe15c64SLorenzo Bianconi &airoha_ppe_debugfs_foe_all_fops); 177*3fe15c64SLorenzo Bianconi debugfs_create_file("bind", 0444, ppe->debugfs_dir, ppe, 178*3fe15c64SLorenzo Bianconi &airoha_ppe_debugfs_foe_bind_fops); 179*3fe15c64SLorenzo Bianconi 180*3fe15c64SLorenzo Bianconi return 0; 181*3fe15c64SLorenzo Bianconi } 182