Merge branch 'net-dsa-microchip-add-ptp-support-for-ksz8463'

Bastien Curutchet says:

====================
net: dsa: microchip: add PTP support for KSZ8463

This series adds PTP support for the KSZ8463.

The KSZ8463 differs quite a lot from other KSZ switches supporting PTP:
it has a different interrupt logic and a different 'PTP engine'.

This third iteration addresses two more Sashiko comments from v2. I
didn't address some other Sashiko comments, I said why in the v2 thread.

Patches 1 to 4 add interrupt support for the KSZ8463
Patches 5 to 10 add the PTP support for the KSZ8463
====================

Link: https://patch.msgid.link/20260727-ksz-new-ptp-v3-0-caba39e680e3@bootlin.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-07-31 16:29:58 -07:00
commit 3441aa6109
9 changed files with 729 additions and 140 deletions

View File

@ -36,6 +36,13 @@
#include "ksz8_reg.h"
#include "ksz8.h"
/*
* We use only the high-byte (so odd addresses) of the 16-bits registers to fit
* in the common IRQ framework
*/
#define KSZ8463_REG_ISR 0x191
#define KSZ8463_REG_IER 0x193
/* ksz88x3_drive_strengths - Drive strength mapping for KSZ8863, KSZ8873, ..
* variants.
* This values are documented in KSZ8873 and KSZ8863 datasheets.
@ -181,6 +188,69 @@ static int ksz8_pme_pwrite8(struct ksz_device *dev, int port, int offset, u8 dat
return ksz8_ind_write8(dev, table, (u8)(offset), data);
}
static void ksz8463_irq_mask(struct irq_data *d)
{
struct ksz_irq *kirq = irq_data_get_irq_chip_data(d);
kirq->masked &= ~BIT(d->hwirq);
}
static void ksz8463_irq_unmask(struct irq_data *d)
{
struct ksz_irq *kirq = irq_data_get_irq_chip_data(d);
kirq->masked |= BIT(d->hwirq);
}
static const struct irq_chip ksz8463_irq_chip = {
.name = "ksz8463-irq",
.irq_mask = ksz8463_irq_mask,
.irq_unmask = ksz8463_irq_unmask,
.irq_bus_lock = ksz_irq_bus_lock,
.irq_bus_sync_unlock = ksz_irq_bus_sync_unlock,
};
static int ksz8463_irq_domain_map(struct irq_domain *d,
unsigned int irq, irq_hw_number_t hwirq)
{
irq_set_chip_data(irq, d->host_data);
irq_set_chip_and_handler(irq, &ksz8463_irq_chip, handle_level_irq);
irq_set_noprobe(irq);
return 0;
}
static const struct irq_domain_ops ksz8463_irq_domain_ops = {
.map = ksz8463_irq_domain_map,
.xlate = irq_domain_xlate_twocell,
};
static int ksz8463_girq_setup(struct ksz_device *dev)
{
struct ksz_irq *girq = &dev->girq;
girq->nirqs = 8;
girq->reg_mask = KSZ8463_REG_IER;
girq->reg_status = KSZ8463_REG_ISR;
girq->masked = 0;
snprintf(girq->name, sizeof(girq->name), "ksz8463-girq");
girq->irq_num = dev->irq;
return ksz_irq_common_setup(dev, girq, &ksz8463_irq_domain_ops);
}
static int ksz8463_reset_switch(struct ksz_device *dev)
{
ksz_cfg(dev, KSZ8463_REG_SW_RESET,
KSZ8463_GLOBAL_SOFTWARE_RESET | KSZ8463_PTP_SOFTWARE_RESET,
true);
ksz_cfg(dev, KSZ8463_REG_SW_RESET,
KSZ8463_GLOBAL_SOFTWARE_RESET | KSZ8463_PTP_SOFTWARE_RESET,
false);
return 0;
}
static int ksz8_reset_switch(struct ksz_device *dev)
{
if (ksz_is_ksz88x3(dev)) {
@ -189,11 +259,6 @@ static int ksz8_reset_switch(struct ksz_device *dev)
KSZ8863_GLOBAL_SOFTWARE_RESET | KSZ8863_PCS_RESET, true);
ksz_cfg(dev, KSZ8863_REG_SW_RESET,
KSZ8863_GLOBAL_SOFTWARE_RESET | KSZ8863_PCS_RESET, false);
} else if (ksz_is_ksz8463(dev)) {
ksz_cfg(dev, KSZ8463_REG_SW_RESET,
KSZ8463_GLOBAL_SOFTWARE_RESET, true);
ksz_cfg(dev, KSZ8463_REG_SW_RESET,
KSZ8463_GLOBAL_SOFTWARE_RESET, false);
} else {
/* reset switch */
ksz_write8(dev, REG_POWER_MANAGEMENT_1,
@ -2026,6 +2091,50 @@ static void ksz88x3_config_rmii_clk(struct ksz_device *dev)
KSZ88X3_PORT3_RMII_CLK_INTERNAL, rmii_clk_internal);
}
static void ksz8463_config_cpu_port(struct dsa_switch *ds)
{
struct ksz_device *dev = ds->priv;
struct ksz_port *p;
u8 fiber_ports = 0;
const u32 *masks;
const u16 *regs;
int i;
masks = dev->info->masks;
regs = dev->info->regs;
ksz_cfg(dev, regs[S_TAIL_TAG_CTRL], masks[SW_TAIL_TAG_ENABLE], true);
ksz8_port_setup(dev, dev->cpu_port, true);
for (i = 0; i < dev->phy_port_cnt; i++)
ksz_port_stp_state_set(ds, i, BR_STATE_DISABLED);
for (i = 0; i < dev->phy_port_cnt; i++) {
p = &dev->ports[i];
ksz_port_cfg(dev, i, regs[P_STP_CTRL], PORT_FORCE_FLOW_CTRL,
p->fiber);
if (p->fiber)
fiber_ports |= (1 << i);
}
/* Setup fiber ports. */
if (fiber_ports) {
fiber_ports &= 3;
regmap_update_bits(ksz_regmap_16(dev), KSZ8463_REG_CFG_CTRL,
fiber_ports << PORT_COPPER_MODE_S,
0);
regmap_update_bits(ksz_regmap_16(dev), KSZ8463_REG_DSP_CTRL_6,
COPPER_RECEIVE_ADJUSTMENT, 0);
}
/* Turn off PTP function as the switch enables it by default */
regmap_update_bits(ksz_regmap_16(dev), KSZ8463_PTP_MSG_CONF1,
PTP_ENABLE, 0);
regmap_update_bits(ksz_regmap_16(dev), KSZ8463_PTP_CLK_CTRL,
PTP_CLK_ENABLE, 0);
}
static void ksz8_config_cpu_port(struct dsa_switch *ds)
{
struct ksz_device *dev = ds->priv;
@ -2033,7 +2142,6 @@ static void ksz8_config_cpu_port(struct dsa_switch *ds)
const u32 *masks;
const u16 *regs;
u8 remote;
u8 fiber_ports = 0;
int i;
masks = dev->info->masks;
@ -2064,32 +2172,6 @@ static void ksz8_config_cpu_port(struct dsa_switch *ds)
else
ksz_port_cfg(dev, i, regs[P_STP_CTRL],
PORT_FORCE_FLOW_CTRL, false);
if (p->fiber)
fiber_ports |= (1 << i);
}
if (ksz_is_ksz8463(dev)) {
/* Setup fiber ports. */
if (fiber_ports) {
fiber_ports &= 3;
regmap_update_bits(ksz_regmap_16(dev),
KSZ8463_REG_CFG_CTRL,
fiber_ports << PORT_COPPER_MODE_S,
0);
regmap_update_bits(ksz_regmap_16(dev),
KSZ8463_REG_DSP_CTRL_6,
COPPER_RECEIVE_ADJUSTMENT, 0);
}
/* Turn off PTP function as the switch's proprietary way of
* handling timestamp is not supported in current Linux PTP
* stack implementation.
*/
regmap_update_bits(ksz_regmap_16(dev),
KSZ8463_PTP_MSG_CONF1,
PTP_ENABLE, 0);
regmap_update_bits(ksz_regmap_16(dev),
KSZ8463_PTP_CLK_CTRL,
PTP_CLK_ENABLE, 0);
}
}
@ -2300,6 +2382,150 @@ static void ksz88xx_r_mib_stats64(struct ksz_device *dev, int port)
spin_unlock(&mib->stats64_lock);
}
static int ksz8463_setup(struct dsa_switch *ds)
{
struct ksz_device *dev = ds->priv;
u16 storm_mask, storm_rate;
struct ksz_port *p;
const u16 *regs;
int i, ret;
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;
ret = ksz8463_reset_switch(dev);
if (ret) {
dev_err(ds->dev, "failed to reset switch\n");
return ret;
}
/* set broadcast storm protection 10% rate */
storm_mask = BROADCAST_STORM_RATE;
storm_rate = (BROADCAST_STORM_VALUE * BROADCAST_STORM_PROT_RATE) / 100;
storm_mask = swab16(storm_mask);
storm_rate = swab16(storm_rate);
regmap_update_bits(ksz_regmap_16(dev), regs[S_BROADCAST_CTRL],
storm_mask, storm_rate);
ksz8463_config_cpu_port(ds);
ksz8_enable_stp_addr(dev);
ds->num_tx_queues = dev->info->num_tx_queues;
regmap_update_bits(ksz_regmap_8(dev), regs[S_MULTICAST_CTRL],
MULTICAST_STORM_DISABLE, MULTICAST_STORM_DISABLE);
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;
/* 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);
/* Enable aggressive back off algorithm in half duplex mode. */
ret = ksz_rmw8(dev, REG_SW_CTRL_1, SW_AGGR_BACKOFF, SW_AGGR_BACKOFF);
if (ret)
return ret;
/*
* 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);
if (ret)
return ret;
ksz_cfg(dev, S_REPLACE_VID_CTRL, SW_REPLACE_VID, false);
ksz_cfg(dev, S_MIRROR_CTRL, SW_MIRROR_RX_TX, false);
for (i = 0; i < (dev->info->num_vlans / 4); i++)
ksz8_r_vlan_entries(dev, i);
/* Start with learning disabled on standalone user ports, and enabled
* on the CPU port. In lack of other finer mechanisms, learning on the
* CPU port will avoid flooding bridge local addresses on the network
* in some cases.
*/
p = &dev->ports[dev->cpu_port];
p->learning = true;
if (dev->irq > 0) {
ret = ksz8463_girq_setup(dev);
if (ret)
return ret;
ret = ksz8463_ptp_irq_setup(ds);
if (ret)
goto free_girq;
ret = ksz_ptp_clock_register(ds);
if (ret) {
dev_err(dev->dev, "Failed to register PTP clock: %d\n",
ret);
goto free_ptp_irq;
}
}
ret = ksz_mdio_register(dev);
if (ret < 0) {
dev_err(dev->dev, "failed to register the mdio");
goto ptp_clock_unregister;
}
ret = ksz_dcb_init(dev);
if (ret)
goto ptp_clock_unregister;
/* start switch */
regmap_update_bits(ksz_regmap_8(dev), regs[S_START_CTRL],
SW_START, SW_START);
return 0;
ptp_clock_unregister:
if (dev->irq > 0)
ksz_ptp_clock_unregister(ds);
free_ptp_irq:
if (dev->irq > 0)
ksz8463_ptp_irq_free(ds);
free_girq:
if (dev->irq > 0)
ksz_irq_free(&dev->girq);
return ret;
}
static void ksz8463_teardown(struct dsa_switch *ds)
{
struct ksz_device *dev = ds->priv;
if (dev->irq > 0) {
ksz_ptp_clock_unregister(ds);
ksz8463_ptp_irq_free(ds);
ksz_irq_free(&dev->girq);
}
}
/**
* ksz88x3_drive_strength_write() - Set the drive strength configuration for
* KSZ8863 compatible chip variants.
@ -2445,10 +2671,6 @@ static int ksz8_setup(struct dsa_switch *ds)
/* set broadcast storm protection 10% rate */
storm_mask = BROADCAST_STORM_RATE;
storm_rate = (BROADCAST_STORM_VALUE * BROADCAST_STORM_PROT_RATE) / 100;
if (ksz_is_ksz8463(dev)) {
storm_mask = swab16(storm_mask);
storm_rate = swab16(storm_rate);
}
regmap_update_bits(ksz_regmap_16(dev), regs[S_BROADCAST_CTRL],
storm_mask, storm_rate);
@ -2499,7 +2721,7 @@ static int ksz8_setup(struct dsa_switch *ds)
ksz_cfg(dev, S_MIRROR_CTRL, SW_MIRROR_RX_TX, false);
if (!ksz_is_ksz88x3(dev) && !ksz_is_ksz8463(dev))
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++)
@ -2623,6 +2845,9 @@ static u32 ksz8_get_port_addr(int port, int offset)
static u32 ksz8463_get_port_addr(int port, int offset)
{
if (offset >= KSZ8463_PTP_CLK_CTRL)
return offset + 0x20 * port;
return offset + 0x18 * port;
}
@ -2755,7 +2980,7 @@ static enum dsa_tag_protocol ksz8463_get_tag_protocol(struct dsa_switch *ds,
int port,
enum dsa_tag_protocol mp)
{
return DSA_TAG_PROTO_KSZ9893;
return DSA_TAG_PROTO_KSZ8463;
}
static int ksz8463_connect_tag_protocol(struct dsa_switch *ds,
@ -2763,7 +2988,7 @@ static int ksz8463_connect_tag_protocol(struct dsa_switch *ds,
{
struct ksz_tagger_data *tagger_data;
if (proto != DSA_TAG_PROTO_KSZ9893)
if (proto != DSA_TAG_PROTO_KSZ8463)
return -EPROTONOSUPPORT;
tagger_data = ksz_tagger_data(ds);
@ -2889,8 +3114,8 @@ const struct ksz_dev_ops ksz88xx_dev_ops = {
const struct dsa_switch_ops ksz8463_switch_ops = {
.get_tag_protocol = ksz8463_get_tag_protocol,
.connect_tag_protocol = ksz8463_connect_tag_protocol,
.setup = ksz8_setup,
.teardown = ksz_teardown,
.setup = ksz8463_setup,
.teardown = ksz8463_teardown,
.phy_read = ksz8463_phy_read16,
.phy_write = ksz8463_phy_write16,
.phylink_get_caps = ksz8_phylink_get_caps,
@ -2918,9 +3143,9 @@ const struct dsa_switch_ops ksz8463_switch_ops = {
.port_max_mtu = ksz88xx_max_mtu,
.suspend = ksz_suspend,
.resume = ksz_resume,
.get_ts_info = ksz_get_ts_info,
.get_ts_info = ksz8463_get_ts_info,
.port_hwtstamp_get = ksz_hwtstamp_get,
.port_hwtstamp_set = ksz_hwtstamp_set,
.port_hwtstamp_set = ksz8463_hwtstamp_set,
.port_txtstamp = ksz_port_txtstamp,
.port_rxtstamp = ksz_port_rxtstamp,
.port_setup_tc = ksz8_setup_tc,

View File

@ -786,6 +786,7 @@
#define KSZ8463_REG_SW_RESET 0x126
#define KSZ8463_GLOBAL_SOFTWARE_RESET BIT(0)
#define KSZ8463_PTP_SOFTWARE_RESET BIT(2)
#define KSZ8463_PTP_CLK_CTRL 0x600

View File

@ -2419,14 +2419,14 @@ static void ksz_irq_unmask(struct irq_data *d)
kirq->masked &= ~BIT(d->hwirq);
}
static void ksz_irq_bus_lock(struct irq_data *d)
void ksz_irq_bus_lock(struct irq_data *d)
{
struct ksz_irq *kirq = irq_data_get_irq_chip_data(d);
mutex_lock(&kirq->dev->lock_irq);
}
static void ksz_irq_bus_sync_unlock(struct irq_data *d)
void ksz_irq_bus_sync_unlock(struct irq_data *d)
{
struct ksz_irq *kirq = irq_data_get_irq_chip_data(d);
struct ksz_device *dev = kirq->dev;
@ -2504,14 +2504,15 @@ static irqreturn_t ksz_irq_thread_fn(int irq, void *dev_id)
return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE);
}
static int ksz_irq_common_setup(struct ksz_device *dev, struct ksz_irq *kirq)
int ksz_irq_common_setup(struct ksz_device *dev, struct ksz_irq *kirq,
const struct irq_domain_ops *ops)
{
int ret, n;
kirq->dev = dev;
kirq->domain = irq_domain_create_simple(dev_fwnode(dev->dev), kirq->nirqs, 0,
&ksz_irq_domain_ops, kirq);
kirq->domain = irq_domain_create_simple(dev_fwnode(dev->dev),
kirq->nirqs, 0, ops, kirq);
if (!kirq->domain)
return -ENOMEM;
@ -2543,7 +2544,7 @@ int ksz_girq_setup(struct ksz_device *dev)
girq->irq_num = dev->irq;
return ksz_irq_common_setup(dev, girq);
return ksz_irq_common_setup(dev, girq, &ksz_irq_domain_ops);
}
int ksz_pirq_setup(struct ksz_device *dev, u8 p)
@ -2560,7 +2561,7 @@ int ksz_pirq_setup(struct ksz_device *dev, u8 p)
if (!pirq->irq_num)
return -EINVAL;
return ksz_irq_common_setup(dev, pirq);
return ksz_irq_common_setup(dev, pirq, &ksz_irq_domain_ops);
}
void ksz_teardown(struct dsa_switch *ds)

View File

@ -8,6 +8,7 @@
#define __KSZ_COMMON_H
#include <linux/etherdevice.h>
#include <linux/irqdomain.h>
#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/pcs/pcs-xpcs.h>
@ -193,6 +194,7 @@ struct ksz_port {
struct kernel_hwtstamp_config tstamp_config;
bool hwts_tx_en;
bool hwts_rx_en;
bool last_tx_is_pdelayresp;
struct ksz_irq ptpirq;
struct ksz_ptp_irq ptpmsg_irq[3];
ktime_t tstamp_msg;
@ -508,6 +510,10 @@ 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);
void ksz_irq_bus_lock(struct irq_data *d);
void ksz_irq_bus_sync_unlock(struct irq_data *d);
int ksz_irq_common_setup(struct ksz_device *dev, struct ksz_irq *kirq,
const struct irq_domain_ops *ops);
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);

View File

@ -32,6 +32,15 @@
#define KSZ_PTP_INT_START 13
/*
* PTP interrupt bit is the bit 12 of the 16-bits ISR/IER. But ksz_common.c only
* accesses the high-byte of these registers so the PTP interrupt bit becomes 4.
*/
#define KSZ8463_SRC_PTP_INT 4
#define KSZ8463_PTP_PORT1_INT_START 12
#define KSZ8463_PTP_PORT2_INT_START 14
#define KSZ8463_PTP_INT_START KSZ8463_PTP_PORT1_INT_START
static int ksz_ptp_tou_gpio(struct ksz_device *dev)
{
int ret;
@ -288,6 +297,31 @@ static int ksz_ptp_enable_mode(struct ksz_device *dev)
tag_en ? PTP_ENABLE : 0);
}
int ksz8463_get_ts_info(struct dsa_switch *ds, int port,
struct kernel_ethtool_ts_info *ts)
{
struct ksz_device *dev = ds->priv;
struct ksz_ptp_data *ptp_data;
ptp_data = &dev->ptp_data;
if (!ptp_data->clock)
return -ENODEV;
ts->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE |
SOF_TIMESTAMPING_RX_HARDWARE |
SOF_TIMESTAMPING_RAW_HARDWARE;
ts->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON);
ts->rx_filters = BIT(HWTSTAMP_FILTER_NONE) |
BIT(HWTSTAMP_FILTER_PTP_V2_L2_EVENT);
ts->phc_index = ptp_clock_index(ptp_data->clock);
return 0;
}
/* The function is return back the capability of timestamping feature when
* requested through ethtool -T <interface> utility
*/
@ -332,6 +366,72 @@ int ksz_hwtstamp_get(struct dsa_switch *ds, int port,
return 0;
}
static int ksz8463_set_hwtstamp_config(struct ksz_device *dev,
struct ksz_port *prt,
struct kernel_hwtstamp_config *config)
{
const u16 *regs = dev->info->regs;
int ret;
if (config->flags)
return -EINVAL;
switch (config->tx_type) {
case HWTSTAMP_TX_OFF:
prt->ptpmsg_irq[KSZ8463_SYNC_MSG].ts_en = false;
prt->ptpmsg_irq[KSZ8463_XDREQ_PDRES_MSG].ts_en = false;
prt->hwts_tx_en = false;
break;
case HWTSTAMP_TX_ON:
prt->ptpmsg_irq[KSZ8463_SYNC_MSG].ts_en = true;
prt->ptpmsg_irq[KSZ8463_XDREQ_PDRES_MSG].ts_en = true;
prt->hwts_tx_en = true;
ret = ksz_rmw16(dev, regs[PTP_MSG_CONF1], PTP_1STEP, 0);
if (ret)
return ret;
break;
default:
return -ERANGE;
}
switch (config->rx_filter) {
case HWTSTAMP_FILTER_NONE:
prt->hwts_rx_en = false;
break;
case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
config->rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT;
prt->hwts_rx_en = true;
break;
default:
config->rx_filter = HWTSTAMP_FILTER_NONE;
return -ERANGE;
}
return ksz_ptp_enable_mode(dev);
}
int ksz8463_hwtstamp_set(struct dsa_switch *ds, int port,
struct kernel_hwtstamp_config *config,
struct netlink_ext_ack *extack)
{
struct ksz_device *dev = ds->priv;
struct ksz_port *prt;
int ret;
prt = &dev->ports[port];
ret = ksz8463_set_hwtstamp_config(dev, prt, config);
if (ret)
return ret;
prt->tstamp_config = *config;
return 0;
}
static int ksz_set_hwtstamp_config(struct ksz_device *dev,
struct ksz_port *prt,
struct kernel_hwtstamp_config *config)
@ -562,6 +662,31 @@ static void ksz_ptp_txtstamp_skb(struct ksz_device *dev,
skb_complete_tx_timestamp(skb, &hwtstamps);
}
static void ksz8463_set_pdelayresp_flag(struct ksz_port *prt,
struct sk_buff *skb)
{
struct ptp_header *hdr;
unsigned int type;
u8 ptp_msg_type;
if (!ksz_is_ksz8463(prt->ksz_dev))
return;
if (skb_linearize(skb))
return;
type = ptp_classify_raw(skb);
if (type == PTP_CLASS_NONE)
return;
hdr = ptp_parse_header(skb, type);
if (!hdr)
return;
ptp_msg_type = ptp_get_msgtype(hdr, type);
prt->last_tx_is_pdelayresp = (ptp_msg_type == PTP_MSGTYPE_PDELAY_RESP);
}
void ksz_port_deferred_xmit(struct kthread_work *work)
{
struct ksz_deferred_xmit_work *xmit_work = work_to_xmit_work(work);
@ -578,6 +703,8 @@ void ksz_port_deferred_xmit(struct kthread_work *work)
reinit_completion(&prt->tstamp_msg_comp);
ksz8463_set_pdelayresp_flag(prt, skb);
dsa_enqueue_skb(skb, skb->dev);
ksz_ptp_txtstamp_skb(dev, prt, clone);
@ -944,8 +1071,9 @@ int ksz_ptp_clock_register(struct dsa_switch *ds)
/* Currently only P2P mode is supported. When 802_1AS bit is set, it
* forwards all PTP packets to host port and none to other ports.
*/
ret = ksz_rmw16(dev, regs[PTP_MSG_CONF1], PTP_TC_P2P | PTP_802_1AS,
PTP_TC_P2P | PTP_802_1AS);
ret = ksz_rmw16(dev, regs[PTP_MSG_CONF1],
PTP_TC_P2P | PTP_802_1AS | PTP_ETH_ENABLE,
PTP_TC_P2P | PTP_802_1AS | PTP_ETH_ENABLE);
if (ret)
return ret;
@ -969,7 +1097,22 @@ void ksz_ptp_clock_unregister(struct dsa_switch *ds)
static int ksz_read_ts(struct ksz_port *port, u16 reg, u32 *ts)
{
return ksz_read32(port->ksz_dev, reg, ts);
u16 ts_reg = reg;
/**
* On KSZ8463 DREQ and DRESP timestamps share one interrupt line
* so we have to check the nature of the latest event sent to know
* where the timestamp is located
*/
if (ksz_is_ksz8463(port->ksz_dev)) {
const struct ksz_dev_ops *ops = port->ksz_dev->dev_ops;
if (port->last_tx_is_pdelayresp &&
ts_reg == ops->get_port_addr(port->num, KSZ8463_REG_PORT_DREQ_TS))
ts_reg += KSZ8463_DRESP_TS_OFFSET;
}
return ksz_read32(port->ksz_dev, ts_reg, ts);
}
static irqreturn_t ksz_ptp_msg_thread_fn(int irq, void *dev_id)
@ -1131,6 +1274,128 @@ static int ksz_ptp_msg_irq_setup(struct ksz_port *port, u8 n)
return ret;
}
static int ksz8463_ptp_port_irq_setup(struct ksz_irq *ptpirq,
struct ksz_port *port, int hw_irq)
{
u16 ts_reg[] = {KSZ8463_REG_PORT_SYNC_TS, KSZ8463_REG_PORT_DREQ_TS};
static const char * const name[] = {"sync-msg", "delay-msg"};
const struct ksz_dev_ops *ops = port->ksz_dev->dev_ops;
struct ksz_ptp_irq *ptpmsg_irq;
int ret;
int i;
init_completion(&port->tstamp_msg_comp);
for (i = 0; i < 2; i++) {
ptpmsg_irq = &port->ptpmsg_irq[i];
ptpmsg_irq->num = irq_create_mapping(ptpirq->domain,
hw_irq + i);
if (!ptpmsg_irq->num) {
ret = -EINVAL;
goto release_msg_irq;
}
ptpmsg_irq->port = port;
ptpmsg_irq->ts_reg = ops->get_port_addr(port->num, ts_reg[i]);
strscpy(ptpmsg_irq->name, name[i]);
ret = request_threaded_irq(ptpmsg_irq->num, NULL,
ksz_ptp_msg_thread_fn, IRQF_ONESHOT,
ptpmsg_irq->name, ptpmsg_irq);
if (ret) {
irq_dispose_mapping(ptpmsg_irq->num);
goto release_msg_irq;
}
}
return 0;
release_msg_irq:
while (i--)
ksz_ptp_msg_irq_free(port, i);
return ret;
}
static void ksz8463_ptp_port_irq_teardown(struct ksz_port *port)
{
int i;
for (i = 0; i < 2; i++)
ksz_ptp_msg_irq_free(port, i);
}
int ksz8463_ptp_irq_setup(struct dsa_switch *ds)
{
struct ksz_device *dev = ds->priv;
struct ksz_port *port1, *port2;
struct ksz_irq *ptpirq;
int ret;
port1 = &dev->ports[0];
port2 = &dev->ports[1];
ptpirq = &port1->ptpirq;
ptpirq->irq_num = irq_find_mapping(dev->girq.domain,
KSZ8463_SRC_PTP_INT);
if (!ptpirq->irq_num)
return -EINVAL;
ptpirq->dev = dev;
ptpirq->nirqs = 4;
ptpirq->reg_mask = KSZ8463_PTP_TS_IER;
ptpirq->reg_status = KSZ8463_PTP_TS_ISR;
ptpirq->irq0_offset = KSZ8463_PTP_INT_START;
snprintf(ptpirq->name, sizeof(ptpirq->name), "ptp-irq");
ptpirq->domain = irq_domain_create_linear(dev_fwnode(dev->dev),
ptpirq->nirqs,
&ksz_ptp_irq_domain_ops,
ptpirq);
if (!ptpirq->domain)
return -ENOMEM;
ret = ksz8463_ptp_port_irq_setup(ptpirq, port1,
KSZ8463_PTP_PORT1_INT_START - KSZ8463_PTP_INT_START);
if (ret)
goto release_domain;
ret = ksz8463_ptp_port_irq_setup(ptpirq, port2,
KSZ8463_PTP_PORT2_INT_START - KSZ8463_PTP_INT_START);
if (ret)
goto free_port1;
ret = request_threaded_irq(ptpirq->irq_num, NULL, ksz_ptp_irq_thread_fn,
IRQF_ONESHOT, ptpirq->name, ptpirq);
if (ret)
goto free_port2;
return 0;
free_port2:
ksz8463_ptp_port_irq_teardown(port2);
free_port1:
ksz8463_ptp_port_irq_teardown(port1);
release_domain:
irq_domain_remove(ptpirq->domain);
return ret;
}
void ksz8463_ptp_irq_free(struct dsa_switch *ds)
{
struct ksz_device *dev = ds->priv;
struct ksz_port *port1 = &dev->ports[0];
struct ksz_port *port2 = &dev->ports[1];
struct ksz_irq *ptpirq = &port1->ptpirq;
free_irq(ptpirq->irq_num, ptpirq);
ksz8463_ptp_port_irq_teardown(port2);
ksz8463_ptp_port_irq_teardown(port1);
irq_domain_remove(ptpirq->domain);
}
int ksz_ptp_irq_setup(struct dsa_switch *ds, u8 p)
{
struct ksz_device *dev = ds->priv;

View File

@ -39,17 +39,24 @@ 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 ksz8463_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);
int ksz8463_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);
int ksz8463_ptp_irq_setup(struct dsa_switch *ds);
void ksz8463_ptp_irq_free(struct dsa_switch *ds);
#else
@ -72,11 +79,20 @@ static inline int ksz_ptp_irq_setup(struct dsa_switch *ds, u8 p)
static inline void ksz_ptp_irq_free(struct dsa_switch *ds, u8 p) {}
static inline int ksz8463_ptp_irq_setup(struct dsa_switch *ds)
{
return 0;
}
static inline void ksz8463_ptp_irq_free(struct dsa_switch *ds) {}
#define ksz_get_ts_info NULL
#define ksz8463_get_ts_info NULL
#define ksz_hwtstamp_get NULL
#define ksz_hwtstamp_set NULL
#define ksz8463_hwtstamp_set NULL
#define ksz_port_rxtstamp NULL

View File

@ -121,6 +121,12 @@
#define REG_PTP_PORT_SYNC_TS 0x0C0C
#define REG_PTP_PORT_PDRESP_TS 0x0C10
#define KSZ8463_REG_PORT_DREQ_TS 0x0648
#define KSZ8463_REG_PORT_SYNC_TS 0x064C
#define KSZ8463_REG_PORT_DRESP_TS 0x0650
#define KSZ8463_PTP_TS_ISR 0x068C
#define KSZ8463_PTP_TS_IER 0x068E
#define REG_PTP_PORT_TX_INT_STATUS__2 0x0C14
#define REG_PTP_PORT_TX_INT_ENABLE__2 0x0C16
@ -131,4 +137,8 @@
#define KSZ_XDREQ_MSG 1
#define KSZ_PDRES_MSG 0
#define KSZ8463_DRESP_TS_OFFSET (KSZ8463_REG_PORT_DRESP_TS - KSZ8463_REG_PORT_DREQ_TS)
#define KSZ8463_SYNC_MSG 0
#define KSZ8463_XDREQ_PDRES_MSG 1
#endif

View File

@ -59,6 +59,7 @@ struct tc_action;
#define DSA_TAG_PROTO_MXL_GSW1XX_VALUE 31
#define DSA_TAG_PROTO_MXL862_VALUE 32
#define DSA_TAG_PROTO_NETC_VALUE 33
#define DSA_TAG_PROTO_KSZ8463_VALUE 34
enum dsa_tag_protocol {
DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE,
@ -95,6 +96,7 @@ enum dsa_tag_protocol {
DSA_TAG_PROTO_MXL_GSW1XX = DSA_TAG_PROTO_MXL_GSW1XX_VALUE,
DSA_TAG_PROTO_MXL862 = DSA_TAG_PROTO_MXL862_VALUE,
DSA_TAG_PROTO_NETC = DSA_TAG_PROTO_NETC_VALUE,
DSA_TAG_PROTO_KSZ8463 = DSA_TAG_PROTO_KSZ8463_VALUE,
};
struct dsa_switch;

View File

@ -12,6 +12,7 @@
#include "tag.h"
#define KSZ8463_NAME "ksz8463"
#define KSZ8795_NAME "ksz8795"
#define KSZ9477_NAME "ksz9477"
#define KSZ9893_NAME "ksz9893"
@ -103,72 +104,6 @@ static struct sk_buff *ksz_common_rcv(struct sk_buff *skb,
return skb;
}
/*
* For Ingress (Host -> KSZ8795), 1 byte is added before FCS.
* ---------------------------------------------------------------------------
* DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag(1byte)|FCS(4bytes)
* ---------------------------------------------------------------------------
* tag : each bit represents port (eg, 0x01=port1, 0x02=port2, 0x10=port5)
*
* For Egress (KSZ8795 -> Host), 1 byte is added before FCS.
* ---------------------------------------------------------------------------
* DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag0(1byte)|FCS(4bytes)
* ---------------------------------------------------------------------------
* tag0 : zero-based value represents port
* (eg, 0x0=port1, 0x2=port3, 0x3=port4)
*/
#define KSZ8795_TAIL_TAG_EG_PORT_M GENMASK(1, 0)
#define KSZ8795_TAIL_TAG_OVERRIDE BIT(6)
#define KSZ8795_TAIL_TAG_LOOKUP BIT(7)
static struct sk_buff *ksz8795_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct ethhdr *hdr;
u8 *tag;
if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) {
kfree_skb(skb);
return NULL;
}
/* Tag encoding */
tag = skb_put(skb, KSZ_INGRESS_TAG_LEN);
hdr = skb_eth_hdr(skb);
*tag = dsa_xmit_port_mask(skb, dev);
if (is_link_local_ether_addr(hdr->h_dest))
*tag |= KSZ8795_TAIL_TAG_OVERRIDE;
return skb;
}
static struct sk_buff *ksz8795_rcv(struct sk_buff *skb, struct net_device *dev)
{
u8 *tag;
if (skb_linearize(skb)) {
kfree_skb(skb);
return NULL;
}
tag = skb_tail_pointer(skb) - KSZ_EGRESS_TAG_LEN;
return ksz_common_rcv(skb, dev, tag[0] & KSZ8795_TAIL_TAG_EG_PORT_M,
KSZ_EGRESS_TAG_LEN);
}
static const struct dsa_device_ops ksz8795_netdev_ops = {
.name = KSZ8795_NAME,
.proto = DSA_TAG_PROTO_KSZ8795,
.xmit = ksz8795_xmit,
.rcv = ksz8795_rcv,
.needed_tailroom = KSZ_INGRESS_TAG_LEN,
};
DSA_TAG_DRIVER(ksz8795_netdev_ops);
MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ8795, KSZ8795_NAME);
/*
* For Ingress (Host -> KSZ9477), 2/6 bytes are added before FCS.
* ---------------------------------------------------------------------------
@ -284,6 +219,37 @@ static struct sk_buff *ksz_defer_xmit(struct dsa_port *dp, struct sk_buff *skb)
return NULL;
}
static struct sk_buff *ksz_common_xmit(struct sk_buff *skb,
struct net_device *dev,
bool do_tstamp,
u8 prio,
u8 override_mask)
{
struct dsa_port *dp = dsa_user_to_port(dev);
struct ethhdr *hdr;
u8 *tag;
if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) {
kfree_skb(skb);
return NULL;
}
/* Tag encoding */
if (do_tstamp)
ksz_xmit_timestamp(dp, skb);
tag = skb_put(skb, KSZ_INGRESS_TAG_LEN);
hdr = skb_eth_hdr(skb);
*tag = dsa_xmit_port_mask(skb, dev);
*tag |= prio;
if (is_link_local_ether_addr(hdr->h_dest))
*tag |= override_mask;
return ksz_defer_xmit(dp, skb);
}
static struct sk_buff *ksz9477_xmit(struct sk_buff *skb,
struct net_device *dev)
{
@ -353,6 +319,56 @@ static const struct dsa_device_ops ksz9477_netdev_ops = {
DSA_TAG_DRIVER(ksz9477_netdev_ops);
MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ9477, KSZ9477_NAME);
/*
* For Ingress (Host -> KSZ8795), 1 byte is added before FCS.
* ---------------------------------------------------------------------------
* DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag(1byte)|FCS(4bytes)
* ---------------------------------------------------------------------------
* tag : each bit represents port (eg, 0x01=port1, 0x02=port2, 0x10=port5)
*
* For Egress (KSZ8795 -> Host), 1 byte is added before FCS.
* ---------------------------------------------------------------------------
* DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag0(1byte)|FCS(4bytes)
* ---------------------------------------------------------------------------
* tag0 : zero-based value represents port
* (eg, 0x0=port1, 0x2=port3, 0x3=port4)
*/
#define KSZ8795_TAIL_TAG_EG_PORT_M GENMASK(1, 0)
#define KSZ8795_TAIL_TAG_OVERRIDE BIT(6)
#define KSZ8795_TAIL_TAG_LOOKUP BIT(7)
static struct sk_buff *ksz8795_xmit(struct sk_buff *skb, struct net_device *dev)
{
return ksz_common_xmit(skb, dev, false, 0, KSZ8795_TAIL_TAG_OVERRIDE);
}
static struct sk_buff *ksz8795_rcv(struct sk_buff *skb, struct net_device *dev)
{
u8 *tag;
if (skb_linearize(skb)) {
kfree_skb(skb);
return NULL;
}
tag = skb_tail_pointer(skb) - KSZ_EGRESS_TAG_LEN;
return ksz_common_rcv(skb, dev, tag[0] & KSZ8795_TAIL_TAG_EG_PORT_M,
KSZ_EGRESS_TAG_LEN);
}
static const struct dsa_device_ops ksz8795_netdev_ops = {
.name = KSZ8795_NAME,
.proto = DSA_TAG_PROTO_KSZ8795,
.xmit = ksz8795_xmit,
.rcv = ksz8795_rcv,
.needed_tailroom = KSZ_INGRESS_TAG_LEN,
};
DSA_TAG_DRIVER(ksz8795_netdev_ops);
MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ8795, KSZ8795_NAME);
#define KSZ9893_TAIL_TAG_PRIO GENMASK(4, 3)
#define KSZ9893_TAIL_TAG_OVERRIDE BIT(5)
#define KSZ9893_TAIL_TAG_LOOKUP BIT(6)
@ -362,28 +378,10 @@ static struct sk_buff *ksz9893_xmit(struct sk_buff *skb,
{
u16 queue_mapping = skb_get_queue_mapping(skb);
u8 prio = netdev_txq_to_tc(dev, queue_mapping);
struct dsa_port *dp = dsa_user_to_port(dev);
struct ethhdr *hdr;
u8 *tag;
if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) {
kfree_skb(skb);
return NULL;
}
/* Tag encoding */
ksz_xmit_timestamp(dp, skb);
tag = skb_put(skb, KSZ_INGRESS_TAG_LEN);
hdr = skb_eth_hdr(skb);
*tag = dsa_xmit_port_mask(skb, dev);
*tag |= FIELD_PREP(KSZ9893_TAIL_TAG_PRIO, prio);
if (is_link_local_ether_addr(hdr->h_dest))
*tag |= KSZ9893_TAIL_TAG_OVERRIDE;
return ksz_defer_xmit(dp, skb);
return ksz_common_xmit(skb, dev, true,
FIELD_PREP(KSZ9893_TAIL_TAG_PRIO, prio),
KSZ9893_TAIL_TAG_OVERRIDE);
}
static const struct dsa_device_ops ksz9893_netdev_ops = {
@ -399,6 +397,70 @@ static const struct dsa_device_ops ksz9893_netdev_ops = {
DSA_TAG_DRIVER(ksz9893_netdev_ops);
MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ9893, KSZ9893_NAME);
#define KSZ8463_TAIL_TAG_PRIO GENMASK(4, 3)
#define KSZ8463_TAIL_TAG_EG_PORT_M GENMASK(2, 0)
static struct sk_buff *ksz8463_xmit(struct sk_buff *skb,
struct net_device *dev)
{
u16 queue_mapping = skb_get_queue_mapping(skb);
u8 prio = netdev_txq_to_tc(dev, queue_mapping);
return ksz_common_xmit(skb, dev, false,
FIELD_PREP(KSZ8463_TAIL_TAG_PRIO, prio),
0);
}
static struct sk_buff *ksz8463_rcv(struct sk_buff *skb, struct net_device *dev)
{
unsigned int len = KSZ_EGRESS_TAG_LEN;
struct ptp_header *ptp_hdr;
unsigned int ptp_class;
unsigned int port;
ktime_t ts;
u8 *tag;
if (skb_linearize(skb)) {
kfree_skb(skb);
return NULL;
}
KSZ_SKB_CB(skb)->tstamp = 0;
/* Tag decoding */
tag = skb_tail_pointer(skb) - KSZ_EGRESS_TAG_LEN;
port = tag[0] & KSZ8463_TAIL_TAG_EG_PORT_M;
__skb_push(skb, ETH_HLEN);
ptp_class = ptp_classify_raw(skb);
__skb_pull(skb, ETH_HLEN);
if (ptp_class == PTP_CLASS_NONE)
goto common_rcv;
ptp_hdr = ptp_parse_header(skb, ptp_class);
if (ptp_hdr) {
ts = ksz_decode_tstamp(get_unaligned_be32(&ptp_hdr->reserved2));
KSZ_SKB_CB(skb)->tstamp = ts;
ptp_hdr->reserved2 = 0;
}
common_rcv:
return ksz_common_rcv(skb, dev, port, len);
}
static const struct dsa_device_ops ksz8463_netdev_ops = {
.name = KSZ8463_NAME,
.proto = DSA_TAG_PROTO_KSZ8463,
.xmit = ksz8463_xmit,
.rcv = ksz8463_rcv,
.connect = ksz_connect,
.disconnect = ksz_disconnect,
.needed_tailroom = KSZ_INGRESS_TAG_LEN,
};
DSA_TAG_DRIVER(ksz8463_netdev_ops);
MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ8463, KSZ8463_NAME);
/* For xmit, 2/6 bytes are added before FCS.
* ---------------------------------------------------------------------------
* DA(6bytes)|SA(6bytes)|....|Data(nbytes)|ts(4bytes)|tag0(1byte)|tag1(1byte)|
@ -471,6 +533,7 @@ DSA_TAG_DRIVER(lan937x_netdev_ops);
MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_LAN937X, LAN937X_NAME);
static struct dsa_tag_driver *dsa_tag_driver_array[] = {
&DSA_TAG_DRIVER_NAME(ksz8463_netdev_ops),
&DSA_TAG_DRIVER_NAME(ksz8795_netdev_ops),
&DSA_TAG_DRIVER_NAME(ksz9477_netdev_ops),
&DSA_TAG_DRIVER_NAME(ksz9893_netdev_ops),