Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.11-rc7 177 lines 4.7 kB view raw
1/* 2 * Gemini OnChip RTC 3 * 4 * Copyright (C) 2009 Janos Laube <janos.dev@gmail.com> 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 as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * Original code for older kernel 2.6.15 are from Stormlinksemi 17 * first update from Janos Laube for > 2.6.29 kernels 18 * 19 * checkpatch fixes and usage of rtc-lib code 20 * Hans Ulli Kroll <ulli.kroll@googlemail.com> 21 */ 22 23#include <linux/rtc.h> 24#include <linux/io.h> 25#include <linux/slab.h> 26#include <linux/platform_device.h> 27#include <linux/kernel.h> 28#include <linux/module.h> 29 30#define DRV_NAME "rtc-gemini" 31 32MODULE_AUTHOR("Hans Ulli Kroll <ulli.kroll@googlemail.com>"); 33MODULE_DESCRIPTION("RTC driver for Gemini SoC"); 34MODULE_LICENSE("GPL"); 35MODULE_ALIAS("platform:" DRV_NAME); 36 37struct gemini_rtc { 38 struct rtc_device *rtc_dev; 39 void __iomem *rtc_base; 40 int rtc_irq; 41}; 42 43enum gemini_rtc_offsets { 44 GEMINI_RTC_SECOND = 0x00, 45 GEMINI_RTC_MINUTE = 0x04, 46 GEMINI_RTC_HOUR = 0x08, 47 GEMINI_RTC_DAYS = 0x0C, 48 GEMINI_RTC_ALARM_SECOND = 0x10, 49 GEMINI_RTC_ALARM_MINUTE = 0x14, 50 GEMINI_RTC_ALARM_HOUR = 0x18, 51 GEMINI_RTC_RECORD = 0x1C, 52 GEMINI_RTC_CR = 0x20 53}; 54 55static irqreturn_t gemini_rtc_interrupt(int irq, void *dev) 56{ 57 return IRQ_HANDLED; 58} 59 60/* 61 * Looks like the RTC in the Gemini SoC is (totaly) broken 62 * We can't read/write directly the time from RTC registers. 63 * We must do some "offset" calculation to get the real time 64 * 65 * This FIX works pretty fine and Stormlinksemi aka Cortina-Networks does 66 * the same thing, without the rtc-lib.c calls. 67 */ 68 69static int gemini_rtc_read_time(struct device *dev, struct rtc_time *tm) 70{ 71 struct gemini_rtc *rtc = dev_get_drvdata(dev); 72 73 unsigned int days, hour, min, sec; 74 unsigned long offset, time; 75 76 sec = readl(rtc->rtc_base + GEMINI_RTC_SECOND); 77 min = readl(rtc->rtc_base + GEMINI_RTC_MINUTE); 78 hour = readl(rtc->rtc_base + GEMINI_RTC_HOUR); 79 days = readl(rtc->rtc_base + GEMINI_RTC_DAYS); 80 offset = readl(rtc->rtc_base + GEMINI_RTC_RECORD); 81 82 time = offset + days * 86400 + hour * 3600 + min * 60 + sec; 83 84 rtc_time_to_tm(time, tm); 85 86 return 0; 87} 88 89static int gemini_rtc_set_time(struct device *dev, struct rtc_time *tm) 90{ 91 struct gemini_rtc *rtc = dev_get_drvdata(dev); 92 unsigned int sec, min, hour, day; 93 unsigned long offset, time; 94 95 if (tm->tm_year >= 2148) /* EPOCH Year + 179 */ 96 return -EINVAL; 97 98 rtc_tm_to_time(tm, &time); 99 100 sec = readl(rtc->rtc_base + GEMINI_RTC_SECOND); 101 min = readl(rtc->rtc_base + GEMINI_RTC_MINUTE); 102 hour = readl(rtc->rtc_base + GEMINI_RTC_HOUR); 103 day = readl(rtc->rtc_base + GEMINI_RTC_DAYS); 104 105 offset = time - (day * 86400 + hour * 3600 + min * 60 + sec); 106 107 writel(offset, rtc->rtc_base + GEMINI_RTC_RECORD); 108 writel(0x01, rtc->rtc_base + GEMINI_RTC_CR); 109 110 return 0; 111} 112 113static const struct rtc_class_ops gemini_rtc_ops = { 114 .read_time = gemini_rtc_read_time, 115 .set_time = gemini_rtc_set_time, 116}; 117 118static int gemini_rtc_probe(struct platform_device *pdev) 119{ 120 struct gemini_rtc *rtc; 121 struct device *dev = &pdev->dev; 122 struct resource *res; 123 int ret; 124 125 rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); 126 if (unlikely(!rtc)) 127 return -ENOMEM; 128 platform_set_drvdata(pdev, rtc); 129 130 res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 131 if (!res) 132 return -ENODEV; 133 134 rtc->rtc_irq = res->start; 135 136 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 137 if (!res) 138 return -ENODEV; 139 140 rtc->rtc_base = devm_ioremap(dev, res->start, 141 resource_size(res)); 142 143 ret = devm_request_irq(dev, rtc->rtc_irq, gemini_rtc_interrupt, 144 IRQF_SHARED, pdev->name, dev); 145 if (unlikely(ret)) 146 return ret; 147 148 rtc->rtc_dev = rtc_device_register(pdev->name, dev, 149 &gemini_rtc_ops, THIS_MODULE); 150 return PTR_ERR_OR_ZERO(rtc->rtc_dev); 151} 152 153static int gemini_rtc_remove(struct platform_device *pdev) 154{ 155 struct gemini_rtc *rtc = platform_get_drvdata(pdev); 156 157 rtc_device_unregister(rtc->rtc_dev); 158 159 return 0; 160} 161 162static const struct of_device_id gemini_rtc_dt_match[] = { 163 { .compatible = "cortina,gemini-rtc" }, 164 { } 165}; 166MODULE_DEVICE_TABLE(of, gemini_rtc_dt_match); 167 168static struct platform_driver gemini_rtc_driver = { 169 .driver = { 170 .name = DRV_NAME, 171 .of_match_table = gemini_rtc_dt_match, 172 }, 173 .probe = gemini_rtc_probe, 174 .remove = gemini_rtc_remove, 175}; 176 177module_platform_driver_probe(gemini_rtc_driver, gemini_rtc_probe);