From patchwork Sun Apr 10 14:59:25 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Akinobu Mita X-Patchwork-Id: 608528 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from mail-yw0-x23b.google.com (mail-yw0-x23b.google.com [IPv6:2607:f8b0:4002:c05::23b]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3qjbvs4N7tz9snm for ; Mon, 11 Apr 2016 00:59:53 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=googlegroups.com header.i=@googlegroups.com header.b=dDvrj4z0; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b=PikB4Ozb; dkim-atps=neutral Received: by mail-yw0-x23b.google.com with SMTP id k197sf46047242ywe.0 for ; Sun, 10 Apr 2016 07:59:53 -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=iL0FN92lfEQGZfeLLo/4ucNbR9uJYzjR0N0evVQE/Vw=; b=dDvrj4z0Kyumv7DPWDb5J26TMlKr+rVyJ4dGMRmgI23ZvtOh2qB8bh57s2FEyykCfR 68bBcEwJy5dk128lyMZH0a9ONPaQB2xYmo0Y3NY/ijGwO0sMWLhDdEAJ4RvJxWly7s88 9SJjtmxjeRW08nAxq5kdNUSkc3tVn1V3Lsfp1Ou//jq/7ygPVDuKmhTBEyNzPf7zAFcA 8eLTNqqjE574pHhqWknWXm8aCCuctbvrmacCWMOKCpsmJWszbQ8k5WeIQpLmE5QHg8SX EGXtOOp9YIu6WjHF4io2rsLfw8idJP0DV/auluf1J27KzrOOZbm3cZ/jKcXma9IQFxtN IMRA== 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=iL0FN92lfEQGZfeLLo/4ucNbR9uJYzjR0N0evVQE/Vw=; b=PikB4OzbWla+oalO93ciZDIcCbvmn5asnF100Sd4RISkV55yY3xYmsMXfemBCjH7uv F5dr9yJg0duzZxUWSzYWb35DCiqqS6Z5M19NMtWOAaNoFR+UOc+8cBOrt0qZhDYnMwLz lE29BDtMKp2bDXOELctwEp94aOdJjY+6sPsL4NPUbVqgx8eetKUk4dTcypDcmPzCHX2P aE5Y/s/yvEVZGMw9oZcRCCEYTCGFfMBXFvKdBr9aFZ8TvubgJGMlqo4/vwSVj843J/x6 Us/itTj1R4bek0/adKxSiXQI8l7fzn43m6cWdr4ud8u0ZsA/oGSFHoofIOOGscfNqX0Q X1xw== 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=iL0FN92lfEQGZfeLLo/4ucNbR9uJYzjR0N0evVQE/Vw=; b=C73a03sW8XTuD8plVblxc4RZN1sEqbbDHcfx7YblOwVAMy2nnXh/MXOhtyT30LZ0TI depn0hHiIb+YEbU0IWPAutSEOWjyGqU9CF34XNm+hL0F0c6r7bzZdqr1p+LmaMbyCr6P IcLuI6rO97yIKFXWRNl1IwLvW/M15EfiXpC84GfzZF2PA9+SEckHQm5aznKS/AmyBYqM EKJc4RbG4cmxwBE/ozkENyQH2nXwUgVHBhXPngFHJA48vbY5R4n7wZFHs+6Ly/RE1pMs 9laaEhg6ESMCZ16ODQMZFYrsbjTDcNVQqBVvOlLsEela1u01cBqQemdUccBrg0IDC2z7 8b0A== Sender: rtc-linux@googlegroups.com X-Gm-Message-State: AD7BkJKR+F9kFqXiMDRN87z0QCPwRi1zc97HOmeabSYJ1nl1rfR5WS9BoLYURswSEstVbA== X-Received: by 10.50.86.134 with SMTP id p6mr240223igz.9.1460300391452; Sun, 10 Apr 2016 07:59:51 -0700 (PDT) MIME-Version: 1.0 X-BeenThere: rtc-linux@googlegroups.com Received: by 10.50.142.7 with SMTP id rs7ls639631igb.14.gmail; Sun, 10 Apr 2016 07:59:51 -0700 (PDT) X-Received: by 10.66.149.9 with SMTP id tw9mr12003217pab.30.1460300391148; Sun, 10 Apr 2016 07:59:51 -0700 (PDT) Received: from mail-pa0-x241.google.com (mail-pa0-x241.google.com. [2607:f8b0:400e:c03::241]) by gmr-mx.google.com with ESMTPS id tu5si1175898pab.2.2016.04.10.07.59.51 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 10 Apr 2016 07:59:51 -0700 (PDT) Received-SPF: pass (google.com: domain of akinobu.mita@gmail.com designates 2607:f8b0:400e:c03::241 as permitted sender) client-ip=2607:f8b0:400e:c03::241; Received: by mail-pa0-x241.google.com with SMTP id k3so8836924pav.3 for ; Sun, 10 Apr 2016 07:59:51 -0700 (PDT) X-Received: by 10.66.235.129 with SMTP id um1mr26379805pac.17.1460300390967; Sun, 10 Apr 2016 07:59:50 -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.48 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sun, 10 Apr 2016 07:59:50 -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 3/4] rtc: ds1302: add register access abstraction layer Date: Sun, 10 Apr 2016 23:59:25 +0900 Message-Id: <1460300366-25248-4-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:c03::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: , The rtc-ds1302 driver now implemented using SPI 3wire mode. But I would like to access it with using three wires connected to GPIO lines. This adds abstraction layer for DS1302 register access in order to prepare to support for using GPIO lines. This enables to share common code between SPI driver and GPIO driver. Signed-off-by: Akinobu Mita Cc: Sergey Yanovich Cc: Alessandro Zummo Cc: Alexandre Belloni --- drivers/rtc/rtc-ds1302.c | 224 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 155 insertions(+), 69 deletions(-) diff --git a/drivers/rtc/rtc-ds1302.c b/drivers/rtc/rtc-ds1302.c index 454248f..1647848 100644 --- a/drivers/rtc/rtc-ds1302.c +++ b/drivers/rtc/rtc-ds1302.c @@ -7,6 +7,8 @@ * This file is subject to the terms and conditions of the GNU General Public * License version 2. See the file "COPYING" in the main directory of * this archive for more details. + * + * Datasheet: https://datasheets.maximintegrated.com/en/ds/DS1302.pdf */ #include @@ -40,27 +42,58 @@ #define RTC_ADDR_MIN 0x01 /* Address of minute register */ #define RTC_ADDR_SEC 0x00 /* Address of second register */ +struct ds1302 { + const struct ds1302_ops *ops; + struct device *dev; +}; + +struct ds1302_ops { + int (*readbyte)(struct ds1302 *, u8); + int (*writebyte)(struct ds1302 *, u8, u8); + int (*readburst)(struct ds1302 *, u8, u8 *, int); + int (*writeburst)(struct ds1302 *, u8, const u8 *, int); +}; + +static int ds1302_readbyte(struct ds1302 *ds1302, u8 addr) +{ + return ds1302->ops->readbyte(ds1302, addr); +} + +static int ds1302_writebyte(struct ds1302 *ds1302, u8 addr, u8 val) +{ + return ds1302->ops->writebyte(ds1302, addr, val); +} + +static int ds1302_readburst(struct ds1302 *ds1302, u8 addr, u8 *buf, int size) +{ + if (addr != RTC_CLCK_BURST) + return -EINVAL; + + return ds1302->ops->readburst(ds1302, addr, buf, size); +} + +static int ds1302_writeburst(struct ds1302 *ds1302, u8 addr, const u8 *buf, + int size) +{ + if (addr != RTC_CLCK_BURST) + return -EINVAL; + + return ds1302->ops->writeburst(ds1302, addr, buf, size); +} + static int ds1302_rtc_set_time(struct device *dev, struct rtc_time *time) { - struct spi_device *spi = dev_get_drvdata(dev); - u8 buf[1 + RTC_CLCK_LEN]; + struct ds1302 *ds1302 = dev_get_drvdata(dev); + u8 buf[RTC_CLCK_LEN]; u8 *bp = buf; int status; /* Enable writing */ - bp = buf; - *bp++ = RTC_ADDR_CTRL << 1 | RTC_CMD_WRITE; - *bp++ = RTC_CMD_WRITE_ENABLE; - - status = spi_write_then_read(spi, buf, 2, - NULL, 0); + status = ds1302_writebyte(ds1302, RTC_ADDR_CTRL, RTC_CMD_WRITE_ENABLE); if (status) return status; /* Write registers starting at the first time/date address. */ - bp = buf; - *bp++ = RTC_CLCK_BURST << 1 | RTC_CMD_WRITE; - *bp++ = bin2bcd(time->tm_sec); *bp++ = bin2bcd(time->tm_min); *bp++ = bin2bcd(time->tm_hour); @@ -70,23 +103,16 @@ static int ds1302_rtc_set_time(struct device *dev, struct rtc_time *time) *bp++ = bin2bcd(time->tm_year % 100); *bp++ = RTC_CMD_WRITE_DISABLE; - /* use write-then-read since dma from stack is nonportable */ - return spi_write_then_read(spi, buf, sizeof(buf), - NULL, 0); + return ds1302_writeburst(ds1302, RTC_CLCK_BURST, buf, sizeof(buf)); } static int ds1302_rtc_get_time(struct device *dev, struct rtc_time *time) { - struct spi_device *spi = dev_get_drvdata(dev); - u8 addr = RTC_CLCK_BURST << 1 | RTC_CMD_READ; + struct ds1302 *ds1302 = dev_get_drvdata(dev); u8 buf[RTC_CLCK_LEN - 1]; int status; - /* Use write-then-read to get all the date/time registers - * since dma from stack is nonportable - */ - status = spi_write_then_read(spi, &addr, sizeof(addr), - buf, sizeof(buf)); + status = ds1302_readburst(ds1302, RTC_CLCK_BURST, buf, sizeof(buf)); if (status < 0) return status; @@ -108,94 +134,155 @@ static struct rtc_class_ops ds1302_rtc_ops = { .set_time = ds1302_rtc_set_time, }; -static int ds1302_probe(struct spi_device *spi) +static int ds1302_probe(struct ds1302 *ds1302) { struct rtc_device *rtc; - u8 addr; - u8 buf[4]; - u8 *bp = buf; int status; - /* Sanity check board setup data. This may be hooked up - * in 3wire mode, but we don't care. Note that unless - * there's an inverter in place, this needs SPI_CS_HIGH! - */ - if (spi->bits_per_word && (spi->bits_per_word != 8)) { - dev_err(&spi->dev, "bad word length\n"); - return -EINVAL; - } else if (spi->max_speed_hz > 2000000) { - dev_err(&spi->dev, "speed is too high\n"); - return -EINVAL; - } else if (spi->mode & SPI_CPHA) { - dev_err(&spi->dev, "bad mode\n"); - return -EINVAL; - } + dev_set_drvdata(ds1302->dev, ds1302); - addr = RTC_ADDR_CTRL << 1 | RTC_CMD_READ; - status = spi_write_then_read(spi, &addr, sizeof(addr), buf, 1); + status = ds1302_readbyte(ds1302, RTC_ADDR_CTRL); if (status < 0) { - dev_err(&spi->dev, "control register read error %d\n", + dev_err(ds1302->dev, "control register read error %d\n", status); return status; } - if ((buf[0] & ~RTC_CMD_WRITE_DISABLE) != 0) { - status = spi_write_then_read(spi, &addr, sizeof(addr), buf, 1); + if (status & ~RTC_CMD_WRITE_DISABLE) { + status = ds1302_readbyte(ds1302, RTC_ADDR_CTRL); if (status < 0) { - dev_err(&spi->dev, "control register read error %d\n", + dev_err(ds1302->dev, "control register read error %d\n", status); return status; } - if ((buf[0] & ~RTC_CMD_WRITE_DISABLE) != 0) { - dev_err(&spi->dev, "junk in control register\n"); + if (status & ~RTC_CMD_WRITE_DISABLE) { + dev_err(ds1302->dev, "junk in control register\n"); return -ENODEV; } } - if (buf[0] == 0) { - bp = buf; - *bp++ = RTC_ADDR_CTRL << 1 | RTC_CMD_WRITE; - *bp++ = RTC_CMD_WRITE_DISABLE; - - status = spi_write_then_read(spi, buf, 2, NULL, 0); + if (status == 0) { + status = ds1302_writebyte(ds1302, RTC_ADDR_CTRL, + RTC_CMD_WRITE_DISABLE); if (status < 0) { - dev_err(&spi->dev, "control register write error %d\n", - status); + dev_err(ds1302->dev, + "control register write error %d\n", status); return status; } - addr = RTC_ADDR_CTRL << 1 | RTC_CMD_READ; - status = spi_write_then_read(spi, &addr, sizeof(addr), buf, 1); + status = ds1302_readbyte(ds1302, RTC_ADDR_CTRL); if (status < 0) { - dev_err(&spi->dev, + dev_err(ds1302->dev, "error %d reading control register\n", status); return status; } - if (buf[0] != RTC_CMD_WRITE_DISABLE) { - dev_err(&spi->dev, "failed to detect chip\n"); + if (status != RTC_CMD_WRITE_DISABLE) { + dev_err(ds1302->dev, "failed to detect chip\n"); return -ENODEV; } } - spi_set_drvdata(spi, spi); - - rtc = devm_rtc_device_register(&spi->dev, "ds1302", + rtc = devm_rtc_device_register(ds1302->dev, "ds1302", &ds1302_rtc_ops, THIS_MODULE); if (IS_ERR(rtc)) { status = PTR_ERR(rtc); - dev_err(&spi->dev, "error %d registering rtc\n", status); + dev_err(ds1302->dev, "error %d registering rtc\n", status); return status; } return 0; } -static int ds1302_remove(struct spi_device *spi) +static int ds1302_spi_readbyte(struct ds1302 *ds1302, u8 addr) { - spi_set_drvdata(spi, NULL); - return 0; + struct spi_device *spi = to_spi_device(ds1302->dev); + int err; + u8 buf; + + addr = addr << 1 | RTC_CMD_READ; + err = spi_write_then_read(spi, &addr, sizeof(addr), &buf, 1); + if (err) + return err; + + return buf; +} + +static int ds1302_spi_writebyte(struct ds1302 *ds1302, u8 addr, u8 val) +{ + struct spi_device *spi = to_spi_device(ds1302->dev); + u8 buf[2] = { + addr << 1 | RTC_CMD_WRITE, + val, + }; + + return spi_write_then_read(spi, buf, sizeof(buf), NULL, 0); +} + +static int ds1302_spi_readburst(struct ds1302 *ds1302, u8 addr, u8 *buf, + int size) +{ + struct spi_device *spi = to_spi_device(ds1302->dev); + + addr = addr << 1 | RTC_CMD_READ; + + /* Use write-then-read to get all the date/time registers + * since dma from stack is nonportable + */ + return spi_write_then_read(spi, &addr, sizeof(addr), buf, size); +} + +static int ds1302_spi_writeburst(struct ds1302 *ds1302, u8 addr, const u8 *buf, + int size) +{ + struct spi_device *spi = to_spi_device(ds1302->dev); + u8 write_buf[RTC_CLCK_LEN + 1]; + + if (size + 1 > sizeof(write_buf)) + return -EINVAL; + + write_buf[0] = addr << 1 | RTC_CMD_WRITE; + memcpy(write_buf + 1, buf, size); + + /* use write-then-read since dma from stack is nonportable */ + return spi_write_then_read(spi, write_buf, size + 1, NULL, 0); +} + +static const struct ds1302_ops ds1302_spi_ops = { + .readbyte = ds1302_spi_readbyte, + .writebyte = ds1302_spi_writebyte, + .readburst = ds1302_spi_readburst, + .writeburst = ds1302_spi_writeburst, +}; + +static int ds1302_spi_probe(struct spi_device *spi) +{ + struct ds1302 *ds1302; + + /* Sanity check board setup data. This may be hooked up + * in 3wire mode, but we don't care. Note that unless + * there's an inverter in place, this needs SPI_CS_HIGH! + */ + if (spi->bits_per_word && (spi->bits_per_word != 8)) { + dev_err(&spi->dev, "bad word length\n"); + return -EINVAL; + } else if (spi->max_speed_hz > 2000000) { + dev_err(&spi->dev, "speed is too high\n"); + return -EINVAL; + } else if (spi->mode & SPI_CPHA) { + dev_err(&spi->dev, "bad mode\n"); + return -EINVAL; + } + + ds1302 = devm_kzalloc(&spi->dev, sizeof(*ds1302), GFP_KERNEL); + if (!ds1302) + return -ENOMEM; + + ds1302->ops = &ds1302_spi_ops; + ds1302->dev = &spi->dev; + + return ds1302_probe(ds1302); } #ifdef CONFIG_OF @@ -209,8 +296,7 @@ MODULE_DEVICE_TABLE(of, ds1302_dt_ids); static struct spi_driver ds1302_driver = { .driver.name = "rtc-ds1302", .driver.of_match_table = of_match_ptr(ds1302_dt_ids), - .probe = ds1302_probe, - .remove = ds1302_remove, + .probe = ds1302_spi_probe, }; module_spi_driver(ds1302_driver);