From patchwork Wed Jun 2 13:53:07 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: thomas.koeller@baslerweb.com X-Patchwork-Id: 54375 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from mail-wy0-f184.google.com (mail-wy0-f184.google.com [74.125.82.184]) by ozlabs.org (Postfix) with ESMTP id 0FD94B7D29 for ; Wed, 2 Jun 2010 23:53:47 +1000 (EST) Received: by wya21 with SMTP id 21sf2267511wya.11 for ; Wed, 02 Jun 2010 06:53:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlegroups.com; s=beta; h=domainkey-signature:received:mime-version:x-beenthere:received :received:received:received:received-spf:received:received:from:to :cc:subject:date:message-id:x-mailer:in-reply-to:references :x-originalarrivaltime:x-original-authentication-results :x-original-sender:reply-to:precedence:mailing-list:list-id :list-post:list-help:list-archive:sender:list-subscribe :list-unsubscribe:content-type; bh=0SBQ3gqp2ZJIoP2nqMPa9auVJUJ7VkolQlYLY7xL4PA=; b=cV8UYXVZ5Nna9oB4MTcKBD827Hci6Sv9DnMc9OdFcMZBDBXWCeKaFatuDqZrjvyOIh BMMLkQnPBTx+xjJd19US6dLBJUSTNwMWkxOxs0WGl1S6BbkzylEHo6pLjGFzM0wVu38k DVCn2wFw4uy9b5jXAJdfOOut7FOaR2c6j9ZpA= DomainKey-Signature: a=rsa-sha1; c=nofws; d=googlegroups.com; s=beta; h=mime-version:x-beenthere:received-spf:from:to:cc:subject:date :message-id:x-mailer:in-reply-to:references:x-originalarrivaltime :x-original-authentication-results:x-original-sender:reply-to :precedence:mailing-list:list-id:list-post:list-help:list-archive :sender:list-subscribe:list-unsubscribe:content-type; b=zz6bqauXN0f+ls9E+IJ/Oor4s2nlXWJqlckxrN5Sr0u+PB/9YbtUFTdM051ozfc4Dg HjFqEKKUG7q3fg01/JzrdHB7w5pgpRgf+bGwUfx3TiIXLBTkkBQtxsLQSSXoNSSpVjXr HUt+0QpDigQWatYP3I5trnd4fR5cZTfeUAjiY= Received: by 10.223.132.205 with SMTP id c13mr349160fat.28.1275486825265; Wed, 02 Jun 2010 06:53:45 -0700 (PDT) MIME-Version: 1.0 X-BeenThere: rtc-linux@googlegroups.com Received: by 10.204.5.219 with SMTP id 27ls644502bkw.1.p; Wed, 02 Jun 2010 06:53:44 -0700 (PDT) Received: by 10.204.142.16 with SMTP id o16mr157237bku.12.1275486824836; Wed, 02 Jun 2010 06:53:44 -0700 (PDT) Received: by 10.204.142.16 with SMTP id o16mr157236bku.12.1275486824782; Wed, 02 Jun 2010 06:53:44 -0700 (PDT) Received: from mail01.baslerweb.com (mail01.baslerweb.com [80.156.24.166]) by gmr-mx.google.com with ESMTP id q19si1277194bks.1.2010.06.02.06.53.44; Wed, 02 Jun 2010 06:53:44 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of Thomas.Koeller@baslerweb.com designates 80.156.24.166 as permitted sender) client-ip=80.156.24.166; Received: from unknown (HELO AHR075S.basler.corp) ([172.16.20.75]) by mail01-out.baslerweb.com with ESMTP; 02 Jun 2010 15:53:22 +0200 Received: from rd-ipcam.basler.corp ([172.16.13.131]) by AHR075S.basler.corp with Microsoft SMTPSVC(6.0.3790.4675); Wed, 2 Jun 2010 15:53:44 +0200 From: thomas.koeller@baslerweb.com To: p_gortmaker@yahoo.com, rtc-linux@googlegroups.com Cc: Thomas Koeller Subject: [rtc-linux] [PATCH 4/4] rtc: rtc-rs5c372: optionally return epoch if time may be unreliable Date: Wed, 2 Jun 2010 15:53:07 +0200 Message-Id: <1275486787-30406-5-git-send-email-thomas.koeller@baslerweb.com> X-Mailer: git-send-email 1.7.1 In-Reply-To: <1275486787-30406-1-git-send-email-thomas.koeller@baslerweb.com> References: <1275486787-30406-1-git-send-email-thomas.koeller@baslerweb.com> X-OriginalArrivalTime: 02 Jun 2010 13:53:44.0267 (UTC) FILETIME=[040161B0:01CB025B] X-Original-Authentication-Results: gmr-mx.google.com; spf=pass (google.com: best guess record for domain of Thomas.Koeller@baslerweb.com designates 80.156.24.166 as permitted sender) smtp.mail=Thomas.Koeller@baslerweb.com X-Original-Sender: thomas.koeller@baslerweb.com Reply-To: rtc-linux@googlegroups.com Precedence: list Mailing-list: list rtc-linux@googlegroups.com; contact rtc-linux+owners@googlegroups.com List-ID: List-Post: , List-Help: , List-Archive: Sender: rtc-linux@googlegroups.com List-Subscribe: , List-Unsubscribe: , From: Thomas Koeller The time stored in the chip may be wrong due to power outage, oscillator stall etc. A boolean parameter named 'epoch_if_invalid' has been added, controlling the behavior in response to the detection of such a condition. If set, it causes the time to be reported as the epoch. This is intended to make it obvious that the time value returned may be invalid, which otherwise may be difficult to detect. Signed-off-by: Thomas Koeller --- drivers/rtc/rtc-rs5c372.c | 133 +++++++++++++++++++++++++++++++++++++++------ 1 files changed, 116 insertions(+), 17 deletions(-) diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c index 41518ef..376a943 100644 --- a/drivers/rtc/rtc-rs5c372.c +++ b/drivers/rtc/rtc-rs5c372.c @@ -13,6 +13,7 @@ #include #include #include +#include #define DRV_VERSION "0.6" @@ -51,18 +52,27 @@ # define RS5C_CTRL1_CT0 (0 << 0) /* no periodic irq */ # define RS5C_CTRL1_CT4 (4 << 0) /* 1 Hz level irq */ #define RS5C_REG_CTRL2 15 +# define R2025_CTRL2_VDET (1 << 6) /* VDD low */ # define RS5C372_CTRL2_24 (1 << 5) -# define R2025_CTRL2_XST (1 << 5) +# define R2025_CTRL2_XST (1 << 5) /* OSC stopped */ # define RS5C_CTRL2_XSTP (1 << 4) /* only if !R2025S/D */ +# define R2025_CTRL2_PON (1 << 4) /* power loss */ # define RS5C_CTRL2_CTFG (1 << 2) # define RS5C_CTRL2_AAFG (1 << 1) /* or WAFG */ # define RS5C_CTRL2_BAFG (1 << 0) /* or DAFG */ +#define R2025_TIME_VALIDMASK \ + (R2025_CTRL2_VDET | R2025_CTRL2_XST | R2025_CTRL2_PON) /* to read (style 1) or write registers starting at R */ #define RS5C_ADDR(R) (((R) << 4) | 0) +static int epoch_if_invalid; +module_param(epoch_if_invalid, bool, 0644); +MODULE_PARM_DESC(reset_if_invalid, "If invalid time, return the epoch"); + + enum rtc_type { rtc_undef = 0, rtc_r2025sd, @@ -89,6 +99,8 @@ struct rs5c372 { struct i2c_client *client; struct rtc_device *rtc; enum rtc_type type; + int (*is_valid_time)(const struct rs5c372 *rs5c); + int (*clear_invalid)(const struct rs5c372 *rs5c); unsigned time24:1; unsigned has_irq:1; unsigned smbus:1; @@ -96,6 +108,40 @@ struct rs5c372 { char *regs; }; +static int rs5c372_is_valid_time(const struct rs5c372 *rs5c) +{ + return (rs5c->regs[RS5C_REG_CTRL2] | RS5C_CTRL2_XSTP) ? 1 : 0; +} + +static int rs5c372_clear_invalid(const struct rs5c372 *rs5c) +{ + int ret = 0, addr = RS5C_ADDR(RS5C_REG_CTRL2); + + ret = i2c_smbus_read_byte_data(rs5c->client, addr); + return (0 <= ret) ? + i2c_smbus_write_byte_data( + rs5c->client, addr, (u8) ret | RS5C_CTRL2_XSTP) : + ret; +} + +static int r2025_is_valid_time(const struct rs5c372 *rs5c) +{ + return (rs5c->regs[RS5C_REG_CTRL2] & R2025_TIME_VALIDMASK) == + R2025_CTRL2_XST; +} + +static int r2025_clear_invalid(const struct rs5c372 *rs5c) +{ + int ret, addr = RS5C_ADDR(RS5C_REG_CTRL2); + + ret = i2c_smbus_read_byte_data(rs5c->client, addr); + return (0 <= ret) ? + i2c_smbus_write_byte_data( + rs5c->client, addr, + (u8) (ret & ~R2025_TIME_VALIDMASK) | R2025_CTRL2_XST) : + ret; +} + static int rs5c_get_regs(struct rs5c372 *rs5c) { struct i2c_client *client = rs5c->client; @@ -178,6 +224,11 @@ static int rs5c372_get_datetime(struct i2c_client *client, struct rtc_time *tm) if (status < 0) return status; + if (epoch_if_invalid && !rs5c->is_valid_time(rs5c)) { + rtc_time_to_tm(0, tm); + return 0; + }; + tm->tm_sec = bcd2bin(rs5c->regs[RS5C372_REG_SECS] & 0x7f); tm->tm_min = bcd2bin(rs5c->regs[RS5C372_REG_MINS] & 0x7f); tm->tm_hour = rs5c_reg2hr(rs5c, rs5c->regs[RS5C372_REG_HOURS]); @@ -227,7 +278,9 @@ static int rs5c372_set_datetime(struct i2c_client *client, struct rtc_time *tm) buf[6] = bin2bcd(tm->tm_year); } - if (i2c_smbus_write_i2c_block_data(client, addr, sizeof(buf), buf) < 0) { + if ((i2c_smbus_write_i2c_block_data(client, addr, + sizeof(buf), buf) < 0) || + (rs5c->clear_invalid(rs5c) < 0)) { dev_err(&client->dev, "%s: write error\n", __func__); return -EIO; } @@ -532,20 +585,41 @@ static void rs5c_sysfs_unregister(struct device *dev) static struct i2c_driver rs5c372_driver; -static int rs5c_oscillator_setup(struct rs5c372 *rs5c372) +static int rs5c_oscillator_setup_pon(struct rs5c372 *rs5c372) +{ + u8 buf, invalid; + int ret; + + invalid = (rs5c372->regs[RS5C_REG_CTRL2] & R2025_TIME_VALIDMASK) ^ + R2025_CTRL2_XST; + + if (invalid & R2025_CTRL2_PON) + dev_warn(&rs5c372->client->dev, "Time invalid: power lost\n"); + else if (invalid & R2025_CTRL2_VDET) + dev_warn(&rs5c372->client->dev, "Time invalid: VDD low\n"); + else if (invalid & R2025_CTRL2_XST) + dev_warn(&rs5c372->client->dev, "Time invalid: osc. stalled\n"); + else + return 0; + + buf = rs5c372->regs[RS5C_REG_CTRL1] | RV5C387_CTRL1_24; + ret = i2c_smbus_write_byte_data(rs5c372->client, + RS5C_ADDR(RS5C_REG_CTRL1), buf); + if (unlikely(ret < 0)) + return ret; + rs5c372->regs[RS5C_REG_CTRL1] = buf; + rs5c372->time24 = 1; + + return 0; +} + +static int rs5c_oscillator_setup_xstp(struct rs5c372 *rs5c372) { unsigned char buf[2]; int addr, i, ret = 0; - if (rs5c372->type == rtc_r2025sd) { - if (rs5c372->regs[RS5C_REG_CTRL2] & R2025_CTRL2_XST) - return ret; - rs5c372->regs[RS5C_REG_CTRL2] |= R2025_CTRL2_XST; - } else { - if (!(rs5c372->regs[RS5C_REG_CTRL2] & RS5C_CTRL2_XSTP)) - return ret; - rs5c372->regs[RS5C_REG_CTRL2] &= ~RS5C_CTRL2_XSTP; - } + if (!(rs5c372->regs[RS5C_REG_CTRL2] & RS5C_CTRL2_XSTP)) + return ret; addr = RS5C_ADDR(RS5C_REG_CTRL1); buf[0] = rs5c372->regs[RS5C_REG_CTRL1]; @@ -558,7 +632,6 @@ static int rs5c_oscillator_setup(struct rs5c372 *rs5c372) buf[1] |= RS5C372_CTRL2_24; rs5c372->time24 = 1; break; - case rtc_r2025sd: case rtc_rv5c386: case rtc_rv5c387a: buf[0] |= RV5C387_CTRL1_24; @@ -582,6 +655,32 @@ static int rs5c_oscillator_setup(struct rs5c372 *rs5c372) return 0; } +static int rs5c_oscillator_setup(struct rs5c372 *rs5c372) +{ + int res; + + switch (rs5c372->type) { + case rtc_r2025sd: + rs5c372->is_valid_time = r2025_is_valid_time; + rs5c372->clear_invalid = r2025_clear_invalid; + res = rs5c_oscillator_setup_pon(rs5c372); + break; + case rtc_rs5c372a: + case rtc_rs5c372b: + case rtc_rv5c386: + case rtc_rv5c387a: + rs5c372->is_valid_time = rs5c372_is_valid_time; + rs5c372->clear_invalid = rs5c372_clear_invalid; + res = rs5c_oscillator_setup_xstp(rs5c372); + break; + default: + res = -EINVAL; + break; + } + + return res; +} + static int rs5c372_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -633,14 +732,14 @@ static int rs5c372_probe(struct i2c_client *client, /* alarm uses ALARM_A; and nINTRA on 372a, nINTR on 372b. * so does periodic irq, except some 327a modes. */ - if (rs5c372->regs[RS5C_REG_CTRL2] & RS5C372_CTRL2_24) - rs5c372->time24 = 1; + rs5c372->time24 = + (rs5c372->regs[RS5C_REG_CTRL2] & RS5C372_CTRL2_24) != 0; break; case rtc_r2025sd: case rtc_rv5c386: case rtc_rv5c387a: - if (rs5c372->regs[RS5C_REG_CTRL1] & RV5C387_CTRL1_24) - rs5c372->time24 = 1; + rs5c372->time24 = + (rs5c372->regs[RS5C_REG_CTRL1] & RV5C387_CTRL1_24) != 0; /* alarm uses ALARM_W; and nINTRB for alarm and periodic * irq, on both 386 and 387 */