rtc-ds1553.c 10.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13
/*
 * An rtc driver for the Dallas DS1553
 *
 * Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/bcd.h>
#include <linux/init.h>
#include <linux/kernel.h>
14
#include <linux/gfp.h>
15 16 17 18 19 20
#include <linux/delay.h>
#include <linux/jiffies.h>
#include <linux/interrupt.h>
#include <linux/rtc.h>
#include <linux/platform_device.h>
#include <linux/io.h>
21
#include <linux/module.h>
22

23
#define DRV_VERSION "0.3"
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

#define RTC_REG_SIZE		0x2000
#define RTC_OFFSET		0x1ff0

#define RTC_FLAGS		(RTC_OFFSET + 0)
#define RTC_SECONDS_ALARM	(RTC_OFFSET + 2)
#define RTC_MINUTES_ALARM	(RTC_OFFSET + 3)
#define RTC_HOURS_ALARM		(RTC_OFFSET + 4)
#define RTC_DATE_ALARM		(RTC_OFFSET + 5)
#define RTC_INTERRUPTS		(RTC_OFFSET + 6)
#define RTC_WATCHDOG		(RTC_OFFSET + 7)
#define RTC_CONTROL		(RTC_OFFSET + 8)
#define RTC_CENTURY		(RTC_OFFSET + 8)
#define RTC_SECONDS		(RTC_OFFSET + 9)
#define RTC_MINUTES		(RTC_OFFSET + 10)
#define RTC_HOURS		(RTC_OFFSET + 11)
#define RTC_DAY			(RTC_OFFSET + 12)
#define RTC_DATE		(RTC_OFFSET + 13)
#define RTC_MONTH		(RTC_OFFSET + 14)
#define RTC_YEAR		(RTC_OFFSET + 15)

#define RTC_CENTURY_MASK	0x3f
#define RTC_SECONDS_MASK	0x7f
#define RTC_DAY_MASK		0x07

/* Bits in the Control/Century register */
#define RTC_WRITE		0x80
#define RTC_READ		0x40

/* Bits in the Seconds register */
#define RTC_STOP		0x80

/* Bits in the Flags register */
#define RTC_FLAGS_AF		0x40
#define RTC_FLAGS_BLF		0x10

/* Bits in the Interrupts register */
#define RTC_INTS_AE		0x80

struct rtc_plat_data {
	struct rtc_device *rtc;
	void __iomem *ioaddr;
	unsigned long last_jiffies;
	int irq;
	unsigned int irqen;
	int alrm_sec;
	int alrm_min;
	int alrm_hour;
	int alrm_mday;
73
	spinlock_t lock;
74 75 76 77 78 79 80 81 82
};

static int ds1553_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
	void __iomem *ioaddr = pdata->ioaddr;
	u8 century;

A
Adrian Bunk 已提交
83
	century = bin2bcd((tm->tm_year + 1900) / 100);
84 85 86

	writeb(RTC_WRITE, pdata->ioaddr + RTC_CONTROL);

A
Adrian Bunk 已提交
87 88 89 90 91 92 93
	writeb(bin2bcd(tm->tm_year % 100), ioaddr + RTC_YEAR);
	writeb(bin2bcd(tm->tm_mon + 1), ioaddr + RTC_MONTH);
	writeb(bin2bcd(tm->tm_wday) & RTC_DAY_MASK, ioaddr + RTC_DAY);
	writeb(bin2bcd(tm->tm_mday), ioaddr + RTC_DATE);
	writeb(bin2bcd(tm->tm_hour), ioaddr + RTC_HOURS);
	writeb(bin2bcd(tm->tm_min), ioaddr + RTC_MINUTES);
	writeb(bin2bcd(tm->tm_sec) & RTC_SECONDS_MASK, ioaddr + RTC_SECONDS);
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

	/* RTC_CENTURY and RTC_CONTROL share same register */
	writeb(RTC_WRITE | (century & RTC_CENTURY_MASK), ioaddr + RTC_CENTURY);
	writeb(century & RTC_CENTURY_MASK, ioaddr + RTC_CONTROL);
	return 0;
}

static int ds1553_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
	void __iomem *ioaddr = pdata->ioaddr;
	unsigned int year, month, day, hour, minute, second, week;
	unsigned int century;

	/* give enough time to update RTC in case of continuous read */
	if (pdata->last_jiffies == jiffies)
		msleep(1);
	pdata->last_jiffies = jiffies;
	writeb(RTC_READ, ioaddr + RTC_CONTROL);
	second = readb(ioaddr + RTC_SECONDS) & RTC_SECONDS_MASK;
	minute = readb(ioaddr + RTC_MINUTES);
	hour = readb(ioaddr + RTC_HOURS);
	day = readb(ioaddr + RTC_DATE);
	week = readb(ioaddr + RTC_DAY) & RTC_DAY_MASK;
	month = readb(ioaddr + RTC_MONTH);
	year = readb(ioaddr + RTC_YEAR);
	century = readb(ioaddr + RTC_CENTURY) & RTC_CENTURY_MASK;
	writeb(0, ioaddr + RTC_CONTROL);
A
Adrian Bunk 已提交
123 124 125 126 127 128
	tm->tm_sec = bcd2bin(second);
	tm->tm_min = bcd2bin(minute);
	tm->tm_hour = bcd2bin(hour);
	tm->tm_mday = bcd2bin(day);
	tm->tm_wday = bcd2bin(week);
	tm->tm_mon = bcd2bin(month) - 1;
129
	/* year is 1900 + tm->tm_year */
A
Adrian Bunk 已提交
130
	tm->tm_year = bcd2bin(year) + bcd2bin(century) * 100 - 1900;
131 132 133 134 135 136 137 138 139 140 141 142 143

	if (rtc_valid_tm(tm) < 0) {
		dev_err(dev, "retrieved date/time is not valid.\n");
		rtc_time_to_tm(0, tm);
	}
	return 0;
}

static void ds1553_rtc_update_alarm(struct rtc_plat_data *pdata)
{
	void __iomem *ioaddr = pdata->ioaddr;
	unsigned long flags;

144
	spin_lock_irqsave(&pdata->lock, flags);
145
	writeb(pdata->alrm_mday < 0 || (pdata->irqen & RTC_UF) ?
A
Adrian Bunk 已提交
146
	       0x80 : bin2bcd(pdata->alrm_mday),
147 148
	       ioaddr + RTC_DATE_ALARM);
	writeb(pdata->alrm_hour < 0 || (pdata->irqen & RTC_UF) ?
A
Adrian Bunk 已提交
149
	       0x80 : bin2bcd(pdata->alrm_hour),
150 151
	       ioaddr + RTC_HOURS_ALARM);
	writeb(pdata->alrm_min < 0 || (pdata->irqen & RTC_UF) ?
A
Adrian Bunk 已提交
152
	       0x80 : bin2bcd(pdata->alrm_min),
153 154
	       ioaddr + RTC_MINUTES_ALARM);
	writeb(pdata->alrm_sec < 0 || (pdata->irqen & RTC_UF) ?
A
Adrian Bunk 已提交
155
	       0x80 : bin2bcd(pdata->alrm_sec),
156 157 158
	       ioaddr + RTC_SECONDS_ALARM);
	writeb(pdata->irqen ? RTC_INTS_AE : 0, ioaddr + RTC_INTERRUPTS);
	readb(ioaddr + RTC_FLAGS);	/* clear interrupts */
159
	spin_unlock_irqrestore(&pdata->lock, flags);
160 161 162 163 164 165 166
}

static int ds1553_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct rtc_plat_data *pdata = platform_get_drvdata(pdev);

167
	if (pdata->irq <= 0)
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
		return -EINVAL;
	pdata->alrm_mday = alrm->time.tm_mday;
	pdata->alrm_hour = alrm->time.tm_hour;
	pdata->alrm_min = alrm->time.tm_min;
	pdata->alrm_sec = alrm->time.tm_sec;
	if (alrm->enabled)
		pdata->irqen |= RTC_AF;
	ds1553_rtc_update_alarm(pdata);
	return 0;
}

static int ds1553_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct rtc_plat_data *pdata = platform_get_drvdata(pdev);

184
	if (pdata->irq <= 0)
185 186 187 188 189 190 191 192 193
		return -EINVAL;
	alrm->time.tm_mday = pdata->alrm_mday < 0 ? 0 : pdata->alrm_mday;
	alrm->time.tm_hour = pdata->alrm_hour < 0 ? 0 : pdata->alrm_hour;
	alrm->time.tm_min = pdata->alrm_min < 0 ? 0 : pdata->alrm_min;
	alrm->time.tm_sec = pdata->alrm_sec < 0 ? 0 : pdata->alrm_sec;
	alrm->enabled = (pdata->irqen & RTC_AF) ? 1 : 0;
	return 0;
}

194
static irqreturn_t ds1553_rtc_interrupt(int irq, void *dev_id)
195 196 197 198
{
	struct platform_device *pdev = dev_id;
	struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
	void __iomem *ioaddr = pdata->ioaddr;
199
	unsigned long events = 0;
200

201
	spin_lock(&pdata->lock);
202
	/* read and clear interrupt */
203 204 205 206 207 208 209 210 211 212 213
	if (readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_AF) {
		events = RTC_IRQF;
		if (readb(ioaddr + RTC_SECONDS_ALARM) & 0x80)
			events |= RTC_UF;
		else
			events |= RTC_AF;
		if (likely(pdata->rtc))
			rtc_update_irq(pdata->rtc, 1, events);
	}
	spin_unlock(&pdata->lock);
	return events ? IRQ_HANDLED : IRQ_NONE;
214 215
}

216
static int ds1553_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
217 218 219 220
{
	struct platform_device *pdev = to_platform_device(dev);
	struct rtc_plat_data *pdata = platform_get_drvdata(pdev);

221
	if (pdata->irq <= 0)
222 223
		return -EINVAL;
	if (enabled)
224
		pdata->irqen |= RTC_AF;
225 226 227 228 229 230
	else
		pdata->irqen &= ~RTC_AF;
	ds1553_rtc_update_alarm(pdata);
	return 0;
}

231
static const struct rtc_class_ops ds1553_rtc_ops = {
232 233 234 235 236
	.read_time		= ds1553_rtc_read_time,
	.set_time		= ds1553_rtc_set_time,
	.read_alarm		= ds1553_rtc_read_alarm,
	.set_alarm		= ds1553_rtc_set_alarm,
	.alarm_irq_enable	= ds1553_rtc_alarm_irq_enable,
237 238
};

239
static ssize_t ds1553_nvram_read(struct file *filp, struct kobject *kobj,
240 241
				 struct bin_attribute *bin_attr,
				 char *buf, loff_t pos, size_t size)
242
{
243 244
	struct device *dev = container_of(kobj, struct device, kobj);
	struct platform_device *pdev = to_platform_device(dev);
245 246 247 248 249 250 251 252 253
	struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
	void __iomem *ioaddr = pdata->ioaddr;
	ssize_t count;

	for (count = 0; size > 0 && pos < RTC_OFFSET; count++, size--)
		*buf++ = readb(ioaddr + pos++);
	return count;
}

254
static ssize_t ds1553_nvram_write(struct file *filp, struct kobject *kobj,
255 256
				  struct bin_attribute *bin_attr,
				  char *buf, loff_t pos, size_t size)
257
{
258 259
	struct device *dev = container_of(kobj, struct device, kobj);
	struct platform_device *pdev = to_platform_device(dev);
260 261 262 263 264 265 266 267 268 269 270 271
	struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
	void __iomem *ioaddr = pdata->ioaddr;
	ssize_t count;

	for (count = 0; size > 0 && pos < RTC_OFFSET; count++, size--)
		writeb(*buf++, ioaddr + pos++);
	return count;
}

static struct bin_attribute ds1553_nvram_attr = {
	.attr = {
		.name = "nvram",
D
David Brownell 已提交
272
		.mode = S_IRUGO | S_IWUSR,
273 274 275 276 277 278
	},
	.size = RTC_OFFSET,
	.read = ds1553_nvram_read,
	.write = ds1553_nvram_write,
};

279
static int ds1553_rtc_probe(struct platform_device *pdev)
280 281 282 283
{
	struct rtc_device *rtc;
	struct resource *res;
	unsigned int cen, sec;
284 285
	struct rtc_plat_data *pdata;
	void __iomem *ioaddr;
286 287 288 289 290
	int ret = 0;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!res)
		return -ENODEV;
291
	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
292 293
	if (!pdata)
		return -ENOMEM;
294 295 296 297 298 299 300
	if (!devm_request_mem_region(&pdev->dev, res->start, RTC_REG_SIZE,
			pdev->name))
		return -EBUSY;

	ioaddr = devm_ioremap(&pdev->dev, res->start, RTC_REG_SIZE);
	if (!ioaddr)
		return -ENOMEM;
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
	pdata->ioaddr = ioaddr;
	pdata->irq = platform_get_irq(pdev, 0);

	/* turn RTC on if it was not on */
	sec = readb(ioaddr + RTC_SECONDS);
	if (sec & RTC_STOP) {
		sec &= RTC_SECONDS_MASK;
		cen = readb(ioaddr + RTC_CENTURY) & RTC_CENTURY_MASK;
		writeb(RTC_WRITE, ioaddr + RTC_CONTROL);
		writeb(sec, ioaddr + RTC_SECONDS);
		writeb(cen & RTC_CENTURY_MASK, ioaddr + RTC_CONTROL);
	}
	if (readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_BLF)
		dev_warn(&pdev->dev, "voltage-low detected.\n");

316 317 318
	spin_lock_init(&pdata->lock);
	pdata->last_jiffies = jiffies;
	platform_set_drvdata(pdev, pdata);
319
	if (pdata->irq > 0) {
320
		writeb(0, ioaddr + RTC_INTERRUPTS);
321 322
		if (devm_request_irq(&pdev->dev, pdata->irq,
				ds1553_rtc_interrupt,
Y
Yong Zhang 已提交
323
				0, pdev->name, pdev) < 0) {
324
			dev_warn(&pdev->dev, "interrupt not available.\n");
325
			pdata->irq = 0;
326 327 328 329 330
		}
	}

	rtc = rtc_device_register(pdev->name, &pdev->dev,
				  &ds1553_rtc_ops, THIS_MODULE);
331 332
	if (IS_ERR(rtc))
		return PTR_ERR(rtc);
333
	pdata->rtc = rtc;
334

335 336
	ret = sysfs_create_bin_file(&pdev->dev.kobj, &ds1553_nvram_attr);
	if (ret)
337
		rtc_device_unregister(rtc);
338 339 340
	return ret;
}

341
static int ds1553_rtc_remove(struct platform_device *pdev)
342 343 344 345 346
{
	struct rtc_plat_data *pdata = platform_get_drvdata(pdev);

	sysfs_remove_bin_file(&pdev->dev.kobj, &ds1553_nvram_attr);
	rtc_device_unregister(pdata->rtc);
347
	if (pdata->irq > 0)
348 349 350 351
		writeb(0, pdata->ioaddr + RTC_INTERRUPTS);
	return 0;
}

352 353 354
/* work with hotplug and coldplug */
MODULE_ALIAS("platform:rtc-ds1553");

355 356
static struct platform_driver ds1553_rtc_driver = {
	.probe		= ds1553_rtc_probe,
357
	.remove		= ds1553_rtc_remove,
358
	.driver		= {
359
		.name	= "rtc-ds1553",
360 361 362 363
		.owner	= THIS_MODULE,
	},
};

364
module_platform_driver(ds1553_rtc_driver);
365 366 367 368 369

MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
MODULE_DESCRIPTION("Dallas DS1553 RTC driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);