From patchwork Wed Mar 25 09:08:08 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Darius Augulis X-Patchwork-Id: 25063 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.24]) by ozlabs.org (Postfix) with ESMTP id 4B94BDDF9D for ; Wed, 25 Mar 2009 20:10:25 +1100 (EST) Received: by yx-out-2122.google.com with SMTP id 8so1222294yxj.11 for ; Wed, 25 Mar 2009 02:10:23 -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=KBUK8uGVSMm9YE8rByKHE3qkhsaWNpqBaQKIsjZaFZM=; b=qlyv9H/FEf5MuoRNcGU2+IKoTh0vuUi0/8crvOsCe5bOaepAmr2h/2qU3ZXWF2sMO5 8888+6SC1XZvoA1psv1uA4rciA/qKuPGiOD1/XuDKGFxcUPl9o9BWNm2t8GARbPag5Pr dHY5KwB+FejKak0FqMy6sfiw45L3ux5YUxSvU= 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=cBNU1Tvoloi5n/IWmntqlLnXX3FCNizKwJ0+bv9sGisdNG3lyDeHYO5mhz4BTUAk5J m2v52AAOYdpJM0GtOAIQo9yyPWDHA/HA2RducMeMAwVdBDfPoRK3LueriP9IQkAJgxHD iah3EFn33I11y9EUJMTOouM2N035wyXAsLvxw= Received: by 10.151.145.12 with SMTP id x12mr1105698ybn.23.1237972220360; Wed, 25 Mar 2009 02:10:20 -0700 (PDT) Received: by 10.177.37.21 with SMTP id p21gr2038yqj.0; Wed, 25 Mar 2009 02:10:20 -0700 (PDT) X-Sender: augulis.darius@gmail.com X-Apparently-To: rtc-linux@googlegroups.com Received: by 10.204.55.200 with SMTP id v8mr229034bkg.19.1237972219509; Wed, 25 Mar 2009 02:10:19 -0700 (PDT) Received: from mail-bw0-f208.google.com (mail-bw0-f208.google.com [209.85.218.208]) by gmr-mx.google.com with ESMTP id 16si473566fxm.2.2009.03.25.02.10.18; Wed, 25 Mar 2009 02:10:18 -0700 (PDT) Received-SPF: pass (google.com: domain of augulis.darius@gmail.com designates 209.85.218.208 as permitted sender) client-ip=209.85.218.208; Authentication-Results: gmr-mx.google.com; spf=pass (google.com: domain of augulis.darius@gmail.com designates 209.85.218.208 as permitted sender) smtp.mail=augulis.darius@gmail.com; dkim=pass (test mode) header.i=@gmail.com Received: by mail-bw0-f208.google.com with SMTP id 4so2512988bwz.25 for ; Wed, 25 Mar 2009 02:10:18 -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=UNXE73nTmI1A+GOg7k6jo3pLSv/4QZp6ZNURn1O+Z04=; b=jqox+Q1iSMgUrjNOxJuQalJNakSJPbZKZPuppXrIqlChdyrT6wa/0gB7JgVXQOuZFt 2ZiFOm/sD2b5Ex15pjbiVlF0YkDTgtIvt3xJKINumFp7EOPVCK8zaIDDkGIYF0stojdu GrMeeEZOAuBPRjlC+nHZFdDL3hkvM7DNcgJ7o= 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=FVzMm0iZND70vWSz1kmoC+1dRXuMUSBBIRtG9eCgudgMqrDZUxyYjW8mJFDhyqYwZs 8WqzyiK8KAV+MOE3gpOtU9UrkuffVY0L4HCLCwHo88O7h75zGfZd71OQh0j42FkyMMgC syZIFb3rjfMhL+e3fByjmHBvq5NBDS82fEZJw= Received: by 10.223.107.68 with SMTP id a4mr7913233fap.104.1237972217280; Wed, 25 Mar 2009 02:10:17 -0700 (PDT) Received: from localhost.localdomain ([217.147.39.138]) by mx.google.com with ESMTPS id e17sm9815068fke.8.2009.03.25.02.10.16 (version=TLSv1/SSLv3 cipher=RC4-MD5); Wed, 25 Mar 2009 02:10:17 -0700 (PDT) Message-ID: <49C9F478.6010509@gmail.com> Date: Wed, 25 Mar 2009 11:08:08 +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: Sascha Hauer , a.zummo@towertech.it, rtc-linux@googlegroups.com Subject: [rtc-linux] [PATCH V2 3/9] New drivers for MXC: add RTC support 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/drivers/rtc/rtc-imx.c =================================================================== --- /dev/null +++ linux-2.6.29/drivers/rtc/rtc-imx.c @@ -0,0 +1,553 @@ +/* + * 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 f_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->f_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->f_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, +}; + +#ifdef CONFIG_PM +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; +} +#else +#define imx_rtc_suspend NULL +#define imx_rtc_resume NULL +#endif + +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 = resource_size(res); + 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->f_32khz = 0; + } else { + priv->f_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->f_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->f_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/drivers/rtc/Kconfig =================================================================== --- linux-2.6.29.orig/drivers/rtc/Kconfig +++ linux-2.6.29/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/drivers/rtc/Makefile =================================================================== --- linux-2.6.29.orig/drivers/rtc/Makefile +++ linux-2.6.29/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/arch/arm/plat-mxc/include/mach/rtc.h =================================================================== --- /dev/null +++ linux-2.6.29/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_ */