b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame^] | 1 | From 5b5567547d0088ec96160634c4e342bb06e52f19 Mon Sep 17 00:00:00 2001 |
| 2 | From: Ioana Ciornei <ioana.ciornei@nxp.com> |
| 3 | Date: Wed, 14 Mar 2018 19:25:27 +0200 |
| 4 | Subject: [PATCH] bus: fsl-mc: add fsl-mc userspace support |
| 5 | |
| 6 | Adding userspace support for the MC (Management Complex) means exporting |
| 7 | an ioctl capable device file representing the root resource container. |
| 8 | |
| 9 | This new functionality in the fsl-mc bus driver intends to provide |
| 10 | userspace applications an interface to interact with the MC firmware. |
| 11 | |
| 12 | Commands that are composed in userspace are sent to the MC firmware |
| 13 | through the FSL_MC_SEND_MC_COMMAND ioctl. By default the implicit MC |
| 14 | I/O portal is used for this operation, but if the implicit one is busy, |
| 15 | a dynamic portal is allocated and then freed upon execution. |
| 16 | |
| 17 | Signed-off-by: Ioana Ciornei <ioana.ciornei@nxp.com> |
| 18 | --- |
| 19 | Documentation/ioctl/ioctl-number.rst | 1 + |
| 20 | drivers/bus/fsl-mc/Kconfig | 7 ++ |
| 21 | drivers/bus/fsl-mc/Makefile | 3 + |
| 22 | drivers/bus/fsl-mc/dprc-driver.c | 14 ++- |
| 23 | drivers/bus/fsl-mc/fsl-mc-private.h | 39 ++++++++ |
| 24 | drivers/bus/fsl-mc/fsl-mc-uapi.c | 168 +++++++++++++++++++++++++++++++++++ |
| 25 | include/uapi/linux/fsl_mc.h | 9 ++ |
| 26 | 7 files changed, 240 insertions(+), 1 deletion(-) |
| 27 | create mode 100644 drivers/bus/fsl-mc/fsl-mc-uapi.c |
| 28 | |
| 29 | --- a/Documentation/ioctl/ioctl-number.rst |
| 30 | +++ b/Documentation/ioctl/ioctl-number.rst |
| 31 | @@ -180,6 +180,7 @@ Code Seq# Include File |
| 32 | 'R' 00-1F linux/random.h conflict! |
| 33 | 'R' 01 linux/rfkill.h conflict! |
| 34 | 'R' C0-DF net/bluetooth/rfcomm.h |
| 35 | +'R' E0 uapi/linux/fsl_mc.h |
| 36 | 'S' all linux/cdrom.h conflict! |
| 37 | 'S' 80-81 scsi/scsi_ioctl.h conflict! |
| 38 | 'S' 82-FF scsi/scsi.h conflict! |
| 39 | --- a/drivers/bus/fsl-mc/Kconfig |
| 40 | +++ b/drivers/bus/fsl-mc/Kconfig |
| 41 | @@ -14,3 +14,10 @@ config FSL_MC_BUS |
| 42 | architecture. The fsl-mc bus driver handles discovery of |
| 43 | DPAA2 objects (which are represented as Linux devices) and |
| 44 | binding objects to drivers. |
| 45 | + |
| 46 | +config FSL_MC_UAPI_SUPPORT |
| 47 | + bool "Management Complex (MC) userspace support" |
| 48 | + depends on FSL_MC_BUS |
| 49 | + help |
| 50 | + Provides userspace support for creating/destroying/configuring |
| 51 | + DPAA2 objects in the Management Complex. |
| 52 | --- a/drivers/bus/fsl-mc/Makefile |
| 53 | +++ b/drivers/bus/fsl-mc/Makefile |
| 54 | @@ -16,3 +16,6 @@ mc-bus-driver-objs := fsl-mc-bus.o \ |
| 55 | fsl-mc-allocator.o \ |
| 56 | fsl-mc-msi.o \ |
| 57 | dpmcp.o |
| 58 | + |
| 59 | +# MC userspace support |
| 60 | +obj-$(CONFIG_FSL_MC_UAPI_SUPPORT) += fsl-mc-uapi.o |
| 61 | --- a/drivers/bus/fsl-mc/dprc-driver.c |
| 62 | +++ b/drivers/bus/fsl-mc/dprc-driver.c |
| 63 | @@ -647,6 +647,12 @@ static int dprc_probe(struct fsl_mc_devi |
| 64 | } else { |
| 65 | dev_set_msi_domain(&mc_dev->dev, mc_msi_domain); |
| 66 | msi_domain_set = true; |
| 67 | + |
| 68 | + error = fsl_mc_uapi_create_device_file(mc_bus); |
| 69 | + if (error < 0) { |
| 70 | + error = -EPROBE_DEFER; |
| 71 | + goto error_cleanup_msi_domain; |
| 72 | + } |
| 73 | } |
| 74 | } |
| 75 | |
| 76 | @@ -654,7 +660,7 @@ static int dprc_probe(struct fsl_mc_devi |
| 77 | &mc_dev->mc_handle); |
| 78 | if (error < 0) { |
| 79 | dev_err(&mc_dev->dev, "dprc_open() failed: %d\n", error); |
| 80 | - goto error_cleanup_msi_domain; |
| 81 | + goto error_cleanup_uapi; |
| 82 | } |
| 83 | |
| 84 | error = dprc_get_attributes(mc_dev->mc_io, 0, mc_dev->mc_handle, |
| 85 | @@ -706,6 +712,10 @@ static int dprc_probe(struct fsl_mc_devi |
| 86 | error_cleanup_open: |
| 87 | (void)dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle); |
| 88 | |
| 89 | +error_cleanup_uapi: |
| 90 | + if (fsl_mc_is_root_dprc(&mc_dev->dev)) |
| 91 | + fsl_mc_uapi_remove_device_file(mc_bus); |
| 92 | + |
| 93 | error_cleanup_msi_domain: |
| 94 | if (msi_domain_set) |
| 95 | dev_set_msi_domain(&mc_dev->dev, NULL); |
| 96 | @@ -774,6 +784,8 @@ static int dprc_remove(struct fsl_mc_dev |
| 97 | if (!fsl_mc_is_root_dprc(&mc_dev->dev)) { |
| 98 | fsl_destroy_mc_io(mc_dev->mc_io); |
| 99 | mc_dev->mc_io = NULL; |
| 100 | + } else { |
| 101 | + fsl_mc_uapi_remove_device_file(mc_bus); |
| 102 | } |
| 103 | |
| 104 | dev_info(&mc_dev->dev, "DPRC device unbound from driver"); |
| 105 | --- a/drivers/bus/fsl-mc/fsl-mc-private.h |
| 106 | +++ b/drivers/bus/fsl-mc/fsl-mc-private.h |
| 107 | @@ -10,6 +10,8 @@ |
| 108 | |
| 109 | #include <linux/fsl/mc.h> |
| 110 | #include <linux/mutex.h> |
| 111 | +#include <linux/ioctl.h> |
| 112 | +#include <linux/miscdevice.h> |
| 113 | |
| 114 | /* |
| 115 | * Data Path Management Complex (DPMNG) General API |
| 116 | @@ -505,6 +507,22 @@ struct fsl_mc_resource_pool { |
| 117 | }; |
| 118 | |
| 119 | /** |
| 120 | + * struct fsl_mc_uapi - information associated with a device file |
| 121 | + * @misc: struct miscdevice linked to the root dprc |
| 122 | + * @device: newly created device in /dev |
| 123 | + * @mutex: mutex lock to serialize the open/release operations |
| 124 | + * @local_instance_in_use: local MC I/O instance in use or not |
| 125 | + * @static_mc_io: pointer to the static MC I/O object |
| 126 | + */ |
| 127 | +struct fsl_mc_uapi { |
| 128 | + struct miscdevice misc; |
| 129 | + struct device *device; |
| 130 | + struct mutex mutex; /* serialize open/release operations */ |
| 131 | + u32 local_instance_in_use; |
| 132 | + struct fsl_mc_io *static_mc_io; |
| 133 | +}; |
| 134 | + |
| 135 | +/** |
| 136 | * struct fsl_mc_bus - logical bus that corresponds to a physical DPRC |
| 137 | * @mc_dev: fsl-mc device for the bus device itself. |
| 138 | * @resource_pools: array of resource pools (one pool per resource type) |
| 139 | @@ -513,6 +531,7 @@ struct fsl_mc_resource_pool { |
| 140 | * @irq_resources: Pointer to array of IRQ objects for the IRQ pool |
| 141 | * @scan_mutex: Serializes bus scanning |
| 142 | * @dprc_attr: DPRC attributes |
| 143 | + * @uapi_misc: struct that abstracts the interaction with userspace |
| 144 | */ |
| 145 | struct fsl_mc_bus { |
| 146 | struct fsl_mc_device mc_dev; |
| 147 | @@ -520,6 +539,7 @@ struct fsl_mc_bus { |
| 148 | struct fsl_mc_device_irq *irq_resources; |
| 149 | struct mutex scan_mutex; /* serializes bus scanning */ |
| 150 | struct dprc_attributes dprc_attr; |
| 151 | + struct fsl_mc_uapi uapi_misc; |
| 152 | }; |
| 153 | |
| 154 | #define to_fsl_mc_bus(_mc_dev) \ |
| 155 | @@ -574,4 +594,23 @@ void fsl_destroy_mc_io(struct fsl_mc_io |
| 156 | |
| 157 | bool fsl_mc_is_root_dprc(struct device *dev); |
| 158 | |
| 159 | +#ifdef CONFIG_FSL_MC_UAPI_SUPPORT |
| 160 | + |
| 161 | +int fsl_mc_uapi_create_device_file(struct fsl_mc_bus *mc_bus); |
| 162 | + |
| 163 | +void fsl_mc_uapi_remove_device_file(struct fsl_mc_bus *mc_bus); |
| 164 | + |
| 165 | +#else |
| 166 | + |
| 167 | +static inline int fsl_mc_uapi_create_device_file(struct fsl_mc_bus *mc_bus) |
| 168 | +{ |
| 169 | + return 0; |
| 170 | +} |
| 171 | + |
| 172 | +static inline void fsl_mc_uapi_remove_device_file(struct fsl_mc_bus *mc_bus) |
| 173 | +{ |
| 174 | +} |
| 175 | + |
| 176 | +#endif |
| 177 | + |
| 178 | #endif /* _FSL_MC_PRIVATE_H_ */ |
| 179 | --- /dev/null |
| 180 | +++ b/drivers/bus/fsl-mc/fsl-mc-uapi.c |
| 181 | @@ -0,0 +1,168 @@ |
| 182 | +// SPDX-License-Identifier: GPL-2.0 |
| 183 | +/* |
| 184 | + * Management Complex (MC) userspace support |
| 185 | + * |
| 186 | + * Copyright 2018 NXP |
| 187 | + * |
| 188 | + */ |
| 189 | + |
| 190 | +#include <linux/slab.h> |
| 191 | +#include <linux/fs.h> |
| 192 | +#include <linux/uaccess.h> |
| 193 | +#include <linux/miscdevice.h> |
| 194 | + |
| 195 | +#include "fsl-mc-private.h" |
| 196 | + |
| 197 | +struct uapi_priv_data { |
| 198 | + struct fsl_mc_uapi *uapi; |
| 199 | + struct fsl_mc_io *mc_io; |
| 200 | +}; |
| 201 | + |
| 202 | +static int fsl_mc_uapi_send_command(unsigned long arg, |
| 203 | + struct fsl_mc_io *mc_io) |
| 204 | +{ |
| 205 | + struct fsl_mc_command mc_cmd; |
| 206 | + int error; |
| 207 | + |
| 208 | + error = copy_from_user(&mc_cmd, (void __user *)arg, sizeof(mc_cmd)); |
| 209 | + if (error) |
| 210 | + return -EFAULT; |
| 211 | + |
| 212 | + error = mc_send_command(mc_io, &mc_cmd); |
| 213 | + if (error) |
| 214 | + return error; |
| 215 | + |
| 216 | + error = copy_to_user((void __user *)arg, &mc_cmd, sizeof(mc_cmd)); |
| 217 | + if (error) |
| 218 | + return -EFAULT; |
| 219 | + |
| 220 | + return 0; |
| 221 | +} |
| 222 | + |
| 223 | +static int fsl_mc_uapi_dev_open(struct inode *inode, struct file *filep) |
| 224 | +{ |
| 225 | + struct fsl_mc_device *root_mc_device; |
| 226 | + struct uapi_priv_data *priv_data; |
| 227 | + struct fsl_mc_io *dynamic_mc_io; |
| 228 | + struct fsl_mc_uapi *mc_uapi; |
| 229 | + struct fsl_mc_bus *mc_bus; |
| 230 | + int error; |
| 231 | + |
| 232 | + priv_data = kzalloc(sizeof(*priv_data), GFP_KERNEL); |
| 233 | + if (!priv_data) |
| 234 | + return -ENOMEM; |
| 235 | + |
| 236 | + mc_uapi = container_of(filep->private_data, struct fsl_mc_uapi, misc); |
| 237 | + mc_bus = container_of(mc_uapi, struct fsl_mc_bus, uapi_misc); |
| 238 | + root_mc_device = &mc_bus->mc_dev; |
| 239 | + |
| 240 | + mutex_lock(&mc_uapi->mutex); |
| 241 | + |
| 242 | + if (!mc_uapi->local_instance_in_use) { |
| 243 | + priv_data->mc_io = mc_uapi->static_mc_io; |
| 244 | + mc_uapi->local_instance_in_use = 1; |
| 245 | + } else { |
| 246 | + error = fsl_mc_portal_allocate(root_mc_device, 0, |
| 247 | + &dynamic_mc_io); |
| 248 | + if (error) { |
| 249 | + dev_dbg(&root_mc_device->dev, |
| 250 | + "Could not allocate MC portal\n"); |
| 251 | + goto error_portal_allocate; |
| 252 | + } |
| 253 | + |
| 254 | + priv_data->mc_io = dynamic_mc_io; |
| 255 | + } |
| 256 | + priv_data->uapi = mc_uapi; |
| 257 | + filep->private_data = priv_data; |
| 258 | + |
| 259 | + mutex_unlock(&mc_uapi->mutex); |
| 260 | + |
| 261 | + return 0; |
| 262 | + |
| 263 | +error_portal_allocate: |
| 264 | + mutex_unlock(&mc_uapi->mutex); |
| 265 | + |
| 266 | + return error; |
| 267 | +} |
| 268 | + |
| 269 | +static int fsl_mc_uapi_dev_release(struct inode *inode, struct file *filep) |
| 270 | +{ |
| 271 | + struct uapi_priv_data *priv_data; |
| 272 | + struct fsl_mc_uapi *mc_uapi; |
| 273 | + struct fsl_mc_io *mc_io; |
| 274 | + |
| 275 | + priv_data = filep->private_data; |
| 276 | + mc_uapi = priv_data->uapi; |
| 277 | + mc_io = priv_data->mc_io; |
| 278 | + |
| 279 | + mutex_lock(&mc_uapi->mutex); |
| 280 | + |
| 281 | + if (mc_io == mc_uapi->static_mc_io) |
| 282 | + mc_uapi->local_instance_in_use = 0; |
| 283 | + else |
| 284 | + fsl_mc_portal_free(mc_io); |
| 285 | + |
| 286 | + kfree(filep->private_data); |
| 287 | + filep->private_data = NULL; |
| 288 | + |
| 289 | + mutex_unlock(&mc_uapi->mutex); |
| 290 | + |
| 291 | + return 0; |
| 292 | +} |
| 293 | + |
| 294 | +static long fsl_mc_uapi_dev_ioctl(struct file *file, |
| 295 | + unsigned int cmd, |
| 296 | + unsigned long arg) |
| 297 | +{ |
| 298 | + struct uapi_priv_data *priv_data = file->private_data; |
| 299 | + struct fsl_mc_device *root_mc_device; |
| 300 | + struct fsl_mc_bus *mc_bus; |
| 301 | + int error; |
| 302 | + |
| 303 | + mc_bus = container_of(priv_data->uapi, struct fsl_mc_bus, uapi_misc); |
| 304 | + root_mc_device = &mc_bus->mc_dev; |
| 305 | + |
| 306 | + switch (cmd) { |
| 307 | + case FSL_MC_SEND_MC_COMMAND: |
| 308 | + error = fsl_mc_uapi_send_command(arg, priv_data->mc_io); |
| 309 | + break; |
| 310 | + default: |
| 311 | + dev_dbg(&root_mc_device->dev, "unexpected ioctl call number\n"); |
| 312 | + error = -EINVAL; |
| 313 | + } |
| 314 | + |
| 315 | + return error; |
| 316 | +} |
| 317 | + |
| 318 | +static const struct file_operations fsl_mc_uapi_dev_fops = { |
| 319 | + .owner = THIS_MODULE, |
| 320 | + .open = fsl_mc_uapi_dev_open, |
| 321 | + .release = fsl_mc_uapi_dev_release, |
| 322 | + .unlocked_ioctl = fsl_mc_uapi_dev_ioctl, |
| 323 | +}; |
| 324 | + |
| 325 | +int fsl_mc_uapi_create_device_file(struct fsl_mc_bus *mc_bus) |
| 326 | +{ |
| 327 | + struct fsl_mc_device *mc_dev = &mc_bus->mc_dev; |
| 328 | + struct fsl_mc_uapi *mc_uapi = &mc_bus->uapi_misc; |
| 329 | + int error; |
| 330 | + |
| 331 | + mc_uapi->misc.minor = MISC_DYNAMIC_MINOR; |
| 332 | + mc_uapi->misc.name = dev_name(&mc_dev->dev); |
| 333 | + mc_uapi->misc.fops = &fsl_mc_uapi_dev_fops; |
| 334 | + |
| 335 | + error = misc_register(&mc_uapi->misc); |
| 336 | + if (error) |
| 337 | + return error; |
| 338 | + |
| 339 | + mc_uapi->static_mc_io = mc_bus->mc_dev.mc_io; |
| 340 | + |
| 341 | + mutex_init(&mc_uapi->mutex); |
| 342 | + |
| 343 | + return 0; |
| 344 | +} |
| 345 | + |
| 346 | +void fsl_mc_uapi_remove_device_file(struct fsl_mc_bus *mc_bus) |
| 347 | +{ |
| 348 | + misc_deregister(&mc_bus->uapi_misc.misc); |
| 349 | +} |
| 350 | --- a/include/uapi/linux/fsl_mc.h |
| 351 | +++ b/include/uapi/linux/fsl_mc.h |
| 352 | @@ -16,10 +16,19 @@ |
| 353 | * struct fsl_mc_command - Management Complex (MC) command structure |
| 354 | * @header: MC command header |
| 355 | * @params: MC command parameters |
| 356 | + * |
| 357 | + * Used by FSL_MC_SEND_MC_COMMAND |
| 358 | */ |
| 359 | struct fsl_mc_command { |
| 360 | __le64 header; |
| 361 | __le64 params[MC_CMD_NUM_OF_PARAMS]; |
| 362 | }; |
| 363 | |
| 364 | +#define FSL_MC_SEND_CMD_IOCTL_TYPE 'R' |
| 365 | +#define FSL_MC_SEND_CMD_IOCTL_SEQ 0xE0 |
| 366 | + |
| 367 | +#define FSL_MC_SEND_MC_COMMAND \ |
| 368 | + _IOWR(FSL_MC_SEND_CMD_IOCTL_TYPE, FSL_MC_SEND_CMD_IOCTL_SEQ, \ |
| 369 | + struct fsl_mc_command) |
| 370 | + |
| 371 | #endif /* _UAPI_FSL_MC_H_ */ |