From patchwork Fri Oct 16 22:35:32 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: miguel.aguilar@ridgerun.com X-Patchwork-Id: 36277 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from mail-qy0-f142.google.com (mail-qy0-f142.google.com [209.85.221.142]) by ozlabs.org (Postfix) with ESMTP id 0819DB7088 for ; Sat, 17 Oct 2009 09:35:35 +1100 (EST) Received: by qyk6 with SMTP id 6so1952978qyk.1 for ; Fri, 16 Oct 2009 15:35:33 -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:from:to:cc:date :message-id:x-mailer:x-sa-exim-connect-ip:x-sa-exim-mail-from :x-spam-checker-version:x-spam-level:x-spam-status:subject :x-sa-exim-version:x-sa-exim-scanned:reply-to:sender:precedence :x-google-loop:mailing-list:list-id:list-post:list-help :list-unsubscribe:x-beenthere-env:x-beenthere; bh=eC1VSv3e7nTc54fLdrrTI1mO/C5c9V6EiLL58g/kA1Q=; b=OtUR06vFm4Y50eo0MfFuKDYcwThbG9wfeXCxdz83t3RVgXXfLb4GN9Vt/E/fisJMAv rzLbrVRbaCMssOR4kOO5cKUxnQ9czU+RoT1/Ev+Cohr0qdJjw6TNOhvallLWangaua+q fh/I+KYwRZumdDRkbPLxTumafKBt5lqAIrO7Q= DomainKey-Signature: a=rsa-sha1; c=nofws; d=googlegroups.com; s=beta; h=x-sender:x-apparently-to:received-spf:authentication-results:from :to:cc:date:message-id:x-mailer:x-sa-exim-connect-ip :x-sa-exim-mail-from:x-spam-checker-version:x-spam-level :x-spam-status:subject:x-sa-exim-version:x-sa-exim-scanned:reply-to :sender:precedence:x-google-loop:mailing-list:list-id:list-post :list-help:list-unsubscribe:x-beenthere-env:x-beenthere; b=rSkea8F7lfE+IzMCFci4CkRzJjWDjcQlznOYXhqSYJkhh3KTk29cCC0+gsfVLEgclT /1GjG/eVhpPGfl10nRLS73lGKsAc0k7kAwpJQ+TRCgdRiNa0BczfauOaZAEw5DBQcGqA 8LTUS+ssBJS2xGC4CGZbn1XA5MkvokIi2MXak= Received: by 10.224.112.78 with SMTP id v14mr119417qap.22.1255732531751; Fri, 16 Oct 2009 15:35:31 -0700 (PDT) Received: by 10.177.5.4 with SMTP id h4gr2970yqi.0; Fri, 16 Oct 2009 15:35:31 -0700 (PDT) X-Sender: miguel.aguilar@ridgerun.com X-Apparently-To: rtc-linux@googlegroups.com Received: by 10.231.10.18 with SMTP id n18mr73220ibn.13.1255732530161; Fri, 16 Oct 2009 15:35:30 -0700 (PDT) Received: by 10.231.10.18 with SMTP id n18mr73219ibn.13.1255732530131; Fri, 16 Oct 2009 15:35:30 -0700 (PDT) Received: from mail.navvo.net (mail.navvo.net [74.208.67.6]) by gmr-mx.google.com with ESMTP id 18si119545iwn.5.2009.10.16.15.35.29; Fri, 16 Oct 2009 15:35:29 -0700 (PDT) Received-SPF: pass (google.com: domain of miguel.aguilar@ridgerun.com designates 74.208.67.6 as permitted sender) client-ip=74.208.67.6; Authentication-Results: gmr-mx.google.com; spf=pass (google.com: domain of miguel.aguilar@ridgerun.com designates 74.208.67.6 as permitted sender) smtp.mail=miguel.aguilar@ridgerun.com Received: from [201.198.127.70] (helo=localhost.localdomain) by mail.navvo.net with esmtpa (Exim 4.63) (envelope-from ) id 1MyvOQ-0005R8-3u; Fri, 16 Oct 2009 17:35:28 -0500 From: miguel.aguilar@ridgerun.com To: davinci-linux-open-source@linux.davincidsp.com, nsnehaprabha@ti.com, rtc-linux@googlegroups.com Cc: todd.fischer@ridgerun.com, diego.dompe@ridgerun.com, clark.becker@ridgerun.com, santiago.nunez@ridgerun.com, Miguel Aguilar Date: Fri, 16 Oct 2009 16:35:32 -0600 Message-Id: <1255732532-14047-1-git-send-email-miguel.aguilar@ridgerun.com> X-Mailer: git-send-email 1.6.0.4 X-SA-Exim-Connect-IP: 201.198.127.70 X-SA-Exim-Mail-From: miguel.aguilar@ridgerun.com X-Spam-Checker-Version: SpamAssassin 3.1.7-deb (2006-10-05) on mail.navvo.net X-Spam-Level: X-Spam-Status: No, score=-3.3 required=5.0 tests=ALL_TRUSTED, AWL, BAYES_00, NO_REAL_NAME, UPPERCASE_25_50 autolearn=ham version=3.1.7-deb Subject: [rtc-linux] [PATCH 1/2] RTC: DaVinci RTC driver X-SA-Exim-Version: 4.2.1 (built Tue, 09 Jan 2007 17:23:22 +0000) X-SA-Exim-Scanned: Yes (on mail.navvo.net) 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 From: Miguel Aguilar This driver features: * Alarm support. * Periodic interrupt by using a timer include into the RTC module. * The update interrupt is not supported by this RTC module. This driver was tested on a DM365 EVM by using the rtc-test application from the Documentation/rtc.txt. Signed-off-by: Miguel Aguilar --- drivers/rtc/Kconfig | 10 + drivers/rtc/Makefile | 1 + drivers/rtc/rtc-davinci.c | 671 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 682 insertions(+), 0 deletions(-) create mode 100644 drivers/rtc/rtc-davinci.c diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 3c20dae..61bd4d6 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -576,6 +576,16 @@ config RTC_DRV_AB3100 comment "on-CPU RTC drivers" +config RTC_DRV_DAVINCI + tristate "TI DaVinci RTC" + depends on ARCH_DAVINCI_DM365 + help + If you say yes here you get support for the RTC on the + DaVinci platforms (DM365). + + This driver can also be built as a module. If so, the module + will be called rtc-davinci. + config RTC_DRV_OMAP tristate "TI OMAP1" depends on ARCH_OMAP15XX || ARCH_OMAP16XX || ARCH_OMAP730 diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index aa3fbd5..d968694 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o +obj-$(CONFIG_RTC_DRV_DAVINCI) += rtc-davinci.o obj-$(CONFIG_RTC_DRV_DM355EVM) += rtc-dm355evm.o obj-$(CONFIG_RTC_DRV_DS1216) += rtc-ds1216.o obj-$(CONFIG_RTC_DRV_DS1286) += rtc-ds1286.o diff --git a/drivers/rtc/rtc-davinci.c b/drivers/rtc/rtc-davinci.c new file mode 100644 index 0000000..d691a58 --- /dev/null +++ b/drivers/rtc/rtc-davinci.c @@ -0,0 +1,671 @@ +/* + * DaVinci Power Management and Real Time Clock Driver for TI platforms + * + * Copyright (C) 2009 Texas Instruments, Inc + * + * Author: Miguel Aguilar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DM365_RTC_BASE 0x01c69000 + +/* + * The DaVinci RTC is a simple RTC with the following + * Sec: 0 - 59 : BCD count + * Min: 0 - 59 : BCD count + * Hour: 0 - 23 : BCD count + * Day: 0 - 0x7FFF(32767) : Binary count ( Over 89 years ) + */ + +/* PRTC interface registers */ +#define DAVINCI_PRTCIF_PID 0x00 +#define DAVINCI_PRTCIF_CTLR 0x04 +#define DAVINCI_PRTCIF_LDATA 0x08 +#define DAVINCI_PRTCIF_UDATA 0x0C +#define DAVINCI_PRTCIF_INTEN 0x10 +#define DAVINCI_PRTCIF_INTFLG 0x14 + +/* DAVINCI_PRTCIF_CTLR bit fields */ +#define DAVINCI_PRTCIF_CTLR_BUSY (1<<31) +#define DAVINCI_PRTCIF_CTLR_SIZE (1<<25) +#define DAVINCI_PRTCIF_CTLR_DIR (1<<24) +#define DAVINCI_PRTCIF_CTLR_BENU_MSB (1<<23) +#define DAVINCI_PRTCIF_CTLR_BENU_3RD_BYTE (1<<22) +#define DAVINCI_PRTCIF_CTLR_BENU_2ND_BYTE (1<<21) +#define DAVINCI_PRTCIF_CTLR_BENU_LSB (1<<20) +#define DAVINCI_PRTCIF_CTLR_BENU_MASK (0x00F00000) +#define DAVINCI_PRTCIF_CTLR_BENL_MSB (1<<19) +#define DAVINCI_PRTCIF_CTLR_BENL_3RD_BYTE (1<<18) +#define DAVINCI_PRTCIF_CTLR_BENL_2ND_BYTE (1<<17) +#define DAVINCI_PRTCIF_CTLR_BENL_LSB (1<<16) +#define DAVINCI_PRTCIF_CTLR_BENL_MASK (0x000F0000) + +/* DAVINCI_PRTCIF_INTEN bit fields */ +#define DAVINCI_PRTCIF_INTEN_RTCSS (1<<1) +#define DAVINCI_PRTCIF_INTEN_RTCIF (1<<0) +#define DAVINCI_PRTCIF_INTEN_MASK (DAVINCI_PRTCIF_INTEN_RTCSS \ + | DAVINCI_PRTCIF_INTEN_RTCIF) + +/* DAVINCI_PRTCIF_INTFLG bit fields */ +#define DAVINCI_PRTCIF_INTFLG_RTCSS (1<<1) +#define DAVINCI_PRTCIF_INTFLG_RTCIF (1<<0) +#define DAVINCI_PRTCIF_INTFLG_MASK (DAVINCI_PRTCIF_INTFLG_RTCSS \ + | DAVINCI_PRTCIF_INTFLG_RTCIF) + +/* PRTC subsystem registers */ +#define DAVINCI_PRTCSS_RTC_INTC_EXTENA1 (0x0C) +#define DAVINCI_PRTCSS_RTC_CTRL (0x10) +#define DAVINCI_PRTCSS_RTC_WDT (0x11) +#define DAVINCI_PRTCSS_RTC_TMR0 (0x12) +#define DAVINCI_PRTCSS_RTC_TMR1 (0x13) +#define DAVINCI_PRTCSS_RTC_CCTRL (0x14) +#define DAVINCI_PRTCSS_RTC_SEC (0x15) +#define DAVINCI_PRTCSS_RTC_MIN (0x16) +#define DAVINCI_PRTCSS_RTC_HOUR (0x17) +#define DAVINCI_PRTCSS_RTC_DAY0 (0x18) +#define DAVINCI_PRTCSS_RTC_DAY1 (0x19) +#define DAVINCI_PRTCSS_RTC_AMIN (0x1A) +#define DAVINCI_PRTCSS_RTC_AHOUR (0x1B) +#define DAVINCI_PRTCSS_RTC_ADAY0 (0x1C) +#define DAVINCI_PRTCSS_RTC_ADAY1 (0x1D) +#define DAVINCI_PRTCSS_RTC_CLKC_CNT (0x20) + +/* DAVINCI_PRTCSS_RTC_INTC_EXTENA1 */ +#define DAVINCI_PRTCSS_RTC_INTC_EXTENA1_MASK (0x07) + +/* DAVINCI_PRTCSS_RTC_CTRL bit fields */ +#define DAVINCI_PRTCSS_RTC_CTRL_WDTBUS (1<<7) +#define DAVINCI_PRTCSS_RTC_CTRL_WEN (1<<6) +#define DAVINCI_PRTCSS_RTC_CTRL_WDRT (1<<5) +#define DAVINCI_PRTCSS_RTC_CTRL_WDTFLG (1<<4) +#define DAVINCI_PRTCSS_RTC_CTRL_TE (1<<3) +#define DAVINCI_PRTCSS_RTC_CTRL_TIEN (1<<2) +#define DAVINCI_PRTCSS_RTC_CTRL_TMRFLG (1<<1) +#define DAVINCI_PRTCSS_RTC_CTRL_TMMD (1<<0) + +/* DAVINCI_PRTCSS_RTC_CCTRL bit fields */ +#define DAVINCI_PRTCSS_RTC_CCTRL_CALBUSY (1<<7) +#define DAVINCI_PRTCSS_RTC_CCTRL_DAEN (1<<5) +#define DAVINCI_PRTCSS_RTC_CCTRL_HAEN (1<<4) +#define DAVINCI_PRTCSS_RTC_CCTRL_MAEN (1<<3) +#define DAVINCI_PRTCSS_RTC_CCTRL_ALMFLG (1<<2) +#define DAVINCI_PRTCSS_RTC_CCTRL_AIEN (1<<1) +#define DAVINCI_PRTCSS_RTC_CCTRL_CAEN (1<<0) + +static DEFINE_SPINLOCK(davinci_rtc_lock); + +struct davinci_rtc { + struct rtc_device *rtc; + void __iomem *base; + resource_size_t pbase; + size_t base_size; + int irq; +} *davinci_rtc; + +static void davinci_rtcif_write(u32 val, u32 addr) +{ + __raw_writel(val, davinci_rtc->base + addr); +} + +static u32 davinci_rtcif_read(u32 addr) +{ + return __raw_readl(davinci_rtc->base + addr); +} + +static void davinci_rtcss_write(unsigned long val, u8 addr) +{ + while (davinci_rtcif_read(DAVINCI_PRTCIF_CTLR) & + DAVINCI_PRTCIF_CTLR_BUSY); + + davinci_rtcif_write(DAVINCI_PRTCIF_CTLR_BENL_LSB | addr, + DAVINCI_PRTCIF_CTLR); + davinci_rtcif_write(val, DAVINCI_PRTCIF_LDATA); + + while (davinci_rtcif_read(DAVINCI_PRTCIF_CTLR) & + DAVINCI_PRTCIF_CTLR_BUSY); +} + +static u8 davinci_rtcss_read(u8 addr) +{ + while (davinci_rtcif_read(DAVINCI_PRTCIF_CTLR) & + DAVINCI_PRTCIF_CTLR_BUSY); + + davinci_rtcif_write(DAVINCI_PRTCIF_CTLR_DIR | + DAVINCI_PRTCIF_CTLR_BENL_LSB | + addr, DAVINCI_PRTCIF_CTLR); + + while (davinci_rtcif_read(DAVINCI_PRTCIF_CTLR) & + DAVINCI_PRTCIF_CTLR_BUSY); + + return davinci_rtcif_read(DAVINCI_PRTCIF_LDATA); +} + +static irqreturn_t davinci_rtc_interrupt(int irq, void *class_dev) +{ + unsigned long events = 0; + u32 irq_flg; + u8 alm_irq, tmr_irq; + u8 rtc_ctrl, rtc_cctrl; + int ret = IRQ_NONE; + + irq_flg = davinci_rtcif_read(DAVINCI_PRTCIF_INTFLG) & + DAVINCI_PRTCIF_INTFLG_RTCSS; + + alm_irq = davinci_rtcss_read(DAVINCI_PRTCSS_RTC_CCTRL) & + DAVINCI_PRTCSS_RTC_CCTRL_ALMFLG; + + tmr_irq = davinci_rtcss_read(DAVINCI_PRTCSS_RTC_CTRL) & + DAVINCI_PRTCSS_RTC_CTRL_TMRFLG; + + if (irq_flg) { + if (alm_irq) { + events |= RTC_IRQF | RTC_AF; + rtc_cctrl = davinci_rtcss_read(DAVINCI_PRTCSS_RTC_CCTRL); + rtc_cctrl |= DAVINCI_PRTCSS_RTC_CCTRL_ALMFLG; + davinci_rtcss_write(rtc_cctrl, DAVINCI_PRTCSS_RTC_CCTRL); + } else if (tmr_irq) { + events |= RTC_IRQF | RTC_PF; + rtc_ctrl = davinci_rtcss_read(DAVINCI_PRTCSS_RTC_CTRL); + rtc_ctrl |= DAVINCI_PRTCSS_RTC_CTRL_TMRFLG; + davinci_rtcss_write(rtc_ctrl, DAVINCI_PRTCSS_RTC_CTRL); + } + + davinci_rtcif_write(DAVINCI_PRTCIF_INTFLG_RTCSS, + DAVINCI_PRTCIF_INTFLG); + rtc_update_irq(class_dev, 1, events); + ret = IRQ_HANDLED; + } + + return ret; +} + +static int +davinci_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +{ + u8 rtc_ctrl; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&davinci_rtc_lock, flags); + + rtc_ctrl = davinci_rtcss_read(DAVINCI_PRTCSS_RTC_CTRL); + + switch (cmd) { + case RTC_WIE_ON: + rtc_ctrl |= (DAVINCI_PRTCSS_RTC_CTRL_WEN | + DAVINCI_PRTCSS_RTC_CTRL_WDTFLG); + break; + case RTC_WIE_OFF: + rtc_ctrl &= ~DAVINCI_PRTCSS_RTC_CTRL_WEN; + break; + case RTC_UIE_OFF: + case RTC_UIE_ON: + ret = -ENOTTY; + break; + default: + ret = -ENOIOCTLCMD; + } + + davinci_rtcss_write(rtc_ctrl, DAVINCI_PRTCSS_RTC_CTRL); + + spin_unlock_irqrestore(&davinci_rtc_lock, flags); + + return ret; +} + +static int convertfromdays(u16 days, struct rtc_time *tm) +{ + int tmp_days, year, mon; + + for (year = 2000;; year++) { + tmp_days = rtc_year_days(1, 12, year); + if (days >= tmp_days) + days -= tmp_days; + else { + for (mon = 0;; mon++) { + tmp_days = rtc_month_days(mon, year); + if (days >= tmp_days) { + days -= tmp_days; + } else { + tm->tm_year = year - 1900; + tm->tm_mon = mon; + tm->tm_mday = days + 1; + break; + } + } + break; + } + } + return 0; +} + +static int convert2days(u16 *days, struct rtc_time *tm) +{ + int i; + *days = 0; + + /* epoch == 1900 */ + if (tm->tm_year < 100 || tm->tm_year > 199) + return -EINVAL; + + for (i = 2000; i < 1900 + tm->tm_year; i++) + *days += rtc_year_days(1, 12, i); + + *days += rtc_year_days(tm->tm_mday, tm->tm_mon, 1900 + tm->tm_year); + + return 0; +} + +static int davinci_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + u16 days = 0; + u8 day0, day1; + unsigned long flags; + + spin_lock_irqsave(&davinci_rtc_lock, flags); + + while (davinci_rtcss_read(DAVINCI_PRTCSS_RTC_CCTRL) & + DAVINCI_PRTCSS_RTC_CCTRL_CALBUSY); + tm->tm_sec = bcd2bin(davinci_rtcss_read(DAVINCI_PRTCSS_RTC_SEC)); + + while (davinci_rtcss_read(DAVINCI_PRTCSS_RTC_CCTRL) & + DAVINCI_PRTCSS_RTC_CCTRL_CALBUSY); + tm->tm_min = bcd2bin(davinci_rtcss_read(DAVINCI_PRTCSS_RTC_MIN)); + + while (davinci_rtcss_read(DAVINCI_PRTCSS_RTC_CCTRL) & + DAVINCI_PRTCSS_RTC_CCTRL_CALBUSY); + tm->tm_hour = bcd2bin(davinci_rtcss_read(DAVINCI_PRTCSS_RTC_HOUR)); + + while (davinci_rtcss_read(DAVINCI_PRTCSS_RTC_CCTRL) & + DAVINCI_PRTCSS_RTC_CCTRL_CALBUSY); + day0 = davinci_rtcss_read(DAVINCI_PRTCSS_RTC_DAY0); + + while (davinci_rtcss_read(DAVINCI_PRTCSS_RTC_CCTRL) & + DAVINCI_PRTCSS_RTC_CCTRL_CALBUSY); + day1 = davinci_rtcss_read(DAVINCI_PRTCSS_RTC_DAY1); + + spin_unlock_irqrestore(&davinci_rtc_lock, flags); + + days |= day1; + days <<= 8; + days |= day0; + + if (convertfromdays(days, tm) < 0) + return -EINVAL; + + return 0; +} + +static int davinci_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + u16 days; + u8 rtc_cctrl; + unsigned long flags; + + if (convert2days(&days, tm) < 0) + return -EINVAL; + + spin_lock_irqsave(&davinci_rtc_lock, flags); + + while (davinci_rtcss_read(DAVINCI_PRTCSS_RTC_CCTRL) & + DAVINCI_PRTCSS_RTC_CCTRL_CALBUSY); + davinci_rtcss_write(bin2bcd(tm->tm_sec), DAVINCI_PRTCSS_RTC_SEC); + + while (davinci_rtcss_read(DAVINCI_PRTCSS_RTC_CCTRL) & + DAVINCI_PRTCSS_RTC_CCTRL_CALBUSY); + davinci_rtcss_write(bin2bcd(tm->tm_min), DAVINCI_PRTCSS_RTC_MIN); + + while (davinci_rtcss_read(DAVINCI_PRTCSS_RTC_CCTRL) & + DAVINCI_PRTCSS_RTC_CCTRL_CALBUSY); + davinci_rtcss_write(bin2bcd(tm->tm_hour), DAVINCI_PRTCSS_RTC_HOUR); + + while (davinci_rtcss_read(DAVINCI_PRTCSS_RTC_CCTRL) & + DAVINCI_PRTCSS_RTC_CCTRL_CALBUSY); + davinci_rtcss_write(days & 0xFF, DAVINCI_PRTCSS_RTC_DAY0); + + while (davinci_rtcss_read(DAVINCI_PRTCSS_RTC_CCTRL) & + DAVINCI_PRTCSS_RTC_CCTRL_CALBUSY); + davinci_rtcss_write((days & 0xFF00) >> 8, DAVINCI_PRTCSS_RTC_DAY1); + + rtc_cctrl = davinci_rtcss_read(DAVINCI_PRTCSS_RTC_CCTRL); + + rtc_cctrl |= DAVINCI_PRTCSS_RTC_CCTRL_CAEN; + + davinci_rtcss_write(rtc_cctrl, DAVINCI_PRTCSS_RTC_CCTRL); + + spin_unlock_irqrestore(&davinci_rtc_lock, flags); + + return 0; +} + +static int davinci_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + unsigned long flags; + u8 rtc_cctrl = davinci_rtcss_read(DAVINCI_PRTCSS_RTC_CCTRL); + + spin_lock_irqsave(&davinci_rtc_lock, flags); + + if (enabled) + rtc_cctrl |= (DAVINCI_PRTCSS_RTC_CCTRL_DAEN | + DAVINCI_PRTCSS_RTC_CCTRL_HAEN | + DAVINCI_PRTCSS_RTC_CCTRL_MAEN | + DAVINCI_PRTCSS_RTC_CCTRL_ALMFLG | + DAVINCI_PRTCSS_RTC_CCTRL_AIEN); + else + rtc_cctrl &= ~DAVINCI_PRTCSS_RTC_CCTRL_AIEN; + + while (davinci_rtcss_read(DAVINCI_PRTCSS_RTC_CCTRL) & + DAVINCI_PRTCSS_RTC_CCTRL_CALBUSY); + davinci_rtcss_write(rtc_cctrl, DAVINCI_PRTCSS_RTC_CCTRL); + + spin_unlock_irqrestore(&davinci_rtc_lock, flags); + + return 0; +} + +static int davinci_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + u16 days = 0; + u8 day0, day1; + unsigned long flags; + + spin_lock_irqsave(&davinci_rtc_lock, flags); + + while (davinci_rtcss_read(DAVINCI_PRTCSS_RTC_CCTRL) & + DAVINCI_PRTCSS_RTC_CCTRL_CALBUSY); + alm->time.tm_min = bcd2bin(davinci_rtcss_read(DAVINCI_PRTCSS_RTC_AMIN)); + + while (davinci_rtcss_read(DAVINCI_PRTCSS_RTC_CCTRL) & + DAVINCI_PRTCSS_RTC_CCTRL_CALBUSY); + alm->time.tm_hour = bcd2bin(davinci_rtcss_read(DAVINCI_PRTCSS_RTC_AHOUR)); + + while (davinci_rtcss_read(DAVINCI_PRTCSS_RTC_CCTRL) & + DAVINCI_PRTCSS_RTC_CCTRL_CALBUSY); + day0 = davinci_rtcss_read(DAVINCI_PRTCSS_RTC_ADAY0); + + while (davinci_rtcss_read(DAVINCI_PRTCSS_RTC_CCTRL) & + DAVINCI_PRTCSS_RTC_CCTRL_CALBUSY); + day1 = davinci_rtcss_read(DAVINCI_PRTCSS_RTC_ADAY1); + + spin_unlock_irqrestore(&davinci_rtc_lock, flags); + days |= day1; + days <<= 8; + days |= day0; + + if (convertfromdays(days, &alm->time) < 0) + return -EINVAL; + + alm->pending = !!(davinci_rtcss_read(DAVINCI_PRTCSS_RTC_CCTRL) & + DAVINCI_PRTCSS_RTC_CCTRL_AIEN); + alm->enabled = alm->pending && device_may_wakeup(dev); + + return 0; +} + +static int davinci_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + unsigned long flags; + u16 days; + + if (alm->time.tm_mday <= 0 && alm->time.tm_mon < 0 + && alm->time.tm_year < 0) { + struct rtc_time tm; + unsigned long now, then; + + davinci_rtc_read_time(dev, &tm); + rtc_tm_to_time(&tm, &now); + + alm->time.tm_mday = tm.tm_mday; + alm->time.tm_mon = tm.tm_mon; + alm->time.tm_year = tm.tm_year; + rtc_tm_to_time(&alm->time, &then); + + if (then < now) { + rtc_time_to_tm(now + 24 * 60 * 60, &tm); + alm->time.tm_mday = tm.tm_mday; + alm->time.tm_mon = tm.tm_mon; + alm->time.tm_year = tm.tm_year; + } + } + + if (convert2days(&days, &alm->time) < 0) + return -EINVAL; + + spin_lock_irqsave(&davinci_rtc_lock, flags); + + while (davinci_rtcss_read(DAVINCI_PRTCSS_RTC_CCTRL) & + DAVINCI_PRTCSS_RTC_CCTRL_CALBUSY); + davinci_rtcss_write(bin2bcd(alm->time.tm_min), DAVINCI_PRTCSS_RTC_AMIN); + + while (davinci_rtcss_read(DAVINCI_PRTCSS_RTC_CCTRL) & + DAVINCI_PRTCSS_RTC_CCTRL_CALBUSY); + davinci_rtcss_write(bin2bcd(alm->time.tm_hour), DAVINCI_PRTCSS_RTC_AHOUR); + + while (davinci_rtcss_read(DAVINCI_PRTCSS_RTC_CCTRL) & + DAVINCI_PRTCSS_RTC_CCTRL_CALBUSY); + davinci_rtcss_write(days & 0xFF, DAVINCI_PRTCSS_RTC_ADAY0); + + while (davinci_rtcss_read(DAVINCI_PRTCSS_RTC_CCTRL) & + DAVINCI_PRTCSS_RTC_CCTRL_CALBUSY); + davinci_rtcss_write((days & 0xFF00) >> 8, DAVINCI_PRTCSS_RTC_ADAY1); + + spin_unlock_irqrestore(&davinci_rtc_lock, flags); + + return 0; +} + +static int davinci_rtc_irq_set_state(struct device *dev, int enabled) +{ + unsigned long flags; + u8 rtc_ctrl; + + spin_lock_irqsave(&davinci_rtc_lock, flags); + + rtc_ctrl = davinci_rtcss_read(DAVINCI_PRTCSS_RTC_CTRL); + + if (enabled) { + while (davinci_rtcss_read(DAVINCI_PRTCSS_RTC_CTRL) & + DAVINCI_PRTCSS_RTC_CTRL_WDTBUS); + + rtc_ctrl |= DAVINCI_PRTCSS_RTC_CTRL_TE; + davinci_rtcss_write(rtc_ctrl, DAVINCI_PRTCSS_RTC_CTRL); + + davinci_rtcss_write(0x0, DAVINCI_PRTCSS_RTC_CLKC_CNT); + rtc_ctrl |= DAVINCI_PRTCSS_RTC_CTRL_TIEN | + DAVINCI_PRTCSS_RTC_CTRL_TMMD | + DAVINCI_PRTCSS_RTC_CTRL_TMRFLG; + } + else + rtc_ctrl &= ~DAVINCI_PRTCSS_RTC_CTRL_TIEN; + + davinci_rtcss_write(rtc_ctrl, DAVINCI_PRTCSS_RTC_CTRL); + + spin_unlock_irqrestore(&davinci_rtc_lock, flags); + + return 0; +} + +static int davinci_rtc_irq_set_freq(struct device *dev, int freq) +{ + unsigned long flags; + u16 tmr_counter = (0x8000 >> (ffs(freq) - 1)); + + spin_lock_irqsave(&davinci_rtc_lock, flags); + + davinci_rtcss_write(tmr_counter & 0xFF, DAVINCI_PRTCSS_RTC_TMR0); + davinci_rtcss_write((tmr_counter & 0xFF00) >> 8, DAVINCI_PRTCSS_RTC_TMR1); + + spin_unlock_irqrestore(&davinci_rtc_lock, flags); + + return 0; +} + +static struct rtc_class_ops davinci_rtc_ops = { + .ioctl = davinci_rtc_ioctl, + .read_time = davinci_rtc_read_time, + .set_time = davinci_rtc_set_time, + .alarm_irq_enable = davinci_rtc_alarm_irq_enable, + .read_alarm = davinci_rtc_read_alarm, + .set_alarm = davinci_rtc_set_alarm, + .irq_set_state = davinci_rtc_irq_set_state, + .irq_set_freq = davinci_rtc_irq_set_freq, +}; + +static int __init davinci_rtc_probe(struct platform_device *pdev) +{ + struct device * dev = &pdev->dev; + struct resource *res, *mem; + int ret = 0; + + davinci_rtc = kzalloc(sizeof(struct davinci_rtc), GFP_KERNEL); + if(!davinci_rtc) { + dev_dbg(dev, "could not allocate memory for private data\n"); + return -ENOMEM; + } + + davinci_rtc->irq = platform_get_irq(pdev, 0); + if (davinci_rtc->irq < 0) { + dev_err(dev, "no RTC irq\n"); + ret = davinci_rtc->irq; + goto fail1; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "no mem resource\n"); + ret = -EINVAL; + goto fail1; + } + + davinci_rtc->pbase = res->start; + davinci_rtc->base_size = resource_size(res); + + mem = request_mem_region(davinci_rtc->pbase, davinci_rtc->base_size, pdev->name); + if (!mem) { + dev_err(dev, "RTC registers at %08x are not free\n", + davinci_rtc->pbase); + ret = -EBUSY; + goto fail1; + } + + davinci_rtc->base = ioremap(davinci_rtc->pbase, davinci_rtc->base_size); + if (!davinci_rtc->base) { + dev_err(dev, "unable to ioremap MEM resource\n"); + ret = -ENOMEM; + goto fail2; + } + + davinci_rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, + &davinci_rtc_ops, THIS_MODULE); + if (IS_ERR(davinci_rtc->rtc)) { + dev_err(dev, "unable to register RTC device, err %ld\n", + PTR_ERR(davinci_rtc->rtc)); + goto fail3; + } + + davinci_rtcif_write(DAVINCI_PRTCIF_INTFLG_RTCSS, DAVINCI_PRTCIF_INTFLG); + davinci_rtcif_write(0, DAVINCI_PRTCIF_INTEN); + davinci_rtcss_write(0, DAVINCI_PRTCSS_RTC_INTC_EXTENA1); + + davinci_rtcss_write(0, DAVINCI_PRTCSS_RTC_CTRL); + davinci_rtcss_write(0, DAVINCI_PRTCSS_RTC_CCTRL); + + ret = request_irq(davinci_rtc->irq, davinci_rtc_interrupt, IRQF_DISABLED, + "davinci_rtc", davinci_rtc->rtc); + if (ret < 0) { + dev_err(dev, "unable to register davinci RTC interrupt\n"); + goto fail4; + } + + /* Enable interrupts */ + davinci_rtcif_write(DAVINCI_PRTCIF_INTEN_RTCSS, DAVINCI_PRTCIF_INTEN); + davinci_rtcss_write(DAVINCI_PRTCSS_RTC_INTC_EXTENA1_MASK, + DAVINCI_PRTCSS_RTC_INTC_EXTENA1); + + davinci_rtcss_write(DAVINCI_PRTCSS_RTC_CCTRL_CAEN, DAVINCI_PRTCSS_RTC_CCTRL); + + platform_set_drvdata(pdev, davinci_rtc); + + device_init_wakeup(&pdev->dev, 0); + + return 0; + +fail4: + rtc_device_unregister(davinci_rtc->rtc); +fail3: + iounmap(davinci_rtc->base); +fail2: + release_mem_region(davinci_rtc->pbase, davinci_rtc->base_size); +fail1: + kfree(davinci_rtc); + + return ret; +} + +static int __devexit davinci_rtc_remove(struct platform_device *pdev) +{ + device_init_wakeup(&pdev->dev, 0); + + davinci_rtcif_write(0, DAVINCI_PRTCIF_INTEN); + + free_irq(davinci_rtc->irq, davinci_rtc); + + rtc_device_unregister(davinci_rtc->rtc); + + iounmap(davinci_rtc->base); + release_mem_region(davinci_rtc->pbase, davinci_rtc->base_size); + + platform_set_drvdata(pdev, NULL); + + kfree(davinci_rtc); + + return 0; +} + +static struct platform_driver davinci_rtc_driver = { + .probe = davinci_rtc_probe, + .remove = __devexit_p(davinci_rtc_remove), + .driver = { + .name = "rtc_davinci", + .owner = THIS_MODULE, + }, +}; + +static int __init rtc_init(void) +{ + return platform_driver_probe(&davinci_rtc_driver, davinci_rtc_probe); +} +module_init(rtc_init); + +static void __exit rtc_exit(void) +{ + platform_driver_unregister(&davinci_rtc_driver); +} +module_exit(rtc_exit); + +MODULE_AUTHOR("Miguel Aguilar"); +MODULE_DESCRIPTION("Texas Instruments DaVinci PRTC Driver"); +MODULE_LICENSE("GPL");