1df17f631Sdmitry pervushin /* 2df17f631Sdmitry pervushin * Freescale STMP37XX/STMP378X Real Time Clock driver 3df17f631Sdmitry pervushin * 4df17f631Sdmitry pervushin * Copyright (c) 2007 Sigmatel, Inc. 5df17f631Sdmitry pervushin * Peter Hartley, <peter.hartley@sigmatel.com> 6df17f631Sdmitry pervushin * 7df17f631Sdmitry pervushin * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved. 8df17f631Sdmitry pervushin * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. 9df17f631Sdmitry pervushin */ 10df17f631Sdmitry pervushin 11df17f631Sdmitry pervushin /* 12df17f631Sdmitry pervushin * The code contained herein is licensed under the GNU General Public 13df17f631Sdmitry pervushin * License. You may obtain a copy of the GNU General Public License 14df17f631Sdmitry pervushin * Version 2 or later at the following locations: 15df17f631Sdmitry pervushin * 16df17f631Sdmitry pervushin * http://www.opensource.org/licenses/gpl-license.html 17df17f631Sdmitry pervushin * http://www.gnu.org/copyleft/gpl.html 18df17f631Sdmitry pervushin */ 19df17f631Sdmitry pervushin #include <linux/kernel.h> 20df17f631Sdmitry pervushin #include <linux/module.h> 21df17f631Sdmitry pervushin #include <linux/init.h> 22df17f631Sdmitry pervushin #include <linux/platform_device.h> 23df17f631Sdmitry pervushin #include <linux/interrupt.h> 24df17f631Sdmitry pervushin #include <linux/rtc.h> 255a0e3ad6STejun Heo #include <linux/slab.h> 26df17f631Sdmitry pervushin 27df17f631Sdmitry pervushin #include <mach/platform.h> 28df17f631Sdmitry pervushin #include <mach/stmp3xxx.h> 29*47eac337SWolfram Sang 30*47eac337SWolfram Sang #define STMP3XXX_RTC_CTRL 0x0 31*47eac337SWolfram Sang #define STMP3XXX_RTC_CTRL_ALARM_IRQ_EN 0x00000001 32*47eac337SWolfram Sang #define STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN 0x00000002 33*47eac337SWolfram Sang #define STMP3XXX_RTC_CTRL_ALARM_IRQ 0x00000004 34*47eac337SWolfram Sang #define STMP3XXX_RTC_CTRL_ONEMSEC_IRQ 0x00000008 35*47eac337SWolfram Sang 36*47eac337SWolfram Sang #define STMP3XXX_RTC_STAT 0x10 37*47eac337SWolfram Sang #define STMP3XXX_RTC_STAT_STALE_SHIFT 16 38*47eac337SWolfram Sang #define STMP3XXX_RTC_STAT_RTC_PRESENT 0x80000000 39*47eac337SWolfram Sang 40*47eac337SWolfram Sang #define STMP3XXX_RTC_SECONDS 0x30 41*47eac337SWolfram Sang 42*47eac337SWolfram Sang #define STMP3XXX_RTC_ALARM 0x40 43*47eac337SWolfram Sang 44*47eac337SWolfram Sang #define STMP3XXX_RTC_PERSISTENT0 0x60 45*47eac337SWolfram Sang #define STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN 0x00000002 46*47eac337SWolfram Sang #define STMP3XXX_RTC_PERSISTENT0_ALARM_EN 0x00000004 47*47eac337SWolfram Sang #define STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE 0x00000080 48df17f631Sdmitry pervushin 49df17f631Sdmitry pervushin struct stmp3xxx_rtc_data { 50df17f631Sdmitry pervushin struct rtc_device *rtc; 51df17f631Sdmitry pervushin unsigned irq_count; 52df17f631Sdmitry pervushin void __iomem *io; 53df17f631Sdmitry pervushin int irq_alarm, irq_1msec; 54df17f631Sdmitry pervushin }; 55df17f631Sdmitry pervushin 56df17f631Sdmitry pervushin static void stmp3xxx_wait_time(struct stmp3xxx_rtc_data *rtc_data) 57df17f631Sdmitry pervushin { 58df17f631Sdmitry pervushin /* 59df17f631Sdmitry pervushin * The datasheet doesn't say which way round the 60df17f631Sdmitry pervushin * NEW_REGS/STALE_REGS bitfields go. In fact it's 0x1=P0, 61df17f631Sdmitry pervushin * 0x2=P1, .., 0x20=P5, 0x40=ALARM, 0x80=SECONDS 62df17f631Sdmitry pervushin */ 63*47eac337SWolfram Sang while (__raw_readl(rtc_data->io + STMP3XXX_RTC_STAT) & 64*47eac337SWolfram Sang (0x80 << STMP3XXX_RTC_STAT_STALE_SHIFT)) 65df17f631Sdmitry pervushin cpu_relax(); 66df17f631Sdmitry pervushin } 67df17f631Sdmitry pervushin 68df17f631Sdmitry pervushin /* Time read/write */ 69df17f631Sdmitry pervushin static int stmp3xxx_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) 70df17f631Sdmitry pervushin { 71df17f631Sdmitry pervushin struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); 72df17f631Sdmitry pervushin 73df17f631Sdmitry pervushin stmp3xxx_wait_time(rtc_data); 74*47eac337SWolfram Sang rtc_time_to_tm(__raw_readl(rtc_data->io + STMP3XXX_RTC_SECONDS), 75*47eac337SWolfram Sang rtc_tm); 76df17f631Sdmitry pervushin return 0; 77df17f631Sdmitry pervushin } 78df17f631Sdmitry pervushin 79df17f631Sdmitry pervushin static int stmp3xxx_rtc_set_mmss(struct device *dev, unsigned long t) 80df17f631Sdmitry pervushin { 81df17f631Sdmitry pervushin struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); 82df17f631Sdmitry pervushin 83*47eac337SWolfram Sang __raw_writel(t, rtc_data->io + STMP3XXX_RTC_SECONDS); 84df17f631Sdmitry pervushin stmp3xxx_wait_time(rtc_data); 85df17f631Sdmitry pervushin return 0; 86df17f631Sdmitry pervushin } 87df17f631Sdmitry pervushin 88df17f631Sdmitry pervushin /* interrupt(s) handler */ 89df17f631Sdmitry pervushin static irqreturn_t stmp3xxx_rtc_interrupt(int irq, void *dev_id) 90df17f631Sdmitry pervushin { 91df17f631Sdmitry pervushin struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev_id); 92df17f631Sdmitry pervushin u32 status; 93df17f631Sdmitry pervushin u32 events = 0; 94df17f631Sdmitry pervushin 95*47eac337SWolfram Sang status = __raw_readl(rtc_data->io + STMP3XXX_RTC_CTRL) & 96*47eac337SWolfram Sang (STMP3XXX_RTC_CTRL_ALARM_IRQ | 97*47eac337SWolfram Sang STMP3XXX_RTC_CTRL_ONEMSEC_IRQ); 98df17f631Sdmitry pervushin 99*47eac337SWolfram Sang if (status & STMP3XXX_RTC_CTRL_ALARM_IRQ) { 100*47eac337SWolfram Sang stmp3xxx_clearl(STMP3XXX_RTC_CTRL_ALARM_IRQ, 101*47eac337SWolfram Sang rtc_data->io + STMP3XXX_RTC_CTRL); 102df17f631Sdmitry pervushin events |= RTC_AF | RTC_IRQF; 103df17f631Sdmitry pervushin } 104df17f631Sdmitry pervushin 105*47eac337SWolfram Sang if (status & STMP3XXX_RTC_CTRL_ONEMSEC_IRQ) { 106*47eac337SWolfram Sang stmp3xxx_clearl(STMP3XXX_RTC_CTRL_ONEMSEC_IRQ, 107*47eac337SWolfram Sang rtc_data->io + STMP3XXX_RTC_CTRL); 108df17f631Sdmitry pervushin if (++rtc_data->irq_count % 1000 == 0) { 109df17f631Sdmitry pervushin events |= RTC_UF | RTC_IRQF; 110df17f631Sdmitry pervushin rtc_data->irq_count = 0; 111df17f631Sdmitry pervushin } 112df17f631Sdmitry pervushin } 113df17f631Sdmitry pervushin 114df17f631Sdmitry pervushin if (events) 115df17f631Sdmitry pervushin rtc_update_irq(rtc_data->rtc, 1, events); 116df17f631Sdmitry pervushin 117df17f631Sdmitry pervushin return IRQ_HANDLED; 118df17f631Sdmitry pervushin } 119df17f631Sdmitry pervushin 120df17f631Sdmitry pervushin static int stmp3xxx_alarm_irq_enable(struct device *dev, unsigned int enabled) 121df17f631Sdmitry pervushin { 122df17f631Sdmitry pervushin struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); 123*47eac337SWolfram Sang void __iomem *p = rtc_data->io + STMP3XXX_RTC_PERSISTENT0, 124*47eac337SWolfram Sang *ctl = rtc_data->io + STMP3XXX_RTC_CTRL; 125df17f631Sdmitry pervushin 126df17f631Sdmitry pervushin if (enabled) { 127*47eac337SWolfram Sang stmp3xxx_setl(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | 128*47eac337SWolfram Sang STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN, p); 129*47eac337SWolfram Sang stmp3xxx_setl(STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, ctl); 130df17f631Sdmitry pervushin } else { 131*47eac337SWolfram Sang stmp3xxx_clearl(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | 132*47eac337SWolfram Sang STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN, p); 133*47eac337SWolfram Sang stmp3xxx_clearl(STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, ctl); 134df17f631Sdmitry pervushin } 135df17f631Sdmitry pervushin return 0; 136df17f631Sdmitry pervushin } 137df17f631Sdmitry pervushin 138df17f631Sdmitry pervushin static int stmp3xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) 139df17f631Sdmitry pervushin { 140df17f631Sdmitry pervushin struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); 141df17f631Sdmitry pervushin 142*47eac337SWolfram Sang rtc_time_to_tm(__raw_readl(rtc_data->io + STMP3XXX_RTC_ALARM), 143*47eac337SWolfram Sang &alm->time); 144df17f631Sdmitry pervushin return 0; 145df17f631Sdmitry pervushin } 146df17f631Sdmitry pervushin 147df17f631Sdmitry pervushin static int stmp3xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) 148df17f631Sdmitry pervushin { 149df17f631Sdmitry pervushin unsigned long t; 150df17f631Sdmitry pervushin struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); 151df17f631Sdmitry pervushin 152df17f631Sdmitry pervushin rtc_tm_to_time(&alm->time, &t); 153*47eac337SWolfram Sang __raw_writel(t, rtc_data->io + STMP3XXX_RTC_ALARM); 154df17f631Sdmitry pervushin return 0; 155df17f631Sdmitry pervushin } 156df17f631Sdmitry pervushin 157df17f631Sdmitry pervushin static struct rtc_class_ops stmp3xxx_rtc_ops = { 158df17f631Sdmitry pervushin .alarm_irq_enable = 159df17f631Sdmitry pervushin stmp3xxx_alarm_irq_enable, 160df17f631Sdmitry pervushin .read_time = stmp3xxx_rtc_gettime, 161df17f631Sdmitry pervushin .set_mmss = stmp3xxx_rtc_set_mmss, 162df17f631Sdmitry pervushin .read_alarm = stmp3xxx_rtc_read_alarm, 163df17f631Sdmitry pervushin .set_alarm = stmp3xxx_rtc_set_alarm, 164df17f631Sdmitry pervushin }; 165df17f631Sdmitry pervushin 166df17f631Sdmitry pervushin static int stmp3xxx_rtc_remove(struct platform_device *pdev) 167df17f631Sdmitry pervushin { 168df17f631Sdmitry pervushin struct stmp3xxx_rtc_data *rtc_data = platform_get_drvdata(pdev); 169df17f631Sdmitry pervushin 170df17f631Sdmitry pervushin if (!rtc_data) 171df17f631Sdmitry pervushin return 0; 172df17f631Sdmitry pervushin 173*47eac337SWolfram Sang stmp3xxx_clearl(STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN | 174*47eac337SWolfram Sang STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, 175*47eac337SWolfram Sang rtc_data->io + STMP3XXX_RTC_CTRL); 176df17f631Sdmitry pervushin free_irq(rtc_data->irq_alarm, &pdev->dev); 177df17f631Sdmitry pervushin free_irq(rtc_data->irq_1msec, &pdev->dev); 178df17f631Sdmitry pervushin rtc_device_unregister(rtc_data->rtc); 179df17f631Sdmitry pervushin iounmap(rtc_data->io); 180df17f631Sdmitry pervushin kfree(rtc_data); 181df17f631Sdmitry pervushin 182df17f631Sdmitry pervushin return 0; 183df17f631Sdmitry pervushin } 184df17f631Sdmitry pervushin 185df17f631Sdmitry pervushin static int stmp3xxx_rtc_probe(struct platform_device *pdev) 186df17f631Sdmitry pervushin { 187df17f631Sdmitry pervushin struct stmp3xxx_rtc_data *rtc_data; 188df17f631Sdmitry pervushin struct resource *r; 189df17f631Sdmitry pervushin int err; 190df17f631Sdmitry pervushin 191df17f631Sdmitry pervushin rtc_data = kzalloc(sizeof *rtc_data, GFP_KERNEL); 192df17f631Sdmitry pervushin if (!rtc_data) 193df17f631Sdmitry pervushin return -ENOMEM; 194df17f631Sdmitry pervushin 195df17f631Sdmitry pervushin r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 196df17f631Sdmitry pervushin if (!r) { 197df17f631Sdmitry pervushin dev_err(&pdev->dev, "failed to get resource\n"); 198df17f631Sdmitry pervushin err = -ENXIO; 199df17f631Sdmitry pervushin goto out_free; 200df17f631Sdmitry pervushin } 201df17f631Sdmitry pervushin 202df17f631Sdmitry pervushin rtc_data->io = ioremap(r->start, resource_size(r)); 203df17f631Sdmitry pervushin if (!rtc_data->io) { 204df17f631Sdmitry pervushin dev_err(&pdev->dev, "ioremap failed\n"); 205df17f631Sdmitry pervushin err = -EIO; 206df17f631Sdmitry pervushin goto out_free; 207df17f631Sdmitry pervushin } 208df17f631Sdmitry pervushin 209df17f631Sdmitry pervushin rtc_data->irq_alarm = platform_get_irq(pdev, 0); 210df17f631Sdmitry pervushin rtc_data->irq_1msec = platform_get_irq(pdev, 1); 211df17f631Sdmitry pervushin 212*47eac337SWolfram Sang if (!(__raw_readl(STMP3XXX_RTC_STAT + rtc_data->io) & 213*47eac337SWolfram Sang STMP3XXX_RTC_STAT_RTC_PRESENT)) { 214df17f631Sdmitry pervushin dev_err(&pdev->dev, "no device onboard\n"); 215df17f631Sdmitry pervushin err = -ENODEV; 216df17f631Sdmitry pervushin goto out_remap; 217df17f631Sdmitry pervushin } 218df17f631Sdmitry pervushin 219df17f631Sdmitry pervushin stmp3xxx_reset_block(rtc_data->io, true); 220*47eac337SWolfram Sang stmp3xxx_clearl(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | 221*47eac337SWolfram Sang STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN | 222*47eac337SWolfram Sang STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE, 223*47eac337SWolfram Sang rtc_data->io + STMP3XXX_RTC_PERSISTENT0); 224df17f631Sdmitry pervushin rtc_data->rtc = rtc_device_register(pdev->name, &pdev->dev, 225df17f631Sdmitry pervushin &stmp3xxx_rtc_ops, THIS_MODULE); 226df17f631Sdmitry pervushin if (IS_ERR(rtc_data->rtc)) { 227df17f631Sdmitry pervushin err = PTR_ERR(rtc_data->rtc); 228df17f631Sdmitry pervushin goto out_remap; 229df17f631Sdmitry pervushin } 230df17f631Sdmitry pervushin 231df17f631Sdmitry pervushin rtc_data->irq_count = 0; 232df17f631Sdmitry pervushin err = request_irq(rtc_data->irq_alarm, stmp3xxx_rtc_interrupt, 233df17f631Sdmitry pervushin IRQF_DISABLED, "RTC alarm", &pdev->dev); 234df17f631Sdmitry pervushin if (err) { 235df17f631Sdmitry pervushin dev_err(&pdev->dev, "Cannot claim IRQ%d\n", 236df17f631Sdmitry pervushin rtc_data->irq_alarm); 237df17f631Sdmitry pervushin goto out_irq_alarm; 238df17f631Sdmitry pervushin } 239df17f631Sdmitry pervushin err = request_irq(rtc_data->irq_1msec, stmp3xxx_rtc_interrupt, 240df17f631Sdmitry pervushin IRQF_DISABLED, "RTC tick", &pdev->dev); 241df17f631Sdmitry pervushin if (err) { 242df17f631Sdmitry pervushin dev_err(&pdev->dev, "Cannot claim IRQ%d\n", 243df17f631Sdmitry pervushin rtc_data->irq_1msec); 244df17f631Sdmitry pervushin goto out_irq1; 245df17f631Sdmitry pervushin } 246df17f631Sdmitry pervushin 247df17f631Sdmitry pervushin platform_set_drvdata(pdev, rtc_data); 248df17f631Sdmitry pervushin 249df17f631Sdmitry pervushin return 0; 250df17f631Sdmitry pervushin 251df17f631Sdmitry pervushin out_irq1: 252df17f631Sdmitry pervushin free_irq(rtc_data->irq_alarm, &pdev->dev); 253df17f631Sdmitry pervushin out_irq_alarm: 254*47eac337SWolfram Sang stmp3xxx_clearl(STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN | 255*47eac337SWolfram Sang STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, 256*47eac337SWolfram Sang rtc_data->io + STMP3XXX_RTC_CTRL); 257df17f631Sdmitry pervushin rtc_device_unregister(rtc_data->rtc); 258df17f631Sdmitry pervushin out_remap: 259df17f631Sdmitry pervushin iounmap(rtc_data->io); 260df17f631Sdmitry pervushin out_free: 261df17f631Sdmitry pervushin kfree(rtc_data); 262df17f631Sdmitry pervushin return err; 263df17f631Sdmitry pervushin } 264df17f631Sdmitry pervushin 265df17f631Sdmitry pervushin #ifdef CONFIG_PM 266df17f631Sdmitry pervushin static int stmp3xxx_rtc_suspend(struct platform_device *dev, pm_message_t state) 267df17f631Sdmitry pervushin { 268df17f631Sdmitry pervushin return 0; 269df17f631Sdmitry pervushin } 270df17f631Sdmitry pervushin 271df17f631Sdmitry pervushin static int stmp3xxx_rtc_resume(struct platform_device *dev) 272df17f631Sdmitry pervushin { 273df17f631Sdmitry pervushin struct stmp3xxx_rtc_data *rtc_data = platform_get_drvdata(dev); 274df17f631Sdmitry pervushin 275df17f631Sdmitry pervushin stmp3xxx_reset_block(rtc_data->io, true); 276*47eac337SWolfram Sang stmp3xxx_clearl(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | 277*47eac337SWolfram Sang STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN | 278*47eac337SWolfram Sang STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE, 279*47eac337SWolfram Sang rtc_data->io + STMP3XXX_RTC_PERSISTENT0); 280df17f631Sdmitry pervushin return 0; 281df17f631Sdmitry pervushin } 282df17f631Sdmitry pervushin #else 283df17f631Sdmitry pervushin #define stmp3xxx_rtc_suspend NULL 284df17f631Sdmitry pervushin #define stmp3xxx_rtc_resume NULL 285df17f631Sdmitry pervushin #endif 286df17f631Sdmitry pervushin 287df17f631Sdmitry pervushin static struct platform_driver stmp3xxx_rtcdrv = { 288df17f631Sdmitry pervushin .probe = stmp3xxx_rtc_probe, 289df17f631Sdmitry pervushin .remove = stmp3xxx_rtc_remove, 290df17f631Sdmitry pervushin .suspend = stmp3xxx_rtc_suspend, 291df17f631Sdmitry pervushin .resume = stmp3xxx_rtc_resume, 292df17f631Sdmitry pervushin .driver = { 293df17f631Sdmitry pervushin .name = "stmp3xxx-rtc", 294df17f631Sdmitry pervushin .owner = THIS_MODULE, 295df17f631Sdmitry pervushin }, 296df17f631Sdmitry pervushin }; 297df17f631Sdmitry pervushin 298df17f631Sdmitry pervushin static int __init stmp3xxx_rtc_init(void) 299df17f631Sdmitry pervushin { 300df17f631Sdmitry pervushin return platform_driver_register(&stmp3xxx_rtcdrv); 301df17f631Sdmitry pervushin } 302df17f631Sdmitry pervushin 303df17f631Sdmitry pervushin static void __exit stmp3xxx_rtc_exit(void) 304df17f631Sdmitry pervushin { 305df17f631Sdmitry pervushin platform_driver_unregister(&stmp3xxx_rtcdrv); 306df17f631Sdmitry pervushin } 307df17f631Sdmitry pervushin 308df17f631Sdmitry pervushin module_init(stmp3xxx_rtc_init); 309df17f631Sdmitry pervushin module_exit(stmp3xxx_rtc_exit); 310df17f631Sdmitry pervushin 311df17f631Sdmitry pervushin MODULE_DESCRIPTION("STMP3xxx RTC Driver"); 312df17f631Sdmitry pervushin MODULE_AUTHOR("dmitry pervushin <dpervushin@embeddedalley.com>"); 313df17f631Sdmitry pervushin MODULE_LICENSE("GPL"); 314