Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'net-sparx5-clean-up-probe-remove-init-and-deinit-paths'

Daniel Machon says:

====================
net: sparx5: clean up probe/remove init and deinit paths

This series refactors the sparx5 init and deinit code out of
sparx5_start() and into probe(), adding proper per-subsystem cleanup
labels and deinit functions.

Currently, the sparx5 driver initializes most subsystems inside
sparx5_start(), which is called from probe(). This includes registering
netdevs, starting worker threads for stats and MAC table polling,
requesting PTP IRQs, and initializing VCAP. The function has grown to
handle many unrelated subsystems, and has no granular error handling —
it either succeeds entirely or returns an error, leaving cleanup to a
single catch-all label in probe().

The remove() path has a similar problem: teardown is not structured as
the reverse of initialization, and several subsystems lack proper deinit
functions. For example, the stats workqueue has no corresponding
cleanup, and the mact workqueue is destroyed without first cancelling
its delayed work.

Refactor this by moving each init function out of sparx5_start() and
into probe(), with a corresponding goto-based cleanup label. Add deinit
functions for subsystems that allocate resources, to properly cancel
work and destroy workqueues. Ensure that cleanup order in both error
paths and remove() follows the reverse of initialization order.
sparx5_start() is eliminated entirely — its hardware register setup
is renamed to sparx5_forwarding_init() and its FDMA/XTR setup is
extracted to sparx5_frame_io_init().

Before this series, most init functions live inside sparx5_start() with
no individual cleanup:

probe():
sparx5_start(): <- no granular error handling
sparx5_mact_init()
sparx_stats_init() <- starts worker, no cleanup
mact_queue setup <- no cancel on teardown
sparx5_register_netdevs()
sparx5_register_notifier_blocks()
sparx5_vcap_init()
sparx5_ptp_init()

probe() error path:
cleanup_ports:
sparx5_cleanup_ports()
destroy_workqueue(mact_queue)

After this series, probe() initializes subsystems in order with
matching cleanup labels, and remove() tears down in reverse:

probe():
sparx5_pgid_init()
sparx5_vlan_init()
sparx5_board_init()
sparx5_forwarding_init()
sparx5_calendar_init() -> cleanup_ports
sparx5_qos_init() -> cleanup_ports
sparx5_vcap_init() -> cleanup_ports
sparx5_mact_init() -> cleanup_vcap
sparx5_stats_init() -> cleanup_mact
sparx5_frame_io_init() -> cleanup_stats
sparx5_ptp_init() -> cleanup_frame_io
sparx5_register_netdevs() -> cleanup_ptp
sparx5_register_notifier_blocks() -> cleanup_netdevs

remove():
sparx5_unregister_notifier_blocks()
sparx5_unregister_netdevs()
sparx5_ptp_deinit()
sparx5_frame_io_deinit()
sparx5_stats_deinit()
sparx5_mact_deinit()
sparx5_vcap_deinit()
sparx5_destroy_netdevs()
====================

Link: https://patch.msgid.link/20260227-sparx5-init-deinit-v2-0-10ba54ccf005@microchip.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+196 -147
+13 -2
drivers/net/ethernet/microchip/sparx5/sparx5_calendar.c
··· 151 151 } 152 152 153 153 /* Auto configure the QSYS calendar based on port configuration */ 154 - int sparx5_config_auto_calendar(struct sparx5 *sparx5) 154 + static int sparx5_config_auto_calendar(struct sparx5 *sparx5) 155 155 { 156 156 const struct sparx5_consts *consts = sparx5->data->consts; 157 157 u32 cal[7], value, idx, portno; ··· 578 578 } 579 579 580 580 /* Configure the DSM calendar based on port configuration */ 581 - int sparx5_config_dsm_calendar(struct sparx5 *sparx5) 581 + static int sparx5_config_dsm_calendar(struct sparx5 *sparx5) 582 582 { 583 583 const struct sparx5_ops *ops = sparx5->data->ops; 584 584 int taxi; ··· 609 609 cal_out: 610 610 kfree(data); 611 611 return err; 612 + } 613 + 614 + int sparx5_calendar_init(struct sparx5 *sparx5) 615 + { 616 + int err; 617 + 618 + err = sparx5_config_auto_calendar(sparx5); 619 + if (err) 620 + return err; 621 + 622 + return sparx5_config_dsm_calendar(sparx5); 612 623 }
+8 -1
drivers/net/ethernet/microchip/sparx5/sparx5_ethtool.c
··· 1244 1244 .set_pauseparam = sparx5_set_pauseparam, 1245 1245 }; 1246 1246 1247 - int sparx_stats_init(struct sparx5 *sparx5) 1247 + int sparx5_stats_init(struct sparx5 *sparx5) 1248 1248 { 1249 1249 const struct sparx5_consts *consts = sparx5->data->consts; 1250 1250 char queue_name[32]; ··· 1277 1277 SPX5_STATS_CHECK_DELAY); 1278 1278 1279 1279 return 0; 1280 + } 1281 + 1282 + void sparx5_stats_deinit(struct sparx5 *sparx5) 1283 + { 1284 + cancel_delayed_work_sync(&sparx5->stats_work); 1285 + destroy_workqueue(sparx5->stats_queue); 1286 + mutex_destroy(&sparx5->queue_stats_lock); 1280 1287 }
+32 -2
drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c
··· 419 419 true); 420 420 } 421 421 422 - void sparx5_mact_pull_work(struct work_struct *work) 422 + static void sparx5_mact_pull_work(struct work_struct *work) 423 423 { 424 424 struct delayed_work *del_work = to_delayed_work(work); 425 425 struct sparx5 *sparx5 = container_of(del_work, struct sparx5, ··· 489 489 LRN_AUTOAGE_CFG(0)); 490 490 } 491 491 492 - void sparx5_mact_init(struct sparx5 *sparx5) 492 + int sparx5_mact_init(struct sparx5 *sparx5) 493 493 { 494 + char queue_name[32]; 495 + 494 496 mutex_init(&sparx5->lock); 495 497 496 498 /* Flush MAC table */ ··· 504 502 dev_warn(sparx5->dev, "MAC flush error\n"); 505 503 506 504 sparx5_set_ageing(sparx5, BR_DEFAULT_AGEING_TIME / HZ * 1000); 505 + 506 + /* Add host mode BC address (points only to CPU) */ 507 + sparx5_mact_learn(sparx5, sparx5_get_pgid(sparx5, PGID_CPU), 508 + (unsigned char[]){0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 509 + NULL_VID); 510 + 511 + mutex_init(&sparx5->mdb_lock); 512 + INIT_LIST_HEAD(&sparx5->mdb_entries); 513 + mutex_init(&sparx5->mact_lock); 514 + INIT_LIST_HEAD(&sparx5->mact_entries); 515 + snprintf(queue_name, sizeof(queue_name), "%s-mact", 516 + dev_name(sparx5->dev)); 517 + sparx5->mact_queue = create_singlethread_workqueue(queue_name); 518 + if (!sparx5->mact_queue) 519 + return -ENOMEM; 520 + 521 + INIT_DELAYED_WORK(&sparx5->mact_work, sparx5_mact_pull_work); 522 + queue_delayed_work(sparx5->mact_queue, &sparx5->mact_work, 523 + SPX5_MACT_PULL_DELAY); 524 + 525 + return 0; 526 + } 527 + 528 + void sparx5_mact_deinit(struct sparx5 *sparx5) 529 + { 530 + cancel_delayed_work_sync(&sparx5->mact_work); 531 + destroy_workqueue(sparx5->mact_queue); 532 + mutex_destroy(&sparx5->mact_lock); 507 533 }
+118 -135
drivers/net/ethernet/microchip/sparx5/sparx5_main.c
··· 658 658 return 0; 659 659 } 660 660 661 + static int sparx5_frame_io_init(struct sparx5 *sparx5) 662 + { 663 + const struct sparx5_ops *ops = sparx5->data->ops; 664 + int err = -ENXIO; 665 + 666 + /* Start Frame DMA with fallback to register based INJ/XTR */ 667 + if (sparx5->fdma_irq >= 0) { 668 + if (GCB_CHIP_ID_REV_ID_GET(sparx5->chip_id) > 0 || 669 + !is_sparx5(sparx5)) 670 + err = devm_request_irq(sparx5->dev, 671 + sparx5->fdma_irq, 672 + sparx5_fdma_handler, 673 + 0, 674 + "sparx5-fdma", sparx5); 675 + if (!err) { 676 + err = ops->fdma_init(sparx5); 677 + if (!err) 678 + sparx5_fdma_start(sparx5); 679 + } 680 + if (err) 681 + sparx5->fdma_irq = -ENXIO; 682 + } else { 683 + sparx5->fdma_irq = -ENXIO; 684 + } 685 + if (err && sparx5->xtr_irq >= 0) { 686 + err = devm_request_irq(sparx5->dev, sparx5->xtr_irq, 687 + sparx5_xtr_handler, IRQF_SHARED, 688 + "sparx5-xtr", sparx5); 689 + if (!err) 690 + err = sparx5_manual_injection_mode(sparx5); 691 + if (err) 692 + sparx5->xtr_irq = -ENXIO; 693 + } else { 694 + sparx5->xtr_irq = -ENXIO; 695 + } 696 + 697 + return err; 698 + } 699 + 700 + static void sparx5_frame_io_deinit(struct sparx5 *sparx5) 701 + { 702 + if (sparx5->xtr_irq >= 0) { 703 + disable_irq(sparx5->xtr_irq); 704 + sparx5->xtr_irq = -ENXIO; 705 + } 706 + if (sparx5->fdma_irq >= 0) { 707 + disable_irq(sparx5->fdma_irq); 708 + sparx5->data->ops->fdma_deinit(sparx5); 709 + sparx5->fdma_irq = -ENXIO; 710 + } 711 + } 712 + 661 713 /* Some boards needs to map the SGPIO for signal detect explicitly to the 662 714 * port module 663 715 */ ··· 735 683 GCB_HW_SGPIO_TO_SD_MAP_CFG(idx)); 736 684 } 737 685 738 - static int sparx5_start(struct sparx5 *sparx5) 686 + static void sparx5_forwarding_init(struct sparx5 *sparx5) 739 687 { 740 - u8 broadcast[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; 741 688 const struct sparx5_consts *consts = sparx5->data->consts; 742 - const struct sparx5_ops *ops = sparx5->data->ops; 743 - char queue_name[32]; 744 689 u32 idx; 745 - int err; 746 690 747 691 /* Setup own UPSIDs */ 748 692 for (idx = 0; idx < consts->n_own_upsids; idx++) { ··· 776 728 ANA_CL_FILTER_CTRL_FORCE_FCS_UPDATE_ENA, 777 729 sparx5, ANA_CL_FILTER_CTRL(idx)); 778 730 779 - /* Init MAC table, ageing */ 780 - sparx5_mact_init(sparx5); 781 - 782 - /* Init PGID table arbitrator */ 783 - sparx5_pgid_init(sparx5); 784 - 785 - /* Setup VLANs */ 786 - sparx5_vlan_init(sparx5); 787 - 788 - /* Add host mode BC address (points only to CPU) */ 789 - sparx5_mact_learn(sparx5, sparx5_get_pgid(sparx5, PGID_CPU), broadcast, 790 - NULL_VID); 791 - 792 731 /* Enable queue limitation watermarks */ 793 732 sparx5_qlim_set(sparx5); 794 733 795 - err = sparx5_config_auto_calendar(sparx5); 796 - if (err) 797 - return err; 798 - 799 - err = sparx5_config_dsm_calendar(sparx5); 800 - if (err) 801 - return err; 802 - 803 - /* Init stats */ 804 - err = sparx_stats_init(sparx5); 805 - if (err) 806 - return err; 807 - 808 - /* Init mact_sw struct */ 809 - mutex_init(&sparx5->mact_lock); 810 - INIT_LIST_HEAD(&sparx5->mact_entries); 811 - snprintf(queue_name, sizeof(queue_name), "%s-mact", 812 - dev_name(sparx5->dev)); 813 - sparx5->mact_queue = create_singlethread_workqueue(queue_name); 814 - if (!sparx5->mact_queue) 815 - return -ENOMEM; 816 - 817 - INIT_DELAYED_WORK(&sparx5->mact_work, sparx5_mact_pull_work); 818 - queue_delayed_work(sparx5->mact_queue, &sparx5->mact_work, 819 - SPX5_MACT_PULL_DELAY); 820 - 821 - mutex_init(&sparx5->mdb_lock); 822 - INIT_LIST_HEAD(&sparx5->mdb_entries); 823 - 824 - err = sparx5_register_netdevs(sparx5); 825 - if (err) 826 - return err; 827 - 828 - sparx5_board_init(sparx5); 829 - err = sparx5_register_notifier_blocks(sparx5); 830 - if (err) 831 - return err; 832 - 833 - err = sparx5_vcap_init(sparx5); 834 - if (err) { 835 - sparx5_unregister_notifier_blocks(sparx5); 836 - return err; 837 - } 838 - 839 - /* Start Frame DMA with fallback to register based INJ/XTR */ 840 - err = -ENXIO; 841 - if (sparx5->fdma_irq >= 0) { 842 - if (GCB_CHIP_ID_REV_ID_GET(sparx5->chip_id) > 0 || 843 - !is_sparx5(sparx5)) 844 - err = devm_request_irq(sparx5->dev, 845 - sparx5->fdma_irq, 846 - sparx5_fdma_handler, 847 - 0, 848 - "sparx5-fdma", sparx5); 849 - if (!err) { 850 - err = ops->fdma_init(sparx5); 851 - if (!err) 852 - sparx5_fdma_start(sparx5); 853 - } 854 - if (err) 855 - sparx5->fdma_irq = -ENXIO; 856 - } else { 857 - sparx5->fdma_irq = -ENXIO; 858 - } 859 - if (err && sparx5->xtr_irq >= 0) { 860 - err = devm_request_irq(sparx5->dev, sparx5->xtr_irq, 861 - sparx5_xtr_handler, IRQF_SHARED, 862 - "sparx5-xtr", sparx5); 863 - if (!err) 864 - err = sparx5_manual_injection_mode(sparx5); 865 - if (err) 866 - sparx5->xtr_irq = -ENXIO; 867 - } else { 868 - sparx5->xtr_irq = -ENXIO; 869 - } 870 - 871 - if (sparx5->ptp_irq >= 0 && 872 - sparx5_has_feature(sparx5, SPX5_FEATURE_PTP)) { 873 - err = devm_request_threaded_irq(sparx5->dev, sparx5->ptp_irq, 874 - NULL, ops->ptp_irq_handler, 875 - IRQF_ONESHOT, "sparx5-ptp", 876 - sparx5); 877 - if (err) 878 - sparx5->ptp_irq = -ENXIO; 879 - 880 - sparx5->ptp = 1; 881 - } 882 - 883 - return err; 884 - } 885 - 886 - static void sparx5_cleanup_ports(struct sparx5 *sparx5) 887 - { 888 - sparx5_unregister_netdevs(sparx5); 889 - sparx5_destroy_netdevs(sparx5); 890 734 } 891 735 892 736 static int mchp_sparx5_probe(struct platform_device *pdev) ··· 939 999 } 940 1000 } 941 1001 942 - err = sparx5_start(sparx5); 1002 + sparx5_pgid_init(sparx5); 1003 + sparx5_vlan_init(sparx5); 1004 + sparx5_board_init(sparx5); 1005 + sparx5_forwarding_init(sparx5); 1006 + 1007 + err = sparx5_calendar_init(sparx5); 943 1008 if (err) { 944 - dev_err(sparx5->dev, "Start failed\n"); 1009 + dev_err(sparx5->dev, "Failed to initialize calendar\n"); 945 1010 goto cleanup_ports; 946 1011 } 947 1012 ··· 956 1011 goto cleanup_ports; 957 1012 } 958 1013 959 - err = sparx5_ptp_init(sparx5); 1014 + err = sparx5_vcap_init(sparx5); 960 1015 if (err) { 961 - dev_err(sparx5->dev, "PTP failed\n"); 1016 + dev_err(sparx5->dev, "Failed to initialize VCAP\n"); 962 1017 goto cleanup_ports; 1018 + } 1019 + 1020 + err = sparx5_mact_init(sparx5); 1021 + if (err) { 1022 + dev_err(sparx5->dev, "Failed to initialize MAC table\n"); 1023 + goto cleanup_vcap; 1024 + } 1025 + 1026 + err = sparx5_stats_init(sparx5); 1027 + if (err) { 1028 + dev_err(sparx5->dev, "Failed to initialize stats\n"); 1029 + goto cleanup_mact; 963 1030 } 964 1031 965 1032 INIT_LIST_HEAD(&sparx5->mall_entries); 966 1033 1034 + err = sparx5_frame_io_init(sparx5); 1035 + if (err) { 1036 + dev_err(sparx5->dev, "Failed to initialize frame I/O\n"); 1037 + goto cleanup_stats; 1038 + } 1039 + 1040 + err = sparx5_ptp_init(sparx5); 1041 + if (err) { 1042 + dev_err(sparx5->dev, "Failed to initialize PTP\n"); 1043 + goto cleanup_frame_io; 1044 + } 1045 + 1046 + err = sparx5_register_netdevs(sparx5); 1047 + if (err) { 1048 + dev_err(sparx5->dev, "Failed to register net devices\n"); 1049 + goto cleanup_ptp; 1050 + } 1051 + 1052 + err = sparx5_register_notifier_blocks(sparx5); 1053 + if (err) { 1054 + dev_err(sparx5->dev, "Failed to register notifier blocks\n"); 1055 + goto cleanup_netdevs; 1056 + } 1057 + 967 1058 goto cleanup_config; 968 1059 1060 + cleanup_netdevs: 1061 + sparx5_unregister_netdevs(sparx5); 1062 + cleanup_ptp: 1063 + sparx5_ptp_deinit(sparx5); 1064 + cleanup_frame_io: 1065 + sparx5_frame_io_deinit(sparx5); 1066 + cleanup_stats: 1067 + sparx5_stats_deinit(sparx5); 1068 + cleanup_mact: 1069 + sparx5_mact_deinit(sparx5); 1070 + cleanup_vcap: 1071 + sparx5_vcap_deinit(sparx5); 969 1072 cleanup_ports: 970 - sparx5_cleanup_ports(sparx5); 971 - if (sparx5->mact_queue) 972 - destroy_workqueue(sparx5->mact_queue); 1073 + sparx5_destroy_netdevs(sparx5); 973 1074 cleanup_config: 974 1075 kfree(configs); 975 1076 cleanup_pnode: ··· 1026 1035 static void mchp_sparx5_remove(struct platform_device *pdev) 1027 1036 { 1028 1037 struct sparx5 *sparx5 = platform_get_drvdata(pdev); 1029 - const struct sparx5_ops *ops = sparx5->data->ops; 1030 1038 1031 1039 debugfs_remove_recursive(sparx5->debugfs_root); 1032 - if (sparx5->xtr_irq) { 1033 - disable_irq(sparx5->xtr_irq); 1034 - sparx5->xtr_irq = -ENXIO; 1035 - } 1036 - if (sparx5->fdma_irq) { 1037 - disable_irq(sparx5->fdma_irq); 1038 - sparx5->fdma_irq = -ENXIO; 1039 - } 1040 - sparx5_ptp_deinit(sparx5); 1041 - ops->fdma_deinit(sparx5); 1042 - sparx5_cleanup_ports(sparx5); 1043 - sparx5_vcap_destroy(sparx5); 1044 - /* Unregister netdevs */ 1045 1040 sparx5_unregister_notifier_blocks(sparx5); 1046 - destroy_workqueue(sparx5->mact_queue); 1041 + sparx5_unregister_netdevs(sparx5); 1042 + sparx5_ptp_deinit(sparx5); 1043 + sparx5_frame_io_deinit(sparx5); 1044 + sparx5_stats_deinit(sparx5); 1045 + sparx5_mact_deinit(sparx5); 1046 + sparx5_vcap_deinit(sparx5); 1047 + sparx5_destroy_netdevs(sparx5); 1047 1048 } 1048 1049 1049 1050 static const struct sparx5_regs sparx5_regs = {
+6 -6
drivers/net/ethernet/microchip/sparx5/sparx5_main.h
··· 470 470 void sparx5_fdma_injection_mode(struct sparx5 *sparx5); 471 471 472 472 /* sparx5_mactable.c */ 473 - void sparx5_mact_pull_work(struct work_struct *work); 474 473 int sparx5_mact_learn(struct sparx5 *sparx5, int port, 475 474 const unsigned char mac[ETH_ALEN], u16 vid); 476 475 bool sparx5_mact_getnext(struct sparx5 *sparx5, ··· 488 489 int sparx5_mc_sync(struct net_device *dev, const unsigned char *addr); 489 490 int sparx5_mc_unsync(struct net_device *dev, const unsigned char *addr); 490 491 void sparx5_set_ageing(struct sparx5 *sparx5, int msecs); 491 - void sparx5_mact_init(struct sparx5 *sparx5); 492 + int sparx5_mact_init(struct sparx5 *sparx5); 493 + void sparx5_mact_deinit(struct sparx5 *sparx5); 492 494 493 495 /* sparx5_vlan.c */ 494 496 void sparx5_pgid_update_mask(struct sparx5_port *port, int pgid, bool enable); ··· 504 504 void sparx5_vlan_port_apply(struct sparx5 *sparx5, struct sparx5_port *port); 505 505 506 506 /* sparx5_calendar.c */ 507 - int sparx5_config_auto_calendar(struct sparx5 *sparx5); 508 - int sparx5_config_dsm_calendar(struct sparx5 *sparx5); 507 + int sparx5_calendar_init(struct sparx5 *sparx5); 509 508 int sparx5_dsm_calendar_calc(struct sparx5 *sparx5, u32 taxi, 510 509 struct sparx5_calendar_data *data); 511 510 u32 sparx5_cal_speed_to_value(enum sparx5_cal_bw speed); ··· 513 514 514 515 /* sparx5_ethtool.c */ 515 516 void sparx5_get_stats64(struct net_device *ndev, struct rtnl_link_stats64 *stats); 516 - int sparx_stats_init(struct sparx5 *sparx5); 517 + int sparx5_stats_init(struct sparx5 *sparx5); 518 + void sparx5_stats_deinit(struct sparx5 *sparx5); 517 519 518 520 /* sparx5_dcb.c */ 519 521 #ifdef CONFIG_SPARX5_DCB ··· 563 563 564 564 /* sparx5_vcap_impl.c */ 565 565 int sparx5_vcap_init(struct sparx5 *sparx5); 566 - void sparx5_vcap_destroy(struct sparx5 *sparx5); 566 + void sparx5_vcap_deinit(struct sparx5 *sparx5); 567 567 568 568 /* sparx5_pgid.c */ 569 569 enum sparx5_pgid_type {
+18
drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c
··· 606 606 int sparx5_ptp_init(struct sparx5 *sparx5) 607 607 { 608 608 u64 tod_adj = sparx5_ptp_get_nominal_value(sparx5); 609 + const struct sparx5_ops *ops = sparx5->data->ops; 609 610 struct sparx5_port *port; 610 611 int err, i; 612 + 613 + if (sparx5->ptp_irq >= 0 && 614 + sparx5_has_feature(sparx5, SPX5_FEATURE_PTP)) { 615 + err = devm_request_threaded_irq(sparx5->dev, sparx5->ptp_irq, 616 + NULL, ops->ptp_irq_handler, 617 + IRQF_ONESHOT, "sparx5-ptp", 618 + sparx5); 619 + if (err) 620 + sparx5->ptp_irq = -ENXIO; 621 + 622 + sparx5->ptp = 1; 623 + } 611 624 612 625 if (!sparx5->ptp) 613 626 return 0; ··· 672 659 { 673 660 struct sparx5_port *port; 674 661 int i; 662 + 663 + if (sparx5->ptp_irq >= 0) { 664 + disable_irq(sparx5->ptp_irq); 665 + sparx5->ptp_irq = -ENXIO; 666 + } 675 667 676 668 for (i = 0; i < sparx5->data->consts->n_ports; i++) { 677 669 port = sparx5->ports[i];
+1 -1
drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c
··· 2083 2083 return err; 2084 2084 } 2085 2085 2086 - void sparx5_vcap_destroy(struct sparx5 *sparx5) 2086 + void sparx5_vcap_deinit(struct sparx5 *sparx5) 2087 2087 { 2088 2088 struct vcap_control *ctrl = sparx5->vcap_ctrl; 2089 2089 struct vcap_admin *admin, *admin_next;