18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Microchip KSZ8795 switch driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2017 Microchip Technology Inc. 68c2ecf20Sopenharmony_ci * Tristram Ha <Tristram.Ha@microchip.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/delay.h> 108c2ecf20Sopenharmony_ci#include <linux/export.h> 118c2ecf20Sopenharmony_ci#include <linux/gpio.h> 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/platform_data/microchip-ksz.h> 158c2ecf20Sopenharmony_ci#include <linux/phy.h> 168c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 178c2ecf20Sopenharmony_ci#include <linux/if_bridge.h> 188c2ecf20Sopenharmony_ci#include <net/dsa.h> 198c2ecf20Sopenharmony_ci#include <net/switchdev.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include "ksz_common.h" 228c2ecf20Sopenharmony_ci#include "ksz8795_reg.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic const struct { 258c2ecf20Sopenharmony_ci char string[ETH_GSTRING_LEN]; 268c2ecf20Sopenharmony_ci} mib_names[TOTAL_SWITCH_COUNTER_NUM] = { 278c2ecf20Sopenharmony_ci { "rx_hi" }, 288c2ecf20Sopenharmony_ci { "rx_undersize" }, 298c2ecf20Sopenharmony_ci { "rx_fragments" }, 308c2ecf20Sopenharmony_ci { "rx_oversize" }, 318c2ecf20Sopenharmony_ci { "rx_jabbers" }, 328c2ecf20Sopenharmony_ci { "rx_symbol_err" }, 338c2ecf20Sopenharmony_ci { "rx_crc_err" }, 348c2ecf20Sopenharmony_ci { "rx_align_err" }, 358c2ecf20Sopenharmony_ci { "rx_mac_ctrl" }, 368c2ecf20Sopenharmony_ci { "rx_pause" }, 378c2ecf20Sopenharmony_ci { "rx_bcast" }, 388c2ecf20Sopenharmony_ci { "rx_mcast" }, 398c2ecf20Sopenharmony_ci { "rx_ucast" }, 408c2ecf20Sopenharmony_ci { "rx_64_or_less" }, 418c2ecf20Sopenharmony_ci { "rx_65_127" }, 428c2ecf20Sopenharmony_ci { "rx_128_255" }, 438c2ecf20Sopenharmony_ci { "rx_256_511" }, 448c2ecf20Sopenharmony_ci { "rx_512_1023" }, 458c2ecf20Sopenharmony_ci { "rx_1024_1522" }, 468c2ecf20Sopenharmony_ci { "rx_1523_2000" }, 478c2ecf20Sopenharmony_ci { "rx_2001" }, 488c2ecf20Sopenharmony_ci { "tx_hi" }, 498c2ecf20Sopenharmony_ci { "tx_late_col" }, 508c2ecf20Sopenharmony_ci { "tx_pause" }, 518c2ecf20Sopenharmony_ci { "tx_bcast" }, 528c2ecf20Sopenharmony_ci { "tx_mcast" }, 538c2ecf20Sopenharmony_ci { "tx_ucast" }, 548c2ecf20Sopenharmony_ci { "tx_deferred" }, 558c2ecf20Sopenharmony_ci { "tx_total_col" }, 568c2ecf20Sopenharmony_ci { "tx_exc_col" }, 578c2ecf20Sopenharmony_ci { "tx_single_col" }, 588c2ecf20Sopenharmony_ci { "tx_mult_col" }, 598c2ecf20Sopenharmony_ci { "rx_total" }, 608c2ecf20Sopenharmony_ci { "tx_total" }, 618c2ecf20Sopenharmony_ci { "rx_discards" }, 628c2ecf20Sopenharmony_ci { "tx_discards" }, 638c2ecf20Sopenharmony_ci}; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic void ksz_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci regmap_update_bits(dev->regmap[0], addr, bits, set ? bits : 0); 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic void ksz_port_cfg(struct ksz_device *dev, int port, int offset, u8 bits, 718c2ecf20Sopenharmony_ci bool set) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci regmap_update_bits(dev->regmap[0], PORT_CTRL_ADDR(port, offset), 748c2ecf20Sopenharmony_ci bits, set ? bits : 0); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic int ksz8795_reset_switch(struct ksz_device *dev) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci /* reset switch */ 808c2ecf20Sopenharmony_ci ksz_write8(dev, REG_POWER_MANAGEMENT_1, 818c2ecf20Sopenharmony_ci SW_SOFTWARE_POWER_DOWN << SW_POWER_MANAGEMENT_MODE_S); 828c2ecf20Sopenharmony_ci ksz_write8(dev, REG_POWER_MANAGEMENT_1, 0); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci return 0; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic void ksz8795_set_prio_queue(struct ksz_device *dev, int port, int queue) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci u8 hi, lo; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci /* Number of queues can only be 1, 2, or 4. */ 928c2ecf20Sopenharmony_ci switch (queue) { 938c2ecf20Sopenharmony_ci case 4: 948c2ecf20Sopenharmony_ci case 3: 958c2ecf20Sopenharmony_ci queue = PORT_QUEUE_SPLIT_4; 968c2ecf20Sopenharmony_ci break; 978c2ecf20Sopenharmony_ci case 2: 988c2ecf20Sopenharmony_ci queue = PORT_QUEUE_SPLIT_2; 998c2ecf20Sopenharmony_ci break; 1008c2ecf20Sopenharmony_ci default: 1018c2ecf20Sopenharmony_ci queue = PORT_QUEUE_SPLIT_1; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci ksz_pread8(dev, port, REG_PORT_CTRL_0, &lo); 1048c2ecf20Sopenharmony_ci ksz_pread8(dev, port, P_DROP_TAG_CTRL, &hi); 1058c2ecf20Sopenharmony_ci lo &= ~PORT_QUEUE_SPLIT_L; 1068c2ecf20Sopenharmony_ci if (queue & PORT_QUEUE_SPLIT_2) 1078c2ecf20Sopenharmony_ci lo |= PORT_QUEUE_SPLIT_L; 1088c2ecf20Sopenharmony_ci hi &= ~PORT_QUEUE_SPLIT_H; 1098c2ecf20Sopenharmony_ci if (queue & PORT_QUEUE_SPLIT_4) 1108c2ecf20Sopenharmony_ci hi |= PORT_QUEUE_SPLIT_H; 1118c2ecf20Sopenharmony_ci ksz_pwrite8(dev, port, REG_PORT_CTRL_0, lo); 1128c2ecf20Sopenharmony_ci ksz_pwrite8(dev, port, P_DROP_TAG_CTRL, hi); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /* Default is port based for egress rate limit. */ 1158c2ecf20Sopenharmony_ci if (queue != PORT_QUEUE_SPLIT_1) 1168c2ecf20Sopenharmony_ci ksz_cfg(dev, REG_SW_CTRL_19, SW_OUT_RATE_LIMIT_QUEUE_BASED, 1178c2ecf20Sopenharmony_ci true); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic void ksz8795_r_mib_cnt(struct ksz_device *dev, int port, u16 addr, 1218c2ecf20Sopenharmony_ci u64 *cnt) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci u16 ctrl_addr; 1248c2ecf20Sopenharmony_ci u32 data; 1258c2ecf20Sopenharmony_ci u8 check; 1268c2ecf20Sopenharmony_ci int loop; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci ctrl_addr = addr + SWITCH_COUNTER_NUM * port; 1298c2ecf20Sopenharmony_ci ctrl_addr |= IND_ACC_TABLE(TABLE_MIB | TABLE_READ); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci mutex_lock(&dev->alu_mutex); 1328c2ecf20Sopenharmony_ci ksz_write16(dev, REG_IND_CTRL_0, ctrl_addr); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci /* It is almost guaranteed to always read the valid bit because of 1358c2ecf20Sopenharmony_ci * slow SPI speed. 1368c2ecf20Sopenharmony_ci */ 1378c2ecf20Sopenharmony_ci for (loop = 2; loop > 0; loop--) { 1388c2ecf20Sopenharmony_ci ksz_read8(dev, REG_IND_MIB_CHECK, &check); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci if (check & MIB_COUNTER_VALID) { 1418c2ecf20Sopenharmony_ci ksz_read32(dev, REG_IND_DATA_LO, &data); 1428c2ecf20Sopenharmony_ci if (check & MIB_COUNTER_OVERFLOW) 1438c2ecf20Sopenharmony_ci *cnt += MIB_COUNTER_VALUE + 1; 1448c2ecf20Sopenharmony_ci *cnt += data & MIB_COUNTER_VALUE; 1458c2ecf20Sopenharmony_ci break; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci mutex_unlock(&dev->alu_mutex); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic void ksz8795_r_mib_pkt(struct ksz_device *dev, int port, u16 addr, 1528c2ecf20Sopenharmony_ci u64 *dropped, u64 *cnt) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci u16 ctrl_addr; 1558c2ecf20Sopenharmony_ci u32 data; 1568c2ecf20Sopenharmony_ci u8 check; 1578c2ecf20Sopenharmony_ci int loop; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci addr -= SWITCH_COUNTER_NUM; 1608c2ecf20Sopenharmony_ci ctrl_addr = (KS_MIB_TOTAL_RX_1 - KS_MIB_TOTAL_RX_0) * port; 1618c2ecf20Sopenharmony_ci ctrl_addr += addr + KS_MIB_TOTAL_RX_0; 1628c2ecf20Sopenharmony_ci ctrl_addr |= IND_ACC_TABLE(TABLE_MIB | TABLE_READ); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci mutex_lock(&dev->alu_mutex); 1658c2ecf20Sopenharmony_ci ksz_write16(dev, REG_IND_CTRL_0, ctrl_addr); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* It is almost guaranteed to always read the valid bit because of 1688c2ecf20Sopenharmony_ci * slow SPI speed. 1698c2ecf20Sopenharmony_ci */ 1708c2ecf20Sopenharmony_ci for (loop = 2; loop > 0; loop--) { 1718c2ecf20Sopenharmony_ci ksz_read8(dev, REG_IND_MIB_CHECK, &check); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci if (check & MIB_COUNTER_VALID) { 1748c2ecf20Sopenharmony_ci ksz_read32(dev, REG_IND_DATA_LO, &data); 1758c2ecf20Sopenharmony_ci if (addr < 2) { 1768c2ecf20Sopenharmony_ci u64 total; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci total = check & MIB_TOTAL_BYTES_H; 1798c2ecf20Sopenharmony_ci total <<= 32; 1808c2ecf20Sopenharmony_ci *cnt += total; 1818c2ecf20Sopenharmony_ci *cnt += data; 1828c2ecf20Sopenharmony_ci if (check & MIB_COUNTER_OVERFLOW) { 1838c2ecf20Sopenharmony_ci total = MIB_TOTAL_BYTES_H + 1; 1848c2ecf20Sopenharmony_ci total <<= 32; 1858c2ecf20Sopenharmony_ci *cnt += total; 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci } else { 1888c2ecf20Sopenharmony_ci if (check & MIB_COUNTER_OVERFLOW) 1898c2ecf20Sopenharmony_ci *cnt += MIB_PACKET_DROPPED + 1; 1908c2ecf20Sopenharmony_ci *cnt += data & MIB_PACKET_DROPPED; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci break; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci mutex_unlock(&dev->alu_mutex); 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic void ksz8795_freeze_mib(struct ksz_device *dev, int port, bool freeze) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci /* enable the port for flush/freeze function */ 2018c2ecf20Sopenharmony_ci if (freeze) 2028c2ecf20Sopenharmony_ci ksz_cfg(dev, REG_SW_CTRL_6, BIT(port), true); 2038c2ecf20Sopenharmony_ci ksz_cfg(dev, REG_SW_CTRL_6, SW_MIB_COUNTER_FREEZE, freeze); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci /* disable the port after freeze is done */ 2068c2ecf20Sopenharmony_ci if (!freeze) 2078c2ecf20Sopenharmony_ci ksz_cfg(dev, REG_SW_CTRL_6, BIT(port), false); 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic void ksz8795_port_init_cnt(struct ksz_device *dev, int port) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci struct ksz_port_mib *mib = &dev->ports[port].mib; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* flush all enabled port MIB counters */ 2158c2ecf20Sopenharmony_ci ksz_cfg(dev, REG_SW_CTRL_6, BIT(port), true); 2168c2ecf20Sopenharmony_ci ksz_cfg(dev, REG_SW_CTRL_6, SW_MIB_COUNTER_FLUSH, true); 2178c2ecf20Sopenharmony_ci ksz_cfg(dev, REG_SW_CTRL_6, BIT(port), false); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci mib->cnt_ptr = 0; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci /* Some ports may not have MIB counters before SWITCH_COUNTER_NUM. */ 2228c2ecf20Sopenharmony_ci while (mib->cnt_ptr < dev->reg_mib_cnt) { 2238c2ecf20Sopenharmony_ci dev->dev_ops->r_mib_cnt(dev, port, mib->cnt_ptr, 2248c2ecf20Sopenharmony_ci &mib->counters[mib->cnt_ptr]); 2258c2ecf20Sopenharmony_ci ++mib->cnt_ptr; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci /* Some ports may not have MIB counters after SWITCH_COUNTER_NUM. */ 2298c2ecf20Sopenharmony_ci while (mib->cnt_ptr < dev->mib_cnt) { 2308c2ecf20Sopenharmony_ci dev->dev_ops->r_mib_pkt(dev, port, mib->cnt_ptr, 2318c2ecf20Sopenharmony_ci NULL, &mib->counters[mib->cnt_ptr]); 2328c2ecf20Sopenharmony_ci ++mib->cnt_ptr; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci mib->cnt_ptr = 0; 2358c2ecf20Sopenharmony_ci memset(mib->counters, 0, dev->mib_cnt * sizeof(u64)); 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic void ksz8795_r_table(struct ksz_device *dev, int table, u16 addr, 2398c2ecf20Sopenharmony_ci u64 *data) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci u16 ctrl_addr; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci ctrl_addr = IND_ACC_TABLE(table | TABLE_READ) | addr; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci mutex_lock(&dev->alu_mutex); 2468c2ecf20Sopenharmony_ci ksz_write16(dev, REG_IND_CTRL_0, ctrl_addr); 2478c2ecf20Sopenharmony_ci ksz_read64(dev, REG_IND_DATA_HI, data); 2488c2ecf20Sopenharmony_ci mutex_unlock(&dev->alu_mutex); 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic void ksz8795_w_table(struct ksz_device *dev, int table, u16 addr, 2528c2ecf20Sopenharmony_ci u64 data) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci u16 ctrl_addr; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci ctrl_addr = IND_ACC_TABLE(table) | addr; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci mutex_lock(&dev->alu_mutex); 2598c2ecf20Sopenharmony_ci ksz_write64(dev, REG_IND_DATA_HI, data); 2608c2ecf20Sopenharmony_ci ksz_write16(dev, REG_IND_CTRL_0, ctrl_addr); 2618c2ecf20Sopenharmony_ci mutex_unlock(&dev->alu_mutex); 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic int ksz8795_valid_dyn_entry(struct ksz_device *dev, u8 *data) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci int timeout = 100; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci do { 2698c2ecf20Sopenharmony_ci ksz_read8(dev, REG_IND_DATA_CHECK, data); 2708c2ecf20Sopenharmony_ci timeout--; 2718c2ecf20Sopenharmony_ci } while ((*data & DYNAMIC_MAC_TABLE_NOT_READY) && timeout); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci /* Entry is not ready for accessing. */ 2748c2ecf20Sopenharmony_ci if (*data & DYNAMIC_MAC_TABLE_NOT_READY) { 2758c2ecf20Sopenharmony_ci return -EAGAIN; 2768c2ecf20Sopenharmony_ci /* Entry is ready for accessing. */ 2778c2ecf20Sopenharmony_ci } else { 2788c2ecf20Sopenharmony_ci ksz_read8(dev, REG_IND_DATA_8, data); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci /* There is no valid entry in the table. */ 2818c2ecf20Sopenharmony_ci if (*data & DYNAMIC_MAC_TABLE_MAC_EMPTY) 2828c2ecf20Sopenharmony_ci return -ENXIO; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci return 0; 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistatic int ksz8795_r_dyn_mac_table(struct ksz_device *dev, u16 addr, 2888c2ecf20Sopenharmony_ci u8 *mac_addr, u8 *fid, u8 *src_port, 2898c2ecf20Sopenharmony_ci u8 *timestamp, u16 *entries) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci u32 data_hi, data_lo; 2928c2ecf20Sopenharmony_ci u16 ctrl_addr; 2938c2ecf20Sopenharmony_ci u8 data; 2948c2ecf20Sopenharmony_ci int rc; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci ctrl_addr = IND_ACC_TABLE(TABLE_DYNAMIC_MAC | TABLE_READ) | addr; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci mutex_lock(&dev->alu_mutex); 2998c2ecf20Sopenharmony_ci ksz_write16(dev, REG_IND_CTRL_0, ctrl_addr); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci rc = ksz8795_valid_dyn_entry(dev, &data); 3028c2ecf20Sopenharmony_ci if (rc == -EAGAIN) { 3038c2ecf20Sopenharmony_ci if (addr == 0) 3048c2ecf20Sopenharmony_ci *entries = 0; 3058c2ecf20Sopenharmony_ci } else if (rc == -ENXIO) { 3068c2ecf20Sopenharmony_ci *entries = 0; 3078c2ecf20Sopenharmony_ci /* At least one valid entry in the table. */ 3088c2ecf20Sopenharmony_ci } else { 3098c2ecf20Sopenharmony_ci u64 buf = 0; 3108c2ecf20Sopenharmony_ci int cnt; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci ksz_read64(dev, REG_IND_DATA_HI, &buf); 3138c2ecf20Sopenharmony_ci data_hi = (u32)(buf >> 32); 3148c2ecf20Sopenharmony_ci data_lo = (u32)buf; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci /* Check out how many valid entry in the table. */ 3178c2ecf20Sopenharmony_ci cnt = data & DYNAMIC_MAC_TABLE_ENTRIES_H; 3188c2ecf20Sopenharmony_ci cnt <<= DYNAMIC_MAC_ENTRIES_H_S; 3198c2ecf20Sopenharmony_ci cnt |= (data_hi & DYNAMIC_MAC_TABLE_ENTRIES) >> 3208c2ecf20Sopenharmony_ci DYNAMIC_MAC_ENTRIES_S; 3218c2ecf20Sopenharmony_ci *entries = cnt + 1; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci *fid = (data_hi & DYNAMIC_MAC_TABLE_FID) >> 3248c2ecf20Sopenharmony_ci DYNAMIC_MAC_FID_S; 3258c2ecf20Sopenharmony_ci *src_port = (data_hi & DYNAMIC_MAC_TABLE_SRC_PORT) >> 3268c2ecf20Sopenharmony_ci DYNAMIC_MAC_SRC_PORT_S; 3278c2ecf20Sopenharmony_ci *timestamp = (data_hi & DYNAMIC_MAC_TABLE_TIMESTAMP) >> 3288c2ecf20Sopenharmony_ci DYNAMIC_MAC_TIMESTAMP_S; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci mac_addr[5] = (u8)data_lo; 3318c2ecf20Sopenharmony_ci mac_addr[4] = (u8)(data_lo >> 8); 3328c2ecf20Sopenharmony_ci mac_addr[3] = (u8)(data_lo >> 16); 3338c2ecf20Sopenharmony_ci mac_addr[2] = (u8)(data_lo >> 24); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci mac_addr[1] = (u8)data_hi; 3368c2ecf20Sopenharmony_ci mac_addr[0] = (u8)(data_hi >> 8); 3378c2ecf20Sopenharmony_ci rc = 0; 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci mutex_unlock(&dev->alu_mutex); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci return rc; 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic int ksz8795_r_sta_mac_table(struct ksz_device *dev, u16 addr, 3458c2ecf20Sopenharmony_ci struct alu_struct *alu) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci u32 data_hi, data_lo; 3488c2ecf20Sopenharmony_ci u64 data; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci ksz8795_r_table(dev, TABLE_STATIC_MAC, addr, &data); 3518c2ecf20Sopenharmony_ci data_hi = data >> 32; 3528c2ecf20Sopenharmony_ci data_lo = (u32)data; 3538c2ecf20Sopenharmony_ci if (data_hi & (STATIC_MAC_TABLE_VALID | STATIC_MAC_TABLE_OVERRIDE)) { 3548c2ecf20Sopenharmony_ci alu->mac[5] = (u8)data_lo; 3558c2ecf20Sopenharmony_ci alu->mac[4] = (u8)(data_lo >> 8); 3568c2ecf20Sopenharmony_ci alu->mac[3] = (u8)(data_lo >> 16); 3578c2ecf20Sopenharmony_ci alu->mac[2] = (u8)(data_lo >> 24); 3588c2ecf20Sopenharmony_ci alu->mac[1] = (u8)data_hi; 3598c2ecf20Sopenharmony_ci alu->mac[0] = (u8)(data_hi >> 8); 3608c2ecf20Sopenharmony_ci alu->port_forward = (data_hi & STATIC_MAC_TABLE_FWD_PORTS) >> 3618c2ecf20Sopenharmony_ci STATIC_MAC_FWD_PORTS_S; 3628c2ecf20Sopenharmony_ci alu->is_override = 3638c2ecf20Sopenharmony_ci (data_hi & STATIC_MAC_TABLE_OVERRIDE) ? 1 : 0; 3648c2ecf20Sopenharmony_ci data_hi >>= 1; 3658c2ecf20Sopenharmony_ci alu->is_use_fid = (data_hi & STATIC_MAC_TABLE_USE_FID) ? 1 : 0; 3668c2ecf20Sopenharmony_ci alu->fid = (data_hi & STATIC_MAC_TABLE_FID) >> 3678c2ecf20Sopenharmony_ci STATIC_MAC_FID_S; 3688c2ecf20Sopenharmony_ci return 0; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci return -ENXIO; 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cistatic void ksz8795_w_sta_mac_table(struct ksz_device *dev, u16 addr, 3748c2ecf20Sopenharmony_ci struct alu_struct *alu) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci u32 data_hi, data_lo; 3778c2ecf20Sopenharmony_ci u64 data; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci data_lo = ((u32)alu->mac[2] << 24) | 3808c2ecf20Sopenharmony_ci ((u32)alu->mac[3] << 16) | 3818c2ecf20Sopenharmony_ci ((u32)alu->mac[4] << 8) | alu->mac[5]; 3828c2ecf20Sopenharmony_ci data_hi = ((u32)alu->mac[0] << 8) | alu->mac[1]; 3838c2ecf20Sopenharmony_ci data_hi |= (u32)alu->port_forward << STATIC_MAC_FWD_PORTS_S; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci if (alu->is_override) 3868c2ecf20Sopenharmony_ci data_hi |= STATIC_MAC_TABLE_OVERRIDE; 3878c2ecf20Sopenharmony_ci if (alu->is_use_fid) { 3888c2ecf20Sopenharmony_ci data_hi |= STATIC_MAC_TABLE_USE_FID; 3898c2ecf20Sopenharmony_ci data_hi |= (u32)alu->fid << STATIC_MAC_FID_S; 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci if (alu->is_static) 3928c2ecf20Sopenharmony_ci data_hi |= STATIC_MAC_TABLE_VALID; 3938c2ecf20Sopenharmony_ci else 3948c2ecf20Sopenharmony_ci data_hi &= ~STATIC_MAC_TABLE_OVERRIDE; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci data = (u64)data_hi << 32 | data_lo; 3978c2ecf20Sopenharmony_ci ksz8795_w_table(dev, TABLE_STATIC_MAC, addr, data); 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic void ksz8795_from_vlan(u16 vlan, u8 *fid, u8 *member, u8 *valid) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci *fid = vlan & VLAN_TABLE_FID; 4038c2ecf20Sopenharmony_ci *member = (vlan & VLAN_TABLE_MEMBERSHIP) >> VLAN_TABLE_MEMBERSHIP_S; 4048c2ecf20Sopenharmony_ci *valid = !!(vlan & VLAN_TABLE_VALID); 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic void ksz8795_to_vlan(u8 fid, u8 member, u8 valid, u16 *vlan) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci *vlan = fid; 4108c2ecf20Sopenharmony_ci *vlan |= (u16)member << VLAN_TABLE_MEMBERSHIP_S; 4118c2ecf20Sopenharmony_ci if (valid) 4128c2ecf20Sopenharmony_ci *vlan |= VLAN_TABLE_VALID; 4138c2ecf20Sopenharmony_ci} 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_cistatic void ksz8795_r_vlan_entries(struct ksz_device *dev, u16 addr) 4168c2ecf20Sopenharmony_ci{ 4178c2ecf20Sopenharmony_ci u64 data; 4188c2ecf20Sopenharmony_ci int i; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci ksz8795_r_table(dev, TABLE_VLAN, addr, &data); 4218c2ecf20Sopenharmony_ci addr *= 4; 4228c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 4238c2ecf20Sopenharmony_ci dev->vlan_cache[addr + i].table[0] = (u16)data; 4248c2ecf20Sopenharmony_ci data >>= VLAN_TABLE_S; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci} 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_cistatic void ksz8795_r_vlan_table(struct ksz_device *dev, u16 vid, u16 *vlan) 4298c2ecf20Sopenharmony_ci{ 4308c2ecf20Sopenharmony_ci int index; 4318c2ecf20Sopenharmony_ci u16 *data; 4328c2ecf20Sopenharmony_ci u16 addr; 4338c2ecf20Sopenharmony_ci u64 buf; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci data = (u16 *)&buf; 4368c2ecf20Sopenharmony_ci addr = vid / 4; 4378c2ecf20Sopenharmony_ci index = vid & 3; 4388c2ecf20Sopenharmony_ci ksz8795_r_table(dev, TABLE_VLAN, addr, &buf); 4398c2ecf20Sopenharmony_ci *vlan = data[index]; 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic void ksz8795_w_vlan_table(struct ksz_device *dev, u16 vid, u16 vlan) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci int index; 4458c2ecf20Sopenharmony_ci u16 *data; 4468c2ecf20Sopenharmony_ci u16 addr; 4478c2ecf20Sopenharmony_ci u64 buf; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci data = (u16 *)&buf; 4508c2ecf20Sopenharmony_ci addr = vid / 4; 4518c2ecf20Sopenharmony_ci index = vid & 3; 4528c2ecf20Sopenharmony_ci ksz8795_r_table(dev, TABLE_VLAN, addr, &buf); 4538c2ecf20Sopenharmony_ci data[index] = vlan; 4548c2ecf20Sopenharmony_ci dev->vlan_cache[vid].table[0] = vlan; 4558c2ecf20Sopenharmony_ci ksz8795_w_table(dev, TABLE_VLAN, addr, buf); 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_cistatic void ksz8795_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci u8 restart, speed, ctrl, link; 4618c2ecf20Sopenharmony_ci int processed = true; 4628c2ecf20Sopenharmony_ci u16 data = 0; 4638c2ecf20Sopenharmony_ci u8 p = phy; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci switch (reg) { 4668c2ecf20Sopenharmony_ci case PHY_REG_CTRL: 4678c2ecf20Sopenharmony_ci ksz_pread8(dev, p, P_NEG_RESTART_CTRL, &restart); 4688c2ecf20Sopenharmony_ci ksz_pread8(dev, p, P_SPEED_STATUS, &speed); 4698c2ecf20Sopenharmony_ci ksz_pread8(dev, p, P_FORCE_CTRL, &ctrl); 4708c2ecf20Sopenharmony_ci if (restart & PORT_PHY_LOOPBACK) 4718c2ecf20Sopenharmony_ci data |= PHY_LOOPBACK; 4728c2ecf20Sopenharmony_ci if (ctrl & PORT_FORCE_100_MBIT) 4738c2ecf20Sopenharmony_ci data |= PHY_SPEED_100MBIT; 4748c2ecf20Sopenharmony_ci if (!(ctrl & PORT_AUTO_NEG_DISABLE)) 4758c2ecf20Sopenharmony_ci data |= PHY_AUTO_NEG_ENABLE; 4768c2ecf20Sopenharmony_ci if (restart & PORT_POWER_DOWN) 4778c2ecf20Sopenharmony_ci data |= PHY_POWER_DOWN; 4788c2ecf20Sopenharmony_ci if (restart & PORT_AUTO_NEG_RESTART) 4798c2ecf20Sopenharmony_ci data |= PHY_AUTO_NEG_RESTART; 4808c2ecf20Sopenharmony_ci if (ctrl & PORT_FORCE_FULL_DUPLEX) 4818c2ecf20Sopenharmony_ci data |= PHY_FULL_DUPLEX; 4828c2ecf20Sopenharmony_ci if (speed & PORT_HP_MDIX) 4838c2ecf20Sopenharmony_ci data |= PHY_HP_MDIX; 4848c2ecf20Sopenharmony_ci if (restart & PORT_FORCE_MDIX) 4858c2ecf20Sopenharmony_ci data |= PHY_FORCE_MDIX; 4868c2ecf20Sopenharmony_ci if (restart & PORT_AUTO_MDIX_DISABLE) 4878c2ecf20Sopenharmony_ci data |= PHY_AUTO_MDIX_DISABLE; 4888c2ecf20Sopenharmony_ci if (restart & PORT_TX_DISABLE) 4898c2ecf20Sopenharmony_ci data |= PHY_TRANSMIT_DISABLE; 4908c2ecf20Sopenharmony_ci if (restart & PORT_LED_OFF) 4918c2ecf20Sopenharmony_ci data |= PHY_LED_DISABLE; 4928c2ecf20Sopenharmony_ci break; 4938c2ecf20Sopenharmony_ci case PHY_REG_STATUS: 4948c2ecf20Sopenharmony_ci ksz_pread8(dev, p, P_LINK_STATUS, &link); 4958c2ecf20Sopenharmony_ci data = PHY_100BTX_FD_CAPABLE | 4968c2ecf20Sopenharmony_ci PHY_100BTX_CAPABLE | 4978c2ecf20Sopenharmony_ci PHY_10BT_FD_CAPABLE | 4988c2ecf20Sopenharmony_ci PHY_10BT_CAPABLE | 4998c2ecf20Sopenharmony_ci PHY_AUTO_NEG_CAPABLE; 5008c2ecf20Sopenharmony_ci if (link & PORT_AUTO_NEG_COMPLETE) 5018c2ecf20Sopenharmony_ci data |= PHY_AUTO_NEG_ACKNOWLEDGE; 5028c2ecf20Sopenharmony_ci if (link & PORT_STAT_LINK_GOOD) 5038c2ecf20Sopenharmony_ci data |= PHY_LINK_STATUS; 5048c2ecf20Sopenharmony_ci break; 5058c2ecf20Sopenharmony_ci case PHY_REG_ID_1: 5068c2ecf20Sopenharmony_ci data = KSZ8795_ID_HI; 5078c2ecf20Sopenharmony_ci break; 5088c2ecf20Sopenharmony_ci case PHY_REG_ID_2: 5098c2ecf20Sopenharmony_ci data = KSZ8795_ID_LO; 5108c2ecf20Sopenharmony_ci break; 5118c2ecf20Sopenharmony_ci case PHY_REG_AUTO_NEGOTIATION: 5128c2ecf20Sopenharmony_ci ksz_pread8(dev, p, P_LOCAL_CTRL, &ctrl); 5138c2ecf20Sopenharmony_ci data = PHY_AUTO_NEG_802_3; 5148c2ecf20Sopenharmony_ci if (ctrl & PORT_AUTO_NEG_SYM_PAUSE) 5158c2ecf20Sopenharmony_ci data |= PHY_AUTO_NEG_SYM_PAUSE; 5168c2ecf20Sopenharmony_ci if (ctrl & PORT_AUTO_NEG_100BTX_FD) 5178c2ecf20Sopenharmony_ci data |= PHY_AUTO_NEG_100BTX_FD; 5188c2ecf20Sopenharmony_ci if (ctrl & PORT_AUTO_NEG_100BTX) 5198c2ecf20Sopenharmony_ci data |= PHY_AUTO_NEG_100BTX; 5208c2ecf20Sopenharmony_ci if (ctrl & PORT_AUTO_NEG_10BT_FD) 5218c2ecf20Sopenharmony_ci data |= PHY_AUTO_NEG_10BT_FD; 5228c2ecf20Sopenharmony_ci if (ctrl & PORT_AUTO_NEG_10BT) 5238c2ecf20Sopenharmony_ci data |= PHY_AUTO_NEG_10BT; 5248c2ecf20Sopenharmony_ci break; 5258c2ecf20Sopenharmony_ci case PHY_REG_REMOTE_CAPABILITY: 5268c2ecf20Sopenharmony_ci ksz_pread8(dev, p, P_REMOTE_STATUS, &link); 5278c2ecf20Sopenharmony_ci data = PHY_AUTO_NEG_802_3; 5288c2ecf20Sopenharmony_ci if (link & PORT_REMOTE_SYM_PAUSE) 5298c2ecf20Sopenharmony_ci data |= PHY_AUTO_NEG_SYM_PAUSE; 5308c2ecf20Sopenharmony_ci if (link & PORT_REMOTE_100BTX_FD) 5318c2ecf20Sopenharmony_ci data |= PHY_AUTO_NEG_100BTX_FD; 5328c2ecf20Sopenharmony_ci if (link & PORT_REMOTE_100BTX) 5338c2ecf20Sopenharmony_ci data |= PHY_AUTO_NEG_100BTX; 5348c2ecf20Sopenharmony_ci if (link & PORT_REMOTE_10BT_FD) 5358c2ecf20Sopenharmony_ci data |= PHY_AUTO_NEG_10BT_FD; 5368c2ecf20Sopenharmony_ci if (link & PORT_REMOTE_10BT) 5378c2ecf20Sopenharmony_ci data |= PHY_AUTO_NEG_10BT; 5388c2ecf20Sopenharmony_ci if (data & ~PHY_AUTO_NEG_802_3) 5398c2ecf20Sopenharmony_ci data |= PHY_REMOTE_ACKNOWLEDGE_NOT; 5408c2ecf20Sopenharmony_ci break; 5418c2ecf20Sopenharmony_ci default: 5428c2ecf20Sopenharmony_ci processed = false; 5438c2ecf20Sopenharmony_ci break; 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci if (processed) 5468c2ecf20Sopenharmony_ci *val = data; 5478c2ecf20Sopenharmony_ci} 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_cistatic void ksz8795_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci u8 p = phy; 5528c2ecf20Sopenharmony_ci u8 restart, speed, ctrl, data; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci switch (reg) { 5558c2ecf20Sopenharmony_ci case PHY_REG_CTRL: 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci /* Do not support PHY reset function. */ 5588c2ecf20Sopenharmony_ci if (val & PHY_RESET) 5598c2ecf20Sopenharmony_ci break; 5608c2ecf20Sopenharmony_ci ksz_pread8(dev, p, P_SPEED_STATUS, &speed); 5618c2ecf20Sopenharmony_ci data = speed; 5628c2ecf20Sopenharmony_ci if (val & PHY_HP_MDIX) 5638c2ecf20Sopenharmony_ci data |= PORT_HP_MDIX; 5648c2ecf20Sopenharmony_ci else 5658c2ecf20Sopenharmony_ci data &= ~PORT_HP_MDIX; 5668c2ecf20Sopenharmony_ci if (data != speed) 5678c2ecf20Sopenharmony_ci ksz_pwrite8(dev, p, P_SPEED_STATUS, data); 5688c2ecf20Sopenharmony_ci ksz_pread8(dev, p, P_FORCE_CTRL, &ctrl); 5698c2ecf20Sopenharmony_ci data = ctrl; 5708c2ecf20Sopenharmony_ci if (!(val & PHY_AUTO_NEG_ENABLE)) 5718c2ecf20Sopenharmony_ci data |= PORT_AUTO_NEG_DISABLE; 5728c2ecf20Sopenharmony_ci else 5738c2ecf20Sopenharmony_ci data &= ~PORT_AUTO_NEG_DISABLE; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci /* Fiber port does not support auto-negotiation. */ 5768c2ecf20Sopenharmony_ci if (dev->ports[p].fiber) 5778c2ecf20Sopenharmony_ci data |= PORT_AUTO_NEG_DISABLE; 5788c2ecf20Sopenharmony_ci if (val & PHY_SPEED_100MBIT) 5798c2ecf20Sopenharmony_ci data |= PORT_FORCE_100_MBIT; 5808c2ecf20Sopenharmony_ci else 5818c2ecf20Sopenharmony_ci data &= ~PORT_FORCE_100_MBIT; 5828c2ecf20Sopenharmony_ci if (val & PHY_FULL_DUPLEX) 5838c2ecf20Sopenharmony_ci data |= PORT_FORCE_FULL_DUPLEX; 5848c2ecf20Sopenharmony_ci else 5858c2ecf20Sopenharmony_ci data &= ~PORT_FORCE_FULL_DUPLEX; 5868c2ecf20Sopenharmony_ci if (data != ctrl) 5878c2ecf20Sopenharmony_ci ksz_pwrite8(dev, p, P_FORCE_CTRL, data); 5888c2ecf20Sopenharmony_ci ksz_pread8(dev, p, P_NEG_RESTART_CTRL, &restart); 5898c2ecf20Sopenharmony_ci data = restart; 5908c2ecf20Sopenharmony_ci if (val & PHY_LED_DISABLE) 5918c2ecf20Sopenharmony_ci data |= PORT_LED_OFF; 5928c2ecf20Sopenharmony_ci else 5938c2ecf20Sopenharmony_ci data &= ~PORT_LED_OFF; 5948c2ecf20Sopenharmony_ci if (val & PHY_TRANSMIT_DISABLE) 5958c2ecf20Sopenharmony_ci data |= PORT_TX_DISABLE; 5968c2ecf20Sopenharmony_ci else 5978c2ecf20Sopenharmony_ci data &= ~PORT_TX_DISABLE; 5988c2ecf20Sopenharmony_ci if (val & PHY_AUTO_NEG_RESTART) 5998c2ecf20Sopenharmony_ci data |= PORT_AUTO_NEG_RESTART; 6008c2ecf20Sopenharmony_ci else 6018c2ecf20Sopenharmony_ci data &= ~(PORT_AUTO_NEG_RESTART); 6028c2ecf20Sopenharmony_ci if (val & PHY_POWER_DOWN) 6038c2ecf20Sopenharmony_ci data |= PORT_POWER_DOWN; 6048c2ecf20Sopenharmony_ci else 6058c2ecf20Sopenharmony_ci data &= ~PORT_POWER_DOWN; 6068c2ecf20Sopenharmony_ci if (val & PHY_AUTO_MDIX_DISABLE) 6078c2ecf20Sopenharmony_ci data |= PORT_AUTO_MDIX_DISABLE; 6088c2ecf20Sopenharmony_ci else 6098c2ecf20Sopenharmony_ci data &= ~PORT_AUTO_MDIX_DISABLE; 6108c2ecf20Sopenharmony_ci if (val & PHY_FORCE_MDIX) 6118c2ecf20Sopenharmony_ci data |= PORT_FORCE_MDIX; 6128c2ecf20Sopenharmony_ci else 6138c2ecf20Sopenharmony_ci data &= ~PORT_FORCE_MDIX; 6148c2ecf20Sopenharmony_ci if (val & PHY_LOOPBACK) 6158c2ecf20Sopenharmony_ci data |= PORT_PHY_LOOPBACK; 6168c2ecf20Sopenharmony_ci else 6178c2ecf20Sopenharmony_ci data &= ~PORT_PHY_LOOPBACK; 6188c2ecf20Sopenharmony_ci if (data != restart) 6198c2ecf20Sopenharmony_ci ksz_pwrite8(dev, p, P_NEG_RESTART_CTRL, data); 6208c2ecf20Sopenharmony_ci break; 6218c2ecf20Sopenharmony_ci case PHY_REG_AUTO_NEGOTIATION: 6228c2ecf20Sopenharmony_ci ksz_pread8(dev, p, P_LOCAL_CTRL, &ctrl); 6238c2ecf20Sopenharmony_ci data = ctrl; 6248c2ecf20Sopenharmony_ci data &= ~(PORT_AUTO_NEG_SYM_PAUSE | 6258c2ecf20Sopenharmony_ci PORT_AUTO_NEG_100BTX_FD | 6268c2ecf20Sopenharmony_ci PORT_AUTO_NEG_100BTX | 6278c2ecf20Sopenharmony_ci PORT_AUTO_NEG_10BT_FD | 6288c2ecf20Sopenharmony_ci PORT_AUTO_NEG_10BT); 6298c2ecf20Sopenharmony_ci if (val & PHY_AUTO_NEG_SYM_PAUSE) 6308c2ecf20Sopenharmony_ci data |= PORT_AUTO_NEG_SYM_PAUSE; 6318c2ecf20Sopenharmony_ci if (val & PHY_AUTO_NEG_100BTX_FD) 6328c2ecf20Sopenharmony_ci data |= PORT_AUTO_NEG_100BTX_FD; 6338c2ecf20Sopenharmony_ci if (val & PHY_AUTO_NEG_100BTX) 6348c2ecf20Sopenharmony_ci data |= PORT_AUTO_NEG_100BTX; 6358c2ecf20Sopenharmony_ci if (val & PHY_AUTO_NEG_10BT_FD) 6368c2ecf20Sopenharmony_ci data |= PORT_AUTO_NEG_10BT_FD; 6378c2ecf20Sopenharmony_ci if (val & PHY_AUTO_NEG_10BT) 6388c2ecf20Sopenharmony_ci data |= PORT_AUTO_NEG_10BT; 6398c2ecf20Sopenharmony_ci if (data != ctrl) 6408c2ecf20Sopenharmony_ci ksz_pwrite8(dev, p, P_LOCAL_CTRL, data); 6418c2ecf20Sopenharmony_ci break; 6428c2ecf20Sopenharmony_ci default: 6438c2ecf20Sopenharmony_ci break; 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_cistatic enum dsa_tag_protocol ksz8795_get_tag_protocol(struct dsa_switch *ds, 6488c2ecf20Sopenharmony_ci int port, 6498c2ecf20Sopenharmony_ci enum dsa_tag_protocol mp) 6508c2ecf20Sopenharmony_ci{ 6518c2ecf20Sopenharmony_ci return DSA_TAG_PROTO_KSZ8795; 6528c2ecf20Sopenharmony_ci} 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_cistatic void ksz8795_get_strings(struct dsa_switch *ds, int port, 6558c2ecf20Sopenharmony_ci u32 stringset, uint8_t *buf) 6568c2ecf20Sopenharmony_ci{ 6578c2ecf20Sopenharmony_ci int i; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci for (i = 0; i < TOTAL_SWITCH_COUNTER_NUM; i++) { 6608c2ecf20Sopenharmony_ci memcpy(buf + i * ETH_GSTRING_LEN, mib_names[i].string, 6618c2ecf20Sopenharmony_ci ETH_GSTRING_LEN); 6628c2ecf20Sopenharmony_ci } 6638c2ecf20Sopenharmony_ci} 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_cistatic void ksz8795_cfg_port_member(struct ksz_device *dev, int port, 6668c2ecf20Sopenharmony_ci u8 member) 6678c2ecf20Sopenharmony_ci{ 6688c2ecf20Sopenharmony_ci u8 data; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci ksz_pread8(dev, port, P_MIRROR_CTRL, &data); 6718c2ecf20Sopenharmony_ci data &= ~PORT_VLAN_MEMBERSHIP; 6728c2ecf20Sopenharmony_ci data |= (member & dev->port_mask); 6738c2ecf20Sopenharmony_ci ksz_pwrite8(dev, port, P_MIRROR_CTRL, data); 6748c2ecf20Sopenharmony_ci dev->ports[port].member = member; 6758c2ecf20Sopenharmony_ci} 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_cistatic void ksz8795_port_stp_state_set(struct dsa_switch *ds, int port, 6788c2ecf20Sopenharmony_ci u8 state) 6798c2ecf20Sopenharmony_ci{ 6808c2ecf20Sopenharmony_ci struct ksz_device *dev = ds->priv; 6818c2ecf20Sopenharmony_ci int forward = dev->member; 6828c2ecf20Sopenharmony_ci struct ksz_port *p; 6838c2ecf20Sopenharmony_ci int member = -1; 6848c2ecf20Sopenharmony_ci u8 data; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci p = &dev->ports[port]; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci ksz_pread8(dev, port, P_STP_CTRL, &data); 6898c2ecf20Sopenharmony_ci data &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE | PORT_LEARN_DISABLE); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci switch (state) { 6928c2ecf20Sopenharmony_ci case BR_STATE_DISABLED: 6938c2ecf20Sopenharmony_ci data |= PORT_LEARN_DISABLE; 6948c2ecf20Sopenharmony_ci if (port < SWITCH_PORT_NUM) 6958c2ecf20Sopenharmony_ci member = 0; 6968c2ecf20Sopenharmony_ci break; 6978c2ecf20Sopenharmony_ci case BR_STATE_LISTENING: 6988c2ecf20Sopenharmony_ci data |= (PORT_RX_ENABLE | PORT_LEARN_DISABLE); 6998c2ecf20Sopenharmony_ci if (port < SWITCH_PORT_NUM && 7008c2ecf20Sopenharmony_ci p->stp_state == BR_STATE_DISABLED) 7018c2ecf20Sopenharmony_ci member = dev->host_mask | p->vid_member; 7028c2ecf20Sopenharmony_ci break; 7038c2ecf20Sopenharmony_ci case BR_STATE_LEARNING: 7048c2ecf20Sopenharmony_ci data |= PORT_RX_ENABLE; 7058c2ecf20Sopenharmony_ci break; 7068c2ecf20Sopenharmony_ci case BR_STATE_FORWARDING: 7078c2ecf20Sopenharmony_ci data |= (PORT_TX_ENABLE | PORT_RX_ENABLE); 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci /* This function is also used internally. */ 7108c2ecf20Sopenharmony_ci if (port == dev->cpu_port) 7118c2ecf20Sopenharmony_ci break; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci /* Port is a member of a bridge. */ 7148c2ecf20Sopenharmony_ci if (dev->br_member & BIT(port)) { 7158c2ecf20Sopenharmony_ci dev->member |= BIT(port); 7168c2ecf20Sopenharmony_ci member = dev->member; 7178c2ecf20Sopenharmony_ci } else { 7188c2ecf20Sopenharmony_ci member = dev->host_mask | p->vid_member; 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci break; 7218c2ecf20Sopenharmony_ci case BR_STATE_BLOCKING: 7228c2ecf20Sopenharmony_ci data |= PORT_LEARN_DISABLE; 7238c2ecf20Sopenharmony_ci if (port < SWITCH_PORT_NUM && 7248c2ecf20Sopenharmony_ci p->stp_state == BR_STATE_DISABLED) 7258c2ecf20Sopenharmony_ci member = dev->host_mask | p->vid_member; 7268c2ecf20Sopenharmony_ci break; 7278c2ecf20Sopenharmony_ci default: 7288c2ecf20Sopenharmony_ci dev_err(ds->dev, "invalid STP state: %d\n", state); 7298c2ecf20Sopenharmony_ci return; 7308c2ecf20Sopenharmony_ci } 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci ksz_pwrite8(dev, port, P_STP_CTRL, data); 7338c2ecf20Sopenharmony_ci p->stp_state = state; 7348c2ecf20Sopenharmony_ci /* Port membership may share register with STP state. */ 7358c2ecf20Sopenharmony_ci if (member >= 0 && member != p->member) 7368c2ecf20Sopenharmony_ci ksz8795_cfg_port_member(dev, port, (u8)member); 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci /* Check if forwarding needs to be updated. */ 7398c2ecf20Sopenharmony_ci if (state != BR_STATE_FORWARDING) { 7408c2ecf20Sopenharmony_ci if (dev->br_member & BIT(port)) 7418c2ecf20Sopenharmony_ci dev->member &= ~BIT(port); 7428c2ecf20Sopenharmony_ci } 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci /* When topology has changed the function ksz_update_port_member 7458c2ecf20Sopenharmony_ci * should be called to modify port forwarding behavior. 7468c2ecf20Sopenharmony_ci */ 7478c2ecf20Sopenharmony_ci if (forward != dev->member) 7488c2ecf20Sopenharmony_ci ksz_update_port_member(dev, port); 7498c2ecf20Sopenharmony_ci} 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_cistatic void ksz8795_flush_dyn_mac_table(struct ksz_device *dev, int port) 7528c2ecf20Sopenharmony_ci{ 7538c2ecf20Sopenharmony_ci u8 learn[TOTAL_PORT_NUM]; 7548c2ecf20Sopenharmony_ci int first, index, cnt; 7558c2ecf20Sopenharmony_ci struct ksz_port *p; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci if ((uint)port < TOTAL_PORT_NUM) { 7588c2ecf20Sopenharmony_ci first = port; 7598c2ecf20Sopenharmony_ci cnt = port + 1; 7608c2ecf20Sopenharmony_ci } else { 7618c2ecf20Sopenharmony_ci /* Flush all ports. */ 7628c2ecf20Sopenharmony_ci first = 0; 7638c2ecf20Sopenharmony_ci cnt = dev->mib_port_cnt; 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci for (index = first; index < cnt; index++) { 7668c2ecf20Sopenharmony_ci p = &dev->ports[index]; 7678c2ecf20Sopenharmony_ci if (!p->on) 7688c2ecf20Sopenharmony_ci continue; 7698c2ecf20Sopenharmony_ci ksz_pread8(dev, index, P_STP_CTRL, &learn[index]); 7708c2ecf20Sopenharmony_ci if (!(learn[index] & PORT_LEARN_DISABLE)) 7718c2ecf20Sopenharmony_ci ksz_pwrite8(dev, index, P_STP_CTRL, 7728c2ecf20Sopenharmony_ci learn[index] | PORT_LEARN_DISABLE); 7738c2ecf20Sopenharmony_ci } 7748c2ecf20Sopenharmony_ci ksz_cfg(dev, S_FLUSH_TABLE_CTRL, SW_FLUSH_DYN_MAC_TABLE, true); 7758c2ecf20Sopenharmony_ci for (index = first; index < cnt; index++) { 7768c2ecf20Sopenharmony_ci p = &dev->ports[index]; 7778c2ecf20Sopenharmony_ci if (!p->on) 7788c2ecf20Sopenharmony_ci continue; 7798c2ecf20Sopenharmony_ci if (!(learn[index] & PORT_LEARN_DISABLE)) 7808c2ecf20Sopenharmony_ci ksz_pwrite8(dev, index, P_STP_CTRL, learn[index]); 7818c2ecf20Sopenharmony_ci } 7828c2ecf20Sopenharmony_ci} 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_cistatic int ksz8795_port_vlan_filtering(struct dsa_switch *ds, int port, 7858c2ecf20Sopenharmony_ci bool flag, 7868c2ecf20Sopenharmony_ci struct switchdev_trans *trans) 7878c2ecf20Sopenharmony_ci{ 7888c2ecf20Sopenharmony_ci struct ksz_device *dev = ds->priv; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci if (switchdev_trans_ph_prepare(trans)) 7918c2ecf20Sopenharmony_ci return 0; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci /* Discard packets with VID not enabled on the switch */ 7948c2ecf20Sopenharmony_ci ksz_cfg(dev, S_MIRROR_CTRL, SW_VLAN_ENABLE, flag); 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci /* Discard packets with VID not enabled on the ingress port */ 7978c2ecf20Sopenharmony_ci for (port = 0; port < dev->phy_port_cnt; ++port) 7988c2ecf20Sopenharmony_ci ksz_port_cfg(dev, port, REG_PORT_CTRL_2, PORT_INGRESS_FILTER, 7998c2ecf20Sopenharmony_ci flag); 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci return 0; 8028c2ecf20Sopenharmony_ci} 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_cistatic bool ksz8795_port_vlan_changes_remove_tag( 8058c2ecf20Sopenharmony_ci struct dsa_switch *ds, int port, 8068c2ecf20Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan) 8078c2ecf20Sopenharmony_ci{ 8088c2ecf20Sopenharmony_ci bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; 8098c2ecf20Sopenharmony_ci struct ksz_device *dev = ds->priv; 8108c2ecf20Sopenharmony_ci struct ksz_port *p = &dev->ports[port]; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci /* If a VLAN is added with untagged flag different from the 8138c2ecf20Sopenharmony_ci * port's Remove Tag flag, we need to change the latter. 8148c2ecf20Sopenharmony_ci * Ignore VID 0, which is always untagged. 8158c2ecf20Sopenharmony_ci * Ignore CPU port, which will always be tagged. 8168c2ecf20Sopenharmony_ci */ 8178c2ecf20Sopenharmony_ci return untagged != p->remove_tag && 8188c2ecf20Sopenharmony_ci !(vlan->vid_begin == 0 && vlan->vid_end == 0) && 8198c2ecf20Sopenharmony_ci port != dev->cpu_port; 8208c2ecf20Sopenharmony_ci} 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ciint ksz8795_port_vlan_prepare(struct dsa_switch *ds, int port, 8238c2ecf20Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan) 8248c2ecf20Sopenharmony_ci{ 8258c2ecf20Sopenharmony_ci struct ksz_device *dev = ds->priv; 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci /* Reject attempts to add a VLAN that requires the Remove Tag 8288c2ecf20Sopenharmony_ci * flag to be changed, unless there are no other VLANs 8298c2ecf20Sopenharmony_ci * currently configured. 8308c2ecf20Sopenharmony_ci */ 8318c2ecf20Sopenharmony_ci if (ksz8795_port_vlan_changes_remove_tag(ds, port, vlan)) { 8328c2ecf20Sopenharmony_ci unsigned int vid; 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci for (vid = 1; vid < dev->num_vlans; ++vid) { 8358c2ecf20Sopenharmony_ci u8 fid, member, valid; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci /* Skip the VIDs we are going to add or reconfigure */ 8388c2ecf20Sopenharmony_ci if (vid == vlan->vid_begin) { 8398c2ecf20Sopenharmony_ci vid = vlan->vid_end; 8408c2ecf20Sopenharmony_ci continue; 8418c2ecf20Sopenharmony_ci } 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci ksz8795_from_vlan(dev->vlan_cache[vid].table[0], 8448c2ecf20Sopenharmony_ci &fid, &member, &valid); 8458c2ecf20Sopenharmony_ci if (valid && (member & BIT(port))) 8468c2ecf20Sopenharmony_ci return -EINVAL; 8478c2ecf20Sopenharmony_ci } 8488c2ecf20Sopenharmony_ci } 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci return ksz_port_vlan_prepare(ds, port, vlan); 8518c2ecf20Sopenharmony_ci} 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_cistatic void ksz8795_port_vlan_add(struct dsa_switch *ds, int port, 8548c2ecf20Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan) 8558c2ecf20Sopenharmony_ci{ 8568c2ecf20Sopenharmony_ci bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; 8578c2ecf20Sopenharmony_ci struct ksz_device *dev = ds->priv; 8588c2ecf20Sopenharmony_ci struct ksz_port *p = &dev->ports[port]; 8598c2ecf20Sopenharmony_ci u16 data, vid, new_pvid = 0; 8608c2ecf20Sopenharmony_ci u8 fid, member, valid; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci if (ksz8795_port_vlan_changes_remove_tag(ds, port, vlan)) { 8638c2ecf20Sopenharmony_ci ksz_port_cfg(dev, port, P_TAG_CTRL, PORT_REMOVE_TAG, untagged); 8648c2ecf20Sopenharmony_ci p->remove_tag = untagged; 8658c2ecf20Sopenharmony_ci } 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { 8688c2ecf20Sopenharmony_ci ksz8795_r_vlan_table(dev, vid, &data); 8698c2ecf20Sopenharmony_ci ksz8795_from_vlan(data, &fid, &member, &valid); 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci /* First time to setup the VLAN entry. */ 8728c2ecf20Sopenharmony_ci if (!valid) { 8738c2ecf20Sopenharmony_ci /* Need to find a way to map VID to FID. */ 8748c2ecf20Sopenharmony_ci fid = 1; 8758c2ecf20Sopenharmony_ci valid = 1; 8768c2ecf20Sopenharmony_ci } 8778c2ecf20Sopenharmony_ci member |= BIT(port); 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci ksz8795_to_vlan(fid, member, valid, &data); 8808c2ecf20Sopenharmony_ci ksz8795_w_vlan_table(dev, vid, data); 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci /* change PVID */ 8838c2ecf20Sopenharmony_ci if (vlan->flags & BRIDGE_VLAN_INFO_PVID) 8848c2ecf20Sopenharmony_ci new_pvid = vid; 8858c2ecf20Sopenharmony_ci } 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci if (new_pvid) { 8888c2ecf20Sopenharmony_ci ksz_pread16(dev, port, REG_PORT_CTRL_VID, &vid); 8898c2ecf20Sopenharmony_ci vid &= ~VLAN_VID_MASK; 8908c2ecf20Sopenharmony_ci vid |= new_pvid; 8918c2ecf20Sopenharmony_ci ksz_pwrite16(dev, port, REG_PORT_CTRL_VID, vid); 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci ksz_pwrite8(dev, port, REG_PORT_CTRL_12, 0x0f); 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci} 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_cistatic int ksz8795_port_vlan_del(struct dsa_switch *ds, int port, 8988c2ecf20Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan) 8998c2ecf20Sopenharmony_ci{ 9008c2ecf20Sopenharmony_ci struct ksz_device *dev = ds->priv; 9018c2ecf20Sopenharmony_ci u16 data, vid, pvid; 9028c2ecf20Sopenharmony_ci u8 fid, member, valid; 9038c2ecf20Sopenharmony_ci bool del_pvid = false; 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci ksz_pread16(dev, port, REG_PORT_CTRL_VID, &pvid); 9068c2ecf20Sopenharmony_ci pvid = pvid & 0xFFF; 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { 9098c2ecf20Sopenharmony_ci ksz8795_r_vlan_table(dev, vid, &data); 9108c2ecf20Sopenharmony_ci ksz8795_from_vlan(data, &fid, &member, &valid); 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci member &= ~BIT(port); 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci /* Invalidate the entry if no more member. */ 9158c2ecf20Sopenharmony_ci if (!member) { 9168c2ecf20Sopenharmony_ci fid = 0; 9178c2ecf20Sopenharmony_ci valid = 0; 9188c2ecf20Sopenharmony_ci } 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci if (pvid == vid) 9218c2ecf20Sopenharmony_ci del_pvid = true; 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci ksz8795_to_vlan(fid, member, valid, &data); 9248c2ecf20Sopenharmony_ci ksz8795_w_vlan_table(dev, vid, data); 9258c2ecf20Sopenharmony_ci } 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci if (del_pvid) 9288c2ecf20Sopenharmony_ci ksz_pwrite8(dev, port, REG_PORT_CTRL_12, 0x00); 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci return 0; 9318c2ecf20Sopenharmony_ci} 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_cistatic int ksz8795_port_mirror_add(struct dsa_switch *ds, int port, 9348c2ecf20Sopenharmony_ci struct dsa_mall_mirror_tc_entry *mirror, 9358c2ecf20Sopenharmony_ci bool ingress) 9368c2ecf20Sopenharmony_ci{ 9378c2ecf20Sopenharmony_ci struct ksz_device *dev = ds->priv; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci if (ingress) { 9408c2ecf20Sopenharmony_ci ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_RX, true); 9418c2ecf20Sopenharmony_ci dev->mirror_rx |= BIT(port); 9428c2ecf20Sopenharmony_ci } else { 9438c2ecf20Sopenharmony_ci ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_TX, true); 9448c2ecf20Sopenharmony_ci dev->mirror_tx |= BIT(port); 9458c2ecf20Sopenharmony_ci } 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_SNIFFER, false); 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci /* configure mirror port */ 9508c2ecf20Sopenharmony_ci if (dev->mirror_rx || dev->mirror_tx) 9518c2ecf20Sopenharmony_ci ksz_port_cfg(dev, mirror->to_local_port, P_MIRROR_CTRL, 9528c2ecf20Sopenharmony_ci PORT_MIRROR_SNIFFER, true); 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci return 0; 9558c2ecf20Sopenharmony_ci} 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_cistatic void ksz8795_port_mirror_del(struct dsa_switch *ds, int port, 9588c2ecf20Sopenharmony_ci struct dsa_mall_mirror_tc_entry *mirror) 9598c2ecf20Sopenharmony_ci{ 9608c2ecf20Sopenharmony_ci struct ksz_device *dev = ds->priv; 9618c2ecf20Sopenharmony_ci u8 data; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci if (mirror->ingress) { 9648c2ecf20Sopenharmony_ci ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_RX, false); 9658c2ecf20Sopenharmony_ci dev->mirror_rx &= ~BIT(port); 9668c2ecf20Sopenharmony_ci } else { 9678c2ecf20Sopenharmony_ci ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_TX, false); 9688c2ecf20Sopenharmony_ci dev->mirror_tx &= ~BIT(port); 9698c2ecf20Sopenharmony_ci } 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci ksz_pread8(dev, port, P_MIRROR_CTRL, &data); 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci if (!dev->mirror_rx && !dev->mirror_tx) 9748c2ecf20Sopenharmony_ci ksz_port_cfg(dev, mirror->to_local_port, P_MIRROR_CTRL, 9758c2ecf20Sopenharmony_ci PORT_MIRROR_SNIFFER, false); 9768c2ecf20Sopenharmony_ci} 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_cistatic void ksz8795_port_setup(struct ksz_device *dev, int port, bool cpu_port) 9798c2ecf20Sopenharmony_ci{ 9808c2ecf20Sopenharmony_ci struct ksz_port *p = &dev->ports[port]; 9818c2ecf20Sopenharmony_ci u8 data8, member; 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci /* enable broadcast storm limit */ 9848c2ecf20Sopenharmony_ci ksz_port_cfg(dev, port, P_BCAST_STORM_CTRL, PORT_BROADCAST_STORM, true); 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci ksz8795_set_prio_queue(dev, port, 4); 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci /* disable DiffServ priority */ 9898c2ecf20Sopenharmony_ci ksz_port_cfg(dev, port, P_PRIO_CTRL, PORT_DIFFSERV_ENABLE, false); 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci /* replace priority */ 9928c2ecf20Sopenharmony_ci ksz_port_cfg(dev, port, P_802_1P_CTRL, PORT_802_1P_REMAPPING, false); 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci /* enable 802.1p priority */ 9958c2ecf20Sopenharmony_ci ksz_port_cfg(dev, port, P_PRIO_CTRL, PORT_802_1P_ENABLE, true); 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci if (cpu_port) { 9988c2ecf20Sopenharmony_ci if (!p->interface && dev->compat_interface) { 9998c2ecf20Sopenharmony_ci dev_warn(dev->dev, 10008c2ecf20Sopenharmony_ci "Using legacy switch \"phy-mode\" property, because it is missing on port %d node. " 10018c2ecf20Sopenharmony_ci "Please update your device tree.\n", 10028c2ecf20Sopenharmony_ci port); 10038c2ecf20Sopenharmony_ci p->interface = dev->compat_interface; 10048c2ecf20Sopenharmony_ci } 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci /* Configure MII interface for proper network communication. */ 10078c2ecf20Sopenharmony_ci ksz_read8(dev, REG_PORT_5_CTRL_6, &data8); 10088c2ecf20Sopenharmony_ci data8 &= ~PORT_INTERFACE_TYPE; 10098c2ecf20Sopenharmony_ci data8 &= ~PORT_GMII_1GPS_MODE; 10108c2ecf20Sopenharmony_ci switch (p->interface) { 10118c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_MII: 10128c2ecf20Sopenharmony_ci p->phydev.speed = SPEED_100; 10138c2ecf20Sopenharmony_ci break; 10148c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_RMII: 10158c2ecf20Sopenharmony_ci data8 |= PORT_INTERFACE_RMII; 10168c2ecf20Sopenharmony_ci p->phydev.speed = SPEED_100; 10178c2ecf20Sopenharmony_ci break; 10188c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_GMII: 10198c2ecf20Sopenharmony_ci data8 |= PORT_GMII_1GPS_MODE; 10208c2ecf20Sopenharmony_ci data8 |= PORT_INTERFACE_GMII; 10218c2ecf20Sopenharmony_ci p->phydev.speed = SPEED_1000; 10228c2ecf20Sopenharmony_ci break; 10238c2ecf20Sopenharmony_ci default: 10248c2ecf20Sopenharmony_ci data8 &= ~PORT_RGMII_ID_IN_ENABLE; 10258c2ecf20Sopenharmony_ci data8 &= ~PORT_RGMII_ID_OUT_ENABLE; 10268c2ecf20Sopenharmony_ci if (p->interface == PHY_INTERFACE_MODE_RGMII_ID || 10278c2ecf20Sopenharmony_ci p->interface == PHY_INTERFACE_MODE_RGMII_RXID) 10288c2ecf20Sopenharmony_ci data8 |= PORT_RGMII_ID_IN_ENABLE; 10298c2ecf20Sopenharmony_ci if (p->interface == PHY_INTERFACE_MODE_RGMII_ID || 10308c2ecf20Sopenharmony_ci p->interface == PHY_INTERFACE_MODE_RGMII_TXID) 10318c2ecf20Sopenharmony_ci data8 |= PORT_RGMII_ID_OUT_ENABLE; 10328c2ecf20Sopenharmony_ci data8 |= PORT_GMII_1GPS_MODE; 10338c2ecf20Sopenharmony_ci data8 |= PORT_INTERFACE_RGMII; 10348c2ecf20Sopenharmony_ci p->phydev.speed = SPEED_1000; 10358c2ecf20Sopenharmony_ci break; 10368c2ecf20Sopenharmony_ci } 10378c2ecf20Sopenharmony_ci ksz_write8(dev, REG_PORT_5_CTRL_6, data8); 10388c2ecf20Sopenharmony_ci p->phydev.duplex = 1; 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci member = dev->port_mask; 10418c2ecf20Sopenharmony_ci } else { 10428c2ecf20Sopenharmony_ci member = dev->host_mask | p->vid_member; 10438c2ecf20Sopenharmony_ci } 10448c2ecf20Sopenharmony_ci ksz8795_cfg_port_member(dev, port, member); 10458c2ecf20Sopenharmony_ci} 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_cistatic void ksz8795_config_cpu_port(struct dsa_switch *ds) 10488c2ecf20Sopenharmony_ci{ 10498c2ecf20Sopenharmony_ci struct ksz_device *dev = ds->priv; 10508c2ecf20Sopenharmony_ci struct ksz_port *p; 10518c2ecf20Sopenharmony_ci u8 remote; 10528c2ecf20Sopenharmony_ci int i; 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci ds->num_ports = dev->port_cnt + 1; 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci /* Switch marks the maximum frame with extra byte as oversize. */ 10578c2ecf20Sopenharmony_ci ksz_cfg(dev, REG_SW_CTRL_2, SW_LEGAL_PACKET_DISABLE, true); 10588c2ecf20Sopenharmony_ci ksz_cfg(dev, S_TAIL_TAG_CTRL, SW_TAIL_TAG_ENABLE, true); 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci p = &dev->ports[dev->cpu_port]; 10618c2ecf20Sopenharmony_ci p->vid_member = dev->port_mask; 10628c2ecf20Sopenharmony_ci p->on = 1; 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci ksz8795_port_setup(dev, dev->cpu_port, true); 10658c2ecf20Sopenharmony_ci dev->member = dev->host_mask; 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci for (i = 0; i < SWITCH_PORT_NUM; i++) { 10688c2ecf20Sopenharmony_ci p = &dev->ports[i]; 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci /* Initialize to non-zero so that ksz_cfg_port_member() will 10718c2ecf20Sopenharmony_ci * be called. 10728c2ecf20Sopenharmony_ci */ 10738c2ecf20Sopenharmony_ci p->vid_member = BIT(i); 10748c2ecf20Sopenharmony_ci p->member = dev->port_mask; 10758c2ecf20Sopenharmony_ci ksz8795_port_stp_state_set(ds, i, BR_STATE_DISABLED); 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci /* Last port may be disabled. */ 10788c2ecf20Sopenharmony_ci if (i == dev->port_cnt) 10798c2ecf20Sopenharmony_ci break; 10808c2ecf20Sopenharmony_ci p->on = 1; 10818c2ecf20Sopenharmony_ci p->phy = 1; 10828c2ecf20Sopenharmony_ci } 10838c2ecf20Sopenharmony_ci for (i = 0; i < dev->phy_port_cnt; i++) { 10848c2ecf20Sopenharmony_ci p = &dev->ports[i]; 10858c2ecf20Sopenharmony_ci if (!p->on) 10868c2ecf20Sopenharmony_ci continue; 10878c2ecf20Sopenharmony_ci ksz_pread8(dev, i, P_REMOTE_STATUS, &remote); 10888c2ecf20Sopenharmony_ci if (remote & PORT_FIBER_MODE) 10898c2ecf20Sopenharmony_ci p->fiber = 1; 10908c2ecf20Sopenharmony_ci if (p->fiber) 10918c2ecf20Sopenharmony_ci ksz_port_cfg(dev, i, P_STP_CTRL, PORT_FORCE_FLOW_CTRL, 10928c2ecf20Sopenharmony_ci true); 10938c2ecf20Sopenharmony_ci else 10948c2ecf20Sopenharmony_ci ksz_port_cfg(dev, i, P_STP_CTRL, PORT_FORCE_FLOW_CTRL, 10958c2ecf20Sopenharmony_ci false); 10968c2ecf20Sopenharmony_ci } 10978c2ecf20Sopenharmony_ci} 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_cistatic int ksz8795_setup(struct dsa_switch *ds) 11008c2ecf20Sopenharmony_ci{ 11018c2ecf20Sopenharmony_ci struct ksz_device *dev = ds->priv; 11028c2ecf20Sopenharmony_ci struct alu_struct alu; 11038c2ecf20Sopenharmony_ci int i, ret = 0; 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci dev->vlan_cache = devm_kcalloc(dev->dev, sizeof(struct vlan_table), 11068c2ecf20Sopenharmony_ci dev->num_vlans, GFP_KERNEL); 11078c2ecf20Sopenharmony_ci if (!dev->vlan_cache) 11088c2ecf20Sopenharmony_ci return -ENOMEM; 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci ret = ksz8795_reset_switch(dev); 11118c2ecf20Sopenharmony_ci if (ret) { 11128c2ecf20Sopenharmony_ci dev_err(ds->dev, "failed to reset switch\n"); 11138c2ecf20Sopenharmony_ci return ret; 11148c2ecf20Sopenharmony_ci } 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci ksz_cfg(dev, S_REPLACE_VID_CTRL, SW_FLOW_CTRL, true); 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci /* Enable automatic fast aging when link changed detected. */ 11198c2ecf20Sopenharmony_ci ksz_cfg(dev, S_LINK_AGING_CTRL, SW_LINK_AUTO_AGING, true); 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci /* Enable aggressive back off algorithm in half duplex mode. */ 11228c2ecf20Sopenharmony_ci regmap_update_bits(dev->regmap[0], REG_SW_CTRL_1, 11238c2ecf20Sopenharmony_ci SW_AGGR_BACKOFF, SW_AGGR_BACKOFF); 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci /* 11268c2ecf20Sopenharmony_ci * Make sure unicast VLAN boundary is set as default and 11278c2ecf20Sopenharmony_ci * enable no excessive collision drop. 11288c2ecf20Sopenharmony_ci */ 11298c2ecf20Sopenharmony_ci regmap_update_bits(dev->regmap[0], REG_SW_CTRL_2, 11308c2ecf20Sopenharmony_ci UNICAST_VLAN_BOUNDARY | NO_EXC_COLLISION_DROP, 11318c2ecf20Sopenharmony_ci UNICAST_VLAN_BOUNDARY | NO_EXC_COLLISION_DROP); 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci ksz8795_config_cpu_port(ds); 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci ksz_cfg(dev, REG_SW_CTRL_2, MULTICAST_STORM_DISABLE, true); 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci ksz_cfg(dev, S_REPLACE_VID_CTRL, SW_REPLACE_VID, false); 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci ksz_cfg(dev, S_MIRROR_CTRL, SW_MIRROR_RX_TX, false); 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci ksz_cfg(dev, REG_SW_CTRL_19, SW_INS_TAG_ENABLE, true); 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci /* set broadcast storm protection 10% rate */ 11448c2ecf20Sopenharmony_ci regmap_update_bits(dev->regmap[1], S_REPLACE_VID_CTRL, 11458c2ecf20Sopenharmony_ci BROADCAST_STORM_RATE, 11468c2ecf20Sopenharmony_ci (BROADCAST_STORM_VALUE * 11478c2ecf20Sopenharmony_ci BROADCAST_STORM_PROT_RATE) / 100); 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci for (i = 0; i < VLAN_TABLE_ENTRIES; i++) 11508c2ecf20Sopenharmony_ci ksz8795_r_vlan_entries(dev, i); 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci /* Setup STP address for STP operation. */ 11538c2ecf20Sopenharmony_ci memset(&alu, 0, sizeof(alu)); 11548c2ecf20Sopenharmony_ci ether_addr_copy(alu.mac, eth_stp_addr); 11558c2ecf20Sopenharmony_ci alu.is_static = true; 11568c2ecf20Sopenharmony_ci alu.is_override = true; 11578c2ecf20Sopenharmony_ci alu.port_forward = dev->host_mask; 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci ksz8795_w_sta_mac_table(dev, 0, &alu); 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci ksz_init_mib_timer(dev); 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci return 0; 11648c2ecf20Sopenharmony_ci} 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_cistatic const struct dsa_switch_ops ksz8795_switch_ops = { 11678c2ecf20Sopenharmony_ci .get_tag_protocol = ksz8795_get_tag_protocol, 11688c2ecf20Sopenharmony_ci .setup = ksz8795_setup, 11698c2ecf20Sopenharmony_ci .phy_read = ksz_phy_read16, 11708c2ecf20Sopenharmony_ci .phy_write = ksz_phy_write16, 11718c2ecf20Sopenharmony_ci .phylink_mac_link_down = ksz_mac_link_down, 11728c2ecf20Sopenharmony_ci .port_enable = ksz_enable_port, 11738c2ecf20Sopenharmony_ci .get_strings = ksz8795_get_strings, 11748c2ecf20Sopenharmony_ci .get_ethtool_stats = ksz_get_ethtool_stats, 11758c2ecf20Sopenharmony_ci .get_sset_count = ksz_sset_count, 11768c2ecf20Sopenharmony_ci .port_bridge_join = ksz_port_bridge_join, 11778c2ecf20Sopenharmony_ci .port_bridge_leave = ksz_port_bridge_leave, 11788c2ecf20Sopenharmony_ci .port_stp_state_set = ksz8795_port_stp_state_set, 11798c2ecf20Sopenharmony_ci .port_fast_age = ksz_port_fast_age, 11808c2ecf20Sopenharmony_ci .port_vlan_filtering = ksz8795_port_vlan_filtering, 11818c2ecf20Sopenharmony_ci .port_vlan_prepare = ksz8795_port_vlan_prepare, 11828c2ecf20Sopenharmony_ci .port_vlan_add = ksz8795_port_vlan_add, 11838c2ecf20Sopenharmony_ci .port_vlan_del = ksz8795_port_vlan_del, 11848c2ecf20Sopenharmony_ci .port_fdb_dump = ksz_port_fdb_dump, 11858c2ecf20Sopenharmony_ci .port_mdb_prepare = ksz_port_mdb_prepare, 11868c2ecf20Sopenharmony_ci .port_mdb_add = ksz_port_mdb_add, 11878c2ecf20Sopenharmony_ci .port_mdb_del = ksz_port_mdb_del, 11888c2ecf20Sopenharmony_ci .port_mirror_add = ksz8795_port_mirror_add, 11898c2ecf20Sopenharmony_ci .port_mirror_del = ksz8795_port_mirror_del, 11908c2ecf20Sopenharmony_ci}; 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_cistatic u32 ksz8795_get_port_addr(int port, int offset) 11938c2ecf20Sopenharmony_ci{ 11948c2ecf20Sopenharmony_ci return PORT_CTRL_ADDR(port, offset); 11958c2ecf20Sopenharmony_ci} 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_cistatic int ksz8795_switch_detect(struct ksz_device *dev) 11988c2ecf20Sopenharmony_ci{ 11998c2ecf20Sopenharmony_ci u8 id1, id2; 12008c2ecf20Sopenharmony_ci u16 id16; 12018c2ecf20Sopenharmony_ci int ret; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci /* read chip id */ 12048c2ecf20Sopenharmony_ci ret = ksz_read16(dev, REG_CHIP_ID0, &id16); 12058c2ecf20Sopenharmony_ci if (ret) 12068c2ecf20Sopenharmony_ci return ret; 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci id1 = id16 >> 8; 12098c2ecf20Sopenharmony_ci id2 = id16 & SW_CHIP_ID_M; 12108c2ecf20Sopenharmony_ci if (id1 != FAMILY_ID || 12118c2ecf20Sopenharmony_ci (id2 != CHIP_ID_94 && id2 != CHIP_ID_95)) 12128c2ecf20Sopenharmony_ci return -ENODEV; 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci dev->mib_port_cnt = TOTAL_PORT_NUM; 12158c2ecf20Sopenharmony_ci dev->phy_port_cnt = SWITCH_PORT_NUM; 12168c2ecf20Sopenharmony_ci dev->port_cnt = SWITCH_PORT_NUM; 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci if (id2 == CHIP_ID_95) { 12198c2ecf20Sopenharmony_ci u8 val; 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci id2 = 0x95; 12228c2ecf20Sopenharmony_ci ksz_read8(dev, REG_PORT_1_STATUS_0, &val); 12238c2ecf20Sopenharmony_ci if (val & PORT_FIBER_MODE) 12248c2ecf20Sopenharmony_ci id2 = 0x65; 12258c2ecf20Sopenharmony_ci } else if (id2 == CHIP_ID_94) { 12268c2ecf20Sopenharmony_ci dev->port_cnt--; 12278c2ecf20Sopenharmony_ci dev->last_port = dev->port_cnt; 12288c2ecf20Sopenharmony_ci id2 = 0x94; 12298c2ecf20Sopenharmony_ci } 12308c2ecf20Sopenharmony_ci id16 &= ~0xff; 12318c2ecf20Sopenharmony_ci id16 |= id2; 12328c2ecf20Sopenharmony_ci dev->chip_id = id16; 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci dev->cpu_port = dev->mib_port_cnt - 1; 12358c2ecf20Sopenharmony_ci dev->host_mask = BIT(dev->cpu_port); 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci return 0; 12388c2ecf20Sopenharmony_ci} 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_cistruct ksz_chip_data { 12418c2ecf20Sopenharmony_ci u16 chip_id; 12428c2ecf20Sopenharmony_ci const char *dev_name; 12438c2ecf20Sopenharmony_ci int num_vlans; 12448c2ecf20Sopenharmony_ci int num_alus; 12458c2ecf20Sopenharmony_ci int num_statics; 12468c2ecf20Sopenharmony_ci int cpu_ports; 12478c2ecf20Sopenharmony_ci int port_cnt; 12488c2ecf20Sopenharmony_ci}; 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_cistatic const struct ksz_chip_data ksz8795_switch_chips[] = { 12518c2ecf20Sopenharmony_ci { 12528c2ecf20Sopenharmony_ci .chip_id = 0x8795, 12538c2ecf20Sopenharmony_ci .dev_name = "KSZ8795", 12548c2ecf20Sopenharmony_ci .num_vlans = 4096, 12558c2ecf20Sopenharmony_ci .num_alus = 0, 12568c2ecf20Sopenharmony_ci .num_statics = 8, 12578c2ecf20Sopenharmony_ci .cpu_ports = 0x10, /* can be configured as cpu port */ 12588c2ecf20Sopenharmony_ci .port_cnt = 4, /* total physical port count */ 12598c2ecf20Sopenharmony_ci }, 12608c2ecf20Sopenharmony_ci { 12618c2ecf20Sopenharmony_ci .chip_id = 0x8794, 12628c2ecf20Sopenharmony_ci .dev_name = "KSZ8794", 12638c2ecf20Sopenharmony_ci .num_vlans = 4096, 12648c2ecf20Sopenharmony_ci .num_alus = 0, 12658c2ecf20Sopenharmony_ci .num_statics = 8, 12668c2ecf20Sopenharmony_ci .cpu_ports = 0x10, /* can be configured as cpu port */ 12678c2ecf20Sopenharmony_ci .port_cnt = 3, /* total physical port count */ 12688c2ecf20Sopenharmony_ci }, 12698c2ecf20Sopenharmony_ci { 12708c2ecf20Sopenharmony_ci .chip_id = 0x8765, 12718c2ecf20Sopenharmony_ci .dev_name = "KSZ8765", 12728c2ecf20Sopenharmony_ci .num_vlans = 4096, 12738c2ecf20Sopenharmony_ci .num_alus = 0, 12748c2ecf20Sopenharmony_ci .num_statics = 8, 12758c2ecf20Sopenharmony_ci .cpu_ports = 0x10, /* can be configured as cpu port */ 12768c2ecf20Sopenharmony_ci .port_cnt = 4, /* total physical port count */ 12778c2ecf20Sopenharmony_ci }, 12788c2ecf20Sopenharmony_ci}; 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_cistatic int ksz8795_switch_init(struct ksz_device *dev) 12818c2ecf20Sopenharmony_ci{ 12828c2ecf20Sopenharmony_ci int i; 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci dev->ds->ops = &ksz8795_switch_ops; 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ksz8795_switch_chips); i++) { 12878c2ecf20Sopenharmony_ci const struct ksz_chip_data *chip = &ksz8795_switch_chips[i]; 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci if (dev->chip_id == chip->chip_id) { 12908c2ecf20Sopenharmony_ci dev->name = chip->dev_name; 12918c2ecf20Sopenharmony_ci dev->num_vlans = chip->num_vlans; 12928c2ecf20Sopenharmony_ci dev->num_alus = chip->num_alus; 12938c2ecf20Sopenharmony_ci dev->num_statics = chip->num_statics; 12948c2ecf20Sopenharmony_ci dev->port_cnt = chip->port_cnt; 12958c2ecf20Sopenharmony_ci dev->cpu_ports = chip->cpu_ports; 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci break; 12988c2ecf20Sopenharmony_ci } 12998c2ecf20Sopenharmony_ci } 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci /* no switch found */ 13028c2ecf20Sopenharmony_ci if (!dev->cpu_ports) 13038c2ecf20Sopenharmony_ci return -ENODEV; 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci dev->port_mask = BIT(dev->port_cnt) - 1; 13068c2ecf20Sopenharmony_ci dev->port_mask |= dev->host_mask; 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci dev->reg_mib_cnt = SWITCH_COUNTER_NUM; 13098c2ecf20Sopenharmony_ci dev->mib_cnt = TOTAL_SWITCH_COUNTER_NUM; 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci i = dev->mib_port_cnt; 13128c2ecf20Sopenharmony_ci dev->ports = devm_kzalloc(dev->dev, sizeof(struct ksz_port) * i, 13138c2ecf20Sopenharmony_ci GFP_KERNEL); 13148c2ecf20Sopenharmony_ci if (!dev->ports) 13158c2ecf20Sopenharmony_ci return -ENOMEM; 13168c2ecf20Sopenharmony_ci for (i = 0; i < dev->mib_port_cnt; i++) { 13178c2ecf20Sopenharmony_ci mutex_init(&dev->ports[i].mib.cnt_mutex); 13188c2ecf20Sopenharmony_ci dev->ports[i].mib.counters = 13198c2ecf20Sopenharmony_ci devm_kzalloc(dev->dev, 13208c2ecf20Sopenharmony_ci sizeof(u64) * 13218c2ecf20Sopenharmony_ci (TOTAL_SWITCH_COUNTER_NUM + 1), 13228c2ecf20Sopenharmony_ci GFP_KERNEL); 13238c2ecf20Sopenharmony_ci if (!dev->ports[i].mib.counters) 13248c2ecf20Sopenharmony_ci return -ENOMEM; 13258c2ecf20Sopenharmony_ci } 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci /* set the real number of ports */ 13288c2ecf20Sopenharmony_ci dev->ds->num_ports = dev->port_cnt + 1; 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci /* We rely on software untagging on the CPU port, so that we 13318c2ecf20Sopenharmony_ci * can support both tagged and untagged VLANs 13328c2ecf20Sopenharmony_ci */ 13338c2ecf20Sopenharmony_ci dev->ds->untag_bridge_pvid = true; 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci /* VLAN filtering is partly controlled by the global VLAN 13368c2ecf20Sopenharmony_ci * Enable flag 13378c2ecf20Sopenharmony_ci */ 13388c2ecf20Sopenharmony_ci dev->ds->vlan_filtering_is_global = true; 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci return 0; 13418c2ecf20Sopenharmony_ci} 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_cistatic void ksz8795_switch_exit(struct ksz_device *dev) 13448c2ecf20Sopenharmony_ci{ 13458c2ecf20Sopenharmony_ci ksz8795_reset_switch(dev); 13468c2ecf20Sopenharmony_ci} 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_cistatic const struct ksz_dev_ops ksz8795_dev_ops = { 13498c2ecf20Sopenharmony_ci .get_port_addr = ksz8795_get_port_addr, 13508c2ecf20Sopenharmony_ci .cfg_port_member = ksz8795_cfg_port_member, 13518c2ecf20Sopenharmony_ci .flush_dyn_mac_table = ksz8795_flush_dyn_mac_table, 13528c2ecf20Sopenharmony_ci .port_setup = ksz8795_port_setup, 13538c2ecf20Sopenharmony_ci .r_phy = ksz8795_r_phy, 13548c2ecf20Sopenharmony_ci .w_phy = ksz8795_w_phy, 13558c2ecf20Sopenharmony_ci .r_dyn_mac_table = ksz8795_r_dyn_mac_table, 13568c2ecf20Sopenharmony_ci .r_sta_mac_table = ksz8795_r_sta_mac_table, 13578c2ecf20Sopenharmony_ci .w_sta_mac_table = ksz8795_w_sta_mac_table, 13588c2ecf20Sopenharmony_ci .r_mib_cnt = ksz8795_r_mib_cnt, 13598c2ecf20Sopenharmony_ci .r_mib_pkt = ksz8795_r_mib_pkt, 13608c2ecf20Sopenharmony_ci .freeze_mib = ksz8795_freeze_mib, 13618c2ecf20Sopenharmony_ci .port_init_cnt = ksz8795_port_init_cnt, 13628c2ecf20Sopenharmony_ci .shutdown = ksz8795_reset_switch, 13638c2ecf20Sopenharmony_ci .detect = ksz8795_switch_detect, 13648c2ecf20Sopenharmony_ci .init = ksz8795_switch_init, 13658c2ecf20Sopenharmony_ci .exit = ksz8795_switch_exit, 13668c2ecf20Sopenharmony_ci}; 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ciint ksz8795_switch_register(struct ksz_device *dev) 13698c2ecf20Sopenharmony_ci{ 13708c2ecf20Sopenharmony_ci return ksz_switch_register(dev, &ksz8795_dev_ops); 13718c2ecf20Sopenharmony_ci} 13728c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ksz8795_switch_register); 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ciMODULE_AUTHOR("Tristram Ha <Tristram.Ha@microchip.com>"); 13758c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Microchip KSZ8795 Series Switch DSA Driver"); 13768c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1377