mirror of
https://github.com/torvalds/linux.git
synced 2026-09-23 05:04:02 +02:00
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:
commit
3441aa6109
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user