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 17 struct mlxsw_linecard_dev { 18 struct mlxsw_linecard *linecard; 19 }; 20 21 struct mlxsw_linecard_bdev { 22 struct auxiliary_device adev; 23 struct mlxsw_linecard *linecard; 24 struct mlxsw_linecard_dev *linecard_dev; 25 }; 26 27 static DEFINE_IDA(mlxsw_linecard_bdev_ida); 28 29 static int mlxsw_linecard_bdev_id_alloc(void) 30 { 31 return ida_alloc(&mlxsw_linecard_bdev_ida, GFP_KERNEL); 32 } 33 34 static void mlxsw_linecard_bdev_id_free(int id) 35 { 36 ida_free(&mlxsw_linecard_bdev_ida, id); 37 } 38 39 static 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 50 int 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 88 void 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 100 static 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 110 static int 111 mlxsw_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 122 static 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 127 static 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 136 devlink = devlink_alloc(&mlxsw_linecard_dev_devlink_ops, 137 sizeof(*linecard_dev), &adev->dev); 138 if (!devlink) 139 return -ENOMEM; 140 linecard_dev = devlink_priv(devlink); 141 linecard_dev->linecard = linecard_bdev->linecard; 142 linecard_bdev->linecard_dev = linecard_dev; 143 144 devlink_register(devlink); 145 devlink_linecard_nested_dl_set(linecard->devlink_linecard, devlink); 146 return 0; 147 } 148 149 static void mlxsw_linecard_bdev_remove(struct auxiliary_device *adev) 150 { 151 struct mlxsw_linecard_bdev *linecard_bdev = 152 container_of(adev, struct mlxsw_linecard_bdev, adev); 153 struct devlink *devlink = priv_to_devlink(linecard_bdev->linecard_dev); 154 struct mlxsw_linecard *linecard = linecard_bdev->linecard; 155 156 devlink_linecard_nested_dl_set(linecard->devlink_linecard, NULL); 157 devlink_unregister(devlink); 158 devlink_free(devlink); 159 } 160 161 static const struct auxiliary_device_id mlxsw_linecard_bdev_id_table[] = { 162 { .name = KBUILD_MODNAME "." MLXSW_LINECARD_DEV_ID_NAME }, 163 {}, 164 }; 165 166 MODULE_DEVICE_TABLE(auxiliary, mlxsw_linecard_bdev_id_table); 167 168 static struct auxiliary_driver mlxsw_linecard_driver = { 169 .name = MLXSW_LINECARD_DEV_ID_NAME, 170 .probe = mlxsw_linecard_bdev_probe, 171 .remove = mlxsw_linecard_bdev_remove, 172 .id_table = mlxsw_linecard_bdev_id_table, 173 }; 174 175 int mlxsw_linecard_driver_register(void) 176 { 177 return auxiliary_driver_register(&mlxsw_linecard_driver); 178 } 179 180 void mlxsw_linecard_driver_unregister(void) 181 { 182 auxiliary_driver_unregister(&mlxsw_linecard_driver); 183 } 184