layerscape: add patches-5.4
[openwrt/staging/wigyori.git] / target / linux / layerscape / patches-5.4 / 701-net-0308-staging-fsl_ppfe-add-support-for-a-char-dev-for-link.patch
diff --git a/target/linux/layerscape/patches-5.4/701-net-0308-staging-fsl_ppfe-add-support-for-a-char-dev-for-link.patch b/target/linux/layerscape/patches-5.4/701-net-0308-staging-fsl_ppfe-add-support-for-a-char-dev-for-link.patch
new file mode 100644 (file)
index 0000000..c445952
--- /dev/null
@@ -0,0 +1,364 @@
+From a41d4af2ed4b02edca9d7f69955893d407274dc2 Mon Sep 17 00:00:00 2001
+From: Shreyansh Jain <shreyansh.jain@nxp.com>
+Date: Wed, 6 Jun 2018 14:19:34 +0530
+Subject: [PATCH] staging: fsl_ppfe: add support for a char dev for link status
+
+Read and IOCTL support is added. Application would need to open,
+read/ioctl the /dev/pfe_us_cdev device.
+select is pending as it requires a wait_queue.
+
+Signed-off-by: Shreyansh Jain <shreyansh.jain@nxp.com>
+Signed-off-by: Calvin Johnson <calvin.johnson@nxp.com>
+---
+ drivers/staging/fsl_ppfe/Makefile   |   3 +-
+ drivers/staging/fsl_ppfe/pfe_cdev.c | 207 ++++++++++++++++++++++++++++++++++++
+ drivers/staging/fsl_ppfe/pfe_cdev.h |  52 +++++++++
+ drivers/staging/fsl_ppfe/pfe_eth.c  |  14 +++
+ drivers/staging/fsl_ppfe/pfe_mod.c  |  14 +++
+ 5 files changed, 289 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/staging/fsl_ppfe/pfe_cdev.c
+ create mode 100644 drivers/staging/fsl_ppfe/pfe_cdev.h
+
+--- a/drivers/staging/fsl_ppfe/Makefile
++++ b/drivers/staging/fsl_ppfe/Makefile
+@@ -16,4 +16,5 @@ pfe-y += pfe_mod.o \
+       pfe_sysfs.o \
+       pfe_debugfs.o \
+       pfe_ls1012a_platform.o \
+-      pfe_hal.o
++      pfe_hal.o \
++      pfe_cdev.o
+--- /dev/null
++++ b/drivers/staging/fsl_ppfe/pfe_cdev.c
+@@ -0,0 +1,207 @@
++/*
++ * Copyright 2018 NXP
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
++ */
++
++/* @pfe_cdev.c.
++ *  Dummy device representing the PFE US in userspace.
++ *  - used for interacting with the kernel layer for link status
++ */
++
++#include "pfe_cdev.h"
++
++static int pfe_majno;
++static struct class *pfe_char_class;
++static struct device *pfe_char_dev;
++
++struct pfe_shared_info link_states[PFE_CDEV_ETH_COUNT];
++
++static int pfe_cdev_open(struct inode *inp, struct file *fp)
++{
++      pr_debug("PFE CDEV device opened.\n");
++      return 0;
++}
++
++static ssize_t pfe_cdev_read(struct file *fp, char *buf,
++                           size_t len, loff_t *off)
++{
++      int ret = 0;
++
++      pr_info("PFE CDEV attempt copying (%lu) size of user.\n",
++              sizeof(link_states));
++
++      pr_debug("Dump link_state on screen before copy_to_user\n");
++      for (; ret < PFE_CDEV_ETH_COUNT; ret++) {
++              pr_debug("%u  %u", link_states[ret].phy_id,
++                       link_states[ret].state);
++              pr_debug("\n");
++      }
++
++      /* Copy to user the value in buffer sized len */
++      ret = copy_to_user(buf, &link_states, sizeof(link_states));
++      if (ret != 0) {
++              pr_err("Failed to send (%d)bytes of (%lu) requested.\n",
++                     ret, len);
++              return -EFAULT;
++      }
++
++      /* offset set back to 0 as there is contextual reading offset */
++      *off = 0;
++      pr_debug("Read of (%lu) bytes performed.\n", sizeof(link_states));
++
++      return sizeof(link_states);
++}
++
++/**
++ * This function is for getting some commands from user through non-IOCTL
++ * channel. It can used to configure the device.
++ * TODO: To be filled in future, if require duplex communication with user
++ * space.
++ */
++static ssize_t pfe_cdev_write(struct file *fp, const char *buf,
++                            size_t len, loff_t *off)
++{
++      pr_info("PFE CDEV Write operation not supported!\n");
++
++      return -EFAULT;
++}
++
++static int pfe_cdev_release(struct inode *inp, struct file *fp)
++{
++      pr_info("PFE_CDEV: Device successfully closed\n");
++      return 0;
++}
++
++static long pfe_cdev_ioctl(struct file *fp, unsigned int cmd,
++                         unsigned long arg)
++{
++      int ret = -EFAULT;
++      int __user *argp = (int __user *)arg;
++
++      pr_debug("PFE CDEV IOCTL Called with cmd=(%u)\n", cmd);
++
++      switch (cmd) {
++      case PFE_CDEV_ETH0_STATE_GET:
++              /* Return an unsigned int (link state) for ETH0 */
++              *argp = link_states[0].state;
++              pr_debug("Returning state=%d for ETH0\n", *argp);
++              ret = 0;
++              break;
++      case PFE_CDEV_ETH1_STATE_GET:
++              /* Return an unsigned int (link state) for ETH0 */
++              *argp = link_states[1].state;
++              pr_debug("Returning state=%d for ETH1\n", *argp);
++              ret = 0;
++              break;
++      default:
++              pr_info("Unsupport cmd (%d) for PFE CDEV.\n", cmd);
++              break;
++      };
++
++      return ret;
++}
++
++static unsigned int pfe_cdev_poll(struct file *fp,
++                                struct poll_table_struct *wait)
++{
++      pr_info("PFE CDEV poll method not supported\n");
++      return 0;
++}
++
++static const struct file_operations pfe_cdev_fops = {
++      .open = pfe_cdev_open,
++      .read = pfe_cdev_read,
++      .write = pfe_cdev_write,
++      .release = pfe_cdev_release,
++      .unlocked_ioctl = pfe_cdev_ioctl,
++      .poll = pfe_cdev_poll,
++};
++
++int pfe_cdev_init(void)
++{
++      int ret;
++
++      pr_debug("PFE CDEV initialization begin\n");
++
++      /* Register the major number for the device */
++      pfe_majno = register_chrdev(0, PFE_CDEV_NAME, &pfe_cdev_fops);
++      if (pfe_majno < 0) {
++              pr_err("Unable to register PFE CDEV. PFE CDEV not available\n");
++              ret = pfe_majno;
++              goto cleanup;
++      }
++
++      pr_debug("PFE CDEV assigned major number: %d\n", pfe_majno);
++
++      /* Register the class for the device */
++      pfe_char_class = class_create(THIS_MODULE, PFE_CLASS_NAME);
++      if (IS_ERR(pfe_char_class)) {
++              pr_err(
++              "Failed to init class for PFE CDEV. PFE CDEV not available.\n");
++              goto cleanup;
++      }
++
++      pr_debug("PFE CDEV Class created successfully.\n");
++
++      /* Create the device without any parent and without any callback data */
++          pfe_char_dev = device_create(pfe_char_class, NULL,
++                                       MKDEV(pfe_majno, 0), NULL,
++                                       PFE_CDEV_NAME);
++      if (IS_ERR(pfe_char_dev)) {
++              pr_err("Unable to PFE CDEV device. PFE CDEV not available.\n");
++              ret = PTR_ERR(pfe_char_dev);
++              goto cleanup;
++      }
++
++      /* Information structure being shared with the userspace */
++      memset(link_states, 0, sizeof(struct pfe_shared_info) *
++                      PFE_CDEV_ETH_COUNT);
++
++      pr_info("PFE CDEV created: %s\n", PFE_CDEV_NAME);
++
++      ret = 0;
++      return ret;
++
++cleanup:
++      if (!IS_ERR(pfe_char_class))
++              class_destroy(pfe_char_class);
++
++      if (pfe_majno > 0)
++              unregister_chrdev(pfe_majno, PFE_CDEV_NAME);
++
++      ret = -EFAULT;
++      return ret;
++}
++
++void pfe_cdev_exit(void)
++{
++      if (!IS_ERR(pfe_char_dev))
++              device_destroy(pfe_char_class, MKDEV(pfe_majno, 0));
++
++      if (!IS_ERR(pfe_char_class)) {
++              class_unregister(pfe_char_class);
++              class_destroy(pfe_char_class);
++      }
++
++      if (pfe_majno > 0)
++              unregister_chrdev(pfe_majno, PFE_CDEV_NAME);
++
++      /* reset the variables */
++      pfe_majno = 0;
++      pfe_char_class = NULL;
++      pfe_char_dev = NULL;
++
++      pr_info("PFE CDEV Removed.\n");
++}
+--- /dev/null
++++ b/drivers/staging/fsl_ppfe/pfe_cdev.h
+@@ -0,0 +1,52 @@
++/*
++ * Copyright 2018 NXP
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#ifndef _PFE_CDEV_H_
++#define _PFE_CDEV_H_
++
++#include <linux/init.h>
++#include <linux/device.h>
++#include <linux/err.h>
++#include <linux/kernel.h>
++#include <linux/fs.h>
++#include <linux/uaccess.h>
++#include <linux/poll.h>
++
++#define  PFE_CDEV_NAME "pfe_us_cdev"
++#define  PFE_CLASS_NAME  "ppfe_us"
++
++/* Extracted from ls1012a_pfe_platform_data, there are 3 interfaces which are
++ * supported by PFE driver. Should be updated if number of eth devices are
++ * changed.
++ */
++#define PFE_CDEV_ETH_COUNT 3
++
++struct pfe_shared_info {
++      uint32_t phy_id; /* Link phy ID */
++      uint8_t state;  /* Has either 0 or 1 */
++};
++
++extern struct pfe_shared_info link_states[PFE_CDEV_ETH_COUNT];
++
++/* IOCTL Commands */
++#define PFE_CDEV_ETH0_STATE_GET       0
++#define PFE_CDEV_ETH1_STATE_GET       1
++
++int pfe_cdev_init(void);
++void pfe_cdev_exit(void);
++
++#endif /* _PFE_CDEV_H_ */
+--- a/drivers/staging/fsl_ppfe/pfe_eth.c
++++ b/drivers/staging/fsl_ppfe/pfe_eth.c
+@@ -55,6 +55,7 @@
+ #include "pfe_mod.h"
+ #include "pfe_eth.h"
++#include "pfe_cdev.h"
+ #define LS1012A_REV_1_0               0x87040010
+@@ -1160,6 +1161,19 @@ static void pfe_eth_adjust_link(struct n
+               phy_print_status(phydev);
+       spin_unlock_irqrestore(&priv->lock, flags);
++
++      /* Now, dump the details to the cdev.
++       * XXX: Locking would be required? (uniprocess arch)
++       *      Or, maybe move it in spinlock above
++       */
++      if (us && priv->einfo->gem_id < PFE_CDEV_ETH_COUNT) {
++              pr_debug("Changing link state from (%u) to (%u) for ID=(%u)\n",
++                       link_states[priv->einfo->gem_id].state,
++                       phydev->link,
++                       priv->einfo->gem_id);
++              link_states[priv->einfo->gem_id].phy_id = priv->einfo->gem_id;
++              link_states[priv->einfo->gem_id].state = phydev->link;
++      }
+ }
+ /* pfe_phy_exit
+--- a/drivers/staging/fsl_ppfe/pfe_mod.c
++++ b/drivers/staging/fsl_ppfe/pfe_mod.c
+@@ -18,6 +18,7 @@
+ #include <linux/dma-mapping.h>
+ #include "pfe_mod.h"
++#include "pfe_cdev.h"
+ unsigned int us;
+ module_param(us, uint, 0444);
+@@ -92,8 +93,18 @@ firmware_init:
+       if (rc < 0)
+               goto err_debugfs;
++      if (us) {
++              /* Creating a character device */
++              rc = pfe_cdev_init();
++              if (rc < 0)
++                      goto err_cdev;
++      }
++
+       return 0;
++err_cdev:
++      pfe_debugfs_exit(pfe);
++
+ err_debugfs:
+       pfe_sysfs_exit(pfe);
+@@ -129,6 +140,9 @@ int pfe_remove(struct pfe *pfe)
+ {
+       pr_info("%s\n", __func__);
++      if (us)
++              pfe_cdev_exit();
++
+       pfe_debugfs_exit(pfe);
+       pfe_sysfs_exit(pfe);