Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 | /*
* Copyright (c) 2011 Zhao Zhang <zhzhl555@gmail.com>
*
* Derived from driver/rtc/rtc-au1xxx.c
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/rtc.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/types.h>
#include <linux/io.h>
#include <asm/mach-loongson1/loongson1.h>
#define LS1X_RTC_REG_OFFSET (LS1X_RTC_BASE + 0x20)
#define LS1X_RTC_REGS(x) \
((void __iomem *)KSEG1ADDR(LS1X_RTC_REG_OFFSET + (x)))
/*RTC programmable counters 0 and 1*/
#define SYS_COUNTER_CNTRL (LS1X_RTC_REGS(0x20))
#define SYS_CNTRL_ERS (1 << 23)
#define SYS_CNTRL_RTS (1 << 20)
#define SYS_CNTRL_RM2 (1 << 19)
#define SYS_CNTRL_RM1 (1 << 18)
#define SYS_CNTRL_RM0 (1 << 17)
#define SYS_CNTRL_RS (1 << 16)
#define SYS_CNTRL_BP (1 << 14)
#define SYS_CNTRL_REN (1 << 13)
#define SYS_CNTRL_BRT (1 << 12)
#define SYS_CNTRL_TEN (1 << 11)
#define SYS_CNTRL_BTT (1 << 10)
#define SYS_CNTRL_E0 (1 << 8)
#define SYS_CNTRL_ETS (1 << 7)
#define SYS_CNTRL_32S (1 << 5)
#define SYS_CNTRL_TTS (1 << 4)
#define SYS_CNTRL_TM2 (1 << 3)
#define SYS_CNTRL_TM1 (1 << 2)
#define SYS_CNTRL_TM0 (1 << 1)
#define SYS_CNTRL_TS (1 << 0)
/* Programmable Counter 0 Registers */
#define SYS_TOYTRIM (LS1X_RTC_REGS(0))
#define SYS_TOYWRITE0 (LS1X_RTC_REGS(4))
#define SYS_TOYWRITE1 (LS1X_RTC_REGS(8))
#define SYS_TOYREAD0 (LS1X_RTC_REGS(0xC))
#define SYS_TOYREAD1 (LS1X_RTC_REGS(0x10))
#define SYS_TOYMATCH0 (LS1X_RTC_REGS(0x14))
#define SYS_TOYMATCH1 (LS1X_RTC_REGS(0x18))
#define SYS_TOYMATCH2 (LS1X_RTC_REGS(0x1C))
/* Programmable Counter 1 Registers */
#define SYS_RTCTRIM (LS1X_RTC_REGS(0x40))
#define SYS_RTCWRITE0 (LS1X_RTC_REGS(0x44))
#define SYS_RTCREAD0 (LS1X_RTC_REGS(0x48))
#define SYS_RTCMATCH0 (LS1X_RTC_REGS(0x4C))
#define SYS_RTCMATCH1 (LS1X_RTC_REGS(0x50))
#define SYS_RTCMATCH2 (LS1X_RTC_REGS(0x54))
#define LS1X_SEC_OFFSET (4)
#define LS1X_MIN_OFFSET (10)
#define LS1X_HOUR_OFFSET (16)
#define LS1X_DAY_OFFSET (21)
#define LS1X_MONTH_OFFSET (26)
#define LS1X_SEC_MASK (0x3f)
#define LS1X_MIN_MASK (0x3f)
#define LS1X_HOUR_MASK (0x1f)
#define LS1X_DAY_MASK (0x1f)
#define LS1X_MONTH_MASK (0x3f)
#define LS1X_YEAR_MASK (0xffffffff)
#define ls1x_get_sec(t) (((t) >> LS1X_SEC_OFFSET) & LS1X_SEC_MASK)
#define ls1x_get_min(t) (((t) >> LS1X_MIN_OFFSET) & LS1X_MIN_MASK)
#define ls1x_get_hour(t) (((t) >> LS1X_HOUR_OFFSET) & LS1X_HOUR_MASK)
#define ls1x_get_day(t) (((t) >> LS1X_DAY_OFFSET) & LS1X_DAY_MASK)
#define ls1x_get_month(t) (((t) >> LS1X_MONTH_OFFSET) & LS1X_MONTH_MASK)
#define RTC_CNTR_OK (SYS_CNTRL_E0 | SYS_CNTRL_32S)
static int ls1x_rtc_read_time(struct device *dev, struct rtc_time *rtm)
{
unsigned long v, t;
v = readl(SYS_TOYREAD0);
t = readl(SYS_TOYREAD1);
memset(rtm, 0, sizeof(struct rtc_time));
t = mktime((t & LS1X_YEAR_MASK), ls1x_get_month(v),
ls1x_get_day(v), ls1x_get_hour(v),
ls1x_get_min(v), ls1x_get_sec(v));
rtc_time_to_tm(t, rtm);
return rtc_valid_tm(rtm);
}
static int ls1x_rtc_set_time(struct device *dev, struct rtc_time *rtm)
{
unsigned long v, t, c;
int ret = -ETIMEDOUT;
v = ((rtm->tm_mon + 1) << LS1X_MONTH_OFFSET)
| (rtm->tm_mday << LS1X_DAY_OFFSET)
| (rtm->tm_hour << LS1X_HOUR_OFFSET)
| (rtm->tm_min << LS1X_MIN_OFFSET)
| (rtm->tm_sec << LS1X_SEC_OFFSET);
writel(v, SYS_TOYWRITE0);
c = 0x10000;
/* add timeout check counter, for more safe */
while ((readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TS) && --c)
usleep_range(1000, 3000);
if (!c) {
dev_err(dev, "set time timeout!\n");
goto err;
}
t = rtm->tm_year + 1900;
writel(t, SYS_TOYWRITE1);
c = 0x10000;
while ((readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TS) && --c)
usleep_range(1000, 3000);
if (!c) {
dev_err(dev, "set time timeout!\n");
goto err;
}
return 0;
err:
return ret;
}
static struct rtc_class_ops ls1x_rtc_ops = {
.read_time = ls1x_rtc_read_time,
.set_time = ls1x_rtc_set_time,
};
static int __devinit ls1x_rtc_probe(struct platform_device *pdev)
{
struct rtc_device *rtcdev;
unsigned long v;
int ret;
v = readl(SYS_COUNTER_CNTRL);
if (!(v & RTC_CNTR_OK)) {
dev_err(&pdev->dev, "rtc counters not working\n");
ret = -ENODEV;
goto err;
}
ret = -ETIMEDOUT;
/* set to 1 HZ if needed */
if (readl(SYS_TOYTRIM) != 32767) {
v = 0x100000;
while ((readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TTS) && --v)
usleep_range(1000, 3000);
if (!v) {
dev_err(&pdev->dev, "time out\n");
goto err;
}
writel(32767, SYS_TOYTRIM);
}
/* this loop coundn't be endless */
while (readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TTS)
usleep_range(1000, 3000);
rtcdev = rtc_device_register("ls1x-rtc", &pdev->dev,
&ls1x_rtc_ops , THIS_MODULE);
if (IS_ERR(rtcdev)) {
ret = PTR_ERR(rtcdev);
goto err;
}
platform_set_drvdata(pdev, rtcdev);
return 0;
err:
return ret;
}
static int __devexit ls1x_rtc_remove(struct platform_device *pdev)
{
struct rtc_device *rtcdev = platform_get_drvdata(pdev);
rtc_device_unregister(rtcdev);
platform_set_drvdata(pdev, NULL);
return 0;
}
static struct platform_driver ls1x_rtc_driver = {
.driver = {
.name = "ls1x-rtc",
.owner = THIS_MODULE,
},
.remove = __devexit_p(ls1x_rtc_remove),
.probe = ls1x_rtc_probe,
};
module_platform_driver(ls1x_rtc_driver);
MODULE_AUTHOR("zhao zhang <zhzhl555@gmail.com>");
MODULE_LICENSE("GPL");
|