From patchwork Sun Jun 17 07:50:45 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven King X-Patchwork-Id: 165328 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from mail-vb0-f56.google.com (mail-vb0-f56.google.com [209.85.212.56]) (using TLSv1 with cipher ECDHE-RSA-RC4-SHA (128/128 bits)) (Client CN "smtp.gmail.com", Issuer "Google Internet Authority" (not verified)) by ozlabs.org (Postfix) with ESMTPS id C9F8DB70FC for ; Sun, 17 Jun 2012 17:50:54 +1000 (EST) Received: by vbbfa15 with SMTP id fa15sf3982943vbb.11 for ; Sun, 17 Jun 2012 00:50:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlegroups.com; s=beta; h=x-beenthere:received-spf:x-yahoo-newman-id:x-yahoo-newman-property :x-ymail-osg:x-yahoo-smtp:from:organization:to:subject:date :user-agent:cc:mime-version:message-id:x-original-sender :x-original-authentication-results:reply-to:precedence:mailing-list :list-id:x-google-group-id:list-post:list-help:list-archive:sender :list-subscribe:list-unsubscribe:content-type:content-disposition; bh=pJBBPR4JcN1ErTpN0c92AFZDnUWwa6EhL/eAmMjiP3E=; b=PdNtT4XMbP2j5Imncgc7Eh2A88qNB39nNKZyosIbF+f4I0Hp9rKSzVn1ceLLMckN3c RN/JZfqOeCCTLDVPgOjMvJozlY7s0mm5PWPYqIZD9PwUNx6w4VYQR2YBPQjNdSVxmVjO zNsMRFA++4k0xlBo203sbKW56Ido4cdjR6iS8= Received: by 10.236.128.46 with SMTP id e34mr368970yhi.2.1339919451891; Sun, 17 Jun 2012 00:50:51 -0700 (PDT) X-BeenThere: rtc-linux@googlegroups.com Received: by 10.236.128.9 with SMTP id e9ls4788338yhi.9.gmail; Sun, 17 Jun 2012 00:50:51 -0700 (PDT) Received: by 10.100.255.2 with SMTP id c2mr6061895ani.18.1339919451339; Sun, 17 Jun 2012 00:50:51 -0700 (PDT) Received: by 10.100.255.2 with SMTP id c2mr6061894ani.18.1339919451320; Sun, 17 Jun 2012 00:50:51 -0700 (PDT) Received: from nm26-vm0.access.bullet.mail.mud.yahoo.com (nm26-vm0.access.bullet.mail.mud.yahoo.com. [66.94.236.225]) by gmr-mx.google.com with SMTP id q35si9941457yhe.6.2012.06.17.00.50.50; Sun, 17 Jun 2012 00:50:51 -0700 (PDT) Received-SPF: neutral (google.com: 66.94.236.225 is neither permitted nor denied by best guess record for domain of sfking@fdwdc.com) client-ip=66.94.236.225; Received: from [66.94.237.199] by nm26.access.bullet.mail.mud.yahoo.com with NNFMP; 17 Jun 2012 07:50:50 -0000 Received: from [98.138.207.12] by tm10.access.bullet.mail.mud.yahoo.com with NNFMP; 17 Jun 2012 07:50:50 -0000 Received: from [127.0.0.1] by smtp105.biz.mail.ne1.yahoo.com with NNFMP; 17 Jun 2012 07:50:50 -0000 X-Yahoo-Newman-Id: 457332.46950.bm@smtp105.biz.mail.ne1.yahoo.com X-Yahoo-Newman-Property: ymail-3 X-YMail-OSG: ojh9cwUVM1mVtZXorewqfGiFTTqert1a_2ZizazLqJstEed lFgNNzRKmzndomwMQ2h8MQ.6yPHvyqs85YGpUMqpLvz9r3wHpLtSkzgAewfX QtN1xxGnfIRAb2Vve5PFSERozDsW71F8fYJ.grxt0wyK_SK.rLoPtbl0ZYKx vQ1DOviE.Hw3k.KwxA6IAcpqfjUZY0mbkY0bcNjIT1kmb.A9P2vzSCx4wiDM umnDNv03xnBFm8al.R3ywaUcDIDZP_40URtOGf5xefx83kYamHkDeNk3FfSY biS62snBKfEc9cbmL5xLbCKVrsbHbKHu0jLyDQItyxXWoavMmznztc3OLiz6 3g0CXm.sExyH6MgHAWs1dSIjjDcuX8SEOBvozGmWA_0Rc5rDbyCdnqk3L3A5 dgSQk7GlFp6XOKwBZ0nv1yxS6K.qpmj2DuXHRfnCXeaUbyMV1LjKmQf7LhEH Ln78wtENYy.6lYKdxGF.1l5uXXUqB.GkIA7Wi X-Yahoo-SMTP: qGLgp.mswBDSnFfWmYVMF5Rmg6NJ Received: from fdwdc.com (sfking@67.139.206.186 with plain) by smtp105.biz.mail.ne1.yahoo.com with SMTP; 17 Jun 2012 00:50:50 -0700 PDT From: Steven King Organization: fdwdc To: rtc-linux@googlegroups.com Subject: [rtc-linux] [PATCH] Add rtc driver for Coldfire M5441x. Date: Sun, 17 Jun 2012 00:50:45 -0700 User-Agent: KMail/1.9.9 Cc: Alessandro Zummo , uClinux development list , Greg Ungerer MIME-Version: 1.0 Message-Id: <201206170050.45652.sfking@fdwdc.com> X-Original-Sender: sfking@fdwdc.com X-Original-Authentication-Results: gmr-mx.google.com; spf=neutral (google.com: 66.94.236.225 is neither permitted nor denied by best guess record for domain of sfking@fdwdc.com) smtp.mail=sfking@fdwdc.com Reply-To: rtc-linux@googlegroups.com Precedence: list Mailing-list: list rtc-linux@googlegroups.com; contact rtc-linux+owners@googlegroups.com List-ID: X-Google-Group-Id: 712029733259 List-Post: , List-Help: , List-Archive: Sender: rtc-linux@googlegroups.com List-Subscribe: , List-Unsubscribe: , Content-Disposition: inline Add support for the Coldfire m5441x on-chip rtc. Signed-off-by: Stven King --- drivers/rtc/Kconfig | 10 ++ drivers/rtc/Makefile | 1 + drivers/rtc/rtc-m5441x.c | 363 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 374 insertions(+) diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 08cbdb9..da2b3c4 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1087,4 +1087,14 @@ config RTC_DRV_MXC This driver can also be built as a module, if so, the module will be called "rtc-mxc". +config RTC_DRV_M5441x + tristate "Freescale Coldfire M5441x RTC support" + depends on M5441x + help + This enables support for the RTC on the Freescale Coldfire 5441x + (54410/54415/54416/54417/54418). + + This driver can also be built as a module. If so, the module + will be called rtc-m5441x. + endif # RTC_CLASS diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 2973921..a9ded2c 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -61,6 +61,7 @@ obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o obj-$(CONFIG_RTC_DRV_M48T35) += rtc-m48t35.o obj-$(CONFIG_RTC_DRV_M48T59) += rtc-m48t59.o obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o +obj-$(CONFIG_RTC_DRV_M5441x) += rtc-m5441x.o obj-$(CONFIG_RTC_DRV_MXC) += rtc-mxc.o obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o obj-$(CONFIG_RTC_DRV_MAX8925) += rtc-max8925.o diff --git a/drivers/rtc/rtc-m5441x.c b/drivers/rtc/rtc-m5441x.c new file mode 100644 index 0000000..ba677f3 --- /dev/null +++ b/drivers/rtc/rtc-m5441x.c @@ -0,0 +1,363 @@ +/* + * RTC driver for the Freescale Coldfire 5441x SoCs. + * + * Copyright(C) 2012, Steven King + */ + +#include +#include +#include +#include +#include +#include +#include + +static void __iomem *m5441x_rtc_base; +static int m5441x_rtc_irq; +static struct clk *m5441x_rtc_clk; + +#define M5441X_RTC_YEARMON (m5441x_rtc_base + 0x00) +#define M5441X_RTC_DAYS (m5441x_rtc_base + 0x02) +#define M5441X_RTC_HOURMIN (m5441x_rtc_base + 0x04) +#define M5441X_RTC_SECONDS (m5441x_rtc_base + 0x06) +#define M5441X_RTC_ALM_YRMON (m5441x_rtc_base + 0x08) +#define M5441X_RTC_ALM_DAYS (m5441x_rtc_base + 0x0a) +#define M5441X_RTC_ALM_HM (m5441x_rtc_base + 0x0c) +#define M5441X_RTC_ALM_SEC (m5441x_rtc_base + 0x0e) +#define M5441X_RTC_CR (m5441x_rtc_base + 0x10) +#define M5441X_RTC_CR_AM0 (0 << 2) /* secs/mins/hrs */ +#define M5441X_RTC_CR_AM1 (1 << 2) /* secs/mins/days */ +#define M5441X_RTC_CR_AM2 (2 << 2) /* secs/mins/days/mons */ +#define M5441X_RTC_CR_AM3 (3 << 2) /* alarm match all */ +#define M5441X_RTC_CR_DTDEN (1 << 6) /* DST enable */ +#define M5441X_RTC_CR_BCDEN (1 << 7) /* BCD enable */ +#define M5441X_RTC_CR_SWR (1 << 8) /* software reset */ +#define M5441X_RTC_SR (m5441x_rtc_base + 0x12) +#define M5441X_RTC_SR_INVAL (1 << 0) /* time invalid */ +#define M5441X_RTC_SR_CDON (1 << 1) /* Compensation done */ +#define M5441X_RTC_SR_BERR (1 << 2) /* Bus err */ +#define M5441X_RTC_SR_WPE (1 << 4) /* write protect enabled */ +#define M5441X_RTC_SR_PORB (1 << 6) /* power on reset boot */ +#define M5441X_RTC_ISR (m5441x_rtc_base + 0x14) +#define M5441X_RTC_ISR_STW (1 << 1) /* Stop watch */ +#define M5441X_RTC_ISR_ALM (1 << 2) /* Alarm */ +#define M5441X_RTC_ISR_DAY (1 << 3) /* day counter inc */ +#define M5441X_RTC_ISR_HR (1 << 4) /* hour counter inc */ +#define M5441X_RTC_ISR_MIN (1 << 5) /* minute counter inc */ +#define M5441X_RTC_ISR_1HZ (1 << 6) /* second counter inc */ +#define M5441X_RTC_ISR_2HZ (1 << 7) /* 2Hz counter inc */ +#define M5441X_RTC_IER (m5441x_rtc_base + 0x16) +#define M5441X_RTC_IER_STW (1 << 1) /* Stop watch */ +#define M5441X_RTC_IER_ALM (1 << 2) /* Alarm */ +#define M5441X_RTC_IER_DAY (1 << 3) /* day counter inc */ +#define M5441X_RTC_IER_HR (1 << 4) /* hour counter inc */ +#define M5441X_RTC_IER_MIN (1 << 5) /* minute counter inc */ +#define M5441X_RTC_IER_1HZ (1 << 6) /* second counter inc */ +#define M5441X_RTC_IER_2HZ (1 << 7) /* 2Hz counter inc */ +#define M5441X_RTC_COUNT_DN (m5441x_rtc_base + 0x18) +#define M5441X_RTC_CFG_DATA (m5441x_rtc_base + 0x20) +#define M5441X_RTC_DST_HOUR (m5441x_rtc_base + 0x22) +#define M5441X_RTC_DST_MON (m5441x_rtc_base + 0x24) +#define M5441X_RTC_DST_DAY (m5441x_rtc_base + 0x26) +#define M5441X_RTC_RTC_COMPEN (m5441x_rtc_base + 0x28) +#define M5441X_RTC_UP_CNTRH (m5441x_rtc_base + 0x32) +#define M5441X_RTC_UP_CNTRL (m5441x_rtc_base + 0x24) +#define M5441X_RTC_Standy_RAM (m5441x_rtc_base + 0x40) + +static int m5441x_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + u16 r[4]; + + do { + r[0] = __raw_readw(M5441X_RTC_SECONDS); + r[1] = __raw_readw(M5441X_RTC_HOURMIN); + r[2] = __raw_readw(M5441X_RTC_DAYS); + r[3] = __raw_readw(M5441X_RTC_YEARMON); + } while (r[0] != __raw_readw(M5441X_RTC_SECONDS)); + + tm->tm_sec = r[0]; + tm->tm_min = r[1] & 0xff; + tm->tm_hour = r[1] >> 8; + tm->tm_mday = (r[2] & 0xff) - 1; + tm->tm_wday = r[2] >> 8; + tm->tm_mon = (r[3] & 0xff) - 1; + tm->tm_year = (((s16)r[3]) >> 8) + 212; + + return rtc_valid_tm(tm); +} + +static int m5441x_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + u16 r[4]; + + if ((tm->tm_year < 84) || (tm->tm_year > 339)) + return -EINVAL; /* 1984 <-> 2239 */ + + r[0] = tm->tm_sec; + r[1] = (tm->tm_hour << 8) | tm->tm_min; + r[2] = (tm->tm_wday << 8) | (tm->tm_mday + 1); + r[3] = ((tm->tm_year - 212) << 8) | (tm->tm_mon + 1); + + /* disable write protect */ + __raw_writew(0x0, M5441X_RTC_CR); + __raw_writew(0x1, M5441X_RTC_CR); + __raw_writew(0x3, M5441X_RTC_CR); + __raw_writew(0x2, M5441X_RTC_CR); + + __raw_writew(r[0], M5441X_RTC_SECONDS); + __raw_writew(r[1], M5441X_RTC_HOURMIN); + __raw_writew(r[2], M5441X_RTC_DAYS); + __raw_writew(r[3], M5441X_RTC_YEARMON); + + /* enable write protect */ + __raw_writew(0x2, M5441X_RTC_CR); + + return 0; +} + +static int m5441x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + u16 r[4]; + + r[0] = __raw_readw(M5441X_RTC_ALM_SEC); + r[1] = __raw_readw(M5441X_RTC_ALM_HM); + r[2] = __raw_readw(M5441X_RTC_ALM_DAYS); + r[3] = __raw_readw(M5441X_RTC_ALM_YRMON); + + alm->time.tm_sec = r[0] & 0xff; + alm->time.tm_min = r[1] & 0xff; + alm->time.tm_hour = r[1] >> 8; + alm->time.tm_mday = (r[2] & 0xff) - 1; + alm->time.tm_mon = (r[3] & 0xff) - 1; + alm->time.tm_year = (((s16)r[3]) >> 8) + 212; + + alm->enabled = !!(__raw_readw(M5441X_RTC_IER) & M5441X_RTC_IER_ALM); + + return 0; +} + +static int m5441x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + u16 r[4]; + + if ((alm->time.tm_year < 84) || (alm->time.tm_year > 339)) + return -EINVAL; + + r[0] = alm->time.tm_sec; + r[1] = (alm->time.tm_hour << 8) | alm->time.tm_min; + r[2] = alm->time.tm_mday + 1; + r[3] = ((alm->time.tm_year - 212) << 8) | (alm->time.tm_mon + 1); + + local_irq_disable(); + + /* disable write protect */ + __raw_writew(0x0, M5441X_RTC_CR); + __raw_writew(0x1, M5441X_RTC_CR); + __raw_writew(0x3, M5441X_RTC_CR); + __raw_writew(0x2, M5441X_RTC_CR); + + __raw_writew(r[0], M5441X_RTC_ALM_SEC); + __raw_writew(r[1], M5441X_RTC_ALM_HM); + __raw_writew(r[2], M5441X_RTC_ALM_DAYS); + __raw_writew(r[3], M5441X_RTC_ALM_YRMON); + + r[0] = __raw_readw(M5441X_RTC_IER); + if (alm->enabled) + r[0] |= M5441X_RTC_IER_ALM; + else + r[0] &= ~M5441X_RTC_IER_ALM; + __raw_writew(r[0], M5441X_RTC_IER); + + /* enable write protect */ + __raw_writew(0x2, M5441X_RTC_CR); + + local_irq_enable(); + + return 0; +} + +static int m5441x_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) +{ + u16 r; + + local_irq_disable(); + + /* disable write protect */ + __raw_writew(0x0, M5441X_RTC_CR); + __raw_writew(0x1, M5441X_RTC_CR); + __raw_writew(0x3, M5441X_RTC_CR); + __raw_writew(0x2, M5441X_RTC_CR); + + r = __raw_readw(M5441X_RTC_IER); + if (enable) + r |= M5441X_RTC_IER_ALM; + else + r &= ~M5441X_RTC_IER_ALM; + __raw_writew(r, M5441X_RTC_IER); + + /* enable write protect */ + __raw_writew(0x2, M5441X_RTC_CR); + + local_irq_enable(); + + return 0; +} + +static struct rtc_class_ops m5441x_rtc_ops = { + .read_time = m5441x_rtc_read_time, + .set_time = m5441x_rtc_set_time, + .read_alarm = m5441x_rtc_read_alarm, + .set_alarm = m5441x_rtc_set_alarm, + .alarm_irq_enable = m5441x_rtc_alarm_irq_enable, +}; + +static irqreturn_t m5441x_rtc_irq_handler(int irq, void *rtc) +{ + unsigned long events = 0; + u16 r; + + /* disable write protect */ + __raw_writew(0x0, M5441X_RTC_CR); + __raw_writew(0x1, M5441X_RTC_CR); + __raw_writew(0x3, M5441X_RTC_CR); + __raw_writew(0x2, M5441X_RTC_CR); + + while ((r = __raw_readw(M5441X_RTC_ISR)) != 0) { + if (r & M5441X_RTC_ISR_ALM) + events |= RTC_IRQF | RTC_AF; + if (r & M5441X_RTC_ISR_1HZ) + events |= RTC_IRQF | RTC_UF; + __raw_writew(r, M5441X_RTC_ISR); + } + + /* enable write protect */ + __raw_writew(0x2, M5441X_RTC_CR); + + if (events) + rtc_update_irq(rtc, 1, events); + return IRQ_HANDLED; +} + +static int __devinit m5441x_rtc_probe(struct platform_device *pdev) +{ + struct rtc_device *m5441x_rtc; + struct resource *res; + int status; + + m5441x_rtc_irq = platform_get_irq(pdev, 0); + if (m5441x_rtc_irq < 0) { + dev_dbg(&pdev->dev, "platform_get_irq failed\n"); + return -ENOENT; + } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_dbg(&pdev->dev, "platform_get_resource failed\n"); + return -ENOENT; + } + if (!request_mem_region(res->start, resource_size(res), pdev->name)) { + dev_dbg(&pdev->dev, "request_mem_region failed\n"); + return -EBUSY; + } + + m5441x_rtc_base = ioremap(res->start, resource_size(res)); + if (!m5441x_rtc_base) { + dev_dbg(&pdev->dev, "ioremap failed\n"); + status = -ENXIO; + goto fail0; + } + + m5441x_rtc_clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(m5441x_rtc_clk)) { + dev_dbg(&pdev->dev, "clk_get failed\n"); + status = PTR_ERR(m5441x_rtc_clk); + goto fail1; + } + + m5441x_rtc = rtc_device_register("mcfrtc", &pdev->dev, &m5441x_rtc_ops, + THIS_MODULE); + if (IS_ERR(m5441x_rtc)) { + dev_dbg(&pdev->dev, "rtc_device_register failed\n"); + status = PTR_ERR(m5441x_rtc); + goto fail2; + } + + platform_set_drvdata(pdev, m5441x_rtc); + + if (__raw_readw(M5441X_RTC_SR) & M5441X_RTC_SR_PORB) + dev_info(&pdev->dev, "power lost? RTC time maybe invalid\n"); + + /* disable write protect */ + __raw_writew(0x0, M5441X_RTC_CR); + __raw_writew(0x1, M5441X_RTC_CR); + __raw_writew(0x3, M5441X_RTC_CR); + __raw_writew(0x2, M5441X_RTC_CR); + /* clear any previous alarms */ + __raw_writew(M5441X_RTC_CR_SWR, M5441X_RTC_CR); + /* alarm match on seconds/mmins/days/months/year */ + __raw_writew(M5441X_RTC_CR_AM3, M5441X_RTC_CR); + /* 1Hz alarm */ + __raw_writew(M5441X_RTC_IER_1HZ, M5441X_RTC_IER); + /* enable write protect */ + __raw_writew(0x2, M5441X_RTC_CR); + + status = request_irq(m5441x_rtc_irq, m5441x_rtc_irq_handler, 0, + pdev->name, m5441x_rtc); + if (status) { + dev_dbg(&pdev->dev, "request_irq failed\n"); + goto fail3; + } + return 0; + +fail3: + platform_set_drvdata(pdev, NULL); + rtc_device_unregister(m5441x_rtc); +fail2: + clk_disable(m5441x_rtc_clk); + clk_put(m5441x_rtc_clk); +fail1: + iounmap(m5441x_rtc_base); +fail0: + release_mem_region(res->start, resource_size(res)); + dev_dbg(&pdev->dev, "probe failed\n"); + + return status; +} + +static int __devexit m5441x_rtc_remove(struct platform_device *pdev) +{ + struct rtc_device *m5441x_rtc = platform_get_drvdata(pdev); + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + free_irq(m5441x_rtc_irq, m5441x_rtc); + platform_set_drvdata(pdev, NULL); + rtc_device_unregister(m5441x_rtc); + clk_disable(m5441x_rtc_clk); + clk_put(m5441x_rtc_clk); + iounmap(m5441x_rtc_base); + release_mem_region(res->start, resource_size(res)); + + return 0; +} + +static struct platform_driver m5441x_rtc_driver = { + .driver.name = "mcfrtc", + .driver.owner = THIS_MODULE, + .remove = __devexit_p(m5441x_rtc_remove), +}; + +static int __init m5441x_rtc_init(void) +{ + return platform_driver_probe(&m5441x_rtc_driver, m5441x_rtc_probe); +} +module_init(m5441x_rtc_init); + +static void __exit m5441x_rtc_exit(void) +{ + platform_driver_unregister(&m5441x_rtc_driver); +} +module_exit(m5441x_rtc_exit); + +MODULE_AUTHOR("Steven King "); +MODULE_DESCRIPTION("M5441x RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:rtc");