1d6a5c562SArend van Spriel // SPDX-License-Identifier: ISC 2d6a5c562SArend van Spriel /* 3d6a5c562SArend van Spriel * Copyright (c) 2022 Broadcom Corporation 4d6a5c562SArend van Spriel */ 5d6a5c562SArend van Spriel #include <linux/errno.h> 6d6a5c562SArend van Spriel #include <linux/export.h> 7d6a5c562SArend van Spriel #include <linux/module.h> 8d6a5c562SArend van Spriel #include <linux/kmod.h> 9d6a5c562SArend van Spriel #include <linux/list.h> 10d6a5c562SArend van Spriel #include <linux/completion.h> 11d6a5c562SArend van Spriel #include <linux/mutex.h> 12d6a5c562SArend van Spriel #include <linux/printk.h> 13d6a5c562SArend van Spriel #include <linux/jiffies.h> 14d6a5c562SArend van Spriel #include <linux/workqueue.h> 15d6a5c562SArend van Spriel 16d6a5c562SArend van Spriel #include "core.h" 17d6a5c562SArend van Spriel #include "bus.h" 18d6a5c562SArend van Spriel #include "debug.h" 19d6a5c562SArend van Spriel #include "fwvid.h" 20d6a5c562SArend van Spriel 21d6a5c562SArend van Spriel #include "wcc/vops.h" 22*f74f1ec2SArend van Spriel #include "cyw/vops.h" 23d6a5c562SArend van Spriel 24d6a5c562SArend van Spriel struct brcmf_fwvid_entry { 25d6a5c562SArend van Spriel const char *name; 26d6a5c562SArend van Spriel const struct brcmf_fwvid_ops *vops; 27d6a5c562SArend van Spriel struct list_head drvr_list; 28d6a5c562SArend van Spriel #if IS_MODULE(CONFIG_BRCMFMAC) 29d6a5c562SArend van Spriel struct module *vmod; 30d6a5c562SArend van Spriel struct completion reg_done; 31d6a5c562SArend van Spriel #endif 32d6a5c562SArend van Spriel }; 33d6a5c562SArend van Spriel 34d6a5c562SArend van Spriel static DEFINE_MUTEX(fwvid_list_lock); 35d6a5c562SArend van Spriel 36d6a5c562SArend van Spriel #if IS_MODULE(CONFIG_BRCMFMAC) 37d6a5c562SArend van Spriel #define FWVID_ENTRY_INIT(_vid, _name) \ 38d6a5c562SArend van Spriel [BRCMF_FWVENDOR_ ## _vid] = { \ 39d6a5c562SArend van Spriel .name = #_name, \ 40d6a5c562SArend van Spriel .reg_done = COMPLETION_INITIALIZER(fwvid_list[BRCMF_FWVENDOR_ ## _vid].reg_done), \ 41d6a5c562SArend van Spriel .drvr_list = LIST_HEAD_INIT(fwvid_list[BRCMF_FWVENDOR_ ## _vid].drvr_list), \ 42d6a5c562SArend van Spriel } 43d6a5c562SArend van Spriel #else 44d6a5c562SArend van Spriel #define FWVID_ENTRY_INIT(_vid, _name) \ 45d6a5c562SArend van Spriel [BRCMF_FWVENDOR_ ## _vid] = { \ 46d6a5c562SArend van Spriel .name = #_name, \ 47d6a5c562SArend van Spriel .drvr_list = LIST_HEAD_INIT(fwvid_list[BRCMF_FWVENDOR_ ## _vid].drvr_list), \ 48d6a5c562SArend van Spriel .vops = _vid ## _VOPS \ 49d6a5c562SArend van Spriel } 50d6a5c562SArend van Spriel #endif /* IS_MODULE(CONFIG_BRCMFMAC) */ 51d6a5c562SArend van Spriel 52d6a5c562SArend van Spriel static struct brcmf_fwvid_entry fwvid_list[BRCMF_FWVENDOR_NUM] = { 53d6a5c562SArend van Spriel FWVID_ENTRY_INIT(WCC, wcc), 54*f74f1ec2SArend van Spriel FWVID_ENTRY_INIT(CYW, cyw), 55d6a5c562SArend van Spriel }; 56d6a5c562SArend van Spriel 57d6a5c562SArend van Spriel #if IS_MODULE(CONFIG_BRCMFMAC) 58d6a5c562SArend van Spriel static int brcmf_fwvid_request_module(enum brcmf_fwvendor fwvid) 59d6a5c562SArend van Spriel { 60d6a5c562SArend van Spriel int ret; 61d6a5c562SArend van Spriel 62d6a5c562SArend van Spriel if (!fwvid_list[fwvid].vmod) { 63d6a5c562SArend van Spriel struct completion *reg_done = &fwvid_list[fwvid].reg_done; 64d6a5c562SArend van Spriel 65d6a5c562SArend van Spriel mutex_unlock(&fwvid_list_lock); 66d6a5c562SArend van Spriel 67d6a5c562SArend van Spriel ret = request_module("brcmfmac-%s", fwvid_list[fwvid].name); 68d6a5c562SArend van Spriel if (ret) 69d6a5c562SArend van Spriel goto fail; 70d6a5c562SArend van Spriel 71d6a5c562SArend van Spriel ret = wait_for_completion_interruptible(reg_done); 72d6a5c562SArend van Spriel if (ret) 73d6a5c562SArend van Spriel goto fail; 74d6a5c562SArend van Spriel 75d6a5c562SArend van Spriel mutex_lock(&fwvid_list_lock); 76d6a5c562SArend van Spriel } 77d6a5c562SArend van Spriel return 0; 78d6a5c562SArend van Spriel 79d6a5c562SArend van Spriel fail: 80d6a5c562SArend van Spriel brcmf_err("mod=%s: failed %d\n", fwvid_list[fwvid].name, ret); 81d6a5c562SArend van Spriel return ret; 82d6a5c562SArend van Spriel } 83d6a5c562SArend van Spriel 84d6a5c562SArend van Spriel int brcmf_fwvid_register_vendor(enum brcmf_fwvendor fwvid, struct module *vmod, 85d6a5c562SArend van Spriel const struct brcmf_fwvid_ops *vops) 86d6a5c562SArend van Spriel { 87d6a5c562SArend van Spriel if (fwvid >= BRCMF_FWVENDOR_NUM) 88d6a5c562SArend van Spriel return -ERANGE; 89d6a5c562SArend van Spriel 90d6a5c562SArend van Spriel if (WARN_ON(!vmod) || WARN_ON(!vops) || 91d6a5c562SArend van Spriel WARN_ON(!vops->attach) || WARN_ON(!vops->detach)) 92d6a5c562SArend van Spriel return -EINVAL; 93d6a5c562SArend van Spriel 94d6a5c562SArend van Spriel if (WARN_ON(fwvid_list[fwvid].vmod)) 95d6a5c562SArend van Spriel return -EEXIST; 96d6a5c562SArend van Spriel 97d6a5c562SArend van Spriel brcmf_dbg(TRACE, "mod=%s: enter\n", fwvid_list[fwvid].name); 98d6a5c562SArend van Spriel 99d6a5c562SArend van Spriel mutex_lock(&fwvid_list_lock); 100d6a5c562SArend van Spriel 101d6a5c562SArend van Spriel fwvid_list[fwvid].vmod = vmod; 102d6a5c562SArend van Spriel fwvid_list[fwvid].vops = vops; 103d6a5c562SArend van Spriel 104d6a5c562SArend van Spriel mutex_unlock(&fwvid_list_lock); 105d6a5c562SArend van Spriel 106d6a5c562SArend van Spriel complete_all(&fwvid_list[fwvid].reg_done); 107d6a5c562SArend van Spriel 108d6a5c562SArend van Spriel return 0; 109d6a5c562SArend van Spriel } 110d6a5c562SArend van Spriel EXPORT_SYMBOL(brcmf_fwvid_register_vendor); 111d6a5c562SArend van Spriel 112d6a5c562SArend van Spriel int brcmf_fwvid_unregister_vendor(enum brcmf_fwvendor fwvid, struct module *mod) 113d6a5c562SArend van Spriel { 114d6a5c562SArend van Spriel struct brcmf_bus *bus, *tmp; 115d6a5c562SArend van Spriel 116d6a5c562SArend van Spriel if (fwvid >= BRCMF_FWVENDOR_NUM) 117d6a5c562SArend van Spriel return -ERANGE; 118d6a5c562SArend van Spriel 119d6a5c562SArend van Spriel if (WARN_ON(fwvid_list[fwvid].vmod != mod)) 120d6a5c562SArend van Spriel return -ENOENT; 121d6a5c562SArend van Spriel 122d6a5c562SArend van Spriel mutex_lock(&fwvid_list_lock); 123d6a5c562SArend van Spriel 124d6a5c562SArend van Spriel list_for_each_entry_safe(bus, tmp, &fwvid_list[fwvid].drvr_list, list) { 125d6a5c562SArend van Spriel mutex_unlock(&fwvid_list_lock); 126d6a5c562SArend van Spriel 127d6a5c562SArend van Spriel brcmf_dbg(INFO, "mod=%s: removing %s\n", fwvid_list[fwvid].name, 128d6a5c562SArend van Spriel dev_name(bus->dev)); 129d6a5c562SArend van Spriel brcmf_bus_remove(bus); 130d6a5c562SArend van Spriel 131d6a5c562SArend van Spriel mutex_lock(&fwvid_list_lock); 132d6a5c562SArend van Spriel } 133d6a5c562SArend van Spriel 134d6a5c562SArend van Spriel fwvid_list[fwvid].vmod = NULL; 135d6a5c562SArend van Spriel fwvid_list[fwvid].vops = NULL; 136d6a5c562SArend van Spriel reinit_completion(&fwvid_list[fwvid].reg_done); 137d6a5c562SArend van Spriel 138d6a5c562SArend van Spriel brcmf_dbg(TRACE, "mod=%s: exit\n", fwvid_list[fwvid].name); 139d6a5c562SArend van Spriel mutex_unlock(&fwvid_list_lock); 140d6a5c562SArend van Spriel 141d6a5c562SArend van Spriel return 0; 142d6a5c562SArend van Spriel } 143d6a5c562SArend van Spriel EXPORT_SYMBOL(brcmf_fwvid_unregister_vendor); 144d6a5c562SArend van Spriel #else 145d6a5c562SArend van Spriel static inline int brcmf_fwvid_request_module(enum brcmf_fwvendor fwvid) 146d6a5c562SArend van Spriel { 147d6a5c562SArend van Spriel return 0; 148d6a5c562SArend van Spriel } 149d6a5c562SArend van Spriel #endif 150d6a5c562SArend van Spriel 151d6a5c562SArend van Spriel int brcmf_fwvid_attach_ops(struct brcmf_pub *drvr) 152d6a5c562SArend van Spriel { 153d6a5c562SArend van Spriel enum brcmf_fwvendor fwvid = drvr->bus_if->fwvid; 154d6a5c562SArend van Spriel int ret; 155d6a5c562SArend van Spriel 156d6a5c562SArend van Spriel if (fwvid >= ARRAY_SIZE(fwvid_list)) 157d6a5c562SArend van Spriel return -ERANGE; 158d6a5c562SArend van Spriel 159d6a5c562SArend van Spriel brcmf_dbg(TRACE, "mod=%s: enter: dev %s\n", fwvid_list[fwvid].name, 160d6a5c562SArend van Spriel dev_name(drvr->bus_if->dev)); 161d6a5c562SArend van Spriel 162d6a5c562SArend van Spriel mutex_lock(&fwvid_list_lock); 163d6a5c562SArend van Spriel 164d6a5c562SArend van Spriel ret = brcmf_fwvid_request_module(fwvid); 165d6a5c562SArend van Spriel if (ret) 166d6a5c562SArend van Spriel return ret; 167d6a5c562SArend van Spriel 168d6a5c562SArend van Spriel drvr->vops = fwvid_list[fwvid].vops; 169d6a5c562SArend van Spriel list_add(&drvr->bus_if->list, &fwvid_list[fwvid].drvr_list); 170d6a5c562SArend van Spriel 171d6a5c562SArend van Spriel mutex_unlock(&fwvid_list_lock); 172d6a5c562SArend van Spriel 173d6a5c562SArend van Spriel return ret; 174d6a5c562SArend van Spriel } 175d6a5c562SArend van Spriel 176d6a5c562SArend van Spriel void brcmf_fwvid_detach_ops(struct brcmf_pub *drvr) 177d6a5c562SArend van Spriel { 178d6a5c562SArend van Spriel enum brcmf_fwvendor fwvid = drvr->bus_if->fwvid; 179d6a5c562SArend van Spriel 180d6a5c562SArend van Spriel if (fwvid >= ARRAY_SIZE(fwvid_list)) 181d6a5c562SArend van Spriel return; 182d6a5c562SArend van Spriel 183d6a5c562SArend van Spriel brcmf_dbg(TRACE, "mod=%s: enter: dev %s\n", fwvid_list[fwvid].name, 184d6a5c562SArend van Spriel dev_name(drvr->bus_if->dev)); 185d6a5c562SArend van Spriel 186d6a5c562SArend van Spriel mutex_lock(&fwvid_list_lock); 187d6a5c562SArend van Spriel 188d6a5c562SArend van Spriel drvr->vops = NULL; 189d6a5c562SArend van Spriel list_del(&drvr->bus_if->list); 190d6a5c562SArend van Spriel 191d6a5c562SArend van Spriel mutex_unlock(&fwvid_list_lock); 192d6a5c562SArend van Spriel } 193