1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 2/* Copyright (c) 2022 NVIDIA Corporation and Mellanox Technologies. All rights reserved */ 3 4#include <linux/kernel.h> 5#include <linux/module.h> 6#include <linux/err.h> 7#include <linux/types.h> 8#include <linux/auxiliary_bus.h> 9#include <linux/idr.h> 10#include <linux/gfp.h> 11#include <linux/slab.h> 12#include <net/devlink.h> 13#include "core.h" 14 15#define MLXSW_LINECARD_DEV_ID_NAME "lc" 16 17struct mlxsw_linecard_dev { 18 struct mlxsw_linecard *linecard; 19}; 20 21struct mlxsw_linecard_bdev { 22 struct auxiliary_device adev; 23 struct mlxsw_linecard *linecard; 24 struct mlxsw_linecard_dev *linecard_dev; 25}; 26 27static DEFINE_IDA(mlxsw_linecard_bdev_ida); 28 29static int mlxsw_linecard_bdev_id_alloc(void) 30{ 31 return ida_alloc(&mlxsw_linecard_bdev_ida, GFP_KERNEL); 32} 33 34static void mlxsw_linecard_bdev_id_free(int id) 35{ 36 ida_free(&mlxsw_linecard_bdev_ida, id); 37} 38 39static void mlxsw_linecard_bdev_release(struct device *device) 40{ 41 struct auxiliary_device *adev = 42 container_of(device, struct auxiliary_device, dev); 43 struct mlxsw_linecard_bdev *linecard_bdev = 44 container_of(adev, struct mlxsw_linecard_bdev, adev); 45 46 mlxsw_linecard_bdev_id_free(adev->id); 47 kfree(linecard_bdev); 48} 49 50int mlxsw_linecard_bdev_add(struct mlxsw_linecard *linecard) 51{ 52 struct mlxsw_linecard_bdev *linecard_bdev; 53 int err; 54 int id; 55 56 id = mlxsw_linecard_bdev_id_alloc(); 57 if (id < 0) 58 return id; 59 60 linecard_bdev = kzalloc(sizeof(*linecard_bdev), GFP_KERNEL); 61 if (!linecard_bdev) { 62 mlxsw_linecard_bdev_id_free(id); 63 return -ENOMEM; 64 } 65 linecard_bdev->adev.id = id; 66 linecard_bdev->adev.name = MLXSW_LINECARD_DEV_ID_NAME; 67 linecard_bdev->adev.dev.release = mlxsw_linecard_bdev_release; 68 linecard_bdev->adev.dev.parent = linecard->linecards->bus_info->dev; 69 linecard_bdev->linecard = linecard; 70 71 err = auxiliary_device_init(&linecard_bdev->adev); 72 if (err) { 73 mlxsw_linecard_bdev_id_free(id); 74 kfree(linecard_bdev); 75 return err; 76 } 77 78 err = auxiliary_device_add(&linecard_bdev->adev); 79 if (err) { 80 auxiliary_device_uninit(&linecard_bdev->adev); 81 return err; 82 } 83 84 linecard->bdev = linecard_bdev; 85 return 0; 86} 87 88void mlxsw_linecard_bdev_del(struct mlxsw_linecard *linecard) 89{ 90 struct mlxsw_linecard_bdev *linecard_bdev = linecard->bdev; 91 92 if (!linecard_bdev) 93 /* Unprovisioned line cards do not have an auxiliary device. */ 94 return; 95 auxiliary_device_delete(&linecard_bdev->adev); 96 auxiliary_device_uninit(&linecard_bdev->adev); 97 linecard->bdev = NULL; 98} 99 100static int mlxsw_linecard_dev_devlink_info_get(struct devlink *devlink, 101 struct devlink_info_req *req, 102 struct netlink_ext_ack *extack) 103{ 104 struct mlxsw_linecard_dev *linecard_dev = devlink_priv(devlink); 105 struct mlxsw_linecard *linecard = linecard_dev->linecard; 106 107 return mlxsw_linecard_devlink_info_get(linecard, req, extack); 108} 109 110static int 111mlxsw_linecard_dev_devlink_flash_update(struct devlink *devlink, 112 struct devlink_flash_update_params *params, 113 struct netlink_ext_ack *extack) 114{ 115 struct mlxsw_linecard_dev *linecard_dev = devlink_priv(devlink); 116 struct mlxsw_linecard *linecard = linecard_dev->linecard; 117 118 return mlxsw_linecard_flash_update(devlink, linecard, 119 params->fw, extack); 120} 121 122static const struct devlink_ops mlxsw_linecard_dev_devlink_ops = { 123 .info_get = mlxsw_linecard_dev_devlink_info_get, 124 .flash_update = mlxsw_linecard_dev_devlink_flash_update, 125}; 126 127static int mlxsw_linecard_bdev_probe(struct auxiliary_device *adev, 128 const struct auxiliary_device_id *id) 129{ 130 struct mlxsw_linecard_bdev *linecard_bdev = 131 container_of(adev, struct mlxsw_linecard_bdev, adev); 132 struct mlxsw_linecard *linecard = linecard_bdev->linecard; 133 struct mlxsw_linecard_dev *linecard_dev; 134 struct devlink *devlink; 135 int err; 136 137 devlink = devlink_alloc(&mlxsw_linecard_dev_devlink_ops, 138 sizeof(*linecard_dev), &adev->dev); 139 if (!devlink) 140 return -ENOMEM; 141 linecard_dev = devlink_priv(devlink); 142 linecard_dev->linecard = linecard_bdev->linecard; 143 linecard_bdev->linecard_dev = linecard_dev; 144 145 err = devlink_linecard_nested_dl_set(linecard->devlink_linecard, devlink); 146 if (err) { 147 devlink_free(devlink); 148 return err; 149 } 150 devlink_register(devlink); 151 return 0; 152} 153 154static void mlxsw_linecard_bdev_remove(struct auxiliary_device *adev) 155{ 156 struct mlxsw_linecard_bdev *linecard_bdev = 157 container_of(adev, struct mlxsw_linecard_bdev, adev); 158 struct devlink *devlink = priv_to_devlink(linecard_bdev->linecard_dev); 159 160 devlink_unregister(devlink); 161 devlink_free(devlink); 162} 163 164static const struct auxiliary_device_id mlxsw_linecard_bdev_id_table[] = { 165 { .name = KBUILD_MODNAME "." MLXSW_LINECARD_DEV_ID_NAME }, 166 {}, 167}; 168 169MODULE_DEVICE_TABLE(auxiliary, mlxsw_linecard_bdev_id_table); 170 171static struct auxiliary_driver mlxsw_linecard_driver = { 172 .name = MLXSW_LINECARD_DEV_ID_NAME, 173 .probe = mlxsw_linecard_bdev_probe, 174 .remove = mlxsw_linecard_bdev_remove, 175 .id_table = mlxsw_linecard_bdev_id_table, 176}; 177 178int mlxsw_linecard_driver_register(void) 179{ 180 return auxiliary_driver_register(&mlxsw_linecard_driver); 181} 182 183void mlxsw_linecard_driver_unregister(void) 184{ 185 auxiliary_driver_unregister(&mlxsw_linecard_driver); 186} 187