From patchwork Sun Apr 10 14:59:26 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Akinobu Mita X-Patchwork-Id: 608529 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from mail-ig0-x237.google.com (mail-ig0-x237.google.com [IPv6:2607:f8b0:4001:c05::237]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3qjbvw19llz9snm for ; Mon, 11 Apr 2016 00:59:56 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=googlegroups.com header.i=@googlegroups.com header.b=cqolLv1C; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b=d3Oag+/d; dkim-atps=neutral Received: by mail-ig0-x237.google.com with SMTP id gy3sf8262780igb.1 for ; Sun, 10 Apr 2016 07:59:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlegroups.com; s=20120806; h=sender:mime-version:from:to:cc:subject:date:message-id:in-reply-to :references:x-original-sender:x-original-authentication-results :reply-to:precedence:mailing-list:list-id:x-spam-checked-in-group :list-post:list-help:list-archive:list-subscribe:list-unsubscribe; bh=zq05IaxTFd/cfGE8cTyEDygq5sg3kZSHqex/Ls1ANJo=; b=cqolLv1CzhZLIYy0lNzKkvjb4+U7ssGLo+TqoC4mZKiSFtddyoyshJxjZCkSLJDgEg 9GMGic4EEisRmWm39V/kSKin2tMvjeyzBPwvPjKcG+3vJ3An2K0HtsQmGSqBPjcIUjid Ve6+bpCH1EPjPDiP5+KN8fChC8v6vNTPJdHlFHeG/Z72MKPpId5PXP3naZwvRWxMrM0i 54TYkdg19dCGqsrveriWQmnmCqGMcB487MSMAJn4lFXO26u4uMZQmRHKrfvPCqinErQd GxOozeYXOaqHvvhvS+xDZgwkcote5gS9uRlyT/RzsiRGODOWh61Y7kmiiJy9aJiDHBzO nE6g== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:from:to:cc:subject:date:message-id:in-reply-to :references:x-original-sender:x-original-authentication-results :reply-to:precedence:mailing-list:list-id:x-spam-checked-in-group :list-post:list-help:list-archive:list-subscribe:list-unsubscribe; bh=zq05IaxTFd/cfGE8cTyEDygq5sg3kZSHqex/Ls1ANJo=; b=d3Oag+/dleM9kxp0mCTDqiN8+j0sCnEx5VccbK435xYDzOSHwl3jcJRN/1isWz2dmE fVh7TJaPnvizoGMklUonNJ7bQ9GTHgIxndzsi771aCVIFp9ESmsEnbWqX9D72l6TpO2O sTzaNnNNX5fkI1m6TmPMzI6zn5Xd2lsZiSLCZKVr5BcdzmLcNoe3SsXCEbz9rt+maN1O WzCr7C900T00v+jvzAu2O8sFhPFzqJT+OKNhaQspVtMIOlMxGT0TwQufdEDDLhprQELv BGTMwW9ECN/rtL/HrD5ppMLFKrzz/j/A/D8UlHUamnwwSvUEkARiIeJumi0T9BHaeSYY z38w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=sender:x-gm-message-state:mime-version:from:to:cc:subject:date :message-id:in-reply-to:references:x-original-sender :x-original-authentication-results:reply-to:precedence:mailing-list :list-id:x-spam-checked-in-group:list-post:list-help:list-archive :list-subscribe:list-unsubscribe; bh=zq05IaxTFd/cfGE8cTyEDygq5sg3kZSHqex/Ls1ANJo=; b=XmzXQ9cyfM0eF/WQq01az1BJt3UIS8EP1pN9oA19xzVG5UHs4hOt08kjwEuQcd7qAR azLC67Vrxp+v9aLgUuLi23n2qISpMi1QyhxVPK6K8bSDs6kJIQd1LPKfYZycdiDN1NJy oVGSH5WLU2Qb/i+iIe09aBWtvYsUxmy/SFwooAgZ6lVkUuui1l6dePAdymcmN/GtVNKK mDYcLePVcfwAvqAH3JG880rmLpJhMce8mQP1aO1AeokVhK3uXC9RcaDSMaA8Zpuf4AzV czf9DPoHdUhI56/k1Tbg98SbtCC3HvtmMYNLJH2wVhVqBBhhsaiYT+TDXg7pj+xhWbVV /TVQ== Sender: rtc-linux@googlegroups.com X-Gm-Message-State: AD7BkJKKeyQNnVMJZPdE7LZ479TxW01eqr9zm1eeBrjsxZ3CDsoCRs+lFHA8uWSwzy+kHA== X-Received: by 10.140.102.230 with SMTP id w93mr486294qge.11.1460300394239; Sun, 10 Apr 2016 07:59:54 -0700 (PDT) MIME-Version: 1.0 X-BeenThere: rtc-linux@googlegroups.com Received: by 10.140.17.33 with SMTP id 30ls3366272qgc.99.gmail; Sun, 10 Apr 2016 07:59:54 -0700 (PDT) X-Received: by 10.159.34.111 with SMTP id 102mr12031243uad.4.1460300393980; Sun, 10 Apr 2016 07:59:53 -0700 (PDT) Received: from mail-pf0-x241.google.com (mail-pf0-x241.google.com. [2607:f8b0:400e:c00::241]) by gmr-mx.google.com with ESMTPS id h69si1022020pfj.0.2016.04.10.07.59.53 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 10 Apr 2016 07:59:53 -0700 (PDT) Received-SPF: pass (google.com: domain of akinobu.mita@gmail.com designates 2607:f8b0:400e:c00::241 as permitted sender) client-ip=2607:f8b0:400e:c00::241; Received: by mail-pf0-x241.google.com with SMTP id r187so13165182pfr.2 for ; Sun, 10 Apr 2016 07:59:53 -0700 (PDT) X-Received: by 10.98.86.28 with SMTP id k28mr26525917pfb.43.1460300393741; Sun, 10 Apr 2016 07:59:53 -0700 (PDT) Received: from localhost.localdomain (KD113159139091.ppp-bb.dion.ne.jp. [113.159.139.91]) by smtp.gmail.com with ESMTPSA id lq10sm30302002pab.36.2016.04.10.07.59.51 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sun, 10 Apr 2016 07:59:52 -0700 (PDT) From: Akinobu Mita To: rtc-linux@googlegroups.com, devicetree@vger.kernel.org Cc: Akinobu Mita , Sergey Yanovich , Alessandro Zummo , Alexandre Belloni Subject: [rtc-linux] [PATCH 4/4] rtc: ds1302: support control with using GPIO lines Date: Sun, 10 Apr 2016 23:59:26 +0900 Message-Id: <1460300366-25248-5-git-send-email-akinobu.mita@gmail.com> X-Mailer: git-send-email 2.5.0 In-Reply-To: <1460300366-25248-1-git-send-email-akinobu.mita@gmail.com> References: <1460300366-25248-1-git-send-email-akinobu.mita@gmail.com> X-Original-Sender: Akinobu.Mita@gmail.com X-Original-Authentication-Results: gmr-mx.google.com; dkim=pass header.i=@gmail.com; spf=pass (google.com: domain of akinobu.mita@gmail.com designates 2607:f8b0:400e:c00::241 as permitted sender) smtp.mailfrom=akinobu.mita@gmail.com; dmarc=pass (p=NONE dis=NONE) header.from=gmail.com Reply-To: rtc-linux@googlegroups.com Precedence: list Mailing-list: list rtc-linux@googlegroups.com; contact rtc-linux+owners@googlegroups.com List-ID: X-Spam-Checked-In-Group: rtc-linux@googlegroups.com X-Google-Group-Id: 712029733259 List-Post: , List-Help: , List-Archive: , List-Unsubscribe: , This adds support control with GPIO lines connected to the DS1302 which can communicate with three wires. Signed-off-by: Akinobu Mita Cc: Sergey Yanovich Cc: Alessandro Zummo Cc: Alexandre Belloni --- .../devicetree/bindings/rtc/maxim-ds1302.txt | 13 + drivers/rtc/Kconfig | 17 +- drivers/rtc/rtc-ds1302.c | 340 ++++++++++++++++++++- 3 files changed, 351 insertions(+), 19 deletions(-) diff --git a/Documentation/devicetree/bindings/rtc/maxim-ds1302.txt b/Documentation/devicetree/bindings/rtc/maxim-ds1302.txt index ba470c5..d489753 100644 --- a/Documentation/devicetree/bindings/rtc/maxim-ds1302.txt +++ b/Documentation/devicetree/bindings/rtc/maxim-ds1302.txt @@ -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>; + }; diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index a86a562..734dc1b 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -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 diff --git a/drivers/rtc/rtc-ds1302.c b/drivers/rtc/rtc-ds1302.c index 1647848..778f39a 100644 --- a/drivers/rtc/rtc-ds1302.c +++ b/drivers/rtc/rtc-ds1302.c @@ -19,6 +19,10 @@ #include #include #include +#include +#include +#include +#include #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);