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. 97e794cb7SWolfram Sang * Copyright 2011 Wolfram Sang, Pengutronix e.K. 10df17f631Sdmitry pervushin */ 11df17f631Sdmitry pervushin 12df17f631Sdmitry pervushin /* 13df17f631Sdmitry pervushin * The code contained herein is licensed under the GNU General Public 14df17f631Sdmitry pervushin * License. You may obtain a copy of the GNU General Public License 15df17f631Sdmitry pervushin * Version 2 or later at the following locations: 16df17f631Sdmitry pervushin * 17df17f631Sdmitry pervushin * http://www.opensource.org/licenses/gpl-license.html 18df17f631Sdmitry pervushin * http://www.gnu.org/copyleft/gpl.html 19df17f631Sdmitry pervushin */ 20df17f631Sdmitry pervushin #include <linux/kernel.h> 21df17f631Sdmitry pervushin #include <linux/module.h> 22b5167159SWolfram Sang #include <linux/io.h> 23df17f631Sdmitry pervushin #include <linux/init.h> 24df17f631Sdmitry pervushin #include <linux/platform_device.h> 25df17f631Sdmitry pervushin #include <linux/interrupt.h> 26df17f631Sdmitry pervushin #include <linux/rtc.h> 275a0e3ad6STejun Heo #include <linux/slab.h> 28*dd8d20a3SMarek Vasut #include <linux/of_device.h> 29df17f631Sdmitry pervushin 3046b21218SWolfram Sang #include <mach/common.h> 3147eac337SWolfram Sang 3247eac337SWolfram Sang #define STMP3XXX_RTC_CTRL 0x0 33b5167159SWolfram Sang #define STMP3XXX_RTC_CTRL_SET 0x4 34b5167159SWolfram Sang #define STMP3XXX_RTC_CTRL_CLR 0x8 3547eac337SWolfram Sang #define STMP3XXX_RTC_CTRL_ALARM_IRQ_EN 0x00000001 3647eac337SWolfram Sang #define STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN 0x00000002 3747eac337SWolfram Sang #define STMP3XXX_RTC_CTRL_ALARM_IRQ 0x00000004 3847eac337SWolfram Sang 3947eac337SWolfram Sang #define STMP3XXX_RTC_STAT 0x10 4047eac337SWolfram Sang #define STMP3XXX_RTC_STAT_STALE_SHIFT 16 4147eac337SWolfram Sang #define STMP3XXX_RTC_STAT_RTC_PRESENT 0x80000000 4247eac337SWolfram Sang 4347eac337SWolfram Sang #define STMP3XXX_RTC_SECONDS 0x30 4447eac337SWolfram Sang 4547eac337SWolfram Sang #define STMP3XXX_RTC_ALARM 0x40 4647eac337SWolfram Sang 4747eac337SWolfram Sang #define STMP3XXX_RTC_PERSISTENT0 0x60 48b5167159SWolfram Sang #define STMP3XXX_RTC_PERSISTENT0_SET 0x64 49b5167159SWolfram Sang #define STMP3XXX_RTC_PERSISTENT0_CLR 0x68 5047eac337SWolfram Sang #define STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN 0x00000002 5147eac337SWolfram Sang #define STMP3XXX_RTC_PERSISTENT0_ALARM_EN 0x00000004 5247eac337SWolfram Sang #define STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE 0x00000080 53df17f631Sdmitry pervushin 54df17f631Sdmitry pervushin struct stmp3xxx_rtc_data { 55df17f631Sdmitry pervushin struct rtc_device *rtc; 56df17f631Sdmitry pervushin void __iomem *io; 577e794cb7SWolfram Sang int irq_alarm; 58df17f631Sdmitry pervushin }; 59df17f631Sdmitry pervushin 60df17f631Sdmitry pervushin static void stmp3xxx_wait_time(struct stmp3xxx_rtc_data *rtc_data) 61df17f631Sdmitry pervushin { 62df17f631Sdmitry pervushin /* 63df17f631Sdmitry pervushin * The datasheet doesn't say which way round the 64df17f631Sdmitry pervushin * NEW_REGS/STALE_REGS bitfields go. In fact it's 0x1=P0, 65df17f631Sdmitry pervushin * 0x2=P1, .., 0x20=P5, 0x40=ALARM, 0x80=SECONDS 66df17f631Sdmitry pervushin */ 67b5167159SWolfram Sang while (readl(rtc_data->io + STMP3XXX_RTC_STAT) & 6847eac337SWolfram Sang (0x80 << STMP3XXX_RTC_STAT_STALE_SHIFT)) 69df17f631Sdmitry pervushin cpu_relax(); 70df17f631Sdmitry pervushin } 71df17f631Sdmitry pervushin 72df17f631Sdmitry pervushin /* Time read/write */ 73df17f631Sdmitry pervushin static int stmp3xxx_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) 74df17f631Sdmitry pervushin { 75df17f631Sdmitry pervushin struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); 76df17f631Sdmitry pervushin 77df17f631Sdmitry pervushin stmp3xxx_wait_time(rtc_data); 78b5167159SWolfram Sang rtc_time_to_tm(readl(rtc_data->io + STMP3XXX_RTC_SECONDS), rtc_tm); 79df17f631Sdmitry pervushin return 0; 80df17f631Sdmitry pervushin } 81df17f631Sdmitry pervushin 82df17f631Sdmitry pervushin static int stmp3xxx_rtc_set_mmss(struct device *dev, unsigned long t) 83df17f631Sdmitry pervushin { 84df17f631Sdmitry pervushin struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); 85df17f631Sdmitry pervushin 86b5167159SWolfram Sang writel(t, rtc_data->io + STMP3XXX_RTC_SECONDS); 87df17f631Sdmitry pervushin stmp3xxx_wait_time(rtc_data); 88df17f631Sdmitry pervushin return 0; 89df17f631Sdmitry pervushin } 90df17f631Sdmitry pervushin 91df17f631Sdmitry pervushin /* interrupt(s) handler */ 92df17f631Sdmitry pervushin static irqreturn_t stmp3xxx_rtc_interrupt(int irq, void *dev_id) 93df17f631Sdmitry pervushin { 94df17f631Sdmitry pervushin struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev_id); 957e794cb7SWolfram Sang u32 status = readl(rtc_data->io + STMP3XXX_RTC_CTRL); 96df17f631Sdmitry pervushin 9747eac337SWolfram Sang if (status & STMP3XXX_RTC_CTRL_ALARM_IRQ) { 98b5167159SWolfram Sang writel(STMP3XXX_RTC_CTRL_ALARM_IRQ, 99b5167159SWolfram Sang rtc_data->io + STMP3XXX_RTC_CTRL_CLR); 1007e794cb7SWolfram Sang rtc_update_irq(rtc_data->rtc, 1, RTC_AF | RTC_IRQF); 101df17f631Sdmitry pervushin return IRQ_HANDLED; 102df17f631Sdmitry pervushin } 103df17f631Sdmitry pervushin 1047e794cb7SWolfram Sang return IRQ_NONE; 1057e794cb7SWolfram Sang } 1067e794cb7SWolfram Sang 107df17f631Sdmitry pervushin static int stmp3xxx_alarm_irq_enable(struct device *dev, unsigned int enabled) 108df17f631Sdmitry pervushin { 109df17f631Sdmitry pervushin struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); 110df17f631Sdmitry pervushin 111df17f631Sdmitry pervushin if (enabled) { 112b5167159SWolfram Sang writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | 113b5167159SWolfram Sang STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN, 114b5167159SWolfram Sang rtc_data->io + STMP3XXX_RTC_PERSISTENT0_SET); 115b5167159SWolfram Sang writel(STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, 116b5167159SWolfram Sang rtc_data->io + STMP3XXX_RTC_CTRL_SET); 117df17f631Sdmitry pervushin } else { 118b5167159SWolfram Sang writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | 119b5167159SWolfram Sang STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN, 120b5167159SWolfram Sang rtc_data->io + STMP3XXX_RTC_PERSISTENT0_CLR); 121b5167159SWolfram Sang writel(STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, 122b5167159SWolfram Sang rtc_data->io + STMP3XXX_RTC_CTRL_CLR); 123df17f631Sdmitry pervushin } 124df17f631Sdmitry pervushin return 0; 125df17f631Sdmitry pervushin } 126df17f631Sdmitry pervushin 127df17f631Sdmitry pervushin static int stmp3xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) 128df17f631Sdmitry pervushin { 129df17f631Sdmitry pervushin struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); 130df17f631Sdmitry pervushin 131b5167159SWolfram Sang rtc_time_to_tm(readl(rtc_data->io + STMP3XXX_RTC_ALARM), &alm->time); 132df17f631Sdmitry pervushin return 0; 133df17f631Sdmitry pervushin } 134df17f631Sdmitry pervushin 135df17f631Sdmitry pervushin static int stmp3xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) 136df17f631Sdmitry pervushin { 137df17f631Sdmitry pervushin unsigned long t; 138df17f631Sdmitry pervushin struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); 139df17f631Sdmitry pervushin 140df17f631Sdmitry pervushin rtc_tm_to_time(&alm->time, &t); 141b5167159SWolfram Sang writel(t, rtc_data->io + STMP3XXX_RTC_ALARM); 1427e794cb7SWolfram Sang 1437e794cb7SWolfram Sang stmp3xxx_alarm_irq_enable(dev, alm->enabled); 1447e794cb7SWolfram Sang 145df17f631Sdmitry pervushin return 0; 146df17f631Sdmitry pervushin } 147df17f631Sdmitry pervushin 148df17f631Sdmitry pervushin static struct rtc_class_ops stmp3xxx_rtc_ops = { 149df17f631Sdmitry pervushin .alarm_irq_enable = 150df17f631Sdmitry pervushin stmp3xxx_alarm_irq_enable, 151df17f631Sdmitry pervushin .read_time = stmp3xxx_rtc_gettime, 152df17f631Sdmitry pervushin .set_mmss = stmp3xxx_rtc_set_mmss, 153df17f631Sdmitry pervushin .read_alarm = stmp3xxx_rtc_read_alarm, 154df17f631Sdmitry pervushin .set_alarm = stmp3xxx_rtc_set_alarm, 155df17f631Sdmitry pervushin }; 156df17f631Sdmitry pervushin 157df17f631Sdmitry pervushin static int stmp3xxx_rtc_remove(struct platform_device *pdev) 158df17f631Sdmitry pervushin { 159df17f631Sdmitry pervushin struct stmp3xxx_rtc_data *rtc_data = platform_get_drvdata(pdev); 160df17f631Sdmitry pervushin 161df17f631Sdmitry pervushin if (!rtc_data) 162df17f631Sdmitry pervushin return 0; 163df17f631Sdmitry pervushin 1647e794cb7SWolfram Sang writel(STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, 165b5167159SWolfram Sang rtc_data->io + STMP3XXX_RTC_CTRL_CLR); 166df17f631Sdmitry pervushin free_irq(rtc_data->irq_alarm, &pdev->dev); 167df17f631Sdmitry pervushin rtc_device_unregister(rtc_data->rtc); 168a91d2babSWolfram Sang platform_set_drvdata(pdev, NULL); 169df17f631Sdmitry pervushin iounmap(rtc_data->io); 170df17f631Sdmitry pervushin kfree(rtc_data); 171df17f631Sdmitry pervushin 172df17f631Sdmitry pervushin return 0; 173df17f631Sdmitry pervushin } 174df17f631Sdmitry pervushin 175df17f631Sdmitry pervushin static int stmp3xxx_rtc_probe(struct platform_device *pdev) 176df17f631Sdmitry pervushin { 177df17f631Sdmitry pervushin struct stmp3xxx_rtc_data *rtc_data; 178df17f631Sdmitry pervushin struct resource *r; 179df17f631Sdmitry pervushin int err; 180df17f631Sdmitry pervushin 181df17f631Sdmitry pervushin rtc_data = kzalloc(sizeof *rtc_data, GFP_KERNEL); 182df17f631Sdmitry pervushin if (!rtc_data) 183df17f631Sdmitry pervushin return -ENOMEM; 184df17f631Sdmitry pervushin 185df17f631Sdmitry pervushin r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 186df17f631Sdmitry pervushin if (!r) { 187df17f631Sdmitry pervushin dev_err(&pdev->dev, "failed to get resource\n"); 188df17f631Sdmitry pervushin err = -ENXIO; 189df17f631Sdmitry pervushin goto out_free; 190df17f631Sdmitry pervushin } 191df17f631Sdmitry pervushin 192df17f631Sdmitry pervushin rtc_data->io = ioremap(r->start, resource_size(r)); 193df17f631Sdmitry pervushin if (!rtc_data->io) { 194df17f631Sdmitry pervushin dev_err(&pdev->dev, "ioremap failed\n"); 195df17f631Sdmitry pervushin err = -EIO; 196df17f631Sdmitry pervushin goto out_free; 197df17f631Sdmitry pervushin } 198df17f631Sdmitry pervushin 199df17f631Sdmitry pervushin rtc_data->irq_alarm = platform_get_irq(pdev, 0); 200df17f631Sdmitry pervushin 201b5167159SWolfram Sang if (!(readl(STMP3XXX_RTC_STAT + rtc_data->io) & 20247eac337SWolfram Sang STMP3XXX_RTC_STAT_RTC_PRESENT)) { 203df17f631Sdmitry pervushin dev_err(&pdev->dev, "no device onboard\n"); 204df17f631Sdmitry pervushin err = -ENODEV; 205df17f631Sdmitry pervushin goto out_remap; 206df17f631Sdmitry pervushin } 207df17f631Sdmitry pervushin 208a91d2babSWolfram Sang platform_set_drvdata(pdev, rtc_data); 209a91d2babSWolfram Sang 21046b21218SWolfram Sang mxs_reset_block(rtc_data->io); 211b5167159SWolfram Sang writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | 21247eac337SWolfram Sang STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN | 21347eac337SWolfram Sang STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE, 214b5167159SWolfram Sang rtc_data->io + STMP3XXX_RTC_PERSISTENT0_CLR); 215a91d2babSWolfram Sang 2167e794cb7SWolfram Sang writel(STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN | 2177e794cb7SWolfram Sang STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, 2187e794cb7SWolfram Sang rtc_data->io + STMP3XXX_RTC_CTRL_CLR); 2197e794cb7SWolfram Sang 220df17f631Sdmitry pervushin rtc_data->rtc = rtc_device_register(pdev->name, &pdev->dev, 221df17f631Sdmitry pervushin &stmp3xxx_rtc_ops, THIS_MODULE); 222df17f631Sdmitry pervushin if (IS_ERR(rtc_data->rtc)) { 223df17f631Sdmitry pervushin err = PTR_ERR(rtc_data->rtc); 224df17f631Sdmitry pervushin goto out_remap; 225df17f631Sdmitry pervushin } 226df17f631Sdmitry pervushin 2277e794cb7SWolfram Sang err = request_irq(rtc_data->irq_alarm, stmp3xxx_rtc_interrupt, 0, 2287e794cb7SWolfram Sang "RTC alarm", &pdev->dev); 229df17f631Sdmitry pervushin if (err) { 230df17f631Sdmitry pervushin dev_err(&pdev->dev, "Cannot claim IRQ%d\n", 231df17f631Sdmitry pervushin rtc_data->irq_alarm); 232df17f631Sdmitry pervushin goto out_irq_alarm; 233df17f631Sdmitry pervushin } 234df17f631Sdmitry pervushin 235df17f631Sdmitry pervushin return 0; 236df17f631Sdmitry pervushin 237df17f631Sdmitry pervushin out_irq_alarm: 238df17f631Sdmitry pervushin rtc_device_unregister(rtc_data->rtc); 239df17f631Sdmitry pervushin out_remap: 240a91d2babSWolfram Sang platform_set_drvdata(pdev, NULL); 241df17f631Sdmitry pervushin iounmap(rtc_data->io); 242df17f631Sdmitry pervushin out_free: 243df17f631Sdmitry pervushin kfree(rtc_data); 244df17f631Sdmitry pervushin return err; 245df17f631Sdmitry pervushin } 246df17f631Sdmitry pervushin 247df17f631Sdmitry pervushin #ifdef CONFIG_PM 248df17f631Sdmitry pervushin static int stmp3xxx_rtc_suspend(struct platform_device *dev, pm_message_t state) 249df17f631Sdmitry pervushin { 250df17f631Sdmitry pervushin return 0; 251df17f631Sdmitry pervushin } 252df17f631Sdmitry pervushin 253df17f631Sdmitry pervushin static int stmp3xxx_rtc_resume(struct platform_device *dev) 254df17f631Sdmitry pervushin { 255df17f631Sdmitry pervushin struct stmp3xxx_rtc_data *rtc_data = platform_get_drvdata(dev); 256df17f631Sdmitry pervushin 25746b21218SWolfram Sang mxs_reset_block(rtc_data->io); 258b5167159SWolfram Sang writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | 25947eac337SWolfram Sang STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN | 26047eac337SWolfram Sang STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE, 261b5167159SWolfram Sang rtc_data->io + STMP3XXX_RTC_PERSISTENT0_CLR); 262df17f631Sdmitry pervushin return 0; 263df17f631Sdmitry pervushin } 264df17f631Sdmitry pervushin #else 265df17f631Sdmitry pervushin #define stmp3xxx_rtc_suspend NULL 266df17f631Sdmitry pervushin #define stmp3xxx_rtc_resume NULL 267df17f631Sdmitry pervushin #endif 268df17f631Sdmitry pervushin 269*dd8d20a3SMarek Vasut static const struct of_device_id rtc_dt_ids[] = { 270*dd8d20a3SMarek Vasut { .compatible = "fsl,stmp3xxx-rtc", }, 271*dd8d20a3SMarek Vasut { /* sentinel */ } 272*dd8d20a3SMarek Vasut }; 273*dd8d20a3SMarek Vasut MODULE_DEVICE_TABLE(of, rtc_dt_ids); 274*dd8d20a3SMarek Vasut 275df17f631Sdmitry pervushin static struct platform_driver stmp3xxx_rtcdrv = { 276df17f631Sdmitry pervushin .probe = stmp3xxx_rtc_probe, 277df17f631Sdmitry pervushin .remove = stmp3xxx_rtc_remove, 278df17f631Sdmitry pervushin .suspend = stmp3xxx_rtc_suspend, 279df17f631Sdmitry pervushin .resume = stmp3xxx_rtc_resume, 280df17f631Sdmitry pervushin .driver = { 281df17f631Sdmitry pervushin .name = "stmp3xxx-rtc", 282df17f631Sdmitry pervushin .owner = THIS_MODULE, 283*dd8d20a3SMarek Vasut .of_match_table = rtc_dt_ids, 284df17f631Sdmitry pervushin }, 285df17f631Sdmitry pervushin }; 286df17f631Sdmitry pervushin 2870c4eae66SAxel Lin module_platform_driver(stmp3xxx_rtcdrv); 288df17f631Sdmitry pervushin 289df17f631Sdmitry pervushin MODULE_DESCRIPTION("STMP3xxx RTC Driver"); 2907e794cb7SWolfram Sang MODULE_AUTHOR("dmitry pervushin <dpervushin@embeddedalley.com> and " 2917e794cb7SWolfram Sang "Wolfram Sang <w.sang@pengutronix.de>"); 292df17f631Sdmitry pervushin MODULE_LICENSE("GPL"); 293