All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/3] Add PWM support for Intel Keem Bay SoC
@ 2020-05-17 13:52 ` vineetha.g.jaya.kumaran-ral2JQCrhuEAvxtiuMwx3w
  0 siblings, 0 replies; 19+ messages in thread
From: vineetha.g.jaya.kumaran @ 2020-05-17 13:52 UTC (permalink / raw)
  To: thierry.reding, u.kleine-koenig, robh+dt
  Cc: linux-pwm, devicetree, wan.ahmad.zainie.wan.mohamad,
	andriy.shevchenko, vineetha.g.jaya.kumaran

From: "Vineetha G. Jaya Kumaran" <vineetha.g.jaya.kumaran@intel.com>

Hi,

This patch set enables support for PWM on the Intel Keem Bay SoC.
Keem Bay is an ARM based SoC, and the GPIO module allows
configuration of 6 PWM outputs. Patch 1 adds a new count attribute to
the sysfs, Patch 2 adds the PWM driver and Patch 3 is for the
required Device Tree bindings documentation.

This driver was tested on the Keem Bay evaluation module board.

Thank you.

Best regards,
Vineetha

Lai, Poey Seng (2):
  pwm: Add count attribute in sysfs for Intel Keem Bay
  pwm: Add PWM driver for Intel Keem Bay

Vineetha G. Jaya Kumaran (1):
  dt-bindings: pwm: keembay: Add bindings for Intel Keem Bay PWM

 Documentation/ABI/testing/sysfs-class-pwm          |   9 +
 .../devicetree/bindings/pwm/pwm-keembay.yaml       |  39 +++
 drivers/pwm/Kconfig                                |   9 +
 drivers/pwm/Makefile                               |   1 +
 drivers/pwm/core.c                                 |   3 +-
 drivers/pwm/pwm-keembay.c                          | 308 +++++++++++++++++++++
 drivers/pwm/sysfs.c                                |  37 +++
 include/linux/pwm.h                                |   2 +
 8 files changed, 407 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/pwm/pwm-keembay.yaml
 create mode 100644 drivers/pwm/pwm-keembay.c

-- 
1.9.1


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

* [PATCH 0/3] Add PWM support for Intel Keem Bay SoC
@ 2020-05-17 13:52 ` vineetha.g.jaya.kumaran-ral2JQCrhuEAvxtiuMwx3w
  0 siblings, 0 replies; 19+ messages in thread
From: vineetha.g.jaya.kumaran-ral2JQCrhuEAvxtiuMwx3w @ 2020-05-17 13:52 UTC (permalink / raw)
  To: thierry.reding-Re5JQEeQqe8AvxtiuMwx3w,
	u.kleine-koenig-bIcnvbaLZ9MEGnE8C9+IrQ,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A
  Cc: linux-pwm-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	wan.ahmad.zainie.wan.mohamad-ral2JQCrhuEAvxtiuMwx3w,
	andriy.shevchenko-ral2JQCrhuEAvxtiuMwx3w,
	vineetha.g.jaya.kumaran-ral2JQCrhuEAvxtiuMwx3w

From: "Vineetha G. Jaya Kumaran" <vineetha.g.jaya.kumaran-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>

Hi,

This patch set enables support for PWM on the Intel Keem Bay SoC.
Keem Bay is an ARM based SoC, and the GPIO module allows
configuration of 6 PWM outputs. Patch 1 adds a new count attribute to
the sysfs, Patch 2 adds the PWM driver and Patch 3 is for the
required Device Tree bindings documentation.

This driver was tested on the Keem Bay evaluation module board.

Thank you.

Best regards,
Vineetha

Lai, Poey Seng (2):
  pwm: Add count attribute in sysfs for Intel Keem Bay
  pwm: Add PWM driver for Intel Keem Bay

Vineetha G. Jaya Kumaran (1):
  dt-bindings: pwm: keembay: Add bindings for Intel Keem Bay PWM

 Documentation/ABI/testing/sysfs-class-pwm          |   9 +
 .../devicetree/bindings/pwm/pwm-keembay.yaml       |  39 +++
 drivers/pwm/Kconfig                                |   9 +
 drivers/pwm/Makefile                               |   1 +
 drivers/pwm/core.c                                 |   3 +-
 drivers/pwm/pwm-keembay.c                          | 308 +++++++++++++++++++++
 drivers/pwm/sysfs.c                                |  37 +++
 include/linux/pwm.h                                |   2 +
 8 files changed, 407 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/pwm/pwm-keembay.yaml
 create mode 100644 drivers/pwm/pwm-keembay.c

-- 
1.9.1

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

* [PATCH 1/3] pwm: Add count attribute in sysfs for Intel Keem Bay
@ 2020-05-17 13:52   ` vineetha.g.jaya.kumaran-ral2JQCrhuEAvxtiuMwx3w
  0 siblings, 0 replies; 19+ messages in thread
From: vineetha.g.jaya.kumaran @ 2020-05-17 13:52 UTC (permalink / raw)
  To: thierry.reding, u.kleine-koenig, robh+dt
  Cc: linux-pwm, devicetree, wan.ahmad.zainie.wan.mohamad,
	andriy.shevchenko, vineetha.g.jaya.kumaran

From: "Lai, Poey Seng" <poey.seng.lai@intel.com>

In Keem Bay, the number of repetitions for the period/waveform
can be configured from userspace. This requires addition of a sysfs
attribute to get/set the repetition count. Setting this value to 0
will result in continuous repetition of the waveform until the
channel is disabled or reconfigured.

Signed-off-by: Lai, Poey Seng <poey.seng.lai@intel.com>
Signed-off-by: Vineetha G. Jaya Kumaran <vineetha.g.jaya.kumaran@intel.com>
---
 Documentation/ABI/testing/sysfs-class-pwm |  9 ++++++++
 drivers/pwm/core.c                        |  3 ++-
 drivers/pwm/sysfs.c                       | 37 +++++++++++++++++++++++++++++++
 include/linux/pwm.h                       |  2 ++
 4 files changed, 50 insertions(+), 1 deletion(-)

diff --git a/Documentation/ABI/testing/sysfs-class-pwm b/Documentation/ABI/testing/sysfs-class-pwm
index c20e613..87e219f 100644
--- a/Documentation/ABI/testing/sysfs-class-pwm
+++ b/Documentation/ABI/testing/sysfs-class-pwm
@@ -86,3 +86,12 @@ Description:
 		Capture information about a PWM signal. The output format is a
 		pair unsigned integers (period and duty cycle), separated by a
 		single space.
+
+What:		/sys/class/pwm/pwmchipN/pwmX/count
+Date:		May 2020
+KernelVersion:	5.6
+Contact:	Vineetha G. Jaya Kumaran <vineetha.g.jaya.kumaran@intel.com>
+Description:
+		Sets the repetition count of a PWM waveform. A value of 0 will
+		result in continuous repetition of the waveform until the
+		channel is disabled or reconfigured.
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index bca0496..fd42fb6 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -584,7 +584,8 @@ int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state)
 	if (state->period == pwm->state.period &&
 	    state->duty_cycle == pwm->state.duty_cycle &&
 	    state->polarity == pwm->state.polarity &&
-	    state->enabled == pwm->state.enabled)
+	    state->enabled == pwm->state.enabled &&
+	    state->count == pwm->state.count)
 		return 0;
 
 	if (chip->ops->apply) {
diff --git a/drivers/pwm/sysfs.c b/drivers/pwm/sysfs.c
index 2389b86..3c474fa 100644
--- a/drivers/pwm/sysfs.c
+++ b/drivers/pwm/sysfs.c
@@ -215,11 +215,47 @@ static ssize_t capture_show(struct device *child,
 	return sprintf(buf, "%u %u\n", result.period, result.duty_cycle);
 }
 
+static ssize_t count_store(struct device *child,
+			      struct device_attribute *attr,
+			      const char *buf, size_t size)
+{
+	struct pwm_export *export = child_to_pwm_export(child);
+	struct pwm_device *pwm = export->pwm;
+	struct pwm_state state;
+	unsigned int val;
+	int ret;
+
+	ret = kstrtouint(buf, 0, &val);
+	if (ret)
+		return ret;
+
+	mutex_lock(&export->lock);
+	pwm_get_state(pwm, &state);
+	state.count = val;
+	ret = pwm_apply_state(pwm, &state);
+	mutex_unlock(&export->lock);
+
+	return ret ? : size;
+}
+
+static ssize_t count_show(struct device *child,
+			    struct device_attribute *attr,
+			    char *buf)
+{
+	const struct pwm_device *pwm = child_to_pwm_device(child);
+	struct pwm_state state;
+
+	pwm_get_state(pwm, &state);
+
+	return sprintf(buf, "%d\n", state.count);
+}
+
 static DEVICE_ATTR_RW(period);
 static DEVICE_ATTR_RW(duty_cycle);
 static DEVICE_ATTR_RW(enable);
 static DEVICE_ATTR_RW(polarity);
 static DEVICE_ATTR_RO(capture);
+static DEVICE_ATTR_RW(count);
 
 static struct attribute *pwm_attrs[] = {
 	&dev_attr_period.attr,
@@ -227,6 +263,7 @@ static ssize_t capture_show(struct device *child,
 	&dev_attr_enable.attr,
 	&dev_attr_polarity.attr,
 	&dev_attr_capture.attr,
+	&dev_attr_count.attr,
 	NULL
 };
 ATTRIBUTE_GROUPS(pwm);
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index 2635b2a..c874559 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -52,12 +52,14 @@ enum {
  * struct pwm_state - state of a PWM channel
  * @period: PWM period (in nanoseconds)
  * @duty_cycle: PWM duty cycle (in nanoseconds)
+ * @count: Repeat count of PWM waveforms.
  * @polarity: PWM polarity
  * @enabled: PWM enabled status
  */
 struct pwm_state {
 	unsigned int period;
 	unsigned int duty_cycle;
+	unsigned int count;
 	enum pwm_polarity polarity;
 	bool enabled;
 };
-- 
1.9.1


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

* [PATCH 1/3] pwm: Add count attribute in sysfs for Intel Keem Bay
@ 2020-05-17 13:52   ` vineetha.g.jaya.kumaran-ral2JQCrhuEAvxtiuMwx3w
  0 siblings, 0 replies; 19+ messages in thread
From: vineetha.g.jaya.kumaran-ral2JQCrhuEAvxtiuMwx3w @ 2020-05-17 13:52 UTC (permalink / raw)
  To: thierry.reding-Re5JQEeQqe8AvxtiuMwx3w,
	u.kleine-koenig-bIcnvbaLZ9MEGnE8C9+IrQ,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A
  Cc: linux-pwm-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	wan.ahmad.zainie.wan.mohamad-ral2JQCrhuEAvxtiuMwx3w,
	andriy.shevchenko-ral2JQCrhuEAvxtiuMwx3w,
	vineetha.g.jaya.kumaran-ral2JQCrhuEAvxtiuMwx3w

From: "Lai, Poey Seng" <poey.seng.lai-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>

In Keem Bay, the number of repetitions for the period/waveform
can be configured from userspace. This requires addition of a sysfs
attribute to get/set the repetition count. Setting this value to 0
will result in continuous repetition of the waveform until the
channel is disabled or reconfigured.

Signed-off-by: Lai, Poey Seng <poey.seng.lai-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
Signed-off-by: Vineetha G. Jaya Kumaran <vineetha.g.jaya.kumaran-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
---
 Documentation/ABI/testing/sysfs-class-pwm |  9 ++++++++
 drivers/pwm/core.c                        |  3 ++-
 drivers/pwm/sysfs.c                       | 37 +++++++++++++++++++++++++++++++
 include/linux/pwm.h                       |  2 ++
 4 files changed, 50 insertions(+), 1 deletion(-)

diff --git a/Documentation/ABI/testing/sysfs-class-pwm b/Documentation/ABI/testing/sysfs-class-pwm
index c20e613..87e219f 100644
--- a/Documentation/ABI/testing/sysfs-class-pwm
+++ b/Documentation/ABI/testing/sysfs-class-pwm
@@ -86,3 +86,12 @@ Description:
 		Capture information about a PWM signal. The output format is a
 		pair unsigned integers (period and duty cycle), separated by a
 		single space.
+
+What:		/sys/class/pwm/pwmchipN/pwmX/count
+Date:		May 2020
+KernelVersion:	5.6
+Contact:	Vineetha G. Jaya Kumaran <vineetha.g.jaya.kumaran-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
+Description:
+		Sets the repetition count of a PWM waveform. A value of 0 will
+		result in continuous repetition of the waveform until the
+		channel is disabled or reconfigured.
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index bca0496..fd42fb6 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -584,7 +584,8 @@ int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state)
 	if (state->period == pwm->state.period &&
 	    state->duty_cycle == pwm->state.duty_cycle &&
 	    state->polarity == pwm->state.polarity &&
-	    state->enabled == pwm->state.enabled)
+	    state->enabled == pwm->state.enabled &&
+	    state->count == pwm->state.count)
 		return 0;
 
 	if (chip->ops->apply) {
diff --git a/drivers/pwm/sysfs.c b/drivers/pwm/sysfs.c
index 2389b86..3c474fa 100644
--- a/drivers/pwm/sysfs.c
+++ b/drivers/pwm/sysfs.c
@@ -215,11 +215,47 @@ static ssize_t capture_show(struct device *child,
 	return sprintf(buf, "%u %u\n", result.period, result.duty_cycle);
 }
 
+static ssize_t count_store(struct device *child,
+			      struct device_attribute *attr,
+			      const char *buf, size_t size)
+{
+	struct pwm_export *export = child_to_pwm_export(child);
+	struct pwm_device *pwm = export->pwm;
+	struct pwm_state state;
+	unsigned int val;
+	int ret;
+
+	ret = kstrtouint(buf, 0, &val);
+	if (ret)
+		return ret;
+
+	mutex_lock(&export->lock);
+	pwm_get_state(pwm, &state);
+	state.count = val;
+	ret = pwm_apply_state(pwm, &state);
+	mutex_unlock(&export->lock);
+
+	return ret ? : size;
+}
+
+static ssize_t count_show(struct device *child,
+			    struct device_attribute *attr,
+			    char *buf)
+{
+	const struct pwm_device *pwm = child_to_pwm_device(child);
+	struct pwm_state state;
+
+	pwm_get_state(pwm, &state);
+
+	return sprintf(buf, "%d\n", state.count);
+}
+
 static DEVICE_ATTR_RW(period);
 static DEVICE_ATTR_RW(duty_cycle);
 static DEVICE_ATTR_RW(enable);
 static DEVICE_ATTR_RW(polarity);
 static DEVICE_ATTR_RO(capture);
+static DEVICE_ATTR_RW(count);
 
 static struct attribute *pwm_attrs[] = {
 	&dev_attr_period.attr,
@@ -227,6 +263,7 @@ static ssize_t capture_show(struct device *child,
 	&dev_attr_enable.attr,
 	&dev_attr_polarity.attr,
 	&dev_attr_capture.attr,
+	&dev_attr_count.attr,
 	NULL
 };
 ATTRIBUTE_GROUPS(pwm);
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index 2635b2a..c874559 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -52,12 +52,14 @@ enum {
  * struct pwm_state - state of a PWM channel
  * @period: PWM period (in nanoseconds)
  * @duty_cycle: PWM duty cycle (in nanoseconds)
+ * @count: Repeat count of PWM waveforms.
  * @polarity: PWM polarity
  * @enabled: PWM enabled status
  */
 struct pwm_state {
 	unsigned int period;
 	unsigned int duty_cycle;
+	unsigned int count;
 	enum pwm_polarity polarity;
 	bool enabled;
 };
-- 
1.9.1

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

* [PATCH 2/3] pwm: Add PWM driver for Intel Keem Bay
  2020-05-17 13:52 ` vineetha.g.jaya.kumaran-ral2JQCrhuEAvxtiuMwx3w
  (?)
  (?)
@ 2020-05-17 13:52 ` vineetha.g.jaya.kumaran
  2020-05-23 21:40   ` Uwe Kleine-König
  -1 siblings, 1 reply; 19+ messages in thread
From: vineetha.g.jaya.kumaran @ 2020-05-17 13:52 UTC (permalink / raw)
  To: thierry.reding, u.kleine-koenig, robh+dt
  Cc: linux-pwm, devicetree, wan.ahmad.zainie.wan.mohamad,
	andriy.shevchenko, vineetha.g.jaya.kumaran

From: "Lai, Poey Seng" <poey.seng.lai@intel.com>

Enable PWM support for the Intel Keem Bay SoC.

Signed-off-by: Lai, Poey Seng <poey.seng.lai@intel.com>
Signed-off-by: Vineetha G. Jaya Kumaran <vineetha.g.jaya.kumaran@intel.com>
---
 drivers/pwm/Kconfig       |   9 ++
 drivers/pwm/Makefile      |   1 +
 drivers/pwm/pwm-keembay.c | 308 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 318 insertions(+)
 create mode 100644 drivers/pwm/pwm-keembay.c

diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index c13d146..5311975 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -569,4 +569,13 @@ config PWM_ZX
 	  To compile this driver as a module, choose M here: the module
 	  will be called pwm-zx.
 
+config PWM_KEEMBAY
+	tristate "Intel Keem Bay PWM driver"
+	depends on ARM64
+	help
+	  The platform driver for Intel Keem Bay PWM controller.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called pwm-keembay.
+
 endif
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index a59c710..0c84ff2 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -55,3 +55,4 @@ obj-$(CONFIG_PWM_TWL)		+= pwm-twl.o
 obj-$(CONFIG_PWM_TWL_LED)	+= pwm-twl-led.o
 obj-$(CONFIG_PWM_VT8500)	+= pwm-vt8500.o
 obj-$(CONFIG_PWM_ZX)		+= pwm-zx.o
+obj-$(CONFIG_PWM_KEEMBAY)	+= pwm-keembay.o
diff --git a/drivers/pwm/pwm-keembay.c b/drivers/pwm/pwm-keembay.c
new file mode 100644
index 0000000..39c7310
--- /dev/null
+++ b/drivers/pwm/pwm-keembay.c
@@ -0,0 +1,308 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel Keem Bay PWM driver
+ *
+ * Copyright (C) 2020 Intel Corporation
+ * Authors: Lai Poey Seng <poey.seng.lai@intel.com>
+ *          Vineetha G. Jaya Kumaran <vineetha.g.jaya.kumaran@intel.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+
+#define TOTAL_PWM_CHANNELS		6
+#define LEAD_IN_DEFAULT			0
+#define PWM_COUNT_MAX			65535
+
+#define KEEMBAY_PWM_EN_BIT		31
+
+/* Mask */
+#define KEEMBAY_PWM_RPT_CNT_MASK	GENMASK(15, 0)
+#define KEEMBAY_PWM_LEAD_IN_MASK	GENMASK(30, 16)
+#define KEEMBAY_PWM_HIGH_MASK		GENMASK(31, 16)
+#define KEEMBAY_PWM_LOW_MASK		GENMASK(15, 0)
+
+/* PWM Register offset */
+#define PWM_LEADIN0_OFFSET		0x00
+#define PWM_LEADIN1_OFFSET		0x04
+#define PWM_LEADIN2_OFFSET		0x08
+#define PWM_LEADIN3_OFFSET		0x0c
+#define PWM_LEADIN4_OFFSET		0x10
+#define PWM_LEADIN5_OFFSET		0x14
+
+#define PWM_HIGHLOW0_OFFSET		0x20
+#define PWM_HIGHLOW1_OFFSET		0x24
+#define PWM_HIGHLOW2_OFFSET		0x28
+#define PWM_HIGHLOW3_OFFSET		0x2c
+#define PWM_HIGHLOW4_OFFSET		0x30
+#define PWM_HIGHLOW5_OFFSET		0x34
+
+struct keembay_pwm {
+	struct pwm_chip chip;
+	struct device *dev;
+	struct clk *clk;
+	void __iomem *regmap;
+};
+
+static inline struct keembay_pwm *to_keembay_pwm_dev(struct pwm_chip *chip)
+{
+	return container_of(chip, struct keembay_pwm, chip);
+}
+
+static inline void keembay_pwm_enable_channel(struct keembay_pwm *priv, int ch)
+{
+	u32 buff, offset;
+	void __iomem *address;
+
+	offset = PWM_LEADIN0_OFFSET + ch * 4;
+	address = priv->regmap + offset;
+	buff = readl(address);
+	buff |= BIT(KEEMBAY_PWM_EN_BIT);
+	writel(buff, address);
+}
+
+static inline void keembay_pwm_disable_channel(struct keembay_pwm *priv, int ch)
+{
+	u32 buff, offset;
+	void __iomem *address;
+
+	offset = PWM_LEADIN0_OFFSET + ch * 4;
+	address = priv->regmap + offset;
+	buff = readl(address);
+	buff &= ~BIT(KEEMBAY_PWM_EN_BIT);
+	writel(buff, address);
+}
+
+static inline void keembay_pwm_update_bits(struct keembay_pwm *priv, u32 mask,
+					   u32 val, u32 reg, int ch)
+{
+	u32 buff, offset, tmp;
+	void __iomem *address;
+
+	offset = reg + ch * 4;
+	address = priv->regmap + offset;
+	buff = readl(address);
+	tmp = buff & ~mask;
+	tmp |= FIELD_PREP(mask, val);
+	writel(tmp, address);
+}
+
+static inline u32 keembay_pwm_config_min(struct keembay_pwm *priv)
+{
+	unsigned long long divd, divs;
+
+	divd = NSEC_PER_SEC;
+	divs = clk_get_rate(priv->clk);
+	do_div(divd, divs);
+
+	return (u32)divd;
+}
+
+static inline u16 keembay_pwm_config_duty_cycle(struct keembay_pwm *priv,
+						int duty_ns, u32 ns_min)
+{
+	unsigned long long divd;
+
+	divd = duty_ns;
+	do_div(divd, ns_min);
+	if ((u16)divd == 0)
+		return 0;
+
+	return (u16)divd - 1;
+}
+
+static inline u16 keembay_pwm_config_period(struct keembay_pwm *priv,
+					    int period_ns,
+					    int duty_ns,
+					    u32 ns_min)
+{
+	unsigned long long divd;
+
+	divd = period_ns - duty_ns;
+	do_div(divd, ns_min);
+	if ((u16)divd == 0)
+		return 0;
+
+	return (u16)divd - 1;
+}
+
+/*
+ *	For calculating "high time" register value:
+ *	High time (quotient only) = duty_cycle / ns_min
+ *
+ *	For calculating "low time" register value:
+ *	Low time (quotient only) = (period - duty_cycle) / ns_min
+ *
+ *	All values used are in nanoseconds for calculation.
+ */
+static int keembay_pwm_config(struct keembay_pwm *priv, int ch,
+			      int duty_ns, int period_ns, int count)
+{
+	u32 ns_min;
+	u16 pwm_h_count, pwm_l_count;
+
+	/* Write to lead in */
+	keembay_pwm_update_bits(priv, KEEMBAY_PWM_LEAD_IN_MASK,
+				LEAD_IN_DEFAULT,
+				PWM_LEADIN0_OFFSET, ch);
+
+	/* Write the number of PWM pulse repetition */
+	keembay_pwm_update_bits(priv, KEEMBAY_PWM_RPT_CNT_MASK, count,
+				PWM_LEADIN0_OFFSET, ch);
+
+	/* Calculate min */
+	ns_min = keembay_pwm_config_min(priv);
+
+	/* For duty cycle */
+	pwm_h_count = keembay_pwm_config_duty_cycle(priv, duty_ns, ns_min);
+
+	/* Write to high registers */
+	keembay_pwm_update_bits(priv, KEEMBAY_PWM_HIGH_MASK, pwm_h_count,
+				PWM_HIGHLOW0_OFFSET, ch);
+
+	/* For period */
+	pwm_l_count = keembay_pwm_config_period(priv, period_ns, duty_ns,
+						ns_min);
+
+	/* Write to low registers */
+	keembay_pwm_update_bits(priv, KEEMBAY_PWM_LOW_MASK, pwm_l_count,
+				PWM_HIGHLOW0_OFFSET, ch);
+
+	return 0;
+}
+
+static int keembay_pwm_enable(struct keembay_pwm *priv, int ch)
+{
+	int ret;
+
+	ret = clk_enable(priv->clk);
+	if (ret)
+		return ret;
+
+	/* Enable channel */
+	keembay_pwm_enable_channel(priv, ch);
+
+	return 0;
+}
+
+static void keembay_pwm_disable(struct keembay_pwm *priv, int ch)
+{
+	/* Disable channel */
+	keembay_pwm_disable_channel(priv, ch);
+
+	clk_disable(priv->clk);
+}
+
+static int keembay_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+			     const struct pwm_state *state)
+{
+	struct keembay_pwm *priv = to_keembay_pwm_dev(chip);
+
+	if (!state->enabled && pwm_is_enabled(pwm)) {
+		keembay_pwm_disable(priv, pwm->hwpwm);
+		return 0;
+	}
+
+	if (state->count > PWM_COUNT_MAX)
+		return -EINVAL;
+
+	if (state->polarity != pwm_get_polarity(pwm))
+		return -ENOSYS;
+
+	keembay_pwm_config(priv, pwm->hwpwm, state->duty_cycle,
+			   state->period, state->count);
+
+	if (state->enabled && !pwm_is_enabled(pwm))
+		return keembay_pwm_enable(priv, pwm->hwpwm);
+
+	return 0;
+}
+
+static const struct pwm_ops keembay_pwm_ops = {
+	.owner = THIS_MODULE,
+	.apply = keembay_pwm_apply,
+};
+
+static int keembay_pwm_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct keembay_pwm *priv;
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(priv->clk))
+		return PTR_ERR(priv->clk);
+
+	/*
+	 * Prepare clock here, and carry out clock enabling/disabling
+	 * during channel enablement/disablement.
+	 * The clock will not be unprepared due to shared usage with GPIO.
+	 */
+	ret = clk_prepare(priv->clk);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to prepare PWM clock\n");
+		return ret;
+	}
+
+	priv->regmap = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(priv->regmap))
+		return PTR_ERR(priv->regmap);
+
+	priv->chip.base = -1;
+	priv->chip.dev = dev;
+	priv->chip.ops = &keembay_pwm_ops;
+	priv->chip.npwm = TOTAL_PWM_CHANNELS;
+
+	ret = pwmchip_add(&priv->chip);
+	if (ret < 0) {
+		dev_err(dev, "Failed to add PWM chip: %d\n", ret);
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, priv);
+
+	return 0;
+}
+
+static int keembay_pwm_remove(struct platform_device *pdev)
+{
+	struct keembay_pwm *priv = platform_get_drvdata(pdev);
+	unsigned int i;
+
+	for (i = 0; i < priv->chip.npwm; i++)
+		pwm_disable(&priv->chip.pwms[i]);
+
+	pwmchip_remove(&priv->chip);
+
+	return 0;
+}
+
+static const struct of_device_id keembay_pwm_of_match[] = {
+	{ .compatible = "intel,keembay-pwm" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, keembay_pwm_of_match);
+
+static struct platform_driver keembay_pwm_driver = {
+	.probe	= keembay_pwm_probe,
+	.remove	= keembay_pwm_remove,
+	.driver	= {
+		.name = "pwm-keembay",
+		.of_match_table = keembay_pwm_of_match,
+	},
+};
+module_platform_driver(keembay_pwm_driver);
+
+MODULE_ALIAS("platform:keembay");
+MODULE_DESCRIPTION("Intel Keem Bay PWM driver");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1


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

* [PATCH 3/3] dt-bindings: pwm: keembay: Add bindings for Intel Keem Bay PWM
  2020-05-17 13:52 ` vineetha.g.jaya.kumaran-ral2JQCrhuEAvxtiuMwx3w
                   ` (2 preceding siblings ...)
  (?)
@ 2020-05-17 13:52 ` vineetha.g.jaya.kumaran
  2020-05-18 14:18     ` Rob Herring
  2020-05-18 14:21   ` Rob Herring
  -1 siblings, 2 replies; 19+ messages in thread
From: vineetha.g.jaya.kumaran @ 2020-05-17 13:52 UTC (permalink / raw)
  To: thierry.reding, u.kleine-koenig, robh+dt
  Cc: linux-pwm, devicetree, wan.ahmad.zainie.wan.mohamad,
	andriy.shevchenko, vineetha.g.jaya.kumaran

From: "Vineetha G. Jaya Kumaran" <vineetha.g.jaya.kumaran@intel.com>

Add PWM Device Tree bindings documentation for the Intel Keem Bay SoC.

Signed-off-by: Vineetha G. Jaya Kumaran <vineetha.g.jaya.kumaran@intel.com>
---
 .../devicetree/bindings/pwm/pwm-keembay.yaml       | 39 ++++++++++++++++++++++
 1 file changed, 39 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pwm/pwm-keembay.yaml

diff --git a/Documentation/devicetree/bindings/pwm/pwm-keembay.yaml b/Documentation/devicetree/bindings/pwm/pwm-keembay.yaml
new file mode 100644
index 0000000..00968d7
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/pwm-keembay.yaml
@@ -0,0 +1,39 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2020 Intel Corporation
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pwm/pwm-keembay.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Intel Keem Bay PWM Device Tree Bindings
+
+maintainers:
+  - Vineetha G. Jaya Kumaran <vineetha.g.jaya.kumaran@intel.com>
+
+allOf:
+  - $ref: pwm.yaml#
+
+properties:
+  compatible:
+    enum:
+      - intel,keembay-pwm
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    description:
+        phandle to the reference clock.
+
+required:
+ - compatible
+ - reg
+ - clocks
+
+examples:
+  - |
+    pwm@203200a0 {
+      compatible = "intel,keembay-pwm";
+      reg = <0x0 0x203200a0 0x0 0xe8>;
+      clocks = <&scmi_clk KEEM_BAY_A53_GPIO>;
+    };
-- 
1.9.1


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

* Re: [PATCH 3/3] dt-bindings: pwm: keembay: Add bindings for Intel Keem Bay PWM
@ 2020-05-18 14:18     ` Rob Herring
  0 siblings, 0 replies; 19+ messages in thread
From: Rob Herring @ 2020-05-18 14:18 UTC (permalink / raw)
  To: vineetha.g.jaya.kumaran
  Cc: devicetree, u.kleine-koenig, wan.ahmad.zainie.wan.mohamad,
	robh+dt, linux-pwm, andriy.shevchenko, thierry.reding

On Sun, 17 May 2020 21:52:40 +0800,  wrote:
> From: "Vineetha G. Jaya Kumaran" <vineetha.g.jaya.kumaran@intel.com>
> 
> Add PWM Device Tree bindings documentation for the Intel Keem Bay SoC.
> 
> Signed-off-by: Vineetha G. Jaya Kumaran <vineetha.g.jaya.kumaran@intel.com>
> ---
>  .../devicetree/bindings/pwm/pwm-keembay.yaml       | 39 ++++++++++++++++++++++
>  1 file changed, 39 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/pwm/pwm-keembay.yaml
> 


My bot found errors running 'make dt_binding_check' on your patch:

Error: Documentation/devicetree/bindings/pwm/pwm-keembay.example.dts:22.31-32 syntax error
FATAL ERROR: Unable to parse input tree
scripts/Makefile.lib:312: recipe for target 'Documentation/devicetree/bindings/pwm/pwm-keembay.example.dt.yaml' failed
make[1]: *** [Documentation/devicetree/bindings/pwm/pwm-keembay.example.dt.yaml] Error 1
Makefile:1300: recipe for target 'dt_binding_check' failed
make: *** [dt_binding_check] Error 2

See https://patchwork.ozlabs.org/patch/1292157

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure dt-schema is up to date:

pip3 install git+https://github.com/devicetree-org/dt-schema.git@master --upgrade

Please check and re-submit.


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

* Re: [PATCH 3/3] dt-bindings: pwm: keembay: Add bindings for Intel Keem Bay PWM
@ 2020-05-18 14:18     ` Rob Herring
  0 siblings, 0 replies; 19+ messages in thread
From: Rob Herring @ 2020-05-18 14:18 UTC (permalink / raw)
  To: vineetha.g.jaya.kumaran-ral2JQCrhuEAvxtiuMwx3w
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	u.kleine-koenig-bIcnvbaLZ9MEGnE8C9+IrQ,
	wan.ahmad.zainie.wan.mohamad-ral2JQCrhuEAvxtiuMwx3w,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, linux-pwm-u79uwXL29TY76Z2rM5mHXA,
	andriy.shevchenko-ral2JQCrhuEAvxtiuMwx3w,
	thierry.reding-Re5JQEeQqe8AvxtiuMwx3w

On Sun, 17 May 2020 21:52:40 +0800,  wrote:
> From: "Vineetha G. Jaya Kumaran" <vineetha.g.jaya.kumaran-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
> 
> Add PWM Device Tree bindings documentation for the Intel Keem Bay SoC.
> 
> Signed-off-by: Vineetha G. Jaya Kumaran <vineetha.g.jaya.kumaran-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
> ---
>  .../devicetree/bindings/pwm/pwm-keembay.yaml       | 39 ++++++++++++++++++++++
>  1 file changed, 39 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/pwm/pwm-keembay.yaml
> 


My bot found errors running 'make dt_binding_check' on your patch:

Error: Documentation/devicetree/bindings/pwm/pwm-keembay.example.dts:22.31-32 syntax error
FATAL ERROR: Unable to parse input tree
scripts/Makefile.lib:312: recipe for target 'Documentation/devicetree/bindings/pwm/pwm-keembay.example.dt.yaml' failed
make[1]: *** [Documentation/devicetree/bindings/pwm/pwm-keembay.example.dt.yaml] Error 1
Makefile:1300: recipe for target 'dt_binding_check' failed
make: *** [dt_binding_check] Error 2

See https://patchwork.ozlabs.org/patch/1292157

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure dt-schema is up to date:

pip3 install git+https://github.com/devicetree-org/dt-schema.git@master --upgrade

Please check and re-submit.

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

* Re: [PATCH 3/3] dt-bindings: pwm: keembay: Add bindings for Intel Keem Bay PWM
  2020-05-17 13:52 ` [PATCH 3/3] dt-bindings: pwm: keembay: Add bindings for Intel Keem Bay PWM vineetha.g.jaya.kumaran
  2020-05-18 14:18     ` Rob Herring
@ 2020-05-18 14:21   ` Rob Herring
  2020-05-20 10:52       ` G Jaya Kumaran, Vineetha
  1 sibling, 1 reply; 19+ messages in thread
From: Rob Herring @ 2020-05-18 14:21 UTC (permalink / raw)
  To: vineetha.g.jaya.kumaran
  Cc: thierry.reding, u.kleine-koenig, linux-pwm, devicetree,
	wan.ahmad.zainie.wan.mohamad, andriy.shevchenko

On Sun, May 17, 2020 at 09:52:40PM +0800, vineetha.g.jaya.kumaran@intel.com wrote:
> From: "Vineetha G. Jaya Kumaran" <vineetha.g.jaya.kumaran@intel.com>
> 
> Add PWM Device Tree bindings documentation for the Intel Keem Bay SoC.
> 
> Signed-off-by: Vineetha G. Jaya Kumaran <vineetha.g.jaya.kumaran@intel.com>
> ---
>  .../devicetree/bindings/pwm/pwm-keembay.yaml       | 39 ++++++++++++++++++++++
>  1 file changed, 39 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/pwm/pwm-keembay.yaml

Use compatible string for filename: intel,keembay-pwn.yaml

> 
> diff --git a/Documentation/devicetree/bindings/pwm/pwm-keembay.yaml b/Documentation/devicetree/bindings/pwm/pwm-keembay.yaml
> new file mode 100644
> index 0000000..00968d7
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pwm/pwm-keembay.yaml
> @@ -0,0 +1,39 @@
> +# SPDX-License-Identifier: GPL-2.0

Dual license new bindings:

(GPL-2.0-only OR BSD-2-Clause)

> +# Copyright (C) 2020 Intel Corporation
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/pwm/pwm-keembay.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Intel Keem Bay PWM Device Tree Bindings
> +
> +maintainers:
> +  - Vineetha G. Jaya Kumaran <vineetha.g.jaya.kumaran@intel.com>
> +
> +allOf:
> +  - $ref: pwm.yaml#
> +
> +properties:
> +  compatible:
> +    enum:
> +      - intel,keembay-pwm
> +
> +  reg:
> +    maxItems: 1
> +
> +  clocks:
> +    description:
> +        phandle to the reference clock.

How many clocks? (maxItems: 1?)

You can drop the description.

> +
> +required:
> + - compatible
> + - reg
> + - clocks
> +
> +examples:
> +  - |
> +    pwm@203200a0 {
> +      compatible = "intel,keembay-pwm";
> +      reg = <0x0 0x203200a0 0x0 0xe8>;
> +      clocks = <&scmi_clk KEEM_BAY_A53_GPIO>;
> +    };
> -- 
> 1.9.1
> 

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

* RE: [PATCH 3/3] dt-bindings: pwm: keembay: Add bindings for Intel Keem Bay PWM
  2020-05-18 14:18     ` Rob Herring
@ 2020-05-20 10:49       ` G Jaya Kumaran, Vineetha
  -1 siblings, 0 replies; 19+ messages in thread
From: G Jaya Kumaran, Vineetha @ 2020-05-20 10:49 UTC (permalink / raw)
  To: Rob Herring
  Cc: devicetree, u.kleine-koenig, Wan Mohamad, Wan Ahmad Zainie,
	robh+dt, linux-pwm, Shevchenko, Andriy, thierry.reding


> -----Original Message-----
> From: Rob Herring <robh@kernel.org>
> Sent: Monday, May 18, 2020 10:19 PM
> To: G Jaya Kumaran, Vineetha <vineetha.g.jaya.kumaran@intel.com>
> Cc: devicetree@vger.kernel.org; u.kleine-koenig@pengutronix.de; Wan
> Mohamad, Wan Ahmad Zainie <wan.ahmad.zainie.wan.mohamad@intel.com>;
> robh+dt@kernel.org; linux-pwm@vger.kernel.org; Shevchenko, Andriy
> <andriy.shevchenko@intel.com>; thierry.reding@gmail.com
> Subject: Re: [PATCH 3/3] dt-bindings: pwm: keembay: Add bindings for Intel
> Keem Bay PWM
> 
> On Sun, 17 May 2020 21:52:40 +0800,  wrote:
> > From: "Vineetha G. Jaya Kumaran" <vineetha.g.jaya.kumaran@intel.com>
> >
> > Add PWM Device Tree bindings documentation for the Intel Keem Bay SoC.
> >
> > Signed-off-by: Vineetha G. Jaya Kumaran
> > <vineetha.g.jaya.kumaran@intel.com>
> > ---
> >  .../devicetree/bindings/pwm/pwm-keembay.yaml       | 39
> ++++++++++++++++++++++
> >  1 file changed, 39 insertions(+)
> >  create mode 100644
> > Documentation/devicetree/bindings/pwm/pwm-keembay.yaml
> >
> 
> 
> My bot found errors running 'make dt_binding_check' on your patch:
> 
> Error: Documentation/devicetree/bindings/pwm/pwm-
> keembay.example.dts:22.31-32 syntax error FATAL ERROR: Unable to parse
> input tree
> scripts/Makefile.lib:312: recipe for target
> 'Documentation/devicetree/bindings/pwm/pwm-keembay.example.dt.yaml'
> failed
> make[1]: *** [Documentation/devicetree/bindings/pwm/pwm-
> keembay.example.dt.yaml] Error 1
> Makefile:1300: recipe for target 'dt_binding_check' failed
> make: *** [dt_binding_check] Error 2
> 
> See https://patchwork.ozlabs.org/patch/1292157
> 
> If you already ran 'make dt_binding_check' and didn't see the above error(s),
> then make sure dt-schema is up to date:
> 
> pip3 install git+https://github.com/devicetree-org/dt-schema.git@master --
> upgrade
> 
> Please check and re-submit.

Thank you for reviewing. Will check this again and make sure it passes before submitting v2.

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

* RE: [PATCH 3/3] dt-bindings: pwm: keembay: Add bindings for Intel Keem Bay PWM
@ 2020-05-20 10:49       ` G Jaya Kumaran, Vineetha
  0 siblings, 0 replies; 19+ messages in thread
From: G Jaya Kumaran, Vineetha @ 2020-05-20 10:49 UTC (permalink / raw)
  To: Rob Herring
  Cc: devicetree, u.kleine-koenig, Wan Mohamad, Wan Ahmad Zainie,
	robh+dt, linux-pwm, Shevchenko, Andriy, thierry.reding


> -----Original Message-----
> From: Rob Herring <robh@kernel.org>
> Sent: Monday, May 18, 2020 10:19 PM
> To: G Jaya Kumaran, Vineetha <vineetha.g.jaya.kumaran@intel.com>
> Cc: devicetree@vger.kernel.org; u.kleine-koenig@pengutronix.de; Wan
> Mohamad, Wan Ahmad Zainie <wan.ahmad.zainie.wan.mohamad@intel.com>;
> robh+dt@kernel.org; linux-pwm@vger.kernel.org; Shevchenko, Andriy
> <andriy.shevchenko@intel.com>; thierry.reding@gmail.com
> Subject: Re: [PATCH 3/3] dt-bindings: pwm: keembay: Add bindings for Intel
> Keem Bay PWM
> 
> On Sun, 17 May 2020 21:52:40 +0800,  wrote:
> > From: "Vineetha G. Jaya Kumaran" <vineetha.g.jaya.kumaran@intel.com>
> >
> > Add PWM Device Tree bindings documentation for the Intel Keem Bay SoC.
> >
> > Signed-off-by: Vineetha G. Jaya Kumaran
> > <vineetha.g.jaya.kumaran@intel.com>
> > ---
> >  .../devicetree/bindings/pwm/pwm-keembay.yaml       | 39
> ++++++++++++++++++++++
> >  1 file changed, 39 insertions(+)
> >  create mode 100644
> > Documentation/devicetree/bindings/pwm/pwm-keembay.yaml
> >
> 
> 
> My bot found errors running 'make dt_binding_check' on your patch:
> 
> Error: Documentation/devicetree/bindings/pwm/pwm-
> keembay.example.dts:22.31-32 syntax error FATAL ERROR: Unable to parse
> input tree
> scripts/Makefile.lib:312: recipe for target
> 'Documentation/devicetree/bindings/pwm/pwm-keembay.example.dt.yaml'
> failed
> make[1]: *** [Documentation/devicetree/bindings/pwm/pwm-
> keembay.example.dt.yaml] Error 1
> Makefile:1300: recipe for target 'dt_binding_check' failed
> make: *** [dt_binding_check] Error 2
> 
> See https://patchwork.ozlabs.org/patch/1292157
> 
> If you already ran 'make dt_binding_check' and didn't see the above error(s),
> then make sure dt-schema is up to date:
> 
> pip3 install git+https://github.com/devicetree-org/dt-schema.git@master --
> upgrade
> 
> Please check and re-submit.

Thank you for reviewing. Will check this again and make sure it passes before submitting v2.

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

* RE: [PATCH 3/3] dt-bindings: pwm: keembay: Add bindings for Intel Keem Bay PWM
  2020-05-18 14:21   ` Rob Herring
@ 2020-05-20 10:52       ` G Jaya Kumaran, Vineetha
  0 siblings, 0 replies; 19+ messages in thread
From: G Jaya Kumaran, Vineetha @ 2020-05-20 10:52 UTC (permalink / raw)
  To: Rob Herring
  Cc: thierry.reding, u.kleine-koenig, linux-pwm, devicetree,
	Wan Mohamad, Wan Ahmad Zainie, Shevchenko, Andriy


> -----Original Message-----
> From: Rob Herring <robh@kernel.org>
> Sent: Monday, May 18, 2020 10:22 PM
> To: G Jaya Kumaran, Vineetha <vineetha.g.jaya.kumaran@intel.com>
> Cc: thierry.reding@gmail.com; u.kleine-koenig@pengutronix.de; linux-
> pwm@vger.kernel.org; devicetree@vger.kernel.org; Wan Mohamad, Wan
> Ahmad Zainie <wan.ahmad.zainie.wan.mohamad@intel.com>; Shevchenko,
> Andriy <andriy.shevchenko@intel.com>
> Subject: Re: [PATCH 3/3] dt-bindings: pwm: keembay: Add bindings for Intel
> Keem Bay PWM
> 
> On Sun, May 17, 2020 at 09:52:40PM +0800,
> vineetha.g.jaya.kumaran@intel.com wrote:
> > From: "Vineetha G. Jaya Kumaran" <vineetha.g.jaya.kumaran@intel.com>
> >
> > Add PWM Device Tree bindings documentation for the Intel Keem Bay SoC.
> >
> > Signed-off-by: Vineetha G. Jaya Kumaran
> > <vineetha.g.jaya.kumaran@intel.com>
> > ---
> >  .../devicetree/bindings/pwm/pwm-keembay.yaml       | 39
> ++++++++++++++++++++++
> >  1 file changed, 39 insertions(+)
> >  create mode 100644
> > Documentation/devicetree/bindings/pwm/pwm-keembay.yaml
> 
> Use compatible string for filename: intel,keembay-pwn.yaml
> 

Will fix the filename in v2.

> >
> > diff --git a/Documentation/devicetree/bindings/pwm/pwm-keembay.yaml
> > b/Documentation/devicetree/bindings/pwm/pwm-keembay.yaml
> > new file mode 100644
> > index 0000000..00968d7
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/pwm/pwm-keembay.yaml
> > @@ -0,0 +1,39 @@
> > +# SPDX-License-Identifier: GPL-2.0
> 
> Dual license new bindings:
> 
> (GPL-2.0-only OR BSD-2-Clause)
> 

OK, will update the licensing info.

> > +# Copyright (C) 2020 Intel Corporation %YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/pwm/pwm-keembay.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Intel Keem Bay PWM Device Tree Bindings
> > +
> > +maintainers:
> > +  - Vineetha G. Jaya Kumaran <vineetha.g.jaya.kumaran@intel.com>
> > +
> > +allOf:
> > +  - $ref: pwm.yaml#
> > +
> > +properties:
> > +  compatible:
> > +    enum:
> > +      - intel,keembay-pwm
> > +
> > +  reg:
> > +    maxItems: 1
> > +
> > +  clocks:
> > +    description:
> > +        phandle to the reference clock.
> 
> How many clocks? (maxItems: 1?)
> 
> You can drop the description.
> 

1 clock is needed for this case, will add in maxItems and drop the description.

> > +
> > +required:
> > + - compatible
> > + - reg
> > + - clocks
> > +
> > +examples:
> > +  - |
> > +    pwm@203200a0 {
> > +      compatible = "intel,keembay-pwm";
> > +      reg = <0x0 0x203200a0 0x0 0xe8>;
> > +      clocks = <&scmi_clk KEEM_BAY_A53_GPIO>;
> > +    };
> > --
> > 1.9.1
> >

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

* RE: [PATCH 3/3] dt-bindings: pwm: keembay: Add bindings for Intel Keem Bay PWM
@ 2020-05-20 10:52       ` G Jaya Kumaran, Vineetha
  0 siblings, 0 replies; 19+ messages in thread
From: G Jaya Kumaran, Vineetha @ 2020-05-20 10:52 UTC (permalink / raw)
  To: Rob Herring
  Cc: thierry.reding, u.kleine-koenig, linux-pwm, devicetree,
	Wan Mohamad, Wan Ahmad Zainie, Shevchenko, Andriy


> -----Original Message-----
> From: Rob Herring <robh@kernel.org>
> Sent: Monday, May 18, 2020 10:22 PM
> To: G Jaya Kumaran, Vineetha <vineetha.g.jaya.kumaran@intel.com>
> Cc: thierry.reding@gmail.com; u.kleine-koenig@pengutronix.de; linux-
> pwm@vger.kernel.org; devicetree@vger.kernel.org; Wan Mohamad, Wan
> Ahmad Zainie <wan.ahmad.zainie.wan.mohamad@intel.com>; Shevchenko,
> Andriy <andriy.shevchenko@intel.com>
> Subject: Re: [PATCH 3/3] dt-bindings: pwm: keembay: Add bindings for Intel
> Keem Bay PWM
> 
> On Sun, May 17, 2020 at 09:52:40PM +0800,
> vineetha.g.jaya.kumaran@intel.com wrote:
> > From: "Vineetha G. Jaya Kumaran" <vineetha.g.jaya.kumaran@intel.com>
> >
> > Add PWM Device Tree bindings documentation for the Intel Keem Bay SoC.
> >
> > Signed-off-by: Vineetha G. Jaya Kumaran
> > <vineetha.g.jaya.kumaran@intel.com>
> > ---
> >  .../devicetree/bindings/pwm/pwm-keembay.yaml       | 39
> ++++++++++++++++++++++
> >  1 file changed, 39 insertions(+)
> >  create mode 100644
> > Documentation/devicetree/bindings/pwm/pwm-keembay.yaml
> 
> Use compatible string for filename: intel,keembay-pwn.yaml
> 

Will fix the filename in v2.

> >
> > diff --git a/Documentation/devicetree/bindings/pwm/pwm-keembay.yaml
> > b/Documentation/devicetree/bindings/pwm/pwm-keembay.yaml
> > new file mode 100644
> > index 0000000..00968d7
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/pwm/pwm-keembay.yaml
> > @@ -0,0 +1,39 @@
> > +# SPDX-License-Identifier: GPL-2.0
> 
> Dual license new bindings:
> 
> (GPL-2.0-only OR BSD-2-Clause)
> 

OK, will update the licensing info.

> > +# Copyright (C) 2020 Intel Corporation %YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/pwm/pwm-keembay.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Intel Keem Bay PWM Device Tree Bindings
> > +
> > +maintainers:
> > +  - Vineetha G. Jaya Kumaran <vineetha.g.jaya.kumaran@intel.com>
> > +
> > +allOf:
> > +  - $ref: pwm.yaml#
> > +
> > +properties:
> > +  compatible:
> > +    enum:
> > +      - intel,keembay-pwm
> > +
> > +  reg:
> > +    maxItems: 1
> > +
> > +  clocks:
> > +    description:
> > +        phandle to the reference clock.
> 
> How many clocks? (maxItems: 1?)
> 
> You can drop the description.
> 

1 clock is needed for this case, will add in maxItems and drop the description.

> > +
> > +required:
> > + - compatible
> > + - reg
> > + - clocks
> > +
> > +examples:
> > +  - |
> > +    pwm@203200a0 {
> > +      compatible = "intel,keembay-pwm";
> > +      reg = <0x0 0x203200a0 0x0 0xe8>;
> > +      clocks = <&scmi_clk KEEM_BAY_A53_GPIO>;
> > +    };
> > --
> > 1.9.1
> >

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

* Re: [PATCH 1/3] pwm: Add count attribute in sysfs for Intel Keem Bay
  2020-05-17 13:52   ` vineetha.g.jaya.kumaran-ral2JQCrhuEAvxtiuMwx3w
  (?)
@ 2020-05-23 21:05   ` Uwe Kleine-König
  2020-06-05 13:49       ` G Jaya Kumaran, Vineetha
  -1 siblings, 1 reply; 19+ messages in thread
From: Uwe Kleine-König @ 2020-05-23 21:05 UTC (permalink / raw)
  To: vineetha.g.jaya.kumaran
  Cc: thierry.reding, robh+dt, linux-pwm, devicetree,
	wan.ahmad.zainie.wan.mohamad, andriy.shevchenko

On Sun, May 17, 2020 at 09:52:38PM +0800, vineetha.g.jaya.kumaran@intel.com wrote:
> From: "Lai, Poey Seng" <poey.seng.lai@intel.com>
> 
> In Keem Bay, the number of repetitions for the period/waveform
> can be configured from userspace. This requires addition of a sysfs
> attribute to get/set the repetition count. Setting this value to 0
> will result in continuous repetition of the waveform until the
> channel is disabled or reconfigured.

There is nothing specific for Keem Bay in this patch, the only special
thing here is that this driver is the first implementor.

IMHO all drivers that don't support count should be changed to fail if
a count > 0 is passed and introducing support in the sysfs interface
should be a separate change.

Having said that I'm not convinced this is a good idea given that only
very few driver can support this interface. Also this needs
documentation, e.g. what is expected from .get_state().

You should also motivate what this functionality is used for in the
commit log and I'd prefer to see an in-tree user (apart from sysfs which
I don't count as such).

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | https://www.pengutronix.de/ |

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

* Re: [PATCH 2/3] pwm: Add PWM driver for Intel Keem Bay
  2020-05-17 13:52 ` [PATCH 2/3] pwm: Add PWM driver " vineetha.g.jaya.kumaran
@ 2020-05-23 21:40   ` Uwe Kleine-König
  2020-06-05 13:48       ` G Jaya Kumaran, Vineetha
  0 siblings, 1 reply; 19+ messages in thread
From: Uwe Kleine-König @ 2020-05-23 21:40 UTC (permalink / raw)
  To: vineetha.g.jaya.kumaran
  Cc: thierry.reding, robh+dt, linux-pwm, devicetree,
	wan.ahmad.zainie.wan.mohamad, andriy.shevchenko

Hello,

On Sun, May 17, 2020 at 09:52:39PM +0800, vineetha.g.jaya.kumaran@intel.com wrote:
> From: "Lai, Poey Seng" <poey.seng.lai@intel.com>
> 
> Enable PWM support for the Intel Keem Bay SoC.
> 
> Signed-off-by: Lai, Poey Seng <poey.seng.lai@intel.com>
> Signed-off-by: Vineetha G. Jaya Kumaran <vineetha.g.jaya.kumaran@intel.com>
> ---
>  drivers/pwm/Kconfig       |   9 ++
>  drivers/pwm/Makefile      |   1 +
>  drivers/pwm/pwm-keembay.c | 308 ++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 318 insertions(+)
>  create mode 100644 drivers/pwm/pwm-keembay.c
> 
> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
> index c13d146..5311975 100644
> --- a/drivers/pwm/Kconfig
> +++ b/drivers/pwm/Kconfig
> @@ -569,4 +569,13 @@ config PWM_ZX
>  	  To compile this driver as a module, choose M here: the module
>  	  will be called pwm-zx.
>  
> +config PWM_KEEMBAY
> +	tristate "Intel Keem Bay PWM driver"
> +	depends on ARM64

Support for COMPILE_TEST would be nice here.

> +	help
> +	  The platform driver for Intel Keem Bay PWM controller.
> +
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called pwm-keembay.
> +
>  endif
> diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
> index a59c710..0c84ff2 100644
> --- a/drivers/pwm/Makefile
> +++ b/drivers/pwm/Makefile
> @@ -55,3 +55,4 @@ obj-$(CONFIG_PWM_TWL)		+= pwm-twl.o
>  obj-$(CONFIG_PWM_TWL_LED)	+= pwm-twl-led.o
>  obj-$(CONFIG_PWM_VT8500)	+= pwm-vt8500.o
>  obj-$(CONFIG_PWM_ZX)		+= pwm-zx.o
> +obj-$(CONFIG_PWM_KEEMBAY)	+= pwm-keembay.o
> diff --git a/drivers/pwm/pwm-keembay.c b/drivers/pwm/pwm-keembay.c
> new file mode 100644
> index 0000000..39c7310
> --- /dev/null
> +++ b/drivers/pwm/pwm-keembay.c
> @@ -0,0 +1,308 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Intel Keem Bay PWM driver
> + *
> + * Copyright (C) 2020 Intel Corporation
> + * Authors: Lai Poey Seng <poey.seng.lai@intel.com>
> + *          Vineetha G. Jaya Kumaran <vineetha.g.jaya.kumaran@intel.com>

Is there publically available documentation? If so, please add a link
here.

> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/clk.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/pwm.h>
> +#include <linux/regmap.h>
> +
> +#define TOTAL_PWM_CHANNELS		6
> +#define LEAD_IN_DEFAULT			0
> +#define PWM_COUNT_MAX			65535

please use a unique prefix for all defines in your driver. The names as
they are are too generic.

> +
> +#define KEEMBAY_PWM_EN_BIT		31
> +
> +/* Mask */
> +#define KEEMBAY_PWM_RPT_CNT_MASK	GENMASK(15, 0)
> +#define KEEMBAY_PWM_LEAD_IN_MASK	GENMASK(30, 16)
> +#define KEEMBAY_PWM_HIGH_MASK		GENMASK(31, 16)
> +#define KEEMBAY_PWM_LOW_MASK		GENMASK(15, 0)
> +
> +/* PWM Register offset */
> +#define PWM_LEADIN0_OFFSET		0x00
> +#define PWM_LEADIN1_OFFSET		0x04
> +#define PWM_LEADIN2_OFFSET		0x08
> +#define PWM_LEADIN3_OFFSET		0x0c
> +#define PWM_LEADIN4_OFFSET		0x10
> +#define PWM_LEADIN5_OFFSET		0x14
> +
> +#define PWM_HIGHLOW0_OFFSET		0x20
> +#define PWM_HIGHLOW1_OFFSET		0x24
> +#define PWM_HIGHLOW2_OFFSET		0x28
> +#define PWM_HIGHLOW3_OFFSET		0x2c
> +#define PWM_HIGHLOW4_OFFSET		0x30
> +#define PWM_HIGHLOW5_OFFSET		0x34
> +
> +struct keembay_pwm {
> +	struct pwm_chip chip;
> +	struct device *dev;
> +	struct clk *clk;
> +	void __iomem *regmap;

I'd call this member "base" instead of "regmap" as the latter has a
different meaning in the kernel.

> +};
> +
> +static inline struct keembay_pwm *to_keembay_pwm_dev(struct pwm_chip *chip)
> +{
> +	return container_of(chip, struct keembay_pwm, chip);
> +}
> +
> +static inline void keembay_pwm_enable_channel(struct keembay_pwm *priv, int ch)
> +{
> +	u32 buff, offset;
> +	void __iomem *address;
> +
> +	offset = PWM_LEADIN0_OFFSET + ch * 4;
> +	address = priv->regmap + offset;
> +	buff = readl(address);
> +	buff |= BIT(KEEMBAY_PWM_EN_BIT);
> +	writel(buff, address);
> +}
> +
> +static inline void keembay_pwm_disable_channel(struct keembay_pwm *priv, int ch)
> +{
> +	u32 buff, offset;
> +	void __iomem *address;
> +
> +	offset = PWM_LEADIN0_OFFSET + ch * 4;
> +	address = priv->regmap + offset;
> +	buff = readl(address);
> +	buff &= ~BIT(KEEMBAY_PWM_EN_BIT);
> +	writel(buff, address);
> +}

The two functions above could make use of keembay_pwm_update_bits().

> +
> +static inline void keembay_pwm_update_bits(struct keembay_pwm *priv, u32 mask,
> +					   u32 val, u32 reg, int ch)
> +{
> +	u32 buff, offset, tmp;
> +	void __iomem *address;
> +
> +	offset = reg + ch * 4;
> +	address = priv->regmap + offset;
> +	buff = readl(address);
> +	tmp = buff & ~mask;
> +	tmp |= FIELD_PREP(mask, val);
> +	writel(tmp, address);
> +}
> +
> +static inline u32 keembay_pwm_config_min(struct keembay_pwm *priv)
> +{
> +	unsigned long long divd, divs;
> +
> +	divd = NSEC_PER_SEC;
> +	divs = clk_get_rate(priv->clk);
> +	do_div(divd, divs);

Given that both NSEC_PER_SEC and the return value of clk_get_rate fit
into an unsigned long, this seems too much.

> +	return (u32)divd;
> +}
> +
> +static inline u16 keembay_pwm_config_duty_cycle(struct keembay_pwm *priv,
> +						int duty_ns, u32 ns_min)
> +{
> +	unsigned long long divd;
> +
> +	divd = duty_ns;
> +	do_div(divd, ns_min);
> +	if ((u16)divd == 0)
> +		return 0;
> +
> +	return (u16)divd - 1;

missing range checking.

> +}
> +
> +static inline u16 keembay_pwm_config_period(struct keembay_pwm *priv,
> +					    int period_ns,
> +					    int duty_ns,
> +					    u32 ns_min)
> +{
> +	unsigned long long divd;
> +
> +	divd = period_ns - duty_ns;
> +	do_div(divd, ns_min);
> +	if ((u16)divd == 0)
> +		return 0;
> +
> +	return (u16)divd - 1;

I wonder if both keembay_pwm_config_duty_cycle() and
keembay_pwm_config_period() would be easier to understand if they were not
separate functions but unrolled into their only user.

As above there is no range checking.

> +}
> +
> +/*
> + *	For calculating "high time" register value:
> + *	High time (quotient only) = duty_cycle / ns_min
> + *
> + *	For calculating "low time" register value:
> + *	Low time (quotient only) = (period - duty_cycle) / ns_min
> + *
> + *	All values used are in nanoseconds for calculation.
> + */
> +static int keembay_pwm_config(struct keembay_pwm *priv, int ch,
> +			      int duty_ns, int period_ns, int count)

this function is only called once, I wonder if it can better be folded
into its only user.

> +{
> +	u32 ns_min;
> +	u16 pwm_h_count, pwm_l_count;
> +
> +	/* Write to lead in */
> +	keembay_pwm_update_bits(priv, KEEMBAY_PWM_LEAD_IN_MASK,
> +				LEAD_IN_DEFAULT,
> +				PWM_LEADIN0_OFFSET, ch);

What is the effect of this "lead in"?

> +	/* Write the number of PWM pulse repetition */
> +	keembay_pwm_update_bits(priv, KEEMBAY_PWM_RPT_CNT_MASK, count,
> +				PWM_LEADIN0_OFFSET, ch);

Is this racy? E.g. if the PWM is already running and just after you
update the repetition count completes a period?

This writes to the same register as the lead in above. Can this be done
in a single register access?

> +	/* Calculate min */
> +	ns_min = keembay_pwm_config_min(priv);

What is "min"?

> +	/* For duty cycle */
> +	pwm_h_count = keembay_pwm_config_duty_cycle(priv, duty_ns, ns_min);

this is ineffective and less exact as possible. ns_min is the result of
a division and in keembay_pwm_config_duty_cycle you divide by it.

> +	/* Write to high registers */
> +	keembay_pwm_update_bits(priv, KEEMBAY_PWM_HIGH_MASK, pwm_h_count,
> +				PWM_HIGHLOW0_OFFSET, ch);
> +
> +	/* For period */
> +	pwm_l_count = keembay_pwm_config_period(priv, period_ns, duty_ns,
> +						ns_min);
> +
> +	/* Write to low registers */
> +	keembay_pwm_update_bits(priv, KEEMBAY_PWM_LOW_MASK, pwm_l_count,
> +				PWM_HIGHLOW0_OFFSET, ch);

Can you please explain in a comment what's the resulting wave form for a
given value of this register?

Can writing h_count and l_count be combined in a single register access?
(And if not, what happens if a period completes between the two
updates?)

How does the hardware behave on a change here? Is the currently running
period completed before the new values take effect?

> +
> +	return 0;
> +}
> +
> +static int keembay_pwm_enable(struct keembay_pwm *priv, int ch)
> +{
> +	int ret;
> +
> +	ret = clk_enable(priv->clk);
> +	if (ret)
> +		return ret;
> +
> +	/* Enable channel */
> +	keembay_pwm_enable_channel(priv, ch);
> +
> +	return 0;
> +}
> +
> +static void keembay_pwm_disable(struct keembay_pwm *priv, int ch)
> +{
> +	/* Disable channel */
> +	keembay_pwm_disable_channel(priv, ch);
> +
> +	clk_disable(priv->clk);
> +}
> +
> +static int keembay_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
> +			     const struct pwm_state *state)
> +{
> +	struct keembay_pwm *priv = to_keembay_pwm_dev(chip);
> +
> +	if (!state->enabled && pwm_is_enabled(pwm)) {

Please don't call API functions in the driver.

> +		keembay_pwm_disable(priv, pwm->hwpwm);

Is a currently running period completed here? How does the output behave
on disable? (i.e. does it go in its inactive state?)

> +		return 0;
> +	}
> +
> +	if (state->count > PWM_COUNT_MAX)
> +		return -EINVAL;
> +
> +	if (state->polarity != pwm_get_polarity(pwm))

Using:

	if (state->polarity != PWM_POLARITY_NORMAL)

seems both more robust and easier to understand.

> +		return -ENOSYS;
> +
> +	keembay_pwm_config(priv, pwm->hwpwm, state->duty_cycle,
> +			   state->period, state->count);
> +
> +	if (state->enabled && !pwm_is_enabled(pwm))
> +		return keembay_pwm_enable(priv, pwm->hwpwm);
> +
> +	return 0;
> +}
> +
> +static const struct pwm_ops keembay_pwm_ops = {
> +	.owner = THIS_MODULE,
> +	.apply = keembay_pwm_apply,

Please implement .get_state().

> +};
> +
> +static int keembay_pwm_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct keembay_pwm *priv;
> +	int ret;
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->clk = devm_clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(priv->clk))

Error message please. (Something like:

		int ret = PTR_ERR(ret);

		if (ret != -EPROBE_DEFER)
			dev_err(pdev->dev, "Failed to get clk: %pE\n", priv->clk);

		return ret;
)

> +		return PTR_ERR(priv->clk);
> +
> +	/*
> +	 * Prepare clock here, and carry out clock enabling/disabling
> +	 * during channel enablement/disablement.
> +	 * The clock will not be unprepared due to shared usage with GPIO.

What has this clock to do with GPIO? If the GPIO drivers gets and
enables this clock itself there should be no problem.

> +	 */
> +	ret = clk_prepare(priv->clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Failed to prepare PWM clock\n");

Please include the error code in the message (preferably using %pE as
above).

> +		return ret;
> +	}
> +
> +	priv->regmap = devm_platform_ioremap_resource(pdev, 0);
> +	if (IS_ERR(priv->regmap))
> +		return PTR_ERR(priv->regmap);
> +
> +	priv->chip.base = -1;
> +	priv->chip.dev = dev;
> +	priv->chip.ops = &keembay_pwm_ops;
> +	priv->chip.npwm = TOTAL_PWM_CHANNELS;
> +
> +	ret = pwmchip_add(&priv->chip);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to add PWM chip: %d\n", ret);

%pE please.

> +		return ret;
> +	}
> +
> +	platform_set_drvdata(pdev, priv);
> +
> +	return 0;
> +}
> +
> +static int keembay_pwm_remove(struct platform_device *pdev)
> +{
> +	struct keembay_pwm *priv = platform_get_drvdata(pdev);
> +	unsigned int i;
> +
> +	for (i = 0; i < priv->chip.npwm; i++)
> +		pwm_disable(&priv->chip.pwms[i]);

That's wrong. It's the responsibility of the consumer to disable the
clock.

> +
> +	pwmchip_remove(&priv->chip);

clk_unprepare missing here.

> +
> +	return 0;
> +}
> +
> +static const struct of_device_id keembay_pwm_of_match[] = {
> +	{ .compatible = "intel,keembay-pwm" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, keembay_pwm_of_match);
> +
> +static struct platform_driver keembay_pwm_driver = {
> +	.probe	= keembay_pwm_probe,
> +	.remove	= keembay_pwm_remove,
> +	.driver	= {
> +		.name = "pwm-keembay",
> +		.of_match_table = keembay_pwm_of_match,
> +	},
> +};
> +module_platform_driver(keembay_pwm_driver);
> +
> +MODULE_ALIAS("platform:keembay");
> +MODULE_DESCRIPTION("Intel Keem Bay PWM driver");
> +MODULE_LICENSE("GPL v2");

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | https://www.pengutronix.de/ |

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

* RE: [PATCH 2/3] pwm: Add PWM driver for Intel Keem Bay
@ 2020-06-05 13:48       ` G Jaya Kumaran, Vineetha
  0 siblings, 0 replies; 19+ messages in thread
From: G Jaya Kumaran, Vineetha @ 2020-06-05 13:48 UTC (permalink / raw)
  To: u.kleine-koenig
  Cc: devicetree, Wan Mohamad, Wan Ahmad Zainie, robh+dt, linux-pwm,
	Shevchenko, Andriy, thierry.reding

Hello Uwe,

Thank you for taking the time to review this patch set.

> -----Original Message-----
> From: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> Sent: Sunday, May 24, 2020 5:41 AM
> To: G Jaya Kumaran, Vineetha <vineetha.g.jaya.kumaran@intel.com>
> Cc: thierry.reding@gmail.com; robh+dt@kernel.org; linux-
> pwm@vger.kernel.org; devicetree@vger.kernel.org; Wan Mohamad, Wan
> Ahmad Zainie <wan.ahmad.zainie.wan.mohamad@intel.com>; Shevchenko,
> Andriy <andriy.shevchenko@intel.com>
> Subject: Re: [PATCH 2/3] pwm: Add PWM driver for Intel Keem Bay
> 
> Hello,
> 
> On Sun, May 17, 2020 at 09:52:39PM +0800,
> vineetha.g.jaya.kumaran@intel.com wrote:
> > From: "Lai, Poey Seng" <poey.seng.lai@intel.com>
> >
> > Enable PWM support for the Intel Keem Bay SoC.
> >
> > Signed-off-by: Lai, Poey Seng <poey.seng.lai@intel.com>
> > Signed-off-by: Vineetha G. Jaya Kumaran
> > <vineetha.g.jaya.kumaran@intel.com>
> > ---
> >  drivers/pwm/Kconfig       |   9 ++
> >  drivers/pwm/Makefile      |   1 +
> >  drivers/pwm/pwm-keembay.c | 308
> > ++++++++++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 318 insertions(+)
> >  create mode 100644 drivers/pwm/pwm-keembay.c
> >
> > diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index
> > c13d146..5311975 100644
> > --- a/drivers/pwm/Kconfig
> > +++ b/drivers/pwm/Kconfig
> > @@ -569,4 +569,13 @@ config PWM_ZX
> >  	  To compile this driver as a module, choose M here: the module
> >  	  will be called pwm-zx.
> >
> > +config PWM_KEEMBAY
> > +	tristate "Intel Keem Bay PWM driver"
> > +	depends on ARM64
> 
> Support for COMPILE_TEST would be nice here.
> 

I will add this in v2.

> > +	help
> > +	  The platform driver for Intel Keem Bay PWM controller.
> > +
> > +	  To compile this driver as a module, choose M here: the module
> > +	  will be called pwm-keembay.
> > +
> >  endif
> > diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index
> > a59c710..0c84ff2 100644
> > --- a/drivers/pwm/Makefile
> > +++ b/drivers/pwm/Makefile
> > @@ -55,3 +55,4 @@ obj-$(CONFIG_PWM_TWL)		+= pwm-twl.o
> >  obj-$(CONFIG_PWM_TWL_LED)	+= pwm-twl-led.o
> >  obj-$(CONFIG_PWM_VT8500)	+= pwm-vt8500.o
> >  obj-$(CONFIG_PWM_ZX)		+= pwm-zx.o
> > +obj-$(CONFIG_PWM_KEEMBAY)	+= pwm-keembay.o
> > diff --git a/drivers/pwm/pwm-keembay.c b/drivers/pwm/pwm-keembay.c new
> > file mode 100644 index 0000000..39c7310
> > --- /dev/null
> > +++ b/drivers/pwm/pwm-keembay.c
> > @@ -0,0 +1,308 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Intel Keem Bay PWM driver
> > + *
> > + * Copyright (C) 2020 Intel Corporation
> > + * Authors: Lai Poey Seng <poey.seng.lai@intel.com>
> > + *          Vineetha G. Jaya Kumaran <vineetha.g.jaya.kumaran@intel.com>
> 
> Is there publically available documentation? If so, please add a link here.
> 

Will check, and add it here if any are available.

> > + */
> > +
> > +#include <linux/bitfield.h>
> > +#include <linux/clk.h>
> > +#include <linux/io.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pwm.h>
> > +#include <linux/regmap.h>
> > +
> > +#define TOTAL_PWM_CHANNELS		6
> > +#define LEAD_IN_DEFAULT			0
> > +#define PWM_COUNT_MAX			65535
> 
> please use a unique prefix for all defines in your driver. The names as they are
> are too generic.
> 

I'll update the naming in v2, to match the mask defines.

> > +
> > +#define KEEMBAY_PWM_EN_BIT		31
> > +
> > +/* Mask */
> > +#define KEEMBAY_PWM_RPT_CNT_MASK	GENMASK(15, 0)
> > +#define KEEMBAY_PWM_LEAD_IN_MASK	GENMASK(30, 16)
> > +#define KEEMBAY_PWM_HIGH_MASK		GENMASK(31, 16)
> > +#define KEEMBAY_PWM_LOW_MASK		GENMASK(15, 0)
> > +
> > +/* PWM Register offset */
> > +#define PWM_LEADIN0_OFFSET		0x00
> > +#define PWM_LEADIN1_OFFSET		0x04
> > +#define PWM_LEADIN2_OFFSET		0x08
> > +#define PWM_LEADIN3_OFFSET		0x0c
> > +#define PWM_LEADIN4_OFFSET		0x10
> > +#define PWM_LEADIN5_OFFSET		0x14
> > +
> > +#define PWM_HIGHLOW0_OFFSET		0x20
> > +#define PWM_HIGHLOW1_OFFSET		0x24
> > +#define PWM_HIGHLOW2_OFFSET		0x28
> > +#define PWM_HIGHLOW3_OFFSET		0x2c
> > +#define PWM_HIGHLOW4_OFFSET		0x30
> > +#define PWM_HIGHLOW5_OFFSET		0x34
> > +
> > +struct keembay_pwm {
> > +	struct pwm_chip chip;
> > +	struct device *dev;
> > +	struct clk *clk;
> > +	void __iomem *regmap;
> 
> I'd call this member "base" instead of "regmap" as the latter has a different
> meaning in the kernel.
> 

OK, will update the naming.

> > +};
> > +
> > +static inline struct keembay_pwm *to_keembay_pwm_dev(struct pwm_chip
> > +*chip) {
> > +	return container_of(chip, struct keembay_pwm, chip); }
> > +
> > +static inline void keembay_pwm_enable_channel(struct keembay_pwm
> > +*priv, int ch) {
> > +	u32 buff, offset;
> > +	void __iomem *address;
> > +
> > +	offset = PWM_LEADIN0_OFFSET + ch * 4;
> > +	address = priv->regmap + offset;
> > +	buff = readl(address);
> > +	buff |= BIT(KEEMBAY_PWM_EN_BIT);
> > +	writel(buff, address);
> > +}
> > +
> > +static inline void keembay_pwm_disable_channel(struct keembay_pwm
> > +*priv, int ch) {
> > +	u32 buff, offset;
> > +	void __iomem *address;
> > +
> > +	offset = PWM_LEADIN0_OFFSET + ch * 4;
> > +	address = priv->regmap + offset;
> > +	buff = readl(address);
> > +	buff &= ~BIT(KEEMBAY_PWM_EN_BIT);
> > +	writel(buff, address);
> > +}
> 
> The two functions above could make use of keembay_pwm_update_bits().
> 

Agreed, will update them to reflect this.

> > +
> > +static inline void keembay_pwm_update_bits(struct keembay_pwm *priv, u32
> mask,
> > +					   u32 val, u32 reg, int ch)
> > +{
> > +	u32 buff, offset, tmp;
> > +	void __iomem *address;
> > +
> > +	offset = reg + ch * 4;
> > +	address = priv->regmap + offset;
> > +	buff = readl(address);
> > +	tmp = buff & ~mask;
> > +	tmp |= FIELD_PREP(mask, val);
> > +	writel(tmp, address);
> > +}
> > +
> > +static inline u32 keembay_pwm_config_min(struct keembay_pwm *priv) {
> > +	unsigned long long divd, divs;
> > +
> > +	divd = NSEC_PER_SEC;
> > +	divs = clk_get_rate(priv->clk);
> > +	do_div(divd, divs);
> 
> Given that both NSEC_PER_SEC and the return value of clk_get_rate fit into an
> unsigned long, this seems too much.
> 

Noted, will modify to use a different data type.

> > +	return (u32)divd;
> > +}
> > +
> > +static inline u16 keembay_pwm_config_duty_cycle(struct keembay_pwm
> *priv,
> > +						int duty_ns, u32 ns_min)
> > +{
> > +	unsigned long long divd;
> > +
> > +	divd = duty_ns;
> > +	do_div(divd, ns_min);
> > +	if ((u16)divd == 0)
> > +		return 0;
> > +
> > +	return (u16)divd - 1;
> 
> missing range checking.
> 

Will add this in.

> > +}
> > +
> > +static inline u16 keembay_pwm_config_period(struct keembay_pwm *priv,
> > +					    int period_ns,
> > +					    int duty_ns,
> > +					    u32 ns_min)
> > +{
> > +	unsigned long long divd;
> > +
> > +	divd = period_ns - duty_ns;
> > +	do_div(divd, ns_min);
> > +	if ((u16)divd == 0)
> > +		return 0;
> > +
> > +	return (u16)divd - 1;
> 
> I wonder if both keembay_pwm_config_duty_cycle() and
> keembay_pwm_config_period() would be easier to understand if they were not
> separate functions but unrolled into their only user.
> 
> As above there is no range checking.
> 

OK - will move this into keembay_pwm_apply, and add the range check as well.

> > +}
> > +
> > +/*
> > + *	For calculating "high time" register value:
> > + *	High time (quotient only) = duty_cycle / ns_min
> > + *
> > + *	For calculating "low time" register value:
> > + *	Low time (quotient only) = (period - duty_cycle) / ns_min
> > + *
> > + *	All values used are in nanoseconds for calculation.
> > + */
> > +static int keembay_pwm_config(struct keembay_pwm *priv, int ch,
> > +			      int duty_ns, int period_ns, int count)
> 
> this function is only called once, I wonder if it can better be folded into its only
> user.
> 

Will move this into keembay_pwm_apply.

> > +{
> > +	u32 ns_min;
> > +	u16 pwm_h_count, pwm_l_count;
> > +
> > +	/* Write to lead in */
> > +	keembay_pwm_update_bits(priv, KEEMBAY_PWM_LEAD_IN_MASK,
> > +				LEAD_IN_DEFAULT,
> > +				PWM_LEADIN0_OFFSET, ch);
> 
> What is the effect of this "lead in"?
> 

It is used to specify a low period between the trigger(enable bit set) and start of the waveform.
So the PWM output remains low for the number of clock cycles specified by this LEAD_IN value.

> > +	/* Write the number of PWM pulse repetition */
> > +	keembay_pwm_update_bits(priv, KEEMBAY_PWM_RPT_CNT_MASK,
> count,
> > +				PWM_LEADIN0_OFFSET, ch);
> 
> Is this racy? E.g. if the PWM is already running and just after you update the
> repetition count completes a period?
> 

The repetition count will only take effect once the channel is disabled and reenabled again.
So, it will not affect the currently running waveform.

> This writes to the same register as the lead in above. Can this be done in a single
> register access?
> 

Yes -- will change this to single access instead. 

> > +	/* Calculate min */
> > +	ns_min = keembay_pwm_config_min(priv);
> 
> What is "min"?
> 

This refers to the ns equivalent of 1 clock cycle.
However, since the calculation for the high/low time will be changed in v2 (based on further comments below), 
I will remove this.

> > +	/* For duty cycle */
> > +	pwm_h_count = keembay_pwm_config_duty_cycle(priv, duty_ns,
> ns_min);
> 
> this is ineffective and less exact as possible. ns_min is the result of a division and
> in keembay_pwm_config_duty_cycle you divide by it.
> 

Understood - will change the formula used to get the high time/low time to something like below instead:
value = clk_rate * state->duty_cycle;
pwm_h_count = do_div(value, NSEC_PER_SEC);


> > +	/* Write to high registers */
> > +	keembay_pwm_update_bits(priv, KEEMBAY_PWM_HIGH_MASK,
> pwm_h_count,
> > +				PWM_HIGHLOW0_OFFSET, ch);
> > +
> > +	/* For period */
> > +	pwm_l_count = keembay_pwm_config_period(priv, period_ns, duty_ns,
> > +						ns_min);
> > +
> > +	/* Write to low registers */
> > +	keembay_pwm_update_bits(priv, KEEMBAY_PWM_LOW_MASK,
> pwm_l_count,
> > +				PWM_HIGHLOW0_OFFSET, ch);
> 
> Can you please explain in a comment what's the resulting wave form for a given
> value of this register?
> 

Okay, I will add a comment in the driver explaining this.
In summary, the upper/lower 16 bits of the register represent the waveform's high/low time in number of clock cycles - 1.
e.g. for a period of 70000ns, duty cycle of 35000ns and clock rate of 500MHz:
The resulting register value would be 0x445B445B.

> Can writing h_count and l_count be combined in a single register access?
> (And if not, what happens if a period completes between the two
> updates?)
> 
> How does the hardware behave on a change here? Is the currently running
> period completed before the new values take effect?
> 

The write can be combined, I will make this change for v2. 
As for the HW behaviour, yes, the current period will
be completed before the new configuration takes effect.

> > +
> > +	return 0;
> > +}
> > +
> > +static int keembay_pwm_enable(struct keembay_pwm *priv, int ch) {
> > +	int ret;
> > +
> > +	ret = clk_enable(priv->clk);
> > +	if (ret)
> > +		return ret;
> > +
> > +	/* Enable channel */
> > +	keembay_pwm_enable_channel(priv, ch);
> > +
> > +	return 0;
> > +}
> > +
> > +static void keembay_pwm_disable(struct keembay_pwm *priv, int ch) {
> > +	/* Disable channel */
> > +	keembay_pwm_disable_channel(priv, ch);
> > +
> > +	clk_disable(priv->clk);
> > +}
> > +
> > +static int keembay_pwm_apply(struct pwm_chip *chip, struct pwm_device
> *pwm,
> > +			     const struct pwm_state *state) {
> > +	struct keembay_pwm *priv = to_keembay_pwm_dev(chip);
> > +
> > +	if (!state->enabled && pwm_is_enabled(pwm)) {
> 
> Please don't call API functions in the driver.
> 

Noted, will change this in v2.

> > +		keembay_pwm_disable(priv, pwm->hwpwm);
> 
> Is a currently running period completed here? How does the output behave on
> disable? (i.e. does it go in its inactive state?)
> 

Upon disable (the "enable" bit is cleared), the output signal is stopped/inactive.
The currently running period will not be completed.

> > +		return 0;
> > +	}
> > +
> > +	if (state->count > PWM_COUNT_MAX)
> > +		return -EINVAL;
> > +
> > +	if (state->polarity != pwm_get_polarity(pwm))
> 
> Using:
> 
> 	if (state->polarity != PWM_POLARITY_NORMAL)
> 
> seems both more robust and easier to understand.
> 

Will update this.

> > +		return -ENOSYS;
> > +
> > +	keembay_pwm_config(priv, pwm->hwpwm, state->duty_cycle,
> > +			   state->period, state->count);
> > +
> > +	if (state->enabled && !pwm_is_enabled(pwm))
> > +		return keembay_pwm_enable(priv, pwm->hwpwm);
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct pwm_ops keembay_pwm_ops = {
> > +	.owner = THIS_MODULE,
> > +	.apply = keembay_pwm_apply,
> 
> Please implement .get_state().
> 

Will add this in v2.

> > +};
> > +
> > +static int keembay_pwm_probe(struct platform_device *pdev) {
> > +	struct device *dev = &pdev->dev;
> > +	struct keembay_pwm *priv;
> > +	int ret;
> > +
> > +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> > +	if (!priv)
> > +		return -ENOMEM;
> > +
> > +	priv->clk = devm_clk_get(&pdev->dev, NULL);
> > +	if (IS_ERR(priv->clk))
> 
> Error message please. (Something like:
> 
> 		int ret = PTR_ERR(ret);
> 
> 		if (ret != -EPROBE_DEFER)
> 			dev_err(pdev->dev, "Failed to get clk: %pE\n", priv-
> >clk);
> 
> 		return ret;
> )
> 

Noted, I will add this in.

> > +		return PTR_ERR(priv->clk);
> > +
> > +	/*
> > +	 * Prepare clock here, and carry out clock enabling/disabling
> > +	 * during channel enablement/disablement.
> > +	 * The clock will not be unprepared due to shared usage with GPIO.
> 
> What has this clock to do with GPIO? If the GPIO drivers gets and enables this
> clock itself there should be no problem.
> 

The PWM outputs are actually part of the GPIO block, hence use the same clock as it.
The current behaviour is unpreparing causes the system to hang - I will cross-check regarding this.

> > +	 */
> > +	ret = clk_prepare(priv->clk);
> > +	if (ret) {
> > +		dev_err(&pdev->dev, "Failed to prepare PWM clock\n");
> 
> Please include the error code in the message (preferably using %pE as above).
> 

Noted, will update.

> > +		return ret;
> > +	}
> > +
> > +	priv->regmap = devm_platform_ioremap_resource(pdev, 0);
> > +	if (IS_ERR(priv->regmap))
> > +		return PTR_ERR(priv->regmap);
> > +
> > +	priv->chip.base = -1;
> > +	priv->chip.dev = dev;
> > +	priv->chip.ops = &keembay_pwm_ops;
> > +	priv->chip.npwm = TOTAL_PWM_CHANNELS;
> > +
> > +	ret = pwmchip_add(&priv->chip);
> > +	if (ret < 0) {
> > +		dev_err(dev, "Failed to add PWM chip: %d\n", ret);
> 
> %pE please.
> 

Will update.

> > +		return ret;
> > +	}
> > +
> > +	platform_set_drvdata(pdev, priv);
> > +
> > +	return 0;
> > +}
> > +
> > +static int keembay_pwm_remove(struct platform_device *pdev) {
> > +	struct keembay_pwm *priv = platform_get_drvdata(pdev);
> > +	unsigned int i;
> > +
> > +	for (i = 0; i < priv->chip.npwm; i++)
> > +		pwm_disable(&priv->chip.pwms[i]);
> 
> That's wrong. It's the responsibility of the consumer to disable the clock.
> 

Noted, will remove this.

> > +
> > +	pwmchip_remove(&priv->chip);
> 
> clk_unprepare missing here.
> 
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct of_device_id keembay_pwm_of_match[] = {
> > +	{ .compatible = "intel,keembay-pwm" },
> > +	{ }
> > +};
> > +MODULE_DEVICE_TABLE(of, keembay_pwm_of_match);
> > +
> > +static struct platform_driver keembay_pwm_driver = {
> > +	.probe	= keembay_pwm_probe,
> > +	.remove	= keembay_pwm_remove,
> > +	.driver	= {
> > +		.name = "pwm-keembay",
> > +		.of_match_table = keembay_pwm_of_match,
> > +	},
> > +};
> > +module_platform_driver(keembay_pwm_driver);
> > +
> > +MODULE_ALIAS("platform:keembay");
> > +MODULE_DESCRIPTION("Intel Keem Bay PWM driver");
> MODULE_LICENSE("GPL
> > +v2");
> 
> Best regards
> Uwe
> 
> --
> Pengutronix e.K.                           | Uwe Kleine-König            |
> Industrial Linux Solutions                 | https://www.pengutronix.de/ |

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

* RE: [PATCH 2/3] pwm: Add PWM driver for Intel Keem Bay
@ 2020-06-05 13:48       ` G Jaya Kumaran, Vineetha
  0 siblings, 0 replies; 19+ messages in thread
From: G Jaya Kumaran, Vineetha @ 2020-06-05 13:48 UTC (permalink / raw)
  To: u.kleine-koenig-bIcnvbaLZ9MEGnE8C9+IrQ
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Wan Mohamad, Wan Ahmad Zainie,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, linux-pwm-u79uwXL29TY76Z2rM5mHXA,
	Shevchenko, Andriy, thierry.reding-Re5JQEeQqe8AvxtiuMwx3w

Hello Uwe,

Thank you for taking the time to review this patch set.

> -----Original Message-----
> From: Uwe Kleine-König <u.kleine-koenig-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> Sent: Sunday, May 24, 2020 5:41 AM
> To: G Jaya Kumaran, Vineetha <vineetha.g.jaya.kumaran-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
> Cc: thierry.reding-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org; robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org; linux-
> pwm-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; Wan Mohamad, Wan
> Ahmad Zainie <wan.ahmad.zainie.wan.mohamad-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>; Shevchenko,
> Andriy <andriy.shevchenko-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
> Subject: Re: [PATCH 2/3] pwm: Add PWM driver for Intel Keem Bay
> 
> Hello,
> 
> On Sun, May 17, 2020 at 09:52:39PM +0800,
> vineetha.g.jaya.kumaran-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org wrote:
> > From: "Lai, Poey Seng" <poey.seng.lai-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
> >
> > Enable PWM support for the Intel Keem Bay SoC.
> >
> > Signed-off-by: Lai, Poey Seng <poey.seng.lai-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
> > Signed-off-by: Vineetha G. Jaya Kumaran
> > <vineetha.g.jaya.kumaran-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
> > ---
> >  drivers/pwm/Kconfig       |   9 ++
> >  drivers/pwm/Makefile      |   1 +
> >  drivers/pwm/pwm-keembay.c | 308
> > ++++++++++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 318 insertions(+)
> >  create mode 100644 drivers/pwm/pwm-keembay.c
> >
> > diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index
> > c13d146..5311975 100644
> > --- a/drivers/pwm/Kconfig
> > +++ b/drivers/pwm/Kconfig
> > @@ -569,4 +569,13 @@ config PWM_ZX
> >  	  To compile this driver as a module, choose M here: the module
> >  	  will be called pwm-zx.
> >
> > +config PWM_KEEMBAY
> > +	tristate "Intel Keem Bay PWM driver"
> > +	depends on ARM64
> 
> Support for COMPILE_TEST would be nice here.
> 

I will add this in v2.

> > +	help
> > +	  The platform driver for Intel Keem Bay PWM controller.
> > +
> > +	  To compile this driver as a module, choose M here: the module
> > +	  will be called pwm-keembay.
> > +
> >  endif
> > diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index
> > a59c710..0c84ff2 100644
> > --- a/drivers/pwm/Makefile
> > +++ b/drivers/pwm/Makefile
> > @@ -55,3 +55,4 @@ obj-$(CONFIG_PWM_TWL)		+= pwm-twl.o
> >  obj-$(CONFIG_PWM_TWL_LED)	+= pwm-twl-led.o
> >  obj-$(CONFIG_PWM_VT8500)	+= pwm-vt8500.o
> >  obj-$(CONFIG_PWM_ZX)		+= pwm-zx.o
> > +obj-$(CONFIG_PWM_KEEMBAY)	+= pwm-keembay.o
> > diff --git a/drivers/pwm/pwm-keembay.c b/drivers/pwm/pwm-keembay.c new
> > file mode 100644 index 0000000..39c7310
> > --- /dev/null
> > +++ b/drivers/pwm/pwm-keembay.c
> > @@ -0,0 +1,308 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Intel Keem Bay PWM driver
> > + *
> > + * Copyright (C) 2020 Intel Corporation
> > + * Authors: Lai Poey Seng <poey.seng.lai-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
> > + *          Vineetha G. Jaya Kumaran <vineetha.g.jaya.kumaran-ral2JQCrhuE@public.gmane.orgm>
> 
> Is there publically available documentation? If so, please add a link here.
> 

Will check, and add it here if any are available.

> > + */
> > +
> > +#include <linux/bitfield.h>
> > +#include <linux/clk.h>
> > +#include <linux/io.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pwm.h>
> > +#include <linux/regmap.h>
> > +
> > +#define TOTAL_PWM_CHANNELS		6
> > +#define LEAD_IN_DEFAULT			0
> > +#define PWM_COUNT_MAX			65535
> 
> please use a unique prefix for all defines in your driver. The names as they are
> are too generic.
> 

I'll update the naming in v2, to match the mask defines.

> > +
> > +#define KEEMBAY_PWM_EN_BIT		31
> > +
> > +/* Mask */
> > +#define KEEMBAY_PWM_RPT_CNT_MASK	GENMASK(15, 0)
> > +#define KEEMBAY_PWM_LEAD_IN_MASK	GENMASK(30, 16)
> > +#define KEEMBAY_PWM_HIGH_MASK		GENMASK(31, 16)
> > +#define KEEMBAY_PWM_LOW_MASK		GENMASK(15, 0)
> > +
> > +/* PWM Register offset */
> > +#define PWM_LEADIN0_OFFSET		0x00
> > +#define PWM_LEADIN1_OFFSET		0x04
> > +#define PWM_LEADIN2_OFFSET		0x08
> > +#define PWM_LEADIN3_OFFSET		0x0c
> > +#define PWM_LEADIN4_OFFSET		0x10
> > +#define PWM_LEADIN5_OFFSET		0x14
> > +
> > +#define PWM_HIGHLOW0_OFFSET		0x20
> > +#define PWM_HIGHLOW1_OFFSET		0x24
> > +#define PWM_HIGHLOW2_OFFSET		0x28
> > +#define PWM_HIGHLOW3_OFFSET		0x2c
> > +#define PWM_HIGHLOW4_OFFSET		0x30
> > +#define PWM_HIGHLOW5_OFFSET		0x34
> > +
> > +struct keembay_pwm {
> > +	struct pwm_chip chip;
> > +	struct device *dev;
> > +	struct clk *clk;
> > +	void __iomem *regmap;
> 
> I'd call this member "base" instead of "regmap" as the latter has a different
> meaning in the kernel.
> 

OK, will update the naming.

> > +};
> > +
> > +static inline struct keembay_pwm *to_keembay_pwm_dev(struct pwm_chip
> > +*chip) {
> > +	return container_of(chip, struct keembay_pwm, chip); }
> > +
> > +static inline void keembay_pwm_enable_channel(struct keembay_pwm
> > +*priv, int ch) {
> > +	u32 buff, offset;
> > +	void __iomem *address;
> > +
> > +	offset = PWM_LEADIN0_OFFSET + ch * 4;
> > +	address = priv->regmap + offset;
> > +	buff = readl(address);
> > +	buff |= BIT(KEEMBAY_PWM_EN_BIT);
> > +	writel(buff, address);
> > +}
> > +
> > +static inline void keembay_pwm_disable_channel(struct keembay_pwm
> > +*priv, int ch) {
> > +	u32 buff, offset;
> > +	void __iomem *address;
> > +
> > +	offset = PWM_LEADIN0_OFFSET + ch * 4;
> > +	address = priv->regmap + offset;
> > +	buff = readl(address);
> > +	buff &= ~BIT(KEEMBAY_PWM_EN_BIT);
> > +	writel(buff, address);
> > +}
> 
> The two functions above could make use of keembay_pwm_update_bits().
> 

Agreed, will update them to reflect this.

> > +
> > +static inline void keembay_pwm_update_bits(struct keembay_pwm *priv, u32
> mask,
> > +					   u32 val, u32 reg, int ch)
> > +{
> > +	u32 buff, offset, tmp;
> > +	void __iomem *address;
> > +
> > +	offset = reg + ch * 4;
> > +	address = priv->regmap + offset;
> > +	buff = readl(address);
> > +	tmp = buff & ~mask;
> > +	tmp |= FIELD_PREP(mask, val);
> > +	writel(tmp, address);
> > +}
> > +
> > +static inline u32 keembay_pwm_config_min(struct keembay_pwm *priv) {
> > +	unsigned long long divd, divs;
> > +
> > +	divd = NSEC_PER_SEC;
> > +	divs = clk_get_rate(priv->clk);
> > +	do_div(divd, divs);
> 
> Given that both NSEC_PER_SEC and the return value of clk_get_rate fit into an
> unsigned long, this seems too much.
> 

Noted, will modify to use a different data type.

> > +	return (u32)divd;
> > +}
> > +
> > +static inline u16 keembay_pwm_config_duty_cycle(struct keembay_pwm
> *priv,
> > +						int duty_ns, u32 ns_min)
> > +{
> > +	unsigned long long divd;
> > +
> > +	divd = duty_ns;
> > +	do_div(divd, ns_min);
> > +	if ((u16)divd == 0)
> > +		return 0;
> > +
> > +	return (u16)divd - 1;
> 
> missing range checking.
> 

Will add this in.

> > +}
> > +
> > +static inline u16 keembay_pwm_config_period(struct keembay_pwm *priv,
> > +					    int period_ns,
> > +					    int duty_ns,
> > +					    u32 ns_min)
> > +{
> > +	unsigned long long divd;
> > +
> > +	divd = period_ns - duty_ns;
> > +	do_div(divd, ns_min);
> > +	if ((u16)divd == 0)
> > +		return 0;
> > +
> > +	return (u16)divd - 1;
> 
> I wonder if both keembay_pwm_config_duty_cycle() and
> keembay_pwm_config_period() would be easier to understand if they were not
> separate functions but unrolled into their only user.
> 
> As above there is no range checking.
> 

OK - will move this into keembay_pwm_apply, and add the range check as well.

> > +}
> > +
> > +/*
> > + *	For calculating "high time" register value:
> > + *	High time (quotient only) = duty_cycle / ns_min
> > + *
> > + *	For calculating "low time" register value:
> > + *	Low time (quotient only) = (period - duty_cycle) / ns_min
> > + *
> > + *	All values used are in nanoseconds for calculation.
> > + */
> > +static int keembay_pwm_config(struct keembay_pwm *priv, int ch,
> > +			      int duty_ns, int period_ns, int count)
> 
> this function is only called once, I wonder if it can better be folded into its only
> user.
> 

Will move this into keembay_pwm_apply.

> > +{
> > +	u32 ns_min;
> > +	u16 pwm_h_count, pwm_l_count;
> > +
> > +	/* Write to lead in */
> > +	keembay_pwm_update_bits(priv, KEEMBAY_PWM_LEAD_IN_MASK,
> > +				LEAD_IN_DEFAULT,
> > +				PWM_LEADIN0_OFFSET, ch);
> 
> What is the effect of this "lead in"?
> 

It is used to specify a low period between the trigger(enable bit set) and start of the waveform.
So the PWM output remains low for the number of clock cycles specified by this LEAD_IN value.

> > +	/* Write the number of PWM pulse repetition */
> > +	keembay_pwm_update_bits(priv, KEEMBAY_PWM_RPT_CNT_MASK,
> count,
> > +				PWM_LEADIN0_OFFSET, ch);
> 
> Is this racy? E.g. if the PWM is already running and just after you update the
> repetition count completes a period?
> 

The repetition count will only take effect once the channel is disabled and reenabled again.
So, it will not affect the currently running waveform.

> This writes to the same register as the lead in above. Can this be done in a single
> register access?
> 

Yes -- will change this to single access instead. 

> > +	/* Calculate min */
> > +	ns_min = keembay_pwm_config_min(priv);
> 
> What is "min"?
> 

This refers to the ns equivalent of 1 clock cycle.
However, since the calculation for the high/low time will be changed in v2 (based on further comments below), 
I will remove this.

> > +	/* For duty cycle */
> > +	pwm_h_count = keembay_pwm_config_duty_cycle(priv, duty_ns,
> ns_min);
> 
> this is ineffective and less exact as possible. ns_min is the result of a division and
> in keembay_pwm_config_duty_cycle you divide by it.
> 

Understood - will change the formula used to get the high time/low time to something like below instead:
value = clk_rate * state->duty_cycle;
pwm_h_count = do_div(value, NSEC_PER_SEC);


> > +	/* Write to high registers */
> > +	keembay_pwm_update_bits(priv, KEEMBAY_PWM_HIGH_MASK,
> pwm_h_count,
> > +				PWM_HIGHLOW0_OFFSET, ch);
> > +
> > +	/* For period */
> > +	pwm_l_count = keembay_pwm_config_period(priv, period_ns, duty_ns,
> > +						ns_min);
> > +
> > +	/* Write to low registers */
> > +	keembay_pwm_update_bits(priv, KEEMBAY_PWM_LOW_MASK,
> pwm_l_count,
> > +				PWM_HIGHLOW0_OFFSET, ch);
> 
> Can you please explain in a comment what's the resulting wave form for a given
> value of this register?
> 

Okay, I will add a comment in the driver explaining this.
In summary, the upper/lower 16 bits of the register represent the waveform's high/low time in number of clock cycles - 1.
e.g. for a period of 70000ns, duty cycle of 35000ns and clock rate of 500MHz:
The resulting register value would be 0x445B445B.

> Can writing h_count and l_count be combined in a single register access?
> (And if not, what happens if a period completes between the two
> updates?)
> 
> How does the hardware behave on a change here? Is the currently running
> period completed before the new values take effect?
> 

The write can be combined, I will make this change for v2. 
As for the HW behaviour, yes, the current period will
be completed before the new configuration takes effect.

> > +
> > +	return 0;
> > +}
> > +
> > +static int keembay_pwm_enable(struct keembay_pwm *priv, int ch) {
> > +	int ret;
> > +
> > +	ret = clk_enable(priv->clk);
> > +	if (ret)
> > +		return ret;
> > +
> > +	/* Enable channel */
> > +	keembay_pwm_enable_channel(priv, ch);
> > +
> > +	return 0;
> > +}
> > +
> > +static void keembay_pwm_disable(struct keembay_pwm *priv, int ch) {
> > +	/* Disable channel */
> > +	keembay_pwm_disable_channel(priv, ch);
> > +
> > +	clk_disable(priv->clk);
> > +}
> > +
> > +static int keembay_pwm_apply(struct pwm_chip *chip, struct pwm_device
> *pwm,
> > +			     const struct pwm_state *state) {
> > +	struct keembay_pwm *priv = to_keembay_pwm_dev(chip);
> > +
> > +	if (!state->enabled && pwm_is_enabled(pwm)) {
> 
> Please don't call API functions in the driver.
> 

Noted, will change this in v2.

> > +		keembay_pwm_disable(priv, pwm->hwpwm);
> 
> Is a currently running period completed here? How does the output behave on
> disable? (i.e. does it go in its inactive state?)
> 

Upon disable (the "enable" bit is cleared), the output signal is stopped/inactive.
The currently running period will not be completed.

> > +		return 0;
> > +	}
> > +
> > +	if (state->count > PWM_COUNT_MAX)
> > +		return -EINVAL;
> > +
> > +	if (state->polarity != pwm_get_polarity(pwm))
> 
> Using:
> 
> 	if (state->polarity != PWM_POLARITY_NORMAL)
> 
> seems both more robust and easier to understand.
> 

Will update this.

> > +		return -ENOSYS;
> > +
> > +	keembay_pwm_config(priv, pwm->hwpwm, state->duty_cycle,
> > +			   state->period, state->count);
> > +
> > +	if (state->enabled && !pwm_is_enabled(pwm))
> > +		return keembay_pwm_enable(priv, pwm->hwpwm);
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct pwm_ops keembay_pwm_ops = {
> > +	.owner = THIS_MODULE,
> > +	.apply = keembay_pwm_apply,
> 
> Please implement .get_state().
> 

Will add this in v2.

> > +};
> > +
> > +static int keembay_pwm_probe(struct platform_device *pdev) {
> > +	struct device *dev = &pdev->dev;
> > +	struct keembay_pwm *priv;
> > +	int ret;
> > +
> > +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> > +	if (!priv)
> > +		return -ENOMEM;
> > +
> > +	priv->clk = devm_clk_get(&pdev->dev, NULL);
> > +	if (IS_ERR(priv->clk))
> 
> Error message please. (Something like:
> 
> 		int ret = PTR_ERR(ret);
> 
> 		if (ret != -EPROBE_DEFER)
> 			dev_err(pdev->dev, "Failed to get clk: %pE\n", priv-
> >clk);
> 
> 		return ret;
> )
> 

Noted, I will add this in.

> > +		return PTR_ERR(priv->clk);
> > +
> > +	/*
> > +	 * Prepare clock here, and carry out clock enabling/disabling
> > +	 * during channel enablement/disablement.
> > +	 * The clock will not be unprepared due to shared usage with GPIO.
> 
> What has this clock to do with GPIO? If the GPIO drivers gets and enables this
> clock itself there should be no problem.
> 

The PWM outputs are actually part of the GPIO block, hence use the same clock as it.
The current behaviour is unpreparing causes the system to hang - I will cross-check regarding this.

> > +	 */
> > +	ret = clk_prepare(priv->clk);
> > +	if (ret) {
> > +		dev_err(&pdev->dev, "Failed to prepare PWM clock\n");
> 
> Please include the error code in the message (preferably using %pE as above).
> 

Noted, will update.

> > +		return ret;
> > +	}
> > +
> > +	priv->regmap = devm_platform_ioremap_resource(pdev, 0);
> > +	if (IS_ERR(priv->regmap))
> > +		return PTR_ERR(priv->regmap);
> > +
> > +	priv->chip.base = -1;
> > +	priv->chip.dev = dev;
> > +	priv->chip.ops = &keembay_pwm_ops;
> > +	priv->chip.npwm = TOTAL_PWM_CHANNELS;
> > +
> > +	ret = pwmchip_add(&priv->chip);
> > +	if (ret < 0) {
> > +		dev_err(dev, "Failed to add PWM chip: %d\n", ret);
> 
> %pE please.
> 

Will update.

> > +		return ret;
> > +	}
> > +
> > +	platform_set_drvdata(pdev, priv);
> > +
> > +	return 0;
> > +}
> > +
> > +static int keembay_pwm_remove(struct platform_device *pdev) {
> > +	struct keembay_pwm *priv = platform_get_drvdata(pdev);
> > +	unsigned int i;
> > +
> > +	for (i = 0; i < priv->chip.npwm; i++)
> > +		pwm_disable(&priv->chip.pwms[i]);
> 
> That's wrong. It's the responsibility of the consumer to disable the clock.
> 

Noted, will remove this.

> > +
> > +	pwmchip_remove(&priv->chip);
> 
> clk_unprepare missing here.
> 
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct of_device_id keembay_pwm_of_match[] = {
> > +	{ .compatible = "intel,keembay-pwm" },
> > +	{ }
> > +};
> > +MODULE_DEVICE_TABLE(of, keembay_pwm_of_match);
> > +
> > +static struct platform_driver keembay_pwm_driver = {
> > +	.probe	= keembay_pwm_probe,
> > +	.remove	= keembay_pwm_remove,
> > +	.driver	= {
> > +		.name = "pwm-keembay",
> > +		.of_match_table = keembay_pwm_of_match,
> > +	},
> > +};
> > +module_platform_driver(keembay_pwm_driver);
> > +
> > +MODULE_ALIAS("platform:keembay");
> > +MODULE_DESCRIPTION("Intel Keem Bay PWM driver");
> MODULE_LICENSE("GPL
> > +v2");
> 
> Best regards
> Uwe
> 
> --
> Pengutronix e.K.                           | Uwe Kleine-König            |
> Industrial Linux Solutions                 | https://www.pengutronix.de/ |

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

* RE: [PATCH 1/3] pwm: Add count attribute in sysfs for Intel Keem Bay
  2020-05-23 21:05   ` Uwe Kleine-König
@ 2020-06-05 13:49       ` G Jaya Kumaran, Vineetha
  0 siblings, 0 replies; 19+ messages in thread
From: G Jaya Kumaran, Vineetha @ 2020-06-05 13:49 UTC (permalink / raw)
  To: u.kleine-koenig
  Cc: devicetree, Wan Mohamad, Wan Ahmad Zainie, robh+dt, linux-pwm,
	Shevchenko, Andriy, thierry.reding


> -----Original Message-----
> From: linux-pwm-owner@vger.kernel.org <linux-pwm-owner@vger.kernel.org>
> On Behalf Of Uwe Kleine-König
> Sent: Sunday, May 24, 2020 5:05 AM
> To: G Jaya Kumaran, Vineetha <vineetha.g.jaya.kumaran@intel.com>
> Cc: thierry.reding@gmail.com; robh+dt@kernel.org; linux-
> pwm@vger.kernel.org; devicetree@vger.kernel.org; Wan Mohamad, Wan
> Ahmad Zainie <wan.ahmad.zainie.wan.mohamad@intel.com>; Shevchenko,
> Andriy <andriy.shevchenko@intel.com>
> Subject: Re: [PATCH 1/3] pwm: Add count attribute in sysfs for Intel Keem Bay
> 
> On Sun, May 17, 2020 at 09:52:38PM +0800,
> vineetha.g.jaya.kumaran@intel.com wrote:
> > From: "Lai, Poey Seng" <poey.seng.lai@intel.com>
> >
> > In Keem Bay, the number of repetitions for the period/waveform can be
> > configured from userspace. This requires addition of a sysfs attribute
> > to get/set the repetition count. Setting this value to 0 will result
> > in continuous repetition of the waveform until the channel is disabled
> > or reconfigured.
> 
> There is nothing specific for Keem Bay in this patch, the only special thing here is
> that this driver is the first implementor.
> 
> IMHO all drivers that don't support count should be changed to fail if a count > 0
> is passed and introducing support in the sysfs interface should be a separate
> change.
> 
> Having said that I'm not convinced this is a good idea given that only very few
> driver can support this interface. Also this needs documentation, e.g. what is
> expected from .get_state().
> 
> You should also motivate what this functionality is used for in the commit log
> and I'd prefer to see an in-tree user (apart from sysfs which I don't count as
> such).
> 

Agreed, the wording used here is not accurate as this is not specific for Keem Bay.
Before submitting v2, I will cross-check about the use-case for this functionality, and if it 
can be implemented in some other less intrusive way to the framework (perhaps via a DT property?)

> Best regards
> Uwe
> 
> --
> Pengutronix e.K.                           | Uwe Kleine-König            |
> Industrial Linux Solutions                 | https://www.pengutronix.de/ |

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

* RE: [PATCH 1/3] pwm: Add count attribute in sysfs for Intel Keem Bay
@ 2020-06-05 13:49       ` G Jaya Kumaran, Vineetha
  0 siblings, 0 replies; 19+ messages in thread
From: G Jaya Kumaran, Vineetha @ 2020-06-05 13:49 UTC (permalink / raw)
  To: u.kleine-koenig
  Cc: devicetree, Wan Mohamad, Wan Ahmad Zainie, robh+dt, linux-pwm,
	Shevchenko, Andriy, thierry.reding


> -----Original Message-----
> From: linux-pwm-owner@vger.kernel.org <linux-pwm-owner@vger.kernel.org>
> On Behalf Of Uwe Kleine-König
> Sent: Sunday, May 24, 2020 5:05 AM
> To: G Jaya Kumaran, Vineetha <vineetha.g.jaya.kumaran@intel.com>
> Cc: thierry.reding@gmail.com; robh+dt@kernel.org; linux-
> pwm@vger.kernel.org; devicetree@vger.kernel.org; Wan Mohamad, Wan
> Ahmad Zainie <wan.ahmad.zainie.wan.mohamad@intel.com>; Shevchenko,
> Andriy <andriy.shevchenko@intel.com>
> Subject: Re: [PATCH 1/3] pwm: Add count attribute in sysfs for Intel Keem Bay
> 
> On Sun, May 17, 2020 at 09:52:38PM +0800,
> vineetha.g.jaya.kumaran@intel.com wrote:
> > From: "Lai, Poey Seng" <poey.seng.lai@intel.com>
> >
> > In Keem Bay, the number of repetitions for the period/waveform can be
> > configured from userspace. This requires addition of a sysfs attribute
> > to get/set the repetition count. Setting this value to 0 will result
> > in continuous repetition of the waveform until the channel is disabled
> > or reconfigured.
> 
> There is nothing specific for Keem Bay in this patch, the only special thing here is
> that this driver is the first implementor.
> 
> IMHO all drivers that don't support count should be changed to fail if a count > 0
> is passed and introducing support in the sysfs interface should be a separate
> change.
> 
> Having said that I'm not convinced this is a good idea given that only very few
> driver can support this interface. Also this needs documentation, e.g. what is
> expected from .get_state().
> 
> You should also motivate what this functionality is used for in the commit log
> and I'd prefer to see an in-tree user (apart from sysfs which I don't count as
> such).
> 

Agreed, the wording used here is not accurate as this is not specific for Keem Bay.
Before submitting v2, I will cross-check about the use-case for this functionality, and if it 
can be implemented in some other less intrusive way to the framework (perhaps via a DT property?)

> Best regards
> Uwe
> 
> --
> Pengutronix e.K.                           | Uwe Kleine-König            |
> Industrial Linux Solutions                 | https://www.pengutronix.de/ |

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

end of thread, other threads:[~2020-06-05 13:56 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-05-17 13:52 [PATCH 0/3] Add PWM support for Intel Keem Bay SoC vineetha.g.jaya.kumaran
2020-05-17 13:52 ` vineetha.g.jaya.kumaran-ral2JQCrhuEAvxtiuMwx3w
2020-05-17 13:52 ` [PATCH 1/3] pwm: Add count attribute in sysfs for Intel Keem Bay vineetha.g.jaya.kumaran
2020-05-17 13:52   ` vineetha.g.jaya.kumaran-ral2JQCrhuEAvxtiuMwx3w
2020-05-23 21:05   ` Uwe Kleine-König
2020-06-05 13:49     ` G Jaya Kumaran, Vineetha
2020-06-05 13:49       ` G Jaya Kumaran, Vineetha
2020-05-17 13:52 ` [PATCH 2/3] pwm: Add PWM driver " vineetha.g.jaya.kumaran
2020-05-23 21:40   ` Uwe Kleine-König
2020-06-05 13:48     ` G Jaya Kumaran, Vineetha
2020-06-05 13:48       ` G Jaya Kumaran, Vineetha
2020-05-17 13:52 ` [PATCH 3/3] dt-bindings: pwm: keembay: Add bindings for Intel Keem Bay PWM vineetha.g.jaya.kumaran
2020-05-18 14:18   ` Rob Herring
2020-05-18 14:18     ` Rob Herring
2020-05-20 10:49     ` G Jaya Kumaran, Vineetha
2020-05-20 10:49       ` G Jaya Kumaran, Vineetha
2020-05-18 14:21   ` Rob Herring
2020-05-20 10:52     ` G Jaya Kumaran, Vineetha
2020-05-20 10:52       ` G Jaya Kumaran, Vineetha

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.