linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/4] rtc: m41t80: improve SQW support
@ 2017-04-25 14:45 Gary Bisson
  2017-04-25 14:45 ` [PATCH 1/4] rtc: m41t80: fix SQWE override when setting an alarm Gary Bisson
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Gary Bisson @ 2017-04-25 14:45 UTC (permalink / raw)
  To: rtc-linux; +Cc: linux-kernel, alexandre.belloni, a.zummo, Gary Bisson

Hi,

This series aims at improving the square-wave support for some of the
devices supported by the m41t80 driver.

The first two patches are fixes that prevent the driver from overriding
the SQW configuration (divider & enable bit).

Then the last two remove the old sysfs entry and replace it with a clock
provider approach.

It has been tested on a Boundary Devices Nitrogen6_MAX which features a
MicroCrystal RV4162 RTC. The SQW (named clkout in RV4162 datasheet) can be
probed on TP70 on the platform.

Let me know if you have any question/suggestion.

Regards,
Gary

Gary Bisson (4):
  rtc: m41t80: fix SQWE override when setting an alarm
  rtc: m41t80: fix SQW dividers override when setting a date
  rtc: m41t80: remove sqw sysfs entry
  rtc: m41t80: add clock provider support

 drivers/rtc/rtc-m41t80.c | 251 +++++++++++++++++++++++++++++++++--------------
 1 file changed, 175 insertions(+), 76 deletions(-)

-- 
2.11.0

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

* [PATCH 1/4] rtc: m41t80: fix SQWE override when setting an alarm
  2017-04-25 14:45 [PATCH 0/4] rtc: m41t80: improve SQW support Gary Bisson
@ 2017-04-25 14:45 ` Gary Bisson
  2017-04-25 14:45 ` [PATCH 2/4] rtc: m41t80: fix SQW dividers override when setting a date Gary Bisson
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Gary Bisson @ 2017-04-25 14:45 UTC (permalink / raw)
  To: rtc-linux; +Cc: linux-kernel, alexandre.belloni, a.zummo, Gary Bisson

Currently setting an alarm clears the SQWE bit which means that the
clock output is disabled no matter its previous state.

Signed-off-by: Gary Bisson <gary.bisson@boundarydevices.com>
---
 drivers/rtc/rtc-m41t80.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c
index 58698d21c2c3..93684ab293f2 100644
--- a/drivers/rtc/rtc-m41t80.c
+++ b/drivers/rtc/rtc-m41t80.c
@@ -273,6 +273,9 @@ static int m41t80_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 		return err;
 	}
 
+	/* Keep SQWE bit value */
+	alarmvals[0] |= (ret & M41T80_ALMON_SQWE);
+
 	ret = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS);
 	if (ret < 0)
 		return ret;
-- 
2.11.0

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

* [PATCH 2/4] rtc: m41t80: fix SQW dividers override when setting a date
  2017-04-25 14:45 [PATCH 0/4] rtc: m41t80: improve SQW support Gary Bisson
  2017-04-25 14:45 ` [PATCH 1/4] rtc: m41t80: fix SQWE override when setting an alarm Gary Bisson
@ 2017-04-25 14:45 ` Gary Bisson
  2017-04-25 14:45 ` [PATCH 3/4] rtc: m41t80: remove sqw sysfs entry Gary Bisson
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Gary Bisson @ 2017-04-25 14:45 UTC (permalink / raw)
  To: rtc-linux; +Cc: linux-kernel, alexandre.belloni, a.zummo, Gary Bisson

This patch is only relevant for RTC with the SQ_ALT feature which
means the clock output frequency divider is stored in the weekday
register.

Current implementation discards the previous dividers value and clear
them as soon as the time is set.

Signed-off-by: Gary Bisson <gary.bisson@boundarydevices.com>
---
 drivers/rtc/rtc-m41t80.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c
index 93684ab293f2..0c142653c309 100644
--- a/drivers/rtc/rtc-m41t80.c
+++ b/drivers/rtc/rtc-m41t80.c
@@ -168,6 +168,7 @@ static int m41t80_get_datetime(struct i2c_client *client,
 /* Sets the given date and time to the real time clock. */
 static int m41t80_set_datetime(struct i2c_client *client, struct rtc_time *tm)
 {
+	struct m41t80_data *clientdata = i2c_get_clientdata(client);
 	unsigned char buf[8];
 	int err, flags;
 
@@ -183,6 +184,17 @@ static int m41t80_set_datetime(struct i2c_client *client, struct rtc_time *tm)
 	buf[M41T80_REG_YEAR] = bin2bcd(tm->tm_year - 100);
 	buf[M41T80_REG_WDAY] = tm->tm_wday;
 
+	/* If the square wave output is controlled in the weekday register */
+	if (clientdata->features & M41T80_FEATURE_SQ_ALT) {
+		int val;
+
+		val = i2c_smbus_read_byte_data(client, M41T80_REG_WDAY);
+		if (val < 0)
+			return val;
+
+		buf[M41T80_REG_WDAY] |= (val & 0xf0);
+	}
+
 	err = i2c_smbus_write_i2c_block_data(client, M41T80_REG_SSEC,
 					     sizeof(buf), buf);
 	if (err < 0) {
-- 
2.11.0

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

* [PATCH 3/4] rtc: m41t80: remove sqw sysfs entry
  2017-04-25 14:45 [PATCH 0/4] rtc: m41t80: improve SQW support Gary Bisson
  2017-04-25 14:45 ` [PATCH 1/4] rtc: m41t80: fix SQWE override when setting an alarm Gary Bisson
  2017-04-25 14:45 ` [PATCH 2/4] rtc: m41t80: fix SQW dividers override when setting a date Gary Bisson
@ 2017-04-25 14:45 ` Gary Bisson
  2017-04-25 14:45 ` [PATCH 4/4] rtc: m41t80: add clock provider support Gary Bisson
  2017-05-31  1:40 ` [PATCH 0/4] rtc: m41t80: improve SQW support Alexandre Belloni
  4 siblings, 0 replies; 6+ messages in thread
From: Gary Bisson @ 2017-04-25 14:45 UTC (permalink / raw)
  To: rtc-linux; +Cc: linux-kernel, alexandre.belloni, a.zummo, Gary Bisson

In order to use the proper clock framework to control this feature.

Signed-off-by: Gary Bisson <gary.bisson@boundarydevices.com>
---
 drivers/rtc/rtc-m41t80.c | 88 ------------------------------------------------
 1 file changed, 88 deletions(-)

diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c
index 0c142653c309..2bdf494a9bd4 100644
--- a/drivers/rtc/rtc-m41t80.c
+++ b/drivers/rtc/rtc-m41t80.c
@@ -387,96 +387,8 @@ static ssize_t flags_show(struct device *dev,
 }
 static DEVICE_ATTR_RO(flags);
 
-static ssize_t sqwfreq_show(struct device *dev,
-			    struct device_attribute *attr, char *buf)
-{
-	struct i2c_client *client = to_i2c_client(dev);
-	struct m41t80_data *clientdata = i2c_get_clientdata(client);
-	int val, reg_sqw;
-
-	if (!(clientdata->features & M41T80_FEATURE_SQ))
-		return -EINVAL;
-
-	reg_sqw = M41T80_REG_SQW;
-	if (clientdata->features & M41T80_FEATURE_SQ_ALT)
-		reg_sqw = M41T80_REG_WDAY;
-	val = i2c_smbus_read_byte_data(client, reg_sqw);
-	if (val < 0)
-		return val;
-	val = (val >> 4) & 0xf;
-	switch (val) {
-	case 0:
-		break;
-	case 1:
-		val = 32768;
-		break;
-	default:
-		val = 32768 >> val;
-	}
-	return sprintf(buf, "%d\n", val);
-}
-
-static ssize_t sqwfreq_store(struct device *dev,
-			     struct device_attribute *attr,
-			     const char *buf, size_t count)
-{
-	struct i2c_client *client = to_i2c_client(dev);
-	struct m41t80_data *clientdata = i2c_get_clientdata(client);
-	int almon, sqw, reg_sqw, rc;
-	unsigned long val;
-
-	rc = kstrtoul(buf, 0, &val);
-	if (rc < 0)
-		return rc;
-
-	if (!(clientdata->features & M41T80_FEATURE_SQ))
-		return -EINVAL;
-
-	if (val) {
-		if (!is_power_of_2(val))
-			return -EINVAL;
-		val = ilog2(val);
-		if (val == 15)
-			val = 1;
-		else if (val < 14)
-			val = 15 - val;
-		else
-			return -EINVAL;
-	}
-	/* disable SQW, set SQW frequency & re-enable */
-	almon = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON);
-	if (almon < 0)
-		return almon;
-	reg_sqw = M41T80_REG_SQW;
-	if (clientdata->features & M41T80_FEATURE_SQ_ALT)
-		reg_sqw = M41T80_REG_WDAY;
-	sqw = i2c_smbus_read_byte_data(client, reg_sqw);
-	if (sqw < 0)
-		return sqw;
-	sqw = (sqw & 0x0f) | (val << 4);
-
-	rc = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON,
-				       almon & ~M41T80_ALMON_SQWE);
-	if (rc < 0)
-		return rc;
-
-	if (val) {
-		rc = i2c_smbus_write_byte_data(client, reg_sqw, sqw);
-		if (rc < 0)
-			return rc;
-
-		rc = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON,
-					       almon | M41T80_ALMON_SQWE);
-		if (rc < 0)
-			return rc;
-	}
-	return count;
-}
-static DEVICE_ATTR_RW(sqwfreq);
-
 static struct attribute *attrs[] = {
 	&dev_attr_flags.attr,
-	&dev_attr_sqwfreq.attr,
 	NULL,
 };
 
-- 
2.11.0

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

* [PATCH 4/4] rtc: m41t80: add clock provider support
  2017-04-25 14:45 [PATCH 0/4] rtc: m41t80: improve SQW support Gary Bisson
                   ` (2 preceding siblings ...)
  2017-04-25 14:45 ` [PATCH 3/4] rtc: m41t80: remove sqw sysfs entry Gary Bisson
@ 2017-04-25 14:45 ` Gary Bisson
  2017-05-31  1:40 ` [PATCH 0/4] rtc: m41t80: improve SQW support Alexandre Belloni
  4 siblings, 0 replies; 6+ messages in thread
From: Gary Bisson @ 2017-04-25 14:45 UTC (permalink / raw)
  To: rtc-linux; +Cc: linux-kernel, alexandre.belloni, a.zummo, Gary Bisson

Some devices supported by the m41t80 driver have a programmable
square-wave output signal (see M41T80_FEATURE_SQ).

This enables to use this feature as a clock provider of common
clock framework.

Signed-off-by: Gary Bisson <gary.bisson@boundarydevices.com>
---
 drivers/rtc/rtc-m41t80.c | 172 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 172 insertions(+)

diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c
index 2bdf494a9bd4..ea3d1efe9192 100644
--- a/drivers/rtc/rtc-m41t80.c
+++ b/drivers/rtc/rtc-m41t80.c
@@ -16,6 +16,7 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/bcd.h>
+#include <linux/clk-provider.h>
 #include <linux/i2c.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
@@ -52,6 +53,8 @@
 #define M41T80_ALARM_REG_SIZE	\
 	(M41T80_REG_ALARM_SEC + 1 - M41T80_REG_ALARM_MON)
 
+#define M41T80_SQW_MAX_FREQ	32768
+
 #define M41T80_SEC_ST		BIT(7)	/* ST: Stop Bit */
 #define M41T80_ALMON_AFE	BIT(7)	/* AFE: AF Enable Bit */
 #define M41T80_ALMON_SQWE	BIT(6)	/* SQWE: SQW Enable Bit */
@@ -88,7 +91,11 @@ MODULE_DEVICE_TABLE(i2c, m41t80_id);
 
 struct m41t80_data {
 	u8 features;
+	struct i2c_client *client;
 	struct rtc_device *rtc;
+#ifdef CONFIG_COMMON_CLK
+	struct clk_hw sqw;
+#endif
 };
 
 static irqreturn_t m41t80_handle_irq(int irq, void *dev_id)
@@ -396,6 +403,166 @@ static struct attribute_group attr_group = {
 	.attrs = attrs,
 };
 
+#ifdef CONFIG_COMMON_CLK
+#define sqw_to_m41t80_data(_hw) container_of(_hw, struct m41t80_data, sqw)
+
+static unsigned long m41t80_sqw_recalc_rate(struct clk_hw *hw,
+					    unsigned long parent_rate)
+{
+	struct m41t80_data *m41t80 = sqw_to_m41t80_data(hw);
+	struct i2c_client *client = m41t80->client;
+	int reg_sqw = (m41t80->features & M41T80_FEATURE_SQ_ALT) ?
+		M41T80_REG_WDAY : M41T80_REG_SQW;
+	int ret = i2c_smbus_read_byte_data(client, reg_sqw);
+	unsigned long val = M41T80_SQW_MAX_FREQ;
+
+	if (ret < 0)
+		return 0;
+
+	ret >>= 4;
+	if (ret == 0)
+		val = 0;
+	else if (ret > 1)
+		val = val / (1 << ret);
+
+	return val;
+}
+
+static long m41t80_sqw_round_rate(struct clk_hw *hw, unsigned long rate,
+				  unsigned long *prate)
+{
+	int i, freq = M41T80_SQW_MAX_FREQ;
+
+	if (freq <= rate)
+		return freq;
+
+	for (i = 2; i <= ilog2(M41T80_SQW_MAX_FREQ); i++) {
+		freq /= 1 << i;
+		if (freq <= rate)
+			return freq;
+	}
+
+	return 0;
+}
+
+static int m41t80_sqw_set_rate(struct clk_hw *hw, unsigned long rate,
+			       unsigned long parent_rate)
+{
+	struct m41t80_data *m41t80 = sqw_to_m41t80_data(hw);
+	struct i2c_client *client = m41t80->client;
+	int reg_sqw = (m41t80->features & M41T80_FEATURE_SQ_ALT) ?
+		M41T80_REG_WDAY : M41T80_REG_SQW;
+	int reg, ret, val = 0;
+
+	if (rate) {
+		if (!is_power_of_2(rate))
+			return -EINVAL;
+		val = ilog2(rate);
+		if (val == ilog2(M41T80_SQW_MAX_FREQ))
+			val = 1;
+		else if (val < (ilog2(M41T80_SQW_MAX_FREQ) - 1))
+			val = ilog2(M41T80_SQW_MAX_FREQ) - val;
+		else
+			return -EINVAL;
+	}
+
+	reg = i2c_smbus_read_byte_data(client, reg_sqw);
+	if (reg < 0)
+		return reg;
+
+	reg = (reg & 0x0f) | (val << 4);
+
+	ret = i2c_smbus_write_byte_data(client, reg_sqw, reg);
+	if (ret < 0)
+		return ret;
+
+	return -EINVAL;
+}
+
+static int m41t80_sqw_control(struct clk_hw *hw, bool enable)
+{
+	struct m41t80_data *m41t80 = sqw_to_m41t80_data(hw);
+	struct i2c_client *client = m41t80->client;
+	int ret = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON);
+
+	if (ret < 0)
+		return ret;
+
+	if (enable)
+		ret |= M41T80_ALMON_SQWE;
+	else
+		ret &= ~M41T80_ALMON_SQWE;
+
+	return i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, ret);
+}
+
+static int m41t80_sqw_prepare(struct clk_hw *hw)
+{
+	return m41t80_sqw_control(hw, 1);
+}
+
+static void m41t80_sqw_unprepare(struct clk_hw *hw)
+{
+	m41t80_sqw_control(hw, 0);
+}
+
+static int m41t80_sqw_is_prepared(struct clk_hw *hw)
+{
+	struct m41t80_data *m41t80 = sqw_to_m41t80_data(hw);
+	struct i2c_client *client = m41t80->client;
+	int ret = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON);
+
+	if (ret < 0)
+		return ret;
+
+	return !!(ret & M41T80_ALMON_SQWE);
+}
+
+static const struct clk_ops m41t80_sqw_ops = {
+	.prepare = m41t80_sqw_prepare,
+	.unprepare = m41t80_sqw_unprepare,
+	.is_prepared = m41t80_sqw_is_prepared,
+	.recalc_rate = m41t80_sqw_recalc_rate,
+	.round_rate = m41t80_sqw_round_rate,
+	.set_rate = m41t80_sqw_set_rate,
+};
+
+static struct clk *m41t80_sqw_register_clk(struct m41t80_data *m41t80)
+{
+	struct i2c_client *client = m41t80->client;
+	struct device_node *node = client->dev.of_node;
+	struct clk *clk;
+	struct clk_init_data init;
+	int ret;
+
+	/* First disable the clock */
+	ret = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON);
+	if (ret < 0)
+		return ERR_PTR(ret);
+	ret = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON,
+					ret & ~(M41T80_ALMON_SQWE));
+	if (ret < 0)
+		return ERR_PTR(ret);
+
+	init.name = "m41t80-sqw";
+	init.ops = &m41t80_sqw_ops;
+	init.flags = 0;
+	init.parent_names = NULL;
+	init.num_parents = 0;
+	m41t80->sqw.init = &init;
+
+	/* optional override of the clockname */
+	of_property_read_string(node, "clock-output-names", &init.name);
+
+	/* register the clock */
+	clk = clk_register(&client->dev, &m41t80->sqw);
+	if (!IS_ERR(clk))
+		of_clk_add_provider(node, of_clk_src_simple_get, clk);
+
+	return clk;
+}
+#endif
+
 #ifdef CONFIG_RTC_DRV_M41T80_WDT
 /*
  *****************************************************************************
@@ -713,6 +880,7 @@ static int m41t80_probe(struct i2c_client *client,
 	if (!m41t80_data)
 		return -ENOMEM;
 
+	m41t80_data->client = client;
 	m41t80_data->features = id->driver_data;
 	i2c_set_clientdata(client, m41t80_data);
 
@@ -801,6 +969,10 @@ static int m41t80_probe(struct i2c_client *client,
 		}
 	}
 #endif
+#ifdef CONFIG_COMMON_CLK
+	if (m41t80_data->features & M41T80_FEATURE_SQ)
+		m41t80_sqw_register_clk(m41t80_data);
+#endif
 	return 0;
 }
 
-- 
2.11.0

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

* Re: [PATCH 0/4] rtc: m41t80: improve SQW support
  2017-04-25 14:45 [PATCH 0/4] rtc: m41t80: improve SQW support Gary Bisson
                   ` (3 preceding siblings ...)
  2017-04-25 14:45 ` [PATCH 4/4] rtc: m41t80: add clock provider support Gary Bisson
@ 2017-05-31  1:40 ` Alexandre Belloni
  4 siblings, 0 replies; 6+ messages in thread
From: Alexandre Belloni @ 2017-05-31  1:40 UTC (permalink / raw)
  To: Gary Bisson; +Cc: rtc-linux, linux-kernel, a.zummo, linux-rtc

On 25/04/2017 at 16:45:13 +0200, Gary Bisson wrote:
> Hi,
> 
> This series aims at improving the square-wave support for some of the
> devices supported by the m41t80 driver.
> 
> The first two patches are fixes that prevent the driver from overriding
> the SQW configuration (divider & enable bit).
> 
> Then the last two remove the old sysfs entry and replace it with a clock
> provider approach.
> 
> It has been tested on a Boundary Devices Nitrogen6_MAX which features a
> MicroCrystal RV4162 RTC. The SQW (named clkout in RV4162 datasheet) can be
> probed on TP70 on the platform.
> 
> Let me know if you have any question/suggestion.
> 
> Regards,
> Gary
> 
> Gary Bisson (4):
>   rtc: m41t80: fix SQWE override when setting an alarm
>   rtc: m41t80: fix SQW dividers override when setting a date
>   rtc: m41t80: remove sqw sysfs entry
>   rtc: m41t80: add clock provider support
> 
>  drivers/rtc/rtc-m41t80.c | 251 +++++++++++++++++++++++++++++++++--------------
>  1 file changed, 175 insertions(+), 76 deletions(-)
> 

All applied now, I had to rebase the last patch on top of v4.12-rc1

Thanks!

-- 
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

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

end of thread, other threads:[~2017-05-31  1:40 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-04-25 14:45 [PATCH 0/4] rtc: m41t80: improve SQW support Gary Bisson
2017-04-25 14:45 ` [PATCH 1/4] rtc: m41t80: fix SQWE override when setting an alarm Gary Bisson
2017-04-25 14:45 ` [PATCH 2/4] rtc: m41t80: fix SQW dividers override when setting a date Gary Bisson
2017-04-25 14:45 ` [PATCH 3/4] rtc: m41t80: remove sqw sysfs entry Gary Bisson
2017-04-25 14:45 ` [PATCH 4/4] rtc: m41t80: add clock provider support Gary Bisson
2017-05-31  1:40 ` [PATCH 0/4] rtc: m41t80: improve SQW support Alexandre Belloni

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