summaryrefslogtreecommitdiff
path: root/drivers/net/dsa/microchip
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-10-22 15:58:00 -1000
committerLinus Torvalds <torvalds@linux-foundation.org>2026-10-22 15:58:00 -1000
commit734bef548a96e8774d6eef5dfa2026c6edc4e80b (patch)
treece2520b2602ce3b82f9842490d21836f9838d441 /drivers/net/dsa/microchip
parent864c07de0c7dcbca58b61625dfa2e461ae63b242 (diff)
parent1a2c35c49cc7c6be860038b8442b52424a8f123c (diff)
downloadlinux-next-stable.tar.gz
linux-next-stable.zip
Merge tag 'net-next-7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-nextstable
Pull networking updates from Paolo Abeni: "This includes a significant number of low-priority fixes - especially in the NIC drivers area - that we deferred from the last few RCs in the previous cycle, which can't be easily outlined in the synopsis below. Core & protocols: - Continue the work on removing rtnl_lock dependencies: - per-netns ARP and ND neighbour tables, with the default settings now configurable per netns (inheritance from init_net is controlled by the new net.core.neigh_inherit_init_net sysctl) - RCU-protected configuration and rtnl_lock-less fill_info() for hsr, netkit, sit, vlan and vxlan devices - per-netns device unregistration for ip tunnel and gre devices - use the netdev instance lock instead of rtnl_lock when reading speed and duplex via sysfs - batch the flushing of uncached routes at device unregistration time, relieving rtnl_lock contention on netns dismantle. - Add hardware pacing offload support, admin-controlled via a new rtnetlink attribute, with sch_fq integration (implemented in idpf). - Re-segment BIG TCP GSO packets into GSO packets fitting the egress device limits instead of plain MSS-sized segments, improving throughput for mixed BIG TCP / non-BIG TCP paths by up to 70%. - Generate RSS keys that spread flows over all the RX queues; the previous random keys could leave some queues unused. - Optimize the bridge VLAN forwarding paths: cache the port-VLAN in fdb entries and consider only VLAN members when flooding, for ~10% more Mpps in the fdb forwarding path. - A few TCP/IP performance improvements aimed at efficient cache usage and reducing contention. - Add the TCP ROCCET congestion control, a CUBIC variant aimed at avoiding bufferbloat in cellular networks. - Continue the conversion of getsockopt callbacks to sockopt_t. - Add an ethtool command to list the front-facing ports of an interface, including SFP cages and modules, using the phy_port representation. - Enable BIG TCP support for bareudp tunnels. - Remove the IPV6_ADDRFORM socket option and disallow buggy TCP state transitions (listen() + shutdown() + connect() and connect() + connect(AF_UNSPEC) + listen()). - Remove most BUG_ON()s from core packet processing, replacing them with error paths. Wireless: - Latency improvements, including lockless station get/dump and beacon updates, plus RTNL reduction and locking cleanups. - Infrastructure for non-station TX/RX with MLO. - NAN (Wi-Fi Aware) Instant Communication support and other NAN improvements. - Control frame Integrity Protocol (CIP) and protected TWT support. - Improvements for AP regulatory checks. Netfilter: - Disable br_netfilter in user namespaces and schedule it for removal in 2027. - Restrict the raw table in nft_compat to the specific family, hook and priority. - Prepare the flowtable for mixed IPv4-over-IPv6 tunneling (and vice versa). - Use GFP_KERNEL_ACCOUNT for most netfilter allocations, for better integration with memcg. - Allow discovering conntrack zone filtering support in ctnetlink dumps, and fix nfnetlink_hook handling of large dumps. Deletions: - Move the LLC type 2 sockets out of tree. There are no known users and the code attracts a never-ending stream of LLM-driven fixes. Device drivers: - New drivers: - Meta Platforms NIC (mpnic), the second-generation NIC from Meta - initial skeleton of the Nebula-matrix NICs driver (nbl) - X-Powers AC200/AC300 Fast Ethernet PHYs - Ethernet high-speed NICs: - Intel: - i40e: XDP metadata support for RX hash and VLAN tag - ice: expose the E825 TSPLL state via the DPLL subsystem - nVidia/Mellanox: - move the data direct infrastructure from IB to core, in preparation for netdev support. - reduce the memory used per offloaded TC rule - Meta (fbnic): - support large io_uring zero-copy RX buffers - Microsoft vNIC (mana): - add BQL support - Ethernet NICs consumer, and embedded: - Airoha: - LRO offload support - Cadence (macb): - context swapping for ringparam and MTU changes, avoiding buffer loss under memory pressure - Mucse (rnpgbe): - TX/RX and link status support - NXP (enetc): - SR-IOV improvements and VF support for ENETC v4 - Qualcomm (ppe): - IPQ5424 support - Realtek PCI (r8169): - phylink support - RSS support for RTL8127 - Renesas (ravb): - gPTP support for R-Car Gen4 - Synopsys (stmmac): - XDP multi-buffer support on the TX side - tc mqprio hw queue priority offload - support for RK3568 SGMII, Allwinner H616 EMAC1, UltraRISC DP1000 and Qualcomm sa8255p - Ethernet switches: - Mediatek (mt7530): - fast age and change conduit support - Microchip (ksz): - support for KSZ8995XA/KS8995XA - Microchip (lan966x): - PCIe FDMA support, ~20x throughput increase in PCIe endpoint - Microchip (sparx5): - Time Aware Shaper (taprio) offload - Motorcomm (yt921x): - LED and SerDes PCS support - Realtek (rtl8365mb): - EEE support - Ethernet PHYs: - Broadcom: - enable jumbo frames on BCM54xx - Realtek: - support for RTL8261CE_CG and RTL8261D_VM - MDIO support for RTL838x and RTL839x - WiFi drivers: - Broadcom (brcmfmac): - FT/OKC roaming offload - Intel (iwlwifi): - UHR sniffer support - support for newer UEFI/BIOS tables - MediaTek (mt76): - mt7925 NAN improvements - offchannel/scan improvements and MLO fixes - Morse Micro (mm81x): - monitor mode support - Qualcomm (ath12k): - IPQ5332 platform infrastructure - Realtek (rtw88): - RTL8723BS support - SDIO TX performance improvements - Realtek (rtw89): - P2P_DEVICE support - BT-coex updates for RTL8922D - Bluetooth: - move H:4 reassembly into the Bluetooth core - add generic support for vendor packets - MGMT: add security level changed event - Bluetooth drivers: - btusb: support Bulk Serialization Mode - Intel: support Draco and Titan Lake, allow unsigned firmware - Mediatek: MT7928 support - Qualcomm: QCC2072 support - Broadcom: Synaptics 4384 support" * tag 'net-next-7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (2126 commits) Bluetooth: btusb: disable by default Qualcomm multi-subsystem tipc: fix broadcast sender hang on last acker departure tipc: fix use-after-free of broadcast reception link at node release r8169: clear TX LPI on RTL8168h at link up after phylink conversion tun: Require CAP_NET_ADMIN to attach to an ownerless device net/sched/sch_cake: drop dead nla_nest_end() error check net/sched/sch_cake: widen buffer_used to u64 net/sched/sch_cake: reduce parent backlog when clearing a tin net/sched/sch_cake: serialize reconfiguration with the datapath tg3: Add Datto SIRIS 5 1U Server to restart AER quirk table net/mlx5e: Stop asserting for RTNL when closing channels net: shrink the payload-only part of skb dataref to one bit net: wwan: iosm: validate msg_head before indexing msg_ring net: bgmac: rate limit RX error messages selftests: net: veth: test XDP_TX to a peer with GRO enabled veth: schedule the peer's NAPI after XDP_TX when only GRO is enabled pppoe: delete session hash entries by pointer match net: lan969x: don't disable hardirqs in fdma tx clear net: sparx5/lan969x: do not clobber the PSFP cycle timer update period net: lan969x: fix DSM calendar for empty taxis ...
Diffstat (limited to 'drivers/net/dsa/microchip')
-rw-r--r--drivers/net/dsa/microchip/Kconfig1
-rw-r--r--drivers/net/dsa/microchip/ksz8.c254
-rw-r--r--drivers/net/dsa/microchip/ksz8.h2
-rw-r--r--drivers/net/dsa/microchip/ksz8_reg.h2
-rw-r--r--drivers/net/dsa/microchip/ksz9477.c1
-rw-r--r--drivers/net/dsa/microchip/ksz_common.c136
-rw-r--r--drivers/net/dsa/microchip/ksz_common.h18
-rw-r--r--drivers/net/dsa/microchip/ksz_dcb.c68
-rw-r--r--drivers/net/dsa/microchip/ksz_ptp.c490
-rw-r--r--drivers/net/dsa/microchip/ksz_ptp.h9
-rw-r--r--drivers/net/dsa/microchip/ksz_ptp_reg.h16
-rw-r--r--drivers/net/dsa/microchip/ksz_spi.c33
-rw-r--r--drivers/net/dsa/microchip/lan937x_main.c1
13 files changed, 883 insertions, 148 deletions
diff --git a/drivers/net/dsa/microchip/Kconfig b/drivers/net/dsa/microchip/Kconfig
index c71d3fd5dfeb..75c9b2114afd 100644
--- a/drivers/net/dsa/microchip/Kconfig
+++ b/drivers/net/dsa/microchip/Kconfig
@@ -2,6 +2,7 @@
menuconfig NET_DSA_MICROCHIP_KSZ_COMMON
tristate "Microchip KSZ8XXX/KSZ9XXX/LAN937X series switch support"
depends on NET_DSA
+ select NET_DSA_TAG_KS8995
select NET_DSA_TAG_KSZ
select NET_DSA_TAG_NONE
select NET_IEEE8021Q_HELPERS
diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c
index 941ae9f66f70..3c96dd4581cf 100644
--- a/drivers/net/dsa/microchip/ksz8.c
+++ b/drivers/net/dsa/microchip/ksz8.c
@@ -3,6 +3,7 @@
* Microchip KSZ8XXX series switch driver
*
* It supports the following switches:
+ * - KSZ8995XA (the oldest ancestor)
* - KSZ8463
* - KSZ8863, KSZ8873 aka KSZ88X3
* - KSZ8895, KSZ8864 aka KSZ8895 family
@@ -20,6 +21,7 @@
#include <linux/export.h>
#include <linux/gpio/consumer.h>
#include <linux/if_vlan.h>
+#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_data/microchip-ksz.h>
@@ -253,7 +255,35 @@ static int ksz8463_reset_switch(struct ksz_device *dev)
static int ksz8_reset_switch(struct ksz_device *dev)
{
- if (ksz_is_ksz88x3(dev)) {
+ if (ksz_is_ksz8995xa(dev)) {
+ unsigned int port, reg, val;
+ int ret;
+
+ /* The KSZ8995XA switch itself cannot be reset by software, but
+ * often boot loaders have fun with ports, so reset them. This
+ * reset bit is self-clearing.
+ */
+ for (port = 0; port < dev->info->port_cnt; port++) {
+ ret = ksz_prmw8(dev, port, REG_PORT_STATUS_3,
+ PORT_PHY_SOFT_RESET,
+ PORT_PHY_SOFT_RESET);
+ if (ret)
+ return ret;
+ }
+
+ /* IEEE 802.3 allows up to 500 ms for a PHY reset. */
+ for (port = 0; port < dev->info->port_cnt; port++) {
+ reg = dev->dev_ops->get_port_addr(port, REG_PORT_STATUS_3);
+ ret = regmap_read_poll_timeout(ksz_regmap_8(dev), reg, val,
+ !(val & PORT_PHY_SOFT_RESET),
+ 1000, 600000);
+ if (ret)
+ return ret;
+ }
+
+ /* Some PHYs need additional settling time after reset. */
+ usleep_range(1000, 2000);
+ } else if (ksz_is_ksz88x3(dev)) {
/* reset switch */
ksz_cfg(dev, KSZ8863_REG_SW_RESET,
KSZ8863_GLOBAL_SOFTWARE_RESET | KSZ8863_PCS_RESET, true);
@@ -269,6 +299,31 @@ static int ksz8_reset_switch(struct ksz_device *dev)
return 0;
}
+static int ksz8995xa_change_mtu(struct dsa_switch *ds, int port, int mtu)
+{
+ struct ksz_device *dev = ds->priv;
+ int frame_size;
+ u8 ctrl2 = 0;
+
+ if (!dsa_is_cpu_port(dev->ds, port))
+ return 0;
+
+ frame_size = mtu + VLAN_ETH_HLEN + ETH_FCS_LEN;
+
+ /* The legal packet size bit polarity documented in the KSZ8995XA
+ * manual appears to be wrong. Practical tests show that the same
+ * semantics as the other switches are correct; using the documented
+ * polarity makes the switch hang on big packets.
+ */
+ if (frame_size <= KSZ8995XA_LEGAL_PACKET_SIZE)
+ ctrl2 |= KSZ8863_LEGAL_PACKET_ENABLE;
+ else if (frame_size > KSZ8863_NORMAL_PACKET_SIZE)
+ ctrl2 |= KSZ8863_HUGE_PACKET_ENABLE;
+
+ return ksz_rmw8(dev, REG_SW_CTRL_2, KSZ8863_LEGAL_PACKET_ENABLE |
+ KSZ8863_HUGE_PACKET_ENABLE, ctrl2);
+}
+
static int ksz88xx_change_mtu(struct dsa_switch *ds, int port, int mtu)
{
struct ksz_device *dev = ds->priv;
@@ -325,8 +380,8 @@ static int ksz88xx_max_mtu(struct dsa_switch *ds, int port)
static int ksz8_port_queue_split(struct ksz_device *dev, int port, int queues)
{
- u8 mask_4q, mask_2q;
- u8 reg_4q, reg_2q;
+ u8 mask_4q = 0, mask_2q;
+ u8 reg_4q = 0, reg_2q;
u8 data_4q = 0;
u8 data_2q = 0;
int ret;
@@ -352,6 +407,10 @@ static int ksz8_port_queue_split(struct ksz_device *dev, int port, int queues)
mask_2q = KSZ8873_PORT_2QUEUE_SPLIT_EN;
reg_4q = P1CR1;
reg_2q = P1CR1 + 1;
+ } else if (ksz_is_ksz8995xa(dev)) {
+ /* This switch has no 4way split support */
+ mask_2q = KSZ8795_PORT_2QUEUE_SPLIT_EN;
+ reg_2q = REG_PORT_CTRL_0;
} else {
mask_4q = KSZ8795_PORT_4QUEUE_SPLIT_EN;
mask_2q = KSZ8795_PORT_2QUEUE_SPLIT_EN;
@@ -373,9 +432,11 @@ static int ksz8_port_queue_split(struct ksz_device *dev, int port, int queues)
else if (queues == 2)
data_2q = mask_2q;
- ret = ksz_prmw8(dev, port, reg_4q, mask_4q, data_4q);
- if (ret)
- return ret;
+ if (mask_4q) {
+ ret = ksz_prmw8(dev, port, reg_4q, mask_4q, data_4q);
+ if (ret)
+ return ret;
+ }
return ksz_prmw8(dev, port, reg_2q, mask_2q, data_2q);
}
@@ -970,6 +1031,10 @@ static int ksz8_r_phy_ctrl(struct ksz_device *dev, int port, u16 *val)
if (reg_val & PORT_MDIX_STATUS)
*val |= KSZ886X_CTRL_MDIX_STAT;
+ /* KSZ8995XA has no fancy features in register 0xA */
+ if (ksz_is_ksz8995xa(dev))
+ return 0;
+
ret = ksz_pread8(dev, port, REG_PORT_LINK_MD_CTRL, &reg_val);
if (ret < 0)
return ret;
@@ -1068,8 +1133,10 @@ static int ksz8_r_phy_bmcr(struct ksz_device *dev, u16 port, u16 *val)
if (ctrl & PORT_FORCE_FULL_DUPLEX)
*val |= BMCR_FULLDPLX;
- if (speed & PORT_HP_MDIX)
- *val |= KSZ886X_BMCR_HP_MDIX;
+ if (!ksz_is_ksz8995xa(dev)) {
+ if (speed & PORT_HP_MDIX)
+ *val |= KSZ886X_BMCR_HP_MDIX;
+ }
if (restart & PORT_FORCE_MDIX)
*val |= KSZ886X_BMCR_FORCE_MDI;
@@ -1164,6 +1231,9 @@ static int ksz8_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val)
data |= LPA_LPACK;
break;
case PHY_REG_LINK_MD:
+ if (ksz_is_ksz8995xa(dev))
+ return -EOPNOTSUPP;
+
ret = ksz_pread8(dev, p, REG_PORT_LINK_MD_CTRL, &val1);
if (ret)
return ret;
@@ -1307,13 +1377,15 @@ static int ksz8_w_phy_bmcr(struct ksz_device *dev, u16 port, u16 val)
if (val & BMCR_RESET)
return 0;
- speed = 0;
- if (val & KSZ886X_BMCR_HP_MDIX)
- speed |= PORT_HP_MDIX;
+ if (!ksz_is_ksz8995xa(dev)) {
+ speed = 0;
+ if (val & KSZ886X_BMCR_HP_MDIX)
+ speed |= PORT_HP_MDIX;
- ret = ksz_prmw8(dev, port, regs[P_SPEED_STATUS], PORT_HP_MDIX, speed);
- if (ret)
- return ret;
+ ret = ksz_prmw8(dev, port, regs[P_SPEED_STATUS], PORT_HP_MDIX, speed);
+ if (ret)
+ return ret;
+ }
ctrl = 0;
if (ksz_is_ksz88x3(dev)) {
@@ -1423,11 +1495,17 @@ static int ksz8_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val)
}
break;
case PHY_REG_LINK_MD:
+ if (ksz_is_ksz8995xa(dev))
+ return -EOPNOTSUPP;
+
if (val & PHY_START_CABLE_DIAG)
ksz_port_cfg(dev, p, REG_PORT_LINK_MD_CTRL, PORT_START_CABLE_DIAG, true);
break;
case PHY_REG_PHY_CTRL:
+ if (ksz_is_ksz8995xa(dev))
+ return -EOPNOTSUPP;
+
ret = ksz8_w_phy_ctrl(dev, p, val);
if (ret)
return ret;
@@ -1527,8 +1605,18 @@ static void ksz8_flush_dyn_mac_table(struct dsa_switch *ds, int port)
if (ksz_is_ksz8463(dev)) {
reg = KSZ8463_FLUSH_TABLE_CTRL;
mask = KSZ8463_FLUSH_DYN_MAC_TABLE;
+ } else if (ksz_is_ksz8995xa(dev)) {
+ reg = REG_SW_CTRL_1;
+ mask = SW_FAST_AGING;
}
ksz_cfg(dev, reg, mask, true);
+ if (ksz_is_ksz8995xa(dev)) {
+ /* Allow the 800 us fast-age cycle to finish before restoring
+ * normal ageing and port learning.
+ */
+ usleep_range(1000, 2000);
+ ksz_cfg(dev, reg, mask, false);
+ }
for (index = first; index < cnt; index++) {
if (!(learn[index] & PORT_LEARN_DISABLE))
ksz_pwrite8(dev, index, regs[P_STP_CTRL], learn[index]);
@@ -2043,12 +2131,14 @@ static void ksz8_port_setup(struct ksz_device *dev, int port, bool cpu_port)
ksz8_port_queue_split(dev, port, dev->info->num_tx_queues);
- /* replace priority */
- offset = P_802_1P_CTRL;
- if (ksz_is_ksz8463(dev))
- offset = P1CR2;
- ksz_port_cfg(dev, port, offset,
- masks[PORT_802_1P_REMAPPING], false);
+ if (!ksz_is_ksz8995xa(dev)) {
+ /* replace priority */
+ offset = P_802_1P_CTRL;
+ if (ksz_is_ksz8463(dev))
+ offset = P1CR2;
+ ksz_port_cfg(dev, port, offset,
+ masks[PORT_802_1P_REMAPPING], false);
+ }
if (cpu_port)
member = dsa_user_ports(ds);
@@ -2057,6 +2147,20 @@ static void ksz8_port_setup(struct ksz_device *dev, int port, bool cpu_port)
ksz8_cfg_port_member(dev, port, member);
+ if (ksz_is_ksz8995xa(dev)) {
+ /* Use VID 0 to identify untagged frames in the special tag */
+ ksz_pwrite16(dev, port, REG_PORT_CTRL_VID, 0);
+
+ /* The KSZ8995XA has a special tag format in the front of the frame
+ * that need to be inserted by the CPU and then removed by each
+ * port. PORT_REMOVE_TAG simply means "remove tags coming from the
+ * CPU port" it does not affect ingress packets.
+ */
+ ksz_prmw8(dev, port, REG_PORT_CTRL_0,
+ PORT_INSERT_TAG | PORT_REMOVE_TAG,
+ cpu_port ? PORT_INSERT_TAG : PORT_REMOVE_TAG);
+ }
+
/* Disable all WoL options by default. Otherwise
* ksz_switch_macaddr_get/put logic will not work properly.
* CPU port 4 has no WoL functionality.
@@ -2073,6 +2177,7 @@ static int ksz8_dsa_port_setup(struct dsa_switch *ds, int port)
return 0;
ksz8_port_setup(dev, port, false);
+
return ksz_dcb_init_port(dev, port);
}
@@ -2147,7 +2252,9 @@ static void ksz8_config_cpu_port(struct dsa_switch *ds)
masks = dev->info->masks;
regs = dev->info->regs;
- ksz_cfg(dev, regs[S_TAIL_TAG_CTRL], masks[SW_TAIL_TAG_ENABLE], true);
+ /* KSZ8995XA uses a tag in the header instead of the tail */
+ if (!ksz_is_ksz8995xa(dev))
+ ksz_cfg(dev, regs[S_TAIL_TAG_CTRL], masks[SW_TAIL_TAG_ENABLE], true);
ksz8_port_setup(dev, dev->cpu_port, true);
@@ -2323,6 +2430,10 @@ static int ksz8_enable_stp_addr(struct ksz_device *dev)
{
struct alu_struct alu;
+ /* KSZ8995XA lacks STP */
+ if (ksz_is_ksz8995xa(dev))
+ return 0;
+
/* Setup STP address for STP operation. */
memset(&alu, 0, sizeof(alu));
ether_addr_copy(alu.mac, eth_stp_addr);
@@ -2478,6 +2589,7 @@ static int ksz8463_setup(struct dsa_switch *ds)
if (ret)
goto free_girq;
+ ksz8463_ptp_set_caps(ds);
ret = ksz_ptp_clock_register(ds);
if (ret) {
dev_err(dev->dev, "Failed to register PTP clock: %d\n",
@@ -2650,13 +2762,17 @@ static int ksz8_setup(struct dsa_switch *ds)
struct ksz_port *p;
const u16 *regs;
int i, ret;
+ u8 val;
regs = dev->info->regs;
- dev->vlan_cache = devm_kcalloc(dev->dev, sizeof(struct vlan_table),
- dev->info->num_vlans, GFP_KERNEL);
- if (!dev->vlan_cache)
- return -ENOMEM;
+ /* KSZ8995XA has no SW controlled VLAN handling */
+ if (!ksz_is_ksz8995xa(dev)) {
+ dev->vlan_cache = devm_kcalloc(dev->dev, sizeof(struct vlan_table),
+ dev->info->num_vlans, GFP_KERNEL);
+ if (!dev->vlan_cache)
+ return -ENOMEM;
+ }
ret = ksz8_reset_switch(dev);
if (ret) {
@@ -2685,19 +2801,22 @@ static int ksz8_setup(struct dsa_switch *ds)
ksz_init_mib_timer(dev);
- ds->configure_vlan_while_not_filtering = false;
ds->dscp_prio_mapping_is_global = true;
ds->mtu_enforcement_ingress = true;
- /* We rely on software untagging on the CPU port, so that we
- * can support both tagged and untagged VLANs
- */
- ds->untag_bridge_pvid = true;
+ if (!ksz_is_ksz8995xa(dev)) {
+ ds->configure_vlan_while_not_filtering = false;
- /* VLAN filtering is partly controlled by the global VLAN
- * Enable flag
- */
- ds->vlan_filtering_is_global = true;
+ /* We rely on software untagging on the CPU port, so that we
+ * can support both tagged and untagged VLANs
+ */
+ ds->untag_bridge_pvid = true;
+
+ /* VLAN filtering is partly controlled by the global VLAN
+ * Enable flag
+ */
+ ds->vlan_filtering_is_global = true;
+ }
/* Enable automatic fast aging when link changed detected. */
ksz_cfg(dev, S_LINK_AGING_CTRL, SW_LINK_AUTO_AGING, true);
@@ -2711,9 +2830,10 @@ static int ksz8_setup(struct dsa_switch *ds)
* Make sure unicast VLAN boundary is set as default and
* enable no excessive collision drop.
*/
- ret = ksz_rmw8(dev, REG_SW_CTRL_2,
- UNICAST_VLAN_BOUNDARY | NO_EXC_COLLISION_DROP,
- UNICAST_VLAN_BOUNDARY | NO_EXC_COLLISION_DROP);
+ val = NO_EXC_COLLISION_DROP;
+ if (!ksz_is_ksz8995xa(dev))
+ val |= UNICAST_VLAN_BOUNDARY;
+ ret = ksz_rmw8(dev, REG_SW_CTRL_2, val, val);
if (ret)
return ret;
@@ -2721,11 +2841,15 @@ static int ksz8_setup(struct dsa_switch *ds)
ksz_cfg(dev, S_MIRROR_CTRL, SW_MIRROR_RX_TX, false);
- if (!ksz_is_ksz88x3(dev))
+ if (ksz_is_ksz8995xa(dev))
+ ksz_cfg(dev, REG_SW_CTRL_9, SW_SPECIAL_TAG, true);
+ else if (!ksz_is_ksz88x3(dev))
ksz_cfg(dev, REG_SW_CTRL_19, SW_INS_TAG_ENABLE, true);
- for (i = 0; i < (dev->info->num_vlans / 4); i++)
- ksz8_r_vlan_entries(dev, i);
+ if (!ksz_is_ksz8995xa(dev)) {
+ for (i = 0; i < (dev->info->num_vlans / 4); i++)
+ ksz8_r_vlan_entries(dev, i);
+ }
/* Make sure PME (WoL) is not enabled. If requested, it will
* be enabled by ksz_wol_pre_shutdown(). Otherwise, some PMICs
@@ -2775,6 +2899,7 @@ static int ksz8_setup(struct dsa_switch *ds)
}
if (dev->info->ptp_capable) {
+ ksz_ptp_set_caps(ds);
ret = ksz_ptp_clock_register(ds);
if (ret) {
dev_err(dev->dev, "Failed to register PTP clock: %d\n",
@@ -2976,6 +3101,13 @@ static int ksz8_switch_init(struct ksz_device *dev)
return 0;
}
+static enum dsa_tag_protocol ksz8995xa_get_tag_protocol(struct dsa_switch *ds,
+ int port,
+ enum dsa_tag_protocol mp)
+{
+ return DSA_TAG_PROTO_KS8995;
+}
+
static enum dsa_tag_protocol ksz8463_get_tag_protocol(struct dsa_switch *ds,
int port,
enum dsa_tag_protocol mp)
@@ -3072,6 +3204,16 @@ const struct phylink_mac_ops ksz8_phylink_mac_ops = {
.mac_enable_tx_lpi = ksz_phylink_mac_enable_tx_lpi,
};
+/*
+ * The KS(Z)8995XA has no indirect access, meaning no MIB counters,
+ * no FDB access, and no VLAN handling.
+ */
+const struct ksz_dev_ops ksz8995xa_dev_ops = {
+ .get_port_addr = ksz8_get_port_addr,
+ .cfg_port_member = ksz8_cfg_port_member,
+ .init = ksz8_switch_init,
+};
+
const struct ksz_dev_ops ksz8463_dev_ops = {
.get_port_addr = ksz8463_get_port_addr,
.cfg_port_member = ksz8_cfg_port_member,
@@ -3111,6 +3253,38 @@ const struct ksz_dev_ops ksz88xx_dev_ops = {
.pme_pwrite8 = ksz8_pme_pwrite8,
};
+/*
+ * Restricted operations for KSZ8995XA, so many things are not supported
+ * by this old switch that we need diet DSA operations.
+ */
+const struct dsa_switch_ops ksz8995xa_switch_ops = {
+ .get_tag_protocol = ksz8995xa_get_tag_protocol,
+ .setup = ksz8_setup,
+ .teardown = ksz_teardown,
+ .phy_read = ksz8_phy_read16,
+ .phy_write = ksz8_phy_write16,
+ .phylink_get_caps = ksz8_phylink_get_caps,
+ .port_setup = ksz8_dsa_port_setup,
+ .port_bridge_join = ksz_port_bridge_join,
+ .port_bridge_leave = ksz_port_bridge_leave,
+ .port_set_mac_address = ksz_port_set_mac_address,
+ .port_stp_state_set = ksz_port_stp_state_set,
+ .port_pre_bridge_flags = ksz_port_pre_bridge_flags,
+ .port_bridge_flags = ksz_port_bridge_flags,
+ .port_fast_age = ksz8_flush_dyn_mac_table,
+ .port_mirror_add = ksz8_port_mirror_add,
+ .port_mirror_del = ksz8_port_mirror_del,
+ .port_change_mtu = ksz8995xa_change_mtu,
+ .port_max_mtu = ksz88xx_max_mtu,
+ .port_get_default_prio = ksz_port_get_default_prio,
+ .port_set_default_prio = ksz_port_set_default_prio,
+ .port_get_dscp_prio = ksz_port_get_dscp_prio,
+ .port_add_dscp_prio = ksz_port_add_dscp_prio,
+ .port_del_dscp_prio = ksz_port_del_dscp_prio,
+ .port_get_apptrust = ksz_port_get_apptrust,
+ .port_set_apptrust = ksz_port_set_apptrust,
+};
+
const struct dsa_switch_ops ksz8463_switch_ops = {
.get_tag_protocol = ksz8463_get_tag_protocol,
.connect_tag_protocol = ksz8463_connect_tag_protocol,
diff --git a/drivers/net/dsa/microchip/ksz8.h b/drivers/net/dsa/microchip/ksz8.h
index bc371cc26c6f..9b37c56c9617 100644
--- a/drivers/net/dsa/microchip/ksz8.h
+++ b/drivers/net/dsa/microchip/ksz8.h
@@ -12,11 +12,13 @@
#include <net/dsa.h>
#include "ksz_common.h"
+extern const struct ksz_dev_ops ksz8995xa_dev_ops;
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 ksz8995xa_switch_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;
diff --git a/drivers/net/dsa/microchip/ksz8_reg.h b/drivers/net/dsa/microchip/ksz8_reg.h
index 6bc511da1f7d..f858bce84bfd 100644
--- a/drivers/net/dsa/microchip/ksz8_reg.h
+++ b/drivers/net/dsa/microchip/ksz8_reg.h
@@ -95,6 +95,8 @@
#define SW_LED_LINK_ACT_DUPLEX 2
#define SW_LED_LINK_DUPLEX 3
+#define SW_SPECIAL_TAG BIT(0) /* KSZ8995XA only */
+
#define REG_SW_CTRL_10 0x0C
#define SW_PASS_PAUSE BIT(0)
diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c
index 3ee995545c57..72528a53b67d 100644
--- a/drivers/net/dsa/microchip/ksz9477.c
+++ b/drivers/net/dsa/microchip/ksz9477.c
@@ -1781,6 +1781,7 @@ static int ksz9477_setup(struct dsa_switch *ds)
}
if (dev->info->ptp_capable) {
+ ksz_ptp_set_caps(ds);
ret = ksz_ptp_clock_register(ds);
if (ret) {
dev_err(dev->dev, "Failed to register PTP clock: %d\n",
diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
index 1a9d6f83a023..767c8fe8e819 100644
--- a/drivers/net/dsa/microchip/ksz_common.c
+++ b/drivers/net/dsa/microchip/ksz_common.c
@@ -446,6 +446,20 @@ static const u8 ksz8895_shifts[] = {
[DYNAMIC_MAC_SRC_PORT] = 24,
};
+static const u16 ksz8995xa_regs[] = {
+ [REG_SW_MAC_ADDR] = 0x68,
+ [P_FORCE_CTRL] = 0x0C,
+ [P_LINK_STATUS] = 0x0E,
+ [P_LOCAL_CTRL] = 0x0C,
+ [P_NEG_RESTART_CTRL] = 0x0D,
+ [P_REMOTE_STATUS] = 0x0E,
+ [P_SPEED_STATUS] = 0x09,
+ [P_STP_CTRL] = 0x02,
+ [S_START_CTRL] = 0x01,
+ [S_BROADCAST_CTRL] = 0x06,
+ [S_MULTICAST_CTRL] = 0x04,
+};
+
static const u16 ksz9477_regs[] = {
[REG_SW_MAC_ADDR] = 0x0302,
[P_STP_CTRL] = 0x0B04,
@@ -1161,6 +1175,8 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.supports_mii = {false, false, true},
.supports_rmii = {false, false, true},
.internal_phy = {true, true, false},
+ .n_pins = 12,
+ .n_per_out = 12,
},
[KSZ8563] = {
@@ -1194,6 +1210,8 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.ptp_capable = true,
.wr_table = &ksz8563_register_set,
.rd_table = &ksz8563_register_set,
+ .n_pins = 2,
+ .n_per_out = 3,
},
[KSZ8795] = {
@@ -1377,6 +1395,21 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.internal_phy = {true, true, true, true, false},
},
+ [KSZ8995XA] = {
+ .chip_id = KSZ8995XA_CHIP_ID, /* Also known as KS8995XA */
+ .dev_name = "KSZ8995XA",
+ .cpu_ports = 0x10, /* can be configured as cpu port */
+ .port_cnt = 5, /* total cpu and user ports */
+ .num_tx_queues = 2, /* low/hi priority queues, no more */
+ .num_ipms = 2,
+ .ops = &ksz8995xa_dev_ops,
+ .switch_ops = &ksz8995xa_switch_ops,
+ .phylink_mac_ops = &ksz88x3_phylink_mac_ops,
+ .regs = ksz8995xa_regs,
+ .supports_mii = {true, true, true, true, true},
+ .internal_phy = {true, true, true, true, false},
+ },
+
[KSZ9477] = {
.chip_id = KSZ9477_CHIP_ID,
.dev_name = "KSZ9477",
@@ -1414,6 +1447,8 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.sgmii_port = 7,
.wr_table = &ksz9477_register_set,
.rd_table = &ksz9477_register_set,
+ .n_pins = 2,
+ .n_per_out = 3,
},
[KSZ9896] = {
@@ -1486,6 +1521,41 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.gbit_capable = {true, true, true, true, true, true, true},
},
+ [KSZ9897S] = {
+ .chip_id = KSZ9897S_CHIP_ID,
+ .dev_name = "KSZ9897S",
+ .num_vlans = 4096,
+ .num_alus = 4096,
+ .num_statics = 16,
+ .cpu_ports = 0x7F, /* can be configured as cpu port */
+ .port_cnt = 7, /* total physical port count */
+ .port_nirqs = 2,
+ .num_tx_queues = 4,
+ .num_ipms = 8,
+ .ops = &ksz9477_dev_ops,
+ .switch_ops = &ksz9477_switch_ops,
+ .phylink_mac_ops = &ksz9477_phylink_mac_ops,
+ .phy_errata_9477 = true,
+ .mib_names = ksz9477_mib_names,
+ .mib_cnt = ARRAY_SIZE(ksz9477_mib_names),
+ .reg_mib_cnt = MIB_COUNTER_NUM,
+ .regs = ksz9477_regs,
+ .masks = ksz9477_masks,
+ .shifts = ksz9477_shifts,
+ .xmii_ctrl0 = ksz9477_xmii_ctrl0,
+ .xmii_ctrl1 = ksz9477_xmii_ctrl1,
+ .supports_mii = {false, false, false, false,
+ false, true, false},
+ .supports_rmii = {false, false, false, false,
+ false, true, false},
+ .supports_rgmii = {false, false, false, false,
+ false, true, false},
+ .internal_phy = {true, true, true, true,
+ true, false, false},
+ .gbit_capable = {true, true, true, true, true, true, true},
+ .sgmii_port = 7,
+ },
+
[KSZ9893] = {
.chip_id = KSZ9893_CHIP_ID,
.dev_name = "KSZ9893",
@@ -1544,6 +1614,8 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.internal_phy = {true, true, false},
.gbit_capable = {true, true, true},
.ptp_capable = true,
+ .n_pins = 2,
+ .n_per_out = 3,
},
[KSZ8567] = {
@@ -1581,6 +1653,8 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.gbit_capable = {false, false, false, false, false,
true, true},
.ptp_capable = true,
+ .n_pins = 2,
+ .n_per_out = 3,
},
[KSZ9567] = {
@@ -1615,6 +1689,8 @@ const struct ksz_chip_data ksz_switch_chips[] = {
true, false, false},
.gbit_capable = {true, true, true, true, true, true, true},
.ptp_capable = true,
+ .n_pins = 2,
+ .n_per_out = 3,
},
[LAN9370] = {
@@ -1646,6 +1722,8 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.supports_rgmii = {false, false, false, false, true},
.internal_phy = {true, true, true, true, false},
.ptp_capable = true,
+ .n_pins = 2,
+ .n_per_out = 3,
},
[LAN9371] = {
@@ -1677,6 +1755,8 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.supports_rgmii = {false, false, false, false, true, true},
.internal_phy = {true, true, true, true, false, false},
.ptp_capable = true,
+ .n_pins = 2,
+ .n_per_out = 3,
},
[LAN9372] = {
@@ -1712,6 +1792,8 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.internal_phy = {true, true, true, true,
false, false, true, true},
.ptp_capable = true,
+ .n_pins = 2,
+ .n_per_out = 3,
},
[LAN9373] = {
@@ -1747,6 +1829,8 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.internal_phy = {true, true, true, false,
false, false, true, true},
.ptp_capable = true,
+ .n_pins = 2,
+ .n_per_out = 3,
},
[LAN9374] = {
@@ -1782,6 +1866,8 @@ const struct ksz_chip_data ksz_switch_chips[] = {
.internal_phy = {true, true, true, true,
false, false, true, true},
.ptp_capable = true,
+ .n_pins = 2,
+ .n_per_out = 3,
},
[LAN9646] = {
@@ -2649,6 +2735,10 @@ void ksz_init_mib_timer(struct ksz_device *dev)
{
int i;
+ /* KSZ8995XA lacks MiB features */
+ if (ksz_is_ksz8995xa(dev))
+ return;
+
INIT_DELAYED_WORK(&dev->mib_read, ksz_mib_read_work);
for (i = 0; i < dev->info->port_cnt; i++) {
@@ -2944,11 +3034,15 @@ static int ksz_switch_detect(struct ksz_device *dev)
return -ENODEV;
break;
case KSZ8895_FAMILY_ID:
- if (id2 == KSZ8895_CHIP_ID_95 ||
- id2 == KSZ8895_CHIP_ID_95R)
+ if (id2 == KSZ8895_CHIP_ID_95XA) {
+ dev->chip_id = KSZ8995XA_CHIP_ID;
+ break;
+ } else if (id2 == KSZ8895_CHIP_ID_95 ||
+ id2 == KSZ8895_CHIP_ID_95R) {
dev->chip_id = KSZ8895_CHIP_ID;
- else
+ } else {
return -ENODEV;
+ }
ret = ksz_read8(dev, REG_KSZ8864_CHIP_ID, &id4);
if (ret)
return ret;
@@ -3897,6 +3991,7 @@ int ksz_switch_register(struct ksz_device *dev)
struct device_node *ports;
phy_interface_t interface;
unsigned int port_num;
+ u32 lookup_chip_id;
int ret;
int i;
@@ -3933,7 +4028,30 @@ int ksz_switch_register(struct ksz_device *dev)
if (ret)
return ret;
- info = ksz_lookup_info(dev->chip_id);
+ lookup_chip_id = dev->chip_id;
+
+ /* The KSZ9897S and the KSZ9897R report the same chip ID and differ
+ * only in chip_data: only the S has the SGMII port 7, the R has a
+ * second RGMII port instead. Bit 7 of the port 7 XMII control 0
+ * register is read-only and tells them apart; it reads one on the S.
+ * Compare the KSZ9897S data sheet DS00002394C section 5.2.4.1 with
+ * the KSZ9897R data sheet DS00002330D section 5.2.3.1.
+ *
+ * Only the chip_data entry differs, so dev->chip_id keeps the shared
+ * chip ID and nothing else has to know about the variant.
+ */
+ if (dev->chip_id == KSZ9897_CHIP_ID) {
+ u8 val;
+
+ ret = ksz_read8(dev, KSZ9897_REG_PORT7_XMII_CTRL_0, &val);
+ if (ret)
+ return ret;
+
+ if (val & KSZ9897_PORT7_SGMII_SEL)
+ lookup_chip_id = KSZ9897S_CHIP_ID;
+ }
+
+ info = ksz_lookup_info(lookup_chip_id);
if (!info)
return -ENODEV;
@@ -4029,11 +4147,13 @@ int ksz_switch_register(struct ksz_device *dev)
if (ret)
return ret;
- /* Read MIB counters every 30 seconds to avoid overflow. */
- dev->mib_read_interval = msecs_to_jiffies(5000);
+ if (!ksz_is_ksz8995xa(dev)) {
+ /* Read MIB counters every 30 seconds to avoid overflow. */
+ dev->mib_read_interval = msecs_to_jiffies(5000);
- /* Start the MIB timer. */
- schedule_delayed_work(&dev->mib_read, 0);
+ /* Start the MIB timer. */
+ schedule_delayed_work(&dev->mib_read, 0);
+ }
return ret;
}
diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
index f6104f714a8d..8819670cbb05 100644
--- a/drivers/net/dsa/microchip/ksz_common.h
+++ b/drivers/net/dsa/microchip/ksz_common.h
@@ -139,6 +139,8 @@ struct ksz_chip_data {
u8 sgmii_port;
const struct regmap_access_table *wr_table;
const struct regmap_access_table *rd_table;
+ const u8 n_pins;
+ const u8 n_per_out;
};
struct ksz_irq {
@@ -281,9 +283,11 @@ enum ksz_model {
KSZ88X3,
KSZ8864,
KSZ8895,
+ KSZ8995XA,
KSZ9477,
KSZ9896,
KSZ9897,
+ KSZ9897S,
KSZ9893,
KSZ9563,
KSZ9567,
@@ -789,7 +793,13 @@ static inline bool ksz_is_ksz88x3(struct ksz_device *dev)
static inline bool ksz_is_8895_family(struct ksz_device *dev)
{
return dev->chip_id == KSZ8895_CHIP_ID ||
- dev->chip_id == KSZ8864_CHIP_ID;
+ dev->chip_id == KSZ8864_CHIP_ID ||
+ dev->chip_id == KSZ8995XA_CHIP_ID;
+}
+
+static inline bool ksz_is_ksz8995xa(struct ksz_device *dev)
+{
+ return dev->chip_id == KSZ8995XA_CHIP_ID;
}
static inline bool is_ksz8(struct ksz_device *dev)
@@ -860,6 +870,7 @@ static inline bool ksz_is_sgmii_port(struct ksz_device *dev, int port)
#define KSZ87_CHIP_ID_94 0x6
#define KSZ87_CHIP_ID_95 0x9
#define KSZ88_CHIP_ID_63 0x3
+#define KSZ8895_CHIP_ID_95XA 0x0
#define KSZ8895_CHIP_ID_95 0x4
#define KSZ8895_CHIP_ID_95R 0x6
@@ -874,6 +885,10 @@ static inline bool ksz_is_sgmii_port(struct ksz_device *dev, int port)
#define SKU_ID_KSZ8563 0x3c
#define SKU_ID_KSZ9563 0x1c
+/* KSZ9897 specific register: port 7 XMII control 0 */
+#define KSZ9897_REG_PORT7_XMII_CTRL_0 0x7300
+#define KSZ9897_PORT7_SGMII_SEL BIT(7)
+
/* Driver set switch broadcast storm protection at 10% rate. */
#define BROADCAST_STORM_PROT_RATE 10
@@ -923,6 +938,7 @@ static inline bool ksz_is_sgmii_port(struct ksz_device *dev, int port)
#define KSZ8863_HUGE_PACKET_SIZE 1916
#define KSZ8863_NORMAL_PACKET_SIZE 1536
#define KSZ8_LEGAL_PACKET_SIZE 1518
+#define KSZ8995XA_LEGAL_PACKET_SIZE 1522
#define KSZ9477_MAX_FRAME_SIZE 9000
#define KSZ8873_REG_GLOBAL_CTRL_12 0x0e
diff --git a/drivers/net/dsa/microchip/ksz_dcb.c b/drivers/net/dsa/microchip/ksz_dcb.c
index 7131c5caac54..aa0260f07252 100644
--- a/drivers/net/dsa/microchip/ksz_dcb.c
+++ b/drivers/net/dsa/microchip/ksz_dcb.c
@@ -20,10 +20,12 @@
#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 KSZ8995XA_PORT_BASED_PRIO BIT(4)
#define KSZ8463_REG_TOS_DSCP_CTRL 0x16
#define KSZ88X3_REG_TOS_DSCP_CTRL 0x60
#define KSZ8765_REG_TOS_DSCP_CTRL 0x90
+#define KSZ8995XA_REG_TOS_DSCP_CTRL_7 0x67
#define KSZ9477_REG_SW_MAC_TOS_CTRL 0x033e
#define KSZ9477_SW_TOS_DSCP_REMAP BIT(0)
@@ -98,8 +100,11 @@ static void ksz_get_default_port_prio_reg(struct ksz_device *dev, int *reg,
{
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_ksz8995xa(dev))
+ *mask = KSZ8995XA_PORT_BASED_PRIO;
+ else
+ *mask = KSZ8_PORT_BASED_PRIO_M;
+ *shift = __bf_shf(*mask);
if (ksz_is_ksz8463(dev))
*reg = KSZ8463_REG_PORT_1_CTRL_0;
} else {
@@ -112,31 +117,45 @@ static void ksz_get_default_port_prio_reg(struct ksz_device *dev, int *reg,
/**
* ksz_get_dscp_prio_reg - Retrieves the DSCP-to-priority-mapping register
* @dev: Pointer to the KSZ switch device structure
+ * @dscp: DSCP value for which to retrieve the register
* @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
+ * @shift: Pointer to the bit shift 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.
+ * This function retrieves the register, mask and shift for a DSCP to priority
+ * mapping entry.
*/
-static void ksz_get_dscp_prio_reg(struct ksz_device *dev, int *reg,
- int *per_reg, u8 *mask)
+static void ksz_get_dscp_prio_reg(struct ksz_device *dev, u8 dscp, int *reg,
+ u8 *mask, int *shift)
{
+ int per_reg;
+
+ if (ksz_is_ksz8995xa(dev)) {
+ /* KSZ8995XA stores DSCP groups in descending register order. */
+ *reg = KSZ8995XA_REG_TOS_DSCP_CTRL_7 - dscp / 8;
+ *mask = BIT(0);
+ *shift = dscp % 8;
+ return;
+ }
+
if (ksz_is_ksz87xx(dev) || ksz_is_8895_family(dev)) {
*reg = KSZ8765_REG_TOS_DSCP_CTRL;
- *per_reg = 4;
+ 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;
+ 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;
+ per_reg = 2;
*mask = GENMASK(2, 0);
}
+
+ *reg += dscp / per_reg;
+ *shift = (dscp % per_reg) * (8 / per_reg);
}
/**
@@ -236,10 +255,10 @@ 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)
{
struct ksz_device *dev = ds->priv;
- int reg, per_reg, ret, shift;
+ int reg, ret, shift;
u8 data, mask;
- ksz_get_dscp_prio_reg(dev, &reg, &per_reg, &mask);
+ ksz_get_dscp_prio_reg(dev, dscp, &reg, &mask, &shift);
/* If DSCP remapping is disabled, DSCP bits 3-5 are used as Internal
* Priority Map (IPM)
@@ -260,13 +279,10 @@ int ksz_port_get_dscp_prio(struct dsa_switch *ds, int port, u8 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;
}
@@ -283,15 +299,12 @@ int ksz_port_get_dscp_prio(struct dsa_switch *ds, int port, u8 dscp)
*/
static int ksz_set_global_dscp_entry(struct ksz_device *dev, u8 dscp, u8 ipm)
{
- int reg, per_reg, shift;
+ int reg, shift;
u8 mask;
- ksz_get_dscp_prio_reg(dev, &reg, &per_reg, &mask);
+ ksz_get_dscp_prio_reg(dev, dscp, &reg, &mask, &shift);
- shift = (dscp % per_reg) * (8 / per_reg);
-
- return ksz_rmw8(dev, reg + (dscp / per_reg), mask << shift,
- ipm << shift);
+ return ksz_rmw8(dev, reg, mask << shift, ipm << shift);
}
/**
@@ -441,6 +454,9 @@ static void ksz_apptrust_error(struct ksz_device *dev)
* 2. IEEE_8021QAZ_APP_SEL_DSCP - Differentiated Services Code Point selector
* (lowest priority)
*
+ * KSZ8995XA instead ORs the classification results, so it supports only
+ * one trusted selector at a time.
+ *
* Return: 0 on success, or a negative error code on failure
*/
static int ksz_port_set_apptrust_validate(struct ksz_device *dev, int port,
@@ -449,6 +465,12 @@ static int ksz_port_set_apptrust_validate(struct ksz_device *dev, int port,
int i, j, found;
int j_prev = 0;
+ if (ksz_is_ksz8995xa(dev) && nsel > 1) {
+ dev_err(dev->dev,
+ "KSZ8995XA supports only one apptrust selector at a time\n");
+ return -EOPNOTSUPP;
+ }
+
/* Iterate through the requested selectors */
for (i = 0; i < nsel; i++) {
found = 0;
@@ -552,6 +574,10 @@ int ksz_port_get_apptrust(struct dsa_switch *ds, int port, u8 *sel, int *nsel)
if (ret)
return ret;
+ /* XA's combined classification cannot be reported as a trust order. */
+ if (ksz_is_ksz8995xa(dev) && (data & mask) == mask)
+ return -EOPNOTSUPP;
+
*nsel = 0;
for (i = 0; i < ARRAY_SIZE(ksz_supported_apptrust); i++) {
if (data & map[i].bit)
diff --git a/drivers/net/dsa/microchip/ksz_ptp.c b/drivers/net/dsa/microchip/ksz_ptp.c
index 39cc70d65900..1c59428b159b 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.c
+++ b/drivers/net/dsa/microchip/ksz_ptp.c
@@ -26,6 +26,7 @@
*/
#define KSZ_MAX_DRIFT_CORR 6249999
#define KSZ_MAX_PULSE_WIDTH 125000000LL
+#define KSZ8463_MAX_PULSE_WIDTH 500000LL
#define KSZ_PTP_INC_NS 40ULL /* HW clock is incremented every 40 ns (by 40) */
#define KSZ_PTP_SUBNS_BITS 32
@@ -63,6 +64,17 @@ static int ksz_ptp_tou_gpio(struct ksz_device *dev)
LED_SRC_PTP_GPIO_1 | LED_SRC_PTP_GPIO_2);
}
+static int ksz8463_ptp_tou_reset(struct ksz_device *dev, u8 unit)
+{
+ int ret;
+
+ ret = ksz_rmw16(dev, KSZ8463_TOU_SW_RST, BIT(unit), BIT(unit));
+ if (ret)
+ return ret;
+
+ return ksz_rmw16(dev, KSZ8463_TOU_SW_RST, BIT(unit), 0);
+}
+
static int ksz_ptp_tou_reset(struct ksz_device *dev, u8 unit)
{
u32 data;
@@ -86,7 +98,7 @@ static int ksz_ptp_tou_reset(struct ksz_device *dev, u8 unit)
0);
}
-static int ksz_ptp_tou_pulse_verify(u64 pulse_ns)
+static int ksz_ptp_tou_pulse_verify(u64 pulse_ns, u32 mask)
{
u32 data;
@@ -94,7 +106,7 @@ static int ksz_ptp_tou_pulse_verify(u64 pulse_ns)
return -EINVAL;
data = (pulse_ns / 8);
- if (!FIELD_FIT(TRIG_PULSE_WIDTH_M, data))
+ if ((mask & data) != data)
return -ERANGE;
return 0;
@@ -120,6 +132,28 @@ static int ksz_ptp_tou_target_time_set(struct ksz_device *dev,
return 0;
}
+static int ksz8463_ptp_tou_start(struct ksz_device *dev, u8 unit)
+{
+ u16 data;
+ int ret;
+
+ ret = ksz_rmw16(dev, KSZ8463_TOU_EN, BIT(unit), BIT(unit));
+ if (ret)
+ return ret;
+
+ ret = ksz_read16(dev, KSZ8463_TOU_ACTIVE, &data);
+ if (ret)
+ return ret;
+
+ if (!(data & BIT(unit))) {
+ dev_err(dev->dev, "%s: Trigger unit%d error!\n", __func__,
+ unit);
+ return -EIO;
+ }
+
+ return 0;
+}
+
static int ksz_ptp_tou_start(struct ksz_device *dev, u8 unit)
{
u32 data;
@@ -147,6 +181,56 @@ static int ksz_ptp_tou_start(struct ksz_device *dev, u8 unit)
return 0;
}
+static int ksz8463_ptp_configure_perout(struct ksz_device *dev,
+ struct ptp_perout_request const *request,
+ u32 cycle_width_ns, u32 pulse_width_ns,
+ u8 index)
+{
+ struct ptp_pin_desc *pin = &dev->ptp_data.pin_config[index];
+ u16 cfg_base = KSZ8463_TRIG1_CFG + KSZ8463_TRIGN_CFG_SIZE * pin->chan;
+ u16 data;
+ int ret;
+
+ /* Hardware has only 32 bit for the second field */
+ if ((request->start.sec & 0xffffffff) != request->start.sec)
+ return -EINVAL;
+
+ data = KSZ8463_NOTIFY_BIT |
+ FIELD_PREP(KSZ8463_PATTERN_M, TRIG_POS_PERIOD) |
+ pin->index;
+ ret = ksz_write16(dev, cfg_base + KSZ8463_PATTERN_OFF, data);
+ if (ret)
+ return ret;
+
+ ret = ksz_write32(dev, cfg_base + KSZ8463_CYCLE_WIDTH_OFF,
+ cycle_width_ns);
+ if (ret)
+ return ret;
+
+ /* Set cycle count 0 - Infinite */
+ ret = ksz_write16(dev, cfg_base + KSZ8463_CYCLE_CNT_OFF, 0);
+ if (ret)
+ return ret;
+
+ /* KSZ8463 uses a 8 ns unit value to compute the pulse width */
+ data = (pulse_width_ns / 8);
+ ret = ksz_write16(dev, cfg_base + KSZ8463_PULSE_WIDTH_OFF, data);
+ if (ret)
+ return ret;
+
+ ret = ksz_write32(dev, cfg_base + KSZ8463_TARGET_NSEC,
+ request->start.nsec);
+ if (ret)
+ return ret;
+
+ ret = ksz_write32(dev, cfg_base + KSZ8463_TARGET_SEC,
+ request->start.sec);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
static int ksz_ptp_configure_perout(struct ksz_device *dev,
u32 cycle_width_ns, u32 pulse_width_ns,
struct timespec64 const *target_time,
@@ -183,17 +267,11 @@ static int ksz_ptp_configure_perout(struct ksz_device *dev,
return 0;
}
-static int ksz_ptp_enable_perout(struct ksz_device *dev,
- struct ptp_perout_request const *request,
- int on)
+static int ksz_ptp_get_pin(struct ksz_device *dev,
+ struct ptp_perout_request const *request)
{
struct ksz_ptp_data *ptp_data = &dev->ptp_data;
- u64 req_pulse_width_ns;
- u64 cycle_width_ns;
- u64 pulse_width_ns;
- int pin = 0;
- u32 data32;
- int ret;
+ int pin;
if (request->flags & ~PTP_PEROUT_DUTY_CYCLE)
return -EOPNOTSUPP;
@@ -206,6 +284,119 @@ static int ksz_ptp_enable_perout(struct ksz_device *dev,
if (pin < 0)
return -EINVAL;
+ return pin;
+}
+
+static int ksz_ptp_compute_perout_cycle(struct ksz_device *dev,
+ struct ptp_perout_request const *request,
+ u64 max_pulse_width,
+ u64 *cycle_width_ns,
+ u64 *pulse_width_ns)
+{
+ struct timespec64 tmp;
+
+ if (request->period.sec < 0)
+ return -EINVAL;
+
+ if (!request->period.sec && !request->period.nsec)
+ return -EINVAL;
+
+ tmp.tv_sec = request->period.sec;
+ tmp.tv_nsec = request->period.nsec;
+ *cycle_width_ns = timespec64_to_ns(&tmp);
+ if ((*cycle_width_ns & TRIG_CYCLE_WIDTH_M) != *cycle_width_ns) {
+ *cycle_width_ns = 0;
+ *pulse_width_ns = 0;
+ return -EINVAL;
+ }
+
+ if (request->flags & PTP_PEROUT_DUTY_CYCLE) {
+ *pulse_width_ns = request->on.sec * NSEC_PER_SEC
+ + request->on.nsec;
+ return 0;
+ }
+
+ /* Use a duty cycle of 50%. Maximum pulse width supported by the
+ * hardware is a little bit more than 125 ms.
+ */
+ *pulse_width_ns = (request->period.sec * NSEC_PER_SEC +
+ request->period.nsec) / 2;
+ *pulse_width_ns = min_t(u64, *pulse_width_ns, max_pulse_width);
+
+ return 0;
+}
+
+static int ksz8463_ptp_enable_perout(struct ksz_device *dev,
+ struct ptp_perout_request const *request,
+ int on)
+{
+ struct ksz_ptp_data *ptp_data = &dev->ptp_data;
+ u64 cycle_width_ns;
+ u64 pulse_width_ns;
+ int pin;
+ int ret;
+
+ pin = ksz_ptp_get_pin(dev, request);
+ if (pin < 0)
+ return pin;
+
+ ret = ksz8463_ptp_tou_reset(dev, request->index);
+ if (ret)
+ return ret;
+
+ if (!on) {
+ ptp_data->tou_mode = KSZ_PTP_TOU_IDLE;
+ return 0;
+ }
+
+ ret = ksz_ptp_compute_perout_cycle(dev, request,
+ KSZ8463_MAX_PULSE_WIDTH,
+ &cycle_width_ns, &pulse_width_ns);
+ if (ret)
+ return ret;
+
+ ret = ksz_ptp_tou_pulse_verify(pulse_width_ns,
+ KSZ8463_TRIG_PULSE_WIDTH_M);
+ if (ret)
+ return ret;
+
+ ret = ksz8463_ptp_configure_perout(dev, request,
+ cycle_width_ns, pulse_width_ns,
+ pin);
+ if (ret)
+ return ret;
+
+ ret = ksz_ptp_tou_gpio(dev);
+ if (ret)
+ return ret;
+
+ ret = ksz8463_ptp_tou_start(dev, request->index);
+ if (ret)
+ return ret;
+
+ memcpy(&ptp_data->perout_request, request,
+ sizeof(struct ptp_perout_request));
+ ptp_data->tou_mode = KSZ_PTP_TOU_PEROUT;
+
+ return 0;
+}
+
+static int ksz_ptp_enable_perout(struct ksz_device *dev,
+ struct ptp_perout_request const *request,
+ int on)
+{
+ struct ksz_ptp_data *ptp_data = &dev->ptp_data;
+ struct timespec64 tmp;
+ u64 cycle_width_ns;
+ u64 pulse_width_ns;
+ int pin = 0;
+ u32 data32;
+ int ret;
+
+ pin = ksz_ptp_get_pin(dev, request);
+ if (pin < 0)
+ return pin;
+
data32 = FIELD_PREP(PTP_GPIO_INDEX, pin) |
FIELD_PREP(PTP_TOU_INDEX, request->index);
ret = ksz_rmw32(dev, REG_PTP_UNIT_INDEX__4,
@@ -222,36 +413,19 @@ static int ksz_ptp_enable_perout(struct ksz_device *dev,
return 0;
}
- ptp_data->perout_target_time_first.tv_sec = request->start.sec;
- ptp_data->perout_target_time_first.tv_nsec = request->start.nsec;
-
- ptp_data->perout_period.tv_sec = request->period.sec;
- ptp_data->perout_period.tv_nsec = request->period.nsec;
-
- cycle_width_ns = timespec64_to_ns(&ptp_data->perout_period);
- if ((cycle_width_ns & TRIG_CYCLE_WIDTH_M) != cycle_width_ns)
- return -EINVAL;
-
- if (request->flags & PTP_PEROUT_DUTY_CYCLE) {
- pulse_width_ns = request->on.sec * NSEC_PER_SEC +
- request->on.nsec;
- } else {
- /* Use a duty cycle of 50%. Maximum pulse width supported by the
- * hardware is a little bit more than 125 ms.
- */
- req_pulse_width_ns = (request->period.sec * NSEC_PER_SEC +
- request->period.nsec) / 2;
- pulse_width_ns = min_t(u64, req_pulse_width_ns,
- KSZ_MAX_PULSE_WIDTH);
- }
+ ret = ksz_ptp_compute_perout_cycle(dev, request, KSZ_MAX_PULSE_WIDTH,
+ &cycle_width_ns, &pulse_width_ns);
+ if (ret)
+ return ret;
- ret = ksz_ptp_tou_pulse_verify(pulse_width_ns);
+ ret = ksz_ptp_tou_pulse_verify(pulse_width_ns, TRIG_PULSE_WIDTH_M);
if (ret)
return ret;
+ tmp.tv_sec = request->start.sec;
+ tmp.tv_nsec = request->start.nsec;
ret = ksz_ptp_configure_perout(dev, cycle_width_ns, pulse_width_ns,
- &ptp_data->perout_target_time_first,
- pin);
+ &tmp, pin);
if (ret)
return ret;
@@ -263,6 +437,8 @@ static int ksz_ptp_enable_perout(struct ksz_device *dev,
if (ret)
return ret;
+ memcpy(&ptp_data->perout_request, request,
+ sizeof(struct ptp_perout_request));
ptp_data->tou_mode = KSZ_PTP_TOU_PEROUT;
return 0;
@@ -756,13 +932,14 @@ static int ksz_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
return ret;
}
-static int ksz_ptp_restart_perout(struct ksz_device *dev)
+static int ksz_ptp_prepare_restart_perout(struct ksz_device *dev,
+ struct ptp_perout_request *request)
{
struct ksz_ptp_data *ptp_data = &dev->ptp_data;
s64 now_ns, first_ns, period_ns, next_ns;
- struct ptp_perout_request request;
struct timespec64 next;
struct timespec64 now;
+ struct timespec64 tmp;
unsigned int count;
int ret;
@@ -773,10 +950,14 @@ static int ksz_ptp_restart_perout(struct ksz_device *dev)
return ret;
now_ns = timespec64_to_ns(&now);
- first_ns = timespec64_to_ns(&ptp_data->perout_target_time_first);
+ tmp.tv_sec = ptp_data->perout_request.start.sec;
+ tmp.tv_nsec = ptp_data->perout_request.start.nsec;
+ first_ns = timespec64_to_ns(&tmp);
/* Calculate next perout event based on start time and period */
- period_ns = timespec64_to_ns(&ptp_data->perout_period);
+ tmp.tv_sec = ptp_data->perout_request.period.sec;
+ tmp.tv_nsec = ptp_data->perout_request.period.nsec;
+ period_ns = timespec64_to_ns(&tmp);
if (first_ns < now_ns) {
count = div_u64(now_ns - first_ns, period_ns);
@@ -791,41 +972,110 @@ static int ksz_ptp_restart_perout(struct ksz_device *dev)
/* Restart periodic output signal */
next = ns_to_timespec64(next_ns);
- request.start.sec = next.tv_sec;
- request.start.nsec = next.tv_nsec;
- request.period.sec = ptp_data->perout_period.tv_sec;
- request.period.nsec = ptp_data->perout_period.tv_nsec;
- request.index = 0;
- request.flags = 0;
+ memcpy(request, &ptp_data->perout_request,
+ sizeof(struct ptp_perout_request));
+ request->start.sec = next.tv_sec;
+ request->start.nsec = next.tv_nsec;
+
+ return 0;
+}
+
+static int ksz_ptp_restart_perout(struct ksz_device *dev)
+{
+ struct ptp_perout_request request;
+ int ret;
+
+ ret = ksz_ptp_prepare_restart_perout(dev, &request);
+ if (ret)
+ return ret;
return ksz_ptp_enable_perout(dev, &request, 1);
}
-static int ksz_ptp_settime(struct ptp_clock_info *ptp,
- const struct timespec64 *ts)
+static int ksz8463_ptp_restart_perout(struct ksz_device *dev)
{
- struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
- struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
- const u16 *regs = dev->info->regs;
+ struct ptp_perout_request request;
int ret;
- mutex_lock(&ptp_data->lock);
+ ret = ksz_ptp_prepare_restart_perout(dev, &request);
+ if (ret)
+ return ret;
+
+ return ksz8463_ptp_enable_perout(dev, &request, 1);
+}
+
+static int __ksz_ptp_settime(struct ksz_device *dev,
+ const struct timespec64 *ts)
+{
+ const u16 *regs = dev->info->regs;
+ int ret;
/* Write to shadow registers and Load PTP clock */
ret = ksz_write16(dev, regs[PTP_RTC_SUB_NANOSEC], PTP_RTC_0NS);
if (ret)
- goto unlock;
+ return ret;
ret = ksz_write32(dev, regs[PTP_RTC_NANOSEC], ts->tv_nsec);
if (ret)
- goto unlock;
+ return ret;
ret = ksz_write32(dev, regs[PTP_RTC_SEC], ts->tv_sec);
if (ret)
- goto unlock;
+ return ret;
ret = ksz_rmw16(dev, regs[PTP_CLK_CTRL], PTP_LOAD_TIME, PTP_LOAD_TIME);
if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int ksz8463_ptp_settime(struct ptp_clock_info *ptp,
+ const struct timespec64 *ts)
+{
+ struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
+ struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
+ int ret;
+
+ mutex_lock(&ptp_data->lock);
+
+ ret = __ksz_ptp_settime(dev, ts);
+ if (ret)
+ goto unlock;
+
+ switch (ptp_data->tou_mode) {
+ case KSZ_PTP_TOU_IDLE:
+ break;
+
+ case KSZ_PTP_TOU_PEROUT:
+ ret = ksz8463_ptp_restart_perout(dev);
+ if (ret)
+ goto unlock;
+
+ break;
+ }
+
+ spin_lock_bh(&ptp_data->clock_lock);
+ ptp_data->clock_time = *ts;
+ spin_unlock_bh(&ptp_data->clock_lock);
+
+unlock:
+ mutex_unlock(&ptp_data->lock);
+
+ return ret;
+}
+
+static int ksz_ptp_settime(struct ptp_clock_info *ptp,
+ const struct timespec64 *ts)
+{
+ struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
+ struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
+ int ret;
+
+ mutex_lock(&ptp_data->lock);
+
+ ret = __ksz_ptp_settime(dev, ts);
+ if (ret)
goto unlock;
switch (ptp_data->tou_mode) {
@@ -890,18 +1140,13 @@ unlock:
return ret;
}
-static int ksz_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
+static int __ksz_ptp_adjtime(struct ksz_device *dev, s64 delta)
{
- struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
- struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
- struct timespec64 delta64 = ns_to_timespec64(delta);
const u16 *regs = dev->info->regs;
s32 sec, nsec;
u16 data16;
int ret;
- mutex_lock(&ptp_data->lock);
-
/* do not use ns_to_timespec64(),
* both sec and nsec are subtracted by hw
*/
@@ -909,15 +1154,15 @@ static int ksz_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
ret = ksz_write32(dev, regs[PTP_RTC_NANOSEC], abs(nsec));
if (ret)
- goto unlock;
+ return ret;
ret = ksz_write32(dev, regs[PTP_RTC_SEC], abs(sec));
if (ret)
- goto unlock;
+ return ret;
ret = ksz_read16(dev, regs[PTP_CLK_CTRL], &data16);
if (ret)
- goto unlock;
+ return ret;
data16 |= PTP_STEP_ADJ;
@@ -929,6 +1174,56 @@ static int ksz_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
ret = ksz_write16(dev, regs[PTP_CLK_CTRL], data16);
if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int ksz8463_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+ struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
+ struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
+ struct timespec64 delta64 = ns_to_timespec64(delta);
+ int ret;
+
+ mutex_lock(&ptp_data->lock);
+
+ ret = __ksz_ptp_adjtime(dev, delta);
+ if (ret)
+ goto unlock;
+
+ switch (ptp_data->tou_mode) {
+ case KSZ_PTP_TOU_IDLE:
+ break;
+
+ case KSZ_PTP_TOU_PEROUT:
+ ret = ksz8463_ptp_restart_perout(dev);
+ if (ret)
+ goto unlock;
+
+ break;
+ }
+
+ spin_lock_bh(&ptp_data->clock_lock);
+ ptp_data->clock_time = timespec64_add(ptp_data->clock_time, delta64);
+ spin_unlock_bh(&ptp_data->clock_lock);
+
+unlock:
+ mutex_unlock(&ptp_data->lock);
+ return ret;
+}
+
+static int ksz_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+ struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
+ struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
+ struct timespec64 delta64 = ns_to_timespec64(delta);
+ int ret;
+
+ mutex_lock(&ptp_data->lock);
+
+ ret = __ksz_ptp_adjtime(dev, delta);
+ if (ret)
goto unlock;
switch (ptp_data->tou_mode) {
@@ -952,6 +1247,26 @@ unlock:
return ret;
}
+static int ksz8463_ptp_enable(struct ptp_clock_info *ptp,
+ struct ptp_clock_request *req, int on)
+{
+ struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
+ struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
+ int ret;
+
+ switch (req->type) {
+ case PTP_CLK_REQ_PEROUT:
+ mutex_lock(&ptp_data->lock);
+ ret = ksz8463_ptp_enable_perout(dev, &req->perout, on);
+ mutex_unlock(&ptp_data->lock);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return ret;
+}
+
static int ksz_ptp_enable(struct ptp_clock_info *ptp,
struct ptp_clock_request *req, int on)
{
@@ -1028,17 +1343,33 @@ static int ksz_ptp_start_clock(struct ksz_device *dev)
return 0;
}
-int ksz_ptp_clock_register(struct dsa_switch *ds)
+void ksz8463_ptp_set_caps(struct dsa_switch *ds)
+{
+ struct ksz_device *dev = ds->priv;
+ struct ksz_ptp_data *ptp_data;
+
+ ptp_data = &dev->ptp_data;
+
+ ptp_data->caps.owner = THIS_MODULE;
+ snprintf(ptp_data->caps.name, 16, "Microchip Clock");
+ ptp_data->caps.max_adj = KSZ_MAX_DRIFT_CORR;
+ ptp_data->caps.gettime64 = ksz_ptp_gettime;
+ ptp_data->caps.settime64 = ksz8463_ptp_settime;
+ ptp_data->caps.adjfine = ksz_ptp_adjfine;
+ ptp_data->caps.adjtime = ksz8463_ptp_adjtime;
+ ptp_data->caps.do_aux_work = ksz_ptp_do_aux_work;
+ ptp_data->caps.enable = ksz8463_ptp_enable;
+ ptp_data->caps.verify = ksz_ptp_verify_pin;
+ ptp_data->caps.n_pins = dev->info->n_pins;
+ ptp_data->caps.n_per_out = dev->info->n_per_out;
+}
+
+void ksz_ptp_set_caps(struct dsa_switch *ds)
{
struct ksz_device *dev = ds->priv;
- const u16 *regs = dev->info->regs;
struct ksz_ptp_data *ptp_data;
- int ret;
- u8 i;
ptp_data = &dev->ptp_data;
- mutex_init(&ptp_data->lock);
- spin_lock_init(&ptp_data->clock_lock);
ptp_data->caps.owner = THIS_MODULE;
snprintf(ptp_data->caps.name, 16, "Microchip Clock");
@@ -1050,14 +1381,27 @@ int ksz_ptp_clock_register(struct dsa_switch *ds)
ptp_data->caps.do_aux_work = ksz_ptp_do_aux_work;
ptp_data->caps.enable = ksz_ptp_enable;
ptp_data->caps.verify = ksz_ptp_verify_pin;
- ptp_data->caps.n_pins = KSZ_PTP_N_GPIO;
- ptp_data->caps.n_per_out = 3;
+ ptp_data->caps.n_pins = dev->info->n_pins;
+ ptp_data->caps.n_per_out = dev->info->n_per_out;
+}
+
+int ksz_ptp_clock_register(struct dsa_switch *ds)
+{
+ struct ksz_device *dev = ds->priv;
+ const u16 *regs = dev->info->regs;
+ struct ksz_ptp_data *ptp_data;
+ int ret;
+ u8 i;
+
+ ptp_data = &dev->ptp_data;
+ mutex_init(&ptp_data->lock);
+ spin_lock_init(&ptp_data->clock_lock);
ret = ksz_ptp_start_clock(dev);
if (ret)
return ret;
- for (i = 0; i < KSZ_PTP_N_GPIO; i++) {
+ for (i = 0; i < dev->info->n_pins; i++) {
struct ptp_pin_desc *ptp_pin = &ptp_data->pin_config[i];
snprintf(ptp_pin->name,
diff --git a/drivers/net/dsa/microchip/ksz_ptp.h b/drivers/net/dsa/microchip/ksz_ptp.h
index 7067ec9bd1e6..22898a22723c 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.h
+++ b/drivers/net/dsa/microchip/ksz_ptp.h
@@ -12,7 +12,7 @@
#include <linux/ptp_clock_kernel.h>
-#define KSZ_PTP_N_GPIO 2
+#define KSZ_PTP_N_GPIO 12
enum ksz_ptp_tou_mode {
KSZ_PTP_TOU_IDLE,
@@ -29,10 +29,11 @@ struct ksz_ptp_data {
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;
+ struct ptp_perout_request perout_request;
};
+void ksz_ptp_set_caps(struct dsa_switch *ds);
+void ksz8463_ptp_set_caps(struct dsa_switch *ds);
int ksz_ptp_clock_register(struct dsa_switch *ds);
void ksz_ptp_clock_unregister(struct dsa_switch *ds);
@@ -65,6 +66,8 @@ struct ksz_ptp_data {
struct mutex lock;
};
+static inline void ksz_ptp_set_caps(struct dsa_switch *ds) { }
+static inline void ksz8463_ptp_set_caps(struct dsa_switch *ds) { }
static inline int ksz_ptp_clock_register(struct dsa_switch *ds)
{
return 0;
diff --git a/drivers/net/dsa/microchip/ksz_ptp_reg.h b/drivers/net/dsa/microchip/ksz_ptp_reg.h
index 65ea8577af75..16f117fa5d49 100644
--- a/drivers/net/dsa/microchip/ksz_ptp_reg.h
+++ b/drivers/net/dsa/microchip/ksz_ptp_reg.h
@@ -51,6 +51,22 @@
#define REG_PTP_UNIT_INDEX__4 0x0520
+#define KSZ8463_TOU_ACTIVE 0x202
+#define KSZ8463_TOU_EN 0x206
+#define KSZ8463_TOU_SW_RST 0x208
+
+#define KSZ8463_TRIGN_CFG_SIZE 0x20
+#define KSZ8463_TRIG1_CFG 0x220
+#define KSZ8463_TARGET_NSEC 0x000
+#define KSZ8463_TARGET_SEC 0x004
+#define KSZ8463_PATTERN_OFF 0x008
+#define KSZ8463_NOTIFY_BIT BIT(8)
+#define KSZ8463_PATTERN_M GENMASK(6, 4)
+#define KSZ8463_PULSE_WIDTH_OFF 0x00A
+#define KSZ8463_TRIG_PULSE_WIDTH_M GENMASK(15, 0)
+#define KSZ8463_CYCLE_WIDTH_OFF 0x00C
+#define KSZ8463_CYCLE_CNT_OFF 0x010
+
#define PTP_GPIO_INDEX GENMASK(19, 16)
#define PTP_TSI_INDEX BIT(8)
#define PTP_TOU_INDEX GENMASK(1, 0)
diff --git a/drivers/net/dsa/microchip/ksz_spi.c b/drivers/net/dsa/microchip/ksz_spi.c
index 373e9054947c..e3bb9e604186 100644
--- a/drivers/net/dsa/microchip/ksz_spi.c
+++ b/drivers/net/dsa/microchip/ksz_spi.c
@@ -161,7 +161,8 @@ static int ksz_spi_probe(struct spi_device *spi)
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 ||
+ else if (chip->chip_id == KSZ8995XA_CHIP_ID ||
+ chip->chip_id == KSZ8895_CHIP_ID ||
chip->chip_id == KSZ8864_CHIP_ID)
regmap_config = ksz8863_regmap_config;
else
@@ -185,7 +186,10 @@ static int ksz_spi_probe(struct spi_device *spi)
dev->pdata = spi->dev.platform_data;
/* setup spi */
- spi->mode = SPI_MODE_3;
+ if (chip->chip_id == KSZ8995XA_CHIP_ID)
+ spi->mode = SPI_MODE_0;
+ else
+ spi->mode = SPI_MODE_3;
ret = spi_setup(spi);
if (ret)
return ret;
@@ -224,6 +228,25 @@ static void ksz_spi_shutdown(struct spi_device *spi)
}
static const struct of_device_id ksz_dt_ids[] = {
+ /*
+ * Legacy Micrel bindings. In 2015 Microchip acquired
+ * Micrel which is the originator of the KSZ series, and
+ * devices branded for Micrel already existed, as well as
+ * some device tree bindings. These two products are identical
+ * to the same Microchip products.
+ */
+ {
+ .compatible = "micrel,ksz8864",
+ .data = &ksz_switch_chips[KSZ8864]
+ },
+ {
+ .compatible = "micrel,ksz8795",
+ .data = &ksz_switch_chips[KSZ8795]
+ },
+ {
+ .compatible = "micrel,ks8995",
+ .data = &ksz_switch_chips[KSZ8995XA]
+ },
{
.compatible = "microchip,ksz8463",
.data = &ksz_switch_chips[KSZ8463]
@@ -257,6 +280,10 @@ static const struct of_device_id ksz_dt_ids[] = {
.data = &ksz_switch_chips[KSZ8895]
},
{
+ .compatible = "microchip,ksz8995xa",
+ .data = &ksz_switch_chips[KSZ8995XA]
+ },
+ {
.compatible = "microchip,ksz9477",
.data = &ksz_switch_chips[KSZ9477]
},
@@ -317,6 +344,7 @@ static const struct of_device_id ksz_dt_ids[] = {
MODULE_DEVICE_TABLE(of, ksz_dt_ids);
static const struct spi_device_id ksz_spi_ids[] = {
+ { "ks8995" },
{ "ksz8463" },
{ "ksz8765" },
{ "ksz8794" },
@@ -325,6 +353,7 @@ static const struct spi_device_id ksz_spi_ids[] = {
{ "ksz8864" },
{ "ksz8873" },
{ "ksz8895" },
+ { "ksz8995xa" },
{ "ksz9477" },
{ "ksz9896" },
{ "ksz9897" },
diff --git a/drivers/net/dsa/microchip/lan937x_main.c b/drivers/net/dsa/microchip/lan937x_main.c
index 86ce3a86705f..3a209122fc7d 100644
--- a/drivers/net/dsa/microchip/lan937x_main.c
+++ b/drivers/net/dsa/microchip/lan937x_main.c
@@ -867,6 +867,7 @@ static int lan937x_setup(struct dsa_switch *ds)
}
}
+ ksz_ptp_set_caps(ds);
ret = ksz_ptp_clock_register(ds);
if (ret) {
dev_err(dev->dev, "Failed to register PTP clock: %d\n",