1 // SPDX-License-Identifier: GPL-2.0
3 * Management Complex (MC) userspace support
9 #include <linux/slab.h>
11 #include <linux/uaccess.h>
12 #include <linux/miscdevice.h>
14 #include "fsl-mc-private.h"
16 struct uapi_priv_data
{
17 struct fsl_mc_uapi
*uapi
;
18 struct fsl_mc_io
*mc_io
;
21 struct fsl_mc_cmd_desc
{
29 #define FSL_MC_CHECK_MODULE_ID BIT(0)
30 #define FSL_MC_CAP_NET_ADMIN_NEEDED BIT(1)
32 enum fsl_mc_cmd_index
{
35 DPRC_GET_CONTAINER_ID
,
53 DPAIOP_GET_SL_VERSION
,
63 DPNI_GET_MAX_FRAME_LENGTH
,
67 DPSW_IF_GET_MAX_FRAME_LENGTH
,
69 DPDMUX_IF_GET_MAX_FRAME_LENGTH
,
80 static struct fsl_mc_cmd_desc fsl_mc_accepted_cmds
[] = {
82 .cmdid_value
= 0x1300,
88 .cmdid_value
= 0x1400,
93 [DPRC_GET_CONTAINER_ID
] = {
94 .cmdid_value
= 0x8300,
99 [DPRC_CREATE_CONT
] = {
100 .cmdid_value
= 0x1510,
101 .cmdid_mask
= 0xFFF0,
104 .flags
= FSL_MC_CAP_NET_ADMIN_NEEDED
,
106 [DPRC_DESTROY_CONT
] = {
107 .cmdid_value
= 0x1520,
108 .cmdid_mask
= 0xFFF0,
111 .flags
= FSL_MC_CAP_NET_ADMIN_NEEDED
,
114 .cmdid_value
= 0x1570,
115 .cmdid_mask
= 0xFFF0,
118 .flags
= FSL_MC_CAP_NET_ADMIN_NEEDED
,
121 .cmdid_value
= 0x1580,
122 .cmdid_mask
= 0xFFF0,
125 .flags
= FSL_MC_CAP_NET_ADMIN_NEEDED
,
127 [DPRC_GET_OBJ_COUNT
] = {
128 .cmdid_value
= 0x1590,
129 .cmdid_mask
= 0xFFF0,
134 .cmdid_value
= 0x15A0,
135 .cmdid_mask
= 0xFFF0,
139 [DPRC_GET_RES_COUNT
] = {
140 .cmdid_value
= 0x15B0,
141 .cmdid_mask
= 0xFFF0,
145 [DPRC_GET_RES_IDS
] = {
146 .cmdid_value
= 0x15C0,
147 .cmdid_mask
= 0xFFF0,
151 [DPRC_SET_OBJ_LABEL
] = {
152 .cmdid_value
= 0x1610,
153 .cmdid_mask
= 0xFFF0,
156 .flags
= FSL_MC_CAP_NET_ADMIN_NEEDED
,
158 [DPRC_SET_LOCKED
] = {
159 .cmdid_value
= 0x16B0,
160 .cmdid_mask
= 0xFFF0,
163 .flags
= FSL_MC_CAP_NET_ADMIN_NEEDED
,
166 .cmdid_value
= 0x1670,
167 .cmdid_mask
= 0xFFF0,
170 .flags
= FSL_MC_CAP_NET_ADMIN_NEEDED
,
172 [DPRC_DISCONNECT
] = {
173 .cmdid_value
= 0x1680,
174 .cmdid_mask
= 0xFFF0,
177 .flags
= FSL_MC_CAP_NET_ADMIN_NEEDED
,
180 .cmdid_value
= 0x1690,
181 .cmdid_mask
= 0xFFF0,
185 [DPRC_GET_POOL_COUNT
] = {
186 .cmdid_value
= 0x16A0,
187 .cmdid_mask
= 0xFFF0,
191 [DPRC_GET_CONNECTION
] = {
192 .cmdid_value
= 0x16C0,
193 .cmdid_mask
= 0xFFF0,
198 [DPCI_GET_LINK_STATE
] = {
199 .cmdid_value
= 0x0E10,
200 .cmdid_mask
= 0xFFF0,
204 [DPCI_GET_PEER_ATTR
] = {
205 .cmdid_value
= 0x0E20,
206 .cmdid_mask
= 0xFFF0,
210 [DPAIOP_GET_SL_VERSION
] = {
211 .cmdid_value
= 0x2820,
212 .cmdid_mask
= 0xFFF0,
216 [DPAIOP_GET_STATE
] = {
217 .cmdid_value
= 0x2830,
218 .cmdid_mask
= 0xFFF0,
222 [DPMNG_GET_VERSION
] = {
223 .cmdid_value
= 0x8310,
224 .cmdid_mask
= 0xFFF0,
228 [DPSECI_GET_TX_QUEUE
] = {
229 .cmdid_value
= 0x1970,
230 .cmdid_mask
= 0xFFF0,
234 [DPMAC_GET_COUNTER
] = {
235 .cmdid_value
= 0x0c40,
236 .cmdid_mask
= 0xFFF0,
240 [DPMAC_GET_MAC_ADDR
] = {
241 .cmdid_value
= 0x0c50,
242 .cmdid_mask
= 0xFFF0,
246 [DPNI_SET_PRIM_MAC
] = {
247 .cmdid_value
= 0x2240,
248 .cmdid_mask
= 0xFFF0,
251 .flags
= FSL_MC_CAP_NET_ADMIN_NEEDED
,
253 [DPNI_GET_PRIM_MAC
] = {
254 .cmdid_value
= 0x2250,
255 .cmdid_mask
= 0xFFF0,
259 [DPNI_GET_STATISTICS
] = {
260 .cmdid_value
= 0x25D0,
261 .cmdid_mask
= 0xFFF0,
265 [DPNI_GET_LINK_STATE
] = {
266 .cmdid_value
= 0x2150,
267 .cmdid_mask
= 0xFFF0,
271 [DPNI_GET_MAX_FRAME_LENGTH
] = {
272 .cmdid_value
= 0x2170,
273 .cmdid_mask
= 0xFFF0,
277 [DPSW_GET_TAILDROP
] = {
278 .cmdid_value
= 0x0A80,
279 .cmdid_mask
= 0xFFF0,
283 [DPSW_SET_TAILDROP
] = {
284 .cmdid_value
= 0x0A90,
285 .cmdid_mask
= 0xFFF0,
288 .flags
= FSL_MC_CAP_NET_ADMIN_NEEDED
,
290 [DPSW_IF_GET_COUNTER
] = {
291 .cmdid_value
= 0x0340,
292 .cmdid_mask
= 0xFFF0,
296 [DPSW_IF_GET_MAX_FRAME_LENGTH
] = {
297 .cmdid_value
= 0x0450,
298 .cmdid_mask
= 0xFFF0,
302 [DPDMUX_GET_COUNTER
] = {
303 .cmdid_value
= 0x0b20,
304 .cmdid_mask
= 0xFFF0,
308 [DPDMUX_IF_GET_MAX_FRAME_LENGTH
] = {
309 .cmdid_value
= 0x0a20,
310 .cmdid_mask
= 0xFFF0,
315 .cmdid_value
= 0x0040,
316 .cmdid_mask
= 0xFFF0,
321 .cmdid_value
= 0x0150,
322 .cmdid_mask
= 0xFFF0,
327 .cmdid_value
= 0x0160,
328 .cmdid_mask
= 0xFFF0,
333 .cmdid_value
= 0x8000,
334 .cmdid_mask
= 0xFFF0,
339 /* Common commands amongst all types of objects. Must be checked last. */
341 .cmdid_value
= 0x8000,
342 .cmdid_mask
= 0xFC00,
345 .flags
= FSL_MC_CHECK_MODULE_ID
,
347 [GET_API_VERSION
] = {
348 .cmdid_value
= 0xA000,
349 .cmdid_mask
= 0xFC00,
352 .flags
= FSL_MC_CHECK_MODULE_ID
,
355 .cmdid_value
= 0x9800,
356 .cmdid_mask
= 0xFC00,
359 .flags
= FSL_MC_CHECK_MODULE_ID
| FSL_MC_CAP_NET_ADMIN_NEEDED
,
362 .cmdid_value
= 0x9000,
363 .cmdid_mask
= 0xFC00,
366 .flags
= FSL_MC_CHECK_MODULE_ID
| FSL_MC_CAP_NET_ADMIN_NEEDED
,
370 #define FSL_MC_NUM_ACCEPTED_CMDS ARRAY_SIZE(fsl_mc_accepted_cmds)
372 #define FSL_MC_MAX_MODULE_ID 0x10
374 static int fsl_mc_command_check(struct fsl_mc_device
*mc_dev
,
375 struct fsl_mc_command
*mc_cmd
)
377 struct fsl_mc_cmd_desc
*desc
= NULL
;
378 int mc_cmd_max_size
, i
;
380 u16 cmdid
, module_id
;
384 /* Check if this is an accepted MC command */
385 cmdid
= mc_cmd_hdr_read_cmdid(mc_cmd
);
386 for (i
= 0; i
< FSL_MC_NUM_ACCEPTED_CMDS
; i
++) {
387 desc
= &fsl_mc_accepted_cmds
[i
];
388 if ((cmdid
& desc
->cmdid_mask
) == desc
->cmdid_value
)
391 if (i
== FSL_MC_NUM_ACCEPTED_CMDS
) {
392 dev_err(&mc_dev
->dev
, "MC command 0x%04x: cmdid not accepted\n", cmdid
);
396 /* Check if the size of the command is honored. Anything beyond the
397 * last valid byte of the command should be zeroed.
399 mc_cmd_max_size
= sizeof(*mc_cmd
);
400 mc_cmd_end
= ((char *)mc_cmd
) + desc
->size
;
401 for (i
= desc
->size
; i
< mc_cmd_max_size
; i
++)
402 sum
|= *mc_cmd_end
++;
404 dev_err(&mc_dev
->dev
, "MC command 0x%04x: garbage beyond max size of %d bytes!\n",
409 /* Some MC commands request a token to be passed so that object
410 * identification is possible. Check if the token passed in the command
413 token_provided
= mc_cmd_hdr_read_token(mc_cmd
) ? true : false;
414 if (token_provided
!= desc
->token
) {
415 dev_err(&mc_dev
->dev
, "MC command 0x%04x: token 0x%04x is invalid!\n",
416 cmdid
, mc_cmd_hdr_read_token(mc_cmd
));
420 /* If needed, check if the module ID passed is valid */
421 if (desc
->flags
& FSL_MC_CHECK_MODULE_ID
) {
422 /* The module ID is represented by bits [4:9] from the cmdid */
423 module_id
= (cmdid
& GENMASK(9, 4)) >> 4;
424 if (module_id
== 0 || module_id
> FSL_MC_MAX_MODULE_ID
) {
425 dev_err(&mc_dev
->dev
, "MC command 0x%04x: unknown module ID 0x%x\n",
431 /* Some commands alter how hardware resources are managed. For these
432 * commands, check for CAP_NET_ADMIN.
434 if (desc
->flags
& FSL_MC_CAP_NET_ADMIN_NEEDED
) {
435 if (!capable(CAP_NET_ADMIN
)) {
436 dev_err(&mc_dev
->dev
, "MC command 0x%04x: needs CAP_NET_ADMIN!\n",
445 static int fsl_mc_uapi_send_command(struct fsl_mc_device
*mc_dev
, unsigned long arg
,
446 struct fsl_mc_io
*mc_io
)
448 struct fsl_mc_command mc_cmd
;
451 error
= copy_from_user(&mc_cmd
, (void __user
*)arg
, sizeof(mc_cmd
));
455 error
= fsl_mc_command_check(mc_dev
, &mc_cmd
);
459 error
= mc_send_command(mc_io
, &mc_cmd
);
463 error
= copy_to_user((void __user
*)arg
, &mc_cmd
, sizeof(mc_cmd
));
470 static int fsl_mc_uapi_dev_open(struct inode
*inode
, struct file
*filep
)
472 struct fsl_mc_device
*root_mc_device
;
473 struct uapi_priv_data
*priv_data
;
474 struct fsl_mc_io
*dynamic_mc_io
;
475 struct fsl_mc_uapi
*mc_uapi
;
476 struct fsl_mc_bus
*mc_bus
;
479 priv_data
= kzalloc(sizeof(*priv_data
), GFP_KERNEL
);
483 mc_uapi
= container_of(filep
->private_data
, struct fsl_mc_uapi
, misc
);
484 mc_bus
= container_of(mc_uapi
, struct fsl_mc_bus
, uapi_misc
);
485 root_mc_device
= &mc_bus
->mc_dev
;
487 mutex_lock(&mc_uapi
->mutex
);
489 if (!mc_uapi
->local_instance_in_use
) {
490 priv_data
->mc_io
= mc_uapi
->static_mc_io
;
491 mc_uapi
->local_instance_in_use
= 1;
493 error
= fsl_mc_portal_allocate(root_mc_device
, 0,
496 dev_dbg(&root_mc_device
->dev
,
497 "Could not allocate MC portal\n");
498 goto error_portal_allocate
;
501 priv_data
->mc_io
= dynamic_mc_io
;
503 priv_data
->uapi
= mc_uapi
;
504 filep
->private_data
= priv_data
;
506 mutex_unlock(&mc_uapi
->mutex
);
510 error_portal_allocate
:
511 mutex_unlock(&mc_uapi
->mutex
);
517 static int fsl_mc_uapi_dev_release(struct inode
*inode
, struct file
*filep
)
519 struct uapi_priv_data
*priv_data
;
520 struct fsl_mc_uapi
*mc_uapi
;
521 struct fsl_mc_io
*mc_io
;
523 priv_data
= filep
->private_data
;
524 mc_uapi
= priv_data
->uapi
;
525 mc_io
= priv_data
->mc_io
;
527 mutex_lock(&mc_uapi
->mutex
);
529 if (mc_io
== mc_uapi
->static_mc_io
)
530 mc_uapi
->local_instance_in_use
= 0;
532 fsl_mc_portal_free(mc_io
);
534 kfree(filep
->private_data
);
535 filep
->private_data
= NULL
;
537 mutex_unlock(&mc_uapi
->mutex
);
542 static long fsl_mc_uapi_dev_ioctl(struct file
*file
,
546 struct uapi_priv_data
*priv_data
= file
->private_data
;
547 struct fsl_mc_device
*root_mc_device
;
548 struct fsl_mc_bus
*mc_bus
;
551 mc_bus
= container_of(priv_data
->uapi
, struct fsl_mc_bus
, uapi_misc
);
552 root_mc_device
= &mc_bus
->mc_dev
;
555 case FSL_MC_SEND_MC_COMMAND
:
556 error
= fsl_mc_uapi_send_command(root_mc_device
, arg
, priv_data
->mc_io
);
559 dev_dbg(&root_mc_device
->dev
, "unexpected ioctl call number\n");
566 static const struct file_operations fsl_mc_uapi_dev_fops
= {
567 .owner
= THIS_MODULE
,
568 .open
= fsl_mc_uapi_dev_open
,
569 .release
= fsl_mc_uapi_dev_release
,
570 .unlocked_ioctl
= fsl_mc_uapi_dev_ioctl
,
573 int fsl_mc_uapi_create_device_file(struct fsl_mc_bus
*mc_bus
)
575 struct fsl_mc_device
*mc_dev
= &mc_bus
->mc_dev
;
576 struct fsl_mc_uapi
*mc_uapi
= &mc_bus
->uapi_misc
;
579 mc_uapi
->misc
.minor
= MISC_DYNAMIC_MINOR
;
580 mc_uapi
->misc
.name
= dev_name(&mc_dev
->dev
);
581 mc_uapi
->misc
.fops
= &fsl_mc_uapi_dev_fops
;
583 error
= misc_register(&mc_uapi
->misc
);
587 mc_uapi
->static_mc_io
= mc_bus
->mc_dev
.mc_io
;
589 mutex_init(&mc_uapi
->mutex
);
594 void fsl_mc_uapi_remove_device_file(struct fsl_mc_bus
*mc_bus
)
596 misc_deregister(&mc_bus
->uapi_misc
.misc
);