linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH next 2/2] misc: Add Renesas Synchronization Management Unit (SMU) support
@ 2021-03-26 15:34 min.li.xe
  2021-03-27  7:58 ` kernel test robot
                   ` (2 more replies)
  0 siblings, 3 replies; 13+ messages in thread
From: min.li.xe @ 2021-03-26 15:34 UTC (permalink / raw)
  To: derek.kiernan, dragan.cvetic, arnd, gregkh; +Cc: linux-kernel, Min Li

From: Min Li <min.li.xe@renesas.com>

This driver is developed for the IDT ClockMatrix(TM) and 82P33xxx families
of timing and synchronization devices.It will be used by Renesas PTP Clock
Manager for Linux (pcm4l) software to provide support to GNSS assisted
partial timing support (APTS) and other networking timing functions.

Current version provides kernel API's to support the following functions
-set combomode to enable SYNCE clock support
-read dpll's state to determine if the dpll is locked to the GNSS channel
-read dpll's ffo (fractional frequency offset) in ppqt

Signed-off-by: Min Li <min.li.xe@renesas.com>
---
Change log
-rebase change to linux-next tree
-remove uncessary condition checks suggested by Greg
-fix compile error for x86_64

 drivers/misc/Kconfig      |   9 ++
 drivers/misc/Makefile     |   2 +
 drivers/misc/rsmu_cdev.c  | 321 ++++++++++++++++++++++++++++++++++++++++++++++
 drivers/misc/rsmu_cdev.h  |  72 +++++++++++
 drivers/misc/rsmu_cm.c    | 166 ++++++++++++++++++++++++
 drivers/misc/rsmu_sabre.c | 128 ++++++++++++++++++
 include/uapi/linux/rsmu.h |  64 +++++++++
 7 files changed, 762 insertions(+)
 create mode 100644 drivers/misc/rsmu_cdev.c
 create mode 100644 drivers/misc/rsmu_cdev.h
 create mode 100644 drivers/misc/rsmu_cm.c
 create mode 100644 drivers/misc/rsmu_sabre.c
 create mode 100644 include/uapi/linux/rsmu.h

diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index f532c59..49b523a 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -445,6 +445,15 @@ config HISI_HIKEY_USB
 	  switching between the dual-role USB-C port and the USB-A host ports
 	  using only one USB controller.
 
+config RSMU
+	tristate "Renesas Synchronization Management Unit (SMU)"
+	help
+	  This option enables support for the IDT ClockMatrix(TM) and 82P33xxx
+	  families of timing and synchronization devices. It will be used by
+	  Renesas PTP Clock Manager for Linux (pcm4l) software to provide support
+	  for GNSS assisted partial timing support (APTS) and other networking
+	  timing functions.
+
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 99b6f15..21b8ed4 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -56,3 +56,5 @@ obj-$(CONFIG_HABANA_AI)		+= habanalabs/
 obj-$(CONFIG_UACCE)		+= uacce/
 obj-$(CONFIG_XILINX_SDFEC)	+= xilinx_sdfec.o
 obj-$(CONFIG_HISI_HIKEY_USB)	+= hisi_hikey_usb.o
+rsmu-objs			:= rsmu_cdev.o rsmu_cm.o rsmu_sabre.o
+obj-$(CONFIG_RSMU)		+= rsmu.o
diff --git a/drivers/misc/rsmu_cdev.c b/drivers/misc/rsmu_cdev.c
new file mode 100644
index 0000000..2df5788
--- /dev/null
+++ b/drivers/misc/rsmu_cdev.c
@@ -0,0 +1,321 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * This driver is developed for the IDT ClockMatrix(TM) and 82P33xxx families
+ * of timing and synchronization devices. It will be used by Renesas PTP Clock
+ * Manager for Linux (pcm4l) software to provide support to GNSS assisted
+ * partial timing support (APTS) and other networking timing functions.
+ *
+ * Please note it must work with Renesas MFD driver to access device through
+ * I2C/SPI.
+ *
+ * Copyright (C) 2019 Integrated Device Technology, Inc., a Renesas Company.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/mfd/rsmu.h>
+#include <uapi/linux/rsmu.h>
+
+#include "rsmu_cdev.h"
+
+#define DRIVER_NAME	"rsmu"
+#define DRIVER_MAX_DEV	BIT(MINORBITS)
+
+static struct class *rsmu_class;
+static dev_t rsmu_cdevt;
+static struct rsmu_ops *ops_array[] = {
+	[RSMU_CM] = &cm_ops,
+	[RSMU_SABRE] = &sabre_ops,
+};
+
+static int
+rsmu_set_combomode(struct rsmu_cdev *rsmu, void __user *arg)
+{
+	struct rsmu_ops *ops = rsmu->ops;
+	struct rsmu_combomode mode;
+	int err;
+
+	if (copy_from_user(&mode, arg, sizeof(mode)))
+		return -EFAULT;
+
+	if (ops->set_combomode == NULL)
+		return -ENOTSUPP;
+
+	mutex_lock(rsmu->lock);
+	err = ops->set_combomode(rsmu, mode.dpll, mode.mode);
+	mutex_unlock(rsmu->lock);
+
+	return err;
+}
+
+static int
+rsmu_get_dpll_state(struct rsmu_cdev *rsmu, void __user *arg)
+{
+	struct rsmu_ops *ops = rsmu->ops;
+	struct rsmu_get_state state_request;
+	u8 state;
+	int err;
+
+	if (copy_from_user(&state_request, arg, sizeof(state_request)))
+		return -EFAULT;
+
+	if (ops->get_dpll_state == NULL)
+		return -ENOTSUPP;
+
+	mutex_lock(rsmu->lock);
+	err = ops->get_dpll_state(rsmu, state_request.dpll, &state);
+	mutex_unlock(rsmu->lock);
+
+	state_request.state = state;
+	if (copy_to_user(arg, &state_request, sizeof(state_request)))
+		return -EFAULT;
+
+	return err;
+}
+
+static int
+rsmu_get_dpll_ffo(struct rsmu_cdev *rsmu, void __user *arg)
+{
+	struct rsmu_ops *ops = rsmu->ops;
+	struct rsmu_get_ffo ffo_request;
+	int err;
+
+	if (copy_from_user(&ffo_request, arg, sizeof(ffo_request)))
+		return -EFAULT;
+
+	if (ops->get_dpll_ffo == NULL)
+		return -ENOTSUPP;
+
+	mutex_lock(rsmu->lock);
+	err = ops->get_dpll_ffo(rsmu, ffo_request.dpll, &ffo_request);
+	mutex_unlock(rsmu->lock);
+
+	if (copy_to_user(arg, &ffo_request, sizeof(ffo_request)))
+		return -EFAULT;
+
+	return err;
+}
+
+static int
+rsmu_open(struct inode *iptr, struct file *fptr)
+{
+	struct rsmu_cdev *rsmu;
+
+	rsmu = container_of(iptr->i_cdev, struct rsmu_cdev, rsmu_cdev);
+	fptr->private_data = rsmu;
+	return 0;
+}
+
+static int
+rsmu_release(struct inode *iptr, struct file *fptr)
+{
+	return 0;
+}
+
+static long
+rsmu_ioctl(struct file *fptr, unsigned int cmd, unsigned long data)
+{
+	struct rsmu_cdev *rsmu = fptr->private_data;
+	void __user *arg = (void __user *)data;
+	int err = 0;
+
+	switch (cmd) {
+	case RSMU_SET_COMBOMODE:
+		err = rsmu_set_combomode(rsmu, arg);
+		break;
+	case RSMU_GET_STATE:
+		err = rsmu_get_dpll_state(rsmu, arg);
+		break;
+	case RSMU_GET_FFO:
+		err = rsmu_get_dpll_ffo(rsmu, arg);
+		break;
+	default:
+		/* Should not get here */
+		dev_err(rsmu->dev, "Undefined RSMU IOCTL");
+		err = -EINVAL;
+		break;
+	}
+
+	return err;
+}
+
+static long rsmu_compat_ioctl(struct file *fptr, unsigned int cmd,
+			      unsigned long data)
+{
+	return rsmu_ioctl(fptr, cmd, data);
+}
+
+static const struct file_operations rsmu_fops = {
+	.owner = THIS_MODULE,
+	.open = rsmu_open,
+	.release = rsmu_release,
+	.unlocked_ioctl = rsmu_ioctl,
+	.compat_ioctl =	rsmu_compat_ioctl,
+};
+
+static int rsmu_init_ops(struct rsmu_cdev *rsmu)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ops_array); i++)
+		if (ops_array[i]->type == rsmu->type)
+			break;
+
+	if (i == ARRAY_SIZE(ops_array))
+		return -EINVAL;
+
+	rsmu->ops = ops_array[i];
+	return 0;
+}
+
+static int
+rsmu_probe(struct platform_device *pdev)
+{
+	struct rsmu_pdata *pdata = dev_get_platdata(&pdev->dev);
+	struct rsmu_cdev *rsmu;
+	struct device *rsmu_cdev;
+	int err;
+
+	rsmu = devm_kzalloc(&pdev->dev, sizeof(*rsmu), GFP_KERNEL);
+	if (!rsmu)
+		return -ENOMEM;
+
+	rsmu->dev = &pdev->dev;
+	rsmu->mfd = pdev->dev.parent;
+	rsmu->type = pdata->type;
+	rsmu->lock = pdata->lock;
+	rsmu->index = pdata->index;
+
+	/* Save driver private data */
+	platform_set_drvdata(pdev, rsmu);
+
+	cdev_init(&rsmu->rsmu_cdev, &rsmu_fops);
+	rsmu->rsmu_cdev.owner = THIS_MODULE;
+	err = cdev_add(&rsmu->rsmu_cdev,
+		       MKDEV(MAJOR(rsmu_cdevt), 0), 1);
+	if (err < 0) {
+		dev_err(rsmu->dev, "cdev_add failed");
+		err = -EIO;
+		goto err_rsmu_dev;
+	}
+
+	if (!rsmu_class) {
+		err = -EIO;
+		dev_err(rsmu->dev, "rsmu class not created correctly");
+		goto err_rsmu_cdev;
+	}
+
+	rsmu_cdev = device_create(rsmu_class, rsmu->dev,
+				  MKDEV(MAJOR(rsmu_cdevt), 0),
+				  rsmu, "rsmu%d", rsmu->index);
+	if (IS_ERR(rsmu_cdev)) {
+		dev_err(rsmu->dev, "Unable to create char device");
+		err = PTR_ERR(rsmu_cdev);
+		goto err_rsmu_cdev;
+	}
+
+	err = rsmu_init_ops(rsmu);
+	if (err) {
+		dev_err(rsmu->dev, "Unable to match type %d", rsmu->type);
+		goto err_rsmu_cdev;
+	}
+
+	dev_info(rsmu->dev, "Probe SMU type %d successful\n", rsmu->type);
+	return 0;
+
+	/* Failure cleanup */
+err_rsmu_cdev:
+	cdev_del(&rsmu->rsmu_cdev);
+err_rsmu_dev:
+	return err;
+}
+
+static int
+rsmu_remove(struct platform_device *pdev)
+{
+	struct rsmu_cdev *rsmu = platform_get_drvdata(pdev);
+	struct device *dev = &pdev->dev;
+
+	if (!rsmu_class) {
+		dev_err(dev, "rsmu_class is NULL");
+		return -EIO;
+	}
+
+	device_destroy(rsmu_class, MKDEV(MAJOR(rsmu_cdevt), 0));
+	cdev_del(&rsmu->rsmu_cdev);
+
+	return 0;
+}
+
+static const struct platform_device_id rsmu_id_table[] = {
+	{ "rsmu-cdev0", },
+	{ "rsmu-cdev1", },
+	{ "rsmu-cdev2", },
+	{ "rsmu-cdev3", },
+	{}
+};
+MODULE_DEVICE_TABLE(platform, rsmu_id_table);
+
+static struct platform_driver rsmu_driver = {
+	.driver = {
+		.name = DRIVER_NAME,
+	},
+	.probe = rsmu_probe,
+	.remove =  rsmu_remove,
+	.id_table = rsmu_id_table,
+};
+
+static int __init rsmu_init(void)
+{
+	int err;
+
+	rsmu_class = class_create(THIS_MODULE, DRIVER_NAME);
+	if (IS_ERR(rsmu_class)) {
+		err = PTR_ERR(rsmu_class);
+		pr_err("Unable to register rsmu class");
+		return err;
+	}
+
+	err = alloc_chrdev_region(&rsmu_cdevt, 0, DRIVER_MAX_DEV, DRIVER_NAME);
+	if (err < 0) {
+		pr_err("Unable to get major number");
+		goto err_rsmu_class;
+	}
+
+	err = platform_driver_register(&rsmu_driver);
+	if (err < 0) {
+		pr_err("Unabled to register %s driver", DRIVER_NAME);
+		goto err_rsmu_drv;
+	}
+	return 0;
+
+	/* Error Path */
+err_rsmu_drv:
+	unregister_chrdev_region(rsmu_cdevt, DRIVER_MAX_DEV);
+err_rsmu_class:
+	class_destroy(rsmu_class);
+	return err;
+}
+
+static void __exit rsmu_exit(void)
+{
+	platform_driver_unregister(&rsmu_driver);
+	unregister_chrdev_region(rsmu_cdevt, DRIVER_MAX_DEV);
+	class_destroy(rsmu_class);
+	rsmu_class = NULL;
+}
+
+module_init(rsmu_init);
+module_exit(rsmu_exit);
+
+MODULE_DESCRIPTION("Renesas SMU character device driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/rsmu_cdev.h b/drivers/misc/rsmu_cdev.h
new file mode 100644
index 0000000..3ced817
--- /dev/null
+++ b/drivers/misc/rsmu_cdev.h
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * This driver is developed for the IDT ClockMatrix(TM) of
+ * timing and synchronization devices.
+ *
+ * Copyright (C) 2019 Integrated Device Technology, Inc., a Renesas Company.
+ */
+#ifndef __LINUX_RSMU_CDEV_H
+#define __LINUX_RSMU_CDEV_H
+
+#include <linux/cdev.h>
+
+struct rsmu_ops;
+
+/**
+ * struct rsmu_cdev - Driver data for RSMU character device
+ * @dev: pointer to platform device
+ * @mfd: pointer to MFD device
+ * @rsmu_cdev: character device handle
+ * @lock: mutex to protect operations from being interrupted
+ * @type: rsmu device type
+ * @ops: rsmu device methods
+ * @index: rsmu device index
+ */
+struct rsmu_cdev {
+	struct device *dev;
+	struct device *mfd;
+	struct cdev rsmu_cdev;
+	struct mutex *lock;
+	enum rsmu_type type;
+	struct rsmu_ops *ops;
+	u8 index;
+};
+
+extern struct rsmu_ops cm_ops;
+extern struct rsmu_ops sabre_ops;
+
+struct rsmu_ops {
+	enum rsmu_type type;
+	int (*set_combomode)(struct rsmu_cdev *rsmu, u8 dpll, u8 mode);
+	int (*get_dpll_state)(struct rsmu_cdev *rsmu, u8 dpll, u8 *state);
+	int (*get_dpll_ffo)(struct rsmu_cdev *rsmu, u8 dpll,
+			    struct rsmu_get_ffo *ffo);
+};
+
+/**
+ * Enumerated type listing DPLL combination modes
+ */
+enum rsmu_dpll_combomode {
+	E_COMBOMODE_CURRENT = 0,
+	E_COMBOMODE_FASTAVG,
+	E_COMBOMODE_SLOWAVG,
+	E_COMBOMODE_HOLDOVER,
+	E_COMBOMODE_MAX
+};
+
+/**
+ * An id used to identify the respective child class states.
+ */
+enum rsmu_class_state {
+	E_SRVLOINITIALSTATE = 0,
+	E_SRVLOUNQUALIFIEDSTATE = 1,
+	E_SRVLOLOCKACQSTATE = 2,
+	E_SRVLOFREQUENCYLOCKEDSTATE = 3,
+	E_SRVLOTIMELOCKEDSTATE = 4,
+	E_SRVLOHOLDOVERINSPECSTATE = 5,
+	E_SRVLOHOLDOVEROUTOFSPECSTATE = 6,
+	E_SRVLOFREERUNSTATE = 7,
+	E_SRVNUMBERLOSTATES = 8,
+	E_SRVLOSTATEINVALID = 9,
+};
+#endif
diff --git a/drivers/misc/rsmu_cm.c b/drivers/misc/rsmu_cm.c
new file mode 100644
index 0000000..d5af624
--- /dev/null
+++ b/drivers/misc/rsmu_cm.c
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * This driver is developed for the IDT ClockMatrix(TM) of
+ * timing and synchronization devices.
+ *
+ * Copyright (C) 2019 Integrated Device Technology, Inc., a Renesas Company.
+ */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/mfd/idt8a340_reg.h>
+#include <linux/mfd/rsmu.h>
+#include <uapi/linux/rsmu.h>
+#include <asm/unaligned.h>
+
+#include "rsmu_cdev.h"
+
+static int rsmu_cm_set_combomode(struct rsmu_cdev *rsmu, u8 dpll, u8 mode)
+{
+	u16 dpll_ctrl_n;
+	u8 cfg;
+	int err;
+
+	switch (dpll) {
+	case 0:
+		dpll_ctrl_n = DPLL_CTRL_0;
+		break;
+	case 1:
+		dpll_ctrl_n = DPLL_CTRL_1;
+		break;
+	case 2:
+		dpll_ctrl_n = DPLL_CTRL_2;
+		break;
+	case 3:
+		dpll_ctrl_n = DPLL_CTRL_3;
+		break;
+	case 4:
+		dpll_ctrl_n = DPLL_CTRL_4;
+		break;
+	case 5:
+		dpll_ctrl_n = DPLL_CTRL_5;
+		break;
+	case 6:
+		dpll_ctrl_n = DPLL_CTRL_6;
+		break;
+	case 7:
+		dpll_ctrl_n = DPLL_CTRL_7;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (mode >= E_COMBOMODE_MAX)
+		return -EINVAL;
+
+	err = rsmu_read(rsmu->mfd, dpll_ctrl_n + DPLL_CTRL_COMBO_MASTER_CFG,
+			&cfg, sizeof(cfg));
+	if (err)
+		return err;
+
+	/* Only need to enable/disable COMBO_MODE_HOLD. */
+	if (mode)
+		cfg |= COMBO_MASTER_HOLD;
+	else
+		cfg &= ~COMBO_MASTER_HOLD;
+
+	return rsmu_write(rsmu->mfd, dpll_ctrl_n + DPLL_CTRL_COMBO_MASTER_CFG,
+			  &cfg, sizeof(cfg));
+}
+
+static int rsmu_cm_get_dpll_state(struct rsmu_cdev *rsmu, u8 dpll, u8 *state)
+{
+	u8 cfg;
+	int err;
+
+	/* 8 is sys dpll */
+	if (dpll > 8)
+		return -EINVAL;
+
+	err = rsmu_read(rsmu->mfd,
+			  STATUS + DPLL0_STATUS + dpll,
+			  &cfg, sizeof(cfg));
+	if (err)
+		return err;
+
+	switch (cfg & DPLL_STATE_MASK) {
+	case DPLL_STATE_FREERUN:
+		*state = E_SRVLOUNQUALIFIEDSTATE;
+		break;
+	case DPLL_STATE_LOCKACQ:
+	case DPLL_STATE_LOCKREC:
+		*state = E_SRVLOLOCKACQSTATE;
+		break;
+	case DPLL_STATE_LOCKED:
+		*state = E_SRVLOTIMELOCKEDSTATE;
+		break;
+	case DPLL_STATE_HOLDOVER:
+		*state = E_SRVLOHOLDOVERINSPECSTATE;
+		break;
+	default:
+		*state = E_SRVLOSTATEINVALID;
+		break;
+	}
+
+	return 0;
+}
+
+static int rsmu_cm_get_dpll_ffo(struct rsmu_cdev *rsmu, u8 dpll,
+				struct rsmu_get_ffo *ffo)
+{
+	u8 buf[8] = {0};
+	s64 fcw = 0;
+	u16 dpll_filter_status;
+	int err;
+
+	switch (dpll) {
+	case 0:
+		dpll_filter_status = DPLL0_FILTER_STATUS;
+		break;
+	case 1:
+		dpll_filter_status = DPLL1_FILTER_STATUS;
+		break;
+	case 2:
+		dpll_filter_status = DPLL2_FILTER_STATUS;
+		break;
+	case 3:
+		dpll_filter_status = DPLL3_FILTER_STATUS;
+		break;
+	case 4:
+		dpll_filter_status = DPLL4_FILTER_STATUS;
+		break;
+	case 5:
+		dpll_filter_status = DPLL5_FILTER_STATUS;
+		break;
+	case 6:
+		dpll_filter_status = DPLL6_FILTER_STATUS;
+		break;
+	case 7:
+		dpll_filter_status = DPLL7_FILTER_STATUS;
+		break;
+	case 8:
+		dpll_filter_status = DPLLSYS_FILTER_STATUS;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	err = rsmu_read(rsmu->mfd, STATUS + dpll_filter_status, buf, 6);
+	if (err)
+		return err;
+
+	/* Convert to frequency control word */
+	fcw = sign_extend64(get_unaligned_le64(buf), 47);
+
+	/* FCW unit is 2 ^ -53 = 1.1102230246251565404236316680908e-16 */
+	ffo->ffo = fcw * 111;
+
+	return 0;
+}
+
+struct rsmu_ops cm_ops = {
+	.type = RSMU_CM,
+	.set_combomode = rsmu_cm_set_combomode,
+	.get_dpll_state = rsmu_cm_get_dpll_state,
+	.get_dpll_ffo = rsmu_cm_get_dpll_ffo,
+};
diff --git a/drivers/misc/rsmu_sabre.c b/drivers/misc/rsmu_sabre.c
new file mode 100644
index 0000000..aa772f1
--- /dev/null
+++ b/drivers/misc/rsmu_sabre.c
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * This driver is developed for the IDT 82P33XXX series of
+ * timing and synchronization devices.
+ *
+ * Copyright (C) 2019 Integrated Device Technology, Inc., a Renesas Company.
+ */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/mfd/idt82p33_reg.h>
+#include <linux/mfd/rsmu.h>
+#include <uapi/linux/rsmu.h>
+#include <asm/unaligned.h>
+
+#include "rsmu_cdev.h"
+
+static int rsmu_sabre_set_combomode(struct rsmu_cdev *rsmu, u8 dpll, u8 mode)
+{
+	u16 dpll_ctrl_n;
+	u8 cfg;
+	int err;
+
+	switch (dpll) {
+	case 0:
+		dpll_ctrl_n = DPLL1_OPERATING_MODE_CNFG;
+		break;
+	case 1:
+		dpll_ctrl_n = DPLL2_OPERATING_MODE_CNFG;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (mode >= E_COMBOMODE_MAX)
+		return -EINVAL;
+
+	err = rsmu_read(rsmu->mfd, dpll_ctrl_n, &cfg, sizeof(cfg));
+	if (err)
+		return err;
+
+	cfg &= ~(COMBO_MODE_MASK << COMBO_MODE_SHIFT);
+	cfg |= mode << COMBO_MODE_SHIFT;
+
+	return rsmu_write(rsmu->mfd, dpll_ctrl_n, &cfg, sizeof(cfg));
+}
+
+static int rsmu_sabre_get_dpll_state(struct rsmu_cdev *rsmu, u8 dpll, u8 *state)
+{
+	u16 dpll_sts_n;
+	u8 cfg;
+	int err;
+
+	switch (dpll) {
+	case 0:
+		dpll_sts_n = DPLL1_OPERATING_STS;
+		break;
+	case 1:
+		dpll_sts_n = DPLL2_OPERATING_STS;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	err = rsmu_read(rsmu->mfd, dpll_sts_n, &cfg, sizeof(cfg));
+	if (err)
+		return err;
+
+	switch (cfg & OPERATING_STS_MASK) {
+	case DPLL_STATE_FREERUN:
+		*state = E_SRVLOUNQUALIFIEDSTATE;
+		break;
+	case DPLL_STATE_PRELOCKED2:
+	case DPLL_STATE_PRELOCKED:
+		*state = E_SRVLOLOCKACQSTATE;
+		break;
+	case DPLL_STATE_LOCKED:
+		*state = E_SRVLOTIMELOCKEDSTATE;
+		break;
+	case DPLL_STATE_HOLDOVER:
+		*state = E_SRVLOHOLDOVERINSPECSTATE;
+		break;
+	default:
+		*state = E_SRVLOSTATEINVALID;
+		break;
+	}
+
+	return 0;
+}
+
+static int rsmu_sabre_get_dpll_ffo(struct rsmu_cdev *rsmu, u8 dpll,
+				   struct rsmu_get_ffo *ffo)
+{
+	u8 buf[8] = {0};
+	s64 fcw = 0;
+	u16 dpll_freq_n;
+	int err;
+
+	switch (dpll) {
+	case 0:
+		dpll_freq_n = DPLL1_CURRENT_FREQ_STS;
+		break;
+	case 1:
+		dpll_freq_n = DPLL2_CURRENT_FREQ_STS;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	err = rsmu_read(rsmu->mfd, dpll_freq_n, buf, 5);
+	if (err)
+		return err;
+
+	/* Convert to frequency control word */
+	fcw = sign_extend64(get_unaligned_le64(buf), 39);
+
+	/* FCW unit is 77760 / ( 1638400 * 2^48) = 1.68615121864946 * 10^-16 */
+	ffo->ffo = div_s64(fcw * 168615, 1000);
+
+	return 0;
+}
+
+struct rsmu_ops sabre_ops = {
+	.type = RSMU_SABRE,
+	.set_combomode = rsmu_sabre_set_combomode,
+	.get_dpll_state = rsmu_sabre_get_dpll_state,
+	.get_dpll_ffo = rsmu_sabre_get_dpll_ffo,
+};
diff --git a/include/uapi/linux/rsmu.h b/include/uapi/linux/rsmu.h
new file mode 100644
index 0000000..02c9e38
--- /dev/null
+++ b/include/uapi/linux/rsmu.h
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/*
+ * Driver for the IDT ClockMatrix(TM) and 82p33xxx families of
+ * timing and synchronization devices.
+ *
+ * Copyright (C) 2019 Integrated Device Technology, Inc., a Renesas Company.
+ */
+
+#ifndef __UAPI_LINUX_RSMU_CDEV_H
+#define __UAPI_LINUX_RSMU_CDEV_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+/* Set dpll combomode */
+struct rsmu_combomode {
+	__u8 dpll;
+	__u8 mode;
+};
+
+/* Get dpll state */
+struct rsmu_get_state {
+	__u8 dpll;
+	__u8 state;
+};
+
+/* Get dpll ffo (fractional frequency offset) in ppqt*/
+struct rsmu_get_ffo {
+	__u8 dpll;
+	__s64 ffo;
+};
+
+/*
+ * RSMU IOCTL List
+ */
+#define RSMU_MAGIC '?'
+
+/**
+ * @Description
+ * ioctl to set SMU combo mode.
+ *
+ * @Parameters
+ * pointer to struct rsmu_combomode that contains dpll combomode setting
+ */
+#define RSMU_SET_COMBOMODE  _IOW(RSMU_MAGIC, 1, struct rsmu_combomode)
+
+/**
+ * @Description
+ * ioctl to get SMU dpll state.
+ *
+ * @Parameters
+ * pointer to struct rsmu_get_state that contains dpll state
+ */
+#define RSMU_GET_STATE  _IOR(RSMU_MAGIC, 2, struct rsmu_get_state)
+
+/**
+ * @Description
+ * ioctl to get SMU dpll ffo.
+ *
+ * @Parameters
+ * pointer to struct rsmu_get_ffo that contains dpll ffo in ppqt
+ */
+#define RSMU_GET_FFO  _IOR(RSMU_MAGIC, 3, struct rsmu_get_ffo)
+#endif /* __UAPI_LINUX_RSMU_CDEV_H */
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH next 2/2] misc: Add Renesas Synchronization Management Unit (SMU) support
  2021-03-26 15:34 [PATCH next 2/2] misc: Add Renesas Synchronization Management Unit (SMU) support min.li.xe
@ 2021-03-27  7:58 ` kernel test robot
  2021-03-27  9:53 ` kernel test robot
  2021-03-28 12:13 ` Greg KH
  2 siblings, 0 replies; 13+ messages in thread
From: kernel test robot @ 2021-03-27  7:58 UTC (permalink / raw)
  To: min.li.xe, derek.kiernan, dragan.cvetic, arnd, gregkh
  Cc: kbuild-all, linux-kernel, Min Li

[-- Attachment #1: Type: text/plain, Size: 5832 bytes --]

Hi,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on next-20210326]

url:    https://github.com/0day-ci/linux/commits/min-li-xe-renesas-com/mfd-Add-Renesas-Synchronization-Management-Unit-SMU-support/20210327-150316
base:    931294922e65a23e1aad6398b9ae02df74044679
config: x86_64-randconfig-r036-20210327 (attached as .config)
compiler: gcc-9 (Debian 9.3.0-22) 9.3.0
reproduce (this is a W=1 build):
        # https://github.com/0day-ci/linux/commit/0719d3e2c97f4073f3b495311f260c6c3e0dda28
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review min-li-xe-renesas-com/mfd-Add-Renesas-Synchronization-Management-Unit-SMU-support/20210327-150316
        git checkout 0719d3e2c97f4073f3b495311f260c6c3e0dda28
        # save the attached .config to linux build tree
        make W=1 ARCH=x86_64 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   ld: drivers/misc/rsmu_cm.o: in function `rsmu_cm_get_dpll_ffo':
>> drivers/misc/rsmu_cm.c:148: undefined reference to `rsmu_read'
   ld: drivers/misc/rsmu_cm.o: in function `rsmu_cm_get_dpll_state':
   drivers/misc/rsmu_cm.c:80: undefined reference to `rsmu_read'
   ld: drivers/misc/rsmu_cm.o: in function `rsmu_cm_set_combomode':
   drivers/misc/rsmu_cm.c:56: undefined reference to `rsmu_read'
>> ld: drivers/misc/rsmu_cm.c:67: undefined reference to `rsmu_write'
   ld: drivers/misc/rsmu_sabre.o: in function `rsmu_sabre_get_dpll_ffo':
>> drivers/misc/rsmu_sabre.c:110: undefined reference to `rsmu_read'
   ld: drivers/misc/rsmu_sabre.o: in function `rsmu_sabre_get_dpll_state':
   drivers/misc/rsmu_sabre.c:65: undefined reference to `rsmu_read'
   ld: drivers/misc/rsmu_sabre.o: in function `rsmu_sabre_set_combomode':
   drivers/misc/rsmu_sabre.c:38: undefined reference to `rsmu_read'
>> ld: drivers/misc/rsmu_sabre.c:45: undefined reference to `rsmu_write'


vim +148 drivers/misc/rsmu_cm.c

    17	
    18	static int rsmu_cm_set_combomode(struct rsmu_cdev *rsmu, u8 dpll, u8 mode)
    19	{
    20		u16 dpll_ctrl_n;
    21		u8 cfg;
    22		int err;
    23	
    24		switch (dpll) {
    25		case 0:
    26			dpll_ctrl_n = DPLL_CTRL_0;
    27			break;
    28		case 1:
    29			dpll_ctrl_n = DPLL_CTRL_1;
    30			break;
    31		case 2:
    32			dpll_ctrl_n = DPLL_CTRL_2;
    33			break;
    34		case 3:
    35			dpll_ctrl_n = DPLL_CTRL_3;
    36			break;
    37		case 4:
    38			dpll_ctrl_n = DPLL_CTRL_4;
    39			break;
    40		case 5:
    41			dpll_ctrl_n = DPLL_CTRL_5;
    42			break;
    43		case 6:
    44			dpll_ctrl_n = DPLL_CTRL_6;
    45			break;
    46		case 7:
    47			dpll_ctrl_n = DPLL_CTRL_7;
    48			break;
    49		default:
    50			return -EINVAL;
    51		}
    52	
    53		if (mode >= E_COMBOMODE_MAX)
    54			return -EINVAL;
    55	
    56		err = rsmu_read(rsmu->mfd, dpll_ctrl_n + DPLL_CTRL_COMBO_MASTER_CFG,
    57				&cfg, sizeof(cfg));
    58		if (err)
    59			return err;
    60	
    61		/* Only need to enable/disable COMBO_MODE_HOLD. */
    62		if (mode)
    63			cfg |= COMBO_MASTER_HOLD;
    64		else
    65			cfg &= ~COMBO_MASTER_HOLD;
    66	
  > 67		return rsmu_write(rsmu->mfd, dpll_ctrl_n + DPLL_CTRL_COMBO_MASTER_CFG,
    68				  &cfg, sizeof(cfg));
    69	}
    70	
    71	static int rsmu_cm_get_dpll_state(struct rsmu_cdev *rsmu, u8 dpll, u8 *state)
    72	{
    73		u8 cfg;
    74		int err;
    75	
    76		/* 8 is sys dpll */
    77		if (dpll > 8)
    78			return -EINVAL;
    79	
    80		err = rsmu_read(rsmu->mfd,
    81				  STATUS + DPLL0_STATUS + dpll,
    82				  &cfg, sizeof(cfg));
    83		if (err)
    84			return err;
    85	
    86		switch (cfg & DPLL_STATE_MASK) {
    87		case DPLL_STATE_FREERUN:
    88			*state = E_SRVLOUNQUALIFIEDSTATE;
    89			break;
    90		case DPLL_STATE_LOCKACQ:
    91		case DPLL_STATE_LOCKREC:
    92			*state = E_SRVLOLOCKACQSTATE;
    93			break;
    94		case DPLL_STATE_LOCKED:
    95			*state = E_SRVLOTIMELOCKEDSTATE;
    96			break;
    97		case DPLL_STATE_HOLDOVER:
    98			*state = E_SRVLOHOLDOVERINSPECSTATE;
    99			break;
   100		default:
   101			*state = E_SRVLOSTATEINVALID;
   102			break;
   103		}
   104	
   105		return 0;
   106	}
   107	
   108	static int rsmu_cm_get_dpll_ffo(struct rsmu_cdev *rsmu, u8 dpll,
   109					struct rsmu_get_ffo *ffo)
   110	{
   111		u8 buf[8] = {0};
   112		s64 fcw = 0;
   113		u16 dpll_filter_status;
   114		int err;
   115	
   116		switch (dpll) {
   117		case 0:
   118			dpll_filter_status = DPLL0_FILTER_STATUS;
   119			break;
   120		case 1:
   121			dpll_filter_status = DPLL1_FILTER_STATUS;
   122			break;
   123		case 2:
   124			dpll_filter_status = DPLL2_FILTER_STATUS;
   125			break;
   126		case 3:
   127			dpll_filter_status = DPLL3_FILTER_STATUS;
   128			break;
   129		case 4:
   130			dpll_filter_status = DPLL4_FILTER_STATUS;
   131			break;
   132		case 5:
   133			dpll_filter_status = DPLL5_FILTER_STATUS;
   134			break;
   135		case 6:
   136			dpll_filter_status = DPLL6_FILTER_STATUS;
   137			break;
   138		case 7:
   139			dpll_filter_status = DPLL7_FILTER_STATUS;
   140			break;
   141		case 8:
   142			dpll_filter_status = DPLLSYS_FILTER_STATUS;
   143			break;
   144		default:
   145			return -EINVAL;
   146		}
   147	
 > 148		err = rsmu_read(rsmu->mfd, STATUS + dpll_filter_status, buf, 6);
   149		if (err)
   150			return err;
   151	
   152		/* Convert to frequency control word */
   153		fcw = sign_extend64(get_unaligned_le64(buf), 47);
   154	
   155		/* FCW unit is 2 ^ -53 = 1.1102230246251565404236316680908e-16 */
   156		ffo->ffo = fcw * 111;
   157	
   158		return 0;
   159	}
   160	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 29323 bytes --]

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH next 2/2] misc: Add Renesas Synchronization Management Unit (SMU) support
  2021-03-26 15:34 [PATCH next 2/2] misc: Add Renesas Synchronization Management Unit (SMU) support min.li.xe
  2021-03-27  7:58 ` kernel test robot
@ 2021-03-27  9:53 ` kernel test robot
  2021-03-28 12:13 ` Greg KH
  2 siblings, 0 replies; 13+ messages in thread
From: kernel test robot @ 2021-03-27  9:53 UTC (permalink / raw)
  To: min.li.xe, derek.kiernan, dragan.cvetic, arnd, gregkh
  Cc: kbuild-all, linux-kernel, Min Li

[-- Attachment #1: Type: text/plain, Size: 2341 bytes --]

Hi,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on next-20210326]

url:    https://github.com/0day-ci/linux/commits/min-li-xe-renesas-com/mfd-Add-Renesas-Synchronization-Management-Unit-SMU-support/20210327-150316
base:    931294922e65a23e1aad6398b9ae02df74044679
config: microblaze-randconfig-r003-20210327 (attached as .config)
compiler: microblaze-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/0719d3e2c97f4073f3b495311f260c6c3e0dda28
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review min-li-xe-renesas-com/mfd-Add-Renesas-Synchronization-Management-Unit-SMU-support/20210327-150316
        git checkout 0719d3e2c97f4073f3b495311f260c6c3e0dda28
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=microblaze 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   microblaze-linux-ld: drivers/misc/rsmu_cm.o: in function `rsmu_cm_get_dpll_ffo':
>> (.text+0xb4): undefined reference to `rsmu_read'
   microblaze-linux-ld: drivers/misc/rsmu_cm.o: in function `rsmu_cm_get_dpll_state':
   (.text+0x52c): undefined reference to `rsmu_read'
   microblaze-linux-ld: drivers/misc/rsmu_cm.o: in function `rsmu_cm_set_combomode':
   (.text+0x7c0): undefined reference to `rsmu_read'
>> microblaze-linux-ld: (.text+0x87c): undefined reference to `rsmu_write'
   microblaze-linux-ld: drivers/misc/rsmu_sabre.o: in function `rsmu_sabre_get_dpll_state':
   (.text+0xd0): undefined reference to `rsmu_read'
   microblaze-linux-ld: drivers/misc/rsmu_sabre.o: in function `rsmu_sabre_get_dpll_ffo':
   (.text+0x388): undefined reference to `rsmu_read'
   microblaze-linux-ld: drivers/misc/rsmu_sabre.o: in function `rsmu_sabre_set_combomode':
   (.text+0x714): undefined reference to `rsmu_read'
   microblaze-linux-ld: (.text+0x7ac): undefined reference to `rsmu_write'

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 26169 bytes --]

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH next 2/2] misc: Add Renesas Synchronization Management Unit (SMU) support
  2021-03-26 15:34 [PATCH next 2/2] misc: Add Renesas Synchronization Management Unit (SMU) support min.li.xe
  2021-03-27  7:58 ` kernel test robot
  2021-03-27  9:53 ` kernel test robot
@ 2021-03-28 12:13 ` Greg KH
  2021-03-29 17:03   ` Min Li
  2 siblings, 1 reply; 13+ messages in thread
From: Greg KH @ 2021-03-28 12:13 UTC (permalink / raw)
  To: min.li.xe; +Cc: derek.kiernan, dragan.cvetic, arnd, linux-kernel

On Fri, Mar 26, 2021 at 11:34:11AM -0400, min.li.xe@renesas.com wrote:
> From: Min Li <min.li.xe@renesas.com>
> 
> This driver is developed for the IDT ClockMatrix(TM) and 82P33xxx families
> of timing and synchronization devices.It will be used by Renesas PTP Clock
> Manager for Linux (pcm4l) software to provide support to GNSS assisted
> partial timing support (APTS) and other networking timing functions.
> 
> Current version provides kernel API's to support the following functions
> -set combomode to enable SYNCE clock support
> -read dpll's state to determine if the dpll is locked to the GNSS channel
> -read dpll's ffo (fractional frequency offset) in ppqt
> 
> Signed-off-by: Min Li <min.li.xe@renesas.com>
> ---
> Change log
> -rebase change to linux-next tree
> -remove uncessary condition checks suggested by Greg
> -fix compile error for x86_64

Where is patch 1/2 of this series?

Also, please fix up the errors that the testing bot found, and properly
version your patch submission so I know which one is the "latest" one to
look at.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 13+ messages in thread

* RE: [PATCH next 2/2] misc: Add Renesas Synchronization Management Unit (SMU) support
  2021-03-28 12:13 ` Greg KH
@ 2021-03-29 17:03   ` Min Li
  2021-04-02 14:22     ` Greg KH
  0 siblings, 1 reply; 13+ messages in thread
From: Min Li @ 2021-03-29 17:03 UTC (permalink / raw)
  To: Greg KH; +Cc: derek.kiernan, dragan.cvetic, arnd, linux-kernel

> 
> Where is patch 1/2 of this series?
> 
> Also, please fix up the errors that the testing bot found, and properly version
> your patch submission so I know which one is the "latest" one to look at.
> 

Hi Greg

The first patch is mfd so I was not sure if I should send that to you guys in the first place. 

Anyways, I just sent it over.

Min

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH next 2/2] misc: Add Renesas Synchronization Management Unit (SMU) support
  2021-03-29 17:03   ` Min Li
@ 2021-04-02 14:22     ` Greg KH
  2021-04-03 22:06       ` Min Li
  0 siblings, 1 reply; 13+ messages in thread
From: Greg KH @ 2021-04-02 14:22 UTC (permalink / raw)
  To: Min Li; +Cc: derek.kiernan, dragan.cvetic, arnd, linux-kernel

On Mon, Mar 29, 2021 at 05:03:53PM +0000, Min Li wrote:
> > 
> > Where is patch 1/2 of this series?
> > 
> > Also, please fix up the errors that the testing bot found, and properly version
> > your patch submission so I know which one is the "latest" one to look at.
> > 
> 
> Hi Greg
> 
> The first patch is mfd so I was not sure if I should send that to you guys in the first place. 

Then the patches are independant and they should be sent as such,
otherwise it causes confusion and our tools get messed up when trying to
grab the whole "series" of patches.

Can you please fix this up and just send two independant patches?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 13+ messages in thread

* RE: [PATCH next 2/2] misc: Add Renesas Synchronization Management Unit (SMU) support
  2021-04-02 14:22     ` Greg KH
@ 2021-04-03 22:06       ` Min Li
  0 siblings, 0 replies; 13+ messages in thread
From: Min Li @ 2021-04-03 22:06 UTC (permalink / raw)
  To: Greg KH; +Cc: derek.kiernan, dragan.cvetic, arnd, linux-kernel


> 
> Then the patches are independant and they should be sent as such,
> otherwise it causes confusion and our tools get messed up when trying to
> grab the whole "series" of patches.
> 
> Can you please fix this up and just send two independant patches?
> 

Hi Greg

These 2 patches are not independent. Patch 2/2 depends on patch 1/2 to build and work successfully.
They just belong to different domain, patch 1/2 belongs to MFD while patch 2/2 belongs to MISC.
That is why I wasn't sure if I should send both patches to you in the first place. Anyways, I just sent both patches again.

Thanks

Min

^ permalink raw reply	[flat|nested] 13+ messages in thread

* RE: [PATCH next 2/2] misc: Add Renesas Synchronization Management Unit (SMU) support
  2021-04-05  6:37       ` Greg KH
@ 2021-04-05 13:53         ` Min Li
  0 siblings, 0 replies; 13+ messages in thread
From: Min Li @ 2021-04-05 13:53 UTC (permalink / raw)
  To: Greg KH; +Cc: derek.kiernan, dragan.cvetic, arnd, linux-kernel

> > >
> > > Any specific reason you are not using the misc_device api?  That
> > > would clean up this driver a lot, there's no need to create a whole
> > > class just for a single driver.
> > >
> >
> > Hi Greg
> >
> > No specific reason. I just didn't know the existence of misc_register API.
> 
> Your file is in drivers/misc/ :)
> 
> > Do you recommend using this API to create the device?
> 
> Yes.
> 
> > If yes, can you tell me how to obtain a appropriate MINOR number from
> > miscdevice.h?
> 
> No need to reserve one, we don't do that anymore, just ask for a dynamic
> value and the next availble one will be given to your driver automatically.
> 
> thanks,
> 
> greg k-h

Thanks, I will change to use misc_register and get back to you.

Thanks

Min

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH next 2/2] misc: Add Renesas Synchronization Management Unit (SMU) support
  2021-04-05  1:52     ` Min Li
@ 2021-04-05  6:37       ` Greg KH
  2021-04-05 13:53         ` Min Li
  0 siblings, 1 reply; 13+ messages in thread
From: Greg KH @ 2021-04-05  6:37 UTC (permalink / raw)
  To: Min Li; +Cc: derek.kiernan, dragan.cvetic, arnd, linux-kernel

On Mon, Apr 05, 2021 at 01:52:39AM +0000, Min Li wrote:
> > 
> > Any specific reason you are not using the misc_device api?  That would clean
> > up this driver a lot, there's no need to create a whole class just for a single
> > driver.
> > 
> 
> Hi Greg
> 
> No specific reason. I just didn't know the existence of misc_register API. 

Your file is in drivers/misc/ :)

> Do you recommend using this API to create the device?

Yes.

> If yes, can you tell me how to obtain a appropriate MINOR number from
> miscdevice.h?

No need to reserve one, we don't do that anymore, just ask for a dynamic
value and the next availble one will be given to your driver
automatically.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 13+ messages in thread

* RE: [PATCH next 2/2] misc: Add Renesas Synchronization Management Unit (SMU) support
  2021-04-04 10:08   ` Greg KH
@ 2021-04-05  1:52     ` Min Li
  2021-04-05  6:37       ` Greg KH
  0 siblings, 1 reply; 13+ messages in thread
From: Min Li @ 2021-04-05  1:52 UTC (permalink / raw)
  To: Greg KH; +Cc: derek.kiernan, dragan.cvetic, arnd, linux-kernel

> 
> Any specific reason you are not using the misc_device api?  That would clean
> up this driver a lot, there's no need to create a whole class just for a single
> driver.
> 

Hi Greg

No specific reason. I just didn't know the existence of misc_register API. 
Do you recommend using this API to create the device? If yes, 
can you tell me how to obtain a appropriate MINOR number from miscdevice.h?

Thanks

Min

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH next 2/2] misc: Add Renesas Synchronization Management Unit (SMU) support
  2021-04-03 22:08 ` [PATCH next 2/2] misc: " min.li.xe
@ 2021-04-04 10:08   ` Greg KH
  2021-04-05  1:52     ` Min Li
  0 siblings, 1 reply; 13+ messages in thread
From: Greg KH @ 2021-04-04 10:08 UTC (permalink / raw)
  To: min.li.xe; +Cc: derek.kiernan, dragan.cvetic, arnd, linux-kernel

On Sat, Apr 03, 2021 at 06:08:34PM -0400, min.li.xe@renesas.com wrote:
> From: Min Li <min.li.xe@renesas.com>
> 
> This driver is developed for the IDT ClockMatrix(TM) and 82P33xxx families
> of timing and synchronization devices.It will be used by Renesas PTP Clock
> Manager for Linux (pcm4l) software to provide support to GNSS assisted
> partial timing support (APTS) and other networking timing functions.
> 
> Current version provides kernel API's to support the following functions
> -set combomode to enable SYNCE clock support
> -read dpll's state to determine if the dpll is locked to the GNSS channel
> -read dpll's ffo (fractional frequency offset) in ppqt
> 
> Signed-off-by: Min Li <min.li.xe@renesas.com>

Any specific reason you are not using the misc_device api?  That would
clean up this driver a lot, there's no need to create a whole class just
for a single driver.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH next 2/2] misc: Add Renesas Synchronization Management Unit (SMU) support
  2021-04-03 22:08 [PATCH next 1/2] mfd: " min.li.xe
@ 2021-04-03 22:08 ` min.li.xe
  2021-04-04 10:08   ` Greg KH
  0 siblings, 1 reply; 13+ messages in thread
From: min.li.xe @ 2021-04-03 22:08 UTC (permalink / raw)
  To: derek.kiernan, dragan.cvetic, arnd, gregkh; +Cc: linux-kernel, Min Li

From: Min Li <min.li.xe@renesas.com>

This driver is developed for the IDT ClockMatrix(TM) and 82P33xxx families
of timing and synchronization devices.It will be used by Renesas PTP Clock
Manager for Linux (pcm4l) software to provide support to GNSS assisted
partial timing support (APTS) and other networking timing functions.

Current version provides kernel API's to support the following functions
-set combomode to enable SYNCE clock support
-read dpll's state to determine if the dpll is locked to the GNSS channel
-read dpll's ffo (fractional frequency offset) in ppqt

Signed-off-by: Min Li <min.li.xe@renesas.com>
---
Change log
-rebase change to linux-next tree
-remove uncessary condition checks suggested by Greg
-fix compile error for x86_64

 drivers/misc/Kconfig      |   9 ++
 drivers/misc/Makefile     |   2 +
 drivers/misc/rsmu_cdev.c  | 321 ++++++++++++++++++++++++++++++++++++++++++++++
 drivers/misc/rsmu_cdev.h  |  72 +++++++++++
 drivers/misc/rsmu_cm.c    | 166 ++++++++++++++++++++++++
 drivers/misc/rsmu_sabre.c | 128 ++++++++++++++++++
 include/uapi/linux/rsmu.h |  64 +++++++++
 7 files changed, 762 insertions(+)
 create mode 100644 drivers/misc/rsmu_cdev.c
 create mode 100644 drivers/misc/rsmu_cdev.h
 create mode 100644 drivers/misc/rsmu_cm.c
 create mode 100644 drivers/misc/rsmu_sabre.c
 create mode 100644 include/uapi/linux/rsmu.h

diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index f532c59..49b523a 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -445,6 +445,15 @@ config HISI_HIKEY_USB
 	  switching between the dual-role USB-C port and the USB-A host ports
 	  using only one USB controller.
 
+config RSMU
+	tristate "Renesas Synchronization Management Unit (SMU)"
+	help
+	  This option enables support for the IDT ClockMatrix(TM) and 82P33xxx
+	  families of timing and synchronization devices. It will be used by
+	  Renesas PTP Clock Manager for Linux (pcm4l) software to provide support
+	  for GNSS assisted partial timing support (APTS) and other networking
+	  timing functions.
+
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 99b6f15..21b8ed4 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -56,3 +56,5 @@ obj-$(CONFIG_HABANA_AI)		+= habanalabs/
 obj-$(CONFIG_UACCE)		+= uacce/
 obj-$(CONFIG_XILINX_SDFEC)	+= xilinx_sdfec.o
 obj-$(CONFIG_HISI_HIKEY_USB)	+= hisi_hikey_usb.o
+rsmu-objs			:= rsmu_cdev.o rsmu_cm.o rsmu_sabre.o
+obj-$(CONFIG_RSMU)		+= rsmu.o
diff --git a/drivers/misc/rsmu_cdev.c b/drivers/misc/rsmu_cdev.c
new file mode 100644
index 0000000..2df5788
--- /dev/null
+++ b/drivers/misc/rsmu_cdev.c
@@ -0,0 +1,321 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * This driver is developed for the IDT ClockMatrix(TM) and 82P33xxx families
+ * of timing and synchronization devices. It will be used by Renesas PTP Clock
+ * Manager for Linux (pcm4l) software to provide support to GNSS assisted
+ * partial timing support (APTS) and other networking timing functions.
+ *
+ * Please note it must work with Renesas MFD driver to access device through
+ * I2C/SPI.
+ *
+ * Copyright (C) 2019 Integrated Device Technology, Inc., a Renesas Company.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/mfd/rsmu.h>
+#include <uapi/linux/rsmu.h>
+
+#include "rsmu_cdev.h"
+
+#define DRIVER_NAME	"rsmu"
+#define DRIVER_MAX_DEV	BIT(MINORBITS)
+
+static struct class *rsmu_class;
+static dev_t rsmu_cdevt;
+static struct rsmu_ops *ops_array[] = {
+	[RSMU_CM] = &cm_ops,
+	[RSMU_SABRE] = &sabre_ops,
+};
+
+static int
+rsmu_set_combomode(struct rsmu_cdev *rsmu, void __user *arg)
+{
+	struct rsmu_ops *ops = rsmu->ops;
+	struct rsmu_combomode mode;
+	int err;
+
+	if (copy_from_user(&mode, arg, sizeof(mode)))
+		return -EFAULT;
+
+	if (ops->set_combomode == NULL)
+		return -ENOTSUPP;
+
+	mutex_lock(rsmu->lock);
+	err = ops->set_combomode(rsmu, mode.dpll, mode.mode);
+	mutex_unlock(rsmu->lock);
+
+	return err;
+}
+
+static int
+rsmu_get_dpll_state(struct rsmu_cdev *rsmu, void __user *arg)
+{
+	struct rsmu_ops *ops = rsmu->ops;
+	struct rsmu_get_state state_request;
+	u8 state;
+	int err;
+
+	if (copy_from_user(&state_request, arg, sizeof(state_request)))
+		return -EFAULT;
+
+	if (ops->get_dpll_state == NULL)
+		return -ENOTSUPP;
+
+	mutex_lock(rsmu->lock);
+	err = ops->get_dpll_state(rsmu, state_request.dpll, &state);
+	mutex_unlock(rsmu->lock);
+
+	state_request.state = state;
+	if (copy_to_user(arg, &state_request, sizeof(state_request)))
+		return -EFAULT;
+
+	return err;
+}
+
+static int
+rsmu_get_dpll_ffo(struct rsmu_cdev *rsmu, void __user *arg)
+{
+	struct rsmu_ops *ops = rsmu->ops;
+	struct rsmu_get_ffo ffo_request;
+	int err;
+
+	if (copy_from_user(&ffo_request, arg, sizeof(ffo_request)))
+		return -EFAULT;
+
+	if (ops->get_dpll_ffo == NULL)
+		return -ENOTSUPP;
+
+	mutex_lock(rsmu->lock);
+	err = ops->get_dpll_ffo(rsmu, ffo_request.dpll, &ffo_request);
+	mutex_unlock(rsmu->lock);
+
+	if (copy_to_user(arg, &ffo_request, sizeof(ffo_request)))
+		return -EFAULT;
+
+	return err;
+}
+
+static int
+rsmu_open(struct inode *iptr, struct file *fptr)
+{
+	struct rsmu_cdev *rsmu;
+
+	rsmu = container_of(iptr->i_cdev, struct rsmu_cdev, rsmu_cdev);
+	fptr->private_data = rsmu;
+	return 0;
+}
+
+static int
+rsmu_release(struct inode *iptr, struct file *fptr)
+{
+	return 0;
+}
+
+static long
+rsmu_ioctl(struct file *fptr, unsigned int cmd, unsigned long data)
+{
+	struct rsmu_cdev *rsmu = fptr->private_data;
+	void __user *arg = (void __user *)data;
+	int err = 0;
+
+	switch (cmd) {
+	case RSMU_SET_COMBOMODE:
+		err = rsmu_set_combomode(rsmu, arg);
+		break;
+	case RSMU_GET_STATE:
+		err = rsmu_get_dpll_state(rsmu, arg);
+		break;
+	case RSMU_GET_FFO:
+		err = rsmu_get_dpll_ffo(rsmu, arg);
+		break;
+	default:
+		/* Should not get here */
+		dev_err(rsmu->dev, "Undefined RSMU IOCTL");
+		err = -EINVAL;
+		break;
+	}
+
+	return err;
+}
+
+static long rsmu_compat_ioctl(struct file *fptr, unsigned int cmd,
+			      unsigned long data)
+{
+	return rsmu_ioctl(fptr, cmd, data);
+}
+
+static const struct file_operations rsmu_fops = {
+	.owner = THIS_MODULE,
+	.open = rsmu_open,
+	.release = rsmu_release,
+	.unlocked_ioctl = rsmu_ioctl,
+	.compat_ioctl =	rsmu_compat_ioctl,
+};
+
+static int rsmu_init_ops(struct rsmu_cdev *rsmu)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ops_array); i++)
+		if (ops_array[i]->type == rsmu->type)
+			break;
+
+	if (i == ARRAY_SIZE(ops_array))
+		return -EINVAL;
+
+	rsmu->ops = ops_array[i];
+	return 0;
+}
+
+static int
+rsmu_probe(struct platform_device *pdev)
+{
+	struct rsmu_pdata *pdata = dev_get_platdata(&pdev->dev);
+	struct rsmu_cdev *rsmu;
+	struct device *rsmu_cdev;
+	int err;
+
+	rsmu = devm_kzalloc(&pdev->dev, sizeof(*rsmu), GFP_KERNEL);
+	if (!rsmu)
+		return -ENOMEM;
+
+	rsmu->dev = &pdev->dev;
+	rsmu->mfd = pdev->dev.parent;
+	rsmu->type = pdata->type;
+	rsmu->lock = pdata->lock;
+	rsmu->index = pdata->index;
+
+	/* Save driver private data */
+	platform_set_drvdata(pdev, rsmu);
+
+	cdev_init(&rsmu->rsmu_cdev, &rsmu_fops);
+	rsmu->rsmu_cdev.owner = THIS_MODULE;
+	err = cdev_add(&rsmu->rsmu_cdev,
+		       MKDEV(MAJOR(rsmu_cdevt), 0), 1);
+	if (err < 0) {
+		dev_err(rsmu->dev, "cdev_add failed");
+		err = -EIO;
+		goto err_rsmu_dev;
+	}
+
+	if (!rsmu_class) {
+		err = -EIO;
+		dev_err(rsmu->dev, "rsmu class not created correctly");
+		goto err_rsmu_cdev;
+	}
+
+	rsmu_cdev = device_create(rsmu_class, rsmu->dev,
+				  MKDEV(MAJOR(rsmu_cdevt), 0),
+				  rsmu, "rsmu%d", rsmu->index);
+	if (IS_ERR(rsmu_cdev)) {
+		dev_err(rsmu->dev, "Unable to create char device");
+		err = PTR_ERR(rsmu_cdev);
+		goto err_rsmu_cdev;
+	}
+
+	err = rsmu_init_ops(rsmu);
+	if (err) {
+		dev_err(rsmu->dev, "Unable to match type %d", rsmu->type);
+		goto err_rsmu_cdev;
+	}
+
+	dev_info(rsmu->dev, "Probe SMU type %d successful\n", rsmu->type);
+	return 0;
+
+	/* Failure cleanup */
+err_rsmu_cdev:
+	cdev_del(&rsmu->rsmu_cdev);
+err_rsmu_dev:
+	return err;
+}
+
+static int
+rsmu_remove(struct platform_device *pdev)
+{
+	struct rsmu_cdev *rsmu = platform_get_drvdata(pdev);
+	struct device *dev = &pdev->dev;
+
+	if (!rsmu_class) {
+		dev_err(dev, "rsmu_class is NULL");
+		return -EIO;
+	}
+
+	device_destroy(rsmu_class, MKDEV(MAJOR(rsmu_cdevt), 0));
+	cdev_del(&rsmu->rsmu_cdev);
+
+	return 0;
+}
+
+static const struct platform_device_id rsmu_id_table[] = {
+	{ "rsmu-cdev0", },
+	{ "rsmu-cdev1", },
+	{ "rsmu-cdev2", },
+	{ "rsmu-cdev3", },
+	{}
+};
+MODULE_DEVICE_TABLE(platform, rsmu_id_table);
+
+static struct platform_driver rsmu_driver = {
+	.driver = {
+		.name = DRIVER_NAME,
+	},
+	.probe = rsmu_probe,
+	.remove =  rsmu_remove,
+	.id_table = rsmu_id_table,
+};
+
+static int __init rsmu_init(void)
+{
+	int err;
+
+	rsmu_class = class_create(THIS_MODULE, DRIVER_NAME);
+	if (IS_ERR(rsmu_class)) {
+		err = PTR_ERR(rsmu_class);
+		pr_err("Unable to register rsmu class");
+		return err;
+	}
+
+	err = alloc_chrdev_region(&rsmu_cdevt, 0, DRIVER_MAX_DEV, DRIVER_NAME);
+	if (err < 0) {
+		pr_err("Unable to get major number");
+		goto err_rsmu_class;
+	}
+
+	err = platform_driver_register(&rsmu_driver);
+	if (err < 0) {
+		pr_err("Unabled to register %s driver", DRIVER_NAME);
+		goto err_rsmu_drv;
+	}
+	return 0;
+
+	/* Error Path */
+err_rsmu_drv:
+	unregister_chrdev_region(rsmu_cdevt, DRIVER_MAX_DEV);
+err_rsmu_class:
+	class_destroy(rsmu_class);
+	return err;
+}
+
+static void __exit rsmu_exit(void)
+{
+	platform_driver_unregister(&rsmu_driver);
+	unregister_chrdev_region(rsmu_cdevt, DRIVER_MAX_DEV);
+	class_destroy(rsmu_class);
+	rsmu_class = NULL;
+}
+
+module_init(rsmu_init);
+module_exit(rsmu_exit);
+
+MODULE_DESCRIPTION("Renesas SMU character device driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/rsmu_cdev.h b/drivers/misc/rsmu_cdev.h
new file mode 100644
index 0000000..3ced817
--- /dev/null
+++ b/drivers/misc/rsmu_cdev.h
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * This driver is developed for the IDT ClockMatrix(TM) of
+ * timing and synchronization devices.
+ *
+ * Copyright (C) 2019 Integrated Device Technology, Inc., a Renesas Company.
+ */
+#ifndef __LINUX_RSMU_CDEV_H
+#define __LINUX_RSMU_CDEV_H
+
+#include <linux/cdev.h>
+
+struct rsmu_ops;
+
+/**
+ * struct rsmu_cdev - Driver data for RSMU character device
+ * @dev: pointer to platform device
+ * @mfd: pointer to MFD device
+ * @rsmu_cdev: character device handle
+ * @lock: mutex to protect operations from being interrupted
+ * @type: rsmu device type
+ * @ops: rsmu device methods
+ * @index: rsmu device index
+ */
+struct rsmu_cdev {
+	struct device *dev;
+	struct device *mfd;
+	struct cdev rsmu_cdev;
+	struct mutex *lock;
+	enum rsmu_type type;
+	struct rsmu_ops *ops;
+	u8 index;
+};
+
+extern struct rsmu_ops cm_ops;
+extern struct rsmu_ops sabre_ops;
+
+struct rsmu_ops {
+	enum rsmu_type type;
+	int (*set_combomode)(struct rsmu_cdev *rsmu, u8 dpll, u8 mode);
+	int (*get_dpll_state)(struct rsmu_cdev *rsmu, u8 dpll, u8 *state);
+	int (*get_dpll_ffo)(struct rsmu_cdev *rsmu, u8 dpll,
+			    struct rsmu_get_ffo *ffo);
+};
+
+/**
+ * Enumerated type listing DPLL combination modes
+ */
+enum rsmu_dpll_combomode {
+	E_COMBOMODE_CURRENT = 0,
+	E_COMBOMODE_FASTAVG,
+	E_COMBOMODE_SLOWAVG,
+	E_COMBOMODE_HOLDOVER,
+	E_COMBOMODE_MAX
+};
+
+/**
+ * An id used to identify the respective child class states.
+ */
+enum rsmu_class_state {
+	E_SRVLOINITIALSTATE = 0,
+	E_SRVLOUNQUALIFIEDSTATE = 1,
+	E_SRVLOLOCKACQSTATE = 2,
+	E_SRVLOFREQUENCYLOCKEDSTATE = 3,
+	E_SRVLOTIMELOCKEDSTATE = 4,
+	E_SRVLOHOLDOVERINSPECSTATE = 5,
+	E_SRVLOHOLDOVEROUTOFSPECSTATE = 6,
+	E_SRVLOFREERUNSTATE = 7,
+	E_SRVNUMBERLOSTATES = 8,
+	E_SRVLOSTATEINVALID = 9,
+};
+#endif
diff --git a/drivers/misc/rsmu_cm.c b/drivers/misc/rsmu_cm.c
new file mode 100644
index 0000000..d5af624
--- /dev/null
+++ b/drivers/misc/rsmu_cm.c
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * This driver is developed for the IDT ClockMatrix(TM) of
+ * timing and synchronization devices.
+ *
+ * Copyright (C) 2019 Integrated Device Technology, Inc., a Renesas Company.
+ */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/mfd/idt8a340_reg.h>
+#include <linux/mfd/rsmu.h>
+#include <uapi/linux/rsmu.h>
+#include <asm/unaligned.h>
+
+#include "rsmu_cdev.h"
+
+static int rsmu_cm_set_combomode(struct rsmu_cdev *rsmu, u8 dpll, u8 mode)
+{
+	u16 dpll_ctrl_n;
+	u8 cfg;
+	int err;
+
+	switch (dpll) {
+	case 0:
+		dpll_ctrl_n = DPLL_CTRL_0;
+		break;
+	case 1:
+		dpll_ctrl_n = DPLL_CTRL_1;
+		break;
+	case 2:
+		dpll_ctrl_n = DPLL_CTRL_2;
+		break;
+	case 3:
+		dpll_ctrl_n = DPLL_CTRL_3;
+		break;
+	case 4:
+		dpll_ctrl_n = DPLL_CTRL_4;
+		break;
+	case 5:
+		dpll_ctrl_n = DPLL_CTRL_5;
+		break;
+	case 6:
+		dpll_ctrl_n = DPLL_CTRL_6;
+		break;
+	case 7:
+		dpll_ctrl_n = DPLL_CTRL_7;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (mode >= E_COMBOMODE_MAX)
+		return -EINVAL;
+
+	err = rsmu_read(rsmu->mfd, dpll_ctrl_n + DPLL_CTRL_COMBO_MASTER_CFG,
+			&cfg, sizeof(cfg));
+	if (err)
+		return err;
+
+	/* Only need to enable/disable COMBO_MODE_HOLD. */
+	if (mode)
+		cfg |= COMBO_MASTER_HOLD;
+	else
+		cfg &= ~COMBO_MASTER_HOLD;
+
+	return rsmu_write(rsmu->mfd, dpll_ctrl_n + DPLL_CTRL_COMBO_MASTER_CFG,
+			  &cfg, sizeof(cfg));
+}
+
+static int rsmu_cm_get_dpll_state(struct rsmu_cdev *rsmu, u8 dpll, u8 *state)
+{
+	u8 cfg;
+	int err;
+
+	/* 8 is sys dpll */
+	if (dpll > 8)
+		return -EINVAL;
+
+	err = rsmu_read(rsmu->mfd,
+			  STATUS + DPLL0_STATUS + dpll,
+			  &cfg, sizeof(cfg));
+	if (err)
+		return err;
+
+	switch (cfg & DPLL_STATE_MASK) {
+	case DPLL_STATE_FREERUN:
+		*state = E_SRVLOUNQUALIFIEDSTATE;
+		break;
+	case DPLL_STATE_LOCKACQ:
+	case DPLL_STATE_LOCKREC:
+		*state = E_SRVLOLOCKACQSTATE;
+		break;
+	case DPLL_STATE_LOCKED:
+		*state = E_SRVLOTIMELOCKEDSTATE;
+		break;
+	case DPLL_STATE_HOLDOVER:
+		*state = E_SRVLOHOLDOVERINSPECSTATE;
+		break;
+	default:
+		*state = E_SRVLOSTATEINVALID;
+		break;
+	}
+
+	return 0;
+}
+
+static int rsmu_cm_get_dpll_ffo(struct rsmu_cdev *rsmu, u8 dpll,
+				struct rsmu_get_ffo *ffo)
+{
+	u8 buf[8] = {0};
+	s64 fcw = 0;
+	u16 dpll_filter_status;
+	int err;
+
+	switch (dpll) {
+	case 0:
+		dpll_filter_status = DPLL0_FILTER_STATUS;
+		break;
+	case 1:
+		dpll_filter_status = DPLL1_FILTER_STATUS;
+		break;
+	case 2:
+		dpll_filter_status = DPLL2_FILTER_STATUS;
+		break;
+	case 3:
+		dpll_filter_status = DPLL3_FILTER_STATUS;
+		break;
+	case 4:
+		dpll_filter_status = DPLL4_FILTER_STATUS;
+		break;
+	case 5:
+		dpll_filter_status = DPLL5_FILTER_STATUS;
+		break;
+	case 6:
+		dpll_filter_status = DPLL6_FILTER_STATUS;
+		break;
+	case 7:
+		dpll_filter_status = DPLL7_FILTER_STATUS;
+		break;
+	case 8:
+		dpll_filter_status = DPLLSYS_FILTER_STATUS;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	err = rsmu_read(rsmu->mfd, STATUS + dpll_filter_status, buf, 6);
+	if (err)
+		return err;
+
+	/* Convert to frequency control word */
+	fcw = sign_extend64(get_unaligned_le64(buf), 47);
+
+	/* FCW unit is 2 ^ -53 = 1.1102230246251565404236316680908e-16 */
+	ffo->ffo = fcw * 111;
+
+	return 0;
+}
+
+struct rsmu_ops cm_ops = {
+	.type = RSMU_CM,
+	.set_combomode = rsmu_cm_set_combomode,
+	.get_dpll_state = rsmu_cm_get_dpll_state,
+	.get_dpll_ffo = rsmu_cm_get_dpll_ffo,
+};
diff --git a/drivers/misc/rsmu_sabre.c b/drivers/misc/rsmu_sabre.c
new file mode 100644
index 0000000..aa772f1
--- /dev/null
+++ b/drivers/misc/rsmu_sabre.c
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * This driver is developed for the IDT 82P33XXX series of
+ * timing and synchronization devices.
+ *
+ * Copyright (C) 2019 Integrated Device Technology, Inc., a Renesas Company.
+ */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/mfd/idt82p33_reg.h>
+#include <linux/mfd/rsmu.h>
+#include <uapi/linux/rsmu.h>
+#include <asm/unaligned.h>
+
+#include "rsmu_cdev.h"
+
+static int rsmu_sabre_set_combomode(struct rsmu_cdev *rsmu, u8 dpll, u8 mode)
+{
+	u16 dpll_ctrl_n;
+	u8 cfg;
+	int err;
+
+	switch (dpll) {
+	case 0:
+		dpll_ctrl_n = DPLL1_OPERATING_MODE_CNFG;
+		break;
+	case 1:
+		dpll_ctrl_n = DPLL2_OPERATING_MODE_CNFG;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (mode >= E_COMBOMODE_MAX)
+		return -EINVAL;
+
+	err = rsmu_read(rsmu->mfd, dpll_ctrl_n, &cfg, sizeof(cfg));
+	if (err)
+		return err;
+
+	cfg &= ~(COMBO_MODE_MASK << COMBO_MODE_SHIFT);
+	cfg |= mode << COMBO_MODE_SHIFT;
+
+	return rsmu_write(rsmu->mfd, dpll_ctrl_n, &cfg, sizeof(cfg));
+}
+
+static int rsmu_sabre_get_dpll_state(struct rsmu_cdev *rsmu, u8 dpll, u8 *state)
+{
+	u16 dpll_sts_n;
+	u8 cfg;
+	int err;
+
+	switch (dpll) {
+	case 0:
+		dpll_sts_n = DPLL1_OPERATING_STS;
+		break;
+	case 1:
+		dpll_sts_n = DPLL2_OPERATING_STS;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	err = rsmu_read(rsmu->mfd, dpll_sts_n, &cfg, sizeof(cfg));
+	if (err)
+		return err;
+
+	switch (cfg & OPERATING_STS_MASK) {
+	case DPLL_STATE_FREERUN:
+		*state = E_SRVLOUNQUALIFIEDSTATE;
+		break;
+	case DPLL_STATE_PRELOCKED2:
+	case DPLL_STATE_PRELOCKED:
+		*state = E_SRVLOLOCKACQSTATE;
+		break;
+	case DPLL_STATE_LOCKED:
+		*state = E_SRVLOTIMELOCKEDSTATE;
+		break;
+	case DPLL_STATE_HOLDOVER:
+		*state = E_SRVLOHOLDOVERINSPECSTATE;
+		break;
+	default:
+		*state = E_SRVLOSTATEINVALID;
+		break;
+	}
+
+	return 0;
+}
+
+static int rsmu_sabre_get_dpll_ffo(struct rsmu_cdev *rsmu, u8 dpll,
+				   struct rsmu_get_ffo *ffo)
+{
+	u8 buf[8] = {0};
+	s64 fcw = 0;
+	u16 dpll_freq_n;
+	int err;
+
+	switch (dpll) {
+	case 0:
+		dpll_freq_n = DPLL1_CURRENT_FREQ_STS;
+		break;
+	case 1:
+		dpll_freq_n = DPLL2_CURRENT_FREQ_STS;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	err = rsmu_read(rsmu->mfd, dpll_freq_n, buf, 5);
+	if (err)
+		return err;
+
+	/* Convert to frequency control word */
+	fcw = sign_extend64(get_unaligned_le64(buf), 39);
+
+	/* FCW unit is 77760 / ( 1638400 * 2^48) = 1.68615121864946 * 10^-16 */
+	ffo->ffo = div_s64(fcw * 168615, 1000);
+
+	return 0;
+}
+
+struct rsmu_ops sabre_ops = {
+	.type = RSMU_SABRE,
+	.set_combomode = rsmu_sabre_set_combomode,
+	.get_dpll_state = rsmu_sabre_get_dpll_state,
+	.get_dpll_ffo = rsmu_sabre_get_dpll_ffo,
+};
diff --git a/include/uapi/linux/rsmu.h b/include/uapi/linux/rsmu.h
new file mode 100644
index 0000000..02c9e38
--- /dev/null
+++ b/include/uapi/linux/rsmu.h
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/*
+ * Driver for the IDT ClockMatrix(TM) and 82p33xxx families of
+ * timing and synchronization devices.
+ *
+ * Copyright (C) 2019 Integrated Device Technology, Inc., a Renesas Company.
+ */
+
+#ifndef __UAPI_LINUX_RSMU_CDEV_H
+#define __UAPI_LINUX_RSMU_CDEV_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+/* Set dpll combomode */
+struct rsmu_combomode {
+	__u8 dpll;
+	__u8 mode;
+};
+
+/* Get dpll state */
+struct rsmu_get_state {
+	__u8 dpll;
+	__u8 state;
+};
+
+/* Get dpll ffo (fractional frequency offset) in ppqt*/
+struct rsmu_get_ffo {
+	__u8 dpll;
+	__s64 ffo;
+};
+
+/*
+ * RSMU IOCTL List
+ */
+#define RSMU_MAGIC '?'
+
+/**
+ * @Description
+ * ioctl to set SMU combo mode.
+ *
+ * @Parameters
+ * pointer to struct rsmu_combomode that contains dpll combomode setting
+ */
+#define RSMU_SET_COMBOMODE  _IOW(RSMU_MAGIC, 1, struct rsmu_combomode)
+
+/**
+ * @Description
+ * ioctl to get SMU dpll state.
+ *
+ * @Parameters
+ * pointer to struct rsmu_get_state that contains dpll state
+ */
+#define RSMU_GET_STATE  _IOR(RSMU_MAGIC, 2, struct rsmu_get_state)
+
+/**
+ * @Description
+ * ioctl to get SMU dpll ffo.
+ *
+ * @Parameters
+ * pointer to struct rsmu_get_ffo that contains dpll ffo in ppqt
+ */
+#define RSMU_GET_FFO  _IOR(RSMU_MAGIC, 3, struct rsmu_get_ffo)
+#endif /* __UAPI_LINUX_RSMU_CDEV_H */
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH next 2/2] misc: Add Renesas Synchronization Management Unit (SMU) support
@ 2021-03-23 16:40 min.li.xe
  0 siblings, 0 replies; 13+ messages in thread
From: min.li.xe @ 2021-03-23 16:40 UTC (permalink / raw)
  To: derek.kiernan, dragan.cvetic, arnd, gregkh; +Cc: linux-kernel, Min Li

From: Min Li <min.li.xe@renesas.com>

This driver is developed for the IDT ClockMatrix(TM) and 82P33xxx families
of timing and synchronization devices.It will be used by Renesas PTP Clock
Manager for Linux (pcm4l) software to provide support to GNSS assisted
partial timing support (APTS) and other networking timing functions.

Current version provides kernel API's to support the following functions
-set combomode to enable SYNCE clock support
-read dpll's state to determine if the dpll is locked to the GNSS channel
-read dpll's ffo (fractional frequency offset) in ppqt

Signed-off-by: Min Li <min.li.xe@renesas.com>
---
Change log
-rebase change to linux-next tree
-remove uncessary condition checks suggested by Greg
-fix compile error for x86_64

 drivers/misc/Kconfig      |   9 ++
 drivers/misc/Makefile     |   2 +
 drivers/misc/rsmu_cdev.c  | 321 ++++++++++++++++++++++++++++++++++++++++++++++
 drivers/misc/rsmu_cdev.h  |  72 +++++++++++
 drivers/misc/rsmu_cm.c    | 166 ++++++++++++++++++++++++
 drivers/misc/rsmu_sabre.c | 128 ++++++++++++++++++
 include/uapi/linux/rsmu.h |  64 +++++++++
 7 files changed, 762 insertions(+)
 create mode 100644 drivers/misc/rsmu_cdev.c
 create mode 100644 drivers/misc/rsmu_cdev.h
 create mode 100644 drivers/misc/rsmu_cm.c
 create mode 100644 drivers/misc/rsmu_sabre.c
 create mode 100644 include/uapi/linux/rsmu.h

diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index f532c59..49b523a 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -445,6 +445,15 @@ config HISI_HIKEY_USB
 	  switching between the dual-role USB-C port and the USB-A host ports
 	  using only one USB controller.
 
+config RSMU
+	tristate "Renesas Synchronization Management Unit (SMU)"
+	help
+	  This option enables support for the IDT ClockMatrix(TM) and 82P33xxx
+	  families of timing and synchronization devices. It will be used by
+	  Renesas PTP Clock Manager for Linux (pcm4l) software to provide support
+	  for GNSS assisted partial timing support (APTS) and other networking
+	  timing functions.
+
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 99b6f15..21b8ed4 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -56,3 +56,5 @@ obj-$(CONFIG_HABANA_AI)		+= habanalabs/
 obj-$(CONFIG_UACCE)		+= uacce/
 obj-$(CONFIG_XILINX_SDFEC)	+= xilinx_sdfec.o
 obj-$(CONFIG_HISI_HIKEY_USB)	+= hisi_hikey_usb.o
+rsmu-objs			:= rsmu_cdev.o rsmu_cm.o rsmu_sabre.o
+obj-$(CONFIG_RSMU)		+= rsmu.o
diff --git a/drivers/misc/rsmu_cdev.c b/drivers/misc/rsmu_cdev.c
new file mode 100644
index 0000000..2df5788
--- /dev/null
+++ b/drivers/misc/rsmu_cdev.c
@@ -0,0 +1,321 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * This driver is developed for the IDT ClockMatrix(TM) and 82P33xxx families
+ * of timing and synchronization devices. It will be used by Renesas PTP Clock
+ * Manager for Linux (pcm4l) software to provide support to GNSS assisted
+ * partial timing support (APTS) and other networking timing functions.
+ *
+ * Please note it must work with Renesas MFD driver to access device through
+ * I2C/SPI.
+ *
+ * Copyright (C) 2019 Integrated Device Technology, Inc., a Renesas Company.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/mfd/rsmu.h>
+#include <uapi/linux/rsmu.h>
+
+#include "rsmu_cdev.h"
+
+#define DRIVER_NAME	"rsmu"
+#define DRIVER_MAX_DEV	BIT(MINORBITS)
+
+static struct class *rsmu_class;
+static dev_t rsmu_cdevt;
+static struct rsmu_ops *ops_array[] = {
+	[RSMU_CM] = &cm_ops,
+	[RSMU_SABRE] = &sabre_ops,
+};
+
+static int
+rsmu_set_combomode(struct rsmu_cdev *rsmu, void __user *arg)
+{
+	struct rsmu_ops *ops = rsmu->ops;
+	struct rsmu_combomode mode;
+	int err;
+
+	if (copy_from_user(&mode, arg, sizeof(mode)))
+		return -EFAULT;
+
+	if (ops->set_combomode == NULL)
+		return -ENOTSUPP;
+
+	mutex_lock(rsmu->lock);
+	err = ops->set_combomode(rsmu, mode.dpll, mode.mode);
+	mutex_unlock(rsmu->lock);
+
+	return err;
+}
+
+static int
+rsmu_get_dpll_state(struct rsmu_cdev *rsmu, void __user *arg)
+{
+	struct rsmu_ops *ops = rsmu->ops;
+	struct rsmu_get_state state_request;
+	u8 state;
+	int err;
+
+	if (copy_from_user(&state_request, arg, sizeof(state_request)))
+		return -EFAULT;
+
+	if (ops->get_dpll_state == NULL)
+		return -ENOTSUPP;
+
+	mutex_lock(rsmu->lock);
+	err = ops->get_dpll_state(rsmu, state_request.dpll, &state);
+	mutex_unlock(rsmu->lock);
+
+	state_request.state = state;
+	if (copy_to_user(arg, &state_request, sizeof(state_request)))
+		return -EFAULT;
+
+	return err;
+}
+
+static int
+rsmu_get_dpll_ffo(struct rsmu_cdev *rsmu, void __user *arg)
+{
+	struct rsmu_ops *ops = rsmu->ops;
+	struct rsmu_get_ffo ffo_request;
+	int err;
+
+	if (copy_from_user(&ffo_request, arg, sizeof(ffo_request)))
+		return -EFAULT;
+
+	if (ops->get_dpll_ffo == NULL)
+		return -ENOTSUPP;
+
+	mutex_lock(rsmu->lock);
+	err = ops->get_dpll_ffo(rsmu, ffo_request.dpll, &ffo_request);
+	mutex_unlock(rsmu->lock);
+
+	if (copy_to_user(arg, &ffo_request, sizeof(ffo_request)))
+		return -EFAULT;
+
+	return err;
+}
+
+static int
+rsmu_open(struct inode *iptr, struct file *fptr)
+{
+	struct rsmu_cdev *rsmu;
+
+	rsmu = container_of(iptr->i_cdev, struct rsmu_cdev, rsmu_cdev);
+	fptr->private_data = rsmu;
+	return 0;
+}
+
+static int
+rsmu_release(struct inode *iptr, struct file *fptr)
+{
+	return 0;
+}
+
+static long
+rsmu_ioctl(struct file *fptr, unsigned int cmd, unsigned long data)
+{
+	struct rsmu_cdev *rsmu = fptr->private_data;
+	void __user *arg = (void __user *)data;
+	int err = 0;
+
+	switch (cmd) {
+	case RSMU_SET_COMBOMODE:
+		err = rsmu_set_combomode(rsmu, arg);
+		break;
+	case RSMU_GET_STATE:
+		err = rsmu_get_dpll_state(rsmu, arg);
+		break;
+	case RSMU_GET_FFO:
+		err = rsmu_get_dpll_ffo(rsmu, arg);
+		break;
+	default:
+		/* Should not get here */
+		dev_err(rsmu->dev, "Undefined RSMU IOCTL");
+		err = -EINVAL;
+		break;
+	}
+
+	return err;
+}
+
+static long rsmu_compat_ioctl(struct file *fptr, unsigned int cmd,
+			      unsigned long data)
+{
+	return rsmu_ioctl(fptr, cmd, data);
+}
+
+static const struct file_operations rsmu_fops = {
+	.owner = THIS_MODULE,
+	.open = rsmu_open,
+	.release = rsmu_release,
+	.unlocked_ioctl = rsmu_ioctl,
+	.compat_ioctl =	rsmu_compat_ioctl,
+};
+
+static int rsmu_init_ops(struct rsmu_cdev *rsmu)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ops_array); i++)
+		if (ops_array[i]->type == rsmu->type)
+			break;
+
+	if (i == ARRAY_SIZE(ops_array))
+		return -EINVAL;
+
+	rsmu->ops = ops_array[i];
+	return 0;
+}
+
+static int
+rsmu_probe(struct platform_device *pdev)
+{
+	struct rsmu_pdata *pdata = dev_get_platdata(&pdev->dev);
+	struct rsmu_cdev *rsmu;
+	struct device *rsmu_cdev;
+	int err;
+
+	rsmu = devm_kzalloc(&pdev->dev, sizeof(*rsmu), GFP_KERNEL);
+	if (!rsmu)
+		return -ENOMEM;
+
+	rsmu->dev = &pdev->dev;
+	rsmu->mfd = pdev->dev.parent;
+	rsmu->type = pdata->type;
+	rsmu->lock = pdata->lock;
+	rsmu->index = pdata->index;
+
+	/* Save driver private data */
+	platform_set_drvdata(pdev, rsmu);
+
+	cdev_init(&rsmu->rsmu_cdev, &rsmu_fops);
+	rsmu->rsmu_cdev.owner = THIS_MODULE;
+	err = cdev_add(&rsmu->rsmu_cdev,
+		       MKDEV(MAJOR(rsmu_cdevt), 0), 1);
+	if (err < 0) {
+		dev_err(rsmu->dev, "cdev_add failed");
+		err = -EIO;
+		goto err_rsmu_dev;
+	}
+
+	if (!rsmu_class) {
+		err = -EIO;
+		dev_err(rsmu->dev, "rsmu class not created correctly");
+		goto err_rsmu_cdev;
+	}
+
+	rsmu_cdev = device_create(rsmu_class, rsmu->dev,
+				  MKDEV(MAJOR(rsmu_cdevt), 0),
+				  rsmu, "rsmu%d", rsmu->index);
+	if (IS_ERR(rsmu_cdev)) {
+		dev_err(rsmu->dev, "Unable to create char device");
+		err = PTR_ERR(rsmu_cdev);
+		goto err_rsmu_cdev;
+	}
+
+	err = rsmu_init_ops(rsmu);
+	if (err) {
+		dev_err(rsmu->dev, "Unable to match type %d", rsmu->type);
+		goto err_rsmu_cdev;
+	}
+
+	dev_info(rsmu->dev, "Probe SMU type %d successful\n", rsmu->type);
+	return 0;
+
+	/* Failure cleanup */
+err_rsmu_cdev:
+	cdev_del(&rsmu->rsmu_cdev);
+err_rsmu_dev:
+	return err;
+}
+
+static int
+rsmu_remove(struct platform_device *pdev)
+{
+	struct rsmu_cdev *rsmu = platform_get_drvdata(pdev);
+	struct device *dev = &pdev->dev;
+
+	if (!rsmu_class) {
+		dev_err(dev, "rsmu_class is NULL");
+		return -EIO;
+	}
+
+	device_destroy(rsmu_class, MKDEV(MAJOR(rsmu_cdevt), 0));
+	cdev_del(&rsmu->rsmu_cdev);
+
+	return 0;
+}
+
+static const struct platform_device_id rsmu_id_table[] = {
+	{ "rsmu-cdev0", },
+	{ "rsmu-cdev1", },
+	{ "rsmu-cdev2", },
+	{ "rsmu-cdev3", },
+	{}
+};
+MODULE_DEVICE_TABLE(platform, rsmu_id_table);
+
+static struct platform_driver rsmu_driver = {
+	.driver = {
+		.name = DRIVER_NAME,
+	},
+	.probe = rsmu_probe,
+	.remove =  rsmu_remove,
+	.id_table = rsmu_id_table,
+};
+
+static int __init rsmu_init(void)
+{
+	int err;
+
+	rsmu_class = class_create(THIS_MODULE, DRIVER_NAME);
+	if (IS_ERR(rsmu_class)) {
+		err = PTR_ERR(rsmu_class);
+		pr_err("Unable to register rsmu class");
+		return err;
+	}
+
+	err = alloc_chrdev_region(&rsmu_cdevt, 0, DRIVER_MAX_DEV, DRIVER_NAME);
+	if (err < 0) {
+		pr_err("Unable to get major number");
+		goto err_rsmu_class;
+	}
+
+	err = platform_driver_register(&rsmu_driver);
+	if (err < 0) {
+		pr_err("Unabled to register %s driver", DRIVER_NAME);
+		goto err_rsmu_drv;
+	}
+	return 0;
+
+	/* Error Path */
+err_rsmu_drv:
+	unregister_chrdev_region(rsmu_cdevt, DRIVER_MAX_DEV);
+err_rsmu_class:
+	class_destroy(rsmu_class);
+	return err;
+}
+
+static void __exit rsmu_exit(void)
+{
+	platform_driver_unregister(&rsmu_driver);
+	unregister_chrdev_region(rsmu_cdevt, DRIVER_MAX_DEV);
+	class_destroy(rsmu_class);
+	rsmu_class = NULL;
+}
+
+module_init(rsmu_init);
+module_exit(rsmu_exit);
+
+MODULE_DESCRIPTION("Renesas SMU character device driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/rsmu_cdev.h b/drivers/misc/rsmu_cdev.h
new file mode 100644
index 0000000..3ced817
--- /dev/null
+++ b/drivers/misc/rsmu_cdev.h
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * This driver is developed for the IDT ClockMatrix(TM) of
+ * timing and synchronization devices.
+ *
+ * Copyright (C) 2019 Integrated Device Technology, Inc., a Renesas Company.
+ */
+#ifndef __LINUX_RSMU_CDEV_H
+#define __LINUX_RSMU_CDEV_H
+
+#include <linux/cdev.h>
+
+struct rsmu_ops;
+
+/**
+ * struct rsmu_cdev - Driver data for RSMU character device
+ * @dev: pointer to platform device
+ * @mfd: pointer to MFD device
+ * @rsmu_cdev: character device handle
+ * @lock: mutex to protect operations from being interrupted
+ * @type: rsmu device type
+ * @ops: rsmu device methods
+ * @index: rsmu device index
+ */
+struct rsmu_cdev {
+	struct device *dev;
+	struct device *mfd;
+	struct cdev rsmu_cdev;
+	struct mutex *lock;
+	enum rsmu_type type;
+	struct rsmu_ops *ops;
+	u8 index;
+};
+
+extern struct rsmu_ops cm_ops;
+extern struct rsmu_ops sabre_ops;
+
+struct rsmu_ops {
+	enum rsmu_type type;
+	int (*set_combomode)(struct rsmu_cdev *rsmu, u8 dpll, u8 mode);
+	int (*get_dpll_state)(struct rsmu_cdev *rsmu, u8 dpll, u8 *state);
+	int (*get_dpll_ffo)(struct rsmu_cdev *rsmu, u8 dpll,
+			    struct rsmu_get_ffo *ffo);
+};
+
+/**
+ * Enumerated type listing DPLL combination modes
+ */
+enum rsmu_dpll_combomode {
+	E_COMBOMODE_CURRENT = 0,
+	E_COMBOMODE_FASTAVG,
+	E_COMBOMODE_SLOWAVG,
+	E_COMBOMODE_HOLDOVER,
+	E_COMBOMODE_MAX
+};
+
+/**
+ * An id used to identify the respective child class states.
+ */
+enum rsmu_class_state {
+	E_SRVLOINITIALSTATE = 0,
+	E_SRVLOUNQUALIFIEDSTATE = 1,
+	E_SRVLOLOCKACQSTATE = 2,
+	E_SRVLOFREQUENCYLOCKEDSTATE = 3,
+	E_SRVLOTIMELOCKEDSTATE = 4,
+	E_SRVLOHOLDOVERINSPECSTATE = 5,
+	E_SRVLOHOLDOVEROUTOFSPECSTATE = 6,
+	E_SRVLOFREERUNSTATE = 7,
+	E_SRVNUMBERLOSTATES = 8,
+	E_SRVLOSTATEINVALID = 9,
+};
+#endif
diff --git a/drivers/misc/rsmu_cm.c b/drivers/misc/rsmu_cm.c
new file mode 100644
index 0000000..d5af624
--- /dev/null
+++ b/drivers/misc/rsmu_cm.c
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * This driver is developed for the IDT ClockMatrix(TM) of
+ * timing and synchronization devices.
+ *
+ * Copyright (C) 2019 Integrated Device Technology, Inc., a Renesas Company.
+ */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/mfd/idt8a340_reg.h>
+#include <linux/mfd/rsmu.h>
+#include <uapi/linux/rsmu.h>
+#include <asm/unaligned.h>
+
+#include "rsmu_cdev.h"
+
+static int rsmu_cm_set_combomode(struct rsmu_cdev *rsmu, u8 dpll, u8 mode)
+{
+	u16 dpll_ctrl_n;
+	u8 cfg;
+	int err;
+
+	switch (dpll) {
+	case 0:
+		dpll_ctrl_n = DPLL_CTRL_0;
+		break;
+	case 1:
+		dpll_ctrl_n = DPLL_CTRL_1;
+		break;
+	case 2:
+		dpll_ctrl_n = DPLL_CTRL_2;
+		break;
+	case 3:
+		dpll_ctrl_n = DPLL_CTRL_3;
+		break;
+	case 4:
+		dpll_ctrl_n = DPLL_CTRL_4;
+		break;
+	case 5:
+		dpll_ctrl_n = DPLL_CTRL_5;
+		break;
+	case 6:
+		dpll_ctrl_n = DPLL_CTRL_6;
+		break;
+	case 7:
+		dpll_ctrl_n = DPLL_CTRL_7;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (mode >= E_COMBOMODE_MAX)
+		return -EINVAL;
+
+	err = rsmu_read(rsmu->mfd, dpll_ctrl_n + DPLL_CTRL_COMBO_MASTER_CFG,
+			&cfg, sizeof(cfg));
+	if (err)
+		return err;
+
+	/* Only need to enable/disable COMBO_MODE_HOLD. */
+	if (mode)
+		cfg |= COMBO_MASTER_HOLD;
+	else
+		cfg &= ~COMBO_MASTER_HOLD;
+
+	return rsmu_write(rsmu->mfd, dpll_ctrl_n + DPLL_CTRL_COMBO_MASTER_CFG,
+			  &cfg, sizeof(cfg));
+}
+
+static int rsmu_cm_get_dpll_state(struct rsmu_cdev *rsmu, u8 dpll, u8 *state)
+{
+	u8 cfg;
+	int err;
+
+	/* 8 is sys dpll */
+	if (dpll > 8)
+		return -EINVAL;
+
+	err = rsmu_read(rsmu->mfd,
+			  STATUS + DPLL0_STATUS + dpll,
+			  &cfg, sizeof(cfg));
+	if (err)
+		return err;
+
+	switch (cfg & DPLL_STATE_MASK) {
+	case DPLL_STATE_FREERUN:
+		*state = E_SRVLOUNQUALIFIEDSTATE;
+		break;
+	case DPLL_STATE_LOCKACQ:
+	case DPLL_STATE_LOCKREC:
+		*state = E_SRVLOLOCKACQSTATE;
+		break;
+	case DPLL_STATE_LOCKED:
+		*state = E_SRVLOTIMELOCKEDSTATE;
+		break;
+	case DPLL_STATE_HOLDOVER:
+		*state = E_SRVLOHOLDOVERINSPECSTATE;
+		break;
+	default:
+		*state = E_SRVLOSTATEINVALID;
+		break;
+	}
+
+	return 0;
+}
+
+static int rsmu_cm_get_dpll_ffo(struct rsmu_cdev *rsmu, u8 dpll,
+				struct rsmu_get_ffo *ffo)
+{
+	u8 buf[8] = {0};
+	s64 fcw = 0;
+	u16 dpll_filter_status;
+	int err;
+
+	switch (dpll) {
+	case 0:
+		dpll_filter_status = DPLL0_FILTER_STATUS;
+		break;
+	case 1:
+		dpll_filter_status = DPLL1_FILTER_STATUS;
+		break;
+	case 2:
+		dpll_filter_status = DPLL2_FILTER_STATUS;
+		break;
+	case 3:
+		dpll_filter_status = DPLL3_FILTER_STATUS;
+		break;
+	case 4:
+		dpll_filter_status = DPLL4_FILTER_STATUS;
+		break;
+	case 5:
+		dpll_filter_status = DPLL5_FILTER_STATUS;
+		break;
+	case 6:
+		dpll_filter_status = DPLL6_FILTER_STATUS;
+		break;
+	case 7:
+		dpll_filter_status = DPLL7_FILTER_STATUS;
+		break;
+	case 8:
+		dpll_filter_status = DPLLSYS_FILTER_STATUS;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	err = rsmu_read(rsmu->mfd, STATUS + dpll_filter_status, buf, 6);
+	if (err)
+		return err;
+
+	/* Convert to frequency control word */
+	fcw = sign_extend64(get_unaligned_le64(buf), 47);
+
+	/* FCW unit is 2 ^ -53 = 1.1102230246251565404236316680908e-16 */
+	ffo->ffo = fcw * 111;
+
+	return 0;
+}
+
+struct rsmu_ops cm_ops = {
+	.type = RSMU_CM,
+	.set_combomode = rsmu_cm_set_combomode,
+	.get_dpll_state = rsmu_cm_get_dpll_state,
+	.get_dpll_ffo = rsmu_cm_get_dpll_ffo,
+};
diff --git a/drivers/misc/rsmu_sabre.c b/drivers/misc/rsmu_sabre.c
new file mode 100644
index 0000000..aa772f1
--- /dev/null
+++ b/drivers/misc/rsmu_sabre.c
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * This driver is developed for the IDT 82P33XXX series of
+ * timing and synchronization devices.
+ *
+ * Copyright (C) 2019 Integrated Device Technology, Inc., a Renesas Company.
+ */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/mfd/idt82p33_reg.h>
+#include <linux/mfd/rsmu.h>
+#include <uapi/linux/rsmu.h>
+#include <asm/unaligned.h>
+
+#include "rsmu_cdev.h"
+
+static int rsmu_sabre_set_combomode(struct rsmu_cdev *rsmu, u8 dpll, u8 mode)
+{
+	u16 dpll_ctrl_n;
+	u8 cfg;
+	int err;
+
+	switch (dpll) {
+	case 0:
+		dpll_ctrl_n = DPLL1_OPERATING_MODE_CNFG;
+		break;
+	case 1:
+		dpll_ctrl_n = DPLL2_OPERATING_MODE_CNFG;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (mode >= E_COMBOMODE_MAX)
+		return -EINVAL;
+
+	err = rsmu_read(rsmu->mfd, dpll_ctrl_n, &cfg, sizeof(cfg));
+	if (err)
+		return err;
+
+	cfg &= ~(COMBO_MODE_MASK << COMBO_MODE_SHIFT);
+	cfg |= mode << COMBO_MODE_SHIFT;
+
+	return rsmu_write(rsmu->mfd, dpll_ctrl_n, &cfg, sizeof(cfg));
+}
+
+static int rsmu_sabre_get_dpll_state(struct rsmu_cdev *rsmu, u8 dpll, u8 *state)
+{
+	u16 dpll_sts_n;
+	u8 cfg;
+	int err;
+
+	switch (dpll) {
+	case 0:
+		dpll_sts_n = DPLL1_OPERATING_STS;
+		break;
+	case 1:
+		dpll_sts_n = DPLL2_OPERATING_STS;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	err = rsmu_read(rsmu->mfd, dpll_sts_n, &cfg, sizeof(cfg));
+	if (err)
+		return err;
+
+	switch (cfg & OPERATING_STS_MASK) {
+	case DPLL_STATE_FREERUN:
+		*state = E_SRVLOUNQUALIFIEDSTATE;
+		break;
+	case DPLL_STATE_PRELOCKED2:
+	case DPLL_STATE_PRELOCKED:
+		*state = E_SRVLOLOCKACQSTATE;
+		break;
+	case DPLL_STATE_LOCKED:
+		*state = E_SRVLOTIMELOCKEDSTATE;
+		break;
+	case DPLL_STATE_HOLDOVER:
+		*state = E_SRVLOHOLDOVERINSPECSTATE;
+		break;
+	default:
+		*state = E_SRVLOSTATEINVALID;
+		break;
+	}
+
+	return 0;
+}
+
+static int rsmu_sabre_get_dpll_ffo(struct rsmu_cdev *rsmu, u8 dpll,
+				   struct rsmu_get_ffo *ffo)
+{
+	u8 buf[8] = {0};
+	s64 fcw = 0;
+	u16 dpll_freq_n;
+	int err;
+
+	switch (dpll) {
+	case 0:
+		dpll_freq_n = DPLL1_CURRENT_FREQ_STS;
+		break;
+	case 1:
+		dpll_freq_n = DPLL2_CURRENT_FREQ_STS;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	err = rsmu_read(rsmu->mfd, dpll_freq_n, buf, 5);
+	if (err)
+		return err;
+
+	/* Convert to frequency control word */
+	fcw = sign_extend64(get_unaligned_le64(buf), 39);
+
+	/* FCW unit is 77760 / ( 1638400 * 2^48) = 1.68615121864946 * 10^-16 */
+	ffo->ffo = div_s64(fcw * 168615, 1000);
+
+	return 0;
+}
+
+struct rsmu_ops sabre_ops = {
+	.type = RSMU_SABRE,
+	.set_combomode = rsmu_sabre_set_combomode,
+	.get_dpll_state = rsmu_sabre_get_dpll_state,
+	.get_dpll_ffo = rsmu_sabre_get_dpll_ffo,
+};
diff --git a/include/uapi/linux/rsmu.h b/include/uapi/linux/rsmu.h
new file mode 100644
index 0000000..02c9e38
--- /dev/null
+++ b/include/uapi/linux/rsmu.h
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/*
+ * Driver for the IDT ClockMatrix(TM) and 82p33xxx families of
+ * timing and synchronization devices.
+ *
+ * Copyright (C) 2019 Integrated Device Technology, Inc., a Renesas Company.
+ */
+
+#ifndef __UAPI_LINUX_RSMU_CDEV_H
+#define __UAPI_LINUX_RSMU_CDEV_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+/* Set dpll combomode */
+struct rsmu_combomode {
+	__u8 dpll;
+	__u8 mode;
+};
+
+/* Get dpll state */
+struct rsmu_get_state {
+	__u8 dpll;
+	__u8 state;
+};
+
+/* Get dpll ffo (fractional frequency offset) in ppqt*/
+struct rsmu_get_ffo {
+	__u8 dpll;
+	__s64 ffo;
+};
+
+/*
+ * RSMU IOCTL List
+ */
+#define RSMU_MAGIC '?'
+
+/**
+ * @Description
+ * ioctl to set SMU combo mode.
+ *
+ * @Parameters
+ * pointer to struct rsmu_combomode that contains dpll combomode setting
+ */
+#define RSMU_SET_COMBOMODE  _IOW(RSMU_MAGIC, 1, struct rsmu_combomode)
+
+/**
+ * @Description
+ * ioctl to get SMU dpll state.
+ *
+ * @Parameters
+ * pointer to struct rsmu_get_state that contains dpll state
+ */
+#define RSMU_GET_STATE  _IOR(RSMU_MAGIC, 2, struct rsmu_get_state)
+
+/**
+ * @Description
+ * ioctl to get SMU dpll ffo.
+ *
+ * @Parameters
+ * pointer to struct rsmu_get_ffo that contains dpll ffo in ppqt
+ */
+#define RSMU_GET_FFO  _IOR(RSMU_MAGIC, 3, struct rsmu_get_ffo)
+#endif /* __UAPI_LINUX_RSMU_CDEV_H */
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2021-04-05 13:53 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-03-26 15:34 [PATCH next 2/2] misc: Add Renesas Synchronization Management Unit (SMU) support min.li.xe
2021-03-27  7:58 ` kernel test robot
2021-03-27  9:53 ` kernel test robot
2021-03-28 12:13 ` Greg KH
2021-03-29 17:03   ` Min Li
2021-04-02 14:22     ` Greg KH
2021-04-03 22:06       ` Min Li
  -- strict thread matches above, loose matches on Subject: below --
2021-04-03 22:08 [PATCH next 1/2] mfd: " min.li.xe
2021-04-03 22:08 ` [PATCH next 2/2] misc: " min.li.xe
2021-04-04 10:08   ` Greg KH
2021-04-05  1:52     ` Min Li
2021-04-05  6:37       ` Greg KH
2021-04-05 13:53         ` Min Li
2021-03-23 16:40 min.li.xe

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).