linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/3] dt-bindings: define vendor prefix for whwave, Inc.
@ 2018-11-14  9:17 zoro
  2018-11-14  9:17 ` [PATCH 2/3] rtc: sd3078: new driver zoro
  2018-11-14  9:17 ` [PATCH 3/3] dt-bindings: rtc: sd3078: add device tree documentation zoro
  0 siblings, 2 replies; 8+ messages in thread
From: zoro @ 2018-11-14  9:17 UTC (permalink / raw)
  To: alexandre.belloni
  Cc: a.zummo, mark.rutland, linux-rtc, devicetree, linux-kernel, zoro

Introduce vendor prefix for whwave, Inc.
for SD3078 rtc device.

Signed-off-by: zoro <long17.cool@163.com>
---
 .../devicetree/bindings/vendor-prefixes.txt        |    1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 4b1a2a8..7d2e9b01 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -423,6 +423,7 @@ vot	Vision Optical Technology Co., Ltd.
 wd	Western Digital Corp.
 wetek	WeTek Electronics, limited.
 wexler	Wexler
+whwave	Shenzhen whwave Electronics crop
 wi2wi	Wi2Wi, Inc.
 winbond Winbond Electronics corp.
 winstar	Winstar Display Corp.
-- 
1.7.9.5



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

* [PATCH 2/3] rtc: sd3078: new driver.
  2018-11-14  9:17 [PATCH 1/3] dt-bindings: define vendor prefix for whwave, Inc zoro
@ 2018-11-14  9:17 ` zoro
  2018-11-14  9:17 ` [PATCH 3/3] dt-bindings: rtc: sd3078: add device tree documentation zoro
  1 sibling, 0 replies; 8+ messages in thread
From: zoro @ 2018-11-14  9:17 UTC (permalink / raw)
  To: alexandre.belloni
  Cc: a.zummo, mark.rutland, linux-rtc, devicetree, linux-kernel, zoro

The sd3078 is a combination RTC and SRAM device with I2C interface.

Signed-off-by: zoro <long17.cool@163.com>
---
 MAINTAINERS              |    6 ++
 drivers/rtc/Kconfig      |    9 ++
 drivers/rtc/Makefile     |    1 +
 drivers/rtc/rtc-sd3078.c |  232 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 248 insertions(+)
 create mode 100644 drivers/rtc/rtc-sd3078.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 2f3eba4..002fcd7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -16125,6 +16125,12 @@ L:	linux-gpio@vger.kernel.org
 S:	Maintained
 F:	drivers/gpio/gpio-wcove.c
 
+WHWAVE RTC DRIVER
+M:	Zoro Li <long17.cool@163.com>
+L:	linux-rtc@vger.kernel.org
+S:	Maintained
+F:	drivers/rtc/rtc-sd3078.c
+
 WIIMOTE HID DRIVER
 M:	David Herrmann <dh.herrmann@googlemail.com>
 L:	linux-input@vger.kernel.org
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index a819ef0..5b4b1b4 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -646,6 +646,15 @@ config RTC_DRV_S5M
 	  This driver can also be built as a module. If so, the module
 	  will be called rtc-s5m.
 
+config RTC_DRV_SD3078
+    tristate "ZXW Crystal SD3078"
+    help
+      If you say yes here you get support for the ZXW Crystal
+      SD3078 RTC chips.
+
+      This driver can also be built as a module. If so, the module
+      will be called rtc-sd3078.
+
 endif # I2C
 
 comment "SPI RTC drivers"
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 290c173..f973bd0 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -148,6 +148,7 @@ obj-$(CONFIG_RTC_DRV_S3C)	+= rtc-s3c.o
 obj-$(CONFIG_RTC_DRV_S5M)	+= rtc-s5m.o
 obj-$(CONFIG_RTC_DRV_SA1100)	+= rtc-sa1100.o
 obj-$(CONFIG_RTC_DRV_SC27XX)	+= rtc-sc27xx.o
+obj-$(CONFIG_RTC_DRV_SD3078)	+= rtc-sd3078.o
 obj-$(CONFIG_RTC_DRV_SH)	+= rtc-sh.o
 obj-$(CONFIG_RTC_DRV_SIRFSOC)	+= rtc-sirfsoc.o
 obj-$(CONFIG_RTC_DRV_SNVS)	+= rtc-snvs.o
diff --git a/drivers/rtc/rtc-sd3078.c b/drivers/rtc/rtc-sd3078.c
new file mode 100644
index 0000000..9428a33
--- /dev/null
+++ b/drivers/rtc/rtc-sd3078.c
@@ -0,0 +1,232 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Real Time Clock (RTC) Driver for sd3078
+ * Copyright (C) 2018 Zoro Li
+ */
+
+#include <linux/bcd.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+
+#define SD3078_REG_SC			0x00
+#define SD3078_REG_MN			0x01
+#define SD3078_REG_HR			0x02
+#define SD3078_REG_DW			0x03
+#define SD3078_REG_DM			0x04
+#define SD3078_REG_MO			0x05
+#define SD3078_REG_YR			0x06
+
+#define SD3078_REG_CTRL1		0x0f
+#define SD3078_REG_CTRL2		0x10
+#define SD3078_REG_CTRL3		0x11
+
+#define KEY_WRITE1		0x80
+#define KEY_WRITE2		0x04
+#define KEY_WRITE3		0x80
+
+#define NUM_TIME_REGS   (SD3078_REG_YR - SD3078_REG_SC + 1)
+
+/*
+ * The sd3078 has write protection
+ * and we can choose whether or not to use it.
+ * Write protection is turned off by default.
+ */
+#define WRITE_PROTECT_EN	0
+
+struct sd3078 {
+	struct rtc_device	*rtc;
+	struct regmap		*regmap;
+};
+
+/*
+ * In order to prevent arbitrary modification of the time register,
+ * when modification of the register,
+ * the "write" bit needs to be written in a certain order.
+ * 1. set WRITE1 bit
+ * 2. set WRITE2 bit
+ * 3. set WRITE3 bit
+ */
+static void sd3078_enable_reg_write(struct sd3078 *sd3078)
+{
+	regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL2,
+			   KEY_WRITE1, KEY_WRITE1);
+	regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1,
+			   KEY_WRITE2, KEY_WRITE2);
+	regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1,
+			   KEY_WRITE3, KEY_WRITE3);
+}
+
+#if WRITE_PROTECT_EN
+/*
+ * In order to prevent arbitrary modification of the time register,
+ * we should disable the write function.
+ * when disable write,
+ * the "write" bit needs to be clear in a certain order.
+ * 1. clear WRITE2 bit
+ * 2. clear WRITE3 bit
+ * 3. clear WRITE1 bit
+ */
+static void sd3078_disable_reg_write(struct sd3078 *sd3078)
+{
+	regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1,
+			   KEY_WRITE2, 0);
+	regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1,
+			   KEY_WRITE3, 0);
+	regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL2,
+			   KEY_WRITE1, 0);
+}
+#endif
+
+static int sd3078_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	unsigned char hour;
+	unsigned char rtc_data[NUM_TIME_REGS] = {0};
+	struct i2c_client *client = to_i2c_client(dev);
+	struct sd3078 *sd3078 = i2c_get_clientdata(client);
+	int ret;
+
+	ret = regmap_bulk_read(sd3078->regmap, SD3078_REG_SC, rtc_data,
+			       NUM_TIME_REGS);
+	if (ret < 0) {
+		dev_err(dev, "reading from RTC failed with err:%d\n", ret);
+		return ret;
+	}
+
+	tm->tm_sec	= bcd2bin(rtc_data[SD3078_REG_SC] & 0x7F);
+	tm->tm_min	= bcd2bin(rtc_data[SD3078_REG_MN] & 0x7F);
+
+	/*
+	 * The sd3078 supports 12/24 hour mode.
+	 * When getting time,
+	 * we need to convert the 12 hour mode to the 24 hour mode.
+	 */
+	hour = rtc_data[SD3078_REG_HR];
+	if (hour & 0x80) /* 24H MODE */
+		tm->tm_hour = bcd2bin(rtc_data[SD3078_REG_HR] & 0x3F);
+	else if (hour & 0x20) /* 12H MODE PM */
+		tm->tm_hour = bcd2bin(rtc_data[SD3078_REG_HR] & 0x1F) + 12;
+	else /* 12H MODE AM */
+		tm->tm_hour = bcd2bin(rtc_data[SD3078_REG_HR] & 0x1F);
+
+	tm->tm_mday = bcd2bin(rtc_data[SD3078_REG_DM] & 0x3F);
+	tm->tm_wday = rtc_data[SD3078_REG_DW] & 0x07;
+	tm->tm_mon	= bcd2bin(rtc_data[SD3078_REG_MO] & 0x1F) - 1;
+	tm->tm_year = bcd2bin(rtc_data[SD3078_REG_YR]) + 100;
+
+	return 0;
+}
+
+static int sd3078_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+	unsigned char rtc_data[NUM_TIME_REGS];
+	struct i2c_client *client = to_i2c_client(dev);
+	struct sd3078 *sd3078 = i2c_get_clientdata(client);
+	int ret;
+
+	rtc_data[SD3078_REG_SC] = bin2bcd(tm->tm_sec);
+	rtc_data[SD3078_REG_MN] = bin2bcd(tm->tm_min);
+	rtc_data[SD3078_REG_HR] = bin2bcd(tm->tm_hour) | 0x80;
+	rtc_data[SD3078_REG_DM] = bin2bcd(tm->tm_mday);
+	rtc_data[SD3078_REG_DW] = tm->tm_wday & 0x07;
+	rtc_data[SD3078_REG_MO] = bin2bcd(tm->tm_mon) + 1;
+	rtc_data[SD3078_REG_YR] = bin2bcd(tm->tm_year - 100);
+
+#if WRITE_PROTECT_EN
+	sd3078_enable_reg_write(sd3078);
+#endif
+
+	ret = regmap_bulk_write(sd3078->regmap, SD3078_REG_SC, rtc_data,
+				NUM_TIME_REGS);
+	if (ret < 0) {
+		dev_err(dev, "writing to RTC failed with err:%d\n", ret);
+		return ret;
+	}
+
+#if WRITE_PROTECT_EN
+	sd3078_disable_reg_write(sd3078);
+#endif
+
+	return 0;
+}
+
+static const struct rtc_class_ops sd3078_rtc_ops = {
+	.read_time	= sd3078_rtc_read_time,
+	.set_time	= sd3078_rtc_set_time,
+};
+
+static const struct regmap_config regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = 0x11,
+};
+
+static int sd3078_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	int ret;
+	struct sd3078 *sd3078;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+		return -ENODEV;
+
+	sd3078 = devm_kzalloc(&client->dev, sizeof(*sd3078), GFP_KERNEL);
+	if (!sd3078)
+		return -ENOMEM;
+
+	sd3078->regmap = devm_regmap_init_i2c(client, &regmap_config);
+	if (IS_ERR(sd3078->regmap)) {
+		dev_err(&client->dev, "regmap allocation failed\n");
+		return PTR_ERR(sd3078->regmap);
+	}
+
+	i2c_set_clientdata(client, sd3078);
+
+	sd3078->rtc = devm_rtc_allocate_device(&client->dev);
+	if (IS_ERR(sd3078->rtc))
+		return PTR_ERR(sd3078->rtc);
+
+	sd3078->rtc->ops = &sd3078_rtc_ops;
+	sd3078->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
+	sd3078->rtc->range_max = RTC_TIMESTAMP_END_2099;
+
+	ret = rtc_register_device(sd3078->rtc);
+	if (ret) {
+		dev_err(&client->dev, "failed to register rtc device\n");
+		return ret;
+	}
+
+	sd3078_enable_reg_write(sd3078);
+
+	return 0;
+}
+
+static const struct i2c_device_id sd3078_id[] = {
+	{"sd3078", 0},
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, sd3078_id);
+
+static const struct of_device_id rtc_dt_match[] = {
+	{ .compatible = "whwave,sd3078" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, rtc_dt_match);
+
+struct i2c_driver sd3078_driver = {
+	.driver     = {
+		.name   = "sd3078",
+		.owner  = THIS_MODULE,
+		.of_match_table = of_match_ptr(rtc_dt_match),
+	},
+	.probe      = sd3078_probe,
+	.id_table   = sd3078_id,
+};
+
+module_i2c_driver(sd3078_driver);
+
+MODULE_AUTHOR("Zoro Li <long17.cool@163.com>");
+MODULE_DESCRIPTION("SD3078 RTC driver");
+MODULE_LICENSE("GPL v2");
-- 
1.7.9.5



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

* [PATCH 3/3] dt-bindings: rtc: sd3078: add device tree documentation
  2018-11-14  9:17 [PATCH 1/3] dt-bindings: define vendor prefix for whwave, Inc zoro
  2018-11-14  9:17 ` [PATCH 2/3] rtc: sd3078: new driver zoro
@ 2018-11-14  9:17 ` zoro
  1 sibling, 0 replies; 8+ messages in thread
From: zoro @ 2018-11-14  9:17 UTC (permalink / raw)
  To: alexandre.belloni
  Cc: a.zummo, mark.rutland, linux-rtc, devicetree, linux-kernel, zoro

The devicetree documentation for the SD3078 device tree
binding is added with a short example.

Signed-off-by: zoro <long17.cool@163.com>
---
 .../devicetree/bindings/rtc/rtc-sd3078.txt         |   13 +++++++++++++
 1 file changed, 13 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/rtc/rtc-sd3078.txt

diff --git a/Documentation/devicetree/bindings/rtc/rtc-sd3078.txt b/Documentation/devicetree/bindings/rtc/rtc-sd3078.txt
new file mode 100644
index 0000000..676cee8
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/rtc-sd3078.txt
@@ -0,0 +1,13 @@
+SD3078 Real Time Clock
+============================
+
+Required properties:
+- compatible: Should contain "whwave,sd3078".
+- reg: I2C address for chip.
+
+Example:
+
+sd3078: sd3078@32 {
+       compatible = "whwave,sd3078";
+       reg = <0x32>;
+};
-- 
1.7.9.5



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

* [PATCH 2/3] rtc: sd3078: new driver.
  2018-11-23 10:14 [PATCH 1/3] dt-bindings: define vendor prefix for whwave, Inc zoro
@ 2018-11-23 10:14 ` zoro
  0 siblings, 0 replies; 8+ messages in thread
From: zoro @ 2018-11-23 10:14 UTC (permalink / raw)
  To: alexandre.belloni
  Cc: a.zummo, robh, mark.rutland, linux-rtc, devicetree, linux-kernel, zoro

The sd3078 is a combination RTC and SRAM device with I2C interface.

Signed-off-by: zoro <long17.cool@163.com>
---
 MAINTAINERS              |    6 ++
 drivers/rtc/Kconfig      |    9 ++
 drivers/rtc/Makefile     |    1 +
 drivers/rtc/rtc-sd3078.c |  232 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 248 insertions(+)
 create mode 100644 drivers/rtc/rtc-sd3078.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 2f3eba4..002fcd7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -16125,6 +16125,12 @@ L:	linux-gpio@vger.kernel.org
 S:	Maintained
 F:	drivers/gpio/gpio-wcove.c
 
+WHWAVE RTC DRIVER
+M:	Zoro Li <long17.cool@163.com>
+L:	linux-rtc@vger.kernel.org
+S:	Maintained
+F:	drivers/rtc/rtc-sd3078.c
+
 WIIMOTE HID DRIVER
 M:	David Herrmann <dh.herrmann@googlemail.com>
 L:	linux-input@vger.kernel.org
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index a819ef0..5b4b1b4 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -646,6 +646,15 @@ config RTC_DRV_S5M
 	  This driver can also be built as a module. If so, the module
 	  will be called rtc-s5m.
 
+config RTC_DRV_SD3078
+    tristate "ZXW Crystal SD3078"
+    help
+      If you say yes here you get support for the ZXW Crystal
+      SD3078 RTC chips.
+
+      This driver can also be built as a module. If so, the module
+      will be called rtc-sd3078.
+
 endif # I2C
 
 comment "SPI RTC drivers"
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 290c173..f973bd0 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -148,6 +148,7 @@ obj-$(CONFIG_RTC_DRV_S3C)	+= rtc-s3c.o
 obj-$(CONFIG_RTC_DRV_S5M)	+= rtc-s5m.o
 obj-$(CONFIG_RTC_DRV_SA1100)	+= rtc-sa1100.o
 obj-$(CONFIG_RTC_DRV_SC27XX)	+= rtc-sc27xx.o
+obj-$(CONFIG_RTC_DRV_SD3078)	+= rtc-sd3078.o
 obj-$(CONFIG_RTC_DRV_SH)	+= rtc-sh.o
 obj-$(CONFIG_RTC_DRV_SIRFSOC)	+= rtc-sirfsoc.o
 obj-$(CONFIG_RTC_DRV_SNVS)	+= rtc-snvs.o
diff --git a/drivers/rtc/rtc-sd3078.c b/drivers/rtc/rtc-sd3078.c
new file mode 100644
index 0000000..9428a33
--- /dev/null
+++ b/drivers/rtc/rtc-sd3078.c
@@ -0,0 +1,232 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Real Time Clock (RTC) Driver for sd3078
+ * Copyright (C) 2018 Zoro Li
+ */
+
+#include <linux/bcd.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+
+#define SD3078_REG_SC			0x00
+#define SD3078_REG_MN			0x01
+#define SD3078_REG_HR			0x02
+#define SD3078_REG_DW			0x03
+#define SD3078_REG_DM			0x04
+#define SD3078_REG_MO			0x05
+#define SD3078_REG_YR			0x06
+
+#define SD3078_REG_CTRL1		0x0f
+#define SD3078_REG_CTRL2		0x10
+#define SD3078_REG_CTRL3		0x11
+
+#define KEY_WRITE1		0x80
+#define KEY_WRITE2		0x04
+#define KEY_WRITE3		0x80
+
+#define NUM_TIME_REGS   (SD3078_REG_YR - SD3078_REG_SC + 1)
+
+/*
+ * The sd3078 has write protection
+ * and we can choose whether or not to use it.
+ * Write protection is turned off by default.
+ */
+#define WRITE_PROTECT_EN	0
+
+struct sd3078 {
+	struct rtc_device	*rtc;
+	struct regmap		*regmap;
+};
+
+/*
+ * In order to prevent arbitrary modification of the time register,
+ * when modification of the register,
+ * the "write" bit needs to be written in a certain order.
+ * 1. set WRITE1 bit
+ * 2. set WRITE2 bit
+ * 3. set WRITE3 bit
+ */
+static void sd3078_enable_reg_write(struct sd3078 *sd3078)
+{
+	regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL2,
+			   KEY_WRITE1, KEY_WRITE1);
+	regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1,
+			   KEY_WRITE2, KEY_WRITE2);
+	regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1,
+			   KEY_WRITE3, KEY_WRITE3);
+}
+
+#if WRITE_PROTECT_EN
+/*
+ * In order to prevent arbitrary modification of the time register,
+ * we should disable the write function.
+ * when disable write,
+ * the "write" bit needs to be clear in a certain order.
+ * 1. clear WRITE2 bit
+ * 2. clear WRITE3 bit
+ * 3. clear WRITE1 bit
+ */
+static void sd3078_disable_reg_write(struct sd3078 *sd3078)
+{
+	regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1,
+			   KEY_WRITE2, 0);
+	regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1,
+			   KEY_WRITE3, 0);
+	regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL2,
+			   KEY_WRITE1, 0);
+}
+#endif
+
+static int sd3078_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	unsigned char hour;
+	unsigned char rtc_data[NUM_TIME_REGS] = {0};
+	struct i2c_client *client = to_i2c_client(dev);
+	struct sd3078 *sd3078 = i2c_get_clientdata(client);
+	int ret;
+
+	ret = regmap_bulk_read(sd3078->regmap, SD3078_REG_SC, rtc_data,
+			       NUM_TIME_REGS);
+	if (ret < 0) {
+		dev_err(dev, "reading from RTC failed with err:%d\n", ret);
+		return ret;
+	}
+
+	tm->tm_sec	= bcd2bin(rtc_data[SD3078_REG_SC] & 0x7F);
+	tm->tm_min	= bcd2bin(rtc_data[SD3078_REG_MN] & 0x7F);
+
+	/*
+	 * The sd3078 supports 12/24 hour mode.
+	 * When getting time,
+	 * we need to convert the 12 hour mode to the 24 hour mode.
+	 */
+	hour = rtc_data[SD3078_REG_HR];
+	if (hour & 0x80) /* 24H MODE */
+		tm->tm_hour = bcd2bin(rtc_data[SD3078_REG_HR] & 0x3F);
+	else if (hour & 0x20) /* 12H MODE PM */
+		tm->tm_hour = bcd2bin(rtc_data[SD3078_REG_HR] & 0x1F) + 12;
+	else /* 12H MODE AM */
+		tm->tm_hour = bcd2bin(rtc_data[SD3078_REG_HR] & 0x1F);
+
+	tm->tm_mday = bcd2bin(rtc_data[SD3078_REG_DM] & 0x3F);
+	tm->tm_wday = rtc_data[SD3078_REG_DW] & 0x07;
+	tm->tm_mon	= bcd2bin(rtc_data[SD3078_REG_MO] & 0x1F) - 1;
+	tm->tm_year = bcd2bin(rtc_data[SD3078_REG_YR]) + 100;
+
+	return 0;
+}
+
+static int sd3078_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+	unsigned char rtc_data[NUM_TIME_REGS];
+	struct i2c_client *client = to_i2c_client(dev);
+	struct sd3078 *sd3078 = i2c_get_clientdata(client);
+	int ret;
+
+	rtc_data[SD3078_REG_SC] = bin2bcd(tm->tm_sec);
+	rtc_data[SD3078_REG_MN] = bin2bcd(tm->tm_min);
+	rtc_data[SD3078_REG_HR] = bin2bcd(tm->tm_hour) | 0x80;
+	rtc_data[SD3078_REG_DM] = bin2bcd(tm->tm_mday);
+	rtc_data[SD3078_REG_DW] = tm->tm_wday & 0x07;
+	rtc_data[SD3078_REG_MO] = bin2bcd(tm->tm_mon) + 1;
+	rtc_data[SD3078_REG_YR] = bin2bcd(tm->tm_year - 100);
+
+#if WRITE_PROTECT_EN
+	sd3078_enable_reg_write(sd3078);
+#endif
+
+	ret = regmap_bulk_write(sd3078->regmap, SD3078_REG_SC, rtc_data,
+				NUM_TIME_REGS);
+	if (ret < 0) {
+		dev_err(dev, "writing to RTC failed with err:%d\n", ret);
+		return ret;
+	}
+
+#if WRITE_PROTECT_EN
+	sd3078_disable_reg_write(sd3078);
+#endif
+
+	return 0;
+}
+
+static const struct rtc_class_ops sd3078_rtc_ops = {
+	.read_time	= sd3078_rtc_read_time,
+	.set_time	= sd3078_rtc_set_time,
+};
+
+static const struct regmap_config regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = 0x11,
+};
+
+static int sd3078_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	int ret;
+	struct sd3078 *sd3078;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+		return -ENODEV;
+
+	sd3078 = devm_kzalloc(&client->dev, sizeof(*sd3078), GFP_KERNEL);
+	if (!sd3078)
+		return -ENOMEM;
+
+	sd3078->regmap = devm_regmap_init_i2c(client, &regmap_config);
+	if (IS_ERR(sd3078->regmap)) {
+		dev_err(&client->dev, "regmap allocation failed\n");
+		return PTR_ERR(sd3078->regmap);
+	}
+
+	i2c_set_clientdata(client, sd3078);
+
+	sd3078->rtc = devm_rtc_allocate_device(&client->dev);
+	if (IS_ERR(sd3078->rtc))
+		return PTR_ERR(sd3078->rtc);
+
+	sd3078->rtc->ops = &sd3078_rtc_ops;
+	sd3078->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
+	sd3078->rtc->range_max = RTC_TIMESTAMP_END_2099;
+
+	ret = rtc_register_device(sd3078->rtc);
+	if (ret) {
+		dev_err(&client->dev, "failed to register rtc device\n");
+		return ret;
+	}
+
+	sd3078_enable_reg_write(sd3078);
+
+	return 0;
+}
+
+static const struct i2c_device_id sd3078_id[] = {
+	{"sd3078", 0},
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, sd3078_id);
+
+static const struct of_device_id rtc_dt_match[] = {
+	{ .compatible = "whwave,sd3078" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, rtc_dt_match);
+
+struct i2c_driver sd3078_driver = {
+	.driver     = {
+		.name   = "sd3078",
+		.owner  = THIS_MODULE,
+		.of_match_table = of_match_ptr(rtc_dt_match),
+	},
+	.probe      = sd3078_probe,
+	.id_table   = sd3078_id,
+};
+
+module_i2c_driver(sd3078_driver);
+
+MODULE_AUTHOR("Zoro Li <long17.cool@163.com>");
+MODULE_DESCRIPTION("SD3078 RTC driver");
+MODULE_LICENSE("GPL v2");
-- 
1.7.9.5



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

* [PATCH 2/3] rtc: sd3078: new driver.
  2018-11-12 18:03 [PATCH 1/3] dt-bindings: define vendor prefix for whwave, Inc zoro
@ 2018-11-12 18:03 ` zoro
  0 siblings, 0 replies; 8+ messages in thread
From: zoro @ 2018-11-12 18:03 UTC (permalink / raw)
  To: alexandre.belloni
  Cc: a.zummo, mark.rutland, linux-rtc, devicetree, linux-kernel, zoro

The sd3078 is a combination RTC and SRAM device with I2C interface.

Signed-off-by: zoro <long17.cool@163.com>
---
 MAINTAINERS              |    6 ++
 drivers/rtc/Kconfig      |    9 ++
 drivers/rtc/Makefile     |    1 +
 drivers/rtc/rtc-sd3078.c |  232 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 248 insertions(+)
 create mode 100644 drivers/rtc/rtc-sd3078.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 2f3eba4..002fcd7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -16125,6 +16125,12 @@ L:	linux-gpio@vger.kernel.org
 S:	Maintained
 F:	drivers/gpio/gpio-wcove.c
 
+WHWAVE RTC DRIVER
+M:	Zoro Li <long17.cool@163.com>
+L:	linux-rtc@vger.kernel.org
+S:	Maintained
+F:	drivers/rtc/rtc-sd3078.c
+
 WIIMOTE HID DRIVER
 M:	David Herrmann <dh.herrmann@googlemail.com>
 L:	linux-input@vger.kernel.org
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index a819ef0..5b4b1b4 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -646,6 +646,15 @@ config RTC_DRV_S5M
 	  This driver can also be built as a module. If so, the module
 	  will be called rtc-s5m.
 
+config RTC_DRV_SD3078
+    tristate "ZXW Crystal SD3078"
+    help
+      If you say yes here you get support for the ZXW Crystal
+      SD3078 RTC chips.
+
+      This driver can also be built as a module. If so, the module
+      will be called rtc-sd3078.
+
 endif # I2C
 
 comment "SPI RTC drivers"
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 290c173..f973bd0 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -148,6 +148,7 @@ obj-$(CONFIG_RTC_DRV_S3C)	+= rtc-s3c.o
 obj-$(CONFIG_RTC_DRV_S5M)	+= rtc-s5m.o
 obj-$(CONFIG_RTC_DRV_SA1100)	+= rtc-sa1100.o
 obj-$(CONFIG_RTC_DRV_SC27XX)	+= rtc-sc27xx.o
+obj-$(CONFIG_RTC_DRV_SD3078)	+= rtc-sd3078.o
 obj-$(CONFIG_RTC_DRV_SH)	+= rtc-sh.o
 obj-$(CONFIG_RTC_DRV_SIRFSOC)	+= rtc-sirfsoc.o
 obj-$(CONFIG_RTC_DRV_SNVS)	+= rtc-snvs.o
diff --git a/drivers/rtc/rtc-sd3078.c b/drivers/rtc/rtc-sd3078.c
new file mode 100644
index 0000000..9428a33
--- /dev/null
+++ b/drivers/rtc/rtc-sd3078.c
@@ -0,0 +1,232 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Real Time Clock (RTC) Driver for sd3078
+ * Copyright (C) 2018 Zoro Li
+ */
+
+#include <linux/bcd.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+
+#define SD3078_REG_SC			0x00
+#define SD3078_REG_MN			0x01
+#define SD3078_REG_HR			0x02
+#define SD3078_REG_DW			0x03
+#define SD3078_REG_DM			0x04
+#define SD3078_REG_MO			0x05
+#define SD3078_REG_YR			0x06
+
+#define SD3078_REG_CTRL1		0x0f
+#define SD3078_REG_CTRL2		0x10
+#define SD3078_REG_CTRL3		0x11
+
+#define KEY_WRITE1		0x80
+#define KEY_WRITE2		0x04
+#define KEY_WRITE3		0x80
+
+#define NUM_TIME_REGS   (SD3078_REG_YR - SD3078_REG_SC + 1)
+
+/*
+ * The sd3078 has write protection
+ * and we can choose whether or not to use it.
+ * Write protection is turned off by default.
+ */
+#define WRITE_PROTECT_EN	0
+
+struct sd3078 {
+	struct rtc_device	*rtc;
+	struct regmap		*regmap;
+};
+
+/*
+ * In order to prevent arbitrary modification of the time register,
+ * when modification of the register,
+ * the "write" bit needs to be written in a certain order.
+ * 1. set WRITE1 bit
+ * 2. set WRITE2 bit
+ * 3. set WRITE3 bit
+ */
+static void sd3078_enable_reg_write(struct sd3078 *sd3078)
+{
+	regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL2,
+			   KEY_WRITE1, KEY_WRITE1);
+	regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1,
+			   KEY_WRITE2, KEY_WRITE2);
+	regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1,
+			   KEY_WRITE3, KEY_WRITE3);
+}
+
+#if WRITE_PROTECT_EN
+/*
+ * In order to prevent arbitrary modification of the time register,
+ * we should disable the write function.
+ * when disable write,
+ * the "write" bit needs to be clear in a certain order.
+ * 1. clear WRITE2 bit
+ * 2. clear WRITE3 bit
+ * 3. clear WRITE1 bit
+ */
+static void sd3078_disable_reg_write(struct sd3078 *sd3078)
+{
+	regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1,
+			   KEY_WRITE2, 0);
+	regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1,
+			   KEY_WRITE3, 0);
+	regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL2,
+			   KEY_WRITE1, 0);
+}
+#endif
+
+static int sd3078_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	unsigned char hour;
+	unsigned char rtc_data[NUM_TIME_REGS] = {0};
+	struct i2c_client *client = to_i2c_client(dev);
+	struct sd3078 *sd3078 = i2c_get_clientdata(client);
+	int ret;
+
+	ret = regmap_bulk_read(sd3078->regmap, SD3078_REG_SC, rtc_data,
+			       NUM_TIME_REGS);
+	if (ret < 0) {
+		dev_err(dev, "reading from RTC failed with err:%d\n", ret);
+		return ret;
+	}
+
+	tm->tm_sec	= bcd2bin(rtc_data[SD3078_REG_SC] & 0x7F);
+	tm->tm_min	= bcd2bin(rtc_data[SD3078_REG_MN] & 0x7F);
+
+	/*
+	 * The sd3078 supports 12/24 hour mode.
+	 * When getting time,
+	 * we need to convert the 12 hour mode to the 24 hour mode.
+	 */
+	hour = rtc_data[SD3078_REG_HR];
+	if (hour & 0x80) /* 24H MODE */
+		tm->tm_hour = bcd2bin(rtc_data[SD3078_REG_HR] & 0x3F);
+	else if (hour & 0x20) /* 12H MODE PM */
+		tm->tm_hour = bcd2bin(rtc_data[SD3078_REG_HR] & 0x1F) + 12;
+	else /* 12H MODE AM */
+		tm->tm_hour = bcd2bin(rtc_data[SD3078_REG_HR] & 0x1F);
+
+	tm->tm_mday = bcd2bin(rtc_data[SD3078_REG_DM] & 0x3F);
+	tm->tm_wday = rtc_data[SD3078_REG_DW] & 0x07;
+	tm->tm_mon	= bcd2bin(rtc_data[SD3078_REG_MO] & 0x1F) - 1;
+	tm->tm_year = bcd2bin(rtc_data[SD3078_REG_YR]) + 100;
+
+	return 0;
+}
+
+static int sd3078_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+	unsigned char rtc_data[NUM_TIME_REGS];
+	struct i2c_client *client = to_i2c_client(dev);
+	struct sd3078 *sd3078 = i2c_get_clientdata(client);
+	int ret;
+
+	rtc_data[SD3078_REG_SC] = bin2bcd(tm->tm_sec);
+	rtc_data[SD3078_REG_MN] = bin2bcd(tm->tm_min);
+	rtc_data[SD3078_REG_HR] = bin2bcd(tm->tm_hour) | 0x80;
+	rtc_data[SD3078_REG_DM] = bin2bcd(tm->tm_mday);
+	rtc_data[SD3078_REG_DW] = tm->tm_wday & 0x07;
+	rtc_data[SD3078_REG_MO] = bin2bcd(tm->tm_mon) + 1;
+	rtc_data[SD3078_REG_YR] = bin2bcd(tm->tm_year - 100);
+
+#if WRITE_PROTECT_EN
+	sd3078_enable_reg_write(sd3078);
+#endif
+
+	ret = regmap_bulk_write(sd3078->regmap, SD3078_REG_SC, rtc_data,
+				NUM_TIME_REGS);
+	if (ret < 0) {
+		dev_err(dev, "writing to RTC failed with err:%d\n", ret);
+		return ret;
+	}
+
+#if WRITE_PROTECT_EN
+	sd3078_disable_reg_write(sd3078);
+#endif
+
+	return 0;
+}
+
+static const struct rtc_class_ops sd3078_rtc_ops = {
+	.read_time	= sd3078_rtc_read_time,
+	.set_time	= sd3078_rtc_set_time,
+};
+
+static const struct regmap_config regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = 0x11,
+};
+
+static int sd3078_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	int ret;
+	struct sd3078 *sd3078;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+		return -ENODEV;
+
+	sd3078 = devm_kzalloc(&client->dev, sizeof(*sd3078), GFP_KERNEL);
+	if (!sd3078)
+		return -ENOMEM;
+
+	sd3078->regmap = devm_regmap_init_i2c(client, &regmap_config);
+	if (IS_ERR(sd3078->regmap)) {
+		dev_err(&client->dev, "regmap allocation failed\n");
+		return PTR_ERR(sd3078->regmap);
+	}
+
+	i2c_set_clientdata(client, sd3078);
+
+	sd3078->rtc = devm_rtc_allocate_device(&client->dev);
+	if (IS_ERR(sd3078->rtc))
+		return PTR_ERR(sd3078->rtc);
+
+	sd3078->rtc->ops = &sd3078_rtc_ops;
+	sd3078->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
+	sd3078->rtc->range_max = RTC_TIMESTAMP_END_2099;
+
+	ret = rtc_register_device(sd3078->rtc);
+	if (ret) {
+		dev_err(&client->dev, "failed to register rtc device\n");
+		return ret;
+	}
+
+	sd3078_enable_reg_write(sd3078);
+
+	return 0;
+}
+
+static const struct i2c_device_id sd3078_id[] = {
+	{"sd3078", 0},
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, sd3078_id);
+
+static const struct of_device_id rtc_dt_match[] = {
+	{ .compatible = "whwave,sd3078" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, rtc_dt_match);
+
+struct i2c_driver sd3078_driver = {
+	.driver     = {
+		.name   = "sd3078",
+		.owner  = THIS_MODULE,
+		.of_match_table = of_match_ptr(rtc_dt_match),
+	},
+	.probe      = sd3078_probe,
+	.id_table   = sd3078_id,
+};
+
+module_i2c_driver(sd3078_driver);
+
+MODULE_AUTHOR("Zoro Li <long17.cool@163.com>");
+MODULE_DESCRIPTION("SD3078 RTC driver");
+MODULE_LICENSE("GPL v2");
-- 
1.7.9.5



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

* Re: [PATCH 2/3] rtc: sd3078: new driver.
  2018-11-10  8:43 ` [PATCH 2/3] rtc: sd3078: new driver zoro
  2018-11-10 12:41   ` Alexandre Belloni
@ 2018-11-10 15:57   ` kbuild test robot
  1 sibling, 0 replies; 8+ messages in thread
From: kbuild test robot @ 2018-11-10 15:57 UTC (permalink / raw)
  To: zoro
  Cc: kbuild-all, a.zummo, alexandre.belloni, robh+dt, mark.rutland,
	linux-rtc, devicetree, linux-kernel, zoro

Hi zoro,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on abelloni/rtc-next]
[also build test WARNING on v4.20-rc1 next-20181109]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/zoro/dt-bindings-define-vendor-prefix-for-whwave-Inc/20181110-205048
base:   https://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux.git rtc-next


coccinelle warnings: (new ones prefixed by >>)

>> drivers/rtc/rtc-sd3078.c:248:1-3: WARNING: PTR_ERR_OR_ZERO can be used

Please review and possibly fold the followup patch.

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

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

* Re: [PATCH 2/3] rtc: sd3078: new driver.
  2018-11-10  8:43 ` [PATCH 2/3] rtc: sd3078: new driver zoro
@ 2018-11-10 12:41   ` Alexandre Belloni
  2018-11-10 15:57   ` kbuild test robot
  1 sibling, 0 replies; 8+ messages in thread
From: Alexandre Belloni @ 2018-11-10 12:41 UTC (permalink / raw)
  To: zoro; +Cc: a.zummo, robh+dt, mark.rutland, linux-rtc, devicetree, linux-kernel

Hello,

On 10/11/2018 16:43:52+0800, zoro wrote:
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> index 290c173..de9b670 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -176,3 +176,4 @@ obj-$(CONFIG_RTC_DRV_WM8350)	+= rtc-wm8350.o
>  obj-$(CONFIG_RTC_DRV_X1205)	+= rtc-x1205.o
>  obj-$(CONFIG_RTC_DRV_XGENE)	+= rtc-xgene.o
>  obj-$(CONFIG_RTC_DRV_ZYNQMP)	+= rtc-zynqmp.o
> +obj-$(CONFIG_RTC_DRV_SD3078)	+= rtc-sd3078.o

This list is sorted alphabetically.

> diff --git a/drivers/rtc/rtc-sd3078.c b/drivers/rtc/rtc-sd3078.c
> new file mode 100644
> index 0000000..bb24ba1
> --- /dev/null
> +++ b/drivers/rtc/rtc-sd3078.c
> @@ -0,0 +1,300 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Real Time Clock (RTC) Driver for sd3078
> + * Copyright (C) 2018 Zoro Li
> + */
> +
> +#include <linux/module.h>
> +#include <linux/i2c.h>
> +#include <linux/bcd.h>
> +#include <linux/rtc.h>
> +#include <linux/slab.h>
> +
> +#define SD3078_REG_SC			0x00
> +#define SD3078_REG_MN			0x01
> +#define SD3078_REG_HR			0x02
> +#define SD3078_REG_DW			0x03
> +#define SD3078_REG_DM			0x04
> +#define SD3078_REG_MO			0x05
> +#define SD3078_REG_YR			0x06
> +
> +#define SD3078_REG_CTRL1		0x0f
> +#define SD3078_REG_CTRL2		0x10
> +#define SD3078_REG_CTRL3		0x11
> +
> +#define KEY_WRITE1		0x80
> +#define KEY_WRITE2		0x04
> +#define KEY_WRITE3		0x80
> +
> +struct sd3078 {
> +	struct rtc_device *rtc;
> +};
> +

This structure seems useless as it contains only one member and removing
it allows to avoid the allocation in probe.

> +struct i2c_driver sd3078_driver;
> +

This is unnecessary, see below...

> +
> +static int sd3078_i2c_read_regs(struct i2c_client *client,
> +	unsigned char reg, unsigned char buf[], unsigned int len)
> +{
> +	struct i2c_adapter *adap = client->adapter;
> +	unsigned char reg_buf = reg;
> +	int ret;
> +	struct i2c_msg msgs[] = {
> +		{/* setup read ptr */
> +			.addr = client->addr,
> +			.len = 1,
> +			.buf = &reg_buf
> +		},
> +		{/* read status + date */
> +			.addr = client->addr,
> +			.flags = I2C_M_RD,
> +			.len = len,
> +			.buf = buf
> +		},
> +	};
> +
> +	ret = i2c_transfer(adap, msgs, 2);
> +	return (ret == 2) ? len : ret;
> +}
> +
> +static int sd3078_i2c_write_reg(struct i2c_client *client,
> +	unsigned char reg, unsigned char value)
> +{
> +	unsigned char data[2];
> +	int ret;
> +
> +	data[0] = reg;
> +	data[1] = value;
> +	ret = i2c_master_send(client, data, 2);
> +	return ret;
> +}
> +
> +/*
> + * In order to prevent arbitrary modification of the time register,
> + * when modification of the register,
> + * the "write" bit needs to be written in a certain order.
> + * 1. set WRITE1 bit
> + * 2. set WRITE2 bit
> + * 1. set WRITE3 bit
> + */
> +static void sd3078_enable_reg_write(struct i2c_client *client)
> +{
> +	unsigned char reg1, reg2;
> +
> +	sd3078_i2c_read_regs(client, SD3078_REG_CTRL1, &reg1, 1);
> +	sd3078_i2c_read_regs(client, SD3078_REG_CTRL2, &reg2, 1);
> +
> +	reg2 |= KEY_WRITE1;
> +	sd3078_i2c_write_reg(client, SD3078_REG_CTRL2, reg2);

You should probably use regmap instead of defining your own accessors.
This way, you have access to regmap_update_bits which would be shorter.

> +
> +	reg1 |= KEY_WRITE2;
> +	sd3078_i2c_write_reg(client, SD3078_REG_CTRL1, reg1);
> +
> +	reg1 |= KEY_WRITE3;
> +	sd3078_i2c_write_reg(client, SD3078_REG_CTRL1, reg1);
> +
> +	sd3078_i2c_read_regs(client, SD3078_REG_CTRL1, &reg1, 1);
> +	sd3078_i2c_read_regs(client, SD3078_REG_CTRL2, &reg2, 1);
> +}
> +
> +/*
> + * In order to prevent arbitrary modification of the time register,
> + * we should disable the write function.
> + * when disable write,
> + * the "write" bit needs to be clear in a certain order.
> + * 1. clear WRITE2 bit
> + * 2. clear WRITE3 bit
> + * 3. clear WRITE1 bit
> + */
> +static void sd3078_disable_reg_write(struct i2c_client *client)
> +{
> +	unsigned char reg1, reg2;
> +
> +	sd3078_i2c_read_regs(client, SD3078_REG_CTRL1, &reg1, 1);
> +	sd3078_i2c_read_regs(client, SD3078_REG_CTRL2, &reg2, 1);
> +
> +	reg1 &= ~KEY_WRITE2;
> +	sd3078_i2c_write_reg(client, SD3078_REG_CTRL1, reg1);
> +
> +	reg1 &= ~KEY_WRITE3;
> +	sd3078_i2c_write_reg(client, SD3078_REG_CTRL1, reg1);
> +
> +	reg2 &= ~KEY_WRITE1;
> +	sd3078_i2c_write_reg(client, SD3078_REG_CTRL2, reg2);
> +
> +	sd3078_i2c_read_regs(client, SD3078_REG_CTRL1, &reg1, 1);
> +	sd3078_i2c_read_regs(client, SD3078_REG_CTRL2, &reg2, 1);
> +}
> +
> +static int sd3078_get_datetime(struct i2c_client *client, struct rtc_time *tm)
> +{
> +	unsigned char buf[7] = {0};
> +	unsigned char hour;
> +
> +	sd3078_i2c_read_regs(client, SD3078_REG_SC, buf, 7);
> +
> +	tm->tm_sec	= bcd2bin(buf[SD3078_REG_SC] & 0x7F);
> +	tm->tm_min	= bcd2bin(buf[SD3078_REG_MN] & 0x7F);
> +	hour = buf[SD3078_REG_HR];
> +
> +	if (hour & 0x80) /* 24H PM */
> +		tm->tm_hour = bcd2bin(buf[SD3078_REG_HR] & 0x3F);
> +	else if (hour & 0x20) /* 12H PM */
> +		tm->tm_hour = bcd2bin(buf[SD3078_REG_HR] & 0x1F) + 12;
> +	else /* 12H AM */
> +		tm->tm_hour = bcd2bin(buf[SD3078_REG_HR] & 0x1F);
> +

Is it really useful to support 12H mode?

> +	tm->tm_mday = bcd2bin(buf[SD3078_REG_DM] & 0x3F);
> +	tm->tm_wday = buf[SD3078_REG_DW] & 0x07;
> +	/* rtc mn 1-12 */
> +	tm->tm_mon	= bcd2bin(buf[SD3078_REG_MO] & 0x1F) - 1;
> +	tm->tm_year = bcd2bin(buf[SD3078_REG_YR])+100;

spaces preferred around that '+'

> +
> +	if (rtc_valid_tm(tm) < 0)

This rtc_valid_tm call is useless because this is the first thing the
core is doing after calling the read_time callback.

> +		dev_err(&client->dev, "retrieved date/time is not valid.\n");
> +
> +	return 0;
> +}
> +
> +static int sd3078_set_datetime(struct i2c_client *client, struct rtc_time *tm)
> +{
> +	int i, err;
> +	unsigned char buf[9];
> +
> +	dev_dbg(&client->dev,
> +		"%s: secs=%d, mins=%d, hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
> +		__func__,
> +		tm->tm_sec, tm->tm_min, tm->tm_hour,
> +		tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
> +

I don't find that debug to be useful as you already know what the core
is passing to the set_time function.

> +    /* hours, minutes and seconds */
> +	buf[SD3078_REG_SC] = bin2bcd(tm->tm_sec);
> +	buf[SD3078_REG_MN] = bin2bcd(tm->tm_min);
> +	/* set 24H mode */
> +	buf[SD3078_REG_HR] = bin2bcd(tm->tm_hour) | 0x80;
> +
> +	buf[SD3078_REG_DM] = bin2bcd(tm->tm_mday);
> +
> +    /* month, 1 - 12 */
> +	buf[SD3078_REG_MO] = bin2bcd(tm->tm_mon) + 1;
> +
> +    /* year and century */
> +	buf[SD3078_REG_YR] = bin2bcd(tm->tm_year % 100);

please, don't use % 100, instead set the range of the RTC properly in
probe.

> +	buf[SD3078_REG_DW] = tm->tm_wday & 0x07;
> +
> +	sd3078_enable_reg_write(client);
> +
> +    /* write register's data */
> +	for (i = 0; i < 7; i++) {
> +		err = sd3078_i2c_write_reg(client,
> +			SD3078_REG_SC + i, buf[SD3078_REG_SC + i]);

Again, regmap would allow you to avoid this loop

> +		if (err != 2)
> +			return -EIO;
> +	}
> +
> +	sd3078_disable_reg_write(client);

I don't find it too useful to enable write protection. It is actually
harmful when trying to precisely set the RTC as this adds more latency.
But it is up to you to keep it.

> +
> +	return 0;
> +}
> +
> +
> +#ifdef CONFIG_RTC_INTF_DEV
> +static int sd3078_rtc_ioctl(struct device *dev,
> +		unsigned int cmd, unsigned long arg)
> +{
> +	switch (cmd) {
> +	default:
> +		return -ENOIOCTLCMD;
> +	}
> +}
> +#else
> +#define sd3078_rtc_ioctl NULL
> +#endif
> +

This block is totally useless.

> +static int sd3078_rtc_read_time(struct device *dev, struct rtc_time *tm)
> +{
> +	return sd3078_get_datetime(to_i2c_client(dev), tm);

Please remove the useless indirection.

> +}
> +
> +static int sd3078_rtc_set_time(struct device *dev, struct rtc_time *tm)
> +{
> +	return sd3078_set_datetime(to_i2c_client(dev), tm);

Ditto.

> +}
> +
> +static const struct rtc_class_ops sd3078_rtc_ops = {
> +	.ioctl		= sd3078_rtc_ioctl,
> +	.read_time	= sd3078_rtc_read_time,
> +	.set_time	= sd3078_rtc_set_time,
> +};
> +
> +static int sd3078_probe(struct i2c_client *client,
> +		const struct i2c_device_id *id)
> +{
> +	struct sd3078 *sd3078;
> +
> +	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
> +		return -ENODEV;
> +
> +	sd3078 = devm_kzalloc(&client->dev, sizeof(*sd3078), GFP_KERNEL);
> +	if (!sd3078)
> +		return -ENOMEM;
> +
> +	i2c_set_clientdata(client, sd3078);
> +
> +	sd3078->rtc = devm_rtc_device_register(&client->dev,
> +			sd3078_driver.driver.name,
> +			&sd3078_rtc_ops, THIS_MODULE);
> +

Please use devm_rtc_allocate_device and rtc_register_device. The name is
not needed anymore.

This also allow you to set the range. Probably something like:

rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
rtc->range_max = RTC_TIMESTAMP_END_2099;

Maybe you can go up to 2199 if your RTC has a century bit and properly
handle the leap day in 2100.

> +	if (IS_ERR(sd3078->rtc))
> +		return PTR_ERR(sd3078->rtc);
> +
> +	return 0;
> +}
> +
> +static int sd3078_remove(struct i2c_client *client)
> +{
> +	return 0;
> +}
> +

This function is useless.

> +static const struct i2c_device_id sd3078_id[] = {
> +	{"sd3078", 0},
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, sd3078_id);
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id rtc_dt_ids[] = {
> +	{ .compatible = "whwave,sd3078" },
> +	{},
> +};

This is missing MODULE_DEVICE_TABLE(of, rtc_dt_ids);

> +#endif
> +
> +struct i2c_driver sd3078_driver = {
> +	.driver     = {
> +		.name   = "sd3078",
> +		.owner  = THIS_MODULE,
> +#ifdef CONFIG_OF

This ifdef is unecessary

> +		.of_match_table = of_match_ptr(rtc_dt_ids),
> +#endif
> +	},
> +	.probe      = sd3078_probe,
> +	.remove     = sd3078_remove,
> +	.id_table   = sd3078_id,
> +};
> +
> +static int __init sd3078_init(void)
> +{
> +	return i2c_add_driver(&sd3078_driver);
> +}
> +
> +static void __exit sd3078_exit(void)
> +{
> +	i2c_del_driver(&sd3078_driver);
> +}
> +
> +module_init(sd3078_init);
> +module_exit(sd3078_exit);

Please use module_i2c_driver instead of open coding.

> +
> +MODULE_AUTHOR("Zoro Li <long17.cool@163.com>");
> +MODULE_DESCRIPTION("SD3078 RTC driver");
> +MODULE_LICENSE("GPL");

Your SPDX identifier says GPL v2 while this is GPL v2+.

> -- 
> 1.7.9.5
> 

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

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

* [PATCH 2/3] rtc: sd3078: new driver.
  2018-11-10  8:43 [PATCH 1/3] dt-bindings: define vendor prefix for whwave, Inc zoro
@ 2018-11-10  8:43 ` zoro
  2018-11-10 12:41   ` Alexandre Belloni
  2018-11-10 15:57   ` kbuild test robot
  0 siblings, 2 replies; 8+ messages in thread
From: zoro @ 2018-11-10  8:43 UTC (permalink / raw)
  To: a.zummo
  Cc: alexandre.belloni, robh+dt, mark.rutland, linux-rtc, devicetree,
	linux-kernel, zoro

The sd3078 is a combination RTC and SRAM device with I2C interface.

Signed-off-by: zoro <long17.cool@163.com>
---
 MAINTAINERS              |    6 +
 drivers/rtc/Kconfig      |    9 ++
 drivers/rtc/Makefile     |    1 +
 drivers/rtc/rtc-sd3078.c |  300 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 316 insertions(+)
 create mode 100644 drivers/rtc/rtc-sd3078.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 2f3eba4..002fcd7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -16125,6 +16125,12 @@ L:	linux-gpio@vger.kernel.org
 S:	Maintained
 F:	drivers/gpio/gpio-wcove.c
 
+WHWAVE RTC DRIVER
+M:	Zoro Li <long17.cool@163.com>
+L:	linux-rtc@vger.kernel.org
+S:	Maintained
+F:	drivers/rtc/rtc-sd3078.c
+
 WIIMOTE HID DRIVER
 M:	David Herrmann <dh.herrmann@googlemail.com>
 L:	linux-input@vger.kernel.org
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index a819ef0..5b4b1b4 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -646,6 +646,15 @@ config RTC_DRV_S5M
 	  This driver can also be built as a module. If so, the module
 	  will be called rtc-s5m.
 
+config RTC_DRV_SD3078
+    tristate "ZXW Crystal SD3078"
+    help
+      If you say yes here you get support for the ZXW Crystal
+      SD3078 RTC chips.
+
+      This driver can also be built as a module. If so, the module
+      will be called rtc-sd3078.
+
 endif # I2C
 
 comment "SPI RTC drivers"
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 290c173..de9b670 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -176,3 +176,4 @@ obj-$(CONFIG_RTC_DRV_WM8350)	+= rtc-wm8350.o
 obj-$(CONFIG_RTC_DRV_X1205)	+= rtc-x1205.o
 obj-$(CONFIG_RTC_DRV_XGENE)	+= rtc-xgene.o
 obj-$(CONFIG_RTC_DRV_ZYNQMP)	+= rtc-zynqmp.o
+obj-$(CONFIG_RTC_DRV_SD3078)	+= rtc-sd3078.o
diff --git a/drivers/rtc/rtc-sd3078.c b/drivers/rtc/rtc-sd3078.c
new file mode 100644
index 0000000..bb24ba1
--- /dev/null
+++ b/drivers/rtc/rtc-sd3078.c
@@ -0,0 +1,300 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Real Time Clock (RTC) Driver for sd3078
+ * Copyright (C) 2018 Zoro Li
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/bcd.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+
+#define SD3078_REG_SC			0x00
+#define SD3078_REG_MN			0x01
+#define SD3078_REG_HR			0x02
+#define SD3078_REG_DW			0x03
+#define SD3078_REG_DM			0x04
+#define SD3078_REG_MO			0x05
+#define SD3078_REG_YR			0x06
+
+#define SD3078_REG_CTRL1		0x0f
+#define SD3078_REG_CTRL2		0x10
+#define SD3078_REG_CTRL3		0x11
+
+#define KEY_WRITE1		0x80
+#define KEY_WRITE2		0x04
+#define KEY_WRITE3		0x80
+
+struct sd3078 {
+	struct rtc_device *rtc;
+};
+
+struct i2c_driver sd3078_driver;
+
+
+static int sd3078_i2c_read_regs(struct i2c_client *client,
+	unsigned char reg, unsigned char buf[], unsigned int len)
+{
+	struct i2c_adapter *adap = client->adapter;
+	unsigned char reg_buf = reg;
+	int ret;
+	struct i2c_msg msgs[] = {
+		{/* setup read ptr */
+			.addr = client->addr,
+			.len = 1,
+			.buf = &reg_buf
+		},
+		{/* read status + date */
+			.addr = client->addr,
+			.flags = I2C_M_RD,
+			.len = len,
+			.buf = buf
+		},
+	};
+
+	ret = i2c_transfer(adap, msgs, 2);
+	return (ret == 2) ? len : ret;
+}
+
+static int sd3078_i2c_write_reg(struct i2c_client *client,
+	unsigned char reg, unsigned char value)
+{
+	unsigned char data[2];
+	int ret;
+
+	data[0] = reg;
+	data[1] = value;
+	ret = i2c_master_send(client, data, 2);
+	return ret;
+}
+
+/*
+ * In order to prevent arbitrary modification of the time register,
+ * when modification of the register,
+ * the "write" bit needs to be written in a certain order.
+ * 1. set WRITE1 bit
+ * 2. set WRITE2 bit
+ * 1. set WRITE3 bit
+ */
+static void sd3078_enable_reg_write(struct i2c_client *client)
+{
+	unsigned char reg1, reg2;
+
+	sd3078_i2c_read_regs(client, SD3078_REG_CTRL1, &reg1, 1);
+	sd3078_i2c_read_regs(client, SD3078_REG_CTRL2, &reg2, 1);
+
+	reg2 |= KEY_WRITE1;
+	sd3078_i2c_write_reg(client, SD3078_REG_CTRL2, reg2);
+
+	reg1 |= KEY_WRITE2;
+	sd3078_i2c_write_reg(client, SD3078_REG_CTRL1, reg1);
+
+	reg1 |= KEY_WRITE3;
+	sd3078_i2c_write_reg(client, SD3078_REG_CTRL1, reg1);
+
+	sd3078_i2c_read_regs(client, SD3078_REG_CTRL1, &reg1, 1);
+	sd3078_i2c_read_regs(client, SD3078_REG_CTRL2, &reg2, 1);
+}
+
+/*
+ * In order to prevent arbitrary modification of the time register,
+ * we should disable the write function.
+ * when disable write,
+ * the "write" bit needs to be clear in a certain order.
+ * 1. clear WRITE2 bit
+ * 2. clear WRITE3 bit
+ * 3. clear WRITE1 bit
+ */
+static void sd3078_disable_reg_write(struct i2c_client *client)
+{
+	unsigned char reg1, reg2;
+
+	sd3078_i2c_read_regs(client, SD3078_REG_CTRL1, &reg1, 1);
+	sd3078_i2c_read_regs(client, SD3078_REG_CTRL2, &reg2, 1);
+
+	reg1 &= ~KEY_WRITE2;
+	sd3078_i2c_write_reg(client, SD3078_REG_CTRL1, reg1);
+
+	reg1 &= ~KEY_WRITE3;
+	sd3078_i2c_write_reg(client, SD3078_REG_CTRL1, reg1);
+
+	reg2 &= ~KEY_WRITE1;
+	sd3078_i2c_write_reg(client, SD3078_REG_CTRL2, reg2);
+
+	sd3078_i2c_read_regs(client, SD3078_REG_CTRL1, &reg1, 1);
+	sd3078_i2c_read_regs(client, SD3078_REG_CTRL2, &reg2, 1);
+}
+
+static int sd3078_get_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+	unsigned char buf[7] = {0};
+	unsigned char hour;
+
+	sd3078_i2c_read_regs(client, SD3078_REG_SC, buf, 7);
+
+	tm->tm_sec	= bcd2bin(buf[SD3078_REG_SC] & 0x7F);
+	tm->tm_min	= bcd2bin(buf[SD3078_REG_MN] & 0x7F);
+	hour = buf[SD3078_REG_HR];
+
+	if (hour & 0x80) /* 24H PM */
+		tm->tm_hour = bcd2bin(buf[SD3078_REG_HR] & 0x3F);
+	else if (hour & 0x20) /* 12H PM */
+		tm->tm_hour = bcd2bin(buf[SD3078_REG_HR] & 0x1F) + 12;
+	else /* 12H AM */
+		tm->tm_hour = bcd2bin(buf[SD3078_REG_HR] & 0x1F);
+
+	tm->tm_mday = bcd2bin(buf[SD3078_REG_DM] & 0x3F);
+	tm->tm_wday = buf[SD3078_REG_DW] & 0x07;
+	/* rtc mn 1-12 */
+	tm->tm_mon	= bcd2bin(buf[SD3078_REG_MO] & 0x1F) - 1;
+	tm->tm_year = bcd2bin(buf[SD3078_REG_YR])+100;
+
+	if (rtc_valid_tm(tm) < 0)
+		dev_err(&client->dev, "retrieved date/time is not valid.\n");
+
+	return 0;
+}
+
+static int sd3078_set_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+	int i, err;
+	unsigned char buf[9];
+
+	dev_dbg(&client->dev,
+		"%s: secs=%d, mins=%d, hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
+		__func__,
+		tm->tm_sec, tm->tm_min, tm->tm_hour,
+		tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+    /* hours, minutes and seconds */
+	buf[SD3078_REG_SC] = bin2bcd(tm->tm_sec);
+	buf[SD3078_REG_MN] = bin2bcd(tm->tm_min);
+	/* set 24H mode */
+	buf[SD3078_REG_HR] = bin2bcd(tm->tm_hour) | 0x80;
+
+	buf[SD3078_REG_DM] = bin2bcd(tm->tm_mday);
+
+    /* month, 1 - 12 */
+	buf[SD3078_REG_MO] = bin2bcd(tm->tm_mon) + 1;
+
+    /* year and century */
+	buf[SD3078_REG_YR] = bin2bcd(tm->tm_year % 100);
+	buf[SD3078_REG_DW] = tm->tm_wday & 0x07;
+
+	sd3078_enable_reg_write(client);
+
+    /* write register's data */
+	for (i = 0; i < 7; i++) {
+		err = sd3078_i2c_write_reg(client,
+			SD3078_REG_SC + i, buf[SD3078_REG_SC + i]);
+		if (err != 2)
+			return -EIO;
+	}
+
+	sd3078_disable_reg_write(client);
+
+	return 0;
+}
+
+
+#ifdef CONFIG_RTC_INTF_DEV
+static int sd3078_rtc_ioctl(struct device *dev,
+		unsigned int cmd, unsigned long arg)
+{
+	switch (cmd) {
+	default:
+		return -ENOIOCTLCMD;
+	}
+}
+#else
+#define sd3078_rtc_ioctl NULL
+#endif
+
+static int sd3078_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	return sd3078_get_datetime(to_i2c_client(dev), tm);
+}
+
+static int sd3078_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+	return sd3078_set_datetime(to_i2c_client(dev), tm);
+}
+
+static const struct rtc_class_ops sd3078_rtc_ops = {
+	.ioctl		= sd3078_rtc_ioctl,
+	.read_time	= sd3078_rtc_read_time,
+	.set_time	= sd3078_rtc_set_time,
+};
+
+static int sd3078_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	struct sd3078 *sd3078;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+		return -ENODEV;
+
+	sd3078 = devm_kzalloc(&client->dev, sizeof(*sd3078), GFP_KERNEL);
+	if (!sd3078)
+		return -ENOMEM;
+
+	i2c_set_clientdata(client, sd3078);
+
+	sd3078->rtc = devm_rtc_device_register(&client->dev,
+			sd3078_driver.driver.name,
+			&sd3078_rtc_ops, THIS_MODULE);
+
+	if (IS_ERR(sd3078->rtc))
+		return PTR_ERR(sd3078->rtc);
+
+	return 0;
+}
+
+static int sd3078_remove(struct i2c_client *client)
+{
+	return 0;
+}
+
+static const struct i2c_device_id sd3078_id[] = {
+	{"sd3078", 0},
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, sd3078_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id rtc_dt_ids[] = {
+	{ .compatible = "whwave,sd3078" },
+	{},
+};
+#endif
+
+struct i2c_driver sd3078_driver = {
+	.driver     = {
+		.name   = "sd3078",
+		.owner  = THIS_MODULE,
+#ifdef CONFIG_OF
+		.of_match_table = of_match_ptr(rtc_dt_ids),
+#endif
+	},
+	.probe      = sd3078_probe,
+	.remove     = sd3078_remove,
+	.id_table   = sd3078_id,
+};
+
+static int __init sd3078_init(void)
+{
+	return i2c_add_driver(&sd3078_driver);
+}
+
+static void __exit sd3078_exit(void)
+{
+	i2c_del_driver(&sd3078_driver);
+}
+
+module_init(sd3078_init);
+module_exit(sd3078_exit);
+
+MODULE_AUTHOR("Zoro Li <long17.cool@163.com>");
+MODULE_DESCRIPTION("SD3078 RTC driver");
+MODULE_LICENSE("GPL");
-- 
1.7.9.5


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

end of thread, other threads:[~2018-11-23 10:15 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-11-14  9:17 [PATCH 1/3] dt-bindings: define vendor prefix for whwave, Inc zoro
2018-11-14  9:17 ` [PATCH 2/3] rtc: sd3078: new driver zoro
2018-11-14  9:17 ` [PATCH 3/3] dt-bindings: rtc: sd3078: add device tree documentation zoro
  -- strict thread matches above, loose matches on Subject: below --
2018-11-23 10:14 [PATCH 1/3] dt-bindings: define vendor prefix for whwave, Inc zoro
2018-11-23 10:14 ` [PATCH 2/3] rtc: sd3078: new driver zoro
2018-11-12 18:03 [PATCH 1/3] dt-bindings: define vendor prefix for whwave, Inc zoro
2018-11-12 18:03 ` [PATCH 2/3] rtc: sd3078: new driver zoro
2018-11-10  8:43 [PATCH 1/3] dt-bindings: define vendor prefix for whwave, Inc zoro
2018-11-10  8:43 ` [PATCH 2/3] rtc: sd3078: new driver zoro
2018-11-10 12:41   ` Alexandre Belloni
2018-11-10 15:57   ` kbuild test robot

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).