Path: blob/main/sys/dev/bnxt/bnxt_en/bnxt_auxbus_compat.c
39536 views
/*-1* Broadcom NetXtreme-C/E network driver.2*3* Copyright (c) 2024 Broadcom, All Rights Reserved.4* The term Broadcom refers to Broadcom Limited and/or its subsidiaries5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11* 2. Redistributions in binary form must reproduce the above copyright12* notice, this list of conditions and the following disclaimer in the13* documentation and/or other materials provided with the distribution.14*15* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS'16* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE17* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE18* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS19* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR20* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF21* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS22* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN23* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)24* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF25* THE POSSIBILITY OF SUCH DAMAGE.26*/2728#include <linux/device.h>29#include <linux/slab.h>30#include <linux/string.h>31#include <linux/types.h>32#include <linux/list.h>33#include <linux/delay.h>3435#include "bnxt_auxbus_compat.h"3637static struct list_head bnxt_aux_bus_dev_list = LINUX_LIST_HEAD_INIT(bnxt_aux_bus_dev_list);38static struct list_head bnxt_aux_bus_drv_list = LINUX_LIST_HEAD_INIT(bnxt_aux_bus_drv_list);39static DEFINE_MUTEX(bnxt_auxbus_lock);4041static const struct auxiliary_device_id *auxiliary_match_id(const struct auxiliary_device_id *id,42const struct auxiliary_device *auxdev)43{44for (; id->name[0]; id++) {45const char *p = strrchr(dev_name(&auxdev->dev), '.');46int match_size;4748if (!p)49continue;50match_size = p - dev_name(&auxdev->dev);5152if (strlen(id->name) == match_size &&53!strncmp(dev_name(&auxdev->dev), id->name, match_size))54return id;55}56return NULL;57}5859int auxiliary_device_init(struct auxiliary_device *auxdev)60{61struct device *dev = &auxdev->dev;62char *modname = KBUILD_MODNAME;63int ret;6465if (!dev->parent) {66pr_err("auxiliary_device has a NULL dev->parent\n");67return -EINVAL;68}6970if (!auxdev->name) {71pr_err("auxiliary_device has a NULL name\n");72return -EINVAL;73}7475ret = dev_set_name(dev, "%s.%s.%d", modname, auxdev->name, auxdev->id);76if (ret) {77dev_err(dev, "auxiliary device dev_set_name failed: %d\n", ret);78return ret;79}8081return 0;82}8384int auxiliary_device_add(struct auxiliary_device *auxdev)85{86const struct auxiliary_device_id *id;87struct auxiliary_driver *auxdrv = NULL;88bool found = true;89int ret = 0;9091mutex_lock(&bnxt_auxbus_lock);92list_for_each_entry(auxdrv, &bnxt_aux_bus_drv_list, list) {93if (auxdrv) {94msleep(2 * 1000);9596id = auxiliary_match_id(auxdrv->id_table, auxdev);97if (id) {98ret = auxdrv->probe(auxdev, id);99if (!ret)100auxdev->dev.driver = &auxdrv->driver;101else102found = false;103break;104}105}106}107108if (found)109list_add_tail(&auxdev->list, &bnxt_aux_bus_dev_list);110mutex_unlock(&bnxt_auxbus_lock);111112return ret;113}114115void auxiliary_device_uninit(struct auxiliary_device *auxdev)116{117return;118}119120void auxiliary_device_delete(struct auxiliary_device *auxdev)121{122struct auxiliary_driver *auxdrv;123124mutex_lock(&bnxt_auxbus_lock);125list_for_each_entry(auxdrv, &bnxt_aux_bus_drv_list, list) {126if (auxdev->dev.driver != &auxdrv->driver)127continue;128if (auxdrv->remove)129auxdrv->remove(auxdev);130auxdev->dev.driver = NULL;131}132list_del(&auxdev->list);133mutex_unlock(&bnxt_auxbus_lock);134}135136int auxiliary_driver_register(struct auxiliary_driver *auxdrv)137{138const struct auxiliary_device_id *id;139struct auxiliary_device *auxdev;140int ret = 0;141142if (WARN_ON(!auxdrv->probe) || WARN_ON(!auxdrv->id_table))143return -EINVAL;144145if (auxdrv->name)146auxdrv->driver.name = kasprintf(GFP_KERNEL, "%s.%s", KBUILD_MODNAME,147auxdrv->name);148else149auxdrv->driver.name = kasprintf(GFP_KERNEL, "%s", KBUILD_MODNAME);150if (!auxdrv->driver.name)151return -ENOMEM;152153mutex_lock(&bnxt_auxbus_lock);154list_for_each_entry(auxdev, &bnxt_aux_bus_dev_list, list) {155if (auxdev->dev.driver)156continue;157158id = auxiliary_match_id(auxdrv->id_table, auxdev);159if (id) {160ret = auxdrv->probe(auxdev, id);161if (ret)162continue;163auxdev->dev.driver = &auxdrv->driver;164}165}166list_add_tail(&auxdrv->list, &bnxt_aux_bus_drv_list);167mutex_unlock(&bnxt_auxbus_lock);168return 0;169}170EXPORT_SYMBOL(auxiliary_driver_register);171172void auxiliary_driver_unregister(struct auxiliary_driver *auxdrv)173{174struct auxiliary_device *auxdev;175176/* PF auxiliary devices are added to the list first and then VF devices.177* If we remove PF aux device driver first, it causes failures while178* removing VF driver.179* We need to remove VF auxiliary drivers first, so walk backwards.180*/181mutex_lock(&bnxt_auxbus_lock);182list_for_each_entry_reverse(auxdev, &bnxt_aux_bus_dev_list, list) {183if (auxdev->dev.driver != &auxdrv->driver)184continue;185if (auxdrv->remove)186auxdrv->remove(auxdev);187auxdev->dev.driver = NULL;188}189kfree(auxdrv->driver.name);190list_del(&auxdrv->list);191mutex_unlock(&bnxt_auxbus_lock);192}193EXPORT_SYMBOL(auxiliary_driver_unregister);194195196