Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,47 @@
# SPDX-License-Identifier: GPL-2.0-only
menuconfig NET_DSA_MICROCHIP_KSZ_COMMON
tristate "Microchip KSZ8XXX/KSZ9XXX/LAN937X series switch support"
depends on NET_DSA
select NET_DSA_TAG_KSZ
select NET_DSA_TAG_NONE
select NET_IEEE8021Q_HELPERS
select DCB
select PCS_XPCS
help
This driver adds support for Microchip KSZ8, KSZ9 and
LAN937X series switch chips, being KSZ8863/8873,
KSZ8895/8864, KSZ8794/8795/8765,
KSZ9477/9897/9896/9567/8567, KSZ9893/9563/8563 and
LAN9370/9371/9372/9373/9374.
config NET_DSA_MICROCHIP_KSZ9477_I2C
tristate "KSZ series I2C connected switch driver"
depends on NET_DSA_MICROCHIP_KSZ_COMMON && I2C
select REGMAP_I2C
help
Select to enable support for registering switches configured through I2C.
config NET_DSA_MICROCHIP_KSZ_SPI
tristate "KSZ series SPI connected switch driver"
depends on NET_DSA_MICROCHIP_KSZ_COMMON && SPI
select REGMAP_SPI
help
Select to enable support for registering switches configured through SPI.
config NET_DSA_MICROCHIP_KSZ_PTP
bool "Support for the PTP clock on the KSZ9563/LAN937x Ethernet Switch"
depends on NET_DSA_MICROCHIP_KSZ_COMMON && PTP_1588_CLOCK
depends on NET_DSA_MICROCHIP_KSZ_COMMON=m || PTP_1588_CLOCK=y
help
Select to enable support for timestamping & PTP clock manipulation in
KSZ8563/KSZ9563/LAN937x series of switches. KSZ9563/KSZ8563 supports
only one step timestamping. LAN937x switch supports both one step and
two step timestamping.
config NET_DSA_MICROCHIP_KSZ8863_SMI
tristate "KSZ series SMI connected switch driver"
depends on NET_DSA_MICROCHIP_KSZ_COMMON
select MDIO_BITBANG
help
Select to enable support for registering switches configured through
Microchip SMI. It supports the KSZ8863 and KSZ8873 switch.
@@ -0,0 +1,14 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ_COMMON) += ksz_switch.o
ksz_switch-objs := ksz_common.o ksz_dcb.o
ksz_switch-objs += ksz9477.o ksz9477_acl.o ksz9477_tc_flower.o
ksz_switch-objs += ksz8.o
ksz_switch-objs += lan937x_main.o
ifdef CONFIG_NET_DSA_MICROCHIP_KSZ_PTP
ksz_switch-objs += ksz_ptp.o
endif
obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ9477_I2C) += ksz9477_i2c.o
obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ_SPI) += ksz_spi.o
obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ8863_SMI) += ksz8863_smi.o
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,24 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Microchip KSZ8XXX series register access
*
* Copyright (C) 2020 Pengutronix, Michael Grzeschik <kernel@pengutronix.de>
*/
#ifndef __KSZ8XXX_H
#define __KSZ8XXX_H
#include <linux/types.h>
#include <net/dsa.h>
#include "ksz_common.h"
extern const struct ksz_dev_ops ksz8463_dev_ops;
extern const struct ksz_dev_ops ksz87xx_dev_ops;
extern const struct ksz_dev_ops ksz88xx_dev_ops;
extern const struct phylink_mac_ops ksz88x3_phylink_mac_ops;
extern const struct phylink_mac_ops ksz8_phylink_mac_ops;
extern const struct dsa_switch_ops ksz8463_switch_ops;
extern const struct dsa_switch_ops ksz87xx_switch_ops;
extern const struct dsa_switch_ops ksz88xx_switch_ops;
#endif
@@ -0,0 +1,225 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Microchip KSZ8863 series register access through SMI
*
* Copyright (C) 2019 Pengutronix, Michael Grzeschik <kernel@pengutronix.de>
*/
#include <linux/property.h>
#include "ksz8.h"
#include "ksz_common.h"
/* Serial Management Interface (SMI) uses the following frame format:
*
* preamble|start|Read/Write| PHY | REG |TA| Data bits | Idle
* |frame| OP code |address |address| | |
* read | 32x1´s | 01 | 00 | 1xRRR | RRRRR |Z0| 00000000DDDDDDDD | Z
* write| 32x1´s | 01 | 00 | 0xRRR | RRRRR |10| xxxxxxxxDDDDDDDD | Z
*
*/
#define SMI_KSZ88XX_READ_PHY BIT(4)
static int ksz8863_mdio_read(void *ctx, const void *reg_buf, size_t reg_len,
void *val_buf, size_t val_len)
{
struct ksz_device *dev = ctx;
struct mdio_device *mdev;
u8 reg = *(u8 *)reg_buf;
u8 *val = val_buf;
int i, ret = 0;
mdev = dev->priv;
mutex_lock_nested(&mdev->bus->mdio_lock, MDIO_MUTEX_NESTED);
for (i = 0; i < val_len; i++) {
int tmp = reg + i;
ret = __mdiobus_read(mdev->bus, ((tmp & 0xE0) >> 5) |
SMI_KSZ88XX_READ_PHY, tmp);
if (ret < 0)
goto out;
val[i] = ret;
}
ret = 0;
out:
mutex_unlock(&mdev->bus->mdio_lock);
return ret;
}
static int ksz8863_mdio_write(void *ctx, const void *data, size_t count)
{
struct ksz_device *dev = ctx;
struct mdio_device *mdev;
int i, ret = 0;
u32 reg;
u8 *val;
mdev = dev->priv;
val = (u8 *)(data + 4);
reg = *(u32 *)data;
mutex_lock_nested(&mdev->bus->mdio_lock, MDIO_MUTEX_NESTED);
for (i = 0; i < (count - 4); i++) {
int tmp = reg + i;
ret = __mdiobus_write(mdev->bus, ((tmp & 0xE0) >> 5),
tmp, val[i]);
if (ret < 0)
goto out;
}
out:
mutex_unlock(&mdev->bus->mdio_lock);
return ret;
}
static const struct regmap_bus regmap_smi[] = {
{
.read = ksz8863_mdio_read,
.write = ksz8863_mdio_write,
},
{
.read = ksz8863_mdio_read,
.write = ksz8863_mdio_write,
.val_format_endian_default = REGMAP_ENDIAN_BIG,
},
{
.read = ksz8863_mdio_read,
.write = ksz8863_mdio_write,
.val_format_endian_default = REGMAP_ENDIAN_BIG,
}
};
static const struct regmap_config ksz8863_regmap_config[] = {
{
.name = "#8",
.reg_bits = 8,
.pad_bits = 24,
.val_bits = 8,
.cache_type = REGCACHE_NONE,
.lock = ksz_regmap_lock,
.unlock = ksz_regmap_unlock,
.max_register = U8_MAX,
},
{
.name = "#16",
.reg_bits = 8,
.pad_bits = 24,
.val_bits = 16,
.cache_type = REGCACHE_NONE,
.lock = ksz_regmap_lock,
.unlock = ksz_regmap_unlock,
.max_register = U8_MAX,
},
{
.name = "#32",
.reg_bits = 8,
.pad_bits = 24,
.val_bits = 32,
.cache_type = REGCACHE_NONE,
.lock = ksz_regmap_lock,
.unlock = ksz_regmap_unlock,
.max_register = U8_MAX,
}
};
static int ksz8863_smi_probe(struct mdio_device *mdiodev)
{
struct device *ddev = &mdiodev->dev;
const struct ksz_chip_data *chip;
struct regmap_config rc;
struct ksz_device *dev;
int ret;
int i;
chip = device_get_match_data(ddev);
if (!chip)
return -EINVAL;
dev = ksz_switch_alloc(&mdiodev->dev, chip, mdiodev);
if (!dev)
return -ENOMEM;
for (i = 0; i < __KSZ_NUM_REGMAPS; i++) {
rc = ksz8863_regmap_config[i];
rc.lock_arg = &dev->regmap_mutex;
rc.wr_table = chip->wr_table;
rc.rd_table = chip->rd_table;
dev->regmap[i] = devm_regmap_init(&mdiodev->dev,
&regmap_smi[i], dev,
&rc);
if (IS_ERR(dev->regmap[i])) {
return dev_err_probe(&mdiodev->dev,
PTR_ERR(dev->regmap[i]),
"Failed to initialize regmap%i\n",
ksz8863_regmap_config[i].val_bits);
}
}
if (mdiodev->dev.platform_data)
dev->pdata = mdiodev->dev.platform_data;
ret = ksz_switch_register(dev);
/* Main DSA driver may not be started yet. */
if (ret)
return ret;
dev_set_drvdata(&mdiodev->dev, dev);
return 0;
}
static void ksz8863_smi_remove(struct mdio_device *mdiodev)
{
struct ksz_device *dev = dev_get_drvdata(&mdiodev->dev);
if (dev)
ksz_switch_remove(dev);
}
static void ksz8863_smi_shutdown(struct mdio_device *mdiodev)
{
struct ksz_device *dev = dev_get_drvdata(&mdiodev->dev);
if (dev)
dsa_switch_shutdown(dev->ds);
dev_set_drvdata(&mdiodev->dev, NULL);
}
static const struct of_device_id ksz8863_dt_ids[] = {
{
.compatible = "microchip,ksz8863",
.data = &ksz_switch_chips[KSZ88X3]
},
{
.compatible = "microchip,ksz8873",
.data = &ksz_switch_chips[KSZ88X3]
},
{ },
};
MODULE_DEVICE_TABLE(of, ksz8863_dt_ids);
static struct mdio_driver ksz8863_driver = {
.probe = ksz8863_smi_probe,
.remove = ksz8863_smi_remove,
.shutdown = ksz8863_smi_shutdown,
.mdiodrv.driver = {
.name = "ksz8863-switch",
.of_match_table = ksz8863_dt_ids,
},
};
mdio_module_driver(ksz8863_driver);
MODULE_AUTHOR("Michael Grzeschik <m.grzeschik@pengutronix.de>");
MODULE_DESCRIPTION("Microchip KSZ8863 SMI Switch driver");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,871 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Microchip KSZ8XXX series register definitions
*
* The base for these definitions is KSZ8795 but unless indicated
* differently by their prefix, they apply to all KSZ8 series
* devices. Registers and masks that do change are defined in
* dedicated structures in ksz_common.c.
*
* Copyright (c) 2017 Microchip Technology Inc.
* Tristram Ha <Tristram.Ha@microchip.com>
*/
#ifndef __KSZ8_REG_H
#define __KSZ8_REG_H
#define KS_PORT_M 0x1F
#define KS_PRIO_M 0x3
#define KS_PRIO_S 2
#define SW_REVISION_M 0x0E
#define SW_REVISION_S 1
#define KSZ8863_REG_SW_RESET 0x43
#define KSZ8863_GLOBAL_SOFTWARE_RESET BIT(4)
#define KSZ8863_PCS_RESET BIT(0)
#define KSZ88X3_REG_FVID_AND_HOST_MODE 0xC6
#define KSZ88X3_PORT3_RMII_CLK_INTERNAL BIT(3)
#define REG_SW_CTRL_0 0x02
#define SW_NEW_BACKOFF BIT(7)
#define SW_GLOBAL_RESET BIT(6)
#define SW_FLUSH_DYN_MAC_TABLE BIT(5)
#define SW_FLUSH_STA_MAC_TABLE BIT(4)
#define SW_LINK_AUTO_AGING BIT(0)
#define REG_SW_CTRL_1 0x03
#define SW_HUGE_PACKET BIT(6)
#define SW_TX_FLOW_CTRL_DISABLE BIT(5)
#define SW_RX_FLOW_CTRL_DISABLE BIT(4)
#define SW_CHECK_LENGTH BIT(3)
#define SW_AGING_ENABLE BIT(2)
#define SW_FAST_AGING BIT(1)
#define SW_AGGR_BACKOFF BIT(0)
#define REG_SW_CTRL_2 0x04
#define UNICAST_VLAN_BOUNDARY BIT(7)
#define SW_BACK_PRESSURE BIT(5)
#define FAIR_FLOW_CTRL BIT(4)
#define NO_EXC_COLLISION_DROP BIT(3)
#define SW_LEGAL_PACKET_DISABLE BIT(1)
#define KSZ8863_HUGE_PACKET_ENABLE BIT(2)
#define KSZ8863_LEGAL_PACKET_ENABLE BIT(1)
#define REG_SW_CTRL_3 0x05
#define WEIGHTED_FAIR_QUEUE_ENABLE BIT(3)
#define SW_VLAN_ENABLE BIT(7)
#define SW_IGMP_SNOOP BIT(6)
#define SW_MIRROR_RX_TX BIT(0)
#define REG_SW_CTRL_4 0x06
#define SW_HALF_DUPLEX_FLOW_CTRL BIT(7)
#define SW_HALF_DUPLEX BIT(6)
#define SW_FLOW_CTRL BIT(5)
#define SW_10_MBIT BIT(4)
#define SW_REPLACE_VID BIT(3)
#define REG_SW_CTRL_5 0x07
#define REG_SW_CTRL_6 0x08
#define SW_MIB_COUNTER_FLUSH BIT(7)
#define SW_MIB_COUNTER_FREEZE BIT(6)
#define SW_MIB_COUNTER_CTRL_ENABLE KS_PORT_M
#define REG_SW_CTRL_9 0x0B
#define SPI_CLK_125_MHZ 0x80
#define SPI_CLK_62_5_MHZ 0x40
#define SPI_CLK_31_25_MHZ 0x00
#define SW_LED_MODE_M 0x3
#define SW_LED_MODE_S 4
#define SW_LED_LINK_ACT_SPEED 0
#define SW_LED_LINK_ACT 1
#define SW_LED_LINK_ACT_DUPLEX 2
#define SW_LED_LINK_DUPLEX 3
#define REG_SW_CTRL_10 0x0C
#define SW_PASS_PAUSE BIT(0)
#define REG_SW_CTRL_11 0x0D
#define REG_POWER_MANAGEMENT_1 0x0E
#define SW_PLL_POWER_DOWN BIT(5)
#define SW_POWER_MANAGEMENT_MODE_M 0x3
#define SW_POWER_MANAGEMENT_MODE_S 3
#define SW_POWER_NORMAL 0
#define SW_ENERGY_DETECTION 1
#define SW_SOFTWARE_POWER_DOWN 2
#define REG_POWER_MANAGEMENT_2 0x0F
#define REG_PORT_1_CTRL_0 0x10
#define REG_PORT_2_CTRL_0 0x20
#define REG_PORT_3_CTRL_0 0x30
#define REG_PORT_4_CTRL_0 0x40
#define REG_PORT_5_CTRL_0 0x50
#define PORT_BROADCAST_STORM BIT(7)
#define PORT_DIFFSERV_ENABLE BIT(6)
#define PORT_802_1P_ENABLE BIT(5)
#define PORT_BASED_PRIO_S 3
#define PORT_BASED_PRIO_M KS_PRIO_M
#define PORT_BASED_PRIO_0 0
#define PORT_BASED_PRIO_1 1
#define PORT_BASED_PRIO_2 2
#define PORT_BASED_PRIO_3 3
#define PORT_INSERT_TAG BIT(2)
#define PORT_REMOVE_TAG BIT(1)
#define KSZ8795_PORT_2QUEUE_SPLIT_EN BIT(0)
#define KSZ8873_PORT_4QUEUE_SPLIT_EN BIT(0)
#define REG_PORT_1_CTRL_1 0x11
#define REG_PORT_2_CTRL_1 0x21
#define REG_PORT_3_CTRL_1 0x31
#define REG_PORT_4_CTRL_1 0x41
#define REG_PORT_5_CTRL_1 0x51
#define PORT_MIRROR_SNIFFER BIT(7)
#define PORT_MIRROR_RX BIT(6)
#define PORT_MIRROR_TX BIT(5)
#define PORT_VLAN_MEMBERSHIP KS_PORT_M
#define REG_PORT_1_CTRL_2 0x12
#define REG_PORT_2_CTRL_2 0x22
#define REG_PORT_3_CTRL_2 0x32
#define REG_PORT_4_CTRL_2 0x42
#define REG_PORT_5_CTRL_2 0x52
#define KSZ8873_PORT_2QUEUE_SPLIT_EN BIT(7)
#define PORT_INGRESS_FILTER BIT(6)
#define PORT_DISCARD_NON_VID BIT(5)
#define PORT_FORCE_FLOW_CTRL BIT(4)
#define PORT_BACK_PRESSURE BIT(3)
#define REG_PORT_1_CTRL_3 0x13
#define REG_PORT_2_CTRL_3 0x23
#define REG_PORT_3_CTRL_3 0x33
#define REG_PORT_4_CTRL_3 0x43
#define REG_PORT_5_CTRL_3 0x53
#define REG_PORT_1_CTRL_4 0x14
#define REG_PORT_2_CTRL_4 0x24
#define REG_PORT_3_CTRL_4 0x34
#define REG_PORT_4_CTRL_4 0x44
#define REG_PORT_5_CTRL_4 0x54
#define PORT_DEFAULT_VID 0x0001
#define REG_PORT_1_CTRL_5 0x15
#define REG_PORT_2_CTRL_5 0x25
#define REG_PORT_3_CTRL_5 0x35
#define REG_PORT_4_CTRL_5 0x45
#define REG_PORT_5_CTRL_5 0x55
#define PORT_ACL_ENABLE BIT(2)
#define PORT_AUTHEN_MODE 0x3
#define PORT_AUTHEN_PASS 0
#define PORT_AUTHEN_BLOCK 1
#define PORT_AUTHEN_TRAP 2
#define REG_PORT_5_CTRL_6 0x56
#define PORT_MII_INTERNAL_CLOCK BIT(7)
#define PORT_GMII_MAC_MODE BIT(2)
#define REG_PORT_1_CTRL_7 0x17
#define REG_PORT_2_CTRL_7 0x27
#define REG_PORT_3_CTRL_7 0x37
#define REG_PORT_4_CTRL_7 0x47
#define PORT_AUTO_NEG_ASYM_PAUSE BIT(5)
#define PORT_AUTO_NEG_SYM_PAUSE BIT(4)
#define PORT_AUTO_NEG_100BTX_FD BIT(3)
#define PORT_AUTO_NEG_100BTX BIT(2)
#define PORT_AUTO_NEG_10BT_FD BIT(1)
#define PORT_AUTO_NEG_10BT BIT(0)
#define REG_PORT_1_STATUS_0 0x18
#define REG_PORT_2_STATUS_0 0x28
#define REG_PORT_3_STATUS_0 0x38
#define REG_PORT_4_STATUS_0 0x48
/* KSZ87xx LinkMD registers (TABLE_LINK_MD_V) */
#define KSZ87XX_REG_DSP_EQ 0x08 /* DSP EQ initial value */
#define KSZ87XX_REG_PHY_LPF 0x4C /* RX LPF bandwidth */
/* For KSZ8765. */
#define PORT_REMOTE_ASYM_PAUSE BIT(5)
#define PORT_REMOTE_SYM_PAUSE BIT(4)
#define PORT_REMOTE_100BTX_FD BIT(3)
#define PORT_REMOTE_100BTX BIT(2)
#define PORT_REMOTE_10BT_FD BIT(1)
#define PORT_REMOTE_10BT BIT(0)
#define REG_PORT_1_STATUS_1 0x19
#define REG_PORT_2_STATUS_1 0x29
#define REG_PORT_3_STATUS_1 0x39
#define REG_PORT_4_STATUS_1 0x49
#define PORT_HP_MDIX BIT(7)
#define PORT_REVERSED_POLARITY BIT(5)
#define PORT_TX_FLOW_CTRL BIT(4)
#define PORT_RX_FLOW_CTRL BIT(3)
#define PORT_STAT_SPEED_100MBIT BIT(2)
#define PORT_STAT_FULL_DUPLEX BIT(1)
#define PORT_REMOTE_FAULT BIT(0)
#define REG_PORT_1_LINK_MD_CTRL 0x1A
#define REG_PORT_2_LINK_MD_CTRL 0x2A
#define REG_PORT_3_LINK_MD_CTRL 0x3A
#define REG_PORT_4_LINK_MD_CTRL 0x4A
#define PORT_CABLE_10M_SHORT BIT(7)
#define PORT_CABLE_DIAG_RESULT_M GENMASK(6, 5)
#define PORT_CABLE_DIAG_RESULT_S 5
#define PORT_CABLE_STAT_NORMAL 0
#define PORT_CABLE_STAT_OPEN 1
#define PORT_CABLE_STAT_SHORT 2
#define PORT_CABLE_STAT_FAILED 3
#define PORT_START_CABLE_DIAG BIT(4)
#define PORT_FORCE_LINK BIT(3)
#define PORT_POWER_SAVING BIT(2)
#define PORT_PHY_REMOTE_LOOPBACK BIT(1)
#define PORT_CABLE_FAULT_COUNTER_H 0x01
#define REG_PORT_1_LINK_MD_RESULT 0x1B
#define REG_PORT_2_LINK_MD_RESULT 0x2B
#define REG_PORT_3_LINK_MD_RESULT 0x3B
#define REG_PORT_4_LINK_MD_RESULT 0x4B
#define PORT_CABLE_FAULT_COUNTER_L 0xFF
#define PORT_CABLE_FAULT_COUNTER 0x1FF
#define REG_PORT_1_CTRL_9 0x1C
#define REG_PORT_2_CTRL_9 0x2C
#define REG_PORT_3_CTRL_9 0x3C
#define REG_PORT_4_CTRL_9 0x4C
#define PORT_AUTO_NEG_ENABLE BIT(7)
#define PORT_AUTO_NEG_DISABLE BIT(7)
#define PORT_FORCE_100_MBIT BIT(6)
#define PORT_FORCE_FULL_DUPLEX BIT(5)
#define REG_PORT_1_CTRL_10 0x1D
#define REG_PORT_2_CTRL_10 0x2D
#define REG_PORT_3_CTRL_10 0x3D
#define REG_PORT_4_CTRL_10 0x4D
#define PORT_LED_OFF BIT(7)
#define PORT_TX_DISABLE BIT(6)
#define PORT_AUTO_NEG_RESTART BIT(5)
#define PORT_POWER_DOWN BIT(3)
#define PORT_AUTO_MDIX_DISABLE BIT(2)
#define PORT_FORCE_MDIX BIT(1)
#define PORT_MAC_LOOPBACK BIT(0)
#define KSZ8873_PORT_PHY_LOOPBACK BIT(0)
#define REG_PORT_1_STATUS_2 0x1E
#define REG_PORT_2_STATUS_2 0x2E
#define REG_PORT_3_STATUS_2 0x3E
#define REG_PORT_4_STATUS_2 0x4E
#define PORT_MDIX_STATUS BIT(7)
#define PORT_AUTO_NEG_COMPLETE BIT(6)
#define PORT_STAT_LINK_GOOD BIT(5)
#define REG_PORT_1_STATUS_3 0x1F
#define REG_PORT_2_STATUS_3 0x2F
#define REG_PORT_3_STATUS_3 0x3F
#define REG_PORT_4_STATUS_3 0x4F
#define PORT_PHY_LOOPBACK BIT(7)
#define PORT_PHY_ISOLATE BIT(5)
#define PORT_PHY_SOFT_RESET BIT(4)
#define PORT_PHY_FORCE_LINK BIT(3)
#define PORT_PHY_MODE_M 0x7
#define PHY_MODE_IN_AUTO_NEG 1
#define PHY_MODE_10BT_HALF 2
#define PHY_MODE_100BT_HALF 3
#define PHY_MODE_10BT_FULL 5
#define PHY_MODE_100BT_FULL 6
#define PHY_MODE_ISOLDATE 7
#define REG_PORT_CTRL_0 0x00
#define REG_PORT_CTRL_1 0x01
#define REG_PORT_CTRL_2 0x02
#define REG_PORT_CTRL_VID 0x03
#define REG_PORT_CTRL_5 0x05
#define REG_PORT_STATUS_1 0x09
#define REG_PORT_LINK_MD_CTRL 0x0A
#define REG_PORT_LINK_MD_RESULT 0x0B
#define REG_PORT_CTRL_9 0x0C
#define REG_PORT_CTRL_10 0x0D
#define REG_PORT_STATUS_3 0x0F
#define REG_PORT_CTRL_12 0xA0
#define REG_PORT_CTRL_13 0xA1
#define REG_PORT_RATE_CTRL_3 0xA2
#define REG_PORT_RATE_CTRL_2 0xA3
#define REG_PORT_RATE_CTRL_1 0xA4
#define REG_PORT_RATE_CTRL_0 0xA5
#define REG_PORT_RATE_LIMIT 0xA6
#define REG_PORT_IN_RATE_0 0xA7
#define REG_PORT_IN_RATE_1 0xA8
#define REG_PORT_IN_RATE_2 0xA9
#define REG_PORT_IN_RATE_3 0xAA
#define REG_PORT_OUT_RATE_0 0xAB
#define REG_PORT_OUT_RATE_1 0xAC
#define REG_PORT_OUT_RATE_2 0xAD
#define REG_PORT_OUT_RATE_3 0xAE
#define PORT_CTRL_ADDR(port, addr) \
((addr) + REG_PORT_1_CTRL_0 + (port) * \
(REG_PORT_2_CTRL_0 - REG_PORT_1_CTRL_0))
#define TABLE_EXT_SELECT_S 5
#define TABLE_EEE_V 1
#define TABLE_ACL_V 2
#define TABLE_PME_V 4
#define TABLE_LINK_MD_V 5
#define TABLE_EEE (TABLE_EEE_V << TABLE_EXT_SELECT_S)
#define TABLE_ACL (TABLE_ACL_V << TABLE_EXT_SELECT_S)
#define TABLE_PME (TABLE_PME_V << TABLE_EXT_SELECT_S)
#define TABLE_LINK_MD (TABLE_LINK_MD_V << TABLE_EXT_SELECT_S)
#define TABLE_READ BIT(4)
#define TABLE_SELECT_S 2
#define TABLE_STATIC_MAC_V 0
#define TABLE_VLAN_V 1
#define TABLE_DYNAMIC_MAC_V 2
#define TABLE_MIB_V 3
#define TABLE_STATIC_MAC (TABLE_STATIC_MAC_V << TABLE_SELECT_S)
#define TABLE_VLAN (TABLE_VLAN_V << TABLE_SELECT_S)
#define TABLE_DYNAMIC_MAC (TABLE_DYNAMIC_MAC_V << TABLE_SELECT_S)
#define TABLE_MIB (TABLE_MIB_V << TABLE_SELECT_S)
#define REG_IND_CTRL_1 0x6F
#define TABLE_ENTRY_MASK 0x03FF
#define TABLE_EXT_ENTRY_MASK 0x0FFF
#define REG_IND_DATA_5 0x73
#define REG_IND_DATA_2 0x76
#define REG_IND_DATA_1 0x77
#define REG_IND_DATA_0 0x78
#define REG_INT_STATUS 0x7C
#define REG_INT_ENABLE 0x7D
#define INT_PME BIT(4)
#define REG_ACL_INT_STATUS 0x7E
#define REG_ACL_INT_ENABLE 0x7F
#define INT_PORT_5 BIT(4)
#define INT_PORT_4 BIT(3)
#define INT_PORT_3 BIT(2)
#define INT_PORT_2 BIT(1)
#define INT_PORT_1 BIT(0)
#define INT_PORT_ALL \
(INT_PORT_5 | INT_PORT_4 | INT_PORT_3 | INT_PORT_2 | INT_PORT_1)
#define REG_SW_CTRL_12 0x80
#define REG_SW_CTRL_13 0x81
#define SWITCH_802_1P_MASK 3
#define SWITCH_802_1P_BASE 3
#define SWITCH_802_1P_SHIFT 2
#define SW_802_1P_MAP_M KS_PRIO_M
#define SW_802_1P_MAP_S KS_PRIO_S
#define REG_SWITCH_CTRL_14 0x82
#define SW_PRIO_MAPPING_M KS_PRIO_M
#define SW_PRIO_MAPPING_S 6
#define SW_PRIO_MAP_3_HI 0
#define SW_PRIO_MAP_2_HI 2
#define SW_PRIO_MAP_0_LO 3
#define REG_SW_CTRL_15 0x83
#define REG_SW_CTRL_16 0x84
#define REG_SW_CTRL_17 0x85
#define REG_SW_CTRL_18 0x86
#define SW_SELF_ADDR_FILTER_ENABLE BIT(6)
#define REG_SW_UNK_UCAST_CTRL 0x83
#define REG_SW_UNK_MCAST_CTRL 0x84
#define REG_SW_UNK_VID_CTRL 0x85
#define REG_SW_UNK_IP_MCAST_CTRL 0x86
#define SW_UNK_FWD_ENABLE BIT(5)
#define SW_UNK_FWD_MAP KS_PORT_M
#define REG_SW_CTRL_19 0x87
#define SW_IN_RATE_LIMIT_PERIOD_M 0x3
#define SW_IN_RATE_LIMIT_PERIOD_S 4
#define SW_IN_RATE_LIMIT_16_MS 0
#define SW_IN_RATE_LIMIT_64_MS 1
#define SW_IN_RATE_LIMIT_256_MS 2
#define SW_OUT_RATE_LIMIT_QUEUE_BASED BIT(3)
#define SW_INS_TAG_ENABLE BIT(2)
#define REG_TOS_PRIO_CTRL_0 0x90
#define REG_TOS_PRIO_CTRL_1 0x91
#define REG_TOS_PRIO_CTRL_2 0x92
#define REG_TOS_PRIO_CTRL_3 0x93
#define REG_TOS_PRIO_CTRL_4 0x94
#define REG_TOS_PRIO_CTRL_5 0x95
#define REG_TOS_PRIO_CTRL_6 0x96
#define REG_TOS_PRIO_CTRL_7 0x97
#define REG_TOS_PRIO_CTRL_8 0x98
#define REG_TOS_PRIO_CTRL_9 0x99
#define REG_TOS_PRIO_CTRL_10 0x9A
#define REG_TOS_PRIO_CTRL_11 0x9B
#define REG_TOS_PRIO_CTRL_12 0x9C
#define REG_TOS_PRIO_CTRL_13 0x9D
#define REG_TOS_PRIO_CTRL_14 0x9E
#define REG_TOS_PRIO_CTRL_15 0x9F
#define TOS_PRIO_M KS_PRIO_M
#define TOS_PRIO_S KS_PRIO_S
#define REG_SW_CTRL_21 0xA4
#define SW_IPV6_MLD_OPTION BIT(3)
#define SW_IPV6_MLD_SNOOP BIT(2)
#define REG_PORT_1_CTRL_12 0xB0
#define REG_PORT_2_CTRL_12 0xC0
#define REG_PORT_3_CTRL_12 0xD0
#define REG_PORT_4_CTRL_12 0xE0
#define REG_PORT_5_CTRL_12 0xF0
#define PORT_PASS_ALL BIT(6)
#define PORT_INS_TAG_FOR_PORT_5_S 3
#define PORT_INS_TAG_FOR_PORT_5 BIT(3)
#define PORT_INS_TAG_FOR_PORT_4 BIT(2)
#define PORT_INS_TAG_FOR_PORT_3 BIT(1)
#define PORT_INS_TAG_FOR_PORT_2 BIT(0)
#define REG_PORT_1_CTRL_13 0xB1
#define REG_PORT_2_CTRL_13 0xC1
#define REG_PORT_3_CTRL_13 0xD1
#define REG_PORT_4_CTRL_13 0xE1
#define REG_PORT_5_CTRL_13 0xF1
#define KSZ8795_PORT_4QUEUE_SPLIT_EN BIT(1)
#define PORT_DROP_TAG BIT(0)
#define REG_PORT_1_CTRL_14 0xB2
#define REG_PORT_2_CTRL_14 0xC2
#define REG_PORT_3_CTRL_14 0xD2
#define REG_PORT_4_CTRL_14 0xE2
#define REG_PORT_5_CTRL_14 0xF2
#define REG_PORT_1_CTRL_15 0xB3
#define REG_PORT_2_CTRL_15 0xC3
#define REG_PORT_3_CTRL_15 0xD3
#define REG_PORT_4_CTRL_15 0xE3
#define REG_PORT_5_CTRL_15 0xF3
#define REG_PORT_1_CTRL_16 0xB4
#define REG_PORT_2_CTRL_16 0xC4
#define REG_PORT_3_CTRL_16 0xD4
#define REG_PORT_4_CTRL_16 0xE4
#define REG_PORT_5_CTRL_16 0xF4
#define REG_PORT_1_CTRL_17 0xB5
#define REG_PORT_2_CTRL_17 0xC5
#define REG_PORT_3_CTRL_17 0xD5
#define REG_PORT_4_CTRL_17 0xE5
#define REG_PORT_5_CTRL_17 0xF5
#define REG_PORT_1_RATE_CTRL_3 0xB2
#define REG_PORT_1_RATE_CTRL_2 0xB3
#define REG_PORT_1_RATE_CTRL_1 0xB4
#define REG_PORT_1_RATE_CTRL_0 0xB5
#define REG_PORT_2_RATE_CTRL_3 0xC2
#define REG_PORT_2_RATE_CTRL_2 0xC3
#define REG_PORT_2_RATE_CTRL_1 0xC4
#define REG_PORT_2_RATE_CTRL_0 0xC5
#define REG_PORT_3_RATE_CTRL_3 0xD2
#define REG_PORT_3_RATE_CTRL_2 0xD3
#define REG_PORT_3_RATE_CTRL_1 0xD4
#define REG_PORT_3_RATE_CTRL_0 0xD5
#define REG_PORT_4_RATE_CTRL_3 0xE2
#define REG_PORT_4_RATE_CTRL_2 0xE3
#define REG_PORT_4_RATE_CTRL_1 0xE4
#define REG_PORT_4_RATE_CTRL_0 0xE5
#define REG_PORT_5_RATE_CTRL_3 0xF2
#define REG_PORT_5_RATE_CTRL_2 0xF3
#define REG_PORT_5_RATE_CTRL_1 0xF4
#define REG_PORT_5_RATE_CTRL_0 0xF5
#define RATE_CTRL_ENABLE BIT(7)
#define RATE_RATIO_M (BIT(7) - 1)
#define PORT_OUT_RATE_ENABLE BIT(7)
#define REG_PORT_1_RATE_LIMIT 0xB6
#define REG_PORT_2_RATE_LIMIT 0xC6
#define REG_PORT_3_RATE_LIMIT 0xD6
#define REG_PORT_4_RATE_LIMIT 0xE6
#define REG_PORT_5_RATE_LIMIT 0xF6
#define PORT_IN_PORT_BASED_S 6
#define PORT_RATE_PACKET_BASED_S 5
#define PORT_IN_FLOW_CTRL_S 4
#define PORT_IN_LIMIT_MODE_M 0x3
#define PORT_IN_LIMIT_MODE_S 2
#define PORT_COUNT_IFG_S 1
#define PORT_COUNT_PREAMBLE_S 0
#define PORT_IN_PORT_BASED BIT(PORT_IN_PORT_BASED_S)
#define PORT_RATE_PACKET_BASED BIT(PORT_RATE_PACKET_BASED_S)
#define PORT_IN_FLOW_CTRL BIT(PORT_IN_FLOW_CTRL_S)
#define PORT_IN_ALL 0
#define PORT_IN_UNICAST 1
#define PORT_IN_MULTICAST 2
#define PORT_IN_BROADCAST 3
#define PORT_COUNT_IFG BIT(PORT_COUNT_IFG_S)
#define PORT_COUNT_PREAMBLE BIT(PORT_COUNT_PREAMBLE_S)
#define REG_PORT_1_IN_RATE_0 0xB7
#define REG_PORT_2_IN_RATE_0 0xC7
#define REG_PORT_3_IN_RATE_0 0xD7
#define REG_PORT_4_IN_RATE_0 0xE7
#define REG_PORT_5_IN_RATE_0 0xF7
#define REG_PORT_1_IN_RATE_1 0xB8
#define REG_PORT_2_IN_RATE_1 0xC8
#define REG_PORT_3_IN_RATE_1 0xD8
#define REG_PORT_4_IN_RATE_1 0xE8
#define REG_PORT_5_IN_RATE_1 0xF8
#define REG_PORT_1_IN_RATE_2 0xB9
#define REG_PORT_2_IN_RATE_2 0xC9
#define REG_PORT_3_IN_RATE_2 0xD9
#define REG_PORT_4_IN_RATE_2 0xE9
#define REG_PORT_5_IN_RATE_2 0xF9
#define REG_PORT_1_IN_RATE_3 0xBA
#define REG_PORT_2_IN_RATE_3 0xCA
#define REG_PORT_3_IN_RATE_3 0xDA
#define REG_PORT_4_IN_RATE_3 0xEA
#define REG_PORT_5_IN_RATE_3 0xFA
#define PORT_IN_RATE_ENABLE BIT(7)
#define PORT_RATE_LIMIT_M (BIT(7) - 1)
#define REG_PORT_1_OUT_RATE_0 0xBB
#define REG_PORT_2_OUT_RATE_0 0xCB
#define REG_PORT_3_OUT_RATE_0 0xDB
#define REG_PORT_4_OUT_RATE_0 0xEB
#define REG_PORT_5_OUT_RATE_0 0xFB
#define REG_PORT_1_OUT_RATE_1 0xBC
#define REG_PORT_2_OUT_RATE_1 0xCC
#define REG_PORT_3_OUT_RATE_1 0xDC
#define REG_PORT_4_OUT_RATE_1 0xEC
#define REG_PORT_5_OUT_RATE_1 0xFC
#define REG_PORT_1_OUT_RATE_2 0xBD
#define REG_PORT_2_OUT_RATE_2 0xCD
#define REG_PORT_3_OUT_RATE_2 0xDD
#define REG_PORT_4_OUT_RATE_2 0xED
#define REG_PORT_5_OUT_RATE_2 0xFD
#define REG_PORT_1_OUT_RATE_3 0xBE
#define REG_PORT_2_OUT_RATE_3 0xCE
#define REG_PORT_3_OUT_RATE_3 0xDE
#define REG_PORT_4_OUT_RATE_3 0xEE
#define REG_PORT_5_OUT_RATE_3 0xFE
/* 88x3 specific */
#define REG_SW_INSERT_SRC_PVID 0xC2
/* PME */
#define SW_PME_OUTPUT_ENABLE BIT(1)
#define SW_PME_ACTIVE_HIGH BIT(0)
#define PORT_MAGIC_PACKET_DETECT BIT(2)
#define PORT_LINK_UP_DETECT BIT(1)
#define PORT_ENERGY_DETECT BIT(0)
/* ACL */
#define ACL_FIRST_RULE_M 0xF
#define ACL_MODE_M 0x3
#define ACL_MODE_S 4
#define ACL_MODE_DISABLE 0
#define ACL_MODE_LAYER_2 1
#define ACL_MODE_LAYER_3 2
#define ACL_MODE_LAYER_4 3
#define ACL_ENABLE_M 0x3
#define ACL_ENABLE_S 2
#define ACL_ENABLE_2_COUNT 0
#define ACL_ENABLE_2_TYPE 1
#define ACL_ENABLE_2_MAC 2
#define ACL_ENABLE_2_BOTH 3
#define ACL_ENABLE_3_IP 1
#define ACL_ENABLE_3_SRC_DST_COMP 2
#define ACL_ENABLE_4_PROTOCOL 0
#define ACL_ENABLE_4_TCP_PORT_COMP 1
#define ACL_ENABLE_4_UDP_PORT_COMP 2
#define ACL_ENABLE_4_TCP_SEQN_COMP 3
#define ACL_SRC BIT(1)
#define ACL_EQUAL BIT(0)
#define ACL_MAX_PORT 0xFFFF
#define ACL_MIN_PORT 0xFFFF
#define ACL_IP_ADDR 0xFFFFFFFF
#define ACL_TCP_SEQNUM 0xFFFFFFFF
#define ACL_RESERVED 0xF8
#define ACL_PORT_MODE_M 0x3
#define ACL_PORT_MODE_S 1
#define ACL_PORT_MODE_DISABLE 0
#define ACL_PORT_MODE_EITHER 1
#define ACL_PORT_MODE_IN_RANGE 2
#define ACL_PORT_MODE_OUT_OF_RANGE 3
#define ACL_TCP_FLAG_ENABLE BIT(0)
#define ACL_TCP_FLAG_M 0xFF
#define ACL_TCP_FLAG 0xFF
#define ACL_ETH_TYPE 0xFFFF
#define ACL_IP_M 0xFFFFFFFF
#define ACL_PRIO_MODE_M 0x3
#define ACL_PRIO_MODE_S 6
#define ACL_PRIO_MODE_DISABLE 0
#define ACL_PRIO_MODE_HIGHER 1
#define ACL_PRIO_MODE_LOWER 2
#define ACL_PRIO_MODE_REPLACE 3
#define ACL_PRIO_M 0x7
#define ACL_PRIO_S 3
#define ACL_VLAN_PRIO_REPLACE BIT(2)
#define ACL_VLAN_PRIO_M 0x7
#define ACL_VLAN_PRIO_HI_M 0x3
#define ACL_VLAN_PRIO_LO_M 0x8
#define ACL_VLAN_PRIO_S 7
#define ACL_MAP_MODE_M 0x3
#define ACL_MAP_MODE_S 5
#define ACL_MAP_MODE_DISABLE 0
#define ACL_MAP_MODE_OR 1
#define ACL_MAP_MODE_AND 2
#define ACL_MAP_MODE_REPLACE 3
#define ACL_MAP_PORT_M 0x1F
#define ACL_CNT_M (BIT(11) - 1)
#define ACL_CNT_S 5
#define ACL_MSEC_UNIT BIT(4)
#define ACL_INTR_MODE BIT(3)
#define REG_PORT_ACL_BYTE_EN_MSB 0x10
#define ACL_BYTE_EN_MSB_M 0x3F
#define REG_PORT_ACL_BYTE_EN_LSB 0x11
#define ACL_ACTION_START 0xA
#define ACL_ACTION_LEN 2
#define ACL_INTR_CNT_START 0xB
#define ACL_RULESET_START 0xC
#define ACL_RULESET_LEN 2
#define ACL_TABLE_LEN 14
#define ACL_ACTION_ENABLE 0x000C
#define ACL_MATCH_ENABLE 0x1FF0
#define ACL_RULESET_ENABLE 0x2003
#define ACL_BYTE_ENABLE ((ACL_BYTE_EN_MSB_M << 8) | 0xFF)
#define ACL_MODE_ENABLE (0x10 << 8)
#define REG_PORT_ACL_CTRL_0 0x12
#define PORT_ACL_WRITE_DONE BIT(6)
#define PORT_ACL_READ_DONE BIT(5)
#define PORT_ACL_WRITE BIT(4)
#define PORT_ACL_INDEX_M 0xF
#define REG_PORT_ACL_CTRL_1 0x13
#define PORT_ACL_FORCE_DLR_MISS BIT(0)
#define KSZ8795_ID_HI 0x0022
#define KSZ8795_ID_LO 0x1550
#define KSZ8863_ID_LO 0x1430
#define PHY_REG_LINK_MD 0x1D
#define PHY_START_CABLE_DIAG BIT(15)
#define PHY_CABLE_DIAG_RESULT_M GENMASK(14, 13)
#define PHY_CABLE_DIAG_RESULT 0x6000
#define PHY_CABLE_STAT_NORMAL 0x0000
#define PHY_CABLE_STAT_OPEN 0x2000
#define PHY_CABLE_STAT_SHORT 0x4000
#define PHY_CABLE_STAT_FAILED 0x6000
#define PHY_CABLE_10M_SHORT BIT(12)
#define PHY_CABLE_FAULT_COUNTER_M GENMASK(8, 0)
#define PHY_REG_PHY_CTRL 0x1F
#define PHY_MODE_M 0x7
#define PHY_MODE_S 8
#define PHY_STAT_REVERSED_POLARITY BIT(5)
#define PHY_STAT_MDIX BIT(4)
#define PHY_FORCE_LINK BIT(3)
#define PHY_POWER_SAVING_ENABLE BIT(2)
#define PHY_REMOTE_LOOPBACK BIT(1)
/* Vendor-specific Clause 22 PHY registers (virtualized) */
#define PHY_REG_KSZ87XX_SHORT_CABLE 0x1A
#define PHY_REG_KSZ87XX_LPF_BW 0x1B
#define PHY_REG_KSZ87XX_EQ_INIT 0x1C
/* LPF bandwidth bits [7:6]: 00 = 90MHz (default), 01 = 62MHz, 10 = 55MHz, 11 = 44MHz */
#define KSZ87XX_PHY_LPF_MASK GENMASK(7, 6)
#define KSZ87XX_PHY_LPF_90MHZ FIELD_PREP(KSZ87XX_PHY_LPF_MASK, 0)
#define KSZ87XX_PHY_LPF_62MHZ FIELD_PREP(KSZ87XX_PHY_LPF_MASK, 1)
#define KSZ87XX_PHY_LPF_55MHZ FIELD_PREP(KSZ87XX_PHY_LPF_MASK, 2)
#define KSZ87XX_PHY_LPF_44MHZ FIELD_PREP(KSZ87XX_PHY_LPF_MASK, 3)
/* Low-loss workaround DSP EQ INIT VALUE */
#define KSZ87XX_DSP_EQ_VALID_MASK GENMASK(5, 0)
#define KSZ87XX_DSP_EQ_INIT_LOW_LOSS 0x00
#define KSZ87XX_DSP_EQ_INIT_FACTORY 0x0F
/* KSZ8463 specific registers. */
#define P1MBCR 0x4C
#define P1MBSR 0x4E
#define PHY1ILR 0x50
#define PHY1IHR 0x52
#define P1ANAR 0x54
#define P1ANLPR 0x56
#define P2MBCR 0x58
#define P2MBSR 0x5A
#define PHY2ILR 0x5C
#define PHY2IHR 0x5E
#define P2ANAR 0x60
#define P2ANLPR 0x62
#define P1CR1 0x6C
#define P1CR2 0x6E
#define P1CR3 0x72
#define P1CR4 0x7E
#define P1SR 0x80
#define KSZ8463_FLUSH_TABLE_CTRL 0xAD
#define KSZ8463_FLUSH_DYN_MAC_TABLE BIT(2)
#define KSZ8463_FLUSH_STA_MAC_TABLE BIT(1)
#define KSZ8463_REG_SW_CTRL_9 0xAE
#define KSZ8463_REG_CFG_CTRL 0xD8
#define PORT_2_COPPER_MODE BIT(7)
#define PORT_1_COPPER_MODE BIT(6)
#define PORT_COPPER_MODE_S 6
#define KSZ8463_REG_SW_RESET 0x126
#define KSZ8463_GLOBAL_SOFTWARE_RESET BIT(0)
#define KSZ8463_PTP_CLK_CTRL 0x600
#define PTP_CLK_ENABLE BIT(1)
#define KSZ8463_PTP_MSG_CONF1 0x620
#define PTP_ENABLE BIT(6)
#define KSZ8463_REG_DSP_CTRL_6 0x734
#define COPPER_RECEIVE_ADJUSTMENT BIT(13)
/* Chip resource */
#define PRIO_QUEUES 4
#define KS_PRIO_IN_REG 4
#define MIB_COUNTER_NUM 0x20
/* Common names used by other drivers */
#define P_BCAST_STORM_CTRL REG_PORT_CTRL_0
#define P_PRIO_CTRL REG_PORT_CTRL_0
#define P_TAG_CTRL REG_PORT_CTRL_0
#define P_MIRROR_CTRL REG_PORT_CTRL_1
#define P_802_1P_CTRL REG_PORT_CTRL_2
#define P_PASS_ALL_CTRL REG_PORT_CTRL_12
#define P_INS_SRC_PVID_CTRL REG_PORT_CTRL_12
#define P_DROP_TAG_CTRL REG_PORT_CTRL_13
#define P_RATE_LIMIT_CTRL REG_PORT_RATE_LIMIT
#define S_UNKNOWN_DA_CTRL REG_SWITCH_CTRL_12
#define S_FORWARD_INVALID_VID_CTRL REG_FORWARD_INVALID_VID
#define S_FLUSH_TABLE_CTRL REG_SW_CTRL_0
#define S_LINK_AGING_CTRL REG_SW_CTRL_0
#define S_HUGE_PACKET_CTRL REG_SW_CTRL_1
#define S_MIRROR_CTRL REG_SW_CTRL_3
#define S_REPLACE_VID_CTRL REG_SW_CTRL_4
#define S_PASS_PAUSE_CTRL REG_SW_CTRL_10
#define S_802_1P_PRIO_CTRL REG_SW_CTRL_12
#define S_TOS_PRIO_CTRL REG_TOS_PRIO_CTRL_0
#define S_IPV6_MLD_CTRL REG_SW_CTRL_21
#define IND_ACC_TABLE(table) ((table) << 8)
/* */
#define REG_IND_EEE_GLOB2_LO 0x34
#define REG_IND_EEE_GLOB2_HI 0x35
/**
* MIB_COUNTER_VALUE 00-00000000-3FFFFFFF
* MIB_TOTAL_BYTES 00-0000000F-FFFFFFFF
* MIB_PACKET_DROPPED 00-00000000-0000FFFF
* MIB_COUNTER_VALID 00-00000020-00000000
* MIB_COUNTER_OVERFLOW 00-00000040-00000000
*/
#define MIB_COUNTER_VALUE 0x3FFFFFFF
#define KSZ8795_MIB_TOTAL_RX_0 0x100
#define KSZ8795_MIB_TOTAL_TX_0 0x101
#define KSZ8795_MIB_TOTAL_RX_1 0x104
#define KSZ8795_MIB_TOTAL_TX_1 0x105
#define KSZ8863_MIB_PACKET_DROPPED_TX_0 0x100
#define KSZ8863_MIB_PACKET_DROPPED_RX_0 0x103
#define KSZ8895_MIB_PACKET_DROPPED_RX_0 0x105
#define MIB_PACKET_DROPPED 0x0000FFFF
#define MIB_TOTAL_BYTES_H 0x0000000F
#define TAIL_TAG_OVERRIDE BIT(6)
#define TAIL_TAG_LOOKUP BIT(7)
#define FID_ENTRIES 128
#define KSZ8_DYN_MAC_ENTRIES 1024
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,96 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Microchip KSZ9477 series Header file
*
* Copyright (C) 2017-2025 Microchip Technology Inc.
*/
#ifndef __KSZ9477_H
#define __KSZ9477_H
#include <net/dsa.h>
#include "ksz_common.h"
u32 ksz9477_get_port_addr(int port, int offset);
void ksz9477_cfg_port_member(struct ksz_device *dev, int port, u8 member);
void ksz9477_flush_dyn_mac_table(struct dsa_switch *ds, int port);
void ksz9477_r_mib_cnt(struct ksz_device *dev, int port, u16 addr, u64 *cnt);
void ksz9477_r_mib_pkt(struct ksz_device *dev, int port, u16 addr,
u64 *dropped, u64 *cnt);
void ksz9477_freeze_mib(struct ksz_device *dev, int port, bool freeze);
void ksz9477_port_init_cnt(struct ksz_device *dev, int port);
int ksz9477_port_vlan_filtering(struct dsa_switch *ds, int port,
bool flag, struct netlink_ext_ack *extack);
int ksz9477_port_vlan_add(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan,
struct netlink_ext_ack *extack);
int ksz9477_port_vlan_del(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan);
int ksz9477_port_mirror_add(struct dsa_switch *ds, int port,
struct dsa_mall_mirror_tc_entry *mirror,
bool ingress, struct netlink_ext_ack *extack);
void ksz9477_port_mirror_del(struct dsa_switch *ds, int port,
struct dsa_mall_mirror_tc_entry *mirror);
int ksz9477_errata_monitor(struct ksz_device *dev, int port,
u64 tx_late_col);
int ksz9477_fdb_dump(struct dsa_switch *ds, int port,
dsa_fdb_dump_cb_t *cb, void *data);
int ksz9477_fdb_add(struct dsa_switch *ds, int port,
const unsigned char *addr, u16 vid, struct dsa_db db);
int ksz9477_fdb_del(struct dsa_switch *ds, int port,
const unsigned char *addr, u16 vid, struct dsa_db db);
int ksz9477_mdb_add(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_mdb *mdb, struct dsa_db db);
int ksz9477_mdb_del(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_mdb *mdb, struct dsa_db db);
int ksz9477_enable_stp_addr(struct ksz_device *dev);
void ksz9477_port_queue_split(struct ksz_device *dev, int port);
int ksz9477_port_acl_init(struct ksz_device *dev, int port);
void ksz9477_port_acl_free(struct ksz_device *dev, int port);
int ksz9477_cls_flower_add(struct dsa_switch *ds, int port,
struct flow_cls_offload *cls, bool ingress);
int ksz9477_cls_flower_del(struct dsa_switch *ds, int port,
struct flow_cls_offload *cls, bool ingress);
#define KSZ9477_ACL_ENTRY_SIZE 18
#define KSZ9477_ACL_MAX_ENTRIES 16
struct ksz9477_acl_entry {
u8 entry[KSZ9477_ACL_ENTRY_SIZE];
unsigned long cookie;
u32 prio;
};
struct ksz9477_acl_entries {
struct ksz9477_acl_entry entries[KSZ9477_ACL_MAX_ENTRIES];
int entries_count;
};
struct ksz9477_acl_priv {
struct ksz9477_acl_entries acles;
};
void ksz9477_acl_remove_entries(struct ksz_device *dev, int port,
struct ksz9477_acl_entries *acles,
unsigned long cookie);
int ksz9477_acl_write_list(struct ksz_device *dev, int port);
int ksz9477_sort_acl_entries(struct ksz_device *dev, int port);
void ksz9477_acl_action_rule_cfg(u8 *entry, bool force_prio, u8 prio_val);
void ksz9477_acl_processing_rule_set_action(u8 *entry, u8 action_idx);
void ksz9477_acl_match_process_l2(struct ksz_device *dev, int port,
u16 ethtype, u8 *src_mac, u8 *dst_mac,
unsigned long cookie, u32 prio);
void ksz9477_phylink_mac_link_up(struct phylink_config *config,
struct phy_device *phydev,
unsigned int mode,
phy_interface_t interface,
int speed, int duplex, bool tx_pause,
bool rx_pause);
extern const struct ksz_dev_ops ksz9477_dev_ops;
extern const struct phylink_mac_ops ksz9477_phylink_mac_ops;
extern const struct dsa_switch_ops ksz9477_switch_ops;
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,149 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Microchip KSZ9477 series register access through I2C
*
* Copyright (C) 2018-2024 Microchip Technology Inc.
*/
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include "ksz_common.h"
KSZ_REGMAP_TABLE(ksz9477, not_used, 16, 0, 0);
static int ksz9477_i2c_probe(struct i2c_client *i2c)
{
const struct ksz_chip_data *chip;
struct device *ddev = &i2c->dev;
struct regmap_config rc;
struct ksz_device *dev;
int i, ret;
chip = device_get_match_data(ddev);
if (!chip)
return -EINVAL;
dev = ksz_switch_alloc(&i2c->dev, chip, i2c);
if (!dev)
return -ENOMEM;
/* Save chip id to do special initialization when probing. */
dev->chip_id = chip->chip_id;
for (i = 0; i < __KSZ_NUM_REGMAPS; i++) {
rc = ksz9477_regmap_config[i];
rc.lock_arg = &dev->regmap_mutex;
dev->regmap[i] = devm_regmap_init_i2c(i2c, &rc);
if (IS_ERR(dev->regmap[i])) {
return dev_err_probe(&i2c->dev, PTR_ERR(dev->regmap[i]),
"Failed to initialize regmap%i\n",
ksz9477_regmap_config[i].val_bits);
}
}
if (i2c->dev.platform_data)
dev->pdata = i2c->dev.platform_data;
dev->irq = i2c->irq;
ret = ksz_switch_register(dev);
/* Main DSA driver may not be started yet. */
if (ret)
return ret;
i2c_set_clientdata(i2c, dev);
return 0;
}
static void ksz9477_i2c_remove(struct i2c_client *i2c)
{
struct ksz_device *dev = i2c_get_clientdata(i2c);
if (dev)
ksz_switch_remove(dev);
}
static void ksz9477_i2c_shutdown(struct i2c_client *i2c)
{
struct ksz_device *dev = i2c_get_clientdata(i2c);
if (!dev)
return;
ksz_switch_shutdown(dev);
i2c_set_clientdata(i2c, NULL);
}
static const struct i2c_device_id ksz9477_i2c_id[] = {
{ .name = "ksz9477-switch" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ksz9477_i2c_id);
static const struct of_device_id ksz9477_dt_ids[] = {
{
.compatible = "microchip,ksz9477",
.data = &ksz_switch_chips[KSZ9477]
},
{
.compatible = "microchip,ksz9896",
.data = &ksz_switch_chips[KSZ9896]
},
{
.compatible = "microchip,ksz9897",
.data = &ksz_switch_chips[KSZ9897]
},
{
.compatible = "microchip,ksz9893",
.data = &ksz_switch_chips[KSZ9893]
},
{
.compatible = "microchip,ksz9563",
.data = &ksz_switch_chips[KSZ9563]
},
{
.compatible = "microchip,ksz8563",
.data = &ksz_switch_chips[KSZ8563]
},
{
.compatible = "microchip,ksz8567",
.data = &ksz_switch_chips[KSZ8567]
},
{
.compatible = "microchip,ksz9567",
.data = &ksz_switch_chips[KSZ9567]
},
{
.compatible = "microchip,lan9646",
.data = &ksz_switch_chips[LAN9646]
},
{},
};
MODULE_DEVICE_TABLE(of, ksz9477_dt_ids);
static DEFINE_SIMPLE_DEV_PM_OPS(ksz_i2c_pm_ops,
ksz_switch_suspend, ksz_switch_resume);
static struct i2c_driver ksz9477_i2c_driver = {
.driver = {
.name = "ksz9477-switch",
.of_match_table = ksz9477_dt_ids,
.pm = &ksz_i2c_pm_ops,
},
.probe = ksz9477_i2c_probe,
.remove = ksz9477_i2c_remove,
.shutdown = ksz9477_i2c_shutdown,
.id_table = ksz9477_i2c_id,
};
module_i2c_driver(ksz9477_i2c_driver);
MODULE_AUTHOR("Tristram Ha <Tristram.Ha@microchip.com>");
MODULE_DESCRIPTION("Microchip KSZ9477 Series Switch I2C access Driver");
MODULE_LICENSE("GPL v2");
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,284 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2023 Pengutronix, Oleksij Rempel <kernel@pengutronix.de>
#include "ksz9477.h"
#include "ksz9477_reg.h"
#include "ksz_common.h"
#define ETHER_TYPE_FULL_MASK cpu_to_be16(~0)
#define KSZ9477_MAX_TC 7
/**
* ksz9477_flower_parse_key_l2 - Parse Layer 2 key from flow rule and configure
* ACL entries accordingly.
* @dev: Pointer to the ksz_device.
* @port: Port number.
* @extack: Pointer to the netlink_ext_ack.
* @rule: Pointer to the flow_rule.
* @cookie: The cookie to associate with the entry.
* @prio: The priority of the entry.
*
* This function parses the Layer 2 key from the flow rule and configures
* the corresponding ACL entries. It checks for unsupported offloads and
* available entries before proceeding with the configuration.
*
* Returns: 0 on success or a negative error code on failure.
*/
static int ksz9477_flower_parse_key_l2(struct ksz_device *dev, int port,
struct netlink_ext_ack *extack,
struct flow_rule *rule,
unsigned long cookie, u32 prio)
{
struct ksz9477_acl_priv *acl = dev->ports[port].acl_priv;
struct flow_match_eth_addrs ematch;
struct ksz9477_acl_entries *acles;
int required_entries;
u8 *src_mac = NULL;
u8 *dst_mac = NULL;
u16 ethtype = 0;
if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
struct flow_match_basic match;
flow_rule_match_basic(rule, &match);
if (match.key->n_proto) {
if (match.mask->n_proto != ETHER_TYPE_FULL_MASK) {
NL_SET_ERR_MSG_MOD(extack,
"ethernet type mask must be a full mask");
return -EINVAL;
}
ethtype = be16_to_cpu(match.key->n_proto);
}
}
if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
flow_rule_match_eth_addrs(rule, &ematch);
if (!is_zero_ether_addr(ematch.key->src)) {
if (!is_broadcast_ether_addr(ematch.mask->src))
goto not_full_mask_err;
src_mac = ematch.key->src;
}
if (!is_zero_ether_addr(ematch.key->dst)) {
if (!is_broadcast_ether_addr(ematch.mask->dst))
goto not_full_mask_err;
dst_mac = ematch.key->dst;
}
}
acles = &acl->acles;
/* ACL supports only one MAC per entry */
required_entries = src_mac && dst_mac ? 2 : 1;
/* Check if there are enough available entries */
if (acles->entries_count + required_entries > KSZ9477_ACL_MAX_ENTRIES) {
NL_SET_ERR_MSG_MOD(extack, "ACL entry limit reached");
return -EOPNOTSUPP;
}
ksz9477_acl_match_process_l2(dev, port, ethtype, src_mac, dst_mac,
cookie, prio);
return 0;
not_full_mask_err:
NL_SET_ERR_MSG_MOD(extack, "MAC address mask must be a full mask");
return -EOPNOTSUPP;
}
/**
* ksz9477_flower_parse_key - Parse flow rule keys for a specified port on a
* ksz_device.
* @dev: The ksz_device instance.
* @port: The port number to parse the flow rule keys for.
* @extack: The netlink extended ACK for reporting errors.
* @rule: The flow_rule to parse.
* @cookie: The cookie to associate with the entry.
* @prio: The priority of the entry.
*
* This function checks if the used keys in the flow rule are supported by
* the device and parses the L2 keys if they match. If unsupported keys are
* used, an error message is set in the extended ACK.
*
* Returns: 0 on success or a negative error code on failure.
*/
static int ksz9477_flower_parse_key(struct ksz_device *dev, int port,
struct netlink_ext_ack *extack,
struct flow_rule *rule,
unsigned long cookie, u32 prio)
{
struct flow_dissector *dissector = rule->match.dissector;
int ret;
if (dissector->used_keys &
~(BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) |
BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL))) {
NL_SET_ERR_MSG_MOD(extack,
"Unsupported keys used");
return -EOPNOTSUPP;
}
if (flow_rule_match_has_control_flags(rule, extack))
return -EOPNOTSUPP;
if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC) ||
flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
ret = ksz9477_flower_parse_key_l2(dev, port, extack, rule,
cookie, prio);
if (ret)
return ret;
}
return 0;
}
/**
* ksz9477_flower_parse_action - Parse flow rule actions for a specified port
* on a ksz_device.
* @dev: The ksz_device instance.
* @port: The port number to parse the flow rule actions for.
* @extack: The netlink extended ACK for reporting errors.
* @cls: The flow_cls_offload instance containing the flow rule.
* @entry_idx: The index of the ACL entry to store the action.
*
* This function checks if the actions in the flow rule are supported by
* the device. Currently, only actions that change priorities are supported.
* If unsupported actions are encountered, an error message is set in the
* extended ACK.
*
* Returns: 0 on success or a negative error code on failure.
*/
static int ksz9477_flower_parse_action(struct ksz_device *dev, int port,
struct netlink_ext_ack *extack,
struct flow_cls_offload *cls,
int entry_idx)
{
struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
struct ksz9477_acl_priv *acl = dev->ports[port].acl_priv;
const struct flow_action_entry *act;
struct ksz9477_acl_entry *entry;
bool prio_force = false;
u8 prio_val = 0;
int i;
if (TC_H_MIN(cls->classid)) {
NL_SET_ERR_MSG_MOD(extack, "hw_tc is not supported. Use: action skbedit prio");
return -EOPNOTSUPP;
}
flow_action_for_each(i, act, &rule->action) {
switch (act->id) {
case FLOW_ACTION_PRIORITY:
if (act->priority > KSZ9477_MAX_TC) {
NL_SET_ERR_MSG_MOD(extack, "Priority value is too high");
return -EOPNOTSUPP;
}
prio_force = true;
prio_val = act->priority;
break;
default:
NL_SET_ERR_MSG_MOD(extack, "action not supported");
return -EOPNOTSUPP;
}
}
/* pick entry to store action */
entry = &acl->acles.entries[entry_idx];
ksz9477_acl_action_rule_cfg(entry->entry, prio_force, prio_val);
ksz9477_acl_processing_rule_set_action(entry->entry, entry_idx);
return 0;
}
/**
* ksz9477_cls_flower_add - Add a flow classification rule for a specified port
* on a ksz_device.
* @ds: The DSA switch instance.
* @port: The port number to add the flow classification rule to.
* @cls: The flow_cls_offload instance containing the flow rule.
* @ingress: A flag indicating if the rule is applied on the ingress path.
*
* This function adds a flow classification rule for a specified port on a
* ksz_device. It checks if the ACL offloading is supported and parses the flow
* keys and actions. If the ACL is not supported, it returns an error. If there
* are unprocessed entries, it parses the action for the rule.
*
* Returns: 0 on success or a negative error code on failure.
*/
int ksz9477_cls_flower_add(struct dsa_switch *ds, int port,
struct flow_cls_offload *cls, bool ingress)
{
struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
struct netlink_ext_ack *extack = cls->common.extack;
struct ksz_device *dev = ds->priv;
struct ksz9477_acl_priv *acl;
int action_entry_idx;
int ret;
acl = dev->ports[port].acl_priv;
if (!acl) {
NL_SET_ERR_MSG_MOD(extack, "ACL offloading is not supported");
return -EOPNOTSUPP;
}
/* A complex rule set can take multiple entries. Use first entry
* to store the action.
*/
action_entry_idx = acl->acles.entries_count;
ret = ksz9477_flower_parse_key(dev, port, extack, rule, cls->cookie,
cls->common.prio);
if (ret)
return ret;
ret = ksz9477_flower_parse_action(dev, port, extack, cls,
action_entry_idx);
if (ret)
return ret;
ret = ksz9477_sort_acl_entries(dev, port);
if (ret)
return ret;
return ksz9477_acl_write_list(dev, port);
}
/**
* ksz9477_cls_flower_del - Remove a flow classification rule for a specified
* port on a ksz_device.
* @ds: The DSA switch instance.
* @port: The port number to remove the flow classification rule from.
* @cls: The flow_cls_offload instance containing the flow rule.
* @ingress: A flag indicating if the rule is applied on the ingress path.
*
* This function removes a flow classification rule for a specified port on a
* ksz_device. It checks if the ACL is initialized, and if not, returns an
* error. If the ACL is initialized, it removes entries with the specified
* cookie and rewrites the ACL list.
*
* Returns: 0 on success or a negative error code on failure.
*/
int ksz9477_cls_flower_del(struct dsa_switch *ds, int port,
struct flow_cls_offload *cls, bool ingress)
{
unsigned long cookie = cls->cookie;
struct ksz_device *dev = ds->priv;
struct ksz9477_acl_priv *acl;
acl = dev->ports[port].acl_priv;
if (!acl)
return -EOPNOTSUPP;
ksz9477_acl_remove_entries(dev, port, &acl->acles, cookie);
return ksz9477_acl_write_list(dev, port);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,980 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Microchip switch driver common header
*
* Copyright (C) 2017-2025 Microchip Technology Inc.
*/
#ifndef __KSZ_COMMON_H
#define __KSZ_COMMON_H
#include <linux/etherdevice.h>
#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/pcs/pcs-xpcs.h>
#include <linux/phy.h>
#include <linux/regmap.h>
#include <net/dsa.h>
#include <linux/irq.h>
#include <linux/platform_data/microchip-ksz.h>
#include "ksz_ptp.h"
#define KSZ_MAX_NUM_PORTS 8
/* all KSZ switches count ports from 1 */
#define KSZ_PORT_1 0
#define KSZ_PORT_2 1
#define KSZ_PORT_4 3
struct ksz_device;
struct ksz_port;
struct phylink_mac_ops;
enum ksz_regmap_width {
KSZ_REGMAP_8,
KSZ_REGMAP_16,
KSZ_REGMAP_32,
__KSZ_NUM_REGMAPS,
};
struct vlan_table {
u32 table[3];
};
struct ksz_port_mib {
struct mutex cnt_mutex; /* structure access */
u8 cnt_ptr;
u64 *counters;
struct rtnl_link_stats64 stats64;
struct ethtool_pause_stats pause_stats;
struct spinlock stats64_lock;
};
struct ksz_mib_names {
int index;
char string[ETH_GSTRING_LEN];
};
struct ksz_chip_data {
u32 chip_id;
const char *dev_name;
int num_vlans;
int num_alus;
int num_statics;
int cpu_ports;
int port_cnt;
u8 port_nirqs;
u8 num_tx_queues;
u8 num_ipms; /* number of Internal Priority Maps */
bool tc_cbs_supported;
/**
* @phy_side_mdio_supported: Indicates if the chip supports an additional
* side MDIO channel for accessing integrated PHYs.
*/
bool phy_side_mdio_supported;
const struct ksz_dev_ops *ops;
const struct dsa_switch_ops *switch_ops;
const struct phylink_mac_ops *phylink_mac_ops;
bool phy_errata_9477;
bool ksz87xx_eee_link_erratum;
const struct ksz_mib_names *mib_names;
int mib_cnt;
u8 reg_mib_cnt;
const u16 *regs;
const u32 *masks;
const u8 *shifts;
const u8 *xmii_ctrl0;
const u8 *xmii_ctrl1;
int stp_ctrl_reg;
int broadcast_ctrl_reg;
int multicast_ctrl_reg;
int start_ctrl_reg;
bool supports_mii[KSZ_MAX_NUM_PORTS];
bool supports_rmii[KSZ_MAX_NUM_PORTS];
bool supports_rgmii[KSZ_MAX_NUM_PORTS];
bool internal_phy[KSZ_MAX_NUM_PORTS];
bool gbit_capable[KSZ_MAX_NUM_PORTS];
bool ptp_capable;
u8 sgmii_port;
const struct regmap_access_table *wr_table;
const struct regmap_access_table *rd_table;
};
struct ksz_irq {
u16 masked;
u16 reg_mask;
u16 reg_status;
struct irq_domain *domain;
int nirqs;
int irq_num;
char name[16];
struct ksz_device *dev;
u16 irq0_offset;
};
struct ksz_ptp_irq {
struct ksz_port *port;
u16 ts_reg;
bool ts_en;
char name[16];
int num;
};
struct ksz_switch_macaddr {
unsigned char addr[ETH_ALEN];
refcount_t refcount;
};
struct ksz_port {
bool remove_tag; /* Remove Tag flag set, for ksz8795 only */
bool learning;
bool isolated;
int stp_state;
int speed;
int duplex;
bool link;
u32 fiber:1; /* port is fiber */
u32 force:1;
u32 read:1; /* read MIB counters in background */
u32 freeze:1; /* MIB counter freeze is enabled */
u32 sgmii_adv_write:1;
struct ksz_port_mib mib;
phy_interface_t interface;
u32 rgmii_tx_val;
u32 rgmii_rx_val;
struct ksz_device *ksz_dev;
void *acl_priv;
struct ksz_irq pirq;
u8 num;
struct phylink_pcs *pcs;
#if IS_ENABLED(CONFIG_NET_DSA_MICROCHIP_KSZ_PTP)
struct kernel_hwtstamp_config tstamp_config;
bool hwts_tx_en;
bool hwts_rx_en;
struct ksz_irq ptpirq;
struct ksz_ptp_irq ptpmsg_irq[3];
ktime_t tstamp_msg;
struct completion tstamp_msg_comp;
#endif
bool manual_flow;
};
struct ksz_device {
struct dsa_switch *ds;
struct ksz_platform_data *pdata;
const struct ksz_chip_data *info;
struct mutex dev_mutex; /* device access */
struct mutex regmap_mutex; /* regmap access */
struct mutex alu_mutex; /* ALU access */
struct mutex vlan_mutex; /* vlan access */
const struct ksz_dev_ops *dev_ops;
struct device *dev;
struct regmap *regmap[__KSZ_NUM_REGMAPS];
void *priv;
int irq;
struct gpio_desc *reset_gpio; /* Optional reset GPIO */
/* chip specific data */
u32 chip_id;
u8 chip_rev;
int cpu_port; /* port connected to CPU */
int phy_port_cnt;
phy_interface_t compat_interface;
bool synclko_125;
bool synclko_disable;
bool wakeup_source;
bool pme_active_high;
struct vlan_table *vlan_cache;
struct ksz_port *ports;
struct delayed_work mib_read;
unsigned long mib_read_interval;
u16 mirror_rx;
u16 mirror_tx;
u16 port_mask;
struct mutex lock_irq; /* IRQ Access */
struct ksz_irq girq;
struct ksz_ptp_data ptp_data;
struct ksz_switch_macaddr *switch_macaddr;
struct net_device *hsr_dev; /* HSR */
u8 hsr_ports;
/**
* @phy_addr_map: Array mapping switch ports to their corresponding PHY
* addresses.
*/
u8 phy_addr_map[KSZ_MAX_NUM_PORTS];
/**
* @parent_mdio_bus: Pointer to the external MDIO bus controller.
*
* This points to an external MDIO bus controller that is used to access
* the PHYs integrated within the switch. Unlike an integrated MDIO
* bus, this external controller provides a direct path for managing
* the switch’s internal PHYs, bypassing the main SPI interface.
*/
struct mii_bus *parent_mdio_bus;
/* KSZ87xx low-loss tuning state */
u8 lpf_bw; /* KSZ87XX_PHY_LPF_* */
u8 eq_init; /* DSP EQ initial value */
};
/* List of supported models */
enum ksz_model {
KSZ8463,
KSZ8563,
KSZ8567,
KSZ8795,
KSZ8794,
KSZ8765,
KSZ88X3,
KSZ8864,
KSZ8895,
KSZ9477,
KSZ9896,
KSZ9897,
KSZ9893,
KSZ9563,
KSZ9567,
LAN9370,
LAN9371,
LAN9372,
LAN9373,
LAN9374,
LAN9646,
};
enum ksz_regs {
REG_SW_MAC_ADDR,
REG_IND_CTRL_0,
REG_IND_DATA_8,
REG_IND_DATA_CHECK,
REG_IND_DATA_HI,
REG_IND_DATA_LO,
REG_IND_MIB_CHECK,
REG_IND_BYTE,
P_FORCE_CTRL,
P_LINK_STATUS,
P_LOCAL_CTRL,
P_NEG_RESTART_CTRL,
P_REMOTE_STATUS,
P_SPEED_STATUS,
S_TAIL_TAG_CTRL,
P_STP_CTRL,
S_START_CTRL,
S_BROADCAST_CTRL,
S_MULTICAST_CTRL,
P_XMII_CTRL_0,
P_XMII_CTRL_1,
REG_SW_PME_CTRL,
REG_PORT_PME_STATUS,
REG_PORT_PME_CTRL,
PTP_CLK_CTRL,
PTP_RTC_NANOSEC,
PTP_RTC_SEC,
PTP_RTC_SUB_NANOSEC,
PTP_SUBNANOSEC_RATE,
PTP_MSG_CONF1,
};
enum ksz_masks {
PORT_802_1P_REMAPPING,
SW_TAIL_TAG_ENABLE,
MIB_COUNTER_OVERFLOW,
MIB_COUNTER_VALID,
VLAN_TABLE_FID,
VLAN_TABLE_MEMBERSHIP,
VLAN_TABLE_VALID,
STATIC_MAC_TABLE_VALID,
STATIC_MAC_TABLE_USE_FID,
STATIC_MAC_TABLE_FID,
STATIC_MAC_TABLE_OVERRIDE,
STATIC_MAC_TABLE_FWD_PORTS,
DYNAMIC_MAC_TABLE_ENTRIES_H,
DYNAMIC_MAC_TABLE_MAC_EMPTY,
DYNAMIC_MAC_TABLE_NOT_READY,
DYNAMIC_MAC_TABLE_ENTRIES,
DYNAMIC_MAC_TABLE_FID,
DYNAMIC_MAC_TABLE_SRC_PORT,
DYNAMIC_MAC_TABLE_TIMESTAMP,
ALU_STAT_WRITE,
ALU_STAT_READ,
ALU_STAT_DIRECT,
ALU_RESV_MCAST_ADDR,
P_MII_TX_FLOW_CTRL,
P_MII_RX_FLOW_CTRL,
};
enum ksz_shifts {
VLAN_TABLE_MEMBERSHIP_S,
VLAN_TABLE,
STATIC_MAC_FWD_PORTS,
STATIC_MAC_FID,
DYNAMIC_MAC_ENTRIES_H,
DYNAMIC_MAC_ENTRIES,
DYNAMIC_MAC_FID,
DYNAMIC_MAC_TIMESTAMP,
DYNAMIC_MAC_SRC_PORT,
ALU_STAT_INDEX,
};
enum ksz_xmii_ctrl0 {
P_MII_100MBIT,
P_MII_10MBIT,
P_MII_FULL_DUPLEX,
P_MII_HALF_DUPLEX,
};
enum ksz_xmii_ctrl1 {
P_RGMII_SEL,
P_RMII_SEL,
P_GMII_SEL,
P_MII_SEL,
P_GMII_1GBIT,
P_GMII_NOT_1GBIT,
};
struct alu_struct {
/* entry 1 */
u8 is_static:1;
u8 is_src_filter:1;
u8 is_dst_filter:1;
u8 prio_age:3;
u32 _reserv_0_1:23;
u8 mstp:3;
/* entry 2 */
u8 is_override:1;
u8 is_use_fid:1;
u32 _reserv_1_1:23;
u8 port_forward:7;
/* entry 3 & 4*/
u32 _reserv_2_1:9;
u8 fid:7;
u8 mac[ETH_ALEN];
};
struct ksz_dev_ops {
u32 (*get_port_addr)(int port, int offset);
void (*cfg_port_member)(struct ksz_device *dev, int port, u8 member);
void (*r_mib_cnt)(struct ksz_device *dev, int port, u16 addr,
u64 *cnt);
void (*r_mib_pkt)(struct ksz_device *dev, int port, u16 addr,
u64 *dropped, u64 *cnt);
void (*r_mib_stat64)(struct ksz_device *dev, int port);
int (*pme_write8)(struct ksz_device *dev, u32 reg, u8 value);
int (*pme_pread8)(struct ksz_device *dev, int port, int offset,
u8 *data);
int (*pme_pwrite8)(struct ksz_device *dev, int port, int offset,
u8 data);
void (*freeze_mib)(struct ksz_device *dev, int port, bool freeze);
void (*port_init_cnt)(struct ksz_device *dev, int port);
int (*tc_cbs_set_cinc)(struct ksz_device *dev, int port, u32 val);
int (*init)(struct ksz_device *dev);
};
struct ksz_device *ksz_switch_alloc(struct device *base,
const struct ksz_chip_data *chip,
void *priv);
int ksz_switch_register(struct ksz_device *dev);
void ksz_switch_remove(struct ksz_device *dev);
int ksz_switch_suspend(struct device *dev);
int ksz_switch_resume(struct device *dev);
void ksz_teardown(struct dsa_switch *ds);
void ksz_init_mib_timer(struct ksz_device *dev);
bool ksz_is_port_mac_global_usable(struct dsa_switch *ds, int port);
void ksz_r_mib_stats64(struct ksz_device *dev, int port);
void ksz88xx_r_mib_stats64(struct ksz_device *dev, int port);
void ksz_port_stp_state_set(struct dsa_switch *ds, int port, u8 state);
bool ksz_get_gbit(struct ksz_device *dev, int port);
phy_interface_t ksz_get_xmii(struct ksz_device *dev, int port, bool gbit);
extern const struct ksz_chip_data ksz_switch_chips[];
int ksz_switch_macaddr_get(struct dsa_switch *ds, int port,
struct netlink_ext_ack *extack);
void ksz_switch_macaddr_put(struct dsa_switch *ds);
void ksz_switch_shutdown(struct ksz_device *dev);
int ksz_handle_wake_reason(struct ksz_device *dev, int port);
int ksz_sset_count(struct dsa_switch *ds, int port, int sset);
void ksz_get_ethtool_stats(struct dsa_switch *ds, int port,
uint64_t *buf);
void ksz_get_stats64(struct dsa_switch *ds, int port,
struct rtnl_link_stats64 *s);
void ksz_get_pause_stats(struct dsa_switch *ds, int port,
struct ethtool_pause_stats *pause_stats);
void ksz_get_strings(struct dsa_switch *ds, int port,
u32 stringset, uint8_t *buf);
int ksz_port_bridge_join(struct dsa_switch *ds, int port,
struct dsa_bridge bridge,
bool *tx_fwd_offload,
struct netlink_ext_ack *extack);
void ksz_port_bridge_leave(struct dsa_switch *ds, int port,
struct dsa_bridge bridge);
int ksz_port_pre_bridge_flags(struct dsa_switch *ds, int port,
struct switchdev_brport_flags flags,
struct netlink_ext_ack *extack);
int ksz_port_bridge_flags(struct dsa_switch *ds, int port,
struct switchdev_brport_flags flags,
struct netlink_ext_ack *extack);
void ksz_phylink_get_caps(struct dsa_switch *ds, int port,
struct phylink_config *config);
void ksz_phylink_mac_disable_tx_lpi(struct phylink_config *config);
int ksz_phylink_mac_enable_tx_lpi(struct phylink_config *config,
u32 timer, bool tx_clock_stop);
void ksz_set_xmii(struct ksz_device *dev, int port, phy_interface_t interface);
bool ksz_phylink_need_config(struct phylink_config *config, unsigned int mode);
void ksz_phylink_mac_config(struct phylink_config *config,
unsigned int mode,
const struct phylink_link_state *state);
void ksz_phylink_mac_link_down(struct phylink_config *config,
unsigned int mode,
phy_interface_t interface);
int ksz_max_mtu(struct dsa_switch *ds, int port);
int ksz_set_mac_eee(struct dsa_switch *ds, int port,
struct ethtool_keee *e);
int ksz_setup_tc(struct dsa_switch *ds, int port,
enum tc_setup_type type, void *type_data);
void ksz_get_wol(struct dsa_switch *ds, int port,
struct ethtool_wolinfo *wol);
int ksz_set_wol(struct dsa_switch *ds, int port,
struct ethtool_wolinfo *wol);
int ksz_port_set_mac_address(struct dsa_switch *ds, int port,
const unsigned char *addr);
int ksz_suspend(struct dsa_switch *ds);
int ksz_resume(struct dsa_switch *ds);
int ksz_parse_dt_phy_config(struct ksz_device *dev, struct mii_bus *bus,
struct device_node *mdio_np);
int ksz_sw_mdio_read(struct mii_bus *bus, int addr, int regnum);
int ksz_sw_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val);
int ksz_parent_mdio_read(struct mii_bus *bus, int addr, int regnum);
int ksz_parent_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val);
int ksz_mdio_register(struct ksz_device *dev);
int ksz_pirq_setup(struct ksz_device *dev, u8 p);
int ksz_girq_setup(struct ksz_device *dev);
void ksz_irq_free(struct ksz_irq *kirq);
int ksz_parse_drive_strength(struct ksz_device *dev);
int ksz9477_set_default_prio_queue_mapping(struct ksz_device *dev, int port);
/* Common register access functions */
static inline struct regmap *ksz_regmap_8(struct ksz_device *dev)
{
return dev->regmap[KSZ_REGMAP_8];
}
static inline struct regmap *ksz_regmap_16(struct ksz_device *dev)
{
return dev->regmap[KSZ_REGMAP_16];
}
static inline struct regmap *ksz_regmap_32(struct ksz_device *dev)
{
return dev->regmap[KSZ_REGMAP_32];
}
static inline bool ksz_is_ksz8463(struct ksz_device *dev)
{
return dev->chip_id == KSZ8463_CHIP_ID;
}
static inline int ksz_read8(struct ksz_device *dev, u32 reg, u8 *val)
{
unsigned int value;
int ret = regmap_read(ksz_regmap_8(dev), reg, &value);
if (ret)
dev_err(dev->dev, "can't read 8bit reg: 0x%x %pe\n", reg,
ERR_PTR(ret));
*val = value;
return ret;
}
static inline int ksz_read16(struct ksz_device *dev, u32 reg, u16 *val)
{
unsigned int value;
int ret = regmap_read(ksz_regmap_16(dev), reg, &value);
if (ret)
dev_err(dev->dev, "can't read 16bit reg: 0x%x %pe\n", reg,
ERR_PTR(ret));
*val = value;
return ret;
}
static inline int ksz_read32(struct ksz_device *dev, u32 reg, u32 *val)
{
unsigned int value;
int ret = regmap_read(ksz_regmap_32(dev), reg, &value);
if (ret)
dev_err(dev->dev, "can't read 32bit reg: 0x%x %pe\n", reg,
ERR_PTR(ret));
*val = value;
return ret;
}
static inline int ksz_read64(struct ksz_device *dev, u32 reg, u64 *val)
{
u32 value[2];
int ret;
ret = regmap_bulk_read(ksz_regmap_32(dev), reg, value, 2);
if (ret)
dev_err(dev->dev, "can't read 64bit reg: 0x%x %pe\n", reg,
ERR_PTR(ret));
else
*val = (u64)value[0] << 32 | value[1];
return ret;
}
static inline int ksz_write8(struct ksz_device *dev, u32 reg, u8 value)
{
int ret;
ret = regmap_write(ksz_regmap_8(dev), reg, value);
if (ret)
dev_err(dev->dev, "can't write 8bit reg: 0x%x %pe\n", reg,
ERR_PTR(ret));
return ret;
}
static inline int ksz_write16(struct ksz_device *dev, u32 reg, u16 value)
{
int ret;
ret = regmap_write(ksz_regmap_16(dev), reg, value);
if (ret)
dev_err(dev->dev, "can't write 16bit reg: 0x%x %pe\n", reg,
ERR_PTR(ret));
return ret;
}
static inline int ksz_write32(struct ksz_device *dev, u32 reg, u32 value)
{
int ret;
ret = regmap_write(ksz_regmap_32(dev), reg, value);
if (ret)
dev_err(dev->dev, "can't write 32bit reg: 0x%x %pe\n", reg,
ERR_PTR(ret));
return ret;
}
static inline int ksz_rmw16(struct ksz_device *dev, u32 reg, u16 mask,
u16 value)
{
int ret;
ret = regmap_update_bits(ksz_regmap_16(dev), reg, mask, value);
if (ret)
dev_err(dev->dev, "can't rmw 16bit reg 0x%x: %pe\n", reg,
ERR_PTR(ret));
return ret;
}
static inline int ksz_rmw32(struct ksz_device *dev, u32 reg, u32 mask,
u32 value)
{
int ret;
ret = regmap_update_bits(ksz_regmap_32(dev), reg, mask, value);
if (ret)
dev_err(dev->dev, "can't rmw 32bit reg 0x%x: %pe\n", reg,
ERR_PTR(ret));
return ret;
}
static inline int ksz_write64(struct ksz_device *dev, u32 reg, u64 value)
{
u32 val[2];
/* Ick! ToDo: Add 64bit R/W to regmap on 32bit systems */
value = swab64(value);
val[0] = swab32(value & 0xffffffffULL);
val[1] = swab32(value >> 32ULL);
return regmap_bulk_write(ksz_regmap_32(dev), reg, val, 2);
}
static inline int ksz_rmw8(struct ksz_device *dev, int offset, u8 mask, u8 val)
{
int ret;
ret = regmap_update_bits(ksz_regmap_8(dev), offset, mask, val);
if (ret)
dev_err(dev->dev, "can't rmw 8bit reg 0x%x: %pe\n", offset,
ERR_PTR(ret));
return ret;
}
static inline int ksz_pread8(struct ksz_device *dev, int port, int offset,
u8 *data)
{
return ksz_read8(dev, dev->dev_ops->get_port_addr(port, offset), data);
}
static inline int ksz_pread16(struct ksz_device *dev, int port, int offset,
u16 *data)
{
return ksz_read16(dev, dev->dev_ops->get_port_addr(port, offset), data);
}
static inline int ksz_pread32(struct ksz_device *dev, int port, int offset,
u32 *data)
{
return ksz_read32(dev, dev->dev_ops->get_port_addr(port, offset), data);
}
static inline int ksz_pwrite8(struct ksz_device *dev, int port, int offset,
u8 data)
{
return ksz_write8(dev, dev->dev_ops->get_port_addr(port, offset), data);
}
static inline int ksz_pwrite16(struct ksz_device *dev, int port, int offset,
u16 data)
{
return ksz_write16(dev, dev->dev_ops->get_port_addr(port, offset),
data);
}
static inline int ksz_pwrite32(struct ksz_device *dev, int port, int offset,
u32 data)
{
return ksz_write32(dev, dev->dev_ops->get_port_addr(port, offset),
data);
}
static inline int ksz_prmw8(struct ksz_device *dev, int port, int offset,
u8 mask, u8 val)
{
return ksz_rmw8(dev, dev->dev_ops->get_port_addr(port, offset),
mask, val);
}
static inline int ksz_prmw32(struct ksz_device *dev, int port, int offset,
u32 mask, u32 val)
{
return ksz_rmw32(dev, dev->dev_ops->get_port_addr(port, offset),
mask, val);
}
static inline void ksz_regmap_lock(void *__mtx)
{
struct mutex *mtx = __mtx;
mutex_lock(mtx);
}
static inline void ksz_regmap_unlock(void *__mtx)
{
struct mutex *mtx = __mtx;
mutex_unlock(mtx);
}
static inline bool ksz_is_ksz87xx(struct ksz_device *dev)
{
return dev->chip_id == KSZ8795_CHIP_ID ||
dev->chip_id == KSZ8794_CHIP_ID ||
dev->chip_id == KSZ8765_CHIP_ID;
}
static inline bool ksz_is_ksz88x3(struct ksz_device *dev)
{
return dev->chip_id == KSZ88X3_CHIP_ID;
}
static inline bool ksz_is_8895_family(struct ksz_device *dev)
{
return dev->chip_id == KSZ8895_CHIP_ID ||
dev->chip_id == KSZ8864_CHIP_ID;
}
static inline bool is_ksz8(struct ksz_device *dev)
{
return ksz_is_ksz87xx(dev) || ksz_is_ksz88x3(dev) ||
ksz_is_8895_family(dev) || ksz_is_ksz8463(dev);
}
static inline bool is_ksz88xx(struct ksz_device *dev)
{
return ksz_is_ksz88x3(dev) || ksz_is_8895_family(dev) ||
ksz_is_ksz8463(dev);
}
static inline bool is_ksz9477(struct ksz_device *dev)
{
return dev->chip_id == KSZ9477_CHIP_ID;
}
static inline int is_lan937x(struct ksz_device *dev)
{
return dev->chip_id == LAN9370_CHIP_ID ||
dev->chip_id == LAN9371_CHIP_ID ||
dev->chip_id == LAN9372_CHIP_ID ||
dev->chip_id == LAN9373_CHIP_ID ||
dev->chip_id == LAN9374_CHIP_ID;
}
static inline bool is_lan937x_tx_phy(struct ksz_device *dev, int port)
{
return (dev->chip_id == LAN9371_CHIP_ID ||
dev->chip_id == LAN9372_CHIP_ID) && port == KSZ_PORT_4;
}
static inline int ksz_get_sgmii_port(struct ksz_device *dev)
{
return dev->info->sgmii_port - 1;
}
static inline bool ksz_has_sgmii_port(struct ksz_device *dev)
{
return dev->info->sgmii_port > 0;
}
static inline bool ksz_is_sgmii_port(struct ksz_device *dev, int port)
{
return dev->info->sgmii_port == port + 1;
}
/* STP State Defines */
#define PORT_TX_ENABLE BIT(2)
#define PORT_RX_ENABLE BIT(1)
#define PORT_LEARN_DISABLE BIT(0)
/* Switch ID Defines */
#define REG_CHIP_ID0 0x00
#define SW_FAMILY_ID_M GENMASK(15, 8)
#define KSZ84_FAMILY_ID 0x84
#define KSZ87_FAMILY_ID 0x87
#define KSZ88_FAMILY_ID 0x88
#define KSZ8895_FAMILY_ID 0x95
#define KSZ8_PORT_STATUS_0 0x08
#define KSZ8_PORT_FIBER_MODE BIT(7)
#define SW_CHIP_ID_M GENMASK(7, 4)
#define KSZ87_CHIP_ID_94 0x6
#define KSZ87_CHIP_ID_95 0x9
#define KSZ88_CHIP_ID_63 0x3
#define KSZ8895_CHIP_ID_95 0x4
#define KSZ8895_CHIP_ID_95R 0x6
/* KSZ8895 specific register */
#define REG_KSZ8864_CHIP_ID 0xFE
#define SW_KSZ8864 BIT(7)
#define SW_REV_ID_M GENMASK(7, 4)
/* KSZ9893, KSZ9563, KSZ8563 specific register */
#define REG_CHIP_ID4 0x0f
#define SKU_ID_KSZ8563 0x3c
#define SKU_ID_KSZ9563 0x1c
/* Driver set switch broadcast storm protection at 10% rate. */
#define BROADCAST_STORM_PROT_RATE 10
/* 148,800 frames * 67 ms / 100 */
#define BROADCAST_STORM_VALUE 9969
#define BROADCAST_STORM_RATE_HI 0x07
#define BROADCAST_STORM_RATE_LO 0xFF
#define BROADCAST_STORM_RATE 0x07FF
#define MULTICAST_STORM_DISABLE BIT(6)
#define SW_START 0x01
/* xMII configuration */
#define P_MII_DUPLEX_M BIT(6)
#define P_MII_100MBIT_M BIT(4)
#define P_GMII_1GBIT_M BIT(6)
#define P_RGMII_ID_IG_ENABLE BIT(4)
#define P_RGMII_ID_EG_ENABLE BIT(3)
#define P_MII_MAC_MODE BIT(2)
#define P_MII_SEL_M 0x3
/* KSZ9477, KSZ87xx Wake-on-LAN (WoL) masks */
#define PME_WOL_MAGICPKT BIT(2)
#define PME_WOL_LINKUP BIT(1)
#define PME_WOL_ENERGY BIT(0)
#define PME_ENABLE BIT(1)
#define PME_POLARITY BIT(0)
#define KSZ87XX_REG_INT_EN 0x7D
#define KSZ87XX_INT_PME_MASK BIT(4)
/* Interrupt */
#define REG_SW_PORT_INT_STATUS__1 0x001B
#define REG_SW_PORT_INT_MASK__1 0x001F
#define REG_PORT_INT_STATUS 0x001B
#define REG_PORT_INT_MASK 0x001F
#define PORT_SRC_PHY_INT 1
#define PORT_SRC_PTP_INT 2
#define KSZ8795_HUGE_PACKET_SIZE 2000
#define KSZ8863_HUGE_PACKET_SIZE 1916
#define KSZ8863_NORMAL_PACKET_SIZE 1536
#define KSZ8_LEGAL_PACKET_SIZE 1518
#define KSZ9477_MAX_FRAME_SIZE 9000
#define KSZ8873_REG_GLOBAL_CTRL_12 0x0e
/* Drive Strength of I/O Pad
* 0: 8mA, 1: 16mA
*/
#define KSZ8873_DRIVE_STRENGTH_16MA BIT(6)
#define KSZ8795_REG_SW_CTRL_20 0xa3
#define KSZ9477_REG_SW_IO_STRENGTH 0x010d
#define SW_DRIVE_STRENGTH_M 0x7
#define SW_DRIVE_STRENGTH_2MA 0
#define SW_DRIVE_STRENGTH_4MA 1
#define SW_DRIVE_STRENGTH_8MA 2
#define SW_DRIVE_STRENGTH_12MA 3
#define SW_DRIVE_STRENGTH_16MA 4
#define SW_DRIVE_STRENGTH_20MA 5
#define SW_DRIVE_STRENGTH_24MA 6
#define SW_DRIVE_STRENGTH_28MA 7
#define SW_HI_SPEED_DRIVE_STRENGTH_S 4
#define SW_LO_SPEED_DRIVE_STRENGTH_S 0
/* TXQ Split Control Register for per-port, per-queue configuration.
* Register 0xAF is TXQ Split for Q3 on Port 1.
* Register offset formula: 0xAF + (port * 4) + (3 - queue)
* where: port = 0..2, queue = 0..3
*/
#define KSZ8873_TXQ_SPLIT_CTRL_REG(port, queue) \
(0xAF + ((port) * 4) + (3 - (queue)))
/* Bit 7 selects between:
* 0 = Strict priority mode (highest-priority queue first)
* 1 = Weighted Fair Queuing (WFQ) mode:
* Queue weights: Q3:Q2:Q1:Q0 = 8:4:2:1
* If any queues are empty, weight is redistributed.
*
* Note: This is referred to as "Weighted Fair Queuing" (WFQ) in KSZ8863/8873
* documentation, and as "Weighted Round Robin" (WRR) in KSZ9477 family docs.
*/
#define KSZ8873_TXQ_WFQ_ENABLE BIT(7)
#define KSZ9477_REG_PORT_OUT_RATE_0 0x0420
#define KSZ9477_OUT_RATE_NO_LIMIT 0
#define KSZ9477_PORT_MRI_TC_MAP__4 0x0808
#define KSZ9477_PORT_TC_MAP_S 4
/* CBS related registers */
#define REG_PORT_MTI_QUEUE_INDEX__4 0x0900
#define REG_PORT_MTI_QUEUE_CTRL_0 0x0914
#define MTI_SCHEDULE_MODE_M GENMASK(7, 6)
#define MTI_SCHEDULE_STRICT_PRIO 0
#define MTI_SCHEDULE_WRR 2
#define MTI_SHAPING_M GENMASK(5, 4)
#define MTI_SHAPING_OFF 0
#define MTI_SHAPING_SRP 1
#define MTI_SHAPING_TIME_AWARE 2
#define KSZ9477_PORT_MTI_QUEUE_CTRL_1 0x0915
#define KSZ9477_DEFAULT_WRR_WEIGHT 1
#define REG_PORT_MTI_HI_WATER_MARK 0x0916
#define REG_PORT_MTI_LO_WATER_MARK 0x0918
/* Regmap tables generation */
#define KSZ_SPI_OP_RD 3
#define KSZ_SPI_OP_WR 2
#define swabnot_used(x) 0
#define KSZ_SPI_OP_FLAG_MASK(opcode, swp, regbits, regpad) \
swab##swp((opcode) << ((regbits) + (regpad)))
#define KSZ_REGMAP_ENTRY(width, swp, regbits, regpad, regalign) \
{ \
.name = #width, \
.val_bits = (width), \
.reg_stride = 1, \
.reg_bits = (regbits) + (regalign), \
.pad_bits = (regpad), \
.max_register = BIT(regbits) - 1, \
.cache_type = REGCACHE_NONE, \
.read_flag_mask = \
KSZ_SPI_OP_FLAG_MASK(KSZ_SPI_OP_RD, swp, \
regbits, regpad), \
.write_flag_mask = \
KSZ_SPI_OP_FLAG_MASK(KSZ_SPI_OP_WR, swp, \
regbits, regpad), \
.lock = ksz_regmap_lock, \
.unlock = ksz_regmap_unlock, \
.reg_format_endian = REGMAP_ENDIAN_BIG, \
.val_format_endian = REGMAP_ENDIAN_BIG \
}
#define KSZ_REGMAP_TABLE(ksz, swp, regbits, regpad, regalign) \
static const struct regmap_config ksz##_regmap_config[] = { \
[KSZ_REGMAP_8] = KSZ_REGMAP_ENTRY(8, swp, (regbits), (regpad), (regalign)), \
[KSZ_REGMAP_16] = KSZ_REGMAP_ENTRY(16, swp, (regbits), (regpad), (regalign)), \
[KSZ_REGMAP_32] = KSZ_REGMAP_ENTRY(32, swp, (regbits), (regpad), (regalign)), \
}
#define KSZ8463_REGMAP_ENTRY(width, regbits, regpad, regalign) \
{ \
.name = #width, \
.val_bits = (width), \
.reg_stride = (width / 8), \
.reg_bits = (regbits) + (regalign), \
.pad_bits = (regpad), \
.read = ksz8463_spi_read, \
.write = ksz8463_spi_write, \
.max_register = BIT(regbits) - 1, \
.cache_type = REGCACHE_NONE, \
.zero_flag_mask = 1, \
.use_single_read = 1, \
.use_single_write = 1, \
.lock = ksz_regmap_lock, \
.unlock = ksz_regmap_unlock, \
}
#define KSZ8463_REGMAP_TABLE(ksz, regbits, regpad, regalign) \
static const struct regmap_config ksz##_regmap_config[] = { \
[KSZ_REGMAP_8] = KSZ8463_REGMAP_ENTRY(8, (regbits), (regpad), (regalign)), \
[KSZ_REGMAP_16] = KSZ8463_REGMAP_ENTRY(16, (regbits), (regpad), (regalign)), \
[KSZ_REGMAP_32] = KSZ8463_REGMAP_ENTRY(32, (regbits), (regpad), (regalign)), \
}
#endif
@@ -0,0 +1,612 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2024 Pengutronix, Oleksij Rempel <kernel@pengutronix.de>
#include <linux/dsa/ksz_common.h>
#include <net/dsa.h>
#include <net/dscp.h>
#include <net/ieee8021q.h>
#include "ksz_common.h"
#include "ksz_dcb.h"
#include "ksz8.h"
/* Port X Control 0 register.
* The datasheet specifies: Port 1 - 0x10, Port 2 - 0x20, Port 3 - 0x30.
* However, the driver uses get_port_addr(), which maps Port 1 to offset 0.
* Therefore, we define the base offset as 0x00 here to align with that logic.
*/
#define KSZ8_REG_PORT_1_CTRL_0 0x00
#define KSZ8463_REG_PORT_1_CTRL_0 0x6C
#define KSZ8_PORT_DIFFSERV_ENABLE BIT(6)
#define KSZ8_PORT_802_1P_ENABLE BIT(5)
#define KSZ8_PORT_BASED_PRIO_M GENMASK(4, 3)
#define KSZ8463_REG_TOS_DSCP_CTRL 0x16
#define KSZ88X3_REG_TOS_DSCP_CTRL 0x60
#define KSZ8765_REG_TOS_DSCP_CTRL 0x90
#define KSZ9477_REG_SW_MAC_TOS_CTRL 0x033e
#define KSZ9477_SW_TOS_DSCP_REMAP BIT(0)
#define KSZ9477_SW_TOS_DSCP_DEFAULT_PRIO_M GENMASK(5, 3)
#define KSZ9477_REG_DIFFSERV_PRIO_MAP 0x0340
#define KSZ9477_REG_PORT_MRI_PRIO_CTRL 0x0801
#define KSZ9477_PORT_HIGHEST_PRIO BIT(7)
#define KSZ9477_PORT_OR_PRIO BIT(6)
#define KSZ9477_PORT_MAC_PRIO_ENABLE BIT(4)
#define KSZ9477_PORT_VLAN_PRIO_ENABLE BIT(3)
#define KSZ9477_PORT_802_1P_PRIO_ENABLE BIT(2)
#define KSZ9477_PORT_DIFFSERV_PRIO_ENABLE BIT(1)
#define KSZ9477_PORT_ACL_PRIO_ENABLE BIT(0)
#define KSZ9477_REG_PORT_MRI_MAC_CTRL 0x0802
#define KSZ9477_PORT_BASED_PRIO_M GENMASK(2, 0)
struct ksz_apptrust_map {
u8 apptrust;
u8 bit;
};
static const struct ksz_apptrust_map ksz8_apptrust_map_to_bit[] = {
{ DCB_APP_SEL_PCP, KSZ8_PORT_802_1P_ENABLE },
{ IEEE_8021QAZ_APP_SEL_DSCP, KSZ8_PORT_DIFFSERV_ENABLE },
};
static const struct ksz_apptrust_map ksz9477_apptrust_map_to_bit[] = {
{ DCB_APP_SEL_PCP, KSZ9477_PORT_802_1P_PRIO_ENABLE },
{ IEEE_8021QAZ_APP_SEL_DSCP, KSZ9477_PORT_DIFFSERV_PRIO_ENABLE },
};
/* ksz_supported_apptrust[] - Supported apptrust selectors and Priority Order
* of Internal Priority Map (IPM) sources.
*
* This array defines the apptrust selectors supported by the hardware, where
* the index within the array indicates the priority of the selector - lower
* indices correspond to higher priority. This fixed priority scheme is due to
* the hardware's design, which does not support configurable priority among
* different priority sources.
*
* The priority sources, including Tail Tag, ACL, VLAN PCP and DSCP are ordered
* by the hardware's fixed logic, as detailed below. The order reflects a
* non-configurable precedence where certain types of priority information
* override others:
*
* 1. Tail Tag - Highest priority, overrides ACL, VLAN PCP, and DSCP priorities.
* 2. ACL - Overrides VLAN PCP and DSCP priorities.
* 3. VLAN PCP - Overrides DSCP priority.
* 4. DSCP - Lowest priority, does not override any other priority source.
*
* In this context, the array's lower index (higher priority) for
* 'DCB_APP_SEL_PCP' suggests its relative priority over
* 'IEEE_8021QAZ_APP_SEL_DSCP' within the system's fixed priority scheme.
*
* DCB_APP_SEL_PCP - Priority Code Point selector
* IEEE_8021QAZ_APP_SEL_DSCP - Differentiated Services Code Point selector
*/
static const u8 ksz_supported_apptrust[] = {
DCB_APP_SEL_PCP,
IEEE_8021QAZ_APP_SEL_DSCP,
};
static const char * const ksz_supported_apptrust_variants[] = {
"empty", "dscp", "pcp", "dscp pcp"
};
static void ksz_get_default_port_prio_reg(struct ksz_device *dev, int *reg,
u8 *mask, int *shift)
{
if (is_ksz8(dev)) {
*reg = KSZ8_REG_PORT_1_CTRL_0;
*mask = KSZ8_PORT_BASED_PRIO_M;
*shift = __bf_shf(KSZ8_PORT_BASED_PRIO_M);
if (ksz_is_ksz8463(dev))
*reg = KSZ8463_REG_PORT_1_CTRL_0;
} else {
*reg = KSZ9477_REG_PORT_MRI_MAC_CTRL;
*mask = KSZ9477_PORT_BASED_PRIO_M;
*shift = __bf_shf(KSZ9477_PORT_BASED_PRIO_M);
}
}
/**
* ksz_get_dscp_prio_reg - Retrieves the DSCP-to-priority-mapping register
* @dev: Pointer to the KSZ switch device structure
* @reg: Pointer to the register address to be set
* @per_reg: Pointer to the number of DSCP values per register
* @mask: Pointer to the mask to be set
*
* This function retrieves the DSCP to priority mapping register, the number of
* DSCP values per register, and the mask to be set.
*/
static void ksz_get_dscp_prio_reg(struct ksz_device *dev, int *reg,
int *per_reg, u8 *mask)
{
if (ksz_is_ksz87xx(dev) || ksz_is_8895_family(dev)) {
*reg = KSZ8765_REG_TOS_DSCP_CTRL;
*per_reg = 4;
*mask = GENMASK(1, 0);
} else if (ksz_is_ksz88x3(dev) || ksz_is_ksz8463(dev)) {
*reg = KSZ88X3_REG_TOS_DSCP_CTRL;
*per_reg = 4;
*mask = GENMASK(1, 0);
if (ksz_is_ksz8463(dev))
*reg = KSZ8463_REG_TOS_DSCP_CTRL;
} else {
*reg = KSZ9477_REG_DIFFSERV_PRIO_MAP;
*per_reg = 2;
*mask = GENMASK(2, 0);
}
}
/**
* ksz_get_apptrust_map_and_reg - Retrieves the apptrust map and register
* @dev: Pointer to the KSZ switch device structure
* @map: Pointer to the apptrust map to be set
* @reg: Pointer to the register address to be set
* @mask: Pointer to the mask to be set
*
* This function retrieves the apptrust map and register address for the
* apptrust configuration.
*/
static void ksz_get_apptrust_map_and_reg(struct ksz_device *dev,
const struct ksz_apptrust_map **map,
int *reg, u8 *mask)
{
if (is_ksz8(dev)) {
*map = ksz8_apptrust_map_to_bit;
*reg = KSZ8_REG_PORT_1_CTRL_0;
*mask = KSZ8_PORT_DIFFSERV_ENABLE | KSZ8_PORT_802_1P_ENABLE;
if (ksz_is_ksz8463(dev))
*reg = KSZ8463_REG_PORT_1_CTRL_0;
} else {
*map = ksz9477_apptrust_map_to_bit;
*reg = KSZ9477_REG_PORT_MRI_PRIO_CTRL;
*mask = KSZ9477_PORT_802_1P_PRIO_ENABLE |
KSZ9477_PORT_DIFFSERV_PRIO_ENABLE;
}
}
/**
* ksz_port_get_default_prio - Retrieves the default priority for a port on a
* KSZ switch
* @ds: Pointer to the DSA switch structure
* @port: Port number from which to get the default priority
*
* This function fetches the default priority for the specified port on a KSZ
* switch.
*
* Return: The default priority of the port on success, or a negative error
* code on failure.
*/
int ksz_port_get_default_prio(struct dsa_switch *ds, int port)
{
struct ksz_device *dev = ds->priv;
int ret, reg, shift;
u8 data, mask;
ksz_get_default_port_prio_reg(dev, &reg, &mask, &shift);
ret = ksz_pread8(dev, port, reg, &data);
if (ret)
return ret;
return (data & mask) >> shift;
}
/**
* ksz_port_set_default_prio - Sets the default priority for a port on a KSZ
* switch
* @ds: Pointer to the DSA switch structure
* @port: Port number for which to set the default priority
* @prio: Priority value to set
*
* This function sets the default priority for the specified port on a KSZ
* switch.
*
* Return: 0 on success, or a negative error code on failure.
*/
int ksz_port_set_default_prio(struct dsa_switch *ds, int port, u8 prio)
{
struct ksz_device *dev = ds->priv;
int reg, shift;
u8 mask;
if (prio >= dev->info->num_ipms)
return -EINVAL;
ksz_get_default_port_prio_reg(dev, &reg, &mask, &shift);
return ksz_prmw8(dev, port, reg, mask, (prio << shift) & mask);
}
/**
* ksz_port_get_dscp_prio - Retrieves the priority for a DSCP value on a KSZ
* switch
* @ds: Pointer to the DSA switch structure
* @port: Port number for which to get the priority
* @dscp: DSCP value for which to get the priority
*
* This function fetches the priority value from switch global DSCP-to-priorty
* mapping table for the specified DSCP value.
*
* Return: The priority value for the DSCP on success, or a negative error
* code on failure.
*/
int ksz_port_get_dscp_prio(struct dsa_switch *ds, int port, u8 dscp)
{
struct ksz_device *dev = ds->priv;
int reg, per_reg, ret, shift;
u8 data, mask;
ksz_get_dscp_prio_reg(dev, &reg, &per_reg, &mask);
/* If DSCP remapping is disabled, DSCP bits 3-5 are used as Internal
* Priority Map (IPM)
*/
if (!is_ksz8(dev)) {
ret = ksz_read8(dev, KSZ9477_REG_SW_MAC_TOS_CTRL, &data);
if (ret)
return ret;
/* If DSCP remapping is disabled, DSCP bits 3-5 are used as
* Internal Priority Map (IPM)
*/
if (!(data & KSZ9477_SW_TOS_DSCP_REMAP))
return FIELD_GET(KSZ9477_SW_TOS_DSCP_DEFAULT_PRIO_M,
dscp);
}
/* In case DSCP remapping is enabled, we need to write the DSCP to
* priority mapping table.
*/
reg += dscp / per_reg;
ret = ksz_read8(dev, reg, &data);
if (ret)
return ret;
shift = (dscp % per_reg) * (8 / per_reg);
return (data >> shift) & mask;
}
/**
* ksz_set_global_dscp_entry - Sets the global DSCP-to-priority mapping entry
* @dev: Pointer to the KSZ switch device structure
* @dscp: DSCP value for which to set the priority
* @ipm: Priority value to set
*
* This function sets the global DSCP-to-priority mapping entry for the
* specified DSCP value.
*
* Return: 0 on success, or a negative error code on failure.
*/
static int ksz_set_global_dscp_entry(struct ksz_device *dev, u8 dscp, u8 ipm)
{
int reg, per_reg, shift;
u8 mask;
ksz_get_dscp_prio_reg(dev, &reg, &per_reg, &mask);
shift = (dscp % per_reg) * (8 / per_reg);
return ksz_rmw8(dev, reg + (dscp / per_reg), mask << shift,
ipm << shift);
}
/**
* ksz_init_global_dscp_map - Initializes the global DSCP-to-priority mapping
* @dev: Pointer to the KSZ switch device structure
*
* This function initializes the global DSCP-to-priority mapping table for the
* switch.
*
* Return: 0 on success, or a negative error code on failure
*/
static int ksz_init_global_dscp_map(struct ksz_device *dev)
{
int ret, dscp;
/* On KSZ9xxx variants, DSCP remapping is disabled by default.
* Enable to have, predictable and reproducible behavior across
* different devices.
*/
if (!is_ksz8(dev)) {
ret = ksz_rmw8(dev, KSZ9477_REG_SW_MAC_TOS_CTRL,
KSZ9477_SW_TOS_DSCP_REMAP,
KSZ9477_SW_TOS_DSCP_REMAP);
if (ret)
return ret;
}
for (dscp = 0; dscp < DSCP_MAX; dscp++) {
int ipm, tt;
/* Map DSCP to Traffic Type, which is corresponding to the
* Internal Priority Map (IPM) in the switch.
*/
if (!is_ksz8(dev)) {
ipm = ietf_dscp_to_ieee8021q_tt(dscp);
} else {
/* On KSZ8xxx variants we do not have IPM to queue
* remapping table. We need to convert DSCP to Traffic
* Type and then to queue.
*/
tt = ietf_dscp_to_ieee8021q_tt(dscp);
if (tt < 0)
return tt;
ipm = ieee8021q_tt_to_tc(tt, dev->info->num_tx_queues);
}
if (ipm < 0)
return ipm;
ret = ksz_set_global_dscp_entry(dev, dscp, ipm);
}
return 0;
}
/**
* ksz_port_add_dscp_prio - Adds a DSCP-to-priority mapping entry for a port on
* a KSZ switch.
* @ds: Pointer to the DSA switch structure
* @port: Port number for which to add the DSCP-to-priority mapping entry
* @dscp: DSCP value for which to add the priority
* @prio: Priority value to set
*
* Return: 0 on success, or a negative error code on failure
*/
int ksz_port_add_dscp_prio(struct dsa_switch *ds, int port, u8 dscp, u8 prio)
{
struct ksz_device *dev = ds->priv;
if (prio >= dev->info->num_ipms)
return -ERANGE;
return ksz_set_global_dscp_entry(dev, dscp, prio);
}
/**
* ksz_port_del_dscp_prio - Deletes a DSCP-to-priority mapping entry for a port
* on a KSZ switch.
* @ds: Pointer to the DSA switch structure
* @port: Port number for which to delete the DSCP-to-priority mapping entry
* @dscp: DSCP value for which to delete the priority
* @prio: Priority value to delete
*
* Return: 0 on success, or a negative error code on failure
*/
int ksz_port_del_dscp_prio(struct dsa_switch *ds, int port, u8 dscp, u8 prio)
{
struct ksz_device *dev = ds->priv;
int ipm;
if (ksz_port_get_dscp_prio(ds, port, dscp) != prio)
return 0;
if (is_ksz8(dev)) {
ipm = ieee8021q_tt_to_tc(IEEE8021Q_TT_BE,
dev->info->num_tx_queues);
if (ipm < 0)
return ipm;
} else {
ipm = IEEE8021Q_TT_BE;
}
return ksz_set_global_dscp_entry(dev, dscp, ipm);
}
/**
* ksz_apptrust_error - Prints an error message for an invalid apptrust selector
* @dev: Pointer to the KSZ switch device structure
*
* This function prints an error message when an invalid apptrust selector is
* provided.
*/
static void ksz_apptrust_error(struct ksz_device *dev)
{
char supported_apptrust_variants[64];
int i;
supported_apptrust_variants[0] = '\0';
for (i = 0; i < ARRAY_SIZE(ksz_supported_apptrust_variants); i++) {
if (i > 0)
strlcat(supported_apptrust_variants, ", ",
sizeof(supported_apptrust_variants));
strlcat(supported_apptrust_variants,
ksz_supported_apptrust_variants[i],
sizeof(supported_apptrust_variants));
}
dev_err(dev->dev, "Invalid apptrust selector or priority order. Supported: %s\n",
supported_apptrust_variants);
}
/**
* ksz_port_set_apptrust_validate - Validates the apptrust selectors
* @dev: Pointer to the KSZ switch device structure
* @port: Port number for which to set the apptrust selectors
* @sel: Array of apptrust selectors to validate
* @nsel: Number of apptrust selectors in the array
*
* This function validates the apptrust selectors provided and ensures that
* they are in the correct order.
*
* This family of switches supports two apptrust selectors: DCB_APP_SEL_PCP and
* IEEE_8021QAZ_APP_SEL_DSCP. The priority order of the selectors is fixed and
* cannot be changed. The order is as follows:
* 1. DCB_APP_SEL_PCP - Priority Code Point selector (highest priority)
* 2. IEEE_8021QAZ_APP_SEL_DSCP - Differentiated Services Code Point selector
* (lowest priority)
*
* Return: 0 on success, or a negative error code on failure
*/
static int ksz_port_set_apptrust_validate(struct ksz_device *dev, int port,
const u8 *sel, int nsel)
{
int i, j, found;
int j_prev = 0;
/* Iterate through the requested selectors */
for (i = 0; i < nsel; i++) {
found = 0;
/* Check if the current selector is supported by the hardware */
for (j = 0; j < sizeof(ksz_supported_apptrust); j++) {
if (sel[i] != ksz_supported_apptrust[j])
continue;
found = 1;
/* Ensure that no higher priority selector (lower index)
* precedes a lower priority one
*/
if (i > 0 && j <= j_prev)
goto err_sel_not_vaild;
j_prev = j;
break;
}
if (!found)
goto err_sel_not_vaild;
}
return 0;
err_sel_not_vaild:
ksz_apptrust_error(dev);
return -EINVAL;
}
/**
* ksz_port_set_apptrust - Sets the apptrust selectors for a port on a KSZ
* switch
* @ds: Pointer to the DSA switch structure
* @port: Port number for which to set the apptrust selectors
* @sel: Array of apptrust selectors to set
* @nsel: Number of apptrust selectors in the array
*
* This function sets the apptrust selectors for the specified port on a KSZ
* switch.
*
* Return: 0 on success, or a negative error code on failure
*/
int ksz_port_set_apptrust(struct dsa_switch *ds, int port,
const u8 *sel, int nsel)
{
const struct ksz_apptrust_map *map;
struct ksz_device *dev = ds->priv;
int reg, i, ret;
u8 data = 0;
u8 mask;
ret = ksz_port_set_apptrust_validate(dev, port, sel, nsel);
if (ret)
return ret;
ksz_get_apptrust_map_and_reg(dev, &map, &reg, &mask);
for (i = 0; i < nsel; i++) {
int j;
for (j = 0; j < ARRAY_SIZE(ksz_supported_apptrust); j++) {
if (sel[i] != ksz_supported_apptrust[j])
continue;
data |= map[j].bit;
break;
}
}
return ksz_prmw8(dev, port, reg, mask, data);
}
/**
* ksz_port_get_apptrust - Retrieves the apptrust selectors for a port on a KSZ
* switch
* @ds: Pointer to the DSA switch structure
* @port: Port number for which to get the apptrust selectors
* @sel: Array to store the apptrust selectors
* @nsel: Number of apptrust selectors in the array
*
* This function fetches the apptrust selectors for the specified port on a KSZ
* switch.
*
* Return: 0 on success, or a negative error code on failure
*/
int ksz_port_get_apptrust(struct dsa_switch *ds, int port, u8 *sel, int *nsel)
{
const struct ksz_apptrust_map *map;
struct ksz_device *dev = ds->priv;
int reg, i, ret;
u8 data;
u8 mask;
ksz_get_apptrust_map_and_reg(dev, &map, &reg, &mask);
ret = ksz_pread8(dev, port, reg, &data);
if (ret)
return ret;
*nsel = 0;
for (i = 0; i < ARRAY_SIZE(ksz_supported_apptrust); i++) {
if (data & map[i].bit)
sel[(*nsel)++] = ksz_supported_apptrust[i];
}
return 0;
}
/**
* ksz_dcb_init_port - Initializes the DCB configuration for a port on a KSZ
* @dev: Pointer to the KSZ switch device structure
* @port: Port number for which to initialize the DCB configuration
*
* This function initializes the DCB configuration for the specified port on a
* KSZ switch. Particular DCB configuration is set for the port, including the
* default priority and apptrust selectors.
* The default priority is set to Best Effort, and the apptrust selectors are
* set to all supported selectors.
*
* Return: 0 on success, or a negative error code on failure
*/
int ksz_dcb_init_port(struct ksz_device *dev, int port)
{
const u8 ksz_default_apptrust[] = { DCB_APP_SEL_PCP };
int ret, ipm;
if (is_ksz8(dev)) {
ipm = ieee8021q_tt_to_tc(IEEE8021Q_TT_BE,
dev->info->num_tx_queues);
if (ipm < 0)
return ipm;
} else {
ipm = IEEE8021Q_TT_BE;
}
/* Set the default priority for the port to Best Effort */
ret = ksz_port_set_default_prio(dev->ds, port, ipm);
if (ret)
return ret;
return ksz_port_set_apptrust(dev->ds, port, ksz_default_apptrust,
ARRAY_SIZE(ksz_default_apptrust));
}
/**
* ksz_dcb_init - Initializes the DCB configuration for a KSZ switch
* @dev: Pointer to the KSZ switch device structure
*
* This function initializes the DCB configuration for a KSZ switch. The global
* DSCP-to-priority mapping table is initialized.
*
* Return: 0 on success, or a negative error code on failure
*/
int ksz_dcb_init(struct ksz_device *dev)
{
return ksz_init_global_dscp_map(dev);
}
@@ -0,0 +1,23 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2024 Pengutronix, Oleksij Rempel <kernel@pengutronix.de> */
#ifndef __KSZ_DCB_H
#define __KSZ_DCB_H
#include <net/dsa.h>
#include "ksz_common.h"
int ksz_port_get_default_prio(struct dsa_switch *ds, int port);
int ksz_port_set_default_prio(struct dsa_switch *ds, int port, u8 prio);
int ksz_port_get_dscp_prio(struct dsa_switch *ds, int port, u8 dscp);
int ksz_port_add_dscp_prio(struct dsa_switch *ds, int port, u8 dscp, u8 prio);
int ksz_port_del_dscp_prio(struct dsa_switch *ds, int port, u8 dscp, u8 prio);
int ksz_port_set_apptrust(struct dsa_switch *ds, int port,
const unsigned char *sel,
int nsel);
int ksz_port_get_apptrust(struct dsa_switch *ds, int port, u8 *sel, int *nsel);
int ksz_dcb_init_port(struct ksz_device *dev, int port);
int ksz_dcb_init(struct ksz_device *dev);
#endif /* __KSZ_DCB_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,89 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Microchip KSZ PTP Implementation
*
* Copyright (C) 2020 ARRI Lighting
* Copyright (C) 2022 Microchip Technology Inc.
*/
#ifndef _NET_DSA_DRIVERS_KSZ_PTP_H
#define _NET_DSA_DRIVERS_KSZ_PTP_H
#if IS_ENABLED(CONFIG_NET_DSA_MICROCHIP_KSZ_PTP)
#include <linux/ptp_clock_kernel.h>
#define KSZ_PTP_N_GPIO 2
enum ksz_ptp_tou_mode {
KSZ_PTP_TOU_IDLE,
KSZ_PTP_TOU_PEROUT,
};
struct ksz_ptp_data {
struct ptp_clock_info caps;
struct ptp_clock *clock;
struct ptp_pin_desc pin_config[KSZ_PTP_N_GPIO];
/* Serializes all operations on the PTP hardware clock */
struct mutex lock;
/* lock for accessing the clock_time */
spinlock_t clock_lock;
struct timespec64 clock_time;
enum ksz_ptp_tou_mode tou_mode;
struct timespec64 perout_target_time_first; /* start of first pulse */
struct timespec64 perout_period;
};
int ksz_ptp_clock_register(struct dsa_switch *ds);
void ksz_ptp_clock_unregister(struct dsa_switch *ds);
int ksz_get_ts_info(struct dsa_switch *ds, int port,
struct kernel_ethtool_ts_info *ts);
int ksz_hwtstamp_get(struct dsa_switch *ds, int port,
struct kernel_hwtstamp_config *config);
int ksz_hwtstamp_set(struct dsa_switch *ds, int port,
struct kernel_hwtstamp_config *config,
struct netlink_ext_ack *extack);
void ksz_port_txtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb);
void ksz_port_deferred_xmit(struct kthread_work *work);
bool ksz_port_rxtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb,
unsigned int type);
int ksz_ptp_irq_setup(struct dsa_switch *ds, u8 p);
void ksz_ptp_irq_free(struct dsa_switch *ds, u8 p);
#else
struct ksz_ptp_data {
/* Serializes all operations on the PTP hardware clock */
struct mutex lock;
};
static inline int ksz_ptp_clock_register(struct dsa_switch *ds)
{
return 0;
}
static inline void ksz_ptp_clock_unregister(struct dsa_switch *ds) { }
static inline int ksz_ptp_irq_setup(struct dsa_switch *ds, u8 p)
{
return 0;
}
static inline void ksz_ptp_irq_free(struct dsa_switch *ds, u8 p) {}
#define ksz_get_ts_info NULL
#define ksz_hwtstamp_get NULL
#define ksz_hwtstamp_set NULL
#define ksz_port_rxtstamp NULL
#define ksz_port_txtstamp NULL
#define ksz_port_deferred_xmit NULL
#endif /* End of CONFIG_NET_DSA_MICROCHIP_KSZ_PTP */
#endif
@@ -0,0 +1,134 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Microchip KSZ PTP register definitions
* Copyright (C) 2022 Microchip Technology Inc.
*/
#ifndef __KSZ_PTP_REGS_H
#define __KSZ_PTP_REGS_H
#define REG_SW_GLOBAL_LED_OVR__4 0x0120
#define LED_OVR_2 BIT(1)
#define LED_OVR_1 BIT(0)
#define REG_SW_GLOBAL_LED_SRC__4 0x0128
#define LED_SRC_PTP_GPIO_1 BIT(3)
#define LED_SRC_PTP_GPIO_2 BIT(2)
/* 5 - PTP Clock */
/* REG_PTP_CLK_CTRL */
#define PTP_STEP_ADJ BIT(6)
#define PTP_STEP_DIR BIT(5)
#define PTP_READ_TIME BIT(4)
#define PTP_LOAD_TIME BIT(3)
#define PTP_CLK_ADJ_ENABLE BIT(2)
#define PTP_CLK_ENABLE BIT(1)
#define PTP_CLK_RESET BIT(0)
/* REG_PTP_RTC_SUB_NANOSEC */
#define PTP_RTC_SUB_NANOSEC_M 0x0007
#define PTP_RTC_0NS 0x00
/* REG_PTP_SUBNANOSEC_RATE */
#define PTP_SUBNANOSEC_M 0x3FFFFFFF
#define PTP_RATE_DIR BIT(31)
#define PTP_TMP_RATE_ENABLE BIT(30)
#define REG_PTP_SUBNANOSEC_RATE_L 0x050E
#define REG_PTP_RATE_DURATION 0x0510
#define REG_PTP_RATE_DURATION_H 0x0510
#define REG_PTP_RATE_DURATION_L 0x0512
/* REG_PTP_MSG_CONF1 */
#define PTP_802_1AS BIT(7)
#define PTP_ENABLE BIT(6)
#define PTP_ETH_ENABLE BIT(5)
#define PTP_IPV4_UDP_ENABLE BIT(4)
#define PTP_IPV6_UDP_ENABLE BIT(3)
#define PTP_TC_P2P BIT(2)
#define PTP_MASTER BIT(1)
#define PTP_1STEP BIT(0)
#define REG_PTP_UNIT_INDEX__4 0x0520
#define PTP_GPIO_INDEX GENMASK(19, 16)
#define PTP_TSI_INDEX BIT(8)
#define PTP_TOU_INDEX GENMASK(1, 0)
#define REG_PTP_TRIG_STATUS__4 0x0524
#define TRIG_ERROR_M GENMASK(18, 16)
#define TRIG_DONE_M GENMASK(2, 0)
#define REG_PTP_INT_STATUS__4 0x0528
#define TRIG_INT_M GENMASK(18, 16)
#define TS_INT_M GENMASK(1, 0)
#define REG_PTP_CTRL_STAT__4 0x052C
#define GPIO_IN BIT(7)
#define GPIO_OUT BIT(6)
#define TS_INT_ENABLE BIT(5)
#define TRIG_ACTIVE BIT(4)
#define TRIG_ENABLE BIT(3)
#define TRIG_RESET BIT(2)
#define TS_ENABLE BIT(1)
#define TS_RESET BIT(0)
#define REG_TRIG_TARGET_NANOSEC 0x0530
#define REG_TRIG_TARGET_SEC 0x0534
#define REG_TRIG_CTRL__4 0x0538
#define TRIG_CASCADE_ENABLE BIT(31)
#define TRIG_CASCADE_TAIL BIT(30)
#define TRIG_CASCADE_UPS_M GENMASK(29, 26)
#define TRIG_NOW BIT(25)
#define TRIG_NOTIFY BIT(24)
#define TRIG_EDGE BIT(23)
#define TRIG_PATTERN_M GENMASK(22, 20)
#define TRIG_NEG_EDGE 0
#define TRIG_POS_EDGE 1
#define TRIG_NEG_PULSE 2
#define TRIG_POS_PULSE 3
#define TRIG_NEG_PERIOD 4
#define TRIG_POS_PERIOD 5
#define TRIG_REG_OUTPUT 6
#define TRIG_GPO_M GENMASK(19, 16)
#define TRIG_CASCADE_ITERATE_CNT_M GENMASK(15, 0)
#define REG_TRIG_CYCLE_WIDTH 0x053C
#define TRIG_CYCLE_WIDTH_M GENMASK(31, 0)
#define REG_TRIG_CYCLE_CNT 0x0540
#define TRIG_CYCLE_CNT_M GENMASK(31, 16)
#define TRIG_BIT_PATTERN_M GENMASK(15, 0)
#define REG_TRIG_ITERATE_TIME 0x0544
#define REG_TRIG_PULSE_WIDTH__4 0x0548
#define TRIG_PULSE_WIDTH_M GENMASK(23, 0)
/* Port PTP Register */
#define REG_PTP_PORT_RX_DELAY__2 0x0C00
#define REG_PTP_PORT_TX_DELAY__2 0x0C02
#define REG_PTP_PORT_ASYM_DELAY__2 0x0C04
#define REG_PTP_PORT_XDELAY_TS 0x0C08
#define REG_PTP_PORT_SYNC_TS 0x0C0C
#define REG_PTP_PORT_PDRESP_TS 0x0C10
#define REG_PTP_PORT_TX_INT_STATUS__2 0x0C14
#define REG_PTP_PORT_TX_INT_ENABLE__2 0x0C16
#define PTP_PORT_SYNC_INT BIT(15)
#define PTP_PORT_XDELAY_REQ_INT BIT(14)
#define PTP_PORT_PDELAY_RESP_INT BIT(13)
#define KSZ_SYNC_MSG 2
#define KSZ_XDREQ_MSG 1
#define KSZ_PDRES_MSG 0
#endif
@@ -0,0 +1,366 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Microchip ksz series register access through SPI
*
* Copyright (C) 2017-2024 Microchip Technology Inc.
* Tristram Ha <Tristram.Ha@microchip.com>
*/
#include <linux/unaligned.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include "ksz_common.h"
#define KSZ8463_SPI_ADDR_SHIFT 13
#define KSZ8463_SPI_ADDR_ALIGN 3
#define KSZ8463_SPI_TURNAROUND_SHIFT 2
#define KSZ8795_SPI_ADDR_SHIFT 12
#define KSZ8795_SPI_ADDR_ALIGN 3
#define KSZ8795_SPI_TURNAROUND_SHIFT 1
#define KSZ8863_SPI_ADDR_SHIFT 8
#define KSZ8863_SPI_ADDR_ALIGN 8
#define KSZ8863_SPI_TURNAROUND_SHIFT 0
#define KSZ9477_SPI_ADDR_SHIFT 24
#define KSZ9477_SPI_ADDR_ALIGN 3
#define KSZ9477_SPI_TURNAROUND_SHIFT 5
KSZ_REGMAP_TABLE(ksz8795, 16, KSZ8795_SPI_ADDR_SHIFT,
KSZ8795_SPI_TURNAROUND_SHIFT, KSZ8795_SPI_ADDR_ALIGN);
KSZ_REGMAP_TABLE(ksz8863, 16, KSZ8863_SPI_ADDR_SHIFT,
KSZ8863_SPI_TURNAROUND_SHIFT, KSZ8863_SPI_ADDR_ALIGN);
KSZ_REGMAP_TABLE(ksz9477, 32, KSZ9477_SPI_ADDR_SHIFT,
KSZ9477_SPI_TURNAROUND_SHIFT, KSZ9477_SPI_ADDR_ALIGN);
static u16 ksz8463_reg(u16 reg, size_t size)
{
switch (size) {
case 1:
reg = ((reg >> 2) << 4) | (1 << (reg & 3));
break;
case 2:
reg = ((reg >> 2) << 4) | (reg & 2 ? 0x0c : 0x03);
break;
default:
reg = ((reg >> 2) << 4) | 0xf;
break;
}
reg <<= KSZ8463_SPI_TURNAROUND_SHIFT;
return reg;
}
static int ksz8463_spi_read(void *context,
const void *reg, size_t reg_size,
void *val, size_t val_size)
{
struct device *dev = context;
struct spi_device *spi = to_spi_device(dev);
u8 bytes[2];
u16 cmd;
int rc;
if (reg_size > 2 || val_size > 4)
return -EINVAL;
memcpy(&cmd, reg, sizeof(u16));
cmd = ksz8463_reg(cmd, val_size);
/* SPI command uses big-endian format. */
put_unaligned_be16(cmd, bytes);
rc = spi_write_then_read(spi, bytes, reg_size, val, val_size);
#if defined(__BIG_ENDIAN)
/* Register value uses little-endian format so need to convert when
* running in big-endian system.
*/
if (!rc && val_size > 1) {
if (val_size == 2) {
u16 v = get_unaligned_le16(val);
memcpy(val, &v, sizeof(v));
} else if (val_size == 4) {
u32 v = get_unaligned_le32(val);
memcpy(val, &v, sizeof(v));
}
}
#endif
return rc;
}
static int ksz8463_spi_write(void *context, const void *data, size_t count)
{
struct device *dev = context;
struct spi_device *spi = to_spi_device(dev);
size_t val_size = count - 2;
u8 bytes[6];
u16 cmd;
if (count <= 2 || count > 6)
return -EINVAL;
memcpy(bytes, data, count);
memcpy(&cmd, data, sizeof(u16));
cmd = ksz8463_reg(cmd, val_size);
cmd |= (1 << (KSZ8463_SPI_ADDR_SHIFT + KSZ8463_SPI_TURNAROUND_SHIFT));
/* SPI command uses big-endian format. */
put_unaligned_be16(cmd, bytes);
#if defined(__BIG_ENDIAN)
/* Register value uses little-endian format so need to convert when
* running in big-endian system.
*/
if (val_size == 2) {
u8 *val = &bytes[2];
u16 v;
memcpy(&v, val, sizeof(v));
put_unaligned_le16(v, val);
} else if (val_size == 4) {
u8 *val = &bytes[2];
u32 v;
memcpy(&v, val, sizeof(v));
put_unaligned_le32(v, val);
}
#endif
return spi_write(spi, bytes, count);
}
KSZ8463_REGMAP_TABLE(ksz8463, KSZ8463_SPI_ADDR_SHIFT, 0,
KSZ8463_SPI_ADDR_ALIGN);
static int ksz_spi_probe(struct spi_device *spi)
{
const struct regmap_config *regmap_config;
const struct ksz_chip_data *chip;
struct device *ddev = &spi->dev;
struct regmap_config rc;
struct ksz_device *dev;
int i, ret = 0;
chip = device_get_match_data(ddev);
if (!chip)
return -EINVAL;
dev = ksz_switch_alloc(&spi->dev, chip, spi);
if (!dev)
return -ENOMEM;
/* Save chip id to do special initialization when probing. */
dev->chip_id = chip->chip_id;
if (chip->chip_id == KSZ88X3_CHIP_ID)
regmap_config = ksz8863_regmap_config;
else if (chip->chip_id == KSZ8463_CHIP_ID)
regmap_config = ksz8463_regmap_config;
else if (chip->chip_id == KSZ8795_CHIP_ID ||
chip->chip_id == KSZ8794_CHIP_ID ||
chip->chip_id == KSZ8765_CHIP_ID)
regmap_config = ksz8795_regmap_config;
else if (chip->chip_id == KSZ8895_CHIP_ID ||
chip->chip_id == KSZ8864_CHIP_ID)
regmap_config = ksz8863_regmap_config;
else
regmap_config = ksz9477_regmap_config;
for (i = 0; i < __KSZ_NUM_REGMAPS; i++) {
rc = regmap_config[i];
rc.lock_arg = &dev->regmap_mutex;
rc.wr_table = chip->wr_table;
rc.rd_table = chip->rd_table;
dev->regmap[i] = devm_regmap_init_spi(spi, &rc);
if (IS_ERR(dev->regmap[i])) {
return dev_err_probe(&spi->dev, PTR_ERR(dev->regmap[i]),
"Failed to initialize regmap%i\n",
regmap_config[i].val_bits);
}
}
if (spi->dev.platform_data)
dev->pdata = spi->dev.platform_data;
/* setup spi */
spi->mode = SPI_MODE_3;
ret = spi_setup(spi);
if (ret)
return ret;
dev->irq = spi->irq;
ret = ksz_switch_register(dev);
/* Main DSA driver may not be started yet. */
if (ret)
return ret;
spi_set_drvdata(spi, dev);
return 0;
}
static void ksz_spi_remove(struct spi_device *spi)
{
struct ksz_device *dev = spi_get_drvdata(spi);
if (dev)
ksz_switch_remove(dev);
}
static void ksz_spi_shutdown(struct spi_device *spi)
{
struct ksz_device *dev = spi_get_drvdata(spi);
if (!dev)
return;
ksz_switch_shutdown(dev);
spi_set_drvdata(spi, NULL);
}
static const struct of_device_id ksz_dt_ids[] = {
{
.compatible = "microchip,ksz8463",
.data = &ksz_switch_chips[KSZ8463]
},
{
.compatible = "microchip,ksz8765",
.data = &ksz_switch_chips[KSZ8765]
},
{
.compatible = "microchip,ksz8794",
.data = &ksz_switch_chips[KSZ8794]
},
{
.compatible = "microchip,ksz8795",
.data = &ksz_switch_chips[KSZ8795]
},
{
.compatible = "microchip,ksz8863",
.data = &ksz_switch_chips[KSZ88X3]
},
{
.compatible = "microchip,ksz8864",
.data = &ksz_switch_chips[KSZ8864]
},
{
.compatible = "microchip,ksz8873",
.data = &ksz_switch_chips[KSZ88X3]
},
{
.compatible = "microchip,ksz8895",
.data = &ksz_switch_chips[KSZ8895]
},
{
.compatible = "microchip,ksz9477",
.data = &ksz_switch_chips[KSZ9477]
},
{
.compatible = "microchip,ksz9896",
.data = &ksz_switch_chips[KSZ9896]
},
{
.compatible = "microchip,ksz9897",
.data = &ksz_switch_chips[KSZ9897]
},
{
.compatible = "microchip,ksz9893",
.data = &ksz_switch_chips[KSZ9893]
},
{
.compatible = "microchip,ksz9563",
.data = &ksz_switch_chips[KSZ9563]
},
{
.compatible = "microchip,ksz8563",
.data = &ksz_switch_chips[KSZ8563]
},
{
.compatible = "microchip,ksz8567",
.data = &ksz_switch_chips[KSZ8567]
},
{
.compatible = "microchip,ksz9567",
.data = &ksz_switch_chips[KSZ9567]
},
{
.compatible = "microchip,lan9370",
.data = &ksz_switch_chips[LAN9370]
},
{
.compatible = "microchip,lan9371",
.data = &ksz_switch_chips[LAN9371]
},
{
.compatible = "microchip,lan9372",
.data = &ksz_switch_chips[LAN9372]
},
{
.compatible = "microchip,lan9373",
.data = &ksz_switch_chips[LAN9373]
},
{
.compatible = "microchip,lan9374",
.data = &ksz_switch_chips[LAN9374]
},
{
.compatible = "microchip,lan9646",
.data = &ksz_switch_chips[LAN9646]
},
{},
};
MODULE_DEVICE_TABLE(of, ksz_dt_ids);
static const struct spi_device_id ksz_spi_ids[] = {
{ "ksz8463" },
{ "ksz8765" },
{ "ksz8794" },
{ "ksz8795" },
{ "ksz8863" },
{ "ksz8864" },
{ "ksz8873" },
{ "ksz8895" },
{ "ksz9477" },
{ "ksz9896" },
{ "ksz9897" },
{ "ksz9893" },
{ "ksz9563" },
{ "ksz8563" },
{ "ksz8567" },
{ "ksz9567" },
{ "lan9370" },
{ "lan9371" },
{ "lan9372" },
{ "lan9373" },
{ "lan9374" },
{ "lan9646" },
{ },
};
MODULE_DEVICE_TABLE(spi, ksz_spi_ids);
static DEFINE_SIMPLE_DEV_PM_OPS(ksz_spi_pm_ops,
ksz_switch_suspend, ksz_switch_resume);
static struct spi_driver ksz_spi_driver = {
.driver = {
.name = "ksz-switch",
.of_match_table = ksz_dt_ids,
.pm = &ksz_spi_pm_ops,
},
.id_table = ksz_spi_ids,
.probe = ksz_spi_probe,
.remove = ksz_spi_remove,
.shutdown = ksz_spi_shutdown,
};
module_spi_driver(ksz_spi_driver);
MODULE_ALIAS("spi:lan937x");
MODULE_AUTHOR("Tristram Ha <Tristram.Ha@microchip.com>");
MODULE_DESCRIPTION("Microchip ksz Series Switch SPI Driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,13 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Microchip lan937x dev ops headers
* Copyright (C) 2019-2022 Microchip Technology Inc.
*/
#ifndef __LAN937X_CFG_H
#define __LAN937X_CFG_H
extern const struct ksz_dev_ops lan937x_dev_ops;
extern const struct phylink_mac_ops lan937x_phylink_mac_ops;
extern const struct dsa_switch_ops lan937x_switch_ops;
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,217 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Microchip LAN937X switch register definitions
* Copyright (C) 2019-2024 Microchip Technology Inc.
*/
#ifndef __LAN937X_REG_H
#define __LAN937X_REG_H
#define PORT_CTRL_ADDR(port, addr) ((addr) | (((port) + 1) << 12))
/* 0 - Operation */
#define REG_GLOBAL_CTRL_0 0x0007
#define SW_PHY_REG_BLOCK BIT(7)
#define SW_FAST_MODE BIT(3)
#define SW_FAST_MODE_OVERRIDE BIT(2)
#define REG_SW_INT_STATUS__4 0x0010
#define REG_SW_INT_MASK__4 0x0014
#define LUE_INT BIT(31)
#define TRIG_TS_INT BIT(30)
#define APB_TIMEOUT_INT BIT(29)
#define OVER_TEMP_INT BIT(28)
#define HSR_INT BIT(27)
#define PIO_INT BIT(26)
#define POR_READY_INT BIT(25)
#define SWITCH_INT_MASK \
(LUE_INT | TRIG_TS_INT | APB_TIMEOUT_INT | OVER_TEMP_INT | HSR_INT | \
PIO_INT | POR_READY_INT)
#define REG_SW_PORT_INT_STATUS__4 0x0018
#define REG_SW_PORT_INT_MASK__4 0x001C
/* 1 - Global */
#define REG_SW_GLOBAL_OUTPUT_CTRL__1 0x0103
#define SW_CLK125_ENB BIT(1)
#define SW_CLK25_ENB BIT(0)
#define REG_SW_CFG_STRAP_VAL 0x0200
#define SW_CASCADE_ID_CFG BIT(15)
#define SW_VPHY_ADD_CFG BIT(0)
/* 2 - PHY Control */
#define REG_SW_CFG_STRAP_OVR 0x0214
#define SW_VPHY_DISABLE BIT(31)
/* 3 - Operation Control */
#define REG_SW_OPERATION 0x0300
#define SW_DOUBLE_TAG BIT(7)
#define SW_OVER_TEMP_ENABLE BIT(2)
#define SW_RESET BIT(1)
#define REG_SW_LUE_CTRL_0 0x0310
#define SW_VLAN_ENABLE BIT(7)
#define SW_DROP_INVALID_VID BIT(6)
#define SW_AGE_CNT_M GENMASK(5, 3)
#define SW_RESV_MCAST_ENABLE BIT(2)
#define REG_SW_LUE_CTRL_1 0x0311
#define UNICAST_LEARN_DISABLE BIT(7)
#define SW_FLUSH_STP_TABLE BIT(5)
#define SW_FLUSH_MSTP_TABLE BIT(4)
#define SW_SRC_ADDR_FILTER BIT(3)
#define SW_AGING_ENABLE BIT(2)
#define SW_FAST_AGING BIT(1)
#define SW_LINK_AUTO_AGING BIT(0)
#define REG_SW_LUE_CTRL_2 0x0312
#define SW_AGE_CNT_IN_MICROSEC BIT(7)
#define REG_SW_AGE_PERIOD__1 0x0313
#define SW_AGE_PERIOD_7_0_M GENMASK(7, 0)
#define REG_SW_AGE_PERIOD__2 0x0320
#define SW_AGE_PERIOD_19_8_M GENMASK(19, 8)
#define REG_SW_MAC_CTRL_0 0x0330
#define SW_NEW_BACKOFF BIT(7)
#define SW_PAUSE_UNH_MODE BIT(1)
#define SW_AGGR_BACKOFF BIT(0)
#define REG_SW_MAC_CTRL_1 0x0331
#define SW_SHORT_IFG BIT(7)
#define MULTICAST_STORM_DISABLE BIT(6)
#define SW_BACK_PRESSURE BIT(5)
#define FAIR_FLOW_CTRL BIT(4)
#define NO_EXC_COLLISION_DROP BIT(3)
#define SW_LEGAL_PACKET_DISABLE BIT(1)
#define SW_PASS_SHORT_FRAME BIT(0)
#define REG_SW_MAC_CTRL_6 0x0336
#define SW_MIB_COUNTER_FLUSH BIT(7)
#define SW_MIB_COUNTER_FREEZE BIT(6)
/* 4 - LUE */
#define REG_SW_ALU_STAT_CTRL__4 0x041C
#define REG_SW_ALU_VAL_B 0x0424
#define ALU_V_OVERRIDE BIT(31)
#define ALU_V_USE_FID BIT(30)
#define ALU_V_PORT_MAP 0xFF
/* 7 - VPhy */
#define REG_VPHY_IND_ADDR__2 0x075C
#define REG_VPHY_IND_DATA__2 0x0760
#define REG_VPHY_IND_CTRL__2 0x0768
#define VPHY_IND_WRITE BIT(1)
#define VPHY_IND_BUSY BIT(0)
#define REG_VPHY_SPECIAL_CTRL__2 0x077C
#define VPHY_SMI_INDIRECT_ENABLE BIT(15)
#define VPHY_SW_LOOPBACK BIT(14)
#define VPHY_MDIO_INTERNAL_ENABLE BIT(13)
#define VPHY_SPI_INDIRECT_ENABLE BIT(12)
#define VPHY_PORT_MODE_M 0x3
#define VPHY_PORT_MODE_S 8
#define VPHY_MODE_RGMII 0
#define VPHY_MODE_MII_PHY 1
#define VPHY_MODE_SGMII 2
#define VPHY_MODE_RMII_PHY 3
#define VPHY_SW_COLLISION_TEST BIT(7)
#define VPHY_SPEED_DUPLEX_STAT_M 0x7
#define VPHY_SPEED_DUPLEX_STAT_S 2
#define VPHY_SPEED_1000 BIT(4)
#define VPHY_SPEED_100 BIT(3)
#define VPHY_FULL_DUPLEX BIT(2)
/* Port Registers */
/* 0 - Operation */
#define REG_PORT_INT_STATUS 0x001B
#define REG_PORT_INT_MASK 0x001F
#define PORT_TAS_INT BIT(5)
#define PORT_QCI_INT BIT(4)
#define PORT_SGMII_INT BIT(3)
#define PORT_PTP_INT BIT(2)
#define PORT_PHY_INT BIT(1)
#define PORT_ACL_INT BIT(0)
#define PORT_SRC_PHY_INT 1
#define REG_PORT_CTRL_0 0x0020
#define PORT_MAC_LOOPBACK BIT(7)
#define PORT_MAC_REMOTE_LOOPBACK BIT(6)
#define PORT_K2L_INSERT_ENABLE BIT(5)
#define PORT_K2L_DEBUG_ENABLE BIT(4)
#define PORT_TAIL_TAG_ENABLE BIT(2)
#define PORT_QUEUE_SPLIT_ENABLE 0x3
/* 1 - Phy */
#define REG_PORT_T1_PHY_CTRL_BASE 0x0100
#define REG_PORT_TX_PHY_CTRL_BASE 0x0280
/* 3 - xMII */
#define PORT_SGMII_SEL BIT(7)
#define PORT_GRXC_ENABLE BIT(0)
#define PORT_MII_SEL_EDGE BIT(5)
#define REG_PORT_XMII_CTRL_4 0x0304
#define REG_PORT_XMII_CTRL_5 0x0306
#define PORT_DLL_RESET BIT(15)
#define PORT_TUNE_ADJ GENMASK(13, 7)
/* 4 - MAC */
#define REG_PORT_MAC_CTRL_0 0x0400
#define PORT_CHECK_LENGTH BIT(2)
#define PORT_BROADCAST_STORM BIT(1)
#define PORT_JUMBO_PACKET BIT(0)
#define REG_PORT_MAC_CTRL_1 0x0401
#define PORT_BACK_PRESSURE BIT(3)
#define PORT_PASS_ALL BIT(0)
#define PORT_MAX_FR_SIZE 0x404
#define FR_MIN_SIZE 1522
/* 8 - Classification and Policing */
#define REG_PORT_MRI_PRIO_CTRL 0x0801
#define PORT_HIGHEST_PRIO BIT(7)
#define PORT_OR_PRIO BIT(6)
#define PORT_MAC_PRIO_ENABLE BIT(4)
#define PORT_VLAN_PRIO_ENABLE BIT(3)
#define PORT_802_1P_PRIO_ENABLE BIT(2)
#define PORT_DIFFSERV_PRIO_ENABLE BIT(1)
#define PORT_ACL_PRIO_ENABLE BIT(0)
#define P_PRIO_CTRL REG_PORT_MRI_PRIO_CTRL
/* 9 - Shaping */
#define REG_PORT_MTI_CREDIT_INCREMENT 0x091C
/* The port number as per the datasheet */
#define RGMII_2_PORT_NUM 5
#define RGMII_1_PORT_NUM 6
#define LAN937X_RGMII_2_PORT (RGMII_2_PORT_NUM - 1)
#define LAN937X_RGMII_1_PORT (RGMII_1_PORT_NUM - 1)
#define RGMII_1_TX_DELAY_2NS 2
#define RGMII_2_TX_DELAY_2NS 0
#define RGMII_1_RX_DELAY_2NS 0x1B
#define RGMII_2_RX_DELAY_2NS 0x14
#define LAN937X_TAG_LEN 2
#endif