From patchwork Tue Mar 24 14:47:47 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Darius Augulis X-Patchwork-Id: 25018 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from yx-out-2122.google.com (yx-out-2122.google.com [74.125.44.26]) by ozlabs.org (Postfix) with ESMTP id 4AC1EDDF77 for ; Wed, 25 Mar 2009 01:50:16 +1100 (EST) Received: by yx-out-2122.google.com with SMTP id 8so1066821yxj.11 for ; Tue, 24 Mar 2009 07:50:14 -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-spf:authentication-results:received :dkim-signature:domainkey-signature:received:received:message-id :date:from:user-agent:mime-version:content-type:to:cc:subject :reply-to:sender:precedence:x-google-loop:mailing-list:list-id :list-post:list-help:list-unsubscribe:x-beenthere-env:x-beenthere; bh=v2bQCE6b5Ts9sObOlrNIUCsOskuZ9U7nANYOcoL65IU=; b=5qaT3ndE6EzAYjDMlrqD6IZw71ZF/vOHz+tYJqRY6lmpPdDXO0h8hJQO0PV7ExhWRi oMidN93fivdtZQW/sS8Dk3UCYIaoJ8Szq8Hmqr5+o1N7HOKBBdnxPzFP+EH5jyvrLPXj 5IxaU9bWImTXlxaOmS5C0cTuJTMrF9jvCRkxc= 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:message-id:date:from:user-agent :mime-version:content-type:to:cc:subject:reply-to:sender:precedence :x-google-loop:mailing-list:list-id:list-post:list-help :list-unsubscribe:x-beenthere-env:x-beenthere; b=6bThaI4hRPetAZGCgHrz45S6St+XdabUhBWpLlSbHATzICK+OECaSA+zIKnVGdUq7x MV21+Cr48X9fZSJiCN0Yy3fj86XKDuJO1cf31NjPDdBcWrunWSKew0XzjAdwDpUah9x4 09qmtAO9yuIyZepyF5iovD57WKEoJKAMxelBo= Received: by 10.100.43.14 with SMTP id q14mr1354048anq.0.1237906211512; Tue, 24 Mar 2009 07:50:11 -0700 (PDT) Received: by 10.177.37.21 with SMTP id p21gr2035yqj.0; Tue, 24 Mar 2009 07:50:11 -0700 (PDT) X-Sender: augulis.darius@gmail.com X-Apparently-To: rtc-linux@googlegroups.com Received: by 10.103.243.7 with SMTP id v7mr87644mur.20.1237906210471; Tue, 24 Mar 2009 07:50:10 -0700 (PDT) Received: from mail-bw0-f174.google.com (mail-bw0-f174.google.com [209.85.218.174]) by gmr-mx.google.com with ESMTP id 15si423218fxm.5.2009.03.24.07.50.09; Tue, 24 Mar 2009 07:50:09 -0700 (PDT) Received-SPF: pass (google.com: domain of augulis.darius@gmail.com designates 209.85.218.174 as permitted sender) client-ip=209.85.218.174; Authentication-Results: gmr-mx.google.com; spf=pass (google.com: domain of augulis.darius@gmail.com designates 209.85.218.174 as permitted sender) smtp.mail=augulis.darius@gmail.com; dkim=pass (test mode) header.i=@gmail.com Received: by bwz22 with SMTP id 22so2140741bwz.17 for ; Tue, 24 Mar 2009 07:50:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:received:received:message-id:date:from :user-agent:mime-version:to:cc:subject:content-type; bh=lnlmHc5NCWxhEFcNWzQ7fQ9RNqyA8cFTuSnuDpc+HhU=; b=AgGAE/CeKIv8keNkwFXOlYkeMYS+7bjGckelcYDCgoDib/dYb+++YQP6qyzL216I6r cPyjdHgT5cL/TqNeHYH6Uz8OijdC9yMbkhoMxx57euSf1yeCp5JpDpwbMC8DCFT0/ooT RHtpEbU/Wsj0ZCs5xYNpUQIiS8YUM0ZoE8eqI= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=message-id:date:from:user-agent:mime-version:to:cc:subject :content-type; b=RQ5tq/F7LmU+NiPwCa6Wvp5Y2X33opfVU7GnhjzZLAupBmNz4ZITteSBaXVaKXjTFY ieQRtccVmhPHGWAqYUob31nzyDZgR35ozCE+NBy5lkxtRJU9F4h9AKANFKuavJJd3c5v DPcJvQmGIhcA/kNgxKbktB+bEbsHrF1gVvFiU= Received: by 10.223.126.145 with SMTP id c17mr7162779fas.16.1237906209131; Tue, 24 Mar 2009 07:50:09 -0700 (PDT) Received: from localhost.localdomain ([217.147.39.138]) by mx.google.com with ESMTPS id d13sm5214367fka.0.2009.03.24.07.50.07 (version=TLSv1/SSLv3 cipher=RC4-MD5); Tue, 24 Mar 2009 07:50:08 -0700 (PDT) Message-ID: <49C8F293.3070900@gmail.com> Date: Tue, 24 Mar 2009 16:47:47 +0200 From: Darius Augulis User-Agent: Thunderbird 2.0.0.19 (X11/20090105) Mime-Version: 1.0 To: linux-arm-kernel@lists.arm.linux.org.uk CC: a.zummo@towertech.it, rtc-linux@googlegroups.com Subject: [rtc-linux] [PATCH 4/5] RTC driver for MX1 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 --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to "rtc-linux". Membership options at http://groups.google.com/group/rtc-linux . Please read http://groups.google.com/group/rtc-linux/web/checklist before submitting a driver. -~----------~----~----~----~------~----~------~--~--- From: Paulius Zaleckas Add i.MX1/L RTC driver Signed-off-by: Paulius Zaleckas Index: linux-2.6.29-rc5/drivers/rtc/rtc-imx.c =================================================================== --- /dev/null +++ linux-2.6.29-rc5/drivers/rtc/rtc-imx.c @@ -0,0 +1,550 @@ +/* + * i.MX SoC RTC driver + * + * Copyright (c) 2008 Paulius Zaleckas + * + * This file may be distributed under the terms of the GNU General + * Public License, version 2. + * + * Hardware bugs/workarounds: + * 1. When alarm seconds is set to odd number false interrupt is generated one + * second before the real one. Workaround is to compare if actual seconds + * is equal to alarm seconds to be sure this is real alarm interrupt. This + * bug and workaround is described in MX1/L erratum. + * 2. 2Hz flag in ISR doesn't generate interrupt. It is needed to generate + * RTC 2Hz periodic interrupts. Workaround is to enable 4Hz sampling + * interrupt and check if 2Hz ISR flag is set. + * 3. SWR (software reset) bit in RTC control register doesn't reset time/date + * registers. Workaround is to reset them one by one manually. + * 4. Not really a bug, but... There is no registers for year and month. + * So we have to calculate month from the days register (value 0-511). + * And it is not possible to have years here... so we will have to handle + * this to platform specific code through set_year() and get_year() functions + * passed by the platform_data. + */ + +#include +#include +#include +#include +#include + +/* + * RTC registers + */ + +#define RCCTL 0x10 /* RTC Control Register */ +#define RTCISR 0x14 /* RTC Interrupt Status Register */ +#define RTCIENR 0x18 /* RTC Interrupt Enable Register */ +#define SECONDS 0x04 /* RTC Seconds Counter Register */ +#define HOURMIN 0x00 /* RTC Hours and Minutes Counter Register */ +#define DAYR 0x20 /* RTC Days Counter Register */ +#define ALRM_SEC 0x0C /* RTC Seconds Alarm Register */ +#define ALRM_HM 0x08 /* RTC Hours and Minutes Alarm Register */ +#define DAYALARM 0x24 /* RTC Day Alarm Register */ + +#define RCCTL_EN (1 << 7) +#define RCCTL_XTL_32KHZ (1 << 5) + +#define RTCI_SAM(x) (1 << ((x) + 8)) +#define RTCI_2HZ (1 << 7) +#define RTCI_1HZ (1 << 4) +#define RTCI_ALM (1 << 2) + +/* See Hardware bugs/workarounds No.2 for details */ +#define RTC_IRQ_MASK 0x007F +#define RTC_SAM_IRQ_MASK 0xFF80 + +#define MIN_MASK 0x3f +#define HOUR_MASK 0x1f +#define HOUR_SHIFT 8 +#define DAY_MAX 511 /* we have 9bit register for days */ + +#define DRV_NAME "rtc-imx" + +struct imx_priv { + struct rtc_device *rtc; + void __iomem *ioaddr; + int irq; + int sam_irq; + size_t size; + unsigned long baseaddr; + struct imxrtc_platform_data *pdata; + int year; + int _32khz; + int per_frq; + unsigned int save_irq; +}; + +static void imx_rtc_int_set(struct imx_priv *rtc, unsigned int rtc_int) +{ + unsigned int temp; + + temp = __raw_readl(rtc->ioaddr + RTCIENR); + __raw_writel(temp | rtc_int, rtc->ioaddr + RTCIENR); +} + +static void imx_rtc_int_clear(struct imx_priv *rtc, unsigned int rtc_int) +{ + unsigned int temp; + + temp = __raw_readl(rtc->ioaddr + RTCIENR); + __raw_writel(temp & ~rtc_int, rtc->ioaddr + RTCIENR); +} + +static irqreturn_t imx_rtc_per_irq(int irq, void *data) +{ + struct imx_priv *priv = data; + unsigned int status; + + /* we serve only interrupts belonging to this irq handler */ + status = __raw_readl(priv->ioaddr + RTCISR) & RTC_SAM_IRQ_MASK; + + /* clear irq status register */ + __raw_writel(status, priv->ioaddr + RTCISR); + + dev_dbg(&priv->rtc->dev, "Sampling irq status: 0x%x\n", status); + + if (unlikely(!status)) + return IRQ_NONE; + + /* See Hardware bugs/workarounds No.2 for details */ + if ((__raw_readl(priv->ioaddr + RTCIENR) & RTCI_2HZ) && + (status & RTCI_SAM(0)) && !(status & RTCI_2HZ)) + return IRQ_HANDLED; + + rtc_update_irq(priv->rtc, 1, RTC_IRQF | RTC_PF); + + return IRQ_HANDLED; +} + +static irqreturn_t imx_rtc_irq(int irq, void *data) +{ + struct imx_priv *priv = data; + unsigned long events = 0; + unsigned long number = 0; + unsigned int status; + irqreturn_t ret = IRQ_NONE; + + /* we serve only interrupts belonging to this irq handler */ + status = __raw_readl(priv->ioaddr + RTCISR) & RTC_IRQ_MASK; + + /* clear irq status register */ + __raw_writel(status, priv->ioaddr + RTCISR); + + dev_dbg(&priv->rtc->dev, "RTC irq status: 0x%x\n", status); + + if (status & RTCI_ALM) { + /* See Hardware bugs/workarounds No.1 for details */ + if (__raw_readl(priv->ioaddr + SECONDS) == + __raw_readl(priv->ioaddr + ALRM_SEC)) { + events |= RTC_AF | RTC_IRQF; + number++; + /* + * disable irq, because it will be triggered again after + * 512 days. Alarm is single-shot event. + */ + imx_rtc_int_clear(priv, RTCI_ALM); + } else { + ret = IRQ_HANDLED; + } + } + + if (status & RTCI_1HZ) { + events |= RTC_IRQF | RTC_UF; + number++; + } + + if (events) { + rtc_update_irq(priv->rtc, number, events); + ret = IRQ_HANDLED; + } + + return ret; +} + +static int imx_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct imx_priv *priv = dev_get_drvdata(dev); + int temp; + + tm->tm_sec = __raw_readl(priv->ioaddr + SECONDS); + temp = __raw_readl(priv->ioaddr + HOURMIN); + tm->tm_min = temp & MIN_MASK; + tm->tm_hour = (temp >> HOUR_SHIFT) & HOUR_MASK; + tm->tm_yday = __raw_readl(priv->ioaddr + DAYR); + /* + * Maybe our day counter leaped over the year? + * See Hardware bugs/workarounds No.4 for details + */ + temp = rtc_year_days(1, 12, priv->year); + if (tm->tm_yday > temp) { + priv->year++; + if (priv->pdata && priv->pdata->set_year) + priv->pdata->set_year(dev, priv->year); + tm->tm_yday -= temp; + /* Write new days value back to register */ + __raw_writel(tm->tm_yday, priv->ioaddr + DAYR); + } + tm->tm_year = priv->year; + /* calculate month and month day from year days */ + tm->tm_mon = 0; + tm->tm_mday = tm->tm_yday; + temp = rtc_month_days(0, tm->tm_year); + while(tm->tm_mday > temp) { + tm->tm_mon++; + tm->tm_mday -= temp; + temp = rtc_month_days(tm->tm_mon, tm->tm_year); + } + /* first day in month is 1 */ + tm->tm_mday++; + + return 0; +} + +static int imx_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct imx_priv *priv = dev_get_drvdata(dev); + int temp; + + __raw_writel(tm->tm_sec, priv->ioaddr + SECONDS); + temp = tm->tm_min | (tm->tm_hour << HOUR_SHIFT); + __raw_writel(temp, priv->ioaddr + HOURMIN); + temp = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year); + __raw_writel(temp, priv->ioaddr + DAYR); + priv->year = tm->tm_year; + /* See Hardware bugs/workarounds No.4 for details */ + if (priv->pdata && priv->pdata->set_year) + priv->pdata->set_year(dev, priv->year); + + return 0; +} + +static int imx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct imx_priv *priv = dev_get_drvdata(dev); + struct rtc_time *tm = &alm->time; + int temp; + + tm->tm_sec = __raw_readl(priv->ioaddr + ALRM_SEC); + temp = __raw_readl(priv->ioaddr + ALRM_HM); + tm->tm_min = temp & MIN_MASK; + tm->tm_hour = (temp >> HOUR_SHIFT) & HOUR_MASK; + tm->tm_yday = __raw_readl(priv->ioaddr + DAYALARM); + tm->tm_year = priv->year; + /* Maybe our day counter leaped over the year? */ + temp = rtc_year_days(1, 12, priv->year); + if (tm->tm_yday > temp) { + tm->tm_year++; + tm->tm_yday -= temp; + } + /* calculate month and month day from year days */ + tm->tm_mon = 0; + tm->tm_mday = tm->tm_yday; + temp = rtc_month_days(0, tm->tm_year); + while(tm->tm_mday > temp) { + tm->tm_mon++; + tm->tm_mday -= temp; + temp = rtc_month_days(tm->tm_mon, tm->tm_year); + } + /* first day in month is 1 */ + tm->tm_mday++; + + alm->enabled = !!(__raw_readl(priv->ioaddr + RTCIENR) & RTCI_ALM); + + return 0; +} + +static int imx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct imx_priv *priv = dev_get_drvdata(dev); + struct rtc_time *tm = &alm->time; + int temp = 0; + + if (tm->tm_year != priv->year) { + /* This RTC doesn't support alarms longer than 2 years */ + if (tm->tm_year - priv->year > 1) + return -EINVAL; + temp = rtc_year_days(1, 12, priv->year); + } + temp += rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year); + /* check if we will fit in days register */ + if (temp > DAY_MAX) + return -EINVAL; + __raw_writel(temp, priv->ioaddr + DAYALARM); + __raw_writel(tm->tm_sec, priv->ioaddr + ALRM_SEC); + temp = tm->tm_min | (tm->tm_hour << HOUR_SHIFT); + __raw_writel(temp, priv->ioaddr + ALRM_HM); + + if (alm->enabled) + imx_rtc_int_set(priv, RTCI_ALM); + else + imx_rtc_int_clear(priv, RTCI_ALM); + + return 0; +} + +static int imx_rtc_set_per_state(struct device *dev, int enabled) +{ + struct imx_priv *priv = dev_get_drvdata(dev); + + /* periodic interrupts are not supported if crystal is 32kHz */ + if (priv->_32khz) + return -ENXIO; + + if (enabled) + { + /* See Hardware bugs/workarounds No.2 for details */ + if (priv->per_frq == 2) + imx_rtc_int_set(priv, RTCI_2HZ | RTCI_SAM(0)); + else + imx_rtc_int_set(priv, RTCI_SAM(ilog2(priv->per_frq) - 2)); + } else { + imx_rtc_int_clear(priv, RTCI_SAM(7) | RTCI_SAM(6) | + RTCI_SAM(5) | RTCI_SAM(4) | + RTCI_SAM(3) | RTCI_SAM(2) | + RTCI_SAM(1) | RTCI_SAM(0) | RTCI_2HZ); + } + + return 0; +} + +static int imx_rtc_set_per_freq(struct device *dev, int freq) +{ + struct imx_priv *priv = dev_get_drvdata(dev); + + /* periodic interrupts are not supported if crystal is 32kHz */ + if (priv->_32khz) + return -ENXIO; + + if (freq > 512) + return -EINVAL; + + priv->per_frq = freq; + + /* reinitialize irq for new frequency if periodic irq was enabled */ + if (__raw_readl(priv->ioaddr + RTCIENR) & (RTCI_SAM(7) | RTCI_SAM(6) | + RTCI_SAM(5) | RTCI_SAM(4) | RTCI_SAM(3) | RTCI_SAM(2) | + RTCI_SAM(1) | RTCI_SAM(0) | RTCI_2HZ)) { + imx_rtc_set_per_state(dev, 0); + imx_rtc_set_per_state(dev, 1); + } + + return 0; +} + +static int +imx_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +{ + struct imx_priv *priv = dev_get_drvdata(dev); + + switch (cmd) { + /* AIE = Alarm Interrupt Enable */ + case RTC_AIE_OFF: + imx_rtc_int_clear(priv, RTCI_ALM); + break; + case RTC_AIE_ON: + imx_rtc_int_set(priv, RTCI_ALM); + break; + /* UIE = Update Interrupt Enable (1/second) */ + case RTC_UIE_OFF: + imx_rtc_int_clear(priv, RTCI_1HZ); + break; + case RTC_UIE_ON: + imx_rtc_int_set(priv, RTCI_1HZ); + break; + default: + return -ENOIOCTLCMD; + } + + return 0; +} + +static const struct rtc_class_ops imx_rtc_ops = { + .read_time = imx_rtc_read_time, + .set_time = imx_rtc_set_time, + .read_alarm = imx_rtc_read_alarm, + .set_alarm = imx_rtc_set_alarm, + .irq_set_state = imx_rtc_set_per_state, + .irq_set_freq = imx_rtc_set_per_freq, + .ioctl = imx_rtc_ioctl, +}; + +static int imx_rtc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct imx_priv *priv = platform_get_drvdata(pdev); + + priv->save_irq = __raw_readl(priv->ioaddr + RTCIENR); + + if (device_may_wakeup(&pdev->dev) && (priv->save_irq & RTCI_ALM)) { + priv->save_irq &= ~RTCI_ALM; + enable_irq_wake(priv->irq); + } + + imx_rtc_int_clear(priv, priv->save_irq); + + return 0; +} + +static int imx_rtc_resume(struct platform_device *pdev) +{ + struct imx_priv *priv = platform_get_drvdata(pdev); + + if (device_may_wakeup(&pdev->dev) && (priv->save_irq & RTCI_ALM)) + disable_irq_wake(priv->irq); + + imx_rtc_int_set(priv, priv->save_irq); + + return 0; +} + +static int __init imx_rtc_probe(struct platform_device *pdev) +{ + struct rtc_device *rtc; + struct resource *res; + int irq; + struct imx_priv *priv; + struct rtc_time tm; + int ret = 0; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + priv = kzalloc(sizeof *priv, GFP_KERNEL); + if (!priv) + return -ENOMEM; + priv->sam_irq = -1; + priv->size = res->end - res->start + 1; + if (!request_mem_region(res->start, priv->size, pdev->name)) { + ret = -EBUSY; + goto out; + } + priv->baseaddr = res->start; + priv->irq = irq; + priv->ioaddr = ioremap(priv->baseaddr, priv->size); + if (!priv->ioaddr) { + ret = -ENOMEM; + goto out; + } + + device_init_wakeup(&pdev->dev, 1); + + rtc = rtc_device_register(pdev->name, &pdev->dev, + &imx_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc)) { + ret = PTR_ERR(rtc); + goto out; + } + priv->rtc = rtc; + platform_set_drvdata(pdev, priv); + + /* request irq */ + ret = request_irq(priv->irq, imx_rtc_irq, 0, DRV_NAME, priv); + if (ret) + goto out; + + priv->pdata = pdev->dev.platform_data; + + priv->year = 70; + + if (!priv->pdata) { + dev_warn(&pdev->dev, "No platform data provided\n"); + priv->_32khz = 0; + } else { + priv->_32khz = priv->pdata->freq_is_32khz; + /* See Hardware bugs/workarounds No.4 for details */ + if (priv->pdata->get_year) + priv->year = priv->pdata->get_year(&pdev->dev); + } + + /* Reset the RTC if it has invalid values */ + imx_rtc_read_time(&pdev->dev, &tm); + if (rtc_valid_tm(&tm)) + { + dev_dbg(&pdev->dev, "Invalid time. Resetting RTC.\n"); + /* See Hardware bugs/workarounds No.3 for details */ + __raw_writel(0, priv->ioaddr + SECONDS); + __raw_writel(0, priv->ioaddr + HOURMIN); + __raw_writel(0, priv->ioaddr + DAYR); + } + + /* Initialize RTC */ + if (priv->_32khz) { + __raw_writel(RCCTL_EN | RCCTL_XTL_32KHZ, priv->ioaddr + RCCTL); + } else { + __raw_writel(RCCTL_EN, priv->ioaddr + RCCTL); + priv->sam_irq = platform_get_irq(pdev, 1); + if (priv->sam_irq >= 0) + if(request_irq(priv->sam_irq, imx_rtc_per_irq, 0, + DRV_NAME, priv)) + priv->sam_irq = -1; + /* + * hack to disable periodic irq if we failed to + * register RTC sampling interrupt. + */ + if (priv->sam_irq < 0) { + dev_warn(&pdev->dev, "Failed to register i.MX RTC " + "sampling interrupt\n"); + priv->_32khz = 1; + } + } + + return 0; + +out: + if (priv->sam_irq >= 0) + free_irq(priv->sam_irq, priv); + if (priv->rtc) + rtc_device_unregister(priv->rtc); + if (priv->ioaddr) + iounmap(priv->ioaddr); + if (priv->baseaddr) + release_mem_region(priv->baseaddr, priv->size); + kfree(priv); + return ret; +} + +static int __exit imx_rtc_remove(struct platform_device *pdev) +{ + struct imx_priv *priv = platform_get_drvdata(pdev); + + if (priv->sam_irq >= 0) + free_irq(priv->sam_irq, priv); + free_irq(priv->irq, priv); + rtc_device_unregister(priv->rtc); + iounmap(priv->ioaddr); + release_mem_region(priv->baseaddr, priv->size); + kfree(priv); + return 0; +} + +static struct platform_driver imx_rtc_platform_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .remove = __exit_p(imx_rtc_remove), + .suspend = imx_rtc_suspend, + .resume = imx_rtc_resume, +}; + +static int __init imx_rtc_init(void) +{ + return platform_driver_probe(&imx_rtc_platform_driver, imx_rtc_probe); +} + +static void __exit imx_rtc_exit(void) +{ + platform_driver_unregister(&imx_rtc_platform_driver); +} + +module_init(imx_rtc_init); +module_exit(imx_rtc_exit); + +MODULE_AUTHOR("Paulius Zaleckas "); +MODULE_DESCRIPTION("i.MX SoC RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); Index: linux-2.6.29-rc5/drivers/rtc/Kconfig =================================================================== --- linux-2.6.29-rc5.orig/drivers/rtc/Kconfig +++ linux-2.6.29-rc5/drivers/rtc/Kconfig @@ -736,4 +736,14 @@ config RTC_DRV_MV This driver can also be built as a module. If so, the module will be called rtc-mv. +config RTC_DRV_IMX + tristate "i.MX On-Chip RTC" + depends on ARCH_MX1 + help + If you say yes here you will get support for the + i.MX On-Chip Real Time Clock. + + This driver can also be built as a module. If so, the module + will be called rtc-imx. + endif # RTC_CLASS Index: linux-2.6.29-rc5/drivers/rtc/Makefile =================================================================== --- linux-2.6.29-rc5.orig/drivers/rtc/Makefile +++ linux-2.6.29-rc5/drivers/rtc/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds17 obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o +obj-$(CONFIG_RTC_DRV_IMX) += rtc-imx.o obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o Index: linux-2.6.29-rc5/arch/arm/plat-mxc/include/mach/rtc.h =================================================================== --- /dev/null +++ linux-2.6.29-rc5/arch/arm/plat-mxc/include/mach/rtc.h @@ -0,0 +1,19 @@ +/* + * rtc.h - i.MX RTC driver header file + * + * Copyright (c) 2008, Paulius Zaleckas + * + * This file is released under the GPLv2 + */ + +#ifndef __ASM_ARCH_RTC_H_ +#define __ASM_ARCH_RTC_H_ + +struct imxrtc_platform_data { + void (*set_year)(struct device *, int); + int (*get_year)(struct device *); + + int freq_is_32khz; +}; + +#endif /* __ASM_ARCH_RTC_H_ */