// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 /* Copyright (c) 2022 NVIDIA Corporation and Mellanox Technologies. All rights reserved */ #include #include #include #include #include #include #include #include #include #include "core.h" #define MLXSW_LINECARD_DEV_ID_NAME "lc" struct mlxsw_linecard_dev { struct mlxsw_linecard *linecard; }; struct mlxsw_linecard_bdev { struct auxiliary_device adev; struct mlxsw_linecard *linecard; struct mlxsw_linecard_dev *linecard_dev; }; static DEFINE_IDA(mlxsw_linecard_bdev_ida); static int mlxsw_linecard_bdev_id_alloc(void) { return ida_alloc(&mlxsw_linecard_bdev_ida, GFP_KERNEL); } static void mlxsw_linecard_bdev_id_free(int id) { ida_free(&mlxsw_linecard_bdev_ida, id); } static void mlxsw_linecard_bdev_release(struct device *device) { struct auxiliary_device *adev = container_of(device, struct auxiliary_device, dev); struct mlxsw_linecard_bdev *linecard_bdev = container_of(adev, struct mlxsw_linecard_bdev, adev); mlxsw_linecard_bdev_id_free(adev->id); kfree(linecard_bdev); } int mlxsw_linecard_bdev_add(struct mlxsw_linecard *linecard) { struct mlxsw_linecard_bdev *linecard_bdev; int err; int id; id = mlxsw_linecard_bdev_id_alloc(); if (id < 0) return id; linecard_bdev = kzalloc(sizeof(*linecard_bdev), GFP_KERNEL); if (!linecard_bdev) { mlxsw_linecard_bdev_id_free(id); return -ENOMEM; } linecard_bdev->adev.id = id; linecard_bdev->adev.name = MLXSW_LINECARD_DEV_ID_NAME; linecard_bdev->adev.dev.release = mlxsw_linecard_bdev_release; linecard_bdev->adev.dev.parent = linecard->linecards->bus_info->dev; linecard_bdev->linecard = linecard; err = auxiliary_device_init(&linecard_bdev->adev); if (err) { mlxsw_linecard_bdev_id_free(id); kfree(linecard_bdev); return err; } err = auxiliary_device_add(&linecard_bdev->adev); if (err) { auxiliary_device_uninit(&linecard_bdev->adev); return err; } linecard->bdev = linecard_bdev; return 0; } void mlxsw_linecard_bdev_del(struct mlxsw_linecard *linecard) { struct mlxsw_linecard_bdev *linecard_bdev = linecard->bdev; if (!linecard_bdev) /* Unprovisioned line cards do not have an auxiliary device. */ return; auxiliary_device_delete(&linecard_bdev->adev); auxiliary_device_uninit(&linecard_bdev->adev); linecard->bdev = NULL; } static int mlxsw_linecard_dev_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req, struct netlink_ext_ack *extack) { struct mlxsw_linecard_dev *linecard_dev = devlink_priv(devlink); struct mlxsw_linecard *linecard = linecard_dev->linecard; return mlxsw_linecard_devlink_info_get(linecard, req, extack); } static int mlxsw_linecard_dev_devlink_flash_update(struct devlink *devlink, struct devlink_flash_update_params *params, struct netlink_ext_ack *extack) { struct mlxsw_linecard_dev *linecard_dev = devlink_priv(devlink); struct mlxsw_linecard *linecard = linecard_dev->linecard; return mlxsw_linecard_flash_update(devlink, linecard, params->fw, extack); } static const struct devlink_ops mlxsw_linecard_dev_devlink_ops = { .info_get = mlxsw_linecard_dev_devlink_info_get, .flash_update = mlxsw_linecard_dev_devlink_flash_update, }; static int mlxsw_linecard_bdev_probe(struct auxiliary_device *adev, const struct auxiliary_device_id *id) { struct mlxsw_linecard_bdev *linecard_bdev = container_of(adev, struct mlxsw_linecard_bdev, adev); struct mlxsw_linecard *linecard = linecard_bdev->linecard; struct mlxsw_linecard_dev *linecard_dev; struct devlink *devlink; int err; devlink = devlink_alloc(&mlxsw_linecard_dev_devlink_ops, sizeof(*linecard_dev), &adev->dev); if (!devlink) return -ENOMEM; linecard_dev = devlink_priv(devlink); linecard_dev->linecard = linecard_bdev->linecard; linecard_bdev->linecard_dev = linecard_dev; err = devlink_linecard_nested_dl_set(linecard->devlink_linecard, devlink); if (err) { devlink_free(devlink); return err; } devlink_register(devlink); return 0; } static void mlxsw_linecard_bdev_remove(struct auxiliary_device *adev) { struct mlxsw_linecard_bdev *linecard_bdev = container_of(adev, struct mlxsw_linecard_bdev, adev); struct devlink *devlink = priv_to_devlink(linecard_bdev->linecard_dev); devlink_unregister(devlink); devlink_free(devlink); } static const struct auxiliary_device_id mlxsw_linecard_bdev_id_table[] = { { .name = KBUILD_MODNAME "." MLXSW_LINECARD_DEV_ID_NAME }, {}, }; MODULE_DEVICE_TABLE(auxiliary, mlxsw_linecard_bdev_id_table); static struct auxiliary_driver mlxsw_linecard_driver = { .name = MLXSW_LINECARD_DEV_ID_NAME, .probe = mlxsw_linecard_bdev_probe, .remove = mlxsw_linecard_bdev_remove, .id_table = mlxsw_linecard_bdev_id_table, }; int mlxsw_linecard_driver_register(void) { return auxiliary_driver_register(&mlxsw_linecard_driver); } void mlxsw_linecard_driver_unregister(void) { auxiliary_driver_unregister(&mlxsw_linecard_driver); }