linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 00/14] rtc: pcf2127: add PCF2131 driver
@ 2022-12-15 15:02 Hugo Villeneuve
  2022-12-15 15:02 ` [PATCH v3 01/14] rtc: pcf2127: add variant-specific configuration structure Hugo Villeneuve
                   ` (14 more replies)
  0 siblings, 15 replies; 72+ messages in thread
From: Hugo Villeneuve @ 2022-12-15 15:02 UTC (permalink / raw)
  To: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt
  Cc: linux-rtc, devicetree, linux-kernel, hugo, Hugo Villeneuve

From: Hugo Villeneuve <hvilleneuve@dimonoff.com>

Hello,
this patch series adds the driver for the PCF2131 real-time clock.

This RTC is very similar in functionality to the PCF2127/29 with the
following differences:
  -supports two new control registers at offsets 4 and 5
  -supports a new reset register
  -supports 4 tamper detection functions instead of 1
  -has no nvmem (like the PCF2129)
  -has two output interrupt pins instead of one
  -has 1/100th seconds capabilities (not supported in this driver)
  -pcf2127 has watchdog clock sources: 1/60,   1, 64 and 4096Hz
   pcf2131 has watchdog clock sources: 1/64, 1/4,  4 and   64Hz
  -watchdog value register cannot be read after being set

Most of the register addresses are very different, although they still
follow the same layout. For example, the time/date and tamper registers
have a different base address, but the offsets are all the same.
Consequently, the source code of the PCF2127 driver can be easily adapted
to support this new device.

Patches 1 to 6 modify the existing pcf2127 driver to make it more generic
and able to support multiple variants, like the PCF2131. This is done
mostly by using offsets instead of absolute hardcoded register addresses.

Patch 7 add actual support for the PCF2131.

Patch 8 configures all interrupt sources to go through the INT A pin.

Patch 9 changes the PWRMNG bits to be the same with the PCF2131 as they
      are with the PCF2127/29 (different default values).

Patch 10 allow to confirm PCF2131 device presence by reading the reset
      register fixed pattern.

Patch 11 adapt the time/date registers write sequence for PCF2131 (STOP and
      CPR bits).

Patch 12 add support for generic watchdog timing configuration.

Patch 13 add a new flag to identify if device has read support for reading
      watchdog register value.
      Since the watchdog value register cannot be read on the PCF2131 after
      being set, it seems that we cannot detect if watchdog timer was
      started by bootloader. I am not sure what is the best way to handle
      this situation, suggestions are welcomed.

Patch 14 add the dt-bindings for the PCF2131.

I have tested the driver using a PCF2131-ARD evaluation board connected to
an NXP imx8mp evaluation board:
  - Time get/set ok;
  - Alarms get/set ok
  - Timestamp 1 to 4 ok
  - IRQ alarm ok
  - Watchdog ok
  - Also tested successfully with "RTC Driver Test Example" from
    Documentation/rtc.txt

I have also tested the driver on a custom PCF2129 adapter board connected to a
beaglebone black.

Thank you.

Link: [v1] https://patchwork.ozlabs.org/project/rtc-linux/patch/20220125200009.900660-2-hugo@hugovil.com/
Link: [v2] https://patchwork.ozlabs.org/project/rtc-linux/list/?series=285734

Changes for V3:
- Rebased for kernel v6.1

Changes for V2:
- In general, fix and improvements after I have tested on real hardware
- Fix alarm interrupt A/B mask setting for PCF2131:
  PCF2131_BIT_INT_AIE must be cleared, not set, to enable interrupt.
- Remove low_reg validation: only check if TS interrupt flag is
  defined, as low_reg is defined at address 0 for PCF2127/29.
- Change PWRMNG value for PCF2131: default is different than PCF2127/29.
- Adapt time/date registers write sequence for PCF2131 (STOP and CPR bits).
- Map all interrupt sources to INT A pin
- Read and validate PCF2131 device presence from RESET register
- Adapt watchdog configuration for PCF2131

Hugo Villeneuve (14):
  rtc: pcf2127: add variant-specific configuration structure
  rtc: pcf2127: adapt for time/date registers at any offset
  rtc: pcf2127: adapt for alarm registers at any offset
  rtc: pcf2127: adapt for WD registers at any offset
  rtc: pcf2127: adapt for CLKOUT register at any offset
  rtc: pcf2127: add support for multiple TS functions
  rtc: pcf2127: add support for PCF2131 RTC
  rtc: pcf2127: add support for PCF2131 interrupts on output INT_A
  rtc: pcf2127: set PWRMNG value for PCF2131
  rtc: pcf2127: read and validate PCF2131 device signature
  rtc: pcf2127: adapt time/date registers write sequence for PCF2131
  rtc: pcf2127: support generic watchdog timing configuration
  rtc: pcf2127: add flag for watchdog register value read support
  dt-bindings: rtc: pcf2127: add PCF2131

 .../devicetree/bindings/rtc/nxp,pcf2127.yaml  |   4 +-
 drivers/rtc/Kconfig                           |   4 +-
 drivers/rtc/rtc-pcf2127.c                     | 939 ++++++++++++++----
 3 files changed, 752 insertions(+), 195 deletions(-)

-- 
2.30.2


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

* [PATCH v3 01/14] rtc: pcf2127: add variant-specific configuration structure
  2022-12-15 15:02 [PATCH v3 00/14] rtc: pcf2127: add PCF2131 driver Hugo Villeneuve
@ 2022-12-15 15:02 ` Hugo Villeneuve
  2022-12-19  9:05   ` Bruno Thomsen
  2023-01-07 16:52   ` Bruno Thomsen
  2022-12-15 15:02 ` [PATCH v3 02/14] rtc: pcf2127: adapt for time/date registers at any offset Hugo Villeneuve
                   ` (13 subsequent siblings)
  14 siblings, 2 replies; 72+ messages in thread
From: Hugo Villeneuve @ 2022-12-15 15:02 UTC (permalink / raw)
  To: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt
  Cc: linux-rtc, devicetree, linux-kernel, hugo, Hugo Villeneuve

From: Hugo Villeneuve <hvilleneuve@dimonoff.com>

Create variant-specific configuration structures to simplify the
implementation of new variants into this driver. It will also avoid
to have too many tests for a specific variant, or a list of variants
for new devices, inside the code itself.

Add configuration options for the support of the NVMEM, bit CD0 in
register WD_CTL as well as the maximum number of registers for each
variant, instead of hardcoding the variant (PCF2127) inside the
i2c_device_id and spi_device_id structures.

Also specify a different maximum number of registers (max_register)
for the PCF2129.

Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
---
 drivers/rtc/rtc-pcf2127.c | 95 +++++++++++++++++++++++++++++++--------
 1 file changed, 76 insertions(+), 19 deletions(-)

diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
index 87f4fc9df68b..b9a5d47a439f 100644
--- a/drivers/rtc/rtc-pcf2127.c
+++ b/drivers/rtc/rtc-pcf2127.c
@@ -21,6 +21,7 @@
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_irq.h>
+#include <linux/of_device.h>
 #include <linux/regmap.h>
 #include <linux/watchdog.h>
 
@@ -101,10 +102,17 @@
 		PCF2127_BIT_CTRL2_WDTF | \
 		PCF2127_BIT_CTRL2_TSF2)
 
+struct pcf21xx_config {
+	int max_register;
+	unsigned int has_nvmem:1;
+	unsigned int has_bit_wd_ctl_cd0:1;
+};
+
 struct pcf2127 {
 	struct rtc_device *rtc;
 	struct watchdog_device wdd;
 	struct regmap *regmap;
+	const struct pcf21xx_config *cfg;
 	time64_t ts;
 	bool ts_valid;
 	bool irq_enabled;
@@ -631,8 +639,27 @@ static const struct attribute_group pcf2127_attr_group = {
 	.attrs	= pcf2127_attrs,
 };
 
+enum pcf21xx_type {
+	PCF2127,
+	PCF2129,
+	PCF21XX_LAST_ID
+};
+
+static struct pcf21xx_config pcf21xx_cfg[] = {
+	[PCF2127] = {
+		.max_register = 0x1d,
+		.has_nvmem = 1,
+		.has_bit_wd_ctl_cd0 = 1,
+	},
+	[PCF2129] = {
+		.max_register = 0x19,
+		.has_nvmem = 0,
+		.has_bit_wd_ctl_cd0 = 0,
+	},
+};
+
 static int pcf2127_probe(struct device *dev, struct regmap *regmap,
-			 int alarm_irq, const char *name, bool is_pcf2127)
+			 int alarm_irq, const char *name, const struct pcf21xx_config *config)
 {
 	struct pcf2127 *pcf2127;
 	int ret = 0;
@@ -645,6 +672,7 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
 		return -ENOMEM;
 
 	pcf2127->regmap = regmap;
+	pcf2127->cfg = config;
 
 	dev_set_drvdata(dev, pcf2127);
 
@@ -688,7 +716,7 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
 		set_bit(RTC_FEATURE_ALARM, pcf2127->rtc->features);
 	}
 
-	if (is_pcf2127) {
+	if (pcf2127->cfg->has_nvmem) {
 		struct nvmem_config nvmem_cfg = {
 			.priv = pcf2127,
 			.reg_read = pcf2127_nvmem_read,
@@ -734,7 +762,7 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
 				 PCF2127_BIT_WD_CTL_TF1 |
 				 PCF2127_BIT_WD_CTL_TF0,
 				 PCF2127_BIT_WD_CTL_CD1 |
-				 (is_pcf2127 ? PCF2127_BIT_WD_CTL_CD0 : 0) |
+				 (pcf2127->cfg->has_bit_wd_ctl_cd0 ? PCF2127_BIT_WD_CTL_CD0 : 0) |
 				 PCF2127_BIT_WD_CTL_TF1);
 	if (ret) {
 		dev_err(dev, "%s: watchdog config (wd_ctl) failed\n", __func__);
@@ -799,9 +827,9 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
 
 #ifdef CONFIG_OF
 static const struct of_device_id pcf2127_of_match[] = {
-	{ .compatible = "nxp,pcf2127" },
-	{ .compatible = "nxp,pcf2129" },
-	{ .compatible = "nxp,pca2129" },
+	{ .compatible = "nxp,pcf2127", .data = &pcf21xx_cfg[PCF2127] },
+	{ .compatible = "nxp,pcf2129", .data = &pcf21xx_cfg[PCF2129] },
+	{ .compatible = "nxp,pca2129", .data = &pcf21xx_cfg[PCF2129] },
 	{}
 };
 MODULE_DEVICE_TABLE(of, pcf2127_of_match);
@@ -886,26 +914,40 @@ static const struct regmap_bus pcf2127_i2c_regmap = {
 static struct i2c_driver pcf2127_i2c_driver;
 
 static const struct i2c_device_id pcf2127_i2c_id[] = {
-	{ "pcf2127", 1 },
-	{ "pcf2129", 0 },
-	{ "pca2129", 0 },
+	{ "pcf2127", PCF2127 },
+	{ "pcf2129", PCF2129 },
+	{ "pca2129", PCF2129 },
 	{ }
 };
 MODULE_DEVICE_TABLE(i2c, pcf2127_i2c_id);
 
 static int pcf2127_i2c_probe(struct i2c_client *client)
 {
-	const struct i2c_device_id *id = i2c_match_id(pcf2127_i2c_id, client);
 	struct regmap *regmap;
-	static const struct regmap_config config = {
+	static struct regmap_config config = {
 		.reg_bits = 8,
 		.val_bits = 8,
-		.max_register = 0x1d,
 	};
+	const struct pcf21xx_config *variant;
 
 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
 		return -ENODEV;
 
+	if (client->dev.of_node) {
+		variant = of_device_get_match_data(&client->dev);
+		if (!variant)
+			return -ENODEV;
+	} else {
+		enum pcf21xx_type type =
+			i2c_match_id(pcf2127_i2c_id, client)->driver_data;
+
+		if (type >= PCF21XX_LAST_ID)
+			return -ENODEV;
+		variant = &pcf21xx_cfg[type];
+	}
+
+	config.max_register = variant->max_register,
+
 	regmap = devm_regmap_init(&client->dev, &pcf2127_i2c_regmap,
 					&client->dev, &config);
 	if (IS_ERR(regmap)) {
@@ -915,7 +957,7 @@ static int pcf2127_i2c_probe(struct i2c_client *client)
 	}
 
 	return pcf2127_probe(&client->dev, regmap, client->irq,
-			     pcf2127_i2c_driver.driver.name, id->driver_data);
+			     pcf2127_i2c_driver.driver.name, variant);
 }
 
 static struct i2c_driver pcf2127_i2c_driver = {
@@ -953,17 +995,32 @@ static void pcf2127_i2c_unregister_driver(void)
 #if IS_ENABLED(CONFIG_SPI_MASTER)
 
 static struct spi_driver pcf2127_spi_driver;
+static const struct spi_device_id pcf2127_spi_id[];
 
 static int pcf2127_spi_probe(struct spi_device *spi)
 {
-	static const struct regmap_config config = {
+	static struct regmap_config config = {
 		.reg_bits = 8,
 		.val_bits = 8,
 		.read_flag_mask = 0xa0,
 		.write_flag_mask = 0x20,
-		.max_register = 0x1d,
 	};
 	struct regmap *regmap;
+	const struct pcf21xx_config *variant;
+
+	if (spi->dev.of_node) {
+		variant = of_device_get_match_data(&spi->dev);
+		if (!variant)
+			return -ENODEV;
+	} else {
+		enum pcf21xx_type type = spi_get_device_id(spi)->driver_data;
+
+		if (type >= PCF21XX_LAST_ID)
+			return -ENODEV;
+		variant = &pcf21xx_cfg[type];
+	}
+
+	config.max_register = variant->max_register,
 
 	regmap = devm_regmap_init_spi(spi, &config);
 	if (IS_ERR(regmap)) {
@@ -974,13 +1031,13 @@ static int pcf2127_spi_probe(struct spi_device *spi)
 
 	return pcf2127_probe(&spi->dev, regmap, spi->irq,
 			     pcf2127_spi_driver.driver.name,
-			     spi_get_device_id(spi)->driver_data);
+			     variant);
 }
 
 static const struct spi_device_id pcf2127_spi_id[] = {
-	{ "pcf2127", 1 },
-	{ "pcf2129", 0 },
-	{ "pca2129", 0 },
+	{ "pcf2127", PCF2127 },
+	{ "pcf2129", PCF2129 },
+	{ "pca2129", PCF2129 },
 	{ }
 };
 MODULE_DEVICE_TABLE(spi, pcf2127_spi_id);
-- 
2.30.2


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

* [PATCH v3 02/14] rtc: pcf2127: adapt for time/date registers at any offset
  2022-12-15 15:02 [PATCH v3 00/14] rtc: pcf2127: add PCF2131 driver Hugo Villeneuve
  2022-12-15 15:02 ` [PATCH v3 01/14] rtc: pcf2127: add variant-specific configuration structure Hugo Villeneuve
@ 2022-12-15 15:02 ` Hugo Villeneuve
  2022-12-19  9:34   ` Bruno Thomsen
  2023-01-20 18:47   ` Alexandre Belloni
  2022-12-15 15:02 ` [PATCH v3 03/14] rtc: pcf2127: adapt for alarm " Hugo Villeneuve
                   ` (12 subsequent siblings)
  14 siblings, 2 replies; 72+ messages in thread
From: Hugo Villeneuve @ 2022-12-15 15:02 UTC (permalink / raw)
  To: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt
  Cc: linux-rtc, devicetree, linux-kernel, hugo, Hugo Villeneuve

From: Hugo Villeneuve <hvilleneuve@dimonoff.com>

This will simplify the implementation of new variants into this driver.

Some variants (PCF2131) have a 100th seconds register. This register is
currently not supported in this driver.

Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
---
 drivers/rtc/rtc-pcf2127.c | 68 ++++++++++++++++++++++-----------------
 1 file changed, 39 insertions(+), 29 deletions(-)

diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
index b9a5d47a439f..fb0caacaabee 100644
--- a/drivers/rtc/rtc-pcf2127.c
+++ b/drivers/rtc/rtc-pcf2127.c
@@ -44,14 +44,17 @@
 #define PCF2127_BIT_CTRL3_BF			BIT(3)
 #define PCF2127_BIT_CTRL3_BTSE			BIT(4)
 /* Time and date registers */
-#define PCF2127_REG_SC			0x03
+#define PCF2127_REG_TIME_DATE_BASE	0x03
+/* Time and date registers offsets (starting from base register) */
+#define PCF2127_OFFSET_TD_SC		0
+#define PCF2127_OFFSET_TD_MN		1
+#define PCF2127_OFFSET_TD_HR		2
+#define PCF2127_OFFSET_TD_DM		3
+#define PCF2127_OFFSET_TD_DW		4
+#define PCF2127_OFFSET_TD_MO		5
+#define PCF2127_OFFSET_TD_YR		6
+/* Time and date registers bits */
 #define PCF2127_BIT_SC_OSF			BIT(7)
-#define PCF2127_REG_MN			0x04
-#define PCF2127_REG_HR			0x05
-#define PCF2127_REG_DM			0x06
-#define PCF2127_REG_DW			0x07
-#define PCF2127_REG_MO			0x08
-#define PCF2127_REG_YR			0x09
 /* Alarm registers */
 #define PCF2127_REG_ALARM_SC		0x0A
 #define PCF2127_REG_ALARM_MN		0x0B
@@ -106,6 +109,7 @@ struct pcf21xx_config {
 	int max_register;
 	unsigned int has_nvmem:1;
 	unsigned int has_bit_wd_ctl_cd0:1;
+	u8 regs_td_base; /* Time/data base registers. */
 };
 
 struct pcf2127 {
@@ -125,27 +129,31 @@ struct pcf2127 {
 static int pcf2127_rtc_read_time(struct device *dev, struct rtc_time *tm)
 {
 	struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
-	unsigned char buf[10];
+	unsigned char buf[7];
+	unsigned int ctrl3;
 	int ret;
 
 	/*
 	 * Avoid reading CTRL2 register as it causes WD_VAL register
 	 * value to reset to 0 which means watchdog is stopped.
 	 */
-	ret = regmap_bulk_read(pcf2127->regmap, PCF2127_REG_CTRL3,
-			       (buf + PCF2127_REG_CTRL3),
-			       ARRAY_SIZE(buf) - PCF2127_REG_CTRL3);
-	if (ret) {
-		dev_err(dev, "%s: read error\n", __func__);
+	ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL3, &ctrl3);
+	if (ret)
 		return ret;
-	}
 
-	if (buf[PCF2127_REG_CTRL3] & PCF2127_BIT_CTRL3_BLF)
+	if (ctrl3 & PCF2127_BIT_CTRL3_BLF)
 		dev_info(dev,
 			"low voltage detected, check/replace RTC battery.\n");
 
+	ret = regmap_bulk_read(pcf2127->regmap, pcf2127->cfg->regs_td_base,
+			       buf, sizeof(buf));
+	if (ret) {
+		dev_err(dev, "%s: read error\n", __func__);
+		return ret;
+	}
+
 	/* Clock integrity is not guaranteed when OSF flag is set. */
-	if (buf[PCF2127_REG_SC] & PCF2127_BIT_SC_OSF) {
+	if (buf[PCF2127_OFFSET_TD_SC] & PCF2127_BIT_SC_OSF) {
 		/*
 		 * no need clear the flag here,
 		 * it will be cleared once the new date is saved
@@ -158,18 +166,18 @@ static int pcf2127_rtc_read_time(struct device *dev, struct rtc_time *tm)
 	dev_dbg(dev,
 		"%s: raw data is cr3=%02x, sec=%02x, min=%02x, hr=%02x, "
 		"mday=%02x, wday=%02x, mon=%02x, year=%02x\n",
-		__func__, buf[PCF2127_REG_CTRL3], buf[PCF2127_REG_SC],
-		buf[PCF2127_REG_MN], buf[PCF2127_REG_HR],
-		buf[PCF2127_REG_DM], buf[PCF2127_REG_DW],
-		buf[PCF2127_REG_MO], buf[PCF2127_REG_YR]);
-
-	tm->tm_sec = bcd2bin(buf[PCF2127_REG_SC] & 0x7F);
-	tm->tm_min = bcd2bin(buf[PCF2127_REG_MN] & 0x7F);
-	tm->tm_hour = bcd2bin(buf[PCF2127_REG_HR] & 0x3F); /* rtc hr 0-23 */
-	tm->tm_mday = bcd2bin(buf[PCF2127_REG_DM] & 0x3F);
-	tm->tm_wday = buf[PCF2127_REG_DW] & 0x07;
-	tm->tm_mon = bcd2bin(buf[PCF2127_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */
-	tm->tm_year = bcd2bin(buf[PCF2127_REG_YR]);
+		__func__, ctrl3, buf[PCF2127_OFFSET_TD_SC],
+		buf[PCF2127_OFFSET_TD_MN], buf[PCF2127_OFFSET_TD_HR],
+		buf[PCF2127_OFFSET_TD_DM], buf[PCF2127_OFFSET_TD_DW],
+		buf[PCF2127_OFFSET_TD_MO], buf[PCF2127_OFFSET_TD_YR]);
+
+	tm->tm_sec = bcd2bin(buf[PCF2127_OFFSET_TD_SC] & 0x7F);
+	tm->tm_min = bcd2bin(buf[PCF2127_OFFSET_TD_MN] & 0x7F);
+	tm->tm_hour = bcd2bin(buf[PCF2127_OFFSET_TD_HR] & 0x3F); /* rtc hr 0-23 */
+	tm->tm_mday = bcd2bin(buf[PCF2127_OFFSET_TD_DM] & 0x3F);
+	tm->tm_wday = buf[PCF2127_OFFSET_TD_DW] & 0x07;
+	tm->tm_mon = bcd2bin(buf[PCF2127_OFFSET_TD_MO] & 0x1F) - 1; /* rtc mn 1-12 */
+	tm->tm_year = bcd2bin(buf[PCF2127_OFFSET_TD_YR]);
 	tm->tm_year += 100;
 
 	dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
@@ -207,7 +215,7 @@ static int pcf2127_rtc_set_time(struct device *dev, struct rtc_time *tm)
 	buf[i++] = bin2bcd(tm->tm_year - 100);
 
 	/* write register's data */
-	err = regmap_bulk_write(pcf2127->regmap, PCF2127_REG_SC, buf, i);
+	err = regmap_bulk_write(pcf2127->regmap, pcf2127->cfg->regs_td_base, buf, i);
 	if (err) {
 		dev_err(dev,
 			"%s: err=%d", __func__, err);
@@ -650,11 +658,13 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
 		.max_register = 0x1d,
 		.has_nvmem = 1,
 		.has_bit_wd_ctl_cd0 = 1,
+		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
 	},
 	[PCF2129] = {
 		.max_register = 0x19,
 		.has_nvmem = 0,
 		.has_bit_wd_ctl_cd0 = 0,
+		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
 	},
 };
 
-- 
2.30.2


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

* [PATCH v3 03/14] rtc: pcf2127: adapt for alarm registers at any offset
  2022-12-15 15:02 [PATCH v3 00/14] rtc: pcf2127: add PCF2131 driver Hugo Villeneuve
  2022-12-15 15:02 ` [PATCH v3 01/14] rtc: pcf2127: add variant-specific configuration structure Hugo Villeneuve
  2022-12-15 15:02 ` [PATCH v3 02/14] rtc: pcf2127: adapt for time/date registers at any offset Hugo Villeneuve
@ 2022-12-15 15:02 ` Hugo Villeneuve
  2023-01-07 16:57   ` Bruno Thomsen
  2023-01-20 17:10   ` Alexandre Belloni
  2022-12-15 15:02 ` [PATCH v3 04/14] rtc: pcf2127: adapt for WD " Hugo Villeneuve
                   ` (11 subsequent siblings)
  14 siblings, 2 replies; 72+ messages in thread
From: Hugo Villeneuve @ 2022-12-15 15:02 UTC (permalink / raw)
  To: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt
  Cc: linux-rtc, devicetree, linux-kernel, hugo, Hugo Villeneuve

From: Hugo Villeneuve <hvilleneuve@dimonoff.com>

This will simplify the implementation of new variants into this driver.

Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
---
 drivers/rtc/rtc-pcf2127.c | 42 ++++++++++++++++++++++-----------------
 1 file changed, 24 insertions(+), 18 deletions(-)

diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
index fb0caacaabee..db0cb784c0c9 100644
--- a/drivers/rtc/rtc-pcf2127.c
+++ b/drivers/rtc/rtc-pcf2127.c
@@ -56,11 +56,14 @@
 /* Time and date registers bits */
 #define PCF2127_BIT_SC_OSF			BIT(7)
 /* Alarm registers */
-#define PCF2127_REG_ALARM_SC		0x0A
-#define PCF2127_REG_ALARM_MN		0x0B
-#define PCF2127_REG_ALARM_HR		0x0C
-#define PCF2127_REG_ALARM_DM		0x0D
-#define PCF2127_REG_ALARM_DW		0x0E
+#define PCF2127_REG_ALARM_BASE		0x0A
+/* Alarm registers offsets (starting from base register) */
+#define PCF2127_OFFSET_ALARM_SC		0
+#define PCF2127_OFFSET_ALARM_MN		1
+#define PCF2127_OFFSET_ALARM_HR		2
+#define PCF2127_OFFSET_ALARM_DM		3
+#define PCF2127_OFFSET_ALARM_DW		4
+/* Alarm bits */
 #define PCF2127_BIT_ALARM_AE			BIT(7)
 /* CLKOUT control register */
 #define PCF2127_REG_CLKOUT		0x0f
@@ -110,6 +113,7 @@ struct pcf21xx_config {
 	unsigned int has_nvmem:1;
 	unsigned int has_bit_wd_ctl_cd0:1;
 	u8 regs_td_base; /* Time/data base registers. */
+	u8 regs_alarm_base; /* Alarm function base registers. */
 };
 
 struct pcf2127 {
@@ -402,18 +406,18 @@ static int pcf2127_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 	if (ret)
 		return ret;
 
-	ret = regmap_bulk_read(pcf2127->regmap, PCF2127_REG_ALARM_SC, buf,
-			       sizeof(buf));
+	ret = regmap_bulk_read(pcf2127->regmap, pcf2127->cfg->regs_alarm_base,
+			       buf, sizeof(buf));
 	if (ret)
 		return ret;
 
 	alrm->enabled = ctrl2 & PCF2127_BIT_CTRL2_AIE;
 	alrm->pending = ctrl2 & PCF2127_BIT_CTRL2_AF;
 
-	alrm->time.tm_sec = bcd2bin(buf[0] & 0x7F);
-	alrm->time.tm_min = bcd2bin(buf[1] & 0x7F);
-	alrm->time.tm_hour = bcd2bin(buf[2] & 0x3F);
-	alrm->time.tm_mday = bcd2bin(buf[3] & 0x3F);
+	alrm->time.tm_sec = bcd2bin(buf[PCF2127_OFFSET_ALARM_SC] & 0x7F);
+	alrm->time.tm_min = bcd2bin(buf[PCF2127_OFFSET_ALARM_MN] & 0x7F);
+	alrm->time.tm_hour = bcd2bin(buf[PCF2127_OFFSET_ALARM_HR] & 0x3F);
+	alrm->time.tm_mday = bcd2bin(buf[PCF2127_OFFSET_ALARM_DM] & 0x3F);
 
 	return 0;
 }
@@ -447,14 +451,14 @@ static int pcf2127_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 	if (ret)
 		return ret;
 
-	buf[0] = bin2bcd(alrm->time.tm_sec);
-	buf[1] = bin2bcd(alrm->time.tm_min);
-	buf[2] = bin2bcd(alrm->time.tm_hour);
-	buf[3] = bin2bcd(alrm->time.tm_mday);
-	buf[4] = PCF2127_BIT_ALARM_AE; /* Do not match on week day */
+	buf[PCF2127_OFFSET_ALARM_SC] = bin2bcd(alrm->time.tm_sec);
+	buf[PCF2127_OFFSET_ALARM_MN] = bin2bcd(alrm->time.tm_min);
+	buf[PCF2127_OFFSET_ALARM_HR] = bin2bcd(alrm->time.tm_hour);
+	buf[PCF2127_OFFSET_ALARM_DM] = bin2bcd(alrm->time.tm_mday);
+	buf[PCF2127_OFFSET_ALARM_DW] = PCF2127_BIT_ALARM_AE; /* Do not match on week day */
 
-	ret = regmap_bulk_write(pcf2127->regmap, PCF2127_REG_ALARM_SC, buf,
-				sizeof(buf));
+	ret = regmap_bulk_write(pcf2127->regmap, pcf2127->cfg->regs_alarm_base,
+				buf, sizeof(buf));
 	if (ret)
 		return ret;
 
@@ -659,12 +663,14 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
 		.has_nvmem = 1,
 		.has_bit_wd_ctl_cd0 = 1,
 		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
+		.regs_alarm_base = PCF2127_REG_ALARM_BASE,
 	},
 	[PCF2129] = {
 		.max_register = 0x19,
 		.has_nvmem = 0,
 		.has_bit_wd_ctl_cd0 = 0,
 		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
+		.regs_alarm_base = PCF2127_REG_ALARM_BASE,
 	},
 };
 
-- 
2.30.2


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

* [PATCH v3 04/14] rtc: pcf2127: adapt for WD registers at any offset
  2022-12-15 15:02 [PATCH v3 00/14] rtc: pcf2127: add PCF2131 driver Hugo Villeneuve
                   ` (2 preceding siblings ...)
  2022-12-15 15:02 ` [PATCH v3 03/14] rtc: pcf2127: adapt for alarm " Hugo Villeneuve
@ 2022-12-15 15:02 ` Hugo Villeneuve
  2023-01-07 16:59   ` Bruno Thomsen
  2022-12-15 15:02 ` [PATCH v3 05/14] rtc: pcf2127: adapt for CLKOUT register " Hugo Villeneuve
                   ` (10 subsequent siblings)
  14 siblings, 1 reply; 72+ messages in thread
From: Hugo Villeneuve @ 2022-12-15 15:02 UTC (permalink / raw)
  To: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt
  Cc: linux-rtc, devicetree, linux-kernel, hugo, Hugo Villeneuve

From: Hugo Villeneuve <hvilleneuve@dimonoff.com>

This will simplify the implementation of new variants into this driver.

Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
---
 drivers/rtc/rtc-pcf2127.c | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
index db0cb784c0c9..5d8c06e32dce 100644
--- a/drivers/rtc/rtc-pcf2127.c
+++ b/drivers/rtc/rtc-pcf2127.c
@@ -114,6 +114,8 @@ struct pcf21xx_config {
 	unsigned int has_bit_wd_ctl_cd0:1;
 	u8 regs_td_base; /* Time/data base registers. */
 	u8 regs_alarm_base; /* Alarm function base registers. */
+	u8 reg_wd_ctl; /* Watchdog control register. */
+	u8 reg_wd_val; /* Watchdog value register. */
 };
 
 struct pcf2127 {
@@ -297,7 +299,7 @@ static int pcf2127_wdt_ping(struct watchdog_device *wdd)
 {
 	struct pcf2127 *pcf2127 = watchdog_get_drvdata(wdd);
 
-	return regmap_write(pcf2127->regmap, PCF2127_REG_WD_VAL, wdd->timeout);
+	return regmap_write(pcf2127->regmap, pcf2127->cfg->reg_wd_val, wdd->timeout);
 }
 
 /*
@@ -331,7 +333,7 @@ static int pcf2127_wdt_stop(struct watchdog_device *wdd)
 {
 	struct pcf2127 *pcf2127 = watchdog_get_drvdata(wdd);
 
-	return regmap_write(pcf2127->regmap, PCF2127_REG_WD_VAL,
+	return regmap_write(pcf2127->regmap, pcf2127->cfg->reg_wd_val,
 			    PCF2127_WD_VAL_STOP);
 }
 
@@ -380,7 +382,7 @@ static int pcf2127_watchdog_init(struct device *dev, struct pcf2127 *pcf2127)
 	watchdog_set_drvdata(&pcf2127->wdd, pcf2127);
 
 	/* Test if watchdog timer is started by bootloader */
-	ret = regmap_read(pcf2127->regmap, PCF2127_REG_WD_VAL, &wdd_timeout);
+	ret = regmap_read(pcf2127->regmap, pcf2127->cfg->reg_wd_val, &wdd_timeout);
 	if (ret)
 		return ret;
 
@@ -664,6 +666,8 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
 		.has_bit_wd_ctl_cd0 = 1,
 		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
 		.regs_alarm_base = PCF2127_REG_ALARM_BASE,
+		.reg_wd_ctl = PCF2127_REG_WD_CTL,
+		.reg_wd_val = PCF2127_REG_WD_VAL,
 	},
 	[PCF2129] = {
 		.max_register = 0x19,
@@ -671,6 +675,8 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
 		.has_bit_wd_ctl_cd0 = 0,
 		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
 		.regs_alarm_base = PCF2127_REG_ALARM_BASE,
+		.reg_wd_ctl = PCF2127_REG_WD_CTL,
+		.reg_wd_val = PCF2127_REG_WD_VAL,
 	},
 };
 
@@ -772,7 +778,7 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
 	 * as T. Bits labeled as T must always be written with
 	 * logic 0.
 	 */
-	ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_WD_CTL,
+	ret = regmap_update_bits(pcf2127->regmap, pcf2127->cfg->reg_wd_ctl,
 				 PCF2127_BIT_WD_CTL_CD1 |
 				 PCF2127_BIT_WD_CTL_CD0 |
 				 PCF2127_BIT_WD_CTL_TF1 |
-- 
2.30.2


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

* [PATCH v3 05/14] rtc: pcf2127: adapt for CLKOUT register at any offset
  2022-12-15 15:02 [PATCH v3 00/14] rtc: pcf2127: add PCF2131 driver Hugo Villeneuve
                   ` (3 preceding siblings ...)
  2022-12-15 15:02 ` [PATCH v3 04/14] rtc: pcf2127: adapt for WD " Hugo Villeneuve
@ 2022-12-15 15:02 ` Hugo Villeneuve
  2023-01-07 17:01   ` Bruno Thomsen
  2022-12-15 15:02 ` [PATCH v3 06/14] rtc: pcf2127: add support for multiple TS functions Hugo Villeneuve
                   ` (9 subsequent siblings)
  14 siblings, 1 reply; 72+ messages in thread
From: Hugo Villeneuve @ 2022-12-15 15:02 UTC (permalink / raw)
  To: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt
  Cc: linux-rtc, devicetree, linux-kernel, hugo, Hugo Villeneuve

From: Hugo Villeneuve <hvilleneuve@dimonoff.com>

This will simplify the implementation of new variants into this driver.

Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
---
 drivers/rtc/rtc-pcf2127.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
index 5d8c06e32dce..38816ad065eb 100644
--- a/drivers/rtc/rtc-pcf2127.c
+++ b/drivers/rtc/rtc-pcf2127.c
@@ -116,6 +116,7 @@ struct pcf21xx_config {
 	u8 regs_alarm_base; /* Alarm function base registers. */
 	u8 reg_wd_ctl; /* Watchdog control register. */
 	u8 reg_wd_val; /* Watchdog value register. */
+	u8 reg_clkout; /* Clkout register. */
 };
 
 struct pcf2127 {
@@ -668,6 +669,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
 		.regs_alarm_base = PCF2127_REG_ALARM_BASE,
 		.reg_wd_ctl = PCF2127_REG_WD_CTL,
 		.reg_wd_val = PCF2127_REG_WD_VAL,
+		.reg_clkout = PCF2127_REG_CLKOUT,
 	},
 	[PCF2129] = {
 		.max_register = 0x19,
@@ -677,6 +679,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
 		.regs_alarm_base = PCF2127_REG_ALARM_BASE,
 		.reg_wd_ctl = PCF2127_REG_WD_CTL,
 		.reg_wd_val = PCF2127_REG_WD_VAL,
+		.reg_clkout = PCF2127_REG_CLKOUT,
 	},
 };
 
@@ -756,12 +759,12 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
 	regmap_clear_bits(pcf2127->regmap, PCF2127_REG_CTRL1,
 				PCF2127_BIT_CTRL1_POR_OVRD);
 
-	ret = regmap_read(pcf2127->regmap, PCF2127_REG_CLKOUT, &val);
+	ret = regmap_read(pcf2127->regmap, pcf2127->cfg->reg_clkout, &val);
 	if (ret < 0)
 		return ret;
 
 	if (!(val & PCF2127_BIT_CLKOUT_OTPR)) {
-		ret = regmap_set_bits(pcf2127->regmap, PCF2127_REG_CLKOUT,
+		ret = regmap_set_bits(pcf2127->regmap, pcf2127->cfg->reg_clkout,
 				      PCF2127_BIT_CLKOUT_OTPR);
 		if (ret < 0)
 			return ret;
-- 
2.30.2


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

* [PATCH v3 06/14] rtc: pcf2127: add support for multiple TS functions
  2022-12-15 15:02 [PATCH v3 00/14] rtc: pcf2127: add PCF2131 driver Hugo Villeneuve
                   ` (4 preceding siblings ...)
  2022-12-15 15:02 ` [PATCH v3 05/14] rtc: pcf2127: adapt for CLKOUT register " Hugo Villeneuve
@ 2022-12-15 15:02 ` Hugo Villeneuve
  2023-01-07 17:58   ` Bruno Thomsen
  2022-12-15 15:02 ` [PATCH v3 07/14] rtc: pcf2127: add support for PCF2131 RTC Hugo Villeneuve
                   ` (8 subsequent siblings)
  14 siblings, 1 reply; 72+ messages in thread
From: Hugo Villeneuve @ 2022-12-15 15:02 UTC (permalink / raw)
  To: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt
  Cc: linux-rtc, devicetree, linux-kernel, hugo, Hugo Villeneuve

From: Hugo Villeneuve <hvilleneuve@dimonoff.com>

This will simplify the implementation of new variants into this driver.

Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
---
 drivers/rtc/rtc-pcf2127.c | 303 +++++++++++++++++++++++++++-----------
 1 file changed, 215 insertions(+), 88 deletions(-)

diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
index 38816ad065eb..3265878edc48 100644
--- a/drivers/rtc/rtc-pcf2127.c
+++ b/drivers/rtc/rtc-pcf2127.c
@@ -75,16 +75,19 @@
 #define PCF2127_BIT_WD_CTL_CD0			BIT(6)
 #define PCF2127_BIT_WD_CTL_CD1			BIT(7)
 #define PCF2127_REG_WD_VAL		0x11
-/* Tamper timestamp registers */
-#define PCF2127_REG_TS_CTRL		0x12
+/* Tamper timestamp1 registers */
+#define PCF2127_REG_TS1_BASE		0x12
+/* Tamper timestamp registers common offsets (starting from base register) */
+#define PCF2127_OFFSET_TS_CTL		0
+#define PCF2127_OFFSET_TS_SC		1
+#define PCF2127_OFFSET_TS_MN		2
+#define PCF2127_OFFSET_TS_HR		3
+#define PCF2127_OFFSET_TS_DM		4
+#define PCF2127_OFFSET_TS_MO		5
+#define PCF2127_OFFSET_TS_YR		6
+/* Tamper timestamp registers common bits */
 #define PCF2127_BIT_TS_CTRL_TSOFF		BIT(6)
 #define PCF2127_BIT_TS_CTRL_TSM			BIT(7)
-#define PCF2127_REG_TS_SC		0x13
-#define PCF2127_REG_TS_MN		0x14
-#define PCF2127_REG_TS_HR		0x15
-#define PCF2127_REG_TS_DM		0x16
-#define PCF2127_REG_TS_MO		0x17
-#define PCF2127_REG_TS_YR		0x18
 /*
  * RAM registers
  * PCF2127 has 512 bytes general-purpose static RAM (SRAM) that is
@@ -108,6 +111,20 @@
 		PCF2127_BIT_CTRL2_WDTF | \
 		PCF2127_BIT_CTRL2_TSF2)
 
+struct pcf21xx_ts_config {
+	u8 regs_base; /* Base register to read timestamp values. */
+	/* TS input pin driven to GND detection (supported by all variants): */
+	u8 low_reg; /* Interrupt control register. */
+	u8 low_bit; /* Interrupt flag in low_reg control register. */
+	/* TS input pin driven to intermediate level between GND and supply
+	 * detection (optional feature depending on variant):
+	 */
+	u8 inter_reg; /* Interrupt control register. */
+	u8 inter_bit; /* Interrupt flag in inter_reg control register. */
+	u8 ie_reg; /* Interrupt enable control register. */
+	u8 ie_bit; /* Interrupt enable bit. */
+};
+
 struct pcf21xx_config {
 	int max_register;
 	unsigned int has_nvmem:1;
@@ -117,6 +134,9 @@ struct pcf21xx_config {
 	u8 reg_wd_ctl; /* Watchdog control register. */
 	u8 reg_wd_val; /* Watchdog value register. */
 	u8 reg_clkout; /* Clkout register. */
+	unsigned int ts_count;
+	struct pcf21xx_ts_config ts[4];
+	struct attribute_group attribute_group;
 };
 
 struct pcf2127 {
@@ -124,9 +144,9 @@ struct pcf2127 {
 	struct watchdog_device wdd;
 	struct regmap *regmap;
 	const struct pcf21xx_config *cfg;
-	time64_t ts;
-	bool ts_valid;
 	bool irq_enabled;
+	time64_t ts[4]; /* Timestamp values. */
+	bool ts_valid[4];  /* Timestamp valid indication. */
 };
 
 /*
@@ -469,38 +489,39 @@ static int pcf2127_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 }
 
 /*
- * This function reads ctrl2 register, caller is responsible for calling
- * pcf2127_wdt_active_ping()
+ * This function reads one timestamp function data, caller is responsible for
+ * calling pcf2127_wdt_active_ping()
  */
-static int pcf2127_rtc_ts_read(struct device *dev, time64_t *ts)
+static int pcf2127_rtc_ts_read(struct device *dev, time64_t *ts,
+			       int ts_id)
 {
 	struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
 	struct rtc_time tm;
 	int ret;
-	unsigned char data[25];
+	unsigned char data[7]; /* To store result of reading 7 consecutive
+				* timestamp registers.
+				*/
 
-	ret = regmap_bulk_read(pcf2127->regmap, PCF2127_REG_CTRL1, data,
-			       sizeof(data));
+	ret = regmap_bulk_read(pcf2127->regmap, pcf2127->cfg->ts[ts_id].regs_base,
+			       data, sizeof(data));
 	if (ret) {
-		dev_err(dev, "%s: read error ret=%d\n", __func__, ret);
+		dev_err(dev, "%s: bulk read error ret=%d\n", __func__, ret);
 		return ret;
 	}
 
 	dev_dbg(dev,
-		"%s: raw data is cr1=%02x, cr2=%02x, cr3=%02x, ts_sc=%02x, ts_mn=%02x, ts_hr=%02x, ts_dm=%02x, ts_mo=%02x, ts_yr=%02x\n",
-		__func__, data[PCF2127_REG_CTRL1], data[PCF2127_REG_CTRL2],
-		data[PCF2127_REG_CTRL3], data[PCF2127_REG_TS_SC],
-		data[PCF2127_REG_TS_MN], data[PCF2127_REG_TS_HR],
-		data[PCF2127_REG_TS_DM], data[PCF2127_REG_TS_MO],
-		data[PCF2127_REG_TS_YR]);
-
-	tm.tm_sec = bcd2bin(data[PCF2127_REG_TS_SC] & 0x7F);
-	tm.tm_min = bcd2bin(data[PCF2127_REG_TS_MN] & 0x7F);
-	tm.tm_hour = bcd2bin(data[PCF2127_REG_TS_HR] & 0x3F);
-	tm.tm_mday = bcd2bin(data[PCF2127_REG_TS_DM] & 0x3F);
+		"%s: raw data is ts_sc=%02x, ts_mn=%02x, ts_hr=%02x, ts_dm=%02x, ts_mo=%02x, ts_yr=%02x\n",
+		__func__, data[PCF2127_OFFSET_TS_SC], data[PCF2127_OFFSET_TS_MN],
+		data[PCF2127_OFFSET_TS_HR], data[PCF2127_OFFSET_TS_DM],
+		data[PCF2127_OFFSET_TS_MO], data[PCF2127_OFFSET_TS_YR]);
+
+	tm.tm_sec = bcd2bin(data[PCF2127_OFFSET_TS_SC] & 0x7F);
+	tm.tm_min = bcd2bin(data[PCF2127_OFFSET_TS_MN] & 0x7F);
+	tm.tm_hour = bcd2bin(data[PCF2127_OFFSET_TS_HR] & 0x3F);
+	tm.tm_mday = bcd2bin(data[PCF2127_OFFSET_TS_DM] & 0x3F);
 	/* TS_MO register (month) value range: 1-12 */
-	tm.tm_mon = bcd2bin(data[PCF2127_REG_TS_MO] & 0x1F) - 1;
-	tm.tm_year = bcd2bin(data[PCF2127_REG_TS_YR]);
+	tm.tm_mon = bcd2bin(data[PCF2127_OFFSET_TS_MO] & 0x1F) - 1;
+	tm.tm_year = bcd2bin(data[PCF2127_OFFSET_TS_YR]);
 	if (tm.tm_year < 70)
 		tm.tm_year += 100; /* assume we are in 1970...2069 */
 
@@ -514,18 +535,21 @@ static int pcf2127_rtc_ts_read(struct device *dev, time64_t *ts)
 	return 0;
 };
 
-static void pcf2127_rtc_ts_snapshot(struct device *dev)
+static void pcf2127_rtc_ts_snapshot(struct device *dev, int ts_id)
 {
 	struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
 	int ret;
 
+	if (ts_id >= pcf2127->cfg->ts_count)
+		return;
+
 	/* Let userspace read the first timestamp */
-	if (pcf2127->ts_valid)
+	if (pcf2127->ts_valid[ts_id])
 		return;
 
-	ret = pcf2127_rtc_ts_read(dev, &pcf2127->ts);
+	ret = pcf2127_rtc_ts_read(dev, &pcf2127->ts[ts_id], ts_id);
 	if (!ret)
-		pcf2127->ts_valid = true;
+		pcf2127->ts_valid[ts_id] = true;
 }
 
 static irqreturn_t pcf2127_rtc_irq(int irq, void *dev)
@@ -546,7 +570,7 @@ static irqreturn_t pcf2127_rtc_irq(int irq, void *dev)
 		return IRQ_NONE;
 
 	if (ctrl1 & PCF2127_BIT_CTRL1_TSF1 || ctrl2 & PCF2127_BIT_CTRL2_TSF2)
-		pcf2127_rtc_ts_snapshot(dev);
+		pcf2127_rtc_ts_snapshot(dev, 0);
 
 	if (ctrl1 & PCF2127_CTRL1_IRQ_MASK)
 		regmap_write(pcf2127->regmap, PCF2127_REG_CTRL1,
@@ -575,28 +599,40 @@ static const struct rtc_class_ops pcf2127_rtc_ops = {
 
 /* sysfs interface */
 
-static ssize_t timestamp0_store(struct device *dev,
-				struct device_attribute *attr,
-				const char *buf, size_t count)
+static ssize_t timestamp_store(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf, size_t count, int ts_id)
 {
 	struct pcf2127 *pcf2127 = dev_get_drvdata(dev->parent);
 	int ret;
 
+	if (ts_id >= pcf2127->cfg->ts_count)
+		return 0;
+
 	if (pcf2127->irq_enabled) {
-		pcf2127->ts_valid = false;
+		pcf2127->ts_valid[ts_id] = false;
 	} else {
-		ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL1,
-			PCF2127_BIT_CTRL1_TSF1, 0);
+		/* Always clear LOW interrupt bit. */
+		ret = regmap_update_bits(pcf2127->regmap,
+					 pcf2127->cfg->ts[ts_id].low_reg,
+					 pcf2127->cfg->ts[ts_id].low_bit,
+					 0);
+
 		if (ret) {
-			dev_err(dev, "%s: update ctrl1 ret=%d\n", __func__, ret);
+			dev_err(dev, "%s: update TS low ret=%d\n", __func__, ret);
 			return ret;
 		}
 
-		ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL2,
-			PCF2127_BIT_CTRL2_TSF2, 0);
-		if (ret) {
-			dev_err(dev, "%s: update ctrl2 ret=%d\n", __func__, ret);
-			return ret;
+		if (pcf2127->cfg->ts[ts_id].inter_bit) {
+			/* Clear INTERMEDIATE interrupt bit if supported. */
+			ret = regmap_update_bits(pcf2127->regmap,
+						 pcf2127->cfg->ts[ts_id].inter_reg,
+						 pcf2127->cfg->ts[ts_id].inter_bit,
+						 0);
+			if (ret) {
+				dev_err(dev, "%s: update TS intermediate ret=%d\n", __func__, ret);
+				return ret;
+			}
 		}
 
 		ret = pcf2127_wdt_active_ping(&pcf2127->wdd);
@@ -605,34 +641,63 @@ static ssize_t timestamp0_store(struct device *dev,
 	}
 
 	return count;
+}
+
+static ssize_t timestamp0_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	return timestamp_store(dev, attr, buf, count, 0);
 };
 
-static ssize_t timestamp0_show(struct device *dev,
-			       struct device_attribute *attr, char *buf)
+static ssize_t timestamp_show(struct device *dev,
+			      struct device_attribute *attr, char *buf,
+			      int ts_id)
 {
 	struct pcf2127 *pcf2127 = dev_get_drvdata(dev->parent);
-	unsigned int ctrl1, ctrl2;
 	int ret;
 	time64_t ts;
 
+	if (ts_id >= pcf2127->cfg->ts_count)
+		return 0;
+
 	if (pcf2127->irq_enabled) {
-		if (!pcf2127->ts_valid)
+		if (!pcf2127->ts_valid[ts_id])
 			return 0;
-		ts = pcf2127->ts;
+		ts = pcf2127->ts[ts_id];
 	} else {
-		ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL1, &ctrl1);
-		if (ret)
-			return 0;
+		u8 valid_low = 0;
+		u8 valid_inter = 0;
+		unsigned int ctrl;
 
-		ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL2, &ctrl2);
+		/* Check if TS input pin is driven to GND, supported by all
+		 * variants.
+		 */
+		ret = regmap_read(pcf2127->regmap,
+				  pcf2127->cfg->ts[ts_id].low_reg,
+				  &ctrl);
 		if (ret)
 			return 0;
 
-		if (!(ctrl1 & PCF2127_BIT_CTRL1_TSF1) &&
-		    !(ctrl2 & PCF2127_BIT_CTRL2_TSF2))
+		valid_low = ctrl & pcf2127->cfg->ts[ts_id].low_bit;
+
+		if (pcf2127->cfg->ts[ts_id].inter_bit) {
+			/* Check if TS input pin is driven to intermediate level
+			 * between GND and supply, if supported by variant.
+			 */
+			ret = regmap_read(pcf2127->regmap,
+					  pcf2127->cfg->ts[ts_id].inter_reg,
+					  &ctrl);
+			if (ret)
+				return 0;
+
+			valid_inter = ctrl & pcf2127->cfg->ts[ts_id].inter_bit;
+		}
+
+		if (!valid_low && !valid_inter)
 			return 0;
 
-		ret = pcf2127_rtc_ts_read(dev->parent, &ts);
+		ret = pcf2127_rtc_ts_read(dev->parent, &ts, ts_id);
 		if (ret)
 			return 0;
 
@@ -641,6 +706,12 @@ static ssize_t timestamp0_show(struct device *dev,
 			return ret;
 	}
 	return sprintf(buf, "%llu\n", (unsigned long long)ts);
+}
+
+static ssize_t timestamp0_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	return timestamp_show(dev, attr, buf, 0);
 };
 
 static DEVICE_ATTR_RW(timestamp0);
@@ -650,10 +721,6 @@ static struct attribute *pcf2127_attrs[] = {
 	NULL
 };
 
-static const struct attribute_group pcf2127_attr_group = {
-	.attrs	= pcf2127_attrs,
-};
-
 enum pcf21xx_type {
 	PCF2127,
 	PCF2129,
@@ -670,6 +737,19 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
 		.reg_wd_ctl = PCF2127_REG_WD_CTL,
 		.reg_wd_val = PCF2127_REG_WD_VAL,
 		.reg_clkout = PCF2127_REG_CLKOUT,
+		.ts_count = 1,
+		.ts[0] = {
+			.regs_base = PCF2127_REG_TS1_BASE,
+			.low_reg   = PCF2127_REG_CTRL1,
+			.low_bit   = PCF2127_BIT_CTRL1_TSF1,
+			.inter_reg = PCF2127_REG_CTRL2,
+			.inter_bit = PCF2127_BIT_CTRL2_TSF2,
+			.ie_reg    = PCF2127_REG_CTRL2,
+			.ie_bit    = PCF2127_BIT_CTRL2_TSIE,
+		},
+		.attribute_group = {
+			.attrs	= pcf2127_attrs,
+		},
 	},
 	[PCF2129] = {
 		.max_register = 0x19,
@@ -680,15 +760,81 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
 		.reg_wd_ctl = PCF2127_REG_WD_CTL,
 		.reg_wd_val = PCF2127_REG_WD_VAL,
 		.reg_clkout = PCF2127_REG_CLKOUT,
+		.ts_count = 1,
+		.ts[0] = {
+			.regs_base = PCF2127_REG_TS1_BASE,
+			.low_reg   = PCF2127_REG_CTRL1,
+			.low_bit   = PCF2127_BIT_CTRL1_TSF1,
+			.inter_reg = PCF2127_REG_CTRL2,
+			.inter_bit = PCF2127_BIT_CTRL2_TSF2,
+			.ie_reg    = PCF2127_REG_CTRL2,
+			.ie_bit    = PCF2127_BIT_CTRL2_TSIE,
+		},
+		.attribute_group = {
+			.attrs	= pcf2127_attrs,
+		},
 	},
 };
 
+/*
+ * Enable timestamp function and corresponding interrupt(s).
+ */
+static int pcf2127_enable_ts(struct device *dev, int ts_id)
+{
+	struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
+	int ret;
+
+	if (ts_id >= pcf2127->cfg->ts_count) {
+		dev_err(dev, "%s: invalid tamper detection ID (%d)\n",
+			__func__, ts_id);
+		return -EINVAL;
+	}
+
+	/* Enable timestamp function. */
+	ret = regmap_update_bits(pcf2127->regmap,
+				 pcf2127->cfg->ts[ts_id].regs_base,
+				 PCF2127_BIT_TS_CTRL_TSOFF |
+				 PCF2127_BIT_TS_CTRL_TSM,
+				 PCF2127_BIT_TS_CTRL_TSM);
+	if (ret) {
+		dev_err(dev, "%s: tamper detection config (ts%d_ctrl) failed\n",
+			__func__, ts_id);
+		return ret;
+	}
+
+	/* TS input pin driven to GND detection is supported by all variants.
+	 * Make sure that low_bit is defined.
+	 */
+	if (pcf2127->cfg->ts[ts_id].low_bit == 0) {
+		dev_err(dev, "%s: tamper detection LOW configuration invalid\n",
+			__func__);
+		return ret;
+	}
+
+	/*
+	 * Enable interrupt generation when TSF timestamp flag is set.
+	 * Interrupt signals are open-drain outputs and can be left floating if
+	 * unused.
+	 */
+	ret = regmap_update_bits(pcf2127->regmap, pcf2127->cfg->ts[ts_id].ie_reg,
+				 pcf2127->cfg->ts[ts_id].ie_bit,
+				 pcf2127->cfg->ts[ts_id].ie_bit);
+	if (ret) {
+		dev_err(dev, "%s: tamper detection TSIE%d config failed\n",
+			__func__, ts_id);
+		return ret;
+	}
+
+	return ret;
+}
+
 static int pcf2127_probe(struct device *dev, struct regmap *regmap,
 			 int alarm_irq, const char *name, const struct pcf21xx_config *config)
 {
 	struct pcf2127 *pcf2127;
 	int ret = 0;
 	unsigned int val;
+	int i;
 
 	dev_dbg(dev, "%s\n", __func__);
 
@@ -813,34 +959,15 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
 	}
 
 	/*
-	 * Enable timestamp function and store timestamp of first trigger
-	 * event until TSF1 and TSF2 interrupt flags are cleared.
+	 * Enable timestamp functions 1 to 4.
 	 */
-	ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_TS_CTRL,
-				 PCF2127_BIT_TS_CTRL_TSOFF |
-				 PCF2127_BIT_TS_CTRL_TSM,
-				 PCF2127_BIT_TS_CTRL_TSM);
-	if (ret) {
-		dev_err(dev, "%s: tamper detection config (ts_ctrl) failed\n",
-			__func__);
-		return ret;
-	}
-
-	/*
-	 * Enable interrupt generation when TSF1 or TSF2 timestamp flags
-	 * are set. Interrupt signal is an open-drain output and can be
-	 * left floating if unused.
-	 */
-	ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL2,
-				 PCF2127_BIT_CTRL2_TSIE,
-				 PCF2127_BIT_CTRL2_TSIE);
-	if (ret) {
-		dev_err(dev, "%s: tamper detection config (ctrl2) failed\n",
-			__func__);
-		return ret;
+	for (i = 0; i < pcf2127->cfg->ts_count; i++) {
+		ret = pcf2127_enable_ts(dev, i);
+		if (ret)
+			return ret;
 	}
 
-	ret = rtc_add_group(pcf2127->rtc, &pcf2127_attr_group);
+	ret = rtc_add_group(pcf2127->rtc, &pcf2127->cfg->attribute_group);
 	if (ret) {
 		dev_err(dev, "%s: tamper sysfs registering failed\n",
 			__func__);
-- 
2.30.2


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

* [PATCH v3 07/14] rtc: pcf2127: add support for PCF2131 RTC
  2022-12-15 15:02 [PATCH v3 00/14] rtc: pcf2127: add PCF2131 driver Hugo Villeneuve
                   ` (5 preceding siblings ...)
  2022-12-15 15:02 ` [PATCH v3 06/14] rtc: pcf2127: add support for multiple TS functions Hugo Villeneuve
@ 2022-12-15 15:02 ` Hugo Villeneuve
  2023-01-07 18:15   ` Bruno Thomsen
  2023-01-20 18:57   ` Alexandre Belloni
  2022-12-15 15:02 ` [PATCH v3 08/14] rtc: pcf2127: add support for PCF2131 interrupts on output INT_A Hugo Villeneuve
                   ` (7 subsequent siblings)
  14 siblings, 2 replies; 72+ messages in thread
From: Hugo Villeneuve @ 2022-12-15 15:02 UTC (permalink / raw)
  To: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt
  Cc: linux-rtc, devicetree, linux-kernel, hugo, Hugo Villeneuve

From: Hugo Villeneuve <hvilleneuve@dimonoff.com>

This RTC is very similar in functionality to the PCF2127/29.

Basically it:
  -supports two new control registers at offsets 4 and 5
  -supports a new reset register (not implemented in this driver)
  -supports 4 tamper detection functions instead of 1
  -has no nvmem (like the PCF2129)
  -has two output interrupt pins

Because of that, most of the register addresses are very different,
although they still follow the same layout. For example, the tamper
registers have a different base address, but the offsets are all the same.

Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
---
 drivers/rtc/Kconfig       |   4 +-
 drivers/rtc/rtc-pcf2127.c | 234 ++++++++++++++++++++++++++++++++++----
 2 files changed, 215 insertions(+), 23 deletions(-)

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 2bb640d1521d..3d4043ce0057 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -910,9 +910,9 @@ config RTC_DRV_PCF2127
 	select REGMAP_SPI if SPI_MASTER
 	select WATCHDOG_CORE if WATCHDOG
 	help
-	  If you say yes here you get support for the NXP PCF2127/29 RTC
+	  If you say yes here you get support for the NXP PCF2127/29/31 RTC
 	  chips with integrated quartz crystal for industrial applications.
-	  Both chips also have watchdog timer and tamper switch detection
+	  These chips also have watchdog timer and tamper switch detection
 	  features.
 
 	  PCF2127 has an additional feature of 512 bytes battery backed
diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
index 3265878edc48..4148e135f935 100644
--- a/drivers/rtc/rtc-pcf2127.c
+++ b/drivers/rtc/rtc-pcf2127.c
@@ -1,16 +1,26 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * An I2C and SPI driver for the NXP PCF2127/29 RTC
+ * An I2C and SPI driver for the NXP PCF2127/29/31 RTC
  * Copyright 2013 Til-Technologies
+ * Copyright 2021 DimOnOff
  *
  * Author: Renaud Cerrato <r.cerrato@til-technologies.fr>
  *
  * Watchdog and tamper functions
  * Author: Bruno Thomsen <bruno.thomsen@gmail.com>
  *
+ * PCF2131 support
+ * Author: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+ *
  * based on the other drivers in this same directory.
  *
- * Datasheet: https://www.nxp.com/docs/en/data-sheet/PCF2127.pdf
+ * Datasheets: https://www.nxp.com/docs/en/data-sheet/PCF2127.pdf
+ *             https://www.nxp.com/docs/en/data-sheet/PCF2131DS.pdf
+ */
+
+/*
+ * The following features are not yet implemented for the PCF2131:
+ *   - support for 1/100th seconds
  */
 
 #include <linux/i2c.h>
@@ -43,8 +53,30 @@
 #define PCF2127_BIT_CTRL3_BLF			BIT(2)
 #define PCF2127_BIT_CTRL3_BF			BIT(3)
 #define PCF2127_BIT_CTRL3_BTSE			BIT(4)
+/* Control register 4 */
+#define PCF2131_REG_CTRL4		0x03
+#define PCF2131_BIT_CTRL4_TSF4			BIT(4)
+#define PCF2131_BIT_CTRL4_TSF3			BIT(5)
+#define PCF2131_BIT_CTRL4_TSF2			BIT(6)
+#define PCF2131_BIT_CTRL4_TSF1			BIT(7)
+/* Control register 5 */
+#define PCF2131_REG_CTRL5		0x04
+#define PCF2131_BIT_CTRL5_TSIE4			BIT(4)
+#define PCF2131_BIT_CTRL5_TSIE3			BIT(5)
+#define PCF2131_BIT_CTRL5_TSIE2			BIT(6)
+#define PCF2131_BIT_CTRL5_TSIE1			BIT(7)
+/* Software reset register */
+#define PCF2131_REG_SR_RESET		0x05
+#define PCF2131_SR_RESET_READ_PATTERN	0b00100100 /* Fixed pattern. */
+#define PCF2131_SR_RESET_RESET_CMD	0x2C /* SR is bit 3. */
 /* Time and date registers */
 #define PCF2127_REG_TIME_DATE_BASE	0x03
+#define PCF2131_REG_TIME_DATE_BASE	0x07 /* Register 0x06 is 100th seconds,
+					      * but we do not support it. By
+					      * using offset 0x07, we can be
+					      * compatible with existing
+					      * time/date functions.
+					      */
 /* Time and date registers offsets (starting from base register) */
 #define PCF2127_OFFSET_TD_SC		0
 #define PCF2127_OFFSET_TD_MN		1
@@ -57,6 +89,7 @@
 #define PCF2127_BIT_SC_OSF			BIT(7)
 /* Alarm registers */
 #define PCF2127_REG_ALARM_BASE		0x0A
+#define PCF2131_REG_ALARM_BASE		0x0E
 /* Alarm registers offsets (starting from base register) */
 #define PCF2127_OFFSET_ALARM_SC		0
 #define PCF2127_OFFSET_ALARM_MN		1
@@ -67,16 +100,26 @@
 #define PCF2127_BIT_ALARM_AE			BIT(7)
 /* CLKOUT control register */
 #define PCF2127_REG_CLKOUT		0x0f
+#define PCF2131_REG_CLKOUT		0x13
 #define PCF2127_BIT_CLKOUT_OTPR			BIT(5)
 /* Watchdog registers */
 #define PCF2127_REG_WD_CTL		0x10
+#define PCF2131_REG_WD_CTL		0x35
 #define PCF2127_BIT_WD_CTL_TF0			BIT(0)
 #define PCF2127_BIT_WD_CTL_TF1			BIT(1)
 #define PCF2127_BIT_WD_CTL_CD0			BIT(6)
 #define PCF2127_BIT_WD_CTL_CD1			BIT(7)
 #define PCF2127_REG_WD_VAL		0x11
+#define PCF2131_REG_WD_VAL		0x36
 /* Tamper timestamp1 registers */
 #define PCF2127_REG_TS1_BASE		0x12
+#define PCF2131_REG_TS1_BASE		0x14
+/* Tamper timestamp2 registers */
+#define PCF2131_REG_TS2_BASE		0x1B
+/* Tamper timestamp3 registers */
+#define PCF2131_REG_TS3_BASE		0x22
+/* Tamper timestamp4 registers */
+#define PCF2131_REG_TS4_BASE		0x29
 /* Tamper timestamp registers common offsets (starting from base register) */
 #define PCF2127_OFFSET_TS_CTL		0
 #define PCF2127_OFFSET_TS_SC		1
@@ -92,11 +135,22 @@
  * RAM registers
  * PCF2127 has 512 bytes general-purpose static RAM (SRAM) that is
  * battery backed and can survive a power outage.
- * PCF2129 doesn't have this feature.
+ * PCF2129/31 doesn't have this feature.
  */
 #define PCF2127_REG_RAM_ADDR_MSB	0x1A
 #define PCF2127_REG_RAM_WRT_CMD		0x1C
 #define PCF2127_REG_RAM_RD_CMD		0x1D
+/* Interrupt mask registers */
+#define PCF2131_REG_INT_A_MASK1		0x31
+#define PCF2131_REG_INT_A_MASK2		0x32
+#define PCF2131_REG_INT_B_MASK1		0x33
+#define PCF2131_REG_INT_B_MASK2		0x34
+#define PCF2131_BIT_INT_BLIE		BIT(0)
+#define PCF2131_BIT_INT_BIE		BIT(1)
+#define PCF2131_BIT_INT_AIE		BIT(2)
+#define PCF2131_BIT_INT_WD_CD		BIT(3)
+#define PCF2131_BIT_INT_SI		BIT(4)
+#define PCF2131_BIT_INT_MI		BIT(5)
 
 /* Watchdog timer value constants */
 #define PCF2127_WD_VAL_STOP		0
@@ -110,6 +164,14 @@
 		PCF2127_BIT_CTRL2_AF | \
 		PCF2127_BIT_CTRL2_WDTF | \
 		PCF2127_BIT_CTRL2_TSF2)
+#define PCF2131_CTRL2_IRQ_MASK ( \
+		PCF2127_BIT_CTRL2_AF | \
+		PCF2127_BIT_CTRL2_WDTF)
+#define PCF2131_CTRL4_IRQ_MASK ( \
+		PCF2131_BIT_CTRL4_TSF4 | \
+		PCF2131_BIT_CTRL4_TSF3 | \
+		PCF2131_BIT_CTRL4_TSF2 | \
+		PCF2131_BIT_CTRL4_TSF1)
 
 struct pcf21xx_ts_config {
 	u8 regs_base; /* Base register to read timestamp values. */
@@ -370,7 +432,7 @@ static int pcf2127_wdt_set_timeout(struct watchdog_device *wdd,
 }
 
 static const struct watchdog_info pcf2127_wdt_info = {
-	.identity = "NXP PCF2127/PCF2129 Watchdog",
+	.identity = "NXP PCF2127/29/31 Watchdog",
 	.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT,
 };
 
@@ -555,30 +617,64 @@ static void pcf2127_rtc_ts_snapshot(struct device *dev, int ts_id)
 static irqreturn_t pcf2127_rtc_irq(int irq, void *dev)
 {
 	struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
-	unsigned int ctrl1, ctrl2;
+	unsigned int ctrl2;
 	int ret = 0;
 
-	ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL1, &ctrl1);
-	if (ret)
-		return IRQ_NONE;
-
 	ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL2, &ctrl2);
 	if (ret)
 		return IRQ_NONE;
 
-	if (!(ctrl1 & PCF2127_CTRL1_IRQ_MASK || ctrl2 & PCF2127_CTRL2_IRQ_MASK))
-		return IRQ_NONE;
+	if (pcf2127->cfg->ts_count == 1) {
+		/* PCF2127/29 */
+		unsigned int ctrl1;
+
+		ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL1, &ctrl1);
+		if (ret)
+			return IRQ_NONE;
+
+		if (!(ctrl1 & PCF2127_CTRL1_IRQ_MASK || ctrl2 & PCF2127_CTRL2_IRQ_MASK))
+			return IRQ_NONE;
 
-	if (ctrl1 & PCF2127_BIT_CTRL1_TSF1 || ctrl2 & PCF2127_BIT_CTRL2_TSF2)
-		pcf2127_rtc_ts_snapshot(dev, 0);
+		if (ctrl1 & PCF2127_BIT_CTRL1_TSF1 || ctrl2 & PCF2127_BIT_CTRL2_TSF2)
+			pcf2127_rtc_ts_snapshot(dev, 0);
 
-	if (ctrl1 & PCF2127_CTRL1_IRQ_MASK)
-		regmap_write(pcf2127->regmap, PCF2127_REG_CTRL1,
-			ctrl1 & ~PCF2127_CTRL1_IRQ_MASK);
+		if (ctrl1 & PCF2127_CTRL1_IRQ_MASK)
+			regmap_write(pcf2127->regmap, PCF2127_REG_CTRL1,
+				     ctrl1 & ~PCF2127_CTRL1_IRQ_MASK);
+
+		if (ctrl2 & PCF2127_CTRL2_IRQ_MASK)
+			regmap_write(pcf2127->regmap, PCF2127_REG_CTRL2,
+				     ctrl2 & ~PCF2127_CTRL2_IRQ_MASK);
+	} else {
+		/* PCF2131. */
+		unsigned int ctrl4;
+
+		ret = regmap_read(pcf2127->regmap, PCF2131_REG_CTRL4, &ctrl4);
+		if (ret)
+			return IRQ_NONE;
 
-	if (ctrl2 & PCF2127_CTRL2_IRQ_MASK)
-		regmap_write(pcf2127->regmap, PCF2127_REG_CTRL2,
-			ctrl2 & ~PCF2127_CTRL2_IRQ_MASK);
+		if (!(ctrl4 & PCF2131_CTRL4_IRQ_MASK || ctrl2 & PCF2131_CTRL2_IRQ_MASK))
+			return IRQ_NONE;
+
+		if (ctrl4 & PCF2131_CTRL4_IRQ_MASK) {
+			int i;
+			int tsf_bit = PCF2131_BIT_CTRL4_TSF1; /* Start at bit 7. */
+
+			for (i = 0; i < pcf2127->cfg->ts_count; i++) {
+				if (ctrl4 & tsf_bit)
+					pcf2127_rtc_ts_snapshot(dev, i);
+
+				tsf_bit = tsf_bit >> 1;
+			}
+
+			regmap_write(pcf2127->regmap, PCF2131_REG_CTRL4,
+				     ctrl4 & ~PCF2131_CTRL4_IRQ_MASK);
+		}
+
+		if (ctrl2 & PCF2131_CTRL2_IRQ_MASK)
+			regmap_write(pcf2127->regmap, PCF2127_REG_CTRL2,
+				     ctrl2 & ~PCF2131_CTRL2_IRQ_MASK);
+	}
 
 	if (ctrl2 & PCF2127_BIT_CTRL2_AF)
 		rtc_update_irq(pcf2127->rtc, 1, RTC_IRQF | RTC_AF);
@@ -650,6 +746,27 @@ static ssize_t timestamp0_store(struct device *dev,
 	return timestamp_store(dev, attr, buf, count, 0);
 };
 
+static ssize_t timestamp1_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	return timestamp_store(dev, attr, buf, count, 1);
+};
+
+static ssize_t timestamp2_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	return timestamp_store(dev, attr, buf, count, 2);
+};
+
+static ssize_t timestamp3_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	return timestamp_store(dev, attr, buf, count, 3);
+};
+
 static ssize_t timestamp_show(struct device *dev,
 			      struct device_attribute *attr, char *buf,
 			      int ts_id)
@@ -714,16 +831,46 @@ static ssize_t timestamp0_show(struct device *dev,
 	return timestamp_show(dev, attr, buf, 0);
 };
 
+static ssize_t timestamp1_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	return timestamp_show(dev, attr, buf, 1);
+};
+
+static ssize_t timestamp2_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	return timestamp_show(dev, attr, buf, 2);
+};
+
+static ssize_t timestamp3_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	return timestamp_show(dev, attr, buf, 3);
+};
+
 static DEVICE_ATTR_RW(timestamp0);
+static DEVICE_ATTR_RW(timestamp1);
+static DEVICE_ATTR_RW(timestamp2);
+static DEVICE_ATTR_RW(timestamp3);
 
 static struct attribute *pcf2127_attrs[] = {
 	&dev_attr_timestamp0.attr,
 	NULL
 };
 
+static struct attribute *pcf2131_attrs[] = {
+	&dev_attr_timestamp0.attr,
+	&dev_attr_timestamp1.attr,
+	&dev_attr_timestamp2.attr,
+	&dev_attr_timestamp3.attr,
+	NULL
+};
+
 enum pcf21xx_type {
 	PCF2127,
 	PCF2129,
+	PCF2131,
 	PCF21XX_LAST_ID
 };
 
@@ -774,6 +921,48 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
 			.attrs	= pcf2127_attrs,
 		},
 	},
+	[PCF2131] = {
+		.max_register = 0x36,
+		.has_nvmem = 0,
+		.has_bit_wd_ctl_cd0 = 0,
+		.regs_td_base = PCF2131_REG_TIME_DATE_BASE,
+		.regs_alarm_base = PCF2131_REG_ALARM_BASE,
+		.reg_wd_ctl = PCF2131_REG_WD_CTL,
+		.reg_wd_val = PCF2131_REG_WD_VAL,
+		.reg_clkout = PCF2131_REG_CLKOUT,
+		.ts_count = 4,
+		.ts[0] = {
+			.regs_base = PCF2131_REG_TS1_BASE,
+			.low_reg   = PCF2131_REG_CTRL4,
+			.low_bit   = PCF2131_BIT_CTRL4_TSF1,
+			.ie_reg    = PCF2131_REG_CTRL5,
+			.ie_bit    = PCF2131_BIT_CTRL5_TSIE1,
+		},
+		.ts[1] = {
+			.regs_base = PCF2131_REG_TS2_BASE,
+			.low_reg   = PCF2131_REG_CTRL4,
+			.low_bit   = PCF2131_BIT_CTRL4_TSF2,
+			.ie_reg    = PCF2131_REG_CTRL5,
+			.ie_bit    = PCF2131_BIT_CTRL5_TSIE2,
+		},
+		.ts[2] = {
+			.regs_base = PCF2131_REG_TS3_BASE,
+			.low_reg   = PCF2131_REG_CTRL4,
+			.low_bit   = PCF2131_BIT_CTRL4_TSF3,
+			.ie_reg    = PCF2131_REG_CTRL5,
+			.ie_bit    = PCF2131_BIT_CTRL5_TSIE3,
+		},
+		.ts[3] = {
+			.regs_base = PCF2131_REG_TS4_BASE,
+			.low_reg   = PCF2131_REG_CTRL4,
+			.low_bit   = PCF2131_BIT_CTRL4_TSF4,
+			.ie_reg    = PCF2131_REG_CTRL5,
+			.ie_bit    = PCF2131_BIT_CTRL5_TSIE4,
+		},
+		.attribute_group = {
+			.attrs	= pcf2131_attrs,
+		},
+	},
 };
 
 /*
@@ -922,7 +1111,7 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
 	 * Watchdog timer enabled and reset pin /RST activated when timed out.
 	 * Select 1Hz clock source for watchdog timer.
 	 * Note: Countdown timer disabled and not available.
-	 * For pca2129, pcf2129, only bit[7] is for Symbol WD_CD
+	 * For pca2129, pcf2129 and pcf2131, only bit[7] is for Symbol WD_CD
 	 * of register watchdg_tim_ctl. The bit[6] is labeled
 	 * as T. Bits labeled as T must always be written with
 	 * logic 0.
@@ -982,6 +1171,7 @@ static const struct of_device_id pcf2127_of_match[] = {
 	{ .compatible = "nxp,pcf2127", .data = &pcf21xx_cfg[PCF2127] },
 	{ .compatible = "nxp,pcf2129", .data = &pcf21xx_cfg[PCF2129] },
 	{ .compatible = "nxp,pca2129", .data = &pcf21xx_cfg[PCF2129] },
+	{ .compatible = "nxp,pcf2131", .data = &pcf21xx_cfg[PCF2131] },
 	{}
 };
 MODULE_DEVICE_TABLE(of, pcf2127_of_match);
@@ -1069,6 +1259,7 @@ static const struct i2c_device_id pcf2127_i2c_id[] = {
 	{ "pcf2127", PCF2127 },
 	{ "pcf2129", PCF2129 },
 	{ "pca2129", PCF2129 },
+	{ "pcf2131", PCF2131 },
 	{ }
 };
 MODULE_DEVICE_TABLE(i2c, pcf2127_i2c_id);
@@ -1190,6 +1381,7 @@ static const struct spi_device_id pcf2127_spi_id[] = {
 	{ "pcf2127", PCF2127 },
 	{ "pcf2129", PCF2129 },
 	{ "pca2129", PCF2129 },
+	{ "pcf2131", PCF2131 },
 	{ }
 };
 MODULE_DEVICE_TABLE(spi, pcf2127_spi_id);
@@ -1254,5 +1446,5 @@ static void __exit pcf2127_exit(void)
 module_exit(pcf2127_exit)
 
 MODULE_AUTHOR("Renaud Cerrato <r.cerrato@til-technologies.fr>");
-MODULE_DESCRIPTION("NXP PCF2127/29 RTC driver");
+MODULE_DESCRIPTION("NXP PCF2127/29/31 RTC driver");
 MODULE_LICENSE("GPL v2");
-- 
2.30.2


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

* [PATCH v3 08/14] rtc: pcf2127: add support for PCF2131 interrupts on output INT_A
  2022-12-15 15:02 [PATCH v3 00/14] rtc: pcf2127: add PCF2131 driver Hugo Villeneuve
                   ` (6 preceding siblings ...)
  2022-12-15 15:02 ` [PATCH v3 07/14] rtc: pcf2127: add support for PCF2131 RTC Hugo Villeneuve
@ 2022-12-15 15:02 ` Hugo Villeneuve
  2023-01-07 18:17   ` Bruno Thomsen
  2023-01-20 16:56   ` Alexandre Belloni
  2022-12-15 15:02 ` [PATCH v3 09/14] rtc: pcf2127: set PWRMNG value for PCF2131 Hugo Villeneuve
                   ` (6 subsequent siblings)
  14 siblings, 2 replies; 72+ messages in thread
From: Hugo Villeneuve @ 2022-12-15 15:02 UTC (permalink / raw)
  To: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt
  Cc: linux-rtc, devicetree, linux-kernel, hugo, Hugo Villeneuve

From: Hugo Villeneuve <hvilleneuve@dimonoff.com>

The PCF2127 and PCF2129 have one output interrupt pin. The PCF2131 has
two, named INT_A and INT_B. The hardware support that any interrupt
source can be routed to either one or both of them.

Force all interrupt sources to go to the INT A pin.

Support to route any interrupt source to INT A/B pins is not supported
by this driver at the moment.

Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
---
 drivers/rtc/rtc-pcf2127.c | 35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
index 4148e135f935..68af4d0438b8 100644
--- a/drivers/rtc/rtc-pcf2127.c
+++ b/drivers/rtc/rtc-pcf2127.c
@@ -191,6 +191,7 @@ struct pcf21xx_config {
 	int max_register;
 	unsigned int has_nvmem:1;
 	unsigned int has_bit_wd_ctl_cd0:1;
+	unsigned int has_int_a_b:1; /* PCF2131 supports two interrupt outputs. */
 	u8 regs_td_base; /* Time/data base registers. */
 	u8 regs_alarm_base; /* Alarm function base registers. */
 	u8 reg_wd_ctl; /* Watchdog control register. */
@@ -879,6 +880,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
 		.max_register = 0x1d,
 		.has_nvmem = 1,
 		.has_bit_wd_ctl_cd0 = 1,
+		.has_int_a_b = 0,
 		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
 		.regs_alarm_base = PCF2127_REG_ALARM_BASE,
 		.reg_wd_ctl = PCF2127_REG_WD_CTL,
@@ -902,6 +904,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
 		.max_register = 0x19,
 		.has_nvmem = 0,
 		.has_bit_wd_ctl_cd0 = 0,
+		.has_int_a_b = 0,
 		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
 		.regs_alarm_base = PCF2127_REG_ALARM_BASE,
 		.reg_wd_ctl = PCF2127_REG_WD_CTL,
@@ -925,6 +928,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
 		.max_register = 0x36,
 		.has_nvmem = 0,
 		.has_bit_wd_ctl_cd0 = 0,
+		.has_int_a_b = 1,
 		.regs_td_base = PCF2131_REG_TIME_DATE_BASE,
 		.regs_alarm_base = PCF2131_REG_ALARM_BASE,
 		.reg_wd_ctl = PCF2131_REG_WD_CTL,
@@ -1017,6 +1021,28 @@ static int pcf2127_enable_ts(struct device *dev, int ts_id)
 	return ret;
 }
 
+/* Route all interrupt sources to INT A pin. */
+static int pcf2127_configure_interrupt_pins(struct device *dev)
+{
+	struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
+	int ret;
+
+	/* Mask bits need to be cleared to enable corresponding
+	 * interrupt source.
+	 */
+	ret = regmap_write(pcf2127->regmap,
+			   PCF2131_REG_INT_A_MASK1, 0);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(pcf2127->regmap,
+			   PCF2131_REG_INT_A_MASK2, 0);
+	if (ret)
+		return ret;
+
+	return ret;
+}
+
 static int pcf2127_probe(struct device *dev, struct regmap *regmap,
 			 int alarm_irq, const char *name, const struct pcf21xx_config *config)
 {
@@ -1076,6 +1102,15 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
 		set_bit(RTC_FEATURE_ALARM, pcf2127->rtc->features);
 	}
 
+	if (pcf2127->cfg->has_int_a_b) {
+		/* Configure int A/B pins, independently of alarm_irq. */
+		ret = pcf2127_configure_interrupt_pins(dev);
+		if (ret) {
+			dev_err(dev, "failed to configure interrupt pins\n");
+			return ret;
+		}
+	}
+
 	if (pcf2127->cfg->has_nvmem) {
 		struct nvmem_config nvmem_cfg = {
 			.priv = pcf2127,
-- 
2.30.2


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

* [PATCH v3 09/14] rtc: pcf2127: set PWRMNG value for PCF2131
  2022-12-15 15:02 [PATCH v3 00/14] rtc: pcf2127: add PCF2131 driver Hugo Villeneuve
                   ` (7 preceding siblings ...)
  2022-12-15 15:02 ` [PATCH v3 08/14] rtc: pcf2127: add support for PCF2131 interrupts on output INT_A Hugo Villeneuve
@ 2022-12-15 15:02 ` Hugo Villeneuve
  2023-01-07 18:36   ` Bruno Thomsen
  2022-12-15 15:02 ` [PATCH v3 10/14] rtc: pcf2127: read and validate PCF2131 device signature Hugo Villeneuve
                   ` (5 subsequent siblings)
  14 siblings, 1 reply; 72+ messages in thread
From: Hugo Villeneuve @ 2022-12-15 15:02 UTC (permalink / raw)
  To: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt
  Cc: linux-rtc, devicetree, linux-kernel, hugo, Hugo Villeneuve

From: Hugo Villeneuve <hvilleneuve@dimonoff.com>

Default PWRMNG[2:0] bits are set to 000b for PCF2127/29, but to
111b for PCF2131.

Set these bits to 000b to select same mode as PCF2127/29.

Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
---
 drivers/rtc/rtc-pcf2127.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
index 68af4d0438b8..241189ee4a05 100644
--- a/drivers/rtc/rtc-pcf2127.c
+++ b/drivers/rtc/rtc-pcf2127.c
@@ -53,6 +53,7 @@
 #define PCF2127_BIT_CTRL3_BLF			BIT(2)
 #define PCF2127_BIT_CTRL3_BF			BIT(3)
 #define PCF2127_BIT_CTRL3_BTSE			BIT(4)
+#define PCF2127_CTRL3_PWRMNG_MASK		GENMASK(7, 5)
 /* Control register 4 */
 #define PCF2131_REG_CTRL4		0x03
 #define PCF2131_BIT_CTRL4_TSF4			BIT(4)
@@ -1129,6 +1130,20 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
 	regmap_clear_bits(pcf2127->regmap, PCF2127_REG_CTRL1,
 				PCF2127_BIT_CTRL1_POR_OVRD);
 
+	/* Make sure PWRMNG[2:0] is set to 000b. This is the default for
+	 * PCF2127/29, but not for PCF2131 (default of 111b).
+	 *
+	 * PWRMNG[2:0]  = 000b:
+	 *   battery switch-over function is enabled in standard mode;
+	 *   battery low detection function is enabled
+	 */
+	ret = regmap_clear_bits(pcf2127->regmap, PCF2127_REG_CTRL3,
+				PCF2127_CTRL3_PWRMNG_MASK);
+	if (ret < 0) {
+		dev_err(dev, "PWRMNG config failed\n");
+		return ret;
+	}
+
 	ret = regmap_read(pcf2127->regmap, pcf2127->cfg->reg_clkout, &val);
 	if (ret < 0)
 		return ret;
-- 
2.30.2


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

* [PATCH v3 10/14] rtc: pcf2127: read and validate PCF2131 device signature
  2022-12-15 15:02 [PATCH v3 00/14] rtc: pcf2127: add PCF2131 driver Hugo Villeneuve
                   ` (8 preceding siblings ...)
  2022-12-15 15:02 ` [PATCH v3 09/14] rtc: pcf2127: set PWRMNG value for PCF2131 Hugo Villeneuve
@ 2022-12-15 15:02 ` Hugo Villeneuve
  2023-01-20 17:01   ` Alexandre Belloni
  2022-12-15 15:02 ` [PATCH v3 11/14] rtc: pcf2127: adapt time/date registers write sequence for PCF2131 Hugo Villeneuve
                   ` (4 subsequent siblings)
  14 siblings, 1 reply; 72+ messages in thread
From: Hugo Villeneuve @ 2022-12-15 15:02 UTC (permalink / raw)
  To: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt
  Cc: linux-rtc, devicetree, linux-kernel, hugo, Hugo Villeneuve

From: Hugo Villeneuve <hvilleneuve@dimonoff.com>

Make sure the device we are probing is really the device we are
interested in.

Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
---
 drivers/rtc/rtc-pcf2127.c | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
index 241189ee4a05..e4b78b9c03f9 100644
--- a/drivers/rtc/rtc-pcf2127.c
+++ b/drivers/rtc/rtc-pcf2127.c
@@ -193,11 +193,13 @@ struct pcf21xx_config {
 	unsigned int has_nvmem:1;
 	unsigned int has_bit_wd_ctl_cd0:1;
 	unsigned int has_int_a_b:1; /* PCF2131 supports two interrupt outputs. */
+	unsigned int has_reset_reg:1; /* If variant has a reset register. */
 	u8 regs_td_base; /* Time/data base registers. */
 	u8 regs_alarm_base; /* Alarm function base registers. */
 	u8 reg_wd_ctl; /* Watchdog control register. */
 	u8 reg_wd_val; /* Watchdog value register. */
 	u8 reg_clkout; /* Clkout register. */
+	u8 reg_reset;  /* Reset register if available. */
 	unsigned int ts_count;
 	struct pcf21xx_ts_config ts[4];
 	struct attribute_group attribute_group;
@@ -882,6 +884,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
 		.has_nvmem = 1,
 		.has_bit_wd_ctl_cd0 = 1,
 		.has_int_a_b = 0,
+		.has_reset_reg = 0,
 		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
 		.regs_alarm_base = PCF2127_REG_ALARM_BASE,
 		.reg_wd_ctl = PCF2127_REG_WD_CTL,
@@ -906,6 +909,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
 		.has_nvmem = 0,
 		.has_bit_wd_ctl_cd0 = 0,
 		.has_int_a_b = 0,
+		.has_reset_reg = 0,
 		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
 		.regs_alarm_base = PCF2127_REG_ALARM_BASE,
 		.reg_wd_ctl = PCF2127_REG_WD_CTL,
@@ -930,11 +934,13 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
 		.has_nvmem = 0,
 		.has_bit_wd_ctl_cd0 = 0,
 		.has_int_a_b = 1,
+		.has_reset_reg = 1,
 		.regs_td_base = PCF2131_REG_TIME_DATE_BASE,
 		.regs_alarm_base = PCF2131_REG_ALARM_BASE,
 		.reg_wd_ctl = PCF2131_REG_WD_CTL,
 		.reg_wd_val = PCF2131_REG_WD_VAL,
 		.reg_clkout = PCF2131_REG_CLKOUT,
+		.reg_reset  = PCF2131_REG_SR_RESET,
 		.ts_count = 4,
 		.ts[0] = {
 			.regs_base = PCF2131_REG_TS1_BASE,
@@ -1075,6 +1081,20 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
 	clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, pcf2127->rtc->features);
 	clear_bit(RTC_FEATURE_ALARM, pcf2127->rtc->features);
 
+	/* Read device signature if available. */
+	if (pcf2127->cfg->has_reset_reg) {
+		ret = regmap_read(pcf2127->regmap, pcf2127->cfg->reg_reset, &val);
+		if (ret < 0) {
+			dev_err(dev, "reading RESET register failed\n");
+			return ret;
+		}
+
+		if (val != PCF2131_SR_RESET_READ_PATTERN) {
+			dev_err(dev, "invalid device signature: $%02X\n", (u8)val);
+			return -ENODEV;
+		}
+	}
+
 	if (alarm_irq > 0) {
 		unsigned long flags;
 
-- 
2.30.2


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

* [PATCH v3 11/14] rtc: pcf2127: adapt time/date registers write sequence for PCF2131
  2022-12-15 15:02 [PATCH v3 00/14] rtc: pcf2127: add PCF2131 driver Hugo Villeneuve
                   ` (9 preceding siblings ...)
  2022-12-15 15:02 ` [PATCH v3 10/14] rtc: pcf2127: read and validate PCF2131 device signature Hugo Villeneuve
@ 2022-12-15 15:02 ` Hugo Villeneuve
  2023-01-07 18:44   ` Bruno Thomsen
  2023-01-20 17:09   ` Alexandre Belloni
  2022-12-15 15:02 ` [PATCH v3 12/14] rtc: pcf2127: support generic watchdog timing configuration Hugo Villeneuve
                   ` (3 subsequent siblings)
  14 siblings, 2 replies; 72+ messages in thread
From: Hugo Villeneuve @ 2022-12-15 15:02 UTC (permalink / raw)
  To: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt
  Cc: linux-rtc, devicetree, linux-kernel, hugo, Hugo Villeneuve

From: Hugo Villeneuve <hvilleneuve@dimonoff.com>

The sequence for updating the time/date registers is slightly
different between PCF2127/29 and PCF2131.

For PCF2127/29, during write operations, the time counting
circuits (memory locations 03h through 09h) are automatically blocked.

For PCF2131, time/date registers write access requires setting the
STOP bit and sending the clear prescaler instruction (CPR). STOP then
needs to be released once write operation is completed.

Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
---
 drivers/rtc/rtc-pcf2127.c | 38 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 37 insertions(+), 1 deletion(-)

diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
index e4b78b9c03f9..11fbdab6bf01 100644
--- a/drivers/rtc/rtc-pcf2127.c
+++ b/drivers/rtc/rtc-pcf2127.c
@@ -39,6 +39,7 @@
 #define PCF2127_REG_CTRL1		0x00
 #define PCF2127_BIT_CTRL1_POR_OVRD		BIT(3)
 #define PCF2127_BIT_CTRL1_TSF1			BIT(4)
+#define PCF2127_BIT_CTRL1_STOP			BIT(5)
 /* Control register 2 */
 #define PCF2127_REG_CTRL2		0x01
 #define PCF2127_BIT_CTRL2_AIE			BIT(1)
@@ -70,6 +71,7 @@
 #define PCF2131_REG_SR_RESET		0x05
 #define PCF2131_SR_RESET_READ_PATTERN	0b00100100 /* Fixed pattern. */
 #define PCF2131_SR_RESET_RESET_CMD	0x2C /* SR is bit 3. */
+#define PCF2131_SR_RESET_CPR_CMD	0xA4 /* CPR is bit 7. */
 /* Time and date registers */
 #define PCF2127_REG_TIME_DATE_BASE	0x03
 #define PCF2131_REG_TIME_DATE_BASE	0x07 /* Register 0x06 is 100th seconds,
@@ -307,7 +309,31 @@ static int pcf2127_rtc_set_time(struct device *dev, struct rtc_time *tm)
 	/* year */
 	buf[i++] = bin2bcd(tm->tm_year - 100);
 
-	/* write register's data */
+	/* Write access to time registers:
+	 * PCF2127/29: no special action required.
+	 * PCF2131:    requires setting the STOP bit. STOP bit needs to
+	 *             be cleared after time registers are updated.
+	 *             It is also recommended to set CPR bit, although
+	 *             write access will work without it.
+	 */
+	if (pcf2127->cfg->has_reset_reg) {
+		err = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL1,
+					 PCF2127_BIT_CTRL1_STOP,
+					 PCF2127_BIT_CTRL1_STOP);
+		if (err) {
+			dev_err(dev, "setting STOP bit failed\n");
+			return err;
+		}
+
+		err = regmap_write(pcf2127->regmap, pcf2127->cfg->reg_reset,
+				   PCF2131_SR_RESET_CPR_CMD);
+		if (err) {
+			dev_err(dev, "sending CPR cmd failed\n");
+			return err;
+		}
+	}
+
+	/* write time register's data */
 	err = regmap_bulk_write(pcf2127->regmap, pcf2127->cfg->regs_td_base, buf, i);
 	if (err) {
 		dev_err(dev,
@@ -315,6 +341,16 @@ static int pcf2127_rtc_set_time(struct device *dev, struct rtc_time *tm)
 		return err;
 	}
 
+	if (pcf2127->cfg->has_reset_reg) {
+		/* Clear STOP bit (PCF2131 only) after write is completed. */
+		err = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL1,
+					 PCF2127_BIT_CTRL1_STOP, 0);
+		if (err) {
+			dev_err(dev, "clearing STOP bit failed\n");
+			return err;
+		}
+	}
+
 	return 0;
 }
 
-- 
2.30.2


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

* [PATCH v3 12/14] rtc: pcf2127: support generic watchdog timing configuration
  2022-12-15 15:02 [PATCH v3 00/14] rtc: pcf2127: add PCF2131 driver Hugo Villeneuve
                   ` (10 preceding siblings ...)
  2022-12-15 15:02 ` [PATCH v3 11/14] rtc: pcf2127: adapt time/date registers write sequence for PCF2131 Hugo Villeneuve
@ 2022-12-15 15:02 ` Hugo Villeneuve
  2023-01-18 13:23   ` Philipp Rosenberger
  2022-12-15 15:02 ` [PATCH v3 13/14] rtc: pcf2127: add flag for watchdog register value read support Hugo Villeneuve
                   ` (2 subsequent siblings)
  14 siblings, 1 reply; 72+ messages in thread
From: Hugo Villeneuve @ 2022-12-15 15:02 UTC (permalink / raw)
  To: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt
  Cc: linux-rtc, devicetree, linux-kernel, hugo, Hugo Villeneuve

From: Hugo Villeneuve <hvilleneuve@dimonoff.com>

Introduce in the configuration structure two new values to hold the
watchdog clock source and the min_hw_heartbeat_ms value.

The minimum and maximum timeout values are automatically computed from
the watchdog clock source value for each variant.

The PCF2131 has no 1Hz watchdog clock source, as is the case for
PCF2127/29.

The next best choice is using a 1/4Hz clock, giving a watchdog timeout
range between 4 and 1016s. By using the same register configuration as
for the PCF2127/29, the 1/4Hz clock source is selected.

Note: the PCF2127 datasheet gives a min/max range between 1 and 255s,
but it should be between 2 and 254s, because the watchdog is triggered
when the timer value reaches 1, not 0.

Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
---
 drivers/rtc/rtc-pcf2127.c | 56 +++++++++++++++++++++++++++++++++------
 1 file changed, 48 insertions(+), 8 deletions(-)

diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
index 11fbdab6bf01..3fd2fee4978b 100644
--- a/drivers/rtc/rtc-pcf2127.c
+++ b/drivers/rtc/rtc-pcf2127.c
@@ -157,9 +157,29 @@
 
 /* Watchdog timer value constants */
 #define PCF2127_WD_VAL_STOP		0
-#define PCF2127_WD_VAL_MIN		2
-#define PCF2127_WD_VAL_MAX		255
-#define PCF2127_WD_VAL_DEFAULT		60
+#define PCF2127_WD_VAL_DEFAULT		60 /* In seconds. */
+/* PCF2127/29 watchdog timer value constants */
+#define PCF2127_WD_CLOCK_HZ_X1000	1000 /* 1Hz */
+#define PCF2127_WD_MIN_HW_HEARTBEAT_MS	500
+/* PCF2131 watchdog timer value constants */
+#define PCF2131_WD_CLOCK_HZ_X1000	250  /* 1/4Hz */
+#define PCF2131_WD_MIN_HW_HEARTBEAT_MS	4000
+/*
+ * Compute watchdog period, t, in seconds, from the WATCHDG_TIM_VAL register
+ * value, n, and the clock frequency, f, in Hz.
+ *
+ * The PCF2127/29 datasheet gives t as:
+ *   t = n / f
+ * The PCF2131 datasheet gives t as:
+ *   t = (n - 1) / f
+ * For both variants, the watchdog is triggered when the WATCHDG_TIM_VAL reaches
+ * the value 1, and not zero. Consequently, the equation from the PCF2131
+ * datasheet seems to be the correct one for both variants.
+ */
+#define WD_PERIOD_S(_n_, _f1000_) ((1000 * ((_n_) - 1)) / (_f1000_))
+
+/* Compute value of WATCHDG_TIM_VAL to obtain period t, in seconds. */
+#define WD_COUNTER(_t_, _f1000_) ((((_t_) * (_f1000_)) / 1000) + 1)
 
 /* Mask for currently enabled interrupts */
 #define PCF2127_CTRL1_IRQ_MASK (PCF2127_BIT_CTRL1_TSF1)
@@ -202,6 +222,11 @@ struct pcf21xx_config {
 	u8 reg_wd_val; /* Watchdog value register. */
 	u8 reg_clkout; /* Clkout register. */
 	u8 reg_reset;  /* Reset register if available. */
+
+	/* Watchdog configuration. */
+	int wdd_clock_hz_x1000; /* Value in Hz multiplicated by 1000 */
+	int wdd_min_hw_heartbeat_ms;
+
 	unsigned int ts_count;
 	struct pcf21xx_ts_config ts[4];
 	struct attribute_group attribute_group;
@@ -496,10 +521,19 @@ static int pcf2127_watchdog_init(struct device *dev, struct pcf2127 *pcf2127)
 	pcf2127->wdd.parent = dev;
 	pcf2127->wdd.info = &pcf2127_wdt_info;
 	pcf2127->wdd.ops = &pcf2127_watchdog_ops;
-	pcf2127->wdd.min_timeout = PCF2127_WD_VAL_MIN;
-	pcf2127->wdd.max_timeout = PCF2127_WD_VAL_MAX;
-	pcf2127->wdd.timeout = PCF2127_WD_VAL_DEFAULT;
-	pcf2127->wdd.min_hw_heartbeat_ms = 500;
+
+	pcf2127->wdd.min_timeout =
+		WD_PERIOD_S(2, pcf2127->cfg->wdd_clock_hz_x1000);
+	pcf2127->wdd.max_timeout =
+		WD_PERIOD_S(255, pcf2127->cfg->wdd_clock_hz_x1000);
+	pcf2127->wdd.timeout = WD_COUNTER(PCF2127_WD_VAL_DEFAULT,
+					  pcf2127->cfg->wdd_clock_hz_x1000);
+
+	dev_dbg(dev, "%s min = %ds\n", __func__, pcf2127->wdd.min_timeout);
+	dev_dbg(dev, "%s max = %ds\n", __func__, pcf2127->wdd.max_timeout);
+	dev_dbg(dev, "%s def = %d\n", __func__, pcf2127->wdd.timeout);
+
+	pcf2127->wdd.min_hw_heartbeat_ms = pcf2127->cfg->wdd_min_hw_heartbeat_ms;
 	pcf2127->wdd.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
 
 	watchdog_set_drvdata(&pcf2127->wdd, pcf2127);
@@ -926,6 +960,8 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
 		.reg_wd_ctl = PCF2127_REG_WD_CTL,
 		.reg_wd_val = PCF2127_REG_WD_VAL,
 		.reg_clkout = PCF2127_REG_CLKOUT,
+		.wdd_clock_hz_x1000 = PCF2127_WD_CLOCK_HZ_X1000,
+		.wdd_min_hw_heartbeat_ms = PCF2127_WD_MIN_HW_HEARTBEAT_MS,
 		.ts_count = 1,
 		.ts[0] = {
 			.regs_base = PCF2127_REG_TS1_BASE,
@@ -951,6 +987,8 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
 		.reg_wd_ctl = PCF2127_REG_WD_CTL,
 		.reg_wd_val = PCF2127_REG_WD_VAL,
 		.reg_clkout = PCF2127_REG_CLKOUT,
+		.wdd_clock_hz_x1000 = PCF2127_WD_CLOCK_HZ_X1000,
+		.wdd_min_hw_heartbeat_ms = PCF2127_WD_MIN_HW_HEARTBEAT_MS,
 		.ts_count = 1,
 		.ts[0] = {
 			.regs_base = PCF2127_REG_TS1_BASE,
@@ -977,6 +1015,8 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
 		.reg_wd_val = PCF2131_REG_WD_VAL,
 		.reg_clkout = PCF2131_REG_CLKOUT,
 		.reg_reset  = PCF2131_REG_SR_RESET,
+		.wdd_clock_hz_x1000 = PCF2131_WD_CLOCK_HZ_X1000,
+		.wdd_min_hw_heartbeat_ms = PCF2131_WD_MIN_HW_HEARTBEAT_MS,
 		.ts_count = 4,
 		.ts[0] = {
 			.regs_base = PCF2131_REG_TS1_BASE,
@@ -1215,7 +1255,7 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
 
 	/*
 	 * Watchdog timer enabled and reset pin /RST activated when timed out.
-	 * Select 1Hz clock source for watchdog timer.
+	 * Select 1Hz clock source for watchdog timer (1/4Hz for PCF2131).
 	 * Note: Countdown timer disabled and not available.
 	 * For pca2129, pcf2129 and pcf2131, only bit[7] is for Symbol WD_CD
 	 * of register watchdg_tim_ctl. The bit[6] is labeled
-- 
2.30.2


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

* [PATCH v3 13/14] rtc: pcf2127: add flag for watchdog register value read support
  2022-12-15 15:02 [PATCH v3 00/14] rtc: pcf2127: add PCF2131 driver Hugo Villeneuve
                   ` (11 preceding siblings ...)
  2022-12-15 15:02 ` [PATCH v3 12/14] rtc: pcf2127: support generic watchdog timing configuration Hugo Villeneuve
@ 2022-12-15 15:02 ` Hugo Villeneuve
  2023-01-07 18:47   ` Bruno Thomsen
  2022-12-15 15:02 ` [PATCH v3 14/14] dt-bindings: rtc: pcf2127: add PCF2131 Hugo Villeneuve
  2023-01-20 19:05 ` [PATCH v3 00/14] rtc: pcf2127: add PCF2131 driver Alexandre Belloni
  14 siblings, 1 reply; 72+ messages in thread
From: Hugo Villeneuve @ 2022-12-15 15:02 UTC (permalink / raw)
  To: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt
  Cc: linux-rtc, devicetree, linux-kernel, hugo, Hugo Villeneuve

From: Hugo Villeneuve <hvilleneuve@dimonoff.com>

The watchdog value register cannot be read on the PCF2131 after being
set.

Add a new flag to identify which variant has read access to this
register, and use this flag to selectively test if watchdog timer was
started by bootloader.

Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
---
 drivers/rtc/rtc-pcf2127.c | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
index 3fd2fee4978b..1d2b5c9e6757 100644
--- a/drivers/rtc/rtc-pcf2127.c
+++ b/drivers/rtc/rtc-pcf2127.c
@@ -214,6 +214,7 @@ struct pcf21xx_config {
 	int max_register;
 	unsigned int has_nvmem:1;
 	unsigned int has_bit_wd_ctl_cd0:1;
+	unsigned int wd_val_reg_readable:1; /* If watchdog value register can be read. */
 	unsigned int has_int_a_b:1; /* PCF2131 supports two interrupt outputs. */
 	unsigned int has_reset_reg:1; /* If variant has a reset register. */
 	u8 regs_td_base; /* Time/data base registers. */
@@ -511,7 +512,6 @@ static const struct watchdog_ops pcf2127_watchdog_ops = {
 
 static int pcf2127_watchdog_init(struct device *dev, struct pcf2127 *pcf2127)
 {
-	u32 wdd_timeout;
 	int ret;
 
 	if (!IS_ENABLED(CONFIG_WATCHDOG) ||
@@ -539,12 +539,17 @@ static int pcf2127_watchdog_init(struct device *dev, struct pcf2127 *pcf2127)
 	watchdog_set_drvdata(&pcf2127->wdd, pcf2127);
 
 	/* Test if watchdog timer is started by bootloader */
-	ret = regmap_read(pcf2127->regmap, pcf2127->cfg->reg_wd_val, &wdd_timeout);
-	if (ret)
-		return ret;
+	if (pcf2127->cfg->wd_val_reg_readable) {
+		u32 wdd_timeout;
+
+		ret = regmap_read(pcf2127->regmap, pcf2127->cfg->reg_wd_val,
+				  &wdd_timeout);
+		if (ret)
+			return ret;
 
-	if (wdd_timeout)
-		set_bit(WDOG_HW_RUNNING, &pcf2127->wdd.status);
+		if (wdd_timeout)
+			set_bit(WDOG_HW_RUNNING, &pcf2127->wdd.status);
+	}
 
 	return devm_watchdog_register_device(dev, &pcf2127->wdd);
 }
@@ -953,6 +958,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
 		.max_register = 0x1d,
 		.has_nvmem = 1,
 		.has_bit_wd_ctl_cd0 = 1,
+		.wd_val_reg_readable = 1,
 		.has_int_a_b = 0,
 		.has_reset_reg = 0,
 		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
@@ -980,6 +986,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
 		.max_register = 0x19,
 		.has_nvmem = 0,
 		.has_bit_wd_ctl_cd0 = 0,
+		.wd_val_reg_readable = 1,
 		.has_int_a_b = 0,
 		.has_reset_reg = 0,
 		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
@@ -1007,6 +1014,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
 		.max_register = 0x36,
 		.has_nvmem = 0,
 		.has_bit_wd_ctl_cd0 = 0,
+		.wd_val_reg_readable = 0,
 		.has_int_a_b = 1,
 		.has_reset_reg = 1,
 		.regs_td_base = PCF2131_REG_TIME_DATE_BASE,
-- 
2.30.2


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

* [PATCH v3 14/14] dt-bindings: rtc: pcf2127: add PCF2131
  2022-12-15 15:02 [PATCH v3 00/14] rtc: pcf2127: add PCF2131 driver Hugo Villeneuve
                   ` (12 preceding siblings ...)
  2022-12-15 15:02 ` [PATCH v3 13/14] rtc: pcf2127: add flag for watchdog register value read support Hugo Villeneuve
@ 2022-12-15 15:02 ` Hugo Villeneuve
  2022-12-16 13:24   ` Krzysztof Kozlowski
  2022-12-19  9:14   ` Bruno Thomsen
  2023-01-20 19:05 ` [PATCH v3 00/14] rtc: pcf2127: add PCF2131 driver Alexandre Belloni
  14 siblings, 2 replies; 72+ messages in thread
From: Hugo Villeneuve @ 2022-12-15 15:02 UTC (permalink / raw)
  To: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt
  Cc: linux-rtc, devicetree, linux-kernel, hugo, Hugo Villeneuve

From: Hugo Villeneuve <hvilleneuve@dimonoff.com>

Add support for new NXP RTC PCF2131.

Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
---
 Documentation/devicetree/bindings/rtc/nxp,pcf2127.yaml | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/rtc/nxp,pcf2127.yaml b/Documentation/devicetree/bindings/rtc/nxp,pcf2127.yaml
index cde7b1675ead..a8f8c23da4d8 100644
--- a/Documentation/devicetree/bindings/rtc/nxp,pcf2127.yaml
+++ b/Documentation/devicetree/bindings/rtc/nxp,pcf2127.yaml
@@ -14,7 +14,9 @@ maintainers:
 
 properties:
   compatible:
-    const: nxp,pcf2127
+    enum:
+      - nxp,pcf2127
+      - nxp,pcf2131
 
   reg:
     maxItems: 1
-- 
2.30.2


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

* Re: [PATCH v3 14/14] dt-bindings: rtc: pcf2127: add PCF2131
  2022-12-15 15:02 ` [PATCH v3 14/14] dt-bindings: rtc: pcf2127: add PCF2131 Hugo Villeneuve
@ 2022-12-16 13:24   ` Krzysztof Kozlowski
  2022-12-19  9:14   ` Bruno Thomsen
  1 sibling, 0 replies; 72+ messages in thread
From: Krzysztof Kozlowski @ 2022-12-16 13:24 UTC (permalink / raw)
  To: Hugo Villeneuve, a.zummo, alexandre.belloni, robh+dt,
	krzysztof.kozlowski+dt
  Cc: linux-rtc, devicetree, linux-kernel, Hugo Villeneuve

On 15/12/2022 16:02, Hugo Villeneuve wrote:
> From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> 
> Add support for new NXP RTC PCF2131.
> 
> Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> ---
>  Documentation/devicetree/bindings/rtc/nxp,pcf2127.yaml | 4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
> 


Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>

Best regards,
Krzysztof


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

* Re: [PATCH v3 01/14] rtc: pcf2127: add variant-specific configuration structure
  2022-12-15 15:02 ` [PATCH v3 01/14] rtc: pcf2127: add variant-specific configuration structure Hugo Villeneuve
@ 2022-12-19  9:05   ` Bruno Thomsen
  2022-12-19 15:15     ` Hugo Villeneuve
  2023-01-07 16:52   ` Bruno Thomsen
  1 sibling, 1 reply; 72+ messages in thread
From: Bruno Thomsen @ 2022-12-19  9:05 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt,
	linux-rtc, devicetree, linux-kernel, Hugo Villeneuve

Den tor. 15. dec. 2022 kl. 16.19 skrev Hugo Villeneuve <hugo@hugovil.com>:
>
> From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
>
> Create variant-specific configuration structures to simplify the
> implementation of new variants into this driver. It will also avoid
> to have too many tests for a specific variant, or a list of variants
> for new devices, inside the code itself.
>
> Add configuration options for the support of the NVMEM, bit CD0 in
> register WD_CTL as well as the maximum number of registers for each
> variant, instead of hardcoding the variant (PCF2127) inside the
> i2c_device_id and spi_device_id structures.
>
> Also specify a different maximum number of registers (max_register)
> for the PCF2129.
>
> Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> ---
>  drivers/rtc/rtc-pcf2127.c | 95 +++++++++++++++++++++++++++++++--------
>  1 file changed, 76 insertions(+), 19 deletions(-)
>
> diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> index 87f4fc9df68b..b9a5d47a439f 100644
> --- a/drivers/rtc/rtc-pcf2127.c
> +++ b/drivers/rtc/rtc-pcf2127.c
> @@ -21,6 +21,7 @@
>  #include <linux/module.h>
>  #include <linux/of.h>
>  #include <linux/of_irq.h>
> +#include <linux/of_device.h>
>  #include <linux/regmap.h>
>  #include <linux/watchdog.h>
>
> @@ -101,10 +102,17 @@
>                 PCF2127_BIT_CTRL2_WDTF | \
>                 PCF2127_BIT_CTRL2_TSF2)
>
> +struct pcf21xx_config {
> +       int max_register;
> +       unsigned int has_nvmem:1;
> +       unsigned int has_bit_wd_ctl_cd0:1;
> +};
> +
>  struct pcf2127 {
>         struct rtc_device *rtc;
>         struct watchdog_device wdd;
>         struct regmap *regmap;
> +       const struct pcf21xx_config *cfg;
>         time64_t ts;
>         bool ts_valid;
>         bool irq_enabled;
> @@ -631,8 +639,27 @@ static const struct attribute_group pcf2127_attr_group = {
>         .attrs  = pcf2127_attrs,
>  };
>
> +enum pcf21xx_type {
> +       PCF2127,
> +       PCF2129,
> +       PCF21XX_LAST_ID
> +};
> +
> +static struct pcf21xx_config pcf21xx_cfg[] = {
> +       [PCF2127] = {
> +               .max_register = 0x1d,
> +               .has_nvmem = 1,
> +               .has_bit_wd_ctl_cd0 = 1,
> +       },
> +       [PCF2129] = {
> +               .max_register = 0x19,
> +               .has_nvmem = 0,
> +               .has_bit_wd_ctl_cd0 = 0,
> +       },
> +};
> +
>  static int pcf2127_probe(struct device *dev, struct regmap *regmap,
> -                        int alarm_irq, const char *name, bool is_pcf2127)
> +                        int alarm_irq, const char *name, const struct pcf21xx_config *config)
>  {
>         struct pcf2127 *pcf2127;
>         int ret = 0;
> @@ -645,6 +672,7 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
>                 return -ENOMEM;
>
>         pcf2127->regmap = regmap;
> +       pcf2127->cfg = config;
>
>         dev_set_drvdata(dev, pcf2127);
>
> @@ -688,7 +716,7 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
>                 set_bit(RTC_FEATURE_ALARM, pcf2127->rtc->features);
>         }
>
> -       if (is_pcf2127) {
> +       if (pcf2127->cfg->has_nvmem) {
>                 struct nvmem_config nvmem_cfg = {
>                         .priv = pcf2127,
>                         .reg_read = pcf2127_nvmem_read,
> @@ -734,7 +762,7 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
>                                  PCF2127_BIT_WD_CTL_TF1 |
>                                  PCF2127_BIT_WD_CTL_TF0,
>                                  PCF2127_BIT_WD_CTL_CD1 |
> -                                (is_pcf2127 ? PCF2127_BIT_WD_CTL_CD0 : 0) |
> +                                (pcf2127->cfg->has_bit_wd_ctl_cd0 ? PCF2127_BIT_WD_CTL_CD0 : 0) |
>                                  PCF2127_BIT_WD_CTL_TF1);
>         if (ret) {
>                 dev_err(dev, "%s: watchdog config (wd_ctl) failed\n", __func__);
> @@ -799,9 +827,9 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
>
>  #ifdef CONFIG_OF
>  static const struct of_device_id pcf2127_of_match[] = {
> -       { .compatible = "nxp,pcf2127" },
> -       { .compatible = "nxp,pcf2129" },
> -       { .compatible = "nxp,pca2129" },
> +       { .compatible = "nxp,pcf2127", .data = &pcf21xx_cfg[PCF2127] },
> +       { .compatible = "nxp,pcf2129", .data = &pcf21xx_cfg[PCF2129] },
> +       { .compatible = "nxp,pca2129", .data = &pcf21xx_cfg[PCF2129] },
>         {}
>  };
>  MODULE_DEVICE_TABLE(of, pcf2127_of_match);
> @@ -886,26 +914,40 @@ static const struct regmap_bus pcf2127_i2c_regmap = {
>  static struct i2c_driver pcf2127_i2c_driver;
>
>  static const struct i2c_device_id pcf2127_i2c_id[] = {
> -       { "pcf2127", 1 },
> -       { "pcf2129", 0 },
> -       { "pca2129", 0 },
> +       { "pcf2127", PCF2127 },
> +       { "pcf2129", PCF2129 },
> +       { "pca2129", PCF2129 },
>         { }
>  };
>  MODULE_DEVICE_TABLE(i2c, pcf2127_i2c_id);
>
>  static int pcf2127_i2c_probe(struct i2c_client *client)
>  {
> -       const struct i2c_device_id *id = i2c_match_id(pcf2127_i2c_id, client);
>         struct regmap *regmap;
> -       static const struct regmap_config config = {
> +       static struct regmap_config config = {
>                 .reg_bits = 8,
>                 .val_bits = 8,
> -               .max_register = 0x1d,
>         };
> +       const struct pcf21xx_config *variant;

Hi Hugo,

Patch series does not apply on 6.1 tree as pcf2127_i2c_probe() call
signature does not match[1].

static int pcf2127_i2c_probe(struct i2c_client *client,
      const struct i2c_device_id *id)


[1] https://elixir.bootlin.com/linux/v6.1/source/drivers/rtc/rtc-pcf2127.c#L888

/Bruno

>
>         if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
>                 return -ENODEV;
>
> +       if (client->dev.of_node) {
> +               variant = of_device_get_match_data(&client->dev);
> +               if (!variant)
> +                       return -ENODEV;
> +       } else {
> +               enum pcf21xx_type type =
> +                       i2c_match_id(pcf2127_i2c_id, client)->driver_data;
> +
> +               if (type >= PCF21XX_LAST_ID)
> +                       return -ENODEV;
> +               variant = &pcf21xx_cfg[type];
> +       }
> +
> +       config.max_register = variant->max_register,
> +
>         regmap = devm_regmap_init(&client->dev, &pcf2127_i2c_regmap,
>                                         &client->dev, &config);
>         if (IS_ERR(regmap)) {
> @@ -915,7 +957,7 @@ static int pcf2127_i2c_probe(struct i2c_client *client)
>         }
>
>         return pcf2127_probe(&client->dev, regmap, client->irq,
> -                            pcf2127_i2c_driver.driver.name, id->driver_data);
> +                            pcf2127_i2c_driver.driver.name, variant);
>  }
>
>  static struct i2c_driver pcf2127_i2c_driver = {
> @@ -953,17 +995,32 @@ static void pcf2127_i2c_unregister_driver(void)
>  #if IS_ENABLED(CONFIG_SPI_MASTER)
>
>  static struct spi_driver pcf2127_spi_driver;
> +static const struct spi_device_id pcf2127_spi_id[];
>
>  static int pcf2127_spi_probe(struct spi_device *spi)
>  {
> -       static const struct regmap_config config = {
> +       static struct regmap_config config = {
>                 .reg_bits = 8,
>                 .val_bits = 8,
>                 .read_flag_mask = 0xa0,
>                 .write_flag_mask = 0x20,
> -               .max_register = 0x1d,
>         };
>         struct regmap *regmap;
> +       const struct pcf21xx_config *variant;
> +
> +       if (spi->dev.of_node) {
> +               variant = of_device_get_match_data(&spi->dev);
> +               if (!variant)
> +                       return -ENODEV;
> +       } else {
> +               enum pcf21xx_type type = spi_get_device_id(spi)->driver_data;
> +
> +               if (type >= PCF21XX_LAST_ID)
> +                       return -ENODEV;
> +               variant = &pcf21xx_cfg[type];
> +       }
> +
> +       config.max_register = variant->max_register,
>
>         regmap = devm_regmap_init_spi(spi, &config);
>         if (IS_ERR(regmap)) {
> @@ -974,13 +1031,13 @@ static int pcf2127_spi_probe(struct spi_device *spi)
>
>         return pcf2127_probe(&spi->dev, regmap, spi->irq,
>                              pcf2127_spi_driver.driver.name,
> -                            spi_get_device_id(spi)->driver_data);
> +                            variant);
>  }
>
>  static const struct spi_device_id pcf2127_spi_id[] = {
> -       { "pcf2127", 1 },
> -       { "pcf2129", 0 },
> -       { "pca2129", 0 },
> +       { "pcf2127", PCF2127 },
> +       { "pcf2129", PCF2129 },
> +       { "pca2129", PCF2129 },
>         { }
>  };
>  MODULE_DEVICE_TABLE(spi, pcf2127_spi_id);
> --
> 2.30.2
>

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

* Re: [PATCH v3 14/14] dt-bindings: rtc: pcf2127: add PCF2131
  2022-12-15 15:02 ` [PATCH v3 14/14] dt-bindings: rtc: pcf2127: add PCF2131 Hugo Villeneuve
  2022-12-16 13:24   ` Krzysztof Kozlowski
@ 2022-12-19  9:14   ` Bruno Thomsen
  2022-12-19 16:25     ` Hugo Villeneuve
  1 sibling, 1 reply; 72+ messages in thread
From: Bruno Thomsen @ 2022-12-19  9:14 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt,
	linux-rtc, devicetree, linux-kernel, Hugo Villeneuve

Den tor. 15. dec. 2022 kl. 16.19 skrev Hugo Villeneuve <hugo@hugovil.com>:
>
> From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
>
> Add support for new NXP RTC PCF2131.
>
> Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> ---
>  Documentation/devicetree/bindings/rtc/nxp,pcf2127.yaml | 4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/devicetree/bindings/rtc/nxp,pcf2127.yaml b/Documentation/devicetree/bindings/rtc/nxp,pcf2127.yaml
> index cde7b1675ead..a8f8c23da4d8 100644
> --- a/Documentation/devicetree/bindings/rtc/nxp,pcf2127.yaml
> +++ b/Documentation/devicetree/bindings/rtc/nxp,pcf2127.yaml
> @@ -14,7 +14,9 @@ maintainers:
>
>  properties:
>    compatible:
> -    const: nxp,pcf2127
> +    enum:
> +      - nxp,pcf2127
> +      - nxp,pcf2131

The enum is incomplete as pcf2127_of_match struct also contains:
nxp,pcf2129
ncp,pca2129

/Bruno

>
>    reg:
>      maxItems: 1
> --
> 2.30.2
>

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

* Re: [PATCH v3 02/14] rtc: pcf2127: adapt for time/date registers at any offset
  2022-12-15 15:02 ` [PATCH v3 02/14] rtc: pcf2127: adapt for time/date registers at any offset Hugo Villeneuve
@ 2022-12-19  9:34   ` Bruno Thomsen
  2022-12-19 16:27     ` Hugo Villeneuve
  2023-01-07 16:49     ` Bruno Thomsen
  2023-01-20 18:47   ` Alexandre Belloni
  1 sibling, 2 replies; 72+ messages in thread
From: Bruno Thomsen @ 2022-12-19  9:34 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt,
	linux-rtc, devicetree, linux-kernel, Hugo Villeneuve

Den tor. 15. dec. 2022 kl. 16.20 skrev Hugo Villeneuve <hugo@hugovil.com>:
>
> From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
>
> This will simplify the implementation of new variants into this driver.
>
> Some variants (PCF2131) have a 100th seconds register. This register is
> currently not supported in this driver.
>
> Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> ---
>  drivers/rtc/rtc-pcf2127.c | 68 ++++++++++++++++++++++-----------------
>  1 file changed, 39 insertions(+), 29 deletions(-)
>
> diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> index b9a5d47a439f..fb0caacaabee 100644
> --- a/drivers/rtc/rtc-pcf2127.c
> +++ b/drivers/rtc/rtc-pcf2127.c
> @@ -44,14 +44,17 @@
>  #define PCF2127_BIT_CTRL3_BF                   BIT(3)
>  #define PCF2127_BIT_CTRL3_BTSE                 BIT(4)
>  /* Time and date registers */
> -#define PCF2127_REG_SC                 0x03
> +#define PCF2127_REG_TIME_DATE_BASE     0x03
> +/* Time and date registers offsets (starting from base register) */
> +#define PCF2127_OFFSET_TD_SC           0
> +#define PCF2127_OFFSET_TD_MN           1
> +#define PCF2127_OFFSET_TD_HR           2
> +#define PCF2127_OFFSET_TD_DM           3
> +#define PCF2127_OFFSET_TD_DW           4
> +#define PCF2127_OFFSET_TD_MO           5
> +#define PCF2127_OFFSET_TD_YR           6
> +/* Time and date registers bits */
>  #define PCF2127_BIT_SC_OSF                     BIT(7)
> -#define PCF2127_REG_MN                 0x04
> -#define PCF2127_REG_HR                 0x05
> -#define PCF2127_REG_DM                 0x06
> -#define PCF2127_REG_DW                 0x07
> -#define PCF2127_REG_MO                 0x08
> -#define PCF2127_REG_YR                 0x09
>  /* Alarm registers */
>  #define PCF2127_REG_ALARM_SC           0x0A
>  #define PCF2127_REG_ALARM_MN           0x0B
> @@ -106,6 +109,7 @@ struct pcf21xx_config {
>         int max_register;
>         unsigned int has_nvmem:1;
>         unsigned int has_bit_wd_ctl_cd0:1;
> +       u8 regs_td_base; /* Time/data base registers. */

There is only one base register, so no need to add s.
I think td is an odd short, so maybe u8 reg_time_base.

/Bruno

>  };
>
>  struct pcf2127 {
> @@ -125,27 +129,31 @@ struct pcf2127 {
>  static int pcf2127_rtc_read_time(struct device *dev, struct rtc_time *tm)
>  {
>         struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
> -       unsigned char buf[10];
> +       unsigned char buf[7];
> +       unsigned int ctrl3;
>         int ret;
>
>         /*
>          * Avoid reading CTRL2 register as it causes WD_VAL register
>          * value to reset to 0 which means watchdog is stopped.
>          */
> -       ret = regmap_bulk_read(pcf2127->regmap, PCF2127_REG_CTRL3,
> -                              (buf + PCF2127_REG_CTRL3),
> -                              ARRAY_SIZE(buf) - PCF2127_REG_CTRL3);
> -       if (ret) {
> -               dev_err(dev, "%s: read error\n", __func__);
> +       ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL3, &ctrl3);
> +       if (ret)
>                 return ret;
> -       }
>
> -       if (buf[PCF2127_REG_CTRL3] & PCF2127_BIT_CTRL3_BLF)
> +       if (ctrl3 & PCF2127_BIT_CTRL3_BLF)
>                 dev_info(dev,
>                         "low voltage detected, check/replace RTC battery.\n");
>
> +       ret = regmap_bulk_read(pcf2127->regmap, pcf2127->cfg->regs_td_base,
> +                              buf, sizeof(buf));
> +       if (ret) {
> +               dev_err(dev, "%s: read error\n", __func__);
> +               return ret;
> +       }
> +
>         /* Clock integrity is not guaranteed when OSF flag is set. */
> -       if (buf[PCF2127_REG_SC] & PCF2127_BIT_SC_OSF) {
> +       if (buf[PCF2127_OFFSET_TD_SC] & PCF2127_BIT_SC_OSF) {
>                 /*
>                  * no need clear the flag here,
>                  * it will be cleared once the new date is saved
> @@ -158,18 +166,18 @@ static int pcf2127_rtc_read_time(struct device *dev, struct rtc_time *tm)
>         dev_dbg(dev,
>                 "%s: raw data is cr3=%02x, sec=%02x, min=%02x, hr=%02x, "
>                 "mday=%02x, wday=%02x, mon=%02x, year=%02x\n",
> -               __func__, buf[PCF2127_REG_CTRL3], buf[PCF2127_REG_SC],
> -               buf[PCF2127_REG_MN], buf[PCF2127_REG_HR],
> -               buf[PCF2127_REG_DM], buf[PCF2127_REG_DW],
> -               buf[PCF2127_REG_MO], buf[PCF2127_REG_YR]);
> -
> -       tm->tm_sec = bcd2bin(buf[PCF2127_REG_SC] & 0x7F);
> -       tm->tm_min = bcd2bin(buf[PCF2127_REG_MN] & 0x7F);
> -       tm->tm_hour = bcd2bin(buf[PCF2127_REG_HR] & 0x3F); /* rtc hr 0-23 */
> -       tm->tm_mday = bcd2bin(buf[PCF2127_REG_DM] & 0x3F);
> -       tm->tm_wday = buf[PCF2127_REG_DW] & 0x07;
> -       tm->tm_mon = bcd2bin(buf[PCF2127_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */
> -       tm->tm_year = bcd2bin(buf[PCF2127_REG_YR]);
> +               __func__, ctrl3, buf[PCF2127_OFFSET_TD_SC],
> +               buf[PCF2127_OFFSET_TD_MN], buf[PCF2127_OFFSET_TD_HR],
> +               buf[PCF2127_OFFSET_TD_DM], buf[PCF2127_OFFSET_TD_DW],
> +               buf[PCF2127_OFFSET_TD_MO], buf[PCF2127_OFFSET_TD_YR]);
> +
> +       tm->tm_sec = bcd2bin(buf[PCF2127_OFFSET_TD_SC] & 0x7F);
> +       tm->tm_min = bcd2bin(buf[PCF2127_OFFSET_TD_MN] & 0x7F);
> +       tm->tm_hour = bcd2bin(buf[PCF2127_OFFSET_TD_HR] & 0x3F); /* rtc hr 0-23 */
> +       tm->tm_mday = bcd2bin(buf[PCF2127_OFFSET_TD_DM] & 0x3F);
> +       tm->tm_wday = buf[PCF2127_OFFSET_TD_DW] & 0x07;
> +       tm->tm_mon = bcd2bin(buf[PCF2127_OFFSET_TD_MO] & 0x1F) - 1; /* rtc mn 1-12 */
> +       tm->tm_year = bcd2bin(buf[PCF2127_OFFSET_TD_YR]);
>         tm->tm_year += 100;
>
>         dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
> @@ -207,7 +215,7 @@ static int pcf2127_rtc_set_time(struct device *dev, struct rtc_time *tm)
>         buf[i++] = bin2bcd(tm->tm_year - 100);
>
>         /* write register's data */
> -       err = regmap_bulk_write(pcf2127->regmap, PCF2127_REG_SC, buf, i);
> +       err = regmap_bulk_write(pcf2127->regmap, pcf2127->cfg->regs_td_base, buf, i);
>         if (err) {
>                 dev_err(dev,
>                         "%s: err=%d", __func__, err);
> @@ -650,11 +658,13 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
>                 .max_register = 0x1d,
>                 .has_nvmem = 1,
>                 .has_bit_wd_ctl_cd0 = 1,
> +               .regs_td_base = PCF2127_REG_TIME_DATE_BASE,
>         },
>         [PCF2129] = {
>                 .max_register = 0x19,
>                 .has_nvmem = 0,
>                 .has_bit_wd_ctl_cd0 = 0,
> +               .regs_td_base = PCF2127_REG_TIME_DATE_BASE,
>         },
>  };
>
> --
> 2.30.2
>

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

* Re: [PATCH v3 01/14] rtc: pcf2127: add variant-specific configuration structure
  2022-12-19  9:05   ` Bruno Thomsen
@ 2022-12-19 15:15     ` Hugo Villeneuve
  2022-12-19 17:17       ` Bruno Thomsen
  0 siblings, 1 reply; 72+ messages in thread
From: Hugo Villeneuve @ 2022-12-19 15:15 UTC (permalink / raw)
  To: Bruno Thomsen
  Cc: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt,
	linux-rtc, devicetree, linux-kernel, Hugo Villeneuve, hugo

On Mon, 19 Dec 2022 10:05:53 +0100
Bruno Thomsen <bruno.thomsen@gmail.com> wrote:

> Den tor. 15. dec. 2022 kl. 16.19 skrev Hugo Villeneuve <hugo@hugovil.com>:
> >
> > From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> >
> > Create variant-specific configuration structures to simplify the
> > implementation of new variants into this driver. It will also avoid
> > to have too many tests for a specific variant, or a list of variants
> > for new devices, inside the code itself.
> >
> > Add configuration options for the support of the NVMEM, bit CD0 in
> > register WD_CTL as well as the maximum number of registers for each
> > variant, instead of hardcoding the variant (PCF2127) inside the
> > i2c_device_id and spi_device_id structures.
> >
> > Also specify a different maximum number of registers (max_register)
> > for the PCF2129.
> >
> > Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > ---
> >  drivers/rtc/rtc-pcf2127.c | 95 +++++++++++++++++++++++++++++++--------
> >  1 file changed, 76 insertions(+), 19 deletions(-)
> >
> > diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> > index 87f4fc9df68b..b9a5d47a439f 100644
> > --- a/drivers/rtc/rtc-pcf2127.c
> > +++ b/drivers/rtc/rtc-pcf2127.c
> > @@ -21,6 +21,7 @@
> >  #include <linux/module.h>
> >  #include <linux/of.h>
> >  #include <linux/of_irq.h>
> > +#include <linux/of_device.h>
> >  #include <linux/regmap.h>
> >  #include <linux/watchdog.h>
> >
> > @@ -101,10 +102,17 @@
> >                 PCF2127_BIT_CTRL2_WDTF | \
> >                 PCF2127_BIT_CTRL2_TSF2)
> >
> > +struct pcf21xx_config {
> > +       int max_register;
> > +       unsigned int has_nvmem:1;
> > +       unsigned int has_bit_wd_ctl_cd0:1;
> > +};
> > +
> >  struct pcf2127 {
> >         struct rtc_device *rtc;
> >         struct watchdog_device wdd;
> >         struct regmap *regmap;
> > +       const struct pcf21xx_config *cfg;
> >         time64_t ts;
> >         bool ts_valid;
> >         bool irq_enabled;
> > @@ -631,8 +639,27 @@ static const struct attribute_group pcf2127_attr_group = {
> >         .attrs  = pcf2127_attrs,
> >  };
> >
> > +enum pcf21xx_type {
> > +       PCF2127,
> > +       PCF2129,
> > +       PCF21XX_LAST_ID
> > +};
> > +
> > +static struct pcf21xx_config pcf21xx_cfg[] = {
> > +       [PCF2127] = {
> > +               .max_register = 0x1d,
> > +               .has_nvmem = 1,
> > +               .has_bit_wd_ctl_cd0 = 1,
> > +       },
> > +       [PCF2129] = {
> > +               .max_register = 0x19,
> > +               .has_nvmem = 0,
> > +               .has_bit_wd_ctl_cd0 = 0,
> > +       },
> > +};
> > +
> >  static int pcf2127_probe(struct device *dev, struct regmap *regmap,
> > -                        int alarm_irq, const char *name, bool is_pcf2127)
> > +                        int alarm_irq, const char *name, const struct pcf21xx_config *config)
> >  {
> >         struct pcf2127 *pcf2127;
> >         int ret = 0;
> > @@ -645,6 +672,7 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
> >                 return -ENOMEM;
> >
> >         pcf2127->regmap = regmap;
> > +       pcf2127->cfg = config;
> >
> >         dev_set_drvdata(dev, pcf2127);
> >
> > @@ -688,7 +716,7 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
> >                 set_bit(RTC_FEATURE_ALARM, pcf2127->rtc->features);
> >         }
> >
> > -       if (is_pcf2127) {
> > +       if (pcf2127->cfg->has_nvmem) {
> >                 struct nvmem_config nvmem_cfg = {
> >                         .priv = pcf2127,
> >                         .reg_read = pcf2127_nvmem_read,
> > @@ -734,7 +762,7 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
> >                                  PCF2127_BIT_WD_CTL_TF1 |
> >                                  PCF2127_BIT_WD_CTL_TF0,
> >                                  PCF2127_BIT_WD_CTL_CD1 |
> > -                                (is_pcf2127 ? PCF2127_BIT_WD_CTL_CD0 : 0) |
> > +                                (pcf2127->cfg->has_bit_wd_ctl_cd0 ? PCF2127_BIT_WD_CTL_CD0 : 0) |
> >                                  PCF2127_BIT_WD_CTL_TF1);
> >         if (ret) {
> >                 dev_err(dev, "%s: watchdog config (wd_ctl) failed\n", __func__);
> > @@ -799,9 +827,9 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
> >
> >  #ifdef CONFIG_OF
> >  static const struct of_device_id pcf2127_of_match[] = {
> > -       { .compatible = "nxp,pcf2127" },
> > -       { .compatible = "nxp,pcf2129" },
> > -       { .compatible = "nxp,pca2129" },
> > +       { .compatible = "nxp,pcf2127", .data = &pcf21xx_cfg[PCF2127] },
> > +       { .compatible = "nxp,pcf2129", .data = &pcf21xx_cfg[PCF2129] },
> > +       { .compatible = "nxp,pca2129", .data = &pcf21xx_cfg[PCF2129] },
> >         {}
> >  };
> >  MODULE_DEVICE_TABLE(of, pcf2127_of_match);
> > @@ -886,26 +914,40 @@ static const struct regmap_bus pcf2127_i2c_regmap = {
> >  static struct i2c_driver pcf2127_i2c_driver;
> >
> >  static const struct i2c_device_id pcf2127_i2c_id[] = {
> > -       { "pcf2127", 1 },
> > -       { "pcf2129", 0 },
> > -       { "pca2129", 0 },
> > +       { "pcf2127", PCF2127 },
> > +       { "pcf2129", PCF2129 },
> > +       { "pca2129", PCF2129 },
> >         { }
> >  };
> >  MODULE_DEVICE_TABLE(i2c, pcf2127_i2c_id);
> >
> >  static int pcf2127_i2c_probe(struct i2c_client *client)
> >  {
> > -       const struct i2c_device_id *id = i2c_match_id(pcf2127_i2c_id, client);
> >         struct regmap *regmap;
> > -       static const struct regmap_config config = {
> > +       static struct regmap_config config = {
> >                 .reg_bits = 8,
> >                 .val_bits = 8,
> > -               .max_register = 0x1d,
> >         };
> > +       const struct pcf21xx_config *variant;
> 
> Hi Hugo,
> 
> Patch series does not apply on 6.1 tree as pcf2127_i2c_probe() call
> signature does not match[1].
> 
> static int pcf2127_i2c_probe(struct i2c_client *client,
>       const struct i2c_device_id *id)
> 
> 
> [1] https://elixir.bootlin.com/linux/v6.1/source/drivers/rtc/rtc-pcf2127.c#L888
> 
> /Bruno

Hi Bruno,
I based my driver on the git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux.git repo, as indicated in the MAINTAINERS file for the RTC subsystem (T: entry). I used the rtc-next branch on this repo.

Can you tell me exactly which repo and branch I need to use to resubmit the driver?

Thank you, Hugo.


> >         if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
> >                 return -ENODEV;
> >
> > +       if (client->dev.of_node) {
> > +               variant = of_device_get_match_data(&client->dev);
> > +               if (!variant)
> > +                       return -ENODEV;
> > +       } else {
> > +               enum pcf21xx_type type =
> > +                       i2c_match_id(pcf2127_i2c_id, client)->driver_data;
> > +
> > +               if (type >= PCF21XX_LAST_ID)
> > +                       return -ENODEV;
> > +               variant = &pcf21xx_cfg[type];
> > +       }
> > +
> > +       config.max_register = variant->max_register,
> > +
> >         regmap = devm_regmap_init(&client->dev, &pcf2127_i2c_regmap,
> >                                         &client->dev, &config);
> >         if (IS_ERR(regmap)) {
> > @@ -915,7 +957,7 @@ static int pcf2127_i2c_probe(struct i2c_client *client)
> >         }
> >
> >         return pcf2127_probe(&client->dev, regmap, client->irq,
> > -                            pcf2127_i2c_driver.driver.name, id->driver_data);
> > +                            pcf2127_i2c_driver.driver.name, variant);
> >  }
> >
> >  static struct i2c_driver pcf2127_i2c_driver = {
> > @@ -953,17 +995,32 @@ static void pcf2127_i2c_unregister_driver(void)
> >  #if IS_ENABLED(CONFIG_SPI_MASTER)
> >
> >  static struct spi_driver pcf2127_spi_driver;
> > +static const struct spi_device_id pcf2127_spi_id[];
> >
> >  static int pcf2127_spi_probe(struct spi_device *spi)
> >  {
> > -       static const struct regmap_config config = {
> > +       static struct regmap_config config = {
> >                 .reg_bits = 8,
> >                 .val_bits = 8,
> >                 .read_flag_mask = 0xa0,
> >                 .write_flag_mask = 0x20,
> > -               .max_register = 0x1d,
> >         };
> >         struct regmap *regmap;
> > +       const struct pcf21xx_config *variant;
> > +
> > +       if (spi->dev.of_node) {
> > +               variant = of_device_get_match_data(&spi->dev);
> > +               if (!variant)
> > +                       return -ENODEV;
> > +       } else {
> > +               enum pcf21xx_type type = spi_get_device_id(spi)->driver_data;
> > +
> > +               if (type >= PCF21XX_LAST_ID)
> > +                       return -ENODEV;
> > +               variant = &pcf21xx_cfg[type];
> > +       }
> > +
> > +       config.max_register = variant->max_register,
> >
> >         regmap = devm_regmap_init_spi(spi, &config);
> >         if (IS_ERR(regmap)) {
> > @@ -974,13 +1031,13 @@ static int pcf2127_spi_probe(struct spi_device *spi)
> >
> >         return pcf2127_probe(&spi->dev, regmap, spi->irq,
> >                              pcf2127_spi_driver.driver.name,
> > -                            spi_get_device_id(spi)->driver_data);
> > +                            variant);
> >  }
> >
> >  static const struct spi_device_id pcf2127_spi_id[] = {
> > -       { "pcf2127", 1 },
> > -       { "pcf2129", 0 },
> > -       { "pca2129", 0 },
> > +       { "pcf2127", PCF2127 },
> > +       { "pcf2129", PCF2129 },
> > +       { "pca2129", PCF2129 },
> >         { }
> >  };
> >  MODULE_DEVICE_TABLE(spi, pcf2127_spi_id);
> > --
> > 2.30.2
> >
> 


-- 
Hugo Villeneuve <hugo@hugovil.com>

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

* Re: [PATCH v3 14/14] dt-bindings: rtc: pcf2127: add PCF2131
  2022-12-19  9:14   ` Bruno Thomsen
@ 2022-12-19 16:25     ` Hugo Villeneuve
  2022-12-19 17:18       ` Bruno Thomsen
  0 siblings, 1 reply; 72+ messages in thread
From: Hugo Villeneuve @ 2022-12-19 16:25 UTC (permalink / raw)
  To: Bruno Thomsen
  Cc: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt,
	linux-rtc, devicetree, linux-kernel, Hugo Villeneuve

On Mon, 19 Dec 2022 10:14:10 +0100
Bruno Thomsen <bruno.thomsen@gmail.com> wrote:

> Den tor. 15. dec. 2022 kl. 16.19 skrev Hugo Villeneuve <hugo@hugovil.com>:
> >
> > From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> >
> > Add support for new NXP RTC PCF2131.
> >
> > Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > ---
> >  Documentation/devicetree/bindings/rtc/nxp,pcf2127.yaml | 4 +++-
> >  1 file changed, 3 insertions(+), 1 deletion(-)
> >
> > diff --git a/Documentation/devicetree/bindings/rtc/nxp,pcf2127.yaml b/Documentation/devicetree/bindings/rtc/nxp,pcf2127.yaml
> > index cde7b1675ead..a8f8c23da4d8 100644
> > --- a/Documentation/devicetree/bindings/rtc/nxp,pcf2127.yaml
> > +++ b/Documentation/devicetree/bindings/rtc/nxp,pcf2127.yaml
> > @@ -14,7 +14,9 @@ maintainers:
> >
> >  properties:
> >    compatible:
> > -    const: nxp,pcf2127
> > +    enum:
> > +      - nxp,pcf2127
> > +      - nxp,pcf2131
> 
> The enum is incomplete as pcf2127_of_match struct also contains:
> nxp,pcf2129
> ncp,pca2129
> 
> /Bruno

Hi,
if I understand correctly, this means that the pca2129 and pcf2129 entries are already missing and should be added in a fix or a patch outside the scope of my new driver...

Hugo.


> >    reg:
> >      maxItems: 1
> > --
> > 2.30.2
> >
> 


-- 
Hugo Villeneuve <hugo@hugovil.com>

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

* Re: [PATCH v3 02/14] rtc: pcf2127: adapt for time/date registers at any offset
  2022-12-19  9:34   ` Bruno Thomsen
@ 2022-12-19 16:27     ` Hugo Villeneuve
  2023-01-07 16:49     ` Bruno Thomsen
  1 sibling, 0 replies; 72+ messages in thread
From: Hugo Villeneuve @ 2022-12-19 16:27 UTC (permalink / raw)
  To: Bruno Thomsen
  Cc: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt,
	linux-rtc, devicetree, linux-kernel, Hugo Villeneuve

On Mon, 19 Dec 2022 10:34:08 +0100
Bruno Thomsen <bruno.thomsen@gmail.com> wrote:

> Den tor. 15. dec. 2022 kl. 16.20 skrev Hugo Villeneuve <hugo@hugovil.com>:
> >
> > From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> >
> > This will simplify the implementation of new variants into this driver.
> >
> > Some variants (PCF2131) have a 100th seconds register. This register is
> > currently not supported in this driver.
> >
> > Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > ---
> >  drivers/rtc/rtc-pcf2127.c | 68 ++++++++++++++++++++++-----------------
> >  1 file changed, 39 insertions(+), 29 deletions(-)
> >
> > diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> > index b9a5d47a439f..fb0caacaabee 100644
> > --- a/drivers/rtc/rtc-pcf2127.c
> > +++ b/drivers/rtc/rtc-pcf2127.c
> > @@ -44,14 +44,17 @@
> >  #define PCF2127_BIT_CTRL3_BF                   BIT(3)
> >  #define PCF2127_BIT_CTRL3_BTSE                 BIT(4)
> >  /* Time and date registers */
> > -#define PCF2127_REG_SC                 0x03
> > +#define PCF2127_REG_TIME_DATE_BASE     0x03
> > +/* Time and date registers offsets (starting from base register) */
> > +#define PCF2127_OFFSET_TD_SC           0
> > +#define PCF2127_OFFSET_TD_MN           1
> > +#define PCF2127_OFFSET_TD_HR           2
> > +#define PCF2127_OFFSET_TD_DM           3
> > +#define PCF2127_OFFSET_TD_DW           4
> > +#define PCF2127_OFFSET_TD_MO           5
> > +#define PCF2127_OFFSET_TD_YR           6
> > +/* Time and date registers bits */
> >  #define PCF2127_BIT_SC_OSF                     BIT(7)
> > -#define PCF2127_REG_MN                 0x04
> > -#define PCF2127_REG_HR                 0x05
> > -#define PCF2127_REG_DM                 0x06
> > -#define PCF2127_REG_DW                 0x07
> > -#define PCF2127_REG_MO                 0x08
> > -#define PCF2127_REG_YR                 0x09
> >  /* Alarm registers */
> >  #define PCF2127_REG_ALARM_SC           0x0A
> >  #define PCF2127_REG_ALARM_MN           0x0B
> > @@ -106,6 +109,7 @@ struct pcf21xx_config {
> >         int max_register;
> >         unsigned int has_nvmem:1;
> >         unsigned int has_bit_wd_ctl_cd0:1;
> > +       u8 regs_td_base; /* Time/data base registers. */
> 
> There is only one base register, so no need to add s.
> I think td is an odd short, so maybe u8 reg_time_base.
> 
> /Bruno

Hi,
it makes sense, I will fix it in V4.

I will also fix the comment "Time/data" -> "Time/date".

Hugo.


> >  };
> >
> >  struct pcf2127 {
> > @@ -125,27 +129,31 @@ struct pcf2127 {
> >  static int pcf2127_rtc_read_time(struct device *dev, struct rtc_time *tm)
> >  {
> >         struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
> > -       unsigned char buf[10];
> > +       unsigned char buf[7];
> > +       unsigned int ctrl3;
> >         int ret;
> >
> >         /*
> >          * Avoid reading CTRL2 register as it causes WD_VAL register
> >          * value to reset to 0 which means watchdog is stopped.
> >          */
> > -       ret = regmap_bulk_read(pcf2127->regmap, PCF2127_REG_CTRL3,
> > -                              (buf + PCF2127_REG_CTRL3),
> > -                              ARRAY_SIZE(buf) - PCF2127_REG_CTRL3);
> > -       if (ret) {
> > -               dev_err(dev, "%s: read error\n", __func__);
> > +       ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL3, &ctrl3);
> > +       if (ret)
> >                 return ret;
> > -       }
> >
> > -       if (buf[PCF2127_REG_CTRL3] & PCF2127_BIT_CTRL3_BLF)
> > +       if (ctrl3 & PCF2127_BIT_CTRL3_BLF)
> >                 dev_info(dev,
> >                         "low voltage detected, check/replace RTC battery.\n");
> >
> > +       ret = regmap_bulk_read(pcf2127->regmap, pcf2127->cfg->regs_td_base,
> > +                              buf, sizeof(buf));
> > +       if (ret) {
> > +               dev_err(dev, "%s: read error\n", __func__);
> > +               return ret;
> > +       }
> > +
> >         /* Clock integrity is not guaranteed when OSF flag is set. */
> > -       if (buf[PCF2127_REG_SC] & PCF2127_BIT_SC_OSF) {
> > +       if (buf[PCF2127_OFFSET_TD_SC] & PCF2127_BIT_SC_OSF) {
> >                 /*
> >                  * no need clear the flag here,
> >                  * it will be cleared once the new date is saved
> > @@ -158,18 +166,18 @@ static int pcf2127_rtc_read_time(struct device *dev, struct rtc_time *tm)
> >         dev_dbg(dev,
> >                 "%s: raw data is cr3=%02x, sec=%02x, min=%02x, hr=%02x, "
> >                 "mday=%02x, wday=%02x, mon=%02x, year=%02x\n",
> > -               __func__, buf[PCF2127_REG_CTRL3], buf[PCF2127_REG_SC],
> > -               buf[PCF2127_REG_MN], buf[PCF2127_REG_HR],
> > -               buf[PCF2127_REG_DM], buf[PCF2127_REG_DW],
> > -               buf[PCF2127_REG_MO], buf[PCF2127_REG_YR]);
> > -
> > -       tm->tm_sec = bcd2bin(buf[PCF2127_REG_SC] & 0x7F);
> > -       tm->tm_min = bcd2bin(buf[PCF2127_REG_MN] & 0x7F);
> > -       tm->tm_hour = bcd2bin(buf[PCF2127_REG_HR] & 0x3F); /* rtc hr 0-23 */
> > -       tm->tm_mday = bcd2bin(buf[PCF2127_REG_DM] & 0x3F);
> > -       tm->tm_wday = buf[PCF2127_REG_DW] & 0x07;
> > -       tm->tm_mon = bcd2bin(buf[PCF2127_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */
> > -       tm->tm_year = bcd2bin(buf[PCF2127_REG_YR]);
> > +               __func__, ctrl3, buf[PCF2127_OFFSET_TD_SC],
> > +               buf[PCF2127_OFFSET_TD_MN], buf[PCF2127_OFFSET_TD_HR],
> > +               buf[PCF2127_OFFSET_TD_DM], buf[PCF2127_OFFSET_TD_DW],
> > +               buf[PCF2127_OFFSET_TD_MO], buf[PCF2127_OFFSET_TD_YR]);
> > +
> > +       tm->tm_sec = bcd2bin(buf[PCF2127_OFFSET_TD_SC] & 0x7F);
> > +       tm->tm_min = bcd2bin(buf[PCF2127_OFFSET_TD_MN] & 0x7F);
> > +       tm->tm_hour = bcd2bin(buf[PCF2127_OFFSET_TD_HR] & 0x3F); /* rtc hr 0-23 */
> > +       tm->tm_mday = bcd2bin(buf[PCF2127_OFFSET_TD_DM] & 0x3F);
> > +       tm->tm_wday = buf[PCF2127_OFFSET_TD_DW] & 0x07;
> > +       tm->tm_mon = bcd2bin(buf[PCF2127_OFFSET_TD_MO] & 0x1F) - 1; /* rtc mn 1-12 */
> > +       tm->tm_year = bcd2bin(buf[PCF2127_OFFSET_TD_YR]);
> >         tm->tm_year += 100;
> >
> >         dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
> > @@ -207,7 +215,7 @@ static int pcf2127_rtc_set_time(struct device *dev, struct rtc_time *tm)
> >         buf[i++] = bin2bcd(tm->tm_year - 100);
> >
> >         /* write register's data */
> > -       err = regmap_bulk_write(pcf2127->regmap, PCF2127_REG_SC, buf, i);
> > +       err = regmap_bulk_write(pcf2127->regmap, pcf2127->cfg->regs_td_base, buf, i);
> >         if (err) {
> >                 dev_err(dev,
> >                         "%s: err=%d", __func__, err);
> > @@ -650,11 +658,13 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
> >                 .max_register = 0x1d,
> >                 .has_nvmem = 1,
> >                 .has_bit_wd_ctl_cd0 = 1,
> > +               .regs_td_base = PCF2127_REG_TIME_DATE_BASE,
> >         },
> >         [PCF2129] = {
> >                 .max_register = 0x19,
> >                 .has_nvmem = 0,
> >                 .has_bit_wd_ctl_cd0 = 0,
> > +               .regs_td_base = PCF2127_REG_TIME_DATE_BASE,
> >         },
> >  };
> >
> > --
> > 2.30.2
> >
> 


-- 
Hugo Villeneuve <hugo@hugovil.com>

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

* Re: [PATCH v3 01/14] rtc: pcf2127: add variant-specific configuration structure
  2022-12-19 15:15     ` Hugo Villeneuve
@ 2022-12-19 17:17       ` Bruno Thomsen
  2022-12-19 18:30         ` Hugo Villeneuve
  0 siblings, 1 reply; 72+ messages in thread
From: Bruno Thomsen @ 2022-12-19 17:17 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt,
	linux-rtc, devicetree, linux-kernel, Hugo Villeneuve

Hi Hugo,

Looking more into it I think you based it against the right tree,
but if you use --base= when doing format-patch it's easier to spot :)
I only use torvalds tree when testing patches on a device.

/Bruno

Den man. 19. dec. 2022 kl. 16.15 skrev Hugo Villeneuve <hugo@hugovil.com>:
>
> On Mon, 19 Dec 2022 10:05:53 +0100
> Bruno Thomsen <bruno.thomsen@gmail.com> wrote:
>
> > Den tor. 15. dec. 2022 kl. 16.19 skrev Hugo Villeneuve <hugo@hugovil.com>:
> > >
> > > From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > >
> > > Create variant-specific configuration structures to simplify the
> > > implementation of new variants into this driver. It will also avoid
> > > to have too many tests for a specific variant, or a list of variants
> > > for new devices, inside the code itself.
> > >
> > > Add configuration options for the support of the NVMEM, bit CD0 in
> > > register WD_CTL as well as the maximum number of registers for each
> > > variant, instead of hardcoding the variant (PCF2127) inside the
> > > i2c_device_id and spi_device_id structures.
> > >
> > > Also specify a different maximum number of registers (max_register)
> > > for the PCF2129.
> > >
> > > Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > > ---
> > >  drivers/rtc/rtc-pcf2127.c | 95 +++++++++++++++++++++++++++++++--------
> > >  1 file changed, 76 insertions(+), 19 deletions(-)
> > >
> > > diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> > > index 87f4fc9df68b..b9a5d47a439f 100644
> > > --- a/drivers/rtc/rtc-pcf2127.c
> > > +++ b/drivers/rtc/rtc-pcf2127.c
> > > @@ -21,6 +21,7 @@
> > >  #include <linux/module.h>
> > >  #include <linux/of.h>
> > >  #include <linux/of_irq.h>
> > > +#include <linux/of_device.h>
> > >  #include <linux/regmap.h>
> > >  #include <linux/watchdog.h>
> > >
> > > @@ -101,10 +102,17 @@
> > >                 PCF2127_BIT_CTRL2_WDTF | \
> > >                 PCF2127_BIT_CTRL2_TSF2)
> > >
> > > +struct pcf21xx_config {
> > > +       int max_register;
> > > +       unsigned int has_nvmem:1;
> > > +       unsigned int has_bit_wd_ctl_cd0:1;
> > > +};
> > > +
> > >  struct pcf2127 {
> > >         struct rtc_device *rtc;
> > >         struct watchdog_device wdd;
> > >         struct regmap *regmap;
> > > +       const struct pcf21xx_config *cfg;
> > >         time64_t ts;
> > >         bool ts_valid;
> > >         bool irq_enabled;
> > > @@ -631,8 +639,27 @@ static const struct attribute_group pcf2127_attr_group = {
> > >         .attrs  = pcf2127_attrs,
> > >  };
> > >
> > > +enum pcf21xx_type {
> > > +       PCF2127,
> > > +       PCF2129,
> > > +       PCF21XX_LAST_ID
> > > +};
> > > +
> > > +static struct pcf21xx_config pcf21xx_cfg[] = {
> > > +       [PCF2127] = {
> > > +               .max_register = 0x1d,
> > > +               .has_nvmem = 1,
> > > +               .has_bit_wd_ctl_cd0 = 1,
> > > +       },
> > > +       [PCF2129] = {
> > > +               .max_register = 0x19,
> > > +               .has_nvmem = 0,
> > > +               .has_bit_wd_ctl_cd0 = 0,
> > > +       },
> > > +};
> > > +
> > >  static int pcf2127_probe(struct device *dev, struct regmap *regmap,
> > > -                        int alarm_irq, const char *name, bool is_pcf2127)
> > > +                        int alarm_irq, const char *name, const struct pcf21xx_config *config)
> > >  {
> > >         struct pcf2127 *pcf2127;
> > >         int ret = 0;
> > > @@ -645,6 +672,7 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
> > >                 return -ENOMEM;
> > >
> > >         pcf2127->regmap = regmap;
> > > +       pcf2127->cfg = config;
> > >
> > >         dev_set_drvdata(dev, pcf2127);
> > >
> > > @@ -688,7 +716,7 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
> > >                 set_bit(RTC_FEATURE_ALARM, pcf2127->rtc->features);
> > >         }
> > >
> > > -       if (is_pcf2127) {
> > > +       if (pcf2127->cfg->has_nvmem) {
> > >                 struct nvmem_config nvmem_cfg = {
> > >                         .priv = pcf2127,
> > >                         .reg_read = pcf2127_nvmem_read,
> > > @@ -734,7 +762,7 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
> > >                                  PCF2127_BIT_WD_CTL_TF1 |
> > >                                  PCF2127_BIT_WD_CTL_TF0,
> > >                                  PCF2127_BIT_WD_CTL_CD1 |
> > > -                                (is_pcf2127 ? PCF2127_BIT_WD_CTL_CD0 : 0) |
> > > +                                (pcf2127->cfg->has_bit_wd_ctl_cd0 ? PCF2127_BIT_WD_CTL_CD0 : 0) |
> > >                                  PCF2127_BIT_WD_CTL_TF1);
> > >         if (ret) {
> > >                 dev_err(dev, "%s: watchdog config (wd_ctl) failed\n", __func__);
> > > @@ -799,9 +827,9 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
> > >
> > >  #ifdef CONFIG_OF
> > >  static const struct of_device_id pcf2127_of_match[] = {
> > > -       { .compatible = "nxp,pcf2127" },
> > > -       { .compatible = "nxp,pcf2129" },
> > > -       { .compatible = "nxp,pca2129" },
> > > +       { .compatible = "nxp,pcf2127", .data = &pcf21xx_cfg[PCF2127] },
> > > +       { .compatible = "nxp,pcf2129", .data = &pcf21xx_cfg[PCF2129] },
> > > +       { .compatible = "nxp,pca2129", .data = &pcf21xx_cfg[PCF2129] },
> > >         {}
> > >  };
> > >  MODULE_DEVICE_TABLE(of, pcf2127_of_match);
> > > @@ -886,26 +914,40 @@ static const struct regmap_bus pcf2127_i2c_regmap = {
> > >  static struct i2c_driver pcf2127_i2c_driver;
> > >
> > >  static const struct i2c_device_id pcf2127_i2c_id[] = {
> > > -       { "pcf2127", 1 },
> > > -       { "pcf2129", 0 },
> > > -       { "pca2129", 0 },
> > > +       { "pcf2127", PCF2127 },
> > > +       { "pcf2129", PCF2129 },
> > > +       { "pca2129", PCF2129 },
> > >         { }
> > >  };
> > >  MODULE_DEVICE_TABLE(i2c, pcf2127_i2c_id);
> > >
> > >  static int pcf2127_i2c_probe(struct i2c_client *client)
> > >  {
> > > -       const struct i2c_device_id *id = i2c_match_id(pcf2127_i2c_id, client);
> > >         struct regmap *regmap;
> > > -       static const struct regmap_config config = {
> > > +       static struct regmap_config config = {
> > >                 .reg_bits = 8,
> > >                 .val_bits = 8,
> > > -               .max_register = 0x1d,
> > >         };
> > > +       const struct pcf21xx_config *variant;
> >
> > Hi Hugo,
> >
> > Patch series does not apply on 6.1 tree as pcf2127_i2c_probe() call
> > signature does not match[1].
> >
> > static int pcf2127_i2c_probe(struct i2c_client *client,
> >       const struct i2c_device_id *id)
> >
> >
> > [1] https://elixir.bootlin.com/linux/v6.1/source/drivers/rtc/rtc-pcf2127.c#L888
> >
> > /Bruno
>
> Hi Bruno,
> I based my driver on the git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux.git repo, as indicated in the MAINTAINERS file for the RTC subsystem (T: entry). I used the rtc-next branch on this repo.
>
> Can you tell me exactly which repo and branch I need to use to resubmit the driver?
>
> Thank you, Hugo.
>
>
> > >         if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
> > >                 return -ENODEV;
> > >
> > > +       if (client->dev.of_node) {
> > > +               variant = of_device_get_match_data(&client->dev);
> > > +               if (!variant)
> > > +                       return -ENODEV;
> > > +       } else {
> > > +               enum pcf21xx_type type =
> > > +                       i2c_match_id(pcf2127_i2c_id, client)->driver_data;
> > > +
> > > +               if (type >= PCF21XX_LAST_ID)
> > > +                       return -ENODEV;
> > > +               variant = &pcf21xx_cfg[type];
> > > +       }
> > > +
> > > +       config.max_register = variant->max_register,
> > > +
> > >         regmap = devm_regmap_init(&client->dev, &pcf2127_i2c_regmap,
> > >                                         &client->dev, &config);
> > >         if (IS_ERR(regmap)) {
> > > @@ -915,7 +957,7 @@ static int pcf2127_i2c_probe(struct i2c_client *client)
> > >         }
> > >
> > >         return pcf2127_probe(&client->dev, regmap, client->irq,
> > > -                            pcf2127_i2c_driver.driver.name, id->driver_data);
> > > +                            pcf2127_i2c_driver.driver.name, variant);
> > >  }
> > >
> > >  static struct i2c_driver pcf2127_i2c_driver = {
> > > @@ -953,17 +995,32 @@ static void pcf2127_i2c_unregister_driver(void)
> > >  #if IS_ENABLED(CONFIG_SPI_MASTER)
> > >
> > >  static struct spi_driver pcf2127_spi_driver;
> > > +static const struct spi_device_id pcf2127_spi_id[];
> > >
> > >  static int pcf2127_spi_probe(struct spi_device *spi)
> > >  {
> > > -       static const struct regmap_config config = {
> > > +       static struct regmap_config config = {
> > >                 .reg_bits = 8,
> > >                 .val_bits = 8,
> > >                 .read_flag_mask = 0xa0,
> > >                 .write_flag_mask = 0x20,
> > > -               .max_register = 0x1d,
> > >         };
> > >         struct regmap *regmap;
> > > +       const struct pcf21xx_config *variant;
> > > +
> > > +       if (spi->dev.of_node) {
> > > +               variant = of_device_get_match_data(&spi->dev);
> > > +               if (!variant)
> > > +                       return -ENODEV;
> > > +       } else {
> > > +               enum pcf21xx_type type = spi_get_device_id(spi)->driver_data;
> > > +
> > > +               if (type >= PCF21XX_LAST_ID)
> > > +                       return -ENODEV;
> > > +               variant = &pcf21xx_cfg[type];
> > > +       }
> > > +
> > > +       config.max_register = variant->max_register,
> > >
> > >         regmap = devm_regmap_init_spi(spi, &config);
> > >         if (IS_ERR(regmap)) {
> > > @@ -974,13 +1031,13 @@ static int pcf2127_spi_probe(struct spi_device *spi)
> > >
> > >         return pcf2127_probe(&spi->dev, regmap, spi->irq,
> > >                              pcf2127_spi_driver.driver.name,
> > > -                            spi_get_device_id(spi)->driver_data);
> > > +                            variant);
> > >  }
> > >
> > >  static const struct spi_device_id pcf2127_spi_id[] = {
> > > -       { "pcf2127", 1 },
> > > -       { "pcf2129", 0 },
> > > -       { "pca2129", 0 },
> > > +       { "pcf2127", PCF2127 },
> > > +       { "pcf2129", PCF2129 },
> > > +       { "pca2129", PCF2129 },
> > >         { }
> > >  };
> > >  MODULE_DEVICE_TABLE(spi, pcf2127_spi_id);
> > > --
> > > 2.30.2
> > >
> >
>
>
> --
> Hugo Villeneuve <hugo@hugovil.com>

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

* Re: [PATCH v3 14/14] dt-bindings: rtc: pcf2127: add PCF2131
  2022-12-19 16:25     ` Hugo Villeneuve
@ 2022-12-19 17:18       ` Bruno Thomsen
  2022-12-19 18:31         ` Hugo Villeneuve
  0 siblings, 1 reply; 72+ messages in thread
From: Bruno Thomsen @ 2022-12-19 17:18 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt,
	linux-rtc, devicetree, linux-kernel, Hugo Villeneuve

Den man. 19. dec. 2022 kl. 17.25 skrev Hugo Villeneuve <hugo@hugovil.com>:
>
> On Mon, 19 Dec 2022 10:14:10 +0100
> Bruno Thomsen <bruno.thomsen@gmail.com> wrote:
>
> > Den tor. 15. dec. 2022 kl. 16.19 skrev Hugo Villeneuve <hugo@hugovil.com>:
> > >
> > > From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > >
> > > Add support for new NXP RTC PCF2131.
> > >
> > > Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > > ---
> > >  Documentation/devicetree/bindings/rtc/nxp,pcf2127.yaml | 4 +++-
> > >  1 file changed, 3 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/Documentation/devicetree/bindings/rtc/nxp,pcf2127.yaml b/Documentation/devicetree/bindings/rtc/nxp,pcf2127.yaml
> > > index cde7b1675ead..a8f8c23da4d8 100644
> > > --- a/Documentation/devicetree/bindings/rtc/nxp,pcf2127.yaml
> > > +++ b/Documentation/devicetree/bindings/rtc/nxp,pcf2127.yaml
> > > @@ -14,7 +14,9 @@ maintainers:
> > >
> > >  properties:
> > >    compatible:
> > > -    const: nxp,pcf2127
> > > +    enum:
> > > +      - nxp,pcf2127
> > > +      - nxp,pcf2131
> >
> > The enum is incomplete as pcf2127_of_match struct also contains:
> > nxp,pcf2129
> > ncp,pca2129
> >
> > /Bruno
>
> Hi,
> if I understand correctly, this means that the pca2129 and pcf2129 entries are already missing and should be added in a fix or a patch outside the scope of my new driver...
>

Correct, I just noticed while doing review.

> Hugo.
>
>
> > >    reg:
> > >      maxItems: 1
> > > --
> > > 2.30.2
> > >
> >
>
>
> --
> Hugo Villeneuve <hugo@hugovil.com>

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

* Re: [PATCH v3 01/14] rtc: pcf2127: add variant-specific configuration structure
  2022-12-19 17:17       ` Bruno Thomsen
@ 2022-12-19 18:30         ` Hugo Villeneuve
  0 siblings, 0 replies; 72+ messages in thread
From: Hugo Villeneuve @ 2022-12-19 18:30 UTC (permalink / raw)
  To: Bruno Thomsen
  Cc: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt,
	linux-rtc, devicetree, linux-kernel, Hugo Villeneuve

On Mon, 19 Dec 2022 18:17:45 +0100
Bruno Thomsen <bruno.thomsen@gmail.com> wrote:

> Hi Hugo,
> 
> Looking more into it I think you based it against the right tree,
> but if you use --base= when doing format-patch it's easier to spot :)
> I only use torvalds tree when testing patches on a device.
> 
> /Bruno

Hi Bruno,
thanks for the tip, I will use "--base=" for V4.

Hugo.


> Den man. 19. dec. 2022 kl. 16.15 skrev Hugo Villeneuve <hugo@hugovil.com>:
> >
> > On Mon, 19 Dec 2022 10:05:53 +0100
> > Bruno Thomsen <bruno.thomsen@gmail.com> wrote:
> >
> > > Den tor. 15. dec. 2022 kl. 16.19 skrev Hugo Villeneuve <hugo@hugovil.com>:
> > > >
> > > > From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > > >
> > > > Create variant-specific configuration structures to simplify the
> > > > implementation of new variants into this driver. It will also avoid
> > > > to have too many tests for a specific variant, or a list of variants
> > > > for new devices, inside the code itself.
> > > >
> > > > Add configuration options for the support of the NVMEM, bit CD0 in
> > > > register WD_CTL as well as the maximum number of registers for each
> > > > variant, instead of hardcoding the variant (PCF2127) inside the
> > > > i2c_device_id and spi_device_id structures.
> > > >
> > > > Also specify a different maximum number of registers (max_register)
> > > > for the PCF2129.
> > > >
> > > > Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > > > ---
> > > >  drivers/rtc/rtc-pcf2127.c | 95 +++++++++++++++++++++++++++++++--------
> > > >  1 file changed, 76 insertions(+), 19 deletions(-)
> > > >
> > > > diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> > > > index 87f4fc9df68b..b9a5d47a439f 100644
> > > > --- a/drivers/rtc/rtc-pcf2127.c
> > > > +++ b/drivers/rtc/rtc-pcf2127.c
> > > > @@ -21,6 +21,7 @@
> > > >  #include <linux/module.h>
> > > >  #include <linux/of.h>
> > > >  #include <linux/of_irq.h>
> > > > +#include <linux/of_device.h>
> > > >  #include <linux/regmap.h>
> > > >  #include <linux/watchdog.h>
> > > >
> > > > @@ -101,10 +102,17 @@
> > > >                 PCF2127_BIT_CTRL2_WDTF | \
> > > >                 PCF2127_BIT_CTRL2_TSF2)
> > > >
> > > > +struct pcf21xx_config {
> > > > +       int max_register;
> > > > +       unsigned int has_nvmem:1;
> > > > +       unsigned int has_bit_wd_ctl_cd0:1;
> > > > +};
> > > > +
> > > >  struct pcf2127 {
> > > >         struct rtc_device *rtc;
> > > >         struct watchdog_device wdd;
> > > >         struct regmap *regmap;
> > > > +       const struct pcf21xx_config *cfg;
> > > >         time64_t ts;
> > > >         bool ts_valid;
> > > >         bool irq_enabled;
> > > > @@ -631,8 +639,27 @@ static const struct attribute_group pcf2127_attr_group = {
> > > >         .attrs  = pcf2127_attrs,
> > > >  };
> > > >
> > > > +enum pcf21xx_type {
> > > > +       PCF2127,
> > > > +       PCF2129,
> > > > +       PCF21XX_LAST_ID
> > > > +};
> > > > +
> > > > +static struct pcf21xx_config pcf21xx_cfg[] = {
> > > > +       [PCF2127] = {
> > > > +               .max_register = 0x1d,
> > > > +               .has_nvmem = 1,
> > > > +               .has_bit_wd_ctl_cd0 = 1,
> > > > +       },
> > > > +       [PCF2129] = {
> > > > +               .max_register = 0x19,
> > > > +               .has_nvmem = 0,
> > > > +               .has_bit_wd_ctl_cd0 = 0,
> > > > +       },
> > > > +};
> > > > +
> > > >  static int pcf2127_probe(struct device *dev, struct regmap *regmap,
> > > > -                        int alarm_irq, const char *name, bool is_pcf2127)
> > > > +                        int alarm_irq, const char *name, const struct pcf21xx_config *config)
> > > >  {
> > > >         struct pcf2127 *pcf2127;
> > > >         int ret = 0;
> > > > @@ -645,6 +672,7 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
> > > >                 return -ENOMEM;
> > > >
> > > >         pcf2127->regmap = regmap;
> > > > +       pcf2127->cfg = config;
> > > >
> > > >         dev_set_drvdata(dev, pcf2127);
> > > >
> > > > @@ -688,7 +716,7 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
> > > >                 set_bit(RTC_FEATURE_ALARM, pcf2127->rtc->features);
> > > >         }
> > > >
> > > > -       if (is_pcf2127) {
> > > > +       if (pcf2127->cfg->has_nvmem) {
> > > >                 struct nvmem_config nvmem_cfg = {
> > > >                         .priv = pcf2127,
> > > >                         .reg_read = pcf2127_nvmem_read,
> > > > @@ -734,7 +762,7 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
> > > >                                  PCF2127_BIT_WD_CTL_TF1 |
> > > >                                  PCF2127_BIT_WD_CTL_TF0,
> > > >                                  PCF2127_BIT_WD_CTL_CD1 |
> > > > -                                (is_pcf2127 ? PCF2127_BIT_WD_CTL_CD0 : 0) |
> > > > +                                (pcf2127->cfg->has_bit_wd_ctl_cd0 ? PCF2127_BIT_WD_CTL_CD0 : 0) |
> > > >                                  PCF2127_BIT_WD_CTL_TF1);
> > > >         if (ret) {
> > > >                 dev_err(dev, "%s: watchdog config (wd_ctl) failed\n", __func__);
> > > > @@ -799,9 +827,9 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
> > > >
> > > >  #ifdef CONFIG_OF
> > > >  static const struct of_device_id pcf2127_of_match[] = {
> > > > -       { .compatible = "nxp,pcf2127" },
> > > > -       { .compatible = "nxp,pcf2129" },
> > > > -       { .compatible = "nxp,pca2129" },
> > > > +       { .compatible = "nxp,pcf2127", .data = &pcf21xx_cfg[PCF2127] },
> > > > +       { .compatible = "nxp,pcf2129", .data = &pcf21xx_cfg[PCF2129] },
> > > > +       { .compatible = "nxp,pca2129", .data = &pcf21xx_cfg[PCF2129] },
> > > >         {}
> > > >  };
> > > >  MODULE_DEVICE_TABLE(of, pcf2127_of_match);
> > > > @@ -886,26 +914,40 @@ static const struct regmap_bus pcf2127_i2c_regmap = {
> > > >  static struct i2c_driver pcf2127_i2c_driver;
> > > >
> > > >  static const struct i2c_device_id pcf2127_i2c_id[] = {
> > > > -       { "pcf2127", 1 },
> > > > -       { "pcf2129", 0 },
> > > > -       { "pca2129", 0 },
> > > > +       { "pcf2127", PCF2127 },
> > > > +       { "pcf2129", PCF2129 },
> > > > +       { "pca2129", PCF2129 },
> > > >         { }
> > > >  };
> > > >  MODULE_DEVICE_TABLE(i2c, pcf2127_i2c_id);
> > > >
> > > >  static int pcf2127_i2c_probe(struct i2c_client *client)
> > > >  {
> > > > -       const struct i2c_device_id *id = i2c_match_id(pcf2127_i2c_id, client);
> > > >         struct regmap *regmap;
> > > > -       static const struct regmap_config config = {
> > > > +       static struct regmap_config config = {
> > > >                 .reg_bits = 8,
> > > >                 .val_bits = 8,
> > > > -               .max_register = 0x1d,
> > > >         };
> > > > +       const struct pcf21xx_config *variant;
> > >
> > > Hi Hugo,
> > >
> > > Patch series does not apply on 6.1 tree as pcf2127_i2c_probe() call
> > > signature does not match[1].
> > >
> > > static int pcf2127_i2c_probe(struct i2c_client *client,
> > >       const struct i2c_device_id *id)
> > >
> > >
> > > [1] https://elixir.bootlin.com/linux/v6.1/source/drivers/rtc/rtc-pcf2127.c#L888
> > >
> > > /Bruno
> >
> > Hi Bruno,
> > I based my driver on the git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux.git repo, as indicated in the MAINTAINERS file for the RTC subsystem (T: entry). I used the rtc-next branch on this repo.
> >
> > Can you tell me exactly which repo and branch I need to use to resubmit the driver?
> >
> > Thank you, Hugo.
> >
> >
> > > >         if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
> > > >                 return -ENODEV;
> > > >
> > > > +       if (client->dev.of_node) {
> > > > +               variant = of_device_get_match_data(&client->dev);
> > > > +               if (!variant)
> > > > +                       return -ENODEV;
> > > > +       } else {
> > > > +               enum pcf21xx_type type =
> > > > +                       i2c_match_id(pcf2127_i2c_id, client)->driver_data;
> > > > +
> > > > +               if (type >= PCF21XX_LAST_ID)
> > > > +                       return -ENODEV;
> > > > +               variant = &pcf21xx_cfg[type];
> > > > +       }
> > > > +
> > > > +       config.max_register = variant->max_register,
> > > > +
> > > >         regmap = devm_regmap_init(&client->dev, &pcf2127_i2c_regmap,
> > > >                                         &client->dev, &config);
> > > >         if (IS_ERR(regmap)) {
> > > > @@ -915,7 +957,7 @@ static int pcf2127_i2c_probe(struct i2c_client *client)
> > > >         }
> > > >
> > > >         return pcf2127_probe(&client->dev, regmap, client->irq,
> > > > -                            pcf2127_i2c_driver.driver.name, id->driver_data);
> > > > +                            pcf2127_i2c_driver.driver.name, variant);
> > > >  }
> > > >
> > > >  static struct i2c_driver pcf2127_i2c_driver = {
> > > > @@ -953,17 +995,32 @@ static void pcf2127_i2c_unregister_driver(void)
> > > >  #if IS_ENABLED(CONFIG_SPI_MASTER)
> > > >
> > > >  static struct spi_driver pcf2127_spi_driver;
> > > > +static const struct spi_device_id pcf2127_spi_id[];
> > > >
> > > >  static int pcf2127_spi_probe(struct spi_device *spi)
> > > >  {
> > > > -       static const struct regmap_config config = {
> > > > +       static struct regmap_config config = {
> > > >                 .reg_bits = 8,
> > > >                 .val_bits = 8,
> > > >                 .read_flag_mask = 0xa0,
> > > >                 .write_flag_mask = 0x20,
> > > > -               .max_register = 0x1d,
> > > >         };
> > > >         struct regmap *regmap;
> > > > +       const struct pcf21xx_config *variant;
> > > > +
> > > > +       if (spi->dev.of_node) {
> > > > +               variant = of_device_get_match_data(&spi->dev);
> > > > +               if (!variant)
> > > > +                       return -ENODEV;
> > > > +       } else {
> > > > +               enum pcf21xx_type type = spi_get_device_id(spi)->driver_data;
> > > > +
> > > > +               if (type >= PCF21XX_LAST_ID)
> > > > +                       return -ENODEV;
> > > > +               variant = &pcf21xx_cfg[type];
> > > > +       }
> > > > +
> > > > +       config.max_register = variant->max_register,
> > > >
> > > >         regmap = devm_regmap_init_spi(spi, &config);
> > > >         if (IS_ERR(regmap)) {
> > > > @@ -974,13 +1031,13 @@ static int pcf2127_spi_probe(struct spi_device *spi)
> > > >
> > > >         return pcf2127_probe(&spi->dev, regmap, spi->irq,
> > > >                              pcf2127_spi_driver.driver.name,
> > > > -                            spi_get_device_id(spi)->driver_data);
> > > > +                            variant);
> > > >  }
> > > >
> > > >  static const struct spi_device_id pcf2127_spi_id[] = {
> > > > -       { "pcf2127", 1 },
> > > > -       { "pcf2129", 0 },
> > > > -       { "pca2129", 0 },
> > > > +       { "pcf2127", PCF2127 },
> > > > +       { "pcf2129", PCF2129 },
> > > > +       { "pca2129", PCF2129 },
> > > >         { }
> > > >  };
> > > >  MODULE_DEVICE_TABLE(spi, pcf2127_spi_id);
> > > > --
> > > > 2.30.2
> > > >
> > >
> >
> >
> > --
> > Hugo Villeneuve <hugo@hugovil.com>
> 


-- 
Hugo Villeneuve <hugo@hugovil.com>

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

* Re: [PATCH v3 14/14] dt-bindings: rtc: pcf2127: add PCF2131
  2022-12-19 17:18       ` Bruno Thomsen
@ 2022-12-19 18:31         ` Hugo Villeneuve
  0 siblings, 0 replies; 72+ messages in thread
From: Hugo Villeneuve @ 2022-12-19 18:31 UTC (permalink / raw)
  To: Bruno Thomsen
  Cc: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt,
	linux-rtc, devicetree, linux-kernel, Hugo Villeneuve

On Mon, 19 Dec 2022 18:18:58 +0100
Bruno Thomsen <bruno.thomsen@gmail.com> wrote:

> Den man. 19. dec. 2022 kl. 17.25 skrev Hugo Villeneuve <hugo@hugovil.com>:
> >
> > On Mon, 19 Dec 2022 10:14:10 +0100
> > Bruno Thomsen <bruno.thomsen@gmail.com> wrote:
> >
> > > Den tor. 15. dec. 2022 kl. 16.19 skrev Hugo Villeneuve <hugo@hugovil.com>:
> > > >
> > > > From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > > >
> > > > Add support for new NXP RTC PCF2131.
> > > >
> > > > Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > > > ---
> > > >  Documentation/devicetree/bindings/rtc/nxp,pcf2127.yaml | 4 +++-
> > > >  1 file changed, 3 insertions(+), 1 deletion(-)
> > > >
> > > > diff --git a/Documentation/devicetree/bindings/rtc/nxp,pcf2127.yaml b/Documentation/devicetree/bindings/rtc/nxp,pcf2127.yaml
> > > > index cde7b1675ead..a8f8c23da4d8 100644
> > > > --- a/Documentation/devicetree/bindings/rtc/nxp,pcf2127.yaml
> > > > +++ b/Documentation/devicetree/bindings/rtc/nxp,pcf2127.yaml
> > > > @@ -14,7 +14,9 @@ maintainers:
> > > >
> > > >  properties:
> > > >    compatible:
> > > > -    const: nxp,pcf2127
> > > > +    enum:
> > > > +      - nxp,pcf2127
> > > > +      - nxp,pcf2131
> > >
> > > The enum is incomplete as pcf2127_of_match struct also contains:
> > > nxp,pcf2129
> > > ncp,pca2129
> > >
> > > /Bruno
> >
> > Hi,
> > if I understand correctly, this means that the pca2129 and pcf2129 entries are already missing and should be added in a fix or a patch outside the scope of my new driver...
> >
> 
> Correct, I just noticed while doing review.

Hi,
I will send a separate patch very soon.

Thank you,
Hugo. V.


> > > >    reg:
> > > >      maxItems: 1
> > > > --
> > > > 2.30.2
> > > >
> > >
> >
> >
> > --
> > Hugo Villeneuve <hugo@hugovil.com>
> 


-- 
Hugo Villeneuve <hugo@hugovil.com>

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

* Re: [PATCH v3 02/14] rtc: pcf2127: adapt for time/date registers at any offset
  2022-12-19  9:34   ` Bruno Thomsen
  2022-12-19 16:27     ` Hugo Villeneuve
@ 2023-01-07 16:49     ` Bruno Thomsen
  1 sibling, 0 replies; 72+ messages in thread
From: Bruno Thomsen @ 2023-01-07 16:49 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt,
	linux-rtc, devicetree, linux-kernel, Hugo Villeneuve

Den man. 19. dec. 2022 kl. 10.34 skrev Bruno Thomsen <bruno.thomsen@gmail.com>:
>
> Den tor. 15. dec. 2022 kl. 16.20 skrev Hugo Villeneuve <hugo@hugovil.com>:
> >
> > From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> >
> > This will simplify the implementation of new variants into this driver.
> >
> > Some variants (PCF2131) have a 100th seconds register. This register is
> > currently not supported in this driver.
> >
> > Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > ---
> >  drivers/rtc/rtc-pcf2127.c | 68 ++++++++++++++++++++++-----------------
> >  1 file changed, 39 insertions(+), 29 deletions(-)
> >
> > diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> > index b9a5d47a439f..fb0caacaabee 100644
> > --- a/drivers/rtc/rtc-pcf2127.c
> > +++ b/drivers/rtc/rtc-pcf2127.c
> > @@ -44,14 +44,17 @@
> >  #define PCF2127_BIT_CTRL3_BF                   BIT(3)
> >  #define PCF2127_BIT_CTRL3_BTSE                 BIT(4)
> >  /* Time and date registers */
> > -#define PCF2127_REG_SC                 0x03
> > +#define PCF2127_REG_TIME_DATE_BASE     0x03
> > +/* Time and date registers offsets (starting from base register) */
> > +#define PCF2127_OFFSET_TD_SC           0

Maybe do the same change for register defines as the pc721xx_config
struct parameter. This will also match what you did in patch 3 with
alarm registers.

- PCF2127_REG_TIME_DATE_BASE
+ PCF2127_REG_TIME_BASE

- PCF2127_OFFSET_TD_SC
+ PCF2127_OFFSET_TIME_SC

etc.

> > +#define PCF2127_OFFSET_TD_MN           1
> > +#define PCF2127_OFFSET_TD_HR           2
> > +#define PCF2127_OFFSET_TD_DM           3
> > +#define PCF2127_OFFSET_TD_DW           4
> > +#define PCF2127_OFFSET_TD_MO           5
> > +#define PCF2127_OFFSET_TD_YR           6
> > +/* Time and date registers bits */
> >  #define PCF2127_BIT_SC_OSF                     BIT(7)
> > -#define PCF2127_REG_MN                 0x04
> > -#define PCF2127_REG_HR                 0x05
> > -#define PCF2127_REG_DM                 0x06
> > -#define PCF2127_REG_DW                 0x07
> > -#define PCF2127_REG_MO                 0x08
> > -#define PCF2127_REG_YR                 0x09
> >  /* Alarm registers */
> >  #define PCF2127_REG_ALARM_SC           0x0A
> >  #define PCF2127_REG_ALARM_MN           0x0B
> > @@ -106,6 +109,7 @@ struct pcf21xx_config {
> >         int max_register;
> >         unsigned int has_nvmem:1;
> >         unsigned int has_bit_wd_ctl_cd0:1;
> > +       u8 regs_td_base; /* Time/data base registers. */
>
> There is only one base register, so no need to add s.
> I think td is an odd short, so maybe u8 reg_time_base.
>
> /Bruno
>
> >  };
> >
> >  struct pcf2127 {
> > @@ -125,27 +129,31 @@ struct pcf2127 {
> >  static int pcf2127_rtc_read_time(struct device *dev, struct rtc_time *tm)
> >  {
> >         struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
> > -       unsigned char buf[10];
> > +       unsigned char buf[7];
> > +       unsigned int ctrl3;
> >         int ret;
> >
> >         /*
> >          * Avoid reading CTRL2 register as it causes WD_VAL register
> >          * value to reset to 0 which means watchdog is stopped.
> >          */
> > -       ret = regmap_bulk_read(pcf2127->regmap, PCF2127_REG_CTRL3,
> > -                              (buf + PCF2127_REG_CTRL3),
> > -                              ARRAY_SIZE(buf) - PCF2127_REG_CTRL3);
> > -       if (ret) {
> > -               dev_err(dev, "%s: read error\n", __func__);
> > +       ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL3, &ctrl3);
> > +       if (ret)
> >                 return ret;
> > -       }
> >
> > -       if (buf[PCF2127_REG_CTRL3] & PCF2127_BIT_CTRL3_BLF)
> > +       if (ctrl3 & PCF2127_BIT_CTRL3_BLF)
> >                 dev_info(dev,
> >                         "low voltage detected, check/replace RTC battery.\n");
> >
> > +       ret = regmap_bulk_read(pcf2127->regmap, pcf2127->cfg->regs_td_base,
> > +                              buf, sizeof(buf));
> > +       if (ret) {
> > +               dev_err(dev, "%s: read error\n", __func__);
> > +               return ret;
> > +       }
> > +
> >         /* Clock integrity is not guaranteed when OSF flag is set. */
> > -       if (buf[PCF2127_REG_SC] & PCF2127_BIT_SC_OSF) {
> > +       if (buf[PCF2127_OFFSET_TD_SC] & PCF2127_BIT_SC_OSF) {
> >                 /*
> >                  * no need clear the flag here,
> >                  * it will be cleared once the new date is saved
> > @@ -158,18 +166,18 @@ static int pcf2127_rtc_read_time(struct device *dev, struct rtc_time *tm)
> >         dev_dbg(dev,
> >                 "%s: raw data is cr3=%02x, sec=%02x, min=%02x, hr=%02x, "
> >                 "mday=%02x, wday=%02x, mon=%02x, year=%02x\n",
> > -               __func__, buf[PCF2127_REG_CTRL3], buf[PCF2127_REG_SC],
> > -               buf[PCF2127_REG_MN], buf[PCF2127_REG_HR],
> > -               buf[PCF2127_REG_DM], buf[PCF2127_REG_DW],
> > -               buf[PCF2127_REG_MO], buf[PCF2127_REG_YR]);
> > -
> > -       tm->tm_sec = bcd2bin(buf[PCF2127_REG_SC] & 0x7F);
> > -       tm->tm_min = bcd2bin(buf[PCF2127_REG_MN] & 0x7F);
> > -       tm->tm_hour = bcd2bin(buf[PCF2127_REG_HR] & 0x3F); /* rtc hr 0-23 */
> > -       tm->tm_mday = bcd2bin(buf[PCF2127_REG_DM] & 0x3F);
> > -       tm->tm_wday = buf[PCF2127_REG_DW] & 0x07;
> > -       tm->tm_mon = bcd2bin(buf[PCF2127_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */
> > -       tm->tm_year = bcd2bin(buf[PCF2127_REG_YR]);
> > +               __func__, ctrl3, buf[PCF2127_OFFSET_TD_SC],
> > +               buf[PCF2127_OFFSET_TD_MN], buf[PCF2127_OFFSET_TD_HR],
> > +               buf[PCF2127_OFFSET_TD_DM], buf[PCF2127_OFFSET_TD_DW],
> > +               buf[PCF2127_OFFSET_TD_MO], buf[PCF2127_OFFSET_TD_YR]);
> > +
> > +       tm->tm_sec = bcd2bin(buf[PCF2127_OFFSET_TD_SC] & 0x7F);
> > +       tm->tm_min = bcd2bin(buf[PCF2127_OFFSET_TD_MN] & 0x7F);
> > +       tm->tm_hour = bcd2bin(buf[PCF2127_OFFSET_TD_HR] & 0x3F); /* rtc hr 0-23 */
> > +       tm->tm_mday = bcd2bin(buf[PCF2127_OFFSET_TD_DM] & 0x3F);
> > +       tm->tm_wday = buf[PCF2127_OFFSET_TD_DW] & 0x07;
> > +       tm->tm_mon = bcd2bin(buf[PCF2127_OFFSET_TD_MO] & 0x1F) - 1; /* rtc mn 1-12 */
> > +       tm->tm_year = bcd2bin(buf[PCF2127_OFFSET_TD_YR]);
> >         tm->tm_year += 100;
> >
> >         dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
> > @@ -207,7 +215,7 @@ static int pcf2127_rtc_set_time(struct device *dev, struct rtc_time *tm)
> >         buf[i++] = bin2bcd(tm->tm_year - 100);
> >
> >         /* write register's data */
> > -       err = regmap_bulk_write(pcf2127->regmap, PCF2127_REG_SC, buf, i);
> > +       err = regmap_bulk_write(pcf2127->regmap, pcf2127->cfg->regs_td_base, buf, i);
> >         if (err) {
> >                 dev_err(dev,
> >                         "%s: err=%d", __func__, err);
> > @@ -650,11 +658,13 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
> >                 .max_register = 0x1d,
> >                 .has_nvmem = 1,
> >                 .has_bit_wd_ctl_cd0 = 1,
> > +               .regs_td_base = PCF2127_REG_TIME_DATE_BASE,
> >         },
> >         [PCF2129] = {
> >                 .max_register = 0x19,
> >                 .has_nvmem = 0,
> >                 .has_bit_wd_ctl_cd0 = 0,
> > +               .regs_td_base = PCF2127_REG_TIME_DATE_BASE,
> >         },
> >  };
> >
> > --
> > 2.30.2
> >

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

* Re: [PATCH v3 01/14] rtc: pcf2127: add variant-specific configuration structure
  2022-12-15 15:02 ` [PATCH v3 01/14] rtc: pcf2127: add variant-specific configuration structure Hugo Villeneuve
  2022-12-19  9:05   ` Bruno Thomsen
@ 2023-01-07 16:52   ` Bruno Thomsen
  1 sibling, 0 replies; 72+ messages in thread
From: Bruno Thomsen @ 2023-01-07 16:52 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt,
	linux-rtc, devicetree, linux-kernel, Hugo Villeneuve

Den tor. 15. dec. 2022 kl. 16.19 skrev Hugo Villeneuve <hugo@hugovil.com>:
>
> From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
>
> Create variant-specific configuration structures to simplify the
> implementation of new variants into this driver. It will also avoid
> to have too many tests for a specific variant, or a list of variants
> for new devices, inside the code itself.
>
> Add configuration options for the support of the NVMEM, bit CD0 in
> register WD_CTL as well as the maximum number of registers for each
> variant, instead of hardcoding the variant (PCF2127) inside the
> i2c_device_id and spi_device_id structures.
>
> Also specify a different maximum number of registers (max_register)
> for the PCF2129.
>
> Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>

Reviewed-by: Bruno Thomsen <bruno.thomsen@gmail.com>

> ---
>  drivers/rtc/rtc-pcf2127.c | 95 +++++++++++++++++++++++++++++++--------
>  1 file changed, 76 insertions(+), 19 deletions(-)
>
> diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> index 87f4fc9df68b..b9a5d47a439f 100644
> --- a/drivers/rtc/rtc-pcf2127.c
> +++ b/drivers/rtc/rtc-pcf2127.c
> @@ -21,6 +21,7 @@
>  #include <linux/module.h>
>  #include <linux/of.h>
>  #include <linux/of_irq.h>
> +#include <linux/of_device.h>
>  #include <linux/regmap.h>
>  #include <linux/watchdog.h>
>
> @@ -101,10 +102,17 @@
>                 PCF2127_BIT_CTRL2_WDTF | \
>                 PCF2127_BIT_CTRL2_TSF2)
>
> +struct pcf21xx_config {
> +       int max_register;
> +       unsigned int has_nvmem:1;
> +       unsigned int has_bit_wd_ctl_cd0:1;
> +};
> +
>  struct pcf2127 {
>         struct rtc_device *rtc;
>         struct watchdog_device wdd;
>         struct regmap *regmap;
> +       const struct pcf21xx_config *cfg;
>         time64_t ts;
>         bool ts_valid;
>         bool irq_enabled;
> @@ -631,8 +639,27 @@ static const struct attribute_group pcf2127_attr_group = {
>         .attrs  = pcf2127_attrs,
>  };
>
> +enum pcf21xx_type {
> +       PCF2127,
> +       PCF2129,
> +       PCF21XX_LAST_ID
> +};
> +
> +static struct pcf21xx_config pcf21xx_cfg[] = {
> +       [PCF2127] = {
> +               .max_register = 0x1d,
> +               .has_nvmem = 1,
> +               .has_bit_wd_ctl_cd0 = 1,
> +       },
> +       [PCF2129] = {
> +               .max_register = 0x19,
> +               .has_nvmem = 0,
> +               .has_bit_wd_ctl_cd0 = 0,
> +       },
> +};
> +
>  static int pcf2127_probe(struct device *dev, struct regmap *regmap,
> -                        int alarm_irq, const char *name, bool is_pcf2127)
> +                        int alarm_irq, const char *name, const struct pcf21xx_config *config)
>  {
>         struct pcf2127 *pcf2127;
>         int ret = 0;
> @@ -645,6 +672,7 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
>                 return -ENOMEM;
>
>         pcf2127->regmap = regmap;
> +       pcf2127->cfg = config;
>
>         dev_set_drvdata(dev, pcf2127);
>
> @@ -688,7 +716,7 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
>                 set_bit(RTC_FEATURE_ALARM, pcf2127->rtc->features);
>         }
>
> -       if (is_pcf2127) {
> +       if (pcf2127->cfg->has_nvmem) {
>                 struct nvmem_config nvmem_cfg = {
>                         .priv = pcf2127,
>                         .reg_read = pcf2127_nvmem_read,
> @@ -734,7 +762,7 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
>                                  PCF2127_BIT_WD_CTL_TF1 |
>                                  PCF2127_BIT_WD_CTL_TF0,
>                                  PCF2127_BIT_WD_CTL_CD1 |
> -                                (is_pcf2127 ? PCF2127_BIT_WD_CTL_CD0 : 0) |
> +                                (pcf2127->cfg->has_bit_wd_ctl_cd0 ? PCF2127_BIT_WD_CTL_CD0 : 0) |
>                                  PCF2127_BIT_WD_CTL_TF1);
>         if (ret) {
>                 dev_err(dev, "%s: watchdog config (wd_ctl) failed\n", __func__);
> @@ -799,9 +827,9 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
>
>  #ifdef CONFIG_OF
>  static const struct of_device_id pcf2127_of_match[] = {
> -       { .compatible = "nxp,pcf2127" },
> -       { .compatible = "nxp,pcf2129" },
> -       { .compatible = "nxp,pca2129" },
> +       { .compatible = "nxp,pcf2127", .data = &pcf21xx_cfg[PCF2127] },
> +       { .compatible = "nxp,pcf2129", .data = &pcf21xx_cfg[PCF2129] },
> +       { .compatible = "nxp,pca2129", .data = &pcf21xx_cfg[PCF2129] },
>         {}
>  };
>  MODULE_DEVICE_TABLE(of, pcf2127_of_match);
> @@ -886,26 +914,40 @@ static const struct regmap_bus pcf2127_i2c_regmap = {
>  static struct i2c_driver pcf2127_i2c_driver;
>
>  static const struct i2c_device_id pcf2127_i2c_id[] = {
> -       { "pcf2127", 1 },
> -       { "pcf2129", 0 },
> -       { "pca2129", 0 },
> +       { "pcf2127", PCF2127 },
> +       { "pcf2129", PCF2129 },
> +       { "pca2129", PCF2129 },
>         { }
>  };
>  MODULE_DEVICE_TABLE(i2c, pcf2127_i2c_id);
>
>  static int pcf2127_i2c_probe(struct i2c_client *client)
>  {
> -       const struct i2c_device_id *id = i2c_match_id(pcf2127_i2c_id, client);
>         struct regmap *regmap;
> -       static const struct regmap_config config = {
> +       static struct regmap_config config = {
>                 .reg_bits = 8,
>                 .val_bits = 8,
> -               .max_register = 0x1d,
>         };
> +       const struct pcf21xx_config *variant;
>
>         if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
>                 return -ENODEV;
>
> +       if (client->dev.of_node) {
> +               variant = of_device_get_match_data(&client->dev);
> +               if (!variant)
> +                       return -ENODEV;
> +       } else {
> +               enum pcf21xx_type type =
> +                       i2c_match_id(pcf2127_i2c_id, client)->driver_data;
> +
> +               if (type >= PCF21XX_LAST_ID)
> +                       return -ENODEV;
> +               variant = &pcf21xx_cfg[type];
> +       }
> +
> +       config.max_register = variant->max_register,
> +
>         regmap = devm_regmap_init(&client->dev, &pcf2127_i2c_regmap,
>                                         &client->dev, &config);
>         if (IS_ERR(regmap)) {
> @@ -915,7 +957,7 @@ static int pcf2127_i2c_probe(struct i2c_client *client)
>         }
>
>         return pcf2127_probe(&client->dev, regmap, client->irq,
> -                            pcf2127_i2c_driver.driver.name, id->driver_data);
> +                            pcf2127_i2c_driver.driver.name, variant);
>  }
>
>  static struct i2c_driver pcf2127_i2c_driver = {
> @@ -953,17 +995,32 @@ static void pcf2127_i2c_unregister_driver(void)
>  #if IS_ENABLED(CONFIG_SPI_MASTER)
>
>  static struct spi_driver pcf2127_spi_driver;
> +static const struct spi_device_id pcf2127_spi_id[];
>
>  static int pcf2127_spi_probe(struct spi_device *spi)
>  {
> -       static const struct regmap_config config = {
> +       static struct regmap_config config = {
>                 .reg_bits = 8,
>                 .val_bits = 8,
>                 .read_flag_mask = 0xa0,
>                 .write_flag_mask = 0x20,
> -               .max_register = 0x1d,
>         };
>         struct regmap *regmap;
> +       const struct pcf21xx_config *variant;
> +
> +       if (spi->dev.of_node) {
> +               variant = of_device_get_match_data(&spi->dev);
> +               if (!variant)
> +                       return -ENODEV;
> +       } else {
> +               enum pcf21xx_type type = spi_get_device_id(spi)->driver_data;
> +
> +               if (type >= PCF21XX_LAST_ID)
> +                       return -ENODEV;
> +               variant = &pcf21xx_cfg[type];
> +       }
> +
> +       config.max_register = variant->max_register,
>
>         regmap = devm_regmap_init_spi(spi, &config);
>         if (IS_ERR(regmap)) {
> @@ -974,13 +1031,13 @@ static int pcf2127_spi_probe(struct spi_device *spi)
>
>         return pcf2127_probe(&spi->dev, regmap, spi->irq,
>                              pcf2127_spi_driver.driver.name,
> -                            spi_get_device_id(spi)->driver_data);
> +                            variant);
>  }
>
>  static const struct spi_device_id pcf2127_spi_id[] = {
> -       { "pcf2127", 1 },
> -       { "pcf2129", 0 },
> -       { "pca2129", 0 },
> +       { "pcf2127", PCF2127 },
> +       { "pcf2129", PCF2129 },
> +       { "pca2129", PCF2129 },
>         { }
>  };
>  MODULE_DEVICE_TABLE(spi, pcf2127_spi_id);
> --
> 2.30.2
>

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

* Re: [PATCH v3 03/14] rtc: pcf2127: adapt for alarm registers at any offset
  2022-12-15 15:02 ` [PATCH v3 03/14] rtc: pcf2127: adapt for alarm " Hugo Villeneuve
@ 2023-01-07 16:57   ` Bruno Thomsen
  2023-01-20 17:10   ` Alexandre Belloni
  1 sibling, 0 replies; 72+ messages in thread
From: Bruno Thomsen @ 2023-01-07 16:57 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt,
	linux-rtc, devicetree, linux-kernel, Hugo Villeneuve

Den tor. 15. dec. 2022 kl. 16.20 skrev Hugo Villeneuve <hugo@hugovil.com>:
>
> From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
>
> This will simplify the implementation of new variants into this driver.
>
> Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>

I have tested the entire patch series on
imx7d-flex-concentrator.dts[1] using pcf2127,
and have not observed any issues but I don't use the alarm and CLKOUT functions.

Reviewed-by: Bruno Thomsen <bruno.thomsen@gmail.com>

[1] https://elixir.bootlin.com/linux/latest/source/arch/arm/boot/dts/imx7d-flex-concentrator.dts

> ---
>  drivers/rtc/rtc-pcf2127.c | 42 ++++++++++++++++++++++-----------------
>  1 file changed, 24 insertions(+), 18 deletions(-)
>
> diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> index fb0caacaabee..db0cb784c0c9 100644
> --- a/drivers/rtc/rtc-pcf2127.c
> +++ b/drivers/rtc/rtc-pcf2127.c
> @@ -56,11 +56,14 @@
>  /* Time and date registers bits */
>  #define PCF2127_BIT_SC_OSF                     BIT(7)
>  /* Alarm registers */
> -#define PCF2127_REG_ALARM_SC           0x0A
> -#define PCF2127_REG_ALARM_MN           0x0B
> -#define PCF2127_REG_ALARM_HR           0x0C
> -#define PCF2127_REG_ALARM_DM           0x0D
> -#define PCF2127_REG_ALARM_DW           0x0E
> +#define PCF2127_REG_ALARM_BASE         0x0A
> +/* Alarm registers offsets (starting from base register) */
> +#define PCF2127_OFFSET_ALARM_SC                0
> +#define PCF2127_OFFSET_ALARM_MN                1
> +#define PCF2127_OFFSET_ALARM_HR                2
> +#define PCF2127_OFFSET_ALARM_DM                3
> +#define PCF2127_OFFSET_ALARM_DW                4
> +/* Alarm bits */
>  #define PCF2127_BIT_ALARM_AE                   BIT(7)
>  /* CLKOUT control register */
>  #define PCF2127_REG_CLKOUT             0x0f
> @@ -110,6 +113,7 @@ struct pcf21xx_config {
>         unsigned int has_nvmem:1;
>         unsigned int has_bit_wd_ctl_cd0:1;
>         u8 regs_td_base; /* Time/data base registers. */
> +       u8 regs_alarm_base; /* Alarm function base registers. */
>  };
>
>  struct pcf2127 {
> @@ -402,18 +406,18 @@ static int pcf2127_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
>         if (ret)
>                 return ret;
>
> -       ret = regmap_bulk_read(pcf2127->regmap, PCF2127_REG_ALARM_SC, buf,
> -                              sizeof(buf));
> +       ret = regmap_bulk_read(pcf2127->regmap, pcf2127->cfg->regs_alarm_base,
> +                              buf, sizeof(buf));
>         if (ret)
>                 return ret;
>
>         alrm->enabled = ctrl2 & PCF2127_BIT_CTRL2_AIE;
>         alrm->pending = ctrl2 & PCF2127_BIT_CTRL2_AF;
>
> -       alrm->time.tm_sec = bcd2bin(buf[0] & 0x7F);
> -       alrm->time.tm_min = bcd2bin(buf[1] & 0x7F);
> -       alrm->time.tm_hour = bcd2bin(buf[2] & 0x3F);
> -       alrm->time.tm_mday = bcd2bin(buf[3] & 0x3F);
> +       alrm->time.tm_sec = bcd2bin(buf[PCF2127_OFFSET_ALARM_SC] & 0x7F);
> +       alrm->time.tm_min = bcd2bin(buf[PCF2127_OFFSET_ALARM_MN] & 0x7F);
> +       alrm->time.tm_hour = bcd2bin(buf[PCF2127_OFFSET_ALARM_HR] & 0x3F);
> +       alrm->time.tm_mday = bcd2bin(buf[PCF2127_OFFSET_ALARM_DM] & 0x3F);
>
>         return 0;
>  }
> @@ -447,14 +451,14 @@ static int pcf2127_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
>         if (ret)
>                 return ret;
>
> -       buf[0] = bin2bcd(alrm->time.tm_sec);
> -       buf[1] = bin2bcd(alrm->time.tm_min);
> -       buf[2] = bin2bcd(alrm->time.tm_hour);
> -       buf[3] = bin2bcd(alrm->time.tm_mday);
> -       buf[4] = PCF2127_BIT_ALARM_AE; /* Do not match on week day */
> +       buf[PCF2127_OFFSET_ALARM_SC] = bin2bcd(alrm->time.tm_sec);
> +       buf[PCF2127_OFFSET_ALARM_MN] = bin2bcd(alrm->time.tm_min);
> +       buf[PCF2127_OFFSET_ALARM_HR] = bin2bcd(alrm->time.tm_hour);
> +       buf[PCF2127_OFFSET_ALARM_DM] = bin2bcd(alrm->time.tm_mday);
> +       buf[PCF2127_OFFSET_ALARM_DW] = PCF2127_BIT_ALARM_AE; /* Do not match on week day */
>
> -       ret = regmap_bulk_write(pcf2127->regmap, PCF2127_REG_ALARM_SC, buf,
> -                               sizeof(buf));
> +       ret = regmap_bulk_write(pcf2127->regmap, pcf2127->cfg->regs_alarm_base,
> +                               buf, sizeof(buf));
>         if (ret)
>                 return ret;
>
> @@ -659,12 +663,14 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
>                 .has_nvmem = 1,
>                 .has_bit_wd_ctl_cd0 = 1,
>                 .regs_td_base = PCF2127_REG_TIME_DATE_BASE,
> +               .regs_alarm_base = PCF2127_REG_ALARM_BASE,
>         },
>         [PCF2129] = {
>                 .max_register = 0x19,
>                 .has_nvmem = 0,
>                 .has_bit_wd_ctl_cd0 = 0,
>                 .regs_td_base = PCF2127_REG_TIME_DATE_BASE,
> +               .regs_alarm_base = PCF2127_REG_ALARM_BASE,
>         },
>  };
>
> --
> 2.30.2
>

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

* Re: [PATCH v3 04/14] rtc: pcf2127: adapt for WD registers at any offset
  2022-12-15 15:02 ` [PATCH v3 04/14] rtc: pcf2127: adapt for WD " Hugo Villeneuve
@ 2023-01-07 16:59   ` Bruno Thomsen
  0 siblings, 0 replies; 72+ messages in thread
From: Bruno Thomsen @ 2023-01-07 16:59 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt,
	linux-rtc, devicetree, linux-kernel, Hugo Villeneuve

Den tor. 15. dec. 2022 kl. 16.19 skrev Hugo Villeneuve <hugo@hugovil.com>:
>
> From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
>
> This will simplify the implementation of new variants into this driver.
>
> Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>

Reviewed-by: Bruno Thomsen <bruno.thomsen@gmail.com>
Tested-by: Bruno Thomsen <bruno.thomsen@gmail.com>

> ---
>  drivers/rtc/rtc-pcf2127.c | 14 ++++++++++----
>  1 file changed, 10 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> index db0cb784c0c9..5d8c06e32dce 100644
> --- a/drivers/rtc/rtc-pcf2127.c
> +++ b/drivers/rtc/rtc-pcf2127.c
> @@ -114,6 +114,8 @@ struct pcf21xx_config {
>         unsigned int has_bit_wd_ctl_cd0:1;
>         u8 regs_td_base; /* Time/data base registers. */
>         u8 regs_alarm_base; /* Alarm function base registers. */
> +       u8 reg_wd_ctl; /* Watchdog control register. */
> +       u8 reg_wd_val; /* Watchdog value register. */
>  };
>
>  struct pcf2127 {
> @@ -297,7 +299,7 @@ static int pcf2127_wdt_ping(struct watchdog_device *wdd)
>  {
>         struct pcf2127 *pcf2127 = watchdog_get_drvdata(wdd);
>
> -       return regmap_write(pcf2127->regmap, PCF2127_REG_WD_VAL, wdd->timeout);
> +       return regmap_write(pcf2127->regmap, pcf2127->cfg->reg_wd_val, wdd->timeout);
>  }
>
>  /*
> @@ -331,7 +333,7 @@ static int pcf2127_wdt_stop(struct watchdog_device *wdd)
>  {
>         struct pcf2127 *pcf2127 = watchdog_get_drvdata(wdd);
>
> -       return regmap_write(pcf2127->regmap, PCF2127_REG_WD_VAL,
> +       return regmap_write(pcf2127->regmap, pcf2127->cfg->reg_wd_val,
>                             PCF2127_WD_VAL_STOP);
>  }
>
> @@ -380,7 +382,7 @@ static int pcf2127_watchdog_init(struct device *dev, struct pcf2127 *pcf2127)
>         watchdog_set_drvdata(&pcf2127->wdd, pcf2127);
>
>         /* Test if watchdog timer is started by bootloader */
> -       ret = regmap_read(pcf2127->regmap, PCF2127_REG_WD_VAL, &wdd_timeout);
> +       ret = regmap_read(pcf2127->regmap, pcf2127->cfg->reg_wd_val, &wdd_timeout);
>         if (ret)
>                 return ret;
>
> @@ -664,6 +666,8 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
>                 .has_bit_wd_ctl_cd0 = 1,
>                 .regs_td_base = PCF2127_REG_TIME_DATE_BASE,
>                 .regs_alarm_base = PCF2127_REG_ALARM_BASE,
> +               .reg_wd_ctl = PCF2127_REG_WD_CTL,
> +               .reg_wd_val = PCF2127_REG_WD_VAL,
>         },
>         [PCF2129] = {
>                 .max_register = 0x19,
> @@ -671,6 +675,8 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
>                 .has_bit_wd_ctl_cd0 = 0,
>                 .regs_td_base = PCF2127_REG_TIME_DATE_BASE,
>                 .regs_alarm_base = PCF2127_REG_ALARM_BASE,
> +               .reg_wd_ctl = PCF2127_REG_WD_CTL,
> +               .reg_wd_val = PCF2127_REG_WD_VAL,
>         },
>  };
>
> @@ -772,7 +778,7 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
>          * as T. Bits labeled as T must always be written with
>          * logic 0.
>          */
> -       ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_WD_CTL,
> +       ret = regmap_update_bits(pcf2127->regmap, pcf2127->cfg->reg_wd_ctl,
>                                  PCF2127_BIT_WD_CTL_CD1 |
>                                  PCF2127_BIT_WD_CTL_CD0 |
>                                  PCF2127_BIT_WD_CTL_TF1 |
> --
> 2.30.2
>

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

* Re: [PATCH v3 05/14] rtc: pcf2127: adapt for CLKOUT register at any offset
  2022-12-15 15:02 ` [PATCH v3 05/14] rtc: pcf2127: adapt for CLKOUT register " Hugo Villeneuve
@ 2023-01-07 17:01   ` Bruno Thomsen
  0 siblings, 0 replies; 72+ messages in thread
From: Bruno Thomsen @ 2023-01-07 17:01 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt,
	linux-rtc, devicetree, linux-kernel, Hugo Villeneuve

Den tor. 15. dec. 2022 kl. 16.19 skrev Hugo Villeneuve <hugo@hugovil.com>:
>
> From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
>
> This will simplify the implementation of new variants into this driver.
>
> Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>

Reviewed-by: Bruno Thomsen <bruno.thomsen@gmail.com>

> ---
>  drivers/rtc/rtc-pcf2127.c | 7 +++++--
>  1 file changed, 5 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> index 5d8c06e32dce..38816ad065eb 100644
> --- a/drivers/rtc/rtc-pcf2127.c
> +++ b/drivers/rtc/rtc-pcf2127.c
> @@ -116,6 +116,7 @@ struct pcf21xx_config {
>         u8 regs_alarm_base; /* Alarm function base registers. */
>         u8 reg_wd_ctl; /* Watchdog control register. */
>         u8 reg_wd_val; /* Watchdog value register. */
> +       u8 reg_clkout; /* Clkout register. */
>  };
>
>  struct pcf2127 {
> @@ -668,6 +669,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
>                 .regs_alarm_base = PCF2127_REG_ALARM_BASE,
>                 .reg_wd_ctl = PCF2127_REG_WD_CTL,
>                 .reg_wd_val = PCF2127_REG_WD_VAL,
> +               .reg_clkout = PCF2127_REG_CLKOUT,
>         },
>         [PCF2129] = {
>                 .max_register = 0x19,
> @@ -677,6 +679,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
>                 .regs_alarm_base = PCF2127_REG_ALARM_BASE,
>                 .reg_wd_ctl = PCF2127_REG_WD_CTL,
>                 .reg_wd_val = PCF2127_REG_WD_VAL,
> +               .reg_clkout = PCF2127_REG_CLKOUT,
>         },
>  };
>
> @@ -756,12 +759,12 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
>         regmap_clear_bits(pcf2127->regmap, PCF2127_REG_CTRL1,
>                                 PCF2127_BIT_CTRL1_POR_OVRD);
>
> -       ret = regmap_read(pcf2127->regmap, PCF2127_REG_CLKOUT, &val);
> +       ret = regmap_read(pcf2127->regmap, pcf2127->cfg->reg_clkout, &val);
>         if (ret < 0)
>                 return ret;
>
>         if (!(val & PCF2127_BIT_CLKOUT_OTPR)) {
> -               ret = regmap_set_bits(pcf2127->regmap, PCF2127_REG_CLKOUT,
> +               ret = regmap_set_bits(pcf2127->regmap, pcf2127->cfg->reg_clkout,
>                                       PCF2127_BIT_CLKOUT_OTPR);
>                 if (ret < 0)
>                         return ret;
> --
> 2.30.2
>

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

* Re: [PATCH v3 06/14] rtc: pcf2127: add support for multiple TS functions
  2022-12-15 15:02 ` [PATCH v3 06/14] rtc: pcf2127: add support for multiple TS functions Hugo Villeneuve
@ 2023-01-07 17:58   ` Bruno Thomsen
  2023-01-23 20:41     ` Hugo Villeneuve
  0 siblings, 1 reply; 72+ messages in thread
From: Bruno Thomsen @ 2023-01-07 17:58 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt,
	linux-rtc, devicetree, linux-kernel, Hugo Villeneuve

Den tor. 15. dec. 2022 kl. 16.48 skrev Hugo Villeneuve <hugo@hugovil.com>:
>
> From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
>
> This will simplify the implementation of new variants into this driver.
>
> Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> ---
>  drivers/rtc/rtc-pcf2127.c | 303 +++++++++++++++++++++++++++-----------
>  1 file changed, 215 insertions(+), 88 deletions(-)
>
> diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> index 38816ad065eb..3265878edc48 100644
> --- a/drivers/rtc/rtc-pcf2127.c
> +++ b/drivers/rtc/rtc-pcf2127.c
> @@ -75,16 +75,19 @@
>  #define PCF2127_BIT_WD_CTL_CD0                 BIT(6)
>  #define PCF2127_BIT_WD_CTL_CD1                 BIT(7)
>  #define PCF2127_REG_WD_VAL             0x11
> -/* Tamper timestamp registers */
> -#define PCF2127_REG_TS_CTRL            0x12
> +/* Tamper timestamp1 registers */
> +#define PCF2127_REG_TS1_BASE           0x12
> +/* Tamper timestamp registers common offsets (starting from base register) */
> +#define PCF2127_OFFSET_TS_CTL          0
> +#define PCF2127_OFFSET_TS_SC           1
> +#define PCF2127_OFFSET_TS_MN           2
> +#define PCF2127_OFFSET_TS_HR           3
> +#define PCF2127_OFFSET_TS_DM           4
> +#define PCF2127_OFFSET_TS_MO           5
> +#define PCF2127_OFFSET_TS_YR           6
> +/* Tamper timestamp registers common bits */
>  #define PCF2127_BIT_TS_CTRL_TSOFF              BIT(6)
>  #define PCF2127_BIT_TS_CTRL_TSM                        BIT(7)
> -#define PCF2127_REG_TS_SC              0x13
> -#define PCF2127_REG_TS_MN              0x14
> -#define PCF2127_REG_TS_HR              0x15
> -#define PCF2127_REG_TS_DM              0x16
> -#define PCF2127_REG_TS_MO              0x17
> -#define PCF2127_REG_TS_YR              0x18
>  /*
>   * RAM registers
>   * PCF2127 has 512 bytes general-purpose static RAM (SRAM) that is
> @@ -108,6 +111,20 @@
>                 PCF2127_BIT_CTRL2_WDTF | \
>                 PCF2127_BIT_CTRL2_TSF2)
>
> +struct pcf21xx_ts_config {
> +       u8 regs_base; /* Base register to read timestamp values. */

reg_base as there is only one.

> +       /* TS input pin driven to GND detection (supported by all variants): */
> +       u8 low_reg; /* Interrupt control register. */
> +       u8 low_bit; /* Interrupt flag in low_reg control register. */
> +       /* TS input pin driven to intermediate level between GND and supply
> +        * detection (optional feature depending on variant):
> +        */
> +       u8 inter_reg; /* Interrupt control register. */
> +       u8 inter_bit; /* Interrupt flag in inter_reg control register. */

I had to read this a couple of times to understand it :)

Maybe rename variables to low_ctl_reg, low_ctl_bit,
inter_ctl_reg and inter_ctl_bit.

> +       u8 ie_reg; /* Interrupt enable control register. */
> +       u8 ie_bit; /* Interrupt enable bit. */
> +};
> +
>  struct pcf21xx_config {
>         int max_register;
>         unsigned int has_nvmem:1;
> @@ -117,6 +134,9 @@ struct pcf21xx_config {
>         u8 reg_wd_ctl; /* Watchdog control register. */
>         u8 reg_wd_val; /* Watchdog value register. */
>         u8 reg_clkout; /* Clkout register. */
> +       unsigned int ts_count;
> +       struct pcf21xx_ts_config ts[4];

You should create a driver constant with the maximum number
of supported timestamps, something like:

#define PCF2127_MAX_TS_SUPPORTED  1

and raise it to 4 when pcf2131 support is added.

> +       struct attribute_group attribute_group;
>  };
>
>  struct pcf2127 {
> @@ -124,9 +144,9 @@ struct pcf2127 {
>         struct watchdog_device wdd;
>         struct regmap *regmap;
>         const struct pcf21xx_config *cfg;
> -       time64_t ts;
> -       bool ts_valid;
>         bool irq_enabled;
> +       time64_t ts[4]; /* Timestamp values. */
> +       bool ts_valid[4];  /* Timestamp valid indication. */
>  };
>
>  /*
> @@ -469,38 +489,39 @@ static int pcf2127_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
>  }
>
>  /*
> - * This function reads ctrl2 register, caller is responsible for calling
> - * pcf2127_wdt_active_ping()
> + * This function reads one timestamp function data, caller is responsible for
> + * calling pcf2127_wdt_active_ping()
>   */
> -static int pcf2127_rtc_ts_read(struct device *dev, time64_t *ts)
> +static int pcf2127_rtc_ts_read(struct device *dev, time64_t *ts,
> +                              int ts_id)
>  {
>         struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
>         struct rtc_time tm;
>         int ret;
> -       unsigned char data[25];
> +       unsigned char data[7]; /* To store result of reading 7 consecutive
> +                               * timestamp registers.
> +                               */

Remove comment about data variable.

>
> -       ret = regmap_bulk_read(pcf2127->regmap, PCF2127_REG_CTRL1, data,
> -                              sizeof(data));
> +       ret = regmap_bulk_read(pcf2127->regmap, pcf2127->cfg->ts[ts_id].regs_base,
> +                              data, sizeof(data));
>         if (ret) {
> -               dev_err(dev, "%s: read error ret=%d\n", __func__, ret);
> +               dev_err(dev, "%s: bulk read error ret=%d\n", __func__, ret);
>                 return ret;
>         }
>
>         dev_dbg(dev,
> -               "%s: raw data is cr1=%02x, cr2=%02x, cr3=%02x, ts_sc=%02x, ts_mn=%02x, ts_hr=%02x, ts_dm=%02x, ts_mo=%02x, ts_yr=%02x\n",
> -               __func__, data[PCF2127_REG_CTRL1], data[PCF2127_REG_CTRL2],
> -               data[PCF2127_REG_CTRL3], data[PCF2127_REG_TS_SC],
> -               data[PCF2127_REG_TS_MN], data[PCF2127_REG_TS_HR],
> -               data[PCF2127_REG_TS_DM], data[PCF2127_REG_TS_MO],
> -               data[PCF2127_REG_TS_YR]);
> -
> -       tm.tm_sec = bcd2bin(data[PCF2127_REG_TS_SC] & 0x7F);
> -       tm.tm_min = bcd2bin(data[PCF2127_REG_TS_MN] & 0x7F);
> -       tm.tm_hour = bcd2bin(data[PCF2127_REG_TS_HR] & 0x3F);
> -       tm.tm_mday = bcd2bin(data[PCF2127_REG_TS_DM] & 0x3F);
> +               "%s: raw data is ts_sc=%02x, ts_mn=%02x, ts_hr=%02x, ts_dm=%02x, ts_mo=%02x, ts_yr=%02x\n",
> +               __func__, data[PCF2127_OFFSET_TS_SC], data[PCF2127_OFFSET_TS_MN],
> +               data[PCF2127_OFFSET_TS_HR], data[PCF2127_OFFSET_TS_DM],
> +               data[PCF2127_OFFSET_TS_MO], data[PCF2127_OFFSET_TS_YR]);
> +
> +       tm.tm_sec = bcd2bin(data[PCF2127_OFFSET_TS_SC] & 0x7F);
> +       tm.tm_min = bcd2bin(data[PCF2127_OFFSET_TS_MN] & 0x7F);
> +       tm.tm_hour = bcd2bin(data[PCF2127_OFFSET_TS_HR] & 0x3F);
> +       tm.tm_mday = bcd2bin(data[PCF2127_OFFSET_TS_DM] & 0x3F);
>         /* TS_MO register (month) value range: 1-12 */
> -       tm.tm_mon = bcd2bin(data[PCF2127_REG_TS_MO] & 0x1F) - 1;
> -       tm.tm_year = bcd2bin(data[PCF2127_REG_TS_YR]);
> +       tm.tm_mon = bcd2bin(data[PCF2127_OFFSET_TS_MO] & 0x1F) - 1;
> +       tm.tm_year = bcd2bin(data[PCF2127_OFFSET_TS_YR]);
>         if (tm.tm_year < 70)
>                 tm.tm_year += 100; /* assume we are in 1970...2069 */
>
> @@ -514,18 +535,21 @@ static int pcf2127_rtc_ts_read(struct device *dev, time64_t *ts)
>         return 0;
>  };
>
> -static void pcf2127_rtc_ts_snapshot(struct device *dev)
> +static void pcf2127_rtc_ts_snapshot(struct device *dev, int ts_id)
>  {
>         struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
>         int ret;
>
> +       if (ts_id >= pcf2127->cfg->ts_count)
> +               return;
> +
>         /* Let userspace read the first timestamp */
> -       if (pcf2127->ts_valid)
> +       if (pcf2127->ts_valid[ts_id])
>                 return;
>
> -       ret = pcf2127_rtc_ts_read(dev, &pcf2127->ts);
> +       ret = pcf2127_rtc_ts_read(dev, &pcf2127->ts[ts_id], ts_id);
>         if (!ret)
> -               pcf2127->ts_valid = true;
> +               pcf2127->ts_valid[ts_id] = true;
>  }
>
>  static irqreturn_t pcf2127_rtc_irq(int irq, void *dev)
> @@ -546,7 +570,7 @@ static irqreturn_t pcf2127_rtc_irq(int irq, void *dev)
>                 return IRQ_NONE;
>
>         if (ctrl1 & PCF2127_BIT_CTRL1_TSF1 || ctrl2 & PCF2127_BIT_CTRL2_TSF2)
> -               pcf2127_rtc_ts_snapshot(dev);
> +               pcf2127_rtc_ts_snapshot(dev, 0);
>
>         if (ctrl1 & PCF2127_CTRL1_IRQ_MASK)
>                 regmap_write(pcf2127->regmap, PCF2127_REG_CTRL1,
> @@ -575,28 +599,40 @@ static const struct rtc_class_ops pcf2127_rtc_ops = {
>
>  /* sysfs interface */
>
> -static ssize_t timestamp0_store(struct device *dev,
> -                               struct device_attribute *attr,
> -                               const char *buf, size_t count)
> +static ssize_t timestamp_store(struct device *dev,
> +                              struct device_attribute *attr,
> +                              const char *buf, size_t count, int ts_id)
>  {
>         struct pcf2127 *pcf2127 = dev_get_drvdata(dev->parent);
>         int ret;
>
> +       if (ts_id >= pcf2127->cfg->ts_count)
> +               return 0;
> +
>         if (pcf2127->irq_enabled) {
> -               pcf2127->ts_valid = false;
> +               pcf2127->ts_valid[ts_id] = false;
>         } else {
> -               ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL1,
> -                       PCF2127_BIT_CTRL1_TSF1, 0);
> +               /* Always clear LOW interrupt bit. */
> +               ret = regmap_update_bits(pcf2127->regmap,
> +                                        pcf2127->cfg->ts[ts_id].low_reg,
> +                                        pcf2127->cfg->ts[ts_id].low_bit,
> +                                        0);
> +
>                 if (ret) {
> -                       dev_err(dev, "%s: update ctrl1 ret=%d\n", __func__, ret);
> +                       dev_err(dev, "%s: update TS low ret=%d\n", __func__, ret);
>                         return ret;
>                 }
>
> -               ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL2,
> -                       PCF2127_BIT_CTRL2_TSF2, 0);
> -               if (ret) {
> -                       dev_err(dev, "%s: update ctrl2 ret=%d\n", __func__, ret);
> -                       return ret;
> +               if (pcf2127->cfg->ts[ts_id].inter_bit) {
> +                       /* Clear INTERMEDIATE interrupt bit if supported. */
> +                       ret = regmap_update_bits(pcf2127->regmap,
> +                                                pcf2127->cfg->ts[ts_id].inter_reg,
> +                                                pcf2127->cfg->ts[ts_id].inter_bit,
> +                                                0);
> +                       if (ret) {
> +                               dev_err(dev, "%s: update TS intermediate ret=%d\n", __func__, ret);
> +                               return ret;
> +                       }
>                 }
>
>                 ret = pcf2127_wdt_active_ping(&pcf2127->wdd);
> @@ -605,34 +641,63 @@ static ssize_t timestamp0_store(struct device *dev,
>         }
>
>         return count;
> +}
> +
> +static ssize_t timestamp0_store(struct device *dev,
> +                               struct device_attribute *attr,
> +                               const char *buf, size_t count)
> +{
> +       return timestamp_store(dev, attr, buf, count, 0);
>  };
>
> -static ssize_t timestamp0_show(struct device *dev,
> -                              struct device_attribute *attr, char *buf)
> +static ssize_t timestamp_show(struct device *dev,
> +                             struct device_attribute *attr, char *buf,
> +                             int ts_id)
>  {
>         struct pcf2127 *pcf2127 = dev_get_drvdata(dev->parent);
> -       unsigned int ctrl1, ctrl2;
>         int ret;
>         time64_t ts;
>
> +       if (ts_id >= pcf2127->cfg->ts_count)
> +               return 0;
> +
>         if (pcf2127->irq_enabled) {
> -               if (!pcf2127->ts_valid)
> +               if (!pcf2127->ts_valid[ts_id])
>                         return 0;
> -               ts = pcf2127->ts;
> +               ts = pcf2127->ts[ts_id];
>         } else {
> -               ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL1, &ctrl1);
> -               if (ret)
> -                       return 0;
> +               u8 valid_low = 0;
> +               u8 valid_inter = 0;
> +               unsigned int ctrl;
>
> -               ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL2, &ctrl2);
> +               /* Check if TS input pin is driven to GND, supported by all
> +                * variants.
> +                */
> +               ret = regmap_read(pcf2127->regmap,
> +                                 pcf2127->cfg->ts[ts_id].low_reg,
> +                                 &ctrl);
>                 if (ret)
>                         return 0;
>
> -               if (!(ctrl1 & PCF2127_BIT_CTRL1_TSF1) &&
> -                   !(ctrl2 & PCF2127_BIT_CTRL2_TSF2))
> +               valid_low = ctrl & pcf2127->cfg->ts[ts_id].low_bit;
> +
> +               if (pcf2127->cfg->ts[ts_id].inter_bit) {
> +                       /* Check if TS input pin is driven to intermediate level
> +                        * between GND and supply, if supported by variant.
> +                        */
> +                       ret = regmap_read(pcf2127->regmap,
> +                                         pcf2127->cfg->ts[ts_id].inter_reg,
> +                                         &ctrl);
> +                       if (ret)
> +                               return 0;
> +
> +                       valid_inter = ctrl & pcf2127->cfg->ts[ts_id].inter_bit;
> +               }
> +
> +               if (!valid_low && !valid_inter)
>                         return 0;
>
> -               ret = pcf2127_rtc_ts_read(dev->parent, &ts);
> +               ret = pcf2127_rtc_ts_read(dev->parent, &ts, ts_id);
>                 if (ret)
>                         return 0;
>
> @@ -641,6 +706,12 @@ static ssize_t timestamp0_show(struct device *dev,
>                         return ret;
>         }
>         return sprintf(buf, "%llu\n", (unsigned long long)ts);
> +}
> +
> +static ssize_t timestamp0_show(struct device *dev,
> +                              struct device_attribute *attr, char *buf)
> +{
> +       return timestamp_show(dev, attr, buf, 0);
>  };
>
>  static DEVICE_ATTR_RW(timestamp0);
> @@ -650,10 +721,6 @@ static struct attribute *pcf2127_attrs[] = {
>         NULL
>  };
>
> -static const struct attribute_group pcf2127_attr_group = {
> -       .attrs  = pcf2127_attrs,
> -};
> -
>  enum pcf21xx_type {
>         PCF2127,
>         PCF2129,
> @@ -670,6 +737,19 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
>                 .reg_wd_ctl = PCF2127_REG_WD_CTL,
>                 .reg_wd_val = PCF2127_REG_WD_VAL,
>                 .reg_clkout = PCF2127_REG_CLKOUT,
> +               .ts_count = 1,
> +               .ts[0] = {
> +                       .regs_base = PCF2127_REG_TS1_BASE,
> +                       .low_reg   = PCF2127_REG_CTRL1,
> +                       .low_bit   = PCF2127_BIT_CTRL1_TSF1,
> +                       .inter_reg = PCF2127_REG_CTRL2,
> +                       .inter_bit = PCF2127_BIT_CTRL2_TSF2,
> +                       .ie_reg    = PCF2127_REG_CTRL2,
> +                       .ie_bit    = PCF2127_BIT_CTRL2_TSIE,
> +               },
> +               .attribute_group = {
> +                       .attrs  = pcf2127_attrs,
> +               },
>         },
>         [PCF2129] = {
>                 .max_register = 0x19,
> @@ -680,15 +760,81 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
>                 .reg_wd_ctl = PCF2127_REG_WD_CTL,
>                 .reg_wd_val = PCF2127_REG_WD_VAL,
>                 .reg_clkout = PCF2127_REG_CLKOUT,
> +               .ts_count = 1,
> +               .ts[0] = {
> +                       .regs_base = PCF2127_REG_TS1_BASE,
> +                       .low_reg   = PCF2127_REG_CTRL1,
> +                       .low_bit   = PCF2127_BIT_CTRL1_TSF1,
> +                       .inter_reg = PCF2127_REG_CTRL2,
> +                       .inter_bit = PCF2127_BIT_CTRL2_TSF2,
> +                       .ie_reg    = PCF2127_REG_CTRL2,
> +                       .ie_bit    = PCF2127_BIT_CTRL2_TSIE,
> +               },
> +               .attribute_group = {
> +                       .attrs  = pcf2127_attrs,
> +               },
>         },
>  };
>
> +/*
> + * Enable timestamp function and corresponding interrupt(s).
> + */
> +static int pcf2127_enable_ts(struct device *dev, int ts_id)
> +{
> +       struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
> +       int ret;
> +
> +       if (ts_id >= pcf2127->cfg->ts_count) {
> +               dev_err(dev, "%s: invalid tamper detection ID (%d)\n",
> +                       __func__, ts_id);
> +               return -EINVAL;
> +       }
> +
> +       /* Enable timestamp function. */
> +       ret = regmap_update_bits(pcf2127->regmap,
> +                                pcf2127->cfg->ts[ts_id].regs_base,
> +                                PCF2127_BIT_TS_CTRL_TSOFF |
> +                                PCF2127_BIT_TS_CTRL_TSM,
> +                                PCF2127_BIT_TS_CTRL_TSM);
> +       if (ret) {
> +               dev_err(dev, "%s: tamper detection config (ts%d_ctrl) failed\n",
> +                       __func__, ts_id);
> +               return ret;
> +       }
> +
> +       /* TS input pin driven to GND detection is supported by all variants.
> +        * Make sure that low_bit is defined.
> +        */
> +       if (pcf2127->cfg->ts[ts_id].low_bit == 0) {
> +               dev_err(dev, "%s: tamper detection LOW configuration invalid\n",
> +                       __func__);
> +               return ret;
> +       }
> +
> +       /*
> +        * Enable interrupt generation when TSF timestamp flag is set.
> +        * Interrupt signals are open-drain outputs and can be left floating if
> +        * unused.
> +        */
> +       ret = regmap_update_bits(pcf2127->regmap, pcf2127->cfg->ts[ts_id].ie_reg,
> +                                pcf2127->cfg->ts[ts_id].ie_bit,
> +                                pcf2127->cfg->ts[ts_id].ie_bit);
> +       if (ret) {
> +               dev_err(dev, "%s: tamper detection TSIE%d config failed\n",
> +                       __func__, ts_id);
> +               return ret;
> +       }
> +
> +       return ret;
> +}
> +
>  static int pcf2127_probe(struct device *dev, struct regmap *regmap,
>                          int alarm_irq, const char *name, const struct pcf21xx_config *config)
>  {
>         struct pcf2127 *pcf2127;
>         int ret = 0;
>         unsigned int val;
> +       int i;
>
>         dev_dbg(dev, "%s\n", __func__);
>
> @@ -813,34 +959,15 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
>         }
>
>         /*
> -        * Enable timestamp function and store timestamp of first trigger
> -        * event until TSF1 and TSF2 interrupt flags are cleared.
> +        * Enable timestamp functions 1 to 4.
>          */
> -       ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_TS_CTRL,
> -                                PCF2127_BIT_TS_CTRL_TSOFF |
> -                                PCF2127_BIT_TS_CTRL_TSM,
> -                                PCF2127_BIT_TS_CTRL_TSM);
> -       if (ret) {
> -               dev_err(dev, "%s: tamper detection config (ts_ctrl) failed\n",
> -                       __func__);
> -               return ret;
> -       }
> -
> -       /*
> -        * Enable interrupt generation when TSF1 or TSF2 timestamp flags
> -        * are set. Interrupt signal is an open-drain output and can be
> -        * left floating if unused.
> -        */
> -       ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL2,
> -                                PCF2127_BIT_CTRL2_TSIE,
> -                                PCF2127_BIT_CTRL2_TSIE);
> -       if (ret) {
> -               dev_err(dev, "%s: tamper detection config (ctrl2) failed\n",
> -                       __func__);
> -               return ret;
> +       for (i = 0; i < pcf2127->cfg->ts_count; i++) {

Just define the loop counter variable inline.

for (int i = 0; i < pcf2127->cfg->ts_count; i++) {

> +               ret = pcf2127_enable_ts(dev, i);
> +               if (ret)
> +                       return ret;
>         }
>
> -       ret = rtc_add_group(pcf2127->rtc, &pcf2127_attr_group);
> +       ret = rtc_add_group(pcf2127->rtc, &pcf2127->cfg->attribute_group);
>         if (ret) {
>                 dev_err(dev, "%s: tamper sysfs registering failed\n",
>                         __func__);
> --
> 2.30.2
>

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

* Re: [PATCH v3 07/14] rtc: pcf2127: add support for PCF2131 RTC
  2022-12-15 15:02 ` [PATCH v3 07/14] rtc: pcf2127: add support for PCF2131 RTC Hugo Villeneuve
@ 2023-01-07 18:15   ` Bruno Thomsen
  2023-01-23 19:06     ` Hugo Villeneuve
  2023-01-20 18:57   ` Alexandre Belloni
  1 sibling, 1 reply; 72+ messages in thread
From: Bruno Thomsen @ 2023-01-07 18:15 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt,
	linux-rtc, devicetree, linux-kernel, Hugo Villeneuve

Den tor. 15. dec. 2022 kl. 16.20 skrev Hugo Villeneuve <hugo@hugovil.com>:
>
> From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
>
> This RTC is very similar in functionality to the PCF2127/29.
>
> Basically it:
>   -supports two new control registers at offsets 4 and 5
>   -supports a new reset register (not implemented in this driver)
>   -supports 4 tamper detection functions instead of 1
>   -has no nvmem (like the PCF2129)
>   -has two output interrupt pins
>
> Because of that, most of the register addresses are very different,
> although they still follow the same layout. For example, the tamper
> registers have a different base address, but the offsets are all the same.
>
> Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> ---
>  drivers/rtc/Kconfig       |   4 +-
>  drivers/rtc/rtc-pcf2127.c | 234 ++++++++++++++++++++++++++++++++++----
>  2 files changed, 215 insertions(+), 23 deletions(-)
>
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index 2bb640d1521d..3d4043ce0057 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -910,9 +910,9 @@ config RTC_DRV_PCF2127
>         select REGMAP_SPI if SPI_MASTER
>         select WATCHDOG_CORE if WATCHDOG
>         help
> -         If you say yes here you get support for the NXP PCF2127/29 RTC
> +         If you say yes here you get support for the NXP PCF2127/29/31 RTC
>           chips with integrated quartz crystal for industrial applications.
> -         Both chips also have watchdog timer and tamper switch detection
> +         These chips also have watchdog timer and tamper switch detection
>           features.
>
>           PCF2127 has an additional feature of 512 bytes battery backed
> diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> index 3265878edc48..4148e135f935 100644
> --- a/drivers/rtc/rtc-pcf2127.c
> +++ b/drivers/rtc/rtc-pcf2127.c
> @@ -1,16 +1,26 @@
>  // SPDX-License-Identifier: GPL-2.0-only
>  /*
> - * An I2C and SPI driver for the NXP PCF2127/29 RTC
> + * An I2C and SPI driver for the NXP PCF2127/29/31 RTC
>   * Copyright 2013 Til-Technologies
> + * Copyright 2021 DimOnOff
>   *
>   * Author: Renaud Cerrato <r.cerrato@til-technologies.fr>
>   *
>   * Watchdog and tamper functions
>   * Author: Bruno Thomsen <bruno.thomsen@gmail.com>
>   *
> + * PCF2131 support
> + * Author: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> + *
>   * based on the other drivers in this same directory.
>   *
> - * Datasheet: https://www.nxp.com/docs/en/data-sheet/PCF2127.pdf
> + * Datasheets: https://www.nxp.com/docs/en/data-sheet/PCF2127.pdf
> + *             https://www.nxp.com/docs/en/data-sheet/PCF2131DS.pdf
> + */
> +
> +/*
> + * The following features are not yet implemented for the PCF2131:
> + *   - support for 1/100th seconds
>   */
>
>  #include <linux/i2c.h>
> @@ -43,8 +53,30 @@
>  #define PCF2127_BIT_CTRL3_BLF                  BIT(2)
>  #define PCF2127_BIT_CTRL3_BF                   BIT(3)
>  #define PCF2127_BIT_CTRL3_BTSE                 BIT(4)
> +/* Control register 4 */
> +#define PCF2131_REG_CTRL4              0x03
> +#define PCF2131_BIT_CTRL4_TSF4                 BIT(4)
> +#define PCF2131_BIT_CTRL4_TSF3                 BIT(5)
> +#define PCF2131_BIT_CTRL4_TSF2                 BIT(6)
> +#define PCF2131_BIT_CTRL4_TSF1                 BIT(7)
> +/* Control register 5 */
> +#define PCF2131_REG_CTRL5              0x04
> +#define PCF2131_BIT_CTRL5_TSIE4                        BIT(4)
> +#define PCF2131_BIT_CTRL5_TSIE3                        BIT(5)
> +#define PCF2131_BIT_CTRL5_TSIE2                        BIT(6)
> +#define PCF2131_BIT_CTRL5_TSIE1                        BIT(7)
> +/* Software reset register */
> +#define PCF2131_REG_SR_RESET           0x05
> +#define PCF2131_SR_RESET_READ_PATTERN  0b00100100 /* Fixed pattern. */

Replace 0b00100100 with (BIT(2) | BIT(5))

> +#define PCF2131_SR_RESET_RESET_CMD     0x2C /* SR is bit 3. */

Replace 0x2C with (BIT(2) | BIT(3) | BIT(5)) or
(PCF2131_SR_RESET_READ_PATTERN | BIT(3))
and then drop the comment.

>  /* Time and date registers */
>  #define PCF2127_REG_TIME_DATE_BASE     0x03
> +#define PCF2131_REG_TIME_DATE_BASE     0x07 /* Register 0x06 is 100th seconds,
> +                                             * but we do not support it. By
> +                                             * using offset 0x07, we can be
> +                                             * compatible with existing
> +                                             * time/date functions.
> +                                             */

I think you should add this rather big comment above
#define PCF2131_REG_TIME_DATE_BASE 0x07

and rename the define to PCF2131_REG_TIME_BASE.

>  /* Time and date registers offsets (starting from base register) */
>  #define PCF2127_OFFSET_TD_SC           0
>  #define PCF2127_OFFSET_TD_MN           1
> @@ -57,6 +89,7 @@
>  #define PCF2127_BIT_SC_OSF                     BIT(7)
>  /* Alarm registers */
>  #define PCF2127_REG_ALARM_BASE         0x0A
> +#define PCF2131_REG_ALARM_BASE         0x0E
>  /* Alarm registers offsets (starting from base register) */
>  #define PCF2127_OFFSET_ALARM_SC                0
>  #define PCF2127_OFFSET_ALARM_MN                1
> @@ -67,16 +100,26 @@
>  #define PCF2127_BIT_ALARM_AE                   BIT(7)
>  /* CLKOUT control register */
>  #define PCF2127_REG_CLKOUT             0x0f
> +#define PCF2131_REG_CLKOUT             0x13
>  #define PCF2127_BIT_CLKOUT_OTPR                        BIT(5)
>  /* Watchdog registers */
>  #define PCF2127_REG_WD_CTL             0x10
> +#define PCF2131_REG_WD_CTL             0x35
>  #define PCF2127_BIT_WD_CTL_TF0                 BIT(0)
>  #define PCF2127_BIT_WD_CTL_TF1                 BIT(1)
>  #define PCF2127_BIT_WD_CTL_CD0                 BIT(6)
>  #define PCF2127_BIT_WD_CTL_CD1                 BIT(7)
>  #define PCF2127_REG_WD_VAL             0x11
> +#define PCF2131_REG_WD_VAL             0x36
>  /* Tamper timestamp1 registers */
>  #define PCF2127_REG_TS1_BASE           0x12
> +#define PCF2131_REG_TS1_BASE           0x14
> +/* Tamper timestamp2 registers */
> +#define PCF2131_REG_TS2_BASE           0x1B
> +/* Tamper timestamp3 registers */
> +#define PCF2131_REG_TS3_BASE           0x22
> +/* Tamper timestamp4 registers */
> +#define PCF2131_REG_TS4_BASE           0x29
>  /* Tamper timestamp registers common offsets (starting from base register) */
>  #define PCF2127_OFFSET_TS_CTL          0
>  #define PCF2127_OFFSET_TS_SC           1
> @@ -92,11 +135,22 @@
>   * RAM registers
>   * PCF2127 has 512 bytes general-purpose static RAM (SRAM) that is
>   * battery backed and can survive a power outage.
> - * PCF2129 doesn't have this feature.
> + * PCF2129/31 doesn't have this feature.
>   */
>  #define PCF2127_REG_RAM_ADDR_MSB       0x1A
>  #define PCF2127_REG_RAM_WRT_CMD                0x1C
>  #define PCF2127_REG_RAM_RD_CMD         0x1D
> +/* Interrupt mask registers */
> +#define PCF2131_REG_INT_A_MASK1                0x31
> +#define PCF2131_REG_INT_A_MASK2                0x32
> +#define PCF2131_REG_INT_B_MASK1                0x33
> +#define PCF2131_REG_INT_B_MASK2                0x34
> +#define PCF2131_BIT_INT_BLIE           BIT(0)
> +#define PCF2131_BIT_INT_BIE            BIT(1)
> +#define PCF2131_BIT_INT_AIE            BIT(2)
> +#define PCF2131_BIT_INT_WD_CD          BIT(3)
> +#define PCF2131_BIT_INT_SI             BIT(4)
> +#define PCF2131_BIT_INT_MI             BIT(5)
>
>  /* Watchdog timer value constants */
>  #define PCF2127_WD_VAL_STOP            0
> @@ -110,6 +164,14 @@
>                 PCF2127_BIT_CTRL2_AF | \
>                 PCF2127_BIT_CTRL2_WDTF | \
>                 PCF2127_BIT_CTRL2_TSF2)
> +#define PCF2131_CTRL2_IRQ_MASK ( \
> +               PCF2127_BIT_CTRL2_AF | \
> +               PCF2127_BIT_CTRL2_WDTF)
> +#define PCF2131_CTRL4_IRQ_MASK ( \
> +               PCF2131_BIT_CTRL4_TSF4 | \
> +               PCF2131_BIT_CTRL4_TSF3 | \
> +               PCF2131_BIT_CTRL4_TSF2 | \
> +               PCF2131_BIT_CTRL4_TSF1)
>
>  struct pcf21xx_ts_config {
>         u8 regs_base; /* Base register to read timestamp values. */
> @@ -370,7 +432,7 @@ static int pcf2127_wdt_set_timeout(struct watchdog_device *wdd,
>  }
>
>  static const struct watchdog_info pcf2127_wdt_info = {
> -       .identity = "NXP PCF2127/PCF2129 Watchdog",
> +       .identity = "NXP PCF2127/29/31 Watchdog",
>         .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT,
>  };
>
> @@ -555,30 +617,64 @@ static void pcf2127_rtc_ts_snapshot(struct device *dev, int ts_id)
>  static irqreturn_t pcf2127_rtc_irq(int irq, void *dev)
>  {
>         struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
> -       unsigned int ctrl1, ctrl2;
> +       unsigned int ctrl2;
>         int ret = 0;
>
> -       ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL1, &ctrl1);
> -       if (ret)
> -               return IRQ_NONE;
> -
>         ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL2, &ctrl2);
>         if (ret)
>                 return IRQ_NONE;
>
> -       if (!(ctrl1 & PCF2127_CTRL1_IRQ_MASK || ctrl2 & PCF2127_CTRL2_IRQ_MASK))
> -               return IRQ_NONE;
> +       if (pcf2127->cfg->ts_count == 1) {
> +               /* PCF2127/29 */
> +               unsigned int ctrl1;
> +
> +               ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL1, &ctrl1);
> +               if (ret)
> +                       return IRQ_NONE;
> +
> +               if (!(ctrl1 & PCF2127_CTRL1_IRQ_MASK || ctrl2 & PCF2127_CTRL2_IRQ_MASK))
> +                       return IRQ_NONE;
>
> -       if (ctrl1 & PCF2127_BIT_CTRL1_TSF1 || ctrl2 & PCF2127_BIT_CTRL2_TSF2)
> -               pcf2127_rtc_ts_snapshot(dev, 0);
> +               if (ctrl1 & PCF2127_BIT_CTRL1_TSF1 || ctrl2 & PCF2127_BIT_CTRL2_TSF2)
> +                       pcf2127_rtc_ts_snapshot(dev, 0);
>
> -       if (ctrl1 & PCF2127_CTRL1_IRQ_MASK)
> -               regmap_write(pcf2127->regmap, PCF2127_REG_CTRL1,
> -                       ctrl1 & ~PCF2127_CTRL1_IRQ_MASK);
> +               if (ctrl1 & PCF2127_CTRL1_IRQ_MASK)
> +                       regmap_write(pcf2127->regmap, PCF2127_REG_CTRL1,
> +                                    ctrl1 & ~PCF2127_CTRL1_IRQ_MASK);
> +
> +               if (ctrl2 & PCF2127_CTRL2_IRQ_MASK)
> +                       regmap_write(pcf2127->regmap, PCF2127_REG_CTRL2,
> +                                    ctrl2 & ~PCF2127_CTRL2_IRQ_MASK);
> +       } else {
> +               /* PCF2131. */
> +               unsigned int ctrl4;
> +
> +               ret = regmap_read(pcf2127->regmap, PCF2131_REG_CTRL4, &ctrl4);
> +               if (ret)
> +                       return IRQ_NONE;
>
> -       if (ctrl2 & PCF2127_CTRL2_IRQ_MASK)
> -               regmap_write(pcf2127->regmap, PCF2127_REG_CTRL2,
> -                       ctrl2 & ~PCF2127_CTRL2_IRQ_MASK);
> +               if (!(ctrl4 & PCF2131_CTRL4_IRQ_MASK || ctrl2 & PCF2131_CTRL2_IRQ_MASK))
> +                       return IRQ_NONE;
> +
> +               if (ctrl4 & PCF2131_CTRL4_IRQ_MASK) {
> +                       int i;
> +                       int tsf_bit = PCF2131_BIT_CTRL4_TSF1; /* Start at bit 7. */
> +
> +                       for (i = 0; i < pcf2127->cfg->ts_count; i++) {
> +                               if (ctrl4 & tsf_bit)
> +                                       pcf2127_rtc_ts_snapshot(dev, i);
> +
> +                               tsf_bit = tsf_bit >> 1;
> +                       }
> +
> +                       regmap_write(pcf2127->regmap, PCF2131_REG_CTRL4,
> +                                    ctrl4 & ~PCF2131_CTRL4_IRQ_MASK);
> +               }
> +
> +               if (ctrl2 & PCF2131_CTRL2_IRQ_MASK)
> +                       regmap_write(pcf2127->regmap, PCF2127_REG_CTRL2,
> +                                    ctrl2 & ~PCF2131_CTRL2_IRQ_MASK);
> +       }
>
>         if (ctrl2 & PCF2127_BIT_CTRL2_AF)
>                 rtc_update_irq(pcf2127->rtc, 1, RTC_IRQF | RTC_AF);
> @@ -650,6 +746,27 @@ static ssize_t timestamp0_store(struct device *dev,
>         return timestamp_store(dev, attr, buf, count, 0);
>  };
>
> +static ssize_t timestamp1_store(struct device *dev,
> +                               struct device_attribute *attr,
> +                               const char *buf, size_t count)
> +{
> +       return timestamp_store(dev, attr, buf, count, 1);
> +};
> +
> +static ssize_t timestamp2_store(struct device *dev,
> +                               struct device_attribute *attr,
> +                               const char *buf, size_t count)
> +{
> +       return timestamp_store(dev, attr, buf, count, 2);
> +};
> +
> +static ssize_t timestamp3_store(struct device *dev,
> +                               struct device_attribute *attr,
> +                               const char *buf, size_t count)
> +{
> +       return timestamp_store(dev, attr, buf, count, 3);
> +};
> +
>  static ssize_t timestamp_show(struct device *dev,
>                               struct device_attribute *attr, char *buf,
>                               int ts_id)
> @@ -714,16 +831,46 @@ static ssize_t timestamp0_show(struct device *dev,
>         return timestamp_show(dev, attr, buf, 0);
>  };
>
> +static ssize_t timestamp1_show(struct device *dev,
> +                              struct device_attribute *attr, char *buf)
> +{
> +       return timestamp_show(dev, attr, buf, 1);
> +};
> +
> +static ssize_t timestamp2_show(struct device *dev,
> +                              struct device_attribute *attr, char *buf)
> +{
> +       return timestamp_show(dev, attr, buf, 2);
> +};
> +
> +static ssize_t timestamp3_show(struct device *dev,
> +                              struct device_attribute *attr, char *buf)
> +{
> +       return timestamp_show(dev, attr, buf, 3);
> +};
> +
>  static DEVICE_ATTR_RW(timestamp0);
> +static DEVICE_ATTR_RW(timestamp1);
> +static DEVICE_ATTR_RW(timestamp2);
> +static DEVICE_ATTR_RW(timestamp3);
>
>  static struct attribute *pcf2127_attrs[] = {
>         &dev_attr_timestamp0.attr,
>         NULL
>  };
>
> +static struct attribute *pcf2131_attrs[] = {
> +       &dev_attr_timestamp0.attr,
> +       &dev_attr_timestamp1.attr,
> +       &dev_attr_timestamp2.attr,
> +       &dev_attr_timestamp3.attr,
> +       NULL
> +};
> +
>  enum pcf21xx_type {
>         PCF2127,
>         PCF2129,
> +       PCF2131,
>         PCF21XX_LAST_ID
>  };
>
> @@ -774,6 +921,48 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
>                         .attrs  = pcf2127_attrs,
>                 },
>         },
> +       [PCF2131] = {
> +               .max_register = 0x36,
> +               .has_nvmem = 0,
> +               .has_bit_wd_ctl_cd0 = 0,
> +               .regs_td_base = PCF2131_REG_TIME_DATE_BASE,
> +               .regs_alarm_base = PCF2131_REG_ALARM_BASE,
> +               .reg_wd_ctl = PCF2131_REG_WD_CTL,
> +               .reg_wd_val = PCF2131_REG_WD_VAL,
> +               .reg_clkout = PCF2131_REG_CLKOUT,
> +               .ts_count = 4,
> +               .ts[0] = {
> +                       .regs_base = PCF2131_REG_TS1_BASE,
> +                       .low_reg   = PCF2131_REG_CTRL4,
> +                       .low_bit   = PCF2131_BIT_CTRL4_TSF1,

Set .inter_bit to 0 as it's used/read in timestamp_store() to
control flow in if-condition.

> +                       .ie_reg    = PCF2131_REG_CTRL5,
> +                       .ie_bit    = PCF2131_BIT_CTRL5_TSIE1,
> +               },
> +               .ts[1] = {
> +                       .regs_base = PCF2131_REG_TS2_BASE,
> +                       .low_reg   = PCF2131_REG_CTRL4,
> +                       .low_bit   = PCF2131_BIT_CTRL4_TSF2,
> +                       .ie_reg    = PCF2131_REG_CTRL5,
> +                       .ie_bit    = PCF2131_BIT_CTRL5_TSIE2,
> +               },
> +               .ts[2] = {
> +                       .regs_base = PCF2131_REG_TS3_BASE,
> +                       .low_reg   = PCF2131_REG_CTRL4,
> +                       .low_bit   = PCF2131_BIT_CTRL4_TSF3,
> +                       .ie_reg    = PCF2131_REG_CTRL5,
> +                       .ie_bit    = PCF2131_BIT_CTRL5_TSIE3,
> +               },
> +               .ts[3] = {
> +                       .regs_base = PCF2131_REG_TS4_BASE,
> +                       .low_reg   = PCF2131_REG_CTRL4,
> +                       .low_bit   = PCF2131_BIT_CTRL4_TSF4,
> +                       .ie_reg    = PCF2131_REG_CTRL5,
> +                       .ie_bit    = PCF2131_BIT_CTRL5_TSIE4,
> +               },
> +               .attribute_group = {
> +                       .attrs  = pcf2131_attrs,
> +               },
> +       },
>  };
>
>  /*
> @@ -922,7 +1111,7 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
>          * Watchdog timer enabled and reset pin /RST activated when timed out.
>          * Select 1Hz clock source for watchdog timer.
>          * Note: Countdown timer disabled and not available.
> -        * For pca2129, pcf2129, only bit[7] is for Symbol WD_CD
> +        * For pca2129, pcf2129 and pcf2131, only bit[7] is for Symbol WD_CD
>          * of register watchdg_tim_ctl. The bit[6] is labeled
>          * as T. Bits labeled as T must always be written with
>          * logic 0.
> @@ -982,6 +1171,7 @@ static const struct of_device_id pcf2127_of_match[] = {
>         { .compatible = "nxp,pcf2127", .data = &pcf21xx_cfg[PCF2127] },
>         { .compatible = "nxp,pcf2129", .data = &pcf21xx_cfg[PCF2129] },
>         { .compatible = "nxp,pca2129", .data = &pcf21xx_cfg[PCF2129] },
> +       { .compatible = "nxp,pcf2131", .data = &pcf21xx_cfg[PCF2131] },
>         {}
>  };
>  MODULE_DEVICE_TABLE(of, pcf2127_of_match);
> @@ -1069,6 +1259,7 @@ static const struct i2c_device_id pcf2127_i2c_id[] = {
>         { "pcf2127", PCF2127 },
>         { "pcf2129", PCF2129 },
>         { "pca2129", PCF2129 },
> +       { "pcf2131", PCF2131 },
>         { }
>  };
>  MODULE_DEVICE_TABLE(i2c, pcf2127_i2c_id);
> @@ -1190,6 +1381,7 @@ static const struct spi_device_id pcf2127_spi_id[] = {
>         { "pcf2127", PCF2127 },
>         { "pcf2129", PCF2129 },
>         { "pca2129", PCF2129 },
> +       { "pcf2131", PCF2131 },
>         { }
>  };
>  MODULE_DEVICE_TABLE(spi, pcf2127_spi_id);
> @@ -1254,5 +1446,5 @@ static void __exit pcf2127_exit(void)
>  module_exit(pcf2127_exit)
>
>  MODULE_AUTHOR("Renaud Cerrato <r.cerrato@til-technologies.fr>");
> -MODULE_DESCRIPTION("NXP PCF2127/29 RTC driver");
> +MODULE_DESCRIPTION("NXP PCF2127/29/31 RTC driver");
>  MODULE_LICENSE("GPL v2");
> --
> 2.30.2
>

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

* Re: [PATCH v3 08/14] rtc: pcf2127: add support for PCF2131 interrupts on output INT_A
  2022-12-15 15:02 ` [PATCH v3 08/14] rtc: pcf2127: add support for PCF2131 interrupts on output INT_A Hugo Villeneuve
@ 2023-01-07 18:17   ` Bruno Thomsen
  2023-01-20 16:56   ` Alexandre Belloni
  1 sibling, 0 replies; 72+ messages in thread
From: Bruno Thomsen @ 2023-01-07 18:17 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt,
	linux-rtc, devicetree, linux-kernel, Hugo Villeneuve

Den tor. 15. dec. 2022 kl. 16.20 skrev Hugo Villeneuve <hugo@hugovil.com>:
>
> From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
>
> The PCF2127 and PCF2129 have one output interrupt pin. The PCF2131 has
> two, named INT_A and INT_B. The hardware support that any interrupt
> source can be routed to either one or both of them.
>
> Force all interrupt sources to go to the INT A pin.
>
> Support to route any interrupt source to INT A/B pins is not supported
> by this driver at the moment.
>
> Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>

Reviewed-by: Bruno Thomsen <bruno.thomsen@gmail.com>

> ---
>  drivers/rtc/rtc-pcf2127.c | 35 +++++++++++++++++++++++++++++++++++
>  1 file changed, 35 insertions(+)
>
> diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> index 4148e135f935..68af4d0438b8 100644
> --- a/drivers/rtc/rtc-pcf2127.c
> +++ b/drivers/rtc/rtc-pcf2127.c
> @@ -191,6 +191,7 @@ struct pcf21xx_config {
>         int max_register;
>         unsigned int has_nvmem:1;
>         unsigned int has_bit_wd_ctl_cd0:1;
> +       unsigned int has_int_a_b:1; /* PCF2131 supports two interrupt outputs. */
>         u8 regs_td_base; /* Time/data base registers. */
>         u8 regs_alarm_base; /* Alarm function base registers. */
>         u8 reg_wd_ctl; /* Watchdog control register. */
> @@ -879,6 +880,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
>                 .max_register = 0x1d,
>                 .has_nvmem = 1,
>                 .has_bit_wd_ctl_cd0 = 1,
> +               .has_int_a_b = 0,
>                 .regs_td_base = PCF2127_REG_TIME_DATE_BASE,
>                 .regs_alarm_base = PCF2127_REG_ALARM_BASE,
>                 .reg_wd_ctl = PCF2127_REG_WD_CTL,
> @@ -902,6 +904,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
>                 .max_register = 0x19,
>                 .has_nvmem = 0,
>                 .has_bit_wd_ctl_cd0 = 0,
> +               .has_int_a_b = 0,
>                 .regs_td_base = PCF2127_REG_TIME_DATE_BASE,
>                 .regs_alarm_base = PCF2127_REG_ALARM_BASE,
>                 .reg_wd_ctl = PCF2127_REG_WD_CTL,
> @@ -925,6 +928,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
>                 .max_register = 0x36,
>                 .has_nvmem = 0,
>                 .has_bit_wd_ctl_cd0 = 0,
> +               .has_int_a_b = 1,
>                 .regs_td_base = PCF2131_REG_TIME_DATE_BASE,
>                 .regs_alarm_base = PCF2131_REG_ALARM_BASE,
>                 .reg_wd_ctl = PCF2131_REG_WD_CTL,
> @@ -1017,6 +1021,28 @@ static int pcf2127_enable_ts(struct device *dev, int ts_id)
>         return ret;
>  }
>
> +/* Route all interrupt sources to INT A pin. */
> +static int pcf2127_configure_interrupt_pins(struct device *dev)
> +{
> +       struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
> +       int ret;
> +
> +       /* Mask bits need to be cleared to enable corresponding
> +        * interrupt source.
> +        */
> +       ret = regmap_write(pcf2127->regmap,
> +                          PCF2131_REG_INT_A_MASK1, 0);
> +       if (ret)
> +               return ret;
> +
> +       ret = regmap_write(pcf2127->regmap,
> +                          PCF2131_REG_INT_A_MASK2, 0);
> +       if (ret)
> +               return ret;
> +
> +       return ret;
> +}
> +
>  static int pcf2127_probe(struct device *dev, struct regmap *regmap,
>                          int alarm_irq, const char *name, const struct pcf21xx_config *config)
>  {
> @@ -1076,6 +1102,15 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
>                 set_bit(RTC_FEATURE_ALARM, pcf2127->rtc->features);
>         }
>
> +       if (pcf2127->cfg->has_int_a_b) {
> +               /* Configure int A/B pins, independently of alarm_irq. */
> +               ret = pcf2127_configure_interrupt_pins(dev);
> +               if (ret) {
> +                       dev_err(dev, "failed to configure interrupt pins\n");
> +                       return ret;
> +               }
> +       }
> +
>         if (pcf2127->cfg->has_nvmem) {
>                 struct nvmem_config nvmem_cfg = {
>                         .priv = pcf2127,
> --
> 2.30.2
>

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

* Re: [PATCH v3 09/14] rtc: pcf2127: set PWRMNG value for PCF2131
  2022-12-15 15:02 ` [PATCH v3 09/14] rtc: pcf2127: set PWRMNG value for PCF2131 Hugo Villeneuve
@ 2023-01-07 18:36   ` Bruno Thomsen
  2023-01-20 16:39     ` Alexandre Belloni
  0 siblings, 1 reply; 72+ messages in thread
From: Bruno Thomsen @ 2023-01-07 18:36 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt,
	linux-rtc, devicetree, linux-kernel, Hugo Villeneuve

Den tor. 15. dec. 2022 kl. 16.19 skrev Hugo Villeneuve <hugo@hugovil.com>:
>
> From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
>
> Default PWRMNG[2:0] bits are set to 000b for PCF2127/29, but to
> 111b for PCF2131.
>
> Set these bits to 000b to select same mode as PCF2127/29.
>
> Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>

Reviewed-by: Bruno Thomsen <bruno.thomsen@gmail.com>

I think it's a good idea[1] but there have been concerns about
setting default values in the past[2]. In case somebody needs
a different behaviour they should add a device tree property.

[1] https://lore.kernel.org/linux-rtc/20190910143945.9364-1-bruno.thomsen@gmail.com/
[2] https://lore.kernel.org/linux-rtc/20191211163354.GC1463890@piout.net/

> ---
>  drivers/rtc/rtc-pcf2127.c | 15 +++++++++++++++
>  1 file changed, 15 insertions(+)
>
> diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> index 68af4d0438b8..241189ee4a05 100644
> --- a/drivers/rtc/rtc-pcf2127.c
> +++ b/drivers/rtc/rtc-pcf2127.c
> @@ -53,6 +53,7 @@
>  #define PCF2127_BIT_CTRL3_BLF                  BIT(2)
>  #define PCF2127_BIT_CTRL3_BF                   BIT(3)
>  #define PCF2127_BIT_CTRL3_BTSE                 BIT(4)
> +#define PCF2127_CTRL3_PWRMNG_MASK              GENMASK(7, 5)
>  /* Control register 4 */
>  #define PCF2131_REG_CTRL4              0x03
>  #define PCF2131_BIT_CTRL4_TSF4                 BIT(4)
> @@ -1129,6 +1130,20 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
>         regmap_clear_bits(pcf2127->regmap, PCF2127_REG_CTRL1,
>                                 PCF2127_BIT_CTRL1_POR_OVRD);
>
> +       /* Make sure PWRMNG[2:0] is set to 000b. This is the default for
> +        * PCF2127/29, but not for PCF2131 (default of 111b).
> +        *
> +        * PWRMNG[2:0]  = 000b:
> +        *   battery switch-over function is enabled in standard mode;
> +        *   battery low detection function is enabled
> +        */
> +       ret = regmap_clear_bits(pcf2127->regmap, PCF2127_REG_CTRL3,
> +                               PCF2127_CTRL3_PWRMNG_MASK);
> +       if (ret < 0) {
> +               dev_err(dev, "PWRMNG config failed\n");
> +               return ret;
> +       }
> +
>         ret = regmap_read(pcf2127->regmap, pcf2127->cfg->reg_clkout, &val);
>         if (ret < 0)
>                 return ret;
> --
> 2.30.2
>

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

* Re: [PATCH v3 11/14] rtc: pcf2127: adapt time/date registers write sequence for PCF2131
  2022-12-15 15:02 ` [PATCH v3 11/14] rtc: pcf2127: adapt time/date registers write sequence for PCF2131 Hugo Villeneuve
@ 2023-01-07 18:44   ` Bruno Thomsen
  2023-01-23 20:55     ` Hugo Villeneuve
  2023-01-20 17:09   ` Alexandre Belloni
  1 sibling, 1 reply; 72+ messages in thread
From: Bruno Thomsen @ 2023-01-07 18:44 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt,
	linux-rtc, devicetree, linux-kernel, Hugo Villeneuve

Den tor. 15. dec. 2022 kl. 16.19 skrev Hugo Villeneuve <hugo@hugovil.com>:
>
> From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
>
> The sequence for updating the time/date registers is slightly
> different between PCF2127/29 and PCF2131.
>
> For PCF2127/29, during write operations, the time counting
> circuits (memory locations 03h through 09h) are automatically blocked.
>
> For PCF2131, time/date registers write access requires setting the
> STOP bit and sending the clear prescaler instruction (CPR). STOP then
> needs to be released once write operation is completed.
>
> Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> ---
>  drivers/rtc/rtc-pcf2127.c | 38 +++++++++++++++++++++++++++++++++++++-
>  1 file changed, 37 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> index e4b78b9c03f9..11fbdab6bf01 100644
> --- a/drivers/rtc/rtc-pcf2127.c
> +++ b/drivers/rtc/rtc-pcf2127.c
> @@ -39,6 +39,7 @@
>  #define PCF2127_REG_CTRL1              0x00
>  #define PCF2127_BIT_CTRL1_POR_OVRD             BIT(3)
>  #define PCF2127_BIT_CTRL1_TSF1                 BIT(4)
> +#define PCF2127_BIT_CTRL1_STOP                 BIT(5)
>  /* Control register 2 */
>  #define PCF2127_REG_CTRL2              0x01
>  #define PCF2127_BIT_CTRL2_AIE                  BIT(1)
> @@ -70,6 +71,7 @@
>  #define PCF2131_REG_SR_RESET           0x05
>  #define PCF2131_SR_RESET_READ_PATTERN  0b00100100 /* Fixed pattern. */
>  #define PCF2131_SR_RESET_RESET_CMD     0x2C /* SR is bit 3. */
> +#define PCF2131_SR_RESET_CPR_CMD       0xA4 /* CPR is bit 7. */

Replace 0xA4 with (BIT(2) | BIT(5) | BIT(7)) or
(PCF2131_SR_RESET_READ_PATTERN | BIT(7))

>  /* Time and date registers */
>  #define PCF2127_REG_TIME_DATE_BASE     0x03
>  #define PCF2131_REG_TIME_DATE_BASE     0x07 /* Register 0x06 is 100th seconds,
> @@ -307,7 +309,31 @@ static int pcf2127_rtc_set_time(struct device *dev, struct rtc_time *tm)
>         /* year */
>         buf[i++] = bin2bcd(tm->tm_year - 100);
>
> -       /* write register's data */
> +       /* Write access to time registers:
> +        * PCF2127/29: no special action required.
> +        * PCF2131:    requires setting the STOP bit. STOP bit needs to
> +        *             be cleared after time registers are updated.
> +        *             It is also recommended to set CPR bit, although
> +        *             write access will work without it.
> +        */
> +       if (pcf2127->cfg->has_reset_reg) {
> +               err = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL1,
> +                                        PCF2127_BIT_CTRL1_STOP,
> +                                        PCF2127_BIT_CTRL1_STOP);
> +               if (err) {
> +                       dev_err(dev, "setting STOP bit failed\n");
> +                       return err;
> +               }
> +
> +               err = regmap_write(pcf2127->regmap, pcf2127->cfg->reg_reset,
> +                                  PCF2131_SR_RESET_CPR_CMD);
> +               if (err) {
> +                       dev_err(dev, "sending CPR cmd failed\n");
> +                       return err;
> +               }
> +       }
> +
> +       /* write time register's data */
>         err = regmap_bulk_write(pcf2127->regmap, pcf2127->cfg->regs_td_base, buf, i);
>         if (err) {
>                 dev_err(dev,
> @@ -315,6 +341,16 @@ static int pcf2127_rtc_set_time(struct device *dev, struct rtc_time *tm)
>                 return err;
>         }
>
> +       if (pcf2127->cfg->has_reset_reg) {
> +               /* Clear STOP bit (PCF2131 only) after write is completed. */
> +               err = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL1,
> +                                        PCF2127_BIT_CTRL1_STOP, 0);
> +               if (err) {
> +                       dev_err(dev, "clearing STOP bit failed\n");
> +                       return err;
> +               }
> +       }
> +
>         return 0;
>  }
>
> --
> 2.30.2
>

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

* Re: [PATCH v3 13/14] rtc: pcf2127: add flag for watchdog register value read support
  2022-12-15 15:02 ` [PATCH v3 13/14] rtc: pcf2127: add flag for watchdog register value read support Hugo Villeneuve
@ 2023-01-07 18:47   ` Bruno Thomsen
  0 siblings, 0 replies; 72+ messages in thread
From: Bruno Thomsen @ 2023-01-07 18:47 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt,
	linux-rtc, devicetree, linux-kernel, Hugo Villeneuve

Den tor. 15. dec. 2022 kl. 16.19 skrev Hugo Villeneuve <hugo@hugovil.com>:
>
> From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
>
> The watchdog value register cannot be read on the PCF2131 after being
> set.
>
> Add a new flag to identify which variant has read access to this
> register, and use this flag to selectively test if watchdog timer was
> started by bootloader.
>
> Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>

Reviewed-by: Bruno Thomsen <bruno.thomsen@gmail.com>

> ---
>  drivers/rtc/rtc-pcf2127.c | 20 ++++++++++++++------
>  1 file changed, 14 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> index 3fd2fee4978b..1d2b5c9e6757 100644
> --- a/drivers/rtc/rtc-pcf2127.c
> +++ b/drivers/rtc/rtc-pcf2127.c
> @@ -214,6 +214,7 @@ struct pcf21xx_config {
>         int max_register;
>         unsigned int has_nvmem:1;
>         unsigned int has_bit_wd_ctl_cd0:1;
> +       unsigned int wd_val_reg_readable:1; /* If watchdog value register can be read. */
>         unsigned int has_int_a_b:1; /* PCF2131 supports two interrupt outputs. */
>         unsigned int has_reset_reg:1; /* If variant has a reset register. */
>         u8 regs_td_base; /* Time/data base registers. */
> @@ -511,7 +512,6 @@ static const struct watchdog_ops pcf2127_watchdog_ops = {
>
>  static int pcf2127_watchdog_init(struct device *dev, struct pcf2127 *pcf2127)
>  {
> -       u32 wdd_timeout;
>         int ret;
>
>         if (!IS_ENABLED(CONFIG_WATCHDOG) ||
> @@ -539,12 +539,17 @@ static int pcf2127_watchdog_init(struct device *dev, struct pcf2127 *pcf2127)
>         watchdog_set_drvdata(&pcf2127->wdd, pcf2127);
>
>         /* Test if watchdog timer is started by bootloader */
> -       ret = regmap_read(pcf2127->regmap, pcf2127->cfg->reg_wd_val, &wdd_timeout);
> -       if (ret)
> -               return ret;
> +       if (pcf2127->cfg->wd_val_reg_readable) {
> +               u32 wdd_timeout;
> +
> +               ret = regmap_read(pcf2127->regmap, pcf2127->cfg->reg_wd_val,
> +                                 &wdd_timeout);
> +               if (ret)
> +                       return ret;
>
> -       if (wdd_timeout)
> -               set_bit(WDOG_HW_RUNNING, &pcf2127->wdd.status);
> +               if (wdd_timeout)
> +                       set_bit(WDOG_HW_RUNNING, &pcf2127->wdd.status);
> +       }
>
>         return devm_watchdog_register_device(dev, &pcf2127->wdd);
>  }
> @@ -953,6 +958,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
>                 .max_register = 0x1d,
>                 .has_nvmem = 1,
>                 .has_bit_wd_ctl_cd0 = 1,
> +               .wd_val_reg_readable = 1,
>                 .has_int_a_b = 0,
>                 .has_reset_reg = 0,
>                 .regs_td_base = PCF2127_REG_TIME_DATE_BASE,
> @@ -980,6 +986,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
>                 .max_register = 0x19,
>                 .has_nvmem = 0,
>                 .has_bit_wd_ctl_cd0 = 0,
> +               .wd_val_reg_readable = 1,
>                 .has_int_a_b = 0,
>                 .has_reset_reg = 0,
>                 .regs_td_base = PCF2127_REG_TIME_DATE_BASE,
> @@ -1007,6 +1014,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
>                 .max_register = 0x36,
>                 .has_nvmem = 0,
>                 .has_bit_wd_ctl_cd0 = 0,
> +               .wd_val_reg_readable = 0,
>                 .has_int_a_b = 1,
>                 .has_reset_reg = 1,
>                 .regs_td_base = PCF2131_REG_TIME_DATE_BASE,
> --
> 2.30.2
>

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

* Re: [PATCH v3 12/14] rtc: pcf2127: support generic watchdog timing configuration
  2022-12-15 15:02 ` [PATCH v3 12/14] rtc: pcf2127: support generic watchdog timing configuration Hugo Villeneuve
@ 2023-01-18 13:23   ` Philipp Rosenberger
  2023-01-19 17:48     ` Hugo Villeneuve
  0 siblings, 1 reply; 72+ messages in thread
From: Philipp Rosenberger @ 2023-01-18 13:23 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt,
	linux-rtc, devicetree, linux-kernel, Hugo Villeneuve

Hi Hugo,

shouldn't the timeout set with pcf2127_wdt_set_timeout() be in seconds? 
With your changes this value is dependent on the configuration of the 
timer source clock for watchdog timer. So with a default of 1/4Hz this 
will be almost seconds * 4.

I think we need to do the same calculations as in the 
pcf2127_watchdog_init() when calculating the timeout from the 
PCF2127_WD_VAL_DEFAULT.

Best regards,
Philipp


On 15.12.22 16:02, Hugo Villeneuve wrote:
> From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> 
> Introduce in the configuration structure two new values to hold the
> watchdog clock source and the min_hw_heartbeat_ms value.
> 
> The minimum and maximum timeout values are automatically computed from
> the watchdog clock source value for each variant.
> 
> The PCF2131 has no 1Hz watchdog clock source, as is the case for
> PCF2127/29.
> 
> The next best choice is using a 1/4Hz clock, giving a watchdog timeout
> range between 4 and 1016s. By using the same register configuration as
> for the PCF2127/29, the 1/4Hz clock source is selected.
> 
> Note: the PCF2127 datasheet gives a min/max range between 1 and 255s,
> but it should be between 2 and 254s, because the watchdog is triggered
> when the timer value reaches 1, not 0.
> 
> Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> ---
>   drivers/rtc/rtc-pcf2127.c | 56 +++++++++++++++++++++++++++++++++------
>   1 file changed, 48 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> index 11fbdab6bf01..3fd2fee4978b 100644
> --- a/drivers/rtc/rtc-pcf2127.c
> +++ b/drivers/rtc/rtc-pcf2127.c
> @@ -157,9 +157,29 @@
>   
>   /* Watchdog timer value constants */
>   #define PCF2127_WD_VAL_STOP		0
> -#define PCF2127_WD_VAL_MIN		2
> -#define PCF2127_WD_VAL_MAX		255
> -#define PCF2127_WD_VAL_DEFAULT		60
> +#define PCF2127_WD_VAL_DEFAULT		60 /* In seconds. */
> +/* PCF2127/29 watchdog timer value constants */
> +#define PCF2127_WD_CLOCK_HZ_X1000	1000 /* 1Hz */
> +#define PCF2127_WD_MIN_HW_HEARTBEAT_MS	500
> +/* PCF2131 watchdog timer value constants */
> +#define PCF2131_WD_CLOCK_HZ_X1000	250  /* 1/4Hz */
> +#define PCF2131_WD_MIN_HW_HEARTBEAT_MS	4000
> +/*
> + * Compute watchdog period, t, in seconds, from the WATCHDG_TIM_VAL register
> + * value, n, and the clock frequency, f, in Hz.
> + *
> + * The PCF2127/29 datasheet gives t as:
> + *   t = n / f
> + * The PCF2131 datasheet gives t as:
> + *   t = (n - 1) / f
> + * For both variants, the watchdog is triggered when the WATCHDG_TIM_VAL reaches
> + * the value 1, and not zero. Consequently, the equation from the PCF2131
> + * datasheet seems to be the correct one for both variants.
> + */
> +#define WD_PERIOD_S(_n_, _f1000_) ((1000 * ((_n_) - 1)) / (_f1000_))
> +
> +/* Compute value of WATCHDG_TIM_VAL to obtain period t, in seconds. */
> +#define WD_COUNTER(_t_, _f1000_) ((((_t_) * (_f1000_)) / 1000) + 1)
>   
>   /* Mask for currently enabled interrupts */
>   #define PCF2127_CTRL1_IRQ_MASK (PCF2127_BIT_CTRL1_TSF1)
> @@ -202,6 +222,11 @@ struct pcf21xx_config {
>   	u8 reg_wd_val; /* Watchdog value register. */
>   	u8 reg_clkout; /* Clkout register. */
>   	u8 reg_reset;  /* Reset register if available. */
> +
> +	/* Watchdog configuration. */
> +	int wdd_clock_hz_x1000; /* Value in Hz multiplicated by 1000 */
> +	int wdd_min_hw_heartbeat_ms;
> +
>   	unsigned int ts_count;
>   	struct pcf21xx_ts_config ts[4];
>   	struct attribute_group attribute_group;
> @@ -496,10 +521,19 @@ static int pcf2127_watchdog_init(struct device *dev, struct pcf2127 *pcf2127)
>   	pcf2127->wdd.parent = dev;
>   	pcf2127->wdd.info = &pcf2127_wdt_info;
>   	pcf2127->wdd.ops = &pcf2127_watchdog_ops;
> -	pcf2127->wdd.min_timeout = PCF2127_WD_VAL_MIN;
> -	pcf2127->wdd.max_timeout = PCF2127_WD_VAL_MAX;
> -	pcf2127->wdd.timeout = PCF2127_WD_VAL_DEFAULT;
> -	pcf2127->wdd.min_hw_heartbeat_ms = 500;
> +
> +	pcf2127->wdd.min_timeout =
> +		WD_PERIOD_S(2, pcf2127->cfg->wdd_clock_hz_x1000);
> +	pcf2127->wdd.max_timeout =
> +		WD_PERIOD_S(255, pcf2127->cfg->wdd_clock_hz_x1000);
> +	pcf2127->wdd.timeout = WD_COUNTER(PCF2127_WD_VAL_DEFAULT,
> +					  pcf2127->cfg->wdd_clock_hz_x1000);
> +
> +	dev_dbg(dev, "%s min = %ds\n", __func__, pcf2127->wdd.min_timeout);
> +	dev_dbg(dev, "%s max = %ds\n", __func__, pcf2127->wdd.max_timeout);
> +	dev_dbg(dev, "%s def = %d\n", __func__, pcf2127->wdd.timeout);
> +
> +	pcf2127->wdd.min_hw_heartbeat_ms = pcf2127->cfg->wdd_min_hw_heartbeat_ms;
>   	pcf2127->wdd.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
>   
>   	watchdog_set_drvdata(&pcf2127->wdd, pcf2127);
> @@ -926,6 +960,8 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
>   		.reg_wd_ctl = PCF2127_REG_WD_CTL,
>   		.reg_wd_val = PCF2127_REG_WD_VAL,
>   		.reg_clkout = PCF2127_REG_CLKOUT,
> +		.wdd_clock_hz_x1000 = PCF2127_WD_CLOCK_HZ_X1000,
> +		.wdd_min_hw_heartbeat_ms = PCF2127_WD_MIN_HW_HEARTBEAT_MS,
>   		.ts_count = 1,
>   		.ts[0] = {
>   			.regs_base = PCF2127_REG_TS1_BASE,
> @@ -951,6 +987,8 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
>   		.reg_wd_ctl = PCF2127_REG_WD_CTL,
>   		.reg_wd_val = PCF2127_REG_WD_VAL,
>   		.reg_clkout = PCF2127_REG_CLKOUT,
> +		.wdd_clock_hz_x1000 = PCF2127_WD_CLOCK_HZ_X1000,
> +		.wdd_min_hw_heartbeat_ms = PCF2127_WD_MIN_HW_HEARTBEAT_MS,
>   		.ts_count = 1,
>   		.ts[0] = {
>   			.regs_base = PCF2127_REG_TS1_BASE,
> @@ -977,6 +1015,8 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
>   		.reg_wd_val = PCF2131_REG_WD_VAL,
>   		.reg_clkout = PCF2131_REG_CLKOUT,
>   		.reg_reset  = PCF2131_REG_SR_RESET,
> +		.wdd_clock_hz_x1000 = PCF2131_WD_CLOCK_HZ_X1000,
> +		.wdd_min_hw_heartbeat_ms = PCF2131_WD_MIN_HW_HEARTBEAT_MS,
>   		.ts_count = 4,
>   		.ts[0] = {
>   			.regs_base = PCF2131_REG_TS1_BASE,
> @@ -1215,7 +1255,7 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
>   
>   	/*
>   	 * Watchdog timer enabled and reset pin /RST activated when timed out.
> -	 * Select 1Hz clock source for watchdog timer.
> +	 * Select 1Hz clock source for watchdog timer (1/4Hz for PCF2131).
>   	 * Note: Countdown timer disabled and not available.
>   	 * For pca2129, pcf2129 and pcf2131, only bit[7] is for Symbol WD_CD
>   	 * of register watchdg_tim_ctl. The bit[6] is labeled

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

* Re: [PATCH v3 12/14] rtc: pcf2127: support generic watchdog timing configuration
  2023-01-18 13:23   ` Philipp Rosenberger
@ 2023-01-19 17:48     ` Hugo Villeneuve
  2023-01-20  8:06       ` Philipp Rosenberger
  0 siblings, 1 reply; 72+ messages in thread
From: Hugo Villeneuve @ 2023-01-19 17:48 UTC (permalink / raw)
  To: Philipp Rosenberger
  Cc: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt,
	linux-rtc, devicetree, linux-kernel, Hugo Villeneuve

On Wed, 18 Jan 2023 14:23:14 +0100
Philipp Rosenberger <p.rosenberger@kunbus.com> wrote:

> Hi Hugo,
> 
> shouldn't the timeout set with pcf2127_wdt_set_timeout() be in seconds? 
> With your changes this value is dependent on the configuration of the 
> timer source clock for watchdog timer. So with a default of 1/4Hz this 
> will be almost seconds * 4.
> 
> I think we need to do the same calculations as in the 
> pcf2127_watchdog_init() when calculating the timeout from the 
> PCF2127_WD_VAL_DEFAULT.
> 
> Best regards,
> Philipp

Hi Philipp,
you are right that the value store/computed inside the structure wdd (struct watchdog_device) should be in seconds, according to the header file documentation in watchdog.h.

However, in the PCF2127 datasheet, the value n that is stored in the PCF2127_REG_WD_VAL register does not represent a value in seconds, but a counter value. It is given by this equation:

    n = source_clock_frequency x timer_period_in_seconds

For the PCF2127, since the clock used is 1Hz, it works as the previous equation can be simplified as:

    n = timer_period_in_seconds

However, if the source clock is different than 1Hz, it would no longer work. Also, since the PCF2131 uses a default clock of 1/4 Hz, it also would not work.

That is the reason why I modified the watchdog timer value (n) computation to take the clock into account. I then use the desired timeout in seconds given by  PCF2127_WD_VAL_DEFAULT to compute the counter value (n).

So what I am proposing to do is to store the PCF2127_WD_VAL_DEFAULT value in wdd->timeout, as before, but convert it to a counter value in pcf2127_wdt_active_ping(). Or to only compute it once, I could define a new variable and compute/set it in pcf2127_watchdog_init().

Hugo.

 
> On 15.12.22 16:02, Hugo Villeneuve wrote:
> > From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > 
> > Introduce in the configuration structure two new values to hold the
> > watchdog clock source and the min_hw_heartbeat_ms value.
> > 
> > The minimum and maximum timeout values are automatically computed from
> > the watchdog clock source value for each variant.
> > 
> > The PCF2131 has no 1Hz watchdog clock source, as is the case for
> > PCF2127/29.
> > 
> > The next best choice is using a 1/4Hz clock, giving a watchdog timeout
> > range between 4 and 1016s. By using the same register configuration as
> > for the PCF2127/29, the 1/4Hz clock source is selected.
> > 
> > Note: the PCF2127 datasheet gives a min/max range between 1 and 255s,
> > but it should be between 2 and 254s, because the watchdog is triggered
> > when the timer value reaches 1, not 0.
> > 
> > Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > ---
> >   drivers/rtc/rtc-pcf2127.c | 56 +++++++++++++++++++++++++++++++++------
> >   1 file changed, 48 insertions(+), 8 deletions(-)
> > 
> > diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> > index 11fbdab6bf01..3fd2fee4978b 100644
> > --- a/drivers/rtc/rtc-pcf2127.c
> > +++ b/drivers/rtc/rtc-pcf2127.c
> > @@ -157,9 +157,29 @@
> >   
> >   /* Watchdog timer value constants */
> >   #define PCF2127_WD_VAL_STOP		0
> > -#define PCF2127_WD_VAL_MIN		2
> > -#define PCF2127_WD_VAL_MAX		255
> > -#define PCF2127_WD_VAL_DEFAULT		60
> > +#define PCF2127_WD_VAL_DEFAULT		60 /* In seconds. */
> > +/* PCF2127/29 watchdog timer value constants */
> > +#define PCF2127_WD_CLOCK_HZ_X1000	1000 /* 1Hz */
> > +#define PCF2127_WD_MIN_HW_HEARTBEAT_MS	500
> > +/* PCF2131 watchdog timer value constants */
> > +#define PCF2131_WD_CLOCK_HZ_X1000	250  /* 1/4Hz */
> > +#define PCF2131_WD_MIN_HW_HEARTBEAT_MS	4000
> > +/*
> > + * Compute watchdog period, t, in seconds, from the WATCHDG_TIM_VAL register
> > + * value, n, and the clock frequency, f, in Hz.
> > + *
> > + * The PCF2127/29 datasheet gives t as:
> > + *   t = n / f
> > + * The PCF2131 datasheet gives t as:
> > + *   t = (n - 1) / f
> > + * For both variants, the watchdog is triggered when the WATCHDG_TIM_VAL reaches
> > + * the value 1, and not zero. Consequently, the equation from the PCF2131
> > + * datasheet seems to be the correct one for both variants.
> > + */
> > +#define WD_PERIOD_S(_n_, _f1000_) ((1000 * ((_n_) - 1)) / (_f1000_))
> > +
> > +/* Compute value of WATCHDG_TIM_VAL to obtain period t, in seconds. */
> > +#define WD_COUNTER(_t_, _f1000_) ((((_t_) * (_f1000_)) / 1000) + 1)
> >   
> >   /* Mask for currently enabled interrupts */
> >   #define PCF2127_CTRL1_IRQ_MASK (PCF2127_BIT_CTRL1_TSF1)
> > @@ -202,6 +222,11 @@ struct pcf21xx_config {
> >   	u8 reg_wd_val; /* Watchdog value register. */
> >   	u8 reg_clkout; /* Clkout register. */
> >   	u8 reg_reset;  /* Reset register if available. */
> > +
> > +	/* Watchdog configuration. */
> > +	int wdd_clock_hz_x1000; /* Value in Hz multiplicated by 1000 */
> > +	int wdd_min_hw_heartbeat_ms;
> > +
> >   	unsigned int ts_count;
> >   	struct pcf21xx_ts_config ts[4];
> >   	struct attribute_group attribute_group;
> > @@ -496,10 +521,19 @@ static int pcf2127_watchdog_init(struct device *dev, struct pcf2127 *pcf2127)
> >   	pcf2127->wdd.parent = dev;
> >   	pcf2127->wdd.info = &pcf2127_wdt_info;
> >   	pcf2127->wdd.ops = &pcf2127_watchdog_ops;
> > -	pcf2127->wdd.min_timeout = PCF2127_WD_VAL_MIN;
> > -	pcf2127->wdd.max_timeout = PCF2127_WD_VAL_MAX;
> > -	pcf2127->wdd.timeout = PCF2127_WD_VAL_DEFAULT;
> > -	pcf2127->wdd.min_hw_heartbeat_ms = 500;
> > +
> > +	pcf2127->wdd.min_timeout =
> > +		WD_PERIOD_S(2, pcf2127->cfg->wdd_clock_hz_x1000);
> > +	pcf2127->wdd.max_timeout =
> > +		WD_PERIOD_S(255, pcf2127->cfg->wdd_clock_hz_x1000);
> > +	pcf2127->wdd.timeout = WD_COUNTER(PCF2127_WD_VAL_DEFAULT,
> > +					  pcf2127->cfg->wdd_clock_hz_x1000);
> > +
> > +	dev_dbg(dev, "%s min = %ds\n", __func__, pcf2127->wdd.min_timeout);
> > +	dev_dbg(dev, "%s max = %ds\n", __func__, pcf2127->wdd.max_timeout);
> > +	dev_dbg(dev, "%s def = %d\n", __func__, pcf2127->wdd.timeout);
> > +
> > +	pcf2127->wdd.min_hw_heartbeat_ms = pcf2127->cfg->wdd_min_hw_heartbeat_ms;
> >   	pcf2127->wdd.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
> >   
> >   	watchdog_set_drvdata(&pcf2127->wdd, pcf2127);
> > @@ -926,6 +960,8 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
> >   		.reg_wd_ctl = PCF2127_REG_WD_CTL,
> >   		.reg_wd_val = PCF2127_REG_WD_VAL,
> >   		.reg_clkout = PCF2127_REG_CLKOUT,
> > +		.wdd_clock_hz_x1000 = PCF2127_WD_CLOCK_HZ_X1000,
> > +		.wdd_min_hw_heartbeat_ms = PCF2127_WD_MIN_HW_HEARTBEAT_MS,
> >   		.ts_count = 1,
> >   		.ts[0] = {
> >   			.regs_base = PCF2127_REG_TS1_BASE,
> > @@ -951,6 +987,8 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
> >   		.reg_wd_ctl = PCF2127_REG_WD_CTL,
> >   		.reg_wd_val = PCF2127_REG_WD_VAL,
> >   		.reg_clkout = PCF2127_REG_CLKOUT,
> > +		.wdd_clock_hz_x1000 = PCF2127_WD_CLOCK_HZ_X1000,
> > +		.wdd_min_hw_heartbeat_ms = PCF2127_WD_MIN_HW_HEARTBEAT_MS,
> >   		.ts_count = 1,
> >   		.ts[0] = {
> >   			.regs_base = PCF2127_REG_TS1_BASE,
> > @@ -977,6 +1015,8 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
> >   		.reg_wd_val = PCF2131_REG_WD_VAL,
> >   		.reg_clkout = PCF2131_REG_CLKOUT,
> >   		.reg_reset  = PCF2131_REG_SR_RESET,
> > +		.wdd_clock_hz_x1000 = PCF2131_WD_CLOCK_HZ_X1000,
> > +		.wdd_min_hw_heartbeat_ms = PCF2131_WD_MIN_HW_HEARTBEAT_MS,
> >   		.ts_count = 4,
> >   		.ts[0] = {
> >   			.regs_base = PCF2131_REG_TS1_BASE,
> > @@ -1215,7 +1255,7 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
> >   
> >   	/*
> >   	 * Watchdog timer enabled and reset pin /RST activated when timed out.
> > -	 * Select 1Hz clock source for watchdog timer.
> > +	 * Select 1Hz clock source for watchdog timer (1/4Hz for PCF2131).
> >   	 * Note: Countdown timer disabled and not available.
> >   	 * For pca2129, pcf2129 and pcf2131, only bit[7] is for Symbol WD_CD
> >   	 * of register watchdg_tim_ctl. The bit[6] is labeled
> 


-- 
Hugo Villeneuve <hugo@hugovil.com>

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

* Re: [PATCH v3 12/14] rtc: pcf2127: support generic watchdog timing configuration
  2023-01-19 17:48     ` Hugo Villeneuve
@ 2023-01-20  8:06       ` Philipp Rosenberger
  2023-01-20 14:44         ` Hugo Villeneuve
  0 siblings, 1 reply; 72+ messages in thread
From: Philipp Rosenberger @ 2023-01-20  8:06 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt,
	linux-rtc, devicetree, linux-kernel, Hugo Villeneuve

On 19.01.23 18:48, Hugo Villeneuve wrote:
> On Wed, 18 Jan 2023 14:23:14 +0100
> Philipp Rosenberger <p.rosenberger@kunbus.com> wrote:
> 
> Hi Philipp,
> you are right that the value store/computed inside the structure wdd (struct watchdog_device) should be in seconds, according to the header file documentation in watchdog.h.
> 
> However, in the PCF2127 datasheet, the value n that is stored in the PCF2127_REG_WD_VAL register does not represent a value in seconds, but a counter value. It is given by this equation:
> 
>      n = source_clock_frequency x timer_period_in_seconds
> 
> For the PCF2127, since the clock used is 1Hz, it works as the previous equation can be simplified as:
> 
>      n = timer_period_in_seconds
> 
> However, if the source clock is different than 1Hz, it would no longer work. Also, since the PCF2131 uses a default clock of 1/4 Hz, it also would not work.
> 
> That is the reason why I modified the watchdog timer value (n) computation to take the clock into account. I then use the desired timeout in seconds given by  PCF2127_WD_VAL_DEFAULT to compute the counter value (n).
> 
> So what I am proposing to do is to store the PCF2127_WD_VAL_DEFAULT value in wdd->timeout, as before, but convert it to a counter value in pcf2127_wdt_active_ping(). Or to only compute it once, I could define a new variable and compute/set it in pcf2127_watchdog_init().
> 
> Hugo.
> 

Hi Hugo,

with some small changes to your code I was able to get the behavior 
right. But with this the timeout get converted from seconds to the 
counter register format on every ping.

Just add WD_COUNTER(wdd->timeout, pcf2127->cfg->wdd_clock_hz_x1000) to 
the regmap_write() in pcf2127_wdt_ping(). Then in 
pcf2127_watchdog_init() just do:

pcf2127->wdd.timeout = PCF2127_WD_VAL_DEFAULT;

Best regards,
Philipp

> 
>> On 15.12.22 16:02, Hugo Villeneuve wrote:
>>> From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
>>>
>>> Introduce in the configuration structure two new values to hold the
>>> watchdog clock source and the min_hw_heartbeat_ms value.
>>>
>>> The minimum and maximum timeout values are automatically computed from
>>> the watchdog clock source value for each variant.
>>>
>>> The PCF2131 has no 1Hz watchdog clock source, as is the case for
>>> PCF2127/29.
>>>
>>> The next best choice is using a 1/4Hz clock, giving a watchdog timeout
>>> range between 4 and 1016s. By using the same register configuration as
>>> for the PCF2127/29, the 1/4Hz clock source is selected.
>>>
>>> Note: the PCF2127 datasheet gives a min/max range between 1 and 255s,
>>> but it should be between 2 and 254s, because the watchdog is triggered
>>> when the timer value reaches 1, not 0.
>>>
>>> Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
>>> ---
>>>    drivers/rtc/rtc-pcf2127.c | 56 +++++++++++++++++++++++++++++++++------
>>>    1 file changed, 48 insertions(+), 8 deletions(-)
>>>
>>> diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
>>> index 11fbdab6bf01..3fd2fee4978b 100644
>>> --- a/drivers/rtc/rtc-pcf2127.c
>>> +++ b/drivers/rtc/rtc-pcf2127.c
>>> @@ -157,9 +157,29 @@
>>>
>>>    /* Watchdog timer value constants */
>>>    #define PCF2127_WD_VAL_STOP               0
>>> -#define PCF2127_WD_VAL_MIN         2
>>> -#define PCF2127_WD_VAL_MAX         255
>>> -#define PCF2127_WD_VAL_DEFAULT             60
>>> +#define PCF2127_WD_VAL_DEFAULT             60 /* In seconds. */
>>> +/* PCF2127/29 watchdog timer value constants */
>>> +#define PCF2127_WD_CLOCK_HZ_X1000  1000 /* 1Hz */
>>> +#define PCF2127_WD_MIN_HW_HEARTBEAT_MS     500
>>> +/* PCF2131 watchdog timer value constants */
>>> +#define PCF2131_WD_CLOCK_HZ_X1000  250  /* 1/4Hz */
>>> +#define PCF2131_WD_MIN_HW_HEARTBEAT_MS     4000
>>> +/*
>>> + * Compute watchdog period, t, in seconds, from the WATCHDG_TIM_VAL register
>>> + * value, n, and the clock frequency, f, in Hz.
>>> + *
>>> + * The PCF2127/29 datasheet gives t as:
>>> + *   t = n / f
>>> + * The PCF2131 datasheet gives t as:
>>> + *   t = (n - 1) / f
>>> + * For both variants, the watchdog is triggered when the WATCHDG_TIM_VAL reaches
>>> + * the value 1, and not zero. Consequently, the equation from the PCF2131
>>> + * datasheet seems to be the correct one for both variants.
>>> + */
>>> +#define WD_PERIOD_S(_n_, _f1000_) ((1000 * ((_n_) - 1)) / (_f1000_))
>>> +
>>> +/* Compute value of WATCHDG_TIM_VAL to obtain period t, in seconds. */
>>> +#define WD_COUNTER(_t_, _f1000_) ((((_t_) * (_f1000_)) / 1000) + 1)
>>>
>>>    /* Mask for currently enabled interrupts */
>>>    #define PCF2127_CTRL1_IRQ_MASK (PCF2127_BIT_CTRL1_TSF1)
>>> @@ -202,6 +222,11 @@ struct pcf21xx_config {
>>>      u8 reg_wd_val; /* Watchdog value register. */
>>>      u8 reg_clkout; /* Clkout register. */
>>>      u8 reg_reset;  /* Reset register if available. */
>>> +
>>> +   /* Watchdog configuration. */
>>> +   int wdd_clock_hz_x1000; /* Value in Hz multiplicated by 1000 */
>>> +   int wdd_min_hw_heartbeat_ms;
>>> +
>>>      unsigned int ts_count;
>>>      struct pcf21xx_ts_config ts[4];
>>>      struct attribute_group attribute_group;
>>> @@ -496,10 +521,19 @@ static int pcf2127_watchdog_init(struct device *dev, struct pcf2127 *pcf2127)
>>>      pcf2127->wdd.parent = dev;
>>>      pcf2127->wdd.info = &pcf2127_wdt_info;
>>>      pcf2127->wdd.ops = &pcf2127_watchdog_ops;
>>> -   pcf2127->wdd.min_timeout = PCF2127_WD_VAL_MIN;
>>> -   pcf2127->wdd.max_timeout = PCF2127_WD_VAL_MAX;
>>> -   pcf2127->wdd.timeout = PCF2127_WD_VAL_DEFAULT;
>>> -   pcf2127->wdd.min_hw_heartbeat_ms = 500;
>>> +
>>> +   pcf2127->wdd.min_timeout =
>>> +           WD_PERIOD_S(2, pcf2127->cfg->wdd_clock_hz_x1000);
>>> +   pcf2127->wdd.max_timeout =
>>> +           WD_PERIOD_S(255, pcf2127->cfg->wdd_clock_hz_x1000);
>>> +   pcf2127->wdd.timeout = WD_COUNTER(PCF2127_WD_VAL_DEFAULT,
>>> +                                     pcf2127->cfg->wdd_clock_hz_x1000);
>>> +
>>> +   dev_dbg(dev, "%s min = %ds\n", __func__, pcf2127->wdd.min_timeout);
>>> +   dev_dbg(dev, "%s max = %ds\n", __func__, pcf2127->wdd.max_timeout);
>>> +   dev_dbg(dev, "%s def = %d\n", __func__, pcf2127->wdd.timeout);
>>> +
>>> +   pcf2127->wdd.min_hw_heartbeat_ms = pcf2127->cfg->wdd_min_hw_heartbeat_ms;
>>>      pcf2127->wdd.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
>>>
>>>      watchdog_set_drvdata(&pcf2127->wdd, pcf2127);
>>> @@ -926,6 +960,8 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
>>>              .reg_wd_ctl = PCF2127_REG_WD_CTL,
>>>              .reg_wd_val = PCF2127_REG_WD_VAL,
>>>              .reg_clkout = PCF2127_REG_CLKOUT,
>>> +           .wdd_clock_hz_x1000 = PCF2127_WD_CLOCK_HZ_X1000,
>>> +           .wdd_min_hw_heartbeat_ms = PCF2127_WD_MIN_HW_HEARTBEAT_MS,
>>>              .ts_count = 1,
>>>              .ts[0] = {
>>>                      .regs_base = PCF2127_REG_TS1_BASE,
>>> @@ -951,6 +987,8 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
>>>              .reg_wd_ctl = PCF2127_REG_WD_CTL,
>>>              .reg_wd_val = PCF2127_REG_WD_VAL,
>>>              .reg_clkout = PCF2127_REG_CLKOUT,
>>> +           .wdd_clock_hz_x1000 = PCF2127_WD_CLOCK_HZ_X1000,
>>> +           .wdd_min_hw_heartbeat_ms = PCF2127_WD_MIN_HW_HEARTBEAT_MS,
>>>              .ts_count = 1,
>>>              .ts[0] = {
>>>                      .regs_base = PCF2127_REG_TS1_BASE,
>>> @@ -977,6 +1015,8 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
>>>              .reg_wd_val = PCF2131_REG_WD_VAL,
>>>              .reg_clkout = PCF2131_REG_CLKOUT,
>>>              .reg_reset  = PCF2131_REG_SR_RESET,
>>> +           .wdd_clock_hz_x1000 = PCF2131_WD_CLOCK_HZ_X1000,
>>> +           .wdd_min_hw_heartbeat_ms = PCF2131_WD_MIN_HW_HEARTBEAT_MS,
>>>              .ts_count = 4,
>>>              .ts[0] = {
>>>                      .regs_base = PCF2131_REG_TS1_BASE,
>>> @@ -1215,7 +1255,7 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
>>>
>>>      /*
>>>       * Watchdog timer enabled and reset pin /RST activated when timed out.
>>> -    * Select 1Hz clock source for watchdog timer.
>>> +    * Select 1Hz clock source for watchdog timer (1/4Hz for PCF2131).
>>>       * Note: Countdown timer disabled and not available.
>>>       * For pca2129, pcf2129 and pcf2131, only bit[7] is for Symbol WD_CD
>>>       * of register watchdg_tim_ctl. The bit[6] is labeled
>>
> 
> 
> --
> Hugo Villeneuve <hugo@hugovil.com>

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

* Re: [PATCH v3 12/14] rtc: pcf2127: support generic watchdog timing configuration
  2023-01-20  8:06       ` Philipp Rosenberger
@ 2023-01-20 14:44         ` Hugo Villeneuve
  0 siblings, 0 replies; 72+ messages in thread
From: Hugo Villeneuve @ 2023-01-20 14:44 UTC (permalink / raw)
  To: Philipp Rosenberger
  Cc: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt,
	linux-rtc, devicetree, linux-kernel, Hugo Villeneuve

On Fri, 20 Jan 2023 09:06:31 +0100
Philipp Rosenberger <p.rosenberger@kunbus.com> wrote:

> On 19.01.23 18:48, Hugo Villeneuve wrote:
> > On Wed, 18 Jan 2023 14:23:14 +0100
> > Philipp Rosenberger <p.rosenberger@kunbus.com> wrote:
> > 
> > Hi Philipp,
> > you are right that the value store/computed inside the structure wdd (struct watchdog_device) should be in seconds, according to the header file documentation in watchdog.h.
> > 
> > However, in the PCF2127 datasheet, the value n that is stored in the PCF2127_REG_WD_VAL register does not represent a value in seconds, but a counter value. It is given by this equation:
> > 
> >      n = source_clock_frequency x timer_period_in_seconds
> > 
> > For the PCF2127, since the clock used is 1Hz, it works as the previous equation can be simplified as:
> > 
> >      n = timer_period_in_seconds
> > 
> > However, if the source clock is different than 1Hz, it would no longer work. Also, since the PCF2131 uses a default clock of 1/4 Hz, it also would not work.
> > 
> > That is the reason why I modified the watchdog timer value (n) computation to take the clock into account. I then use the desired timeout in seconds given by  PCF2127_WD_VAL_DEFAULT to compute the counter value (n).
> > 
> > So what I am proposing to do is to store the PCF2127_WD_VAL_DEFAULT value in wdd->timeout, as before, but convert it to a counter value in pcf2127_wdt_active_ping(). Or to only compute it once, I could define a new variable and compute/set it in pcf2127_watchdog_init().
> > 
> > Hugo.
> > 
> 
> Hi Hugo,
> 
> with some small changes to your code I was able to get the behavior 
> right. But with this the timeout get converted from seconds to the 
> counter register format on every ping.
> 
> Just add WD_COUNTER(wdd->timeout, pcf2127->cfg->wdd_clock_hz_x1000) to 
> the regmap_write() in pcf2127_wdt_ping(). Then in 
> pcf2127_watchdog_init() just do:
> 
> pcf2127->wdd.timeout = PCF2127_WD_VAL_DEFAULT;
> 
> Best regards,
> Philipp

Hi Philipp,
in the end this is exactly what I did. I will send an updated patch series once I have adressed the other comments.

Hugo.


> > 
> >> On 15.12.22 16:02, Hugo Villeneuve wrote:
> >>> From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> >>>
> >>> Introduce in the configuration structure two new values to hold the
> >>> watchdog clock source and the min_hw_heartbeat_ms value.
> >>>
> >>> The minimum and maximum timeout values are automatically computed from
> >>> the watchdog clock source value for each variant.
> >>>
> >>> The PCF2131 has no 1Hz watchdog clock source, as is the case for
> >>> PCF2127/29.
> >>>
> >>> The next best choice is using a 1/4Hz clock, giving a watchdog timeout
> >>> range between 4 and 1016s. By using the same register configuration as
> >>> for the PCF2127/29, the 1/4Hz clock source is selected.
> >>>
> >>> Note: the PCF2127 datasheet gives a min/max range between 1 and 255s,
> >>> but it should be between 2 and 254s, because the watchdog is triggered
> >>> when the timer value reaches 1, not 0.
> >>>
> >>> Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> >>> ---
> >>>    drivers/rtc/rtc-pcf2127.c | 56 +++++++++++++++++++++++++++++++++------
> >>>    1 file changed, 48 insertions(+), 8 deletions(-)
> >>>
> >>> diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> >>> index 11fbdab6bf01..3fd2fee4978b 100644
> >>> --- a/drivers/rtc/rtc-pcf2127.c
> >>> +++ b/drivers/rtc/rtc-pcf2127.c
> >>> @@ -157,9 +157,29 @@
> >>>
> >>>    /* Watchdog timer value constants */
> >>>    #define PCF2127_WD_VAL_STOP               0
> >>> -#define PCF2127_WD_VAL_MIN         2
> >>> -#define PCF2127_WD_VAL_MAX         255
> >>> -#define PCF2127_WD_VAL_DEFAULT             60
> >>> +#define PCF2127_WD_VAL_DEFAULT             60 /* In seconds. */
> >>> +/* PCF2127/29 watchdog timer value constants */
> >>> +#define PCF2127_WD_CLOCK_HZ_X1000  1000 /* 1Hz */
> >>> +#define PCF2127_WD_MIN_HW_HEARTBEAT_MS     500
> >>> +/* PCF2131 watchdog timer value constants */
> >>> +#define PCF2131_WD_CLOCK_HZ_X1000  250  /* 1/4Hz */
> >>> +#define PCF2131_WD_MIN_HW_HEARTBEAT_MS     4000
> >>> +/*
> >>> + * Compute watchdog period, t, in seconds, from the WATCHDG_TIM_VAL register
> >>> + * value, n, and the clock frequency, f, in Hz.
> >>> + *
> >>> + * The PCF2127/29 datasheet gives t as:
> >>> + *   t = n / f
> >>> + * The PCF2131 datasheet gives t as:
> >>> + *   t = (n - 1) / f
> >>> + * For both variants, the watchdog is triggered when the WATCHDG_TIM_VAL reaches
> >>> + * the value 1, and not zero. Consequently, the equation from the PCF2131
> >>> + * datasheet seems to be the correct one for both variants.
> >>> + */
> >>> +#define WD_PERIOD_S(_n_, _f1000_) ((1000 * ((_n_) - 1)) / (_f1000_))
> >>> +
> >>> +/* Compute value of WATCHDG_TIM_VAL to obtain period t, in seconds. */
> >>> +#define WD_COUNTER(_t_, _f1000_) ((((_t_) * (_f1000_)) / 1000) + 1)
> >>>
> >>>    /* Mask for currently enabled interrupts */
> >>>    #define PCF2127_CTRL1_IRQ_MASK (PCF2127_BIT_CTRL1_TSF1)
> >>> @@ -202,6 +222,11 @@ struct pcf21xx_config {
> >>>      u8 reg_wd_val; /* Watchdog value register. */
> >>>      u8 reg_clkout; /* Clkout register. */
> >>>      u8 reg_reset;  /* Reset register if available. */
> >>> +
> >>> +   /* Watchdog configuration. */
> >>> +   int wdd_clock_hz_x1000; /* Value in Hz multiplicated by 1000 */
> >>> +   int wdd_min_hw_heartbeat_ms;
> >>> +
> >>>      unsigned int ts_count;
> >>>      struct pcf21xx_ts_config ts[4];
> >>>      struct attribute_group attribute_group;
> >>> @@ -496,10 +521,19 @@ static int pcf2127_watchdog_init(struct device *dev, struct pcf2127 *pcf2127)
> >>>      pcf2127->wdd.parent = dev;
> >>>      pcf2127->wdd.info = &pcf2127_wdt_info;
> >>>      pcf2127->wdd.ops = &pcf2127_watchdog_ops;
> >>> -   pcf2127->wdd.min_timeout = PCF2127_WD_VAL_MIN;
> >>> -   pcf2127->wdd.max_timeout = PCF2127_WD_VAL_MAX;
> >>> -   pcf2127->wdd.timeout = PCF2127_WD_VAL_DEFAULT;
> >>> -   pcf2127->wdd.min_hw_heartbeat_ms = 500;
> >>> +
> >>> +   pcf2127->wdd.min_timeout =
> >>> +           WD_PERIOD_S(2, pcf2127->cfg->wdd_clock_hz_x1000);
> >>> +   pcf2127->wdd.max_timeout =
> >>> +           WD_PERIOD_S(255, pcf2127->cfg->wdd_clock_hz_x1000);
> >>> +   pcf2127->wdd.timeout = WD_COUNTER(PCF2127_WD_VAL_DEFAULT,
> >>> +                                     pcf2127->cfg->wdd_clock_hz_x1000);
> >>> +
> >>> +   dev_dbg(dev, "%s min = %ds\n", __func__, pcf2127->wdd.min_timeout);
> >>> +   dev_dbg(dev, "%s max = %ds\n", __func__, pcf2127->wdd.max_timeout);
> >>> +   dev_dbg(dev, "%s def = %d\n", __func__, pcf2127->wdd.timeout);
> >>> +
> >>> +   pcf2127->wdd.min_hw_heartbeat_ms = pcf2127->cfg->wdd_min_hw_heartbeat_ms;
> >>>      pcf2127->wdd.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
> >>>
> >>>      watchdog_set_drvdata(&pcf2127->wdd, pcf2127);
> >>> @@ -926,6 +960,8 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
> >>>              .reg_wd_ctl = PCF2127_REG_WD_CTL,
> >>>              .reg_wd_val = PCF2127_REG_WD_VAL,
> >>>              .reg_clkout = PCF2127_REG_CLKOUT,
> >>> +           .wdd_clock_hz_x1000 = PCF2127_WD_CLOCK_HZ_X1000,
> >>> +           .wdd_min_hw_heartbeat_ms = PCF2127_WD_MIN_HW_HEARTBEAT_MS,
> >>>              .ts_count = 1,
> >>>              .ts[0] = {
> >>>                      .regs_base = PCF2127_REG_TS1_BASE,
> >>> @@ -951,6 +987,8 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
> >>>              .reg_wd_ctl = PCF2127_REG_WD_CTL,
> >>>              .reg_wd_val = PCF2127_REG_WD_VAL,
> >>>              .reg_clkout = PCF2127_REG_CLKOUT,
> >>> +           .wdd_clock_hz_x1000 = PCF2127_WD_CLOCK_HZ_X1000,
> >>> +           .wdd_min_hw_heartbeat_ms = PCF2127_WD_MIN_HW_HEARTBEAT_MS,
> >>>              .ts_count = 1,
> >>>              .ts[0] = {
> >>>                      .regs_base = PCF2127_REG_TS1_BASE,
> >>> @@ -977,6 +1015,8 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
> >>>              .reg_wd_val = PCF2131_REG_WD_VAL,
> >>>              .reg_clkout = PCF2131_REG_CLKOUT,
> >>>              .reg_reset  = PCF2131_REG_SR_RESET,
> >>> +           .wdd_clock_hz_x1000 = PCF2131_WD_CLOCK_HZ_X1000,
> >>> +           .wdd_min_hw_heartbeat_ms = PCF2131_WD_MIN_HW_HEARTBEAT_MS,
> >>>              .ts_count = 4,
> >>>              .ts[0] = {
> >>>                      .regs_base = PCF2131_REG_TS1_BASE,
> >>> @@ -1215,7 +1255,7 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
> >>>
> >>>      /*
> >>>       * Watchdog timer enabled and reset pin /RST activated when timed out.
> >>> -    * Select 1Hz clock source for watchdog timer.
> >>> +    * Select 1Hz clock source for watchdog timer (1/4Hz for PCF2131).
> >>>       * Note: Countdown timer disabled and not available.
> >>>       * For pca2129, pcf2129 and pcf2131, only bit[7] is for Symbol WD_CD
> >>>       * of register watchdg_tim_ctl. The bit[6] is labeled
> >>
> > 
> > 
> > --
> > Hugo Villeneuve <hugo@hugovil.com>
> 


-- 
Hugo Villeneuve <hugo@hugovil.com>

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

* Re: [PATCH v3 09/14] rtc: pcf2127: set PWRMNG value for PCF2131
  2023-01-07 18:36   ` Bruno Thomsen
@ 2023-01-20 16:39     ` Alexandre Belloni
  2023-01-23 22:07       ` Hugo Villeneuve
  0 siblings, 1 reply; 72+ messages in thread
From: Alexandre Belloni @ 2023-01-20 16:39 UTC (permalink / raw)
  To: Bruno Thomsen
  Cc: Hugo Villeneuve, a.zummo, robh+dt, krzysztof.kozlowski+dt,
	linux-rtc, devicetree, linux-kernel, Hugo Villeneuve

Hello,

On 07/01/2023 19:36:06+0100, Bruno Thomsen wrote:
> Den tor. 15. dec. 2022 kl. 16.19 skrev Hugo Villeneuve <hugo@hugovil.com>:
> >
> > From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> >
> > Default PWRMNG[2:0] bits are set to 000b for PCF2127/29, but to
> > 111b for PCF2131.
> >
> > Set these bits to 000b to select same mode as PCF2127/29.
> >
> > Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> 
> Reviewed-by: Bruno Thomsen <bruno.thomsen@gmail.com>
> 
> I think it's a good idea[1] but there have been concerns about
> setting default values in the past[2]. In case somebody needs
> a different behaviour they should add a device tree property.
> 
> [1] https://lore.kernel.org/linux-rtc/20190910143945.9364-1-bruno.thomsen@gmail.com/
> [2] https://lore.kernel.org/linux-rtc/20191211163354.GC1463890@piout.net/

I confirm this is still my point of view and I won't take this patch as
this may break existing users.

> 
> > ---
> >  drivers/rtc/rtc-pcf2127.c | 15 +++++++++++++++
> >  1 file changed, 15 insertions(+)
> >
> > diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> > index 68af4d0438b8..241189ee4a05 100644
> > --- a/drivers/rtc/rtc-pcf2127.c
> > +++ b/drivers/rtc/rtc-pcf2127.c
> > @@ -53,6 +53,7 @@
> >  #define PCF2127_BIT_CTRL3_BLF                  BIT(2)
> >  #define PCF2127_BIT_CTRL3_BF                   BIT(3)
> >  #define PCF2127_BIT_CTRL3_BTSE                 BIT(4)
> > +#define PCF2127_CTRL3_PWRMNG_MASK              GENMASK(7, 5)
> >  /* Control register 4 */
> >  #define PCF2131_REG_CTRL4              0x03
> >  #define PCF2131_BIT_CTRL4_TSF4                 BIT(4)
> > @@ -1129,6 +1130,20 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
> >         regmap_clear_bits(pcf2127->regmap, PCF2127_REG_CTRL1,
> >                                 PCF2127_BIT_CTRL1_POR_OVRD);
> >
> > +       /* Make sure PWRMNG[2:0] is set to 000b. This is the default for
> > +        * PCF2127/29, but not for PCF2131 (default of 111b).
> > +        *
> > +        * PWRMNG[2:0]  = 000b:
> > +        *   battery switch-over function is enabled in standard mode;
> > +        *   battery low detection function is enabled
> > +        */
> > +       ret = regmap_clear_bits(pcf2127->regmap, PCF2127_REG_CTRL3,
> > +                               PCF2127_CTRL3_PWRMNG_MASK);
> > +       if (ret < 0) {
> > +               dev_err(dev, "PWRMNG config failed\n");
> > +               return ret;
> > +       }
> > +
> >         ret = regmap_read(pcf2127->regmap, pcf2127->cfg->reg_clkout, &val);
> >         if (ret < 0)
> >                 return ret;
> > --
> > 2.30.2
> >

-- 
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

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

* Re: [PATCH v3 08/14] rtc: pcf2127: add support for PCF2131 interrupts on output INT_A
  2022-12-15 15:02 ` [PATCH v3 08/14] rtc: pcf2127: add support for PCF2131 interrupts on output INT_A Hugo Villeneuve
  2023-01-07 18:17   ` Bruno Thomsen
@ 2023-01-20 16:56   ` Alexandre Belloni
  2023-01-23 20:52     ` Hugo Villeneuve
  1 sibling, 1 reply; 72+ messages in thread
From: Alexandre Belloni @ 2023-01-20 16:56 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: a.zummo, robh+dt, krzysztof.kozlowski+dt, linux-rtc, devicetree,
	linux-kernel, Hugo Villeneuve

On 15/12/2022 10:02:09-0500, Hugo Villeneuve wrote:
> From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> 
> The PCF2127 and PCF2129 have one output interrupt pin. The PCF2131 has
> two, named INT_A and INT_B. The hardware support that any interrupt
> source can be routed to either one or both of them.
> 
> Force all interrupt sources to go to the INT A pin.
> 
> Support to route any interrupt source to INT A/B pins is not supported
> by this driver at the moment.
> 

The main issue with this is that this will created a breaking change
once someone needs support for INTB

> Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> ---
>  drivers/rtc/rtc-pcf2127.c | 35 +++++++++++++++++++++++++++++++++++
>  1 file changed, 35 insertions(+)
> 
> diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> index 4148e135f935..68af4d0438b8 100644
> --- a/drivers/rtc/rtc-pcf2127.c
> +++ b/drivers/rtc/rtc-pcf2127.c
> @@ -191,6 +191,7 @@ struct pcf21xx_config {
>  	int max_register;
>  	unsigned int has_nvmem:1;
>  	unsigned int has_bit_wd_ctl_cd0:1;
> +	unsigned int has_int_a_b:1; /* PCF2131 supports two interrupt outputs. */
>  	u8 regs_td_base; /* Time/data base registers. */
>  	u8 regs_alarm_base; /* Alarm function base registers. */
>  	u8 reg_wd_ctl; /* Watchdog control register. */
> @@ -879,6 +880,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
>  		.max_register = 0x1d,
>  		.has_nvmem = 1,
>  		.has_bit_wd_ctl_cd0 = 1,
> +		.has_int_a_b = 0,
>  		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
>  		.regs_alarm_base = PCF2127_REG_ALARM_BASE,
>  		.reg_wd_ctl = PCF2127_REG_WD_CTL,
> @@ -902,6 +904,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
>  		.max_register = 0x19,
>  		.has_nvmem = 0,
>  		.has_bit_wd_ctl_cd0 = 0,
> +		.has_int_a_b = 0,
>  		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
>  		.regs_alarm_base = PCF2127_REG_ALARM_BASE,
>  		.reg_wd_ctl = PCF2127_REG_WD_CTL,
> @@ -925,6 +928,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
>  		.max_register = 0x36,
>  		.has_nvmem = 0,
>  		.has_bit_wd_ctl_cd0 = 0,
> +		.has_int_a_b = 1,
>  		.regs_td_base = PCF2131_REG_TIME_DATE_BASE,
>  		.regs_alarm_base = PCF2131_REG_ALARM_BASE,
>  		.reg_wd_ctl = PCF2131_REG_WD_CTL,
> @@ -1017,6 +1021,28 @@ static int pcf2127_enable_ts(struct device *dev, int ts_id)
>  	return ret;
>  }
>  
> +/* Route all interrupt sources to INT A pin. */
> +static int pcf2127_configure_interrupt_pins(struct device *dev)
> +{
> +	struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
> +	int ret;
> +
> +	/* Mask bits need to be cleared to enable corresponding
> +	 * interrupt source.
> +	 */
> +	ret = regmap_write(pcf2127->regmap,
> +			   PCF2131_REG_INT_A_MASK1, 0);
> +	if (ret)
> +		return ret;
> +
> +	ret = regmap_write(pcf2127->regmap,
> +			   PCF2131_REG_INT_A_MASK2, 0);
> +	if (ret)
> +		return ret;
> +
> +	return ret;
> +}
> +
>  static int pcf2127_probe(struct device *dev, struct regmap *regmap,
>  			 int alarm_irq, const char *name, const struct pcf21xx_config *config)
>  {
> @@ -1076,6 +1102,15 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
>  		set_bit(RTC_FEATURE_ALARM, pcf2127->rtc->features);
>  	}
>  
> +	if (pcf2127->cfg->has_int_a_b) {
> +		/* Configure int A/B pins, independently of alarm_irq. */
> +		ret = pcf2127_configure_interrupt_pins(dev);
> +		if (ret) {
> +			dev_err(dev, "failed to configure interrupt pins\n");
> +			return ret;
> +		}
> +	}
> +
>  	if (pcf2127->cfg->has_nvmem) {
>  		struct nvmem_config nvmem_cfg = {
>  			.priv = pcf2127,
> -- 
> 2.30.2
> 

-- 
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

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

* Re: [PATCH v3 10/14] rtc: pcf2127: read and validate PCF2131 device signature
  2022-12-15 15:02 ` [PATCH v3 10/14] rtc: pcf2127: read and validate PCF2131 device signature Hugo Villeneuve
@ 2023-01-20 17:01   ` Alexandre Belloni
  2023-01-23 17:31     ` Hugo Villeneuve
  0 siblings, 1 reply; 72+ messages in thread
From: Alexandre Belloni @ 2023-01-20 17:01 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: a.zummo, robh+dt, krzysztof.kozlowski+dt, linux-rtc, devicetree,
	linux-kernel, Hugo Villeneuve

On 15/12/2022 10:02:11-0500, Hugo Villeneuve wrote:
> From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> 
> Make sure the device we are probing is really the device we are
> interested in.
> 
> Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> ---
>  drivers/rtc/rtc-pcf2127.c | 20 ++++++++++++++++++++
>  1 file changed, 20 insertions(+)
> 
> diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> index 241189ee4a05..e4b78b9c03f9 100644
> --- a/drivers/rtc/rtc-pcf2127.c
> +++ b/drivers/rtc/rtc-pcf2127.c
> @@ -193,11 +193,13 @@ struct pcf21xx_config {
>  	unsigned int has_nvmem:1;
>  	unsigned int has_bit_wd_ctl_cd0:1;
>  	unsigned int has_int_a_b:1; /* PCF2131 supports two interrupt outputs. */
> +	unsigned int has_reset_reg:1; /* If variant has a reset register. */
>  	u8 regs_td_base; /* Time/data base registers. */
>  	u8 regs_alarm_base; /* Alarm function base registers. */
>  	u8 reg_wd_ctl; /* Watchdog control register. */
>  	u8 reg_wd_val; /* Watchdog value register. */
>  	u8 reg_clkout; /* Clkout register. */
> +	u8 reg_reset;  /* Reset register if available. */
>  	unsigned int ts_count;
>  	struct pcf21xx_ts_config ts[4];
>  	struct attribute_group attribute_group;
> @@ -882,6 +884,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
>  		.has_nvmem = 1,
>  		.has_bit_wd_ctl_cd0 = 1,
>  		.has_int_a_b = 0,
> +		.has_reset_reg = 0,
>  		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
>  		.regs_alarm_base = PCF2127_REG_ALARM_BASE,
>  		.reg_wd_ctl = PCF2127_REG_WD_CTL,
> @@ -906,6 +909,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
>  		.has_nvmem = 0,
>  		.has_bit_wd_ctl_cd0 = 0,
>  		.has_int_a_b = 0,
> +		.has_reset_reg = 0,
>  		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
>  		.regs_alarm_base = PCF2127_REG_ALARM_BASE,
>  		.reg_wd_ctl = PCF2127_REG_WD_CTL,
> @@ -930,11 +934,13 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
>  		.has_nvmem = 0,
>  		.has_bit_wd_ctl_cd0 = 0,
>  		.has_int_a_b = 1,
> +		.has_reset_reg = 1,
>  		.regs_td_base = PCF2131_REG_TIME_DATE_BASE,
>  		.regs_alarm_base = PCF2131_REG_ALARM_BASE,
>  		.reg_wd_ctl = PCF2131_REG_WD_CTL,
>  		.reg_wd_val = PCF2131_REG_WD_VAL,
>  		.reg_clkout = PCF2131_REG_CLKOUT,
> +		.reg_reset  = PCF2131_REG_SR_RESET,
>  		.ts_count = 4,
>  		.ts[0] = {
>  			.regs_base = PCF2131_REG_TS1_BASE,
> @@ -1075,6 +1081,20 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
>  	clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, pcf2127->rtc->features);
>  	clear_bit(RTC_FEATURE_ALARM, pcf2127->rtc->features);
>  
> +	/* Read device signature if available. */
> +	if (pcf2127->cfg->has_reset_reg) {
> +		ret = regmap_read(pcf2127->regmap, pcf2127->cfg->reg_reset, &val);
> +		if (ret < 0) {
> +			dev_err(dev, "reading RESET register failed\n");

This is too verbose, please cut down on the number of strings you are
adding.

> +			return ret;
> +		}
> +
> +		if (val != PCF2131_SR_RESET_READ_PATTERN) {
> +			dev_err(dev, "invalid device signature: $%02X\n", (u8)val);

I'm also not convinced this is actually useful. This may have to be
updated for the next rtc the driver will support and what if this
contradicts what the device tree is claiming at this address?

> +			return -ENODEV;
> +		}
> +	}
> +
>  	if (alarm_irq > 0) {
>  		unsigned long flags;
>  
> -- 
> 2.30.2
> 

-- 
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

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

* Re: [PATCH v3 11/14] rtc: pcf2127: adapt time/date registers write sequence for PCF2131
  2022-12-15 15:02 ` [PATCH v3 11/14] rtc: pcf2127: adapt time/date registers write sequence for PCF2131 Hugo Villeneuve
  2023-01-07 18:44   ` Bruno Thomsen
@ 2023-01-20 17:09   ` Alexandre Belloni
  2023-01-23 21:57     ` Hugo Villeneuve
  1 sibling, 1 reply; 72+ messages in thread
From: Alexandre Belloni @ 2023-01-20 17:09 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: a.zummo, robh+dt, krzysztof.kozlowski+dt, linux-rtc, devicetree,
	linux-kernel, Hugo Villeneuve

On 15/12/2022 10:02:12-0500, Hugo Villeneuve wrote:
> From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> 
> The sequence for updating the time/date registers is slightly
> different between PCF2127/29 and PCF2131.
> 
> For PCF2127/29, during write operations, the time counting
> circuits (memory locations 03h through 09h) are automatically blocked.
> 
> For PCF2131, time/date registers write access requires setting the
> STOP bit and sending the clear prescaler instruction (CPR). STOP then
> needs to be released once write operation is completed.
> 
> Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> ---
>  drivers/rtc/rtc-pcf2127.c | 38 +++++++++++++++++++++++++++++++++++++-
>  1 file changed, 37 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> index e4b78b9c03f9..11fbdab6bf01 100644
> --- a/drivers/rtc/rtc-pcf2127.c
> +++ b/drivers/rtc/rtc-pcf2127.c
> @@ -39,6 +39,7 @@
>  #define PCF2127_REG_CTRL1		0x00
>  #define PCF2127_BIT_CTRL1_POR_OVRD		BIT(3)
>  #define PCF2127_BIT_CTRL1_TSF1			BIT(4)
> +#define PCF2127_BIT_CTRL1_STOP			BIT(5)
>  /* Control register 2 */
>  #define PCF2127_REG_CTRL2		0x01
>  #define PCF2127_BIT_CTRL2_AIE			BIT(1)
> @@ -70,6 +71,7 @@
>  #define PCF2131_REG_SR_RESET		0x05
>  #define PCF2131_SR_RESET_READ_PATTERN	0b00100100 /* Fixed pattern. */
>  #define PCF2131_SR_RESET_RESET_CMD	0x2C /* SR is bit 3. */
> +#define PCF2131_SR_RESET_CPR_CMD	0xA4 /* CPR is bit 7. */
>  /* Time and date registers */
>  #define PCF2127_REG_TIME_DATE_BASE	0x03
>  #define PCF2131_REG_TIME_DATE_BASE	0x07 /* Register 0x06 is 100th seconds,
> @@ -307,7 +309,31 @@ static int pcf2127_rtc_set_time(struct device *dev, struct rtc_time *tm)
>  	/* year */
>  	buf[i++] = bin2bcd(tm->tm_year - 100);
>  
> -	/* write register's data */
> +	/* Write access to time registers:
> +	 * PCF2127/29: no special action required.
> +	 * PCF2131:    requires setting the STOP bit. STOP bit needs to
> +	 *             be cleared after time registers are updated.
> +	 *             It is also recommended to set CPR bit, although
> +	 *             write access will work without it.
> +	 */
> +	if (pcf2127->cfg->has_reset_reg) {

This should probably be tied to the actual rtc model rather than the
presence of the reset register.
You MUST clear CPR to be able to set the time precisely.

> +		err = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL1,
> +					 PCF2127_BIT_CTRL1_STOP,
> +					 PCF2127_BIT_CTRL1_STOP);
> +		if (err) {
> +			dev_err(dev, "setting STOP bit failed\n");

This really needs to be less verbose. There is nothing a user can really
do after having seen this message. Having an error in userspace will
anyway prompt the user to retry the operation which is the only action
it can do.

> +			return err;
> +		}
> +
> +		err = regmap_write(pcf2127->regmap, pcf2127->cfg->reg_reset,
> +				   PCF2131_SR_RESET_CPR_CMD);
> +		if (err) {
> +			dev_err(dev, "sending CPR cmd failed\n");
> +			return err;
> +		}
> +	}
> +
> +	/* write time register's data */
>  	err = regmap_bulk_write(pcf2127->regmap, pcf2127->cfg->regs_td_base, buf, i);
>  	if (err) {
>  		dev_err(dev,
> @@ -315,6 +341,16 @@ static int pcf2127_rtc_set_time(struct device *dev, struct rtc_time *tm)
>  		return err;
>  	}
>  
> +	if (pcf2127->cfg->has_reset_reg) {
> +		/* Clear STOP bit (PCF2131 only) after write is completed. */
> +		err = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL1,
> +					 PCF2127_BIT_CTRL1_STOP, 0);
> +		if (err) {
> +			dev_err(dev, "clearing STOP bit failed\n");
> +			return err;
> +		}
> +	}
> +
>  	return 0;
>  }
>  
> -- 
> 2.30.2
> 

-- 
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

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

* Re: [PATCH v3 03/14] rtc: pcf2127: adapt for alarm registers at any offset
  2022-12-15 15:02 ` [PATCH v3 03/14] rtc: pcf2127: adapt for alarm " Hugo Villeneuve
  2023-01-07 16:57   ` Bruno Thomsen
@ 2023-01-20 17:10   ` Alexandre Belloni
  2023-01-23 16:02     ` Hugo Villeneuve
  1 sibling, 1 reply; 72+ messages in thread
From: Alexandre Belloni @ 2023-01-20 17:10 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: a.zummo, robh+dt, krzysztof.kozlowski+dt, linux-rtc, devicetree,
	linux-kernel, Hugo Villeneuve

On 15/12/2022 10:02:04-0500, Hugo Villeneuve wrote:
> From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> 
> This will simplify the implementation of new variants into this driver.
> 
> Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> ---
>  drivers/rtc/rtc-pcf2127.c | 42 ++++++++++++++++++++++-----------------
>  1 file changed, 24 insertions(+), 18 deletions(-)
> 
> diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> index fb0caacaabee..db0cb784c0c9 100644
> --- a/drivers/rtc/rtc-pcf2127.c
> +++ b/drivers/rtc/rtc-pcf2127.c
> @@ -56,11 +56,14 @@
>  /* Time and date registers bits */
>  #define PCF2127_BIT_SC_OSF			BIT(7)
>  /* Alarm registers */
> -#define PCF2127_REG_ALARM_SC		0x0A
> -#define PCF2127_REG_ALARM_MN		0x0B
> -#define PCF2127_REG_ALARM_HR		0x0C
> -#define PCF2127_REG_ALARM_DM		0x0D
> -#define PCF2127_REG_ALARM_DW		0x0E
> +#define PCF2127_REG_ALARM_BASE		0x0A
> +/* Alarm registers offsets (starting from base register) */
> +#define PCF2127_OFFSET_ALARM_SC		0
> +#define PCF2127_OFFSET_ALARM_MN		1
> +#define PCF2127_OFFSET_ALARM_HR		2
> +#define PCF2127_OFFSET_ALARM_DM		3
> +#define PCF2127_OFFSET_ALARM_DW		4
> +/* Alarm bits */
>  #define PCF2127_BIT_ALARM_AE			BIT(7)
>  /* CLKOUT control register */
>  #define PCF2127_REG_CLKOUT		0x0f
> @@ -110,6 +113,7 @@ struct pcf21xx_config {
>  	unsigned int has_nvmem:1;
>  	unsigned int has_bit_wd_ctl_cd0:1;
>  	u8 regs_td_base; /* Time/data base registers. */
> +	u8 regs_alarm_base; /* Alarm function base registers. */
>  };
>  
>  struct pcf2127 {
> @@ -402,18 +406,18 @@ static int pcf2127_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
>  	if (ret)
>  		return ret;
>  
> -	ret = regmap_bulk_read(pcf2127->regmap, PCF2127_REG_ALARM_SC, buf,
> -			       sizeof(buf));
> +	ret = regmap_bulk_read(pcf2127->regmap, pcf2127->cfg->regs_alarm_base,
> +			       buf, sizeof(buf));
>  	if (ret)
>  		return ret;
>  
>  	alrm->enabled = ctrl2 & PCF2127_BIT_CTRL2_AIE;
>  	alrm->pending = ctrl2 & PCF2127_BIT_CTRL2_AF;
>  
> -	alrm->time.tm_sec = bcd2bin(buf[0] & 0x7F);
> -	alrm->time.tm_min = bcd2bin(buf[1] & 0x7F);
> -	alrm->time.tm_hour = bcd2bin(buf[2] & 0x3F);
> -	alrm->time.tm_mday = bcd2bin(buf[3] & 0x3F);
> +	alrm->time.tm_sec = bcd2bin(buf[PCF2127_OFFSET_ALARM_SC] & 0x7F);
> +	alrm->time.tm_min = bcd2bin(buf[PCF2127_OFFSET_ALARM_MN] & 0x7F);
> +	alrm->time.tm_hour = bcd2bin(buf[PCF2127_OFFSET_ALARM_HR] & 0x3F);
> +	alrm->time.tm_mday = bcd2bin(buf[PCF2127_OFFSET_ALARM_DM] & 0x3F);

I feel like this is adding defines for the sake of having defines. I
don't think this is particularly useful.


>  
>  	return 0;
>  }
> @@ -447,14 +451,14 @@ static int pcf2127_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
>  	if (ret)
>  		return ret;
>  
> -	buf[0] = bin2bcd(alrm->time.tm_sec);
> -	buf[1] = bin2bcd(alrm->time.tm_min);
> -	buf[2] = bin2bcd(alrm->time.tm_hour);
> -	buf[3] = bin2bcd(alrm->time.tm_mday);
> -	buf[4] = PCF2127_BIT_ALARM_AE; /* Do not match on week day */
> +	buf[PCF2127_OFFSET_ALARM_SC] = bin2bcd(alrm->time.tm_sec);
> +	buf[PCF2127_OFFSET_ALARM_MN] = bin2bcd(alrm->time.tm_min);
> +	buf[PCF2127_OFFSET_ALARM_HR] = bin2bcd(alrm->time.tm_hour);
> +	buf[PCF2127_OFFSET_ALARM_DM] = bin2bcd(alrm->time.tm_mday);
> +	buf[PCF2127_OFFSET_ALARM_DW] = PCF2127_BIT_ALARM_AE; /* Do not match on week day */
>  
> -	ret = regmap_bulk_write(pcf2127->regmap, PCF2127_REG_ALARM_SC, buf,
> -				sizeof(buf));
> +	ret = regmap_bulk_write(pcf2127->regmap, pcf2127->cfg->regs_alarm_base,
> +				buf, sizeof(buf));
>  	if (ret)
>  		return ret;
>  
> @@ -659,12 +663,14 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
>  		.has_nvmem = 1,
>  		.has_bit_wd_ctl_cd0 = 1,
>  		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
> +		.regs_alarm_base = PCF2127_REG_ALARM_BASE,
>  	},
>  	[PCF2129] = {
>  		.max_register = 0x19,
>  		.has_nvmem = 0,
>  		.has_bit_wd_ctl_cd0 = 0,
>  		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
> +		.regs_alarm_base = PCF2127_REG_ALARM_BASE,
>  	},
>  };
>  
> -- 
> 2.30.2
> 

-- 
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

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

* Re: [PATCH v3 02/14] rtc: pcf2127: adapt for time/date registers at any offset
  2022-12-15 15:02 ` [PATCH v3 02/14] rtc: pcf2127: adapt for time/date registers at any offset Hugo Villeneuve
  2022-12-19  9:34   ` Bruno Thomsen
@ 2023-01-20 18:47   ` Alexandre Belloni
  2023-01-23 15:54     ` Hugo Villeneuve
  1 sibling, 1 reply; 72+ messages in thread
From: Alexandre Belloni @ 2023-01-20 18:47 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: a.zummo, robh+dt, krzysztof.kozlowski+dt, linux-rtc, devicetree,
	linux-kernel, Hugo Villeneuve

On 15/12/2022 10:02:03-0500, Hugo Villeneuve wrote:
> From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> 
> This will simplify the implementation of new variants into this driver.
> 
> Some variants (PCF2131) have a 100th seconds register. This register is
> currently not supported in this driver.
> 
> Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> ---
>  drivers/rtc/rtc-pcf2127.c | 68 ++++++++++++++++++++++-----------------
>  1 file changed, 39 insertions(+), 29 deletions(-)
> 
> diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> index b9a5d47a439f..fb0caacaabee 100644
> --- a/drivers/rtc/rtc-pcf2127.c
> +++ b/drivers/rtc/rtc-pcf2127.c
> @@ -44,14 +44,17 @@
>  #define PCF2127_BIT_CTRL3_BF			BIT(3)
>  #define PCF2127_BIT_CTRL3_BTSE			BIT(4)
>  /* Time and date registers */
> -#define PCF2127_REG_SC			0x03
> +#define PCF2127_REG_TIME_DATE_BASE	0x03
> +/* Time and date registers offsets (starting from base register) */
> +#define PCF2127_OFFSET_TD_SC		0
> +#define PCF2127_OFFSET_TD_MN		1
> +#define PCF2127_OFFSET_TD_HR		2
> +#define PCF2127_OFFSET_TD_DM		3
> +#define PCF2127_OFFSET_TD_DW		4
> +#define PCF2127_OFFSET_TD_MO		5
> +#define PCF2127_OFFSET_TD_YR		6

Same comment as for the alarms, I would simply remove the defines as
they don't really carry any useful information.

> +/* Time and date registers bits */
>  #define PCF2127_BIT_SC_OSF			BIT(7)
> -#define PCF2127_REG_MN			0x04
> -#define PCF2127_REG_HR			0x05
> -#define PCF2127_REG_DM			0x06
> -#define PCF2127_REG_DW			0x07
> -#define PCF2127_REG_MO			0x08
> -#define PCF2127_REG_YR			0x09
>  /* Alarm registers */
>  #define PCF2127_REG_ALARM_SC		0x0A
>  #define PCF2127_REG_ALARM_MN		0x0B
> @@ -106,6 +109,7 @@ struct pcf21xx_config {
>  	int max_register;
>  	unsigned int has_nvmem:1;
>  	unsigned int has_bit_wd_ctl_cd0:1;
> +	u8 regs_td_base; /* Time/data base registers. */
>  };
>  
>  struct pcf2127 {
> @@ -125,27 +129,31 @@ struct pcf2127 {
>  static int pcf2127_rtc_read_time(struct device *dev, struct rtc_time *tm)
>  {
>  	struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
> -	unsigned char buf[10];
> +	unsigned char buf[7];
> +	unsigned int ctrl3;
>  	int ret;
>  
>  	/*
>  	 * Avoid reading CTRL2 register as it causes WD_VAL register
>  	 * value to reset to 0 which means watchdog is stopped.
>  	 */
> -	ret = regmap_bulk_read(pcf2127->regmap, PCF2127_REG_CTRL3,
> -			       (buf + PCF2127_REG_CTRL3),
> -			       ARRAY_SIZE(buf) - PCF2127_REG_CTRL3);
> -	if (ret) {
> -		dev_err(dev, "%s: read error\n", __func__);
> +	ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL3, &ctrl3);
> +	if (ret)
>  		return ret;
> -	}
>  
> -	if (buf[PCF2127_REG_CTRL3] & PCF2127_BIT_CTRL3_BLF)
> +	if (ctrl3 & PCF2127_BIT_CTRL3_BLF)
>  		dev_info(dev,
>  			"low voltage detected, check/replace RTC battery.\n");
>  
> +	ret = regmap_bulk_read(pcf2127->regmap, pcf2127->cfg->regs_td_base,
> +			       buf, sizeof(buf));
> +	if (ret) {
> +		dev_err(dev, "%s: read error\n", __func__);
> +		return ret;
> +	}
> +
>  	/* Clock integrity is not guaranteed when OSF flag is set. */
> -	if (buf[PCF2127_REG_SC] & PCF2127_BIT_SC_OSF) {
> +	if (buf[PCF2127_OFFSET_TD_SC] & PCF2127_BIT_SC_OSF) {
>  		/*
>  		 * no need clear the flag here,
>  		 * it will be cleared once the new date is saved
> @@ -158,18 +166,18 @@ static int pcf2127_rtc_read_time(struct device *dev, struct rtc_time *tm)
>  	dev_dbg(dev,
>  		"%s: raw data is cr3=%02x, sec=%02x, min=%02x, hr=%02x, "
>  		"mday=%02x, wday=%02x, mon=%02x, year=%02x\n",
> -		__func__, buf[PCF2127_REG_CTRL3], buf[PCF2127_REG_SC],
> -		buf[PCF2127_REG_MN], buf[PCF2127_REG_HR],
> -		buf[PCF2127_REG_DM], buf[PCF2127_REG_DW],
> -		buf[PCF2127_REG_MO], buf[PCF2127_REG_YR]);
> -
> -	tm->tm_sec = bcd2bin(buf[PCF2127_REG_SC] & 0x7F);
> -	tm->tm_min = bcd2bin(buf[PCF2127_REG_MN] & 0x7F);
> -	tm->tm_hour = bcd2bin(buf[PCF2127_REG_HR] & 0x3F); /* rtc hr 0-23 */
> -	tm->tm_mday = bcd2bin(buf[PCF2127_REG_DM] & 0x3F);
> -	tm->tm_wday = buf[PCF2127_REG_DW] & 0x07;
> -	tm->tm_mon = bcd2bin(buf[PCF2127_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */
> -	tm->tm_year = bcd2bin(buf[PCF2127_REG_YR]);
> +		__func__, ctrl3, buf[PCF2127_OFFSET_TD_SC],
> +		buf[PCF2127_OFFSET_TD_MN], buf[PCF2127_OFFSET_TD_HR],
> +		buf[PCF2127_OFFSET_TD_DM], buf[PCF2127_OFFSET_TD_DW],
> +		buf[PCF2127_OFFSET_TD_MO], buf[PCF2127_OFFSET_TD_YR]);
> +
> +	tm->tm_sec = bcd2bin(buf[PCF2127_OFFSET_TD_SC] & 0x7F);
> +	tm->tm_min = bcd2bin(buf[PCF2127_OFFSET_TD_MN] & 0x7F);
> +	tm->tm_hour = bcd2bin(buf[PCF2127_OFFSET_TD_HR] & 0x3F); /* rtc hr 0-23 */

You can drop the comment

> +	tm->tm_mday = bcd2bin(buf[PCF2127_OFFSET_TD_DM] & 0x3F);
> +	tm->tm_wday = buf[PCF2127_OFFSET_TD_DW] & 0x07;
> +	tm->tm_mon = bcd2bin(buf[PCF2127_OFFSET_TD_MO] & 0x1F) - 1; /* rtc mn 1-12 */

This comment too.

> +	tm->tm_year = bcd2bin(buf[PCF2127_OFFSET_TD_YR]);
>  	tm->tm_year += 100;
>  
>  	dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
> @@ -207,7 +215,7 @@ static int pcf2127_rtc_set_time(struct device *dev, struct rtc_time *tm)
>  	buf[i++] = bin2bcd(tm->tm_year - 100);
>  
>  	/* write register's data */
> -	err = regmap_bulk_write(pcf2127->regmap, PCF2127_REG_SC, buf, i);
> +	err = regmap_bulk_write(pcf2127->regmap, pcf2127->cfg->regs_td_base, buf, i);
>  	if (err) {
>  		dev_err(dev,
>  			"%s: err=%d", __func__, err);
> @@ -650,11 +658,13 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
>  		.max_register = 0x1d,
>  		.has_nvmem = 1,
>  		.has_bit_wd_ctl_cd0 = 1,
> +		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
>  	},
>  	[PCF2129] = {
>  		.max_register = 0x19,
>  		.has_nvmem = 0,
>  		.has_bit_wd_ctl_cd0 = 0,
> +		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
>  	},
>  };
>  
> -- 
> 2.30.2
> 

-- 
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

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

* Re: [PATCH v3 07/14] rtc: pcf2127: add support for PCF2131 RTC
  2022-12-15 15:02 ` [PATCH v3 07/14] rtc: pcf2127: add support for PCF2131 RTC Hugo Villeneuve
  2023-01-07 18:15   ` Bruno Thomsen
@ 2023-01-20 18:57   ` Alexandre Belloni
  2023-01-23 17:27     ` Hugo Villeneuve
  1 sibling, 1 reply; 72+ messages in thread
From: Alexandre Belloni @ 2023-01-20 18:57 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: a.zummo, robh+dt, krzysztof.kozlowski+dt, linux-rtc, devicetree,
	linux-kernel, Hugo Villeneuve

On 15/12/2022 10:02:08-0500, Hugo Villeneuve wrote:
>  	  PCF2127 has an additional feature of 512 bytes battery backed
> diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> index 3265878edc48..4148e135f935 100644
> --- a/drivers/rtc/rtc-pcf2127.c
> +++ b/drivers/rtc/rtc-pcf2127.c
> @@ -1,16 +1,26 @@
>  // SPDX-License-Identifier: GPL-2.0-only
>  /*
> - * An I2C and SPI driver for the NXP PCF2127/29 RTC
> + * An I2C and SPI driver for the NXP PCF2127/29/31 RTC
>   * Copyright 2013 Til-Technologies
> + * Copyright 2021 DimOnOff

For the record, I don't really like that because git will be the
authoritative source for the copyright. This will only end up being
outdated info (as it is already)

>   *
>   * Author: Renaud Cerrato <r.cerrato@til-technologies.fr>
>   *
>   * Watchdog and tamper functions
>   * Author: Bruno Thomsen <bruno.thomsen@gmail.com>
>   *
> + * PCF2131 support
> + * Author: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> + *
>   * based on the other drivers in this same directory.
>   *
> - * Datasheet: https://www.nxp.com/docs/en/data-sheet/PCF2127.pdf
> + * Datasheets: https://www.nxp.com/docs/en/data-sheet/PCF2127.pdf
> + *             https://www.nxp.com/docs/en/data-sheet/PCF2131DS.pdf
> + */
> +
> +/*
> + * The following features are not yet implemented for the PCF2131:
> + *   - support for 1/100th seconds

This will never be added so I would remove that comment

>   */
>  
>  #include <linux/i2c.h>
> @@ -43,8 +53,30 @@
>  #define PCF2127_BIT_CTRL3_BLF			BIT(2)
>  #define PCF2127_BIT_CTRL3_BF			BIT(3)
>  #define PCF2127_BIT_CTRL3_BTSE			BIT(4)
> +/* Control register 4 */
> +#define PCF2131_REG_CTRL4		0x03
> +#define PCF2131_BIT_CTRL4_TSF4			BIT(4)
> +#define PCF2131_BIT_CTRL4_TSF3			BIT(5)
> +#define PCF2131_BIT_CTRL4_TSF2			BIT(6)
> +#define PCF2131_BIT_CTRL4_TSF1			BIT(7)
> +/* Control register 5 */
> +#define PCF2131_REG_CTRL5		0x04
> +#define PCF2131_BIT_CTRL5_TSIE4			BIT(4)
> +#define PCF2131_BIT_CTRL5_TSIE3			BIT(5)
> +#define PCF2131_BIT_CTRL5_TSIE2			BIT(6)
> +#define PCF2131_BIT_CTRL5_TSIE1			BIT(7)
> +/* Software reset register */
> +#define PCF2131_REG_SR_RESET		0x05
> +#define PCF2131_SR_RESET_READ_PATTERN	0b00100100 /* Fixed pattern. */
> +#define PCF2131_SR_RESET_RESET_CMD	0x2C /* SR is bit 3. */
>  /* Time and date registers */
>  #define PCF2127_REG_TIME_DATE_BASE	0x03
> +#define PCF2131_REG_TIME_DATE_BASE	0x07 /* Register 0x06 is 100th seconds,
> +					      * but we do not support it. By
> +					      * using offset 0x07, we can be
> +					      * compatible with existing
> +					      * time/date functions.
> +					      */

Because we will never support 100th of seconds, this comment is not
useful

>  /* Time and date registers offsets (starting from base register) */
>  #define PCF2127_OFFSET_TD_SC		0
>  #define PCF2127_OFFSET_TD_MN		1
> @@ -57,6 +89,7 @@
>  #define PCF2127_BIT_SC_OSF			BIT(7)
>  /* Alarm registers */
>  #define PCF2127_REG_ALARM_BASE		0x0A
> +#define PCF2131_REG_ALARM_BASE		0x0E

I'd keep the defines ordered by address, so you could move all the
PCF2131 defines after the PCF2127 ones

>  /* Alarm registers offsets (starting from base register) */
>  #define PCF2127_OFFSET_ALARM_SC		0
>  #define PCF2127_OFFSET_ALARM_MN		1
> @@ -67,16 +100,26 @@
>  #define PCF2127_BIT_ALARM_AE			BIT(7)
>  /* CLKOUT control register */
>  #define PCF2127_REG_CLKOUT		0x0f
> +#define PCF2131_REG_CLKOUT		0x13
>  #define PCF2127_BIT_CLKOUT_OTPR			BIT(5)
>  /* Watchdog registers */
>  #define PCF2127_REG_WD_CTL		0x10
> +#define PCF2131_REG_WD_CTL		0x35
>  #define PCF2127_BIT_WD_CTL_TF0			BIT(0)
>  #define PCF2127_BIT_WD_CTL_TF1			BIT(1)
>  #define PCF2127_BIT_WD_CTL_CD0			BIT(6)
>  #define PCF2127_BIT_WD_CTL_CD1			BIT(7)
>  #define PCF2127_REG_WD_VAL		0x11
> +#define PCF2131_REG_WD_VAL		0x36
>  /* Tamper timestamp1 registers */
>  #define PCF2127_REG_TS1_BASE		0x12
> +#define PCF2131_REG_TS1_BASE		0x14
> +/* Tamper timestamp2 registers */
> +#define PCF2131_REG_TS2_BASE		0x1B
> +/* Tamper timestamp3 registers */
> +#define PCF2131_REG_TS3_BASE		0x22
> +/* Tamper timestamp4 registers */
> +#define PCF2131_REG_TS4_BASE		0x29
>  /* Tamper timestamp registers common offsets (starting from base register) */
>  #define PCF2127_OFFSET_TS_CTL		0
>  #define PCF2127_OFFSET_TS_SC		1
> @@ -92,11 +135,22 @@
>   * RAM registers
>   * PCF2127 has 512 bytes general-purpose static RAM (SRAM) that is
>   * battery backed and can survive a power outage.
> - * PCF2129 doesn't have this feature.
> + * PCF2129/31 doesn't have this feature.
>   */
>  #define PCF2127_REG_RAM_ADDR_MSB	0x1A
>  #define PCF2127_REG_RAM_WRT_CMD		0x1C
>  #define PCF2127_REG_RAM_RD_CMD		0x1D
> +/* Interrupt mask registers */
> +#define PCF2131_REG_INT_A_MASK1		0x31
> +#define PCF2131_REG_INT_A_MASK2		0x32
> +#define PCF2131_REG_INT_B_MASK1		0x33
> +#define PCF2131_REG_INT_B_MASK2		0x34
> +#define PCF2131_BIT_INT_BLIE		BIT(0)
> +#define PCF2131_BIT_INT_BIE		BIT(1)
> +#define PCF2131_BIT_INT_AIE		BIT(2)
> +#define PCF2131_BIT_INT_WD_CD		BIT(3)
> +#define PCF2131_BIT_INT_SI		BIT(4)
> +#define PCF2131_BIT_INT_MI		BIT(5)
>  
>  /* Watchdog timer value constants */
>  #define PCF2127_WD_VAL_STOP		0
> @@ -110,6 +164,14 @@
>  		PCF2127_BIT_CTRL2_AF | \
>  		PCF2127_BIT_CTRL2_WDTF | \
>  		PCF2127_BIT_CTRL2_TSF2)
> +#define PCF2131_CTRL2_IRQ_MASK ( \
> +		PCF2127_BIT_CTRL2_AF | \
> +		PCF2127_BIT_CTRL2_WDTF)
> +#define PCF2131_CTRL4_IRQ_MASK ( \
> +		PCF2131_BIT_CTRL4_TSF4 | \
> +		PCF2131_BIT_CTRL4_TSF3 | \
> +		PCF2131_BIT_CTRL4_TSF2 | \
> +		PCF2131_BIT_CTRL4_TSF1)
>  
>  struct pcf21xx_ts_config {
>  	u8 regs_base; /* Base register to read timestamp values. */
> @@ -370,7 +432,7 @@ static int pcf2127_wdt_set_timeout(struct watchdog_device *wdd,
>  }
>  
>  static const struct watchdog_info pcf2127_wdt_info = {
> -	.identity = "NXP PCF2127/PCF2129 Watchdog",
> +	.identity = "NXP PCF2127/29/31 Watchdog",

This change may break userspace tools as this is exposed to userspace


-- 
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

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

* Re: [PATCH v3 00/14] rtc: pcf2127: add PCF2131 driver
  2022-12-15 15:02 [PATCH v3 00/14] rtc: pcf2127: add PCF2131 driver Hugo Villeneuve
                   ` (13 preceding siblings ...)
  2022-12-15 15:02 ` [PATCH v3 14/14] dt-bindings: rtc: pcf2127: add PCF2131 Hugo Villeneuve
@ 2023-01-20 19:05 ` Alexandre Belloni
  2023-01-23 15:51   ` Hugo Villeneuve
  2023-06-21 14:14   ` Hugo Villeneuve
  14 siblings, 2 replies; 72+ messages in thread
From: Alexandre Belloni @ 2023-01-20 19:05 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: a.zummo, robh+dt, krzysztof.kozlowski+dt, linux-rtc, devicetree,
	linux-kernel, Hugo Villeneuve

Hello,

I know I've been holding off on the review of this series for a while
and I'm sorry for that.

One of the main issue that is remaining is that the driver ends up being
53% bigger and generaly less efficient for no added functionality for
the existing RTCs.

I know performance is not a concern however, having more code in the
set/read time and irq paths means that it is more difficult to set an
get the time precisely.

I guess I'll take it as a merged driver but I took a different decision
for other RTCs.

On 15/12/2022 10:02:01-0500, Hugo Villeneuve wrote:
> From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> 
> Hello,
> this patch series adds the driver for the PCF2131 real-time clock.
> 
> This RTC is very similar in functionality to the PCF2127/29 with the
> following differences:
>   -supports two new control registers at offsets 4 and 5
>   -supports a new reset register
>   -supports 4 tamper detection functions instead of 1
>   -has no nvmem (like the PCF2129)
>   -has two output interrupt pins instead of one
>   -has 1/100th seconds capabilities (not supported in this driver)
>   -pcf2127 has watchdog clock sources: 1/60,   1, 64 and 4096Hz
>    pcf2131 has watchdog clock sources: 1/64, 1/4,  4 and   64Hz
>   -watchdog value register cannot be read after being set
> 
> Most of the register addresses are very different, although they still
> follow the same layout. For example, the time/date and tamper registers
> have a different base address, but the offsets are all the same.
> Consequently, the source code of the PCF2127 driver can be easily adapted
> to support this new device.
> 
> Patches 1 to 6 modify the existing pcf2127 driver to make it more generic
> and able to support multiple variants, like the PCF2131. This is done
> mostly by using offsets instead of absolute hardcoded register addresses.
> 
> Patch 7 add actual support for the PCF2131.
> 
> Patch 8 configures all interrupt sources to go through the INT A pin.
> 
> Patch 9 changes the PWRMNG bits to be the same with the PCF2131 as they
>       are with the PCF2127/29 (different default values).
> 
> Patch 10 allow to confirm PCF2131 device presence by reading the reset
>       register fixed pattern.
> 
> Patch 11 adapt the time/date registers write sequence for PCF2131 (STOP and
>       CPR bits).
> 
> Patch 12 add support for generic watchdog timing configuration.
> 
> Patch 13 add a new flag to identify if device has read support for reading
>       watchdog register value.
>       Since the watchdog value register cannot be read on the PCF2131 after
>       being set, it seems that we cannot detect if watchdog timer was
>       started by bootloader. I am not sure what is the best way to handle
>       this situation, suggestions are welcomed.
> 
> Patch 14 add the dt-bindings for the PCF2131.
> 
> I have tested the driver using a PCF2131-ARD evaluation board connected to
> an NXP imx8mp evaluation board:
>   - Time get/set ok;
>   - Alarms get/set ok
>   - Timestamp 1 to 4 ok
>   - IRQ alarm ok
>   - Watchdog ok
>   - Also tested successfully with "RTC Driver Test Example" from
>     Documentation/rtc.txt
> 
> I have also tested the driver on a custom PCF2129 adapter board connected to a
> beaglebone black.
> 
> Thank you.
> 
> Link: [v1] https://patchwork.ozlabs.org/project/rtc-linux/patch/20220125200009.900660-2-hugo@hugovil.com/
> Link: [v2] https://patchwork.ozlabs.org/project/rtc-linux/list/?series=285734
> 
> Changes for V3:
> - Rebased for kernel v6.1
> 
> Changes for V2:
> - In general, fix and improvements after I have tested on real hardware
> - Fix alarm interrupt A/B mask setting for PCF2131:
>   PCF2131_BIT_INT_AIE must be cleared, not set, to enable interrupt.
> - Remove low_reg validation: only check if TS interrupt flag is
>   defined, as low_reg is defined at address 0 for PCF2127/29.
> - Change PWRMNG value for PCF2131: default is different than PCF2127/29.
> - Adapt time/date registers write sequence for PCF2131 (STOP and CPR bits).
> - Map all interrupt sources to INT A pin
> - Read and validate PCF2131 device presence from RESET register
> - Adapt watchdog configuration for PCF2131
> 
> Hugo Villeneuve (14):
>   rtc: pcf2127: add variant-specific configuration structure
>   rtc: pcf2127: adapt for time/date registers at any offset
>   rtc: pcf2127: adapt for alarm registers at any offset
>   rtc: pcf2127: adapt for WD registers at any offset
>   rtc: pcf2127: adapt for CLKOUT register at any offset
>   rtc: pcf2127: add support for multiple TS functions
>   rtc: pcf2127: add support for PCF2131 RTC
>   rtc: pcf2127: add support for PCF2131 interrupts on output INT_A
>   rtc: pcf2127: set PWRMNG value for PCF2131
>   rtc: pcf2127: read and validate PCF2131 device signature
>   rtc: pcf2127: adapt time/date registers write sequence for PCF2131
>   rtc: pcf2127: support generic watchdog timing configuration
>   rtc: pcf2127: add flag for watchdog register value read support
>   dt-bindings: rtc: pcf2127: add PCF2131
> 
>  .../devicetree/bindings/rtc/nxp,pcf2127.yaml  |   4 +-
>  drivers/rtc/Kconfig                           |   4 +-
>  drivers/rtc/rtc-pcf2127.c                     | 939 ++++++++++++++----
>  3 files changed, 752 insertions(+), 195 deletions(-)
> 
> -- 
> 2.30.2
> 

-- 
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

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

* Re: [PATCH v3 00/14] rtc: pcf2127: add PCF2131 driver
  2023-01-20 19:05 ` [PATCH v3 00/14] rtc: pcf2127: add PCF2131 driver Alexandre Belloni
@ 2023-01-23 15:51   ` Hugo Villeneuve
  2023-06-21 14:14   ` Hugo Villeneuve
  1 sibling, 0 replies; 72+ messages in thread
From: Hugo Villeneuve @ 2023-01-23 15:51 UTC (permalink / raw)
  To: Alexandre Belloni
  Cc: a.zummo, robh+dt, krzysztof.kozlowski+dt, linux-rtc, devicetree,
	linux-kernel, Hugo Villeneuve

On Fri, 20 Jan 2023 20:05:07 +0100
Alexandre Belloni <alexandre.belloni@bootlin.com> wrote:

> Hello,
> 
> I know I've been holding off on the review of this series for a while
> and I'm sorry for that.
> 
> One of the main issue that is remaining is that the driver ends up being
> 53% bigger and generaly less efficient for no added functionality for
> the existing RTCs.

Hi Alexandre,
that is why before submitting my driver I sent an RFC on the RTC mailing list to ask what was the better approach for developping this driver (separate or merged into pcf2127), but I didn't got an answer from any of the maintainers.

> I know performance is not a concern however, having more code in the
> set/read time and irq paths means that it is more difficult to set an
> get the time precisely.

Just some ideas about that...

Looking at pcf2127_rtc_read_time(), we now do a separate read operation of CTRL3 to check for a low voltage condition. And we only display a message if the battery is low (no abort of read time). Looking at pcf8523 driver for example, this check is done only when responding to an ioctl. Could we do the same in our driver?

Another scheme I say is in rtc-ab-b5ze-s3 driver where this detection is done at startup and using the BLF interrupt flag...

> I guess I'll take it as a merged driver but I took a different decision
> for other RTCs.

I'll address all the comments/issues Bruno and you found and submit a V4 soon then.

Thank you, Hugo.


> On 15/12/2022 10:02:01-0500, Hugo Villeneuve wrote:
> > From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > 
> > Hello,
> > this patch series adds the driver for the PCF2131 real-time clock.
> > 
> > This RTC is very similar in functionality to the PCF2127/29 with the
> > following differences:
> >   -supports two new control registers at offsets 4 and 5
> >   -supports a new reset register
> >   -supports 4 tamper detection functions instead of 1
> >   -has no nvmem (like the PCF2129)
> >   -has two output interrupt pins instead of one
> >   -has 1/100th seconds capabilities (not supported in this driver)
> >   -pcf2127 has watchdog clock sources: 1/60,   1, 64 and 4096Hz
> >    pcf2131 has watchdog clock sources: 1/64, 1/4,  4 and   64Hz
> >   -watchdog value register cannot be read after being set
> > 
> > Most of the register addresses are very different, although they still
> > follow the same layout. For example, the time/date and tamper registers
> > have a different base address, but the offsets are all the same.
> > Consequently, the source code of the PCF2127 driver can be easily adapted
> > to support this new device.
> > 
> > Patches 1 to 6 modify the existing pcf2127 driver to make it more generic
> > and able to support multiple variants, like the PCF2131. This is done
> > mostly by using offsets instead of absolute hardcoded register addresses.
> > 
> > Patch 7 add actual support for the PCF2131.
> > 
> > Patch 8 configures all interrupt sources to go through the INT A pin.
> > 
> > Patch 9 changes the PWRMNG bits to be the same with the PCF2131 as they
> >       are with the PCF2127/29 (different default values).
> > 
> > Patch 10 allow to confirm PCF2131 device presence by reading the reset
> >       register fixed pattern.
> > 
> > Patch 11 adapt the time/date registers write sequence for PCF2131 (STOP and
> >       CPR bits).
> > 
> > Patch 12 add support for generic watchdog timing configuration.
> > 
> > Patch 13 add a new flag to identify if device has read support for reading
> >       watchdog register value.
> >       Since the watchdog value register cannot be read on the PCF2131 after
> >       being set, it seems that we cannot detect if watchdog timer was
> >       started by bootloader. I am not sure what is the best way to handle
> >       this situation, suggestions are welcomed.
> > 
> > Patch 14 add the dt-bindings for the PCF2131.
> > 
> > I have tested the driver using a PCF2131-ARD evaluation board connected to
> > an NXP imx8mp evaluation board:
> >   - Time get/set ok;
> >   - Alarms get/set ok
> >   - Timestamp 1 to 4 ok
> >   - IRQ alarm ok
> >   - Watchdog ok
> >   - Also tested successfully with "RTC Driver Test Example" from
> >     Documentation/rtc.txt
> > 
> > I have also tested the driver on a custom PCF2129 adapter board connected to a
> > beaglebone black.
> > 
> > Thank you.
> > 
> > Link: [v1] https://patchwork.ozlabs.org/project/rtc-linux/patch/20220125200009.900660-2-hugo@hugovil.com/
> > Link: [v2] https://patchwork.ozlabs.org/project/rtc-linux/list/?series=285734
> > 
> > Changes for V3:
> > - Rebased for kernel v6.1
> > 
> > Changes for V2:
> > - In general, fix and improvements after I have tested on real hardware
> > - Fix alarm interrupt A/B mask setting for PCF2131:
> >   PCF2131_BIT_INT_AIE must be cleared, not set, to enable interrupt.
> > - Remove low_reg validation: only check if TS interrupt flag is
> >   defined, as low_reg is defined at address 0 for PCF2127/29.
> > - Change PWRMNG value for PCF2131: default is different than PCF2127/29.
> > - Adapt time/date registers write sequence for PCF2131 (STOP and CPR bits).
> > - Map all interrupt sources to INT A pin
> > - Read and validate PCF2131 device presence from RESET register
> > - Adapt watchdog configuration for PCF2131
> > 
> > Hugo Villeneuve (14):
> >   rtc: pcf2127: add variant-specific configuration structure
> >   rtc: pcf2127: adapt for time/date registers at any offset
> >   rtc: pcf2127: adapt for alarm registers at any offset
> >   rtc: pcf2127: adapt for WD registers at any offset
> >   rtc: pcf2127: adapt for CLKOUT register at any offset
> >   rtc: pcf2127: add support for multiple TS functions
> >   rtc: pcf2127: add support for PCF2131 RTC
> >   rtc: pcf2127: add support for PCF2131 interrupts on output INT_A
> >   rtc: pcf2127: set PWRMNG value for PCF2131
> >   rtc: pcf2127: read and validate PCF2131 device signature
> >   rtc: pcf2127: adapt time/date registers write sequence for PCF2131
> >   rtc: pcf2127: support generic watchdog timing configuration
> >   rtc: pcf2127: add flag for watchdog register value read support
> >   dt-bindings: rtc: pcf2127: add PCF2131
> > 
> >  .../devicetree/bindings/rtc/nxp,pcf2127.yaml  |   4 +-
> >  drivers/rtc/Kconfig                           |   4 +-
> >  drivers/rtc/rtc-pcf2127.c                     | 939 ++++++++++++++----
> >  3 files changed, 752 insertions(+), 195 deletions(-)
> > 
> > -- 
> > 2.30.2
> > 
> 
> -- 
> Alexandre Belloni, co-owner and COO, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com
> 


-- 
Hugo Villeneuve <hugo@hugovil.com>

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

* Re: [PATCH v3 02/14] rtc: pcf2127: adapt for time/date registers at any offset
  2023-01-20 18:47   ` Alexandre Belloni
@ 2023-01-23 15:54     ` Hugo Villeneuve
  2023-06-21 18:18       ` Alexandre Belloni
  0 siblings, 1 reply; 72+ messages in thread
From: Hugo Villeneuve @ 2023-01-23 15:54 UTC (permalink / raw)
  To: Alexandre Belloni
  Cc: a.zummo, robh+dt, krzysztof.kozlowski+dt, linux-rtc, devicetree,
	linux-kernel, Hugo Villeneuve

On Fri, 20 Jan 2023 19:47:53 +0100
Alexandre Belloni <alexandre.belloni@bootlin.com> wrote:

> On 15/12/2022 10:02:03-0500, Hugo Villeneuve wrote:
> > From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > 
> > This will simplify the implementation of new variants into this driver.
> > 
> > Some variants (PCF2131) have a 100th seconds register. This register is
> > currently not supported in this driver.
> > 
> > Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > ---
> >  drivers/rtc/rtc-pcf2127.c | 68 ++++++++++++++++++++++-----------------
> >  1 file changed, 39 insertions(+), 29 deletions(-)
> > 
> > diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> > index b9a5d47a439f..fb0caacaabee 100644
> > --- a/drivers/rtc/rtc-pcf2127.c
> > +++ b/drivers/rtc/rtc-pcf2127.c
> > @@ -44,14 +44,17 @@
> >  #define PCF2127_BIT_CTRL3_BF			BIT(3)
> >  #define PCF2127_BIT_CTRL3_BTSE			BIT(4)
> >  /* Time and date registers */
> > -#define PCF2127_REG_SC			0x03
> > +#define PCF2127_REG_TIME_DATE_BASE	0x03
> > +/* Time and date registers offsets (starting from base register) */
> > +#define PCF2127_OFFSET_TD_SC		0
> > +#define PCF2127_OFFSET_TD_MN		1
> > +#define PCF2127_OFFSET_TD_HR		2
> > +#define PCF2127_OFFSET_TD_DM		3
> > +#define PCF2127_OFFSET_TD_DW		4
> > +#define PCF2127_OFFSET_TD_MO		5
> > +#define PCF2127_OFFSET_TD_YR		6
> 
> Same comment as for the alarms, I would simply remove the defines as
> they don't really carry any useful information.

Note that if I remove them, the patch for pcf2127_rtc_read_time() would look like this:

 	/* Clock integrity is not guaranteed when OSF flag is set. */
-	if (buf[PCF2127_REG_SC] & PCF2127_BIT_SC_OSF) {
+	if (buf[0] & PCF2127_BIT_SC_OSF) {
...
-		__func__, buf[PCF2127_REG_CTRL3], buf[PCF2127_REG_SC],
-		buf[PCF2127_REG_MN], buf[PCF2127_REG_HR],
-		buf[PCF2127_REG_DM], buf[PCF2127_REG_DW],
-		buf[PCF2127_REG_MO], buf[PCF2127_REG_YR]);
-
-	tm->tm_sec = bcd2bin(buf[PCF2127_REG_SC] & 0x7F);
-	tm->tm_min = bcd2bin(buf[PCF2127_REG_MN] & 0x7F);
-	tm->tm_hour = bcd2bin(buf[PCF2127_REG_HR] & 0x3F); /* rtc hr 0-23 */
-	tm->tm_mday = bcd2bin(buf[PCF2127_REG_DM] & 0x3F);
-	tm->tm_wday = buf[PCF2127_REG_DW] & 0x07;
-	tm->tm_mon = bcd2bin(buf[PCF2127_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */
-	tm->tm_year = bcd2bin(buf[PCF2127_REG_YR]);
+		__func__, ctrl3, buf[0],
+		buf[1], buf[2],
+		buf[3], buf[4],
+		buf[5], buf[PCF2127_OFFSET_TD_YR]);
+
+	tm->tm_sec = bcd2bin(buf[0] & 0x7F);
+	tm->tm_min = bcd2bin(buf[1] & 0x7F);
+	tm->tm_hour = bcd2bin(buf[2] & 0x3F); /* rtc hr 0-23 */
+	tm->tm_mday = bcd2bin(buf[3] & 0x3F);
+	tm->tm_wday = buf[4] & 0x07;
+	tm->tm_mon = bcd2bin(buf[5] & 0x1F) - 1; /* rtc mn 1-12 */
+	tm->tm_year = bcd2bin(buf[6]);

Do you still want to remove the defines then?


> > +/* Time and date registers bits */
> >  #define PCF2127_BIT_SC_OSF			BIT(7)
> > -#define PCF2127_REG_MN			0x04
> > -#define PCF2127_REG_HR			0x05
> > -#define PCF2127_REG_DM			0x06
> > -#define PCF2127_REG_DW			0x07
> > -#define PCF2127_REG_MO			0x08
> > -#define PCF2127_REG_YR			0x09
> >  /* Alarm registers */
> >  #define PCF2127_REG_ALARM_SC		0x0A
> >  #define PCF2127_REG_ALARM_MN		0x0B
> > @@ -106,6 +109,7 @@ struct pcf21xx_config {
> >  	int max_register;
> >  	unsigned int has_nvmem:1;
> >  	unsigned int has_bit_wd_ctl_cd0:1;
> > +	u8 regs_td_base; /* Time/data base registers. */
> >  };
> >  
> >  struct pcf2127 {
> > @@ -125,27 +129,31 @@ struct pcf2127 {
> >  static int pcf2127_rtc_read_time(struct device *dev, struct rtc_time *tm)
> >  {
> >  	struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
> > -	unsigned char buf[10];
> > +	unsigned char buf[7];
> > +	unsigned int ctrl3;
> >  	int ret;
> >  
> >  	/*
> >  	 * Avoid reading CTRL2 register as it causes WD_VAL register
> >  	 * value to reset to 0 which means watchdog is stopped.
> >  	 */
> > -	ret = regmap_bulk_read(pcf2127->regmap, PCF2127_REG_CTRL3,
> > -			       (buf + PCF2127_REG_CTRL3),
> > -			       ARRAY_SIZE(buf) - PCF2127_REG_CTRL3);
> > -	if (ret) {
> > -		dev_err(dev, "%s: read error\n", __func__);
> > +	ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL3, &ctrl3);
> > +	if (ret)
> >  		return ret;
> > -	}
> >  
> > -	if (buf[PCF2127_REG_CTRL3] & PCF2127_BIT_CTRL3_BLF)
> > +	if (ctrl3 & PCF2127_BIT_CTRL3_BLF)
> >  		dev_info(dev,
> >  			"low voltage detected, check/replace RTC battery.\n");
> >  
> > +	ret = regmap_bulk_read(pcf2127->regmap, pcf2127->cfg->regs_td_base,
> > +			       buf, sizeof(buf));
> > +	if (ret) {
> > +		dev_err(dev, "%s: read error\n", __func__);
> > +		return ret;
> > +	}
> > +
> >  	/* Clock integrity is not guaranteed when OSF flag is set. */
> > -	if (buf[PCF2127_REG_SC] & PCF2127_BIT_SC_OSF) {
> > +	if (buf[PCF2127_OFFSET_TD_SC] & PCF2127_BIT_SC_OSF) {
> >  		/*
> >  		 * no need clear the flag here,
> >  		 * it will be cleared once the new date is saved
> > @@ -158,18 +166,18 @@ static int pcf2127_rtc_read_time(struct device *dev, struct rtc_time *tm)
> >  	dev_dbg(dev,
> >  		"%s: raw data is cr3=%02x, sec=%02x, min=%02x, hr=%02x, "
> >  		"mday=%02x, wday=%02x, mon=%02x, year=%02x\n",
> > -		__func__, buf[PCF2127_REG_CTRL3], buf[PCF2127_REG_SC],
> > -		buf[PCF2127_REG_MN], buf[PCF2127_REG_HR],
> > -		buf[PCF2127_REG_DM], buf[PCF2127_REG_DW],
> > -		buf[PCF2127_REG_MO], buf[PCF2127_REG_YR]);
> > -
> > -	tm->tm_sec = bcd2bin(buf[PCF2127_REG_SC] & 0x7F);
> > -	tm->tm_min = bcd2bin(buf[PCF2127_REG_MN] & 0x7F);
> > -	tm->tm_hour = bcd2bin(buf[PCF2127_REG_HR] & 0x3F); /* rtc hr 0-23 */
> > -	tm->tm_mday = bcd2bin(buf[PCF2127_REG_DM] & 0x3F);
> > -	tm->tm_wday = buf[PCF2127_REG_DW] & 0x07;
> > -	tm->tm_mon = bcd2bin(buf[PCF2127_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */
> > -	tm->tm_year = bcd2bin(buf[PCF2127_REG_YR]);
> > +		__func__, ctrl3, buf[PCF2127_OFFSET_TD_SC],
> > +		buf[PCF2127_OFFSET_TD_MN], buf[PCF2127_OFFSET_TD_HR],
> > +		buf[PCF2127_OFFSET_TD_DM], buf[PCF2127_OFFSET_TD_DW],
> > +		buf[PCF2127_OFFSET_TD_MO], buf[PCF2127_OFFSET_TD_YR]);
> > +
> > +	tm->tm_sec = bcd2bin(buf[PCF2127_OFFSET_TD_SC] & 0x7F);
> > +	tm->tm_min = bcd2bin(buf[PCF2127_OFFSET_TD_MN] & 0x7F);
> > +	tm->tm_hour = bcd2bin(buf[PCF2127_OFFSET_TD_HR] & 0x3F); /* rtc hr 0-23 */
> 
> You can drop the comment

Done

> 
> > +	tm->tm_mday = bcd2bin(buf[PCF2127_OFFSET_TD_DM] & 0x3F);
> > +	tm->tm_wday = buf[PCF2127_OFFSET_TD_DW] & 0x07;
> > +	tm->tm_mon = bcd2bin(buf[PCF2127_OFFSET_TD_MO] & 0x1F) - 1; /* rtc mn 1-12 */
> 
> This comment too.

Done

> 
> > +	tm->tm_year = bcd2bin(buf[PCF2127_OFFSET_TD_YR]);
> >  	tm->tm_year += 100;
> >  
> >  	dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
> > @@ -207,7 +215,7 @@ static int pcf2127_rtc_set_time(struct device *dev, struct rtc_time *tm)
> >  	buf[i++] = bin2bcd(tm->tm_year - 100);
> >  
> >  	/* write register's data */
> > -	err = regmap_bulk_write(pcf2127->regmap, PCF2127_REG_SC, buf, i);
> > +	err = regmap_bulk_write(pcf2127->regmap, pcf2127->cfg->regs_td_base, buf, i);
> >  	if (err) {
> >  		dev_err(dev,
> >  			"%s: err=%d", __func__, err);
> > @@ -650,11 +658,13 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
> >  		.max_register = 0x1d,
> >  		.has_nvmem = 1,
> >  		.has_bit_wd_ctl_cd0 = 1,
> > +		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
> >  	},
> >  	[PCF2129] = {
> >  		.max_register = 0x19,
> >  		.has_nvmem = 0,
> >  		.has_bit_wd_ctl_cd0 = 0,
> > +		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
> >  	},
> >  };
> >  
> > -- 
> > 2.30.2
> > 
> 
> -- 
> Alexandre Belloni, co-owner and COO, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com
> 


-- 
Hugo Villeneuve <hugo@hugovil.com>

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

* Re: [PATCH v3 03/14] rtc: pcf2127: adapt for alarm registers at any offset
  2023-01-20 17:10   ` Alexandre Belloni
@ 2023-01-23 16:02     ` Hugo Villeneuve
  0 siblings, 0 replies; 72+ messages in thread
From: Hugo Villeneuve @ 2023-01-23 16:02 UTC (permalink / raw)
  To: Alexandre Belloni
  Cc: a.zummo, robh+dt, krzysztof.kozlowski+dt, linux-rtc, devicetree,
	linux-kernel, Hugo Villeneuve

On Fri, 20 Jan 2023 18:10:36 +0100
Alexandre Belloni <alexandre.belloni@bootlin.com> wrote:

> On 15/12/2022 10:02:04-0500, Hugo Villeneuve wrote:
> > From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > 
> > This will simplify the implementation of new variants into this driver.
> > 
> > Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > ---
> >  drivers/rtc/rtc-pcf2127.c | 42 ++++++++++++++++++++++-----------------
> >  1 file changed, 24 insertions(+), 18 deletions(-)
> > 
> > diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> > index fb0caacaabee..db0cb784c0c9 100644
> > --- a/drivers/rtc/rtc-pcf2127.c
> > +++ b/drivers/rtc/rtc-pcf2127.c
> > @@ -56,11 +56,14 @@
> >  /* Time and date registers bits */
> >  #define PCF2127_BIT_SC_OSF			BIT(7)
> >  /* Alarm registers */
> > -#define PCF2127_REG_ALARM_SC		0x0A
> > -#define PCF2127_REG_ALARM_MN		0x0B
> > -#define PCF2127_REG_ALARM_HR		0x0C
> > -#define PCF2127_REG_ALARM_DM		0x0D
> > -#define PCF2127_REG_ALARM_DW		0x0E
> > +#define PCF2127_REG_ALARM_BASE		0x0A
> > +/* Alarm registers offsets (starting from base register) */
> > +#define PCF2127_OFFSET_ALARM_SC		0
> > +#define PCF2127_OFFSET_ALARM_MN		1
> > +#define PCF2127_OFFSET_ALARM_HR		2
> > +#define PCF2127_OFFSET_ALARM_DM		3
> > +#define PCF2127_OFFSET_ALARM_DW		4
> > +/* Alarm bits */
> >  #define PCF2127_BIT_ALARM_AE			BIT(7)
> >  /* CLKOUT control register */
> >  #define PCF2127_REG_CLKOUT		0x0f
> > @@ -110,6 +113,7 @@ struct pcf21xx_config {
> >  	unsigned int has_nvmem:1;
> >  	unsigned int has_bit_wd_ctl_cd0:1;
> >  	u8 regs_td_base; /* Time/data base registers. */
> > +	u8 regs_alarm_base; /* Alarm function base registers. */
> >  };
> >  
> >  struct pcf2127 {
> > @@ -402,18 +406,18 @@ static int pcf2127_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
> >  	if (ret)
> >  		return ret;
> >  
> > -	ret = regmap_bulk_read(pcf2127->regmap, PCF2127_REG_ALARM_SC, buf,
> > -			       sizeof(buf));
> > +	ret = regmap_bulk_read(pcf2127->regmap, pcf2127->cfg->regs_alarm_base,
> > +			       buf, sizeof(buf));
> >  	if (ret)
> >  		return ret;
> >  
> >  	alrm->enabled = ctrl2 & PCF2127_BIT_CTRL2_AIE;
> >  	alrm->pending = ctrl2 & PCF2127_BIT_CTRL2_AF;
> >  
> > -	alrm->time.tm_sec = bcd2bin(buf[0] & 0x7F);
> > -	alrm->time.tm_min = bcd2bin(buf[1] & 0x7F);
> > -	alrm->time.tm_hour = bcd2bin(buf[2] & 0x3F);
> > -	alrm->time.tm_mday = bcd2bin(buf[3] & 0x3F);
> > +	alrm->time.tm_sec = bcd2bin(buf[PCF2127_OFFSET_ALARM_SC] & 0x7F);
> > +	alrm->time.tm_min = bcd2bin(buf[PCF2127_OFFSET_ALARM_MN] & 0x7F);
> > +	alrm->time.tm_hour = bcd2bin(buf[PCF2127_OFFSET_ALARM_HR] & 0x3F);
> > +	alrm->time.tm_mday = bcd2bin(buf[PCF2127_OFFSET_ALARM_DM] & 0x3F);
> 
> I feel like this is adding defines for the sake of having defines. I
> don't think this is particularly useful.

Ok, done.

> 
> 
> >  
> >  	return 0;
> >  }
> > @@ -447,14 +451,14 @@ static int pcf2127_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
> >  	if (ret)
> >  		return ret;
> >  
> > -	buf[0] = bin2bcd(alrm->time.tm_sec);
> > -	buf[1] = bin2bcd(alrm->time.tm_min);
> > -	buf[2] = bin2bcd(alrm->time.tm_hour);
> > -	buf[3] = bin2bcd(alrm->time.tm_mday);
> > -	buf[4] = PCF2127_BIT_ALARM_AE; /* Do not match on week day */
> > +	buf[PCF2127_OFFSET_ALARM_SC] = bin2bcd(alrm->time.tm_sec);
> > +	buf[PCF2127_OFFSET_ALARM_MN] = bin2bcd(alrm->time.tm_min);
> > +	buf[PCF2127_OFFSET_ALARM_HR] = bin2bcd(alrm->time.tm_hour);
> > +	buf[PCF2127_OFFSET_ALARM_DM] = bin2bcd(alrm->time.tm_mday);
> > +	buf[PCF2127_OFFSET_ALARM_DW] = PCF2127_BIT_ALARM_AE; /* Do not match on week day */
> >  
> > -	ret = regmap_bulk_write(pcf2127->regmap, PCF2127_REG_ALARM_SC, buf,
> > -				sizeof(buf));
> > +	ret = regmap_bulk_write(pcf2127->regmap, pcf2127->cfg->regs_alarm_base,
> > +				buf, sizeof(buf));
> >  	if (ret)
> >  		return ret;
> >  
> > @@ -659,12 +663,14 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
> >  		.has_nvmem = 1,
> >  		.has_bit_wd_ctl_cd0 = 1,
> >  		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
> > +		.regs_alarm_base = PCF2127_REG_ALARM_BASE,
> >  	},
> >  	[PCF2129] = {
> >  		.max_register = 0x19,
> >  		.has_nvmem = 0,
> >  		.has_bit_wd_ctl_cd0 = 0,
> >  		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
> > +		.regs_alarm_base = PCF2127_REG_ALARM_BASE,
> >  	},
> >  };
> >  
> > -- 
> > 2.30.2
> > 
> 
> -- 
> Alexandre Belloni, co-owner and COO, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com
> 


-- 
Hugo Villeneuve <hugo@hugovil.com>

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

* Re: [PATCH v3 07/14] rtc: pcf2127: add support for PCF2131 RTC
  2023-01-20 18:57   ` Alexandre Belloni
@ 2023-01-23 17:27     ` Hugo Villeneuve
  0 siblings, 0 replies; 72+ messages in thread
From: Hugo Villeneuve @ 2023-01-23 17:27 UTC (permalink / raw)
  To: Alexandre Belloni
  Cc: a.zummo, robh+dt, krzysztof.kozlowski+dt, linux-rtc, devicetree,
	linux-kernel, Hugo Villeneuve

On Fri, 20 Jan 2023 19:57:07 +0100
Alexandre Belloni <alexandre.belloni@bootlin.com> wrote:

> On 15/12/2022 10:02:08-0500, Hugo Villeneuve wrote:
> >  	  PCF2127 has an additional feature of 512 bytes battery backed
> > diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> > index 3265878edc48..4148e135f935 100644
> > --- a/drivers/rtc/rtc-pcf2127.c
> > +++ b/drivers/rtc/rtc-pcf2127.c
> > @@ -1,16 +1,26 @@
> >  // SPDX-License-Identifier: GPL-2.0-only
> >  /*
> > - * An I2C and SPI driver for the NXP PCF2127/29 RTC
> > + * An I2C and SPI driver for the NXP PCF2127/29/31 RTC
> >   * Copyright 2013 Til-Technologies
> > + * Copyright 2021 DimOnOff
> 
> For the record, I don't really like that because git will be the
> authoritative source for the copyright. This will only end up being
> outdated info (as it is already)

Removed.

> 
> >   *
> >   * Author: Renaud Cerrato <r.cerrato@til-technologies.fr>
> >   *
> >   * Watchdog and tamper functions
> >   * Author: Bruno Thomsen <bruno.thomsen@gmail.com>
> >   *
> > + * PCF2131 support
> > + * Author: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > + *
> >   * based on the other drivers in this same directory.
> >   *
> > - * Datasheet: https://www.nxp.com/docs/en/data-sheet/PCF2127.pdf
> > + * Datasheets: https://www.nxp.com/docs/en/data-sheet/PCF2127.pdf
> > + *             https://www.nxp.com/docs/en/data-sheet/PCF2131DS.pdf
> > + */
> > +
> > +/*
> > + * The following features are not yet implemented for the PCF2131:
> > + *   - support for 1/100th seconds
> 
> This will never be added so I would remove that comment

Done.

> 
> >   */
> >  
> >  #include <linux/i2c.h>
> > @@ -43,8 +53,30 @@
> >  #define PCF2127_BIT_CTRL3_BLF			BIT(2)
> >  #define PCF2127_BIT_CTRL3_BF			BIT(3)
> >  #define PCF2127_BIT_CTRL3_BTSE			BIT(4)
> > +/* Control register 4 */
> > +#define PCF2131_REG_CTRL4		0x03
> > +#define PCF2131_BIT_CTRL4_TSF4			BIT(4)
> > +#define PCF2131_BIT_CTRL4_TSF3			BIT(5)
> > +#define PCF2131_BIT_CTRL4_TSF2			BIT(6)
> > +#define PCF2131_BIT_CTRL4_TSF1			BIT(7)
> > +/* Control register 5 */
> > +#define PCF2131_REG_CTRL5		0x04
> > +#define PCF2131_BIT_CTRL5_TSIE4			BIT(4)
> > +#define PCF2131_BIT_CTRL5_TSIE3			BIT(5)
> > +#define PCF2131_BIT_CTRL5_TSIE2			BIT(6)
> > +#define PCF2131_BIT_CTRL5_TSIE1			BIT(7)
> > +/* Software reset register */
> > +#define PCF2131_REG_SR_RESET		0x05
> > +#define PCF2131_SR_RESET_READ_PATTERN	0b00100100 /* Fixed pattern. */
> > +#define PCF2131_SR_RESET_RESET_CMD	0x2C /* SR is bit 3. */
> >  /* Time and date registers */
> >  #define PCF2127_REG_TIME_DATE_BASE	0x03
> > +#define PCF2131_REG_TIME_DATE_BASE	0x07 /* Register 0x06 is 100th seconds,
> > +					      * but we do not support it. By
> > +					      * using offset 0x07, we can be
> > +					      * compatible with existing
> > +					      * time/date functions.
> > +					      */
> 
> Because we will never support 100th of seconds, this comment is not
> useful

Removed.

> 
> >  /* Time and date registers offsets (starting from base register) */
> >  #define PCF2127_OFFSET_TD_SC		0
> >  #define PCF2127_OFFSET_TD_MN		1
> > @@ -57,6 +89,7 @@
> >  #define PCF2127_BIT_SC_OSF			BIT(7)
> >  /* Alarm registers */
> >  #define PCF2127_REG_ALARM_BASE		0x0A
> > +#define PCF2131_REG_ALARM_BASE		0x0E
> 
> I'd keep the defines ordered by address, so you could move all the
> PCF2131 defines after the PCF2127 ones

Done.


> 
> >  /* Alarm registers offsets (starting from base register) */
> >  #define PCF2127_OFFSET_ALARM_SC		0
> >  #define PCF2127_OFFSET_ALARM_MN		1
> > @@ -67,16 +100,26 @@
> >  #define PCF2127_BIT_ALARM_AE			BIT(7)
> >  /* CLKOUT control register */
> >  #define PCF2127_REG_CLKOUT		0x0f
> > +#define PCF2131_REG_CLKOUT		0x13
> >  #define PCF2127_BIT_CLKOUT_OTPR			BIT(5)
> >  /* Watchdog registers */
> >  #define PCF2127_REG_WD_CTL		0x10
> > +#define PCF2131_REG_WD_CTL		0x35
> >  #define PCF2127_BIT_WD_CTL_TF0			BIT(0)
> >  #define PCF2127_BIT_WD_CTL_TF1			BIT(1)
> >  #define PCF2127_BIT_WD_CTL_CD0			BIT(6)
> >  #define PCF2127_BIT_WD_CTL_CD1			BIT(7)
> >  #define PCF2127_REG_WD_VAL		0x11
> > +#define PCF2131_REG_WD_VAL		0x36
> >  /* Tamper timestamp1 registers */
> >  #define PCF2127_REG_TS1_BASE		0x12
> > +#define PCF2131_REG_TS1_BASE		0x14
> > +/* Tamper timestamp2 registers */
> > +#define PCF2131_REG_TS2_BASE		0x1B
> > +/* Tamper timestamp3 registers */
> > +#define PCF2131_REG_TS3_BASE		0x22
> > +/* Tamper timestamp4 registers */
> > +#define PCF2131_REG_TS4_BASE		0x29
> >  /* Tamper timestamp registers common offsets (starting from base register) */
> >  #define PCF2127_OFFSET_TS_CTL		0
> >  #define PCF2127_OFFSET_TS_SC		1
> > @@ -92,11 +135,22 @@
> >   * RAM registers
> >   * PCF2127 has 512 bytes general-purpose static RAM (SRAM) that is
> >   * battery backed and can survive a power outage.
> > - * PCF2129 doesn't have this feature.
> > + * PCF2129/31 doesn't have this feature.
> >   */
> >  #define PCF2127_REG_RAM_ADDR_MSB	0x1A
> >  #define PCF2127_REG_RAM_WRT_CMD		0x1C
> >  #define PCF2127_REG_RAM_RD_CMD		0x1D
> > +/* Interrupt mask registers */
> > +#define PCF2131_REG_INT_A_MASK1		0x31
> > +#define PCF2131_REG_INT_A_MASK2		0x32
> > +#define PCF2131_REG_INT_B_MASK1		0x33
> > +#define PCF2131_REG_INT_B_MASK2		0x34
> > +#define PCF2131_BIT_INT_BLIE		BIT(0)
> > +#define PCF2131_BIT_INT_BIE		BIT(1)
> > +#define PCF2131_BIT_INT_AIE		BIT(2)
> > +#define PCF2131_BIT_INT_WD_CD		BIT(3)
> > +#define PCF2131_BIT_INT_SI		BIT(4)
> > +#define PCF2131_BIT_INT_MI		BIT(5)
> >  
> >  /* Watchdog timer value constants */
> >  #define PCF2127_WD_VAL_STOP		0
> > @@ -110,6 +164,14 @@
> >  		PCF2127_BIT_CTRL2_AF | \
> >  		PCF2127_BIT_CTRL2_WDTF | \
> >  		PCF2127_BIT_CTRL2_TSF2)
> > +#define PCF2131_CTRL2_IRQ_MASK ( \
> > +		PCF2127_BIT_CTRL2_AF | \
> > +		PCF2127_BIT_CTRL2_WDTF)
> > +#define PCF2131_CTRL4_IRQ_MASK ( \
> > +		PCF2131_BIT_CTRL4_TSF4 | \
> > +		PCF2131_BIT_CTRL4_TSF3 | \
> > +		PCF2131_BIT_CTRL4_TSF2 | \
> > +		PCF2131_BIT_CTRL4_TSF1)
> >  
> >  struct pcf21xx_ts_config {
> >  	u8 regs_base; /* Base register to read timestamp values. */
> > @@ -370,7 +432,7 @@ static int pcf2127_wdt_set_timeout(struct watchdog_device *wdd,
> >  }
> >  
> >  static const struct watchdog_info pcf2127_wdt_info = {
> > -	.identity = "NXP PCF2127/PCF2129 Watchdog",
> > +	.identity = "NXP PCF2127/29/31 Watchdog",
> 
> This change may break userspace tools as this is exposed to userspace

Ok, removed.


> Alexandre Belloni, co-owner and COO, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com

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

* Re: [PATCH v3 10/14] rtc: pcf2127: read and validate PCF2131 device signature
  2023-01-20 17:01   ` Alexandre Belloni
@ 2023-01-23 17:31     ` Hugo Villeneuve
  0 siblings, 0 replies; 72+ messages in thread
From: Hugo Villeneuve @ 2023-01-23 17:31 UTC (permalink / raw)
  To: Alexandre Belloni
  Cc: a.zummo, robh+dt, krzysztof.kozlowski+dt, linux-rtc, devicetree,
	linux-kernel, Hugo Villeneuve

On Fri, 20 Jan 2023 18:01:35 +0100
Alexandre Belloni <alexandre.belloni@bootlin.com> wrote:

> On 15/12/2022 10:02:11-0500, Hugo Villeneuve wrote:
> > From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > 
> > Make sure the device we are probing is really the device we are
> > interested in.
> > 
> > Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > ---
> >  drivers/rtc/rtc-pcf2127.c | 20 ++++++++++++++++++++
> >  1 file changed, 20 insertions(+)
> > 
> > diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> > index 241189ee4a05..e4b78b9c03f9 100644
> > --- a/drivers/rtc/rtc-pcf2127.c
> > +++ b/drivers/rtc/rtc-pcf2127.c
> > @@ -193,11 +193,13 @@ struct pcf21xx_config {
> >  	unsigned int has_nvmem:1;
> >  	unsigned int has_bit_wd_ctl_cd0:1;
> >  	unsigned int has_int_a_b:1; /* PCF2131 supports two interrupt outputs. */
> > +	unsigned int has_reset_reg:1; /* If variant has a reset register. */
> >  	u8 regs_td_base; /* Time/data base registers. */
> >  	u8 regs_alarm_base; /* Alarm function base registers. */
> >  	u8 reg_wd_ctl; /* Watchdog control register. */
> >  	u8 reg_wd_val; /* Watchdog value register. */
> >  	u8 reg_clkout; /* Clkout register. */
> > +	u8 reg_reset;  /* Reset register if available. */
> >  	unsigned int ts_count;
> >  	struct pcf21xx_ts_config ts[4];
> >  	struct attribute_group attribute_group;
> > @@ -882,6 +884,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
> >  		.has_nvmem = 1,
> >  		.has_bit_wd_ctl_cd0 = 1,
> >  		.has_int_a_b = 0,
> > +		.has_reset_reg = 0,
> >  		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
> >  		.regs_alarm_base = PCF2127_REG_ALARM_BASE,
> >  		.reg_wd_ctl = PCF2127_REG_WD_CTL,
> > @@ -906,6 +909,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
> >  		.has_nvmem = 0,
> >  		.has_bit_wd_ctl_cd0 = 0,
> >  		.has_int_a_b = 0,
> > +		.has_reset_reg = 0,
> >  		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
> >  		.regs_alarm_base = PCF2127_REG_ALARM_BASE,
> >  		.reg_wd_ctl = PCF2127_REG_WD_CTL,
> > @@ -930,11 +934,13 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
> >  		.has_nvmem = 0,
> >  		.has_bit_wd_ctl_cd0 = 0,
> >  		.has_int_a_b = 1,
> > +		.has_reset_reg = 1,
> >  		.regs_td_base = PCF2131_REG_TIME_DATE_BASE,
> >  		.regs_alarm_base = PCF2131_REG_ALARM_BASE,
> >  		.reg_wd_ctl = PCF2131_REG_WD_CTL,
> >  		.reg_wd_val = PCF2131_REG_WD_VAL,
> >  		.reg_clkout = PCF2131_REG_CLKOUT,
> > +		.reg_reset  = PCF2131_REG_SR_RESET,
> >  		.ts_count = 4,
> >  		.ts[0] = {
> >  			.regs_base = PCF2131_REG_TS1_BASE,
> > @@ -1075,6 +1081,20 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
> >  	clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, pcf2127->rtc->features);
> >  	clear_bit(RTC_FEATURE_ALARM, pcf2127->rtc->features);
> >  
> > +	/* Read device signature if available. */
> > +	if (pcf2127->cfg->has_reset_reg) {
> > +		ret = regmap_read(pcf2127->regmap, pcf2127->cfg->reg_reset, &val);
> > +		if (ret < 0) {
> > +			dev_err(dev, "reading RESET register failed\n");
> 
> This is too verbose, please cut down on the number of strings you are
> adding.

See comment below.

> 
> > +			return ret;
> > +		}
> > +
> > +		if (val != PCF2131_SR_RESET_READ_PATTERN) {
> > +			dev_err(dev, "invalid device signature: $%02X\n", (u8)val);
> 
> I'm also not convinced this is actually useful. This may have to be
> updated for the next rtc the driver will support and what if this
> contradicts what the device tree is claiming at this address?

I will drop that section.


> > +			return -ENODEV;
> > +		}
> > +	}
> > +
> >  	if (alarm_irq > 0) {
> >  		unsigned long flags;
> >  
> > -- 
> > 2.30.2
> > 
> 
> -- 
> Alexandre Belloni, co-owner and COO, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com
> 


-- 
Hugo Villeneuve <hugo@hugovil.com>

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

* Re: [PATCH v3 07/14] rtc: pcf2127: add support for PCF2131 RTC
  2023-01-07 18:15   ` Bruno Thomsen
@ 2023-01-23 19:06     ` Hugo Villeneuve
  0 siblings, 0 replies; 72+ messages in thread
From: Hugo Villeneuve @ 2023-01-23 19:06 UTC (permalink / raw)
  To: Bruno Thomsen
  Cc: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt,
	linux-rtc, devicetree, linux-kernel, Hugo Villeneuve

On Sat, 7 Jan 2023 19:15:00 +0100
Bruno Thomsen <bruno.thomsen@gmail.com> wrote:

> Den tor. 15. dec. 2022 kl. 16.20 skrev Hugo Villeneuve <hugo@hugovil.com>:
> >
> > From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> >
> > This RTC is very similar in functionality to the PCF2127/29.
> >
> > Basically it:
> >   -supports two new control registers at offsets 4 and 5
> >   -supports a new reset register (not implemented in this driver)
> >   -supports 4 tamper detection functions instead of 1
> >   -has no nvmem (like the PCF2129)
> >   -has two output interrupt pins
> >
> > Because of that, most of the register addresses are very different,
> > although they still follow the same layout. For example, the tamper
> > registers have a different base address, but the offsets are all the same.
> >
> > Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > ---
> >  drivers/rtc/Kconfig       |   4 +-
> >  drivers/rtc/rtc-pcf2127.c | 234 ++++++++++++++++++++++++++++++++++----
> >  2 files changed, 215 insertions(+), 23 deletions(-)
> >
> > diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> > index 2bb640d1521d..3d4043ce0057 100644
> > --- a/drivers/rtc/Kconfig
> > +++ b/drivers/rtc/Kconfig
> > @@ -910,9 +910,9 @@ config RTC_DRV_PCF2127
> >         select REGMAP_SPI if SPI_MASTER
> >         select WATCHDOG_CORE if WATCHDOG
> >         help
> > -         If you say yes here you get support for the NXP PCF2127/29 RTC
> > +         If you say yes here you get support for the NXP PCF2127/29/31 RTC
> >           chips with integrated quartz crystal for industrial applications.
> > -         Both chips also have watchdog timer and tamper switch detection
> > +         These chips also have watchdog timer and tamper switch detection
> >           features.
> >
> >           PCF2127 has an additional feature of 512 bytes battery backed
> > diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> > index 3265878edc48..4148e135f935 100644
> > --- a/drivers/rtc/rtc-pcf2127.c
> > +++ b/drivers/rtc/rtc-pcf2127.c
> > @@ -1,16 +1,26 @@
> >  // SPDX-License-Identifier: GPL-2.0-only
> >  /*
> > - * An I2C and SPI driver for the NXP PCF2127/29 RTC
> > + * An I2C and SPI driver for the NXP PCF2127/29/31 RTC
> >   * Copyright 2013 Til-Technologies
> > + * Copyright 2021 DimOnOff
> >   *
> >   * Author: Renaud Cerrato <r.cerrato@til-technologies.fr>
> >   *
> >   * Watchdog and tamper functions
> >   * Author: Bruno Thomsen <bruno.thomsen@gmail.com>
> >   *
> > + * PCF2131 support
> > + * Author: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > + *
> >   * based on the other drivers in this same directory.
> >   *
> > - * Datasheet: https://www.nxp.com/docs/en/data-sheet/PCF2127.pdf
> > + * Datasheets: https://www.nxp.com/docs/en/data-sheet/PCF2127.pdf
> > + *             https://www.nxp.com/docs/en/data-sheet/PCF2131DS.pdf
> > + */
> > +
> > +/*
> > + * The following features are not yet implemented for the PCF2131:
> > + *   - support for 1/100th seconds
> >   */
> >
> >  #include <linux/i2c.h>
> > @@ -43,8 +53,30 @@
> >  #define PCF2127_BIT_CTRL3_BLF                  BIT(2)
> >  #define PCF2127_BIT_CTRL3_BF                   BIT(3)
> >  #define PCF2127_BIT_CTRL3_BTSE                 BIT(4)
> > +/* Control register 4 */
> > +#define PCF2131_REG_CTRL4              0x03
> > +#define PCF2131_BIT_CTRL4_TSF4                 BIT(4)
> > +#define PCF2131_BIT_CTRL4_TSF3                 BIT(5)
> > +#define PCF2131_BIT_CTRL4_TSF2                 BIT(6)
> > +#define PCF2131_BIT_CTRL4_TSF1                 BIT(7)
> > +/* Control register 5 */
> > +#define PCF2131_REG_CTRL5              0x04
> > +#define PCF2131_BIT_CTRL5_TSIE4                        BIT(4)
> > +#define PCF2131_BIT_CTRL5_TSIE3                        BIT(5)
> > +#define PCF2131_BIT_CTRL5_TSIE2                        BIT(6)
> > +#define PCF2131_BIT_CTRL5_TSIE1                        BIT(7)
> > +/* Software reset register */
> > +#define PCF2131_REG_SR_RESET           0x05
> > +#define PCF2131_SR_RESET_READ_PATTERN  0b00100100 /* Fixed pattern. */
> 
> Replace 0b00100100 with (BIT(2) | BIT(5))

Removed this define as the reset signature is no longer verified.


> > +#define PCF2131_SR_RESET_RESET_CMD     0x2C /* SR is bit 3. */
> 
> Replace 0x2C with (BIT(2) | BIT(3) | BIT(5)) or
> (PCF2131_SR_RESET_READ_PATTERN | BIT(3))
> and then drop the comment.

Done.


> 
> >  /* Time and date registers */
> >  #define PCF2127_REG_TIME_DATE_BASE     0x03
> > +#define PCF2131_REG_TIME_DATE_BASE     0x07 /* Register 0x06 is 100th seconds,
> > +                                             * but we do not support it. By
> > +                                             * using offset 0x07, we can be
> > +                                             * compatible with existing
> > +                                             * time/date functions.
> > +                                             */
> 
> I think you should add this rather big comment above
> #define PCF2131_REG_TIME_DATE_BASE 0x07

Removed comment as suggested by Alexandre.

> 
> and rename the define to PCF2131_REG_TIME_BASE.

Done.


> 
> >  /* Time and date registers offsets (starting from base register) */
> >  #define PCF2127_OFFSET_TD_SC           0
> >  #define PCF2127_OFFSET_TD_MN           1
> > @@ -57,6 +89,7 @@
> >  #define PCF2127_BIT_SC_OSF                     BIT(7)
> >  /* Alarm registers */
> >  #define PCF2127_REG_ALARM_BASE         0x0A
> > +#define PCF2131_REG_ALARM_BASE         0x0E
> >  /* Alarm registers offsets (starting from base register) */
> >  #define PCF2127_OFFSET_ALARM_SC                0
> >  #define PCF2127_OFFSET_ALARM_MN                1
> > @@ -67,16 +100,26 @@
> >  #define PCF2127_BIT_ALARM_AE                   BIT(7)
> >  /* CLKOUT control register */
> >  #define PCF2127_REG_CLKOUT             0x0f
> > +#define PCF2131_REG_CLKOUT             0x13
> >  #define PCF2127_BIT_CLKOUT_OTPR                        BIT(5)
> >  /* Watchdog registers */
> >  #define PCF2127_REG_WD_CTL             0x10
> > +#define PCF2131_REG_WD_CTL             0x35
> >  #define PCF2127_BIT_WD_CTL_TF0                 BIT(0)
> >  #define PCF2127_BIT_WD_CTL_TF1                 BIT(1)
> >  #define PCF2127_BIT_WD_CTL_CD0                 BIT(6)
> >  #define PCF2127_BIT_WD_CTL_CD1                 BIT(7)
> >  #define PCF2127_REG_WD_VAL             0x11
> > +#define PCF2131_REG_WD_VAL             0x36
> >  /* Tamper timestamp1 registers */
> >  #define PCF2127_REG_TS1_BASE           0x12
> > +#define PCF2131_REG_TS1_BASE           0x14
> > +/* Tamper timestamp2 registers */
> > +#define PCF2131_REG_TS2_BASE           0x1B
> > +/* Tamper timestamp3 registers */
> > +#define PCF2131_REG_TS3_BASE           0x22
> > +/* Tamper timestamp4 registers */
> > +#define PCF2131_REG_TS4_BASE           0x29
> >  /* Tamper timestamp registers common offsets (starting from base register) */
> >  #define PCF2127_OFFSET_TS_CTL          0
> >  #define PCF2127_OFFSET_TS_SC           1
> > @@ -92,11 +135,22 @@
> >   * RAM registers
> >   * PCF2127 has 512 bytes general-purpose static RAM (SRAM) that is
> >   * battery backed and can survive a power outage.
> > - * PCF2129 doesn't have this feature.
> > + * PCF2129/31 doesn't have this feature.
> >   */
> >  #define PCF2127_REG_RAM_ADDR_MSB       0x1A
> >  #define PCF2127_REG_RAM_WRT_CMD                0x1C
> >  #define PCF2127_REG_RAM_RD_CMD         0x1D
> > +/* Interrupt mask registers */
> > +#define PCF2131_REG_INT_A_MASK1                0x31
> > +#define PCF2131_REG_INT_A_MASK2                0x32
> > +#define PCF2131_REG_INT_B_MASK1                0x33
> > +#define PCF2131_REG_INT_B_MASK2                0x34
> > +#define PCF2131_BIT_INT_BLIE           BIT(0)
> > +#define PCF2131_BIT_INT_BIE            BIT(1)
> > +#define PCF2131_BIT_INT_AIE            BIT(2)
> > +#define PCF2131_BIT_INT_WD_CD          BIT(3)
> > +#define PCF2131_BIT_INT_SI             BIT(4)
> > +#define PCF2131_BIT_INT_MI             BIT(5)
> >
> >  /* Watchdog timer value constants */
> >  #define PCF2127_WD_VAL_STOP            0
> > @@ -110,6 +164,14 @@
> >                 PCF2127_BIT_CTRL2_AF | \
> >                 PCF2127_BIT_CTRL2_WDTF | \
> >                 PCF2127_BIT_CTRL2_TSF2)
> > +#define PCF2131_CTRL2_IRQ_MASK ( \
> > +               PCF2127_BIT_CTRL2_AF | \
> > +               PCF2127_BIT_CTRL2_WDTF)
> > +#define PCF2131_CTRL4_IRQ_MASK ( \
> > +               PCF2131_BIT_CTRL4_TSF4 | \
> > +               PCF2131_BIT_CTRL4_TSF3 | \
> > +               PCF2131_BIT_CTRL4_TSF2 | \
> > +               PCF2131_BIT_CTRL4_TSF1)
> >
> >  struct pcf21xx_ts_config {
> >         u8 regs_base; /* Base register to read timestamp values. */
> > @@ -370,7 +432,7 @@ static int pcf2127_wdt_set_timeout(struct watchdog_device *wdd,
> >  }
> >
> >  static const struct watchdog_info pcf2127_wdt_info = {
> > -       .identity = "NXP PCF2127/PCF2129 Watchdog",
> > +       .identity = "NXP PCF2127/29/31 Watchdog",
> >         .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT,
> >  };
> >
> > @@ -555,30 +617,64 @@ static void pcf2127_rtc_ts_snapshot(struct device *dev, int ts_id)
> >  static irqreturn_t pcf2127_rtc_irq(int irq, void *dev)
> >  {
> >         struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
> > -       unsigned int ctrl1, ctrl2;
> > +       unsigned int ctrl2;
> >         int ret = 0;
> >
> > -       ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL1, &ctrl1);
> > -       if (ret)
> > -               return IRQ_NONE;
> > -
> >         ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL2, &ctrl2);
> >         if (ret)
> >                 return IRQ_NONE;
> >
> > -       if (!(ctrl1 & PCF2127_CTRL1_IRQ_MASK || ctrl2 & PCF2127_CTRL2_IRQ_MASK))
> > -               return IRQ_NONE;
> > +       if (pcf2127->cfg->ts_count == 1) {
> > +               /* PCF2127/29 */
> > +               unsigned int ctrl1;
> > +
> > +               ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL1, &ctrl1);
> > +               if (ret)
> > +                       return IRQ_NONE;
> > +
> > +               if (!(ctrl1 & PCF2127_CTRL1_IRQ_MASK || ctrl2 & PCF2127_CTRL2_IRQ_MASK))
> > +                       return IRQ_NONE;
> >
> > -       if (ctrl1 & PCF2127_BIT_CTRL1_TSF1 || ctrl2 & PCF2127_BIT_CTRL2_TSF2)
> > -               pcf2127_rtc_ts_snapshot(dev, 0);
> > +               if (ctrl1 & PCF2127_BIT_CTRL1_TSF1 || ctrl2 & PCF2127_BIT_CTRL2_TSF2)
> > +                       pcf2127_rtc_ts_snapshot(dev, 0);
> >
> > -       if (ctrl1 & PCF2127_CTRL1_IRQ_MASK)
> > -               regmap_write(pcf2127->regmap, PCF2127_REG_CTRL1,
> > -                       ctrl1 & ~PCF2127_CTRL1_IRQ_MASK);
> > +               if (ctrl1 & PCF2127_CTRL1_IRQ_MASK)
> > +                       regmap_write(pcf2127->regmap, PCF2127_REG_CTRL1,
> > +                                    ctrl1 & ~PCF2127_CTRL1_IRQ_MASK);
> > +
> > +               if (ctrl2 & PCF2127_CTRL2_IRQ_MASK)
> > +                       regmap_write(pcf2127->regmap, PCF2127_REG_CTRL2,
> > +                                    ctrl2 & ~PCF2127_CTRL2_IRQ_MASK);
> > +       } else {
> > +               /* PCF2131. */
> > +               unsigned int ctrl4;
> > +
> > +               ret = regmap_read(pcf2127->regmap, PCF2131_REG_CTRL4, &ctrl4);
> > +               if (ret)
> > +                       return IRQ_NONE;
> >
> > -       if (ctrl2 & PCF2127_CTRL2_IRQ_MASK)
> > -               regmap_write(pcf2127->regmap, PCF2127_REG_CTRL2,
> > -                       ctrl2 & ~PCF2127_CTRL2_IRQ_MASK);
> > +               if (!(ctrl4 & PCF2131_CTRL4_IRQ_MASK || ctrl2 & PCF2131_CTRL2_IRQ_MASK))
> > +                       return IRQ_NONE;
> > +
> > +               if (ctrl4 & PCF2131_CTRL4_IRQ_MASK) {
> > +                       int i;
> > +                       int tsf_bit = PCF2131_BIT_CTRL4_TSF1; /* Start at bit 7. */
> > +
> > +                       for (i = 0; i < pcf2127->cfg->ts_count; i++) {
> > +                               if (ctrl4 & tsf_bit)
> > +                                       pcf2127_rtc_ts_snapshot(dev, i);
> > +
> > +                               tsf_bit = tsf_bit >> 1;
> > +                       }
> > +
> > +                       regmap_write(pcf2127->regmap, PCF2131_REG_CTRL4,
> > +                                    ctrl4 & ~PCF2131_CTRL4_IRQ_MASK);
> > +               }
> > +
> > +               if (ctrl2 & PCF2131_CTRL2_IRQ_MASK)
> > +                       regmap_write(pcf2127->regmap, PCF2127_REG_CTRL2,
> > +                                    ctrl2 & ~PCF2131_CTRL2_IRQ_MASK);
> > +       }
> >
> >         if (ctrl2 & PCF2127_BIT_CTRL2_AF)
> >                 rtc_update_irq(pcf2127->rtc, 1, RTC_IRQF | RTC_AF);
> > @@ -650,6 +746,27 @@ static ssize_t timestamp0_store(struct device *dev,
> >         return timestamp_store(dev, attr, buf, count, 0);
> >  };
> >
> > +static ssize_t timestamp1_store(struct device *dev,
> > +                               struct device_attribute *attr,
> > +                               const char *buf, size_t count)
> > +{
> > +       return timestamp_store(dev, attr, buf, count, 1);
> > +};
> > +
> > +static ssize_t timestamp2_store(struct device *dev,
> > +                               struct device_attribute *attr,
> > +                               const char *buf, size_t count)
> > +{
> > +       return timestamp_store(dev, attr, buf, count, 2);
> > +};
> > +
> > +static ssize_t timestamp3_store(struct device *dev,
> > +                               struct device_attribute *attr,
> > +                               const char *buf, size_t count)
> > +{
> > +       return timestamp_store(dev, attr, buf, count, 3);
> > +};
> > +
> >  static ssize_t timestamp_show(struct device *dev,
> >                               struct device_attribute *attr, char *buf,
> >                               int ts_id)
> > @@ -714,16 +831,46 @@ static ssize_t timestamp0_show(struct device *dev,
> >         return timestamp_show(dev, attr, buf, 0);
> >  };
> >
> > +static ssize_t timestamp1_show(struct device *dev,
> > +                              struct device_attribute *attr, char *buf)
> > +{
> > +       return timestamp_show(dev, attr, buf, 1);
> > +};
> > +
> > +static ssize_t timestamp2_show(struct device *dev,
> > +                              struct device_attribute *attr, char *buf)
> > +{
> > +       return timestamp_show(dev, attr, buf, 2);
> > +};
> > +
> > +static ssize_t timestamp3_show(struct device *dev,
> > +                              struct device_attribute *attr, char *buf)
> > +{
> > +       return timestamp_show(dev, attr, buf, 3);
> > +};
> > +
> >  static DEVICE_ATTR_RW(timestamp0);
> > +static DEVICE_ATTR_RW(timestamp1);
> > +static DEVICE_ATTR_RW(timestamp2);
> > +static DEVICE_ATTR_RW(timestamp3);
> >
> >  static struct attribute *pcf2127_attrs[] = {
> >         &dev_attr_timestamp0.attr,
> >         NULL
> >  };
> >
> > +static struct attribute *pcf2131_attrs[] = {
> > +       &dev_attr_timestamp0.attr,
> > +       &dev_attr_timestamp1.attr,
> > +       &dev_attr_timestamp2.attr,
> > +       &dev_attr_timestamp3.attr,
> > +       NULL
> > +};
> > +
> >  enum pcf21xx_type {
> >         PCF2127,
> >         PCF2129,
> > +       PCF2131,
> >         PCF21XX_LAST_ID
> >  };
> >
> > @@ -774,6 +921,48 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
> >                         .attrs  = pcf2127_attrs,
> >                 },
> >         },
> > +       [PCF2131] = {
> > +               .max_register = 0x36,
> > +               .has_nvmem = 0,
> > +               .has_bit_wd_ctl_cd0 = 0,
> > +               .regs_td_base = PCF2131_REG_TIME_DATE_BASE,
> > +               .regs_alarm_base = PCF2131_REG_ALARM_BASE,
> > +               .reg_wd_ctl = PCF2131_REG_WD_CTL,
> > +               .reg_wd_val = PCF2131_REG_WD_VAL,
> > +               .reg_clkout = PCF2131_REG_CLKOUT,
> > +               .ts_count = 4,
> > +               .ts[0] = {
> > +                       .regs_base = PCF2131_REG_TS1_BASE,
> > +                       .low_reg   = PCF2131_REG_CTRL4,
> > +                       .low_bit   = PCF2131_BIT_CTRL4_TSF1,
> 
> Set .inter_bit to 0 as it's used/read in timestamp_store() to
> control flow in if-condition.

Done for all 4 ts[] structures.


> 
> > +                       .ie_reg    = PCF2131_REG_CTRL5,
> > +                       .ie_bit    = PCF2131_BIT_CTRL5_TSIE1,
> > +               },
> > +               .ts[1] = {
> > +                       .regs_base = PCF2131_REG_TS2_BASE,
> > +                       .low_reg   = PCF2131_REG_CTRL4,
> > +                       .low_bit   = PCF2131_BIT_CTRL4_TSF2,
> > +                       .ie_reg    = PCF2131_REG_CTRL5,
> > +                       .ie_bit    = PCF2131_BIT_CTRL5_TSIE2,
> > +               },
> > +               .ts[2] = {
> > +                       .regs_base = PCF2131_REG_TS3_BASE,
> > +                       .low_reg   = PCF2131_REG_CTRL4,
> > +                       .low_bit   = PCF2131_BIT_CTRL4_TSF3,
> > +                       .ie_reg    = PCF2131_REG_CTRL5,
> > +                       .ie_bit    = PCF2131_BIT_CTRL5_TSIE3,
> > +               },
> > +               .ts[3] = {
> > +                       .regs_base = PCF2131_REG_TS4_BASE,
> > +                       .low_reg   = PCF2131_REG_CTRL4,
> > +                       .low_bit   = PCF2131_BIT_CTRL4_TSF4,
> > +                       .ie_reg    = PCF2131_REG_CTRL5,
> > +                       .ie_bit    = PCF2131_BIT_CTRL5_TSIE4,
> > +               },
> > +               .attribute_group = {
> > +                       .attrs  = pcf2131_attrs,
> > +               },
> > +       },
> >  };
> >
> >  /*
> > @@ -922,7 +1111,7 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
> >          * Watchdog timer enabled and reset pin /RST activated when timed out.
> >          * Select 1Hz clock source for watchdog timer.
> >          * Note: Countdown timer disabled and not available.
> > -        * For pca2129, pcf2129, only bit[7] is for Symbol WD_CD
> > +        * For pca2129, pcf2129 and pcf2131, only bit[7] is for Symbol WD_CD
> >          * of register watchdg_tim_ctl. The bit[6] is labeled
> >          * as T. Bits labeled as T must always be written with
> >          * logic 0.
> > @@ -982,6 +1171,7 @@ static const struct of_device_id pcf2127_of_match[] = {
> >         { .compatible = "nxp,pcf2127", .data = &pcf21xx_cfg[PCF2127] },
> >         { .compatible = "nxp,pcf2129", .data = &pcf21xx_cfg[PCF2129] },
> >         { .compatible = "nxp,pca2129", .data = &pcf21xx_cfg[PCF2129] },
> > +       { .compatible = "nxp,pcf2131", .data = &pcf21xx_cfg[PCF2131] },
> >         {}
> >  };
> >  MODULE_DEVICE_TABLE(of, pcf2127_of_match);
> > @@ -1069,6 +1259,7 @@ static const struct i2c_device_id pcf2127_i2c_id[] = {
> >         { "pcf2127", PCF2127 },
> >         { "pcf2129", PCF2129 },
> >         { "pca2129", PCF2129 },
> > +       { "pcf2131", PCF2131 },
> >         { }
> >  };
> >  MODULE_DEVICE_TABLE(i2c, pcf2127_i2c_id);
> > @@ -1190,6 +1381,7 @@ static const struct spi_device_id pcf2127_spi_id[] = {
> >         { "pcf2127", PCF2127 },
> >         { "pcf2129", PCF2129 },
> >         { "pca2129", PCF2129 },
> > +       { "pcf2131", PCF2131 },
> >         { }
> >  };
> >  MODULE_DEVICE_TABLE(spi, pcf2127_spi_id);
> > @@ -1254,5 +1446,5 @@ static void __exit pcf2127_exit(void)
> >  module_exit(pcf2127_exit)
> >
> >  MODULE_AUTHOR("Renaud Cerrato <r.cerrato@til-technologies.fr>");
> > -MODULE_DESCRIPTION("NXP PCF2127/29 RTC driver");
> > +MODULE_DESCRIPTION("NXP PCF2127/29/31 RTC driver");
> >  MODULE_LICENSE("GPL v2");
> > --
> > 2.30.2
> >
> 


-- 
Hugo Villeneuve <hugo@hugovil.com>

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

* Re: [PATCH v3 06/14] rtc: pcf2127: add support for multiple TS functions
  2023-01-07 17:58   ` Bruno Thomsen
@ 2023-01-23 20:41     ` Hugo Villeneuve
  0 siblings, 0 replies; 72+ messages in thread
From: Hugo Villeneuve @ 2023-01-23 20:41 UTC (permalink / raw)
  To: Bruno Thomsen
  Cc: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt,
	linux-rtc, devicetree, linux-kernel, Hugo Villeneuve

On Sat, 7 Jan 2023 18:58:25 +0100
Bruno Thomsen <bruno.thomsen@gmail.com> wrote:

> Den tor. 15. dec. 2022 kl. 16.48 skrev Hugo Villeneuve <hugo@hugovil.com>:
> >
> > From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> >
> > This will simplify the implementation of new variants into this driver.
> >
> > Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > ---
> >  drivers/rtc/rtc-pcf2127.c | 303 +++++++++++++++++++++++++++-----------
> >  1 file changed, 215 insertions(+), 88 deletions(-)
> >
> > diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> > index 38816ad065eb..3265878edc48 100644
> > --- a/drivers/rtc/rtc-pcf2127.c
> > +++ b/drivers/rtc/rtc-pcf2127.c
> > @@ -75,16 +75,19 @@
> >  #define PCF2127_BIT_WD_CTL_CD0                 BIT(6)
> >  #define PCF2127_BIT_WD_CTL_CD1                 BIT(7)
> >  #define PCF2127_REG_WD_VAL             0x11
> > -/* Tamper timestamp registers */
> > -#define PCF2127_REG_TS_CTRL            0x12
> > +/* Tamper timestamp1 registers */
> > +#define PCF2127_REG_TS1_BASE           0x12
> > +/* Tamper timestamp registers common offsets (starting from base register) */
> > +#define PCF2127_OFFSET_TS_CTL          0
> > +#define PCF2127_OFFSET_TS_SC           1
> > +#define PCF2127_OFFSET_TS_MN           2
> > +#define PCF2127_OFFSET_TS_HR           3
> > +#define PCF2127_OFFSET_TS_DM           4
> > +#define PCF2127_OFFSET_TS_MO           5
> > +#define PCF2127_OFFSET_TS_YR           6
> > +/* Tamper timestamp registers common bits */
> >  #define PCF2127_BIT_TS_CTRL_TSOFF              BIT(6)
> >  #define PCF2127_BIT_TS_CTRL_TSM                        BIT(7)
> > -#define PCF2127_REG_TS_SC              0x13
> > -#define PCF2127_REG_TS_MN              0x14
> > -#define PCF2127_REG_TS_HR              0x15
> > -#define PCF2127_REG_TS_DM              0x16
> > -#define PCF2127_REG_TS_MO              0x17
> > -#define PCF2127_REG_TS_YR              0x18
> >  /*
> >   * RAM registers
> >   * PCF2127 has 512 bytes general-purpose static RAM (SRAM) that is
> > @@ -108,6 +111,20 @@
> >                 PCF2127_BIT_CTRL2_WDTF | \
> >                 PCF2127_BIT_CTRL2_TSF2)
> >
> > +struct pcf21xx_ts_config {
> > +       u8 regs_base; /* Base register to read timestamp values. */
> 
> reg_base as there is only one.

Done.


> 
> > +       /* TS input pin driven to GND detection (supported by all variants): */
> > +       u8 low_reg; /* Interrupt control register. */
> > +       u8 low_bit; /* Interrupt flag in low_reg control register. */
> > +       /* TS input pin driven to intermediate level between GND and supply
> > +        * detection (optional feature depending on variant):
> > +        */
> > +       u8 inter_reg; /* Interrupt control register. */
> > +       u8 inter_bit; /* Interrupt flag in inter_reg control register. */
> 
> I had to read this a couple of times to understand it :)
> 
> Maybe rename variables to low_ctl_reg, low_ctl_bit,
> inter_ctl_reg and inter_ctl_bit.

I modified the comments and renamed the variables so that I hope it is clearer:

+struct pcf21xx_ts_config {
+	u8 reg_base; /* Base register to read timestamp values. */
+
+	/*
+	 * If the TS input pin is driven to GND, an interrupt can be generated
+	 * (supported by all variants).
+	 */
+	u8 gnd_detect_reg; /* Interrupt control register address. */
+	u8 gnd_detect_bit; /* Interrupt bit. */
+
+	/*
+	 * If the TS input pin is driven to an intermediate level between GND
+	 * and supply, an interrupt can be generated (optional feature depending
+	 * on variant).
+	 */
+	u8 inter_detect_reg; /* Interrupt control register address. */
+	u8 inter_detect_bit; /* Interrupt bit. */
+
+	u8 ie_reg; /* Interrupt enable control register. */
+	u8 ie_bit; /* Interrupt enable bit. */
+};
+



> > +       u8 ie_reg; /* Interrupt enable control register. */
> > +       u8 ie_bit; /* Interrupt enable bit. */
> > +};
> > +
> >  struct pcf21xx_config {
> >         int max_register;
> >         unsigned int has_nvmem:1;
> > @@ -117,6 +134,9 @@ struct pcf21xx_config {
> >         u8 reg_wd_ctl; /* Watchdog control register. */
> >         u8 reg_wd_val; /* Watchdog value register. */
> >         u8 reg_clkout; /* Clkout register. */
> > +       unsigned int ts_count;
> > +       struct pcf21xx_ts_config ts[4];
> 
> You should create a driver constant with the maximum number
> of supported timestamps, something like:
> 
> #define PCF2127_MAX_TS_SUPPORTED  1
> 
> and raise it to 4 when pcf2131 support is added.

Done.


> 
> > +       struct attribute_group attribute_group;
> >  };
> >
> >  struct pcf2127 {
> > @@ -124,9 +144,9 @@ struct pcf2127 {
> >         struct watchdog_device wdd;
> >         struct regmap *regmap;
> >         const struct pcf21xx_config *cfg;
> > -       time64_t ts;
> > -       bool ts_valid;
> >         bool irq_enabled;
> > +       time64_t ts[4]; /* Timestamp values. */
> > +       bool ts_valid[4];  /* Timestamp valid indication. */
> >  };
> >
> >  /*
> > @@ -469,38 +489,39 @@ static int pcf2127_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
> >  }
> >
> >  /*
> > - * This function reads ctrl2 register, caller is responsible for calling
> > - * pcf2127_wdt_active_ping()
> > + * This function reads one timestamp function data, caller is responsible for
> > + * calling pcf2127_wdt_active_ping()
> >   */
> > -static int pcf2127_rtc_ts_read(struct device *dev, time64_t *ts)
> > +static int pcf2127_rtc_ts_read(struct device *dev, time64_t *ts,
> > +                              int ts_id)
> >  {
> >         struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
> >         struct rtc_time tm;
> >         int ret;
> > -       unsigned char data[25];
> > +       unsigned char data[7]; /* To store result of reading 7 consecutive
> > +                               * timestamp registers.
> > +                               */
> 
> Remove comment about data variable.

Done.


> 
> >
> > -       ret = regmap_bulk_read(pcf2127->regmap, PCF2127_REG_CTRL1, data,
> > -                              sizeof(data));
> > +       ret = regmap_bulk_read(pcf2127->regmap, pcf2127->cfg->ts[ts_id].regs_base,
> > +                              data, sizeof(data));
> >         if (ret) {
> > -               dev_err(dev, "%s: read error ret=%d\n", __func__, ret);
> > +               dev_err(dev, "%s: bulk read error ret=%d\n", __func__, ret);
> >                 return ret;
> >         }
> >
> >         dev_dbg(dev,
> > -               "%s: raw data is cr1=%02x, cr2=%02x, cr3=%02x, ts_sc=%02x, ts_mn=%02x, ts_hr=%02x, ts_dm=%02x, ts_mo=%02x, ts_yr=%02x\n",
> > -               __func__, data[PCF2127_REG_CTRL1], data[PCF2127_REG_CTRL2],
> > -               data[PCF2127_REG_CTRL3], data[PCF2127_REG_TS_SC],
> > -               data[PCF2127_REG_TS_MN], data[PCF2127_REG_TS_HR],
> > -               data[PCF2127_REG_TS_DM], data[PCF2127_REG_TS_MO],
> > -               data[PCF2127_REG_TS_YR]);
> > -
> > -       tm.tm_sec = bcd2bin(data[PCF2127_REG_TS_SC] & 0x7F);
> > -       tm.tm_min = bcd2bin(data[PCF2127_REG_TS_MN] & 0x7F);
> > -       tm.tm_hour = bcd2bin(data[PCF2127_REG_TS_HR] & 0x3F);
> > -       tm.tm_mday = bcd2bin(data[PCF2127_REG_TS_DM] & 0x3F);
> > +               "%s: raw data is ts_sc=%02x, ts_mn=%02x, ts_hr=%02x, ts_dm=%02x, ts_mo=%02x, ts_yr=%02x\n",
> > +               __func__, data[PCF2127_OFFSET_TS_SC], data[PCF2127_OFFSET_TS_MN],
> > +               data[PCF2127_OFFSET_TS_HR], data[PCF2127_OFFSET_TS_DM],
> > +               data[PCF2127_OFFSET_TS_MO], data[PCF2127_OFFSET_TS_YR]);
> > +
> > +       tm.tm_sec = bcd2bin(data[PCF2127_OFFSET_TS_SC] & 0x7F);
> > +       tm.tm_min = bcd2bin(data[PCF2127_OFFSET_TS_MN] & 0x7F);
> > +       tm.tm_hour = bcd2bin(data[PCF2127_OFFSET_TS_HR] & 0x3F);
> > +       tm.tm_mday = bcd2bin(data[PCF2127_OFFSET_TS_DM] & 0x3F);
> >         /* TS_MO register (month) value range: 1-12 */
> > -       tm.tm_mon = bcd2bin(data[PCF2127_REG_TS_MO] & 0x1F) - 1;
> > -       tm.tm_year = bcd2bin(data[PCF2127_REG_TS_YR]);
> > +       tm.tm_mon = bcd2bin(data[PCF2127_OFFSET_TS_MO] & 0x1F) - 1;
> > +       tm.tm_year = bcd2bin(data[PCF2127_OFFSET_TS_YR]);
> >         if (tm.tm_year < 70)
> >                 tm.tm_year += 100; /* assume we are in 1970...2069 */
> >
> > @@ -514,18 +535,21 @@ static int pcf2127_rtc_ts_read(struct device *dev, time64_t *ts)
> >         return 0;
> >  };
> >
> > -static void pcf2127_rtc_ts_snapshot(struct device *dev)
> > +static void pcf2127_rtc_ts_snapshot(struct device *dev, int ts_id)
> >  {
> >         struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
> >         int ret;
> >
> > +       if (ts_id >= pcf2127->cfg->ts_count)
> > +               return;
> > +
> >         /* Let userspace read the first timestamp */
> > -       if (pcf2127->ts_valid)
> > +       if (pcf2127->ts_valid[ts_id])
> >                 return;
> >
> > -       ret = pcf2127_rtc_ts_read(dev, &pcf2127->ts);
> > +       ret = pcf2127_rtc_ts_read(dev, &pcf2127->ts[ts_id], ts_id);
> >         if (!ret)
> > -               pcf2127->ts_valid = true;
> > +               pcf2127->ts_valid[ts_id] = true;
> >  }
> >
> >  static irqreturn_t pcf2127_rtc_irq(int irq, void *dev)
> > @@ -546,7 +570,7 @@ static irqreturn_t pcf2127_rtc_irq(int irq, void *dev)
> >                 return IRQ_NONE;
> >
> >         if (ctrl1 & PCF2127_BIT_CTRL1_TSF1 || ctrl2 & PCF2127_BIT_CTRL2_TSF2)
> > -               pcf2127_rtc_ts_snapshot(dev);
> > +               pcf2127_rtc_ts_snapshot(dev, 0);
> >
> >         if (ctrl1 & PCF2127_CTRL1_IRQ_MASK)
> >                 regmap_write(pcf2127->regmap, PCF2127_REG_CTRL1,
> > @@ -575,28 +599,40 @@ static const struct rtc_class_ops pcf2127_rtc_ops = {
> >
> >  /* sysfs interface */
> >
> > -static ssize_t timestamp0_store(struct device *dev,
> > -                               struct device_attribute *attr,
> > -                               const char *buf, size_t count)
> > +static ssize_t timestamp_store(struct device *dev,
> > +                              struct device_attribute *attr,
> > +                              const char *buf, size_t count, int ts_id)
> >  {
> >         struct pcf2127 *pcf2127 = dev_get_drvdata(dev->parent);
> >         int ret;
> >
> > +       if (ts_id >= pcf2127->cfg->ts_count)
> > +               return 0;
> > +
> >         if (pcf2127->irq_enabled) {
> > -               pcf2127->ts_valid = false;
> > +               pcf2127->ts_valid[ts_id] = false;
> >         } else {
> > -               ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL1,
> > -                       PCF2127_BIT_CTRL1_TSF1, 0);
> > +               /* Always clear LOW interrupt bit. */
> > +               ret = regmap_update_bits(pcf2127->regmap,
> > +                                        pcf2127->cfg->ts[ts_id].low_reg,
> > +                                        pcf2127->cfg->ts[ts_id].low_bit,
> > +                                        0);
> > +
> >                 if (ret) {
> > -                       dev_err(dev, "%s: update ctrl1 ret=%d\n", __func__, ret);
> > +                       dev_err(dev, "%s: update TS low ret=%d\n", __func__, ret);
> >                         return ret;
> >                 }
> >
> > -               ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL2,
> > -                       PCF2127_BIT_CTRL2_TSF2, 0);
> > -               if (ret) {
> > -                       dev_err(dev, "%s: update ctrl2 ret=%d\n", __func__, ret);
> > -                       return ret;
> > +               if (pcf2127->cfg->ts[ts_id].inter_bit) {
> > +                       /* Clear INTERMEDIATE interrupt bit if supported. */
> > +                       ret = regmap_update_bits(pcf2127->regmap,
> > +                                                pcf2127->cfg->ts[ts_id].inter_reg,
> > +                                                pcf2127->cfg->ts[ts_id].inter_bit,
> > +                                                0);
> > +                       if (ret) {
> > +                               dev_err(dev, "%s: update TS intermediate ret=%d\n", __func__, ret);
> > +                               return ret;
> > +                       }
> >                 }
> >
> >                 ret = pcf2127_wdt_active_ping(&pcf2127->wdd);
> > @@ -605,34 +641,63 @@ static ssize_t timestamp0_store(struct device *dev,
> >         }
> >
> >         return count;
> > +}
> > +
> > +static ssize_t timestamp0_store(struct device *dev,
> > +                               struct device_attribute *attr,
> > +                               const char *buf, size_t count)
> > +{
> > +       return timestamp_store(dev, attr, buf, count, 0);
> >  };
> >
> > -static ssize_t timestamp0_show(struct device *dev,
> > -                              struct device_attribute *attr, char *buf)
> > +static ssize_t timestamp_show(struct device *dev,
> > +                             struct device_attribute *attr, char *buf,
> > +                             int ts_id)
> >  {
> >         struct pcf2127 *pcf2127 = dev_get_drvdata(dev->parent);
> > -       unsigned int ctrl1, ctrl2;
> >         int ret;
> >         time64_t ts;
> >
> > +       if (ts_id >= pcf2127->cfg->ts_count)
> > +               return 0;
> > +
> >         if (pcf2127->irq_enabled) {
> > -               if (!pcf2127->ts_valid)
> > +               if (!pcf2127->ts_valid[ts_id])
> >                         return 0;
> > -               ts = pcf2127->ts;
> > +               ts = pcf2127->ts[ts_id];
> >         } else {
> > -               ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL1, &ctrl1);
> > -               if (ret)
> > -                       return 0;
> > +               u8 valid_low = 0;
> > +               u8 valid_inter = 0;
> > +               unsigned int ctrl;
> >
> > -               ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL2, &ctrl2);
> > +               /* Check if TS input pin is driven to GND, supported by all
> > +                * variants.
> > +                */
> > +               ret = regmap_read(pcf2127->regmap,
> > +                                 pcf2127->cfg->ts[ts_id].low_reg,
> > +                                 &ctrl);
> >                 if (ret)
> >                         return 0;
> >
> > -               if (!(ctrl1 & PCF2127_BIT_CTRL1_TSF1) &&
> > -                   !(ctrl2 & PCF2127_BIT_CTRL2_TSF2))
> > +               valid_low = ctrl & pcf2127->cfg->ts[ts_id].low_bit;
> > +
> > +               if (pcf2127->cfg->ts[ts_id].inter_bit) {
> > +                       /* Check if TS input pin is driven to intermediate level
> > +                        * between GND and supply, if supported by variant.
> > +                        */
> > +                       ret = regmap_read(pcf2127->regmap,
> > +                                         pcf2127->cfg->ts[ts_id].inter_reg,
> > +                                         &ctrl);
> > +                       if (ret)
> > +                               return 0;
> > +
> > +                       valid_inter = ctrl & pcf2127->cfg->ts[ts_id].inter_bit;
> > +               }
> > +
> > +               if (!valid_low && !valid_inter)
> >                         return 0;
> >
> > -               ret = pcf2127_rtc_ts_read(dev->parent, &ts);
> > +               ret = pcf2127_rtc_ts_read(dev->parent, &ts, ts_id);
> >                 if (ret)
> >                         return 0;
> >
> > @@ -641,6 +706,12 @@ static ssize_t timestamp0_show(struct device *dev,
> >                         return ret;
> >         }
> >         return sprintf(buf, "%llu\n", (unsigned long long)ts);
> > +}
> > +
> > +static ssize_t timestamp0_show(struct device *dev,
> > +                              struct device_attribute *attr, char *buf)
> > +{
> > +       return timestamp_show(dev, attr, buf, 0);
> >  };
> >
> >  static DEVICE_ATTR_RW(timestamp0);
> > @@ -650,10 +721,6 @@ static struct attribute *pcf2127_attrs[] = {
> >         NULL
> >  };
> >
> > -static const struct attribute_group pcf2127_attr_group = {
> > -       .attrs  = pcf2127_attrs,
> > -};
> > -
> >  enum pcf21xx_type {
> >         PCF2127,
> >         PCF2129,
> > @@ -670,6 +737,19 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
> >                 .reg_wd_ctl = PCF2127_REG_WD_CTL,
> >                 .reg_wd_val = PCF2127_REG_WD_VAL,
> >                 .reg_clkout = PCF2127_REG_CLKOUT,
> > +               .ts_count = 1,
> > +               .ts[0] = {
> > +                       .regs_base = PCF2127_REG_TS1_BASE,
> > +                       .low_reg   = PCF2127_REG_CTRL1,
> > +                       .low_bit   = PCF2127_BIT_CTRL1_TSF1,
> > +                       .inter_reg = PCF2127_REG_CTRL2,
> > +                       .inter_bit = PCF2127_BIT_CTRL2_TSF2,
> > +                       .ie_reg    = PCF2127_REG_CTRL2,
> > +                       .ie_bit    = PCF2127_BIT_CTRL2_TSIE,
> > +               },
> > +               .attribute_group = {
> > +                       .attrs  = pcf2127_attrs,
> > +               },
> >         },
> >         [PCF2129] = {
> >                 .max_register = 0x19,
> > @@ -680,15 +760,81 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
> >                 .reg_wd_ctl = PCF2127_REG_WD_CTL,
> >                 .reg_wd_val = PCF2127_REG_WD_VAL,
> >                 .reg_clkout = PCF2127_REG_CLKOUT,
> > +               .ts_count = 1,
> > +               .ts[0] = {
> > +                       .regs_base = PCF2127_REG_TS1_BASE,
> > +                       .low_reg   = PCF2127_REG_CTRL1,
> > +                       .low_bit   = PCF2127_BIT_CTRL1_TSF1,
> > +                       .inter_reg = PCF2127_REG_CTRL2,
> > +                       .inter_bit = PCF2127_BIT_CTRL2_TSF2,
> > +                       .ie_reg    = PCF2127_REG_CTRL2,
> > +                       .ie_bit    = PCF2127_BIT_CTRL2_TSIE,
> > +               },
> > +               .attribute_group = {
> > +                       .attrs  = pcf2127_attrs,
> > +               },
> >         },
> >  };
> >
> > +/*
> > + * Enable timestamp function and corresponding interrupt(s).
> > + */
> > +static int pcf2127_enable_ts(struct device *dev, int ts_id)
> > +{
> > +       struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
> > +       int ret;
> > +
> > +       if (ts_id >= pcf2127->cfg->ts_count) {
> > +               dev_err(dev, "%s: invalid tamper detection ID (%d)\n",
> > +                       __func__, ts_id);
> > +               return -EINVAL;
> > +       }
> > +
> > +       /* Enable timestamp function. */
> > +       ret = regmap_update_bits(pcf2127->regmap,
> > +                                pcf2127->cfg->ts[ts_id].regs_base,
> > +                                PCF2127_BIT_TS_CTRL_TSOFF |
> > +                                PCF2127_BIT_TS_CTRL_TSM,
> > +                                PCF2127_BIT_TS_CTRL_TSM);
> > +       if (ret) {
> > +               dev_err(dev, "%s: tamper detection config (ts%d_ctrl) failed\n",
> > +                       __func__, ts_id);
> > +               return ret;
> > +       }
> > +
> > +       /* TS input pin driven to GND detection is supported by all variants.
> > +        * Make sure that low_bit is defined.
> > +        */
> > +       if (pcf2127->cfg->ts[ts_id].low_bit == 0) {
> > +               dev_err(dev, "%s: tamper detection LOW configuration invalid\n",
> > +                       __func__);
> > +               return ret;
> > +       }
> > +
> > +       /*
> > +        * Enable interrupt generation when TSF timestamp flag is set.
> > +        * Interrupt signals are open-drain outputs and can be left floating if
> > +        * unused.
> > +        */
> > +       ret = regmap_update_bits(pcf2127->regmap, pcf2127->cfg->ts[ts_id].ie_reg,
> > +                                pcf2127->cfg->ts[ts_id].ie_bit,
> > +                                pcf2127->cfg->ts[ts_id].ie_bit);
> > +       if (ret) {
> > +               dev_err(dev, "%s: tamper detection TSIE%d config failed\n",
> > +                       __func__, ts_id);
> > +               return ret;
> > +       }
> > +
> > +       return ret;
> > +}
> > +
> >  static int pcf2127_probe(struct device *dev, struct regmap *regmap,
> >                          int alarm_irq, const char *name, const struct pcf21xx_config *config)
> >  {
> >         struct pcf2127 *pcf2127;
> >         int ret = 0;
> >         unsigned int val;
> > +       int i;
> >
> >         dev_dbg(dev, "%s\n", __func__);
> >
> > @@ -813,34 +959,15 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
> >         }
> >
> >         /*
> > -        * Enable timestamp function and store timestamp of first trigger
> > -        * event until TSF1 and TSF2 interrupt flags are cleared.
> > +        * Enable timestamp functions 1 to 4.
> >          */
> > -       ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_TS_CTRL,
> > -                                PCF2127_BIT_TS_CTRL_TSOFF |
> > -                                PCF2127_BIT_TS_CTRL_TSM,
> > -                                PCF2127_BIT_TS_CTRL_TSM);
> > -       if (ret) {
> > -               dev_err(dev, "%s: tamper detection config (ts_ctrl) failed\n",
> > -                       __func__);
> > -               return ret;
> > -       }
> > -
> > -       /*
> > -        * Enable interrupt generation when TSF1 or TSF2 timestamp flags
> > -        * are set. Interrupt signal is an open-drain output and can be
> > -        * left floating if unused.
> > -        */
> > -       ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL2,
> > -                                PCF2127_BIT_CTRL2_TSIE,
> > -                                PCF2127_BIT_CTRL2_TSIE);
> > -       if (ret) {
> > -               dev_err(dev, "%s: tamper detection config (ctrl2) failed\n",
> > -                       __func__);
> > -               return ret;
> > +       for (i = 0; i < pcf2127->cfg->ts_count; i++) {
> 
> Just define the loop counter variable inline.
> 
> for (int i = 0; i < pcf2127->cfg->ts_count; i++) {

Done.


> 
> > +               ret = pcf2127_enable_ts(dev, i);
> > +               if (ret)
> > +                       return ret;
> >         }
> >
> > -       ret = rtc_add_group(pcf2127->rtc, &pcf2127_attr_group);
> > +       ret = rtc_add_group(pcf2127->rtc, &pcf2127->cfg->attribute_group);
> >         if (ret) {
> >                 dev_err(dev, "%s: tamper sysfs registering failed\n",
> >                         __func__);
> > --
> > 2.30.2
> >
> 


-- 
Hugo Villeneuve <hugo@hugovil.com>

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

* Re: [PATCH v3 08/14] rtc: pcf2127: add support for PCF2131 interrupts on output INT_A
  2023-01-20 16:56   ` Alexandre Belloni
@ 2023-01-23 20:52     ` Hugo Villeneuve
  2023-05-11 17:19       ` Hugo Villeneuve
  0 siblings, 1 reply; 72+ messages in thread
From: Hugo Villeneuve @ 2023-01-23 20:52 UTC (permalink / raw)
  To: Alexandre Belloni
  Cc: a.zummo, robh+dt, krzysztof.kozlowski+dt, linux-rtc, devicetree,
	linux-kernel, Hugo Villeneuve

On Fri, 20 Jan 2023 17:56:39 +0100
Alexandre Belloni <alexandre.belloni@bootlin.com> wrote:

> On 15/12/2022 10:02:09-0500, Hugo Villeneuve wrote:
> > From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > 
> > The PCF2127 and PCF2129 have one output interrupt pin. The PCF2131 has
> > two, named INT_A and INT_B. The hardware support that any interrupt
> > source can be routed to either one or both of them.
> > 
> > Force all interrupt sources to go to the INT A pin.
> > 
> > Support to route any interrupt source to INT A/B pins is not supported
> > by this driver at the moment.
> > 
> 
> The main issue with this is that this will created a breaking change
> once someone needs support for INTB

We already had a discussion about this a while ago:

    https://lore.kernel.org/linux-rtc/7be3f9541eaed7e17e334267e49665f442b1b458.camel@dimonoff.com/

What exactly do you suggest? I personnaly don't have any need for INTB at the moment and I would prefer to avoid the great complexity of supporting any combination of routing interrupts to any A ou  pins.


> > Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > ---
> >  drivers/rtc/rtc-pcf2127.c | 35 +++++++++++++++++++++++++++++++++++
> >  1 file changed, 35 insertions(+)
> > 
> > diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> > index 4148e135f935..68af4d0438b8 100644
> > --- a/drivers/rtc/rtc-pcf2127.c
> > +++ b/drivers/rtc/rtc-pcf2127.c
> > @@ -191,6 +191,7 @@ struct pcf21xx_config {
> >  	int max_register;
> >  	unsigned int has_nvmem:1;
> >  	unsigned int has_bit_wd_ctl_cd0:1;
> > +	unsigned int has_int_a_b:1; /* PCF2131 supports two interrupt outputs. */
> >  	u8 regs_td_base; /* Time/data base registers. */
> >  	u8 regs_alarm_base; /* Alarm function base registers. */
> >  	u8 reg_wd_ctl; /* Watchdog control register. */
> > @@ -879,6 +880,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
> >  		.max_register = 0x1d,
> >  		.has_nvmem = 1,
> >  		.has_bit_wd_ctl_cd0 = 1,
> > +		.has_int_a_b = 0,
> >  		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
> >  		.regs_alarm_base = PCF2127_REG_ALARM_BASE,
> >  		.reg_wd_ctl = PCF2127_REG_WD_CTL,
> > @@ -902,6 +904,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
> >  		.max_register = 0x19,
> >  		.has_nvmem = 0,
> >  		.has_bit_wd_ctl_cd0 = 0,
> > +		.has_int_a_b = 0,
> >  		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
> >  		.regs_alarm_base = PCF2127_REG_ALARM_BASE,
> >  		.reg_wd_ctl = PCF2127_REG_WD_CTL,
> > @@ -925,6 +928,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
> >  		.max_register = 0x36,
> >  		.has_nvmem = 0,
> >  		.has_bit_wd_ctl_cd0 = 0,
> > +		.has_int_a_b = 1,
> >  		.regs_td_base = PCF2131_REG_TIME_DATE_BASE,
> >  		.regs_alarm_base = PCF2131_REG_ALARM_BASE,
> >  		.reg_wd_ctl = PCF2131_REG_WD_CTL,
> > @@ -1017,6 +1021,28 @@ static int pcf2127_enable_ts(struct device *dev, int ts_id)
> >  	return ret;
> >  }
> >  
> > +/* Route all interrupt sources to INT A pin. */
> > +static int pcf2127_configure_interrupt_pins(struct device *dev)
> > +{
> > +	struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
> > +	int ret;
> > +
> > +	/* Mask bits need to be cleared to enable corresponding
> > +	 * interrupt source.
> > +	 */
> > +	ret = regmap_write(pcf2127->regmap,
> > +			   PCF2131_REG_INT_A_MASK1, 0);
> > +	if (ret)
> > +		return ret;
> > +
> > +	ret = regmap_write(pcf2127->regmap,
> > +			   PCF2131_REG_INT_A_MASK2, 0);
> > +	if (ret)
> > +		return ret;
> > +
> > +	return ret;
> > +}
> > +
> >  static int pcf2127_probe(struct device *dev, struct regmap *regmap,
> >  			 int alarm_irq, const char *name, const struct pcf21xx_config *config)
> >  {
> > @@ -1076,6 +1102,15 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
> >  		set_bit(RTC_FEATURE_ALARM, pcf2127->rtc->features);
> >  	}
> >  
> > +	if (pcf2127->cfg->has_int_a_b) {
> > +		/* Configure int A/B pins, independently of alarm_irq. */
> > +		ret = pcf2127_configure_interrupt_pins(dev);
> > +		if (ret) {
> > +			dev_err(dev, "failed to configure interrupt pins\n");
> > +			return ret;
> > +		}
> > +	}
> > +
> >  	if (pcf2127->cfg->has_nvmem) {
> >  		struct nvmem_config nvmem_cfg = {
> >  			.priv = pcf2127,
> > -- 
> > 2.30.2
> > 
> 
> -- 
> Alexandre Belloni, co-owner and COO, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com
> 


-- 
Hugo Villeneuve <hugo@hugovil.com>

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

* Re: [PATCH v3 11/14] rtc: pcf2127: adapt time/date registers write sequence for PCF2131
  2023-01-07 18:44   ` Bruno Thomsen
@ 2023-01-23 20:55     ` Hugo Villeneuve
  0 siblings, 0 replies; 72+ messages in thread
From: Hugo Villeneuve @ 2023-01-23 20:55 UTC (permalink / raw)
  To: Bruno Thomsen
  Cc: a.zummo, alexandre.belloni, robh+dt, krzysztof.kozlowski+dt,
	linux-rtc, devicetree, linux-kernel, Hugo Villeneuve

On Sat, 7 Jan 2023 19:44:23 +0100
Bruno Thomsen <bruno.thomsen@gmail.com> wrote:

> Den tor. 15. dec. 2022 kl. 16.19 skrev Hugo Villeneuve <hugo@hugovil.com>:
> >
> > From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> >
> > The sequence for updating the time/date registers is slightly
> > different between PCF2127/29 and PCF2131.
> >
> > For PCF2127/29, during write operations, the time counting
> > circuits (memory locations 03h through 09h) are automatically blocked.
> >
> > For PCF2131, time/date registers write access requires setting the
> > STOP bit and sending the clear prescaler instruction (CPR). STOP then
> > needs to be released once write operation is completed.
> >
> > Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > ---
> >  drivers/rtc/rtc-pcf2127.c | 38 +++++++++++++++++++++++++++++++++++++-
> >  1 file changed, 37 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> > index e4b78b9c03f9..11fbdab6bf01 100644
> > --- a/drivers/rtc/rtc-pcf2127.c
> > +++ b/drivers/rtc/rtc-pcf2127.c
> > @@ -39,6 +39,7 @@
> >  #define PCF2127_REG_CTRL1              0x00
> >  #define PCF2127_BIT_CTRL1_POR_OVRD             BIT(3)
> >  #define PCF2127_BIT_CTRL1_TSF1                 BIT(4)
> > +#define PCF2127_BIT_CTRL1_STOP                 BIT(5)
> >  /* Control register 2 */
> >  #define PCF2127_REG_CTRL2              0x01
> >  #define PCF2127_BIT_CTRL2_AIE                  BIT(1)
> > @@ -70,6 +71,7 @@
> >  #define PCF2131_REG_SR_RESET           0x05
> >  #define PCF2131_SR_RESET_READ_PATTERN  0b00100100 /* Fixed pattern. */
> >  #define PCF2131_SR_RESET_RESET_CMD     0x2C /* SR is bit 3. */
> > +#define PCF2131_SR_RESET_CPR_CMD       0xA4 /* CPR is bit 7. */
> 
> Replace 0xA4 with (BIT(2) | BIT(5) | BIT(7)) or
> (PCF2131_SR_RESET_READ_PATTERN | BIT(7))

Done.

 
> >  /* Time and date registers */
> >  #define PCF2127_REG_TIME_DATE_BASE     0x03
> >  #define PCF2131_REG_TIME_DATE_BASE     0x07 /* Register 0x06 is 100th seconds,
> > @@ -307,7 +309,31 @@ static int pcf2127_rtc_set_time(struct device *dev, struct rtc_time *tm)
> >         /* year */
> >         buf[i++] = bin2bcd(tm->tm_year - 100);
> >
> > -       /* write register's data */
> > +       /* Write access to time registers:
> > +        * PCF2127/29: no special action required.
> > +        * PCF2131:    requires setting the STOP bit. STOP bit needs to
> > +        *             be cleared after time registers are updated.
> > +        *             It is also recommended to set CPR bit, although
> > +        *             write access will work without it.
> > +        */
> > +       if (pcf2127->cfg->has_reset_reg) {
> > +               err = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL1,
> > +                                        PCF2127_BIT_CTRL1_STOP,
> > +                                        PCF2127_BIT_CTRL1_STOP);
> > +               if (err) {
> > +                       dev_err(dev, "setting STOP bit failed\n");
> > +                       return err;
> > +               }
> > +
> > +               err = regmap_write(pcf2127->regmap, pcf2127->cfg->reg_reset,
> > +                                  PCF2131_SR_RESET_CPR_CMD);
> > +               if (err) {
> > +                       dev_err(dev, "sending CPR cmd failed\n");
> > +                       return err;
> > +               }
> > +       }
> > +
> > +       /* write time register's data */
> >         err = regmap_bulk_write(pcf2127->regmap, pcf2127->cfg->regs_td_base, buf, i);
> >         if (err) {
> >                 dev_err(dev,
> > @@ -315,6 +341,16 @@ static int pcf2127_rtc_set_time(struct device *dev, struct rtc_time *tm)
> >                 return err;
> >         }
> >
> > +       if (pcf2127->cfg->has_reset_reg) {
> > +               /* Clear STOP bit (PCF2131 only) after write is completed. */
> > +               err = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL1,
> > +                                        PCF2127_BIT_CTRL1_STOP, 0);
> > +               if (err) {
> > +                       dev_err(dev, "clearing STOP bit failed\n");
> > +                       return err;
> > +               }
> > +       }
> > +
> >         return 0;
> >  }
> >
> > --
> > 2.30.2
> >
> 


-- 
Hugo Villeneuve <hugo@hugovil.com>

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

* Re: [PATCH v3 11/14] rtc: pcf2127: adapt time/date registers write sequence for PCF2131
  2023-01-20 17:09   ` Alexandre Belloni
@ 2023-01-23 21:57     ` Hugo Villeneuve
  2023-06-21 19:36       ` Alexandre Belloni
  0 siblings, 1 reply; 72+ messages in thread
From: Hugo Villeneuve @ 2023-01-23 21:57 UTC (permalink / raw)
  To: Alexandre Belloni
  Cc: a.zummo, robh+dt, krzysztof.kozlowski+dt, linux-rtc, devicetree,
	linux-kernel, Hugo Villeneuve

On Fri, 20 Jan 2023 18:09:41 +0100
Alexandre Belloni <alexandre.belloni@bootlin.com> wrote:

> On 15/12/2022 10:02:12-0500, Hugo Villeneuve wrote:
> > From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > 
> > The sequence for updating the time/date registers is slightly
> > different between PCF2127/29 and PCF2131.
> > 
> > For PCF2127/29, during write operations, the time counting
> > circuits (memory locations 03h through 09h) are automatically blocked.
> > 
> > For PCF2131, time/date registers write access requires setting the
> > STOP bit and sending the clear prescaler instruction (CPR). STOP then
> > needs to be released once write operation is completed.
> > 
> > Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > ---
> >  drivers/rtc/rtc-pcf2127.c | 38 +++++++++++++++++++++++++++++++++++++-
> >  1 file changed, 37 insertions(+), 1 deletion(-)
> > 
> > diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> > index e4b78b9c03f9..11fbdab6bf01 100644
> > --- a/drivers/rtc/rtc-pcf2127.c
> > +++ b/drivers/rtc/rtc-pcf2127.c
> > @@ -39,6 +39,7 @@
> >  #define PCF2127_REG_CTRL1		0x00
> >  #define PCF2127_BIT_CTRL1_POR_OVRD		BIT(3)
> >  #define PCF2127_BIT_CTRL1_TSF1			BIT(4)
> > +#define PCF2127_BIT_CTRL1_STOP			BIT(5)
> >  /* Control register 2 */
> >  #define PCF2127_REG_CTRL2		0x01
> >  #define PCF2127_BIT_CTRL2_AIE			BIT(1)
> > @@ -70,6 +71,7 @@
> >  #define PCF2131_REG_SR_RESET		0x05
> >  #define PCF2131_SR_RESET_READ_PATTERN	0b00100100 /* Fixed pattern. */
> >  #define PCF2131_SR_RESET_RESET_CMD	0x2C /* SR is bit 3. */
> > +#define PCF2131_SR_RESET_CPR_CMD	0xA4 /* CPR is bit 7. */
> >  /* Time and date registers */
> >  #define PCF2127_REG_TIME_DATE_BASE	0x03
> >  #define PCF2131_REG_TIME_DATE_BASE	0x07 /* Register 0x06 is 100th seconds,
> > @@ -307,7 +309,31 @@ static int pcf2127_rtc_set_time(struct device *dev, struct rtc_time *tm)
> >  	/* year */
> >  	buf[i++] = bin2bcd(tm->tm_year - 100);
> >  
> > -	/* write register's data */
> > +	/* Write access to time registers:
> > +	 * PCF2127/29: no special action required.
> > +	 * PCF2131:    requires setting the STOP bit. STOP bit needs to
> > +	 *             be cleared after time registers are updated.
> > +	 *             It is also recommended to set CPR bit, although
> > +	 *             write access will work without it.
> > +	 */
> > +	if (pcf2127->cfg->has_reset_reg) {
> 
> This should probably be tied to the actual rtc model rather than the
> presence of the reset register.
> You MUST clear CPR to be able to set the time precisely.

In fact you must actually SET the CPR bit to clear the prescaler, confusing!

I was already setting the CPR bit (clearing prescaler), so I modified the confusing comment.

The CPR bit is only present IF the reset register is also present, that is why I simply used the presence of the reset register to take the correct action. This avoids to define a new bit or matching on a device model for that functionality (adding newer models could potentially mean modifying the model match).

But if you absolutely want to match on the model, I would like to know how you would like to practically do it (maybe an example)?



> 
> > +		err = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL1,
> > +					 PCF2127_BIT_CTRL1_STOP,
> > +					 PCF2127_BIT_CTRL1_STOP);
> > +		if (err) {
> > +			dev_err(dev, "setting STOP bit failed\n");
> 
> This really needs to be less verbose. There is nothing a user can really
> do after having seen this message. Having an error in userspace will
> anyway prompt the user to retry the operation which is the only action
> it can do.

I converted the dev_err messages to dev_dbg.

In the original driver and in the same function, there is also a dev_err to handle regmap_bulk_write() failure. Do you suggest that we also make it less verbose:

err = regmap_bulk_write(pcf2127->regmap, pcf2127->cfg->reg_time_base, buf, i);
 	if (err) {
 		dev_err(dev,

???


> > +			return err;
> > +		}
> > +
> > +		err = regmap_write(pcf2127->regmap, pcf2127->cfg->reg_reset,
> > +				   PCF2131_SR_RESET_CPR_CMD);
> > +		if (err) {
> > +			dev_err(dev, "sending CPR cmd failed\n");
> > +			return err;
> > +		}
> > +	}
> > +
> > +	/* write time register's data */
> >  	err = regmap_bulk_write(pcf2127->regmap, pcf2127->cfg->regs_td_base, buf, i);
> >  	if (err) {
> >  		dev_err(dev,
> > @@ -315,6 +341,16 @@ static int pcf2127_rtc_set_time(struct device *dev, struct rtc_time *tm)
> >  		return err;
> >  	}
> >  
> > +	if (pcf2127->cfg->has_reset_reg) {
> > +		/* Clear STOP bit (PCF2131 only) after write is completed. */
> > +		err = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL1,
> > +					 PCF2127_BIT_CTRL1_STOP, 0);
> > +		if (err) {
> > +			dev_err(dev, "clearing STOP bit failed\n");
> > +			return err;
> > +		}
> > +	}
> > +
> >  	return 0;
> >  }
> >  
> > -- 
> > 2.30.2
> > 
> 
> -- 
> Alexandre Belloni, co-owner and COO, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com
> 


-- 
Hugo Villeneuve <hugo@hugovil.com>

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

* Re: [PATCH v3 09/14] rtc: pcf2127: set PWRMNG value for PCF2131
  2023-01-20 16:39     ` Alexandre Belloni
@ 2023-01-23 22:07       ` Hugo Villeneuve
  0 siblings, 0 replies; 72+ messages in thread
From: Hugo Villeneuve @ 2023-01-23 22:07 UTC (permalink / raw)
  To: Alexandre Belloni
  Cc: Bruno Thomsen, a.zummo, robh+dt, krzysztof.kozlowski+dt,
	linux-rtc, devicetree, linux-kernel, Hugo Villeneuve

On Fri, 20 Jan 2023 17:39:17 +0100
Alexandre Belloni <alexandre.belloni@bootlin.com> wrote:

> Hello,
> 
> On 07/01/2023 19:36:06+0100, Bruno Thomsen wrote:
> > Den tor. 15. dec. 2022 kl. 16.19 skrev Hugo Villeneuve <hugo@hugovil.com>:
> > >
> > > From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > >
> > > Default PWRMNG[2:0] bits are set to 000b for PCF2127/29, but to
> > > 111b for PCF2131.
> > >
> > > Set these bits to 000b to select same mode as PCF2127/29.
> > >
> > > Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > 
> > Reviewed-by: Bruno Thomsen <bruno.thomsen@gmail.com>
> > 
> > I think it's a good idea[1] but there have been concerns about
> > setting default values in the past[2]. In case somebody needs
> > a different behaviour they should add a device tree property.
> > 
> > [1] https://lore.kernel.org/linux-rtc/20190910143945.9364-1-bruno.thomsen@gmail.com/
> > [2] https://lore.kernel.org/linux-rtc/20191211163354.GC1463890@piout.net/
> 
> I confirm this is still my point of view and I won't take this patch as
> this may break existing users.

Patch dropped.

 
> > 
> > > ---
> > >  drivers/rtc/rtc-pcf2127.c | 15 +++++++++++++++
> > >  1 file changed, 15 insertions(+)
> > >
> > > diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> > > index 68af4d0438b8..241189ee4a05 100644
> > > --- a/drivers/rtc/rtc-pcf2127.c
> > > +++ b/drivers/rtc/rtc-pcf2127.c
> > > @@ -53,6 +53,7 @@
> > >  #define PCF2127_BIT_CTRL3_BLF                  BIT(2)
> > >  #define PCF2127_BIT_CTRL3_BF                   BIT(3)
> > >  #define PCF2127_BIT_CTRL3_BTSE                 BIT(4)
> > > +#define PCF2127_CTRL3_PWRMNG_MASK              GENMASK(7, 5)
> > >  /* Control register 4 */
> > >  #define PCF2131_REG_CTRL4              0x03
> > >  #define PCF2131_BIT_CTRL4_TSF4                 BIT(4)
> > > @@ -1129,6 +1130,20 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
> > >         regmap_clear_bits(pcf2127->regmap, PCF2127_REG_CTRL1,
> > >                                 PCF2127_BIT_CTRL1_POR_OVRD);
> > >
> > > +       /* Make sure PWRMNG[2:0] is set to 000b. This is the default for
> > > +        * PCF2127/29, but not for PCF2131 (default of 111b).
> > > +        *
> > > +        * PWRMNG[2:0]  = 000b:
> > > +        *   battery switch-over function is enabled in standard mode;
> > > +        *   battery low detection function is enabled
> > > +        */
> > > +       ret = regmap_clear_bits(pcf2127->regmap, PCF2127_REG_CTRL3,
> > > +                               PCF2127_CTRL3_PWRMNG_MASK);
> > > +       if (ret < 0) {
> > > +               dev_err(dev, "PWRMNG config failed\n");
> > > +               return ret;
> > > +       }
> > > +
> > >         ret = regmap_read(pcf2127->regmap, pcf2127->cfg->reg_clkout, &val);
> > >         if (ret < 0)
> > >                 return ret;
> > > --
> > > 2.30.2
> > >
> 
> -- 
> Alexandre Belloni, co-owner and COO, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com
> 


-- 
Hugo Villeneuve <hugo@hugovil.com>

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

* Re: [PATCH v3 08/14] rtc: pcf2127: add support for PCF2131 interrupts on output INT_A
  2023-01-23 20:52     ` Hugo Villeneuve
@ 2023-05-11 17:19       ` Hugo Villeneuve
  2023-06-21 19:24         ` Alexandre Belloni
  0 siblings, 1 reply; 72+ messages in thread
From: Hugo Villeneuve @ 2023-05-11 17:19 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: Alexandre Belloni, a.zummo, robh+dt, krzysztof.kozlowski+dt,
	linux-rtc, devicetree, linux-kernel, Hugo Villeneuve, hugo

On Mon, 23 Jan 2023 15:52:40 -0500
Hugo Villeneuve <hugo@hugovil.com> wrote:

> On Fri, 20 Jan 2023 17:56:39 +0100
> Alexandre Belloni <alexandre.belloni@bootlin.com> wrote:
> 
> > On 15/12/2022 10:02:09-0500, Hugo Villeneuve wrote:
> > > From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > > 
> > > The PCF2127 and PCF2129 have one output interrupt pin. The PCF2131 has
> > > two, named INT_A and INT_B. The hardware support that any interrupt
> > > source can be routed to either one or both of them.
> > > 
> > > Force all interrupt sources to go to the INT A pin.
> > > 
> > > Support to route any interrupt source to INT A/B pins is not supported
> > > by this driver at the moment.
> > > 
> > 
> > The main issue with this is that this will created a breaking change
> > once someone needs support for INTB
> 
> We already had a discussion about this a while ago:
> 
>     https://lore.kernel.org/linux-rtc/7be3f9541eaed7e17e334267e49665f442b1b458.camel@dimonoff.com/
> 
> What exactly do you suggest? I personnaly don't have any need for INTB at the moment and I would prefer to avoid the great complexity of supporting any combination of routing interrupts to any A ou  pins.

Hi Alexandre,
a few months later, and I am still waiting for your feedback on this (and other questions/interrogations I raised for other patches related to this series) to submit the next version of this patch series.

Can you have a look at it and provide some answers?

Thank you,
Hugo.


> > > Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > > ---
> > >  drivers/rtc/rtc-pcf2127.c | 35 +++++++++++++++++++++++++++++++++++
> > >  1 file changed, 35 insertions(+)
> > > 
> > > diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> > > index 4148e135f935..68af4d0438b8 100644
> > > --- a/drivers/rtc/rtc-pcf2127.c
> > > +++ b/drivers/rtc/rtc-pcf2127.c
> > > @@ -191,6 +191,7 @@ struct pcf21xx_config {
> > >  	int max_register;
> > >  	unsigned int has_nvmem:1;
> > >  	unsigned int has_bit_wd_ctl_cd0:1;
> > > +	unsigned int has_int_a_b:1; /* PCF2131 supports two interrupt outputs. */
> > >  	u8 regs_td_base; /* Time/data base registers. */
> > >  	u8 regs_alarm_base; /* Alarm function base registers. */
> > >  	u8 reg_wd_ctl; /* Watchdog control register. */
> > > @@ -879,6 +880,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
> > >  		.max_register = 0x1d,
> > >  		.has_nvmem = 1,
> > >  		.has_bit_wd_ctl_cd0 = 1,
> > > +		.has_int_a_b = 0,
> > >  		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
> > >  		.regs_alarm_base = PCF2127_REG_ALARM_BASE,
> > >  		.reg_wd_ctl = PCF2127_REG_WD_CTL,
> > > @@ -902,6 +904,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
> > >  		.max_register = 0x19,
> > >  		.has_nvmem = 0,
> > >  		.has_bit_wd_ctl_cd0 = 0,
> > > +		.has_int_a_b = 0,
> > >  		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
> > >  		.regs_alarm_base = PCF2127_REG_ALARM_BASE,
> > >  		.reg_wd_ctl = PCF2127_REG_WD_CTL,
> > > @@ -925,6 +928,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
> > >  		.max_register = 0x36,
> > >  		.has_nvmem = 0,
> > >  		.has_bit_wd_ctl_cd0 = 0,
> > > +		.has_int_a_b = 1,
> > >  		.regs_td_base = PCF2131_REG_TIME_DATE_BASE,
> > >  		.regs_alarm_base = PCF2131_REG_ALARM_BASE,
> > >  		.reg_wd_ctl = PCF2131_REG_WD_CTL,
> > > @@ -1017,6 +1021,28 @@ static int pcf2127_enable_ts(struct device *dev, int ts_id)
> > >  	return ret;
> > >  }
> > >  
> > > +/* Route all interrupt sources to INT A pin. */
> > > +static int pcf2127_configure_interrupt_pins(struct device *dev)
> > > +{
> > > +	struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
> > > +	int ret;
> > > +
> > > +	/* Mask bits need to be cleared to enable corresponding
> > > +	 * interrupt source.
> > > +	 */
> > > +	ret = regmap_write(pcf2127->regmap,
> > > +			   PCF2131_REG_INT_A_MASK1, 0);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	ret = regmap_write(pcf2127->regmap,
> > > +			   PCF2131_REG_INT_A_MASK2, 0);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	return ret;
> > > +}
> > > +
> > >  static int pcf2127_probe(struct device *dev, struct regmap *regmap,
> > >  			 int alarm_irq, const char *name, const struct pcf21xx_config *config)
> > >  {
> > > @@ -1076,6 +1102,15 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
> > >  		set_bit(RTC_FEATURE_ALARM, pcf2127->rtc->features);
> > >  	}
> > >  
> > > +	if (pcf2127->cfg->has_int_a_b) {
> > > +		/* Configure int A/B pins, independently of alarm_irq. */
> > > +		ret = pcf2127_configure_interrupt_pins(dev);
> > > +		if (ret) {
> > > +			dev_err(dev, "failed to configure interrupt pins\n");
> > > +			return ret;
> > > +		}
> > > +	}
> > > +
> > >  	if (pcf2127->cfg->has_nvmem) {
> > >  		struct nvmem_config nvmem_cfg = {
> > >  			.priv = pcf2127,
> > > -- 
> > > 2.30.2
> > > 
> > 
> > -- 
> > Alexandre Belloni, co-owner and COO, Bootlin
> > Embedded Linux and Kernel engineering
> > https://bootlin.com
> > 
> 
> 
> -- 
> Hugo Villeneuve <hugo@hugovil.com>


-- 
Hugo Villeneuve

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

* Re: [PATCH v3 00/14] rtc: pcf2127: add PCF2131 driver
  2023-01-20 19:05 ` [PATCH v3 00/14] rtc: pcf2127: add PCF2131 driver Alexandre Belloni
  2023-01-23 15:51   ` Hugo Villeneuve
@ 2023-06-21 14:14   ` Hugo Villeneuve
  2023-06-21 16:59     ` Hugo Villeneuve
  1 sibling, 1 reply; 72+ messages in thread
From: Hugo Villeneuve @ 2023-06-21 14:14 UTC (permalink / raw)
  To: Alexandre Belloni
  Cc: a.zummo, robh+dt, krzysztof.kozlowski+dt, linux-rtc, devicetree,
	linux-kernel, Hugo Villeneuve

On Fri, 20 Jan 2023 20:05:07 +0100
Alexandre Belloni <alexandre.belloni@bootlin.com> wrote:

> Hello,
> 
> I know I've been holding off on the review of this series for a while
> and I'm sorry for that.
> 
> One of the main issue that is remaining is that the driver ends up being
> 53% bigger and generaly less efficient for no added functionality for
> the existing RTCs.
> 
> I know performance is not a concern however, having more code in the
> set/read time and irq paths means that it is more difficult to set an
> get the time precisely.

Hi Alexandre,
one way to keep rtc_read_time() as efficient as before, and even more
efficient by reading 7 instead of 10 registers, would be to drop reading
the CTRL3 register, which is only used to detect and display an info
message for the low battery condition. This low battery check could be
moved to an ioctl call, like it is done in the PCF8523 driver.

Hugo.


> I guess I'll take it as a merged driver but I took a different decision
> for other RTCs.
> 
> On 15/12/2022 10:02:01-0500, Hugo Villeneuve wrote:
> > From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > 
> > Hello,
> > this patch series adds the driver for the PCF2131 real-time clock.
> > 
> > This RTC is very similar in functionality to the PCF2127/29 with the
> > following differences:
> >   -supports two new control registers at offsets 4 and 5
> >   -supports a new reset register
> >   -supports 4 tamper detection functions instead of 1
> >   -has no nvmem (like the PCF2129)
> >   -has two output interrupt pins instead of one
> >   -has 1/100th seconds capabilities (not supported in this driver)
> >   -pcf2127 has watchdog clock sources: 1/60,   1, 64 and 4096Hz
> >    pcf2131 has watchdog clock sources: 1/64, 1/4,  4 and   64Hz
> >   -watchdog value register cannot be read after being set
> > 
> > Most of the register addresses are very different, although they still
> > follow the same layout. For example, the time/date and tamper registers
> > have a different base address, but the offsets are all the same.
> > Consequently, the source code of the PCF2127 driver can be easily adapted
> > to support this new device.
> > 
> > Patches 1 to 6 modify the existing pcf2127 driver to make it more generic
> > and able to support multiple variants, like the PCF2131. This is done
> > mostly by using offsets instead of absolute hardcoded register addresses.
> > 
> > Patch 7 add actual support for the PCF2131.
> > 
> > Patch 8 configures all interrupt sources to go through the INT A pin.
> > 
> > Patch 9 changes the PWRMNG bits to be the same with the PCF2131 as they
> >       are with the PCF2127/29 (different default values).
> > 
> > Patch 10 allow to confirm PCF2131 device presence by reading the reset
> >       register fixed pattern.
> > 
> > Patch 11 adapt the time/date registers write sequence for PCF2131 (STOP and
> >       CPR bits).
> > 
> > Patch 12 add support for generic watchdog timing configuration.
> > 
> > Patch 13 add a new flag to identify if device has read support for reading
> >       watchdog register value.
> >       Since the watchdog value register cannot be read on the PCF2131 after
> >       being set, it seems that we cannot detect if watchdog timer was
> >       started by bootloader. I am not sure what is the best way to handle
> >       this situation, suggestions are welcomed.
> > 
> > Patch 14 add the dt-bindings for the PCF2131.
> > 
> > I have tested the driver using a PCF2131-ARD evaluation board connected to
> > an NXP imx8mp evaluation board:
> >   - Time get/set ok;
> >   - Alarms get/set ok
> >   - Timestamp 1 to 4 ok
> >   - IRQ alarm ok
> >   - Watchdog ok
> >   - Also tested successfully with "RTC Driver Test Example" from
> >     Documentation/rtc.txt
> > 
> > I have also tested the driver on a custom PCF2129 adapter board connected to a
> > beaglebone black.
> > 
> > Thank you.
> > 
> > Link: [v1] https://patchwork.ozlabs.org/project/rtc-linux/patch/20220125200009.900660-2-hugo@hugovil.com/
> > Link: [v2] https://patchwork.ozlabs.org/project/rtc-linux/list/?series=285734
> > 
> > Changes for V3:
> > - Rebased for kernel v6.1
> > 
> > Changes for V2:
> > - In general, fix and improvements after I have tested on real hardware
> > - Fix alarm interrupt A/B mask setting for PCF2131:
> >   PCF2131_BIT_INT_AIE must be cleared, not set, to enable interrupt.
> > - Remove low_reg validation: only check if TS interrupt flag is
> >   defined, as low_reg is defined at address 0 for PCF2127/29.
> > - Change PWRMNG value for PCF2131: default is different than PCF2127/29.
> > - Adapt time/date registers write sequence for PCF2131 (STOP and CPR bits).
> > - Map all interrupt sources to INT A pin
> > - Read and validate PCF2131 device presence from RESET register
> > - Adapt watchdog configuration for PCF2131
> > 
> > Hugo Villeneuve (14):
> >   rtc: pcf2127: add variant-specific configuration structure
> >   rtc: pcf2127: adapt for time/date registers at any offset
> >   rtc: pcf2127: adapt for alarm registers at any offset
> >   rtc: pcf2127: adapt for WD registers at any offset
> >   rtc: pcf2127: adapt for CLKOUT register at any offset
> >   rtc: pcf2127: add support for multiple TS functions
> >   rtc: pcf2127: add support for PCF2131 RTC
> >   rtc: pcf2127: add support for PCF2131 interrupts on output INT_A
> >   rtc: pcf2127: set PWRMNG value for PCF2131
> >   rtc: pcf2127: read and validate PCF2131 device signature
> >   rtc: pcf2127: adapt time/date registers write sequence for PCF2131
> >   rtc: pcf2127: support generic watchdog timing configuration
> >   rtc: pcf2127: add flag for watchdog register value read support
> >   dt-bindings: rtc: pcf2127: add PCF2131
> > 
> >  .../devicetree/bindings/rtc/nxp,pcf2127.yaml  |   4 +-
> >  drivers/rtc/Kconfig                           |   4 +-
> >  drivers/rtc/rtc-pcf2127.c                     | 939 ++++++++++++++----
> >  3 files changed, 752 insertions(+), 195 deletions(-)
> > 
> > -- 
> > 2.30.2
> > 
> 
> -- 
> Alexandre Belloni, co-owner and COO, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com
> 

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

* Re: [PATCH v3 00/14] rtc: pcf2127: add PCF2131 driver
  2023-06-21 14:14   ` Hugo Villeneuve
@ 2023-06-21 16:59     ` Hugo Villeneuve
  2023-06-21 18:14       ` Alexandre Belloni
  0 siblings, 1 reply; 72+ messages in thread
From: Hugo Villeneuve @ 2023-06-21 16:59 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: Alexandre Belloni, a.zummo, robh+dt, krzysztof.kozlowski+dt,
	linux-rtc, devicetree, linux-kernel, Hugo Villeneuve

On Wed, 21 Jun 2023 10:14:29 -0400
Hugo Villeneuve <hugo@hugovil.com> wrote:

> On Fri, 20 Jan 2023 20:05:07 +0100
> Alexandre Belloni <alexandre.belloni@bootlin.com> wrote:
> 
> > Hello,
> > 
> > I know I've been holding off on the review of this series for a while
> > and I'm sorry for that.
> > 
> > One of the main issue that is remaining is that the driver ends up being
> > 53% bigger and generaly less efficient for no added functionality for
> > the existing RTCs.
> > 
> > I know performance is not a concern however, having more code in the
> > set/read time and irq paths means that it is more difficult to set an
> > get the time precisely.
> 
> Hi Alexandre,
> one way to keep rtc_read_time() as efficient as before, and even more
> efficient by reading 7 instead of 10 registers, would be to drop reading
> the CTRL3 register, which is only used to detect and display an info
> message for the low battery condition. This low battery check could be
> moved to an ioctl call, like it is done in the PCF8523 driver.
> 
> Hugo.

Hi,
in fact it is already part of the ioctl, so it is even simpler...

Hugo.


> 
> 
> > I guess I'll take it as a merged driver but I took a different decision
> > for other RTCs.
> > 
> > On 15/12/2022 10:02:01-0500, Hugo Villeneuve wrote:
> > > From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > > 
> > > Hello,
> > > this patch series adds the driver for the PCF2131 real-time clock.
> > > 
> > > This RTC is very similar in functionality to the PCF2127/29 with the
> > > following differences:
> > >   -supports two new control registers at offsets 4 and 5
> > >   -supports a new reset register
> > >   -supports 4 tamper detection functions instead of 1
> > >   -has no nvmem (like the PCF2129)
> > >   -has two output interrupt pins instead of one
> > >   -has 1/100th seconds capabilities (not supported in this driver)
> > >   -pcf2127 has watchdog clock sources: 1/60,   1, 64 and 4096Hz
> > >    pcf2131 has watchdog clock sources: 1/64, 1/4,  4 and   64Hz
> > >   -watchdog value register cannot be read after being set
> > > 
> > > Most of the register addresses are very different, although they still
> > > follow the same layout. For example, the time/date and tamper registers
> > > have a different base address, but the offsets are all the same.
> > > Consequently, the source code of the PCF2127 driver can be easily adapted
> > > to support this new device.
> > > 
> > > Patches 1 to 6 modify the existing pcf2127 driver to make it more generic
> > > and able to support multiple variants, like the PCF2131. This is done
> > > mostly by using offsets instead of absolute hardcoded register addresses.
> > > 
> > > Patch 7 add actual support for the PCF2131.
> > > 
> > > Patch 8 configures all interrupt sources to go through the INT A pin.
> > > 
> > > Patch 9 changes the PWRMNG bits to be the same with the PCF2131 as they
> > >       are with the PCF2127/29 (different default values).
> > > 
> > > Patch 10 allow to confirm PCF2131 device presence by reading the reset
> > >       register fixed pattern.
> > > 
> > > Patch 11 adapt the time/date registers write sequence for PCF2131 (STOP and
> > >       CPR bits).
> > > 
> > > Patch 12 add support for generic watchdog timing configuration.
> > > 
> > > Patch 13 add a new flag to identify if device has read support for reading
> > >       watchdog register value.
> > >       Since the watchdog value register cannot be read on the PCF2131 after
> > >       being set, it seems that we cannot detect if watchdog timer was
> > >       started by bootloader. I am not sure what is the best way to handle
> > >       this situation, suggestions are welcomed.
> > > 
> > > Patch 14 add the dt-bindings for the PCF2131.
> > > 
> > > I have tested the driver using a PCF2131-ARD evaluation board connected to
> > > an NXP imx8mp evaluation board:
> > >   - Time get/set ok;
> > >   - Alarms get/set ok
> > >   - Timestamp 1 to 4 ok
> > >   - IRQ alarm ok
> > >   - Watchdog ok
> > >   - Also tested successfully with "RTC Driver Test Example" from
> > >     Documentation/rtc.txt
> > > 
> > > I have also tested the driver on a custom PCF2129 adapter board connected to a
> > > beaglebone black.
> > > 
> > > Thank you.
> > > 
> > > Link: [v1] https://patchwork.ozlabs.org/project/rtc-linux/patch/20220125200009.900660-2-hugo@hugovil.com/
> > > Link: [v2] https://patchwork.ozlabs.org/project/rtc-linux/list/?series=285734
> > > 
> > > Changes for V3:
> > > - Rebased for kernel v6.1
> > > 
> > > Changes for V2:
> > > - In general, fix and improvements after I have tested on real hardware
> > > - Fix alarm interrupt A/B mask setting for PCF2131:
> > >   PCF2131_BIT_INT_AIE must be cleared, not set, to enable interrupt.
> > > - Remove low_reg validation: only check if TS interrupt flag is
> > >   defined, as low_reg is defined at address 0 for PCF2127/29.
> > > - Change PWRMNG value for PCF2131: default is different than PCF2127/29.
> > > - Adapt time/date registers write sequence for PCF2131 (STOP and CPR bits).
> > > - Map all interrupt sources to INT A pin
> > > - Read and validate PCF2131 device presence from RESET register
> > > - Adapt watchdog configuration for PCF2131
> > > 
> > > Hugo Villeneuve (14):
> > >   rtc: pcf2127: add variant-specific configuration structure
> > >   rtc: pcf2127: adapt for time/date registers at any offset
> > >   rtc: pcf2127: adapt for alarm registers at any offset
> > >   rtc: pcf2127: adapt for WD registers at any offset
> > >   rtc: pcf2127: adapt for CLKOUT register at any offset
> > >   rtc: pcf2127: add support for multiple TS functions
> > >   rtc: pcf2127: add support for PCF2131 RTC
> > >   rtc: pcf2127: add support for PCF2131 interrupts on output INT_A
> > >   rtc: pcf2127: set PWRMNG value for PCF2131
> > >   rtc: pcf2127: read and validate PCF2131 device signature
> > >   rtc: pcf2127: adapt time/date registers write sequence for PCF2131
> > >   rtc: pcf2127: support generic watchdog timing configuration
> > >   rtc: pcf2127: add flag for watchdog register value read support
> > >   dt-bindings: rtc: pcf2127: add PCF2131
> > > 
> > >  .../devicetree/bindings/rtc/nxp,pcf2127.yaml  |   4 +-
> > >  drivers/rtc/Kconfig                           |   4 +-
> > >  drivers/rtc/rtc-pcf2127.c                     | 939 ++++++++++++++----
> > >  3 files changed, 752 insertions(+), 195 deletions(-)
> > > 
> > > -- 
> > > 2.30.2
> > > 
> > 
> > -- 
> > Alexandre Belloni, co-owner and COO, Bootlin
> > Embedded Linux and Kernel engineering
> > https://bootlin.com
> > 

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

* Re: [PATCH v3 00/14] rtc: pcf2127: add PCF2131 driver
  2023-06-21 16:59     ` Hugo Villeneuve
@ 2023-06-21 18:14       ` Alexandre Belloni
  2023-06-21 18:28         ` Hugo Villeneuve
  0 siblings, 1 reply; 72+ messages in thread
From: Alexandre Belloni @ 2023-06-21 18:14 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: a.zummo, robh+dt, krzysztof.kozlowski+dt, linux-rtc, devicetree,
	linux-kernel, Hugo Villeneuve

On 21/06/2023 12:59:45-0400, Hugo Villeneuve wrote:
> On Wed, 21 Jun 2023 10:14:29 -0400
> Hugo Villeneuve <hugo@hugovil.com> wrote:
> 
> > On Fri, 20 Jan 2023 20:05:07 +0100
> > Alexandre Belloni <alexandre.belloni@bootlin.com> wrote:
> > 
> > > Hello,
> > > 
> > > I know I've been holding off on the review of this series for a while
> > > and I'm sorry for that.
> > > 
> > > One of the main issue that is remaining is that the driver ends up being
> > > 53% bigger and generaly less efficient for no added functionality for
> > > the existing RTCs.
> > > 
> > > I know performance is not a concern however, having more code in the
> > > set/read time and irq paths means that it is more difficult to set an
> > > get the time precisely.
> > 
> > Hi Alexandre,
> > one way to keep rtc_read_time() as efficient as before, and even more
> > efficient by reading 7 instead of 10 registers, would be to drop reading
> > the CTRL3 register, which is only used to detect and display an info
> > message for the low battery condition. This low battery check could be
> > moved to an ioctl call, like it is done in the PCF8523 driver.
> > 
> > Hugo.
> 
> Hi,
> in fact it is already part of the ioctl, so it is even simpler...
> 

Yes, the dev_info can be removed.

> Hugo.
> 
> 
> > 
> > 
> > > I guess I'll take it as a merged driver but I took a different decision
> > > for other RTCs.
> > > 
> > > On 15/12/2022 10:02:01-0500, Hugo Villeneuve wrote:
> > > > From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > > > 
> > > > Hello,
> > > > this patch series adds the driver for the PCF2131 real-time clock.
> > > > 
> > > > This RTC is very similar in functionality to the PCF2127/29 with the
> > > > following differences:
> > > >   -supports two new control registers at offsets 4 and 5
> > > >   -supports a new reset register
> > > >   -supports 4 tamper detection functions instead of 1
> > > >   -has no nvmem (like the PCF2129)
> > > >   -has two output interrupt pins instead of one
> > > >   -has 1/100th seconds capabilities (not supported in this driver)
> > > >   -pcf2127 has watchdog clock sources: 1/60,   1, 64 and 4096Hz
> > > >    pcf2131 has watchdog clock sources: 1/64, 1/4,  4 and   64Hz
> > > >   -watchdog value register cannot be read after being set
> > > > 
> > > > Most of the register addresses are very different, although they still
> > > > follow the same layout. For example, the time/date and tamper registers
> > > > have a different base address, but the offsets are all the same.
> > > > Consequently, the source code of the PCF2127 driver can be easily adapted
> > > > to support this new device.
> > > > 
> > > > Patches 1 to 6 modify the existing pcf2127 driver to make it more generic
> > > > and able to support multiple variants, like the PCF2131. This is done
> > > > mostly by using offsets instead of absolute hardcoded register addresses.
> > > > 
> > > > Patch 7 add actual support for the PCF2131.
> > > > 
> > > > Patch 8 configures all interrupt sources to go through the INT A pin.
> > > > 
> > > > Patch 9 changes the PWRMNG bits to be the same with the PCF2131 as they
> > > >       are with the PCF2127/29 (different default values).
> > > > 
> > > > Patch 10 allow to confirm PCF2131 device presence by reading the reset
> > > >       register fixed pattern.
> > > > 
> > > > Patch 11 adapt the time/date registers write sequence for PCF2131 (STOP and
> > > >       CPR bits).
> > > > 
> > > > Patch 12 add support for generic watchdog timing configuration.
> > > > 
> > > > Patch 13 add a new flag to identify if device has read support for reading
> > > >       watchdog register value.
> > > >       Since the watchdog value register cannot be read on the PCF2131 after
> > > >       being set, it seems that we cannot detect if watchdog timer was
> > > >       started by bootloader. I am not sure what is the best way to handle
> > > >       this situation, suggestions are welcomed.
> > > > 
> > > > Patch 14 add the dt-bindings for the PCF2131.
> > > > 
> > > > I have tested the driver using a PCF2131-ARD evaluation board connected to
> > > > an NXP imx8mp evaluation board:
> > > >   - Time get/set ok;
> > > >   - Alarms get/set ok
> > > >   - Timestamp 1 to 4 ok
> > > >   - IRQ alarm ok
> > > >   - Watchdog ok
> > > >   - Also tested successfully with "RTC Driver Test Example" from
> > > >     Documentation/rtc.txt
> > > > 
> > > > I have also tested the driver on a custom PCF2129 adapter board connected to a
> > > > beaglebone black.
> > > > 
> > > > Thank you.
> > > > 
> > > > Link: [v1] https://patchwork.ozlabs.org/project/rtc-linux/patch/20220125200009.900660-2-hugo@hugovil.com/
> > > > Link: [v2] https://patchwork.ozlabs.org/project/rtc-linux/list/?series=285734
> > > > 
> > > > Changes for V3:
> > > > - Rebased for kernel v6.1
> > > > 
> > > > Changes for V2:
> > > > - In general, fix and improvements after I have tested on real hardware
> > > > - Fix alarm interrupt A/B mask setting for PCF2131:
> > > >   PCF2131_BIT_INT_AIE must be cleared, not set, to enable interrupt.
> > > > - Remove low_reg validation: only check if TS interrupt flag is
> > > >   defined, as low_reg is defined at address 0 for PCF2127/29.
> > > > - Change PWRMNG value for PCF2131: default is different than PCF2127/29.
> > > > - Adapt time/date registers write sequence for PCF2131 (STOP and CPR bits).
> > > > - Map all interrupt sources to INT A pin
> > > > - Read and validate PCF2131 device presence from RESET register
> > > > - Adapt watchdog configuration for PCF2131
> > > > 
> > > > Hugo Villeneuve (14):
> > > >   rtc: pcf2127: add variant-specific configuration structure
> > > >   rtc: pcf2127: adapt for time/date registers at any offset
> > > >   rtc: pcf2127: adapt for alarm registers at any offset
> > > >   rtc: pcf2127: adapt for WD registers at any offset
> > > >   rtc: pcf2127: adapt for CLKOUT register at any offset
> > > >   rtc: pcf2127: add support for multiple TS functions
> > > >   rtc: pcf2127: add support for PCF2131 RTC
> > > >   rtc: pcf2127: add support for PCF2131 interrupts on output INT_A
> > > >   rtc: pcf2127: set PWRMNG value for PCF2131
> > > >   rtc: pcf2127: read and validate PCF2131 device signature
> > > >   rtc: pcf2127: adapt time/date registers write sequence for PCF2131
> > > >   rtc: pcf2127: support generic watchdog timing configuration
> > > >   rtc: pcf2127: add flag for watchdog register value read support
> > > >   dt-bindings: rtc: pcf2127: add PCF2131
> > > > 
> > > >  .../devicetree/bindings/rtc/nxp,pcf2127.yaml  |   4 +-
> > > >  drivers/rtc/Kconfig                           |   4 +-
> > > >  drivers/rtc/rtc-pcf2127.c                     | 939 ++++++++++++++----
> > > >  3 files changed, 752 insertions(+), 195 deletions(-)
> > > > 
> > > > -- 
> > > > 2.30.2
> > > > 
> > > 
> > > -- 
> > > Alexandre Belloni, co-owner and COO, Bootlin
> > > Embedded Linux and Kernel engineering
> > > https://bootlin.com
> > > 

-- 
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

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

* Re: [PATCH v3 02/14] rtc: pcf2127: adapt for time/date registers at any offset
  2023-01-23 15:54     ` Hugo Villeneuve
@ 2023-06-21 18:18       ` Alexandre Belloni
  0 siblings, 0 replies; 72+ messages in thread
From: Alexandre Belloni @ 2023-06-21 18:18 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: a.zummo, robh+dt, krzysztof.kozlowski+dt, linux-rtc, devicetree,
	linux-kernel, Hugo Villeneuve

On 23/01/2023 10:54:57-0500, Hugo Villeneuve wrote:
> On Fri, 20 Jan 2023 19:47:53 +0100
> Alexandre Belloni <alexandre.belloni@bootlin.com> wrote:
> 
> > On 15/12/2022 10:02:03-0500, Hugo Villeneuve wrote:
> > > From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > > 
> > > This will simplify the implementation of new variants into this driver.
> > > 
> > > Some variants (PCF2131) have a 100th seconds register. This register is
> > > currently not supported in this driver.
> > > 
> > > Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > > ---
> > >  drivers/rtc/rtc-pcf2127.c | 68 ++++++++++++++++++++++-----------------
> > >  1 file changed, 39 insertions(+), 29 deletions(-)
> > > 
> > > diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> > > index b9a5d47a439f..fb0caacaabee 100644
> > > --- a/drivers/rtc/rtc-pcf2127.c
> > > +++ b/drivers/rtc/rtc-pcf2127.c
> > > @@ -44,14 +44,17 @@
> > >  #define PCF2127_BIT_CTRL3_BF			BIT(3)
> > >  #define PCF2127_BIT_CTRL3_BTSE			BIT(4)
> > >  /* Time and date registers */
> > > -#define PCF2127_REG_SC			0x03
> > > +#define PCF2127_REG_TIME_DATE_BASE	0x03
> > > +/* Time and date registers offsets (starting from base register) */
> > > +#define PCF2127_OFFSET_TD_SC		0
> > > +#define PCF2127_OFFSET_TD_MN		1
> > > +#define PCF2127_OFFSET_TD_HR		2
> > > +#define PCF2127_OFFSET_TD_DM		3
> > > +#define PCF2127_OFFSET_TD_DW		4
> > > +#define PCF2127_OFFSET_TD_MO		5
> > > +#define PCF2127_OFFSET_TD_YR		6
> > 
> > Same comment as for the alarms, I would simply remove the defines as
> > they don't really carry any useful information.
> 
> Note that if I remove them, the patch for pcf2127_rtc_read_time() would look like this:
> 
>  	/* Clock integrity is not guaranteed when OSF flag is set. */
> -	if (buf[PCF2127_REG_SC] & PCF2127_BIT_SC_OSF) {
> +	if (buf[0] & PCF2127_BIT_SC_OSF) {
> ...
> -		__func__, buf[PCF2127_REG_CTRL3], buf[PCF2127_REG_SC],
> -		buf[PCF2127_REG_MN], buf[PCF2127_REG_HR],
> -		buf[PCF2127_REG_DM], buf[PCF2127_REG_DW],
> -		buf[PCF2127_REG_MO], buf[PCF2127_REG_YR]);
> -
> -	tm->tm_sec = bcd2bin(buf[PCF2127_REG_SC] & 0x7F);
> -	tm->tm_min = bcd2bin(buf[PCF2127_REG_MN] & 0x7F);
> -	tm->tm_hour = bcd2bin(buf[PCF2127_REG_HR] & 0x3F); /* rtc hr 0-23 */
> -	tm->tm_mday = bcd2bin(buf[PCF2127_REG_DM] & 0x3F);
> -	tm->tm_wday = buf[PCF2127_REG_DW] & 0x07;
> -	tm->tm_mon = bcd2bin(buf[PCF2127_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */
> -	tm->tm_year = bcd2bin(buf[PCF2127_REG_YR]);
> +		__func__, ctrl3, buf[0],
> +		buf[1], buf[2],
> +		buf[3], buf[4],
> +		buf[5], buf[PCF2127_OFFSET_TD_YR]);
> +
> +	tm->tm_sec = bcd2bin(buf[0] & 0x7F);
> +	tm->tm_min = bcd2bin(buf[1] & 0x7F);
> +	tm->tm_hour = bcd2bin(buf[2] & 0x3F); /* rtc hr 0-23 */
> +	tm->tm_mday = bcd2bin(buf[3] & 0x3F);
> +	tm->tm_wday = buf[4] & 0x07;
> +	tm->tm_mon = bcd2bin(buf[5] & 0x1F) - 1; /* rtc mn 1-12 */
> +	tm->tm_year = bcd2bin(buf[6]);
> 
> Do you still want to remove the defines then?

This is fine, yes.

> 
> 
> > > +/* Time and date registers bits */
> > >  #define PCF2127_BIT_SC_OSF			BIT(7)
> > > -#define PCF2127_REG_MN			0x04
> > > -#define PCF2127_REG_HR			0x05
> > > -#define PCF2127_REG_DM			0x06
> > > -#define PCF2127_REG_DW			0x07
> > > -#define PCF2127_REG_MO			0x08
> > > -#define PCF2127_REG_YR			0x09
> > >  /* Alarm registers */
> > >  #define PCF2127_REG_ALARM_SC		0x0A
> > >  #define PCF2127_REG_ALARM_MN		0x0B
> > > @@ -106,6 +109,7 @@ struct pcf21xx_config {
> > >  	int max_register;
> > >  	unsigned int has_nvmem:1;
> > >  	unsigned int has_bit_wd_ctl_cd0:1;
> > > +	u8 regs_td_base; /* Time/data base registers. */
> > >  };
> > >  
> > >  struct pcf2127 {
> > > @@ -125,27 +129,31 @@ struct pcf2127 {
> > >  static int pcf2127_rtc_read_time(struct device *dev, struct rtc_time *tm)
> > >  {
> > >  	struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
> > > -	unsigned char buf[10];
> > > +	unsigned char buf[7];
> > > +	unsigned int ctrl3;
> > >  	int ret;
> > >  
> > >  	/*
> > >  	 * Avoid reading CTRL2 register as it causes WD_VAL register
> > >  	 * value to reset to 0 which means watchdog is stopped.
> > >  	 */
> > > -	ret = regmap_bulk_read(pcf2127->regmap, PCF2127_REG_CTRL3,
> > > -			       (buf + PCF2127_REG_CTRL3),
> > > -			       ARRAY_SIZE(buf) - PCF2127_REG_CTRL3);
> > > -	if (ret) {
> > > -		dev_err(dev, "%s: read error\n", __func__);
> > > +	ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL3, &ctrl3);
> > > +	if (ret)
> > >  		return ret;
> > > -	}
> > >  
> > > -	if (buf[PCF2127_REG_CTRL3] & PCF2127_BIT_CTRL3_BLF)
> > > +	if (ctrl3 & PCF2127_BIT_CTRL3_BLF)
> > >  		dev_info(dev,
> > >  			"low voltage detected, check/replace RTC battery.\n");
> > >  
> > > +	ret = regmap_bulk_read(pcf2127->regmap, pcf2127->cfg->regs_td_base,
> > > +			       buf, sizeof(buf));
> > > +	if (ret) {
> > > +		dev_err(dev, "%s: read error\n", __func__);
> > > +		return ret;
> > > +	}
> > > +
> > >  	/* Clock integrity is not guaranteed when OSF flag is set. */
> > > -	if (buf[PCF2127_REG_SC] & PCF2127_BIT_SC_OSF) {
> > > +	if (buf[PCF2127_OFFSET_TD_SC] & PCF2127_BIT_SC_OSF) {
> > >  		/*
> > >  		 * no need clear the flag here,
> > >  		 * it will be cleared once the new date is saved
> > > @@ -158,18 +166,18 @@ static int pcf2127_rtc_read_time(struct device *dev, struct rtc_time *tm)
> > >  	dev_dbg(dev,
> > >  		"%s: raw data is cr3=%02x, sec=%02x, min=%02x, hr=%02x, "
> > >  		"mday=%02x, wday=%02x, mon=%02x, year=%02x\n",
> > > -		__func__, buf[PCF2127_REG_CTRL3], buf[PCF2127_REG_SC],
> > > -		buf[PCF2127_REG_MN], buf[PCF2127_REG_HR],
> > > -		buf[PCF2127_REG_DM], buf[PCF2127_REG_DW],
> > > -		buf[PCF2127_REG_MO], buf[PCF2127_REG_YR]);
> > > -
> > > -	tm->tm_sec = bcd2bin(buf[PCF2127_REG_SC] & 0x7F);
> > > -	tm->tm_min = bcd2bin(buf[PCF2127_REG_MN] & 0x7F);
> > > -	tm->tm_hour = bcd2bin(buf[PCF2127_REG_HR] & 0x3F); /* rtc hr 0-23 */
> > > -	tm->tm_mday = bcd2bin(buf[PCF2127_REG_DM] & 0x3F);
> > > -	tm->tm_wday = buf[PCF2127_REG_DW] & 0x07;
> > > -	tm->tm_mon = bcd2bin(buf[PCF2127_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */
> > > -	tm->tm_year = bcd2bin(buf[PCF2127_REG_YR]);
> > > +		__func__, ctrl3, buf[PCF2127_OFFSET_TD_SC],
> > > +		buf[PCF2127_OFFSET_TD_MN], buf[PCF2127_OFFSET_TD_HR],
> > > +		buf[PCF2127_OFFSET_TD_DM], buf[PCF2127_OFFSET_TD_DW],
> > > +		buf[PCF2127_OFFSET_TD_MO], buf[PCF2127_OFFSET_TD_YR]);
> > > +
> > > +	tm->tm_sec = bcd2bin(buf[PCF2127_OFFSET_TD_SC] & 0x7F);
> > > +	tm->tm_min = bcd2bin(buf[PCF2127_OFFSET_TD_MN] & 0x7F);
> > > +	tm->tm_hour = bcd2bin(buf[PCF2127_OFFSET_TD_HR] & 0x3F); /* rtc hr 0-23 */
> > 
> > You can drop the comment
> 
> Done
> 
> > 
> > > +	tm->tm_mday = bcd2bin(buf[PCF2127_OFFSET_TD_DM] & 0x3F);
> > > +	tm->tm_wday = buf[PCF2127_OFFSET_TD_DW] & 0x07;
> > > +	tm->tm_mon = bcd2bin(buf[PCF2127_OFFSET_TD_MO] & 0x1F) - 1; /* rtc mn 1-12 */
> > 
> > This comment too.
> 
> Done
> 
> > 
> > > +	tm->tm_year = bcd2bin(buf[PCF2127_OFFSET_TD_YR]);
> > >  	tm->tm_year += 100;
> > >  
> > >  	dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
> > > @@ -207,7 +215,7 @@ static int pcf2127_rtc_set_time(struct device *dev, struct rtc_time *tm)
> > >  	buf[i++] = bin2bcd(tm->tm_year - 100);
> > >  
> > >  	/* write register's data */
> > > -	err = regmap_bulk_write(pcf2127->regmap, PCF2127_REG_SC, buf, i);
> > > +	err = regmap_bulk_write(pcf2127->regmap, pcf2127->cfg->regs_td_base, buf, i);
> > >  	if (err) {
> > >  		dev_err(dev,
> > >  			"%s: err=%d", __func__, err);
> > > @@ -650,11 +658,13 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
> > >  		.max_register = 0x1d,
> > >  		.has_nvmem = 1,
> > >  		.has_bit_wd_ctl_cd0 = 1,
> > > +		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
> > >  	},
> > >  	[PCF2129] = {
> > >  		.max_register = 0x19,
> > >  		.has_nvmem = 0,
> > >  		.has_bit_wd_ctl_cd0 = 0,
> > > +		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
> > >  	},
> > >  };
> > >  
> > > -- 
> > > 2.30.2
> > > 
> > 
> > -- 
> > Alexandre Belloni, co-owner and COO, Bootlin
> > Embedded Linux and Kernel engineering
> > https://bootlin.com
> > 
> 
> 
> -- 
> Hugo Villeneuve <hugo@hugovil.com>

-- 
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

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

* Re: [PATCH v3 00/14] rtc: pcf2127: add PCF2131 driver
  2023-06-21 18:14       ` Alexandre Belloni
@ 2023-06-21 18:28         ` Hugo Villeneuve
  2023-07-05 13:40           ` Hugo Villeneuve
  0 siblings, 1 reply; 72+ messages in thread
From: Hugo Villeneuve @ 2023-06-21 18:28 UTC (permalink / raw)
  To: Alexandre Belloni
  Cc: a.zummo, robh+dt, krzysztof.kozlowski+dt, linux-rtc, devicetree,
	linux-kernel, Hugo Villeneuve

On Wed, 21 Jun 2023 20:14:41 +0200
Alexandre Belloni <alexandre.belloni@bootlin.com> wrote:

> On 21/06/2023 12:59:45-0400, Hugo Villeneuve wrote:
> > On Wed, 21 Jun 2023 10:14:29 -0400
> > Hugo Villeneuve <hugo@hugovil.com> wrote:
> > 
> > > On Fri, 20 Jan 2023 20:05:07 +0100
> > > Alexandre Belloni <alexandre.belloni@bootlin.com> wrote:
> > > 
> > > > Hello,
> > > > 
> > > > I know I've been holding off on the review of this series for a while
> > > > and I'm sorry for that.
> > > > 
> > > > One of the main issue that is remaining is that the driver ends up being
> > > > 53% bigger and generaly less efficient for no added functionality for
> > > > the existing RTCs.
> > > > 
> > > > I know performance is not a concern however, having more code in the
> > > > set/read time and irq paths means that it is more difficult to set an
> > > > get the time precisely.
> > > 
> > > Hi Alexandre,
> > > one way to keep rtc_read_time() as efficient as before, and even more
> > > efficient by reading 7 instead of 10 registers, would be to drop reading
> > > the CTRL3 register, which is only used to detect and display an info
> > > message for the low battery condition. This low battery check could be
> > > moved to an ioctl call, like it is done in the PCF8523 driver.
> > > 
> > > Hugo.
> > 
> > Hi,
> > in fact it is already part of the ioctl, so it is even simpler...
> > 
> 
> Yes, the dev_info can be removed.

Hi,
great, I will integrate that patch to improve rtc_read_time()
performance, and resubmit V4 soon with the requested changes mentioned
during V3 review.

Thank you, Hugo.


> > > > I guess I'll take it as a merged driver but I took a different decision
> > > > for other RTCs.
> > > > 
> > > > On 15/12/2022 10:02:01-0500, Hugo Villeneuve wrote:
> > > > > From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > > > > 
> > > > > Hello,
> > > > > this patch series adds the driver for the PCF2131 real-time clock.
> > > > > 
> > > > > This RTC is very similar in functionality to the PCF2127/29 with the
> > > > > following differences:
> > > > >   -supports two new control registers at offsets 4 and 5
> > > > >   -supports a new reset register
> > > > >   -supports 4 tamper detection functions instead of 1
> > > > >   -has no nvmem (like the PCF2129)
> > > > >   -has two output interrupt pins instead of one
> > > > >   -has 1/100th seconds capabilities (not supported in this driver)
> > > > >   -pcf2127 has watchdog clock sources: 1/60,   1, 64 and 4096Hz
> > > > >    pcf2131 has watchdog clock sources: 1/64, 1/4,  4 and   64Hz
> > > > >   -watchdog value register cannot be read after being set
> > > > > 
> > > > > Most of the register addresses are very different, although they still
> > > > > follow the same layout. For example, the time/date and tamper registers
> > > > > have a different base address, but the offsets are all the same.
> > > > > Consequently, the source code of the PCF2127 driver can be easily adapted
> > > > > to support this new device.
> > > > > 
> > > > > Patches 1 to 6 modify the existing pcf2127 driver to make it more generic
> > > > > and able to support multiple variants, like the PCF2131. This is done
> > > > > mostly by using offsets instead of absolute hardcoded register addresses.
> > > > > 
> > > > > Patch 7 add actual support for the PCF2131.
> > > > > 
> > > > > Patch 8 configures all interrupt sources to go through the INT A pin.
> > > > > 
> > > > > Patch 9 changes the PWRMNG bits to be the same with the PCF2131 as they
> > > > >       are with the PCF2127/29 (different default values).
> > > > > 
> > > > > Patch 10 allow to confirm PCF2131 device presence by reading the reset
> > > > >       register fixed pattern.
> > > > > 
> > > > > Patch 11 adapt the time/date registers write sequence for PCF2131 (STOP and
> > > > >       CPR bits).
> > > > > 
> > > > > Patch 12 add support for generic watchdog timing configuration.
> > > > > 
> > > > > Patch 13 add a new flag to identify if device has read support for reading
> > > > >       watchdog register value.
> > > > >       Since the watchdog value register cannot be read on the PCF2131 after
> > > > >       being set, it seems that we cannot detect if watchdog timer was
> > > > >       started by bootloader. I am not sure what is the best way to handle
> > > > >       this situation, suggestions are welcomed.
> > > > > 
> > > > > Patch 14 add the dt-bindings for the PCF2131.
> > > > > 
> > > > > I have tested the driver using a PCF2131-ARD evaluation board connected to
> > > > > an NXP imx8mp evaluation board:
> > > > >   - Time get/set ok;
> > > > >   - Alarms get/set ok
> > > > >   - Timestamp 1 to 4 ok
> > > > >   - IRQ alarm ok
> > > > >   - Watchdog ok
> > > > >   - Also tested successfully with "RTC Driver Test Example" from
> > > > >     Documentation/rtc.txt
> > > > > 
> > > > > I have also tested the driver on a custom PCF2129 adapter board connected to a
> > > > > beaglebone black.
> > > > > 
> > > > > Thank you.
> > > > > 
> > > > > Link: [v1] https://patchwork.ozlabs.org/project/rtc-linux/patch/20220125200009.900660-2-hugo@hugovil.com/
> > > > > Link: [v2] https://patchwork.ozlabs.org/project/rtc-linux/list/?series=285734
> > > > > 
> > > > > Changes for V3:
> > > > > - Rebased for kernel v6.1
> > > > > 
> > > > > Changes for V2:
> > > > > - In general, fix and improvements after I have tested on real hardware
> > > > > - Fix alarm interrupt A/B mask setting for PCF2131:
> > > > >   PCF2131_BIT_INT_AIE must be cleared, not set, to enable interrupt.
> > > > > - Remove low_reg validation: only check if TS interrupt flag is
> > > > >   defined, as low_reg is defined at address 0 for PCF2127/29.
> > > > > - Change PWRMNG value for PCF2131: default is different than PCF2127/29.
> > > > > - Adapt time/date registers write sequence for PCF2131 (STOP and CPR bits).
> > > > > - Map all interrupt sources to INT A pin
> > > > > - Read and validate PCF2131 device presence from RESET register
> > > > > - Adapt watchdog configuration for PCF2131
> > > > > 
> > > > > Hugo Villeneuve (14):
> > > > >   rtc: pcf2127: add variant-specific configuration structure
> > > > >   rtc: pcf2127: adapt for time/date registers at any offset
> > > > >   rtc: pcf2127: adapt for alarm registers at any offset
> > > > >   rtc: pcf2127: adapt for WD registers at any offset
> > > > >   rtc: pcf2127: adapt for CLKOUT register at any offset
> > > > >   rtc: pcf2127: add support for multiple TS functions
> > > > >   rtc: pcf2127: add support for PCF2131 RTC
> > > > >   rtc: pcf2127: add support for PCF2131 interrupts on output INT_A
> > > > >   rtc: pcf2127: set PWRMNG value for PCF2131
> > > > >   rtc: pcf2127: read and validate PCF2131 device signature
> > > > >   rtc: pcf2127: adapt time/date registers write sequence for PCF2131
> > > > >   rtc: pcf2127: support generic watchdog timing configuration
> > > > >   rtc: pcf2127: add flag for watchdog register value read support
> > > > >   dt-bindings: rtc: pcf2127: add PCF2131
> > > > > 
> > > > >  .../devicetree/bindings/rtc/nxp,pcf2127.yaml  |   4 +-
> > > > >  drivers/rtc/Kconfig                           |   4 +-
> > > > >  drivers/rtc/rtc-pcf2127.c                     | 939 ++++++++++++++----
> > > > >  3 files changed, 752 insertions(+), 195 deletions(-)
> > > > > 
> > > > > -- 
> > > > > 2.30.2
> > > > > 
> > > > 
> > > > -- 
> > > > Alexandre Belloni, co-owner and COO, Bootlin
> > > > Embedded Linux and Kernel engineering
> > > > https://bootlin.com
> > > > 
> 
> -- 
> Alexandre Belloni, co-owner and COO, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com
> 

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

* Re: [PATCH v3 08/14] rtc: pcf2127: add support for PCF2131 interrupts on output INT_A
  2023-05-11 17:19       ` Hugo Villeneuve
@ 2023-06-21 19:24         ` Alexandre Belloni
  2023-06-21 19:26           ` Alexandre Belloni
  0 siblings, 1 reply; 72+ messages in thread
From: Alexandre Belloni @ 2023-06-21 19:24 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: a.zummo, robh+dt, krzysztof.kozlowski+dt, linux-rtc, devicetree,
	linux-kernel, Hugo Villeneuve

On 11/05/2023 13:19:58-0400, Hugo Villeneuve wrote:
> On Mon, 23 Jan 2023 15:52:40 -0500
> Hugo Villeneuve <hugo@hugovil.com> wrote:
> 
> > On Fri, 20 Jan 2023 17:56:39 +0100
> > Alexandre Belloni <alexandre.belloni@bootlin.com> wrote:
> > 
> > > On 15/12/2022 10:02:09-0500, Hugo Villeneuve wrote:
> > > > From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > > > 
> > > > The PCF2127 and PCF2129 have one output interrupt pin. The PCF2131 has
> > > > two, named INT_A and INT_B. The hardware support that any interrupt
> > > > source can be routed to either one or both of them.
> > > > 
> > > > Force all interrupt sources to go to the INT A pin.
> > > > 
> > > > Support to route any interrupt source to INT A/B pins is not supported
> > > > by this driver at the moment.
> > > > 
> > > 
> > > The main issue with this is that this will created a breaking change
> > > once someone needs support for INTB
> > 
> > We already had a discussion about this a while ago:
> > 
> >     https://lore.kernel.org/linux-rtc/7be3f9541eaed7e17e334267e49665f442b1b458.camel@dimonoff.com/
> > 
> > What exactly do you suggest? I personnaly don't have any need for INTB at the moment and I would prefer to avoid the great complexity of supporting any combination of routing interrupts to any A ou  pins.
> 
> Hi Alexandre,
> a few months later, and I am still waiting for your feedback on this (and other questions/interrogations I raised for other patches related to this series) to submit the next version of this patch series.
> 
> Can you have a look at it and provide some answers?
> 

I'm very very sorry this takes so long. For this one, I don't have a
precise idea. I guess we could have one property per pin with a mask of
the interrupts we are interested in. That would cover all the use cases.
For example, a PMIC could take the alarms on INTB and the CPU could have
alarms, battery low and UIE on INTA.


> Thank you,
> Hugo.
> 
> 
> > > > Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > > > ---
> > > >  drivers/rtc/rtc-pcf2127.c | 35 +++++++++++++++++++++++++++++++++++
> > > >  1 file changed, 35 insertions(+)
> > > > 
> > > > diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> > > > index 4148e135f935..68af4d0438b8 100644
> > > > --- a/drivers/rtc/rtc-pcf2127.c
> > > > +++ b/drivers/rtc/rtc-pcf2127.c
> > > > @@ -191,6 +191,7 @@ struct pcf21xx_config {
> > > >  	int max_register;
> > > >  	unsigned int has_nvmem:1;
> > > >  	unsigned int has_bit_wd_ctl_cd0:1;
> > > > +	unsigned int has_int_a_b:1; /* PCF2131 supports two interrupt outputs. */
> > > >  	u8 regs_td_base; /* Time/data base registers. */
> > > >  	u8 regs_alarm_base; /* Alarm function base registers. */
> > > >  	u8 reg_wd_ctl; /* Watchdog control register. */
> > > > @@ -879,6 +880,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
> > > >  		.max_register = 0x1d,
> > > >  		.has_nvmem = 1,
> > > >  		.has_bit_wd_ctl_cd0 = 1,
> > > > +		.has_int_a_b = 0,
> > > >  		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
> > > >  		.regs_alarm_base = PCF2127_REG_ALARM_BASE,
> > > >  		.reg_wd_ctl = PCF2127_REG_WD_CTL,
> > > > @@ -902,6 +904,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
> > > >  		.max_register = 0x19,
> > > >  		.has_nvmem = 0,
> > > >  		.has_bit_wd_ctl_cd0 = 0,
> > > > +		.has_int_a_b = 0,
> > > >  		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
> > > >  		.regs_alarm_base = PCF2127_REG_ALARM_BASE,
> > > >  		.reg_wd_ctl = PCF2127_REG_WD_CTL,
> > > > @@ -925,6 +928,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
> > > >  		.max_register = 0x36,
> > > >  		.has_nvmem = 0,
> > > >  		.has_bit_wd_ctl_cd0 = 0,
> > > > +		.has_int_a_b = 1,
> > > >  		.regs_td_base = PCF2131_REG_TIME_DATE_BASE,
> > > >  		.regs_alarm_base = PCF2131_REG_ALARM_BASE,
> > > >  		.reg_wd_ctl = PCF2131_REG_WD_CTL,
> > > > @@ -1017,6 +1021,28 @@ static int pcf2127_enable_ts(struct device *dev, int ts_id)
> > > >  	return ret;
> > > >  }
> > > >  
> > > > +/* Route all interrupt sources to INT A pin. */
> > > > +static int pcf2127_configure_interrupt_pins(struct device *dev)
> > > > +{
> > > > +	struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
> > > > +	int ret;
> > > > +
> > > > +	/* Mask bits need to be cleared to enable corresponding
> > > > +	 * interrupt source.
> > > > +	 */
> > > > +	ret = regmap_write(pcf2127->regmap,
> > > > +			   PCF2131_REG_INT_A_MASK1, 0);
> > > > +	if (ret)
> > > > +		return ret;
> > > > +
> > > > +	ret = regmap_write(pcf2127->regmap,
> > > > +			   PCF2131_REG_INT_A_MASK2, 0);
> > > > +	if (ret)
> > > > +		return ret;
> > > > +
> > > > +	return ret;
> > > > +}
> > > > +
> > > >  static int pcf2127_probe(struct device *dev, struct regmap *regmap,
> > > >  			 int alarm_irq, const char *name, const struct pcf21xx_config *config)
> > > >  {
> > > > @@ -1076,6 +1102,15 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
> > > >  		set_bit(RTC_FEATURE_ALARM, pcf2127->rtc->features);
> > > >  	}
> > > >  
> > > > +	if (pcf2127->cfg->has_int_a_b) {
> > > > +		/* Configure int A/B pins, independently of alarm_irq. */
> > > > +		ret = pcf2127_configure_interrupt_pins(dev);
> > > > +		if (ret) {
> > > > +			dev_err(dev, "failed to configure interrupt pins\n");
> > > > +			return ret;
> > > > +		}
> > > > +	}
> > > > +
> > > >  	if (pcf2127->cfg->has_nvmem) {
> > > >  		struct nvmem_config nvmem_cfg = {
> > > >  			.priv = pcf2127,
> > > > -- 
> > > > 2.30.2
> > > > 
> > > 
> > > -- 
> > > Alexandre Belloni, co-owner and COO, Bootlin
> > > Embedded Linux and Kernel engineering
> > > https://bootlin.com
> > > 
> > 
> > 
> > -- 
> > Hugo Villeneuve <hugo@hugovil.com>
> 
> 
> -- 
> Hugo Villeneuve

-- 
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

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

* Re: [PATCH v3 08/14] rtc: pcf2127: add support for PCF2131 interrupts on output INT_A
  2023-06-21 19:24         ` Alexandre Belloni
@ 2023-06-21 19:26           ` Alexandre Belloni
  2023-06-22 14:21             ` Hugo Villeneuve
  0 siblings, 1 reply; 72+ messages in thread
From: Alexandre Belloni @ 2023-06-21 19:26 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: a.zummo, robh+dt, krzysztof.kozlowski+dt, linux-rtc, devicetree,
	linux-kernel, Hugo Villeneuve

On 21/06/2023 21:24:37+0200, Alexandre Belloni wrote:
> On 11/05/2023 13:19:58-0400, Hugo Villeneuve wrote:
> > On Mon, 23 Jan 2023 15:52:40 -0500
> > Hugo Villeneuve <hugo@hugovil.com> wrote:
> > 
> > > On Fri, 20 Jan 2023 17:56:39 +0100
> > > Alexandre Belloni <alexandre.belloni@bootlin.com> wrote:
> > > 
> > > > On 15/12/2022 10:02:09-0500, Hugo Villeneuve wrote:
> > > > > From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > > > > 
> > > > > The PCF2127 and PCF2129 have one output interrupt pin. The PCF2131 has
> > > > > two, named INT_A and INT_B. The hardware support that any interrupt
> > > > > source can be routed to either one or both of them.
> > > > > 
> > > > > Force all interrupt sources to go to the INT A pin.
> > > > > 
> > > > > Support to route any interrupt source to INT A/B pins is not supported
> > > > > by this driver at the moment.
> > > > > 
> > > > 
> > > > The main issue with this is that this will created a breaking change
> > > > once someone needs support for INTB
> > > 
> > > We already had a discussion about this a while ago:
> > > 
> > >     https://lore.kernel.org/linux-rtc/7be3f9541eaed7e17e334267e49665f442b1b458.camel@dimonoff.com/
> > > 
> > > What exactly do you suggest? I personnaly don't have any need for INTB at the moment and I would prefer to avoid the great complexity of supporting any combination of routing interrupts to any A ou  pins.
> > 
> > Hi Alexandre,
> > a few months later, and I am still waiting for your feedback on this (and other questions/interrogations I raised for other patches related to this series) to submit the next version of this patch series.
> > 
> > Can you have a look at it and provide some answers?
> > 
> 
> I'm very very sorry this takes so long. For this one, I don't have a
> precise idea. I guess we could have one property per pin with a mask of
> the interrupts we are interested in. That would cover all the use cases.
> For example, a PMIC could take the alarms on INTB and the CPU could have
> alarms, battery low and UIE on INTA.

As the mask for INTA and INTB are set to have interrupts on both by
default, maybe you could keep that in a separate series so we can wait
for the DT maintainer to give their opinion.
> 
> 
> > Thank you,
> > Hugo.
> > 
> > 
> > > > > Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > > > > ---
> > > > >  drivers/rtc/rtc-pcf2127.c | 35 +++++++++++++++++++++++++++++++++++
> > > > >  1 file changed, 35 insertions(+)
> > > > > 
> > > > > diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> > > > > index 4148e135f935..68af4d0438b8 100644
> > > > > --- a/drivers/rtc/rtc-pcf2127.c
> > > > > +++ b/drivers/rtc/rtc-pcf2127.c
> > > > > @@ -191,6 +191,7 @@ struct pcf21xx_config {
> > > > >  	int max_register;
> > > > >  	unsigned int has_nvmem:1;
> > > > >  	unsigned int has_bit_wd_ctl_cd0:1;
> > > > > +	unsigned int has_int_a_b:1; /* PCF2131 supports two interrupt outputs. */
> > > > >  	u8 regs_td_base; /* Time/data base registers. */
> > > > >  	u8 regs_alarm_base; /* Alarm function base registers. */
> > > > >  	u8 reg_wd_ctl; /* Watchdog control register. */
> > > > > @@ -879,6 +880,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
> > > > >  		.max_register = 0x1d,
> > > > >  		.has_nvmem = 1,
> > > > >  		.has_bit_wd_ctl_cd0 = 1,
> > > > > +		.has_int_a_b = 0,
> > > > >  		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
> > > > >  		.regs_alarm_base = PCF2127_REG_ALARM_BASE,
> > > > >  		.reg_wd_ctl = PCF2127_REG_WD_CTL,
> > > > > @@ -902,6 +904,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
> > > > >  		.max_register = 0x19,
> > > > >  		.has_nvmem = 0,
> > > > >  		.has_bit_wd_ctl_cd0 = 0,
> > > > > +		.has_int_a_b = 0,
> > > > >  		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
> > > > >  		.regs_alarm_base = PCF2127_REG_ALARM_BASE,
> > > > >  		.reg_wd_ctl = PCF2127_REG_WD_CTL,
> > > > > @@ -925,6 +928,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
> > > > >  		.max_register = 0x36,
> > > > >  		.has_nvmem = 0,
> > > > >  		.has_bit_wd_ctl_cd0 = 0,
> > > > > +		.has_int_a_b = 1,
> > > > >  		.regs_td_base = PCF2131_REG_TIME_DATE_BASE,
> > > > >  		.regs_alarm_base = PCF2131_REG_ALARM_BASE,
> > > > >  		.reg_wd_ctl = PCF2131_REG_WD_CTL,
> > > > > @@ -1017,6 +1021,28 @@ static int pcf2127_enable_ts(struct device *dev, int ts_id)
> > > > >  	return ret;
> > > > >  }
> > > > >  
> > > > > +/* Route all interrupt sources to INT A pin. */
> > > > > +static int pcf2127_configure_interrupt_pins(struct device *dev)
> > > > > +{
> > > > > +	struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
> > > > > +	int ret;
> > > > > +
> > > > > +	/* Mask bits need to be cleared to enable corresponding
> > > > > +	 * interrupt source.
> > > > > +	 */
> > > > > +	ret = regmap_write(pcf2127->regmap,
> > > > > +			   PCF2131_REG_INT_A_MASK1, 0);
> > > > > +	if (ret)
> > > > > +		return ret;
> > > > > +
> > > > > +	ret = regmap_write(pcf2127->regmap,
> > > > > +			   PCF2131_REG_INT_A_MASK2, 0);
> > > > > +	if (ret)
> > > > > +		return ret;
> > > > > +
> > > > > +	return ret;
> > > > > +}
> > > > > +
> > > > >  static int pcf2127_probe(struct device *dev, struct regmap *regmap,
> > > > >  			 int alarm_irq, const char *name, const struct pcf21xx_config *config)
> > > > >  {
> > > > > @@ -1076,6 +1102,15 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
> > > > >  		set_bit(RTC_FEATURE_ALARM, pcf2127->rtc->features);
> > > > >  	}
> > > > >  
> > > > > +	if (pcf2127->cfg->has_int_a_b) {
> > > > > +		/* Configure int A/B pins, independently of alarm_irq. */
> > > > > +		ret = pcf2127_configure_interrupt_pins(dev);
> > > > > +		if (ret) {
> > > > > +			dev_err(dev, "failed to configure interrupt pins\n");
> > > > > +			return ret;
> > > > > +		}
> > > > > +	}
> > > > > +
> > > > >  	if (pcf2127->cfg->has_nvmem) {
> > > > >  		struct nvmem_config nvmem_cfg = {
> > > > >  			.priv = pcf2127,
> > > > > -- 
> > > > > 2.30.2
> > > > > 
> > > > 
> > > > -- 
> > > > Alexandre Belloni, co-owner and COO, Bootlin
> > > > Embedded Linux and Kernel engineering
> > > > https://bootlin.com
> > > > 
> > > 
> > > 
> > > -- 
> > > Hugo Villeneuve <hugo@hugovil.com>
> > 
> > 
> > -- 
> > Hugo Villeneuve
> 
> -- 
> Alexandre Belloni, co-owner and COO, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com

-- 
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

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

* Re: [PATCH v3 11/14] rtc: pcf2127: adapt time/date registers write sequence for PCF2131
  2023-01-23 21:57     ` Hugo Villeneuve
@ 2023-06-21 19:36       ` Alexandre Belloni
  0 siblings, 0 replies; 72+ messages in thread
From: Alexandre Belloni @ 2023-06-21 19:36 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: a.zummo, robh+dt, krzysztof.kozlowski+dt, linux-rtc, devicetree,
	linux-kernel, Hugo Villeneuve

On 23/01/2023 16:57:41-0500, Hugo Villeneuve wrote:
> On Fri, 20 Jan 2023 18:09:41 +0100
> Alexandre Belloni <alexandre.belloni@bootlin.com> wrote:
> 
> > On 15/12/2022 10:02:12-0500, Hugo Villeneuve wrote:
> > > From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > > 
> > > The sequence for updating the time/date registers is slightly
> > > different between PCF2127/29 and PCF2131.
> > > 
> > > For PCF2127/29, during write operations, the time counting
> > > circuits (memory locations 03h through 09h) are automatically blocked.
> > > 
> > > For PCF2131, time/date registers write access requires setting the
> > > STOP bit and sending the clear prescaler instruction (CPR). STOP then
> > > needs to be released once write operation is completed.
> > > 
> > > Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > > ---
> > >  drivers/rtc/rtc-pcf2127.c | 38 +++++++++++++++++++++++++++++++++++++-
> > >  1 file changed, 37 insertions(+), 1 deletion(-)
> > > 
> > > diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> > > index e4b78b9c03f9..11fbdab6bf01 100644
> > > --- a/drivers/rtc/rtc-pcf2127.c
> > > +++ b/drivers/rtc/rtc-pcf2127.c
> > > @@ -39,6 +39,7 @@
> > >  #define PCF2127_REG_CTRL1		0x00
> > >  #define PCF2127_BIT_CTRL1_POR_OVRD		BIT(3)
> > >  #define PCF2127_BIT_CTRL1_TSF1			BIT(4)
> > > +#define PCF2127_BIT_CTRL1_STOP			BIT(5)
> > >  /* Control register 2 */
> > >  #define PCF2127_REG_CTRL2		0x01
> > >  #define PCF2127_BIT_CTRL2_AIE			BIT(1)
> > > @@ -70,6 +71,7 @@
> > >  #define PCF2131_REG_SR_RESET		0x05
> > >  #define PCF2131_SR_RESET_READ_PATTERN	0b00100100 /* Fixed pattern. */
> > >  #define PCF2131_SR_RESET_RESET_CMD	0x2C /* SR is bit 3. */
> > > +#define PCF2131_SR_RESET_CPR_CMD	0xA4 /* CPR is bit 7. */
> > >  /* Time and date registers */
> > >  #define PCF2127_REG_TIME_DATE_BASE	0x03
> > >  #define PCF2131_REG_TIME_DATE_BASE	0x07 /* Register 0x06 is 100th seconds,
> > > @@ -307,7 +309,31 @@ static int pcf2127_rtc_set_time(struct device *dev, struct rtc_time *tm)
> > >  	/* year */
> > >  	buf[i++] = bin2bcd(tm->tm_year - 100);
> > >  
> > > -	/* write register's data */
> > > +	/* Write access to time registers:
> > > +	 * PCF2127/29: no special action required.
> > > +	 * PCF2131:    requires setting the STOP bit. STOP bit needs to
> > > +	 *             be cleared after time registers are updated.
> > > +	 *             It is also recommended to set CPR bit, although
> > > +	 *             write access will work without it.
> > > +	 */
> > > +	if (pcf2127->cfg->has_reset_reg) {
> > 
> > This should probably be tied to the actual rtc model rather than the
> > presence of the reset register.
> > You MUST clear CPR to be able to set the time precisely.
> 
> In fact you must actually SET the CPR bit to clear the prescaler, confusing!
> 
> I was already setting the CPR bit (clearing prescaler), so I modified the confusing comment.
> 
> The CPR bit is only present IF the reset register is also present, that is why I simply used the presence of the reset register to take the correct action. This avoids to define a new bit or matching on a device model for that functionality (adding newer models could potentially mean modifying the model match).
> 
> But if you absolutely want to match on the model, I would like to know how you would like to practically do it (maybe an example)?
> 

You can keep pcf21xx_type around, in pcf21xx_config for example.

> 
> 
> > 
> > > +		err = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL1,
> > > +					 PCF2127_BIT_CTRL1_STOP,
> > > +					 PCF2127_BIT_CTRL1_STOP);
> > > +		if (err) {
> > > +			dev_err(dev, "setting STOP bit failed\n");
> > 
> > This really needs to be less verbose. There is nothing a user can really
> > do after having seen this message. Having an error in userspace will
> > anyway prompt the user to retry the operation which is the only action
> > it can do.
> 
> I converted the dev_err messages to dev_dbg.
> 
> In the original driver and in the same function, there is also a dev_err to handle regmap_bulk_write() failure. Do you suggest that we also make it less verbose:
> 
> err = regmap_bulk_write(pcf2127->regmap, pcf2127->cfg->reg_time_base, buf, i);
>  	if (err) {
>  		dev_err(dev,
> 
> ???

yes, you can remove it as part of your previous patches.

> 
> 
> > > +			return err;
> > > +		}
> > > +
> > > +		err = regmap_write(pcf2127->regmap, pcf2127->cfg->reg_reset,
> > > +				   PCF2131_SR_RESET_CPR_CMD);
> > > +		if (err) {
> > > +			dev_err(dev, "sending CPR cmd failed\n");
> > > +			return err;
> > > +		}
> > > +	}
> > > +
> > > +	/* write time register's data */
> > >  	err = regmap_bulk_write(pcf2127->regmap, pcf2127->cfg->regs_td_base, buf, i);
> > >  	if (err) {
> > >  		dev_err(dev,
> > > @@ -315,6 +341,16 @@ static int pcf2127_rtc_set_time(struct device *dev, struct rtc_time *tm)
> > >  		return err;
> > >  	}
> > >  
> > > +	if (pcf2127->cfg->has_reset_reg) {
> > > +		/* Clear STOP bit (PCF2131 only) after write is completed. */
> > > +		err = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL1,
> > > +					 PCF2127_BIT_CTRL1_STOP, 0);
> > > +		if (err) {
> > > +			dev_err(dev, "clearing STOP bit failed\n");
> > > +			return err;
> > > +		}
> > > +	}
> > > +
> > >  	return 0;
> > >  }
> > >  
> > > -- 
> > > 2.30.2
> > > 
> > 
> > -- 
> > Alexandre Belloni, co-owner and COO, Bootlin
> > Embedded Linux and Kernel engineering
> > https://bootlin.com
> > 
> 
> 
> -- 
> Hugo Villeneuve <hugo@hugovil.com>

-- 
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

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

* Re: [PATCH v3 08/14] rtc: pcf2127: add support for PCF2131 interrupts on output INT_A
  2023-06-21 19:26           ` Alexandre Belloni
@ 2023-06-22 14:21             ` Hugo Villeneuve
  0 siblings, 0 replies; 72+ messages in thread
From: Hugo Villeneuve @ 2023-06-22 14:21 UTC (permalink / raw)
  To: Alexandre Belloni
  Cc: a.zummo, robh+dt, krzysztof.kozlowski+dt, linux-rtc, devicetree,
	linux-kernel, Hugo Villeneuve

On Wed, 21 Jun 2023 21:26:21 +0200
Alexandre Belloni <alexandre.belloni@bootlin.com> wrote:

> On 21/06/2023 21:24:37+0200, Alexandre Belloni wrote:
> > On 11/05/2023 13:19:58-0400, Hugo Villeneuve wrote:
> > > On Mon, 23 Jan 2023 15:52:40 -0500
> > > Hugo Villeneuve <hugo@hugovil.com> wrote:
> > > 
> > > > On Fri, 20 Jan 2023 17:56:39 +0100
> > > > Alexandre Belloni <alexandre.belloni@bootlin.com> wrote:
> > > > 
> > > > > On 15/12/2022 10:02:09-0500, Hugo Villeneuve wrote:
> > > > > > From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > > > > > 
> > > > > > The PCF2127 and PCF2129 have one output interrupt pin. The PCF2131 has
> > > > > > two, named INT_A and INT_B. The hardware support that any interrupt
> > > > > > source can be routed to either one or both of them.
> > > > > > 
> > > > > > Force all interrupt sources to go to the INT A pin.
> > > > > > 
> > > > > > Support to route any interrupt source to INT A/B pins is not supported
> > > > > > by this driver at the moment.
> > > > > > 
> > > > > 
> > > > > The main issue with this is that this will created a breaking change
> > > > > once someone needs support for INTB
> > > > 
> > > > We already had a discussion about this a while ago:
> > > > 
> > > >     https://lore.kernel.org/linux-rtc/7be3f9541eaed7e17e334267e49665f442b1b458.camel@dimonoff.com/
> > > > 
> > > > What exactly do you suggest? I personnaly don't have any need for INTB at the moment and I would prefer to avoid the great complexity of supporting any combination of routing interrupts to any A ou  pins.
> > > 
> > > Hi Alexandre,
> > > a few months later, and I am still waiting for your feedback on this (and other questions/interrogations I raised for other patches related to this series) to submit the next version of this patch series.
> > > 
> > > Can you have a look at it and provide some answers?
> > > 
> > 
> > I'm very very sorry this takes so long. For this one, I don't have a
> > precise idea. I guess we could have one property per pin with a mask of
> > the interrupts we are interested in. That would cover all the use cases.
> > For example, a PMIC could take the alarms on INTB and the CPU could have
> > alarms, battery low and UIE on INTA.
> 
> As the mask for INTA and INTB are set to have interrupts on both by
> default, maybe you could keep that in a separate series so we can wait
> for the DT maintainer to give their opinion.

Hi Alexandre,
great, this will allow us to focus on closing this current series, and
I will prepare a new series specifically for handling interrupts A and
B after.

I have finished implementing all the changes requested during V3
review, and will submit V4 soon.

Thank you, Hugo.


> > > > > > Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > > > > > ---
> > > > > >  drivers/rtc/rtc-pcf2127.c | 35 +++++++++++++++++++++++++++++++++++
> > > > > >  1 file changed, 35 insertions(+)
> > > > > > 
> > > > > > diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
> > > > > > index 4148e135f935..68af4d0438b8 100644
> > > > > > --- a/drivers/rtc/rtc-pcf2127.c
> > > > > > +++ b/drivers/rtc/rtc-pcf2127.c
> > > > > > @@ -191,6 +191,7 @@ struct pcf21xx_config {
> > > > > >  	int max_register;
> > > > > >  	unsigned int has_nvmem:1;
> > > > > >  	unsigned int has_bit_wd_ctl_cd0:1;
> > > > > > +	unsigned int has_int_a_b:1; /* PCF2131 supports two interrupt outputs. */
> > > > > >  	u8 regs_td_base; /* Time/data base registers. */
> > > > > >  	u8 regs_alarm_base; /* Alarm function base registers. */
> > > > > >  	u8 reg_wd_ctl; /* Watchdog control register. */
> > > > > > @@ -879,6 +880,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
> > > > > >  		.max_register = 0x1d,
> > > > > >  		.has_nvmem = 1,
> > > > > >  		.has_bit_wd_ctl_cd0 = 1,
> > > > > > +		.has_int_a_b = 0,
> > > > > >  		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
> > > > > >  		.regs_alarm_base = PCF2127_REG_ALARM_BASE,
> > > > > >  		.reg_wd_ctl = PCF2127_REG_WD_CTL,
> > > > > > @@ -902,6 +904,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
> > > > > >  		.max_register = 0x19,
> > > > > >  		.has_nvmem = 0,
> > > > > >  		.has_bit_wd_ctl_cd0 = 0,
> > > > > > +		.has_int_a_b = 0,
> > > > > >  		.regs_td_base = PCF2127_REG_TIME_DATE_BASE,
> > > > > >  		.regs_alarm_base = PCF2127_REG_ALARM_BASE,
> > > > > >  		.reg_wd_ctl = PCF2127_REG_WD_CTL,
> > > > > > @@ -925,6 +928,7 @@ static struct pcf21xx_config pcf21xx_cfg[] = {
> > > > > >  		.max_register = 0x36,
> > > > > >  		.has_nvmem = 0,
> > > > > >  		.has_bit_wd_ctl_cd0 = 0,
> > > > > > +		.has_int_a_b = 1,
> > > > > >  		.regs_td_base = PCF2131_REG_TIME_DATE_BASE,
> > > > > >  		.regs_alarm_base = PCF2131_REG_ALARM_BASE,
> > > > > >  		.reg_wd_ctl = PCF2131_REG_WD_CTL,
> > > > > > @@ -1017,6 +1021,28 @@ static int pcf2127_enable_ts(struct device *dev, int ts_id)
> > > > > >  	return ret;
> > > > > >  }
> > > > > >  
> > > > > > +/* Route all interrupt sources to INT A pin. */
> > > > > > +static int pcf2127_configure_interrupt_pins(struct device *dev)
> > > > > > +{
> > > > > > +	struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
> > > > > > +	int ret;
> > > > > > +
> > > > > > +	/* Mask bits need to be cleared to enable corresponding
> > > > > > +	 * interrupt source.
> > > > > > +	 */
> > > > > > +	ret = regmap_write(pcf2127->regmap,
> > > > > > +			   PCF2131_REG_INT_A_MASK1, 0);
> > > > > > +	if (ret)
> > > > > > +		return ret;
> > > > > > +
> > > > > > +	ret = regmap_write(pcf2127->regmap,
> > > > > > +			   PCF2131_REG_INT_A_MASK2, 0);
> > > > > > +	if (ret)
> > > > > > +		return ret;
> > > > > > +
> > > > > > +	return ret;
> > > > > > +}
> > > > > > +
> > > > > >  static int pcf2127_probe(struct device *dev, struct regmap *regmap,
> > > > > >  			 int alarm_irq, const char *name, const struct pcf21xx_config *config)
> > > > > >  {
> > > > > > @@ -1076,6 +1102,15 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
> > > > > >  		set_bit(RTC_FEATURE_ALARM, pcf2127->rtc->features);
> > > > > >  	}
> > > > > >  
> > > > > > +	if (pcf2127->cfg->has_int_a_b) {
> > > > > > +		/* Configure int A/B pins, independently of alarm_irq. */
> > > > > > +		ret = pcf2127_configure_interrupt_pins(dev);
> > > > > > +		if (ret) {
> > > > > > +			dev_err(dev, "failed to configure interrupt pins\n");
> > > > > > +			return ret;
> > > > > > +		}
> > > > > > +	}
> > > > > > +
> > > > > >  	if (pcf2127->cfg->has_nvmem) {
> > > > > >  		struct nvmem_config nvmem_cfg = {
> > > > > >  			.priv = pcf2127,
> > > > > > -- 
> > > > > > 2.30.2
> > > > > > 
> > > > > 
> > > > > -- 
> > > > > Alexandre Belloni, co-owner and COO, Bootlin
> > > > > Embedded Linux and Kernel engineering
> > > > > https://bootlin.com
> > > > > 
> > > > 
> > > > 
> > > > -- 
> > > > Hugo Villeneuve <hugo@hugovil.com>
> > > 
> > > 
> > > -- 
> > > Hugo Villeneuve
> > 
> > -- 
> > Alexandre Belloni, co-owner and COO, Bootlin
> > Embedded Linux and Kernel engineering
> > https://bootlin.com
> 
> -- 
> Alexandre Belloni, co-owner and COO, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com
> 

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

* Re: [PATCH v3 00/14] rtc: pcf2127: add PCF2131 driver
  2023-06-21 18:28         ` Hugo Villeneuve
@ 2023-07-05 13:40           ` Hugo Villeneuve
  2023-07-07 14:16             ` Alexandre Belloni
  0 siblings, 1 reply; 72+ messages in thread
From: Hugo Villeneuve @ 2023-07-05 13:40 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: Alexandre Belloni, a.zummo, robh+dt, krzysztof.kozlowski+dt,
	linux-rtc, devicetree, linux-kernel, Hugo Villeneuve

On Wed, 21 Jun 2023 14:28:52 -0400
Hugo Villeneuve <hugo@hugovil.com> wrote:

> On Wed, 21 Jun 2023 20:14:41 +0200
> Alexandre Belloni <alexandre.belloni@bootlin.com> wrote:
> 
> > On 21/06/2023 12:59:45-0400, Hugo Villeneuve wrote:
> > > On Wed, 21 Jun 2023 10:14:29 -0400
> > > Hugo Villeneuve <hugo@hugovil.com> wrote:
> > > 
> > > > On Fri, 20 Jan 2023 20:05:07 +0100
> > > > Alexandre Belloni <alexandre.belloni@bootlin.com> wrote:
> > > > 
> > > > > Hello,
> > > > > 
> > > > > I know I've been holding off on the review of this series for a while
> > > > > and I'm sorry for that.
> > > > > 
> > > > > One of the main issue that is remaining is that the driver ends up being
> > > > > 53% bigger and generaly less efficient for no added functionality for
> > > > > the existing RTCs.
> > > > > 
> > > > > I know performance is not a concern however, having more code in the
> > > > > set/read time and irq paths means that it is more difficult to set an
> > > > > get the time precisely.
> > > > 
> > > > Hi Alexandre,
> > > > one way to keep rtc_read_time() as efficient as before, and even more
> > > > efficient by reading 7 instead of 10 registers, would be to drop reading
> > > > the CTRL3 register, which is only used to detect and display an info
> > > > message for the low battery condition. This low battery check could be
> > > > moved to an ioctl call, like it is done in the PCF8523 driver.
> > > > 
> > > > Hugo.
> > > 
> > > Hi,
> > > in fact it is already part of the ioctl, so it is even simpler...
> > > 
> > 
> > Yes, the dev_info can be removed.
> 
> Hi,
> great, I will integrate that patch to improve rtc_read_time()
> performance, and resubmit V4 soon with the requested changes mentioned
> during V3 review.
> 
> Thank you, Hugo.

Hi Alexandre,
I submitted V4 a few days ago, please let me know if everything is
in order and all comments properly addressed.

If all is good, any chance we can have that integrated into v6.5?

Thank you, Hugo.


> > > > > I guess I'll take it as a merged driver but I took a different decision
> > > > > for other RTCs.
> > > > > 
> > > > > On 15/12/2022 10:02:01-0500, Hugo Villeneuve wrote:
> > > > > > From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > > > > > 
> > > > > > Hello,
> > > > > > this patch series adds the driver for the PCF2131 real-time clock.
> > > > > > 
> > > > > > This RTC is very similar in functionality to the PCF2127/29 with the
> > > > > > following differences:
> > > > > >   -supports two new control registers at offsets 4 and 5
> > > > > >   -supports a new reset register
> > > > > >   -supports 4 tamper detection functions instead of 1
> > > > > >   -has no nvmem (like the PCF2129)
> > > > > >   -has two output interrupt pins instead of one
> > > > > >   -has 1/100th seconds capabilities (not supported in this driver)
> > > > > >   -pcf2127 has watchdog clock sources: 1/60,   1, 64 and 4096Hz
> > > > > >    pcf2131 has watchdog clock sources: 1/64, 1/4,  4 and   64Hz
> > > > > >   -watchdog value register cannot be read after being set
> > > > > > 
> > > > > > Most of the register addresses are very different, although they still
> > > > > > follow the same layout. For example, the time/date and tamper registers
> > > > > > have a different base address, but the offsets are all the same.
> > > > > > Consequently, the source code of the PCF2127 driver can be easily adapted
> > > > > > to support this new device.
> > > > > > 
> > > > > > Patches 1 to 6 modify the existing pcf2127 driver to make it more generic
> > > > > > and able to support multiple variants, like the PCF2131. This is done
> > > > > > mostly by using offsets instead of absolute hardcoded register addresses.
> > > > > > 
> > > > > > Patch 7 add actual support for the PCF2131.
> > > > > > 
> > > > > > Patch 8 configures all interrupt sources to go through the INT A pin.
> > > > > > 
> > > > > > Patch 9 changes the PWRMNG bits to be the same with the PCF2131 as they
> > > > > >       are with the PCF2127/29 (different default values).
> > > > > > 
> > > > > > Patch 10 allow to confirm PCF2131 device presence by reading the reset
> > > > > >       register fixed pattern.
> > > > > > 
> > > > > > Patch 11 adapt the time/date registers write sequence for PCF2131 (STOP and
> > > > > >       CPR bits).
> > > > > > 
> > > > > > Patch 12 add support for generic watchdog timing configuration.
> > > > > > 
> > > > > > Patch 13 add a new flag to identify if device has read support for reading
> > > > > >       watchdog register value.
> > > > > >       Since the watchdog value register cannot be read on the PCF2131 after
> > > > > >       being set, it seems that we cannot detect if watchdog timer was
> > > > > >       started by bootloader. I am not sure what is the best way to handle
> > > > > >       this situation, suggestions are welcomed.
> > > > > > 
> > > > > > Patch 14 add the dt-bindings for the PCF2131.
> > > > > > 
> > > > > > I have tested the driver using a PCF2131-ARD evaluation board connected to
> > > > > > an NXP imx8mp evaluation board:
> > > > > >   - Time get/set ok;
> > > > > >   - Alarms get/set ok
> > > > > >   - Timestamp 1 to 4 ok
> > > > > >   - IRQ alarm ok
> > > > > >   - Watchdog ok
> > > > > >   - Also tested successfully with "RTC Driver Test Example" from
> > > > > >     Documentation/rtc.txt
> > > > > > 
> > > > > > I have also tested the driver on a custom PCF2129 adapter board connected to a
> > > > > > beaglebone black.
> > > > > > 
> > > > > > Thank you.
> > > > > > 
> > > > > > Link: [v1] https://patchwork.ozlabs.org/project/rtc-linux/patch/20220125200009.900660-2-hugo@hugovil.com/
> > > > > > Link: [v2] https://patchwork.ozlabs.org/project/rtc-linux/list/?series=285734
> > > > > > 
> > > > > > Changes for V3:
> > > > > > - Rebased for kernel v6.1
> > > > > > 
> > > > > > Changes for V2:
> > > > > > - In general, fix and improvements after I have tested on real hardware
> > > > > > - Fix alarm interrupt A/B mask setting for PCF2131:
> > > > > >   PCF2131_BIT_INT_AIE must be cleared, not set, to enable interrupt.
> > > > > > - Remove low_reg validation: only check if TS interrupt flag is
> > > > > >   defined, as low_reg is defined at address 0 for PCF2127/29.
> > > > > > - Change PWRMNG value for PCF2131: default is different than PCF2127/29.
> > > > > > - Adapt time/date registers write sequence for PCF2131 (STOP and CPR bits).
> > > > > > - Map all interrupt sources to INT A pin
> > > > > > - Read and validate PCF2131 device presence from RESET register
> > > > > > - Adapt watchdog configuration for PCF2131
> > > > > > 
> > > > > > Hugo Villeneuve (14):
> > > > > >   rtc: pcf2127: add variant-specific configuration structure
> > > > > >   rtc: pcf2127: adapt for time/date registers at any offset
> > > > > >   rtc: pcf2127: adapt for alarm registers at any offset
> > > > > >   rtc: pcf2127: adapt for WD registers at any offset
> > > > > >   rtc: pcf2127: adapt for CLKOUT register at any offset
> > > > > >   rtc: pcf2127: add support for multiple TS functions
> > > > > >   rtc: pcf2127: add support for PCF2131 RTC
> > > > > >   rtc: pcf2127: add support for PCF2131 interrupts on output INT_A
> > > > > >   rtc: pcf2127: set PWRMNG value for PCF2131
> > > > > >   rtc: pcf2127: read and validate PCF2131 device signature
> > > > > >   rtc: pcf2127: adapt time/date registers write sequence for PCF2131
> > > > > >   rtc: pcf2127: support generic watchdog timing configuration
> > > > > >   rtc: pcf2127: add flag for watchdog register value read support
> > > > > >   dt-bindings: rtc: pcf2127: add PCF2131
> > > > > > 
> > > > > >  .../devicetree/bindings/rtc/nxp,pcf2127.yaml  |   4 +-
> > > > > >  drivers/rtc/Kconfig                           |   4 +-
> > > > > >  drivers/rtc/rtc-pcf2127.c                     | 939 ++++++++++++++----
> > > > > >  3 files changed, 752 insertions(+), 195 deletions(-)
> > > > > > 
> > > > > > -- 
> > > > > > 2.30.2
> > > > > > 
> > > > > 
> > > > > -- 
> > > > > Alexandre Belloni, co-owner and COO, Bootlin
> > > > > Embedded Linux and Kernel engineering
> > > > > https://bootlin.com
> > > > > 
> > 
> > -- 
> > Alexandre Belloni, co-owner and COO, Bootlin
> > Embedded Linux and Kernel engineering
> > https://bootlin.com
> > 

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

* Re: [PATCH v3 00/14] rtc: pcf2127: add PCF2131 driver
  2023-07-05 13:40           ` Hugo Villeneuve
@ 2023-07-07 14:16             ` Alexandre Belloni
  0 siblings, 0 replies; 72+ messages in thread
From: Alexandre Belloni @ 2023-07-07 14:16 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: a.zummo, robh+dt, krzysztof.kozlowski+dt, linux-rtc, devicetree,
	linux-kernel, Hugo Villeneuve

Hello,

On 05/07/2023 09:40:12-0400, Hugo Villeneuve wrote:
> On Wed, 21 Jun 2023 14:28:52 -0400
> Hugo Villeneuve <hugo@hugovil.com> wrote:
> 
> > On Wed, 21 Jun 2023 20:14:41 +0200
> > Alexandre Belloni <alexandre.belloni@bootlin.com> wrote:
> > 
> > > On 21/06/2023 12:59:45-0400, Hugo Villeneuve wrote:
> > > > On Wed, 21 Jun 2023 10:14:29 -0400
> > > > Hugo Villeneuve <hugo@hugovil.com> wrote:
> > > > 
> > > > > On Fri, 20 Jan 2023 20:05:07 +0100
> > > > > Alexandre Belloni <alexandre.belloni@bootlin.com> wrote:
> > > > > 
> > > > > > Hello,
> > > > > > 
> > > > > > I know I've been holding off on the review of this series for a while
> > > > > > and I'm sorry for that.
> > > > > > 
> > > > > > One of the main issue that is remaining is that the driver ends up being
> > > > > > 53% bigger and generaly less efficient for no added functionality for
> > > > > > the existing RTCs.
> > > > > > 
> > > > > > I know performance is not a concern however, having more code in the
> > > > > > set/read time and irq paths means that it is more difficult to set an
> > > > > > get the time precisely.
> > > > > 
> > > > > Hi Alexandre,
> > > > > one way to keep rtc_read_time() as efficient as before, and even more
> > > > > efficient by reading 7 instead of 10 registers, would be to drop reading
> > > > > the CTRL3 register, which is only used to detect and display an info
> > > > > message for the low battery condition. This low battery check could be
> > > > > moved to an ioctl call, like it is done in the PCF8523 driver.
> > > > > 
> > > > > Hugo.
> > > > 
> > > > Hi,
> > > > in fact it is already part of the ioctl, so it is even simpler...
> > > > 
> > > 
> > > Yes, the dev_info can be removed.
> > 
> > Hi,
> > great, I will integrate that patch to improve rtc_read_time()
> > performance, and resubmit V4 soon with the requested changes mentioned
> > during V3 review.
> > 
> > Thank you, Hugo.
> 
> Hi Alexandre,
> I submitted V4 a few days ago, please let me know if everything is
> in order and all comments properly addressed.
> 
> If all is good, any chance we can have that integrated into v6.5?
> 

I've seen v4, I'll review more this week end but it looks good. However,
I have to wait for 6.5-rc1 to apply it. This means it won't be in before v6.6.

> Thank you, Hugo.
> 
> 
> > > > > > I guess I'll take it as a merged driver but I took a different decision
> > > > > > for other RTCs.
> > > > > > 
> > > > > > On 15/12/2022 10:02:01-0500, Hugo Villeneuve wrote:
> > > > > > > From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
> > > > > > > 
> > > > > > > Hello,
> > > > > > > this patch series adds the driver for the PCF2131 real-time clock.
> > > > > > > 
> > > > > > > This RTC is very similar in functionality to the PCF2127/29 with the
> > > > > > > following differences:
> > > > > > >   -supports two new control registers at offsets 4 and 5
> > > > > > >   -supports a new reset register
> > > > > > >   -supports 4 tamper detection functions instead of 1
> > > > > > >   -has no nvmem (like the PCF2129)
> > > > > > >   -has two output interrupt pins instead of one
> > > > > > >   -has 1/100th seconds capabilities (not supported in this driver)
> > > > > > >   -pcf2127 has watchdog clock sources: 1/60,   1, 64 and 4096Hz
> > > > > > >    pcf2131 has watchdog clock sources: 1/64, 1/4,  4 and   64Hz
> > > > > > >   -watchdog value register cannot be read after being set
> > > > > > > 
> > > > > > > Most of the register addresses are very different, although they still
> > > > > > > follow the same layout. For example, the time/date and tamper registers
> > > > > > > have a different base address, but the offsets are all the same.
> > > > > > > Consequently, the source code of the PCF2127 driver can be easily adapted
> > > > > > > to support this new device.
> > > > > > > 
> > > > > > > Patches 1 to 6 modify the existing pcf2127 driver to make it more generic
> > > > > > > and able to support multiple variants, like the PCF2131. This is done
> > > > > > > mostly by using offsets instead of absolute hardcoded register addresses.
> > > > > > > 
> > > > > > > Patch 7 add actual support for the PCF2131.
> > > > > > > 
> > > > > > > Patch 8 configures all interrupt sources to go through the INT A pin.
> > > > > > > 
> > > > > > > Patch 9 changes the PWRMNG bits to be the same with the PCF2131 as they
> > > > > > >       are with the PCF2127/29 (different default values).
> > > > > > > 
> > > > > > > Patch 10 allow to confirm PCF2131 device presence by reading the reset
> > > > > > >       register fixed pattern.
> > > > > > > 
> > > > > > > Patch 11 adapt the time/date registers write sequence for PCF2131 (STOP and
> > > > > > >       CPR bits).
> > > > > > > 
> > > > > > > Patch 12 add support for generic watchdog timing configuration.
> > > > > > > 
> > > > > > > Patch 13 add a new flag to identify if device has read support for reading
> > > > > > >       watchdog register value.
> > > > > > >       Since the watchdog value register cannot be read on the PCF2131 after
> > > > > > >       being set, it seems that we cannot detect if watchdog timer was
> > > > > > >       started by bootloader. I am not sure what is the best way to handle
> > > > > > >       this situation, suggestions are welcomed.
> > > > > > > 
> > > > > > > Patch 14 add the dt-bindings for the PCF2131.
> > > > > > > 
> > > > > > > I have tested the driver using a PCF2131-ARD evaluation board connected to
> > > > > > > an NXP imx8mp evaluation board:
> > > > > > >   - Time get/set ok;
> > > > > > >   - Alarms get/set ok
> > > > > > >   - Timestamp 1 to 4 ok
> > > > > > >   - IRQ alarm ok
> > > > > > >   - Watchdog ok
> > > > > > >   - Also tested successfully with "RTC Driver Test Example" from
> > > > > > >     Documentation/rtc.txt
> > > > > > > 
> > > > > > > I have also tested the driver on a custom PCF2129 adapter board connected to a
> > > > > > > beaglebone black.
> > > > > > > 
> > > > > > > Thank you.
> > > > > > > 
> > > > > > > Link: [v1] https://patchwork.ozlabs.org/project/rtc-linux/patch/20220125200009.900660-2-hugo@hugovil.com/
> > > > > > > Link: [v2] https://patchwork.ozlabs.org/project/rtc-linux/list/?series=285734
> > > > > > > 
> > > > > > > Changes for V3:
> > > > > > > - Rebased for kernel v6.1
> > > > > > > 
> > > > > > > Changes for V2:
> > > > > > > - In general, fix and improvements after I have tested on real hardware
> > > > > > > - Fix alarm interrupt A/B mask setting for PCF2131:
> > > > > > >   PCF2131_BIT_INT_AIE must be cleared, not set, to enable interrupt.
> > > > > > > - Remove low_reg validation: only check if TS interrupt flag is
> > > > > > >   defined, as low_reg is defined at address 0 for PCF2127/29.
> > > > > > > - Change PWRMNG value for PCF2131: default is different than PCF2127/29.
> > > > > > > - Adapt time/date registers write sequence for PCF2131 (STOP and CPR bits).
> > > > > > > - Map all interrupt sources to INT A pin
> > > > > > > - Read and validate PCF2131 device presence from RESET register
> > > > > > > - Adapt watchdog configuration for PCF2131
> > > > > > > 
> > > > > > > Hugo Villeneuve (14):
> > > > > > >   rtc: pcf2127: add variant-specific configuration structure
> > > > > > >   rtc: pcf2127: adapt for time/date registers at any offset
> > > > > > >   rtc: pcf2127: adapt for alarm registers at any offset
> > > > > > >   rtc: pcf2127: adapt for WD registers at any offset
> > > > > > >   rtc: pcf2127: adapt for CLKOUT register at any offset
> > > > > > >   rtc: pcf2127: add support for multiple TS functions
> > > > > > >   rtc: pcf2127: add support for PCF2131 RTC
> > > > > > >   rtc: pcf2127: add support for PCF2131 interrupts on output INT_A
> > > > > > >   rtc: pcf2127: set PWRMNG value for PCF2131
> > > > > > >   rtc: pcf2127: read and validate PCF2131 device signature
> > > > > > >   rtc: pcf2127: adapt time/date registers write sequence for PCF2131
> > > > > > >   rtc: pcf2127: support generic watchdog timing configuration
> > > > > > >   rtc: pcf2127: add flag for watchdog register value read support
> > > > > > >   dt-bindings: rtc: pcf2127: add PCF2131
> > > > > > > 
> > > > > > >  .../devicetree/bindings/rtc/nxp,pcf2127.yaml  |   4 +-
> > > > > > >  drivers/rtc/Kconfig                           |   4 +-
> > > > > > >  drivers/rtc/rtc-pcf2127.c                     | 939 ++++++++++++++----
> > > > > > >  3 files changed, 752 insertions(+), 195 deletions(-)
> > > > > > > 
> > > > > > > -- 
> > > > > > > 2.30.2
> > > > > > > 
> > > > > > 
> > > > > > -- 
> > > > > > Alexandre Belloni, co-owner and COO, Bootlin
> > > > > > Embedded Linux and Kernel engineering
> > > > > > https://bootlin.com
> > > > > > 
> > > 
> > > -- 
> > > Alexandre Belloni, co-owner and COO, Bootlin
> > > Embedded Linux and Kernel engineering
> > > https://bootlin.com
> > > 

-- 
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

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

end of thread, other threads:[~2023-07-07 14:16 UTC | newest]

Thread overview: 72+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-12-15 15:02 [PATCH v3 00/14] rtc: pcf2127: add PCF2131 driver Hugo Villeneuve
2022-12-15 15:02 ` [PATCH v3 01/14] rtc: pcf2127: add variant-specific configuration structure Hugo Villeneuve
2022-12-19  9:05   ` Bruno Thomsen
2022-12-19 15:15     ` Hugo Villeneuve
2022-12-19 17:17       ` Bruno Thomsen
2022-12-19 18:30         ` Hugo Villeneuve
2023-01-07 16:52   ` Bruno Thomsen
2022-12-15 15:02 ` [PATCH v3 02/14] rtc: pcf2127: adapt for time/date registers at any offset Hugo Villeneuve
2022-12-19  9:34   ` Bruno Thomsen
2022-12-19 16:27     ` Hugo Villeneuve
2023-01-07 16:49     ` Bruno Thomsen
2023-01-20 18:47   ` Alexandre Belloni
2023-01-23 15:54     ` Hugo Villeneuve
2023-06-21 18:18       ` Alexandre Belloni
2022-12-15 15:02 ` [PATCH v3 03/14] rtc: pcf2127: adapt for alarm " Hugo Villeneuve
2023-01-07 16:57   ` Bruno Thomsen
2023-01-20 17:10   ` Alexandre Belloni
2023-01-23 16:02     ` Hugo Villeneuve
2022-12-15 15:02 ` [PATCH v3 04/14] rtc: pcf2127: adapt for WD " Hugo Villeneuve
2023-01-07 16:59   ` Bruno Thomsen
2022-12-15 15:02 ` [PATCH v3 05/14] rtc: pcf2127: adapt for CLKOUT register " Hugo Villeneuve
2023-01-07 17:01   ` Bruno Thomsen
2022-12-15 15:02 ` [PATCH v3 06/14] rtc: pcf2127: add support for multiple TS functions Hugo Villeneuve
2023-01-07 17:58   ` Bruno Thomsen
2023-01-23 20:41     ` Hugo Villeneuve
2022-12-15 15:02 ` [PATCH v3 07/14] rtc: pcf2127: add support for PCF2131 RTC Hugo Villeneuve
2023-01-07 18:15   ` Bruno Thomsen
2023-01-23 19:06     ` Hugo Villeneuve
2023-01-20 18:57   ` Alexandre Belloni
2023-01-23 17:27     ` Hugo Villeneuve
2022-12-15 15:02 ` [PATCH v3 08/14] rtc: pcf2127: add support for PCF2131 interrupts on output INT_A Hugo Villeneuve
2023-01-07 18:17   ` Bruno Thomsen
2023-01-20 16:56   ` Alexandre Belloni
2023-01-23 20:52     ` Hugo Villeneuve
2023-05-11 17:19       ` Hugo Villeneuve
2023-06-21 19:24         ` Alexandre Belloni
2023-06-21 19:26           ` Alexandre Belloni
2023-06-22 14:21             ` Hugo Villeneuve
2022-12-15 15:02 ` [PATCH v3 09/14] rtc: pcf2127: set PWRMNG value for PCF2131 Hugo Villeneuve
2023-01-07 18:36   ` Bruno Thomsen
2023-01-20 16:39     ` Alexandre Belloni
2023-01-23 22:07       ` Hugo Villeneuve
2022-12-15 15:02 ` [PATCH v3 10/14] rtc: pcf2127: read and validate PCF2131 device signature Hugo Villeneuve
2023-01-20 17:01   ` Alexandre Belloni
2023-01-23 17:31     ` Hugo Villeneuve
2022-12-15 15:02 ` [PATCH v3 11/14] rtc: pcf2127: adapt time/date registers write sequence for PCF2131 Hugo Villeneuve
2023-01-07 18:44   ` Bruno Thomsen
2023-01-23 20:55     ` Hugo Villeneuve
2023-01-20 17:09   ` Alexandre Belloni
2023-01-23 21:57     ` Hugo Villeneuve
2023-06-21 19:36       ` Alexandre Belloni
2022-12-15 15:02 ` [PATCH v3 12/14] rtc: pcf2127: support generic watchdog timing configuration Hugo Villeneuve
2023-01-18 13:23   ` Philipp Rosenberger
2023-01-19 17:48     ` Hugo Villeneuve
2023-01-20  8:06       ` Philipp Rosenberger
2023-01-20 14:44         ` Hugo Villeneuve
2022-12-15 15:02 ` [PATCH v3 13/14] rtc: pcf2127: add flag for watchdog register value read support Hugo Villeneuve
2023-01-07 18:47   ` Bruno Thomsen
2022-12-15 15:02 ` [PATCH v3 14/14] dt-bindings: rtc: pcf2127: add PCF2131 Hugo Villeneuve
2022-12-16 13:24   ` Krzysztof Kozlowski
2022-12-19  9:14   ` Bruno Thomsen
2022-12-19 16:25     ` Hugo Villeneuve
2022-12-19 17:18       ` Bruno Thomsen
2022-12-19 18:31         ` Hugo Villeneuve
2023-01-20 19:05 ` [PATCH v3 00/14] rtc: pcf2127: add PCF2131 driver Alexandre Belloni
2023-01-23 15:51   ` Hugo Villeneuve
2023-06-21 14:14   ` Hugo Villeneuve
2023-06-21 16:59     ` Hugo Villeneuve
2023-06-21 18:14       ` Alexandre Belloni
2023-06-21 18:28         ` Hugo Villeneuve
2023-07-05 13:40           ` Hugo Villeneuve
2023-07-07 14:16             ` 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).