From patchwork Wed May 27 19:35:41 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: dmitry pervushin X-Patchwork-Id: 27753 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from yw-out-2122.google.com (yw-out-2122.google.com [74.125.46.27]) by bilbo.ozlabs.org (Postfix) with ESMTP id 5A59BB7080 for ; Thu, 28 May 2009 05:35:49 +1000 (EST) Received: by yw-out-2122.google.com with SMTP id 7so671823ywi.11 for ; Wed, 27 May 2009 12:35:47 -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 :received:subject:from:reply-to:to:cc:mime-version:content-type :organization:date:message-id:x-mailer:sender:precedence :x-google-loop:mailing-list:list-id:list-post:list-help :list-unsubscribe:x-beenthere-env:x-beenthere; bh=RtAOkABMh46/J2zCvEU02lgU0ESf++/OAY1D4qiyeSk=; b=Cni2NXwNce+1RrGjgDj7jpxS3ETli8SrW+fL4efWujO3kZkeJDy+bMGhokUHRzNe8r C9mL4WOpbgRrAYhHoqCQjoEGcW8eskiJIzHe5DXukYexi/1jBo531ddlpwJXnkDlHHeO XfdgR42TQUUsTTAWbT67IO80aWXmyhqlhJNLY= DomainKey-Signature: a=rsa-sha1; c=nofws; d=googlegroups.com; s=beta; h=x-sender:x-apparently-to:received-spf:authentication-results :subject:from:reply-to:to:cc:mime-version:content-type:organization :date:message-id:x-mailer:sender:precedence:x-google-loop :mailing-list:list-id:list-post:list-help:list-unsubscribe :x-beenthere-env:x-beenthere; b=qLC8b/aNxvnHGBmwYsZ1dPNgu74RtT0N+ND0UF7HdfxKouJ9cZcF1GUmb7h5FmkQOV Bz0+Bhez2tUznAYu5gXUBpJjGlAvbfx0kaoYsd9Kt4dQpwHrMZEiBqQwVpRKXyyM73GA D6ywmZ40UwOYV/KKVTyl32Sq4tIt7zVFEQgy8= Received: by 10.100.7.13 with SMTP id 13mr60543ang.25.1243452943542; Wed, 27 May 2009 12:35:43 -0700 (PDT) Received: by 10.106.168.10 with SMTP id q10gr2432pre.0; Wed, 27 May 2009 12:35:43 -0700 (PDT) X-Sender: dpervushin@embeddedalley.com X-Apparently-To: rtc-linux@googlegroups.com Received: by 10.115.110.15 with SMTP id n15mr84835wam.11.1243452942943; Wed, 27 May 2009 12:35:42 -0700 (PDT) Received: from easi.embeddedalley.com (easi.embeddedalley.com [71.6.201.124]) by gmr-mx.google.com with SMTP id 21si575526pzk.0.2009.05.27.12.35.42; Wed, 27 May 2009 12:35:42 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of dpervushin@embeddedalley.com designates 71.6.201.124 as permitted sender) client-ip=71.6.201.124; Authentication-Results: gmr-mx.google.com; spf=pass (google.com: best guess record for domain of dpervushin@embeddedalley.com designates 71.6.201.124 as permitted sender) smtp.mail=dpervushin@embeddedalley.com Received: (qmail 19166 invoked from network); 27 May 2009 19:35:41 -0000 Received: from easi.embeddedalley.com (HELO ?192.168.66.237?) (easi@71.6.201.124) by easi.embeddedalley.com with SMTP; 27 May 2009 12:35:41 -0700 Subject: [rtc-linux] [PATCH] Freescale stmp378x/37xx RTC driver From: dmitry pervushin Reply-To: rtc-linux@googlegroups.com To: a.zummo@towertech.it, p_gortmaker@yahoo.com Cc: rtc-linux@googlegroups.com Mime-Version: 1.0 Organization: EmbeddedAlley Date: Wed, 27 May 2009 23:35:41 +0400 Message-Id: <1243452941.4067.6.camel@hp.diimka.lan> X-Mailer: Evolution 2.24.1.1 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 Hello Alessandro, Paul, rtc folks, The patch below adds support for RTC on the Freescale STMP37xx/378x platform. Signed-off-by: dmitry pervushin --- drivers/rtc/Kconfig | 10 + drivers/rtc/Makefile | 1 drivers/rtc/rtc-stmp3xxx.c | 318 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 329 insertions(+) --~--~---------~--~----~------------~-------~--~----~ 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. -~----------~----~----~----~------~----~------~--~--- Index: linux-2.6-arm/drivers/rtc/Kconfig =================================================================== --- linux-2.6-arm.orig/drivers/rtc/Kconfig +++ linux-2.6-arm/drivers/rtc/Kconfig @@ -750,4 +750,14 @@ config RTC_DRV_PS3 This driver can also be built as a module. If so, the module will be called rtc-ps3. +config RTC_DRV_STMP + tristate "Freescale STMP3xxx RTC" + depends on ARCH_STMP3XXX + help + If you say yes here you will get support for the onboard + STMP3xxx RTC. + + This driver can also be built as a module. If so, the module + will be called rtc-stmp3xxx. + endif # RTC_CLASS Index: linux-2.6-arm/drivers/rtc/Makefile =================================================================== --- linux-2.6-arm.orig/drivers/rtc/Makefile +++ linux-2.6-arm/drivers/rtc/Makefile @@ -77,3 +77,4 @@ obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm83 obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o obj-$(CONFIG_RTC_DRV_PCF50633) += rtc-pcf50633.o obj-$(CONFIG_RTC_DRV_PS3) += rtc-ps3.o +obj-$(CONFIG_RTC_DRV_STMP) += rtc-stmp3xxx.o Index: linux-2.6-arm/drivers/rtc/rtc-stmp3xxx.c =================================================================== --- /dev/null +++ linux-2.6-arm/drivers/rtc/rtc-stmp3xxx.c @@ -0,0 +1,318 @@ +/* + * Freescale STMP37XX/STMP378X Real Time Clock driver + * + * Copyright (c) 2007 Sigmatel, Inc. + * Peter Hartley, + * + * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +struct stmp3xxx_rtc_data { + struct rtc_device *rtc; + unsigned irq_count; + void __iomem *io; + int irq_alarm, irq_1msec; +}; + +static void stmp3xxx_wait_time(struct stmp3xxx_rtc_data *rtc_data) +{ + /* + * The datasheet doesn't say which way round the + * NEW_REGS/STALE_REGS bitfields go. In fact it's 0x1=P0, + * 0x2=P1, .., 0x20=P5, 0x40=ALARM, 0x80=SECONDS + */ + while (__raw_readl(rtc_data->io + HW_RTC_STAT) & + BF(0x80, RTC_STAT_STALE_REGS)) + cpu_relax(); +} + +/* Time read/write */ +static int stmp3xxx_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) +{ + struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); + + stmp3xxx_wait_time(rtc_data); + rtc_time_to_tm(__raw_readl(rtc_data->io + HW_RTC_SECONDS), rtc_tm); + return 0; +} + +static int stmp3xxx_rtc_settime(struct device *dev, struct rtc_time *rtc_tm) +{ + unsigned long t; + struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); + int rc = rtc_tm_to_time(rtc_tm, &t); + + if (rc == 0) { + __raw_writel(t, rtc_data->io + HW_RTC_SECONDS); + stmp3xxx_wait_time(rtc_data); + } + return rc; +} + +static irqreturn_t stmp3xxx_rtc_interrupt(int irq, void *dev_id) +{ + struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev_id); + u32 status; + u32 events = 0; + + status = __raw_readl(rtc_data->io + HW_RTC_CTRL) & + (BM_RTC_CTRL_ALARM_IRQ | BM_RTC_CTRL_ONEMSEC_IRQ); + + if (status & BM_RTC_CTRL_ALARM_IRQ) { + stmp3xxx_clearl(BM_RTC_CTRL_ALARM_IRQ, + rtc_data->io + HW_RTC_CTRL); + events |= RTC_AF | RTC_IRQF; + } + + if (status & BM_RTC_CTRL_ONEMSEC_IRQ) { + stmp3xxx_clearl(BM_RTC_CTRL_ONEMSEC_IRQ, + rtc_data->io + HW_RTC_CTRL); + if (++rtc_data->irq_count % 1000 == 0) { + events |= RTC_UF | RTC_IRQF; + rtc_data->irq_count = 0; + } + } + + if (events) + rtc_update_irq(rtc_data->rtc, 1, events); + + return IRQ_HANDLED; +} + +static int stmp3xxx_rtc_open(struct device *dev) +{ + int r; + struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); + + rtc_data->irq_count = 0; + r = request_irq(rtc_data->irq_alarm, stmp3xxx_rtc_interrupt, + IRQF_DISABLED, "RTC alarm", dev); + if (r) { + dev_err(dev, "Cannot claim IRQ%d\n", rtc_data->irq_alarm); + goto fail_1; + } + r = request_irq(rtc_data->irq_1msec, stmp3xxx_rtc_interrupt, + IRQF_DISABLED, "RTC tick", dev); + if (r) { + dev_err(dev, "Cannot claim IRQ%d\n", rtc_data->irq_1msec); + goto fail_2; + } + + return 0; +fail_2: + free_irq(rtc_data->irq_alarm, dev); +fail_1: + return r; +} + +static void stmp3xxx_rtc_release(struct device *dev) +{ + struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); + + stmp3xxx_clearl(BM_RTC_CTRL_ONEMSEC_IRQ_EN | BM_RTC_CTRL_ALARM_IRQ_EN, + rtc_data->io + HW_RTC_CTRL); + free_irq(rtc_data->irq_alarm, dev); + free_irq(rtc_data->irq_1msec, dev); +} + +static int stmp3xxx_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); + void __iomem *p = rtc_data->io + HW_RTC_PERSISTENT0, + *ctl = rtc_data->io + HW_RTC_CTRL; + + if (enabled) { + stmp3xxx_setl(BM_RTC_PERSISTENT0_ALARM_EN | + BM_RTC_PERSISTENT0_ALARM_WAKE_EN, p); + stmp3xxx_setl(BM_RTC_CTRL_ALARM_IRQ_EN, ctl); + } else { + stmp3xxx_clearl(BM_RTC_PERSISTENT0_ALARM_EN | + BM_RTC_PERSISTENT0_ALARM_WAKE_EN, p); + stmp3xxx_clearl(BM_RTC_CTRL_ALARM_IRQ_EN, ctl); + } + return 0; +} + +static int stmp3xxx_update_irq_enable(struct device *dev, unsigned int enabled) +{ + struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); + + if (enabled) + stmp3xxx_setl(BM_RTC_CTRL_ONEMSEC_IRQ_EN, + rtc_data->io + HW_RTC_CTRL); + else + stmp3xxx_clearl(BM_RTC_CTRL_ONEMSEC_IRQ_EN, + rtc_data->io + HW_RTC_CTRL); + return 0; +} + +static int stmp3xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); + + rtc_time_to_tm(__raw_readl(rtc_data->io + HW_RTC_ALARM), &alm->time); + return 0; +} + +static int stmp3xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + unsigned long t; + struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); + + rtc_tm_to_time(&alm->time, &t); + __raw_writel(t, rtc_data->io + HW_RTC_ALARM); + return 0; +} + +static struct rtc_class_ops stmp3xxx_rtc_ops = { + .open = stmp3xxx_rtc_open, + .release = stmp3xxx_rtc_release, + .alarm_irq_enable = + stmp3xxx_alarm_irq_enable, + .update_irq_enable = + stmp3xxx_update_irq_enable, + .read_time = stmp3xxx_rtc_gettime, + .set_time = stmp3xxx_rtc_settime, + .read_alarm = stmp3xxx_rtc_read_alarm, + .set_alarm = stmp3xxx_rtc_set_alarm, +}; + +static int stmp3xxx_rtc_remove(struct platform_device *dev) +{ + struct stmp3xxx_rtc_data *rtc_data = platform_get_drvdata(dev); + + if (rtc_data) { + rtc_device_unregister(rtc_data->rtc); + iounmap(rtc_data->io); + kfree(rtc_data); + } + + return 0; +} + +static int stmp3xxx_rtc_probe(struct platform_device *pdev) +{ + struct stmp3xxx_rtc_data *rtc_data; + struct resource *r; + int err; + + rtc_data = kzalloc(sizeof *rtc_data, GFP_KERNEL); + if (!rtc_data) + return -ENOMEM; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "failed to get resource\n"); + err = -ENXIO; + goto out_free; + } + + rtc_data->io = ioremap(r->start, resource_size(r)); + if (!rtc_data->io) { + dev_err(&pdev->dev, "ioremap failed\n"); + err = -EIO; + goto out_free; + } + + rtc_data->irq_alarm = platform_get_irq(pdev, 0); + rtc_data->irq_1msec = platform_get_irq(pdev, 1); + + if (!(__raw_readl(HW_RTC_STAT + rtc_data->io) & + BM_RTC_STAT_RTC_PRESENT)) { + dev_err(&pdev->dev, "no device onboard\n"); + err = -ENODEV; + goto out_remap; + } + + stmp3xxx_reset_block(rtc_data->io, true); + stmp3xxx_clearl(BM_RTC_PERSISTENT0_ALARM_EN | + BM_RTC_PERSISTENT0_ALARM_WAKE_EN | + BM_RTC_PERSISTENT0_ALARM_WAKE, + rtc_data->io + HW_RTC_PERSISTENT0); + rtc_data->rtc = rtc_device_register(pdev->name, &pdev->dev, + &stmp3xxx_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc_data->rtc)) { + err = PTR_ERR(rtc_data->rtc); + goto out_remap; + } + + platform_set_drvdata(pdev, rtc_data); + + return 0; + +out_remap: + iounmap(rtc_data->io); +out_free: + kfree(rtc_data); + return err; +} + +#ifdef CONFIG_PM +static int stmp3xxx_rtc_suspend(struct platform_device *dev, pm_message_t state) +{ + return 0; +} + +static int stmp3xxx_rtc_resume(struct platform_device *dev) +{ + struct stmp3xxx_rtc_data *rtc_data = platform_get_drvdata(dev); + + stmp3xxx_reset_block(rtc_data->io, true); + stmp3xxx_clearl(BM_RTC_PERSISTENT0_ALARM_EN | + BM_RTC_PERSISTENT0_ALARM_WAKE_EN | + BM_RTC_PERSISTENT0_ALARM_WAKE, + rtc_base->io + HW_RTC_PERSISTENT0); + return 0; +} +#else +#define stmp3xxx_rtc_suspend NULL +#define stmp3xxx_rtc_resume NULL +#endif + +static struct platform_driver stmp3xxx_rtcdrv = { + .probe = stmp3xxx_rtc_probe, + .remove = stmp3xxx_rtc_remove, + .suspend = stmp3xxx_rtc_suspend, + .resume = stmp3xxx_rtc_resume, + .driver = { + .name = "stmp3xxx-rtc", + .owner = THIS_MODULE, + }, +}; + +static int __init stmp3xxx_rtc_init(void) +{ + return platform_driver_register(&stmp3xxx_rtcdrv); +} + +static void __exit stmp3xxx_rtc_exit(void) +{ + platform_driver_unregister(&stmp3xxx_rtcdrv); +} + +module_init(stmp3xxx_rtc_init); +module_exit(stmp3xxx_rtc_exit); + +MODULE_DESCRIPTION("STMP3xxx RTC Driver"); +MODULE_AUTHOR("dmitry pervushin "); +MODULE_LICENSE("GPL");