@@ -27,6 +27,12 @@ Required SPI properties:
- spi-cs-high: DS-1302 has active high chip select line. This is
required unless inverted in hardware.
+Required properties when using GPIO lines:
+
+- gpio-ce: GPIO connected to CE pin
+- gpio-io: GPIO connected to I/O pin
+- gpio-reset: GPIO connected to SCLK pin
+
Example:
spi@901c {
@@ -44,3 +50,10 @@ spi@901c {
spi-cs-high;
};
};
+
+ rtc: ds1302 {
+ compatible = "maxim,ds1302";
+ gpio-ce = <&gpio2 6 GPIO_ACTIVE_HIGH>;
+ gpio-io = <&gpio2 7 GPIO_ACTIVE_HIGH>;
+ gpio-sclk = <&gpio2 8 GPIO_ACTIVE_HIGH>;
+ };
@@ -636,15 +636,6 @@ config RTC_DRV_M41T94
This driver can also be built as a module. If so, the module
will be called rtc-m41t94.
-config RTC_DRV_DS1302
- tristate "Dallas/Maxim DS1302"
- depends on SPI
- help
- If you say yes here you get support for the Dallas DS1302 RTC chips.
-
- This driver can also be built as a module. If so, the module
- will be called rtc-ds1302.
-
config RTC_DRV_DS1305
tristate "Dallas/Maxim DS1305/DS1306"
help
@@ -1639,6 +1630,14 @@ config RTC_DRV_XGENE
This driver can also be built as a module, if so, the module
will be called "rtc-xgene".
+config RTC_DRV_DS1302
+ tristate "Dallas/Maxim DS1302"
+ help
+ If you say yes here you get support for the Dallas DS1302 RTC chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ds1302.
+
comment "HID Sensor RTC drivers"
config RTC_DRV_HID_SENSOR_TIME
@@ -19,6 +19,10 @@
#include <linux/of.h>
#include <linux/rtc.h>
#include <linux/spi/spi.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio/consumer.h>
#define DRV_NAME "rtc-ds1302"
#define DRV_VERSION "1.0.0"
@@ -195,6 +199,16 @@ static int ds1302_probe(struct ds1302 *ds1302)
return 0;
}
+#ifdef CONFIG_OF
+static const struct of_device_id ds1302_dt_ids[] = {
+ { .compatible = "maxim,ds1302", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ds1302_dt_ids);
+#endif
+
+#if IS_ENABLED(CONFIG_SPI_MASTER)
+
static int ds1302_spi_readbyte(struct ds1302 *ds1302, u8 addr)
{
struct spi_device *spi = to_spi_device(ds1302->dev);
@@ -285,21 +299,327 @@ static int ds1302_spi_probe(struct spi_device *spi)
return ds1302_probe(ds1302);
}
-#ifdef CONFIG_OF
-static const struct of_device_id ds1302_dt_ids[] = {
- { .compatible = "maxim,ds1302", },
- { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, ds1302_dt_ids);
-#endif
-
-static struct spi_driver ds1302_driver = {
+static struct spi_driver ds1302_spi_driver = {
.driver.name = "rtc-ds1302",
.driver.of_match_table = of_match_ptr(ds1302_dt_ids),
.probe = ds1302_spi_probe,
};
-module_spi_driver(ds1302_driver);
+static int ds1302_spi_register_driver(void)
+{
+ return spi_register_driver(&ds1302_spi_driver);
+}
+
+static void ds1302_spi_unregister_driver(void)
+{
+ spi_unregister_driver(&ds1302_spi_driver);
+}
+
+#else
+
+static int ds1302_spi_register_driver(void)
+{
+ return 0;
+}
+
+static void ds1302_spi_unregister_driver(void)
+{
+}
+
+#endif
+
+/*
+ * ds1302 driver using three GPIO lines
+ *
+ * The information to implement this is gleaned from
+ * http://playground.arduino.cc/Main/DS1302
+ */
+struct ds1302_gpio {
+ struct gpio_desc *gpiod_ce;
+ struct gpio_desc *gpiod_io;
+ struct gpio_desc *gpiod_sclk;
+
+ struct ds1302 ds1302;
+};
+
+static struct ds1302_gpio *to_ds1302_gpio(struct ds1302 *ds1302)
+{
+ return container_of(ds1302, struct ds1302_gpio, ds1302);
+}
+
+static int ds1302_gpio_reset(struct ds1302 *ds1302)
+{
+ struct ds1302_gpio *gpio = to_ds1302_gpio(ds1302);
+ int ret;
+
+ ret = gpiod_direction_output(gpio->gpiod_ce, 0);
+ if (ret)
+ return ret;
+
+ ret = gpiod_direction_output(gpio->gpiod_io, 0);
+ if (ret)
+ return ret;
+
+ return gpiod_direction_output(gpio->gpiod_sclk, 0);
+}
+
+static void ds1302_gpio_chip_enable(struct ds1302 *ds1302, int enable)
+{
+ struct ds1302_gpio *gpio = to_ds1302_gpio(ds1302);
+
+ gpiod_set_value(gpio->gpiod_ce, enable);
+ if (enable) {
+ /*
+ * tCC (CE to CLK Setup): 4us
+ */
+ udelay(4);
+ } else {
+ /*
+ * tCWH (CE Inactive Time): 4us
+ */
+ udelay(4);
+ }
+}
+
+static int ds1302_gpio_sendbits(struct ds1302 *ds1302, u8 val)
+{
+ struct ds1302_gpio *gpio = to_ds1302_gpio(ds1302);
+ int i;
+
+ for (i = 0; i < 8; i++, val >>= 1) {
+ gpiod_set_value(gpio->gpiod_sclk, 0);
+ /* tCL (CLK Low Time): 1000ns */
+ udelay(1);
+
+ gpiod_set_value(gpio->gpiod_io, val & 0x1);
+ /* tDC (Data to CLK Setup): 200ns */
+ udelay(1);
+
+ gpiod_set_value(gpio->gpiod_sclk, 1);
+ /*
+ * tCH (CLK High Time): 1000ns
+ * tCDH (CLK to Data Hold): 800ns
+ */
+ udelay(1);
+ }
+
+ return 0;
+}
+
+static unsigned int ds1302_gpio_recvbits(struct ds1302 *ds1302)
+{
+ unsigned int val;
+ int i;
+ struct ds1302_gpio *gpio = to_ds1302_gpio(ds1302);
+
+ for (i = 0, val = 0; i < 8; i++) {
+ int bit;
+
+ gpiod_set_value(gpio->gpiod_sclk, 0);
+ /*
+ * tCL (CLK Low Time): 1000ns
+ * tCDD (CLK to Data Delay): 800ns
+ */
+ udelay(1);
+
+ bit = gpiod_get_value(gpio->gpiod_io);
+ if (bit < 0)
+ return bit;
+ val |= bit << i;
+
+ gpiod_set_value(gpio->gpiod_sclk, 1);
+ /* tCH (CLK High Time): 1000ns */
+ udelay(1);
+ }
+
+ return val;
+}
+
+static int ds1302_gpio_readburst(struct ds1302 *ds1302, u8 addr, u8 *buf,
+ int size)
+{
+ int i;
+ int ret;
+ struct ds1302_gpio *gpio = to_ds1302_gpio(ds1302);
+
+ ret = ds1302_gpio_reset(ds1302);
+ if (ret)
+ return ret;
+
+ ds1302_gpio_chip_enable(ds1302, true);
+
+ ds1302_gpio_sendbits(ds1302, ((addr & 0x3f) << 1) | RTC_CMD_READ);
+
+ ret = gpiod_direction_input(gpio->gpiod_io);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < size; i++) {
+ ret = ds1302_gpio_recvbits(ds1302);
+ if (ret < 0)
+ break;
+ buf[i] = ret;
+ }
+
+ ds1302_gpio_chip_enable(ds1302, false);
+
+ return ret < 0 ? ret : 0;
+}
+
+static int ds1302_gpio_readbyte(struct ds1302 *ds1302, u8 addr)
+{
+ u8 val;
+ int err;
+
+ err = ds1302_gpio_readburst(ds1302, addr, &val, 1);
+ if (err)
+ return err;
+
+ return val;
+}
+
+static int ds1302_gpio_writeburst(struct ds1302 *ds1302, u8 addr, const u8 *buf,
+ int size)
+{
+ int err;
+ int i;
+
+ err = ds1302_gpio_reset(ds1302);
+ if (err)
+ return err;
+
+ ds1302_gpio_chip_enable(ds1302, true);
+
+ ds1302_gpio_sendbits(ds1302, ((addr & 0x3f) << 1) | RTC_CMD_WRITE);
+
+ for (i = 0; i < size; i++) {
+ err = ds1302_gpio_sendbits(ds1302, buf[i]);
+ if (err)
+ break;
+ }
+
+ ds1302_gpio_chip_enable(ds1302, false);
+
+ return err;
+}
+
+static int ds1302_gpio_writebyte(struct ds1302 *ds1302, u8 addr, u8 val)
+{
+ return ds1302_gpio_writeburst(ds1302, addr, &val, 1);
+}
+
+static const struct ds1302_ops ds1302_gpio_ops = {
+ .readbyte = ds1302_gpio_readbyte,
+ .writebyte = ds1302_gpio_writebyte,
+ .readburst = ds1302_gpio_readburst,
+ .writeburst = ds1302_gpio_writeburst,
+};
+
+static struct gpio_desc *ds1302_gpiod_request(struct device *dev,
+ const char *name)
+{
+ int gpio;
+ int ret;
+
+ if (!dev->of_node)
+ return ERR_PTR(-ENODEV);
+
+ gpio = of_get_named_gpio(dev->of_node, name, 0);
+ if (!gpio_is_valid(gpio))
+ return ERR_PTR(gpio);
+
+ ret = devm_gpio_request_one(dev, gpio, 0, name);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return gpio_to_desc(gpio);
+}
+
+static struct ds1302 *ds1302_gpio_init(struct device *dev)
+{
+ struct ds1302_gpio *ds1302_gpio;
+ struct gpio_desc *gpiod;
+
+ ds1302_gpio = devm_kzalloc(dev, sizeof(*ds1302_gpio), GFP_KERNEL);
+ if (!ds1302_gpio)
+ return ERR_PTR(-ENOMEM);
+
+ ds1302_gpio->ds1302.ops = &ds1302_gpio_ops;
+ ds1302_gpio->ds1302.dev = dev;
+
+ gpiod = ds1302_gpiod_request(dev, "gpio-ce");
+ if (IS_ERR(gpiod))
+ return ERR_CAST(gpiod);
+ ds1302_gpio->gpiod_ce = gpiod;
+
+ gpiod = ds1302_gpiod_request(dev, "gpio-io");
+ if (IS_ERR(gpiod))
+ return ERR_CAST(gpiod);
+ ds1302_gpio->gpiod_io = gpiod;
+
+ gpiod = ds1302_gpiod_request(dev, "gpio-sclk");
+ if (IS_ERR(gpiod))
+ return ERR_CAST(gpiod);
+ ds1302_gpio->gpiod_sclk = gpiod;
+
+ return &ds1302_gpio->ds1302;
+}
+
+static int ds1302_gpio_probe(struct platform_device *pdev)
+ {
+ struct ds1302 *ds1302;
+
+ ds1302 = ds1302_gpio_init(&pdev->dev);
+ if (IS_ERR(ds1302))
+ return PTR_ERR(ds1302);;
+
+ return ds1302_probe(ds1302);
+}
+
+static struct platform_driver ds1302_gpio_driver = {
+ .probe = ds1302_gpio_probe,
+ .driver = {
+ .name = "rtc-ds1302",
+ .of_match_table = of_match_ptr(ds1302_dt_ids),
+ },
+};
+
+static int ds1302_gpio_register_driver(void)
+{
+ return platform_driver_register(&ds1302_gpio_driver);
+}
+
+static void ds1302_gpio_unregister_driver(void)
+{
+ return platform_driver_unregister(&ds1302_gpio_driver);
+}
+
+static int __init ds1302_init(void)
+{
+ int ret;
+
+ ret = ds1302_spi_register_driver();
+ if (ret) {
+ pr_err("Failed to register ds1302 spi driver: %d\n", ret);
+ return ret;
+ }
+
+ ret = ds1302_gpio_register_driver();
+ if (ret) {
+ pr_err("Failed to register ds1302 gpio driver: %d\n", ret);
+ ds1302_spi_unregister_driver();
+ }
+
+ return ret;
+}
+module_init(ds1302_init)
+
+static void __exit ds1302_exit(void)
+{
+ ds1302_spi_unregister_driver();
+ ds1302_gpio_unregister_driver();
+}
+module_exit(ds1302_exit)
MODULE_DESCRIPTION("Dallas DS1302 RTC driver");
MODULE_VERSION(DRV_VERSION);
This adds support control with GPIO lines connected to the DS1302 which can communicate with three wires. Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com> Cc: Sergey Yanovich <ynvich@gmail.com> Cc: Alessandro Zummo <a.zummo@towertech.it> Cc: Alexandre Belloni <alexandre.belloni@free-electrons.com> --- .../devicetree/bindings/rtc/maxim-ds1302.txt | 13 + drivers/rtc/Kconfig | 17 +- drivers/rtc/rtc-ds1302.c | 340 ++++++++++++++++++++- 3 files changed, 351 insertions(+), 19 deletions(-)