rtc: goldfish: Add RTC driver for Android emulator

Add device driver for a virtual RTC device in Android emulator.

The compatible string used by OS for binding the driver is defined
as "google,goldfish-rtc".

Signed-off-by: Miodrag Dinic <miodrag.dinic@imgtec.com>
Signed-off-by: Goran Ferenc <goran.ferenc@imgtec.com>
Signed-off-by: Aleksandar Markovic <aleksandar.markovic@imgtec.com>
Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>

authored by

Miodrag Dinic and committed by
Alexandre Belloni
f22d9cdc 7a08de1d

+247
+1
MAINTAINERS
··· 845 845 M: Miodrag Dinic <miodrag.dinic@imgtec.com> 846 846 S: Supported 847 847 F: Documentation/devicetree/bindings/rtc/google,goldfish-rtc.txt 848 + F: drivers/rtc/rtc-goldfish.c 848 849 849 850 ANDROID ION DRIVER 850 851 M: Laura Abbott <labbott@redhat.com>
+8
drivers/rtc/Kconfig
··· 1780 1780 If this driver is compiled as a module, it will be named 1781 1781 rtc-hid-sensor-time. 1782 1782 1783 + config RTC_DRV_GOLDFISH 1784 + tristate "Goldfish Real Time Clock" 1785 + depends on MIPS && (GOLDFISH || COMPILE_TEST) 1786 + help 1787 + Say yes to enable RTC driver for the Goldfish based virtual platform. 1788 + 1789 + Goldfish is a code name for the virtual platform developed by Google 1790 + for Android emulation. 1783 1791 1784 1792 endif # RTC_CLASS
+1
drivers/rtc/Makefile
··· 170 170 obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o 171 171 obj-$(CONFIG_RTC_DRV_XGENE) += rtc-xgene.o 172 172 obj-$(CONFIG_RTC_DRV_ZYNQMP) += rtc-zynqmp.o 173 + obj-$(CONFIG_RTC_DRV_GOLDFISH) += rtc-goldfish.o
+237
drivers/rtc/rtc-goldfish.c
··· 1 + /* drivers/rtc/rtc-goldfish.c 2 + * 3 + * Copyright (C) 2007 Google, Inc. 4 + * Copyright (C) 2017 Imagination Technologies Ltd. 5 + * 6 + * This software is licensed under the terms of the GNU General Public 7 + * License version 2, as published by the Free Software Foundation, and 8 + * may be copied, distributed, and modified under those terms. 9 + * 10 + * This program is distributed in the hope that it will be useful, 11 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 + * GNU General Public License for more details. 14 + * 15 + */ 16 + 17 + #include <linux/module.h> 18 + #include <linux/platform_device.h> 19 + #include <linux/rtc.h> 20 + #include <linux/io.h> 21 + 22 + #define TIMER_TIME_LOW 0x00 /* get low bits of current time */ 23 + /* and update TIMER_TIME_HIGH */ 24 + #define TIMER_TIME_HIGH 0x04 /* get high bits of time at last */ 25 + /* TIMER_TIME_LOW read */ 26 + #define TIMER_ALARM_LOW 0x08 /* set low bits of alarm and */ 27 + /* activate it */ 28 + #define TIMER_ALARM_HIGH 0x0c /* set high bits of next alarm */ 29 + #define TIMER_IRQ_ENABLED 0x10 30 + #define TIMER_CLEAR_ALARM 0x14 31 + #define TIMER_ALARM_STATUS 0x18 32 + #define TIMER_CLEAR_INTERRUPT 0x1c 33 + 34 + struct goldfish_rtc { 35 + void __iomem *base; 36 + int irq; 37 + struct rtc_device *rtc; 38 + }; 39 + 40 + static int goldfish_rtc_read_alarm(struct device *dev, 41 + struct rtc_wkalrm *alrm) 42 + { 43 + u64 rtc_alarm; 44 + u64 rtc_alarm_low; 45 + u64 rtc_alarm_high; 46 + void __iomem *base; 47 + struct goldfish_rtc *rtcdrv; 48 + 49 + rtcdrv = dev_get_drvdata(dev); 50 + base = rtcdrv->base; 51 + 52 + rtc_alarm_low = readl(base + TIMER_ALARM_LOW); 53 + rtc_alarm_high = readl(base + TIMER_ALARM_HIGH); 54 + rtc_alarm = (rtc_alarm_high << 32) | rtc_alarm_low; 55 + 56 + do_div(rtc_alarm, NSEC_PER_SEC); 57 + memset(alrm, 0, sizeof(struct rtc_wkalrm)); 58 + 59 + rtc_time_to_tm(rtc_alarm, &alrm->time); 60 + 61 + if (readl(base + TIMER_ALARM_STATUS)) 62 + alrm->enabled = 1; 63 + else 64 + alrm->enabled = 0; 65 + 66 + return 0; 67 + } 68 + 69 + static int goldfish_rtc_set_alarm(struct device *dev, 70 + struct rtc_wkalrm *alrm) 71 + { 72 + struct goldfish_rtc *rtcdrv; 73 + unsigned long rtc_alarm; 74 + u64 rtc_alarm64; 75 + u64 rtc_status_reg; 76 + void __iomem *base; 77 + int ret = 0; 78 + 79 + rtcdrv = dev_get_drvdata(dev); 80 + base = rtcdrv->base; 81 + 82 + if (alrm->enabled) { 83 + ret = rtc_tm_to_time(&alrm->time, &rtc_alarm); 84 + if (ret != 0) 85 + return ret; 86 + 87 + rtc_alarm64 = rtc_alarm * NSEC_PER_SEC; 88 + writel((rtc_alarm64 >> 32), base + TIMER_ALARM_HIGH); 89 + writel(rtc_alarm64, base + TIMER_ALARM_LOW); 90 + } else { 91 + /* 92 + * if this function was called with enabled=0 93 + * then it could mean that the application is 94 + * trying to cancel an ongoing alarm 95 + */ 96 + rtc_status_reg = readl(base + TIMER_ALARM_STATUS); 97 + if (rtc_status_reg) 98 + writel(1, base + TIMER_CLEAR_ALARM); 99 + } 100 + 101 + return ret; 102 + } 103 + 104 + static int goldfish_rtc_alarm_irq_enable(struct device *dev, 105 + unsigned int enabled) 106 + { 107 + void __iomem *base; 108 + struct goldfish_rtc *rtcdrv; 109 + 110 + rtcdrv = dev_get_drvdata(dev); 111 + base = rtcdrv->base; 112 + 113 + if (enabled) 114 + writel(1, base + TIMER_IRQ_ENABLED); 115 + else 116 + writel(0, base + TIMER_IRQ_ENABLED); 117 + 118 + return 0; 119 + } 120 + 121 + static irqreturn_t goldfish_rtc_interrupt(int irq, void *dev_id) 122 + { 123 + struct goldfish_rtc *rtcdrv = dev_id; 124 + void __iomem *base = rtcdrv->base; 125 + 126 + writel(1, base + TIMER_CLEAR_INTERRUPT); 127 + 128 + rtc_update_irq(rtcdrv->rtc, 1, RTC_IRQF | RTC_AF); 129 + 130 + return IRQ_HANDLED; 131 + } 132 + 133 + static int goldfish_rtc_read_time(struct device *dev, struct rtc_time *tm) 134 + { 135 + struct goldfish_rtc *rtcdrv; 136 + void __iomem *base; 137 + u64 time_high; 138 + u64 time_low; 139 + u64 time; 140 + 141 + rtcdrv = dev_get_drvdata(dev); 142 + base = rtcdrv->base; 143 + 144 + time_low = readl(base + TIMER_TIME_LOW); 145 + time_high = readl(base + TIMER_TIME_HIGH); 146 + time = (time_high << 32) | time_low; 147 + 148 + do_div(time, NSEC_PER_SEC); 149 + 150 + rtc_time_to_tm(time, tm); 151 + 152 + return 0; 153 + } 154 + 155 + static int goldfish_rtc_set_time(struct device *dev, struct rtc_time *tm) 156 + { 157 + struct goldfish_rtc *rtcdrv; 158 + void __iomem *base; 159 + unsigned long now; 160 + u64 now64; 161 + int ret; 162 + 163 + rtcdrv = dev_get_drvdata(dev); 164 + base = rtcdrv->base; 165 + 166 + ret = rtc_tm_to_time(tm, &now); 167 + if (ret == 0) { 168 + now64 = now * NSEC_PER_SEC; 169 + writel((now64 >> 32), base + TIMER_TIME_HIGH); 170 + writel(now64, base + TIMER_TIME_LOW); 171 + } 172 + 173 + return ret; 174 + } 175 + 176 + static const struct rtc_class_ops goldfish_rtc_ops = { 177 + .read_time = goldfish_rtc_read_time, 178 + .set_time = goldfish_rtc_set_time, 179 + .read_alarm = goldfish_rtc_read_alarm, 180 + .set_alarm = goldfish_rtc_set_alarm, 181 + .alarm_irq_enable = goldfish_rtc_alarm_irq_enable 182 + }; 183 + 184 + static int goldfish_rtc_probe(struct platform_device *pdev) 185 + { 186 + struct goldfish_rtc *rtcdrv; 187 + struct resource *r; 188 + int err; 189 + 190 + rtcdrv = devm_kzalloc(&pdev->dev, sizeof(*rtcdrv), GFP_KERNEL); 191 + if (!rtcdrv) 192 + return -ENOMEM; 193 + 194 + platform_set_drvdata(pdev, rtcdrv); 195 + 196 + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 197 + if (!r) 198 + return -ENODEV; 199 + 200 + rtcdrv->base = devm_ioremap_resource(&pdev->dev, r); 201 + if (IS_ERR(rtcdrv->base)) 202 + return -ENODEV; 203 + 204 + rtcdrv->irq = platform_get_irq(pdev, 0); 205 + if (rtcdrv->irq < 0) 206 + return -ENODEV; 207 + 208 + rtcdrv->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, 209 + &goldfish_rtc_ops, 210 + THIS_MODULE); 211 + if (IS_ERR(rtcdrv->rtc)) 212 + return PTR_ERR(rtcdrv->rtc); 213 + 214 + err = devm_request_irq(&pdev->dev, rtcdrv->irq, 215 + goldfish_rtc_interrupt, 216 + 0, pdev->name, rtcdrv); 217 + if (err) 218 + return err; 219 + 220 + return 0; 221 + } 222 + 223 + static const struct of_device_id goldfish_rtc_of_match[] = { 224 + { .compatible = "google,goldfish-rtc", }, 225 + {}, 226 + }; 227 + MODULE_DEVICE_TABLE(of, goldfish_rtc_of_match); 228 + 229 + static struct platform_driver goldfish_rtc = { 230 + .probe = goldfish_rtc_probe, 231 + .driver = { 232 + .name = "goldfish_rtc", 233 + .of_match_table = goldfish_rtc_of_match, 234 + } 235 + }; 236 + 237 + module_platform_driver(goldfish_rtc);