You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
490 lines
14 KiB
Diff
490 lines
14 KiB
Diff
From fb4881d149742e4c5595aca8bf86c99d2ea155ad Mon Sep 17 00:00:00 2001
|
|
From: Lijun Pan <Lijun.Pan@freescale.com>
|
|
Date: Mon, 8 Feb 2016 17:40:18 -0600
|
|
Subject: [PATCH 197/226] staging: fsl-mc: Management Complex restool driver
|
|
|
|
The kernel support for the restool (a user space resource management
|
|
tool) is a driver for the /dev/dprc.N device file.
|
|
Its purpose is to provide an ioctl interface,
|
|
which the restool uses to interact with the MC bus driver
|
|
and with the MC firmware.
|
|
We allocate a dpmcp at driver initialization,
|
|
and keep that dpmcp until driver exit.
|
|
We use that dpmcp by default.
|
|
If that dpmcp is in use, we create another portal at run time
|
|
and destroy the newly created portal after use.
|
|
The ioctl RESTOOL_SEND_MC_COMMAND sends user space command to fsl-mc
|
|
bus and utilizes the fsl-mc bus to communicate with MC firmware.
|
|
The ioctl RESTOOL_DPRC_SYNC request the mc-bus launch
|
|
objects scan under root dprc.
|
|
In order to support multiple root dprc, we utilize the bus notify
|
|
mechanism to scan fsl_mc_bus_type for the newly added root dprc.
|
|
After discovering the root dprc, it creates a miscdevice
|
|
/dev/dprc.N to associate with this root dprc.
|
|
|
|
Signed-off-by: Lijun Pan <Lijun.Pan@freescale.com>
|
|
[Stuart: minor fix to resolve compile error]
|
|
Signed-off-by: Stuart Yoder <stuart.yoder@nxp.com>
|
|
---
|
|
Documentation/ioctl/ioctl-number.txt | 1 +
|
|
drivers/staging/fsl-mc/bus/Kconfig | 7 +-
|
|
drivers/staging/fsl-mc/bus/Makefile | 3 +
|
|
drivers/staging/fsl-mc/bus/mc-ioctl.h | 22 ++
|
|
drivers/staging/fsl-mc/bus/mc-restool.c | 392 +++++++++++++++++++++++++++++++
|
|
5 files changed, 424 insertions(+), 1 deletion(-)
|
|
create mode 100644 drivers/staging/fsl-mc/bus/mc-ioctl.h
|
|
create mode 100644 drivers/staging/fsl-mc/bus/mc-restool.c
|
|
|
|
--- a/Documentation/ioctl/ioctl-number.txt
|
|
+++ b/Documentation/ioctl/ioctl-number.txt
|
|
@@ -170,6 +170,7 @@ Code Seq#(hex) Include File Comments
|
|
'R' 00-1F linux/random.h conflict!
|
|
'R' 01 linux/rfkill.h conflict!
|
|
'R' C0-DF net/bluetooth/rfcomm.h
|
|
+'R' E0-EF drivers/staging/fsl-mc/bus/mc-ioctl.h
|
|
'S' all linux/cdrom.h conflict!
|
|
'S' 80-81 scsi/scsi_ioctl.h conflict!
|
|
'S' 82-FF scsi/scsi.h conflict!
|
|
--- a/drivers/staging/fsl-mc/bus/Kconfig
|
|
+++ b/drivers/staging/fsl-mc/bus/Kconfig
|
|
@@ -22,4 +22,9 @@ config FSL_MC_BUS
|
|
Only enable this option when building the kernel for
|
|
Freescale QorQIQ LS2xxxx SoCs.
|
|
|
|
-
|
|
+config FSL_MC_RESTOOL
|
|
+ tristate "Freescale Management Complex (MC) restool driver"
|
|
+ depends on FSL_MC_BUS
|
|
+ help
|
|
+ Driver that provides kernel support for the Freescale Management
|
|
+ Complex resource manager user-space tool.
|
|
--- a/drivers/staging/fsl-mc/bus/Makefile
|
|
+++ b/drivers/staging/fsl-mc/bus/Makefile
|
|
@@ -18,3 +18,6 @@ mc-bus-driver-objs := mc-bus.o \
|
|
dpmcp.o \
|
|
dpbp.o \
|
|
dpcon.o
|
|
+
|
|
+# MC restool kernel support
|
|
+obj-$(CONFIG_FSL_MC_RESTOOL) += mc-restool.o
|
|
--- /dev/null
|
|
+++ b/drivers/staging/fsl-mc/bus/mc-ioctl.h
|
|
@@ -0,0 +1,22 @@
|
|
+/*
|
|
+ * Freescale Management Complex (MC) ioclt interface
|
|
+ *
|
|
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
|
|
+ * Author: Lijun Pan <Lijun.Pan@freescale.com>
|
|
+ *
|
|
+ * This file is licensed under the terms of the GNU General Public
|
|
+ * License version 2. This program is licensed "as is" without any
|
|
+ * warranty of any kind, whether express or implied.
|
|
+ */
|
|
+#ifndef _FSL_MC_IOCTL_H_
|
|
+#define _FSL_MC_IOCTL_H_
|
|
+
|
|
+#include <linux/ioctl.h>
|
|
+#include "../include/mc-sys.h"
|
|
+
|
|
+#define RESTOOL_IOCTL_TYPE 'R'
|
|
+
|
|
+#define RESTOOL_SEND_MC_COMMAND \
|
|
+ _IOWR(RESTOOL_IOCTL_TYPE, 0xE0, struct mc_command)
|
|
+
|
|
+#endif /* _FSL_MC_IOCTL_H_ */
|
|
--- /dev/null
|
|
+++ b/drivers/staging/fsl-mc/bus/mc-restool.c
|
|
@@ -0,0 +1,392 @@
|
|
+/*
|
|
+ * Freescale Management Complex (MC) restool driver
|
|
+ *
|
|
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
|
|
+ * Author: Lijun Pan <Lijun.Pan@freescale.com>
|
|
+ *
|
|
+ * This file is licensed under the terms of the GNU General Public
|
|
+ * License version 2. This program is licensed "as is" without any
|
|
+ * warranty of any kind, whether express or implied.
|
|
+ */
|
|
+
|
|
+#include "../include/mc-private.h"
|
|
+#include <linux/module.h>
|
|
+#include <linux/fs.h>
|
|
+#include <linux/miscdevice.h>
|
|
+#include <linux/mm.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/uaccess.h>
|
|
+#include <linux/mutex.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include "mc-ioctl.h"
|
|
+#include "../include/mc-sys.h"
|
|
+#include "../include/mc-cmd.h"
|
|
+#include "../include/dpmng.h"
|
|
+
|
|
+/**
|
|
+ * Maximum number of DPRCs that can be opened at the same time
|
|
+ */
|
|
+#define MAX_DPRC_HANDLES 64
|
|
+
|
|
+/**
|
|
+ * restool_misc - information associated with the newly added miscdevice
|
|
+ * @misc: newly created miscdevice associated with root dprc
|
|
+ * @miscdevt: device id of this miscdevice
|
|
+ * @list: a linked list node representing this miscdevcie
|
|
+ * @static_mc_io: pointer to the static MC I/O object used by the restool
|
|
+ * @dynamic_instance_count: number of dynamically created instances
|
|
+ * @static_instance_in_use: static instance is in use or not
|
|
+ * @mutex: mutex lock to serialze the open/release operations
|
|
+ * @dev: root dprc associated with this miscdevice
|
|
+ */
|
|
+struct restool_misc {
|
|
+ struct miscdevice misc;
|
|
+ dev_t miscdevt;
|
|
+ struct list_head list;
|
|
+ struct fsl_mc_io *static_mc_io;
|
|
+ u32 dynamic_instance_count;
|
|
+ bool static_instance_in_use;
|
|
+ struct mutex mutex; /* serialze the open/release operations */
|
|
+ struct device *dev;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * initialize a global list to link all
|
|
+ * the miscdevice nodes (struct restool_misc)
|
|
+ */
|
|
+static LIST_HEAD(misc_list);
|
|
+static DEFINE_MUTEX(misc_list_mutex);
|
|
+
|
|
+static int fsl_mc_restool_dev_open(struct inode *inode, struct file *filep)
|
|
+{
|
|
+ struct fsl_mc_device *root_mc_dev;
|
|
+ int error;
|
|
+ struct fsl_mc_io *dynamic_mc_io = NULL;
|
|
+ struct restool_misc *restool_misc = NULL;
|
|
+ struct restool_misc *restool_misc_cursor;
|
|
+
|
|
+ mutex_lock(&misc_list_mutex);
|
|
+
|
|
+ list_for_each_entry(restool_misc_cursor, &misc_list, list) {
|
|
+ if (restool_misc_cursor->miscdevt == inode->i_rdev) {
|
|
+ restool_misc = restool_misc_cursor;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ mutex_unlock(&misc_list_mutex);
|
|
+
|
|
+ if (!restool_misc)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (WARN_ON(!restool_misc->dev))
|
|
+ return -EINVAL;
|
|
+
|
|
+ mutex_lock(&restool_misc->mutex);
|
|
+
|
|
+ if (!restool_misc->static_instance_in_use) {
|
|
+ restool_misc->static_instance_in_use = true;
|
|
+ filep->private_data = restool_misc->static_mc_io;
|
|
+ } else {
|
|
+ dynamic_mc_io = kzalloc(sizeof(*dynamic_mc_io), GFP_KERNEL);
|
|
+ if (!dynamic_mc_io) {
|
|
+ error = -ENOMEM;
|
|
+ goto err_unlock;
|
|
+ }
|
|
+
|
|
+ root_mc_dev = to_fsl_mc_device(restool_misc->dev);
|
|
+ error = fsl_mc_portal_allocate(root_mc_dev, 0, &dynamic_mc_io);
|
|
+ if (error < 0) {
|
|
+ pr_err("Not able to allocate MC portal\n");
|
|
+ goto free_dynamic_mc_io;
|
|
+ }
|
|
+ ++restool_misc->dynamic_instance_count;
|
|
+ filep->private_data = dynamic_mc_io;
|
|
+ }
|
|
+
|
|
+ mutex_unlock(&restool_misc->mutex);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+free_dynamic_mc_io:
|
|
+ kfree(dynamic_mc_io);
|
|
+err_unlock:
|
|
+ mutex_unlock(&restool_misc->mutex);
|
|
+
|
|
+ return error;
|
|
+}
|
|
+
|
|
+static int fsl_mc_restool_dev_release(struct inode *inode, struct file *filep)
|
|
+{
|
|
+ struct fsl_mc_io *local_mc_io = filep->private_data;
|
|
+ struct restool_misc *restool_misc = NULL;
|
|
+ struct restool_misc *restool_misc_cursor;
|
|
+
|
|
+ if (WARN_ON(!filep->private_data))
|
|
+ return -EINVAL;
|
|
+
|
|
+ mutex_lock(&misc_list_mutex);
|
|
+
|
|
+ list_for_each_entry(restool_misc_cursor, &misc_list, list) {
|
|
+ if (restool_misc_cursor->miscdevt == inode->i_rdev) {
|
|
+ restool_misc = restool_misc_cursor;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ mutex_unlock(&misc_list_mutex);
|
|
+
|
|
+ if (!restool_misc)
|
|
+ return -EINVAL;
|
|
+
|
|
+ mutex_lock(&restool_misc->mutex);
|
|
+
|
|
+ if (WARN_ON(restool_misc->dynamic_instance_count == 0 &&
|
|
+ !restool_misc->static_instance_in_use)) {
|
|
+ mutex_unlock(&restool_misc->mutex);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /* Globally clean up opened/untracked handles */
|
|
+ fsl_mc_portal_reset(local_mc_io);
|
|
+
|
|
+ /*
|
|
+ * must check
|
|
+ * whether local_mc_io is dynamic or static instance
|
|
+ * Otherwise it will free up the reserved portal by accident
|
|
+ * or even not free up the dynamic allocated portal
|
|
+ * if 2 or more instances running concurrently
|
|
+ */
|
|
+ if (local_mc_io == restool_misc->static_mc_io) {
|
|
+ restool_misc->static_instance_in_use = false;
|
|
+ } else {
|
|
+ fsl_mc_portal_free(local_mc_io);
|
|
+ kfree(filep->private_data);
|
|
+ --restool_misc->dynamic_instance_count;
|
|
+ }
|
|
+
|
|
+ filep->private_data = NULL;
|
|
+ mutex_unlock(&restool_misc->mutex);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int restool_send_mc_command(unsigned long arg,
|
|
+ struct fsl_mc_io *local_mc_io)
|
|
+{
|
|
+ int error;
|
|
+ struct mc_command mc_cmd;
|
|
+
|
|
+ if (copy_from_user(&mc_cmd, (void __user *)arg, sizeof(mc_cmd)))
|
|
+ return -EFAULT;
|
|
+
|
|
+ /*
|
|
+ * Send MC command to the MC:
|
|
+ */
|
|
+ error = mc_send_command(local_mc_io, &mc_cmd);
|
|
+ if (error < 0)
|
|
+ return error;
|
|
+
|
|
+ if (copy_to_user((void __user *)arg, &mc_cmd, sizeof(mc_cmd)))
|
|
+ return -EFAULT;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static long
|
|
+fsl_mc_restool_dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|
+{
|
|
+ int error;
|
|
+
|
|
+ switch (cmd) {
|
|
+ case RESTOOL_SEND_MC_COMMAND:
|
|
+ error = restool_send_mc_command(arg, file->private_data);
|
|
+ break;
|
|
+ default:
|
|
+ pr_err("%s: unexpected ioctl call number\n", __func__);
|
|
+ error = -EINVAL;
|
|
+ }
|
|
+
|
|
+ return error;
|
|
+}
|
|
+
|
|
+static const struct file_operations fsl_mc_restool_dev_fops = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .open = fsl_mc_restool_dev_open,
|
|
+ .release = fsl_mc_restool_dev_release,
|
|
+ .unlocked_ioctl = fsl_mc_restool_dev_ioctl,
|
|
+};
|
|
+
|
|
+static int restool_add_device_file(struct device *dev)
|
|
+{
|
|
+ u32 name1 = 0;
|
|
+ char name2[20] = {0};
|
|
+ int error;
|
|
+ struct fsl_mc_device *root_mc_dev;
|
|
+ struct restool_misc *restool_misc;
|
|
+
|
|
+ if (dev->bus == &platform_bus_type && dev->driver_data) {
|
|
+ if (sscanf(dev_name(dev), "%x.%s", &name1, name2) != 2)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (strcmp(name2, "fsl-mc") == 0)
|
|
+ pr_debug("platform's root dprc name is: %s\n",
|
|
+ dev_name(&(((struct fsl_mc *)
|
|
+ (dev->driver_data))->root_mc_bus_dev->dev)));
|
|
+ }
|
|
+
|
|
+ if (!fsl_mc_is_root_dprc(dev))
|
|
+ return 0;
|
|
+
|
|
+ restool_misc = kzalloc(sizeof(*restool_misc), GFP_KERNEL);
|
|
+ if (!restool_misc)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ restool_misc->dev = dev;
|
|
+ root_mc_dev = to_fsl_mc_device(dev);
|
|
+ error = fsl_mc_portal_allocate(root_mc_dev, 0,
|
|
+ &restool_misc->static_mc_io);
|
|
+ if (error < 0) {
|
|
+ pr_err("Not able to allocate MC portal\n");
|
|
+ goto free_restool_misc;
|
|
+ }
|
|
+
|
|
+ restool_misc->misc.minor = MISC_DYNAMIC_MINOR;
|
|
+ restool_misc->misc.name = dev_name(dev);
|
|
+ restool_misc->misc.fops = &fsl_mc_restool_dev_fops;
|
|
+
|
|
+ error = misc_register(&restool_misc->misc);
|
|
+ if (error < 0) {
|
|
+ pr_err("misc_register() failed: %d\n", error);
|
|
+ goto free_portal;
|
|
+ }
|
|
+
|
|
+ restool_misc->miscdevt = restool_misc->misc.this_device->devt;
|
|
+ mutex_init(&restool_misc->mutex);
|
|
+ mutex_lock(&misc_list_mutex);
|
|
+ list_add(&restool_misc->list, &misc_list);
|
|
+ mutex_unlock(&misc_list_mutex);
|
|
+
|
|
+ pr_info("/dev/%s driver registered\n", dev_name(dev));
|
|
+
|
|
+ return 0;
|
|
+
|
|
+free_portal:
|
|
+ fsl_mc_portal_free(restool_misc->static_mc_io);
|
|
+free_restool_misc:
|
|
+ kfree(restool_misc);
|
|
+
|
|
+ return error;
|
|
+}
|
|
+
|
|
+static int restool_bus_notifier(struct notifier_block *nb,
|
|
+ unsigned long action, void *data)
|
|
+{
|
|
+ int error;
|
|
+ struct device *dev = data;
|
|
+
|
|
+ switch (action) {
|
|
+ case BUS_NOTIFY_ADD_DEVICE:
|
|
+ error = restool_add_device_file(dev);
|
|
+ if (error)
|
|
+ return error;
|
|
+ break;
|
|
+ case BUS_NOTIFY_DEL_DEVICE:
|
|
+ case BUS_NOTIFY_REMOVED_DEVICE:
|
|
+ case BUS_NOTIFY_BIND_DRIVER:
|
|
+ case BUS_NOTIFY_BOUND_DRIVER:
|
|
+ case BUS_NOTIFY_UNBIND_DRIVER:
|
|
+ case BUS_NOTIFY_UNBOUND_DRIVER:
|
|
+ break;
|
|
+ default:
|
|
+ pr_err("%s: unrecognized device action from %s\n", __func__,
|
|
+ dev_name(dev));
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int add_to_restool(struct device *dev, void *data)
|
|
+{
|
|
+ return restool_add_device_file(dev);
|
|
+}
|
|
+
|
|
+static int __init fsl_mc_restool_driver_init(void)
|
|
+{
|
|
+ int error;
|
|
+ struct notifier_block *nb;
|
|
+
|
|
+ nb = kzalloc(sizeof(*nb), GFP_KERNEL);
|
|
+ if (!nb)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ nb->notifier_call = restool_bus_notifier;
|
|
+ error = bus_register_notifier(&fsl_mc_bus_type, nb);
|
|
+ if (error)
|
|
+ goto free_nb;
|
|
+
|
|
+ /*
|
|
+ * This driver runs after fsl-mc bus driver runs.
|
|
+ * Hence, many of the root dprcs are already attached to fsl-mc bus
|
|
+ * In order to make sure we find all the root dprcs,
|
|
+ * we need to scan the fsl_mc_bus_type.
|
|
+ */
|
|
+ error = bus_for_each_dev(&fsl_mc_bus_type, NULL, NULL, add_to_restool);
|
|
+ if (error) {
|
|
+ bus_unregister_notifier(&fsl_mc_bus_type, nb);
|
|
+ kfree(nb);
|
|
+ pr_err("restool driver registration failure\n");
|
|
+ return error;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+
|
|
+free_nb:
|
|
+ kfree(nb);
|
|
+ return error;
|
|
+}
|
|
+
|
|
+module_init(fsl_mc_restool_driver_init);
|
|
+
|
|
+static void __exit fsl_mc_restool_driver_exit(void)
|
|
+{
|
|
+ struct restool_misc *restool_misc;
|
|
+ struct restool_misc *restool_misc_tmp;
|
|
+ char name1[20] = {0};
|
|
+ u32 name2 = 0;
|
|
+
|
|
+ list_for_each_entry_safe(restool_misc, restool_misc_tmp,
|
|
+ &misc_list, list) {
|
|
+ if (sscanf(restool_misc->misc.name, "%4s.%u", name1, &name2)
|
|
+ != 2)
|
|
+ continue;
|
|
+
|
|
+ pr_debug("name1=%s,name2=%u\n", name1, name2);
|
|
+ pr_debug("misc-device: %s\n", restool_misc->misc.name);
|
|
+ if (strcmp(name1, "dprc") != 0)
|
|
+ continue;
|
|
+
|
|
+ if (WARN_ON(!restool_misc->static_mc_io))
|
|
+ return;
|
|
+
|
|
+ if (WARN_ON(restool_misc->dynamic_instance_count != 0))
|
|
+ return;
|
|
+
|
|
+ if (WARN_ON(restool_misc->static_instance_in_use))
|
|
+ return;
|
|
+
|
|
+ misc_deregister(&restool_misc->misc);
|
|
+ pr_info("/dev/%s driver unregistered\n",
|
|
+ restool_misc->misc.name);
|
|
+ fsl_mc_portal_free(restool_misc->static_mc_io);
|
|
+ list_del(&restool_misc->list);
|
|
+ kfree(restool_misc);
|
|
+ }
|
|
+}
|
|
+
|
|
+module_exit(fsl_mc_restool_driver_exit);
|
|
+
|
|
+MODULE_AUTHOR("Freescale Semiconductor Inc.");
|
|
+MODULE_DESCRIPTION("Freescale's MC restool driver");
|
|
+MODULE_LICENSE("GPL");
|