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(ida: &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(id: adev->id);
47 kfree(objp: 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(size: 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(auxdev: &linecard_bdev->adev);
72 if (err) {
73 mlxsw_linecard_bdev_id_free(id);
74 kfree(objp: linecard_bdev);
75 return err;
76 }
77
78 err = auxiliary_device_add(&linecard_bdev->adev);
79 if (err) {
80 auxiliary_device_uninit(auxdev: &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(auxdev: &linecard_bdev->adev);
96 auxiliary_device_uninit(auxdev: &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(linecard_devlink: devlink, linecard,
119 firmware: 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(ops: &mlxsw_linecard_dev_devlink_ops,
138 priv_size: sizeof(*linecard_dev), 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: linecard->devlink_linecard, nested_devlink: 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(priv: 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(auxdrv: &mlxsw_linecard_driver);
186}
187

source code of linux/drivers/net/ethernet/mellanox/mlxsw/core_linecard_dev.c