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