1a190901cSRussell King /* 2a190901cSRussell King * linux/drivers/rtc/rtc-pl030.c 3a190901cSRussell King * 4a190901cSRussell King * Copyright (C) 2000-2001 Deep Blue Solutions Ltd. 5a190901cSRussell King * 6a190901cSRussell King * This program is free software; you can redistribute it and/or modify 7a190901cSRussell King * it under the terms of the GNU General Public License version 2 as 8a190901cSRussell King * published by the Free Software Foundation. 9a190901cSRussell King */ 10a190901cSRussell King #include <linux/module.h> 11a190901cSRussell King #include <linux/rtc.h> 12a190901cSRussell King #include <linux/init.h> 13a190901cSRussell King #include <linux/interrupt.h> 14a190901cSRussell King #include <linux/amba/bus.h> 15a190901cSRussell King #include <linux/io.h> 165a0e3ad6STejun Heo #include <linux/slab.h> 17a190901cSRussell King 18a190901cSRussell King #define RTC_DR (0) 19a190901cSRussell King #define RTC_MR (4) 20a190901cSRussell King #define RTC_STAT (8) 21a190901cSRussell King #define RTC_EOI (8) 22a190901cSRussell King #define RTC_LR (12) 23a190901cSRussell King #define RTC_CR (16) 24a190901cSRussell King #define RTC_CR_MIE (1 << 0) 25a190901cSRussell King 26a190901cSRussell King struct pl030_rtc { 27a190901cSRussell King struct rtc_device *rtc; 28a190901cSRussell King void __iomem *base; 29a190901cSRussell King }; 30a190901cSRussell King 31a190901cSRussell King static irqreturn_t pl030_interrupt(int irq, void *dev_id) 32a190901cSRussell King { 33a190901cSRussell King struct pl030_rtc *rtc = dev_id; 34a190901cSRussell King writel(0, rtc->base + RTC_EOI); 35a190901cSRussell King return IRQ_HANDLED; 36a190901cSRussell King } 37a190901cSRussell King 38a190901cSRussell King static int pl030_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) 39a190901cSRussell King { 40a190901cSRussell King return -ENOIOCTLCMD; 41a190901cSRussell King } 42a190901cSRussell King 43a190901cSRussell King static int pl030_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) 44a190901cSRussell King { 45a190901cSRussell King struct pl030_rtc *rtc = dev_get_drvdata(dev); 46a190901cSRussell King 47a190901cSRussell King rtc_time_to_tm(readl(rtc->base + RTC_MR), &alrm->time); 48a190901cSRussell King return 0; 49a190901cSRussell King } 50a190901cSRussell King 51a190901cSRussell King static int pl030_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) 52a190901cSRussell King { 53a190901cSRussell King struct pl030_rtc *rtc = dev_get_drvdata(dev); 54a190901cSRussell King unsigned long time; 55a190901cSRussell King int ret; 56a190901cSRussell King 57a190901cSRussell King /* 58a190901cSRussell King * At the moment, we can only deal with non-wildcarded alarm times. 59a190901cSRussell King */ 60a190901cSRussell King ret = rtc_valid_tm(&alrm->time); 61a190901cSRussell King if (ret == 0) 62a190901cSRussell King ret = rtc_tm_to_time(&alrm->time, &time); 63a190901cSRussell King if (ret == 0) 64a190901cSRussell King writel(time, rtc->base + RTC_MR); 65a190901cSRussell King return ret; 66a190901cSRussell King } 67a190901cSRussell King 68a190901cSRussell King static int pl030_read_time(struct device *dev, struct rtc_time *tm) 69a190901cSRussell King { 70a190901cSRussell King struct pl030_rtc *rtc = dev_get_drvdata(dev); 71a190901cSRussell King 72a190901cSRussell King rtc_time_to_tm(readl(rtc->base + RTC_DR), tm); 73a190901cSRussell King 74a190901cSRussell King return 0; 75a190901cSRussell King } 76a190901cSRussell King 77a190901cSRussell King /* 78a190901cSRussell King * Set the RTC time. Unfortunately, we can't accurately set 79a190901cSRussell King * the point at which the counter updates. 80a190901cSRussell King * 81a190901cSRussell King * Also, since RTC_LR is transferred to RTC_CR on next rising 82a190901cSRussell King * edge of the 1Hz clock, we must write the time one second 83a190901cSRussell King * in advance. 84a190901cSRussell King */ 85a190901cSRussell King static int pl030_set_time(struct device *dev, struct rtc_time *tm) 86a190901cSRussell King { 87a190901cSRussell King struct pl030_rtc *rtc = dev_get_drvdata(dev); 88a190901cSRussell King unsigned long time; 89a190901cSRussell King int ret; 90a190901cSRussell King 91a190901cSRussell King ret = rtc_tm_to_time(tm, &time); 92a190901cSRussell King if (ret == 0) 93a190901cSRussell King writel(time + 1, rtc->base + RTC_LR); 94a190901cSRussell King 95a190901cSRussell King return ret; 96a190901cSRussell King } 97a190901cSRussell King 98a190901cSRussell King static const struct rtc_class_ops pl030_ops = { 99a190901cSRussell King .ioctl = pl030_ioctl, 100a190901cSRussell King .read_time = pl030_read_time, 101a190901cSRussell King .set_time = pl030_set_time, 102a190901cSRussell King .read_alarm = pl030_read_alarm, 103a190901cSRussell King .set_alarm = pl030_set_alarm, 104a190901cSRussell King }; 105a190901cSRussell King 106*aa25afadSRussell King static int pl030_probe(struct amba_device *dev, const struct amba_id *id) 107a190901cSRussell King { 108a190901cSRussell King struct pl030_rtc *rtc; 109a190901cSRussell King int ret; 110a190901cSRussell King 111a190901cSRussell King ret = amba_request_regions(dev, NULL); 112a190901cSRussell King if (ret) 113a190901cSRussell King goto err_req; 114a190901cSRussell King 115a190901cSRussell King rtc = kmalloc(sizeof(*rtc), GFP_KERNEL); 116a190901cSRussell King if (!rtc) { 117a190901cSRussell King ret = -ENOMEM; 118a190901cSRussell King goto err_rtc; 119a190901cSRussell King } 120a190901cSRussell King 121dc890c2dSLinus Walleij rtc->base = ioremap(dev->res.start, resource_size(&dev->res)); 122a190901cSRussell King if (!rtc->base) { 123a190901cSRussell King ret = -ENOMEM; 124a190901cSRussell King goto err_map; 125a190901cSRussell King } 126a190901cSRussell King 127a190901cSRussell King __raw_writel(0, rtc->base + RTC_CR); 128a190901cSRussell King __raw_writel(0, rtc->base + RTC_EOI); 129a190901cSRussell King 130a190901cSRussell King amba_set_drvdata(dev, rtc); 131a190901cSRussell King 132a190901cSRussell King ret = request_irq(dev->irq[0], pl030_interrupt, IRQF_DISABLED, 133a190901cSRussell King "rtc-pl030", rtc); 134a190901cSRussell King if (ret) 135a190901cSRussell King goto err_irq; 136a190901cSRussell King 137a190901cSRussell King rtc->rtc = rtc_device_register("pl030", &dev->dev, &pl030_ops, 138a190901cSRussell King THIS_MODULE); 139a190901cSRussell King if (IS_ERR(rtc->rtc)) { 140a190901cSRussell King ret = PTR_ERR(rtc->rtc); 141a190901cSRussell King goto err_reg; 142a190901cSRussell King } 143a190901cSRussell King 144a190901cSRussell King return 0; 145a190901cSRussell King 146a190901cSRussell King err_reg: 147a190901cSRussell King free_irq(dev->irq[0], rtc); 148a190901cSRussell King err_irq: 149a190901cSRussell King iounmap(rtc->base); 150a190901cSRussell King err_map: 151a190901cSRussell King kfree(rtc); 152a190901cSRussell King err_rtc: 153a190901cSRussell King amba_release_regions(dev); 154a190901cSRussell King err_req: 155a190901cSRussell King return ret; 156a190901cSRussell King } 157a190901cSRussell King 158a190901cSRussell King static int pl030_remove(struct amba_device *dev) 159a190901cSRussell King { 160a190901cSRussell King struct pl030_rtc *rtc = amba_get_drvdata(dev); 161a190901cSRussell King 162a190901cSRussell King amba_set_drvdata(dev, NULL); 163a190901cSRussell King 164a190901cSRussell King writel(0, rtc->base + RTC_CR); 165a190901cSRussell King 166a190901cSRussell King free_irq(dev->irq[0], rtc); 167a190901cSRussell King rtc_device_unregister(rtc->rtc); 168a190901cSRussell King iounmap(rtc->base); 169a190901cSRussell King kfree(rtc); 170a190901cSRussell King amba_release_regions(dev); 171a190901cSRussell King 172a190901cSRussell King return 0; 173a190901cSRussell King } 174a190901cSRussell King 175a190901cSRussell King static struct amba_id pl030_ids[] = { 176a190901cSRussell King { 177a190901cSRussell King .id = 0x00041030, 178a190901cSRussell King .mask = 0x000fffff, 179a190901cSRussell King }, 180a190901cSRussell King { 0, 0 }, 181a190901cSRussell King }; 182a190901cSRussell King 183a190901cSRussell King static struct amba_driver pl030_driver = { 184a190901cSRussell King .drv = { 185a190901cSRussell King .name = "rtc-pl030", 186a190901cSRussell King }, 187a190901cSRussell King .probe = pl030_probe, 188a190901cSRussell King .remove = pl030_remove, 189a190901cSRussell King .id_table = pl030_ids, 190a190901cSRussell King }; 191a190901cSRussell King 192a190901cSRussell King static int __init pl030_init(void) 193a190901cSRussell King { 194a190901cSRussell King return amba_driver_register(&pl030_driver); 195a190901cSRussell King } 196a190901cSRussell King 197a190901cSRussell King static void __exit pl030_exit(void) 198a190901cSRussell King { 199a190901cSRussell King amba_driver_unregister(&pl030_driver); 200a190901cSRussell King } 201a190901cSRussell King 202a190901cSRussell King module_init(pl030_init); 203a190901cSRussell King module_exit(pl030_exit); 204a190901cSRussell King 205a190901cSRussell King MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); 206a190901cSRussell King MODULE_DESCRIPTION("ARM AMBA PL030 RTC Driver"); 207a190901cSRussell King MODULE_LICENSE("GPL"); 208