From patchwork Sun Feb 20 21:13:10 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ryan Mallon X-Patchwork-Id: 83754 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from mail-pz0-f56.google.com (mail-pz0-f56.google.com [209.85.210.56]) (using TLSv1 with cipher RC4-SHA (128/128 bits)) (Client CN "smtp.gmail.com", Issuer "Google Internet Authority" (verified OK)) by ozlabs.org (Postfix) with ESMTPS id 7F5EBB718C for ; Mon, 21 Feb 2011 08:12:40 +1100 (EST) Received: by pzk9 with SMTP id 9sf674137pzk.11 for ; Sun, 20 Feb 2011 13:12:38 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlegroups.com; s=beta; h=domainkey-signature:x-beenthere:received-spf:message-id:date:from :user-agent:mime-version:to:cc:subject:x-enigmail-version :x-original-sender:x-original-authentication-results:reply-to :precedence:mailing-list:list-id:list-post:list-help:list-archive :sender:list-subscribe:list-unsubscribe:content-type; bh=/9B3icGaOdi5gb7LhzqGF0EMRMv6I02w8ayfGhCeb0w=; b=7GgzBQF1rB/35r++sBAn+upxqOXvbDafUA0R6Sg5rsUN5Z1BKcjt41zVoEA+s5NW0M JEmFXaDpERaS1mYOBnaL021f2yrHRIqk0bqha/OrvyMizR1S3lvtTfQWFaqwPwNwoy6C yZZED//XKanLvI79jc1qcNNP/GHBhFnQM60ik= DomainKey-Signature: a=rsa-sha1; c=nofws; d=googlegroups.com; s=beta; h=x-beenthere:received-spf:message-id:date:from:user-agent :mime-version:to:cc:subject:x-enigmail-version:x-original-sender :x-original-authentication-results:reply-to:precedence:mailing-list :list-id:list-post:list-help:list-archive:sender:list-subscribe :list-unsubscribe:content-type; b=iTP6VLcQIu+zXt8jmWdtoi+g0WyeUzgFjNbcMBgc8cMIWPCPzIpMehMOidYEzgZMYY 2z3n7UVgEujICJUwjDSIssqGA86QYrX1nhWeF/nNxw+l9cZNbPTbL1xherY8STfys7I3 4gISF4K490V/XDPj1W53ObpzoOjKF8vJruGnA= Received: by 10.142.239.6 with SMTP id m6mr41616wfh.54.1298236356995; Sun, 20 Feb 2011 13:12:36 -0800 (PST) X-BeenThere: rtc-linux@googlegroups.com Received: by 10.143.178.6 with SMTP id f6ls2658513wfp.3.p; Sun, 20 Feb 2011 13:12:36 -0800 (PST) Received: by 10.142.128.13 with SMTP id a13mr151301wfd.58.1298236356310; Sun, 20 Feb 2011 13:12:36 -0800 (PST) Received: by 10.142.128.13 with SMTP id a13mr151300wfd.58.1298236356260; Sun, 20 Feb 2011 13:12:36 -0800 (PST) Received: from hayes.bluewaternz.com (mail.bluewatersys.com [202.124.120.130]) by gmr-mx.google.com with ESMTPS id x35si648410wfd.0.2011.02.20.13.12.34 (version=TLSv1/SSLv3 cipher=OTHER); Sun, 20 Feb 2011 13:12:35 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of ryan@bluewatersys.com designates 202.124.120.130 as permitted sender) client-ip=202.124.120.130; Received: (qmail 22860 invoked by uid 89); 20 Feb 2011 21:30:23 -0000 Received: from unknown (HELO ?192.168.2.96?) (ryan@192.168.2.96) by 0 with ESMTPA; 20 Feb 2011 21:30:23 -0000 Message-ID: <4D6183E6.1030706@bluewatersys.com> Date: Mon, 21 Feb 2011 10:13:10 +1300 From: Ryan Mallon User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.13) Gecko/20101208 Lightning/1.0b2 Thunderbird/3.1.7 MIME-Version: 1.0 To: a.zummo@towertech.it CC: rtc-linux@googlegroups.com, linux kernel , hvr@gnu.org Subject: [rtc-linux] [REPOST PATCH] rtc-isl1208: Add alarm support X-Enigmail-Version: 1.1.2 X-Original-Sender: ryan@bluewatersys.com X-Original-Authentication-Results: gmr-mx.google.com; spf=pass (google.com: best guess record for domain of ryan@bluewatersys.com designates 202.124.120.130 as permitted sender) smtp.mail=ryan@bluewatersys.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: , Add alarm/wakeup support to rtc isl1208 driver Signed-off-by: Ryan Mallon diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c index 468200c..94aa4f5 100644 --- a/drivers/rtc/rtc-isl1208.c +++ b/drivers/rtc/rtc-isl1208.c @@ -39,6 +39,8 @@ #define ISL1208_REG_SR_BAT (1<<1) /* battery */ #define ISL1208_REG_SR_RTCF (1<<0) /* rtc fail */ #define ISL1208_REG_INT 0x08 +#define ISL1208_REG_INT_ALME (1<<6) /* alarm enable */ +#define ISL1208_REG_INT_IM (1<<7) /* interrupt/alarm mode */ #define ISL1208_REG_09 0x09 /* reserved */ #define ISL1208_REG_ATR 0x0a #define ISL1208_REG_DTR 0x0b @@ -202,6 +204,31 @@ isl1208_i2c_set_usr(struct i2c_client *client, u16 usr) } static int +isl1208_rtc_toggle_alarm(struct i2c_client *client, int enable) +{ + int icr; + + icr = i2c_smbus_read_byte_data(client, ISL1208_REG_INT); + if (icr < 0) { + dev_err(&client->dev, "%s: reading INT failed\n", __func__); + return icr; + } + + if (enable) + icr |= ISL1208_REG_INT_ALME | ISL1208_REG_INT_IM; + else + icr &= ~(ISL1208_REG_INT_ALME | ISL1208_REG_INT_IM); + + icr = i2c_smbus_write_byte_data(client, ISL1208_REG_INT, icr); + if (icr < 0) { + dev_err(&client->dev, "%s: writing INT failed\n", __func__); + return icr; + } + + return 0; +} + +static int isl1208_rtc_proc(struct device *dev, struct seq_file *seq) { struct i2c_client *const client = to_i2c_client(dev); @@ -288,7 +315,7 @@ isl1208_i2c_read_alarm(struct i2c_client *client, struct rtc_wkalrm *alarm) { struct rtc_time *const tm = &alarm->time; u8 regs[ISL1208_ALARM_SECTION_LEN] = { 0, }; - int sr; + int icr, yr, sr; sr = isl1208_i2c_get_sr(client); if (sr < 0) { @@ -313,6 +340,73 @@ isl1208_i2c_read_alarm(struct i2c_client *client, struct rtc_wkalrm *alarm) bcd2bin(regs[ISL1208_REG_MOA - ISL1208_REG_SCA] & 0x1f) - 1; tm->tm_wday = bcd2bin(regs[ISL1208_REG_DWA - ISL1208_REG_SCA] & 0x03); + /* The alarm doesn't store the year so get it from the rtc section */ + yr = i2c_smbus_read_byte_data(client, ISL1208_REG_YR); + if (yr < 0) { + dev_err(&client->dev, "%s: reading RTC YR failed\n", __func__); + return yr; + } + tm->tm_year = bcd2bin(yr) + 100; + + icr = i2c_smbus_read_byte_data(client, ISL1208_REG_INT); + if (icr < 0) { + dev_err(&client->dev, "%s: reading INT failed\n", __func__); + return icr; + } + alarm->enabled = !!(icr & ISL1208_REG_INT_ALME); + + return 0; +} + +static int +isl1208_i2c_set_alarm(struct i2c_client *client, struct rtc_wkalrm *alarm) +{ + struct rtc_time *alarm_tm = &alarm->time; + u8 regs[ISL1208_ALARM_SECTION_LEN] = { 0, }; + const int offs = ISL1208_REG_SCA; + unsigned long rtc_secs, alarm_secs; + struct rtc_time rtc_tm; + int err, enable; + + err = isl1208_i2c_read_time(client, &rtc_tm); + if (err) + return err; + err = rtc_tm_to_time(&rtc_tm, &rtc_secs); + if (err) + return err; + err = rtc_tm_to_time(alarm_tm, &alarm_secs); + if (err) + return err; + + /* If the alarm time is before the current time disable the alarm */ + if (!alarm->enabled || alarm_secs <= rtc_secs) + enable = 0x00; + else + enable = 0x80; + + /* Program the alarm and enable it for each setting */ + regs[ISL1208_REG_SCA - offs] = bin2bcd(alarm_tm->tm_sec) | enable; + regs[ISL1208_REG_MNA - offs] = bin2bcd(alarm_tm->tm_min) | enable; + regs[ISL1208_REG_HRA - offs] = bin2bcd(alarm_tm->tm_hour) | + ISL1208_REG_HR_MIL | enable; + + regs[ISL1208_REG_DTA - offs] = bin2bcd(alarm_tm->tm_mday) | enable; + regs[ISL1208_REG_MOA - offs] = bin2bcd(alarm_tm->tm_mon + 1) | enable; + regs[ISL1208_REG_DWA - offs] = bin2bcd(alarm_tm->tm_wday & 7) | enable; + + /* write ALARM registers */ + err = isl1208_i2c_set_regs(client, offs, regs, + ISL1208_ALARM_SECTION_LEN); + if (err < 0) { + dev_err(&client->dev, "%s: writing ALARM section failed\n", + __func__); + return err; + } + + err = isl1208_rtc_toggle_alarm(client, enable); + if (err) + return err; + return 0; } @@ -391,12 +485,63 @@ isl1208_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) return isl1208_i2c_read_alarm(to_i2c_client(dev), alarm); } +static int +isl1208_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + return isl1208_i2c_set_alarm(to_i2c_client(dev), alarm); +} + +static irqreturn_t +isl1208_rtc_interrupt(int irq, void *data) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(1000); + struct i2c_client *client = data; + int handled = 0, sr, err; + + /* + * I2C reads get NAK'ed if we read straight away after an interrupt? + * Using a mdelay/msleep didn't seem to help either, so we work around + * this by continually trying to read the register for a short time. + */ + while (1) { + sr = isl1208_i2c_get_sr(client); + if (sr >= 0) + break; + + if (time_after(jiffies, timeout)) { + dev_dbg(&client->dev, "%s: reading SR failed\n", + __func__); + return sr; + } + } + + if (sr & ISL1208_REG_SR_ALM) { + dev_dbg(&client->dev, "alarm!\n"); + + /* Clear the alarm */ + sr &= ~ISL1208_REG_SR_ALM; + sr = i2c_smbus_write_byte_data(client, ISL1208_REG_SR, sr); + if (sr < 0) + dev_err(&client->dev, "%s: writing SR failed\n", + __func__); + else + handled = 1; + + /* Disable the alarm */ + err = isl1208_rtc_toggle_alarm(client, 0); + if (err) + return err; + } + + return handled ? IRQ_HANDLED : IRQ_NONE; +} + static const struct rtc_class_ops isl1208_rtc_ops = { .proc = isl1208_rtc_proc, .read_time = isl1208_rtc_read_time, .set_time = isl1208_rtc_set_time, .read_alarm = isl1208_rtc_read_alarm, - /*.set_alarm = isl1208_rtc_set_alarm, */ + .set_alarm = isl1208_rtc_set_alarm, }; /* sysfs interface */ @@ -488,11 +633,29 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id) dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n"); + if (client->irq > 0) { + rc = request_threaded_irq(client->irq, NULL, + isl1208_rtc_interrupt, + IRQF_SHARED, + isl1208_driver.driver.name, client); + if (!rc) { + device_init_wakeup(&client->dev, 1); + enable_irq_wake(client->irq); + } else { + dev_err(&client->dev, + "Unable to request irq %d, no alarm support\n", + client->irq); + client->irq = 0; + } + } + rtc = rtc_device_register(isl1208_driver.driver.name, &client->dev, &isl1208_rtc_ops, THIS_MODULE); - if (IS_ERR(rtc)) - return PTR_ERR(rtc); + if (IS_ERR(rtc)) { + rc = PTR_ERR(rtc); + goto exit_free_irq; + } i2c_set_clientdata(client, rtc); @@ -514,6 +677,9 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id) exit_unregister: rtc_device_unregister(rtc); +exit_free_irq: + if (client->irq) + free_irq(client->irq, client); return rc; } @@ -525,6 +691,8 @@ isl1208_remove(struct i2c_client *client) sysfs_remove_group(&client->dev.kobj, &isl1208_rtc_sysfs_files); rtc_device_unregister(rtc); + if (client->irq) + free_irq(client->irq, client); return 0; }