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(ida: &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(id: adev->id); |
47 | kfree(objp: 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(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 | |
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(auxdev: &linecard_bdev->adev); |
96 | auxiliary_device_uninit(auxdev: &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(linecard_devlink: devlink, linecard, |
119 | firmware: 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 | 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 | |
154 | static 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 | |
164 | static const struct auxiliary_device_id mlxsw_linecard_bdev_id_table[] = { |
165 | { .name = KBUILD_MODNAME "." MLXSW_LINECARD_DEV_ID_NAME }, |
166 | {}, |
167 | }; |
168 | |
169 | MODULE_DEVICE_TABLE(auxiliary, mlxsw_linecard_bdev_id_table); |
170 | |
171 | static 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 | |
178 | int mlxsw_linecard_driver_register(void) |
179 | { |
180 | return auxiliary_driver_register(&mlxsw_linecard_driver); |
181 | } |
182 | |
183 | void mlxsw_linecard_driver_unregister(void) |
184 | { |
185 | auxiliary_driver_unregister(auxdrv: &mlxsw_linecard_driver); |
186 | } |
187 | |