From patchwork Sat Oct 24 18:25:42 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sergey Matyukevich X-Patchwork-Id: 36848 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from mail-yx0-f158.google.com (mail-yx0-f158.google.com [209.85.210.158]) by ozlabs.org (Postfix) with ESMTP id 3716BB7BCF for ; Sun, 25 Oct 2009 05:32:21 +1100 (EST) Received: by yxe30 with SMTP id 30so8467905yxe.29 for ; Sat, 24 Oct 2009 11:32:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlegroups.com; s=beta; h=domainkey-signature:received:received:x-sender:x-apparently-to :received:received:received:received-spf:received:dkim-signature :domainkey-signature:received:received:from:to:cc:subject:date :message-id:x-mailer:in-reply-to:references:reply-to:sender :precedence:x-google-loop:mailing-list:list-id:list-post:list-help :list-unsubscribe:x-beenthere-env:x-beenthere; bh=swelydT0wibvj9/jBX+JOU1VZpS7VF8WLXtYhQfImC8=; b=wZxm5cKEbXmRP/dVzdlc2LpBH0JNN+JTNOfwNhVZS7trdxEgar/XfcglQDGW+H+exL 4FrRJXwHdQIdxl+qzmA1kTND9IPKhJSfBf+Kda/nz9ohu0t1SpEXdTcI+KaeXn0Zlh4q avxs0wFCG9mbtxROIJpWk7H8/bkfT2DQVmr0w= DomainKey-Signature: a=rsa-sha1; c=nofws; d=googlegroups.com; s=beta; h=x-sender:x-apparently-to:received-spf:authentication-results :dkim-signature:domainkey-signature:from:to:cc:subject:date :message-id:x-mailer:in-reply-to:references:reply-to:sender :precedence:x-google-loop:mailing-list:list-id:list-post:list-help :list-unsubscribe:x-beenthere-env:x-beenthere; b=cVuq/Mt82evlEokK2CtHm3IoTNM2oYGCmhJZ2GRQD/kW1Av3IcU4Bb0mpB58larDPZ GRmFFxYMF2L65Qs/GVMv1UGOQN9X4bye4WatGAySo/GNMUPg+GgBHYNp/VQrp5bKxM8a QKp8nEIvQuzy3F//4tZKcjAwKWKOU07n58sXk= Received: by 10.101.131.33 with SMTP id i33mr1227862ann.26.1256409139334; Sat, 24 Oct 2009 11:32:19 -0700 (PDT) Received: by 10.176.181.37 with SMTP id d37gr2988yqf.0; Sat, 24 Oct 2009 11:32:19 -0700 (PDT) X-Sender: geomatsi@gmail.com X-Apparently-To: rtc-linux@googlegroups.com Received: by 10.211.144.19 with SMTP id w19mr83088ebn.2.1256409138605; Sat, 24 Oct 2009 11:32:18 -0700 (PDT) Received: by 10.211.144.19 with SMTP id w19mr83087ebn.2.1256409138578; Sat, 24 Oct 2009 11:32:18 -0700 (PDT) Received: from mail-ew0-f223.google.com (mail-ew0-f223.google.com [209.85.219.223]) by gmr-mx.google.com with ESMTP id 13si712330ewy.2.2009.10.24.11.32.17; Sat, 24 Oct 2009 11:32:17 -0700 (PDT) Received-SPF: pass (google.com: domain of geomatsi@gmail.com designates 209.85.219.223 as permitted sender) client-ip=209.85.219.223; Authentication-Results: gmr-mx.google.com; spf=pass (google.com: domain of geomatsi@gmail.com designates 209.85.219.223 as permitted sender) smtp.mail=geomatsi@gmail.com; dkim=pass (test mode) header.i=@gmail.com Received: by ewy23 with SMTP id 23so9581219ewy.2 for ; Sat, 24 Oct 2009 11:32:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:received:received:from:to:cc:subject:date :message-id:x-mailer:in-reply-to:references; bh=HjE3TZO+b6wUfcnZJ8RG6D9Abykgls1sPGqKstUXL9w=; b=FpSPpCAYu3siO8wnLmTRk44Mc3qQuhcoSqyIfthtYSVvKeJFM7VxugRBYkqCNPlc02 ai+Jc/PwwQKWd2dAJULUSWz1+oP9jmGeex/wYqMKCNd2NR4YSjxgB4usFb9bzySayM6/ CI19wSukW44ikThAahtroqCTMFWFEPcsGiFIc= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references; b=rnX3ZnnX/g06shISBgk0oAbgpqBmi9wdQboBj6R5VBz7lW/TIG0gMJsCaYIuwvxURz WyKn8TS83BULdSwy6TlG6huE0OWI5N93lKnZqAD1ABhgbeUiM7yIQw6MOxJV5zLFvF7h utl/3jjD3lCy/lu4ru1obD0GjYYU8H8fp/tZM= Received: by 10.211.142.17 with SMTP id u17mr4863280ebn.0.1256409137480; Sat, 24 Oct 2009 11:32:17 -0700 (PDT) Received: from localhost.localdomain (ppp78-37-31-159.pppoe.avangarddsl.ru [78.37.31.159]) by mx.google.com with ESMTPS id 7sm8910484eyg.17.2009.10.24.11.32.16 (version=TLSv1/SSLv3 cipher=RC4-MD5); Sat, 24 Oct 2009 11:32:17 -0700 (PDT) From: Sergey Matyukevich To: rtc-linux@googlegroups.com Cc: Sergey Lapin Subject: [rtc-linux] [PATCH v2 2/3] RTC: new functionality for fm3130 Date: Sat, 24 Oct 2009 22:25:42 +0400 Message-Id: <1256408743-6644-2-git-send-email-geomatsi@gmail.com> X-Mailer: git-send-email 1.6.2.5 In-Reply-To: <1256408743-6644-1-git-send-email-geomatsi@gmail.com> References: <1256369471-11901-1-git-send-email-geomatsi@gmail.com> <1256408743-6644-1-git-send-email-geomatsi@gmail.com> Reply-To: rtc-linux@googlegroups.com Sender: rtc-linux@googlegroups.com Precedence: bulk X-Google-Loop: groups Mailing-List: list rtc-linux@googlegroups.com; contact rtc-linux+owner@googlegroups.com List-Id: List-Post: List-Help: List-Unsubscribe: , X-BeenThere-Env: rtc-linux@googlegroups.com X-BeenThere: rtc-linux@googlegroups.com This patch contains the following fixes and enhancements for fm3130 rtc driver: * add sanity check for alarm data in fm3130_probe * fix fm3130_set_alarm: according to datasheet, setting match bit '0' indicates that the corresponding alarm field will be used in the match process * add ioctls for RTC_AIE_ON, RTC_AIE_OFF * Remove clearing of AF bit after reading rtc/alarm control register: according to datasheet this bit is cleared anyway when rtc/alarm control register is read Acked-by: Sergey Lapin --- drivers/rtc/rtc-fm3130.c | 179 +++++++++++++++++++++++++++++++++------------- 1 files changed, 128 insertions(+), 51 deletions(-) diff --git a/drivers/rtc/rtc-fm3130.c b/drivers/rtc/rtc-fm3130.c index 812c667..5df991a 100644 --- a/drivers/rtc/rtc-fm3130.c +++ b/drivers/rtc/rtc-fm3130.c @@ -51,8 +51,8 @@ struct fm3130 { struct i2c_msg msg[4]; struct i2c_client *client; struct rtc_device *rtc; + int alarm_valid; int data_valid; - int alarm; }; static const struct i2c_device_id fm3130_id[] = { { "fm3130", 0 }, @@ -86,11 +86,7 @@ static void fm3130_rtc_mode(struct device *dev, int mode) dev_dbg(dev, "invalid mode %d\n", mode); break; } - /* Checking for alarm */ - if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AF) { - fm3130->alarm = 1; - fm3130->regs[FM3130_RTC_CONTROL] &= ~FM3130_RTC_CONTROL_BIT_AF; - } + i2c_smbus_write_byte_data(fm3130->client, FM3130_RTC_CONTROL, fm3130->regs[FM3130_RTC_CONTROL]); } @@ -207,6 +203,15 @@ static int fm3130_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) struct fm3130 *fm3130 = dev_get_drvdata(dev); int tmp; struct rtc_time *tm = &alrm->time; + + if (!fm3130->alarm_valid) { + /* We have invalid alarm in RTC, probably due + to battery faults or other problems. Return EIO + for now, it will allow us to set alarm value later + instead of error during probing which disables device */ + return -EIO; + } + /* read the RTC alarm registers all at once */ tmp = i2c_transfer(to_i2c_adapter(fm3130->client->dev.parent), &fm3130->msg[2], 2); @@ -221,20 +226,30 @@ static int fm3130_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) fm3130->regs[FM3130_ALARM_DATE], fm3130->regs[FM3130_ALARM_MONTHS]); - tm->tm_sec = bcd2bin(fm3130->regs[FM3130_ALARM_SECONDS] & 0x7F); tm->tm_min = bcd2bin(fm3130->regs[FM3130_ALARM_MINUTES] & 0x7F); tm->tm_hour = bcd2bin(fm3130->regs[FM3130_ALARM_HOURS] & 0x3F); tm->tm_mday = bcd2bin(fm3130->regs[FM3130_ALARM_DATE] & 0x3F); tm->tm_mon = bcd2bin(fm3130->regs[FM3130_ALARM_MONTHS] & 0x1F); + if (tm->tm_mon > 0) tm->tm_mon -= 1; /* RTC is 1-12, tm_mon is 0-11 */ + dev_dbg(dev, "%s secs=%d, mins=%d, " "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", "read alarm", tm->tm_sec, tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + /* check if alarm enabled */ + fm3130->regs[FM3130_RTC_CONTROL] = + i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL); + + if ((fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AEN) && + (~fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_CAL)) { + alrm->enabled = 1; + } + return 0; } @@ -250,25 +265,20 @@ static int fm3130_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) tm->tm_hour, tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); - if (tm->tm_sec != -1) - fm3130->regs[FM3130_ALARM_SECONDS] = - bin2bcd(tm->tm_sec) | 0x80; + fm3130->regs[FM3130_ALARM_SECONDS] = + (tm->tm_sec != -1) ? bin2bcd(tm->tm_sec) : 0x80; - if (tm->tm_min != -1) - fm3130->regs[FM3130_ALARM_MINUTES] = - bin2bcd(tm->tm_min) | 0x80; + fm3130->regs[FM3130_ALARM_MINUTES] = + (tm->tm_min != -1) ? bin2bcd(tm->tm_min) : 0x80; - if (tm->tm_hour != -1) - fm3130->regs[FM3130_ALARM_HOURS] = - bin2bcd(tm->tm_hour) | 0x80; + fm3130->regs[FM3130_ALARM_HOURS] = + (tm->tm_hour != -1) ? bin2bcd(tm->tm_hour) : 0x80; - if (tm->tm_mday != -1) - fm3130->regs[FM3130_ALARM_DATE] = - bin2bcd(tm->tm_mday) | 0x80; + fm3130->regs[FM3130_ALARM_DATE] = + (tm->tm_mday != -1) ? bin2bcd(tm->tm_mday) : 0x80; - if (tm->tm_mon != -1) - fm3130->regs[FM3130_ALARM_MONTHS] = - bin2bcd(tm->tm_mon + 1) | 0x80; + fm3130->regs[FM3130_ALARM_MONTHS] = + (tm->tm_mon != -1) ? bin2bcd(tm->tm_mon + 1) : 0x80; dev_dbg(dev, "alarm write %02x %02x %02x %02x %02x\n", fm3130->regs[FM3130_ALARM_SECONDS], @@ -284,11 +294,8 @@ static int fm3130_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) } fm3130->regs[FM3130_RTC_CONTROL] = i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL); - /* Checking for alarm */ - if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AF) { - fm3130->alarm = 1; - fm3130->regs[FM3130_RTC_CONTROL] &= ~FM3130_RTC_CONTROL_BIT_AF; - } + + /* enable or disable alarm */ if (alrm->enabled) { i2c_smbus_write_byte_data(fm3130->client, FM3130_RTC_CONTROL, (fm3130->regs[FM3130_RTC_CONTROL] & @@ -297,12 +304,52 @@ static int fm3130_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) } else { i2c_smbus_write_byte_data(fm3130->client, FM3130_RTC_CONTROL, fm3130->regs[FM3130_RTC_CONTROL] & - ~(FM3130_RTC_CONTROL_BIT_AEN)); + ~(FM3130_RTC_CONTROL_BIT_CAL) & + ~(FM3130_RTC_CONTROL_BIT_AEN)); } + + /* We assume here that data is valid once written */ + if (!fm3130->alarm_valid) + fm3130->alarm_valid = 1; + return 0; } +static int fm3130_ioctl(struct device *dev, unsigned int cmd, + unsigned long arg) +{ + struct fm3130 *fm3130 = dev_get_drvdata(dev); + int ret = 0; + + fm3130->regs[FM3130_RTC_CONTROL] = + i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL); + + dev_dbg(dev, "ioctl: cmd=%08x, FM3130_RTC_CONTROL=%08x\n", + cmd, fm3130->regs[FM3130_RTC_CONTROL]); + + switch (cmd) { + case RTC_AIE_OFF: /* alarm off */ + ret = i2c_smbus_write_byte_data(fm3130->client, + FM3130_RTC_CONTROL, fm3130->regs[FM3130_RTC_CONTROL] & + ~(FM3130_RTC_CONTROL_BIT_CAL) & + ~(FM3130_RTC_CONTROL_BIT_AEN)); + break; + case RTC_AIE_ON: /* alarm on */ + ret = i2c_smbus_write_byte_data(fm3130->client, + FM3130_RTC_CONTROL, (fm3130->regs[FM3130_RTC_CONTROL] & + ~(FM3130_RTC_CONTROL_BIT_CAL)) | + FM3130_RTC_CONTROL_BIT_AEN); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + static const struct rtc_class_ops fm3130_rtc_ops = { + .ioctl = fm3130_ioctl, .read_time = fm3130_get_time, .set_time = fm3130_set_time, .read_alarm = fm3130_read_alarm, @@ -355,6 +402,7 @@ static int __devinit fm3130_probe(struct i2c_client *client, fm3130->msg[3].len = FM3130_ALARM_REGS; fm3130->msg[3].buf = &fm3130->regs[FM3130_ALARM_SECONDS]; + fm3130->alarm_valid = 0; fm3130->data_valid = 0; tmp = i2c_transfer(adapter, fm3130->msg, 4); @@ -369,12 +417,6 @@ static int __devinit fm3130_probe(struct i2c_client *client, fm3130->regs[FM3130_CAL_CONTROL] = i2c_smbus_read_byte_data(client, FM3130_CAL_CONTROL); - /* Checking for alarm */ - if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AF) { - fm3130->alarm = 1; - fm3130->regs[FM3130_RTC_CONTROL] &= ~FM3130_RTC_CONTROL_BIT_AF; - } - /* Disabling calibration mode */ if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_CAL) { i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL, @@ -399,44 +441,79 @@ static int __devinit fm3130_probe(struct i2c_client *client, fm3130->regs[FM3130_CAL_CONTROL] & ~(FM3130_CAL_CONTROL_BIT_nOSCEN)); - /* oscillator fault? clear flag, and warn */ - if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_LB) + /* low battery? clear flag, and warn */ + if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_LB) { + i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL, + fm3130->regs[FM3130_RTC_CONTROL] & + ~(FM3130_RTC_CONTROL_BIT_LB)); dev_warn(&client->dev, "Low battery!\n"); + } - /* oscillator fault? clear flag, and warn */ + /* check if Power On Reset bit is set */ if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_POR) { i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL, fm3130->regs[FM3130_RTC_CONTROL] & ~FM3130_RTC_CONTROL_BIT_POR); - dev_warn(&client->dev, "SET TIME!\n"); + dev_dbg(&client->dev, "POR bit is set\n"); } /* ACS is controlled by alarm */ i2c_smbus_write_byte_data(client, FM3130_ALARM_WP_CONTROL, 0x80); - /* TODO */ - /* TODO need to sanity check alarm */ - tmp = fm3130->regs[FM3130_RTC_SECONDS]; - tmp = bcd2bin(tmp & 0x7f); - if (tmp > 60) - goto exit_bad; + /* alarm registers sanity check */ + tmp = bcd2bin(fm3130->regs[FM3130_RTC_SECONDS] & 0x7f); + if (tmp > 59) + goto bad_alarm; + tmp = bcd2bin(fm3130->regs[FM3130_RTC_MINUTES] & 0x7f); - if (tmp > 60) - goto exit_bad; + if (tmp > 59) + goto bad_alarm; + + tmp = bcd2bin(fm3130->regs[FM3130_RTC_HOURS] & 0x3f); + if (tmp > 23) + goto bad_alarm; tmp = bcd2bin(fm3130->regs[FM3130_RTC_DATE] & 0x3f); if (tmp == 0 || tmp > 31) - goto exit_bad; + goto bad_alarm; tmp = bcd2bin(fm3130->regs[FM3130_RTC_MONTHS] & 0x1f); if (tmp == 0 || tmp > 12) - goto exit_bad; + goto bad_alarm; - tmp = fm3130->regs[FM3130_RTC_HOURS]; + fm3130->alarm_valid = 1; + +bad_alarm: + + /* clock registers sanity chek */ + tmp = bcd2bin(fm3130->regs[FM3130_RTC_SECONDS] & 0x7f); + if (tmp > 59) + goto bad_clock; + + tmp = bcd2bin(fm3130->regs[FM3130_RTC_MINUTES] & 0x7f); + if (tmp > 59) + goto bad_clock; + + tmp = bcd2bin(fm3130->regs[FM3130_RTC_HOURS] & 0x3f); + if (tmp > 23) + goto bad_clock; + + tmp = bcd2bin(fm3130->regs[FM3130_RTC_DAY] & 0x7); + if (tmp == 0 || tmp > 7) + goto bad_clock; + + tmp = bcd2bin(fm3130->regs[FM3130_RTC_DATE] & 0x3f); + if (tmp == 0 || tmp > 31) + goto bad_clock; + + tmp = bcd2bin(fm3130->regs[FM3130_RTC_MONTHS] & 0x1f); + if (tmp == 0 || tmp > 12) + goto bad_clock; fm3130->data_valid = 1; -exit_bad: - if (!fm3130->data_valid) +bad_clock: + + if (!fm3130->data_valid || !fm3130->alarm_valid) dev_dbg(&client->dev, "%s: %02x %02x %02x %02x %02x %02x %02x %02x" "%02x %02x %02x %02x %02x %02x %02x\n",