From patchwork Wed Oct 28 15:28:23 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: 56305 Received: from bear.ext.ti.com (bear.ext.ti.com [192.94.94.41]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id n9SFVoSv008148 for ; Wed, 28 Oct 2009 15:31:50 GMT Received: from dlep36.itg.ti.com ([157.170.170.91]) by bear.ext.ti.com (8.13.7/8.13.7) with ESMTP id n9SFSrOP020681 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Wed, 28 Oct 2009 10:28:54 -0500 Received: from linux.omap.com (localhost [127.0.0.1]) by dlep36.itg.ti.com (8.13.8/8.13.8) with ESMTP id n9SFSqB5026679; Wed, 28 Oct 2009 10:28:53 -0500 (CDT) Received: from linux.omap.com (localhost [127.0.0.1]) by linux.omap.com (Postfix) with ESMTP id 8C49A80627; Wed, 28 Oct 2009 09:28:52 -0600 (CST) X-Original-To: davinci-linux-open-source@linux.davincidsp.com Delivered-To: davinci-linux-open-source@linux.davincidsp.com Received: from dflp51.itg.ti.com (dflp51.itg.ti.com [128.247.22.94]) by linux.omap.com (Postfix) with ESMTP id 59EDE80626 for ; Wed, 28 Oct 2009 09:28:49 -0600 (CST) Received: from neches.ext.ti.com (localhost [127.0.0.1]) by dflp51.itg.ti.com (8.13.7/8.13.7) with ESMTP id n9SFSnIH019235 for ; Wed, 28 Oct 2009 10:28:49 -0500 (CDT) Received: from mail104-tx2-R.bigfish.com (mail-tx2.bigfish.com [65.55.88.113]) by neches.ext.ti.com (8.13.7/8.13.7) with ESMTP id n9SFSmVX004476 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=FAIL) for ; Wed, 28 Oct 2009 10:28:48 -0500 Received: from mail104-tx2 (localhost.localdomain [127.0.0.1]) by mail104-tx2-R.bigfish.com (Postfix) with ESMTP id 3C06B19D848A for ; Wed, 28 Oct 2009 15:28:48 +0000 (UTC) X-SpamScore: 2 X-BigFish: vps2(zz18c1J655Na4b1oc8kzz1202hzzz2fh6bh65h) X-Spam-TCS-SCL: 4:0 X-MS-Exchange-Organization-Antispam-Report: OrigIP: 74.208.67.6; Service: EHS Received: by mail104-tx2 (MessageSwitch) id 1256743705374504_13364; Wed, 28 Oct 2009 15:28:25 +0000 (UCT) Received: from TX2EHSMHS009.bigfish.com (unknown [10.9.14.236]) by mail104-tx2.bigfish.com (Postfix) with ESMTP id E061ACC8059; Wed, 28 Oct 2009 15:28:24 +0000 (UTC) Received: from mail.navvo.net (74.208.67.6) by TX2EHSMHS009.bigfish.com (10.9.99.109) with Microsoft SMTP Server (TLS) id 14.0.482.32; Wed, 28 Oct 2009 15:28:22 +0000 Received: from [201.198.127.70] (helo=localhost.localdomain) by mail.navvo.net with esmtpa (Exim 4.63) (envelope-from ) id 1N3ARW-0007ZA-9R; Wed, 28 Oct 2009 10:28:20 -0500 From: To: davinci-linux-open-source@linux.davincidsp.com, nsnehaprabha@ti.com, rtc-linux@googlegroups.com Date: Wed, 28 Oct 2009 09:28:23 -0600 Message-ID: <1256743703-24537-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 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) MIME-Version: 1.0 X-Reverse-DNS: mail.navvo.net Cc: santiago.nunez@ridgerun.com, todd.fischer@ridgerun.com, clark.becker@ridgerun.com, Miguel Aguilar Subject: [PATCH 1/2] RTC: DaVinci RTC driver X-BeenThere: davinci-linux-open-source@linux.davincidsp.com X-Mailman-Version: 2.1.4 Precedence: list List-Id: davinci-linux-open-source.linux.davincidsp.com List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: davinci-linux-open-source-bounces@linux.davincidsp.com Errors-To: davinci-linux-open-source-bounces@linux.davincidsp.com 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..9180a25 --- /dev/null +++ b/drivers/rtc/rtc-davinci.c @@ -0,0 +1,703 @@ +/* + * 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 + +/* + * 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 BIT(31) +#define DAVINCI_PRTCIF_CTLR_SIZE BIT(25) +#define DAVINCI_PRTCIF_CTLR_DIR BIT(24) +#define DAVINCI_PRTCIF_CTLR_BENU_MSB BIT(23) +#define DAVINCI_PRTCIF_CTLR_BENU_3RD_BYTE BIT(22) +#define DAVINCI_PRTCIF_CTLR_BENU_2ND_BYTE BIT(21) +#define DAVINCI_PRTCIF_CTLR_BENU_LSB BIT(20) +#define DAVINCI_PRTCIF_CTLR_BENU_MASK (0x00F00000) +#define DAVINCI_PRTCIF_CTLR_BENL_MSB BIT(19) +#define DAVINCI_PRTCIF_CTLR_BENL_3RD_BYTE BIT(18) +#define DAVINCI_PRTCIF_CTLR_BENL_2ND_BYTE BIT(17) +#define DAVINCI_PRTCIF_CTLR_BENL_LSB BIT(16) +#define DAVINCI_PRTCIF_CTLR_BENL_MASK (0x000F0000) + +/* DAVINCI_PRTCIF_INTEN bit fields */ +#define DAVINCI_PRTCIF_INTEN_RTCSS BIT(1) +#define DAVINCI_PRTCIF_INTEN_RTCIF BIT(0) +#define DAVINCI_PRTCIF_INTEN_MASK (DAVINCI_PRTCIF_INTEN_RTCSS \ + | DAVINCI_PRTCIF_INTEN_RTCIF) + +/* DAVINCI_PRTCIF_INTFLG bit fields */ +#define DAVINCI_PRTCIF_INTFLG_RTCSS BIT(1) +#define DAVINCI_PRTCIF_INTFLG_RTCIF BIT(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 BIT(7) +#define DAVINCI_PRTCSS_RTC_CTRL_WEN BIT(6) +#define DAVINCI_PRTCSS_RTC_CTRL_WDRT BIT(5) +#define DAVINCI_PRTCSS_RTC_CTRL_WDTFLG BIT(4) +#define DAVINCI_PRTCSS_RTC_CTRL_TE BIT(3) +#define DAVINCI_PRTCSS_RTC_CTRL_TIEN BIT(2) +#define DAVINCI_PRTCSS_RTC_CTRL_TMRFLG BIT(1) +#define DAVINCI_PRTCSS_RTC_CTRL_TMMD BIT(0) + +/* DAVINCI_PRTCSS_RTC_CCTRL bit fields */ +#define DAVINCI_PRTCSS_RTC_CCTRL_CALBUSY BIT(7) +#define DAVINCI_PRTCSS_RTC_CCTRL_DAEN BIT(5) +#define DAVINCI_PRTCSS_RTC_CCTRL_HAEN BIT(4) +#define DAVINCI_PRTCSS_RTC_CCTRL_MAEN BIT(3) +#define DAVINCI_PRTCSS_RTC_CCTRL_ALMFLG BIT(2) +#define DAVINCI_PRTCSS_RTC_CCTRL_AIEN BIT(1) +#define DAVINCI_PRTCSS_RTC_CCTRL_CAEN BIT(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; +}; + +static inline void davinci_rtcif_write(struct davinci_rtc *davinci_rtc, + u32 val, u32 addr) +{ + writel(val, davinci_rtc->base + addr); +} + +static inline u32 davinci_rtcif_read(struct davinci_rtc *davinci_rtc, u32 addr) +{ + return readl(davinci_rtc->base + addr); +} + +static inline void davinci_rtcif_wait(struct davinci_rtc *davinci_rtc) +{ + while (davinci_rtcif_read(davinci_rtc, DAVINCI_PRTCIF_CTLR) & + DAVINCI_PRTCIF_CTLR_BUSY) + cpu_relax(); +} + +static inline void davinci_rtcss_write(struct davinci_rtc *davinci_rtc, + unsigned long val, u8 addr) +{ + davinci_rtcif_wait(davinci_rtc); + + davinci_rtcif_write(davinci_rtc, DAVINCI_PRTCIF_CTLR_BENL_LSB | addr, + DAVINCI_PRTCIF_CTLR); + davinci_rtcif_write(davinci_rtc, val, DAVINCI_PRTCIF_LDATA); + + davinci_rtcif_wait(davinci_rtc); +} + +static inline u8 davinci_rtcss_read(struct davinci_rtc *davinci_rtc, u8 addr) +{ + davinci_rtcif_wait(davinci_rtc); + + davinci_rtcif_write(davinci_rtc, DAVINCI_PRTCIF_CTLR_DIR | + DAVINCI_PRTCIF_CTLR_BENL_LSB | + addr, DAVINCI_PRTCIF_CTLR); + + davinci_rtcif_wait(davinci_rtc); + + return davinci_rtcif_read(davinci_rtc, DAVINCI_PRTCIF_LDATA); +} + +static inline void davinci_rtcss_calendar_wait(struct davinci_rtc *davinci_rtc) +{ + while (davinci_rtcss_read(davinci_rtc, DAVINCI_PRTCSS_RTC_CCTRL) & + DAVINCI_PRTCSS_RTC_CCTRL_CALBUSY) + cpu_relax(); +} + +static irqreturn_t davinci_rtc_interrupt(int irq, void *class_dev) +{ + struct davinci_rtc *davinci_rtc = 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_rtc, DAVINCI_PRTCIF_INTFLG) & + DAVINCI_PRTCIF_INTFLG_RTCSS; + + alm_irq = davinci_rtcss_read(davinci_rtc, DAVINCI_PRTCSS_RTC_CCTRL) & + DAVINCI_PRTCSS_RTC_CCTRL_ALMFLG; + + tmr_irq = davinci_rtcss_read(davinci_rtc, 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_rtc, + DAVINCI_PRTCSS_RTC_CCTRL); + rtc_cctrl |= DAVINCI_PRTCSS_RTC_CCTRL_ALMFLG; + davinci_rtcss_write(davinci_rtc, rtc_cctrl, + DAVINCI_PRTCSS_RTC_CCTRL); + } else if (tmr_irq) { + events |= RTC_IRQF | RTC_PF; + rtc_ctrl = davinci_rtcss_read(davinci_rtc, + DAVINCI_PRTCSS_RTC_CTRL); + rtc_ctrl |= DAVINCI_PRTCSS_RTC_CTRL_TMRFLG; + davinci_rtcss_write(davinci_rtc, rtc_ctrl, + DAVINCI_PRTCSS_RTC_CTRL); + } + + davinci_rtcif_write(davinci_rtc, DAVINCI_PRTCIF_INTFLG_RTCSS, + DAVINCI_PRTCIF_INTFLG); + rtc_update_irq(davinci_rtc->rtc, 1, events); + ret = IRQ_HANDLED; + } + + return ret; +} + +static int +davinci_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +{ + struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev); + u8 rtc_ctrl; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&davinci_rtc_lock, flags); + + rtc_ctrl = davinci_rtcss_read(davinci_rtc, 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(davinci_rtc, 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) +{ + struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev); + u16 days = 0; + u8 day0, day1; + unsigned long flags; + + spin_lock_irqsave(&davinci_rtc_lock, flags); + + davinci_rtcss_calendar_wait(davinci_rtc); + tm->tm_sec = bcd2bin(davinci_rtcss_read(davinci_rtc, + DAVINCI_PRTCSS_RTC_SEC)); + + davinci_rtcss_calendar_wait(davinci_rtc); + tm->tm_min = bcd2bin(davinci_rtcss_read(davinci_rtc, + DAVINCI_PRTCSS_RTC_MIN)); + + davinci_rtcss_calendar_wait(davinci_rtc); + tm->tm_hour = bcd2bin(davinci_rtcss_read(davinci_rtc, + DAVINCI_PRTCSS_RTC_HOUR)); + + davinci_rtcss_calendar_wait(davinci_rtc); + day0 = davinci_rtcss_read(davinci_rtc, DAVINCI_PRTCSS_RTC_DAY0); + + davinci_rtcss_calendar_wait(davinci_rtc); + day1 = davinci_rtcss_read(davinci_rtc, 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) +{ + struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev); + u16 days; + u8 rtc_cctrl; + unsigned long flags; + + if (convert2days(&days, tm) < 0) + return -EINVAL; + + spin_lock_irqsave(&davinci_rtc_lock, flags); + + davinci_rtcss_calendar_wait(davinci_rtc); + davinci_rtcss_write(davinci_rtc, bin2bcd(tm->tm_sec), + DAVINCI_PRTCSS_RTC_SEC); + + davinci_rtcss_calendar_wait(davinci_rtc); + davinci_rtcss_write(davinci_rtc, bin2bcd(tm->tm_min), + DAVINCI_PRTCSS_RTC_MIN); + + davinci_rtcss_calendar_wait(davinci_rtc); + davinci_rtcss_write(davinci_rtc, bin2bcd(tm->tm_hour), + DAVINCI_PRTCSS_RTC_HOUR); + + davinci_rtcss_calendar_wait(davinci_rtc); + davinci_rtcss_write(davinci_rtc, days & 0xFF, + DAVINCI_PRTCSS_RTC_DAY0); + + davinci_rtcss_calendar_wait(davinci_rtc); + davinci_rtcss_write(davinci_rtc, (days & 0xFF00) >> 8, + DAVINCI_PRTCSS_RTC_DAY1); + + rtc_cctrl = davinci_rtcss_read(davinci_rtc, DAVINCI_PRTCSS_RTC_CCTRL); + + rtc_cctrl |= DAVINCI_PRTCSS_RTC_CCTRL_CAEN; + + davinci_rtcss_write(davinci_rtc, 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) +{ + struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev); + unsigned long flags; + u8 rtc_cctrl = davinci_rtcss_read(davinci_rtc, + 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; + + davinci_rtcss_calendar_wait(davinci_rtc); + davinci_rtcss_write(davinci_rtc, 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) +{ + struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev); + u16 days = 0; + u8 day0, day1; + unsigned long flags; + + spin_lock_irqsave(&davinci_rtc_lock, flags); + + davinci_rtcss_calendar_wait(davinci_rtc); + alm->time.tm_min = bcd2bin(davinci_rtcss_read(davinci_rtc, + DAVINCI_PRTCSS_RTC_AMIN)); + + davinci_rtcss_calendar_wait(davinci_rtc); + alm->time.tm_hour = bcd2bin(davinci_rtcss_read(davinci_rtc, + DAVINCI_PRTCSS_RTC_AHOUR)); + + davinci_rtcss_calendar_wait(davinci_rtc); + day0 = davinci_rtcss_read(davinci_rtc, DAVINCI_PRTCSS_RTC_ADAY0); + + davinci_rtcss_calendar_wait(davinci_rtc); + day1 = davinci_rtcss_read(davinci_rtc, 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_rtc, + 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) +{ + struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev); + 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); + + davinci_rtcss_calendar_wait(davinci_rtc); + davinci_rtcss_write(davinci_rtc, bin2bcd(alm->time.tm_min), + DAVINCI_PRTCSS_RTC_AMIN); + + davinci_rtcss_calendar_wait(davinci_rtc); + davinci_rtcss_write(davinci_rtc, bin2bcd(alm->time.tm_hour), + DAVINCI_PRTCSS_RTC_AHOUR); + + davinci_rtcss_calendar_wait(davinci_rtc); + davinci_rtcss_write(davinci_rtc, days & 0xFF, + DAVINCI_PRTCSS_RTC_ADAY0); + + davinci_rtcss_calendar_wait(davinci_rtc); + davinci_rtcss_write(davinci_rtc, (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) +{ + struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev); + unsigned long flags; + u8 rtc_ctrl; + + spin_lock_irqsave(&davinci_rtc_lock, flags); + + rtc_ctrl = davinci_rtcss_read(davinci_rtc, DAVINCI_PRTCSS_RTC_CTRL); + + if (enabled) { + while (davinci_rtcss_read(davinci_rtc, DAVINCI_PRTCSS_RTC_CTRL) + & DAVINCI_PRTCSS_RTC_CTRL_WDTBUS) + cpu_relax(); + + rtc_ctrl |= DAVINCI_PRTCSS_RTC_CTRL_TE; + davinci_rtcss_write(davinci_rtc, rtc_ctrl, + DAVINCI_PRTCSS_RTC_CTRL); + + davinci_rtcss_write(davinci_rtc, 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(davinci_rtc, 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) +{ + struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev); + unsigned long flags; + u16 tmr_counter = (0x8000 >> (ffs(freq) - 1)); + + spin_lock_irqsave(&davinci_rtc_lock, flags); + + davinci_rtcss_write(davinci_rtc, tmr_counter & 0xFF, + DAVINCI_PRTCSS_RTC_TMR0); + davinci_rtcss_write(davinci_rtc, (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 davinci_rtc *davinci_rtc; + 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_rtc, DAVINCI_PRTCIF_INTFLG_RTCSS, + DAVINCI_PRTCIF_INTFLG); + davinci_rtcif_write(davinci_rtc, 0, DAVINCI_PRTCIF_INTEN); + davinci_rtcss_write(davinci_rtc, 0, DAVINCI_PRTCSS_RTC_INTC_EXTENA1); + + davinci_rtcss_write(davinci_rtc, 0, DAVINCI_PRTCSS_RTC_CTRL); + davinci_rtcss_write(davinci_rtc, 0, DAVINCI_PRTCSS_RTC_CCTRL); + + ret = request_irq(davinci_rtc->irq, davinci_rtc_interrupt, + IRQF_DISABLED, "davinci_rtc", davinci_rtc); + if (ret < 0) { + dev_err(dev, "unable to register davinci RTC interrupt\n"); + goto fail4; + } + + /* Enable interrupts */ + davinci_rtcif_write(davinci_rtc, DAVINCI_PRTCIF_INTEN_RTCSS, + DAVINCI_PRTCIF_INTEN); + davinci_rtcss_write(davinci_rtc, DAVINCI_PRTCSS_RTC_INTC_EXTENA1_MASK, + DAVINCI_PRTCSS_RTC_INTC_EXTENA1); + + davinci_rtcss_write(davinci_rtc, 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) +{ + struct davinci_rtc *davinci_rtc = platform_get_drvdata(pdev); + + device_init_wakeup(&pdev->dev, 0); + + davinci_rtcif_write(davinci_rtc, 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");