diff options
Diffstat (limited to 'drivers')
210 files changed, 10219 insertions, 1975 deletions
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index d0a1ed216d15..368c6f5b327e 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -403,7 +403,6 @@ config TUN_VNET_CROSS_LE config VETH tristate "Virtual ethernet pair device" select PAGE_POOL - select PAGE_POOL_STATS help This device is a local ethernet tunnel. Devices are created in pairs. When one end receives the packet it appears on its pair and vice diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 3fed888629f7..fce81fafbedd 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1,8 +1,9 @@ +// SPDX-License-Identifier: GPL-1.0+ /* * originally based on the dummy device. * * Copyright 1999, Thomas Davis, tadavis@lbl.gov. - * Licensed under the GPL. Based on dummy.c, and eql.c devices. + * Based on dummy.c, and eql.c devices. * * bonding.c: an Ethernet Bonding driver * @@ -2871,6 +2872,8 @@ static bool bond_has_this_ip(struct bonding *bond, __be32 ip) return ret; } +#define BOND_VLAN_PROTO_NONE cpu_to_be16(0xffff) + static bool bond_handle_vlan(struct slave *slave, struct bond_vlan_tag *tags, struct sk_buff *skb) { @@ -2878,13 +2881,13 @@ static bool bond_handle_vlan(struct slave *slave, struct bond_vlan_tag *tags, struct net_device *slave_dev = slave->dev; struct bond_vlan_tag *outer_tag = tags; - if (!tags || tags->vlan_proto == VLAN_N_VID) + if (!tags || tags->vlan_proto == BOND_VLAN_PROTO_NONE) return true; tags++; /* Go through all the tags backwards and add them to the packet */ - while (tags->vlan_proto != VLAN_N_VID) { + while (tags->vlan_proto != BOND_VLAN_PROTO_NONE) { if (!tags->vlan_id) { tags++; continue; @@ -2960,7 +2963,7 @@ struct bond_vlan_tag *bond_verify_device_path(struct net_device *start_dev, tags = kcalloc(level + 1, sizeof(*tags), GFP_ATOMIC); if (!tags) return ERR_PTR(-ENOMEM); - tags[level].vlan_proto = VLAN_N_VID; + tags[level].vlan_proto = BOND_VLAN_PROTO_NONE; return tags; } diff --git a/drivers/net/bonding/bonding_priv.h b/drivers/net/bonding/bonding_priv.h index 48cdf3a49a7d..fef6288c6944 100644 --- a/drivers/net/bonding/bonding_priv.h +++ b/drivers/net/bonding/bonding_priv.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-1.0+ */ /* * Bond several ethernet interfaces into a Cisco, running 'Etherchannel'. * @@ -7,9 +8,6 @@ * BUT, I'm the one who modified it for ethernet, so: * (c) Copyright 1999, Thomas Davis, tadavis@lbl.gov * - * This software may be used and distributed according to the terms - * of the GNU Public License, incorporated herein by reference. - * */ #ifndef _BONDING_PRIV_H diff --git a/drivers/net/can/at91_can.c b/drivers/net/can/at91_can.c index 199cb200f2bd..4621266851ed 100644 --- a/drivers/net/can/at91_can.c +++ b/drivers/net/can/at91_can.c @@ -1346,7 +1346,7 @@ static int at91_can_probe(struct platform_device *pdev) return err; } -static int at91_can_remove(struct platform_device *pdev) +static void at91_can_remove(struct platform_device *pdev) { struct net_device *dev = platform_get_drvdata(pdev); struct at91_priv *priv = netdev_priv(dev); @@ -1362,8 +1362,6 @@ static int at91_can_remove(struct platform_device *pdev) clk_put(priv->clk); free_candev(dev); - - return 0; } static const struct platform_device_id at91_can_id_table[] = { @@ -1381,7 +1379,7 @@ MODULE_DEVICE_TABLE(platform, at91_can_id_table); static struct platform_driver at91_can_driver = { .probe = at91_can_probe, - .remove = at91_can_remove, + .remove_new = at91_can_remove, .driver = { .name = KBUILD_MODNAME, .of_match_table = of_match_ptr(at91_can_dt_ids), diff --git a/drivers/net/can/bxcan.c b/drivers/net/can/bxcan.c index 027a8a162fe4..39de7164bc4e 100644 --- a/drivers/net/can/bxcan.c +++ b/drivers/net/can/bxcan.c @@ -966,22 +966,16 @@ static int bxcan_probe(struct platform_device *pdev) } rx_irq = platform_get_irq_byname(pdev, "rx0"); - if (rx_irq < 0) { - dev_err(dev, "failed to get rx0 irq\n"); + if (rx_irq < 0) return rx_irq; - } tx_irq = platform_get_irq_byname(pdev, "tx"); - if (tx_irq < 0) { - dev_err(dev, "failed to get tx irq\n"); + if (tx_irq < 0) return tx_irq; - } sce_irq = platform_get_irq_byname(pdev, "sce"); - if (sce_irq < 0) { - dev_err(dev, "failed to get sce irq\n"); + if (sce_irq < 0) return sce_irq; - } ndev = alloc_candev(sizeof(struct bxcan_priv), BXCAN_TX_MB_NUM); if (!ndev) { @@ -1039,7 +1033,7 @@ out_free_candev: return err; } -static int bxcan_remove(struct platform_device *pdev) +static void bxcan_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); struct bxcan_priv *priv = netdev_priv(ndev); @@ -1048,7 +1042,6 @@ static int bxcan_remove(struct platform_device *pdev) clk_disable_unprepare(priv->clk); can_rx_offload_del(&priv->offload); free_candev(ndev); - return 0; } static int __maybe_unused bxcan_suspend(struct device *dev) @@ -1100,7 +1093,7 @@ static struct platform_driver bxcan_driver = { .of_match_table = bxcan_of_match, }, .probe = bxcan_probe, - .remove = bxcan_remove, + .remove_new = bxcan_remove, }; module_platform_driver(bxcan_driver); diff --git a/drivers/net/can/c_can/c_can_platform.c b/drivers/net/can/c_can/c_can_platform.c index 03ccb7cfacaf..925930b6c4ca 100644 --- a/drivers/net/can/c_can/c_can_platform.c +++ b/drivers/net/can/c_can/c_can_platform.c @@ -410,7 +410,7 @@ exit: return ret; } -static int c_can_plat_remove(struct platform_device *pdev) +static void c_can_plat_remove(struct platform_device *pdev) { struct net_device *dev = platform_get_drvdata(pdev); struct c_can_priv *priv = netdev_priv(dev); @@ -418,8 +418,6 @@ static int c_can_plat_remove(struct platform_device *pdev) unregister_c_can_dev(dev); pm_runtime_disable(priv->device); free_c_can_dev(dev); - - return 0; } #ifdef CONFIG_PM @@ -487,7 +485,7 @@ static struct platform_driver c_can_plat_driver = { .of_match_table = c_can_of_table, }, .probe = c_can_plat_probe, - .remove = c_can_plat_remove, + .remove_new = c_can_plat_remove, .suspend = c_can_suspend, .resume = c_can_resume, .id_table = c_can_id_table, diff --git a/drivers/net/can/cc770/cc770_isa.c b/drivers/net/can/cc770/cc770_isa.c index 8f6dccd5a587..22009440a983 100644 --- a/drivers/net/can/cc770/cc770_isa.c +++ b/drivers/net/can/cc770/cc770_isa.c @@ -285,7 +285,7 @@ exit: return err; } -static int cc770_isa_remove(struct platform_device *pdev) +static void cc770_isa_remove(struct platform_device *pdev) { struct net_device *dev = platform_get_drvdata(pdev); struct cc770_priv *priv = netdev_priv(dev); @@ -303,13 +303,11 @@ static int cc770_isa_remove(struct platform_device *pdev) release_region(port[idx], CC770_IOSIZE); } free_cc770dev(dev); - - return 0; } static struct platform_driver cc770_isa_driver = { .probe = cc770_isa_probe, - .remove = cc770_isa_remove, + .remove_new = cc770_isa_remove, .driver = { .name = KBUILD_MODNAME, }, diff --git a/drivers/net/can/cc770/cc770_platform.c b/drivers/net/can/cc770/cc770_platform.c index 8dcc32e4e30e..13bcfba05f18 100644 --- a/drivers/net/can/cc770/cc770_platform.c +++ b/drivers/net/can/cc770/cc770_platform.c @@ -230,7 +230,7 @@ exit_release_mem: return err; } -static int cc770_platform_remove(struct platform_device *pdev) +static void cc770_platform_remove(struct platform_device *pdev) { struct net_device *dev = platform_get_drvdata(pdev); struct cc770_priv *priv = netdev_priv(dev); @@ -242,8 +242,6 @@ static int cc770_platform_remove(struct platform_device *pdev) mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(mem->start, resource_size(mem)); - - return 0; } static const struct of_device_id cc770_platform_table[] = { @@ -259,7 +257,7 @@ static struct platform_driver cc770_platform_driver = { .of_match_table = cc770_platform_table, }, .probe = cc770_platform_probe, - .remove = cc770_platform_remove, + .remove_new = cc770_platform_remove, }; module_platform_driver(cc770_platform_driver); diff --git a/drivers/net/can/ctucanfd/ctucanfd_platform.c b/drivers/net/can/ctucanfd/ctucanfd_platform.c index a17561d97192..55bb10b157b4 100644 --- a/drivers/net/can/ctucanfd/ctucanfd_platform.c +++ b/drivers/net/can/ctucanfd/ctucanfd_platform.c @@ -86,7 +86,7 @@ err: * This function frees all the resources allocated to the device. * Return: 0 always */ -static int ctucan_platform_remove(struct platform_device *pdev) +static void ctucan_platform_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); struct ctucan_priv *priv = netdev_priv(ndev); @@ -97,8 +97,6 @@ static int ctucan_platform_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); netif_napi_del(&priv->napi); free_candev(ndev); - - return 0; } static SIMPLE_DEV_PM_OPS(ctucan_platform_pm_ops, ctucan_suspend, ctucan_resume); @@ -113,7 +111,7 @@ MODULE_DEVICE_TABLE(of, ctucan_of_match); static struct platform_driver ctucanfd_driver = { .probe = ctucan_platform_probe, - .remove = ctucan_platform_remove, + .remove_new = ctucan_platform_remove, .driver = { .name = DRV_NAME, .pm = &ctucan_platform_pm_ops, diff --git a/drivers/net/can/flexcan/flexcan-core.c b/drivers/net/can/flexcan/flexcan-core.c index 6d638c93977b..ff0fc18baf13 100644 --- a/drivers/net/can/flexcan/flexcan-core.c +++ b/drivers/net/can/flexcan/flexcan-core.c @@ -2218,7 +2218,7 @@ static int flexcan_probe(struct platform_device *pdev) return err; } -static int flexcan_remove(struct platform_device *pdev) +static void flexcan_remove(struct platform_device *pdev) { struct net_device *dev = platform_get_drvdata(pdev); @@ -2227,8 +2227,6 @@ static int flexcan_remove(struct platform_device *pdev) unregister_flexcandev(dev); pm_runtime_disable(&pdev->dev); free_candev(dev); - - return 0; } static int __maybe_unused flexcan_suspend(struct device *device) @@ -2379,7 +2377,7 @@ static struct platform_driver flexcan_driver = { .of_match_table = flexcan_of_match, }, .probe = flexcan_probe, - .remove = flexcan_remove, + .remove_new = flexcan_remove, .id_table = flexcan_id_table, }; diff --git a/drivers/net/can/grcan.c b/drivers/net/can/grcan.c index 4bedcc3eea0d..3174efdae271 100644 --- a/drivers/net/can/grcan.c +++ b/drivers/net/can/grcan.c @@ -1696,7 +1696,7 @@ exit_error: return err; } -static int grcan_remove(struct platform_device *ofdev) +static void grcan_remove(struct platform_device *ofdev) { struct net_device *dev = platform_get_drvdata(ofdev); struct grcan_priv *priv = netdev_priv(dev); @@ -1706,8 +1706,6 @@ static int grcan_remove(struct platform_device *ofdev) irq_dispose_mapping(dev->irq); netif_napi_del(&priv->napi); free_candev(dev); - - return 0; } static const struct of_device_id grcan_match[] = { @@ -1726,7 +1724,7 @@ static struct platform_driver grcan_driver = { .of_match_table = grcan_match, }, .probe = grcan_probe, - .remove = grcan_remove, + .remove_new = grcan_remove, }; module_platform_driver(grcan_driver); diff --git a/drivers/net/can/ifi_canfd/ifi_canfd.c b/drivers/net/can/ifi_canfd/ifi_canfd.c index 07eaf724a572..1d6642c94f2f 100644 --- a/drivers/net/can/ifi_canfd/ifi_canfd.c +++ b/drivers/net/can/ifi_canfd/ifi_canfd.c @@ -1013,15 +1013,13 @@ err_reg: return ret; } -static int ifi_canfd_plat_remove(struct platform_device *pdev) +static void ifi_canfd_plat_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); unregister_candev(ndev); platform_set_drvdata(pdev, NULL); free_candev(ndev); - - return 0; } static const struct of_device_id ifi_canfd_of_table[] = { @@ -1036,7 +1034,7 @@ static struct platform_driver ifi_canfd_plat_driver = { .of_match_table = ifi_canfd_of_table, }, .probe = ifi_canfd_plat_probe, - .remove = ifi_canfd_plat_remove, + .remove_new = ifi_canfd_plat_remove, }; module_platform_driver(ifi_canfd_plat_driver); diff --git a/drivers/net/can/janz-ican3.c b/drivers/net/can/janz-ican3.c index 0732a5092141..d048ea565b89 100644 --- a/drivers/net/can/janz-ican3.c +++ b/drivers/net/can/janz-ican3.c @@ -2023,7 +2023,7 @@ out_return: return ret; } -static int ican3_remove(struct platform_device *pdev) +static void ican3_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); struct ican3_dev *mod = netdev_priv(ndev); @@ -2042,8 +2042,6 @@ static int ican3_remove(struct platform_device *pdev) iounmap(mod->dpm); free_candev(ndev); - - return 0; } static struct platform_driver ican3_driver = { @@ -2051,7 +2049,7 @@ static struct platform_driver ican3_driver = { .name = DRV_NAME, }, .probe = ican3_probe, - .remove = ican3_remove, + .remove_new = ican3_remove, }; module_platform_driver(ican3_driver); diff --git a/drivers/net/can/m_can/m_can_platform.c b/drivers/net/can/m_can/m_can_platform.c index 9c1dcf838006..94dc82644113 100644 --- a/drivers/net/can/m_can/m_can_platform.c +++ b/drivers/net/can/m_can/m_can_platform.c @@ -164,7 +164,7 @@ static __maybe_unused int m_can_resume(struct device *dev) return m_can_class_resume(dev); } -static int m_can_plat_remove(struct platform_device *pdev) +static void m_can_plat_remove(struct platform_device *pdev) { struct m_can_plat_priv *priv = platform_get_drvdata(pdev); struct m_can_classdev *mcan_class = &priv->cdev; @@ -172,8 +172,6 @@ static int m_can_plat_remove(struct platform_device *pdev) m_can_class_unregister(mcan_class); m_can_class_free_dev(mcan_class->net); - - return 0; } static int __maybe_unused m_can_runtime_suspend(struct device *dev) @@ -223,7 +221,7 @@ static struct platform_driver m_can_plat_driver = { .pm = &m_can_pmops, }, .probe = m_can_plat_probe, - .remove = m_can_plat_remove, + .remove_new = m_can_plat_remove, }; module_platform_driver(m_can_plat_driver); diff --git a/drivers/net/can/mscan/mpc5xxx_can.c b/drivers/net/can/mscan/mpc5xxx_can.c index b0ed798ae70f..4837df6efa92 100644 --- a/drivers/net/can/mscan/mpc5xxx_can.c +++ b/drivers/net/can/mscan/mpc5xxx_can.c @@ -349,7 +349,7 @@ exit_unmap_mem: return err; } -static int mpc5xxx_can_remove(struct platform_device *ofdev) +static void mpc5xxx_can_remove(struct platform_device *ofdev) { const struct of_device_id *match; const struct mpc5xxx_can_data *data; @@ -365,8 +365,6 @@ static int mpc5xxx_can_remove(struct platform_device *ofdev) iounmap(priv->reg_base); irq_dispose_mapping(dev->irq); free_candev(dev); - - return 0; } #ifdef CONFIG_PM @@ -437,7 +435,7 @@ static struct platform_driver mpc5xxx_can_driver = { .of_match_table = mpc5xxx_can_table, }, .probe = mpc5xxx_can_probe, - .remove = mpc5xxx_can_remove, + .remove_new = mpc5xxx_can_remove, #ifdef CONFIG_PM .suspend = mpc5xxx_can_suspend, .resume = mpc5xxx_can_resume, diff --git a/drivers/net/can/rcar/rcar_can.c b/drivers/net/can/rcar/rcar_can.c index cc43c9c5e38c..f5aa5dbacaf2 100644 --- a/drivers/net/can/rcar/rcar_can.c +++ b/drivers/net/can/rcar/rcar_can.c @@ -824,7 +824,7 @@ fail: return err; } -static int rcar_can_remove(struct platform_device *pdev) +static void rcar_can_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); struct rcar_can_priv *priv = netdev_priv(ndev); @@ -832,7 +832,6 @@ static int rcar_can_remove(struct platform_device *pdev) unregister_candev(ndev); netif_napi_del(&priv->napi); free_candev(ndev); - return 0; } static int __maybe_unused rcar_can_suspend(struct device *dev) @@ -908,7 +907,7 @@ static struct platform_driver rcar_can_driver = { .pm = &rcar_can_pm_ops, }, .probe = rcar_can_probe, - .remove = rcar_can_remove, + .remove_new = rcar_can_remove, }; module_platform_driver(rcar_can_driver); diff --git a/drivers/net/can/rcar/rcar_canfd.c b/drivers/net/can/rcar/rcar_canfd.c index 963c42f43755..e4d748913439 100644 --- a/drivers/net/can/rcar/rcar_canfd.c +++ b/drivers/net/can/rcar/rcar_canfd.c @@ -2078,7 +2078,7 @@ fail_dev: return err; } -static int rcar_canfd_remove(struct platform_device *pdev) +static void rcar_canfd_remove(struct platform_device *pdev) { struct rcar_canfd_global *gpriv = platform_get_drvdata(pdev); u32 ch; @@ -2096,8 +2096,6 @@ static int rcar_canfd_remove(struct platform_device *pdev) clk_disable_unprepare(gpriv->clkp); reset_control_assert(gpriv->rstc1); reset_control_assert(gpriv->rstc2); - - return 0; } static int __maybe_unused rcar_canfd_suspend(struct device *dev) @@ -2130,7 +2128,7 @@ static struct platform_driver rcar_canfd_driver = { .pm = &rcar_canfd_pm_ops, }, .probe = rcar_canfd_probe, - .remove = rcar_canfd_remove, + .remove_new = rcar_canfd_remove, }; module_platform_driver(rcar_canfd_driver); diff --git a/drivers/net/can/sja1000/sja1000_isa.c b/drivers/net/can/sja1000/sja1000_isa.c index db3e767d5320..fca5a9a1d857 100644 --- a/drivers/net/can/sja1000/sja1000_isa.c +++ b/drivers/net/can/sja1000/sja1000_isa.c @@ -223,7 +223,7 @@ exit: return err; } -static int sja1000_isa_remove(struct platform_device *pdev) +static void sja1000_isa_remove(struct platform_device *pdev) { struct net_device *dev = platform_get_drvdata(pdev); struct sja1000_priv *priv = netdev_priv(dev); @@ -241,13 +241,11 @@ static int sja1000_isa_remove(struct platform_device *pdev) release_region(port[idx], SJA1000_IOSIZE); } free_sja1000dev(dev); - - return 0; } static struct platform_driver sja1000_isa_driver = { .probe = sja1000_isa_probe, - .remove = sja1000_isa_remove, + .remove_new = sja1000_isa_remove, .driver = { .name = DRV_NAME, }, diff --git a/drivers/net/can/sja1000/sja1000_platform.c b/drivers/net/can/sja1000/sja1000_platform.c index 6779d5357069..b4889b5746e5 100644 --- a/drivers/net/can/sja1000/sja1000_platform.c +++ b/drivers/net/can/sja1000/sja1000_platform.c @@ -317,19 +317,17 @@ static int sp_probe(struct platform_device *pdev) return err; } -static int sp_remove(struct platform_device *pdev) +static void sp_remove(struct platform_device *pdev) { struct net_device *dev = platform_get_drvdata(pdev); unregister_sja1000dev(dev); free_sja1000dev(dev); - - return 0; } static struct platform_driver sp_driver = { .probe = sp_probe, - .remove = sp_remove, + .remove_new = sp_remove, .driver = { .name = DRV_NAME, .of_match_table = sp_of_table, diff --git a/drivers/net/can/softing/softing_main.c b/drivers/net/can/softing/softing_main.c index c72f505d29fe..bd25137062c5 100644 --- a/drivers/net/can/softing/softing_main.c +++ b/drivers/net/can/softing/softing_main.c @@ -729,7 +729,7 @@ static const struct attribute_group softing_pdev_group = { /* * platform driver */ -static int softing_pdev_remove(struct platform_device *pdev) +static void softing_pdev_remove(struct platform_device *pdev) { struct softing *card = platform_get_drvdata(pdev); int j; @@ -747,7 +747,6 @@ static int softing_pdev_remove(struct platform_device *pdev) iounmap(card->dpram); kfree(card); - return 0; } static int softing_pdev_probe(struct platform_device *pdev) @@ -855,7 +854,7 @@ static struct platform_driver softing_driver = { .name = KBUILD_MODNAME, }, .probe = softing_pdev_probe, - .remove = softing_pdev_remove, + .remove_new = softing_pdev_remove, }; module_platform_driver(softing_driver); diff --git a/drivers/net/can/sun4i_can.c b/drivers/net/can/sun4i_can.c index 2b78f9197681..0827830bbf28 100644 --- a/drivers/net/can/sun4i_can.c +++ b/drivers/net/can/sun4i_can.c @@ -791,14 +791,12 @@ static const struct of_device_id sun4ican_of_match[] = { MODULE_DEVICE_TABLE(of, sun4ican_of_match); -static int sun4ican_remove(struct platform_device *pdev) +static void sun4ican_remove(struct platform_device *pdev) { struct net_device *dev = platform_get_drvdata(pdev); unregister_netdev(dev); free_candev(dev); - - return 0; } static int sun4ican_probe(struct platform_device *pdev) @@ -901,7 +899,7 @@ static struct platform_driver sun4i_can_driver = { .of_match_table = sun4ican_of_match, }, .probe = sun4ican_probe, - .remove = sun4ican_remove, + .remove_new = sun4ican_remove, }; module_platform_driver(sun4i_can_driver); diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c index 27700f72eac2..9bab0b4cc449 100644 --- a/drivers/net/can/ti_hecc.c +++ b/drivers/net/can/ti_hecc.c @@ -963,7 +963,7 @@ probe_exit_candev: return err; } -static int ti_hecc_remove(struct platform_device *pdev) +static void ti_hecc_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); struct ti_hecc_priv *priv = netdev_priv(ndev); @@ -973,8 +973,6 @@ static int ti_hecc_remove(struct platform_device *pdev) clk_put(priv->clk); can_rx_offload_del(&priv->offload); free_candev(ndev); - - return 0; } #ifdef CONFIG_PM @@ -1028,7 +1026,7 @@ static struct platform_driver ti_hecc_driver = { .of_match_table = ti_hecc_dt_ids, }, .probe = ti_hecc_probe, - .remove = ti_hecc_remove, + .remove_new = ti_hecc_remove, .suspend = ti_hecc_suspend, .resume = ti_hecc_resume, }; diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig index 445504ababce..58fcd2b34820 100644 --- a/drivers/net/can/usb/Kconfig +++ b/drivers/net/can/usb/Kconfig @@ -38,6 +38,18 @@ config CAN_ETAS_ES58X To compile this driver as a module, choose M here: the module will be called etas_es58x. +config CAN_F81604 + tristate "Fintek F81604 USB to 2CAN interface" + help + This driver supports the Fintek F81604 USB to 2CAN interface. + The device can support CAN2.0A/B protocol and also support + 2 output pins to control external terminator (optional). + + To compile this driver as a module, choose M here: the module will + be called f81604. + + (see also https://www.fintek.com.tw). + config CAN_GS_USB tristate "Geschwister Schneider UG and candleLight compatible interfaces" help diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile index 1ea16be5743b..8b11088e9a59 100644 --- a/drivers/net/can/usb/Makefile +++ b/drivers/net/can/usb/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_CAN_8DEV_USB) += usb_8dev.o obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o obj-$(CONFIG_CAN_ESD_USB) += esd_usb.o obj-$(CONFIG_CAN_ETAS_ES58X) += etas_es58x/ +obj-$(CONFIG_CAN_F81604) += f81604.o obj-$(CONFIG_CAN_GS_USB) += gs_usb.o obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb/ obj-$(CONFIG_CAN_MCBA_USB) += mcba_usb.o diff --git a/drivers/net/can/usb/f81604.c b/drivers/net/can/usb/f81604.c new file mode 100644 index 000000000000..ec8cef7fd2d5 --- /dev/null +++ b/drivers/net/can/usb/f81604.c @@ -0,0 +1,1201 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Fintek F81604 USB-to-2CAN controller driver. + * + * Copyright (C) 2023 Ji-Ze Hong (Peter Hong) <peter_hong@fintek.com.tw> + */ +#include <linux/bitfield.h> +#include <linux/netdevice.h> +#include <linux/units.h> +#include <linux/usb.h> + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> +#include <linux/can/platform/sja1000.h> + +#include <asm-generic/unaligned.h> + +/* vendor and product id */ +#define F81604_VENDOR_ID 0x2c42 +#define F81604_PRODUCT_ID 0x1709 +#define F81604_CAN_CLOCK (12 * MEGA) +#define F81604_MAX_DEV 2 +#define F81604_SET_DEVICE_RETRY 10 + +#define F81604_USB_TIMEOUT 2000 +#define F81604_SET_GET_REGISTER 0xA0 +#define F81604_PORT_OFFSET 0x1000 +#define F81604_MAX_RX_URBS 4 + +#define F81604_CMD_DATA 0x00 + +#define F81604_DLC_LEN_MASK GENMASK(3, 0) +#define F81604_DLC_EFF_BIT BIT(7) +#define F81604_DLC_RTR_BIT BIT(6) + +#define F81604_SFF_SHIFT 5 +#define F81604_EFF_SHIFT 3 + +#define F81604_BRP_MASK GENMASK(5, 0) +#define F81604_SJW_MASK GENMASK(7, 6) + +#define F81604_SEG1_MASK GENMASK(3, 0) +#define F81604_SEG2_MASK GENMASK(6, 4) + +#define F81604_CLEAR_ALC 0 +#define F81604_CLEAR_ECC 1 +#define F81604_CLEAR_OVERRUN 2 + +/* device setting */ +#define F81604_CTRL_MODE_REG 0x80 +#define F81604_TX_ONESHOT (0x03 << 3) +#define F81604_TX_NORMAL (0x01 << 3) +#define F81604_RX_AUTO_RELEASE_BUF BIT(1) +#define F81604_INT_WHEN_CHANGE BIT(0) + +#define F81604_TERMINATOR_REG 0x105 +#define F81604_CAN0_TERM BIT(2) +#define F81604_CAN1_TERM BIT(3) + +#define F81604_TERMINATION_DISABLED CAN_TERMINATION_DISABLED +#define F81604_TERMINATION_ENABLED 120 + +/* SJA1000 registers - manual section 6.4 (Pelican Mode) */ +#define F81604_SJA1000_MOD 0x00 +#define F81604_SJA1000_CMR 0x01 +#define F81604_SJA1000_IR 0x03 +#define F81604_SJA1000_IER 0x04 +#define F81604_SJA1000_ALC 0x0B +#define F81604_SJA1000_ECC 0x0C +#define F81604_SJA1000_RXERR 0x0E +#define F81604_SJA1000_TXERR 0x0F +#define F81604_SJA1000_ACCC0 0x10 +#define F81604_SJA1000_ACCM0 0x14 +#define F81604_MAX_FILTER_CNT 4 + +/* Common registers - manual section 6.5 */ +#define F81604_SJA1000_BTR0 0x06 +#define F81604_SJA1000_BTR1 0x07 +#define F81604_SJA1000_BTR1_SAMPLE_TRIPLE BIT(7) +#define F81604_SJA1000_OCR 0x08 +#define F81604_SJA1000_CDR 0x1F + +/* mode register */ +#define F81604_SJA1000_MOD_RM 0x01 +#define F81604_SJA1000_MOD_LOM 0x02 +#define F81604_SJA1000_MOD_STM 0x04 + +/* commands */ +#define F81604_SJA1000_CMD_CDO 0x08 + +/* interrupt sources */ +#define F81604_SJA1000_IRQ_BEI 0x80 +#define F81604_SJA1000_IRQ_ALI 0x40 +#define F81604_SJA1000_IRQ_EPI 0x20 +#define F81604_SJA1000_IRQ_DOI 0x08 +#define F81604_SJA1000_IRQ_EI 0x04 +#define F81604_SJA1000_IRQ_TI 0x02 +#define F81604_SJA1000_IRQ_RI 0x01 +#define F81604_SJA1000_IRQ_ALL 0xFF +#define F81604_SJA1000_IRQ_OFF 0x00 + +/* status register content */ +#define F81604_SJA1000_SR_BS 0x80 +#define F81604_SJA1000_SR_ES 0x40 +#define F81604_SJA1000_SR_TCS 0x08 + +/* ECC register */ +#define F81604_SJA1000_ECC_SEG 0x1F +#define F81604_SJA1000_ECC_DIR 0x20 +#define F81604_SJA1000_ECC_BIT 0x00 +#define F81604_SJA1000_ECC_FORM 0x40 +#define F81604_SJA1000_ECC_STUFF 0x80 +#define F81604_SJA1000_ECC_MASK 0xc0 + +/* ALC register */ +#define F81604_SJA1000_ALC_MASK 0x1f + +/* table of devices that work with this driver */ +static const struct usb_device_id f81604_table[] = { + { USB_DEVICE(F81604_VENDOR_ID, F81604_PRODUCT_ID) }, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, f81604_table); + +static const struct ethtool_ops f81604_ethtool_ops = { + .get_ts_info = ethtool_op_get_ts_info, +}; + +static const u16 f81604_termination[] = { F81604_TERMINATION_DISABLED, + F81604_TERMINATION_ENABLED }; + +struct f81604_priv { + struct net_device *netdev[F81604_MAX_DEV]; +}; + +struct f81604_port_priv { + struct can_priv can; + struct net_device *netdev; + struct sk_buff *echo_skb; + + unsigned long clear_flags; + struct work_struct clear_reg_work; + + struct usb_device *dev; + struct usb_interface *intf; + + struct usb_anchor urbs_anchor; +}; + +/* Interrupt endpoint data format: + * Byte 0: Status register. + * Byte 1: Interrupt register. + * Byte 2: Interrupt enable register. + * Byte 3: Arbitration lost capture(ALC) register. + * Byte 4: Error code capture(ECC) register. + * Byte 5: Error warning limit register. + * Byte 6: RX error counter register. + * Byte 7: TX error counter register. + * Byte 8: Reserved. + */ +struct f81604_int_data { + u8 sr; + u8 isrc; + u8 ier; + u8 alc; + u8 ecc; + u8 ewlr; + u8 rxerr; + u8 txerr; + u8 val; +} __packed __aligned(4); + +struct f81604_sff { + __be16 id; + u8 data[CAN_MAX_DLEN]; +} __packed __aligned(2); + +struct f81604_eff { + __be32 id; + u8 data[CAN_MAX_DLEN]; +} __packed __aligned(2); + +struct f81604_can_frame { + u8 cmd; + + /* According for F81604 DLC define: + * bit 3~0: data length (0~8) + * bit6: is RTR flag. + * bit7: is EFF frame. + */ + u8 dlc; + + union { + struct f81604_sff sff; + struct f81604_eff eff; + }; +} __packed __aligned(2); + +static const u8 bulk_in_addr[F81604_MAX_DEV] = { 2, 4 }; +static const u8 bulk_out_addr[F81604_MAX_DEV] = { 1, 3 }; +static const u8 int_in_addr[F81604_MAX_DEV] = { 1, 3 }; + +static int f81604_write(struct usb_device *dev, u16 reg, u8 data) +{ + int ret; + + ret = usb_control_msg_send(dev, 0, F81604_SET_GET_REGISTER, + USB_TYPE_VENDOR | USB_DIR_OUT, 0, reg, + &data, sizeof(data), F81604_USB_TIMEOUT, + GFP_KERNEL); + if (ret) + dev_err(&dev->dev, "%s: reg: %x data: %x failed: %pe\n", + __func__, reg, data, ERR_PTR(ret)); + + return ret; +} + +static int f81604_read(struct usb_device *dev, u16 reg, u8 *data) +{ + int ret; + + ret = usb_control_msg_recv(dev, 0, F81604_SET_GET_REGISTER, + USB_TYPE_VENDOR | USB_DIR_IN, 0, reg, data, + sizeof(*data), F81604_USB_TIMEOUT, + GFP_KERNEL); + + if (ret < 0) + dev_err(&dev->dev, "%s: reg: %x failed: %pe\n", __func__, reg, + ERR_PTR(ret)); + + return ret; +} + +static int f81604_update_bits(struct usb_device *dev, u16 reg, u8 mask, + u8 data) +{ + int ret; + u8 tmp; + + ret = f81604_read(dev, reg, &tmp); + if (ret) + return ret; + + tmp &= ~mask; + tmp |= (mask & data); + + return f81604_write(dev, reg, tmp); +} + +static int f81604_sja1000_write(struct f81604_port_priv *priv, u16 reg, + u8 data) +{ + int port = priv->netdev->dev_port; + int real_reg; + + real_reg = reg + F81604_PORT_OFFSET * port + F81604_PORT_OFFSET; + return f81604_write(priv->dev, real_reg, data); +} + +static int f81604_sja1000_read(struct f81604_port_priv *priv, u16 reg, + u8 *data) +{ + int port = priv->netdev->dev_port; + int real_reg; + + real_reg = reg + F81604_PORT_OFFSET * port + F81604_PORT_OFFSET; + return f81604_read(priv->dev, real_reg, data); +} + +static int f81604_set_reset_mode(struct f81604_port_priv *priv) +{ + int ret, i; + u8 tmp; + + /* disable interrupts */ + ret = f81604_sja1000_write(priv, F81604_SJA1000_IER, + F81604_SJA1000_IRQ_OFF); + if (ret) + return ret; + + for (i = 0; i < F81604_SET_DEVICE_RETRY; i++) { + ret = f81604_sja1000_read(priv, F81604_SJA1000_MOD, &tmp); + if (ret) + return ret; + + /* check reset bit */ + if (tmp & F81604_SJA1000_MOD_RM) { + priv->can.state = CAN_STATE_STOPPED; + return 0; + } + + /* reset chip */ + ret = f81604_sja1000_write(priv, F81604_SJA1000_MOD, + F81604_SJA1000_MOD_RM); + if (ret) + return ret; + } + + return -EPERM; +} + +static int f81604_set_normal_mode(struct f81604_port_priv *priv) +{ + u8 tmp, ier = 0; + u8 mod_reg = 0; + int ret, i; + + for (i = 0; i < F81604_SET_DEVICE_RETRY; i++) { + ret = f81604_sja1000_read(priv, F81604_SJA1000_MOD, &tmp); + if (ret) + return ret; + + /* check reset bit */ + if ((tmp & F81604_SJA1000_MOD_RM) == 0) { + priv->can.state = CAN_STATE_ERROR_ACTIVE; + /* enable interrupts, RI handled by bulk-in */ + ier = F81604_SJA1000_IRQ_ALL & ~F81604_SJA1000_IRQ_RI; + if (!(priv->can.ctrlmode & + CAN_CTRLMODE_BERR_REPORTING)) + ier &= ~F81604_SJA1000_IRQ_BEI; + + return f81604_sja1000_write(priv, F81604_SJA1000_IER, + ier); + } + + /* set chip to normal mode */ + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + mod_reg |= F81604_SJA1000_MOD_LOM; + if (priv->can.ctrlmode & CAN_CTRLMODE_PRESUME_ACK) + mod_reg |= F81604_SJA1000_MOD_STM; + + ret = f81604_sja1000_write(priv, F81604_SJA1000_MOD, mod_reg); + if (ret) + return ret; + } + + return -EPERM; +} + +static int f81604_chipset_init(struct f81604_port_priv *priv) +{ + int i, ret; + + /* set clock divider and output control register */ + ret = f81604_sja1000_write(priv, F81604_SJA1000_CDR, + CDR_CBP | CDR_PELICAN); + if (ret) + return ret; + + /* set acceptance filter (accept all) */ + for (i = 0; i < F81604_MAX_FILTER_CNT; ++i) { + ret = f81604_sja1000_write(priv, F81604_SJA1000_ACCC0 + i, 0); + if (ret) + return ret; + } + + for (i = 0; i < F81604_MAX_FILTER_CNT; ++i) { + ret = f81604_sja1000_write(priv, F81604_SJA1000_ACCM0 + i, + 0xFF); + if (ret) + return ret; + } + + return f81604_sja1000_write(priv, F81604_SJA1000_OCR, + OCR_TX0_PUSHPULL | OCR_TX1_PUSHPULL | + OCR_MODE_NORMAL); +} + +static void f81604_process_rx_packet(struct net_device *netdev, + struct f81604_can_frame *frame) +{ + struct net_device_stats *stats = &netdev->stats; + struct can_frame *cf; + struct sk_buff *skb; + + if (frame->cmd != F81604_CMD_DATA) + return; + + skb = alloc_can_skb(netdev, &cf); + if (!skb) { + stats->rx_dropped++; + return; + } + + cf->len = can_cc_dlc2len(frame->dlc & F81604_DLC_LEN_MASK); + + if (frame->dlc & F81604_DLC_EFF_BIT) { + cf->can_id = get_unaligned_be32(&frame->eff.id) >> + F81604_EFF_SHIFT; + cf->can_id |= CAN_EFF_FLAG; + + if (!(frame->dlc & F81604_DLC_RTR_BIT)) + memcpy(cf->data, frame->eff.data, cf->len); + } else { + cf->can_id = get_unaligned_be16(&frame->sff.id) >> + F81604_SFF_SHIFT; + + if (!(frame->dlc & F81604_DLC_RTR_BIT)) + memcpy(cf->data, frame->sff.data, cf->len); + } + + if (frame->dlc & F81604_DLC_RTR_BIT) + cf->can_id |= CAN_RTR_FLAG; + else + stats->rx_bytes += cf->len; + + stats->rx_packets++; + netif_rx(skb); +} + +static void f81604_read_bulk_callback(struct urb *urb) +{ + struct f81604_can_frame *frame = urb->transfer_buffer; + struct net_device *netdev = urb->context; + int ret; + + if (!netif_device_present(netdev)) + return; + + if (urb->status) + netdev_info(netdev, "%s: URB aborted %pe\n", __func__, + ERR_PTR(urb->status)); + + switch (urb->status) { + case 0: /* success */ + break; + + case -ENOENT: + case -EPIPE: + case -EPROTO: + case -ESHUTDOWN: + return; + + default: + goto resubmit_urb; + } + + if (urb->actual_length != sizeof(*frame)) { + netdev_warn(netdev, "URB length %u not equal to %zu\n", + urb->actual_length, sizeof(*frame)); + goto resubmit_urb; + } + + f81604_process_rx_packet(netdev, frame); + +resubmit_urb: + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret == -ENODEV) + netif_device_detach(netdev); + else if (ret) + netdev_err(netdev, + "%s: failed to resubmit read bulk urb: %pe\n", + __func__, ERR_PTR(ret)); +} + +static void f81604_handle_tx(struct f81604_port_priv *priv, + struct f81604_int_data *data) +{ + struct net_device *netdev = priv->netdev; + struct net_device_stats *stats = &netdev->stats; + + /* transmission buffer released */ + if (priv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT && + !(data->sr & F81604_SJA1000_SR_TCS)) { + stats->tx_errors++; + can_free_echo_skb(netdev, 0, NULL); + } else { + /* transmission complete */ + stats->tx_bytes += can_get_echo_skb(netdev, 0, NULL); + stats->tx_packets++; + } + + netif_wake_queue(netdev); +} + +static void f81604_handle_can_bus_errors(struct f81604_port_priv *priv, + struct f81604_int_data *data) +{ + enum can_state can_state = priv->can.state; + struct net_device *netdev = priv->netdev; + struct net_device_stats *stats = &netdev->stats; + struct can_frame *cf; + struct sk_buff *skb; + + /* Note: ALC/ECC will not auto clear by read here, must be cleared by + * read register (via clear_reg_work). + */ + + skb = alloc_can_err_skb(netdev, &cf); + if (skb) { + cf->can_id |= CAN_ERR_CNT; + cf->data[6] = data->txerr; + cf->data[7] = data->rxerr; + } + + if (data->isrc & F81604_SJA1000_IRQ_DOI) { + /* data overrun interrupt */ + netdev_dbg(netdev, "data overrun interrupt\n"); + + if (skb) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + } + + stats->rx_over_errors++; + stats->rx_errors++; + + set_bit(F81604_CLEAR_OVERRUN, &priv->clear_flags); + } + + if (data->isrc & F81604_SJA1000_IRQ_EI) { + /* error warning interrupt */ + netdev_dbg(netdev, "error warning interrupt\n"); + + if (data->sr & F81604_SJA1000_SR_BS) + can_state = CAN_STATE_BUS_OFF; + else if (data->sr & F81604_SJA1000_SR_ES) + can_state = CAN_STATE_ERROR_WARNING; + else + can_state = CAN_STATE_ERROR_ACTIVE; + } + + if (data->isrc & F81604_SJA1000_IRQ_BEI) { + /* bus error interrupt */ + netdev_dbg(netdev, "bus error interrupt\n"); + + priv->can.can_stats.bus_error++; + stats->rx_errors++; + + if (skb) { + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + + /* set error type */ + switch (data->ecc & F81604_SJA1000_ECC_MASK) { + case F81604_SJA1000_ECC_BIT: + cf->data[2] |= CAN_ERR_PROT_BIT; + break; + case F81604_SJA1000_ECC_FORM: + cf->data[2] |= CAN_ERR_PROT_FORM; + break; + case F81604_SJA1000_ECC_STUFF: + cf->data[2] |= CAN_ERR_PROT_STUFF; + break; + default: + break; + } + + /* set error location */ + cf->data[3] = data->ecc & F81604_SJA1000_ECC_SEG; + + /* Error occurred during transmission? */ + if ((data->ecc & F81604_SJA1000_ECC_DIR) == 0) + cf->data[2] |= CAN_ERR_PROT_TX; + } + + set_bit(F81604_CLEAR_ECC, &priv->clear_flags); + } + + if (data->isrc & F81604_SJA1000_IRQ_EPI) { + if (can_state == CAN_STATE_ERROR_PASSIVE) + can_state = CAN_STATE_ERROR_WARNING; + else + can_state = CAN_STATE_ERROR_PASSIVE; + + /* error passive interrupt */ + netdev_dbg(netdev, "error passive interrupt: %d\n", can_state); + } + + if (data->isrc & F81604_SJA1000_IRQ_ALI) { + /* arbitration lost interrupt */ + netdev_dbg(netdev, "arbitration lost interrupt\n"); + + priv->can.can_stats.arbitration_lost++; + + if (skb) { + cf->can_id |= CAN_ERR_LOSTARB; + cf->data[0] = data->alc & F81604_SJA1000_ALC_MASK; + } + + set_bit(F81604_CLEAR_ALC, &priv->clear_flags); + } + + if (can_state != priv->can.state) { + enum can_state tx_state, rx_state; + + tx_state = data->txerr >= data->rxerr ? can_state : 0; + rx_state = data->txerr <= data->rxerr ? can_state : 0; + + can_change_state(netdev, cf, tx_state, rx_state); + + if (can_state == CAN_STATE_BUS_OFF) + can_bus_off(netdev); + } + + if (priv->clear_flags) + schedule_work(&priv->clear_reg_work); + + if (skb) + netif_rx(skb); +} + +static void f81604_read_int_callback(struct urb *urb) +{ + struct f81604_int_data *data = urb->transfer_buffer; + struct net_device *netdev = urb->context; + struct f81604_port_priv *priv; + int ret; + + priv = netdev_priv(netdev); + + if (!netif_device_present(netdev)) + return; + + if (urb->status) + netdev_info(netdev, "%s: Int URB aborted: %pe\n", __func__, + ERR_PTR(urb->status)); + + switch (urb->status) { + case 0: /* success */ + break; + + case -ENOENT: + case -EPIPE: + case -EPROTO: + case -ESHUTDOWN: + return; + + default: + goto resubmit_urb; + } + + /* handle Errors */ + if (data->isrc & (F81604_SJA1000_IRQ_DOI | F81604_SJA1000_IRQ_EI | + F81604_SJA1000_IRQ_BEI | F81604_SJA1000_IRQ_EPI | + F81604_SJA1000_IRQ_ALI)) + f81604_handle_can_bus_errors(priv, data); + + /* handle TX */ + if (priv->can.state != CAN_STATE_BUS_OFF && + (data->isrc & F81604_SJA1000_IRQ_TI)) + f81604_handle_tx(priv, data); + +resubmit_urb: + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret == -ENODEV) + netif_device_detach(netdev); + else if (ret) + netdev_err(netdev, "%s: failed to resubmit int urb: %pe\n", + __func__, ERR_PTR(ret)); +} + +static void f81604_unregister_urbs(struct f81604_port_priv *priv) +{ + usb_kill_anchored_urbs(&priv->urbs_anchor); +} + +static int f81604_register_urbs(struct f81604_port_priv *priv) +{ + struct net_device *netdev = priv->netdev; + struct f81604_int_data *int_data; + int id = netdev->dev_port; + struct urb *int_urb; + int rx_urb_cnt; + int ret; + + for (rx_urb_cnt = 0; rx_urb_cnt < F81604_MAX_RX_URBS; ++rx_urb_cnt) { + struct f81604_can_frame *frame; + struct urb *rx_urb; + + rx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!rx_urb) { + ret = -ENOMEM; + break; + } + + frame = kmalloc(sizeof(*frame), GFP_KERNEL); + if (!frame) { + usb_free_urb(rx_urb); + ret = -ENOMEM; + break; + } + + usb_fill_bulk_urb(rx_urb, priv->dev, + usb_rcvbulkpipe(priv->dev, bulk_in_addr[id]), + frame, sizeof(*frame), + f81604_read_bulk_callback, netdev); + + rx_urb->transfer_flags |= URB_FREE_BUFFER; + usb_anchor_urb(rx_urb, &priv->urbs_anchor); + + ret = usb_submit_urb(rx_urb, GFP_KERNEL); + if (ret) { + usb_unanchor_urb(rx_urb); + usb_free_urb(rx_urb); + break; + } + + /* Drop reference, USB core will take care of freeing it */ + usb_free_urb(rx_urb); + } + + if (rx_urb_cnt == 0) { + netdev_warn(netdev, "%s: submit rx urb failed: %pe\n", + __func__, ERR_PTR(ret)); + + goto error; + } + + int_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!int_urb) { + ret = -ENOMEM; + goto error; + } + + int_data = kmalloc(sizeof(*int_data), GFP_KERNEL); + if (!int_data) { + usb_free_urb(int_urb); + ret = -ENOMEM; + goto error; + } + + usb_fill_int_urb(int_urb, priv->dev, + usb_rcvintpipe(priv->dev, int_in_addr[id]), int_data, + sizeof(*int_data), f81604_read_int_callback, netdev, + 1); + + int_urb->transfer_flags |= URB_FREE_BUFFER; + usb_anchor_urb(int_urb, &priv->urbs_anchor); + + ret = usb_submit_urb(int_urb, GFP_KERNEL); + if (ret) { + usb_unanchor_urb(int_urb); + usb_free_urb(int_urb); + + netdev_warn(netdev, "%s: submit int urb failed: %pe\n", + __func__, ERR_PTR(ret)); + goto error; + } + + /* Drop reference, USB core will take care of freeing it */ + usb_free_urb(int_urb); + + return 0; + +error: + f81604_unregister_urbs(priv); + return ret; +} + +static int f81604_start(struct net_device *netdev) +{ + struct f81604_port_priv *priv = netdev_priv(netdev); + int ret; + u8 mode; + u8 tmp; + + mode = F81604_RX_AUTO_RELEASE_BUF | F81604_INT_WHEN_CHANGE; + + /* Set TR/AT mode */ + if (priv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT) + mode |= F81604_TX_ONESHOT; + else + mode |= F81604_TX_NORMAL; + + ret = f81604_sja1000_write(priv, F81604_CTRL_MODE_REG, mode); + if (ret) + return ret; + + /* set reset mode */ + ret = f81604_set_reset_mode(priv); + if (ret) + return ret; + + ret = f81604_chipset_init(priv); + if (ret) + return ret; + + /* Clear error counters and error code capture */ + ret = f81604_sja1000_write(priv, F81604_SJA1000_TXERR, 0); + if (ret) + return ret; + + ret = f81604_sja1000_write(priv, F81604_SJA1000_RXERR, 0); + if (ret) + return ret; + + /* Read clear for ECC/ALC/IR register */ + ret = f81604_sja1000_read(priv, F81604_SJA1000_ECC, &tmp); + if (ret) + return ret; + + ret = f81604_sja1000_read(priv, F81604_SJA1000_ALC, &tmp); + if (ret) + return ret; + + ret = f81604_sja1000_read(priv, F81604_SJA1000_IR, &tmp); + if (ret) + return ret; + + ret = f81604_register_urbs(priv); + if (ret) + return ret; + + ret = f81604_set_normal_mode(priv); + if (ret) { + f81604_unregister_urbs(priv); + return ret; + } + + return 0; +} + +static int f81604_set_bittiming(struct net_device *dev) +{ + struct f81604_port_priv *priv = netdev_priv(dev); + struct can_bittiming *bt = &priv->can.bittiming; + u8 btr0, btr1; + int ret; + + btr0 = FIELD_PREP(F81604_BRP_MASK, bt->brp - 1) | + FIELD_PREP(F81604_SJW_MASK, bt->sjw - 1); + + btr1 = FIELD_PREP(F81604_SEG1_MASK, + bt->prop_seg + bt->phase_seg1 - 1) | + FIELD_PREP(F81604_SEG2_MASK, bt->phase_seg2 - 1); + + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + btr1 |= F81604_SJA1000_BTR1_SAMPLE_TRIPLE; + + ret = f81604_sja1000_write(priv, F81604_SJA1000_BTR0, btr0); + if (ret) { + netdev_warn(dev, "%s: Set BTR0 failed: %pe\n", __func__, + ERR_PTR(ret)); + return ret; + } + + ret = f81604_sja1000_write(priv, F81604_SJA1000_BTR1, btr1); + if (ret) { + netdev_warn(dev, "%s: Set BTR1 failed: %pe\n", __func__, + ERR_PTR(ret)); + return ret; + } + + return 0; +} + +static int f81604_set_mode(struct net_device *netdev, enum can_mode mode) +{ + int ret; + + switch (mode) { + case CAN_MODE_START: + ret = f81604_start(netdev); + if (!ret && netif_queue_stopped(netdev)) + netif_wake_queue(netdev); + break; + + default: + ret = -EOPNOTSUPP; + } + + return ret; +} + +static void f81604_write_bulk_callback(struct urb *urb) +{ + struct net_device *netdev = urb->context; + + if (!netif_device_present(netdev)) + return; + + if (urb->status) + netdev_info(netdev, "%s: Tx URB error: %pe\n", __func__, + ERR_PTR(urb->status)); +} + +static void f81604_clear_reg_work(struct work_struct *work) +{ + struct f81604_port_priv *priv; + u8 tmp; + + priv = container_of(work, struct f81604_port_priv, clear_reg_work); + + /* dummy read for clear Arbitration lost capture(ALC) register. */ + if (test_and_clear_bit(F81604_CLEAR_ALC, &priv->clear_flags)) + f81604_sja1000_read(priv, F81604_SJA1000_ALC, &tmp); + + /* dummy read for clear Error code capture(ECC) register. */ + if (test_and_clear_bit(F81604_CLEAR_ECC, &priv->clear_flags)) + f81604_sja1000_read(priv, F81604_SJA1000_ECC, &tmp); + + /* dummy write for clear data overrun flag. */ + if (test_and_clear_bit(F81604_CLEAR_OVERRUN, &priv->clear_flags)) + f81604_sja1000_write(priv, F81604_SJA1000_CMR, + F81604_SJA1000_CMD_CDO); +} + +static netdev_tx_t f81604_start_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + struct can_frame *cf = (struct can_frame *)skb->data; + struct f81604_port_priv *priv = netdev_priv(netdev); + struct net_device_stats *stats = &netdev->stats; + struct f81604_can_frame *frame; + struct urb *write_urb; + int ret; + + if (can_dev_dropped_skb(netdev, skb)) + return NETDEV_TX_OK; + + netif_stop_queue(netdev); + + write_urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!write_urb) + goto nomem_urb; + + frame = kzalloc(sizeof(*frame), GFP_ATOMIC); + if (!frame) + goto nomem_buf; + + usb_fill_bulk_urb(write_urb, priv->dev, + usb_sndbulkpipe(priv->dev, + bulk_out_addr[netdev->dev_port]), + frame, sizeof(*frame), f81604_write_bulk_callback, + priv->netdev); + + write_urb->transfer_flags |= URB_FREE_BUFFER; + + frame->cmd = F81604_CMD_DATA; + frame->dlc = cf->len; + + if (cf->can_id & CAN_RTR_FLAG) + frame->dlc |= F81604_DLC_RTR_BIT; + + if (cf->can_id & CAN_EFF_FLAG) { + u32 id = (cf->can_id & CAN_EFF_MASK) << F81604_EFF_SHIFT; + + put_unaligned_be32(id, &frame->eff.id); + + frame->dlc |= F81604_DLC_EFF_BIT; + + if (!(cf->can_id & CAN_RTR_FLAG)) + memcpy(&frame->eff.data, cf->data, cf->len); + } else { + u32 id = (cf->can_id & CAN_SFF_MASK) << F81604_SFF_SHIFT; + + put_unaligned_be16(id, &frame->sff.id); + + if (!(cf->can_id & CAN_RTR_FLAG)) + memcpy(&frame->sff.data, cf->data, cf->len); + } + + can_put_echo_skb(skb, netdev, 0, 0); + + ret = usb_submit_urb(write_urb, GFP_ATOMIC); + if (ret) { + netdev_err(netdev, "%s: failed to resubmit tx bulk urb: %pe\n", + __func__, ERR_PTR(ret)); + + can_free_echo_skb(netdev, 0, NULL); + stats->tx_dropped++; + stats->tx_errors++; + + if (ret == -ENODEV) + netif_device_detach(netdev); + else + netif_wake_queue(netdev); + } + + /* let usb core take care of this urb */ + usb_free_urb(write_urb); + + return NETDEV_TX_OK; + +nomem_buf: + usb_free_urb(write_urb); + +nomem_urb: + dev_kfree_skb(skb); + stats->tx_dropped++; + stats->tx_errors++; + netif_wake_queue(netdev); + + return NETDEV_TX_OK; +} + +static int f81604_get_berr_counter(const struct net_device *netdev, + struct can_berr_counter *bec) +{ + struct f81604_port_priv *priv = netdev_priv(netdev); + u8 txerr, rxerr; + int ret; + + ret = f81604_sja1000_read(priv, F81604_SJA1000_TXERR, &txerr); + if (ret) + return ret; + + ret = f81604_sja1000_read(priv, F81604_SJA1000_RXERR, &rxerr); + if (ret) + return ret; + + bec->txerr = txerr; + bec->rxerr = rxerr; + + return 0; +} + +/* Open USB device */ +static int f81604_open(struct net_device *netdev) +{ + int ret; + + ret = open_candev(netdev); + if (ret) + return ret; + + ret = f81604_start(netdev); + if (ret) { + if (ret == -ENODEV) + netif_device_detach(netdev); + + close_candev(netdev); + return ret; + } + + netif_start_queue(netdev); + return 0; +} + +/* Close USB device */ +static int f81604_close(struct net_device *netdev) +{ + struct f81604_port_priv *priv = netdev_priv(netdev); + + f81604_set_reset_mode(priv); + + netif_stop_queue(netdev); + cancel_work_sync(&priv->clear_reg_work); + close_candev(netdev); + + f81604_unregister_urbs(priv); + + return 0; +} + +static const struct net_device_ops f81604_netdev_ops = { + .ndo_open = f81604_open, + .ndo_stop = f81604_close, + .ndo_start_xmit = f81604_start_xmit, + .ndo_change_mtu = can_change_mtu, +}; + +static const struct can_bittiming_const f81604_bittiming_const = { + .name = KBUILD_MODNAME, + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 64, + .brp_inc = 1, +}; + +/* Called by the usb core when driver is unloaded or device is removed */ +static void f81604_disconnect(struct usb_interface *intf) +{ + struct f81604_priv *priv = usb_get_intfdata(intf); + int i; + + for (i = 0; i < ARRAY_SIZE(priv->netdev); ++i) { + if (!priv->netdev[i]) + continue; + + unregister_netdev(priv->netdev[i]); + free_candev(priv->netdev[i]); + } +} + +static int __f81604_set_termination(struct usb_device *dev, int idx, u16 term) +{ + u8 mask, data = 0; + + if (idx == 0) + mask = F81604_CAN0_TERM; + else + mask = F81604_CAN1_TERM; + + if (term) + data = mask; + + return f81604_update_bits(dev, F81604_TERMINATOR_REG, mask, data); +} + +static int f81604_set_termination(struct net_device *netdev, u16 term) +{ + struct f81604_port_priv *port_priv = netdev_priv(netdev); + + ASSERT_RTNL(); + + return __f81604_set_termination(port_priv->dev, netdev->dev_port, + term); +} + +static int f81604_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct net_device *netdev; + struct f81604_priv *priv; + int i, ret; + + priv = devm_kzalloc(&intf->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + usb_set_intfdata(intf, priv); + + for (i = 0; i < ARRAY_SIZE(priv->netdev); ++i) { + ret = __f81604_set_termination(dev, i, 0); + if (ret) { + dev_err(&intf->dev, + "Setting termination of CH#%d failed: %pe\n", + i, ERR_PTR(ret)); + return ret; + } + } + + for (i = 0; i < ARRAY_SIZE(priv->netdev); ++i) { + struct f81604_port_priv *port_priv; + + netdev = alloc_candev(sizeof(*port_priv), 1); + if (!netdev) { + dev_err(&intf->dev, "Couldn't alloc candev: %d\n", i); + ret = -ENOMEM; + + goto failure_cleanup; + } + + port_priv = netdev_priv(netdev); + + INIT_WORK(&port_priv->clear_reg_work, f81604_clear_reg_work); + init_usb_anchor(&port_priv->urbs_anchor); + + port_priv->intf = intf; + port_priv->dev = dev; + port_priv->netdev = netdev; + port_priv->can.clock.freq = F81604_CAN_CLOCK; + + port_priv->can.termination_const = f81604_termination; + port_priv->can.termination_const_cnt = + ARRAY_SIZE(f81604_termination); + port_priv->can.bittiming_const = &f81604_bittiming_const; + port_priv->can.do_set_bittiming = f81604_set_bittiming; + port_priv->can.do_set_mode = f81604_set_mode; + port_priv->can.do_set_termination = f81604_set_termination; + port_priv->can.do_get_berr_counter = f81604_get_berr_counter; + port_priv->can.ctrlmode_supported = + CAN_CTRLMODE_LISTENONLY | CAN_CTRLMODE_3_SAMPLES | + CAN_CTRLMODE_ONE_SHOT | CAN_CTRLMODE_BERR_REPORTING | + CAN_CTRLMODE_PRESUME_ACK; + + netdev->ethtool_ops = &f81604_ethtool_ops; + netdev->netdev_ops = &f81604_netdev_ops; + netdev->flags |= IFF_ECHO; + netdev->dev_port = i; + + SET_NETDEV_DEV(netdev, &intf->dev); + + ret = register_candev(netdev); + if (ret) { + netdev_err(netdev, "register CAN device failed: %pe\n", + ERR_PTR(ret)); + free_candev(netdev); + + goto failure_cleanup; + } + + priv->netdev[i] = netdev; + } + + return 0; + +failure_cleanup: + f81604_disconnect(intf); + return ret; +} + +static struct usb_driver f81604_driver = { + .name = KBUILD_MODNAME, + .probe = f81604_probe, + .disconnect = f81604_disconnect, + .id_table = f81604_table, +}; + +module_usb_driver(f81604_driver); + +MODULE_AUTHOR("Ji-Ze Hong (Peter Hong) <peter_hong@fintek.com.tw>"); +MODULE_DESCRIPTION("Fintek F81604 USB to 2xCANBUS"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/can/xilinx_can.c b/drivers/net/can/xilinx_can.c index 43c812ea1de0..797c69a0314d 100644 --- a/drivers/net/can/xilinx_can.c +++ b/drivers/net/can/xilinx_can.c @@ -1898,20 +1898,18 @@ err: * This function frees all the resources allocated to the device. * Return: 0 always */ -static int xcan_remove(struct platform_device *pdev) +static void xcan_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); unregister_candev(ndev); pm_runtime_disable(&pdev->dev); free_candev(ndev); - - return 0; } static struct platform_driver xcan_driver = { .probe = xcan_probe, - .remove = xcan_remove, + .remove_new = xcan_remove, .driver = { .name = DRIVER_NAME, .pm = &xcan_dev_pm_ops, diff --git a/drivers/net/ethernet/8390/8390.h b/drivers/net/ethernet/8390/8390.h index e52264465998..f784a6e2ab0e 100644 --- a/drivers/net/ethernet/8390/8390.h +++ b/drivers/net/ethernet/8390/8390.h @@ -1,3 +1,5 @@ +/* SPDX-License-Identifier: GPL-1.0+ */ + /* Generic NS8390 register definitions. */ /* This file is part of Donald Becker's 8390 drivers, and is distributed diff --git a/drivers/net/ethernet/8390/apne.c b/drivers/net/ethernet/8390/apne.c index 991ad953aa79..a09f383dd249 100644 --- a/drivers/net/ethernet/8390/apne.c +++ b/drivers/net/ethernet/8390/apne.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Amiga Linux/68k 8390 based PCMCIA Ethernet Driver for the Amiga 1200 * @@ -19,12 +20,6 @@ * * ---------------------------------------------------------------------------- * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of the Linux - * distribution for more details. - * - * ---------------------------------------------------------------------------- - * */ diff --git a/drivers/net/ethernet/8390/axnet_cs.c b/drivers/net/ethernet/8390/axnet_cs.c index 78f985885547..fea489af72fb 100644 --- a/drivers/net/ethernet/8390/axnet_cs.c +++ b/drivers/net/ethernet/8390/axnet_cs.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-1.0+ + /*====================================================================== A PCMCIA ethernet driver for Asix AX88190-based cards @@ -17,9 +19,7 @@ Written 1992,1993 by Donald Becker. Copyright 1993 United States Government as represented by the - Director, National Security Agency. This software may be used and - distributed according to the terms of the GNU General Public License, - incorporated herein by reference. + Director, National Security Agency. Donald Becker may be reached at becker@scyld.com ======================================================================*/ diff --git a/drivers/net/ethernet/8390/hydra.c b/drivers/net/ethernet/8390/hydra.c index 1df7601af86a..24f49a8ff903 100644 --- a/drivers/net/ethernet/8390/hydra.c +++ b/drivers/net/ethernet/8390/hydra.c @@ -1,10 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only + /* New Hydra driver using generic 8390 core */ /* Based on old hydra driver by Topi Kanerva (topi@susanna.oulu.fi) */ -/* This file is subject to the terms and conditions of the GNU General */ -/* Public License. See the file COPYING in the main directory of the */ -/* Linux distribution for more details. */ - /* Peter De Schrijver (p2@mind.be) */ /* Oldenburg 2000 */ diff --git a/drivers/net/ethernet/8390/lib8390.c b/drivers/net/ethernet/8390/lib8390.c index e84021282edf..84aeb8054304 100644 --- a/drivers/net/ethernet/8390/lib8390.c +++ b/drivers/net/ethernet/8390/lib8390.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-1.0+ + /* 8390.c: A general NS8390 ethernet driver core for linux. */ /* Written 1992-94 by Donald Becker. @@ -5,9 +7,6 @@ Copyright 1993 United States Government as represented by the Director, National Security Agency. - This software may be used and distributed according to the terms - of the GNU General Public License, incorporated herein by reference. - The author may be reached as becker@scyld.com, or C/O Scyld Computing Corporation 410 Severn Ave., Suite 210 diff --git a/drivers/net/ethernet/8390/mac8390.c b/drivers/net/ethernet/8390/mac8390.c index 7fb819b9b89a..4a0a095a1a8a 100644 --- a/drivers/net/ethernet/8390/mac8390.c +++ b/drivers/net/ethernet/8390/mac8390.c @@ -1,11 +1,9 @@ +// SPDX-License-Identifier: GPL-1.0+ /* mac8390.c: New driver for 8390-based Nubus (or Nubus-alike) Ethernet cards on Linux */ /* Based on the former daynaport.c driver, by Alan Cox. Some code taken from or inspired by skeleton.c by Donald Becker, acenic.c by - Jes Sorensen, and ne2k-pci.c by Donald Becker and Paul Gortmaker. - - This software may be used and distributed according to the terms of - the GNU Public License, incorporated herein by reference. */ + Jes Sorensen, and ne2k-pci.c by Donald Becker and Paul Gortmaker. */ /* 2000-02-28: support added for Dayna and Kinetics cards by A.G.deWijn@phys.uu.nl */ diff --git a/drivers/net/ethernet/8390/mcf8390.c b/drivers/net/ethernet/8390/mcf8390.c index 8a7918d33419..217838b28220 100644 --- a/drivers/net/ethernet/8390/mcf8390.c +++ b/drivers/net/ethernet/8390/mcf8390.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Support for ColdFire CPU based boards using a NS8390 Ethernet device. * @@ -5,9 +6,6 @@ * * (C) Copyright 2012, Greg Ungerer <gerg@uclinux.org> * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of the Linux - * distribution for more details. */ #include <linux/module.h> diff --git a/drivers/net/ethernet/8390/ne.c b/drivers/net/ethernet/8390/ne.c index bc9c81dc00fd..7d89ec1cf273 100644 --- a/drivers/net/ethernet/8390/ne.c +++ b/drivers/net/ethernet/8390/ne.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-1.0+ /* ne.c: A general non-shared-memory NS8390 ethernet driver for linux. */ /* Written 1992-94 by Donald Becker. @@ -5,9 +6,6 @@ Copyright 1993 United States Government as represented by the Director, National Security Agency. - This software may be used and distributed according to the terms - of the GNU General Public License, incorporated herein by reference. - The author may be reached as becker@scyld.com, or C/O Scyld Computing Corporation, 410 Severn Ave., Suite 210, Annapolis MD 21403 diff --git a/drivers/net/ethernet/8390/ne2k-pci.c b/drivers/net/ethernet/8390/ne2k-pci.c index 6a0a2039600a..2c6bd36d2f31 100644 --- a/drivers/net/ethernet/8390/ne2k-pci.c +++ b/drivers/net/ethernet/8390/ne2k-pci.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-1.0+ /* A Linux device driver for PCI NE2000 clones. * * Authors and other copyright holders: diff --git a/drivers/net/ethernet/8390/pcnet_cs.c b/drivers/net/ethernet/8390/pcnet_cs.c index 0f07fe03da98..9bd5e991f1e5 100644 --- a/drivers/net/ethernet/8390/pcnet_cs.c +++ b/drivers/net/ethernet/8390/pcnet_cs.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-1.0+ /*====================================================================== A PCMCIA ethernet driver for NS8390-based cards @@ -17,9 +18,7 @@ Written 1992,1993 by Donald Becker. Copyright 1993 United States Government as represented by the - Director, National Security Agency. This software may be used and - distributed according to the terms of the GNU General Public License, - incorporated herein by reference. + Director, National Security Agency. Donald Becker may be reached at becker@scyld.com Based also on Keith Moore's changes to Don Becker's code, for IBM diff --git a/drivers/net/ethernet/8390/smc-ultra.c b/drivers/net/ethernet/8390/smc-ultra.c index 7465650c8078..22ca804b2e95 100644 --- a/drivers/net/ethernet/8390/smc-ultra.c +++ b/drivers/net/ethernet/8390/smc-ultra.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-1.0+ /* smc-ultra.c: A SMC Ultra ethernet driver for linux. */ /* This is a driver for the SMC Ultra and SMC EtherEZ ISA ethercards. @@ -7,9 +8,6 @@ Copyright 1993 United States Government as represented by the Director, National Security Agency. - This software may be used and distributed according to the terms - of the GNU General Public License, incorporated herein by reference. - The author may be reached as becker@scyld.com, or C/O Scyld Computing Corporation 410 Severn Ave., Suite 210 diff --git a/drivers/net/ethernet/8390/stnic.c b/drivers/net/ethernet/8390/stnic.c index bd89ca8a92df..265976e3b64a 100644 --- a/drivers/net/ethernet/8390/stnic.c +++ b/drivers/net/ethernet/8390/stnic.c @@ -1,9 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* stnic.c : A SH7750 specific part of driver for NS DP83902A ST-NIC. * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * * Copyright (C) 1999 kaz Kojima */ diff --git a/drivers/net/ethernet/8390/wd.c b/drivers/net/ethernet/8390/wd.c index 119021d41451..ffd639477dfc 100644 --- a/drivers/net/ethernet/8390/wd.c +++ b/drivers/net/ethernet/8390/wd.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-1.0+ /* wd.c: A WD80x3 ethernet driver for linux. */ /* Written 1993-94 by Donald Becker. @@ -5,9 +6,6 @@ Copyright 1993 United States Government as represented by the Director, National Security Agency. - This software may be used and distributed according to the terms - of the GNU General Public License, incorporated herein by reference. - The author may be reached as becker@scyld.com, or C/O Scyld Computing Corporation 410 Severn Ave., Suite 210 diff --git a/drivers/net/ethernet/8390/zorro8390.c b/drivers/net/ethernet/8390/zorro8390.c index e8b4fe813a08..d70390e9d03d 100644 --- a/drivers/net/ethernet/8390/zorro8390.c +++ b/drivers/net/ethernet/8390/zorro8390.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Amiga Linux/m68k and Linux/PPC Zorro NS8390 Ethernet Driver * @@ -9,12 +10,6 @@ * * --------------------------------------------------------------------------- * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of the Linux - * distribution for more details. - * - * --------------------------------------------------------------------------- - * * The Ariadne II and X-Surf are Zorro-II boards containing Realtek RTL8019AS * Ethernet Controllers. */ diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_macsec.c b/drivers/net/ethernet/aquantia/atlantic/aq_macsec.c index 7eb5851eb95d..6afff8af5e86 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_macsec.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_macsec.c @@ -289,7 +289,7 @@ static int aq_get_txsc_stats(struct aq_hw_s *hw, const int sc_idx, static int aq_mdo_dev_open(struct macsec_context *ctx) { - struct aq_nic_s *nic = netdev_priv(ctx->netdev); + struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev); int ret = 0; if (netif_carrier_ok(nic->ndev)) @@ -300,7 +300,7 @@ static int aq_mdo_dev_open(struct macsec_context *ctx) static int aq_mdo_dev_stop(struct macsec_context *ctx) { - struct aq_nic_s *nic = netdev_priv(ctx->netdev); + struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev); int i; for (i = 0; i < AQ_MACSEC_MAX_SC; i++) { @@ -439,7 +439,7 @@ static enum aq_macsec_sc_sa sc_sa_from_num_an(const int num_an) static int aq_mdo_add_secy(struct macsec_context *ctx) { - struct aq_nic_s *nic = netdev_priv(ctx->netdev); + struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev); struct aq_macsec_cfg *cfg = nic->macsec_cfg; const struct macsec_secy *secy = ctx->secy; enum aq_macsec_sc_sa sc_sa; @@ -474,7 +474,7 @@ static int aq_mdo_add_secy(struct macsec_context *ctx) static int aq_mdo_upd_secy(struct macsec_context *ctx) { - struct aq_nic_s *nic = netdev_priv(ctx->netdev); + struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev); const struct macsec_secy *secy = ctx->secy; int txsc_idx; int ret = 0; @@ -528,7 +528,7 @@ static int aq_clear_txsc(struct aq_nic_s *nic, const int txsc_idx, static int aq_mdo_del_secy(struct macsec_context *ctx) { - struct aq_nic_s *nic = netdev_priv(ctx->netdev); + struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev); int ret = 0; if (!nic->macsec_cfg) @@ -576,7 +576,7 @@ static int aq_update_txsa(struct aq_nic_s *nic, const unsigned int sc_idx, static int aq_mdo_add_txsa(struct macsec_context *ctx) { - struct aq_nic_s *nic = netdev_priv(ctx->netdev); + struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev); struct aq_macsec_cfg *cfg = nic->macsec_cfg; const struct macsec_secy *secy = ctx->secy; struct aq_macsec_txsc *aq_txsc; @@ -603,7 +603,7 @@ static int aq_mdo_add_txsa(struct macsec_context *ctx) static int aq_mdo_upd_txsa(struct macsec_context *ctx) { - struct aq_nic_s *nic = netdev_priv(ctx->netdev); + struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev); struct aq_macsec_cfg *cfg = nic->macsec_cfg; const struct macsec_secy *secy = ctx->secy; struct aq_macsec_txsc *aq_txsc; @@ -652,7 +652,7 @@ static int aq_clear_txsa(struct aq_nic_s *nic, struct aq_macsec_txsc *aq_txsc, static int aq_mdo_del_txsa(struct macsec_context *ctx) { - struct aq_nic_s *nic = netdev_priv(ctx->netdev); + struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev); struct aq_macsec_cfg *cfg = nic->macsec_cfg; int txsc_idx; int ret = 0; @@ -744,7 +744,7 @@ static int aq_set_rxsc(struct aq_nic_s *nic, const u32 rxsc_idx) static int aq_mdo_add_rxsc(struct macsec_context *ctx) { - struct aq_nic_s *nic = netdev_priv(ctx->netdev); + struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev); struct aq_macsec_cfg *cfg = nic->macsec_cfg; const u32 rxsc_idx_max = aq_sc_idx_max(cfg->sc_sa); u32 rxsc_idx; @@ -775,7 +775,7 @@ static int aq_mdo_add_rxsc(struct macsec_context *ctx) static int aq_mdo_upd_rxsc(struct macsec_context *ctx) { - struct aq_nic_s *nic = netdev_priv(ctx->netdev); + struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev); int rxsc_idx; int ret = 0; @@ -838,7 +838,7 @@ static int aq_clear_rxsc(struct aq_nic_s *nic, const int rxsc_idx, static int aq_mdo_del_rxsc(struct macsec_context *ctx) { - struct aq_nic_s *nic = netdev_priv(ctx->netdev); + struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev); enum aq_clear_type clear_type = AQ_CLEAR_SW; int rxsc_idx; int ret = 0; @@ -906,8 +906,8 @@ static int aq_update_rxsa(struct aq_nic_s *nic, const unsigned int sc_idx, static int aq_mdo_add_rxsa(struct macsec_context *ctx) { + struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev); const struct macsec_rx_sc *rx_sc = ctx->sa.rx_sa->sc; - struct aq_nic_s *nic = netdev_priv(ctx->netdev); const struct macsec_secy *secy = ctx->secy; struct aq_macsec_rxsc *aq_rxsc; int rxsc_idx; @@ -933,8 +933,8 @@ static int aq_mdo_add_rxsa(struct macsec_context *ctx) static int aq_mdo_upd_rxsa(struct macsec_context *ctx) { + struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev); const struct macsec_rx_sc *rx_sc = ctx->sa.rx_sa->sc; - struct aq_nic_s *nic = netdev_priv(ctx->netdev); struct aq_macsec_cfg *cfg = nic->macsec_cfg; const struct macsec_secy *secy = ctx->secy; int rxsc_idx; @@ -982,8 +982,8 @@ static int aq_clear_rxsa(struct aq_nic_s *nic, struct aq_macsec_rxsc *aq_rxsc, static int aq_mdo_del_rxsa(struct macsec_context *ctx) { + struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev); const struct macsec_rx_sc *rx_sc = ctx->sa.rx_sa->sc; - struct aq_nic_s *nic = netdev_priv(ctx->netdev); struct aq_macsec_cfg *cfg = nic->macsec_cfg; int rxsc_idx; int ret = 0; @@ -1000,7 +1000,7 @@ static int aq_mdo_del_rxsa(struct macsec_context *ctx) static int aq_mdo_get_dev_stats(struct macsec_context *ctx) { - struct aq_nic_s *nic = netdev_priv(ctx->netdev); + struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev); struct aq_macsec_common_stats *stats = &nic->macsec_cfg->stats; struct aq_hw_s *hw = nic->aq_hw; @@ -1020,7 +1020,7 @@ static int aq_mdo_get_dev_stats(struct macsec_context *ctx) static int aq_mdo_get_tx_sc_stats(struct macsec_context *ctx) { - struct aq_nic_s *nic = netdev_priv(ctx->netdev); + struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev); struct aq_macsec_tx_sc_stats *stats; struct aq_hw_s *hw = nic->aq_hw; struct aq_macsec_txsc *aq_txsc; @@ -1044,7 +1044,7 @@ static int aq_mdo_get_tx_sc_stats(struct macsec_context *ctx) static int aq_mdo_get_tx_sa_stats(struct macsec_context *ctx) { - struct aq_nic_s *nic = netdev_priv(ctx->netdev); + struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev); struct aq_macsec_cfg *cfg = nic->macsec_cfg; struct aq_macsec_tx_sa_stats *stats; struct aq_hw_s *hw = nic->aq_hw; @@ -1084,7 +1084,7 @@ static int aq_mdo_get_tx_sa_stats(struct macsec_context *ctx) static int aq_mdo_get_rx_sc_stats(struct macsec_context *ctx) { - struct aq_nic_s *nic = netdev_priv(ctx->netdev); + struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev); struct aq_macsec_cfg *cfg = nic->macsec_cfg; struct aq_macsec_rx_sa_stats *stats; struct aq_hw_s *hw = nic->aq_hw; @@ -1129,7 +1129,7 @@ static int aq_mdo_get_rx_sc_stats(struct macsec_context *ctx) static int aq_mdo_get_rx_sa_stats(struct macsec_context *ctx) { - struct aq_nic_s *nic = netdev_priv(ctx->netdev); + struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev); struct aq_macsec_cfg *cfg = nic->macsec_cfg; struct aq_macsec_rx_sa_stats *stats; struct aq_hw_s *hw = nic->aq_hw; @@ -1399,7 +1399,7 @@ static void aq_check_txsa_expiration(struct aq_nic_s *nic) #define AQ_LOCKED_MDO_DEF(mdo) \ static int aq_locked_mdo_##mdo(struct macsec_context *ctx) \ { \ - struct aq_nic_s *nic = netdev_priv(ctx->netdev); \ + struct aq_nic_s *nic = macsec_netdev_priv(ctx->netdev); \ int ret; \ mutex_lock(&nic->macsec_mutex); \ ret = aq_mdo_##mdo(ctx); \ diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c index 7f933175cbda..4de22eed099a 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c @@ -532,10 +532,10 @@ static bool aq_add_rx_fragment(struct device *dev, buff_->rxdata.pg_off, buff_->len, DMA_FROM_DEVICE); - skb_frag_off_set(frag, buff_->rxdata.pg_off); - skb_frag_size_set(frag, buff_->len); sinfo->xdp_frags_size += buff_->len; - __skb_frag_set_page(frag, buff_->rxdata.page); + skb_frag_fill_page_desc(frag, buff_->rxdata.page, + buff_->rxdata.pg_off, + buff_->len); buff_->is_cleaned = 1; diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c index 466e1d62bcf6..0d917a9699c5 100644 --- a/drivers/net/ethernet/broadcom/bnx2.c +++ b/drivers/net/ethernet/broadcom/bnx2.c @@ -2955,7 +2955,6 @@ bnx2_reuse_rx_skb_pages(struct bnx2 *bp, struct bnx2_rx_ring_info *rxr, shinfo = skb_shinfo(skb); shinfo->nr_frags--; page = skb_frag_page(&shinfo->frags[shinfo->nr_frags]); - __skb_frag_set_page(&shinfo->frags[shinfo->nr_frags], NULL); cons_rx_pg->page = page; dev_kfree_skb(skb); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index dcd9367f05af..f42e51bd3e42 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -1085,9 +1085,8 @@ static u32 __bnxt_rx_agg_pages(struct bnxt *bp, RX_AGG_CMP_LEN) >> RX_AGG_CMP_LEN_SHIFT; cons_rx_buf = &rxr->rx_agg_ring[cons]; - skb_frag_off_set(frag, cons_rx_buf->offset); - skb_frag_size_set(frag, frag_len); - __skb_frag_set_page(frag, cons_rx_buf->page); + skb_frag_fill_page_desc(frag, cons_rx_buf->page, + cons_rx_buf->offset, frag_len); shinfo->nr_frags = i + 1; __clear_bit(cons, rxr->rx_agg_bmap); @@ -1103,10 +1102,7 @@ static u32 __bnxt_rx_agg_pages(struct bnxt *bp, xdp_buff_set_frag_pfmemalloc(xdp); if (bnxt_alloc_rx_page(bp, rxr, prod, GFP_ATOMIC) != 0) { - unsigned int nr_frags; - - nr_frags = --shinfo->nr_frags; - __skb_frag_set_page(&shinfo->frags[nr_frags], NULL); + --shinfo->nr_frags; cons_rx_buf->page = page; /* Update prod since possibly some pages have been diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c b/drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c index 3a4b6cb7b7b9..7a41cad5788f 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c @@ -42,6 +42,12 @@ void bcmgenet_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) struct bcmgenet_priv *priv = netdev_priv(dev); struct device *kdev = &priv->pdev->dev; + if (dev->phydev) { + phy_ethtool_get_wol(dev->phydev, wol); + if (wol->supported) + return; + } + if (!device_can_wakeup(kdev)) { wol->supported = 0; wol->wolopts = 0; @@ -63,6 +69,14 @@ int bcmgenet_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { struct bcmgenet_priv *priv = netdev_priv(dev); struct device *kdev = &priv->pdev->dev; + int ret; + + /* Try Wake-on-LAN from the PHY first */ + if (dev->phydev) { + ret = phy_ethtool_set_wol(dev->phydev, wol); + if (ret != -EOPNOTSUPP) + return ret; + } if (!device_can_wakeup(kdev)) return -ENOTSUPP; diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index cfbdd0022764..b6d5bf8deb79 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -1181,6 +1181,7 @@ struct macb_config { struct clk **hclk, struct clk **tx_clk, struct clk **rx_clk, struct clk **tsu_clk); int (*init)(struct platform_device *pdev); + unsigned int max_tx_length; int jumbo_max_len; const struct macb_usrio_config *usrio; }; diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 29a1199dad14..50a4b04315e9 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -4117,14 +4117,12 @@ static int macb_init(struct platform_device *pdev) /* setup appropriated routines according to adapter type */ if (macb_is_gem(bp)) { - bp->max_tx_length = GEM_MAX_TX_LEN; bp->macbgem_ops.mog_alloc_rx_buffers = gem_alloc_rx_buffers; bp->macbgem_ops.mog_free_rx_buffers = gem_free_rx_buffers; bp->macbgem_ops.mog_init_rings = gem_init_rings; bp->macbgem_ops.mog_rx = gem_rx; dev->ethtool_ops = &gem_ethtool_ops; } else { - bp->max_tx_length = MACB_MAX_TX_LEN; bp->macbgem_ops.mog_alloc_rx_buffers = macb_alloc_rx_buffers; bp->macbgem_ops.mog_free_rx_buffers = macb_free_rx_buffers; bp->macbgem_ops.mog_init_rings = macb_init_rings; @@ -4861,7 +4859,8 @@ static const struct macb_config mpfs_config = { .clk_init = macb_clk_init, .init = init_reset_optional, .usrio = &macb_default_usrio, - .jumbo_max_len = 10240, + .max_tx_length = 4040, /* Cadence Erratum 1686 */ + .jumbo_max_len = 4040, }; static const struct macb_config sama7g5_gem_config = { @@ -5012,6 +5011,13 @@ static int macb_probe(struct platform_device *pdev) if (macb_config) bp->jumbo_max_len = macb_config->jumbo_max_len; + if (!hw_is_gem(bp->regs, bp->native_io)) + bp->max_tx_length = MACB_MAX_TX_LEN; + else if (macb_config->max_tx_length) + bp->max_tx_length = macb_config->max_tx_length; + else + bp->max_tx_length = GEM_MAX_TX_LEN; + bp->wol = 0; if (of_property_read_bool(np, "magic-packet")) bp->wol |= MACB_WOL_HAS_MAGIC_PACKET; diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 9bd1d2d7027d..100daadbea2a 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -191,8 +191,7 @@ static void octeon_droq_bh(struct tasklet_struct *t) static int lio_wait_for_oq_pkts(struct octeon_device *oct) { - struct octeon_device_priv *oct_priv = - (struct octeon_device_priv *)oct->priv; + struct octeon_device_priv *oct_priv = oct->priv; int retry = 100, pkt_cnt = 0, pending_pkts = 0; int i; @@ -950,8 +949,7 @@ static void octeon_destroy_resources(struct octeon_device *oct) { int i, refcount; struct msix_entry *msix_entries; - struct octeon_device_priv *oct_priv = - (struct octeon_device_priv *)oct->priv; + struct octeon_device_priv *oct_priv = oct->priv; struct handshake *hs; @@ -1211,8 +1209,7 @@ static int send_rx_ctrl_cmd(struct lio *lio, int start_stop) static void liquidio_destroy_nic_device(struct octeon_device *oct, int ifidx) { struct net_device *netdev = oct->props[ifidx].netdev; - struct octeon_device_priv *oct_priv = - (struct octeon_device_priv *)oct->priv; + struct octeon_device_priv *oct_priv = oct->priv; struct napi_struct *napi, *n; struct lio *lio; @@ -1774,8 +1771,7 @@ static int liquidio_open(struct net_device *netdev) { struct lio *lio = GET_LIO(netdev); struct octeon_device *oct = lio->oct_dev; - struct octeon_device_priv *oct_priv = - (struct octeon_device_priv *)oct->priv; + struct octeon_device_priv *oct_priv = oct->priv; struct napi_struct *napi, *n; int ret = 0; @@ -1855,8 +1851,7 @@ static int liquidio_stop(struct net_device *netdev) { struct lio *lio = GET_LIO(netdev); struct octeon_device *oct = lio->oct_dev; - struct octeon_device_priv *oct_priv = - (struct octeon_device_priv *)oct->priv; + struct octeon_device_priv *oct_priv = oct->priv; struct napi_struct *napi, *n; int ret = 0; @@ -4057,8 +4052,7 @@ static int octeon_device_init(struct octeon_device *octeon_dev) char bootcmd[] = "\n"; char *dbg_enb = NULL; enum lio_fw_state fw_state; - struct octeon_device_priv *oct_priv = - (struct octeon_device_priv *)octeon_dev->priv; + struct octeon_device_priv *oct_priv = octeon_dev->priv; atomic_set(&octeon_dev->status, OCT_DEV_BEGIN_STATE); /* Enable access to the octeon device and make its DMA capability diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index e2921aec3da0..62c2eadc33e3 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -72,8 +72,7 @@ static int liquidio_stop(struct net_device *netdev); static int lio_wait_for_oq_pkts(struct octeon_device *oct) { - struct octeon_device_priv *oct_priv = - (struct octeon_device_priv *)oct->priv; + struct octeon_device_priv *oct_priv = oct->priv; int retry = MAX_IO_PENDING_PKT_COUNT; int pkt_cnt = 0, pending_pkts; int i; @@ -442,8 +441,7 @@ static void octeon_pci_flr(struct octeon_device *oct) */ static void octeon_destroy_resources(struct octeon_device *oct) { - struct octeon_device_priv *oct_priv = - (struct octeon_device_priv *)oct->priv; + struct octeon_device_priv *oct_priv = oct->priv; struct msix_entry *msix_entries; int i; @@ -659,8 +657,7 @@ static int send_rx_ctrl_cmd(struct lio *lio, int start_stop) static void liquidio_destroy_nic_device(struct octeon_device *oct, int ifidx) { struct net_device *netdev = oct->props[ifidx].netdev; - struct octeon_device_priv *oct_priv = - (struct octeon_device_priv *)oct->priv; + struct octeon_device_priv *oct_priv = oct->priv; struct napi_struct *napi, *n; struct lio *lio; @@ -909,8 +906,7 @@ static int liquidio_open(struct net_device *netdev) { struct lio *lio = GET_LIO(netdev); struct octeon_device *oct = lio->oct_dev; - struct octeon_device_priv *oct_priv = - (struct octeon_device_priv *)oct->priv; + struct octeon_device_priv *oct_priv = oct->priv; struct napi_struct *napi, *n; int ret = 0; @@ -956,8 +952,7 @@ static int liquidio_stop(struct net_device *netdev) { struct lio *lio = GET_LIO(netdev); struct octeon_device *oct = lio->oct_dev; - struct octeon_device_priv *oct_priv = - (struct octeon_device_priv *)oct->priv; + struct octeon_device_priv *oct_priv = oct->priv; struct napi_struct *napi, *n; int ret = 0; diff --git a/drivers/net/ethernet/chelsio/cxgb3/sge.c b/drivers/net/ethernet/chelsio/cxgb3/sge.c index efa7f401529e..2e9a74fe0970 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb3/sge.c @@ -2184,9 +2184,8 @@ static void lro_add_page(struct adapter *adap, struct sge_qset *qs, len -= offset; rx_frag += nr_frags; - __skb_frag_set_page(rx_frag, sd->pg_chunk.page); - skb_frag_off_set(rx_frag, sd->pg_chunk.offset + offset); - skb_frag_size_set(rx_frag, len); + skb_frag_fill_page_desc(rx_frag, sd->pg_chunk.page, + sd->pg_chunk.offset + offset, len); skb->len += len; skb->data_len += len; diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 7e408bcc88de..3164ed205cf7 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -2343,11 +2343,10 @@ static void skb_fill_rx_data(struct be_rx_obj *rxo, struct sk_buff *skb, hdr_len = ETH_HLEN; memcpy(skb->data, start, hdr_len); skb_shinfo(skb)->nr_frags = 1; - skb_frag_set_page(skb, 0, page_info->page); - skb_frag_off_set(&skb_shinfo(skb)->frags[0], - page_info->page_offset + hdr_len); - skb_frag_size_set(&skb_shinfo(skb)->frags[0], - curr_frag_len - hdr_len); + skb_frag_fill_page_desc(&skb_shinfo(skb)->frags[0], + page_info->page, + page_info->page_offset + hdr_len, + curr_frag_len - hdr_len); skb->data_len = curr_frag_len - hdr_len; skb->truesize += rx_frag_size; skb->tail += hdr_len; @@ -2369,16 +2368,17 @@ static void skb_fill_rx_data(struct be_rx_obj *rxo, struct sk_buff *skb, if (page_info->page_offset == 0) { /* Fresh page */ j++; - skb_frag_set_page(skb, j, page_info->page); - skb_frag_off_set(&skb_shinfo(skb)->frags[j], - page_info->page_offset); - skb_frag_size_set(&skb_shinfo(skb)->frags[j], 0); + skb_frag_fill_page_desc(&skb_shinfo(skb)->frags[j], + page_info->page, + page_info->page_offset, + curr_frag_len); skb_shinfo(skb)->nr_frags++; } else { put_page(page_info->page); + skb_frag_size_add(&skb_shinfo(skb)->frags[j], + curr_frag_len); } - skb_frag_size_add(&skb_shinfo(skb)->frags[j], curr_frag_len); skb->len += curr_frag_len; skb->data_len += curr_frag_len; skb->truesize += rx_frag_size; @@ -2451,14 +2451,16 @@ static void be_rx_compl_process_gro(struct be_rx_obj *rxo, if (i == 0 || page_info->page_offset == 0) { /* First frag or Fresh page */ j++; - skb_frag_set_page(skb, j, page_info->page); - skb_frag_off_set(&skb_shinfo(skb)->frags[j], - page_info->page_offset); - skb_frag_size_set(&skb_shinfo(skb)->frags[j], 0); + skb_frag_fill_page_desc(&skb_shinfo(skb)->frags[j], + page_info->page, + page_info->page_offset, + curr_frag_len); } else { put_page(page_info->page); + skb_frag_size_add(&skb_shinfo(skb)->frags[j], + curr_frag_len); } - skb_frag_size_add(&skb_shinfo(skb)->frags[j], curr_frag_len); + skb->truesize += rx_frag_size; remaining -= curr_frag_len; memset(page_info, 0, sizeof(*page_info)); diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c index 3c4fa26f0f9b..63854294ac33 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc.c +++ b/drivers/net/ethernet/freescale/enetc/enetc.c @@ -1445,9 +1445,8 @@ static void enetc_add_rx_buff_to_xdp(struct enetc_bdr *rx_ring, int i, xdp_buff_set_frag_pfmemalloc(xdp_buff); frag = &shinfo->frags[shinfo->nr_frags]; - skb_frag_off_set(frag, rx_swbd->page_offset); - skb_frag_size_set(frag, size); - __skb_frag_set_page(frag, rx_swbd->page); + skb_frag_fill_page_desc(frag, rx_swbd->page, rx_swbd->page_offset, + size); shinfo->nr_frags++; } diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 577d94821b3e..6d0b46c76924 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -3798,7 +3798,7 @@ static int fec_enet_txq_xmit_frame(struct fec_enet_private *fep, entries_free = fec_enet_get_free_txdesc_num(txq); if (entries_free < MAX_SKB_FRAGS + 1) { netdev_err(fep->netdev, "NOT enough BD for SG!\n"); - return NETDEV_TX_BUSY; + return -EBUSY; } /* Fill in a Tx ring entry */ @@ -3812,7 +3812,7 @@ static int fec_enet_txq_xmit_frame(struct fec_enet_private *fep, dma_addr = dma_map_single(&fep->pdev->dev, frame->data, frame->len, DMA_TO_DEVICE); if (dma_mapping_error(&fep->pdev->dev, dma_addr)) - return FEC_ENET_XDP_CONSUMED; + return -ENOMEM; status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST); if (fep->bufdesc_ex) @@ -3868,7 +3868,7 @@ static int fec_enet_xdp_xmit(struct net_device *dev, __netif_tx_lock(nq, cpu); for (i = 0; i < num_frames; i++) { - if (fec_enet_txq_xmit_frame(fep, txq, frames[i]) != 0) + if (fec_enet_txq_xmit_frame(fep, txq, frames[i]) < 0) break; sent_frames++; } diff --git a/drivers/net/ethernet/fungible/funeth/funeth_rx.c b/drivers/net/ethernet/fungible/funeth/funeth_rx.c index 29a6c2ede43a..7e2584895de3 100644 --- a/drivers/net/ethernet/fungible/funeth/funeth_rx.c +++ b/drivers/net/ethernet/fungible/funeth/funeth_rx.c @@ -323,9 +323,8 @@ static int fun_gather_pkt(struct funeth_rxq *q, unsigned int tot_len, if (ref_ok) ref_ok |= buf->node; - __skb_frag_set_page(frags, buf->page); - skb_frag_off_set(frags, q->buf_offset); - skb_frag_size_set(frags++, frag_len); + skb_frag_fill_page_desc(frags++, buf->page, q->buf_offset, + frag_len); tot_len -= frag_len; if (!tot_len) diff --git a/drivers/net/ethernet/i825xx/82596.c b/drivers/net/ethernet/i825xx/82596.c index 3ee89ae496d0..773d7aa29ef5 100644 --- a/drivers/net/ethernet/i825xx/82596.c +++ b/drivers/net/ethernet/i825xx/82596.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-1.0+ /* 82596.c: A generic 82596 ethernet driver for linux. */ /* Based on Apricot.c @@ -31,9 +32,7 @@ Driver skeleton Written 1993 by Donald Becker. Copyright 1993 United States Government as represented by the Director, - National Security Agency. This software may only be used and distributed - according to the terms of the GNU General Public License as modified by SRC, - incorporated herein by reference. + National Security Agency. The author may be reached as becker@scyld.com, or C/O Scyld Computing Corporation, 410 Severn Ave., Suite 210, Annapolis MD 21403 diff --git a/drivers/net/ethernet/i825xx/lasi_82596.c b/drivers/net/ethernet/i825xx/lasi_82596.c index 0af70094aba3..3e53e0c243ba 100644 --- a/drivers/net/ethernet/i825xx/lasi_82596.c +++ b/drivers/net/ethernet/i825xx/lasi_82596.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-1.0+ /* lasi_82596.c -- driver for the intel 82596 ethernet controller, as munged into HPPA boxen . @@ -59,9 +60,7 @@ Driver skeleton Written 1993 by Donald Becker. Copyright 1993 United States Government as represented by the Director, - National Security Agency. This software may only be used and distributed - according to the terms of the GNU General Public License as modified by SRC, - incorporated herein by reference. + National Security Agency. The author may be reached as becker@scyld.com, or C/O Scyld Computing Corporation, 410 Severn Ave., Suite 210, Annapolis MD 21403 diff --git a/drivers/net/ethernet/i825xx/lib82596.c b/drivers/net/ethernet/i825xx/lib82596.c index ca2fb303fcc6..67d248a7a6f4 100644 --- a/drivers/net/ethernet/i825xx/lib82596.c +++ b/drivers/net/ethernet/i825xx/lib82596.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-1.0+ /* lasi_82596.c -- driver for the intel 82596 ethernet controller, as munged into HPPA boxen . @@ -59,9 +60,7 @@ Driver skeleton Written 1993 by Donald Becker. Copyright 1993 United States Government as represented by the Director, - National Security Agency. This software may only be used and distributed - according to the terms of the GNU General Public License as modified by SRC, - incorporated herein by reference. + National Security Agency. The author may be reached as becker@scyld.com, or C/O Scyld Computing Corporation, 410 Severn Ave., Suite 210, Annapolis MD 21403 diff --git a/drivers/net/ethernet/i825xx/sun3_82586.c b/drivers/net/ethernet/i825xx/sun3_82586.c index 3909c6a0af89..5e27470c6b1e 100644 --- a/drivers/net/ethernet/i825xx/sun3_82586.c +++ b/drivers/net/ethernet/i825xx/sun3_82586.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Sun3 i82586 Ethernet driver * diff --git a/drivers/net/ethernet/i825xx/sun3_82586.h b/drivers/net/ethernet/i825xx/sun3_82586.h index d82eca563266..d8e249d704a7 100644 --- a/drivers/net/ethernet/i825xx/sun3_82586.h +++ b/drivers/net/ethernet/i825xx/sun3_82586.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Intel i82586 Ethernet definitions * diff --git a/drivers/net/ethernet/intel/ice/Makefile b/drivers/net/ethernet/intel/ice/Makefile index 5d89392f969b..817977e3039d 100644 --- a/drivers/net/ethernet/intel/ice/Makefile +++ b/drivers/net/ethernet/intel/ice/Makefile @@ -18,6 +18,7 @@ ice-y := ice_main.o \ ice_txrx_lib.o \ ice_txrx.o \ ice_fltr.o \ + ice_irq.o \ ice_pf_vsi_vlan_ops.o \ ice_vsi_vlan_ops.o \ ice_vsi_vlan_lib.o \ diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h index aa32111afd6e..d637032c8139 100644 --- a/drivers/net/ethernet/intel/ice/ice.h +++ b/drivers/net/ethernet/intel/ice/ice.h @@ -74,6 +74,7 @@ #include "ice_lag.h" #include "ice_vsi_vlan_ops.h" #include "ice_gnss.h" +#include "ice_irq.h" #define ICE_BAR0 0 #define ICE_REQ_DESC_MULTIPLE 32 @@ -103,11 +104,6 @@ #define ICE_Q_WAIT_RETRY_LIMIT 10 #define ICE_Q_WAIT_MAX_RETRY (5 * ICE_Q_WAIT_RETRY_LIMIT) #define ICE_MAX_LG_RSS_QS 256 -#define ICE_RES_VALID_BIT 0x8000 -#define ICE_RES_MISC_VEC_ID (ICE_RES_VALID_BIT - 1) -#define ICE_RES_RDMA_VEC_ID (ICE_RES_MISC_VEC_ID - 1) -/* All VF control VSIs share the same IRQ, so assign a unique ID for them */ -#define ICE_RES_VF_CTRL_VEC_ID (ICE_RES_RDMA_VEC_ID - 1) #define ICE_INVAL_Q_INDEX 0xffff #define ICE_MAX_RXQS_PER_TC 256 /* Used when setting VSI context per TC Rx queues */ @@ -245,12 +241,6 @@ struct ice_tc_cfg { struct ice_tc_info tc_info[ICE_MAX_TRAFFIC_CLASS]; }; -struct ice_res_tracker { - u16 num_entries; - u16 end; - u16 list[]; -}; - struct ice_qs_cfg { struct mutex *qs_mutex; /* will be assigned to &pf->avail_q_mutex */ unsigned long *pf_map; @@ -348,7 +338,9 @@ struct ice_vsi { u32 rx_buf_failed; u32 rx_page_failed; u16 num_q_vectors; - u16 base_vector; /* IRQ base for OS reserved vectors */ + /* tell if only dynamic irq allocation is allowed */ + bool irq_dyn_alloc; + enum ice_vsi_type type; u16 vsi_num; /* HW (absolute) index of this VSI */ u16 idx; /* software index in pf->vsi[] */ @@ -479,6 +471,7 @@ struct ice_q_vector { char name[ICE_INT_NAME_STR_LEN]; u16 total_events; /* net_dim(): number of interrupts processed */ + struct msi_map irq; } ____cacheline_internodealigned_in_smp; enum ice_pf_flags { @@ -539,7 +532,7 @@ struct ice_pf { /* OS reserved IRQ details */ struct msix_entry *msix_entries; - struct ice_res_tracker *irq_tracker; + struct ice_irq_tracker irq_tracker; /* First MSIX vector used by SR-IOV VFs. Calculated by subtracting the * number of MSIX vectors needed for all SR-IOV VFs from the number of * MSIX vectors allowed on this PF. @@ -583,8 +576,7 @@ struct ice_pf { u32 hw_csum_rx_error; u32 oicr_err_reg; - u16 oicr_idx; /* Other interrupt cause MSIX vector index */ - u16 num_avail_sw_msix; /* remaining MSIX SW vectors left unclaimed */ + struct msi_map oicr_irq; /* Other interrupt cause MSIX vector */ u16 max_pf_txqs; /* Total Tx queues PF wide */ u16 max_pf_rxqs; /* Total Rx queues PF wide */ u16 num_lan_msix; /* Total MSIX vectors for base driver */ @@ -670,7 +662,7 @@ ice_irq_dynamic_ena(struct ice_hw *hw, struct ice_vsi *vsi, struct ice_q_vector *q_vector) { u32 vector = (vsi && q_vector) ? q_vector->reg_idx : - ((struct ice_pf *)hw->back)->oicr_idx; + ((struct ice_pf *)hw->back)->oicr_irq.index; int itr = ICE_ITR_NONE; u32 val; diff --git a/drivers/net/ethernet/intel/ice/ice_arfs.c b/drivers/net/ethernet/intel/ice/ice_arfs.c index fba178e07600..cca0e753f38f 100644 --- a/drivers/net/ethernet/intel/ice/ice_arfs.c +++ b/drivers/net/ethernet/intel/ice/ice_arfs.c @@ -596,7 +596,7 @@ int ice_set_cpu_rx_rmap(struct ice_vsi *vsi) { struct net_device *netdev; struct ice_pf *pf; - int base_idx, i; + int i; if (!vsi || vsi->type != ICE_VSI_PF) return 0; @@ -613,10 +613,9 @@ int ice_set_cpu_rx_rmap(struct ice_vsi *vsi) if (unlikely(!netdev->rx_cpu_rmap)) return -EINVAL; - base_idx = vsi->base_vector; ice_for_each_q_vector(vsi, i) if (irq_cpu_rmap_add(netdev->rx_cpu_rmap, - pf->msix_entries[base_idx + i].vector)) { + vsi->q_vectors[i]->irq.virq)) { ice_free_cpu_rx_rmap(vsi); return -EINVAL; } diff --git a/drivers/net/ethernet/intel/ice/ice_base.c b/drivers/net/ethernet/intel/ice/ice_base.c index 1911d644dfa8..4a12316f7b46 100644 --- a/drivers/net/ethernet/intel/ice/ice_base.c +++ b/drivers/net/ethernet/intel/ice/ice_base.c @@ -103,10 +103,10 @@ static int ice_vsi_alloc_q_vector(struct ice_vsi *vsi, u16 v_idx) { struct ice_pf *pf = vsi->back; struct ice_q_vector *q_vector; + int err; /* allocate q_vector */ - q_vector = devm_kzalloc(ice_pf_to_dev(pf), sizeof(*q_vector), - GFP_KERNEL); + q_vector = kzalloc(sizeof(*q_vector), GFP_KERNEL); if (!q_vector) return -ENOMEM; @@ -118,9 +118,34 @@ static int ice_vsi_alloc_q_vector(struct ice_vsi *vsi, u16 v_idx) q_vector->rx.itr_mode = ITR_DYNAMIC; q_vector->tx.type = ICE_TX_CONTAINER; q_vector->rx.type = ICE_RX_CONTAINER; + q_vector->irq.index = -ENOENT; - if (vsi->type == ICE_VSI_VF) + if (vsi->type == ICE_VSI_VF) { + q_vector->reg_idx = ice_calc_vf_reg_idx(vsi->vf, q_vector); goto out; + } else if (vsi->type == ICE_VSI_CTRL && vsi->vf) { + struct ice_vsi *ctrl_vsi = ice_get_vf_ctrl_vsi(pf, vsi); + + if (ctrl_vsi) { + if (unlikely(!ctrl_vsi->q_vectors)) { + err = -ENOENT; + goto err_free_q_vector; + } + + q_vector->irq = ctrl_vsi->q_vectors[0]->irq; + goto skip_alloc; + } + } + + q_vector->irq = ice_alloc_irq(pf, vsi->irq_dyn_alloc); + if (q_vector->irq.index < 0) { + err = -ENOMEM; + goto err_free_q_vector; + } + +skip_alloc: + q_vector->reg_idx = q_vector->irq.index; + /* only set affinity_mask if the CPU is online */ if (cpu_online(v_idx)) cpumask_set_cpu(v_idx, &q_vector->affinity_mask); @@ -137,6 +162,11 @@ out: vsi->q_vectors[v_idx] = q_vector; return 0; + +err_free_q_vector: + kfree(q_vector); + + return err; } /** @@ -168,7 +198,19 @@ static void ice_free_q_vector(struct ice_vsi *vsi, int v_idx) if (vsi->netdev) netif_napi_del(&q_vector->napi); - devm_kfree(dev, q_vector); + /* release MSIX interrupt if q_vector had interrupt allocated */ + if (q_vector->irq.index < 0) + goto free_q_vector; + + /* only free last VF ctrl vsi interrupt */ + if (vsi->type == ICE_VSI_CTRL && vsi->vf && + ice_get_vf_ctrl_vsi(pf, vsi)) + goto free_q_vector; + + ice_free_irq(pf, q_vector->irq); + +free_q_vector: + kfree(q_vector); vsi->q_vectors[v_idx] = NULL; } diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool.c b/drivers/net/ethernet/intel/ice/ice_ethtool.c index f86e814354a3..8407c7175cf6 100644 --- a/drivers/net/ethernet/intel/ice/ice_ethtool.c +++ b/drivers/net/ethernet/intel/ice/ice_ethtool.c @@ -956,7 +956,7 @@ static u64 ice_intr_test(struct net_device *netdev) netdev_info(netdev, "interrupt test\n"); - wr32(&pf->hw, GLINT_DYN_CTL(pf->oicr_idx), + wr32(&pf->hw, GLINT_DYN_CTL(pf->oicr_irq.index), GLINT_DYN_CTL_SW_ITR_INDX_M | GLINT_DYN_CTL_INTENA_MSK_M | GLINT_DYN_CTL_SWINT_TRIG_M); diff --git a/drivers/net/ethernet/intel/ice/ice_idc.c b/drivers/net/ethernet/intel/ice/ice_idc.c index e6bc2285071e..145b27f2a4ce 100644 --- a/drivers/net/ethernet/intel/ice/ice_idc.c +++ b/drivers/net/ethernet/intel/ice/ice_idc.c @@ -229,20 +229,34 @@ void ice_get_qos_params(struct ice_pf *pf, struct iidc_qos_params *qos) EXPORT_SYMBOL_GPL(ice_get_qos_params); /** - * ice_reserve_rdma_qvector - Reserve vector resources for RDMA driver + * ice_alloc_rdma_qvectors - Allocate vector resources for RDMA driver * @pf: board private structure to initialize */ -static int ice_reserve_rdma_qvector(struct ice_pf *pf) +static int ice_alloc_rdma_qvectors(struct ice_pf *pf) { if (ice_is_rdma_ena(pf)) { - int index; - - index = ice_get_res(pf, pf->irq_tracker, pf->num_rdma_msix, - ICE_RES_RDMA_VEC_ID); - if (index < 0) - return index; - pf->num_avail_sw_msix -= pf->num_rdma_msix; - pf->rdma_base_vector = (u16)index; + int i; + + pf->msix_entries = kcalloc(pf->num_rdma_msix, + sizeof(*pf->msix_entries), + GFP_KERNEL); + if (!pf->msix_entries) + return -ENOMEM; + + /* RDMA is the only user of pf->msix_entries array */ + pf->rdma_base_vector = 0; + + for (i = 0; i < pf->num_rdma_msix; i++) { + struct msix_entry *entry = &pf->msix_entries[i]; + struct msi_map map; + + map = ice_alloc_irq(pf, false); + if (map.index < 0) + break; + + entry->entry = map.index; + entry->vector = map.virq; + } } return 0; } @@ -253,9 +267,21 @@ static int ice_reserve_rdma_qvector(struct ice_pf *pf) */ static void ice_free_rdma_qvector(struct ice_pf *pf) { - pf->num_avail_sw_msix -= pf->num_rdma_msix; - ice_free_res(pf->irq_tracker, pf->rdma_base_vector, - ICE_RES_RDMA_VEC_ID); + int i; + + if (!pf->msix_entries) + return; + + for (i = 0; i < pf->num_rdma_msix; i++) { + struct msi_map map; + + map.index = pf->msix_entries[i].entry; + map.virq = pf->msix_entries[i].vector; + ice_free_irq(pf, map); + } + + kfree(pf->msix_entries); + pf->msix_entries = NULL; } /** @@ -357,7 +383,7 @@ int ice_init_rdma(struct ice_pf *pf) } /* Reserve vector resources */ - ret = ice_reserve_rdma_qvector(pf); + ret = ice_alloc_rdma_qvectors(pf); if (ret < 0) { dev_err(dev, "failed to reserve vectors for RDMA\n"); goto err_reserve_rdma_qvector; diff --git a/drivers/net/ethernet/intel/ice/ice_irq.c b/drivers/net/ethernet/intel/ice/ice_irq.c new file mode 100644 index 000000000000..ad82ff7d1995 --- /dev/null +++ b/drivers/net/ethernet/intel/ice/ice_irq.c @@ -0,0 +1,378 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023, Intel Corporation. */ + +#include "ice.h" +#include "ice_lib.h" +#include "ice_irq.h" + +/** + * ice_init_irq_tracker - initialize interrupt tracker + * @pf: board private structure + * @max_vectors: maximum number of vectors that tracker can hold + * @num_static: number of preallocated interrupts + */ +static void +ice_init_irq_tracker(struct ice_pf *pf, unsigned int max_vectors, + unsigned int num_static) +{ + pf->irq_tracker.num_entries = max_vectors; + pf->irq_tracker.num_static = num_static; + xa_init_flags(&pf->irq_tracker.entries, XA_FLAGS_ALLOC); +} + +/** + * ice_deinit_irq_tracker - free xarray tracker + * @pf: board private structure + */ +static void ice_deinit_irq_tracker(struct ice_pf *pf) +{ + xa_destroy(&pf->irq_tracker.entries); +} + +/** + * ice_free_irq_res - free a block of resources + * @pf: board private structure + * @index: starting index previously returned by ice_get_res + */ +static void ice_free_irq_res(struct ice_pf *pf, u16 index) +{ + struct ice_irq_entry *entry; + + entry = xa_erase(&pf->irq_tracker.entries, index); + kfree(entry); +} + +/** + * ice_get_irq_res - get an interrupt resource + * @pf: board private structure + * @dyn_only: force entry to be dynamically allocated + * + * Allocate new irq entry in the free slot of the tracker. Since xarray + * is used, always allocate new entry at the lowest possible index. Set + * proper allocation limit for maximum tracker entries. + * + * Returns allocated irq entry or NULL on failure. + */ +static struct ice_irq_entry *ice_get_irq_res(struct ice_pf *pf, bool dyn_only) +{ + struct xa_limit limit = { .max = pf->irq_tracker.num_entries, + .min = 0 }; + unsigned int num_static = pf->irq_tracker.num_static; + struct ice_irq_entry *entry; + unsigned int index; + int ret; + + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return NULL; + + /* skip preallocated entries if the caller says so */ + if (dyn_only) + limit.min = num_static; + + ret = xa_alloc(&pf->irq_tracker.entries, &index, entry, limit, + GFP_KERNEL); + + if (ret) { + kfree(entry); + entry = NULL; + } else { + entry->index = index; + entry->dynamic = index >= num_static; + } + + return entry; +} + +/** + * ice_reduce_msix_usage - Reduce usage of MSI-X vectors + * @pf: board private structure + * @v_remain: number of remaining MSI-X vectors to be distributed + * + * Reduce the usage of MSI-X vectors when entire request cannot be fulfilled. + * pf->num_lan_msix and pf->num_rdma_msix values are set based on number of + * remaining vectors. + */ +static void ice_reduce_msix_usage(struct ice_pf *pf, int v_remain) +{ + int v_rdma; + + if (!ice_is_rdma_ena(pf)) { + pf->num_lan_msix = v_remain; + return; + } + + /* RDMA needs at least 1 interrupt in addition to AEQ MSIX */ + v_rdma = ICE_RDMA_NUM_AEQ_MSIX + 1; + + if (v_remain < ICE_MIN_LAN_TXRX_MSIX + ICE_MIN_RDMA_MSIX) { + dev_warn(ice_pf_to_dev(pf), "Not enough MSI-X vectors to support RDMA.\n"); + clear_bit(ICE_FLAG_RDMA_ENA, pf->flags); + + pf->num_rdma_msix = 0; + pf->num_lan_msix = ICE_MIN_LAN_TXRX_MSIX; + } else if ((v_remain < ICE_MIN_LAN_TXRX_MSIX + v_rdma) || + (v_remain - v_rdma < v_rdma)) { + /* Support minimum RDMA and give remaining vectors to LAN MSIX + */ + pf->num_rdma_msix = ICE_MIN_RDMA_MSIX; + pf->num_lan_msix = v_remain - ICE_MIN_RDMA_MSIX; + } else { + /* Split remaining MSIX with RDMA after accounting for AEQ MSIX + */ + pf->num_rdma_msix = (v_remain - ICE_RDMA_NUM_AEQ_MSIX) / 2 + + ICE_RDMA_NUM_AEQ_MSIX; + pf->num_lan_msix = v_remain - pf->num_rdma_msix; + } +} + +/** + * ice_ena_msix_range - Request a range of MSIX vectors from the OS + * @pf: board private structure + * + * Compute the number of MSIX vectors wanted and request from the OS. Adjust + * device usage if there are not enough vectors. Return the number of vectors + * reserved or negative on failure. + */ +static int ice_ena_msix_range(struct ice_pf *pf) +{ + int num_cpus, hw_num_msix, v_other, v_wanted, v_actual; + struct device *dev = ice_pf_to_dev(pf); + int err; + + hw_num_msix = pf->hw.func_caps.common_cap.num_msix_vectors; + num_cpus = num_online_cpus(); + + /* LAN miscellaneous handler */ + v_other = ICE_MIN_LAN_OICR_MSIX; + + /* Flow Director */ + if (test_bit(ICE_FLAG_FD_ENA, pf->flags)) + v_other += ICE_FDIR_MSIX; + + /* switchdev */ + v_other += ICE_ESWITCH_MSIX; + + v_wanted = v_other; + + /* LAN traffic */ + pf->num_lan_msix = num_cpus; + v_wanted += pf->num_lan_msix; + + /* RDMA auxiliary driver */ + if (ice_is_rdma_ena(pf)) { + pf->num_rdma_msix = num_cpus + ICE_RDMA_NUM_AEQ_MSIX; + v_wanted += pf->num_rdma_msix; + } + + if (v_wanted > hw_num_msix) { + int v_remain; + + dev_warn(dev, "not enough device MSI-X vectors. wanted = %d, available = %d\n", + v_wanted, hw_num_msix); + + if (hw_num_msix < ICE_MIN_MSIX) { + err = -ERANGE; + goto exit_err; + } + + v_remain = hw_num_msix - v_other; + if (v_remain < ICE_MIN_LAN_TXRX_MSIX) { + v_other = ICE_MIN_MSIX - ICE_MIN_LAN_TXRX_MSIX; + v_remain = ICE_MIN_LAN_TXRX_MSIX; + } + + ice_reduce_msix_usage(pf, v_remain); + v_wanted = pf->num_lan_msix + pf->num_rdma_msix + v_other; + + dev_notice(dev, "Reducing request to %d MSI-X vectors for LAN traffic.\n", + pf->num_lan_msix); + if (ice_is_rdma_ena(pf)) + dev_notice(dev, "Reducing request to %d MSI-X vectors for RDMA.\n", + pf->num_rdma_msix); + } + + /* actually reserve the vectors */ + v_actual = pci_alloc_irq_vectors(pf->pdev, ICE_MIN_MSIX, v_wanted, + PCI_IRQ_MSIX); + if (v_actual < 0) { + dev_err(dev, "unable to reserve MSI-X vectors\n"); + err = v_actual; + goto exit_err; + } + + if (v_actual < v_wanted) { + dev_warn(dev, "not enough OS MSI-X vectors. requested = %d, obtained = %d\n", + v_wanted, v_actual); + + if (v_actual < ICE_MIN_MSIX) { + /* error if we can't get minimum vectors */ + pci_free_irq_vectors(pf->pdev); + err = -ERANGE; + goto exit_err; + } else { + int v_remain = v_actual - v_other; + + if (v_remain < ICE_MIN_LAN_TXRX_MSIX) + v_remain = ICE_MIN_LAN_TXRX_MSIX; + + ice_reduce_msix_usage(pf, v_remain); + + dev_notice(dev, "Enabled %d MSI-X vectors for LAN traffic.\n", + pf->num_lan_msix); + + if (ice_is_rdma_ena(pf)) + dev_notice(dev, "Enabled %d MSI-X vectors for RDMA.\n", + pf->num_rdma_msix); + } + } + + return v_actual; + +exit_err: + pf->num_rdma_msix = 0; + pf->num_lan_msix = 0; + return err; +} + +/** + * ice_clear_interrupt_scheme - Undo things done by ice_init_interrupt_scheme + * @pf: board private structure + */ +void ice_clear_interrupt_scheme(struct ice_pf *pf) +{ + pci_free_irq_vectors(pf->pdev); + ice_deinit_irq_tracker(pf); +} + +/** + * ice_init_interrupt_scheme - Determine proper interrupt scheme + * @pf: board private structure to initialize + */ +int ice_init_interrupt_scheme(struct ice_pf *pf) +{ + int total_vectors = pf->hw.func_caps.common_cap.num_msix_vectors; + int vectors, max_vectors; + + vectors = ice_ena_msix_range(pf); + + if (vectors < 0) + return -ENOMEM; + + if (pci_msix_can_alloc_dyn(pf->pdev)) + max_vectors = total_vectors; + else + max_vectors = vectors; + + ice_init_irq_tracker(pf, max_vectors, vectors); + + return 0; +} + +/** + * ice_alloc_irq - Allocate new interrupt vector + * @pf: board private structure + * @dyn_only: force dynamic allocation of the interrupt + * + * Allocate new interrupt vector for a given owner id. + * return struct msi_map with interrupt details and track + * allocated interrupt appropriately. + * + * This function reserves new irq entry from the irq_tracker. + * if according to the tracker information all interrupts that + * were allocated with ice_pci_alloc_irq_vectors are already used + * and dynamically allocated interrupts are supported then new + * interrupt will be allocated with pci_msix_alloc_irq_at. + * + * Some callers may only support dynamically allocated interrupts. + * This is indicated with dyn_only flag. + * + * On failure, return map with negative .index. The caller + * is expected to check returned map index. + * + */ +struct msi_map ice_alloc_irq(struct ice_pf *pf, bool dyn_only) +{ + int sriov_base_vector = pf->sriov_base_vector; + struct msi_map map = { .index = -ENOENT }; + struct device *dev = ice_pf_to_dev(pf); + struct ice_irq_entry *entry; + + entry = ice_get_irq_res(pf, dyn_only); + if (!entry) + return map; + + /* fail if we're about to violate SRIOV vectors space */ + if (sriov_base_vector && entry->index >= sriov_base_vector) + goto exit_free_res; + + if (pci_msix_can_alloc_dyn(pf->pdev) && entry->dynamic) { + map = pci_msix_alloc_irq_at(pf->pdev, entry->index, NULL); + if (map.index < 0) + goto exit_free_res; + dev_dbg(dev, "allocated new irq at index %d\n", map.index); + } else { + map.index = entry->index; + map.virq = pci_irq_vector(pf->pdev, map.index); + } + + return map; + +exit_free_res: + dev_err(dev, "Could not allocate irq at idx %d\n", entry->index); + ice_free_irq_res(pf, entry->index); + return map; +} + +/** + * ice_free_irq - Free interrupt vector + * @pf: board private structure + * @map: map with interrupt details + * + * Remove allocated interrupt from the interrupt tracker. If interrupt was + * allocated dynamically, free respective interrupt vector. + */ +void ice_free_irq(struct ice_pf *pf, struct msi_map map) +{ + struct ice_irq_entry *entry; + + entry = xa_load(&pf->irq_tracker.entries, map.index); + + if (!entry) { + dev_err(ice_pf_to_dev(pf), "Failed to get MSIX interrupt entry at index %d", + map.index); + return; + } + + dev_dbg(ice_pf_to_dev(pf), "Free irq at index %d\n", map.index); + + if (entry->dynamic) + pci_msix_free_irq(pf->pdev, map); + + ice_free_irq_res(pf, map.index); +} + +/** + * ice_get_max_used_msix_vector - Get the max used interrupt vector + * @pf: board private structure + * + * Return index of maximum used interrupt vectors with respect to the + * beginning of the MSIX table. Take into account that some interrupts + * may have been dynamically allocated after MSIX was initially enabled. + */ +int ice_get_max_used_msix_vector(struct ice_pf *pf) +{ + unsigned long start, index, max_idx; + void *entry; + + /* Treat all preallocated interrupts as used */ + start = pf->irq_tracker.num_static; + max_idx = start - 1; + + xa_for_each_start(&pf->irq_tracker.entries, index, entry, start) { + if (index > max_idx) + max_idx = index; + } + + return max_idx; +} diff --git a/drivers/net/ethernet/intel/ice/ice_irq.h b/drivers/net/ethernet/intel/ice/ice_irq.h new file mode 100644 index 000000000000..f35efc08575e --- /dev/null +++ b/drivers/net/ethernet/intel/ice/ice_irq.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023, Intel Corporation. */ + +#ifndef _ICE_IRQ_H_ +#define _ICE_IRQ_H_ + +struct ice_irq_entry { + unsigned int index; + bool dynamic; /* allocation type flag */ +}; + +struct ice_irq_tracker { + struct xarray entries; + u16 num_entries; /* total vectors available */ + u16 num_static; /* preallocated entries */ +}; + +int ice_init_interrupt_scheme(struct ice_pf *pf); +void ice_clear_interrupt_scheme(struct ice_pf *pf); + +struct msi_map ice_alloc_irq(struct ice_pf *pf, bool dyn_only); +void ice_free_irq(struct ice_pf *pf, struct msi_map map); +int ice_get_max_used_msix_vector(struct ice_pf *pf); + +#endif diff --git a/drivers/net/ethernet/intel/ice/ice_lib.c b/drivers/net/ethernet/intel/ice/ice_lib.c index 11ae0e41f518..d9731476cd7f 100644 --- a/drivers/net/ethernet/intel/ice/ice_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_lib.c @@ -1371,190 +1371,6 @@ out: } /** - * ice_free_res - free a block of resources - * @res: pointer to the resource - * @index: starting index previously returned by ice_get_res - * @id: identifier to track owner - * - * Returns number of resources freed - */ -int ice_free_res(struct ice_res_tracker *res, u16 index, u16 id) -{ - int count = 0; - int i; - - if (!res || index >= res->end) - return -EINVAL; - - id |= ICE_RES_VALID_BIT; - for (i = index; i < res->end && res->list[i] == id; i++) { - res->list[i] = 0; - count++; - } - - return count; -} - -/** - * ice_search_res - Search the tracker for a block of resources - * @res: pointer to the resource - * @needed: size of the block needed - * @id: identifier to track owner - * - * Returns the base item index of the block, or -ENOMEM for error - */ -static int ice_search_res(struct ice_res_tracker *res, u16 needed, u16 id) -{ - u16 start = 0, end = 0; - - if (needed > res->end) - return -ENOMEM; - - id |= ICE_RES_VALID_BIT; - - do { - /* skip already allocated entries */ - if (res->list[end++] & ICE_RES_VALID_BIT) { - start = end; - if ((start + needed) > res->end) - break; - } - - if (end == (start + needed)) { - int i = start; - - /* there was enough, so assign it to the requestor */ - while (i != end) - res->list[i++] = id; - - return start; - } - } while (end < res->end); - - return -ENOMEM; -} - -/** - * ice_get_free_res_count - Get free count from a resource tracker - * @res: Resource tracker instance - */ -static u16 ice_get_free_res_count(struct ice_res_tracker *res) -{ - u16 i, count = 0; - - for (i = 0; i < res->end; i++) - if (!(res->list[i] & ICE_RES_VALID_BIT)) - count++; - - return count; -} - -/** - * ice_get_res - get a block of resources - * @pf: board private structure - * @res: pointer to the resource - * @needed: size of the block needed - * @id: identifier to track owner - * - * Returns the base item index of the block, or negative for error - */ -int -ice_get_res(struct ice_pf *pf, struct ice_res_tracker *res, u16 needed, u16 id) -{ - if (!res || !pf) - return -EINVAL; - - if (!needed || needed > res->num_entries || id >= ICE_RES_VALID_BIT) { - dev_err(ice_pf_to_dev(pf), "param err: needed=%d, num_entries = %d id=0x%04x\n", - needed, res->num_entries, id); - return -EINVAL; - } - - return ice_search_res(res, needed, id); -} - -/** - * ice_get_vf_ctrl_res - Get VF control VSI resource - * @pf: pointer to the PF structure - * @vsi: the VSI to allocate a resource for - * - * Look up whether another VF has already allocated the control VSI resource. - * If so, re-use this resource so that we share it among all VFs. - * - * Otherwise, allocate the resource and return it. - */ -static int ice_get_vf_ctrl_res(struct ice_pf *pf, struct ice_vsi *vsi) -{ - struct ice_vf *vf; - unsigned int bkt; - int base; - - rcu_read_lock(); - ice_for_each_vf_rcu(pf, bkt, vf) { - if (vf != vsi->vf && vf->ctrl_vsi_idx != ICE_NO_VSI) { - base = pf->vsi[vf->ctrl_vsi_idx]->base_vector; - rcu_read_unlock(); - return base; - } - } - rcu_read_unlock(); - - return ice_get_res(pf, pf->irq_tracker, vsi->num_q_vectors, - ICE_RES_VF_CTRL_VEC_ID); -} - -/** - * ice_vsi_setup_vector_base - Set up the base vector for the given VSI - * @vsi: ptr to the VSI - * - * This should only be called after ice_vsi_alloc_def() which allocates the - * corresponding SW VSI structure and initializes num_queue_pairs for the - * newly allocated VSI. - * - * Returns 0 on success or negative on failure - */ -static int ice_vsi_setup_vector_base(struct ice_vsi *vsi) -{ - struct ice_pf *pf = vsi->back; - struct device *dev; - u16 num_q_vectors; - int base; - - dev = ice_pf_to_dev(pf); - /* SRIOV doesn't grab irq_tracker entries for each VSI */ - if (vsi->type == ICE_VSI_VF) - return 0; - if (vsi->type == ICE_VSI_CHNL) - return 0; - - if (vsi->base_vector) { - dev_dbg(dev, "VSI %d has non-zero base vector %d\n", - vsi->vsi_num, vsi->base_vector); - return -EEXIST; - } - - num_q_vectors = vsi->num_q_vectors; - /* reserve slots from OS requested IRQs */ - if (vsi->type == ICE_VSI_CTRL && vsi->vf) { - base = ice_get_vf_ctrl_res(pf, vsi); - } else { - base = ice_get_res(pf, pf->irq_tracker, num_q_vectors, - vsi->idx); - } - - if (base < 0) { - dev_err(dev, "%d MSI-X interrupts available. %s %d failed to get %d MSI-X vectors\n", - ice_get_free_res_count(pf->irq_tracker), - ice_vsi_type_str(vsi->type), vsi->idx, num_q_vectors); - return -ENOENT; - } - vsi->base_vector = (u16)base; - pf->num_avail_sw_msix -= num_q_vectors; - - return 0; -} - -/** * ice_vsi_clear_rings - Deallocates the Tx and Rx rings for VSI * @vsi: the VSI having rings deallocated */ @@ -2410,50 +2226,6 @@ static void ice_vsi_set_tc_cfg(struct ice_vsi *vsi) } /** - * ice_vsi_set_q_vectors_reg_idx - set the HW register index for all q_vectors - * @vsi: VSI to set the q_vectors register index on - */ -static int -ice_vsi_set_q_vectors_reg_idx(struct ice_vsi *vsi) -{ - u16 i; - - if (!vsi || !vsi->q_vectors) - return -EINVAL; - - ice_for_each_q_vector(vsi, i) { - struct ice_q_vector *q_vector = vsi->q_vectors[i]; - - if (!q_vector) { - dev_err(ice_pf_to_dev(vsi->back), "Failed to set reg_idx on q_vector %d VSI %d\n", - i, vsi->vsi_num); - goto clear_reg_idx; - } - - if (vsi->type == ICE_VSI_VF) { - struct ice_vf *vf = vsi->vf; - - q_vector->reg_idx = ice_calc_vf_reg_idx(vf, q_vector); - } else { - q_vector->reg_idx = - q_vector->v_idx + vsi->base_vector; - } - } - - return 0; - -clear_reg_idx: - ice_for_each_q_vector(vsi, i) { - struct ice_q_vector *q_vector = vsi->q_vectors[i]; - - if (q_vector) - q_vector->reg_idx = 0; - } - - return -EINVAL; -} - -/** * ice_cfg_sw_lldp - Config switch rules for LLDP packet handling * @vsi: the VSI being configured * @tx: bool to determine Tx or Rx rule @@ -2611,37 +2383,6 @@ static void ice_set_agg_vsi(struct ice_vsi *vsi) vsi->agg_node->num_vsis); } -/** - * ice_free_vf_ctrl_res - Free the VF control VSI resource - * @pf: pointer to PF structure - * @vsi: the VSI to free resources for - * - * Check if the VF control VSI resource is still in use. If no VF is using it - * any more, release the VSI resource. Otherwise, leave it to be cleaned up - * once no other VF uses it. - */ -static void ice_free_vf_ctrl_res(struct ice_pf *pf, struct ice_vsi *vsi) -{ - struct ice_vf *vf; - unsigned int bkt; - - rcu_read_lock(); - ice_for_each_vf_rcu(pf, bkt, vf) { - if (vf != vsi->vf && vf->ctrl_vsi_idx != ICE_NO_VSI) { - rcu_read_unlock(); - return; - } - } - rcu_read_unlock(); - - /* No other VFs left that have control VSI. It is now safe to reclaim - * SW interrupts back to the common pool. - */ - ice_free_res(pf->irq_tracker, vsi->base_vector, - ICE_RES_VF_CTRL_VEC_ID); - pf->num_avail_sw_msix += vsi->num_q_vectors; -} - static int ice_vsi_cfg_tc_lan(struct ice_pf *pf, struct ice_vsi *vsi) { u16 max_txqs[ICE_MAX_TRAFFIC_CLASS] = { 0 }; @@ -2728,14 +2469,6 @@ ice_vsi_cfg_def(struct ice_vsi *vsi, struct ice_vsi_cfg_params *params) if (ret) goto unroll_vsi_init; - ret = ice_vsi_setup_vector_base(vsi); - if (ret) - goto unroll_alloc_q_vector; - - ret = ice_vsi_set_q_vectors_reg_idx(vsi); - if (ret) - goto unroll_vector_base; - ret = ice_vsi_alloc_rings(vsi); if (ret) goto unroll_vector_base; @@ -2788,10 +2521,6 @@ ice_vsi_cfg_def(struct ice_vsi *vsi, struct ice_vsi_cfg_params *params) if (ret) goto unroll_alloc_q_vector; - ret = ice_vsi_set_q_vectors_reg_idx(vsi); - if (ret) - goto unroll_vector_base; - ret = ice_vsi_alloc_ring_stats(vsi); if (ret) goto unroll_vector_base; @@ -2827,8 +2556,6 @@ ice_vsi_cfg_def(struct ice_vsi *vsi, struct ice_vsi_cfg_params *params) unroll_vector_base: /* reclaim SW interrupts back to the common pool */ - ice_free_res(pf->irq_tracker, vsi->base_vector, vsi->idx); - pf->num_avail_sw_msix += vsi->num_q_vectors; unroll_alloc_q_vector: ice_vsi_free_q_vectors(vsi); unroll_vsi_init: @@ -2920,14 +2647,6 @@ void ice_vsi_decfg(struct ice_vsi *vsi) * many interrupts each VF needs. SR-IOV MSIX resources are also * cleared in the same manner. */ - if (vsi->type == ICE_VSI_CTRL && vsi->vf) { - ice_free_vf_ctrl_res(pf, vsi); - } else if (vsi->type != ICE_VSI_VF) { - /* reclaim SW interrupts back to the common pool */ - ice_free_res(pf->irq_tracker, vsi->base_vector, vsi->idx); - pf->num_avail_sw_msix += vsi->num_q_vectors; - vsi->base_vector = 0; - } if (vsi->type == ICE_VSI_VF && vsi->agg_node && vsi->agg_node->valid) @@ -3044,7 +2763,6 @@ static void ice_vsi_release_msix(struct ice_vsi *vsi) void ice_vsi_free_irq(struct ice_vsi *vsi) { struct ice_pf *pf = vsi->back; - int base = vsi->base_vector; int i; if (!vsi->q_vectors || !vsi->irqs_ready) @@ -3058,10 +2776,9 @@ void ice_vsi_free_irq(struct ice_vsi *vsi) ice_free_cpu_rx_rmap(vsi); ice_for_each_q_vector(vsi, i) { - u16 vector = i + base; int irq_num; - irq_num = pf->msix_entries[vector].vector; + irq_num = vsi->q_vectors[i]->irq.virq; /* free only the irqs that were actually requested */ if (!vsi->q_vectors[i] || @@ -3193,7 +2910,6 @@ void ice_dis_vsi(struct ice_vsi *vsi, bool locked) */ void ice_vsi_dis_irq(struct ice_vsi *vsi) { - int base = vsi->base_vector; struct ice_pf *pf = vsi->back; struct ice_hw *hw = &pf->hw; u32 val; @@ -3240,7 +2956,7 @@ void ice_vsi_dis_irq(struct ice_vsi *vsi) return; ice_for_each_q_vector(vsi, i) - synchronize_irq(pf->msix_entries[i + base].vector); + synchronize_irq(vsi->q_vectors[i]->irq.virq); } /** diff --git a/drivers/net/ethernet/intel/ice/ice_lib.h b/drivers/net/ethernet/intel/ice/ice_lib.h index 75221478f2dc..e985766e6bb5 100644 --- a/drivers/net/ethernet/intel/ice/ice_lib.h +++ b/drivers/net/ethernet/intel/ice/ice_lib.h @@ -104,11 +104,6 @@ int ice_ena_vsi(struct ice_vsi *vsi, bool locked); void ice_vsi_decfg(struct ice_vsi *vsi); void ice_dis_vsi(struct ice_vsi *vsi, bool locked); -int ice_free_res(struct ice_res_tracker *res, u16 index, u16 id); - -int -ice_get_res(struct ice_pf *pf, struct ice_res_tracker *res, u16 needed, u16 id); - int ice_vsi_rebuild(struct ice_vsi *vsi, u32 vsi_flags); int ice_vsi_cfg(struct ice_vsi *vsi, struct ice_vsi_cfg_params *params); diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index a1f7c8edc22f..62e91512aeab 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -2490,7 +2490,6 @@ static int ice_vsi_req_irq_msix(struct ice_vsi *vsi, char *basename) { int q_vectors = vsi->num_q_vectors; struct ice_pf *pf = vsi->back; - int base = vsi->base_vector; struct device *dev; int rx_int_idx = 0; int tx_int_idx = 0; @@ -2501,7 +2500,7 @@ static int ice_vsi_req_irq_msix(struct ice_vsi *vsi, char *basename) for (vector = 0; vector < q_vectors; vector++) { struct ice_q_vector *q_vector = vsi->q_vectors[vector]; - irq_num = pf->msix_entries[base + vector].vector; + irq_num = q_vector->irq.virq; if (q_vector->tx.tx_ring && q_vector->rx.rx_ring) { snprintf(q_vector->name, sizeof(q_vector->name) - 1, @@ -2555,9 +2554,8 @@ static int ice_vsi_req_irq_msix(struct ice_vsi *vsi, char *basename) return 0; free_q_irqs: - while (vector) { - vector--; - irq_num = pf->msix_entries[base + vector].vector; + while (vector--) { + irq_num = vsi->q_vectors[vector]->irq.virq; if (!IS_ENABLED(CONFIG_RFS_ACCEL)) irq_set_affinity_notifier(irq_num, NULL); irq_set_affinity_hint(irq_num, NULL); @@ -3047,7 +3045,7 @@ static void ice_ena_misc_vector(struct ice_pf *pf) wr32(hw, PFINT_OICR_ENA, val); /* SW_ITR_IDX = 0, but don't change INTENA */ - wr32(hw, GLINT_DYN_CTL(pf->oicr_idx), + wr32(hw, GLINT_DYN_CTL(pf->oicr_irq.index), GLINT_DYN_CTL_SW_ITR_INDX_M | GLINT_DYN_CTL_INTENA_MSK_M); } @@ -3234,6 +3232,7 @@ static void ice_dis_ctrlq_interrupts(struct ice_hw *hw) */ static void ice_free_irq_msix_misc(struct ice_pf *pf) { + int misc_irq_num = pf->oicr_irq.virq; struct ice_hw *hw = &pf->hw; ice_dis_ctrlq_interrupts(hw); @@ -3242,14 +3241,10 @@ static void ice_free_irq_msix_misc(struct ice_pf *pf) wr32(hw, PFINT_OICR_ENA, 0); ice_flush(hw); - if (pf->msix_entries) { - synchronize_irq(pf->msix_entries[pf->oicr_idx].vector); - devm_free_irq(ice_pf_to_dev(pf), - pf->msix_entries[pf->oicr_idx].vector, pf); - } + synchronize_irq(misc_irq_num); + devm_free_irq(ice_pf_to_dev(pf), misc_irq_num, pf); - pf->num_avail_sw_msix += 1; - ice_free_res(pf->irq_tracker, pf->oicr_idx, ICE_RES_MISC_VEC_ID); + ice_free_irq(pf, pf->oicr_irq); } /** @@ -3295,7 +3290,8 @@ static int ice_req_irq_msix_misc(struct ice_pf *pf) { struct device *dev = ice_pf_to_dev(pf); struct ice_hw *hw = &pf->hw; - int oicr_idx, err = 0; + struct msi_map oicr_irq; + int err = 0; if (!pf->int_name[0]) snprintf(pf->int_name, sizeof(pf->int_name) - 1, "%s-%s:misc", @@ -3309,30 +3305,26 @@ static int ice_req_irq_msix_misc(struct ice_pf *pf) goto skip_req_irq; /* reserve one vector in irq_tracker for misc interrupts */ - oicr_idx = ice_get_res(pf, pf->irq_tracker, 1, ICE_RES_MISC_VEC_ID); - if (oicr_idx < 0) - return oicr_idx; - - pf->num_avail_sw_msix -= 1; - pf->oicr_idx = (u16)oicr_idx; - - err = devm_request_threaded_irq(dev, - pf->msix_entries[pf->oicr_idx].vector, - ice_misc_intr, ice_misc_intr_thread_fn, - 0, pf->int_name, pf); + oicr_irq = ice_alloc_irq(pf, false); + if (oicr_irq.index < 0) + return oicr_irq.index; + + pf->oicr_irq = oicr_irq; + err = devm_request_threaded_irq(dev, pf->oicr_irq.virq, ice_misc_intr, + ice_misc_intr_thread_fn, 0, + pf->int_name, pf); if (err) { dev_err(dev, "devm_request_threaded_irq for %s failed: %d\n", pf->int_name, err); - ice_free_res(pf->irq_tracker, 1, ICE_RES_MISC_VEC_ID); - pf->num_avail_sw_msix += 1; + ice_free_irq(pf, pf->oicr_irq); return err; } skip_req_irq: ice_ena_misc_vector(pf); - ice_ena_ctrlq_interrupts(hw, pf->oicr_idx); - wr32(hw, GLINT_ITR(ICE_RX_ITR, pf->oicr_idx), + ice_ena_ctrlq_interrupts(hw, pf->oicr_irq.index); + wr32(hw, GLINT_ITR(ICE_RX_ITR, pf->oicr_irq.index), ITR_REG_ALIGN(ICE_ITR_8K) >> ICE_ITR_GRAN_S); ice_flush(hw); @@ -3901,224 +3893,6 @@ static int ice_init_pf(struct ice_pf *pf) } /** - * ice_reduce_msix_usage - Reduce usage of MSI-X vectors - * @pf: board private structure - * @v_remain: number of remaining MSI-X vectors to be distributed - * - * Reduce the usage of MSI-X vectors when entire request cannot be fulfilled. - * pf->num_lan_msix and pf->num_rdma_msix values are set based on number of - * remaining vectors. - */ -static void ice_reduce_msix_usage(struct ice_pf *pf, int v_remain) -{ - int v_rdma; - - if (!ice_is_rdma_ena(pf)) { - pf->num_lan_msix = v_remain; - return; - } - - /* RDMA needs at least 1 interrupt in addition to AEQ MSIX */ - v_rdma = ICE_RDMA_NUM_AEQ_MSIX + 1; - - if (v_remain < ICE_MIN_LAN_TXRX_MSIX + ICE_MIN_RDMA_MSIX) { - dev_warn(ice_pf_to_dev(pf), "Not enough MSI-X vectors to support RDMA.\n"); - clear_bit(ICE_FLAG_RDMA_ENA, pf->flags); - - pf->num_rdma_msix = 0; - pf->num_lan_msix = ICE_MIN_LAN_TXRX_MSIX; - } else if ((v_remain < ICE_MIN_LAN_TXRX_MSIX + v_rdma) || - (v_remain - v_rdma < v_rdma)) { - /* Support minimum RDMA and give remaining vectors to LAN MSIX */ - pf->num_rdma_msix = ICE_MIN_RDMA_MSIX; - pf->num_lan_msix = v_remain - ICE_MIN_RDMA_MSIX; - } else { - /* Split remaining MSIX with RDMA after accounting for AEQ MSIX - */ - pf->num_rdma_msix = (v_remain - ICE_RDMA_NUM_AEQ_MSIX) / 2 + - ICE_RDMA_NUM_AEQ_MSIX; - pf->num_lan_msix = v_remain - pf->num_rdma_msix; - } -} - -/** - * ice_ena_msix_range - Request a range of MSIX vectors from the OS - * @pf: board private structure - * - * Compute the number of MSIX vectors wanted and request from the OS. Adjust - * device usage if there are not enough vectors. Return the number of vectors - * reserved or negative on failure. - */ -static int ice_ena_msix_range(struct ice_pf *pf) -{ - int num_cpus, hw_num_msix, v_other, v_wanted, v_actual; - struct device *dev = ice_pf_to_dev(pf); - int err, i; - - hw_num_msix = pf->hw.func_caps.common_cap.num_msix_vectors; - num_cpus = num_online_cpus(); - - /* LAN miscellaneous handler */ - v_other = ICE_MIN_LAN_OICR_MSIX; - - /* Flow Director */ - if (test_bit(ICE_FLAG_FD_ENA, pf->flags)) - v_other += ICE_FDIR_MSIX; - - /* switchdev */ - v_other += ICE_ESWITCH_MSIX; - - v_wanted = v_other; - - /* LAN traffic */ - pf->num_lan_msix = num_cpus; - v_wanted += pf->num_lan_msix; - - /* RDMA auxiliary driver */ - if (ice_is_rdma_ena(pf)) { - pf->num_rdma_msix = num_cpus + ICE_RDMA_NUM_AEQ_MSIX; - v_wanted += pf->num_rdma_msix; - } - - if (v_wanted > hw_num_msix) { - int v_remain; - - dev_warn(dev, "not enough device MSI-X vectors. wanted = %d, available = %d\n", - v_wanted, hw_num_msix); - - if (hw_num_msix < ICE_MIN_MSIX) { - err = -ERANGE; - goto exit_err; - } - - v_remain = hw_num_msix - v_other; - if (v_remain < ICE_MIN_LAN_TXRX_MSIX) { - v_other = ICE_MIN_MSIX - ICE_MIN_LAN_TXRX_MSIX; - v_remain = ICE_MIN_LAN_TXRX_MSIX; - } - - ice_reduce_msix_usage(pf, v_remain); - v_wanted = pf->num_lan_msix + pf->num_rdma_msix + v_other; - - dev_notice(dev, "Reducing request to %d MSI-X vectors for LAN traffic.\n", - pf->num_lan_msix); - if (ice_is_rdma_ena(pf)) - dev_notice(dev, "Reducing request to %d MSI-X vectors for RDMA.\n", - pf->num_rdma_msix); - } - - pf->msix_entries = devm_kcalloc(dev, v_wanted, - sizeof(*pf->msix_entries), GFP_KERNEL); - if (!pf->msix_entries) { - err = -ENOMEM; - goto exit_err; - } - - for (i = 0; i < v_wanted; i++) - pf->msix_entries[i].entry = i; - - /* actually reserve the vectors */ - v_actual = pci_enable_msix_range(pf->pdev, pf->msix_entries, - ICE_MIN_MSIX, v_wanted); - if (v_actual < 0) { - dev_err(dev, "unable to reserve MSI-X vectors\n"); - err = v_actual; - goto msix_err; - } - - if (v_actual < v_wanted) { - dev_warn(dev, "not enough OS MSI-X vectors. requested = %d, obtained = %d\n", - v_wanted, v_actual); - - if (v_actual < ICE_MIN_MSIX) { - /* error if we can't get minimum vectors */ - pci_disable_msix(pf->pdev); - err = -ERANGE; - goto msix_err; - } else { - int v_remain = v_actual - v_other; - - if (v_remain < ICE_MIN_LAN_TXRX_MSIX) - v_remain = ICE_MIN_LAN_TXRX_MSIX; - - ice_reduce_msix_usage(pf, v_remain); - - dev_notice(dev, "Enabled %d MSI-X vectors for LAN traffic.\n", - pf->num_lan_msix); - - if (ice_is_rdma_ena(pf)) - dev_notice(dev, "Enabled %d MSI-X vectors for RDMA.\n", - pf->num_rdma_msix); - } - } - - return v_actual; - -msix_err: - devm_kfree(dev, pf->msix_entries); - -exit_err: - pf->num_rdma_msix = 0; - pf->num_lan_msix = 0; - return err; -} - -/** - * ice_dis_msix - Disable MSI-X interrupt setup in OS - * @pf: board private structure - */ -static void ice_dis_msix(struct ice_pf *pf) -{ - pci_disable_msix(pf->pdev); - devm_kfree(ice_pf_to_dev(pf), pf->msix_entries); - pf->msix_entries = NULL; -} - -/** - * ice_clear_interrupt_scheme - Undo things done by ice_init_interrupt_scheme - * @pf: board private structure - */ -static void ice_clear_interrupt_scheme(struct ice_pf *pf) -{ - ice_dis_msix(pf); - - if (pf->irq_tracker) { - devm_kfree(ice_pf_to_dev(pf), pf->irq_tracker); - pf->irq_tracker = NULL; - } -} - -/** - * ice_init_interrupt_scheme - Determine proper interrupt scheme - * @pf: board private structure to initialize - */ -static int ice_init_interrupt_scheme(struct ice_pf *pf) -{ - int vectors; - - vectors = ice_ena_msix_range(pf); - - if (vectors < 0) - return vectors; - - /* set up vector assignment tracking */ - pf->irq_tracker = devm_kzalloc(ice_pf_to_dev(pf), - struct_size(pf->irq_tracker, list, vectors), - GFP_KERNEL); - if (!pf->irq_tracker) { - ice_dis_msix(pf); - return -ENOMEM; - } - - /* populate SW interrupts pool with number of OS granted IRQs. */ - pf->num_avail_sw_msix = (u16)vectors; - pf->irq_tracker->num_entries = (u16)vectors; - pf->irq_tracker->end = pf->irq_tracker->num_entries; - - return 0; -} - -/** * ice_is_wol_supported - check if WoL is supported * @hw: pointer to hardware info * diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c index ac6f06f9a2ed..d4b6c997141d 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c @@ -911,7 +911,7 @@ ice_ptp_release_tx_tracker(struct ice_pf *pf, struct ice_ptp_tx *tx) spin_unlock(&tx->lock); /* wait for potentially outstanding interrupt to complete */ - synchronize_irq(pf->msix_entries[pf->oicr_idx].vector); + synchronize_irq(pf->oicr_irq.virq); ice_ptp_flush_tx_tracker(pf, tx); diff --git a/drivers/net/ethernet/intel/ice/ice_sriov.c b/drivers/net/ethernet/intel/ice/ice_sriov.c index 588ad8696756..2a46ecbc628a 100644 --- a/drivers/net/ethernet/intel/ice/ice_sriov.c +++ b/drivers/net/ethernet/intel/ice/ice_sriov.c @@ -135,18 +135,9 @@ static void ice_dis_vf_mappings(struct ice_vf *vf) */ static int ice_sriov_free_msix_res(struct ice_pf *pf) { - struct ice_res_tracker *res; - if (!pf) return -EINVAL; - res = pf->irq_tracker; - if (!res) - return -EINVAL; - - /* give back irq_tracker resources used */ - WARN_ON(pf->sriov_base_vector < res->num_entries); - pf->sriov_base_vector = 0; return 0; @@ -410,29 +401,6 @@ int ice_calc_vf_reg_idx(struct ice_vf *vf, struct ice_q_vector *q_vector) } /** - * ice_get_max_valid_res_idx - Get the max valid resource index - * @res: pointer to the resource to find the max valid index for - * - * Start from the end of the ice_res_tracker and return right when we find the - * first res->list entry with the ICE_RES_VALID_BIT set. This function is only - * valid for SR-IOV because it is the only consumer that manipulates the - * res->end and this is always called when res->end is set to res->num_entries. - */ -static int ice_get_max_valid_res_idx(struct ice_res_tracker *res) -{ - int i; - - if (!res) - return -EINVAL; - - for (i = res->num_entries - 1; i >= 0; i--) - if (res->list[i] & ICE_RES_VALID_BIT) - return i; - - return 0; -} - -/** * ice_sriov_set_msix_res - Set any used MSIX resources * @pf: pointer to PF structure * @num_msix_needed: number of MSIX vectors needed for all SR-IOV VFs @@ -450,7 +418,7 @@ static int ice_get_max_valid_res_idx(struct ice_res_tracker *res) static int ice_sriov_set_msix_res(struct ice_pf *pf, u16 num_msix_needed) { u16 total_vectors = pf->hw.func_caps.common_cap.num_msix_vectors; - int vectors_used = pf->irq_tracker->num_entries; + int vectors_used = ice_get_max_used_msix_vector(pf); int sriov_base_vector; sriov_base_vector = total_vectors - num_msix_needed; @@ -490,7 +458,7 @@ static int ice_sriov_set_msix_res(struct ice_pf *pf, u16 num_msix_needed) */ static int ice_set_per_vf_res(struct ice_pf *pf, u16 num_vfs) { - int max_valid_res_idx = ice_get_max_valid_res_idx(pf->irq_tracker); + int vectors_used = ice_get_max_used_msix_vector(pf); u16 num_msix_per_vf, num_txq, num_rxq, avail_qs; int msix_avail_per_vf, msix_avail_for_sriov; struct device *dev = ice_pf_to_dev(pf); @@ -501,12 +469,9 @@ static int ice_set_per_vf_res(struct ice_pf *pf, u16 num_vfs) if (!num_vfs) return -EINVAL; - if (max_valid_res_idx < 0) - return -ENOSPC; - /* determine MSI-X resources per VF */ msix_avail_for_sriov = pf->hw.func_caps.common_cap.num_msix_vectors - - pf->irq_tracker->num_entries; + vectors_used; msix_avail_per_vf = msix_avail_for_sriov / num_vfs; if (msix_avail_per_vf >= ICE_NUM_VF_MSIX_MED) { num_msix_per_vf = ICE_NUM_VF_MSIX_MED; @@ -871,7 +836,7 @@ static int ice_ena_vfs(struct ice_pf *pf, u16 num_vfs) int ret; /* Disable global interrupt 0 so we don't try to handle the VFLR. */ - wr32(hw, GLINT_DYN_CTL(pf->oicr_idx), + wr32(hw, GLINT_DYN_CTL(pf->oicr_irq.index), ICE_ITR_NONE << GLINT_DYN_CTL_ITR_INDX_S); set_bit(ICE_OICR_INTR_DIS, pf->state); ice_flush(hw); diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib.c b/drivers/net/ethernet/intel/ice/ice_vf_lib.c index bf74a2f3a4f8..e441968a70ae 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_vf_lib.c @@ -1329,3 +1329,35 @@ void ice_vf_set_initialized(struct ice_vf *vf) set_bit(ICE_VF_STATE_INIT, vf->vf_states); memset(&vf->vlan_v2_caps, 0, sizeof(vf->vlan_v2_caps)); } + +/** + * ice_get_vf_ctrl_vsi - Get first VF control VSI pointer + * @pf: the PF private structure + * @vsi: pointer to the VSI + * + * Return first found VF control VSI other than the vsi + * passed by parameter. This function is used to determine + * whether new resources have to be allocated for control VSI + * or they can be shared with existing one. + * + * Return found VF control VSI pointer other itself. Return + * NULL Otherwise. + * + */ +struct ice_vsi *ice_get_vf_ctrl_vsi(struct ice_pf *pf, struct ice_vsi *vsi) +{ + struct ice_vsi *ctrl_vsi = NULL; + struct ice_vf *vf; + unsigned int bkt; + + rcu_read_lock(); + ice_for_each_vf_rcu(pf, bkt, vf) { + if (vf != vsi->vf && vf->ctrl_vsi_idx != ICE_NO_VSI) { + ctrl_vsi = pf->vsi[vf->ctrl_vsi_idx]; + break; + } + } + + rcu_read_unlock(); + return ctrl_vsi; +} diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib.h b/drivers/net/ethernet/intel/ice/ice_vf_lib.h index a38ef00a3679..67172fdd9bc2 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_lib.h +++ b/drivers/net/ethernet/intel/ice/ice_vf_lib.h @@ -227,6 +227,7 @@ int ice_vf_clear_vsi_promisc(struct ice_vf *vf, struct ice_vsi *vsi, u8 promisc_m); int ice_reset_vf(struct ice_vf *vf, u32 flags); void ice_reset_all_vfs(struct ice_pf *pf); +struct ice_vsi *ice_get_vf_ctrl_vsi(struct ice_pf *pf, struct ice_vsi *vsi); #else /* CONFIG_PCI_IOV */ static inline struct ice_vf *ice_get_vf_by_id(struct ice_pf *pf, u16 vf_id) { @@ -291,6 +292,12 @@ static inline int ice_reset_vf(struct ice_vf *vf, u32 flags) static inline void ice_reset_all_vfs(struct ice_pf *pf) { } + +static inline struct ice_vsi * +ice_get_vf_ctrl_vsi(struct ice_pf *pf, struct ice_vsi *vsi) +{ + return NULL; +} #endif /* !CONFIG_PCI_IOV */ #endif /* _ICE_VF_LIB_H_ */ diff --git a/drivers/net/ethernet/intel/ice/ice_xsk.c b/drivers/net/ethernet/intel/ice/ice_xsk.c index d1e489da7363..a7fe2b4ce655 100644 --- a/drivers/net/ethernet/intel/ice/ice_xsk.c +++ b/drivers/net/ethernet/intel/ice/ice_xsk.c @@ -90,7 +90,6 @@ ice_qvec_dis_irq(struct ice_vsi *vsi, struct ice_rx_ring *rx_ring, { struct ice_pf *pf = vsi->back; struct ice_hw *hw = &pf->hw; - int base = vsi->base_vector; u16 reg; u32 val; @@ -103,11 +102,9 @@ ice_qvec_dis_irq(struct ice_vsi *vsi, struct ice_rx_ring *rx_ring, wr32(hw, QINT_RQCTL(reg), val); if (q_vector) { - u16 v_idx = q_vector->v_idx; - wr32(hw, GLINT_DYN_CTL(q_vector->reg_idx), 0); ice_flush(hw); - synchronize_irq(pf->msix_entries[v_idx + base].vector); + synchronize_irq(q_vector->irq.virq); } } diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h index 34aebf00a512..18d4af934d8c 100644 --- a/drivers/net/ethernet/intel/igc/igc.h +++ b/drivers/net/ethernet/intel/igc/igc.h @@ -13,6 +13,7 @@ #include <linux/ptp_clock_kernel.h> #include <linux/timecounter.h> #include <linux/net_tstamp.h> +#include <linux/bitfield.h> #include "igc_hw.h" @@ -311,6 +312,33 @@ extern char igc_driver_name[]; #define IGC_MRQC_RSS_FIELD_IPV4_UDP 0x00400000 #define IGC_MRQC_RSS_FIELD_IPV6_UDP 0x00800000 +/* RX-desc Write-Back format RSS Type's */ +enum igc_rss_type_num { + IGC_RSS_TYPE_NO_HASH = 0, + IGC_RSS_TYPE_HASH_TCP_IPV4 = 1, + IGC_RSS_TYPE_HASH_IPV4 = 2, + IGC_RSS_TYPE_HASH_TCP_IPV6 = 3, + IGC_RSS_TYPE_HASH_IPV6_EX = 4, + IGC_RSS_TYPE_HASH_IPV6 = 5, + IGC_RSS_TYPE_HASH_TCP_IPV6_EX = 6, + IGC_RSS_TYPE_HASH_UDP_IPV4 = 7, + IGC_RSS_TYPE_HASH_UDP_IPV6 = 8, + IGC_RSS_TYPE_HASH_UDP_IPV6_EX = 9, + IGC_RSS_TYPE_MAX = 10, +}; +#define IGC_RSS_TYPE_MAX_TABLE 16 +#define IGC_RSS_TYPE_MASK GENMASK(3,0) /* 4-bits (3:0) = mask 0x0F */ + +/* igc_rss_type - Rx descriptor RSS type field */ +static inline u32 igc_rss_type(const union igc_adv_rx_desc *rx_desc) +{ + /* RSS Type 4-bits (3:0) number: 0-9 (above 9 is reserved) + * Accessing the same bits via u16 (wb.lower.lo_dword.hs_rss.pkt_info) + * is slightly slower than via u32 (wb.lower.lo_dword.data) + */ + return le32_get_bits(rx_desc->wb.lower.lo_dword.data, IGC_RSS_TYPE_MASK); +} + /* Interrupt defines */ #define IGC_START_ITR 648 /* ~6000 ints/sec */ #define IGC_4K_ITR 980 @@ -471,6 +499,13 @@ struct igc_rx_buffer { }; }; +/* context wrapper around xdp_buff to provide access to descriptor metadata */ +struct igc_xdp_buff { + struct xdp_buff xdp; + union igc_adv_rx_desc *rx_desc; + ktime_t rx_ts; /* data indication bit IGC_RXDADV_STAT_TSIP */ +}; + struct igc_q_vector { struct igc_adapter *adapter; /* backlink */ void __iomem *itr_register; diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index 1c4676882082..38d113b48111 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -1690,14 +1690,36 @@ static void igc_rx_checksum(struct igc_ring *ring, le32_to_cpu(rx_desc->wb.upper.status_error)); } +/* Mapping HW RSS Type to enum pkt_hash_types */ +static const enum pkt_hash_types igc_rss_type_table[IGC_RSS_TYPE_MAX_TABLE] = { + [IGC_RSS_TYPE_NO_HASH] = PKT_HASH_TYPE_L2, + [IGC_RSS_TYPE_HASH_TCP_IPV4] = PKT_HASH_TYPE_L4, + [IGC_RSS_TYPE_HASH_IPV4] = PKT_HASH_TYPE_L3, + [IGC_RSS_TYPE_HASH_TCP_IPV6] = PKT_HASH_TYPE_L4, + [IGC_RSS_TYPE_HASH_IPV6_EX] = PKT_HASH_TYPE_L3, + [IGC_RSS_TYPE_HASH_IPV6] = PKT_HASH_TYPE_L3, + [IGC_RSS_TYPE_HASH_TCP_IPV6_EX] = PKT_HASH_TYPE_L4, + [IGC_RSS_TYPE_HASH_UDP_IPV4] = PKT_HASH_TYPE_L4, + [IGC_RSS_TYPE_HASH_UDP_IPV6] = PKT_HASH_TYPE_L4, + [IGC_RSS_TYPE_HASH_UDP_IPV6_EX] = PKT_HASH_TYPE_L4, + [10] = PKT_HASH_TYPE_NONE, /* RSS Type above 9 "Reserved" by HW */ + [11] = PKT_HASH_TYPE_NONE, /* keep array sized for SW bit-mask */ + [12] = PKT_HASH_TYPE_NONE, /* to handle future HW revisons */ + [13] = PKT_HASH_TYPE_NONE, + [14] = PKT_HASH_TYPE_NONE, + [15] = PKT_HASH_TYPE_NONE, +}; + static inline void igc_rx_hash(struct igc_ring *ring, union igc_adv_rx_desc *rx_desc, struct sk_buff *skb) { - if (ring->netdev->features & NETIF_F_RXHASH) - skb_set_hash(skb, - le32_to_cpu(rx_desc->wb.lower.hi_dword.rss), - PKT_HASH_TYPE_L3); + if (ring->netdev->features & NETIF_F_RXHASH) { + u32 rss_hash = le32_to_cpu(rx_desc->wb.lower.hi_dword.rss); + u32 rss_type = igc_rss_type(rx_desc); + + skb_set_hash(skb, rss_hash, igc_rss_type_table[rss_type]); + } } static void igc_rx_vlan(struct igc_ring *rx_ring, @@ -2214,6 +2236,8 @@ static bool igc_alloc_rx_buffers_zc(struct igc_ring *ring, u16 count) if (!count) return ok; + XSK_CHECK_PRIV_TYPE(struct igc_xdp_buff); + desc = IGC_RX_DESC(ring, i); bi = &ring->rx_buffer_info[i]; i -= ring->count; @@ -2498,8 +2522,8 @@ static int igc_clean_rx_irq(struct igc_q_vector *q_vector, const int budget) union igc_adv_rx_desc *rx_desc; struct igc_rx_buffer *rx_buffer; unsigned int size, truesize; + struct igc_xdp_buff ctx; ktime_t timestamp = 0; - struct xdp_buff xdp; int pkt_offset = 0; void *pktbuf; @@ -2528,18 +2552,20 @@ static int igc_clean_rx_irq(struct igc_q_vector *q_vector, const int budget) if (igc_test_staterr(rx_desc, IGC_RXDADV_STAT_TSIP)) { timestamp = igc_ptp_rx_pktstamp(q_vector->adapter, pktbuf); + ctx.rx_ts = timestamp; pkt_offset = IGC_TS_HDR_LEN; size -= IGC_TS_HDR_LEN; } if (!skb) { - xdp_init_buff(&xdp, truesize, &rx_ring->xdp_rxq); - xdp_prepare_buff(&xdp, pktbuf - igc_rx_offset(rx_ring), + xdp_init_buff(&ctx.xdp, truesize, &rx_ring->xdp_rxq); + xdp_prepare_buff(&ctx.xdp, pktbuf - igc_rx_offset(rx_ring), igc_rx_offset(rx_ring) + pkt_offset, size, true); - xdp_buff_clear_frags_flag(&xdp); + xdp_buff_clear_frags_flag(&ctx.xdp); + ctx.rx_desc = rx_desc; - skb = igc_xdp_run_prog(adapter, &xdp); + skb = igc_xdp_run_prog(adapter, &ctx.xdp); } if (IS_ERR(skb)) { @@ -2561,9 +2587,9 @@ static int igc_clean_rx_irq(struct igc_q_vector *q_vector, const int budget) } else if (skb) igc_add_rx_frag(rx_ring, rx_buffer, skb, size); else if (ring_uses_build_skb(rx_ring)) - skb = igc_build_skb(rx_ring, rx_buffer, &xdp); + skb = igc_build_skb(rx_ring, rx_buffer, &ctx.xdp); else - skb = igc_construct_skb(rx_ring, rx_buffer, &xdp, + skb = igc_construct_skb(rx_ring, rx_buffer, &ctx.xdp, timestamp); /* exit if we failed to retrieve a buffer */ @@ -2664,6 +2690,15 @@ static void igc_dispatch_skb_zc(struct igc_q_vector *q_vector, napi_gro_receive(&q_vector->napi, skb); } +static struct igc_xdp_buff *xsk_buff_to_igc_ctx(struct xdp_buff *xdp) +{ + /* xdp_buff pointer used by ZC code path is alloc as xdp_buff_xsk. The + * igc_xdp_buff shares its layout with xdp_buff_xsk and private + * igc_xdp_buff fields fall into xdp_buff_xsk->cb + */ + return (struct igc_xdp_buff *)xdp; +} + static int igc_clean_rx_irq_zc(struct igc_q_vector *q_vector, const int budget) { struct igc_adapter *adapter = q_vector->adapter; @@ -2682,6 +2717,7 @@ static int igc_clean_rx_irq_zc(struct igc_q_vector *q_vector, const int budget) while (likely(total_packets < budget)) { union igc_adv_rx_desc *desc; struct igc_rx_buffer *bi; + struct igc_xdp_buff *ctx; ktime_t timestamp = 0; unsigned int size; int res; @@ -2699,9 +2735,13 @@ static int igc_clean_rx_irq_zc(struct igc_q_vector *q_vector, const int budget) bi = &ring->rx_buffer_info[ntc]; + ctx = xsk_buff_to_igc_ctx(bi->xdp); + ctx->rx_desc = desc; + if (igc_test_staterr(desc, IGC_RXDADV_STAT_TSIP)) { timestamp = igc_ptp_rx_pktstamp(q_vector->adapter, bi->xdp->data); + ctx->rx_ts = timestamp; bi->xdp->data += IGC_TS_HDR_LEN; @@ -6454,6 +6494,58 @@ u32 igc_rd32(struct igc_hw *hw, u32 reg) return value; } +/* Mapping HW RSS Type to enum xdp_rss_hash_type */ +static enum xdp_rss_hash_type igc_xdp_rss_type[IGC_RSS_TYPE_MAX_TABLE] = { + [IGC_RSS_TYPE_NO_HASH] = XDP_RSS_TYPE_L2, + [IGC_RSS_TYPE_HASH_TCP_IPV4] = XDP_RSS_TYPE_L4_IPV4_TCP, + [IGC_RSS_TYPE_HASH_IPV4] = XDP_RSS_TYPE_L3_IPV4, + [IGC_RSS_TYPE_HASH_TCP_IPV6] = XDP_RSS_TYPE_L4_IPV6_TCP, + [IGC_RSS_TYPE_HASH_IPV6_EX] = XDP_RSS_TYPE_L3_IPV6_EX, + [IGC_RSS_TYPE_HASH_IPV6] = XDP_RSS_TYPE_L3_IPV6, + [IGC_RSS_TYPE_HASH_TCP_IPV6_EX] = XDP_RSS_TYPE_L4_IPV6_TCP_EX, + [IGC_RSS_TYPE_HASH_UDP_IPV4] = XDP_RSS_TYPE_L4_IPV4_UDP, + [IGC_RSS_TYPE_HASH_UDP_IPV6] = XDP_RSS_TYPE_L4_IPV6_UDP, + [IGC_RSS_TYPE_HASH_UDP_IPV6_EX] = XDP_RSS_TYPE_L4_IPV6_UDP_EX, + [10] = XDP_RSS_TYPE_NONE, /* RSS Type above 9 "Reserved" by HW */ + [11] = XDP_RSS_TYPE_NONE, /* keep array sized for SW bit-mask */ + [12] = XDP_RSS_TYPE_NONE, /* to handle future HW revisons */ + [13] = XDP_RSS_TYPE_NONE, + [14] = XDP_RSS_TYPE_NONE, + [15] = XDP_RSS_TYPE_NONE, +}; + +static int igc_xdp_rx_hash(const struct xdp_md *_ctx, u32 *hash, + enum xdp_rss_hash_type *rss_type) +{ + const struct igc_xdp_buff *ctx = (void *)_ctx; + + if (!(ctx->xdp.rxq->dev->features & NETIF_F_RXHASH)) + return -ENODATA; + + *hash = le32_to_cpu(ctx->rx_desc->wb.lower.hi_dword.rss); + *rss_type = igc_xdp_rss_type[igc_rss_type(ctx->rx_desc)]; + + return 0; +} + +static int igc_xdp_rx_timestamp(const struct xdp_md *_ctx, u64 *timestamp) +{ + const struct igc_xdp_buff *ctx = (void *)_ctx; + + if (igc_test_staterr(ctx->rx_desc, IGC_RXDADV_STAT_TSIP)) { + *timestamp = ctx->rx_ts; + + return 0; + } + + return -ENODATA; +} + +static const struct xdp_metadata_ops igc_xdp_metadata_ops = { + .xmo_rx_hash = igc_xdp_rx_hash, + .xmo_rx_timestamp = igc_xdp_rx_timestamp, +}; + /** * igc_probe - Device Initialization Routine * @pdev: PCI device information struct @@ -6527,6 +6619,7 @@ static int igc_probe(struct pci_dev *pdev, hw->hw_addr = adapter->io_addr; netdev->netdev_ops = &igc_netdev_ops; + netdev->xdp_metadata_ops = &igc_xdp_metadata_ops; igc_ethtool_set_ops(netdev); netdev->watchdog_timeo = 5 * HZ; @@ -6554,6 +6647,7 @@ static int igc_probe(struct pci_dev *pdev, netdev->features |= NETIF_F_TSO; netdev->features |= NETIF_F_TSO6; netdev->features |= NETIF_F_TSO_ECN; + netdev->features |= NETIF_F_RXHASH; netdev->features |= NETIF_F_RXCSUM; netdev->features |= NETIF_F_HW_CSUM; netdev->features |= NETIF_F_SCTP_CRC; diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 2cad76d0a50e..e2abc00d0472 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -344,6 +344,15 @@ #define MVNETA_MAX_SKB_DESCS (MVNETA_MAX_TSO_SEGS * 2 + MAX_SKB_FRAGS) +/* The size of a TSO header page */ +#define MVNETA_TSO_PAGE_SIZE (2 * PAGE_SIZE) + +/* Number of TSO headers per page. This should be a power of 2 */ +#define MVNETA_TSO_PER_PAGE (MVNETA_TSO_PAGE_SIZE / TSO_HEADER_SIZE) + +/* Maximum number of TSO header pages */ +#define MVNETA_MAX_TSO_PAGES (MVNETA_MAX_TXD / MVNETA_TSO_PER_PAGE) + /* descriptor aligned size */ #define MVNETA_DESC_ALIGNED_SIZE 32 @@ -364,10 +373,6 @@ MVNETA_SKB_HEADROOM)) #define MVNETA_MAX_RX_BUF_SIZE (PAGE_SIZE - MVNETA_SKB_PAD) -#define IS_TSO_HEADER(txq, addr) \ - ((addr >= txq->tso_hdrs_phys) && \ - (addr < txq->tso_hdrs_phys + txq->size * TSO_HEADER_SIZE)) - #define MVNETA_RX_GET_BM_POOL_ID(rxd) \ (((rxd)->status & MVNETA_RXD_BM_POOL_MASK) >> MVNETA_RXD_BM_POOL_SHIFT) @@ -638,6 +643,7 @@ struct mvneta_rx_desc { #endif enum mvneta_tx_buf_type { + MVNETA_TYPE_TSO, MVNETA_TYPE_SKB, MVNETA_TYPE_XDP_TX, MVNETA_TYPE_XDP_NDO, @@ -690,10 +696,10 @@ struct mvneta_tx_queue { int next_desc_to_proc; /* DMA buffers for TSO headers */ - char *tso_hdrs; + char *tso_hdrs[MVNETA_MAX_TSO_PAGES]; /* DMA address of TSO headers */ - dma_addr_t tso_hdrs_phys; + dma_addr_t tso_hdrs_phys[MVNETA_MAX_TSO_PAGES]; /* Affinity mask for CPUs*/ cpumask_t affinity_mask; @@ -1878,12 +1884,13 @@ static void mvneta_txq_bufs_free(struct mvneta_port *pp, mvneta_txq_inc_get(txq); - if (!IS_TSO_HEADER(txq, tx_desc->buf_phys_addr) && - buf->type != MVNETA_TYPE_XDP_TX) + if (buf->type == MVNETA_TYPE_XDP_NDO || + buf->type == MVNETA_TYPE_SKB) dma_unmap_single(pp->dev->dev.parent, tx_desc->buf_phys_addr, tx_desc->data_size, DMA_TO_DEVICE); - if (buf->type == MVNETA_TYPE_SKB && buf->skb) { + if ((buf->type == MVNETA_TYPE_TSO || + buf->type == MVNETA_TYPE_SKB) && buf->skb) { bytes_compl += buf->skb->len; pkts_compl++; dev_kfree_skb_any(buf->skb); @@ -2369,9 +2376,8 @@ mvneta_swbm_add_rx_fragment(struct mvneta_port *pp, if (data_len > 0 && sinfo->nr_frags < MAX_SKB_FRAGS) { skb_frag_t *frag = &sinfo->frags[sinfo->nr_frags++]; - skb_frag_off_set(frag, pp->rx_offset_correction); - skb_frag_size_set(frag, data_len); - __skb_frag_set_page(frag, page); + skb_frag_fill_page_desc(frag, page, + pp->rx_offset_correction, data_len); if (!xdp_buff_has_frags(xdp)) { sinfo->xdp_frags_size = *size; @@ -2661,20 +2667,72 @@ err_drop_frame: return rx_done; } -static inline void -mvneta_tso_put_hdr(struct sk_buff *skb, struct mvneta_tx_queue *txq) +static void mvneta_free_tso_hdrs(struct mvneta_port *pp, + struct mvneta_tx_queue *txq) +{ + struct device *dev = pp->dev->dev.parent; + int i; + + for (i = 0; i < MVNETA_MAX_TSO_PAGES; i++) { + if (txq->tso_hdrs[i]) { + dma_free_coherent(dev, MVNETA_TSO_PAGE_SIZE, + txq->tso_hdrs[i], + txq->tso_hdrs_phys[i]); + txq->tso_hdrs[i] = NULL; + } + } +} + +static int mvneta_alloc_tso_hdrs(struct mvneta_port *pp, + struct mvneta_tx_queue *txq) +{ + struct device *dev = pp->dev->dev.parent; + int i, num; + + num = DIV_ROUND_UP(txq->size, MVNETA_TSO_PER_PAGE); + for (i = 0; i < num; i++) { + txq->tso_hdrs[i] = dma_alloc_coherent(dev, MVNETA_TSO_PAGE_SIZE, + &txq->tso_hdrs_phys[i], + GFP_KERNEL); + if (!txq->tso_hdrs[i]) { + mvneta_free_tso_hdrs(pp, txq); + return -ENOMEM; + } + } + + return 0; +} + +static char *mvneta_get_tso_hdr(struct mvneta_tx_queue *txq, dma_addr_t *dma) +{ + int index, offset; + + index = txq->txq_put_index / MVNETA_TSO_PER_PAGE; + offset = (txq->txq_put_index % MVNETA_TSO_PER_PAGE) * TSO_HEADER_SIZE; + + *dma = txq->tso_hdrs_phys[index] + offset; + + return txq->tso_hdrs[index] + offset; +} + +static void mvneta_tso_put_hdr(struct sk_buff *skb, struct mvneta_tx_queue *txq, + struct tso_t *tso, int size, bool is_last) { struct mvneta_tx_buf *buf = &txq->buf[txq->txq_put_index]; int hdr_len = skb_tcp_all_headers(skb); struct mvneta_tx_desc *tx_desc; + dma_addr_t hdr_phys; + char *hdr; + + hdr = mvneta_get_tso_hdr(txq, &hdr_phys); + tso_build_hdr(skb, hdr, tso, size, is_last); tx_desc = mvneta_txq_next_desc_get(txq); tx_desc->data_size = hdr_len; tx_desc->command = mvneta_skb_tx_csum(skb); tx_desc->command |= MVNETA_TXD_F_DESC; - tx_desc->buf_phys_addr = txq->tso_hdrs_phys + - txq->txq_put_index * TSO_HEADER_SIZE; - buf->type = MVNETA_TYPE_SKB; + tx_desc->buf_phys_addr = hdr_phys; + buf->type = MVNETA_TYPE_TSO; buf->skb = NULL; mvneta_txq_inc_put(txq); @@ -2714,14 +2772,41 @@ mvneta_tso_put_data(struct net_device *dev, struct mvneta_tx_queue *txq, return 0; } +static void mvneta_release_descs(struct mvneta_port *pp, + struct mvneta_tx_queue *txq, + int first, int num) +{ + int desc_idx, i; + + desc_idx = first + num; + if (desc_idx >= txq->size) + desc_idx -= txq->size; + + for (i = num; i >= 0; i--) { + struct mvneta_tx_desc *tx_desc = txq->descs + desc_idx; + struct mvneta_tx_buf *buf = &txq->buf[desc_idx]; + + if (buf->type == MVNETA_TYPE_SKB) + dma_unmap_single(pp->dev->dev.parent, + tx_desc->buf_phys_addr, + tx_desc->data_size, + DMA_TO_DEVICE); + + mvneta_txq_desc_put(txq); + + if (desc_idx == 0) + desc_idx = txq->size; + desc_idx -= 1; + } +} + static int mvneta_tx_tso(struct sk_buff *skb, struct net_device *dev, struct mvneta_tx_queue *txq) { int hdr_len, total_len, data_left; - int desc_count = 0; + int first_desc, desc_count = 0; struct mvneta_port *pp = netdev_priv(dev); struct tso_t tso; - int i; /* Count needed descriptors */ if ((txq->count + tso_count_descs(skb)) >= txq->size) @@ -2732,22 +2817,19 @@ static int mvneta_tx_tso(struct sk_buff *skb, struct net_device *dev, return 0; } + first_desc = txq->txq_put_index; + /* Initialize the TSO handler, and prepare the first payload */ hdr_len = tso_start(skb, &tso); total_len = skb->len - hdr_len; while (total_len > 0) { - char *hdr; - data_left = min_t(int, skb_shinfo(skb)->gso_size, total_len); total_len -= data_left; desc_count++; /* prepare packet headers: MAC + IP + TCP */ - hdr = txq->tso_hdrs + txq->txq_put_index * TSO_HEADER_SIZE; - tso_build_hdr(skb, hdr, &tso, data_left, total_len == 0); - - mvneta_tso_put_hdr(skb, txq); + mvneta_tso_put_hdr(skb, txq, &tso, data_left, total_len == 0); while (data_left > 0) { int size; @@ -2772,15 +2854,7 @@ err_release: /* Release all used data descriptors; header descriptors must not * be DMA-unmapped. */ - for (i = desc_count - 1; i >= 0; i--) { - struct mvneta_tx_desc *tx_desc = txq->descs + i; - if (!IS_TSO_HEADER(txq, tx_desc->buf_phys_addr)) - dma_unmap_single(pp->dev->dev.parent, - tx_desc->buf_phys_addr, - tx_desc->data_size, - DMA_TO_DEVICE); - mvneta_txq_desc_put(txq); - } + mvneta_release_descs(pp, txq, first_desc, desc_count - 1); return 0; } @@ -2790,6 +2864,7 @@ static int mvneta_tx_frag_process(struct mvneta_port *pp, struct sk_buff *skb, { struct mvneta_tx_desc *tx_desc; int i, nr_frags = skb_shinfo(skb)->nr_frags; + int first_desc = txq->txq_put_index; for (i = 0; i < nr_frags; i++) { struct mvneta_tx_buf *buf = &txq->buf[txq->txq_put_index]; @@ -2828,15 +2903,7 @@ error: /* Release all descriptors that were used to map fragments of * this packet, as well as the corresponding DMA mappings */ - for (i = i - 1; i >= 0; i--) { - tx_desc = txq->descs + i; - dma_unmap_single(pp->dev->dev.parent, - tx_desc->buf_phys_addr, - tx_desc->data_size, - DMA_TO_DEVICE); - mvneta_txq_desc_put(txq); - } - + mvneta_release_descs(pp, txq, first_desc, i - 1); return -ENOMEM; } @@ -3457,7 +3524,7 @@ static void mvneta_rxq_deinit(struct mvneta_port *pp, static int mvneta_txq_sw_init(struct mvneta_port *pp, struct mvneta_tx_queue *txq) { - int cpu; + int cpu, err; txq->size = pp->tx_ring_size; @@ -3482,11 +3549,9 @@ static int mvneta_txq_sw_init(struct mvneta_port *pp, return -ENOMEM; /* Allocate DMA buffers for TSO MAC/IP/TCP headers */ - txq->tso_hdrs = dma_alloc_coherent(pp->dev->dev.parent, - txq->size * TSO_HEADER_SIZE, - &txq->tso_hdrs_phys, GFP_KERNEL); - if (!txq->tso_hdrs) - return -ENOMEM; + err = mvneta_alloc_tso_hdrs(pp, txq); + if (err) + return err; /* Setup XPS mapping */ if (pp->neta_armada3700) @@ -3538,10 +3603,7 @@ static void mvneta_txq_sw_deinit(struct mvneta_port *pp, kfree(txq->buf); - if (txq->tso_hdrs) - dma_free_coherent(pp->dev->dev.parent, - txq->size * TSO_HEADER_SIZE, - txq->tso_hdrs, txq->tso_hdrs_phys); + mvneta_free_tso_hdrs(pp, txq); if (txq->descs) dma_free_coherent(pp->dev->dev.parent, txq->size * MVNETA_DESC_ALIGNED_SIZE, @@ -3550,7 +3612,6 @@ static void mvneta_txq_sw_deinit(struct mvneta_port *pp, netdev_tx_reset_queue(nq); txq->buf = NULL; - txq->tso_hdrs = NULL; txq->descs = NULL; txq->last_desc = 0; txq->next_desc_to_proc = 0; @@ -5821,6 +5882,8 @@ static int __init mvneta_driver_init(void) { int ret; + BUILD_BUG_ON_NOT_POWER_OF_2(MVNETA_TSO_PER_PAGE); + ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, "net/mvneta:online", mvneta_cpu_online, mvneta_cpu_down_prepare); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/common.h b/drivers/net/ethernet/marvell/octeontx2/af/common.h index 8931864ee110..f5bf719a6ccf 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/common.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/common.h @@ -142,7 +142,7 @@ enum nix_scheduler { #define TXSCH_RR_QTM_MAX ((1 << 24) - 1) #define TXSCH_TL1_DFLT_RR_QTM TXSCH_RR_QTM_MAX -#define TXSCH_TL1_DFLT_RR_PRIO (0x1ull) +#define TXSCH_TL1_DFLT_RR_PRIO (0x7ull) #define CN10K_MAX_DWRR_WEIGHT 16384 /* Weight is 14bit on CN10K */ /* Min/Max packet sizes, excluding FCS */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c index 9533b1d92960..3b26893efdf8 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c @@ -1222,6 +1222,11 @@ static int rvu_dbg_npa_ctx_display(struct seq_file *m, void *unused, int ctype) for (aura = id; aura < max_id; aura++) { aq_req.aura_id = aura; + + /* Skip if queue is uninitialized */ + if (ctype == NPA_AQ_CTYPE_POOL && !test_bit(aura, pfvf->pool_bmap)) + continue; + seq_printf(m, "======%s : %d=======\n", (ctype == NPA_AQ_CTYPE_AURA) ? "AURA" : "POOL", aq_req.aura_id); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c index 4ad707e758b9..79ed7af0b0a4 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c @@ -1691,6 +1691,42 @@ exit: return true; } +static void nix_reset_tx_schedule(struct rvu *rvu, int blkaddr, + int lvl, int schq) +{ + u64 tlx_parent = 0, tlx_schedule = 0; + + switch (lvl) { + case NIX_TXSCH_LVL_TL2: + tlx_parent = NIX_AF_TL2X_PARENT(schq); + tlx_schedule = NIX_AF_TL2X_SCHEDULE(schq); + break; + case NIX_TXSCH_LVL_TL3: + tlx_parent = NIX_AF_TL3X_PARENT(schq); + tlx_schedule = NIX_AF_TL3X_SCHEDULE(schq); + break; + case NIX_TXSCH_LVL_TL4: + tlx_parent = NIX_AF_TL4X_PARENT(schq); + tlx_schedule = NIX_AF_TL4X_SCHEDULE(schq); + break; + case NIX_TXSCH_LVL_MDQ: + /* no need to reset SMQ_CFG as HW clears this CSR + * on SMQ flush + */ + tlx_parent = NIX_AF_MDQX_PARENT(schq); + tlx_schedule = NIX_AF_MDQX_SCHEDULE(schq); + break; + default: + return; + } + + if (tlx_parent) + rvu_write64(rvu, blkaddr, tlx_parent, 0x0); + + if (tlx_schedule) + rvu_write64(rvu, blkaddr, tlx_schedule, 0x0); +} + /* Disable shaping of pkts by a scheduler queue * at a given scheduler level. */ @@ -2039,6 +2075,7 @@ int rvu_mbox_handler_nix_txsch_alloc(struct rvu *rvu, pfvf_map[schq] = TXSCH_MAP(pcifunc, 0); nix_reset_tx_linkcfg(rvu, blkaddr, lvl, schq); nix_reset_tx_shaping(rvu, blkaddr, nixlf, lvl, schq); + nix_reset_tx_schedule(rvu, blkaddr, lvl, schq); } for (idx = 0; idx < req->schq[lvl]; idx++) { @@ -2048,6 +2085,7 @@ int rvu_mbox_handler_nix_txsch_alloc(struct rvu *rvu, pfvf_map[schq] = TXSCH_MAP(pcifunc, 0); nix_reset_tx_linkcfg(rvu, blkaddr, lvl, schq); nix_reset_tx_shaping(rvu, blkaddr, nixlf, lvl, schq); + nix_reset_tx_schedule(rvu, blkaddr, lvl, schq); } } @@ -2143,6 +2181,7 @@ static int nix_txschq_free(struct rvu *rvu, u16 pcifunc) continue; nix_reset_tx_linkcfg(rvu, blkaddr, lvl, schq); nix_clear_tx_xoff(rvu, blkaddr, lvl, schq); + nix_reset_tx_shaping(rvu, blkaddr, nixlf, lvl, schq); } } nix_clear_tx_xoff(rvu, blkaddr, NIX_TXSCH_LVL_TL1, @@ -2181,6 +2220,7 @@ static int nix_txschq_free(struct rvu *rvu, u16 pcifunc) for (schq = 0; schq < txsch->schq.max; schq++) { if (TXSCH_MAP_FUNC(txsch->pfvf_map[schq]) != pcifunc) continue; + nix_reset_tx_schedule(rvu, blkaddr, lvl, schq); rvu_free_rsrc(&txsch->schq, schq); txsch->pfvf_map[schq] = TXSCH_MAP(0, NIX_TXSCHQ_FREE); } @@ -2240,6 +2280,9 @@ static int nix_txschq_free_one(struct rvu *rvu, */ nix_clear_tx_xoff(rvu, blkaddr, lvl, schq); + nix_reset_tx_linkcfg(rvu, blkaddr, lvl, schq); + nix_reset_tx_shaping(rvu, blkaddr, nixlf, lvl, schq); + /* Flush if it is a SMQ. Onus of disabling * TL2/3 queue links before SMQ flush is on user */ @@ -2249,6 +2292,8 @@ static int nix_txschq_free_one(struct rvu *rvu, goto err; } + nix_reset_tx_schedule(rvu, blkaddr, lvl, schq); + /* Free the resource */ rvu_free_rsrc(&txsch->schq, schq); txsch->pfvf_map[schq] = TXSCH_MAP(0, NIX_TXSCHQ_FREE); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/Makefile b/drivers/net/ethernet/marvell/octeontx2/nic/Makefile index 73fdb8798614..5664f768cb0c 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/Makefile +++ b/drivers/net/ethernet/marvell/octeontx2/nic/Makefile @@ -8,7 +8,7 @@ obj-$(CONFIG_OCTEONTX2_VF) += rvu_nicvf.o otx2_ptp.o rvu_nicpf-y := otx2_pf.o otx2_common.o otx2_txrx.o otx2_ethtool.o \ otx2_flows.o otx2_tc.o cn10k.o otx2_dmac_flt.o \ - otx2_devlink.o + otx2_devlink.o qos_sq.o qos.o rvu_nicvf-y := otx2_vf.o otx2_devlink.o rvu_nicpf-$(CONFIG_DCB) += otx2_dcbnl.o diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c index a487a98eac88..6e2fb24be8c1 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c @@ -6,7 +6,6 @@ #include <linux/rtnetlink.h> #include <linux/bitfield.h> -#include <net/macsec.h> #include "otx2_common.h" #define MCS_TCAM0_MAC_DA_MASK GENMASK_ULL(47, 0) @@ -212,6 +211,7 @@ static int cn10k_mcs_write_rx_secy(struct otx2_nic *pfvf, struct mcs_secy_plcy_write_req *req; struct mbox *mbox = &pfvf->mbox; u64 policy; + u8 cipher; int ret; mutex_lock(&mbox->lock); @@ -227,7 +227,21 @@ static int cn10k_mcs_write_rx_secy(struct otx2_nic *pfvf, policy |= MCS_RX_SECY_PLCY_RP; policy |= MCS_RX_SECY_PLCY_AUTH_ENA; - policy |= FIELD_PREP(MCS_RX_SECY_PLCY_CIP, MCS_GCM_AES_128); + + switch (secy->key_len) { + case 16: + cipher = secy->xpn ? MCS_GCM_AES_XPN_128 : MCS_GCM_AES_128; + break; + case 32: + cipher = secy->xpn ? MCS_GCM_AES_XPN_256 : MCS_GCM_AES_256; + break; + default: + cipher = MCS_GCM_AES_128; + dev_warn(pfvf->dev, "Unsupported key length\n"); + break; + } + + policy |= FIELD_PREP(MCS_RX_SECY_PLCY_CIP, cipher); policy |= FIELD_PREP(MCS_RX_SECY_PLCY_VAL, secy->validate_frames); policy |= MCS_RX_SECY_PLCY_ENA; @@ -323,9 +337,12 @@ static int cn10k_mcs_write_rx_sa_plcy(struct otx2_nic *pfvf, { unsigned char *src = rxsc->sa_key[assoc_num]; struct mcs_sa_plcy_write_req *plcy_req; + u8 *salt_p = rxsc->salt[assoc_num]; struct mcs_rx_sc_sa_map *map_req; struct mbox *mbox = &pfvf->mbox; + u64 ssci_salt_95_64 = 0; u8 reg, key_len; + u64 salt_63_0; int ret; mutex_lock(&mbox->lock); @@ -349,6 +366,15 @@ static int cn10k_mcs_write_rx_sa_plcy(struct otx2_nic *pfvf, reg++; } + if (secy->xpn) { + memcpy((u8 *)&salt_63_0, salt_p, 8); + memcpy((u8 *)&ssci_salt_95_64, salt_p + 8, 4); + ssci_salt_95_64 |= (__force u64)rxsc->ssci[assoc_num] << 32; + + plcy_req->plcy[0][6] = salt_63_0; + plcy_req->plcy[0][7] = ssci_salt_95_64; + } + plcy_req->sa_index[0] = rxsc->hw_sa_id[assoc_num]; plcy_req->sa_cnt = 1; plcy_req->dir = MCS_RX; @@ -400,12 +426,16 @@ static int cn10k_mcs_write_tx_secy(struct otx2_nic *pfvf, struct mcs_secy_plcy_write_req *req; struct mbox *mbox = &pfvf->mbox; struct macsec_tx_sc *sw_tx_sc; - /* Insert SecTag after 12 bytes (DA+SA)*/ - u8 tag_offset = 12; u8 sectag_tci = 0; + u8 tag_offset; u64 policy; + u8 cipher; int ret; + /* Insert SecTag after 12 bytes (DA+SA) or 16 bytes + * if VLAN tag needs to be sent in clear text. + */ + tag_offset = txsc->vlan_dev ? 16 : 12; sw_tx_sc = &secy->tx_sc; mutex_lock(&mbox->lock); @@ -434,7 +464,21 @@ static int cn10k_mcs_write_tx_secy(struct otx2_nic *pfvf, policy |= FIELD_PREP(MCS_TX_SECY_PLCY_ST_OFFSET, tag_offset); policy |= MCS_TX_SECY_PLCY_INS_MODE; policy |= MCS_TX_SECY_PLCY_AUTH_ENA; - policy |= FIELD_PREP(MCS_TX_SECY_PLCY_CIP, MCS_GCM_AES_128); + + switch (secy->key_len) { + case 16: + cipher = secy->xpn ? MCS_GCM_AES_XPN_128 : MCS_GCM_AES_128; + break; + case 32: + cipher = secy->xpn ? MCS_GCM_AES_XPN_256 : MCS_GCM_AES_256; + break; + default: + cipher = MCS_GCM_AES_128; + dev_warn(pfvf->dev, "Unsupported key length\n"); + break; + } + + policy |= FIELD_PREP(MCS_TX_SECY_PLCY_CIP, cipher); if (secy->protect_frames) policy |= MCS_TX_SECY_PLCY_PROTECT; @@ -544,8 +588,11 @@ static int cn10k_mcs_write_tx_sa_plcy(struct otx2_nic *pfvf, { unsigned char *src = txsc->sa_key[assoc_num]; struct mcs_sa_plcy_write_req *plcy_req; + u8 *salt_p = txsc->salt[assoc_num]; struct mbox *mbox = &pfvf->mbox; + u64 ssci_salt_95_64 = 0; u8 reg, key_len; + u64 salt_63_0; int ret; mutex_lock(&mbox->lock); @@ -561,6 +608,15 @@ static int cn10k_mcs_write_tx_sa_plcy(struct otx2_nic *pfvf, reg++; } + if (secy->xpn) { + memcpy((u8 *)&salt_63_0, salt_p, 8); + memcpy((u8 *)&ssci_salt_95_64, salt_p + 8, 4); + ssci_salt_95_64 |= (__force u64)txsc->ssci[assoc_num] << 32; + + plcy_req->plcy[0][6] = salt_63_0; + plcy_req->plcy[0][7] = ssci_salt_95_64; + } + plcy_req->plcy[0][8] = assoc_num; plcy_req->sa_index[0] = txsc->hw_sa_id[assoc_num]; plcy_req->sa_cnt = 1; @@ -922,8 +978,7 @@ static int cn10k_mcs_secy_tx_cfg(struct otx2_nic *pfvf, struct macsec_secy *secy { if (sw_tx_sa) { cn10k_mcs_write_tx_sa_plcy(pfvf, secy, txsc, sa_num); - cn10k_write_tx_sa_pn(pfvf, txsc, sa_num, - sw_tx_sa->next_pn_halves.lower); + cn10k_write_tx_sa_pn(pfvf, txsc, sa_num, sw_tx_sa->next_pn); cn10k_mcs_link_tx_sa2sc(pfvf, secy, txsc, sa_num, sw_tx_sa->active); } @@ -959,7 +1014,7 @@ static int cn10k_mcs_secy_rx_cfg(struct otx2_nic *pfvf, cn10k_mcs_write_rx_sa_plcy(pfvf, secy, mcs_rx_sc, sa_num, sw_rx_sa->active); cn10k_mcs_write_rx_sa_pn(pfvf, mcs_rx_sc, sa_num, - sw_rx_sa->next_pn_halves.lower); + sw_rx_sa->next_pn); } cn10k_mcs_write_rx_flowid(pfvf, mcs_rx_sc, hw_secy_id); @@ -1053,7 +1108,7 @@ static void cn10k_mcs_sync_stats(struct otx2_nic *pfvf, struct macsec_secy *secy static int cn10k_mdo_open(struct macsec_context *ctx) { - struct otx2_nic *pfvf = netdev_priv(ctx->netdev); + struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; struct macsec_secy *secy = ctx->secy; struct macsec_tx_sa *sw_tx_sa; @@ -1077,7 +1132,7 @@ static int cn10k_mdo_open(struct macsec_context *ctx) static int cn10k_mdo_stop(struct macsec_context *ctx) { - struct otx2_nic *pfvf = netdev_priv(ctx->netdev); + struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; struct cn10k_mcs_txsc *txsc; int err; @@ -1095,7 +1150,7 @@ static int cn10k_mdo_stop(struct macsec_context *ctx) static int cn10k_mdo_add_secy(struct macsec_context *ctx) { - struct otx2_nic *pfvf = netdev_priv(ctx->netdev); + struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; struct macsec_secy *secy = ctx->secy; struct cn10k_mcs_txsc *txsc; @@ -1103,13 +1158,6 @@ static int cn10k_mdo_add_secy(struct macsec_context *ctx) if (secy->icv_len != MACSEC_DEFAULT_ICV_LEN) return -EOPNOTSUPP; - /* Stick to 16 bytes key len until XPN support is added */ - if (secy->key_len != 16) - return -EOPNOTSUPP; - - if (secy->xpn) - return -EOPNOTSUPP; - txsc = cn10k_mcs_create_txsc(pfvf); if (IS_ERR(txsc)) return -ENOSPC; @@ -1118,6 +1166,7 @@ static int cn10k_mdo_add_secy(struct macsec_context *ctx) txsc->encoding_sa = secy->tx_sc.encoding_sa; txsc->last_validate_frames = secy->validate_frames; txsc->last_replay_protect = secy->replay_protect; + txsc->vlan_dev = is_vlan_dev(ctx->netdev); list_add(&txsc->entry, &cfg->txsc_list); @@ -1129,7 +1178,7 @@ static int cn10k_mdo_add_secy(struct macsec_context *ctx) static int cn10k_mdo_upd_secy(struct macsec_context *ctx) { - struct otx2_nic *pfvf = netdev_priv(ctx->netdev); + struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; struct macsec_secy *secy = ctx->secy; struct macsec_tx_sa *sw_tx_sa; @@ -1164,7 +1213,7 @@ static int cn10k_mdo_upd_secy(struct macsec_context *ctx) static int cn10k_mdo_del_secy(struct macsec_context *ctx) { - struct otx2_nic *pfvf = netdev_priv(ctx->netdev); + struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; struct cn10k_mcs_txsc *txsc; @@ -1183,7 +1232,7 @@ static int cn10k_mdo_del_secy(struct macsec_context *ctx) static int cn10k_mdo_add_txsa(struct macsec_context *ctx) { - struct otx2_nic *pfvf = netdev_priv(ctx->netdev); + struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); struct macsec_tx_sa *sw_tx_sa = ctx->sa.tx_sa; struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; struct macsec_secy *secy = ctx->secy; @@ -1202,6 +1251,9 @@ static int cn10k_mdo_add_txsa(struct macsec_context *ctx) return -ENOSPC; memcpy(&txsc->sa_key[sa_num], ctx->sa.key, secy->key_len); + memcpy(&txsc->salt[sa_num], sw_tx_sa->key.salt.bytes, MACSEC_SALT_LEN); + txsc->ssci[sa_num] = sw_tx_sa->ssci; + txsc->sa_bmap |= 1 << sa_num; if (netif_running(secy->netdev)) { @@ -1210,7 +1262,7 @@ static int cn10k_mdo_add_txsa(struct macsec_context *ctx) return err; err = cn10k_write_tx_sa_pn(pfvf, txsc, sa_num, - sw_tx_sa->next_pn_halves.lower); + sw_tx_sa->next_pn); if (err) return err; @@ -1225,7 +1277,7 @@ static int cn10k_mdo_add_txsa(struct macsec_context *ctx) static int cn10k_mdo_upd_txsa(struct macsec_context *ctx) { - struct otx2_nic *pfvf = netdev_priv(ctx->netdev); + struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); struct macsec_tx_sa *sw_tx_sa = ctx->sa.tx_sa; struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; struct macsec_secy *secy = ctx->secy; @@ -1243,7 +1295,7 @@ static int cn10k_mdo_upd_txsa(struct macsec_context *ctx) if (netif_running(secy->netdev)) { /* Keys cannot be changed after creation */ err = cn10k_write_tx_sa_pn(pfvf, txsc, sa_num, - sw_tx_sa->next_pn_halves.lower); + sw_tx_sa->next_pn); if (err) return err; @@ -1258,7 +1310,7 @@ static int cn10k_mdo_upd_txsa(struct macsec_context *ctx) static int cn10k_mdo_del_txsa(struct macsec_context *ctx) { - struct otx2_nic *pfvf = netdev_priv(ctx->netdev); + struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; u8 sa_num = ctx->sa.assoc_num; struct cn10k_mcs_txsc *txsc; @@ -1278,7 +1330,7 @@ static int cn10k_mdo_del_txsa(struct macsec_context *ctx) static int cn10k_mdo_add_rxsc(struct macsec_context *ctx) { - struct otx2_nic *pfvf = netdev_priv(ctx->netdev); + struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; struct macsec_secy *secy = ctx->secy; struct cn10k_mcs_rxsc *rxsc; @@ -1312,7 +1364,7 @@ static int cn10k_mdo_add_rxsc(struct macsec_context *ctx) static int cn10k_mdo_upd_rxsc(struct macsec_context *ctx) { - struct otx2_nic *pfvf = netdev_priv(ctx->netdev); + struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; struct macsec_secy *secy = ctx->secy; bool enable = ctx->rx_sc->active; @@ -1331,7 +1383,7 @@ static int cn10k_mdo_upd_rxsc(struct macsec_context *ctx) static int cn10k_mdo_del_rxsc(struct macsec_context *ctx) { - struct otx2_nic *pfvf = netdev_priv(ctx->netdev); + struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; struct cn10k_mcs_rxsc *rxsc; @@ -1349,11 +1401,10 @@ static int cn10k_mdo_del_rxsc(struct macsec_context *ctx) static int cn10k_mdo_add_rxsa(struct macsec_context *ctx) { + struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); struct macsec_rx_sc *sw_rx_sc = ctx->sa.rx_sa->sc; - struct otx2_nic *pfvf = netdev_priv(ctx->netdev); struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; struct macsec_rx_sa *rx_sa = ctx->sa.rx_sa; - u64 next_pn = rx_sa->next_pn_halves.lower; struct macsec_secy *secy = ctx->secy; bool sa_in_use = rx_sa->active; u8 sa_num = ctx->sa.assoc_num; @@ -1371,6 +1422,9 @@ static int cn10k_mdo_add_rxsa(struct macsec_context *ctx) return -ENOSPC; memcpy(&rxsc->sa_key[sa_num], ctx->sa.key, ctx->secy->key_len); + memcpy(&rxsc->salt[sa_num], rx_sa->key.salt.bytes, MACSEC_SALT_LEN); + rxsc->ssci[sa_num] = rx_sa->ssci; + rxsc->sa_bmap |= 1 << sa_num; if (netif_running(secy->netdev)) { @@ -1379,7 +1433,8 @@ static int cn10k_mdo_add_rxsa(struct macsec_context *ctx) if (err) return err; - err = cn10k_mcs_write_rx_sa_pn(pfvf, rxsc, sa_num, next_pn); + err = cn10k_mcs_write_rx_sa_pn(pfvf, rxsc, sa_num, + rx_sa->next_pn); if (err) return err; } @@ -1389,11 +1444,10 @@ static int cn10k_mdo_add_rxsa(struct macsec_context *ctx) static int cn10k_mdo_upd_rxsa(struct macsec_context *ctx) { + struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); struct macsec_rx_sc *sw_rx_sc = ctx->sa.rx_sa->sc; - struct otx2_nic *pfvf = netdev_priv(ctx->netdev); struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; struct macsec_rx_sa *rx_sa = ctx->sa.rx_sa; - u64 next_pn = rx_sa->next_pn_halves.lower; struct macsec_secy *secy = ctx->secy; bool sa_in_use = rx_sa->active; u8 sa_num = ctx->sa.assoc_num; @@ -1412,7 +1466,8 @@ static int cn10k_mdo_upd_rxsa(struct macsec_context *ctx) if (err) return err; - err = cn10k_mcs_write_rx_sa_pn(pfvf, rxsc, sa_num, next_pn); + err = cn10k_mcs_write_rx_sa_pn(pfvf, rxsc, sa_num, + rx_sa->next_pn); if (err) return err; } @@ -1422,8 +1477,8 @@ static int cn10k_mdo_upd_rxsa(struct macsec_context *ctx) static int cn10k_mdo_del_rxsa(struct macsec_context *ctx) { + struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); struct macsec_rx_sc *sw_rx_sc = ctx->sa.rx_sa->sc; - struct otx2_nic *pfvf = netdev_priv(ctx->netdev); struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; u8 sa_num = ctx->sa.assoc_num; struct cn10k_mcs_rxsc *rxsc; @@ -1445,8 +1500,8 @@ static int cn10k_mdo_del_rxsa(struct macsec_context *ctx) static int cn10k_mdo_get_dev_stats(struct macsec_context *ctx) { + struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); struct mcs_secy_stats tx_rsp = { 0 }, rx_rsp = { 0 }; - struct otx2_nic *pfvf = netdev_priv(ctx->netdev); struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; struct macsec_secy *secy = ctx->secy; struct cn10k_mcs_txsc *txsc; @@ -1481,7 +1536,7 @@ static int cn10k_mdo_get_dev_stats(struct macsec_context *ctx) static int cn10k_mdo_get_tx_sc_stats(struct macsec_context *ctx) { - struct otx2_nic *pfvf = netdev_priv(ctx->netdev); + struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; struct mcs_sc_stats rsp = { 0 }; struct cn10k_mcs_txsc *txsc; @@ -1502,7 +1557,7 @@ static int cn10k_mdo_get_tx_sc_stats(struct macsec_context *ctx) static int cn10k_mdo_get_tx_sa_stats(struct macsec_context *ctx) { - struct otx2_nic *pfvf = netdev_priv(ctx->netdev); + struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; struct mcs_sa_stats rsp = { 0 }; u8 sa_num = ctx->sa.assoc_num; @@ -1525,7 +1580,7 @@ static int cn10k_mdo_get_tx_sa_stats(struct macsec_context *ctx) static int cn10k_mdo_get_rx_sc_stats(struct macsec_context *ctx) { - struct otx2_nic *pfvf = netdev_priv(ctx->netdev); + struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; struct macsec_secy *secy = ctx->secy; struct mcs_sc_stats rsp = { 0 }; @@ -1567,8 +1622,8 @@ static int cn10k_mdo_get_rx_sc_stats(struct macsec_context *ctx) static int cn10k_mdo_get_rx_sa_stats(struct macsec_context *ctx) { + struct otx2_nic *pfvf = macsec_netdev_priv(ctx->netdev); struct macsec_rx_sc *sw_rx_sc = ctx->sa.rx_sa->sc; - struct otx2_nic *pfvf = netdev_priv(ctx->netdev); struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; struct mcs_sa_stats rsp = { 0 }; u8 sa_num = ctx->sa.assoc_num; diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c index 8a41ad8ca04f..f9286648e45c 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c @@ -89,6 +89,11 @@ int otx2_update_sq_stats(struct otx2_nic *pfvf, int qidx) if (!pfvf->qset.sq) return 0; + if (qidx >= pfvf->hw.non_qos_queues) { + if (!test_bit(qidx - pfvf->hw.non_qos_queues, pfvf->qos.qos_sq_bmap)) + return 0; + } + otx2_nix_sq_op_stats(&sq->stats, pfvf, qidx); return 1; } @@ -513,8 +518,8 @@ void otx2_config_irq_coalescing(struct otx2_nic *pfvf, int qidx) (pfvf->hw.cq_ecount_wait - 1)); } -int __otx2_alloc_rbuf(struct otx2_nic *pfvf, struct otx2_pool *pool, - dma_addr_t *dma) +static int __otx2_alloc_rbuf(struct otx2_nic *pfvf, struct otx2_pool *pool, + dma_addr_t *dma) { u8 *buf; @@ -532,8 +537,8 @@ int __otx2_alloc_rbuf(struct otx2_nic *pfvf, struct otx2_pool *pool, return 0; } -static int otx2_alloc_rbuf(struct otx2_nic *pfvf, struct otx2_pool *pool, - dma_addr_t *dma) +int otx2_alloc_rbuf(struct otx2_nic *pfvf, struct otx2_pool *pool, + dma_addr_t *dma) { int ret; @@ -716,7 +721,8 @@ EXPORT_SYMBOL(otx2_smq_flush); int otx2_txsch_alloc(struct otx2_nic *pfvf) { struct nix_txsch_alloc_req *req; - int lvl; + struct nix_txsch_alloc_rsp *rsp; + int lvl, schq, rc; /* Get memory to put this msg */ req = otx2_mbox_alloc_msg_nix_txsch_alloc(&pfvf->mbox); @@ -726,43 +732,83 @@ int otx2_txsch_alloc(struct otx2_nic *pfvf) /* Request one schq per level */ for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) req->schq[lvl] = 1; + rc = otx2_sync_mbox_msg(&pfvf->mbox); + if (rc) + return rc; - return otx2_sync_mbox_msg(&pfvf->mbox); + rsp = (struct nix_txsch_alloc_rsp *) + otx2_mbox_get_rsp(&pfvf->mbox.mbox, 0, &req->hdr); + if (IS_ERR(rsp)) + return PTR_ERR(rsp); + + /* Setup transmit scheduler list */ + for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) + for (schq = 0; schq < rsp->schq[lvl]; schq++) + pfvf->hw.txschq_list[lvl][schq] = + rsp->schq_list[lvl][schq]; + + pfvf->hw.txschq_link_cfg_lvl = rsp->link_cfg_lvl; + + return 0; } -int otx2_txschq_stop(struct otx2_nic *pfvf) +void otx2_txschq_free_one(struct otx2_nic *pfvf, u16 lvl, u16 schq) { struct nix_txsch_free_req *free_req; - int lvl, schq, err; + int err; mutex_lock(&pfvf->mbox.lock); - /* Free the transmit schedulers */ + free_req = otx2_mbox_alloc_msg_nix_txsch_free(&pfvf->mbox); if (!free_req) { mutex_unlock(&pfvf->mbox.lock); - return -ENOMEM; + netdev_err(pfvf->netdev, + "Failed alloc txschq free req\n"); + return; } - free_req->flags = TXSCHQ_FREE_ALL; + free_req->schq_lvl = lvl; + free_req->schq = schq; + err = otx2_sync_mbox_msg(&pfvf->mbox); + if (err) { + netdev_err(pfvf->netdev, + "Failed stop txschq %d at level %d\n", schq, lvl); + } + mutex_unlock(&pfvf->mbox.lock); +} + +void otx2_txschq_stop(struct otx2_nic *pfvf) +{ + int lvl, schq; + + /* free non QOS TLx nodes */ + for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) + otx2_txschq_free_one(pfvf, lvl, + pfvf->hw.txschq_list[lvl][0]); /* Clear the txschq list */ for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) { for (schq = 0; schq < MAX_TXSCHQ_PER_FUNC; schq++) pfvf->hw.txschq_list[lvl][schq] = 0; } - return err; + } void otx2_sqb_flush(struct otx2_nic *pfvf) { int qidx, sqe_tail, sqe_head; + struct otx2_snd_queue *sq; u64 incr, *ptr, val; int timeout = 1000; ptr = (u64 *)otx2_get_regaddr(pfvf, NIX_LF_SQ_OP_STATUS); - for (qidx = 0; qidx < pfvf->hw.tot_tx_queues; qidx++) { + for (qidx = 0; qidx < otx2_get_total_tx_queues(pfvf); qidx++) { + sq = &pfvf->qset.sq[qidx]; + if (!sq->sqb_ptrs) + continue; + incr = (u64)qidx << 32; while (timeout) { val = otx2_atomic64_add(incr, ptr); @@ -862,7 +908,7 @@ int otx2_sq_aq_init(void *dev, u16 qidx, u16 sqb_aura) return otx2_sync_mbox_msg(&pfvf->mbox); } -static int otx2_sq_init(struct otx2_nic *pfvf, u16 qidx, u16 sqb_aura) +int otx2_sq_init(struct otx2_nic *pfvf, u16 qidx, u16 sqb_aura) { struct otx2_qset *qset = &pfvf->qset; struct otx2_snd_queue *sq; @@ -935,9 +981,17 @@ static int otx2_cq_init(struct otx2_nic *pfvf, u16 qidx) cq->cint_idx = qidx - pfvf->hw.rx_queues; cq->cqe_cnt = qset->sqe_cnt; } else { - cq->cq_type = CQ_XDP; - cq->cint_idx = qidx - non_xdp_queues; - cq->cqe_cnt = qset->sqe_cnt; + if (pfvf->hw.xdp_queues && + qidx < non_xdp_queues + pfvf->hw.xdp_queues) { + cq->cq_type = CQ_XDP; + cq->cint_idx = qidx - non_xdp_queues; + cq->cqe_cnt = qset->sqe_cnt; + } else { + cq->cq_type = CQ_QOS; + cq->cint_idx = qidx - non_xdp_queues - + pfvf->hw.xdp_queues; + cq->cqe_cnt = qset->sqe_cnt; + } } cq->cqe_size = pfvf->qset.xqe_size; @@ -1048,7 +1102,7 @@ int otx2_config_nix_queues(struct otx2_nic *pfvf) } /* Initialize TX queues */ - for (qidx = 0; qidx < pfvf->hw.tot_tx_queues; qidx++) { + for (qidx = 0; qidx < pfvf->hw.non_qos_queues; qidx++) { u16 sqb_aura = otx2_get_pool_idx(pfvf, AURA_NIX_SQ, qidx); err = otx2_sq_init(pfvf, qidx, sqb_aura); @@ -1095,7 +1149,7 @@ int otx2_config_nix(struct otx2_nic *pfvf) /* Set RQ/SQ/CQ counts */ nixlf->rq_cnt = pfvf->hw.rx_queues; - nixlf->sq_cnt = pfvf->hw.tot_tx_queues; + nixlf->sq_cnt = otx2_get_total_tx_queues(pfvf); nixlf->cq_cnt = pfvf->qset.cq_cnt; nixlf->rss_sz = MAX_RSS_INDIR_TBL_SIZE; nixlf->rss_grps = MAX_RSS_GROUPS; @@ -1133,7 +1187,7 @@ void otx2_sq_free_sqbs(struct otx2_nic *pfvf) int sqb, qidx; u64 iova, pa; - for (qidx = 0; qidx < hw->tot_tx_queues; qidx++) { + for (qidx = 0; qidx < otx2_get_total_tx_queues(pfvf); qidx++) { sq = &qset->sq[qidx]; if (!sq->sqb_ptrs) continue; @@ -1201,8 +1255,8 @@ void otx2_aura_pool_free(struct otx2_nic *pfvf) pfvf->qset.pool = NULL; } -static int otx2_aura_init(struct otx2_nic *pfvf, int aura_id, - int pool_id, int numptrs) +int otx2_aura_init(struct otx2_nic *pfvf, int aura_id, + int pool_id, int numptrs) { struct npa_aq_enq_req *aq; struct otx2_pool *pool; @@ -1278,8 +1332,8 @@ static int otx2_aura_init(struct otx2_nic *pfvf, int aura_id, return 0; } -static int otx2_pool_init(struct otx2_nic *pfvf, u16 pool_id, - int stack_pages, int numptrs, int buf_size) +int otx2_pool_init(struct otx2_nic *pfvf, u16 pool_id, + int stack_pages, int numptrs, int buf_size) { struct npa_aq_enq_req *aq; struct otx2_pool *pool; @@ -1349,7 +1403,7 @@ int otx2_sq_aura_pool_init(struct otx2_nic *pfvf) stack_pages = (num_sqbs + hw->stack_pg_ptrs - 1) / hw->stack_pg_ptrs; - for (qidx = 0; qidx < hw->tot_tx_queues; qidx++) { + for (qidx = 0; qidx < hw->non_qos_queues; qidx++) { pool_id = otx2_get_pool_idx(pfvf, AURA_NIX_SQ, qidx); /* Initialize aura context */ err = otx2_aura_init(pfvf, pool_id, pool_id, num_sqbs); @@ -1369,7 +1423,7 @@ int otx2_sq_aura_pool_init(struct otx2_nic *pfvf) goto fail; /* Allocate pointers and free them to aura/pool */ - for (qidx = 0; qidx < hw->tot_tx_queues; qidx++) { + for (qidx = 0; qidx < hw->non_qos_queues; qidx++) { pool_id = otx2_get_pool_idx(pfvf, AURA_NIX_SQ, qidx); pool = &pfvf->qset.pool[pool_id]; @@ -1629,21 +1683,6 @@ void mbox_handler_cgx_fec_stats(struct otx2_nic *pfvf, pfvf->hw.cgx_fec_uncorr_blks += rsp->fec_uncorr_blks; } -void mbox_handler_nix_txsch_alloc(struct otx2_nic *pf, - struct nix_txsch_alloc_rsp *rsp) -{ - int lvl, schq; - - /* Setup transmit scheduler list */ - for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) - for (schq = 0; schq < rsp->schq[lvl]; schq++) - pf->hw.txschq_list[lvl][schq] = - rsp->schq_list[lvl][schq]; - - pf->hw.txschq_link_cfg_lvl = rsp->link_cfg_lvl; -} -EXPORT_SYMBOL(mbox_handler_nix_txsch_alloc); - void mbox_handler_npa_lf_alloc(struct otx2_nic *pfvf, struct npa_lf_alloc_rsp *rsp) { diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h index 0c8fc66ade82..b2267c8bec37 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h @@ -15,6 +15,7 @@ #include <linux/ptp_clock_kernel.h> #include <linux/timecounter.h> #include <linux/soc/marvell/octeontx2/asm.h> +#include <net/macsec.h> #include <net/pkt_cls.h> #include <net/devlink.h> #include <linux/time64.h> @@ -27,6 +28,7 @@ #include "otx2_txrx.h" #include "otx2_devlink.h" #include <rvu_trace.h> +#include "qos.h" /* IPv4 flag more fragment bit */ #define IPV4_FLAG_MORE 0x20 @@ -183,13 +185,29 @@ struct mbox { int up_num_msgs; /* mbox_up number of messages */ }; +/* Egress rate limiting definitions */ +#define MAX_BURST_EXPONENT 0x0FULL +#define MAX_BURST_MANTISSA 0xFFULL +#define MAX_BURST_SIZE 130816ULL +#define MAX_RATE_DIVIDER_EXPONENT 12ULL +#define MAX_RATE_EXPONENT 0x0FULL +#define MAX_RATE_MANTISSA 0xFFULL + +/* Bitfields in NIX_TLX_PIR register */ +#define TLX_RATE_MANTISSA GENMASK_ULL(8, 1) +#define TLX_RATE_EXPONENT GENMASK_ULL(12, 9) +#define TLX_RATE_DIVIDER_EXPONENT GENMASK_ULL(16, 13) +#define TLX_BURST_MANTISSA GENMASK_ULL(36, 29) +#define TLX_BURST_EXPONENT GENMASK_ULL(40, 37) + struct otx2_hw { struct pci_dev *pdev; struct otx2_rss_info rss_info; u16 rx_queues; u16 tx_queues; u16 xdp_queues; - u16 tot_tx_queues; + u16 tc_tx_queues; + u16 non_qos_queues; /* tx queues plus xdp queues */ u16 max_queues; u16 pool_cnt; u16 rqpool_cnt; @@ -250,6 +268,7 @@ struct otx2_hw { #define CN10K_RPM 3 #define CN10K_PTP_ONESTEP 4 #define CN10K_HW_MACSEC 5 +#define QOS_CIR_PIR_SUPPORT 6 unsigned long cap_flag; #define LMT_LINE_SIZE 128 @@ -398,6 +417,9 @@ struct cn10k_mcs_txsc { u8 sa_bmap; u8 sa_key[CN10K_MCS_SA_PER_SC][MACSEC_MAX_KEY_LEN]; u8 encoding_sa; + u8 salt[CN10K_MCS_SA_PER_SC][MACSEC_SALT_LEN]; + ssci_t ssci[CN10K_MCS_SA_PER_SC]; + bool vlan_dev; /* macsec running on VLAN ? */ }; struct cn10k_mcs_rxsc { @@ -410,6 +432,8 @@ struct cn10k_mcs_rxsc { u16 hw_sa_id[CN10K_MCS_SA_PER_SC]; u8 sa_bmap; u8 sa_key[CN10K_MCS_SA_PER_SC][MACSEC_MAX_KEY_LEN]; + u8 salt[CN10K_MCS_SA_PER_SC][MACSEC_SALT_LEN]; + ssci_t ssci[CN10K_MCS_SA_PER_SC]; }; struct cn10k_mcs_cfg { @@ -501,6 +525,8 @@ struct otx2_nic { u16 pfc_schq_list[NIX_TXSCH_LVL_CNT][MAX_TXSCHQ_PER_FUNC]; bool pfc_alloc_status[NIX_PF_PFC_PRIO_MAX]; #endif + /* qos */ + struct otx2_qos qos; /* napi event count. It is needed for adaptive irq coalescing. */ u32 napi_events; @@ -582,6 +608,7 @@ static inline void otx2_setup_dev_hw_settings(struct otx2_nic *pfvf) __set_bit(CN10K_LMTST, &hw->cap_flag); __set_bit(CN10K_RPM, &hw->cap_flag); __set_bit(CN10K_PTP_ONESTEP, &hw->cap_flag); + __set_bit(QOS_CIR_PIR_SUPPORT, &hw->cap_flag); } if (is_dev_cn10kb(pfvf->pdev)) @@ -745,8 +772,7 @@ static inline void cn10k_aura_freeptr(void *dev, int aura, u64 buf) /* Alloc pointer from pool/aura */ static inline u64 otx2_aura_allocptr(struct otx2_nic *pfvf, int aura) { - u64 *ptr = (u64 *)otx2_get_regaddr(pfvf, - NPA_LF_AURA_OP_ALLOCX(0)); + u64 *ptr = (__force u64 *)otx2_get_regaddr(pfvf, NPA_LF_AURA_OP_ALLOCX(0)); u64 incr = (u64)aura | BIT_ULL(63); return otx2_atomic64_add(incr, ptr); @@ -888,12 +914,34 @@ static inline void otx2_dma_unmap_page(struct otx2_nic *pfvf, static inline u16 otx2_get_smq_idx(struct otx2_nic *pfvf, u16 qidx) { + u16 smq; #ifdef CONFIG_DCB if (qidx < NIX_PF_PFC_PRIO_MAX && pfvf->pfc_alloc_status[qidx]) return pfvf->pfc_schq_list[NIX_TXSCH_LVL_SMQ][qidx]; #endif + /* check if qidx falls under QOS queues */ + if (qidx >= pfvf->hw.non_qos_queues) + smq = pfvf->qos.qid_to_sqmap[qidx - pfvf->hw.non_qos_queues]; + else + smq = pfvf->hw.txschq_list[NIX_TXSCH_LVL_SMQ][0]; + + return smq; +} + +static inline u16 otx2_get_total_tx_queues(struct otx2_nic *pfvf) +{ + return pfvf->hw.non_qos_queues + pfvf->hw.tc_tx_queues; +} + +static inline u64 otx2_convert_rate(u64 rate) +{ + u64 converted_rate; + + /* Convert bytes per second to Mbps */ + converted_rate = rate * 8; + converted_rate = max_t(u64, converted_rate / 1000000, 1); - return pfvf->hw.txschq_list[NIX_TXSCH_LVL_SMQ][0]; + return converted_rate; } /* MSI-X APIs */ @@ -920,19 +968,25 @@ int otx2_config_nix(struct otx2_nic *pfvf); int otx2_config_nix_queues(struct otx2_nic *pfvf); int otx2_txschq_config(struct otx2_nic *pfvf, int lvl, int prio, bool pfc_en); int otx2_txsch_alloc(struct otx2_nic *pfvf); -int otx2_txschq_stop(struct otx2_nic *pfvf); +void otx2_txschq_stop(struct otx2_nic *pfvf); +void otx2_txschq_free_one(struct otx2_nic *pfvf, u16 lvl, u16 schq); void otx2_sqb_flush(struct otx2_nic *pfvf); -int __otx2_alloc_rbuf(struct otx2_nic *pfvf, struct otx2_pool *pool, - dma_addr_t *dma); +int otx2_alloc_rbuf(struct otx2_nic *pfvf, struct otx2_pool *pool, + dma_addr_t *dma); int otx2_rxtx_enable(struct otx2_nic *pfvf, bool enable); void otx2_ctx_disable(struct mbox *mbox, int type, bool npa); int otx2_nix_config_bp(struct otx2_nic *pfvf, bool enable); void otx2_cleanup_rx_cqes(struct otx2_nic *pfvf, struct otx2_cq_queue *cq); void otx2_cleanup_tx_cqes(struct otx2_nic *pfvf, struct otx2_cq_queue *cq); +int otx2_sq_init(struct otx2_nic *pfvf, u16 qidx, u16 sqb_aura); int otx2_sq_aq_init(void *dev, u16 qidx, u16 sqb_aura); int cn10k_sq_aq_init(void *dev, u16 qidx, u16 sqb_aura); int otx2_alloc_buffer(struct otx2_nic *pfvf, struct otx2_cq_queue *cq, dma_addr_t *dma); +int otx2_pool_init(struct otx2_nic *pfvf, u16 pool_id, + int stack_pages, int numptrs, int buf_size); +int otx2_aura_init(struct otx2_nic *pfvf, int aura_id, + int pool_id, int numptrs); /* RSS configuration APIs*/ int otx2_rss_init(struct otx2_nic *pfvf); @@ -1040,4 +1094,24 @@ static inline void cn10k_handle_mcs_event(struct otx2_nic *pfvf, {} #endif /* CONFIG_MACSEC */ +/* qos support */ +static inline void otx2_qos_init(struct otx2_nic *pfvf, int qos_txqs) +{ + struct otx2_hw *hw = &pfvf->hw; + + hw->tc_tx_queues = qos_txqs; + INIT_LIST_HEAD(&pfvf->qos.qos_tree); + mutex_init(&pfvf->qos.qos_lock); +} + +static inline void otx2_shutdown_qos(struct otx2_nic *pfvf) +{ + mutex_destroy(&pfvf->qos.qos_lock); +} + +u16 otx2_select_queue(struct net_device *netdev, struct sk_buff *skb, + struct net_device *sb_dev); +int otx2_get_txq_by_classid(struct otx2_nic *pfvf, u16 classid); +void otx2_qos_config_txschq(struct otx2_nic *pfvf); +void otx2_clean_qos_queues(struct otx2_nic *pfvf); #endif /* OTX2_COMMON_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c index 0f8d1a69139f..c47d91da32dc 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c @@ -92,10 +92,16 @@ static void otx2_get_qset_strings(struct otx2_nic *pfvf, u8 **data, int qset) *data += ETH_GSTRING_LEN; } } - for (qidx = 0; qidx < pfvf->hw.tx_queues; qidx++) { + + for (qidx = 0; qidx < otx2_get_total_tx_queues(pfvf); qidx++) { for (stats = 0; stats < otx2_n_queue_stats; stats++) { - sprintf(*data, "txq%d: %s", qidx + start_qidx, - otx2_queue_stats[stats].name); + if (qidx >= pfvf->hw.non_qos_queues) + sprintf(*data, "txq_qos%d: %s", + qidx + start_qidx - pfvf->hw.non_qos_queues, + otx2_queue_stats[stats].name); + else + sprintf(*data, "txq%d: %s", qidx + start_qidx, + otx2_queue_stats[stats].name); *data += ETH_GSTRING_LEN; } } @@ -159,7 +165,7 @@ static void otx2_get_qset_stats(struct otx2_nic *pfvf, [otx2_queue_stats[stat].index]; } - for (qidx = 0; qidx < pfvf->hw.tx_queues; qidx++) { + for (qidx = 0; qidx < otx2_get_total_tx_queues(pfvf); qidx++) { if (!otx2_update_sq_stats(pfvf, qidx)) { for (stat = 0; stat < otx2_n_queue_stats; stat++) *((*data)++) = 0; @@ -254,7 +260,7 @@ static int otx2_get_sset_count(struct net_device *netdev, int sset) return -EINVAL; qstats_count = otx2_n_queue_stats * - (pfvf->hw.rx_queues + pfvf->hw.tx_queues); + (pfvf->hw.rx_queues + otx2_get_total_tx_queues(pfvf)); if (!test_bit(CN10K_RPM, &pfvf->hw.cap_flag)) mac_stats = CGX_RX_STATS_COUNT + CGX_TX_STATS_COUNT; otx2_update_lmac_fec_stats(pfvf); @@ -282,7 +288,7 @@ static int otx2_set_channels(struct net_device *dev, { struct otx2_nic *pfvf = netdev_priv(dev); bool if_up = netif_running(dev); - int err = 0; + int err, qos_txqs; if (!channel->rx_count || !channel->tx_count) return -EINVAL; @@ -296,14 +302,19 @@ static int otx2_set_channels(struct net_device *dev, if (if_up) dev->netdev_ops->ndo_stop(dev); - err = otx2_set_real_num_queues(dev, channel->tx_count, + qos_txqs = bitmap_weight(pfvf->qos.qos_sq_bmap, + OTX2_QOS_MAX_LEAF_NODES); + + err = otx2_set_real_num_queues(dev, channel->tx_count + qos_txqs, channel->rx_count); if (err) return err; pfvf->hw.rx_queues = channel->rx_count; pfvf->hw.tx_queues = channel->tx_count; - pfvf->qset.cq_cnt = pfvf->hw.tx_queues + pfvf->hw.rx_queues; + if (pfvf->xdp_prog) + pfvf->hw.xdp_queues = channel->rx_count; + pfvf->hw.non_qos_queues = pfvf->hw.tx_queues + pfvf->hw.xdp_queues; if (if_up) err = dev->netdev_ops->ndo_open(dev); @@ -1405,7 +1416,7 @@ static int otx2vf_get_sset_count(struct net_device *netdev, int sset) return -EINVAL; qstats_count = otx2_n_queue_stats * - (vf->hw.rx_queues + vf->hw.tx_queues); + (vf->hw.rx_queues + otx2_get_total_tx_queues(vf)); return otx2_n_dev_stats + otx2_n_drv_stats + qstats_count + 1; } diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index 18284ad75157..e1883c3edda3 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -23,6 +23,7 @@ #include "otx2_struct.h" #include "otx2_ptp.h" #include "cn10k.h" +#include "qos.h" #include <rvu_trace.h> #define DRV_NAME "rvu_nicpf" @@ -791,10 +792,6 @@ static void otx2_process_pfaf_mbox_msg(struct otx2_nic *pf, case MBOX_MSG_NIX_LF_ALLOC: mbox_handler_nix_lf_alloc(pf, (struct nix_lf_alloc_rsp *)msg); break; - case MBOX_MSG_NIX_TXSCH_ALLOC: - mbox_handler_nix_txsch_alloc(pf, - (struct nix_txsch_alloc_rsp *)msg); - break; case MBOX_MSG_NIX_BP_ENABLE: mbox_handler_nix_bp_enable(pf, (struct nix_bp_cfg_rsp *)msg); break; @@ -1228,6 +1225,7 @@ static char *nix_snd_status_e_str[NIX_SND_STATUS_MAX] = { static irqreturn_t otx2_q_intr_handler(int irq, void *data) { struct otx2_nic *pf = data; + struct otx2_snd_queue *sq; u64 val, *ptr; u64 qidx = 0; @@ -1257,10 +1255,14 @@ static irqreturn_t otx2_q_intr_handler(int irq, void *data) } /* SQ */ - for (qidx = 0; qidx < pf->hw.tot_tx_queues; qidx++) { + for (qidx = 0; qidx < otx2_get_total_tx_queues(pf); qidx++) { u64 sq_op_err_dbg, mnq_err_dbg, snd_err_dbg; u8 sq_op_err_code, mnq_err_code, snd_err_code; + sq = &pf->qset.sq[qidx]; + if (!sq->sqb_ptrs) + continue; + /* Below debug registers captures first errors corresponding to * those registers. We don't have to check against SQ qid as * these are fatal errors. @@ -1383,8 +1385,11 @@ static void otx2_free_sq_res(struct otx2_nic *pf) otx2_ctx_disable(&pf->mbox, NIX_AQ_CTYPE_SQ, false); /* Free SQB pointers */ otx2_sq_free_sqbs(pf); - for (qidx = 0; qidx < pf->hw.tot_tx_queues; qidx++) { + for (qidx = 0; qidx < otx2_get_total_tx_queues(pf); qidx++) { sq = &qset->sq[qidx]; + /* Skip freeing Qos queues if they are not initialized */ + if (!sq->sqe) + continue; qmem_free(pf->dev, sq->sqe); qmem_free(pf->dev, sq->tso_hdrs); kfree(sq->sg); @@ -1433,7 +1438,7 @@ static int otx2_init_hw_resources(struct otx2_nic *pf) * so, aura count = pool count. */ hw->rqpool_cnt = hw->rx_queues; - hw->sqpool_cnt = hw->tot_tx_queues; + hw->sqpool_cnt = otx2_get_total_tx_queues(pf); hw->pool_cnt = hw->rqpool_cnt + hw->sqpool_cnt; /* Maximum hardware supported transmit length */ @@ -1516,8 +1521,7 @@ err_free_nix_queues: otx2_free_cq_res(pf); otx2_ctx_disable(mbox, NIX_AQ_CTYPE_RQ, false); err_free_txsch: - if (otx2_txschq_stop(pf)) - dev_err(pf->dev, "%s failed to stop TX schedulers\n", __func__); + otx2_txschq_stop(pf); err_free_sq_ptrs: otx2_sq_free_sqbs(pf); err_free_rq_ptrs: @@ -1552,21 +1556,21 @@ static void otx2_free_hw_resources(struct otx2_nic *pf) struct mbox *mbox = &pf->mbox; struct otx2_cq_queue *cq; struct msg_req *req; - int qidx, err; + int qidx; /* Ensure all SQE are processed */ otx2_sqb_flush(pf); /* Stop transmission */ - err = otx2_txschq_stop(pf); - if (err) - dev_err(pf->dev, "RVUPF: Failed to stop/free TX schedulers\n"); + otx2_txschq_stop(pf); #ifdef CONFIG_DCB if (pf->pfc_en) otx2_pfc_txschq_stop(pf); #endif + otx2_clean_qos_queues(pf); + mutex_lock(&mbox->lock); /* Disable backpressure */ if (!(pf->pcifunc & RVU_PFVF_FUNC_MASK)) @@ -1688,11 +1692,14 @@ int otx2_open(struct net_device *netdev) netif_carrier_off(netdev); - pf->qset.cq_cnt = pf->hw.rx_queues + pf->hw.tot_tx_queues; /* RQ and SQs are mapped to different CQs, * so find out max CQ IRQs (i.e CINTs) needed. */ - pf->hw.cint_cnt = max(pf->hw.rx_queues, pf->hw.tx_queues); + pf->hw.cint_cnt = max3(pf->hw.rx_queues, pf->hw.tx_queues, + pf->hw.tc_tx_queues); + + pf->qset.cq_cnt = pf->hw.rx_queues + otx2_get_total_tx_queues(pf); + qset->napi = kcalloc(pf->hw.cint_cnt, sizeof(*cq_poll), GFP_KERNEL); if (!qset->napi) return -ENOMEM; @@ -1708,7 +1715,7 @@ int otx2_open(struct net_device *netdev) if (!qset->cq) goto err_free_mem; - qset->sq = kcalloc(pf->hw.tot_tx_queues, + qset->sq = kcalloc(otx2_get_total_tx_queues(pf), sizeof(struct otx2_snd_queue), GFP_KERNEL); if (!qset->sq) goto err_free_mem; @@ -1743,6 +1750,11 @@ int otx2_open(struct net_device *netdev) else cq_poll->cq_ids[CQ_XDP] = CINT_INVALID_CQ; + cq_poll->cq_ids[CQ_QOS] = (qidx < pf->hw.tc_tx_queues) ? + (qidx + pf->hw.rx_queues + + pf->hw.non_qos_queues) : + CINT_INVALID_CQ; + cq_poll->dev = (void *)pf; cq_poll->dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_CQE; INIT_WORK(&cq_poll->dim.work, otx2_dim_work); @@ -1826,6 +1838,9 @@ int otx2_open(struct net_device *netdev) /* 'intf_down' may be checked on any cpu */ smp_wmb(); + /* Enable QoS configuration before starting tx queues */ + otx2_qos_config_txschq(pf); + /* we have already received link status notification */ if (pf->linfo.link_up && !(pf->pcifunc & RVU_PFVF_FUNC_MASK)) otx2_handle_link_event(pf); @@ -1947,6 +1962,12 @@ static netdev_tx_t otx2_xmit(struct sk_buff *skb, struct net_device *netdev) int qidx = skb_get_queue_mapping(skb); struct otx2_snd_queue *sq; struct netdev_queue *txq; + int sq_idx; + + /* XDP SQs are not mapped with TXQs + * advance qid to derive correct sq mapped with QOS + */ + sq_idx = (qidx >= pf->hw.tx_queues) ? (qidx + pf->hw.xdp_queues) : qidx; /* Check for minimum and maximum packet length */ if (skb->len <= ETH_HLEN || @@ -1955,7 +1976,7 @@ static netdev_tx_t otx2_xmit(struct sk_buff *skb, struct net_device *netdev) return NETDEV_TX_OK; } - sq = &pf->qset.sq[qidx]; + sq = &pf->qset.sq[sq_idx]; txq = netdev_get_tx_queue(netdev, qidx); if (!otx2_sq_append_skb(netdev, sq, skb, qidx)) { @@ -1973,14 +1994,48 @@ static netdev_tx_t otx2_xmit(struct sk_buff *skb, struct net_device *netdev) return NETDEV_TX_OK; } -static u16 otx2_select_queue(struct net_device *netdev, struct sk_buff *skb, - struct net_device *sb_dev) +static int otx2_qos_select_htb_queue(struct otx2_nic *pf, struct sk_buff *skb, + u16 htb_maj_id) +{ + u16 classid; + + if ((TC_H_MAJ(skb->priority) >> 16) == htb_maj_id) + classid = TC_H_MIN(skb->priority); + else + classid = READ_ONCE(pf->qos.defcls); + + if (!classid) + return 0; + + return otx2_get_txq_by_classid(pf, classid); +} + +u16 otx2_select_queue(struct net_device *netdev, struct sk_buff *skb, + struct net_device *sb_dev) { -#ifdef CONFIG_DCB struct otx2_nic *pf = netdev_priv(netdev); + bool qos_enabled; +#ifdef CONFIG_DCB u8 vlan_prio; #endif + int txq; + qos_enabled = (netdev->real_num_tx_queues > pf->hw.tx_queues) ? true : false; + if (unlikely(qos_enabled)) { + /* This smp_load_acquire() pairs with smp_store_release() in + * otx2_qos_root_add() called from htb offload root creation + */ + u16 htb_maj_id = smp_load_acquire(&pf->qos.maj_id); + + if (unlikely(htb_maj_id)) { + txq = otx2_qos_select_htb_queue(pf, skb, htb_maj_id); + if (txq > 0) + return txq; + goto process_pfc; + } + } + +process_pfc: #ifdef CONFIG_DCB if (!skb_vlan_tag_present(skb)) goto pick_tx; @@ -1994,8 +2049,13 @@ static u16 otx2_select_queue(struct net_device *netdev, struct sk_buff *skb, pick_tx: #endif - return netdev_pick_tx(netdev, skb, NULL); + txq = netdev_pick_tx(netdev, skb, NULL); + if (unlikely(qos_enabled)) + return txq % pf->hw.tx_queues; + + return txq; } +EXPORT_SYMBOL(otx2_select_queue); static netdev_features_t otx2_fix_features(struct net_device *dev, netdev_features_t features) @@ -2529,7 +2589,7 @@ static int otx2_xdp_setup(struct otx2_nic *pf, struct bpf_prog *prog) xdp_features_clear_redirect_target(dev); } - pf->hw.tot_tx_queues += pf->hw.xdp_queues; + pf->hw.non_qos_queues += pf->hw.xdp_queues; if (if_up) otx2_open(pf->netdev); @@ -2712,10 +2772,10 @@ static void otx2_sriov_vfcfg_cleanup(struct otx2_nic *pf) static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct device *dev = &pdev->dev; + int err, qcount, qos_txqs; struct net_device *netdev; struct otx2_nic *pf; struct otx2_hw *hw; - int err, qcount; int num_vec; err = pcim_enable_device(pdev); @@ -2740,8 +2800,9 @@ static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id) /* Set number of queues */ qcount = min_t(int, num_online_cpus(), OTX2_MAX_CQ_CNT); + qos_txqs = min_t(int, qcount, OTX2_QOS_MAX_LEAF_NODES); - netdev = alloc_etherdev_mqs(sizeof(*pf), qcount, qcount); + netdev = alloc_etherdev_mqs(sizeof(*pf), qcount + qos_txqs, qcount); if (!netdev) { err = -ENOMEM; goto err_release_regions; @@ -2760,7 +2821,7 @@ static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id) hw->pdev = pdev; hw->rx_queues = qcount; hw->tx_queues = qcount; - hw->tot_tx_queues = qcount; + hw->non_qos_queues = qcount; hw->max_queues = qcount; hw->rbuf_len = OTX2_DEFAULT_RBUF_LEN; /* Use CQE of 128 byte descriptor size by default */ @@ -2929,6 +2990,8 @@ static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err_pf_sriov_init; #endif + otx2_qos_init(pf, qos_txqs); + return 0; err_pf_sriov_init: @@ -3104,6 +3167,7 @@ static void otx2_remove(struct pci_dev *pdev) otx2_ptp_destroy(pf); otx2_mcam_flow_del(pf); otx2_shutdown_tc(pf); + otx2_shutdown_qos(pf); otx2_detach_resources(&pf->mbox); if (pf->hw.lmt_info) free_percpu(pf->hw.lmt_info); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_reg.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_reg.h index 1b967eaf948b..45a32e4b49d1 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_reg.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_reg.h @@ -145,12 +145,25 @@ #define NIX_AF_TL1X_TOPOLOGY(a) (0xC80 | (a) << 16) #define NIX_AF_TL2X_PARENT(a) (0xE88 | (a) << 16) #define NIX_AF_TL2X_SCHEDULE(a) (0xE00 | (a) << 16) +#define NIX_AF_TL2X_TOPOLOGY(a) (0xE80 | (a) << 16) +#define NIX_AF_TL2X_CIR(a) (0xE20 | (a) << 16) +#define NIX_AF_TL2X_PIR(a) (0xE30 | (a) << 16) #define NIX_AF_TL3X_PARENT(a) (0x1088 | (a) << 16) #define NIX_AF_TL3X_SCHEDULE(a) (0x1000 | (a) << 16) +#define NIX_AF_TL3X_SHAPE(a) (0x1010 | (a) << 16) +#define NIX_AF_TL3X_CIR(a) (0x1020 | (a) << 16) +#define NIX_AF_TL3X_PIR(a) (0x1030 | (a) << 16) +#define NIX_AF_TL3X_TOPOLOGY(a) (0x1080 | (a) << 16) #define NIX_AF_TL4X_PARENT(a) (0x1288 | (a) << 16) #define NIX_AF_TL4X_SCHEDULE(a) (0x1200 | (a) << 16) +#define NIX_AF_TL4X_SHAPE(a) (0x1210 | (a) << 16) +#define NIX_AF_TL4X_CIR(a) (0x1220 | (a) << 16) #define NIX_AF_TL4X_PIR(a) (0x1230 | (a) << 16) +#define NIX_AF_TL4X_TOPOLOGY(a) (0x1280 | (a) << 16) #define NIX_AF_MDQX_SCHEDULE(a) (0x1400 | (a) << 16) +#define NIX_AF_MDQX_SHAPE(a) (0x1410 | (a) << 16) +#define NIX_AF_MDQX_CIR(a) (0x1420 | (a) << 16) +#define NIX_AF_MDQX_PIR(a) (0x1430 | (a) << 16) #define NIX_AF_MDQX_PARENT(a) (0x1480 | (a) << 16) #define NIX_AF_TL3_TL2X_LINKX_CFG(a, b) (0x1700 | (a) << 16 | (b) << 3) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c index 8392f63e433f..231c3f0efb60 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c @@ -19,25 +19,11 @@ #include "cn10k.h" #include "otx2_common.h" - -/* Egress rate limiting definitions */ -#define MAX_BURST_EXPONENT 0x0FULL -#define MAX_BURST_MANTISSA 0xFFULL -#define MAX_BURST_SIZE 130816ULL -#define MAX_RATE_DIVIDER_EXPONENT 12ULL -#define MAX_RATE_EXPONENT 0x0FULL -#define MAX_RATE_MANTISSA 0xFFULL +#include "qos.h" #define CN10K_MAX_BURST_MANTISSA 0x7FFFULL #define CN10K_MAX_BURST_SIZE 8453888ULL -/* Bitfields in NIX_TLX_PIR register */ -#define TLX_RATE_MANTISSA GENMASK_ULL(8, 1) -#define TLX_RATE_EXPONENT GENMASK_ULL(12, 9) -#define TLX_RATE_DIVIDER_EXPONENT GENMASK_ULL(16, 13) -#define TLX_BURST_MANTISSA GENMASK_ULL(36, 29) -#define TLX_BURST_EXPONENT GENMASK_ULL(40, 37) - #define CN10K_TLX_BURST_MANTISSA GENMASK_ULL(43, 29) #define CN10K_TLX_BURST_EXPONENT GENMASK_ULL(47, 44) @@ -147,8 +133,8 @@ static void otx2_get_egress_rate_cfg(u64 maxrate, u32 *exp, } } -static u64 otx2_get_txschq_rate_regval(struct otx2_nic *nic, - u64 maxrate, u32 burst) +u64 otx2_get_txschq_rate_regval(struct otx2_nic *nic, + u64 maxrate, u32 burst) { u32 burst_exp, burst_mantissa; u32 exp, mantissa, div_exp; @@ -264,7 +250,6 @@ static int otx2_tc_egress_matchall_install(struct otx2_nic *nic, struct netlink_ext_ack *extack = cls->common.extack; struct flow_action *actions = &cls->rule->action; struct flow_action_entry *entry; - u64 rate; int err; err = otx2_tc_validate_flow(nic, actions, extack); @@ -288,10 +273,8 @@ static int otx2_tc_egress_matchall_install(struct otx2_nic *nic, NL_SET_ERR_MSG_MOD(extack, "QoS offload not support packets per second"); return -EOPNOTSUPP; } - /* Convert bytes per second to Mbps */ - rate = entry->police.rate_bytes_ps * 8; - rate = max_t(u64, rate / 1000000, 1); - err = otx2_set_matchall_egress_rate(nic, entry->police.burst, rate); + err = otx2_set_matchall_egress_rate(nic, entry->police.burst, + otx2_convert_rate(entry->police.rate_bytes_ps)); if (err) return err; nic->flags |= OTX2_FLAG_TC_MATCHALL_EGRESS_ENABLED; @@ -1127,6 +1110,8 @@ int otx2_setup_tc(struct net_device *netdev, enum tc_setup_type type, switch (type) { case TC_SETUP_BLOCK: return otx2_setup_tc_block(netdev, type_data); + case TC_SETUP_QDISC_HTB: + return otx2_setup_tc_htb(netdev, type_data); default: return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c index 7045fedfd73a..e288f46b23a8 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c @@ -464,12 +464,13 @@ process_cqe: break; } - if (cq->cq_type == CQ_XDP) { + qidx = cq->cq_idx - pfvf->hw.rx_queues; + + if (cq->cq_type == CQ_XDP) otx2_xdp_snd_pkt_handler(pfvf, sq, cqe); - } else { - otx2_snd_pkt_handler(pfvf, cq, sq, cqe, budget, - &tx_pkts, &tx_bytes); - } + else + otx2_snd_pkt_handler(pfvf, cq, &pfvf->qset.sq[qidx], + cqe, budget, &tx_pkts, &tx_bytes); cqe->hdr.cqe_type = NIX_XQE_TYPE_INVALID; processed_cqe++; @@ -486,7 +487,11 @@ process_cqe: if (likely(tx_pkts)) { struct netdev_queue *txq; - txq = netdev_get_tx_queue(pfvf->netdev, cq->cint_idx); + qidx = cq->cq_idx - pfvf->hw.rx_queues; + + if (qidx >= pfvf->hw.tx_queues) + qidx -= pfvf->hw.xdp_queues; + txq = netdev_get_tx_queue(pfvf->netdev, qidx); netdev_tx_completed_queue(txq, tx_pkts, tx_bytes); /* Check if queue was stopped earlier due to ring full */ smp_mb(); @@ -736,7 +741,8 @@ static void otx2_sqe_add_hdr(struct otx2_nic *pfvf, struct otx2_snd_queue *sq, sqe_hdr->aura = sq->aura_id; /* Post a CQE Tx after pkt transmission */ sqe_hdr->pnc = 1; - sqe_hdr->sq = qidx; + sqe_hdr->sq = (qidx >= pfvf->hw.tx_queues) ? + qidx + pfvf->hw.xdp_queues : qidx; } sqe_hdr->total = skb->len; /* Set SQE identifier which will be used later for freeing SKB */ @@ -1221,8 +1227,10 @@ void otx2_cleanup_tx_cqes(struct otx2_nic *pfvf, struct otx2_cq_queue *cq) struct nix_cqe_tx_s *cqe; int processed_cqe = 0; struct sg_list *sg; + int qidx; - sq = &pfvf->qset.sq[cq->cint_idx]; + qidx = cq->cq_idx - pfvf->hw.rx_queues; + sq = &pfvf->qset.sq[qidx]; if (otx2_nix_cq_op_status(pfvf, cq) || !cq->pend_cqe) return; diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h index 93cac2c2664c..7ab6db9a986f 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h @@ -102,7 +102,8 @@ enum cq_type { CQ_RX, CQ_TX, CQ_XDP, - CQS_PER_CINT = 3, /* RQ + SQ + XDP */ + CQ_QOS, + CQS_PER_CINT = 4, /* RQ + SQ + XDP + QOS_SQ */ }; struct otx2_cq_poll { diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c index 53366dbfbf27..3734c799e416 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c @@ -70,10 +70,6 @@ static void otx2vf_process_vfaf_mbox_msg(struct otx2_nic *vf, case MBOX_MSG_NIX_LF_ALLOC: mbox_handler_nix_lf_alloc(vf, (struct nix_lf_alloc_rsp *)msg); break; - case MBOX_MSG_NIX_TXSCH_ALLOC: - mbox_handler_nix_txsch_alloc(vf, - (struct nix_txsch_alloc_rsp *)msg); - break; case MBOX_MSG_NIX_BP_ENABLE: mbox_handler_nix_bp_enable(vf, (struct nix_bp_cfg_rsp *)msg); break; @@ -479,6 +475,7 @@ static const struct net_device_ops otx2vf_netdev_ops = { .ndo_open = otx2vf_open, .ndo_stop = otx2vf_stop, .ndo_start_xmit = otx2vf_xmit, + .ndo_select_queue = otx2_select_queue, .ndo_set_rx_mode = otx2vf_set_rx_mode, .ndo_set_mac_address = otx2_set_mac_address, .ndo_change_mtu = otx2vf_change_mtu, @@ -524,10 +521,10 @@ static int otx2vf_probe(struct pci_dev *pdev, const struct pci_device_id *id) { int num_vec = pci_msix_vec_count(pdev); struct device *dev = &pdev->dev; + int err, qcount, qos_txqs; struct net_device *netdev; struct otx2_nic *vf; struct otx2_hw *hw; - int err, qcount; err = pcim_enable_device(pdev); if (err) { @@ -550,7 +547,8 @@ static int otx2vf_probe(struct pci_dev *pdev, const struct pci_device_id *id) pci_set_master(pdev); qcount = num_online_cpus(); - netdev = alloc_etherdev_mqs(sizeof(*vf), qcount, qcount); + qos_txqs = min_t(int, qcount, OTX2_QOS_MAX_LEAF_NODES); + netdev = alloc_etherdev_mqs(sizeof(*vf), qcount + qos_txqs, qcount); if (!netdev) { err = -ENOMEM; goto err_release_regions; @@ -570,7 +568,7 @@ static int otx2vf_probe(struct pci_dev *pdev, const struct pci_device_id *id) hw->rx_queues = qcount; hw->tx_queues = qcount; hw->max_queues = qcount; - hw->tot_tx_queues = qcount; + hw->non_qos_queues = qcount; hw->rbuf_len = OTX2_DEFAULT_RBUF_LEN; /* Use CQE of 128 byte descriptor size by default */ hw->xqe_size = 128; @@ -699,6 +697,7 @@ static int otx2vf_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (err) goto err_shutdown_tc; #endif + otx2_qos_init(vf, qos_txqs); return 0; @@ -761,6 +760,7 @@ static void otx2vf_remove(struct pci_dev *pdev) otx2_ptp_destroy(vf); otx2_mcam_flow_del(vf); otx2_shutdown_tc(vf); + otx2_shutdown_qos(vf); otx2vf_disable_mbox_intr(vf); otx2_detach_resources(&vf->mbox); free_percpu(vf->hw.lmt_info); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/qos.c b/drivers/net/ethernet/marvell/octeontx2/nic/qos.c new file mode 100644 index 000000000000..d3a76c5ccda8 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/qos.c @@ -0,0 +1,1363 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell RVU Ethernet driver + * + * Copyright (C) 2023 Marvell. + * + */ +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/inetdevice.h> +#include <linux/bitfield.h> + +#include "otx2_common.h" +#include "cn10k.h" +#include "qos.h" + +#define OTX2_QOS_QID_INNER 0xFFFFU +#define OTX2_QOS_QID_NONE 0xFFFEU +#define OTX2_QOS_ROOT_CLASSID 0xFFFFFFFF +#define OTX2_QOS_CLASS_NONE 0 +#define OTX2_QOS_DEFAULT_PRIO 0xF +#define OTX2_QOS_INVALID_SQ 0xFFFF + +static void otx2_qos_update_tx_netdev_queues(struct otx2_nic *pfvf) +{ + struct otx2_hw *hw = &pfvf->hw; + int tx_queues, qos_txqs, err; + + qos_txqs = bitmap_weight(pfvf->qos.qos_sq_bmap, + OTX2_QOS_MAX_LEAF_NODES); + + tx_queues = hw->tx_queues + qos_txqs; + + err = netif_set_real_num_tx_queues(pfvf->netdev, tx_queues); + if (err) { + netdev_err(pfvf->netdev, + "Failed to set no of Tx queues: %d\n", tx_queues); + return; + } +} + +static void otx2_qos_get_regaddr(struct otx2_qos_node *node, + struct nix_txschq_config *cfg, + int index) +{ + if (node->level == NIX_TXSCH_LVL_SMQ) { + cfg->reg[index++] = NIX_AF_MDQX_PARENT(node->schq); + cfg->reg[index++] = NIX_AF_MDQX_SCHEDULE(node->schq); + cfg->reg[index++] = NIX_AF_MDQX_PIR(node->schq); + cfg->reg[index] = NIX_AF_MDQX_CIR(node->schq); + } else if (node->level == NIX_TXSCH_LVL_TL4) { + cfg->reg[index++] = NIX_AF_TL4X_PARENT(node->schq); + cfg->reg[index++] = NIX_AF_TL4X_SCHEDULE(node->schq); + cfg->reg[index++] = NIX_AF_TL4X_PIR(node->schq); + cfg->reg[index] = NIX_AF_TL4X_CIR(node->schq); + } else if (node->level == NIX_TXSCH_LVL_TL3) { + cfg->reg[index++] = NIX_AF_TL3X_PARENT(node->schq); + cfg->reg[index++] = NIX_AF_TL3X_SCHEDULE(node->schq); + cfg->reg[index++] = NIX_AF_TL3X_PIR(node->schq); + cfg->reg[index] = NIX_AF_TL3X_CIR(node->schq); + } else if (node->level == NIX_TXSCH_LVL_TL2) { + cfg->reg[index++] = NIX_AF_TL2X_PARENT(node->schq); + cfg->reg[index++] = NIX_AF_TL2X_SCHEDULE(node->schq); + cfg->reg[index++] = NIX_AF_TL2X_PIR(node->schq); + cfg->reg[index] = NIX_AF_TL2X_CIR(node->schq); + } +} + +static void otx2_config_sched_shaping(struct otx2_nic *pfvf, + struct otx2_qos_node *node, + struct nix_txschq_config *cfg, + int *num_regs) +{ + u64 maxrate; + + otx2_qos_get_regaddr(node, cfg, *num_regs); + + /* configure parent txschq */ + cfg->regval[*num_regs] = node->parent->schq << 16; + (*num_regs)++; + + /* configure prio/quantum */ + if (node->qid == OTX2_QOS_QID_NONE) { + cfg->regval[*num_regs] = node->prio << 24 | + mtu_to_dwrr_weight(pfvf, pfvf->tx_max_pktlen); + (*num_regs)++; + return; + } + + /* configure priority */ + cfg->regval[*num_regs] = (node->schq - node->parent->prio_anchor) << 24; + (*num_regs)++; + + /* configure PIR */ + maxrate = (node->rate > node->ceil) ? node->rate : node->ceil; + + cfg->regval[*num_regs] = + otx2_get_txschq_rate_regval(pfvf, maxrate, 65536); + (*num_regs)++; + + /* Don't configure CIR when both CIR+PIR not supported + * On 96xx, CIR + PIR + RED_ALGO=STALL causes deadlock + */ + if (!test_bit(QOS_CIR_PIR_SUPPORT, &pfvf->hw.cap_flag)) + return; + + cfg->regval[*num_regs] = + otx2_get_txschq_rate_regval(pfvf, node->rate, 65536); + (*num_regs)++; +} + +static void __otx2_qos_txschq_cfg(struct otx2_nic *pfvf, + struct otx2_qos_node *node, + struct nix_txschq_config *cfg) +{ + struct otx2_hw *hw = &pfvf->hw; + int num_regs = 0; + u8 level; + + level = node->level; + + /* program txschq registers */ + if (level == NIX_TXSCH_LVL_SMQ) { + cfg->reg[num_regs] = NIX_AF_SMQX_CFG(node->schq); + cfg->regval[num_regs] = ((u64)pfvf->tx_max_pktlen << 8) | + OTX2_MIN_MTU; + cfg->regval[num_regs] |= (0x20ULL << 51) | (0x80ULL << 39) | + (0x2ULL << 36); + num_regs++; + + otx2_config_sched_shaping(pfvf, node, cfg, &num_regs); + + } else if (level == NIX_TXSCH_LVL_TL4) { + otx2_config_sched_shaping(pfvf, node, cfg, &num_regs); + } else if (level == NIX_TXSCH_LVL_TL3) { + /* configure link cfg */ + if (level == pfvf->qos.link_cfg_lvl) { + cfg->reg[num_regs] = NIX_AF_TL3_TL2X_LINKX_CFG(node->schq, hw->tx_link); + cfg->regval[num_regs] = BIT_ULL(13) | BIT_ULL(12); + num_regs++; + } + + otx2_config_sched_shaping(pfvf, node, cfg, &num_regs); + } else if (level == NIX_TXSCH_LVL_TL2) { + /* configure link cfg */ + if (level == pfvf->qos.link_cfg_lvl) { + cfg->reg[num_regs] = NIX_AF_TL3_TL2X_LINKX_CFG(node->schq, hw->tx_link); + cfg->regval[num_regs] = BIT_ULL(13) | BIT_ULL(12); + num_regs++; + } + + /* check if node is root */ + if (node->qid == OTX2_QOS_QID_INNER && !node->parent) { + cfg->reg[num_regs] = NIX_AF_TL2X_SCHEDULE(node->schq); + cfg->regval[num_regs] = TXSCH_TL1_DFLT_RR_PRIO << 24 | + mtu_to_dwrr_weight(pfvf, + pfvf->tx_max_pktlen); + num_regs++; + goto txschq_cfg_out; + } + + otx2_config_sched_shaping(pfvf, node, cfg, &num_regs); + } + +txschq_cfg_out: + cfg->num_regs = num_regs; +} + +static int otx2_qos_txschq_set_parent_topology(struct otx2_nic *pfvf, + struct otx2_qos_node *parent) +{ + struct mbox *mbox = &pfvf->mbox; + struct nix_txschq_config *cfg; + int rc; + + if (parent->level == NIX_TXSCH_LVL_MDQ) + return 0; + + mutex_lock(&mbox->lock); + + cfg = otx2_mbox_alloc_msg_nix_txschq_cfg(&pfvf->mbox); + if (!cfg) { + mutex_unlock(&mbox->lock); + return -ENOMEM; + } + + cfg->lvl = parent->level; + + if (parent->level == NIX_TXSCH_LVL_TL4) + cfg->reg[0] = NIX_AF_TL4X_TOPOLOGY(parent->schq); + else if (parent->level == NIX_TXSCH_LVL_TL3) + cfg->reg[0] = NIX_AF_TL3X_TOPOLOGY(parent->schq); + else if (parent->level == NIX_TXSCH_LVL_TL2) + cfg->reg[0] = NIX_AF_TL2X_TOPOLOGY(parent->schq); + else if (parent->level == NIX_TXSCH_LVL_TL1) + cfg->reg[0] = NIX_AF_TL1X_TOPOLOGY(parent->schq); + + cfg->regval[0] = (u64)parent->prio_anchor << 32; + if (parent->level == NIX_TXSCH_LVL_TL1) + cfg->regval[0] |= (u64)TXSCH_TL1_DFLT_RR_PRIO << 1; + + cfg->num_regs++; + + rc = otx2_sync_mbox_msg(&pfvf->mbox); + + mutex_unlock(&mbox->lock); + + return rc; +} + +static void otx2_qos_free_hw_node_schq(struct otx2_nic *pfvf, + struct otx2_qos_node *parent) +{ + struct otx2_qos_node *node; + + list_for_each_entry_reverse(node, &parent->child_schq_list, list) + otx2_txschq_free_one(pfvf, node->level, node->schq); +} + +static void otx2_qos_free_hw_node(struct otx2_nic *pfvf, + struct otx2_qos_node *parent) +{ + struct otx2_qos_node *node, *tmp; + + list_for_each_entry_safe(node, tmp, &parent->child_list, list) { + otx2_qos_free_hw_node(pfvf, node); + otx2_qos_free_hw_node_schq(pfvf, node); + otx2_txschq_free_one(pfvf, node->level, node->schq); + } +} + +static void otx2_qos_free_hw_cfg(struct otx2_nic *pfvf, + struct otx2_qos_node *node) +{ + mutex_lock(&pfvf->qos.qos_lock); + + /* free child node hw mappings */ + otx2_qos_free_hw_node(pfvf, node); + otx2_qos_free_hw_node_schq(pfvf, node); + + /* free node hw mappings */ + otx2_txschq_free_one(pfvf, node->level, node->schq); + + mutex_unlock(&pfvf->qos.qos_lock); +} + +static void otx2_qos_sw_node_delete(struct otx2_nic *pfvf, + struct otx2_qos_node *node) +{ + hash_del_rcu(&node->hlist); + + if (node->qid != OTX2_QOS_QID_INNER && node->qid != OTX2_QOS_QID_NONE) { + __clear_bit(node->qid, pfvf->qos.qos_sq_bmap); + otx2_qos_update_tx_netdev_queues(pfvf); + } + + list_del(&node->list); + kfree(node); +} + +static void otx2_qos_free_sw_node_schq(struct otx2_nic *pfvf, + struct otx2_qos_node *parent) +{ + struct otx2_qos_node *node, *tmp; + + list_for_each_entry_safe(node, tmp, &parent->child_schq_list, list) { + list_del(&node->list); + kfree(node); + } +} + +static void __otx2_qos_free_sw_node(struct otx2_nic *pfvf, + struct otx2_qos_node *parent) +{ + struct otx2_qos_node *node, *tmp; + + list_for_each_entry_safe(node, tmp, &parent->child_list, list) { + __otx2_qos_free_sw_node(pfvf, node); + otx2_qos_free_sw_node_schq(pfvf, node); + otx2_qos_sw_node_delete(pfvf, node); + } +} + +static void otx2_qos_free_sw_node(struct otx2_nic *pfvf, + struct otx2_qos_node *node) +{ + mutex_lock(&pfvf->qos.qos_lock); + + __otx2_qos_free_sw_node(pfvf, node); + otx2_qos_free_sw_node_schq(pfvf, node); + otx2_qos_sw_node_delete(pfvf, node); + + mutex_unlock(&pfvf->qos.qos_lock); +} + +static void otx2_qos_destroy_node(struct otx2_nic *pfvf, + struct otx2_qos_node *node) +{ + otx2_qos_free_hw_cfg(pfvf, node); + otx2_qos_free_sw_node(pfvf, node); +} + +static void otx2_qos_fill_cfg_schq(struct otx2_qos_node *parent, + struct otx2_qos_cfg *cfg) +{ + struct otx2_qos_node *node; + + list_for_each_entry(node, &parent->child_schq_list, list) + cfg->schq[node->level]++; +} + +static void otx2_qos_fill_cfg_tl(struct otx2_qos_node *parent, + struct otx2_qos_cfg *cfg) +{ + struct otx2_qos_node *node; + + list_for_each_entry(node, &parent->child_list, list) { + otx2_qos_fill_cfg_tl(node, cfg); + cfg->schq_contig[node->level]++; + otx2_qos_fill_cfg_schq(node, cfg); + } +} + +static void otx2_qos_prepare_txschq_cfg(struct otx2_nic *pfvf, + struct otx2_qos_node *parent, + struct otx2_qos_cfg *cfg) +{ + mutex_lock(&pfvf->qos.qos_lock); + otx2_qos_fill_cfg_tl(parent, cfg); + mutex_unlock(&pfvf->qos.qos_lock); +} + +static void otx2_qos_read_txschq_cfg_schq(struct otx2_qos_node *parent, + struct otx2_qos_cfg *cfg) +{ + struct otx2_qos_node *node; + int cnt; + + list_for_each_entry(node, &parent->child_schq_list, list) { + cnt = cfg->dwrr_node_pos[node->level]; + cfg->schq_list[node->level][cnt] = node->schq; + cfg->schq[node->level]++; + cfg->dwrr_node_pos[node->level]++; + } +} + +static void otx2_qos_read_txschq_cfg_tl(struct otx2_qos_node *parent, + struct otx2_qos_cfg *cfg) +{ + struct otx2_qos_node *node; + int cnt; + + list_for_each_entry(node, &parent->child_list, list) { + otx2_qos_read_txschq_cfg_tl(node, cfg); + cnt = cfg->static_node_pos[node->level]; + cfg->schq_contig_list[node->level][cnt] = node->schq; + cfg->schq_contig[node->level]++; + cfg->static_node_pos[node->level]++; + otx2_qos_read_txschq_cfg_schq(node, cfg); + } +} + +static void otx2_qos_read_txschq_cfg(struct otx2_nic *pfvf, + struct otx2_qos_node *node, + struct otx2_qos_cfg *cfg) +{ + mutex_lock(&pfvf->qos.qos_lock); + otx2_qos_read_txschq_cfg_tl(node, cfg); + mutex_unlock(&pfvf->qos.qos_lock); +} + +static struct otx2_qos_node * +otx2_qos_alloc_root(struct otx2_nic *pfvf) +{ + struct otx2_qos_node *node; + + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) + return ERR_PTR(-ENOMEM); + + node->parent = NULL; + if (!is_otx2_vf(pfvf->pcifunc)) + node->level = NIX_TXSCH_LVL_TL1; + else + node->level = NIX_TXSCH_LVL_TL2; + + WRITE_ONCE(node->qid, OTX2_QOS_QID_INNER); + node->classid = OTX2_QOS_ROOT_CLASSID; + + hash_add_rcu(pfvf->qos.qos_hlist, &node->hlist, node->classid); + list_add_tail(&node->list, &pfvf->qos.qos_tree); + INIT_LIST_HEAD(&node->child_list); + INIT_LIST_HEAD(&node->child_schq_list); + + return node; +} + +static int otx2_qos_add_child_node(struct otx2_qos_node *parent, + struct otx2_qos_node *node) +{ + struct list_head *head = &parent->child_list; + struct otx2_qos_node *tmp_node; + struct list_head *tmp; + + for (tmp = head->next; tmp != head; tmp = tmp->next) { + tmp_node = list_entry(tmp, struct otx2_qos_node, list); + if (tmp_node->prio == node->prio) + return -EEXIST; + if (tmp_node->prio > node->prio) { + list_add_tail(&node->list, tmp); + return 0; + } + } + + list_add_tail(&node->list, head); + return 0; +} + +static int otx2_qos_alloc_txschq_node(struct otx2_nic *pfvf, + struct otx2_qos_node *node) +{ + struct otx2_qos_node *txschq_node, *parent, *tmp; + int lvl; + + parent = node; + for (lvl = node->level - 1; lvl >= NIX_TXSCH_LVL_MDQ; lvl--) { + txschq_node = kzalloc(sizeof(*txschq_node), GFP_KERNEL); + if (!txschq_node) + goto err_out; + + txschq_node->parent = parent; + txschq_node->level = lvl; + txschq_node->classid = OTX2_QOS_CLASS_NONE; + WRITE_ONCE(txschq_node->qid, OTX2_QOS_QID_NONE); + txschq_node->rate = 0; + txschq_node->ceil = 0; + txschq_node->prio = 0; + + mutex_lock(&pfvf->qos.qos_lock); + list_add_tail(&txschq_node->list, &node->child_schq_list); + mutex_unlock(&pfvf->qos.qos_lock); + + INIT_LIST_HEAD(&txschq_node->child_list); + INIT_LIST_HEAD(&txschq_node->child_schq_list); + parent = txschq_node; + } + + return 0; + +err_out: + list_for_each_entry_safe(txschq_node, tmp, &node->child_schq_list, + list) { + list_del(&txschq_node->list); + kfree(txschq_node); + } + return -ENOMEM; +} + +static struct otx2_qos_node * +otx2_qos_sw_create_leaf_node(struct otx2_nic *pfvf, + struct otx2_qos_node *parent, + u16 classid, u32 prio, u64 rate, u64 ceil, + u16 qid) +{ + struct otx2_qos_node *node; + int err; + + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) + return ERR_PTR(-ENOMEM); + + node->parent = parent; + node->level = parent->level - 1; + node->classid = classid; + WRITE_ONCE(node->qid, qid); + + node->rate = otx2_convert_rate(rate); + node->ceil = otx2_convert_rate(ceil); + node->prio = prio; + + __set_bit(qid, pfvf->qos.qos_sq_bmap); + + hash_add_rcu(pfvf->qos.qos_hlist, &node->hlist, classid); + + mutex_lock(&pfvf->qos.qos_lock); + err = otx2_qos_add_child_node(parent, node); + if (err) { + mutex_unlock(&pfvf->qos.qos_lock); + return ERR_PTR(err); + } + mutex_unlock(&pfvf->qos.qos_lock); + + INIT_LIST_HEAD(&node->child_list); + INIT_LIST_HEAD(&node->child_schq_list); + + err = otx2_qos_alloc_txschq_node(pfvf, node); + if (err) { + otx2_qos_sw_node_delete(pfvf, node); + return ERR_PTR(-ENOMEM); + } + + return node; +} + +static struct otx2_qos_node * +otx2_sw_node_find(struct otx2_nic *pfvf, u32 classid) +{ + struct otx2_qos_node *node = NULL; + + hash_for_each_possible(pfvf->qos.qos_hlist, node, hlist, classid) { + if (node->classid == classid) + break; + } + + return node; +} + +static struct otx2_qos_node * +otx2_sw_node_find_rcu(struct otx2_nic *pfvf, u32 classid) +{ + struct otx2_qos_node *node = NULL; + + hash_for_each_possible_rcu(pfvf->qos.qos_hlist, node, hlist, classid) { + if (node->classid == classid) + break; + } + + return node; +} + +int otx2_get_txq_by_classid(struct otx2_nic *pfvf, u16 classid) +{ + struct otx2_qos_node *node; + u16 qid; + int res; + + node = otx2_sw_node_find_rcu(pfvf, classid); + if (!node) { + res = -ENOENT; + goto out; + } + qid = READ_ONCE(node->qid); + if (qid == OTX2_QOS_QID_INNER) { + res = -EINVAL; + goto out; + } + res = pfvf->hw.tx_queues + qid; +out: + return res; +} + +static int +otx2_qos_txschq_config(struct otx2_nic *pfvf, struct otx2_qos_node *node) +{ + struct mbox *mbox = &pfvf->mbox; + struct nix_txschq_config *req; + int rc; + + mutex_lock(&mbox->lock); + + req = otx2_mbox_alloc_msg_nix_txschq_cfg(&pfvf->mbox); + if (!req) { + mutex_unlock(&mbox->lock); + return -ENOMEM; + } + + req->lvl = node->level; + __otx2_qos_txschq_cfg(pfvf, node, req); + + rc = otx2_sync_mbox_msg(&pfvf->mbox); + + mutex_unlock(&mbox->lock); + + return rc; +} + +static int otx2_qos_txschq_alloc(struct otx2_nic *pfvf, + struct otx2_qos_cfg *cfg) +{ + struct nix_txsch_alloc_req *req; + struct nix_txsch_alloc_rsp *rsp; + struct mbox *mbox = &pfvf->mbox; + int lvl, rc, schq; + + mutex_lock(&mbox->lock); + req = otx2_mbox_alloc_msg_nix_txsch_alloc(&pfvf->mbox); + if (!req) { + mutex_unlock(&mbox->lock); + return -ENOMEM; + } + + for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) { + req->schq[lvl] = cfg->schq[lvl]; + req->schq_contig[lvl] = cfg->schq_contig[lvl]; + } + + rc = otx2_sync_mbox_msg(&pfvf->mbox); + if (rc) { + mutex_unlock(&mbox->lock); + return rc; + } + + rsp = (struct nix_txsch_alloc_rsp *) + otx2_mbox_get_rsp(&pfvf->mbox.mbox, 0, &req->hdr); + + if (IS_ERR(rsp)) { + rc = PTR_ERR(rsp); + goto out; + } + + for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) { + for (schq = 0; schq < rsp->schq_contig[lvl]; schq++) { + cfg->schq_contig_list[lvl][schq] = + rsp->schq_contig_list[lvl][schq]; + } + } + + for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) { + for (schq = 0; schq < rsp->schq[lvl]; schq++) { + cfg->schq_list[lvl][schq] = + rsp->schq_list[lvl][schq]; + } + } + + pfvf->qos.link_cfg_lvl = rsp->link_cfg_lvl; + +out: + mutex_unlock(&mbox->lock); + return rc; +} + +static void otx2_qos_txschq_fill_cfg_schq(struct otx2_nic *pfvf, + struct otx2_qos_node *node, + struct otx2_qos_cfg *cfg) +{ + struct otx2_qos_node *tmp; + int cnt; + + list_for_each_entry(tmp, &node->child_schq_list, list) { + cnt = cfg->dwrr_node_pos[tmp->level]; + tmp->schq = cfg->schq_list[tmp->level][cnt]; + cfg->dwrr_node_pos[tmp->level]++; + } +} + +static void otx2_qos_txschq_fill_cfg_tl(struct otx2_nic *pfvf, + struct otx2_qos_node *node, + struct otx2_qos_cfg *cfg) +{ + struct otx2_qos_node *tmp; + int cnt; + + list_for_each_entry(tmp, &node->child_list, list) { + otx2_qos_txschq_fill_cfg_tl(pfvf, tmp, cfg); + cnt = cfg->static_node_pos[tmp->level]; + tmp->schq = cfg->schq_contig_list[tmp->level][cnt]; + if (cnt == 0) + node->prio_anchor = tmp->schq; + cfg->static_node_pos[tmp->level]++; + otx2_qos_txschq_fill_cfg_schq(pfvf, tmp, cfg); + } +} + +static void otx2_qos_txschq_fill_cfg(struct otx2_nic *pfvf, + struct otx2_qos_node *node, + struct otx2_qos_cfg *cfg) +{ + mutex_lock(&pfvf->qos.qos_lock); + otx2_qos_txschq_fill_cfg_tl(pfvf, node, cfg); + otx2_qos_txschq_fill_cfg_schq(pfvf, node, cfg); + mutex_unlock(&pfvf->qos.qos_lock); +} + +static int otx2_qos_txschq_push_cfg_schq(struct otx2_nic *pfvf, + struct otx2_qos_node *node, + struct otx2_qos_cfg *cfg) +{ + struct otx2_qos_node *tmp; + int ret; + + list_for_each_entry(tmp, &node->child_schq_list, list) { + ret = otx2_qos_txschq_config(pfvf, tmp); + if (ret) + return -EIO; + ret = otx2_qos_txschq_set_parent_topology(pfvf, tmp->parent); + if (ret) + return -EIO; + } + + return 0; +} + +static int otx2_qos_txschq_push_cfg_tl(struct otx2_nic *pfvf, + struct otx2_qos_node *node, + struct otx2_qos_cfg *cfg) +{ + struct otx2_qos_node *tmp; + int ret; + + list_for_each_entry(tmp, &node->child_list, list) { + ret = otx2_qos_txschq_push_cfg_tl(pfvf, tmp, cfg); + if (ret) + return -EIO; + ret = otx2_qos_txschq_config(pfvf, tmp); + if (ret) + return -EIO; + ret = otx2_qos_txschq_push_cfg_schq(pfvf, tmp, cfg); + if (ret) + return -EIO; + } + + ret = otx2_qos_txschq_set_parent_topology(pfvf, node); + if (ret) + return -EIO; + + return 0; +} + +static int otx2_qos_txschq_push_cfg(struct otx2_nic *pfvf, + struct otx2_qos_node *node, + struct otx2_qos_cfg *cfg) +{ + int ret; + + mutex_lock(&pfvf->qos.qos_lock); + ret = otx2_qos_txschq_push_cfg_tl(pfvf, node, cfg); + if (ret) + goto out; + ret = otx2_qos_txschq_push_cfg_schq(pfvf, node, cfg); +out: + mutex_unlock(&pfvf->qos.qos_lock); + return ret; +} + +static int otx2_qos_txschq_update_config(struct otx2_nic *pfvf, + struct otx2_qos_node *node, + struct otx2_qos_cfg *cfg) +{ + otx2_qos_txschq_fill_cfg(pfvf, node, cfg); + + return otx2_qos_txschq_push_cfg(pfvf, node, cfg); +} + +static int otx2_qos_txschq_update_root_cfg(struct otx2_nic *pfvf, + struct otx2_qos_node *root, + struct otx2_qos_cfg *cfg) +{ + root->schq = cfg->schq_list[root->level][0]; + return otx2_qos_txschq_config(pfvf, root); +} + +static void otx2_qos_free_cfg(struct otx2_nic *pfvf, struct otx2_qos_cfg *cfg) +{ + int lvl, idx, schq; + + for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) { + for (idx = 0; idx < cfg->schq[lvl]; idx++) { + schq = cfg->schq_list[lvl][idx]; + otx2_txschq_free_one(pfvf, lvl, schq); + } + } + + for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) { + for (idx = 0; idx < cfg->schq_contig[lvl]; idx++) { + schq = cfg->schq_contig_list[lvl][idx]; + otx2_txschq_free_one(pfvf, lvl, schq); + } + } +} + +static void otx2_qos_enadis_sq(struct otx2_nic *pfvf, + struct otx2_qos_node *node, + u16 qid) +{ + if (pfvf->qos.qid_to_sqmap[qid] != OTX2_QOS_INVALID_SQ) + otx2_qos_disable_sq(pfvf, qid); + + pfvf->qos.qid_to_sqmap[qid] = node->schq; + otx2_qos_enable_sq(pfvf, qid); +} + +static void otx2_qos_update_smq_schq(struct otx2_nic *pfvf, + struct otx2_qos_node *node, + bool action) +{ + struct otx2_qos_node *tmp; + + if (node->qid == OTX2_QOS_QID_INNER) + return; + + list_for_each_entry(tmp, &node->child_schq_list, list) { + if (tmp->level == NIX_TXSCH_LVL_MDQ) { + if (action == QOS_SMQ_FLUSH) + otx2_smq_flush(pfvf, tmp->schq); + else + otx2_qos_enadis_sq(pfvf, tmp, node->qid); + } + } +} + +static void __otx2_qos_update_smq(struct otx2_nic *pfvf, + struct otx2_qos_node *node, + bool action) +{ + struct otx2_qos_node *tmp; + + list_for_each_entry(tmp, &node->child_list, list) { + __otx2_qos_update_smq(pfvf, tmp, action); + if (tmp->qid == OTX2_QOS_QID_INNER) + continue; + if (tmp->level == NIX_TXSCH_LVL_MDQ) { + if (action == QOS_SMQ_FLUSH) + otx2_smq_flush(pfvf, tmp->schq); + else + otx2_qos_enadis_sq(pfvf, tmp, tmp->qid); + } else { + otx2_qos_update_smq_schq(pfvf, tmp, action); + } + } +} + +static void otx2_qos_update_smq(struct otx2_nic *pfvf, + struct otx2_qos_node *node, + bool action) +{ + mutex_lock(&pfvf->qos.qos_lock); + __otx2_qos_update_smq(pfvf, node, action); + otx2_qos_update_smq_schq(pfvf, node, action); + mutex_unlock(&pfvf->qos.qos_lock); +} + +static int otx2_qos_push_txschq_cfg(struct otx2_nic *pfvf, + struct otx2_qos_node *node, + struct otx2_qos_cfg *cfg) +{ + int ret; + + ret = otx2_qos_txschq_alloc(pfvf, cfg); + if (ret) + return -ENOSPC; + + if (!(pfvf->netdev->flags & IFF_UP)) { + otx2_qos_txschq_fill_cfg(pfvf, node, cfg); + return 0; + } + + ret = otx2_qos_txschq_update_config(pfvf, node, cfg); + if (ret) { + otx2_qos_free_cfg(pfvf, cfg); + return -EIO; + } + + otx2_qos_update_smq(pfvf, node, QOS_CFG_SQ); + + return 0; +} + +static int otx2_qos_update_tree(struct otx2_nic *pfvf, + struct otx2_qos_node *node, + struct otx2_qos_cfg *cfg) +{ + otx2_qos_prepare_txschq_cfg(pfvf, node->parent, cfg); + return otx2_qos_push_txschq_cfg(pfvf, node->parent, cfg); +} + +static int otx2_qos_root_add(struct otx2_nic *pfvf, u16 htb_maj_id, u16 htb_defcls, + struct netlink_ext_ack *extack) +{ + struct otx2_qos_cfg *new_cfg; + struct otx2_qos_node *root; + int err; + + netdev_dbg(pfvf->netdev, + "TC_HTB_CREATE: handle=0x%x defcls=0x%x\n", + htb_maj_id, htb_defcls); + + root = otx2_qos_alloc_root(pfvf); + if (IS_ERR(root)) { + err = PTR_ERR(root); + return err; + } + + /* allocate txschq queue */ + new_cfg = kzalloc(sizeof(*new_cfg), GFP_KERNEL); + if (!new_cfg) { + NL_SET_ERR_MSG_MOD(extack, "Memory allocation error"); + err = -ENOMEM; + goto free_root_node; + } + /* allocate htb root node */ + new_cfg->schq[root->level] = 1; + err = otx2_qos_txschq_alloc(pfvf, new_cfg); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "Error allocating txschq"); + goto free_root_node; + } + + if (!(pfvf->netdev->flags & IFF_UP) || + root->level == NIX_TXSCH_LVL_TL1) { + root->schq = new_cfg->schq_list[root->level][0]; + goto out; + } + + /* update the txschq configuration in hw */ + err = otx2_qos_txschq_update_root_cfg(pfvf, root, new_cfg); + if (err) { + NL_SET_ERR_MSG_MOD(extack, + "Error updating txschq configuration"); + goto txschq_free; + } + +out: + WRITE_ONCE(pfvf->qos.defcls, htb_defcls); + /* Pairs with smp_load_acquire() in ndo_select_queue */ + smp_store_release(&pfvf->qos.maj_id, htb_maj_id); + kfree(new_cfg); + return 0; + +txschq_free: + otx2_qos_free_cfg(pfvf, new_cfg); +free_root_node: + kfree(new_cfg); + otx2_qos_sw_node_delete(pfvf, root); + return err; +} + +static int otx2_qos_root_destroy(struct otx2_nic *pfvf) +{ + struct otx2_qos_node *root; + + netdev_dbg(pfvf->netdev, "TC_HTB_DESTROY\n"); + + /* find root node */ + root = otx2_sw_node_find(pfvf, OTX2_QOS_ROOT_CLASSID); + if (!root) + return -ENOENT; + + /* free the hw mappings */ + otx2_qos_destroy_node(pfvf, root); + + return 0; +} + +static int otx2_qos_validate_configuration(struct otx2_qos_node *parent, + struct netlink_ext_ack *extack, + struct otx2_nic *pfvf, + u64 prio) +{ + if (test_bit(prio, parent->prio_bmap)) { + NL_SET_ERR_MSG_MOD(extack, + "Static priority child with same priority exists"); + return -EEXIST; + } + + if (prio == TXSCH_TL1_DFLT_RR_PRIO) { + NL_SET_ERR_MSG_MOD(extack, + "Priority is reserved for Round Robin"); + return -EINVAL; + } + + return 0; +} + +static int otx2_qos_leaf_alloc_queue(struct otx2_nic *pfvf, u16 classid, + u32 parent_classid, u64 rate, u64 ceil, + u64 prio, struct netlink_ext_ack *extack) +{ + struct otx2_qos_cfg *old_cfg, *new_cfg; + struct otx2_qos_node *node, *parent; + int qid, ret, err; + + netdev_dbg(pfvf->netdev, + "TC_HTB_LEAF_ALLOC_QUEUE: classid=0x%x parent_classid=0x%x rate=%lld ceil=%lld prio=%lld\n", + classid, parent_classid, rate, ceil, prio); + + if (prio > OTX2_QOS_MAX_PRIO) { + NL_SET_ERR_MSG_MOD(extack, "Valid priority range 0 to 7"); + ret = -EOPNOTSUPP; + goto out; + } + + /* get parent node */ + parent = otx2_sw_node_find(pfvf, parent_classid); + if (!parent) { + NL_SET_ERR_MSG_MOD(extack, "parent node not found"); + ret = -ENOENT; + goto out; + } + if (parent->level == NIX_TXSCH_LVL_MDQ) { + NL_SET_ERR_MSG_MOD(extack, "HTB qos max levels reached"); + ret = -EOPNOTSUPP; + goto out; + } + + ret = otx2_qos_validate_configuration(parent, extack, pfvf, prio); + if (ret) + goto out; + + set_bit(prio, parent->prio_bmap); + + /* read current txschq configuration */ + old_cfg = kzalloc(sizeof(*old_cfg), GFP_KERNEL); + if (!old_cfg) { + NL_SET_ERR_MSG_MOD(extack, "Memory allocation error"); + ret = -ENOMEM; + goto reset_prio; + } + otx2_qos_read_txschq_cfg(pfvf, parent, old_cfg); + + /* allocate a new sq */ + qid = otx2_qos_get_qid(pfvf); + if (qid < 0) { + NL_SET_ERR_MSG_MOD(extack, "Reached max supported QOS SQ's"); + ret = -ENOMEM; + goto free_old_cfg; + } + + /* Actual SQ mapping will be updated after SMQ alloc */ + pfvf->qos.qid_to_sqmap[qid] = OTX2_QOS_INVALID_SQ; + + /* allocate and initialize a new child node */ + node = otx2_qos_sw_create_leaf_node(pfvf, parent, classid, prio, rate, + ceil, qid); + if (IS_ERR(node)) { + NL_SET_ERR_MSG_MOD(extack, "Unable to allocate leaf node"); + ret = PTR_ERR(node); + goto free_old_cfg; + } + + /* push new txschq config to hw */ + new_cfg = kzalloc(sizeof(*new_cfg), GFP_KERNEL); + if (!new_cfg) { + NL_SET_ERR_MSG_MOD(extack, "Memory allocation error"); + ret = -ENOMEM; + goto free_node; + } + ret = otx2_qos_update_tree(pfvf, node, new_cfg); + if (ret) { + NL_SET_ERR_MSG_MOD(extack, "HTB HW configuration error"); + kfree(new_cfg); + otx2_qos_sw_node_delete(pfvf, node); + /* restore the old qos tree */ + err = otx2_qos_txschq_update_config(pfvf, parent, old_cfg); + if (err) { + netdev_err(pfvf->netdev, + "Failed to restore txcshq configuration"); + goto free_old_cfg; + } + + otx2_qos_update_smq(pfvf, parent, QOS_CFG_SQ); + goto free_old_cfg; + } + + /* update tx_real_queues */ + otx2_qos_update_tx_netdev_queues(pfvf); + + /* free new txschq config */ + kfree(new_cfg); + + /* free old txschq config */ + otx2_qos_free_cfg(pfvf, old_cfg); + kfree(old_cfg); + + return pfvf->hw.tx_queues + qid; + +free_node: + otx2_qos_sw_node_delete(pfvf, node); +free_old_cfg: + kfree(old_cfg); +reset_prio: + clear_bit(prio, parent->prio_bmap); +out: + return ret; +} + +static int otx2_qos_leaf_to_inner(struct otx2_nic *pfvf, u16 classid, + u16 child_classid, u64 rate, u64 ceil, u64 prio, + struct netlink_ext_ack *extack) +{ + struct otx2_qos_cfg *old_cfg, *new_cfg; + struct otx2_qos_node *node, *child; + int ret, err; + u16 qid; + + netdev_dbg(pfvf->netdev, + "TC_HTB_LEAF_TO_INNER classid %04x, child %04x, rate %llu, ceil %llu\n", + classid, child_classid, rate, ceil); + + if (prio > OTX2_QOS_MAX_PRIO) { + NL_SET_ERR_MSG_MOD(extack, "Valid priority range 0 to 7"); + ret = -EOPNOTSUPP; + goto out; + } + + /* find node related to classid */ + node = otx2_sw_node_find(pfvf, classid); + if (!node) { + NL_SET_ERR_MSG_MOD(extack, "HTB node not found"); + ret = -ENOENT; + goto out; + } + /* check max qos txschq level */ + if (node->level == NIX_TXSCH_LVL_MDQ) { + NL_SET_ERR_MSG_MOD(extack, "HTB qos level not supported"); + ret = -EOPNOTSUPP; + goto out; + } + + set_bit(prio, node->prio_bmap); + + /* store the qid to assign to leaf node */ + qid = node->qid; + + /* read current txschq configuration */ + old_cfg = kzalloc(sizeof(*old_cfg), GFP_KERNEL); + if (!old_cfg) { + NL_SET_ERR_MSG_MOD(extack, "Memory allocation error"); + ret = -ENOMEM; + goto reset_prio; + } + otx2_qos_read_txschq_cfg(pfvf, node, old_cfg); + + /* delete the txschq nodes allocated for this node */ + otx2_qos_free_sw_node_schq(pfvf, node); + + /* mark this node as htb inner node */ + WRITE_ONCE(node->qid, OTX2_QOS_QID_INNER); + + /* allocate and initialize a new child node */ + child = otx2_qos_sw_create_leaf_node(pfvf, node, child_classid, + prio, rate, ceil, qid); + if (IS_ERR(child)) { + NL_SET_ERR_MSG_MOD(extack, "Unable to allocate leaf node"); + ret = PTR_ERR(child); + goto free_old_cfg; + } + + /* push new txschq config to hw */ + new_cfg = kzalloc(sizeof(*new_cfg), GFP_KERNEL); + if (!new_cfg) { + NL_SET_ERR_MSG_MOD(extack, "Memory allocation error"); + ret = -ENOMEM; + goto free_node; + } + ret = otx2_qos_update_tree(pfvf, child, new_cfg); + if (ret) { + NL_SET_ERR_MSG_MOD(extack, "HTB HW configuration error"); + kfree(new_cfg); + otx2_qos_sw_node_delete(pfvf, child); + /* restore the old qos tree */ + WRITE_ONCE(node->qid, qid); + err = otx2_qos_alloc_txschq_node(pfvf, node); + if (err) { + netdev_err(pfvf->netdev, + "Failed to restore old leaf node"); + goto free_old_cfg; + } + err = otx2_qos_txschq_update_config(pfvf, node, old_cfg); + if (err) { + netdev_err(pfvf->netdev, + "Failed to restore txcshq configuration"); + goto free_old_cfg; + } + otx2_qos_update_smq(pfvf, node, QOS_CFG_SQ); + goto free_old_cfg; + } + + /* free new txschq config */ + kfree(new_cfg); + + /* free old txschq config */ + otx2_qos_free_cfg(pfvf, old_cfg); + kfree(old_cfg); + + return 0; + +free_node: + otx2_qos_sw_node_delete(pfvf, child); +free_old_cfg: + kfree(old_cfg); +reset_prio: + clear_bit(prio, node->prio_bmap); +out: + return ret; +} + +static int otx2_qos_leaf_del(struct otx2_nic *pfvf, u16 *classid, + struct netlink_ext_ack *extack) +{ + struct otx2_qos_node *node, *parent; + u64 prio; + u16 qid; + + netdev_dbg(pfvf->netdev, "TC_HTB_LEAF_DEL classid %04x\n", *classid); + + /* find node related to classid */ + node = otx2_sw_node_find(pfvf, *classid); + if (!node) { + NL_SET_ERR_MSG_MOD(extack, "HTB node not found"); + return -ENOENT; + } + parent = node->parent; + prio = node->prio; + qid = node->qid; + + otx2_qos_disable_sq(pfvf, node->qid); + + otx2_qos_destroy_node(pfvf, node); + pfvf->qos.qid_to_sqmap[qid] = OTX2_QOS_INVALID_SQ; + + clear_bit(prio, parent->prio_bmap); + + return 0; +} + +static int otx2_qos_leaf_del_last(struct otx2_nic *pfvf, u16 classid, bool force, + struct netlink_ext_ack *extack) +{ + struct otx2_qos_node *node, *parent; + struct otx2_qos_cfg *new_cfg; + u64 prio; + int err; + u16 qid; + + netdev_dbg(pfvf->netdev, + "TC_HTB_LEAF_DEL_LAST classid %04x\n", classid); + + /* find node related to classid */ + node = otx2_sw_node_find(pfvf, classid); + if (!node) { + NL_SET_ERR_MSG_MOD(extack, "HTB node not found"); + return -ENOENT; + } + + /* save qid for use by parent */ + qid = node->qid; + prio = node->prio; + + parent = otx2_sw_node_find(pfvf, node->parent->classid); + if (!parent) { + NL_SET_ERR_MSG_MOD(extack, "parent node not found"); + return -ENOENT; + } + + /* destroy the leaf node */ + otx2_qos_destroy_node(pfvf, node); + pfvf->qos.qid_to_sqmap[qid] = OTX2_QOS_INVALID_SQ; + + clear_bit(prio, parent->prio_bmap); + + /* create downstream txschq entries to parent */ + err = otx2_qos_alloc_txschq_node(pfvf, parent); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "HTB failed to create txsch configuration"); + return err; + } + WRITE_ONCE(parent->qid, qid); + __set_bit(qid, pfvf->qos.qos_sq_bmap); + + /* push new txschq config to hw */ + new_cfg = kzalloc(sizeof(*new_cfg), GFP_KERNEL); + if (!new_cfg) { + NL_SET_ERR_MSG_MOD(extack, "Memory allocation error"); + return -ENOMEM; + } + /* fill txschq cfg and push txschq cfg to hw */ + otx2_qos_fill_cfg_schq(parent, new_cfg); + err = otx2_qos_push_txschq_cfg(pfvf, parent, new_cfg); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "HTB HW configuration error"); + kfree(new_cfg); + return err; + } + kfree(new_cfg); + + /* update tx_real_queues */ + otx2_qos_update_tx_netdev_queues(pfvf); + + return 0; +} + +void otx2_clean_qos_queues(struct otx2_nic *pfvf) +{ + struct otx2_qos_node *root; + + root = otx2_sw_node_find(pfvf, OTX2_QOS_ROOT_CLASSID); + if (!root) + return; + + otx2_qos_update_smq(pfvf, root, QOS_SMQ_FLUSH); +} + +void otx2_qos_config_txschq(struct otx2_nic *pfvf) +{ + struct otx2_qos_node *root; + int err; + + root = otx2_sw_node_find(pfvf, OTX2_QOS_ROOT_CLASSID); + if (!root) + return; + + err = otx2_qos_txschq_config(pfvf, root); + if (err) { + netdev_err(pfvf->netdev, "Error update txschq configuration\n"); + goto root_destroy; + } + + err = otx2_qos_txschq_push_cfg_tl(pfvf, root, NULL); + if (err) { + netdev_err(pfvf->netdev, "Error update txschq configuration\n"); + goto root_destroy; + } + + otx2_qos_update_smq(pfvf, root, QOS_CFG_SQ); + return; + +root_destroy: + netdev_err(pfvf->netdev, "Failed to update Scheduler/Shaping config in Hardware\n"); + /* Free resources allocated */ + otx2_qos_root_destroy(pfvf); +} + +int otx2_setup_tc_htb(struct net_device *ndev, struct tc_htb_qopt_offload *htb) +{ + struct otx2_nic *pfvf = netdev_priv(ndev); + int res; + + switch (htb->command) { + case TC_HTB_CREATE: + return otx2_qos_root_add(pfvf, htb->parent_classid, + htb->classid, htb->extack); + case TC_HTB_DESTROY: + return otx2_qos_root_destroy(pfvf); + case TC_HTB_LEAF_ALLOC_QUEUE: + res = otx2_qos_leaf_alloc_queue(pfvf, htb->classid, + htb->parent_classid, + htb->rate, htb->ceil, + htb->prio, htb->extack); + if (res < 0) + return res; + htb->qid = res; + return 0; + case TC_HTB_LEAF_TO_INNER: + return otx2_qos_leaf_to_inner(pfvf, htb->parent_classid, + htb->classid, htb->rate, + htb->ceil, htb->prio, + htb->extack); + case TC_HTB_LEAF_DEL: + return otx2_qos_leaf_del(pfvf, &htb->classid, htb->extack); + case TC_HTB_LEAF_DEL_LAST: + case TC_HTB_LEAF_DEL_LAST_FORCE: + return otx2_qos_leaf_del_last(pfvf, htb->classid, + htb->command == TC_HTB_LEAF_DEL_LAST_FORCE, + htb->extack); + case TC_HTB_LEAF_QUERY_QUEUE: + res = otx2_get_txq_by_classid(pfvf, htb->classid); + htb->qid = res; + return 0; + case TC_HTB_NODE_MODIFY: + fallthrough; + default: + return -EOPNOTSUPP; + } +} diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/qos.h b/drivers/net/ethernet/marvell/octeontx2/nic/qos.h new file mode 100644 index 000000000000..19773284be27 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/qos.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Marvell RVU Ethernet driver + * + * Copyright (C) 2023 Marvell. + * + */ +#ifndef OTX2_QOS_H +#define OTX2_QOS_H + +#include <linux/types.h> +#include <linux/netdevice.h> +#include <linux/rhashtable.h> + +#define OTX2_QOS_MAX_LVL 4 +#define OTX2_QOS_MAX_PRIO 7 +#define OTX2_QOS_MAX_LEAF_NODES 16 + +enum qos_smq_operations { + QOS_CFG_SQ, + QOS_SMQ_FLUSH, +}; + +u64 otx2_get_txschq_rate_regval(struct otx2_nic *nic, u64 maxrate, u32 burst); + +int otx2_setup_tc_htb(struct net_device *ndev, struct tc_htb_qopt_offload *htb); +int otx2_qos_get_qid(struct otx2_nic *pfvf); +void otx2_qos_free_qid(struct otx2_nic *pfvf, int qidx); +int otx2_qos_enable_sq(struct otx2_nic *pfvf, int qidx); +void otx2_qos_disable_sq(struct otx2_nic *pfvf, int qidx); + +struct otx2_qos_cfg { + u16 schq[NIX_TXSCH_LVL_CNT]; + u16 schq_contig[NIX_TXSCH_LVL_CNT]; + int static_node_pos[NIX_TXSCH_LVL_CNT]; + int dwrr_node_pos[NIX_TXSCH_LVL_CNT]; + u16 schq_contig_list[NIX_TXSCH_LVL_CNT][MAX_TXSCHQ_PER_FUNC]; + u16 schq_list[NIX_TXSCH_LVL_CNT][MAX_TXSCHQ_PER_FUNC]; +}; + +struct otx2_qos { + DECLARE_HASHTABLE(qos_hlist, order_base_2(OTX2_QOS_MAX_LEAF_NODES)); + struct mutex qos_lock; /* child list lock */ + u16 qid_to_sqmap[OTX2_QOS_MAX_LEAF_NODES]; + struct list_head qos_tree; + DECLARE_BITMAP(qos_sq_bmap, OTX2_QOS_MAX_LEAF_NODES); + u16 maj_id; + u16 defcls; + u8 link_cfg_lvl; /* LINKX_CFG CSRs mapped to TL3 or TL2's index ? */ +}; + +struct otx2_qos_node { + struct list_head list; /* list management */ + struct list_head child_list; + struct list_head child_schq_list; + struct hlist_node hlist; + DECLARE_BITMAP(prio_bmap, OTX2_QOS_MAX_PRIO + 1); + struct otx2_qos_node *parent; /* parent qos node */ + u64 rate; /* htb params */ + u64 ceil; + u32 classid; + u32 prio; + u16 schq; /* hw txschq */ + u16 qid; + u16 prio_anchor; + u8 level; +}; + + +#endif diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/qos_sq.c b/drivers/net/ethernet/marvell/octeontx2/nic/qos_sq.c new file mode 100644 index 000000000000..d96ed29c1567 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/qos_sq.c @@ -0,0 +1,296 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell RVU Physical Function ethernet driver + * + * Copyright (C) 2023 Marvell. + * + */ + +#include <linux/netdevice.h> +#include <net/tso.h> + +#include "cn10k.h" +#include "otx2_reg.h" +#include "otx2_common.h" +#include "otx2_txrx.h" +#include "otx2_struct.h" + +#define OTX2_QOS_MAX_LEAF_NODES 16 + +static void otx2_qos_aura_pool_free(struct otx2_nic *pfvf, int pool_id) +{ + struct otx2_pool *pool; + + if (!pfvf->qset.pool) + return; + + pool = &pfvf->qset.pool[pool_id]; + qmem_free(pfvf->dev, pool->stack); + qmem_free(pfvf->dev, pool->fc_addr); + pool->stack = NULL; + pool->fc_addr = NULL; +} + +static int otx2_qos_sq_aura_pool_init(struct otx2_nic *pfvf, int qidx) +{ + struct otx2_qset *qset = &pfvf->qset; + int pool_id, stack_pages, num_sqbs; + struct otx2_hw *hw = &pfvf->hw; + struct otx2_snd_queue *sq; + struct otx2_pool *pool; + dma_addr_t bufptr; + int err, ptr; + u64 iova, pa; + + /* Calculate number of SQBs needed. + * + * For a 128byte SQE, and 4K size SQB, 31 SQEs will fit in one SQB. + * Last SQE is used for pointing to next SQB. + */ + num_sqbs = (hw->sqb_size / 128) - 1; + num_sqbs = (qset->sqe_cnt + num_sqbs) / num_sqbs; + + /* Get no of stack pages needed */ + stack_pages = + (num_sqbs + hw->stack_pg_ptrs - 1) / hw->stack_pg_ptrs; + + pool_id = otx2_get_pool_idx(pfvf, AURA_NIX_SQ, qidx); + pool = &pfvf->qset.pool[pool_id]; + + /* Initialize aura context */ + err = otx2_aura_init(pfvf, pool_id, pool_id, num_sqbs); + if (err) + return err; + + /* Initialize pool context */ + err = otx2_pool_init(pfvf, pool_id, stack_pages, + num_sqbs, hw->sqb_size); + if (err) + goto aura_free; + + /* Flush accumulated messages */ + err = otx2_sync_mbox_msg(&pfvf->mbox); + if (err) + goto pool_free; + + /* Allocate pointers and free them to aura/pool */ + sq = &qset->sq[qidx]; + sq->sqb_count = 0; + sq->sqb_ptrs = kcalloc(num_sqbs, sizeof(*sq->sqb_ptrs), GFP_KERNEL); + if (!sq->sqb_ptrs) { + err = -ENOMEM; + goto pool_free; + } + + for (ptr = 0; ptr < num_sqbs; ptr++) { + err = otx2_alloc_rbuf(pfvf, pool, &bufptr); + if (err) + goto sqb_free; + pfvf->hw_ops->aura_freeptr(pfvf, pool_id, bufptr); + sq->sqb_ptrs[sq->sqb_count++] = (u64)bufptr; + } + + return 0; + +sqb_free: + while (ptr--) { + if (!sq->sqb_ptrs[ptr]) + continue; + iova = sq->sqb_ptrs[ptr]; + pa = otx2_iova_to_phys(pfvf->iommu_domain, iova); + dma_unmap_page_attrs(pfvf->dev, iova, hw->sqb_size, + DMA_FROM_DEVICE, + DMA_ATTR_SKIP_CPU_SYNC); + put_page(virt_to_page(phys_to_virt(pa))); + otx2_aura_allocptr(pfvf, pool_id); + } + sq->sqb_count = 0; + kfree(sq->sqb_ptrs); +pool_free: + qmem_free(pfvf->dev, pool->stack); +aura_free: + qmem_free(pfvf->dev, pool->fc_addr); + otx2_mbox_reset(&pfvf->mbox.mbox, 0); + return err; +} + +static void otx2_qos_sq_free_sqbs(struct otx2_nic *pfvf, int qidx) +{ + struct otx2_qset *qset = &pfvf->qset; + struct otx2_hw *hw = &pfvf->hw; + struct otx2_snd_queue *sq; + u64 iova, pa; + int sqb; + + sq = &qset->sq[qidx]; + if (!sq->sqb_ptrs) + return; + for (sqb = 0; sqb < sq->sqb_count; sqb++) { + if (!sq->sqb_ptrs[sqb]) + continue; + iova = sq->sqb_ptrs[sqb]; + pa = otx2_iova_to_phys(pfvf->iommu_domain, iova); + dma_unmap_page_attrs(pfvf->dev, iova, hw->sqb_size, + DMA_FROM_DEVICE, + DMA_ATTR_SKIP_CPU_SYNC); + put_page(virt_to_page(phys_to_virt(pa))); + } + + sq->sqb_count = 0; + + sq = &qset->sq[qidx]; + qmem_free(pfvf->dev, sq->sqe); + qmem_free(pfvf->dev, sq->tso_hdrs); + kfree(sq->sg); + kfree(sq->sqb_ptrs); + qmem_free(pfvf->dev, sq->timestamps); + + memset((void *)sq, 0, sizeof(*sq)); +} + +/* send queue id */ +static void otx2_qos_sqb_flush(struct otx2_nic *pfvf, int qidx) +{ + int sqe_tail, sqe_head; + u64 incr, *ptr, val; + + ptr = (__force u64 *)otx2_get_regaddr(pfvf, NIX_LF_SQ_OP_STATUS); + incr = (u64)qidx << 32; + val = otx2_atomic64_add(incr, ptr); + sqe_head = (val >> 20) & 0x3F; + sqe_tail = (val >> 28) & 0x3F; + if (sqe_head != sqe_tail) + usleep_range(50, 60); +} + +static int otx2_qos_ctx_disable(struct otx2_nic *pfvf, u16 qidx, int aura_id) +{ + struct nix_cn10k_aq_enq_req *cn10k_sq_aq; + struct npa_aq_enq_req *aura_aq; + struct npa_aq_enq_req *pool_aq; + struct nix_aq_enq_req *sq_aq; + + if (test_bit(CN10K_LMTST, &pfvf->hw.cap_flag)) { + cn10k_sq_aq = otx2_mbox_alloc_msg_nix_cn10k_aq_enq(&pfvf->mbox); + if (!cn10k_sq_aq) + return -ENOMEM; + cn10k_sq_aq->qidx = qidx; + cn10k_sq_aq->sq.ena = 0; + cn10k_sq_aq->sq_mask.ena = 1; + cn10k_sq_aq->ctype = NIX_AQ_CTYPE_SQ; + cn10k_sq_aq->op = NIX_AQ_INSTOP_WRITE; + } else { + sq_aq = otx2_mbox_alloc_msg_nix_aq_enq(&pfvf->mbox); + if (!sq_aq) + return -ENOMEM; + sq_aq->qidx = qidx; + sq_aq->sq.ena = 0; + sq_aq->sq_mask.ena = 1; + sq_aq->ctype = NIX_AQ_CTYPE_SQ; + sq_aq->op = NIX_AQ_INSTOP_WRITE; + } + + aura_aq = otx2_mbox_alloc_msg_npa_aq_enq(&pfvf->mbox); + if (!aura_aq) { + otx2_mbox_reset(&pfvf->mbox.mbox, 0); + return -ENOMEM; + } + + aura_aq->aura_id = aura_id; + aura_aq->aura.ena = 0; + aura_aq->aura_mask.ena = 1; + aura_aq->ctype = NPA_AQ_CTYPE_AURA; + aura_aq->op = NPA_AQ_INSTOP_WRITE; + + pool_aq = otx2_mbox_alloc_msg_npa_aq_enq(&pfvf->mbox); + if (!pool_aq) { + otx2_mbox_reset(&pfvf->mbox.mbox, 0); + return -ENOMEM; + } + + pool_aq->aura_id = aura_id; + pool_aq->pool.ena = 0; + pool_aq->pool_mask.ena = 1; + + pool_aq->ctype = NPA_AQ_CTYPE_POOL; + pool_aq->op = NPA_AQ_INSTOP_WRITE; + + return otx2_sync_mbox_msg(&pfvf->mbox); +} + +int otx2_qos_get_qid(struct otx2_nic *pfvf) +{ + int qidx; + + qidx = find_first_zero_bit(pfvf->qos.qos_sq_bmap, + pfvf->hw.tc_tx_queues); + + return qidx == pfvf->hw.tc_tx_queues ? -ENOSPC : qidx; +} + +void otx2_qos_free_qid(struct otx2_nic *pfvf, int qidx) +{ + clear_bit(qidx, pfvf->qos.qos_sq_bmap); +} + +int otx2_qos_enable_sq(struct otx2_nic *pfvf, int qidx) +{ + struct otx2_hw *hw = &pfvf->hw; + int pool_id, sq_idx, err; + + if (pfvf->flags & OTX2_FLAG_INTF_DOWN) + return -EPERM; + + sq_idx = hw->non_qos_queues + qidx; + + mutex_lock(&pfvf->mbox.lock); + err = otx2_qos_sq_aura_pool_init(pfvf, sq_idx); + if (err) + goto out; + + pool_id = otx2_get_pool_idx(pfvf, AURA_NIX_SQ, sq_idx); + err = otx2_sq_init(pfvf, sq_idx, pool_id); + if (err) + goto out; +out: + mutex_unlock(&pfvf->mbox.lock); + return err; +} + +void otx2_qos_disable_sq(struct otx2_nic *pfvf, int qidx) +{ + struct otx2_qset *qset = &pfvf->qset; + struct otx2_hw *hw = &pfvf->hw; + struct otx2_snd_queue *sq; + struct otx2_cq_queue *cq; + int pool_id, sq_idx; + + sq_idx = hw->non_qos_queues + qidx; + + /* If the DOWN flag is set SQs are already freed */ + if (pfvf->flags & OTX2_FLAG_INTF_DOWN) + return; + + sq = &pfvf->qset.sq[sq_idx]; + if (!sq->sqb_ptrs) + return; + + if (sq_idx < hw->non_qos_queues || + sq_idx >= otx2_get_total_tx_queues(pfvf)) { + netdev_err(pfvf->netdev, "Send Queue is not a QoS queue\n"); + return; + } + + cq = &qset->cq[pfvf->hw.rx_queues + sq_idx]; + pool_id = otx2_get_pool_idx(pfvf, AURA_NIX_SQ, sq_idx); + + otx2_qos_sqb_flush(pfvf, sq_idx); + otx2_smq_flush(pfvf, otx2_get_smq_idx(pfvf, sq_idx)); + otx2_cleanup_tx_cqes(pfvf, cq); + + mutex_lock(&pfvf->mbox.lock); + otx2_qos_ctx_disable(pfvf, sq_idx, pool_id); + mutex_unlock(&pfvf->mbox.lock); + + otx2_qos_sq_free_sqbs(pfvf, sq_idx); + otx2_qos_aura_pool_free(pfvf, pool_id); +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/qos.c b/drivers/net/ethernet/mellanox/mlx5/core/en/qos.c index 2842195ee548..1874c2f0587f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/qos.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/qos.c @@ -379,6 +379,12 @@ int mlx5e_htb_setup_tc(struct mlx5e_priv *priv, struct tc_htb_qopt_offload *htb_ if (!htb && htb_qopt->command != TC_HTB_CREATE) return -EINVAL; + if (htb_qopt->prio) { + NL_SET_ERR_MSG_MOD(htb_qopt->extack, + "prio parameter is not supported by device with HTB offload enabled."); + return -EOPNOTSUPP; + } + switch (htb_qopt->command) { case TC_HTB_CREATE: if (!mlx5_qos_is_supported(priv->mdev)) { @@ -515,4 +521,3 @@ int mlx5e_mqprio_rl_get_node_hw_id(struct mlx5e_mqprio_rl *rl, int tc, u32 *hw_i *hw_id = rl->leaves_id[tc]; return 0; } - diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c index 6b7b563f844a..592b165530ff 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c @@ -349,15 +349,6 @@ static void mlx5e_macsec_cleanup_sa(struct mlx5e_macsec *macsec, sa->macsec_rule = NULL; } -static struct mlx5e_priv *macsec_netdev_priv(const struct net_device *dev) -{ -#if IS_ENABLED(CONFIG_VLAN_8021Q) - if (is_vlan_dev(dev)) - return netdev_priv(vlan_dev_priv(dev)->real_dev); -#endif - return netdev_priv(dev); -} - static int mlx5e_macsec_init_sa(struct macsec_context *ctx, struct mlx5e_macsec_sa *sa, bool encrypt, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index 69634829558e..704b022cd1f0 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -491,9 +491,7 @@ mlx5e_add_skb_shared_info_frag(struct mlx5e_rq *rq, struct skb_shared_info *sinf } frag = &sinfo->frags[sinfo->nr_frags++]; - __skb_frag_set_page(frag, frag_page->page); - skb_frag_off_set(frag, frag_offset); - skb_frag_size_set(frag, len); + skb_frag_fill_page_desc(frag, frag_page->page, frag_offset, len); if (page_is_pfmemalloc(frag_page->page)) xdp_buff_set_frag_pfmemalloc(xdp); diff --git a/drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2_format.h b/drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2_format.h index b001e5258091..47f6cc0401c3 100644 --- a/drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2_format.h +++ b/drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2_format.h @@ -44,7 +44,7 @@ MLXFW_MFA2_TLV(multi, struct mlxfw_mfa2_tlv_multi, MLXFW_MFA2_TLV_MULTI_PART); struct mlxfw_mfa2_tlv_psid { - u8 psid[0]; + DECLARE_FLEX_ARRAY(u8, psid); } __packed; MLXFW_MFA2_TLV_VARSIZE(psid, struct mlxfw_mfa2_tlv_psid, diff --git a/drivers/net/ethernet/microchip/enc28j60.c b/drivers/net/ethernet/microchip/enc28j60.c index 176efbeae127..d6c9491537e4 100644 --- a/drivers/net/ethernet/microchip/enc28j60.c +++ b/drivers/net/ethernet/microchip/enc28j60.c @@ -58,7 +58,6 @@ struct enc28j60_net { struct mutex lock; struct sk_buff *tx_skb; struct work_struct tx_work; - struct work_struct irq_work; struct work_struct setrx_work; struct work_struct restart_work; u8 bank; /* current register bank selected */ @@ -1118,10 +1117,9 @@ static int enc28j60_rx_interrupt(struct net_device *ndev) return ret; } -static void enc28j60_irq_work_handler(struct work_struct *work) +static irqreturn_t enc28j60_irq(int irq, void *dev_id) { - struct enc28j60_net *priv = - container_of(work, struct enc28j60_net, irq_work); + struct enc28j60_net *priv = dev_id; struct net_device *ndev = priv->netdev; int intflags, loop; @@ -1225,6 +1223,8 @@ static void enc28j60_irq_work_handler(struct work_struct *work) /* re-enable interrupts */ locked_reg_bfset(priv, EIE, EIE_INTIE); + + return IRQ_HANDLED; } /* @@ -1309,22 +1309,6 @@ static void enc28j60_tx_work_handler(struct work_struct *work) enc28j60_hw_tx(priv); } -static irqreturn_t enc28j60_irq(int irq, void *dev_id) -{ - struct enc28j60_net *priv = dev_id; - - /* - * Can't do anything in interrupt context because we need to - * block (spi_sync() is blocking) so fire of the interrupt - * handling workqueue. - * Remember that we access enc28j60 registers through SPI bus - * via spi_sync() call. - */ - schedule_work(&priv->irq_work); - - return IRQ_HANDLED; -} - static void enc28j60_tx_timeout(struct net_device *ndev, unsigned int txqueue) { struct enc28j60_net *priv = netdev_priv(ndev); @@ -1559,7 +1543,6 @@ static int enc28j60_probe(struct spi_device *spi) mutex_init(&priv->lock); INIT_WORK(&priv->tx_work, enc28j60_tx_work_handler); INIT_WORK(&priv->setrx_work, enc28j60_setrx_work_handler); - INIT_WORK(&priv->irq_work, enc28j60_irq_work_handler); INIT_WORK(&priv->restart_work, enc28j60_restart_work_handler); spi_set_drvdata(spi, priv); /* spi to priv reference */ SET_NETDEV_DEV(dev, &spi->dev); @@ -1578,7 +1561,8 @@ static int enc28j60_probe(struct spi_device *spi) /* Board setup must set the relevant edge trigger type; * level triggers won't currently work. */ - ret = request_irq(spi->irq, enc28j60_irq, 0, DRV_NAME, priv); + ret = request_threaded_irq(spi->irq, NULL, enc28j60_irq, IRQF_ONESHOT, + DRV_NAME, priv); if (ret < 0) { if (netif_msg_probe(priv)) dev_err(&spi->dev, "request irq %d failed (ret = %d)\n", diff --git a/drivers/net/ethernet/microchip/lan966x/Kconfig b/drivers/net/ethernet/microchip/lan966x/Kconfig index 571e6d4da1e9..f9ebffc04eb8 100644 --- a/drivers/net/ethernet/microchip/lan966x/Kconfig +++ b/drivers/net/ethernet/microchip/lan966x/Kconfig @@ -10,3 +10,14 @@ config LAN966X_SWITCH select VCAP help This driver supports the Lan966x network switch device. + +config LAN966X_DCB + bool "Data Center Bridging (DCB) support" + depends on LAN966X_SWITCH && DCB + default y + help + Say Y here if you want to use Data Center Bridging (DCB) in the + driver. This can be used to assign priority to traffic, based on + DSCP and PCP. + + If unsure, set to Y. diff --git a/drivers/net/ethernet/microchip/lan966x/Makefile b/drivers/net/ethernet/microchip/lan966x/Makefile index 7b0cda4ffa6b..3b6ac331691d 100644 --- a/drivers/net/ethernet/microchip/lan966x/Makefile +++ b/drivers/net/ethernet/microchip/lan966x/Makefile @@ -15,6 +15,7 @@ lan966x-switch-objs := lan966x_main.o lan966x_phylink.o lan966x_port.o \ lan966x_xdp.o lan966x_vcap_impl.o lan966x_vcap_ag_api.o \ lan966x_tc_flower.o lan966x_goto.o +lan966x-switch-$(CONFIG_LAN966X_DCB) += lan966x_dcb.o lan966x-switch-$(CONFIG_DEBUG_FS) += lan966x_vcap_debugfs.o # Provide include files diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_dcb.c b/drivers/net/ethernet/microchip/lan966x/lan966x_dcb.c new file mode 100644 index 000000000000..ed2d96d7908e --- /dev/null +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_dcb.c @@ -0,0 +1,365 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include "lan966x_main.h" + +enum lan966x_dcb_apptrust_values { + LAN966X_DCB_APPTRUST_EMPTY, + LAN966X_DCB_APPTRUST_DSCP, + LAN966X_DCB_APPTRUST_PCP, + LAN966X_DCB_APPTRUST_DSCP_PCP, + __LAN966X_DCB_APPTRUST_MAX +}; + +static const struct lan966x_dcb_apptrust { + u8 selectors[IEEE_8021QAZ_APP_SEL_MAX + 1]; + int nselectors; +} *lan966x_port_apptrust[NUM_PHYS_PORTS]; + +static const char *lan966x_dcb_apptrust_names[__LAN966X_DCB_APPTRUST_MAX] = { + [LAN966X_DCB_APPTRUST_EMPTY] = "empty", + [LAN966X_DCB_APPTRUST_DSCP] = "dscp", + [LAN966X_DCB_APPTRUST_PCP] = "pcp", + [LAN966X_DCB_APPTRUST_DSCP_PCP] = "dscp pcp" +}; + +/* Lan966x supported apptrust policies */ +static const struct lan966x_dcb_apptrust + lan966x_dcb_apptrust_policies[__LAN966X_DCB_APPTRUST_MAX] = { + /* Empty *must* be first */ + [LAN966X_DCB_APPTRUST_EMPTY] = { { 0 }, 0 }, + [LAN966X_DCB_APPTRUST_DSCP] = { { IEEE_8021QAZ_APP_SEL_DSCP }, 1 }, + [LAN966X_DCB_APPTRUST_PCP] = { { DCB_APP_SEL_PCP }, 1 }, + [LAN966X_DCB_APPTRUST_DSCP_PCP] = { { IEEE_8021QAZ_APP_SEL_DSCP, + DCB_APP_SEL_PCP }, 2 }, +}; + +static bool lan966x_dcb_apptrust_contains(int portno, u8 selector) +{ + const struct lan966x_dcb_apptrust *conf = lan966x_port_apptrust[portno]; + + for (int i = 0; i < conf->nselectors; i++) + if (conf->selectors[i] == selector) + return true; + + return false; +} + +static void lan966x_dcb_app_update(struct net_device *dev) +{ + struct dcb_ieee_app_prio_map dscp_rewr_map = {0}; + struct dcb_rewr_prio_pcp_map pcp_rewr_map = {0}; + struct lan966x_port *port = netdev_priv(dev); + struct lan966x_port_qos qos = {0}; + struct dcb_app app_itr; + bool dscp_rewr = false; + bool pcp_rewr = false; + + /* Get pcp ingress mapping */ + for (int i = 0; i < ARRAY_SIZE(qos.pcp.map); i++) { + app_itr.selector = DCB_APP_SEL_PCP; + app_itr.protocol = i; + qos.pcp.map[i] = dcb_getapp(dev, &app_itr); + } + + /* Get dscp ingress mapping */ + for (int i = 0; i < ARRAY_SIZE(qos.dscp.map); i++) { + app_itr.selector = IEEE_8021QAZ_APP_SEL_DSCP; + app_itr.protocol = i; + qos.dscp.map[i] = dcb_getapp(dev, &app_itr); + } + + /* Get default prio */ + qos.default_prio = dcb_ieee_getapp_default_prio_mask(dev); + if (qos.default_prio) + qos.default_prio = fls(qos.default_prio) - 1; + + /* Get pcp rewrite mapping */ + dcb_getrewr_prio_pcp_mask_map(dev, &pcp_rewr_map); + for (int i = 0; i < ARRAY_SIZE(pcp_rewr_map.map); i++) { + if (!pcp_rewr_map.map[i]) + continue; + + pcp_rewr = true; + qos.pcp_rewr.map[i] = fls(pcp_rewr_map.map[i]) - 1; + } + + /* Get dscp rewrite mapping */ + dcb_getrewr_prio_dscp_mask_map(dev, &dscp_rewr_map); + for (int i = 0; i < ARRAY_SIZE(dscp_rewr_map.map); i++) { + if (!dscp_rewr_map.map[i]) + continue; + + dscp_rewr = true; + qos.dscp_rewr.map[i] = fls64(dscp_rewr_map.map[i]) - 1; + } + + /* Enable use of pcp for queue classification */ + if (lan966x_dcb_apptrust_contains(port->chip_port, DCB_APP_SEL_PCP)) { + qos.pcp.enable = true; + + if (pcp_rewr) + qos.pcp_rewr.enable = true; + } + + /* Enable use of dscp for queue classification */ + if (lan966x_dcb_apptrust_contains(port->chip_port, IEEE_8021QAZ_APP_SEL_DSCP)) { + qos.dscp.enable = true; + + if (dscp_rewr) + qos.dscp_rewr.enable = true; + } + + lan966x_port_qos_set(port, &qos); +} + +/* DSCP mapping is global for all ports, so set and delete app entries are + * replicated for each port. + */ +static int lan966x_dcb_ieee_dscp_setdel(struct net_device *dev, + struct dcb_app *app, + int (*setdel)(struct net_device *, + struct dcb_app *)) +{ + struct lan966x_port *port = netdev_priv(dev); + struct lan966x *lan966x = port->lan966x; + int err; + + for (int i = 0; i < NUM_PHYS_PORTS; i++) { + port = lan966x->ports[i]; + if (!port) + continue; + + err = setdel(port->dev, app); + if (err) + return err; + } + + return 0; +} + +static int lan966x_dcb_app_validate(struct net_device *dev, + const struct dcb_app *app) +{ + int err = 0; + + switch (app->selector) { + /* Default priority checks */ + case IEEE_8021QAZ_APP_SEL_ETHERTYPE: + if (app->protocol) + err = -EINVAL; + else if (app->priority >= NUM_PRIO_QUEUES) + err = -ERANGE; + break; + /* Dscp checks */ + case IEEE_8021QAZ_APP_SEL_DSCP: + if (app->protocol >= LAN966X_PORT_QOS_DSCP_COUNT) + err = -EINVAL; + else if (app->priority >= NUM_PRIO_QUEUES) + err = -ERANGE; + break; + /* Pcp checks */ + case DCB_APP_SEL_PCP: + if (app->protocol >= LAN966X_PORT_QOS_PCP_DEI_COUNT) + err = -EINVAL; + else if (app->priority >= NUM_PRIO_QUEUES) + err = -ERANGE; + break; + default: + err = -EINVAL; + break; + } + + if (err) + netdev_err(dev, "Invalid entry: %d:%d\n", app->protocol, + app->priority); + + return err; +} + +static int lan966x_dcb_ieee_delapp(struct net_device *dev, struct dcb_app *app) +{ + int err; + + if (app->selector == IEEE_8021QAZ_APP_SEL_DSCP) + err = lan966x_dcb_ieee_dscp_setdel(dev, app, dcb_ieee_delapp); + else + err = dcb_ieee_delapp(dev, app); + + if (err) + return err; + + lan966x_dcb_app_update(dev); + + return 0; +} + +static int lan966x_dcb_ieee_setapp(struct net_device *dev, struct dcb_app *app) +{ + struct dcb_app app_itr; + int err; + u8 prio; + + err = lan966x_dcb_app_validate(dev, app); + if (err) + return err; + + /* Delete current mapping, if it exists */ + prio = dcb_getapp(dev, app); + if (prio) { + app_itr = *app; + app_itr.priority = prio; + lan966x_dcb_ieee_delapp(dev, &app_itr); + } + + if (app->selector == IEEE_8021QAZ_APP_SEL_DSCP) + err = lan966x_dcb_ieee_dscp_setdel(dev, app, dcb_ieee_setapp); + else + err = dcb_ieee_setapp(dev, app); + + if (err) + return err; + + lan966x_dcb_app_update(dev); + + return 0; +} + +static int lan966x_dcb_apptrust_validate(struct net_device *dev, + u8 *selectors, + int nselectors) +{ + for (int i = 0; i < ARRAY_SIZE(lan966x_dcb_apptrust_policies); i++) { + bool match; + + if (lan966x_dcb_apptrust_policies[i].nselectors != nselectors) + continue; + + match = true; + for (int j = 0; j < nselectors; j++) { + if (lan966x_dcb_apptrust_policies[i].selectors[j] != + *(selectors + j)) { + match = false; + break; + } + } + if (match) + return i; + } + + netdev_err(dev, "Valid apptrust configurations are:\n"); + for (int i = 0; i < ARRAY_SIZE(lan966x_dcb_apptrust_names); i++) + pr_info("order: %s\n", lan966x_dcb_apptrust_names[i]); + + return -EOPNOTSUPP; +} + +static int lan966x_dcb_setapptrust(struct net_device *dev, + u8 *selectors, + int nselectors) +{ + struct lan966x_port *port = netdev_priv(dev); + int idx; + + idx = lan966x_dcb_apptrust_validate(dev, selectors, nselectors); + if (idx < 0) + return idx; + + lan966x_port_apptrust[port->chip_port] = &lan966x_dcb_apptrust_policies[idx]; + lan966x_dcb_app_update(dev); + + return 0; +} + +static int lan966x_dcb_getapptrust(struct net_device *dev, u8 *selectors, + int *nselectors) +{ + struct lan966x_port *port = netdev_priv(dev); + const struct lan966x_dcb_apptrust *trust; + + trust = lan966x_port_apptrust[port->chip_port]; + + memcpy(selectors, trust->selectors, trust->nselectors); + *nselectors = trust->nselectors; + + return 0; +} + +static int lan966x_dcb_delrewr(struct net_device *dev, struct dcb_app *app) +{ + int err; + + if (app->selector == IEEE_8021QAZ_APP_SEL_DSCP) + err = lan966x_dcb_ieee_dscp_setdel(dev, app, dcb_delrewr); + else + err = dcb_delrewr(dev, app); + + if (err < 0) + return err; + + lan966x_dcb_app_update(dev); + + return 0; +} + +static int lan966x_dcb_setrewr(struct net_device *dev, struct dcb_app *app) +{ + struct dcb_app app_itr; + u16 proto; + int err; + + err = lan966x_dcb_app_validate(dev, app); + if (err) + goto out; + + /* Delete current mapping, if it exists. */ + proto = dcb_getrewr(dev, app); + if (proto) { + app_itr = *app; + app_itr.protocol = proto; + lan966x_dcb_delrewr(dev, &app_itr); + } + + if (app->selector == IEEE_8021QAZ_APP_SEL_DSCP) + err = lan966x_dcb_ieee_dscp_setdel(dev, app, dcb_setrewr); + else + err = dcb_setrewr(dev, app); + + if (err) + goto out; + + lan966x_dcb_app_update(dev); + +out: + return err; +} + +static const struct dcbnl_rtnl_ops lan966x_dcbnl_ops = { + .ieee_setapp = lan966x_dcb_ieee_setapp, + .ieee_delapp = lan966x_dcb_ieee_delapp, + .dcbnl_setapptrust = lan966x_dcb_setapptrust, + .dcbnl_getapptrust = lan966x_dcb_getapptrust, + .dcbnl_setrewr = lan966x_dcb_setrewr, + .dcbnl_delrewr = lan966x_dcb_delrewr, +}; + +void lan966x_dcb_init(struct lan966x *lan966x) +{ + for (int p = 0; p < lan966x->num_phys_ports; ++p) { + struct lan966x_port *port; + + port = lan966x->ports[p]; + if (!port) + continue; + + port->dev->dcbnl_ops = &lan966x_dcbnl_ops; + + lan966x_port_apptrust[port->chip_port] = + &lan966x_dcb_apptrust_policies[LAN966X_DCB_APPTRUST_DSCP_PCP]; + + /* Enable DSCP classification based on classified QoS class and + * DP, for all DSCP values, for all ports. + */ + lan966x_port_qos_dscp_rewr_mode_set(port, + LAN966X_PORT_QOS_REWR_DSCP_ALL); + } +} diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c index 2b6e046e1d10..5f01b21acdd1 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c @@ -1213,6 +1213,8 @@ static int lan966x_probe(struct platform_device *pdev) if (err) goto cleanup_fdma; + lan966x_dcb_init(lan966x); + return 0; cleanup_fdma: diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h index c977c70abc3d..27f272831ea5 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h @@ -101,6 +101,25 @@ #define LAN966X_VCAP_CID_IS2_L1 VCAP_CID_INGRESS_STAGE2_L1 /* IS2 lookup 1 */ #define LAN966X_VCAP_CID_IS2_MAX (VCAP_CID_INGRESS_STAGE2_L2 - 1) /* IS2 Max */ +#define LAN966X_VCAP_CID_ES0_L0 VCAP_CID_EGRESS_L0 /* ES0 lookup 0 */ +#define LAN966X_VCAP_CID_ES0_MAX (VCAP_CID_EGRESS_L1 - 1) /* ES0 Max */ + +#define LAN966X_PORT_QOS_PCP_COUNT 8 +#define LAN966X_PORT_QOS_DEI_COUNT 8 +#define LAN966X_PORT_QOS_PCP_DEI_COUNT \ + (LAN966X_PORT_QOS_PCP_COUNT + LAN966X_PORT_QOS_DEI_COUNT) + +#define LAN966X_PORT_QOS_DSCP_COUNT 64 + +/* Port PCP rewrite mode */ +#define LAN966X_PORT_REW_TAG_CTRL_CLASSIFIED 0 +#define LAN966X_PORT_REW_TAG_CTRL_MAPPED 2 + +/* Port DSCP rewrite mode */ +#define LAN966X_PORT_REW_DSCP_FRAME 0 +#define LAN966X_PORT_REW_DSCP_ANALIZER 1 +#define LAN966X_PORT_QOS_REWR_DSCP_ALL 3 + /* MAC table entry types. * ENTRYTYPE_NORMAL is subject to aging. * ENTRYTYPE_LOCKED is not subject to aging. @@ -389,6 +408,34 @@ struct lan966x_port_tc { struct flow_stats mirror_stat; }; +struct lan966x_port_qos_pcp { + u8 map[LAN966X_PORT_QOS_PCP_DEI_COUNT]; + bool enable; +}; + +struct lan966x_port_qos_dscp { + u8 map[LAN966X_PORT_QOS_DSCP_COUNT]; + bool enable; +}; + +struct lan966x_port_qos_pcp_rewr { + u16 map[NUM_PRIO_QUEUES]; + bool enable; +}; + +struct lan966x_port_qos_dscp_rewr { + u16 map[LAN966X_PORT_QOS_DSCP_COUNT]; + bool enable; +}; + +struct lan966x_port_qos { + struct lan966x_port_qos_pcp pcp; + struct lan966x_port_qos_dscp dscp; + struct lan966x_port_qos_pcp_rewr pcp_rewr; + struct lan966x_port_qos_dscp_rewr dscp_rewr; + u8 default_prio; +}; + struct lan966x_port { struct net_device *dev; struct lan966x *lan966x; @@ -453,6 +500,11 @@ int lan966x_port_pcs_set(struct lan966x_port *port, struct lan966x_port_config *config); void lan966x_port_init(struct lan966x_port *port); +void lan966x_port_qos_set(struct lan966x_port *port, + struct lan966x_port_qos *qos); +void lan966x_port_qos_dscp_rewr_mode_set(struct lan966x_port *port, + int mode); + int lan966x_mac_ip_learn(struct lan966x *lan966x, bool cpu_copy, const unsigned char mac[ETH_ALEN], @@ -677,6 +729,14 @@ int lan966x_goto_port_del(struct lan966x_port *port, unsigned long goto_id, struct netlink_ext_ack *extack); +#ifdef CONFIG_LAN966X_DCB +void lan966x_dcb_init(struct lan966x *lan966x); +#else +static inline void lan966x_dcb_init(struct lan966x *lan966x) +{ +} +#endif + 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_port.c b/drivers/net/ethernet/microchip/lan966x/lan966x_port.c index 0050fcb988b7..92108d354051 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_port.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_port.c @@ -394,6 +394,155 @@ int lan966x_port_pcs_set(struct lan966x_port *port, return 0; } +static void lan966x_port_qos_pcp_set(struct lan966x_port *port, + struct lan966x_port_qos_pcp *qos) +{ + u8 *pcp_itr = qos->map; + u8 pcp, dp; + + lan_rmw(ANA_QOS_CFG_QOS_PCP_ENA_SET(qos->enable), + ANA_QOS_CFG_QOS_PCP_ENA, + port->lan966x, ANA_QOS_CFG(port->chip_port)); + + /* Map PCP and DEI to priority */ + for (int i = 0; i < ARRAY_SIZE(qos->map); i++) { + pcp = *(pcp_itr + i); + dp = (i < LAN966X_PORT_QOS_PCP_COUNT) ? 0 : 1; + + lan_rmw(ANA_PCP_DEI_CFG_QOS_PCP_DEI_VAL_SET(pcp) | + ANA_PCP_DEI_CFG_DP_PCP_DEI_VAL_SET(dp), + ANA_PCP_DEI_CFG_QOS_PCP_DEI_VAL | + ANA_PCP_DEI_CFG_DP_PCP_DEI_VAL, + port->lan966x, + ANA_PCP_DEI_CFG(port->chip_port, i)); + } +} + +static void lan966x_port_qos_dscp_set(struct lan966x_port *port, + struct lan966x_port_qos_dscp *qos) +{ + struct lan966x *lan966x = port->lan966x; + + /* Enable/disable dscp for qos classification. */ + lan_rmw(ANA_QOS_CFG_QOS_DSCP_ENA_SET(qos->enable), + ANA_QOS_CFG_QOS_DSCP_ENA, + lan966x, ANA_QOS_CFG(port->chip_port)); + + /* Map each dscp value to priority and dp */ + for (int i = 0; i < ARRAY_SIZE(qos->map); i++) + lan_rmw(ANA_DSCP_CFG_DP_DSCP_VAL_SET(0) | + ANA_DSCP_CFG_QOS_DSCP_VAL_SET(*(qos->map + i)), + ANA_DSCP_CFG_DP_DSCP_VAL | + ANA_DSCP_CFG_QOS_DSCP_VAL, + lan966x, ANA_DSCP_CFG(i)); + + /* Set per-dscp trust */ + for (int i = 0; i < ARRAY_SIZE(qos->map); i++) + lan_rmw(ANA_DSCP_CFG_DSCP_TRUST_ENA_SET(qos->enable), + ANA_DSCP_CFG_DSCP_TRUST_ENA, + lan966x, ANA_DSCP_CFG(i)); +} + +static int lan966x_port_qos_default_set(struct lan966x_port *port, + struct lan966x_port_qos *qos) +{ + /* Set default prio and dp level */ + lan_rmw(ANA_QOS_CFG_DP_DEFAULT_VAL_SET(0) | + ANA_QOS_CFG_QOS_DEFAULT_VAL_SET(qos->default_prio), + ANA_QOS_CFG_DP_DEFAULT_VAL | + ANA_QOS_CFG_QOS_DEFAULT_VAL, + port->lan966x, ANA_QOS_CFG(port->chip_port)); + + /* Set default pcp and dei for untagged frames */ + lan_rmw(ANA_VLAN_CFG_VLAN_DEI_SET(0) | + ANA_VLAN_CFG_VLAN_PCP_SET(0), + ANA_VLAN_CFG_VLAN_DEI | + ANA_VLAN_CFG_VLAN_PCP, + port->lan966x, ANA_VLAN_CFG(port->chip_port)); + + return 0; +} + +static void lan966x_port_qos_pcp_rewr_set(struct lan966x_port *port, + struct lan966x_port_qos_pcp_rewr *qos) +{ + u8 mode = LAN966X_PORT_REW_TAG_CTRL_CLASSIFIED; + u8 pcp, dei; + + if (qos->enable) + mode = LAN966X_PORT_REW_TAG_CTRL_MAPPED; + + /* Map the values only if it is enabled otherwise will be the classified + * value + */ + lan_rmw(REW_TAG_CFG_TAG_PCP_CFG_SET(mode) | + REW_TAG_CFG_TAG_DEI_CFG_SET(mode), + REW_TAG_CFG_TAG_PCP_CFG | + REW_TAG_CFG_TAG_DEI_CFG, + port->lan966x, REW_TAG_CFG(port->chip_port)); + + /* Map each value to pcp and dei */ + for (int i = 0; i < ARRAY_SIZE(qos->map); i++) { + pcp = qos->map[i]; + if (pcp > LAN966X_PORT_QOS_PCP_COUNT) + dei = 1; + else + dei = 0; + + lan_rmw(REW_PCP_DEI_CFG_DEI_QOS_VAL_SET(dei) | + REW_PCP_DEI_CFG_PCP_QOS_VAL_SET(pcp), + REW_PCP_DEI_CFG_DEI_QOS_VAL | + REW_PCP_DEI_CFG_PCP_QOS_VAL, + port->lan966x, + REW_PCP_DEI_CFG(port->chip_port, + i + dei * LAN966X_PORT_QOS_PCP_COUNT)); + } +} + +static void lan966x_port_qos_dscp_rewr_set(struct lan966x_port *port, + struct lan966x_port_qos_dscp_rewr *qos) +{ + u16 dscp; + u8 mode; + + if (qos->enable) + mode = LAN966X_PORT_REW_DSCP_ANALIZER; + else + mode = LAN966X_PORT_REW_DSCP_FRAME; + + /* Enable the rewrite otherwise will use the values from the frame */ + lan_rmw(REW_DSCP_CFG_DSCP_REWR_CFG_SET(mode), + REW_DSCP_CFG_DSCP_REWR_CFG, + port->lan966x, REW_DSCP_CFG(port->chip_port)); + + /* Map each classified Qos class and DP to classified DSCP value */ + for (int i = 0; i < ARRAY_SIZE(qos->map); i++) { + dscp = qos->map[i]; + + lan_rmw(ANA_DSCP_REWR_CFG_DSCP_QOS_REWR_VAL_SET(dscp), + ANA_DSCP_REWR_CFG_DSCP_QOS_REWR_VAL, + port->lan966x, ANA_DSCP_REWR_CFG(i)); + } +} + +void lan966x_port_qos_dscp_rewr_mode_set(struct lan966x_port *port, + int mode) +{ + lan_rmw(ANA_QOS_CFG_DSCP_REWR_CFG_SET(mode), + ANA_QOS_CFG_DSCP_REWR_CFG, + port->lan966x, ANA_QOS_CFG(port->chip_port)); +} + +void lan966x_port_qos_set(struct lan966x_port *port, + struct lan966x_port_qos *qos) +{ + lan966x_port_qos_pcp_set(port, &qos->pcp); + lan966x_port_qos_dscp_set(port, &qos->dscp); + lan966x_port_qos_default_set(port, qos); + lan966x_port_qos_pcp_rewr_set(port, &qos->pcp_rewr); + lan966x_port_qos_dscp_rewr_set(port, &qos->dscp_rewr); +} + void lan966x_port_init(struct lan966x_port *port) { struct lan966x_port_config *config = &port->config; diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_regs.h b/drivers/net/ethernet/microchip/lan966x/lan966x_regs.h index f99f88b5caa8..4b553927d2e0 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_regs.h +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_regs.h @@ -283,6 +283,18 @@ enum lan966x_target { #define ANA_VLAN_CFG_VLAN_POP_CNT_GET(x)\ FIELD_GET(ANA_VLAN_CFG_VLAN_POP_CNT, x) +#define ANA_VLAN_CFG_VLAN_PCP GENMASK(15, 13) +#define ANA_VLAN_CFG_VLAN_PCP_SET(x)\ + FIELD_PREP(ANA_VLAN_CFG_VLAN_PCP, x) +#define ANA_VLAN_CFG_VLAN_PCP_GET(x)\ + FIELD_GET(ANA_VLAN_CFG_VLAN_PCP, x) + +#define ANA_VLAN_CFG_VLAN_DEI BIT(12) +#define ANA_VLAN_CFG_VLAN_DEI_SET(x)\ + FIELD_PREP(ANA_VLAN_CFG_VLAN_DEI, x) +#define ANA_VLAN_CFG_VLAN_DEI_GET(x)\ + FIELD_GET(ANA_VLAN_CFG_VLAN_DEI, x) + #define ANA_VLAN_CFG_VLAN_VID GENMASK(11, 0) #define ANA_VLAN_CFG_VLAN_VID_SET(x)\ FIELD_PREP(ANA_VLAN_CFG_VLAN_VID, x) @@ -316,6 +328,39 @@ enum lan966x_target { #define ANA_DROP_CFG_DROP_MC_SMAC_ENA_GET(x)\ FIELD_GET(ANA_DROP_CFG_DROP_MC_SMAC_ENA, x) +/* ANA:PORT:QOS_CFG */ +#define ANA_QOS_CFG(g) __REG(TARGET_ANA, 0, 1, 28672, g, 9, 128, 8, 0, 1, 4) + +#define ANA_QOS_CFG_DP_DEFAULT_VAL BIT(8) +#define ANA_QOS_CFG_DP_DEFAULT_VAL_SET(x)\ + FIELD_PREP(ANA_QOS_CFG_DP_DEFAULT_VAL, x) +#define ANA_QOS_CFG_DP_DEFAULT_VAL_GET(x)\ + FIELD_GET(ANA_QOS_CFG_DP_DEFAULT_VAL, x) + +#define ANA_QOS_CFG_QOS_DEFAULT_VAL GENMASK(7, 5) +#define ANA_QOS_CFG_QOS_DEFAULT_VAL_SET(x)\ + FIELD_PREP(ANA_QOS_CFG_QOS_DEFAULT_VAL, x) +#define ANA_QOS_CFG_QOS_DEFAULT_VAL_GET(x)\ + FIELD_GET(ANA_QOS_CFG_QOS_DEFAULT_VAL, x) + +#define ANA_QOS_CFG_QOS_DSCP_ENA BIT(4) +#define ANA_QOS_CFG_QOS_DSCP_ENA_SET(x)\ + FIELD_PREP(ANA_QOS_CFG_QOS_DSCP_ENA, x) +#define ANA_QOS_CFG_QOS_DSCP_ENA_GET(x)\ + FIELD_GET(ANA_QOS_CFG_QOS_DSCP_ENA, x) + +#define ANA_QOS_CFG_QOS_PCP_ENA BIT(3) +#define ANA_QOS_CFG_QOS_PCP_ENA_SET(x)\ + FIELD_PREP(ANA_QOS_CFG_QOS_PCP_ENA, x) +#define ANA_QOS_CFG_QOS_PCP_ENA_GET(x)\ + FIELD_GET(ANA_QOS_CFG_QOS_PCP_ENA, x) + +#define ANA_QOS_CFG_DSCP_REWR_CFG GENMASK(1, 0) +#define ANA_QOS_CFG_DSCP_REWR_CFG_SET(x)\ + FIELD_PREP(ANA_QOS_CFG_DSCP_REWR_CFG, x) +#define ANA_QOS_CFG_DSCP_REWR_CFG_GET(x)\ + FIELD_GET(ANA_QOS_CFG_DSCP_REWR_CFG, x) + /* ANA:PORT:VCAP_CFG */ #define ANA_VCAP_CFG(g) __REG(TARGET_ANA, 0, 1, 28672, g, 9, 128, 12, 0, 1, 4) @@ -415,6 +460,21 @@ enum lan966x_target { #define ANA_VCAP_S2_CFG_OAM_DIS_GET(x)\ FIELD_GET(ANA_VCAP_S2_CFG_OAM_DIS, x) +/* ANA:PORT:QOS_PCP_DEI_MAP_CFG */ +#define ANA_PCP_DEI_CFG(g, r) __REG(TARGET_ANA, 0, 1, 28672, g, 9, 128, 32, r, 16, 4) + +#define ANA_PCP_DEI_CFG_DP_PCP_DEI_VAL BIT(3) +#define ANA_PCP_DEI_CFG_DP_PCP_DEI_VAL_SET(x)\ + FIELD_PREP(ANA_PCP_DEI_CFG_DP_PCP_DEI_VAL, x) +#define ANA_PCP_DEI_CFG_DP_PCP_DEI_VAL_GET(x)\ + FIELD_GET(ANA_PCP_DEI_CFG_DP_PCP_DEI_VAL, x) + +#define ANA_PCP_DEI_CFG_QOS_PCP_DEI_VAL GENMASK(2, 0) +#define ANA_PCP_DEI_CFG_QOS_PCP_DEI_VAL_SET(x)\ + FIELD_PREP(ANA_PCP_DEI_CFG_QOS_PCP_DEI_VAL, x) +#define ANA_PCP_DEI_CFG_QOS_PCP_DEI_VAL_GET(x)\ + FIELD_GET(ANA_PCP_DEI_CFG_QOS_PCP_DEI_VAL, x) + /* ANA:PORT:CPU_FWD_CFG */ #define ANA_CPU_FWD_CFG(g) __REG(TARGET_ANA, 0, 1, 28672, g, 9, 128, 96, 0, 1, 4) @@ -478,6 +538,15 @@ enum lan966x_target { #define ANA_PORT_CFG_PORTID_VAL_GET(x)\ FIELD_GET(ANA_PORT_CFG_PORTID_VAL, x) +/* ANA:COMMON:DSCP_REWR_CFG */ +#define ANA_DSCP_REWR_CFG(r) __REG(TARGET_ANA, 0, 1, 31232, 0, 1, 552, 332, r, 16, 4) + +#define ANA_DSCP_REWR_CFG_DSCP_QOS_REWR_VAL GENMASK(5, 0) +#define ANA_DSCP_REWR_CFG_DSCP_QOS_REWR_VAL_SET(x)\ + FIELD_PREP(ANA_DSCP_REWR_CFG_DSCP_QOS_REWR_VAL, x) +#define ANA_DSCP_REWR_CFG_DSCP_QOS_REWR_VAL_GET(x)\ + FIELD_GET(ANA_DSCP_REWR_CFG_DSCP_QOS_REWR_VAL, x) + /* ANA:PORT:POL_CFG */ #define ANA_POL_CFG(g) __REG(TARGET_ANA, 0, 1, 28672, g, 9, 128, 116, 0, 1, 4) @@ -547,6 +616,33 @@ enum lan966x_target { #define ANA_AGGR_CFG_AC_IP4_TCPUDP_ENA_GET(x)\ FIELD_GET(ANA_AGGR_CFG_AC_IP4_TCPUDP_ENA, x) +/* ANA:COMMON:DSCP_CFG */ +#define ANA_DSCP_CFG(r) __REG(TARGET_ANA, 0, 1, 31232, 0, 1, 552, 76, r, 64, 4) + +#define ANA_DSCP_CFG_DP_DSCP_VAL BIT(11) +#define ANA_DSCP_CFG_DP_DSCP_VAL_SET(x)\ + FIELD_PREP(ANA_DSCP_CFG_DP_DSCP_VAL, x) +#define ANA_DSCP_CFG_DP_DSCP_VAL_GET(x)\ + FIELD_GET(ANA_DSCP_CFG_DP_DSCP_VAL, x) + +#define ANA_DSCP_CFG_QOS_DSCP_VAL GENMASK(10, 8) +#define ANA_DSCP_CFG_QOS_DSCP_VAL_SET(x)\ + FIELD_PREP(ANA_DSCP_CFG_QOS_DSCP_VAL, x) +#define ANA_DSCP_CFG_QOS_DSCP_VAL_GET(x)\ + FIELD_GET(ANA_DSCP_CFG_QOS_DSCP_VAL, x) + +#define ANA_DSCP_CFG_DSCP_TRUST_ENA BIT(1) +#define ANA_DSCP_CFG_DSCP_TRUST_ENA_SET(x)\ + FIELD_PREP(ANA_DSCP_CFG_DSCP_TRUST_ENA, x) +#define ANA_DSCP_CFG_DSCP_TRUST_ENA_GET(x)\ + FIELD_GET(ANA_DSCP_CFG_DSCP_TRUST_ENA, x) + +#define ANA_DSCP_CFG_DSCP_REWR_ENA BIT(0) +#define ANA_DSCP_CFG_DSCP_REWR_ENA_SET(x)\ + FIELD_PREP(ANA_DSCP_CFG_DSCP_REWR_ENA, x) +#define ANA_DSCP_CFG_DSCP_REWR_ENA_GET(x)\ + FIELD_GET(ANA_DSCP_CFG_DSCP_REWR_ENA, x) + /* ANA:POL:POL_PIR_CFG */ #define ANA_POL_PIR_CFG(g) __REG(TARGET_ANA, 0, 1, 16384, g, 345, 32, 0, 0, 1, 4) @@ -1468,15 +1564,66 @@ enum lan966x_target { #define REW_TAG_CFG_TAG_TPID_CFG_GET(x)\ FIELD_GET(REW_TAG_CFG_TAG_TPID_CFG, x) +#define REW_TAG_CFG_TAG_PCP_CFG GENMASK(3, 2) +#define REW_TAG_CFG_TAG_PCP_CFG_SET(x)\ + FIELD_PREP(REW_TAG_CFG_TAG_PCP_CFG, x) +#define REW_TAG_CFG_TAG_PCP_CFG_GET(x)\ + FIELD_GET(REW_TAG_CFG_TAG_PCP_CFG, x) + +#define REW_TAG_CFG_TAG_DEI_CFG GENMASK(1, 0) +#define REW_TAG_CFG_TAG_DEI_CFG_SET(x)\ + FIELD_PREP(REW_TAG_CFG_TAG_DEI_CFG, x) +#define REW_TAG_CFG_TAG_DEI_CFG_GET(x)\ + FIELD_GET(REW_TAG_CFG_TAG_DEI_CFG, x) + /* REW:PORT:PORT_CFG */ #define REW_PORT_CFG(g) __REG(TARGET_REW, 0, 1, 0, g, 10, 128, 8, 0, 1, 4) +#define REW_PORT_CFG_ES0_EN BIT(4) +#define REW_PORT_CFG_ES0_EN_SET(x)\ + FIELD_PREP(REW_PORT_CFG_ES0_EN, x) +#define REW_PORT_CFG_ES0_EN_GET(x)\ + FIELD_GET(REW_PORT_CFG_ES0_EN, x) + #define REW_PORT_CFG_NO_REWRITE BIT(0) #define REW_PORT_CFG_NO_REWRITE_SET(x)\ FIELD_PREP(REW_PORT_CFG_NO_REWRITE, x) #define REW_PORT_CFG_NO_REWRITE_GET(x)\ FIELD_GET(REW_PORT_CFG_NO_REWRITE, x) +/* REW:PORT:DSCP_CFG */ +#define REW_DSCP_CFG(g) __REG(TARGET_REW, 0, 1, 0, g, 10, 128, 12, 0, 1, 4) + +#define REW_DSCP_CFG_DSCP_REWR_CFG GENMASK(1, 0) +#define REW_DSCP_CFG_DSCP_REWR_CFG_SET(x)\ + FIELD_PREP(REW_DSCP_CFG_DSCP_REWR_CFG, x) +#define REW_DSCP_CFG_DSCP_REWR_CFG_GET(x)\ + FIELD_GET(REW_DSCP_CFG_DSCP_REWR_CFG, x) + +/* REW:PORT:PCP_DEI_QOS_MAP_CFG */ +#define REW_PCP_DEI_CFG(g, r) __REG(TARGET_REW, 0, 1, 0, g, 10, 128, 16, r, 16, 4) + +#define REW_PCP_DEI_CFG_DEI_QOS_VAL BIT(3) +#define REW_PCP_DEI_CFG_DEI_QOS_VAL_SET(x)\ + FIELD_PREP(REW_PCP_DEI_CFG_DEI_QOS_VAL, x) +#define REW_PCP_DEI_CFG_DEI_QOS_VAL_GET(x)\ + FIELD_GET(REW_PCP_DEI_CFG_DEI_QOS_VAL, x) + +#define REW_PCP_DEI_CFG_PCP_QOS_VAL GENMASK(2, 0) +#define REW_PCP_DEI_CFG_PCP_QOS_VAL_SET(x)\ + FIELD_PREP(REW_PCP_DEI_CFG_PCP_QOS_VAL, x) +#define REW_PCP_DEI_CFG_PCP_QOS_VAL_GET(x)\ + FIELD_GET(REW_PCP_DEI_CFG_PCP_QOS_VAL, x) + +/* REW:COMMON:STAT_CFG */ +#define REW_STAT_CFG __REG(TARGET_REW, 0, 1, 3072, 0, 1, 528, 520, 0, 1, 4) + +#define REW_STAT_CFG_STAT_MODE GENMASK(1, 0) +#define REW_STAT_CFG_STAT_MODE_SET(x)\ + FIELD_PREP(REW_STAT_CFG_STAT_MODE, x) +#define REW_STAT_CFG_STAT_MODE_GET(x)\ + FIELD_GET(REW_STAT_CFG_STAT_MODE, x) + /* SYS:SYSTEM:RESET_CFG */ #define SYS_RESET_CFG __REG(TARGET_SYS, 0, 1, 4128, 0, 1, 168, 0, 0, 1, 4) diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_tc_flower.c b/drivers/net/ethernet/microchip/lan966x/lan966x_tc_flower.c index 47b2f7579dd2..96b3def6c474 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_tc_flower.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_tc_flower.c @@ -5,6 +5,8 @@ #include "vcap_api_client.h" #include "vcap_tc.h" +#define LAN966X_FORCE_UNTAGED 3 + static bool lan966x_tc_is_known_etype(struct vcap_tc_flower_parse_usage *st, u16 etype) { @@ -29,6 +31,8 @@ static bool lan966x_tc_is_known_etype(struct vcap_tc_flower_parse_usage *st, return true; } break; + case VCAP_TYPE_ES0: + return true; default: NL_SET_ERR_MSG_MOD(st->fco->common.extack, "VCAP type not supported"); @@ -318,6 +322,9 @@ static int lan966x_tc_set_actionset(struct vcap_admin *admin, case VCAP_TYPE_IS2: aset = VCAP_AFS_BASE_TYPE; break; + case VCAP_TYPE_ES0: + aset = VCAP_AFS_VID; + break; default: return -EINVAL; } @@ -353,6 +360,10 @@ static int lan966x_tc_add_rule_link_target(struct vcap_admin *admin, /* Add IS2 specific PAG key (for chaining rules from IS1) */ return vcap_rule_add_key_u32(vrule, VCAP_KF_LOOKUP_PAG, link_val, ~0); + case VCAP_TYPE_ES0: + /* Add ES0 specific ISDX key (for chaining rules from IS1) */ + return vcap_rule_add_key_u32(vrule, VCAP_KF_ISDX_CLS, + link_val, ~0); default: break; } @@ -389,6 +400,18 @@ static int lan966x_tc_add_rule_link(struct vcap_control *vctrl, 0xff); if (err) return err; + } else if (admin->vtype == VCAP_TYPE_IS1 && + to_admin->vtype == VCAP_TYPE_ES0) { + /* This works for IS1->ES0 */ + err = vcap_rule_add_action_u32(vrule, VCAP_AF_ISDX_ADD_VAL, + diff); + if (err) + return err; + + err = vcap_rule_add_action_bit(vrule, VCAP_AF_ISDX_REPLACE_ENA, + VCAP_BIT_1); + if (err) + return err; } else { NL_SET_ERR_MSG_MOD(f->common.extack, "Unsupported chain destination"); @@ -398,6 +421,23 @@ static int lan966x_tc_add_rule_link(struct vcap_control *vctrl, return err; } +static int lan966x_tc_add_rule_counter(struct vcap_admin *admin, + struct vcap_rule *vrule) +{ + int err = 0; + + switch (admin->vtype) { + case VCAP_TYPE_ES0: + err = vcap_rule_mod_action_u32(vrule, VCAP_AF_ESDX, + vrule->id); + break; + default: + break; + } + + return err; +} + static int lan966x_tc_flower_add(struct lan966x_port *port, struct flow_cls_offload *f, struct vcap_admin *admin, @@ -466,6 +506,21 @@ static int lan966x_tc_flower_add(struct lan966x_port *port, goto out; break; + case FLOW_ACTION_VLAN_POP: + if (admin->vtype != VCAP_TYPE_ES0) { + NL_SET_ERR_MSG_MOD(f->common.extack, + "Cannot use vlan pop on non es0"); + err = -EOPNOTSUPP; + goto out; + } + + /* Force untag */ + err = vcap_rule_add_action_u32(vrule, VCAP_AF_PUSH_OUTER_TAG, + LAN966X_FORCE_UNTAGED); + if (err) + goto out; + + break; default: NL_SET_ERR_MSG_MOD(f->common.extack, "Unsupported TC action"); @@ -474,6 +529,12 @@ static int lan966x_tc_flower_add(struct lan966x_port *port, } } + err = lan966x_tc_add_rule_counter(admin, vrule); + if (err) { + vcap_set_tc_exterr(f, vrule); + goto out; + } + err = vcap_val_rule(vrule, l3_proto); if (err) { vcap_set_tc_exterr(f, vrule); diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_ag_api.c b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_ag_api.c index 66400a082d02..fb6851b94528 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_ag_api.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_ag_api.c @@ -2121,6 +2121,69 @@ static const struct vcap_field is2_smac_sip6_keyfield[] = { }, }; +static const struct vcap_field es0_vid_keyfield[] = { + [VCAP_KF_IF_EGR_PORT_NO] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_IF_IGR_PORT] = { + .type = VCAP_FIELD_U32, + .offset = 4, + .width = 4, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 8, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 9, + .width = 8, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 17, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 18, + .width = 1, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 19, + .width = 12, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 31, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 32, + .width = 3, + }, + [VCAP_KF_L3_DPL_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 35, + .width = 1, + }, + [VCAP_KF_RTP_ID] = { + .type = VCAP_FIELD_U32, + .offset = 36, + .width = 10, + }, + [VCAP_KF_PDU_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 46, + .width = 4, + }, +}; + /* keyfield_set */ static const struct vcap_set is1_keyfield_set[] = { [VCAP_KFS_NORMAL] = { @@ -2228,6 +2291,14 @@ static const struct vcap_set is2_keyfield_set[] = { }, }; +static const struct vcap_set es0_keyfield_set[] = { + [VCAP_KFS_VID] = { + .type_id = -1, + .sw_per_item = 1, + .sw_cnt = 1, + }, +}; + /* keyfield_set map */ static const struct vcap_field *is1_keyfield_set_map[] = { [VCAP_KFS_NORMAL] = is1_normal_keyfield, @@ -2255,6 +2326,10 @@ static const struct vcap_field *is2_keyfield_set_map[] = { [VCAP_KFS_SMAC_SIP6] = is2_smac_sip6_keyfield, }; +static const struct vcap_field *es0_keyfield_set_map[] = { + [VCAP_KFS_VID] = es0_vid_keyfield, +}; + /* keyfield_set map sizes */ static int is1_keyfield_set_map_size[] = { [VCAP_KFS_NORMAL] = ARRAY_SIZE(is1_normal_keyfield), @@ -2282,6 +2357,10 @@ static int is2_keyfield_set_map_size[] = { [VCAP_KFS_SMAC_SIP6] = ARRAY_SIZE(is2_smac_sip6_keyfield), }; +static int es0_keyfield_set_map_size[] = { + [VCAP_KFS_VID] = ARRAY_SIZE(es0_vid_keyfield), +}; + /* actionfields */ static const struct vcap_field is1_s1_actionfield[] = { [VCAP_AF_TYPE] = { @@ -2522,6 +2601,94 @@ static const struct vcap_field is2_smac_sip_actionfield[] = { }, }; +static const struct vcap_field es0_vid_actionfield[] = { + [VCAP_AF_PUSH_OUTER_TAG] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 2, + }, + [VCAP_AF_PUSH_INNER_TAG] = { + .type = VCAP_FIELD_BIT, + .offset = 2, + .width = 1, + }, + [VCAP_AF_TAG_A_TPID_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 3, + .width = 2, + }, + [VCAP_AF_TAG_A_VID_SEL] = { + .type = VCAP_FIELD_BIT, + .offset = 5, + .width = 1, + }, + [VCAP_AF_TAG_A_PCP_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 6, + .width = 2, + }, + [VCAP_AF_TAG_A_DEI_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 8, + .width = 2, + }, + [VCAP_AF_TAG_B_TPID_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 10, + .width = 2, + }, + [VCAP_AF_TAG_B_VID_SEL] = { + .type = VCAP_FIELD_BIT, + .offset = 12, + .width = 1, + }, + [VCAP_AF_TAG_B_PCP_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 13, + .width = 2, + }, + [VCAP_AF_TAG_B_DEI_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 15, + .width = 2, + }, + [VCAP_AF_VID_A_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 17, + .width = 12, + }, + [VCAP_AF_PCP_A_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 29, + .width = 3, + }, + [VCAP_AF_DEI_A_VAL] = { + .type = VCAP_FIELD_BIT, + .offset = 32, + .width = 1, + }, + [VCAP_AF_VID_B_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 33, + .width = 12, + }, + [VCAP_AF_PCP_B_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 45, + .width = 3, + }, + [VCAP_AF_DEI_B_VAL] = { + .type = VCAP_FIELD_BIT, + .offset = 48, + .width = 1, + }, + [VCAP_AF_ESDX] = { + .type = VCAP_FIELD_U32, + .offset = 49, + .width = 8, + }, +}; + /* actionfield_set */ static const struct vcap_set is1_actionfield_set[] = { [VCAP_AFS_S1] = { @@ -2544,6 +2711,14 @@ static const struct vcap_set is2_actionfield_set[] = { }, }; +static const struct vcap_set es0_actionfield_set[] = { + [VCAP_AFS_VID] = { + .type_id = -1, + .sw_per_item = 1, + .sw_cnt = 1, + }, +}; + /* actionfield_set map */ static const struct vcap_field *is1_actionfield_set_map[] = { [VCAP_AFS_S1] = is1_s1_actionfield, @@ -2554,6 +2729,10 @@ static const struct vcap_field *is2_actionfield_set_map[] = { [VCAP_AFS_SMAC_SIP] = is2_smac_sip_actionfield, }; +static const struct vcap_field *es0_actionfield_set_map[] = { + [VCAP_AFS_VID] = es0_vid_actionfield, +}; + /* actionfield_set map size */ static int is1_actionfield_set_map_size[] = { [VCAP_AFS_S1] = ARRAY_SIZE(is1_s1_actionfield), @@ -2564,6 +2743,10 @@ static int is2_actionfield_set_map_size[] = { [VCAP_AFS_SMAC_SIP] = ARRAY_SIZE(is2_smac_sip_actionfield), }; +static int es0_actionfield_set_map_size[] = { + [VCAP_AFS_VID] = ARRAY_SIZE(es0_vid_actionfield), +}; + /* Type Groups */ static const struct vcap_typegroup is1_x4_keyfield_set_typegroups[] = { { @@ -2659,6 +2842,10 @@ static const struct vcap_typegroup is2_x1_keyfield_set_typegroups[] = { {} }; +static const struct vcap_typegroup es0_x1_keyfield_set_typegroups[] = { + {} +}; + static const struct vcap_typegroup *is1_keyfield_set_typegroups[] = { [4] = is1_x4_keyfield_set_typegroups, [2] = is1_x2_keyfield_set_typegroups, @@ -2673,6 +2860,11 @@ static const struct vcap_typegroup *is2_keyfield_set_typegroups[] = { [5] = NULL, }; +static const struct vcap_typegroup *es0_keyfield_set_typegroups[] = { + [1] = es0_x1_keyfield_set_typegroups, + [2] = NULL, +}; + static const struct vcap_typegroup is1_x1_actionfield_set_typegroups[] = { {} }; @@ -2700,6 +2892,10 @@ static const struct vcap_typegroup is2_x1_actionfield_set_typegroups[] = { {} }; +static const struct vcap_typegroup es0_x1_actionfield_set_typegroups[] = { + {} +}; + static const struct vcap_typegroup *is1_actionfield_set_typegroups[] = { [1] = is1_x1_actionfield_set_typegroups, [5] = NULL, @@ -2711,6 +2907,11 @@ static const struct vcap_typegroup *is2_actionfield_set_typegroups[] = { [5] = NULL, }; +static const struct vcap_typegroup *es0_actionfield_set_typegroups[] = { + [1] = es0_x1_actionfield_set_typegroups, + [2] = NULL, +}; + /* Keyfieldset names */ static const char * const vcap_keyfield_set_names[] = { [VCAP_KFS_NO_VALUE] = "(None)", @@ -2743,6 +2944,7 @@ static const char * const vcap_keyfield_set_names[] = { [VCAP_KFS_RT] = "VCAP_KFS_RT", [VCAP_KFS_SMAC_SIP4] = "VCAP_KFS_SMAC_SIP4", [VCAP_KFS_SMAC_SIP6] = "VCAP_KFS_SMAC_SIP6", + [VCAP_KFS_VID] = "VCAP_KFS_VID", }; /* Actionfieldset names */ @@ -2751,9 +2953,11 @@ static const char * const vcap_actionfield_set_names[] = { [VCAP_AFS_BASE_TYPE] = "VCAP_AFS_BASE_TYPE", [VCAP_AFS_CLASSIFICATION] = "VCAP_AFS_CLASSIFICATION", [VCAP_AFS_CLASS_REDUCED] = "VCAP_AFS_CLASS_REDUCED", + [VCAP_AFS_ES0] = "VCAP_AFS_ES0", [VCAP_AFS_FULL] = "VCAP_AFS_FULL", [VCAP_AFS_S1] = "VCAP_AFS_S1", [VCAP_AFS_SMAC_SIP] = "VCAP_AFS_SMAC_SIP", + [VCAP_AFS_VID] = "VCAP_AFS_VID", }; /* Keyfield names */ @@ -2774,6 +2978,7 @@ static const char * const vcap_keyfield_names[] = { [VCAP_KF_8021Q_PCP1] = "8021Q_PCP1", [VCAP_KF_8021Q_PCP2] = "8021Q_PCP2", [VCAP_KF_8021Q_PCP_CLS] = "8021Q_PCP_CLS", + [VCAP_KF_8021Q_TPID] = "8021Q_TPID", [VCAP_KF_8021Q_TPID0] = "8021Q_TPID0", [VCAP_KF_8021Q_TPID1] = "8021Q_TPID1", [VCAP_KF_8021Q_TPID2] = "8021Q_TPID2", @@ -2799,6 +3004,7 @@ static const char * const vcap_keyfield_names[] = { [VCAP_KF_HOST_MATCH] = "HOST_MATCH", [VCAP_KF_IF_EGR_PORT_MASK] = "IF_EGR_PORT_MASK", [VCAP_KF_IF_EGR_PORT_MASK_RNG] = "IF_EGR_PORT_MASK_RNG", + [VCAP_KF_IF_EGR_PORT_NO] = "IF_EGR_PORT_NO", [VCAP_KF_IF_IGR_PORT] = "IF_IGR_PORT", [VCAP_KF_IF_IGR_PORT_MASK] = "IF_IGR_PORT_MASK", [VCAP_KF_IF_IGR_PORT_MASK_L3] = "IF_IGR_PORT_MASK_L3", @@ -2873,7 +3079,9 @@ static const char * const vcap_keyfield_names[] = { [VCAP_KF_OAM_OPCODE] = "OAM_OPCODE", [VCAP_KF_OAM_VER] = "OAM_VER", [VCAP_KF_OAM_Y1731_IS] = "OAM_Y1731_IS", + [VCAP_KF_PDU_TYPE] = "PDU_TYPE", [VCAP_KF_PROT_ACTIVE] = "PROT_ACTIVE", + [VCAP_KF_RTP_ID] = "RTP_ID", [VCAP_KF_RT_FRMID] = "RT_FRMID", [VCAP_KF_RT_TYPE] = "RT_TYPE", [VCAP_KF_RT_VLAN_IDX] = "RT_VLAN_IDX", @@ -2891,18 +3099,25 @@ static const char * const vcap_actionfield_names[] = { [VCAP_AF_COPY_PORT_NUM] = "COPY_PORT_NUM", [VCAP_AF_COPY_QUEUE_NUM] = "COPY_QUEUE_NUM", [VCAP_AF_CPU_COPY_ENA] = "CPU_COPY_ENA", + [VCAP_AF_CPU_QU] = "CPU_QU", [VCAP_AF_CPU_QUEUE_NUM] = "CPU_QUEUE_NUM", [VCAP_AF_CUSTOM_ACE_TYPE_ENA] = "CUSTOM_ACE_TYPE_ENA", + [VCAP_AF_DEI_A_VAL] = "DEI_A_VAL", + [VCAP_AF_DEI_B_VAL] = "DEI_B_VAL", + [VCAP_AF_DEI_C_VAL] = "DEI_C_VAL", [VCAP_AF_DEI_ENA] = "DEI_ENA", [VCAP_AF_DEI_VAL] = "DEI_VAL", [VCAP_AF_DLR_SEL] = "DLR_SEL", [VCAP_AF_DP_ENA] = "DP_ENA", [VCAP_AF_DP_VAL] = "DP_VAL", [VCAP_AF_DSCP_ENA] = "DSCP_ENA", + [VCAP_AF_DSCP_SEL] = "DSCP_SEL", [VCAP_AF_DSCP_VAL] = "DSCP_VAL", [VCAP_AF_ES2_REW_CMD] = "ES2_REW_CMD", + [VCAP_AF_ESDX] = "ESDX", [VCAP_AF_FWD_KILL_ENA] = "FWD_KILL_ENA", [VCAP_AF_FWD_MODE] = "FWD_MODE", + [VCAP_AF_FWD_SEL] = "FWD_SEL", [VCAP_AF_HIT_ME_ONCE] = "HIT_ME_ONCE", [VCAP_AF_HOST_MATCH] = "HOST_MATCH", [VCAP_AF_IGNORE_PIPELINE_CTRL] = "IGNORE_PIPELINE_CTRL", @@ -2912,6 +3127,7 @@ static const char * const vcap_actionfield_names[] = { [VCAP_AF_ISDX_ENA] = "ISDX_ENA", [VCAP_AF_ISDX_REPLACE_ENA] = "ISDX_REPLACE_ENA", [VCAP_AF_ISDX_VAL] = "ISDX_VAL", + [VCAP_AF_LOOP_ENA] = "LOOP_ENA", [VCAP_AF_LRN_DIS] = "LRN_DIS", [VCAP_AF_MAP_IDX] = "MAP_IDX", [VCAP_AF_MAP_KEY] = "MAP_KEY", @@ -2928,15 +3144,23 @@ static const char * const vcap_actionfield_names[] = { [VCAP_AF_OAM_SEL] = "OAM_SEL", [VCAP_AF_PAG_OVERRIDE_MASK] = "PAG_OVERRIDE_MASK", [VCAP_AF_PAG_VAL] = "PAG_VAL", + [VCAP_AF_PCP_A_VAL] = "PCP_A_VAL", + [VCAP_AF_PCP_B_VAL] = "PCP_B_VAL", + [VCAP_AF_PCP_C_VAL] = "PCP_C_VAL", [VCAP_AF_PCP_ENA] = "PCP_ENA", [VCAP_AF_PCP_VAL] = "PCP_VAL", + [VCAP_AF_PIPELINE_ACT] = "PIPELINE_ACT", [VCAP_AF_PIPELINE_FORCE_ENA] = "PIPELINE_FORCE_ENA", [VCAP_AF_PIPELINE_PT] = "PIPELINE_PT", [VCAP_AF_POLICE_ENA] = "POLICE_ENA", [VCAP_AF_POLICE_IDX] = "POLICE_IDX", [VCAP_AF_POLICE_REMARK] = "POLICE_REMARK", [VCAP_AF_POLICE_VCAP_ONLY] = "POLICE_VCAP_ONLY", + [VCAP_AF_POP_VAL] = "POP_VAL", [VCAP_AF_PORT_MASK] = "PORT_MASK", + [VCAP_AF_PUSH_CUSTOMER_TAG] = "PUSH_CUSTOMER_TAG", + [VCAP_AF_PUSH_INNER_TAG] = "PUSH_INNER_TAG", + [VCAP_AF_PUSH_OUTER_TAG] = "PUSH_OUTER_TAG", [VCAP_AF_QOS_ENA] = "QOS_ENA", [VCAP_AF_QOS_VAL] = "QOS_VAL", [VCAP_AF_REW_OP] = "REW_OP", @@ -2945,7 +3169,24 @@ static const char * const vcap_actionfield_names[] = { [VCAP_AF_SFID_VAL] = "SFID_VAL", [VCAP_AF_SGID_ENA] = "SGID_ENA", [VCAP_AF_SGID_VAL] = "SGID_VAL", + [VCAP_AF_SWAP_MACS_ENA] = "SWAP_MACS_ENA", + [VCAP_AF_TAG_A_DEI_SEL] = "TAG_A_DEI_SEL", + [VCAP_AF_TAG_A_PCP_SEL] = "TAG_A_PCP_SEL", + [VCAP_AF_TAG_A_TPID_SEL] = "TAG_A_TPID_SEL", + [VCAP_AF_TAG_A_VID_SEL] = "TAG_A_VID_SEL", + [VCAP_AF_TAG_B_DEI_SEL] = "TAG_B_DEI_SEL", + [VCAP_AF_TAG_B_PCP_SEL] = "TAG_B_PCP_SEL", + [VCAP_AF_TAG_B_TPID_SEL] = "TAG_B_TPID_SEL", + [VCAP_AF_TAG_B_VID_SEL] = "TAG_B_VID_SEL", + [VCAP_AF_TAG_C_DEI_SEL] = "TAG_C_DEI_SEL", + [VCAP_AF_TAG_C_PCP_SEL] = "TAG_C_PCP_SEL", + [VCAP_AF_TAG_C_TPID_SEL] = "TAG_C_TPID_SEL", + [VCAP_AF_TAG_C_VID_SEL] = "TAG_C_VID_SEL", [VCAP_AF_TYPE] = "TYPE", + [VCAP_AF_UNTAG_VID_ENA] = "UNTAG_VID_ENA", + [VCAP_AF_VID_A_VAL] = "VID_A_VAL", + [VCAP_AF_VID_B_VAL] = "VID_B_VAL", + [VCAP_AF_VID_C_VAL] = "VID_C_VAL", [VCAP_AF_VID_REPLACE_ENA] = "VID_REPLACE_ENA", [VCAP_AF_VID_VAL] = "VID_VAL", [VCAP_AF_VLAN_POP_CNT] = "VLAN_POP_CNT", @@ -2996,11 +3237,32 @@ const struct vcap_info lan966x_vcaps[] = { .keyfield_set_typegroups = is2_keyfield_set_typegroups, .actionfield_set_typegroups = is2_actionfield_set_typegroups, }, + [VCAP_TYPE_ES0] = { + .name = "es0", + .rows = 256, + .sw_count = 1, + .sw_width = 96, + .sticky_width = 1, + .act_width = 65, + .default_cnt = 8, + .require_cnt_dis = 0, + .version = 1, + .keyfield_set = es0_keyfield_set, + .keyfield_set_size = ARRAY_SIZE(es0_keyfield_set), + .actionfield_set = es0_actionfield_set, + .actionfield_set_size = ARRAY_SIZE(es0_actionfield_set), + .keyfield_set_map = es0_keyfield_set_map, + .keyfield_set_map_size = es0_keyfield_set_map_size, + .actionfield_set_map = es0_actionfield_set_map, + .actionfield_set_map_size = es0_actionfield_set_map_size, + .keyfield_set_typegroups = es0_keyfield_set_typegroups, + .actionfield_set_typegroups = es0_actionfield_set_typegroups, + }, }; const struct vcap_statistics lan966x_vcap_stats = { .name = "lan966x", - .count = 2, + .count = 3, .keyfield_set_names = vcap_keyfield_set_names, .actionfield_set_names = vcap_actionfield_set_names, .keyfield_names = vcap_keyfield_names, diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_debugfs.c b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_debugfs.c index d90c08cfcf14..ac525ff1503e 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_debugfs.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_debugfs.c @@ -190,6 +190,26 @@ static void lan966x_vcap_is2_port_keys(struct lan966x_port *port, out->prf(out->dst, "\n"); } +static void lan966x_vcap_es0_port_keys(struct lan966x_port *port, + struct vcap_admin *admin, + struct vcap_output_print *out) +{ + struct lan966x *lan966x = port->lan966x; + u32 val; + + out->prf(out->dst, " port[%d] (%s): ", port->chip_port, + netdev_name(port->dev)); + + val = lan_rd(lan966x, REW_PORT_CFG(port->chip_port)); + out->prf(out->dst, "\n state: "); + if (REW_PORT_CFG_ES0_EN_GET(val)) + out->prf(out->dst, "on"); + else + out->prf(out->dst, "off"); + + out->prf(out->dst, "\n"); +} + int lan966x_vcap_port_info(struct net_device *dev, struct vcap_admin *admin, struct vcap_output_print *out) @@ -210,6 +230,9 @@ int lan966x_vcap_port_info(struct net_device *dev, case VCAP_TYPE_IS1: lan966x_vcap_is1_port_keys(port, admin, out); break; + case VCAP_TYPE_ES0: + lan966x_vcap_es0_port_keys(port, admin, out); + break; default: out->prf(out->dst, " no info\n"); break; diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c index 7ea8e8633609..a4414f63c9b1 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c @@ -10,6 +10,12 @@ #define LAN966X_IS1_LOOKUPS 3 #define LAN966X_IS2_LOOKUPS 2 +#define LAN966X_ES0_LOOKUPS 1 + +#define LAN966X_STAT_ESDX_GRN_BYTES 0x300 +#define LAN966X_STAT_ESDX_GRN_PKTS 0x301 +#define LAN966X_STAT_ESDX_YEL_BYTES 0x302 +#define LAN966X_STAT_ESDX_YEL_PKTS 0x303 static struct lan966x_vcap_inst { enum vcap_type vtype; /* type of vcap */ @@ -21,6 +27,14 @@ static struct lan966x_vcap_inst { bool ingress; /* is vcap in the ingress path */ } lan966x_vcap_inst_cfg[] = { { + .vtype = VCAP_TYPE_ES0, + .tgt_inst = 0, + .lookups = LAN966X_ES0_LOOKUPS, + .first_cid = LAN966X_VCAP_CID_ES0_L0, + .last_cid = LAN966X_VCAP_CID_ES0_MAX, + .count = 64, + }, + { .vtype = VCAP_TYPE_IS1, /* IS1-0 */ .tgt_inst = 1, .lookups = LAN966X_IS1_LOOKUPS, @@ -279,6 +293,8 @@ lan966x_vcap_validate_keyset(struct net_device *dev, err = lan966x_vcap_is2_get_port_keysets(dev, lookup, &keysetlist, l3_proto); break; + case VCAP_TYPE_ES0: + return kslist->keysets[0]; default: pr_err("vcap type: %s not supported\n", lan966x_vcaps[admin->vtype].name); @@ -338,6 +354,14 @@ static void lan966x_vcap_is2_add_default_fields(struct lan966x_port *port, VCAP_BIT_0); } +static void lan966x_vcap_es0_add_default_fields(struct lan966x_port *port, + struct vcap_admin *admin, + struct vcap_rule *rule) +{ + vcap_rule_add_key_u32(rule, VCAP_KF_IF_EGR_PORT_NO, + port->chip_port, GENMASK(4, 0)); +} + static void lan966x_vcap_add_default_fields(struct net_device *dev, struct vcap_admin *admin, struct vcap_rule *rule) @@ -351,6 +375,9 @@ static void lan966x_vcap_add_default_fields(struct net_device *dev, case VCAP_TYPE_IS2: lan966x_vcap_is2_add_default_fields(port, admin, rule); break; + case VCAP_TYPE_ES0: + lan966x_vcap_es0_add_default_fields(port, admin, rule); + break; default: pr_err("vcap type: %s not supported\n", lan966x_vcaps[admin->vtype].name); @@ -366,6 +393,40 @@ static void lan966x_vcap_cache_erase(struct vcap_admin *admin) memset(&admin->cache.counter, 0, sizeof(admin->cache.counter)); } +/* The ESDX counter is only used/incremented if the frame has been classified + * with an ISDX > 0 (e.g by a rule in IS0). This is not mentioned in the + * datasheet. + */ +static void lan966x_es0_read_esdx_counter(struct lan966x *lan966x, + struct vcap_admin *admin, u32 id) +{ + u32 counter; + + id = id & 0xff; /* counter limit */ + mutex_lock(&lan966x->stats_lock); + lan_wr(SYS_STAT_CFG_STAT_VIEW_SET(id), lan966x, SYS_STAT_CFG); + counter = lan_rd(lan966x, SYS_CNT(LAN966X_STAT_ESDX_GRN_PKTS)) + + lan_rd(lan966x, SYS_CNT(LAN966X_STAT_ESDX_YEL_PKTS)); + mutex_unlock(&lan966x->stats_lock); + if (counter) + admin->cache.counter = counter; +} + +static void lan966x_es0_write_esdx_counter(struct lan966x *lan966x, + struct vcap_admin *admin, u32 id) +{ + id = id & 0xff; /* counter limit */ + + mutex_lock(&lan966x->stats_lock); + lan_wr(SYS_STAT_CFG_STAT_VIEW_SET(id), lan966x, SYS_STAT_CFG); + lan_wr(0, lan966x, SYS_CNT(LAN966X_STAT_ESDX_GRN_BYTES)); + lan_wr(admin->cache.counter, lan966x, + SYS_CNT(LAN966X_STAT_ESDX_GRN_PKTS)); + lan_wr(0, lan966x, SYS_CNT(LAN966X_STAT_ESDX_YEL_BYTES)); + lan_wr(0, lan966x, SYS_CNT(LAN966X_STAT_ESDX_YEL_PKTS)); + mutex_unlock(&lan966x->stats_lock); +} + static void lan966x_vcap_cache_write(struct net_device *dev, struct vcap_admin *admin, enum vcap_selection sel, @@ -398,6 +459,9 @@ static void lan966x_vcap_cache_write(struct net_device *dev, admin->cache.sticky = admin->cache.counter > 0; lan_wr(admin->cache.counter, lan966x, VCAP_CNT_DAT(admin->tgt_inst, 0)); + + if (admin->vtype == VCAP_TYPE_ES0) + lan966x_es0_write_esdx_counter(lan966x, admin, start); break; default: break; @@ -437,6 +501,9 @@ static void lan966x_vcap_cache_read(struct net_device *dev, admin->cache.counter = lan_rd(lan966x, VCAP_CNT_DAT(instance, 0)); admin->cache.sticky = admin->cache.counter > 0; + + if (admin->vtype == VCAP_TYPE_ES0) + lan966x_es0_read_esdx_counter(lan966x, admin, start); } } @@ -625,6 +692,12 @@ static void lan966x_vcap_port_key_deselection(struct lan966x *lan966x, lan_wr(0, lan966x, ANA_VCAP_S2_CFG(p)); break; + case VCAP_TYPE_ES0: + for (int p = 0; p < lan966x->num_phys_ports; ++p) + lan_rmw(REW_PORT_CFG_ES0_EN_SET(false), + REW_PORT_CFG_ES0_EN, lan966x, + REW_PORT_CFG(p)); + break; default: pr_err("vcap type: %s not supported\n", lan966x_vcaps[admin->vtype].name); @@ -674,9 +747,18 @@ int lan966x_vcap_init(struct lan966x *lan966x) lan_rmw(ANA_VCAP_CFG_S1_ENA_SET(true), ANA_VCAP_CFG_S1_ENA, lan966x, ANA_VCAP_CFG(lan966x->ports[p]->chip_port)); + + lan_rmw(REW_PORT_CFG_ES0_EN_SET(true), + REW_PORT_CFG_ES0_EN, lan966x, + REW_PORT_CFG(lan966x->ports[p]->chip_port)); } } + /* Statistics: Use ESDX from ES0 if hit, otherwise no counting */ + lan_rmw(REW_STAT_CFG_STAT_MODE_SET(1), + REW_STAT_CFG_STAT_MODE, lan966x, + REW_STAT_CFG); + lan966x->vcap_ctrl = ctrl; return 0; diff --git a/drivers/net/ethernet/microchip/vcap/vcap_ag_api.h b/drivers/net/ethernet/microchip/vcap/vcap_ag_api.h index a556c4419986..c3569a4c7b69 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_ag_api.h +++ b/drivers/net/ethernet/microchip/vcap/vcap_ag_api.h @@ -3,8 +3,8 @@ * Microchip VCAP API */ -/* This file is autogenerated by cml-utils 2023-02-16 11:41:14 +0100. - * Commit ID: be85f176b3a151fa748dcaf97c8824a5c2e065f3 +/* This file is autogenerated by cml-utils 2023-03-13 10:16:42 +0100. + * Commit ID: 259f0efd6d6d91bfbf62858de153cc757b6bffa3 (dirty) */ #ifndef __VCAP_AG_API__ @@ -51,6 +51,7 @@ enum vcap_keyfield_set { VCAP_KFS_RT, /* lan966x is1 X1 */ VCAP_KFS_SMAC_SIP4, /* lan966x is2 X1 */ VCAP_KFS_SMAC_SIP6, /* lan966x is2 X2 */ + VCAP_KFS_VID, /* lan966x es0 X1 */ }; /* List of keyfields with description @@ -79,7 +80,7 @@ enum vcap_keyfield_set { * Second DEI in multiple vlan tags (inner tag) * VCAP_KF_8021Q_DEI2: W1, sparx5: is0 * Third DEI in multiple vlan tags (not always available) - * VCAP_KF_8021Q_DEI_CLS: W1, sparx5: is2/es2, lan966x: is2 + * VCAP_KF_8021Q_DEI_CLS: W1, sparx5: is2/es2, lan966x: is2/es0 * Classified DEI * VCAP_KF_8021Q_PCP0: W3, sparx5: is0, lan966x: is1 * First PCP in multiple vlan tags (outer tag or default port tag) @@ -87,7 +88,7 @@ enum vcap_keyfield_set { * Second PCP in multiple vlan tags (inner tag) * VCAP_KF_8021Q_PCP2: W3, sparx5: is0 * Third PCP in multiple vlan tags (not always available) - * VCAP_KF_8021Q_PCP_CLS: W3, sparx5: is2/es2, lan966x: is2 + * VCAP_KF_8021Q_PCP_CLS: W3, sparx5: is2/es2, lan966x: is2/es0 * Classified PCP * VCAP_KF_8021Q_TPID: W3, sparx5: es0 * TPID for outer tag: 0: Customer TPID 1: Service TPID (88A8 or programmable) @@ -104,7 +105,7 @@ enum vcap_keyfield_set { * VCAP_KF_8021Q_VID2: W12, sparx5: is0 * Third VID in multiple vlan tags (not always available) * VCAP_KF_8021Q_VID_CLS: sparx5 is2 W13, sparx5 es0 W13, sparx5 es2 W13, - * lan966x is2 W12 + * lan966x is2 W12, lan966x es0 W12 * Classified VID * VCAP_KF_8021Q_VLAN_DBL_TAGGED_IS: W1, lan966x: is1 * Set if frame has two or more Q-tags. Independent of port VLAN awareness @@ -146,10 +147,10 @@ enum vcap_keyfield_set { * VCAP_KF_IF_EGR_PORT_MASK_RNG: W3, sparx5: es2 * Select which 32 port group is available in IF_EGR_PORT (or virtual ports or * CPU queue) - * VCAP_KF_IF_EGR_PORT_NO: W7, sparx5: es0 + * VCAP_KF_IF_EGR_PORT_NO: sparx5 es0 W7, lan966x es0 W4 * Egress port number * VCAP_KF_IF_IGR_PORT: sparx5 is0 W7, sparx5 es2 W9, lan966x is1 W3, lan966x - * is2 W4 + * is2 W4, lan966x es0 W4 * Sparx5: Logical ingress port number retrieved from * ANA_CL::PORT_ID_CFG.LPORT_NUM or ERLEG, LAN966x: ingress port nunmber * VCAP_KF_IF_IGR_PORT_MASK: sparx5 is0 W65, sparx5 is2 W32, sparx5 is2 W65, @@ -178,11 +179,12 @@ enum vcap_keyfield_set { * Payload after IPv6 header * VCAP_KF_IP_SNAP_IS: W1, sparx5: is0, lan966x: is1 * Set if frame is IPv4, IPv6, or SNAP frame - * VCAP_KF_ISDX_CLS: W12, sparx5: is2/es0/es2 + * VCAP_KF_ISDX_CLS: sparx5 is2 W12, sparx5 es0 W12, sparx5 es2 W12, lan966x es0 + * W8 * Classified ISDX - * VCAP_KF_ISDX_GT0_IS: W1, sparx5: is2/es0/es2, lan966x: is2 + * VCAP_KF_ISDX_GT0_IS: W1, sparx5: is2/es0/es2, lan966x: is2/es0 * Set if classified ISDX > 0 - * VCAP_KF_L2_BC_IS: W1, sparx5: is0/is2/es2, lan966x: is1/is2 + * VCAP_KF_L2_BC_IS: W1, sparx5: is0/is2/es2, lan966x: is1/is2/es0 * Set if frame's destination MAC address is the broadcast address * (FF-FF-FF-FF-FF-FF). * VCAP_KF_L2_DMAC: W48, sparx5: is0/is2/es2, lan966x: is1/is2 @@ -195,7 +197,7 @@ enum vcap_keyfield_set { * LLC header and data after up to two VLAN tags and the type/length field * VCAP_KF_L2_MAC: W48, lan966x: is1 * MAC address (FIRST=1: SMAC, FIRST=0: DMAC) - * VCAP_KF_L2_MC_IS: W1, sparx5: is0/is2/es2, lan966x: is1/is2 + * VCAP_KF_L2_MC_IS: W1, sparx5: is0/is2/es2, lan966x: is1/is2/es0 * Set if frame's destination MAC address is a multicast address (bit 40 = 1). * VCAP_KF_L2_PAYLOAD0: W16, lan966x: is2 * Payload bytes 0-1 after the frame's EtherType @@ -213,7 +215,7 @@ enum vcap_keyfield_set { * SNAP header after LLC header (AA-AA-03) * VCAP_KF_L3_DIP_EQ_SIP_IS: W1, sparx5: is2/es2, lan966x: is2 * Set if Src IP matches Dst IP address - * VCAP_KF_L3_DPL_CLS: W1, sparx5: es0/es2 + * VCAP_KF_L3_DPL_CLS: W1, sparx5: es0/es2, lan966x: es0 * The frames drop precedence level * VCAP_KF_L3_DSCP: W6, sparx5: is0, lan966x: is1 * Frame's DSCP value @@ -330,8 +332,12 @@ enum vcap_keyfield_set { * Frame's OAM version * VCAP_KF_OAM_Y1731_IS: W1, sparx5: is2/es2, lan966x: is2 * Set if frame's EtherType = 0x8902 + * VCAP_KF_PDU_TYPE: W4, lan966x: es0 + * PDU type value (none, OAM CCM, MRP, DLR, RTE, IPv4, IPv6, OAM non-CCM) * VCAP_KF_PROT_ACTIVE: W1, sparx5: es0/es2 * Protection is active + * VCAP_KF_RTP_ID: W10, lan966x: es0 + * Classified RTP_ID * VCAP_KF_RT_FRMID: W32, lan966x: is1 * Profinet or OPC-UA FrameId * VCAP_KF_RT_TYPE: W2, lan966x: is1 @@ -470,7 +476,9 @@ enum vcap_key_field { VCAP_KF_OAM_OPCODE, VCAP_KF_OAM_VER, VCAP_KF_OAM_Y1731_IS, + VCAP_KF_PDU_TYPE, VCAP_KF_PROT_ACTIVE, + VCAP_KF_RTP_ID, VCAP_KF_RT_FRMID, VCAP_KF_RT_TYPE, VCAP_KF_RT_VLAN_IDX, @@ -489,6 +497,7 @@ enum vcap_actionfield_set { VCAP_AFS_FULL, /* sparx5 is0 X3 */ VCAP_AFS_S1, /* lan966x is1 X1 */ VCAP_AFS_SMAC_SIP, /* lan966x is2 X1 */ + VCAP_AFS_VID, /* lan966x es0 X1 */ }; /* List of actionfields with description @@ -523,9 +532,9 @@ enum vcap_actionfield_set { * while bits 1:0 control first lookup. Encoding per lookup: 0: Disabled. 1: * Extract 40 bytes after position corresponding to the location of the IPv4 * header and use as key. 2: Extract 40 bytes after SMAC and use as key - * VCAP_AF_DEI_A_VAL: W1, sparx5: es0 + * VCAP_AF_DEI_A_VAL: W1, sparx5: es0, lan966x: es0 * DEI used in ES0 tag A. See TAG_A_DEI_SEL. - * VCAP_AF_DEI_B_VAL: W1, sparx5: es0 + * VCAP_AF_DEI_B_VAL: W1, sparx5: es0, lan966x: es0 * DEI used in ES0 tag B. See TAG_B_DEI_SEL. * VCAP_AF_DEI_C_VAL: W1, sparx5: es0 * DEI used in ES0 tag C. See TAG_C_DEI_SEL. @@ -556,7 +565,7 @@ enum vcap_actionfield_set { * VCAP_AF_ES2_REW_CMD: W3, sparx5: es2 * Command forwarded to REW: 0: No action. 1: SWAP MAC addresses. 2: Do L2CP * DMAC translation when entering or leaving a tunnel. - * VCAP_AF_ESDX: W13, sparx5: es0 + * VCAP_AF_ESDX: sparx5 es0 W13, lan966x es0 W8 * Egress counter index. Used to index egress counter set as defined in * REW::STAT_CFG. * VCAP_AF_FWD_KILL_ENA: W1, lan966x: is2 @@ -652,9 +661,9 @@ enum vcap_actionfield_set { * (input) AND ~PAG_OVERRIDE_MASK) OR (PAG_VAL AND PAG_OVERRIDE_MASK) * VCAP_AF_PAG_VAL: W8, sparx5: is0, lan966x: is1 * See PAG_OVERRIDE_MASK. - * VCAP_AF_PCP_A_VAL: W3, sparx5: es0 + * VCAP_AF_PCP_A_VAL: W3, sparx5: es0, lan966x: es0 * PCP used in ES0 tag A. See TAG_A_PCP_SEL. - * VCAP_AF_PCP_B_VAL: W3, sparx5: es0 + * VCAP_AF_PCP_B_VAL: W3, sparx5: es0, lan966x: es0 * PCP used in ES0 tag B. See TAG_B_PCP_SEL. * VCAP_AF_PCP_C_VAL: W3, sparx5: es0 * PCP used in ES0 tag C. See TAG_C_PCP_SEL. @@ -691,10 +700,10 @@ enum vcap_actionfield_set { * Selects tag C mode: 0: Do not push tag C. 1: Push tag C if * IFH.VSTAX.TAG.WAS_TAGGED = 1. 2: Push tag C if IFH.VSTAX.TAG.WAS_TAGGED = 0. * 3: Push tag C if UNTAG_VID_ENA = 0 or (C-TAG.VID ! = VID_C_VAL). - * VCAP_AF_PUSH_INNER_TAG: W1, sparx5: es0 + * VCAP_AF_PUSH_INNER_TAG: W1, sparx5: es0, lan966x: es0 * Controls inner tagging. 0: Do not push ES0 tag B as inner tag. 1: Push ES0 * tag B as inner tag. - * VCAP_AF_PUSH_OUTER_TAG: W2, sparx5: es0 + * VCAP_AF_PUSH_OUTER_TAG: W2, sparx5: es0, lan966x: es0 * Controls outer tagging. 0: No ES0 tag A: Port tag is allowed if enabled on * port. 1: ES0 tag A: Push ES0 tag A. No port tag. 2: Force port tag: Always * push port tag. No ES0 tag A. 3: Force untag: Never push port tag or ES0 tag @@ -720,29 +729,29 @@ enum vcap_actionfield_set { * VCAP_AF_SWAP_MACS_ENA: W1, sparx5: es0 * This setting is only active when FWD_SEL = 1 or FWD_SEL = 2 and PIPELINE_ACT * = LBK_ASM. 0: No action. 1: Swap MACs and clear bit 40 in new SMAC. - * VCAP_AF_TAG_A_DEI_SEL: W3, sparx5: es0 + * VCAP_AF_TAG_A_DEI_SEL: sparx5 es0 W3, lan966x es0 W2 * Selects PCP for ES0 tag A. 0: Classified DEI. 1: DEI_A_VAL. 2: DP and QoS * mapped to PCP (per port table). 3: DP. - * VCAP_AF_TAG_A_PCP_SEL: W3, sparx5: es0 + * VCAP_AF_TAG_A_PCP_SEL: sparx5 es0 W3, lan966x es0 W2 * Selects PCP for ES0 tag A. 0: Classified PCP. 1: PCP_A_VAL. 2: DP and QoS * mapped to PCP (per port table). 3: QoS class. - * VCAP_AF_TAG_A_TPID_SEL: W3, sparx5: es0 + * VCAP_AF_TAG_A_TPID_SEL: sparx5 es0 W3, lan966x es0 W2 * Selects TPID for ES0 tag A: 0: 0x8100. 1: 0x88A8. 2: Custom * (REW:PORT:PORT_VLAN_CFG.PORT_TPID). 3: If IFH.TAG_TYPE = 0 then 0x8100 else * custom. - * VCAP_AF_TAG_A_VID_SEL: W2, sparx5: es0 + * VCAP_AF_TAG_A_VID_SEL: sparx5 es0 W2, lan966x es0 W1 * Selects VID for ES0 tag A. 0: Classified VID + VID_A_VAL. 1: VID_A_VAL. - * VCAP_AF_TAG_B_DEI_SEL: W3, sparx5: es0 + * VCAP_AF_TAG_B_DEI_SEL: sparx5 es0 W3, lan966x es0 W2 * Selects PCP for ES0 tag B. 0: Classified DEI. 1: DEI_B_VAL. 2: DP and QoS * mapped to PCP (per port table). 3: DP. - * VCAP_AF_TAG_B_PCP_SEL: W3, sparx5: es0 + * VCAP_AF_TAG_B_PCP_SEL: sparx5 es0 W3, lan966x es0 W2 * Selects PCP for ES0 tag B. 0: Classified PCP. 1: PCP_B_VAL. 2: DP and QoS * mapped to PCP (per port table). 3: QoS class. - * VCAP_AF_TAG_B_TPID_SEL: W3, sparx5: es0 + * VCAP_AF_TAG_B_TPID_SEL: sparx5 es0 W3, lan966x es0 W2 * Selects TPID for ES0 tag B. 0: 0x8100. 1: 0x88A8. 2: Custom * (REW:PORT:PORT_VLAN_CFG.PORT_TPID). 3: If IFH.TAG_TYPE = 0 then 0x8100 else * custom. - * VCAP_AF_TAG_B_VID_SEL: W2, sparx5: es0 + * VCAP_AF_TAG_B_VID_SEL: sparx5 es0 W2, lan966x es0 W1 * Selects VID for ES0 tag B. 0: Classified VID + VID_B_VAL. 1: VID_B_VAL. * VCAP_AF_TAG_C_DEI_SEL: W3, sparx5: es0 * Selects DEI source for ES0 tag C. 0: Classified DEI. 1: DEI_C_VAL. 2: @@ -770,9 +779,9 @@ enum vcap_actionfield_set { * VCAP_AF_UNTAG_VID_ENA: W1, sparx5: es0 * Controls insertion of tag C. Untag or insert mode can be selected. See * PUSH_CUSTOMER_TAG. - * VCAP_AF_VID_A_VAL: W12, sparx5: es0 + * VCAP_AF_VID_A_VAL: W12, sparx5: es0, lan966x: es0 * VID used in ES0 tag A. See TAG_A_VID_SEL. - * VCAP_AF_VID_B_VAL: W12, sparx5: es0 + * VCAP_AF_VID_B_VAL: W12, sparx5: es0, lan966x: es0 * VID used in ES0 tag B. See TAG_B_VID_SEL. * VCAP_AF_VID_C_VAL: W12, sparx5: es0 * VID used in ES0 tag C. See TAG_C_VID_SEL. diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c index 5675b0962bc3..a418ad8e8770 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c @@ -1121,7 +1121,7 @@ static void vcap_copy_to_client_actionfield(struct vcap_rule_internal *ri, vcap_copy_from_w32be(field->data.u128.value, value, field_size, width); break; - }; + } } else { switch (field->ctrl.type) { case VCAP_FIELD_BIT: @@ -1162,7 +1162,7 @@ static void vcap_copy_to_client_actionfield(struct vcap_rule_internal *ri, value, width, field_size); break; - }; + } } } @@ -1236,7 +1236,7 @@ static void vcap_copy_to_client_keyfield(struct vcap_rule_internal *ri, vcap_copy_from_w32be(field->data.u128.mask, mask, field_size, width); break; - }; + } } else { switch (field->ctrl.type) { case VCAP_FIELD_BIT: @@ -1284,7 +1284,7 @@ static void vcap_copy_to_client_keyfield(struct vcap_rule_internal *ri, value, mask, width, field_size); break; - }; + } } } diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index dfedb52b7e70..e75cbb287625 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -436,49 +436,41 @@ static void nfp_add_media_link_mode(struct nfp_port *port, struct nfp_eth_table_port *eth_port, struct ethtool_link_ksettings *cmd) { - u64 supported_modes[2], advertised_modes[2]; - struct nfp_eth_media_buf ethm = { - .eth_index = eth_port->eth_index, - }; - struct nfp_cpp *cpp = port->app->cpp; - - if (nfp_eth_read_media(cpp, ðm)) { - bitmap_fill(port->speed_bitmap, NFP_SUP_SPEED_NUMBER); - return; - } - bitmap_zero(port->speed_bitmap, NFP_SUP_SPEED_NUMBER); - for (u32 i = 0; i < 2; i++) { - supported_modes[i] = le64_to_cpu(ethm.supported_modes[i]); - advertised_modes[i] = le64_to_cpu(ethm.advertised_modes[i]); - } - for (u32 i = 0; i < NFP_MEDIA_LINK_MODES_NUMBER; i++) { if (i < 64) { - if (supported_modes[0] & BIT_ULL(i)) { + if (eth_port->link_modes_supp[0] & BIT_ULL(i)) { __set_bit(nfp_eth_media_table[i].ethtool_link_mode, cmd->link_modes.supported); __set_bit(nfp_eth_media_table[i].speed, port->speed_bitmap); } - if (advertised_modes[0] & BIT_ULL(i)) + if (eth_port->link_modes_ad[0] & BIT_ULL(i)) __set_bit(nfp_eth_media_table[i].ethtool_link_mode, cmd->link_modes.advertising); } else { - if (supported_modes[1] & BIT_ULL(i - 64)) { + if (eth_port->link_modes_supp[1] & BIT_ULL(i - 64)) { __set_bit(nfp_eth_media_table[i].ethtool_link_mode, cmd->link_modes.supported); __set_bit(nfp_eth_media_table[i].speed, port->speed_bitmap); } - if (advertised_modes[1] & BIT_ULL(i - 64)) + if (eth_port->link_modes_ad[1] & BIT_ULL(i - 64)) __set_bit(nfp_eth_media_table[i].ethtool_link_mode, cmd->link_modes.advertising); } } + + /* We take all speeds as supported when it fails to read + * link modes due to old management firmware that doesn't + * support link modes reading or error occurring, so that + * speed change of this port is allowed. + */ + if (bitmap_empty(port->speed_bitmap, NFP_SUP_SPEED_NUMBER)) + bitmap_fill(port->speed_bitmap, NFP_SUP_SPEED_NUMBER); } /** diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h index 781edc451bd4..6e044ac04917 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h @@ -196,6 +196,9 @@ enum nfp_ethtool_link_mode_list { * subports) * @ports.is_split: is interface part of a split port * @ports.fec_modes_supported: bitmap of FEC modes supported + * + * @ports.link_modes_supp: bitmap of link modes supported + * @ports.link_modes_ad: bitmap of link modes advertised */ struct nfp_eth_table { unsigned int count; @@ -235,6 +238,9 @@ struct nfp_eth_table { bool is_split; unsigned int fec_modes_supported; + + u64 link_modes_supp[2]; + u64 link_modes_ad[2]; } ports[]; }; @@ -313,7 +319,6 @@ struct nfp_eth_media_buf { }; int nfp_nsp_read_media(struct nfp_nsp *state, void *buf, unsigned int size); -int nfp_eth_read_media(struct nfp_cpp *cpp, struct nfp_eth_media_buf *ethm); #define NFP_NSP_VERSION_BUFSZ 1024 /* reasonable size, not in the ABI */ diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c index 570ac1bb2122..9d62085d772a 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c @@ -227,6 +227,30 @@ nfp_eth_calc_port_type(struct nfp_cpp *cpp, struct nfp_eth_table_port *entry) entry->port_type = PORT_DA; } +static void +nfp_eth_read_media(struct nfp_cpp *cpp, struct nfp_nsp *nsp, struct nfp_eth_table_port *entry) +{ + struct nfp_eth_media_buf ethm = { + .eth_index = entry->eth_index, + }; + unsigned int i; + int ret; + + if (!nfp_nsp_has_read_media(nsp)) + return; + + ret = nfp_nsp_read_media(nsp, ðm, sizeof(ethm)); + if (ret) { + nfp_err(cpp, "Reading media link modes failed: %d\n", ret); + return; + } + + for (i = 0; i < 2; i++) { + entry->link_modes_supp[i] = le64_to_cpu(ethm.supported_modes[i]); + entry->link_modes_ad[i] = le64_to_cpu(ethm.advertised_modes[i]); + } +} + /** * nfp_eth_read_ports() - retrieve port information * @cpp: NFP CPP handle @@ -293,8 +317,10 @@ __nfp_eth_read_ports(struct nfp_cpp *cpp, struct nfp_nsp *nsp) &table->ports[j++]); nfp_eth_calc_port_geometry(cpp, table); - for (i = 0; i < table->count; i++) + for (i = 0; i < table->count; i++) { nfp_eth_calc_port_type(cpp, &table->ports[i]); + nfp_eth_read_media(cpp, nsp, &table->ports[i]); + } kfree(entries); @@ -647,29 +673,3 @@ int __nfp_eth_set_split(struct nfp_nsp *nsp, unsigned int lanes) return NFP_ETH_SET_BIT_CONFIG(nsp, NSP_ETH_RAW_PORT, NSP_ETH_PORT_LANES, lanes, NSP_ETH_CTRL_SET_LANES); } - -int nfp_eth_read_media(struct nfp_cpp *cpp, struct nfp_eth_media_buf *ethm) -{ - struct nfp_nsp *nsp; - int ret; - - nsp = nfp_nsp_open(cpp); - if (IS_ERR(nsp)) { - nfp_err(cpp, "Failed to access the NSP: %pe\n", nsp); - return PTR_ERR(nsp); - } - - if (!nfp_nsp_has_read_media(nsp)) { - nfp_warn(cpp, "Reading media link modes not supported. Please update flash\n"); - ret = -EOPNOTSUPP; - goto exit_close_nsp; - } - - ret = nfp_nsp_read_media(nsp, ethm, sizeof(*ethm)); - if (ret) - nfp_err(cpp, "Reading media link modes failed: %pe\n", ERR_PTR(ret)); - -exit_close_nsp: - nfp_nsp_close(nsp); - return ret; -} diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_common.h b/drivers/net/ethernet/samsung/sxgbe/sxgbe_common.h index 0f45107db8dd..d14e0cfc3a6b 100644 --- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_common.h +++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_common.h @@ -511,7 +511,7 @@ struct sxgbe_priv_data { struct sxgbe_priv_data *sxgbe_drv_probe(struct device *device, struct sxgbe_plat_data *plat_dat, void __iomem *addr); -int sxgbe_drv_remove(struct net_device *ndev); +void sxgbe_drv_remove(struct net_device *ndev); void sxgbe_set_ethtool_ops(struct net_device *netdev); int sxgbe_mdio_unregister(struct net_device *ndev); int sxgbe_mdio_register(struct net_device *ndev); diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c index 9664f029fa16..71439825ea4e 100644 --- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c @@ -2203,7 +2203,7 @@ error_free_netdev: * Description: this function resets the TX/RX processes, disables the MAC RX/TX * changes the link status, releases the DMA descriptor rings. */ -int sxgbe_drv_remove(struct net_device *ndev) +void sxgbe_drv_remove(struct net_device *ndev) { struct sxgbe_priv_data *priv = netdev_priv(ndev); u8 queue_num; @@ -2231,8 +2231,6 @@ int sxgbe_drv_remove(struct net_device *ndev) kfree(priv->hw); free_netdev(ndev); - - return 0; } #ifdef CONFIG_PM diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_platform.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_platform.c index 4e5526303f07..fb59ff94509a 100644 --- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_platform.c +++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_platform.c @@ -172,9 +172,10 @@ err_out: static int sxgbe_platform_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); - int ret = sxgbe_drv_remove(ndev); - return ret; + sxgbe_drv_remove(ndev); + + return 0; } #ifdef CONFIG_PM diff --git a/drivers/net/ethernet/sfc/mae.c b/drivers/net/ethernet/sfc/mae.c index 49706a7b94bf..37a4c6925ad4 100644 --- a/drivers/net/ethernet/sfc/mae.c +++ b/drivers/net/ethernet/sfc/mae.c @@ -482,12 +482,14 @@ int efx_mae_match_check_caps(struct efx_nic *efx, rc; \ }) /* Checks that the fields needed for encap-rule matches are supported by the - * MAE. All the fields are exact-match. + * MAE. All the fields are exact-match, except possibly ENC_IP_TOS. */ int efx_mae_check_encap_match_caps(struct efx_nic *efx, bool ipv6, + u8 ip_tos_mask, __be16 udp_sport_mask, struct netlink_ext_ack *extack) { u8 *supported_fields = efx->tc->caps->outer_rule_fields; + enum mask_type typ; int rc; if (CHECK(ENC_ETHER_TYPE)) @@ -504,6 +506,22 @@ int efx_mae_check_encap_match_caps(struct efx_nic *efx, bool ipv6, if (CHECK(ENC_L4_DPORT) || CHECK(ENC_IP_PROTO)) return rc; + typ = classify_mask((const u8 *)&udp_sport_mask, sizeof(udp_sport_mask)); + rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_ENC_L4_SPORT], + typ); + if (rc) { + NL_SET_ERR_MSG_FMT_MOD(extack, "No support for %s mask in field %s", + mask_type_name(typ), "enc_src_port"); + return rc; + } + typ = classify_mask(&ip_tos_mask, sizeof(ip_tos_mask)); + rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_ENC_IP_TOS], + typ); + if (rc) { + NL_SET_ERR_MSG_FMT_MOD(extack, "No support for %s mask in field %s", + mask_type_name(typ), "enc_ip_tos"); + return rc; + } return 0; } #undef CHECK @@ -1001,8 +1019,16 @@ int efx_mae_register_encap_match(struct efx_nic *efx, encap->udp_dport); MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_L4_DPORT_BE_MASK, ~(__be16)0); + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_L4_DPORT_BE, + encap->udp_sport); + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_L4_DPORT_BE_MASK, + encap->udp_sport_mask); MCDI_STRUCT_SET_BYTE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_IP_PROTO, IPPROTO_UDP); MCDI_STRUCT_SET_BYTE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_IP_PROTO_MASK, ~0); + MCDI_STRUCT_SET_BYTE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_IP_TOS, + encap->ip_tos); + MCDI_STRUCT_SET_BYTE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_IP_TOS_MASK, + encap->ip_tos_mask); rc = efx_mcdi_rpc(efx, MC_CMD_MAE_OUTER_RULE_INSERT, inbuf, sizeof(inbuf), outbuf, sizeof(outbuf), &outlen); if (rc) diff --git a/drivers/net/ethernet/sfc/mae.h b/drivers/net/ethernet/sfc/mae.h index 9226219491a0..1cf8dfeb0c28 100644 --- a/drivers/net/ethernet/sfc/mae.h +++ b/drivers/net/ethernet/sfc/mae.h @@ -82,6 +82,7 @@ int efx_mae_match_check_caps(struct efx_nic *efx, const struct efx_tc_match_fields *mask, struct netlink_ext_ack *extack); int efx_mae_check_encap_match_caps(struct efx_nic *efx, bool ipv6, + u8 ip_tos_mask, __be16 udp_sport_mask, struct netlink_ext_ack *extack); int efx_mae_check_encap_type_supported(struct efx_nic *efx, enum efx_encap_type typ); diff --git a/drivers/net/ethernet/sfc/tc.c b/drivers/net/ethernet/sfc/tc.c index 0327639a628a..6dfbdb39f2fe 100644 --- a/drivers/net/ethernet/sfc/tc.c +++ b/drivers/net/ethernet/sfc/tc.c @@ -132,23 +132,6 @@ static void efx_tc_free_action_set_list(struct efx_nic *efx, /* Don't kfree, as acts is embedded inside a struct efx_tc_flow_rule */ } -static void efx_tc_flow_free(void *ptr, void *arg) -{ - struct efx_tc_flow_rule *rule = ptr; - struct efx_nic *efx = arg; - - netif_err(efx, drv, efx->net_dev, - "tc rule %lx still present at teardown, removing\n", - rule->cookie); - - efx_mae_delete_rule(efx, rule->fw_id); - - /* Release entries in subsidiary tables */ - efx_tc_free_action_set_list(efx, &rule->acts, true); - - kfree(rule); -} - /* Boilerplate for the simple 'copy a field' cases */ #define _MAP_KEY_AND_MASK(_name, _type, _tcget, _tcfield, _field) \ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_##_name)) { \ @@ -219,6 +202,7 @@ static int efx_tc_flower_parse_match(struct efx_nic *efx, BIT(FLOW_DISSECTOR_KEY_ENC_KEYID) | BIT(FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS) | BIT(FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS) | + BIT(FLOW_DISSECTOR_KEY_ENC_IP) | BIT(FLOW_DISSECTOR_KEY_ENC_PORTS) | BIT(FLOW_DISSECTOR_KEY_ENC_CONTROL) | BIT(FLOW_DISSECTOR_KEY_TCP) | @@ -363,20 +347,48 @@ static int efx_tc_flower_parse_match(struct efx_nic *efx, return 0; } +static void efx_tc_flower_release_encap_match(struct efx_nic *efx, + struct efx_tc_encap_match *encap) +{ + int rc; + + if (!refcount_dec_and_test(&encap->ref)) + return; /* still in use */ + + if (encap->type == EFX_TC_EM_DIRECT) { + rc = efx_mae_unregister_encap_match(efx, encap); + if (rc) + /* Display message but carry on and remove entry from our + * SW tables, because there's not much we can do about it. + */ + netif_err(efx, drv, efx->net_dev, + "Failed to release encap match %#x, rc %d\n", + encap->fw_id, rc); + } + rhashtable_remove_fast(&efx->tc->encap_match_ht, &encap->linkage, + efx_tc_encap_match_ht_params); + if (encap->pseudo) + efx_tc_flower_release_encap_match(efx, encap->pseudo); + kfree(encap); +} + static int efx_tc_flower_record_encap_match(struct efx_nic *efx, struct efx_tc_match *match, enum efx_encap_type type, + enum efx_tc_em_pseudo_type em_type, + u8 child_ip_tos_mask, + __be16 child_udp_sport_mask, struct netlink_ext_ack *extack) { - struct efx_tc_encap_match *encap, *old; + struct efx_tc_encap_match *encap, *old, *pseudo = NULL; bool ipv6 = false; int rc; /* We require that the socket-defining fields (IP addrs and UDP dest - * port) are present and exact-match. Other fields are currently not - * allowed. This meets what OVS will ask for, and means that we don't - * need to handle difficult checks for overlapping matches as could - * come up if we allowed masks or varying sets of match fields. + * port) are present and exact-match. Other fields may only be used + * if the field-set (and any masks) are the same for all encap + * matches on the same <sip,dip,dport> tuple; this is enforced by + * pseudo encap matches. */ if (match->mask.enc_dst_ip | match->mask.enc_src_ip) { if (!IS_ALL_ONES(match->mask.enc_dst_ip)) { @@ -414,29 +426,42 @@ static int efx_tc_flower_record_encap_match(struct efx_nic *efx, NL_SET_ERR_MSG_MOD(extack, "Egress encap match is not exact on dst UDP port"); return -EOPNOTSUPP; } - if (match->mask.enc_sport) { - NL_SET_ERR_MSG_MOD(extack, "Egress encap match on src UDP port not supported"); - return -EOPNOTSUPP; - } - if (match->mask.enc_ip_tos) { - NL_SET_ERR_MSG_MOD(extack, "Egress encap match on IP ToS not supported"); - return -EOPNOTSUPP; + if (match->mask.enc_sport || match->mask.enc_ip_tos) { + struct efx_tc_match pmatch = *match; + + if (em_type == EFX_TC_EM_PSEUDO_MASK) { /* can't happen */ + NL_SET_ERR_MSG_MOD(extack, "Bad recursion in egress encap match handler"); + return -EOPNOTSUPP; + } + pmatch.value.enc_ip_tos = 0; + pmatch.mask.enc_ip_tos = 0; + pmatch.value.enc_sport = 0; + pmatch.mask.enc_sport = 0; + rc = efx_tc_flower_record_encap_match(efx, &pmatch, type, + EFX_TC_EM_PSEUDO_MASK, + match->mask.enc_ip_tos, + match->mask.enc_sport, + extack); + if (rc) + return rc; + pseudo = pmatch.encap; } if (match->mask.enc_ip_ttl) { NL_SET_ERR_MSG_MOD(extack, "Egress encap match on IP TTL not supported"); - return -EOPNOTSUPP; + rc = -EOPNOTSUPP; + goto fail_pseudo; } - rc = efx_mae_check_encap_match_caps(efx, ipv6, extack); - if (rc) { - NL_SET_ERR_MSG_FMT_MOD(extack, "MAE hw reports no support for IPv%d encap matches", - ipv6 ? 6 : 4); - return -EOPNOTSUPP; - } + rc = efx_mae_check_encap_match_caps(efx, ipv6, match->mask.enc_ip_tos, + match->mask.enc_sport, extack); + if (rc) + goto fail_pseudo; encap = kzalloc(sizeof(*encap), GFP_USER); - if (!encap) - return -ENOMEM; + if (!encap) { + rc = -ENOMEM; + goto fail_pseudo; + } encap->src_ip = match->value.enc_src_ip; encap->dst_ip = match->value.enc_dst_ip; #ifdef CONFIG_IPV6 @@ -445,12 +470,66 @@ static int efx_tc_flower_record_encap_match(struct efx_nic *efx, #endif encap->udp_dport = match->value.enc_dport; encap->tun_type = type; + encap->ip_tos = match->value.enc_ip_tos; + encap->ip_tos_mask = match->mask.enc_ip_tos; + encap->child_ip_tos_mask = child_ip_tos_mask; + encap->udp_sport = match->value.enc_sport; + encap->udp_sport_mask = match->mask.enc_sport; + encap->child_udp_sport_mask = child_udp_sport_mask; + encap->type = em_type; + encap->pseudo = pseudo; old = rhashtable_lookup_get_insert_fast(&efx->tc->encap_match_ht, &encap->linkage, efx_tc_encap_match_ht_params); if (old) { /* don't need our new entry */ kfree(encap); + if (pseudo) /* don't need our new pseudo either */ + efx_tc_flower_release_encap_match(efx, pseudo); + /* check old and new em_types are compatible */ + switch (old->type) { + case EFX_TC_EM_DIRECT: + /* old EM is in hardware, so mustn't overlap with a + * pseudo, but may be shared with another direct EM + */ + if (em_type == EFX_TC_EM_DIRECT) + break; + NL_SET_ERR_MSG_MOD(extack, "Pseudo encap match conflicts with existing direct entry"); + return -EEXIST; + case EFX_TC_EM_PSEUDO_MASK: + /* old EM is protecting a ToS- or src port-qualified + * filter, so may only be shared with another pseudo + * for the same ToS and src port masks. + */ + if (em_type != EFX_TC_EM_PSEUDO_MASK) { + NL_SET_ERR_MSG_FMT_MOD(extack, + "%s encap match conflicts with existing pseudo(MASK) entry", + em_type ? "Pseudo" : "Direct"); + return -EEXIST; + } + if (child_ip_tos_mask != old->child_ip_tos_mask) { + NL_SET_ERR_MSG_FMT_MOD(extack, + "Pseudo encap match for TOS mask %#04x conflicts with existing pseudo(MASK) entry for TOS mask %#04x", + child_ip_tos_mask, + old->child_ip_tos_mask); + return -EEXIST; + } + if (child_udp_sport_mask != old->child_udp_sport_mask) { + NL_SET_ERR_MSG_FMT_MOD(extack, + "Pseudo encap match for UDP src port mask %#x conflicts with existing pseudo(MASK) entry for mask %#x", + child_udp_sport_mask, + old->child_udp_sport_mask); + return -EEXIST; + } + break; + default: /* Unrecognised pseudo-type. Just say no */ + NL_SET_ERR_MSG_FMT_MOD(extack, + "%s encap match conflicts with existing pseudo(%d) entry", + em_type ? "Pseudo" : "Direct", + old->type); + return -EEXIST; + } + /* check old and new tun_types are compatible */ if (old->tun_type != type) { NL_SET_ERR_MSG_FMT_MOD(extack, "Egress encap match with conflicting tun_type %u != %u", @@ -462,10 +541,12 @@ static int efx_tc_flower_record_encap_match(struct efx_nic *efx, /* existing entry found */ encap = old; } else { - rc = efx_mae_register_encap_match(efx, encap); - if (rc) { - NL_SET_ERR_MSG_MOD(extack, "Failed to record egress encap match in HW"); - goto fail; + if (em_type == EFX_TC_EM_DIRECT) { + rc = efx_mae_register_encap_match(efx, encap); + if (rc) { + NL_SET_ERR_MSG_MOD(extack, "Failed to record egress encap match in HW"); + goto fail; + } } refcount_set(&encap->ref, 1); } @@ -475,30 +556,12 @@ fail: rhashtable_remove_fast(&efx->tc->encap_match_ht, &encap->linkage, efx_tc_encap_match_ht_params); kfree(encap); +fail_pseudo: + if (pseudo) + efx_tc_flower_release_encap_match(efx, pseudo); return rc; } -static void efx_tc_flower_release_encap_match(struct efx_nic *efx, - struct efx_tc_encap_match *encap) -{ - int rc; - - if (!refcount_dec_and_test(&encap->ref)) - return; /* still in use */ - - rc = efx_mae_unregister_encap_match(efx, encap); - if (rc) - /* Display message but carry on and remove entry from our - * SW tables, because there's not much we can do about it. - */ - netif_err(efx, drv, efx->net_dev, - "Failed to release encap match %#x, rc %d\n", - encap->fw_id, rc); - rhashtable_remove_fast(&efx->tc->encap_match_ht, &encap->linkage, - efx_tc_encap_match_ht_params); - kfree(encap); -} - static void efx_tc_delete_rule(struct efx_nic *efx, struct efx_tc_flow_rule *rule) { efx_mae_delete_rule(efx, rule->fw_id); @@ -652,6 +715,7 @@ static int efx_tc_flower_replace_foreign(struct efx_nic *efx, } rc = efx_tc_flower_record_encap_match(efx, &match, type, + EFX_TC_EM_DIRECT, 0, 0, extack); if (rc) goto release; @@ -1454,6 +1518,21 @@ static void efx_tc_encap_match_free(void *ptr, void *__unused) kfree(encap); } +static void efx_tc_flow_free(void *ptr, void *arg) +{ + struct efx_tc_flow_rule *rule = ptr; + struct efx_nic *efx = arg; + + netif_err(efx, drv, efx->net_dev, + "tc rule %lx still present at teardown, removing\n", + rule->cookie); + + /* Also releases entries in subsidiary tables */ + efx_tc_delete_rule(efx, rule); + + kfree(rule); +} + int efx_init_struct_tc(struct efx_nic *efx) { int rc; diff --git a/drivers/net/ethernet/sfc/tc.h b/drivers/net/ethernet/sfc/tc.h index 04cced6a2d39..24e9640c74e9 100644 --- a/drivers/net/ethernet/sfc/tc.h +++ b/drivers/net/ethernet/sfc/tc.h @@ -74,14 +74,41 @@ static inline bool efx_tc_match_is_encap(const struct efx_tc_match_fields *mask) mask->enc_ip_ttl || mask->enc_sport || mask->enc_dport; } +/** + * enum efx_tc_em_pseudo_type - &struct efx_tc_encap_match pseudo type + * + * These are used to classify "pseudo" encap matches, which don't refer + * to an entry in hardware but rather indicate that a section of the + * match space is in use by another Outer Rule. + * + * @EFX_TC_EM_DIRECT: real HW entry in Outer Rule table; not a pseudo. + * Hardware index in &struct efx_tc_encap_match.fw_id is valid. + * @EFX_TC_EM_PSEUDO_MASK: registered by an encap match which includes a + * match on an optional field (currently ip_tos and/or udp_sport), + * to prevent an overlapping encap match _without_ optional fields. + * The pseudo encap match may be referenced again by an encap match + * with different values for these fields, but all masks must match the + * first (stored in our child_* fields). + */ +enum efx_tc_em_pseudo_type { + EFX_TC_EM_DIRECT, + EFX_TC_EM_PSEUDO_MASK, +}; + struct efx_tc_encap_match { __be32 src_ip, dst_ip; struct in6_addr src_ip6, dst_ip6; __be16 udp_dport; + __be16 udp_sport, udp_sport_mask; + u8 ip_tos, ip_tos_mask; struct rhash_head linkage; enum efx_encap_type tun_type; + u8 child_ip_tos_mask; + __be16 child_udp_sport_mask; refcount_t ref; + enum efx_tc_em_pseudo_type type; u32 fw_id; /* index of this entry in firmware encap match table */ + struct efx_tc_encap_match *pseudo; /* Referenced pseudo EM if needed */ }; struct efx_tc_match { diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-anarion.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-anarion.c index 9354bf419112..58a7f08e8d78 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-anarion.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-anarion.c @@ -141,7 +141,7 @@ MODULE_DEVICE_TABLE(of, anarion_dwmac_match); static struct platform_driver anarion_dwmac_driver = { .probe = anarion_dwmac_probe, - .remove = stmmac_pltfr_remove, + .remove_new = stmmac_pltfr_remove, .driver = { .name = "anarion-dwmac", .pm = &stmmac_pltfr_pm_ops, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c index 18acf7dd74e5..9f88530c5e8c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c @@ -464,7 +464,7 @@ remove_config: return ret; } -static int dwc_eth_dwmac_remove(struct platform_device *pdev) +static void dwc_eth_dwmac_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); struct stmmac_priv *priv = netdev_priv(ndev); @@ -477,8 +477,6 @@ static int dwc_eth_dwmac_remove(struct platform_device *pdev) data->remove(pdev); stmmac_remove_config_dt(pdev, priv->plat); - - return 0; } static const struct of_device_id dwc_eth_dwmac_match[] = { @@ -490,7 +488,7 @@ MODULE_DEVICE_TABLE(of, dwc_eth_dwmac_match); static struct platform_driver dwc_eth_dwmac_driver = { .probe = dwc_eth_dwmac_probe, - .remove = dwc_eth_dwmac_remove, + .remove_new = dwc_eth_dwmac_remove, .driver = { .name = "dwc-eth-dwmac", .pm = &stmmac_pltfr_pm_ops, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c index ef8f3a940938..ef1023930fd0 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c @@ -87,7 +87,7 @@ MODULE_DEVICE_TABLE(of, dwmac_generic_match); static struct platform_driver dwmac_generic_driver = { .probe = dwmac_generic_probe, - .remove = stmmac_pltfr_remove, + .remove_new = stmmac_pltfr_remove, .driver = { .name = STMMAC_RESOURCE_NAME, .pm = &stmmac_pltfr_pm_ops, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-imx.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-imx.c index 7c228bd0d099..b9378a63f0e8 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-imx.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-imx.c @@ -376,7 +376,7 @@ MODULE_DEVICE_TABLE(of, imx_dwmac_match); static struct platform_driver imx_dwmac_driver = { .probe = imx_dwmac_probe, - .remove = stmmac_pltfr_remove, + .remove_new = stmmac_pltfr_remove, .driver = { .name = "imx-dwmac", .pm = &stmmac_pltfr_pm_ops, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-ingenic.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-ingenic.c index 378b4dd826bb..8063ba1c3ce8 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-ingenic.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-ingenic.c @@ -386,7 +386,7 @@ MODULE_DEVICE_TABLE(of, ingenic_mac_of_matches); static struct platform_driver ingenic_mac_driver = { .probe = ingenic_mac_probe, - .remove = stmmac_pltfr_remove, + .remove_new = stmmac_pltfr_remove, .driver = { .name = "ingenic-mac", .pm = pm_ptr(&ingenic_mac_pm_ops), diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel-plat.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel-plat.c index 06d287f104be..a5e639ab0b9e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel-plat.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel-plat.c @@ -169,20 +169,17 @@ err_remove_config_dt: return ret; } -static int intel_eth_plat_remove(struct platform_device *pdev) +static void intel_eth_plat_remove(struct platform_device *pdev) { struct intel_dwmac *dwmac = get_stmmac_bsp_priv(&pdev->dev); - int ret; - ret = stmmac_pltfr_remove(pdev); + stmmac_pltfr_remove(pdev); clk_disable_unprepare(dwmac->tx_clk); - - return ret; } static struct platform_driver intel_eth_plat_driver = { .probe = intel_eth_plat_probe, - .remove = intel_eth_plat_remove, + .remove_new = intel_eth_plat_remove, .driver = { .name = "intel-eth-plat", .pm = &stmmac_pltfr_pm_ops, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c index e888c8a9c830..e39406df8516 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c @@ -498,7 +498,7 @@ MODULE_DEVICE_TABLE(of, ipq806x_gmac_dwmac_match); static struct platform_driver ipq806x_gmac_dwmac_driver = { .probe = ipq806x_gmac_probe, - .remove = stmmac_pltfr_remove, + .remove_new = stmmac_pltfr_remove, .driver = { .name = "ipq806x-gmac-dwmac", .pm = &stmmac_pltfr_pm_ops, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-lpc18xx.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-lpc18xx.c index 9d77c647badd..18e84ba693a6 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-lpc18xx.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-lpc18xx.c @@ -83,7 +83,7 @@ MODULE_DEVICE_TABLE(of, lpc18xx_dwmac_match); static struct platform_driver lpc18xx_dwmac_driver = { .probe = lpc18xx_dwmac_probe, - .remove = stmmac_pltfr_remove, + .remove_new = stmmac_pltfr_remove, .driver = { .name = "lpc18xx-dwmac", .pm = &stmmac_pltfr_pm_ops, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c index 9ae31e3dc821..73c1dfa7ecb1 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c @@ -678,15 +678,12 @@ err_remove_config_dt: return ret; } -static int mediatek_dwmac_remove(struct platform_device *pdev) +static void mediatek_dwmac_remove(struct platform_device *pdev) { struct mediatek_dwmac_plat_data *priv_plat = get_stmmac_bsp_priv(&pdev->dev); - int ret; - ret = stmmac_pltfr_remove(pdev); + stmmac_pltfr_remove(pdev); mediatek_dwmac_clks_config(priv_plat, false); - - return ret; } static const struct of_device_id mediatek_dwmac_match[] = { @@ -701,7 +698,7 @@ MODULE_DEVICE_TABLE(of, mediatek_dwmac_match); static struct platform_driver mediatek_dwmac_driver = { .probe = mediatek_dwmac_probe, - .remove = mediatek_dwmac_remove, + .remove_new = mediatek_dwmac_remove, .driver = { .name = "dwmac-mediatek", .pm = &stmmac_pltfr_pm_ops, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson.c index 16fb66a0ca72..7aa5e6bc04eb 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson.c @@ -91,7 +91,7 @@ MODULE_DEVICE_TABLE(of, meson6_dwmac_match); static struct platform_driver meson6_dwmac_driver = { .probe = meson6_dwmac_probe, - .remove = stmmac_pltfr_remove, + .remove_new = stmmac_pltfr_remove, .driver = { .name = "meson6-dwmac", .pm = &stmmac_pltfr_pm_ops, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c index f6754e3643f3..92b16048f91c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c @@ -539,7 +539,7 @@ MODULE_DEVICE_TABLE(of, meson8b_dwmac_match); static struct platform_driver meson8b_dwmac_driver = { .probe = meson8b_dwmac_probe, - .remove = stmmac_pltfr_remove, + .remove_new = stmmac_pltfr_remove, .driver = { .name = "meson8b-dwmac", .pm = &stmmac_pltfr_pm_ops, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-oxnas.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-oxnas.c index 62a69a91ab22..42954020de2c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-oxnas.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-oxnas.c @@ -231,7 +231,7 @@ MODULE_DEVICE_TABLE(of, oxnas_dwmac_match); static struct platform_driver oxnas_dwmac_driver = { .probe = oxnas_dwmac_probe, - .remove = stmmac_pltfr_remove, + .remove_new = stmmac_pltfr_remove, .driver = { .name = "oxnas-dwmac", .pm = &stmmac_pltfr_pm_ops, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c index 16a8c361283b..1db97a5209c4 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c @@ -665,19 +665,12 @@ err_mem: return ret; } -static int qcom_ethqos_remove(struct platform_device *pdev) +static void qcom_ethqos_remove(struct platform_device *pdev) { - struct qcom_ethqos *ethqos; - int ret; - - ethqos = get_stmmac_bsp_priv(&pdev->dev); - if (!ethqos) - return -ENODEV; + struct qcom_ethqos *ethqos = get_stmmac_bsp_priv(&pdev->dev); - ret = stmmac_pltfr_remove(pdev); + stmmac_pltfr_remove(pdev); ethqos_clks_config(ethqos, false); - - return ret; } static const struct of_device_id qcom_ethqos_match[] = { @@ -690,7 +683,7 @@ MODULE_DEVICE_TABLE(of, qcom_ethqos_match); static struct platform_driver qcom_ethqos_driver = { .probe = qcom_ethqos_probe, - .remove = qcom_ethqos_remove, + .remove_new = qcom_ethqos_remove, .driver = { .name = "qcom-ethqos", .pm = &stmmac_pltfr_pm_ops, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c index 4ea31ccf24d0..d81591b470a2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c @@ -1863,15 +1863,13 @@ err_remove_config_dt: return ret; } -static int rk_gmac_remove(struct platform_device *pdev) +static void rk_gmac_remove(struct platform_device *pdev) { struct rk_priv_data *bsp_priv = get_stmmac_bsp_priv(&pdev->dev); stmmac_dvr_remove(&pdev->dev); rk_gmac_powerdown(bsp_priv); - - return 0; } #ifdef CONFIG_PM_SLEEP @@ -1925,7 +1923,7 @@ MODULE_DEVICE_TABLE(of, rk_gmac_dwmac_match); static struct platform_driver rk_gmac_dwmac_driver = { .probe = rk_gmac_probe, - .remove = rk_gmac_remove, + .remove_new = rk_gmac_remove, .driver = { .name = "rk_gmac-dwmac", .pm = &rk_gmac_pm_ops, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c index 6b447d8f0bd8..6ee050300b31 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c @@ -524,7 +524,7 @@ MODULE_DEVICE_TABLE(of, socfpga_dwmac_match); static struct platform_driver socfpga_dwmac_driver = { .probe = socfpga_dwmac_probe, - .remove = stmmac_pltfr_remove, + .remove_new = stmmac_pltfr_remove, .driver = { .name = "socfpga-dwmac", .pm = &socfpga_dwmac_pm_ops, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c index 4f51a7889642..d3a39d2fb3a9 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c @@ -156,7 +156,7 @@ MODULE_DEVICE_TABLE(of, starfive_dwmac_match); static struct platform_driver starfive_dwmac_driver = { .probe = starfive_dwmac_probe, - .remove = stmmac_pltfr_remove, + .remove_new = stmmac_pltfr_remove, .driver = { .name = "starfive-dwmac", .pm = &stmmac_pltfr_pm_ops, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c index 465ce66ef9c1..dcbb17c4f07a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c @@ -317,15 +317,13 @@ err_remove_config_dt: return ret; } -static int sti_dwmac_remove(struct platform_device *pdev) +static void sti_dwmac_remove(struct platform_device *pdev) { struct sti_dwmac *dwmac = get_stmmac_bsp_priv(&pdev->dev); stmmac_dvr_remove(&pdev->dev); clk_disable_unprepare(dwmac->clk); - - return 0; } #ifdef CONFIG_PM_SLEEP @@ -365,7 +363,7 @@ MODULE_DEVICE_TABLE(of, sti_dwmac_match); static struct platform_driver sti_dwmac_driver = { .probe = sti_dwmac_probe, - .remove = sti_dwmac_remove, + .remove_new = sti_dwmac_remove, .driver = { .name = "sti-dwmac", .pm = &sti_dwmac_pm_ops, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c index 0616b3a04ff3..bdb4de59a672 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c @@ -417,7 +417,7 @@ err_remove_config_dt: return ret; } -static int stm32_dwmac_remove(struct platform_device *pdev) +static void stm32_dwmac_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); struct stmmac_priv *priv = netdev_priv(ndev); @@ -431,8 +431,6 @@ static int stm32_dwmac_remove(struct platform_device *pdev) dev_pm_clear_wake_irq(&pdev->dev); device_init_wakeup(&pdev->dev, false); } - - return 0; } static int stm32mp1_suspend(struct stm32_dwmac *dwmac) @@ -528,7 +526,7 @@ MODULE_DEVICE_TABLE(of, stm32_dwmac_match); static struct platform_driver stm32_dwmac_driver = { .probe = stm32_dwmac_probe, - .remove = stm32_dwmac_remove, + .remove_new = stm32_dwmac_remove, .driver = { .name = "stm32-dwmac", .pm = &stm32_dwmac_pm_ops, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c index c2c592ba0eb8..1e714380d125 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c @@ -1294,7 +1294,7 @@ dwmac_deconfig: return ret; } -static int sun8i_dwmac_remove(struct platform_device *pdev) +static void sun8i_dwmac_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); struct stmmac_priv *priv = netdev_priv(ndev); @@ -1309,8 +1309,6 @@ static int sun8i_dwmac_remove(struct platform_device *pdev) stmmac_pltfr_remove(pdev); sun8i_dwmac_unset_syscon(gmac); - - return 0; } static void sun8i_dwmac_shutdown(struct platform_device *pdev) @@ -1341,7 +1339,7 @@ MODULE_DEVICE_TABLE(of, sun8i_dwmac_match); static struct platform_driver sun8i_dwmac_driver = { .probe = sun8i_dwmac_probe, - .remove = sun8i_dwmac_remove, + .remove_new = sun8i_dwmac_remove, .shutdown = sun8i_dwmac_shutdown, .driver = { .name = "dwmac-sun8i", diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c index fc3b0acc8f99..50963e91c347 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c @@ -179,7 +179,7 @@ MODULE_DEVICE_TABLE(of, sun7i_dwmac_match); static struct platform_driver sun7i_dwmac_driver = { .probe = sun7i_gmac_probe, - .remove = stmmac_pltfr_remove, + .remove_new = stmmac_pltfr_remove, .driver = { .name = "sun7i-dwmac", .pm = &stmmac_pltfr_pm_ops, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c index bdf990cf2f31..f8367c5b490b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c @@ -353,15 +353,13 @@ disable_clks: return err; } -static int tegra_mgbe_remove(struct platform_device *pdev) +static void tegra_mgbe_remove(struct platform_device *pdev) { struct tegra_mgbe *mgbe = get_stmmac_bsp_priv(&pdev->dev); clk_bulk_disable_unprepare(ARRAY_SIZE(mgbe_clks), mgbe->clks); stmmac_pltfr_remove(pdev); - - return 0; } static const struct of_device_id tegra_mgbe_match[] = { @@ -374,7 +372,7 @@ static SIMPLE_DEV_PM_OPS(tegra_mgbe_pm_ops, tegra_mgbe_suspend, tegra_mgbe_resum static struct platform_driver tegra_mgbe_driver = { .probe = tegra_mgbe_probe, - .remove = tegra_mgbe_remove, + .remove_new = tegra_mgbe_remove, .driver = { .name = "tegra-mgbe", .pm = &tegra_mgbe_pm_ops, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-visconti.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-visconti.c index c3f10a92b62b..acbb284be174 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-visconti.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-visconti.c @@ -198,7 +198,7 @@ static int visconti_eth_clock_probe(struct platform_device *pdev, return 0; } -static int visconti_eth_clock_remove(struct platform_device *pdev) +static void visconti_eth_clock_remove(struct platform_device *pdev) { struct visconti_eth *dwmac = get_stmmac_bsp_priv(&pdev->dev); struct net_device *ndev = platform_get_drvdata(pdev); @@ -206,8 +206,6 @@ static int visconti_eth_clock_remove(struct platform_device *pdev) clk_disable_unprepare(dwmac->phy_ref_clk); clk_disable_unprepare(priv->plat->stmmac_clk); - - return 0; } static int visconti_eth_dwmac_probe(struct platform_device *pdev) @@ -259,23 +257,16 @@ remove_config: return ret; } -static int visconti_eth_dwmac_remove(struct platform_device *pdev) +static void visconti_eth_dwmac_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); struct stmmac_priv *priv = netdev_priv(ndev); - int err; - err = stmmac_pltfr_remove(pdev); - if (err < 0) - dev_err(&pdev->dev, "failed to remove platform: %d\n", err); + stmmac_pltfr_remove(pdev); - err = visconti_eth_clock_remove(pdev); - if (err < 0) - dev_err(&pdev->dev, "failed to remove clock: %d\n", err); + visconti_eth_clock_remove(pdev); stmmac_remove_config_dt(pdev, priv->plat); - - return err; } static const struct of_device_id visconti_eth_dwmac_match[] = { @@ -286,7 +277,7 @@ MODULE_DEVICE_TABLE(of, visconti_eth_dwmac_match); static struct platform_driver visconti_eth_dwmac_driver = { .probe = visconti_eth_dwmac_probe, - .remove = visconti_eth_dwmac_remove, + .remove_new = visconti_eth_dwmac_remove, .driver = { .name = "visconti-eth-dwmac", .of_match_table = visconti_eth_dwmac_match, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c index dfd53264e036..070bd912580b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c @@ -368,10 +368,12 @@ static int dwxgmac2_dma_interrupt(struct stmmac_priv *priv, if (likely(intr_status & XGMAC_RI)) { x->rx_normal_irq_n++; + x->rxq_stats[chan].rx_normal_irq_n++; ret |= handle_rx; } if (likely(intr_status & (XGMAC_TI | XGMAC_TBU))) { x->tx_normal_irq_n++; + x->txq_stats[chan].tx_normal_irq_n++; ret |= handle_tx; } } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index eb0b2898daa3..3c6b55b60461 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -707,7 +707,7 @@ EXPORT_SYMBOL_GPL(stmmac_get_platform_resources); * Description: this function calls the main to free the net resources * and calls the platforms hook and release the resources (e.g. mem). */ -int stmmac_pltfr_remove(struct platform_device *pdev) +void stmmac_pltfr_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); struct stmmac_priv *priv = netdev_priv(ndev); @@ -719,8 +719,6 @@ int stmmac_pltfr_remove(struct platform_device *pdev) plat->exit(pdev, plat->bsp_priv); stmmac_remove_config_dt(pdev, plat); - - return 0; } EXPORT_SYMBOL_GPL(stmmac_pltfr_remove); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h index 3fff3f59d73d..f7e457946681 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h @@ -19,7 +19,7 @@ void stmmac_remove_config_dt(struct platform_device *pdev, int stmmac_get_platform_resources(struct platform_device *pdev, struct stmmac_resources *stmmac_res); -int stmmac_pltfr_remove(struct platform_device *pdev); +void stmmac_pltfr_remove(struct platform_device *pdev); extern const struct dev_pm_ops stmmac_pltfr_pm_ops; static inline void *get_stmmac_bsp_priv(struct device *dev) diff --git a/drivers/net/ethernet/sun/cassini.c b/drivers/net/ethernet/sun/cassini.c index d61dfa250feb..b317b9486455 100644 --- a/drivers/net/ethernet/sun/cassini.c +++ b/drivers/net/ethernet/sun/cassini.c @@ -1998,10 +1998,8 @@ static int cas_rx_process_pkt(struct cas *cp, struct cas_rx_comp *rxc, skb->truesize += hlen - swivel; skb->len += hlen - swivel; - __skb_frag_set_page(frag, page->buffer); + skb_frag_fill_page_desc(frag, page->buffer, off, hlen - swivel); __skb_frag_ref(frag); - skb_frag_off_set(frag, off); - skb_frag_size_set(frag, hlen - swivel); /* any more data? */ if ((words[0] & RX_COMP1_SPLIT_PKT) && ((dlen -= hlen) > 0)) { @@ -2024,10 +2022,8 @@ static int cas_rx_process_pkt(struct cas *cp, struct cas_rx_comp *rxc, skb->len += hlen; frag++; - __skb_frag_set_page(frag, page->buffer); + skb_frag_fill_page_desc(frag, page->buffer, 0, hlen); __skb_frag_ref(frag); - skb_frag_off_set(frag, 0); - skb_frag_size_set(frag, hlen); RX_USED_ADD(page, hlen + cp->crc_size); } diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h index 32f952d93009..cbe7f184b50e 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_type.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h @@ -598,7 +598,7 @@ struct wx_q_vector { char name[IFNAMSIZ + 17]; /* for dynamic allocation of rings associated with this q_vector */ - struct wx_ring ring[0] ____cacheline_internodealigned_in_smp; + struct wx_ring ring[] ____cacheline_internodealigned_in_smp; }; enum wx_isb_idx { diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c index ab5133eb1d51..a8977965a7f2 100644 --- a/drivers/net/ipvlan/ipvlan_core.c +++ b/drivers/net/ipvlan/ipvlan_core.c @@ -555,8 +555,7 @@ static void ipvlan_multicast_enqueue(struct ipvl_port *port, spin_lock(&port->backlog.lock); if (skb_queue_len(&port->backlog) < IPVLAN_QBACKLOG_LIMIT) { - if (skb->dev) - dev_hold(skb->dev); + dev_hold(skb->dev); __skb_queue_tail(&port->backlog, skb); spin_unlock(&port->backlog.lock); schedule_work(&port->wq); diff --git a/drivers/net/phy/bcm-phy-lib.c b/drivers/net/phy/bcm-phy-lib.c index b2c0baa51f39..27c57f6ab211 100644 --- a/drivers/net/phy/bcm-phy-lib.c +++ b/drivers/net/phy/bcm-phy-lib.c @@ -6,12 +6,14 @@ #include "bcm-phy-lib.h" #include <linux/bitfield.h> #include <linux/brcmphy.h> +#include <linux/etherdevice.h> #include <linux/export.h> #include <linux/mdio.h> #include <linux/module.h> #include <linux/phy.h> #include <linux/ethtool.h> #include <linux/ethtool_netlink.h> +#include <linux/netdevice.h> #define MII_BCM_CHANNEL_WIDTH 0x2000 #define BCM_CL45VEN_EEE_ADV 0x3c @@ -816,6 +818,216 @@ int bcm_phy_cable_test_get_status_rdb(struct phy_device *phydev, } EXPORT_SYMBOL_GPL(bcm_phy_cable_test_get_status_rdb); +#define BCM54XX_WOL_SUPPORTED_MASK (WAKE_UCAST | \ + WAKE_MCAST | \ + WAKE_BCAST | \ + WAKE_MAGIC | \ + WAKE_MAGICSECURE) + +int bcm_phy_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) +{ + struct net_device *ndev = phydev->attached_dev; + u8 da[ETH_ALEN], mask[ETH_ALEN]; + unsigned int i; + u16 ctl; + int ret; + + /* Allow a MAC driver to play through its own Wake-on-LAN + * implementation + */ + if (wol->wolopts & ~BCM54XX_WOL_SUPPORTED_MASK) + return -EOPNOTSUPP; + + /* The PHY supports passwords of 4, 6 and 8 bytes in size, but Linux's + * ethtool only supports 6, for now. + */ + BUILD_BUG_ON(sizeof(wol->sopass) != ETH_ALEN); + + /* Clear previous interrupts */ + ret = bcm_phy_read_exp(phydev, BCM54XX_WOL_INT_STATUS); + if (ret < 0) + return ret; + + ret = bcm_phy_read_exp(phydev, BCM54XX_WOL_MAIN_CTL); + if (ret < 0) + return ret; + + ctl = ret; + + if (!wol->wolopts) { + if (phy_interrupt_is_valid(phydev)) + disable_irq_wake(phydev->irq); + + /* Leave all interrupts disabled */ + ret = bcm_phy_write_exp(phydev, BCM54XX_WOL_INT_MASK, + BCM54XX_WOL_ALL_INTRS); + if (ret < 0) + return ret; + + /* Disable the global Wake-on-LAN enable bit */ + ctl &= ~BCM54XX_WOL_EN; + + return bcm_phy_write_exp(phydev, BCM54XX_WOL_MAIN_CTL, ctl); + } + + /* Clear the previously configured mode and mask mode for Wake-on-LAN */ + ctl &= ~(BCM54XX_WOL_MODE_MASK << BCM54XX_WOL_MODE_SHIFT); + ctl &= ~(BCM54XX_WOL_MASK_MODE_MASK << BCM54XX_WOL_MASK_MODE_SHIFT); + ctl &= ~BCM54XX_WOL_DIR_PKT_EN; + ctl &= ~(BCM54XX_WOL_SECKEY_OPT_MASK << BCM54XX_WOL_SECKEY_OPT_SHIFT); + + /* When using WAKE_MAGIC, we program the magic pattern filter to match + * the device's MAC address and we accept any MAC DA in the Ethernet + * frame. + * + * When using WAKE_UCAST, WAKE_BCAST or WAKE_MCAST, we program the + * following: + * - WAKE_UCAST -> MAC DA is the device's MAC with a perfect match + * - WAKE_MCAST -> MAC DA is X1:XX:XX:XX:XX:XX where XX is don't care + * - WAKE_BCAST -> MAC DA is FF:FF:FF:FF:FF:FF with a perfect match + * + * Note that the Broadcast MAC DA is inherently going to match the + * multicast pattern being matched. + */ + memset(mask, 0, sizeof(mask)); + + if (wol->wolopts & WAKE_MCAST) { + memset(da, 0, sizeof(da)); + memset(mask, 0xff, sizeof(mask)); + da[0] = 0x01; + mask[0] = ~da[0]; + } else { + if (wol->wolopts & WAKE_UCAST) { + ether_addr_copy(da, ndev->dev_addr); + } else if (wol->wolopts & WAKE_BCAST) { + eth_broadcast_addr(da); + } else if (wol->wolopts & WAKE_MAGICSECURE) { + ether_addr_copy(da, wol->sopass); + } else if (wol->wolopts & WAKE_MAGIC) { + memset(da, 0, sizeof(da)); + memset(mask, 0xff, sizeof(mask)); + } + } + + for (i = 0; i < ETH_ALEN / 2; i++) { + if (wol->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) { + ret = bcm_phy_write_exp(phydev, + BCM54XX_WOL_MPD_DATA1(2 - i), + ndev->dev_addr[i * 2] << 8 | + ndev->dev_addr[i * 2 + 1]); + if (ret < 0) + return ret; + } + + ret = bcm_phy_write_exp(phydev, BCM54XX_WOL_MPD_DATA2(2 - i), + da[i * 2] << 8 | da[i * 2 + 1]); + if (ret < 0) + return ret; + + ret = bcm_phy_write_exp(phydev, BCM54XX_WOL_MASK(2 - i), + mask[i * 2] << 8 | mask[i * 2 + 1]); + if (ret) + return ret; + } + + if (wol->wolopts & WAKE_MAGICSECURE) { + ctl |= BCM54XX_WOL_SECKEY_OPT_6B << + BCM54XX_WOL_SECKEY_OPT_SHIFT; + ctl |= BCM54XX_WOL_MODE_SINGLE_MPDSEC << BCM54XX_WOL_MODE_SHIFT; + ctl |= BCM54XX_WOL_MASK_MODE_DA_FF << + BCM54XX_WOL_MASK_MODE_SHIFT; + } else { + if (wol->wolopts & WAKE_MAGIC) + ctl |= BCM54XX_WOL_MODE_SINGLE_MPD; + else + ctl |= BCM54XX_WOL_DIR_PKT_EN; + ctl |= BCM54XX_WOL_MASK_MODE_DA_ONLY << + BCM54XX_WOL_MASK_MODE_SHIFT; + } + + /* Globally enable Wake-on-LAN */ + ctl |= BCM54XX_WOL_EN | BCM54XX_WOL_CRC_CHK; + + ret = bcm_phy_write_exp(phydev, BCM54XX_WOL_MAIN_CTL, ctl); + if (ret < 0) + return ret; + + /* Enable WOL interrupt on LED4 */ + ret = bcm_phy_read_exp(phydev, BCM54XX_TOP_MISC_LED_CTL); + if (ret < 0) + return ret; + + ret |= BCM54XX_LED4_SEL_INTR; + ret = bcm_phy_write_exp(phydev, BCM54XX_TOP_MISC_LED_CTL, ret); + if (ret < 0) + return ret; + + /* Enable all Wake-on-LAN interrupt sources */ + ret = bcm_phy_write_exp(phydev, BCM54XX_WOL_INT_MASK, 0); + if (ret < 0) + return ret; + + if (phy_interrupt_is_valid(phydev)) + enable_irq_wake(phydev->irq); + + return 0; +} +EXPORT_SYMBOL_GPL(bcm_phy_set_wol); + +void bcm_phy_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) +{ + struct net_device *ndev = phydev->attached_dev; + u8 da[ETH_ALEN]; + unsigned int i; + int ret; + u16 ctl; + + wol->supported = BCM54XX_WOL_SUPPORTED_MASK; + wol->wolopts = 0; + + ret = bcm_phy_read_exp(phydev, BCM54XX_WOL_MAIN_CTL); + if (ret < 0) + return; + + ctl = ret; + + if (!(ctl & BCM54XX_WOL_EN)) + return; + + for (i = 0; i < sizeof(da) / 2; i++) { + ret = bcm_phy_read_exp(phydev, + BCM54XX_WOL_MPD_DATA2(2 - i)); + if (ret < 0) + return; + + da[i * 2] = ret >> 8; + da[i * 2 + 1] = ret & 0xff; + } + + if (ctl & BCM54XX_WOL_DIR_PKT_EN) { + if (is_broadcast_ether_addr(da)) + wol->wolopts |= WAKE_BCAST; + else if (is_multicast_ether_addr(da)) + wol->wolopts |= WAKE_MCAST; + else if (ether_addr_equal(da, ndev->dev_addr)) + wol->wolopts |= WAKE_UCAST; + } else { + ctl = (ctl >> BCM54XX_WOL_MODE_SHIFT) & BCM54XX_WOL_MODE_MASK; + switch (ctl) { + case BCM54XX_WOL_MODE_SINGLE_MPD: + wol->wolopts |= WAKE_MAGIC; + break; + case BCM54XX_WOL_MODE_SINGLE_MPDSEC: + wol->wolopts |= WAKE_MAGICSECURE; + memcpy(wol->sopass, da, sizeof(da)); + break; + default: + break; + } + } +} +EXPORT_SYMBOL_GPL(bcm_phy_get_wol); + MODULE_DESCRIPTION("Broadcom PHY Library"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Broadcom Corporation"); diff --git a/drivers/net/phy/bcm-phy-lib.h b/drivers/net/phy/bcm-phy-lib.h index 729db441797a..c6fed43ec913 100644 --- a/drivers/net/phy/bcm-phy-lib.h +++ b/drivers/net/phy/bcm-phy-lib.h @@ -9,6 +9,8 @@ #include <linux/brcmphy.h> #include <linux/phy.h> +struct ethtool_wolinfo; + /* 28nm only register definitions */ #define MISC_ADDR(base, channel) base, channel @@ -111,4 +113,7 @@ static inline void bcm_ptp_stop(struct bcm_ptp_private *priv) } #endif +int bcm_phy_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol); +void bcm_phy_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol); + #endif /* _LINUX_BCM_PHY_LIB_H */ diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c index ad71c88c87e7..418e6bc0e998 100644 --- a/drivers/net/phy/broadcom.c +++ b/drivers/net/phy/broadcom.c @@ -14,8 +14,12 @@ #include <linux/delay.h> #include <linux/module.h> #include <linux/phy.h> +#include <linux/pm_wakeup.h> #include <linux/brcmphy.h> #include <linux/of.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/gpio/consumer.h> #define BRCM_PHY_MODEL(phydev) \ ((phydev)->drv->phy_id & (phydev)->drv->phy_id_mask) @@ -30,8 +34,17 @@ MODULE_LICENSE("GPL"); struct bcm54xx_phy_priv { u64 *stats; struct bcm_ptp_private *ptp; + int wake_irq; + bool wake_irq_enabled; }; +static bool bcm54xx_phy_can_wakeup(struct phy_device *phydev) +{ + struct bcm54xx_phy_priv *priv = phydev->priv; + + return phy_interrupt_is_valid(phydev) || priv->wake_irq >= 0; +} + static int bcm54xx_config_clock_delay(struct phy_device *phydev) { int rc, val; @@ -413,6 +426,16 @@ static int bcm54xx_config_init(struct phy_device *phydev) bcm54xx_ptp_config_init(phydev); + /* Acknowledge any left over interrupt and charge the device for + * wake-up. + */ + err = bcm_phy_read_exp(phydev, BCM54XX_WOL_INT_STATUS); + if (err < 0) + return err; + + if (err) + pm_wakeup_event(&phydev->mdio.dev, 0); + return 0; } @@ -437,12 +460,39 @@ out: return ret; } +static int bcm54xx_set_wakeup_irq(struct phy_device *phydev, bool state) +{ + struct bcm54xx_phy_priv *priv = phydev->priv; + int ret = 0; + + if (!bcm54xx_phy_can_wakeup(phydev)) + return ret; + + if (priv->wake_irq_enabled != state) { + if (state) + ret = enable_irq_wake(priv->wake_irq); + else + ret = disable_irq_wake(priv->wake_irq); + priv->wake_irq_enabled = state; + } + + return ret; +} + static int bcm54xx_suspend(struct phy_device *phydev) { - int ret; + int ret = 0; bcm54xx_ptp_stop(phydev); + /* Acknowledge any Wake-on-LAN interrupt prior to suspend */ + ret = bcm_phy_read_exp(phydev, BCM54XX_WOL_INT_STATUS); + if (ret < 0) + return ret; + + if (phydev->wol_enabled) + return bcm54xx_set_wakeup_irq(phydev, true); + /* We cannot use a read/modify/write here otherwise the PHY gets into * a bad state where its LEDs keep flashing, thus defeating the purpose * of low power mode. @@ -456,7 +506,13 @@ static int bcm54xx_suspend(struct phy_device *phydev) static int bcm54xx_resume(struct phy_device *phydev) { - int ret; + int ret = 0; + + if (phydev->wol_enabled) { + ret = bcm54xx_set_wakeup_irq(phydev, false); + if (ret) + return ret; + } ret = bcm54xx_iddq_set(phydev, false); if (ret < 0) @@ -801,14 +857,54 @@ static int brcm_fet_suspend(struct phy_device *phydev) return err; } +static void bcm54xx_phy_get_wol(struct phy_device *phydev, + struct ethtool_wolinfo *wol) +{ + /* We cannot wake-up if we do not have a dedicated PHY interrupt line + * or an out of band GPIO descriptor for wake-up. Zeroing + * wol->supported allows the caller (MAC driver) to play through and + * offer its own Wake-on-LAN scheme if available. + */ + if (!bcm54xx_phy_can_wakeup(phydev)) { + wol->supported = 0; + return; + } + + bcm_phy_get_wol(phydev, wol); +} + +static int bcm54xx_phy_set_wol(struct phy_device *phydev, + struct ethtool_wolinfo *wol) +{ + int ret; + + /* We cannot wake-up if we do not have a dedicated PHY interrupt line + * or an out of band GPIO descriptor for wake-up. Returning -EOPNOTSUPP + * allows the caller (MAC driver) to play through and offer its own + * Wake-on-LAN scheme if available. + */ + if (!bcm54xx_phy_can_wakeup(phydev)) + return -EOPNOTSUPP; + + ret = bcm_phy_set_wol(phydev, wol); + if (ret < 0) + return ret; + + return 0; +} + static int bcm54xx_phy_probe(struct phy_device *phydev) { struct bcm54xx_phy_priv *priv; + struct gpio_desc *wakeup_gpio; + int ret = 0; priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; + priv->wake_irq = -ENXIO; + phydev->priv = priv; priv->stats = devm_kcalloc(&phydev->mdio.dev, @@ -821,7 +917,28 @@ static int bcm54xx_phy_probe(struct phy_device *phydev) if (IS_ERR(priv->ptp)) return PTR_ERR(priv->ptp); - return 0; + /* We cannot utilize the _optional variant here since we want to know + * whether the GPIO descriptor exists or not to advertise Wake-on-LAN + * support or not. + */ + wakeup_gpio = devm_gpiod_get(&phydev->mdio.dev, "wakeup", GPIOD_IN); + if (PTR_ERR(wakeup_gpio) == -EPROBE_DEFER) + return PTR_ERR(wakeup_gpio); + + if (!IS_ERR(wakeup_gpio)) { + priv->wake_irq = gpiod_to_irq(wakeup_gpio); + ret = irq_set_irq_type(priv->wake_irq, IRQ_TYPE_LEVEL_LOW); + if (ret) + return ret; + } + + /* If we do not have a main interrupt or a side-band wake-up interrupt, + * then the device cannot be marked as wake-up capable. + */ + if (!bcm54xx_phy_can_wakeup(phydev)) + return 0; + + return device_init_wakeup(&phydev->mdio.dev, true); } static void bcm54xx_get_stats(struct phy_device *phydev, @@ -894,6 +1011,7 @@ static struct phy_driver broadcom_drivers[] = { .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM54210E", /* PHY_GBIT_FEATURES */ + .flags = PHY_ALWAYS_CALL_SUSPEND, .get_sset_count = bcm_phy_get_sset_count, .get_strings = bcm_phy_get_strings, .get_stats = bcm54xx_get_stats, @@ -904,6 +1022,8 @@ static struct phy_driver broadcom_drivers[] = { .link_change_notify = bcm54xx_link_change_notify, .suspend = bcm54xx_suspend, .resume = bcm54xx_resume, + .get_wol = bcm54xx_phy_get_wol, + .set_wol = bcm54xx_phy_set_wol, }, { .phy_id = PHY_ID_BCM5461, .phy_id_mask = 0xfffffff0, diff --git a/drivers/net/phy/dp83869.c b/drivers/net/phy/dp83869.c index 9ab5eff502b7..fa8c6fdcf301 100644 --- a/drivers/net/phy/dp83869.c +++ b/drivers/net/phy/dp83869.c @@ -692,8 +692,19 @@ static int dp83869_configure_mode(struct phy_device *phydev, /* Below init sequence for each operational mode is defined in * section 9.4.8 of the datasheet. */ + phy_ctrl_val = dp83869->mode; + if (phydev->interface == PHY_INTERFACE_MODE_MII) { + if (dp83869->mode == DP83869_100M_MEDIA_CONVERT || + dp83869->mode == DP83869_RGMII_100_BASE) { + phy_ctrl_val |= DP83869_OP_MODE_MII; + } else { + phydev_err(phydev, "selected op-mode is not valid with MII mode\n"); + return -EINVAL; + } + } + ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_OP_MODE, - dp83869->mode); + phy_ctrl_val); if (ret) return ret; diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 17d0d0555a79..8852b0c53114 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -1860,9 +1860,10 @@ int phy_suspend(struct phy_device *phydev) if (phydev->suspended) return 0; - /* If the device has WOL enabled, we cannot suspend the PHY */ phy_ethtool_get_wol(phydev, &wol); - if (wol.wolopts || (netdev && netdev->wol_enabled)) + phydev->wol_enabled = wol.wolopts || (netdev && netdev->wol_enabled); + /* If the device has WOL enabled, we cannot suspend the PHY */ + if (phydev->wol_enabled && !(phydrv->flags & PHY_ALWAYS_CALL_SUSPEND)) return -EBUSY; if (!phydrv || !phydrv->suspend) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index e237949deee6..dc9a740b1ff7 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -708,7 +708,7 @@ static int phylink_validate(struct phylink *pl, unsigned long *supported, } static int phylink_parse_fixedlink(struct phylink *pl, - struct fwnode_handle *fwnode) + const struct fwnode_handle *fwnode) { struct fwnode_handle *fixed_node; bool pause, asym_pause, autoneg; @@ -819,7 +819,8 @@ static int phylink_parse_fixedlink(struct phylink *pl, return 0; } -static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode) +static int phylink_parse_mode(struct phylink *pl, + const struct fwnode_handle *fwnode) { struct fwnode_handle *dn; const char *managed; @@ -1441,7 +1442,7 @@ static void phylink_fixed_poll(struct timer_list *t) static const struct sfp_upstream_ops sfp_phylink_ops; static int phylink_register_sfp(struct phylink *pl, - struct fwnode_handle *fwnode) + const struct fwnode_handle *fwnode) { struct sfp_bus *bus; int ret; @@ -1480,7 +1481,7 @@ static int phylink_register_sfp(struct phylink *pl, * must use IS_ERR() to check for errors from this function. */ struct phylink *phylink_create(struct phylink_config *config, - struct fwnode_handle *fwnode, + const struct fwnode_handle *fwnode, phy_interface_t iface, const struct phylink_mac_ops *mac_ops) { @@ -1809,7 +1810,7 @@ EXPORT_SYMBOL_GPL(phylink_of_phy_connect); * Returns 0 on success or a negative errno. */ int phylink_fwnode_phy_connect(struct phylink *pl, - struct fwnode_handle *fwnode, + const struct fwnode_handle *fwnode, u32 flags) { struct fwnode_handle *phy_fwnode; diff --git a/drivers/net/ppp/Kconfig b/drivers/net/ppp/Kconfig index ac4d162d9455..2fbcae31fc02 100644 --- a/drivers/net/ppp/Kconfig +++ b/drivers/net/ppp/Kconfig @@ -129,6 +129,40 @@ config PPPOE which contains instruction on how to use this driver (under the heading "Kernel mode PPPoE"). +choice + prompt "Number of PPPoE hash bits" + default PPPOE_HASH_BITS_4 + depends on PPPOE + help + Select the number of bits used for hashing PPPoE interfaces. + + Larger sizes reduces the risk of hash collisions at the cost + of slightly increased memory usage. + + This hash table is on a per outer ethernet interface. + +config PPPOE_HASH_BITS_2 + bool "1 bit (2 buckets)" + +config PPPOE_HASH_BITS_2 + bool "2 bits (4 buckets)" + +config PPPOE_HASH_BITS_4 + bool "4 bits (16 buckets)" + +config PPPOE_HASH_BITS_8 + bool "8 bits (256 buckets)" + +endchoice + +config PPPOE_HASH_BITS + int + default 1 if PPPOE_HASH_BITS_1 + default 2 if PPPOE_HASH_BITS_2 + default 4 if PPPOE_HASH_BITS_4 + default 8 if PPPOE_HASH_BITS_8 + default 4 + config PPTP tristate "PPP over IPv4 (PPTP)" depends on PPP && NET_IPGRE_DEMUX diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c index ce2cbb5903d7..3b79c603b936 100644 --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c @@ -80,7 +80,7 @@ #include <linux/uaccess.h> -#define PPPOE_HASH_BITS 4 +#define PPPOE_HASH_BITS CONFIG_PPPOE_HASH_BITS #define PPPOE_HASH_SIZE (1 << PPPOE_HASH_BITS) #define PPPOE_HASH_MASK (PPPOE_HASH_SIZE - 1) diff --git a/drivers/net/veth.c b/drivers/net/veth.c index dce9f9d63e04..614f3e3efab0 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -176,12 +176,27 @@ static int veth_get_sset_count(struct net_device *dev, int sset) } } +static void veth_get_page_pool_stats(struct net_device *dev, u64 *data) +{ +#ifdef CONFIG_PAGE_POOL_STATS + struct veth_priv *priv = netdev_priv(dev); + struct page_pool_stats pp_stats = {}; + int i; + + for (i = 0; i < dev->real_num_rx_queues; i++) { + if (!priv->rq[i].page_pool) + continue; + page_pool_get_stats(priv->rq[i].page_pool, &pp_stats); + } + page_pool_ethtool_stats_get(data, &pp_stats); +#endif /* CONFIG_PAGE_POOL_STATS */ +} + static void veth_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) { struct veth_priv *rcv_priv, *priv = netdev_priv(dev); struct net_device *peer = rtnl_dereference(priv->peer); - struct page_pool_stats pp_stats = {}; int i, j, idx, pp_idx; data[0] = peer ? peer->ifindex : 0; @@ -225,12 +240,7 @@ static void veth_get_ethtool_stats(struct net_device *dev, } page_pool_stats: - for (i = 0; i < dev->real_num_rx_queues; i++) { - if (!priv->rq[i].page_pool) - continue; - page_pool_get_stats(priv->rq[i].page_pool, &pp_stats); - } - page_pool_ethtool_stats_get(&data[pp_idx], &pp_stats); + veth_get_page_pool_stats(dev, &data[pp_idx]); } static void veth_get_channels(struct net_device *dev, @@ -747,7 +757,7 @@ static int veth_convert_skb_to_xdp_buff(struct veth_rq *rq, if (!page) goto drop; - nskb = build_skb(page_address(page), PAGE_SIZE); + nskb = napi_build_skb(page_address(page), PAGE_SIZE); if (!nskb) { page_pool_put_full_page(rq->page_pool, page, true); goto drop; diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 56ca1d270304..5a7f7a76b920 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -443,6 +443,22 @@ static unsigned int mergeable_ctx_to_truesize(void *mrg_ctx) return (unsigned long)mrg_ctx & ((1 << MRG_CTX_HEADER_SHIFT) - 1); } +static struct sk_buff *virtnet_build_skb(void *buf, unsigned int buflen, + unsigned int headroom, + unsigned int len) +{ + struct sk_buff *skb; + + skb = build_skb(buf, buflen); + if (unlikely(!skb)) + return NULL; + + skb_reserve(skb, headroom); + skb_put(skb, len); + + return skb; +} + /* Called from bottom half context */ static struct sk_buff *page_to_skb(struct virtnet_info *vi, struct receive_queue *rq, @@ -476,13 +492,10 @@ static struct sk_buff *page_to_skb(struct virtnet_info *vi, /* copy small packet so we can reuse these pages */ if (!NET_IP_ALIGN && len > GOOD_COPY_LEN && tailroom >= shinfo_size) { - skb = build_skb(buf, truesize); + skb = virtnet_build_skb(buf, truesize, p - buf, len); if (unlikely(!skb)) return NULL; - skb_reserve(skb, p - buf); - skb_put(skb, len); - page = (struct page *)page->private; if (page) give_pages(rq, page); @@ -789,6 +802,75 @@ out: return ret; } +static void put_xdp_frags(struct xdp_buff *xdp) +{ + struct skb_shared_info *shinfo; + struct page *xdp_page; + int i; + + if (xdp_buff_has_frags(xdp)) { + shinfo = xdp_get_shared_info_from_buff(xdp); + for (i = 0; i < shinfo->nr_frags; i++) { + xdp_page = skb_frag_page(&shinfo->frags[i]); + put_page(xdp_page); + } + } +} + +static int virtnet_xdp_handler(struct bpf_prog *xdp_prog, struct xdp_buff *xdp, + struct net_device *dev, + unsigned int *xdp_xmit, + struct virtnet_rq_stats *stats) +{ + struct xdp_frame *xdpf; + int err; + u32 act; + + act = bpf_prog_run_xdp(xdp_prog, xdp); + stats->xdp_packets++; + + switch (act) { + case XDP_PASS: + return act; + + case XDP_TX: + stats->xdp_tx++; + xdpf = xdp_convert_buff_to_frame(xdp); + if (unlikely(!xdpf)) { + netdev_dbg(dev, "convert buff to frame failed for xdp\n"); + return XDP_DROP; + } + + err = virtnet_xdp_xmit(dev, 1, &xdpf, 0); + if (unlikely(!err)) { + xdp_return_frame_rx_napi(xdpf); + } else if (unlikely(err < 0)) { + trace_xdp_exception(dev, xdp_prog, act); + return XDP_DROP; + } + *xdp_xmit |= VIRTIO_XDP_TX; + return act; + + case XDP_REDIRECT: + stats->xdp_redirects++; + err = xdp_do_redirect(dev, xdp, xdp_prog); + if (err) + return XDP_DROP; + + *xdp_xmit |= VIRTIO_XDP_REDIR; + return act; + + default: + bpf_warn_invalid_xdp_action(dev, xdp_prog, act); + fallthrough; + case XDP_ABORTED: + trace_xdp_exception(dev, xdp_prog, act); + fallthrough; + case XDP_DROP: + return XDP_DROP; + } +} + static unsigned int virtnet_get_headroom(struct virtnet_info *vi) { return vi->xdp_enabled ? VIRTIO_XDP_HEADROOM : 0; @@ -862,134 +944,103 @@ err_buf: return NULL; } -static struct sk_buff *receive_small(struct net_device *dev, - struct virtnet_info *vi, - struct receive_queue *rq, - void *buf, void *ctx, - unsigned int len, - unsigned int *xdp_xmit, - struct virtnet_rq_stats *stats) +static struct sk_buff *receive_small_build_skb(struct virtnet_info *vi, + unsigned int xdp_headroom, + void *buf, + unsigned int len) { + unsigned int header_offset; + unsigned int headroom; + unsigned int buflen; struct sk_buff *skb; - struct bpf_prog *xdp_prog; - unsigned int xdp_headroom = (unsigned long)ctx; + + header_offset = VIRTNET_RX_PAD + xdp_headroom; + headroom = vi->hdr_len + header_offset; + buflen = SKB_DATA_ALIGN(GOOD_PACKET_LEN + headroom) + + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + + skb = virtnet_build_skb(buf, buflen, headroom, len); + if (unlikely(!skb)) + return NULL; + + buf += header_offset; + memcpy(skb_vnet_hdr(skb), buf, vi->hdr_len); + + return skb; +} + +static struct sk_buff *receive_small_xdp(struct net_device *dev, + struct virtnet_info *vi, + struct receive_queue *rq, + struct bpf_prog *xdp_prog, + void *buf, + unsigned int xdp_headroom, + unsigned int len, + unsigned int *xdp_xmit, + struct virtnet_rq_stats *stats) +{ unsigned int header_offset = VIRTNET_RX_PAD + xdp_headroom; unsigned int headroom = vi->hdr_len + header_offset; - unsigned int buflen = SKB_DATA_ALIGN(GOOD_PACKET_LEN + headroom) + - SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + struct virtio_net_hdr_mrg_rxbuf *hdr = buf + header_offset; struct page *page = virt_to_head_page(buf); - unsigned int delta = 0; struct page *xdp_page; - int err; + unsigned int buflen; + struct xdp_buff xdp; + struct sk_buff *skb; unsigned int metasize = 0; + u32 act; - len -= vi->hdr_len; - stats->bytes += len; + if (unlikely(hdr->hdr.gso_type)) + goto err_xdp; - if (unlikely(len > GOOD_PACKET_LEN)) { - pr_debug("%s: rx error: len %u exceeds max size %d\n", - dev->name, len, GOOD_PACKET_LEN); - dev->stats.rx_length_errors++; - goto err; - } + buflen = SKB_DATA_ALIGN(GOOD_PACKET_LEN + headroom) + + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + + if (unlikely(xdp_headroom < virtnet_get_headroom(vi))) { + int offset = buf - page_address(page) + header_offset; + unsigned int tlen = len + vi->hdr_len; + int num_buf = 1; + + xdp_headroom = virtnet_get_headroom(vi); + header_offset = VIRTNET_RX_PAD + xdp_headroom; + headroom = vi->hdr_len + header_offset; + buflen = SKB_DATA_ALIGN(GOOD_PACKET_LEN + headroom) + + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + xdp_page = xdp_linearize_page(rq, &num_buf, page, + offset, header_offset, + &tlen); + if (!xdp_page) + goto err_xdp; - if (likely(!vi->xdp_enabled)) { - xdp_prog = NULL; - goto skip_xdp; + buf = page_address(xdp_page); + put_page(page); + page = xdp_page; } - rcu_read_lock(); - xdp_prog = rcu_dereference(rq->xdp_prog); - if (xdp_prog) { - struct virtio_net_hdr_mrg_rxbuf *hdr = buf + header_offset; - struct xdp_frame *xdpf; - struct xdp_buff xdp; - void *orig_data; - u32 act; + xdp_init_buff(&xdp, buflen, &rq->xdp_rxq); + xdp_prepare_buff(&xdp, buf + VIRTNET_RX_PAD + vi->hdr_len, + xdp_headroom, len, true); - if (unlikely(hdr->hdr.gso_type)) - goto err_xdp; + act = virtnet_xdp_handler(xdp_prog, &xdp, dev, xdp_xmit, stats); - if (unlikely(xdp_headroom < virtnet_get_headroom(vi))) { - int offset = buf - page_address(page) + header_offset; - unsigned int tlen = len + vi->hdr_len; - int num_buf = 1; - - xdp_headroom = virtnet_get_headroom(vi); - header_offset = VIRTNET_RX_PAD + xdp_headroom; - headroom = vi->hdr_len + header_offset; - buflen = SKB_DATA_ALIGN(GOOD_PACKET_LEN + headroom) + - SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); - xdp_page = xdp_linearize_page(rq, &num_buf, page, - offset, header_offset, - &tlen); - if (!xdp_page) - goto err_xdp; - - buf = page_address(xdp_page); - put_page(page); - page = xdp_page; - } + switch (act) { + case XDP_PASS: + /* Recalculate length in case bpf program changed it */ + len = xdp.data_end - xdp.data; + metasize = xdp.data - xdp.data_meta; + break; - xdp_init_buff(&xdp, buflen, &rq->xdp_rxq); - xdp_prepare_buff(&xdp, buf + VIRTNET_RX_PAD + vi->hdr_len, - xdp_headroom, len, true); - orig_data = xdp.data; - act = bpf_prog_run_xdp(xdp_prog, &xdp); - stats->xdp_packets++; - - switch (act) { - case XDP_PASS: - /* Recalculate length in case bpf program changed it */ - delta = orig_data - xdp.data; - len = xdp.data_end - xdp.data; - metasize = xdp.data - xdp.data_meta; - break; - case XDP_TX: - stats->xdp_tx++; - xdpf = xdp_convert_buff_to_frame(&xdp); - if (unlikely(!xdpf)) - goto err_xdp; - err = virtnet_xdp_xmit(dev, 1, &xdpf, 0); - if (unlikely(!err)) { - xdp_return_frame_rx_napi(xdpf); - } else if (unlikely(err < 0)) { - trace_xdp_exception(vi->dev, xdp_prog, act); - goto err_xdp; - } - *xdp_xmit |= VIRTIO_XDP_TX; - rcu_read_unlock(); - goto xdp_xmit; - case XDP_REDIRECT: - stats->xdp_redirects++; - err = xdp_do_redirect(dev, &xdp, xdp_prog); - if (err) - goto err_xdp; - *xdp_xmit |= VIRTIO_XDP_REDIR; - rcu_read_unlock(); - goto xdp_xmit; - default: - bpf_warn_invalid_xdp_action(vi->dev, xdp_prog, act); - fallthrough; - case XDP_ABORTED: - trace_xdp_exception(vi->dev, xdp_prog, act); - goto err_xdp; - case XDP_DROP: - goto err_xdp; - } + case XDP_TX: + case XDP_REDIRECT: + goto xdp_xmit; + + default: + goto err_xdp; } - rcu_read_unlock(); -skip_xdp: - skb = build_skb(buf, buflen); - if (!skb) + skb = virtnet_build_skb(buf, buflen, xdp.data - buf, len); + if (unlikely(!skb)) goto err; - skb_reserve(skb, headroom - delta); - skb_put(skb, len); - if (!xdp_prog) { - buf += header_offset; - memcpy(skb_vnet_hdr(skb), buf, vi->hdr_len); - } /* keep zeroed vnet hdr since XDP is loaded */ if (metasize) skb_metadata_set(skb, metasize); @@ -997,7 +1048,6 @@ skip_xdp: return skb; err_xdp: - rcu_read_unlock(); stats->xdp_drops++; err: stats->drops++; @@ -1006,6 +1056,53 @@ xdp_xmit: return NULL; } +static struct sk_buff *receive_small(struct net_device *dev, + struct virtnet_info *vi, + struct receive_queue *rq, + void *buf, void *ctx, + unsigned int len, + unsigned int *xdp_xmit, + struct virtnet_rq_stats *stats) +{ + unsigned int xdp_headroom = (unsigned long)ctx; + struct page *page = virt_to_head_page(buf); + struct sk_buff *skb; + + len -= vi->hdr_len; + stats->bytes += len; + + if (unlikely(len > GOOD_PACKET_LEN)) { + pr_debug("%s: rx error: len %u exceeds max size %d\n", + dev->name, len, GOOD_PACKET_LEN); + dev->stats.rx_length_errors++; + goto err; + } + + if (unlikely(vi->xdp_enabled)) { + struct bpf_prog *xdp_prog; + + rcu_read_lock(); + xdp_prog = rcu_dereference(rq->xdp_prog); + if (xdp_prog) { + skb = receive_small_xdp(dev, vi, rq, xdp_prog, buf, + xdp_headroom, len, xdp_xmit, + stats); + rcu_read_unlock(); + return skb; + } + rcu_read_unlock(); + } + + skb = receive_small_build_skb(vi, xdp_headroom, buf, len); + if (likely(skb)) + return skb; + +err: + stats->drops++; + put_page(page); + return NULL; +} + static struct sk_buff *receive_big(struct net_device *dev, struct virtnet_info *vi, struct receive_queue *rq, @@ -1029,6 +1126,28 @@ err: return NULL; } +static void mergeable_buf_free(struct receive_queue *rq, int num_buf, + struct net_device *dev, + struct virtnet_rq_stats *stats) +{ + struct page *page; + void *buf; + int len; + + while (num_buf-- > 1) { + buf = virtqueue_get_buf(rq->vq, &len); + if (unlikely(!buf)) { + pr_debug("%s: rx error: %d buffers missing\n", + dev->name, num_buf); + dev->stats.rx_length_errors++; + break; + } + stats->bytes += len; + page = virt_to_head_page(buf); + put_page(page); + } +} + /* Why not use xdp_build_skb_from_frame() ? * XDP core assumes that xdp frags are PAGE_SIZE in length, while in * virtio-net there are 2 points that do not match its requirements: @@ -1130,7 +1249,7 @@ static int virtnet_build_xdp_buff_mrg(struct net_device *dev, dev->name, *num_buf, virtio16_to_cpu(vi->vdev, hdr->num_buffers)); dev->stats.rx_length_errors++; - return -EINVAL; + goto err; } stats->bytes += len; @@ -1149,13 +1268,11 @@ static int virtnet_build_xdp_buff_mrg(struct net_device *dev, pr_debug("%s: rx error: len %u exceeds truesize %lu\n", dev->name, len, (unsigned long)(truesize - room)); dev->stats.rx_length_errors++; - return -EINVAL; + goto err; } frag = &shinfo->frags[shinfo->nr_frags++]; - __skb_frag_set_page(frag, page); - skb_frag_off_set(frag, offset); - skb_frag_size_set(frag, len); + skb_frag_fill_page_desc(frag, page, offset, len); if (page_is_pfmemalloc(page)) xdp_buff_set_frag_pfmemalloc(xdp); @@ -1164,6 +1281,144 @@ static int virtnet_build_xdp_buff_mrg(struct net_device *dev, *xdp_frags_truesize = xdp_frags_truesz; return 0; + +err: + put_xdp_frags(xdp); + return -EINVAL; +} + +static void *mergeable_xdp_get_buf(struct virtnet_info *vi, + struct receive_queue *rq, + struct bpf_prog *xdp_prog, + void *ctx, + unsigned int *frame_sz, + int *num_buf, + struct page **page, + int offset, + unsigned int *len, + struct virtio_net_hdr_mrg_rxbuf *hdr) +{ + unsigned int truesize = mergeable_ctx_to_truesize(ctx); + unsigned int headroom = mergeable_ctx_to_headroom(ctx); + struct page *xdp_page; + unsigned int xdp_room; + + /* Transient failure which in theory could occur if + * in-flight packets from before XDP was enabled reach + * the receive path after XDP is loaded. + */ + if (unlikely(hdr->hdr.gso_type)) + return NULL; + + /* Now XDP core assumes frag size is PAGE_SIZE, but buffers + * with headroom may add hole in truesize, which + * make their length exceed PAGE_SIZE. So we disabled the + * hole mechanism for xdp. See add_recvbuf_mergeable(). + */ + *frame_sz = truesize; + + if (likely(headroom >= virtnet_get_headroom(vi) && + (*num_buf == 1 || xdp_prog->aux->xdp_has_frags))) { + return page_address(*page) + offset; + } + + /* This happens when headroom is not enough because + * of the buffer was prefilled before XDP is set. + * This should only happen for the first several packets. + * In fact, vq reset can be used here to help us clean up + * the prefilled buffers, but many existing devices do not + * support it, and we don't want to bother users who are + * using xdp normally. + */ + if (!xdp_prog->aux->xdp_has_frags) { + /* linearize data for XDP */ + xdp_page = xdp_linearize_page(rq, num_buf, + *page, offset, + VIRTIO_XDP_HEADROOM, + len); + if (!xdp_page) + return NULL; + } else { + xdp_room = SKB_DATA_ALIGN(VIRTIO_XDP_HEADROOM + + sizeof(struct skb_shared_info)); + if (*len + xdp_room > PAGE_SIZE) + return NULL; + + xdp_page = alloc_page(GFP_ATOMIC); + if (!xdp_page) + return NULL; + + memcpy(page_address(xdp_page) + VIRTIO_XDP_HEADROOM, + page_address(*page) + offset, *len); + } + + *frame_sz = PAGE_SIZE; + + put_page(*page); + + *page = xdp_page; + + return page_address(*page) + VIRTIO_XDP_HEADROOM; +} + +static struct sk_buff *receive_mergeable_xdp(struct net_device *dev, + struct virtnet_info *vi, + struct receive_queue *rq, + struct bpf_prog *xdp_prog, + void *buf, + void *ctx, + unsigned int len, + unsigned int *xdp_xmit, + struct virtnet_rq_stats *stats) +{ + struct virtio_net_hdr_mrg_rxbuf *hdr = buf; + int num_buf = virtio16_to_cpu(vi->vdev, hdr->num_buffers); + struct page *page = virt_to_head_page(buf); + int offset = buf - page_address(page); + unsigned int xdp_frags_truesz = 0; + struct sk_buff *head_skb; + unsigned int frame_sz; + struct xdp_buff xdp; + void *data; + u32 act; + int err; + + data = mergeable_xdp_get_buf(vi, rq, xdp_prog, ctx, &frame_sz, &num_buf, &page, + offset, &len, hdr); + if (unlikely(!data)) + goto err_xdp; + + err = virtnet_build_xdp_buff_mrg(dev, vi, rq, &xdp, data, len, frame_sz, + &num_buf, &xdp_frags_truesz, stats); + if (unlikely(err)) + goto err_xdp; + + act = virtnet_xdp_handler(xdp_prog, &xdp, dev, xdp_xmit, stats); + + switch (act) { + case XDP_PASS: + head_skb = build_skb_from_xdp_buff(dev, vi, &xdp, xdp_frags_truesz); + if (unlikely(!head_skb)) + break; + return head_skb; + + case XDP_TX: + case XDP_REDIRECT: + return NULL; + + default: + break; + } + + put_xdp_frags(&xdp); + +err_xdp: + put_page(page); + mergeable_buf_free(rq, num_buf, dev, stats); + + stats->xdp_drops++; + stats->drops++; + return NULL; } static struct sk_buff *receive_mergeable(struct net_device *dev, @@ -1180,13 +1435,10 @@ static struct sk_buff *receive_mergeable(struct net_device *dev, struct page *page = virt_to_head_page(buf); int offset = buf - page_address(page); struct sk_buff *head_skb, *curr_skb; - struct bpf_prog *xdp_prog; unsigned int truesize = mergeable_ctx_to_truesize(ctx); unsigned int headroom = mergeable_ctx_to_headroom(ctx); unsigned int tailroom = headroom ? sizeof(struct skb_shared_info) : 0; unsigned int room = SKB_DATA_ALIGN(headroom + tailroom); - unsigned int frame_sz, xdp_room; - int err; head_skb = NULL; stats->bytes += len - vi->hdr_len; @@ -1198,149 +1450,20 @@ static struct sk_buff *receive_mergeable(struct net_device *dev, goto err_skb; } - if (likely(!vi->xdp_enabled)) { - xdp_prog = NULL; - goto skip_xdp; - } - - rcu_read_lock(); - xdp_prog = rcu_dereference(rq->xdp_prog); - if (xdp_prog) { - unsigned int xdp_frags_truesz = 0; - struct skb_shared_info *shinfo; - struct xdp_frame *xdpf; - struct page *xdp_page; - struct xdp_buff xdp; - void *data; - u32 act; - int i; - - /* Transient failure which in theory could occur if - * in-flight packets from before XDP was enabled reach - * the receive path after XDP is loaded. - */ - if (unlikely(hdr->hdr.gso_type)) - goto err_xdp; - - /* Now XDP core assumes frag size is PAGE_SIZE, but buffers - * with headroom may add hole in truesize, which - * make their length exceed PAGE_SIZE. So we disabled the - * hole mechanism for xdp. See add_recvbuf_mergeable(). - */ - frame_sz = truesize; - - /* This happens when headroom is not enough because - * of the buffer was prefilled before XDP is set. - * This should only happen for the first several packets. - * In fact, vq reset can be used here to help us clean up - * the prefilled buffers, but many existing devices do not - * support it, and we don't want to bother users who are - * using xdp normally. - */ - if (!xdp_prog->aux->xdp_has_frags && - (num_buf > 1 || headroom < virtnet_get_headroom(vi))) { - /* linearize data for XDP */ - xdp_page = xdp_linearize_page(rq, &num_buf, - page, offset, - VIRTIO_XDP_HEADROOM, - &len); - frame_sz = PAGE_SIZE; - - if (!xdp_page) - goto err_xdp; - offset = VIRTIO_XDP_HEADROOM; - } else if (unlikely(headroom < virtnet_get_headroom(vi))) { - xdp_room = SKB_DATA_ALIGN(VIRTIO_XDP_HEADROOM + - sizeof(struct skb_shared_info)); - if (len + xdp_room > PAGE_SIZE) - goto err_xdp; - - xdp_page = alloc_page(GFP_ATOMIC); - if (!xdp_page) - goto err_xdp; - - memcpy(page_address(xdp_page) + VIRTIO_XDP_HEADROOM, - page_address(page) + offset, len); - frame_sz = PAGE_SIZE; - offset = VIRTIO_XDP_HEADROOM; - } else { - xdp_page = page; - } - - data = page_address(xdp_page) + offset; - err = virtnet_build_xdp_buff_mrg(dev, vi, rq, &xdp, data, len, frame_sz, - &num_buf, &xdp_frags_truesz, stats); - if (unlikely(err)) - goto err_xdp_frags; + if (unlikely(vi->xdp_enabled)) { + struct bpf_prog *xdp_prog; - act = bpf_prog_run_xdp(xdp_prog, &xdp); - stats->xdp_packets++; - - switch (act) { - case XDP_PASS: - head_skb = build_skb_from_xdp_buff(dev, vi, &xdp, xdp_frags_truesz); - if (unlikely(!head_skb)) - goto err_xdp_frags; - - if (unlikely(xdp_page != page)) - put_page(page); + rcu_read_lock(); + xdp_prog = rcu_dereference(rq->xdp_prog); + if (xdp_prog) { + head_skb = receive_mergeable_xdp(dev, vi, rq, xdp_prog, buf, ctx, + len, xdp_xmit, stats); rcu_read_unlock(); return head_skb; - case XDP_TX: - stats->xdp_tx++; - xdpf = xdp_convert_buff_to_frame(&xdp); - if (unlikely(!xdpf)) { - netdev_dbg(dev, "convert buff to frame failed for xdp\n"); - goto err_xdp_frags; - } - err = virtnet_xdp_xmit(dev, 1, &xdpf, 0); - if (unlikely(!err)) { - xdp_return_frame_rx_napi(xdpf); - } else if (unlikely(err < 0)) { - trace_xdp_exception(vi->dev, xdp_prog, act); - goto err_xdp_frags; - } - *xdp_xmit |= VIRTIO_XDP_TX; - if (unlikely(xdp_page != page)) - put_page(page); - rcu_read_unlock(); - goto xdp_xmit; - case XDP_REDIRECT: - stats->xdp_redirects++; - err = xdp_do_redirect(dev, &xdp, xdp_prog); - if (err) - goto err_xdp_frags; - *xdp_xmit |= VIRTIO_XDP_REDIR; - if (unlikely(xdp_page != page)) - put_page(page); - rcu_read_unlock(); - goto xdp_xmit; - default: - bpf_warn_invalid_xdp_action(vi->dev, xdp_prog, act); - fallthrough; - case XDP_ABORTED: - trace_xdp_exception(vi->dev, xdp_prog, act); - fallthrough; - case XDP_DROP: - goto err_xdp_frags; - } -err_xdp_frags: - if (unlikely(xdp_page != page)) - __free_pages(xdp_page, 0); - - if (xdp_buff_has_frags(&xdp)) { - shinfo = xdp_get_shared_info_from_buff(&xdp); - for (i = 0; i < shinfo->nr_frags; i++) { - xdp_page = skb_frag_page(&shinfo->frags[i]); - put_page(xdp_page); - } } - - goto err_xdp; + rcu_read_unlock(); } - rcu_read_unlock(); -skip_xdp: head_skb = page_to_skb(vi, rq, page, offset, len, truesize, headroom); curr_skb = head_skb; @@ -1406,27 +1529,13 @@ skip_xdp: ewma_pkt_len_add(&rq->mrg_avg_pkt_len, head_skb->len); return head_skb; -err_xdp: - rcu_read_unlock(); - stats->xdp_drops++; err_skb: put_page(page); - while (num_buf-- > 1) { - buf = virtqueue_get_buf(rq->vq, &len); - if (unlikely(!buf)) { - pr_debug("%s: rx error: %d buffers missing\n", - dev->name, num_buf); - dev->stats.rx_length_errors++; - break; - } - stats->bytes += len; - page = virt_to_head_page(buf); - put_page(page); - } + mergeable_buf_free(rq, num_buf, dev, stats); + err_buf: stats->drops++; dev_kfree_skb(head_skb); -xdp_xmit: return NULL; } diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c index f2b76ee866a4..7fa74b8b2100 100644 --- a/drivers/net/vmxnet3/vmxnet3_drv.c +++ b/drivers/net/vmxnet3/vmxnet3_drv.c @@ -686,9 +686,7 @@ vmxnet3_append_frag(struct sk_buff *skb, struct Vmxnet3_RxCompDesc *rcd, BUG_ON(skb_shinfo(skb)->nr_frags >= MAX_SKB_FRAGS); - __skb_frag_set_page(frag, rbi->page); - skb_frag_off_set(frag, 0); - skb_frag_size_set(frag, rcd->len); + skb_frag_fill_page_desc(frag, rbi->page, 0, rcd->len); skb->data_len += rcd->len; skb->truesize += PAGE_SIZE; skb_shinfo(skb)->nr_frags++; diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index 561fe1b314f5..78744549c1b3 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -2352,7 +2352,8 @@ static int encap_bypass_if_local(struct sk_buff *skb, struct net_device *dev, #endif /* Bypass encapsulation if the destination is local */ if (rt_flags & RTCF_LOCAL && - !(rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))) { + !(rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) && + vxlan->cfg.flags & VXLAN_F_LOCALBYPASS) { struct vxlan_dev *dst_vxlan; dst_release(dst); @@ -3172,6 +3173,7 @@ static void vxlan_raw_setup(struct net_device *dev) } static const struct nla_policy vxlan_policy[IFLA_VXLAN_MAX + 1] = { + [IFLA_VXLAN_UNSPEC] = { .strict_start_type = IFLA_VXLAN_LOCALBYPASS }, [IFLA_VXLAN_ID] = { .type = NLA_U32 }, [IFLA_VXLAN_GROUP] = { .len = sizeof_field(struct iphdr, daddr) }, [IFLA_VXLAN_GROUP6] = { .len = sizeof(struct in6_addr) }, @@ -3202,6 +3204,7 @@ static const struct nla_policy vxlan_policy[IFLA_VXLAN_MAX + 1] = { [IFLA_VXLAN_TTL_INHERIT] = { .type = NLA_FLAG }, [IFLA_VXLAN_DF] = { .type = NLA_U8 }, [IFLA_VXLAN_VNIFILTER] = { .type = NLA_U8 }, + [IFLA_VXLAN_LOCALBYPASS] = NLA_POLICY_MAX(NLA_U8, 1), }; static int vxlan_validate(struct nlattr *tb[], struct nlattr *data[], @@ -4011,6 +4014,17 @@ static int vxlan_nl2conf(struct nlattr *tb[], struct nlattr *data[], conf->flags |= VXLAN_F_UDP_ZERO_CSUM_TX; } + if (data[IFLA_VXLAN_LOCALBYPASS]) { + err = vxlan_nl2flag(conf, data, IFLA_VXLAN_LOCALBYPASS, + VXLAN_F_LOCALBYPASS, changelink, + true, extack); + if (err) + return err; + } else if (!changelink) { + /* default to local bypass on a new device */ + conf->flags |= VXLAN_F_LOCALBYPASS; + } + if (data[IFLA_VXLAN_UDP_ZERO_CSUM6_TX]) { err = vxlan_nl2flag(conf, data, IFLA_VXLAN_UDP_ZERO_CSUM6_TX, VXLAN_F_UDP_ZERO_CSUM6_TX, changelink, @@ -4232,6 +4246,7 @@ static size_t vxlan_get_size(const struct net_device *dev) nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_UDP_ZERO_CSUM6_RX */ nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_REMCSUM_TX */ nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_REMCSUM_RX */ + nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_LOCALBYPASS */ 0; } @@ -4308,7 +4323,9 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev) nla_put_u8(skb, IFLA_VXLAN_REMCSUM_TX, !!(vxlan->cfg.flags & VXLAN_F_REMCSUM_TX)) || nla_put_u8(skb, IFLA_VXLAN_REMCSUM_RX, - !!(vxlan->cfg.flags & VXLAN_F_REMCSUM_RX))) + !!(vxlan->cfg.flags & VXLAN_F_REMCSUM_RX)) || + nla_put_u8(skb, IFLA_VXLAN_LOCALBYPASS, + !!(vxlan->cfg.flags & VXLAN_F_LOCALBYPASS))) goto nla_put_failure; if (nla_put(skb, IFLA_VXLAN_PORT_RANGE, sizeof(ports), &ports)) diff --git a/drivers/net/wireless/marvell/mwifiex/11n.h b/drivers/net/wireless/marvell/mwifiex/11n.h index 94b5e3e4ba08..7738ebe1fec1 100644 --- a/drivers/net/wireless/marvell/mwifiex/11n.h +++ b/drivers/net/wireless/marvell/mwifiex/11n.h @@ -102,14 +102,14 @@ static inline u8 mwifiex_space_avail_for_new_ba_stream( { struct mwifiex_private *priv; u8 i; - u32 ba_stream_num = 0, ba_stream_max; + size_t ba_stream_num = 0, ba_stream_max; ba_stream_max = MWIFIEX_MAX_TX_BASTREAM_SUPPORTED; for (i = 0; i < adapter->priv_num; i++) { priv = adapter->priv[i]; if (priv) - ba_stream_num += mwifiex_wmm_list_len( + ba_stream_num += list_count_nodes( &priv->tx_ba_stream_tbl_ptr); } diff --git a/drivers/net/wireless/marvell/mwifiex/scan.c b/drivers/net/wireless/marvell/mwifiex/scan.c index ac8001c84293..644b1e134b01 100644 --- a/drivers/net/wireless/marvell/mwifiex/scan.c +++ b/drivers/net/wireless/marvell/mwifiex/scan.c @@ -2187,9 +2187,9 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, if (nd_config) { adapter->nd_info = - kzalloc(sizeof(struct cfg80211_wowlan_nd_match) + - sizeof(struct cfg80211_wowlan_nd_match *) * - scan_rsp->number_of_sets, GFP_ATOMIC); + kzalloc(struct_size(adapter->nd_info, matches, + scan_rsp->number_of_sets), + GFP_ATOMIC); if (adapter->nd_info) adapter->nd_info->n_matches = scan_rsp->number_of_sets; diff --git a/drivers/net/wireless/marvell/mwifiex/wmm.h b/drivers/net/wireless/marvell/mwifiex/wmm.h index 4f53a271dae0..d7659e688933 100644 --- a/drivers/net/wireless/marvell/mwifiex/wmm.h +++ b/drivers/net/wireless/marvell/mwifiex/wmm.h @@ -39,21 +39,6 @@ mwifiex_get_tid(struct mwifiex_ra_list_tbl *ptr) } /* - * This function gets the length of a list. - */ -static inline int -mwifiex_wmm_list_len(struct list_head *head) -{ - struct list_head *pos; - int count = 0; - - list_for_each(pos, head) - ++count; - - return count; -} - -/* * This function checks if a RA list is empty or not. */ static inline u8 diff --git a/drivers/net/wireless/mediatek/mt7601u/debugfs.c b/drivers/net/wireless/mediatek/mt7601u/debugfs.c index 230b0e1061a7..dbddf256921b 100644 --- a/drivers/net/wireless/mediatek/mt7601u/debugfs.c +++ b/drivers/net/wireless/mediatek/mt7601u/debugfs.c @@ -127,8 +127,6 @@ void mt7601u_init_debugfs(struct mt7601u_dev *dev) struct dentry *dir; dir = debugfs_create_dir("mt7601u", dev->hw->wiphy->debugfsdir); - if (!dir) - return; debugfs_create_u8("temperature", 0400, dir, &dev->raw_temp); debugfs_create_u32("temp_mode", 0400, dir, &dev->temp_mode); diff --git a/drivers/net/wireless/microchip/wilc1000/hif.c b/drivers/net/wireless/microchip/wilc1000/hif.c index 5adc69d5bcae..a28da5938481 100644 --- a/drivers/net/wireless/microchip/wilc1000/hif.c +++ b/drivers/net/wireless/microchip/wilc1000/hif.c @@ -485,6 +485,9 @@ void *wilc_parse_join_bss_param(struct cfg80211_bss *bss, int rsn_ie_len = sizeof(struct element) + rsn_ie[1]; int offset = 8; + param->mode_802_11i = 2; + param->rsn_found = true; + /* extract RSN capabilities */ if (offset < rsn_ie_len) { /* skip over pairwise suites */ @@ -494,11 +497,8 @@ void *wilc_parse_join_bss_param(struct cfg80211_bss *bss, /* skip over authentication suites */ offset += (rsn_ie[offset] * 4) + 2; - if (offset + 1 < rsn_ie_len) { - param->mode_802_11i = 2; - param->rsn_found = true; + if (offset + 1 < rsn_ie_len) memcpy(param->rsn_cap, &rsn_ie[offset], 2); - } } } } diff --git a/drivers/net/wireless/microchip/wilc1000/hif.h b/drivers/net/wireless/microchip/wilc1000/hif.h index baa2881f4465..8e386db72e45 100644 --- a/drivers/net/wireless/microchip/wilc1000/hif.h +++ b/drivers/net/wireless/microchip/wilc1000/hif.h @@ -30,8 +30,6 @@ enum { WILC_GET_CFG }; -#define WILC_MAX_ASSOC_RESP_FRAME_SIZE 256 - struct rf_info { u8 link_speed; s8 rssi; diff --git a/drivers/net/wireless/microchip/wilc1000/wlan_cfg.h b/drivers/net/wireless/microchip/wilc1000/wlan_cfg.h index 614c5673f232..7038b74f8e8f 100644 --- a/drivers/net/wireless/microchip/wilc1000/wlan_cfg.h +++ b/drivers/net/wireless/microchip/wilc1000/wlan_cfg.h @@ -30,7 +30,7 @@ struct wilc_cfg_str { struct wilc_cfg_str_vals { u8 mac_address[7]; u8 firmware_version[129]; - u8 assoc_rsp[256]; + u8 assoc_rsp[WILC_MAX_ASSOC_RESP_FRAME_SIZE]; }; struct wilc_cfg { diff --git a/drivers/net/wireless/microchip/wilc1000/wlan_if.h b/drivers/net/wireless/microchip/wilc1000/wlan_if.h index df2f5a63bdf6..254a046e3b1b 100644 --- a/drivers/net/wireless/microchip/wilc1000/wlan_if.h +++ b/drivers/net/wireless/microchip/wilc1000/wlan_if.h @@ -10,6 +10,8 @@ #include <linux/netdevice.h> #include "fw.h" +#define WILC_MAX_ASSOC_RESP_FRAME_SIZE 512 + /******************************************** * * Wlan Configuration ID diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h index 808c1c895113..376b4b72eb80 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h @@ -1280,6 +1280,9 @@ struct rtl8xxxu_rfregs { #define H2C_JOIN_BSS_DISCONNECT 0 #define H2C_JOIN_BSS_CONNECT 1 +#define H2C_MACID_ROLE_STA 1 +#define H2C_MACID_ROLE_AP 2 + /* * H2C (firmware) commands differ between the older generation chips * 8188[cr]u, 819[12]cu, and 8723au, and the more recent chips 8723bu, @@ -1727,6 +1730,8 @@ struct rtl8xxxu_cfo_tracking { }; #define RTL8XXXU_HW_LED_CONTROL 2 +#define RTL8XXXU_MAX_MAC_ID_NUM 128 +#define RTL8XXXU_BC_MC_MACID 0 struct rtl8xxxu_priv { struct ieee80211_hw *hw; @@ -1851,6 +1856,7 @@ struct rtl8xxxu_priv { struct delayed_work ra_watchdog; struct work_struct c2hcmd_work; struct sk_buff_head c2hcmd_queue; + struct work_struct update_beacon_work; struct rtl8xxxu_btcoex bt_coex; struct rtl8xxxu_ra_report ra_report; struct rtl8xxxu_cfo_tracking cfo_tracking; @@ -1859,6 +1865,14 @@ struct rtl8xxxu_priv { bool led_registered; char led_name[32]; struct led_classdev led_cdev; + DECLARE_BITMAP(mac_id_map, RTL8XXXU_MAX_MAC_ID_NUM); +}; + +struct rtl8xxxu_sta_info { + struct ieee80211_sta *sta; + struct ieee80211_vif *vif; + + u8 macid; }; struct rtl8xxxu_rx_urb { @@ -1903,15 +1917,16 @@ struct rtl8xxxu_fileops { void (*set_tx_power) (struct rtl8xxxu_priv *priv, int channel, bool ht40); void (*update_rate_mask) (struct rtl8xxxu_priv *priv, - u32 ramask, u8 rateid, int sgi, int txbw_40mhz); + u32 ramask, u8 rateid, int sgi, int txbw_40mhz, + u8 macid); void (*report_connect) (struct rtl8xxxu_priv *priv, - u8 macid, bool connect); + u8 macid, u8 role, bool connect); void (*report_rssi) (struct rtl8xxxu_priv *priv, u8 macid, u8 rssi); void (*fill_txdesc) (struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, struct ieee80211_tx_info *tx_info, struct rtl8xxxu_txdesc32 *tx_desc, bool sgi, bool short_preamble, bool ampdu_enable, - u32 rts_rate); + u32 rts_rate, u8 macid); void (*set_crystal_cap) (struct rtl8xxxu_priv *priv, u8 crystal_cap); s8 (*cck_rssi) (struct rtl8xxxu_priv *priv, struct rtl8723au_phy_stats *phy_stats); int (*led_classdev_brightness_set) (struct led_classdev *led_cdev, @@ -1929,6 +1944,8 @@ struct rtl8xxxu_fileops { u8 init_reg_hmtfr:1; u8 ampdu_max_time; u8 ustime_tsf_edca; + u8 supports_ap:1; + u16 max_macid_num; u32 adda_1t_init; u32 adda_1t_path_on; u32 adda_2t_path_on_a; @@ -2022,13 +2039,13 @@ void rtl8xxxu_gen2_config_channel(struct ieee80211_hw *hw); void rtl8xxxu_gen1_usb_quirks(struct rtl8xxxu_priv *priv); void rtl8xxxu_gen2_usb_quirks(struct rtl8xxxu_priv *priv); void rtl8xxxu_update_rate_mask(struct rtl8xxxu_priv *priv, - u32 ramask, u8 rateid, int sgi, int txbw_40mhz); + u32 ramask, u8 rateid, int sgi, int txbw_40mhz, u8 macid); void rtl8xxxu_gen2_update_rate_mask(struct rtl8xxxu_priv *priv, - u32 ramask, u8 rateid, int sgi, int txbw_40mhz); + u32 ramask, u8 rateid, int sgi, int txbw_40mhz, u8 macid); void rtl8xxxu_gen1_report_connect(struct rtl8xxxu_priv *priv, - u8 macid, bool connect); + u8 macid, u8 role, bool connect); void rtl8xxxu_gen2_report_connect(struct rtl8xxxu_priv *priv, - u8 macid, bool connect); + u8 macid, u8 role, bool connect); void rtl8xxxu_gen1_report_rssi(struct rtl8xxxu_priv *priv, u8 macid, u8 rssi); void rtl8xxxu_gen2_report_rssi(struct rtl8xxxu_priv *priv, u8 macid, u8 rssi); void rtl8xxxu_gen1_init_aggregation(struct rtl8xxxu_priv *priv); @@ -2057,17 +2074,17 @@ void rtl8xxxu_fill_txdesc_v1(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, struct ieee80211_tx_info *tx_info, struct rtl8xxxu_txdesc32 *tx_desc, bool sgi, bool short_preamble, bool ampdu_enable, - u32 rts_rate); + u32 rts_rate, u8 macid); void rtl8xxxu_fill_txdesc_v2(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, struct ieee80211_tx_info *tx_info, struct rtl8xxxu_txdesc32 *tx_desc32, bool sgi, bool short_preamble, bool ampdu_enable, - u32 rts_rate); + u32 rts_rate, u8 macid); void rtl8xxxu_fill_txdesc_v3(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, struct ieee80211_tx_info *tx_info, struct rtl8xxxu_txdesc32 *tx_desc32, bool sgi, bool short_preamble, bool ampdu_enable, - u32 rts_rate); + u32 rts_rate, u8 macid); void rtl8723bu_set_ps_tdma(struct rtl8xxxu_priv *priv, u8 arg1, u8 arg2, u8 arg3, u8 arg4, u8 arg5); void rtl8723bu_phy_init_antenna_selection(struct rtl8xxxu_priv *priv); diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188e.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188e.c index 8986783ae8fa..6d0f975f891b 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188e.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188e.c @@ -1794,7 +1794,8 @@ static void rtl8188e_arfb_refresh(struct rtl8xxxu_ra_info *ra) static void rtl8188e_update_rate_mask(struct rtl8xxxu_priv *priv, - u32 ramask, u8 rateid, int sgi, int txbw_40mhz) + u32 ramask, u8 rateid, int sgi, int txbw_40mhz, + u8 macid) { struct rtl8xxxu_ra_info *ra = &priv->ra_info; diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c index dbdfd7787465..71b7f0d31bf4 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8188f.c @@ -1748,6 +1748,8 @@ struct rtl8xxxu_fileops rtl8188fu_fops = { .init_reg_hmtfr = 1, .ampdu_max_time = 0x70, .ustime_tsf_edca = 0x28, + .supports_ap = 1, + .max_macid_num = 16, .adda_1t_init = 0x03c00014, .adda_1t_path_on = 0x03c00014, .trxff_boundary = 0x3f7f, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index 831639d73657..1eb0d5642623 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -1185,6 +1185,20 @@ static void rtl8xxxu_stop_tx_beacon(struct rtl8xxxu_priv *priv) rtl8xxxu_write8(priv, REG_TBTT_PROHIBIT + 2, val8); } +static void rtl8xxxu_start_tx_beacon(struct rtl8xxxu_priv *priv) +{ + u8 val8; + + val8 = rtl8xxxu_read8(priv, REG_FWHW_TXQ_CTRL + 2); + val8 |= EN_BCNQ_DL >> 16; + rtl8xxxu_write8(priv, REG_FWHW_TXQ_CTRL + 2, val8); + + rtl8xxxu_write8(priv, REG_TBTT_PROHIBIT + 1, 0x80); + val8 = rtl8xxxu_read8(priv, REG_TBTT_PROHIBIT + 2); + val8 &= 0xF0; + rtl8xxxu_write8(priv, REG_TBTT_PROHIBIT + 2, val8); +} + /* * The rtl8723a has 3 channel groups for it's efuse settings. It only @@ -3963,6 +3977,34 @@ void rtl8xxxu_init_burst(struct rtl8xxxu_priv *priv) rtl8xxxu_write8(priv, REG_RSV_CTRL, val8); } +static u8 rtl8xxxu_acquire_macid(struct rtl8xxxu_priv *priv) +{ + u8 macid; + + macid = find_first_zero_bit(priv->mac_id_map, RTL8XXXU_MAX_MAC_ID_NUM); + if (macid < RTL8XXXU_MAX_MAC_ID_NUM) + set_bit(macid, priv->mac_id_map); + + return macid; +} + +static void rtl8xxxu_release_macid(struct rtl8xxxu_priv *priv, u8 macid) +{ + clear_bit(macid, priv->mac_id_map); +} + +static inline u8 rtl8xxxu_get_macid(struct rtl8xxxu_priv *priv, + struct ieee80211_sta *sta) +{ + struct rtl8xxxu_sta_info *sta_info; + + if (!priv->vif || priv->vif->type == NL80211_IFTYPE_STATION || !sta) + return 0; + + sta_info = (struct rtl8xxxu_sta_info *)sta->drv_priv; + return sta_info->macid; +} + static int rtl8xxxu_init_device(struct ieee80211_hw *hw) { struct rtl8xxxu_priv *priv = hw->priv; @@ -4433,6 +4475,8 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw) if (priv->rtl_chip == RTL8188E) rtl8188e_ra_info_init_all(&priv->ra_info); + set_bit(RTL8XXXU_BC_MC_MACID, priv->mac_id_map); + exit: return ret; } @@ -4490,6 +4534,16 @@ int rtl8xxxu_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant) return 0; } +static int rtl8xxxu_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, + bool set) +{ + struct rtl8xxxu_priv *priv = hw->priv; + + schedule_work(&priv->update_beacon_work); + + return 0; +} + static void rtl8xxxu_sw_scan_start(struct ieee80211_hw *hw, struct ieee80211_vif *vif, const u8 *mac) { @@ -4513,7 +4567,8 @@ static void rtl8xxxu_sw_scan_complete(struct ieee80211_hw *hw, } void rtl8xxxu_update_rate_mask(struct rtl8xxxu_priv *priv, - u32 ramask, u8 rateid, int sgi, int txbw_40mhz) + u32 ramask, u8 rateid, int sgi, int txbw_40mhz, + u8 macid) { struct h2c_cmd h2c; @@ -4533,7 +4588,8 @@ void rtl8xxxu_update_rate_mask(struct rtl8xxxu_priv *priv, } void rtl8xxxu_gen2_update_rate_mask(struct rtl8xxxu_priv *priv, - u32 ramask, u8 rateid, int sgi, int txbw_40mhz) + u32 ramask, u8 rateid, int sgi, int txbw_40mhz, + u8 macid) { struct h2c_cmd h2c; u8 bw; @@ -4550,6 +4606,7 @@ void rtl8xxxu_gen2_update_rate_mask(struct rtl8xxxu_priv *priv, h2c.b_macid_cfg.ramask1 = (ramask >> 8) & 0xff; h2c.b_macid_cfg.ramask2 = (ramask >> 16) & 0xff; h2c.b_macid_cfg.ramask3 = (ramask >> 24) & 0xff; + h2c.b_macid_cfg.macid = macid; h2c.b_macid_cfg.data1 = rateid; if (sgi) @@ -4563,7 +4620,7 @@ void rtl8xxxu_gen2_update_rate_mask(struct rtl8xxxu_priv *priv, } void rtl8xxxu_gen1_report_connect(struct rtl8xxxu_priv *priv, - u8 macid, bool connect) + u8 macid, u8 role, bool connect) { struct h2c_cmd h2c; @@ -4580,7 +4637,7 @@ void rtl8xxxu_gen1_report_connect(struct rtl8xxxu_priv *priv, } void rtl8xxxu_gen2_report_connect(struct rtl8xxxu_priv *priv, - u8 macid, bool connect) + u8 macid, u8 role, bool connect) { /* * The firmware turns on the rate control when it knows it's @@ -4596,6 +4653,9 @@ void rtl8xxxu_gen2_report_connect(struct rtl8xxxu_priv *priv, else h2c.media_status_rpt.parm &= ~BIT(0); + h2c.media_status_rpt.parm |= ((role << 4) & 0xf0); + h2c.media_status_rpt.macid = macid; + rtl8xxxu_gen2_h2c_cmd(priv, &h2c, sizeof(h2c.media_status_rpt)); } @@ -4912,7 +4972,8 @@ rtl8xxxu_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, priv->vif = vif; priv->rssi_level = RTL8XXXU_RATR_STA_INIT; - priv->fops->update_rate_mask(priv, ramask, 0, sgi, bw == RATE_INFO_BW_40); + priv->fops->update_rate_mask(priv, ramask, 0, sgi, + bw == RATE_INFO_BW_40, 0); rtl8xxxu_write8(priv, REG_BCN_MAX_ERR, 0xff); @@ -4922,13 +4983,13 @@ rtl8xxxu_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, rtl8xxxu_write16(priv, REG_BCN_PSR_RPT, 0xc000 | vif->cfg.aid); - priv->fops->report_connect(priv, 0, true); + priv->fops->report_connect(priv, 0, H2C_MACID_ROLE_AP, true); } else { val8 = rtl8xxxu_read8(priv, REG_BEACON_CTRL); val8 |= BEACON_DISABLE_TSF_UPDATE; rtl8xxxu_write8(priv, REG_BEACON_CTRL, val8); - priv->fops->report_connect(priv, 0, false); + priv->fops->report_connect(priv, 0, H2C_MACID_ROLE_AP, false); } } @@ -4965,10 +5026,35 @@ rtl8xxxu_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, dev_dbg(dev, "Changed BASIC_RATES!\n"); rtl8xxxu_set_basic_rates(priv, bss_conf->basic_rates); } + + if (changed & BSS_CHANGED_BEACON_ENABLED) { + if (bss_conf->enable_beacon) + rtl8xxxu_start_tx_beacon(priv); + else + rtl8xxxu_stop_tx_beacon(priv); + } + + if (changed & BSS_CHANGED_BEACON) + schedule_work(&priv->update_beacon_work); + error: return; } +static int rtl8xxxu_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf) +{ + struct rtl8xxxu_priv *priv = hw->priv; + struct device *dev = &priv->udev->dev; + + dev_dbg(dev, "Start AP mode\n"); + rtl8xxxu_set_bssid(priv, vif->bss_conf.bssid); + rtl8xxxu_write16(priv, REG_BCN_INTERVAL, vif->bss_conf.beacon_int); + priv->fops->report_connect(priv, RTL8XXXU_BC_MC_MACID, 0, true); + + return 0; +} + static u32 rtl8xxxu_80211_to_rtl_queue(u32 queue) { u32 rtlqueue; @@ -4997,7 +5083,9 @@ static u32 rtl8xxxu_queue_select(struct ieee80211_hdr *hdr, struct sk_buff *skb) { u32 queue; - if (ieee80211_is_mgmt(hdr->frame_control)) + if (unlikely(ieee80211_is_beacon(hdr->frame_control))) + queue = TXDESC_QUEUE_BEACON; + else if (ieee80211_is_mgmt(hdr->frame_control)) queue = TXDESC_QUEUE_MGNT; else queue = rtl8xxxu_80211_to_rtl_queue(skb_get_queue_mapping(skb)); @@ -5160,23 +5248,16 @@ void rtl8xxxu_fill_txdesc_v1(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, struct ieee80211_tx_info *tx_info, struct rtl8xxxu_txdesc32 *tx_desc, bool sgi, - bool short_preamble, bool ampdu_enable, u32 rts_rate) + bool short_preamble, bool ampdu_enable, u32 rts_rate, + u8 macid) { - struct ieee80211_rate *tx_rate = ieee80211_get_tx_rate(hw, tx_info); struct rtl8xxxu_priv *priv = hw->priv; struct device *dev = &priv->udev->dev; u8 *qc = ieee80211_get_qos_ctl(hdr); u8 tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; - u32 rate; - u16 rate_flags = tx_info->control.rates[0].flags; + u32 rate = 0; u16 seq_number; - if (rate_flags & IEEE80211_TX_RC_MCS && - !ieee80211_is_mgmt(hdr->frame_control)) - rate = tx_info->control.rates[0].idx + DESC_RATE_MCS0; - else - rate = tx_rate->hw_value; - if (rtl8xxxu_debug & RTL8XXXU_DEBUG_TX) dev_info(dev, "%s: TX rate: %d, pkt size %u\n", __func__, rate, le16_to_cpu(tx_desc->pkt_size)); @@ -5215,10 +5296,10 @@ rtl8xxxu_fill_txdesc_v1(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, * rts_rate is zero if RTS/CTS or CTS to SELF are not enabled */ tx_desc->txdw4 |= cpu_to_le32(rts_rate << TXDESC32_RTS_RATE_SHIFT); - if (ampdu_enable || (rate_flags & IEEE80211_TX_RC_USE_RTS_CTS)) { + if (ampdu_enable || tx_info->control.use_rts) { tx_desc->txdw4 |= cpu_to_le32(TXDESC32_RTS_CTS_ENABLE); tx_desc->txdw4 |= cpu_to_le32(TXDESC32_HW_RTS_ENABLE); - } else if (rate_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { + } else if (tx_info->control.use_cts_prot) { tx_desc->txdw4 |= cpu_to_le32(TXDESC32_CTS_SELF_ENABLE); tx_desc->txdw4 |= cpu_to_le32(TXDESC32_HW_RTS_ENABLE); } @@ -5232,30 +5313,25 @@ void rtl8xxxu_fill_txdesc_v2(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, struct ieee80211_tx_info *tx_info, struct rtl8xxxu_txdesc32 *tx_desc32, bool sgi, - bool short_preamble, bool ampdu_enable, u32 rts_rate) + bool short_preamble, bool ampdu_enable, u32 rts_rate, + u8 macid) { - struct ieee80211_rate *tx_rate = ieee80211_get_tx_rate(hw, tx_info); struct rtl8xxxu_priv *priv = hw->priv; struct device *dev = &priv->udev->dev; struct rtl8xxxu_txdesc40 *tx_desc40; u8 *qc = ieee80211_get_qos_ctl(hdr); u8 tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; - u32 rate; - u16 rate_flags = tx_info->control.rates[0].flags; + u32 rate = 0; u16 seq_number; tx_desc40 = (struct rtl8xxxu_txdesc40 *)tx_desc32; - if (rate_flags & IEEE80211_TX_RC_MCS && - !ieee80211_is_mgmt(hdr->frame_control)) - rate = tx_info->control.rates[0].idx + DESC_RATE_MCS0; - else - rate = tx_rate->hw_value; - if (rtl8xxxu_debug & RTL8XXXU_DEBUG_TX) dev_info(dev, "%s: TX rate: %d, pkt size %u\n", __func__, rate, le16_to_cpu(tx_desc40->pkt_size)); + tx_desc40->txdw1 |= cpu_to_le32(macid << TXDESC40_MACID_SHIFT); + seq_number = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl)); tx_desc40->txdw4 = cpu_to_le32(rate); @@ -5279,17 +5355,21 @@ rtl8xxxu_fill_txdesc_v2(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, tx_desc40->txdw4 |= cpu_to_le32(TXDESC40_RETRY_LIMIT_ENABLE); } + if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) + tx_desc40->txdw8 |= cpu_to_le32(TXDESC40_HW_SEQ_ENABLE); + if (short_preamble) tx_desc40->txdw5 |= cpu_to_le32(TXDESC40_SHORT_PREAMBLE); tx_desc40->txdw4 |= cpu_to_le32(rts_rate << TXDESC40_RTS_RATE_SHIFT); + /* * rts_rate is zero if RTS/CTS or CTS to SELF are not enabled */ - if (ampdu_enable || (rate_flags & IEEE80211_TX_RC_USE_RTS_CTS)) { + if (ampdu_enable || tx_info->control.use_rts) { tx_desc40->txdw3 |= cpu_to_le32(TXDESC40_RTS_CTS_ENABLE); tx_desc40->txdw3 |= cpu_to_le32(TXDESC40_HW_RTS_ENABLE); - } else if (rate_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { + } else if (tx_info->control.use_cts_prot) { /* * For some reason the vendor driver doesn't set * TXDESC40_HW_RTS_ENABLE for CTS to SELF @@ -5307,24 +5387,17 @@ void rtl8xxxu_fill_txdesc_v3(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, struct ieee80211_tx_info *tx_info, struct rtl8xxxu_txdesc32 *tx_desc, bool sgi, - bool short_preamble, bool ampdu_enable, u32 rts_rate) + bool short_preamble, bool ampdu_enable, u32 rts_rate, + u8 macid) { - struct ieee80211_rate *tx_rate = ieee80211_get_tx_rate(hw, tx_info); struct rtl8xxxu_priv *priv = hw->priv; struct device *dev = &priv->udev->dev; struct rtl8xxxu_ra_info *ra = &priv->ra_info; u8 *qc = ieee80211_get_qos_ctl(hdr); u8 tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; - u32 rate; - u16 rate_flags = tx_info->control.rates[0].flags; + u32 rate = 0; u16 seq_number; - if (rate_flags & IEEE80211_TX_RC_MCS && - !ieee80211_is_mgmt(hdr->frame_control)) - rate = tx_info->control.rates[0].idx + DESC_RATE_MCS0; - else - rate = tx_rate->hw_value; - seq_number = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl)); if (ieee80211_is_data(hdr->frame_control)) { @@ -5377,10 +5450,10 @@ rtl8xxxu_fill_txdesc_v3(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, * rts_rate is zero if RTS/CTS or CTS to SELF are not enabled */ tx_desc->txdw4 |= cpu_to_le32(rts_rate << TXDESC32_RTS_RATE_SHIFT); - if (ampdu_enable || (rate_flags & IEEE80211_TX_RC_USE_RTS_CTS)) { + if (ampdu_enable || tx_info->control.use_rts) { tx_desc->txdw4 |= cpu_to_le32(TXDESC32_RTS_CTS_ENABLE); tx_desc->txdw4 |= cpu_to_le32(TXDESC32_HW_RTS_ENABLE); - } else if (rate_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { + } else if (tx_info->control.use_cts_prot) { tx_desc->txdw4 |= cpu_to_le32(TXDESC32_CTS_SELF_ENABLE); tx_desc->txdw4 |= cpu_to_le32(TXDESC32_HW_RTS_ENABLE); } @@ -5404,8 +5477,8 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw, struct device *dev = &priv->udev->dev; u32 queue, rts_rate; u16 pktlen = skb->len; - u16 rate_flag = tx_info->control.rates[0].flags; int tx_desc_size = priv->fops->tx_desc_size; + u8 macid; int ret; bool ampdu_enable, sgi = false, short_preamble = false; @@ -5488,26 +5561,29 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw, } } - if (rate_flag & IEEE80211_TX_RC_SHORT_GI || - (ieee80211_is_data_qos(hdr->frame_control) && - sta && sta->deflink.ht_cap.cap & - (IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_SGI_20))) + if (ieee80211_is_data_qos(hdr->frame_control) && + sta && sta->deflink.ht_cap.cap & + (IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_SGI_20)) sgi = true; - if (rate_flag & IEEE80211_TX_RC_USE_SHORT_PREAMBLE || - (sta && vif && vif->bss_conf.use_short_preamble)) + if (sta && vif && vif->bss_conf.use_short_preamble) short_preamble = true; - if (rate_flag & IEEE80211_TX_RC_USE_RTS_CTS) - rts_rate = ieee80211_get_rts_cts_rate(hw, tx_info)->hw_value; - else if (rate_flag & IEEE80211_TX_RC_USE_CTS_PROTECT) - rts_rate = ieee80211_get_rts_cts_rate(hw, tx_info)->hw_value; + if (skb->len > hw->wiphy->rts_threshold) + tx_info->control.use_rts = true; + + if (sta && vif && vif->bss_conf.use_cts_prot) + tx_info->control.use_cts_prot = true; + + if (ampdu_enable || tx_info->control.use_rts || + tx_info->control.use_cts_prot) + rts_rate = DESC_RATE_24M; else rts_rate = 0; - + macid = rtl8xxxu_get_macid(priv, sta); priv->fops->fill_txdesc(hw, hdr, tx_info, tx_desc, sgi, short_preamble, - ampdu_enable, rts_rate); + ampdu_enable, rts_rate, macid); rtl8xxxu_calc_tx_desc_csum(tx_desc); @@ -5530,6 +5606,55 @@ error: dev_kfree_skb(skb); } +static void rtl8xxxu_send_beacon_frame(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct rtl8xxxu_priv *priv = hw->priv; + struct sk_buff *skb = ieee80211_beacon_get(hw, vif, 0); + struct device *dev = &priv->udev->dev; + int retry; + u8 val8; + + /* BCN_VALID, write 1 to clear, cleared by SW */ + val8 = rtl8xxxu_read8(priv, REG_TDECTRL + 2); + val8 |= BIT_BCN_VALID >> 16; + rtl8xxxu_write8(priv, REG_TDECTRL + 2, val8); + + /* SW_BCN_SEL - Port0 */ + val8 = rtl8xxxu_read8(priv, REG_DWBCN1_CTRL_8723B + 2); + val8 &= ~(BIT_SW_BCN_SEL >> 16); + rtl8xxxu_write8(priv, REG_DWBCN1_CTRL_8723B + 2, val8); + + if (skb) + rtl8xxxu_tx(hw, NULL, skb); + + retry = 100; + do { + val8 = rtl8xxxu_read8(priv, REG_TDECTRL + 2); + if (val8 & (BIT_BCN_VALID >> 16)) + break; + usleep_range(10, 20); + } while (--retry); + + if (!retry) + dev_err(dev, "%s: Failed to read beacon valid bit\n", __func__); +} + +static void rtl8xxxu_update_beacon_work_callback(struct work_struct *work) +{ + struct rtl8xxxu_priv *priv = + container_of(work, struct rtl8xxxu_priv, update_beacon_work); + struct ieee80211_hw *hw = priv->hw; + struct ieee80211_vif *vif = priv->vif; + + if (!vif) { + WARN_ONCE(true, "no vif to update beacon\n"); + return; + } + + rtl8xxxu_send_beacon_frame(hw, vif); +} + void rtl8723au_rx_parse_phystats(struct rtl8xxxu_priv *priv, struct ieee80211_rx_status *rx_status, struct rtl8723au_phy_stats *phy_stats, @@ -6198,61 +6323,98 @@ int rtl8xxxu_parse_rxdesc16(struct rtl8xxxu_priv *priv, struct sk_buff *skb) int rtl8xxxu_parse_rxdesc24(struct rtl8xxxu_priv *priv, struct sk_buff *skb) { struct ieee80211_hw *hw = priv->hw; - struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); - struct rtl8xxxu_rxdesc24 *rx_desc = - (struct rtl8xxxu_rxdesc24 *)skb->data; + struct ieee80211_rx_status *rx_status; + struct rtl8xxxu_rxdesc24 *rx_desc; struct rtl8723au_phy_stats *phy_stats; - __le32 *_rx_desc_le = (__le32 *)skb->data; - u32 *_rx_desc = (u32 *)skb->data; + struct sk_buff *next_skb = NULL; + __le32 *_rx_desc_le; + u32 *_rx_desc; int drvinfo_sz, desc_shift; - int i; + int i, pkt_len, urb_len, pkt_offset; + + urb_len = skb->len; + + if (urb_len < sizeof(struct rtl8xxxu_rxdesc24)) { + kfree_skb(skb); + return RX_TYPE_ERROR; + } - for (i = 0; i < (sizeof(struct rtl8xxxu_rxdesc24) / sizeof(u32)); i++) - _rx_desc[i] = le32_to_cpu(_rx_desc_le[i]); + do { + rx_desc = (struct rtl8xxxu_rxdesc24 *)skb->data; + _rx_desc_le = (__le32 *)skb->data; + _rx_desc = (u32 *)skb->data; - memset(rx_status, 0, sizeof(struct ieee80211_rx_status)); + for (i = 0; i < (sizeof(struct rtl8xxxu_rxdesc24) / sizeof(u32)); i++) + _rx_desc[i] = le32_to_cpu(_rx_desc_le[i]); - skb_pull(skb, sizeof(struct rtl8xxxu_rxdesc24)); + pkt_len = rx_desc->pktlen; - phy_stats = (struct rtl8723au_phy_stats *)skb->data; + drvinfo_sz = rx_desc->drvinfo_sz * 8; + desc_shift = rx_desc->shift; + pkt_offset = roundup(pkt_len + drvinfo_sz + desc_shift + + sizeof(struct rtl8xxxu_rxdesc24), 8); - drvinfo_sz = rx_desc->drvinfo_sz * 8; - desc_shift = rx_desc->shift; - skb_pull(skb, drvinfo_sz + desc_shift); + /* + * Only clone the skb if there's enough data at the end to + * at least cover the rx descriptor + */ + if (urb_len >= (pkt_offset + sizeof(struct rtl8xxxu_rxdesc24))) + next_skb = skb_clone(skb, GFP_ATOMIC); - if (rx_desc->rpt_sel) { - struct device *dev = &priv->udev->dev; - dev_dbg(dev, "%s: C2H packet\n", __func__); - rtl8723bu_handle_c2h(priv, skb); - return RX_TYPE_C2H; - } + rx_status = IEEE80211_SKB_RXCB(skb); + memset(rx_status, 0, sizeof(struct ieee80211_rx_status)); - if (rx_desc->phy_stats) - priv->fops->parse_phystats(priv, rx_status, phy_stats, - rx_desc->rxmcs, (struct ieee80211_hdr *)skb->data, - rx_desc->crc32 || rx_desc->icverr); + skb_pull(skb, sizeof(struct rtl8xxxu_rxdesc24)); - rx_status->mactime = rx_desc->tsfl; - rx_status->flag |= RX_FLAG_MACTIME_START; + phy_stats = (struct rtl8723au_phy_stats *)skb->data; - if (!rx_desc->swdec) - rx_status->flag |= RX_FLAG_DECRYPTED; - if (rx_desc->crc32) - rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; - if (rx_desc->bw) - rx_status->bw = RATE_INFO_BW_40; + skb_pull(skb, drvinfo_sz + desc_shift); - if (rx_desc->rxmcs >= DESC_RATE_MCS0) { - rx_status->encoding = RX_ENC_HT; - rx_status->rate_idx = rx_desc->rxmcs - DESC_RATE_MCS0; - } else { - rx_status->rate_idx = rx_desc->rxmcs; - } + skb_trim(skb, pkt_len); - rx_status->freq = hw->conf.chandef.chan->center_freq; - rx_status->band = hw->conf.chandef.chan->band; + if (rx_desc->rpt_sel) { + struct device *dev = &priv->udev->dev; + dev_dbg(dev, "%s: C2H packet\n", __func__); + rtl8723bu_handle_c2h(priv, skb); + } else { + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + + if (rx_desc->phy_stats) + priv->fops->parse_phystats(priv, rx_status, phy_stats, + rx_desc->rxmcs, hdr, + rx_desc->crc32 || rx_desc->icverr); + + rx_status->mactime = rx_desc->tsfl; + rx_status->flag |= RX_FLAG_MACTIME_START; + + if (!rx_desc->swdec) + rx_status->flag |= RX_FLAG_DECRYPTED; + if (rx_desc->crc32) + rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; + if (rx_desc->bw) + rx_status->bw = RATE_INFO_BW_40; + + if (rx_desc->rxmcs >= DESC_RATE_MCS0) { + rx_status->encoding = RX_ENC_HT; + rx_status->rate_idx = rx_desc->rxmcs - DESC_RATE_MCS0; + } else { + rx_status->rate_idx = rx_desc->rxmcs; + } + + rx_status->freq = hw->conf.chandef.chan->center_freq; + rx_status->band = hw->conf.chandef.chan->band; + + ieee80211_rx_irqsafe(hw, skb); + } + + skb = next_skb; + if (skb) + skb_pull(next_skb, pkt_offset); + + urb_len -= pkt_offset; + next_skb = NULL; + } while (skb && urb_len >= sizeof(struct rtl8xxxu_rxdesc24)); - ieee80211_rx_irqsafe(hw, skb); return RX_TYPE_DATA_PKT; } @@ -6282,7 +6444,6 @@ static void rtl8xxxu_rx_complete(struct urb *urb) cleanup: usb_free_urb(urb); dev_kfree_skb(skb); - return; } static int rtl8xxxu_submit_rx_urb(struct rtl8xxxu_priv *priv, @@ -6372,12 +6533,13 @@ static int rtl8xxxu_add_interface(struct ieee80211_hw *hw, int ret; u8 val8; + if (!priv->vif) + priv->vif = vif; + else + return -EOPNOTSUPP; + switch (vif->type) { case NL80211_IFTYPE_STATION: - if (!priv->vif) - priv->vif = vif; - else - return -EOPNOTSUPP; rtl8xxxu_stop_tx_beacon(priv); val8 = rtl8xxxu_read8(priv, REG_BEACON_CTRL); @@ -6386,11 +6548,33 @@ static int rtl8xxxu_add_interface(struct ieee80211_hw *hw, rtl8xxxu_write8(priv, REG_BEACON_CTRL, val8); ret = 0; break; + case NL80211_IFTYPE_AP: + rtl8xxxu_write8(priv, REG_BEACON_CTRL, + BEACON_DISABLE_TSF_UPDATE | BEACON_CTRL_MBSSID); + rtl8xxxu_write8(priv, REG_ATIMWND, 0x0c); /* 12ms */ + rtl8xxxu_write16(priv, REG_TSFTR_SYN_OFFSET, 0x7fff); /* ~32ms */ + rtl8xxxu_write8(priv, REG_DUAL_TSF_RST, DUAL_TSF_RESET_TSF0); + + /* enable BCN0 function */ + rtl8xxxu_write8(priv, REG_BEACON_CTRL, + BEACON_DISABLE_TSF_UPDATE | + BEACON_FUNCTION_ENABLE | BEACON_CTRL_MBSSID | + BEACON_CTRL_TX_BEACON_RPT); + + /* select BCN on port 0 */ + val8 = rtl8xxxu_read8(priv, REG_CCK_CHECK); + val8 &= ~BIT_BCN_PORT_SEL; + rtl8xxxu_write8(priv, REG_CCK_CHECK, val8); + + ret = 0; + break; default: ret = -EOPNOTSUPP; } rtl8xxxu_set_linktype(priv, vif->type); + ether_addr_copy(priv->mac_addr, vif->addr); + rtl8xxxu_set_mac(priv); return ret; } @@ -6521,22 +6705,22 @@ static void rtl8xxxu_configure_filter(struct ieee80211_hw *hw, */ if (*total_flags & FIF_BCN_PRBRESP_PROMISC) - rcr &= ~RCR_CHECK_BSSID_BEACON; + rcr &= ~(RCR_CHECK_BSSID_BEACON | RCR_CHECK_BSSID_MATCH); else - rcr |= RCR_CHECK_BSSID_BEACON; + rcr |= RCR_CHECK_BSSID_BEACON | RCR_CHECK_BSSID_MATCH; + + if (priv->vif && priv->vif->type == NL80211_IFTYPE_AP) + rcr &= ~RCR_CHECK_BSSID_MATCH; if (*total_flags & FIF_CONTROL) rcr |= RCR_ACCEPT_CTRL_FRAME; else rcr &= ~RCR_ACCEPT_CTRL_FRAME; - if (*total_flags & FIF_OTHER_BSS) { + if (*total_flags & FIF_OTHER_BSS) rcr |= RCR_ACCEPT_AP; - rcr &= ~RCR_CHECK_BSSID_MATCH; - } else { + else rcr &= ~RCR_ACCEPT_AP; - rcr |= RCR_CHECK_BSSID_MATCH; - } if (*total_flags & FIF_PSPOLL) rcr |= RCR_ACCEPT_PM; @@ -6557,7 +6741,7 @@ static void rtl8xxxu_configure_filter(struct ieee80211_hw *hw, static int rtl8xxxu_set_rts_threshold(struct ieee80211_hw *hw, u32 rts) { - if (rts > 2347) + if (rts > 2347 && rts != (u32)-1) return -EINVAL; return 0; @@ -6706,7 +6890,8 @@ static u8 rtl8xxxu_signal_to_snr(int signal) } static void rtl8xxxu_refresh_rate_mask(struct rtl8xxxu_priv *priv, - int signal, struct ieee80211_sta *sta) + int signal, struct ieee80211_sta *sta, + bool force) { struct ieee80211_hw *hw = priv->hw; u16 wireless_mode; @@ -6714,6 +6899,7 @@ static void rtl8xxxu_refresh_rate_mask(struct rtl8xxxu_priv *priv, u8 txbw_40mhz; u8 snr, snr_thresh_high, snr_thresh_low; u8 go_up_gap = 5; + u8 macid = rtl8xxxu_get_macid(priv, sta); rssi_level = priv->rssi_level; snr = rtl8xxxu_signal_to_snr(signal); @@ -6740,7 +6926,7 @@ static void rtl8xxxu_refresh_rate_mask(struct rtl8xxxu_priv *priv, else rssi_level = RTL8XXXU_RATR_STA_LOW; - if (rssi_level != priv->rssi_level) { + if (rssi_level != priv->rssi_level || force) { int sgi = 0; u32 rate_bitmap = 0; @@ -6833,7 +7019,7 @@ static void rtl8xxxu_refresh_rate_mask(struct rtl8xxxu_priv *priv, } priv->rssi_level = rssi_level; - priv->fops->update_rate_mask(priv, rate_bitmap, ratr_idx, sgi, txbw_40mhz); + priv->fops->update_rate_mask(priv, rate_bitmap, ratr_idx, sgi, txbw_40mhz, macid); } } @@ -6956,7 +7142,7 @@ static void rtl8xxxu_watchdog_callback(struct work_struct *work) if (priv->fops->set_crystal_cap) rtl8xxxu_track_cfo(priv); - rtl8xxxu_refresh_rate_mask(priv, signal, sta); + rtl8xxxu_refresh_rate_mask(priv, signal, sta, false); } out: @@ -7087,6 +7273,38 @@ static void rtl8xxxu_stop(struct ieee80211_hw *hw) rtl8xxxu_free_tx_resources(priv); } +static int rtl8xxxu_sta_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct rtl8xxxu_sta_info *sta_info = (struct rtl8xxxu_sta_info *)sta->drv_priv; + struct rtl8xxxu_priv *priv = hw->priv; + + if (vif->type == NL80211_IFTYPE_AP) { + sta_info->macid = rtl8xxxu_acquire_macid(priv); + if (sta_info->macid >= RTL8XXXU_MAX_MAC_ID_NUM) + return -ENOSPC; + + rtl8xxxu_refresh_rate_mask(priv, 0, sta, true); + priv->fops->report_connect(priv, sta_info->macid, H2C_MACID_ROLE_STA, true); + } + + return 0; +} + +static int rtl8xxxu_sta_remove(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct rtl8xxxu_sta_info *sta_info = (struct rtl8xxxu_sta_info *)sta->drv_priv; + struct rtl8xxxu_priv *priv = hw->priv; + + if (vif->type == NL80211_IFTYPE_AP) + rtl8xxxu_release_macid(priv, sta_info->macid); + + return 0; +} + static const struct ieee80211_ops rtl8xxxu_ops = { .tx = rtl8xxxu_tx, .wake_tx_queue = ieee80211_handle_wake_tx_queue, @@ -7095,6 +7313,7 @@ static const struct ieee80211_ops rtl8xxxu_ops = { .config = rtl8xxxu_config, .conf_tx = rtl8xxxu_conf_tx, .bss_info_changed = rtl8xxxu_bss_info_changed, + .start_ap = rtl8xxxu_start_ap, .configure_filter = rtl8xxxu_configure_filter, .set_rts_threshold = rtl8xxxu_set_rts_threshold, .start = rtl8xxxu_start, @@ -7105,6 +7324,9 @@ static const struct ieee80211_ops rtl8xxxu_ops = { .ampdu_action = rtl8xxxu_ampdu_action, .sta_statistics = rtl8xxxu_sta_statistics, .get_antenna = rtl8xxxu_get_antenna, + .set_tim = rtl8xxxu_set_tim, + .sta_add = rtl8xxxu_sta_add, + .sta_remove = rtl8xxxu_sta_remove, }; static int rtl8xxxu_parse_usb(struct rtl8xxxu_priv *priv, @@ -7296,6 +7518,7 @@ static int rtl8xxxu_probe(struct usb_interface *interface, spin_lock_init(&priv->rx_urb_lock); INIT_WORK(&priv->rx_urb_wq, rtl8xxxu_rx_urb_work); INIT_DELAYED_WORK(&priv->ra_watchdog, rtl8xxxu_watchdog_callback); + INIT_WORK(&priv->update_beacon_work, rtl8xxxu_update_beacon_work_callback); skb_queue_head_init(&priv->c2hcmd_queue); usb_set_intfdata(interface, hw); @@ -7347,7 +7570,11 @@ static int rtl8xxxu_probe(struct usb_interface *interface, hw->wiphy->max_scan_ssids = 1; hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; + if (priv->fops->max_macid_num) + hw->wiphy->max_ap_assoc_sta = priv->fops->max_macid_num - 1; hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); + if (priv->fops->supports_ap) + hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP); hw->queues = 4; sband = &rtl8xxxu_supported_band; diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h index 4dffbab494c3..8571d5129f32 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h @@ -456,6 +456,7 @@ #define REG_FIFOPAGE 0x0204 #define REG_TDECTRL 0x0208 +#define BIT_BCN_VALID BIT(16) #define REG_DWBCN0_CTRL_8188F REG_TDECTRL @@ -470,6 +471,7 @@ #define AUTO_LLT_INIT_LLT BIT(16) #define REG_DWBCN1_CTRL_8723B 0x0228 +#define BIT_SW_BCN_SEL BIT(20) /* 0x0280 ~ 0x02FF RXDMA Configuration */ #define REG_RXDMA_AGG_PG_TH 0x0280 /* 0-7 : USB DMA size bits @@ -516,6 +518,7 @@ #define REG_FWHW_TXQ_CTRL 0x0420 #define FWHW_TXQ_CTRL_AMPDU_RETRY BIT(7) #define FWHW_TXQ_CTRL_XMIT_MGMT_ACK BIT(12) +#define EN_BCNQ_DL BIT(22) #define REG_HWSEQ_CTRL 0x0423 #define REG_TXPKTBUF_BCNQ_BDNY 0x0424 @@ -572,6 +575,8 @@ #define REG_ARFR1 0x0448 #define REG_ARFR2 0x044c #define REG_ARFR3 0x0450 +#define REG_CCK_CHECK 0x0454 +#define BIT_BCN_PORT_SEL BIT(5) #define REG_AMPDU_MAX_TIME_8723B 0x0456 #define REG_AGGLEN_LMT 0x0458 #define REG_AMPDU_MIN_SPACE 0x045c diff --git a/drivers/net/wireless/realtek/rtw88/debug.c b/drivers/net/wireless/realtek/rtw88/debug.c index fa3d73b333ba..f8ba133baff0 100644 --- a/drivers/net/wireless/realtek/rtw88/debug.c +++ b/drivers/net/wireless/realtek/rtw88/debug.c @@ -183,8 +183,8 @@ static int rtw_debugfs_copy_from_user(char tmp[], int size, tmp_len = (count > size - 1 ? size - 1 : count); - if (!buffer || copy_from_user(tmp, buffer, tmp_len)) - return count; + if (copy_from_user(tmp, buffer, tmp_len)) + return -EFAULT; tmp[tmp_len] = '\0'; @@ -201,13 +201,16 @@ static ssize_t rtw_debugfs_set_read_reg(struct file *filp, char tmp[32 + 1]; u32 addr, len; int num; + int ret; - rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 2); + ret = rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 2); + if (ret) + return ret; num = sscanf(tmp, "%x %x", &addr, &len); if (num != 2) - return count; + return -EINVAL; if (len != 1 && len != 2 && len != 4) { rtw_warn(rtwdev, "read reg setting wrong len\n"); @@ -288,8 +291,11 @@ static ssize_t rtw_debugfs_set_rsvd_page(struct file *filp, char tmp[32 + 1]; u32 offset, page_num; int num; + int ret; - rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 2); + ret = rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 2); + if (ret) + return ret; num = sscanf(tmp, "%d %d", &offset, &page_num); @@ -314,8 +320,11 @@ static ssize_t rtw_debugfs_set_single_input(struct file *filp, char tmp[32 + 1]; u32 input; int num; + int ret; - rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 1); + ret = rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 1); + if (ret) + return ret; num = kstrtoint(tmp, 0, &input); @@ -338,14 +347,17 @@ static ssize_t rtw_debugfs_set_write_reg(struct file *filp, char tmp[32 + 1]; u32 addr, val, len; int num; + int ret; - rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 3); + ret = rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 3); + if (ret) + return ret; /* write BB/MAC register */ num = sscanf(tmp, "%x %x %x", &addr, &val, &len); if (num != 3) - return count; + return -EINVAL; switch (len) { case 1: @@ -381,8 +393,11 @@ static ssize_t rtw_debugfs_set_h2c(struct file *filp, char tmp[32 + 1]; u8 param[8]; int num; + int ret; - rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 3); + ret = rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 3); + if (ret) + return ret; num = sscanf(tmp, "%hhx,%hhx,%hhx,%hhx,%hhx,%hhx,%hhx,%hhx", ¶m[0], ¶m[1], ¶m[2], ¶m[3], @@ -408,14 +423,17 @@ static ssize_t rtw_debugfs_set_rf_write(struct file *filp, char tmp[32 + 1]; u32 path, addr, mask, val; int num; + int ret; - rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 4); + ret = rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 4); + if (ret) + return ret; num = sscanf(tmp, "%x %x %x %x", &path, &addr, &mask, &val); if (num != 4) { rtw_warn(rtwdev, "invalid args, [path] [addr] [mask] [val]\n"); - return count; + return -EINVAL; } mutex_lock(&rtwdev->mutex); @@ -438,14 +456,17 @@ static ssize_t rtw_debugfs_set_rf_read(struct file *filp, char tmp[32 + 1]; u32 path, addr, mask; int num; + int ret; - rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 3); + ret = rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 3); + if (ret) + return ret; num = sscanf(tmp, "%x %x %x", &path, &addr, &mask); if (num != 3) { rtw_warn(rtwdev, "invalid args, [path] [addr] [mask] [val]\n"); - return count; + return -EINVAL; } debugfs_priv->rf_path = path; @@ -467,7 +488,9 @@ static ssize_t rtw_debugfs_set_fix_rate(struct file *filp, char tmp[32 + 1]; int ret; - rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 1); + ret = rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 1); + if (ret) + return ret; ret = kstrtou8(tmp, 0, &fix_rate); if (ret) { @@ -860,7 +883,9 @@ static ssize_t rtw_debugfs_set_coex_enable(struct file *filp, bool enable; int ret; - rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 1); + ret = rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 1); + if (ret) + return ret; ret = kstrtobool(tmp, &enable); if (ret) { @@ -930,7 +955,9 @@ static ssize_t rtw_debugfs_set_fw_crash(struct file *filp, bool input; int ret; - rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 1); + ret = rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 1); + if (ret) + return ret; ret = kstrtobool(tmp, &input); if (ret) diff --git a/drivers/net/wireless/realtek/rtw88/mac80211.c b/drivers/net/wireless/realtek/rtw88/mac80211.c index a6c024cab7ee..e82e40cac60a 100644 --- a/drivers/net/wireless/realtek/rtw88/mac80211.c +++ b/drivers/net/wireless/realtek/rtw88/mac80211.c @@ -173,8 +173,10 @@ static int rtw_ops_add_interface(struct ieee80211_hw *hw, mutex_lock(&rtwdev->mutex); port = find_first_zero_bit(rtwdev->hw_port, RTW_PORT_NUM); - if (port >= RTW_PORT_NUM) + if (port >= RTW_PORT_NUM) { + mutex_unlock(&rtwdev->mutex); return -EINVAL; + } set_bit(port, rtwdev->hw_port); rtwvif->port = port; diff --git a/drivers/net/wireless/realtek/rtw89/Makefile b/drivers/net/wireless/realtek/rtw89/Makefile index 2dc48fa10c6b..99e870d6a7d7 100644 --- a/drivers/net/wireless/realtek/rtw89/Makefile +++ b/drivers/net/wireless/realtek/rtw89/Makefile @@ -13,7 +13,8 @@ rtw89_core-y += core.o \ coex.o \ ps.o \ chan.o \ - ser.o + ser.o \ + acpi.o rtw89_core-$(CONFIG_PM) += wow.o diff --git a/drivers/net/wireless/realtek/rtw89/acpi.c b/drivers/net/wireless/realtek/rtw89/acpi.c new file mode 100644 index 000000000000..8aaf83a2a6b4 --- /dev/null +++ b/drivers/net/wireless/realtek/rtw89/acpi.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2021-2023 Realtek Corporation + */ + +#include <linux/acpi.h> +#include <linux/uuid.h> + +#include "acpi.h" +#include "debug.h" + +static const guid_t rtw89_guid = GUID_INIT(0xD2A8C3E8, 0x4B69, 0x4F00, + 0x82, 0xBD, 0xFE, 0x86, + 0x07, 0x80, 0x3A, 0xA7); + +static int rtw89_acpi_dsm_get(struct rtw89_dev *rtwdev, union acpi_object *obj, + u8 *value) +{ + switch (obj->type) { + case ACPI_TYPE_INTEGER: + *value = (u8)obj->integer.value; + break; + case ACPI_TYPE_BUFFER: + *value = obj->buffer.pointer[0]; + break; + default: + rtw89_debug(rtwdev, RTW89_DBG_UNEXP, + "acpi dsm return unhandled type: %d\n", obj->type); + return -EINVAL; + } + + return 0; +} + +int rtw89_acpi_evaluate_dsm(struct rtw89_dev *rtwdev, + enum rtw89_acpi_dsm_func func, u8 *value) +{ + union acpi_object *obj; + int ret; + + obj = acpi_evaluate_dsm(ACPI_HANDLE(rtwdev->dev), &rtw89_guid, + 0, func, NULL); + if (!obj) { + rtw89_debug(rtwdev, RTW89_DBG_UNEXP, + "acpi dsm fail to evaluate func: %d\n", func); + return -ENOENT; + } + + ret = rtw89_acpi_dsm_get(rtwdev, obj, value); + + ACPI_FREE(obj); + return ret; +} diff --git a/drivers/net/wireless/realtek/rtw89/acpi.h b/drivers/net/wireless/realtek/rtw89/acpi.h new file mode 100644 index 000000000000..ed74d8ceb733 --- /dev/null +++ b/drivers/net/wireless/realtek/rtw89/acpi.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* Copyright(c) 2021-2023 Realtek Corporation + */ + +#ifndef __RTW89_ACPI_H__ +#define __RTW89_ACPI_H__ + +#include "core.h" + +enum rtw89_acpi_dsm_func { + RTW89_ACPI_DSM_FUNC_IDN_BAND_SUP = 2, + RTW89_ACPI_DSM_FUNC_6G_DIS = 3, + RTW89_ACPI_DSM_FUNC_6G_BP = 4, + RTW89_ACPI_DSM_FUNC_TAS_EN = 5, + RTW89_ACPI_DSM_FUNC_59G_EN = 6, +}; + +int rtw89_acpi_evaluate_dsm(struct rtw89_dev *rtwdev, + enum rtw89_acpi_dsm_func func, u8 *value); + +#endif diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 7fc0a26a4d73..fbcb9b6e6f75 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -77,6 +77,9 @@ static struct ieee80211_channel rtw89_channels_5ghz[] = { RTW89_DEF_CHAN_5G(5785, 157), RTW89_DEF_CHAN_5G(5805, 161), RTW89_DEF_CHAN_5G_NO_HT40MINUS(5825, 165), + RTW89_DEF_CHAN_5G(5845, 169), + RTW89_DEF_CHAN_5G(5865, 173), + RTW89_DEF_CHAN_5G(5885, 177), }; static struct ieee80211_channel rtw89_channels_6ghz[] = { @@ -1244,13 +1247,34 @@ static void rtw89_core_rx_process_phy_ppdu_iter(void *data, struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv; struct rtw89_rx_phy_ppdu *phy_ppdu = (struct rtw89_rx_phy_ppdu *)data; struct rtw89_dev *rtwdev = rtwsta->rtwdev; + struct rtw89_hal *hal = &rtwdev->hal; + u8 ant_num = hal->ant_diversity ? 2 : rtwdev->chip->rf_path_num; + u8 ant_pos = U8_MAX; + u8 evm_pos = 0; int i; - if (rtwsta->mac_id == phy_ppdu->mac_id && phy_ppdu->to_self) { - ewma_rssi_add(&rtwsta->avg_rssi, phy_ppdu->rssi_avg); + if (rtwsta->mac_id != phy_ppdu->mac_id || !phy_ppdu->to_self) + return; + + if (hal->ant_diversity && hal->antenna_rx) { + ant_pos = __ffs(hal->antenna_rx); + evm_pos = ant_pos; + } + + ewma_rssi_add(&rtwsta->avg_rssi, phy_ppdu->rssi_avg); + + if (ant_pos < ant_num) { + ewma_rssi_add(&rtwsta->rssi[ant_pos], phy_ppdu->rssi[0]); + } else { for (i = 0; i < rtwdev->chip->rf_path_num; i++) ewma_rssi_add(&rtwsta->rssi[i], phy_ppdu->rssi[i]); } + + if (phy_ppdu->ofdm.has) { + ewma_snr_add(&rtwsta->avg_snr, phy_ppdu->ofdm.avg_snr); + ewma_evm_add(&rtwsta->evm_min[evm_pos], phy_ppdu->ofdm.evm_min); + ewma_evm_add(&rtwsta->evm_max[evm_pos], phy_ppdu->ofdm.evm_max); + } } #define VAR_LEN 0xff @@ -1277,20 +1301,30 @@ static u16 rtw89_core_get_phy_status_ie_len(struct rtw89_dev *rtwdev, u8 *addr) static void rtw89_core_parse_phy_status_ie01(struct rtw89_dev *rtwdev, u8 *addr, struct rtw89_rx_phy_ppdu *phy_ppdu) { + const struct rtw89_phy_sts_ie0 *ie = (const struct rtw89_phy_sts_ie0 *)addr; s16 cfo; + u32 t; - phy_ppdu->chan_idx = RTW89_GET_PHY_STS_IE01_CH_IDX(addr); + phy_ppdu->chan_idx = le32_get_bits(ie->w0, RTW89_PHY_STS_IE01_W0_CH_IDX); if (phy_ppdu->rate < RTW89_HW_RATE_OFDM6) return; if (!phy_ppdu->to_self) return; + phy_ppdu->ofdm.avg_snr = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_AVG_SNR); + phy_ppdu->ofdm.evm_max = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_EVM_MAX); + phy_ppdu->ofdm.evm_min = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_EVM_MIN); + phy_ppdu->ofdm.has = true; + /* sign conversion for S(12,2) */ - if (rtwdev->chip->cfo_src_fd) - cfo = sign_extend32(RTW89_GET_PHY_STS_IE01_FD_CFO(addr), 11); - else - cfo = sign_extend32(RTW89_GET_PHY_STS_IE01_PREMB_CFO(addr), 11); + if (rtwdev->chip->cfo_src_fd) { + t = le32_get_bits(ie->w1, RTW89_PHY_STS_IE01_W1_FD_CFO); + cfo = sign_extend32(t, 11); + } else { + t = le32_get_bits(ie->w1, RTW89_PHY_STS_IE01_W1_PREMB_CFO); + cfo = sign_extend32(t, 11); + } rtw89_phy_cfo_parse(rtwdev, cfo, phy_ppdu); } @@ -1333,9 +1367,6 @@ static int rtw89_core_rx_process_phy_ppdu(struct rtw89_dev *rtwdev, return -EINVAL; } rtw89_core_update_phy_ppdu(phy_ppdu); - ieee80211_iterate_stations_atomic(rtwdev->hw, - rtw89_core_rx_process_phy_ppdu_iter, - phy_ppdu); return 0; } @@ -1363,6 +1394,8 @@ static int rtw89_core_rx_parse_phy_sts(struct rtw89_dev *rtwdev, } } + rtw89_phy_antdiv_parse(rtwdev, phy_ppdu); + return 0; } @@ -1376,6 +1409,10 @@ static void rtw89_core_rx_process_phy_sts(struct rtw89_dev *rtwdev, rtw89_debug(rtwdev, RTW89_DBG_TXRX, "parse phy sts failed\n"); else phy_ppdu->valid = true; + + ieee80211_iterate_stations_atomic(rtwdev->hw, + rtw89_core_rx_process_phy_ppdu_iter, + phy_ppdu); } static u8 rtw89_rxdesc_to_nl_he_gi(struct rtw89_dev *rtwdev, @@ -1481,6 +1518,34 @@ static void rtw89_stats_trigger_frame(struct rtw89_dev *rtwdev, } } +static void rtw89_cancel_6ghz_probe_work(struct work_struct *work) +{ + struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, + cancel_6ghz_probe_work); + struct list_head *pkt_list = rtwdev->scan_info.pkt_list; + struct rtw89_pktofld_info *info; + + mutex_lock(&rtwdev->mutex); + + if (!rtwdev->scanning) + goto out; + + list_for_each_entry(info, &pkt_list[NL80211_BAND_6GHZ], list) { + if (!info->cancel || !test_bit(info->id, rtwdev->pkt_offload)) + continue; + + rtw89_fw_h2c_del_pkt_offload(rtwdev, info->id); + + /* Don't delete/free info from pkt_list at this moment. Let it + * be deleted/freed in rtw89_release_pkt_list() after scanning, + * since if during scanning, pkt_list is accessed in bottom half. + */ + } + +out: + mutex_unlock(&rtwdev->mutex); +} + static void rtw89_core_cancel_6ghz_probe_tx(struct rtw89_dev *rtwdev, struct sk_buff *skb) { @@ -1489,6 +1554,7 @@ static void rtw89_core_cancel_6ghz_probe_tx(struct rtw89_dev *rtwdev, struct list_head *pkt_list = rtwdev->scan_info.pkt_list; struct rtw89_pktofld_info *info; const u8 *ies = mgmt->u.beacon.variable, *ssid_ie; + bool queue_work = false; if (rx_status->band != NL80211_BAND_6GHZ) return; @@ -1497,16 +1563,22 @@ static void rtw89_core_cancel_6ghz_probe_tx(struct rtw89_dev *rtwdev, list_for_each_entry(info, &pkt_list[NL80211_BAND_6GHZ], list) { if (ether_addr_equal(info->bssid, mgmt->bssid)) { - rtw89_fw_h2c_del_pkt_offload(rtwdev, info->id); + info->cancel = true; + queue_work = true; continue; } if (!ssid_ie || ssid_ie[1] != info->ssid_len || info->ssid_len == 0) continue; - if (memcmp(&ssid_ie[2], info->ssid, info->ssid_len) == 0) - rtw89_fw_h2c_del_pkt_offload(rtwdev, info->id); + if (memcmp(&ssid_ie[2], info->ssid, info->ssid_len) == 0) { + info->cancel = true; + queue_work = true; + } } + + if (queue_work) + ieee80211_queue_work(rtwdev->hw, &rtwdev->cancel_6ghz_probe_work); } static void rtw89_vif_rx_stats_iter(void *data, u8 *mac, @@ -2596,6 +2668,7 @@ static void rtw89_track_work(struct work_struct *work) rtw89_phy_ra_update(rtwdev); rtw89_phy_cfo_track(rtwdev); rtw89_phy_tx_path_div_track(rtwdev); + rtw89_phy_antdiv_track(rtwdev); rtw89_phy_ul_tb_ctrl_track(rtwdev); if (rtwdev->lps_enabled && !rtwdev->btc.lps) @@ -2759,6 +2832,8 @@ int rtw89_core_sta_add(struct rtw89_dev *rtwdev, { struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv; struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv; + struct rtw89_hal *hal = &rtwdev->hal; + u8 ant_num = hal->ant_diversity ? 2 : rtwdev->chip->rf_path_num; int i; int ret; @@ -2772,8 +2847,12 @@ int rtw89_core_sta_add(struct rtw89_dev *rtwdev, rtw89_core_txq_init(rtwdev, sta->txq[i]); ewma_rssi_init(&rtwsta->avg_rssi); - for (i = 0; i < rtwdev->chip->rf_path_num; i++) + ewma_snr_init(&rtwsta->avg_snr); + for (i = 0; i < ant_num; i++) { ewma_rssi_init(&rtwsta->rssi[i]); + ewma_evm_init(&rtwsta->evm_min[i]); + ewma_evm_init(&rtwsta->evm_max[i]); + } if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls) { /* for station mode, assign the mac_id from itself */ @@ -3433,6 +3512,7 @@ void rtw89_core_stop(struct rtw89_dev *rtwdev) mutex_unlock(&rtwdev->mutex); cancel_work_sync(&rtwdev->c2h_work); + cancel_work_sync(&rtwdev->cancel_6ghz_probe_work); cancel_work_sync(&btc->eapol_notify_work); cancel_work_sync(&btc->arp_notify_work); cancel_work_sync(&btc->dhcp_notify_work); @@ -3444,6 +3524,7 @@ void rtw89_core_stop(struct rtw89_dev *rtwdev) cancel_delayed_work_sync(&rtwdev->coex_rfk_chk_work); cancel_delayed_work_sync(&rtwdev->cfo_track_work); cancel_delayed_work_sync(&rtwdev->forbid_ba_work); + cancel_delayed_work_sync(&rtwdev->antdiv_work); mutex_lock(&rtwdev->mutex); @@ -3479,6 +3560,7 @@ int rtw89_core_init(struct rtw89_dev *rtwdev) INIT_DELAYED_WORK(&rtwdev->coex_rfk_chk_work, rtw89_coex_rfk_chk_work); INIT_DELAYED_WORK(&rtwdev->cfo_track_work, rtw89_phy_cfo_track_work); INIT_DELAYED_WORK(&rtwdev->forbid_ba_work, rtw89_forbid_ba_work); + INIT_DELAYED_WORK(&rtwdev->antdiv_work, rtw89_phy_antdiv_work); rtwdev->txq_wq = alloc_workqueue("rtw89_tx_wq", WQ_UNBOUND | WQ_HIGHPRI, 0); if (!rtwdev->txq_wq) return -ENOMEM; @@ -3489,10 +3571,12 @@ int rtw89_core_init(struct rtw89_dev *rtwdev) rtwdev->total_sta_assoc = 0; rtw89_init_wait(&rtwdev->mcc.wait); + rtw89_init_wait(&rtwdev->mac.fw_ofld_wait); INIT_WORK(&rtwdev->c2h_work, rtw89_fw_c2h_work); INIT_WORK(&rtwdev->ips_work, rtw89_ips_work); INIT_WORK(&rtwdev->load_firmware_work, rtw89_load_firmware_work); + INIT_WORK(&rtwdev->cancel_6ghz_probe_work, rtw89_cancel_6ghz_probe_work); skb_queue_head_init(&rtwdev->c2h_queue); rtw89_core_ppdu_sts_init(rtwdev); @@ -3587,7 +3671,7 @@ static void rtw89_read_chip_ver(struct rtw89_dev *rtwdev) if (chip->chip_id == RTL8852B || chip->chip_id == RTL8851B) { ret = rtw89_mac_read_xtal_si(rtwdev, XTAL_SI_CV, &val); - if (!ret) + if (ret) return; rtwdev->hal.acv = u8_get_bits(val, XTAL_SI_ACV_MASK); @@ -3696,6 +3780,7 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev) { struct ieee80211_hw *hw = rtwdev->hw; struct rtw89_efuse *efuse = &rtwdev->efuse; + struct rtw89_hal *hal = &rtwdev->hal; int ret; int tx_headroom = IEEE80211_HT_CTL_LEN; @@ -3734,8 +3819,13 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev) BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO); - hw->wiphy->available_antennas_tx = BIT(rtwdev->chip->rf_path_num) - 1; - hw->wiphy->available_antennas_rx = BIT(rtwdev->chip->rf_path_num) - 1; + if (hal->ant_diversity) { + hw->wiphy->available_antennas_tx = 0x3; + hw->wiphy->available_antennas_rx = 0x3; + } else { + hw->wiphy->available_antennas_tx = BIT(rtwdev->chip->rf_path_num) - 1; + hw->wiphy->available_antennas_rx = BIT(rtwdev->chip->rf_path_num) - 1; + } hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS | WIPHY_FLAG_TDLS_EXTERNAL_SETUP | @@ -3763,7 +3853,12 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev) return ret; } - hw->wiphy->reg_notifier = rtw89_regd_notifier; + ret = rtw89_regd_setup(rtwdev); + if (ret) { + rtw89_err(rtwdev, "failed to set up regd\n"); + goto err_free_supported_band; + } + hw->wiphy->sar_capa = &rtw89_sar_capa; ret = ieee80211_register_hw(hw); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 6df386a38fb4..b60cd9852259 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -122,6 +122,13 @@ enum rtw89_cv { CHIP_CV_INVALID = CHIP_CV_MAX, }; +enum rtw89_bacam_ver { + RTW89_BACAM_V0, + RTW89_BACAM_V1, + + RTW89_BACAM_V0_EXT = 99, +}; + enum rtw89_core_tx_type { RTW89_CORE_TX_TYPE_DATA, RTW89_CORE_TX_TYPE_MGMT, @@ -551,6 +558,12 @@ struct rtw89_rx_phy_ppdu { u8 chan_idx; u8 ie; u16 rate; + struct { + bool has; + u8 avg_snr; + u8 evm_max; + u8 evm_min; + } ofdm; bool to_self; bool valid; }; @@ -2533,6 +2546,8 @@ struct rtw89_ra_report { }; DECLARE_EWMA(rssi, 10, 16); +DECLARE_EWMA(evm, 10, 16); +DECLARE_EWMA(snr, 10, 16); struct rtw89_ba_cam_entry { struct list_head list; @@ -2595,6 +2610,9 @@ struct rtw89_sta { u8 prev_rssi; struct ewma_rssi avg_rssi; struct ewma_rssi rssi[RF_PATH_MAX]; + struct ewma_snr avg_snr; + struct ewma_evm evm_min[RF_PATH_MAX]; + struct ewma_evm evm_max[RF_PATH_MAX]; struct rtw89_ampdu_params ampdu_params[IEEE80211_NUM_TIDS]; struct ieee80211_rx_status rx_status; u16 rx_hw_rate; @@ -3090,6 +3108,12 @@ struct rtw89_imr_info { u32 tmac_imr_set; }; +struct rtw89_xtal_info { + u32 xcap_reg; + u32 sc_xo_mask; + u32 sc_xi_mask; +}; + struct rtw89_rrsr_cfgs { struct rtw89_reg3_def ref_rate; struct rtw89_reg3_def rsc; @@ -3116,6 +3140,25 @@ struct rtw89_phy_ul_tb_info { u8 def_if_bandedge; }; +struct rtw89_antdiv_stats { + struct ewma_rssi cck_rssi_avg; + struct ewma_rssi ofdm_rssi_avg; + struct ewma_rssi non_legacy_rssi_avg; + u16 pkt_cnt_cck; + u16 pkt_cnt_ofdm; + u16 pkt_cnt_non_legacy; + u32 evm; +}; + +struct rtw89_antdiv_info { + struct rtw89_antdiv_stats target_stats; + struct rtw89_antdiv_stats main_stats; + struct rtw89_antdiv_stats aux_stats; + u8 training_count; + u8 rssi_pre; + bool get_stats; +}; + struct rtw89_chip_info { enum rtw89_core_chip_id chip_id; const struct rtw89_chip_ops *ops; @@ -3123,6 +3166,7 @@ struct rtw89_chip_info { u8 fw_format_max; bool try_ce_fw; u32 fifo_size; + bool small_fifo_size; u32 dle_scc_rsvd_size; u16 max_amsdu_limit; bool dis_2g_40m_ul_ofdma; @@ -3135,6 +3179,7 @@ struct rtw89_chip_info { u8 support_chanctx_num; u8 support_bands; bool support_bw160; + bool support_unii4; bool support_ul_tb_ctrl; bool hw_sec_hdr; u8 rf_path_num; @@ -3145,7 +3190,7 @@ struct rtw89_chip_info { u8 scam_num; u8 bacam_num; u8 bacam_dynamic_num; - bool bacam_v1; + enum rtw89_bacam_ver bacam_ver; u8 sec_ctrl_efuse_size; u32 physical_efuse_size; @@ -3162,6 +3207,7 @@ struct rtw89_chip_info { const struct rtw89_phy_table *bb_gain_table; const struct rtw89_phy_table *rf_table[RF_PATH_MAX]; const struct rtw89_phy_table *nctl_table; + const struct rtw89_rfk_tbl *nctl_post_table; const struct rtw89_txpwr_table *byr_table; const struct rtw89_phy_dig_gain_table *dig_table; const struct rtw89_dig_regs *dig_regs; @@ -3215,6 +3261,7 @@ struct rtw89_chip_info { u32 dma_ch_mask; u32 edcca_lvl_reg; const struct wiphy_wowlan_support *wowlan_stub; + const struct rtw89_xtal_info *xtal_info; }; union rtw89_bus_info { @@ -3248,14 +3295,6 @@ enum rtw89_host_rpr_mode { RTW89_RPR_MODE_STF }; -struct rtw89_mac_info { - struct rtw89_dle_info dle_info; - struct rtw89_hfc_param hfc_param; - enum rtw89_qta_mode qta_mode; - u8 rpwm_seq_num; - u8 cpwm_seq_num; -}; - #define RTW89_COMPLETION_BUF_SIZE 24 #define RTW89_WAIT_COND_IDLE UINT_MAX @@ -3278,6 +3317,17 @@ static inline void rtw89_init_wait(struct rtw89_wait_info *wait) atomic_set(&wait->cond, RTW89_WAIT_COND_IDLE); } +struct rtw89_mac_info { + struct rtw89_dle_info dle_info; + struct rtw89_hfc_param hfc_param; + enum rtw89_qta_mode qta_mode; + u8 rpwm_seq_num; + u8 cpwm_seq_num; + + /* see RTW89_FW_OFLD_WAIT_COND series for wait condition */ + struct rtw89_wait_info fw_ofld_wait; +}; + enum rtw89_fw_type { RTW89_FW_NORMAL = 1, RTW89_FW_WOWLAN = 3, @@ -3423,6 +3473,8 @@ struct rtw89_hal { u8 tx_nss; u8 rx_nss; bool tx_path_diversity; + bool ant_diversity; + bool ant_diversity_fixed; bool support_cckpd; bool support_igi; atomic_t roc_entity_idx; @@ -3888,12 +3940,14 @@ enum rtw89_ser_rcvy_step { RTW89_SER_DRV_STOP_RX, RTW89_SER_DRV_STOP_RUN, RTW89_SER_HAL_STOP_DMA, + RTW89_SER_SUPPRESS_LOG, RTW89_NUM_OF_SER_FLAGS }; struct rtw89_ser { u8 state; u8 alarm_event; + bool prehandle_l1; struct work_struct ser_hdl_work; struct delayed_work ser_alarm_work; @@ -4054,6 +4108,7 @@ struct rtw89_dev { struct work_struct c2h_work; struct work_struct ips_work; struct work_struct load_firmware_work; + struct work_struct cancel_6ghz_probe_work; struct list_head early_h2c_list; @@ -4086,6 +4141,7 @@ struct rtw89_dev { struct rtw89_phy_bb_gain_info bb_gain; struct rtw89_phy_efuse_gain efuse_gain; struct rtw89_phy_ul_tb_info ul_tb_info; + struct rtw89_antdiv_info antdiv; struct delayed_work track_work; struct delayed_work coex_act1_work; @@ -4094,6 +4150,7 @@ struct rtw89_dev { struct delayed_work cfo_track_work; struct delayed_work forbid_ba_work; struct delayed_work roc_work; + struct delayed_work antdiv_work; struct rtw89_ppdu_sts_info ppdu_sts; u8 total_sta_assoc; bool scanning; @@ -4990,6 +5047,7 @@ int rtw89_core_release_sta_ba_entry(struct rtw89_dev *rtwdev, void rtw89_vif_type_mapping(struct ieee80211_vif *vif, bool assoc); int rtw89_chip_info_setup(struct rtw89_dev *rtwdev); bool rtw89_ra_report_to_bitrate(struct rtw89_dev *rtwdev, u8 rpt_rate, u16 *bitrate); +int rtw89_regd_setup(struct rtw89_dev *rtwdev); int rtw89_regd_init(struct rtw89_dev *rtwdev, void (*reg_notifier)(struct wiphy *wiphy, struct regulatory_request *request)); void rtw89_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request); diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index 1e5b7a998716..6f418f14ec3f 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -3206,7 +3206,11 @@ static void rtw89_sta_info_get_iter(void *data, struct ieee80211_sta *sta) struct seq_file *m = (struct seq_file *)data; struct rtw89_dev *rtwdev = rtwsta->rtwdev; struct rtw89_hal *hal = &rtwdev->hal; + u8 ant_num = hal->ant_diversity ? 2 : rtwdev->chip->rf_path_num; + bool ant_asterisk = hal->tx_path_diversity || hal->ant_diversity; + u8 evm_min, evm_max; u8 rssi; + u8 snr; int i; seq_printf(m, "TX rate [%d]: ", rtwsta->mac_id); @@ -3256,13 +3260,27 @@ static void rtw89_sta_info_get_iter(void *data, struct ieee80211_sta *sta) rssi = ewma_rssi_read(&rtwsta->avg_rssi); seq_printf(m, "RSSI: %d dBm (raw=%d, prev=%d) [", RTW89_RSSI_RAW_TO_DBM(rssi), rssi, rtwsta->prev_rssi); - for (i = 0; i < rtwdev->chip->rf_path_num; i++) { + for (i = 0; i < ant_num; i++) { rssi = ewma_rssi_read(&rtwsta->rssi[i]); seq_printf(m, "%d%s%s", RTW89_RSSI_RAW_TO_DBM(rssi), - hal->tx_path_diversity && (hal->antenna_tx & BIT(i)) ? "*" : "", - i + 1 == rtwdev->chip->rf_path_num ? "" : ", "); + ant_asterisk && (hal->antenna_tx & BIT(i)) ? "*" : "", + i + 1 == ant_num ? "" : ", "); } seq_puts(m, "]\n"); + + seq_puts(m, "EVM: ["); + for (i = 0; i < (hal->ant_diversity ? 2 : 1); i++) { + evm_min = ewma_evm_read(&rtwsta->evm_min[i]); + evm_max = ewma_evm_read(&rtwsta->evm_max[i]); + + seq_printf(m, "%s(%2u.%02u, %2u.%02u)", i == 0 ? "" : " ", + evm_min >> 2, (evm_min & 0x3) * 25, + evm_max >> 2, (evm_max & 0x3) * 25); + } + seq_puts(m, "]\t"); + + snr = ewma_snr_read(&rtwsta->avg_snr); + seq_printf(m, "SNR: %u\n", snr); } static void diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index b9b675bf9d05..ad277f22b197 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -14,6 +14,8 @@ static void rtw89_fw_c2h_cmd_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb); +static int rtw89_h2c_tx_and_wait(struct rtw89_dev *rtwdev, struct sk_buff *skb, + struct rtw89_wait_info *wait, unsigned int cond); static struct sk_buff *rtw89_fw_h2c_alloc_skb(struct rtw89_dev *rtwdev, u32 len, bool header) @@ -807,7 +809,7 @@ int rtw89_fw_h2c_ba_cam(struct rtw89_dev *rtwdev, struct rtw89_sta *rtwsta, } skb_put(skb, H2C_BA_CAM_LEN); SET_BA_CAM_MACID(skb->data, macid); - if (chip->bacam_v1) + if (chip->bacam_ver == RTW89_BACAM_V0_EXT) SET_BA_CAM_ENTRY_IDX_V1(skb->data, entry_idx); else SET_BA_CAM_ENTRY_IDX(skb->data, entry_idx); @@ -823,7 +825,7 @@ int rtw89_fw_h2c_ba_cam(struct rtw89_dev *rtwdev, struct rtw89_sta *rtwsta, SET_BA_CAM_INIT_REQ(skb->data, 1); SET_BA_CAM_SSN(skb->data, params->ssn); - if (chip->bacam_v1) { + if (chip->bacam_ver == RTW89_BACAM_V0_EXT) { SET_BA_CAM_STD_EN(skb->data, 1); SET_BA_CAM_BAND(skb->data, rtwvif->mac_idx); } @@ -848,8 +850,8 @@ fail: return ret; } -static int rtw89_fw_h2c_init_dynamic_ba_cam_v1(struct rtw89_dev *rtwdev, - u8 entry_idx, u8 uid) +static int rtw89_fw_h2c_init_ba_cam_v0_ext(struct rtw89_dev *rtwdev, + u8 entry_idx, u8 uid) { struct sk_buff *skb; int ret; @@ -886,7 +888,7 @@ fail: return ret; } -void rtw89_fw_h2c_init_ba_cam_v1(struct rtw89_dev *rtwdev) +void rtw89_fw_h2c_init_dynamic_ba_cam_v0_ext(struct rtw89_dev *rtwdev) { const struct rtw89_chip_info *chip = rtwdev->chip; u8 entry_idx = chip->bacam_num; @@ -894,7 +896,7 @@ void rtw89_fw_h2c_init_ba_cam_v1(struct rtw89_dev *rtwdev) int i; for (i = 0; i < chip->bacam_dynamic_num; i++) { - rtw89_fw_h2c_init_dynamic_ba_cam_v1(rtwdev, entry_idx, uid); + rtw89_fw_h2c_init_ba_cam_v0_ext(rtwdev, entry_idx, uid); entry_idx++; uid++; } @@ -997,8 +999,8 @@ void rtw89_fw_release_general_pkt_list_vif(struct rtw89_dev *rtwdev, list_for_each_entry_safe(info, tmp, pkt_list, list) { if (notify_fw) rtw89_fw_h2c_del_pkt_offload(rtwdev, info->id); - rtw89_core_release_bit_map(rtwdev->pkt_offload, - info->id); + else + rtw89_core_release_bit_map(rtwdev->pkt_offload, info->id); list_del(&info->list); kfree(info); } @@ -2440,7 +2442,9 @@ fail: #define H2C_LEN_PKT_OFLD 4 int rtw89_fw_h2c_del_pkt_offload(struct rtw89_dev *rtwdev, u8 id) { + struct rtw89_wait_info *wait = &rtwdev->mac.fw_ofld_wait; struct sk_buff *skb; + unsigned int cond; u8 *cmd; int ret; @@ -2460,23 +2464,26 @@ int rtw89_fw_h2c_del_pkt_offload(struct rtw89_dev *rtwdev, u8 id) H2C_FUNC_PACKET_OFLD, 1, 1, H2C_LEN_PKT_OFLD); - ret = rtw89_h2c_tx(rtwdev, skb, false); + cond = RTW89_FW_OFLD_WAIT_COND_PKT_OFLD(id, RTW89_PKT_OFLD_OP_DEL); + + ret = rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); if (ret) { - rtw89_err(rtwdev, "failed to send h2c\n"); - goto fail; + rtw89_debug(rtwdev, RTW89_DBG_FW, + "failed to del pkt ofld: id %d, ret %d\n", + id, ret); + return ret; } + rtw89_core_release_bit_map(rtwdev->pkt_offload, id); return 0; -fail: - dev_kfree_skb_any(skb); - - return ret; } int rtw89_fw_h2c_add_pkt_offload(struct rtw89_dev *rtwdev, u8 *id, struct sk_buff *skb_ofld) { + struct rtw89_wait_info *wait = &rtwdev->mac.fw_ofld_wait; struct sk_buff *skb; + unsigned int cond; u8 *cmd; u8 alloc_id; int ret; @@ -2507,27 +2514,29 @@ int rtw89_fw_h2c_add_pkt_offload(struct rtw89_dev *rtwdev, u8 *id, H2C_FUNC_PACKET_OFLD, 1, 1, H2C_LEN_PKT_OFLD + skb_ofld->len); - ret = rtw89_h2c_tx(rtwdev, skb, false); + cond = RTW89_FW_OFLD_WAIT_COND_PKT_OFLD(alloc_id, RTW89_PKT_OFLD_OP_ADD); + + ret = rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); if (ret) { - rtw89_err(rtwdev, "failed to send h2c\n"); + rtw89_debug(rtwdev, RTW89_DBG_FW, + "failed to add pkt ofld: id %d, ret %d\n", + alloc_id, ret); rtw89_core_release_bit_map(rtwdev->pkt_offload, alloc_id); - goto fail; + return ret; } return 0; -fail: - dev_kfree_skb_any(skb); - - return ret; } #define H2C_LEN_SCAN_LIST_OFFLOAD 4 int rtw89_fw_h2c_scan_list_offload(struct rtw89_dev *rtwdev, int len, struct list_head *chan_list) { + struct rtw89_wait_info *wait = &rtwdev->mac.fw_ofld_wait; struct rtw89_mac_chinfo *ch_info; struct sk_buff *skb; int skb_len = H2C_LEN_SCAN_LIST_OFFLOAD + len * RTW89_MAC_CHINFO_SIZE; + unsigned int cond; u8 *cmd; int ret; @@ -2574,27 +2583,27 @@ int rtw89_fw_h2c_scan_list_offload(struct rtw89_dev *rtwdev, int len, H2C_CAT_MAC, H2C_CL_MAC_FW_OFLD, H2C_FUNC_ADD_SCANOFLD_CH, 1, 1, skb_len); - ret = rtw89_h2c_tx(rtwdev, skb, false); + cond = RTW89_FW_OFLD_WAIT_COND(0, H2C_FUNC_ADD_SCANOFLD_CH); + + ret = rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); if (ret) { - rtw89_err(rtwdev, "failed to send h2c\n"); - goto fail; + rtw89_debug(rtwdev, RTW89_DBG_FW, "failed to add scan ofld ch\n"); + return ret; } return 0; -fail: - dev_kfree_skb_any(skb); - - return ret; } int rtw89_fw_h2c_scan_offload(struct rtw89_dev *rtwdev, struct rtw89_scan_option *option, struct rtw89_vif *rtwvif) { + struct rtw89_wait_info *wait = &rtwdev->mac.fw_ofld_wait; struct rtw89_chan *op = &rtwdev->scan_info.op_chan; struct rtw89_h2c_scanofld *h2c; u32 len = sizeof(*h2c); struct sk_buff *skb; + unsigned int cond; int ret; skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); @@ -2633,17 +2642,15 @@ int rtw89_fw_h2c_scan_offload(struct rtw89_dev *rtwdev, H2C_FUNC_SCANOFLD, 1, 1, len); - ret = rtw89_h2c_tx(rtwdev, skb, false); + cond = RTW89_FW_OFLD_WAIT_COND(0, H2C_FUNC_SCANOFLD); + + ret = rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); if (ret) { - rtw89_err(rtwdev, "failed to send h2c\n"); - goto fail; + rtw89_debug(rtwdev, RTW89_DBG_FW, "failed to scan ofld\n"); + return ret; } return 0; -fail: - dev_kfree_skb_any(skb); - - return ret; } int rtw89_fw_h2c_rf_reg(struct rtw89_dev *rtwdev, @@ -3019,9 +3026,8 @@ static void rtw89_release_pkt_list(struct rtw89_dev *rtwdev) continue; list_for_each_entry_safe(info, tmp, &pkt_list[idx], list) { - rtw89_fw_h2c_del_pkt_offload(rtwdev, info->id); - rtw89_core_release_bit_map(rtwdev->pkt_offload, - info->id); + if (test_bit(info->id, rtwdev->pkt_offload)) + rtw89_fw_h2c_del_pkt_offload(rtwdev, info->id); list_del(&info->list); kfree(info); } diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 675f85c41471..048283750a2d 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -138,8 +138,13 @@ enum rtw89_pkt_offload_op { RTW89_PKT_OFLD_OP_ADD, RTW89_PKT_OFLD_OP_DEL, RTW89_PKT_OFLD_OP_READ, + + NUM_OF_RTW89_PKT_OFFLOAD_OP, }; +#define RTW89_PKT_OFLD_WAIT_TAG(pkt_id, pkt_op) \ + ((pkt_id) * NUM_OF_RTW89_PKT_OFFLOAD_OP + (pkt_op)) + enum rtw89_scanofld_notify_reason { RTW89_SCAN_DWELL_NOTIFY, RTW89_SCAN_PRE_TX_NOTIFY, @@ -277,6 +282,7 @@ struct rtw89_pktofld_info { u8 ssid_len; u8 bssid[ETH_ALEN]; u16 channel_6ghz; + bool cancel; }; static inline void RTW89_SET_FWCMD_RA_IS_DIS(void *cmd, u32 val) @@ -3215,16 +3221,17 @@ static inline struct rtw89_fw_c2h_attr *RTW89_SKB_C2H_CB(struct sk_buff *skb) #define RTW89_GET_C2H_LOG_SRT_PRT(c2h) (char *)((__le32 *)(c2h) + 2) #define RTW89_GET_C2H_LOG_LEN(len) ((len) - RTW89_C2H_HEADER_LEN) -#define RTW89_GET_MAC_C2H_DONE_ACK_CAT(c2h) \ - le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(1, 0)) -#define RTW89_GET_MAC_C2H_DONE_ACK_CLASS(c2h) \ - le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(7, 2)) -#define RTW89_GET_MAC_C2H_DONE_ACK_FUNC(c2h) \ - le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(15, 8)) -#define RTW89_GET_MAC_C2H_DONE_ACK_H2C_RETURN(c2h) \ - le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(23, 16)) -#define RTW89_GET_MAC_C2H_DONE_ACK_H2C_SEQ(c2h) \ - le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(31, 24)) +struct rtw89_c2h_done_ack { + __le32 w0; + __le32 w1; + __le32 w2; +} __packed; + +#define RTW89_C2H_DONE_ACK_W2_CAT GENMASK(1, 0) +#define RTW89_C2H_DONE_ACK_W2_CLASS GENMASK(7, 2) +#define RTW89_C2H_DONE_ACK_W2_FUNC GENMASK(15, 8) +#define RTW89_C2H_DONE_ACK_W2_H2C_RETURN GENMASK(23, 16) +#define RTW89_C2H_DONE_ACK_W2_H2C_SEQ GENMASK(31, 24) #define RTW89_GET_MAC_C2H_REV_ACK_CAT(c2h) \ le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(1, 0)) @@ -3339,6 +3346,16 @@ static_assert(sizeof(struct rtw89_mac_mcc_tsf_rpt) <= RTW89_COMPLETION_BUF_SIZE) #define RTW89_GET_MAC_C2H_MCC_STATUS_RPT_TSF_HIGH(c2h) \ le32_get_bits(*((const __le32 *)(c2h) + 4), GENMASK(31, 0)) +struct rtw89_c2h_pkt_ofld_rsp { + __le32 w0; + __le32 w1; + __le32 w2; +} __packed; + +#define RTW89_C2H_PKT_OFLD_RSP_W2_PTK_ID GENMASK(7, 0) +#define RTW89_C2H_PKT_OFLD_RSP_W2_PTK_OP GENMASK(10, 8) +#define RTW89_C2H_PKT_OFLD_RSP_W2_PTK_LEN GENMASK(31, 16) + struct rtw89_h2c_bcnfltr { __le32 w0; } __packed; @@ -3497,17 +3514,28 @@ struct rtw89_fw_h2c_rf_reg_info { /* CLASS 9 - FW offload */ #define H2C_CL_MAC_FW_OFLD 0x9 -#define H2C_FUNC_PACKET_OFLD 0x1 -#define H2C_FUNC_MAC_MACID_PAUSE 0x8 -#define H2C_FUNC_USR_EDCA 0xF -#define H2C_FUNC_TSF32_TOGL 0x10 -#define H2C_FUNC_OFLD_CFG 0x14 -#define H2C_FUNC_ADD_SCANOFLD_CH 0x16 -#define H2C_FUNC_SCANOFLD 0x17 -#define H2C_FUNC_PKT_DROP 0x1b -#define H2C_FUNC_CFG_BCNFLTR 0x1e -#define H2C_FUNC_OFLD_RSSI 0x1f -#define H2C_FUNC_OFLD_TP 0x20 +enum rtw89_fw_ofld_h2c_func { + H2C_FUNC_PACKET_OFLD = 0x1, + H2C_FUNC_MAC_MACID_PAUSE = 0x8, + H2C_FUNC_USR_EDCA = 0xF, + H2C_FUNC_TSF32_TOGL = 0x10, + H2C_FUNC_OFLD_CFG = 0x14, + H2C_FUNC_ADD_SCANOFLD_CH = 0x16, + H2C_FUNC_SCANOFLD = 0x17, + H2C_FUNC_PKT_DROP = 0x1b, + H2C_FUNC_CFG_BCNFLTR = 0x1e, + H2C_FUNC_OFLD_RSSI = 0x1f, + H2C_FUNC_OFLD_TP = 0x20, + + NUM_OF_RTW89_FW_OFLD_H2C_FUNC, +}; + +#define RTW89_FW_OFLD_WAIT_COND(tag, func) \ + ((tag) * NUM_OF_RTW89_FW_OFLD_H2C_FUNC + (func)) + +#define RTW89_FW_OFLD_WAIT_COND_PKT_OFLD(pkt_id, pkt_op) \ + RTW89_FW_OFLD_WAIT_COND(RTW89_PKT_OFLD_WAIT_TAG(pkt_id, pkt_op), \ + H2C_FUNC_PACKET_OFLD) /* CLASS 10 - Security CAM */ #define H2C_CL_MAC_SEC_CAM 0xa @@ -3648,7 +3676,7 @@ void rtw89_fw_release_general_pkt_list_vif(struct rtw89_dev *rtwdev, void rtw89_fw_release_general_pkt_list(struct rtw89_dev *rtwdev, bool notify_fw); int rtw89_fw_h2c_ba_cam(struct rtw89_dev *rtwdev, struct rtw89_sta *rtwsta, bool valid, struct ieee80211_ampdu_params *params); -void rtw89_fw_h2c_init_ba_cam_v1(struct rtw89_dev *rtwdev); +void rtw89_fw_h2c_init_dynamic_ba_cam_v0_ext(struct rtw89_dev *rtwdev); int rtw89_fw_h2c_lps_parm(struct rtw89_dev *rtwdev, struct rtw89_lps_parm *lps_param); @@ -3711,8 +3739,8 @@ static inline void rtw89_fw_h2c_init_ba_cam(struct rtw89_dev *rtwdev) { const struct rtw89_chip_info *chip = rtwdev->chip; - if (chip->bacam_v1) - rtw89_fw_h2c_init_ba_cam_v1(rtwdev); + if (chip->bacam_ver == RTW89_BACAM_V0_EXT) + rtw89_fw_h2c_init_dynamic_ba_cam_v0_ext(rtwdev); } #endif diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 512de491a064..0833a9eb88f3 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -644,6 +644,39 @@ static void rtw89_mac_dump_err_status(struct rtw89_dev *rtwdev, rtw89_info(rtwdev, "<---\n"); } +static bool rtw89_mac_suppress_log(struct rtw89_dev *rtwdev, u32 err) +{ + struct rtw89_ser *ser = &rtwdev->ser; + u32 dmac_err, imr, isr; + int ret; + + if (rtwdev->chip->chip_id == RTL8852C) { + ret = rtw89_mac_check_mac_en(rtwdev, 0, RTW89_DMAC_SEL); + if (ret) + return true; + + if (err == MAC_AX_ERR_L1_ERR_DMAC) { + dmac_err = rtw89_read32(rtwdev, R_AX_DMAC_ERR_ISR); + imr = rtw89_read32(rtwdev, R_AX_TXPKTCTL_B0_ERRFLAG_IMR); + isr = rtw89_read32(rtwdev, R_AX_TXPKTCTL_B0_ERRFLAG_ISR); + + if ((dmac_err & B_AX_TXPKTCTRL_ERR_FLAG) && + ((isr & imr) & B_AX_B0_ISR_ERR_CMDPSR_FRZTO)) { + set_bit(RTW89_SER_SUPPRESS_LOG, ser->flags); + return true; + } + } else if (err == MAC_AX_ERR_L1_RESET_DISABLE_DMAC_DONE) { + if (test_bit(RTW89_SER_SUPPRESS_LOG, ser->flags)) + return true; + } else if (err == MAC_AX_ERR_L1_RESET_RECOVERY_DONE) { + if (test_and_clear_bit(RTW89_SER_SUPPRESS_LOG, ser->flags)) + return true; + } + } + + return false; +} + u32 rtw89_mac_get_err_status(struct rtw89_dev *rtwdev) { u32 err, err_scnr; @@ -667,6 +700,9 @@ u32 rtw89_mac_get_err_status(struct rtw89_dev *rtwdev) else if (err_scnr == RTW89_RXI300_ERROR) err = MAC_AX_ERR_RXI300; + if (rtw89_mac_suppress_log(rtwdev, err)) + return err; + rtw89_fw_st_dbg_dump(rtwdev); rtw89_mac_dump_err_status(rtwdev, err); @@ -676,6 +712,7 @@ EXPORT_SYMBOL(rtw89_mac_get_err_status); int rtw89_mac_set_err_status(struct rtw89_dev *rtwdev, u32 err) { + struct rtw89_ser *ser = &rtwdev->ser; u32 halt; int ret = 0; @@ -692,6 +729,11 @@ int rtw89_mac_set_err_status(struct rtw89_dev *rtwdev, u32 err) } rtw89_write32(rtwdev, R_AX_HALT_H2C, err); + + if (ser->prehandle_l1 && + (err == MAC_AX_ERR_L1_DISABLE_EN || err == MAC_AX_ERR_L1_RCVY_EN)) + return 0; + rtw89_write32(rtwdev, R_AX_HALT_H2C_CTRL, B_AX_HALT_H2C_TRIGGER); return 0; @@ -1479,6 +1521,8 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .ple_qt_52a_wow = {264, 0, 32, 20, 64, 13, 1005, 0, 64, 128, 120,}, /* 8852B PCIE WOW */ .ple_qt_52b_wow = {147, 0, 16, 20, 157, 13, 133, 0, 172, 14, 24, 0,}, + /* 8851B PCIE WOW */ + .ple_qt_51b_wow = {147, 0, 16, 20, 157, 13, 133, 0, 172, 14, 24, 0,}, }; EXPORT_SYMBOL(rtw89_mac_size); @@ -2602,6 +2646,7 @@ static int rtw89_mac_read_phycap(struct rtw89_dev *rtwdev, int rtw89_mac_setup_phycap(struct rtw89_dev *rtwdev) { + struct rtw89_efuse *efuse = &rtwdev->efuse; struct rtw89_hal *hal = &rtwdev->hal; const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_mac_c2h_info c2h_info = {0}; @@ -2633,6 +2678,13 @@ int rtw89_mac_setup_phycap(struct rtw89_dev *rtwdev) hal->tx_path_diversity = true; } + if (chip->rf_path_num == 1) { + hal->antenna_tx = RF_A; + hal->antenna_rx = RF_A; + if ((efuse->rfe_type % 3) == 2) + hal->ant_diversity = true; + } + rtw89_debug(rtwdev, RTW89_DBG_FW, "phycap hal/phy/chip: tx_nss=0x%x/0x%x/0x%x rx_nss=0x%x/0x%x/0x%x\n", hal->tx_nss, tx_nss, chip->tx_nss, @@ -2641,6 +2693,7 @@ int rtw89_mac_setup_phycap(struct rtw89_dev *rtwdev) "ant num/bitmap: tx=%d/0x%x rx=%d/0x%x\n", tx_ant, hal->antenna_tx, rx_ant, hal->antenna_rx); rtw89_debug(rtwdev, RTW89_DBG_FW, "TX path diversity=%d\n", hal->tx_path_diversity); + rtw89_debug(rtwdev, RTW89_DBG_FW, "Antenna diversity=%d\n", hal->ant_diversity); return 0; } @@ -4331,6 +4384,8 @@ rtw89_mac_c2h_bcn_fltr_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, static void rtw89_mac_c2h_rec_ack(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) { + /* N.B. This will run in interrupt context. */ + rtw89_debug(rtwdev, RTW89_DBG_FW, "C2H rev ack recv, cat: %d, class: %d, func: %d, seq : %d\n", RTW89_GET_MAC_C2H_REV_ACK_CAT(c2h->data), @@ -4340,15 +4395,44 @@ rtw89_mac_c2h_rec_ack(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) } static void -rtw89_mac_c2h_done_ack(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) -{ +rtw89_mac_c2h_done_ack(struct rtw89_dev *rtwdev, struct sk_buff *skb_c2h, u32 len) +{ + /* N.B. This will run in interrupt context. */ + struct rtw89_wait_info *fw_ofld_wait = &rtwdev->mac.fw_ofld_wait; + const struct rtw89_c2h_done_ack *c2h = + (const struct rtw89_c2h_done_ack *)skb_c2h->data; + u8 h2c_cat = le32_get_bits(c2h->w2, RTW89_C2H_DONE_ACK_W2_CAT); + u8 h2c_class = le32_get_bits(c2h->w2, RTW89_C2H_DONE_ACK_W2_CLASS); + u8 h2c_func = le32_get_bits(c2h->w2, RTW89_C2H_DONE_ACK_W2_FUNC); + u8 h2c_return = le32_get_bits(c2h->w2, RTW89_C2H_DONE_ACK_W2_H2C_RETURN); + u8 h2c_seq = le32_get_bits(c2h->w2, RTW89_C2H_DONE_ACK_W2_H2C_SEQ); + struct rtw89_completion_data data = {}; + unsigned int cond; + rtw89_debug(rtwdev, RTW89_DBG_FW, "C2H done ack recv, cat: %d, class: %d, func: %d, ret: %d, seq : %d\n", - RTW89_GET_MAC_C2H_DONE_ACK_CAT(c2h->data), - RTW89_GET_MAC_C2H_DONE_ACK_CLASS(c2h->data), - RTW89_GET_MAC_C2H_DONE_ACK_FUNC(c2h->data), - RTW89_GET_MAC_C2H_DONE_ACK_H2C_RETURN(c2h->data), - RTW89_GET_MAC_C2H_DONE_ACK_H2C_SEQ(c2h->data)); + h2c_cat, h2c_class, h2c_func, h2c_return, h2c_seq); + + if (h2c_cat != H2C_CAT_MAC) + return; + + switch (h2c_class) { + default: + return; + case H2C_CL_MAC_FW_OFLD: + switch (h2c_func) { + default: + return; + case H2C_FUNC_ADD_SCANOFLD_CH: + case H2C_FUNC_SCANOFLD: + cond = RTW89_FW_OFLD_WAIT_COND(0, h2c_func); + break; + } + + data.err = !!h2c_return; + rtw89_complete_cond(fw_ofld_wait, cond, &data); + return; + } } static void @@ -4364,9 +4448,22 @@ rtw89_mac_c2h_bcn_cnt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) } static void -rtw89_mac_c2h_pkt_ofld_rsp(struct rtw89_dev *rtwdev, struct sk_buff *c2h, +rtw89_mac_c2h_pkt_ofld_rsp(struct rtw89_dev *rtwdev, struct sk_buff *skb_c2h, u32 len) { + struct rtw89_wait_info *wait = &rtwdev->mac.fw_ofld_wait; + const struct rtw89_c2h_pkt_ofld_rsp *c2h = + (const struct rtw89_c2h_pkt_ofld_rsp *)skb_c2h->data; + u16 pkt_len = le32_get_bits(c2h->w2, RTW89_C2H_PKT_OFLD_RSP_W2_PTK_LEN); + u8 pkt_id = le32_get_bits(c2h->w2, RTW89_C2H_PKT_OFLD_RSP_W2_PTK_ID); + u8 pkt_op = le32_get_bits(c2h->w2, RTW89_C2H_PKT_OFLD_RSP_W2_PTK_OP); + struct rtw89_completion_data data = {}; + unsigned int cond; + + data.err = !pkt_len; + cond = RTW89_FW_OFLD_WAIT_COND_PKT_OFLD(pkt_id, pkt_op); + + rtw89_complete_cond(wait, cond, &data); } static void @@ -4574,6 +4671,21 @@ bool rtw89_mac_c2h_chk_atomic(struct rtw89_dev *rtwdev, u8 class, u8 func) switch (class) { default: return false; + case RTW89_MAC_C2H_CLASS_INFO: + switch (func) { + default: + return false; + case RTW89_MAC_C2H_FUNC_REC_ACK: + case RTW89_MAC_C2H_FUNC_DONE_ACK: + return true; + } + case RTW89_MAC_C2H_CLASS_OFLD: + switch (func) { + default: + return false; + case RTW89_MAC_C2H_FUNC_PKT_OFLD_RSP: + return true; + } case RTW89_MAC_C2H_CLASS_MCC: return true; } diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 6ba633ccdd03..0f380b6fb97b 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -642,6 +642,7 @@ enum mac_ax_err_info { MAC_AX_ERR_L0_PROMOTE_TO_L1 = 0x0010, /* L1 */ + MAC_AX_ERR_L1_PREERR_DMAC = 0x999, MAC_AX_ERR_L1_ERR_DMAC = 0x1000, MAC_AX_ERR_L1_RESET_DISABLE_DMAC_DONE = 0x1001, MAC_AX_ERR_L1_RESET_RECOVERY_DONE = 0x1002, @@ -780,6 +781,7 @@ enum mac_ax_err_info { MAC_AX_ERR_L1_RCVY_EN = 0x0002, MAC_AX_ERR_L1_RCVY_STOP_REQ = 0x0003, MAC_AX_ERR_L1_RCVY_START_REQ = 0x0004, + MAC_AX_ERR_L1_RESET_START_DMAC = 0x000A, MAC_AX_ERR_L0_CFG_NOTIFY = 0x0010, MAC_AX_ERR_L0_CFG_DIS_NOTIFY = 0x0011, MAC_AX_ERR_L0_CFG_HANDSHAKE = 0x0012, @@ -819,6 +821,7 @@ struct rtw89_mac_size_set { const struct rtw89_ple_quota ple_qt58; const struct rtw89_ple_quota ple_qt_52a_wow; const struct rtw89_ple_quota ple_qt_52b_wow; + const struct rtw89_ple_quota ple_qt_51b_wow; }; extern const struct rtw89_mac_size_set rtw89_mac_size; diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index ee4588b61b8f..f40d70f016e4 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -762,13 +762,18 @@ int rtw89_ops_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) struct rtw89_dev *rtwdev = hw->priv; struct rtw89_hal *hal = &rtwdev->hal; - if (rx_ant != hw->wiphy->available_antennas_rx && rx_ant != hal->antenna_rx) + if (hal->ant_diversity) { + if (tx_ant != rx_ant || hweight32(tx_ant) != 1) + return -EINVAL; + } else if (rx_ant != hw->wiphy->available_antennas_rx && rx_ant != hal->antenna_rx) { return -EINVAL; + } mutex_lock(&rtwdev->mutex); hal->antenna_tx = tx_ant; hal->antenna_rx = rx_ant; hal->tx_path_diversity = false; + hal->ant_diversity_fixed = true; mutex_unlock(&rtwdev->mutex); return 0; diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c index 70b4754667c9..92bfef942d3a 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.c +++ b/drivers/net/wireless/realtek/rtw89/pci.c @@ -1003,10 +1003,10 @@ static u32 __rtw89_pci_check_and_reclaim_tx_resource(struct rtw89_dev *rtwdev, min_cnt = min(bd_cnt, wd_cnt); if (min_cnt == 0) { /* This message can be frequently shown in low power mode or - * high traffic with 8852B, and we have recognized it as normal + * high traffic with small FIFO chips, and we have recognized it as normal * behavior, so print with mask RTW89_DBG_TXRX in these situations. */ - if (rtwpci->low_power || chip->chip_id == RTL8852B) + if (rtwpci->low_power || chip->small_fifo_size) debug_mask = RTW89_DBG_TXRX; else debug_mask = RTW89_DBG_UNEXP; @@ -3216,11 +3216,16 @@ static void rtw89_pci_clear_resource(struct rtw89_dev *rtwdev, void rtw89_pci_config_intr_mask(struct rtw89_dev *rtwdev) { struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv; + const struct rtw89_chip_info *chip = rtwdev->chip; + u32 hs0isr_ind_int_en = B_AX_HS0ISR_IND_INT_EN; + + if (chip->chip_id == RTL8851B) + hs0isr_ind_int_en = B_AX_HS0ISR_IND_INT_EN_WKARND; rtwpci->halt_c2h_intrs = B_AX_HALT_C2H_INT_EN | 0; if (rtwpci->under_recovery) { - rtwpci->intrs[0] = B_AX_HS0ISR_IND_INT_EN; + rtwpci->intrs[0] = hs0isr_ind_int_en; rtwpci->intrs[1] = 0; } else { rtwpci->intrs[0] = B_AX_TXDMA_STUCK_INT_EN | @@ -3230,7 +3235,7 @@ void rtw89_pci_config_intr_mask(struct rtw89_dev *rtwdev) B_AX_RXDMA_STUCK_INT_EN | B_AX_RDU_INT_EN | B_AX_RPQBD_FULL_INT_EN | - B_AX_HS0ISR_IND_INT_EN; + hs0isr_ind_int_en; rtwpci->intrs[1] = B_AX_HC10ISR_IND_INT_EN; } diff --git a/drivers/net/wireless/realtek/rtw89/pci.h b/drivers/net/wireless/realtek/rtw89/pci.h index 0e4bd210b100..2f3d1ad3b0f7 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.h +++ b/drivers/net/wireless/realtek/rtw89/pci.h @@ -150,6 +150,7 @@ #define B_AX_HD1ISR_IND_INT_EN BIT(26) #define B_AX_HD0ISR_IND_INT_EN BIT(25) #define B_AX_HS0ISR_IND_INT_EN BIT(24) +#define B_AX_HS0ISR_IND_INT_EN_WKARND BIT(23) #define B_AX_RETRAIN_INT_EN BIT(21) #define B_AX_RPQBD_FULL_INT_EN BIT(20) #define B_AX_RDU_INT_EN BIT(19) diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index c7e906123416..568488da3ff1 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -10,6 +10,7 @@ #include "ps.h" #include "reg.h" #include "sar.h" +#include "txrx.h" #include "util.h" static u16 get_max_amsdu_len(struct rtw89_dev *rtwdev, @@ -1400,7 +1401,8 @@ static void rtw89_phy_init_rf_nctl(struct rtw89_dev *rtwdev) rtw89_phy_write32_set(rtwdev, R_IOQ_IQK_DPK, 0x3); rtw89_phy_write32_set(rtwdev, R_GNT_BT_WGT_EN, 0x1); rtw89_phy_write32_set(rtwdev, R_P0_PATH_RST, 0x8000000); - rtw89_phy_write32_set(rtwdev, R_P1_PATH_RST, 0x8000000); + if (chip->chip_id != RTL8851B) + rtw89_phy_write32_set(rtwdev, R_P1_PATH_RST, 0x8000000); if (chip->chip_id == RTL8852B) rtw89_phy_write32_set(rtwdev, R_IOQ_IQK_DPK, 0x2); @@ -1414,6 +1416,9 @@ static void rtw89_phy_init_rf_nctl(struct rtw89_dev *rtwdev) nctl_table = chip->nctl_table; rtw89_phy_init_reg(rtwdev, nctl_table, rtw89_phy_config_bb_reg, NULL); + + if (chip->nctl_post_table) + rtw89_rfk_parser(rtwdev, chip->nctl_post_table); } static u32 rtw89_phy0_phy1_offset(struct rtw89_dev *rtwdev, u32 addr) @@ -2338,27 +2343,29 @@ void rtw89_phy_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, static u8 rtw89_phy_cfo_get_xcap_reg(struct rtw89_dev *rtwdev, bool sc_xo) { + const struct rtw89_xtal_info *xtal = rtwdev->chip->xtal_info; u32 reg_mask; if (sc_xo) - reg_mask = B_AX_XTAL_SC_XO_MASK; + reg_mask = xtal->sc_xo_mask; else - reg_mask = B_AX_XTAL_SC_XI_MASK; + reg_mask = xtal->sc_xi_mask; - return (u8)rtw89_read32_mask(rtwdev, R_AX_XTAL_ON_CTRL0, reg_mask); + return (u8)rtw89_read32_mask(rtwdev, xtal->xcap_reg, reg_mask); } static void rtw89_phy_cfo_set_xcap_reg(struct rtw89_dev *rtwdev, bool sc_xo, u8 val) { + const struct rtw89_xtal_info *xtal = rtwdev->chip->xtal_info; u32 reg_mask; if (sc_xo) - reg_mask = B_AX_XTAL_SC_XO_MASK; + reg_mask = xtal->sc_xo_mask; else - reg_mask = B_AX_XTAL_SC_XI_MASK; + reg_mask = xtal->sc_xi_mask; - rtw89_write32_mask(rtwdev, R_AX_XTAL_ON_CTRL0, reg_mask, val); + rtw89_write32_mask(rtwdev, xtal->xcap_reg, reg_mask, val); } static void rtw89_phy_cfo_set_crystal_cap(struct rtw89_dev *rtwdev, @@ -2371,7 +2378,7 @@ static void rtw89_phy_cfo_set_crystal_cap(struct rtw89_dev *rtwdev, if (!force && cfo->crystal_cap == crystal_cap) return; crystal_cap = clamp_t(u8, crystal_cap, 0, 127); - if (chip->chip_id == RTL8852A) { + if (chip->chip_id == RTL8852A || chip->chip_id == RTL8851B) { rtw89_phy_cfo_set_xcap_reg(rtwdev, true, crystal_cap); rtw89_phy_cfo_set_xcap_reg(rtwdev, false, crystal_cap); sc_xo_val = rtw89_phy_cfo_get_xcap_reg(rtwdev, true); @@ -2946,6 +2953,126 @@ static void rtw89_phy_ul_tb_info_init(struct rtw89_dev *rtwdev) rtw89_phy_read32_mask(rtwdev, R_BANDEDGE, B_BANDEDGE_EN); } +static +void rtw89_phy_antdiv_sts_instance_reset(struct rtw89_antdiv_stats *antdiv_sts) +{ + ewma_rssi_init(&antdiv_sts->cck_rssi_avg); + ewma_rssi_init(&antdiv_sts->ofdm_rssi_avg); + ewma_rssi_init(&antdiv_sts->non_legacy_rssi_avg); + antdiv_sts->pkt_cnt_cck = 0; + antdiv_sts->pkt_cnt_ofdm = 0; + antdiv_sts->pkt_cnt_non_legacy = 0; + antdiv_sts->evm = 0; +} + +static void rtw89_phy_antdiv_sts_instance_add(struct rtw89_dev *rtwdev, + struct rtw89_rx_phy_ppdu *phy_ppdu, + struct rtw89_antdiv_stats *stats) +{ + if (GET_DATA_RATE_MODE(phy_ppdu->rate) == DATA_RATE_MODE_NON_HT) { + if (phy_ppdu->rate < RTW89_HW_RATE_OFDM6) { + ewma_rssi_add(&stats->cck_rssi_avg, phy_ppdu->rssi_avg); + stats->pkt_cnt_cck++; + } else { + ewma_rssi_add(&stats->ofdm_rssi_avg, phy_ppdu->rssi_avg); + stats->pkt_cnt_ofdm++; + stats->evm += phy_ppdu->ofdm.evm_min; + } + } else { + ewma_rssi_add(&stats->non_legacy_rssi_avg, phy_ppdu->rssi_avg); + stats->pkt_cnt_non_legacy++; + stats->evm += phy_ppdu->ofdm.evm_min; + } +} + +static u8 rtw89_phy_antdiv_sts_instance_get_rssi(struct rtw89_antdiv_stats *stats) +{ + if (stats->pkt_cnt_non_legacy >= stats->pkt_cnt_cck && + stats->pkt_cnt_non_legacy >= stats->pkt_cnt_ofdm) + return ewma_rssi_read(&stats->non_legacy_rssi_avg); + else if (stats->pkt_cnt_ofdm >= stats->pkt_cnt_cck && + stats->pkt_cnt_ofdm >= stats->pkt_cnt_non_legacy) + return ewma_rssi_read(&stats->ofdm_rssi_avg); + else + return ewma_rssi_read(&stats->cck_rssi_avg); +} + +static u8 rtw89_phy_antdiv_sts_instance_get_evm(struct rtw89_antdiv_stats *stats) +{ + return phy_div(stats->evm, stats->pkt_cnt_non_legacy + stats->pkt_cnt_ofdm); +} + +void rtw89_phy_antdiv_parse(struct rtw89_dev *rtwdev, + struct rtw89_rx_phy_ppdu *phy_ppdu) +{ + struct rtw89_antdiv_info *antdiv = &rtwdev->antdiv; + struct rtw89_hal *hal = &rtwdev->hal; + + if (!hal->ant_diversity || hal->ant_diversity_fixed) + return; + + rtw89_phy_antdiv_sts_instance_add(rtwdev, phy_ppdu, &antdiv->target_stats); + + if (!antdiv->get_stats) + return; + + if (hal->antenna_rx == RF_A) + rtw89_phy_antdiv_sts_instance_add(rtwdev, phy_ppdu, &antdiv->main_stats); + else if (hal->antenna_rx == RF_B) + rtw89_phy_antdiv_sts_instance_add(rtwdev, phy_ppdu, &antdiv->aux_stats); +} + +static void rtw89_phy_antdiv_reg_init(struct rtw89_dev *rtwdev) +{ + rtw89_phy_write32_idx(rtwdev, R_P0_TRSW, B_P0_ANT_TRAIN_EN, + 0x0, RTW89_PHY_0); + rtw89_phy_write32_idx(rtwdev, R_P0_TRSW, B_P0_TX_ANT_SEL, + 0x0, RTW89_PHY_0); + + rtw89_phy_write32_idx(rtwdev, R_P0_ANT_SW, B_P0_TRSW_TX_EXTEND, + 0x0, RTW89_PHY_0); + rtw89_phy_write32_idx(rtwdev, R_P0_ANT_SW, B_P0_HW_ANTSW_DIS_BY_GNT_BT, + 0x0, RTW89_PHY_0); + + rtw89_phy_write32_idx(rtwdev, R_P0_TRSW, B_P0_BT_FORCE_ANTIDX_EN, + 0x0, RTW89_PHY_0); + + rtw89_phy_write32_idx(rtwdev, R_RFSW_CTRL_ANT0_BASE, B_RFSW_CTRL_ANT_MAPPING, + 0x0100, RTW89_PHY_0); + + rtw89_phy_write32_idx(rtwdev, R_P0_ANTSEL, B_P0_ANTSEL_BTG_TRX, + 0x1, RTW89_PHY_0); + rtw89_phy_write32_idx(rtwdev, R_P0_ANTSEL, B_P0_ANTSEL_HW_CTRL, + 0x0, RTW89_PHY_0); + rtw89_phy_write32_idx(rtwdev, R_P0_ANTSEL, B_P0_ANTSEL_SW_2G, + 0x0, RTW89_PHY_0); + rtw89_phy_write32_idx(rtwdev, R_P0_ANTSEL, B_P0_ANTSEL_SW_5G, + 0x0, RTW89_PHY_0); +} + +static void rtw89_phy_antdiv_sts_reset(struct rtw89_dev *rtwdev) +{ + struct rtw89_antdiv_info *antdiv = &rtwdev->antdiv; + + rtw89_phy_antdiv_sts_instance_reset(&antdiv->target_stats); + rtw89_phy_antdiv_sts_instance_reset(&antdiv->main_stats); + rtw89_phy_antdiv_sts_instance_reset(&antdiv->aux_stats); +} + +static void rtw89_phy_antdiv_init(struct rtw89_dev *rtwdev) +{ + struct rtw89_antdiv_info *antdiv = &rtwdev->antdiv; + struct rtw89_hal *hal = &rtwdev->hal; + + if (!hal->ant_diversity) + return; + + antdiv->get_stats = false; + antdiv->rssi_pre = 0; + rtw89_phy_antdiv_sts_reset(rtwdev); + rtw89_phy_antdiv_reg_init(rtwdev); +} + static void rtw89_phy_stat_thermal_update(struct rtw89_dev *rtwdev) { struct rtw89_phy_stat *phystat = &rtwdev->phystat; @@ -4114,6 +4241,144 @@ void rtw89_phy_tx_path_div_track(struct rtw89_dev *rtwdev) &done); } +#define ANTDIV_MAIN 0 +#define ANTDIV_AUX 1 + +static void rtw89_phy_antdiv_set_ant(struct rtw89_dev *rtwdev) +{ + struct rtw89_hal *hal = &rtwdev->hal; + u8 default_ant, optional_ant; + + if (!hal->ant_diversity || hal->antenna_tx == 0) + return; + + if (hal->antenna_tx == RF_B) { + default_ant = ANTDIV_AUX; + optional_ant = ANTDIV_MAIN; + } else { + default_ant = ANTDIV_MAIN; + optional_ant = ANTDIV_AUX; + } + + rtw89_phy_write32_idx(rtwdev, R_P0_ANTSEL, B_P0_ANTSEL_CGCS_CTRL, + default_ant, RTW89_PHY_0); + rtw89_phy_write32_idx(rtwdev, R_P0_ANTSEL, B_P0_ANTSEL_RX_ORI, + default_ant, RTW89_PHY_0); + rtw89_phy_write32_idx(rtwdev, R_P0_ANTSEL, B_P0_ANTSEL_RX_ALT, + optional_ant, RTW89_PHY_0); + rtw89_phy_write32_idx(rtwdev, R_P0_ANTSEL, B_P0_ANTSEL_TX_ORI, + default_ant, RTW89_PHY_0); +} + +static void rtw89_phy_swap_hal_antenna(struct rtw89_dev *rtwdev) +{ + struct rtw89_hal *hal = &rtwdev->hal; + + hal->antenna_rx = hal->antenna_rx == RF_A ? RF_B : RF_A; + hal->antenna_tx = hal->antenna_rx; +} + +static void rtw89_phy_antdiv_decision_state(struct rtw89_dev *rtwdev) +{ + struct rtw89_antdiv_info *antdiv = &rtwdev->antdiv; + struct rtw89_hal *hal = &rtwdev->hal; + bool no_change = false; + u8 main_rssi, aux_rssi; + u8 main_evm, aux_evm; + u32 candidate; + + antdiv->get_stats = false; + antdiv->training_count = 0; + + main_rssi = rtw89_phy_antdiv_sts_instance_get_rssi(&antdiv->main_stats); + main_evm = rtw89_phy_antdiv_sts_instance_get_evm(&antdiv->main_stats); + aux_rssi = rtw89_phy_antdiv_sts_instance_get_rssi(&antdiv->aux_stats); + aux_evm = rtw89_phy_antdiv_sts_instance_get_evm(&antdiv->aux_stats); + + if (main_evm > aux_evm + ANTDIV_EVM_DIFF_TH) + candidate = RF_A; + else if (aux_evm > main_evm + ANTDIV_EVM_DIFF_TH) + candidate = RF_B; + else if (main_rssi > aux_rssi + RTW89_TX_DIV_RSSI_RAW_TH) + candidate = RF_A; + else if (aux_rssi > main_rssi + RTW89_TX_DIV_RSSI_RAW_TH) + candidate = RF_B; + else + no_change = true; + + if (no_change) { + /* swap back from training antenna to original */ + rtw89_phy_swap_hal_antenna(rtwdev); + return; + } + + hal->antenna_tx = candidate; + hal->antenna_rx = candidate; +} + +static void rtw89_phy_antdiv_training_state(struct rtw89_dev *rtwdev) +{ + struct rtw89_antdiv_info *antdiv = &rtwdev->antdiv; + u64 state_period; + + if (antdiv->training_count % 2 == 0) { + if (antdiv->training_count == 0) + rtw89_phy_antdiv_sts_reset(rtwdev); + + antdiv->get_stats = true; + state_period = msecs_to_jiffies(ANTDIV_TRAINNING_INTVL); + } else { + antdiv->get_stats = false; + state_period = msecs_to_jiffies(ANTDIV_DELAY); + + rtw89_phy_swap_hal_antenna(rtwdev); + rtw89_phy_antdiv_set_ant(rtwdev); + } + + antdiv->training_count++; + ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->antdiv_work, + state_period); +} + +void rtw89_phy_antdiv_work(struct work_struct *work) +{ + struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, + antdiv_work.work); + struct rtw89_antdiv_info *antdiv = &rtwdev->antdiv; + + mutex_lock(&rtwdev->mutex); + + if (antdiv->training_count <= ANTDIV_TRAINNING_CNT) { + rtw89_phy_antdiv_training_state(rtwdev); + } else { + rtw89_phy_antdiv_decision_state(rtwdev); + rtw89_phy_antdiv_set_ant(rtwdev); + } + + mutex_unlock(&rtwdev->mutex); +} + +void rtw89_phy_antdiv_track(struct rtw89_dev *rtwdev) +{ + struct rtw89_antdiv_info *antdiv = &rtwdev->antdiv; + struct rtw89_hal *hal = &rtwdev->hal; + u8 rssi, rssi_pre; + + if (!hal->ant_diversity || hal->ant_diversity_fixed) + return; + + rssi = rtw89_phy_antdiv_sts_instance_get_rssi(&antdiv->target_stats); + rssi_pre = antdiv->rssi_pre; + antdiv->rssi_pre = rssi; + rtw89_phy_antdiv_sts_instance_reset(&antdiv->target_stats); + + if (abs((int)rssi - (int)rssi_pre) < ANTDIV_RSSI_DIFF_TH) + return; + + antdiv->training_count = 0; + ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->antdiv_work, 0); +} + static void rtw89_phy_env_monitor_init(struct rtw89_dev *rtwdev) { rtw89_phy_ccx_top_setting_init(rtwdev); @@ -4133,6 +4398,8 @@ void rtw89_phy_dm_init(struct rtw89_dev *rtwdev) rtw89_phy_dig_init(rtwdev); rtw89_phy_cfo_init(rtwdev); rtw89_phy_ul_tb_info_init(rtwdev); + rtw89_phy_antdiv_init(rtwdev); + rtw89_phy_antdiv_set_ant(rtwdev); rtw89_phy_init_rf_nctl(rtwdev); rtw89_chip_rfk_init(rtwdev); diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 7535867d0f48..ab174a0ba488 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -67,6 +67,14 @@ #define UL_TB_TF_CNT_L2H_TH 100 #define UL_TB_TF_CNT_H2L_TH 70 +#define ANTDIV_TRAINNING_CNT 2 +#define ANTDIV_TRAINNING_INTVL 30 +#define ANTDIV_DELAY 110 +#define ANTDIV_TP_DIFF_TH_HIGH 100 +#define ANTDIV_TP_DIFF_TH_LOW 5 +#define ANTDIV_EVM_DIFF_TH 8 +#define ANTDIV_RSSI_DIFF_TH 3 + #define CCX_MAX_PERIOD 2097 #define CCX_MAX_PERIOD_UNIT 32 #define MS_TO_4US_RATIO 250 @@ -549,6 +557,10 @@ void rtw89_phy_set_phy_regs(struct rtw89_dev *rtwdev, u32 addr, u32 mask, void rtw89_phy_dig_reset(struct rtw89_dev *rtwdev); void rtw89_phy_dig(struct rtw89_dev *rtwdev); void rtw89_phy_tx_path_div_track(struct rtw89_dev *rtwdev); +void rtw89_phy_antdiv_parse(struct rtw89_dev *rtwdev, + struct rtw89_rx_phy_ppdu *phy_ppdu); +void rtw89_phy_antdiv_track(struct rtw89_dev *rtwdev); +void rtw89_phy_antdiv_work(struct work_struct *work); void rtw89_phy_set_bss_color(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif); void rtw89_phy_tssi_ctrl_set_bandedge_cfg(struct rtw89_dev *rtwdev, enum rtw89_mac_idx mac_idx, diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 266e4231b5f3..21f68787ff10 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -243,6 +243,12 @@ #define B_AX_XTAL_SC_XI_MASK GENMASK(16, 10) #define B_AX_XTAL_SC_MASK GENMASK(6, 0) +#define R_AX_XTAL_ON_CTRL3 0x028C +#define B_AX_XTAL_SC_INIT_A_BLOCK_MASK GENMASK(30, 24) +#define B_AX_XTAL_SC_LPS_A_BLOCK_MASK GENMASK(22, 16) +#define B_AX_XTAL_SC_XO_A_BLOCK_MASK GENMASK(14, 8) +#define B_AX_XTAL_SC_XI_A_BLOCK_MASK GENMASK(6, 0) + #define R_AX_GPIO0_7_FUNC_SEL 0x02D0 #define R_AX_EECS_EESK_FUNC_SEL 0x02D8 @@ -3574,6 +3580,7 @@ #define RR_MOD_MASK GENMASK(19, 16) #define RR_MOD_DCK GENMASK(14, 10) #define RR_MOD_RGM GENMASK(13, 4) +#define RR_MOD_RXB GENMASK(9, 5) #define RR_MOD_V_DOWN 0x0 #define RR_MOD_V_STANDBY 0x1 #define RR_TXAGC 0x10001 @@ -3713,6 +3720,7 @@ #define RR_RXBB 0x83 #define RR_RXBB_VOBUF GENMASK(15, 12) #define RR_RXBB_C2G GENMASK(16, 10) +#define RR_RXBB_C2 GENMASK(11, 8) #define RR_RXBB_C1G GENMASK(9, 8) #define RR_RXBB_FATT GENMASK(7, 0) #define RR_RXBB_ATTR GENMASK(7, 4) @@ -3776,15 +3784,21 @@ #define RR_LOGEN 0xa3 #define RR_LOGEN_RPT GENMASK(19, 16) #define RR_SX 0xaf +#define RR_IBD 0xc9 +#define RR_IBD_VAL GENMASK(4, 0) #define RR_LDO 0xb1 #define RR_LDO_SEL GENMASK(8, 6) #define RR_VCO 0xb2 +#define RR_VCO_SEL GENMASK(9, 8) +#define RR_VCI 0xb3 +#define RR_VCI_ON BIT(7) #define RR_LPF 0xb7 #define RR_LPF_BUSY BIT(8) #define RR_XTALX2 0xb8 #define RR_MALSEL 0xbe #define RR_SYNFB 0xc5 #define RR_SYNFB_LK BIT(15) +#define RR_AACK 0xca #define RR_LCKST 0xcf #define RR_LCKST_BIN BIT(0) #define RR_LCK_TRG 0xd3 @@ -3852,6 +3866,9 @@ #define B_ENABLE_CCK BIT(5) #define R_RSTB_ASYNC 0x0704 #define B_RSTB_ASYNC_ALL BIT(1) +#define R_P0_ANT_SW 0x0728 +#define B_P0_HW_ANTSW_DIS_BY_GNT_BT BIT(12) +#define B_P0_TRSW_TX_EXTEND GENMASK(3, 0) #define R_MAC_PIN_SEL 0x0734 #define B_CH_IDX_SEG0 GENMASK(23, 16) #define R_PLCP_HISTOGRAM 0x0738 @@ -4455,10 +4472,24 @@ #define B_P0_RFCTM_VAL GENMASK(25, 20) #define R_P0_RFCTM_RDY BIT(26) #define R_P0_TRSW 0x5868 -#define B_P0_TRSW_B BIT(0) -#define B_P0_TRSW_A BIT(1) +#define B_P0_BT_FORCE_ANTIDX_EN BIT(12) #define B_P0_TRSW_X BIT(2) +#define B_P0_TRSW_A BIT(1) +#define B_P0_TX_ANT_SEL BIT(1) +#define B_P0_TRSW_B BIT(0) +#define B_P0_ANT_TRAIN_EN BIT(0) #define B_P0_TRSW_SO_A2 GENMASK(7, 5) +#define R_P0_ANTSEL 0x586C +#define B_P0_ANTSEL_SW_5G BIT(25) +#define B_P0_ANTSEL_SW_2G BIT(23) +#define B_P0_ANTSEL_BTG_TRX BIT(21) +#define B_P0_ANTSEL_CGCS_CTRL BIT(17) +#define B_P0_ANTSEL_HW_CTRL BIT(16) +#define B_P0_ANTSEL_TX_ORI GENMASK(15, 12) +#define B_P0_ANTSEL_RX_ALT GENMASK(11, 8) +#define B_P0_ANTSEL_RX_ORI GENMASK(7, 4) +#define R_RFSW_CTRL_ANT0_BASE 0x5870 +#define B_RFSW_CTRL_ANT_MAPPING GENMASK(15, 0) #define R_P0_RFM 0x5894 #define B_P0_RFM_DIS_WL BIT(7) #define B_P0_RFM_TX_OPT BIT(6) @@ -4572,6 +4603,8 @@ #define IQK_DF4_TXT_8_25MHZ 0x021 #define R_IQK_CFG 0x8034 #define B_IQK_CFG_SET GENMASK(5, 4) +#define R_IQK_RXA 0x8044 +#define B_IQK_RXAGC GENMASK(15, 13) #define R_TPG_SEL 0x8068 #define R_TPG_MOD 0x806C #define B_TPG_MOD_F GENMASK(2, 1) @@ -4619,6 +4652,7 @@ #define B_PRT_COM_SYNERR BIT(30) #define B_PRT_COM_DCI GENMASK(27, 16) #define B_PRT_COM_CORV GENMASK(15, 8) +#define B_RPT_COM_RDY GENMASK(15, 0) #define B_PRT_COM_DCQ GENMASK(11, 0) #define B_PRT_COM_RXOV BIT(8) #define B_PRT_COM_GL GENMASK(7, 4) @@ -4730,11 +4764,15 @@ #define B_IQKINF2_KCNT GENMASK(15, 8) #define B_IQKINF2_NCTLV GENMASK(7, 0) #define R_DCOF0 0xC000 +#define B_DCOF0_RST BIT(17) #define B_DCOF0_V GENMASK(4, 1) #define R_DCOF1 0xC004 +#define B_DCOF1_RST BIT(17) #define B_DCOF1_S BIT(0) #define R_DCOF8 0xC020 #define B_DCOF8_V GENMASK(4, 1) +#define R_DCOF9 0xC024 +#define B_DCOF9_RST BIT(17) #define R_DACK_S0P0 0xC040 #define B_DACK_S0P0_OK BIT(31) #define R_DACK_BIAS00 0xc048 @@ -4786,6 +4824,7 @@ #define B_ADDCK0D_VAL GENMASK(25, 16) #define R_ADDCK0 0xC0F4 #define B_ADDCK0_TRG BIT(11) +#define B_ADDCK0_IQ BIT(10) #define B_ADDCK0 GENMASK(9, 8) #define B_ADDCK0_MAN GENMASK(5, 4) #define B_ADDCK0_EN BIT(4) @@ -4797,6 +4836,7 @@ #define B_ADDCK0_RL0 GENMASK(17, 8) #define R_ADDCKR0 0xC0FC #define B_ADDCKR0_A0 GENMASK(19, 10) +#define B_ADDCKR0_DC GENMASK(15, 4) #define B_ADDCKR0_A1 GENMASK(9, 0) #define R_DACK10 0xC100 #define B_DACK10 GENMASK(4, 1) @@ -4847,6 +4887,11 @@ #define R_ADDCKR1 0xC1fC #define B_ADDCKR1_A0 GENMASK(19, 10) #define B_ADDCKR1_A1 GENMASK(9, 0) +#define R_DACKN0_CTL 0xC210 +#define B_DACKN0_EN BIT(0) +#define B_DACKN0_V GENMASK(21, 14) +#define R_DACKN1_CTL 0xC224 +#define B_DACKN1_V GENMASK(21, 14) /* WiFi CPU local domain */ #define R_AX_WDT_CTRL 0x0040 diff --git a/drivers/net/wireless/realtek/rtw89/regd.c b/drivers/net/wireless/realtek/rtw89/regd.c index 6e5a740b128f..377a7a1c560b 100644 --- a/drivers/net/wireless/realtek/rtw89/regd.c +++ b/drivers/net/wireless/realtek/rtw89/regd.c @@ -2,6 +2,7 @@ /* Copyright(c) 2019-2020 Realtek Corporation */ +#include "acpi.h" #include "debug.h" #include "ps.h" @@ -282,6 +283,66 @@ do { \ __r->txpwr_regd[RTW89_BAND_6G]); \ } while (0) +static void rtw89_regd_setup_unii4(struct rtw89_dev *rtwdev, + struct wiphy *wiphy) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + bool regd_allow_unii_4 = chip->support_unii4; + struct ieee80211_supported_band *sband; + int ret; + u8 val; + + if (!chip->support_unii4) + goto bottom; + + ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_59G_EN, &val); + if (ret) { + rtw89_debug(rtwdev, RTW89_DBG_REGD, + "acpi: cannot eval unii 4: %d\n", ret); + goto bottom; + } + + rtw89_debug(rtwdev, RTW89_DBG_REGD, + "acpi: eval if allow unii 4: %d\n", val); + + switch (val) { + case 0: + regd_allow_unii_4 = false; + break; + case 1: + regd_allow_unii_4 = true; + break; + default: + break; + } + +bottom: + rtw89_debug(rtwdev, RTW89_DBG_REGD, "regd: allow unii 4: %d\n", + regd_allow_unii_4); + + if (regd_allow_unii_4) + return; + + sband = wiphy->bands[NL80211_BAND_5GHZ]; + if (!sband) + return; + + sband->n_channels -= 3; +} + +int rtw89_regd_setup(struct rtw89_dev *rtwdev) +{ + struct wiphy *wiphy = rtwdev->hw->wiphy; + + if (!wiphy) + return -EINVAL; + + rtw89_regd_setup_unii4(rtwdev, wiphy); + + wiphy->reg_notifier = rtw89_regd_notifier; + return 0; +} + int rtw89_regd_init(struct rtw89_dev *rtwdev, void (*reg_notifier)(struct wiphy *wiphy, struct regulatory_request *request)) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c new file mode 100644 index 000000000000..00cabf92c5a9 --- /dev/null +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2022-2023 Realtek Corporation + */ + +#include "coex.h" +#include "fw.h" +#include "mac.h" +#include "phy.h" +#include "reg.h" +#include "rtw8851b.h" +#include "rtw8851b_rfk_table.h" +#include "rtw8851b_table.h" +#include "txrx.h" +#include "util.h" + +#define RTW8851B_FW_FORMAT_MAX 0 +#define RTW8851B_FW_BASENAME "rtw89/rtw8851b_fw" +#define RTW8851B_MODULE_FIRMWARE \ + RTW8851B_FW_BASENAME ".bin" + +static const struct rtw89_hfc_ch_cfg rtw8851b_hfc_chcfg_pcie[] = { + {5, 343, grp_0}, /* ACH 0 */ + {5, 343, grp_0}, /* ACH 1 */ + {5, 343, grp_0}, /* ACH 2 */ + {5, 343, grp_0}, /* ACH 3 */ + {0, 0, grp_0}, /* ACH 4 */ + {0, 0, grp_0}, /* ACH 5 */ + {0, 0, grp_0}, /* ACH 6 */ + {0, 0, grp_0}, /* ACH 7 */ + {4, 344, grp_0}, /* B0MGQ */ + {4, 344, grp_0}, /* B0HIQ */ + {0, 0, grp_0}, /* B1MGQ */ + {0, 0, grp_0}, /* B1HIQ */ + {40, 0, 0} /* FWCMDQ */ +}; + +static const struct rtw89_hfc_pub_cfg rtw8851b_hfc_pubcfg_pcie = { + 448, /* Group 0 */ + 0, /* Group 1 */ + 448, /* Public Max */ + 0 /* WP threshold */ +}; + +static const struct rtw89_hfc_param_ini rtw8851b_hfc_param_ini_pcie[] = { + [RTW89_QTA_SCC] = {rtw8851b_hfc_chcfg_pcie, &rtw8851b_hfc_pubcfg_pcie, + &rtw89_mac_size.hfc_preccfg_pcie, RTW89_HCIFC_POH}, + [RTW89_QTA_DLFW] = {NULL, NULL, &rtw89_mac_size.hfc_preccfg_pcie, + RTW89_HCIFC_POH}, + [RTW89_QTA_INVALID] = {NULL}, +}; + +static const struct rtw89_dle_mem rtw8851b_dle_mem_pcie[] = { + [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size6, + &rtw89_mac_size.ple_size6, &rtw89_mac_size.wde_qt6, + &rtw89_mac_size.wde_qt6, &rtw89_mac_size.ple_qt18, + &rtw89_mac_size.ple_qt58}, + [RTW89_QTA_WOW] = {RTW89_QTA_WOW, &rtw89_mac_size.wde_size6, + &rtw89_mac_size.ple_size6, &rtw89_mac_size.wde_qt6, + &rtw89_mac_size.wde_qt6, &rtw89_mac_size.ple_qt18, + &rtw89_mac_size.ple_qt_51b_wow}, + [RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &rtw89_mac_size.wde_size9, + &rtw89_mac_size.ple_size8, &rtw89_mac_size.wde_qt4, + &rtw89_mac_size.wde_qt4, &rtw89_mac_size.ple_qt13, + &rtw89_mac_size.ple_qt13}, + [RTW89_QTA_INVALID] = {RTW89_QTA_INVALID, NULL, NULL, NULL, NULL, NULL, + NULL}, +}; + +static const struct rtw89_xtal_info rtw8851b_xtal_info = { + .xcap_reg = R_AX_XTAL_ON_CTRL3, + .sc_xo_mask = B_AX_XTAL_SC_XO_A_BLOCK_MASK, + .sc_xi_mask = B_AX_XTAL_SC_XI_A_BLOCK_MASK, +}; + +static const struct rtw89_chip_ops rtw8851b_chip_ops = { + .fem_setup = NULL, + .fill_txdesc = rtw89_core_fill_txdesc, + .fill_txdesc_fwcmd = rtw89_core_fill_txdesc, + .h2c_dctl_sec_cam = NULL, +}; + +#ifdef CONFIG_PM +static const struct wiphy_wowlan_support rtw_wowlan_stub_8851b = { + .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, + .n_patterns = RTW89_MAX_PATTERN_NUM, + .pattern_max_len = RTW89_MAX_PATTERN_SIZE, + .pattern_min_len = 1, +}; +#endif + +const struct rtw89_chip_info rtw8851b_chip_info = { + .chip_id = RTL8851B, + .ops = &rtw8851b_chip_ops, + .fw_basename = RTW8851B_FW_BASENAME, + .fw_format_max = RTW8851B_FW_FORMAT_MAX, + .try_ce_fw = true, + .fifo_size = 196608, + .small_fifo_size = true, + .dle_scc_rsvd_size = 98304, + .max_amsdu_limit = 3500, + .dis_2g_40m_ul_ofdma = true, + .rsvd_ple_ofst = 0x2f800, + .hfc_param_ini = rtw8851b_hfc_param_ini_pcie, + .dle_mem = rtw8851b_dle_mem_pcie, + .wde_qempty_acq_num = 4, + .wde_qempty_mgq_sel = 4, + .rf_base_addr = {0xe000}, + .pwr_on_seq = NULL, + .pwr_off_seq = NULL, + .bb_table = &rtw89_8851b_phy_bb_table, + .bb_gain_table = &rtw89_8851b_phy_bb_gain_table, + .rf_table = {&rtw89_8851b_phy_radioa_table,}, + .nctl_table = &rtw89_8851b_phy_nctl_table, + .nctl_post_table = &rtw8851b_nctl_post_defs_tbl, + .byr_table = &rtw89_8851b_byr_table, + .dflt_parms = &rtw89_8851b_dflt_parms, + .rfe_parms_conf = rtw89_8851b_rfe_parms_conf, + .txpwr_factor_rf = 2, + .txpwr_factor_mac = 1, + .dig_table = NULL, + .tssi_dbw_table = NULL, + .support_chanctx_num = 0, + .support_bands = BIT(NL80211_BAND_2GHZ) | + BIT(NL80211_BAND_5GHZ), + .support_bw160 = false, + .support_unii4 = true, + .support_ul_tb_ctrl = true, + .hw_sec_hdr = false, + .rf_path_num = 1, + .tx_nss = 1, + .rx_nss = 1, + .acam_num = 32, + .bcam_num = 20, + .scam_num = 128, + .bacam_num = 2, + .bacam_dynamic_num = 4, + .bacam_ver = RTW89_BACAM_V0, + .sec_ctrl_efuse_size = 4, + .physical_efuse_size = 1216, + .logical_efuse_size = 2048, + .limit_efuse_size = 1280, + .dav_phy_efuse_size = 0, + .dav_log_efuse_size = 0, + .phycap_addr = 0x580, + .phycap_size = 128, + .para_ver = 0, + .wlcx_desired = 0x06000000, + .btcx_desired = 0x7, + .scbd = 0x1, + .mailbox = 0x1, + + .ps_mode_supported = BIT(RTW89_PS_MODE_RFOFF) | + BIT(RTW89_PS_MODE_CLK_GATED), + .low_power_hci_modes = 0, + .h2c_cctl_func_id = H2C_FUNC_MAC_CCTLINFO_UD, + .hci_func_en_addr = R_AX_HCI_FUNC_EN, + .h2c_desc_size = sizeof(struct rtw89_txwd_body), + .txwd_body_size = sizeof(struct rtw89_txwd_body), + .bss_clr_map_reg = R_BSS_CLR_MAP_V1, + .dma_ch_mask = BIT(RTW89_DMA_ACH4) | BIT(RTW89_DMA_ACH5) | + BIT(RTW89_DMA_ACH6) | BIT(RTW89_DMA_ACH7) | + BIT(RTW89_DMA_B1MG) | BIT(RTW89_DMA_B1HI), + .edcca_lvl_reg = R_SEG0R_EDCCA_LVL_V1, +#ifdef CONFIG_PM + .wowlan_stub = &rtw_wowlan_stub_8851b, +#endif + .xtal_info = &rtw8851b_xtal_info, +}; +EXPORT_SYMBOL(rtw8851b_chip_info); + +MODULE_FIRMWARE(RTW8851B_MODULE_FIRMWARE); +MODULE_AUTHOR("Realtek Corporation"); +MODULE_DESCRIPTION("Realtek 802.11ax wireless 8851B driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.h b/drivers/net/wireless/realtek/rtw89/rtw8851b.h new file mode 100644 index 000000000000..e34b7d09ad6d --- /dev/null +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* Copyright(c) 2022-2023 Realtek Corporation + */ + +#ifndef __RTW89_8851B_H__ +#define __RTW89_8851B_H__ + +#include "core.h" + +#define RF_PATH_NUM_8851B 1 +#define BB_PATH_NUM_8851B 1 + +extern const struct rtw89_chip_info rtw8851b_chip_info; + +#endif diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8851b_rfk.c new file mode 100644 index 000000000000..6eb47ed82010 --- /dev/null +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b_rfk.c @@ -0,0 +1,1775 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2022-2023 Realtek Corporation + */ + +#include "coex.h" +#include "debug.h" +#include "mac.h" +#include "phy.h" +#include "reg.h" +#include "rtw8851b.h" +#include "rtw8851b_rfk.h" +#include "rtw8851b_rfk_table.h" +#include "rtw8851b_table.h" + +#define RTW8851B_RXK_GROUP_NR 4 +#define RTW8851B_TXK_GROUP_NR 1 +#define RTW8851B_IQK_VER 0x2a +#define RTW8851B_IQK_SS 1 +#define RTW8851B_LOK_GRAM 10 + +enum rtw8851b_iqk_type { + ID_TXAGC = 0x0, + ID_FLOK_COARSE = 0x1, + ID_FLOK_FINE = 0x2, + ID_TXK = 0x3, + ID_RXAGC = 0x4, + ID_RXK = 0x5, + ID_NBTXK = 0x6, + ID_NBRXK = 0x7, + ID_FLOK_VBUFFER = 0x8, + ID_A_FLOK_COARSE = 0x9, + ID_G_FLOK_COARSE = 0xa, + ID_A_FLOK_FINE = 0xb, + ID_G_FLOK_FINE = 0xc, + ID_IQK_RESTORE = 0x10, +}; + +static const u32 g_idxrxgain[RTW8851B_RXK_GROUP_NR] = {0x10e, 0x116, 0x28e, 0x296}; +static const u32 g_idxattc2[RTW8851B_RXK_GROUP_NR] = {0x0, 0xf, 0x0, 0xf}; +static const u32 g_idxrxagc[RTW8851B_RXK_GROUP_NR] = {0x0, 0x1, 0x2, 0x3}; +static const u32 a_idxrxgain[RTW8851B_RXK_GROUP_NR] = {0x10C, 0x112, 0x28c, 0x292}; +static const u32 a_idxattc2[RTW8851B_RXK_GROUP_NR] = {0xf, 0xf, 0xf, 0xf}; +static const u32 a_idxrxagc[RTW8851B_RXK_GROUP_NR] = {0x4, 0x5, 0x6, 0x7}; +static const u32 a_power_range[RTW8851B_TXK_GROUP_NR] = {0x0}; +static const u32 a_track_range[RTW8851B_TXK_GROUP_NR] = {0x6}; +static const u32 a_gain_bb[RTW8851B_TXK_GROUP_NR] = {0x0a}; +static const u32 a_itqt[RTW8851B_TXK_GROUP_NR] = {0x12}; +static const u32 g_power_range[RTW8851B_TXK_GROUP_NR] = {0x0}; +static const u32 g_track_range[RTW8851B_TXK_GROUP_NR] = {0x6}; +static const u32 g_gain_bb[RTW8851B_TXK_GROUP_NR] = {0x10}; +static const u32 g_itqt[RTW8851B_TXK_GROUP_NR] = {0x12}; + +static const u32 rtw8851b_backup_bb_regs[] = {0xc0ec, 0xc0e8}; +static const u32 rtw8851b_backup_rf_regs[] = { + 0xef, 0xde, 0x0, 0x1e, 0x2, 0x85, 0x90, 0x5}; + +#define BACKUP_BB_REGS_NR ARRAY_SIZE(rtw8851b_backup_bb_regs) +#define BACKUP_RF_REGS_NR ARRAY_SIZE(rtw8851b_backup_rf_regs) + +static u8 _kpath(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) +{ + return RF_A; +} + +static void _adc_fifo_rst(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, + u8 path) +{ + rtw89_phy_write32_mask(rtwdev, R_ADC_FIFO, B_ADC_FIFO_RXK, 0x0101); + fsleep(10); + rtw89_phy_write32_mask(rtwdev, R_ADC_FIFO, B_ADC_FIFO_RXK, 0x1111); +} + +static void _wait_rx_mode(struct rtw89_dev *rtwdev, u8 kpath) +{ + u32 rf_mode; + u8 path; + int ret; + + for (path = 0; path < RF_PATH_MAX; path++) { + if (!(kpath & BIT(path))) + continue; + + ret = read_poll_timeout_atomic(rtw89_read_rf, rf_mode, + rf_mode != 2, 2, 5000, false, + rtwdev, path, 0x00, RR_MOD_MASK); + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[RFK] Wait S%d to Rx mode!! (ret = %d)\n", + path, ret); + } +} + +static void _dack_reset(struct rtw89_dev *rtwdev, enum rtw89_rf_path path) +{ + rtw89_phy_write32_mask(rtwdev, R_DCOF0, B_DCOF0_RST, 0x0); + rtw89_phy_write32_mask(rtwdev, R_DCOF0, B_DCOF0_RST, 0x1); +} + +static void _drck(struct rtw89_dev *rtwdev) +{ + u32 rck_d; + u32 val; + int ret; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]Ddie RCK start!!!\n"); + + rtw89_phy_write32_mask(rtwdev, R_DRCK, B_DRCK_IDLE, 0x1); + rtw89_phy_write32_mask(rtwdev, R_DRCK, B_DRCK_EN, 0x1); + + ret = read_poll_timeout_atomic(rtw89_phy_read32_mask, val, val, + 1, 10000, false, + rtwdev, R_DRCK_RES, B_DRCK_POL); + if (ret) + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]DRCK timeout\n"); + + rtw89_phy_write32_mask(rtwdev, R_DRCK, B_DRCK_EN, 0x0); + rtw89_phy_write32_mask(rtwdev, R_DRCK_FH, B_DRCK_LAT, 0x1); + udelay(1); + rtw89_phy_write32_mask(rtwdev, R_DRCK_FH, B_DRCK_LAT, 0x0); + + rck_d = rtw89_phy_read32_mask(rtwdev, R_DRCK_RES, 0x7c00); + rtw89_phy_write32_mask(rtwdev, R_DRCK, B_DRCK_IDLE, 0x0); + rtw89_phy_write32_mask(rtwdev, R_DRCK, B_DRCK_VAL, rck_d); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]0xc0c4 = 0x%x\n", + rtw89_phy_read32_mask(rtwdev, R_DRCK, MASKDWORD)); +} + +static void _addck_backup(struct rtw89_dev *rtwdev) +{ + struct rtw89_dack_info *dack = &rtwdev->dack; + + rtw89_phy_write32_mask(rtwdev, R_ADDCK0, B_ADDCK0, 0x0); + + dack->addck_d[0][0] = rtw89_phy_read32_mask(rtwdev, R_ADDCKR0, B_ADDCKR0_A0); + dack->addck_d[0][1] = rtw89_phy_read32_mask(rtwdev, R_ADDCKR0, B_ADDCKR0_A1); +} + +static void _addck_reload(struct rtw89_dev *rtwdev) +{ + struct rtw89_dack_info *dack = &rtwdev->dack; + + rtw89_phy_write32_mask(rtwdev, R_ADDCK0_RL, B_ADDCK0_RL1, dack->addck_d[0][0]); + rtw89_phy_write32_mask(rtwdev, R_ADDCK0_RL, B_ADDCK0_RL0, dack->addck_d[0][1]); + rtw89_phy_write32_mask(rtwdev, R_ADDCK0_RL, B_ADDCK0_RLS, 0x3); +} + +static void _dack_backup_s0(struct rtw89_dev *rtwdev) +{ + struct rtw89_dack_info *dack = &rtwdev->dack; + u8 i; + + rtw89_phy_write32_mask(rtwdev, R_P0_NRBW, B_P0_NRBW_DBG, 0x1); + + for (i = 0; i < RTW89_DACK_MSBK_NR; i++) { + rtw89_phy_write32_mask(rtwdev, R_DCOF0, B_DCOF0_V, i); + dack->msbk_d[0][0][i] = + rtw89_phy_read32_mask(rtwdev, R_DACK_S0P2, B_DACK_S0M0); + + rtw89_phy_write32_mask(rtwdev, R_DCOF8, B_DCOF8_V, i); + dack->msbk_d[0][1][i] = + rtw89_phy_read32_mask(rtwdev, R_DACK_S0P3, B_DACK_S0M1); + } + + dack->biask_d[0][0] = + rtw89_phy_read32_mask(rtwdev, R_DACK_BIAS00, B_DACK_BIAS00); + dack->biask_d[0][1] = + rtw89_phy_read32_mask(rtwdev, R_DACK_BIAS01, B_DACK_BIAS01); + dack->dadck_d[0][0] = + rtw89_phy_read32_mask(rtwdev, R_DACK_DADCK00, B_DACK_DADCK00) + 24; + dack->dadck_d[0][1] = + rtw89_phy_read32_mask(rtwdev, R_DACK_DADCK01, B_DACK_DADCK01) + 24; +} + +static void _dack_reload_by_path(struct rtw89_dev *rtwdev, + enum rtw89_rf_path path, u8 index) +{ + struct rtw89_dack_info *dack = &rtwdev->dack; + u32 idx_offset, path_offset; + u32 offset, reg; + u32 tmp; + u8 i; + + if (index == 0) + idx_offset = 0; + else + idx_offset = 0x14; + + if (path == RF_PATH_A) + path_offset = 0; + else + path_offset = 0x28; + + offset = idx_offset + path_offset; + + rtw89_phy_write32_mask(rtwdev, R_DCOF1, B_DCOF1_RST, 0x1); + rtw89_phy_write32_mask(rtwdev, R_DCOF9, B_DCOF9_RST, 0x1); + + /* msbk_d: 15/14/13/12 */ + tmp = 0x0; + for (i = 0; i < 4; i++) + tmp |= dack->msbk_d[path][index][i + 12] << (i * 8); + reg = 0xc200 + offset; + rtw89_phy_write32(rtwdev, reg, tmp); + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]0x%x=0x%x\n", reg, + rtw89_phy_read32_mask(rtwdev, reg, MASKDWORD)); + + /* msbk_d: 11/10/9/8 */ + tmp = 0x0; + for (i = 0; i < 4; i++) + tmp |= dack->msbk_d[path][index][i + 8] << (i * 8); + reg = 0xc204 + offset; + rtw89_phy_write32(rtwdev, reg, tmp); + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]0x%x=0x%x\n", reg, + rtw89_phy_read32_mask(rtwdev, reg, MASKDWORD)); + + /* msbk_d: 7/6/5/4 */ + tmp = 0x0; + for (i = 0; i < 4; i++) + tmp |= dack->msbk_d[path][index][i + 4] << (i * 8); + reg = 0xc208 + offset; + rtw89_phy_write32(rtwdev, reg, tmp); + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]0x%x=0x%x\n", reg, + rtw89_phy_read32_mask(rtwdev, reg, MASKDWORD)); + + /* msbk_d: 3/2/1/0 */ + tmp = 0x0; + for (i = 0; i < 4; i++) + tmp |= dack->msbk_d[path][index][i] << (i * 8); + reg = 0xc20c + offset; + rtw89_phy_write32(rtwdev, reg, tmp); + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]0x%x=0x%x\n", reg, + rtw89_phy_read32_mask(rtwdev, reg, MASKDWORD)); + + /* dadak_d/biask_d */ + tmp = 0x0; + tmp = (dack->biask_d[path][index] << 22) | + (dack->dadck_d[path][index] << 14); + reg = 0xc210 + offset; + rtw89_phy_write32(rtwdev, reg, tmp); + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]0x%x=0x%x\n", reg, + rtw89_phy_read32_mask(rtwdev, reg, MASKDWORD)); + + rtw89_phy_write32_mask(rtwdev, R_DACKN0_CTL + offset, B_DACKN0_EN, 0x1); +} + +static void _dack_reload(struct rtw89_dev *rtwdev, enum rtw89_rf_path path) +{ + u8 index; + + for (index = 0; index < 2; index++) + _dack_reload_by_path(rtwdev, path, index); +} + +static void _addck(struct rtw89_dev *rtwdev) +{ + struct rtw89_dack_info *dack = &rtwdev->dack; + u32 val; + int ret; + + rtw89_phy_write32_mask(rtwdev, R_ADDCK0, B_ADDCK0_RST, 0x1); + rtw89_phy_write32_mask(rtwdev, R_ADDCK0, B_ADDCK0_EN, 0x1); + rtw89_phy_write32_mask(rtwdev, R_ADDCK0, B_ADDCK0_EN, 0x0); + udelay(1); + rtw89_phy_write32_mask(rtwdev, R_ADDCK0, B_ADDCK0, 0x1); + + ret = read_poll_timeout_atomic(rtw89_phy_read32_mask, val, val, + 1, 10000, false, + rtwdev, R_ADDCKR0, BIT(0)); + if (ret) { + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]S0 ADDCK timeout\n"); + dack->addck_timeout[0] = true; + } + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]ADDCK ret = %d\n", ret); + + rtw89_phy_write32_mask(rtwdev, R_ADDCK0, B_ADDCK0_RST, 0x0); +} + +static void _new_dadck(struct rtw89_dev *rtwdev) +{ + struct rtw89_dack_info *dack = &rtwdev->dack; + u32 i_dc, q_dc, ic, qc; + u32 val; + int ret; + + rtw89_rfk_parser(rtwdev, &rtw8851b_dadck_setup_defs_tbl); + + ret = read_poll_timeout_atomic(rtw89_phy_read32_mask, val, val, + 1, 10000, false, + rtwdev, R_ADDCKR0, BIT(0)); + if (ret) { + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]S0 DADCK timeout\n"); + dack->addck_timeout[0] = true; + } + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]DADCK ret = %d\n", ret); + + rtw89_phy_write32_mask(rtwdev, R_ADDCK0, B_ADDCK0_IQ, 0x0); + i_dc = rtw89_phy_read32_mask(rtwdev, R_ADDCKR0, B_ADDCKR0_DC); + rtw89_phy_write32_mask(rtwdev, R_ADDCK0, B_ADDCK0_IQ, 0x1); + q_dc = rtw89_phy_read32_mask(rtwdev, R_ADDCKR0, B_ADDCKR0_DC); + + ic = 0x80 - sign_extend32(i_dc, 11) * 6; + qc = 0x80 - sign_extend32(q_dc, 11) * 6; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[DACK]before DADCK, i_dc=0x%x, q_dc=0x%x\n", i_dc, q_dc); + + dack->dadck_d[0][0] = ic; + dack->dadck_d[0][1] = qc; + + rtw89_phy_write32_mask(rtwdev, R_DACKN0_CTL, B_DACKN0_V, dack->dadck_d[0][0]); + rtw89_phy_write32_mask(rtwdev, R_DACKN1_CTL, B_DACKN1_V, dack->dadck_d[0][1]); + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[DACK]after DADCK, 0xc210=0x%x, 0xc224=0x%x\n", + rtw89_phy_read32_mask(rtwdev, R_DACKN0_CTL, MASKDWORD), + rtw89_phy_read32_mask(rtwdev, R_DACKN1_CTL, MASKDWORD)); + + rtw89_rfk_parser(rtwdev, &rtw8851b_dadck_post_defs_tbl); +} + +static bool _dack_s0_poll(struct rtw89_dev *rtwdev) +{ + if (rtw89_phy_read32_mask(rtwdev, R_DACK_S0P0, B_DACK_S0P0_OK) == 0 || + rtw89_phy_read32_mask(rtwdev, R_DACK_S0P1, B_DACK_S0P1_OK) == 0 || + rtw89_phy_read32_mask(rtwdev, R_DACK_S0P2, B_DACK_S0P2_OK) == 0 || + rtw89_phy_read32_mask(rtwdev, R_DACK_S0P3, B_DACK_S0P3_OK) == 0) + return false; + + return true; +} + +static void _dack_s0(struct rtw89_dev *rtwdev) +{ + struct rtw89_dack_info *dack = &rtwdev->dack; + bool done; + int ret; + + rtw89_rfk_parser(rtwdev, &rtw8851b_dack_s0_1_defs_tbl); + _dack_reset(rtwdev, RF_PATH_A); + rtw89_phy_write32_mask(rtwdev, R_DCOF1, B_DCOF1_S, 0x1); + + ret = read_poll_timeout_atomic(_dack_s0_poll, done, done, + 1, 10000, false, rtwdev); + if (ret) { + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]S0 DACK timeout\n"); + dack->msbk_timeout[0] = true; + } + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]DACK ret = %d\n", ret); + + rtw89_rfk_parser(rtwdev, &rtw8851b_dack_s0_2_defs_tbl); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]after S0 DADCK\n"); + + _dack_backup_s0(rtwdev); + _dack_reload(rtwdev, RF_PATH_A); + + rtw89_phy_write32_mask(rtwdev, R_P0_NRBW, B_P0_NRBW_DBG, 0x0); +} + +static void _dack(struct rtw89_dev *rtwdev) +{ + _dack_s0(rtwdev); +} + +static void _dack_dump(struct rtw89_dev *rtwdev) +{ + struct rtw89_dack_info *dack = &rtwdev->dack; + u8 i; + u8 t; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]S0 ADC_DCK ic = 0x%x, qc = 0x%x\n", + dack->addck_d[0][0], dack->addck_d[0][1]); + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]S0 DAC_DCK ic = 0x%x, qc = 0x%x\n", + dack->dadck_d[0][0], dack->dadck_d[0][1]); + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]S0 biask ic = 0x%x, qc = 0x%x\n", + dack->biask_d[0][0], dack->biask_d[0][1]); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]S0 MSBK ic:\n"); + for (i = 0; i < RTW89_DACK_MSBK_NR; i++) { + t = dack->msbk_d[0][0][i]; + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]0x%x\n", t); + } + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]S0 MSBK qc:\n"); + for (i = 0; i < RTW89_DACK_MSBK_NR; i++) { + t = dack->msbk_d[0][1][i]; + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]0x%x\n", t); + } +} + +static void _dack_manual_off(struct rtw89_dev *rtwdev) +{ + rtw89_rfk_parser(rtwdev, &rtw8851b_dack_manual_off_defs_tbl); +} + +static void _dac_cal(struct rtw89_dev *rtwdev, bool force) +{ + struct rtw89_dack_info *dack = &rtwdev->dack; + u32 rf0_0; + + dack->dack_done = false; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]DACK 0x2\n"); + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]DACK start!!!\n"); + rf0_0 = rtw89_read_rf(rtwdev, RF_PATH_A, RR_MOD, RFREG_MASK); + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]RF0=0x%x\n", rf0_0); + + _drck(rtwdev); + _dack_manual_off(rtwdev); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MOD, RFREG_MASK, 0x337e1); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_RSV1, RR_RSV1_RST, 0x0); + + _addck(rtwdev); + _addck_backup(rtwdev); + _addck_reload(rtwdev); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MOD, RFREG_MASK, 0x40001); + + _dack(rtwdev); + _new_dadck(rtwdev); + _dack_dump(rtwdev); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_RSV1, RR_RSV1_RST, 0x1); + + dack->dack_done = true; + dack->dack_cnt++; + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DACK]DACK finish!!!\n"); +} + +static void _iqk_sram(struct rtw89_dev *rtwdev, u8 path) +{ + u32 i; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]===>%s\n", __func__); + + rtw89_phy_write32_mask(rtwdev, R_KIP_RPT1, MASKDWORD, 0x00020000); + rtw89_phy_write32_mask(rtwdev, R_MDPK_RX_DCK, MASKDWORD, 0x80000000); + rtw89_phy_write32_mask(rtwdev, R_SRAM_IQRX2, MASKDWORD, 0x00000080); + rtw89_phy_write32_mask(rtwdev, R_SRAM_IQRX, MASKDWORD, 0x00010000); + rtw89_phy_write32_mask(rtwdev, R_IQK_DIF4, B_IQK_DIF4_TXT, 0x009); + + for (i = 0; i <= 0x9f; i++) { + rtw89_phy_write32_mask(rtwdev, R_SRAM_IQRX, MASKDWORD, + 0x00010000 + i); + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]0x%x\n", + rtw89_phy_read32_mask(rtwdev, R_RPT_COM, B_PRT_COM_DCI)); + } + + for (i = 0; i <= 0x9f; i++) { + rtw89_phy_write32_mask(rtwdev, R_SRAM_IQRX, MASKDWORD, + 0x00010000 + i); + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]0x%x\n", + rtw89_phy_read32_mask(rtwdev, R_RPT_COM, B_PRT_COM_DCQ)); + } + + rtw89_phy_write32_mask(rtwdev, R_SRAM_IQRX2, MASKDWORD, 0x00000000); + rtw89_phy_write32_mask(rtwdev, R_SRAM_IQRX, MASKDWORD, 0x00000000); +} + +static void _iqk_rxk_setting(struct rtw89_dev *rtwdev, u8 path) +{ + rtw89_write_rf(rtwdev, path, RR_MOD, RR_MOD_MASK, 0xc); + rtw89_write_rf(rtwdev, path, RR_RXKPLL, RR_RXKPLL_POW, 0x0); + rtw89_write_rf(rtwdev, path, RR_RXKPLL, RR_RXKPLL_POW, 0x1); +} + +static bool _iqk_check_cal(struct rtw89_dev *rtwdev, u8 path) +{ + bool fail1 = false, fail2 = false; + u32 val; + int ret; + + ret = read_poll_timeout_atomic(rtw89_phy_read32_mask, val, val == 0x55, + 10, 8200, false, + rtwdev, 0xbff8, MASKBYTE0); + if (ret) { + fail1 = true; + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]NCTL1 IQK timeout!!!\n"); + } + + fsleep(10); + + ret = read_poll_timeout_atomic(rtw89_phy_read32_mask, val, val == 0x8000, + 10, 200, false, + rtwdev, R_RPT_COM, B_RPT_COM_RDY); + if (ret) { + fail2 = true; + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]NCTL2 IQK timeout!!!\n"); + } + + fsleep(10); + rtw89_phy_write32_mask(rtwdev, R_NCTL_N1, MASKBYTE0, 0x0); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]S%x, ret = %d, notready = %x fail=%d,%d\n", + path, ret, fail1 || fail2, fail1, fail2); + + return fail1 || fail2; +} + +static bool _iqk_one_shot(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, + u8 path, u8 ktype) +{ + struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; + bool notready; + u32 iqk_cmd; + + switch (ktype) { + case ID_A_FLOK_COARSE: + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]============ S%d ID_A_FLOK_COARSE ============\n", path); + rtw89_phy_write32_mask(rtwdev, R_UPD_CLK, B_IQK_RFC_ON, 0x1); + iqk_cmd = 0x108 | (1 << (4 + path)); + break; + case ID_G_FLOK_COARSE: + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]============ S%d ID_G_FLOK_COARSE ============\n", path); + rtw89_phy_write32_mask(rtwdev, R_UPD_CLK, B_IQK_RFC_ON, 0x1); + iqk_cmd = 0x108 | (1 << (4 + path)); + break; + case ID_A_FLOK_FINE: + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]============ S%d ID_A_FLOK_FINE ============\n", path); + rtw89_phy_write32_mask(rtwdev, R_UPD_CLK, B_IQK_RFC_ON, 0x1); + iqk_cmd = 0x308 | (1 << (4 + path)); + break; + case ID_G_FLOK_FINE: + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]============ S%d ID_G_FLOK_FINE ============\n", path); + rtw89_phy_write32_mask(rtwdev, R_UPD_CLK, B_IQK_RFC_ON, 0x1); + iqk_cmd = 0x308 | (1 << (4 + path)); + break; + case ID_TXK: + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]============ S%d ID_TXK ============\n", path); + rtw89_phy_write32_mask(rtwdev, R_UPD_CLK, B_IQK_RFC_ON, 0x0); + iqk_cmd = 0x008 | (1 << (path + 4)) | + (((0x8 + iqk_info->iqk_bw[path]) & 0xf) << 8); + break; + case ID_RXAGC: + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]============ S%d ID_RXAGC ============\n", path); + rtw89_phy_write32_mask(rtwdev, R_UPD_CLK, B_IQK_RFC_ON, 0x1); + iqk_cmd = 0x708 | (1 << (4 + path)) | (path << 1); + break; + case ID_RXK: + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]============ S%d ID_RXK ============\n", path); + rtw89_phy_write32_mask(rtwdev, R_UPD_CLK, B_IQK_RFC_ON, 0x1); + iqk_cmd = 0x008 | (1 << (path + 4)) | + (((0xc + iqk_info->iqk_bw[path]) & 0xf) << 8); + break; + case ID_NBTXK: + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]============ S%d ID_NBTXK ============\n", path); + rtw89_phy_write32_mask(rtwdev, R_UPD_CLK, B_IQK_RFC_ON, 0x0); + rtw89_phy_write32_mask(rtwdev, R_IQK_DIF4, B_IQK_DIF4_TXT, + 0x00b); + iqk_cmd = 0x408 | (1 << (4 + path)); + break; + case ID_NBRXK: + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]============ S%d ID_NBRXK ============\n", path); + rtw89_phy_write32_mask(rtwdev, R_UPD_CLK, B_IQK_RFC_ON, 0x1); + rtw89_phy_write32_mask(rtwdev, R_IQK_DIF4, B_IQK_DIF4_RXT, + 0x011); + iqk_cmd = 0x608 | (1 << (4 + path)); + break; + default: + return false; + } + + rtw89_phy_write32_mask(rtwdev, R_NCTL_CFG, MASKDWORD, iqk_cmd + 1); + notready = _iqk_check_cal(rtwdev, path); + if (iqk_info->iqk_sram_en && + (ktype == ID_NBRXK || ktype == ID_RXK)) + _iqk_sram(rtwdev, path); + + rtw89_phy_write32_mask(rtwdev, R_UPD_CLK, B_IQK_RFC_ON, 0x0); + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]S%x, ktype= %x, id = %x, notready = %x\n", + path, ktype, iqk_cmd + 1, notready); + + return notready; +} + +static bool _rxk_2g_group_sel(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx, u8 path) +{ + struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; + bool kfail = false; + bool notready; + u32 rf_0; + u8 gp; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]===>%s\n", __func__); + + for (gp = 0; gp < RTW8851B_RXK_GROUP_NR; gp++) { + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]S%x, gp = %x\n", path, gp); + + rtw89_write_rf(rtwdev, path, RR_MOD, RR_MOD_RGM, g_idxrxgain[gp]); + rtw89_write_rf(rtwdev, path, RR_RXBB, RR_RXBB_C2, g_idxattc2[gp]); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT, B_CFIR_LUT_SEL, 0x1); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT, B_CFIR_LUT_G3, 0x0); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT, B_CFIR_LUT_GP_V1, gp); + + rtw89_write_rf(rtwdev, path, RR_RXKPLL, RFREG_MASK, 0x80013); + fsleep(10); + rf_0 = rtw89_read_rf(rtwdev, path, RR_MOD, RFREG_MASK); + rtw89_phy_write32_mask(rtwdev, R_IQK_DIF2, B_IQK_DIF2_RXPI, rf_0); + rtw89_phy_write32_mask(rtwdev, R_IQK_RXA, B_IQK_RXAGC, g_idxrxagc[gp]); + rtw89_phy_write32_mask(rtwdev, R_IQK_DIF4, B_IQK_DIF4_RXT, 0x11); + + notready = _iqk_one_shot(rtwdev, phy_idx, path, ID_RXAGC); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]S%x, RXAGC 0x8008 = 0x%x, rxbb = %x\n", path, + rtw89_phy_read32_mask(rtwdev, R_NCTL_RPT, MASKDWORD), + rtw89_read_rf(rtwdev, path, RR_MOD, 0x003e0)); + + if (gp == 0x3) { + rtw89_write_rf(rtwdev, path, RR_RXKPLL, RR_RXKPLL_OFF, 0x13); + rtw89_phy_write32_mask(rtwdev, R_IQK_DIF4, B_IQK_DIF4_RXT, 0x011); + notready = _iqk_one_shot(rtwdev, phy_idx, path, ID_NBRXK); + iqk_info->nb_rxcfir[path] = + rtw89_phy_read32_mask(rtwdev, R_RXIQC, MASKDWORD) | 0x2; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]S%x, NBRXK 0x8008 = 0x%x\n", path, + rtw89_phy_read32_mask(rtwdev, R_NCTL_RPT, MASKDWORD)); + } + + notready = _iqk_one_shot(rtwdev, phy_idx, path, ID_RXK); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]S%x, WBRXK 0x8008 = 0x%x\n", path, + rtw89_phy_read32_mask(rtwdev, R_NCTL_RPT, MASKDWORD)); + } + + if (!notready) + kfail = !!rtw89_phy_read32_mask(rtwdev, R_NCTL_RPT, B_NCTL_RPT_FLG); + + if (kfail) + _iqk_sram(rtwdev, path); + + if (kfail) { + rtw89_phy_write32_mask(rtwdev, R_RXIQC + (path << 8), + MASKDWORD, iqk_info->nb_rxcfir[path] | 0x2); + iqk_info->is_wb_txiqk[path] = false; + } else { + rtw89_phy_write32_mask(rtwdev, R_RXIQC + (path << 8), + MASKDWORD, 0x40000000); + iqk_info->is_wb_txiqk[path] = true; + } + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]S%x, kfail = 0x%x, 0x8%x3c = 0x%x\n", path, kfail, + 1 << path, iqk_info->nb_rxcfir[path]); + return kfail; +} + +static bool _rxk_5g_group_sel(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx, u8 path) +{ + struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; + bool kfail = false; + bool notready; + u32 rf_0; + u8 gp; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]===>%s\n", __func__); + + for (gp = 0; gp < RTW8851B_RXK_GROUP_NR; gp++) { + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]S%x, gp = %x\n", path, gp); + + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MOD, 0x03ff0, a_idxrxgain[gp]); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_RXA2, RR_RXA2_ATT, a_idxattc2[gp]); + + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT, B_CFIR_LUT_SEL, 0x1); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT, B_CFIR_LUT_G3, 0x0); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT, B_CFIR_LUT_GP_V1, gp); + + rtw89_write_rf(rtwdev, path, RR_RXKPLL, RFREG_MASK, 0x80013); + fsleep(100); + rf_0 = rtw89_read_rf(rtwdev, path, RR_MOD, RFREG_MASK); + rtw89_phy_write32_mask(rtwdev, R_IQK_DIF2, B_IQK_DIF2_RXPI, rf_0); + rtw89_phy_write32_mask(rtwdev, R_IQK_RXA, B_IQK_RXAGC, a_idxrxagc[gp]); + rtw89_phy_write32_mask(rtwdev, R_IQK_DIF4, B_IQK_DIF4_RXT, 0x11); + notready = _iqk_one_shot(rtwdev, phy_idx, path, ID_RXAGC); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]S%x, RXAGC 0x8008 = 0x%x, rxbb = %x\n", path, + rtw89_phy_read32_mask(rtwdev, R_NCTL_RPT, MASKDWORD), + rtw89_read_rf(rtwdev, path, RR_MOD, RR_MOD_RXB)); + + if (gp == 0x3) { + rtw89_write_rf(rtwdev, path, RR_RXKPLL, RR_RXKPLL_OFF, 0x13); + rtw89_phy_write32_mask(rtwdev, R_IQK_DIF4, B_IQK_DIF4_RXT, 0x011); + notready = _iqk_one_shot(rtwdev, phy_idx, path, ID_NBRXK); + iqk_info->nb_rxcfir[path] = + rtw89_phy_read32_mask(rtwdev, R_RXIQC, MASKDWORD) | 0x2; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]S%x, NBRXK 0x8008 = 0x%x\n", path, + rtw89_phy_read32_mask(rtwdev, R_NCTL_RPT, MASKDWORD)); + } + + notready = _iqk_one_shot(rtwdev, phy_idx, path, ID_RXK); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]S%x, WBRXK 0x8008 = 0x%x\n", path, + rtw89_phy_read32_mask(rtwdev, R_NCTL_RPT, MASKDWORD)); + } + + if (!notready) + kfail = !!rtw89_phy_read32_mask(rtwdev, R_NCTL_RPT, B_NCTL_RPT_FLG); + + if (kfail) + _iqk_sram(rtwdev, path); + + if (kfail) { + rtw89_phy_write32_mask(rtwdev, R_RXIQC + (path << 8), MASKDWORD, + iqk_info->nb_rxcfir[path] | 0x2); + iqk_info->is_wb_txiqk[path] = false; + } else { + rtw89_phy_write32_mask(rtwdev, R_RXIQC + (path << 8), MASKDWORD, + 0x40000000); + iqk_info->is_wb_txiqk[path] = true; + } + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]S%x, kfail = 0x%x, 0x8%x3c = 0x%x\n", path, kfail, + 1 << path, iqk_info->nb_rxcfir[path]); + return kfail; +} + +static bool _iqk_5g_nbrxk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, + u8 path) +{ + struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; + bool kfail = false; + bool notready; + u8 gp = 0x3; + u32 rf_0; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]===>%s\n", __func__); + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]S%x, gp = %x\n", path, gp); + + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MOD, RR_MOD_RGM, a_idxrxgain[gp]); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_RXA2, RR_RXA2_ATT, a_idxattc2[gp]); + + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT, B_CFIR_LUT_SEL, 0x1); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT, B_CFIR_LUT_G3, 0x0); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT, B_CFIR_LUT_GP_V1, gp); + + rtw89_write_rf(rtwdev, path, RR_RXKPLL, RFREG_MASK, 0x80013); + fsleep(100); + rf_0 = rtw89_read_rf(rtwdev, path, RR_MOD, RFREG_MASK); + rtw89_phy_write32_mask(rtwdev, R_IQK_DIF2, B_IQK_DIF2_RXPI, rf_0); + rtw89_phy_write32_mask(rtwdev, R_IQK_RXA, B_IQK_RXAGC, a_idxrxagc[gp]); + rtw89_phy_write32_mask(rtwdev, R_IQK_DIF4, B_IQK_DIF4_RXT, 0x11); + notready = _iqk_one_shot(rtwdev, phy_idx, path, ID_RXAGC); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]S%x, RXAGC 0x8008 = 0x%x, rxbb = %x\n", path, + rtw89_phy_read32_mask(rtwdev, R_NCTL_RPT, MASKDWORD), + rtw89_read_rf(rtwdev, path, RR_MOD, 0x003e0)); + + if (gp == 0x3) { + rtw89_write_rf(rtwdev, path, RR_RXKPLL, RR_RXKPLL_OFF, 0x13); + rtw89_phy_write32_mask(rtwdev, R_IQK_DIF4, B_IQK_DIF4_RXT, 0x011); + notready = _iqk_one_shot(rtwdev, phy_idx, path, ID_NBRXK); + iqk_info->nb_rxcfir[path] = + rtw89_phy_read32_mask(rtwdev, R_RXIQC, MASKDWORD) | 0x2; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]S%x, NBRXK 0x8008 = 0x%x\n", path, + rtw89_phy_read32_mask(rtwdev, R_NCTL_RPT, MASKDWORD)); + } + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]S%x, WBRXK 0x8008 = 0x%x\n", + path, rtw89_phy_read32_mask(rtwdev, R_NCTL_RPT, MASKDWORD)); + + if (!notready) + kfail = !!rtw89_phy_read32_mask(rtwdev, R_NCTL_RPT, B_NCTL_RPT_FLG); + + if (kfail) { + rtw89_phy_write32_mask(rtwdev, R_RXIQC + (path << 8), + MASKDWORD, 0x40000002); + iqk_info->is_wb_rxiqk[path] = false; + } else { + iqk_info->is_wb_rxiqk[path] = false; + } + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]S%x, kfail = 0x%x, 0x8%x3c = 0x%x\n", path, kfail, + 1 << path, iqk_info->nb_rxcfir[path]); + + return kfail; +} + +static bool _iqk_2g_nbrxk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, + u8 path) +{ + struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; + bool kfail = false; + bool notready; + u8 gp = 0x3; + u32 rf_0; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]===>%s\n", __func__); + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]S%x, gp = %x\n", path, gp); + + rtw89_write_rf(rtwdev, path, RR_MOD, RR_MOD_RGM, g_idxrxgain[gp]); + rtw89_write_rf(rtwdev, path, RR_RXBB, RR_RXBB_C2, g_idxattc2[gp]); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT, B_CFIR_LUT_SEL, 0x1); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT, B_CFIR_LUT_G3, 0x0); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT, B_CFIR_LUT_GP_V1, gp); + + rtw89_write_rf(rtwdev, path, RR_RXKPLL, RFREG_MASK, 0x80013); + fsleep(10); + rf_0 = rtw89_read_rf(rtwdev, path, RR_MOD, RFREG_MASK); + rtw89_phy_write32_mask(rtwdev, R_IQK_DIF2, B_IQK_DIF2_RXPI, rf_0); + rtw89_phy_write32_mask(rtwdev, R_IQK_RXA, B_IQK_RXAGC, g_idxrxagc[gp]); + rtw89_phy_write32_mask(rtwdev, R_IQK_DIF4, B_IQK_DIF4_RXT, 0x11); + notready = _iqk_one_shot(rtwdev, phy_idx, path, ID_RXAGC); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]S%x, RXAGC 0x8008 = 0x%x, rxbb = %x\n", + path, rtw89_phy_read32_mask(rtwdev, R_NCTL_RPT, MASKDWORD), + rtw89_read_rf(rtwdev, path, RR_MOD, 0x003e0)); + + if (gp == 0x3) { + rtw89_write_rf(rtwdev, path, RR_RXKPLL, RR_RXKPLL_OFF, 0x13); + rtw89_phy_write32_mask(rtwdev, R_IQK_DIF4, B_IQK_DIF4_RXT, 0x011); + notready = _iqk_one_shot(rtwdev, phy_idx, path, ID_NBRXK); + iqk_info->nb_rxcfir[path] = + rtw89_phy_read32_mask(rtwdev, R_RXIQC, MASKDWORD) | 0x2; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]S%x, NBRXK 0x8008 = 0x%x\n", path, + rtw89_phy_read32_mask(rtwdev, R_NCTL_RPT, MASKDWORD)); + } + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]S%x, WBRXK 0x8008 = 0x%x\n", + path, rtw89_phy_read32_mask(rtwdev, R_NCTL_RPT, MASKDWORD)); + + if (!notready) + kfail = !!rtw89_phy_read32_mask(rtwdev, R_NCTL_RPT, B_NCTL_RPT_FLG); + + if (kfail) { + rtw89_phy_write32_mask(rtwdev, R_RXIQC + (path << 8), + MASKDWORD, 0x40000002); + iqk_info->is_wb_rxiqk[path] = false; + } else { + iqk_info->is_wb_rxiqk[path] = false; + } + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]S%x, kfail = 0x%x, 0x8%x3c = 0x%x\n", path, kfail, + 1 << path, iqk_info->nb_rxcfir[path]); + return kfail; +} + +static void _iqk_rxclk_setting(struct rtw89_dev *rtwdev, u8 path) +{ + struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; + + rtw89_write_rf(rtwdev, path, RR_RXBB2, RR_RXBB2_CKT, 0x1); + + if (iqk_info->iqk_bw[path] == RTW89_CHANNEL_WIDTH_80) + rtw89_rfk_parser(rtwdev, &rtw8851b_iqk_rxclk_80_defs_tbl); + else + rtw89_rfk_parser(rtwdev, &rtw8851b_iqk_rxclk_others_defs_tbl); +} + +static bool _txk_5g_group_sel(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx, u8 path) +{ + struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; + bool kfail = false; + bool notready; + u8 gp; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]===>%s\n", __func__); + + for (gp = 0x0; gp < RTW8851B_TXK_GROUP_NR; gp++) { + rtw89_write_rf(rtwdev, path, RR_TXIG, RR_TXIG_GR0, a_power_range[gp]); + rtw89_write_rf(rtwdev, path, RR_TXIG, RR_TXIG_GR1, a_track_range[gp]); + rtw89_write_rf(rtwdev, path, RR_TXIG, RR_TXIG_TG, a_gain_bb[gp]); + + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT, B_CFIR_LUT_SEL, 0x1); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT, B_CFIR_LUT_G3, 0x1); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT, B_CFIR_LUT_G2, 0x0); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT, B_CFIR_LUT_GP, gp); + rtw89_phy_write32_mask(rtwdev, R_NCTL_N1, B_NCTL_N1_CIP, 0x00); + rtw89_phy_write32_mask(rtwdev, R_KIP_IQP, MASKDWORD, a_itqt[gp]); + + notready = _iqk_one_shot(rtwdev, phy_idx, path, ID_NBTXK); + iqk_info->nb_txcfir[path] = + rtw89_phy_read32_mask(rtwdev, R_TXIQC, MASKDWORD) | 0x2; + + rtw89_phy_write32_mask(rtwdev, R_KIP_IQP + (path << 8), + MASKDWORD, a_itqt[gp]); + notready = _iqk_one_shot(rtwdev, phy_idx, path, ID_TXK); + } + + if (!notready) + kfail = !!rtw89_phy_read32_mask(rtwdev, R_NCTL_RPT, B_NCTL_RPT_FLG); + + if (kfail) { + rtw89_phy_write32_mask(rtwdev, R_TXIQC + (path << 8), + MASKDWORD, iqk_info->nb_txcfir[path] | 0x2); + iqk_info->is_wb_txiqk[path] = false; + } else { + rtw89_phy_write32_mask(rtwdev, R_TXIQC + (path << 8), + MASKDWORD, 0x40000000); + iqk_info->is_wb_txiqk[path] = true; + } + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]S%x, kfail = 0x%x, 0x8%x38 = 0x%x\n", path, kfail, + 1 << path, iqk_info->nb_txcfir[path]); + return kfail; +} + +static bool _txk_2g_group_sel(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx, u8 path) +{ + struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; + bool kfail = false; + bool notready; + u8 gp; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]===>%s\n", __func__); + + for (gp = 0x0; gp < RTW8851B_TXK_GROUP_NR; gp++) { + rtw89_write_rf(rtwdev, path, RR_TXIG, RR_TXIG_GR0, g_power_range[gp]); + rtw89_write_rf(rtwdev, path, RR_TXIG, RR_TXIG_GR1, g_track_range[gp]); + rtw89_write_rf(rtwdev, path, RR_TXIG, RR_TXIG_TG, g_gain_bb[gp]); + + rtw89_phy_write32_mask(rtwdev, R_KIP_IQP, MASKDWORD, g_itqt[gp]); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT, B_CFIR_LUT_SEL, 0x1); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT, B_CFIR_LUT_G3, 0x1); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT, B_CFIR_LUT_G2, 0x0); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT, B_CFIR_LUT_GP, gp); + rtw89_phy_write32_mask(rtwdev, R_NCTL_N1, B_NCTL_N1_CIP, 0x00); + + notready = _iqk_one_shot(rtwdev, phy_idx, path, ID_NBTXK); + iqk_info->nb_txcfir[path] = + rtw89_phy_read32_mask(rtwdev, R_TXIQC, MASKDWORD) | 0x2; + + rtw89_phy_write32_mask(rtwdev, R_KIP_IQP + (path << 8), + MASKDWORD, g_itqt[gp]); + notready = _iqk_one_shot(rtwdev, phy_idx, path, ID_TXK); + } + + if (!notready) + kfail = !!rtw89_phy_read32_mask(rtwdev, R_NCTL_RPT, B_NCTL_RPT_FLG); + + if (kfail) { + rtw89_phy_write32_mask(rtwdev, R_TXIQC + (path << 8), + MASKDWORD, iqk_info->nb_txcfir[path] | 0x2); + iqk_info->is_wb_txiqk[path] = false; + } else { + rtw89_phy_write32_mask(rtwdev, R_TXIQC + (path << 8), + MASKDWORD, 0x40000000); + iqk_info->is_wb_txiqk[path] = true; + } + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]S%x, kfail = 0x%x, 0x8%x38 = 0x%x\n", path, kfail, + 1 << path, iqk_info->nb_txcfir[path]); + return kfail; +} + +static bool _iqk_5g_nbtxk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, + u8 path) +{ + struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; + bool kfail = false; + bool notready; + u8 gp; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]===>%s\n", __func__); + + for (gp = 0x0; gp < RTW8851B_TXK_GROUP_NR; gp++) { + rtw89_write_rf(rtwdev, path, RR_TXIG, RR_TXIG_GR0, a_power_range[gp]); + rtw89_write_rf(rtwdev, path, RR_TXIG, RR_TXIG_GR1, a_track_range[gp]); + rtw89_write_rf(rtwdev, path, RR_TXIG, RR_TXIG_TG, a_gain_bb[gp]); + + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT, B_CFIR_LUT_SEL, 0x1); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT, B_CFIR_LUT_G3, 0x1); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT, B_CFIR_LUT_G2, 0x0); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT, B_CFIR_LUT_GP, gp); + rtw89_phy_write32_mask(rtwdev, R_NCTL_N1, B_NCTL_N1_CIP, 0x00); + rtw89_phy_write32_mask(rtwdev, R_KIP_IQP, MASKDWORD, a_itqt[gp]); + + notready = _iqk_one_shot(rtwdev, phy_idx, path, ID_NBTXK); + iqk_info->nb_txcfir[path] = + rtw89_phy_read32_mask(rtwdev, R_TXIQC, MASKDWORD) | 0x2; + } + + if (!notready) + kfail = !!rtw89_phy_read32_mask(rtwdev, R_NCTL_RPT, B_NCTL_RPT_FLG); + + if (kfail) { + rtw89_phy_write32_mask(rtwdev, R_TXIQC + (path << 8), + MASKDWORD, 0x40000002); + iqk_info->is_wb_rxiqk[path] = false; + } else { + iqk_info->is_wb_rxiqk[path] = false; + } + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]S%x, kfail = 0x%x, 0x8%x38 = 0x%x\n", path, kfail, + 1 << path, iqk_info->nb_txcfir[path]); + return kfail; +} + +static bool _iqk_2g_nbtxk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, + u8 path) +{ + struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; + bool kfail = false; + bool notready; + u8 gp; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]===>%s\n", __func__); + + for (gp = 0x0; gp < RTW8851B_TXK_GROUP_NR; gp++) { + rtw89_write_rf(rtwdev, path, RR_TXIG, RR_TXIG_GR0, g_power_range[gp]); + rtw89_write_rf(rtwdev, path, RR_TXIG, RR_TXIG_GR1, g_track_range[gp]); + rtw89_write_rf(rtwdev, path, RR_TXIG, RR_TXIG_TG, g_gain_bb[gp]); + + rtw89_phy_write32_mask(rtwdev, R_KIP_IQP, MASKDWORD, g_itqt[gp]); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT, B_CFIR_LUT_SEL, 0x1); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT, B_CFIR_LUT_G3, 0x1); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT, B_CFIR_LUT_G2, 0x0); + rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT, B_CFIR_LUT_GP, gp); + rtw89_phy_write32_mask(rtwdev, R_NCTL_N1, B_NCTL_N1_CIP, 0x00); + + notready = _iqk_one_shot(rtwdev, phy_idx, path, ID_NBTXK); + iqk_info->nb_txcfir[path] = + rtw89_phy_read32_mask(rtwdev, R_TXIQC + (path << 8), + MASKDWORD) | 0x2; + } + + if (!notready) + kfail = !!rtw89_phy_read32_mask(rtwdev, R_NCTL_RPT, B_NCTL_RPT_FLG); + + if (kfail) { + rtw89_phy_write32_mask(rtwdev, R_TXIQC + (path << 8), + MASKDWORD, 0x40000002); + iqk_info->is_wb_rxiqk[path] = false; + } else { + iqk_info->is_wb_rxiqk[path] = false; + } + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]S%x, kfail = 0x%x, 0x8%x38 = 0x%x\n", path, kfail, + 1 << path, iqk_info->nb_txcfir[path]); + return kfail; +} + +static bool _iqk_2g_lok(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, + u8 path) +{ + static const u32 g_txbb[RTW8851B_LOK_GRAM] = { + 0x02, 0x06, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x17}; + static const u32 g_itqt[RTW8851B_LOK_GRAM] = { + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x12, 0x12, 0x12, 0x1b}; + static const u32 g_wa[RTW8851B_LOK_GRAM] = { + 0x00, 0x04, 0x08, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x17}; + bool fail = false; + u8 i; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]===>%s\n", __func__); + + rtw89_write_rf(rtwdev, RF_PATH_A, RR_LUTDBG, RR_LUTDBG_LOK, 0x0); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_TXIG, RR_TXIG_GR0, 0x0); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_TXIG, RR_TXIG_GR1, 0x6); + + for (i = 0; i < RTW8851B_LOK_GRAM; i++) { + rtw89_write_rf(rtwdev, RF_PATH_A, RR_TXIG, RR_TXIG_TG, g_txbb[i]); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_LUTWA, RR_LUTWA_M1, g_wa[i]); + rtw89_phy_write32_mask(rtwdev, R_UPD_CLK, B_IQK_RFC_ON, 0x1); + rtw89_phy_write32_mask(rtwdev, R_KIP_IQP, B_KIP_IQP_IQSW, g_itqt[i]); + rtw89_phy_write32_mask(rtwdev, R_IQK_DIF4, B_IQK_DIF4_TXT, 0x021); + rtw89_phy_write32_mask(rtwdev, R_NCTL_CFG, MASKDWORD, + 0x00000109 | (1 << (4 + path))); + fail |= _iqk_check_cal(rtwdev, path); + + rtw89_phy_write32_mask(rtwdev, R_NCTL_N1, B_NCTL_N1_CIP, 0x00); + rtw89_phy_write32_mask(rtwdev, R_KIP_IQP, B_KIP_IQP_IQSW, g_itqt[i]); + rtw89_phy_write32_mask(rtwdev, R_NCTL_CFG, MASKDWORD, + 0x00000309 | (1 << (4 + path))); + fail |= _iqk_check_cal(rtwdev, path); + + rtw89_phy_write32_mask(rtwdev, R_NCTL_N1, B_NCTL_N1_CIP, 0x00); + rtw89_phy_write32_mask(rtwdev, R_UPD_CLK, B_IQK_RFC_ON, 0x0); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]S0, i = %x, 0x8[19:15] = 0x%x,0x8[09:05] = 0x%x\n", i, + rtw89_read_rf(rtwdev, RF_PATH_A, RR_DTXLOK, 0xf8000), + rtw89_read_rf(rtwdev, RF_PATH_A, RR_DTXLOK, 0x003e0)); + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]S0, i = %x, 0x9[19:16] = 0x%x,0x9[09:06] = 0x%x\n", i, + rtw89_read_rf(rtwdev, RF_PATH_A, RR_RSV2, 0xf0000), + rtw89_read_rf(rtwdev, RF_PATH_A, RR_RSV2, 0x003c0)); + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]S0, i = %x, 0x58 = %x\n", i, + rtw89_read_rf(rtwdev, RF_PATH_A, RR_TXMO, RFREG_MASK)); + } + + return fail; +} + +static bool _iqk_5g_lok(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, + u8 path) +{ + static const u32 a_txbb[RTW8851B_LOK_GRAM] = { + 0x02, 0x06, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x17}; + static const u32 a_itqt[RTW8851B_LOK_GRAM] = { + 0x09, 0x09, 0x09, 0x12, 0x12, 0x12, 0x1b, 0x1b, 0x1b, 0x1b}; + static const u32 a_wa[RTW8851B_LOK_GRAM] = { + 0x80, 0x84, 0x88, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x97}; + bool fail = false; + u8 i; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]===>%s\n", __func__); + + rtw89_write_rf(rtwdev, RF_PATH_A, RR_LUTDBG, RR_LUTDBG_LOK, 0x0); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_TXIG, RR_TXIG_GR0, 0x0); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_TXIG, RR_TXIG_GR1, 0x7); + + for (i = 0; i < RTW8851B_LOK_GRAM; i++) { + rtw89_write_rf(rtwdev, RF_PATH_A, RR_TXIG, RR_TXIG_TG, a_txbb[i]); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_LUTWA, RR_LUTWA_M1, a_wa[i]); + rtw89_phy_write32_mask(rtwdev, R_UPD_CLK, B_IQK_RFC_ON, 0x1); + rtw89_phy_write32_mask(rtwdev, R_KIP_IQP, B_KIP_IQP_IQSW, a_itqt[i]); + rtw89_phy_write32_mask(rtwdev, R_IQK_DIF4, B_IQK_DIF4_TXT, 0x021); + rtw89_phy_write32_mask(rtwdev, R_NCTL_CFG, MASKDWORD, + 0x00000109 | (1 << (4 + path))); + fail |= _iqk_check_cal(rtwdev, path); + + rtw89_phy_write32_mask(rtwdev, R_NCTL_N1, B_NCTL_N1_CIP, 0x00); + rtw89_phy_write32_mask(rtwdev, R_KIP_IQP, B_KIP_IQP_IQSW, a_itqt[i]); + rtw89_phy_write32_mask(rtwdev, R_IQK_DIF4, B_IQK_DIF4_TXT, 0x021); + rtw89_phy_write32_mask(rtwdev, R_NCTL_CFG, MASKDWORD, + 0x00000309 | (1 << (4 + path))); + fail |= _iqk_check_cal(rtwdev, path); + + rtw89_phy_write32_mask(rtwdev, R_NCTL_N1, B_NCTL_N1_CIP, 0x00); + rtw89_phy_write32_mask(rtwdev, R_UPD_CLK, B_IQK_RFC_ON, 0x0); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]S0, i = %x, 0x8[19:15] = 0x%x,0x8[09:05] = 0x%x\n", i, + rtw89_read_rf(rtwdev, RF_PATH_A, RR_DTXLOK, 0xf8000), + rtw89_read_rf(rtwdev, RF_PATH_A, RR_DTXLOK, 0x003e0)); + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]S0, i = %x, 0x9[19:16] = 0x%x,0x9[09:06] = 0x%x\n", i, + rtw89_read_rf(rtwdev, RF_PATH_A, RR_RSV2, 0xf0000), + rtw89_read_rf(rtwdev, RF_PATH_A, RR_RSV2, 0x003c0)); + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]S0, i = %x, 0x58 = %x\n", i, + rtw89_read_rf(rtwdev, RF_PATH_A, RR_TXMO, RFREG_MASK)); + } + + return fail; +} + +static void _iqk_txk_setting(struct rtw89_dev *rtwdev, u8 path) +{ + struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; + + switch (iqk_info->iqk_band[path]) { + case RTW89_BAND_2G: + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]RTW89_BAND_2G\n"); + rtw89_rfk_parser(rtwdev, &rtw8851b_iqk_txk_2ghz_defs_tbl); + break; + case RTW89_BAND_5G: + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]RTW89_BAND_5G\n"); + rtw89_rfk_parser(rtwdev, &rtw8851b_iqk_txk_5ghz_defs_tbl); + break; + default: + break; + } +} + +#define IQK_LOK_RETRY 1 + +static void _iqk_by_path(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, + u8 path) +{ + struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; + bool lok_is_fail; + u8 i; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]===>%s\n", __func__); + + for (i = 0; i < IQK_LOK_RETRY; i++) { + _iqk_txk_setting(rtwdev, path); + if (iqk_info->iqk_band[path] == RTW89_BAND_2G) + lok_is_fail = _iqk_2g_lok(rtwdev, phy_idx, path); + else + lok_is_fail = _iqk_5g_lok(rtwdev, phy_idx, path); + + if (!lok_is_fail) + break; + } + + if (iqk_info->is_nbiqk) { + if (iqk_info->iqk_band[path] == RTW89_BAND_2G) + iqk_info->iqk_tx_fail[0][path] = + _iqk_2g_nbtxk(rtwdev, phy_idx, path); + else + iqk_info->iqk_tx_fail[0][path] = + _iqk_5g_nbtxk(rtwdev, phy_idx, path); + } else { + if (iqk_info->iqk_band[path] == RTW89_BAND_2G) + iqk_info->iqk_tx_fail[0][path] = + _txk_2g_group_sel(rtwdev, phy_idx, path); + else + iqk_info->iqk_tx_fail[0][path] = + _txk_5g_group_sel(rtwdev, phy_idx, path); + } + + _iqk_rxclk_setting(rtwdev, path); + _iqk_rxk_setting(rtwdev, path); + _adc_fifo_rst(rtwdev, phy_idx, path); + + if (iqk_info->is_nbiqk) { + if (iqk_info->iqk_band[path] == RTW89_BAND_2G) + iqk_info->iqk_rx_fail[0][path] = + _iqk_2g_nbrxk(rtwdev, phy_idx, path); + else + iqk_info->iqk_rx_fail[0][path] = + _iqk_5g_nbrxk(rtwdev, phy_idx, path); + } else { + if (iqk_info->iqk_band[path] == RTW89_BAND_2G) + iqk_info->iqk_rx_fail[0][path] = + _rxk_2g_group_sel(rtwdev, phy_idx, path); + else + iqk_info->iqk_rx_fail[0][path] = + _rxk_5g_group_sel(rtwdev, phy_idx, path); + } +} + +static void _rfk_backup_bb_reg(struct rtw89_dev *rtwdev, + u32 backup_bb_reg_val[]) +{ + u32 i; + + for (i = 0; i < BACKUP_BB_REGS_NR; i++) { + backup_bb_reg_val[i] = + rtw89_phy_read32_mask(rtwdev, rtw8851b_backup_bb_regs[i], + MASKDWORD); + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[RFK]backup bb reg : %x, value =%x\n", + rtw8851b_backup_bb_regs[i], backup_bb_reg_val[i]); + } +} + +static void _rfk_backup_rf_reg(struct rtw89_dev *rtwdev, + u32 backup_rf_reg_val[], u8 rf_path) +{ + u32 i; + + for (i = 0; i < BACKUP_RF_REGS_NR; i++) { + backup_rf_reg_val[i] = + rtw89_read_rf(rtwdev, rf_path, + rtw8851b_backup_rf_regs[i], RFREG_MASK); + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[RFK]backup rf S%d reg : %x, value =%x\n", rf_path, + rtw8851b_backup_rf_regs[i], backup_rf_reg_val[i]); + } +} + +static void _rfk_restore_bb_reg(struct rtw89_dev *rtwdev, + const u32 backup_bb_reg_val[]) +{ + u32 i; + + for (i = 0; i < BACKUP_BB_REGS_NR; i++) { + rtw89_phy_write32_mask(rtwdev, rtw8851b_backup_bb_regs[i], + MASKDWORD, backup_bb_reg_val[i]); + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[RFK]restore bb reg : %x, value =%x\n", + rtw8851b_backup_bb_regs[i], backup_bb_reg_val[i]); + } +} + +static void _rfk_restore_rf_reg(struct rtw89_dev *rtwdev, + const u32 backup_rf_reg_val[], u8 rf_path) +{ + u32 i; + + for (i = 0; i < BACKUP_RF_REGS_NR; i++) { + rtw89_write_rf(rtwdev, rf_path, rtw8851b_backup_rf_regs[i], + RFREG_MASK, backup_rf_reg_val[i]); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[RFK]restore rf S%d reg: %x, value =%x\n", rf_path, + rtw8851b_backup_rf_regs[i], backup_rf_reg_val[i]); + } +} + +static void _iqk_get_ch_info(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + u8 path) +{ + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0); + struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; + u8 idx = 0; + + iqk_info->iqk_band[path] = chan->band_type; + iqk_info->iqk_bw[path] = chan->band_width; + iqk_info->iqk_ch[path] = chan->channel; + iqk_info->iqk_table_idx[path] = idx; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]S%d (PHY%d): / DBCC %s/ %s/ CH%d/ %s\n", + path, phy, rtwdev->dbcc_en ? "on" : "off", + iqk_info->iqk_band[path] == 0 ? "2G" : + iqk_info->iqk_band[path] == 1 ? "5G" : "6G", + iqk_info->iqk_ch[path], + iqk_info->iqk_bw[path] == 0 ? "20M" : + iqk_info->iqk_bw[path] == 1 ? "40M" : "80M"); + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]times = 0x%x, ch =%x\n", + iqk_info->iqk_times, idx); + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]S%x, iqk_info->syn1to2= 0x%x\n", + path, iqk_info->syn1to2); +} + +static void _iqk_start_iqk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, + u8 path) +{ + _iqk_by_path(rtwdev, phy_idx, path); +} + +static void _iqk_restore(struct rtw89_dev *rtwdev, u8 path) +{ + bool fail; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]===>%s\n", __func__); + + rtw89_phy_write32_mask(rtwdev, R_NCTL_CFG, MASKDWORD, 0x00001219); + fsleep(10); + fail = _iqk_check_cal(rtwdev, path); + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK] restore fail=%d\n", fail); + + rtw89_phy_write32_mask(rtwdev, R_NCTL_N1, B_NCTL_N1_CIP, 0x00); + rtw89_phy_write32_mask(rtwdev, R_NCTL_RPT, MASKDWORD, 0x00000000); + rtw89_phy_write32_mask(rtwdev, R_KIP_SYSCFG, MASKDWORD, 0x80000000); +} + +static void _iqk_afebb_restore(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx, u8 path) +{ + rtw89_rfk_parser(rtwdev, &rtw8851b_iqk_afebb_restore_defs_tbl); +} + +static void _iqk_preset(struct rtw89_dev *rtwdev, u8 path) +{ + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]===>%s\n", __func__); + + rtw89_write_rf(rtwdev, path, RR_RSV1, RR_RSV1_RST, 0x0); + rtw89_phy_write32_mask(rtwdev, R_NCTL_RPT, MASKDWORD, 0x00000080); + rtw89_phy_write32_mask(rtwdev, R_KIP_SYSCFG, MASKDWORD, 0x81ff010a); +} + +static void _iqk_macbb_setting(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx, u8 path) +{ + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]===>%s\n", __func__); + + rtw89_rfk_parser(rtwdev, &rtw8851b_iqk_macbb_defs_tbl); +} + +static void _iqk_init(struct rtw89_dev *rtwdev) +{ + struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; + u8 idx, path; + + rtw89_phy_write32_mask(rtwdev, R_IQKINF, MASKDWORD, 0x0); + + if (iqk_info->is_iqk_init) + return; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]===>%s\n", __func__); + + iqk_info->is_iqk_init = true; + iqk_info->is_nbiqk = false; + iqk_info->iqk_fft_en = false; + iqk_info->iqk_sram_en = false; + iqk_info->iqk_cfir_en = false; + iqk_info->iqk_xym_en = false; + iqk_info->thermal_rek_en = false; + iqk_info->iqk_times = 0x0; + + for (idx = 0; idx < RTW89_IQK_CHS_NR; idx++) { + iqk_info->iqk_channel[idx] = 0x0; + for (path = 0; path < RF_PATH_NUM_8851B; path++) { + iqk_info->lok_cor_fail[idx][path] = false; + iqk_info->lok_fin_fail[idx][path] = false; + iqk_info->iqk_tx_fail[idx][path] = false; + iqk_info->iqk_rx_fail[idx][path] = false; + iqk_info->iqk_table_idx[path] = 0x0; + } + } +} + +static void _doiqk(struct rtw89_dev *rtwdev, bool force, + enum rtw89_phy_idx phy_idx, u8 path) +{ + struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; + u8 phy_map = rtw89_btc_phymap(rtwdev, phy_idx, RF_AB); + u32 backup_rf_val[RTW8851B_IQK_SS][BACKUP_RF_REGS_NR]; + u32 backup_bb_val[BACKUP_BB_REGS_NR]; + + rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_IQK, + BTC_WRFK_ONESHOT_START); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[IQK]==========IQK strat!!!!!==========\n"); + iqk_info->iqk_times++; + iqk_info->kcount = 0; + iqk_info->version = RTW8851B_IQK_VER; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]Test Ver 0x%x\n", iqk_info->version); + _iqk_get_ch_info(rtwdev, phy_idx, path); + + _rfk_backup_bb_reg(rtwdev, &backup_bb_val[0]); + _rfk_backup_rf_reg(rtwdev, &backup_rf_val[path][0], path); + _iqk_macbb_setting(rtwdev, phy_idx, path); + _iqk_preset(rtwdev, path); + _iqk_start_iqk(rtwdev, phy_idx, path); + _iqk_restore(rtwdev, path); + _iqk_afebb_restore(rtwdev, phy_idx, path); + _rfk_restore_bb_reg(rtwdev, &backup_bb_val[0]); + _rfk_restore_rf_reg(rtwdev, &backup_rf_val[path][0], path); + + rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_IQK, + BTC_WRFK_ONESHOT_STOP); +} + +static void _iqk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, bool force) +{ + _doiqk(rtwdev, force, phy_idx, RF_PATH_A); +} + +static void _rck(struct rtw89_dev *rtwdev, enum rtw89_rf_path path) +{ + u32 rf_reg5; + u32 rck_val; + u32 val; + int ret; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[RCK] ====== S%d RCK ======\n", path); + + rf_reg5 = rtw89_read_rf(rtwdev, path, RR_RSV1, RFREG_MASK); + + rtw89_write_rf(rtwdev, path, RR_RSV1, RR_RSV1_RST, 0x0); + rtw89_write_rf(rtwdev, path, RR_MOD, RR_MOD_MASK, RR_MOD_V_RX); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[RCK] RF0x00 = 0x%05x\n", + rtw89_read_rf(rtwdev, path, RR_MOD, RFREG_MASK)); + + /* RCK trigger */ + rtw89_write_rf(rtwdev, path, RR_RCKC, RFREG_MASK, 0x00240); + + ret = read_poll_timeout_atomic(rtw89_read_rf, val, val, 2, 30, + false, rtwdev, path, RR_RCKS, BIT(3)); + + rck_val = rtw89_read_rf(rtwdev, path, RR_RCKC, RR_RCKC_CA); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[RCK] rck_val = 0x%x, ret = %d\n", + rck_val, ret); + + rtw89_write_rf(rtwdev, path, RR_RCKC, RFREG_MASK, rck_val); + rtw89_write_rf(rtwdev, path, RR_RSV1, RFREG_MASK, rf_reg5); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[RCK] RF 0x1b = 0x%x\n", + rtw89_read_rf(rtwdev, path, RR_RCKC, RFREG_MASK)); +} + +void rtw8851b_aack(struct rtw89_dev *rtwdev) +{ + u32 tmp05, ib[4]; + u32 tmp; + int ret; + int rek; + int i; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[LCK]DO AACK\n"); + + tmp05 = rtw89_read_rf(rtwdev, RF_PATH_A, RR_RSV1, RFREG_MASK); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MOD, RR_MOD_MASK, 0x3); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_RSV1, RFREG_MASK, 0x0); + + for (rek = 0; rek < 4; rek++) { + rtw89_write_rf(rtwdev, RF_PATH_A, RR_AACK, RFREG_MASK, 0x8201e); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_AACK, RFREG_MASK, 0x8201f); + fsleep(100); + + ret = read_poll_timeout_atomic(rtw89_read_rf, tmp, tmp, + 1, 1000, false, + rtwdev, RF_PATH_A, 0xd0, BIT(16)); + if (ret) + rtw89_warn(rtwdev, "[LCK]AACK timeout\n"); + + rtw89_write_rf(rtwdev, RF_PATH_A, RR_VCI, RR_VCI_ON, 0x1); + for (i = 0; i < 4; i++) { + rtw89_write_rf(rtwdev, RF_PATH_A, RR_VCO, RR_VCO_SEL, i); + ib[i] = rtw89_read_rf(rtwdev, RF_PATH_A, RR_IBD, RR_IBD_VAL); + } + rtw89_write_rf(rtwdev, RF_PATH_A, RR_VCI, RR_VCI_ON, 0x0); + + if (ib[0] != 0 && ib[1] != 0 && ib[2] != 0 && ib[3] != 0) + break; + } + + if (rek != 0) + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[LCK]AACK rek = %d\n", rek); + + rtw89_write_rf(rtwdev, RF_PATH_A, RR_RSV1, RFREG_MASK, tmp05); +} + +void rtw8851b_rck(struct rtw89_dev *rtwdev) +{ + _rck(rtwdev, RF_PATH_A); +} + +void rtw8851b_dack(struct rtw89_dev *rtwdev) +{ + _dac_cal(rtwdev, false); +} + +void rtw8851b_iqk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) +{ + u8 phy_map = rtw89_btc_phymap(rtwdev, phy_idx, 0); + u32 tx_en; + + rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_IQK, BTC_WRFK_START); + rtw89_chip_stop_sch_tx(rtwdev, phy_idx, &tx_en, RTW89_SCH_TX_SEL_ALL); + _wait_rx_mode(rtwdev, _kpath(rtwdev, phy_idx)); + + _iqk_init(rtwdev); + _iqk(rtwdev, phy_idx, false); + + rtw89_chip_resume_sch_tx(rtwdev, phy_idx, tx_en); + rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_IQK, BTC_WRFK_STOP); +} + +static void _bw_setting(struct rtw89_dev *rtwdev, enum rtw89_rf_path path, + enum rtw89_bandwidth bw, bool dav) +{ + u32 reg18_addr = dav ? RR_CFGCH : RR_CFGCH_V1; + u32 rf_reg18; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[RFK]===> %s\n", __func__); + + rf_reg18 = rtw89_read_rf(rtwdev, path, reg18_addr, RFREG_MASK); + if (rf_reg18 == INV_RF_DATA) { + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[RFK]Invalid RF_0x18 for Path-%d\n", path); + return; + } + rf_reg18 &= ~RR_CFGCH_BW; + + switch (bw) { + case RTW89_CHANNEL_WIDTH_5: + case RTW89_CHANNEL_WIDTH_10: + case RTW89_CHANNEL_WIDTH_20: + rf_reg18 |= FIELD_PREP(RR_CFGCH_BW, CFGCH_BW_20M); + break; + case RTW89_CHANNEL_WIDTH_40: + rf_reg18 |= FIELD_PREP(RR_CFGCH_BW, CFGCH_BW_40M); + break; + case RTW89_CHANNEL_WIDTH_80: + rf_reg18 |= FIELD_PREP(RR_CFGCH_BW, CFGCH_BW_80M); + break; + default: + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[RFK]Fail to set CH\n"); + } + + rf_reg18 &= ~(RR_CFGCH_POW_LCK | RR_CFGCH_TRX_AH | RR_CFGCH_BCN | + RR_CFGCH_BW2) & RFREG_MASK; + rf_reg18 |= RR_CFGCH_BW2; + rtw89_write_rf(rtwdev, path, reg18_addr, RFREG_MASK, rf_reg18); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[RFK] set %x at path%d, %x =0x%x\n", + bw, path, reg18_addr, + rtw89_read_rf(rtwdev, path, reg18_addr, RFREG_MASK)); +} + +static void _ctrl_bw(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + enum rtw89_bandwidth bw) +{ + _bw_setting(rtwdev, RF_PATH_A, bw, true); + _bw_setting(rtwdev, RF_PATH_A, bw, false); +} + +static bool _set_s0_arfc18(struct rtw89_dev *rtwdev, u32 val) +{ + u32 bak; + u32 tmp; + int ret; + + bak = rtw89_read_rf(rtwdev, RF_PATH_A, RR_LDO, RFREG_MASK); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_LDO, RR_LDO_SEL, 0x1); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_CFGCH, RFREG_MASK, val); + + ret = read_poll_timeout_atomic(rtw89_read_rf, tmp, tmp == 0, 1, 1000, + false, rtwdev, RF_PATH_A, RR_LPF, RR_LPF_BUSY); + if (ret) + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[LCK]LCK timeout\n"); + + rtw89_write_rf(rtwdev, RF_PATH_A, RR_LDO, RFREG_MASK, bak); + + return !!ret; +} + +static void _lck_check(struct rtw89_dev *rtwdev) +{ + u32 tmp; + + if (rtw89_read_rf(rtwdev, RF_PATH_A, RR_SYNFB, RR_SYNFB_LK) == 0) { + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[LCK]SYN MMD reset\n"); + + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MMD, RR_MMD_RST_EN, 0x1); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MMD, RR_MMD_RST_SYN, 0x0); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MMD, RR_MMD_RST_SYN, 0x1); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MMD, RR_MMD_RST_EN, 0x0); + } + + udelay(10); + + if (rtw89_read_rf(rtwdev, RF_PATH_A, RR_SYNFB, RR_SYNFB_LK) == 0) { + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[LCK]re-set RF 0x18\n"); + + rtw89_write_rf(rtwdev, RF_PATH_A, RR_LCK_TRG, RR_LCK_TRGSEL, 0x1); + tmp = rtw89_read_rf(rtwdev, RF_PATH_A, RR_CFGCH, RFREG_MASK); + _set_s0_arfc18(rtwdev, tmp); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_LCK_TRG, RR_LCK_TRGSEL, 0x0); + } + + if (rtw89_read_rf(rtwdev, RF_PATH_A, RR_SYNFB, RR_SYNFB_LK) == 0) { + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[LCK]SYN off/on\n"); + + tmp = rtw89_read_rf(rtwdev, RF_PATH_A, RR_POW, RFREG_MASK); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_POW, RFREG_MASK, tmp); + tmp = rtw89_read_rf(rtwdev, RF_PATH_A, RR_SX, RFREG_MASK); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_SX, RFREG_MASK, tmp); + + rtw89_write_rf(rtwdev, RF_PATH_A, RR_SYNLUT, RR_SYNLUT_MOD, 0x1); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_POW, RR_POW_SYN, 0x0); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_POW, RR_POW_SYN, 0x3); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_SYNLUT, RR_SYNLUT_MOD, 0x0); + + rtw89_write_rf(rtwdev, RF_PATH_A, RR_LCK_TRG, RR_LCK_TRGSEL, 0x1); + tmp = rtw89_read_rf(rtwdev, RF_PATH_A, RR_CFGCH, RFREG_MASK); + _set_s0_arfc18(rtwdev, tmp); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_LCK_TRG, RR_LCK_TRGSEL, 0x0); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[LCK]0xb2=%x, 0xc5=%x\n", + rtw89_read_rf(rtwdev, RF_PATH_A, RR_VCO, RFREG_MASK), + rtw89_read_rf(rtwdev, RF_PATH_A, RR_SYNFB, RFREG_MASK)); + } +} + +static void _set_ch(struct rtw89_dev *rtwdev, u32 val) +{ + bool timeout; + + timeout = _set_s0_arfc18(rtwdev, val); + if (!timeout) + _lck_check(rtwdev); +} + +static void _ch_setting(struct rtw89_dev *rtwdev, enum rtw89_rf_path path, + u8 central_ch, bool dav) +{ + u32 reg18_addr = dav ? RR_CFGCH : RR_CFGCH_V1; + bool is_2g_ch = central_ch <= 14; + u32 rf_reg18; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[RFK]===> %s\n", __func__); + + rf_reg18 = rtw89_read_rf(rtwdev, path, reg18_addr, RFREG_MASK); + rf_reg18 &= ~(RR_CFGCH_BAND1 | RR_CFGCH_POW_LCK | RR_CFGCH_TRX_AH | + RR_CFGCH_BCN | RR_CFGCH_BAND0 | RR_CFGCH_CH); + rf_reg18 |= FIELD_PREP(RR_CFGCH_CH, central_ch); + + if (!is_2g_ch) + rf_reg18 |= FIELD_PREP(RR_CFGCH_BAND1, CFGCH_BAND1_5G) | + FIELD_PREP(RR_CFGCH_BAND0, CFGCH_BAND0_5G); + + rf_reg18 &= ~(RR_CFGCH_POW_LCK | RR_CFGCH_TRX_AH | RR_CFGCH_BCN | + RR_CFGCH_BW2) & RFREG_MASK; + rf_reg18 |= RR_CFGCH_BW2; + + if (path == RF_PATH_A && dav) + _set_ch(rtwdev, rf_reg18); + else + rtw89_write_rf(rtwdev, path, reg18_addr, RFREG_MASK, rf_reg18); + + rtw89_write_rf(rtwdev, path, RR_LCKST, RR_LCKST_BIN, 0); + rtw89_write_rf(rtwdev, path, RR_LCKST, RR_LCKST_BIN, 1); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[RFK]CH: %d for Path-%d, reg0x%x = 0x%x\n", + central_ch, path, reg18_addr, + rtw89_read_rf(rtwdev, path, reg18_addr, RFREG_MASK)); +} + +static void _ctrl_ch(struct rtw89_dev *rtwdev, u8 central_ch) +{ + _ch_setting(rtwdev, RF_PATH_A, central_ch, true); + _ch_setting(rtwdev, RF_PATH_A, central_ch, false); +} + +static void _set_rxbb_bw(struct rtw89_dev *rtwdev, enum rtw89_bandwidth bw, + enum rtw89_rf_path path) +{ + rtw89_write_rf(rtwdev, path, RR_LUTWE2, RR_LUTWE2_RTXBW, 0x1); + rtw89_write_rf(rtwdev, path, RR_LUTWA, RR_LUTWA_M2, 0x12); + + if (bw == RTW89_CHANNEL_WIDTH_20) + rtw89_write_rf(rtwdev, path, RR_LUTWD0, RR_LUTWD0_LB, 0x1b); + else if (bw == RTW89_CHANNEL_WIDTH_40) + rtw89_write_rf(rtwdev, path, RR_LUTWD0, RR_LUTWD0_LB, 0x13); + else if (bw == RTW89_CHANNEL_WIDTH_80) + rtw89_write_rf(rtwdev, path, RR_LUTWD0, RR_LUTWD0_LB, 0xb); + else + rtw89_write_rf(rtwdev, path, RR_LUTWD0, RR_LUTWD0_LB, 0x3); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, "[RFK] set S%d RXBB BW 0x3F = 0x%x\n", path, + rtw89_read_rf(rtwdev, path, RR_LUTWD0, RR_LUTWD0_LB)); + + rtw89_write_rf(rtwdev, path, RR_LUTWE2, RR_LUTWE2_RTXBW, 0x0); +} + +static void _rxbb_bw(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + enum rtw89_bandwidth bw) +{ + u8 kpath, path; + + kpath = _kpath(rtwdev, phy); + + for (path = 0; path < RF_PATH_NUM_8851B; path++) { + if (!(kpath & BIT(path))) + continue; + + _set_rxbb_bw(rtwdev, bw, path); + } +} + +static void rtw8851b_ctrl_bw_ch(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy, u8 central_ch, + enum rtw89_band band, enum rtw89_bandwidth bw) +{ + _ctrl_ch(rtwdev, central_ch); + _ctrl_bw(rtwdev, phy, bw); + _rxbb_bw(rtwdev, phy, bw); +} + +void rtw8851b_set_channel_rf(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx) +{ + rtw8851b_ctrl_bw_ch(rtwdev, phy_idx, chan->channel, chan->band_type, + chan->band_width); +} diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b_rfk.h b/drivers/net/wireless/realtek/rtw89/rtw8851b_rfk.h new file mode 100644 index 000000000000..d86c630ff47e --- /dev/null +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b_rfk.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* Copyright(c) 2022-2023 Realtek Corporation + */ + +#ifndef __RTW89_8851B_RFK_H__ +#define __RTW89_8851B_RFK_H__ + +#include "core.h" + +void rtw8851b_aack(struct rtw89_dev *rtwdev); +void rtw8851b_rck(struct rtw89_dev *rtwdev); +void rtw8851b_dack(struct rtw89_dev *rtwdev); +void rtw8851b_iqk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); +void rtw8851b_set_channel_rf(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx); + +#endif diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851be.c b/drivers/net/wireless/realtek/rtw89/rtw8851be.c new file mode 100644 index 000000000000..0f7711c50bd1 --- /dev/null +++ b/drivers/net/wireless/realtek/rtw89/rtw8851be.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2022-2023 Realtek Corporation + */ + +#include <linux/module.h> +#include <linux/pci.h> + +#include "pci.h" +#include "reg.h" +#include "rtw8851b.h" + +static const struct rtw89_pci_info rtw8851b_pci_info = { + .txbd_trunc_mode = MAC_AX_BD_TRUNC, + .rxbd_trunc_mode = MAC_AX_BD_TRUNC, + .rxbd_mode = MAC_AX_RXBD_PKT, + .tag_mode = MAC_AX_TAG_MULTI, + .tx_burst = MAC_AX_TX_BURST_2048B, + .rx_burst = MAC_AX_RX_BURST_128B, + .wd_dma_idle_intvl = MAC_AX_WD_DMA_INTVL_256NS, + .wd_dma_act_intvl = MAC_AX_WD_DMA_INTVL_256NS, + .multi_tag_num = MAC_AX_TAG_NUM_8, + .lbc_en = MAC_AX_PCIE_ENABLE, + .lbc_tmr = MAC_AX_LBC_TMR_2MS, + .autok_en = MAC_AX_PCIE_DISABLE, + .io_rcy_en = MAC_AX_PCIE_DISABLE, + .io_rcy_tmr = MAC_AX_IO_RCY_ANA_TMR_6MS, + + .init_cfg_reg = R_AX_PCIE_INIT_CFG1, + .txhci_en_bit = B_AX_TXHCI_EN, + .rxhci_en_bit = B_AX_RXHCI_EN, + .rxbd_mode_bit = B_AX_RXBD_MODE, + .exp_ctrl_reg = R_AX_PCIE_EXP_CTRL, + .max_tag_num_mask = B_AX_MAX_TAG_NUM, + .rxbd_rwptr_clr_reg = R_AX_RXBD_RWPTR_CLR, + .txbd_rwptr_clr2_reg = 0, + .dma_stop1 = {R_AX_PCIE_DMA_STOP1, B_AX_TX_STOP1_MASK_V1}, + .dma_stop2 = {0}, + .dma_busy1 = {R_AX_PCIE_DMA_BUSY1, DMA_BUSY1_CHECK_V1}, + .dma_busy2_reg = 0, + .dma_busy3_reg = R_AX_PCIE_DMA_BUSY1, + + .rpwm_addr = R_AX_PCIE_HRPWM, + .cpwm_addr = R_AX_CPWM, + .tx_dma_ch_mask = BIT(RTW89_TXCH_ACH4) | BIT(RTW89_TXCH_ACH5) | + BIT(RTW89_TXCH_ACH6) | BIT(RTW89_TXCH_ACH7) | + BIT(RTW89_TXCH_CH10) | BIT(RTW89_TXCH_CH11), + .bd_idx_addr_low_power = NULL, + .dma_addr_set = &rtw89_pci_ch_dma_addr_set, + .bd_ram_table = &rtw89_bd_ram_table_single, + + .ltr_set = rtw89_pci_ltr_set, + .fill_txaddr_info = rtw89_pci_fill_txaddr_info, + .config_intr_mask = rtw89_pci_config_intr_mask, + .enable_intr = rtw89_pci_enable_intr, + .disable_intr = rtw89_pci_disable_intr, + .recognize_intrs = rtw89_pci_recognize_intrs, +}; + +static const struct rtw89_driver_info rtw89_8851be_info = { + .chip = &rtw8851b_chip_info, + .bus = { + .pci = &rtw8851b_pci_info, + }, +}; + +static const struct pci_device_id rtw89_8851be_id_table[] = { + { + PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0xb851), + .driver_data = (kernel_ulong_t)&rtw89_8851be_info, + }, + {}, +}; +MODULE_DEVICE_TABLE(pci, rtw89_8851be_id_table); + +static struct pci_driver rtw89_8851be_driver = { + .name = "rtw89_8851be", + .id_table = rtw89_8851be_id_table, + .probe = rtw89_pci_probe, + .remove = rtw89_pci_remove, + .driver.pm = &rtw89_pm_ops, +}; +module_pci_driver(rtw89_8851be_driver); + +MODULE_AUTHOR("Realtek Corporation"); +MODULE_DESCRIPTION("Realtek 802.11ax wireless 8851BE driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index d7930efd89b7..4e6f3bbdc2d8 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -463,6 +463,12 @@ static const struct rtw89_imr_info rtw8852a_imr_info = { .tmac_imr_set = B_AX_TMAC_IMR_SET, }; +static const struct rtw89_xtal_info rtw8852a_xtal_info = { + .xcap_reg = R_AX_XTAL_ON_CTRL0, + .sc_xo_mask = B_AX_XTAL_SC_XO_MASK, + .sc_xi_mask = B_AX_XTAL_SC_XI_MASK, +}; + static const struct rtw89_rrsr_cfgs rtw8852a_rrsr_cfgs = { .ref_rate = {R_AX_TRXPTCL_RRSR_CTL_0, B_AX_WMAC_RESP_REF_RATE_SEL, 0}, .rsc = {R_AX_TRXPTCL_RRSR_CTL_0, B_AX_WMAC_RESP_RSC_MASK, 2}, @@ -2069,6 +2075,7 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .fw_format_max = RTW8852A_FW_FORMAT_MAX, .try_ce_fw = false, .fifo_size = 458752, + .small_fifo_size = false, .dle_scc_rsvd_size = 0, .max_amsdu_limit = 3500, .dis_2g_40m_ul_ofdma = true, @@ -2085,6 +2092,7 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .rf_table = {&rtw89_8852a_phy_radioa_table, &rtw89_8852a_phy_radiob_table,}, .nctl_table = &rtw89_8852a_phy_nctl_table, + .nctl_post_table = NULL, .byr_table = &rtw89_8852a_byr_table, .dflt_parms = &rtw89_8852a_dflt_parms, .rfe_parms_conf = NULL, @@ -2097,6 +2105,7 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .support_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ), .support_bw160 = false, + .support_unii4 = false, .support_ul_tb_ctrl = false, .hw_sec_hdr = false, .rf_path_num = 2, @@ -2107,7 +2116,7 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .scam_num = 128, .bacam_num = 2, .bacam_dynamic_num = 4, - .bacam_v1 = false, + .bacam_ver = RTW89_BACAM_V0, .sec_ctrl_efuse_size = 4, .physical_efuse_size = 1216, .logical_efuse_size = 1536, @@ -2159,6 +2168,7 @@ const struct rtw89_chip_info rtw8852a_chip_info = { #ifdef CONFIG_PM .wowlan_stub = &rtw_wowlan_stub_8852a, #endif + .xtal_info = &rtw8852a_xtal_info, }; EXPORT_SYMBOL(rtw8852a_chip_info); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 6da1b603a9a9..9ed4ade08813 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -2506,6 +2506,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = { .fw_format_max = RTW8852B_FW_FORMAT_MAX, .try_ce_fw = true, .fifo_size = 196608, + .small_fifo_size = true, .dle_scc_rsvd_size = 98304, .max_amsdu_limit = 3500, .dis_2g_40m_ul_ofdma = true, @@ -2522,6 +2523,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = { .rf_table = {&rtw89_8852b_phy_radioa_table, &rtw89_8852b_phy_radiob_table,}, .nctl_table = &rtw89_8852b_phy_nctl_table, + .nctl_post_table = NULL, .byr_table = &rtw89_8852b_byr_table, .dflt_parms = &rtw89_8852b_dflt_parms, .rfe_parms_conf = NULL, @@ -2534,6 +2536,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = { .support_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ), .support_bw160 = false, + .support_unii4 = true, .support_ul_tb_ctrl = true, .hw_sec_hdr = false, .rf_path_num = 2, @@ -2544,7 +2547,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = { .scam_num = 128, .bacam_num = 2, .bacam_dynamic_num = 4, - .bacam_v1 = false, + .bacam_ver = RTW89_BACAM_V0, .sec_ctrl_efuse_size = 4, .physical_efuse_size = 1216, .logical_efuse_size = 2048, @@ -2598,6 +2601,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = { #ifdef CONFIG_PM .wowlan_stub = &rtw_wowlan_stub_8852b, #endif + .xtal_info = NULL, }; EXPORT_SYMBOL(rtw8852b_chip_info); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index ceb819a62efc..f2e70bda8e48 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -2805,6 +2805,7 @@ const struct rtw89_chip_info rtw8852c_chip_info = { .fw_format_max = RTW8852C_FW_FORMAT_MAX, .try_ce_fw = false, .fifo_size = 458752, + .small_fifo_size = false, .dle_scc_rsvd_size = 0, .max_amsdu_limit = 8000, .dis_2g_40m_ul_ofdma = false, @@ -2821,6 +2822,7 @@ const struct rtw89_chip_info rtw8852c_chip_info = { .rf_table = {&rtw89_8852c_phy_radiob_table, &rtw89_8852c_phy_radioa_table,}, .nctl_table = &rtw89_8852c_phy_nctl_table, + .nctl_post_table = NULL, .byr_table = &rtw89_8852c_byr_table, .dflt_parms = &rtw89_8852c_dflt_parms, .rfe_parms_conf = NULL, @@ -2834,6 +2836,7 @@ const struct rtw89_chip_info rtw8852c_chip_info = { BIT(NL80211_BAND_5GHZ) | BIT(NL80211_BAND_6GHZ), .support_bw160 = true, + .support_unii4 = true, .support_ul_tb_ctrl = false, .hw_sec_hdr = true, .rf_path_num = 2, @@ -2844,7 +2847,7 @@ const struct rtw89_chip_info rtw8852c_chip_info = { .scam_num = 128, .bacam_num = 8, .bacam_dynamic_num = 8, - .bacam_v1 = true, + .bacam_ver = RTW89_BACAM_V0_EXT, .sec_ctrl_efuse_size = 4, .physical_efuse_size = 1216, .logical_efuse_size = 2048, @@ -2897,6 +2900,7 @@ const struct rtw89_chip_info rtw8852c_chip_info = { #ifdef CONFIG_PM .wowlan_stub = &rtw_wowlan_stub_8852c, #endif + .xtal_info = NULL, }; EXPORT_SYMBOL(rtw8852c_chip_info); diff --git a/drivers/net/wireless/realtek/rtw89/ser.c b/drivers/net/wireless/realtek/rtw89/ser.c index 9e9f6947e7f1..9ba99f3764e7 100644 --- a/drivers/net/wireless/realtek/rtw89/ser.c +++ b/drivers/net/wireless/realtek/rtw89/ser.c @@ -20,12 +20,14 @@ enum ser_evt { SER_EV_NONE, SER_EV_STATE_IN, SER_EV_STATE_OUT, + SER_EV_L1_RESET_PREPARE, /* pre-M0 */ SER_EV_L1_RESET, /* M1 */ SER_EV_DO_RECOVERY, /* M3 */ SER_EV_MAC_RESET_DONE, /* M5 */ SER_EV_L2_RESET, SER_EV_L2_RECFG_DONE, SER_EV_L2_RECFG_TIMEOUT, + SER_EV_M1_TIMEOUT, SER_EV_M3_TIMEOUT, SER_EV_FW_M5_TIMEOUT, SER_EV_L0_RESET, @@ -34,6 +36,7 @@ enum ser_evt { enum ser_state { SER_IDLE_ST, + SER_L1_RESET_PRE_ST, SER_RESET_TRX_ST, SER_DO_HCI_ST, SER_L2_RESET_ST, @@ -374,6 +377,13 @@ static int hal_stop_dma(struct rtw89_ser *ser) return ret; } +static void hal_send_post_m0_event(struct rtw89_ser *ser) +{ + struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser); + + rtw89_mac_set_err_status(rtwdev, MAC_AX_ERR_L1_RESET_START_DMAC); +} + static void hal_send_m2_event(struct rtw89_ser *ser) { struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser); @@ -398,6 +408,9 @@ static void ser_idle_st_hdl(struct rtw89_ser *ser, u8 evt) rtw89_hci_recovery_complete(rtwdev); clear_bit(RTW89_FLAG_CRASH_SIMULATING, rtwdev->flags); break; + case SER_EV_L1_RESET_PREPARE: + ser_state_goto(ser, SER_L1_RESET_PRE_ST); + break; case SER_EV_L1_RESET: ser_state_goto(ser, SER_RESET_TRX_ST); break; @@ -412,6 +425,28 @@ static void ser_idle_st_hdl(struct rtw89_ser *ser, u8 evt) } } +static void ser_l1_reset_pre_st_hdl(struct rtw89_ser *ser, u8 evt) +{ + switch (evt) { + case SER_EV_STATE_IN: + ser->prehandle_l1 = true; + hal_send_post_m0_event(ser); + ser_set_alarm(ser, 1000, SER_EV_M1_TIMEOUT); + break; + case SER_EV_L1_RESET: + ser_state_goto(ser, SER_RESET_TRX_ST); + break; + case SER_EV_M1_TIMEOUT: + ser_state_goto(ser, SER_L2_RESET_ST); + break; + case SER_EV_STATE_OUT: + ser_del_alarm(ser); + break; + default: + break; + } +} + static void ser_reset_trx_st_hdl(struct rtw89_ser *ser, u8 evt) { struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser); @@ -654,12 +689,14 @@ static const struct event_ent ser_ev_tbl[] = { {SER_EV_NONE, "SER_EV_NONE"}, {SER_EV_STATE_IN, "SER_EV_STATE_IN"}, {SER_EV_STATE_OUT, "SER_EV_STATE_OUT"}, - {SER_EV_L1_RESET, "SER_EV_L1_RESET"}, + {SER_EV_L1_RESET_PREPARE, "SER_EV_L1_RESET_PREPARE pre-m0"}, + {SER_EV_L1_RESET, "SER_EV_L1_RESET m1"}, {SER_EV_DO_RECOVERY, "SER_EV_DO_RECOVERY m3"}, {SER_EV_MAC_RESET_DONE, "SER_EV_MAC_RESET_DONE m5"}, {SER_EV_L2_RESET, "SER_EV_L2_RESET"}, {SER_EV_L2_RECFG_DONE, "SER_EV_L2_RECFG_DONE"}, {SER_EV_L2_RECFG_TIMEOUT, "SER_EV_L2_RECFG_TIMEOUT"}, + {SER_EV_M1_TIMEOUT, "SER_EV_M1_TIMEOUT"}, {SER_EV_M3_TIMEOUT, "SER_EV_M3_TIMEOUT"}, {SER_EV_FW_M5_TIMEOUT, "SER_EV_FW_M5_TIMEOUT"}, {SER_EV_L0_RESET, "SER_EV_L0_RESET"}, @@ -668,6 +705,7 @@ static const struct event_ent ser_ev_tbl[] = { static const struct state_ent ser_st_tbl[] = { {SER_IDLE_ST, "SER_IDLE_ST", ser_idle_st_hdl}, + {SER_L1_RESET_PRE_ST, "SER_L1_RESET_PRE_ST", ser_l1_reset_pre_st_hdl}, {SER_RESET_TRX_ST, "SER_RESET_TRX_ST", ser_reset_trx_st_hdl}, {SER_DO_HCI_ST, "SER_DO_HCI_ST", ser_do_hci_st_hdl}, {SER_L2_RESET_ST, "SER_L2_RESET_ST", ser_l2_reset_st_hdl} @@ -713,6 +751,9 @@ int rtw89_ser_notify(struct rtw89_dev *rtwdev, u32 err) rtw89_info(rtwdev, "SER catches error: 0x%x\n", err); switch (err) { + case MAC_AX_ERR_L1_PREERR_DMAC: /* pre-M0 */ + event = SER_EV_L1_RESET_PREPARE; + break; case MAC_AX_ERR_L1_ERR_DMAC: case MAC_AX_ERR_L0_PROMOTE_TO_L1: event = SER_EV_L1_RESET; /* M1 */ diff --git a/drivers/net/wireless/realtek/rtw89/txrx.h b/drivers/net/wireless/realtek/rtw89/txrx.h index 98eb9607cd21..d880ecb879ca 100644 --- a/drivers/net/wireless/realtek/rtw89/txrx.h +++ b/drivers/net/wireless/realtek/rtw89/txrx.h @@ -298,12 +298,19 @@ le32_get_bits(*((const __le32 *)ie), GENMASK(4, 0)) #define RTW89_GET_PHY_STS_IE_LEN(ie) \ le32_get_bits(*((const __le32 *)ie), GENMASK(11, 5)) -#define RTW89_GET_PHY_STS_IE01_CH_IDX(ie) \ - le32_get_bits(*((const __le32 *)ie), GENMASK(23, 16)) -#define RTW89_GET_PHY_STS_IE01_FD_CFO(ie) \ - le32_get_bits(*((const __le32 *)(ie) + 1), GENMASK(19, 8)) -#define RTW89_GET_PHY_STS_IE01_PREMB_CFO(ie) \ - le32_get_bits(*((const __le32 *)(ie) + 1), GENMASK(31, 20)) + +struct rtw89_phy_sts_ie0 { + __le32 w0; + __le32 w1; + __le32 w2; +} __packed; + +#define RTW89_PHY_STS_IE01_W0_CH_IDX GENMASK(23, 16) +#define RTW89_PHY_STS_IE01_W1_FD_CFO GENMASK(19, 8) +#define RTW89_PHY_STS_IE01_W1_PREMB_CFO GENMASK(31, 20) +#define RTW89_PHY_STS_IE01_W2_AVG_SNR GENMASK(5, 0) +#define RTW89_PHY_STS_IE01_W2_EVM_MAX GENMASK(15, 8) +#define RTW89_PHY_STS_IE01_W2_EVM_MIN GENMASK(23, 16) enum rtw89_tx_channel { RTW89_TXCH_ACH0 = 0, diff --git a/drivers/net/wireless/realtek/rtw89/wow.c b/drivers/net/wireless/realtek/rtw89/wow.c index 2ca8abb70f11..364e54622150 100644 --- a/drivers/net/wireless/realtek/rtw89/wow.c +++ b/drivers/net/wireless/realtek/rtw89/wow.c @@ -91,7 +91,7 @@ static void rtw89_wow_show_wakeup_reason(struct rtw89_dev *rtwdev) u32 wow_reason_reg; u8 reason; - if (chip_id == RTL8852A || chip_id == RTL8852B) + if (chip_id == RTL8852A || chip_id == RTL8852B || chip_id == RTL8851B) wow_reason_reg = R_AX_C2HREG_DATA3 + 3; else wow_reason_reg = R_AX_C2HREG_DATA3_V1 + 3; diff --git a/drivers/net/wwan/iosm/iosm_ipc_imem.h b/drivers/net/wwan/iosm/iosm_ipc_imem.h index e700dc8bfe0a..5664ac507c90 100644 --- a/drivers/net/wwan/iosm/iosm_ipc_imem.h +++ b/drivers/net/wwan/iosm/iosm_ipc_imem.h @@ -140,17 +140,6 @@ enum ipc_channel_state { IMEM_CHANNEL_CLOSING, }; -/* Time Unit */ -enum ipc_time_unit { - IPC_SEC = 0, - IPC_MILLI_SEC = 1, - IPC_MICRO_SEC = 2, - IPC_NANO_SEC = 3, - IPC_PICO_SEC = 4, - IPC_FEMTO_SEC = 5, - IPC_ATTO_SEC = 6, -}; - /** * enum ipc_ctype - Enum defining supported channel type needed for control * /IP traffic. @@ -204,7 +193,6 @@ enum ipc_hp_identifier { * @pipe_nr: Pipe identification number * @irq: Interrupt vector * @dir: Direction of data stream in pipe - * @td_tag: Unique tag of the buffer queued * @buf_size: Buffer size (in bytes) for preallocated * buffers (for DL pipes) * @nr_of_queued_entries: Aueued number of entries @@ -224,7 +212,6 @@ struct ipc_pipe { u32 pipe_nr; u32 irq; enum ipc_mem_pipe_dir dir; - u32 td_tag; u32 buf_size; u16 nr_of_queued_entries; u8 is_open:1; diff --git a/drivers/net/wwan/iosm/iosm_ipc_mux.h b/drivers/net/wwan/iosm/iosm_ipc_mux.h index 9968bb885c1f..17ca8d1f9397 100644 --- a/drivers/net/wwan/iosm/iosm_ipc_mux.h +++ b/drivers/net/wwan/iosm/iosm_ipc_mux.h @@ -333,9 +333,7 @@ struct mux_acb { * @wwan_q_offset: This will hold the offset of the given instance * Useful while passing or receiving packets from * wwan/imem layer. - * @adb_finish_timer: Timer for forcefully finishing the ADB * @acb_tx_sequence_nr: Sequence number for the ACB header. - * @params: user configurable parameters * @adb_tx_sequence_nr: Sequence number for ADB header * @acc_adb_size: Statistic data for logging * @acc_payload_size: Statistic data for logging @@ -367,9 +365,7 @@ struct iosm_mux { long long ul_data_pend_bytes; struct mux_acb acb; int wwan_q_offset; - struct hrtimer adb_finish_timer; u16 acb_tx_sequence_nr; - struct ipc_params *params; u16 adb_tx_sequence_nr; unsigned long long acc_adb_size; unsigned long long acc_payload_size; diff --git a/drivers/net/wwan/iosm/iosm_ipc_wwan.c b/drivers/net/wwan/iosm/iosm_ipc_wwan.c index 4c9022a93e01..ff747fc79aaf 100644 --- a/drivers/net/wwan/iosm/iosm_ipc_wwan.c +++ b/drivers/net/wwan/iosm/iosm_ipc_wwan.c @@ -18,8 +18,6 @@ #define IOSM_IP_TYPE_IPV4 0x40 #define IOSM_IP_TYPE_IPV6 0x60 -#define IOSM_IF_ID_PAYLOAD 2 - /** * struct iosm_netdev_priv - netdev WWAN driver specific private data * @ipc_wwan: Pointer to iosm_wwan struct diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c index c1501f41e2d8..3d79b35eb577 100644 --- a/drivers/net/xen-netback/netback.c +++ b/drivers/net/xen-netback/netback.c @@ -1128,9 +1128,7 @@ static int xenvif_handle_frag_list(struct xenvif_queue *queue, struct sk_buff *s BUG(); offset += len; - __skb_frag_set_page(&frags[i], page); - skb_frag_off_set(&frags[i], 0); - skb_frag_size_set(&frags[i], len); + skb_frag_fill_page_desc(&frags[i], page, 0, len); } /* Release all the original (foreign) frags. */ |