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

Configure Feed

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

at v4.4 172 lines 4.6 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#define DRV_VERSION "0.2" 32 33MODULE_AUTHOR("Hans Ulli Kroll <ulli.kroll@googlemail.com>"); 34MODULE_DESCRIPTION("RTC driver for Gemini SoC"); 35MODULE_LICENSE("GPL"); 36MODULE_ALIAS("platform:" DRV_NAME); 37 38struct gemini_rtc { 39 struct rtc_device *rtc_dev; 40 void __iomem *rtc_base; 41 int rtc_irq; 42}; 43 44enum gemini_rtc_offsets { 45 GEMINI_RTC_SECOND = 0x00, 46 GEMINI_RTC_MINUTE = 0x04, 47 GEMINI_RTC_HOUR = 0x08, 48 GEMINI_RTC_DAYS = 0x0C, 49 GEMINI_RTC_ALARM_SECOND = 0x10, 50 GEMINI_RTC_ALARM_MINUTE = 0x14, 51 GEMINI_RTC_ALARM_HOUR = 0x18, 52 GEMINI_RTC_RECORD = 0x1C, 53 GEMINI_RTC_CR = 0x20 54}; 55 56static irqreturn_t gemini_rtc_interrupt(int irq, void *dev) 57{ 58 return IRQ_HANDLED; 59} 60 61/* 62 * Looks like the RTC in the Gemini SoC is (totaly) broken 63 * We can't read/write directly the time from RTC registers. 64 * We must do some "offset" calculation to get the real time 65 * 66 * This FIX works pretty fine and Stormlinksemi aka Cortina-Networks does 67 * the same thing, without the rtc-lib.c calls. 68 */ 69 70static int gemini_rtc_read_time(struct device *dev, struct rtc_time *tm) 71{ 72 struct gemini_rtc *rtc = dev_get_drvdata(dev); 73 74 unsigned int days, hour, min, sec; 75 unsigned long offset, time; 76 77 sec = readl(rtc->rtc_base + GEMINI_RTC_SECOND); 78 min = readl(rtc->rtc_base + GEMINI_RTC_MINUTE); 79 hour = readl(rtc->rtc_base + GEMINI_RTC_HOUR); 80 days = readl(rtc->rtc_base + GEMINI_RTC_DAYS); 81 offset = readl(rtc->rtc_base + GEMINI_RTC_RECORD); 82 83 time = offset + days * 86400 + hour * 3600 + min * 60 + sec; 84 85 rtc_time_to_tm(time, tm); 86 87 return 0; 88} 89 90static int gemini_rtc_set_time(struct device *dev, struct rtc_time *tm) 91{ 92 struct gemini_rtc *rtc = dev_get_drvdata(dev); 93 unsigned int sec, min, hour, day; 94 unsigned long offset, time; 95 96 if (tm->tm_year >= 2148) /* EPOCH Year + 179 */ 97 return -EINVAL; 98 99 rtc_tm_to_time(tm, &time); 100 101 sec = readl(rtc->rtc_base + GEMINI_RTC_SECOND); 102 min = readl(rtc->rtc_base + GEMINI_RTC_MINUTE); 103 hour = readl(rtc->rtc_base + GEMINI_RTC_HOUR); 104 day = readl(rtc->rtc_base + GEMINI_RTC_DAYS); 105 106 offset = time - (day * 86400 + hour * 3600 + min * 60 + sec); 107 108 writel(offset, rtc->rtc_base + GEMINI_RTC_RECORD); 109 writel(0x01, rtc->rtc_base + GEMINI_RTC_CR); 110 111 return 0; 112} 113 114static struct rtc_class_ops gemini_rtc_ops = { 115 .read_time = gemini_rtc_read_time, 116 .set_time = gemini_rtc_set_time, 117}; 118 119static int gemini_rtc_probe(struct platform_device *pdev) 120{ 121 struct gemini_rtc *rtc; 122 struct device *dev = &pdev->dev; 123 struct resource *res; 124 int ret; 125 126 rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); 127 if (unlikely(!rtc)) 128 return -ENOMEM; 129 platform_set_drvdata(pdev, rtc); 130 131 res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 132 if (!res) 133 return -ENODEV; 134 135 rtc->rtc_irq = res->start; 136 137 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 138 if (!res) 139 return -ENODEV; 140 141 rtc->rtc_base = devm_ioremap(dev, res->start, 142 resource_size(res)); 143 144 ret = devm_request_irq(dev, rtc->rtc_irq, gemini_rtc_interrupt, 145 IRQF_SHARED, pdev->name, dev); 146 if (unlikely(ret)) 147 return ret; 148 149 rtc->rtc_dev = rtc_device_register(pdev->name, dev, 150 &gemini_rtc_ops, THIS_MODULE); 151 return PTR_ERR_OR_ZERO(rtc->rtc_dev); 152} 153 154static int gemini_rtc_remove(struct platform_device *pdev) 155{ 156 struct gemini_rtc *rtc = platform_get_drvdata(pdev); 157 158 rtc_device_unregister(rtc->rtc_dev); 159 platform_set_drvdata(pdev, NULL); 160 161 return 0; 162} 163 164static struct platform_driver gemini_rtc_driver = { 165 .driver = { 166 .name = DRV_NAME, 167 }, 168 .probe = gemini_rtc_probe, 169 .remove = gemini_rtc_remove, 170}; 171 172module_platform_driver_probe(gemini_rtc_driver, gemini_rtc_probe);