layerscape: add ls1088ardb device support
[openwrt/staging/yousong.git] / target / linux / layerscape / patches-4.4 / 7197-staging-fsl-mc-Management-Complex-restool-driver.patch
diff --git a/target/linux/layerscape/patches-4.4/7197-staging-fsl-mc-Management-Complex-restool-driver.patch b/target/linux/layerscape/patches-4.4/7197-staging-fsl-mc-Management-Complex-restool-driver.patch
new file mode 100644 (file)
index 0000000..2a5e5df
--- /dev/null
@@ -0,0 +1,489 @@
+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");