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