* [PATCH net-next v7 0/4] Add FDMA support on ocelot switch driver
From: Clément Léger @ 2021-12-09 10:43 UTC (permalink / raw)
To: David S. Miller, Jakub Kicinski, Rob Herring, Vladimir Oltean,
Claudiu Manoil, Alexandre Belloni, UNGLinuxDriver, Andrew Lunn,
Florian Fainelli, Russell King
Cc: Clément Léger, netdev, devicetree, linux-kernel,
Thomas Petazzoni, Denis Kirjanov, Julian Wiedmann
This series adds support for the Frame DMA present on the VSC7514
switch. The FDMA is able to extract and inject packets on the various
ethernet interfaces present on the switch.
------------------
Changes in V7:
- Fix kernel doc for fdma struct
Changes in V6:
- Remove dead code added in ocelot_vsc7514
- Remove useless include added in mscc/ocelot.h
- Remove trailing whitespace
- Move skb_tx_timestamp before sending the skb
- Fix a few long lines
Changes in V5:
- Add skb freeing for TX and fix RX ring skb not being freed
- Fix napi init in case of netdev registration failure
- Reorganize FDMA register definitions
- Used regmap targets from ocelot structure to get fdma pointer
- s/page_count/page_ref_count
- Move napi back in struct ocelot_fdma
Changes in V4:
- Use regmap for register access
- Removed yaml bindings convertion as well as mac address from dt
- Removed pre-computed IFH for the moment
- Fixed timestamp reading for PTP in FDMA
- Fixed wrong exit path for fdma netdev init
- Removed spinlock from TX cleanup
- Add asynchronous RX chan stop before refilling
- Reduce CH_SAFE wait time to 10us
- Reduce waiting time for channel to be safe
- Completely rework rx to use page recycling (code from gianfar)
- Reenable MTU change support since FDMA now supports it transparently
- Split TX and RX ring size
- Larger RX size to lower page allocation rate
- Add static key to check for FDMA to be enabled in fast path
Changes in V3:
- Add timeouts for hardware registers read
- Add cleanup path in fdma_init
- Rework injection and extraction to used ring like structure
- Added PTP support to FDMA
- Use pskb_expand_head instead of skb_copy_expand in xmit
- Drop jumbo support
- Use of_get_ethdev_address
- Add ocelot_fdma_netdev_init/deinit
Changes in V2:
- Read MAC for each port and not as switch base MAC address
- Add missing static for some functions in ocelot_fdma.c
- Split change_mtu from fdma commit
- Add jumbo support for register based xmit
- Move precomputed header into ocelot_port struct
- Remove use of QUIRK_ENDIAN_LITTLE due to misconfiguration for tests
- Remove fragmented packet sending which has not been tested
Clément Léger (4):
net: ocelot: export ocelot_ifh_port_set() to setup IFH
net: ocelot: add and export ocelot_ptp_rx_timestamp()
net: ocelot: add support for ndo_change_mtu
net: ocelot: add FDMA support
drivers/net/ethernet/mscc/Makefile | 1 +
drivers/net/ethernet/mscc/ocelot.c | 59 +-
drivers/net/ethernet/mscc/ocelot.h | 2 +
drivers/net/ethernet/mscc/ocelot_fdma.c | 894 +++++++++++++++++++++
drivers/net/ethernet/mscc/ocelot_fdma.h | 166 ++++
drivers/net/ethernet/mscc/ocelot_net.c | 39 +-
drivers/net/ethernet/mscc/ocelot_vsc7514.c | 10 +
include/soc/mscc/ocelot.h | 6 +
8 files changed, 1151 insertions(+), 26 deletions(-)
create mode 100644 drivers/net/ethernet/mscc/ocelot_fdma.c
create mode 100644 drivers/net/ethernet/mscc/ocelot_fdma.h
--
2.34.1
^ permalink raw reply
* Re: [PATCH v6 net-next 00/12] allow user to offload tc action to net device
From: Simon Horman @ 2021-12-09 10:41 UTC (permalink / raw)
To: netdev
Cc: Cong Wang, Dan Carpenter, Ido Schimmel, Jamal Hadi Salim,
Jiri Pirko, Oz Shlomo, Roi Dayan, Vlad Buslov, Baowen Zheng,
Louis Peens, oss-drivers
In-Reply-To: <20211209092806.12336-1-simon.horman@corigine.com>
On Thu, Dec 09, 2021 at 10:27:54AM +0100, Simon Horman wrote:
> Baowen Zheng says:
...
Sorry, I appear to have included two sets of slightly different
information in this cover letter.
Please ignore the following two paragraphs, and instead refer
to the two after that.
Ignore this:
> Baowen Zheng (12):
> flow_offload: fill flags to action structure
> flow_offload: reject to offload tc actions in offload drivers
> flow_offload: add index to flow_action_entry structure
> flow_offload: return EOPNOTSUPP for the unsupported mpls action type
> flow_offload: add ops to tc_action_ops for flow action setup
> flow_offload: allow user to offload tc action to net device
> flow_offload: add skip_hw and skip_sw to control if offload the action
> flow_offload: add process to update action stats from hardware
> net: sched: save full flags for tc action
> flow_offload: add reoffload process to update hw_count
> flow_offload: validate flags of filter and actions
> selftests: tc-testing: add action offload selftest for action and
> filter
>
> drivers/net/dsa/ocelot/felix_vsc9959.c | 4 +-
> drivers/net/dsa/sja1105/sja1105_flower.c | 2 +-
> drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c | 2 +-
> .../net/ethernet/freescale/enetc/enetc_qos.c | 6 +-
> .../ethernet/mellanox/mlx5/core/en/rep/tc.c | 3 +
> .../ethernet/mellanox/mlxsw/spectrum_flower.c | 2 +-
> drivers/net/ethernet/mscc/ocelot_flower.c | 2 +-
> .../ethernet/netronome/nfp/flower/offload.c | 3 +
> include/linux/netdevice.h | 1 +
> include/net/act_api.h | 27 +-
> include/net/flow_offload.h | 20 +-
> include/net/pkt_cls.h | 27 +-
> include/net/tc_act/tc_gate.h | 5 -
> include/uapi/linux/pkt_cls.h | 9 +-
> net/core/flow_offload.c | 46 +-
> net/sched/act_api.c | 450 +++++++++++++++++-
> net/sched/act_bpf.c | 2 +-
> net/sched/act_connmark.c | 2 +-
> net/sched/act_csum.c | 19 +
> net/sched/act_ct.c | 21 +
> net/sched/act_ctinfo.c | 2 +-
> net/sched/act_gact.c | 38 ++
> net/sched/act_gate.c | 51 +-
> net/sched/act_ife.c | 2 +-
> net/sched/act_ipt.c | 2 +-
> net/sched/act_mirred.c | 50 ++
> net/sched/act_mpls.c | 54 ++-
> net/sched/act_nat.c | 2 +-
> net/sched/act_pedit.c | 36 +-
> net/sched/act_police.c | 27 +-
> net/sched/act_sample.c | 32 +-
> net/sched/act_simple.c | 2 +-
> net/sched/act_skbedit.c | 38 +-
> net/sched/act_skbmod.c | 2 +-
> net/sched/act_tunnel_key.c | 54 +++
> net/sched/act_vlan.c | 48 ++
> net/sched/cls_api.c | 263 ++--------
> net/sched/cls_flower.c | 9 +-
> net/sched/cls_matchall.c | 9 +-
> net/sched/cls_u32.c | 12 +-
> .../tc-testing/tc-tests/actions/police.json | 24 +
> .../tc-testing/tc-tests/filters/matchall.json | 24 +
> 42 files changed, 1144 insertions(+), 290 deletions(-)
Instead, refer to this:
> Baowen Zheng (12):
> flow_offload: fill flags to action structure
> flow_offload: reject to offload tc actions in offload drivers
> flow_offload: add index to flow_action_entry structure
> flow_offload: return EOPNOTSUPP for the unsupported mpls action type
> flow_offload: add ops to tc_action_ops for flow action setup
> flow_offload: allow user to offload tc action to net device
> flow_offload: add skip_hw and skip_sw to control if offload the action
> flow_offload: add process to update action stats from hardware
> net: sched: save full flags for tc action
> flow_offload: add reoffload process to update hw_count
> flow_offload: validate flags of filter and actions
> selftests: tc-testing: add action offload selftest for action and
> filter
>
> drivers/net/dsa/ocelot/felix_vsc9959.c | 4 +-
> drivers/net/dsa/sja1105/sja1105_flower.c | 2 +-
> drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c | 2 +-
> .../net/ethernet/freescale/enetc/enetc_qos.c | 6 +-
> .../ethernet/mellanox/mlx5/core/en/rep/tc.c | 3 +
> .../ethernet/mellanox/mlxsw/spectrum_flower.c | 2 +-
> drivers/net/ethernet/mscc/ocelot_flower.c | 2 +-
> .../ethernet/netronome/nfp/flower/offload.c | 3 +
> include/linux/netdevice.h | 1 +
> include/net/act_api.h | 27 +-
> include/net/flow_offload.h | 20 +-
> include/net/pkt_cls.h | 27 +-
> include/net/tc_act/tc_gate.h | 5 -
> include/uapi/linux/pkt_cls.h | 9 +-
> net/core/flow_offload.c | 46 +-
> net/sched/act_api.c | 451 +++++++++++++++++-
> net/sched/act_bpf.c | 2 +-
> net/sched/act_connmark.c | 2 +-
> net/sched/act_csum.c | 19 +
> net/sched/act_ct.c | 21 +
> net/sched/act_ctinfo.c | 2 +-
> net/sched/act_gact.c | 38 ++
> net/sched/act_gate.c | 51 +-
> net/sched/act_ife.c | 2 +-
> net/sched/act_ipt.c | 2 +-
> net/sched/act_mirred.c | 50 ++
> net/sched/act_mpls.c | 54 ++-
> net/sched/act_nat.c | 2 +-
> net/sched/act_pedit.c | 36 +-
> net/sched/act_police.c | 27 +-
> net/sched/act_sample.c | 32 +-
> net/sched/act_simple.c | 2 +-
> net/sched/act_skbedit.c | 38 +-
> net/sched/act_skbmod.c | 2 +-
> net/sched/act_tunnel_key.c | 54 +++
> net/sched/act_vlan.c | 48 ++
> net/sched/cls_api.c | 263 ++--------
> net/sched/cls_flower.c | 9 +-
> net/sched/cls_matchall.c | 9 +-
> net/sched/cls_u32.c | 12 +-
> .../tc-testing/tc-tests/actions/police.json | 24 +
> .../tc-testing/tc-tests/filters/matchall.json | 24 +
> 42 files changed, 1145 insertions(+), 290 deletions(-)
^ permalink raw reply
* [PATCHv2 net-next 2/2] Bonding: force user to add HWTSTAMP_FLAG_BONDED_PHC_INDEX when get/set HWTSTAMP
From: Hangbin Liu @ 2021-12-09 10:24 UTC (permalink / raw)
To: netdev
Cc: Jay Vosburgh, Veaceslav Falico, Andy Gospodarek, David S . Miller,
Jakub Kicinski, Richard Cochran, Heiner Kallweit, Hangbin Liu
In-Reply-To: <20211209102449.2000401-1-liuhangbin@gmail.com>
When there is a failover, the PHC index of bond active interface will be
changed. This may break the user space program if the author didn't aware.
By setting this flag, the user should aware that the PHC index get/set
by syscall is not stable. And the user space is able to deal with it.
Without this flag, the kernel will reject the request forwarding to
bonding.
Reported-by: Jakub Kicinski <kuba@kernel.org>
Fixes: 94dd016ae538 ("bond: pass get_ts_info and SIOC[SG]HWTSTAMP ioctl to active device")
Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
---
v2: change the flag name to HWTSTAMP_FLAG_BONDED_PHC_INDEX
---
drivers/net/bonding/bond_main.c | 33 +++++++++++++++++++++------------
1 file changed, 21 insertions(+), 12 deletions(-)
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 0f39ad2af81c..268190a624e0 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -4094,6 +4094,7 @@ static int bond_eth_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cm
struct mii_ioctl_data *mii = NULL;
const struct net_device_ops *ops;
struct net_device *real_dev;
+ struct hwtstamp_config cfg;
struct ifreq ifrr;
int res = 0;
@@ -4124,21 +4125,29 @@ static int bond_eth_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cm
break;
case SIOCSHWTSTAMP:
case SIOCGHWTSTAMP:
- rcu_read_lock();
- real_dev = bond_option_active_slave_get_rcu(bond);
- rcu_read_unlock();
- if (real_dev) {
- strscpy_pad(ifrr.ifr_name, real_dev->name, IFNAMSIZ);
- ifrr.ifr_ifru = ifr->ifr_ifru;
+ if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
+ return -EFAULT;
+
+ if (cfg.flags & HWTSTAMP_FLAG_BONDED_PHC_INDEX) {
+ rcu_read_lock();
+ real_dev = bond_option_active_slave_get_rcu(bond);
+ rcu_read_unlock();
+ if (real_dev) {
+ strscpy_pad(ifrr.ifr_name, real_dev->name, IFNAMSIZ);
+ ifrr.ifr_ifru = ifr->ifr_ifru;
+
+ ops = real_dev->netdev_ops;
+ if (netif_device_present(real_dev) && ops->ndo_eth_ioctl) {
+ res = ops->ndo_eth_ioctl(real_dev, &ifrr, cmd);
- ops = real_dev->netdev_ops;
- if (netif_device_present(real_dev) && ops->ndo_eth_ioctl)
- res = ops->ndo_eth_ioctl(real_dev, &ifrr, cmd);
+ if (!res)
+ ifr->ifr_ifru = ifrr.ifr_ifru;
- if (!res)
- ifr->ifr_ifru = ifrr.ifr_ifru;
+ return res;
+ }
+ }
}
- break;
+ fallthrough;
default:
res = -EOPNOTSUPP;
}
--
2.31.1
^ permalink raw reply related
* [PATCHv2 net-next 1/2] net_tstamp: add new flag HWTSTAMP_FLAG_BONDED_PHC_INDEX
From: Hangbin Liu @ 2021-12-09 10:24 UTC (permalink / raw)
To: netdev
Cc: Jay Vosburgh, Veaceslav Falico, Andy Gospodarek, David S . Miller,
Jakub Kicinski, Richard Cochran, Heiner Kallweit, Hangbin Liu
In-Reply-To: <20211209102449.2000401-1-liuhangbin@gmail.com>
Since commit 94dd016ae538 ("bond: pass get_ts_info and SIOC[SG]HWTSTAMP
ioctl to active device") the user could get bond active interface's
PHC index directly. But when there is a failover, the bond active
interface will change, thus the PHC index is also changed. This may
break the user's program if they did not update the PHC timely.
This patch adds a new hwtstamp_config flag HWTSTAMP_FLAG_BONDED_PHC_INDEX.
When the user wants to get the bond active interface's PHC, they need to
add this flag and be aware the PHC index may be changed.
With the new flag. All flag checks in current drivers are removed. Only
the checking in net_hwtstamp_validate() is kept.
Suggested-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
---
v2: Keep the flag validation check in net_hwtstamp_validate()
Rename the flag to HWTSTAMP_FLAG_BONDED_PHC_INDEX
---
.../net/dsa/hirschmann/hellcreek_hwtstamp.c | 4 ----
drivers/net/dsa/mv88e6xxx/hwtstamp.c | 4 ----
drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 3 ---
.../net/ethernet/aquantia/atlantic/aq_main.c | 3 ---
.../net/ethernet/broadcom/bnx2x/bnx2x_main.c | 5 -----
drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c | 3 ---
drivers/net/ethernet/broadcom/tg3.c | 3 ---
drivers/net/ethernet/cadence/macb_ptp.c | 4 ----
.../net/ethernet/cavium/liquidio/lio_main.c | 3 ---
.../ethernet/cavium/liquidio/lio_vf_main.c | 3 ---
.../net/ethernet/cavium/octeon/octeon_mgmt.c | 3 ---
.../net/ethernet/cavium/thunder/nicvf_main.c | 4 ----
drivers/net/ethernet/engleder/tsnep_ptp.c | 3 ---
drivers/net/ethernet/freescale/fec_ptp.c | 4 ----
drivers/net/ethernet/freescale/gianfar.c | 4 ----
drivers/net/ethernet/intel/e1000e/netdev.c | 4 ----
drivers/net/ethernet/intel/i40e/i40e_ptp.c | 4 ----
drivers/net/ethernet/intel/ice/ice_ptp.c | 4 ----
drivers/net/ethernet/intel/igb/igb_ptp.c | 4 ----
drivers/net/ethernet/intel/igc/igc_ptp.c | 4 ----
drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c | 4 ----
.../net/ethernet/marvell/mvpp2/mvpp2_main.c | 3 ---
.../ethernet/marvell/octeontx2/nic/otx2_pf.c | 4 ----
.../net/ethernet/mellanox/mlx4/en_netdev.c | 4 ----
drivers/net/ethernet/microchip/lan743x_ptp.c | 6 ------
drivers/net/ethernet/mscc/ocelot.c | 4 ----
.../net/ethernet/neterion/vxge/vxge-main.c | 4 ----
.../ethernet/oki-semi/pch_gbe/pch_gbe_main.c | 3 ---
drivers/net/ethernet/qlogic/qede/qede_ptp.c | 5 -----
drivers/net/ethernet/renesas/ravb_main.c | 4 ----
drivers/net/ethernet/sfc/ptp.c | 3 ---
.../net/ethernet/stmicro/stmmac/stmmac_main.c | 4 ----
drivers/net/ethernet/ti/cpsw_priv.c | 4 ----
drivers/net/ethernet/ti/netcp_ethss.c | 4 ----
drivers/net/ethernet/xscale/ixp4xx_eth.c | 3 ---
drivers/net/phy/dp83640.c | 3 ---
drivers/net/phy/mscc/mscc_ptp.c | 3 ---
drivers/ptp/ptp_ines.c | 4 ----
include/uapi/linux/net_tstamp.h | 16 +++++++++++++++-
net/core/dev_ioctl.c | 19 ++++++++++++++-----
40 files changed, 29 insertions(+), 148 deletions(-)
diff --git a/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.c b/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.c
index 40b41c794dfa..b3bc948d6145 100644
--- a/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.c
+++ b/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.c
@@ -52,10 +52,6 @@ static int hellcreek_set_hwtstamp_config(struct hellcreek *hellcreek, int port,
*/
clear_bit_unlock(HELLCREEK_HWTSTAMP_ENABLED, &ps->state);
- /* Reserved for future extensions */
- if (config->flags)
- return -EINVAL;
-
switch (config->tx_type) {
case HWTSTAMP_TX_ON:
tx_tstamp_enable = true;
diff --git a/drivers/net/dsa/mv88e6xxx/hwtstamp.c b/drivers/net/dsa/mv88e6xxx/hwtstamp.c
index 8f74ffc7a279..389f8a6ec0ab 100644
--- a/drivers/net/dsa/mv88e6xxx/hwtstamp.c
+++ b/drivers/net/dsa/mv88e6xxx/hwtstamp.c
@@ -100,10 +100,6 @@ static int mv88e6xxx_set_hwtstamp_config(struct mv88e6xxx_chip *chip, int port,
*/
clear_bit_unlock(MV88E6XXX_HWTSTAMP_ENABLED, &ps->state);
- /* reserved for future extensions */
- if (config->flags)
- return -EINVAL;
-
switch (config->tx_type) {
case HWTSTAMP_TX_OFF:
tstamp_enable = false;
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
index 30d24d19f40d..492ac383f16d 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
@@ -1508,9 +1508,6 @@ static int xgbe_set_hwtstamp_settings(struct xgbe_prv_data *pdata,
if (copy_from_user(&config, ifreq->ifr_data, sizeof(config)))
return -EFAULT;
- if (config.flags)
- return -EINVAL;
-
mac_tscr = 0;
switch (config.tx_type) {
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_main.c b/drivers/net/ethernet/aquantia/atlantic/aq_main.c
index e22935ce9573..e65ce7199dac 100644
--- a/drivers/net/ethernet/aquantia/atlantic/aq_main.c
+++ b/drivers/net/ethernet/aquantia/atlantic/aq_main.c
@@ -231,9 +231,6 @@ static void aq_ndev_set_multicast_settings(struct net_device *ndev)
static int aq_ndev_config_hwtstamp(struct aq_nic_s *aq_nic,
struct hwtstamp_config *config)
{
- if (config->flags)
- return -EINVAL;
-
switch (config->tx_type) {
case HWTSTAMP_TX_OFF:
case HWTSTAMP_TX_ON:
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
index aec666e97683..651bc1d7a57a 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
@@ -15356,11 +15356,6 @@ static int bnx2x_hwtstamp_ioctl(struct bnx2x *bp, struct ifreq *ifr)
DP(BNX2X_MSG_PTP, "Requested tx_type: %d, requested rx_filters = %d\n",
config.tx_type, config.rx_filter);
- if (config.flags) {
- BNX2X_ERR("config.flags is reserved for future use\n");
- return -EINVAL;
- }
-
bp->hwtstamp_ioctl_called = true;
bp->tx_type = config.tx_type;
bp->rx_filter = config.rx_filter;
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c
index 8388be119f9a..48520967746f 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c
@@ -417,9 +417,6 @@ int bnxt_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
if (copy_from_user(&stmpconf, ifr->ifr_data, sizeof(stmpconf)))
return -EFAULT;
- if (stmpconf.flags)
- return -EINVAL;
-
if (stmpconf.tx_type != HWTSTAMP_TX_ON &&
stmpconf.tx_type != HWTSTAMP_TX_OFF)
return -ERANGE;
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index 283f3c1f1195..c28f8cc00d1c 100644
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -13806,9 +13806,6 @@ static int tg3_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
if (copy_from_user(&stmpconf, ifr->ifr_data, sizeof(stmpconf)))
return -EFAULT;
- if (stmpconf.flags)
- return -EINVAL;
-
if (stmpconf.tx_type != HWTSTAMP_TX_ON &&
stmpconf.tx_type != HWTSTAMP_TX_OFF)
return -ERANGE;
diff --git a/drivers/net/ethernet/cadence/macb_ptp.c b/drivers/net/ethernet/cadence/macb_ptp.c
index 095c5a2144a7..fb6b27f46b15 100644
--- a/drivers/net/ethernet/cadence/macb_ptp.c
+++ b/drivers/net/ethernet/cadence/macb_ptp.c
@@ -464,10 +464,6 @@ int gem_set_hwtst(struct net_device *dev, struct ifreq *ifr, int cmd)
sizeof(*tstamp_config)))
return -EFAULT;
- /* reserved for future extensions */
- if (tstamp_config->flags)
- return -EINVAL;
-
switch (tstamp_config->tx_type) {
case HWTSTAMP_TX_OFF:
break;
diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c
index 12eee2bc7f5c..ba28aa444e5a 100644
--- a/drivers/net/ethernet/cavium/liquidio/lio_main.c
+++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c
@@ -2114,9 +2114,6 @@ static int hwtstamp_ioctl(struct net_device *netdev, struct ifreq *ifr)
if (copy_from_user(&conf, ifr->ifr_data, sizeof(conf)))
return -EFAULT;
- if (conf.flags)
- return -EINVAL;
-
switch (conf.tx_type) {
case HWTSTAMP_TX_ON:
case HWTSTAMP_TX_OFF:
diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c
index c607756b731f..568f211d91cc 100644
--- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c
+++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c
@@ -1254,9 +1254,6 @@ static int hwtstamp_ioctl(struct net_device *netdev, struct ifreq *ifr)
if (copy_from_user(&conf, ifr->ifr_data, sizeof(conf)))
return -EFAULT;
- if (conf.flags)
- return -EINVAL;
-
switch (conf.tx_type) {
case HWTSTAMP_TX_ON:
case HWTSTAMP_TX_OFF:
diff --git a/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c b/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c
index 4b4ffdd1044d..103591dcea1c 100644
--- a/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c
+++ b/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c
@@ -702,9 +702,6 @@ static int octeon_mgmt_ioctl_hwtstamp(struct net_device *netdev,
if (copy_from_user(&config, rq->ifr_data, sizeof(config)))
return -EFAULT;
- if (config.flags) /* reserved for future extensions */
- return -EINVAL;
-
/* Check the status of hardware for tiemstamps */
if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) {
/* Get the current state of the PTP clock */
diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c
index bb45d5df2856..63191692f624 100644
--- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c
+++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c
@@ -1917,10 +1917,6 @@ static int nicvf_config_hwtstamp(struct net_device *netdev, struct ifreq *ifr)
if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
return -EFAULT;
- /* reserved for future extensions */
- if (config.flags)
- return -EINVAL;
-
switch (config.tx_type) {
case HWTSTAMP_TX_OFF:
case HWTSTAMP_TX_ON:
diff --git a/drivers/net/ethernet/engleder/tsnep_ptp.c b/drivers/net/ethernet/engleder/tsnep_ptp.c
index 4bfb4d8508f5..eaad453d487e 100644
--- a/drivers/net/ethernet/engleder/tsnep_ptp.c
+++ b/drivers/net/ethernet/engleder/tsnep_ptp.c
@@ -31,9 +31,6 @@ int tsnep_ptp_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
return -EFAULT;
- if (config.flags)
- return -EINVAL;
-
switch (config.tx_type) {
case HWTSTAMP_TX_OFF:
case HWTSTAMP_TX_ON:
diff --git a/drivers/net/ethernet/freescale/fec_ptp.c b/drivers/net/ethernet/freescale/fec_ptp.c
index d71eac7e1924..af99017a5453 100644
--- a/drivers/net/ethernet/freescale/fec_ptp.c
+++ b/drivers/net/ethernet/freescale/fec_ptp.c
@@ -473,10 +473,6 @@ int fec_ptp_set(struct net_device *ndev, struct ifreq *ifr)
if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
return -EFAULT;
- /* reserved for future extensions */
- if (config.flags)
- return -EINVAL;
-
switch (config.tx_type) {
case HWTSTAMP_TX_OFF:
fep->hwts_tx_en = 0;
diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c
index acab58fd3db3..206b7a35eaf5 100644
--- a/drivers/net/ethernet/freescale/gianfar.c
+++ b/drivers/net/ethernet/freescale/gianfar.c
@@ -2076,10 +2076,6 @@ static int gfar_hwtstamp_set(struct net_device *netdev, struct ifreq *ifr)
if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
return -EFAULT;
- /* reserved for future extensions */
- if (config.flags)
- return -EINVAL;
-
switch (config.tx_type) {
case HWTSTAMP_TX_OFF:
priv->hwts_tx_en = 0;
diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c
index 44e2dc8328a2..635a95927e93 100644
--- a/drivers/net/ethernet/intel/e1000e/netdev.c
+++ b/drivers/net/ethernet/intel/e1000e/netdev.c
@@ -3614,10 +3614,6 @@ static int e1000e_config_hwtstamp(struct e1000_adapter *adapter,
if (!(adapter->flags & FLAG_HAS_HW_TIMESTAMP))
return -EINVAL;
- /* flags reserved for future extensions - must be zero */
- if (config->flags)
- return -EINVAL;
-
switch (config->tx_type) {
case HWTSTAMP_TX_OFF:
tsync_tx_ctl = 0;
diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c
index 09b1d5aed1c9..61e5789d78db 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c
@@ -1205,10 +1205,6 @@ static int i40e_ptp_set_timestamp_mode(struct i40e_pf *pf,
INIT_WORK(&pf->ptp_extts0_work, i40e_ptp_extts0_work);
- /* Reserved for future extensions. */
- if (config->flags)
- return -EINVAL;
-
switch (config->tx_type) {
case HWTSTAMP_TX_OFF:
pf->ptp_tx = false;
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c
index bf7247c6f58e..dfc7c830acf6 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp.c
@@ -1205,10 +1205,6 @@ int ice_ptp_get_ts_config(struct ice_pf *pf, struct ifreq *ifr)
static int
ice_ptp_set_timestamp_mode(struct ice_pf *pf, struct hwtstamp_config *config)
{
- /* Reserved for future extensions. */
- if (config->flags)
- return -EINVAL;
-
switch (config->tx_type) {
case HWTSTAMP_TX_OFF:
ice_set_tx_tstamp(pf, false);
diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c b/drivers/net/ethernet/intel/igb/igb_ptp.c
index 0011b15e678c..0ac4cc5eaa2d 100644
--- a/drivers/net/ethernet/intel/igb/igb_ptp.c
+++ b/drivers/net/ethernet/intel/igb/igb_ptp.c
@@ -1015,10 +1015,6 @@ static int igb_ptp_set_timestamp_mode(struct igb_adapter *adapter,
bool is_l2 = false;
u32 regval;
- /* reserved for future extensions */
- if (config->flags)
- return -EINVAL;
-
switch (config->tx_type) {
case HWTSTAMP_TX_OFF:
tsync_tx_ctl = 0;
diff --git a/drivers/net/ethernet/intel/igc/igc_ptp.c b/drivers/net/ethernet/intel/igc/igc_ptp.c
index 30568e3544cd..71813fa8f928 100644
--- a/drivers/net/ethernet/intel/igc/igc_ptp.c
+++ b/drivers/net/ethernet/intel/igc/igc_ptp.c
@@ -560,10 +560,6 @@ static void igc_ptp_enable_tx_timestamp(struct igc_adapter *adapter)
static int igc_ptp_set_timestamp_mode(struct igc_adapter *adapter,
struct hwtstamp_config *config)
{
- /* reserved for future extensions */
- if (config->flags)
- return -EINVAL;
-
switch (config->tx_type) {
case HWTSTAMP_TX_OFF:
igc_ptp_disable_tx_timestamp(adapter);
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c
index 23ddfd79fc8b..336426a67ac1 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c
@@ -992,10 +992,6 @@ static int ixgbe_ptp_set_timestamp_mode(struct ixgbe_adapter *adapter,
bool is_l2 = false;
u32 regval;
- /* reserved for future extensions */
- if (config->flags)
- return -EINVAL;
-
switch (config->tx_type) {
case HWTSTAMP_TX_OFF:
tsync_tx_ctl = 0;
diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
index 252e215a14f1..03f4a1b1f2a4 100644
--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
+++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
@@ -5142,9 +5142,6 @@ static int mvpp2_set_ts_config(struct mvpp2_port *port, struct ifreq *ifr)
if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
return -EFAULT;
- if (config.flags)
- return -EINVAL;
-
if (config.tx_type != HWTSTAMP_TX_OFF &&
config.tx_type != HWTSTAMP_TX_ON)
return -ERANGE;
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
index 1333edf1c361..6080ebd9bd94 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
@@ -2002,10 +2002,6 @@ int otx2_config_hwtstamp(struct net_device *netdev, struct ifreq *ifr)
if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
return -EFAULT;
- /* reserved for future extensions */
- if (config.flags)
- return -EINVAL;
-
switch (config.tx_type) {
case HWTSTAMP_TX_OFF:
otx2_config_hw_tx_tstamp(pfvf, false);
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
index f1c10f2bda78..ad1e4caf48bf 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
@@ -2427,10 +2427,6 @@ static int mlx4_en_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
return -EFAULT;
- /* reserved for future extensions */
- if (config.flags)
- return -EINVAL;
-
/* device doesn't support time stamping */
if (!(mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_TS))
return -EINVAL;
diff --git a/drivers/net/ethernet/microchip/lan743x_ptp.c b/drivers/net/ethernet/microchip/lan743x_ptp.c
index 9380e396f648..8b7a8d879083 100644
--- a/drivers/net/ethernet/microchip/lan743x_ptp.c
+++ b/drivers/net/ethernet/microchip/lan743x_ptp.c
@@ -1305,12 +1305,6 @@ int lan743x_ptp_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
return -EFAULT;
- if (config.flags) {
- netif_warn(adapter, drv, adapter->netdev,
- "ignoring hwtstamp_config.flags == 0x%08X, expected 0\n",
- config.flags);
- }
-
switch (config.tx_type) {
case HWTSTAMP_TX_OFF:
for (index = 0; index < LAN743X_MAX_TX_CHANNELS;
diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
index b1856d8c944b..0be74c823d5e 100644
--- a/drivers/net/ethernet/mscc/ocelot.c
+++ b/drivers/net/ethernet/mscc/ocelot.c
@@ -1602,10 +1602,6 @@ int ocelot_hwstamp_set(struct ocelot *ocelot, int port, struct ifreq *ifr)
if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
return -EFAULT;
- /* reserved for future extensions */
- if (cfg.flags)
- return -EINVAL;
-
/* Tx type sanity check */
switch (cfg.tx_type) {
case HWTSTAMP_TX_ON:
diff --git a/drivers/net/ethernet/neterion/vxge/vxge-main.c b/drivers/net/ethernet/neterion/vxge/vxge-main.c
index 1969009a91e7..2c2e9e56ed4e 100644
--- a/drivers/net/ethernet/neterion/vxge/vxge-main.c
+++ b/drivers/net/ethernet/neterion/vxge/vxge-main.c
@@ -3159,10 +3159,6 @@ static int vxge_hwtstamp_set(struct vxgedev *vdev, void __user *data)
if (copy_from_user(&config, data, sizeof(config)))
return -EFAULT;
- /* reserved for future extensions */
- if (config.flags)
- return -EINVAL;
-
/* Transmit HW Timestamp not supported */
switch (config.tx_type) {
case HWTSTAMP_TX_OFF:
diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c
index 71d234291fc5..1dc40c537281 100644
--- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c
+++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c
@@ -210,9 +210,6 @@ static int hwtstamp_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
return -EFAULT;
- if (cfg.flags) /* reserved for future extensions */
- return -EINVAL;
-
/* Get ieee1588's dev information */
pdev = adapter->ptp_pdev;
diff --git a/drivers/net/ethernet/qlogic/qede/qede_ptp.c b/drivers/net/ethernet/qlogic/qede/qede_ptp.c
index 8c28fabb0ff6..39176e765767 100644
--- a/drivers/net/ethernet/qlogic/qede/qede_ptp.c
+++ b/drivers/net/ethernet/qlogic/qede/qede_ptp.c
@@ -304,11 +304,6 @@ int qede_ptp_hw_ts(struct qede_dev *edev, struct ifreq *ifr)
"HWTSTAMP IOCTL: Requested tx_type = %d, requested rx_filters = %d\n",
config.tx_type, config.rx_filter);
- if (config.flags) {
- DP_ERR(edev, "config.flags is reserved for future use\n");
- return -EINVAL;
- }
-
ptp->hw_ts_ioctl_called = 1;
ptp->tx_type = config.tx_type;
ptp->rx_filter = config.rx_filter;
diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c
index ce09bd45527e..b215cde68e10 100644
--- a/drivers/net/ethernet/renesas/ravb_main.c
+++ b/drivers/net/ethernet/renesas/ravb_main.c
@@ -2221,10 +2221,6 @@ static int ravb_hwtstamp_set(struct net_device *ndev, struct ifreq *req)
if (copy_from_user(&config, req->ifr_data, sizeof(config)))
return -EFAULT;
- /* Reserved for future extensions */
- if (config.flags)
- return -EINVAL;
-
switch (config.tx_type) {
case HWTSTAMP_TX_OFF:
tstamp_tx_ctrl = 0;
diff --git a/drivers/net/ethernet/sfc/ptp.c b/drivers/net/ethernet/sfc/ptp.c
index 797e51802ccb..f0ef515e2ade 100644
--- a/drivers/net/ethernet/sfc/ptp.c
+++ b/drivers/net/ethernet/sfc/ptp.c
@@ -1765,9 +1765,6 @@ static int efx_ptp_ts_init(struct efx_nic *efx, struct hwtstamp_config *init)
{
int rc;
- if (init->flags)
- return -EINVAL;
-
if ((init->tx_type != HWTSTAMP_TX_OFF) &&
(init->tx_type != HWTSTAMP_TX_ON))
return -ERANGE;
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 4e05c1d92935..e4d2748592ee 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -636,10 +636,6 @@ static int stmmac_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
netdev_dbg(priv->dev, "%s config flags:0x%x, tx_type:0x%x, rx_filter:0x%x\n",
__func__, config.flags, config.tx_type, config.rx_filter);
- /* reserved for future extensions */
- if (config.flags)
- return -EINVAL;
-
if (config.tx_type != HWTSTAMP_TX_OFF &&
config.tx_type != HWTSTAMP_TX_ON)
return -ERANGE;
diff --git a/drivers/net/ethernet/ti/cpsw_priv.c b/drivers/net/ethernet/ti/cpsw_priv.c
index c99dd9735087..8624a044776f 100644
--- a/drivers/net/ethernet/ti/cpsw_priv.c
+++ b/drivers/net/ethernet/ti/cpsw_priv.c
@@ -626,10 +626,6 @@ static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
return -EFAULT;
- /* reserved for future extensions */
- if (cfg.flags)
- return -EINVAL;
-
if (cfg.tx_type != HWTSTAMP_TX_OFF && cfg.tx_type != HWTSTAMP_TX_ON)
return -ERANGE;
diff --git a/drivers/net/ethernet/ti/netcp_ethss.c b/drivers/net/ethernet/ti/netcp_ethss.c
index 33c1592d5381..751fb0bc65c5 100644
--- a/drivers/net/ethernet/ti/netcp_ethss.c
+++ b/drivers/net/ethernet/ti/netcp_ethss.c
@@ -2654,10 +2654,6 @@ static int gbe_hwtstamp_set(struct gbe_intf *gbe_intf, struct ifreq *ifr)
if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
return -EFAULT;
- /* reserved for future extensions */
- if (cfg.flags)
- return -EINVAL;
-
switch (cfg.tx_type) {
case HWTSTAMP_TX_OFF:
gbe_dev->tx_ts_enabled = 0;
diff --git a/drivers/net/ethernet/xscale/ixp4xx_eth.c b/drivers/net/ethernet/xscale/ixp4xx_eth.c
index 65fdad1107fc..df77a22d1b81 100644
--- a/drivers/net/ethernet/xscale/ixp4xx_eth.c
+++ b/drivers/net/ethernet/xscale/ixp4xx_eth.c
@@ -382,9 +382,6 @@ static int hwtstamp_set(struct net_device *netdev, struct ifreq *ifr)
if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
return -EFAULT;
- if (cfg.flags) /* reserved for future extensions */
- return -EINVAL;
-
ret = ixp46x_ptp_find(&port->timesync_regs, &port->phc_index);
if (ret)
return ret;
diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c
index 705c16675b80..c2d1a85ec559 100644
--- a/drivers/net/phy/dp83640.c
+++ b/drivers/net/phy/dp83640.c
@@ -1235,9 +1235,6 @@ static int dp83640_hwtstamp(struct mii_timestamper *mii_ts, struct ifreq *ifr)
if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
return -EFAULT;
- if (cfg.flags) /* reserved for future extensions */
- return -EINVAL;
-
if (cfg.tx_type < 0 || cfg.tx_type > HWTSTAMP_TX_ONESTEP_SYNC)
return -ERANGE;
diff --git a/drivers/net/phy/mscc/mscc_ptp.c b/drivers/net/phy/mscc/mscc_ptp.c
index edb951695b13..34f829845d06 100644
--- a/drivers/net/phy/mscc/mscc_ptp.c
+++ b/drivers/net/phy/mscc/mscc_ptp.c
@@ -1057,9 +1057,6 @@ static int vsc85xx_hwtstamp(struct mii_timestamper *mii_ts, struct ifreq *ifr)
if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
return -EFAULT;
- if (cfg.flags)
- return -EINVAL;
-
switch (cfg.tx_type) {
case HWTSTAMP_TX_ONESTEP_SYNC:
one_step = true;
diff --git a/drivers/ptp/ptp_ines.c b/drivers/ptp/ptp_ines.c
index 6c7c2843ba0b..61f47fb9d997 100644
--- a/drivers/ptp/ptp_ines.c
+++ b/drivers/ptp/ptp_ines.c
@@ -338,10 +338,6 @@ static int ines_hwtstamp(struct mii_timestamper *mii_ts, struct ifreq *ifr)
if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
return -EFAULT;
- /* reserved for future extensions */
- if (cfg.flags)
- return -EINVAL;
-
switch (cfg.tx_type) {
case HWTSTAMP_TX_OFF:
ts_stat_tx = 0;
diff --git a/include/uapi/linux/net_tstamp.h b/include/uapi/linux/net_tstamp.h
index fcc61c73a666..1cf3b43308ac 100644
--- a/include/uapi/linux/net_tstamp.h
+++ b/include/uapi/linux/net_tstamp.h
@@ -62,7 +62,7 @@ struct so_timestamping {
/**
* struct hwtstamp_config - %SIOCGHWTSTAMP and %SIOCSHWTSTAMP parameter
*
- * @flags: no flags defined right now, must be zero for %SIOCSHWTSTAMP
+ * @flags: one of HWTSTAMP_FLAG_*
* @tx_type: one of HWTSTAMP_TX_*
* @rx_filter: one of HWTSTAMP_FILTER_*
*
@@ -78,6 +78,20 @@ struct hwtstamp_config {
int rx_filter;
};
+/* possible values for hwtstamp_config->flags */
+enum hwtstamp_flags {
+ /*
+ * With this flag, the user could get bond active interface's
+ * PHC index. Note this PHC index is not stable as when there
+ * is a failover, the bond active interface will be changed, so
+ * will be the PHC index.
+ */
+ HWTSTAMP_FLAG_BONDED_PHC_INDEX = (1<<0),
+
+ /* add new constants above here */
+ __HWTSTAMP_FLAGS_CNT
+};
+
/* possible values for hwtstamp_config->tx_type */
enum hwtstamp_tx_types {
/*
diff --git a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c
index 1d309a666932..10ac5457dcbc 100644
--- a/net/core/dev_ioctl.c
+++ b/net/core/dev_ioctl.c
@@ -186,18 +186,27 @@ static int net_hwtstamp_validate(struct ifreq *ifr)
struct hwtstamp_config cfg;
enum hwtstamp_tx_types tx_type;
enum hwtstamp_rx_filters rx_filter;
- int tx_type_valid = 0;
+ enum hwtstamp_flags flag;
int rx_filter_valid = 0;
+ int tx_type_valid = 0;
+ int flag_valid = 0;
if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
return -EFAULT;
- if (cfg.flags) /* reserved for future extensions */
- return -EINVAL;
-
+ flag = cfg.flags;
tx_type = cfg.tx_type;
rx_filter = cfg.rx_filter;
+ switch (flag) {
+ case HWTSTAMP_FLAG_BONDED_PHC_INDEX:
+ flag_valid = 1;
+ break;
+ case __HWTSTAMP_FLAGS_CNT:
+ /* not a real value */
+ break;
+ }
+
switch (tx_type) {
case HWTSTAMP_TX_OFF:
case HWTSTAMP_TX_ON:
@@ -234,7 +243,7 @@ static int net_hwtstamp_validate(struct ifreq *ifr)
break;
}
- if (!tx_type_valid || !rx_filter_valid)
+ if (!flag_valid || !tx_type_valid || !rx_filter_valid)
return -ERANGE;
return 0;
--
2.31.1
^ permalink raw reply related
* [PATCHv2 net-next 0/2] net: add new hwtstamp flag HWTSTAMP_FLAG_BONDED_PHC_INDEX
From: Hangbin Liu @ 2021-12-09 10:24 UTC (permalink / raw)
To: netdev
Cc: Jay Vosburgh, Veaceslav Falico, Andy Gospodarek, David S . Miller,
Jakub Kicinski, Richard Cochran, Heiner Kallweit, Hangbin Liu
This patchset add a new hwtstamp_config flag HWTSTAMP_FLAG_BONDED_PHC_INDEX.
When user want to get bond active interface's PHC, they need to add this flag
and aware the PHC index may changed.
v2: rename the flag to HWTSTAMP_FLAG_BONDED_PHC_INDEX
Hangbin Liu (2):
net_tstamp: add new flag HWTSTAMP_FLAG_BONDED_PHC_INDEX
Bonding: force user to add HWTSTAMP_FLAG_BONDED_PHC_INDEX when get/set
HWTSTAMP
drivers/net/bonding/bond_main.c | 33 ++++++++++++-------
.../net/dsa/hirschmann/hellcreek_hwtstamp.c | 4 ---
drivers/net/dsa/mv88e6xxx/hwtstamp.c | 4 ---
drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 3 --
.../net/ethernet/aquantia/atlantic/aq_main.c | 3 --
.../net/ethernet/broadcom/bnx2x/bnx2x_main.c | 5 ---
drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c | 3 --
drivers/net/ethernet/broadcom/tg3.c | 3 --
drivers/net/ethernet/cadence/macb_ptp.c | 4 ---
.../net/ethernet/cavium/liquidio/lio_main.c | 3 --
.../ethernet/cavium/liquidio/lio_vf_main.c | 3 --
.../net/ethernet/cavium/octeon/octeon_mgmt.c | 3 --
.../net/ethernet/cavium/thunder/nicvf_main.c | 4 ---
drivers/net/ethernet/engleder/tsnep_ptp.c | 3 --
drivers/net/ethernet/freescale/fec_ptp.c | 4 ---
drivers/net/ethernet/freescale/gianfar.c | 4 ---
drivers/net/ethernet/intel/e1000e/netdev.c | 4 ---
drivers/net/ethernet/intel/i40e/i40e_ptp.c | 4 ---
drivers/net/ethernet/intel/ice/ice_ptp.c | 4 ---
drivers/net/ethernet/intel/igb/igb_ptp.c | 4 ---
drivers/net/ethernet/intel/igc/igc_ptp.c | 4 ---
drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c | 4 ---
.../net/ethernet/marvell/mvpp2/mvpp2_main.c | 3 --
.../ethernet/marvell/octeontx2/nic/otx2_pf.c | 4 ---
.../net/ethernet/mellanox/mlx4/en_netdev.c | 4 ---
drivers/net/ethernet/microchip/lan743x_ptp.c | 6 ----
drivers/net/ethernet/mscc/ocelot.c | 4 ---
.../net/ethernet/neterion/vxge/vxge-main.c | 4 ---
.../ethernet/oki-semi/pch_gbe/pch_gbe_main.c | 3 --
drivers/net/ethernet/qlogic/qede/qede_ptp.c | 5 ---
drivers/net/ethernet/renesas/ravb_main.c | 4 ---
drivers/net/ethernet/sfc/ptp.c | 3 --
.../net/ethernet/stmicro/stmmac/stmmac_main.c | 4 ---
drivers/net/ethernet/ti/cpsw_priv.c | 4 ---
drivers/net/ethernet/ti/netcp_ethss.c | 4 ---
drivers/net/ethernet/xscale/ixp4xx_eth.c | 3 --
drivers/net/phy/dp83640.c | 3 --
drivers/net/phy/mscc/mscc_ptp.c | 3 --
drivers/ptp/ptp_ines.c | 4 ---
include/uapi/linux/net_tstamp.h | 16 ++++++++-
net/core/dev_ioctl.c | 19 ++++++++---
41 files changed, 50 insertions(+), 160 deletions(-)
--
2.31.1
^ permalink raw reply
* RE: [net-next 6/6] can: mcp251xfd: mcp251xfd_regmap_crc_read(): work around broken CRC on TBC register
From: Thomas.Kopp @ 2021-12-09 10:22 UTC (permalink / raw)
To: pavel.modilaynen, mkl; +Cc: drew, linux-can, menschel.p, netdev, will
In-Reply-To: <PR3P174MB0112D073D0E5E080FAAE8510846E9@PR3P174MB0112.EURP174.PROD.OUTLOOK.COM>
Hi Pavel,
> We have the similar CRC read errors but
> the lowest byte is not 0x00 and 0x80, it's actually 0x0x or 0x8x, e.g.
>
> mcp251xfd spi0.0 can0: CRC read error at address 0x0010 (length=4,
> data=82 d1 fa 6c, CRC=0xd9c2) retrying.
>
> 0xb0 0x10 0x04 0x82 0xd1 0xfa 0x6c => 0x59FD (not matching)
>
> but if I flip the first received bit (highest bit in the lowest byte):
> 0xb0 0x10 0x04 0x02 0xd1 0xfa 0x6c => 0xD9C2 (matching!)
What settings do you have on your setup? Can you please print the dmesg output from the init? I'm especially interested in Sysclk and SPI speed.
Thanks,
Thomas
^ permalink raw reply
* [PATCH net-next v4 7/7] net/mlx5: Let user configure max_macs generic param
From: Shay Drory @ 2021-12-09 10:09 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski
Cc: jiri, saeedm, netdev, linux-kernel, Shay Drory, Moshe Shemesh,
Parav Pandit
In-Reply-To: <20211209100929.28115-1-shayd@nvidia.com>
Currently, max_macs is taking 70Kbytes of memory per function. This
size is not needed in all use cases, and is critical with large scale.
Hence, allow user to configure the number of max_macs.
For example, to reduce the number of max_macs to 1, execute::
$ devlink dev param set pci/0000:00:0b.0 name max_macs value 1 \
cmode driverinit
$ devlink dev reload pci/0000:00:0b.0
Signed-off-by: Shay Drory <shayd@nvidia.com>
Reviewed-by: Moshe Shemesh <moshe@nvidia.com>
Reviewed-by: Parav Pandit <parav@nvidia.com>
---
Documentation/networking/devlink/mlx5.rst | 3 +
.../net/ethernet/mellanox/mlx5/core/devlink.c | 67 +++++++++++++++++++
.../net/ethernet/mellanox/mlx5/core/main.c | 21 ++++++
3 files changed, 91 insertions(+)
diff --git a/Documentation/networking/devlink/mlx5.rst b/Documentation/networking/devlink/mlx5.rst
index 38089f0aefcf..38e94ed65936 100644
--- a/Documentation/networking/devlink/mlx5.rst
+++ b/Documentation/networking/devlink/mlx5.rst
@@ -23,6 +23,9 @@ Parameters
* - ``event_eq_size``
- driverinit
- The range is between 64 and 4096.
+ * - ``max_macs``
+ - driverinit
+ - The range is between 1 and 2^31. Only power of 2 values are supported.
The ``mlx5`` driver also implements the following driver-specific
parameters.
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c
index 37b7600c5545..d1093bb2d436 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c
@@ -773,6 +773,66 @@ static void mlx5_devlink_auxdev_params_unregister(struct devlink *devlink)
mlx5_devlink_eth_param_unregister(devlink);
}
+static int mlx5_devlink_max_uc_list_validate(struct devlink *devlink, u32 id,
+ union devlink_param_value val,
+ struct netlink_ext_ack *extack)
+{
+ struct mlx5_core_dev *dev = devlink_priv(devlink);
+
+ if (val.vu32 == 0) {
+ NL_SET_ERR_MSG_MOD(extack, "max_macs value must be greater than 0");
+ return -EINVAL;
+ }
+
+ if (!is_power_of_2(val.vu32)) {
+ NL_SET_ERR_MSG_MOD(extack, "Only power of 2 values are supported for max_macs");
+ return -EINVAL;
+ }
+
+ if (ilog2(val.vu32) >
+ MLX5_CAP_GEN_MAX(dev, log_max_current_uc_list)) {
+ NL_SET_ERR_MSG_MOD(extack, "max_macs value is out of the supported range");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct devlink_param max_uc_list_param =
+ DEVLINK_PARAM_GENERIC(MAX_MACS, BIT(DEVLINK_PARAM_CMODE_DRIVERINIT),
+ NULL, NULL, mlx5_devlink_max_uc_list_validate);
+
+static int mlx5_devlink_max_uc_list_param_register(struct devlink *devlink)
+{
+ struct mlx5_core_dev *dev = devlink_priv(devlink);
+ union devlink_param_value value;
+ int err;
+
+ if (!MLX5_CAP_GEN_MAX(dev, log_max_current_uc_list_wr_supported))
+ return 0;
+
+ err = devlink_param_register(devlink, &max_uc_list_param);
+ if (err)
+ return err;
+
+ value.vu32 = 1 << MLX5_CAP_GEN(dev, log_max_current_uc_list);
+ devlink_param_driverinit_value_set(devlink,
+ DEVLINK_PARAM_GENERIC_ID_MAX_MACS,
+ value);
+ return 0;
+}
+
+static void
+mlx5_devlink_max_uc_list_param_unregister(struct devlink *devlink)
+{
+ struct mlx5_core_dev *dev = devlink_priv(devlink);
+
+ if (!MLX5_CAP_GEN_MAX(dev, log_max_current_uc_list_wr_supported))
+ return;
+
+ devlink_param_unregister(devlink, &max_uc_list_param);
+}
+
#define MLX5_TRAP_DROP(_id, _group_id) \
DEVLINK_TRAP_GENERIC(DROP, DROP, _id, \
DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \
@@ -832,6 +892,10 @@ int mlx5_devlink_register(struct devlink *devlink)
if (err)
goto auxdev_reg_err;
+ err = mlx5_devlink_max_uc_list_param_register(devlink);
+ if (err)
+ goto max_uc_list_err;
+
err = mlx5_devlink_traps_register(devlink);
if (err)
goto traps_reg_err;
@@ -842,6 +906,8 @@ int mlx5_devlink_register(struct devlink *devlink)
return 0;
traps_reg_err:
+ mlx5_devlink_max_uc_list_param_unregister(devlink);
+max_uc_list_err:
mlx5_devlink_auxdev_params_unregister(devlink);
auxdev_reg_err:
devlink_params_unregister(devlink, mlx5_devlink_params,
@@ -852,6 +918,7 @@ int mlx5_devlink_register(struct devlink *devlink)
void mlx5_devlink_unregister(struct devlink *devlink)
{
mlx5_devlink_traps_unregister(devlink);
+ mlx5_devlink_max_uc_list_param_unregister(devlink);
mlx5_devlink_auxdev_params_unregister(devlink);
devlink_params_unregister(devlink, mlx5_devlink_params,
ARRAY_SIZE(mlx5_devlink_params));
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c
index d97c9e86d7b3..b1a82226623c 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c
@@ -484,10 +484,26 @@ static int handle_hca_cap_odp(struct mlx5_core_dev *dev, void *set_ctx)
return set_caps(dev, set_ctx, MLX5_SET_HCA_CAP_OP_MOD_ODP);
}
+static int max_uc_list_get_devlink_param(struct mlx5_core_dev *dev)
+{
+ struct devlink *devlink = priv_to_devlink(dev);
+ union devlink_param_value val;
+ int err;
+
+ err = devlink_param_driverinit_value_get(devlink,
+ DEVLINK_PARAM_GENERIC_ID_MAX_MACS,
+ &val);
+ if (!err)
+ return val.vu32;
+ mlx5_core_dbg(dev, "Failed to get param. err = %d\n", err);
+ return err;
+}
+
static int handle_hca_cap(struct mlx5_core_dev *dev, void *set_ctx)
{
struct mlx5_profile *prof = &dev->profile;
void *set_hca_cap;
+ int max_uc_list;
int err;
err = mlx5_core_get_caps(dev, MLX5_CAP_GENERAL);
@@ -561,6 +577,11 @@ static int handle_hca_cap(struct mlx5_core_dev *dev, void *set_ctx)
if (MLX5_CAP_GEN(dev, roce_rw_supported))
MLX5_SET(cmd_hca_cap, set_hca_cap, roce, mlx5_is_roce_init_enabled(dev));
+ max_uc_list = max_uc_list_get_devlink_param(dev);
+ if (max_uc_list > 0)
+ MLX5_SET(cmd_hca_cap, set_hca_cap, log_max_current_uc_list,
+ ilog2(max_uc_list));
+
return set_caps(dev, set_ctx, MLX5_SET_HCA_CAP_OP_MOD_GENERAL_DEVICE);
}
--
2.21.3
^ permalink raw reply related
* [PATCH net-next v4 6/7] devlink: Clarifies max_macs generic devlink param
From: Shay Drory @ 2021-12-09 10:09 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski
Cc: jiri, saeedm, netdev, linux-kernel, Shay Drory
In-Reply-To: <20211209100929.28115-1-shayd@nvidia.com>
The generic param max_macs documentation isn't clear.
Replace it with a more descriptive documentation
Signed-off-by: Shay Drory <shayd@nvidia.com>
Reviewed-by: Jiri Pirko <jiri@nvidia.com>
---
Documentation/networking/devlink/devlink-params.rst | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/Documentation/networking/devlink/devlink-params.rst b/Documentation/networking/devlink/devlink-params.rst
index da0b5e7f8eec..4e01dc32bc08 100644
--- a/Documentation/networking/devlink/devlink-params.rst
+++ b/Documentation/networking/devlink/devlink-params.rst
@@ -118,8 +118,10 @@ own name.
errors.
* - ``max_macs``
- u32
- - Specifies the maximum number of MAC addresses per ethernet port of
- this device.
+ - Typically macvlan, vlan net devices mac are also programmed in their
+ parent netdevice's Function rx filter. This parameter limit the
+ maximum number of unicast mac address filters to receive traffic from
+ per ethernet port of this device.
* - ``region_snapshot_enable``
- Boolean
- Enable capture of ``devlink-region`` snapshots.
--
2.21.3
^ permalink raw reply related
* [PATCH net-next v4 5/7] net/mlx5: Let user configure event_eq_size param
From: Shay Drory @ 2021-12-09 10:09 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski
Cc: jiri, saeedm, netdev, linux-kernel, Shay Drory, Moshe Shemesh
In-Reply-To: <20211209100929.28115-1-shayd@nvidia.com>
Event EQ is an EQ which received the notification of almost all the
events generated by the NIC.
Currently, each event EQ is taking 512KB of memory. This size is not
needed in most use cases, and is critical with large scale. Hence,
allow user to configure the size of the event EQ.
For example to reduce event EQ size to 64, execute::
$ devlink dev param set pci/0000:00:0b.0 name event_eq_size value 64 \
cmode driverinit
$ devlink dev reload pci/0000:00:0b.0
Signed-off-by: Shay Drory <shayd@nvidia.com>
Reviewed-by: Moshe Shemesh <moshe@nvidia.com>
---
Documentation/networking/devlink/mlx5.rst | 3 +++
.../net/ethernet/mellanox/mlx5/core/devlink.c | 7 +++++++
drivers/net/ethernet/mellanox/mlx5/core/eq.c | 16 +++++++++++++++-
3 files changed, 25 insertions(+), 1 deletion(-)
diff --git a/Documentation/networking/devlink/mlx5.rst b/Documentation/networking/devlink/mlx5.rst
index 291e7f63af73..38089f0aefcf 100644
--- a/Documentation/networking/devlink/mlx5.rst
+++ b/Documentation/networking/devlink/mlx5.rst
@@ -20,6 +20,9 @@ Parameters
* - ``io_eq_size``
- driverinit
- The range is between 64 and 4096.
+ * - ``event_eq_size``
+ - driverinit
+ - The range is between 64 and 4096.
The ``mlx5`` driver also implements the following driver-specific
parameters.
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c
index 177c6e9159f8..37b7600c5545 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c
@@ -579,6 +579,8 @@ static const struct devlink_param mlx5_devlink_params[] = {
mlx5_devlink_enable_remote_dev_reset_set, NULL),
DEVLINK_PARAM_GENERIC(IO_EQ_SIZE, BIT(DEVLINK_PARAM_CMODE_DRIVERINIT),
NULL, NULL, mlx5_devlink_eq_depth_validate),
+ DEVLINK_PARAM_GENERIC(EVENT_EQ_SIZE, BIT(DEVLINK_PARAM_CMODE_DRIVERINIT),
+ NULL, NULL, mlx5_devlink_eq_depth_validate),
};
static void mlx5_devlink_set_params_init_values(struct devlink *devlink)
@@ -622,6 +624,11 @@ static void mlx5_devlink_set_params_init_values(struct devlink *devlink)
devlink_param_driverinit_value_set(devlink,
DEVLINK_PARAM_GENERIC_ID_IO_EQ_SIZE,
value);
+
+ value.vu32 = MLX5_NUM_ASYNC_EQE;
+ devlink_param_driverinit_value_set(devlink,
+ DEVLINK_PARAM_GENERIC_ID_EVENT_EQ_SIZE,
+ value);
}
static const struct devlink_param enable_eth_param =
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eq.c b/drivers/net/ethernet/mellanox/mlx5/core/eq.c
index 7686d7c9c824..b695aad71ee1 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eq.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eq.c
@@ -623,6 +623,20 @@ static void cleanup_async_eq(struct mlx5_core_dev *dev,
name, err);
}
+static u16 async_eq_depth_devlink_param_get(struct mlx5_core_dev *dev)
+{
+ struct devlink *devlink = priv_to_devlink(dev);
+ union devlink_param_value val;
+ int err;
+
+ err = devlink_param_driverinit_value_get(devlink,
+ DEVLINK_PARAM_GENERIC_ID_EVENT_EQ_SIZE,
+ &val);
+ if (!err)
+ return val.vu32;
+ mlx5_core_dbg(dev, "Failed to get param. using default. err = %d\n", err);
+ return MLX5_NUM_ASYNC_EQE;
+}
static int create_async_eqs(struct mlx5_core_dev *dev)
{
struct mlx5_eq_table *table = dev->priv.eq_table;
@@ -647,7 +661,7 @@ static int create_async_eqs(struct mlx5_core_dev *dev)
param = (struct mlx5_eq_param) {
.irq_index = MLX5_IRQ_EQ_CTRL,
- .nent = MLX5_NUM_ASYNC_EQE,
+ .nent = async_eq_depth_devlink_param_get(dev),
};
gather_async_events_mask(dev, param.mask);
--
2.21.3
^ permalink raw reply related
* [PATCH net-next v4 4/7] devlink: Add new "event_eq_size" generic device param
From: Shay Drory @ 2021-12-09 10:09 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski
Cc: jiri, saeedm, netdev, linux-kernel, Shay Drory, Moshe Shemesh
In-Reply-To: <20211209100929.28115-1-shayd@nvidia.com>
Add new device generic parameter to determine the size of the
asynchronous control events EQ.
For example, to reduce event EQ size to 64, execute:
$ devlink dev param set pci/0000:06:00.0 \
name event_eq_size value 64 cmode driverinit
$ devlink dev reload pci/0000:06:00.0
Signed-off-by: Shay Drory <shayd@nvidia.com>
Reviewed-by: Moshe Shemesh <moshe@nvidia.com>
---
Documentation/networking/devlink/devlink-params.rst | 3 +++
include/net/devlink.h | 4 ++++
net/core/devlink.c | 5 +++++
3 files changed, 12 insertions(+)
diff --git a/Documentation/networking/devlink/devlink-params.rst b/Documentation/networking/devlink/devlink-params.rst
index 547c0b430c9e..da0b5e7f8eec 100644
--- a/Documentation/networking/devlink/devlink-params.rst
+++ b/Documentation/networking/devlink/devlink-params.rst
@@ -132,3 +132,6 @@ own name.
* - ``io_eq_size``
- u32
- Control the size of I/O completion EQs.
+ * - ``event_eq_size``
+ - u32
+ - Control the size of asynchronous control events EQ.
diff --git a/include/net/devlink.h b/include/net/devlink.h
index b5f4acd0e0cd..8d5349d2fb68 100644
--- a/include/net/devlink.h
+++ b/include/net/devlink.h
@@ -460,6 +460,7 @@ enum devlink_param_generic_id {
DEVLINK_PARAM_GENERIC_ID_ENABLE_VNET,
DEVLINK_PARAM_GENERIC_ID_ENABLE_IWARP,
DEVLINK_PARAM_GENERIC_ID_IO_EQ_SIZE,
+ DEVLINK_PARAM_GENERIC_ID_EVENT_EQ_SIZE,
/* add new param generic ids above here*/
__DEVLINK_PARAM_GENERIC_ID_MAX,
@@ -515,6 +516,9 @@ enum devlink_param_generic_id {
#define DEVLINK_PARAM_GENERIC_IO_EQ_SIZE_NAME "io_eq_size"
#define DEVLINK_PARAM_GENERIC_IO_EQ_SIZE_TYPE DEVLINK_PARAM_TYPE_U32
+#define DEVLINK_PARAM_GENERIC_EVENT_EQ_SIZE_NAME "event_eq_size"
+#define DEVLINK_PARAM_GENERIC_EVENT_EQ_SIZE_TYPE DEVLINK_PARAM_TYPE_U32
+
#define DEVLINK_PARAM_GENERIC(_id, _cmodes, _get, _set, _validate) \
{ \
.id = DEVLINK_PARAM_GENERIC_ID_##_id, \
diff --git a/net/core/devlink.c b/net/core/devlink.c
index 0d4e63d11585..d9f3c994e704 100644
--- a/net/core/devlink.c
+++ b/net/core/devlink.c
@@ -4471,6 +4471,11 @@ static const struct devlink_param devlink_param_generic[] = {
.name = DEVLINK_PARAM_GENERIC_IO_EQ_SIZE_NAME,
.type = DEVLINK_PARAM_GENERIC_IO_EQ_SIZE_TYPE,
},
+ {
+ .id = DEVLINK_PARAM_GENERIC_ID_EVENT_EQ_SIZE,
+ .name = DEVLINK_PARAM_GENERIC_EVENT_EQ_SIZE_NAME,
+ .type = DEVLINK_PARAM_GENERIC_EVENT_EQ_SIZE_TYPE,
+ },
};
static int devlink_param_generic_verify(const struct devlink_param *param)
--
2.21.3
^ permalink raw reply related
* [PATCH net-next v4 3/7] net/mlx5: Let user configure io_eq_size param
From: Shay Drory @ 2021-12-09 10:09 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski
Cc: jiri, saeedm, netdev, linux-kernel, Shay Drory, Moshe Shemesh
In-Reply-To: <20211209100929.28115-1-shayd@nvidia.com>
Currently, each I/O EQ is taking 128KB of memory. This size
is not needed in all use cases, and is critical with large scale.
Hence, allow user to configure the size of I/O EQs.
For example, to reduce I/O EQ size to 64, execute:
$ devlink dev param set pci/0000:00:0b.0 name io_eq_size value 64 \
cmode driverinit
$ devlink dev reload pci/0000:00:0b.0
Signed-off-by: Shay Drory <shayd@nvidia.com>
Reviewed-by: Moshe Shemesh <moshe@nvidia.com>
---
Documentation/networking/devlink/mlx5.rst | 4 ++++
.../net/ethernet/mellanox/mlx5/core/devlink.c | 14 ++++++++++++++
drivers/net/ethernet/mellanox/mlx5/core/eq.c | 18 +++++++++++++++++-
3 files changed, 35 insertions(+), 1 deletion(-)
diff --git a/Documentation/networking/devlink/mlx5.rst b/Documentation/networking/devlink/mlx5.rst
index 4e4b97f7971a..291e7f63af73 100644
--- a/Documentation/networking/devlink/mlx5.rst
+++ b/Documentation/networking/devlink/mlx5.rst
@@ -14,8 +14,12 @@ Parameters
* - Name
- Mode
+ - Validation
* - ``enable_roce``
- driverinit
+ * - ``io_eq_size``
+ - driverinit
+ - The range is between 64 and 4096.
The ``mlx5`` driver also implements the following driver-specific
parameters.
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c
index 1c98652b244a..177c6e9159f8 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c
@@ -546,6 +546,13 @@ static int mlx5_devlink_enable_remote_dev_reset_get(struct devlink *devlink, u32
return 0;
}
+static int mlx5_devlink_eq_depth_validate(struct devlink *devlink, u32 id,
+ union devlink_param_value val,
+ struct netlink_ext_ack *extack)
+{
+ return (val.vu16 >= 64 && val.vu16 <= 4096) ? 0 : -EINVAL;
+}
+
static const struct devlink_param mlx5_devlink_params[] = {
DEVLINK_PARAM_DRIVER(MLX5_DEVLINK_PARAM_ID_FLOW_STEERING_MODE,
"flow_steering_mode", DEVLINK_PARAM_TYPE_STRING,
@@ -570,6 +577,8 @@ static const struct devlink_param mlx5_devlink_params[] = {
DEVLINK_PARAM_GENERIC(ENABLE_REMOTE_DEV_RESET, BIT(DEVLINK_PARAM_CMODE_RUNTIME),
mlx5_devlink_enable_remote_dev_reset_get,
mlx5_devlink_enable_remote_dev_reset_set, NULL),
+ DEVLINK_PARAM_GENERIC(IO_EQ_SIZE, BIT(DEVLINK_PARAM_CMODE_DRIVERINIT),
+ NULL, NULL, mlx5_devlink_eq_depth_validate),
};
static void mlx5_devlink_set_params_init_values(struct devlink *devlink)
@@ -608,6 +617,11 @@ static void mlx5_devlink_set_params_init_values(struct devlink *devlink)
value);
}
#endif
+
+ value.vu32 = MLX5_COMP_EQ_SIZE;
+ devlink_param_driverinit_value_set(devlink,
+ DEVLINK_PARAM_GENERIC_ID_IO_EQ_SIZE,
+ value);
}
static const struct devlink_param enable_eth_param =
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eq.c b/drivers/net/ethernet/mellanox/mlx5/core/eq.c
index 792e0d6aa861..7686d7c9c824 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eq.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eq.c
@@ -19,6 +19,7 @@
#include "lib/clock.h"
#include "diag/fw_tracer.h"
#include "mlx5_irq.h"
+#include "devlink.h"
enum {
MLX5_EQE_OWNER_INIT_VAL = 0x1,
@@ -796,6 +797,21 @@ static void destroy_comp_eqs(struct mlx5_core_dev *dev)
}
}
+static u16 comp_eq_depth_devlink_param_get(struct mlx5_core_dev *dev)
+{
+ struct devlink *devlink = priv_to_devlink(dev);
+ union devlink_param_value val;
+ int err;
+
+ err = devlink_param_driverinit_value_get(devlink,
+ DEVLINK_PARAM_GENERIC_ID_IO_EQ_SIZE,
+ &val);
+ if (!err)
+ return val.vu32;
+ mlx5_core_dbg(dev, "Failed to get param. using default. err = %d\n", err);
+ return MLX5_COMP_EQ_SIZE;
+}
+
static int create_comp_eqs(struct mlx5_core_dev *dev)
{
struct mlx5_eq_table *table = dev->priv.eq_table;
@@ -807,7 +823,7 @@ static int create_comp_eqs(struct mlx5_core_dev *dev)
INIT_LIST_HEAD(&table->comp_eqs_list);
ncomp_eqs = table->num_comp_eqs;
- nent = MLX5_COMP_EQ_SIZE;
+ nent = comp_eq_depth_devlink_param_get(dev);
for (i = 0; i < ncomp_eqs; i++) {
struct mlx5_eq_param param = {};
int vecidx = i;
--
2.21.3
^ permalink raw reply related
* [PATCH net-next v4 2/7] devlink: Add new "io_eq_size" generic device param
From: Shay Drory @ 2021-12-09 10:09 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski
Cc: jiri, saeedm, netdev, linux-kernel, Shay Drory, Moshe Shemesh
In-Reply-To: <20211209100929.28115-1-shayd@nvidia.com>
Add new device generic parameter to determine the size of the
I/O completion EQs.
For example, to reduce I/O EQ size to 64, execute:
$ devlink dev param set pci/0000:06:00.0 \
name io_eq_size value 64 cmode driverinit
$ devlink dev reload pci/0000:06:00.0
Signed-off-by: Shay Drory <shayd@nvidia.com>
Reviewed-by: Moshe Shemesh <moshe@nvidia.com>
---
Documentation/networking/devlink/devlink-params.rst | 3 +++
include/net/devlink.h | 4 ++++
net/core/devlink.c | 5 +++++
3 files changed, 12 insertions(+)
diff --git a/Documentation/networking/devlink/devlink-params.rst b/Documentation/networking/devlink/devlink-params.rst
index b7dfe693a332..547c0b430c9e 100644
--- a/Documentation/networking/devlink/devlink-params.rst
+++ b/Documentation/networking/devlink/devlink-params.rst
@@ -129,3 +129,6 @@ own name.
will NACK any attempt of other host to reset the device. This parameter
is useful for setups where a device is shared by different hosts, such
as multi-host setup.
+ * - ``io_eq_size``
+ - u32
+ - Control the size of I/O completion EQs.
diff --git a/include/net/devlink.h b/include/net/devlink.h
index 3276a29f2b81..b5f4acd0e0cd 100644
--- a/include/net/devlink.h
+++ b/include/net/devlink.h
@@ -459,6 +459,7 @@ enum devlink_param_generic_id {
DEVLINK_PARAM_GENERIC_ID_ENABLE_RDMA,
DEVLINK_PARAM_GENERIC_ID_ENABLE_VNET,
DEVLINK_PARAM_GENERIC_ID_ENABLE_IWARP,
+ DEVLINK_PARAM_GENERIC_ID_IO_EQ_SIZE,
/* add new param generic ids above here*/
__DEVLINK_PARAM_GENERIC_ID_MAX,
@@ -511,6 +512,9 @@ enum devlink_param_generic_id {
#define DEVLINK_PARAM_GENERIC_ENABLE_IWARP_NAME "enable_iwarp"
#define DEVLINK_PARAM_GENERIC_ENABLE_IWARP_TYPE DEVLINK_PARAM_TYPE_BOOL
+#define DEVLINK_PARAM_GENERIC_IO_EQ_SIZE_NAME "io_eq_size"
+#define DEVLINK_PARAM_GENERIC_IO_EQ_SIZE_TYPE DEVLINK_PARAM_TYPE_U32
+
#define DEVLINK_PARAM_GENERIC(_id, _cmodes, _get, _set, _validate) \
{ \
.id = DEVLINK_PARAM_GENERIC_ID_##_id, \
diff --git a/net/core/devlink.c b/net/core/devlink.c
index db3b52110cf2..0d4e63d11585 100644
--- a/net/core/devlink.c
+++ b/net/core/devlink.c
@@ -4466,6 +4466,11 @@ static const struct devlink_param devlink_param_generic[] = {
.name = DEVLINK_PARAM_GENERIC_ENABLE_IWARP_NAME,
.type = DEVLINK_PARAM_GENERIC_ENABLE_IWARP_TYPE,
},
+ {
+ .id = DEVLINK_PARAM_GENERIC_ID_IO_EQ_SIZE,
+ .name = DEVLINK_PARAM_GENERIC_IO_EQ_SIZE_NAME,
+ .type = DEVLINK_PARAM_GENERIC_IO_EQ_SIZE_TYPE,
+ },
};
static int devlink_param_generic_verify(const struct devlink_param *param)
--
2.21.3
^ permalink raw reply related
* [PATCH net-next v4 1/7] net/mlx5: Introduce log_max_current_uc_list_wr_supported bit
From: Shay Drory @ 2021-12-09 10:09 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski
Cc: jiri, saeedm, netdev, linux-kernel, Shay Drory, Moshe Shemesh
In-Reply-To: <20211209100929.28115-1-shayd@nvidia.com>
Downstream patch will use this bit in order to know whether the device
supports changing of max_uc_list.
Signed-off-by: Shay Drory <shayd@nvidia.com>
Reviewed-by: Moshe Shemesh <moshe@nvidia.com>
---
include/linux/mlx5/mlx5_ifc.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h
index fbaab440a484..e9db12aae8f9 100644
--- a/include/linux/mlx5/mlx5_ifc.h
+++ b/include/linux/mlx5/mlx5_ifc.h
@@ -1621,7 +1621,7 @@ struct mlx5_ifc_cmd_hca_cap_bits {
u8 ext_stride_num_range[0x1];
u8 roce_rw_supported[0x1];
- u8 reserved_at_3a2[0x1];
+ u8 log_max_current_uc_list_wr_supported[0x1];
u8 log_max_stride_sz_rq[0x5];
u8 reserved_at_3a8[0x3];
u8 log_min_stride_sz_rq[0x5];
--
2.21.3
^ permalink raw reply related
* [PATCH net-next v4 0/7] net/mlx5: Memory optimizations
From: Shay Drory @ 2021-12-09 10:09 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski
Cc: jiri, saeedm, netdev, linux-kernel, Shay Drory
This series provides knobs which will enable users to
minimize memory consumption of mlx5 Functions (PF/VF/SF).
mlx5 exposes two new generic devlink params for EQ size
configuration and uses devlink generic param max_macs.
Patches summary:
- Patch-1 Introduce log_max_current_uc_list_wr_supported bit
- Patches-2-3 Provides I/O EQ size param which enables to save
up to 128KB.
- Patches-4-5 Provides event EQ size param which enables to save
up to 512KB.
- Patch-6 Clarify max_macs param.
- Patch-7 Provides max_macs param which enables to save up to 70KB
In total, this series can save up to 700KB per Function.
---
changelog:
v3->v4:
- align devlink_param doc of EQ size params to u32.
v2->v3:
- change type of EQ size params to u32 per Jiri suggestion.
- separate ifc changes to new patch
v1->v2:
- convert io_eq_size and event_eq_size from devlink_resources to
generic devlink_params
Shay Drory (7):
net/mlx5: Introduce log_max_current_uc_list_wr_supported bit
devlink: Add new "io_eq_size" generic device param
net/mlx5: Let user configure io_eq_size param
devlink: Add new "event_eq_size" generic device param
net/mlx5: Let user configure event_eq_size param
devlink: Clarifies max_macs generic devlink param
net/mlx5: Let user configure max_macs generic param
.../networking/devlink/devlink-params.rst | 12 ++-
Documentation/networking/devlink/mlx5.rst | 10 +++
.../net/ethernet/mellanox/mlx5/core/devlink.c | 88 +++++++++++++++++++
drivers/net/ethernet/mellanox/mlx5/core/eq.c | 34 ++++++-
.../net/ethernet/mellanox/mlx5/core/main.c | 21 +++++
include/linux/mlx5/mlx5_ifc.h | 2 +-
include/net/devlink.h | 8 ++
net/core/devlink.c | 10 +++
8 files changed, 180 insertions(+), 5 deletions(-)
--
2.21.3
^ permalink raw reply
* [PATCH net 3/3] net: wwan: iosm: fixes unable to send AT command during mbim tx
From: M Chetan Kumar @ 2021-12-09 10:16 UTC (permalink / raw)
To: netdev
Cc: kuba, davem, johannes, ryazanov.s.a, loic.poulain, krishna.c.sudi,
m.chetan.kumar, m.chetan.kumar, linuxwwan
In-Reply-To: <20211209101629.2940877-1-m.chetan.kumar@linux.intel.com>
ev_cdev_write_pending flag is preventing a TX message post for
AT port while MBIM transfer is ongoing.
Removed the unnecessary check around control port TX transfer.
Signed-off-by: M Chetan Kumar <m.chetan.kumar@linux.intel.com>
Reviewed-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
---
drivers/net/wwan/iosm/iosm_ipc_imem.c | 1 -
drivers/net/wwan/iosm/iosm_ipc_imem.h | 3 ---
drivers/net/wwan/iosm/iosm_ipc_imem_ops.c | 6 ------
3 files changed, 10 deletions(-)
diff --git a/drivers/net/wwan/iosm/iosm_ipc_imem.c b/drivers/net/wwan/iosm/iosm_ipc_imem.c
index e2c096863488..12c03dacb5dd 100644
--- a/drivers/net/wwan/iosm/iosm_ipc_imem.c
+++ b/drivers/net/wwan/iosm/iosm_ipc_imem.c
@@ -1270,7 +1270,6 @@ struct iosm_imem *ipc_imem_init(struct iosm_pcie *pcie, unsigned int device_id,
ipc_imem->pci_device_id = device_id;
- ipc_imem->ev_cdev_write_pending = false;
ipc_imem->cp_version = 0;
ipc_imem->device_sleep = IPC_HOST_SLEEP_ENTER_SLEEP;
diff --git a/drivers/net/wwan/iosm/iosm_ipc_imem.h b/drivers/net/wwan/iosm/iosm_ipc_imem.h
index 6b479fe23a42..6b8a837faef2 100644
--- a/drivers/net/wwan/iosm/iosm_ipc_imem.h
+++ b/drivers/net/wwan/iosm/iosm_ipc_imem.h
@@ -336,8 +336,6 @@ enum ipc_phase {
* process the irq actions.
* @flag: Flag to monitor the state of driver
* @td_update_timer_suspended: if true then td update timer suspend
- * @ev_cdev_write_pending: 0 means inform the IPC tasklet to pass
- * the accumulated uplink buffers to CP.
* @ev_mux_net_transmit_pending:0 means inform the IPC tasklet to pass
* @reset_det_n: Reset detect flag
* @pcie_wake_n: Pcie wake flag
@@ -375,7 +373,6 @@ struct iosm_imem {
u8 ev_irq_pending[IPC_IRQ_VECTORS];
unsigned long flag;
u8 td_update_timer_suspended:1,
- ev_cdev_write_pending:1,
ev_mux_net_transmit_pending:1,
reset_det_n:1,
pcie_wake_n:1;
diff --git a/drivers/net/wwan/iosm/iosm_ipc_imem_ops.c b/drivers/net/wwan/iosm/iosm_ipc_imem_ops.c
index 09261fbb79c1..831cdae28e8a 100644
--- a/drivers/net/wwan/iosm/iosm_ipc_imem_ops.c
+++ b/drivers/net/wwan/iosm/iosm_ipc_imem_ops.c
@@ -41,7 +41,6 @@ void ipc_imem_sys_wwan_close(struct iosm_imem *ipc_imem, int if_id,
static int ipc_imem_tq_cdev_write(struct iosm_imem *ipc_imem, int arg,
void *msg, size_t size)
{
- ipc_imem->ev_cdev_write_pending = false;
ipc_imem_ul_send(ipc_imem);
return 0;
@@ -50,11 +49,6 @@ static int ipc_imem_tq_cdev_write(struct iosm_imem *ipc_imem, int arg,
/* Through tasklet to do sio write. */
static int ipc_imem_call_cdev_write(struct iosm_imem *ipc_imem)
{
- if (ipc_imem->ev_cdev_write_pending)
- return -1;
-
- ipc_imem->ev_cdev_write_pending = true;
-
return ipc_task_queue_send_task(ipc_imem, ipc_imem_tq_cdev_write, 0,
NULL, 0, false);
}
--
2.25.1
^ permalink raw reply related
* [PATCH net 2/3] net: wwan: iosm: fixes net interface nonfunctional after fw flash
From: M Chetan Kumar @ 2021-12-09 10:16 UTC (permalink / raw)
To: netdev
Cc: kuba, davem, johannes, ryazanov.s.a, loic.poulain, krishna.c.sudi,
m.chetan.kumar, m.chetan.kumar, linuxwwan
In-Reply-To: <20211209101629.2940877-1-m.chetan.kumar@linux.intel.com>
Devlink initialization flow was overwriting the IP traffic
channel configuration. This was causing wwan0 network interface
to be unusable after fw flash.
When device boots to fully functional mode restore the IP channel
configuration.
Signed-off-by: M Chetan Kumar <m.chetan.kumar@linux.intel.com>
Reviewed-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
---
drivers/net/wwan/iosm/iosm_ipc_imem.c | 7 ++++++-
drivers/net/wwan/iosm/iosm_ipc_imem.h | 1 +
drivers/net/wwan/iosm/iosm_ipc_imem_ops.c | 1 +
3 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wwan/iosm/iosm_ipc_imem.c b/drivers/net/wwan/iosm/iosm_ipc_imem.c
index b4d47b31ba91..e2c096863488 100644
--- a/drivers/net/wwan/iosm/iosm_ipc_imem.c
+++ b/drivers/net/wwan/iosm/iosm_ipc_imem.c
@@ -531,6 +531,9 @@ static void ipc_imem_run_state_worker(struct work_struct *instance)
return;
}
+ if (test_and_clear_bit(IOSM_DEVLINK_INIT, &ipc_imem->flag))
+ ipc_devlink_deinit(ipc_imem->ipc_devlink);
+
if (!ipc_imem_setup_cp_mux_cap_init(ipc_imem, &mux_cfg))
ipc_imem->mux = ipc_mux_init(&mux_cfg, ipc_imem);
@@ -1171,7 +1174,7 @@ void ipc_imem_cleanup(struct iosm_imem *ipc_imem)
ipc_port_deinit(ipc_imem->ipc_port);
}
- if (ipc_imem->ipc_devlink)
+ if (test_and_clear_bit(IOSM_DEVLINK_INIT, &ipc_imem->flag))
ipc_devlink_deinit(ipc_imem->ipc_devlink);
ipc_imem_device_ipc_uninit(ipc_imem);
@@ -1335,6 +1338,8 @@ struct iosm_imem *ipc_imem_init(struct iosm_pcie *pcie, unsigned int device_id,
if (ipc_flash_link_establish(ipc_imem))
goto devlink_channel_fail;
+
+ set_bit(IOSM_DEVLINK_INIT, &ipc_imem->flag);
}
return ipc_imem;
devlink_channel_fail:
diff --git a/drivers/net/wwan/iosm/iosm_ipc_imem.h b/drivers/net/wwan/iosm/iosm_ipc_imem.h
index 6be6708b4eec..6b479fe23a42 100644
--- a/drivers/net/wwan/iosm/iosm_ipc_imem.h
+++ b/drivers/net/wwan/iosm/iosm_ipc_imem.h
@@ -101,6 +101,7 @@ struct ipc_chnl_cfg;
#define IOSM_CHIP_INFO_SIZE_MAX 100
#define FULLY_FUNCTIONAL 0
+#define IOSM_DEVLINK_INIT 1
/* List of the supported UL/DL pipes. */
enum ipc_mem_pipes {
diff --git a/drivers/net/wwan/iosm/iosm_ipc_imem_ops.c b/drivers/net/wwan/iosm/iosm_ipc_imem_ops.c
index 825e8e5ffb2a..09261fbb79c1 100644
--- a/drivers/net/wwan/iosm/iosm_ipc_imem_ops.c
+++ b/drivers/net/wwan/iosm/iosm_ipc_imem_ops.c
@@ -450,6 +450,7 @@ void ipc_imem_sys_devlink_close(struct iosm_devlink *ipc_devlink)
/* Release the pipe resources */
ipc_imem_pipe_cleanup(ipc_imem, &channel->ul_pipe);
ipc_imem_pipe_cleanup(ipc_imem, &channel->dl_pipe);
+ ipc_imem->nr_of_channels--;
}
void ipc_imem_sys_devlink_notify_rx(struct iosm_devlink *ipc_devlink,
--
2.25.1
^ permalink raw reply related
* [PATCH net 1/3] net: wwan: iosm: fixes unnecessary doorbell send
From: M Chetan Kumar @ 2021-12-09 10:16 UTC (permalink / raw)
To: netdev
Cc: kuba, davem, johannes, ryazanov.s.a, loic.poulain, krishna.c.sudi,
m.chetan.kumar, m.chetan.kumar, linuxwwan
In-Reply-To: <20211209101629.2940877-1-m.chetan.kumar@linux.intel.com>
In TX packet accumulation flow transport layer is
giving a doorbell to device even though there is
no pending control TX transfer that needs immediate
attention.
Introduced a new hpda_ctrl_pending variable to keep
track of pending control TX transfer. If there is a
pending control TX transfer which needs an immediate
attention only then give a doorbell to device.
Signed-off-by: M Chetan Kumar <m.chetan.kumar@linux.intel.com>
Reviewed-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
---
drivers/net/wwan/iosm/iosm_ipc_imem.c | 18 +++++++++++-------
1 file changed, 11 insertions(+), 7 deletions(-)
diff --git a/drivers/net/wwan/iosm/iosm_ipc_imem.c b/drivers/net/wwan/iosm/iosm_ipc_imem.c
index cff3b43ca4d7..b4d47b31ba91 100644
--- a/drivers/net/wwan/iosm/iosm_ipc_imem.c
+++ b/drivers/net/wwan/iosm/iosm_ipc_imem.c
@@ -181,9 +181,9 @@ void ipc_imem_hrtimer_stop(struct hrtimer *hr_timer)
bool ipc_imem_ul_write_td(struct iosm_imem *ipc_imem)
{
struct ipc_mem_channel *channel;
+ bool hpda_ctrl_pending = false;
struct sk_buff_head *ul_list;
bool hpda_pending = false;
- bool forced_hpdu = false;
struct ipc_pipe *pipe;
int i;
@@ -200,15 +200,19 @@ bool ipc_imem_ul_write_td(struct iosm_imem *ipc_imem)
ul_list = &channel->ul_list;
/* Fill the transfer descriptor with the uplink buffer info. */
- hpda_pending |= ipc_protocol_ul_td_send(ipc_imem->ipc_protocol,
+ if (!ipc_imem_check_wwan_ips(channel)) {
+ hpda_ctrl_pending |=
+ ipc_protocol_ul_td_send(ipc_imem->ipc_protocol,
pipe, ul_list);
-
- /* forced HP update needed for non data channels */
- if (hpda_pending && !ipc_imem_check_wwan_ips(channel))
- forced_hpdu = true;
+ } else {
+ hpda_pending |=
+ ipc_protocol_ul_td_send(ipc_imem->ipc_protocol,
+ pipe, ul_list);
+ }
}
- if (forced_hpdu) {
+ /* forced HP update needed for non data channels */
+ if (hpda_ctrl_pending) {
hpda_pending = false;
ipc_protocol_doorbell_trigger(ipc_imem->ipc_protocol,
IPC_HP_UL_WRITE_TD);
--
2.25.1
^ permalink raw reply related
* [PATCH net 0/3] net: wwan: iosm: bug fixes
From: M Chetan Kumar @ 2021-12-09 10:16 UTC (permalink / raw)
To: netdev
Cc: kuba, davem, johannes, ryazanov.s.a, loic.poulain, krishna.c.sudi,
m.chetan.kumar, m.chetan.kumar, linuxwwan
This patch series brings in IOSM driver bug fixes. Patch details are
explained below.
PATCH1:
* stop sending unnecessary doorbell in IP tx flow.
PATCH2:
* Restore the IP channel configuration after fw flash.
PATCH3:
* Removed the unnecessary check around control port TX transfer.
M Chetan Kumar (3):
net: wwan: iosm: fixes unnecessary doorbell send
net: wwan: iosm: fixes net interface nonfunctional after fw flash
net: wwan: iosm: fixes unable to send AT command during mbim tx
drivers/net/wwan/iosm/iosm_ipc_imem.c | 26 +++++++++++++++--------
drivers/net/wwan/iosm/iosm_ipc_imem.h | 4 +---
drivers/net/wwan/iosm/iosm_ipc_imem_ops.c | 7 +-----
3 files changed, 19 insertions(+), 18 deletions(-)
--
2.25.1
^ permalink raw reply
* [PATCH v2, 2/2] net: Add DM9051 driver
From: JosephCHANG @ 2021-12-09 10:07 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Rob Herring, Joseph CHANG,
joseph_chang
Cc: netdev, devicetree, linux-kernel
In-Reply-To: <20211209100702.5609-1-josright123@gmail.com>
Add davicom dm9051 SPI ethernet driver. The driver work with dts
for:
- spi bus number
- spi chip select
- spi clock frequency
- interrupt gpio pin
- interrupt polarity fixed as low
Test OK with Rpi 2 and Rpi 4. Max spi speed is 31200000.
Signed-off-by: JosephCHANG <josright123@gmail.com>
[Submit v1 has Reported-by: kernel test robot <lkp@intel.com>]
---
drivers/net/ethernet/davicom/Kconfig | 30 +
drivers/net/ethernet/davicom/Makefile | 1 +
drivers/net/ethernet/davicom/dm9051.c | 967 ++++++++++++++++++++++++++
drivers/net/ethernet/davicom/dm9051.h | 248 +++++++
4 files changed, 1246 insertions(+)
create mode 100644 drivers/net/ethernet/davicom/dm9051.c
create mode 100644 drivers/net/ethernet/davicom/dm9051.h
diff --git a/drivers/net/ethernet/davicom/Kconfig b/drivers/net/ethernet/davicom/Kconfig
index 7af86b6d4150..9c00328f6e05 100644
--- a/drivers/net/ethernet/davicom/Kconfig
+++ b/drivers/net/ethernet/davicom/Kconfig
@@ -3,6 +3,20 @@
# Davicom device configuration
#
+config NET_VENDOR_DAVICOM
+ bool "Davicom devices"
+ depends on ARM || MIPS || COLDFIRE || NIOS2 || COMPILE_TEST || SPI
+ default y
+ help
+ If you have a network (Ethernet) card belonging to this class, say Y.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Davicom devices. If you say Y, you will be asked
+ for your specific card in the following selections.
+
+if NET_VENDOR_DAVICOM
+
config DM9000
tristate "DM9000 support"
depends on ARM || MIPS || COLDFIRE || NIOS2 || COMPILE_TEST
@@ -22,3 +36,19 @@ config DM9000_FORCE_SIMPLE_PHY_POLL
bit to determine if the link is up or down instead of the more
costly MII PHY reads. Note, this will not work if the chip is
operating with an external PHY.
+
+config DM9051
+ tristate "DM9051 SPI support"
+ depends on SPI
+ select CRC32
+ select MII
+ help
+ Support for DM9051 SPI chipset.
+
+ To compile this driver as a module, choose M here. The module
+ will be called dm9051.
+
+ The SPI mode for the host's SPI master to access DM9051 is mode
+ 0 on the SPI bus.
+
+endif # NET_VENDOR_DAVICOM
diff --git a/drivers/net/ethernet/davicom/Makefile b/drivers/net/ethernet/davicom/Makefile
index 173c87d21076..225f85bc1f53 100644
--- a/drivers/net/ethernet/davicom/Makefile
+++ b/drivers/net/ethernet/davicom/Makefile
@@ -4,3 +4,4 @@
#
obj-$(CONFIG_DM9000) += dm9000.o
+obj-$(CONFIG_DM9051) += dm9051.o
diff --git a/drivers/net/ethernet/davicom/dm9051.c b/drivers/net/ethernet/davicom/dm9051.c
new file mode 100644
index 000000000000..cdcf9d37ed7f
--- /dev/null
+++ b/drivers/net/ethernet/davicom/dm9051.c
@@ -0,0 +1,967 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Ethernet driver for the Davicom DM9051 chip.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Copyright 2021 Davicom Semiconductor,Inc.
+ * http://www.davicom.com.tw/
+ * Joseph CHANG <joseph_chang@davicom.com.tw>
+ * 20211110b, Total 933 lines
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/interrupt.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/cache.h>
+#include <linux/crc32.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/iopoll.h>
+#include <linux/of_gpio.h>
+#include <linux/spi/spi.h>
+
+#include "dm9051.h"
+
+#define DRV_PRODUCT_NAME "dm9051"
+#define DRV_VERSION_CODE DM_VERSION(5, 0, 5) //(VER5.0.0= 0x050000)
+#define DRV_VERSION_DATE "20211209" //(update)"
+
+/* spi-spi_sync, low level code */
+static int burst_xfer(struct board_info *db, u8 cmdphase, u8 *txb, u8 *rxb, unsigned int len)
+{
+ struct device *dev = &db->spidev->dev;
+ int ret = 0;
+
+ db->cmd[0] = cmdphase;
+ db->spi_xfer2[0].tx_buf = &db->cmd[0];
+ db->spi_xfer2[0].rx_buf = NULL;
+ db->spi_xfer2[0].len = 1;
+ if (!rxb) { //write
+ db->spi_xfer2[1].tx_buf = txb;
+ db->spi_xfer2[1].rx_buf = NULL;
+ db->spi_xfer2[1].len = len;
+ } else { //read
+ db->spi_xfer2[1].tx_buf = txb;
+ db->spi_xfer2[1].rx_buf = rxb;
+ db->spi_xfer2[1].len = len;
+ }
+ ret = spi_sync(db->spidev, &db->spi_msg2);
+ if (ret < 0)
+ dev_err(dev, "dm9Err spi burst cmd 0x%02x, ret=%d\n", cmdphase, ret);
+ return ret;
+}
+
+static u8 std_spi_read_reg(struct board_info *db, unsigned int reg)
+{
+ u8 rxb[1];
+
+ burst_xfer(db, DM_SPI_RD | reg, NULL, rxb, 1);
+ return rxb[0];
+}
+
+/* chip ID display */
+static u8 disp_spi_read_reg(struct device *dev, struct board_info *db,
+ unsigned int reg)
+{
+ u8 rxdata;
+
+ rxdata = std_spi_read_reg(db, reg);
+ if (reg == DM9051_PIDL || reg == DM9051_PIDH)
+ dev_info(dev, "dm905.MOSI.p.[%02x][..]\n", reg);
+ if (reg == DM9051_PIDL || reg == DM9051_PIDH)
+ dev_info(dev, "dm905.MISO.e.[..][%02x]\n", rxdata);
+ return rxdata;
+}
+
+static void std_spi_write_reg(struct board_info *db, unsigned int reg, unsigned int val)
+{
+ u8 txb[1];
+
+ txb[0] = val;
+ burst_xfer(db, DM_SPI_WR | reg, txb, NULL, 1);
+}
+
+static void std_read_rx_buf_ncpy(struct board_info *db, u8 *buff, unsigned int len)
+{
+ u8 txb[1];
+
+ burst_xfer(db, DM_SPI_RD | DM_SPI_MRCMD, txb, buff, len);
+}
+
+static int std_write_tx_buf(struct board_info *db, u8 *buff, unsigned int len)
+{
+ burst_xfer(db, DM_SPI_WR | DM_SPI_MWCMD, buff, NULL, len);
+ return 0;
+}
+
+/* basic read/write to phy
+ */
+static int dm_phy_read_func(struct board_info *db, int reg)
+{
+ int ret;
+ u8 check_val;
+
+ iow(db, DM9051_EPAR, DM9051_PHY | reg);
+ iow(db, DM9051_EPCR, EPCR_ERPRR | EPCR_EPOS);
+ read_poll_timeout(ior, check_val, !(check_val & EPCR_ERRE), 100, 10000,
+ true, db, DM9051_EPCR);
+ iow(db, DM9051_EPCR, 0x0);
+ ret = (ior(db, DM9051_EPDRH) << 8) | ior(db, DM9051_EPDRL);
+ return ret;
+}
+
+static void dm_phy_write_func(struct board_info *db, int reg, int value)
+{
+ u8 check_val;
+
+ iow(db, DM9051_EPAR, DM9051_PHY | reg);
+ iow(db, DM9051_EPDRL, value);
+ iow(db, DM9051_EPDRH, value >> 8);
+ iow(db, DM9051_EPCR, EPCR_EPOS | EPCR_ERPRW);
+ read_poll_timeout(ior, check_val, !(check_val & EPCR_ERRE), 100, 10000,
+ true, db, DM9051_EPCR);
+ iow(db, DM9051_EPCR, 0x0);
+}
+
+/* Read a word data from SROM
+ */
+static void dm_read_eeprom_func(struct board_info *db, int offset, u8 *to)
+{
+ u8 check_val;
+
+ mutex_lock(&db->addr_lock);
+ iow(db, DM9051_EPAR, offset);
+ iow(db, DM9051_EPCR, EPCR_ERPRR);
+ read_poll_timeout(ior, check_val, !(check_val & EPCR_ERRE), 100, 10000,
+ true, db, DM9051_EPCR);
+ iow(db, DM9051_EPCR, 0x0);
+ to[0] = ior(db, DM9051_EPDRL);
+ to[1] = ior(db, DM9051_EPDRH);
+ mutex_unlock(&db->addr_lock);
+}
+
+/* Write a word data to SROM
+ */
+static void dm_write_eeprom_func(struct board_info *db, int offset, u8 *data)
+{
+ u8 check_val;
+
+ mutex_lock(&db->addr_lock);
+ iow(db, DM9051_EPAR, offset);
+ iow(db, DM9051_EPDRH, data[1]);
+ iow(db, DM9051_EPDRL, data[0]);
+ iow(db, DM9051_EPCR, EPCR_WEP | EPCR_ERPRW);
+ read_poll_timeout(ior, check_val, !(check_val & EPCR_ERRE), 100, 10000,
+ true, db, DM9051_EPCR);
+ iow(db, DM9051_EPCR, 0);
+ mutex_unlock(&db->addr_lock);
+}
+
+static int dm9051_phy_read_lock(struct net_device *dev, int phy_reg_unused, int reg)
+{
+ int val;
+ struct board_info *db = netdev_priv(dev);
+
+ mutex_lock(&db->addr_lock);
+ val = dm_phy_read_func(db, reg);
+ mutex_unlock(&db->addr_lock);
+ return val;
+}
+
+static void dm9051_phy_write_lock(struct net_device *dev, int phyaddr_unused, int reg, int value)
+{
+ struct board_info *db = netdev_priv(dev);
+
+ mutex_lock(&db->addr_lock);
+ dm_phy_write_func(db, reg, value);
+ mutex_unlock(&db->addr_lock);
+}
+
+/* read chip id
+ */
+static unsigned int dm9051_chipid(struct device *dev, struct board_info *db)
+{
+ unsigned int chipid;
+
+ chipid = iior(dev, db, DM9051_PIDL);
+ chipid |= (unsigned int)iior(dev, db, DM9051_PIDH) << 8;
+ if (chipid == DM9051_ID)
+ return chipid;
+ chipid = iior(dev, db, DM9051_PIDL);
+ chipid |= (unsigned int)iior(dev, db, DM9051_PIDH) << 8;
+ if (chipid == DM9051_ID)
+ return chipid;
+ dev_dbg(dev, "Read [DM9051_PID] = %04x\n", chipid);
+ dev_dbg(dev, "Read [DM9051_PID] error!\n");
+ return chipid;
+}
+
+static void dm9051_reset(struct board_info *db)
+{
+ mdelay(2); //need before NCR_RST
+ ncr_reg_reset(db);
+ mdelay(1);
+ mbd_reg_byte(db);
+ mdelay(1);
+ dm_phy_write_func(db, MII_ADVERTISE, ADVERTISE_PAUSE_CAP |
+ ADVERTISE_ALL | ADVERTISE_CSMA); //for fcr, essential
+ fcr_reg_enable(db);
+ ppcr_reg_seeting(db);
+ ledcr_reg_setting(db, db->lcr_all);
+ intcr_reg_setval(db);
+}
+
+/* ESSENTIAL, ensure rxFifoPoint control, disable/enable the interrupt mask
+ */
+static void dm_imr_disable_lock_essential(struct board_info *db)
+{
+ mutex_lock(&db->addr_lock);
+ imr_reg_stop(db);
+ mutex_unlock(&db->addr_lock);
+}
+
+static void dm_imr_enable_lock_essential(struct board_info *db)
+{
+ mutex_lock(&db->addr_lock);
+ imr_reg_start(db, db->imr_all); //exactly ncr-rst then rxp to 0xc00
+ //rcr_reg_start(db, db->rcr_all); //rx enable later
+ mutex_unlock(&db->addr_lock);
+}
+
+/* functions process mac address is major from EEPROM
+ */
+static void dm9051_read_mac_to_dev(struct device *dev, struct net_device *ndev,
+ struct board_info *db)
+{
+ int i;
+
+ for (i = 0; i < ETH_ALEN; i++)
+ ndev->dev_addr[i] = ior(db, DM9051_PAR + i);
+ if (!is_valid_ether_addr(ndev->dev_addr)) {
+ eth_hw_addr_random(ndev);
+ dev_dbg(dev, "dm9 [reg_netdev][%s][chip MAC: %pM (%s)]\n",
+ ndev->name, ndev->dev_addr, "FIX-1");
+ }
+}
+
+/* mac, hash, and rx enable temporarily
+ */
+static void dm_set_multicast_list_lock(struct board_info *db)
+{
+ struct net_device *ndev = db->ndev;
+
+ if (db->enter_hash) {
+ u8 rcr = RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN;
+ int i, oft;
+ u32 hash_val;
+ u16 hash_table[4];
+ struct netdev_hw_addr *ha;
+
+ db->enter_hash = 0;
+ mutex_lock(&db->addr_lock);
+ for (i = 0, oft = DM9051_PAR; i < ETH_ALEN; i++, oft++)
+ iow(db, oft, ndev->dev_addr[i]);
+
+ /* Clear Hash Table */
+ for (i = 0; i < 4; i++)
+ hash_table[i] = 0x0;
+
+ /* broadcast address */
+ hash_table[3] = 0x8000;
+
+ if (ndev->flags & IFF_PROMISC) {
+ rcr |= RCR_PRMSC;
+ netdev_dbg(ndev, "set_multicast rcr |= RCR_PRMSC, rcr= %02x\n", rcr);
+ }
+
+ if (ndev->flags & IFF_ALLMULTI) {
+ rcr |= RCR_ALL;
+ netdev_dbg(ndev, "set_multicast rcr |= RCR_ALLMULTI, rcr= %02x\n", rcr);
+ }
+
+ /* the multicast address in Hash Table : 64 bits */
+ netdev_for_each_mc_addr(ha, ndev) {
+ hash_val = ether_crc_le(6, ha->addr) & 0x3f;
+ hash_table[hash_val / 16] |= (u16)1 << (hash_val % 16);
+ }
+ /* Write the hash table */
+ for (i = 0, oft = DM9051_MAR; i < 4; i++) {
+ iow(db, oft++, hash_table[i]);
+ iow(db, oft++, hash_table[i] >> 8);
+ }
+ db->rcr_all = rcr;
+ rcr_reg_start(db, db->rcr_all);
+ mutex_unlock(&db->addr_lock);
+ }
+}
+
+/* set mac permanently
+ */
+static void dm_set_mac_lock(struct board_info *db)
+{
+ struct net_device *ndev = db->ndev;
+
+ if (db->enter_setmac) {
+ int i, oft;
+
+ db->enter_setmac = 0;
+ netdev_dbg(ndev, "set_mac_address %pM\n", ndev->dev_addr);
+
+ /* write to net device and chip */
+ mutex_lock(&db->addr_lock);
+ for (i = 0, oft = DM9051_PAR; i < ETH_ALEN; i++, oft++)
+ iow(db, oft, ndev->dev_addr[i]);
+ mutex_unlock(&db->addr_lock);
+
+ /* write to EEPROM */
+ for (i = 0; i < ETH_ALEN; i += 2)
+ dm_write_eeprom_func(db, i / 2, (u8 *)&ndev->dev_addr[i]);
+ }
+}
+
+/* tables, netdev-ops
+ */
+static const struct of_device_id dm9051_match_table[] = {
+ { .compatible = "davicom,dm9051", },
+ {}
+};
+
+struct spi_device_id dm9051_spi_id_table = {
+ "dm9051",
+ 0
+};
+
+static
+const struct net_device_ops dm9051_netdev_ops = {
+ .ndo_open = dm9051_open,
+ .ndo_stop = dm9051_stop,
+ .ndo_start_xmit = dm9051_start_xmit,
+ .ndo_set_rx_mode = dm9051_set_multicast_list_schedule,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = dm9051_set_mac_address,
+};
+
+/* table, ethtool-ops
+ */
+static void dm9051_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ struct board_info *dm = to_dm9051_board(dev);
+
+ strscpy(info->driver, DRVNAME_9051, sizeof(info->driver));
+ strscpy(info->version, dm->DRV_VERSION, sizeof(info->version));
+ strscpy(info->bus_info, dev_name(dev->dev.parent), sizeof(info->bus_info));
+}
+
+static void dm9051_set_msglevel(struct net_device *dev, u32 value)
+{
+ struct board_info *dm = to_dm9051_board(dev);
+
+ dm->msg_enable = value;
+}
+
+static u32 dm9051_get_msglevel(struct net_device *dev)
+{
+ struct board_info *dm = to_dm9051_board(dev);
+
+ return dm->msg_enable;
+}
+
+static int dm9051_get_link_ksettings(struct net_device *dev,
+ struct ethtool_link_ksettings *cmd)
+{
+ struct board_info *dm = to_dm9051_board(dev);
+
+ mii_ethtool_get_link_ksettings(&dm->mii, cmd);
+ return 0;
+}
+
+static int dm9051_set_link_ksettings(struct net_device *dev,
+ const struct ethtool_link_ksettings *cmd)
+{
+ struct board_info *dm = to_dm9051_board(dev);
+
+ return mii_ethtool_set_link_ksettings(&dm->mii, cmd);
+}
+
+static int dm9051_nway_reset(struct net_device *dev)
+{
+ struct board_info *dm = to_dm9051_board(dev);
+
+ return mii_nway_restart(&dm->mii);
+}
+
+static u32 dm9051_get_link(struct net_device *dev)
+{
+ struct board_info *db = to_dm9051_board(dev);
+
+ return mii_link_ok(&db->mii);
+}
+
+static int dm9051_get_eeprom_len(struct net_device *dev)
+{
+ return 128;
+}
+
+static int dm9051_get_eeprom(struct net_device *dev,
+ struct ethtool_eeprom *ee, u8 *data)
+{
+ struct board_info *dm = to_dm9051_board(dev);
+ int offset = ee->offset;
+ int len = ee->len;
+ int i;
+
+ if ((len & 1) != 0 || (offset & 1) != 0)
+ return -EINVAL;
+
+ ee->magic = DM_EEPROM_MAGIC;
+
+ for (i = 0; i < len; i += 2)
+ dm_read_eeprom_func(dm, (offset + i) / 2, data + i);
+ return 0;
+}
+
+static int dm9051_set_eeprom(struct net_device *dev,
+ struct ethtool_eeprom *ee, u8 *data)
+{
+ struct board_info *dm = to_dm9051_board(dev);
+ int offset = ee->offset;
+ int len = ee->len;
+ int i;
+
+ if ((len & 1) != 0 || (offset & 1) != 0)
+ return -EINVAL;
+
+ if (ee->magic != DM_EEPROM_MAGIC)
+ return -EINVAL;
+
+ for (i = 0; i < len; i += 2)
+ dm_write_eeprom_func(dm, (offset + i) / 2, data + i);
+ return 0;
+}
+
+static
+const struct ethtool_ops dm9051_ethtool_ops = {
+ .get_drvinfo = dm9051_get_drvinfo,
+ .get_link_ksettings = dm9051_get_link_ksettings,
+ .set_link_ksettings = dm9051_set_link_ksettings,
+ .get_msglevel = dm9051_get_msglevel,
+ .set_msglevel = dm9051_set_msglevel,
+ .nway_reset = dm9051_nway_reset,
+ .get_link = dm9051_get_link,
+ .get_eeprom_len = dm9051_get_eeprom_len,
+ .get_eeprom = dm9051_get_eeprom,
+ .set_eeprom = dm9051_set_eeprom,
+};
+
+static void dm_operation_clear(struct board_info *db)
+{
+ db->bc.mac_ovrsft_counter = 0;
+ db->bc.large_err_counter = 0;
+ db->bc.DO_FIFO_RST_counter = 0;
+ db->enter_hash = 0;
+ db->enter_setmac = 0;
+}
+
+/* reset and increase the RST counter
+ */
+static void dm9051_fifo_reset(u8 state, u8 *hstr, struct board_info *db)
+{
+ db->bc.DO_FIFO_RST_counter++;
+ dm9051_reset(db);
+}
+
+static void dm9051_reset_dm9051(struct board_info *db, int rxlen)
+{
+ struct net_device *ndev = db->ndev;
+ char *sbuff = (char *)db->prxhdr;
+ char hstr[72];
+
+ netdev_dbg(ndev, "dm9-pkt-Wrong RxLen over-range (%x= %d > %x= %d)\n",
+ rxlen, rxlen, DM9051_PKT_MAX, DM9051_PKT_MAX);
+
+ db->bc.large_err_counter++;
+ db->bc.mac_ovrsft_counter++; //increase the MAC over_shift counter
+ dm9051_fifo_reset(11, hstr, db);
+ sprintf(hstr, "dmfifo_reset( 11 RxLenErr ) rxhdr %02x %02x %02x %02x (quick)",
+ sbuff[0], sbuff[1], sbuff[2], sbuff[3]);
+ netdev_dbg(ndev, "%s\n", hstr);
+ netdev_dbg(ndev, " RxLenErr&MacOvrSft_Er %d, RST_c %d\n",
+ db->bc.mac_ovrsft_counter, db->bc.DO_FIFO_RST_counter);
+}
+
+/* loop rx
+ */
+static int dm9051_lrx(struct board_info *db)
+{
+ struct net_device *ndev = db->ndev;
+ u8 rxbyte;
+ int rxlen;
+ char sbuff[DM_RXHDR_SIZE];
+ struct sk_buff *skb;
+ u8 *rdptr;
+ int scanrr = 0;
+
+ while (1) {
+ rxbyte = ior(db, DM_SPI_MRCMDX); //Dummy read
+ rxbyte = ior(db, DM_SPI_MRCMDX); //Dummy read
+ if (rxbyte != DM9051_PKT_RDY) {
+ isr_reg_clear_to_stop_mrcmd(db);
+ break; //exhaust-empty
+ }
+ dm9inblk(db, sbuff, DM_RXHDR_SIZE);
+ isr_reg_clear_to_stop_mrcmd(db);
+
+ db->prxhdr = (struct dm9051_rxhdr *)sbuff;
+ if (db->prxhdr->rxstatus & 0xbf) {
+ netdev_dbg(ndev, "warn : rxhdr.status 0x%02x\n",
+ db->prxhdr->rxstatus);
+ }
+ if (db->prxhdr->rxlen > DM9051_PKT_MAX) {
+ dm9051_reset_dm9051(db, rxlen);
+ return scanrr;
+ }
+
+ rxlen = db->prxhdr->rxlen;
+ skb = dev_alloc_skb(rxlen + 4);
+ if (!skb) {
+ netdev_dbg(ndev, "alloc skb size %d fail\n", rxlen + 4);
+ return scanrr;
+ }
+ skb_reserve(skb, 2);
+ rdptr = (u8 *)skb_put(skb, rxlen - 4);
+
+ dm9inblk(db, rdptr, rxlen);
+ isr_reg_clear_to_stop_mrcmd(db);
+
+ skb->protocol = eth_type_trans(skb, db->ndev); //JJ found: skb->len -= 14
+ if (db->ndev->features & NETIF_F_RXCSUM)
+ skb_checksum_none_assert(skb);
+ if (in_interrupt())
+ netif_rx(skb);
+ else
+ netif_rx_ni(skb);
+ db->ndev->stats.rx_bytes += rxlen;
+ db->ndev->stats.rx_packets++;
+ scanrr++;
+ }
+ return scanrr;
+}
+
+/* single tx
+ */
+static int dm9051_stx(struct board_info *db, u8 *buff, unsigned int len)
+{
+ int ret;
+ u8 check_val;
+
+ /* shorter waiting time with tx-end check JJ20210617 */
+ ret = read_poll_timeout(ior, check_val, check_val & (NSR_TX2END | NSR_TX1END),
+ 1, 20, false, db, DM9051_NSR);
+ dm9outblk(db, buff, len);
+ iow(db, DM9051_TXPLL, len);
+ iow(db, DM9051_TXPLH, len >> 8);
+ iow(db, DM9051_TCR, TCR_TXREQ);
+ return ret;
+}
+
+static int dm9051_send(struct board_info *db)
+{
+ struct net_device *ndev = db->ndev;
+ int ntx = 0;
+
+ while (!skb_queue_empty(&db->txq)) { //when !empty, JJ20140225
+ struct sk_buff *skb;
+
+ skb = dm_sk_buff_get(db);
+ if (skb) {
+ ntx++;
+ if (dm9051_stx(db, skb->data, skb->len))
+ netdev_dbg(ndev, "timeout %d--- WARNING---do-ntx\n", ntx);
+ ndev->stats.tx_bytes += skb->len;
+ ndev->stats.tx_packets++;
+ dev_kfree_skb(skb); //done
+ }
+ }
+ return ntx;
+}
+
+static void dm_msg_open(struct net_device *ndev)
+{
+ struct board_info *db = netdev_priv(ndev);
+ struct device *dev = &db->spidev->dev;
+
+ snprintf(db->DRV_VERSION, sizeof(db->DRV_VERSION), "%s_V%d.%d.%d_date_%s",
+ DRV_PRODUCT_NAME, (DRV_VERSION_CODE >> 16 & 0xff),
+ (DRV_VERSION_CODE >> 8 & 0xff),
+ (DRV_VERSION_CODE & 0xff),
+ DRV_VERSION_DATE);
+ dev_info(dev, "version: %s\n", db->DRV_VERSION);
+}
+
+/* end with enable the interrupt mask
+ */
+static irqreturn_t dm9051_rx_threaded_irq(int irq, void *pw)
+{
+ struct board_info *db = pw;
+ int nrx;
+
+ mutex_lock(&db->spi_lock); //dlywork essential
+ dm_imr_disable_lock_essential(db); //set imr disable
+ if (netif_carrier_ok(db->ndev)) {
+ mutex_lock(&db->addr_lock);
+ do {
+ nrx = dm9051_lrx(db);
+ dm9051_send(db); //+ more performance (yes)
+ } while (nrx);
+ mutex_unlock(&db->addr_lock);
+ }
+ dm_imr_enable_lock_essential(db); //set imr enable
+ mutex_unlock(&db->spi_lock); //dlywork essential
+ return IRQ_HANDLED;
+}
+
+/* end with enable the interrupt mask
+ */
+static int dm_opencode_receiving(struct net_device *ndev, struct board_info *db)
+{
+ int ret;
+ struct spi_device *spi = db->spidev;
+
+ ndev->irq = spi->irq; //by dts
+ ret = request_threaded_irq(spi->irq, NULL, dm9051_rx_threaded_irq,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ ndev->name, db);
+ if (ret < 0) {
+ netdev_err(ndev, "failed to get irq\n");
+ return ret;
+ }
+ dm_imr_enable_lock_essential(db);
+ schedule_delayed_work(&db->phy_poll, HZ * 1); //sched_start
+ netdev_info(ndev, "[dm_open] %pM irq_no %d ACTIVE_LOW\n", ndev->dev_addr, ndev->irq);
+ return 0;
+}
+
+static void int_tx_delay(struct work_struct *w)
+{
+ struct delayed_work *dw = to_delayed_work(w);
+ struct board_info *db = container_of(dw, struct board_info, tx_work);
+
+ mutex_lock(&db->spi_lock); //dlywork essential
+ mutex_lock(&db->addr_lock);
+ dm9051_send(db);
+ mutex_unlock(&db->addr_lock);
+ mutex_unlock(&db->spi_lock); //dlywork essential
+}
+
+static void int_rxctl_delay(struct work_struct *w)
+{
+ struct delayed_work *dw = to_delayed_work(w);
+ struct board_info *db = container_of(dw, struct board_info, rxctrl_work);
+
+ dm_set_multicast_list_lock(db);
+}
+
+static void int_setmac_delay(struct work_struct *w)
+{
+ struct delayed_work *dw = to_delayed_work(w);
+ struct board_info *db = container_of(dw, struct board_info, setmac_work);
+
+ dm_set_mac_lock(db);
+}
+
+static void int_phy_poll(struct work_struct *w)
+{
+ struct delayed_work *dw = to_delayed_work(w);
+ struct board_info *db = container_of(dw, struct board_info, phy_poll);
+
+ dm_carrier_poll(db);
+ schedule_delayed_work(&db->phy_poll, HZ * 1);
+}
+
+/* Irq free and schedule delays cancel
+ */
+static void dm_stopcode_release(struct board_info *db)
+{
+ free_irq(db->spidev->irq, db);
+ cancel_delayed_work_sync(&db->phy_poll);
+ cancel_delayed_work_sync(&db->rxctrl_work);
+ cancel_delayed_work_sync(&db->setmac_work);
+ cancel_delayed_work_sync(&db->tx_work);
+}
+
+static void dm_control_objects_init(struct board_info *db)
+{
+ mutex_init(&db->spi_lock);
+ mutex_init(&db->addr_lock);
+ INIT_DELAYED_WORK(&db->phy_poll, int_phy_poll);
+ INIT_DELAYED_WORK(&db->rxctrl_work, int_rxctl_delay);
+ INIT_DELAYED_WORK(&db->setmac_work, int_setmac_delay);
+ INIT_DELAYED_WORK(&db->tx_work, int_tx_delay);
+}
+
+static void dm9051_init_dm9051(struct net_device *dev)
+{
+ struct board_info *db = netdev_priv(dev);
+
+ dm9051_fifo_reset(1, NULL, db);
+ imr_reg_stop(db);
+}
+
+static void dm9051_init_display(struct board_info *db)
+{
+ ledcr_wr_disp(db); //empty.hook
+ //ledcr_reg_disp(db); //empty.hook
+ dbg_spibcr_peek(db); //empty.hook
+}
+
+static void dm_opencode_lock(struct net_device *dev, struct board_info *db)
+{
+ mutex_lock(&db->addr_lock); //Note: must
+ iow(db, DM9051_GPR, 0); //Note: Reg 1F is not set by reset, REG_1F bit0 activate phyxcer
+ mdelay(1); //delay needs for activate phyxcer
+ db->imr_all = IMR_PAR | IMR_PRM;
+ db->rcr_all = RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN;
+ db->lcr_all = LMCR_MODE1;
+ dm9051_init_dm9051(dev);
+ dm9051_init_display(db);
+ mutex_unlock(&db->addr_lock);
+}
+
+static void dm_stopcode_lock(struct board_info *db)
+{
+ mutex_lock(&db->addr_lock);
+ dm_phy_write_func(db, MII_BMCR, BMCR_RESET); //PHY RESET
+ iow(db, DM9051_GPR, 0x01); //Power-Down PHY
+ rcr_reg_stop(db); //Disable RX
+ mutex_unlock(&db->addr_lock);
+}
+
+static void dm_opencode_net(struct net_device *ndev, struct board_info *db)
+{
+ dm_sk_buff_head_init(db); //skb_queue_head_init
+ netif_start_queue(ndev);
+ netif_wake_queue(ndev);
+ dm_carrier_init(db); //mii_check_
+}
+
+static void dm_stopcode_net(struct net_device *ndev)
+{
+ netif_stop_queue(ndev);
+ dm_carrier_off(ndev); //_carrier_off
+}
+
+/* Open network device
+ * Called when the network device is marked active, such as a user executing
+ * 'ifconfig up' on the device.
+ */
+static int dm9051_open(struct net_device *ndev)
+{
+ struct board_info *db = netdev_priv(ndev);
+
+ dm_msg_open(ndev);
+ dm_opencode_lock(ndev, db);
+ dm_opencode_net(ndev, db);
+ return dm_opencode_receiving(ndev, db);
+}
+
+/* Close network device
+ * Called to close down a network device which has been active. Cancell any
+ * work, shutdown the RX and TX process and then place the chip into a low
+ * power state while it is not being used.
+ */
+static int dm9051_stop(struct net_device *ndev)
+{
+ struct board_info *db = netdev_priv(ndev);
+
+ dm_stopcode_release(db);
+ dm_stopcode_net(ndev);
+ dm_stopcode_lock(db);
+ return 0;
+}
+
+/* event: play a schedule starter in condition
+ */
+static netdev_tx_t dm9051_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct board_info *db = netdev_priv(dev);
+
+ dm_sk_buff_set(db, skb); //JJ: a skb add
+ schedule_delayed_work(&db->tx_work, 0);
+ return NETDEV_TX_OK;
+}
+
+/* event: play with a schedule starter
+ */
+static void dm9051_set_multicast_list_schedule(struct net_device *ndev)
+{
+ struct board_info *db = netdev_priv(ndev);
+
+ db->enter_hash = 1;
+ schedule_delayed_work(&db->rxctrl_work, 0);
+}
+
+/* event: NOT play with a schedule starter! will iow() directly.
+ */
+static int dm9051_set_mac_address(struct net_device *ndev, void *p)
+{
+ struct board_info *db = netdev_priv(ndev);
+ int ret = eth_mac_addr(ndev, p);
+
+ if (ret < 0)
+ return ret;
+ db->enter_setmac = 1;
+ schedule_delayed_work(&db->setmac_work, 0);
+ return 0;
+}
+
+/* probe subs
+ */
+static void dm_netdev_and_db(struct net_device *ndev, struct board_info *db)
+{
+ ndev->mtu = 1500;
+ ndev->if_port = IF_PORT_100BASET;
+ ndev->netdev_ops = &dm9051_netdev_ops;
+ ndev->ethtool_ops = &dm9051_ethtool_ops;
+ db->mii.dev = ndev;
+ db->mii.phy_id = 1;
+ db->mii.phy_id_mask = 1;
+ db->mii.reg_num_mask = 0x1f;
+ db->mii.mdio_read = dm9051_phy_read_lock;
+ db->mii.mdio_write = dm9051_phy_write_lock;
+}
+
+static void dm_spimsg_addtail(struct board_info *db)
+{
+ memset(&db->spi_xfer2, 0, sizeof(struct spi_transfer) * 2);
+ spi_message_init(&db->spi_msg2);
+ spi_message_add_tail(&db->spi_xfer2[0], &db->spi_msg2);
+ spi_message_add_tail(&db->spi_xfer2[1], &db->spi_msg2);
+}
+
+static int dm_chipid_detect(struct device *dev, struct board_info *db)
+{
+ if (dm9051_chipid(dev, db) == DM9051_ID)
+ return 0;
+ return -ENODEV;
+}
+
+static int dm9051_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct net_device *ndev;
+ struct board_info *db;
+ int ret = 0;
+
+ ndev = alloc_etherdev(sizeof(struct board_info));
+ if (!ndev)
+ return -ENOMEM;
+ SET_NETDEV_DEV(ndev, dev);
+ dev_set_drvdata(dev, ndev);
+ db = netdev_priv(ndev);
+ memset(db, 0, sizeof(struct board_info));
+ db->msg_enable = 0;
+ db->spidev = spi;
+ db->ndev = ndev;
+ dm_netdev_and_db(ndev, db);
+
+ dm_spimsg_addtail(db);
+ dm_control_objects_init(db); //init_delayed_works
+ ret = dm_chipid_detect(dev, db); //access to dm9051
+ if (ret) {
+ dev_err(dev, "chip id error\n");
+ goto err_netdev;
+ }
+ dm9051_read_mac_to_dev(dev, ndev, db);
+ ret = register_netdev(ndev);
+ if (ret) {
+ dev_err(dev, "failed to register network device\n");
+ goto err_netdev;
+ }
+ dm_operation_clear(db); //only in probe
+ dm_carrier_off(ndev); //_carrier_off
+ return 0;
+err_netdev:
+ free_netdev(ndev);
+ return ret;
+}
+
+static int dm9051_drv_remove(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct net_device *ndev = dev_get_drvdata(dev);
+ struct board_info *db = netdev_priv(ndev);
+
+ unregister_netdev(db->ndev);
+ free_netdev(db->ndev);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+//[User must config KConfig to PM_SLEEP for the power-down function!!]
+static int dm9051_drv_suspend(struct device *dev)
+{
+ struct net_device *ndev = dev_get_drvdata(dev);
+ struct board_info *db = netdev_priv(ndev);
+
+ if (netif_running(ndev)) {
+ netif_carrier_off(ndev);
+ netif_device_detach(ndev);
+
+ dm_stopcode_lock(db);
+ }
+ return 0;
+}
+
+static int dm9051_drv_resume(struct device *dev)
+{
+ struct net_device *ndev = dev_get_drvdata(dev);
+ struct board_info *db = netdev_priv(ndev);
+
+ if (netif_running(ndev)) {
+ dm_opencode_lock(ndev, db);
+ dm_imr_enable_lock_essential(db);
+
+ netif_device_attach(ndev);
+ netif_carrier_on(ndev);
+ }
+ return 0;
+}
+#endif //DM_PM_SLEEP
+
+static SIMPLE_DEV_PM_OPS(dm9051_drv_pm_ops, dm9051_drv_suspend, dm9051_drv_resume); //DM_PM_SLEEP
+
+static struct spi_driver dm9051_driver = {
+ .driver = {
+ .name = DRVNAME_9051,
+ .owner = THIS_MODULE,
+ .pm = &dm9051_drv_pm_ops, //DM_PM_SLEEP
+ .of_match_table = dm9051_match_table,
+ .bus = &spi_bus_type,
+ },
+ .probe = dm9051_probe,
+ .remove = dm9051_drv_remove,
+ .id_table = &dm9051_spi_id_table,
+};
+module_spi_driver(dm9051_driver);
+
+MODULE_AUTHOR("Joseph CHANG <joseph_chang@davicom.com.tw>");
+MODULE_DESCRIPTION("Davicom DM9051 network SPI driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/davicom/dm9051.h b/drivers/net/ethernet/davicom/dm9051.h
new file mode 100644
index 000000000000..ecf102d3f0af
--- /dev/null
+++ b/drivers/net/ethernet/davicom/dm9051.h
@@ -0,0 +1,248 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2021 Davicom Semiconductor,Inc.
+ * http://www.davicom.com.tw
+ * 2014/03/11 Joseph CHANG v1.0 Create
+ * 2021/10/26 Joseph CHANG v5.0.1 Update
+ * 2021/12/09 Joseph CHANG v5.0.5 Update
+ *
+ * DM9051 register definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef _DM9051_H_
+#define _DM9051_H_
+
+#define DRVNAME_9051 "dm9051"
+
+#define DM9051_ID 0x9051
+
+#define DM9051_NCR 0x00
+#define DM9051_NSR 0x01
+#define DM9051_TCR 0x02
+#define DM9051_RCR 0x05
+#define DM9051_BPTR 0x08
+#define DM9051_FCR 0x0A
+#define DM9051_EPCR 0x0B
+#define DM9051_EPAR 0x0C
+#define DM9051_EPDRL 0x0D
+#define DM9051_EPDRH 0x0E
+#define DM9051_PAR 0x10
+#define DM9051_MAR 0x16
+#define DM9051_GPCR 0x1E
+#define DM9051_GPR 0x1F
+
+#define DM9051_PIDL 0x2A
+#define DM9051_PIDH 0x2B
+#define DM9051_SMCR 0x2F
+#define DM9051_ATCR 0x30
+#define DM9051_SPIBCR 0x38
+#define DM9051_INTCR 0x39
+#define DM9051_PPCR 0x3D
+
+#define DM9051_MPCR 0x55
+#define DM9051_LMCR 0x57
+#define DM9051_MBNDRY 0x5E
+
+#define DM9051_MRRL 0x74
+#define DM9051_MRRH 0x75
+#define DM9051_MWRL 0x7A
+#define DM9051_MWRH 0x7B
+#define DM9051_TXPLL 0x7C
+#define DM9051_TXPLH 0x7D
+#define DM9051_ISR 0x7E
+#define DM9051_IMR 0x7F
+
+#define DM_SPI_MRCMDX (0x70)
+#define DM_SPI_MRCMD (0x72)
+#define DM_SPI_MWCMD (0x78)
+
+#define DM_SPI_RD (0x00)
+#define DM_SPI_WR (0x80)
+
+/* dm9051 Ethernet
+ */
+//0x00
+#define NCR_WAKEEN BIT(6)
+#define NCR_FDX BIT(3)
+#define NCR_RST BIT(0)
+//0x02
+#define TCR_DIS_JABBER_TIMER BIT(6) /* for Jabber Packet support */
+#define TCR_TXREQ BIT(0)
+//0x01
+#define NSR_SPEED BIT(7)
+#define NSR_LINKST BIT(6)
+#define NSR_WAKEST BIT(5)
+#define NSR_TX2END BIT(3)
+#define NSR_TX1END BIT(2)
+//0x05
+#define RCR_DIS_WATCHDOG_TIMER BIT(6) /* for Jabber Packet support */
+#define RCR_DIS_LONG BIT(5)
+#define RCR_DIS_CRC BIT(4)
+#define RCR_ALL BIT(3)
+#define RCR_PRMSC BIT(1)
+#define RCR_RXEN BIT(0)
+#define RCR_RX_DISABLE (RCR_DIS_LONG | RCR_DIS_CRC)
+//0x06
+#define RSR_RF BIT(7)
+#define RSR_MF BIT(6)
+#define RSR_LCS BIT(5)
+#define RSR_RWTO BIT(4)
+#define RSR_PLE BIT(3)
+#define RSR_AE BIT(2)
+#define RSR_CE BIT(1)
+#define RSR_FOE BIT(0)
+//0x0A
+#define FCR_TXPEN BIT(5)
+#define FCR_BKPM BIT(3)
+#define FCR_FLCE BIT(0)
+#define FCR_FLOW_ENABLE (FCR_TXPEN | FCR_BKPM | FCR_FLCE)
+//0x0B
+#define EPCR_WEP BIT(4)
+#define EPCR_EPOS BIT(3)
+#define EPCR_ERPRR BIT(2)
+#define EPCR_ERPRW BIT(1)
+#define EPCR_ERRE BIT(0)
+//0x1E
+#define GPCR_GEP_CNTL BIT(0)
+//0x30
+#define ATCR_AUTO_TX BIT(7)
+//0x39
+#define INTCR_POL_LOW BIT(0)
+#define INTCR_POL_HIGH (0 << 0)
+//0x3D
+// Pause Packet Control Register - default = 1
+#define PPCR_PAUSE_COUNT 0x08
+//0x55
+#define MPCR_RSTTX BIT(1)
+#define MPCR_RSTRX BIT(0)
+//0x57
+// LEDMode Control Register - LEDMode1
+// Value 0x81 : bit[7] = 1, bit[2] = 0, bit[1:0] = 01b
+#define LMCR_NEWMOD BIT(7)
+#define LMCR_TYPED1 BIT(1)
+#define LMCR_TYPED0 BIT(0)
+#define LMCR_MODE1 (LMCR_NEWMOD | LMCR_TYPED0)
+//0x5E
+#define MBNDRY_BYTE BIT(7)
+//0xFE
+#define ISR_MBS BIT(7)
+#define ISR_ROOS BIT(3)
+#define ISR_ROS BIT(2)
+#define ISR_PTS BIT(1)
+#define ISR_PRS BIT(0)
+#define ISR_CLR_STATUS (ISR_ROOS | ISR_ROS | ISR_PTS | ISR_PRS)
+//0xFF
+#define IMR_PAR BIT(7)
+#define IMR_LNKCHGI BIT(5)
+#define IMR_PTM BIT(1)
+#define IMR_PRM BIT(0)
+
+/* Const
+ */
+#define DM9051_PHY 0x40 /* PHY address 0x01 */
+#define DM9051_PKT_RDY 0x01 /* Packet ready to receive */
+#define DM9051_PKT_MAX 1536 /* Received packet max size */
+#define DM_EEPROM_MAGIC (0x9051)
+
+/* netdev_ops
+ */
+static int dm9051_open(struct net_device *dev);
+static int dm9051_stop(struct net_device *dev);
+static netdev_tx_t dm9051_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static void dm9051_set_multicast_list_schedule(struct net_device *dev);
+static int dm9051_set_mac_address(struct net_device *dev, void *p);
+
+static inline struct board_info *to_dm9051_board(struct net_device *dev)
+{
+ return netdev_priv(dev);
+}
+
+/* Driver information
+ */
+#define DM_VERSION(a, b, c) \
+ (((a) << 16) + ((b) << 8) + (c))
+
+/* carrier
+ */
+#define dm_carrier_init(db) mii_check_link(&(db)->mii)
+#define dm_carrier_poll(db) mii_check_link(&(db)->mii)
+#define dm_carrier_off(dev) netif_carrier_off(dev)
+
+/* xmit support
+ */
+#define dm_sk_buff_head_init(db) skb_queue_head_init(&(db)->txq)
+#define dm_sk_buff_get(db) skb_dequeue(&(db)->txq)
+#define dm_sk_buff_set(db, skb) skb_queue_tail(&(db)->txq, skb)
+
+/* spi transfers
+ */
+#define ior std_spi_read_reg //info.ior
+#define iior disp_spi_read_reg //info.iior
+#define iow std_spi_write_reg //info.iow
+#define dm9inblk std_read_rx_buf_ncpy //dm.inblk
+#define dm9outblk std_write_tx_buf //dm.outblk
+
+#define ncr_reg_reset(db) iow(db, DM9051_NCR, NCR_RST) // reset
+#define mbd_reg_byte(db) iow(db, DM9051_MBNDRY, MBNDRY_BYTE) // MemBound
+#define fcr_reg_enable(db) iow(db, DM9051_FCR, FCR_FLOW_ENABLE) // FlowCtrl
+#define ppcr_reg_seeting(db) iow(db, DM9051_PPCR, PPCR_PAUSE_COUNT) // PauPktCn
+#define isr_reg_clear_to_stop_mrcmd(db) iow(db, DM9051_ISR, 0xff) // ClearISR
+#define rcr_reg_stop(db) iow(db, DM9051_RCR, RCR_RX_DISABLE) // DisabRX
+#define imr_reg_stop(db) iow(db, DM9051_IMR, IMR_PAR) // DisabAll
+#define rcr_reg_start(db, rcr_all) iow(db, DM9051_RCR, rcr_all) // EnabRX
+#define imr_reg_start(db, imr_all) iow(db, DM9051_IMR, imr_all) // Re-enab
+#define intcr_reg_setval(db) iow(db, DM9051_INTCR, INTCR_POL_LOW) // INTCR
+#define ledcr_reg_setting(db, lcr_all) iow(db, DM9051_LMCR, lcr_all) // LEDMode1
+
+/* display functions skelton
+ */
+#define ledcr_wr_disp(db)
+#define ledcr_reg_disp(db)
+#define dbg_spibcr_peek(db)
+
+/* structure definitions
+ */
+struct rx_ctl_mach {
+ u16 large_err_counter; /* The error of 'Large Err' */
+ u16 mac_ovrsft_counter; /* The error of 'MacOvrSft_Er' */
+ u16 DO_FIFO_RST_counter; /* The counter of 'fifo_reset' */
+};
+
+struct dm9051_rxhdr {
+ u8 rxpktready;
+ u8 rxstatus;
+ __le16 rxlen;
+};
+
+struct board_info {
+ u8 cmd[2] ____cacheline_aligned;
+ struct spi_transfer spi_xfer2[2] ____cacheline_aligned;
+ struct spi_message spi_msg2 ____cacheline_aligned;
+ struct rx_ctl_mach bc ____cacheline_aligned;
+ struct dm9051_rxhdr *prxhdr ____cacheline_aligned;
+ struct spi_device *spidev;
+ struct net_device *ndev;
+ struct mii_if_info mii;
+ struct sk_buff_head txq;
+ struct mutex spi_lock; // delayed_work's lock
+ struct mutex addr_lock; // dm9051's REG lock
+ struct delayed_work phy_poll;
+ struct delayed_work rxctrl_work;
+ struct delayed_work setmac_work;
+ struct delayed_work tx_work;
+ struct delayed_work rx_work;
+ u32 msg_enable ____cacheline_aligned;
+ u8 imr_all;
+ u8 rcr_all;
+ u8 lcr_all;
+ u16 enter_hash;
+ u16 enter_setmac;
+ char DRV_VERSION[50];
+};
+
+#define DM_RXHDR_SIZE sizeof(struct dm9051_rxhdr)
+
+#endif /* _DM9051_H_ */
--
2.20.1
^ permalink raw reply related
* [PATCH v2, 1/2] yaml: Add dm9051 SPI network yaml file
From: JosephCHANG @ 2021-12-09 10:07 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Rob Herring, Joseph CHANG,
joseph_chang
Cc: netdev, devicetree, linux-kernel
In-Reply-To: <20211209100702.5609-1-josright123@gmail.com>
For support davicom dm9051 device tree config
Signed-off-by: JosephCHANG <josright123@gmail.com>
---
.../bindings/net/davicom,dm9051.yaml | 62 +++++++++++++++++++
1 file changed, 62 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/davicom,dm9051.yaml
diff --git a/Documentation/devicetree/bindings/net/davicom,dm9051.yaml b/Documentation/devicetree/bindings/net/davicom,dm9051.yaml
new file mode 100644
index 000000000000..5e9ce2920bd3
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/davicom,dm9051.yaml
@@ -0,0 +1,62 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/davicom,dm9051.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Davicom DM9051 SPI Ethernet Controller
+
+maintainers:
+ - Joseph CHANG <josright123@gmail.com>
+
+description: |
+ The DM9051 is a fully integrated and cost-effective low pin count single
+ chip Fast Ethernet controller with a Serial Peripheral Interface (SPI).
+
+allOf:
+ - $ref: ethernet-controller.yaml#
+
+properties:
+ compatible:
+ const: davicom,dm9051
+
+ reg:
+ maxItems: 1
+
+ spi-max-frequency:
+ maximum: 45000000
+
+ interrupts:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - spi-max-frequency
+ - interrupts
+
+additionalProperties: false
+
+examples:
+ - |
+ /* for Raspberry Pi with pin control stuff for GPIO irq */
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ dm9051@0 {
+ compatible = "davicom,dm9051";
+ reg = <0>; /* spi chip select */
+ pinctrl-names = "default";
+ pinctrl-0 = <ð_int_pins>;
+ interrupt-parent = <&gpio>;
+ interrupts = <26 IRQ_TYPE_LEVEL_LOW>;
+ spi-max-frequency = <31200000>;
+ };
+ };
+ gpio {
+ eth_int_pins {
+ brcm,pins = <26>;
+ brcm,function = <0>; /* in */
+ brcm,pull = <0>; /* none */
+ };
+ };
--
2.20.1
^ permalink raw reply related
* [PATCH v2, 0/2] ADD DM9051 ETHERNET DRIVER
From: JosephCHANG @ 2021-12-09 10:07 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Rob Herring, Joseph CHANG,
joseph_chang
Cc: netdev, devicetree, linux-kernel
DM9051 is a spi interface ethernet controller chip
Fewer connect pins to CPU compare to DM9000.
It need only cs/mosi/miso/clock and an interrupt gpio pin
JosephCHANG (2):
yaml: Add dm9051 SPI network yaml file
net: Add DM9051 driver
.../bindings/net/davicom,dm9051.yaml | 62 ++
drivers/net/ethernet/davicom/Kconfig | 30 +
drivers/net/ethernet/davicom/Makefile | 1 +
drivers/net/ethernet/davicom/dm9051.c | 967 ++++++++++++++++++
drivers/net/ethernet/davicom/dm9051.h | 248 +++++
5 files changed, 1308 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/davicom,dm9051.yaml
create mode 100644 drivers/net/ethernet/davicom/dm9051.c
create mode 100644 drivers/net/ethernet/davicom/dm9051.h
base-commit: 9d922f5df53844228b9f7c62f2593f4f06c0b69b
--
2.20.1
^ permalink raw reply
* Re: [syzbot] BUG: sleeping function called from invalid context in hci_cmd_sync_cancel
From: Oliver Neukum @ 2021-12-09 10:06 UTC (permalink / raw)
To: syzbot, Thinh.Nguyen, bberg, changbin.du, christian.brauner,
davem, edumazet, gregkh, johan.hedberg, kuba, linux-bluetooth,
linux-kernel, linux-usb, luiz.dentz, luiz.von.dentz, marcel,
mathias.nyman, netdev, stern, syzkaller-bugs, yajun.deng
In-Reply-To: <00000000000098464c05d2acf3ba@google.com>
On 09.12.21 02:59, syzbot wrote:
> syzbot has bisected this issue to:
>
> commit c97a747efc93f94a4ad6c707972dfbf8d774edf9
> Author: Benjamin Berg <bberg@redhat.com>
> Date: Fri Dec 3 14:59:02 2021 +0000
>
> Bluetooth: btusb: Cancel sync commands for certain URB errors
Hi,
looking at the patch, it sleeps in an interrupt handler (or equivalent)
in two places:
@@ -933,6 +933,8 @@ static void btusb_intr_complete(struct urb *urb)
if (err != -EPERM && err != -ENODEV)
bt_dev_err(hdev, "urb %p failed to resubmit (%d)",
urb, -err);
+ if (err != -EPERM)
+ hci_cmd_sync_cancel(hdev, -err);
@@ -1331,10 +1335,13 @@ static void btusb_tx_complete(struct urb *urb)
if (!test_bit(HCI_RUNNING, &hdev->flags))
goto done;
- if (!urb->status)
+ if (!urb->status) {
hdev->stat.byte_tx += urb->transfer_buffer_length;
- else
+ } else {
+ if (hci_skb_pkt_type(skb) == HCI_COMMAND_PKT)
+ hci_cmd_sync_cancel(hdev, -urb->status);
As __cancel_work_timer can be called from hci_cmd_sync_cancel() this is
just not
an approach you can take. It looks like asynchronously canceling the
scheduled work
would result in a race, so I would for now just revert.
What issue exactly is this trying to fix or improve?
Regards
Oliver
^ permalink raw reply
* Re: [PATCH net-next v2 0/6] net: lan966x: Add switchdev and vlan support
From: Horatiu Vultur @ 2021-12-09 9:52 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Florian Fainelli, Vivien Didelot, Vladimir Oltean, Andrew Lunn,
davem, robh+dt, UNGLinuxDriver, netdev
In-Reply-To: <20211208181712.37c41155@kicinski-fedora-pc1c0hjn.dhcp.thefacebook.com>
The 12/08/2021 18:17, Jakub Kicinski wrote:
>
> On Tue, 7 Dec 2021 13:48:32 +0100 Horatiu Vultur wrote:
> > This patch series extends lan966x with switchdev and vlan support.
> > The first patches just adds new registers and extend the MAC table to
> > handle the interrupts when a new address is learn/forget.
> > The last 2 patches adds the vlan and the switchdev support.
>
> Anyone willing to venture a review?
In case someone will have a look at this, I have sent a new version (v3)
where I have cc everyone in this email thread.
--
/Horatiu
^ permalink raw reply
* [PATCH net-next v3 6/6] net: lan966x: Add switchdev support
From: Horatiu Vultur @ 2021-12-09 9:46 UTC (permalink / raw)
To: netdev, devicetree, linux-kernel
Cc: davem, kuba, robh+dt, UNGLinuxDriver, linux, f.fainelli,
vivien.didelot, vladimir.oltean, andrew, Horatiu Vultur
In-Reply-To: <20211209094615.329379-1-horatiu.vultur@microchip.com>
This adds support for switchdev in lan966x.
It offloads to the HW basic forwarding and vlan filtering. To be able to
offload this to the HW, it is required to disable promisc mode for ports
that are part of the bridge.
Signed-off-by: Horatiu Vultur <horatiu.vultur@microchip.com>
---
.../net/ethernet/microchip/lan966x/Makefile | 3 +-
.../ethernet/microchip/lan966x/lan966x_main.c | 41 +-
.../ethernet/microchip/lan966x/lan966x_main.h | 18 +
.../microchip/lan966x/lan966x_switchdev.c | 548 ++++++++++++++++++
.../ethernet/microchip/lan966x/lan966x_vlan.c | 12 +-
5 files changed, 610 insertions(+), 12 deletions(-)
create mode 100644 drivers/net/ethernet/microchip/lan966x/lan966x_switchdev.c
diff --git a/drivers/net/ethernet/microchip/lan966x/Makefile b/drivers/net/ethernet/microchip/lan966x/Makefile
index f7e6068a91cb..d82e896c2e53 100644
--- a/drivers/net/ethernet/microchip/lan966x/Makefile
+++ b/drivers/net/ethernet/microchip/lan966x/Makefile
@@ -6,4 +6,5 @@
obj-$(CONFIG_LAN966X_SWITCH) += lan966x-switch.o
lan966x-switch-objs := lan966x_main.o lan966x_phylink.o lan966x_port.o \
- lan966x_mac.o lan966x_ethtool.o lan966x_vlan.o
+ lan966x_mac.o lan966x_ethtool.o lan966x_switchdev.o \
+ lan966x_vlan.o
diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c
index 1b4c7e6b4f85..aee36c1cfa17 100644
--- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c
+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c
@@ -306,7 +306,7 @@ static int lan966x_port_xmit(struct sk_buff *skb, struct net_device *dev)
return lan966x_port_ifh_xmit(skb, ifh, dev);
}
-static void lan966x_set_promisc(struct lan966x_port *port, bool enable)
+void lan966x_set_promisc(struct lan966x_port *port, bool enable)
{
struct lan966x *lan966x = port->lan966x;
@@ -318,14 +318,18 @@ static void lan966x_set_promisc(struct lan966x_port *port, bool enable)
static void lan966x_port_change_rx_flags(struct net_device *dev, int flags)
{
struct lan966x_port *port = netdev_priv(dev);
+ bool enable;
if (!(flags & IFF_PROMISC))
return;
- if (dev->flags & IFF_PROMISC)
- lan966x_set_promisc(port, true);
- else
- lan966x_set_promisc(port, false);
+ enable = dev->flags & IFF_PROMISC ? true : false;
+ port->promisc = enable;
+
+ if (port->bridge)
+ return;
+
+ lan966x_set_promisc(port, enable);
}
static int lan966x_port_change_mtu(struct net_device *dev, int new_mtu)
@@ -340,7 +344,7 @@ static int lan966x_port_change_mtu(struct net_device *dev, int new_mtu)
return 0;
}
-static int lan966x_mc_unsync(struct net_device *dev, const unsigned char *addr)
+int lan966x_mc_unsync(struct net_device *dev, const unsigned char *addr)
{
struct lan966x_port *port = netdev_priv(dev);
struct lan966x *lan966x = port->lan966x;
@@ -348,7 +352,7 @@ static int lan966x_mc_unsync(struct net_device *dev, const unsigned char *addr)
return lan966x_mac_forget(lan966x, addr, port->pvid, ENTRYTYPE_LOCKED);
}
-static int lan966x_mc_sync(struct net_device *dev, const unsigned char *addr)
+int lan966x_mc_sync(struct net_device *dev, const unsigned char *addr)
{
struct lan966x_port *port = netdev_priv(dev);
struct lan966x *lan966x = port->lan966x;
@@ -401,6 +405,11 @@ static const struct net_device_ops lan966x_port_netdev_ops = {
.ndo_vlan_rx_kill_vid = lan966x_vlan_rx_kill_vid,
};
+bool lan966x_netdevice_check(const struct net_device *dev)
+{
+ return dev && (dev->netdev_ops == &lan966x_port_netdev_ops);
+}
+
static int lan966x_port_xtr_status(struct lan966x *lan966x, u8 grp)
{
return lan_rd(lan966x, QS_XTR_RD(grp));
@@ -537,6 +546,11 @@ static irqreturn_t lan966x_xtr_irq_handler(int irq, void *args)
skb->protocol = eth_type_trans(skb, dev);
+#ifdef CONFIG_NET_SWITCHDEV
+ if (lan966x->ports[src_port]->bridge)
+ skb->offload_fwd_mark = 1;
+#endif
+
netif_rx_ni(skb);
dev->stats.rx_bytes += len;
dev->stats.rx_packets++;
@@ -619,13 +633,16 @@ static int lan966x_probe_port(struct lan966x *lan966x, u32 p,
dev->netdev_ops = &lan966x_port_netdev_ops;
dev->ethtool_ops = &lan966x_ethtool_ops;
+ dev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER |
+ NETIF_F_RXFCS;
+ dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER |
+ NETIF_F_HW_VLAN_CTAG_TX |
+ NETIF_F_HW_VLAN_STAG_TX;
+ dev->priv_flags |= IFF_UNICAST_FLT;
dev->needed_headroom = IFH_LEN * sizeof(u32);
eth_hw_addr_gen(dev, lan966x->base_mac, p + 1);
- lan966x_mac_learn(lan966x, PGID_CPU, dev->dev_addr, port->pvid,
- ENTRYTYPE_LOCKED);
-
port->phylink_config.dev = &port->dev->dev;
port->phylink_config.type = PHYLINK_NETDEV;
port->phylink_pcs.poll = true;
@@ -949,6 +966,8 @@ static int lan966x_probe(struct platform_device *pdev)
lan966x_port_init(lan966x->ports[p]);
}
+ lan966x_register_notifier_blocks(lan966x);
+
return 0;
cleanup_ports:
@@ -967,6 +986,8 @@ static int lan966x_remove(struct platform_device *pdev)
{
struct lan966x *lan966x = platform_get_drvdata(pdev);
+ lan966x_unregister_notifier_blocks(lan966x);
+
lan966x_cleanup_ports(lan966x);
cancel_delayed_work_sync(&lan966x->stats_work);
diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h
index ec3eccf634b3..4a0988087167 100644
--- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h
+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h
@@ -80,6 +80,11 @@ struct lan966x {
struct list_head mac_entries;
spinlock_t mac_lock; /* lock for mac_entries list */
+ /* Notifiers */
+ struct notifier_block netdevice_nb;
+ struct notifier_block switchdev_nb;
+ struct notifier_block switchdev_blocking_nb;
+
u16 vlan_mask[VLAN_N_VID];
DECLARE_BITMAP(cpu_vlan_mask, VLAN_N_VID);
@@ -112,6 +117,10 @@ struct lan966x_port {
struct net_device *dev;
struct lan966x *lan966x;
+ struct net_device *bridge;
+ u8 stp_state;
+ u8 promisc;
+
u8 chip_port;
u16 pvid;
u16 vid;
@@ -129,6 +138,14 @@ extern const struct phylink_mac_ops lan966x_phylink_mac_ops;
extern const struct phylink_pcs_ops lan966x_phylink_pcs_ops;
extern const struct ethtool_ops lan966x_ethtool_ops;
+int lan966x_mc_unsync(struct net_device *dev, const unsigned char *addr);
+int lan966x_mc_sync(struct net_device *dev, const unsigned char *addr);
+
+bool lan966x_netdevice_check(const struct net_device *dev);
+
+int lan966x_register_notifier_blocks(struct lan966x *lan966x);
+void lan966x_unregister_notifier_blocks(struct lan966x *lan966x);
+
void lan966x_stats_get(struct net_device *dev,
struct rtnl_link_stats64 *stats);
int lan966x_stats_init(struct lan966x *lan966x);
@@ -139,6 +156,7 @@ void lan966x_port_status_get(struct lan966x_port *port,
struct phylink_link_state *state);
int lan966x_port_pcs_set(struct lan966x_port *port,
struct lan966x_port_config *config);
+void lan966x_set_promisc(struct lan966x_port *port, bool enable);
void lan966x_port_init(struct lan966x_port *port);
int lan966x_mac_learn(struct lan966x *lan966x, int port,
diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_switchdev.c b/drivers/net/ethernet/microchip/lan966x/lan966x_switchdev.c
new file mode 100644
index 000000000000..ed6ec78d2d9a
--- /dev/null
+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_switchdev.c
@@ -0,0 +1,548 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <linux/if_bridge.h>
+#include <net/switchdev.h>
+
+#include "lan966x_main.h"
+
+static struct workqueue_struct *lan966x_owq;
+
+struct lan966x_fdb_event_work {
+ struct work_struct work;
+ struct switchdev_notifier_fdb_info fdb_info;
+ struct net_device *dev;
+ struct lan966x *lan966x;
+ unsigned long event;
+};
+
+static void lan966x_port_attr_bridge_flags(struct lan966x_port *port,
+ struct switchdev_brport_flags flags)
+{
+ u32 val = lan_rd(port->lan966x, ANA_PGID(PGID_MC));
+
+ val = ANA_PGID_PGID_GET(val);
+
+ if (flags.mask & BR_MCAST_FLOOD) {
+ if (flags.val & BR_MCAST_FLOOD)
+ val |= BIT(port->chip_port);
+ else
+ val &= ~BIT(port->chip_port);
+ }
+
+ lan_rmw(ANA_PGID_PGID_SET(val),
+ ANA_PGID_PGID,
+ port->lan966x, ANA_PGID(PGID_MC));
+}
+
+static u32 lan966x_get_fwd_mask(struct lan966x_port *port)
+{
+ struct net_device *bridge = port->bridge;
+ struct lan966x *lan966x = port->lan966x;
+ u8 ingress_src = port->chip_port;
+ u32 mask = 0;
+ int p;
+
+ if (port->stp_state != BR_STATE_FORWARDING)
+ goto skip_forwarding;
+
+ for (p = 0; p < lan966x->num_phys_ports; p++) {
+ port = lan966x->ports[p];
+
+ if (!port)
+ continue;
+
+ if (port->stp_state == BR_STATE_FORWARDING &&
+ port->bridge == bridge)
+ mask |= BIT(p);
+ }
+
+skip_forwarding:
+ mask &= ~BIT(ingress_src);
+
+ return mask;
+}
+
+static void lan966x_update_fwd_mask(struct lan966x *lan966x)
+{
+ int p;
+
+ for (p = 0; p < lan966x->num_phys_ports; p++) {
+ struct lan966x_port *port = lan966x->ports[p];
+ unsigned long mask = 0;
+
+ if (port->bridge)
+ mask = lan966x_get_fwd_mask(port);
+
+ mask |= BIT(CPU_PORT);
+
+ lan_wr(ANA_PGID_PGID_SET(mask),
+ lan966x, ANA_PGID(PGID_SRC + p));
+ }
+}
+
+static void lan966x_attr_stp_state_set(struct lan966x_port *port,
+ u8 state)
+{
+ struct lan966x *lan966x = port->lan966x;
+ bool learn_ena = 0;
+
+ port->stp_state = state;
+
+ if (state == BR_STATE_FORWARDING || state == BR_STATE_LEARNING)
+ learn_ena = 1;
+
+ lan_rmw(ANA_PORT_CFG_LEARN_ENA_SET(learn_ena),
+ ANA_PORT_CFG_LEARN_ENA,
+ lan966x, ANA_PORT_CFG(port->chip_port));
+
+ lan966x_update_fwd_mask(lan966x);
+}
+
+static void lan966x_port_attr_ageing_set(struct lan966x_port *port,
+ unsigned long ageing_clock_t)
+{
+ unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock_t);
+ u32 ageing_time = jiffies_to_msecs(ageing_jiffies) / 1000;
+
+ lan966x_mac_set_ageing(port->lan966x, ageing_time);
+}
+
+static int lan966x_port_attr_set(struct net_device *dev, const void *ctx,
+ const struct switchdev_attr *attr,
+ struct netlink_ext_ack *extack)
+{
+ struct lan966x_port *port = netdev_priv(dev);
+
+ switch (attr->id) {
+ case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
+ lan966x_port_attr_bridge_flags(port, attr->u.brport_flags);
+ break;
+ case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
+ lan966x_attr_stp_state_set(port, attr->u.stp_state);
+ break;
+ case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
+ lan966x_port_attr_ageing_set(port, attr->u.ageing_time);
+ break;
+ case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
+ lan966x_vlan_port_set_vlan_aware(port, attr->u.vlan_filtering);
+ lan966x_vlan_port_apply(port);
+ lan966x_vlan_cpu_set_vlan_aware(port);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int lan966x_port_bridge_join(struct lan966x_port *port,
+ struct net_device *bridge,
+ struct netlink_ext_ack *extack)
+{
+ struct net_device *dev = port->dev;
+ int err;
+
+ err = switchdev_bridge_port_offload(dev, dev, NULL, NULL, NULL,
+ false, extack);
+ if (err)
+ return err;
+
+ port->bridge = bridge;
+
+ /* Port enters in bridge mode therefor don't need to copy to CPU
+ * frames for multicast in case the bridge is not requesting them
+ */
+ __dev_mc_unsync(dev, lan966x_mc_unsync);
+
+ /* make sure that the promisc is disabled when entering under the bridge
+ * because we don't want all the frames to come to CPU
+ */
+ lan966x_set_promisc(port, false);
+
+ return 0;
+}
+
+static void lan966x_port_bridge_leave(struct lan966x_port *port,
+ struct net_device *bridge)
+{
+ struct lan966x *lan966x = port->lan966x;
+
+ switchdev_bridge_port_unoffload(port->dev, NULL, NULL, NULL);
+ port->bridge = NULL;
+
+ /* Set the port back to host mode */
+ lan966x_vlan_port_set_vlan_aware(port, 0);
+ lan966x_vlan_port_set_vid(port, HOST_PVID, false, false);
+ lan966x_vlan_port_apply(port);
+
+ lan966x_mac_cpu_learn(lan966x, port->dev->dev_addr, HOST_PVID);
+
+ /* Port enters in host more therefore restore mc list */
+ __dev_mc_sync(port->dev, lan966x_mc_sync, lan966x_mc_unsync);
+
+ /* Restore back the promisc as it was before the interfaces was added to
+ * the bridge
+ */
+ lan966x_set_promisc(port, port->promisc);
+}
+
+static int lan966x_port_changeupper(struct net_device *dev,
+ struct netdev_notifier_changeupper_info *info)
+{
+ struct lan966x_port *port = netdev_priv(dev);
+ struct netlink_ext_ack *extack;
+ int err = 0;
+
+ extack = netdev_notifier_info_to_extack(&info->info);
+
+ if (netif_is_bridge_master(info->upper_dev)) {
+ if (info->linking)
+ err = lan966x_port_bridge_join(port, info->upper_dev,
+ extack);
+ else
+ lan966x_port_bridge_leave(port, info->upper_dev);
+ }
+
+ return err;
+}
+
+static int lan966x_port_add_addr(struct net_device *dev, bool up)
+{
+ struct lan966x_port *port = netdev_priv(dev);
+ struct lan966x *lan966x = port->lan966x;
+ u16 vid;
+
+ vid = lan966x_vlan_port_get_pvid(port);
+
+ if (up)
+ lan966x_mac_cpu_learn(lan966x, dev->dev_addr, vid);
+ else
+ lan966x_mac_cpu_forget(lan966x, dev->dev_addr, vid);
+
+ return 0;
+}
+
+static int lan966x_netdevice_port_event(struct net_device *dev,
+ struct notifier_block *nb,
+ unsigned long event, void *ptr)
+{
+ int err = 0;
+
+ if (!lan966x_netdevice_check(dev))
+ return 0;
+
+ switch (event) {
+ case NETDEV_CHANGEUPPER:
+ err = lan966x_port_changeupper(dev, ptr);
+ break;
+ case NETDEV_PRE_UP:
+ err = lan966x_port_add_addr(dev, true);
+ break;
+ case NETDEV_DOWN:
+ err = lan966x_port_add_addr(dev, false);
+ break;
+ }
+
+ return err;
+}
+
+static int lan966x_netdevice_event(struct notifier_block *nb,
+ unsigned long event, void *ptr)
+{
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+ int ret;
+
+ ret = lan966x_netdevice_port_event(dev, nb, event, ptr);
+
+ return notifier_from_errno(ret);
+}
+
+static void lan966x_fdb_event_work(struct work_struct *work)
+{
+ struct lan966x_fdb_event_work *fdb_work =
+ container_of(work, struct lan966x_fdb_event_work, work);
+ struct switchdev_notifier_fdb_info *fdb_info;
+ struct net_device *dev = fdb_work->dev;
+ struct lan966x_port *port;
+ struct lan966x *lan966x;
+
+ rtnl_lock();
+
+ fdb_info = &fdb_work->fdb_info;
+ lan966x = fdb_work->lan966x;
+
+ if (lan966x_netdevice_check(dev)) {
+ port = netdev_priv(dev);
+
+ switch (fdb_work->event) {
+ case SWITCHDEV_FDB_ADD_TO_DEVICE:
+ if (!fdb_info->added_by_user)
+ break;
+ lan966x_mac_add_entry(lan966x, port, fdb_info->addr,
+ fdb_info->vid);
+ break;
+ case SWITCHDEV_FDB_DEL_TO_DEVICE:
+ if (!fdb_info->added_by_user)
+ break;
+ lan966x_mac_del_entry(lan966x, fdb_info->addr, fdb_info->vid);
+ break;
+ }
+ } else {
+ if (!netif_is_bridge_master(dev))
+ goto out;
+
+ /* If the CPU is not part of the vlan then there is no point
+ * to copy the frames to the CPU because they will be dropped
+ */
+ if (!lan966x_vlan_cpu_member_vlan_mask(lan966x, fdb_info->vid))
+ goto out;
+
+ /* In case the bridge is called */
+ switch (fdb_work->event) {
+ case SWITCHDEV_FDB_ADD_TO_DEVICE:
+ /* If there is no front port in this vlan, there is no
+ * point to copy the frame to CPU because it would be
+ * just dropped at later point. So add it only if
+ * there is a port
+ */
+ if (!lan966x_vlan_port_any_vlan_mask(lan966x, fdb_info->vid))
+ break;
+
+ lan966x_mac_cpu_learn(lan966x, fdb_info->addr, fdb_info->vid);
+ break;
+ case SWITCHDEV_FDB_DEL_TO_DEVICE:
+ /* It is OK to always forget the entry it */
+ lan966x_mac_cpu_forget(lan966x, fdb_info->addr, fdb_info->vid);
+ break;
+ }
+ }
+
+out:
+ rtnl_unlock();
+ kfree(fdb_work->fdb_info.addr);
+ kfree(fdb_work);
+ dev_put(dev);
+}
+
+static int lan966x_switchdev_event(struct notifier_block *nb,
+ unsigned long event, void *ptr)
+{
+ struct lan966x *lan966x = container_of(nb, struct lan966x, switchdev_nb);
+ struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
+ struct switchdev_notifier_fdb_info *fdb_info;
+ struct switchdev_notifier_info *info = ptr;
+ struct lan966x_fdb_event_work *fdb_work;
+ int err;
+
+ switch (event) {
+ case SWITCHDEV_PORT_ATTR_SET:
+ err = switchdev_handle_port_attr_set(dev, ptr,
+ lan966x_netdevice_check,
+ lan966x_port_attr_set);
+ return notifier_from_errno(err);
+ case SWITCHDEV_FDB_ADD_TO_DEVICE:
+ fallthrough;
+ case SWITCHDEV_FDB_DEL_TO_DEVICE:
+ fdb_work = kzalloc(sizeof(*fdb_work), GFP_ATOMIC);
+ if (!fdb_work)
+ return NOTIFY_BAD;
+
+ fdb_info = container_of(info,
+ struct switchdev_notifier_fdb_info,
+ info);
+
+ fdb_work->dev = dev;
+ fdb_work->lan966x = lan966x;
+ fdb_work->event = event;
+ INIT_WORK(&fdb_work->work, lan966x_fdb_event_work);
+ memcpy(&fdb_work->fdb_info, ptr, sizeof(fdb_work->fdb_info));
+ fdb_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
+ if (!fdb_work->fdb_info.addr)
+ goto err_addr_alloc;
+
+ ether_addr_copy((u8 *)fdb_work->fdb_info.addr, fdb_info->addr);
+ dev_hold(dev);
+
+ queue_work(lan966x_owq, &fdb_work->work);
+ break;
+ }
+
+ return NOTIFY_DONE;
+err_addr_alloc:
+ kfree(fdb_work);
+ return NOTIFY_BAD;
+}
+
+static int lan966x_handle_port_vlan_add(struct net_device *dev,
+ struct notifier_block *nb,
+ const struct switchdev_obj_port_vlan *v)
+{
+ struct lan966x_port *port;
+ struct lan966x *lan966x;
+
+ /* When adding a port to a vlan, we get a callback for the port but
+ * also for the bridge. When get the callback for the bridge just bail
+ * out. Then when the bridge is added to the vlan, then we get a
+ * callback here but in this case the flags has set:
+ * BRIDGE_VLAN_INFO_BRENTRY. In this case it means that the CPU
+ * port is added to the vlan, so the broadcast frames and unicast frames
+ * with dmac of the bridge should be foward to CPU.
+ */
+ if (netif_is_bridge_master(dev) &&
+ !(v->flags & BRIDGE_VLAN_INFO_BRENTRY))
+ return 0;
+
+ lan966x = container_of(nb, struct lan966x, switchdev_blocking_nb);
+
+ /* In case the port gets called */
+ if (!(netif_is_bridge_master(dev))) {
+ if (!lan966x_netdevice_check(dev))
+ return -EOPNOTSUPP;
+
+ port = netdev_priv(dev);
+ return lan966x_vlan_port_add_vlan(port, v->vid,
+ v->flags & BRIDGE_VLAN_INFO_PVID,
+ v->flags & BRIDGE_VLAN_INFO_UNTAGGED);
+ }
+
+ /* In case the bridge gets called */
+ if (netif_is_bridge_master(dev))
+ return lan966x_vlan_cpu_add_vlan(lan966x, dev, v->vid);
+
+ return 0;
+}
+
+static int lan966x_handle_port_obj_add(struct net_device *dev,
+ struct notifier_block *nb,
+ struct switchdev_notifier_port_obj_info *info)
+{
+ const struct switchdev_obj *obj = info->obj;
+ int err;
+
+ switch (obj->id) {
+ case SWITCHDEV_OBJ_ID_PORT_VLAN:
+ err = lan966x_handle_port_vlan_add(dev, nb,
+ SWITCHDEV_OBJ_PORT_VLAN(obj));
+ break;
+ default:
+ err = -EOPNOTSUPP;
+ break;
+ }
+
+ info->handled = true;
+ return err;
+}
+
+static int lan966x_handle_port_vlan_del(struct net_device *dev,
+ struct notifier_block *nb,
+ const struct switchdev_obj_port_vlan *v)
+{
+ struct lan966x_port *port;
+ struct lan966x *lan966x;
+
+ lan966x = container_of(nb, struct lan966x, switchdev_blocking_nb);
+
+ /* In case the physical port gets called */
+ if (!netif_is_bridge_master(dev)) {
+ if (!lan966x_netdevice_check(dev))
+ return -EOPNOTSUPP;
+
+ port = netdev_priv(dev);
+ return lan966x_vlan_port_del_vlan(port, v->vid);
+ }
+
+ /* In case the bridge gets called */
+ if (netif_is_bridge_master(dev))
+ return lan966x_vlan_cpu_del_vlan(lan966x, dev, v->vid);
+
+ return 0;
+}
+
+static int lan966x_handle_port_obj_del(struct net_device *dev,
+ struct notifier_block *nb,
+ struct switchdev_notifier_port_obj_info *info)
+{
+ const struct switchdev_obj *obj = info->obj;
+ int err;
+
+ switch (obj->id) {
+ case SWITCHDEV_OBJ_ID_PORT_VLAN:
+ err = lan966x_handle_port_vlan_del(dev, nb,
+ SWITCHDEV_OBJ_PORT_VLAN(obj));
+ break;
+ default:
+ err = -EOPNOTSUPP;
+ break;
+ }
+
+ info->handled = true;
+ return err;
+}
+
+static int lan966x_switchdev_blocking_event(struct notifier_block *nb,
+ unsigned long event,
+ void *ptr)
+{
+ struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
+ int err;
+
+ switch (event) {
+ case SWITCHDEV_PORT_OBJ_ADD:
+ err = lan966x_handle_port_obj_add(dev, nb, ptr);
+ return notifier_from_errno(err);
+ case SWITCHDEV_PORT_OBJ_DEL:
+ err = lan966x_handle_port_obj_del(dev, nb, ptr);
+ return notifier_from_errno(err);
+ case SWITCHDEV_PORT_ATTR_SET:
+ err = switchdev_handle_port_attr_set(dev, ptr,
+ lan966x_netdevice_check,
+ lan966x_port_attr_set);
+ return notifier_from_errno(err);
+ }
+
+ return NOTIFY_DONE;
+}
+
+int lan966x_register_notifier_blocks(struct lan966x *lan966x)
+{
+ int err;
+
+ lan966x->netdevice_nb.notifier_call = lan966x_netdevice_event;
+ err = register_netdevice_notifier(&lan966x->netdevice_nb);
+ if (err)
+ return err;
+
+ lan966x->switchdev_nb.notifier_call = lan966x_switchdev_event;
+ err = register_switchdev_notifier(&lan966x->switchdev_nb);
+ if (err)
+ goto err_switchdev_nb;
+
+ lan966x->switchdev_blocking_nb.notifier_call = lan966x_switchdev_blocking_event;
+ err = register_switchdev_blocking_notifier(&lan966x->switchdev_blocking_nb);
+ if (err)
+ goto err_switchdev_blocking_nb;
+
+ lan966x_owq = alloc_ordered_workqueue("lan966x_order", 0);
+ if (!lan966x_owq) {
+ err = -ENOMEM;
+ goto err_switchdev_blocking_nb;
+ }
+
+ return 0;
+
+err_switchdev_blocking_nb:
+ unregister_switchdev_notifier(&lan966x->switchdev_nb);
+err_switchdev_nb:
+ unregister_netdevice_notifier(&lan966x->netdevice_nb);
+
+ return err;
+}
+
+void lan966x_unregister_notifier_blocks(struct lan966x *lan966x)
+{
+ destroy_workqueue(lan966x_owq);
+
+ unregister_switchdev_blocking_notifier(&lan966x->switchdev_blocking_nb);
+ unregister_switchdev_notifier(&lan966x->switchdev_nb);
+ unregister_netdevice_notifier(&lan966x->netdevice_nb);
+}
diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_vlan.c b/drivers/net/ethernet/microchip/lan966x/lan966x_vlan.c
index e47552775d06..26644503b4e6 100644
--- a/drivers/net/ethernet/microchip/lan966x/lan966x_vlan.c
+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_vlan.c
@@ -155,6 +155,9 @@ static bool lan966x_vlan_cpu_member_cpu_vlan_mask(struct lan966x *lan966x, u16 v
u16 lan966x_vlan_port_get_pvid(struct lan966x_port *port)
{
+ if (!port->bridge)
+ return HOST_PVID;
+
return port->vlan_aware ? port->pvid : UNAWARE_PVID;
}
@@ -210,6 +213,8 @@ void lan966x_vlan_cpu_set_vlan_aware(struct lan966x_port *port)
* table for the front port and the CPU
*/
lan966x_mac_cpu_learn(lan966x, port->dev->dev_addr, UNAWARE_PVID);
+ lan966x_mac_cpu_learn(lan966x, port->bridge->dev_addr,
+ UNAWARE_PVID);
lan966x_vlan_port_add_vlan_mask(port, UNAWARE_PVID);
lan966x_vlan_port_apply(port);
@@ -218,6 +223,8 @@ void lan966x_vlan_cpu_set_vlan_aware(struct lan966x_port *port)
* to vlan unaware
*/
lan966x_mac_cpu_forget(lan966x, port->dev->dev_addr, UNAWARE_PVID);
+ lan966x_mac_cpu_forget(lan966x, port->bridge->dev_addr,
+ UNAWARE_PVID);
lan966x_vlan_port_del_vlan_mask(port, UNAWARE_PVID);
lan966x_vlan_port_apply(port);
@@ -293,6 +300,7 @@ int lan966x_vlan_port_add_vlan(struct lan966x_port *port,
*/
if (lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x, vid)) {
lan966x_mac_cpu_learn(lan966x, port->dev->dev_addr, vid);
+ lan966x_mac_cpu_learn(lan966x, port->bridge->dev_addr, vid);
lan966x_vlan_cpu_add_vlan_mask(lan966x, vid);
}
@@ -322,8 +330,10 @@ int lan966x_vlan_port_del_vlan(struct lan966x_port *port,
* that vlan but still keep it in the mask because it may be needed
* again then another port gets added in tha vlan
*/
- if (!lan966x_vlan_port_any_vlan_mask(lan966x, vid))
+ if (!lan966x_vlan_port_any_vlan_mask(lan966x, vid)) {
+ lan966x_mac_cpu_forget(lan966x, port->bridge->dev_addr, vid);
lan966x_vlan_cpu_del_vlan_mask(lan966x, vid);
+ }
return 0;
}
--
2.33.0
^ permalink raw reply related
* [PATCH net-next v3 5/6] net: lan966x: Add vlan support
From: Horatiu Vultur @ 2021-12-09 9:46 UTC (permalink / raw)
To: netdev, devicetree, linux-kernel
Cc: davem, kuba, robh+dt, UNGLinuxDriver, linux, f.fainelli,
vivien.didelot, vladimir.oltean, andrew, Horatiu Vultur
In-Reply-To: <20211209094615.329379-1-horatiu.vultur@microchip.com>
This adds support for vlan in lan966x.
This allows add/remove front ports from vlans and also allows the CPU
port to be added/remove from vlans. In this way it is possible to
filter frames towards the CPU based on the vlan.
Signed-off-by: Horatiu Vultur <horatiu.vultur@microchip.com>
---
.../net/ethernet/microchip/lan966x/Makefile | 2 +-
.../ethernet/microchip/lan966x/lan966x_main.c | 35 +-
.../ethernet/microchip/lan966x/lan966x_main.h | 40 +-
.../ethernet/microchip/lan966x/lan966x_vlan.c | 436 ++++++++++++++++++
4 files changed, 508 insertions(+), 5 deletions(-)
create mode 100644 drivers/net/ethernet/microchip/lan966x/lan966x_vlan.c
diff --git a/drivers/net/ethernet/microchip/lan966x/Makefile b/drivers/net/ethernet/microchip/lan966x/Makefile
index 2989ba528236..f7e6068a91cb 100644
--- a/drivers/net/ethernet/microchip/lan966x/Makefile
+++ b/drivers/net/ethernet/microchip/lan966x/Makefile
@@ -6,4 +6,4 @@
obj-$(CONFIG_LAN966X_SWITCH) += lan966x-switch.o
lan966x-switch-objs := lan966x_main.o lan966x_phylink.o lan966x_port.o \
- lan966x_mac.o lan966x_ethtool.o
+ lan966x_mac.o lan966x_ethtool.o lan966x_vlan.o
diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c
index 7c6d6293611a..1b4c7e6b4f85 100644
--- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c
+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c
@@ -103,17 +103,18 @@ static int lan966x_create_targets(struct platform_device *pdev,
static int lan966x_port_set_mac_address(struct net_device *dev, void *p)
{
struct lan966x_port *port = netdev_priv(dev);
+ u16 pvid = lan966x_vlan_port_get_pvid(port);
struct lan966x *lan966x = port->lan966x;
const struct sockaddr *addr = p;
int ret;
/* Learn the new net device MAC address in the mac table. */
- ret = lan966x_mac_cpu_learn(lan966x, addr->sa_data, port->pvid);
+ ret = lan966x_mac_cpu_learn(lan966x, addr->sa_data, pvid);
if (ret)
return ret;
/* Then forget the previous one. */
- ret = lan966x_mac_cpu_forget(lan966x, dev->dev_addr, port->pvid);
+ ret = lan966x_mac_cpu_forget(lan966x, dev->dev_addr, pvid);
if (ret)
return ret;
@@ -283,6 +284,12 @@ static void lan966x_ifh_set_ipv(void *ifh, u64 bypass)
IFH_POS_IPV, IFH_LEN * 4, PACK, 0);
}
+static void lan966x_ifh_set_vid(void *ifh, u64 vid)
+{
+ packing(ifh, &vid, IFH_POS_TCI + IFH_WID_TCI - 1,
+ IFH_POS_TCI, IFH_LEN * 4, PACK, 0);
+}
+
static int lan966x_port_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct lan966x_port *port = netdev_priv(dev);
@@ -294,6 +301,7 @@ static int lan966x_port_xmit(struct sk_buff *skb, struct net_device *dev)
lan966x_ifh_set_port(ifh, BIT_ULL(port->chip_port));
lan966x_ifh_set_qos_class(ifh, skb->priority >= 7 ? 0x7 : skb->priority);
lan966x_ifh_set_ipv(ifh, skb->priority >= 7 ? 0x7 : skb->priority);
+ lan966x_ifh_set_vid(ifh, skb_vlan_tag_get(skb));
return lan966x_port_ifh_xmit(skb, ifh, dev);
}
@@ -365,6 +373,18 @@ static int lan966x_port_get_parent_id(struct net_device *dev,
return 0;
}
+static int lan966x_port_set_features(struct net_device *dev,
+ netdev_features_t features)
+{
+ struct lan966x_port *port = netdev_priv(dev);
+ netdev_features_t changed = dev->features ^ features;
+
+ if (changed & NETIF_F_HW_VLAN_CTAG_FILTER)
+ lan966x_vlan_mode(port, features);
+
+ return 0;
+}
+
static const struct net_device_ops lan966x_port_netdev_ops = {
.ndo_open = lan966x_port_open,
.ndo_stop = lan966x_port_stop,
@@ -376,6 +396,9 @@ static const struct net_device_ops lan966x_port_netdev_ops = {
.ndo_get_stats64 = lan966x_stats_get,
.ndo_set_mac_address = lan966x_port_set_mac_address,
.ndo_get_port_parent_id = lan966x_port_get_parent_id,
+ .ndo_set_features = lan966x_port_set_features,
+ .ndo_vlan_rx_add_vid = lan966x_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = lan966x_vlan_rx_kill_vid,
};
static int lan966x_port_xtr_status(struct lan966x *lan966x, u8 grp)
@@ -590,7 +613,6 @@ static int lan966x_probe_port(struct lan966x *lan966x, u32 p,
port->dev = dev;
port->lan966x = lan966x;
port->chip_port = p;
- port->pvid = PORT_PVID;
lan966x->ports[p] = port;
dev->max_mtu = ETH_MAX_MTU;
@@ -643,6 +665,10 @@ static int lan966x_probe_port(struct lan966x *lan966x, u32 p,
return err;
}
+ lan966x_vlan_port_set_vlan_aware(port, 0);
+ lan966x_vlan_port_set_vid(port, HOST_PVID, false, false);
+ lan966x_vlan_port_apply(port);
+
return 0;
}
@@ -653,6 +679,9 @@ static void lan966x_init(struct lan966x *lan966x)
/* MAC table initialization */
lan966x_mac_init(lan966x);
+ /* Vlan initialization */
+ lan966x_vlan_init(lan966x);
+
/* Flush queues */
lan_wr(lan_rd(lan966x, QS_XTR_FLUSH) |
GENMASK(1, 0),
diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h
index fcd5d09a070c..ec3eccf634b3 100644
--- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h
+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h
@@ -4,6 +4,7 @@
#define __LAN966X_MAIN_H__
#include <linux/etherdevice.h>
+#include <linux/if_vlan.h>
#include <linux/jiffies.h>
#include <linux/phy.h>
#include <linux/phylink.h>
@@ -22,7 +23,8 @@
#define PGID_SRC 80
#define PGID_ENTRIES 89
-#define PORT_PVID 0
+#define UNAWARE_PVID 0
+#define HOST_PVID 4095
/* Reserved amount for (SRC, PRIO) at index 8*SRC + PRIO */
#define QSYS_Q_RSRV 95
@@ -78,6 +80,9 @@ struct lan966x {
struct list_head mac_entries;
spinlock_t mac_lock; /* lock for mac_entries list */
+ u16 vlan_mask[VLAN_N_VID];
+ DECLARE_BITMAP(cpu_vlan_mask, VLAN_N_VID);
+
/* stats */
const struct lan966x_stat_layout *stats_layout;
u32 num_stats;
@@ -109,6 +114,8 @@ struct lan966x_port {
u8 chip_port;
u16 pvid;
+ u16 vid;
+ u8 vlan_aware;
struct phylink_config phylink_config;
struct phylink_pcs phylink_pcs;
@@ -157,6 +164,37 @@ int lan966x_mac_add_entry(struct lan966x *lan966x,
void lan966x_mac_purge_entries(struct lan966x *lan966x);
irqreturn_t lan966x_mac_irq_handler(struct lan966x *lan966x);
+void lan966x_vlan_init(struct lan966x *lan966x);
+void lan966x_vlan_port_apply(struct lan966x_port *port);
+
+int lan966x_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid);
+int lan966x_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid);
+
+void lan966x_vlan_mode(struct lan966x_port *port, netdev_features_t features);
+u16 lan966x_vlan_port_get_pvid(struct lan966x_port *port);
+
+bool lan966x_vlan_port_member_vlan_mask(struct lan966x_port *port, u16 vid);
+bool lan966x_vlan_cpu_member_vlan_mask(struct lan966x *lan966x, u16 vid);
+bool lan966x_vlan_port_any_vlan_mask(struct lan966x *lan966x, u16 vid);
+
+void lan966x_vlan_cpu_set_vlan_aware(struct lan966x_port *port);
+void lan966x_vlan_port_set_vlan_aware(struct lan966x_port *port,
+ bool vlan_aware);
+int lan966x_vlan_port_set_vid(struct lan966x_port *port, u16 vid,
+ bool pvid, bool untagged);
+int lan966x_vlan_port_add_vlan(struct lan966x_port *port,
+ u16 vid,
+ bool pvid,
+ bool untagged);
+int lan966x_vlan_port_del_vlan(struct lan966x_port *port,
+ u16 vid);
+int lan966x_vlan_cpu_add_vlan(struct lan966x *lan966x,
+ struct net_device *dev,
+ u16 vid);
+int lan966x_vlan_cpu_del_vlan(struct lan966x *lan966x,
+ struct net_device *dev,
+ u16 vid);
+
static inline void __iomem *lan_addr(void __iomem *base[],
int id, int tinst, int tcnt,
int gbase, int ginst,
diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_vlan.c b/drivers/net/ethernet/microchip/lan966x/lan966x_vlan.c
new file mode 100644
index 000000000000..e47552775d06
--- /dev/null
+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_vlan.c
@@ -0,0 +1,436 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include "lan966x_main.h"
+
+#define VLANACCESS_CMD_IDLE 0
+#define VLANACCESS_CMD_READ 1
+#define VLANACCESS_CMD_WRITE 2
+#define VLANACCESS_CMD_INIT 3
+
+static int lan966x_vlan_get_status(struct lan966x *lan966x)
+{
+ return lan_rd(lan966x, ANA_VLANACCESS);
+}
+
+static int lan966x_vlan_wait_for_completion(struct lan966x *lan966x)
+{
+ u32 val;
+
+ return readx_poll_timeout(lan966x_vlan_get_status,
+ lan966x, val,
+ (val & ANA_VLANACCESS_VLAN_TBL_CMD) ==
+ VLANACCESS_CMD_IDLE,
+ TABLE_UPDATE_SLEEP_US, TABLE_UPDATE_TIMEOUT_US);
+}
+
+static int lan966x_vlan_set_mask(struct lan966x *lan966x, u16 vid)
+{
+ u16 mask = lan966x->vlan_mask[vid];
+ bool cpu_dis;
+
+ cpu_dis = !(mask & BIT(CPU_PORT));
+
+ /* Set flags and the VID to configure */
+ lan_rmw(ANA_VLANTIDX_VLAN_PGID_CPU_DIS_SET(cpu_dis) |
+ ANA_VLANTIDX_V_INDEX_SET(vid),
+ ANA_VLANTIDX_VLAN_PGID_CPU_DIS |
+ ANA_VLANTIDX_V_INDEX,
+ lan966x, ANA_VLANTIDX);
+
+ /* Set the vlan port members mask */
+ lan_rmw(ANA_VLAN_PORT_MASK_VLAN_PORT_MASK_SET(mask),
+ ANA_VLAN_PORT_MASK_VLAN_PORT_MASK,
+ lan966x, ANA_VLAN_PORT_MASK);
+
+ /* Issue a write command */
+ lan_rmw(ANA_VLANACCESS_VLAN_TBL_CMD_SET(VLANACCESS_CMD_WRITE),
+ ANA_VLANACCESS_VLAN_TBL_CMD,
+ lan966x, ANA_VLANACCESS);
+
+ return lan966x_vlan_wait_for_completion(lan966x);
+}
+
+void lan966x_vlan_init(struct lan966x *lan966x)
+{
+ u16 port, vid;
+
+ /* Clear VLAN table, by default all ports are members of all VLANS */
+ lan_rmw(ANA_VLANACCESS_VLAN_TBL_CMD_SET(VLANACCESS_CMD_INIT),
+ ANA_VLANACCESS_VLAN_TBL_CMD,
+ lan966x, ANA_VLANACCESS);
+ lan966x_vlan_wait_for_completion(lan966x);
+
+ for (vid = 1; vid < VLAN_N_VID; vid++) {
+ lan966x->vlan_mask[vid] = 0;
+ lan966x_vlan_set_mask(lan966x, vid);
+ }
+
+ /* Set all the ports + cpu to be part of HOST_PVID and UNAWARE_PVID */
+ lan966x->vlan_mask[HOST_PVID] =
+ GENMASK(lan966x->num_phys_ports - 1, 0) | BIT(CPU_PORT);
+ lan966x_vlan_set_mask(lan966x, HOST_PVID);
+
+ lan966x->vlan_mask[UNAWARE_PVID] =
+ GENMASK(lan966x->num_phys_ports - 1, 0) | BIT(CPU_PORT);
+ lan966x_vlan_set_mask(lan966x, UNAWARE_PVID);
+
+ /* Configure the CPU port to be vlan aware */
+ lan_wr(ANA_VLAN_CFG_VLAN_VID_SET(0) |
+ ANA_VLAN_CFG_VLAN_AWARE_ENA_SET(1) |
+ ANA_VLAN_CFG_VLAN_POP_CNT_SET(1),
+ lan966x, ANA_VLAN_CFG(CPU_PORT));
+
+ /* Set vlan ingress filter mask to all ports */
+ lan_wr(GENMASK(lan966x->num_phys_ports, 0),
+ lan966x, ANA_VLANMASK);
+
+ for (port = 0; port < lan966x->num_phys_ports; port++) {
+ lan_wr(0, lan966x, REW_PORT_VLAN_CFG(port));
+ lan_wr(0, lan966x, REW_TAG_CFG(port));
+ }
+}
+
+static int lan966x_vlan_port_add_vlan_mask(struct lan966x_port *port, u16 vid)
+{
+ struct lan966x *lan966x = port->lan966x;
+ u8 p = port->chip_port;
+
+ lan966x->vlan_mask[vid] |= BIT(p);
+ return lan966x_vlan_set_mask(lan966x, vid);
+}
+
+static int lan966x_vlan_port_del_vlan_mask(struct lan966x_port *port, u16 vid)
+{
+ struct lan966x *lan966x = port->lan966x;
+ u8 p = port->chip_port;
+
+ lan966x->vlan_mask[vid] &= ~BIT(p);
+ return lan966x_vlan_set_mask(lan966x, vid);
+}
+
+bool lan966x_vlan_port_member_vlan_mask(struct lan966x_port *port, u16 vid)
+{
+ struct lan966x *lan966x = port->lan966x;
+ u8 p = port->chip_port;
+
+ return lan966x->vlan_mask[vid] & BIT(p);
+}
+
+bool lan966x_vlan_port_any_vlan_mask(struct lan966x *lan966x, u16 vid)
+{
+ return !!(lan966x->vlan_mask[vid] & ~BIT(CPU_PORT));
+}
+
+static int lan966x_vlan_cpu_add_vlan_mask(struct lan966x *lan966x, u16 vid)
+{
+ lan966x->vlan_mask[vid] |= BIT(CPU_PORT);
+ return lan966x_vlan_set_mask(lan966x, vid);
+}
+
+static int lan966x_vlan_cpu_del_vlan_mask(struct lan966x *lan966x, u16 vid)
+{
+ lan966x->vlan_mask[vid] &= ~BIT(CPU_PORT);
+ return lan966x_vlan_set_mask(lan966x, vid);
+}
+
+bool lan966x_vlan_cpu_member_vlan_mask(struct lan966x *lan966x, u16 vid)
+{
+ return lan966x->vlan_mask[vid] & BIT(CPU_PORT);
+}
+
+static void lan966x_vlan_cpu_add_cpu_vlan_mask(struct lan966x *lan966x, u16 vid)
+{
+ set_bit(vid, lan966x->cpu_vlan_mask);
+}
+
+static void lan966x_vlan_cpu_del_cpu_vlan_mask(struct lan966x *lan966x, u16 vid)
+{
+ clear_bit(vid, lan966x->cpu_vlan_mask);
+}
+
+static bool lan966x_vlan_cpu_member_cpu_vlan_mask(struct lan966x *lan966x, u16 vid)
+{
+ return test_bit(vid, lan966x->cpu_vlan_mask);
+}
+
+u16 lan966x_vlan_port_get_pvid(struct lan966x_port *port)
+{
+ return port->vlan_aware ? port->pvid : UNAWARE_PVID;
+}
+
+int lan966x_vlan_port_set_vid(struct lan966x_port *port, u16 vid,
+ bool pvid, bool untagged)
+{
+ struct lan966x *lan966x = port->lan966x;
+
+ /* Egress vlan classification */
+ if (untagged && port->vid != vid) {
+ if (port->vid) {
+ dev_err(lan966x->dev,
+ "Port already has a native VLAN: %d\n",
+ port->vid);
+ return -EBUSY;
+ }
+ port->vid = vid;
+ }
+
+ /* Default ingress vlan classification */
+ if (pvid)
+ port->pvid = vid;
+
+ return 0;
+}
+
+static int lan966x_vlan_port_remove_vid(struct lan966x_port *port, u16 vid)
+{
+ if (port->pvid == vid)
+ port->pvid = 0;
+
+ if (port->vid == vid)
+ port->vid = 0;
+
+ return 0;
+}
+
+void lan966x_vlan_port_set_vlan_aware(struct lan966x_port *port,
+ bool vlan_aware)
+{
+ port->vlan_aware = vlan_aware;
+}
+
+void lan966x_vlan_cpu_set_vlan_aware(struct lan966x_port *port)
+{
+ struct lan966x *lan966x = port->lan966x;
+
+ if (!port->vlan_aware) {
+ /* In case of vlan unaware, all the ports will be set in
+ * UNAWARE_PVID and have their PVID set to this PVID
+ * The CPU doesn't need to be added because it is always part of
+ * that vlan, it is required just to add entries in the MAC
+ * table for the front port and the CPU
+ */
+ lan966x_mac_cpu_learn(lan966x, port->dev->dev_addr, UNAWARE_PVID);
+
+ lan966x_vlan_port_add_vlan_mask(port, UNAWARE_PVID);
+ lan966x_vlan_port_apply(port);
+ } else {
+ /* In case of vlan aware, just clear what happened when changed
+ * to vlan unaware
+ */
+ lan966x_mac_cpu_forget(lan966x, port->dev->dev_addr, UNAWARE_PVID);
+
+ lan966x_vlan_port_del_vlan_mask(port, UNAWARE_PVID);
+ lan966x_vlan_port_apply(port);
+ }
+}
+
+void lan966x_vlan_port_apply(struct lan966x_port *port)
+{
+ struct lan966x *lan966x = port->lan966x;
+ u16 pvid;
+ u32 val;
+
+ pvid = lan966x_vlan_port_get_pvid(port);
+
+ /* Ingress clasification (ANA_PORT_VLAN_CFG) */
+ /* Default vlan to casify for untagged frames (may be zero) */
+ val = ANA_VLAN_CFG_VLAN_VID_SET(pvid);
+ if (port->vlan_aware)
+ val |= ANA_VLAN_CFG_VLAN_AWARE_ENA_SET(1) |
+ ANA_VLAN_CFG_VLAN_POP_CNT_SET(1);
+
+ lan_rmw(val,
+ ANA_VLAN_CFG_VLAN_VID | ANA_VLAN_CFG_VLAN_AWARE_ENA |
+ ANA_VLAN_CFG_VLAN_POP_CNT,
+ lan966x, ANA_VLAN_CFG(port->chip_port));
+
+ /* Drop frames with multicast source address */
+ val = ANA_DROP_CFG_DROP_MC_SMAC_ENA_SET(1);
+ if (port->vlan_aware && !pvid)
+ /* If port is vlan-aware and tagged, drop untagged and priority
+ * tagged frames.
+ */
+ val |= ANA_DROP_CFG_DROP_UNTAGGED_ENA_SET(1) |
+ ANA_DROP_CFG_DROP_PRIO_S_TAGGED_ENA_SET(1) |
+ ANA_DROP_CFG_DROP_PRIO_C_TAGGED_ENA_SET(1);
+
+ lan_wr(val, lan966x, ANA_DROP_CFG(port->chip_port));
+
+ /* Egress configuration (REW_TAG_CFG): VLAN tag type to 8021Q */
+ val = REW_TAG_CFG_TAG_TPID_CFG_SET(0);
+ if (port->vlan_aware) {
+ if (port->vid)
+ /* Tag all frames except when VID == DEFAULT_VLAN */
+ val |= REW_TAG_CFG_TAG_CFG_SET(1);
+ else
+ val |= REW_TAG_CFG_TAG_CFG_SET(3);
+ }
+
+ /* Update only some bits in the register */
+ lan_rmw(val,
+ REW_TAG_CFG_TAG_TPID_CFG | REW_TAG_CFG_TAG_CFG,
+ lan966x, REW_TAG_CFG(port->chip_port));
+
+ /* Set default VLAN and tag type to 8021Q */
+ lan_rmw(REW_PORT_VLAN_CFG_PORT_TPID_SET(ETH_P_8021Q) |
+ REW_PORT_VLAN_CFG_PORT_VID_SET(port->vid),
+ REW_PORT_VLAN_CFG_PORT_TPID |
+ REW_PORT_VLAN_CFG_PORT_VID,
+ lan966x, REW_PORT_VLAN_CFG(port->chip_port));
+}
+
+int lan966x_vlan_port_add_vlan(struct lan966x_port *port,
+ u16 vid,
+ bool pvid,
+ bool untagged)
+{
+ struct lan966x *lan966x = port->lan966x;
+
+ /* If the CPU(br) is already part of the vlan then add the MAC
+ * address of the device in MAC table to copy the frames to the
+ * CPU(br). If the CPU(br) is not part of the vlan then it would
+ * just drop the frames.
+ */
+ if (lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x, vid)) {
+ lan966x_mac_cpu_learn(lan966x, port->dev->dev_addr, vid);
+ lan966x_vlan_cpu_add_vlan_mask(lan966x, vid);
+ }
+
+ lan966x_vlan_port_set_vid(port, vid, pvid, untagged);
+ lan966x_vlan_port_add_vlan_mask(port, vid);
+ lan966x_vlan_port_apply(port);
+
+ return 0;
+}
+
+int lan966x_vlan_port_del_vlan(struct lan966x_port *port,
+ u16 vid)
+{
+ struct lan966x *lan966x = port->lan966x;
+
+ /* In case the CPU(br) is part of the vlan then remove the MAC entry
+ * because frame doesn't need to reach to CPU
+ */
+ if (lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x, vid))
+ lan966x_mac_cpu_forget(lan966x, port->dev->dev_addr, vid);
+
+ lan966x_vlan_port_remove_vid(port, vid);
+ lan966x_vlan_port_del_vlan_mask(port, vid);
+ lan966x_vlan_port_apply(port);
+
+ /* In case there are no other ports in vlan then remove the CPU from
+ * that vlan but still keep it in the mask because it may be needed
+ * again then another port gets added in tha vlan
+ */
+ if (!lan966x_vlan_port_any_vlan_mask(lan966x, vid))
+ lan966x_vlan_cpu_del_vlan_mask(lan966x, vid);
+
+ return 0;
+}
+
+int lan966x_vlan_cpu_add_vlan(struct lan966x *lan966x,
+ struct net_device *dev,
+ u16 vid)
+{
+ int p;
+
+ /* Iterate over the ports and see which ones are part of the
+ * vlan and for those ports add entry in the MAC table to
+ * copy the frames to the CPU
+ */
+ for (p = 0; p < lan966x->num_phys_ports; p++) {
+ struct lan966x_port *port = lan966x->ports[p];
+
+ if (!port ||
+ !lan966x_vlan_port_member_vlan_mask(port, vid))
+ continue;
+
+ lan966x_mac_cpu_learn(lan966x, port->dev->dev_addr, vid);
+ }
+
+ /* Add an entry in the MAC table for the CPU */
+ if (lan966x_vlan_port_any_vlan_mask(lan966x, vid))
+ lan966x_mac_cpu_learn(lan966x, dev->dev_addr, vid);
+
+ /* Add the CPU part of the vlan only if there is another port in that
+ * vlan otherwise all the broadcast frames in that vlan will go to CPU
+ * even if none of the ports are in the vlan and then the CPU will just
+ * need to discard these frames. It is required to store this
+ * information so when a front port is added then it would add also the
+ * CPU port.
+ */
+ if (lan966x_vlan_port_any_vlan_mask(lan966x, vid))
+ lan966x_vlan_cpu_add_vlan_mask(lan966x, vid);
+
+ lan966x_vlan_cpu_add_cpu_vlan_mask(lan966x, vid);
+
+ return 0;
+}
+
+int lan966x_vlan_cpu_del_vlan(struct lan966x *lan966x,
+ struct net_device *dev,
+ u16 vid)
+{
+ int p;
+
+ /* Iterate over the ports and see which ones are part of the
+ * vlan and for those ports remove entry in the MAC table to
+ * copy the frames to the CPU
+ */
+ for (p = 0; p < lan966x->num_phys_ports; p++) {
+ struct lan966x_port *port = lan966x->ports[p];
+
+ if (!port ||
+ !lan966x_vlan_port_member_vlan_mask(port, vid))
+ continue;
+
+ lan966x_mac_cpu_forget(lan966x, port->dev->dev_addr, vid);
+ }
+
+ /* Remove an entry in the MAC table for the CPU */
+ lan966x_mac_cpu_forget(lan966x, dev->dev_addr, vid);
+
+ /* Remove the CPU part of the vlan */
+ lan966x_vlan_cpu_del_cpu_vlan_mask(lan966x, vid);
+ lan966x_vlan_cpu_del_vlan_mask(lan966x, vid);
+
+ return 0;
+}
+
+int lan966x_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
+{
+ struct lan966x_port *port = netdev_priv(dev);
+
+ lan966x_vlan_port_set_vid(port, vid, false, false);
+ lan966x_vlan_port_add_vlan_mask(port, vid);
+ lan966x_vlan_port_apply(port);
+
+ return 0;
+}
+
+int lan966x_vlan_rx_kill_vid(struct net_device *dev, __be16 proto,
+ u16 vid)
+{
+ struct lan966x_port *port = netdev_priv(dev);
+
+ lan966x_vlan_port_remove_vid(port, vid);
+ lan966x_vlan_port_del_vlan_mask(port, vid);
+ lan966x_vlan_port_apply(port);
+
+ return 0;
+}
+
+void lan966x_vlan_mode(struct lan966x_port *port,
+ netdev_features_t features)
+{
+ struct lan966x *lan966x = port->lan966x;
+ u32 val;
+
+ /* Filtering */
+ val = lan_rd(lan966x, ANA_VLANMASK);
+ if (features & NETIF_F_HW_VLAN_CTAG_FILTER)
+ val |= BIT(port->chip_port);
+ else
+ val &= ~BIT(port->chip_port);
+ lan_wr(val, lan966x, ANA_VLANMASK);
+}
--
2.33.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox