diff options
Diffstat (limited to 'drivers/net/ethernet/mscc/ocelot.c')
-rw-r--r-- | drivers/net/ethernet/mscc/ocelot.c | 146 |
1 files changed, 78 insertions, 68 deletions
diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index 867c680f5917..70bf8c67d7ef 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -5,6 +5,7 @@ * Copyright (c) 2017 Microsemi Corporation */ #include <linux/if_bridge.h> +#include <soc/mscc/ocelot_vcap.h> #include "ocelot.h" #include "ocelot_vcap.h" @@ -107,6 +108,13 @@ static void ocelot_vcap_enable(struct ocelot *ocelot, int port) ocelot_write_gix(ocelot, ANA_PORT_VCAP_S2_CFG_S2_ENA | ANA_PORT_VCAP_S2_CFG_S2_IP6_CFG(0xa), ANA_PORT_VCAP_S2_CFG, port); + + ocelot_write_gix(ocelot, ANA_PORT_VCAP_CFG_S1_ENA, + ANA_PORT_VCAP_CFG, port); + + ocelot_rmw_gix(ocelot, REW_PORT_CFG_ES0_EN, + REW_PORT_CFG_ES0_EN, + REW_PORT_CFG, port); } static inline u32 ocelot_vlant_read_vlanaccess(struct ocelot *ocelot) @@ -191,12 +199,28 @@ static int ocelot_port_set_native_vlan(struct ocelot *ocelot, int port, return 0; } -void ocelot_port_vlan_filtering(struct ocelot *ocelot, int port, - bool vlan_aware) +int ocelot_port_vlan_filtering(struct ocelot *ocelot, int port, + bool vlan_aware, struct switchdev_trans *trans) { struct ocelot_port *ocelot_port = ocelot->ports[port]; u32 val; + if (switchdev_trans_ph_prepare(trans)) { + struct ocelot_vcap_block *block = &ocelot->block[VCAP_IS1]; + struct ocelot_vcap_filter *filter; + + list_for_each_entry(filter, &block->rules, list) { + if (filter->ingress_port_mask & BIT(port) && + filter->action.vid_replace_ena) { + dev_err(ocelot->dev, + "Cannot change VLAN state with vlan modify rules active\n"); + return -EBUSY; + } + } + + return 0; + } + ocelot_port->vlan_aware = vlan_aware; if (vlan_aware) @@ -210,6 +234,8 @@ void ocelot_port_vlan_filtering(struct ocelot *ocelot, int port, ANA_PORT_VLAN_CFG, port); ocelot_port_set_native_vlan(ocelot, port, ocelot_port->vid); + + return 0; } EXPORT_SYMBOL(ocelot_port_vlan_filtering); @@ -413,21 +439,20 @@ void ocelot_port_disable(struct ocelot *ocelot, int port) } EXPORT_SYMBOL(ocelot_port_disable); -int ocelot_port_add_txtstamp_skb(struct ocelot_port *ocelot_port, - struct sk_buff *skb) +void ocelot_port_add_txtstamp_skb(struct ocelot *ocelot, int port, + struct sk_buff *clone) { - struct skb_shared_info *shinfo = skb_shinfo(skb); - struct ocelot *ocelot = ocelot_port->ocelot; - - if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP && - ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) { - shinfo->tx_flags |= SKBTX_IN_PROGRESS; - /* Store timestamp ID in cb[0] of sk_buff */ - skb->cb[0] = ocelot_port->ts_id % 4; - skb_queue_tail(&ocelot_port->tx_skbs, skb); - return 0; - } - return -ENODATA; + struct ocelot_port *ocelot_port = ocelot->ports[port]; + + spin_lock(&ocelot_port->ts_id_lock); + + skb_shinfo(clone)->tx_flags |= SKBTX_IN_PROGRESS; + /* Store timestamp ID in cb[0] of sk_buff */ + clone->cb[0] = ocelot_port->ts_id; + ocelot_port->ts_id = (ocelot_port->ts_id + 1) % 4; + skb_queue_tail(&ocelot_port->tx_skbs, clone); + + spin_unlock(&ocelot_port->ts_id_lock); } EXPORT_SYMBOL(ocelot_port_add_txtstamp_skb); @@ -506,9 +531,7 @@ void ocelot_get_txtstamp(struct ocelot *ocelot) /* Set the timestamp into the skb */ memset(&shhwtstamps, 0, sizeof(shhwtstamps)); shhwtstamps.hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec); - skb_tstamp_tx(skb_match, &shhwtstamps); - - dev_kfree_skb_any(skb_match); + skb_complete_tx_timestamp(skb_match, &shhwtstamps); /* Next ts */ ocelot_write(ocelot, SYS_PTP_NXT_PTP_NXT, SYS_PTP_NXT); @@ -859,7 +882,7 @@ void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state) switch (state) { case BR_STATE_FORWARDING: ocelot->bridge_fwd_mask |= BIT(port); - /* Fallthrough */ + fallthrough; case BR_STATE_LEARNING: port_cfg |= ANA_PORT_PORT_CFG_LEARN_ENA; break; @@ -1097,12 +1120,24 @@ EXPORT_SYMBOL(ocelot_port_bridge_join); int ocelot_port_bridge_leave(struct ocelot *ocelot, int port, struct net_device *bridge) { + struct switchdev_trans trans; + int ret; + ocelot->bridge_mask &= ~BIT(port); if (!ocelot->bridge_mask) ocelot->hw_bridge_dev = NULL; - ocelot_port_vlan_filtering(ocelot, port, 0); + trans.ph_prepare = true; + ret = ocelot_port_vlan_filtering(ocelot, port, false, &trans); + if (ret) + return ret; + + trans.ph_prepare = false; + ret = ocelot_port_vlan_filtering(ocelot, port, false, &trans); + if (ret) + return ret; + ocelot_port_set_pvid(ocelot, port, 0); return ocelot_port_set_native_vlan(ocelot, port, 0); } @@ -1248,7 +1283,7 @@ void ocelot_port_set_maxlen(struct ocelot *ocelot, int port, size_t sdu) struct ocelot_port *ocelot_port = ocelot->ports[port]; int maxlen = sdu + ETH_HLEN + ETH_FCS_LEN; int pause_start, pause_stop; - int atop_wm; + int atop, atop_tot; if (port == ocelot->npi) { maxlen += OCELOT_TAG_LEN; @@ -1269,12 +1304,12 @@ void ocelot_port_set_maxlen(struct ocelot *ocelot, int port, size_t sdu) ocelot_fields_write(ocelot, port, SYS_PAUSE_CFG_PAUSE_STOP, pause_stop); - /* Tail dropping watermark */ - atop_wm = (ocelot->shared_queue_sz - 9 * maxlen) / + /* Tail dropping watermarks */ + atop_tot = (ocelot->shared_queue_sz - 9 * maxlen) / OCELOT_BUFFER_CELL_SZ; - ocelot_write_rix(ocelot, ocelot->ops->wm_enc(9 * maxlen), - SYS_ATOP, port); - ocelot_write(ocelot, ocelot->ops->wm_enc(atop_wm), SYS_ATOP_TOT_CFG); + atop = (9 * maxlen) / OCELOT_BUFFER_CELL_SZ; + ocelot_write_rix(ocelot, ocelot->ops->wm_enc(atop), SYS_ATOP, port); + ocelot_write(ocelot, ocelot->ops->wm_enc(atop_tot), SYS_ATOP_TOT_CFG); } EXPORT_SYMBOL(ocelot_port_set_maxlen); @@ -1300,6 +1335,7 @@ void ocelot_init_port(struct ocelot *ocelot, int port) struct ocelot_port *ocelot_port = ocelot->ports[port]; skb_queue_head_init(&ocelot_port->tx_skbs); + spin_lock_init(&ocelot_port->ts_id_lock); /* Basic L2 initialization */ @@ -1348,22 +1384,14 @@ void ocelot_init_port(struct ocelot *ocelot, int port) } EXPORT_SYMBOL(ocelot_init_port); -/* Configure and enable the CPU port module, which is a set of queues. - * If @npi contains a valid port index, the CPU port module is connected - * to the Node Processor Interface (NPI). This is the mode through which - * frames can be injected from and extracted to an external CPU, - * over Ethernet. +/* Configure and enable the CPU port module, which is a set of queues + * accessible through register MMIO, frame DMA or Ethernet (in case + * NPI mode is used). */ -void ocelot_configure_cpu(struct ocelot *ocelot, int npi, - enum ocelot_tag_prefix injection, - enum ocelot_tag_prefix extraction) +static void ocelot_cpu_port_init(struct ocelot *ocelot) { int cpu = ocelot->num_phys_ports; - ocelot->npi = npi; - ocelot->inj_prefix = injection; - ocelot->xtr_prefix = extraction; - /* The unicast destination PGID for the CPU port module is unused */ ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, cpu); /* Instead set up a multicast destination PGID for traffic copied to @@ -1375,31 +1403,13 @@ void ocelot_configure_cpu(struct ocelot *ocelot, int npi, ANA_PORT_PORT_CFG_PORTID_VAL(cpu), ANA_PORT_PORT_CFG, cpu); - if (npi >= 0 && npi < ocelot->num_phys_ports) { - ocelot_write(ocelot, QSYS_EXT_CPU_CFG_EXT_CPUQ_MSK_M | - QSYS_EXT_CPU_CFG_EXT_CPU_PORT(npi), - QSYS_EXT_CPU_CFG); - - /* Enable NPI port */ - ocelot_fields_write(ocelot, npi, - QSYS_SWITCH_PORT_MODE_PORT_ENA, 1); - /* NPI port Injection/Extraction configuration */ - ocelot_fields_write(ocelot, npi, SYS_PORT_MODE_INCL_XTR_HDR, - extraction); - ocelot_fields_write(ocelot, npi, SYS_PORT_MODE_INCL_INJ_HDR, - injection); - - /* Disable transmission of pause frames */ - ocelot_fields_write(ocelot, npi, SYS_PAUSE_CFG_PAUSE_ENA, 0); - } - /* Enable CPU port module */ ocelot_fields_write(ocelot, cpu, QSYS_SWITCH_PORT_MODE_PORT_ENA, 1); /* CPU port Injection/Extraction configuration */ ocelot_fields_write(ocelot, cpu, SYS_PORT_MODE_INCL_XTR_HDR, - extraction); + ocelot->xtr_prefix); ocelot_fields_write(ocelot, cpu, SYS_PORT_MODE_INCL_INJ_HDR, - injection); + ocelot->inj_prefix); /* Configure the CPU port to be VLAN aware */ ocelot_write_gix(ocelot, ANA_PORT_VLAN_CFG_VLAN_VID(0) | @@ -1407,7 +1417,6 @@ void ocelot_configure_cpu(struct ocelot *ocelot, int npi, ANA_PORT_VLAN_CFG_VLAN_POP_CNT(1), ANA_PORT_VLAN_CFG, cpu); } -EXPORT_SYMBOL(ocelot_configure_cpu); int ocelot_init(struct ocelot *ocelot) { @@ -1447,6 +1456,7 @@ int ocelot_init(struct ocelot *ocelot) ocelot_mact_init(ocelot); ocelot_vlan_init(ocelot); ocelot_vcap_init(ocelot); + ocelot_cpu_port_init(ocelot); for (port = 0; port < ocelot->num_phys_ports; port++) { /* Clear all counters (5 groups) */ @@ -1544,18 +1554,18 @@ EXPORT_SYMBOL(ocelot_init); void ocelot_deinit(struct ocelot *ocelot) { - struct ocelot_port *port; - int i; - cancel_delayed_work(&ocelot->stats_work); destroy_workqueue(ocelot->stats_queue); mutex_destroy(&ocelot->stats_lock); - - for (i = 0; i < ocelot->num_phys_ports; i++) { - port = ocelot->ports[i]; - skb_queue_purge(&port->tx_skbs); - } } EXPORT_SYMBOL(ocelot_deinit); +void ocelot_deinit_port(struct ocelot *ocelot, int port) +{ + struct ocelot_port *ocelot_port = ocelot->ports[port]; + + skb_queue_purge(&ocelot_port->tx_skbs); +} +EXPORT_SYMBOL(ocelot_deinit_port); + MODULE_LICENSE("Dual MIT/GPL"); |