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